From 6859a8402945cf1d74af75a2e1aa4e327a506ab4 Mon Sep 17 00:00:00 2001 From: Alan Mayer Date: Wed, 26 Mar 2008 16:11:31 -0500 Subject: x86: resize NR_IRQS for large machines On machines with very large numbers of cpus, tables that are dimensioned by NR_IRQS get very large, especially the irq_desc table. They are also very sparsely used. When the cpu count is > MAX_IO_APICS, use MAX_IO_APICS to set NR_IRQS, otherwise use NR_CPUS. Signed-off-by: Alan Mayer Reviewed-by: Christoph Lameter Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/kernel_stat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index e8ffce898bf9..cf9f40a91c9c 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -1,11 +1,11 @@ #ifndef _LINUX_KERNEL_STAT_H #define _LINUX_KERNEL_STAT_H -#include #include #include #include #include +#include #include /* -- cgit v1.2.3 From 988f7b5789ccf5cfed14c72e28573a49f0cb4809 Mon Sep 17 00:00:00 2001 From: "venkatesh.pallipadi@intel.com" Date: Tue, 18 Mar 2008 17:00:22 -0700 Subject: x86: PAT export resource_wc in pci sysfs For the ranges with IORESOURCE_PREFETCH, export a new resource_wc interface in pci /sysfs along with resource (which is uncached). Signed-off-by: Venkatesh Pallipadi Signed-off-by: Suresh Siddha Acked-by: Jesse Barnes Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- Documentation/filesystems/sysfs-pci.txt | 1 + drivers/pci/pci-sysfs.c | 84 ++++++++++++++++++++++++--------- include/linux/pci.h | 1 + 3 files changed, 64 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 5daa2aaec2c5..68ef48839c04 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -36,6 +36,7 @@ files, each with their own function. local_cpus nearby CPU mask (cpumask, ro) resource PCI resource host addresses (ascii, ro) resource0..N PCI resource N, if present (binary, mmap) + resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap) rom PCI ROM resource, if present (binary, ro) subsystem_device PCI subsystem device (ascii, ro) subsystem_vendor PCI subsystem vendor (ascii, ro) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 271d41cc05ab..9ec7d3977a82 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -489,13 +489,14 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, * @kobj: kobject for mapping * @attr: struct bin_attribute for the file being mapped * @vma: struct vm_area_struct passed into the mmap + * @write_combine: 1 for write_combine mapping * * Use the regular PCI mapping routines to map a PCI resource into userspace. * FIXME: write combining? maybe automatic for prefetchable regions? */ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, - struct vm_area_struct *vma) + struct vm_area_struct *vma, int write_combine) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); @@ -518,7 +519,21 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, vma->vm_pgoff += start >> PAGE_SHIFT; mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; - return pci_mmap_page_range(pdev, vma, mmap_type, 0); + return pci_mmap_page_range(pdev, vma, mmap_type, write_combine); +} + +static int +pci_mmap_resource_uc(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return pci_mmap_resource(kobj, attr, vma, 0); +} + +static int +pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return pci_mmap_resource(kobj, attr, vma, 1); } /** @@ -541,9 +556,46 @@ pci_remove_resource_files(struct pci_dev *pdev) sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); kfree(res_attr); } + + res_attr = pdev->res_attr_wc[i]; + if (res_attr) { + sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); + kfree(res_attr); + } } } +static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) +{ + /* allocate attribute structure, piggyback attribute name */ + int name_len = write_combine ? 13 : 10; + struct bin_attribute *res_attr; + int retval; + + res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); + if (res_attr) { + char *res_attr_name = (char *)(res_attr + 1); + + if (write_combine) { + pdev->res_attr_wc[num] = res_attr; + sprintf(res_attr_name, "resource%d_wc", num); + res_attr->mmap = pci_mmap_resource_wc; + } else { + pdev->res_attr[num] = res_attr; + sprintf(res_attr_name, "resource%d", num); + res_attr->mmap = pci_mmap_resource_uc; + } + res_attr->attr.name = res_attr_name; + res_attr->attr.mode = S_IRUSR | S_IWUSR; + res_attr->size = pci_resource_len(pdev, num); + res_attr->private = &pdev->resource[num]; + retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); + } else + retval = -ENOMEM; + + return retval; +} + /** * pci_create_resource_files - create resource files in sysfs for @dev * @dev: dev in question @@ -557,31 +609,19 @@ static int pci_create_resource_files(struct pci_dev *pdev) /* Expose the PCI resources from this device as files */ for (i = 0; i < PCI_ROM_RESOURCE; i++) { - struct bin_attribute *res_attr; /* skip empty resources */ if (!pci_resource_len(pdev, i)) continue; - /* allocate attribute structure, piggyback attribute name */ - res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC); - if (res_attr) { - char *res_attr_name = (char *)(res_attr + 1); - - pdev->res_attr[i] = res_attr; - sprintf(res_attr_name, "resource%d", i); - res_attr->attr.name = res_attr_name; - res_attr->attr.mode = S_IRUSR | S_IWUSR; - res_attr->size = pci_resource_len(pdev, i); - res_attr->mmap = pci_mmap_resource; - res_attr->private = &pdev->resource[i]; - retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); - if (retval) { - pci_remove_resource_files(pdev); - return retval; - } - } else { - return -ENOMEM; + retval = pci_create_attr(pdev, i, 0); + /* for prefetchable resources, create a WC mappable file */ + if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH) + retval = pci_create_attr(pdev, i, 1); + + if (retval) { + pci_remove_resource_files(pdev); + return retval; } } return 0; diff --git a/include/linux/pci.h b/include/linux/pci.h index 509159bcd4e7..d18b1dd49fab 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -206,6 +206,7 @@ struct pci_dev { struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */ int rom_attr_enabled; /* has display of the rom attribute been enabled? */ struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ + struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ #ifdef CONFIG_PCI_MSI struct list_head msi_list; #endif -- cgit v1.2.3 From 8e552c36d90c03d2cabf5373788998966751b609 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Mon, 12 May 2008 21:46:29 -0400 Subject: power_supply: add CHARGE_COUNTER property and olpc_battery support for it This adds PROP_CHARGE_COUNTER to the power supply class (documenting it as well). The OLPC battery driver uses this for spitting out its ACR values (in uAh). We have some rounding errors (the data sheet claims 416.7, the math actually works out to 416.666667, so we're forced to choose between overflows or precision loss. I chose precision loss, and stuck w/ data sheet values), but I don't think anyone will care that much. Signed-off-by: Andres Salomon Signed-off-by: Anton Vorontsov --- Documentation/power/power_supply_class.txt | 4 ++++ drivers/power/olpc_battery.c | 11 ++++++++++- drivers/power/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index a8686e5a6857..c6cd4956047c 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -101,6 +101,10 @@ of charge when battery became full/empty". It also could mean "value of charge when battery considered full/empty at given conditions (temperature, age)". I.e. these attributes represents real thresholds, not design values. +CHARGE_COUNTER - the current charge counter (in µAh). This could easily +be negative; there is no empty or full value. It is only useful for +relative, time-based measurements. + ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. CAPACITY - capacity in percents. diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index c8b596a7fc94..9dd1589733c2 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -19,7 +19,7 @@ #define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ #define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */ -#define EC_BAT_ACR 0x12 +#define EC_BAT_ACR 0x12 /* int16_t, *416.7, µAh */ #define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */ #define EC_AMB_TEMP 0x14 /* uint16_t, *100/256, °C */ #define EC_BAT_STATUS 0x15 /* uint8_t, bitmask */ @@ -289,6 +289,14 @@ static int olpc_bat_get_property(struct power_supply *psy, ec_word = be16_to_cpu(ec_word); val->intval = ec_word * 100 / 256; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2); + if (ret) + return ret; + + ec_word = be16_to_cpu(ec_word); + val->intval = ec_word * 4167 / 10; + break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); if (ret) @@ -317,6 +325,7 @@ static enum power_supply_property olpc_bat_props[] = { POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_CHARGE_COUNTER, }; /* EEPROM reading goes completely around the power_supply API, sadly */ diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index c444d6b10c58..82e1246eeb0a 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -99,6 +99,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_empty), POWER_SUPPLY_ATTR(charge_now), POWER_SUPPLY_ATTR(charge_avg), + POWER_SUPPLY_ATTR(charge_counter), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 68ed19ccf1f7..ea96ead1d39d 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -78,6 +78,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGE_EMPTY, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL, -- cgit v1.2.3 From 5a433b3ad497120d80f1045d37bd1a9ce897388f Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Mon, 21 Apr 2008 10:41:10 -0700 Subject: mac80211: remove unnecessary byteshifts in frame control testing Byteswap the constants rather than the frame_control member. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 0b5e03eae6d2..a9102bc78b61 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -552,16 +552,17 @@ enum ieee80211_back_parties { */ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) { - u8 *raw = (u8 *) hdr; - u8 tofrom = (*(raw+1)) & 3; /* get the TODS and FROMDS bits */ - - switch (tofrom) { - case 2: - return hdr->addr3; - case 3: - return hdr->addr4; + __le16 fc = hdr->frame_control; + fc &= cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + + switch (fc) { + case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): + return hdr->addr3; + case __constant_cpu_to_le16(IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS): + return hdr->addr4; + default: + return hdr->addr2; } - return hdr->addr2; } /** @@ -577,12 +578,13 @@ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) */ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) { - u8 *raw = (u8 *) hdr; - u8 to_ds = (*(raw+1)) & 1; /* get the TODS bit */ + __le16 fc = hdr->frame_control; + fc &= cpu_to_le16(IEEE80211_FCTL_TODS); - if (to_ds) + if (fc) return hdr->addr3; - return hdr->addr1; + else + return hdr->addr1; } /** @@ -595,8 +597,8 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) */ static inline int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) { - return (le16_to_cpu(hdr->frame_control) & - IEEE80211_FCTL_MOREFRAGS) != 0; + __le16 fc = hdr->frame_control; + return !!(fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)); } #endif /* IEEE80211_H */ -- cgit v1.2.3 From 09e67ca2c523544e6b38aa570a5f62a0cf20b87b Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 16 May 2008 11:57:45 +1000 Subject: [POWERPC] Move of_device_get_modalias to drivers/of Commit 140b932f8cb6cced10b96860651a198b1b89cbb9 ("Create modalias file in sysfs for of_platform bus") needs this to avoid breaking the sparc builds. Just move the code and add whitespace around some binary operators. Signed-off-by: Stephen Rothwell Acked-by: David S. Miller Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/of_device.c | 48 ----------------------------------------- drivers/of/device.c | 48 +++++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/of_device.h | 2 -- include/linux/of_device.h | 3 +++ 4 files changed, 51 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c index 5748ddb47d9f..e9be908f199b 100644 --- a/arch/powerpc/kernel/of_device.c +++ b/arch/powerpc/kernel/of_device.c @@ -89,54 +89,6 @@ struct of_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); -ssize_t of_device_get_modalias(struct of_device *ofdev, - char *str, ssize_t len) -{ - const char *compat; - int cplen, i; - ssize_t tsize, csize, repend; - - /* Name & Type */ - csize = snprintf(str, len, "of:N%sT%s", - ofdev->node->name, ofdev->node->type); - - /* Get compatible property if any */ - compat = of_get_property(ofdev->node, "compatible", &cplen); - if (!compat) - return csize; - - /* Find true end (we tolerate multiple \0 at the end */ - for (i=(cplen-1); i>=0 && !compat[i]; i--) - cplen--; - if (!cplen) - return csize; - cplen++; - - /* Check space (need cplen+1 chars including final \0) */ - tsize = csize + cplen; - repend = tsize; - - if (csize>=len) /* @ the limit, all is already filled */ - return tsize; - - if (tsize>=len) { /* limit compat list */ - cplen = len-csize-1; - repend = len; - } - - /* Copy and do char replacement */ - memcpy(&str[csize+1], compat, cplen); - for (i=csize; idev); } EXPORT_SYMBOL(of_device_unregister); + +ssize_t of_device_get_modalias(struct of_device *ofdev, + char *str, ssize_t len) +{ + const char *compat; + int cplen, i; + ssize_t tsize, csize, repend; + + /* Name & Type */ + csize = snprintf(str, len, "of:N%sT%s", + ofdev->node->name, ofdev->node->type); + + /* Get compatible property if any */ + compat = of_get_property(ofdev->node, "compatible", &cplen); + if (!compat) + return csize; + + /* Find true end (we tolerate multiple \0 at the end */ + for (i = (cplen - 1); i >= 0 && !compat[i]; i--) + cplen--; + if (!cplen) + return csize; + cplen++; + + /* Check space (need cplen+1 chars including final \0) */ + tsize = csize + cplen; + repend = tsize; + + if (csize >= len) /* @ the limit, all is already filled */ + return tsize; + + if (tsize >= len) { /* limit compat list */ + cplen = len - csize - 1; + repend = len; + } + + /* Copy and do char replacement */ + memcpy(&str[csize + 1], compat, cplen); + for (i = csize; i < repend; i++) { + char c = str[i]; + if (c == '\0') + str[i] = 'C'; + else if (c == ' ') + str[i] = '_'; + } + + return tsize; +} diff --git a/include/asm-powerpc/of_device.h b/include/asm-powerpc/of_device.h index 6526e139a463..3c123990ca2e 100644 --- a/include/asm-powerpc/of_device.h +++ b/include/asm-powerpc/of_device.h @@ -21,8 +21,6 @@ extern struct of_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent); -extern ssize_t of_device_get_modalias(struct of_device *ofdev, - char *str, ssize_t len); extern int of_device_uevent(struct device *dev, struct kobj_uevent_env *env); diff --git a/include/linux/of_device.h b/include/linux/of_device.h index afe338217d91..d3a74e00a3e1 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -24,4 +24,7 @@ static inline void of_device_free(struct of_device *dev) of_release_dev(&dev->dev); } +extern ssize_t of_device_get_modalias(struct of_device *ofdev, + char *str, ssize_t len); + #endif /* _LINUX_OF_DEVICE_H */ -- cgit v1.2.3 From 3e582db0783872a645f5236ef142be6579b300ad Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 May 2008 12:11:29 -0400 Subject: Input: gameport - remove rescan/reconnect facilities They have never been used and are unlikely to be used in the future so remove them altogether. Reported-by: Adrian Bunk Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/gameport.c | 22 ---------------------- include/linux/gameport.h | 1 - 2 files changed, 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index c5600ac5feb3..078e4eed0894 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -36,7 +36,6 @@ EXPORT_SYMBOL(__gameport_register_driver); EXPORT_SYMBOL(gameport_unregister_driver); EXPORT_SYMBOL(gameport_open); EXPORT_SYMBOL(gameport_close); -EXPORT_SYMBOL(gameport_rescan); EXPORT_SYMBOL(gameport_set_phys); EXPORT_SYMBOL(gameport_start_polling); EXPORT_SYMBOL(gameport_stop_polling); @@ -230,8 +229,6 @@ static void gameport_find_driver(struct gameport *gameport) */ enum gameport_event_type { - GAMEPORT_RESCAN, - GAMEPORT_RECONNECT, GAMEPORT_REGISTER_PORT, GAMEPORT_REGISTER_DRIVER, }; @@ -365,15 +362,6 @@ static void gameport_handle_event(void) gameport_add_port(event->object); break; - case GAMEPORT_RECONNECT: - gameport_reconnect_port(event->object); - break; - - case GAMEPORT_RESCAN: - gameport_disconnect_port(event->object); - gameport_find_driver(event->object); - break; - case GAMEPORT_REGISTER_DRIVER: gameport_add_driver(event->object); break; @@ -651,16 +639,6 @@ static void gameport_disconnect_port(struct gameport *gameport) device_release_driver(&gameport->dev); } -void gameport_rescan(struct gameport *gameport) -{ - gameport_queue_event(gameport, NULL, GAMEPORT_RESCAN); -} - -void gameport_reconnect(struct gameport *gameport) -{ - gameport_queue_event(gameport, NULL, GAMEPORT_RECONNECT); -} - /* * Submits register request to kgameportd for subsequent execution. * Note that port registration is always asynchronous. diff --git a/include/linux/gameport.h b/include/linux/gameport.h index afad95272841..f64e29c0ef3f 100644 --- a/include/linux/gameport.h +++ b/include/linux/gameport.h @@ -68,7 +68,6 @@ struct gameport_driver { int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode); void gameport_close(struct gameport *gameport); -void gameport_rescan(struct gameport *gameport); #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -- cgit v1.2.3 From ccd3474569a101914444dc37cc108eec22578999 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 May 2008 12:11:39 -0400 Subject: Input: libps2 - remove delayed command execution Delayed command execution is not used by anyone so let's remove it. Reported-by: Adrian Bunk Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 52 -------------------------------------------- include/linux/libps2.h | 1 - 2 files changed, 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index b819239d74dc..2b304c22c200 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -26,15 +26,6 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -/* Work structure to schedule execution of a command */ -struct ps2work { - struct work_struct work; - struct ps2dev *ps2dev; - int command; - unsigned char param[0]; -}; - - /* * ps2_sendbyte() sends a byte to the device and waits for acknowledge. * It doesn't handle retransmission, though it could - because if there @@ -245,49 +236,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) } EXPORT_SYMBOL(ps2_command); -/* - * ps2_execute_scheduled_command() sends a command, previously scheduled by - * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.) - */ - -static void ps2_execute_scheduled_command(struct work_struct *work) -{ - struct ps2work *ps2work = container_of(work, struct ps2work, work); - - ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command); - kfree(ps2work); -} - -/* - * ps2_schedule_command() allows to schedule delayed execution of a PS/2 - * command and can be used to issue a command from an interrupt or softirq - * context. - */ - -int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command) -{ - struct ps2work *ps2work; - int send = (command >> 12) & 0xf; - int receive = (command >> 8) & 0xf; - - if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC))) - return -1; - - memset(ps2work, 0, sizeof(struct ps2work)); - ps2work->ps2dev = ps2dev; - ps2work->command = command; - memcpy(ps2work->param, param, send); - INIT_WORK(&ps2work->work, ps2_execute_scheduled_command); - - if (!schedule_work(&ps2work->work)) { - kfree(ps2work); - return -1; - } - - return 0; -} -EXPORT_SYMBOL(ps2_schedule_command); - /* * ps2_init() initializes ps2dev structure */ diff --git a/include/linux/libps2.h b/include/linux/libps2.h index f6f301e2b0f5..afc413369101 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -43,7 +43,6 @@ void ps2_init(struct ps2dev *ps2dev, struct serio *serio); int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); -int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); void ps2_cmd_aborted(struct ps2dev *ps2dev); -- cgit v1.2.3 From 253b00f1e6619055d524188f254ccb951bffce5d Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 7 May 2008 11:15:23 -0400 Subject: Input: remove unused defines Remove unused to_dev, to_handler, to_handle from include/linux/input.h Move to_handle_h from include/linux/input.h to drivers/char/keyboard.c Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 2 ++ include/linux/input.h | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 7f7e798c1384..d0369e05490a 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -46,6 +46,8 @@ extern void ctrl_alt_del(void); +#define to_handle_h(n) container_of(n, struct input_handle, h_node) + /* * Exported functions/variables */ diff --git a/include/linux/input.h b/include/linux/input.h index 28a094fcfe20..eff711d8a459 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1213,11 +1213,6 @@ struct input_handle { struct list_head h_node; }; -#define to_dev(n) container_of(n, struct input_dev, node) -#define to_handler(n) container_of(n, struct input_handler, node) -#define to_handle(n) container_of(n, struct input_handle, d_node) -#define to_handle_h(n) container_of(n, struct input_handle, h_node) - struct input_dev *input_allocate_device(void); void input_free_device(struct input_dev *dev); -- cgit v1.2.3 From a33466e31213cd7c46696160d3db78680b58f6a3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 7 May 2008 16:30:15 -0400 Subject: Input: gpio-keys debouncing support Signed-off-by: Dmitry Baryshkov Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 89 +++++++++++++++++++++++++++++--------- include/linux/gpio_keys.h | 1 + 2 files changed, 70 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index bbd00c3fe98c..be58730e636a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -26,23 +26,54 @@ #include +struct gpio_button_data { + struct gpio_keys_button *button; + struct input_dev *input; + struct timer_list timer; +}; + +struct gpio_keys_drvdata { + struct input_dev *input; + struct gpio_button_data data[0]; +}; + +static void gpio_keys_report_event(struct gpio_keys_button *button, + struct input_dev *input) +{ + unsigned int type = button->type ?: EV_KEY; + int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; + + input_event(input, type, button->code, !!state); + input_sync(input); +} + +static void gpio_check_button(unsigned long _data) +{ + struct gpio_button_data *data = (struct gpio_button_data *)_data; + + gpio_keys_report_event(data->button, data->input); +} + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { - int i; struct platform_device *pdev = dev_id; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct input_dev *input = platform_get_drvdata(pdev); + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); + int i; for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; - int gpio = button->gpio; - if (irq == gpio_to_irq(gpio)) { - unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low; + if (irq == gpio_to_irq(button->gpio)) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (button->debounce_interval) + mod_timer(&bdata->timer, + jiffies + + msecs_to_jiffies(button->debounce_interval)); + else + gpio_keys_report_event(button, bdata->input); - input_event(input, type, button->code, !!state); - input_sync(input); return IRQ_HANDLED; } } @@ -53,17 +84,21 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) static int __devinit gpio_keys_probe(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct gpio_keys_drvdata *ddata; struct input_dev *input; int i, error; int wakeup = 0; + ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + + pdata->nbuttons * sizeof(struct gpio_button_data), + GFP_KERNEL); input = input_allocate_device(); - if (!input) - return -ENOMEM; - - platform_set_drvdata(pdev, input); + if (!ddata || !input) { + error = -ENOMEM; + goto fail1; + } - input->evbit[0] = BIT_MASK(EV_KEY); + platform_set_drvdata(pdev, ddata); input->name = pdev->name; input->phys = "gpio-keys/input0"; @@ -74,16 +109,23 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0100; + ddata->input = input; + for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_button_data *bdata = &ddata->data[i]; int irq; unsigned int type = button->type ?: EV_KEY; + bdata->input = input; + setup_timer(&bdata->timer, + gpio_check_button, (unsigned long)bdata); + error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); if (error < 0) { pr_err("gpio-keys: failed to request GPIO %d," " error %d\n", button->gpio, error); - goto fail; + goto fail2; } error = gpio_direction_input(button->gpio); @@ -92,7 +134,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) " direction for GPIO %d, error %d\n", button->gpio, error); gpio_free(button->gpio); - goto fail; + goto fail2; } irq = gpio_to_irq(button->gpio); @@ -102,7 +144,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) " for GPIO %d, error %d\n", button->gpio, error); gpio_free(button->gpio); - goto fail; + goto fail2; } error = request_irq(irq, gpio_keys_isr, @@ -114,7 +156,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) pr_err("gpio-keys: Unable to claim irq %d; error %d\n", irq, error); gpio_free(button->gpio); - goto fail; + goto fail2; } if (button->wakeup) @@ -127,21 +169,25 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) if (error) { pr_err("gpio-keys: Unable to register input device, " "error: %d\n", error); - goto fail; + goto fail2; } device_init_wakeup(&pdev->dev, wakeup); return 0; - fail: + fail2: while (--i >= 0) { free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); + if (pdata->buttons[i].debounce_interval) + del_timer_sync(&ddata->data[i].timer); gpio_free(pdata->buttons[i].gpio); } platform_set_drvdata(pdev, NULL); + fail1: input_free_device(input); + kfree(ddata); return error; } @@ -149,7 +195,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) static int __devexit gpio_keys_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct input_dev *input = platform_get_drvdata(pdev); + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; int i; device_init_wakeup(&pdev->dev, 0); @@ -157,6 +204,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); free_irq(irq, pdev); + if (pdata->buttons[i].debounce_interval) + del_timer_sync(&ddata->data[i].timer); gpio_free(pdata->buttons[i].gpio); } diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index c6d3a9de5634..ec6ecd74781d 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -9,6 +9,7 @@ struct gpio_keys_button { char *desc; int type; /* input event type (EV_KEY, EV_SW) */ int wakeup; /* configure the button as a wake-up source */ + int debounce_interval; /* debounce ticks interval in msecs */ }; struct gpio_keys_platform_data { -- cgit v1.2.3 From 8b09dee67f484e9b42114b1a1f068e080fd7aa56 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:21:05 +0200 Subject: rcupreempt: remove duplicate prototypes rcu_batches_completed and rcu_patches_completed_bh are both declared in rcuclassic.h and rcupreempt.h. This patch removes the extra prototypes for them from rcupdate.h. rcu_batches_completed_bh is defined as a static inline in the rcupreempt.h header file. Trying to export this as EXPORT_SYMBOL_GPL causes linking problems with the powerpc linker. There's no need to export a static inlined function. Modules must be compiled with the same type of RCU implementation as the kernel they are for. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar --- include/linux/rcupdate.h | 2 -- kernel/rcupreempt.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index d42dbec06083..ec2fc5b32646 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -224,8 +224,6 @@ extern void call_rcu_bh(struct rcu_head *head, /* Exported common interfaces */ extern void synchronize_rcu(void); extern void rcu_barrier(void); -extern long rcu_batches_completed(void); -extern long rcu_batches_completed_bh(void); /* Internal to kernel */ extern void rcu_init(void); diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index e1cdf196a515..5e02b7740702 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c @@ -217,8 +217,6 @@ long rcu_batches_completed(void) } EXPORT_SYMBOL_GPL(rcu_batches_completed); -EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); - void __rcu_read_lock(void) { int idx; -- cgit v1.2.3 From 4446a36ff8c74ac3b32feb009b651048e129c6af Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 12 May 2008 21:21:05 +0200 Subject: rcu: add call_rcu_sched() Fourth cut of patch to provide the call_rcu_sched(). This is again to synchronize_sched() as call_rcu() is to synchronize_rcu(). Should be fine for experimental and -rt use, but not ready for inclusion. With some luck, I will be able to tell Andrew to come out of hiding on the next round. Passes multi-day rcutorture sessions with concurrent CPU hotplugging. Fixes since the first version include a bug that could result in indefinite blocking (spotted by Gautham Shenoy), better resiliency against CPU-hotplug operations, and other minor fixes. Fixes since the second version include reworking grace-period detection to avoid deadlocks that could happen when running concurrently with CPU hotplug, adding Mathieu's fix to avoid the softlockup messages, as well as Mathieu's fix to allow use earlier in boot. Fixes since the third version include a wrong-CPU bug spotted by Andrew, getting rid of the obsolete synchronize_kernel API that somehow snuck back in, merging spin_unlock() and local_irq_restore() in a few places, commenting the code that checks for quiescent states based on interrupting from user-mode execution or the idle loop, removing some inline attributes, and some code-style changes. Known/suspected shortcomings: o I still do not entirely trust the sleep/wakeup logic. Next step will be to use a private snapshot of the CPU online mask in rcu_sched_grace_period() -- if the CPU wasn't there at the start of the grace period, we don't need to hear from it. And the bit about accounting for changes in online CPUs inside of rcu_sched_grace_period() is ugly anyway. o It might be good for rcu_sched_grace_period() to invoke resched_cpu() when a given CPU wasn't responding quickly, but resched_cpu() is declared static... This patch also fixes a long-standing bug in the earlier preemptable-RCU implementation of synchronize_rcu() that could result in loss of concurrent external changes to a task's CPU affinity mask. I still cannot remember who reported this... Signed-off-by: Paul E. McKenney Signed-off-by: Mathieu Desnoyers Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/rcuclassic.h | 3 + include/linux/rcupdate.h | 22 +++ include/linux/rcupreempt.h | 42 ++++- init/main.c | 1 + kernel/rcupdate.c | 20 +-- kernel/rcupreempt.c | 414 ++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 434 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index b3aa05baab8a..8c774905dcfe 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -151,7 +151,10 @@ extern struct lockdep_map rcu_lock_map; #define __synchronize_sched() synchronize_rcu() +#define call_rcu_sched(head, func) call_rcu(head, func) + extern void __rcu_init(void); +#define rcu_init_sched() do { } while (0) extern void rcu_check_callbacks(int cpu, int user); extern void rcu_restart_cpu(int cpu); diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index ec2fc5b32646..411969cb5243 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -40,6 +40,7 @@ #include #include #include +#include /** * struct rcu_head - callback structure for use with RCU @@ -168,6 +169,27 @@ struct rcu_head { (p) = (v); \ }) +/* Infrastructure to implement the synchronize_() primitives. */ + +struct rcu_synchronize { + struct rcu_head head; + struct completion completion; +}; + +extern void wakeme_after_rcu(struct rcu_head *head); + +#define synchronize_rcu_xxx(name, func) \ +void name(void) \ +{ \ + struct rcu_synchronize rcu; \ + \ + init_completion(&rcu.completion); \ + /* Will wake me after RCU finished. */ \ + func(&rcu.head, wakeme_after_rcu); \ + /* Wait for it. */ \ + wait_for_completion(&rcu.completion); \ +} + /** * synchronize_sched - block until all CPUs have exited any non-preemptive * kernel code sequences. diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index 8a05c7e20bc4..f04b64eca636 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h @@ -40,10 +40,39 @@ #include #include -#define rcu_qsctr_inc(cpu) +struct rcu_dyntick_sched { + int dynticks; + int dynticks_snap; + int sched_qs; + int sched_qs_snap; + int sched_dynticks_snap; +}; + +DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); + +static inline void rcu_qsctr_inc(int cpu) +{ + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); + + rdssp->sched_qs++; +} #define rcu_bh_qsctr_inc(cpu) #define call_rcu_bh(head, rcu) call_rcu(head, rcu) +/** + * call_rcu_sched - Queue RCU callback for invocation after sched grace period. + * @head: structure to be used for queueing the RCU updates. + * @func: actual update function to be invoked after the grace period + * + * The update function will be invoked some time after a full + * synchronize_sched()-style grace period elapses, in other words after + * all currently executing preempt-disabled sections of code (including + * hardirq handlers, NMI handlers, and local_irq_save() blocks) have + * completed. + */ +extern void call_rcu_sched(struct rcu_head *head, + void (*func)(struct rcu_head *head)); + extern void __rcu_read_lock(void) __acquires(RCU); extern void __rcu_read_unlock(void) __releases(RCU); extern int rcu_pending(int cpu); @@ -55,6 +84,7 @@ extern int rcu_needs_cpu(int cpu); extern void __synchronize_sched(void); extern void __rcu_init(void); +extern void rcu_init_sched(void); extern void rcu_check_callbacks(int cpu, int user); extern void rcu_restart_cpu(int cpu); extern long rcu_batches_completed(void); @@ -81,20 +111,20 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu); struct softirq_action; #ifdef CONFIG_NO_HZ -DECLARE_PER_CPU(long, dynticks_progress_counter); +DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); static inline void rcu_enter_nohz(void) { smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ - __get_cpu_var(dynticks_progress_counter)++; - WARN_ON(__get_cpu_var(dynticks_progress_counter) & 0x1); + __get_cpu_var(rcu_dyntick_sched).dynticks++; + WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1); } static inline void rcu_exit_nohz(void) { - __get_cpu_var(dynticks_progress_counter)++; smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ - WARN_ON(!(__get_cpu_var(dynticks_progress_counter) & 0x1)); + __get_cpu_var(rcu_dyntick_sched).dynticks++; + WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1)); } #else /* CONFIG_NO_HZ */ diff --git a/init/main.c b/init/main.c index f7fb20021d48..a9cc3e0803de 100644 --- a/init/main.c +++ b/init/main.c @@ -758,6 +758,7 @@ static void __init do_initcalls(void) */ static void __init do_basic_setup(void) { + rcu_init_sched(); /* needed by module_init stage. */ /* drivers will send hotplug events */ init_workqueues(); usermodehelper_init(); diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index c09605f8d16c..a4e329d92883 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -39,18 +39,12 @@ #include #include #include -#include #include #include #include #include #include -struct rcu_synchronize { - struct rcu_head head; - struct completion completion; -}; - static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL}; static atomic_t rcu_barrier_cpu_count; static DEFINE_MUTEX(rcu_barrier_mutex); @@ -60,7 +54,7 @@ static struct completion rcu_barrier_completion; * Awaken the corresponding synchronize_rcu() instance now that a * grace period has elapsed. */ -static void wakeme_after_rcu(struct rcu_head *head) +void wakeme_after_rcu(struct rcu_head *head) { struct rcu_synchronize *rcu; @@ -77,17 +71,7 @@ static void wakeme_after_rcu(struct rcu_head *head) * sections are delimited by rcu_read_lock() and rcu_read_unlock(), * and may be nested. */ -void synchronize_rcu(void) -{ - struct rcu_synchronize rcu; - - init_completion(&rcu.completion); - /* Will wake me after RCU finished */ - call_rcu(&rcu.head, wakeme_after_rcu); - - /* Wait for it */ - wait_for_completion(&rcu.completion); -} +synchronize_rcu_xxx(synchronize_rcu, call_rcu) EXPORT_SYMBOL_GPL(synchronize_rcu); static void rcu_barrier_callback(struct rcu_head *notused) diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index 5e02b7740702..aaa7976bd85f 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -87,9 +88,14 @@ struct rcu_data { struct rcu_head **nexttail; struct rcu_head *waitlist[GP_STAGES]; struct rcu_head **waittail[GP_STAGES]; - struct rcu_head *donelist; + struct rcu_head *donelist; /* from waitlist & waitschedlist */ struct rcu_head **donetail; long rcu_flipctr[2]; + struct rcu_head *nextschedlist; + struct rcu_head **nextschedtail; + struct rcu_head *waitschedlist; + struct rcu_head **waitschedtail; + int rcu_sched_sleeping; #ifdef CONFIG_RCU_TRACE struct rcupreempt_trace trace; #endif /* #ifdef CONFIG_RCU_TRACE */ @@ -131,11 +137,24 @@ enum rcu_try_flip_states { rcu_try_flip_waitmb_state, }; +/* + * States for rcu_ctrlblk.rcu_sched_sleep. + */ + +enum rcu_sched_sleep_states { + rcu_sched_not_sleeping, /* Not sleeping, callbacks need GP. */ + rcu_sched_sleep_prep, /* Thinking of sleeping, rechecking. */ + rcu_sched_sleeping, /* Sleeping, awaken if GP needed. */ +}; + struct rcu_ctrlblk { spinlock_t fliplock; /* Protect state-machine transitions. */ long completed; /* Number of last completed batch. */ enum rcu_try_flip_states rcu_try_flip_state; /* The current state of the rcu state machine */ + spinlock_t schedlock; /* Protect rcu_sched sleep state. */ + enum rcu_sched_sleep_states sched_sleep; /* rcu_sched state. */ + wait_queue_head_t sched_wq; /* Place for rcu_sched to sleep. */ }; static DEFINE_PER_CPU(struct rcu_data, rcu_data); @@ -143,8 +162,12 @@ static struct rcu_ctrlblk rcu_ctrlblk = { .fliplock = __SPIN_LOCK_UNLOCKED(rcu_ctrlblk.fliplock), .completed = 0, .rcu_try_flip_state = rcu_try_flip_idle_state, + .schedlock = __SPIN_LOCK_UNLOCKED(rcu_ctrlblk.schedlock), + .sched_sleep = rcu_sched_not_sleeping, + .sched_wq = __WAIT_QUEUE_HEAD_INITIALIZER(rcu_ctrlblk.sched_wq), }; +static struct task_struct *rcu_sched_grace_period_task; #ifdef CONFIG_RCU_TRACE static char *rcu_try_flip_state_names[] = @@ -207,6 +230,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(enum rcu_mb_flag_values, rcu_mb_flag) */ #define RCU_TRACE_RDP(f, rdp) RCU_TRACE(f, &((rdp)->trace)); +#define RCU_SCHED_BATCH_TIME (HZ / 50) + /* * Return the number of RCU batches processed thus far. Useful * for debug and statistics. @@ -411,32 +436,34 @@ static void __rcu_advance_callbacks(struct rcu_data *rdp) } } -#ifdef CONFIG_NO_HZ +DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_dyntick_sched, rcu_dyntick_sched) = { + .dynticks = 1, +}; -DEFINE_PER_CPU(long, dynticks_progress_counter) = 1; -static DEFINE_PER_CPU(long, rcu_dyntick_snapshot); +#ifdef CONFIG_NO_HZ static DEFINE_PER_CPU(int, rcu_update_flag); /** * rcu_irq_enter - Called from Hard irq handlers and NMI/SMI. * * If the CPU was idle with dynamic ticks active, this updates the - * dynticks_progress_counter to let the RCU handling know that the + * rcu_dyntick_sched.dynticks to let the RCU handling know that the * CPU is active. */ void rcu_irq_enter(void) { int cpu = smp_processor_id(); + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); if (per_cpu(rcu_update_flag, cpu)) per_cpu(rcu_update_flag, cpu)++; /* * Only update if we are coming from a stopped ticks mode - * (dynticks_progress_counter is even). + * (rcu_dyntick_sched.dynticks is even). */ if (!in_interrupt() && - (per_cpu(dynticks_progress_counter, cpu) & 0x1) == 0) { + (rdssp->dynticks & 0x1) == 0) { /* * The following might seem like we could have a race * with NMI/SMIs. But this really isn't a problem. @@ -459,12 +486,12 @@ void rcu_irq_enter(void) * RCU read-side critical sections on this CPU would * have already completed. */ - per_cpu(dynticks_progress_counter, cpu)++; + rdssp->dynticks++; /* * The following memory barrier ensures that any * rcu_read_lock() primitives in the irq handler * are seen by other CPUs to follow the above - * increment to dynticks_progress_counter. This is + * increment to rcu_dyntick_sched.dynticks. This is * required in order for other CPUs to correctly * determine when it is safe to advance the RCU * grace-period state machine. @@ -472,7 +499,7 @@ void rcu_irq_enter(void) smp_mb(); /* see above block comment. */ /* * Since we can't determine the dynamic tick mode from - * the dynticks_progress_counter after this routine, + * the rcu_dyntick_sched.dynticks after this routine, * we use a second flag to acknowledge that we came * from an idle state with ticks stopped. */ @@ -480,7 +507,7 @@ void rcu_irq_enter(void) /* * If we take an NMI/SMI now, they will also increment * the rcu_update_flag, and will not update the - * dynticks_progress_counter on exit. That is for + * rcu_dyntick_sched.dynticks on exit. That is for * this IRQ to do. */ } @@ -490,12 +517,13 @@ void rcu_irq_enter(void) * rcu_irq_exit - Called from exiting Hard irq context. * * If the CPU was idle with dynamic ticks active, update the - * dynticks_progress_counter to put let the RCU handling be + * rcu_dyntick_sched.dynticks to put let the RCU handling be * aware that the CPU is going back to idle with no ticks. */ void rcu_irq_exit(void) { int cpu = smp_processor_id(); + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); /* * rcu_update_flag is set if we interrupted the CPU @@ -503,7 +531,7 @@ void rcu_irq_exit(void) * Once this occurs, we keep track of interrupt nesting * because a NMI/SMI could also come in, and we still * only want the IRQ that started the increment of the - * dynticks_progress_counter to be the one that modifies + * rcu_dyntick_sched.dynticks to be the one that modifies * it on exit. */ if (per_cpu(rcu_update_flag, cpu)) { @@ -515,28 +543,29 @@ void rcu_irq_exit(void) /* * If an NMI/SMI happens now we are still - * protected by the dynticks_progress_counter being odd. + * protected by the rcu_dyntick_sched.dynticks being odd. */ /* * The following memory barrier ensures that any * rcu_read_unlock() primitives in the irq handler * are seen by other CPUs to preceed the following - * increment to dynticks_progress_counter. This + * increment to rcu_dyntick_sched.dynticks. This * is required in order for other CPUs to determine * when it is safe to advance the RCU grace-period * state machine. */ smp_mb(); /* see above block comment. */ - per_cpu(dynticks_progress_counter, cpu)++; - WARN_ON(per_cpu(dynticks_progress_counter, cpu) & 0x1); + rdssp->dynticks++; + WARN_ON(rdssp->dynticks & 0x1); } } static void dyntick_save_progress_counter(int cpu) { - per_cpu(rcu_dyntick_snapshot, cpu) = - per_cpu(dynticks_progress_counter, cpu); + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); + + rdssp->dynticks_snap = rdssp->dynticks; } static inline int @@ -544,9 +573,10 @@ rcu_try_flip_waitack_needed(int cpu) { long curr; long snap; + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); - curr = per_cpu(dynticks_progress_counter, cpu); - snap = per_cpu(rcu_dyntick_snapshot, cpu); + curr = rdssp->dynticks; + snap = rdssp->dynticks_snap; smp_mb(); /* force ordering with cpu entering/leaving dynticks. */ /* @@ -580,9 +610,10 @@ rcu_try_flip_waitmb_needed(int cpu) { long curr; long snap; + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); - curr = per_cpu(dynticks_progress_counter, cpu); - snap = per_cpu(rcu_dyntick_snapshot, cpu); + curr = rdssp->dynticks; + snap = rdssp->dynticks_snap; smp_mb(); /* force ordering with cpu entering/leaving dynticks. */ /* @@ -609,14 +640,86 @@ rcu_try_flip_waitmb_needed(int cpu) return 1; } +static void dyntick_save_progress_counter_sched(int cpu) +{ + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); + + rdssp->sched_dynticks_snap = rdssp->dynticks; +} + +static int rcu_qsctr_inc_needed_dyntick(int cpu) +{ + long curr; + long snap; + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); + + curr = rdssp->dynticks; + snap = rdssp->sched_dynticks_snap; + smp_mb(); /* force ordering with cpu entering/leaving dynticks. */ + + /* + * If the CPU remained in dynticks mode for the entire time + * and didn't take any interrupts, NMIs, SMIs, or whatever, + * then it cannot be in the middle of an rcu_read_lock(), so + * the next rcu_read_lock() it executes must use the new value + * of the counter. Therefore, this CPU has been in a quiescent + * state the entire time, and we don't need to wait for it. + */ + + if ((curr == snap) && ((curr & 0x1) == 0)) + return 0; + + /* + * If the CPU passed through or entered a dynticks idle phase with + * no active irq handlers, then, as above, this CPU has already + * passed through a quiescent state. + */ + + if ((curr - snap) > 2 || (snap & 0x1) == 0) + return 0; + + /* We need this CPU to go through a quiescent state. */ + + return 1; +} + #else /* !CONFIG_NO_HZ */ -# define dyntick_save_progress_counter(cpu) do { } while (0) -# define rcu_try_flip_waitack_needed(cpu) (1) -# define rcu_try_flip_waitmb_needed(cpu) (1) +# define dyntick_save_progress_counter(cpu) do { } while (0) +# define rcu_try_flip_waitack_needed(cpu) (1) +# define rcu_try_flip_waitmb_needed(cpu) (1) + +# define dyntick_save_progress_counter_sched(cpu) do { } while (0) +# define rcu_qsctr_inc_needed_dyntick(cpu) (1) #endif /* CONFIG_NO_HZ */ +static void save_qsctr_sched(int cpu) +{ + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); + + rdssp->sched_qs_snap = rdssp->sched_qs; +} + +static inline int rcu_qsctr_inc_needed(int cpu) +{ + struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu); + + /* + * If there has been a quiescent state, no more need to wait + * on this CPU. + */ + + if (rdssp->sched_qs != rdssp->sched_qs_snap) { + smp_mb(); /* force ordering with cpu entering schedule(). */ + return 0; + } + + /* We need this CPU to go through a quiescent state. */ + + return 1; +} + /* * Get here when RCU is idle. Decide whether we need to * move out of idle state, and return non-zero if so. @@ -819,6 +922,26 @@ void rcu_check_callbacks(int cpu, int user) unsigned long flags; struct rcu_data *rdp = RCU_DATA_CPU(cpu); + /* + * If this CPU took its interrupt from user mode or from the + * idle loop, and this is not a nested interrupt, then + * this CPU has to have exited all prior preept-disable + * sections of code. So increment the counter to note this. + * + * The memory barrier is needed to handle the case where + * writes from a preempt-disable section of code get reordered + * into schedule() by this CPU's write buffer. So the memory + * barrier makes sure that the rcu_qsctr_inc() is seen by other + * CPUs to happen after any such write. + */ + + if (user || + (idle_cpu(cpu) && !in_softirq() && + hardirq_count() <= (1 << HARDIRQ_SHIFT))) { + smp_mb(); /* Guard against aggressive schedule(). */ + rcu_qsctr_inc(cpu); + } + rcu_check_mb(cpu); if (rcu_ctrlblk.completed == rdp->completed) rcu_try_flip(); @@ -869,6 +992,8 @@ void rcu_offline_cpu(int cpu) struct rcu_head *list = NULL; unsigned long flags; struct rcu_data *rdp = RCU_DATA_CPU(cpu); + struct rcu_head *schedlist = NULL; + struct rcu_head **schedtail = &schedlist; struct rcu_head **tail = &list; /* @@ -882,6 +1007,11 @@ void rcu_offline_cpu(int cpu) rcu_offline_cpu_enqueue(rdp->waitlist[i], rdp->waittail[i], list, tail); rcu_offline_cpu_enqueue(rdp->nextlist, rdp->nexttail, list, tail); + rcu_offline_cpu_enqueue(rdp->waitschedlist, rdp->waitschedtail, + schedlist, schedtail); + rcu_offline_cpu_enqueue(rdp->nextschedlist, rdp->nextschedtail, + schedlist, schedtail); + rdp->rcu_sched_sleeping = 0; spin_unlock_irqrestore(&rdp->lock, flags); rdp->waitlistcount = 0; @@ -916,22 +1046,40 @@ void rcu_offline_cpu(int cpu) * fix. */ - local_irq_save(flags); + local_irq_save(flags); /* disable preempt till we know what lock. */ rdp = RCU_DATA_ME(); spin_lock(&rdp->lock); *rdp->nexttail = list; if (list) rdp->nexttail = tail; + *rdp->nextschedtail = schedlist; + if (schedlist) + rdp->nextschedtail = schedtail; spin_unlock_irqrestore(&rdp->lock, flags); } void __devinit rcu_online_cpu(int cpu) { unsigned long flags; + struct rcu_data *rdp; spin_lock_irqsave(&rcu_ctrlblk.fliplock, flags); cpu_set(cpu, rcu_cpu_online_map); spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags); + + /* + * The rcu_sched grace-period processing might have bypassed + * this CPU, given that it was not in the rcu_cpu_online_map + * when the grace-period scan started. This means that the + * grace-period task might sleep. So make sure that if this + * should happen, the first callback posted to this CPU will + * wake up the grace-period task if need be. + */ + + rdp = RCU_DATA_CPU(cpu); + spin_lock_irqsave(&rdp->lock, flags); + rdp->rcu_sched_sleeping = 1; + spin_unlock_irqrestore(&rdp->lock, flags); } #else /* #ifdef CONFIG_HOTPLUG_CPU */ @@ -986,31 +1134,196 @@ void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) *rdp->nexttail = head; rdp->nexttail = &head->next; RCU_TRACE_RDP(rcupreempt_trace_next_add, rdp); - spin_unlock(&rdp->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&rdp->lock, flags); } EXPORT_SYMBOL_GPL(call_rcu); +void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) +{ + unsigned long flags; + struct rcu_data *rdp; + int wake_gp = 0; + + head->func = func; + head->next = NULL; + local_irq_save(flags); + rdp = RCU_DATA_ME(); + spin_lock(&rdp->lock); + *rdp->nextschedtail = head; + rdp->nextschedtail = &head->next; + if (rdp->rcu_sched_sleeping) { + + /* Grace-period processing might be sleeping... */ + + rdp->rcu_sched_sleeping = 0; + wake_gp = 1; + } + spin_unlock_irqrestore(&rdp->lock, flags); + if (wake_gp) { + + /* Wake up grace-period processing, unless someone beat us. */ + + spin_lock_irqsave(&rcu_ctrlblk.schedlock, flags); + if (rcu_ctrlblk.sched_sleep != rcu_sched_sleeping) + wake_gp = 0; + rcu_ctrlblk.sched_sleep = rcu_sched_not_sleeping; + spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags); + if (wake_gp) + wake_up_interruptible(&rcu_ctrlblk.sched_wq); + } +} +EXPORT_SYMBOL_GPL(call_rcu_sched); + /* * Wait until all currently running preempt_disable() code segments * (including hardware-irq-disable segments) complete. Note that * in -rt this does -not- necessarily result in all currently executing * interrupt -handlers- having completed. */ -void __synchronize_sched(void) +synchronize_rcu_xxx(__synchronize_sched, call_rcu_sched) +EXPORT_SYMBOL_GPL(__synchronize_sched); + +/* + * kthread function that manages call_rcu_sched grace periods. + */ +static int rcu_sched_grace_period(void *arg) { - cpumask_t oldmask; + int couldsleep; /* might sleep after current pass. */ + int couldsleepnext = 0; /* might sleep after next pass. */ int cpu; + unsigned long flags; + struct rcu_data *rdp; + int ret; - if (sched_getaffinity(0, &oldmask) < 0) - oldmask = cpu_possible_map; - for_each_online_cpu(cpu) { - sched_setaffinity(0, &cpumask_of_cpu(cpu)); - schedule(); - } - sched_setaffinity(0, &oldmask); + /* + * Each pass through the following loop handles one + * rcu_sched grace period cycle. + */ + do { + /* Save each CPU's current state. */ + + for_each_online_cpu(cpu) { + dyntick_save_progress_counter_sched(cpu); + save_qsctr_sched(cpu); + } + + /* + * Sleep for about an RCU grace-period's worth to + * allow better batching and to consume less CPU. + */ + schedule_timeout_interruptible(RCU_SCHED_BATCH_TIME); + + /* + * If there was nothing to do last time, prepare to + * sleep at the end of the current grace period cycle. + */ + couldsleep = couldsleepnext; + couldsleepnext = 1; + if (couldsleep) { + spin_lock_irqsave(&rcu_ctrlblk.schedlock, flags); + rcu_ctrlblk.sched_sleep = rcu_sched_sleep_prep; + spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags); + } + + /* + * Wait on each CPU in turn to have either visited + * a quiescent state or been in dynticks-idle mode. + */ + for_each_online_cpu(cpu) { + while (rcu_qsctr_inc_needed(cpu) && + rcu_qsctr_inc_needed_dyntick(cpu)) { + /* resched_cpu(cpu); @@@ */ + schedule_timeout_interruptible(1); + } + } + + /* Advance callbacks for each CPU. */ + + for_each_online_cpu(cpu) { + + rdp = RCU_DATA_CPU(cpu); + spin_lock_irqsave(&rdp->lock, flags); + + /* + * We are running on this CPU irq-disabled, so no + * CPU can go offline until we re-enable irqs. + * The current CPU might have already gone + * offline (between the for_each_offline_cpu and + * the spin_lock_irqsave), but in that case all its + * callback lists will be empty, so no harm done. + * + * Advance the callbacks! We share normal RCU's + * donelist, since callbacks are invoked the + * same way in either case. + */ + if (rdp->waitschedlist != NULL) { + *rdp->donetail = rdp->waitschedlist; + rdp->donetail = rdp->waitschedtail; + + /* + * Next rcu_check_callbacks() will + * do the required raise_softirq(). + */ + } + if (rdp->nextschedlist != NULL) { + rdp->waitschedlist = rdp->nextschedlist; + rdp->waitschedtail = rdp->nextschedtail; + couldsleep = 0; + couldsleepnext = 0; + } else { + rdp->waitschedlist = NULL; + rdp->waitschedtail = &rdp->waitschedlist; + } + rdp->nextschedlist = NULL; + rdp->nextschedtail = &rdp->nextschedlist; + + /* Mark sleep intention. */ + + rdp->rcu_sched_sleeping = couldsleep; + + spin_unlock_irqrestore(&rdp->lock, flags); + } + + /* If we saw callbacks on the last scan, go deal with them. */ + + if (!couldsleep) + continue; + + /* Attempt to block... */ + + spin_lock_irqsave(&rcu_ctrlblk.schedlock, flags); + if (rcu_ctrlblk.sched_sleep != rcu_sched_sleep_prep) { + + /* + * Someone posted a callback after we scanned. + * Go take care of it. + */ + spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags); + couldsleepnext = 0; + continue; + } + + /* Block until the next person posts a callback. */ + + rcu_ctrlblk.sched_sleep = rcu_sched_sleeping; + spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags); + ret = 0; + __wait_event_interruptible(rcu_ctrlblk.sched_wq, + rcu_ctrlblk.sched_sleep != rcu_sched_sleeping, + ret); + + /* + * Signals would prevent us from sleeping, and we cannot + * do much with them in any case. So flush them. + */ + if (ret) + flush_signals(current); + couldsleepnext = 0; + + } while (!kthread_should_stop()); + + return (0); } -EXPORT_SYMBOL_GPL(__synchronize_sched); /* * Check to see if any future RCU-related work will need to be done @@ -1027,7 +1340,9 @@ int rcu_needs_cpu(int cpu) return (rdp->donelist != NULL || !!rdp->waitlistcount || - rdp->nextlist != NULL); + rdp->nextlist != NULL || + rdp->nextschedlist != NULL || + rdp->waitschedlist != NULL); } int rcu_pending(int cpu) @@ -1038,7 +1353,9 @@ int rcu_pending(int cpu) if (rdp->donelist != NULL || !!rdp->waitlistcount || - rdp->nextlist != NULL) + rdp->nextlist != NULL || + rdp->nextschedlist != NULL || + rdp->waitschedlist != NULL) return 1; /* The RCU core needs an acknowledgement from this CPU. */ @@ -1105,6 +1422,11 @@ void __init __rcu_init(void) rdp->donetail = &rdp->donelist; rdp->rcu_flipctr[0] = 0; rdp->rcu_flipctr[1] = 0; + rdp->nextschedlist = NULL; + rdp->nextschedtail = &rdp->nextschedlist; + rdp->waitschedlist = NULL; + rdp->waitschedtail = &rdp->waitschedlist; + rdp->rcu_sched_sleeping = 0; } register_cpu_notifier(&rcu_nb); @@ -1127,11 +1449,15 @@ void __init __rcu_init(void) } /* - * Deprecated, use synchronize_rcu() or synchronize_sched() instead. + * Late-boot-time RCU initialization that must wait until after scheduler + * has been initialized. */ -void synchronize_kernel(void) +void __init rcu_init_sched(void) { - synchronize_rcu(); + rcu_sched_grace_period_task = kthread_run(rcu_sched_grace_period, + NULL, + "rcu_sched_grace_period"); + WARN_ON(IS_ERR(rcu_sched_grace_period_task)); } #ifdef CONFIG_RCU_TRACE -- cgit v1.2.3 From 70f12f848d3e981479b4f6f751e73c14f7c13e5b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 12 May 2008 21:21:05 +0200 Subject: rcu: add rcu_barrier_sched() and rcu_barrier_bh() Add rcu_barrier_sched() and rcu_barrier_bh(). With these in place, rcutorture no longer gives the occasional oops when repeatedly starting and stopping torturing rcu_bh. Also adds the API needed to flush out pre-existing call_rcu_sched() callbacks. Signed-off-by: Paul E. McKenney Signed-off-by: Mathieu Desnoyers Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/rcupdate.h | 2 ++ kernel/rcupdate.c | 55 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 411969cb5243..e8b4039cfb2f 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -246,6 +246,8 @@ extern void call_rcu_bh(struct rcu_head *head, /* Exported common interfaces */ extern void synchronize_rcu(void); extern void rcu_barrier(void); +extern void rcu_barrier_bh(void); +extern void rcu_barrier_sched(void); /* Internal to kernel */ extern void rcu_init(void); diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index a4e329d92883..4a74b8d48d90 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -45,6 +45,12 @@ #include #include +enum rcu_barrier { + RCU_BARRIER_STD, + RCU_BARRIER_BH, + RCU_BARRIER_SCHED, +}; + static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL}; static atomic_t rcu_barrier_cpu_count; static DEFINE_MUTEX(rcu_barrier_mutex); @@ -83,19 +89,30 @@ static void rcu_barrier_callback(struct rcu_head *notused) /* * Called with preemption disabled, and from cross-cpu IRQ context. */ -static void rcu_barrier_func(void *notused) +static void rcu_barrier_func(void *type) { int cpu = smp_processor_id(); struct rcu_head *head = &per_cpu(rcu_barrier_head, cpu); atomic_inc(&rcu_barrier_cpu_count); - call_rcu(head, rcu_barrier_callback); + switch ((enum rcu_barrier)type) { + case RCU_BARRIER_STD: + call_rcu(head, rcu_barrier_callback); + break; + case RCU_BARRIER_BH: + call_rcu_bh(head, rcu_barrier_callback); + break; + case RCU_BARRIER_SCHED: + call_rcu_sched(head, rcu_barrier_callback); + break; + } } -/** - * rcu_barrier - Wait until all the in-flight RCUs are complete. +/* + * Orchestrate the specified type of RCU barrier, waiting for all + * RCU callbacks of the specified type to complete. */ -void rcu_barrier(void) +static void _rcu_barrier(enum rcu_barrier type) { BUG_ON(in_interrupt()); /* Take cpucontrol mutex to protect against CPU hotplug */ @@ -111,13 +128,39 @@ void rcu_barrier(void) * until all the callbacks are queued. */ rcu_read_lock(); - on_each_cpu(rcu_barrier_func, NULL, 0, 1); + on_each_cpu(rcu_barrier_func, (void *)type, 0, 1); rcu_read_unlock(); wait_for_completion(&rcu_barrier_completion); mutex_unlock(&rcu_barrier_mutex); } + +/** + * rcu_barrier - Wait until all in-flight call_rcu() callbacks complete. + */ +void rcu_barrier(void) +{ + _rcu_barrier(RCU_BARRIER_STD); +} EXPORT_SYMBOL_GPL(rcu_barrier); +/** + * rcu_barrier_bh - Wait until all in-flight call_rcu_bh() callbacks complete. + */ +void rcu_barrier_bh(void) +{ + _rcu_barrier(RCU_BARRIER_BH); +} +EXPORT_SYMBOL_GPL(rcu_barrier_bh); + +/** + * rcu_barrier_sched - Wait for in-flight call_rcu_sched() callbacks. + */ +void rcu_barrier_sched(void) +{ + _rcu_barrier(RCU_BARRIER_SCHED); +} +EXPORT_SYMBOL_GPL(rcu_barrier_sched); + void __init rcu_init(void) { __rcu_init(); -- cgit v1.2.3 From 82524746c27fa418c250a56dd7606b9d3fc79826 Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Mon, 12 May 2008 21:21:05 +0200 Subject: rcu: split list.h and move rcu-protected lists into rculist.h Move rcu-protected lists from list.h into a new header file rculist.h. This is done because list are a very used primitive structure all over the kernel and it's currently impossible to include other header files in this list.h without creating some circular dependencies. For example, list.h implements rcu-protected list and uses rcu_dereference() without including rcupdate.h. It actually compiles because users of rcu_dereference() are macros. Others RCU functions could be used too but aren't probably because of this. Therefore this patch creates rculist.h which includes rcupdates without to many changes/troubles. Signed-off-by: Franck Bui-Huu Acked-by: Paul E. McKenney Acked-by: Josh Triplett Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar --- arch/ia64/sn/kernel/irq.c | 1 + crypto/async_tx/async_tx.c | 1 + drivers/infiniband/hw/ipath/ipath_verbs.c | 1 + drivers/infiniband/hw/ipath/ipath_verbs_mcast.c | 3 +- drivers/net/macvlan.c | 2 +- include/linux/dcache.h | 1 + include/linux/list.h | 367 ---------------------- include/linux/rculist.h | 396 ++++++++++++++++++++++++ kernel/pid.c | 1 + lib/textsearch.c | 1 + net/802/psnap.c | 1 + net/8021q/vlan.c | 1 + net/bridge/br_fdb.c | 1 + net/bridge/br_stp.c | 1 + net/netlabel/netlabel_domainhash.c | 3 +- 15 files changed, 409 insertions(+), 372 deletions(-) create mode 100644 include/linux/rculist.h (limited to 'include/linux') diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index 53351c3cd7b1..96c31b4180c3 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index c6e772fc5ccd..095c798d3170 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -23,6 +23,7 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ +#include #include #include diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index e0ec540042bf..5d830d87ebca 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "ipath_kernel.h" #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c index 9e5abf9c309d..d73e32232879 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c @@ -31,8 +31,7 @@ * SOFTWARE. */ -#include -#include +#include #include "ipath_verbs.h" diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c36a03ae9bfb..860d75d81f82 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 2a6639407c80..1f5cebf10a23 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/list.h b/include/linux/list.h index 08cf4f651889..139ec41d9c2e 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -84,65 +84,6 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head) __list_add(new, head->prev, head); } -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add_rcu(struct list_head * new, - struct list_head * prev, struct list_head * next) -{ - new->next = next; - new->prev = prev; - smp_wmb(); - next->prev = new; - prev->next = new; -} - -/** - * list_add_rcu - add a new entry to rcu-protected list - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as list_add_rcu() - * or list_del_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * list_for_each_entry_rcu(). - */ -static inline void list_add_rcu(struct list_head *new, struct list_head *head) -{ - __list_add_rcu(new, head, head->next); -} - -/** - * list_add_tail_rcu - add a new entry to rcu-protected list - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as list_add_tail_rcu() - * or list_del_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * list_for_each_entry_rcu(). - */ -static inline void list_add_tail_rcu(struct list_head *new, - struct list_head *head) -{ - __list_add_rcu(new, head->prev, head); -} - /* * Delete a list entry by making the prev/next entries * point to each other. @@ -173,36 +114,6 @@ static inline void list_del(struct list_head *entry) extern void list_del(struct list_head *entry); #endif -/** - * list_del_rcu - deletes entry from list without re-initialization - * @entry: the element to delete from the list. - * - * Note: list_empty() on entry does not return true after this, - * the entry is in an undefined state. It is useful for RCU based - * lockfree traversal. - * - * In particular, it means that we can not poison the forward - * pointers that may still be used for walking the list. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as list_del_rcu() - * or list_add_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * list_for_each_entry_rcu(). - * - * Note that the caller is not permitted to immediately free - * the newly deleted entry. Instead, either synchronize_rcu() - * or call_rcu() must be used to defer freeing until an RCU - * grace period has elapsed. - */ -static inline void list_del_rcu(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - entry->prev = LIST_POISON2; -} - /** * list_replace - replace old entry by new one * @old : the element to be replaced @@ -226,25 +137,6 @@ static inline void list_replace_init(struct list_head *old, INIT_LIST_HEAD(old); } -/** - * list_replace_rcu - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert - * - * The @old entry will be replaced with the @new entry atomically. - * Note: @old should not be empty. - */ -static inline void list_replace_rcu(struct list_head *old, - struct list_head *new) -{ - new->next = old->next; - new->prev = old->prev; - smp_wmb(); - new->next->prev = new; - new->prev->next = new; - old->prev = LIST_POISON2; -} - /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. @@ -368,62 +260,6 @@ static inline void list_splice_init(struct list_head *list, } } -/** - * list_splice_init_rcu - splice an RCU-protected list into an existing list. - * @list: the RCU-protected list to splice - * @head: the place in the list to splice the first list into - * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ... - * - * @head can be RCU-read traversed concurrently with this function. - * - * Note that this function blocks. - * - * Important note: the caller must take whatever action is necessary to - * prevent any other updates to @head. In principle, it is possible - * to modify the list as soon as sync() begins execution. - * If this sort of thing becomes necessary, an alternative version - * based on call_rcu() could be created. But only if -really- - * needed -- there is no shortage of RCU API members. - */ -static inline void list_splice_init_rcu(struct list_head *list, - struct list_head *head, - void (*sync)(void)) -{ - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; - - if (list_empty(head)) - return; - - /* "first" and "last" tracking list, so initialize it. */ - - INIT_LIST_HEAD(list); - - /* - * At this point, the list body still points to the source list. - * Wait for any readers to finish using the list before splicing - * the list body into the new list. Any new readers will see - * an empty list. - */ - - sync(); - - /* - * Readers are finished with the source list, so perform splice. - * The order is important if the new list is global and accessible - * to concurrent RCU readers. Note that RCU readers are not - * permitted to traverse the prev pointers without excluding - * this function. - */ - - last->next = at; - smp_wmb(); - head->next = first; - first->prev = head; - at->prev = last; -} - /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. @@ -629,57 +465,6 @@ static inline void list_splice_init_rcu(struct list_head *list, &pos->member != (head); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) -/** - * list_for_each_rcu - iterate over an rcu-protected list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - * - * This list-traversal primitive may safely run concurrently with - * the _rcu list-mutation primitives such as list_add_rcu() - * as long as the traversal is guarded by rcu_read_lock(). - */ -#define list_for_each_rcu(pos, head) \ - for (pos = rcu_dereference((head)->next); \ - prefetch(pos->next), pos != (head); \ - pos = rcu_dereference(pos->next)) - -#define __list_for_each_rcu(pos, head) \ - for (pos = rcu_dereference((head)->next); \ - pos != (head); \ - pos = rcu_dereference(pos->next)) - -/** - * list_for_each_entry_rcu - iterate over rcu list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * This list-traversal primitive may safely run concurrently with - * the _rcu list-mutation primitives such as list_add_rcu() - * as long as the traversal is guarded by rcu_read_lock(). - */ -#define list_for_each_entry_rcu(pos, head, member) \ - for (pos = list_entry(rcu_dereference((head)->next), typeof(*pos), member); \ - prefetch(pos->member.next), &pos->member != (head); \ - pos = list_entry(rcu_dereference(pos->member.next), typeof(*pos), member)) - - -/** - * list_for_each_continue_rcu - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - * - * Iterate over an rcu-protected list, continuing after current point. - * - * This list-traversal primitive may safely run concurrently with - * the _rcu list-mutation primitives such as list_add_rcu() - * as long as the traversal is guarded by rcu_read_lock(). - */ -#define list_for_each_continue_rcu(pos, head) \ - for ((pos) = rcu_dereference((pos)->next); \ - prefetch((pos)->next), (pos) != (head); \ - (pos) = rcu_dereference((pos)->next)) - /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is @@ -730,31 +515,6 @@ static inline void hlist_del(struct hlist_node *n) n->pprev = LIST_POISON2; } -/** - * hlist_del_rcu - deletes entry from hash list without re-initialization - * @n: the element to delete from the hash list. - * - * Note: list_unhashed() on entry does not return true after this, - * the entry is in an undefined state. It is useful for RCU based - * lockfree traversal. - * - * In particular, it means that we can not poison the forward - * pointers that may still be used for walking the hash list. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as hlist_add_head_rcu() - * or hlist_del_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * hlist_for_each_entry(). - */ -static inline void hlist_del_rcu(struct hlist_node *n) -{ - __hlist_del(n); - n->pprev = LIST_POISON2; -} - static inline void hlist_del_init(struct hlist_node *n) { if (!hlist_unhashed(n)) { @@ -763,27 +523,6 @@ static inline void hlist_del_init(struct hlist_node *n) } } -/** - * hlist_replace_rcu - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert - * - * The @old entry will be replaced with the @new entry atomically. - */ -static inline void hlist_replace_rcu(struct hlist_node *old, - struct hlist_node *new) -{ - struct hlist_node *next = old->next; - - new->next = next; - new->pprev = old->pprev; - smp_wmb(); - if (next) - new->next->pprev = &new->next; - *new->pprev = new; - old->pprev = LIST_POISON2; -} - static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; @@ -794,38 +533,6 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) n->pprev = &h->first; } - -/** - * hlist_add_head_rcu - * @n: the element to add to the hash list. - * @h: the list to add to. - * - * Description: - * Adds the specified element to the specified hlist, - * while permitting racing traversals. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as hlist_add_head_rcu() - * or hlist_del_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * hlist_for_each_entry_rcu(), used to prevent memory-consistency - * problems on Alpha CPUs. Regardless of the type of CPU, the - * list-traversal primitive must be guarded by rcu_read_lock(). - */ -static inline void hlist_add_head_rcu(struct hlist_node *n, - struct hlist_head *h) -{ - struct hlist_node *first = h->first; - n->next = first; - n->pprev = &h->first; - smp_wmb(); - if (first) - first->pprev = &n->next; - h->first = n; -} - /* next must be != NULL */ static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) @@ -847,63 +554,6 @@ static inline void hlist_add_after(struct hlist_node *n, next->next->pprev = &next->next; } -/** - * hlist_add_before_rcu - * @n: the new element to add to the hash list. - * @next: the existing element to add the new element before. - * - * Description: - * Adds the specified element to the specified hlist - * before the specified node while permitting racing traversals. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as hlist_add_head_rcu() - * or hlist_del_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * hlist_for_each_entry_rcu(), used to prevent memory-consistency - * problems on Alpha CPUs. - */ -static inline void hlist_add_before_rcu(struct hlist_node *n, - struct hlist_node *next) -{ - n->pprev = next->pprev; - n->next = next; - smp_wmb(); - next->pprev = &n->next; - *(n->pprev) = n; -} - -/** - * hlist_add_after_rcu - * @prev: the existing element to add the new element after. - * @n: the new element to add to the hash list. - * - * Description: - * Adds the specified element to the specified hlist - * after the specified node while permitting racing traversals. - * - * The caller must take whatever precautions are necessary - * (such as holding appropriate locks) to avoid racing - * with another list-mutation primitive, such as hlist_add_head_rcu() - * or hlist_del_rcu(), running on this same list. - * However, it is perfectly legal to run concurrently with - * the _rcu list-traversal primitives, such as - * hlist_for_each_entry_rcu(), used to prevent memory-consistency - * problems on Alpha CPUs. - */ -static inline void hlist_add_after_rcu(struct hlist_node *prev, - struct hlist_node *n) -{ - n->next = prev->next; - n->pprev = &prev->next; - smp_wmb(); - prev->next = n; - if (n->next) - n->next->pprev = &n->next; -} - #define hlist_entry(ptr, type, member) container_of(ptr,type,member) #define hlist_for_each(pos, head) \ @@ -964,21 +614,4 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = n) -/** - * hlist_for_each_entry_rcu - iterate over rcu list of given type - * @tpos: the type * to use as a loop cursor. - * @pos: the &struct hlist_node to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the hlist_node within the struct. - * - * This list-traversal primitive may safely run concurrently with - * the _rcu list-mutation primitives such as hlist_add_head_rcu() - * as long as the traversal is guarded by rcu_read_lock(). - */ -#define hlist_for_each_entry_rcu(tpos, pos, head, member) \ - for (pos = rcu_dereference((head)->first); \ - pos && ({ prefetch(pos->next); 1;}) && \ - ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ - pos = rcu_dereference(pos->next)) - #endif diff --git a/include/linux/rculist.h b/include/linux/rculist.h new file mode 100644 index 000000000000..aa9b3eb15683 --- /dev/null +++ b/include/linux/rculist.h @@ -0,0 +1,396 @@ +#ifndef _LINUX_RCULIST_H +#define _LINUX_RCULIST_H + +#ifdef __KERNEL__ + +/* + * RCU-protected list version + */ +#include + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add_rcu(struct list_head *new, + struct list_head *prev, struct list_head *next) +{ + new->next = next; + new->prev = prev; + smp_wmb(); + next->prev = new; + prev->next = new; +} + +/** + * list_add_rcu - add a new entry to rcu-protected list + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_add_rcu() + * or list_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + */ +static inline void list_add_rcu(struct list_head *new, struct list_head *head) +{ + __list_add_rcu(new, head, head->next); +} + +/** + * list_add_tail_rcu - add a new entry to rcu-protected list + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_add_tail_rcu() + * or list_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + */ +static inline void list_add_tail_rcu(struct list_head *new, + struct list_head *head) +{ + __list_add_rcu(new, head->prev, head); +} + +/** + * list_del_rcu - deletes entry from list without re-initialization + * @entry: the element to delete from the list. + * + * Note: list_empty() on entry does not return true after this, + * the entry is in an undefined state. It is useful for RCU based + * lockfree traversal. + * + * In particular, it means that we can not poison the forward + * pointers that may still be used for walking the list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_del_rcu() + * or list_add_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + * + * Note that the caller is not permitted to immediately free + * the newly deleted entry. Instead, either synchronize_rcu() + * or call_rcu() must be used to defer freeing until an RCU + * grace period has elapsed. + */ +static inline void list_del_rcu(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->prev = LIST_POISON2; +} + +/** + * list_replace_rcu - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * The @old entry will be replaced with the @new entry atomically. + * Note: @old should not be empty. + */ +static inline void list_replace_rcu(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->prev = old->prev; + smp_wmb(); + new->next->prev = new; + new->prev->next = new; + old->prev = LIST_POISON2; +} + +/** + * list_splice_init_rcu - splice an RCU-protected list into an existing list. + * @list: the RCU-protected list to splice + * @head: the place in the list to splice the first list into + * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ... + * + * @head can be RCU-read traversed concurrently with this function. + * + * Note that this function blocks. + * + * Important note: the caller must take whatever action is necessary to + * prevent any other updates to @head. In principle, it is possible + * to modify the list as soon as sync() begins execution. + * If this sort of thing becomes necessary, an alternative version + * based on call_rcu() could be created. But only if -really- + * needed -- there is no shortage of RCU API members. + */ +static inline void list_splice_init_rcu(struct list_head *list, + struct list_head *head, + void (*sync)(void)) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + if (list_empty(head)) + return; + + /* "first" and "last" tracking list, so initialize it. */ + + INIT_LIST_HEAD(list); + + /* + * At this point, the list body still points to the source list. + * Wait for any readers to finish using the list before splicing + * the list body into the new list. Any new readers will see + * an empty list. + */ + + sync(); + + /* + * Readers are finished with the source list, so perform splice. + * The order is important if the new list is global and accessible + * to concurrent RCU readers. Note that RCU readers are not + * permitted to traverse the prev pointers without excluding + * this function. + */ + + last->next = at; + smp_wmb(); + head->next = first; + first->prev = head; + at->prev = last; +} + +/** + * list_for_each_rcu - iterate over an rcu-protected list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_rcu(pos, head) \ + for (pos = (head)->next; \ + prefetch(rcu_dereference(pos)->next), pos != (head); \ + pos = pos->next) + +#define __list_for_each_rcu(pos, head) \ + for (pos = (head)->next; \ + rcu_dereference(pos) != (head); \ + pos = pos->next) + +/** + * list_for_each_safe_rcu + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + * + * Iterate over an rcu-protected list, safe against removal of list entry. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next; \ + n = rcu_dereference(pos)->next, pos != (head); \ + pos = n) + +/** + * list_for_each_entry_rcu - iterate over rcu list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_entry_rcu(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + prefetch(rcu_dereference(pos)->member.next), \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + + +/** + * list_for_each_continue_rcu + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * Iterate over an rcu-protected list, continuing after current point. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next; \ + prefetch(rcu_dereference((pos))->next), (pos) != (head); \ + (pos) = (pos)->next) + +/** + * hlist_del_rcu - deletes entry from hash list without re-initialization + * @n: the element to delete from the hash list. + * + * Note: list_unhashed() on entry does not return true after this, + * the entry is in an undefined state. It is useful for RCU based + * lockfree traversal. + * + * In particular, it means that we can not poison the forward + * pointers that may still be used for walking the hash list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry(). + */ +static inline void hlist_del_rcu(struct hlist_node *n) +{ + __hlist_del(n); + n->pprev = LIST_POISON2; +} + +/** + * hlist_replace_rcu - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * The @old entry will be replaced with the @new entry atomically. + */ +static inline void hlist_replace_rcu(struct hlist_node *old, + struct hlist_node *new) +{ + struct hlist_node *next = old->next; + + new->next = next; + new->pprev = old->pprev; + smp_wmb(); + if (next) + new->next->pprev = &new->next; + *new->pprev = new; + old->pprev = LIST_POISON2; +} + +/** + * hlist_add_head_rcu + * @n: the element to add to the hash list. + * @h: the list to add to. + * + * Description: + * Adds the specified element to the specified hlist, + * while permitting racing traversals. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. Regardless of the type of CPU, the + * list-traversal primitive must be guarded by rcu_read_lock(). + */ +static inline void hlist_add_head_rcu(struct hlist_node *n, + struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + n->pprev = &h->first; + smp_wmb(); + if (first) + first->pprev = &n->next; + h->first = n; +} + +/** + * hlist_add_before_rcu + * @n: the new element to add to the hash list. + * @next: the existing element to add the new element before. + * + * Description: + * Adds the specified element to the specified hlist + * before the specified node while permitting racing traversals. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. + */ +static inline void hlist_add_before_rcu(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + smp_wmb(); + next->pprev = &n->next; + *(n->pprev) = n; +} + +/** + * hlist_add_after_rcu + * @prev: the existing element to add the new element after. + * @n: the new element to add to the hash list. + * + * Description: + * Adds the specified element to the specified hlist + * after the specified node while permitting racing traversals. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. + */ +static inline void hlist_add_after_rcu(struct hlist_node *prev, + struct hlist_node *n) +{ + n->next = prev->next; + n->pprev = &prev->next; + smp_wmb(); + prev->next = n; + if (n->next) + n->next->pprev = &n->next; +} + +/** + * hlist_for_each_entry_rcu - iterate over rcu list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as hlist_add_head_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define hlist_for_each_entry_rcu(tpos, pos, head, member) \ + for (pos = (head)->first; \ + rcu_dereference(pos) && ({ prefetch(pos->next); 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ + pos = pos->next) + +#endif /* __KERNEL__ */ +#endif diff --git a/kernel/pid.c b/kernel/pid.c index 20d59fa2d493..30bd5d4b2ac7 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/textsearch.c b/lib/textsearch.c index be8bda3862f5..a3e500ad51d7 100644 --- a/lib/textsearch.c +++ b/lib/textsearch.c @@ -97,6 +97,7 @@ #include #include #include +#include #include #include #include diff --git a/net/802/psnap.c b/net/802/psnap.c index 31128cb92a23..ea4643931446 100644 --- a/net/802/psnap.c +++ b/net/802/psnap.c @@ -20,6 +20,7 @@ #include #include #include +#include static LIST_HEAD(snap_list); static DEFINE_SPINLOCK(snap_lock); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 2a739adaa92b..e7ddbfa0e02f 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 72c5976a5ce3..142060f02054 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index e38034aa56f5..9e96ffcd29a3 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -13,6 +13,7 @@ * 2 of the License, or (at your option) any later version. */ #include +#include #include "br_private.h" #include "br_private_stp.h" diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 02c2f7c0b255..643c032a3a57 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -30,8 +30,7 @@ */ #include -#include -#include +#include #include #include #include -- cgit v1.2.3 From 10aa9d2cf9878757b003023d33ff90a37aa3044b Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Mon, 12 May 2008 21:21:06 +0200 Subject: rculist.h: use the rcu API Make almost all list mutation primitives use rcu_assign_pointer(). The main point of this being readability improvement. Signed-off-by: Franck Bui-Huu Cc: "Paul E. McKenney" Cc: Josh Triplett Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar --- include/linux/rculist.h | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index aa9b3eb15683..8d2c81fccfe5 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -7,6 +7,7 @@ * RCU-protected list version */ #include +#include /* * Insert a new entry between two known consecutive entries. @@ -19,9 +20,8 @@ static inline void __list_add_rcu(struct list_head *new, { new->next = next; new->prev = prev; - smp_wmb(); + rcu_assign_pointer(prev->next, new); next->prev = new; - prev->next = new; } /** @@ -110,9 +110,8 @@ static inline void list_replace_rcu(struct list_head *old, { new->next = old->next; new->prev = old->prev; - smp_wmb(); + rcu_assign_pointer(new->prev->next, new); new->next->prev = new; - new->prev->next = new; old->prev = LIST_POISON2; } @@ -166,8 +165,7 @@ static inline void list_splice_init_rcu(struct list_head *list, */ last->next = at; - smp_wmb(); - head->next = first; + rcu_assign_pointer(head->next, first); first->prev = head; at->prev = last; } @@ -280,10 +278,9 @@ static inline void hlist_replace_rcu(struct hlist_node *old, new->next = next; new->pprev = old->pprev; - smp_wmb(); + rcu_assign_pointer(*new->pprev, new); if (next) new->next->pprev = &new->next; - *new->pprev = new; old->pprev = LIST_POISON2; } @@ -310,12 +307,12 @@ static inline void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; + n->next = first; n->pprev = &h->first; - smp_wmb(); + rcu_assign_pointer(h->first, n); if (first) first->pprev = &n->next; - h->first = n; } /** @@ -341,9 +338,8 @@ static inline void hlist_add_before_rcu(struct hlist_node *n, { n->pprev = next->pprev; n->next = next; - smp_wmb(); + rcu_assign_pointer(*(n->pprev), n); next->pprev = &n->next; - *(n->pprev) = n; } /** @@ -369,8 +365,7 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, { n->next = prev->next; n->pprev = &prev->next; - smp_wmb(); - prev->next = n; + rcu_assign_pointer(prev->next, n); if (n->next) n->next->pprev = &n->next; } -- cgit v1.2.3 From 78b0e0e9b27b62c4b22f05a147f7a80fa58b1ae3 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 12 May 2008 21:21:06 +0200 Subject: RCU, rculist.h: fix list iterators RCU list iterators: should prefetch ever be optimised out with no side-effects, the current version will lose the barrier completely. Pointed-out-by: Linus Torvalds Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar --- include/linux/rculist.h | 48 +++++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 8d2c81fccfe5..b0f39be08b6c 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -180,31 +180,14 @@ static inline void list_splice_init_rcu(struct list_head *list, * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_rcu(pos, head) \ - for (pos = (head)->next; \ - prefetch(rcu_dereference(pos)->next), pos != (head); \ - pos = pos->next) + for (pos = rcu_dereference((head)->next); \ + prefetch(pos->next), pos != (head); \ + pos = rcu_dereference(pos->next)) #define __list_for_each_rcu(pos, head) \ - for (pos = (head)->next; \ - rcu_dereference(pos) != (head); \ - pos = pos->next) - -/** - * list_for_each_safe_rcu - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - * - * Iterate over an rcu-protected list, safe against removal of list entry. - * - * This list-traversal primitive may safely run concurrently with - * the _rcu list-mutation primitives such as list_add_rcu() - * as long as the traversal is guarded by rcu_read_lock(). - */ -#define list_for_each_safe_rcu(pos, n, head) \ - for (pos = (head)->next; \ - n = rcu_dereference(pos)->next, pos != (head); \ - pos = n) + for (pos = rcu_dereference((head)->next); \ + pos != (head); \ + pos = rcu_dereference(pos->next)) /** * list_for_each_entry_rcu - iterate over rcu list of given type @@ -217,10 +200,9 @@ static inline void list_splice_init_rcu(struct list_head *list, * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_entry_rcu(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - prefetch(rcu_dereference(pos)->member.next), \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) + for (pos = list_entry(rcu_dereference((head)->next), typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(rcu_dereference(pos->member.next), typeof(*pos), member)) /** @@ -235,9 +217,9 @@ static inline void list_splice_init_rcu(struct list_head *list, * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_continue_rcu(pos, head) \ - for ((pos) = (pos)->next; \ - prefetch(rcu_dereference((pos))->next), (pos) != (head); \ - (pos) = (pos)->next) + for ((pos) = rcu_dereference((pos)->next); \ + prefetch((pos)->next), (pos) != (head); \ + (pos) = rcu_dereference((pos)->next)) /** * hlist_del_rcu - deletes entry from hash list without re-initialization @@ -382,10 +364,10 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, * as long as the traversal is guarded by rcu_read_lock(). */ #define hlist_for_each_entry_rcu(tpos, pos, head, member) \ - for (pos = (head)->first; \ - rcu_dereference(pos) && ({ prefetch(pos->next); 1; }) && \ + for (pos = rcu_dereference((head)->first); \ + pos && ({ prefetch(pos->next); 1; }) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ - pos = pos->next) + pos = rcu_dereference(pos->next)) #endif /* __KERNEL__ */ #endif -- cgit v1.2.3 From 1a189b97190d3f0f8cf0379a799d3555b2d648bb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Apr 2008 21:41:55 +0100 Subject: [ARM] pxa: Add bare bones PWM API Signed-off-by: Russell King --- arch/arm/Kconfig | 3 +++ include/linux/pwm.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 include/linux/pwm.h (limited to 'include/linux') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b786e68914d4..c274dbb89a83 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -22,6 +22,9 @@ config ARM Europe. There is an ARM Linux project with a web page at . +config HAVE_PWM + bool + config SYS_SUPPORTS_APM_EMULATION bool diff --git a/include/linux/pwm.h b/include/linux/pwm.h new file mode 100644 index 000000000000..3945f803d514 --- /dev/null +++ b/include/linux/pwm.h @@ -0,0 +1,31 @@ +#ifndef __LINUX_PWM_H +#define __LINUX_PWM_H + +struct pwm_device; + +/* + * pwm_request - request a PWM device + */ +struct pwm_device *pwm_request(int pwm_id, const char *label); + +/* + * pwm_free - free a PWM device + */ +void pwm_free(struct pwm_device *pwm); + +/* + * pwm_config - change a PWM device configuration + */ +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); + +/* + * pwm_enable - start a PWM output toggling + */ +int pwm_enable(struct pwm_device *pwm); + +/* + * pwm_disable - stop a PWM output toggling + */ +void pwm_disable(struct pwm_device *pwm); + +#endif /* __ASM_ARCH_PWM_H */ -- cgit v1.2.3 From 48adcf148c83faa41999fb0b3524299c4e160fd9 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:03:16 +0300 Subject: [CPUFREQ] cpufreq: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Dave Jones --- arch/x86/kernel/cpu/cpufreq/powernow-k7.h | 1 - include/linux/cpufreq.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k7.h b/arch/x86/kernel/cpu/cpufreq/powernow-k7.h index f8a63b3664e3..35fb4eaf6e1c 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k7.h +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k7.h @@ -1,5 +1,4 @@ /* - * $Id: powernow-k7.h,v 1.2 2003/02/10 18:26:01 davej Exp $ * (C) 2003 Dave Jones. * * Licensed under the terms of the GNU GPL License version 2. diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index e7e91dbfde0f..2270ca5ec631 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -4,9 +4,6 @@ * Copyright (C) 2001 Russell King * (C) 2002 - 2003 Dominik Brodowski * - * - * $Id: cpufreq.h,v 1.36 2003/01/20 17:31:48 db Exp $ - * * 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. -- cgit v1.2.3 From d1659fcc59b21ec442564fedb67a5ad371f82380 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 12:17:39 -0400 Subject: Input: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Dmitry Torokhov --- Documentation/input/gameport-programming.txt | 2 -- Documentation/input/input.txt | 1 - Documentation/input/joystick-api.txt | 2 -- Documentation/input/joystick-parport.txt | 1 - Documentation/input/joystick.txt | 1 - drivers/input/evbug.c | 2 -- drivers/input/gameport/emu10k1-gp.c | 2 -- drivers/input/gameport/lightning.c | 2 -- drivers/input/gameport/ns558.c | 2 -- drivers/input/joystick/a3d.c | 2 -- drivers/input/joystick/amijoy.c | 2 -- drivers/input/joystick/cobra.c | 2 -- drivers/input/joystick/db9.c | 2 -- drivers/input/joystick/gf2k.c | 2 -- drivers/input/joystick/grip.c | 2 -- drivers/input/joystick/grip_mp.c | 2 -- drivers/input/joystick/guillemot.c | 2 -- drivers/input/joystick/iforce/iforce-ff.c | 2 -- drivers/input/joystick/iforce/iforce-main.c | 2 -- drivers/input/joystick/iforce/iforce-packets.c | 2 -- drivers/input/joystick/iforce/iforce-serio.c | 2 -- drivers/input/joystick/iforce/iforce-usb.c | 2 -- drivers/input/joystick/iforce/iforce.h | 2 -- drivers/input/joystick/interact.c | 2 -- drivers/input/joystick/joydump.c | 2 -- drivers/input/joystick/magellan.c | 2 -- drivers/input/joystick/spaceball.c | 2 -- drivers/input/joystick/spaceorb.c | 2 -- drivers/input/joystick/stinger.c | 2 -- drivers/input/joystick/tmdc.c | 2 -- drivers/input/joystick/turbografx.c | 2 -- drivers/input/joystick/twidjoy.c | 4 ---- drivers/input/joystick/warrior.c | 2 -- drivers/input/keyboard/amikbd.c | 2 -- drivers/input/keyboard/sunkbd.c | 2 -- drivers/input/keyboard/xtkbd.c | 2 -- drivers/input/mouse/inport.c | 2 -- drivers/input/mouse/logibm.c | 2 -- drivers/input/mouse/pc110pad.c | 2 -- drivers/input/mouse/sermouse.c | 2 -- drivers/input/serio/ct82c710.c | 2 -- drivers/input/serio/q40kbd.c | 2 -- drivers/input/serio/rpckbd.c | 2 -- drivers/input/touchscreen/gunze.c | 2 -- drivers/input/touchscreen/h3600_ts_input.c | 2 -- include/linux/joystick.h | 2 -- 46 files changed, 91 deletions(-) (limited to 'include/linux') diff --git a/Documentation/input/gameport-programming.txt b/Documentation/input/gameport-programming.txt index 14e0a8b70225..03a74fc3b496 100644 --- a/Documentation/input/gameport-programming.txt +++ b/Documentation/input/gameport-programming.txt @@ -1,5 +1,3 @@ -$Id: gameport-programming.txt,v 1.3 2001/04/24 13:51:37 vojtech Exp $ - Programming gameport drivers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/input/input.txt b/Documentation/input/input.txt index ff8cea0225f9..686ee9932dff 100644 --- a/Documentation/input/input.txt +++ b/Documentation/input/input.txt @@ -1,7 +1,6 @@ Linux Input drivers v1.0 (c) 1999-2001 Vojtech Pavlik Sponsored by SuSE - $Id: input.txt,v 1.8 2002/05/29 03:15:01 bradleym Exp $ ---------------------------------------------------------------------------- 0. Disclaimer diff --git a/Documentation/input/joystick-api.txt b/Documentation/input/joystick-api.txt index acbd32b88454..c507330740cd 100644 --- a/Documentation/input/joystick-api.txt +++ b/Documentation/input/joystick-api.txt @@ -5,8 +5,6 @@ 7 Aug 1998 - $Id: joystick-api.txt,v 1.2 2001/05/08 21:21:23 vojtech Exp $ - 1. Initialization ~~~~~~~~~~~~~~~~~ diff --git a/Documentation/input/joystick-parport.txt b/Documentation/input/joystick-parport.txt index ede5f33daad3..1c856f32ff2c 100644 --- a/Documentation/input/joystick-parport.txt +++ b/Documentation/input/joystick-parport.txt @@ -2,7 +2,6 @@ (c) 1998-2000 Vojtech Pavlik (c) 1998 Andree Borrmann Sponsored by SuSE - $Id: joystick-parport.txt,v 1.6 2001/09/25 09:31:32 vojtech Exp $ ---------------------------------------------------------------------------- 0. Disclaimer diff --git a/Documentation/input/joystick.txt b/Documentation/input/joystick.txt index 389de9bd9878..154d767b2acb 100644 --- a/Documentation/input/joystick.txt +++ b/Documentation/input/joystick.txt @@ -1,7 +1,6 @@ Linux Joystick driver v2.0.0 (c) 1996-2000 Vojtech Pavlik Sponsored by SuSE - $Id: joystick.txt,v 1.12 2002/03/03 12:13:07 jdeneux Exp $ ---------------------------------------------------------------------------- 0. Disclaimer diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index c21f2f127234..67754c776d88 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -1,6 +1,4 @@ /* - * $Id: evbug.c,v 1.10 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 9793ac36d17f..b04930f7ea7d 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -1,6 +1,4 @@ /* - * $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $ - * * Copyright (c) 2001 Vojtech Pavlik */ diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c index 6b4d4561d465..06ad36ed3483 100644 --- a/drivers/input/gameport/lightning.c +++ b/drivers/input/gameport/lightning.c @@ -1,6 +1,4 @@ /* - * $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index 7b7a546323cf..2b282cde4b89 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -1,6 +1,4 @@ /* - * $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * Copyright (c) 1999 Brian Gerst */ diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index 52ba16f487c7..92498d470b1f 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -1,6 +1,4 @@ /* - * $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index deb9f825f92c..05022f07ec77 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -1,6 +1,4 @@ /* - * $Id: amijoy.c,v 1.13 2002/01/22 20:26:32 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c index 55646a6d89f5..639b975a8ed7 100644 --- a/drivers/input/joystick/cobra.c +++ b/drivers/input/joystick/cobra.c @@ -1,6 +1,4 @@ /* - * $Id: cobra.c,v 1.19 2002/01/22 20:26:52 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 960e501c60c8..523959484753 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -1,6 +1,4 @@ /* - * $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index 1f6302c0eb3f..cb6eef1f2d99 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -1,6 +1,4 @@ /* - * $Id: gf2k.c,v 1.19 2002/01/22 20:27:43 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c index fd3853ab1aad..684e07cfccc8 100644 --- a/drivers/input/joystick/grip.c +++ b/drivers/input/joystick/grip.c @@ -1,6 +1,4 @@ /* - * $Id: grip.c,v 1.21 2002/01/22 20:27:57 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index c57e21d68c00..8279481b16e7 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -1,6 +1,4 @@ /* - * $Id: grip_mp.c,v 1.9 2002/07/20 19:28:45 bonnland Exp $ - * * Driver for the Gravis Grip Multiport, a gamepad "hub" that * connects up to four 9-pin digital gamepads/joysticks. * Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5. diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c index aa6bfb3fb8cd..25ec3fad9f27 100644 --- a/drivers/input/joystick/guillemot.c +++ b/drivers/input/joystick/guillemot.c @@ -1,6 +1,4 @@ /* - * $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $ - * * Copyright (c) 2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index f2a4381d0ab8..7839b7b6fa96 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index a2517fa72eb8..61ee6e38739d 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 45c4939ced75..015b50aa76fc 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index 7b4bc19cef27..46d5041d2d9d 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2001, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 8f6a987ebb9f..851cc4087c2f 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index a964a7cfd210..f2d91f4028ca 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -1,6 +1,4 @@ /* - * $Id: iforce.h,v 1.13 2002/07/07 10:22:50 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c index bc8ea95dfd0e..8c3290b68205 100644 --- a/drivers/input/joystick/interact.c +++ b/drivers/input/joystick/interact.c @@ -1,6 +1,4 @@ /* - * $Id: interact.c,v 1.16 2002/01/22 20:28:25 vojtech Exp $ - * * Copyright (c) 2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c index 88ec5a918f2e..2a1b82c8b31c 100644 --- a/drivers/input/joystick/joydump.c +++ b/drivers/input/joystick/joydump.c @@ -1,6 +1,4 @@ /* - * $Id: joydump.c,v 1.1 2002/01/23 06:56:16 jsimmons Exp $ - * * Copyright (c) 1996-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c index 54e676948ebb..40e40780747d 100644 --- a/drivers/input/joystick/magellan.c +++ b/drivers/input/joystick/magellan.c @@ -1,6 +1,4 @@ /* - * $Id: magellan.c,v 1.16 2002/01/22 20:28:39 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index d4087fd49656..0cd9b29356a8 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -1,6 +1,4 @@ /* - * $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index f7ce4004f4ba..a694bf8e557b 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -1,6 +1,4 @@ /* - * $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c index baa10b2f7ba1..e0db9f5e4b41 100644 --- a/drivers/input/joystick/stinger.c +++ b/drivers/input/joystick/stinger.c @@ -1,6 +1,4 @@ /* - * $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000 Mark Fletcher */ diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index 0feeb8acb532..60c37bcb938d 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -1,6 +1,4 @@ /* - * $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index 989483f53160..b6f859869540 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -1,6 +1,4 @@ /* - * $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c index 1085c841fec4..3f4ec73c9553 100644 --- a/drivers/input/joystick/twidjoy.c +++ b/drivers/input/joystick/twidjoy.c @@ -1,8 +1,4 @@ /* - * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $ - * - * derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp" - * * Copyright (c) 2001 Arndt Schoenewald * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000 Mark Fletcher diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c index e928b6e3724a..f72c83e15e60 100644 --- a/drivers/input/joystick/warrior.c +++ b/drivers/input/joystick/warrior.c @@ -1,6 +1,4 @@ /* - * $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index 81bf7562aca0..35149ec455a9 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -1,6 +1,4 @@ /* - * $Id: amikbd.c,v 1.13 2002/02/01 16:02:24 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index be0f5d19d023..9fce6d1e29b2 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -1,6 +1,4 @@ /* - * $Id: sunkbd.c,v 1.14 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c index 152a2c070508..37b01d777a4a 100644 --- a/drivers/input/keyboard/xtkbd.c +++ b/drivers/input/keyboard/xtkbd.c @@ -1,6 +1,4 @@ /* - * $Id: xtkbd.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c index 06c35fc553c0..3827a22362de 100644 --- a/drivers/input/mouse/inport.c +++ b/drivers/input/mouse/inport.c @@ -1,6 +1,4 @@ /* - * $Id: inport.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c index 9ea895593b27..e2413113df22 100644 --- a/drivers/input/mouse/logibm.c +++ b/drivers/input/mouse/logibm.c @@ -1,6 +1,4 @@ /* - * $Id: logibm.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index 61cff8374e6c..fd09c8df81f2 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -1,6 +1,4 @@ /* - * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index ed917bfd086a..17ff137b9bd5 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -1,6 +1,4 @@ /* - * $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 0d35018c23a9..d1380fc72cc6 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -1,6 +1,4 @@ /* - * $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index cb89aff2e160..eca125c8d535 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -1,6 +1,4 @@ /* - * $Id: q40kbd.c,v 1.12 2002/02/02 22:26:44 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 34c59d9c6205..1567b7782478 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -1,6 +1,4 @@ /* - * $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2002 Russell King */ diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index a48a15868c4a..a54f90e02ab6 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -1,6 +1,4 @@ /* - * $Id: gunze.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik */ diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index 28ae15ed12c5..4f86081dc7fc 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -1,6 +1,4 @@ /* - * $Id: h3600_ts_input.c,v 1.4 2002/01/23 06:39:37 jsimmons Exp $ - * * Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com * * Sponsored by Transvirtual Technology. diff --git a/include/linux/joystick.h b/include/linux/joystick.h index e2d3a18af456..b5e051295a67 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -2,8 +2,6 @@ #define _LINUX_JOYSTICK_H /* - * $Id: joystick.h,v 1.3 2000/11/30 11:07:05 vojtech Exp $ - * * Copyright (C) 1996-2000 Vojtech Pavlik * * Sponsored by SuSE -- cgit v1.2.3 From 59f0c4523fdea865fab7d69d878269992a9d08dd Mon Sep 17 00:00:00 2001 From: Allan Stephens Date: Wed, 21 May 2008 14:52:30 -0700 Subject: tipc: Fix skb_under_panic when configuring TIPC without privileges This patch prevents a TIPC configuration command requiring network administrator privileges from triggering an skbuff underrun if it is issued by a process lacking those privileges. The revised error handling code avoids the use of a potentially uninitialized global variable by transforming the unauthorized command into a new command, then following the standard command processing path to generate the required error message. Signed-off-by: Allan Stephens Signed-off-by: David S. Miller --- include/linux/tipc_config.h | 10 +++++++++- net/tipc/config.c | 6 +++++- net/tipc/netlink.c | 16 +++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index b0c916d1f375..2bc6fa4adeb5 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -2,7 +2,7 @@ * include/linux/tipc_config.h: Include file for TIPC configuration interface * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -135,6 +135,14 @@ #define TIPC_CMD_SET_MAX_SLAVES 0x800A /* tx unsigned, rx none */ #define TIPC_CMD_SET_NETID 0x800B /* tx unsigned, rx none */ +/* + * Reserved commands: + * May not be issued by any process. + * Used internally by TIPC. + */ + +#define TIPC_CMD_NOT_NET_ADMIN 0xC001 /* tx none, rx none */ + /* * TLV types defined for TIPC */ diff --git a/net/tipc/config.c b/net/tipc/config.c index 91d56f8fee9f..16e7cb74969b 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -2,7 +2,7 @@ * net/tipc/config.c: TIPC configuration management code * * Copyright (c) 2002-2006, Ericsson AB - * Copyright (c) 2004-2006, Wind River Systems + * Copyright (c) 2004-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -602,6 +602,10 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_GET_NETID: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); break; + case TIPC_CMD_NOT_NET_ADMIN: + rep_tlv_buf = + tipc_cfg_reply_error_string(TIPC_CFG_NOT_NET_ADMIN); + break; default: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (unknown command)"); diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 6a7f7b4c2595..c387217bb230 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -2,7 +2,7 @@ * net/tipc/netlink.c: TIPC configuration handling * * Copyright (c) 2005-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,15 +45,17 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) struct nlmsghdr *req_nlh = info->nlhdr; struct tipc_genlmsghdr *req_userhdr = info->userhdr; int hdr_space = NLMSG_SPACE(GENL_HDRLEN + TIPC_GENL_HDRLEN); + u16 cmd; if ((req_userhdr->cmd & 0xC000) && (!capable(CAP_NET_ADMIN))) - rep_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_NET_ADMIN); + cmd = TIPC_CMD_NOT_NET_ADMIN; else - rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, - req_userhdr->cmd, - NLMSG_DATA(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, - NLMSG_PAYLOAD(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), - hdr_space); + cmd = req_userhdr->cmd; + + rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, cmd, + NLMSG_DATA(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, + NLMSG_PAYLOAD(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), + hdr_space); if (rep_buf) { skb_push(rep_buf, hdr_space); -- cgit v1.2.3 From edcdf8b21ac920e06b4180246123fe43b022e020 Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Thu, 15 May 2008 13:53:55 +0800 Subject: mac80211: separate Tx and Rx MCS when configuring HT This patch follows the 11n spec in separation between Tx and Rx MCS capabilities. Up until now, when configuring the HT possible set of Tx MCS only Rx MCS were considered, assuming they are the same as the Tx MCS. This patch fixed this by looking at low level driver Tx capabilities. Signed-off-by: Ron Rindjunsky Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl4965-base.c | 14 +++-- include/linux/ieee80211.h | 18 ++++-- net/mac80211/main.c | 96 ++++++++++++++++++----------- 3 files changed, 83 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 1a2d3768867d..c713b27ec653 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -3958,6 +3958,13 @@ static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv) iwl_free_fw_desc(priv->pci_dev, &priv->ucode_boot); } +static void iwl4965_nic_start(struct iwl_priv *priv) +{ + /* Remove all resets to allow NIC to operate */ + iwl_write32(priv, CSR_RESET, 0); +} + + /** * iwl4965_read_ucode - Read uCode images from disk file. * @@ -4447,8 +4454,7 @@ static int __iwl4965_up(struct iwl_priv *priv) } /* start card; "initialize" will load runtime ucode */ - /* Remove all resets to allow NIC to operate */ - iwl_write32(priv, CSR_RESET, 0); + iwl4965_nic_start(priv); IWL_DEBUG_INFO(DRV_NAME " is coming up\n"); @@ -6842,10 +6848,6 @@ static int __init iwl4965_init(void) return ret; - -#ifdef CONFIG_IWLWIFI_DEBUG - pci_unregister_driver(&iwl_driver); -#endif error_register: iwl4965_rate_control_unregister(); return ret; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a9102bc78b61..3c2ac0c37aa8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -306,8 +306,18 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_SGI_40 0x0040 #define IEEE80211_HT_CAP_DELAY_BA 0x0400 #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 +/* 802.11n HT capability AMPDU settings */ #define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03 #define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C +/* 802.11n HT capability MSC set */ +#define IEEE80211_SUPP_MCS_SET_UEQM 4 +#define IEEE80211_HT_CAP_MAX_STREAMS 4 +#define IEEE80211_SUPP_MCS_SET_LEN 10 +/* maximum streams the spec allows */ +#define IEEE80211_HT_CAP_MCS_TX_DEFINED 0x01 +#define IEEE80211_HT_CAP_MCS_TX_RX_DIFF 0x02 +#define IEEE80211_HT_CAP_MCS_TX_STREAMS 0x0C +#define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10 /* 802.11n HT IE masks */ #define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03 #define IEEE80211_HT_IE_CHA_WIDTH 0x04 @@ -316,10 +326,10 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010 /* MIMO Power Save Modes */ -#define WLAN_HT_CAP_MIMO_PS_STATIC 0 -#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1 -#define WLAN_HT_CAP_MIMO_PS_INVALID 2 -#define WLAN_HT_CAP_MIMO_PS_DISABLED 3 +#define WLAN_HT_CAP_MIMO_PS_STATIC 0 +#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1 +#define WLAN_HT_CAP_MIMO_PS_INVALID 2 +#define WLAN_HT_CAP_MIMO_PS_DISABLED 3 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 36016363d225..b0fddb7de549 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -35,8 +35,6 @@ #include "debugfs.h" #include "debugfs_netdev.h" -#define SUPP_MCS_SET_LEN 16 - /* * For seeing transmitted packets on monitor interfaces * we have a radiotap header too. @@ -1068,56 +1066,84 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht, struct ieee80211_supported_band *sband; struct ieee80211_ht_info ht_conf; struct ieee80211_ht_bss_info ht_bss_conf; - int i; u32 changed = 0; + int i; + u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS; + u8 tx_mcs_set_cap; sband = local->hw.wiphy->bands[conf->channel->band]; + memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info)); + memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info)); + /* HT is not supported */ if (!sband->ht_info.ht_supported) { conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; - return 0; + goto out; } - memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info)); - memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info)); - - if (enable_ht) { - if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) + /* disable HT */ + if (!enable_ht) { + if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) changed |= BSS_CHANGED_HT; + conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; + conf->ht_conf.ht_supported = 0; + goto out; + } - conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE; - ht_conf.ht_supported = 1; - ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap; - ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); - ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) + changed |= BSS_CHANGED_HT; - for (i = 0; i < SUPP_MCS_SET_LEN; i++) - ht_conf.supp_mcs_set[i] = - sband->ht_info.supp_mcs_set[i] & - req_ht_cap->supp_mcs_set[i]; + conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE; + ht_conf.ht_supported = 1; - ht_bss_conf.primary_channel = req_bss_cap->primary_channel; - ht_bss_conf.bss_cap = req_bss_cap->bss_cap; - ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; + ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap; + ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); + ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; + ht_bss_conf.primary_channel = req_bss_cap->primary_channel; + ht_bss_conf.bss_cap = req_bss_cap->bss_cap; + ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; - ht_conf.ampdu_factor = req_ht_cap->ampdu_factor; - ht_conf.ampdu_density = req_ht_cap->ampdu_density; + ht_conf.ampdu_factor = req_ht_cap->ampdu_factor; + ht_conf.ampdu_density = req_ht_cap->ampdu_density; - /* if bss configuration changed store the new one */ - if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) || - memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) { - changed |= BSS_CHANGED_HT; - memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf)); - memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf)); - } - } else { - if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) - changed |= BSS_CHANGED_HT; - conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; - } + /* Bits 96-100 */ + tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12]; + /* configure suppoerted Tx MCS according to requested MCS + * (based in most cases on Rx capabilities of peer) and self + * Tx MCS capabilities (as defined by low level driver HW + * Tx capabilities) */ + if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED)) + goto check_changed; + + /* Counting from 0 therfore + 1 */ + if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF) + max_tx_streams = ((tx_mcs_set_cap & + IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1; + + for (i = 0; i < max_tx_streams; i++) + ht_conf.supp_mcs_set[i] = + sband->ht_info.supp_mcs_set[i] & + req_ht_cap->supp_mcs_set[i]; + + if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM) + for (i = IEEE80211_SUPP_MCS_SET_UEQM; + i < IEEE80211_SUPP_MCS_SET_LEN; i++) + ht_conf.supp_mcs_set[i] = + sband->ht_info.supp_mcs_set[i] & + req_ht_cap->supp_mcs_set[i]; + +check_changed: + /* if bss configuration changed store the new one */ + if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) || + memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) { + changed |= BSS_CHANGED_HT; + memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf)); + memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf)); + } +out: return changed; } -- cgit v1.2.3 From 41df0d61c266998b8049df7fec119cd518a43aa1 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 12 May 2008 21:21:13 +0200 Subject: x86: Add performance variants of cpumask operators * Increase performance for systems with large count NR_CPUS by limiting the range of the cpumask operators that loop over the bits in a cpumask_t variable. This removes a large amount of wasted cpu cycles. * Add performance variants of the cpumask operators: int cpus_weight_nr(mask) Same using nr_cpu_ids instead of NR_CPUS int first_cpu_nr(mask) Number lowest set bit, or nr_cpu_ids int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids for_each_cpu_mask_nr(cpu, mask) for-loop cpu over mask using nr_cpu_ids * Modify following to use performance variants: #define num_online_cpus() cpus_weight_nr(cpu_online_map) #define num_possible_cpus() cpus_weight_nr(cpu_possible_map) #define num_present_cpus() cpus_weight_nr(cpu_present_map) #define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), ...) #define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), ...) #define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), ...) * Comment added to include/linux/cpumask.h: Note: The alternate operations with the suffix "_nr" are used to limit the range of the loop to nr_cpu_ids instead of NR_CPUS when NR_CPUS > 64 for performance reasons. If NR_CPUS is <= 64 then most assembler bitmask operators execute faster with a constant range, so the operator will continue to use NR_CPUS. Another consideration is that nr_cpu_ids is initialized to NR_CPUS and isn't lowered until the possible cpus are discovered (including any disabled cpus). So early uses will span the entire range of NR_CPUS. (The net effect is that for systems with 64 or less CPU's there are no functional changes.) For inclusion into sched-devel/latest tree. Based on: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + sched-devel/latest .../mingo/linux-2.6-sched-devel.git Cc: Paul Jackson Cc: Christoph Lameter Reviewed-by: Paul Jackson Reviewed-by: Christoph Lameter Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 92 +++++++++++++++++++++++++++++++++---------------- lib/cpumask.c | 9 +++++ 2 files changed, 71 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 5df3db58fcc6..b49472d1af84 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -17,6 +17,20 @@ * For details of cpus_onto(), see bitmap_onto in lib/bitmap.c. * For details of cpus_fold(), see bitmap_fold in lib/bitmap.c. * + * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + * Note: The alternate operations with the suffix "_nr" are used + * to limit the range of the loop to nr_cpu_ids instead of + * NR_CPUS when NR_CPUS > 64 for performance reasons. + * If NR_CPUS is <= 64 then most assembler bitmask + * operators execute faster with a constant range, so + * the operator will continue to use NR_CPUS. + * + * Another consideration is that nr_cpu_ids is initialized + * to NR_CPUS and isn't lowered until the possible cpus are + * discovered (including any disabled cpus). So early uses + * will span the entire range of NR_CPUS. + * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + * * The available cpumask operations are: * * void cpu_set(cpu, mask) turn on bit 'cpu' in mask @@ -38,12 +52,14 @@ * int cpus_empty(mask) Is mask empty (no bits sets)? * int cpus_full(mask) Is mask full (all bits sets)? * int cpus_weight(mask) Hamming weigh - number of set bits + * int cpus_weight_nr(mask) Same using nr_cpu_ids instead of NR_CPUS * * void cpus_shift_right(dst, src, n) Shift right * void cpus_shift_left(dst, src, n) Shift left * * int first_cpu(mask) Number lowest set bit, or NR_CPUS * int next_cpu(cpu, mask) Next cpu past 'cpu', or NR_CPUS + * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids * * cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set * CPU_MASK_ALL Initializer - all bits set @@ -59,7 +75,8 @@ * void cpus_onto(dst, orig, relmap) *dst = orig relative to relmap * void cpus_fold(dst, orig, sz) dst bits = orig bits mod sz * - * for_each_cpu_mask(cpu, mask) for-loop cpu over mask + * for_each_cpu_mask(cpu, mask) for-loop cpu over mask using NR_CPUS + * for_each_cpu_mask_nr(cpu, mask) for-loop cpu over mask using nr_cpu_ids * * int num_online_cpus() Number of online CPUs * int num_possible_cpus() Number of all possible CPUs @@ -216,15 +233,6 @@ static inline void __cpus_shift_left(cpumask_t *dstp, bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } -#ifdef CONFIG_SMP -int __first_cpu(const cpumask_t *srcp); -#define first_cpu(src) __first_cpu(&(src)) -int __next_cpu(int n, const cpumask_t *srcp); -#define next_cpu(n, src) __next_cpu((n), &(src)) -#else -#define first_cpu(src) ({ (void)(src); 0; }) -#define next_cpu(n, src) ({ (void)(src); 1; }) -#endif #ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP extern cpumask_t *cpumask_of_cpu_map; @@ -343,15 +351,48 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, bitmap_fold(dstp->bits, origp->bits, sz, nbits); } -#if NR_CPUS > 1 +#if NR_CPUS == 1 + +#define nr_cpu_ids 1 +#define first_cpu(src) ({ (void)(src); 0; }) +#define next_cpu(n, src) ({ (void)(src); 1; }) +#define any_online_cpu(mask) 0 +#define for_each_cpu_mask(cpu, mask) \ + for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) + +#else /* NR_CPUS > 1 */ + +extern int nr_cpu_ids; +int __first_cpu(const cpumask_t *srcp); +int __next_cpu(int n, const cpumask_t *srcp); +int __any_online_cpu(const cpumask_t *mask); + +#define first_cpu(src) __first_cpu(&(src)) +#define next_cpu(n, src) __next_cpu((n), &(src)) +#define any_online_cpu(mask) __any_online_cpu(&(mask)) #define for_each_cpu_mask(cpu, mask) \ for ((cpu) = first_cpu(mask); \ (cpu) < NR_CPUS; \ (cpu) = next_cpu((cpu), (mask))) -#else /* NR_CPUS == 1 */ -#define for_each_cpu_mask(cpu, mask) \ - for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) -#endif /* NR_CPUS */ +#endif + +#if NR_CPUS <= 64 + +#define next_cpu_nr(n, src) next_cpu(n, src) +#define cpus_weight_nr(cpumask) cpus_weight(cpumask) +#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) + +#else /* NR_CPUS > 64 */ + +int __next_cpu_nr(int n, const cpumask_t *srcp); +#define next_cpu_nr(n, src) __next_cpu_nr((n), &(src)) +#define cpus_weight_nr(cpumask) __cpus_weight(&(cpumask), nr_cpu_ids) +#define for_each_cpu_mask_nr(cpu, mask) \ + for ((cpu) = first_cpu(mask); \ + (cpu) < nr_cpu_ids; \ + (cpu) = next_cpu_nr((cpu), (mask))) + +#endif /* NR_CPUS > 64 */ /* * The following particular system cpumasks and operations manage @@ -414,9 +455,9 @@ extern cpumask_t cpu_online_map; extern cpumask_t cpu_present_map; #if NR_CPUS > 1 -#define num_online_cpus() cpus_weight(cpu_online_map) -#define num_possible_cpus() cpus_weight(cpu_possible_map) -#define num_present_cpus() cpus_weight(cpu_present_map) +#define num_online_cpus() cpus_weight_nr(cpu_online_map) +#define num_possible_cpus() cpus_weight_nr(cpu_possible_map) +#define num_present_cpus() cpus_weight_nr(cpu_present_map) #define cpu_online(cpu) cpu_isset((cpu), cpu_online_map) #define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map) #define cpu_present(cpu) cpu_isset((cpu), cpu_present_map) @@ -431,17 +472,8 @@ extern cpumask_t cpu_present_map; #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) -#ifdef CONFIG_SMP -extern int nr_cpu_ids; -#define any_online_cpu(mask) __any_online_cpu(&(mask)) -int __any_online_cpu(const cpumask_t *mask); -#else -#define nr_cpu_ids 1 -#define any_online_cpu(mask) 0 -#endif - -#define for_each_possible_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map) -#define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map) -#define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map) +#define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_possible_map) +#define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_online_map) +#define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_present_map) #endif /* __LINUX_CPUMASK_H */ diff --git a/lib/cpumask.c b/lib/cpumask.c index bb4f76d3c3e7..5f97dc25ef9c 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -15,6 +15,15 @@ int __next_cpu(int n, const cpumask_t *srcp) } EXPORT_SYMBOL(__next_cpu); +#if NR_CPUS > 64 +int __next_cpu_nr(int n, const cpumask_t *srcp) +{ + return min_t(int, nr_cpu_ids, + find_next_bit(srcp->bits, nr_cpu_ids, n+1)); +} +EXPORT_SYMBOL(__next_cpu_nr); +#endif + int __any_online_cpu(const cpumask_t *mask) { int cpu; -- cgit v1.2.3 From 7baac8b91f9871ba8cb09af84de4ae1d86d07812 Mon Sep 17 00:00:00 2001 From: Alexander van Heukelum Date: Tue, 13 May 2008 11:28:21 +0200 Subject: cpumask: make for_each_cpu_mask a bit smaller The for_each_cpu_mask loop is used quite often in the kernel. It makes use of two functions: first_cpu and next_cpu. This patch changes for_each_cpu_mask to use only the latter. Because next_cpu finds the next eligible cpu _after_ the given one, the iteration variable has to be initialized to -1 and next_cpu has to be called with this value before the first iteration. An x86_64 defconfig kernel (from sched/latest) is about 2500 bytes smaller with this patch applied: text data bss dec hex filename 6222517 917952 749932 7890401 7865e1 vmlinux.orig 6219922 917952 749932 7887806 785bbe vmlinux The same size reduction is seen for defconfig+MAXSMP text data bss dec hex filename 6241772 2563968 1492716 10298456 9d2458 vmlinux.orig 6239211 2563968 1492716 10295895 9d1a57 vmlinux Signed-off-by: Alexander van Heukelum Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/cpumask.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index b49472d1af84..80226e776143 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -370,10 +370,10 @@ int __any_online_cpu(const cpumask_t *mask); #define first_cpu(src) __first_cpu(&(src)) #define next_cpu(n, src) __next_cpu((n), &(src)) #define any_online_cpu(mask) __any_online_cpu(&(mask)) -#define for_each_cpu_mask(cpu, mask) \ - for ((cpu) = first_cpu(mask); \ - (cpu) < NR_CPUS; \ - (cpu) = next_cpu((cpu), (mask))) +#define for_each_cpu_mask(cpu, mask) \ + for ((cpu) = -1; \ + (cpu) = next_cpu((cpu), (mask)), \ + (cpu) < NR_CPUS; ) #endif #if NR_CPUS <= 64 @@ -387,10 +387,10 @@ int __any_online_cpu(const cpumask_t *mask); int __next_cpu_nr(int n, const cpumask_t *srcp); #define next_cpu_nr(n, src) __next_cpu_nr((n), &(src)) #define cpus_weight_nr(cpumask) __cpus_weight(&(cpumask), nr_cpu_ids) -#define for_each_cpu_mask_nr(cpu, mask) \ - for ((cpu) = first_cpu(mask); \ - (cpu) < nr_cpu_ids; \ - (cpu) = next_cpu_nr((cpu), (mask))) +#define for_each_cpu_mask_nr(cpu, mask) \ + for ((cpu) = -1; \ + (cpu) = next_cpu_nr((cpu), (mask)), \ + (cpu) < nr_cpu_ids; ) #endif /* NR_CPUS > 64 */ -- cgit v1.2.3 From bd3bff9e20f454b242d979ec2f9a4dca0d5fa06f Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:41 +0200 Subject: sched: add latency tracer callbacks to the scheduler add 3 lightweight callbacks to the tracer backend. zero impact if tracing is turned off. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 26 ++++++++++++++++++++++++++ kernel/sched.c | 3 +++ 2 files changed, 29 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5395a6176f4b..717cab8a0c83 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2117,6 +2117,32 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) } #endif +#ifdef CONFIG_CONTEXT_SWITCH_TRACER +extern void +ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next); +#else +static inline void +ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next) +{ +} +#endif + +#ifdef CONFIG_SCHED_TRACER +extern void +ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr); +extern void +ftrace_wake_up_new_task(struct task_struct *wakee, struct task_struct *curr); +#else +static inline void +ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr) +{ +} +static inline void +ftrace_wake_up_new_task(struct task_struct *wakee, struct task_struct *curr) +{ +} +#endif + extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask); extern long sched_getaffinity(pid_t pid, cpumask_t *mask); diff --git a/kernel/sched.c b/kernel/sched.c index cfa222a91539..463dcdb36ef8 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2467,6 +2467,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync) out_activate: #endif /* CONFIG_SMP */ + ftrace_wake_up_task(p, rq->curr); schedstat_inc(p, se.nr_wakeups); if (sync) schedstat_inc(p, se.nr_wakeups_sync); @@ -2611,6 +2612,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) p->sched_class->task_new(rq, p); inc_nr_running(rq); } + ftrace_wake_up_new_task(p, rq->curr); check_preempt_curr(rq, p); #ifdef CONFIG_SMP if (p->sched_class->task_wake_up) @@ -2783,6 +2785,7 @@ context_switch(struct rq *rq, struct task_struct *prev, struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); + ftrace_ctx_switch(prev, next); mm = next->mm; oldmm = prev->active_mm; /* -- cgit v1.2.3 From 7c731e0a495e25e79dc1e9e68772a67a55721a65 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:41 +0200 Subject: ftrace: make the task state char-string visible to all The tracer wants to be able to convert the state number into a user visible character. This patch pulls that conversion string out the scheduler into the header. This way if it were to ever change, other parts of the kernel will know. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 2 ++ kernel/sched.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 717cab8a0c83..6e26f1fdbfe2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2237,6 +2237,8 @@ static inline void mm_init_owner(struct mm_struct *mm, struct task_struct *p) } #endif /* CONFIG_MM_OWNER */ +#define TASK_STATE_TO_CHAR_STR "RSDTtZX" + #endif /* __KERNEL__ */ #endif diff --git a/kernel/sched.c b/kernel/sched.c index 463dcdb36ef8..73e600852365 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5729,7 +5729,7 @@ out_unlock: return retval; } -static const char stat_nam[] = "RSDTtZX"; +static const char stat_nam[] = TASK_STATE_TO_CHAR_STR; void sched_show_task(struct task_struct *p) { -- cgit v1.2.3 From 502825282e6f79c975a644afc124432ec1744de4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:41 +0200 Subject: ftrace: add preempt_enable/disable notrace macros The tracer may need to call preempt_enable and disable functions for time keeping and such. The trace gets ugly when we see these functions show up for all traces. To make the output cleaner this patch adds preempt_enable_notrace and preempt_disable_notrace to be used by tracer (and debugging) functions. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/preempt.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'include/linux') diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 23f0c54175cd..36b03d50bf40 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -52,6 +52,34 @@ do { \ preempt_check_resched(); \ } while (0) +/* For debugging and tracer internals only! */ +#define add_preempt_count_notrace(val) \ + do { preempt_count() += (val); } while (0) +#define sub_preempt_count_notrace(val) \ + do { preempt_count() -= (val); } while (0) +#define inc_preempt_count_notrace() add_preempt_count_notrace(1) +#define dec_preempt_count_notrace() sub_preempt_count_notrace(1) + +#define preempt_disable_notrace() \ +do { \ + inc_preempt_count_notrace(); \ + barrier(); \ +} while (0) + +#define preempt_enable_no_resched_notrace() \ +do { \ + barrier(); \ + dec_preempt_count_notrace(); \ +} while (0) + +/* preempt_check_resched is OK to trace */ +#define preempt_enable_notrace() \ +do { \ + preempt_enable_no_resched_notrace(); \ + barrier(); \ + preempt_check_resched(); \ +} while (0) + #else #define preempt_disable() do { } while (0) @@ -59,6 +87,10 @@ do { \ #define preempt_enable() do { } while (0) #define preempt_check_resched() do { } while (0) +#define preempt_disable_notrace() do { } while (0) +#define preempt_enable_no_resched_notrace() do { } while (0) +#define preempt_enable_notrace() do { } while (0) + #endif #ifdef CONFIG_PREEMPT_NOTIFIERS -- cgit v1.2.3 From ffdc1a09ae7e2cbd714a446ee38a27f625b5f1c8 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:41 +0200 Subject: tracing: add notrace to linkage.h notrace signals that a function should not be traced. Most of the time this is used by tracers to annotate code that cannot be traced - it's in a volatile state (such as in user vdso context or NMI context) or it's in the tracer internals. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/linkage.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 2119610b24f8..14f329c64ba8 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -3,6 +3,8 @@ #include +#define notrace __attribute__((no_instrument_function)) + #ifdef __cplusplus #define CPP_ASMLINKAGE extern "C" #else -- cgit v1.2.3 From 16444a8a40d4c7b4f6de34af0cae1f76a4f6c901 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 12 May 2008 21:20:42 +0200 Subject: ftrace: add basic support for gcc profiler instrumentation If CONFIG_FTRACE is selected and /proc/sys/kernel/ftrace_enabled is set to a non-zero value the ftrace routine will be called everytime we enter a kernel function that is not marked with the "notrace" attribute. The ftrace routine will then call a registered function if a function happens to be registered. [ This code has been highly hacked by Steven Rostedt and Ingo Molnar, so don't blame Arnaldo for all of this ;-) ] Update: It is now possible to register more than one ftrace function. If only one ftrace function is registered, that will be the function that ftrace calls directly. If more than one function is registered, then ftrace will call a function that will loop through the functions to call. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- Makefile | 4 ++ arch/x86/Kconfig | 1 + arch/x86/kernel/entry_32.S | 27 +++++++++ arch/x86/kernel/entry_64.S | 37 ++++++++++++ include/linux/ftrace.h | 38 +++++++++++++ kernel/Makefile | 1 + kernel/trace/Kconfig | 5 ++ kernel/trace/Makefile | 3 + kernel/trace/ftrace.c | 138 +++++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig.debug | 2 + 10 files changed, 256 insertions(+) create mode 100644 include/linux/ftrace.h create mode 100644 kernel/trace/Kconfig create mode 100644 kernel/trace/Makefile create mode 100644 kernel/trace/ftrace.c (limited to 'include/linux') diff --git a/Makefile b/Makefile index 20b32351906b..b4a273f19b52 100644 --- a/Makefile +++ b/Makefile @@ -528,6 +528,10 @@ KBUILD_CFLAGS += -g KBUILD_AFLAGS += -gdwarf-2 endif +ifdef CONFIG_FTRACE +KBUILD_CFLAGS += -pg +endif + # We trigger additional mismatches with less inlining ifdef CONFIG_DEBUG_SECTION_MISMATCH KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fe361ae7ef2f..c742dfeb0dbe 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -23,6 +23,7 @@ config X86 select HAVE_OPROFILE select HAVE_KPROBES select HAVE_KRETPROBES + select HAVE_FTRACE select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64) select HAVE_ARCH_KGDB if !X86_VOYAGER diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 2a609dc3271c..f47b9b5440d2 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -1109,6 +1109,33 @@ ENDPROC(xen_failsafe_callback) #endif /* CONFIG_XEN */ +#ifdef CONFIG_FTRACE +ENTRY(mcount) + cmpl $ftrace_stub, ftrace_trace_function + jnz trace + +.globl ftrace_stub +ftrace_stub: + ret + + /* taken from glibc */ +trace: + pushl %eax + pushl %ecx + pushl %edx + movl 0xc(%esp), %eax + movl 0x4(%ebp), %edx + + call *ftrace_trace_function + + popl %edx + popl %ecx + popl %eax + + jmp ftrace_stub +END(mcount) +#endif + .section .rodata,"a" #include "syscall_table_32.S" diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 556a8df522a7..f046e0c64883 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -54,6 +54,43 @@ .code64 +#ifdef CONFIG_FTRACE +ENTRY(mcount) + cmpq $ftrace_stub, ftrace_trace_function + jnz trace +.globl ftrace_stub +ftrace_stub: + retq + +trace: + /* taken from glibc */ + subq $0x38, %rsp + movq %rax, (%rsp) + movq %rcx, 8(%rsp) + movq %rdx, 16(%rsp) + movq %rsi, 24(%rsp) + movq %rdi, 32(%rsp) + movq %r8, 40(%rsp) + movq %r9, 48(%rsp) + + movq 0x38(%rsp), %rdi + movq 8(%rbp), %rsi + + call *ftrace_trace_function + + movq 48(%rsp), %r9 + movq 40(%rsp), %r8 + movq 32(%rsp), %rdi + movq 24(%rsp), %rsi + movq 16(%rsp), %rdx + movq 8(%rsp), %rcx + movq (%rsp), %rax + addq $0x38, %rsp + + jmp ftrace_stub +END(mcount) +#endif + #ifndef CONFIG_PREEMPT #define retint_kernel retint_restore_args #endif diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h new file mode 100644 index 000000000000..b96ef14c249a --- /dev/null +++ b/include/linux/ftrace.h @@ -0,0 +1,38 @@ +#ifndef _LINUX_FTRACE_H +#define _LINUX_FTRACE_H + +#ifdef CONFIG_FTRACE + +#include + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) + +typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); + +struct ftrace_ops { + ftrace_func_t func; + struct ftrace_ops *next; +}; + +/* + * The ftrace_ops must be a static and should also + * be read_mostly. These functions do modify read_mostly variables + * so use them sparely. Never free an ftrace_op or modify the + * next pointer after it has been registered. Even after unregistering + * it, the next pointer may still be used internally. + */ +int register_ftrace_function(struct ftrace_ops *ops); +int unregister_ftrace_function(struct ftrace_ops *ops); +void clear_ftrace_function(void); + +extern void ftrace_stub(unsigned long a0, unsigned long a1); +extern void mcount(void); + +#else /* !CONFIG_FTRACE */ +# define register_ftrace_function(ops) do { } while (0) +# define unregister_ftrace_function(ops) do { } while (0) +# define clear_ftrace_function(ops) do { } while (0) +#endif /* CONFIG_FTRACE */ +#endif /* _LINUX_FTRACE_H */ diff --git a/kernel/Makefile b/kernel/Makefile index 1c9938addb9d..fa05f6d8bdbf 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_MARKERS) += marker.o obj-$(CONFIG_LATENCYTOP) += latencytop.o +obj-$(CONFIG_FTRACE) += trace/ ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra , the -fno-omit-frame-pointer is diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig new file mode 100644 index 000000000000..8185c91417bc --- /dev/null +++ b/kernel/trace/Kconfig @@ -0,0 +1,5 @@ +# +# Architectures that offer an FTRACE implementation should select HAVE_FTRACE: +# +config HAVE_FTRACE + bool diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile new file mode 100644 index 000000000000..bf4fd215a6a9 --- /dev/null +++ b/kernel/trace/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_FTRACE) += libftrace.o + +libftrace-y := ftrace.o diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c new file mode 100644 index 000000000000..b6a80b98a3fb --- /dev/null +++ b/kernel/trace/ftrace.c @@ -0,0 +1,138 @@ +/* + * Infrastructure for profiling code inserted by 'gcc -pg'. + * + * Copyright (C) 2007-2008 Steven Rostedt + * Copyright (C) 2004-2008 Ingo Molnar + * + * Originally ported from the -rt patch by: + * Copyright (C) 2007 Arnaldo Carvalho de Melo + * + * Based on code in the latency_tracer, that is: + * + * Copyright (C) 2004-2006 Ingo Molnar + * Copyright (C) 2004 William Lee Irwin III + */ + +#include +#include + +static DEFINE_SPINLOCK(ftrace_func_lock); +static struct ftrace_ops ftrace_list_end __read_mostly = +{ + .func = ftrace_stub, +}; + +static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; +ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; + +/* mcount is defined per arch in assembly */ +EXPORT_SYMBOL(mcount); + +notrace void ftrace_list_func(unsigned long ip, unsigned long parent_ip) +{ + struct ftrace_ops *op = ftrace_list; + + /* in case someone actually ports this to alpha! */ + read_barrier_depends(); + + while (op != &ftrace_list_end) { + /* silly alpha */ + read_barrier_depends(); + op->func(ip, parent_ip); + op = op->next; + }; +} + +/** + * register_ftrace_function - register a function for profiling + * @ops - ops structure that holds the function for profiling. + * + * Register a function to be called by all functions in the + * kernel. + * + * Note: @ops->func and all the functions it calls must be labeled + * with "notrace", otherwise it will go into a + * recursive loop. + */ +int register_ftrace_function(struct ftrace_ops *ops) +{ + unsigned long flags; + + spin_lock_irqsave(&ftrace_func_lock, flags); + ops->next = ftrace_list; + /* + * We are entering ops into the ftrace_list but another + * CPU might be walking that list. We need to make sure + * the ops->next pointer is valid before another CPU sees + * the ops pointer included into the ftrace_list. + */ + smp_wmb(); + ftrace_list = ops; + /* + * For one func, simply call it directly. + * For more than one func, call the chain. + */ + if (ops->next == &ftrace_list_end) + ftrace_trace_function = ops->func; + else + ftrace_trace_function = ftrace_list_func; + spin_unlock_irqrestore(&ftrace_func_lock, flags); + + return 0; +} + +/** + * unregister_ftrace_function - unresgister a function for profiling. + * @ops - ops structure that holds the function to unregister + * + * Unregister a function that was added to be called by ftrace profiling. + */ +int unregister_ftrace_function(struct ftrace_ops *ops) +{ + unsigned long flags; + struct ftrace_ops **p; + int ret = 0; + + spin_lock_irqsave(&ftrace_func_lock, flags); + + /* + * If we are the only function, then the ftrace pointer is + * pointing directly to that function. + */ + if (ftrace_list == ops && ops->next == &ftrace_list_end) { + ftrace_trace_function = ftrace_stub; + ftrace_list = &ftrace_list_end; + goto out; + } + + for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) + if (*p == ops) + break; + + if (*p != ops) { + ret = -1; + goto out; + } + + *p = (*p)->next; + + /* If we only have one func left, then call that directly */ + if (ftrace_list->next == &ftrace_list_end) + ftrace_trace_function = ftrace_list->func; + + out: + spin_unlock_irqrestore(&ftrace_func_lock, flags); + + return 0; +} + +/** + * clear_ftrace_function - reset the ftrace function + * + * This NULLs the ftrace function and in essence stops + * tracing. There may be lag + */ +void clear_ftrace_function(void) +{ + ftrace_trace_function = ftrace_stub; +} diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d2099f41aa1e..d8b6279a9b42 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -634,6 +634,8 @@ config LATENCYTOP Enable this option if you want to use the LatencyTOP tool to find out which userspace is blocking on what kernel operations. +source kernel/trace/Kconfig + config PROVIDE_OHCI1394_DMA_INIT bool "Remote debugging over FireWire early on boot" depends on PCI && X86 -- cgit v1.2.3 From 352ad25aa4a189c667cb2af333948d34692a2d27 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:42 +0200 Subject: ftrace: tracer for scheduler wakeup latency This patch adds the tracer that tracks the wakeup latency of the highest priority waking task. "wakeup" is added to /debugfs/tracing/available_tracers Also added to /debugfs/tracing tracing_max_latency holds the current max latency for the wakeup wakeup_thresh if set to other than zero, a log will be recorded for every wakeup that takes longer than the number entered in here (usecs for all counters) (deletes previous trace) Examples: (with ftrace_enabled = 0) ============ preemption latency trace v1.1.5 on 2.6.24-rc8 Signed-off-by: Ingo Molnar -------------------------------------------------------------------- latency: 26 us, #2/2, CPU#1 | (M:rt VP:0, KP:0, SP:0 HP:0 #P:2) ----------------- | task: migration/0-3 (uid:0 nice:-5 policy:1 rt_prio:99) ----------------- _------=> CPU# / _-----=> irqs-off | / _----=> need-resched || / _---=> hardirq/softirq ||| / _--=> preempt-depth |||| / ||||| delay cmd pid ||||| time | caller \ / ||||| \ | / quilt-8551 0d..3 0us+: wake_up_process+0x15/0x17 (sched_exec+0xc9/0x100 ) quilt-8551 0d..4 26us : sched_switch_callback+0x73/0x81 (schedule+0x483/0x6d5 ) vim:ft=help ============ (with ftrace_enabled = 1) ============ preemption latency trace v1.1.5 on 2.6.24-rc8 -------------------------------------------------------------------- latency: 36 us, #45/45, CPU#0 | (M:rt VP:0, KP:0, SP:0 HP:0 #P:2) ----------------- | task: migration/1-5 (uid:0 nice:-5 policy:1 rt_prio:99) ----------------- _------=> CPU# / _-----=> irqs-off | / _----=> need-resched || / _---=> hardirq/softirq ||| / _--=> preempt-depth |||| / ||||| delay cmd pid ||||| time | caller \ / ||||| \ | / bash-10653 1d..3 0us : wake_up_process+0x15/0x17 (sched_exec+0xc9/0x100 ) bash-10653 1d..3 1us : try_to_wake_up+0x271/0x2e7 (sub_preempt_count+0xc/0x7a ) bash-10653 1d..2 2us : try_to_wake_up+0x296/0x2e7 (update_rq_clock+0x9/0x20 ) bash-10653 1d..2 2us : update_rq_clock+0x1e/0x20 (__update_rq_clock+0xc/0x90 ) bash-10653 1d..2 3us : __update_rq_clock+0x1b/0x90 (sched_clock+0x9/0x29 ) bash-10653 1d..2 4us : try_to_wake_up+0x2a6/0x2e7 (activate_task+0xc/0x3f ) bash-10653 1d..2 4us : activate_task+0x2d/0x3f (enqueue_task+0xe/0x66 ) bash-10653 1d..2 5us : enqueue_task+0x5b/0x66 (enqueue_task_rt+0x9/0x3c ) bash-10653 1d..2 6us : try_to_wake_up+0x2ba/0x2e7 (check_preempt_wakeup+0x12/0x99 ) [...] bash-10653 1d..5 33us : tracing_record_cmdline+0xcf/0xd4 (_spin_unlock+0x9/0x33 ) bash-10653 1d..5 34us : _spin_unlock+0x19/0x33 (sub_preempt_count+0xc/0x7a ) bash-10653 1d..4 35us : wakeup_sched_switch+0x65/0x2ff (_spin_lock_irqsave+0xc/0xa9 ) bash-10653 1d..4 35us : _spin_lock_irqsave+0x19/0xa9 (add_preempt_count+0xe/0x77 ) bash-10653 1d..4 36us : sched_switch_callback+0x73/0x81 (schedule+0x483/0x6d5 ) vim:ft=help ============ The [...] was added here to not waste your email box space. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 23 ++- kernel/trace/Kconfig | 13 ++ kernel/trace/Makefile | 1 + kernel/trace/trace_sched_wakeup.c | 310 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 343 insertions(+), 4 deletions(-) create mode 100644 kernel/trace/trace_sched_wakeup.c (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index b96ef14c249a..db8a5e7abe41 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -5,10 +5,6 @@ #include -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -#define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) - typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); struct ftrace_ops { @@ -35,4 +31,23 @@ extern void mcount(void); # define unregister_ftrace_function(ops) do { } while (0) # define clear_ftrace_function(ops) do { } while (0) #endif /* CONFIG_FTRACE */ + + +#ifdef CONFIG_FRAME_POINTER +/* TODO: need to fix this for ARM */ +# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) +# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) +# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) +# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) +# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) +#else +# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +# define CALLER_ADDR1 0UL +# define CALLER_ADDR2 0UL +# define CALLER_ADDR3 0UL +# define CALLER_ADDR4 0UL +# define CALLER_ADDR5 0UL +#endif + #endif /* _LINUX_FTRACE_H */ diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 5d6aa92866cd..892ecc94a82b 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -4,6 +4,9 @@ config HAVE_FTRACE bool +config TRACER_MAX_TRACE + bool + config TRACING bool select DEBUG_FS @@ -23,6 +26,16 @@ config FTRACE (the bootup default), then the overhead of the instructions is very small and not measurable even in micro-benchmarks. +config SCHED_TRACER + bool "Scheduling Latency Tracer" + depends on DEBUG_KERNEL + select TRACING + select CONTEXT_SWITCH_TRACER + select TRACER_MAX_TRACE + help + This tracer tracks the latency of the highest priority task + to be scheduled in, starting from the point it has woken up. + config CONTEXT_SWITCH_TRACER bool "Trace process context switches" depends on DEBUG_KERNEL diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 6b54ceb7f16e..5508cdb19aea 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_FTRACE) += libftrace.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FTRACE) += trace_functions.o +obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o libftrace-y := ftrace.o diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c new file mode 100644 index 000000000000..7c3ccefcf4c3 --- /dev/null +++ b/kernel/trace/trace_sched_wakeup.c @@ -0,0 +1,310 @@ +/* + * trace task wakeup timings + * + * Copyright (C) 2007-2008 Steven Rostedt + * Copyright (C) 2008 Ingo Molnar + * + * Based on code from the latency_tracer, that is: + * + * Copyright (C) 2004-2006 Ingo Molnar + * Copyright (C) 2004 William Lee Irwin III + */ +#include +#include +#include +#include +#include +#include + +#include "trace.h" + +static struct trace_array *wakeup_trace; +static int __read_mostly tracer_enabled; + +static struct task_struct *wakeup_task; +static int wakeup_cpu; +static unsigned wakeup_prio = -1; + +static DEFINE_SPINLOCK(wakeup_lock); + +static void notrace __wakeup_reset(struct trace_array *tr); + +/* + * Should this new latency be reported/recorded? + */ +static int notrace report_latency(cycle_t delta) +{ + if (tracing_thresh) { + if (delta < tracing_thresh) + return 0; + } else { + if (delta <= tracing_max_latency) + return 0; + } + return 1; +} + +void notrace +wakeup_sched_switch(struct task_struct *prev, struct task_struct *next) +{ + unsigned long latency = 0, t0 = 0, t1 = 0; + struct trace_array *tr = wakeup_trace; + struct trace_array_cpu *data; + cycle_t T0, T1, delta; + unsigned long flags; + long disabled; + int cpu; + + if (unlikely(!tracer_enabled)) + return; + + /* + * When we start a new trace, we set wakeup_task to NULL + * and then set tracer_enabled = 1. We want to make sure + * that another CPU does not see the tracer_enabled = 1 + * and the wakeup_task with an older task, that might + * actually be the same as next. + */ + smp_rmb(); + + if (next != wakeup_task) + return; + + /* The task we are waitng for is waking up */ + data = tr->data[wakeup_cpu]; + + /* disable local data, not wakeup_cpu data */ + cpu = raw_smp_processor_id(); + disabled = atomic_inc_return(&tr->data[cpu]->disabled); + if (likely(disabled != 1)) + goto out; + + spin_lock_irqsave(&wakeup_lock, flags); + + /* We could race with grabbing wakeup_lock */ + if (unlikely(!tracer_enabled || next != wakeup_task)) + goto out_unlock; + + ftrace(tr, data, CALLER_ADDR1, CALLER_ADDR2, flags); + + /* + * usecs conversion is slow so we try to delay the conversion + * as long as possible: + */ + T0 = data->preempt_timestamp; + T1 = now(cpu); + delta = T1-T0; + + if (!report_latency(delta)) + goto out_unlock; + + latency = nsecs_to_usecs(delta); + + tracing_max_latency = delta; + t0 = nsecs_to_usecs(T0); + t1 = nsecs_to_usecs(T1); + + update_max_tr(tr, wakeup_task, wakeup_cpu); + + if (tracing_thresh) { + printk(KERN_INFO "(%16s-%-5d|#%d): %lu us wakeup latency " + "violates %lu us threshold.\n" + " => started at timestamp %lu: ", + wakeup_task->comm, wakeup_task->pid, + raw_smp_processor_id(), + latency, nsecs_to_usecs(tracing_thresh), t0); + } else { + printk(KERN_INFO "(%16s-%-5d|#%d): new %lu us maximum " + "wakeup latency.\n => started at timestamp %lu: ", + wakeup_task->comm, wakeup_task->pid, + cpu, latency, t0); + } + + printk(KERN_CONT " ended at timestamp %lu: ", t1); + dump_stack(); + t1 = nsecs_to_usecs(now(cpu)); + printk(KERN_CONT " dump-end timestamp %lu\n\n", t1); + +out_unlock: + __wakeup_reset(tr); + spin_unlock_irqrestore(&wakeup_lock, flags); +out: + atomic_dec(&tr->data[cpu]->disabled); +} + +static void notrace __wakeup_reset(struct trace_array *tr) +{ + struct trace_array_cpu *data; + int cpu; + + assert_spin_locked(&wakeup_lock); + + for_each_possible_cpu(cpu) { + data = tr->data[cpu]; + tracing_reset(data); + } + + wakeup_cpu = -1; + wakeup_prio = -1; + + if (wakeup_task) + put_task_struct(wakeup_task); + + wakeup_task = NULL; +} + +static void notrace wakeup_reset(struct trace_array *tr) +{ + unsigned long flags; + + spin_lock_irqsave(&wakeup_lock, flags); + __wakeup_reset(tr); + spin_unlock_irqrestore(&wakeup_lock, flags); +} + +static notrace void +wakeup_check_start(struct trace_array *tr, struct task_struct *p, + struct task_struct *curr) +{ + int cpu = smp_processor_id(); + unsigned long flags; + long disabled; + + if (likely(!rt_task(p)) || + p->prio >= wakeup_prio || + p->prio >= curr->prio) + return; + + disabled = atomic_inc_return(&tr->data[cpu]->disabled); + if (unlikely(disabled != 1)) + goto out; + + /* interrupts should be off from try_to_wake_up */ + spin_lock(&wakeup_lock); + + /* check for races. */ + if (!tracer_enabled || p->prio >= wakeup_prio) + goto out_locked; + + /* reset the trace */ + __wakeup_reset(tr); + + wakeup_cpu = task_cpu(p); + wakeup_prio = p->prio; + + wakeup_task = p; + get_task_struct(wakeup_task); + + local_save_flags(flags); + + tr->data[wakeup_cpu]->preempt_timestamp = now(cpu); + ftrace(tr, tr->data[wakeup_cpu], CALLER_ADDR1, CALLER_ADDR2, flags); + +out_locked: + spin_unlock(&wakeup_lock); +out: + atomic_dec(&tr->data[cpu]->disabled); +} + +notrace void +ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr) +{ + if (likely(!tracer_enabled)) + return; + + wakeup_check_start(wakeup_trace, wakee, curr); +} + +notrace void +ftrace_wake_up_new_task(struct task_struct *wakee, struct task_struct *curr) +{ + if (likely(!tracer_enabled)) + return; + + wakeup_check_start(wakeup_trace, wakee, curr); +} + +static notrace void start_wakeup_tracer(struct trace_array *tr) +{ + wakeup_reset(tr); + + /* + * Don't let the tracer_enabled = 1 show up before + * the wakeup_task is reset. This may be overkill since + * wakeup_reset does a spin_unlock after setting the + * wakeup_task to NULL, but I want to be safe. + * This is a slow path anyway. + */ + smp_wmb(); + + tracer_enabled = 1; + + return; +} + +static notrace void stop_wakeup_tracer(struct trace_array *tr) +{ + tracer_enabled = 0; +} + +static notrace void wakeup_tracer_init(struct trace_array *tr) +{ + wakeup_trace = tr; + + if (tr->ctrl) + start_wakeup_tracer(tr); +} + +static notrace void wakeup_tracer_reset(struct trace_array *tr) +{ + if (tr->ctrl) { + stop_wakeup_tracer(tr); + /* make sure we put back any tasks we are tracing */ + wakeup_reset(tr); + } +} + +static void wakeup_tracer_ctrl_update(struct trace_array *tr) +{ + if (tr->ctrl) + start_wakeup_tracer(tr); + else + stop_wakeup_tracer(tr); +} + +static void notrace wakeup_tracer_open(struct trace_iterator *iter) +{ + /* stop the trace while dumping */ + if (iter->tr->ctrl) + stop_wakeup_tracer(iter->tr); +} + +static void notrace wakeup_tracer_close(struct trace_iterator *iter) +{ + /* forget about any processes we were recording */ + if (iter->tr->ctrl) + start_wakeup_tracer(iter->tr); +} + +static struct tracer wakeup_tracer __read_mostly = +{ + .name = "wakeup", + .init = wakeup_tracer_init, + .reset = wakeup_tracer_reset, + .open = wakeup_tracer_open, + .close = wakeup_tracer_close, + .ctrl_update = wakeup_tracer_ctrl_update, + .print_max = 1, +}; + +__init static int init_wakeup_tracer(void) +{ + int ret; + + ret = register_tracer(&wakeup_tracer); + if (ret) + return ret; + + return 0; +} +device_initcall(init_wakeup_tracer); -- cgit v1.2.3 From 81d68a96a39844853b37f20cc8282d9b65b78ef3 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:42 +0200 Subject: ftrace: trace irq disabled critical timings This patch adds latency tracing for critical timings (how long interrupts are disabled for). "irqsoff" is added to /debugfs/tracing/available_tracers Note: tracing_max_latency also holds the max latency for irqsoff (in usecs). (default to large number so one must start latency tracing) tracing_thresh threshold (in usecs) to always print out if irqs off is detected to be longer than stated here. If irq_thresh is non-zero, then max_irq_latency is ignored. Here's an example of a trace with ftrace_enabled = 0 ======= preemption latency trace v1.1.5 on 2.6.24-rc7 Signed-off-by: Ingo Molnar -------------------------------------------------------------------- latency: 100 us, #3/3, CPU#1 | (M:rt VP:0, KP:0, SP:0 HP:0 #P:2) ----------------- | task: swapper-0 (uid:0 nice:0 policy:0 rt_prio:0) ----------------- => started at: _spin_lock_irqsave+0x2a/0xb7 => ended at: _spin_unlock_irqrestore+0x32/0x5f _------=> CPU# / _-----=> irqs-off | / _----=> need-resched || / _---=> hardirq/softirq ||| / _--=> preempt-depth |||| / ||||| delay cmd pid ||||| time | caller \ / ||||| \ | / swapper-0 1d.s3 0us+: _spin_lock_irqsave+0x2a/0xb7 (e1000_update_stats+0x47/0x64c [e1000]) swapper-0 1d.s3 100us : _spin_unlock_irqrestore+0x32/0x5f (e1000_update_stats+0x641/0x64c [e1000]) swapper-0 1d.s3 100us : trace_hardirqs_on_caller+0x75/0x89 (_spin_unlock_irqrestore+0x32/0x5f) vim:ft=help ======= And this is a trace with ftrace_enabled == 1 ======= preemption latency trace v1.1.5 on 2.6.24-rc7 -------------------------------------------------------------------- latency: 102 us, #12/12, CPU#1 | (M:rt VP:0, KP:0, SP:0 HP:0 #P:2) ----------------- | task: swapper-0 (uid:0 nice:0 policy:0 rt_prio:0) ----------------- => started at: _spin_lock_irqsave+0x2a/0xb7 => ended at: _spin_unlock_irqrestore+0x32/0x5f _------=> CPU# / _-----=> irqs-off | / _----=> need-resched || / _---=> hardirq/softirq ||| / _--=> preempt-depth |||| / ||||| delay cmd pid ||||| time | caller \ / ||||| \ | / swapper-0 1dNs3 0us+: _spin_lock_irqsave+0x2a/0xb7 (e1000_update_stats+0x47/0x64c [e1000]) swapper-0 1dNs3 46us : e1000_read_phy_reg+0x16/0x225 [e1000] (e1000_update_stats+0x5e2/0x64c [e1000]) swapper-0 1dNs3 46us : e1000_swfw_sync_acquire+0x10/0x99 [e1000] (e1000_read_phy_reg+0x49/0x225 [e1000]) swapper-0 1dNs3 46us : e1000_get_hw_eeprom_semaphore+0x12/0xa6 [e1000] (e1000_swfw_sync_acquire+0x36/0x99 [e1000]) swapper-0 1dNs3 47us : __const_udelay+0x9/0x47 (e1000_read_phy_reg+0x116/0x225 [e1000]) swapper-0 1dNs3 47us+: __delay+0x9/0x50 (__const_udelay+0x45/0x47) swapper-0 1dNs3 97us : preempt_schedule+0xc/0x84 (__delay+0x4e/0x50) swapper-0 1dNs3 98us : e1000_swfw_sync_release+0xc/0x55 [e1000] (e1000_read_phy_reg+0x211/0x225 [e1000]) swapper-0 1dNs3 99us+: e1000_put_hw_eeprom_semaphore+0x9/0x35 [e1000] (e1000_swfw_sync_release+0x50/0x55 [e1000]) swapper-0 1dNs3 101us : _spin_unlock_irqrestore+0xe/0x5f (e1000_update_stats+0x641/0x64c [e1000]) swapper-0 1dNs3 102us : _spin_unlock_irqrestore+0x32/0x5f (e1000_update_stats+0x641/0x64c [e1000]) swapper-0 1dNs3 102us : trace_hardirqs_on_caller+0x75/0x89 (_spin_unlock_irqrestore+0x32/0x5f) vim:ft=help ======= Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/process_64.c | 3 + arch/x86/lib/Makefile | 1 + arch/x86/lib/thunk_32.S | 47 +++++ arch/x86/lib/thunk_64.S | 19 +- include/asm-x86/irqflags.h | 24 +-- include/linux/ftrace.h | 8 + include/linux/irqflags.h | 12 +- kernel/fork.c | 2 +- kernel/lockdep.c | 23 ++- kernel/printk.c | 2 + kernel/trace/Kconfig | 18 ++ kernel/trace/Makefile | 1 + kernel/trace/trace_irqsoff.c | 402 +++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 531 insertions(+), 31 deletions(-) create mode 100644 arch/x86/lib/thunk_32.S create mode 100644 kernel/trace/trace_irqsoff.c (limited to 'include/linux') diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index e2319f39988b..dd349c92f051 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -165,7 +165,10 @@ void cpu_idle(void) */ local_irq_disable(); enter_idle(); + /* Don't trace irqs off for idle */ + stop_critical_timings(); idle(); + start_critical_timings(); /* In many cases the interrupt that ended idle has already called exit_idle. But some idle loops can be woken up without interrupt. */ diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 76f60f52a885..84aa2883fe15 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SMP) := msr-on-cpu.o lib-y := delay_$(BITS).o +lib-y += thunk_$(BITS).o lib-y += usercopy_$(BITS).o getuser_$(BITS).o putuser_$(BITS).o lib-y += memcpy_$(BITS).o diff --git a/arch/x86/lib/thunk_32.S b/arch/x86/lib/thunk_32.S new file mode 100644 index 000000000000..650b11e00ecc --- /dev/null +++ b/arch/x86/lib/thunk_32.S @@ -0,0 +1,47 @@ +/* + * Trampoline to trace irqs off. (otherwise CALLER_ADDR1 might crash) + * Copyright 2008 by Steven Rostedt, Red Hat, Inc + * (inspired by Andi Kleen's thunk_64.S) + * Subject to the GNU public license, v.2. No warranty of any kind. + */ + + #include + +#define ARCH_TRACE_IRQS_ON \ + pushl %eax; \ + pushl %ecx; \ + pushl %edx; \ + call trace_hardirqs_on; \ + popl %edx; \ + popl %ecx; \ + popl %eax; + +#define ARCH_TRACE_IRQS_OFF \ + pushl %eax; \ + pushl %ecx; \ + pushl %edx; \ + call trace_hardirqs_off; \ + popl %edx; \ + popl %ecx; \ + popl %eax; + +#ifdef CONFIG_TRACE_IRQFLAGS + /* put return address in eax (arg1) */ + .macro thunk_ra name,func + .globl \name +\name: + pushl %eax + pushl %ecx + pushl %edx + /* Place EIP in the arg1 */ + movl 3*4(%esp), %eax + call \func + popl %edx + popl %ecx + popl %eax + ret + .endm + + thunk_ra trace_hardirqs_on_thunk,trace_hardirqs_on_caller + thunk_ra trace_hardirqs_off_thunk,trace_hardirqs_off_caller +#endif diff --git a/arch/x86/lib/thunk_64.S b/arch/x86/lib/thunk_64.S index e009251d4e9f..bf9a7d5a5428 100644 --- a/arch/x86/lib/thunk_64.S +++ b/arch/x86/lib/thunk_64.S @@ -2,6 +2,7 @@ * Save registers before calling assembly functions. This avoids * disturbance of register allocation in some inline assembly constructs. * Copyright 2001,2002 by Andi Kleen, SuSE Labs. + * Added trace_hardirqs callers - Copyright 2007 Steven Rostedt, Red Hat, Inc. * Subject to the GNU public license, v.2. No warranty of any kind. */ @@ -42,8 +43,22 @@ #endif #ifdef CONFIG_TRACE_IRQFLAGS - thunk trace_hardirqs_on_thunk,trace_hardirqs_on - thunk trace_hardirqs_off_thunk,trace_hardirqs_off + /* put return address in rdi (arg1) */ + .macro thunk_ra name,func + .globl \name +\name: + CFI_STARTPROC + SAVE_ARGS + /* SAVE_ARGS pushs 9 elements */ + /* the next element would be the rip */ + movq 9*8(%rsp), %rdi + call \func + jmp restore + CFI_ENDPROC + .endm + + thunk_ra trace_hardirqs_on_thunk,trace_hardirqs_on_caller + thunk_ra trace_hardirqs_off_thunk,trace_hardirqs_off_caller #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC diff --git a/include/asm-x86/irqflags.h b/include/asm-x86/irqflags.h index c242527f970e..24d71b1eb189 100644 --- a/include/asm-x86/irqflags.h +++ b/include/asm-x86/irqflags.h @@ -179,8 +179,6 @@ static inline void trace_hardirqs_fixup(void) * have a reliable stack. x86_64 only. */ #define SWAPGS_UNSAFE_STACK swapgs -#define ARCH_TRACE_IRQS_ON call trace_hardirqs_on_thunk -#define ARCH_TRACE_IRQS_OFF call trace_hardirqs_off_thunk #define ARCH_LOCKDEP_SYS_EXIT call lockdep_sys_exit_thunk #define ARCH_LOCKDEP_SYS_EXIT_IRQ \ TRACE_IRQS_ON; \ @@ -192,24 +190,6 @@ static inline void trace_hardirqs_fixup(void) TRACE_IRQS_OFF; #else -#define ARCH_TRACE_IRQS_ON \ - pushl %eax; \ - pushl %ecx; \ - pushl %edx; \ - call trace_hardirqs_on; \ - popl %edx; \ - popl %ecx; \ - popl %eax; - -#define ARCH_TRACE_IRQS_OFF \ - pushl %eax; \ - pushl %ecx; \ - pushl %edx; \ - call trace_hardirqs_off; \ - popl %edx; \ - popl %ecx; \ - popl %eax; - #define ARCH_LOCKDEP_SYS_EXIT \ pushl %eax; \ pushl %ecx; \ @@ -223,8 +203,8 @@ static inline void trace_hardirqs_fixup(void) #endif #ifdef CONFIG_TRACE_IRQFLAGS -# define TRACE_IRQS_ON ARCH_TRACE_IRQS_ON -# define TRACE_IRQS_OFF ARCH_TRACE_IRQS_OFF +# define TRACE_IRQS_ON call trace_hardirqs_on_thunk; +# define TRACE_IRQS_OFF call trace_hardirqs_off_thunk; #else # define TRACE_IRQS_ON # define TRACE_IRQS_OFF diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index db8a5e7abe41..0a20445dcbcc 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -50,4 +50,12 @@ extern void mcount(void); # define CALLER_ADDR5 0UL #endif +#ifdef CONFIG_IRQSOFF_TRACER + extern void notrace time_hardirqs_on(unsigned long a0, unsigned long a1); + extern void notrace time_hardirqs_off(unsigned long a0, unsigned long a1); +#else +# define time_hardirqs_on(a0, a1) do { } while (0) +# define time_hardirqs_off(a0, a1) do { } while (0) +#endif + #endif /* _LINUX_FTRACE_H */ diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index e600c4e9b8c5..5b711d4e9fd9 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -12,10 +12,10 @@ #define _LINUX_TRACE_IRQFLAGS_H #ifdef CONFIG_TRACE_IRQFLAGS - extern void trace_hardirqs_on(void); - extern void trace_hardirqs_off(void); extern void trace_softirqs_on(unsigned long ip); extern void trace_softirqs_off(unsigned long ip); + extern void trace_hardirqs_on(void); + extern void trace_hardirqs_off(void); # define trace_hardirq_context(p) ((p)->hardirq_context) # define trace_softirq_context(p) ((p)->softirq_context) # define trace_hardirqs_enabled(p) ((p)->hardirqs_enabled) @@ -41,6 +41,14 @@ # define INIT_TRACE_IRQFLAGS #endif +#ifdef CONFIG_IRQSOFF_TRACER + extern void stop_critical_timings(void); + extern void start_critical_timings(void); +#else +# define stop_critical_timings() do { } while (0) +# define start_critical_timings() do { } while (0) +#endif + #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT #include diff --git a/kernel/fork.c b/kernel/fork.c index 19908b26cf80..d66d676dc362 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -909,7 +909,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, rt_mutex_init_task(p); -#ifdef CONFIG_TRACE_IRQFLAGS +#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_LOCKDEP) DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); #endif diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 81a4e4a3f087..e21924365ea3 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -982,7 +983,7 @@ check_noncircular(struct lock_class *source, unsigned int depth) return 1; } -#ifdef CONFIG_TRACE_IRQFLAGS +#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) /* * Forwards and backwards subgraph searching, for the purposes of * proving that two subgraphs can be connected by a new dependency @@ -1680,7 +1681,7 @@ valid_state(struct task_struct *curr, struct held_lock *this, static int mark_lock(struct task_struct *curr, struct held_lock *this, enum lock_usage_bit new_bit); -#ifdef CONFIG_TRACE_IRQFLAGS +#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) /* * print irq inversion bug: @@ -2013,11 +2014,13 @@ void early_boot_irqs_on(void) /* * Hardirqs will be enabled: */ -void trace_hardirqs_on(void) +void notrace trace_hardirqs_on_caller(unsigned long a0) { struct task_struct *curr = current; unsigned long ip; + time_hardirqs_on(CALLER_ADDR0, a0); + if (unlikely(!debug_locks || current->lockdep_recursion)) return; @@ -2055,16 +2058,23 @@ void trace_hardirqs_on(void) curr->hardirq_enable_event = ++curr->irq_events; debug_atomic_inc(&hardirqs_on_events); } +EXPORT_SYMBOL(trace_hardirqs_on_caller); +void notrace trace_hardirqs_on(void) +{ + trace_hardirqs_on_caller(CALLER_ADDR0); +} EXPORT_SYMBOL(trace_hardirqs_on); /* * Hardirqs were disabled: */ -void trace_hardirqs_off(void) +void notrace trace_hardirqs_off_caller(unsigned long a0) { struct task_struct *curr = current; + time_hardirqs_off(CALLER_ADDR0, a0); + if (unlikely(!debug_locks || current->lockdep_recursion)) return; @@ -2082,7 +2092,12 @@ void trace_hardirqs_off(void) } else debug_atomic_inc(&redundant_hardirqs_off); } +EXPORT_SYMBOL(trace_hardirqs_off_caller); +void notrace trace_hardirqs_off(void) +{ + trace_hardirqs_off_caller(CALLER_ADDR0); +} EXPORT_SYMBOL(trace_hardirqs_off); /* diff --git a/kernel/printk.c b/kernel/printk.c index 8fb01c32aa3b..ae7d5b9e535d 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1041,7 +1041,9 @@ void release_console_sem(void) _log_end = log_end; con_start = log_end; /* Flush */ spin_unlock(&logbuf_lock); + stop_critical_timings(); /* don't trace print latency */ call_console_drivers(_con_start, _log_end); + start_critical_timings(); local_irq_restore(flags); } console_locked = 0; diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 892ecc94a82b..896df1cf6adc 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -26,6 +26,24 @@ config FTRACE (the bootup default), then the overhead of the instructions is very small and not measurable even in micro-benchmarks. +config IRQSOFF_TRACER + bool "Interrupts-off Latency Tracer" + default n + depends on TRACE_IRQFLAGS_SUPPORT + depends on GENERIC_TIME + select TRACE_IRQFLAGS + select TRACING + select TRACER_MAX_TRACE + help + This option measures the time spent in irqs-off critical + sections, with microsecond accuracy. + + The default measurement method is a maximum search, which is + disabled by default and can be runtime (re-)started + via: + + echo 0 > /debugfs/tracing/tracing_max_latency + config SCHED_TRACER bool "Scheduling Latency Tracer" depends on DEBUG_KERNEL diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 5508cdb19aea..46be8647fb65 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_FTRACE) += libftrace.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FTRACE) += trace_functions.o +obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o libftrace-y := ftrace.o diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c new file mode 100644 index 000000000000..a9131b0cf1a5 --- /dev/null +++ b/kernel/trace/trace_irqsoff.c @@ -0,0 +1,402 @@ +/* + * trace irqs off criticall timings + * + * Copyright (C) 2007-2008 Steven Rostedt + * Copyright (C) 2008 Ingo Molnar + * + * From code in the latency_tracer, that is: + * + * Copyright (C) 2004-2006 Ingo Molnar + * Copyright (C) 2004 William Lee Irwin III + */ +#include +#include +#include +#include +#include +#include + +#include "trace.h" + +static struct trace_array *irqsoff_trace __read_mostly; +static int tracer_enabled __read_mostly; + +/* + * Sequence count - we record it when starting a measurement and + * skip the latency if the sequence has changed - some other section + * did a maximum and could disturb our measurement with serial console + * printouts, etc. Truly coinciding maximum latencies should be rare + * and what happens together happens separately as well, so this doesnt + * decrease the validity of the maximum found: + */ +static __cacheline_aligned_in_smp unsigned long max_sequence; + +#ifdef CONFIG_FTRACE +/* + * irqsoff uses its own tracer function to keep the overhead down: + */ +static void notrace +irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip) +{ + struct trace_array *tr = irqsoff_trace; + struct trace_array_cpu *data; + unsigned long flags; + long disabled; + int cpu; + + if (likely(!tracer_enabled)) + return; + + local_save_flags(flags); + + if (!irqs_disabled_flags(flags)) + return; + + cpu = raw_smp_processor_id(); + data = tr->data[cpu]; + disabled = atomic_inc_return(&data->disabled); + + if (likely(disabled == 1)) + ftrace(tr, data, ip, parent_ip, flags); + + atomic_dec(&data->disabled); +} + +static struct ftrace_ops trace_ops __read_mostly = +{ + .func = irqsoff_tracer_call, +}; +#endif /* CONFIG_FTRACE */ + +/* + * Should this new latency be reported/recorded? + */ +static int notrace report_latency(cycle_t delta) +{ + if (tracing_thresh) { + if (delta < tracing_thresh) + return 0; + } else { + if (delta <= tracing_max_latency) + return 0; + } + return 1; +} + +static void notrace +check_critical_timing(struct trace_array *tr, + struct trace_array_cpu *data, + unsigned long parent_ip, + int cpu) +{ + unsigned long latency, t0, t1; + cycle_t T0, T1, T2, delta; + unsigned long flags; + + /* + * usecs conversion is slow so we try to delay the conversion + * as long as possible: + */ + T0 = data->preempt_timestamp; + T1 = now(cpu); + delta = T1-T0; + + local_save_flags(flags); + + if (!report_latency(delta)) + goto out; + + ftrace(tr, data, CALLER_ADDR0, parent_ip, flags); + /* + * Update the timestamp, because the trace entry above + * might change it (it can only get larger so the latency + * is fair to be reported): + */ + T2 = now(cpu); + + delta = T2-T0; + + latency = nsecs_to_usecs(delta); + + if (data->critical_sequence != max_sequence) + goto out; + + tracing_max_latency = delta; + t0 = nsecs_to_usecs(T0); + t1 = nsecs_to_usecs(T1); + + data->critical_end = parent_ip; + + update_max_tr_single(tr, current, cpu); + + if (tracing_thresh) + printk(KERN_INFO "(%16s-%-5d|#%d): %lu us critical section " + "violates %lu us threshold.\n" + " => started at timestamp %lu: ", + current->comm, current->pid, + raw_smp_processor_id(), + latency, nsecs_to_usecs(tracing_thresh), t0); + else + printk(KERN_INFO "(%16s-%-5d|#%d):" + " new %lu us maximum-latency " + "critical section.\n => started at timestamp %lu: ", + current->comm, current->pid, + raw_smp_processor_id(), + latency, t0); + + print_symbol(KERN_CONT "<%s>\n", data->critical_start); + printk(KERN_CONT " => ended at timestamp %lu: ", t1); + print_symbol(KERN_CONT "<%s>\n", data->critical_end); + dump_stack(); + t1 = nsecs_to_usecs(now(cpu)); + printk(KERN_CONT " => dump-end timestamp %lu\n\n", t1); + + max_sequence++; + +out: + data->critical_sequence = max_sequence; + data->preempt_timestamp = now(cpu); + tracing_reset(data); + ftrace(tr, data, CALLER_ADDR0, parent_ip, flags); +} + +static inline void notrace +start_critical_timing(unsigned long ip, unsigned long parent_ip) +{ + int cpu; + struct trace_array *tr = irqsoff_trace; + struct trace_array_cpu *data; + unsigned long flags; + + if (likely(!tracer_enabled)) + return; + + cpu = raw_smp_processor_id(); + data = tr->data[cpu]; + + if (unlikely(!data) || unlikely(!data->trace) || + data->critical_start || atomic_read(&data->disabled)) + return; + + atomic_inc(&data->disabled); + + data->critical_sequence = max_sequence; + data->preempt_timestamp = now(cpu); + data->critical_start = parent_ip; + tracing_reset(data); + + local_save_flags(flags); + ftrace(tr, data, ip, parent_ip, flags); + + atomic_dec(&data->disabled); +} + +static inline void notrace +stop_critical_timing(unsigned long ip, unsigned long parent_ip) +{ + int cpu; + struct trace_array *tr = irqsoff_trace; + struct trace_array_cpu *data; + unsigned long flags; + + if (likely(!tracer_enabled)) + return; + + cpu = raw_smp_processor_id(); + data = tr->data[cpu]; + + if (unlikely(!data) || unlikely(!data->trace) || + !data->critical_start || atomic_read(&data->disabled)) + return; + + atomic_inc(&data->disabled); + local_save_flags(flags); + ftrace(tr, data, ip, parent_ip, flags); + check_critical_timing(tr, data, parent_ip, cpu); + data->critical_start = 0; + atomic_dec(&data->disabled); +} + +void notrace start_critical_timings(void) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); +} + +void notrace stop_critical_timings(void) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); +} + +#ifdef CONFIG_PROVE_LOCKING +void notrace time_hardirqs_on(unsigned long a0, unsigned long a1) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + stop_critical_timing(a0, a1); +} + +void notrace time_hardirqs_off(unsigned long a0, unsigned long a1) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + start_critical_timing(a0, a1); +} + +#else /* !CONFIG_PROVE_LOCKING */ + +/* + * Stubs: + */ + +void early_boot_irqs_off(void) +{ +} + +void early_boot_irqs_on(void) +{ +} + +void trace_softirqs_on(unsigned long ip) +{ +} + +void trace_softirqs_off(unsigned long ip) +{ +} + +inline void print_irqtrace_events(struct task_struct *curr) +{ +} + +/* + * We are only interested in hardirq on/off events: + */ +void notrace trace_hardirqs_on(void) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); +} +EXPORT_SYMBOL(trace_hardirqs_on); + +void notrace trace_hardirqs_off(void) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); +} +EXPORT_SYMBOL(trace_hardirqs_off); + +void notrace trace_hardirqs_on_caller(unsigned long caller_addr) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + stop_critical_timing(CALLER_ADDR0, caller_addr); +} +EXPORT_SYMBOL(trace_hardirqs_on_caller); + +void notrace trace_hardirqs_off_caller(unsigned long caller_addr) +{ + unsigned long flags; + + local_save_flags(flags); + + if (irqs_disabled_flags(flags)) + start_critical_timing(CALLER_ADDR0, caller_addr); +} +EXPORT_SYMBOL(trace_hardirqs_off_caller); + +#endif /* CONFIG_PROVE_LOCKING */ + +static void start_irqsoff_tracer(struct trace_array *tr) +{ + tracer_enabled = 1; + register_ftrace_function(&trace_ops); +} + +static void stop_irqsoff_tracer(struct trace_array *tr) +{ + unregister_ftrace_function(&trace_ops); + tracer_enabled = 0; +} + +static void irqsoff_tracer_init(struct trace_array *tr) +{ + irqsoff_trace = tr; + /* make sure that the tracer is visibel */ + smp_wmb(); + + if (tr->ctrl) + start_irqsoff_tracer(tr); +} + +static void irqsoff_tracer_reset(struct trace_array *tr) +{ + if (tr->ctrl) + stop_irqsoff_tracer(tr); +} + +static void irqsoff_tracer_ctrl_update(struct trace_array *tr) +{ + if (tr->ctrl) + start_irqsoff_tracer(tr); + else + stop_irqsoff_tracer(tr); +} + +static void notrace irqsoff_tracer_open(struct trace_iterator *iter) +{ + /* stop the trace while dumping */ + if (iter->tr->ctrl) + stop_irqsoff_tracer(iter->tr); +} + +static void notrace irqsoff_tracer_close(struct trace_iterator *iter) +{ + if (iter->tr->ctrl) + start_irqsoff_tracer(iter->tr); +} + +static struct tracer irqsoff_tracer __read_mostly = +{ + .name = "irqsoff", + .init = irqsoff_tracer_init, + .reset = irqsoff_tracer_reset, + .open = irqsoff_tracer_open, + .close = irqsoff_tracer_close, + .ctrl_update = irqsoff_tracer_ctrl_update, + .print_max = 1, +}; + +__init static int init_irqsoff_tracer(void) +{ + register_tracer(&irqsoff_tracer); + + return 0; +} +device_initcall(init_irqsoff_tracer); -- cgit v1.2.3 From 6cd8a4bb2f97527a9ceb30bc77ea4e959c6a95e3 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:42 +0200 Subject: ftrace: trace preempt off critical timings Add preempt off timings. A lot of kernel core code is taken from the RT patch latency trace that was written by Ingo Molnar. This adds "preemptoff" and "preemptirqsoff" to /debugfs/tracing/available_tracers Now instead of just tracing irqs off, preemption off can be selected to be recorded. When this is selected, it shares the same files as irqs off timings. One can either trace preemption off, irqs off, or one or the other off. By echoing "preemptoff" into /debugfs/tracing/current_tracer, recording of preempt off only is performed. "irqsoff" will only record the time irqs are disabled, but "preemptirqsoff" will take the total time irqs or preemption are disabled. Runtime switching of these options is now supported by simpling echoing in the appropriate trace name into /debugfs/tracing/current_tracer. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/process_32.c | 3 + include/linux/ftrace.h | 8 ++ include/linux/irqflags.h | 3 +- include/linux/preempt.h | 2 +- kernel/sched.c | 24 +++++- kernel/trace/Kconfig | 25 ++++++ kernel/trace/Makefile | 1 + kernel/trace/trace_irqsoff.c | 184 +++++++++++++++++++++++++++++++------------ 8 files changed, 197 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index f8476dfbb60d..a30aa1f2607a 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -185,7 +185,10 @@ void cpu_idle(void) local_irq_disable(); __get_cpu_var(irq_stat).idle_timestamp = jiffies; + /* Don't trace irqs off for idle */ + stop_critical_timings(); idle(); + start_critical_timings(); } tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 0a20445dcbcc..740c97dcf9cb 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -58,4 +58,12 @@ extern void mcount(void); # define time_hardirqs_off(a0, a1) do { } while (0) #endif +#ifdef CONFIG_PREEMPT_TRACER + extern void notrace trace_preempt_on(unsigned long a0, unsigned long a1); + extern void notrace trace_preempt_off(unsigned long a0, unsigned long a1); +#else +# define trace_preempt_on(a0, a1) do { } while (0) +# define trace_preempt_off(a0, a1) do { } while (0) +#endif + #endif /* _LINUX_FTRACE_H */ diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 5b711d4e9fd9..2b1c2e58566e 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -41,7 +41,8 @@ # define INIT_TRACE_IRQFLAGS #endif -#ifdef CONFIG_IRQSOFF_TRACER +#if defined(CONFIG_IRQSOFF_TRACER) || \ + defined(CONFIG_PREEMPT_TRACER) extern void stop_critical_timings(void); extern void start_critical_timings(void); #else diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 36b03d50bf40..72b1a10a59b6 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -10,7 +10,7 @@ #include #include -#ifdef CONFIG_DEBUG_PREEMPT +#if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER) extern void add_preempt_count(int val); extern void sub_preempt_count(int val); #else diff --git a/kernel/sched.c b/kernel/sched.c index 73e600852365..328494e28df2 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include @@ -4365,26 +4366,44 @@ void scheduler_tick(void) #endif } -#if defined(CONFIG_PREEMPT) && defined(CONFIG_DEBUG_PREEMPT) +#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \ + defined(CONFIG_PREEMPT_TRACER)) + +static inline unsigned long get_parent_ip(unsigned long addr) +{ + if (in_lock_functions(addr)) { + addr = CALLER_ADDR2; + if (in_lock_functions(addr)) + addr = CALLER_ADDR3; + } + return addr; +} void __kprobes add_preempt_count(int val) { +#ifdef CONFIG_DEBUG_PREEMPT /* * Underflow? */ if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) return; +#endif preempt_count() += val; +#ifdef CONFIG_DEBUG_PREEMPT /* * Spinlock count overflowing soon? */ DEBUG_LOCKS_WARN_ON((preempt_count() & PREEMPT_MASK) >= PREEMPT_MASK - 10); +#endif + if (preempt_count() == val) + trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); } EXPORT_SYMBOL(add_preempt_count); void __kprobes sub_preempt_count(int val) { +#ifdef CONFIG_DEBUG_PREEMPT /* * Underflow? */ @@ -4396,7 +4415,10 @@ void __kprobes sub_preempt_count(int val) if (DEBUG_LOCKS_WARN_ON((val < PREEMPT_MASK) && !(preempt_count() & PREEMPT_MASK))) return; +#endif + if (preempt_count() == val) + trace_preempt_on(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); preempt_count() -= val; } EXPORT_SYMBOL(sub_preempt_count); diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 896df1cf6adc..6430016b98e8 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -44,6 +44,31 @@ config IRQSOFF_TRACER echo 0 > /debugfs/tracing/tracing_max_latency + (Note that kernel size and overhead increases with this option + enabled. This option and the preempt-off timing option can be + used together or separately.) + +config PREEMPT_TRACER + bool "Preemption-off Latency Tracer" + default n + depends on GENERIC_TIME + depends on PREEMPT + select TRACING + select TRACER_MAX_TRACE + help + This option measures the time spent in preemption off critical + sections, with microsecond accuracy. + + The default measurement method is a maximum search, which is + disabled by default and can be runtime (re-)started + via: + + echo 0 > /debugfs/tracing/tracing_max_latency + + (Note that kernel size and overhead increases with this option + enabled. This option and the irqs-off timing option can be + used together or separately.) + config SCHED_TRACER bool "Scheduling Latency Tracer" depends on DEBUG_KERNEL diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 46be8647fb65..3fec653d6533 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FTRACE) += trace_functions.o obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o +obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o libftrace-y := ftrace.o diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index a9131b0cf1a5..8b1231633dc5 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -21,6 +21,36 @@ static struct trace_array *irqsoff_trace __read_mostly; static int tracer_enabled __read_mostly; +static DEFINE_PER_CPU(int, tracing_cpu); + +enum { + TRACER_IRQS_OFF = (1 << 1), + TRACER_PREEMPT_OFF = (1 << 2), +}; + +static int trace_type __read_mostly; + +#ifdef CONFIG_PREEMPT_TRACER +static inline int notrace +preempt_trace(void) +{ + return ((trace_type & TRACER_PREEMPT_OFF) && preempt_count()); +} +#else +# define preempt_trace() (0) +#endif + +#ifdef CONFIG_IRQSOFF_TRACER +static inline int notrace +irq_trace(void) +{ + return ((trace_type & TRACER_IRQS_OFF) && + irqs_disabled()); +} +#else +# define irq_trace() (0) +#endif + /* * Sequence count - we record it when starting a measurement and * skip the latency if the sequence has changed - some other section @@ -44,14 +74,11 @@ irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip) long disabled; int cpu; - if (likely(!tracer_enabled)) + if (likely(!__get_cpu_var(tracing_cpu))) return; local_save_flags(flags); - if (!irqs_disabled_flags(flags)) - return; - cpu = raw_smp_processor_id(); data = tr->data[cpu]; disabled = atomic_inc_return(&data->disabled); @@ -171,23 +198,29 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip) if (likely(!tracer_enabled)) return; + if (__get_cpu_var(tracing_cpu)) + return; + cpu = raw_smp_processor_id(); data = tr->data[cpu]; if (unlikely(!data) || unlikely(!data->trace) || - data->critical_start || atomic_read(&data->disabled)) + atomic_read(&data->disabled)) return; atomic_inc(&data->disabled); data->critical_sequence = max_sequence; data->preempt_timestamp = now(cpu); - data->critical_start = parent_ip; + data->critical_start = parent_ip ? : ip; tracing_reset(data); local_save_flags(flags); + ftrace(tr, data, ip, parent_ip, flags); + __get_cpu_var(tracing_cpu) = 1; + atomic_dec(&data->disabled); } @@ -199,7 +232,13 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) struct trace_array_cpu *data; unsigned long flags; - if (likely(!tracer_enabled)) + /* Always clear the tracing cpu on stopping the trace */ + if (unlikely(__get_cpu_var(tracing_cpu))) + __get_cpu_var(tracing_cpu) = 0; + else + return; + + if (!tracer_enabled) return; cpu = raw_smp_processor_id(); @@ -212,49 +251,35 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) atomic_inc(&data->disabled); local_save_flags(flags); ftrace(tr, data, ip, parent_ip, flags); - check_critical_timing(tr, data, parent_ip, cpu); + check_critical_timing(tr, data, parent_ip ? : ip, cpu); data->critical_start = 0; atomic_dec(&data->disabled); } +/* start and stop critical timings used to for stoppage (in idle) */ void notrace start_critical_timings(void) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (preempt_trace() || irq_trace()) start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); } void notrace stop_critical_timings(void) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (preempt_trace() || irq_trace()) stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); } +#ifdef CONFIG_IRQSOFF_TRACER #ifdef CONFIG_PROVE_LOCKING void notrace time_hardirqs_on(unsigned long a0, unsigned long a1) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (!preempt_trace() && irq_trace()) stop_critical_timing(a0, a1); } void notrace time_hardirqs_off(unsigned long a0, unsigned long a1) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (!preempt_trace() && irq_trace()) start_critical_timing(a0, a1); } @@ -289,49 +314,46 @@ inline void print_irqtrace_events(struct task_struct *curr) */ void notrace trace_hardirqs_on(void) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (!preempt_trace() && irq_trace()) stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); } EXPORT_SYMBOL(trace_hardirqs_on); void notrace trace_hardirqs_off(void) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (!preempt_trace() && irq_trace()) start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); } EXPORT_SYMBOL(trace_hardirqs_off); void notrace trace_hardirqs_on_caller(unsigned long caller_addr) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (!preempt_trace() && irq_trace()) stop_critical_timing(CALLER_ADDR0, caller_addr); } EXPORT_SYMBOL(trace_hardirqs_on_caller); void notrace trace_hardirqs_off_caller(unsigned long caller_addr) { - unsigned long flags; - - local_save_flags(flags); - - if (irqs_disabled_flags(flags)) + if (!preempt_trace() && irq_trace()) start_critical_timing(CALLER_ADDR0, caller_addr); } EXPORT_SYMBOL(trace_hardirqs_off_caller); #endif /* CONFIG_PROVE_LOCKING */ +#endif /* CONFIG_IRQSOFF_TRACER */ + +#ifdef CONFIG_PREEMPT_TRACER +void notrace trace_preempt_on(unsigned long a0, unsigned long a1) +{ + stop_critical_timing(a0, a1); +} + +void notrace trace_preempt_off(unsigned long a0, unsigned long a1) +{ + start_critical_timing(a0, a1); +} +#endif /* CONFIG_PREEMPT_TRACER */ static void start_irqsoff_tracer(struct trace_array *tr) { @@ -345,7 +367,7 @@ static void stop_irqsoff_tracer(struct trace_array *tr) tracer_enabled = 0; } -static void irqsoff_tracer_init(struct trace_array *tr) +static void __irqsoff_tracer_init(struct trace_array *tr) { irqsoff_trace = tr; /* make sure that the tracer is visibel */ @@ -382,6 +404,13 @@ static void notrace irqsoff_tracer_close(struct trace_iterator *iter) start_irqsoff_tracer(iter->tr); } +#ifdef CONFIG_IRQSOFF_TRACER +static void irqsoff_tracer_init(struct trace_array *tr) +{ + trace_type = TRACER_IRQS_OFF; + + __irqsoff_tracer_init(tr); +} static struct tracer irqsoff_tracer __read_mostly = { .name = "irqsoff", @@ -392,10 +421,65 @@ static struct tracer irqsoff_tracer __read_mostly = .ctrl_update = irqsoff_tracer_ctrl_update, .print_max = 1, }; +# define register_irqsoff(trace) register_tracer(&trace) +#else +# define register_irqsoff(trace) do { } while (0) +#endif + +#ifdef CONFIG_PREEMPT_TRACER +static void preemptoff_tracer_init(struct trace_array *tr) +{ + trace_type = TRACER_PREEMPT_OFF; + + __irqsoff_tracer_init(tr); +} + +static struct tracer preemptoff_tracer __read_mostly = +{ + .name = "preemptoff", + .init = preemptoff_tracer_init, + .reset = irqsoff_tracer_reset, + .open = irqsoff_tracer_open, + .close = irqsoff_tracer_close, + .ctrl_update = irqsoff_tracer_ctrl_update, + .print_max = 1, +}; +# define register_preemptoff(trace) register_tracer(&trace) +#else +# define register_preemptoff(trace) do { } while (0) +#endif + +#if defined(CONFIG_IRQSOFF_TRACER) && \ + defined(CONFIG_PREEMPT_TRACER) + +static void preemptirqsoff_tracer_init(struct trace_array *tr) +{ + trace_type = TRACER_IRQS_OFF | TRACER_PREEMPT_OFF; + + __irqsoff_tracer_init(tr); +} + +static struct tracer preemptirqsoff_tracer __read_mostly = +{ + .name = "preemptirqsoff", + .init = preemptirqsoff_tracer_init, + .reset = irqsoff_tracer_reset, + .open = irqsoff_tracer_open, + .close = irqsoff_tracer_close, + .ctrl_update = irqsoff_tracer_ctrl_update, + .print_max = 1, +}; + +# define register_preemptirqsoff(trace) register_tracer(&trace) +#else +# define register_preemptirqsoff(trace) do { } while (0) +#endif __init static int init_irqsoff_tracer(void) { - register_tracer(&irqsoff_tracer); + register_irqsoff(irqsoff_tracer); + register_preemptoff(preemptoff_tracer); + register_preemptirqsoff(preemptirqsoff_tracer); return 0; } -- cgit v1.2.3 From 3d0833953e1b98b79ddf491dd49229eef9baeac1 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:42 +0200 Subject: ftrace: dynamic enabling/disabling of function calls This patch adds a feature to dynamically replace the ftrace code with the jmps to allow a kernel with ftrace configured to run as fast as it can without it configured. The way this works, is on bootup (if ftrace is enabled), a ftrace function is registered to record the instruction pointer of all places that call the function. Later, if there's still any code to patch, a kthread is awoken (rate limited to at most once a second) that performs a stop_machine, and replaces all the code that was called with a jmp over the call to ftrace. It only replaces what was found the previous time. Typically the system reaches equilibrium quickly after bootup and there's no code patching needed at all. e.g. call ftrace /* 5 bytes */ is replaced with jmp 3f /* jmp is 2 bytes and we jump 3 forward */ 3: When we want to enable ftrace for function tracing, the IP recording is removed, and stop_machine is called again to replace all the locations of that were recorded back to the call of ftrace. When it is disabled, we replace the code back to the jmp. Allocation is done by the kthread. If the ftrace recording function is called, and we don't have any record slots available, then we simply skip that call. Once a second a new page (if needed) is allocated for recording new ftrace function calls. A large batch is allocated at boot up to get most of the calls there. Because we do this via stop_machine, we don't have to worry about another CPU executing a ftrace call as we modify it. But we do need to worry about NMI's so all functions that might be called via nmi must be annotated with notrace_nmi. When this code is configured in, the NMI code will not call notrace. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/Makefile | 1 + arch/x86/kernel/ftrace.c | 237 +++++++++++++++++++++++++++++++ include/linux/ftrace.h | 18 +++ kernel/trace/Kconfig | 17 +++ kernel/trace/ftrace.c | 356 ++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 597 insertions(+), 32 deletions(-) create mode 100644 arch/x86/kernel/ftrace.c (limited to 'include/linux') diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 5e618c3b4720..e142091524b0 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic_$(BITS).o nmi_$(BITS).o obj-$(CONFIG_X86_IO_APIC) += io_apic_$(BITS).o obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o +obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_KEXEC) += machine_kexec_$(BITS).o obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c new file mode 100644 index 000000000000..5dd58136ef02 --- /dev/null +++ b/arch/x86/kernel/ftrace.c @@ -0,0 +1,237 @@ +/* + * Code for replacing ftrace calls with jumps. + * + * Copyright (C) 2007-2008 Steven Rostedt + * + * Thanks goes to Ingo Molnar, for suggesting the idea. + * Mathieu Desnoyers, for suggesting postponing the modifications. + * Arjan van de Ven, for keeping me straight, and explaining to me + * the dangers of modifying code on the run. + */ + +#include +#include +#include +#include +#include +#include + +#define CALL_BACK 5 + +#define JMPFWD 0x03eb + +static unsigned short ftrace_jmp = JMPFWD; + +struct ftrace_record { + struct dyn_ftrace rec; + int failed; +} __attribute__((packed)); + +struct ftrace_page { + struct ftrace_page *next; + int index; + struct ftrace_record records[]; +} __attribute__((packed)); + +#define ENTRIES_PER_PAGE \ + ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct ftrace_record)) + +/* estimate from running different kernels */ +#define NR_TO_INIT 10000 + +#define MCOUNT_ADDR ((long)(&mcount)) + +union ftrace_code_union { + char code[5]; + struct { + char e8; + int offset; + } __attribute__((packed)); +}; + +static struct ftrace_page *ftrace_pages_start; +static struct ftrace_page *ftrace_pages; + +notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip) +{ + struct ftrace_record *rec; + unsigned short save; + + ip -= CALL_BACK; + save = *(short *)ip; + + /* If this was already converted, skip it */ + if (save == JMPFWD) + return NULL; + + if (ftrace_pages->index == ENTRIES_PER_PAGE) { + if (!ftrace_pages->next) + return NULL; + ftrace_pages = ftrace_pages->next; + } + + rec = &ftrace_pages->records[ftrace_pages->index++]; + + return &rec->rec; +} + +static int notrace +ftrace_modify_code(unsigned long ip, unsigned char *old_code, + unsigned char *new_code) +{ + unsigned short old = *(unsigned short *)old_code; + unsigned short new = *(unsigned short *)new_code; + unsigned short replaced; + int faulted = 0; + + /* + * Note: Due to modules and __init, code can + * disappear and change, we need to protect against faulting + * as well as code changing. + * + * No real locking needed, this code is run through + * kstop_machine. + */ + asm volatile ( + "1: lock\n" + " cmpxchg %w3, (%2)\n" + "2:\n" + ".section .fixup, \"ax\"\n" + " movl $1, %0\n" + "3: jmp 2b\n" + ".previous\n" + _ASM_EXTABLE(1b, 3b) + : "=r"(faulted), "=a"(replaced) + : "r"(ip), "r"(new), "0"(faulted), "a"(old) + : "memory"); + sync_core(); + + if (replaced != old) + faulted = 2; + + return faulted; +} + +static int notrace ftrace_calc_offset(long ip) +{ + return (int)(MCOUNT_ADDR - ip); +} + +notrace void ftrace_code_disable(struct dyn_ftrace *rec) +{ + unsigned long ip; + union ftrace_code_union save; + struct ftrace_record *r = + container_of(rec, struct ftrace_record, rec); + + ip = rec->ip; + + save.e8 = 0xe8; + save.offset = ftrace_calc_offset(ip); + + /* move the IP back to the start of the call */ + ip -= CALL_BACK; + + r->failed = ftrace_modify_code(ip, save.code, (char *)&ftrace_jmp); +} + +static void notrace ftrace_replace_code(int saved) +{ + unsigned char *new = NULL, *old = NULL; + struct ftrace_record *rec; + struct ftrace_page *pg; + unsigned long ip; + int i; + + if (saved) + old = (char *)&ftrace_jmp; + else + new = (char *)&ftrace_jmp; + + for (pg = ftrace_pages_start; pg; pg = pg->next) { + for (i = 0; i < pg->index; i++) { + union ftrace_code_union calc; + rec = &pg->records[i]; + + /* don't modify code that has already faulted */ + if (rec->failed) + continue; + + ip = rec->rec.ip; + + calc.e8 = 0xe8; + calc.offset = ftrace_calc_offset(ip); + + if (saved) + new = calc.code; + else + old = calc.code; + + ip -= CALL_BACK; + + rec->failed = ftrace_modify_code(ip, old, new); + } + } + +} + +notrace void ftrace_startup_code(void) +{ + ftrace_replace_code(1); +} + +notrace void ftrace_shutdown_code(void) +{ + ftrace_replace_code(0); +} + +notrace void ftrace_shutdown_replenish(void) +{ + if (ftrace_pages->next) + return; + + /* allocate another page */ + ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); +} + +notrace int ftrace_shutdown_arch_init(void) +{ + struct ftrace_page *pg; + int cnt; + int i; + + /* allocate a few pages */ + ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL); + if (!ftrace_pages_start) + return -1; + + /* + * Allocate a few more pages. + * + * TODO: have some parser search vmlinux before + * final linking to find all calls to ftrace. + * Then we can: + * a) know how many pages to allocate. + * and/or + * b) set up the table then. + * + * The dynamic code is still necessary for + * modules. + */ + + pg = ftrace_pages = ftrace_pages_start; + + cnt = NR_TO_INIT / ENTRIES_PER_PAGE; + + for (i = 0; i < cnt; i++) { + pg->next = (void *)get_zeroed_page(GFP_KERNEL); + + /* If we fail, we'll try later anyway */ + if (!pg->next) + break; + + pg = pg->next; + } + + return 0; +} diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 740c97dcf9cb..90dbc0ee2046 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -32,6 +32,24 @@ extern void mcount(void); # define clear_ftrace_function(ops) do { } while (0) #endif /* CONFIG_FTRACE */ +#ifdef CONFIG_DYNAMIC_FTRACE +# define FTRACE_HASHBITS 10 +# define FTRACE_HASHSIZE (1< +#include +#include +#include +#include +#include #include +#include +#include +#include + +#include "trace.h" -static DEFINE_SPINLOCK(ftrace_func_lock); +static DEFINE_SPINLOCK(ftrace_lock); static struct ftrace_ops ftrace_list_end __read_mostly = { .func = ftrace_stub, @@ -44,21 +53,21 @@ notrace void ftrace_list_func(unsigned long ip, unsigned long parent_ip) } /** - * register_ftrace_function - register a function for profiling - * @ops - ops structure that holds the function for profiling. - * - * Register a function to be called by all functions in the - * kernel. + * clear_ftrace_function - reset the ftrace function * - * Note: @ops->func and all the functions it calls must be labeled - * with "notrace", otherwise it will go into a - * recursive loop. + * This NULLs the ftrace function and in essence stops + * tracing. There may be lag */ -int register_ftrace_function(struct ftrace_ops *ops) +void clear_ftrace_function(void) { - unsigned long flags; + ftrace_trace_function = ftrace_stub; +} + +static int notrace __register_ftrace_function(struct ftrace_ops *ops) +{ + /* Should never be called by interrupts */ + spin_lock(&ftrace_lock); - spin_lock_irqsave(&ftrace_func_lock, flags); ops->next = ftrace_list; /* * We are entering ops into the ftrace_list but another @@ -68,6 +77,7 @@ int register_ftrace_function(struct ftrace_ops *ops) */ smp_wmb(); ftrace_list = ops; + /* * For one func, simply call it directly. * For more than one func, call the chain. @@ -76,28 +86,22 @@ int register_ftrace_function(struct ftrace_ops *ops) ftrace_trace_function = ops->func; else ftrace_trace_function = ftrace_list_func; - spin_unlock_irqrestore(&ftrace_func_lock, flags); + + spin_unlock(&ftrace_lock); return 0; } -/** - * unregister_ftrace_function - unresgister a function for profiling. - * @ops - ops structure that holds the function to unregister - * - * Unregister a function that was added to be called by ftrace profiling. - */ -int unregister_ftrace_function(struct ftrace_ops *ops) +static int notrace __unregister_ftrace_function(struct ftrace_ops *ops) { - unsigned long flags; struct ftrace_ops **p; int ret = 0; - spin_lock_irqsave(&ftrace_func_lock, flags); + spin_lock(&ftrace_lock); /* - * If we are the only function, then the ftrace pointer is - * pointing directly to that function. + * If we are removing the last function, then simply point + * to the ftrace_stub. */ if (ftrace_list == ops && ops->next == &ftrace_list_end) { ftrace_trace_function = ftrace_stub; @@ -117,22 +121,310 @@ int unregister_ftrace_function(struct ftrace_ops *ops) *p = (*p)->next; /* If we only have one func left, then call that directly */ - if (ftrace_list->next == &ftrace_list_end) + if (ftrace_list == &ftrace_list_end || + ftrace_list->next == &ftrace_list_end) ftrace_trace_function = ftrace_list->func; out: - spin_unlock_irqrestore(&ftrace_func_lock, flags); + spin_unlock(&ftrace_lock); + + return ret; +} + +#ifdef CONFIG_DYNAMIC_FTRACE + +static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; + +static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu); + +static DEFINE_SPINLOCK(ftrace_shutdown_lock); +static DEFINE_MUTEX(ftraced_lock); + +static int ftraced_trigger; +static int ftraced_suspend; + +static int ftrace_record_suspend; + +static inline int +notrace ftrace_ip_in_hash(unsigned long ip, unsigned long key) +{ + struct dyn_ftrace *p; + struct hlist_node *t; + int found = 0; + + hlist_for_each_entry(p, t, &ftrace_hash[key], node) { + if (p->ip == ip) { + found = 1; + break; + } + } + + return found; +} + +static inline void notrace +ftrace_add_hash(struct dyn_ftrace *node, unsigned long key) +{ + hlist_add_head(&node->node, &ftrace_hash[key]); +} + +static void notrace +ftrace_record_ip(unsigned long ip, unsigned long parent_ip) +{ + struct dyn_ftrace *node; + unsigned long flags; + unsigned long key; + int resched; + int atomic; + + resched = need_resched(); + preempt_disable_notrace(); + + /* We simply need to protect against recursion */ + __get_cpu_var(ftrace_shutdown_disable_cpu)++; + if (__get_cpu_var(ftrace_shutdown_disable_cpu) != 1) + goto out; + + if (unlikely(ftrace_record_suspend)) + goto out; + + key = hash_long(ip, FTRACE_HASHBITS); + + WARN_ON_ONCE(key >= FTRACE_HASHSIZE); + + if (ftrace_ip_in_hash(ip, key)) + goto out; + + atomic = irqs_disabled(); + + spin_lock_irqsave(&ftrace_shutdown_lock, flags); + + /* This ip may have hit the hash before the lock */ + if (ftrace_ip_in_hash(ip, key)) + goto out_unlock; + + /* + * There's a slight race that the ftraced will update the + * hash and reset here. The arch alloc is responsible + * for seeing if the IP has already changed, and if + * it has, the alloc will fail. + */ + node = ftrace_alloc_shutdown_node(ip); + if (!node) + goto out_unlock; + + node->ip = ip; + + ftrace_add_hash(node, key); + + ftraced_trigger = 1; + + out_unlock: + spin_unlock_irqrestore(&ftrace_shutdown_lock, flags); + out: + __get_cpu_var(ftrace_shutdown_disable_cpu)--; + + /* prevent recursion with scheduler */ + if (resched) + preempt_enable_no_resched_notrace(); + else + preempt_enable_notrace(); +} + +static struct ftrace_ops ftrace_shutdown_ops __read_mostly = +{ + .func = ftrace_record_ip, +}; + + +static int notrace __ftrace_modify_code(void *data) +{ + void (*func)(void) = data; + + func(); + return 0; +} + +static void notrace ftrace_run_startup_code(void) +{ + stop_machine_run(__ftrace_modify_code, ftrace_startup_code, NR_CPUS); +} + +static void notrace ftrace_run_shutdown_code(void) +{ + stop_machine_run(__ftrace_modify_code, ftrace_shutdown_code, NR_CPUS); +} + +static void notrace ftrace_startup(void) +{ + mutex_lock(&ftraced_lock); + ftraced_suspend++; + if (ftraced_suspend != 1) + goto out; + __unregister_ftrace_function(&ftrace_shutdown_ops); + + ftrace_run_startup_code(); + out: + mutex_unlock(&ftraced_lock); +} + +static void notrace ftrace_shutdown(void) +{ + mutex_lock(&ftraced_lock); + ftraced_suspend--; + if (ftraced_suspend) + goto out; + + ftrace_run_shutdown_code(); + + __register_ftrace_function(&ftrace_shutdown_ops); + out: + mutex_unlock(&ftraced_lock); +} + +static cycle_t ftrace_update_time; +static unsigned long ftrace_update_cnt; +unsigned long ftrace_update_tot_cnt; + +static int notrace __ftrace_update_code(void *ignore) +{ + struct dyn_ftrace *p; + struct hlist_head head; + struct hlist_node *t; + cycle_t start, stop; + int i; + + /* Don't be calling ftrace ops now */ + __unregister_ftrace_function(&ftrace_shutdown_ops); + + start = now(raw_smp_processor_id()); + ftrace_update_cnt = 0; + + /* No locks needed, the machine is stopped! */ + for (i = 0; i < FTRACE_HASHSIZE; i++) { + if (hlist_empty(&ftrace_hash[i])) + continue; + + head = ftrace_hash[i]; + INIT_HLIST_HEAD(&ftrace_hash[i]); + + /* all CPUS are stopped, we are safe to modify code */ + hlist_for_each_entry(p, t, &head, node) { + ftrace_code_disable(p); + ftrace_update_cnt++; + } + + } + + stop = now(raw_smp_processor_id()); + ftrace_update_time = stop - start; + ftrace_update_tot_cnt += ftrace_update_cnt; + + __register_ftrace_function(&ftrace_shutdown_ops); return 0; } +static void notrace ftrace_update_code(void) +{ + stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); +} + +static int notrace ftraced(void *ignore) +{ + unsigned long usecs; + + set_current_state(TASK_INTERRUPTIBLE); + + while (!kthread_should_stop()) { + + /* check once a second */ + schedule_timeout(HZ); + + mutex_lock(&ftraced_lock); + if (ftraced_trigger && !ftraced_suspend) { + ftrace_record_suspend++; + ftrace_update_code(); + usecs = nsecs_to_usecs(ftrace_update_time); + if (ftrace_update_tot_cnt > 100000) { + ftrace_update_tot_cnt = 0; + pr_info("hm, dftrace overflow: %lu change%s" + " (%lu total) in %lu usec%s\n", + ftrace_update_cnt, + ftrace_update_cnt != 1 ? "s" : "", + ftrace_update_tot_cnt, + usecs, usecs != 1 ? "s" : ""); + WARN_ON_ONCE(1); + } + ftraced_trigger = 0; + ftrace_record_suspend--; + } + mutex_unlock(&ftraced_lock); + + ftrace_shutdown_replenish(); + + set_current_state(TASK_INTERRUPTIBLE); + } + __set_current_state(TASK_RUNNING); + return 0; +} + +static int __init notrace ftrace_shutdown_init(void) +{ + struct task_struct *p; + int ret; + + ret = ftrace_shutdown_arch_init(); + if (ret) + return ret; + + p = kthread_run(ftraced, NULL, "ftraced"); + if (IS_ERR(p)) + return -1; + + __register_ftrace_function(&ftrace_shutdown_ops); + + return 0; +} + +core_initcall(ftrace_shutdown_init); +#else +# define ftrace_startup() do { } while (0) +# define ftrace_shutdown() do { } while (0) +#endif /* CONFIG_DYNAMIC_FTRACE */ + /** - * clear_ftrace_function - reset the ftrace function + * register_ftrace_function - register a function for profiling + * @ops - ops structure that holds the function for profiling. * - * This NULLs the ftrace function and in essence stops - * tracing. There may be lag + * Register a function to be called by all functions in the + * kernel. + * + * Note: @ops->func and all the functions it calls must be labeled + * with "notrace", otherwise it will go into a + * recursive loop. */ -void clear_ftrace_function(void) +int register_ftrace_function(struct ftrace_ops *ops) { - ftrace_trace_function = ftrace_stub; + ftrace_startup(); + + return __register_ftrace_function(ops); +} + +/** + * unregister_ftrace_function - unresgister a function for profiling. + * @ops - ops structure that holds the function to unregister + * + * Unregister a function that was added to be called by ftrace profiling. + */ +int unregister_ftrace_function(struct ftrace_ops *ops) +{ + int ret; + + ret = __unregister_ftrace_function(ops); + + if (ftrace_list == &ftrace_list_end) + ftrace_shutdown(); + + return ret; } -- cgit v1.2.3 From b0fc494fae96a7089f3651cb451f461c7291244c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:43 +0200 Subject: ftrace: add ftrace_enabled sysctl to disable mcount function This patch adds back the sysctl ftrace_enabled. This time it is defaulted to on, if DYNAMIC_FTRACE is configured. When ftrace_enabled is disabled, the ftrace function is set to the stub return. If DYNAMIC_FTRACE is also configured, on ftrace_enabled = 0, the registered ftrace functions will all be set to jmps, but no more new calls to ftrace recording (used to find the ftrace calling sites) will be called. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 6 +++ kernel/sysctl.c | 11 +++++ kernel/trace/ftrace.c | 125 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 124 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 90dbc0ee2046..ccd8537dbdb7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -5,6 +5,12 @@ #include +extern int ftrace_enabled; +extern int +ftrace_enable_sysctl(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos); + typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); struct ftrace_ops { diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 29116652dca8..efaf7c5500e9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -455,6 +456,16 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, +#ifdef CONFIG_FTRACE + { + .ctl_name = CTL_UNNUMBERED, + .procname = "ftrace_enabled", + .data = &ftrace_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &ftrace_enable_sysctl, + }, +#endif #ifdef CONFIG_KMOD { .ctl_name = KERN_MODPROBE, diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d1ae2ba25274..d3de37299ba4 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -20,12 +20,24 @@ #include #include #include +#include #include #include #include "trace.h" +#ifdef CONFIG_DYNAMIC_FTRACE +# define FTRACE_ENABLED_INIT 1 +#else +# define FTRACE_ENABLED_INIT 0 +#endif + +int ftrace_enabled = FTRACE_ENABLED_INIT; +static int last_ftrace_enabled = FTRACE_ENABLED_INIT; + static DEFINE_SPINLOCK(ftrace_lock); +static DEFINE_MUTEX(ftrace_sysctl_lock); + static struct ftrace_ops ftrace_list_end __read_mostly = { .func = ftrace_stub, @@ -78,14 +90,16 @@ static int notrace __register_ftrace_function(struct ftrace_ops *ops) smp_wmb(); ftrace_list = ops; - /* - * For one func, simply call it directly. - * For more than one func, call the chain. - */ - if (ops->next == &ftrace_list_end) - ftrace_trace_function = ops->func; - else - ftrace_trace_function = ftrace_list_func; + if (ftrace_enabled) { + /* + * For one func, simply call it directly. + * For more than one func, call the chain. + */ + if (ops->next == &ftrace_list_end) + ftrace_trace_function = ops->func; + else + ftrace_trace_function = ftrace_list_func; + } spin_unlock(&ftrace_lock); @@ -120,10 +134,12 @@ static int notrace __unregister_ftrace_function(struct ftrace_ops *ops) *p = (*p)->next; - /* If we only have one func left, then call that directly */ - if (ftrace_list == &ftrace_list_end || - ftrace_list->next == &ftrace_list_end) - ftrace_trace_function = ftrace_list->func; + if (ftrace_enabled) { + /* If we only have one func left, then call that directly */ + if (ftrace_list == &ftrace_list_end || + ftrace_list->next == &ftrace_list_end) + ftrace_trace_function = ftrace_list->func; + } out: spin_unlock(&ftrace_lock); @@ -263,7 +279,8 @@ static void notrace ftrace_startup(void) goto out; __unregister_ftrace_function(&ftrace_shutdown_ops); - ftrace_run_startup_code(); + if (ftrace_enabled) + ftrace_run_startup_code(); out: mutex_unlock(&ftraced_lock); } @@ -275,13 +292,32 @@ static void notrace ftrace_shutdown(void) if (ftraced_suspend) goto out; - ftrace_run_shutdown_code(); + if (ftrace_enabled) + ftrace_run_shutdown_code(); __register_ftrace_function(&ftrace_shutdown_ops); out: mutex_unlock(&ftraced_lock); } +static void notrace ftrace_startup_sysctl(void) +{ + mutex_lock(&ftraced_lock); + /* ftraced_suspend is true if we want ftrace running */ + if (ftraced_suspend) + ftrace_run_startup_code(); + mutex_unlock(&ftraced_lock); +} + +static void notrace ftrace_shutdown_sysctl(void) +{ + mutex_lock(&ftraced_lock); + /* ftraced_suspend is true if ftrace is running */ + if (ftraced_suspend) + ftrace_run_shutdown_code(); + mutex_unlock(&ftraced_lock); +} + static cycle_t ftrace_update_time; static unsigned long ftrace_update_cnt; unsigned long ftrace_update_tot_cnt; @@ -341,8 +377,9 @@ static int notrace ftraced(void *ignore) /* check once a second */ schedule_timeout(HZ); + mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftraced_lock); - if (ftraced_trigger && !ftraced_suspend) { + if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { ftrace_record_suspend++; ftrace_update_code(); usecs = nsecs_to_usecs(ftrace_update_time); @@ -360,6 +397,7 @@ static int notrace ftraced(void *ignore) ftrace_record_suspend--; } mutex_unlock(&ftraced_lock); + mutex_unlock(&ftrace_sysctl_lock); ftrace_shutdown_replenish(); @@ -389,8 +427,10 @@ static int __init notrace ftrace_shutdown_init(void) core_initcall(ftrace_shutdown_init); #else -# define ftrace_startup() do { } while (0) -# define ftrace_shutdown() do { } while (0) +# define ftrace_startup() do { } while (0) +# define ftrace_shutdown() do { } while (0) +# define ftrace_startup_sysctl() do { } while (0) +# define ftrace_shutdown_sysctl() do { } while (0) #endif /* CONFIG_DYNAMIC_FTRACE */ /** @@ -406,9 +446,15 @@ core_initcall(ftrace_shutdown_init); */ int register_ftrace_function(struct ftrace_ops *ops) { + int ret; + + mutex_lock(&ftrace_sysctl_lock); ftrace_startup(); - return __register_ftrace_function(ops); + ret = __register_ftrace_function(ops); + mutex_unlock(&ftrace_sysctl_lock); + + return ret; } /** @@ -421,10 +467,53 @@ int unregister_ftrace_function(struct ftrace_ops *ops) { int ret; + mutex_lock(&ftrace_sysctl_lock); ret = __unregister_ftrace_function(ops); if (ftrace_list == &ftrace_list_end) ftrace_shutdown(); + mutex_unlock(&ftrace_sysctl_lock); + + return ret; +} + +notrace int +ftrace_enable_sysctl(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + + mutex_lock(&ftrace_sysctl_lock); + + ret = proc_dointvec(table, write, filp, buffer, lenp, ppos); + + if (ret || !write || (last_ftrace_enabled == ftrace_enabled)) + goto out; + + last_ftrace_enabled = ftrace_enabled; + + if (ftrace_enabled) { + + ftrace_startup_sysctl(); + + /* we are starting ftrace again */ + if (ftrace_list != &ftrace_list_end) { + if (ftrace_list->next == &ftrace_list_end) + ftrace_trace_function = ftrace_list->func; + else + ftrace_trace_function = ftrace_list_func; + } + + } else { + /* stopping ftrace calls (just send to ftrace_stub) */ + ftrace_trace_function = ftrace_stub; + + ftrace_shutdown_sysctl(); + } + + out: + mutex_unlock(&ftrace_sysctl_lock); return ret; } -- cgit v1.2.3 From 3c1720f00bb619302ba19d55986ab565e74d06db Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:43 +0200 Subject: ftrace: move memory management out of arch code This patch moves the memory management of the ftrace records out of the arch code and into the generic code making the arch code simpler. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/ftrace.c | 183 ++++++++--------------------------------------- include/linux/ftrace.h | 18 +++-- kernel/trace/ftrace.c | 154 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 192 insertions(+), 163 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 2e060c58b860..b69795efa226 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -23,25 +23,6 @@ /* Long is fine, even if it is only 4 bytes ;-) */ static long *ftrace_nop; -struct ftrace_record { - struct dyn_ftrace rec; - int failed; -} __attribute__((packed)); - -struct ftrace_page { - struct ftrace_page *next; - int index; - struct ftrace_record records[]; -} __attribute__((packed)); - -#define ENTRIES_PER_PAGE \ - ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct ftrace_record)) - -/* estimate from running different kernels */ -#define NR_TO_INIT 10000 - -#define MCOUNT_ADDR ((long)(&mcount)) - union ftrace_code_union { char code[5]; struct { @@ -50,33 +31,41 @@ union ftrace_code_union { } __attribute__((packed)); }; -static struct ftrace_page *ftrace_pages_start; -static struct ftrace_page *ftrace_pages; - -notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip) +notrace int ftrace_ip_converted(unsigned long ip) { - struct ftrace_record *rec; unsigned long save; ip -= CALL_BACK; save = *(long *)ip; - /* If this was already converted, skip it */ - if (save == *ftrace_nop) - return NULL; + return save == *ftrace_nop; +} - if (ftrace_pages->index == ENTRIES_PER_PAGE) { - if (!ftrace_pages->next) - return NULL; - ftrace_pages = ftrace_pages->next; - } +static int notrace ftrace_calc_offset(long ip, long addr) +{ + return (int)(addr - ip); +} - rec = &ftrace_pages->records[ftrace_pages->index++]; +notrace unsigned char *ftrace_nop_replace(void) +{ + return (char *)ftrace_nop; +} + +notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) +{ + static union ftrace_code_union calc; - return &rec->rec; + calc.e8 = 0xe8; + calc.offset = ftrace_calc_offset(ip, addr); + + /* + * No locking needed, this must be called via kstop_machine + * which in essence is like running on a uniprocessor machine. + */ + return calc.code; } -static int notrace +notrace int ftrace_modify_code(unsigned long ip, unsigned char *old_code, unsigned char *new_code) { @@ -86,6 +75,9 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, unsigned char newch = new_code[4]; int faulted = 0; + /* move the IP back to the start of the call */ + ip -= CALL_BACK; + /* * Note: Due to modules and __init, code can * disappear and change, we need to protect against faulting @@ -117,129 +109,12 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, return faulted; } -static int notrace ftrace_calc_offset(long ip) -{ - return (int)(MCOUNT_ADDR - ip); -} - -notrace void ftrace_code_disable(struct dyn_ftrace *rec) -{ - unsigned long ip; - union ftrace_code_union save; - struct ftrace_record *r = - container_of(rec, struct ftrace_record, rec); - - ip = rec->ip; - - save.e8 = 0xe8; - save.offset = ftrace_calc_offset(ip); - - /* move the IP back to the start of the call */ - ip -= CALL_BACK; - - r->failed = ftrace_modify_code(ip, save.code, (char *)ftrace_nop); -} - -static void notrace ftrace_replace_code(int saved) -{ - unsigned char *new = NULL, *old = NULL; - struct ftrace_record *rec; - struct ftrace_page *pg; - unsigned long ip; - int i; - - if (saved) - old = (char *)ftrace_nop; - else - new = (char *)ftrace_nop; - - for (pg = ftrace_pages_start; pg; pg = pg->next) { - for (i = 0; i < pg->index; i++) { - union ftrace_code_union calc; - rec = &pg->records[i]; - - /* don't modify code that has already faulted */ - if (rec->failed) - continue; - - ip = rec->rec.ip; - - calc.e8 = 0xe8; - calc.offset = ftrace_calc_offset(ip); - - if (saved) - new = calc.code; - else - old = calc.code; - - ip -= CALL_BACK; - - rec->failed = ftrace_modify_code(ip, old, new); - } - } - -} - -notrace void ftrace_startup_code(void) -{ - ftrace_replace_code(1); -} - -notrace void ftrace_shutdown_code(void) -{ - ftrace_replace_code(0); -} - -notrace void ftrace_shutdown_replenish(void) -{ - if (ftrace_pages->next) - return; - - /* allocate another page */ - ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); -} - -notrace int __init ftrace_shutdown_arch_init(void) +int __init ftrace_dyn_arch_init(void) { const unsigned char *const *noptable = find_nop_table(); - struct ftrace_page *pg; - int cnt; - int i; ftrace_nop = (unsigned long *)noptable[CALL_BACK]; - /* allocate a few pages */ - ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL); - if (!ftrace_pages_start) - return -1; - - /* - * Allocate a few more pages. - * - * TODO: have some parser search vmlinux before - * final linking to find all calls to ftrace. - * Then we can: - * a) know how many pages to allocate. - * and/or - * b) set up the table then. - * - * The dynamic code is still necessary for - * modules. - */ - - pg = ftrace_pages = ftrace_pages_start; - - cnt = NR_TO_INIT / ENTRIES_PER_PAGE; - - for (i = 0; i < cnt; i++) { - pg->next = (void *)get_zeroed_page(GFP_KERNEL); - - /* If we fail, we'll try later anyway */ - if (!pg->next) - break; - - pg = pg->next; - } - return 0; } + diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ccd8537dbdb7..d509ad6c9cb8 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -42,19 +42,23 @@ extern void mcount(void); # define FTRACE_HASHBITS 10 # define FTRACE_HASHSIZE (1<node, &ftrace_hash[key]); } +static notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip) +{ + /* If this was already converted, skip it */ + if (ftrace_ip_converted(ip)) + return NULL; + + if (ftrace_pages->index == ENTRIES_PER_PAGE) { + if (!ftrace_pages->next) + return NULL; + ftrace_pages = ftrace_pages->next; + } + + return &ftrace_pages->records[ftrace_pages->index++]; +} + static void notrace ftrace_record_ip(unsigned long ip, unsigned long parent_ip) { @@ -252,6 +282,62 @@ static struct ftrace_ops ftrace_shutdown_ops __read_mostly = .func = ftrace_record_ip, }; +#define MCOUNT_ADDR ((long)(&mcount)) + +static void notrace ftrace_replace_code(int saved) +{ + unsigned char *new = NULL, *old = NULL; + struct dyn_ftrace *rec; + struct ftrace_page *pg; + unsigned long ip; + int failed; + int i; + + if (saved) + old = ftrace_nop_replace(); + else + new = ftrace_nop_replace(); + + for (pg = ftrace_pages_start; pg; pg = pg->next) { + for (i = 0; i < pg->index; i++) { + rec = &pg->records[i]; + + /* don't modify code that has already faulted */ + if (rec->flags & FTRACE_FL_FAILED) + continue; + + ip = rec->ip; + + if (saved) + new = ftrace_call_replace(ip, MCOUNT_ADDR); + else + old = ftrace_call_replace(ip, MCOUNT_ADDR); + + failed = ftrace_modify_code(ip, old, new); + if (failed) + rec->flags |= FTRACE_FL_FAILED; + } + } +} + +static notrace void ftrace_startup_code(void) +{ + ftrace_replace_code(1); +} + +static notrace void ftrace_shutdown_code(void) +{ + ftrace_replace_code(0); +} + +static notrace void ftrace_shutdown_replenish(void) +{ + if (ftrace_pages->next) + return; + + /* allocate another page */ + ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); +} static int notrace __ftrace_modify_code(void *data) { @@ -261,6 +347,23 @@ static int notrace __ftrace_modify_code(void *data) return 0; } +static notrace void +ftrace_code_disable(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long ip; + unsigned char *nop, *call; + int failed; + + ip = rec->ip; + + nop = ftrace_nop_replace(); + call = ftrace_call_replace(ip, addr); + + failed = ftrace_modify_code(ip, call, nop); + if (failed) + rec->flags |= FTRACE_FL_FAILED; +} + static void notrace ftrace_run_startup_code(void) { stop_machine_run(__ftrace_modify_code, ftrace_startup_code, NR_CPUS); @@ -346,7 +449,7 @@ static int notrace __ftrace_update_code(void *ignore) /* all CPUS are stopped, we are safe to modify code */ hlist_for_each_entry(p, t, &head, node) { - ftrace_code_disable(p); + ftrace_code_disable(p, MCOUNT_ADDR); ftrace_update_cnt++; } @@ -407,12 +510,59 @@ static int notrace ftraced(void *ignore) return 0; } +static int __init ftrace_dyn_table_alloc(void) +{ + struct ftrace_page *pg; + int cnt; + int i; + int ret; + + ret = ftrace_dyn_arch_init(); + if (ret) + return ret; + + /* allocate a few pages */ + ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL); + if (!ftrace_pages_start) + return -1; + + /* + * Allocate a few more pages. + * + * TODO: have some parser search vmlinux before + * final linking to find all calls to ftrace. + * Then we can: + * a) know how many pages to allocate. + * and/or + * b) set up the table then. + * + * The dynamic code is still necessary for + * modules. + */ + + pg = ftrace_pages = ftrace_pages_start; + + cnt = NR_TO_INIT / ENTRIES_PER_PAGE; + + for (i = 0; i < cnt; i++) { + pg->next = (void *)get_zeroed_page(GFP_KERNEL); + + /* If we fail, we'll try later anyway */ + if (!pg->next) + break; + + pg = pg->next; + } + + return 0; +} + static int __init notrace ftrace_shutdown_init(void) { struct task_struct *p; int ret; - ret = ftrace_shutdown_arch_init(); + ret = ftrace_dyn_table_alloc(); if (ret) return ret; -- cgit v1.2.3 From d61f82d06672f57fca410da6f7fffd15867db622 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:43 +0200 Subject: ftrace: use dynamic patching for updating mcount calls This patch replaces the indirect call to the mcount function pointer with a direct call that will be patched by the dynamic ftrace routines. On boot up, the mcount function calls the ftace_stub function. When the dynamic ftrace code is initialized, the ftrace_stub is replaced with a call to the ftrace_record_ip, which records the instruction pointers of the locations that call it. Later, the ftraced daemon will call kstop_machine and patch all the locations to nops. When a ftrace is enabled, the original calls to mcount will now be set top call ftrace_caller, which will do a direct call to the registered ftrace function. This direct call is also patched when the function that should be called is updated. All patching is performed by a kstop_machine routine to prevent any type of race conditions that is associated with modifying code on the fly. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/entry_32.S | 47 +++++++++++- arch/x86/kernel/entry_64.S | 67 ++++++++++++++++- arch/x86/kernel/ftrace.c | 41 +++++++++- include/linux/ftrace.h | 7 +- kernel/trace/ftrace.c | 183 ++++++++++++++++++++++++++------------------- 5 files changed, 261 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index f47b9b5440d2..e6517ce0b824 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -1110,10 +1110,50 @@ ENDPROC(xen_failsafe_callback) #endif /* CONFIG_XEN */ #ifdef CONFIG_FTRACE +#ifdef CONFIG_DYNAMIC_FTRACE + +ENTRY(mcount) + pushl %eax + pushl %ecx + pushl %edx + movl 0xc(%esp), %eax + +.globl mcount_call +mcount_call: + call ftrace_stub + + popl %edx + popl %ecx + popl %eax + + ret +END(mcount) + +ENTRY(ftrace_caller) + pushl %eax + pushl %ecx + pushl %edx + movl 0xc(%esp), %eax + movl 0x4(%ebp), %edx + +.globl ftrace_call +ftrace_call: + call ftrace_stub + + popl %edx + popl %ecx + popl %eax + +.globl ftrace_stub +ftrace_stub: + ret +END(ftrace_caller) + +#else /* ! CONFIG_DYNAMIC_FTRACE */ + ENTRY(mcount) cmpl $ftrace_stub, ftrace_trace_function jnz trace - .globl ftrace_stub ftrace_stub: ret @@ -1126,7 +1166,7 @@ trace: movl 0xc(%esp), %eax movl 0x4(%ebp), %edx - call *ftrace_trace_function + call *ftrace_trace_function popl %edx popl %ecx @@ -1134,7 +1174,8 @@ trace: jmp ftrace_stub END(mcount) -#endif +#endif /* CONFIG_DYNAMIC_FTRACE */ +#endif /* CONFIG_FTRACE */ .section .rodata,"a" #include "syscall_table_32.S" diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index f046e0c64883..fe25e5febca3 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -55,6 +55,70 @@ .code64 #ifdef CONFIG_FTRACE +#ifdef CONFIG_DYNAMIC_FTRACE +ENTRY(mcount) + + subq $0x38, %rsp + movq %rax, (%rsp) + movq %rcx, 8(%rsp) + movq %rdx, 16(%rsp) + movq %rsi, 24(%rsp) + movq %rdi, 32(%rsp) + movq %r8, 40(%rsp) + movq %r9, 48(%rsp) + + movq 0x38(%rsp), %rdi + +.globl mcount_call +mcount_call: + call ftrace_stub + + movq 48(%rsp), %r9 + movq 40(%rsp), %r8 + movq 32(%rsp), %rdi + movq 24(%rsp), %rsi + movq 16(%rsp), %rdx + movq 8(%rsp), %rcx + movq (%rsp), %rax + addq $0x38, %rsp + + retq +END(mcount) + +ENTRY(ftrace_caller) + + /* taken from glibc */ + subq $0x38, %rsp + movq %rax, (%rsp) + movq %rcx, 8(%rsp) + movq %rdx, 16(%rsp) + movq %rsi, 24(%rsp) + movq %rdi, 32(%rsp) + movq %r8, 40(%rsp) + movq %r9, 48(%rsp) + + movq 0x38(%rsp), %rdi + movq 8(%rbp), %rsi + +.globl ftrace_call +ftrace_call: + call ftrace_stub + + movq 48(%rsp), %r9 + movq 40(%rsp), %r8 + movq 32(%rsp), %rdi + movq 24(%rsp), %rsi + movq 16(%rsp), %rdx + movq 8(%rsp), %rcx + movq (%rsp), %rax + addq $0x38, %rsp + +.globl ftrace_stub +ftrace_stub: + retq +END(ftrace_caller) + +#else /* ! CONFIG_DYNAMIC_FTRACE */ ENTRY(mcount) cmpq $ftrace_stub, ftrace_trace_function jnz trace @@ -89,7 +153,8 @@ trace: jmp ftrace_stub END(mcount) -#endif +#endif /* CONFIG_DYNAMIC_FTRACE */ +#endif /* CONFIG_FTRACE */ #ifndef CONFIG_PREEMPT #define retint_kernel retint_restore_args diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index b69795efa226..9f44623e0072 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -109,10 +109,49 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, return faulted; } -int __init ftrace_dyn_arch_init(void) +notrace int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long ip = (unsigned long)(&ftrace_call); + unsigned char old[5], *new; + int ret; + + ip += CALL_BACK; + + memcpy(old, &ftrace_call, 5); + new = ftrace_call_replace(ip, (unsigned long)func); + ret = ftrace_modify_code(ip, old, new); + + return ret; +} + +notrace int ftrace_mcount_set(unsigned long *data) +{ + unsigned long ip = (long)(&mcount_call); + unsigned long *addr = data; + unsigned char old[5], *new; + + /* ip is at the location, but modify code will subtact this */ + ip += CALL_BACK; + + /* + * Replace the mcount stub with a pointer to the + * ip recorder function. + */ + memcpy(old, &mcount_call, 5); + new = ftrace_call_replace(ip, *addr); + *addr = ftrace_modify_code(ip, old, new); + + return 0; +} + +int __init ftrace_dyn_arch_init(void *data) { const unsigned char *const *noptable = find_nop_table(); + /* This is running in kstop_machine */ + + ftrace_mcount_set(data); + ftrace_nop = (unsigned long *)noptable[CALL_BACK]; return 0; diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index d509ad6c9cb8..b0dd0093058f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -56,9 +56,14 @@ struct dyn_ftrace { extern int ftrace_ip_converted(unsigned long ip); extern unsigned char *ftrace_nop_replace(void); extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr); -extern int ftrace_dyn_arch_init(void); +extern int ftrace_dyn_arch_init(void *data); +extern int ftrace_mcount_set(unsigned long *data); extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code, unsigned char *new_code); +extern int ftrace_update_ftrace_func(ftrace_func_t func); +extern void ftrace_caller(void); +extern void ftrace_call(void); +extern void mcount_call(void); #endif #ifdef CONFIG_FRAME_POINTER diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f6d9af3bf66b..88544f9bc0ed 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -26,14 +26,8 @@ #include "trace.h" -#ifdef CONFIG_DYNAMIC_FTRACE -# define FTRACE_ENABLED_INIT 1 -#else -# define FTRACE_ENABLED_INIT 0 -#endif - -int ftrace_enabled = FTRACE_ENABLED_INIT; -static int last_ftrace_enabled = FTRACE_ENABLED_INIT; +int ftrace_enabled; +static int last_ftrace_enabled; static DEFINE_SPINLOCK(ftrace_lock); static DEFINE_MUTEX(ftrace_sysctl_lock); @@ -149,6 +143,14 @@ static int notrace __unregister_ftrace_function(struct ftrace_ops *ops) #ifdef CONFIG_DYNAMIC_FTRACE +enum { + FTRACE_ENABLE_CALLS = (1 << 0), + FTRACE_DISABLE_CALLS = (1 << 1), + FTRACE_UPDATE_TRACE_FUNC = (1 << 2), + FTRACE_ENABLE_MCOUNT = (1 << 3), + FTRACE_DISABLE_MCOUNT = (1 << 4), +}; + static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu); @@ -199,12 +201,8 @@ ftrace_add_hash(struct dyn_ftrace *node, unsigned long key) hlist_add_head(&node->node, &ftrace_hash[key]); } -static notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip) +static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) { - /* If this was already converted, skip it */ - if (ftrace_ip_converted(ip)) - return NULL; - if (ftrace_pages->index == ENTRIES_PER_PAGE) { if (!ftrace_pages->next) return NULL; @@ -215,7 +213,7 @@ static notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip) } static void notrace -ftrace_record_ip(unsigned long ip, unsigned long parent_ip) +ftrace_record_ip(unsigned long ip) { struct dyn_ftrace *node; unsigned long flags; @@ -223,6 +221,9 @@ ftrace_record_ip(unsigned long ip, unsigned long parent_ip) int resched; int atomic; + if (!ftrace_enabled) + return; + resched = need_resched(); preempt_disable_notrace(); @@ -251,11 +252,12 @@ ftrace_record_ip(unsigned long ip, unsigned long parent_ip) /* * There's a slight race that the ftraced will update the - * hash and reset here. The arch alloc is responsible - * for seeing if the IP has already changed, and if - * it has, the alloc will fail. + * hash and reset here. If it is already converted, skip it. */ - node = ftrace_alloc_shutdown_node(ip); + if (ftrace_ip_converted(ip)) + goto out_unlock; + + node = ftrace_alloc_dyn_node(ip); if (!node) goto out_unlock; @@ -277,11 +279,7 @@ ftrace_record_ip(unsigned long ip, unsigned long parent_ip) preempt_enable_notrace(); } -static struct ftrace_ops ftrace_shutdown_ops __read_mostly = -{ - .func = ftrace_record_ip, -}; - +#define FTRACE_ADDR ((long)(&ftrace_caller)) #define MCOUNT_ADDR ((long)(&mcount)) static void notrace ftrace_replace_code(int saved) @@ -309,9 +307,9 @@ static void notrace ftrace_replace_code(int saved) ip = rec->ip; if (saved) - new = ftrace_call_replace(ip, MCOUNT_ADDR); + new = ftrace_call_replace(ip, FTRACE_ADDR); else - old = ftrace_call_replace(ip, MCOUNT_ADDR); + old = ftrace_call_replace(ip, FTRACE_ADDR); failed = ftrace_modify_code(ip, old, new); if (failed) @@ -320,16 +318,6 @@ static void notrace ftrace_replace_code(int saved) } } -static notrace void ftrace_startup_code(void) -{ - ftrace_replace_code(1); -} - -static notrace void ftrace_shutdown_code(void) -{ - ftrace_replace_code(0); -} - static notrace void ftrace_shutdown_replenish(void) { if (ftrace_pages->next) @@ -339,16 +327,8 @@ static notrace void ftrace_shutdown_replenish(void) ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); } -static int notrace __ftrace_modify_code(void *data) -{ - void (*func)(void) = data; - - func(); - return 0; -} - static notrace void -ftrace_code_disable(struct dyn_ftrace *rec, unsigned long addr) +ftrace_code_disable(struct dyn_ftrace *rec) { unsigned long ip; unsigned char *nop, *call; @@ -357,67 +337,113 @@ ftrace_code_disable(struct dyn_ftrace *rec, unsigned long addr) ip = rec->ip; nop = ftrace_nop_replace(); - call = ftrace_call_replace(ip, addr); + call = ftrace_call_replace(ip, MCOUNT_ADDR); failed = ftrace_modify_code(ip, call, nop); if (failed) rec->flags |= FTRACE_FL_FAILED; } -static void notrace ftrace_run_startup_code(void) +static int notrace __ftrace_modify_code(void *data) { - stop_machine_run(__ftrace_modify_code, ftrace_startup_code, NR_CPUS); + unsigned long addr; + int *command = data; + + if (*command & FTRACE_ENABLE_CALLS) + ftrace_replace_code(1); + else if (*command & FTRACE_DISABLE_CALLS) + ftrace_replace_code(0); + + if (*command & FTRACE_UPDATE_TRACE_FUNC) + ftrace_update_ftrace_func(ftrace_trace_function); + + if (*command & FTRACE_ENABLE_MCOUNT) { + addr = (unsigned long)ftrace_record_ip; + ftrace_mcount_set(&addr); + } else if (*command & FTRACE_DISABLE_MCOUNT) { + addr = (unsigned long)ftrace_stub; + ftrace_mcount_set(&addr); + } + + return 0; } -static void notrace ftrace_run_shutdown_code(void) +static void notrace ftrace_run_update_code(int command) { - stop_machine_run(__ftrace_modify_code, ftrace_shutdown_code, NR_CPUS); + stop_machine_run(__ftrace_modify_code, &command, NR_CPUS); } +static ftrace_func_t saved_ftrace_func; + static void notrace ftrace_startup(void) { + int command = 0; + mutex_lock(&ftraced_lock); ftraced_suspend++; - if (ftraced_suspend != 1) + if (ftraced_suspend == 1) + command |= FTRACE_ENABLE_CALLS; + + if (saved_ftrace_func != ftrace_trace_function) { + saved_ftrace_func = ftrace_trace_function; + command |= FTRACE_UPDATE_TRACE_FUNC; + } + + if (!command || !ftrace_enabled) goto out; - __unregister_ftrace_function(&ftrace_shutdown_ops); - if (ftrace_enabled) - ftrace_run_startup_code(); + ftrace_run_update_code(command); out: mutex_unlock(&ftraced_lock); } static void notrace ftrace_shutdown(void) { + int command = 0; + mutex_lock(&ftraced_lock); ftraced_suspend--; - if (ftraced_suspend) - goto out; + if (!ftraced_suspend) + command |= FTRACE_DISABLE_CALLS; - if (ftrace_enabled) - ftrace_run_shutdown_code(); + if (saved_ftrace_func != ftrace_trace_function) { + saved_ftrace_func = ftrace_trace_function; + command |= FTRACE_UPDATE_TRACE_FUNC; + } - __register_ftrace_function(&ftrace_shutdown_ops); + if (!command || !ftrace_enabled) + goto out; + + ftrace_run_update_code(command); out: mutex_unlock(&ftraced_lock); } static void notrace ftrace_startup_sysctl(void) { + int command = FTRACE_ENABLE_MCOUNT; + mutex_lock(&ftraced_lock); + /* Force update next time */ + saved_ftrace_func = NULL; /* ftraced_suspend is true if we want ftrace running */ if (ftraced_suspend) - ftrace_run_startup_code(); + command |= FTRACE_ENABLE_CALLS; + + ftrace_run_update_code(command); mutex_unlock(&ftraced_lock); } static void notrace ftrace_shutdown_sysctl(void) { + int command = FTRACE_DISABLE_MCOUNT; + mutex_lock(&ftraced_lock); /* ftraced_suspend is true if ftrace is running */ if (ftraced_suspend) - ftrace_run_shutdown_code(); + command |= FTRACE_DISABLE_CALLS; + + ftrace_run_update_code(command); mutex_unlock(&ftraced_lock); } @@ -430,11 +456,13 @@ static int notrace __ftrace_update_code(void *ignore) struct dyn_ftrace *p; struct hlist_head head; struct hlist_node *t; + int save_ftrace_enabled; cycle_t start, stop; int i; - /* Don't be calling ftrace ops now */ - __unregister_ftrace_function(&ftrace_shutdown_ops); + /* Don't be recording funcs now */ + save_ftrace_enabled = ftrace_enabled; + ftrace_enabled = 0; start = now(raw_smp_processor_id()); ftrace_update_cnt = 0; @@ -449,7 +477,7 @@ static int notrace __ftrace_update_code(void *ignore) /* all CPUS are stopped, we are safe to modify code */ hlist_for_each_entry(p, t, &head, node) { - ftrace_code_disable(p, MCOUNT_ADDR); + ftrace_code_disable(p); ftrace_update_cnt++; } @@ -459,7 +487,7 @@ static int notrace __ftrace_update_code(void *ignore) ftrace_update_time = stop - start; ftrace_update_tot_cnt += ftrace_update_cnt; - __register_ftrace_function(&ftrace_shutdown_ops); + ftrace_enabled = save_ftrace_enabled; return 0; } @@ -515,11 +543,6 @@ static int __init ftrace_dyn_table_alloc(void) struct ftrace_page *pg; int cnt; int i; - int ret; - - ret = ftrace_dyn_arch_init(); - if (ret) - return ret; /* allocate a few pages */ ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL); @@ -557,11 +580,19 @@ static int __init ftrace_dyn_table_alloc(void) return 0; } -static int __init notrace ftrace_shutdown_init(void) +static int __init notrace ftrace_dynamic_init(void) { struct task_struct *p; + unsigned long addr; int ret; + addr = (unsigned long)ftrace_record_ip; + stop_machine_run(ftrace_dyn_arch_init, &addr, NR_CPUS); + + /* ftrace_dyn_arch_init places the return code in addr */ + if (addr) + return addr; + ret = ftrace_dyn_table_alloc(); if (ret) return ret; @@ -570,12 +601,12 @@ static int __init notrace ftrace_shutdown_init(void) if (IS_ERR(p)) return -1; - __register_ftrace_function(&ftrace_shutdown_ops); + last_ftrace_enabled = ftrace_enabled = 1; return 0; } -core_initcall(ftrace_shutdown_init); +core_initcall(ftrace_dynamic_init); #else # define ftrace_startup() do { } while (0) # define ftrace_shutdown() do { } while (0) @@ -599,9 +630,8 @@ int register_ftrace_function(struct ftrace_ops *ops) int ret; mutex_lock(&ftrace_sysctl_lock); - ftrace_startup(); - ret = __register_ftrace_function(ops); + ftrace_startup(); mutex_unlock(&ftrace_sysctl_lock); return ret; @@ -619,10 +649,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops) mutex_lock(&ftrace_sysctl_lock); ret = __unregister_ftrace_function(ops); - - if (ftrace_list == &ftrace_list_end) - ftrace_shutdown(); - + ftrace_shutdown(); mutex_unlock(&ftrace_sysctl_lock); return ret; -- cgit v1.2.3 From 5072c59fd45e9976d02ee6f18c7336ef97623cbc Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:43 +0200 Subject: ftrace: add filter select functions to trace This patch adds two files to the debugfs system: /debugfs/tracing/available_filter_functions and /debugfs/tracing/set_ftrace_filter The available_filter_functions lists all functions that has been recorded by the ftraced that has called the ftrace_record_ip function. This is to allow users to see what functions have been converted to nops and can be enabled for tracing. To enable functions, simply echo the names (whitespace delimited) into set_ftrace_filter. Simple wildcards are also allowed. echo 'scheduler' > /debugfs/tracing/set_ftrace_filter Will have only the scheduler be activated when tracing is enabled. echo 'sched_*' > /debugfs/tracing/set_ftrace_filter Will have only the functions starting with 'sched_' be activated. echo '*lock' > /debugfs/tracing/set_ftrace_filter Will have only functions ending with 'lock' be activated. echo '*lock*' > /debugfs/tracing/set_ftrace_filter Will have only functions with 'lock' in its name be activated. Note: 'sched*lock' will not work. The only wildcards that are allowed is an asterisk and the beginning and or end of the string passed in. Multiple names can be passed in with whitespace delimited: echo 'scheduler *lock *acpi*' > /debugfs/tracing/set_ftrace_filter is also the same as: echo 'scheduler' > /debugfs/tracing/set_ftrace_filter echo '*lock' >> /debugfs/tracing/set_ftrace_filter echo '*acpi*' >> /debugfs/tracing/set_ftrace_filter Appending does just that. It appends to the list. To disable all filters simply echo an empty line in: echo > /debugfs/tracing/set_ftrace_filter Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 4 +- kernel/trace/ftrace.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 513 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index b0dd0093058f..f5911d2d42c3 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -43,7 +43,9 @@ extern void mcount(void); # define FTRACE_HASHSIZE (1< #include #include +#include +#include #include #include #include -#include +#include #include #include +#include #include #include "trace.h" @@ -151,12 +154,15 @@ enum { FTRACE_DISABLE_MCOUNT = (1 << 4), }; +static int ftrace_filtered; + static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu); static DEFINE_SPINLOCK(ftrace_shutdown_lock); static DEFINE_MUTEX(ftraced_lock); +static DEFINE_MUTEX(ftrace_filter_lock); struct ftrace_page { struct ftrace_page *next; @@ -282,16 +288,82 @@ ftrace_record_ip(unsigned long ip) #define FTRACE_ADDR ((long)(&ftrace_caller)) #define MCOUNT_ADDR ((long)(&mcount)) -static void notrace ftrace_replace_code(int saved) +static void notrace +__ftrace_replace_code(struct dyn_ftrace *rec, + unsigned char *old, unsigned char *new, int enable) +{ + unsigned long ip; + int failed; + + ip = rec->ip; + + if (ftrace_filtered && enable) { + unsigned long fl; + /* + * If filtering is on: + * + * If this record is set to be filtered and + * is enabled then do nothing. + * + * If this record is set to be filtered and + * it is not enabled, enable it. + * + * If this record is not set to be filtered + * and it is not enabled do nothing. + * + * If this record is not set to be filtered and + * it is enabled, disable it. + */ + fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED); + + if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || + (fl == 0)) + return; + + /* + * If it is enabled disable it, + * otherwise enable it! + */ + if (fl == FTRACE_FL_ENABLED) { + /* swap new and old */ + new = old; + old = ftrace_call_replace(ip, FTRACE_ADDR); + rec->flags &= ~FTRACE_FL_ENABLED; + } else { + new = ftrace_call_replace(ip, FTRACE_ADDR); + rec->flags |= FTRACE_FL_ENABLED; + } + } else { + + if (enable) + new = ftrace_call_replace(ip, FTRACE_ADDR); + else + old = ftrace_call_replace(ip, FTRACE_ADDR); + + if (enable) { + if (rec->flags & FTRACE_FL_ENABLED) + return; + rec->flags |= FTRACE_FL_ENABLED; + } else { + if (!(rec->flags & FTRACE_FL_ENABLED)) + return; + rec->flags &= ~FTRACE_FL_ENABLED; + } + } + + failed = ftrace_modify_code(ip, old, new); + if (failed) + rec->flags |= FTRACE_FL_FAILED; +} + +static void notrace ftrace_replace_code(int enable) { unsigned char *new = NULL, *old = NULL; struct dyn_ftrace *rec; struct ftrace_page *pg; - unsigned long ip; - int failed; int i; - if (saved) + if (enable) old = ftrace_nop_replace(); else new = ftrace_nop_replace(); @@ -304,16 +376,7 @@ static void notrace ftrace_replace_code(int saved) if (rec->flags & FTRACE_FL_FAILED) continue; - ip = rec->ip; - - if (saved) - new = ftrace_call_replace(ip, FTRACE_ADDR); - else - old = ftrace_call_replace(ip, FTRACE_ADDR); - - failed = ftrace_modify_code(ip, old, new); - if (failed) - rec->flags |= FTRACE_FL_FAILED; + __ftrace_replace_code(rec, old, new, enable); } } } @@ -580,6 +643,436 @@ static int __init ftrace_dyn_table_alloc(void) return 0; } +enum { + FTRACE_ITER_FILTER = (1 << 0), + FTRACE_ITER_CONT = (1 << 1), +}; + +#define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ + +struct ftrace_iterator { + loff_t pos; + struct ftrace_page *pg; + unsigned idx; + unsigned flags; + unsigned char buffer[FTRACE_BUFF_MAX+1]; + unsigned buffer_idx; + unsigned filtered; +}; + +static void notrace * +t_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct ftrace_iterator *iter = m->private; + struct dyn_ftrace *rec = NULL; + + (*pos)++; + + retry: + if (iter->idx >= iter->pg->index) { + if (iter->pg->next) { + iter->pg = iter->pg->next; + iter->idx = 0; + goto retry; + } + } else { + rec = &iter->pg->records[iter->idx++]; + if ((rec->flags & FTRACE_FL_FAILED) || + ((iter->flags & FTRACE_ITER_FILTER) && + !(rec->flags & FTRACE_FL_FILTER))) { + rec = NULL; + goto retry; + } + } + + iter->pos = *pos; + + return rec; +} + +static void *t_start(struct seq_file *m, loff_t *pos) +{ + struct ftrace_iterator *iter = m->private; + void *p = NULL; + loff_t l = -1; + + if (*pos != iter->pos) { + for (p = t_next(m, p, &l); p && l < *pos; p = t_next(m, p, &l)) + ; + } else { + l = *pos; + p = t_next(m, p, &l); + } + + return p; +} + +static void t_stop(struct seq_file *m, void *p) +{ +} + +static int t_show(struct seq_file *m, void *v) +{ + struct dyn_ftrace *rec = v; + char str[KSYM_SYMBOL_LEN]; + + if (!rec) + return 0; + + kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); + + seq_printf(m, "%s\n", str); + + return 0; +} + +static struct seq_operations show_ftrace_seq_ops = { + .start = t_start, + .next = t_next, + .stop = t_stop, + .show = t_show, +}; + +static int notrace +ftrace_avail_open(struct inode *inode, struct file *file) +{ + struct ftrace_iterator *iter; + int ret; + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + iter->pg = ftrace_pages_start; + iter->pos = -1; + + ret = seq_open(file, &show_ftrace_seq_ops); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = iter; + } else + kfree(iter); + + return ret; +} + +int ftrace_avail_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = (struct seq_file *)file->private_data; + struct ftrace_iterator *iter = m->private; + + seq_release(inode, file); + kfree(iter); + return 0; +} + +static void notrace ftrace_filter_reset(void) +{ + struct ftrace_page *pg; + struct dyn_ftrace *rec; + unsigned i; + + /* keep kstop machine from running */ + preempt_disable(); + ftrace_filtered = 0; + pg = ftrace_pages_start; + while (pg) { + for (i = 0; i < pg->index; i++) { + rec = &pg->records[i]; + if (rec->flags & FTRACE_FL_FAILED) + continue; + rec->flags &= ~FTRACE_FL_FILTER; + } + pg = pg->next; + } + preempt_enable(); +} + +static int notrace +ftrace_filter_open(struct inode *inode, struct file *file) +{ + struct ftrace_iterator *iter; + int ret = 0; + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + mutex_lock(&ftrace_filter_lock); + if ((file->f_mode & FMODE_WRITE) && + !(file->f_flags & O_APPEND)) + ftrace_filter_reset(); + + if (file->f_mode & FMODE_READ) { + iter->pg = ftrace_pages_start; + iter->pos = -1; + iter->flags = FTRACE_ITER_FILTER; + + ret = seq_open(file, &show_ftrace_seq_ops); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = iter; + } else + kfree(iter); + } else + file->private_data = iter; + mutex_unlock(&ftrace_filter_lock); + + return ret; +} + +static ssize_t notrace +ftrace_filter_read(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + if (file->f_mode & FMODE_READ) + return seq_read(file, ubuf, cnt, ppos); + else + return -EPERM; +} + +static loff_t notrace +ftrace_filter_lseek(struct file *file, loff_t offset, int origin) +{ + loff_t ret; + + if (file->f_mode & FMODE_READ) + ret = seq_lseek(file, offset, origin); + else + file->f_pos = ret = 1; + + return ret; +} + +enum { + MATCH_FULL, + MATCH_FRONT_ONLY, + MATCH_MIDDLE_ONLY, + MATCH_END_ONLY, +}; + +static void notrace +ftrace_match(unsigned char *buff, int len) +{ + char str[KSYM_SYMBOL_LEN]; + char *search = NULL; + struct ftrace_page *pg; + struct dyn_ftrace *rec; + int type = MATCH_FULL; + unsigned i, match = 0, search_len = 0; + + for (i = 0; i < len; i++) { + if (buff[i] == '*') { + if (!i) { + search = buff + i + 1; + type = MATCH_END_ONLY; + search_len = len - (i + 1); + } else { + if (type == MATCH_END_ONLY) { + type = MATCH_MIDDLE_ONLY; + } else { + match = i; + type = MATCH_FRONT_ONLY; + } + buff[i] = 0; + break; + } + } + } + + /* keep kstop machine from running */ + preempt_disable(); + ftrace_filtered = 1; + pg = ftrace_pages_start; + while (pg) { + for (i = 0; i < pg->index; i++) { + int matched = 0; + char *ptr; + + rec = &pg->records[i]; + if (rec->flags & FTRACE_FL_FAILED) + continue; + kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); + switch (type) { + case MATCH_FULL: + if (strcmp(str, buff) == 0) + matched = 1; + break; + case MATCH_FRONT_ONLY: + if (memcmp(str, buff, match) == 0) + matched = 1; + break; + case MATCH_MIDDLE_ONLY: + if (strstr(str, search)) + matched = 1; + break; + case MATCH_END_ONLY: + ptr = strstr(str, search); + if (ptr && (ptr[search_len] == 0)) + matched = 1; + break; + } + if (matched) + rec->flags |= FTRACE_FL_FILTER; + } + pg = pg->next; + } + preempt_enable(); +} + +static ssize_t notrace +ftrace_filter_write(struct file *file, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct ftrace_iterator *iter; + char ch; + size_t read = 0; + ssize_t ret; + + if (!cnt || cnt < 0) + return 0; + + mutex_lock(&ftrace_filter_lock); + + if (file->f_mode & FMODE_READ) { + struct seq_file *m = file->private_data; + iter = m->private; + } else + iter = file->private_data; + + if (!*ppos) { + iter->flags &= ~FTRACE_ITER_CONT; + iter->buffer_idx = 0; + } + + ret = get_user(ch, ubuf++); + if (ret) + goto out; + read++; + cnt--; + + if (!(iter->flags & ~FTRACE_ITER_CONT)) { + /* skip white space */ + while (cnt && isspace(ch)) { + ret = get_user(ch, ubuf++); + if (ret) + goto out; + read++; + cnt--; + } + + + if (isspace(ch)) { + file->f_pos += read; + ret = read; + goto out; + } + + iter->buffer_idx = 0; + } + + while (cnt && !isspace(ch)) { + if (iter->buffer_idx < FTRACE_BUFF_MAX) + iter->buffer[iter->buffer_idx++] = ch; + else { + ret = -EINVAL; + goto out; + } + ret = get_user(ch, ubuf++); + if (ret) + goto out; + read++; + cnt--; + } + + if (isspace(ch)) { + iter->filtered++; + iter->buffer[iter->buffer_idx] = 0; + ftrace_match(iter->buffer, iter->buffer_idx); + iter->buffer_idx = 0; + } else + iter->flags |= FTRACE_ITER_CONT; + + + file->f_pos += read; + + ret = read; + out: + mutex_unlock(&ftrace_filter_lock); + + return ret; +} + +static int notrace +ftrace_filter_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = (struct seq_file *)file->private_data; + struct ftrace_iterator *iter; + + mutex_lock(&ftrace_filter_lock); + if (file->f_mode & FMODE_READ) { + iter = m->private; + + seq_release(inode, file); + } else + iter = file->private_data; + + if (iter->buffer_idx) { + iter->filtered++; + iter->buffer[iter->buffer_idx] = 0; + ftrace_match(iter->buffer, iter->buffer_idx); + } + + mutex_lock(&ftrace_sysctl_lock); + mutex_lock(&ftraced_lock); + if (iter->filtered && ftraced_suspend && ftrace_enabled) + ftrace_run_update_code(FTRACE_ENABLE_CALLS); + mutex_unlock(&ftraced_lock); + mutex_unlock(&ftrace_sysctl_lock); + + kfree(iter); + mutex_unlock(&ftrace_filter_lock); + return 0; +} + +static struct file_operations ftrace_avail_fops = { + .open = ftrace_avail_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ftrace_avail_release, +}; + +static struct file_operations ftrace_filter_fops = { + .open = ftrace_filter_open, + .read = ftrace_filter_read, + .write = ftrace_filter_write, + .llseek = ftrace_filter_lseek, + .release = ftrace_filter_release, +}; + +static __init int ftrace_init_debugfs(void) +{ + struct dentry *d_tracer; + struct dentry *entry; + + d_tracer = tracing_init_dentry(); + + entry = debugfs_create_file("available_filter_functions", 0444, + d_tracer, NULL, &ftrace_avail_fops); + if (!entry) + pr_warning("Could not create debugfs " + "'available_filter_functions' entry\n"); + + entry = debugfs_create_file("set_ftrace_filter", 0644, d_tracer, + NULL, &ftrace_filter_fops); + if (!entry) + pr_warning("Could not create debugfs " + "'set_ftrace_filter' entry\n"); + return 0; +} + +fs_initcall(ftrace_init_debugfs); + static int __init notrace ftrace_dynamic_init(void) { struct task_struct *p; @@ -657,14 +1150,14 @@ int unregister_ftrace_function(struct ftrace_ops *ops) notrace int ftrace_enable_sysctl(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + struct file *file, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; mutex_lock(&ftrace_sysctl_lock); - ret = proc_dointvec(table, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(table, write, file, buffer, lenp, ppos); if (ret || !write || (last_ftrace_enabled == ftrace_enabled)) goto out; -- cgit v1.2.3 From f43fdad8627fec2d21df92799b254dceb66c9c3c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:43 +0200 Subject: ftrace: fix kexec disable the tracer while kexec pulls the rug from under the old kernel. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/machine_kexec_32.c | 4 ++++ arch/x86/kernel/machine_kexec_64.c | 4 ++++ include/linux/ftrace.h | 7 +++++++ 3 files changed, 15 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index d0b234c9fc31..88923fd7a6fc 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -11,6 +11,8 @@ #include #include #include +#include + #include #include #include @@ -107,6 +109,8 @@ NORET_TYPE void machine_kexec(struct kimage *image) unsigned long page_list[PAGES_NR]; void *control_page; + tracer_disable(); + /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index 576a03db4511..1558fdc174f9 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -11,6 +11,8 @@ #include #include #include +#include + #include #include #include @@ -184,6 +186,8 @@ NORET_TYPE void machine_kexec(struct kimage *image) unsigned long page_list[PAGES_NR]; void *control_page; + tracer_disable(); + /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index f5911d2d42c3..a42390c1d6e1 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -68,6 +68,13 @@ extern void ftrace_call(void); extern void mcount_call(void); #endif +static inline void tracer_disable(void) +{ +#ifdef CONFIG_FTRACE + ftrace_enabled = 0; +#endif +} + #ifdef CONFIG_FRAME_POINTER /* TODO: need to fix this for ARM */ # define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -- cgit v1.2.3 From e1c08bdd9fa73e44096e5a82c0d5928b04ab02c8 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:44 +0200 Subject: ftrace: force recording Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 4 ++++ kernel/trace/ftrace.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index a42390c1d6e1..2c1670c65236 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -54,6 +54,8 @@ struct dyn_ftrace { unsigned long flags; }; +int ftrace_force_update(void); + /* defined in arch */ extern int ftrace_ip_converted(unsigned long ip); extern unsigned char *ftrace_nop_replace(void); @@ -66,6 +68,8 @@ extern int ftrace_update_ftrace_func(ftrace_func_t func); extern void ftrace_caller(void); extern void ftrace_call(void); extern void mcount_call(void); +#else +# define ftrace_force_update() do { } while (0) #endif static inline void tracer_disable(void) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 97d5cb7b7e75..4facf5ceeb86 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -146,6 +146,10 @@ static int notrace __unregister_ftrace_function(struct ftrace_ops *ops) #ifdef CONFIG_DYNAMIC_FTRACE +static struct task_struct *ftraced_task; +static DECLARE_WAIT_QUEUE_HEAD(ftraced_waiters); +static unsigned long ftraced_iteration_counter; + enum { FTRACE_ENABLE_CALLS = (1 << 0), FTRACE_DISABLE_CALLS = (1 << 1), @@ -590,9 +594,12 @@ static int notrace ftraced(void *ignore) ftraced_trigger = 0; ftrace_record_suspend--; } + ftraced_iteration_counter++; mutex_unlock(&ftraced_lock); mutex_unlock(&ftrace_sysctl_lock); + wake_up_interruptible(&ftraced_waiters); + ftrace_shutdown_replenish(); set_current_state(TASK_INTERRUPTIBLE); @@ -1050,6 +1057,49 @@ static struct file_operations ftrace_filter_fops = { .release = ftrace_filter_release, }; +/** + * ftrace_force_update - force an update to all recording ftrace functions + * + * The ftrace dynamic update daemon only wakes up once a second. + * There may be cases where an update needs to be done immediately + * for tests or internal kernel tracing to begin. This function + * wakes the daemon to do an update and will not return until the + * update is complete. + */ +int ftrace_force_update(void) +{ + unsigned long last_counter; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + if (!ftraced_task) + return -ENODEV; + + mutex_lock(&ftraced_lock); + last_counter = ftraced_iteration_counter; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&ftraced_waiters, &wait); + + do { + mutex_unlock(&ftraced_lock); + wake_up_process(ftraced_task); + schedule(); + mutex_lock(&ftraced_lock); + if (signal_pending(current)) { + ret = -EINTR; + break; + } + set_current_state(TASK_INTERRUPTIBLE); + } while (last_counter == ftraced_iteration_counter); + + mutex_unlock(&ftraced_lock); + remove_wait_queue(&ftraced_waiters, &wait); + set_current_state(TASK_RUNNING); + + return ret; +} + static __init int ftrace_init_debugfs(void) { struct dentry *d_tracer; @@ -1095,6 +1145,7 @@ static int __init notrace ftrace_dynamic_init(void) return -1; last_ftrace_enabled = ftrace_enabled = 1; + ftraced_task = p; return 0; } -- cgit v1.2.3 From c7aafc549766b87819285d3480648fc652a47bc4 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:45 +0200 Subject: ftrace: cleanups factor out code and clean it up. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 2 +- kernel/trace/ftrace.c | 8 +-- kernel/trace/trace.c | 144 ++++++++++++++++++++++++-------------- kernel/trace/trace.h | 8 ++- kernel/trace/trace_irqsoff.c | 32 ++++----- kernel/trace/trace_sched_wakeup.c | 18 ++--- kernel/trace/trace_selftest.c | 25 ++++--- 7 files changed, 134 insertions(+), 103 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 2c1670c65236..953a36d6a199 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -69,7 +69,7 @@ extern void ftrace_caller(void); extern void ftrace_call(void); extern void mcount_call(void); #else -# define ftrace_force_update() do { } while (0) +# define ftrace_force_update() ({ 0; }) #endif static inline void tracer_disable(void) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 4facf5ceeb86..6d4d2e86debc 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1152,10 +1152,10 @@ static int __init notrace ftrace_dynamic_init(void) core_initcall(ftrace_dynamic_init); #else -# define ftrace_startup() do { } while (0) -# define ftrace_shutdown() do { } while (0) -# define ftrace_startup_sysctl() do { } while (0) -# define ftrace_shutdown_sysctl() do { } while (0) +# define ftrace_startup() do { } while (0) +# define ftrace_shutdown() do { } while (0) +# define ftrace_startup_sysctl() do { } while (0) +# define ftrace_shutdown_sysctl() do { } while (0) #endif /* CONFIG_DYNAMIC_FTRACE */ /** diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index f6d026f17dbb..61d2f0228866 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -142,12 +142,59 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) tracing_record_cmdline(current); } +void check_pages(struct trace_array_cpu *data) +{ + struct page *page, *tmp; + + BUG_ON(data->trace_pages.next->prev != &data->trace_pages); + BUG_ON(data->trace_pages.prev->next != &data->trace_pages); + + list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { + BUG_ON(page->lru.next->prev != &page->lru); + BUG_ON(page->lru.prev->next != &page->lru); + } +} + +void *head_page(struct trace_array_cpu *data) +{ + struct page *page; + + check_pages(data); + if (list_empty(&data->trace_pages)) + return NULL; + + page = list_entry(data->trace_pages.next, struct page, lru); + BUG_ON(&page->lru == &data->trace_pages); + + return page_address(page); +} + +notrace static void +flip_trace(struct trace_array_cpu *tr1, struct trace_array_cpu *tr2) +{ + struct list_head flip_pages; + + INIT_LIST_HEAD(&flip_pages); + + tr1->trace_current = NULL; + memcpy(&tr1->trace_current_idx, &tr2->trace_current_idx, + sizeof(struct trace_array_cpu) - + offsetof(struct trace_array_cpu, trace_current_idx)); + + check_pages(tr1); + check_pages(tr2); + list_splice_init(&tr1->trace_pages, &flip_pages); + list_splice_init(&tr2->trace_pages, &tr1->trace_pages); + list_splice_init(&flip_pages, &tr2->trace_pages); + BUG_ON(!list_empty(&flip_pages)); + check_pages(tr1); + check_pages(tr2); +} + notrace void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) { struct trace_array_cpu *data; - void *save_trace; - struct list_head save_pages; int i; WARN_ON_ONCE(!irqs_disabled()); @@ -155,11 +202,7 @@ update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) /* clear out all the previous traces */ for_each_possible_cpu(i) { data = tr->data[i]; - save_trace = max_tr.data[i]->trace; - save_pages = max_tr.data[i]->trace_pages; - memcpy(max_tr.data[i], data, sizeof(*data)); - data->trace = save_trace; - data->trace_pages = save_pages; + flip_trace(max_tr.data[i], data); tracing_reset(data); } @@ -177,8 +220,6 @@ notrace void update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) { struct trace_array_cpu *data = tr->data[cpu]; - void *save_trace; - struct list_head save_pages; int i; WARN_ON_ONCE(!irqs_disabled()); @@ -186,11 +227,8 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) for_each_possible_cpu(i) tracing_reset(max_tr.data[i]); - save_trace = max_tr.data[cpu]->trace; - save_pages = max_tr.data[cpu]->trace_pages; - memcpy(max_tr.data[cpu], data, sizeof(*data)); - data->trace = save_trace; - data->trace_pages = save_pages; + flip_trace(max_tr.data[cpu], data); + tracing_reset(data); __update_max_tr(tr, tsk, cpu); @@ -234,9 +272,9 @@ int register_tracer(struct tracer *type) * If we fail, we do not register this tracer. */ for_each_possible_cpu(i) { - if (!data->trace) - continue; data = tr->data[i]; + if (!head_page(data)) + continue; tracing_reset(data); } current_trace = type; @@ -298,7 +336,7 @@ void unregister_tracer(struct tracer *type) void notrace tracing_reset(struct trace_array_cpu *data) { data->trace_idx = 0; - data->trace_current = data->trace; + data->trace_current = head_page(data); data->trace_current_idx = 0; } @@ -425,26 +463,31 @@ notrace void tracing_record_cmdline(struct task_struct *tsk) } static inline notrace struct trace_entry * -tracing_get_trace_entry(struct trace_array *tr, - struct trace_array_cpu *data) +tracing_get_trace_entry(struct trace_array *tr, struct trace_array_cpu *data) { unsigned long idx, idx_next; struct trace_entry *entry; - struct page *page; struct list_head *next; + struct page *page; data->trace_idx++; idx = data->trace_current_idx; idx_next = idx + 1; + BUG_ON(idx * TRACE_ENTRY_SIZE >= PAGE_SIZE); + entry = data->trace_current + idx * TRACE_ENTRY_SIZE; if (unlikely(idx_next >= ENTRIES_PER_PAGE)) { page = virt_to_page(data->trace_current); - if (unlikely(&page->lru == data->trace_pages.prev)) - next = data->trace_pages.next; - else - next = page->lru.next; + /* + * Roundrobin - but skip the head (which is not a real page): + */ + next = page->lru.next; + if (unlikely(next == &data->trace_pages)) + next = next->next; + BUG_ON(next == &data->trace_pages); + page = list_entry(next, struct page, lru); data->trace_current = page_address(page); idx_next = 0; @@ -456,18 +499,17 @@ tracing_get_trace_entry(struct trace_array *tr, } static inline notrace void -tracing_generic_entry_update(struct trace_entry *entry, - unsigned long flags) +tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags) { struct task_struct *tsk = current; unsigned long pc; pc = preempt_count(); - entry->idx = atomic_inc_return(&tracer_counter); - entry->preempt_count = pc & 0xff; - entry->pid = tsk->pid; - entry->t = now(raw_smp_processor_id()); + entry->idx = atomic_inc_return(&tracer_counter); + entry->preempt_count = pc & 0xff; + entry->pid = tsk->pid; + entry->t = now(raw_smp_processor_id()); entry->flags = (irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) | ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) | ((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) | @@ -476,16 +518,15 @@ tracing_generic_entry_update(struct trace_entry *entry, notrace void ftrace(struct trace_array *tr, struct trace_array_cpu *data, - unsigned long ip, unsigned long parent_ip, - unsigned long flags) + unsigned long ip, unsigned long parent_ip, unsigned long flags) { struct trace_entry *entry; - entry = tracing_get_trace_entry(tr, data); + entry = tracing_get_trace_entry(tr, data); tracing_generic_entry_update(entry, flags); - entry->type = TRACE_FN; - entry->fn.ip = ip; - entry->fn.parent_ip = parent_ip; + entry->type = TRACE_FN; + entry->fn.ip = ip; + entry->fn.parent_ip = parent_ip; } notrace void @@ -496,7 +537,7 @@ tracing_sched_switch_trace(struct trace_array *tr, { struct trace_entry *entry; - entry = tracing_get_trace_entry(tr, data); + entry = tracing_get_trace_entry(tr, data); tracing_generic_entry_update(entry, flags); entry->type = TRACE_CTX; entry->ctx.prev_pid = prev->pid; @@ -540,6 +581,8 @@ trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data, } page = list_entry(iter->next_page[cpu], struct page, lru); + BUG_ON(&data->trace_pages == &page->lru); + array = page_address(page); return &array[iter->next_page_idx[cpu]]; @@ -554,7 +597,7 @@ find_next_entry(struct trace_iterator *iter, int *ent_cpu) int cpu; for_each_possible_cpu(cpu) { - if (!tr->data[cpu]->trace) + if (!head_page(tr->data[cpu])) continue; ent = trace_entry_idx(tr, tr->data[cpu], iter, cpu); if (ent && @@ -762,7 +805,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter) name = type->name; for_each_possible_cpu(cpu) { - if (tr->data[cpu]->trace) { + if (head_page(tr->data[cpu])) { total += tr->data[cpu]->trace_idx; if (tr->data[cpu]->trace_idx > tr->entries) entries += tr->entries; @@ -975,8 +1018,7 @@ static int trace_empty(struct trace_iterator *iter) for_each_possible_cpu(cpu) { data = iter->tr->data[cpu]; - if (data->trace && - data->trace_idx) + if (head_page(data) && data->trace_idx) return 0; } return 1; @@ -1576,9 +1618,9 @@ static struct tracer no_tracer __read_mostly = static int trace_alloc_page(void) { struct trace_array_cpu *data; - void *array; struct page *page, *tmp; LIST_HEAD(pages); + void *array; int i; /* first allocate a page for each CPU */ @@ -1610,14 +1652,14 @@ static int trace_alloc_page(void) for_each_possible_cpu(i) { data = global_trace.data[i]; page = list_entry(pages.next, struct page, lru); - list_del(&page->lru); + list_del_init(&page->lru); list_add_tail(&page->lru, &data->trace_pages); ClearPageLRU(page); #ifdef CONFIG_TRACER_MAX_TRACE data = max_tr.data[i]; page = list_entry(pages.next, struct page, lru); - list_del(&page->lru); + list_del_init(&page->lru); list_add_tail(&page->lru, &data->trace_pages); SetPageLRU(page); #endif @@ -1628,7 +1670,7 @@ static int trace_alloc_page(void) free_pages: list_for_each_entry_safe(page, tmp, &pages, lru) { - list_del(&page->lru); + list_del_init(&page->lru); __free_page(page); } return -ENOMEM; @@ -1654,7 +1696,6 @@ __init static int tracer_alloc_buffers(void) "for trace buffer!\n"); goto free_buffers; } - data->trace = array; /* set the array to the list */ INIT_LIST_HEAD(&data->trace_pages); @@ -1671,7 +1712,6 @@ __init static int tracer_alloc_buffers(void) "for trace buffer!\n"); goto free_buffers; } - max_tr.data[i]->trace = array; INIT_LIST_HEAD(&max_tr.data[i]->trace_pages); page = virt_to_page(array); @@ -1716,24 +1756,22 @@ __init static int tracer_alloc_buffers(void) struct page *page, *tmp; struct trace_array_cpu *data = global_trace.data[i]; - if (data && data->trace) { + if (data) { list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { - list_del(&page->lru); + list_del_init(&page->lru); __free_page(page); } - data->trace = NULL; } #ifdef CONFIG_TRACER_MAX_TRACE data = max_tr.data[i]; - if (data && data->trace) { + if (data) { list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { - list_del(&page->lru); + list_del_init(&page->lru); __free_page(page); } - data->trace = NULL; } #endif } diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 88edbf1f6788..cc1d34b8b771 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -53,12 +53,12 @@ struct trace_entry { * the trace, etc.) */ struct trace_array_cpu { - void *trace; void *trace_current; - unsigned trace_current_idx; struct list_head trace_pages; - unsigned long trace_idx; atomic_t disabled; + /* these fields get copied into max-trace: */ + unsigned trace_current_idx; + unsigned long trace_idx; unsigned long saved_latency; unsigned long critical_start; unsigned long critical_end; @@ -216,4 +216,6 @@ extern int trace_selftest_startup_sched_switch(struct tracer *trace, #endif #endif /* CONFIG_FTRACE_STARTUP_TEST */ +extern void *head_page(struct trace_array_cpu *data); + #endif /* _LINUX_KERNEL_TRACE_H */ diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 14183b8f79c5..2dfebb67fdfb 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -144,7 +144,7 @@ check_critical_timing(struct trace_array *tr, if (!report_latency(delta)) goto out; - spin_lock(&max_trace_lock); + spin_lock_irqsave(&max_trace_lock, flags); /* check if we are still the max latency */ if (!report_latency(delta)) @@ -165,32 +165,24 @@ check_critical_timing(struct trace_array *tr, update_max_tr_single(tr, current, cpu); - if (tracing_thresh) - printk(KERN_INFO "(%16s-%-5d|#%d): %lu us critical section " - "violates %lu us threshold.\n" - " => started at timestamp %lu: ", + if (tracing_thresh) { + printk(KERN_INFO "(%16s-%-5d|#%d):" + " %lu us critical section violates %lu us threshold.\n", current->comm, current->pid, raw_smp_processor_id(), - latency, nsecs_to_usecs(tracing_thresh), t0); - else + latency, nsecs_to_usecs(tracing_thresh)); + } else { printk(KERN_INFO "(%16s-%-5d|#%d):" - " new %lu us maximum-latency " - "critical section.\n => started at timestamp %lu: ", + " new %lu us maximum-latency critical section.\n", current->comm, current->pid, raw_smp_processor_id(), - latency, t0); - - print_symbol(KERN_CONT "<%s>\n", data->critical_start); - printk(KERN_CONT " => ended at timestamp %lu: ", t1); - print_symbol(KERN_CONT "<%s>\n", data->critical_end); - dump_stack(); - t1 = nsecs_to_usecs(now(cpu)); - printk(KERN_CONT " => dump-end timestamp %lu\n\n", t1); + latency); + } max_sequence++; out_unlock: - spin_unlock(&max_trace_lock); + spin_unlock_irqrestore(&max_trace_lock, flags); out: data->critical_sequence = max_sequence; @@ -216,7 +208,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip) cpu = raw_smp_processor_id(); data = tr->data[cpu]; - if (unlikely(!data) || unlikely(!data->trace) || + if (unlikely(!data) || unlikely(!head_page(data)) || atomic_read(&data->disabled)) return; @@ -256,7 +248,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) cpu = raw_smp_processor_id(); data = tr->data[cpu]; - if (unlikely(!data) || unlikely(!data->trace) || + if (unlikely(!data) || unlikely(!head_page(data)) || !data->critical_start || atomic_read(&data->disabled)) return; diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 3d10ff01f805..688df965f3f2 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -107,24 +107,18 @@ wakeup_sched_switch(struct task_struct *prev, struct task_struct *next) update_max_tr(tr, wakeup_task, wakeup_cpu); if (tracing_thresh) { - printk(KERN_INFO "(%16s-%-5d|#%d): %lu us wakeup latency " - "violates %lu us threshold.\n" - " => started at timestamp %lu: ", + printk(KERN_INFO "(%16s-%-5d|#%d):" + " %lu us wakeup latency violates %lu us threshold.\n", wakeup_task->comm, wakeup_task->pid, raw_smp_processor_id(), - latency, nsecs_to_usecs(tracing_thresh), t0); + latency, nsecs_to_usecs(tracing_thresh)); } else { - printk(KERN_INFO "(%16s-%-5d|#%d): new %lu us maximum " - "wakeup latency.\n => started at timestamp %lu: ", + printk(KERN_INFO "(%16s-%-5d|#%d):" + " new %lu us maximum wakeup latency.\n", wakeup_task->comm, wakeup_task->pid, - cpu, latency, t0); + cpu, latency); } - printk(KERN_CONT " ended at timestamp %lu: ", t1); - dump_stack(); - t1 = nsecs_to_usecs(now(cpu)); - printk(KERN_CONT " dump-end timestamp %lu\n\n", t1); - out_unlock: __wakeup_reset(tr); spin_unlock_irqrestore(&wakeup_lock, flags); diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index ef4d3cc009f5..c01874c3b1f9 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -1,6 +1,7 @@ /* Include in trace.c */ #include +#include static inline int trace_valid_entry(struct trace_entry *entry) { @@ -15,28 +16,29 @@ static inline int trace_valid_entry(struct trace_entry *entry) static int trace_test_buffer_cpu(struct trace_array *tr, struct trace_array_cpu *data) { - struct page *page; struct trace_entry *entries; + struct page *page; int idx = 0; int i; + BUG_ON(list_empty(&data->trace_pages)); page = list_entry(data->trace_pages.next, struct page, lru); entries = page_address(page); - if (data->trace != entries) + if (head_page(data) != entries) goto failed; /* * The starting trace buffer always has valid elements, - * if any element exits. + * if any element exists. */ - entries = data->trace; + entries = head_page(data); for (i = 0; i < tr->entries; i++) { - if (i < data->trace_idx && - !trace_valid_entry(&entries[idx])) { - printk(KERN_CONT ".. invalid entry %d ", entries[idx].type); + if (i < data->trace_idx && !trace_valid_entry(&entries[idx])) { + printk(KERN_CONT ".. invalid entry %d ", + entries[idx].type); goto failed; } @@ -80,11 +82,10 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count) int ret = 0; for_each_possible_cpu(cpu) { - if (!tr->data[cpu]->trace) + if (!head_page(tr->data[cpu])) continue; cnt += tr->data[cpu]->trace_idx; - printk("%d: count = %ld\n", cpu, cnt); ret = trace_test_buffer_cpu(tr, tr->data[cpu]); if (ret) @@ -117,6 +118,8 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) } /* start the tracing */ + ftrace_enabled = 1; + tr->ctrl = 1; trace->init(tr); /* Sleep for a 1/10 of a second */ @@ -124,6 +127,8 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) /* stop the tracing. */ tr->ctrl = 0; trace->ctrl_update(tr); + ftrace_enabled = 0; + /* check the trace buffer */ ret = trace_test_buffer(tr, &count); trace->reset(tr); @@ -328,7 +333,7 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr) /* create a high prio thread */ p = kthread_run(trace_wakeup_test_thread, &isrt, "ftrace-test"); - if (!IS_ERR(p)) { + if (IS_ERR(p)) { printk(KERN_CONT "Failed to create ftrace wakeup test thread "); return -1; } -- cgit v1.2.3 From 77a2b37d227483fe52aead242652aee406c25bf0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:45 +0200 Subject: ftrace: startup tester on dynamic tracing. This patch adds a startup self test on dynamic code modification and filters. The test filters on a specific function, makes sure that no other function is traced, exectutes the function, then makes sure that the function is traced. This patch also fixes a slight bug with the ftrace selftest, where tracer_enabled was not being set. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 2 + kernel/trace/ftrace.c | 19 +++++++ kernel/trace/trace_selftest.c | 113 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 130 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 953a36d6a199..a842d96c6343 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -55,6 +55,7 @@ struct dyn_ftrace { }; int ftrace_force_update(void); +void ftrace_set_filter(unsigned char *buf, int len, int reset); /* defined in arch */ extern int ftrace_ip_converted(unsigned long ip); @@ -70,6 +71,7 @@ extern void ftrace_call(void); extern void mcount_call(void); #else # define ftrace_force_update() ({ 0; }) +# define ftrace_set_filter(buf, len, reset) do { } while (0) #endif static inline void tracer_disable(void) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6d4d2e86debc..5e9389faaf75 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1010,6 +1010,25 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, return ret; } +/** + * ftrace_set_filter - set a function to filter on in ftrace + * @buf - the string that holds the function filter text. + * @len - the length of the string. + * @reset - non zero to reset all filters before applying this filter. + * + * Filters denote which functions should be enabled when tracing is enabled. + * If @buf is NULL and reset is set, all functions will be enabled for tracing. + */ +notrace void ftrace_set_filter(unsigned char *buf, int len, int reset) +{ + mutex_lock(&ftrace_filter_lock); + if (reset) + ftrace_filter_reset(); + if (buf) + ftrace_match(buf, len); + mutex_unlock(&ftrace_filter_lock); +} + static int notrace ftrace_filter_release(struct inode *inode, struct file *file) { diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index c01874c3b1f9..4c8a1b2d8231 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -99,6 +99,100 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count) } #ifdef CONFIG_FTRACE + +#ifdef CONFIG_DYNAMIC_FTRACE + +#define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func +#define __STR(x) #x +#define STR(x) __STR(x) +static int DYN_FTRACE_TEST_NAME(void) +{ + /* used to call mcount */ + return 0; +} + +/* Test dynamic code modification and ftrace filters */ +int trace_selftest_startup_dynamic_tracing(struct tracer *trace, + struct trace_array *tr, + int (*func)(void)) +{ + unsigned long count; + int ret; + int save_ftrace_enabled = ftrace_enabled; + int save_tracer_enabled = tracer_enabled; + + /* The ftrace test PASSED */ + printk(KERN_CONT "PASSED\n"); + pr_info("Testing dynamic ftrace: "); + + /* enable tracing, and record the filter function */ + ftrace_enabled = 1; + tracer_enabled = 1; + + /* passed in by parameter to fool gcc from optimizing */ + func(); + + /* update the records */ + ret = ftrace_force_update(); + if (ret) { + printk(KERN_CONT ".. ftraced failed .. "); + return ret; + } + + /* filter only on our function */ + ftrace_set_filter(STR(DYN_FTRACE_TEST_NAME), + sizeof(STR(DYN_FTRACE_TEST_NAME)), 1); + + /* enable tracing */ + tr->ctrl = 1; + trace->init(tr); + /* Sleep for a 1/10 of a second */ + msleep(100); + + /* we should have nothing in the buffer */ + ret = trace_test_buffer(tr, &count); + if (ret) + goto out; + + if (count) { + ret = -1; + printk(KERN_CONT ".. filter did not filter .. "); + goto out; + } + + /* call our function again */ + func(); + + /* sleep again */ + msleep(100); + + /* stop the tracing. */ + tr->ctrl = 0; + trace->ctrl_update(tr); + ftrace_enabled = 0; + + /* check the trace buffer */ + ret = trace_test_buffer(tr, &count); + trace->reset(tr); + + /* we should only have one item */ + if (!ret && count != 1) { + printk(KERN_CONT ".. filter failed .."); + ret = -1; + goto out; + } + out: + ftrace_enabled = save_ftrace_enabled; + tracer_enabled = save_tracer_enabled; + + /* Enable tracing on all functions again */ + ftrace_set_filter(NULL, 0, 1); + + return ret; +} +#else +# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; }) +#endif /* CONFIG_DYNAMIC_FTRACE */ /* * Simple verification test of ftrace function tracer. * Enable ftrace, sleep 1/10 second, and then read the trace @@ -109,8 +203,13 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) { unsigned long count; int ret; + int save_ftrace_enabled = ftrace_enabled; + int save_tracer_enabled = tracer_enabled; - /* make sure functions have been recorded */ + /* make sure msleep has been recorded */ + msleep(1); + + /* force the recorded functions to be traced */ ret = ftrace_force_update(); if (ret) { printk(KERN_CONT ".. ftraced failed .. "); @@ -119,6 +218,7 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) /* start the tracing */ ftrace_enabled = 1; + tracer_enabled = 1; tr->ctrl = 1; trace->init(tr); @@ -136,8 +236,16 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) if (!ret && !count) { printk(KERN_CONT ".. no entries found .."); ret = -1; + goto out; } + ret = trace_selftest_startup_dynamic_tracing(trace, tr, + DYN_FTRACE_TEST_NAME); + + out: + ftrace_enabled = save_ftrace_enabled; + tracer_enabled = save_tracer_enabled; + return ret; } #endif /* CONFIG_FTRACE */ @@ -415,6 +523,3 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr return ret; } #endif /* CONFIG_CONTEXT_SWITCH_TRACER */ - -#ifdef CONFIG_DYNAMIC_FTRACE -#endif /* CONFIG_DYNAMIC_FTRACE */ -- cgit v1.2.3 From 37ad508419f0fdfda7b378756eb1f35cfd26d96d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:48 +0200 Subject: ftrace - fix dynamic ftrace memory leak The ftrace dynamic function update allocates a record to store the instruction pointers that are being modified. If the modified instruction pointer fails to update, then the record is marked as failed and nothing more is done. Worse, if the modification fails, but the record ip function is still called, it will allocate a new record and try again. In just a matter of time, will this cause a serious memory leak and crash the system. This patch plugs this memory leak. When a record fails, it is included back into the pool of records to be used. Now a record may fail over and over again, but the number of allocated records will not increase. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 7 ++++--- kernel/trace/ftrace.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index a842d96c6343..61e757bd2350 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -43,9 +43,10 @@ extern void mcount(void); # define FTRACE_HASHSIZE (1<node, &ftrace_hash[key]); } +static notrace void ftrace_free_rec(struct dyn_ftrace *rec) +{ + /* no locking, only called from kstop_machine */ + + rec->ip = (unsigned long)ftrace_free_records; + ftrace_free_records = rec; + rec->flags |= FTRACE_FL_FREE; +} + static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) { + struct dyn_ftrace *rec; + + /* First check for freed records */ + if (ftrace_free_records) { + rec = ftrace_free_records; + + /* todo, disable tracing altogether on this warning */ + if (unlikely(!(rec->flags & FTRACE_FL_FREE))) { + WARN_ON_ONCE(1); + ftrace_free_records = NULL; + return NULL; + } + + ftrace_free_records = (void *)rec->ip; + memset(rec, 0, sizeof(*rec)); + return rec; + } + if (ftrace_pages->index == ENTRIES_PER_PAGE) { if (!ftrace_pages->next) return NULL; @@ -356,8 +385,16 @@ __ftrace_replace_code(struct dyn_ftrace *rec, } failed = ftrace_modify_code(ip, old, new); - if (failed) - rec->flags |= FTRACE_FL_FAILED; + if (failed) { + unsigned long key; + /* It is possible that the function hasn't been converted yet */ + key = hash_long(ip, FTRACE_HASHBITS); + if (!ftrace_ip_in_hash(ip, key)) { + rec->flags |= FTRACE_FL_FAILED; + ftrace_free_rec(rec); + } + + } } static void notrace ftrace_replace_code(int enable) @@ -407,8 +444,10 @@ ftrace_code_disable(struct dyn_ftrace *rec) call = ftrace_call_replace(ip, MCOUNT_ADDR); failed = ftrace_modify_code(ip, call, nop); - if (failed) + if (failed) { rec->flags |= FTRACE_FL_FAILED; + ftrace_free_rec(rec); + } } static int notrace __ftrace_modify_code(void *data) -- cgit v1.2.3 From 4eebcc81a33fbc45e28542b50197ed7b3c486d90 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:20:48 +0200 Subject: ftrace: disable tracing on failure Since ftrace touches practically every function. If we detect any anomaly, we want to fully disable ftrace. This patch adds code to try shutdown ftrace as much as possible without doing any more harm is something is detected not quite correct. This only kills ftrace, this patch does have checks for other parts of the tracer (irqsoff, wakeup, etc.). Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 3 ++ kernel/trace/ftrace.c | 112 ++++++++++++++++++++++++++++++++++++++---- kernel/trace/trace_selftest.c | 4 ++ 3 files changed, 110 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 61e757bd2350..4650a3160b7f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -58,6 +58,9 @@ struct dyn_ftrace { int ftrace_force_update(void); void ftrace_set_filter(unsigned char *buf, int len, int reset); +/* totally disable ftrace - can not re-enable after this */ +void ftrace_kill(void); + /* defined in arch */ extern int ftrace_ip_converted(unsigned long ip); extern unsigned char *ftrace_nop_replace(void); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 8e02aa690b2b..ff42345dd78e 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -29,9 +29,16 @@ #include "trace.h" -int ftrace_enabled; +/* ftrace_enabled is a method to turn ftrace on or off */ +int ftrace_enabled __read_mostly; static int last_ftrace_enabled; +/* + * ftrace_disabled is set when an anomaly is discovered. + * ftrace_disabled is much stronger than ftrace_enabled. + */ +static int ftrace_disabled __read_mostly; + static DEFINE_SPINLOCK(ftrace_lock); static DEFINE_MUTEX(ftrace_sysctl_lock); @@ -230,10 +237,11 @@ static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) if (ftrace_free_records) { rec = ftrace_free_records; - /* todo, disable tracing altogether on this warning */ if (unlikely(!(rec->flags & FTRACE_FL_FREE))) { WARN_ON_ONCE(1); ftrace_free_records = NULL; + ftrace_disabled = 1; + ftrace_enabled = 0; return NULL; } @@ -260,7 +268,7 @@ ftrace_record_ip(unsigned long ip) int resched; int atomic; - if (!ftrace_enabled) + if (!ftrace_enabled || ftrace_disabled) return; resched = need_resched(); @@ -485,6 +493,9 @@ static void notrace ftrace_startup(void) { int command = 0; + if (unlikely(ftrace_disabled)) + return; + mutex_lock(&ftraced_lock); ftraced_suspend++; if (ftraced_suspend == 1) @@ -507,6 +518,9 @@ static void notrace ftrace_shutdown(void) { int command = 0; + if (unlikely(ftrace_disabled)) + return; + mutex_lock(&ftraced_lock); ftraced_suspend--; if (!ftraced_suspend) @@ -529,6 +543,9 @@ static void notrace ftrace_startup_sysctl(void) { int command = FTRACE_ENABLE_MCOUNT; + if (unlikely(ftrace_disabled)) + return; + mutex_lock(&ftraced_lock); /* Force update next time */ saved_ftrace_func = NULL; @@ -544,6 +561,9 @@ static void notrace ftrace_shutdown_sysctl(void) { int command = FTRACE_DISABLE_MCOUNT; + if (unlikely(ftrace_disabled)) + return; + mutex_lock(&ftraced_lock); /* ftraced_suspend is true if ftrace is running */ if (ftraced_suspend) @@ -600,6 +620,9 @@ static int notrace __ftrace_update_code(void *ignore) static void notrace ftrace_update_code(void) { + if (unlikely(ftrace_disabled)) + return; + stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); } @@ -614,6 +637,9 @@ static int notrace ftraced(void *ignore) /* check once a second */ schedule_timeout(HZ); + if (unlikely(ftrace_disabled)) + continue; + mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftraced_lock); if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { @@ -628,6 +654,7 @@ static int notrace ftraced(void *ignore) ftrace_update_cnt != 1 ? "s" : "", ftrace_update_tot_cnt, usecs, usecs != 1 ? "s" : ""); + ftrace_disabled = 1; WARN_ON_ONCE(1); } ftraced_trigger = 0; @@ -785,6 +812,9 @@ ftrace_avail_open(struct inode *inode, struct file *file) struct ftrace_iterator *iter; int ret; + if (unlikely(ftrace_disabled)) + return -ENODEV; + iter = kzalloc(sizeof(*iter), GFP_KERNEL); if (!iter) return -ENOMEM; @@ -843,6 +873,9 @@ ftrace_filter_open(struct inode *inode, struct file *file) struct ftrace_iterator *iter; int ret = 0; + if (unlikely(ftrace_disabled)) + return -ENODEV; + iter = kzalloc(sizeof(*iter), GFP_KERNEL); if (!iter) return -ENOMEM; @@ -1063,6 +1096,9 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, */ notrace void ftrace_set_filter(unsigned char *buf, int len, int reset) { + if (unlikely(ftrace_disabled)) + return; + mutex_lock(&ftrace_filter_lock); if (reset) ftrace_filter_reset(); @@ -1133,7 +1169,7 @@ int ftrace_force_update(void) DECLARE_WAITQUEUE(wait, current); int ret = 0; - if (!ftraced_task) + if (unlikely(ftrace_disabled)) return -ENODEV; mutex_lock(&ftraced_lock); @@ -1142,6 +1178,11 @@ int ftrace_force_update(void) set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&ftraced_waiters, &wait); + if (unlikely(!ftraced_task)) { + ret = -ENODEV; + goto out; + } + do { mutex_unlock(&ftraced_lock); wake_up_process(ftraced_task); @@ -1154,6 +1195,7 @@ int ftrace_force_update(void) set_current_state(TASK_INTERRUPTIBLE); } while (last_counter == ftraced_iteration_counter); + out: mutex_unlock(&ftraced_lock); remove_wait_queue(&ftraced_waiters, &wait); set_current_state(TASK_RUNNING); @@ -1161,6 +1203,22 @@ int ftrace_force_update(void) return ret; } +static void ftrace_force_shutdown(void) +{ + struct task_struct *task; + int command = FTRACE_DISABLE_CALLS | FTRACE_UPDATE_TRACE_FUNC; + + mutex_lock(&ftraced_lock); + task = ftraced_task; + ftraced_task = NULL; + ftraced_suspend = -1; + ftrace_run_update_code(command); + mutex_unlock(&ftraced_lock); + + if (task) + kthread_stop(task); +} + static __init int ftrace_init_debugfs(void) { struct dentry *d_tracer; @@ -1194,21 +1252,29 @@ static int __init notrace ftrace_dynamic_init(void) stop_machine_run(ftrace_dyn_arch_init, &addr, NR_CPUS); /* ftrace_dyn_arch_init places the return code in addr */ - if (addr) - return addr; + if (addr) { + ret = (int)addr; + goto failed; + } ret = ftrace_dyn_table_alloc(); if (ret) - return ret; + goto failed; p = kthread_run(ftraced, NULL, "ftraced"); - if (IS_ERR(p)) - return -1; + if (IS_ERR(p)) { + ret = -1; + goto failed; + } last_ftrace_enabled = ftrace_enabled = 1; ftraced_task = p; return 0; + + failed: + ftrace_disabled = 1; + return ret; } core_initcall(ftrace_dynamic_init); @@ -1217,8 +1283,30 @@ core_initcall(ftrace_dynamic_init); # define ftrace_shutdown() do { } while (0) # define ftrace_startup_sysctl() do { } while (0) # define ftrace_shutdown_sysctl() do { } while (0) +# define ftrace_force_shutdown() do { } while (0) #endif /* CONFIG_DYNAMIC_FTRACE */ +/** + * ftrace_kill - totally shutdown ftrace + * + * This is a safety measure. If something was detected that seems + * wrong, calling this function will keep ftrace from doing + * any more modifications, and updates. + * used when something went wrong. + */ +void ftrace_kill(void) +{ + mutex_lock(&ftrace_sysctl_lock); + ftrace_disabled = 1; + ftrace_enabled = 0; + + clear_ftrace_function(); + mutex_unlock(&ftrace_sysctl_lock); + + /* Try to totally disable ftrace */ + ftrace_force_shutdown(); +} + /** * register_ftrace_function - register a function for profiling * @ops - ops structure that holds the function for profiling. @@ -1234,6 +1322,9 @@ int register_ftrace_function(struct ftrace_ops *ops) { int ret; + if (unlikely(ftrace_disabled)) + return -1; + mutex_lock(&ftrace_sysctl_lock); ret = __register_ftrace_function(ops); ftrace_startup(); @@ -1267,6 +1358,9 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, { int ret; + if (unlikely(ftrace_disabled)) + return -ENODEV; + mutex_lock(&ftrace_sysctl_lock); ret = proc_dointvec(table, write, file, buffer, lenp, ppos); diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index a6f1ed75f836..85715b86a342 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -248,6 +248,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) ftrace_enabled = save_ftrace_enabled; tracer_enabled = save_tracer_enabled; + /* kill ftrace totally if we failed */ + if (ret) + ftrace_kill(); + return ret; } #endif /* CONFIG_FTRACE */ -- cgit v1.2.3 From aeaee8a2c9cb4489f166ca0e39c568e8254faaa6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:49 +0200 Subject: ftrace: build fix Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 4650a3160b7f..08fbef1744cc 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -58,9 +58,6 @@ struct dyn_ftrace { int ftrace_force_update(void); void ftrace_set_filter(unsigned char *buf, int len, int reset); -/* totally disable ftrace - can not re-enable after this */ -void ftrace_kill(void); - /* defined in arch */ extern int ftrace_ip_converted(unsigned long ip); extern unsigned char *ftrace_nop_replace(void); @@ -74,10 +71,13 @@ extern void ftrace_caller(void); extern void ftrace_call(void); extern void mcount_call(void); #else -# define ftrace_force_update() ({ 0; }) -# define ftrace_set_filter(buf, len, reset) do { } while (0) +# define ftrace_force_update() ({ 0; }) +# define ftrace_set_filter(buf, len, reset) do { } while (0) #endif +/* totally disable ftrace - can not re-enable after this */ +void ftrace_kill(void); + static inline void tracer_disable(void) { #ifdef CONFIG_FTRACE -- cgit v1.2.3 From 86387f7ee5d3273ff4859e2c64ce656639b6ca65 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:51 +0200 Subject: ftrace: add stack tracing Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 2 + kernel/trace/Kconfig | 1 + kernel/trace/trace.c | 103 ++++++++++++++++++++++++++++++++++++++++--------- kernel/trace/trace.h | 11 ++++++ 4 files changed, 99 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 08fbef1744cc..0d3714e7110b 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -93,6 +93,7 @@ static inline void tracer_disable(void) # define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) # define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) # define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) +# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) #else # define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) # define CALLER_ADDR1 0UL @@ -100,6 +101,7 @@ static inline void tracer_disable(void) # define CALLER_ADDR3 0UL # define CALLER_ADDR4 0UL # define CALLER_ADDR5 0UL +# define CALLER_ADDR6 0UL #endif #ifdef CONFIG_IRQSOFF_TRACER diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 3f73a1710242..eb1988ed84b7 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -10,6 +10,7 @@ config TRACER_MAX_TRACE config TRACING bool select DEBUG_FS + select STACKTRACE config FTRACE bool "Kernel Function Tracer" diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 192c1354a7e0..b4b1b4fe99fd 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -28,6 +28,8 @@ #include #include +#include + #include "trace.h" unsigned long __read_mostly tracing_max_latency = (cycle_t)ULONG_MAX; @@ -88,6 +90,7 @@ enum trace_type { TRACE_FN, TRACE_CTX, TRACE_WAKE, + TRACE_STACK, TRACE_SPECIAL, __TRACE_LAST_TYPE @@ -109,6 +112,7 @@ enum trace_iterator_flags { TRACE_ITER_HEX = 0x20, TRACE_ITER_BIN = 0x40, TRACE_ITER_BLOCK = 0x80, + TRACE_ITER_STACKTRACE = 0x100, }; #define TRACE_ITER_SYM_MASK \ @@ -124,10 +128,11 @@ static const char *trace_options[] = { "hex", "bin", "block", + "stacktrace", NULL }; -static unsigned trace_flags; +static unsigned trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_STACKTRACE; static DEFINE_SPINLOCK(ftrace_max_lock); @@ -657,7 +662,7 @@ trace_function(struct trace_array *tr, struct trace_array_cpu *data, spin_unlock_irqrestore(&data->lock, irq_flags); if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up (&trace_wait); + wake_up(&trace_wait); } void @@ -685,13 +690,39 @@ trace_special(struct trace_array *tr, struct trace_array_cpu *data, spin_unlock_irqrestore(&data->lock, irq_flags); if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up (&trace_wait); + wake_up(&trace_wait); +} + +void __trace_stack(struct trace_array *tr, + struct trace_array_cpu *data, + unsigned long flags, + int skip) +{ + struct trace_entry *entry; + struct stack_trace trace; + + if (!(trace_flags & TRACE_ITER_STACKTRACE)) + return; + + entry = tracing_get_trace_entry(tr, data); + tracing_generic_entry_update(entry, flags); + entry->type = TRACE_STACK; + + memset(&entry->stack, 0, sizeof(entry->stack)); + + trace.nr_entries = 0; + trace.max_entries = FTRACE_STACK_ENTRIES; + trace.skip = skip; + trace.entries = entry->stack.caller; + + save_stack_trace(&trace); } void tracing_sched_switch_trace(struct trace_array *tr, struct trace_array_cpu *data, - struct task_struct *prev, struct task_struct *next, + struct task_struct *prev, + struct task_struct *next, unsigned long flags) { struct trace_entry *entry; @@ -706,16 +737,18 @@ tracing_sched_switch_trace(struct trace_array *tr, entry->ctx.prev_state = prev->state; entry->ctx.next_pid = next->pid; entry->ctx.next_prio = next->prio; + __trace_stack(tr, data, flags, 4); spin_unlock_irqrestore(&data->lock, irq_flags); if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up (&trace_wait); + wake_up(&trace_wait); } void tracing_sched_wakeup_trace(struct trace_array *tr, struct trace_array_cpu *data, - struct task_struct *wakee, struct task_struct *curr, + struct task_struct *wakee, + struct task_struct *curr, unsigned long flags) { struct trace_entry *entry; @@ -730,6 +763,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr, entry->ctx.prev_state = curr->state; entry->ctx.next_pid = wakee->pid; entry->ctx.next_prio = wakee->prio; + __trace_stack(tr, data, flags, 5); spin_unlock_irqrestore(&data->lock, irq_flags); if (!(trace_flags & TRACE_ITER_BLOCK)) @@ -1179,6 +1213,7 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) unsigned long rel_usecs; char *comm; int S; + int i; if (!next_entry) next_entry = entry; @@ -1197,8 +1232,10 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) abs_usecs % 1000, rel_usecs/1000, rel_usecs % 1000); } else { - lat_print_generic(s, entry, cpu); - lat_print_timestamp(s, abs_usecs, rel_usecs); + if (entry->type != TRACE_STACK) { + lat_print_generic(s, entry, cpu); + lat_print_timestamp(s, abs_usecs, rel_usecs); + } } switch (entry->type) { case TRACE_FN: @@ -1226,6 +1263,14 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) entry->special.arg2, entry->special.arg3); break; + case TRACE_STACK: + for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { + if (i) + trace_seq_puts(s, " <= "); + seq_print_ip_sym(s, entry->stack.caller[i], sym_flags); + } + trace_seq_puts(s, "\n"); + break; default: trace_seq_printf(s, "Unknown type %d\n", entry->type); } @@ -1241,8 +1286,9 @@ static int print_trace_fmt(struct trace_iterator *iter) unsigned long long t; unsigned long secs; char *comm; - int S; int ret; + int S; + int i; entry = iter->ent; @@ -1252,15 +1298,17 @@ static int print_trace_fmt(struct trace_iterator *iter) usec_rem = do_div(t, 1000000ULL); secs = (unsigned long)t; - ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid); - if (!ret) - return 0; - ret = trace_seq_printf(s, "[%02d] ", iter->cpu); - if (!ret) - return 0; - ret = trace_seq_printf(s, "%5lu.%06lu: ", secs, usec_rem); - if (!ret) - return 0; + if (entry->type != TRACE_STACK) { + ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid); + if (!ret) + return 0; + ret = trace_seq_printf(s, "[%02d] ", iter->cpu); + if (!ret) + return 0; + ret = trace_seq_printf(s, "%5lu.%06lu: ", secs, usec_rem); + if (!ret) + return 0; + } switch (entry->type) { case TRACE_FN: @@ -1303,6 +1351,22 @@ static int print_trace_fmt(struct trace_iterator *iter) if (!ret) return 0; break; + case TRACE_STACK: + for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { + if (i) { + ret = trace_seq_puts(s, " <= "); + if (!ret) + return 0; + } + ret = seq_print_ip_sym(s, entry->stack.caller[i], + sym_flags); + if (!ret) + return 0; + } + ret = trace_seq_puts(s, "\n"); + if (!ret) + return 0; + break; } return 1; } @@ -1344,6 +1408,7 @@ static int print_raw_fmt(struct trace_iterator *iter) return 0; break; case TRACE_SPECIAL: + case TRACE_STACK: ret = trace_seq_printf(s, " %lx %lx %lx\n", entry->special.arg1, entry->special.arg2, @@ -1399,6 +1464,7 @@ static int print_hex_fmt(struct trace_iterator *iter) SEQ_PUT_HEX_FIELD_RET(s, entry->fn.parent_ip); break; case TRACE_SPECIAL: + case TRACE_STACK: SEQ_PUT_HEX_FIELD_RET(s, entry->special.arg1); SEQ_PUT_HEX_FIELD_RET(s, entry->special.arg2); SEQ_PUT_HEX_FIELD_RET(s, entry->special.arg3); @@ -1433,6 +1499,7 @@ static int print_bin_fmt(struct trace_iterator *iter) SEQ_PUT_FIELD_RET(s, entry->ctx.next_prio); break; case TRACE_SPECIAL: + case TRACE_STACK: SEQ_PUT_FIELD_RET(s, entry->special.arg1); SEQ_PUT_FIELD_RET(s, entry->special.arg2); SEQ_PUT_FIELD_RET(s, entry->special.arg3); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 90e0ba0f6eba..387bdcf45e28 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -34,6 +34,16 @@ struct special_entry { unsigned long arg3; }; +/* + * Stack-trace entry: + */ + +#define FTRACE_STACK_ENTRIES 5 + +struct stack_entry { + unsigned long caller[FTRACE_STACK_ENTRIES]; +}; + /* * The trace entry - the most basic unit of tracing. This is what * is printed in the end as a single line in the trace output, such as: @@ -51,6 +61,7 @@ struct trace_entry { struct ftrace_entry fn; struct ctx_switch_entry ctx; struct special_entry special; + struct stack_entry stack; }; }; -- cgit v1.2.3 From 8ac0fca4ccb355ce50471d7aa3f10f5900b28b95 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:51 +0200 Subject: ftrace: sched tracer fix Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 6 ------ kernel/sched.c | 2 +- kernel/trace/trace_sched_wakeup.c | 13 +++---------- 3 files changed, 4 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 6e26f1fdbfe2..05744f9cb096 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2130,17 +2130,11 @@ ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next) #ifdef CONFIG_SCHED_TRACER extern void ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr); -extern void -ftrace_wake_up_new_task(struct task_struct *wakee, struct task_struct *curr); #else static inline void ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr) { } -static inline void -ftrace_wake_up_new_task(struct task_struct *wakee, struct task_struct *curr) -{ -} #endif extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask); diff --git a/kernel/sched.c b/kernel/sched.c index 328494e28df2..53ab1174664f 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2613,7 +2613,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) p->sched_class->task_new(rq, p); inc_nr_running(rq); } - ftrace_wake_up_new_task(p, rq->curr); + ftrace_wake_up_task(p, rq->curr); check_preempt_curr(rq, p); #ifdef CONFIG_SMP if (p->sched_class->task_wake_up) diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 87fa7b253b57..2a012423f9d0 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -201,20 +201,13 @@ out: atomic_dec(&tr->data[cpu]->disabled); } -void -wakeup_sched_wakeup(struct task_struct *wakee, struct task_struct *curr) +void wakeup_sched_wakeup(struct task_struct *wakee, struct task_struct *curr) { if (likely(!tracer_enabled)) return; - wakeup_check_start(wakeup_trace, wakee, curr); -} - -void -ftrace_wake_up_new_task(struct task_struct *wakee, struct task_struct *curr) -{ - if (likely(!tracer_enabled)) - return; + tracing_record_cmdline(curr); + tracing_record_cmdline(wakee); wakeup_check_start(wakeup_trace, wakee, curr); } -- cgit v1.2.3 From 4e65551905fb0300ae7e667cbaa41ee2e3f29a13 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:52 +0200 Subject: ftrace: sched tracer, trace full rbtree Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 32 ++++++++++++++++------- kernel/sched.c | 35 ++++++++++++++++++++++--- kernel/trace/trace.c | 55 ++++++++++++++++----------------------- kernel/trace/trace.h | 14 ++++++++++ kernel/trace/trace_sched_switch.c | 24 +++++++++++------ 5 files changed, 108 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 05744f9cb096..652d380ae563 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2119,20 +2119,34 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) #ifdef CONFIG_CONTEXT_SWITCH_TRACER extern void -ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next); +ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next); +extern void +ftrace_wake_up_task(void *rq, struct task_struct *wakee, + struct task_struct *curr); +extern void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data); +extern void +__trace_special(void *__tr, void *__data, + unsigned long arg1, unsigned long arg2, unsigned long arg3); #else static inline void -ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next) +ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next) +{ +} +static inline void +sched_trace_special(unsigned long p1, unsigned long p2, unsigned long p3) +{ +} +static inline void +ftrace_wake_up_task(void *rq, struct task_struct *wakee, + struct task_struct *curr) +{ +} +static inline void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data) { } -#endif - -#ifdef CONFIG_SCHED_TRACER -extern void -ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr); -#else static inline void -ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr) +__trace_special(void *__tr, void *__data, + unsigned long arg1, unsigned long arg2, unsigned long arg3) { } #endif diff --git a/kernel/sched.c b/kernel/sched.c index 53ab1174664f..b9208a0e33a0 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2394,6 +2394,35 @@ static int sched_balance_self(int cpu, int flag) #endif /* CONFIG_SMP */ +#ifdef CONFIG_CONTEXT_SWITCH_TRACER + +void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data) +{ + struct sched_entity *se; + struct task_struct *p; + struct rb_node *curr; + struct rq *rq = __rq; + + curr = first_fair(&rq->cfs); + if (!curr) + return; + + while (curr) { + se = rb_entry(curr, struct sched_entity, run_node); + if (!entity_is_task(se)) + continue; + + p = task_of(se); + + __trace_special(__tr, __data, + p->pid, p->se.vruntime, p->se.sum_exec_runtime); + + curr = rb_next(curr); + } +} + +#endif + /*** * try_to_wake_up - wake up a thread * @p: the to-be-woken-up thread @@ -2468,7 +2497,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync) out_activate: #endif /* CONFIG_SMP */ - ftrace_wake_up_task(p, rq->curr); + ftrace_wake_up_task(rq, p, rq->curr); schedstat_inc(p, se.nr_wakeups); if (sync) schedstat_inc(p, se.nr_wakeups_sync); @@ -2613,7 +2642,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) p->sched_class->task_new(rq, p); inc_nr_running(rq); } - ftrace_wake_up_task(p, rq->curr); + ftrace_wake_up_task(rq, p, rq->curr); check_preempt_curr(rq, p); #ifdef CONFIG_SMP if (p->sched_class->task_wake_up) @@ -2786,7 +2815,7 @@ context_switch(struct rq *rq, struct task_struct *prev, struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); - ftrace_ctx_switch(prev, next); + ftrace_ctx_switch(rq, prev, next); mm = next->mm; oldmm = prev->active_mm; /* diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0e4b7119e263..65173b14b914 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -66,7 +66,18 @@ static struct tracer *current_trace __read_mostly; static int max_tracer_type_len; static DEFINE_MUTEX(trace_types_lock); -static DECLARE_WAIT_QUEUE_HEAD (trace_wait); +static DECLARE_WAIT_QUEUE_HEAD(trace_wait); + +unsigned long trace_flags = TRACE_ITER_PRINT_PARENT; + +/* + * FIXME: where should this be called? + */ +void trace_wake_up(void) +{ + if (!(trace_flags & TRACE_ITER_BLOCK)) + wake_up(&trace_wait); +} #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(struct trace_entry)) @@ -103,18 +114,6 @@ enum trace_flag_type { TRACE_FLAG_SOFTIRQ = 0x08, }; -enum trace_iterator_flags { - TRACE_ITER_PRINT_PARENT = 0x01, - TRACE_ITER_SYM_OFFSET = 0x02, - TRACE_ITER_SYM_ADDR = 0x04, - TRACE_ITER_VERBOSE = 0x08, - TRACE_ITER_RAW = 0x10, - TRACE_ITER_HEX = 0x20, - TRACE_ITER_BIN = 0x40, - TRACE_ITER_BLOCK = 0x80, - TRACE_ITER_STACKTRACE = 0x100, -}; - #define TRACE_ITER_SYM_MASK \ (TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR) @@ -132,8 +131,6 @@ static const char *trace_options[] = { NULL }; -static unsigned trace_flags = TRACE_ITER_PRINT_PARENT; - static DEFINE_SPINLOCK(ftrace_max_lock); /* @@ -660,9 +657,6 @@ trace_function(struct trace_array *tr, struct trace_array_cpu *data, entry->fn.ip = ip; entry->fn.parent_ip = parent_ip; spin_unlock_irqrestore(&data->lock, irq_flags); - - if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up(&trace_wait); } void @@ -673,10 +667,14 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, trace_function(tr, data, ip, parent_ip, flags); } +#ifdef CONFIG_CONTEXT_SWITCH_TRACER + void -trace_special(struct trace_array *tr, struct trace_array_cpu *data, - unsigned long arg1, unsigned long arg2, unsigned long arg3) +__trace_special(void *__tr, void *__data, + unsigned long arg1, unsigned long arg2, unsigned long arg3) { + struct trace_array_cpu *data = __data; + struct trace_array *tr = __tr; struct trace_entry *entry; unsigned long irq_flags; @@ -688,11 +686,10 @@ trace_special(struct trace_array *tr, struct trace_array_cpu *data, entry->special.arg2 = arg2; entry->special.arg3 = arg3; spin_unlock_irqrestore(&data->lock, irq_flags); - - if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up(&trace_wait); } +#endif + void __trace_stack(struct trace_array *tr, struct trace_array_cpu *data, unsigned long flags, @@ -739,9 +736,6 @@ tracing_sched_switch_trace(struct trace_array *tr, entry->ctx.next_prio = next->prio; __trace_stack(tr, data, flags, 4); spin_unlock_irqrestore(&data->lock, irq_flags); - - if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up(&trace_wait); } void @@ -765,9 +759,6 @@ tracing_sched_wakeup_trace(struct trace_array *tr, entry->ctx.next_prio = wakee->prio; __trace_stack(tr, data, flags, 5); spin_unlock_irqrestore(&data->lock, irq_flags); - - if (!(trace_flags & TRACE_ITER_BLOCK)) - wake_up(&trace_wait); } #ifdef CONFIG_FTRACE @@ -1258,7 +1249,7 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) comm); break; case TRACE_SPECIAL: - trace_seq_printf(s, " %lx %lx %lx\n", + trace_seq_printf(s, " %ld %ld %ld\n", entry->special.arg1, entry->special.arg2, entry->special.arg3); @@ -1344,7 +1335,7 @@ static int print_trace_fmt(struct trace_iterator *iter) return 0; break; case TRACE_SPECIAL: - ret = trace_seq_printf(s, " %lx %lx %lx\n", + ret = trace_seq_printf(s, " %ld %ld %ld\n", entry->special.arg1, entry->special.arg2, entry->special.arg3); @@ -1409,7 +1400,7 @@ static int print_raw_fmt(struct trace_iterator *iter) break; case TRACE_SPECIAL: case TRACE_STACK: - ret = trace_seq_printf(s, " %lx %lx %lx\n", + ret = trace_seq_printf(s, " %ld %ld %ld\n", entry->special.arg1, entry->special.arg2, entry->special.arg3); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 387bdcf45e28..75e237475674 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -274,4 +274,18 @@ extern int trace_selftest_startup_sched_switch(struct tracer *trace, extern void *head_page(struct trace_array_cpu *data); +extern unsigned long trace_flags; + +enum trace_iterator_flags { + TRACE_ITER_PRINT_PARENT = 0x01, + TRACE_ITER_SYM_OFFSET = 0x02, + TRACE_ITER_SYM_ADDR = 0x04, + TRACE_ITER_VERBOSE = 0x08, + TRACE_ITER_RAW = 0x10, + TRACE_ITER_HEX = 0x20, + TRACE_ITER_BIN = 0x40, + TRACE_ITER_BLOCK = 0x80, + TRACE_ITER_STACKTRACE = 0x100, +}; + #endif /* _LINUX_KERNEL_TRACE_H */ diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c index 8b1cf1a3aee0..12658b3f2b28 100644 --- a/kernel/trace/trace_sched_switch.c +++ b/kernel/trace/trace_sched_switch.c @@ -18,7 +18,7 @@ static struct trace_array *ctx_trace; static int __read_mostly tracer_enabled; static void -ctx_switch_func(struct task_struct *prev, struct task_struct *next) +ctx_switch_func(void *__rq, struct task_struct *prev, struct task_struct *next) { struct trace_array *tr = ctx_trace; struct trace_array_cpu *data; @@ -34,14 +34,17 @@ ctx_switch_func(struct task_struct *prev, struct task_struct *next) data = tr->data[cpu]; disabled = atomic_inc_return(&data->disabled); - if (likely(disabled == 1)) + if (likely(disabled == 1)) { tracing_sched_switch_trace(tr, data, prev, next, flags); + ftrace_all_fair_tasks(__rq, tr, data); + } atomic_dec(&data->disabled); local_irq_restore(flags); } -static void wakeup_func(struct task_struct *wakee, struct task_struct *curr) +static void +wakeup_func(void *__rq, struct task_struct *wakee, struct task_struct *curr) { struct trace_array *tr = ctx_trace; struct trace_array_cpu *data; @@ -57,14 +60,18 @@ static void wakeup_func(struct task_struct *wakee, struct task_struct *curr) data = tr->data[cpu]; disabled = atomic_inc_return(&data->disabled); - if (likely(disabled == 1)) + if (likely(disabled == 1)) { tracing_sched_wakeup_trace(tr, data, wakee, curr, flags); + ftrace_all_fair_tasks(__rq, tr, data); + } atomic_dec(&data->disabled); local_irq_restore(flags); } -void ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next) +void +ftrace_ctx_switch(void *__rq, struct task_struct *prev, + struct task_struct *next) { tracing_record_cmdline(prev); @@ -72,7 +79,7 @@ void ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next) * If tracer_switch_func only points to the local * switch func, it still needs the ptr passed to it. */ - ctx_switch_func(prev, next); + ctx_switch_func(__rq, prev, next); /* * Chain to the wakeup tracer (this is a NOP if disabled): @@ -81,11 +88,12 @@ void ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next) } void -ftrace_wake_up_task(struct task_struct *wakee, struct task_struct *curr) +ftrace_wake_up_task(void *__rq, struct task_struct *wakee, + struct task_struct *curr) { tracing_record_cmdline(curr); - wakeup_func(wakee, curr); + wakeup_func(__rq, wakee, curr); /* * Chain to the wakeup tracer (this is a NOP if disabled): -- cgit v1.2.3 From 017730c11241e26577673eb9d957cfc66172ea91 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:52 +0200 Subject: ftrace: fix wakeups Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 2 ++ kernel/sched.c | 18 ++++++++++++++++++ kernel/trace/trace.c | 15 +++++++++++---- 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 652d380ae563..a3970b563757 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -246,6 +246,8 @@ extern asmlinkage void schedule_tail(struct task_struct *prev); extern void init_idle(struct task_struct *idle, int cpu); extern void init_idle_bootup_task(struct task_struct *idle); +extern int runqueue_is_locked(void); + extern cpumask_t nohz_cpu_mask; #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ) extern int select_nohz_load_balancer(int cpu); diff --git a/kernel/sched.c b/kernel/sched.c index 673b588b713b..9ca4a2e6a236 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -642,6 +642,24 @@ static inline void update_rq_clock(struct rq *rq) # define const_debug static const #endif +/** + * runqueue_is_locked + * + * Returns true if the current cpu runqueue is locked. + * This interface allows printk to be called with the runqueue lock + * held and know whether or not it is OK to wake up the klogd. + */ +int runqueue_is_locked(void) +{ + int cpu = get_cpu(); + struct rq *rq = cpu_rq(cpu); + int ret; + + ret = spin_is_locked(&rq->lock); + put_cpu(); + return ret; +} + /* * Debugging: various feature bits */ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 65173b14b914..2ca9d66aa74e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -70,12 +70,13 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait); unsigned long trace_flags = TRACE_ITER_PRINT_PARENT; -/* - * FIXME: where should this be called? - */ void trace_wake_up(void) { - if (!(trace_flags & TRACE_ITER_BLOCK)) + /* + * The runqueue_is_locked() can fail, but this is the best we + * have for now: + */ + if (!(trace_flags & TRACE_ITER_BLOCK) && !runqueue_is_locked()) wake_up(&trace_wait); } @@ -657,6 +658,8 @@ trace_function(struct trace_array *tr, struct trace_array_cpu *data, entry->fn.ip = ip; entry->fn.parent_ip = parent_ip; spin_unlock_irqrestore(&data->lock, irq_flags); + + trace_wake_up(); } void @@ -686,6 +689,8 @@ __trace_special(void *__tr, void *__data, entry->special.arg2 = arg2; entry->special.arg3 = arg3; spin_unlock_irqrestore(&data->lock, irq_flags); + + trace_wake_up(); } #endif @@ -759,6 +764,8 @@ tracing_sched_wakeup_trace(struct trace_array *tr, entry->ctx.next_prio = wakee->prio; __trace_stack(tr, data, flags, 5); spin_unlock_irqrestore(&data->lock, irq_flags); + + trace_wake_up(); } #ifdef CONFIG_FTRACE -- cgit v1.2.3 From 1a3c3034336320554a3342572dae98d69e054fc7 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:52 +0200 Subject: ftrace: fix __trace_special() Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 20 ++++++++++++-------- kernel/trace/trace.c | 4 ---- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index a3970b563757..5b186bed54bc 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2119,6 +2119,18 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) } #endif +#ifdef CONFIG_TRACING +extern void +__trace_special(void *__tr, void *__data, + unsigned long arg1, unsigned long arg2, unsigned long arg3); +#else +static inline void +__trace_special(void *__tr, void *__data, + unsigned long arg1, unsigned long arg2, unsigned long arg3) +{ +} +#endif + #ifdef CONFIG_CONTEXT_SWITCH_TRACER extern void ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next); @@ -2126,9 +2138,6 @@ extern void ftrace_wake_up_task(void *rq, struct task_struct *wakee, struct task_struct *curr); extern void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data); -extern void -__trace_special(void *__tr, void *__data, - unsigned long arg1, unsigned long arg2, unsigned long arg3); #else static inline void ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next) @@ -2146,11 +2155,6 @@ ftrace_wake_up_task(void *rq, struct task_struct *wakee, static inline void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data) { } -static inline void -__trace_special(void *__tr, void *__data, - unsigned long arg1, unsigned long arg2, unsigned long arg3) -{ -} #endif extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 2ca9d66aa74e..65d2c0a61ed4 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -670,8 +670,6 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, trace_function(tr, data, ip, parent_ip, flags); } -#ifdef CONFIG_CONTEXT_SWITCH_TRACER - void __trace_special(void *__tr, void *__data, unsigned long arg1, unsigned long arg2, unsigned long arg3) @@ -693,8 +691,6 @@ __trace_special(void *__tr, void *__data, trace_wake_up(); } -#endif - void __trace_stack(struct trace_array *tr, struct trace_array_cpu *data, unsigned long flags, -- cgit v1.2.3 From 88a4216c3ec4281fc7e6725cc3a3ccd01fb1aa14 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:20:53 +0200 Subject: ftrace: sched special Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 6 ++++++ kernel/sched_fair.c | 3 +++ kernel/trace/trace.c | 6 +++--- kernel/trace/trace_sched_switch.c | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5b186bed54bc..360ca99033d2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2138,6 +2138,8 @@ extern void ftrace_wake_up_task(void *rq, struct task_struct *wakee, struct task_struct *curr); extern void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data); +extern void +ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); #else static inline void ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next) @@ -2155,6 +2157,10 @@ ftrace_wake_up_task(void *rq, struct task_struct *wakee, static inline void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data) { } +static inline void +ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) +{ +} #endif extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask); diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index e24ecd39c4b8..dc1856f10795 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1061,6 +1061,8 @@ wake_affine(struct rq *rq, struct sched_domain *this_sd, struct rq *this_rq, if (!(this_sd->flags & SD_WAKE_AFFINE)) return 0; + ftrace_special(__LINE__, curr->se.avg_overlap, sync); + ftrace_special(__LINE__, p->se.avg_overlap, -1); /* * If the currently running task will sleep within * a reasonable amount of time then attract this newly @@ -1238,6 +1240,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p) if (unlikely(se == pse)) return; + ftrace_special(__LINE__, p->pid, se->last_wakeup); cfs_rq_of(pse)->next = pse; /* diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3a4032492fcb..b87a26414892 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1251,7 +1251,7 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) comm); break; case TRACE_SPECIAL: - trace_seq_printf(s, " %ld %ld %ld\n", + trace_seq_printf(s, "# %ld %ld %ld\n", entry->special.arg1, entry->special.arg2, entry->special.arg3); @@ -1335,7 +1335,7 @@ static int print_trace_fmt(struct trace_iterator *iter) return 0; break; case TRACE_SPECIAL: - ret = trace_seq_printf(s, " %ld %ld %ld\n", + ret = trace_seq_printf(s, "# %ld %ld %ld\n", entry->special.arg1, entry->special.arg2, entry->special.arg3); @@ -1400,7 +1400,7 @@ static int print_raw_fmt(struct trace_iterator *iter) break; case TRACE_SPECIAL: case TRACE_STACK: - ret = trace_seq_printf(s, " %ld %ld %ld\n", + ret = trace_seq_printf(s, "# %ld %ld %ld\n", entry->special.arg1, entry->special.arg2, entry->special.arg3); diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c index 5a217e863586..bddf676914ed 100644 --- a/kernel/trace/trace_sched_switch.c +++ b/kernel/trace/trace_sched_switch.c @@ -103,6 +103,30 @@ ftrace_wake_up_task(void *__rq, struct task_struct *wakee, wakeup_sched_wakeup(wakee, curr); } +void +ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) +{ + struct trace_array *tr = ctx_trace; + struct trace_array_cpu *data; + unsigned long flags; + long disabled; + int cpu; + + if (!tracer_enabled) + return; + + local_irq_save(flags); + cpu = raw_smp_processor_id(); + data = tr->data[cpu]; + disabled = atomic_inc_return(&data->disabled); + + if (likely(disabled == 1)) + __trace_special(tr, data, arg1, arg2, arg3); + + atomic_dec(&data->disabled); + local_irq_restore(flags); +} + static void sched_switch_reset(struct trace_array *tr) { int cpu; -- cgit v1.2.3 From 3eefae994d9224fb7771a3ddb683868363c23510 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 12 May 2008 21:21:04 +0200 Subject: ftrace: limit trace entries Currently there is no protection from the root user to use up all of memory for trace buffers. If the root user allocates too many entries, the OOM killer might start kill off all tasks. This patch adds an algorith to check the following condition: pages_requested > (freeable_memory + current_trace_buffer_pages) / 4 If the above is met then the allocation fails. The above prevents more than 1/4th of freeable memory from being used by trace buffers. To determine the freeable_memory, I made determine_dirtyable_memory in mm/page-writeback.c global. Special thanks goes to Peter Zijlstra for suggesting the above calculation. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/writeback.h | 2 ++ kernel/trace/trace.c | 38 ++++++++++++++++++++++++++++++++++++++ mm/page-writeback.c | 10 +++++++--- 3 files changed, 47 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/writeback.h b/include/linux/writeback.h index f462439cc288..bd91987c065f 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -105,6 +105,8 @@ extern int vm_highmem_is_dirtyable; extern int block_dump; extern int laptop_mode; +extern unsigned long determine_dirtyable_memory(void); + extern int dirty_ratio_handler(struct ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 82ced406aacf..2824cf48cdca 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -51,6 +52,8 @@ static int trace_free_page(void); static int tracing_disabled = 1; +static unsigned long tracing_pages_allocated; + long ns2usecs(cycle_t nsec) { @@ -2591,12 +2594,41 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, } if (val > global_trace.entries) { + long pages_requested; + unsigned long freeable_pages; + + /* make sure we have enough memory before mapping */ + pages_requested = + (val + (ENTRIES_PER_PAGE-1)) / ENTRIES_PER_PAGE; + + /* account for each buffer (and max_tr) */ + pages_requested *= tracing_nr_buffers * 2; + + /* Check for overflow */ + if (pages_requested < 0) { + cnt = -ENOMEM; + goto out; + } + + freeable_pages = determine_dirtyable_memory(); + + /* we only allow to request 1/4 of useable memory */ + if (pages_requested > + ((freeable_pages + tracing_pages_allocated) / 4)) { + cnt = -ENOMEM; + goto out; + } + while (global_trace.entries < val) { if (trace_alloc_page()) { cnt = -ENOMEM; goto out; } + /* double check that we don't go over the known pages */ + if (tracing_pages_allocated > pages_requested) + break; } + } else { /* include the number of entries in val (inc of page entries) */ while (global_trace.entries > val + (ENTRIES_PER_PAGE - 1)) @@ -2776,6 +2808,7 @@ static int trace_alloc_page(void) struct page *page, *tmp; LIST_HEAD(pages); void *array; + unsigned pages_allocated = 0; int i; /* first allocate a page for each CPU */ @@ -2787,6 +2820,7 @@ static int trace_alloc_page(void) goto free_pages; } + pages_allocated++; page = virt_to_page(array); list_add(&page->lru, &pages); @@ -2798,6 +2832,7 @@ static int trace_alloc_page(void) "for trace buffer!\n"); goto free_pages; } + pages_allocated++; page = virt_to_page(array); list_add(&page->lru, &pages); #endif @@ -2819,6 +2854,7 @@ static int trace_alloc_page(void) SetPageLRU(page); #endif } + tracing_pages_allocated += pages_allocated; global_trace.entries += ENTRIES_PER_PAGE; return 0; @@ -2853,6 +2889,8 @@ static int trace_free_page(void) page = list_entry(p, struct page, lru); ClearPageLRU(page); list_del(&page->lru); + tracing_pages_allocated--; + tracing_pages_allocated--; __free_page(page); tracing_reset(data); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 789b6adbef37..b38f700825fc 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -126,8 +126,6 @@ static void background_writeout(unsigned long _min_pages); static struct prop_descriptor vm_completions; static struct prop_descriptor vm_dirties; -static unsigned long determine_dirtyable_memory(void); - /* * couple the period to the dirty_ratio: * @@ -347,7 +345,13 @@ static unsigned long highmem_dirtyable_memory(unsigned long total) #endif } -static unsigned long determine_dirtyable_memory(void) +/** + * determine_dirtyable_memory - amount of memory that may be used + * + * Returns the numebr of pages that can currently be freed and used + * by the kernel for direct mappings. + */ +unsigned long determine_dirtyable_memory(void) { unsigned long x; -- cgit v1.2.3 From dc102a8fae2d0d6bf5223fc549247f2e23959ae6 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 12 May 2008 21:21:09 +0200 Subject: Markers - remove extra format argument Denys Vlasenko : > Not in this patch, but I noticed: > > #define __trace_mark(name, call_private, format, args...) \ > do { \ > static const char __mstrtab_##name[] \ > __attribute__((section("__markers_strings"))) \ > = #name "\0" format; \ > static struct marker __mark_##name \ > __attribute__((section("__markers"), aligned(8))) = \ > { __mstrtab_##name, &__mstrtab_##name[sizeof(#name)], \ > 0, 0, marker_probe_cb, \ > { __mark_empty_function, NULL}, NULL }; \ > __mark_check_format(format, ## args); \ > if (unlikely(__mark_##name.state)) { \ > (*__mark_##name.call) \ > (&__mark_##name, call_private, \ > format, ## args); \ > } \ > } while (0) > > In this call: > > (*__mark_##name.call) \ > (&__mark_##name, call_private, \ > format, ## args); \ > > you make gcc allocate duplicate format string. You can use > &__mstrtab_##name[sizeof(#name)] instead since it holds the same string, > or drop ", format," above and "const char *fmt" from here: > > void (*call)(const struct marker *mdata, /* Probe wrapper */ > void *call_private, const char *fmt, ...); > > since mdata->format is the same and all callees which need it can take it there. Very good point. I actually thought about dropping it, since it would remove an unnecessary argument from the stack. And actually, since I now have the marker_probe_cb sitting between the marker site and the callbacks, there is no API change required. Thanks :) Mathieu Signed-off-by: Mathieu Desnoyers CC: Denys Vlasenko Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/marker.h | 11 +++++------ kernel/marker.c | 30 ++++++++++++++---------------- 2 files changed, 19 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/marker.h b/include/linux/marker.h index 430f6adf9762..338533abb47b 100644 --- a/include/linux/marker.h +++ b/include/linux/marker.h @@ -44,8 +44,8 @@ struct marker { */ char state; /* Marker state. */ char ptype; /* probe type : 0 : single, 1 : multi */ - void (*call)(const struct marker *mdata, /* Probe wrapper */ - void *call_private, const char *fmt, ...); + /* Probe wrapper */ + void (*call)(const struct marker *mdata, void *call_private, ...); struct marker_probe_closure single; struct marker_probe_closure *multi; } __attribute__((aligned(8))); @@ -72,8 +72,7 @@ struct marker { __mark_check_format(format, ## args); \ if (unlikely(__mark_##name.state)) { \ (*__mark_##name.call) \ - (&__mark_##name, call_private, \ - format, ## args); \ + (&__mark_##name, call_private, ## args);\ } \ } while (0) @@ -117,9 +116,9 @@ static inline void __printf(1, 2) ___mark_check_format(const char *fmt, ...) extern marker_probe_func __mark_empty_function; extern void marker_probe_cb(const struct marker *mdata, - void *call_private, const char *fmt, ...); + void *call_private, ...); extern void marker_probe_cb_noarg(const struct marker *mdata, - void *call_private, const char *fmt, ...); + void *call_private, ...); /* * Connect a probe to a marker. diff --git a/kernel/marker.c b/kernel/marker.c index b5a9fe1d50d5..1abfb923b761 100644 --- a/kernel/marker.c +++ b/kernel/marker.c @@ -55,8 +55,8 @@ static DEFINE_MUTEX(markers_mutex); struct marker_entry { struct hlist_node hlist; char *format; - void (*call)(const struct marker *mdata, /* Probe wrapper */ - void *call_private, const char *fmt, ...); + /* Probe wrapper */ + void (*call)(const struct marker *mdata, void *call_private, ...); struct marker_probe_closure single; struct marker_probe_closure *multi; int refcount; /* Number of times armed. 0 if disarmed. */ @@ -91,15 +91,13 @@ EXPORT_SYMBOL_GPL(__mark_empty_function); * marker_probe_cb Callback that prepares the variable argument list for probes. * @mdata: pointer of type struct marker * @call_private: caller site private data - * @fmt: format string * @...: Variable argument list. * * Since we do not use "typical" pointer based RCU in the 1 argument case, we * need to put a full smp_rmb() in this branch. This is why we do not use * rcu_dereference() for the pointer read. */ -void marker_probe_cb(const struct marker *mdata, void *call_private, - const char *fmt, ...) +void marker_probe_cb(const struct marker *mdata, void *call_private, ...) { va_list args; char ptype; @@ -120,8 +118,9 @@ void marker_probe_cb(const struct marker *mdata, void *call_private, /* Must read the ptr before private data. They are not data * dependant, so we put an explicit smp_rmb() here. */ smp_rmb(); - va_start(args, fmt); - func(mdata->single.probe_private, call_private, fmt, &args); + va_start(args, call_private); + func(mdata->single.probe_private, call_private, mdata->format, + &args); va_end(args); } else { struct marker_probe_closure *multi; @@ -136,9 +135,9 @@ void marker_probe_cb(const struct marker *mdata, void *call_private, smp_read_barrier_depends(); multi = mdata->multi; for (i = 0; multi[i].func; i++) { - va_start(args, fmt); - multi[i].func(multi[i].probe_private, call_private, fmt, - &args); + va_start(args, call_private); + multi[i].func(multi[i].probe_private, call_private, + mdata->format, &args); va_end(args); } } @@ -150,13 +149,11 @@ EXPORT_SYMBOL_GPL(marker_probe_cb); * marker_probe_cb Callback that does not prepare the variable argument list. * @mdata: pointer of type struct marker * @call_private: caller site private data - * @fmt: format string * @...: Variable argument list. * * Should be connected to markers "MARK_NOARGS". */ -void marker_probe_cb_noarg(const struct marker *mdata, - void *call_private, const char *fmt, ...) +void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...) { va_list args; /* not initialized */ char ptype; @@ -172,7 +169,8 @@ void marker_probe_cb_noarg(const struct marker *mdata, /* Must read the ptr before private data. They are not data * dependant, so we put an explicit smp_rmb() here. */ smp_rmb(); - func(mdata->single.probe_private, call_private, fmt, &args); + func(mdata->single.probe_private, call_private, mdata->format, + &args); } else { struct marker_probe_closure *multi; int i; @@ -186,8 +184,8 @@ void marker_probe_cb_noarg(const struct marker *mdata, smp_read_barrier_depends(); multi = mdata->multi; for (i = 0; multi[i].func; i++) - multi[i].func(multi[i].probe_private, call_private, fmt, - &args); + multi[i].func(multi[i].probe_private, call_private, + mdata->format, &args); } preempt_enable(); } -- cgit v1.2.3 From 0aa977f592f17004f9d1d545f2e1bb9ea71896c3 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 12 May 2008 21:21:10 +0200 Subject: Markers - define non optimized marker To support the forthcoming "immediate values" marker optimization, we must have a way to declare markers in few code paths that does not use instruction modification based enable. This will be the case of printk(), some traps and eventually lockdep instrumentation. Changelog : - Fix reversed boolean logic of "generic". Signed-off-by: Mathieu Desnoyers Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/marker.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/marker.h b/include/linux/marker.h index 338533abb47b..1290653f9241 100644 --- a/include/linux/marker.h +++ b/include/linux/marker.h @@ -58,8 +58,12 @@ struct marker { * Make sure the alignment of the structure in the __markers section will * not add unwanted padding between the beginning of the section and the * structure. Force alignment to the same alignment as the section start. + * + * The "generic" argument controls which marker enabling mechanism must be used. + * If generic is true, a variable read is used. + * If generic is false, immediate values are used. */ -#define __trace_mark(name, call_private, format, args...) \ +#define __trace_mark(generic, name, call_private, format, args...) \ do { \ static const char __mstrtab_##name[] \ __attribute__((section("__markers_strings"))) \ @@ -79,7 +83,7 @@ struct marker { extern void marker_update_probe_range(struct marker *begin, struct marker *end); #else /* !CONFIG_MARKERS */ -#define __trace_mark(name, call_private, format, args...) \ +#define __trace_mark(generic, name, call_private, format, args...) \ __mark_check_format(format, ## args) static inline void marker_update_probe_range(struct marker *begin, struct marker *end) @@ -87,15 +91,30 @@ static inline void marker_update_probe_range(struct marker *begin, #endif /* CONFIG_MARKERS */ /** - * trace_mark - Marker + * trace_mark - Marker using code patching * @name: marker name, not quoted. * @format: format string * @args...: variable argument list * - * Places a marker. + * Places a marker using optimized code patching technique (imv_read()) + * to be enabled when immediate values are present. */ #define trace_mark(name, format, args...) \ - __trace_mark(name, NULL, format, ## args) + __trace_mark(0, name, NULL, format, ## args) + +/** + * _trace_mark - Marker using variable read + * @name: marker name, not quoted. + * @format: format string + * @args...: variable argument list + * + * Places a marker using a standard memory read (_imv_read()) to be + * enabled. Should be used for markers in code paths where instruction + * modification based enabling is not welcome. (__init and __exit functions, + * lockdep, some traps, printk). + */ +#define _trace_mark(name, format, args...) \ + __trace_mark(1, name, NULL, format, ## args) /** * MARK_NOARGS - Format string for a marker with no argument. -- cgit v1.2.3 From 5b82a1b08a00b2adca3d9dd9777efff40b7aaaa1 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 12 May 2008 21:21:10 +0200 Subject: Port ftrace to markers Porting ftrace to the marker infrastructure. Don't need to chain to the wakeup tracer from the sched tracer, because markers support multiple probes connected. Signed-off-by: Mathieu Desnoyers CC: Steven Rostedt Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 32 ------- kernel/sched.c | 14 +++- kernel/trace/trace.h | 20 +---- kernel/trace/trace_sched_switch.c | 171 +++++++++++++++++++++++++++++++------- kernel/trace/trace_sched_wakeup.c | 106 +++++++++++++++++++++-- 5 files changed, 255 insertions(+), 88 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 360ca99033d2..c0b1c69b55ce 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2131,38 +2131,6 @@ __trace_special(void *__tr, void *__data, } #endif -#ifdef CONFIG_CONTEXT_SWITCH_TRACER -extern void -ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next); -extern void -ftrace_wake_up_task(void *rq, struct task_struct *wakee, - struct task_struct *curr); -extern void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data); -extern void -ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); -#else -static inline void -ftrace_ctx_switch(void *rq, struct task_struct *prev, struct task_struct *next) -{ -} -static inline void -sched_trace_special(unsigned long p1, unsigned long p2, unsigned long p3) -{ -} -static inline void -ftrace_wake_up_task(void *rq, struct task_struct *wakee, - struct task_struct *curr) -{ -} -static inline void ftrace_all_fair_tasks(void *__rq, void *__tr, void *__data) -{ -} -static inline void -ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) -{ -} -#endif - extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask); extern long sched_getaffinity(pid_t pid, cpumask_t *mask); diff --git a/kernel/sched.c b/kernel/sched.c index ad95cca4e42e..e2e985eeee78 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2500,7 +2500,9 @@ out_activate: success = 1; out_running: - ftrace_wake_up_task(rq, p, rq->curr); + trace_mark(kernel_sched_wakeup, + "pid %d state %ld ## rq %p task %p rq->curr %p", + p->pid, p->state, rq, p, rq->curr); check_preempt_curr(rq, p); p->state = TASK_RUNNING; @@ -2631,7 +2633,9 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) p->sched_class->task_new(rq, p); inc_nr_running(rq); } - ftrace_wake_up_task(rq, p, rq->curr); + trace_mark(kernel_sched_wakeup_new, + "pid %d state %ld ## rq %p task %p rq->curr %p", + p->pid, p->state, rq, p, rq->curr); check_preempt_curr(rq, p); #ifdef CONFIG_SMP if (p->sched_class->task_wake_up) @@ -2804,7 +2808,11 @@ context_switch(struct rq *rq, struct task_struct *prev, struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); - ftrace_ctx_switch(rq, prev, next); + trace_mark(kernel_sched_schedule, + "prev_pid %d next_pid %d prev_state %ld " + "## rq %p prev %p next %p", + prev->pid, next->pid, prev->state, + rq, prev, next); mm = next->mm; oldmm = prev->active_mm; /* diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 8845033ab49d..f5de0601b408 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -234,25 +234,10 @@ void update_max_tr_single(struct trace_array *tr, extern cycle_t ftrace_now(int cpu); -#ifdef CONFIG_SCHED_TRACER -extern void -wakeup_sched_switch(struct task_struct *prev, struct task_struct *next); -extern void -wakeup_sched_wakeup(struct task_struct *wakee, struct task_struct *curr); -#else -static inline void -wakeup_sched_switch(struct task_struct *prev, struct task_struct *next) -{ -} -static inline void -wakeup_sched_wakeup(struct task_struct *wakee, struct task_struct *curr) -{ -} -#endif - #ifdef CONFIG_CONTEXT_SWITCH_TRACER typedef void (*tracer_switch_func_t)(void *private, + void *__rq, struct task_struct *prev, struct task_struct *next); @@ -262,9 +247,6 @@ struct tracer_switch_ops { struct tracer_switch_ops *next; }; -extern int register_tracer_switch(struct tracer_switch_ops *ops); -extern int unregister_tracer_switch(struct tracer_switch_ops *ops); - #endif /* CONFIG_CONTEXT_SWITCH_TRACER */ #ifdef CONFIG_DYNAMIC_FTRACE diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c index a3376478fc2c..d25ffa5eaf2b 100644 --- a/kernel/trace/trace_sched_switch.c +++ b/kernel/trace/trace_sched_switch.c @@ -16,11 +16,14 @@ static struct trace_array *ctx_trace; static int __read_mostly tracer_enabled; +static atomic_t sched_ref; static void -ctx_switch_func(void *__rq, struct task_struct *prev, struct task_struct *next) +sched_switch_func(void *private, void *__rq, struct task_struct *prev, + struct task_struct *next) { - struct trace_array *tr = ctx_trace; + struct trace_array **ptr = private; + struct trace_array *tr = *ptr; struct trace_array_cpu *data; unsigned long flags; long disabled; @@ -41,10 +44,40 @@ ctx_switch_func(void *__rq, struct task_struct *prev, struct task_struct *next) local_irq_restore(flags); } +static notrace void +sched_switch_callback(void *probe_data, void *call_data, + const char *format, va_list *args) +{ + struct task_struct *prev; + struct task_struct *next; + struct rq *__rq; + + if (!atomic_read(&sched_ref)) + return; + + /* skip prev_pid %d next_pid %d prev_state %ld */ + (void)va_arg(*args, int); + (void)va_arg(*args, int); + (void)va_arg(*args, long); + __rq = va_arg(*args, typeof(__rq)); + prev = va_arg(*args, typeof(prev)); + next = va_arg(*args, typeof(next)); + + tracing_record_cmdline(prev); + + /* + * If tracer_switch_func only points to the local + * switch func, it still needs the ptr passed to it. + */ + sched_switch_func(probe_data, __rq, prev, next); +} + static void -wakeup_func(void *__rq, struct task_struct *wakee, struct task_struct *curr) +wakeup_func(void *private, void *__rq, struct task_struct *wakee, struct + task_struct *curr) { - struct trace_array *tr = ctx_trace; + struct trace_array **ptr = private; + struct trace_array *tr = *ptr; struct trace_array_cpu *data; unsigned long flags; long disabled; @@ -67,35 +100,29 @@ wakeup_func(void *__rq, struct task_struct *wakee, struct task_struct *curr) local_irq_restore(flags); } -void -ftrace_ctx_switch(void *__rq, struct task_struct *prev, - struct task_struct *next) +static notrace void +wake_up_callback(void *probe_data, void *call_data, + const char *format, va_list *args) { - if (unlikely(atomic_read(&trace_record_cmdline_enabled))) - tracing_record_cmdline(prev); + struct task_struct *curr; + struct task_struct *task; + struct rq *__rq; - /* - * If tracer_switch_func only points to the local - * switch func, it still needs the ptr passed to it. - */ - ctx_switch_func(__rq, prev, next); + if (likely(!tracer_enabled)) + return; - /* - * Chain to the wakeup tracer (this is a NOP if disabled): - */ - wakeup_sched_switch(prev, next); -} + /* Skip pid %d state %ld */ + (void)va_arg(*args, int); + (void)va_arg(*args, long); + /* now get the meat: "rq %p task %p rq->curr %p" */ + __rq = va_arg(*args, typeof(__rq)); + task = va_arg(*args, typeof(task)); + curr = va_arg(*args, typeof(curr)); -void -ftrace_wake_up_task(void *__rq, struct task_struct *wakee, - struct task_struct *curr) -{ - wakeup_func(__rq, wakee, curr); + tracing_record_cmdline(task); + tracing_record_cmdline(curr); - /* - * Chain to the wakeup tracer (this is a NOP if disabled): - */ - wakeup_sched_wakeup(wakee, curr); + wakeup_func(probe_data, __rq, task, curr); } void @@ -132,15 +159,95 @@ static void sched_switch_reset(struct trace_array *tr) tracing_reset(tr->data[cpu]); } +static int tracing_sched_register(void) +{ + int ret; + + ret = marker_probe_register("kernel_sched_wakeup", + "pid %d state %ld ## rq %p task %p rq->curr %p", + wake_up_callback, + &ctx_trace); + if (ret) { + pr_info("wakeup trace: Couldn't add marker" + " probe to kernel_sched_wakeup\n"); + return ret; + } + + ret = marker_probe_register("kernel_sched_wakeup_new", + "pid %d state %ld ## rq %p task %p rq->curr %p", + wake_up_callback, + &ctx_trace); + if (ret) { + pr_info("wakeup trace: Couldn't add marker" + " probe to kernel_sched_wakeup_new\n"); + goto fail_deprobe; + } + + ret = marker_probe_register("kernel_sched_schedule", + "prev_pid %d next_pid %d prev_state %ld " + "## rq %p prev %p next %p", + sched_switch_callback, + &ctx_trace); + if (ret) { + pr_info("sched trace: Couldn't add marker" + " probe to kernel_sched_schedule\n"); + goto fail_deprobe_wake_new; + } + + return ret; +fail_deprobe_wake_new: + marker_probe_unregister("kernel_sched_wakeup_new", + wake_up_callback, + &ctx_trace); +fail_deprobe: + marker_probe_unregister("kernel_sched_wakeup", + wake_up_callback, + &ctx_trace); + return ret; +} + +static void tracing_sched_unregister(void) +{ + marker_probe_unregister("kernel_sched_schedule", + sched_switch_callback, + &ctx_trace); + marker_probe_unregister("kernel_sched_wakeup_new", + wake_up_callback, + &ctx_trace); + marker_probe_unregister("kernel_sched_wakeup", + wake_up_callback, + &ctx_trace); +} + +void tracing_start_sched_switch(void) +{ + long ref; + + ref = atomic_inc_return(&sched_ref); + if (ref == 1) + tracing_sched_register(); +} + +void tracing_stop_sched_switch(void) +{ + long ref; + + ref = atomic_dec_and_test(&sched_ref); + if (ref) + tracing_sched_unregister(); +} + static void start_sched_trace(struct trace_array *tr) { sched_switch_reset(tr); atomic_inc(&trace_record_cmdline_enabled); tracer_enabled = 1; + tracing_start_sched_switch(); } static void stop_sched_trace(struct trace_array *tr) { + tracing_stop_sched_switch(); atomic_dec(&trace_record_cmdline_enabled); tracer_enabled = 0; } @@ -181,6 +288,14 @@ static struct tracer sched_switch_trace __read_mostly = __init static int init_sched_switch_trace(void) { + int ret = 0; + + if (atomic_read(&sched_ref)) + ret = tracing_sched_register(); + if (ret) { + pr_info("error registering scheduler trace\n"); + return ret; + } return register_tracer(&sched_switch_trace); } device_initcall(init_sched_switch_trace); diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 5948011006bc..5d2fb48e47f8 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "trace.h" @@ -44,11 +45,13 @@ static int report_latency(cycle_t delta) return 1; } -void -wakeup_sched_switch(struct task_struct *prev, struct task_struct *next) +static void notrace +wakeup_sched_switch(void *private, void *rq, struct task_struct *prev, + struct task_struct *next) { unsigned long latency = 0, t0 = 0, t1 = 0; - struct trace_array *tr = wakeup_trace; + struct trace_array **ptr = private; + struct trace_array *tr = *ptr; struct trace_array_cpu *data; cycle_t T0, T1, delta; unsigned long flags; @@ -113,6 +116,31 @@ out: atomic_dec(&tr->data[cpu]->disabled); } +static notrace void +sched_switch_callback(void *probe_data, void *call_data, + const char *format, va_list *args) +{ + struct task_struct *prev; + struct task_struct *next; + struct rq *__rq; + + /* skip prev_pid %d next_pid %d prev_state %ld */ + (void)va_arg(*args, int); + (void)va_arg(*args, int); + (void)va_arg(*args, long); + __rq = va_arg(*args, typeof(__rq)); + prev = va_arg(*args, typeof(prev)); + next = va_arg(*args, typeof(next)); + + tracing_record_cmdline(prev); + + /* + * If tracer_switch_func only points to the local + * switch func, it still needs the ptr passed to it. + */ + wakeup_sched_switch(probe_data, __rq, prev, next); +} + static void __wakeup_reset(struct trace_array *tr) { struct trace_array_cpu *data; @@ -188,19 +216,68 @@ out: atomic_dec(&tr->data[cpu]->disabled); } -void wakeup_sched_wakeup(struct task_struct *wakee, struct task_struct *curr) +static notrace void +wake_up_callback(void *probe_data, void *call_data, + const char *format, va_list *args) { + struct trace_array **ptr = probe_data; + struct trace_array *tr = *ptr; + struct task_struct *curr; + struct task_struct *task; + struct rq *__rq; + if (likely(!tracer_enabled)) return; + /* Skip pid %d state %ld */ + (void)va_arg(*args, int); + (void)va_arg(*args, long); + /* now get the meat: "rq %p task %p rq->curr %p" */ + __rq = va_arg(*args, typeof(__rq)); + task = va_arg(*args, typeof(task)); + curr = va_arg(*args, typeof(curr)); + + tracing_record_cmdline(task); tracing_record_cmdline(curr); - tracing_record_cmdline(wakee); - wakeup_check_start(wakeup_trace, wakee, curr); + wakeup_check_start(tr, task, curr); } static void start_wakeup_tracer(struct trace_array *tr) { + int ret; + + ret = marker_probe_register("kernel_sched_wakeup", + "pid %d state %ld ## rq %p task %p rq->curr %p", + wake_up_callback, + &wakeup_trace); + if (ret) { + pr_info("wakeup trace: Couldn't add marker" + " probe to kernel_sched_wakeup\n"); + return; + } + + ret = marker_probe_register("kernel_sched_wakeup_new", + "pid %d state %ld ## rq %p task %p rq->curr %p", + wake_up_callback, + &wakeup_trace); + if (ret) { + pr_info("wakeup trace: Couldn't add marker" + " probe to kernel_sched_wakeup_new\n"); + goto fail_deprobe; + } + + ret = marker_probe_register("kernel_sched_schedule", + "prev_pid %d next_pid %d prev_state %ld " + "## rq %p prev %p next %p", + sched_switch_callback, + &wakeup_trace); + if (ret) { + pr_info("sched trace: Couldn't add marker" + " probe to kernel_sched_schedule\n"); + goto fail_deprobe_wake_new; + } + wakeup_reset(tr); /* @@ -215,11 +292,28 @@ static void start_wakeup_tracer(struct trace_array *tr) tracer_enabled = 1; return; +fail_deprobe_wake_new: + marker_probe_unregister("kernel_sched_wakeup_new", + wake_up_callback, + &wakeup_trace); +fail_deprobe: + marker_probe_unregister("kernel_sched_wakeup", + wake_up_callback, + &wakeup_trace); } static void stop_wakeup_tracer(struct trace_array *tr) { tracer_enabled = 0; + marker_probe_unregister("kernel_sched_schedule", + sched_switch_callback, + &wakeup_trace); + marker_probe_unregister("kernel_sched_wakeup_new", + wake_up_callback, + &wakeup_trace); + marker_probe_unregister("kernel_sched_wakeup", + wake_up_callback, + &wakeup_trace); } static void wakeup_tracer_init(struct trace_array *tr) -- cgit v1.2.3 From 74f4e369fc5b52433ad824cef32d3bf1304549be Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:21:15 +0200 Subject: ftrace: stacktrace fix Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 8 ++++++++ kernel/semaphore.c | 2 ++ kernel/trace/trace.c | 4 ++-- kernel/trace/trace.h | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 0d3714e7110b..017ab44d572a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -120,4 +120,12 @@ static inline void tracer_disable(void) # define trace_preempt_off(a0, a1) do { } while (0) #endif +#ifdef CONFIG_CONTEXT_SWITCH_TRACER +extern void +ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); +#else +static inline void +ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } +#endif + #endif /* _LINUX_FTRACE_H */ diff --git a/kernel/semaphore.c b/kernel/semaphore.c index 5c2942e768cd..1a064adab658 100644 --- a/kernel/semaphore.c +++ b/kernel/semaphore.c @@ -31,6 +31,7 @@ #include #include #include +#include static noinline void __down(struct semaphore *sem); static noinline int __down_interruptible(struct semaphore *sem); @@ -53,6 +54,7 @@ void down(struct semaphore *sem) { unsigned long flags; + ftrace_special(sem->count, 0, __LINE__); spin_lock_irqsave(&sem->lock, flags); if (likely(sem->count > 0)) sem->count--; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 2824cf48cdca..3271916ff033 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -901,7 +901,7 @@ tracing_sched_switch_trace(struct trace_array *tr, entry->ctx.next_pid = next->pid; entry->ctx.next_prio = next->prio; entry->ctx.next_state = next->state; - __trace_stack(tr, data, flags, 4); + __trace_stack(tr, data, flags, 5); __raw_spin_unlock(&data->lock); raw_local_irq_restore(irq_flags); } @@ -927,7 +927,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr, entry->ctx.next_pid = wakee->pid; entry->ctx.next_prio = wakee->prio; entry->ctx.next_state = wakee->state; - __trace_stack(tr, data, flags, 5); + __trace_stack(tr, data, flags, 6); __raw_spin_unlock(&data->lock); raw_local_irq_restore(irq_flags); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f5de0601b408..c460e85e94ed 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -51,7 +51,7 @@ struct special_entry { * Stack-trace entry: */ -#define FTRACE_STACK_ENTRIES 5 +#define FTRACE_STACK_ENTRIES 8 struct stack_entry { unsigned long caller[FTRACE_STACK_ENTRIES]; -- cgit v1.2.3 From d49dbf33f0bf8748ee3662b973eb57e60525d622 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 16 May 2008 10:41:53 +0200 Subject: ftrace: fix include file dependency Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 017ab44d572a..911d5d80b49f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -4,6 +4,7 @@ #ifdef CONFIG_FTRACE #include +#include extern int ftrace_enabled; extern int -- cgit v1.2.3 From 489f139614596cbc956a06f5e4bb41288e276fe3 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 25 Feb 2008 13:38:05 +0100 Subject: ftrace: fix build bug Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 911d5d80b49f..922e23d0196f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -106,16 +106,16 @@ static inline void tracer_disable(void) #endif #ifdef CONFIG_IRQSOFF_TRACER - extern void notrace time_hardirqs_on(unsigned long a0, unsigned long a1); - extern void notrace time_hardirqs_off(unsigned long a0, unsigned long a1); + extern void time_hardirqs_on(unsigned long a0, unsigned long a1); + extern void time_hardirqs_off(unsigned long a0, unsigned long a1); #else # define time_hardirqs_on(a0, a1) do { } while (0) # define time_hardirqs_off(a0, a1) do { } while (0) #endif #ifdef CONFIG_PREEMPT_TRACER - extern void notrace trace_preempt_on(unsigned long a0, unsigned long a1); - extern void notrace trace_preempt_off(unsigned long a0, unsigned long a1); + extern void trace_preempt_on(unsigned long a0, unsigned long a1); + extern void trace_preempt_off(unsigned long a0, unsigned long a1); #else # define trace_preempt_on(a0, a1) do { } while (0) # define trace_preempt_off(a0, a1) do { } while (0) -- cgit v1.2.3 From 8b7d89d02ef3c6a7c73d6596f28cea7632850af4 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:56 +0200 Subject: x86: mmiotrace - trace memory mapped IO Mmiotrace is a tool for trapping memory mapped IO (MMIO) accesses within the kernel. It is used for debugging and especially for reverse engineering evil binary drivers. Mmiotrace works by wrapping the ioremap family of kernel functions and marking the returned pages as not present. Access to the IO memory triggers a page fault, which will be handled by mmiotrace's custom page fault handler. This will single-step the faulted instruction with the MMIO page marked as present. Access logs are directed to user space via relay and debug_fs. This page fault approach is necessary, because binary drivers have readl/writel etc. calls inlined and therefore extremely difficult to trap with with e.g. kprobes. This patch depends on the custom page fault handlers patch. Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/Kconfig.debug | 27 ++ arch/x86/kernel/Makefile | 2 + arch/x86/kernel/init_task.c | 1 + arch/x86/kernel/mmiotrace/Makefile | 4 + arch/x86/kernel/mmiotrace/kmmio.c | 391 ++++++++++++++++++++++ arch/x86/kernel/mmiotrace/kmmio.h | 58 ++++ arch/x86/kernel/mmiotrace/mmio-mod.c | 527 ++++++++++++++++++++++++++++++ arch/x86/kernel/mmiotrace/pf_in.c | 489 +++++++++++++++++++++++++++ arch/x86/kernel/mmiotrace/pf_in.h | 39 +++ arch/x86/kernel/mmiotrace/testmmiotrace.c | 77 +++++ include/linux/mmiotrace.h | 62 ++++ 11 files changed, 1677 insertions(+) create mode 100644 arch/x86/kernel/mmiotrace/Makefile create mode 100644 arch/x86/kernel/mmiotrace/kmmio.c create mode 100644 arch/x86/kernel/mmiotrace/kmmio.h create mode 100644 arch/x86/kernel/mmiotrace/mmio-mod.c create mode 100644 arch/x86/kernel/mmiotrace/pf_in.c create mode 100644 arch/x86/kernel/mmiotrace/pf_in.h create mode 100644 arch/x86/kernel/mmiotrace/testmmiotrace.c create mode 100644 include/linux/mmiotrace.h (limited to 'include/linux') diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 9431a8399844..7c6496e2225e 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -176,6 +176,33 @@ config PAGE_FAULT_HANDLERS register a function that is called on every page fault. Custom handlers are used by some debugging and reverse engineering tools. +config MMIOTRACE + tristate "Memory mapped IO tracing" + depends on DEBUG_KERNEL && PAGE_FAULT_HANDLERS && RELAY && DEBUG_FS + default n + help + This will build a kernel module called mmiotrace. + + Mmiotrace traces Memory Mapped I/O access and is meant for debugging + and reverse engineering. The kernel module offers wrapped + versions of the ioremap family of functions. The driver to be traced + must be modified to call these wrappers. A user space program is + required to collect the MMIO data. + + See http://nouveau.freedesktop.org/wiki/MmioTrace + If you are not helping to develop drivers, say N. + +config MMIOTRACE_TEST + tristate "Test module for mmiotrace" + depends on MMIOTRACE && m + default n + help + This is a dumb module for testing mmiotrace. It is very dangerous + as it will write garbage to IO memory starting at a given address. + However, it should be safe to use on e.g. unused portion of VRAM. + + Say N, unless you absolutely know what you are doing. + # # IO delay types: # diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 739d49acd2f1..a51ac153685e 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -79,6 +79,8 @@ obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_VM86) += vm86_32.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_MMIOTRACE) += mmiotrace/ + obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_K8_NB) += k8.o diff --git a/arch/x86/kernel/init_task.c b/arch/x86/kernel/init_task.c index a4f93b4120c1..027a5b6a12b2 100644 --- a/arch/x86/kernel/init_task.c +++ b/arch/x86/kernel/init_task.c @@ -15,6 +15,7 @@ static struct signal_struct init_signals = INIT_SIGNALS(init_signals); static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); struct mm_struct init_mm = INIT_MM(init_mm); EXPORT_UNUSED_SYMBOL(init_mm); /* will be removed in 2.6.26 */ +EXPORT_SYMBOL_GPL(init_mm); /* * Initial thread structure. diff --git a/arch/x86/kernel/mmiotrace/Makefile b/arch/x86/kernel/mmiotrace/Makefile new file mode 100644 index 000000000000..d6905f7f981b --- /dev/null +++ b/arch/x86/kernel/mmiotrace/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MMIOTRACE) += mmiotrace.o +mmiotrace-objs := pf_in.o kmmio.o mmio-mod.o + +obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o diff --git a/arch/x86/kernel/mmiotrace/kmmio.c b/arch/x86/kernel/mmiotrace/kmmio.c new file mode 100644 index 000000000000..8ba48f9c91b4 --- /dev/null +++ b/arch/x86/kernel/mmiotrace/kmmio.c @@ -0,0 +1,391 @@ +/* Support for MMIO probes. + * Benfit many code from kprobes + * (C) 2002 Louis Zhuang . + * 2007 Alexander Eichner + * 2008 Pekka Paalanen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kmmio.h" + +#define KMMIO_HASH_BITS 6 +#define KMMIO_TABLE_SIZE (1 << KMMIO_HASH_BITS) +#define KMMIO_PAGE_HASH_BITS 4 +#define KMMIO_PAGE_TABLE_SIZE (1 << KMMIO_PAGE_HASH_BITS) + +struct kmmio_context { + struct kmmio_fault_page *fpage; + struct kmmio_probe *probe; + unsigned long saved_flags; + int active; +}; + +static int kmmio_page_fault(struct pt_regs *regs, unsigned long error_code, + unsigned long address); +static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, + void *args); + +static DEFINE_SPINLOCK(kmmio_lock); + +/* These are protected by kmmio_lock */ +unsigned int kmmio_count; +static unsigned int handler_registered; +static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE]; +static LIST_HEAD(kmmio_probes); + +static struct kmmio_context kmmio_ctx[NR_CPUS]; + +static struct pf_handler kmmio_pf_hook = { + .handler = kmmio_page_fault +}; + +static struct notifier_block nb_die = { + .notifier_call = kmmio_die_notifier +}; + +int init_kmmio(void) +{ + int i; + for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++) + INIT_LIST_HEAD(&kmmio_page_table[i]); + + register_die_notifier(&nb_die); + return 0; +} + +void cleanup_kmmio(void) +{ + /* + * Assume the following have been already cleaned by calling + * unregister_kmmio_probe() appropriately: + * kmmio_page_table, kmmio_probes + */ + if (handler_registered) { + unregister_page_fault_handler(&kmmio_pf_hook); + synchronize_rcu(); + } + unregister_die_notifier(&nb_die); +} + +/* + * this is basically a dynamic stabbing problem: + * Could use the existing prio tree code or + * Possible better implementations: + * The Interval Skip List: A Data Structure for Finding All Intervals That + * Overlap a Point (might be simple) + * Space Efficient Dynamic Stabbing with Fast Queries - Mikkel Thorup + */ +/* Get the kmmio at this addr (if any). You must be holding kmmio_lock. */ +static struct kmmio_probe *get_kmmio_probe(unsigned long addr) +{ + struct kmmio_probe *p; + list_for_each_entry(p, &kmmio_probes, list) { + if (addr >= p->addr && addr <= (p->addr + p->len)) + return p; + } + return NULL; +} + +static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page) +{ + struct list_head *head, *tmp; + + page &= PAGE_MASK; + head = &kmmio_page_table[hash_long(page, KMMIO_PAGE_HASH_BITS)]; + list_for_each(tmp, head) { + struct kmmio_fault_page *p + = list_entry(tmp, struct kmmio_fault_page, list); + if (p->page == page) + return p; + } + + return NULL; +} + +static void arm_kmmio_fault_page(unsigned long page, int *large) +{ + unsigned long address = page & PAGE_MASK; + pgd_t *pgd = pgd_offset_k(address); + pud_t *pud = pud_offset(pgd, address); + pmd_t *pmd = pmd_offset(pud, address); + pte_t *pte = pte_offset_kernel(pmd, address); + + if (pmd_large(*pmd)) { + set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_PRESENT)); + if (large) + *large = 1; + } else { + set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); + } + + __flush_tlb_one(page); +} + +static void disarm_kmmio_fault_page(unsigned long page, int *large) +{ + unsigned long address = page & PAGE_MASK; + pgd_t *pgd = pgd_offset_k(address); + pud_t *pud = pud_offset(pgd, address); + pmd_t *pmd = pmd_offset(pud, address); + pte_t *pte = pte_offset_kernel(pmd, address); + + if (large && *large) { + set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_PRESENT)); + *large = 0; + } else { + set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT)); + } + + __flush_tlb_one(page); +} + +/* + * Interrupts are disabled on entry as trap3 is an interrupt gate + * and they remain disabled thorough out this function. + */ +static int kmmio_handler(struct pt_regs *regs, unsigned long addr) +{ + struct kmmio_context *ctx; + int cpu; + + /* + * Preemption is now disabled to prevent process switch during + * single stepping. We can only handle one active kmmio trace + * per cpu, so ensure that we finish it before something else + * gets to run. + * + * XXX what if an interrupt occurs between returning from + * do_page_fault() and entering the single-step exception handler? + * And that interrupt triggers a kmmio trap? + */ + preempt_disable(); + cpu = smp_processor_id(); + ctx = &kmmio_ctx[cpu]; + + /* interrupts disabled and CPU-local data => atomicity guaranteed. */ + if (ctx->active) { + /* + * This avoids a deadlock with kmmio_lock. + * If this page fault really was due to kmmio trap, + * all hell breaks loose. + */ + printk(KERN_EMERG "mmiotrace: recursive probe hit on CPU %d, " + "for address %lu. Ignoring.\n", + cpu, addr); + goto no_kmmio; + } + ctx->active++; + + /* + * Acquire the kmmio lock to prevent changes affecting + * get_kmmio_fault_page() and get_kmmio_probe(), since we save their + * returned pointers. + * The lock is released in post_kmmio_handler(). + * XXX: could/should get_kmmio_*() be using RCU instead of spinlock? + */ + spin_lock(&kmmio_lock); + + ctx->fpage = get_kmmio_fault_page(addr); + if (!ctx->fpage) { + /* this page fault is not caused by kmmio */ + goto no_kmmio_locked; + } + + ctx->probe = get_kmmio_probe(addr); + ctx->saved_flags = (regs->flags & (TF_MASK|IF_MASK)); + + if (ctx->probe && ctx->probe->pre_handler) + ctx->probe->pre_handler(ctx->probe, regs, addr); + + regs->flags |= TF_MASK; + regs->flags &= ~IF_MASK; + + /* We hold lock, now we set present bit in PTE and single step. */ + disarm_kmmio_fault_page(ctx->fpage->page, NULL); + + return 1; + +no_kmmio_locked: + spin_unlock(&kmmio_lock); + ctx->active--; +no_kmmio: + preempt_enable_no_resched(); + /* page fault not handled by kmmio */ + return 0; +} + +/* + * Interrupts are disabled on entry as trap1 is an interrupt gate + * and they remain disabled thorough out this function. + * And we hold kmmio lock. + */ +static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + struct kmmio_context *ctx = &kmmio_ctx[cpu]; + + if (!ctx->active) + return 0; + + if (ctx->probe && ctx->probe->post_handler) + ctx->probe->post_handler(ctx->probe, condition, regs); + + arm_kmmio_fault_page(ctx->fpage->page, NULL); + + regs->flags &= ~TF_MASK; + regs->flags |= ctx->saved_flags; + + /* These were acquired in kmmio_handler(). */ + ctx->active--; + spin_unlock(&kmmio_lock); + preempt_enable_no_resched(); + + /* + * if somebody else is singlestepping across a probe point, flags + * will have TF set, in which case, continue the remaining processing + * of do_debug, as if this is not a probe hit. + */ + if (regs->flags & TF_MASK) + return 0; + + return 1; +} + +static int add_kmmio_fault_page(unsigned long page) +{ + struct kmmio_fault_page *f; + + page &= PAGE_MASK; + f = get_kmmio_fault_page(page); + if (f) { + f->count++; + return 0; + } + + f = kmalloc(sizeof(*f), GFP_ATOMIC); + if (!f) + return -1; + + f->count = 1; + f->page = page; + list_add(&f->list, + &kmmio_page_table[hash_long(f->page, KMMIO_PAGE_HASH_BITS)]); + + arm_kmmio_fault_page(f->page, NULL); + + return 0; +} + +static void release_kmmio_fault_page(unsigned long page) +{ + struct kmmio_fault_page *f; + + page &= PAGE_MASK; + f = get_kmmio_fault_page(page); + if (!f) + return; + + f->count--; + if (!f->count) { + disarm_kmmio_fault_page(f->page, NULL); + list_del(&f->list); + } +} + +int register_kmmio_probe(struct kmmio_probe *p) +{ + int ret = 0; + unsigned long size = 0; + + spin_lock_irq(&kmmio_lock); + kmmio_count++; + if (get_kmmio_probe(p->addr)) { + ret = -EEXIST; + goto out; + } + list_add(&p->list, &kmmio_probes); + /*printk("adding fault pages...\n");*/ + while (size < p->len) { + if (add_kmmio_fault_page(p->addr + size)) + printk(KERN_ERR "mmio: Unable to set page fault.\n"); + size += PAGE_SIZE; + } + + if (!handler_registered) { + register_page_fault_handler(&kmmio_pf_hook); + handler_registered++; + } + +out: + spin_unlock_irq(&kmmio_lock); + /* + * XXX: What should I do here? + * Here was a call to global_flush_tlb(), but it does not exist + * anymore. + */ + return ret; +} + +void unregister_kmmio_probe(struct kmmio_probe *p) +{ + unsigned long size = 0; + + spin_lock_irq(&kmmio_lock); + while (size < p->len) { + release_kmmio_fault_page(p->addr + size); + size += PAGE_SIZE; + } + list_del(&p->list); + kmmio_count--; + spin_unlock_irq(&kmmio_lock); +} + +/* + * According to 2.6.20, mainly x86_64 arch: + * This is being called from do_page_fault(), via the page fault notifier + * chain. The chain is called for both user space faults and kernel space + * faults (address >= TASK_SIZE64), except not on faults serviced by + * vmalloc_fault(). + * + * We may be in an interrupt or a critical section. Also prefecthing may + * trigger a page fault. We may be in the middle of process switch. + * The page fault hook functionality has put us inside RCU read lock. + * + * Local interrupts are disabled, so preemption cannot happen. + * Do not enable interrupts, do not sleep, and watch out for other CPUs. + */ +static int kmmio_page_fault(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + if (is_kmmio_active()) + if (kmmio_handler(regs, address) == 1) + return -1; + return 0; +} + +static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, + void *args) +{ + struct die_args *arg = args; + + if (val == DIE_DEBUG) + if (post_kmmio_handler(arg->err, arg->regs) == 1) + return NOTIFY_STOP; + + return NOTIFY_DONE; +} diff --git a/arch/x86/kernel/mmiotrace/kmmio.h b/arch/x86/kernel/mmiotrace/kmmio.h new file mode 100644 index 000000000000..85b7f68a3b8a --- /dev/null +++ b/arch/x86/kernel/mmiotrace/kmmio.h @@ -0,0 +1,58 @@ +#ifndef _LINUX_KMMIO_H +#define _LINUX_KMMIO_H + +#include +#include +#include +#include +#include +#include +#include + +struct kmmio_probe; +struct kmmio_fault_page; +struct pt_regs; + +typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *, + struct pt_regs *, unsigned long addr); +typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, + unsigned long condition, struct pt_regs *); + +struct kmmio_probe { + struct list_head list; + + /* start location of the probe point */ + unsigned long addr; + + /* length of the probe region */ + unsigned long len; + + /* Called before addr is executed. */ + kmmio_pre_handler_t pre_handler; + + /* Called after addr is executed, unless... */ + kmmio_post_handler_t post_handler; +}; + +struct kmmio_fault_page { + struct list_head list; + + /* location of the fault page */ + unsigned long page; + + int count; +}; + +/* kmmio is active by some kmmio_probes? */ +static inline int is_kmmio_active(void) +{ + extern unsigned int kmmio_count; + return kmmio_count; +} + +int init_kmmio(void); +void cleanup_kmmio(void); +int register_kmmio_probe(struct kmmio_probe *p); +void unregister_kmmio_probe(struct kmmio_probe *p); + +#endif /* _LINUX_KMMIO_H */ diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c new file mode 100644 index 000000000000..73561fe85f03 --- /dev/null +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c @@ -0,0 +1,527 @@ +/* + * 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. + * + * 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2005 + * Jeff Muizelaar, 2006, 2007 + * Pekka Paalanen, 2008 + * + * Derived from the read-mod example from relay-examples by Tom Zanussi. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for ISA_START_ADDRESS */ + +#include "kmmio.h" +#include "pf_in.h" + +/* This app's relay channel files will appear in /debug/mmio-trace */ +#define APP_DIR "mmio-trace" +/* the marker injection file in /proc */ +#define MARKER_FILE "mmio-marker" + +#define MODULE_NAME "mmiotrace" + +struct trap_reason { + unsigned long addr; + unsigned long ip; + enum reason_type type; + int active_traces; +}; + +static struct trap_reason pf_reason[NR_CPUS]; +static struct mm_io_header_rw cpu_trace[NR_CPUS]; + +static struct file_operations mmio_fops = { + .owner = THIS_MODULE, +}; + +static const size_t subbuf_size = 256*1024; +static struct rchan *chan; +static struct dentry *dir; +static int suspended; /* XXX should this be per cpu? */ +static struct proc_dir_entry *proc_marker_file; + +/* module parameters */ +static unsigned int n_subbufs = 32*4; +static unsigned long filter_offset; +static int nommiotrace; +static int ISA_trace; +static int trace_pc; + +module_param(n_subbufs, uint, 0); +module_param(filter_offset, ulong, 0); +module_param(nommiotrace, bool, 0); +module_param(ISA_trace, bool, 0); +module_param(trace_pc, bool, 0); + +MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128."); +MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); +MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); +MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); +MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions."); + +static void record_timestamp(struct mm_io_header *header) +{ + struct timespec now; + + getnstimeofday(&now); + header->sec = now.tv_sec; + header->nsec = now.tv_nsec; +} + +/* + * Write callback for the /proc entry: + * Read a marker and write it to the mmio trace log + */ +static int write_marker(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char *event = NULL; + struct mm_io_header *headp; + int len = (count > 65535) ? 65535 : count; + + event = kzalloc(sizeof(*headp) + len, GFP_KERNEL); + if (!event) + return -ENOMEM; + + headp = (struct mm_io_header *)event; + headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT); + headp->data_len = len; + record_timestamp(headp); + + if (copy_from_user(event + sizeof(*headp), buffer, len)) { + kfree(event); + return -EFAULT; + } + + relay_write(chan, event, sizeof(*headp) + len); + kfree(event); + return len; +} + +static void print_pte(unsigned long address) +{ + pgd_t *pgd = pgd_offset_k(address); + pud_t *pud = pud_offset(pgd, address); + pmd_t *pmd = pmd_offset(pud, address); + if (pmd_large(*pmd)) { + printk(KERN_EMERG MODULE_NAME ": 4MB pages are not " + "currently supported: %lx\n", + address); + BUG(); + } + printk(KERN_DEBUG MODULE_NAME ": pte for 0x%lx: 0x%lx 0x%lx\n", + address, + pte_val(*pte_offset_kernel(pmd, address)), + pte_val(*pte_offset_kernel(pmd, address)) & _PAGE_PRESENT); +} + +/* + * For some reason the pre/post pairs have been called in an + * unmatched order. Report and die. + */ +static void die_kmmio_nesting_error(struct pt_regs *regs, unsigned long addr) +{ + const unsigned long cpu = smp_processor_id(); + printk(KERN_EMERG MODULE_NAME ": unexpected fault for address: %lx, " + "last fault for address: %lx\n", + addr, pf_reason[cpu].addr); + print_pte(addr); +#ifdef __i386__ + print_symbol(KERN_EMERG "faulting EIP is at %s\n", regs->ip); + print_symbol(KERN_EMERG "last faulting EIP was at %s\n", + pf_reason[cpu].ip); + printk(KERN_EMERG + "eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", + regs->ax, regs->bx, regs->cx, regs->dx); + printk(KERN_EMERG + "esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", + regs->si, regs->di, regs->bp, regs->sp); +#else + print_symbol(KERN_EMERG "faulting RIP is at %s\n", regs->ip); + print_symbol(KERN_EMERG "last faulting RIP was at %s\n", + pf_reason[cpu].ip); + printk(KERN_EMERG "rax: %016lx rcx: %016lx rdx: %016lx\n", + regs->ax, regs->cx, regs->dx); + printk(KERN_EMERG "rsi: %016lx rdi: %016lx " + "rbp: %016lx rsp: %016lx\n", + regs->si, regs->di, regs->bp, regs->sp); +#endif + BUG(); +} + +static void pre(struct kmmio_probe *p, struct pt_regs *regs, + unsigned long addr) +{ + const unsigned long cpu = smp_processor_id(); + const unsigned long instptr = instruction_pointer(regs); + const enum reason_type type = get_ins_type(instptr); + + /* it doesn't make sense to have more than one active trace per cpu */ + if (pf_reason[cpu].active_traces) + die_kmmio_nesting_error(regs, addr); + else + pf_reason[cpu].active_traces++; + + pf_reason[cpu].type = type; + pf_reason[cpu].addr = addr; + pf_reason[cpu].ip = instptr; + + cpu_trace[cpu].header.type = MMIO_MAGIC; + cpu_trace[cpu].header.pid = 0; + cpu_trace[cpu].header.data_len = sizeof(struct mm_io_rw); + cpu_trace[cpu].rw.address = addr; + + /* + * Only record the program counter when requested. + * It may taint clean-room reverse engineering. + */ + if (trace_pc) + cpu_trace[cpu].rw.pc = instptr; + else + cpu_trace[cpu].rw.pc = 0; + + record_timestamp(&cpu_trace[cpu].header); + + switch (type) { + case REG_READ: + cpu_trace[cpu].header.type |= + (MMIO_READ << MMIO_OPCODE_SHIFT) | + (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); + break; + case REG_WRITE: + cpu_trace[cpu].header.type |= + (MMIO_WRITE << MMIO_OPCODE_SHIFT) | + (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); + cpu_trace[cpu].rw.value = get_ins_reg_val(instptr, regs); + break; + case IMM_WRITE: + cpu_trace[cpu].header.type |= + (MMIO_WRITE << MMIO_OPCODE_SHIFT) | + (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); + cpu_trace[cpu].rw.value = get_ins_imm_val(instptr); + break; + default: + { + unsigned char *ip = (unsigned char *)instptr; + cpu_trace[cpu].header.type |= + (MMIO_UNKNOWN_OP << MMIO_OPCODE_SHIFT); + cpu_trace[cpu].rw.value = (*ip) << 16 | + *(ip + 1) << 8 | + *(ip + 2); + } + } +} + +static void post(struct kmmio_probe *p, unsigned long condition, + struct pt_regs *regs) +{ + const unsigned long cpu = smp_processor_id(); + + /* this should always return the active_trace count to 0 */ + pf_reason[cpu].active_traces--; + if (pf_reason[cpu].active_traces) { + printk(KERN_EMERG MODULE_NAME ": unexpected post handler"); + BUG(); + } + + switch (pf_reason[cpu].type) { + case REG_READ: + cpu_trace[cpu].rw.value = get_ins_reg_val(pf_reason[cpu].ip, + regs); + break; + default: + break; + } + relay_write(chan, &cpu_trace[cpu], sizeof(struct mm_io_header_rw)); +} + +/* + * subbuf_start() relay callback. + * + * Defined so that we know when events are dropped due to the buffer-full + * condition. + */ +static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf, + void *prev_subbuf, size_t prev_padding) +{ + if (relay_buf_full(buf)) { + if (!suspended) { + suspended = 1; + printk(KERN_ERR MODULE_NAME + ": cpu %d buffer full!!!\n", + smp_processor_id()); + } + return 0; + } else if (suspended) { + suspended = 0; + printk(KERN_ERR MODULE_NAME + ": cpu %d buffer no longer full.\n", + smp_processor_id()); + } + + return 1; +} + +/* file_create() callback. Creates relay file in debugfs. */ +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, + int mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *buf_file; + + mmio_fops.read = relay_file_operations.read; + mmio_fops.open = relay_file_operations.open; + mmio_fops.poll = relay_file_operations.poll; + mmio_fops.mmap = relay_file_operations.mmap; + mmio_fops.release = relay_file_operations.release; + mmio_fops.splice_read = relay_file_operations.splice_read; + + buf_file = debugfs_create_file(filename, mode, parent, buf, + &mmio_fops); + + return buf_file; +} + +/* file_remove() default callback. Removes relay file in debugfs. */ +static int remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static struct rchan_callbacks relay_callbacks = { + .subbuf_start = subbuf_start_handler, + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + +/* + * create_channel - creates channel /debug/APP_DIR/cpuXXX + * Returns channel on success, NULL otherwise + */ +static struct rchan *create_channel(unsigned size, unsigned n) +{ + return relay_open("cpu", dir, size, n, &relay_callbacks, NULL); +} + +/* destroy_channel - destroys channel /debug/APP_DIR/cpuXXX */ +static void destroy_channel(void) +{ + if (chan) { + relay_close(chan); + chan = NULL; + } +} + +struct remap_trace { + struct list_head list; + struct kmmio_probe probe; +}; +static LIST_HEAD(trace_list); +static DEFINE_SPINLOCK(trace_list_lock); + +static void do_ioremap_trace_core(unsigned long offset, unsigned long size, + void __iomem *addr) +{ + struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL); + struct mm_io_header_map event = { + .header = { + .type = MMIO_MAGIC | + (MMIO_PROBE << MMIO_OPCODE_SHIFT), + .sec = 0, + .nsec = 0, + .pid = 0, + .data_len = sizeof(struct mm_io_map) + }, + .map = { + .phys = offset, + .addr = (unsigned long)addr, + .len = size, + .pc = 0 + } + }; + record_timestamp(&event.header); + + *trace = (struct remap_trace) { + .probe = { + .addr = (unsigned long)addr, + .len = size, + .pre_handler = pre, + .post_handler = post, + } + }; + + relay_write(chan, &event, sizeof(event)); + spin_lock(&trace_list_lock); + list_add_tail(&trace->list, &trace_list); + spin_unlock(&trace_list_lock); + if (!nommiotrace) + register_kmmio_probe(&trace->probe); +} + +static void ioremap_trace_core(unsigned long offset, unsigned long size, + void __iomem *addr) +{ + if ((filter_offset) && (offset != filter_offset)) + return; + + /* Don't trace the low PCI/ISA area, it's always mapped.. */ + if (!ISA_trace && (offset < ISA_END_ADDRESS) && + (offset + size > ISA_START_ADDRESS)) { + printk(KERN_NOTICE MODULE_NAME ": Ignoring map of low " + "PCI/ISA area (0x%lx-0x%lx)\n", + offset, offset + size); + return; + } + do_ioremap_trace_core(offset, size, addr); +} + +void __iomem *ioremap_cache_trace(unsigned long offset, unsigned long size) +{ + void __iomem *p = ioremap_cache(offset, size); + printk(KERN_DEBUG MODULE_NAME ": ioremap_cache(0x%lx, 0x%lx) = %p\n", + offset, size, p); + ioremap_trace_core(offset, size, p); + return p; +} +EXPORT_SYMBOL(ioremap_cache_trace); + +void __iomem *ioremap_nocache_trace(unsigned long offset, unsigned long size) +{ + void __iomem *p = ioremap_nocache(offset, size); + printk(KERN_DEBUG MODULE_NAME ": ioremap_nocache(0x%lx, 0x%lx) = %p\n", + offset, size, p); + ioremap_trace_core(offset, size, p); + return p; +} +EXPORT_SYMBOL(ioremap_nocache_trace); + +void iounmap_trace(volatile void __iomem *addr) +{ + struct mm_io_header_map event = { + .header = { + .type = MMIO_MAGIC | + (MMIO_UNPROBE << MMIO_OPCODE_SHIFT), + .sec = 0, + .nsec = 0, + .pid = 0, + .data_len = sizeof(struct mm_io_map) + }, + .map = { + .phys = 0, + .addr = (unsigned long)addr, + .len = 0, + .pc = 0 + } + }; + struct remap_trace *trace; + struct remap_trace *tmp; + printk(KERN_DEBUG MODULE_NAME ": Unmapping %p.\n", addr); + record_timestamp(&event.header); + + spin_lock(&trace_list_lock); + list_for_each_entry_safe(trace, tmp, &trace_list, list) { + if ((unsigned long)addr == trace->probe.addr) { + if (!nommiotrace) + unregister_kmmio_probe(&trace->probe); + list_del(&trace->list); + kfree(trace); + break; + } + } + spin_unlock(&trace_list_lock); + relay_write(chan, &event, sizeof(event)); + iounmap(addr); +} +EXPORT_SYMBOL(iounmap_trace); + +static void clear_trace_list(void) +{ + struct remap_trace *trace; + struct remap_trace *tmp; + + spin_lock(&trace_list_lock); + list_for_each_entry_safe(trace, tmp, &trace_list, list) { + printk(KERN_WARNING MODULE_NAME ": purging non-iounmapped " + "trace @0x%08lx, size 0x%lx.\n", + trace->probe.addr, trace->probe.len); + if (!nommiotrace) + unregister_kmmio_probe(&trace->probe); + list_del(&trace->list); + kfree(trace); + break; + } + spin_unlock(&trace_list_lock); +} + +static int __init init(void) +{ + if (n_subbufs < 2) + return -EINVAL; + + dir = debugfs_create_dir(APP_DIR, NULL); + if (!dir) { + printk(KERN_ERR MODULE_NAME + ": Couldn't create relay app directory.\n"); + return -ENOMEM; + } + + chan = create_channel(subbuf_size, n_subbufs); + if (!chan) { + debugfs_remove(dir); + printk(KERN_ERR MODULE_NAME + ": relay app channel creation failed\n"); + return -ENOMEM; + } + + init_kmmio(); + + proc_marker_file = create_proc_entry(MARKER_FILE, 0, NULL); + if (proc_marker_file) + proc_marker_file->write_proc = write_marker; + + printk(KERN_DEBUG MODULE_NAME ": loaded.\n"); + if (nommiotrace) + printk(KERN_DEBUG MODULE_NAME ": MMIO tracing disabled.\n"); + if (ISA_trace) + printk(KERN_WARNING MODULE_NAME + ": Warning! low ISA range will be traced.\n"); + return 0; +} + +static void __exit cleanup(void) +{ + printk(KERN_DEBUG MODULE_NAME ": unload...\n"); + clear_trace_list(); + cleanup_kmmio(); + remove_proc_entry(MARKER_FILE, NULL); + destroy_channel(); + if (dir) + debugfs_remove(dir); +} + +module_init(init); +module_exit(cleanup); +MODULE_LICENSE("GPL"); diff --git a/arch/x86/kernel/mmiotrace/pf_in.c b/arch/x86/kernel/mmiotrace/pf_in.c new file mode 100644 index 000000000000..67ea520dde62 --- /dev/null +++ b/arch/x86/kernel/mmiotrace/pf_in.c @@ -0,0 +1,489 @@ +/* + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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. + * + * 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + */ + +/* $Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp $ + * Copyright by Intel Crop., 2002 + * Louis Zhuang (louis.zhuang@intel.com) + * + * Bjorn Steinbrink (B.Steinbrink@gmx.de), 2007 + */ + +#include +#include /* struct pt_regs */ +#include "pf_in.h" + +#ifdef __i386__ +/* IA32 Manual 3, 2-1 */ +static unsigned char prefix_codes[] = { + 0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64, + 0x65, 0x2E, 0x3E, 0x66, 0x67 +}; +/* IA32 Manual 3, 3-432*/ +static unsigned int reg_rop[] = { + 0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F +}; +static unsigned int reg_wop[] = { 0x88, 0x89 }; +static unsigned int imm_wop[] = { 0xC6, 0xC7 }; +/* IA32 Manual 3, 3-432*/ +static unsigned int rw8[] = { 0x88, 0x8A, 0xC6 }; +static unsigned int rw32[] = { + 0x89, 0x8B, 0xC7, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F +}; +static unsigned int mw8[] = { 0x88, 0x8A, 0xC6, 0xB60F, 0xBE0F }; +static unsigned int mw16[] = { 0xB70F, 0xBF0F }; +static unsigned int mw32[] = { 0x89, 0x8B, 0xC7 }; +static unsigned int mw64[] = {}; +#else /* not __i386__ */ +static unsigned char prefix_codes[] = { + 0x66, 0x67, 0x2E, 0x3E, 0x26, 0x64, 0x65, 0x36, + 0xF0, 0xF3, 0xF2, + /* REX Prefixes */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f +}; +/* AMD64 Manual 3, Appendix A*/ +static unsigned int reg_rop[] = { + 0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F +}; +static unsigned int reg_wop[] = { 0x88, 0x89 }; +static unsigned int imm_wop[] = { 0xC6, 0xC7 }; +static unsigned int rw8[] = { 0xC6, 0x88, 0x8A }; +static unsigned int rw32[] = { + 0xC7, 0x89, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F +}; +/* 8 bit only */ +static unsigned int mw8[] = { 0xC6, 0x88, 0x8A, 0xB60F, 0xBE0F }; +/* 16 bit only */ +static unsigned int mw16[] = { 0xB70F, 0xBF0F }; +/* 16 or 32 bit */ +static unsigned int mw32[] = { 0xC7 }; +/* 16, 32 or 64 bit */ +static unsigned int mw64[] = { 0x89, 0x8B }; +#endif /* not __i386__ */ + +static int skip_prefix(unsigned char *addr, int *shorted, int *enlarged, + int *rexr) +{ + int i; + unsigned char *p = addr; + *shorted = 0; + *enlarged = 0; + *rexr = 0; + +restart: + for (i = 0; i < ARRAY_SIZE(prefix_codes); i++) { + if (*p == prefix_codes[i]) { + if (*p == 0x66) + *shorted = 1; +#ifdef __amd64__ + if ((*p & 0xf8) == 0x48) + *enlarged = 1; + if ((*p & 0xf4) == 0x44) + *rexr = 1; +#endif + p++; + goto restart; + } + } + + return (p - addr); +} + +static int get_opcode(unsigned char *addr, unsigned int *opcode) +{ + int len; + + if (*addr == 0x0F) { + /* 0x0F is extension instruction */ + *opcode = *(unsigned short *)addr; + len = 2; + } else { + *opcode = *addr; + len = 1; + } + + return len; +} + +#define CHECK_OP_TYPE(opcode, array, type) \ + for (i = 0; i < ARRAY_SIZE(array); i++) { \ + if (array[i] == opcode) { \ + rv = type; \ + goto exit; \ + } \ + } + +enum reason_type get_ins_type(unsigned long ins_addr) +{ + unsigned int opcode; + unsigned char *p; + int shorted, enlarged, rexr; + int i; + enum reason_type rv = OTHERS; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted, &enlarged, &rexr); + p += get_opcode(p, &opcode); + + CHECK_OP_TYPE(opcode, reg_rop, REG_READ); + CHECK_OP_TYPE(opcode, reg_wop, REG_WRITE); + CHECK_OP_TYPE(opcode, imm_wop, IMM_WRITE); + +exit: + return rv; +} +#undef CHECK_OP_TYPE + +static unsigned int get_ins_reg_width(unsigned long ins_addr) +{ + unsigned int opcode; + unsigned char *p; + int i, shorted, enlarged, rexr; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted, &enlarged, &rexr); + p += get_opcode(p, &opcode); + + for (i = 0; i < ARRAY_SIZE(rw8); i++) + if (rw8[i] == opcode) + return 1; + + for (i = 0; i < ARRAY_SIZE(rw32); i++) + if (rw32[i] == opcode) + return (shorted ? 2 : (enlarged ? 8 : 4)); + + printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode); + return 0; +} + +unsigned int get_ins_mem_width(unsigned long ins_addr) +{ + unsigned int opcode; + unsigned char *p; + int i, shorted, enlarged, rexr; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted, &enlarged, &rexr); + p += get_opcode(p, &opcode); + + for (i = 0; i < ARRAY_SIZE(mw8); i++) + if (mw8[i] == opcode) + return 1; + + for (i = 0; i < ARRAY_SIZE(mw16); i++) + if (mw16[i] == opcode) + return 2; + + for (i = 0; i < ARRAY_SIZE(mw32); i++) + if (mw32[i] == opcode) + return shorted ? 2 : 4; + + for (i = 0; i < ARRAY_SIZE(mw64); i++) + if (mw64[i] == opcode) + return shorted ? 2 : (enlarged ? 8 : 4); + + printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode); + return 0; +} + +/* + * Define register ident in mod/rm byte. + * Note: these are NOT the same as in ptrace-abi.h. + */ +enum { + arg_AL = 0, + arg_CL = 1, + arg_DL = 2, + arg_BL = 3, + arg_AH = 4, + arg_CH = 5, + arg_DH = 6, + arg_BH = 7, + + arg_AX = 0, + arg_CX = 1, + arg_DX = 2, + arg_BX = 3, + arg_SP = 4, + arg_BP = 5, + arg_SI = 6, + arg_DI = 7, +#ifdef __amd64__ + arg_R8 = 8, + arg_R9 = 9, + arg_R10 = 10, + arg_R11 = 11, + arg_R12 = 12, + arg_R13 = 13, + arg_R14 = 14, + arg_R15 = 15 +#endif +}; + +static unsigned char *get_reg_w8(int no, struct pt_regs *regs) +{ + unsigned char *rv = NULL; + + switch (no) { + case arg_AL: + rv = (unsigned char *)®s->ax; + break; + case arg_BL: + rv = (unsigned char *)®s->bx; + break; + case arg_CL: + rv = (unsigned char *)®s->cx; + break; + case arg_DL: + rv = (unsigned char *)®s->dx; + break; + case arg_AH: + rv = 1 + (unsigned char *)®s->ax; + break; + case arg_BH: + rv = 1 + (unsigned char *)®s->bx; + break; + case arg_CH: + rv = 1 + (unsigned char *)®s->cx; + break; + case arg_DH: + rv = 1 + (unsigned char *)®s->dx; + break; +#ifdef __amd64__ + case arg_R8: + rv = (unsigned char *)®s->r8; + break; + case arg_R9: + rv = (unsigned char *)®s->r9; + break; + case arg_R10: + rv = (unsigned char *)®s->r10; + break; + case arg_R11: + rv = (unsigned char *)®s->r11; + break; + case arg_R12: + rv = (unsigned char *)®s->r12; + break; + case arg_R13: + rv = (unsigned char *)®s->r13; + break; + case arg_R14: + rv = (unsigned char *)®s->r14; + break; + case arg_R15: + rv = (unsigned char *)®s->r15; + break; +#endif + default: + printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no); + break; + } + return rv; +} + +static unsigned long *get_reg_w32(int no, struct pt_regs *regs) +{ + unsigned long *rv = NULL; + + switch (no) { + case arg_AX: + rv = ®s->ax; + break; + case arg_BX: + rv = ®s->bx; + break; + case arg_CX: + rv = ®s->cx; + break; + case arg_DX: + rv = ®s->dx; + break; + case arg_SP: + rv = ®s->sp; + break; + case arg_BP: + rv = ®s->bp; + break; + case arg_SI: + rv = ®s->si; + break; + case arg_DI: + rv = ®s->di; + break; +#ifdef __amd64__ + case arg_R8: + rv = ®s->r8; + break; + case arg_R9: + rv = ®s->r9; + break; + case arg_R10: + rv = ®s->r10; + break; + case arg_R11: + rv = ®s->r11; + break; + case arg_R12: + rv = ®s->r12; + break; + case arg_R13: + rv = ®s->r13; + break; + case arg_R14: + rv = ®s->r14; + break; + case arg_R15: + rv = ®s->r15; + break; +#endif + default: + printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no); + } + + return rv; +} + +unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs) +{ + unsigned int opcode; + unsigned char mod_rm; + int reg; + unsigned char *p; + int i, shorted, enlarged, rexr; + unsigned long rv; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted, &enlarged, &rexr); + p += get_opcode(p, &opcode); + for (i = 0; i < ARRAY_SIZE(reg_rop); i++) + if (reg_rop[i] == opcode) { + rv = REG_READ; + goto do_work; + } + + for (i = 0; i < ARRAY_SIZE(reg_wop); i++) + if (reg_wop[i] == opcode) { + rv = REG_WRITE; + goto do_work; + } + + printk(KERN_ERR "mmiotrace: Not a register instruction, opcode " + "0x%02x\n", opcode); + goto err; + +do_work: + mod_rm = *p; + reg = ((mod_rm >> 3) & 0x7) | (rexr << 3); + switch (get_ins_reg_width(ins_addr)) { + case 1: + return *get_reg_w8(reg, regs); + + case 2: + return *(unsigned short *)get_reg_w32(reg, regs); + + case 4: + return *(unsigned int *)get_reg_w32(reg, regs); + +#ifdef __amd64__ + case 8: + return *(unsigned long *)get_reg_w32(reg, regs); +#endif + + default: + printk(KERN_ERR "mmiotrace: Error width# %d\n", reg); + } + +err: + return 0; +} + +unsigned long get_ins_imm_val(unsigned long ins_addr) +{ + unsigned int opcode; + unsigned char mod_rm; + unsigned char mod; + unsigned char *p; + int i, shorted, enlarged, rexr; + unsigned long rv; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted, &enlarged, &rexr); + p += get_opcode(p, &opcode); + for (i = 0; i < ARRAY_SIZE(imm_wop); i++) + if (imm_wop[i] == opcode) { + rv = IMM_WRITE; + goto do_work; + } + + printk(KERN_ERR "mmiotrace: Not an immediate instruction, opcode " + "0x%02x\n", opcode); + goto err; + +do_work: + mod_rm = *p; + mod = mod_rm >> 6; + p++; + switch (mod) { + case 0: + /* if r/m is 5 we have a 32 disp (IA32 Manual 3, Table 2-2) */ + /* AMD64: XXX Check for address size prefix? */ + if ((mod_rm & 0x7) == 0x5) + p += 4; + break; + + case 1: + p += 1; + break; + + case 2: + p += 4; + break; + + case 3: + default: + printk(KERN_ERR "mmiotrace: not a memory access instruction " + "at 0x%lx, rm_mod=0x%02x\n", + ins_addr, mod_rm); + } + + switch (get_ins_reg_width(ins_addr)) { + case 1: + return *(unsigned char *)p; + + case 2: + return *(unsigned short *)p; + + case 4: + return *(unsigned int *)p; + +#ifdef __amd64__ + case 8: + return *(unsigned long *)p; +#endif + + default: + printk(KERN_ERR "mmiotrace: Error: width.\n"); + } + +err: + return 0; +} diff --git a/arch/x86/kernel/mmiotrace/pf_in.h b/arch/x86/kernel/mmiotrace/pf_in.h new file mode 100644 index 000000000000..e05341a51a27 --- /dev/null +++ b/arch/x86/kernel/mmiotrace/pf_in.h @@ -0,0 +1,39 @@ +/* + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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. + * + * 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + */ + +#ifndef __PF_H_ +#define __PF_H_ + +enum reason_type { + NOT_ME, /* page fault is not in regions */ + NOTHING, /* access others point in regions */ + REG_READ, /* read from addr to reg */ + REG_WRITE, /* write from reg to addr */ + IMM_WRITE, /* write from imm to addr */ + OTHERS /* Other instructions can not intercept */ +}; + +enum reason_type get_ins_type(unsigned long ins_addr); +unsigned int get_ins_mem_width(unsigned long ins_addr); +unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs); +unsigned long get_ins_imm_val(unsigned long ins_addr); + +#endif /* __PF_H_ */ diff --git a/arch/x86/kernel/mmiotrace/testmmiotrace.c b/arch/x86/kernel/mmiotrace/testmmiotrace.c new file mode 100644 index 000000000000..40e66b0e6480 --- /dev/null +++ b/arch/x86/kernel/mmiotrace/testmmiotrace.c @@ -0,0 +1,77 @@ +/* + * Written by Pekka Paalanen, 2008 + */ +#include +#include + +extern void __iomem *ioremap_nocache_trace(unsigned long offset, + unsigned long size); +extern void iounmap_trace(volatile void __iomem *addr); + +#define MODULE_NAME "testmmiotrace" + +static unsigned long mmio_address; +module_param(mmio_address, ulong, 0); +MODULE_PARM_DESC(mmio_address, "Start address of the mapping of 16 kB."); + +static void do_write_test(void __iomem *p) +{ + unsigned int i; + for (i = 0; i < 256; i++) + iowrite8(i, p + i); + for (i = 1024; i < (5 * 1024); i += 2) + iowrite16(i * 12 + 7, p + i); + for (i = (5 * 1024); i < (16 * 1024); i += 4) + iowrite32(i * 212371 + 13, p + i); +} + +static void do_read_test(void __iomem *p) +{ + unsigned int i; + volatile unsigned int v; + for (i = 0; i < 256; i++) + v = ioread8(p + i); + for (i = 1024; i < (5 * 1024); i += 2) + v = ioread16(p + i); + for (i = (5 * 1024); i < (16 * 1024); i += 4) + v = ioread32(p + i); +} + +static void do_test(void) +{ + void __iomem *p = ioremap_nocache_trace(mmio_address, 0x4000); + if (!p) { + printk(KERN_ERR MODULE_NAME ": could not ioremap IO memory, " + "aborting.\n"); + return; + } + do_write_test(p); + do_read_test(p); + iounmap_trace(p); +} + +static int __init init(void) +{ + if (mmio_address == 0) { + printk(KERN_ERR MODULE_NAME ": you have to use the module " + "argument mmio_address.\n"); + printk(KERN_ERR MODULE_NAME ": DO NOT LOAD THIS MODULE UNLESS" + " YOU REALLY KNOW WHAT YOU ARE DOING!\n"); + return -ENXIO; + } + + printk(KERN_WARNING MODULE_NAME ": WARNING: mapping 16 kB @ 0x%08lx " + "in PCI address space, and writing " + "rubbish in there.\n", mmio_address); + do_test(); + return 0; +} + +static void __exit cleanup(void) +{ + printk(KERN_DEBUG MODULE_NAME ": unloaded.\n"); +} + +module_init(init); +module_exit(cleanup); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h new file mode 100644 index 000000000000..cb247825f3ec --- /dev/null +++ b/include/linux/mmiotrace.h @@ -0,0 +1,62 @@ +#ifndef MMIOTRACE_H +#define MMIOTRACE_H + +#include + +#define MMIO_VERSION 0x04 + +/* mm_io_header.type */ +#define MMIO_OPCODE_MASK 0xff +#define MMIO_OPCODE_SHIFT 0 +#define MMIO_WIDTH_MASK 0xff00 +#define MMIO_WIDTH_SHIFT 8 +#define MMIO_MAGIC (0x6f000000 | (MMIO_VERSION<<16)) +#define MMIO_MAGIC_MASK 0xffff0000 + +enum mm_io_opcode { /* payload type: */ + MMIO_READ = 0x1, /* struct mm_io_rw */ + MMIO_WRITE = 0x2, /* struct mm_io_rw */ + MMIO_PROBE = 0x3, /* struct mm_io_map */ + MMIO_UNPROBE = 0x4, /* struct mm_io_map */ + MMIO_MARKER = 0x5, /* raw char data */ + MMIO_UNKNOWN_OP = 0x6, /* struct mm_io_rw */ +}; + +struct mm_io_header { + __u32 type; + __u32 sec; /* timestamp */ + __u32 nsec; + __u32 pid; /* PID of the process, or 0 for kernel core */ + __u16 data_len; /* length of the following payload */ +}; + +struct mm_io_rw { + __u64 address; /* virtual address of register */ + __u64 value; + __u64 pc; /* optional program counter */ +}; + +struct mm_io_map { + __u64 phys; /* base address in PCI space */ + __u64 addr; /* base virtual address */ + __u64 len; /* mapping size */ + __u64 pc; /* optional program counter */ +}; + + +/* + * These structures are used to allow a single relay_write() + * call to write a full packet. + */ + +struct mm_io_header_rw { + struct mm_io_header header; + struct mm_io_rw rw; +} __attribute__((packed)); + +struct mm_io_header_map { + struct mm_io_header header; + struct mm_io_map map; +} __attribute__((packed)); + +#endif /* MMIOTRACE_H */ -- cgit v1.2.3 From 63ffa3e456c1a9884a3ebac997d91e3fdae18d78 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:57 +0200 Subject: x86 mmiotrace: comment about user space ABI Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/mmiotrace.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index cb247825f3ec..6ec288f1fe24 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -3,6 +3,10 @@ #include +/* + * If you change anything here, you must bump MMIO_VERSION. + * This is the relay data format for user space. + */ #define MMIO_VERSION 0x04 /* mm_io_header.type */ @@ -23,7 +27,7 @@ enum mm_io_opcode { /* payload type: */ }; struct mm_io_header { - __u32 type; + __u32 type; /* see MMIO_* macros above */ __u32 sec; /* timestamp */ __u32 nsec; __u32 pid; /* PID of the process, or 0 for kernel core */ -- cgit v1.2.3 From 0fd0e3da4557c479b820b9a4a7afa25b4637ddf2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:57 +0200 Subject: x86: mmiotrace full patch, preview 1 kmmio.c handles the list of mmio probes with callbacks, list of traced pages, and attaching into the page fault handler and die notifier. It arms, traps and disarms the given pages, this is the core of mmiotrace. mmio-mod.c is a user interface, hooking into ioremap functions and registering the mmio probes. It also decodes the required information from trapped mmio accesses via the pre and post callbacks in each probe. Currently, hooking into ioremap functions works by redefining the symbols of the target (binary) kernel module, so that it calls the traced versions of the functions. The most notable changes done since the last discussion are: - kmmio.c is a built-in, not part of the module - direct call from fault.c to kmmio.c, removing all dynamic hooks - prepare for unregistering probes at any time - make kmmio re-initializable and accessible to more than one user - rewrite kmmio locking to remove all spinlocks from page fault path Can I abuse call_rcu() like I do in kmmio.c:unregister_kmmio_probe() or is there a better way? The function called via call_rcu() itself calls call_rcu() again, will this work or break? There I need a second grace period for RCU after the first grace period for page faults. Mmiotrace itself (mmio-mod.c) is still a module, I am going to attack that next. At some point I will start looking into how to make mmiotrace a tracer component of ftrace (thanks for the hint, Ingo). Ftrace should make the user space part of mmiotracing as simple as 'cat /debug/trace/mmio > dump.txt'. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/init_task.c | 1 - arch/x86/kernel/mmiotrace/Makefile | 8 +- arch/x86/kernel/mmiotrace/kmmio.c | 349 ++++++++++++++++++++---------- arch/x86/kernel/mmiotrace/kmmio.h | 58 ----- arch/x86/kernel/mmiotrace/mmio-mod.c | 81 ++++--- arch/x86/kernel/mmiotrace/pf_in.c | 2 +- arch/x86/kernel/mmiotrace/testmmiotrace.c | 13 +- arch/x86/mm/fault.c | 59 +---- include/asm-x86/kdebug.h | 7 - include/linux/mmiotrace.h | 38 ++++ 10 files changed, 335 insertions(+), 281 deletions(-) delete mode 100644 arch/x86/kernel/mmiotrace/kmmio.h (limited to 'include/linux') diff --git a/arch/x86/kernel/init_task.c b/arch/x86/kernel/init_task.c index 027a5b6a12b2..a4f93b4120c1 100644 --- a/arch/x86/kernel/init_task.c +++ b/arch/x86/kernel/init_task.c @@ -15,7 +15,6 @@ static struct signal_struct init_signals = INIT_SIGNALS(init_signals); static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); struct mm_struct init_mm = INIT_MM(init_mm); EXPORT_UNUSED_SYMBOL(init_mm); /* will be removed in 2.6.26 */ -EXPORT_SYMBOL_GPL(init_mm); /* * Initial thread structure. diff --git a/arch/x86/kernel/mmiotrace/Makefile b/arch/x86/kernel/mmiotrace/Makefile index d6905f7f981b..cf1e747b463e 100644 --- a/arch/x86/kernel/mmiotrace/Makefile +++ b/arch/x86/kernel/mmiotrace/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_MMIOTRACE) += mmiotrace.o -mmiotrace-objs := pf_in.o kmmio.o mmio-mod.o - -obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o +obj-$(CONFIG_MMIOTRACE_HOOKS) += kmmio.o +obj-$(CONFIG_MMIOTRACE) += mmiotrace.o +mmiotrace-objs := pf_in.o mmio-mod.o +obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o diff --git a/arch/x86/kernel/mmiotrace/kmmio.c b/arch/x86/kernel/mmiotrace/kmmio.c index 5e239d0b8467..539a9b19588f 100644 --- a/arch/x86/kernel/mmiotrace/kmmio.c +++ b/arch/x86/kernel/mmiotrace/kmmio.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -17,70 +18,119 @@ #include #include #include +#include #include #include #include #include #include -#include "kmmio.h" +#include -#define KMMIO_HASH_BITS 6 -#define KMMIO_TABLE_SIZE (1 << KMMIO_HASH_BITS) #define KMMIO_PAGE_HASH_BITS 4 #define KMMIO_PAGE_TABLE_SIZE (1 << KMMIO_PAGE_HASH_BITS) +struct kmmio_fault_page { + struct list_head list; + struct kmmio_fault_page *release_next; + unsigned long page; /* location of the fault page */ + + /* + * Number of times this page has been registered as a part + * of a probe. If zero, page is disarmed and this may be freed. + * Used only by writers (RCU). + */ + int count; +}; + +struct kmmio_delayed_release { + struct rcu_head rcu; + struct kmmio_fault_page *release_list; +}; + struct kmmio_context { struct kmmio_fault_page *fpage; struct kmmio_probe *probe; unsigned long saved_flags; + unsigned long addr; int active; }; -static int kmmio_page_fault(struct pt_regs *regs, unsigned long error_code, - unsigned long address); static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, void *args); +static DECLARE_MUTEX(kmmio_init_mutex); static DEFINE_SPINLOCK(kmmio_lock); /* These are protected by kmmio_lock */ +static int kmmio_initialized; unsigned int kmmio_count; -static unsigned int handler_registered; + +/* Read-protected by RCU, write-protected by kmmio_lock. */ static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE]; static LIST_HEAD(kmmio_probes); +static struct list_head *kmmio_page_list(unsigned long page) +{ + return &kmmio_page_table[hash_long(page, KMMIO_PAGE_HASH_BITS)]; +} + /* Accessed per-cpu */ static DEFINE_PER_CPU(struct kmmio_context, kmmio_ctx); +/* protected by kmmio_init_mutex */ static struct notifier_block nb_die = { .notifier_call = kmmio_die_notifier }; -int init_kmmio(void) +/** + * Makes sure kmmio is initialized and usable. + * This must be called before any other kmmio function defined here. + * May sleep. + */ +void reference_kmmio(void) { - int i; - for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++) - INIT_LIST_HEAD(&kmmio_page_table[i]); - - register_die_notifier(&nb_die); - return 0; + down(&kmmio_init_mutex); + spin_lock_irq(&kmmio_lock); + if (!kmmio_initialized) { + int i; + for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++) + INIT_LIST_HEAD(&kmmio_page_table[i]); + if (register_die_notifier(&nb_die)) + BUG(); + } + kmmio_initialized++; + spin_unlock_irq(&kmmio_lock); + up(&kmmio_init_mutex); } +EXPORT_SYMBOL_GPL(reference_kmmio); -void cleanup_kmmio(void) +/** + * Clean up kmmio after use. This must be called for every call to + * reference_kmmio(). All probes registered after the corresponding + * reference_kmmio() must have been unregistered when calling this. + * May sleep. + */ +void unreference_kmmio(void) { - /* - * Assume the following have been already cleaned by calling - * unregister_kmmio_probe() appropriately: - * kmmio_page_table, kmmio_probes - */ - if (handler_registered) { - if (mmiotrace_unregister_pf(&kmmio_page_fault)) - BUG(); - synchronize_rcu(); + bool unreg = false; + + down(&kmmio_init_mutex); + spin_lock_irq(&kmmio_lock); + + if (kmmio_initialized == 1) { + BUG_ON(is_kmmio_active()); + unreg = true; } - unregister_die_notifier(&nb_die); + kmmio_initialized--; + BUG_ON(kmmio_initialized < 0); + spin_unlock_irq(&kmmio_lock); + + if (unreg) + unregister_die_notifier(&nb_die); /* calls sync_rcu() */ + up(&kmmio_init_mutex); } +EXPORT_SYMBOL(unreference_kmmio); /* * this is basically a dynamic stabbing problem: @@ -90,33 +140,33 @@ void cleanup_kmmio(void) * Overlap a Point (might be simple) * Space Efficient Dynamic Stabbing with Fast Queries - Mikkel Thorup */ -/* Get the kmmio at this addr (if any). You must be holding kmmio_lock. */ +/* Get the kmmio at this addr (if any). You must be holding RCU read lock. */ static struct kmmio_probe *get_kmmio_probe(unsigned long addr) { struct kmmio_probe *p; - list_for_each_entry(p, &kmmio_probes, list) { + list_for_each_entry_rcu(p, &kmmio_probes, list) { if (addr >= p->addr && addr <= (p->addr + p->len)) return p; } return NULL; } +/* You must be holding RCU read lock. */ static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page) { - struct list_head *head, *tmp; + struct list_head *head; + struct kmmio_fault_page *p; page &= PAGE_MASK; - head = &kmmio_page_table[hash_long(page, KMMIO_PAGE_HASH_BITS)]; - list_for_each(tmp, head) { - struct kmmio_fault_page *p - = list_entry(tmp, struct kmmio_fault_page, list); + head = kmmio_page_list(page); + list_for_each_entry_rcu(p, head, list) { if (p->page == page) return p; } - return NULL; } +/** Mark the given page as not present. Access to it will trigger a fault. */ static void arm_kmmio_fault_page(unsigned long page, int *page_level) { unsigned long address = page & PAGE_MASK; @@ -124,8 +174,8 @@ static void arm_kmmio_fault_page(unsigned long page, int *page_level) pte_t *pte = lookup_address(address, &level); if (!pte) { - printk(KERN_ERR "Error in %s: no pte for page 0x%08lx\n", - __FUNCTION__, page); + pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n", + __func__, page); return; } @@ -143,6 +193,7 @@ static void arm_kmmio_fault_page(unsigned long page, int *page_level) __flush_tlb_one(page); } +/** Mark the given page as present. */ static void disarm_kmmio_fault_page(unsigned long page, int *page_level) { unsigned long address = page & PAGE_MASK; @@ -150,8 +201,8 @@ static void disarm_kmmio_fault_page(unsigned long page, int *page_level) pte_t *pte = lookup_address(address, &level); if (!pte) { - printk(KERN_ERR "Error in %s: no pte for page 0x%08lx\n", - __FUNCTION__, page); + pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n", + __func__, page); return; } @@ -169,13 +220,25 @@ static void disarm_kmmio_fault_page(unsigned long page, int *page_level) __flush_tlb_one(page); } +/* + * This is being called from do_page_fault(). + * + * We may be in an interrupt or a critical section. Also prefecthing may + * trigger a page fault. We may be in the middle of process switch. + * We cannot take any locks, because we could be executing especially + * within a kmmio critical section. + * + * Local interrupts are disabled, so preemption cannot happen. + * Do not enable interrupts, do not sleep, and watch out for other CPUs. + */ /* * Interrupts are disabled on entry as trap3 is an interrupt gate * and they remain disabled thorough out this function. */ -static int kmmio_handler(struct pt_regs *regs, unsigned long addr) +int kmmio_handler(struct pt_regs *regs, unsigned long addr) { - struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx); + struct kmmio_context *ctx; + struct kmmio_fault_page *faultpage; /* * Preemption is now disabled to prevent process switch during @@ -186,40 +249,40 @@ static int kmmio_handler(struct pt_regs *regs, unsigned long addr) * XXX what if an interrupt occurs between returning from * do_page_fault() and entering the single-step exception handler? * And that interrupt triggers a kmmio trap? + * XXX If we tracing an interrupt service routine or whatever, is + * this enough to keep it on the current cpu? */ preempt_disable(); - /* interrupts disabled and CPU-local data => atomicity guaranteed. */ + rcu_read_lock(); + faultpage = get_kmmio_fault_page(addr); + if (!faultpage) { + /* + * Either this page fault is not caused by kmmio, or + * another CPU just pulled the kmmio probe from under + * our feet. In the latter case all hell breaks loose. + */ + goto no_kmmio; + } + + ctx = &get_cpu_var(kmmio_ctx); if (ctx->active) { /* - * This avoids a deadlock with kmmio_lock. + * Prevent overwriting already in-flight context. * If this page fault really was due to kmmio trap, * all hell breaks loose. */ - printk(KERN_EMERG "mmiotrace: recursive probe hit on CPU %d, " - "for address %lu. Ignoring.\n", + pr_emerg("kmmio: recursive probe hit on CPU %d, " + "for address 0x%08lx. Ignoring.\n", smp_processor_id(), addr); - goto no_kmmio; + goto no_kmmio_ctx; } ctx->active++; - /* - * Acquire the kmmio lock to prevent changes affecting - * get_kmmio_fault_page() and get_kmmio_probe(), since we save their - * returned pointers. - * The lock is released in post_kmmio_handler(). - * XXX: could/should get_kmmio_*() be using RCU instead of spinlock? - */ - spin_lock(&kmmio_lock); - - ctx->fpage = get_kmmio_fault_page(addr); - if (!ctx->fpage) { - /* this page fault is not caused by kmmio */ - goto no_kmmio_locked; - } - + ctx->fpage = faultpage; ctx->probe = get_kmmio_probe(addr); ctx->saved_flags = (regs->flags & (TF_MASK|IF_MASK)); + ctx->addr = addr; if (ctx->probe && ctx->probe->pre_handler) ctx->probe->pre_handler(ctx->probe, regs, addr); @@ -227,46 +290,62 @@ static int kmmio_handler(struct pt_regs *regs, unsigned long addr) regs->flags |= TF_MASK; regs->flags &= ~IF_MASK; - /* We hold lock, now we set present bit in PTE and single step. */ + /* Now we set present bit in PTE and single step. */ disarm_kmmio_fault_page(ctx->fpage->page, NULL); put_cpu_var(kmmio_ctx); + rcu_read_unlock(); return 1; -no_kmmio_locked: - spin_unlock(&kmmio_lock); - ctx->active--; +no_kmmio_ctx: + put_cpu_var(kmmio_ctx); no_kmmio: + rcu_read_unlock(); preempt_enable_no_resched(); - put_cpu_var(kmmio_ctx); - /* page fault not handled by kmmio */ - return 0; + return 0; /* page fault not handled by kmmio */ } /* * Interrupts are disabled on entry as trap1 is an interrupt gate * and they remain disabled thorough out this function. - * And we hold kmmio lock. + * This must always get called as the pair to kmmio_handler(). */ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) { int ret = 0; + struct kmmio_probe *probe; + struct kmmio_fault_page *faultpage; struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx); if (!ctx->active) goto out; + rcu_read_lock(); + + faultpage = get_kmmio_fault_page(ctx->addr); + probe = get_kmmio_probe(ctx->addr); + if (faultpage != ctx->fpage || probe != ctx->probe) { + /* + * The trace setup changed after kmmio_handler() and before + * running this respective post handler. User does not want + * the result anymore. + */ + ctx->probe = NULL; + ctx->fpage = NULL; + } + if (ctx->probe && ctx->probe->post_handler) ctx->probe->post_handler(ctx->probe, condition, regs); - arm_kmmio_fault_page(ctx->fpage->page, NULL); + if (ctx->fpage) + arm_kmmio_fault_page(ctx->fpage->page, NULL); regs->flags &= ~TF_MASK; regs->flags |= ctx->saved_flags; /* These were acquired in kmmio_handler(). */ ctx->active--; - spin_unlock(&kmmio_lock); + BUG_ON(ctx->active); preempt_enable_no_resched(); /* @@ -277,11 +356,13 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) if (!(regs->flags & TF_MASK)) ret = 1; + rcu_read_unlock(); out: put_cpu_var(kmmio_ctx); return ret; } +/* You must be holding kmmio_lock. */ static int add_kmmio_fault_page(unsigned long page) { struct kmmio_fault_page *f; @@ -289,6 +370,8 @@ static int add_kmmio_fault_page(unsigned long page) page &= PAGE_MASK; f = get_kmmio_fault_page(page); if (f) { + if (!f->count) + arm_kmmio_fault_page(f->page, NULL); f->count++; return 0; } @@ -299,15 +382,16 @@ static int add_kmmio_fault_page(unsigned long page) f->count = 1; f->page = page; - list_add(&f->list, - &kmmio_page_table[hash_long(f->page, KMMIO_PAGE_HASH_BITS)]); + list_add_rcu(&f->list, kmmio_page_list(f->page)); arm_kmmio_fault_page(f->page, NULL); return 0; } -static void release_kmmio_fault_page(unsigned long page) +/* You must be holding kmmio_lock. */ +static void release_kmmio_fault_page(unsigned long page, + struct kmmio_fault_page **release_list) { struct kmmio_fault_page *f; @@ -317,9 +401,11 @@ static void release_kmmio_fault_page(unsigned long page) return; f->count--; + BUG_ON(f->count < 0); if (!f->count) { disarm_kmmio_fault_page(f->page, NULL); - list_del(&f->list); + f->release_next = *release_list; + *release_list = f; } } @@ -334,68 +420,113 @@ int register_kmmio_probe(struct kmmio_probe *p) ret = -EEXIST; goto out; } - list_add(&p->list, &kmmio_probes); - /*printk("adding fault pages...\n");*/ + list_add_rcu(&p->list, &kmmio_probes); while (size < p->len) { if (add_kmmio_fault_page(p->addr + size)) - printk(KERN_ERR "mmio: Unable to set page fault.\n"); + pr_err("kmmio: Unable to set page fault.\n"); size += PAGE_SIZE; } - - if (!handler_registered) { - if (mmiotrace_register_pf(&kmmio_page_fault)) - printk(KERN_ERR "mmiotrace: Cannot register page " - "fault handler.\n"); - else - handler_registered++; - } - out: spin_unlock_irq(&kmmio_lock); /* * XXX: What should I do here? * Here was a call to global_flush_tlb(), but it does not exist - * anymore. + * anymore. It seems it's not needed after all. */ return ret; } +EXPORT_SYMBOL(register_kmmio_probe); +static void rcu_free_kmmio_fault_pages(struct rcu_head *head) +{ + struct kmmio_delayed_release *dr = container_of( + head, + struct kmmio_delayed_release, + rcu); + struct kmmio_fault_page *p = dr->release_list; + while (p) { + struct kmmio_fault_page *next = p->release_next; + BUG_ON(p->count); + kfree(p); + p = next; + } + kfree(dr); +} + +static void remove_kmmio_fault_pages(struct rcu_head *head) +{ + struct kmmio_delayed_release *dr = container_of( + head, + struct kmmio_delayed_release, + rcu); + struct kmmio_fault_page *p = dr->release_list; + struct kmmio_fault_page **prevp = &dr->release_list; + unsigned long flags; + spin_lock_irqsave(&kmmio_lock, flags); + while (p) { + if (!p->count) + list_del_rcu(&p->list); + else + *prevp = p->release_next; + prevp = &p->release_next; + p = p->release_next; + } + spin_unlock_irqrestore(&kmmio_lock, flags); + /* This is the real RCU destroy call. */ + call_rcu(&dr->rcu, rcu_free_kmmio_fault_pages); +} + +/* + * Remove a kmmio probe. You have to synchronize_rcu() before you can be + * sure that the callbacks will not be called anymore. + * + * Unregistering a kmmio fault page has three steps: + * 1. release_kmmio_fault_page() + * Disarm the page, wait a grace period to let all faults finish. + * 2. remove_kmmio_fault_pages() + * Remove the pages from kmmio_page_table. + * 3. rcu_free_kmmio_fault_pages() + * Actally free the kmmio_fault_page structs as with RCU. + */ void unregister_kmmio_probe(struct kmmio_probe *p) { unsigned long size = 0; + struct kmmio_fault_page *release_list = NULL; + struct kmmio_delayed_release *drelease; spin_lock_irq(&kmmio_lock); while (size < p->len) { - release_kmmio_fault_page(p->addr + size); + release_kmmio_fault_page(p->addr + size, &release_list); size += PAGE_SIZE; } - list_del(&p->list); + list_del_rcu(&p->list); kmmio_count--; spin_unlock_irq(&kmmio_lock); -} -/* - * According to 2.6.20, mainly x86_64 arch: - * This is being called from do_page_fault(), via the page fault notifier - * chain. The chain is called for both user space faults and kernel space - * faults (address >= TASK_SIZE64), except not on faults serviced by - * vmalloc_fault(). - * - * We may be in an interrupt or a critical section. Also prefecthing may - * trigger a page fault. We may be in the middle of process switch. - * The page fault hook functionality has put us inside RCU read lock. - * - * Local interrupts are disabled, so preemption cannot happen. - * Do not enable interrupts, do not sleep, and watch out for other CPUs. - */ -static int kmmio_page_fault(struct pt_regs *regs, unsigned long error_code, - unsigned long address) -{ - if (is_kmmio_active()) - if (kmmio_handler(regs, address) == 1) - return -1; - return 0; + drelease = kmalloc(sizeof(*drelease), GFP_ATOMIC); + if (!drelease) { + pr_crit("kmmio: leaking kmmio_fault_page objects.\n"); + return; + } + drelease->release_list = release_list; + + /* + * This is not really RCU here. We have just disarmed a set of + * pages so that they cannot trigger page faults anymore. However, + * we cannot remove the pages from kmmio_page_table, + * because a probe hit might be in flight on another CPU. The + * pages are collected into a list, and they will be removed from + * kmmio_page_table when it is certain that no probe hit related to + * these pages can be in flight. RCU grace period sounds like a + * good choice. + * + * If we removed the pages too early, kmmio page fault handler might + * not find the respective kmmio_fault_page and determine it's not + * a kmmio fault, when it actually is. This would lead to madness. + */ + call_rcu(&drelease->rcu, remove_kmmio_fault_pages); } +EXPORT_SYMBOL(unregister_kmmio_probe); static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, void *args) diff --git a/arch/x86/kernel/mmiotrace/kmmio.h b/arch/x86/kernel/mmiotrace/kmmio.h deleted file mode 100644 index 85b7f68a3b8a..000000000000 --- a/arch/x86/kernel/mmiotrace/kmmio.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _LINUX_KMMIO_H -#define _LINUX_KMMIO_H - -#include -#include -#include -#include -#include -#include -#include - -struct kmmio_probe; -struct kmmio_fault_page; -struct pt_regs; - -typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *, - struct pt_regs *, unsigned long addr); -typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, - unsigned long condition, struct pt_regs *); - -struct kmmio_probe { - struct list_head list; - - /* start location of the probe point */ - unsigned long addr; - - /* length of the probe region */ - unsigned long len; - - /* Called before addr is executed. */ - kmmio_pre_handler_t pre_handler; - - /* Called after addr is executed, unless... */ - kmmio_post_handler_t post_handler; -}; - -struct kmmio_fault_page { - struct list_head list; - - /* location of the fault page */ - unsigned long page; - - int count; -}; - -/* kmmio is active by some kmmio_probes? */ -static inline int is_kmmio_active(void) -{ - extern unsigned int kmmio_count; - return kmmio_count; -} - -int init_kmmio(void); -void cleanup_kmmio(void); -int register_kmmio_probe(struct kmmio_probe *p); -void unregister_kmmio_probe(struct kmmio_probe *p); - -#endif /* _LINUX_KMMIO_H */ diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c index f9c609266d83..e1a508588f03 100644 --- a/arch/x86/kernel/mmiotrace/mmio-mod.c +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c @@ -32,7 +32,6 @@ #include #include -#include "kmmio.h" #include "pf_in.h" /* This app's relay channel files will appear in /debug/mmio-trace */ @@ -129,18 +128,17 @@ static void print_pte(unsigned long address) pte_t *pte = lookup_address(address, &level); if (!pte) { - printk(KERN_ERR "Error in %s: no pte for page 0x%08lx\n", - __FUNCTION__, address); + pr_err(MODULE_NAME ": Error in %s: no pte for page 0x%08lx\n", + __func__, address); return; } if (level == PG_LEVEL_2M) { - printk(KERN_EMERG MODULE_NAME ": 4MB pages are not " - "currently supported: %lx\n", - address); + pr_emerg(MODULE_NAME ": 4MB pages are not currently " + "supported: %lx\n", address); BUG(); } - printk(KERN_DEBUG MODULE_NAME ": pte for 0x%lx: 0x%lx 0x%lx\n", + pr_info(MODULE_NAME ": pte for 0x%lx: 0x%lx 0x%lx\n", address, pte_val(*pte), pte_val(*pte) & _PAGE_PRESENT); } @@ -152,7 +150,7 @@ static void print_pte(unsigned long address) static void die_kmmio_nesting_error(struct pt_regs *regs, unsigned long addr) { const struct trap_reason *my_reason = &get_cpu_var(pf_reason); - printk(KERN_EMERG MODULE_NAME ": unexpected fault for address: %lx, " + pr_emerg(MODULE_NAME ": unexpected fault for address: %lx, " "last fault for address: %lx\n", addr, my_reason->addr); print_pte(addr); @@ -160,20 +158,17 @@ static void die_kmmio_nesting_error(struct pt_regs *regs, unsigned long addr) print_symbol(KERN_EMERG "faulting EIP is at %s\n", regs->ip); print_symbol(KERN_EMERG "last faulting EIP was at %s\n", my_reason->ip); - printk(KERN_EMERG - "eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", + pr_emerg("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", regs->ax, regs->bx, regs->cx, regs->dx); - printk(KERN_EMERG - "esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", + pr_emerg("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", regs->si, regs->di, regs->bp, regs->sp); #else print_symbol(KERN_EMERG "faulting RIP is at %s\n", regs->ip); print_symbol(KERN_EMERG "last faulting RIP was at %s\n", my_reason->ip); - printk(KERN_EMERG "rax: %016lx rcx: %016lx rdx: %016lx\n", + pr_emerg("rax: %016lx rcx: %016lx rdx: %016lx\n", regs->ax, regs->cx, regs->dx); - printk(KERN_EMERG "rsi: %016lx rdi: %016lx " - "rbp: %016lx rsp: %016lx\n", + pr_emerg("rsi: %016lx rdi: %016lx rbp: %016lx rsp: %016lx\n", regs->si, regs->di, regs->bp, regs->sp); #endif put_cpu_var(pf_reason); @@ -251,10 +246,15 @@ static void post(struct kmmio_probe *p, unsigned long condition, struct trap_reason *my_reason = &get_cpu_var(pf_reason); struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); + /* + * XXX: This might not get called, if the probe is removed while + * trace hit is on flight. + */ + /* this should always return the active_trace count to 0 */ my_reason->active_traces--; if (my_reason->active_traces) { - printk(KERN_EMERG MODULE_NAME ": unexpected post handler"); + pr_emerg(MODULE_NAME ": unexpected post handler"); BUG(); } @@ -283,16 +283,15 @@ static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf, atomic_t *drop = &per_cpu(dropped, cpu); int count; if (relay_buf_full(buf)) { - if (atomic_inc_return(drop) == 1) { - printk(KERN_ERR MODULE_NAME ": cpu %d buffer full!\n", - cpu); - } + if (atomic_inc_return(drop) == 1) + pr_err(MODULE_NAME ": cpu %d buffer full!\n", cpu); return 0; - } else if ((count = atomic_read(drop))) { - printk(KERN_ERR MODULE_NAME - ": cpu %d buffer no longer full, " - "missed %d events.\n", - cpu, count); + } + count = atomic_read(drop); + if (count) { + pr_err(MODULE_NAME ": cpu %d buffer no longer full, " + "missed %d events.\n", + cpu, count); atomic_sub(count, drop); } @@ -407,8 +406,8 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, /* Don't trace the low PCI/ISA area, it's always mapped.. */ if (!ISA_trace && (offset < ISA_END_ADDRESS) && (offset + size > ISA_START_ADDRESS)) { - printk(KERN_NOTICE MODULE_NAME ": Ignoring map of low " - "PCI/ISA area (0x%lx-0x%lx)\n", + pr_notice(MODULE_NAME ": Ignoring map of low PCI/ISA area " + "(0x%lx-0x%lx)\n", offset, offset + size); return; } @@ -418,7 +417,7 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, void __iomem *ioremap_cache_trace(unsigned long offset, unsigned long size) { void __iomem *p = ioremap_cache(offset, size); - printk(KERN_DEBUG MODULE_NAME ": ioremap_cache(0x%lx, 0x%lx) = %p\n", + pr_debug(MODULE_NAME ": ioremap_cache(0x%lx, 0x%lx) = %p\n", offset, size, p); ioremap_trace_core(offset, size, p); return p; @@ -428,7 +427,7 @@ EXPORT_SYMBOL(ioremap_cache_trace); void __iomem *ioremap_nocache_trace(unsigned long offset, unsigned long size) { void __iomem *p = ioremap_nocache(offset, size); - printk(KERN_DEBUG MODULE_NAME ": ioremap_nocache(0x%lx, 0x%lx) = %p\n", + pr_debug(MODULE_NAME ": ioremap_nocache(0x%lx, 0x%lx) = %p\n", offset, size, p); ioremap_trace_core(offset, size, p); return p; @@ -455,7 +454,7 @@ void iounmap_trace(volatile void __iomem *addr) }; struct remap_trace *trace; struct remap_trace *tmp; - printk(KERN_DEBUG MODULE_NAME ": Unmapping %p.\n", addr); + pr_debug(MODULE_NAME ": Unmapping %p.\n", addr); record_timestamp(&event.header); spin_lock(&trace_list_lock); @@ -481,7 +480,7 @@ static void clear_trace_list(void) spin_lock(&trace_list_lock); list_for_each_entry_safe(trace, tmp, &trace_list, list) { - printk(KERN_WARNING MODULE_NAME ": purging non-iounmapped " + pr_warning(MODULE_NAME ": purging non-iounmapped " "trace @0x%08lx, size 0x%lx.\n", trace->probe.addr, trace->probe.len); if (!nommiotrace) @@ -500,39 +499,37 @@ static int __init init(void) dir = debugfs_create_dir(APP_DIR, NULL); if (!dir) { - printk(KERN_ERR MODULE_NAME - ": Couldn't create relay app directory.\n"); + pr_err(MODULE_NAME ": Couldn't create relay app directory.\n"); return -ENOMEM; } chan = create_channel(subbuf_size, n_subbufs); if (!chan) { debugfs_remove(dir); - printk(KERN_ERR MODULE_NAME - ": relay app channel creation failed\n"); + pr_err(MODULE_NAME ": relay app channel creation failed\n"); return -ENOMEM; } - init_kmmio(); + reference_kmmio(); proc_marker_file = create_proc_entry(MARKER_FILE, 0, NULL); if (proc_marker_file) proc_marker_file->write_proc = write_marker; - printk(KERN_DEBUG MODULE_NAME ": loaded.\n"); + pr_debug(MODULE_NAME ": loaded.\n"); if (nommiotrace) - printk(KERN_DEBUG MODULE_NAME ": MMIO tracing disabled.\n"); + pr_info(MODULE_NAME ": MMIO tracing disabled.\n"); if (ISA_trace) - printk(KERN_WARNING MODULE_NAME - ": Warning! low ISA range will be traced.\n"); + pr_warning(MODULE_NAME ": Warning! low ISA range will be " + "traced.\n"); return 0; } static void __exit cleanup(void) { - printk(KERN_DEBUG MODULE_NAME ": unload...\n"); + pr_debug(MODULE_NAME ": unload...\n"); clear_trace_list(); - cleanup_kmmio(); + unreference_kmmio(); remove_proc_entry(MARKER_FILE, NULL); destroy_channel(); if (dir) diff --git a/arch/x86/kernel/mmiotrace/pf_in.c b/arch/x86/kernel/mmiotrace/pf_in.c index 67ea520dde62..efa1911e20ca 100644 --- a/arch/x86/kernel/mmiotrace/pf_in.c +++ b/arch/x86/kernel/mmiotrace/pf_in.c @@ -19,7 +19,7 @@ * */ -/* $Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp $ +/* Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp * Copyright by Intel Crop., 2002 * Louis Zhuang (louis.zhuang@intel.com) * diff --git a/arch/x86/kernel/mmiotrace/testmmiotrace.c b/arch/x86/kernel/mmiotrace/testmmiotrace.c index 40e66b0e6480..5ecff578672b 100644 --- a/arch/x86/kernel/mmiotrace/testmmiotrace.c +++ b/arch/x86/kernel/mmiotrace/testmmiotrace.c @@ -41,8 +41,7 @@ static void do_test(void) { void __iomem *p = ioremap_nocache_trace(mmio_address, 0x4000); if (!p) { - printk(KERN_ERR MODULE_NAME ": could not ioremap IO memory, " - "aborting.\n"); + pr_err(MODULE_NAME ": could not ioremap, aborting.\n"); return; } do_write_test(p); @@ -53,14 +52,14 @@ static void do_test(void) static int __init init(void) { if (mmio_address == 0) { - printk(KERN_ERR MODULE_NAME ": you have to use the module " - "argument mmio_address.\n"); - printk(KERN_ERR MODULE_NAME ": DO NOT LOAD THIS MODULE UNLESS" + pr_err(MODULE_NAME ": you have to use the module argument " + "mmio_address.\n"); + pr_err(MODULE_NAME ": DO NOT LOAD THIS MODULE UNLESS" " YOU REALLY KNOW WHAT YOU ARE DOING!\n"); return -ENXIO; } - printk(KERN_WARNING MODULE_NAME ": WARNING: mapping 16 kB @ 0x%08lx " + pr_warning(MODULE_NAME ": WARNING: mapping 16 kB @ 0x%08lx " "in PCI address space, and writing " "rubbish in there.\n", mmio_address); do_test(); @@ -69,7 +68,7 @@ static int __init init(void) static void __exit cleanup(void) { - printk(KERN_DEBUG MODULE_NAME ": unloaded.\n"); + pr_debug(MODULE_NAME ": unloaded.\n"); } module_init(init); diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index e9a086a1a9ff..8c828a68d3b6 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -49,60 +50,14 @@ #define PF_RSVD (1<<3) #define PF_INSTR (1<<4) -#ifdef CONFIG_MMIOTRACE_HOOKS -static pf_handler_func mmiotrace_pf_handler; /* protected by RCU */ -static DEFINE_SPINLOCK(mmiotrace_handler_lock); - -int mmiotrace_register_pf(pf_handler_func new_pfh) -{ - int ret = 0; - unsigned long flags; - spin_lock_irqsave(&mmiotrace_handler_lock, flags); - if (mmiotrace_pf_handler) - ret = -EBUSY; - else - mmiotrace_pf_handler = new_pfh; - spin_unlock_irqrestore(&mmiotrace_handler_lock, flags); - return ret; -} -EXPORT_SYMBOL_GPL(mmiotrace_register_pf); - -/** - * mmiotrace_unregister_pf: - * The caller must ensure @old_pfh is not in use anymore before freeing it. - * This function does not guarantee it. The handler function pointer is - * protected by RCU, so you can do this by e.g. calling synchronize_rcu(). - */ -int mmiotrace_unregister_pf(pf_handler_func old_pfh) -{ - int ret = 0; - unsigned long flags; - spin_lock_irqsave(&mmiotrace_handler_lock, flags); - if (mmiotrace_pf_handler != old_pfh) - ret = -EPERM; - else - mmiotrace_pf_handler = NULL; - spin_unlock_irqrestore(&mmiotrace_handler_lock, flags); - return ret; -} -EXPORT_SYMBOL_GPL(mmiotrace_unregister_pf); -#endif /* CONFIG_MMIOTRACE_HOOKS */ - -/* returns non-zero if do_page_fault() should return */ -static inline int call_mmiotrace(struct pt_regs *regs, - unsigned long error_code, - unsigned long address) +static inline int kmmio_fault(struct pt_regs *regs, unsigned long addr) { #ifdef CONFIG_MMIOTRACE_HOOKS - int ret = 0; - rcu_read_lock(); - if (mmiotrace_pf_handler) - ret = mmiotrace_pf_handler(regs, error_code, address); - rcu_read_unlock(); - return ret; -#else - return 0; + if (unlikely(is_kmmio_active())) + if (kmmio_handler(regs, addr) == 1) + return -1; #endif + return 0; } static inline int notify_page_fault(struct pt_regs *regs) @@ -657,7 +612,7 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) if (notify_page_fault(regs)) return; - if (call_mmiotrace(regs, error_code, address)) + if (unlikely(kmmio_fault(regs, address))) return; /* diff --git a/include/asm-x86/kdebug.h b/include/asm-x86/kdebug.h index 7063281040da..96651bb59ba1 100644 --- a/include/asm-x86/kdebug.h +++ b/include/asm-x86/kdebug.h @@ -35,11 +35,4 @@ extern void show_regs(struct pt_regs *regs); extern unsigned long oops_begin(void); extern void oops_end(unsigned long, struct pt_regs *, int signr); -typedef int (*pf_handler_func)(struct pt_regs *regs, - unsigned long error_code, - unsigned long address); - -extern int mmiotrace_register_pf(pf_handler_func new_pfh); -extern int mmiotrace_unregister_pf(pf_handler_func old_pfh); - #endif diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index 6ec288f1fe24..d87a6cd8b686 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -3,6 +3,44 @@ #include +#ifdef __KERNEL__ + +#include + +struct kmmio_probe; +struct pt_regs; + +typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *, + struct pt_regs *, unsigned long addr); +typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, + unsigned long condition, struct pt_regs *); + +struct kmmio_probe { + struct list_head list; + unsigned long addr; /* start location of the probe point */ + unsigned long len; /* length of the probe region */ + kmmio_pre_handler_t pre_handler; /* Called before addr is executed. */ + kmmio_post_handler_t post_handler; /* Called after addr is executed */ +}; + +/* kmmio is active by some kmmio_probes? */ +static inline int is_kmmio_active(void) +{ + extern unsigned int kmmio_count; + return kmmio_count; +} + +extern void reference_kmmio(void); +extern void unreference_kmmio(void); +extern int register_kmmio_probe(struct kmmio_probe *p); +extern void unregister_kmmio_probe(struct kmmio_probe *p); + +/* Called from page fault handler. */ +extern int kmmio_handler(struct pt_regs *regs, unsigned long addr); + +#endif /* __KERNEL__ */ + + /* * If you change anything here, you must bump MMIO_VERSION. * This is the relay data format for user space. -- cgit v1.2.3 From d61fc44853f46fb002228b18aa5f30db21fcd4ac Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:57 +0200 Subject: x86: mmiotrace, preview 2 Kconfig.debug, Makefile and testmmiotrace.c style fixes. Use real mutex instead of mutex. Fix failure path in register probe func. kmmio: RCU read-locked over single stepping. Generate mapping id's. Make mmio-mod.c built-in and rewrite its locking. Add debugfs file to enable/disable mmiotracing. kmmio: use irqsave spinlocks. Lots of cleanups in mmio-mod.c Marker file moved from /proc into debugfs. Call mmiotrace entrypoints directly from ioremap.c. Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/Kconfig.debug | 20 +- arch/x86/kernel/mmiotrace/Makefile | 2 +- arch/x86/kernel/mmiotrace/kmmio.c | 72 +++--- arch/x86/kernel/mmiotrace/mmio-mod.c | 397 ++++++++++++++++++++---------- arch/x86/kernel/mmiotrace/testmmiotrace.c | 15 +- arch/x86/mm/ioremap.c | 9 +- include/linux/mmiotrace.h | 18 +- 7 files changed, 332 insertions(+), 201 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 9491c0ae03a3..aa0d6462b1fc 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -170,22 +170,19 @@ config IOMMU_LEAK config MMIOTRACE_HOOKS bool - default n config MMIOTRACE - tristate "Memory mapped IO tracing" + bool "Memory mapped IO tracing" depends on DEBUG_KERNEL && RELAY && DEBUG_FS select MMIOTRACE_HOOKS - default n + default y help - This will build a kernel module called mmiotrace. - Making this a built-in is heavily discouraged. - - Mmiotrace traces Memory Mapped I/O access and is meant for debugging - and reverse engineering. The kernel module offers wrapped - versions of the ioremap family of functions. The driver to be traced - must be modified to call these wrappers. A user space program is - required to collect the MMIO data. + Mmiotrace traces Memory Mapped I/O access and is meant for + debugging and reverse engineering. It is called from the ioremap + implementation and works via page faults. A user space program is + required to collect the MMIO data from debugfs files. + Tracing is disabled by default and can be enabled from a debugfs + file. See http://nouveau.freedesktop.org/wiki/MmioTrace If you are not helping to develop drivers, say N. @@ -193,7 +190,6 @@ config MMIOTRACE config MMIOTRACE_TEST tristate "Test module for mmiotrace" depends on MMIOTRACE && m - default n help This is a dumb module for testing mmiotrace. It is very dangerous as it will write garbage to IO memory starting at a given address. diff --git a/arch/x86/kernel/mmiotrace/Makefile b/arch/x86/kernel/mmiotrace/Makefile index cf1e747b463e..dbcd8d50fb8d 100644 --- a/arch/x86/kernel/mmiotrace/Makefile +++ b/arch/x86/kernel/mmiotrace/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_MMIOTRACE_HOOKS) += kmmio.o obj-$(CONFIG_MMIOTRACE) += mmiotrace.o -mmiotrace-objs := pf_in.o mmio-mod.o +mmiotrace-y := pf_in.o mmio-mod.o obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o diff --git a/arch/x86/kernel/mmiotrace/kmmio.c b/arch/x86/kernel/mmiotrace/kmmio.c index 539a9b19588f..efb467933087 100644 --- a/arch/x86/kernel/mmiotrace/kmmio.c +++ b/arch/x86/kernel/mmiotrace/kmmio.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,7 @@ struct kmmio_context { static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, void *args); -static DECLARE_MUTEX(kmmio_init_mutex); +static DEFINE_MUTEX(kmmio_init_mutex); static DEFINE_SPINLOCK(kmmio_lock); /* These are protected by kmmio_lock */ @@ -90,7 +91,7 @@ static struct notifier_block nb_die = { */ void reference_kmmio(void) { - down(&kmmio_init_mutex); + mutex_lock(&kmmio_init_mutex); spin_lock_irq(&kmmio_lock); if (!kmmio_initialized) { int i; @@ -101,7 +102,7 @@ void reference_kmmio(void) } kmmio_initialized++; spin_unlock_irq(&kmmio_lock); - up(&kmmio_init_mutex); + mutex_unlock(&kmmio_init_mutex); } EXPORT_SYMBOL_GPL(reference_kmmio); @@ -115,7 +116,7 @@ void unreference_kmmio(void) { bool unreg = false; - down(&kmmio_init_mutex); + mutex_lock(&kmmio_init_mutex); spin_lock_irq(&kmmio_lock); if (kmmio_initialized == 1) { @@ -128,7 +129,7 @@ void unreference_kmmio(void) if (unreg) unregister_die_notifier(&nb_die); /* calls sync_rcu() */ - up(&kmmio_init_mutex); + mutex_unlock(&kmmio_init_mutex); } EXPORT_SYMBOL(unreference_kmmio); @@ -244,17 +245,13 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr) * Preemption is now disabled to prevent process switch during * single stepping. We can only handle one active kmmio trace * per cpu, so ensure that we finish it before something else - * gets to run. - * - * XXX what if an interrupt occurs between returning from - * do_page_fault() and entering the single-step exception handler? - * And that interrupt triggers a kmmio trap? - * XXX If we tracing an interrupt service routine or whatever, is - * this enough to keep it on the current cpu? + * gets to run. We also hold the RCU read lock over single + * stepping to avoid looking up the probe and kmmio_fault_page + * again. */ preempt_disable(); - rcu_read_lock(); + faultpage = get_kmmio_fault_page(addr); if (!faultpage) { /* @@ -287,14 +284,24 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr) if (ctx->probe && ctx->probe->pre_handler) ctx->probe->pre_handler(ctx->probe, regs, addr); + /* + * Enable single-stepping and disable interrupts for the faulting + * context. Local interrupts must not get enabled during stepping. + */ regs->flags |= TF_MASK; regs->flags &= ~IF_MASK; /* Now we set present bit in PTE and single step. */ disarm_kmmio_fault_page(ctx->fpage->page, NULL); + /* + * If another cpu accesses the same page while we are stepping, + * the access will not be caught. It will simply succeed and the + * only downside is we lose the event. If this becomes a problem, + * the user should drop to single cpu before tracing. + */ + put_cpu_var(kmmio_ctx); - rcu_read_unlock(); return 1; no_kmmio_ctx: @@ -313,32 +320,15 @@ no_kmmio: static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) { int ret = 0; - struct kmmio_probe *probe; - struct kmmio_fault_page *faultpage; struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx); if (!ctx->active) goto out; - rcu_read_lock(); - - faultpage = get_kmmio_fault_page(ctx->addr); - probe = get_kmmio_probe(ctx->addr); - if (faultpage != ctx->fpage || probe != ctx->probe) { - /* - * The trace setup changed after kmmio_handler() and before - * running this respective post handler. User does not want - * the result anymore. - */ - ctx->probe = NULL; - ctx->fpage = NULL; - } - if (ctx->probe && ctx->probe->post_handler) ctx->probe->post_handler(ctx->probe, condition, regs); - if (ctx->fpage) - arm_kmmio_fault_page(ctx->fpage->page, NULL); + arm_kmmio_fault_page(ctx->fpage->page, NULL); regs->flags &= ~TF_MASK; regs->flags |= ctx->saved_flags; @@ -346,6 +336,7 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) /* These were acquired in kmmio_handler(). */ ctx->active--; BUG_ON(ctx->active); + rcu_read_unlock(); preempt_enable_no_resched(); /* @@ -355,8 +346,6 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) */ if (!(regs->flags & TF_MASK)) ret = 1; - - rcu_read_unlock(); out: put_cpu_var(kmmio_ctx); return ret; @@ -411,15 +400,16 @@ static void release_kmmio_fault_page(unsigned long page, int register_kmmio_probe(struct kmmio_probe *p) { + unsigned long flags; int ret = 0; unsigned long size = 0; - spin_lock_irq(&kmmio_lock); - kmmio_count++; + spin_lock_irqsave(&kmmio_lock, flags); if (get_kmmio_probe(p->addr)) { ret = -EEXIST; goto out; } + kmmio_count++; list_add_rcu(&p->list, &kmmio_probes); while (size < p->len) { if (add_kmmio_fault_page(p->addr + size)) @@ -427,7 +417,7 @@ int register_kmmio_probe(struct kmmio_probe *p) size += PAGE_SIZE; } out: - spin_unlock_irq(&kmmio_lock); + spin_unlock_irqrestore(&kmmio_lock, flags); /* * XXX: What should I do here? * Here was a call to global_flush_tlb(), but it does not exist @@ -478,7 +468,8 @@ static void remove_kmmio_fault_pages(struct rcu_head *head) /* * Remove a kmmio probe. You have to synchronize_rcu() before you can be - * sure that the callbacks will not be called anymore. + * sure that the callbacks will not be called anymore. Only after that + * you may actually release your struct kmmio_probe. * * Unregistering a kmmio fault page has three steps: * 1. release_kmmio_fault_page() @@ -490,18 +481,19 @@ static void remove_kmmio_fault_pages(struct rcu_head *head) */ void unregister_kmmio_probe(struct kmmio_probe *p) { + unsigned long flags; unsigned long size = 0; struct kmmio_fault_page *release_list = NULL; struct kmmio_delayed_release *drelease; - spin_lock_irq(&kmmio_lock); + spin_lock_irqsave(&kmmio_lock, flags); while (size < p->len) { release_kmmio_fault_page(p->addr + size, &release_list); size += PAGE_SIZE; } list_del_rcu(&p->list); kmmio_count--; - spin_unlock_irq(&kmmio_lock); + spin_unlock_irqrestore(&kmmio_lock, flags); drelease = kmalloc(sizeof(*drelease), GFP_ATOMIC); if (!drelease) { diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c index e1a508588f03..738644061e4e 100644 --- a/arch/x86/kernel/mmiotrace/mmio-mod.c +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c @@ -19,6 +19,8 @@ * * Derived from the read-mod example from relay-examples by Tom Zanussi. */ +#define DEBUG 1 + #include #include #include @@ -34,12 +36,12 @@ #include "pf_in.h" -/* This app's relay channel files will appear in /debug/mmio-trace */ -#define APP_DIR "mmio-trace" -/* the marker injection file in /proc */ -#define MARKER_FILE "mmio-marker" +#define NAME "mmiotrace: " -#define MODULE_NAME "mmiotrace" +/* This app's relay channel files will appear in /debug/mmio-trace */ +static const char APP_DIR[] = "mmio-trace"; +/* the marker injection file in /debug/APP_DIR */ +static const char MARKER_FILE[] = "mmio-marker"; struct trap_reason { unsigned long addr; @@ -48,6 +50,15 @@ struct trap_reason { int active_traces; }; +struct remap_trace { + struct list_head list; + struct kmmio_probe probe; + unsigned long phys; + unsigned long id; +}; + +static const size_t subbuf_size = 256*1024; + /* Accessed per-cpu. */ static DEFINE_PER_CPU(struct trap_reason, pf_reason); static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); @@ -55,33 +66,53 @@ static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); /* Access to this is not per-cpu. */ static DEFINE_PER_CPU(atomic_t, dropped); -static struct file_operations mmio_fops = { - .owner = THIS_MODULE, -}; +static struct dentry *dir; +static struct dentry *enabled_file; +static struct dentry *marker_file; -static const size_t subbuf_size = 256*1024; +static DEFINE_MUTEX(mmiotrace_mutex); +static DEFINE_SPINLOCK(trace_lock); +static atomic_t mmiotrace_enabled; +static LIST_HEAD(trace_list); /* struct remap_trace */ static struct rchan *chan; -static struct dentry *dir; -static struct proc_dir_entry *proc_marker_file; + +/* + * Locking in this file: + * - mmiotrace_mutex enforces enable/disable_mmiotrace() critical sections. + * - mmiotrace_enabled may be modified only when holding mmiotrace_mutex + * and trace_lock. + * - Routines depending on is_enabled() must take trace_lock. + * - trace_list users must hold trace_lock. + * - is_enabled() guarantees that chan is valid. + * - pre/post callbacks assume the effect of is_enabled() being true. + */ /* module parameters */ -static unsigned int n_subbufs = 32*4; -static unsigned long filter_offset; -static int nommiotrace; -static int ISA_trace; -static int trace_pc; +static unsigned int n_subbufs = 32*4; +static unsigned long filter_offset; +static int nommiotrace; +static int ISA_trace; +static int trace_pc; +static int enable_now; module_param(n_subbufs, uint, 0); module_param(filter_offset, ulong, 0); module_param(nommiotrace, bool, 0); module_param(ISA_trace, bool, 0); module_param(trace_pc, bool, 0); +module_param(enable_now, bool, 0); MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128."); MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions."); +MODULE_PARM_DESC(enable_now, "Start mmiotracing immediately on module load."); + +static bool is_enabled(void) +{ + return atomic_read(&mmiotrace_enabled); +} static void record_timestamp(struct mm_io_header *header) { @@ -93,15 +124,15 @@ static void record_timestamp(struct mm_io_header *header) } /* - * Write callback for the /proc entry: + * Write callback for the debugfs entry: * Read a marker and write it to the mmio trace log */ -static int write_marker(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t write_marker(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) { char *event = NULL; struct mm_io_header *headp; - int len = (count > 65535) ? 65535 : count; + ssize_t len = (count > 65535) ? 65535 : count; event = kzalloc(sizeof(*headp) + len, GFP_KERNEL); if (!event) @@ -117,7 +148,12 @@ static int write_marker(struct file *file, const char __user *buffer, return -EFAULT; } - relay_write(chan, event, sizeof(*headp) + len); + spin_lock_irq(&trace_lock); + if (is_enabled()) + relay_write(chan, event, sizeof(*headp) + len); + else + len = -EINVAL; + spin_unlock_irq(&trace_lock); kfree(event); return len; } @@ -128,19 +164,18 @@ static void print_pte(unsigned long address) pte_t *pte = lookup_address(address, &level); if (!pte) { - pr_err(MODULE_NAME ": Error in %s: no pte for page 0x%08lx\n", + pr_err(NAME "Error in %s: no pte for page 0x%08lx\n", __func__, address); return; } if (level == PG_LEVEL_2M) { - pr_emerg(MODULE_NAME ": 4MB pages are not currently " - "supported: %lx\n", address); + pr_emerg(NAME "4MB pages are not currently supported: " + "0x%08lx\n", address); BUG(); } - pr_info(MODULE_NAME ": pte for 0x%lx: 0x%lx 0x%lx\n", - address, pte_val(*pte), - pte_val(*pte) & _PAGE_PRESENT); + pr_info(NAME "pte for 0x%lx: 0x%lx 0x%lx\n", address, pte_val(*pte), + pte_val(*pte) & _PAGE_PRESENT); } /* @@ -150,22 +185,18 @@ static void print_pte(unsigned long address) static void die_kmmio_nesting_error(struct pt_regs *regs, unsigned long addr) { const struct trap_reason *my_reason = &get_cpu_var(pf_reason); - pr_emerg(MODULE_NAME ": unexpected fault for address: %lx, " - "last fault for address: %lx\n", + pr_emerg(NAME "unexpected fault for address: 0x%08lx, " + "last fault for address: 0x%08lx\n", addr, my_reason->addr); print_pte(addr); + print_symbol(KERN_EMERG "faulting IP is at %s\n", regs->ip); + print_symbol(KERN_EMERG "last faulting IP was at %s\n", my_reason->ip); #ifdef __i386__ - print_symbol(KERN_EMERG "faulting EIP is at %s\n", regs->ip); - print_symbol(KERN_EMERG "last faulting EIP was at %s\n", - my_reason->ip); pr_emerg("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", regs->ax, regs->bx, regs->cx, regs->dx); pr_emerg("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", regs->si, regs->di, regs->bp, regs->sp); #else - print_symbol(KERN_EMERG "faulting RIP is at %s\n", regs->ip); - print_symbol(KERN_EMERG "last faulting RIP was at %s\n", - my_reason->ip); pr_emerg("rax: %016lx rcx: %016lx rdx: %016lx\n", regs->ax, regs->cx, regs->dx); pr_emerg("rsi: %016lx rdi: %016lx rbp: %016lx rsp: %016lx\n", @@ -197,6 +228,10 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, my_trace->header.pid = 0; my_trace->header.data_len = sizeof(struct mm_io_rw); my_trace->rw.address = addr; + /* + * struct remap_trace *trace = p->user_data; + * phys = addr - trace->probe.addr + trace->phys; + */ /* * Only record the program counter when requested. @@ -246,15 +281,10 @@ static void post(struct kmmio_probe *p, unsigned long condition, struct trap_reason *my_reason = &get_cpu_var(pf_reason); struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); - /* - * XXX: This might not get called, if the probe is removed while - * trace hit is on flight. - */ - /* this should always return the active_trace count to 0 */ my_reason->active_traces--; if (my_reason->active_traces) { - pr_emerg(MODULE_NAME ": unexpected post handler"); + pr_emerg(NAME "unexpected post handler"); BUG(); } @@ -284,20 +314,23 @@ static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf, int count; if (relay_buf_full(buf)) { if (atomic_inc_return(drop) == 1) - pr_err(MODULE_NAME ": cpu %d buffer full!\n", cpu); + pr_err(NAME "cpu %d buffer full!\n", cpu); return 0; } count = atomic_read(drop); if (count) { - pr_err(MODULE_NAME ": cpu %d buffer no longer full, " - "missed %d events.\n", - cpu, count); + pr_err(NAME "cpu %d buffer no longer full, missed %d events.\n", + cpu, count); atomic_sub(count, drop); } return 1; } +static struct file_operations mmio_fops = { + .owner = THIS_MODULE, +}; + /* file_create() callback. Creates relay file in debugfs. */ static struct dentry *create_buf_file_handler(const char *filename, struct dentry *parent, @@ -333,34 +366,10 @@ static struct rchan_callbacks relay_callbacks = { .remove_buf_file = remove_buf_file_handler, }; -/* - * create_channel - creates channel /debug/APP_DIR/cpuXXX - * Returns channel on success, NULL otherwise - */ -static struct rchan *create_channel(unsigned size, unsigned n) -{ - return relay_open("cpu", dir, size, n, &relay_callbacks, NULL); -} - -/* destroy_channel - destroys channel /debug/APP_DIR/cpuXXX */ -static void destroy_channel(void) -{ - if (chan) { - relay_close(chan); - chan = NULL; - } -} - -struct remap_trace { - struct list_head list; - struct kmmio_probe probe; -}; -static LIST_HEAD(trace_list); -static DEFINE_SPINLOCK(trace_list_lock); - -static void do_ioremap_trace_core(unsigned long offset, unsigned long size, +static void ioremap_trace_core(unsigned long offset, unsigned long size, void __iomem *addr) { + static atomic_t next_id; struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL); struct mm_io_header_map event = { .header = { @@ -380,61 +389,49 @@ static void do_ioremap_trace_core(unsigned long offset, unsigned long size, }; record_timestamp(&event.header); + if (!trace) { + pr_err(NAME "kmalloc failed in ioremap\n"); + return; + } + *trace = (struct remap_trace) { .probe = { .addr = (unsigned long)addr, .len = size, .pre_handler = pre, .post_handler = post, - } + .user_data = trace + }, + .phys = offset, + .id = atomic_inc_return(&next_id) }; + spin_lock_irq(&trace_lock); + if (!is_enabled()) + goto not_enabled; + relay_write(chan, &event, sizeof(event)); - spin_lock(&trace_list_lock); list_add_tail(&trace->list, &trace_list); - spin_unlock(&trace_list_lock); if (!nommiotrace) register_kmmio_probe(&trace->probe); + +not_enabled: + spin_unlock_irq(&trace_lock); } -static void ioremap_trace_core(unsigned long offset, unsigned long size, - void __iomem *addr) +void +mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr) { - if ((filter_offset) && (offset != filter_offset)) + if (!is_enabled()) /* recheck and proper locking in *_core() */ return; - /* Don't trace the low PCI/ISA area, it's always mapped.. */ - if (!ISA_trace && (offset < ISA_END_ADDRESS) && - (offset + size > ISA_START_ADDRESS)) { - pr_notice(MODULE_NAME ": Ignoring map of low PCI/ISA area " - "(0x%lx-0x%lx)\n", - offset, offset + size); + pr_debug(NAME "ioremap_*(0x%lx, 0x%lx) = %p\n", offset, size, addr); + if ((filter_offset) && (offset != filter_offset)) return; - } - do_ioremap_trace_core(offset, size, addr); -} - -void __iomem *ioremap_cache_trace(unsigned long offset, unsigned long size) -{ - void __iomem *p = ioremap_cache(offset, size); - pr_debug(MODULE_NAME ": ioremap_cache(0x%lx, 0x%lx) = %p\n", - offset, size, p); - ioremap_trace_core(offset, size, p); - return p; + ioremap_trace_core(offset, size, addr); } -EXPORT_SYMBOL(ioremap_cache_trace); -void __iomem *ioremap_nocache_trace(unsigned long offset, unsigned long size) -{ - void __iomem *p = ioremap_nocache(offset, size); - pr_debug(MODULE_NAME ": ioremap_nocache(0x%lx, 0x%lx) = %p\n", - offset, size, p); - ioremap_trace_core(offset, size, p); - return p; -} -EXPORT_SYMBOL(ioremap_nocache_trace); - -void iounmap_trace(volatile void __iomem *addr) +static void iounmap_trace_core(volatile void __iomem *addr) { struct mm_io_header_map event = { .header = { @@ -454,84 +451,212 @@ void iounmap_trace(volatile void __iomem *addr) }; struct remap_trace *trace; struct remap_trace *tmp; - pr_debug(MODULE_NAME ": Unmapping %p.\n", addr); + struct remap_trace *found_trace = NULL; + + pr_debug(NAME "Unmapping %p.\n", addr); record_timestamp(&event.header); - spin_lock(&trace_list_lock); + spin_lock_irq(&trace_lock); + if (!is_enabled()) + goto not_enabled; + list_for_each_entry_safe(trace, tmp, &trace_list, list) { if ((unsigned long)addr == trace->probe.addr) { if (!nommiotrace) unregister_kmmio_probe(&trace->probe); list_del(&trace->list); - kfree(trace); + found_trace = trace; break; } } - spin_unlock(&trace_list_lock); relay_write(chan, &event, sizeof(event)); - iounmap(addr); + +not_enabled: + spin_unlock_irq(&trace_lock); + if (found_trace) { + synchronize_rcu(); /* unregister_kmmio_probe() requirement */ + kfree(found_trace); + } +} + +void mmiotrace_iounmap(volatile void __iomem *addr) +{ + might_sleep(); + if (is_enabled()) /* recheck and proper locking in *_core() */ + iounmap_trace_core(addr); } -EXPORT_SYMBOL(iounmap_trace); static void clear_trace_list(void) { struct remap_trace *trace; struct remap_trace *tmp; - spin_lock(&trace_list_lock); - list_for_each_entry_safe(trace, tmp, &trace_list, list) { - pr_warning(MODULE_NAME ": purging non-iounmapped " + /* + * No locking required, because the caller ensures we are in a + * critical section via mutex, and is_enabled() is false, + * i.e. nothing can traverse or modify this list. + * Caller also ensures is_enabled() cannot change. + */ + list_for_each_entry(trace, &trace_list, list) { + pr_notice(NAME "purging non-iounmapped " "trace @0x%08lx, size 0x%lx.\n", trace->probe.addr, trace->probe.len); if (!nommiotrace) unregister_kmmio_probe(&trace->probe); + } + synchronize_rcu(); /* unregister_kmmio_probe() requirement */ + + list_for_each_entry_safe(trace, tmp, &trace_list, list) { list_del(&trace->list); kfree(trace); + } +} + +static ssize_t read_enabled_file_bool(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[3]; + + if (is_enabled()) + buf[0] = '1'; + else + buf[0] = '0'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static void enable_mmiotrace(void); +static void disable_mmiotrace(void); + +static ssize_t write_enabled_file_bool(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[32]; + int buf_size = min(count, (sizeof(buf)-1)); + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + switch (buf[0]) { + case 'y': + case 'Y': + case '1': + enable_mmiotrace(); + break; + case 'n': + case 'N': + case '0': + disable_mmiotrace(); break; } - spin_unlock(&trace_list_lock); + + return count; +} + +/* this ripped from kernel/kprobes.c */ +static struct file_operations fops_enabled = { + .owner = THIS_MODULE, + .read = read_enabled_file_bool, + .write = write_enabled_file_bool +}; + +static struct file_operations fops_marker = { + .owner = THIS_MODULE, + .write = write_marker +}; + +static void enable_mmiotrace(void) +{ + mutex_lock(&mmiotrace_mutex); + if (is_enabled()) + goto out; + + chan = relay_open("cpu", dir, subbuf_size, n_subbufs, + &relay_callbacks, NULL); + if (!chan) { + pr_err(NAME "relay app channel creation failed.\n"); + goto out; + } + + reference_kmmio(); + + marker_file = debugfs_create_file("marker", 0660, dir, NULL, + &fops_marker); + if (!marker_file) + pr_err(NAME "marker file creation failed.\n"); + + if (nommiotrace) + pr_info(NAME "MMIO tracing disabled.\n"); + if (ISA_trace) + pr_warning(NAME "Warning! low ISA range will be traced.\n"); + spin_lock_irq(&trace_lock); + atomic_inc(&mmiotrace_enabled); + spin_unlock_irq(&trace_lock); + pr_info(NAME "enabled.\n"); +out: + mutex_unlock(&mmiotrace_mutex); +} + +static void disable_mmiotrace(void) +{ + mutex_lock(&mmiotrace_mutex); + if (!is_enabled()) + goto out; + + spin_lock_irq(&trace_lock); + atomic_dec(&mmiotrace_enabled); + BUG_ON(is_enabled()); + spin_unlock_irq(&trace_lock); + + clear_trace_list(); /* guarantees: no more kmmio callbacks */ + unreference_kmmio(); + if (marker_file) { + debugfs_remove(marker_file); + marker_file = NULL; + } + if (chan) { + relay_close(chan); + chan = NULL; + } + + pr_info(NAME "disabled.\n"); +out: + mutex_unlock(&mmiotrace_mutex); } static int __init init(void) { + pr_debug(NAME "load...\n"); if (n_subbufs < 2) return -EINVAL; dir = debugfs_create_dir(APP_DIR, NULL); if (!dir) { - pr_err(MODULE_NAME ": Couldn't create relay app directory.\n"); + pr_err(NAME "Couldn't create relay app directory.\n"); return -ENOMEM; } - chan = create_channel(subbuf_size, n_subbufs); - if (!chan) { + enabled_file = debugfs_create_file("enabled", 0600, dir, NULL, + &fops_enabled); + if (!enabled_file) { + pr_err(NAME "Couldn't create enabled file.\n"); debugfs_remove(dir); - pr_err(MODULE_NAME ": relay app channel creation failed\n"); return -ENOMEM; } - reference_kmmio(); - - proc_marker_file = create_proc_entry(MARKER_FILE, 0, NULL); - if (proc_marker_file) - proc_marker_file->write_proc = write_marker; + if (enable_now) + enable_mmiotrace(); - pr_debug(MODULE_NAME ": loaded.\n"); - if (nommiotrace) - pr_info(MODULE_NAME ": MMIO tracing disabled.\n"); - if (ISA_trace) - pr_warning(MODULE_NAME ": Warning! low ISA range will be " - "traced.\n"); return 0; } static void __exit cleanup(void) { - pr_debug(MODULE_NAME ": unload...\n"); - clear_trace_list(); - unreference_kmmio(); - remove_proc_entry(MARKER_FILE, NULL); - destroy_channel(); + pr_debug(NAME "unload...\n"); + if (enabled_file) + debugfs_remove(enabled_file); + disable_mmiotrace(); if (dir) debugfs_remove(dir); } diff --git a/arch/x86/kernel/mmiotrace/testmmiotrace.c b/arch/x86/kernel/mmiotrace/testmmiotrace.c index 5ecff578672b..cfa60b227c8d 100644 --- a/arch/x86/kernel/mmiotrace/testmmiotrace.c +++ b/arch/x86/kernel/mmiotrace/testmmiotrace.c @@ -4,10 +4,6 @@ #include #include -extern void __iomem *ioremap_nocache_trace(unsigned long offset, - unsigned long size); -extern void iounmap_trace(volatile void __iomem *addr); - #define MODULE_NAME "testmmiotrace" static unsigned long mmio_address; @@ -28,25 +24,24 @@ static void do_write_test(void __iomem *p) static void do_read_test(void __iomem *p) { unsigned int i; - volatile unsigned int v; for (i = 0; i < 256; i++) - v = ioread8(p + i); + ioread8(p + i); for (i = 1024; i < (5 * 1024); i += 2) - v = ioread16(p + i); + ioread16(p + i); for (i = (5 * 1024); i < (16 * 1024); i += 4) - v = ioread32(p + i); + ioread32(p + i); } static void do_test(void) { - void __iomem *p = ioremap_nocache_trace(mmio_address, 0x4000); + void __iomem *p = ioremap_nocache(mmio_address, 0x4000); if (!p) { pr_err(MODULE_NAME ": could not ioremap, aborting.\n"); return; } do_write_test(p); do_read_test(p); - iounmap_trace(p); + iounmap(p); } static int __init init(void) diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 71bb3159031a..8927c878544d 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,7 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, unsigned long new_prot_val; pgprot_t prot; int retval; + void __iomem *ret_addr; /* Don't allow wraparound or zero size */ last_addr = phys_addr + size - 1; @@ -233,7 +235,10 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, return NULL; } - return (void __iomem *) (vaddr + offset); + ret_addr = (void __iomem *) (vaddr + offset); + mmiotrace_ioremap(phys_addr, size, ret_addr); + + return ret_addr; } /** @@ -325,6 +330,8 @@ void iounmap(volatile void __iomem *addr) addr = (volatile void __iomem *) (PAGE_MASK & (unsigned long __force)addr); + mmiotrace_iounmap(addr); + /* Use the vm area unlocked, assuming the caller ensures there isn't another iounmap for the same address in parallel. Reuse of the virtual address is prevented by diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index d87a6cd8b686..cb5efd0c7f51 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -16,11 +16,12 @@ typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, unsigned long condition, struct pt_regs *); struct kmmio_probe { - struct list_head list; + struct list_head list; /* kmmio internal list */ unsigned long addr; /* start location of the probe point */ unsigned long len; /* length of the probe region */ kmmio_pre_handler_t pre_handler; /* Called before addr is executed. */ kmmio_post_handler_t post_handler; /* Called after addr is executed */ + void *user_data; }; /* kmmio is active by some kmmio_probes? */ @@ -38,6 +39,21 @@ extern void unregister_kmmio_probe(struct kmmio_probe *p); /* Called from page fault handler. */ extern int kmmio_handler(struct pt_regs *regs, unsigned long addr); +/* Called from ioremap.c */ +#ifdef CONFIG_MMIOTRACE +extern void +mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr); +extern void mmiotrace_iounmap(volatile void __iomem *addr); +#else +static inline void +mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr) +{ +} +static inline void mmiotrace_iounmap(volatile void __iomem *addr) +{ +} +#endif /* CONFIG_MMIOTRACE_HOOKS */ + #endif /* __KERNEL__ */ -- cgit v1.2.3 From f984b51e0779a6dd30feedc41404013ca54e5d05 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:57 +0200 Subject: ftrace: add mmiotrace plugin On Sat, 22 Mar 2008 13:07:47 +0100 Ingo Molnar wrote: > > > i'd suggest the following: pull x86.git and sched-devel.git into a > > > single tree [the two will combine without rejects]. Then try to add a > > > kernel/tracing/trace_mmiotrace.c ftrace plugin. The trace_sysprof.c > > > plugin might be a good example. > > > > I did this and now I have mmiotrace enabled/disabled via the tracing > > framework (what do we call this, since ftrace is one of the tracers?). > > cool! could you send the patches for that? (even if they are not fully > functional yet) Patch attached in the end. Nice to see how much code disappeared. I tried to mark all the features I had to break with XXX-comments. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/Kconfig.debug | 3 +- arch/x86/kernel/mmiotrace/mmio-mod.c | 208 +++++------------------------------ include/linux/mmiotrace.h | 6 + kernel/trace/Makefile | 1 + kernel/trace/trace_mmiotrace.c | 84 ++++++++++++++ 5 files changed, 123 insertions(+), 179 deletions(-) create mode 100644 kernel/trace/trace_mmiotrace.c (limited to 'include/linux') diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index aa0d6462b1fc..7e4b8494078e 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -173,7 +173,8 @@ config MMIOTRACE_HOOKS config MMIOTRACE bool "Memory mapped IO tracing" - depends on DEBUG_KERNEL && RELAY && DEBUG_FS + depends on DEBUG_KERNEL && RELAY + select TRACING select MMIOTRACE_HOOKS default y help diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c index 738644061e4e..c7a67d7e482b 100644 --- a/arch/x86/kernel/mmiotrace/mmio-mod.c +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c @@ -22,9 +22,8 @@ #define DEBUG 1 #include -#include #include -#include +#include #include #include #include @@ -63,18 +62,18 @@ static const size_t subbuf_size = 256*1024; static DEFINE_PER_CPU(struct trap_reason, pf_reason); static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); +#if 0 /* XXX: no way gather this info anymore */ /* Access to this is not per-cpu. */ static DEFINE_PER_CPU(atomic_t, dropped); +#endif static struct dentry *dir; -static struct dentry *enabled_file; static struct dentry *marker_file; static DEFINE_MUTEX(mmiotrace_mutex); static DEFINE_SPINLOCK(trace_lock); static atomic_t mmiotrace_enabled; static LIST_HEAD(trace_list); /* struct remap_trace */ -static struct rchan *chan; /* * Locking in this file: @@ -93,36 +92,24 @@ static unsigned long filter_offset; static int nommiotrace; static int ISA_trace; static int trace_pc; -static int enable_now; module_param(n_subbufs, uint, 0); module_param(filter_offset, ulong, 0); module_param(nommiotrace, bool, 0); module_param(ISA_trace, bool, 0); module_param(trace_pc, bool, 0); -module_param(enable_now, bool, 0); MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128."); MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions."); -MODULE_PARM_DESC(enable_now, "Start mmiotracing immediately on module load."); static bool is_enabled(void) { return atomic_read(&mmiotrace_enabled); } -static void record_timestamp(struct mm_io_header *header) -{ - struct timespec now; - - getnstimeofday(&now); - header->sec = now.tv_sec; - header->nsec = now.tv_nsec; -} - /* * Write callback for the debugfs entry: * Read a marker and write it to the mmio trace log @@ -141,7 +128,6 @@ static ssize_t write_marker(struct file *file, const char __user *buffer, headp = (struct mm_io_header *)event; headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT); headp->data_len = len; - record_timestamp(headp); if (copy_from_user(event + sizeof(*headp), buffer, len)) { kfree(event); @@ -149,9 +135,11 @@ static ssize_t write_marker(struct file *file, const char __user *buffer, } spin_lock_irq(&trace_lock); +#if 0 /* XXX: convert this to use tracing */ if (is_enabled()) relay_write(chan, event, sizeof(*headp) + len); else +#endif len = -EINVAL; spin_unlock_irq(&trace_lock); kfree(event); @@ -242,7 +230,11 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, else my_trace->rw.pc = 0; - record_timestamp(&my_trace->header); + /* + * XXX: the timestamp recorded will be *after* the tracing has been + * done, not at the time we hit the instruction. SMP implications + * on event ordering? + */ switch (type) { case REG_READ: @@ -295,77 +287,19 @@ static void post(struct kmmio_probe *p, unsigned long condition, default: break; } - relay_write(chan, my_trace, sizeof(*my_trace)); + + /* + * XXX: Several required values are ignored: + * - mapping id + * - program counter + * Also the address should be physical, not virtual. + */ + mmio_trace_record(my_trace->header.type, my_trace->rw.address, + my_trace->rw.value); put_cpu_var(cpu_trace); put_cpu_var(pf_reason); } -/* - * subbuf_start() relay callback. - * - * Defined so that we know when events are dropped due to the buffer-full - * condition. - */ -static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf, - void *prev_subbuf, size_t prev_padding) -{ - unsigned int cpu = buf->cpu; - atomic_t *drop = &per_cpu(dropped, cpu); - int count; - if (relay_buf_full(buf)) { - if (atomic_inc_return(drop) == 1) - pr_err(NAME "cpu %d buffer full!\n", cpu); - return 0; - } - count = atomic_read(drop); - if (count) { - pr_err(NAME "cpu %d buffer no longer full, missed %d events.\n", - cpu, count); - atomic_sub(count, drop); - } - - return 1; -} - -static struct file_operations mmio_fops = { - .owner = THIS_MODULE, -}; - -/* file_create() callback. Creates relay file in debugfs. */ -static struct dentry *create_buf_file_handler(const char *filename, - struct dentry *parent, - int mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *buf_file; - - mmio_fops.read = relay_file_operations.read; - mmio_fops.open = relay_file_operations.open; - mmio_fops.poll = relay_file_operations.poll; - mmio_fops.mmap = relay_file_operations.mmap; - mmio_fops.release = relay_file_operations.release; - mmio_fops.splice_read = relay_file_operations.splice_read; - - buf_file = debugfs_create_file(filename, mode, parent, buf, - &mmio_fops); - - return buf_file; -} - -/* file_remove() default callback. Removes relay file in debugfs. */ -static int remove_buf_file_handler(struct dentry *dentry) -{ - debugfs_remove(dentry); - return 0; -} - -static struct rchan_callbacks relay_callbacks = { - .subbuf_start = subbuf_start_handler, - .create_buf_file = create_buf_file_handler, - .remove_buf_file = remove_buf_file_handler, -}; - static void ioremap_trace_core(unsigned long offset, unsigned long size, void __iomem *addr) { @@ -387,7 +321,6 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, .pc = 0 } }; - record_timestamp(&event.header); if (!trace) { pr_err(NAME "kmalloc failed in ioremap\n"); @@ -410,7 +343,10 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, if (!is_enabled()) goto not_enabled; - relay_write(chan, &event, sizeof(event)); + /* + * XXX: Insufficient data recorded! + */ + mmio_trace_record(event.header.type, event.map.addr, event.map.len); list_add_tail(&trace->list, &trace_list); if (!nommiotrace) register_kmmio_probe(&trace->probe); @@ -454,7 +390,6 @@ static void iounmap_trace_core(volatile void __iomem *addr) struct remap_trace *found_trace = NULL; pr_debug(NAME "Unmapping %p.\n", addr); - record_timestamp(&event.header); spin_lock_irq(&trace_lock); if (!is_enabled()) @@ -469,7 +404,8 @@ static void iounmap_trace_core(volatile void __iomem *addr) break; } } - relay_write(chan, &event, sizeof(event)); + mmio_trace_record(event.header.type, event.map.addr, + found_trace ? found_trace->id : -1); not_enabled: spin_unlock_irq(&trace_lock); @@ -512,77 +448,23 @@ static void clear_trace_list(void) } } -static ssize_t read_enabled_file_bool(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char buf[3]; - - if (is_enabled()) - buf[0] = '1'; - else - buf[0] = '0'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static void enable_mmiotrace(void); -static void disable_mmiotrace(void); - -static ssize_t write_enabled_file_bool(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - char buf[32]; - int buf_size = min(count, (sizeof(buf)-1)); - - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - switch (buf[0]) { - case 'y': - case 'Y': - case '1': - enable_mmiotrace(); - break; - case 'n': - case 'N': - case '0': - disable_mmiotrace(); - break; - } - - return count; -} - -/* this ripped from kernel/kprobes.c */ -static struct file_operations fops_enabled = { - .owner = THIS_MODULE, - .read = read_enabled_file_bool, - .write = write_enabled_file_bool -}; - static struct file_operations fops_marker = { .owner = THIS_MODULE, .write = write_marker }; -static void enable_mmiotrace(void) +void enable_mmiotrace(void) { mutex_lock(&mmiotrace_mutex); if (is_enabled()) goto out; - chan = relay_open("cpu", dir, subbuf_size, n_subbufs, - &relay_callbacks, NULL); - if (!chan) { - pr_err(NAME "relay app channel creation failed.\n"); - goto out; - } - reference_kmmio(); +#if 0 /* XXX: tracing does not support text entries */ marker_file = debugfs_create_file("marker", 0660, dir, NULL, &fops_marker); +#endif if (!marker_file) pr_err(NAME "marker file creation failed.\n"); @@ -598,7 +480,7 @@ out: mutex_unlock(&mmiotrace_mutex); } -static void disable_mmiotrace(void) +void disable_mmiotrace(void) { mutex_lock(&mmiotrace_mutex); if (!is_enabled()) @@ -615,17 +497,13 @@ static void disable_mmiotrace(void) debugfs_remove(marker_file); marker_file = NULL; } - if (chan) { - relay_close(chan); - chan = NULL; - } pr_info(NAME "disabled.\n"); out: mutex_unlock(&mmiotrace_mutex); } -static int __init init(void) +int __init init_mmiotrace(void) { pr_debug(NAME "load...\n"); if (n_subbufs < 2) @@ -636,31 +514,5 @@ static int __init init(void) pr_err(NAME "Couldn't create relay app directory.\n"); return -ENOMEM; } - - enabled_file = debugfs_create_file("enabled", 0600, dir, NULL, - &fops_enabled); - if (!enabled_file) { - pr_err(NAME "Couldn't create enabled file.\n"); - debugfs_remove(dir); - return -ENOMEM; - } - - if (enable_now) - enable_mmiotrace(); - return 0; } - -static void __exit cleanup(void) -{ - pr_debug(NAME "unload...\n"); - if (enabled_file) - debugfs_remove(enabled_file); - disable_mmiotrace(); - if (dir) - debugfs_remove(dir); -} - -module_init(init); -module_exit(cleanup); -MODULE_LICENSE("GPL"); diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index cb5efd0c7f51..579b3b06c90e 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -54,6 +54,12 @@ static inline void mmiotrace_iounmap(volatile void __iomem *addr) } #endif /* CONFIG_MMIOTRACE_HOOKS */ +/* in kernel/trace/trace_mmiotrace.c */ +extern int __init init_mmiotrace(void); +extern void enable_mmiotrace(void); +extern void disable_mmiotrace(void); +extern void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg); + #endif /* __KERNEL__ */ diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index d9efbbfa2bdf..c44a7dce9086 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_FTRACE) += trace_functions.o obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o +obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o libftrace-y := ftrace.o diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c new file mode 100644 index 000000000000..e4dd03cc5aa6 --- /dev/null +++ b/kernel/trace/trace_mmiotrace.c @@ -0,0 +1,84 @@ +/* + * Memory mapped I/O tracing + * + * Copyright (C) 2008 Pekka Paalanen + */ + +#define DEBUG 1 + +#include +#include + +#include "trace.h" + +extern void +__trace_special(void *__tr, void *__data, + unsigned long arg1, unsigned long arg2, unsigned long arg3); + +static struct trace_array *mmio_trace_array; + + +static void mmio_trace_init(struct trace_array *tr) +{ + pr_debug("in %s\n", __func__); + mmio_trace_array = tr; + if (tr->ctrl) + enable_mmiotrace(); +} + +static void mmio_trace_reset(struct trace_array *tr) +{ + pr_debug("in %s\n", __func__); + if (tr->ctrl) + disable_mmiotrace(); +} + +static void mmio_trace_ctrl_update(struct trace_array *tr) +{ + pr_debug("in %s\n", __func__); + if (tr->ctrl) + enable_mmiotrace(); + else + disable_mmiotrace(); +} + +static struct tracer mmio_tracer __read_mostly = +{ + .name = "mmiotrace", + .init = mmio_trace_init, + .reset = mmio_trace_reset, + .ctrl_update = mmio_trace_ctrl_update, +}; + +__init static int init_mmio_trace(void) +{ + int ret = init_mmiotrace(); + if (ret) + return ret; + return register_tracer(&mmio_tracer); +} +device_initcall(init_mmio_trace); + +void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg) +{ + struct trace_array *tr = mmio_trace_array; + struct trace_array_cpu *data = tr->data[smp_processor_id()]; + + if (!current || current->pid == 0) { + /* + * XXX: This is a problem. We need to able to record, no + * matter what. tracing_generic_entry_update() would crash. + */ + static unsigned limit; + if (limit++ < 12) + pr_err("Error in %s: no current.\n", __func__); + return; + } + if (!tr || !data) { + static unsigned limit; + if (limit++ < 12) + pr_err("%s: no tr or data\n", __func__); + return; + } + __trace_special(tr, data, type, addr, arg); +} -- cgit v1.2.3 From bd8ac686c73c7e925fcfe0b02dc4e7b947127864 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:57 +0200 Subject: ftrace: mmiotrace, updates here is a patch that makes mmiotrace work almost well within the tracing framework. The patch applies on top of my previous patch. I have my own output formatting in place now. Summary of changes: - fix the NULL dereference that was due to not calling tracing_reset() - add print_line() callback into struct tracer - implement print_line() for mmiotrace, producing up-to-spec text - add my output header, but that is not really called in the right place - rewrote the main structs in mmiotrace - added two new trace entry types: TRACE_MMIO_RW and TRACE_MMIO_MAP - made some functions in trace.c non-static - check current==NULL in tracing_generic_entry_update() - fix(?) comparison in trace_seq_printf() Things seem to work fine except a few issues. Markers (text lines injected into mmiotrace log) are missing, I did not feel hacking them in before we have variable length entries. My output header is printed only for 'trace' file, but not 'trace_pipe'. For some reason, despite my quick fix, iter->trace is NULL in print_trace_line() when called from 'trace_pipe' file, which means I don't get proper output formatting. I only tried by loading nouveau.ko, which just detects the card, and that is traced fine. I didn't try further. Map, two reads and unmap. Works perfectly. I am missing the information about overflows, I'd prefer to have a counter for lost events. I didn't try, but I guess currently there is no way of knowning when it overflows? So, not too far from being fully operational, it seems :-) And looking at the diffstat, there also is some 700-900 lines of user space code that just became obsolete. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/Kconfig.debug | 2 +- arch/x86/kernel/mmiotrace/mmio-mod.c | 140 ++++++++++---------------------- include/linux/mmiotrace.h | 85 ++++++-------------- kernel/trace/trace.c | 34 ++++++++ kernel/trace/trace.h | 14 ++++ kernel/trace/trace_mmiotrace.c | 151 ++++++++++++++++++++++++++++------- 6 files changed, 238 insertions(+), 188 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 7e4b8494078e..1d6de0d67f99 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -173,7 +173,7 @@ config MMIOTRACE_HOOKS config MMIOTRACE bool "Memory mapped IO tracing" - depends on DEBUG_KERNEL && RELAY + depends on DEBUG_KERNEL select TRACING select MMIOTRACE_HOOKS default y diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c index c7a67d7e482b..62abc281a512 100644 --- a/arch/x86/kernel/mmiotrace/mmio-mod.c +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c @@ -37,11 +37,6 @@ #define NAME "mmiotrace: " -/* This app's relay channel files will appear in /debug/mmio-trace */ -static const char APP_DIR[] = "mmio-trace"; -/* the marker injection file in /debug/APP_DIR */ -static const char MARKER_FILE[] = "mmio-marker"; - struct trap_reason { unsigned long addr; unsigned long ip; @@ -56,18 +51,15 @@ struct remap_trace { unsigned long id; }; -static const size_t subbuf_size = 256*1024; - /* Accessed per-cpu. */ static DEFINE_PER_CPU(struct trap_reason, pf_reason); -static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); +static DEFINE_PER_CPU(struct mmiotrace_rw, cpu_trace); #if 0 /* XXX: no way gather this info anymore */ /* Access to this is not per-cpu. */ static DEFINE_PER_CPU(atomic_t, dropped); #endif -static struct dentry *dir; static struct dentry *marker_file; static DEFINE_MUTEX(mmiotrace_mutex); @@ -82,24 +74,21 @@ static LIST_HEAD(trace_list); /* struct remap_trace */ * and trace_lock. * - Routines depending on is_enabled() must take trace_lock. * - trace_list users must hold trace_lock. - * - is_enabled() guarantees that chan is valid. + * - is_enabled() guarantees that mmio_trace_record is allowed. * - pre/post callbacks assume the effect of is_enabled() being true. */ /* module parameters */ -static unsigned int n_subbufs = 32*4; static unsigned long filter_offset; static int nommiotrace; static int ISA_trace; static int trace_pc; -module_param(n_subbufs, uint, 0); module_param(filter_offset, ulong, 0); module_param(nommiotrace, bool, 0); module_param(ISA_trace, bool, 0); module_param(trace_pc, bool, 0); -MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128."); MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); @@ -110,6 +99,7 @@ static bool is_enabled(void) return atomic_read(&mmiotrace_enabled); } +#if 0 /* XXX: needs rewrite */ /* * Write callback for the debugfs entry: * Read a marker and write it to the mmio trace log @@ -145,6 +135,7 @@ static ssize_t write_marker(struct file *file, const char __user *buffer, kfree(event); return len; } +#endif static void print_pte(unsigned long address) { @@ -198,9 +189,10 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, unsigned long addr) { struct trap_reason *my_reason = &get_cpu_var(pf_reason); - struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); + struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace); const unsigned long instptr = instruction_pointer(regs); const enum reason_type type = get_ins_type(instptr); + struct remap_trace *trace = p->user_data; /* it doesn't make sense to have more than one active trace per cpu */ if (my_reason->active_traces) @@ -212,23 +204,17 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, my_reason->addr = addr; my_reason->ip = instptr; - my_trace->header.type = MMIO_MAGIC; - my_trace->header.pid = 0; - my_trace->header.data_len = sizeof(struct mm_io_rw); - my_trace->rw.address = addr; - /* - * struct remap_trace *trace = p->user_data; - * phys = addr - trace->probe.addr + trace->phys; - */ + my_trace->phys = addr - trace->probe.addr + trace->phys; + my_trace->map_id = trace->id; /* * Only record the program counter when requested. * It may taint clean-room reverse engineering. */ if (trace_pc) - my_trace->rw.pc = instptr; + my_trace->pc = instptr; else - my_trace->rw.pc = 0; + my_trace->pc = 0; /* * XXX: the timestamp recorded will be *after* the tracing has been @@ -238,28 +224,25 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, switch (type) { case REG_READ: - my_trace->header.type |= - (MMIO_READ << MMIO_OPCODE_SHIFT) | - (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); + my_trace->opcode = MMIO_READ; + my_trace->width = get_ins_mem_width(instptr); break; case REG_WRITE: - my_trace->header.type |= - (MMIO_WRITE << MMIO_OPCODE_SHIFT) | - (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); - my_trace->rw.value = get_ins_reg_val(instptr, regs); + my_trace->opcode = MMIO_WRITE; + my_trace->width = get_ins_mem_width(instptr); + my_trace->value = get_ins_reg_val(instptr, regs); break; case IMM_WRITE: - my_trace->header.type |= - (MMIO_WRITE << MMIO_OPCODE_SHIFT) | - (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); - my_trace->rw.value = get_ins_imm_val(instptr); + my_trace->opcode = MMIO_WRITE; + my_trace->width = get_ins_mem_width(instptr); + my_trace->value = get_ins_imm_val(instptr); break; default: { unsigned char *ip = (unsigned char *)instptr; - my_trace->header.type |= - (MMIO_UNKNOWN_OP << MMIO_OPCODE_SHIFT); - my_trace->rw.value = (*ip) << 16 | *(ip + 1) << 8 | + my_trace->opcode = MMIO_UNKNOWN_OP; + my_trace->width = 0; + my_trace->value = (*ip) << 16 | *(ip + 1) << 8 | *(ip + 2); } } @@ -271,7 +254,7 @@ static void post(struct kmmio_probe *p, unsigned long condition, struct pt_regs *regs) { struct trap_reason *my_reason = &get_cpu_var(pf_reason); - struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); + struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace); /* this should always return the active_trace count to 0 */ my_reason->active_traces--; @@ -282,20 +265,13 @@ static void post(struct kmmio_probe *p, unsigned long condition, switch (my_reason->type) { case REG_READ: - my_trace->rw.value = get_ins_reg_val(my_reason->ip, regs); + my_trace->value = get_ins_reg_val(my_reason->ip, regs); break; default: break; } - /* - * XXX: Several required values are ignored: - * - mapping id - * - program counter - * Also the address should be physical, not virtual. - */ - mmio_trace_record(my_trace->header.type, my_trace->rw.address, - my_trace->rw.value); + mmio_trace_rw(my_trace); put_cpu_var(cpu_trace); put_cpu_var(pf_reason); } @@ -305,21 +281,11 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, { static atomic_t next_id; struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL); - struct mm_io_header_map event = { - .header = { - .type = MMIO_MAGIC | - (MMIO_PROBE << MMIO_OPCODE_SHIFT), - .sec = 0, - .nsec = 0, - .pid = 0, - .data_len = sizeof(struct mm_io_map) - }, - .map = { - .phys = offset, - .addr = (unsigned long)addr, - .len = size, - .pc = 0 - } + struct mmiotrace_map map = { + .phys = offset, + .virt = (unsigned long)addr, + .len = size, + .opcode = MMIO_PROBE }; if (!trace) { @@ -338,15 +304,13 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, .phys = offset, .id = atomic_inc_return(&next_id) }; + map.map_id = trace->id; spin_lock_irq(&trace_lock); if (!is_enabled()) goto not_enabled; - /* - * XXX: Insufficient data recorded! - */ - mmio_trace_record(event.header.type, event.map.addr, event.map.len); + mmio_trace_mapping(&map); list_add_tail(&trace->list, &trace_list); if (!nommiotrace) register_kmmio_probe(&trace->probe); @@ -369,21 +333,11 @@ mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr) static void iounmap_trace_core(volatile void __iomem *addr) { - struct mm_io_header_map event = { - .header = { - .type = MMIO_MAGIC | - (MMIO_UNPROBE << MMIO_OPCODE_SHIFT), - .sec = 0, - .nsec = 0, - .pid = 0, - .data_len = sizeof(struct mm_io_map) - }, - .map = { - .phys = 0, - .addr = (unsigned long)addr, - .len = 0, - .pc = 0 - } + struct mmiotrace_map map = { + .phys = 0, + .virt = (unsigned long)addr, + .len = 0, + .opcode = MMIO_UNPROBE }; struct remap_trace *trace; struct remap_trace *tmp; @@ -404,8 +358,8 @@ static void iounmap_trace_core(volatile void __iomem *addr) break; } } - mmio_trace_record(event.header.type, event.map.addr, - found_trace ? found_trace->id : -1); + map.map_id = (found_trace) ? found_trace->id : -1; + mmio_trace_mapping(&map); not_enabled: spin_unlock_irq(&trace_lock); @@ -448,10 +402,12 @@ static void clear_trace_list(void) } } +#if 0 /* XXX: out of order */ static struct file_operations fops_marker = { .owner = THIS_MODULE, .write = write_marker }; +#endif void enable_mmiotrace(void) { @@ -464,9 +420,9 @@ void enable_mmiotrace(void) #if 0 /* XXX: tracing does not support text entries */ marker_file = debugfs_create_file("marker", 0660, dir, NULL, &fops_marker); -#endif if (!marker_file) pr_err(NAME "marker file creation failed.\n"); +#endif if (nommiotrace) pr_info(NAME "MMIO tracing disabled.\n"); @@ -502,17 +458,3 @@ void disable_mmiotrace(void) out: mutex_unlock(&mmiotrace_mutex); } - -int __init init_mmiotrace(void) -{ - pr_debug(NAME "load...\n"); - if (n_subbufs < 2) - return -EINVAL; - - dir = debugfs_create_dir(APP_DIR, NULL); - if (!dir) { - pr_err(NAME "Couldn't create relay app directory.\n"); - return -ENOMEM; - } - return 0; -} diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index 579b3b06c90e..c88a9c197d22 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -54,73 +54,38 @@ static inline void mmiotrace_iounmap(volatile void __iomem *addr) } #endif /* CONFIG_MMIOTRACE_HOOKS */ -/* in kernel/trace/trace_mmiotrace.c */ -extern int __init init_mmiotrace(void); -extern void enable_mmiotrace(void); -extern void disable_mmiotrace(void); -extern void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg); - -#endif /* __KERNEL__ */ - - -/* - * If you change anything here, you must bump MMIO_VERSION. - * This is the relay data format for user space. - */ -#define MMIO_VERSION 0x04 - -/* mm_io_header.type */ -#define MMIO_OPCODE_MASK 0xff -#define MMIO_OPCODE_SHIFT 0 -#define MMIO_WIDTH_MASK 0xff00 -#define MMIO_WIDTH_SHIFT 8 -#define MMIO_MAGIC (0x6f000000 | (MMIO_VERSION<<16)) -#define MMIO_MAGIC_MASK 0xffff0000 - -enum mm_io_opcode { /* payload type: */ - MMIO_READ = 0x1, /* struct mm_io_rw */ - MMIO_WRITE = 0x2, /* struct mm_io_rw */ - MMIO_PROBE = 0x3, /* struct mm_io_map */ - MMIO_UNPROBE = 0x4, /* struct mm_io_map */ +enum mm_io_opcode { + MMIO_READ = 0x1, /* struct mmiotrace_rw */ + MMIO_WRITE = 0x2, /* struct mmiotrace_rw */ + MMIO_PROBE = 0x3, /* struct mmiotrace_map */ + MMIO_UNPROBE = 0x4, /* struct mmiotrace_map */ MMIO_MARKER = 0x5, /* raw char data */ - MMIO_UNKNOWN_OP = 0x6, /* struct mm_io_rw */ + MMIO_UNKNOWN_OP = 0x6, /* struct mmiotrace_rw */ }; -struct mm_io_header { - __u32 type; /* see MMIO_* macros above */ - __u32 sec; /* timestamp */ - __u32 nsec; - __u32 pid; /* PID of the process, or 0 for kernel core */ - __u16 data_len; /* length of the following payload */ +struct mmiotrace_rw { + unsigned long phys; /* PCI address of register */ + unsigned long value; + unsigned long pc; /* optional program counter */ + int map_id; + unsigned char opcode; /* one of MMIO_{READ,WRITE,UNKNOWN_OP} */ + unsigned char width; /* size of register access in bytes */ }; -struct mm_io_rw { - __u64 address; /* virtual address of register */ - __u64 value; - __u64 pc; /* optional program counter */ +struct mmiotrace_map { + unsigned long phys; /* base address in PCI space */ + unsigned long virt; /* base virtual address */ + unsigned long len; /* mapping size */ + int map_id; + unsigned char opcode; /* MMIO_PROBE or MMIO_UNPROBE */ }; -struct mm_io_map { - __u64 phys; /* base address in PCI space */ - __u64 addr; /* base virtual address */ - __u64 len; /* mapping size */ - __u64 pc; /* optional program counter */ -}; - - -/* - * These structures are used to allow a single relay_write() - * call to write a full packet. - */ - -struct mm_io_header_rw { - struct mm_io_header header; - struct mm_io_rw rw; -} __attribute__((packed)); +/* in kernel/trace/trace_mmiotrace.c */ +extern void enable_mmiotrace(void); +extern void disable_mmiotrace(void); +extern void mmio_trace_rw(struct mmiotrace_rw *rw); +extern void mmio_trace_mapping(struct mmiotrace_map *map); -struct mm_io_header_map { - struct mm_io_header header; - struct mm_io_map map; -} __attribute__((packed)); +#endif /* __KERNEL__ */ #endif /* MMIOTRACE_H */ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3271916ff033..d14fe49e9638 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -831,6 +831,40 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, trace_function(tr, data, ip, parent_ip, flags); } +#ifdef CONFIG_MMIOTRACE +void __trace_mmiotrace_rw(struct trace_array *tr, struct trace_array_cpu *data, + struct mmiotrace_rw *rw) +{ + struct trace_entry *entry; + unsigned long irq_flags; + + spin_lock_irqsave(&data->lock, irq_flags); + entry = tracing_get_trace_entry(tr, data); + tracing_generic_entry_update(entry, 0); + entry->type = TRACE_MMIO_RW; + entry->mmiorw = *rw; + spin_unlock_irqrestore(&data->lock, irq_flags); + + trace_wake_up(); +} + +void __trace_mmiotrace_map(struct trace_array *tr, struct trace_array_cpu *data, + struct mmiotrace_map *map) +{ + struct trace_entry *entry; + unsigned long irq_flags; + + spin_lock_irqsave(&data->lock, irq_flags); + entry = tracing_get_trace_entry(tr, data); + tracing_generic_entry_update(entry, 0); + entry->type = TRACE_MMIO_MAP; + entry->mmiomap = *map; + spin_unlock_irqrestore(&data->lock, irq_flags); + + trace_wake_up(); +} +#endif + void __trace_stack(struct trace_array *tr, struct trace_array_cpu *data, unsigned long flags, diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index c460e85e94ed..0ef9ef74c806 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -5,6 +5,7 @@ #include #include #include +#include enum trace_type { __TRACE_FIRST_TYPE = 0, @@ -14,6 +15,8 @@ enum trace_type { TRACE_WAKE, TRACE_STACK, TRACE_SPECIAL, + TRACE_MMIO_RW, + TRACE_MMIO_MAP, __TRACE_LAST_TYPE }; @@ -75,6 +78,8 @@ struct trace_entry { struct ctx_switch_entry ctx; struct special_entry special; struct stack_entry stack; + struct mmiotrace_rw mmiorw; + struct mmiotrace_map mmiomap; }; }; @@ -255,6 +260,15 @@ extern unsigned long ftrace_update_tot_cnt; extern int DYN_FTRACE_TEST_NAME(void); #endif +#ifdef CONFIG_MMIOTRACE +extern void __trace_mmiotrace_rw(struct trace_array *tr, + struct trace_array_cpu *data, + struct mmiotrace_rw *rw); +extern void __trace_mmiotrace_map(struct trace_array *tr, + struct trace_array_cpu *data, + struct mmiotrace_map *map); +#endif + #ifdef CONFIG_FTRACE_STARTUP_TEST #ifdef CONFIG_FTRACE extern int trace_selftest_startup_function(struct tracer *trace, diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index e4dd03cc5aa6..3a12b1ad0c63 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -11,19 +11,26 @@ #include "trace.h" -extern void -__trace_special(void *__tr, void *__data, - unsigned long arg1, unsigned long arg2, unsigned long arg3); - static struct trace_array *mmio_trace_array; +static void mmio_reset_data(struct trace_array *tr) +{ + int cpu; + + tr->time_start = ftrace_now(tr->cpu); + + for_each_online_cpu(cpu) + tracing_reset(tr->data[cpu]); +} static void mmio_trace_init(struct trace_array *tr) { pr_debug("in %s\n", __func__); mmio_trace_array = tr; - if (tr->ctrl) + if (tr->ctrl) { + mmio_reset_data(tr); enable_mmiotrace(); + } } static void mmio_trace_reset(struct trace_array *tr) @@ -31,15 +38,110 @@ static void mmio_trace_reset(struct trace_array *tr) pr_debug("in %s\n", __func__); if (tr->ctrl) disable_mmiotrace(); + mmio_reset_data(tr); + mmio_trace_array = NULL; } static void mmio_trace_ctrl_update(struct trace_array *tr) { pr_debug("in %s\n", __func__); - if (tr->ctrl) + if (tr->ctrl) { + mmio_reset_data(tr); enable_mmiotrace(); - else + } else { disable_mmiotrace(); + } +} + +/* XXX: This is not called for trace_pipe file! */ +void mmio_print_header(struct trace_iterator *iter) +{ + struct trace_seq *s = &iter->seq; + trace_seq_printf(s, "VERSION broken 20070824\n"); + /* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */ +} + +static int mmio_print_rw(struct trace_iterator *iter) +{ + struct trace_entry *entry = iter->ent; + struct mmiotrace_rw *rw = &entry->mmiorw; + struct trace_seq *s = &iter->seq; + unsigned long long t = ns2usecs(entry->t); + unsigned long usec_rem = do_div(t, 1000000ULL); + unsigned secs = (unsigned long)t; + int ret = 1; + + switch (entry->mmiorw.opcode) { + case MMIO_READ: + ret = trace_seq_printf(s, + "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", + rw->width, secs, usec_rem, rw->map_id, rw->phys, + rw->value, rw->pc, entry->pid); + break; + case MMIO_WRITE: + ret = trace_seq_printf(s, + "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", + rw->width, secs, usec_rem, rw->map_id, rw->phys, + rw->value, rw->pc, entry->pid); + break; + case MMIO_UNKNOWN_OP: + ret = trace_seq_printf(s, + "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n", + secs, usec_rem, rw->map_id, rw->phys, + (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, + (rw->value >> 0) & 0xff, rw->pc, entry->pid); + break; + default: + ret = trace_seq_printf(s, "rw what?\n"); + break; + } + if (ret) + return 1; + return 0; +} + +static int mmio_print_map(struct trace_iterator *iter) +{ + struct trace_entry *entry = iter->ent; + struct mmiotrace_map *m = &entry->mmiomap; + struct trace_seq *s = &iter->seq; + unsigned long long t = ns2usecs(entry->t); + unsigned long usec_rem = do_div(t, 1000000ULL); + unsigned secs = (unsigned long)t; + int ret = 1; + + switch (entry->mmiorw.opcode) { + case MMIO_PROBE: + ret = trace_seq_printf(s, + "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n", + secs, usec_rem, m->map_id, m->phys, m->virt, m->len, + 0UL, entry->pid); + break; + case MMIO_UNPROBE: + ret = trace_seq_printf(s, + "UNMAP %lu.%06lu %d 0x%lx %d\n", + secs, usec_rem, m->map_id, 0UL, entry->pid); + break; + default: + ret = trace_seq_printf(s, "map what?\n"); + break; + } + if (ret) + return 1; + return 0; +} + +/* return 0 to abort printing without consuming current entry in pipe mode */ +static int mmio_print_line(struct trace_iterator *iter) +{ + switch (iter->ent->type) { + case TRACE_MMIO_RW: + return mmio_print_rw(iter); + case TRACE_MMIO_MAP: + return mmio_print_map(iter); + default: + return 1; /* ignore unknown entries */ + } } static struct tracer mmio_tracer __read_mostly = @@ -47,38 +149,31 @@ static struct tracer mmio_tracer __read_mostly = .name = "mmiotrace", .init = mmio_trace_init, .reset = mmio_trace_reset, + .open = mmio_print_header, .ctrl_update = mmio_trace_ctrl_update, + .print_line = mmio_print_line, }; __init static int init_mmio_trace(void) { - int ret = init_mmiotrace(); - if (ret) - return ret; return register_tracer(&mmio_tracer); } device_initcall(init_mmio_trace); -void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg) +void mmio_trace_rw(struct mmiotrace_rw *rw) { struct trace_array *tr = mmio_trace_array; struct trace_array_cpu *data = tr->data[smp_processor_id()]; + __trace_mmiotrace_rw(tr, data, rw); +} - if (!current || current->pid == 0) { - /* - * XXX: This is a problem. We need to able to record, no - * matter what. tracing_generic_entry_update() would crash. - */ - static unsigned limit; - if (limit++ < 12) - pr_err("Error in %s: no current.\n", __func__); - return; - } - if (!tr || !data) { - static unsigned limit; - if (limit++ < 12) - pr_err("%s: no tr or data\n", __func__); - return; - } - __trace_special(tr, data, type, addr, arg); +void mmio_trace_mapping(struct mmiotrace_map *map) +{ + struct trace_array *tr = mmio_trace_array; + struct trace_array_cpu *data; + + preempt_disable(); + data = tr->data[smp_processor_id()]; + __trace_mmiotrace_map(tr, data, map); + preempt_enable(); } -- cgit v1.2.3 From 138295373ccf7625fcb0218dfea114837983bc39 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:20:58 +0200 Subject: ftrace: mmiotrace update, #2 another weekend, another patch. This should apply on top of my previous patch from March 23rd. Summary of changes: - Print PCI device list in output header - work around recursive probe hits on SMP - refactor dis/arm_kmmio_fault_page() and add check for page levels - remove un/reference_kmmio(), the die notifier hook is registered permanently into the list - explicitly check for single stepping in die notifier callback I have tested this version on my UP Athlon64 desktop with Nouveau, and SMP Core 2 Duo laptop with the proprietary nvidia driver. Both systems are 64-bit. One previously unknown bug crept into daylight: the ftrace framework's output routines print the first entry last after buffer has wrapped around. The most important regressions compared to non-ftrace mmiotrace at this time are: - failure of trace_pipe file - illegal lines in output file - unaware of losing data due to buffer full Personally I'd like to see these three solved before submitting to mainline. Other issues may come up once we know when we lose events. Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/mmiotrace/kmmio.c | 186 ++++++++++++++--------------------- arch/x86/kernel/mmiotrace/mmio-mod.c | 3 - include/linux/mmiotrace.h | 2 - kernel/trace/trace_mmiotrace.c | 47 ++++++++- 4 files changed, 120 insertions(+), 118 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/mmiotrace/kmmio.c b/arch/x86/kernel/mmiotrace/kmmio.c index efb467933087..cd0d95fe4fe6 100644 --- a/arch/x86/kernel/mmiotrace/kmmio.c +++ b/arch/x86/kernel/mmiotrace/kmmio.c @@ -5,15 +5,12 @@ * 2008 Pekka Paalanen */ -#include #include #include #include #include #include -#include #include -#include #include #include #include @@ -22,10 +19,9 @@ #include #include #include -#include #include -#include - +#include +#include #include #define KMMIO_PAGE_HASH_BITS 4 @@ -57,14 +53,9 @@ struct kmmio_context { int active; }; -static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, - void *args); - -static DEFINE_MUTEX(kmmio_init_mutex); static DEFINE_SPINLOCK(kmmio_lock); -/* These are protected by kmmio_lock */ -static int kmmio_initialized; +/* Protected by kmmio_lock */ unsigned int kmmio_count; /* Read-protected by RCU, write-protected by kmmio_lock. */ @@ -79,60 +70,6 @@ static struct list_head *kmmio_page_list(unsigned long page) /* Accessed per-cpu */ static DEFINE_PER_CPU(struct kmmio_context, kmmio_ctx); -/* protected by kmmio_init_mutex */ -static struct notifier_block nb_die = { - .notifier_call = kmmio_die_notifier -}; - -/** - * Makes sure kmmio is initialized and usable. - * This must be called before any other kmmio function defined here. - * May sleep. - */ -void reference_kmmio(void) -{ - mutex_lock(&kmmio_init_mutex); - spin_lock_irq(&kmmio_lock); - if (!kmmio_initialized) { - int i; - for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++) - INIT_LIST_HEAD(&kmmio_page_table[i]); - if (register_die_notifier(&nb_die)) - BUG(); - } - kmmio_initialized++; - spin_unlock_irq(&kmmio_lock); - mutex_unlock(&kmmio_init_mutex); -} -EXPORT_SYMBOL_GPL(reference_kmmio); - -/** - * Clean up kmmio after use. This must be called for every call to - * reference_kmmio(). All probes registered after the corresponding - * reference_kmmio() must have been unregistered when calling this. - * May sleep. - */ -void unreference_kmmio(void) -{ - bool unreg = false; - - mutex_lock(&kmmio_init_mutex); - spin_lock_irq(&kmmio_lock); - - if (kmmio_initialized == 1) { - BUG_ON(is_kmmio_active()); - unreg = true; - } - kmmio_initialized--; - BUG_ON(kmmio_initialized < 0); - spin_unlock_irq(&kmmio_lock); - - if (unreg) - unregister_die_notifier(&nb_die); /* calls sync_rcu() */ - mutex_unlock(&kmmio_init_mutex); -} -EXPORT_SYMBOL(unreference_kmmio); - /* * this is basically a dynamic stabbing problem: * Could use the existing prio tree code or @@ -167,58 +104,56 @@ static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page) return NULL; } -/** Mark the given page as not present. Access to it will trigger a fault. */ -static void arm_kmmio_fault_page(unsigned long page, int *page_level) +static void set_page_present(unsigned long addr, bool present, int *pglevel) { - unsigned long address = page & PAGE_MASK; + pteval_t pteval; + pmdval_t pmdval; int level; - pte_t *pte = lookup_address(address, &level); + pmd_t *pmd; + pte_t *pte = lookup_address(addr, &level); if (!pte) { - pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n", - __func__, page); + pr_err("kmmio: no pte for page 0x%08lx\n", addr); return; } - if (level == PG_LEVEL_2M) { - pmd_t *pmd = (pmd_t *)pte; - set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_PRESENT)); - } else { - /* PG_LEVEL_4K */ - set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); + if (pglevel) + *pglevel = level; + + switch (level) { + case PG_LEVEL_2M: + pmd = (pmd_t *)pte; + pmdval = pmd_val(*pmd) & ~_PAGE_PRESENT; + if (present) + pmdval |= _PAGE_PRESENT; + set_pmd(pmd, __pmd(pmdval)); + break; + + case PG_LEVEL_4K: + pteval = pte_val(*pte) & ~_PAGE_PRESENT; + if (present) + pteval |= _PAGE_PRESENT; + set_pte_atomic(pte, __pte(pteval)); + break; + + default: + pr_err("kmmio: unexpected page level 0x%x.\n", level); + return; } - if (page_level) - *page_level = level; + __flush_tlb_one(addr); +} - __flush_tlb_one(page); +/** Mark the given page as not present. Access to it will trigger a fault. */ +static void arm_kmmio_fault_page(unsigned long page, int *page_level) +{ + set_page_present(page & PAGE_MASK, false, page_level); } /** Mark the given page as present. */ static void disarm_kmmio_fault_page(unsigned long page, int *page_level) { - unsigned long address = page & PAGE_MASK; - int level; - pte_t *pte = lookup_address(address, &level); - - if (!pte) { - pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n", - __func__, page); - return; - } - - if (level == PG_LEVEL_2M) { - pmd_t *pmd = (pmd_t *)pte; - set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_PRESENT)); - } else { - /* PG_LEVEL_4K */ - set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT)); - } - - if (page_level) - *page_level = level; - - __flush_tlb_one(page); + set_page_present(page & PAGE_MASK, true, page_level); } /* @@ -240,6 +175,7 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr) { struct kmmio_context *ctx; struct kmmio_fault_page *faultpage; + int ret = 0; /* default to fault not handled */ /* * Preemption is now disabled to prevent process switch during @@ -257,21 +193,35 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr) /* * Either this page fault is not caused by kmmio, or * another CPU just pulled the kmmio probe from under - * our feet. In the latter case all hell breaks loose. + * our feet. The latter case should not be possible. */ goto no_kmmio; } ctx = &get_cpu_var(kmmio_ctx); if (ctx->active) { + disarm_kmmio_fault_page(faultpage->page, NULL); + if (addr == ctx->addr) { + /* + * On SMP we sometimes get recursive probe hits on the + * same address. Context is already saved, fall out. + */ + pr_debug("kmmio: duplicate probe hit on CPU %d, for " + "address 0x%08lx.\n", + smp_processor_id(), addr); + ret = 1; + goto no_kmmio_ctx; + } /* * Prevent overwriting already in-flight context. - * If this page fault really was due to kmmio trap, - * all hell breaks loose. + * This should not happen, let's hope disarming at least + * prevents a panic. */ pr_emerg("kmmio: recursive probe hit on CPU %d, " "for address 0x%08lx. Ignoring.\n", smp_processor_id(), addr); + pr_emerg("kmmio: previous hit was at 0x%08lx.\n", + ctx->addr); goto no_kmmio_ctx; } ctx->active++; @@ -302,14 +252,14 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr) */ put_cpu_var(kmmio_ctx); - return 1; + return 1; /* fault handled */ no_kmmio_ctx: put_cpu_var(kmmio_ctx); no_kmmio: rcu_read_unlock(); preempt_enable_no_resched(); - return 0; /* page fault not handled by kmmio */ + return ret; } /* @@ -322,8 +272,11 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) int ret = 0; struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx); - if (!ctx->active) + if (!ctx->active) { + pr_debug("kmmio: spurious debug trap on CPU %d.\n", + smp_processor_id()); goto out; + } if (ctx->probe && ctx->probe->post_handler) ctx->probe->post_handler(ctx->probe, condition, regs); @@ -525,9 +478,22 @@ static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val, { struct die_args *arg = args; - if (val == DIE_DEBUG) + if (val == DIE_DEBUG && (arg->err & DR_STEP)) if (post_kmmio_handler(arg->err, arg->regs) == 1) return NOTIFY_STOP; return NOTIFY_DONE; } + +static struct notifier_block nb_die = { + .notifier_call = kmmio_die_notifier +}; + +static int __init init_kmmio(void) +{ + int i; + for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++) + INIT_LIST_HEAD(&kmmio_page_table[i]); + return register_die_notifier(&nb_die); +} +fs_initcall(init_kmmio); /* should be before device_initcall() */ diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c index 62abc281a512..8256546d49bf 100644 --- a/arch/x86/kernel/mmiotrace/mmio-mod.c +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c @@ -415,8 +415,6 @@ void enable_mmiotrace(void) if (is_enabled()) goto out; - reference_kmmio(); - #if 0 /* XXX: tracing does not support text entries */ marker_file = debugfs_create_file("marker", 0660, dir, NULL, &fops_marker); @@ -448,7 +446,6 @@ void disable_mmiotrace(void) spin_unlock_irq(&trace_lock); clear_trace_list(); /* guarantees: no more kmmio callbacks */ - unreference_kmmio(); if (marker_file) { debugfs_remove(marker_file); marker_file = NULL; diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index c88a9c197d22..dd6b64b160fc 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -31,8 +31,6 @@ static inline int is_kmmio_active(void) return kmmio_count; } -extern void reference_kmmio(void); -extern void unreference_kmmio(void); extern int register_kmmio_probe(struct kmmio_probe *p); extern void unregister_kmmio_probe(struct kmmio_probe *p); diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index 3a12b1ad0c63..361472b5788c 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -8,6 +8,7 @@ #include #include +#include #include "trace.h" @@ -53,12 +54,52 @@ static void mmio_trace_ctrl_update(struct trace_array *tr) } } +static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev) +{ + int ret = 0; + int i; + resource_size_t start, end; + const struct pci_driver *drv = pci_dev_driver(dev); + + /* XXX: incomplete checks for trace_seq_printf() return value */ + ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x", + dev->bus->number, dev->devfn, + dev->vendor, dev->device, dev->irq); + /* + * XXX: is pci_resource_to_user() appropriate, since we are + * supposed to interpret the __ioremap() phys_addr argument based on + * these printed values? + */ + for (i = 0; i < 7; i++) { + pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); + ret += trace_seq_printf(s, " %llx", + (unsigned long long)(start | + (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); + } + for (i = 0; i < 7; i++) { + pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); + ret += trace_seq_printf(s, " %llx", + dev->resource[i].start < dev->resource[i].end ? + (unsigned long long)(end - start) + 1 : 0); + } + if (drv) + ret += trace_seq_printf(s, " %s\n", drv->name); + else + ret += trace_seq_printf(s, " \n"); + return ret; +} + /* XXX: This is not called for trace_pipe file! */ -void mmio_print_header(struct trace_iterator *iter) +static void mmio_print_header(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; - trace_seq_printf(s, "VERSION broken 20070824\n"); - /* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */ + struct pci_dev *dev = NULL; + + trace_seq_printf(s, "VERSION 20070824\n"); + + for_each_pci_dev(dev) + mmio_print_pcidev(s, dev); + /* XXX: return value? What if header is very long? */ } static int mmio_print_rw(struct trace_iterator *iter) -- cgit v1.2.3 From 970e6fa03885f32cc43e42cb08c73a5f54cd8bd9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:21:03 +0200 Subject: mmiotrace: code style cleanups From c2da03771e29159627c5c7b9509ec70bce9f91ee Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 28 Apr 2008 21:25:22 +0300 Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar --- arch/x86/mm/kmmio.c | 4 ++-- arch/x86/mm/mmio-mod.c | 7 +++---- arch/x86/mm/testmmiotrace.c | 2 +- include/linux/mmiotrace.h | 6 +----- 4 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/kmmio.c b/arch/x86/mm/kmmio.c index 3ad27b8504a5..6a92d9111b64 100644 --- a/arch/x86/mm/kmmio.c +++ b/arch/x86/mm/kmmio.c @@ -17,10 +17,10 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include diff --git a/arch/x86/mm/mmio-mod.c b/arch/x86/mm/mmio-mod.c index 1f77d8532037..a8d2a0019da4 100644 --- a/arch/x86/mm/mmio-mod.c +++ b/arch/x86/mm/mmio-mod.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -418,11 +418,10 @@ static void enter_uniprocessor(void) for_each_cpu_mask(cpu, downed_cpus) { err = cpu_down(cpu); - if (!err) { + if (!err) pr_info(NAME "CPU%d is down.\n", cpu); - } else { + else pr_err(NAME "Error taking CPU%d down: %d\n", cpu, err); - } } if (num_online_cpus() > 1) pr_warning(NAME "multiple CPUs still online, " diff --git a/arch/x86/mm/testmmiotrace.c b/arch/x86/mm/testmmiotrace.c index cfa60b227c8d..d877c5b423ef 100644 --- a/arch/x86/mm/testmmiotrace.c +++ b/arch/x86/mm/testmmiotrace.c @@ -2,7 +2,7 @@ * Written by Pekka Paalanen, 2008 */ #include -#include +#include #define MODULE_NAME "testmmiotrace" diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index dd6b64b160fc..de8e91258da7 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -1,9 +1,7 @@ #ifndef MMIOTRACE_H #define MMIOTRACE_H -#include - -#ifdef __KERNEL__ +#include #include @@ -84,6 +82,4 @@ extern void disable_mmiotrace(void); extern void mmio_trace_rw(struct mmiotrace_rw *rw); extern void mmio_trace_mapping(struct mmiotrace_map *map); -#endif /* __KERNEL__ */ - #endif /* MMIOTRACE_H */ -- cgit v1.2.3 From dee310d0adf41019aca476052ac3085ff286d9be Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:21:03 +0200 Subject: x86 mmiotrace: use resource_size_t for phys addresses Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar --- arch/x86/mm/mmio-mod.c | 11 ++++++----- include/linux/mmiotrace.h | 14 +++++++------- kernel/trace/trace_mmiotrace.c | 20 ++++++++++++-------- 3 files changed, 25 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/mmio-mod.c b/arch/x86/mm/mmio-mod.c index 278998c1998f..3b04a0126121 100644 --- a/arch/x86/mm/mmio-mod.c +++ b/arch/x86/mm/mmio-mod.c @@ -48,7 +48,7 @@ struct trap_reason { struct remap_trace { struct list_head list; struct kmmio_probe probe; - unsigned long phys; + resource_size_t phys; unsigned long id; }; @@ -275,7 +275,7 @@ static void post(struct kmmio_probe *p, unsigned long condition, put_cpu_var(pf_reason); } -static void ioremap_trace_core(unsigned long offset, unsigned long size, +static void ioremap_trace_core(resource_size_t offset, unsigned long size, void __iomem *addr) { static atomic_t next_id; @@ -319,13 +319,14 @@ not_enabled: spin_unlock_irq(&trace_lock); } -void -mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr) +void mmiotrace_ioremap(resource_size_t offset, unsigned long size, + void __iomem *addr) { if (!is_enabled()) /* recheck and proper locking in *_core() */ return; - pr_debug(NAME "ioremap_*(0x%lx, 0x%lx) = %p\n", offset, size, addr); + pr_debug(NAME "ioremap_*(0x%llx, 0x%lx) = %p\n", + (unsigned long long)offset, size, addr); if ((filter_offset) && (offset != filter_offset)) return; ioremap_trace_core(offset, size, addr); diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index de8e91258da7..5cbbc374e945 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -2,7 +2,6 @@ #define MMIOTRACE_H #include - #include struct kmmio_probe; @@ -37,14 +36,15 @@ extern int kmmio_handler(struct pt_regs *regs, unsigned long addr); /* Called from ioremap.c */ #ifdef CONFIG_MMIOTRACE -extern void -mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr); +extern void mmiotrace_ioremap(resource_size_t offset, unsigned long size, + void __iomem *addr); extern void mmiotrace_iounmap(volatile void __iomem *addr); #else -static inline void -mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr) +static inline void mmiotrace_ioremap(resource_size_t offset, + unsigned long size, void __iomem *addr) { } + static inline void mmiotrace_iounmap(volatile void __iomem *addr) { } @@ -60,7 +60,7 @@ enum mm_io_opcode { }; struct mmiotrace_rw { - unsigned long phys; /* PCI address of register */ + resource_size_t phys; /* PCI address of register */ unsigned long value; unsigned long pc; /* optional program counter */ int map_id; @@ -69,7 +69,7 @@ struct mmiotrace_rw { }; struct mmiotrace_map { - unsigned long phys; /* base address in PCI space */ + resource_size_t phys; /* base address in PCI space */ unsigned long virt; /* base virtual address */ unsigned long len; /* mapping size */ int map_id; diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index 3c1dacdc2d85..b13dc19dcbb4 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -184,20 +184,23 @@ static int mmio_print_rw(struct trace_iterator *iter) switch (entry->mmiorw.opcode) { case MMIO_READ: ret = trace_seq_printf(s, - "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", - rw->width, secs, usec_rem, rw->map_id, rw->phys, + "R %d %lu.%06lu %d 0x%llx 0x%lx 0x%lx %d\n", + rw->width, secs, usec_rem, rw->map_id, + (unsigned long long)rw->phys, rw->value, rw->pc, 0); break; case MMIO_WRITE: ret = trace_seq_printf(s, - "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", - rw->width, secs, usec_rem, rw->map_id, rw->phys, + "W %d %lu.%06lu %d 0x%llx 0x%lx 0x%lx %d\n", + rw->width, secs, usec_rem, rw->map_id, + (unsigned long long)rw->phys, rw->value, rw->pc, 0); break; case MMIO_UNKNOWN_OP: ret = trace_seq_printf(s, - "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n", - secs, usec_rem, rw->map_id, rw->phys, + "UNKNOWN %lu.%06lu %d 0x%llx %02x,%02x,%02x 0x%lx %d\n", + secs, usec_rem, rw->map_id, + (unsigned long long)rw->phys, (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, (rw->value >> 0) & 0xff, rw->pc, 0); break; @@ -223,8 +226,9 @@ static int mmio_print_map(struct trace_iterator *iter) switch (entry->mmiorw.opcode) { case MMIO_PROBE: ret = trace_seq_printf(s, - "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n", - secs, usec_rem, m->map_id, m->phys, m->virt, m->len, + "MAP %lu.%06lu %d 0x%llx 0x%lx 0x%lx 0x%lx %d\n", + secs, usec_rem, m->map_id, + (unsigned long long)m->phys, m->virt, m->len, 0UL, 0); break; case MMIO_UNPROBE: -- cgit v1.2.3 From a50445d76c22a34ae149704ea5adaef171c8acb7 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 May 2008 21:21:03 +0200 Subject: mmiotrace: rename kmmio_probe::user_data to :private. Signed-off-by: Pekka Paalanen Signed-off-by: Ingo Molnar --- arch/x86/mm/mmio-mod.c | 4 ++-- include/linux/mmiotrace.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/mmio-mod.c b/arch/x86/mm/mmio-mod.c index 3b04a0126121..ed0e0e90b3ef 100644 --- a/arch/x86/mm/mmio-mod.c +++ b/arch/x86/mm/mmio-mod.c @@ -191,7 +191,7 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace); const unsigned long instptr = instruction_pointer(regs); const enum reason_type type = get_ins_type(instptr); - struct remap_trace *trace = p->user_data; + struct remap_trace *trace = p->private; /* it doesn't make sense to have more than one active trace per cpu */ if (my_reason->active_traces) @@ -299,7 +299,7 @@ static void ioremap_trace_core(resource_size_t offset, unsigned long size, .len = size, .pre_handler = pre, .post_handler = post, - .user_data = trace + .private = trace }, .phys = offset, .id = atomic_inc_return(&next_id) diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index 5cbbc374e945..61d19e1b7a0b 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h @@ -18,7 +18,7 @@ struct kmmio_probe { unsigned long len; /* length of the probe region */ kmmio_pre_handler_t pre_handler; /* Called before addr is executed. */ kmmio_post_handler_t post_handler; /* Called after addr is executed */ - void *user_data; + void *private; }; /* kmmio is active by some kmmio_probes? */ -- cgit v1.2.3 From 42fdfa238a23643226910acf922ea930b3286032 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 24 May 2008 23:14:51 +0200 Subject: namespacecheck: more kernel/printk.c fixes [ Stephen Rothwell : build fix ] Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/kernel.h | 6 ------ kernel/printk.c | 13 ------------- 2 files changed, 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 792bf0aa779b..f2a668c195bf 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -184,9 +184,6 @@ asmlinkage int vprintk(const char *fmt, va_list args) __attribute__ ((format (printf, 1, 0))); asmlinkage int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))) __cold; -extern int log_buf_get_len(void); -extern int log_buf_read(int idx); -extern int log_buf_copy(char *dest, int idx, int len); extern int printk_ratelimit_jiffies; extern int printk_ratelimit_burst; @@ -202,9 +199,6 @@ static inline int vprintk(const char *s, va_list args) { return 0; } static inline int printk(const char *s, ...) __attribute__ ((format (printf, 1, 2))); static inline int __cold printk(const char *s, ...) { return 0; } -static inline int log_buf_get_len(void) { return 0; } -static inline int log_buf_read(int idx) { return 0; } -static inline int log_buf_copy(char *dest, int idx, int len) { return 0; } static inline int printk_ratelimit(void) { return 0; } static inline int __printk_ratelimit(int ratelimit_jiffies, \ int ratelimit_burst) { return 0; } diff --git a/kernel/printk.c b/kernel/printk.c index b620e3d96136..55d16e57499a 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -267,19 +267,6 @@ int log_buf_copy(char *dest, int idx, int len) return ret; } -/* - * Extract a single character from the log buffer. - */ -static int log_buf_read(int idx) -{ - char ret; - - if (log_buf_copy(&ret, idx, 1) == 1) - return ret; - else - return -1; -} - /* * Commands to do_syslog: * -- cgit v1.2.3 From 9c44bc03fff44ff04237a7d92e35304a0e50c331 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:21:04 +0200 Subject: softlockup: allow panic on lockup allow users to configure the softlockup detector to generate a panic instead of a warning message. high-availability systems might opt for this strict method (combined with panic_timeout= boot option/sysctl), instead of generating softlockup warnings ad infinitum. also, automated tests work better if the system reboots reliably (into a safe kernel) in case of a lockup. The full spectrum of configurability is supported: boot option, sysctl option and Kconfig option. it's default-disabled. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- Documentation/kernel-parameters.txt | 3 +++ include/linux/sched.h | 3 ++- kernel/softlockup.c | 21 +++++++++++++++++++++ kernel/sysctl.c | 11 +++++++++++ lib/Kconfig.debug | 26 +++++++++++++++++++++++++- 5 files changed, 62 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e07c432c731f..042588fa12e5 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1971,6 +1971,9 @@ and is between 256 and 4096 characters. It is defined in the file snd-ymfpci= [HW,ALSA] + softlockup_panic= + [KNL] Should the soft-lockup detector generate panics. + sonypi.*= [HW] Sony Programmable I/O Control Device driver See Documentation/sonypi.txt diff --git a/include/linux/sched.h b/include/linux/sched.h index 5395a6176f4b..71f5972dc48e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -294,7 +294,8 @@ extern void softlockup_tick(void); extern void spawn_softlockup_task(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); -extern unsigned long softlockup_thresh; +extern unsigned int softlockup_panic; +extern unsigned long softlockup_thresh; extern unsigned long sysctl_hung_task_check_count; extern unsigned long sysctl_hung_task_timeout_secs; extern unsigned long sysctl_hung_task_warnings; diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 01b6522fd92b..78e0ad21cb0c 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -27,6 +27,21 @@ static DEFINE_PER_CPU(struct task_struct *, watchdog_task); static int __read_mostly did_panic; unsigned long __read_mostly softlockup_thresh = 60; +/* + * Should we panic (and reboot, if panic_timeout= is set) when a + * soft-lockup occurs: + */ +unsigned int __read_mostly softlockup_panic = + CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE; + +static int __init softlockup_panic_setup(char *str) +{ + softlockup_panic = simple_strtoul(str, NULL, 0); + + return 1; +} +__setup("softlockup_panic=", softlockup_panic_setup); + static int softlock_panic(struct notifier_block *this, unsigned long event, void *ptr) { @@ -120,6 +135,9 @@ void softlockup_tick(void) else dump_stack(); spin_unlock(&print_lock); + + if (softlockup_panic) + panic("softlockup: hung tasks"); } /* @@ -172,6 +190,9 @@ static void check_hung_task(struct task_struct *t, unsigned long now) t->last_switch_timestamp = now; touch_nmi_watchdog(); + + if (softlockup_panic) + panic("softlockup: blocked tasks"); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 29116652dca8..2d3b388c402d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -727,6 +727,17 @@ static struct ctl_table kern_table[] = { }, #endif #ifdef CONFIG_DETECT_SOFTLOCKUP + { + .ctl_name = CTL_UNNUMBERED, + .procname = "softlockup_panic", + .data = &softlockup_panic, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_doulongvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + .extra2 = &one, + }, { .ctl_name = CTL_UNNUMBERED, .procname = "softlockup_thresh", diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d2099f41aa1e..509ae35a9ef5 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -147,7 +147,7 @@ config DETECT_SOFTLOCKUP help Say Y here to enable the kernel to detect "soft lockups", which are bugs that cause the kernel to loop in kernel - mode for more than 10 seconds, without giving other tasks a + mode for more than 60 seconds, without giving other tasks a chance to run. When a soft-lockup is detected, the kernel will print the @@ -159,6 +159,30 @@ config DETECT_SOFTLOCKUP can be detected via the NMI-watchdog, on platforms that support it.) +config BOOTPARAM_SOFTLOCKUP_PANIC + bool "Panic (Reboot) On Soft Lockups" + depends on DETECT_SOFTLOCKUP + help + Say Y here to enable the kernel to panic on "soft lockups", + which are bugs that cause the kernel to loop in kernel + mode for more than 60 seconds, without giving other tasks a + chance to run. + + The panic can be used in combination with panic_timeout, + to cause the system to reboot automatically after a + lockup has been detected. This feature is useful for + high-availability systems that have uptime guarantees and + where a lockup must be resolved ASAP. + + Say N if unsure. + +config BOOTPARAM_SOFTLOCKUP_PANIC_VALUE + int + depends on DETECT_SOFTLOCKUP + range 0 1 + default 0 if !BOOTPARAM_SOFTLOCKUP_PANIC + default 1 if BOOTPARAM_SOFTLOCKUP_PANIC + config SCHED_DEBUG bool "Collect scheduler debugging info" depends on DEBUG_KERNEL && PROC_FS -- cgit v1.2.3 From 9383d9679056e6cc4e7ff70f31da945a268238f4 Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Mon, 12 May 2008 21:21:14 +0200 Subject: softlockup: fix softlockup_thresh unaligned access and disable detection at runtime Fix unaligned access errors when setting softlockup_thresh on 64 bit platforms. Allow softlockup detection to be disabled by setting softlockup_thresh <= 0. Detect that boot time softlockup detection has been disabled earlier in softlockup_tick. Signed-off-by: Dimitri Sivanich Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 2 +- kernel/softlockup.c | 12 ++++++++++-- kernel/sysctl.c | 9 +++++---- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 71f5972dc48e..ea26221644e2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -295,10 +295,10 @@ extern void spawn_softlockup_task(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); extern unsigned int softlockup_panic; -extern unsigned long softlockup_thresh; extern unsigned long sysctl_hung_task_check_count; extern unsigned long sysctl_hung_task_timeout_secs; extern unsigned long sysctl_hung_task_warnings; +extern int softlockup_thresh; #else static inline void softlockup_tick(void) { diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 78e0ad21cb0c..a3a0b239b7f7 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -25,7 +25,7 @@ static DEFINE_PER_CPU(unsigned long, print_timestamp); static DEFINE_PER_CPU(struct task_struct *, watchdog_task); static int __read_mostly did_panic; -unsigned long __read_mostly softlockup_thresh = 60; +int __read_mostly softlockup_thresh = 60; /* * Should we panic (and reboot, if panic_timeout= is set) when a @@ -94,6 +94,14 @@ void softlockup_tick(void) struct pt_regs *regs = get_irq_regs(); unsigned long now; + /* Is detection switched off? */ + if (!per_cpu(watchdog_task, this_cpu) || softlockup_thresh <= 0) { + /* Be sure we don't false trigger if switched back on */ + if (touch_timestamp) + per_cpu(touch_timestamp, this_cpu) = 0; + return; + } + if (touch_timestamp == 0) { touch_softlockup_watchdog(); return; @@ -104,7 +112,7 @@ void softlockup_tick(void) /* report at most once a second */ if ((print_timestamp >= touch_timestamp && print_timestamp < (touch_timestamp + 1)) || - did_panic || !per_cpu(watchdog_task, this_cpu)) { + did_panic) { return; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2d3b388c402d..31c19a79738d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -84,12 +84,13 @@ extern int latencytop_enabled; extern int sysctl_nr_open_min, sysctl_nr_open_max; /* Constants used for minimum and maximum */ -#if defined(CONFIG_DETECT_SOFTLOCKUP) || defined(CONFIG_HIGHMEM) +#ifdef CONFIG_HIGHMEM static int one = 1; #endif #ifdef CONFIG_DETECT_SOFTLOCKUP static int sixty = 60; +static int neg_one = -1; #endif #ifdef CONFIG_MMU @@ -742,11 +743,11 @@ static struct ctl_table kern_table[] = { .ctl_name = CTL_UNNUMBERED, .procname = "softlockup_thresh", .data = &softlockup_thresh, - .maxlen = sizeof(unsigned long), + .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_doulongvec_minmax, + .proc_handler = &proc_dointvec_minmax, .strategy = &sysctl_intvec, - .extra1 = &one, + .extra1 = &neg_one, .extra2 = &sixty, }, { -- cgit v1.2.3 From 63cc8c75156462d4b42cbdd76c293b7eee7ddbfe Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 12 May 2008 15:44:40 +0200 Subject: percpu: introduce DEFINE_PER_CPU_PAGE_ALIGNED() macro While examining holes in percpu section I found this : c05f5000 D per_cpu__current_task c05f5000 D __per_cpu_start c05f5004 D per_cpu__cpu_number c05f5008 D per_cpu__irq_regs c05f500c d per_cpu__cpu_devices c05f5040 D per_cpu__cyc2ns c05f6000 d per_cpu__cpuid4_info c05f6004 d per_cpu__cache_kobject c05f6008 d per_cpu__index_kobject c05f7000 D per_cpu__gdt_page This is because gdt_page is a percpu variable, defined with a page alignement, and linker is doing its job, two times because of .o nesting in the build process. I introduced a new macro DEFINE_PER_CPU_PAGE_ALIGNED() to avoid wasting this space. All page aligned variables (only one at this time) are put in a separate subsection .data.percpu.page_aligned, at the very begining of percpu zone. Before patch , on a x86_32 machine : .data.percpu 30232 3227471872 .data.percpu 22168 3227471872 Thats 8064 bytes saved for each CPU. Signed-off-by: Eric Dumazet Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/cpu/common.c | 2 +- arch/x86/kernel/vmlinux_32.lds.S | 1 + include/asm-generic/vmlinux.lds.h | 1 + include/linux/percpu.h | 7 +++++++ 4 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index d0463a946247..b2f54fafb8bc 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -21,7 +21,7 @@ #include "cpu.h" -DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = { +DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = { [GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00cf9a00 } } }, [GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9200 } } }, [GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00cffa00 } } }, diff --git a/arch/x86/kernel/vmlinux_32.lds.S b/arch/x86/kernel/vmlinux_32.lds.S index ce5ed083a1e9..0f7c29a8c4ea 100644 --- a/arch/x86/kernel/vmlinux_32.lds.S +++ b/arch/x86/kernel/vmlinux_32.lds.S @@ -189,6 +189,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { __per_cpu_start = .; + *(.data.percpu.page_aligned) *(.data.percpu) *(.data.percpu.shared_aligned) __per_cpu_end = .; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index f054778e916c..69e5c1182fde 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -348,6 +348,7 @@ . = ALIGN(align); \ __per_cpu_start = .; \ .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { \ + *(.data.percpu.page_aligned) \ *(.data.percpu) \ *(.data.percpu.shared_aligned) \ } \ diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 4cdd393e71e1..2edacc8e6b8b 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -23,12 +23,19 @@ __attribute__((__section__(SHARED_ALIGNED_SECTION))) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \ ____cacheline_aligned_in_smp + +#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \ + __attribute__((__section__(".data.percpu.page_aligned"))) \ + PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name #else #define DEFINE_PER_CPU(type, name) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name #define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \ DEFINE_PER_CPU(type, name) + +#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \ + DEFINE_PER_CPU(type, name) #endif #define EXPORT_PER_CPU_SYMBOL(var) EXPORT_SYMBOL(per_cpu__##var) -- cgit v1.2.3 From 63687a528c39a67c1a213cdffa09feb0e6af9dbe Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 12 May 2008 15:44:41 +0200 Subject: x86: move tracedata to RODATA .. allowing it to be write-protected just as other read-only data under CONFIG_DEBUG_RODATA. Signed-off-by: Jan Beulich Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/vmlinux_32.lds.S | 7 ------- arch/x86/kernel/vmlinux_64.lds.S | 7 ------- drivers/base/power/trace.c | 2 +- include/asm-generic/vmlinux.lds.h | 14 ++++++++++++++ include/asm-x86/resume-trace.h | 2 +- include/linux/resume-trace.h | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/vmlinux_32.lds.S b/arch/x86/kernel/vmlinux_32.lds.S index ce5ed083a1e9..2674f5796275 100644 --- a/arch/x86/kernel/vmlinux_32.lds.S +++ b/arch/x86/kernel/vmlinux_32.lds.S @@ -60,13 +60,6 @@ SECTIONS BUG_TABLE :text - . = ALIGN(4); - .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { - __tracedata_start = .; - *(.tracedata) - __tracedata_end = .; - } - RODATA /* writeable */ diff --git a/arch/x86/kernel/vmlinux_64.lds.S b/arch/x86/kernel/vmlinux_64.lds.S index fad3674b06a5..687041bfbae5 100644 --- a/arch/x86/kernel/vmlinux_64.lds.S +++ b/arch/x86/kernel/vmlinux_64.lds.S @@ -53,13 +53,6 @@ SECTIONS RODATA - . = ALIGN(4); - .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { - __tracedata_start = .; - *(.tracedata) - __tracedata_end = .; - } - . = ALIGN(PAGE_SIZE); /* Align data segment to page size boundary */ /* Data */ .data : AT(ADDR(.data) - LOAD_OFFSET) { diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 2b4b392dcbc1..87a7f1d02578 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -153,7 +153,7 @@ EXPORT_SYMBOL(set_trace_device); * it's not any guarantee, but it's a high _likelihood_ that * the match is valid). */ -void generate_resume_trace(void *tracedata, unsigned int user) +void generate_resume_trace(const void *tracedata, unsigned int user) { unsigned short lineno = *(unsigned short *)tracedata; const char *file = *(const char **)(tracedata + 2); diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index f054778e916c..f1992dc5c424 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -93,6 +93,8 @@ VMLINUX_SYMBOL(__end_rio_route_ops) = .; \ } \ \ + TRACEDATA \ + \ /* Kernel symbol table: Normal symbols */ \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab) = .; \ @@ -318,6 +320,18 @@ __stop___bug_table = .; \ } +#ifdef CONFIG_PM_TRACE +#define TRACEDATA \ + . = ALIGN(4); \ + .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { \ + __tracedata_start = .; \ + *(.tracedata) \ + __tracedata_end = .; \ + } +#else +#define TRACEDATA +#endif + #define NOTES \ .notes : AT(ADDR(.notes) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start_notes) = .; \ diff --git a/include/asm-x86/resume-trace.h b/include/asm-x86/resume-trace.h index 2557514d7ef6..8d9f0b41ee86 100644 --- a/include/asm-x86/resume-trace.h +++ b/include/asm-x86/resume-trace.h @@ -6,7 +6,7 @@ #define TRACE_RESUME(user) \ do { \ if (pm_trace_enabled) { \ - void *tracedata; \ + const void *tracedata; \ asm volatile(_ASM_MOV_UL " $1f,%0\n" \ ".section .tracedata,\"a\"\n" \ "1:\t.word %c1\n\t" \ diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h index f3f4f28c6960..c9ba2fdf807d 100644 --- a/include/linux/resume-trace.h +++ b/include/linux/resume-trace.h @@ -8,7 +8,7 @@ extern int pm_trace_enabled; struct device; extern void set_trace_device(struct device *); -extern void generate_resume_trace(void *tracedata, unsigned int user); +extern void generate_resume_trace(const void *tracedata, unsigned int user); #define TRACE_DEVICE(dev) do { \ if (pm_trace_enabled) \ -- cgit v1.2.3 From 962cf36c5bf6d2840b8d66ee9a606fae2f540bbd Mon Sep 17 00:00:00 2001 From: "Carlos R. Mafra" Date: Thu, 15 May 2008 11:15:37 -0300 Subject: Remove argument from open_softirq which is always NULL As git-grep shows, open_softirq() is always called with the last argument being NULL block/blk-core.c: open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL); kernel/hrtimer.c: open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq, NULL); kernel/rcuclassic.c: open_softirq(RCU_SOFTIRQ, rcu_process_callbacks, NULL); kernel/rcupreempt.c: open_softirq(RCU_SOFTIRQ, rcu_process_callbacks, NULL); kernel/sched.c: open_softirq(SCHED_SOFTIRQ, run_rebalance_domains, NULL); kernel/softirq.c: open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); kernel/softirq.c: open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); kernel/timer.c: open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL); net/core/dev.c: open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); net/core/dev.c: open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); This observation has already been made by Matthew Wilcox in June 2002 (http://www.cs.helsinki.fi/linux/linux-kernel/2002-25/0687.html) "I notice that none of the current softirq routines use the data element passed to them." and the situation hasn't changed since them. So it appears we can safely remove that extra argument to save 128 (54) bytes of kernel data (text). Signed-off-by: Carlos R. Mafra Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- block/blk-core.c | 2 +- include/linux/interrupt.h | 3 +-- kernel/hrtimer.c | 2 +- kernel/rcuclassic.c | 2 +- kernel/rcupreempt.c | 2 +- kernel/sched.c | 2 +- kernel/softirq.c | 7 +++---- kernel/timer.c | 2 +- net/core/dev.c | 4 ++-- 9 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 6a9cc0d22a61..75fdc65136e8 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2048,7 +2048,7 @@ int __init blk_dev_init(void) for_each_possible_cpu(i) INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i)); - open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL); + open_softirq(BLOCK_SOFTIRQ, blk_done_softirq); register_hotcpu_notifier(&blk_cpu_notifier); return 0; diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index f1fc7470d26c..a86186dd0474 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -285,12 +285,11 @@ enum struct softirq_action { void (*action)(struct softirq_action *); - void *data; }; asmlinkage void do_softirq(void); asmlinkage void __do_softirq(void); -extern void open_softirq(int nr, void (*action)(struct softirq_action*), void *data); +extern void open_softirq(int nr, void (*action)(struct softirq_action *)); extern void softirq_init(void); #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) extern void raise_softirq_irqoff(unsigned int nr); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 421be5fe5cc7..861b4088092a 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1669,7 +1669,7 @@ void __init hrtimers_init(void) (void *)(long)smp_processor_id()); register_cpu_notifier(&hrtimers_nb); #ifdef CONFIG_HIGH_RES_TIMERS - open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq, NULL); + open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq); #endif } diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index f4ffbd0f306f..f6e01f3ae9c6 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -529,7 +529,7 @@ static void __cpuinit rcu_online_cpu(int cpu) rcu_init_percpu_data(cpu, &rcu_ctrlblk, rdp); rcu_init_percpu_data(cpu, &rcu_bh_ctrlblk, bh_rdp); - open_softirq(RCU_SOFTIRQ, rcu_process_callbacks, NULL); + open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); } static int __cpuinit rcu_cpu_notify(struct notifier_block *self, diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index e1cdf196a515..9dd827db359f 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c @@ -1125,7 +1125,7 @@ void __init __rcu_init(void) for_each_online_cpu(cpu) rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE, (void *)(long) cpu); - open_softirq(RCU_SOFTIRQ, rcu_process_callbacks, NULL); + open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); } /* diff --git a/kernel/sched.c b/kernel/sched.c index cfa222a91539..56ea3a203a5a 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -8154,7 +8154,7 @@ void __init sched_init(void) #endif #ifdef CONFIG_SMP - open_softirq(SCHED_SOFTIRQ, run_rebalance_domains, NULL); + open_softirq(SCHED_SOFTIRQ, run_rebalance_domains); #endif #ifdef CONFIG_RT_MUTEXES diff --git a/kernel/softirq.c b/kernel/softirq.c index 36e061740047..059256874e9b 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -347,9 +347,8 @@ void raise_softirq(unsigned int nr) local_irq_restore(flags); } -void open_softirq(int nr, void (*action)(struct softirq_action*), void *data) +void open_softirq(int nr, void (*action)(struct softirq_action *)) { - softirq_vec[nr].data = data; softirq_vec[nr].action = action; } @@ -503,8 +502,8 @@ void __init softirq_init(void) &per_cpu(tasklet_hi_vec, cpu).head; } - open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); - open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); + open_softirq(TASKLET_SOFTIRQ, tasklet_action); + open_softirq(HI_SOFTIRQ, tasklet_hi_action); } static int ksoftirqd(void * __bind_cpu) diff --git a/kernel/timer.c b/kernel/timer.c index ceacc6626572..b4da888497fa 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1502,7 +1502,7 @@ void __init init_timers(void) BUG_ON(err == NOTIFY_BAD); register_cpu_notifier(&timers_nb); - open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL); + open_softirq(TIMER_SOFTIRQ, run_timer_softirq); } /** diff --git a/net/core/dev.c b/net/core/dev.c index 582963077877..cf0e16731dc7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4563,8 +4563,8 @@ static int __init net_dev_init(void) dev_boot_phase = 0; - open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); - open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); + open_softirq(NET_TX_SOFTIRQ, net_tx_action); + open_softirq(NET_RX_SOFTIRQ, net_rx_action); hotcpu_notifier(dev_cpu_callback, 0); dst_init(); -- cgit v1.2.3 From e9197bf0114661195bee35e7795cfc42164d9b2c Mon Sep 17 00:00:00 2001 From: Paul Jackson Date: Wed, 14 May 2008 08:15:10 -0700 Subject: x86 boot: remove some unused extern function declarations Remove three extern declarations for routines that don't exist. Fix a typo in a comment. Signed-off-by: Paul Jackson Signed-off-by: Ingo Molnar --- arch/x86/mm/numa_64.c | 2 +- include/linux/efi.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index c5066d519e5d..afb07ffb931d 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -233,7 +233,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, else bootmap_start = round_up(start, PAGE_SIZE); /* - * SMP_CAHCE_BYTES could be enough, but init_bootmem_node like + * SMP_CACHE_BYTES could be enough, but init_bootmem_node like * to use that to align to PAGE_SIZE */ bootmap = early_node_mem(nodeid, bootmap_start, end, diff --git a/include/linux/efi.h b/include/linux/efi.h index a5f359a7ad0e..807373d467f7 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -287,7 +287,6 @@ efi_guid_unparse(efi_guid_t *guid, char *out) extern void efi_init (void); extern void *efi_get_pal_addr (void); extern void efi_map_pal_code (void); -extern void efi_map_memmap(void); extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg); extern void efi_gettimeofday (struct timespec *ts); extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ @@ -295,14 +294,11 @@ extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); -extern int efi_mem_attribute_range (unsigned long phys_addr, unsigned long size, - u64 attr); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); extern unsigned long efi_get_time(void); extern int efi_set_rtc_mmss(unsigned long nowtime); -extern int is_available_memory(efi_memory_desc_t * md); extern struct efi_memory_map memmap; /** -- cgit v1.2.3 From c801ed3860fe2f84281d4cae4c3e6ca87e81e890 Mon Sep 17 00:00:00 2001 From: Paul Jackson Date: Wed, 14 May 2008 08:15:23 -0700 Subject: x86 boot: simplify pageblock_bits enum declaration The use of #defines with '##' pre-processor concatenation is a useful way to form several symbol names with a common pattern. But when there is just a single name obtained from that #define, it's just obfuscation. Better to just write the plain symbol name, as is. The following patch is a result of my wasting ten minutes looking through the kernel to figure out what 'PB_migrate_end' meant, and forgetting what I came to do, by the time I figured out that the #define PB_range macro defined it. Signed-off-by: Paul Jackson Signed-off-by: Ingo Molnar --- include/linux/pageblock-flags.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index e875905f7b12..e8c06122be36 100644 --- a/include/linux/pageblock-flags.h +++ b/include/linux/pageblock-flags.h @@ -25,13 +25,11 @@ #include -/* Macro to aid the definition of ranges of bits */ -#define PB_range(name, required_bits) \ - name, name ## _end = (name + required_bits) - 1 - /* Bit indices that affect a whole block of pages */ enum pageblock_bits { - PB_range(PB_migrate, 3), /* 3 bits required for migrate types */ + PB_migrate, + PB_migrate_end = PB_migrate + 3 - 1, + /* 3 bits required for migrate types */ NR_PAGEBLOCK_BITS }; -- cgit v1.2.3 From 41c52c0db9607e59f90da7da5309489fa06e887f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 May 2008 11:46:33 -0400 Subject: ftrace: set_ftrace_notrace feature While debugging latencies in the RT kernel, I found that it would be nice to be able to filter away functions from the trace than just to filter on functions. I added a new interface to the debugfs tracing directory called set_ftrace_notrace When dynamic frace is enabled, this lets you filter away functions that will not be recorded in the trace. It is similar to adding 'notrace' to those functions but by doing it without recompiling the kernel. Here's how set_ftrace_filter and set_ftrace_notrace interact. Remember, if set_ftrace_filter is set, it removes all functions from the trace execpt for those listed in the set_ftrace_filter. set_ftrace_notrace will prevent those functions from being traced. If you were to set one function in both set_ftrace_filter and set_ftrace_notrace and that function was the same, then you would end up with an empty trace. the set of functions to trace is: set_ftrace_filter == empty then all functions not in set_ftrace_notrace else set of the set_ftrace_filter and not in set of set_ftrace_notrace. Signed-off-by: Steven Rostedt Signed-off-by: Thomas Gleixner --- include/linux/ftrace.h | 1 + kernel/trace/ftrace.c | 170 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 131 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 922e23d0196f..ffbbd54a720e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -48,6 +48,7 @@ enum { FTRACE_FL_FAILED = (1 << 1), FTRACE_FL_FILTER = (1 << 2), FTRACE_FL_ENABLED = (1 << 3), + FTRACE_FL_NOTRACE = (1 << 4), }; struct dyn_ftrace { diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 89bd9a6f52ec..2552454609cf 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -170,7 +170,7 @@ static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu); static DEFINE_SPINLOCK(ftrace_shutdown_lock); static DEFINE_MUTEX(ftraced_lock); -static DEFINE_MUTEX(ftrace_filter_lock); +static DEFINE_MUTEX(ftrace_regex_lock); struct ftrace_page { struct ftrace_page *next; @@ -337,13 +337,12 @@ static void __ftrace_replace_code(struct dyn_ftrace *rec, unsigned char *old, unsigned char *new, int enable) { - unsigned long ip; + unsigned long ip, fl; int failed; ip = rec->ip; if (ftrace_filtered && enable) { - unsigned long fl; /* * If filtering is on: * @@ -356,13 +355,16 @@ __ftrace_replace_code(struct dyn_ftrace *rec, * If this record is not set to be filtered * and it is not enabled do nothing. * + * If this record is set not to trace then + * do nothing. + * * If this record is not set to be filtered and * it is enabled, disable it. */ fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED); if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || - (fl == 0)) + (fl == 0) || (rec->flags & FTRACE_FL_NOTRACE)) return; /* @@ -380,9 +382,17 @@ __ftrace_replace_code(struct dyn_ftrace *rec, } } else { - if (enable) + if (enable) { + /* + * If this record is set not to trace and is + * not enabled, do nothing. + */ + fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); + if (fl == FTRACE_FL_NOTRACE) + return; + new = ftrace_call_replace(ip, FTRACE_ADDR); - else + } else old = ftrace_call_replace(ip, FTRACE_ADDR); if (enable) { @@ -721,6 +731,7 @@ static int __init ftrace_dyn_table_alloc(void) enum { FTRACE_ITER_FILTER = (1 << 0), FTRACE_ITER_CONT = (1 << 1), + FTRACE_ITER_NOTRACE = (1 << 2), }; #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ @@ -754,7 +765,9 @@ t_next(struct seq_file *m, void *v, loff_t *pos) rec = &iter->pg->records[iter->idx++]; if ((rec->flags & FTRACE_FL_FAILED) || ((iter->flags & FTRACE_ITER_FILTER) && - !(rec->flags & FTRACE_FL_FILTER))) { + !(rec->flags & FTRACE_FL_FILTER)) || + ((iter->flags & FTRACE_ITER_NOTRACE) && + !(rec->flags & FTRACE_FL_NOTRACE))) { rec = NULL; goto retry; } @@ -847,22 +860,24 @@ int ftrace_avail_release(struct inode *inode, struct file *file) return 0; } -static void ftrace_filter_reset(void) +static void ftrace_filter_reset(int enable) { struct ftrace_page *pg; struct dyn_ftrace *rec; + unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; unsigned i; /* keep kstop machine from running */ preempt_disable(); - ftrace_filtered = 0; + if (enable) + ftrace_filtered = 0; pg = ftrace_pages_start; while (pg) { for (i = 0; i < pg->index; i++) { rec = &pg->records[i]; if (rec->flags & FTRACE_FL_FAILED) continue; - rec->flags &= ~FTRACE_FL_FILTER; + rec->flags &= ~type; } pg = pg->next; } @@ -870,7 +885,7 @@ static void ftrace_filter_reset(void) } static int -ftrace_filter_open(struct inode *inode, struct file *file) +ftrace_regex_open(struct inode *inode, struct file *file, int enable) { struct ftrace_iterator *iter; int ret = 0; @@ -882,15 +897,16 @@ ftrace_filter_open(struct inode *inode, struct file *file) if (!iter) return -ENOMEM; - mutex_lock(&ftrace_filter_lock); + mutex_lock(&ftrace_regex_lock); if ((file->f_mode & FMODE_WRITE) && !(file->f_flags & O_APPEND)) - ftrace_filter_reset(); + ftrace_filter_reset(enable); if (file->f_mode & FMODE_READ) { iter->pg = ftrace_pages_start; iter->pos = -1; - iter->flags = FTRACE_ITER_FILTER; + iter->flags = enable ? FTRACE_ITER_FILTER : + FTRACE_ITER_NOTRACE; ret = seq_open(file, &show_ftrace_seq_ops); if (!ret) { @@ -900,13 +916,25 @@ ftrace_filter_open(struct inode *inode, struct file *file) kfree(iter); } else file->private_data = iter; - mutex_unlock(&ftrace_filter_lock); + mutex_unlock(&ftrace_regex_lock); return ret; } +static int +ftrace_filter_open(struct inode *inode, struct file *file) +{ + return ftrace_regex_open(inode, file, 1); +} + +static int +ftrace_notrace_open(struct inode *inode, struct file *file) +{ + return ftrace_regex_open(inode, file, 0); +} + static ssize_t -ftrace_filter_read(struct file *file, char __user *ubuf, +ftrace_regex_read(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { if (file->f_mode & FMODE_READ) @@ -916,7 +944,7 @@ ftrace_filter_read(struct file *file, char __user *ubuf, } static loff_t -ftrace_filter_lseek(struct file *file, loff_t offset, int origin) +ftrace_regex_lseek(struct file *file, loff_t offset, int origin) { loff_t ret; @@ -936,13 +964,14 @@ enum { }; static void -ftrace_match(unsigned char *buff, int len) +ftrace_match(unsigned char *buff, int len, int enable) { char str[KSYM_SYMBOL_LEN]; char *search = NULL; struct ftrace_page *pg; struct dyn_ftrace *rec; int type = MATCH_FULL; + unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; unsigned i, match = 0, search_len = 0; for (i = 0; i < len; i++) { @@ -966,7 +995,8 @@ ftrace_match(unsigned char *buff, int len) /* keep kstop machine from running */ preempt_disable(); - ftrace_filtered = 1; + if (enable) + ftrace_filtered = 1; pg = ftrace_pages_start; while (pg) { for (i = 0; i < pg->index; i++) { @@ -997,7 +1027,7 @@ ftrace_match(unsigned char *buff, int len) break; } if (matched) - rec->flags |= FTRACE_FL_FILTER; + rec->flags |= flag; } pg = pg->next; } @@ -1005,8 +1035,8 @@ ftrace_match(unsigned char *buff, int len) } static ssize_t -ftrace_filter_write(struct file *file, const char __user *ubuf, - size_t cnt, loff_t *ppos) +ftrace_regex_write(struct file *file, const char __user *ubuf, + size_t cnt, loff_t *ppos, int enable) { struct ftrace_iterator *iter; char ch; @@ -1016,7 +1046,7 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, if (!cnt || cnt < 0) return 0; - mutex_lock(&ftrace_filter_lock); + mutex_lock(&ftrace_regex_lock); if (file->f_mode & FMODE_READ) { struct seq_file *m = file->private_data; @@ -1045,7 +1075,6 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, cnt--; } - if (isspace(ch)) { file->f_pos += read; ret = read; @@ -1072,7 +1101,7 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, if (isspace(ch)) { iter->filtered++; iter->buffer[iter->buffer_idx] = 0; - ftrace_match(iter->buffer, iter->buffer_idx); + ftrace_match(iter->buffer, iter->buffer_idx, enable); iter->buffer_idx = 0; } else iter->flags |= FTRACE_ITER_CONT; @@ -1082,11 +1111,39 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, ret = read; out: - mutex_unlock(&ftrace_filter_lock); + mutex_unlock(&ftrace_regex_lock); return ret; } +static ssize_t +ftrace_filter_write(struct file *file, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + return ftrace_regex_write(file, ubuf, cnt, ppos, 1); +} + +static ssize_t +ftrace_notrace_write(struct file *file, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + return ftrace_regex_write(file, ubuf, cnt, ppos, 0); +} + +static void +ftrace_set_regex(unsigned char *buf, int len, int reset, int enable) +{ + if (unlikely(ftrace_disabled)) + return; + + mutex_lock(&ftrace_regex_lock); + if (reset) + ftrace_filter_reset(enable); + if (buf) + ftrace_match(buf, len, enable); + mutex_unlock(&ftrace_regex_lock); +} + /** * ftrace_set_filter - set a function to filter on in ftrace * @buf - the string that holds the function filter text. @@ -1098,24 +1155,31 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, */ void ftrace_set_filter(unsigned char *buf, int len, int reset) { - if (unlikely(ftrace_disabled)) - return; + ftrace_set_regex(buf, len, reset, 1); +} - mutex_lock(&ftrace_filter_lock); - if (reset) - ftrace_filter_reset(); - if (buf) - ftrace_match(buf, len); - mutex_unlock(&ftrace_filter_lock); +/** + * ftrace_set_notrace - set a function to not trace in ftrace + * @buf - the string that holds the function notrace text. + * @len - the length of the string. + * @reset - non zero to reset all filters before applying this filter. + * + * Notrace Filters denote which functions should not be enabled when tracing + * is enabled. If @buf is NULL and reset is set, all functions will be enabled + * for tracing. + */ +void ftrace_set_notrace(unsigned char *buf, int len, int reset) +{ + ftrace_set_regex(buf, len, reset, 0); } static int -ftrace_filter_release(struct inode *inode, struct file *file) +ftrace_regex_release(struct inode *inode, struct file *file, int enable) { struct seq_file *m = (struct seq_file *)file->private_data; struct ftrace_iterator *iter; - mutex_lock(&ftrace_filter_lock); + mutex_lock(&ftrace_regex_lock); if (file->f_mode & FMODE_READ) { iter = m->private; @@ -1126,7 +1190,7 @@ ftrace_filter_release(struct inode *inode, struct file *file) if (iter->buffer_idx) { iter->filtered++; iter->buffer[iter->buffer_idx] = 0; - ftrace_match(iter->buffer, iter->buffer_idx); + ftrace_match(iter->buffer, iter->buffer_idx, enable); } mutex_lock(&ftrace_sysctl_lock); @@ -1137,10 +1201,22 @@ ftrace_filter_release(struct inode *inode, struct file *file) mutex_unlock(&ftrace_sysctl_lock); kfree(iter); - mutex_unlock(&ftrace_filter_lock); + mutex_unlock(&ftrace_regex_lock); return 0; } +static int +ftrace_filter_release(struct inode *inode, struct file *file) +{ + return ftrace_regex_release(inode, file, 1); +} + +static int +ftrace_notrace_release(struct inode *inode, struct file *file) +{ + return ftrace_regex_release(inode, file, 0); +} + static struct file_operations ftrace_avail_fops = { .open = ftrace_avail_open, .read = seq_read, @@ -1150,12 +1226,20 @@ static struct file_operations ftrace_avail_fops = { static struct file_operations ftrace_filter_fops = { .open = ftrace_filter_open, - .read = ftrace_filter_read, + .read = ftrace_regex_read, .write = ftrace_filter_write, - .llseek = ftrace_filter_lseek, + .llseek = ftrace_regex_lseek, .release = ftrace_filter_release, }; +static struct file_operations ftrace_notrace_fops = { + .open = ftrace_notrace_open, + .read = ftrace_regex_read, + .write = ftrace_notrace_write, + .llseek = ftrace_regex_lseek, + .release = ftrace_notrace_release, +}; + /** * ftrace_force_update - force an update to all recording ftrace functions * @@ -1239,6 +1323,12 @@ static __init int ftrace_init_debugfs(void) if (!entry) pr_warning("Could not create debugfs " "'set_ftrace_filter' entry\n"); + + entry = debugfs_create_file("set_ftrace_notrace", 0644, d_tracer, + NULL, &ftrace_notrace_fops); + if (!entry) + pr_warning("Could not create debugfs " + "'set_ftrace_notrace' entry\n"); return 0; } -- cgit v1.2.3 From 9e124fe16ff24746d6de5a2ad685266d7bce0e08 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 26 May 2008 23:31:07 +0100 Subject: xen: Enable console tty by default in domU if it's not a dummy Without console= arguments on the kernel command line, the first console to register becomes enabled and the preferred console (the one behind /dev/console). This is normally tty (assuming CONFIG_VT_CONSOLE is enabled, which it commonly is). This is okay as long tty is a useful console. But unless we have the PV framebuffer, and it is enabled for this domain, tty0 in domU is merely a dummy. In that case, we want the preferred console to be the Xen console hvc0, and we want it without having to fiddle with the kernel command line. Commit b8c2d3dfbc117dff26058fbac316b8acfc2cb5f7 did that for us. Since we now have the PV framebuffer, we want to enable and prefer tty again, but only when PVFB is enabled. But even then we still want to enable the Xen console as well. Problem: when tty registers, we can't yet know whether the PVFB is enabled. By the time we can know (xenstore is up), the console setup game is over. Solution: enable console tty by default, but keep hvc as the preferred console. Change the preferred console to tty when PVFB probes successfully, unless we've been given console kernel parameters. Signed-off-by: Markus Armbruster Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Thomas Gleixner --- arch/x86/xen/enlighten.c | 4 +++- drivers/video/xen-fbfront.c | 25 +++++++++++++++++++++++++ include/linux/console.h | 2 ++ kernel/printk.c | 3 +++ 4 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 1b4b5fa498b3..6cfb708408e9 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -1256,8 +1256,10 @@ asmlinkage void __init xen_start_kernel(void) ? __pa(xen_start_info->mod_start) : 0; boot_params.hdr.ramdisk_size = xen_start_info->mod_len; - if (!is_initial_xendomain()) + if (!is_initial_xendomain()) { + add_preferred_console("tty", 0, NULL); add_preferred_console("hvc", 0, NULL); + } /* Start the world */ start_kernel(); diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 619a6f8d65a2..4e10876e62fc 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -18,6 +18,7 @@ * frame buffer. */ +#include #include #include #include @@ -48,6 +49,7 @@ struct xenfb_info { static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8; +static void xenfb_make_preferred_console(void); static int xenfb_remove(struct xenbus_device *); static void xenfb_init_shared_page(struct xenfb_info *); static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); @@ -348,6 +350,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, if (ret < 0) goto error; + xenfb_make_preferred_console(); return 0; error_nomem: @@ -358,6 +361,28 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, return ret; } +static __devinit void +xenfb_make_preferred_console(void) +{ + struct console *c; + + if (console_set_on_cmdline) + return; + + acquire_console_sem(); + for (c = console_drivers; c; c = c->next) { + if (!strcmp(c->name, "tty") && c->index == 0) + break; + } + release_console_sem(); + if (c) { + unregister_console(c); + c->flags |= CON_CONSDEV; + c->flags &= ~CON_PRINTBUFFER; /* don't print again */ + register_console(c); + } +} + static int xenfb_resume(struct xenbus_device *dev) { struct xenfb_info *info = dev->dev.driver_data; diff --git a/include/linux/console.h b/include/linux/console.h index a4f27fbdf549..248e6e3b9b73 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -108,6 +108,8 @@ struct console { struct console *next; }; +extern int console_set_on_cmdline; + extern int add_preferred_console(char *name, int idx, char *options); extern int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options); extern void register_console(struct console *); diff --git a/kernel/printk.c b/kernel/printk.c index 8fb01c32aa3b..028ed75d4864 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -121,6 +121,8 @@ struct console_cmdline static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; static int selected_console = -1; static int preferred_console = -1; +int console_set_on_cmdline; +EXPORT_SYMBOL(console_set_on_cmdline); /* Flag: console code may call schedule() */ static int console_may_schedule; @@ -890,6 +892,7 @@ static int __init console_setup(char *str) *s = 0; __add_preferred_console(buf, idx, options, brl_options); + console_set_on_cmdline = 1; return 1; } __setup("console=", console_setup); -- cgit v1.2.3 From 0e91398f2a5d4eb6b07df8115917d0d1cf3e9b58 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 26 May 2008 23:31:27 +0100 Subject: xen: implement save/restore This patch implements Xen save/restore and migration. Saving is triggered via xenbus, which is polled in drivers/xen/manage.c. When a suspend request comes in, the kernel prepares itself for saving by: 1 - Freeze all processes. This is primarily to prevent any partially-completed pagetable updates from confusing the suspend process. If CONFIG_PREEMPT isn't defined, then this isn't necessary. 2 - Suspend xenbus and other devices 3 - Stop_machine, to make sure all the other vcpus are quiescent. The Xen tools require the domain to run its save off vcpu0. 4 - Within the stop_machine state, it pins any unpinned pgds (under construction or destruction), performs canonicalizes various other pieces of state (mostly converting mfns to pfns), and finally 5 - Suspend the domain Restore reverses the steps used to save the domain, ending when all the frozen processes are thawed. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Thomas Gleixner --- arch/x86/xen/Makefile | 2 +- arch/x86/xen/enlighten.c | 6 +-- arch/x86/xen/mmu.c | 46 +++++++++++++++++ arch/x86/xen/smp.c | 2 +- arch/x86/xen/suspend.c | 42 +++++++++++++++ arch/x86/xen/time.c | 8 +++ arch/x86/xen/xen-ops.h | 4 ++ drivers/xen/events.c | 83 +++++++++++++++++++++++++++++ drivers/xen/grant-table.c | 4 +- drivers/xen/manage.c | 126 ++++++++++++++++++++++++++++++++++++++++----- include/linux/page-flags.h | 1 + include/xen/events.h | 3 ++ include/xen/grant_table.h | 3 ++ include/xen/xen-ops.h | 9 ++++ 14 files changed, 318 insertions(+), 21 deletions(-) create mode 100644 arch/x86/xen/suspend.c (limited to 'include/linux') diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 40b119b6b103..2ba2d1649131 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -1,4 +1,4 @@ obj-y := enlighten.o setup.o multicalls.o mmu.o \ - time.o xen-asm.o grant-table.o + time.o xen-asm.o grant-table.o suspend.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index ce67dc8f36af..b94f63ac228b 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -857,7 +857,7 @@ static __init void xen_pagetable_setup_start(pgd_t *base) PFN_DOWN(__pa(xen_start_info->pt_base))); } -static __init void setup_shared_info(void) +void xen_setup_shared_info(void) { if (!xen_feature(XENFEAT_auto_translated_physmap)) { unsigned long addr = fix_to_virt(FIX_PARAVIRT_BOOTMAP); @@ -894,7 +894,7 @@ static __init void xen_pagetable_setup_done(pgd_t *base) pv_mmu_ops.release_pmd = xen_release_pmd; pv_mmu_ops.set_pte = xen_set_pte; - setup_shared_info(); + xen_setup_shared_info(); /* Actually pin the pagetable down, but we can't set PG_pinned yet because the page structures don't exist yet. */ @@ -902,7 +902,7 @@ static __init void xen_pagetable_setup_done(pgd_t *base) } /* This is called once we have the cpu_possible_map */ -void __init xen_setup_vcpu_info_placement(void) +void xen_setup_vcpu_info_placement(void) { int cpu; diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 4740cda36563..e95955968ba3 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -560,6 +560,29 @@ void xen_pgd_pin(pgd_t *pgd) xen_mc_issue(0); } +/* + * On save, we need to pin all pagetables to make sure they get their + * mfns turned into pfns. Search the list for any unpinned pgds and pin + * them (unpinned pgds are not currently in use, probably because the + * process is under construction or destruction). + */ +void xen_mm_pin_all(void) +{ + unsigned long flags; + struct page *page; + + spin_lock_irqsave(&pgd_lock, flags); + + list_for_each_entry(page, &pgd_list, lru) { + if (!PagePinned(page)) { + xen_pgd_pin((pgd_t *)page_address(page)); + SetPageSavePinned(page); + } + } + + spin_unlock_irqrestore(&pgd_lock, flags); +} + /* The init_mm pagetable is really pinned as soon as its created, but that's before we have page structures to store the bits. So do all the book-keeping now. */ @@ -617,6 +640,29 @@ static void xen_pgd_unpin(pgd_t *pgd) xen_mc_issue(0); } +/* + * On resume, undo any pinning done at save, so that the rest of the + * kernel doesn't see any unexpected pinned pagetables. + */ +void xen_mm_unpin_all(void) +{ + unsigned long flags; + struct page *page; + + spin_lock_irqsave(&pgd_lock, flags); + + list_for_each_entry(page, &pgd_list, lru) { + if (PageSavePinned(page)) { + BUG_ON(!PagePinned(page)); + printk("unpinning pinned %p\n", page_address(page)); + xen_pgd_unpin((pgd_t *)page_address(page)); + ClearPageSavePinned(page); + } + } + + spin_unlock_irqrestore(&pgd_lock, flags); +} + void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next) { spin_lock(&next->page_table_lock); diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 74ab8968c525..d2e3c20127d7 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -35,7 +35,7 @@ #include "xen-ops.h" #include "mmu.h" -static cpumask_t xen_cpu_initialized_map; +cpumask_t xen_cpu_initialized_map; static DEFINE_PER_CPU(int, resched_irq) = -1; static DEFINE_PER_CPU(int, callfunc_irq) = -1; static DEFINE_PER_CPU(int, debug_irq) = -1; diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c new file mode 100644 index 000000000000..7620a16fe535 --- /dev/null +++ b/arch/x86/xen/suspend.c @@ -0,0 +1,42 @@ +#include + +#include +#include +#include + +#include +#include + +#include "xen-ops.h" +#include "mmu.h" + +void xen_pre_suspend(void) +{ + xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn); + xen_start_info->console.domU.mfn = + mfn_to_pfn(xen_start_info->console.domU.mfn); + + BUG_ON(!irqs_disabled()); + + HYPERVISOR_shared_info = &xen_dummy_shared_info; + if (HYPERVISOR_update_va_mapping(fix_to_virt(FIX_PARAVIRT_BOOTMAP), + __pte_ma(0), 0)) + BUG(); +} + +void xen_post_suspend(int suspend_cancelled) +{ + if (suspend_cancelled) { + xen_start_info->store_mfn = + pfn_to_mfn(xen_start_info->store_mfn); + xen_start_info->console.domU.mfn = + pfn_to_mfn(xen_start_info->console.domU.mfn); + } else { +#ifdef CONFIG_SMP + xen_cpu_initialized_map = cpu_online_map; +#endif + } + + xen_setup_shared_info(); +} + diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index c39e1a5aa241..0bef256e5f2d 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -572,6 +572,14 @@ void xen_setup_cpu_clockevents(void) clockevents_register_device(&__get_cpu_var(xen_clock_events)); } +void xen_time_suspend(void) +{ +} + +void xen_time_resume(void) +{ +} + __init void xen_time_init(void) { int cpu = smp_processor_id(); diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index a1bc89a8f169..a0503acad664 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -9,6 +9,7 @@ extern const char xen_hypervisor_callback[]; extern const char xen_failsafe_callback[]; +struct trap_info; void xen_copy_trap_info(struct trap_info *traps); DECLARE_PER_CPU(unsigned long, xen_cr3); @@ -19,6 +20,7 @@ extern struct shared_info xen_dummy_shared_info; extern struct shared_info *HYPERVISOR_shared_info; void xen_setup_mfn_list_list(void); +void xen_setup_shared_info(void); char * __init xen_memory_setup(void); void __init xen_arch_setup(void); @@ -59,6 +61,8 @@ int xen_smp_call_function_single(int cpu, void (*func) (void *info), void *info, int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, int wait); +extern cpumask_t xen_cpu_initialized_map; + /* Declare an asm function, along with symbols needed to make it inlineable */ diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 70375a690761..73d78dc9b875 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -674,6 +674,89 @@ static int retrigger_dynirq(unsigned int irq) return ret; } +static void restore_cpu_virqs(unsigned int cpu) +{ + struct evtchn_bind_virq bind_virq; + int virq, irq, evtchn; + + for (virq = 0; virq < NR_VIRQS; virq++) { + if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) + continue; + + BUG_ON(irq_info[irq].type != IRQT_VIRQ); + BUG_ON(irq_info[irq].index != virq); + + /* Get a new binding from Xen. */ + bind_virq.virq = virq; + bind_virq.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, + &bind_virq) != 0) + BUG(); + evtchn = bind_virq.port; + + /* Record the new mapping. */ + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); + bind_evtchn_to_cpu(evtchn, cpu); + + /* Ready for use. */ + unmask_evtchn(evtchn); + } +} + +static void restore_cpu_ipis(unsigned int cpu) +{ + struct evtchn_bind_ipi bind_ipi; + int ipi, irq, evtchn; + + for (ipi = 0; ipi < XEN_NR_IPIS; ipi++) { + if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) + continue; + + BUG_ON(irq_info[irq].type != IRQT_IPI); + BUG_ON(irq_info[irq].index != ipi); + + /* Get a new binding from Xen. */ + bind_ipi.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, + &bind_ipi) != 0) + BUG(); + evtchn = bind_ipi.port; + + /* Record the new mapping. */ + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); + bind_evtchn_to_cpu(evtchn, cpu); + + /* Ready for use. */ + unmask_evtchn(evtchn); + + } +} + +void xen_irq_resume(void) +{ + unsigned int cpu, irq, evtchn; + + init_evtchn_cpu_bindings(); + + /* New event-channel space is not 'live' yet. */ + for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) + mask_evtchn(evtchn); + + /* No IRQ <-> event-channel mappings. */ + for (irq = 0; irq < NR_IRQS; irq++) + irq_info[irq].evtchn = 0; /* zap event-channel binding */ + + for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) + evtchn_to_irq[evtchn] = -1; + + for_each_possible_cpu(cpu) { + restore_cpu_virqs(cpu); + restore_cpu_ipis(cpu); + } +} + static struct irq_chip xen_dynamic_chip __read_mostly = { .name = "xen-dyn", .mask = disable_dynirq, diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 52b6b41b909d..e9e11168616a 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -471,14 +471,14 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) return 0; } -static int gnttab_resume(void) +int gnttab_resume(void) { if (max_nr_grant_frames() < nr_grant_frames) return -ENOSYS; return gnttab_map(0, nr_grant_frames - 1); } -static int gnttab_suspend(void) +int gnttab_suspend(void) { arch_gnttab_unmap_shared(shared, nr_grant_frames); return 0; diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index aa7af9e6abc0..ba85fa2cff33 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -5,21 +5,113 @@ #include #include #include +#include +#include #include - -#define SHUTDOWN_INVALID -1 -#define SHUTDOWN_POWEROFF 0 -#define SHUTDOWN_SUSPEND 2 -/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only - * report a crash, not be instructed to crash! - * HALT is the same as POWEROFF, as far as we're concerned. The tools use - * the distinction when we return the reason code to them. - */ -#define SHUTDOWN_HALT 4 +#include +#include +#include +#include + +#include +#include + +enum shutdown_state { + SHUTDOWN_INVALID = -1, + SHUTDOWN_POWEROFF = 0, + SHUTDOWN_SUSPEND = 2, + /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only + report a crash, not be instructed to crash! + HALT is the same as POWEROFF, as far as we're concerned. The tools use + the distinction when we return the reason code to them. */ + SHUTDOWN_HALT = 4, +}; /* Ignore multiple shutdown requests. */ -static int shutting_down = SHUTDOWN_INVALID; +static enum shutdown_state shutting_down = SHUTDOWN_INVALID; + +static int xen_suspend(void *data) +{ + int *cancelled = data; + + BUG_ON(!irqs_disabled()); + + load_cr3(swapper_pg_dir); + + xen_mm_pin_all(); + gnttab_suspend(); + xen_time_suspend(); + xen_pre_suspend(); + + /* + * This hypercall returns 1 if suspend was cancelled + * or the domain was merely checkpointed, and 0 if it + * is resuming in a new domain. + */ + *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); + + xen_post_suspend(*cancelled); + xen_time_resume(); + gnttab_resume(); + xen_mm_unpin_all(); + + if (!*cancelled) { + xen_irq_resume(); + xen_console_resume(); + } + + return 0; +} + +static void do_suspend(void) +{ + int err; + int cancelled = 1; + + shutting_down = SHUTDOWN_SUSPEND; + +#ifdef CONFIG_PREEMPT + /* If the kernel is preemptible, we need to freeze all the processes + to prevent them from being in the middle of a pagetable update + during suspend. */ + err = freeze_processes(); + if (err) { + printk(KERN_ERR "xen suspend: freeze failed %d\n", err); + return; + } +#endif + + err = device_suspend(PMSG_SUSPEND); + if (err) { + printk(KERN_ERR "xen suspend: device_suspend %d\n", err); + goto out; + } + + printk("suspending xenbus...\n"); + /* XXX use normal device tree? */ + xenbus_suspend(); + + err = stop_machine_run(xen_suspend, &cancelled, 0); + if (err) { + printk(KERN_ERR "failed to start xen_suspend: %d\n", err); + goto out; + } + + if (!cancelled) + xenbus_resume(); + else + xenbus_suspend_cancel(); + + device_resume(); + + +out: +#ifdef CONFIG_PREEMPT + thaw_processes(); +#endif + shutting_down = SHUTDOWN_INVALID; +} static void shutdown_handler(struct xenbus_watch *watch, const char **vec, unsigned int len) @@ -52,11 +144,17 @@ static void shutdown_handler(struct xenbus_watch *watch, } if (strcmp(str, "poweroff") == 0 || - strcmp(str, "halt") == 0) + strcmp(str, "halt") == 0) { + shutting_down = SHUTDOWN_POWEROFF; orderly_poweroff(false); - else if (strcmp(str, "reboot") == 0) + } else if (strcmp(str, "reboot") == 0) { + shutting_down = SHUTDOWN_POWEROFF; /* ? */ ctrl_alt_del(); - else { +#ifdef CONFIG_PM_SLEEP + } else if (strcmp(str, "suspend") == 0) { + do_suspend(); +#endif + } else { printk(KERN_INFO "Ignoring shutdown request: %s\n", str); shutting_down = SHUTDOWN_INVALID; } diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 590cff32415d..02955a1c3d76 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -157,6 +157,7 @@ PAGEFLAG(Active, active) __CLEARPAGEFLAG(Active, active) __PAGEFLAG(Slab, slab) PAGEFLAG(Checked, owner_priv_1) /* Used by some filesystems */ PAGEFLAG(Pinned, owner_priv_1) TESTSCFLAG(Pinned, owner_priv_1) /* Xen */ +PAGEFLAG(SavePinned, dirty); /* Xen */ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) diff --git a/include/xen/events.h b/include/xen/events.h index a82ec0c45c38..67c4436554a9 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -41,4 +41,7 @@ static inline void notify_remote_via_evtchn(int port) } extern void notify_remote_via_irq(int irq); + +extern void xen_irq_resume(void); + #endif /* _XEN_EVENTS_H */ diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index 466204846121..a40f1cd91be1 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -51,6 +51,9 @@ struct gnttab_free_callback { u16 count; }; +int gnttab_suspend(void); +int gnttab_resume(void); + int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, int readonly); diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 10ddfe0142d0..5d7a6db54a8c 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -5,4 +5,13 @@ DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); +void xen_pre_suspend(void); +void xen_post_suspend(int suspend_cancelled); + +void xen_mm_pin_all(void); +void xen_mm_unpin_all(void); + +void xen_time_suspend(void); +void xen_time_resume(void); + #endif /* INCLUDE_XEN_OPS_H */ -- cgit v1.2.3 From b1829d2705daa7cb72eb1e08bdc8b7e9fad34266 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 28 May 2008 01:22:08 +0200 Subject: ftrace: fix merge Signed-off-by: Ingo Molnar --- include/linux/ftrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ffbbd54a720e..b482fe88bc04 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -122,7 +122,7 @@ static inline void tracer_disable(void) # define trace_preempt_off(a0, a1) do { } while (0) #endif -#ifdef CONFIG_CONTEXT_SWITCH_TRACER +#ifdef CONFIG_TRACING extern void ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); #else -- cgit v1.2.3 From 57e6983cbde91b4569b4014b933f3a16e12b99fd Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 25 May 2008 23:48:31 -0700 Subject: tg3: Add 5785 ASIC revision This patch added the 5785 device ID and ASIC revision to the code. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: Benjamin Li Signed-off-by: David S. Miller --- drivers/net/tg3.c | 58 ++++++++++++++++++++++++++++++++++++------------- drivers/net/tg3.h | 1 + include/linux/pci_ids.h | 1 + 3 files changed, 45 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 028276edd3bc..69ded90cd96e 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -204,6 +204,7 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5723)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761E)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5785)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)}, {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)}, @@ -5710,7 +5711,8 @@ static int tg3_chip_reset(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) tw32(GRC_FASTBOOT_PC, 0); /* @@ -7004,7 +7006,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) return err; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) { /* This value is determined during the probe time DMA * engine test, tg3_test_dma. */ @@ -7243,7 +7246,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB | RDMAC_MODE_LNGREAD_ENAB); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) rdmac_mode |= RDMAC_MODE_BD_SBD_CRPT_ENAB | RDMAC_MODE_MBUF_RBD_CRPT_ENAB | RDMAC_MODE_MBUF_SBD_CRPT_ENAB; @@ -7411,7 +7415,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) || - (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761)) + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)) val |= WDMAC_MODE_STATUS_TAG_FIX; tw32_f(WDMAC_MODE, val); @@ -7473,7 +7478,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tp->rx_mode = RX_MODE_ENABLE; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) tp->rx_mode |= RX_MODE_IPV6_CSUM_ENABLE; tw32_f(MAC_RX_MODE, tp->rx_mode); @@ -9028,7 +9035,10 @@ static int tg3_set_tso(struct net_device *dev, u32 value) (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) { if (value) { dev->features |= NETIF_F_TSO6; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 && + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) dev->features |= NETIF_F_TSO_ECN; } else dev->features &= ~(NETIF_F_TSO6 | NETIF_F_TSO_ECN); @@ -9286,7 +9296,8 @@ static int tg3_set_tx_csum(struct net_device *dev, u32 data) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) ethtool_op_set_tx_ipv6_csum(dev, data); else ethtool_op_set_tx_csum(dev, data); @@ -9807,7 +9818,8 @@ static int tg3_test_memory(struct tg3 *tp) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) mem_tbl = mem_tbl_5755; else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) mem_tbl = mem_tbl_5906; @@ -10014,7 +10026,8 @@ static int tg3_test_loopback(struct tg3 *tp) return TG3_LOOPBACK_FAILED; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { int i; u32 status; @@ -10042,7 +10055,8 @@ static int tg3_test_loopback(struct tg3 *tp) err |= TG3_MAC_LOOPBACK_FAILED; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { tw32(TG3_CPMU_CTRL, cpmuctrl); /* Release the mutex */ @@ -10690,7 +10704,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp) else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) tg3_get_5755_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) tg3_get_5787_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) tg3_get_5761_nvram_info(tp); @@ -11021,6 +11036,7 @@ static int tg3_nvram_write_block_buffered(struct tg3 *tp, u32 offset, u32 len, (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) && + (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) && (tp->nvram_jedecnum == JEDEC_ST) && (nvram_cmd & NVRAM_CMD_FIRST)) { @@ -11924,6 +11940,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) tp->tg3_flags2 |= TG3_FLG2_5750_PLUS; @@ -11945,6 +11962,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; @@ -12147,7 +12165,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) } if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT; if (tp->pci_chip_rev_id == CHIPREV_ID_5784_A0 || @@ -12231,7 +12250,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG; if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5755M) tp->tg3_flags2 |= TG3_FLG2_PHY_ADJUST_TRIM; - } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; } @@ -12252,6 +12272,9 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) tp->coalesce_mode |= HOSTCC_MODE_32BYTE; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) + tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB; + err = tg3_mdio_init(tp); if (err) return err; @@ -12383,6 +12406,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) tp->dev->hard_start_xmit = tg3_start_xmit; else @@ -13307,7 +13331,10 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) dev->features |= NETIF_F_TSO6; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 && + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) dev->features |= NETIF_F_TSO_ECN; } @@ -13373,7 +13400,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) dev->features |= NETIF_F_IPV6_CSUM; tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS; diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 48f45c17f60d..95ac4c9590a1 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -128,6 +128,7 @@ #define ASIC_REV_USE_PROD_ID_REG 0x0f #define ASIC_REV_5784 0x5784 #define ASIC_REV_5761 0x5761 +#define ASIC_REV_5785 0x5785 #define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) #define CHIPREV_5700_AX 0x70 #define CHIPREV_5700_BX 0x71 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index cf6dbd759395..72c038560e7d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1979,6 +1979,7 @@ #define PCI_DEVICE_ID_TIGON3_5787M 0x1693 #define PCI_DEVICE_ID_TIGON3_5782 0x1696 #define PCI_DEVICE_ID_TIGON3_5784 0x1698 +#define PCI_DEVICE_ID_TIGON3_5785 0x1699 #define PCI_DEVICE_ID_TIGON3_5786 0x169a #define PCI_DEVICE_ID_TIGON3_5787 0x169b #define PCI_DEVICE_ID_TIGON3_5788 0x169c -- cgit v1.2.3 From a9daf36746b1fb5c2db8d164ca70c30c63a0d7b2 Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 25 May 2008 23:49:44 -0700 Subject: tg3: Add shmem options. This patch adds some options obtained through shared memory. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: Benjamin Li Signed-off-by: David S. Miller --- drivers/net/tg3.c | 95 +++++++++++++++++++++++++++++++++++++++++++++---- drivers/net/tg3.h | 30 +++++++++++++++- include/linux/brcmphy.h | 6 ++++ 3 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 include/linux/brcmphy.h (limited to 'include/linux') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 69ded90cd96e..365fcffc3144 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -869,6 +870,51 @@ static int tg3_mdio_reset(struct mii_bus *bp) return 0; } +static void tg3_mdio_config(struct tg3 *tp) +{ + u32 val; + + if (tp->mdio_bus.phy_map[PHY_ADDR]->interface != + PHY_INTERFACE_MODE_RGMII) + return; + + val = tr32(MAC_PHYCFG1) & ~(MAC_PHYCFG1_RGMII_EXT_RX_DEC | + MAC_PHYCFG1_RGMII_SND_STAT_EN); + if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) { + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) + val |= MAC_PHYCFG1_RGMII_EXT_RX_DEC; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN) + val |= MAC_PHYCFG1_RGMII_SND_STAT_EN; + } + tw32(MAC_PHYCFG1, val | MAC_PHYCFG1_RGMII_INT | MAC_PHYCFG1_TXC_DRV); + + val = tr32(MAC_PHYCFG2) & ~(MAC_PHYCFG2_INBAND_ENABLE); + if (!(tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE)) + val |= MAC_PHYCFG2_INBAND_ENABLE; + tw32(MAC_PHYCFG2, val); + + val = tr32(MAC_EXT_RGMII_MODE); + val &= ~(MAC_RGMII_MODE_RX_INT_B | + MAC_RGMII_MODE_RX_QUALITY | + MAC_RGMII_MODE_RX_ACTIVITY | + MAC_RGMII_MODE_RX_ENG_DET | + MAC_RGMII_MODE_TX_ENABLE | + MAC_RGMII_MODE_TX_LOWPWR | + MAC_RGMII_MODE_TX_RESET); + if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) { + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) + val |= MAC_RGMII_MODE_RX_INT_B | + MAC_RGMII_MODE_RX_QUALITY | + MAC_RGMII_MODE_RX_ACTIVITY | + MAC_RGMII_MODE_RX_ENG_DET; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN) + val |= MAC_RGMII_MODE_TX_ENABLE | + MAC_RGMII_MODE_TX_LOWPWR | + MAC_RGMII_MODE_TX_RESET; + } + tw32(MAC_EXT_RGMII_MODE, val); +} + static void tg3_mdio_start(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { @@ -880,6 +926,9 @@ static void tg3_mdio_start(struct tg3 *tp) tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); + + if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) + tg3_mdio_config(tp); } static void tg3_mdio_stop(struct tg3 *tp) @@ -895,6 +944,7 @@ static int tg3_mdio_init(struct tg3 *tp) { int i; u32 reg; + struct phy_device *phydev; struct mii_bus *mdio_bus = &tp->mdio_bus; tg3_mdio_start(tp); @@ -928,13 +978,34 @@ static int tg3_mdio_init(struct tg3 *tp) tg3_bmcr_reset(tp); i = mdiobus_register(mdio_bus); - if (!i) - tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED; - else + if (i) { printk(KERN_WARNING "%s: mdiobus_reg failed (0x%x)\n", tp->dev->name, i); + return i; + } - return i; + tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED; + + phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + + switch (phydev->phy_id) { + case TG3_PHY_ID_BCM50610: + phydev->interface = PHY_INTERFACE_MODE_RGMII; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) + phydev->dev_flags |= PHY_BRCM_STD_IBND_DISABLE; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) + phydev->dev_flags |= PHY_BRCM_EXT_IBND_RX_ENABLE; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN) + phydev->dev_flags |= PHY_BRCM_EXT_IBND_TX_ENABLE; + break; + case TG3_PHY_ID_BCMAC131: + phydev->interface = PHY_INTERFACE_MODE_MII; + break; + } + + tg3_mdio_config(tp); + + return 0; } static void tg3_mdio_fini(struct tg3 *tp) @@ -1238,8 +1309,8 @@ static int tg3_phy_init(struct tg3 *tp) phydev = tp->mdio_bus.phy_map[PHY_ADDR]; /* Attach the MAC to the PHY. */ - phydev = phy_connect(tp->dev, phydev->dev.bus_id, - tg3_adjust_link, 0, phydev->interface); + phydev = phy_connect(tp->dev, phydev->dev.bus_id, tg3_adjust_link, + phydev->dev_flags, phydev->interface); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", tp->dev->name); return PTR_ERR(phydev); @@ -11219,7 +11290,7 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg, led_cfg; - u32 nic_phy_id, ver, cfg2 = 0, eeprom_phy_id; + u32 nic_phy_id, ver, cfg2 = 0, cfg4 = 0, eeprom_phy_id; int eeprom_phy_serdes = 0; tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); @@ -11233,6 +11304,9 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) (ver > 0) && (ver < 0x100)) tg3_read_mem(tp, NIC_SRAM_DATA_CFG_2, &cfg2); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) + tg3_read_mem(tp, NIC_SRAM_DATA_CFG_4, &cfg4); + if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) eeprom_phy_serdes = 1; @@ -11357,6 +11431,13 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) if (cfg3 & NIC_SRAM_ASPM_DEBOUNCE) tp->tg3_flags |= TG3_FLAG_ASPM_WORKAROUND; } + + if (cfg4 & NIC_SRAM_RGMII_STD_IBND_DISABLE) + tp->tg3_flags3 |= TG3_FLG3_RGMII_STD_IBND_DISABLE; + if (cfg4 & NIC_SRAM_RGMII_EXT_IBND_RX_EN) + tp->tg3_flags3 |= TG3_FLG3_RGMII_EXT_IBND_RX_EN; + if (cfg4 & NIC_SRAM_RGMII_EXT_IBND_TX_EN) + tp->tg3_flags3 |= TG3_FLG3_RGMII_EXT_IBND_TX_EN; } } diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 95ac4c9590a1..df07842172b7 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -529,7 +529,23 @@ #define MAC_SERDES_CFG 0x00000590 #define MAC_SERDES_CFG_EDGE_SELECT 0x00001000 #define MAC_SERDES_STAT 0x00000594 -/* 0x598 --> 0x5b0 unused */ +/* 0x598 --> 0x5a0 unused */ +#define MAC_PHYCFG1 0x000005a0 +#define MAC_PHYCFG1_RGMII_INT 0x00000001 +#define MAC_PHYCFG1_RGMII_EXT_RX_DEC 0x02000000 +#define MAC_PHYCFG1_RGMII_SND_STAT_EN 0x04000000 +#define MAC_PHYCFG1_TXC_DRV 0x20000000 +#define MAC_PHYCFG2 0x000005a4 +#define MAC_PHYCFG2_INBAND_ENABLE 0x00000001 +#define MAC_EXT_RGMII_MODE 0x000005a8 +#define MAC_RGMII_MODE_TX_ENABLE 0x00000001 +#define MAC_RGMII_MODE_TX_LOWPWR 0x00000002 +#define MAC_RGMII_MODE_TX_RESET 0x00000004 +#define MAC_RGMII_MODE_RX_INT_B 0x00000100 +#define MAC_RGMII_MODE_RX_QUALITY 0x00000200 +#define MAC_RGMII_MODE_RX_ACTIVITY 0x00000400 +#define MAC_RGMII_MODE_RX_ENG_DET 0x00000800 +/* 0x5ac --> 0x5b0 unused */ #define SERDES_RX_CTRL 0x000005b0 /* 5780/5714 only */ #define SERDES_RX_SIG_DETECT 0x00000400 #define SG_DIG_CTRL 0x000005b0 @@ -1715,6 +1731,12 @@ #define NIC_SRAM_DATA_CFG_3 0x00000d3c #define NIC_SRAM_ASPM_DEBOUNCE 0x00000002 +#define NIC_SRAM_DATA_CFG_4 0x00000d60 +#define NIC_SRAM_GMII_MODE 0x00000002 +#define NIC_SRAM_RGMII_STD_IBND_DISABLE 0x00000004 +#define NIC_SRAM_RGMII_EXT_IBND_RX_EN 0x00000008 +#define NIC_SRAM_RGMII_EXT_IBND_TX_EN 0x00000010 + #define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000 #define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000 @@ -2486,6 +2508,9 @@ struct tg3 { #define TG3_FLG3_MDIOBUS_INITED 0x00000020 #define TG3_FLG3_MDIOBUS_PAUSED 0x00000040 #define TG3_FLG3_PHY_CONNECTED 0x00000080 +#define TG3_FLG3_RGMII_STD_IBND_DISABLE 0x00000100 +#define TG3_FLG3_RGMII_EXT_IBND_RX_EN 0x00000200 +#define TG3_FLG3_RGMII_EXT_IBND_TX_EN 0x00000400 struct timer_list timer; u16 timer_counter; @@ -2556,6 +2581,9 @@ struct tg3 { #define PHY_REV_BCM5401_B2 0x3 #define PHY_REV_BCM5401_C0 0x6 #define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ +#define TG3_PHY_ID_BCM50610 0x143bd60 +#define TG3_PHY_ID_BCMAC131 0x143bc70 + u32 led_ctrl; u32 phy_otp; diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h new file mode 100644 index 000000000000..9b64b6d67873 --- /dev/null +++ b/include/linux/brcmphy.h @@ -0,0 +1,6 @@ +#define PHY_BRCM_WIRESPEED_ENABLE 0x00000001 +#define PHY_BRCM_AUTO_PWRDWN_ENABLE 0x00000002 +#define PHY_BRCM_APD_CLK125_ENABLE 0x00000004 +#define PHY_BRCM_STD_IBND_DISABLE 0x00000008 +#define PHY_BRCM_EXT_IBND_RX_ENABLE 0x00000010 +#define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00000020 -- cgit v1.2.3 From 75a9cd524c6bb441c61c85bae7020ce5e8b2e807 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 29 May 2008 01:43:48 -0700 Subject: wanrouter: Fix ioctl handler declaration. Signed-off-by: David S. Miller --- include/linux/wanrouter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h index 3add87465b1f..e0aa39612eba 100644 --- a/include/linux/wanrouter.h +++ b/include/linux/wanrouter.h @@ -522,7 +522,7 @@ extern int wanrouter_proc_init(void); extern void wanrouter_proc_cleanup(void); extern int wanrouter_proc_add(struct wan_device *wandev); extern int wanrouter_proc_delete(struct wan_device *wandev); -extern int wanrouter_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +extern long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* Public Data */ /* list of registered devices */ -- cgit v1.2.3 From b79eeeb9e48457579cb742cd02e162fcd673c4a3 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Thu, 29 May 2008 03:25:23 -0700 Subject: tcp: Reorganize tcp_sock to fill 64-bit holes & improve locality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I tried to group recovery related fields nearby (non-CA_Open related variables, to be more accurate) so that one to three cachelines would not be necessary in CA_Open. These are now contiguously deployed: struct sk_buff_head out_of_order_queue; /* 1968 80 */ /* --- cacheline 32 boundary (2048 bytes) --- */ struct tcp_sack_block duplicate_sack[1]; /* 2048 8 */ struct tcp_sack_block selective_acks[4]; /* 2056 32 */ struct tcp_sack_block recv_sack_cache[4]; /* 2088 32 */ /* --- cacheline 33 boundary (2112 bytes) was 8 bytes ago --- */ struct sk_buff * highest_sack; /* 2120 8 */ int lost_cnt_hint; /* 2128 4 */ int retransmit_cnt_hint; /* 2132 4 */ u32 lost_retrans_low; /* 2136 4 */ u8 reordering; /* 2140 1 */ u8 keepalive_probes; /* 2141 1 */ /* XXX 2 bytes hole, try to pack */ u32 prior_ssthresh; /* 2144 4 */ u32 high_seq; /* 2148 4 */ u32 retrans_stamp; /* 2152 4 */ u32 undo_marker; /* 2156 4 */ int undo_retrans; /* 2160 4 */ u32 total_retrans; /* 2164 4 */ ...and they're then followed by URG slowpath & keepalive related variables. Head of the out_of_order_queue always needed for empty checks, if that's empty (and TCP is in CA_Open), following ~200 bytes (in 64-bit) shouldn't be necessary for anything. If only OFO queue exists but TCP is in CA_Open, selective_acks (and possibly duplicate_sack) are necessary besides the out_of_order_queue but the rest of the block again shouldn't be (ie., the other direction had losses). As the cacheline boundaries depend on many factors in the preceeding stuff, trying to align considering them doesn't make too much sense. Commented one ordering hazard. There are number of low utilized u8/16s that could be combined get 2 bytes less in total so that the hole could be made to vanish (includes at least ecn_flags, urg_data, urg_mode, frto_counter, nonagle). Signed-off-by: Ilpo Järvinen Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 18e62e3d406f..9881295f3857 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -296,10 +296,9 @@ struct tcp_sock { u32 rcv_ssthresh; /* Current window clamp */ u32 frto_highmark; /* snd_nxt when RTO occurred */ - u8 reordering; /* Packet reordering metric. */ + u16 advmss; /* Advertised MSS */ u8 frto_counter; /* Number of new acks after RTO */ u8 nonagle; /* Disable Nagle algorithm? */ - u8 keepalive_probes; /* num of allowed keep alive probes */ /* RTT measurement */ u32 srtt; /* smoothed round trip time << 3 */ @@ -310,6 +309,10 @@ struct tcp_sock { u32 packets_out; /* Packets which are "in flight" */ u32 retrans_out; /* Retransmitted packets out */ + + u16 urg_data; /* Saved octet of OOB data and control flags */ + u8 urg_mode; /* In urgent mode */ + u8 ecn_flags; /* ECN status bits. */ /* * Options received (usually on last packet, some only on SYN packets). */ @@ -325,13 +328,24 @@ struct tcp_sock { u32 snd_cwnd_used; u32 snd_cwnd_stamp; - struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ - u32 rcv_wnd; /* Current receiver window */ u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ u32 pushed_seq; /* Last pushed seq, required to talk to windows */ + u32 lost_out; /* Lost packets */ + u32 sacked_out; /* SACK'd packets */ + u32 fackets_out; /* FACK'd packets */ + u32 tso_deferred; + u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */ -/* SACKs data */ + /* from STCP, retrans queue hinting */ + struct sk_buff* lost_skb_hint; + struct sk_buff *scoreboard_skb_hint; + struct sk_buff *retransmit_skb_hint; + struct sk_buff *forward_skb_hint; + + struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ + + /* SACKs data, these 2 need to be together (see tcp_build_and_update_options) */ struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */ struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/ @@ -342,23 +356,14 @@ struct tcp_sock { * sacked_out > 0) */ - /* from STCP, retrans queue hinting */ - struct sk_buff* lost_skb_hint; - - struct sk_buff *scoreboard_skb_hint; - struct sk_buff *retransmit_skb_hint; - struct sk_buff *forward_skb_hint; - int lost_cnt_hint; int retransmit_cnt_hint; u32 lost_retrans_low; /* Sent seq after any rxmit (lowest) */ - u16 advmss; /* Advertised MSS */ + u8 reordering; /* Packet reordering metric. */ + u8 keepalive_probes; /* num of allowed keep alive probes */ u32 prior_ssthresh; /* ssthresh saved at recovery start */ - u32 lost_out; /* Lost packets */ - u32 sacked_out; /* SACK'd packets */ - u32 fackets_out; /* FACK'd packets */ u32 high_seq; /* snd_nxt at onset of congestion */ u32 retrans_stamp; /* Timestamp of the last retransmit, @@ -366,25 +371,18 @@ struct tcp_sock { * the first SYN. */ u32 undo_marker; /* tracking retrans started here. */ int undo_retrans; /* number of undoable retransmissions. */ + u32 total_retrans; /* Total retransmits for entire connection */ + u32 urg_seq; /* Seq of received urgent pointer */ - u16 urg_data; /* Saved octet of OOB data and control flags */ - u8 urg_mode; /* In urgent mode */ - u8 ecn_flags; /* ECN status bits. */ u32 snd_up; /* Urgent pointer */ - u32 total_retrans; /* Total retransmits for entire connection */ - u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */ - unsigned int keepalive_time; /* time before keep alive takes place */ unsigned int keepalive_intvl; /* time interval between keep alive probes */ - int linger2; struct tcp_deferred_accept_info defer_tcp_accept; unsigned long last_synq_overflow; - u32 tso_deferred; - /* Receiver side RTT estimation */ struct { u32 rtt; @@ -412,6 +410,8 @@ struct tcp_sock { /* TCP MD5 Signagure Option information */ struct tcp_md5sig_info *md5sig_info; #endif + + int linger2; }; static inline struct tcp_sock *tcp_sk(const struct sock *sk) -- cgit v1.2.3 From f6d65610df3bd4e7138da03aec391224219df135 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 2 Jun 2008 00:39:45 -0400 Subject: Input: atkbd - use ushort instead of uchar keymap Since some of the keycodes defined in input.h have values greater than 255 we should use unsigned shorts in keymaps. Tested-by: Carlos Corbacho Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 20 ++++++++++---------- include/linux/input.h | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 4a95adc4cc78..56857d1e56d5 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -68,7 +68,7 @@ MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and * are loadable via an userland utility. */ -static unsigned char atkbd_set2_keycode[512] = { +static const unsigned short atkbd_set2_keycode[512] = { #ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES @@ -99,7 +99,7 @@ static unsigned char atkbd_set2_keycode[512] = { #endif }; -static unsigned char atkbd_set3_keycode[512] = { +static const unsigned short atkbd_set3_keycode[512] = { 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, @@ -115,7 +115,7 @@ static unsigned char atkbd_set3_keycode[512] = { 148,149,147,140 }; -static unsigned char atkbd_unxlate_table[128] = { +static const unsigned short atkbd_unxlate_table[128] = { 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, @@ -161,7 +161,7 @@ static unsigned char atkbd_unxlate_table[128] = { #define ATKBD_SCR_LEFT 249 #define ATKBD_SCR_RIGHT 248 -#define ATKBD_SPECIAL 248 +#define ATKBD_SPECIAL ATKBD_SCR_RIGHT #define ATKBD_LED_EVENT_BIT 0 #define ATKBD_REP_EVENT_BIT 1 @@ -173,7 +173,7 @@ static unsigned char atkbd_unxlate_table[128] = { #define ATKBD_XL_HANGEUL 0x10 #define ATKBD_XL_HANJA 0x20 -static struct { +static const struct { unsigned char keycode; unsigned char set2; } atkbd_scroll_keys[] = { @@ -200,7 +200,7 @@ struct atkbd { char phys[32]; unsigned short id; - unsigned char keycode[512]; + unsigned short keycode[512]; DECLARE_BITMAP(force_release_mask, 512); unsigned char set; unsigned char translated; @@ -357,7 +357,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, unsigned int code = data; int scroll = 0, hscroll = 0, click = -1; int value; - unsigned char keycode; + unsigned short keycode; #ifdef ATKBD_DEBUG printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags); @@ -959,16 +959,16 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) input_dev->evbit[0] |= BIT_MASK(EV_REL); input_dev->relbit[0] = BIT_MASK(REL_WHEEL) | BIT_MASK(REL_HWHEEL); - set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); } input_dev->keycode = atkbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodesize = sizeof(unsigned short); input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); for (i = 0; i < 512; i++) if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL) - set_bit(atkbd->keycode[i], input_dev->keybit); + __set_bit(atkbd->keycode[i], input_dev->keybit); } /* diff --git a/include/linux/input.h b/include/linux/input.h index eff711d8a459..af9c6314153b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -373,6 +373,8 @@ struct input_absinfo { #define KEY_WIMAX 246 +/* Range 248 - 255 is reserved for special needs of AT keyboard driver */ + #define BTN_MISC 0x100 #define BTN_0 0x100 #define BTN_1 0x101 -- cgit v1.2.3 From 70ef6d595b6e51618a0cbe44b848d8c9db11a010 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 29 May 2008 18:41:04 +0800 Subject: x86: get irq for hpet timer HPET timer's IRQ is 0 by default. So we have to select which irq will be used by these timers. We wait to set the timer's irq until we really open it in order to reduce the chance of conflicting with other device. Signed-off-by: Kevin Hao Signed-off-by: Ingo Molnar --- drivers/char/hpet.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hpet.h | 3 ++- 2 files changed, 65 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index e7fb0bca3667..c9bf5d44402d 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -184,6 +184,67 @@ static irqreturn_t hpet_interrupt(int irq, void *data) return IRQ_HANDLED; } +static void hpet_timer_set_irq(struct hpet_dev *devp) +{ + unsigned long v; + int irq, gsi; + struct hpet_timer __iomem *timer; + + spin_lock_irq(&hpet_lock); + if (devp->hd_hdwirq) { + spin_unlock_irq(&hpet_lock); + return; + } + + timer = devp->hd_timer; + + /* we prefer level triggered mode */ + v = readl(&timer->hpet_config); + if (!(v & Tn_INT_TYPE_CNF_MASK)) { + v |= Tn_INT_TYPE_CNF_MASK; + writel(v, &timer->hpet_config); + } + spin_unlock_irq(&hpet_lock); + + v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >> + Tn_INT_ROUTE_CAP_SHIFT; + + /* + * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by + * legacy device. In IO APIC mode, we skip all the legacy IRQS. + */ + if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) + v &= ~0xf3df; + else + v &= ~0xffff; + + for (irq = find_first_bit(&v, HPET_MAX_IRQ); irq < HPET_MAX_IRQ; + irq = find_next_bit(&v, HPET_MAX_IRQ, 1 + irq)) { + + if (irq >= NR_IRQS) { + irq = HPET_MAX_IRQ; + break; + } + + gsi = acpi_register_gsi(irq, ACPI_LEVEL_SENSITIVE, + ACPI_ACTIVE_LOW); + if (gsi > 0) + break; + + /* FIXME: Setup interrupt source table */ + } + + if (irq < HPET_MAX_IRQ) { + spin_lock_irq(&hpet_lock); + v = readl(&timer->hpet_config); + v |= irq << Tn_INT_ROUTE_CNF_SHIFT; + writel(v, &timer->hpet_config); + devp->hd_hdwirq = gsi; + spin_unlock_irq(&hpet_lock); + } + return; +} + static int hpet_open(struct inode *inode, struct file *file) { struct hpet_dev *devp; @@ -215,6 +276,8 @@ static int hpet_open(struct inode *inode, struct file *file) devp->hd_flags |= HPET_OPEN; spin_unlock_irq(&hpet_lock); + hpet_timer_set_irq(devp); + return 0; } diff --git a/include/linux/hpet.h b/include/linux/hpet.h index 2dc29ce6c8e4..6d2626b63a9a 100644 --- a/include/linux/hpet.h +++ b/include/linux/hpet.h @@ -37,6 +37,7 @@ struct hpet { #define hpet_compare _u1._hpet_compare #define HPET_MAX_TIMERS (32) +#define HPET_MAX_IRQ (32) /* * HPET general capabilities register @@ -64,7 +65,7 @@ struct hpet { */ #define Tn_INT_ROUTE_CAP_MASK (0xffffffff00000000ULL) -#define Tn_INI_ROUTE_CAP_SHIFT (32UL) +#define Tn_INT_ROUTE_CAP_SHIFT (32UL) #define Tn_FSB_INT_DELCAP_MASK (0x8000UL) #define Tn_FSB_INT_DELCAP_SHIFT (15) #define Tn_FSB_EN_CNF_MASK (0x4000UL) -- cgit v1.2.3 From ad90c0e3ce8d20d6873b57e36181ef6d7a0097fe Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 27 May 2008 20:48:37 -0400 Subject: ftrace: user update and disable dynamic ftrace daemon In dynamic ftrace, the mcount function starts off pointing to a stub function that just returns. On start up, the call to the stub is modified to point to a "record_ip" function. The job of the record_ip function is to add the function to a pre-allocated hash list. If the function is already there, it simply is ignored, otherwise it is added to the list. Later, a ftraced daemon wakes up and calls kstop_machine if any functions have been recorded, and changes the calls to the recorded functions to a simple nop. If no functions were recorded, the daemon goes back to sleep. The daemon wakes up once a second to see if it needs to update any newly recorded functions into nops. Usually it does not, but if a lot of code has been executed for the first time in the kernel, the ftraced daemon will call kstop_machine to update those into nops. The problem currently is that there's no way to stop the daemon from doing this, and it can cause unneeded latencies (800us which for some is bothersome). This patch adds a new file /debugfs/tracing/ftraced_enabled. If the daemon is active, reading this will return "enabled\n" and "disabled\n" when the daemon is not running. To disable the daemon, the user can echo "0" or "disable" into this file, and "1" or "enable" to re-enable the daemon. Since the daemon is used to convert the functions into nops to increase the performance of the system, I also added that anytime something is written into the ftraced_enabled file, kstop_machine will run if there are new functions that have been detected that need to be converted. This way the user can disable the daemon but still be able to control the conversion of the mcount calls to nops by simply, "echo 0 > /debugfs/tracing/ftraced_enabled" when they need to do more conversions. To see the number of converted functions: "cat /debugfs/tracing/dyn_ftrace_total_info" Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar --- include/linux/ftrace.h | 6 ++ kernel/trace/ftrace.c | 157 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 116 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index b482fe88bc04..623819433ed5 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -72,9 +72,15 @@ extern int ftrace_update_ftrace_func(ftrace_func_t func); extern void ftrace_caller(void); extern void ftrace_call(void); extern void mcount_call(void); + +void ftrace_disable_daemon(void); +void ftrace_enable_daemon(void); + #else # define ftrace_force_update() ({ 0; }) # define ftrace_set_filter(buf, len, reset) do { } while (0) +# define ftrace_disable_daemon() do { } while (0) +# define ftrace_enable_daemon() do { } while (0) #endif /* totally disable ftrace - can not re-enable after this */ diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1843edc098a6..f762f5a2d331 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -151,8 +151,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) #ifdef CONFIG_DYNAMIC_FTRACE static struct task_struct *ftraced_task; -static DECLARE_WAIT_QUEUE_HEAD(ftraced_waiters); -static unsigned long ftraced_iteration_counter; enum { FTRACE_ENABLE_CALLS = (1 << 0), @@ -189,6 +187,7 @@ static struct ftrace_page *ftrace_pages; static int ftraced_trigger; static int ftraced_suspend; +static int ftraced_stop; static int ftrace_record_suspend; @@ -474,14 +473,21 @@ ftrace_code_disable(struct dyn_ftrace *rec) return 1; } +static int __ftrace_update_code(void *ignore); + static int __ftrace_modify_code(void *data) { unsigned long addr; int *command = data; - if (*command & FTRACE_ENABLE_CALLS) + if (*command & FTRACE_ENABLE_CALLS) { + /* + * Update any recorded ips now that we have the + * machine stopped + */ + __ftrace_update_code(NULL); ftrace_replace_code(1); - else if (*command & FTRACE_DISABLE_CALLS) + } else if (*command & FTRACE_DISABLE_CALLS) ftrace_replace_code(0); if (*command & FTRACE_UPDATE_TRACE_FUNC) @@ -503,6 +509,25 @@ static void ftrace_run_update_code(int command) stop_machine_run(__ftrace_modify_code, &command, NR_CPUS); } +void ftrace_disable_daemon(void) +{ + /* Stop the daemon from calling kstop_machine */ + mutex_lock(&ftraced_lock); + ftraced_stop = 1; + mutex_unlock(&ftraced_lock); + + ftrace_force_update(); +} + +void ftrace_enable_daemon(void) +{ + mutex_lock(&ftraced_lock); + ftraced_stop = 0; + mutex_unlock(&ftraced_lock); + + ftrace_force_update(); +} + static ftrace_func_t saved_ftrace_func; static void ftrace_startup(void) @@ -603,6 +628,7 @@ static int __ftrace_update_code(void *ignore) int i; /* Don't be recording funcs now */ + ftrace_record_suspend++; save_ftrace_enabled = ftrace_enabled; ftrace_enabled = 0; @@ -628,18 +654,23 @@ static int __ftrace_update_code(void *ignore) stop = ftrace_now(raw_smp_processor_id()); ftrace_update_time = stop - start; ftrace_update_tot_cnt += ftrace_update_cnt; + ftraced_trigger = 0; ftrace_enabled = save_ftrace_enabled; + ftrace_record_suspend--; return 0; } -static void ftrace_update_code(void) +static int ftrace_update_code(void) { - if (unlikely(ftrace_disabled)) - return; + if (unlikely(ftrace_disabled) || + !ftrace_enabled || !ftraced_trigger) + return 0; stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); + + return 1; } static int ftraced(void *ignore) @@ -658,14 +689,13 @@ static int ftraced(void *ignore) mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftraced_lock); - if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { - ftrace_record_suspend++; - ftrace_update_code(); + if (!ftraced_suspend && !ftraced_stop && + ftrace_update_code()) { usecs = nsecs_to_usecs(ftrace_update_time); if (ftrace_update_tot_cnt > 100000) { ftrace_update_tot_cnt = 0; pr_info("hm, dftrace overflow: %lu change%s" - " (%lu total) in %lu usec%s\n", + " (%lu total) in %lu usec%s\n", ftrace_update_cnt, ftrace_update_cnt != 1 ? "s" : "", ftrace_update_tot_cnt, @@ -673,15 +703,10 @@ static int ftraced(void *ignore) ftrace_disabled = 1; WARN_ON_ONCE(1); } - ftraced_trigger = 0; - ftrace_record_suspend--; } - ftraced_iteration_counter++; mutex_unlock(&ftraced_lock); mutex_unlock(&ftrace_sysctl_lock); - wake_up_interruptible(&ftraced_waiters); - ftrace_shutdown_replenish(); } __set_current_state(TASK_RUNNING); @@ -1219,6 +1244,55 @@ ftrace_notrace_release(struct inode *inode, struct file *file) return ftrace_regex_release(inode, file, 0); } +static ssize_t +ftraced_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + /* don't worry about races */ + char *buf = ftraced_stop ? "disabled\n" : "enabled\n"; + int r = strlen(buf); + + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t +ftraced_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char buf[64]; + long val; + int ret; + + if (cnt >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + + if (strncmp(buf, "enable", 6) == 0) + val = 1; + else if (strncmp(buf, "disable", 7) == 0) + val = 0; + else { + buf[cnt] = 0; + + ret = strict_strtoul(buf, 10, &val); + if (ret < 0) + return ret; + + val = !!val; + } + + if (val) + ftrace_enable_daemon(); + else + ftrace_disable_daemon(); + + filp->f_pos += cnt; + + return cnt; +} + static struct file_operations ftrace_avail_fops = { .open = ftrace_avail_open, .read = seq_read, @@ -1242,51 +1316,34 @@ static struct file_operations ftrace_notrace_fops = { .release = ftrace_notrace_release, }; +static struct file_operations ftraced_fops = { + .open = tracing_open_generic, + .read = ftraced_read, + .write = ftraced_write, +}; + /** * ftrace_force_update - force an update to all recording ftrace functions - * - * The ftrace dynamic update daemon only wakes up once a second. - * There may be cases where an update needs to be done immediately - * for tests or internal kernel tracing to begin. This function - * wakes the daemon to do an update and will not return until the - * update is complete. */ int ftrace_force_update(void) { - unsigned long last_counter; - DECLARE_WAITQUEUE(wait, current); int ret = 0; if (unlikely(ftrace_disabled)) return -ENODEV; + mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftraced_lock); - last_counter = ftraced_iteration_counter; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&ftraced_waiters, &wait); - if (unlikely(!ftraced_task)) { - ret = -ENODEV; - goto out; - } - - do { - mutex_unlock(&ftraced_lock); - wake_up_process(ftraced_task); - schedule(); - mutex_lock(&ftraced_lock); - if (signal_pending(current)) { - ret = -EINTR; - break; - } - set_current_state(TASK_INTERRUPTIBLE); - } while (last_counter == ftraced_iteration_counter); + /* + * If ftraced_trigger is not set, then there is nothing + * to update. + */ + if (ftraced_trigger && !ftrace_update_code()) + ret = -EBUSY; - out: mutex_unlock(&ftraced_lock); - remove_wait_queue(&ftraced_waiters, &wait); - set_current_state(TASK_RUNNING); + mutex_unlock(&ftrace_sysctl_lock); return ret; } @@ -1331,6 +1388,12 @@ static __init int ftrace_init_debugfs(void) if (!entry) pr_warning("Could not create debugfs " "'set_ftrace_notrace' entry\n"); + + entry = debugfs_create_file("ftraced_enabled", 0644, d_tracer, + NULL, &ftraced_fops); + if (!entry) + pr_warning("Could not create debugfs " + "'ftraced_enabled' entry\n"); return 0; } -- cgit v1.2.3 From 9306102ea5696a3815f8d24ac0c0fbd1e19be7d3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 29 May 2008 16:35:23 +0800 Subject: mac80211: allow disable FAT in specific configurations This patch allows to disable FAT channel in specific configurations. For example the configuration (8, +1), (primary channel 8, extension channel 12) isn't permitted in U.S., but (8, -1), (primary channel 8, extension channel 4) is. When FAT channel configuration is not permitted, FAT channel should be reported as not supported in the capabilities of the HT IE in association request. And sssociation is performed on 20Mhz channel. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 2 ++ include/net/wireless.h | 6 ++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 3c2ac0c37aa8..9300f37cd7e8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -320,6 +320,8 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10 /* 802.11n HT IE masks */ #define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01 +#define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03 #define IEEE80211_HT_IE_CHA_WIDTH 0x04 #define IEEE80211_HT_IE_HT_PROTECTION 0x0003 #define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004 diff --git a/include/net/wireless.h b/include/net/wireless.h index 667b4080d30f..9324f8dd183e 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -39,12 +39,18 @@ enum ieee80211_band { * on this channel. * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel. * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. + * @IEEE80211_CHAN_NO_FAT_ABOVE: extension channel above this channel + * is not permitted. + * @IEEE80211_CHAN_NO_FAT_BELOW: extension channel below this channel + * is not permitted. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, IEEE80211_CHAN_NO_IBSS = 1<<2, IEEE80211_CHAN_RADAR = 1<<3, + IEEE80211_CHAN_NO_FAT_ABOVE = 1<<4, + IEEE80211_CHAN_NO_FAT_BELOW = 1<<5, }; /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3f8601cafffb..432011cd3647 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -92,6 +92,8 @@ struct ieee80211_sta_bss { size_t wmm_ie_len; u8 *ht_ie; size_t ht_ie_len; + u8 *ht_add_ie; + size_t ht_add_ie_len; #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6faa7006681a..d30c11337b04 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -808,8 +808,29 @@ static void ieee80211_send_assoc(struct net_device *dev, /* wmm support is a must to HT */ if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) && - sband->ht_info.ht_supported) { - __le16 tmp = cpu_to_le16(sband->ht_info.cap); + sband->ht_info.ht_supported && bss->ht_add_ie) { + struct ieee80211_ht_addt_info *ht_add_info = + (struct ieee80211_ht_addt_info *)bss->ht_add_ie; + u16 cap = sband->ht_info.cap; + __le16 tmp; + u32 flags = local->hw.conf.channel->flags; + + switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) { + case IEEE80211_HT_IE_CHA_SEC_ABOVE: + if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) { + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH; + cap &= ~IEEE80211_HT_CAP_SGI_40; + } + break; + case IEEE80211_HT_IE_CHA_SEC_BELOW: + if (flags & IEEE80211_CHAN_NO_FAT_BELOW) { + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH; + cap &= ~IEEE80211_HT_CAP_SGI_40; + } + break; + } + + tmp = cpu_to_le16(cap); pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2); *pos++ = WLAN_EID_HT_CAPABILITY; *pos++ = sizeof(struct ieee80211_ht_cap); @@ -2264,6 +2285,7 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) kfree(bss->rsn_ie); kfree(bss->wmm_ie); kfree(bss->ht_ie); + kfree(bss->ht_add_ie); kfree(bss_mesh_id(bss)); kfree(bss_mesh_cfg(bss)); kfree(bss); @@ -2640,6 +2662,26 @@ static void ieee80211_rx_bss_info(struct net_device *dev, bss->ht_ie_len = 0; } + if (elems.ht_info_elem && + (!bss->ht_add_ie || + bss->ht_add_ie_len != elems.ht_info_elem_len || + memcmp(bss->ht_add_ie, elems.ht_info_elem, + elems.ht_info_elem_len))) { + kfree(bss->ht_add_ie); + bss->ht_add_ie = + kmalloc(elems.ht_info_elem_len + 2, GFP_ATOMIC); + if (bss->ht_add_ie) { + memcpy(bss->ht_add_ie, elems.ht_info_elem - 2, + elems.ht_info_elem_len + 2); + bss->ht_add_ie_len = elems.ht_info_elem_len + 2; + } else + bss->ht_add_ie_len = 0; + } else if (!elems.ht_info_elem && bss->ht_add_ie) { + kfree(bss->ht_add_ie); + bss->ht_add_ie = NULL; + bss->ht_add_ie_len = 0; + } + bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); -- cgit v1.2.3 From ba569b4c68f11906da2996ee252bcff0df61cb90 Mon Sep 17 00:00:00 2001 From: Masakazu Mokuno Date: Fri, 30 May 2008 16:52:23 +0900 Subject: WEXT: Add support for passing PMK and capability flags to WEXT This defines the flags for setting the PMK to the driver and the capability flag for this so that the user space program can figure out whether the target driver wants to do 4-way hand shake by itself and pass the PMK which is needed before 4-way handshake to the driver. Signed-off-by: Masakazu Mokuno Signed-off-by: John W. Linville --- include/linux/wireless.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 0a9b5b41ed67..4a95a0e5eeca 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -611,6 +611,7 @@ #define IW_ENCODE_ALG_WEP 1 #define IW_ENCODE_ALG_TKIP 2 #define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 /* struct iw_encode_ext ->ext_flags */ #define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 #define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 @@ -630,6 +631,7 @@ #define IW_ENC_CAPA_WPA2 0x00000002 #define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 #define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 /* Event capability macros - in (struct iw_range *)->event_capa * Because we have more than 32 possible events, we use an array of -- cgit v1.2.3 From a29ccf6f823a84d89e1c7aaaf221cf7282022024 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 3 Jun 2008 14:59:40 +0100 Subject: Make console charset translation optional By turning off the new CONSOLE_TRANSLATIONS option and dropping the associated code and tables from the kernel, we can save about 7KiB. Taken from linux-tiny project by Tim Bird and mangled further by dwmw2. Signed-off-by: Tim Bird Signed-off-by: David Woodhouse --- drivers/char/Kconfig | 8 ++++++++ drivers/char/Makefile | 4 ++-- drivers/char/vt.c | 2 +- include/linux/consolemap.h | 14 ++++++++++++++ include/linux/vt_kern.h | 19 +++++++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 595a925c62a9..b7f7371dee73 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -36,6 +36,14 @@ config VT If unsure, say Y, or else you won't be able to do much with your new shiny Linux system :-) +config CONSOLE_TRANSLATIONS + depends on VT + default y + bool "Enable character translations in console" if EMBEDDED + ---help--- + This enables support for font mapping and Unicode translation + on virtual consoles. + config VT_CONSOLE bool "Support for console on virtual terminal" if EMBEDDED depends on VT diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 4c1c584e9eb6..6ef173cab144 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -12,8 +12,8 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-y += misc.o -obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ - consolemap_deftbl.o selection.o keyboard.o +obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o +obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o obj-$(CONFIG_AUDIT) += tty_audit.o obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o diff --git a/drivers/char/vt.c b/drivers/char/vt.c index fa1ffbf2c621..18b7fb06dace 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2208,7 +2208,7 @@ rescan_last_byte: c = 0xfffd; tc = c; } else { /* no utf or alternate charset mode */ - tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; + tc = vc_translate(vc, c); } param.c = tc; diff --git a/include/linux/consolemap.h b/include/linux/consolemap.h index e2bf7e5db39a..c4811da1338b 100644 --- a/include/linux/consolemap.h +++ b/include/linux/consolemap.h @@ -3,6 +3,9 @@ * * Interface between console.c, selection.c and consolemap.c */ +#ifndef __LINUX_CONSOLEMAP_H__ +#define __LINUX_CONSOLEMAP_H__ + #define LAT1_MAP 0 #define GRAF_MAP 1 #define IBMPC_MAP 2 @@ -10,6 +13,7 @@ #include +#ifdef CONFIG_CONSOLE_TRANSLATIONS struct vc_data; extern u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode); @@ -18,3 +22,13 @@ extern int conv_uni_to_pc(struct vc_data *conp, long ucs); extern u32 conv_8bit_to_uni(unsigned char c); extern int conv_uni_to_8bit(u32 uni); void console_map_init(void); +#else +#define inverse_translate(conp, glyph, uni) ((uint16_t)glyph) +#define set_translate(m, vc) ((unsigned short *)NULL) +#define conv_uni_to_pc(conp, ucs) ((int) (ucs > 0xff ? -1: ucs)) +#define conv_8bit_to_uni(c) ((uint32_t)(c)) +#define conv_uni_to_8bit(c) ((int) ((c) & 0xff)) +#define console_map_init(c) do { ; } while (0) +#endif /* CONFIG_CONSOLE_TRANSLATIONS */ + +#endif /* __LINUX_CONSOLEMAP_H__ */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 9448ffbdcbf6..14c0e91be9b5 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * Presently, a lot of graphics programs do not restore the contents of @@ -54,6 +55,7 @@ void redraw_screen(struct vc_data *vc, int is_switch); struct tty_struct; int tioclinux(struct tty_struct *tty, unsigned long arg); +#ifdef CONFIG_CONSOLE_TRANSLATIONS /* consolemap.c */ struct unimapinit; @@ -71,6 +73,23 @@ void con_free_unimap(struct vc_data *vc); void con_protect_unimap(struct vc_data *vc, int rdonly); int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); +#define vc_translate(vc, c) ((vc)->vc_translate[(c) | \ + (vc)->vc_toggle_meta ? 0x80 : 0]) +#else +#define con_set_trans_old(arg) (0) +#define con_get_trans_old(arg) (-EINVAL) +#define con_set_trans_new(arg) (0) +#define con_get_trans_new(arg) (-EINVAL) +#define con_clear_unimap(vc, ui) (0) +#define con_set_unimap(vc, ct, list) (0) +#define con_set_default_unimap(vc) (0) +#define con_copy_unimap(d, s) (0) +#define con_get_unimap(vc, ct, uct, list) (-EINVAL) +#define con_free_unimap(vc) do { ; } while (0) + +#define vc_translate(vc, c) (c) +#endif + /* vt.c */ int vt_waitactive(int vt); void change_console(struct vc_data *new_vc); -- cgit v1.2.3 From 59018b6d2acabb114ab58637e6ab95ba424a89d0 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:03:52 +0300 Subject: MTD/JFFS2: remove CVS keywords Once upon a time, the MTD repository was using CVS. This patch therefore removes all usages of the no longer updated CVS keywords from the MTD code. This also includes code that printed them to the user. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 2 -- drivers/mtd/afs.c | 2 -- drivers/mtd/chips/cfi_cmdset_0001.c | 2 -- drivers/mtd/chips/cfi_cmdset_0002.c | 3 --- drivers/mtd/chips/cfi_cmdset_0020.c | 2 -- drivers/mtd/chips/cfi_probe.c | 1 - drivers/mtd/chips/cfi_util.c | 3 --- drivers/mtd/chips/chipreg.c | 2 -- drivers/mtd/chips/gen_probe.c | 1 - drivers/mtd/chips/jedec_probe.c | 1 - drivers/mtd/chips/map_absent.c | 1 - drivers/mtd/chips/map_ram.c | 1 - drivers/mtd/chips/map_rom.c | 1 - drivers/mtd/cmdlinepart.c | 2 -- drivers/mtd/devices/Kconfig | 1 - drivers/mtd/devices/Makefile | 1 - drivers/mtd/devices/block2mtd.c | 6 ------ drivers/mtd/devices/doc2000.c | 2 -- drivers/mtd/devices/doc2001.c | 2 -- drivers/mtd/devices/doc2001plus.c | 2 -- drivers/mtd/devices/docecc.c | 2 -- drivers/mtd/devices/docprobe.c | 3 --- drivers/mtd/devices/lart.c | 2 -- drivers/mtd/devices/ms02-nv.c | 2 -- drivers/mtd/devices/ms02-nv.h | 2 -- drivers/mtd/devices/mtdram.c | 1 - drivers/mtd/devices/phram.c | 2 -- drivers/mtd/devices/pmc551.c | 2 -- drivers/mtd/devices/slram.c | 2 -- drivers/mtd/ftl.c | 3 --- drivers/mtd/inftlcore.c | 5 ----- drivers/mtd/inftlmount.c | 4 ---- drivers/mtd/maps/Kconfig | 1 - drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/amd76xrom.c | 1 - drivers/mtd/maps/autcpu12-nvram.c | 2 -- drivers/mtd/maps/bast-flash.c | 2 -- drivers/mtd/maps/cdb89712.c | 1 - drivers/mtd/maps/ceiva.c | 1 - drivers/mtd/maps/cfi_flagadm.c | 2 -- drivers/mtd/maps/dbox2-flash.c | 2 -- drivers/mtd/maps/dc21285.c | 2 -- drivers/mtd/maps/dilnetpc.c | 2 -- drivers/mtd/maps/dmv182.c | 2 -- drivers/mtd/maps/ebony.c | 2 -- drivers/mtd/maps/edb7312.c | 2 -- drivers/mtd/maps/fortunet.c | 1 - drivers/mtd/maps/h720x-flash.c | 2 -- drivers/mtd/maps/ichxrom.c | 1 - drivers/mtd/maps/impa7.c | 2 -- drivers/mtd/maps/integrator-flash.c | 2 -- drivers/mtd/maps/ipaq-flash.c | 2 -- drivers/mtd/maps/ixp2000.c | 2 -- drivers/mtd/maps/ixp4xx.c | 2 -- drivers/mtd/maps/l440gx.c | 2 -- drivers/mtd/maps/map_funcs.c | 2 -- drivers/mtd/maps/mbx860.c | 2 -- drivers/mtd/maps/mtx-1_flash.c | 2 -- drivers/mtd/maps/netsc520.c | 2 -- drivers/mtd/maps/nettel.c | 2 -- drivers/mtd/maps/octagon-5066.c | 1 - drivers/mtd/maps/omap-toto-flash.c | 2 -- drivers/mtd/maps/pci.c | 2 -- drivers/mtd/maps/pcmciamtd.c | 5 +---- drivers/mtd/maps/physmap.c | 2 -- drivers/mtd/maps/plat-ram.c | 2 -- drivers/mtd/maps/redwood.c | 2 -- drivers/mtd/maps/rpxlite.c | 2 -- drivers/mtd/maps/sa1100-flash.c | 2 -- drivers/mtd/maps/sbc8240.c | 3 --- drivers/mtd/maps/sbc_gxx.c | 2 -- drivers/mtd/maps/sc520cdp.c | 2 -- drivers/mtd/maps/scb2_flash.c | 1 - drivers/mtd/maps/scx200_docflash.c | 2 -- drivers/mtd/maps/sharpsl-flash.c | 2 -- drivers/mtd/maps/solutionengine.c | 2 -- drivers/mtd/maps/sun_uflash.c | 2 +- drivers/mtd/maps/tqm8xxl.c | 2 -- drivers/mtd/maps/ts5500_flash.c | 2 -- drivers/mtd/maps/tsunami_flash.c | 1 - drivers/mtd/maps/uclinux.c | 2 -- drivers/mtd/maps/vmax301.c | 1 - drivers/mtd/maps/walnut.c | 2 -- drivers/mtd/maps/wr_sbc82xx_flash.c | 2 -- drivers/mtd/mtd_blkdevs.c | 2 -- drivers/mtd/mtdblock.c | 2 -- drivers/mtd/mtdblock_ro.c | 2 -- drivers/mtd/mtdchar.c | 2 -- drivers/mtd/mtdconcat.c | 2 -- drivers/mtd/mtdcore.c | 2 -- drivers/mtd/mtdpart.c | 2 -- drivers/mtd/nand/Kconfig | 1 - drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/au1550nd.c | 2 -- drivers/mtd/nand/autcpu12.c | 2 -- drivers/mtd/nand/diskonchip.c | 2 -- drivers/mtd/nand/edb7312.c | 2 -- drivers/mtd/nand/h1910.c | 2 -- drivers/mtd/nand/nand_bbt.c | 2 -- drivers/mtd/nand/nand_ecc.c | 2 -- drivers/mtd/nand/nand_ids.c | 2 -- drivers/mtd/nand/nandsim.c | 2 -- drivers/mtd/nand/ppchameleonevb.c | 2 -- drivers/mtd/nand/rtc_from4.c | 2 -- drivers/mtd/nand/s3c2410.c | 2 -- drivers/mtd/nand/sharpsl.c | 2 -- drivers/mtd/nand/spia.c | 2 -- drivers/mtd/nand/toto.c | 2 -- drivers/mtd/nand/ts7250.c | 2 -- drivers/mtd/nftlcore.c | 5 ----- drivers/mtd/nftlmount.c | 4 ---- drivers/mtd/redboot.c | 2 -- drivers/mtd/rfd_ftl.c | 2 -- include/linux/jffs2.h | 3 --- include/linux/mtd/blktrans.h | 2 -- include/linux/mtd/cfi.h | 1 - include/linux/mtd/cfi_endian.h | 5 ----- include/linux/mtd/concat.h | 2 -- include/linux/mtd/doc2000.h | 2 -- include/linux/mtd/flashchip.h | 3 --- include/linux/mtd/ftl.h | 2 -- include/linux/mtd/gen_probe.h | 1 - include/linux/mtd/inftl.h | 4 ---- include/linux/mtd/map.h | 1 - include/linux/mtd/mtd.h | 2 -- include/linux/mtd/nand.h | 2 -- include/linux/mtd/nand_ecc.h | 2 -- include/linux/mtd/nftl.h | 2 -- include/linux/mtd/partitions.h | 2 -- include/linux/mtd/physmap.h | 2 -- include/linux/mtd/plat-ram.h | 2 -- include/linux/mtd/pmc551.h | 4 +--- include/linux/mtd/xip.h | 2 -- include/mtd/inftl-user.h | 2 -- include/mtd/jffs2-user.h | 2 -- include/mtd/mtd-abi.h | 2 -- include/mtd/mtd-user.h | 2 -- include/mtd/nftl-user.h | 2 -- 138 files changed, 3 insertions(+), 279 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index eed06d068fd1..14f11f8b9e5f 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,5 +1,3 @@ -# $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $ - menuconfig MTD tristate "Memory Technology Device (MTD) support" depends on HAS_IOMEM diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index 52d51eb91c16..d072ca5be689 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -21,8 +21,6 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: afs.c,v 1.15 2005/11/07 11:14:19 gleixner Exp $ - ======================================================================*/ #include diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index cc6b7bb6de02..324ff82a3cd9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,8 +4,6 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.186 2005/11/23 22:07:52 nico Exp $ - * * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index f7fcc6389533..a972cc6be436 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -16,9 +16,6 @@ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * This code is GPL - * - * $Id: cfi_cmdset_0002.c,v 1.122 2005/11/07 11:14:22 gleixner Exp $ - * */ #include diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 1b720cc571f3..d4714dd9f7ab 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -4,8 +4,6 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0020.c,v 1.22 2005/11/07 11:14:22 gleixner Exp $ - * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and * independent of the flash geometry (buswidth, interleave, etc.) diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index a4463a91ce31..c418e92e1d92 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,7 +1,6 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.86 2005/11/29 14:48:31 gleixner Exp $ */ #include diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 72e0022a47bf..0ee457018016 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -6,9 +6,6 @@ * Copyright (C) 2003 STMicroelectronics Limited * * This code is covered by the GPL. - * - * $Id: cfi_util.c,v 1.10 2005/11/07 11:14:23 gleixner Exp $ - * */ #include diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index 2174c97549f0..c85760968227 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -1,6 +1,4 @@ /* - * $Id: chipreg.c,v 1.17 2004/11/16 18:29:00 dwmw2 Exp $ - * * Registration for chip drivers * */ diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index d338b8c92780..e53a58ae384f 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -2,7 +2,6 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.24 2005/11/07 11:14:23 gleixner Exp $ */ #include diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index b229da8060dd..afb35e3a3cee 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,7 +1,6 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.66 2005/11/07 11:14:23 gleixner Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index fc478c0f93f5..494d30d0631a 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -1,7 +1,6 @@ /* * Common code to handle absent "placeholder" devices * Copyright 2001 Resilience Corporation - * $Id: map_absent.c,v 1.6 2005/11/07 11:14:23 gleixner Exp $ * * This map driver is used to allocate "placeholder" MTD * devices on systems that have socketed/removable media. diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 5cb6d5263661..072dd8abf33a 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,6 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index cb27f855074c..821d0ed6bae3 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,6 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index e472a0e9de9d..68782ab2f0de 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,6 +1,4 @@ /* - * $Id: cmdlinepart.c,v 1.19 2005/11/07 11:14:19 gleixner Exp $ - * * Read flash partition table from command line * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 35ed1103dbb2..9c613f06623c 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.18 2005/11/07 11:14:24 gleixner Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0f788d5c4bf8..0993d5cf3923 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 519d942e7940..303ea9b8cfe4 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -1,6 +1,4 @@ /* - * $Id: block2mtd.c,v 1.30 2005/11/29 14:48:32 gleixner Exp $ - * * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans @@ -20,9 +18,6 @@ #include #include -#define VERSION "$Revision: 1.30 $" - - #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) #define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) @@ -451,7 +446,6 @@ MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); static int __init block2mtd_init(void) { int ret = 0; - INFO("version " VERSION); #ifndef MODULE if (strlen(block2mtd_paramline)) diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 846989f292e3..50de839c77a9 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -3,8 +3,6 @@ * Linux driver for Disk-On-Chip 2000 and Millennium * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse - * - * $Id: doc2000.c,v 1.67 2005/11/07 11:14:24 gleixner Exp $ */ #include diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 6413efc045e0..e32c568c1145 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -3,8 +3,6 @@ * Linux driver for Disk-On-Chip Millennium * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse - * - * $Id: doc2001.c,v 1.49 2005/11/07 11:14:24 gleixner Exp $ */ #include diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 83be3461658f..d853f891b586 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -6,8 +6,6 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001plus.c,v 1.14 2005/11/07 11:14:24 gleixner Exp $ - * * Released under GPL */ diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index fd8a8daba3a8..874e51b110a2 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -7,8 +7,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: docecc.c,v 1.7 2005/11/07 11:14:25 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index d8cc94ec4e50..6e5d811ae83a 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -4,9 +4,6 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse */ -/* $Id: docprobe.c,v 1.46 2005/11/07 11:14:25 gleixner Exp $ */ - - /* DOC_PASSIVE_PROBE: In order to ensure that the BIOS checksum is correct at boot time, and diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 1d324e5c412d..f4bda4cee495 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -2,8 +2,6 @@ /* * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * - * $Id: lart.c,v 1.9 2005/11/07 11:14:25 gleixner Exp $ - * * Author: Abraham vd Merwe * * Copyright (c) 2001, 2d3D, Inc. diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 9cff119a2024..6a9a24a80a6d 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -5,8 +5,6 @@ * 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. - * - * $Id: ms02-nv.c,v 1.11 2005/11/14 13:41:47 macro Exp $ */ #include diff --git a/drivers/mtd/devices/ms02-nv.h b/drivers/mtd/devices/ms02-nv.h index 8a6eef7cfee3..04deafd3a771 100644 --- a/drivers/mtd/devices/ms02-nv.h +++ b/drivers/mtd/devices/ms02-nv.h @@ -9,8 +9,6 @@ * 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. - * - * $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 0399be178620..3aaca88847d3 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,6 +1,5 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.37 2005/04/21 03:42:11 joern Exp $ * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index c7987b1c5e01..088fbb7595b5 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,6 +1,4 @@ /** - * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $ - * * Copyright (c) ???? Jochen Schäuble * Copyright (c) 2003-2004 Joern Engel * diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index bc9981749064..d38bca64bb15 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,6 +1,4 @@ /* - * $Id: pmc551.c,v 1.32 2005/11/07 11:14:25 gleixner Exp $ - * * PMC551 PCI Mezzanine Ram Device * * Author: diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index cb86db746f28..a425d09f35a0 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -1,7 +1,5 @@ /*====================================================================== - $Id: slram.c,v 1.36 2005/11/07 11:14:25 gleixner Exp $ - This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually use slram at least mtdblock or mtdchar is required (for block or diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 4a79b187b568..3fed8f94ac6f 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,5 +1,4 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $ * * Fixes: Arnaldo Carvalho de Melo * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -1082,8 +1081,6 @@ static struct mtd_blktrans_ops ftl_tr = { static int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n"); - return register_mtd_blktrans(&ftl_tr); } diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index b0e396504e67..c4f9d3378b24 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -7,8 +7,6 @@ * (c) 1999 Machine Vision Holdings, Inc. * Author: David Woodhouse * - * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $ - * * 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 @@ -953,9 +951,6 @@ static struct mtd_blktrans_ops inftl_tr = { static int __init init_inftl(void) { - printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, " - "inftlmount.c %s\n", inftlmountrev); - return register_mtd_blktrans(&inftl_tr); } diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index c551d2f0779c..9113628ed1ef 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -8,8 +8,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.18 2005/11/07 11:14:20 gleixner Exp $ - * * 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 @@ -39,8 +37,6 @@ #include #include -char inftlmountrev[]="$Revision: 1.18 $"; - /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 1bd69aa9e22a..b2c180245618 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.61 2005/11/07 11:14:26 gleixner Exp $ menu "Mapping drivers for chip access" depends on MTD!=n diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index a9cbe80f99a0..5444eaf4026f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.34 2005/11/07 11:14:26 gleixner Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 728aed6ad722..948b86f35ef4 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -2,7 +2,6 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.21 2005/11/07 11:14:26 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index 7ed3424dd959..cf32267263df 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -2,8 +2,6 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.9 2005/11/07 11:14:26 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c index 1f492062f8ca..ca5414880341 100644 --- a/drivers/mtd/maps/bast-flash.c +++ b/drivers/mtd/maps/bast-flash.c @@ -9,8 +9,6 @@ * 20-Sep-2004 BJD Initial version * 17-Jan-2005 BJD Add whole device if no partitions found * - * $Id: bast-flash.c,v 1.5 2005/11/07 11:14:26 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index 9f17bb6c5a9d..cb507da0a87d 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -1,7 +1,6 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.11 2005/11/07 11:14:26 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index 629e6e2641a8..6464d487eb1a 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -11,7 +11,6 @@ * * (C) 2000 Nicolas Pitre * - * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 65e5ee552010..0ecc3f6d735b 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -1,8 +1,6 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson * - * $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 92a9c7fac993..e115667bf1d0 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -1,6 +1,4 @@ /* - * $Id: dbox2-flash.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $ - * * D-Box 2 flash driver */ diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index b32bb9347d71..3aa018c092f8 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre * * This code is GPL - * - * $Id: dc21285.c,v 1.24 2005/11/07 11:14:26 gleixner Exp $ */ #include #include diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index 1c3b34ad7325..0713e3a5a22c 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -14,8 +14,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.20 2005/11/07 11:14:26 gleixner Exp $ - * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c index e0558b0b2fe6..d171674eb2ed 100644 --- a/drivers/mtd/maps/dmv182.c +++ b/drivers/mtd/maps/dmv182.c @@ -4,8 +4,6 @@ * * Flash map driver for the Dy4 SVME182 board * - * $Id: dmv182.c,v 1.6 2005/11/07 11:14:26 gleixner Exp $ - * * Copyright 2003-2004, TimeSys Corporation * * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp. diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c index 1488bb92f26f..d92b7c70d3ed 100644 --- a/drivers/mtd/maps/ebony.c +++ b/drivers/mtd/maps/ebony.c @@ -1,6 +1,4 @@ /* - * $Id: ebony.c,v 1.16 2005/11/07 11:14:26 gleixner Exp $ - * * Mapping for Ebony user flash * * Matt Porter diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 1c5b97c89685..9433738c1664 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -1,6 +1,4 @@ /* - * $Id: edb7312.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the NOR flash on Cogent EDB7312 boards * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index 7c50c271651c..a8e3fde4cbd5 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -1,6 +1,5 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.11 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 6dde3182d64a..ef8915474462 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -2,8 +2,6 @@ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based * evaluation boards * - * $Id: h720x-flash.c,v 1.12 2005/11/07 11:14:27 gleixner Exp $ - * * (C) 2002 Jungjun Kim * 2003 Thomas Gleixner */ diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2c884c49e84a..aeb6c916e23f 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -2,7 +2,6 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.19 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index a0b4dc7155dc..2682ab51a367 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -1,6 +1,4 @@ /* - * $Id: impa7.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the NOR flash on implementa A7 boards * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 325c8880c437..ee361aaadb1e 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -22,8 +22,6 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: integrator-flash.c,v 1.20 2005/11/07 11:14:27 gleixner Exp $ - ======================================================================*/ #include diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c index f27c132794c3..a806119797e0 100644 --- a/drivers/mtd/maps/ipaq-flash.c +++ b/drivers/mtd/maps/ipaq-flash.c @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre * (C) 2002 Hewlett-Packard Company * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes - * - * $Id: ipaq-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c8396b8574c4..c2264792a20b 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -1,6 +1,4 @@ /* - * $Id: ixp2000.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $ - * * drivers/mtd/maps/ixp2000.c * * Mapping for the Intel XScale IXP2000 based systems diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 01f19a4714b5..9c7a5fbd4e51 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -1,6 +1,4 @@ /* - * $Id: ixp4xx.c,v 1.13 2005/11/16 16:23:21 dvrabel Exp $ - * * drivers/mtd/maps/ixp4xx.c * * MTD Map file for IXP4XX based systems. Please do not make per-board diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 67620adf4811..9e054503c4cf 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -1,6 +1,4 @@ /* - * $Id: l440gx.c,v 1.18 2005/11/07 11:14:27 gleixner Exp $ - * * BIOS Flash chip on Intel 440GX board. * * Bugs this currently does not work under linuxBIOS. diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c index 9105e6ca0aa6..3f268370eeca 100644 --- a/drivers/mtd/maps/map_funcs.c +++ b/drivers/mtd/maps/map_funcs.c @@ -1,6 +1,4 @@ /* - * $Id: map_funcs.c,v 1.10 2005/06/06 23:04:36 tpoynor Exp $ - * * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS * is enabled. */ diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 06b118727846..706f67394b07 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c @@ -1,6 +1,4 @@ /* - * $Id: mbx860.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the flash on MBX860 boards * * Author: Anton Todorov diff --git a/drivers/mtd/maps/mtx-1_flash.c b/drivers/mtd/maps/mtx-1_flash.c index 2a8fde9b92f0..a3b651904127 100644 --- a/drivers/mtd/maps/mtx-1_flash.c +++ b/drivers/mtd/maps/mtx-1_flash.c @@ -1,8 +1,6 @@ /* * Flash memory access on 4G Systems MTX-1 boards * - * $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $ - * * (C) 2005 Bruno Randolf * (C) 2005 Joern Engel * diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index 95dcab2146ad..c0cb319b2b70 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -3,8 +3,6 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 0c9b305a72e0..965e6c6d6ab0 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -5,8 +5,6 @@ * * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) - * - * $Id: nettel.c,v 1.12 2005/11/29 14:30:00 gleixner Exp $ */ /****************************************************************************/ diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index a6642db3d325..43e04c1d22a9 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,3 @@ -// $Id: octagon-5066.c,v 1.28 2005/11/07 11:14:27 gleixner Exp $ /* ###################################################################### Octagon 5066 MTD Driver. diff --git a/drivers/mtd/maps/omap-toto-flash.c b/drivers/mtd/maps/omap-toto-flash.c index e6e391efbeb6..0a60ebbc2175 100644 --- a/drivers/mtd/maps/omap-toto-flash.c +++ b/drivers/mtd/maps/omap-toto-flash.c @@ -4,8 +4,6 @@ * jzhang@ti.com (C) 2003 Texas Instruments. * * (C) 2002 MontVista Software, Inc. - * - * $Id: omap-toto-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index d2ab1bae9c34..5c6a25c90380 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -7,8 +7,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.14 2005/11/17 08:20:27 dwmw2 Exp $ - * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. * - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001 diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 1912d968718b..8f7ca863f89d 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -1,6 +1,4 @@ /* - * $Id: pcmciamtd.c,v 1.55 2005/11/07 11:14:28 gleixner Exp $ - * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * * Author: Simon Evans @@ -48,7 +46,6 @@ static const int debug = 0; #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.55 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -790,7 +787,7 @@ static struct pcmcia_driver pcmciamtd_driver = { static int __init init_pcmciamtd(void) { - info(DRIVER_DESC " " DRIVER_VERSION); + info(DRIVER_DESC); if(bankwidth && bankwidth != 1 && bankwidth != 2) { info("bad bankwidth (%d), using default", bankwidth); diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 183255fcfdcb..1f6b9066b63e 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,6 +1,4 @@ /* - * $Id: physmap.c,v 1.39 2005/11/29 14:49:36 gleixner Exp $ - * * Normal mappings of chips in physical memory * * Copyright (C) 2003 MontaVista Software Inc. diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 3eb2643b2328..e7dd9c8a965e 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -6,8 +6,6 @@ * * Generic platfrom device based RAM map * - * $Id: plat-ram.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index 4d858b3d5f82..de002eb1a7fe 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -1,6 +1,4 @@ /* - * $Id: redwood.c,v 1.11 2005/11/07 11:14:28 gleixner Exp $ - * * drivers/mtd/maps/redwood.c * * FLASH map for the IBM Redwood 4/5/6 boards. diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 809a0c8e7aaf..14d90edb4430 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -1,6 +1,4 @@ /* - * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $ - * * Handle mapping of the flash on the RPX Lite and CLLF boards */ diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index c7d5a52a2d55..e177a43dfff0 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -2,8 +2,6 @@ * Flash memory access on SA11x0 based devices * * (C) 2000 Nicolas Pitre - * - * $Id: sa1100-flash.c,v 1.51 2005/11/07 11:14:28 gleixner Exp $ */ #include #include diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index b8c1331b7a04..6e1e99cd2b59 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c @@ -4,9 +4,6 @@ * Carolyn Smith, Tektronix, Inc. * * This code is GPLed - * - * $Id: sbc8240.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $ - * */ /* diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 7cc4041d096d..1b1c0b7e11ef 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -17,8 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: sbc_gxx.c,v 1.35 2005/11/07 11:14:28 gleixner Exp $ - The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 4045e372b90d..85c1e56309ec 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -16,8 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: sc520cdp.c,v 1.23 2005/11/17 08:20:27 dwmw2 Exp $ - * * * The SC520CDP is an evaluation board for the Elan SC520 processor available * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size, diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 0fc5584324e3..21169e6d646c 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -1,6 +1,5 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin * diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 5e2bce22f37c..b5391ebb736e 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -2,8 +2,6 @@ Copyright (c) 2001,2002 Christer Weinigel - $Id: scx200_docflash.c,v 1.12 2005/11/07 11:14:28 gleixner Exp $ - National Semiconductor SCx200 flash mapped with DOCCS */ diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 917dc778f24e..026eab028189 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -4,8 +4,6 @@ * Copyright (C) 2001 Lineo Japan, Inc. * Copyright (C) 2002 SHARP * - * $Id: sharpsl-flash.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $ - * * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp * Handle mapping of the flash on the RPX Lite and CLLF boards * diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c index d76ceef453ce..0eb41d9c6786 100644 --- a/drivers/mtd/maps/solutionengine.c +++ b/drivers/mtd/maps/solutionengine.c @@ -1,6 +1,4 @@ /* - * $Id: solutionengine.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $ - * * Flash and EPROM on Hitachi Solution Engine and similar boards. * * (C) 2001 Red Hat, Inc. diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 001af7f7ddda..0d7c88396c88 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -1,4 +1,4 @@ -/* $Id: sun_uflash.c,v 1.13 2005/11/07 11:14:28 gleixner Exp $ +/* * * sun_uflash - Driver implementation for user-programmable flash * present on many Sun Microsystems SME boardsets. diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 521734057314..a5d3d8531faa 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -2,8 +2,6 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $ - * * based on rpxlite.c * * Copyright(C) 2001 Kirk Lee diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index b47270e850bc..e2147bf11c88 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -22,8 +22,6 @@ * - Drive A and B use the resident flash disk (RFD) flash translation layer. * - If you have created your own jffs file system and the bios overwrites * it during boot, try disabling Drive A: and B: in the boot order. - * - * $Id: ts5500_flash.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/tsunami_flash.c b/drivers/mtd/maps/tsunami_flash.c index 0f915ac3102e..77a8bfc02577 100644 --- a/drivers/mtd/maps/tsunami_flash.c +++ b/drivers/mtd/maps/tsunami_flash.c @@ -2,7 +2,6 @@ * tsunami_flash.c * * flash chip on alpha ds10... - * $Id: tsunami_flash.c,v 1.10 2005/11/07 11:14:29 gleixner Exp $ */ #include #include diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index c42f4b83f686..bac000a88313 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -4,8 +4,6 @@ * uclinux.c -- generic memory mapped MTD driver for uclinux * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * $Id: uclinux.c,v 1.12 2005/11/07 11:14:29 gleixner Exp $ */ /****************************************************************************/ diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index b3e487395435..5a0c9a353b0f 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -1,4 +1,3 @@ -// $Id: vmax301.c,v 1.32 2005/11/07 11:14:29 gleixner Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. diff --git a/drivers/mtd/maps/walnut.c b/drivers/mtd/maps/walnut.c index ca932122fb64..e243476c8171 100644 --- a/drivers/mtd/maps/walnut.c +++ b/drivers/mtd/maps/walnut.c @@ -1,6 +1,4 @@ /* - * $Id: walnut.c,v 1.3 2005/11/07 11:14:29 gleixner Exp $ - * * Mapping for Walnut flash * (used ebony.c as a "framework") * diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index ac5b8105b6ef..413b0cf9bbd2 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -1,6 +1,4 @@ /* - * $Id: wr_sbc82xx_flash.c,v 1.8 2005/11/07 11:14:29 gleixner Exp $ - * * Map for flash chips on Wind River PowerQUICC II SBC82xx board. * * Copyright (C) 2004 Red Hat, Inc. diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 839eed8430a2..a0ada45672d8 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -1,6 +1,4 @@ /* - * $Id: mtd_blkdevs.c,v 1.27 2005/11/07 11:14:20 gleixner Exp $ - * * (C) 2003 David Woodhouse * * Interface to Linux 2.5 block layer for MTD 'translation layers'. diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 952da30b1745..208c6faa0358 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,8 +1,6 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.68 2005/11/07 11:14:20 gleixner Exp $ - * * (C) 2000-2003 Nicolas Pitre * (C) 1999-2003 David Woodhouse */ diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index f79dbb49b1a2..852165f8b1c3 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -1,6 +1,4 @@ /* - * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $ - * * (C) 2003 David Woodhouse * * Simple read-only (writable only for RAM) mtdblock driver diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5d3ac512ce16..4b3156f9b36f 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,6 +1,4 @@ /* - * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ - * * Character-device access to raw MTD devices. * */ diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index d563dcd4b264..2972a5edb73d 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -6,8 +6,6 @@ * NAND support by Christian Gan * * This code is GPL - * - * $Id: mtdconcat.c,v 1.11 2005/11/07 11:14:20 gleixner Exp $ */ #include diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 8c61035b968b..4373790401d3 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,6 +1,4 @@ /* - * $Id: mtdcore.c,v 1.47 2005/11/07 11:14:20 gleixner Exp $ - * * Core registration and callback routines for MTD * drivers and users. * diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 07c701169344..11b803cc405b 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,8 +5,6 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $ - * * 02-21-2002 Thomas Gleixner * added support for read_oob, write_oob */ diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5076faf9ca66..7dea6c3a6603 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ menuconfig MTD_NAND tristate "NAND Device Support" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a6e74a46992a..d95a10c51866 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 09e421a96893..22ad9f367760 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Embedded Edge, LLC * - * $Id: au1550nd.c,v 1.13 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index dd38011ee0b7..553dd7e9b41c 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 0e72153b3297..cd4393ce2562 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -15,8 +15,6 @@ * converted to the generic Reed-Solomon library by Thomas Gleixner * * Interface to generic NAND code for M-Systems DiskOnChip devices - * - * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $ */ #include diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index ba67bbec20d3..387e4352903e 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.12 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 2d585d2d090c..9e59de501c2e 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -7,8 +7,6 @@ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 5e121ceaa598..0b1c48595f12 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,8 +6,6 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 9003a135e050..918a806a8471 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -9,8 +9,6 @@ * * Copyright (C) 2006 Thomas Gleixner * - * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ - * * This file 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 diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index a3e3ab0185d5..69ee2c90eb0b 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -3,8 +3,6 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 68c150c8ff9d..add975a229a7 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -21,8 +21,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA - * - * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $ */ #include diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 082073acf20f..cc8658431851 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/edb7312.c * * - * $Id: ppchameleonevb.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 26f88215bc47..a033c4cd8e16 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: rtc_from4.c,v 1.10 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index b34a460ab679..91f42e485520 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -20,8 +20,6 @@ * 20-Oct-2005 BJD Fix timing calculation bug * 14-Jan-2006 BJD Allow clock to be stopped when idle * - * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $ - * * 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 diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 033f8800b1e6..6dba2fb66ae5 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Richard Purdie * - * $Id: sharpsl.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * Based on Sharp's NAND driver sharp_sl.c * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 1f6d429b1583..0cc6d0acb8fe 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -8,8 +8,6 @@ * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.25 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index f9e2d4a0ab8c..bbf492e6830d 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -14,8 +14,6 @@ * Overview: * This is a device driver for the NAND flash device found on the * TI fido board. It supports 32MiB and 64MiB cards - * - * $Id: toto.c,v 1.5 2005/11/07 11:14:31 gleixner Exp $ */ #include diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index f40081069ab2..807a72752eeb 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -9,8 +9,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $ - * * 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. diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 0c9ce19ea27a..320b929abe79 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1,7 +1,6 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */ /* The contents of this file are distributed under the GNU General @@ -803,12 +802,8 @@ static struct mtd_blktrans_ops nftl_tr = { .owner = THIS_MODULE, }; -extern char nftlmountrev[]; - static int __init init_nftl(void) { - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev); - return register_mtd_blktrans(&nftl_tr); } diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 345e6eff89ce..ccc4f209fbb5 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,8 +4,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.41 2005/11/07 11:14:21 gleixner Exp $ - * * 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 @@ -31,8 +29,6 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.41 $"; - /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 47474903263c..5afa268c02f1 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,6 +1,4 @@ /* - * $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $ - * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. */ diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index c84e45465499..e538c0a72abb 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2005 Sean Young * - * $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $ - * * This type of flash translation layer (FTL) is used by the Embedded BIOS * by General Software. It is known as the Resident Flash Disk (RFD), see: * diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 6b563cae23df..da720bc3eb15 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -7,9 +7,6 @@ * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. - * - * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $ - * */ #ifndef __LINUX_JFFS2_H__ diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index 9a6e2f953cba..310e61606415 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -1,6 +1,4 @@ /* - * $Id: blktrans.h,v 1.6 2005/11/07 11:14:54 gleixner Exp $ - * * (C) 2003 David Woodhouse * * Interface to Linux block layer for MTD 'translation layers'. diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index b0ddf4b25862..d6fb115f5a07 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -1,7 +1,6 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.57 2005/11/15 23:28:17 tpoynor Exp $ */ #ifndef __MTD_CFI_H__ diff --git a/include/linux/mtd/cfi_endian.h b/include/linux/mtd/cfi_endian.h index 25724f7d3867..d802f7736be3 100644 --- a/include/linux/mtd/cfi_endian.h +++ b/include/linux/mtd/cfi_endian.h @@ -1,8 +1,3 @@ -/* - * $Id: cfi_endian.h,v 1.11 2002/01/30 23:20:48 awozniak Exp $ - * - */ - #include #ifndef CONFIG_MTD_CFI_ADV_OPTIONS diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h index ed8dc6755219..c02f3d264ecf 100644 --- a/include/linux/mtd/concat.h +++ b/include/linux/mtd/concat.h @@ -4,8 +4,6 @@ * (C) 2002 Robert Kaiser * * This code is GPL - * - * $Id: concat.h,v 1.1 2002/03/08 16:34:36 rkaiser Exp $ */ #ifndef MTD_CONCAT_H diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 9addd073bf15..0a6d516ab71d 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -6,8 +6,6 @@ * Copyright (C) 2002-2003 Greg Ungerer * Copyright (C) 2002-2003 SnapGear Inc * - * $Id: doc2000.h,v 1.25 2005/11/07 11:14:54 gleixner Exp $ - * * Released under GPL */ diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index 39e7d2a1be9a..08dd131301c1 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -5,9 +5,6 @@ * Contains information about the location and state of a given flash device * * (C) 2000 Red Hat. GPLd. - * - * $Id: flashchip.h,v 1.18 2005/11/07 11:14:54 gleixner Exp $ - * */ #ifndef __MTD_FLASHCHIP_H__ diff --git a/include/linux/mtd/ftl.h b/include/linux/mtd/ftl.h index d99609113307..0be442f881dd 100644 --- a/include/linux/mtd/ftl.h +++ b/include/linux/mtd/ftl.h @@ -1,6 +1,4 @@ /* - * $Id: ftl.h,v 1.7 2005/11/07 11:14:54 gleixner Exp $ - * * Derived from (and probably identical to): * ftl.h 1.7 1999/10/25 20:23:17 * diff --git a/include/linux/mtd/gen_probe.h b/include/linux/mtd/gen_probe.h index 256e7342ed1e..df362ddf2949 100644 --- a/include/linux/mtd/gen_probe.h +++ b/include/linux/mtd/gen_probe.h @@ -1,7 +1,6 @@ /* * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.h,v 1.4 2005/11/07 11:14:54 gleixner Exp $ */ #ifndef __LINUX_MTD_GEN_PROBE_H__ diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index 85fd041d44ad..64ee53ce95a9 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -2,8 +2,6 @@ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * $Id: inftl.h,v 1.7 2005/06/13 13:08:45 sean Exp $ */ #ifndef __MTD_INFTL_H__ @@ -52,8 +50,6 @@ struct INFTLrecord { int INFTL_mount(struct INFTLrecord *s); int INFTL_formatblock(struct INFTLrecord *s, int block); -extern char inftlmountrev[]; - void INFTL_dumptables(struct INFTLrecord *s); void INFTL_dumpVUchains(struct INFTLrecord *s); diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index a9fae032ba81..85e3939cf487 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,6 +1,5 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.54 2005/11/07 11:14:54 gleixner Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 245f9098e171..31ed234b2a74 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,6 +1,4 @@ /* - * $Id: mtd.h,v 1.61 2005/11/07 11:14:54 gleixner Exp $ - * * Copyright (C) 1999-2003 David Woodhouse et al. * * Released under GPL diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c42bc7f533a5..1288be7b7740 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -5,8 +5,6 @@ * Steven J. Hill * Thomas Gleixner * - * $Id: nand.h,v 1.74 2005/09/15 13:58:50 vwool Exp $ - * * 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. diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 12c5bc342ead..090da505425d 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -3,8 +3,6 @@ * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $ - * * 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. diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index 001eec50cac6..dcaf611ed748 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -1,6 +1,4 @@ /* - * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $ - * * (C) 1999-2003 David Woodhouse */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 7c37d7e55abc..5014f7a9f5df 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre * * This code is GPL - * - * $Id: partitions.h,v 1.17 2005/11/07 11:14:55 gleixner Exp $ */ #ifndef MTD_PARTITIONS_H diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 0dc07d5f3354..c8e63a5ee72e 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -2,8 +2,6 @@ * For boards with physically mapped flash and using * drivers/mtd/maps/physmap.c mapping driver. * - * $Id: physmap.h,v 1.4 2005/11/07 11:14:55 gleixner Exp $ - * * Copyright (C) 2003 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * diff --git a/include/linux/mtd/plat-ram.h b/include/linux/mtd/plat-ram.h index 0e37ad07bce2..e07890aff1cf 100644 --- a/include/linux/mtd/plat-ram.h +++ b/include/linux/mtd/plat-ram.h @@ -6,8 +6,6 @@ * * Generic platform device based RAM map * - * $Id: plat-ram.h,v 1.2 2005/01/24 00:37:40 bjd Exp $ - * * 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. diff --git a/include/linux/mtd/pmc551.h b/include/linux/mtd/pmc551.h index 5cc070c24d88..27ad40aed19f 100644 --- a/include/linux/mtd/pmc551.h +++ b/include/linux/mtd/pmc551.h @@ -1,6 +1,4 @@ /* - * $Id: pmc551.h,v 1.6 2005/11/07 11:14:55 gleixner Exp $ - * * PMC551 PCI Mezzanine Ram Device * * Author: @@ -17,7 +15,7 @@ #include -#define PMC551_VERSION "$Id: pmc551.h,v 1.6 2005/11/07 11:14:55 gleixner Exp $\n"\ +#define PMC551_VERSION \ "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n" /* diff --git a/include/linux/mtd/xip.h b/include/linux/mtd/xip.h index e9d40bdde48c..36efcba15ecd 100644 --- a/include/linux/mtd/xip.h +++ b/include/linux/mtd/xip.h @@ -11,8 +11,6 @@ * 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. - * - * $Id: xip.h,v 1.5 2005/11/07 11:14:55 gleixner Exp $ */ #ifndef __LINUX_MTD_XIP_H__ diff --git a/include/mtd/inftl-user.h b/include/mtd/inftl-user.h index 9b1e2526b45e..e17eda302b2d 100644 --- a/include/mtd/inftl-user.h +++ b/include/mtd/inftl-user.h @@ -1,6 +1,4 @@ /* - * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ - * * Parts of INFTL headers shared with userspace * */ diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h index d508ef0ae091..001685d7fa88 100644 --- a/include/mtd/jffs2-user.h +++ b/include/mtd/jffs2-user.h @@ -1,6 +1,4 @@ /* - * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $ - * * JFFS2 definitions for use in user space only */ diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 615072c4da04..c6c61cd5a254 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -1,6 +1,4 @@ /* - * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $ - * * Portions of MTD ABI definition which are shared by kernel and user space */ diff --git a/include/mtd/mtd-user.h b/include/mtd/mtd-user.h index 713f34d3e62e..170ceca3b2d0 100644 --- a/include/mtd/mtd-user.h +++ b/include/mtd/mtd-user.h @@ -1,6 +1,4 @@ /* - * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $ - * * MTD ABI header for use by user space only. */ diff --git a/include/mtd/nftl-user.h b/include/mtd/nftl-user.h index b2bca18e7311..390d21c080aa 100644 --- a/include/mtd/nftl-user.h +++ b/include/mtd/nftl-user.h @@ -1,6 +1,4 @@ /* - * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ - * * Parts of NFTL headers shared with userspace * */ -- cgit v1.2.3 From 18404756765c713a0be4eb1082920c04822ce588 Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Thu, 29 May 2008 11:02:52 -0700 Subject: genirq: Expose default irq affinity mask (take 3) Current IRQ affinity interface does not provide a way to set affinity for the IRQs that will be allocated/activated in the future. This patch creates /proc/irq/default_smp_affinity that lets users set default affinity mask for the newly allocated IRQs. Changing the default does not affect affinity masks for the currently active IRQs, they have to be changed explicitly. Updated based on Paul J's comments and added some more documentation. Signed-off-by: Max Krasnyansky Cc: pj@sgi.com Cc: a.p.zijlstra@chello.nl Cc: tglx@linutronix.de Cc: rdunlap@xenotime.net Cc: mingo@elte.hu Signed-off-by: Thomas Gleixner --- Documentation/IRQ-affinity.txt | 37 ++++++++++++++++++------ Documentation/filesystems/proc.txt | 29 ++++++++++++------- arch/alpha/kernel/irq.c | 5 ++-- include/linux/interrupt.h | 5 ++++ include/linux/irq.h | 9 ------ kernel/irq/manage.c | 28 ++++++++++++++++-- kernel/irq/proc.c | 59 +++++++++++++++++++++++++++++++++++--- 7 files changed, 134 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/Documentation/IRQ-affinity.txt b/Documentation/IRQ-affinity.txt index 938d7dd05490..b4a615b78403 100644 --- a/Documentation/IRQ-affinity.txt +++ b/Documentation/IRQ-affinity.txt @@ -1,17 +1,26 @@ +ChangeLog: + Started by Ingo Molnar + Update by Max Krasnyansky -SMP IRQ affinity, started by Ingo Molnar - +SMP IRQ affinity /proc/irq/IRQ#/smp_affinity specifies which target CPUs are permitted for a given IRQ source. It's a bitmask of allowed CPUs. It's not allowed to turn off all CPUs, and if an IRQ controller does not support IRQ affinity then the value will not change from the default 0xffffffff. +/proc/irq/default_smp_affinity specifies default affinity mask that applies +to all non-active IRQs. Once IRQ is allocated/activated its affinity bitmask +will be set to the default mask. It can then be changed as described above. +Default mask is 0xffffffff. + Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting -the IRQ to CPU4-7 (this is an 8-CPU SMP box): +it to CPU4-7 (this is an 8-CPU SMP box): +[root@moon 44]# cd /proc/irq/44 [root@moon 44]# cat smp_affinity ffffffff + [root@moon 44]# echo 0f > smp_affinity [root@moon 44]# cat smp_affinity 0000000f @@ -21,17 +30,27 @@ PING hell (195.4.7.3): 56 data bytes --- hell ping statistics --- 6029 packets transmitted, 6027 packets received, 0% packet loss round-trip min/avg/max = 0.1/0.1/0.4 ms -[root@moon 44]# cat /proc/interrupts | grep 44: - 44: 0 1785 1785 1783 1783 1 -1 0 IO-APIC-level eth1 +[root@moon 44]# cat /proc/interrupts | grep 'CPU\|44:' + CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 + 44: 1068 1785 1785 1783 0 0 0 0 IO-APIC-level eth1 + +As can be seen from the line above IRQ44 was delivered only to the first four +processors (0-3). +Now lets restrict that IRQ to CPU(4-7). + [root@moon 44]# echo f0 > smp_affinity +[root@moon 44]# cat smp_affinity +000000f0 [root@moon 44]# ping -f h PING hell (195.4.7.3): 56 data bytes .. --- hell ping statistics --- 2779 packets transmitted, 2777 packets received, 0% packet loss round-trip min/avg/max = 0.1/0.5/585.4 ms -[root@moon 44]# cat /proc/interrupts | grep 44: - 44: 1068 1785 1785 1784 1784 1069 1070 1069 IO-APIC-level eth1 -[root@moon 44]# +[root@moon 44]# cat /proc/interrupts | 'CPU\|44:' + CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 + 44: 1068 1785 1785 1783 1784 1069 1070 1069 IO-APIC-level eth1 + +This time around IRQ44 was delivered only to the last four processors. +i.e counters for the CPU0-3 did not change. diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index dbc3c6a3650f..7f268f327d75 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -380,28 +380,35 @@ i386 and x86_64 platforms support the new IRQ vector displays. Of some interest is the introduction of the /proc/irq directory to 2.4. It could be used to set IRQ to CPU affinity, this means that you can "hook" an IRQ to only one CPU, or to exclude a CPU of handling IRQs. The contents of the -irq subdir is one subdir for each IRQ, and one file; prof_cpu_mask +irq subdir is one subdir for each IRQ, and two files; default_smp_affinity and +prof_cpu_mask. For example > ls /proc/irq/ 0 10 12 14 16 18 2 4 6 8 prof_cpu_mask - 1 11 13 15 17 19 3 5 7 9 + 1 11 13 15 17 19 3 5 7 9 default_smp_affinity > ls /proc/irq/0/ smp_affinity -The contents of the prof_cpu_mask file and each smp_affinity file for each IRQ -is the same by default: +smp_affinity is a bitmask, in which you can specify which CPUs can handle the +IRQ, you can set it by doing: - > cat /proc/irq/0/smp_affinity - ffffffff + > echo 1 > /proc/irq/10/smp_affinity + +This means that only the first CPU will handle the IRQ, but you can also echo +5 which means that only the first and fourth CPU can handle the IRQ. -It's a bitmask, in which you can specify which CPUs can handle the IRQ, you can -set it by doing: +The contents of each smp_affinity file is the same by default: + + > cat /proc/irq/0/smp_affinity + ffffffff - > echo 1 > /proc/irq/prof_cpu_mask +The default_smp_affinity mask applies to all non-active IRQs, which are the +IRQs which have not yet been allocated/activated, and hence which lack a +/proc/irq/[0-9]* directory. -This means that only the first CPU will handle the IRQ, but you can also echo 5 -which means that only the first and fourth CPU can handle the IRQ. +prof_cpu_mask specifies which CPUs are to be profiled by the system wide +profiler. Default value is ffffffff (all cpus). The way IRQs are routed is handled by the IO-APIC, and it's Round Robin between all the CPUs which are allowed to handle it. As usual the kernel has diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index facf82a5499a..c626a821cdcb 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -42,8 +42,7 @@ void ack_bad_irq(unsigned int irq) #ifdef CONFIG_SMP static char irq_user_affinity[NR_IRQS]; -int -select_smp_affinity(unsigned int irq) +int irq_select_affinity(unsigned int irq) { static int last_cpu; int cpu = last_cpu + 1; @@ -51,7 +50,7 @@ select_smp_affinity(unsigned int irq) if (!irq_desc[irq].chip->set_affinity || irq_user_affinity[irq]) return 1; - while (!cpu_possible(cpu)) + while (!cpu_possible(cpu) || !cpu_isset(cpu, irq_default_affinity)) cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0); last_cpu = cpu; diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index f1fc7470d26c..043400f3d458 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -104,8 +104,11 @@ extern void enable_irq(unsigned int irq); #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS) +extern cpumask_t irq_default_affinity; + extern int irq_set_affinity(unsigned int irq, cpumask_t cpumask); extern int irq_can_set_affinity(unsigned int irq); +extern int irq_select_affinity(unsigned int irq); #else /* CONFIG_SMP */ @@ -119,6 +122,8 @@ static inline int irq_can_set_affinity(unsigned int irq) return 0; } +static inline int irq_select_affinity(unsigned int irq) { return 0; } + #endif /* CONFIG_SMP && CONFIG_GENERIC_HARDIRQS */ #ifdef CONFIG_GENERIC_HARDIRQS diff --git a/include/linux/irq.h b/include/linux/irq.h index 552e0ec269c9..8ccb462ea42c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -244,15 +244,6 @@ static inline void set_balance_irq_affinity(unsigned int irq, cpumask_t mask) } #endif -#ifdef CONFIG_AUTO_IRQ_AFFINITY -extern int select_smp_affinity(unsigned int irq); -#else -static inline int select_smp_affinity(unsigned int irq) -{ - return 1; -} -#endif - extern int no_irq_affinity; static inline int irq_balancing_disabled(unsigned int irq) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 46d6611a33bb..469814e9b9ee 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -17,6 +17,8 @@ #ifdef CONFIG_SMP +cpumask_t irq_default_affinity = CPU_MASK_ALL; + /** * synchronize_irq - wait for pending IRQ handlers (on other CPUs) * @irq: interrupt number to wait for @@ -95,6 +97,27 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask) return 0; } +#ifndef CONFIG_AUTO_IRQ_AFFINITY +/* + * Generic version of the affinity autoselector. + */ +int irq_select_affinity(unsigned int irq) +{ + cpumask_t mask; + + if (!irq_can_set_affinity(irq)) + return 0; + + cpus_and(mask, cpu_online_map, irq_default_affinity); + + irq_desc[irq].affinity = mask; + irq_desc[irq].chip->set_affinity(irq, mask); + + set_balance_irq_affinity(irq, mask); + return 0; +} +#endif + #endif /** @@ -382,6 +405,9 @@ int setup_irq(unsigned int irq, struct irqaction *new) } else /* Undo nested disables: */ desc->depth = 1; + + /* Set default affinity mask once everything is setup */ + irq_select_affinity(irq); } /* Reset broken irq detection when installing new handler */ desc->irq_count = 0; @@ -571,8 +597,6 @@ int request_irq(unsigned int irq, irq_handler_t handler, action->next = NULL; action->dev_id = dev_id; - select_smp_affinity(irq); - #ifdef CONFIG_DEBUG_SHIRQ if (irqflags & IRQF_SHARED) { /* diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index c2f2ccb0549a..6c6d35d68ee9 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -44,7 +44,7 @@ static int irq_affinity_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) { unsigned int irq = (int)(long)data, full_count = count, err; - cpumask_t new_value, tmp; + cpumask_t new_value; if (!irq_desc[irq].chip->set_affinity || no_irq_affinity || irq_balancing_disabled(irq)) @@ -62,17 +62,51 @@ static int irq_affinity_write_proc(struct file *file, const char __user *buffer, * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ - cpus_and(tmp, new_value, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpus_intersects(new_value, cpu_online_map)) /* Special case for empty set - allow the architecture code to set default SMP affinity. */ - return select_smp_affinity(irq) ? -EINVAL : full_count; + return irq_select_affinity(irq) ? -EINVAL : full_count; irq_set_affinity(irq, new_value); return full_count; } +static int default_affinity_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, irq_default_affinity); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int default_affinity_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + unsigned int full_count = count, err; + cpumask_t new_value; + + err = cpumask_parse_user(buffer, count, new_value); + if (err) + return err; + + if (!is_affinity_mask_valid(new_value)) + return -EINVAL; + + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!cpus_intersects(new_value, cpu_online_map)) + return -EINVAL; + + irq_default_affinity = new_value; + + return full_count; +} #endif static int irq_spurious_read(char *page, char **start, off_t off, @@ -171,6 +205,21 @@ void unregister_handler_proc(unsigned int irq, struct irqaction *action) remove_proc_entry(action->dir->name, irq_desc[irq].dir); } +void register_default_affinity_proc(void) +{ +#ifdef CONFIG_SMP + struct proc_dir_entry *entry; + + /* create /proc/irq/default_smp_affinity */ + entry = create_proc_entry("default_smp_affinity", 0600, root_irq_dir); + if (entry) { + entry->data = NULL; + entry->read_proc = default_affinity_read; + entry->write_proc = default_affinity_write; + } +#endif +} + void init_irq_proc(void) { int i; @@ -180,6 +229,8 @@ void init_irq_proc(void) if (!root_irq_dir) return; + register_default_affinity_proc(); + /* * Create entries for all existing IRQs. */ -- cgit v1.2.3 From 9457afee85e0dfc2b5075a391d6f34463b4c2b90 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 5 Jun 2008 11:23:39 -0700 Subject: netlink: Remove nonblock parameter from netlink_attachskb Signed-off-by: Denis V. Lunev Signed-off-by: David S. Miller --- include/linux/netlink.h | 2 +- ipc/mqueue.c | 2 +- net/netlink/af_netlink.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index bec1062a25a1..9ff1b54908f3 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -193,7 +193,7 @@ extern int netlink_unregister_notifier(struct notifier_block *nb); /* finegrained unicast helpers: */ struct sock *netlink_getsockbyfilp(struct file *filp); -int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, +int netlink_attachskb(struct sock *sk, struct sk_buff *skb, long *timeo, struct sock *ssk); void netlink_detachskb(struct sock *sk, struct sk_buff *skb); int netlink_sendskb(struct sock *sk, struct sk_buff *skb); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index b3b69fd51330..3e84b958186b 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -1054,7 +1054,7 @@ retry: } timeo = MAX_SCHEDULE_TIMEOUT; - ret = netlink_attachskb(sock, nc, 0, &timeo, NULL); + ret = netlink_attachskb(sock, nc, &timeo, NULL); if (ret == 1) goto retry; if (ret) { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 9b97f8006c9c..6507c02dbe0d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -759,7 +759,7 @@ struct sock *netlink_getsockbyfilp(struct file *filp) * 0: continue * 1: repeat lookup - reference dropped while waiting for socket memory. */ -int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, +int netlink_attachskb(struct sock *sk, struct sk_buff *skb, long *timeo, struct sock *ssk) { struct netlink_sock *nlk; @@ -892,7 +892,7 @@ retry: return err; } - err = netlink_attachskb(sk, skb, nonblock, &timeo, ssk); + err = netlink_attachskb(sk, skb, &timeo, ssk); if (err == 1) goto retry; if (err) -- cgit v1.2.3 From 554ec22f075d46e4363520a407d2b7eeb5dfdd43 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:21:03 +0200 Subject: namespacecheck: more sched.c fixes [ Stephen Rothwell : build fix ] Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index ae0be3c62375..dc36c3aea018 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -134,7 +134,6 @@ extern unsigned long nr_running(void); extern unsigned long nr_uninterruptible(void); extern unsigned long nr_active(void); extern unsigned long nr_iowait(void); -extern unsigned long weighted_cpuload(const int cpu); struct seq_file; struct cfs_rq; @@ -823,23 +822,6 @@ extern int arch_reinit_sched_domains(void); #endif /* CONFIG_SMP */ -/* - * A runqueue laden with a single nice 0 task scores a weighted_cpuload of - * SCHED_LOAD_SCALE. This function returns 1 if any cpu is laden with a - * task of nice 0 or enough lower priority tasks to bring up the - * weighted_cpuload - */ -static inline int above_background_load(void) -{ - unsigned long cpu; - - for_each_online_cpu(cpu) { - if (weighted_cpuload(cpu) >= SCHED_LOAD_SCALE) - return 1; - } - return 0; -} - struct io_context; /* See blkdev.h */ #define NGROUPS_SMALL 32 #define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(gid_t))) -- cgit v1.2.3 From c7aceaba042702538b23cf4e0de1b2891ad8e671 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Thu, 15 May 2008 12:09:15 +0100 Subject: sched: reorder task_struct to reduce padding on 64bit builds This patch removes 24 bytes of padding and allows 1 extra object per slab on my fedora based config. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index dc36c3aea018..ea2857b99596 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1021,6 +1021,7 @@ struct task_struct { #endif int prio, static_prio, normal_prio; + unsigned int rt_priority; const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; @@ -1104,7 +1105,6 @@ struct task_struct { int __user *set_child_tid; /* CLONE_CHILD_SETTID */ int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */ - unsigned int rt_priority; cputime_t utime, stime, utimescaled, stimescaled; cputime_t gtime; cputime_t prev_utime, prev_stime; @@ -1123,12 +1123,12 @@ struct task_struct { gid_t gid,egid,sgid,fsgid; struct group_info *group_info; kernel_cap_t cap_effective, cap_inheritable, cap_permitted, cap_bset; - unsigned securebits; struct user_struct *user; + unsigned securebits; #ifdef CONFIG_KEYS + unsigned char jit_keyring; /* default keyring to attach requested keys to */ struct key *request_key_auth; /* assumed request_key authority */ struct key *thread_keyring; /* keyring private to this thread */ - unsigned char jit_keyring; /* default keyring to attach requested keys to */ #endif char comm[TASK_COMM_LEN]; /* executable name excluding path - access with [gs]et_task_comm (which lock @@ -1215,8 +1215,8 @@ struct task_struct { # define MAX_LOCK_DEPTH 48UL u64 curr_chain_key; int lockdep_depth; - struct held_lock held_locks[MAX_LOCK_DEPTH]; unsigned int lockdep_recursion; + struct held_lock held_locks[MAX_LOCK_DEPTH]; #endif /* journalling filesystem info */ @@ -1244,10 +1244,6 @@ struct task_struct { u64 acct_vm_mem1; /* accumulated virtual memory usage */ cputime_t acct_stimexpd;/* stime since last update */ #endif -#ifdef CONFIG_NUMA - struct mempolicy *mempolicy; - short il_next; -#endif #ifdef CONFIG_CPUSETS nodemask_t mems_allowed; int cpuset_mems_generation; @@ -1266,6 +1262,10 @@ struct task_struct { #endif struct list_head pi_state_list; struct futex_pi_state *pi_state_cache; +#endif +#ifdef CONFIG_NUMA + struct mempolicy *mempolicy; + short il_next; #endif atomic_t fs_excl; /* holding fs exclusive resources */ struct rcu_head rcu; -- cgit v1.2.3 From 1f11eb6a8bc92536d9e93ead48fa3ffbd1478571 Mon Sep 17 00:00:00 2001 From: Gregory Haskins Date: Wed, 4 Jun 2008 15:04:05 -0400 Subject: sched: fix cpupri hotplug support The RT folks over at RedHat found an issue w.r.t. hotplug support which was traced to problems with the cpupri infrastructure in the scheduler: https://bugzilla.redhat.com/show_bug.cgi?id=449676 This bug affects 23-rt12+, 24-rtX, 25-rtX, and sched-devel. This patch applies to 25.4-rt4, though it should trivially apply to most cpupri enabled kernels mentioned above. It turned out that the issue was that offline cpus could get inadvertently registered with cpupri so that they were erroneously selected during migration decisions. The end result would be an OOPS as the offline cpu had tasks routed to it. This patch generalizes the old join/leave domain interface into an online/offline interface, and adjusts the root-domain/hotplug code to utilize it. I was able to easily reproduce the issue prior to this patch, and am no longer able to reproduce it after this patch. I can offline cpus indefinately and everything seems to be in working order. Thanks to Arnaldo (acme), Thomas, and Peter for doing the legwork to point me in the right direction. Also thank you to Peter for reviewing the early iterations of this patch. Signed-off-by: Gregory Haskins Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 4 ++-- kernel/sched.c | 54 ++++++++++++++++++++++++++++++++++++++------------- kernel/sched_rt.c | 24 +++++++++++++++++------ 3 files changed, 60 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index ea2857b99596..d25acf600a32 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -903,8 +903,8 @@ struct sched_class { void (*set_cpus_allowed)(struct task_struct *p, const cpumask_t *newmask); - void (*join_domain)(struct rq *rq); - void (*leave_domain)(struct rq *rq); + void (*rq_online)(struct rq *rq); + void (*rq_offline)(struct rq *rq); void (*switched_from) (struct rq *this_rq, struct task_struct *task, int running); diff --git a/kernel/sched.c b/kernel/sched.c index dc0be113f41d..f0ed81b71282 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -529,6 +529,7 @@ struct rq { int push_cpu; /* cpu of this runqueue: */ int cpu; + int online; struct task_struct *migration_thread; struct list_head migration_queue; @@ -1498,6 +1499,8 @@ static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares) #endif #define sched_class_highest (&rt_sched_class) +#define for_each_class(class) \ + for (class = sched_class_highest; class; class = class->next) static inline void inc_load(struct rq *rq, const struct task_struct *p) { @@ -6065,6 +6068,36 @@ static void unregister_sched_domain_sysctl(void) } #endif +static void set_rq_online(struct rq *rq) +{ + if (!rq->online) { + const struct sched_class *class; + + cpu_set(rq->cpu, rq->rd->online); + rq->online = 1; + + for_each_class(class) { + if (class->rq_online) + class->rq_online(rq); + } + } +} + +static void set_rq_offline(struct rq *rq) +{ + if (rq->online) { + const struct sched_class *class; + + for_each_class(class) { + if (class->rq_offline) + class->rq_offline(rq); + } + + cpu_clear(rq->cpu, rq->rd->online); + rq->online = 0; + } +} + /* * migration_call - callback that gets triggered when a CPU is added. * Here we can start up the necessary migration thread for the new CPU. @@ -6102,7 +6135,8 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) spin_lock_irqsave(&rq->lock, flags); if (rq->rd) { BUG_ON(!cpu_isset(cpu, rq->rd->span)); - cpu_set(cpu, rq->rd->online); + + set_rq_online(rq); } spin_unlock_irqrestore(&rq->lock, flags); break; @@ -6163,7 +6197,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) spin_lock_irqsave(&rq->lock, flags); if (rq->rd) { BUG_ON(!cpu_isset(cpu, rq->rd->span)); - cpu_clear(cpu, rq->rd->online); + set_rq_offline(rq); } spin_unlock_irqrestore(&rq->lock, flags); break; @@ -6385,20 +6419,16 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) static void rq_attach_root(struct rq *rq, struct root_domain *rd) { unsigned long flags; - const struct sched_class *class; spin_lock_irqsave(&rq->lock, flags); if (rq->rd) { struct root_domain *old_rd = rq->rd; - for (class = sched_class_highest; class; class = class->next) { - if (class->leave_domain) - class->leave_domain(rq); - } + if (cpu_isset(rq->cpu, old_rd->online)) + set_rq_offline(rq); cpu_clear(rq->cpu, old_rd->span); - cpu_clear(rq->cpu, old_rd->online); if (atomic_dec_and_test(&old_rd->refcount)) kfree(old_rd); @@ -6409,12 +6439,7 @@ static void rq_attach_root(struct rq *rq, struct root_domain *rd) cpu_set(rq->cpu, rd->span); if (cpu_isset(rq->cpu, cpu_online_map)) - cpu_set(rq->cpu, rd->online); - - for (class = sched_class_highest; class; class = class->next) { - if (class->join_domain) - class->join_domain(rq); - } + set_rq_online(rq); spin_unlock_irqrestore(&rq->lock, flags); } @@ -7824,6 +7849,7 @@ void __init sched_init(void) rq->next_balance = jiffies; rq->push_cpu = 0; rq->cpu = i; + rq->online = 0; rq->migration_thread = NULL; INIT_LIST_HEAD(&rq->migration_queue); rq_attach_root(rq, &def_root_domain); diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index 44b06d75416e..e4821593d4de 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -12,6 +12,9 @@ static inline int rt_overloaded(struct rq *rq) static inline void rt_set_overload(struct rq *rq) { + if (!rq->online) + return; + cpu_set(rq->cpu, rq->rd->rto_mask); /* * Make sure the mask is visible before we set @@ -26,6 +29,9 @@ static inline void rt_set_overload(struct rq *rq) static inline void rt_clear_overload(struct rq *rq) { + if (!rq->online) + return; + /* the order here really doesn't matter */ atomic_dec(&rq->rd->rto_count); cpu_clear(rq->cpu, rq->rd->rto_mask); @@ -394,7 +400,10 @@ void inc_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq) if (rt_se_prio(rt_se) < rt_rq->highest_prio) { struct rq *rq = rq_of_rt_rq(rt_rq); rt_rq->highest_prio = rt_se_prio(rt_se); - cpupri_set(&rq->rd->cpupri, rq->cpu, rt_se_prio(rt_se)); + + if (rq->online) + cpupri_set(&rq->rd->cpupri, rq->cpu, + rt_se_prio(rt_se)); } #endif #ifdef CONFIG_SMP @@ -448,7 +457,10 @@ void dec_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq) if (rt_rq->highest_prio != highest_prio) { struct rq *rq = rq_of_rt_rq(rt_rq); - cpupri_set(&rq->rd->cpupri, rq->cpu, rt_rq->highest_prio); + + if (rq->online) + cpupri_set(&rq->rd->cpupri, rq->cpu, + rt_rq->highest_prio); } update_rt_migration(rq_of_rt_rq(rt_rq)); @@ -1154,7 +1166,7 @@ static void set_cpus_allowed_rt(struct task_struct *p, } /* Assumes rq->lock is held */ -static void join_domain_rt(struct rq *rq) +static void rq_online_rt(struct rq *rq) { if (rq->rt.overloaded) rt_set_overload(rq); @@ -1163,7 +1175,7 @@ static void join_domain_rt(struct rq *rq) } /* Assumes rq->lock is held */ -static void leave_domain_rt(struct rq *rq) +static void rq_offline_rt(struct rq *rq) { if (rq->rt.overloaded) rt_clear_overload(rq); @@ -1331,8 +1343,8 @@ static const struct sched_class rt_sched_class = { .load_balance = load_balance_rt, .move_one_task = move_one_task_rt, .set_cpus_allowed = set_cpus_allowed_rt, - .join_domain = join_domain_rt, - .leave_domain = leave_domain_rt, + .rq_online = rq_online_rt, + .rq_offline = rq_offline_rt, .pre_schedule = pre_schedule_rt, .post_schedule = post_schedule_rt, .task_wake_up = task_wake_up_rt, -- cgit v1.2.3 From 93f65158723ceb7078ee9a0fd4830c0de00f4b9e Mon Sep 17 00:00:00 2001 From: Kuo-lang Tseng Date: Mon, 9 Jun 2008 15:55:45 -0700 Subject: netfilter: ebtables: add IPv6 support It implements matching functions for IPv6 address & traffic class (merged from the patch sent by Jan Engelhardt [jengelh@computergmbh.de] http://marc.info/?l=netfilter-devel&m=120182168424052&w=2), protocol, and layer-4 port id. Corresponding watcher logging function is also added for IPv6. Signed-off-by: Kuo-lang Tseng Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_bridge/ebt_ip6.h | 40 +++++++++ include/linux/netfilter_bridge/ebt_log.h | 3 +- net/bridge/netfilter/Kconfig | 9 ++ net/bridge/netfilter/Makefile | 1 + net/bridge/netfilter/ebt_ip6.c | 144 +++++++++++++++++++++++++++++++ net/bridge/netfilter/ebt_log.c | 64 ++++++++++---- 6 files changed, 244 insertions(+), 17 deletions(-) create mode 100644 include/linux/netfilter_bridge/ebt_ip6.h create mode 100644 net/bridge/netfilter/ebt_ip6.c (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h new file mode 100644 index 000000000000..2273c3ae33ca --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_ip6.h @@ -0,0 +1,40 @@ +/* + * ebt_ip6 + * + * Authors: + * Kuo-Lang Tseng + * Manohar Castelino + * + * Jan 11, 2008 + * + */ + +#ifndef __LINUX_BRIDGE_EBT_IP6_H +#define __LINUX_BRIDGE_EBT_IP6_H + +#define EBT_IP6_SOURCE 0x01 +#define EBT_IP6_DEST 0x02 +#define EBT_IP6_TCLASS 0x04 +#define EBT_IP6_PROTO 0x08 +#define EBT_IP6_SPORT 0x10 +#define EBT_IP6_DPORT 0x20 +#define EBT_IP6_MASK (EBT_IP6_SOURCE | EBT_IP6_DEST | EBT_IP6_TCLASS |\ + EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT) +#define EBT_IP6_MATCH "ip6" + +/* the same values are used for the invflags */ +struct ebt_ip6_info +{ + struct in6_addr saddr; + struct in6_addr daddr; + struct in6_addr smsk; + struct in6_addr dmsk; + uint8_t tclass; + uint8_t protocol; + uint8_t bitmask; + uint8_t invflags; + uint16_t sport[2]; + uint16_t dport[2]; +}; + +#endif diff --git a/include/linux/netfilter_bridge/ebt_log.h b/include/linux/netfilter_bridge/ebt_log.h index 96e231ae7554..b76e653157e5 100644 --- a/include/linux/netfilter_bridge/ebt_log.h +++ b/include/linux/netfilter_bridge/ebt_log.h @@ -4,7 +4,8 @@ #define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */ #define EBT_LOG_ARP 0x02 #define EBT_LOG_NFLOG 0x04 -#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) +#define EBT_LOG_IP6 0x08 +#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP | EBT_LOG_IP6) #define EBT_LOG_PREFIX_SIZE 30 #define EBT_LOG_WATCHER "log" diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 7beeefa0f9c0..fb684c2ff8b6 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -83,6 +83,15 @@ config BRIDGE_EBT_IP To compile it as a module, choose M here. If unsure, say N. +config BRIDGE_EBT_IP6 + tristate "ebt: IP6 filter support" + depends on BRIDGE_NF_EBTABLES + help + This option adds the IP6 match, which allows basic IPV6 header field + filtering. + + To compile it as a module, choose M here. If unsure, say N. + config BRIDGE_EBT_LIMIT tristate "ebt: limit match support" depends on BRIDGE_NF_EBTABLES diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 83715d73a503..dd960645b413 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o +obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip6.o obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c new file mode 100644 index 000000000000..36efb3a75249 --- /dev/null +++ b/net/bridge/netfilter/ebt_ip6.c @@ -0,0 +1,144 @@ +/* + * ebt_ip6 + * + * Authors: + * Manohar Castelino + * Kuo-Lang Tseng + * Jan Engelhardt + * + * Summary: + * This is just a modification of the IPv4 code written by + * Bart De Schuymer + * with the changes required to support IPv6 + * + * Jan, 2008 + */ + +#include +#include +#include +#include +#include +#include +#include + +struct tcpudphdr { + __be16 src; + __be16 dst; +}; + +static int ebt_filter_ip6(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, const void *data, + unsigned int datalen) +{ + const struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; + const struct ipv6hdr *ih6; + struct ipv6hdr _ip6h; + const struct tcpudphdr *pptr; + struct tcpudphdr _ports; + struct in6_addr tmp_addr; + int i; + + ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h); + if (ih6 == NULL) + return EBT_NOMATCH; + if (info->bitmask & EBT_IP6_TCLASS && + FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS)) + return EBT_NOMATCH; + for (i = 0; i < 4; i++) + tmp_addr.in6_u.u6_addr32[i] = ih6->saddr.in6_u.u6_addr32[i] & + info->smsk.in6_u.u6_addr32[i]; + if (info->bitmask & EBT_IP6_SOURCE && + FWINV((ipv6_addr_cmp(&tmp_addr, &info->saddr) != 0), + EBT_IP6_SOURCE)) + return EBT_NOMATCH; + for (i = 0; i < 4; i++) + tmp_addr.in6_u.u6_addr32[i] = ih6->daddr.in6_u.u6_addr32[i] & + info->dmsk.in6_u.u6_addr32[i]; + if (info->bitmask & EBT_IP6_DEST && + FWINV((ipv6_addr_cmp(&tmp_addr, &info->daddr) != 0), EBT_IP6_DEST)) + return EBT_NOMATCH; + if (info->bitmask & EBT_IP6_PROTO) { + uint8_t nexthdr = ih6->nexthdr; + int offset_ph; + + offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr); + if (offset_ph == -1) + return EBT_NOMATCH; + if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO)) + return EBT_NOMATCH; + if (!(info->bitmask & EBT_IP6_DPORT) && + !(info->bitmask & EBT_IP6_SPORT)) + return EBT_MATCH; + pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports), + &_ports); + if (pptr == NULL) + return EBT_NOMATCH; + if (info->bitmask & EBT_IP6_DPORT) { + u32 dst = ntohs(pptr->dst); + if (FWINV(dst < info->dport[0] || + dst > info->dport[1], EBT_IP6_DPORT)) + return EBT_NOMATCH; + } + if (info->bitmask & EBT_IP6_SPORT) { + u32 src = ntohs(pptr->src); + if (FWINV(src < info->sport[0] || + src > info->sport[1], EBT_IP6_SPORT)) + return EBT_NOMATCH; + } + return EBT_MATCH; + } + return EBT_MATCH; +} + +static int ebt_ip6_check(const char *tablename, unsigned int hookmask, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; + + if (datalen != EBT_ALIGN(sizeof(struct ebt_ip6_info))) + return -EINVAL; + if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) + return -EINVAL; + if (info->bitmask & ~EBT_IP6_MASK || info->invflags & ~EBT_IP6_MASK) + return -EINVAL; + if (info->bitmask & (EBT_IP6_DPORT | EBT_IP6_SPORT)) { + if (info->invflags & EBT_IP6_PROTO) + return -EINVAL; + if (info->protocol != IPPROTO_TCP && + info->protocol != IPPROTO_UDP && + info->protocol != IPPROTO_UDPLITE && + info->protocol != IPPROTO_SCTP && + info->protocol != IPPROTO_DCCP) + return -EINVAL; + } + if (info->bitmask & EBT_IP6_DPORT && info->dport[0] > info->dport[1]) + return -EINVAL; + if (info->bitmask & EBT_IP6_SPORT && info->sport[0] > info->sport[1]) + return -EINVAL; + return 0; +} + +static struct ebt_match filter_ip6 = +{ + .name = EBT_IP6_MATCH, + .match = ebt_filter_ip6, + .check = ebt_ip6_check, + .me = THIS_MODULE, +}; + +static int __init ebt_ip6_init(void) +{ + return ebt_register_match(&filter_ip6); +} + +static void __exit ebt_ip6_fini(void) +{ + ebt_unregister_match(&filter_ip6); +} + +module_init(ebt_ip6_init); +module_exit(ebt_ip6_fini); +MODULE_DESCRIPTION("Ebtables: IPv6 protocol packet match"); +MODULE_LICENSE("GPL"); diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 0b209e4aad0a..c883ec8a28b4 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include static DEFINE_SPINLOCK(ebt_log_lock); @@ -58,6 +61,27 @@ static void print_MAC(const unsigned char *p) printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':'); } +static void +print_ports(const struct sk_buff *skb, uint8_t protocol, int offset) +{ + if (protocol == IPPROTO_TCP || + protocol == IPPROTO_UDP || + protocol == IPPROTO_UDPLITE || + protocol == IPPROTO_SCTP || + protocol == IPPROTO_DCCP) { + const struct tcpudphdr *pptr; + struct tcpudphdr _ports; + + pptr = skb_header_pointer(skb, offset, + sizeof(_ports), &_ports); + if (pptr == NULL) { + printk(" INCOMPLETE TCP/UDP header"); + return; + } + printk(" SPT=%u DPT=%u", ntohs(pptr->src), ntohs(pptr->dst)); + } +} + #define myNIPQUAD(a) a[0], a[1], a[2], a[3] static void ebt_log_packet(unsigned int pf, unsigned int hooknum, @@ -95,23 +119,31 @@ ebt_log_packet(unsigned int pf, unsigned int hooknum, printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u, IP " "tos=0x%02X, IP proto=%d", NIPQUAD(ih->saddr), NIPQUAD(ih->daddr), ih->tos, ih->protocol); - if (ih->protocol == IPPROTO_TCP || - ih->protocol == IPPROTO_UDP || - ih->protocol == IPPROTO_UDPLITE || - ih->protocol == IPPROTO_SCTP || - ih->protocol == IPPROTO_DCCP) { - const struct tcpudphdr *pptr; - struct tcpudphdr _ports; - - pptr = skb_header_pointer(skb, ih->ihl*4, - sizeof(_ports), &_ports); - if (pptr == NULL) { - printk(" INCOMPLETE TCP/UDP header"); - goto out; - } - printk(" SPT=%u DPT=%u", ntohs(pptr->src), - ntohs(pptr->dst)); + print_ports(skb, ih->protocol, ih->ihl*4); + goto out; + } + + if ((bitmask & EBT_LOG_IP6) && eth_hdr(skb)->h_proto == + htons(ETH_P_IPV6)) { + const struct ipv6hdr *ih; + struct ipv6hdr _iph; + uint8_t nexthdr; + int offset_ph; + + ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); + if (ih == NULL) { + printk(" INCOMPLETE IPv6 header"); + goto out; } + printk(" IPv6 SRC=%x:%x:%x:%x:%x:%x:%x:%x " + "IPv6 DST=%x:%x:%x:%x:%x:%x:%x:%x, IPv6 " + "priority=0x%01X, Next Header=%d", NIP6(ih->saddr), + NIP6(ih->daddr), ih->priority, ih->nexthdr); + nexthdr = ih->nexthdr; + offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr); + if (offset_ph == -1) + goto out; + print_ports(skb, nexthdr, offset_ph); goto out; } -- cgit v1.2.3 From a258860e01b80e8f554a4ab1a6c95e6042eb8b73 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 9 Jun 2008 15:56:39 -0700 Subject: netfilter: ctnetlink: add full support for SCTP to ctnetlink This patch adds full support for SCTP to ctnetlink. This includes three new attributes: state, original vtag and reply vtag. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/nfnetlink_conntrack.h | 10 ++++ net/netfilter/nf_conntrack_proto_sctp.c | 80 +++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 0a383ac083cb..759bc043dc65 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -81,6 +81,7 @@ enum ctattr_protoinfo { CTA_PROTOINFO_UNSPEC, CTA_PROTOINFO_TCP, CTA_PROTOINFO_DCCP, + CTA_PROTOINFO_SCTP, __CTA_PROTOINFO_MAX }; #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) @@ -103,6 +104,15 @@ enum ctattr_protoinfo_dccp { }; #define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) +enum ctattr_protoinfo_sctp { + CTA_PROTOINFO_SCTP_UNSPEC, + CTA_PROTOINFO_SCTP_STATE, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + __CTA_PROTOINFO_SCTP_MAX +}; +#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1) + enum ctattr_counters { CTA_COUNTERS_UNSPEC, CTA_COUNTERS_PACKETS, /* old 64bit counters */ diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index cbf2e27a22b2..41183a4d2d62 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -463,6 +463,82 @@ static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb, return true; } +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + +#include +#include + +static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, + const struct nf_conn *ct) +{ + struct nlattr *nest_parms; + + read_lock_bh(&sctp_lock); + nest_parms = nla_nest_start(skb, CTA_PROTOINFO_SCTP | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + NLA_PUT_U8(skb, CTA_PROTOINFO_SCTP_STATE, ct->proto.sctp.state); + + NLA_PUT_BE32(skb, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + htonl(ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL])); + + NLA_PUT_BE32(skb, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + htonl(ct->proto.sctp.vtag[IP_CT_DIR_REPLY])); + + read_unlock_bh(&sctp_lock); + + nla_nest_end(skb, nest_parms); + + return 0; + +nla_put_failure: + read_unlock_bh(&sctp_lock); + return -1; +} + +static const struct nla_policy sctp_nla_policy[CTA_PROTOINFO_SCTP_MAX+1] = { + [CTA_PROTOINFO_SCTP_STATE] = { .type = NLA_U8 }, + [CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] = { .type = NLA_U32 }, + [CTA_PROTOINFO_SCTP_VTAG_REPLY] = { .type = NLA_U32 }, +}; + +static int nlattr_to_sctp(struct nlattr *cda[], struct nf_conn *ct) +{ + struct nlattr *attr = cda[CTA_PROTOINFO_SCTP]; + struct nlattr *tb[CTA_PROTOINFO_SCTP_MAX+1]; + int err; + + /* updates may not contain the internal protocol info, skip parsing */ + if (!attr) + return 0; + + err = nla_parse_nested(tb, + CTA_PROTOINFO_SCTP_MAX, + attr, + sctp_nla_policy); + if (err < 0) + return err; + + if (!tb[CTA_PROTOINFO_SCTP_STATE] || + !tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] || + !tb[CTA_PROTOINFO_SCTP_VTAG_REPLY]) + return -EINVAL; + + write_lock_bh(&sctp_lock); + ct->proto.sctp.state = nla_get_u8(tb[CTA_PROTOINFO_SCTP_STATE]); + ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = + ntohl(nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL])); + ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = + ntohl(nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_REPLY])); + write_unlock_bh(&sctp_lock); + + return 0; +} +#endif + #ifdef CONFIG_SYSCTL static unsigned int sctp_sysctl_table_users; static struct ctl_table_header *sctp_sysctl_header; @@ -591,6 +667,8 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .new = sctp_new, .me = THIS_MODULE, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .to_nlattr = sctp_to_nlattr, + .from_nlattr = nlattr_to_sctp, .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, @@ -617,6 +695,8 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .new = sctp_new, .me = THIS_MODULE, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .to_nlattr = sctp_to_nlattr, + .from_nlattr = nlattr_to_sctp, .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, -- cgit v1.2.3 From 560ee653b67074b805f1b661988a72a0e58811a5 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 9 Jun 2008 15:57:24 -0700 Subject: netfilter: ip_tables: add iptables security table for mandatory access control rules The following patch implements a new "security" table for iptables, so that MAC (SELinux etc.) networking rules can be managed separately to standard DAC rules. This is to help with distro integration of the new secmark-based network controls, per various previous discussions. The need for a separate table arises from the fact that existing tools and usage of iptables will likely clash with centralized MAC policy management. The SECMARK and CONNSECMARK targets will still be valid in the mangle table to prevent breakage of existing users. Signed-off-by: James Morris Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv4.h | 1 + include/net/netns/ipv4.h | 1 + net/ipv4/netfilter/Kconfig | 12 +++ net/ipv4/netfilter/Makefile | 1 + net/ipv4/netfilter/iptable_security.c | 180 ++++++++++++++++++++++++++++++++++ net/netfilter/xt_CONNSECMARK.c | 10 +- net/netfilter/xt_SECMARK.c | 10 +- 7 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 net/ipv4/netfilter/iptable_security.c (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index 650318b0c405..29c7727ff0e8 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -60,6 +60,7 @@ enum nf_ip_hook_priorities { NF_IP_PRI_MANGLE = -150, NF_IP_PRI_NAT_DST = -100, NF_IP_PRI_FILTER = 0, + NF_IP_PRI_SECURITY = 50, NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_SELINUX_LAST = 225, NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 34ee348a2cf2..6ef90b5fafb3 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -36,6 +36,7 @@ struct netns_ipv4 { struct xt_table *iptable_mangle; struct xt_table *iptable_raw; struct xt_table *arptable_filter; + struct xt_table *iptable_security; #endif int sysctl_icmp_echo_ignore_all; diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 2767841a8cef..6e251402506e 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -365,6 +365,18 @@ config IP_NF_RAW If you want to compile it as a module, say M here and read . If unsure, say `N'. +# security table for MAC policy +config IP_NF_SECURITY + tristate "Security table" + depends on IP_NF_IPTABLES + depends on SECURITY + default m if NETFILTER_ADVANCED=n + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. + + If unsure, say N. + # ARP tables config IP_NF_ARPTABLES tristate "ARP tables support" diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index d9b92fbf5579..3f31291f37ce 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o obj-$(CONFIG_NF_NAT) += iptable_nat.o obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o +obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o # matches obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c new file mode 100644 index 000000000000..2b472ac2263a --- /dev/null +++ b/net/ipv4/netfilter/iptable_security.c @@ -0,0 +1,180 @@ +/* + * "security" table + * + * This is for use by Mandatory Access Control (MAC) security models, + * which need to be able to manage security policy in separate context + * to DAC. + * + * Based on iptable_mangle.c + * + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * Copyright (C) 2000-2004 Netfilter Core Team netfilter.org> + * Copyright (C) 2008 Red Hat, Inc., James Morris redhat.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 +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Morris redhat.com>"); +MODULE_DESCRIPTION("iptables security table, for MAC rules"); + +#define SECURITY_VALID_HOOKS (1 << NF_INET_LOCAL_IN) | \ + (1 << NF_INET_FORWARD) | \ + (1 << NF_INET_LOCAL_OUT) + +static struct +{ + struct ipt_replace repl; + struct ipt_standard entries[3]; + struct ipt_error term; +} initial_table __initdata = { + .repl = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), + .hook_entry = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ipt_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ipt_standard) * 2, + }, + .underflow = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ipt_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ipt_standard) * 2, + }, + }, + .entries = { + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IPT_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + }, + .term = IPT_ERROR_INIT, /* ERROR */ +}; + +static struct xt_table security_table = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .lock = __RW_LOCK_UNLOCKED(security_table.lock), + .me = THIS_MODULE, + .af = AF_INET, +}; + +static unsigned int +ipt_local_in_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ipt_do_table(skb, hook, in, out, + nf_local_in_net(in, out)->ipv4.iptable_security); +} + +static unsigned int +ipt_forward_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ipt_do_table(skb, hook, in, out, + nf_forward_net(in, out)->ipv4.iptable_security); +} + +static unsigned int +ipt_local_out_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + /* Somebody is playing with raw sockets. */ + if (skb->len < sizeof(struct iphdr) + || ip_hdrlen(skb) < sizeof(struct iphdr)) { + if (net_ratelimit()) + printk(KERN_INFO "iptable_security: ignoring short " + "SOCK_RAW packet.\n"); + return NF_ACCEPT; + } + return ipt_do_table(skb, hook, in, out, + nf_local_out_net(in, out)->ipv4.iptable_security); +} + +static struct nf_hook_ops ipt_ops[] __read_mostly = { + { + .hook = ipt_local_in_hook, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_SECURITY, + }, + { + .hook = ipt_forward_hook, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_SECURITY, + }, + { + .hook = ipt_local_out_hook, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_SECURITY, + }, +}; + +static int __net_init iptable_security_net_init(struct net *net) +{ + net->ipv4.iptable_security = + ipt_register_table(net, &security_table, &initial_table.repl); + + if (IS_ERR(net->ipv4.iptable_security)) + return PTR_ERR(net->ipv4.iptable_security); + + return 0; +} + +static void __net_exit iptable_security_net_exit(struct net *net) +{ + ipt_unregister_table(net->ipv4.iptable_security); +} + +static struct pernet_operations iptable_security_net_ops = { + .init = iptable_security_net_init, + .exit = iptable_security_net_exit, +}; + +static int __init iptable_security_init(void) +{ + int ret; + + ret = register_pernet_subsys(&iptable_security_net_ops); + if (ret < 0) + return ret; + + ret = nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); + if (ret < 0) + goto cleanup_table; + + return ret; + +cleanup_table: + unregister_pernet_subsys(&iptable_security_net_ops); + return ret; +} + +static void __exit iptable_security_fini(void) +{ + nf_unregister_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); + unregister_pernet_subsys(&iptable_security_net_ops); +} + +module_init(iptable_security_init); +module_exit(iptable_security_fini); diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 211189eb2b67..76ca1f2421eb 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -8,7 +8,7 @@ * Copyright (C) 2002,2004 MARA Systems AB * by Henrik Nordstrom * - * (C) 2006 Red Hat, Inc., James Morris + * (C) 2006,2008 Red Hat, Inc., James Morris * * 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 @@ -94,6 +94,12 @@ connsecmark_tg_check(const char *tablename, const void *entry, { const struct xt_connsecmark_target_info *info = targinfo; + if (strcmp(tablename, "mangle") && strcmp(tablename, "security")) { + printk(KERN_INFO PFX "target only valid in the \'mangle\' " + "or \'security\' tables, not \'%s\'.\n", tablename); + return false; + } + switch (info->mode) { case CONNSECMARK_SAVE: case CONNSECMARK_RESTORE: @@ -126,7 +132,6 @@ static struct xt_target connsecmark_tg_reg[] __read_mostly = { .destroy = connsecmark_tg_destroy, .target = connsecmark_tg, .targetsize = sizeof(struct xt_connsecmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, { @@ -136,7 +141,6 @@ static struct xt_target connsecmark_tg_reg[] __read_mostly = { .destroy = connsecmark_tg_destroy, .target = connsecmark_tg, .targetsize = sizeof(struct xt_connsecmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, }; diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index c0284856ccd4..94f87ee7552b 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -5,7 +5,7 @@ * Based on the nfmark match by: * (C) 1999-2001 Marc Boucher * - * (C) 2006 Red Hat, Inc., James Morris + * (C) 2006,2008 Red Hat, Inc., James Morris * * 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 @@ -89,6 +89,12 @@ secmark_tg_check(const char *tablename, const void *entry, { struct xt_secmark_target_info *info = targinfo; + if (strcmp(tablename, "mangle") && strcmp(tablename, "security")) { + printk(KERN_INFO PFX "target only valid in the \'mangle\' " + "or \'security\' tables, not \'%s\'.\n", tablename); + return false; + } + if (mode && mode != info->mode) { printk(KERN_INFO PFX "mode already set to %hu cannot mix with " "rules for mode %hu\n", mode, info->mode); @@ -127,7 +133,6 @@ static struct xt_target secmark_tg_reg[] __read_mostly = { .destroy = secmark_tg_destroy, .target = secmark_tg, .targetsize = sizeof(struct xt_secmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, { @@ -137,7 +142,6 @@ static struct xt_target secmark_tg_reg[] __read_mostly = { .destroy = secmark_tg_destroy, .target = secmark_tg, .targetsize = sizeof(struct xt_secmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, }; -- cgit v1.2.3 From 17e6e59f0a1d7188d783c15dc3ccebd95a0840cd Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 9 Jun 2008 15:58:05 -0700 Subject: netfilter: ip6_tables: add ip6tables security table This is a port of the IPv4 security table for IPv6. Signed-off-by: James Morris Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv6.h | 1 + include/net/netns/ipv6.h | 1 + net/ipv6/netfilter/Kconfig | 12 +++ net/ipv6/netfilter/Makefile | 1 + net/ipv6/netfilter/ip6table_security.c | 172 +++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 net/ipv6/netfilter/ip6table_security.c (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 3475a65dae9b..fd50988b83ec 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -64,6 +64,7 @@ enum nf_ip6_hook_priorities { NF_IP6_PRI_MANGLE = -150, NF_IP6_PRI_NAT_DST = -100, NF_IP6_PRI_FILTER = 0, + NF_IP6_PRI_SECURITY = 50, NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_SELINUX_LAST = 225, NF_IP6_PRI_LAST = INT_MAX, diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index ac053be6c256..5bacd838e88b 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -35,6 +35,7 @@ struct netns_ipv6 { struct xt_table *ip6table_filter; struct xt_table *ip6table_mangle; struct xt_table *ip6table_raw; + struct xt_table *ip6table_security; #endif struct rt6_info *ip6_null_entry; struct rt6_statistics *rt6_stats; diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 6cae5475737e..689dec899c57 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -208,5 +208,17 @@ config IP6_NF_RAW If you want to compile it as a module, say M here and read . If unsure, say `N'. +# security table for MAC policy +config IP6_NF_SECURITY + tristate "Security table" + depends on IP6_NF_IPTABLES + depends on SECURITY + default m if NETFILTER_ADVANCED=n + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. + + If unsure, say N. + endmenu diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index fbf2c14ed887..3f17c948eefb 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o +obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o # objects for l3 independent conntrack nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c new file mode 100644 index 000000000000..063a3d9c3c67 --- /dev/null +++ b/net/ipv6/netfilter/ip6table_security.c @@ -0,0 +1,172 @@ +/* + * "security" table for IPv6 + * + * This is for use by Mandatory Access Control (MAC) security models, + * which need to be able to manage security policy in separate context + * to DAC. + * + * Based on iptable_mangle.c + * + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * Copyright (C) 2000-2004 Netfilter Core Team netfilter.org> + * Copyright (C) 2008 Red Hat, Inc., James Morris redhat.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 +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Morris redhat.com>"); +MODULE_DESCRIPTION("ip6tables security table, for MAC rules"); + +#define SECURITY_VALID_HOOKS (1 << NF_INET_LOCAL_IN) | \ + (1 << NF_INET_FORWARD) | \ + (1 << NF_INET_LOCAL_OUT) + +static struct +{ + struct ip6t_replace repl; + struct ip6t_standard entries[3]; + struct ip6t_error term; +} initial_table __initdata = { + .repl = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error), + .hook_entry = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ip6t_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2, + }, + .underflow = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ip6t_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2, + }, + }, + .entries = { + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + }, + .term = IP6T_ERROR_INIT, /* ERROR */ +}; + +static struct xt_table security_table = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .lock = __RW_LOCK_UNLOCKED(security_table.lock), + .me = THIS_MODULE, + .af = AF_INET6, +}; + +static unsigned int +ip6t_local_in_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ip6t_do_table(skb, hook, in, out, + init_net.ipv6.ip6table_security); +} + +static unsigned int +ip6t_forward_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ip6t_do_table(skb, hook, in, out, + init_net.ipv6.ip6table_security); +} + +static unsigned int +ip6t_local_out_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + /* TBD: handle short packets via raw socket */ + return ip6t_do_table(skb, hook, in, out, + init_net.ipv6.ip6table_security); +} + +static struct nf_hook_ops ip6t_ops[] __read_mostly = { + { + .hook = ip6t_local_in_hook, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP6_PRI_SECURITY, + }, + { + .hook = ip6t_forward_hook, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP6_PRI_SECURITY, + }, + { + .hook = ip6t_local_out_hook, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP6_PRI_SECURITY, + }, +}; + +static int __net_init ip6table_security_net_init(struct net *net) +{ + net->ipv6.ip6table_security = + ip6t_register_table(net, &security_table, &initial_table.repl); + + if (IS_ERR(net->ipv6.ip6table_security)) + return PTR_ERR(net->ipv6.ip6table_security); + + return 0; +} + +static void __net_exit ip6table_security_net_exit(struct net *net) +{ + ip6t_unregister_table(net->ipv6.ip6table_security); +} + +static struct pernet_operations ip6table_security_net_ops = { + .init = ip6table_security_net_init, + .exit = ip6table_security_net_exit, +}; + +static int __init ip6table_security_init(void) +{ + int ret; + + ret = register_pernet_subsys(&ip6table_security_net_ops); + if (ret < 0) + return ret; + + ret = nf_register_hooks(ip6t_ops, ARRAY_SIZE(ip6t_ops)); + if (ret < 0) + goto cleanup_table; + + return ret; + +cleanup_table: + unregister_pernet_subsys(&ip6table_security_net_ops); + return ret; +} + +static void __exit ip6table_security_fini(void) +{ + nf_unregister_hooks(ip6t_ops, ARRAY_SIZE(ip6t_ops)); + unregister_pernet_subsys(&ip6table_security_net_ops); +} + +module_init(ip6table_security_init); +module_exit(ip6table_security_fini); -- cgit v1.2.3 From cc1a9d86ce989083703c4bdc11b75a87e1cc404a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sun, 8 Jun 2008 19:39:16 -0700 Subject: mm, x86: shrink_active_range() should check all Now we are using register_e820_active_regions() instead of add_active_range() directly. So end_pfn could be different between the value in early_node_map to node_end_pfn. So we need to make shrink_active_range() smarter. shrink_active_range() is a generic MM function in mm/page_alloc.c but it is only used on 32-bit x86. Should we move it back to some file in arch/x86? Signed-off-by: Yinghai Lu Signed-off-by: Ingo Molnar --- arch/x86/mm/discontig_32.c | 2 +- include/linux/mm.h | 3 +-- mm/page_alloc.c | 44 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 36 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index a89ccf3d4c14..489605bab85a 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -282,7 +282,7 @@ static unsigned long calculate_numa_remap_pages(void) node_end_pfn[nid] -= size; node_remap_start_pfn[nid] = node_end_pfn[nid]; - shrink_active_range(nid, old_end_pfn, node_end_pfn[nid]); + shrink_active_range(nid, node_end_pfn[nid]); } printk("Reserving total of %ld pages for numa KVA remap\n", reserve_pages); diff --git a/include/linux/mm.h b/include/linux/mm.h index c31a9cd2a30e..7cbd949f2516 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -997,8 +997,7 @@ extern void free_area_init_node(int nid, pg_data_t *pgdat, extern void free_area_init_nodes(unsigned long *max_zone_pfn); extern void add_active_range(unsigned int nid, unsigned long start_pfn, unsigned long end_pfn); -extern void shrink_active_range(unsigned int nid, unsigned long old_end_pfn, - unsigned long new_end_pfn); +extern void shrink_active_range(unsigned int nid, unsigned long new_end_pfn); extern void push_node_boundaries(unsigned int nid, unsigned long start_pfn, unsigned long end_pfn); extern void remove_all_active_ranges(void); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 502223c3c2c6..215408684076 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3579,25 +3579,49 @@ void __init add_active_range(unsigned int nid, unsigned long start_pfn, /** * shrink_active_range - Shrink an existing registered range of PFNs * @nid: The node id the range is on that should be shrunk - * @old_end_pfn: The old end PFN of the range * @new_end_pfn: The new PFN of the range * * i386 with NUMA use alloc_remap() to store a node_mem_map on a local node. - * The map is kept at the end physical page range that has already been - * registered with add_active_range(). This function allows an arch to shrink - * an existing registered range. + * The map is kept near the end physical page range that has already been + * registered. This function allows an arch to shrink an existing registered + * range. */ -void __init shrink_active_range(unsigned int nid, unsigned long old_end_pfn, - unsigned long new_end_pfn) +void __init shrink_active_range(unsigned int nid, unsigned long new_end_pfn) { - int i; + int i, j; + int removed = 0; /* Find the old active region end and shrink */ - for_each_active_range_index_in_nid(i, nid) - if (early_node_map[i].end_pfn == old_end_pfn) { + for_each_active_range_index_in_nid(i, nid) { + if (early_node_map[i].start_pfn >= new_end_pfn) { + /* clear it */ + early_node_map[i].end_pfn = 0; + removed = 1; + continue; + } + if (early_node_map[i].end_pfn > new_end_pfn) { early_node_map[i].end_pfn = new_end_pfn; - break; + continue; } + } + + if (!removed) + return; + + /* remove the blank ones */ + for (i = nr_nodemap_entries - 1; i > 0; i--) { + if (early_node_map[i].nid != nid) + continue; + if (early_node_map[i].end_pfn) + continue; + /* we found it, get rid of it */ + for (j = i; j < nr_nodemap_entries - 1; j++) + memcpy(&early_node_map[j], &early_node_map[j+1], + sizeof(early_node_map[j])); + j = nr_nodemap_entries - 1; + memset(&early_node_map[j], 0, sizeof(early_node_map[j])); + nr_nodemap_entries--; + } } /** -- cgit v1.2.3 From 0eb967012ea15e6e8cfab483d9fa37bc602d400c Mon Sep 17 00:00:00 2001 From: Abhishek Sagar Date: Sun, 1 Jun 2008 21:47:30 +0530 Subject: ftrace: prevent freeing of all failed updates Prevent freeing of records which cause problems and correspond to function from core kernel text. A new flag, FTRACE_FL_CONVERTED is used to mark a record as "converted". All other records are patched lazily to NOPs. Failed records now also remain on frace_hash table. Each invocation of ftrace_record_ip now checks whether the traced function has ever been recorded (including past failures) and doesn't re-record it again. Signed-off-by: Abhishek Sagar Signed-off-by: Ingo Molnar --- include/linux/ftrace.h | 1 + kernel/trace/ftrace.c | 76 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 623819433ed5..20e14d0093c7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -49,6 +49,7 @@ enum { FTRACE_FL_FILTER = (1 << 2), FTRACE_FL_ENABLED = (1 << 3), FTRACE_FL_NOTRACE = (1 << 4), + FTRACE_FL_CONVERTED = (1 << 5), }; struct dyn_ftrace { diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f762f5a2d331..ec54cb7d69d6 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -216,6 +216,12 @@ ftrace_add_hash(struct dyn_ftrace *node, unsigned long key) hlist_add_head_rcu(&node->node, &ftrace_hash[key]); } +/* called from kstop_machine */ +static inline void ftrace_del_hash(struct dyn_ftrace *node) +{ + hlist_del(&node->node); +} + static void ftrace_free_rec(struct dyn_ftrace *rec) { /* no locking, only called from kstop_machine */ @@ -332,12 +338,11 @@ ftrace_record_ip(unsigned long ip) #define FTRACE_ADDR ((long)(ftrace_caller)) #define MCOUNT_ADDR ((long)(mcount)) -static void +static int __ftrace_replace_code(struct dyn_ftrace *rec, unsigned char *old, unsigned char *new, int enable) { unsigned long ip, fl; - int failed; ip = rec->ip; @@ -364,7 +369,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || (fl == 0) || (rec->flags & FTRACE_FL_NOTRACE)) - return; + return 0; /* * If it is enabled disable it, @@ -388,7 +393,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, */ fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); if (fl == FTRACE_FL_NOTRACE) - return; + return 0; new = ftrace_call_replace(ip, FTRACE_ADDR); } else @@ -396,34 +401,24 @@ __ftrace_replace_code(struct dyn_ftrace *rec, if (enable) { if (rec->flags & FTRACE_FL_ENABLED) - return; + return 0; rec->flags |= FTRACE_FL_ENABLED; } else { if (!(rec->flags & FTRACE_FL_ENABLED)) - return; + return 0; rec->flags &= ~FTRACE_FL_ENABLED; } } - failed = ftrace_modify_code(ip, old, new); - if (failed) { - unsigned long key; - /* It is possible that the function hasn't been converted yet */ - key = hash_long(ip, FTRACE_HASHBITS); - if (!ftrace_ip_in_hash(ip, key)) { - rec->flags |= FTRACE_FL_FAILED; - ftrace_free_rec(rec); - } - - } + return ftrace_modify_code(ip, old, new); } static void ftrace_replace_code(int enable) { + int i, failed; unsigned char *new = NULL, *old = NULL; struct dyn_ftrace *rec; struct ftrace_page *pg; - int i; if (enable) old = ftrace_nop_replace(); @@ -438,7 +433,15 @@ static void ftrace_replace_code(int enable) if (rec->flags & FTRACE_FL_FAILED) continue; - __ftrace_replace_code(rec, old, new, enable); + failed = __ftrace_replace_code(rec, old, new, enable); + if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { + rec->flags |= FTRACE_FL_FAILED; + if ((system_state == SYSTEM_BOOTING) || + !kernel_text_address(rec->ip)) { + ftrace_del_hash(rec); + ftrace_free_rec(rec); + } + } } } } @@ -467,7 +470,6 @@ ftrace_code_disable(struct dyn_ftrace *rec) failed = ftrace_modify_code(ip, call, nop); if (failed) { rec->flags |= FTRACE_FL_FAILED; - ftrace_free_rec(rec); return 0; } return 1; @@ -621,8 +623,7 @@ unsigned long ftrace_update_tot_cnt; static int __ftrace_update_code(void *ignore) { struct dyn_ftrace *p; - struct hlist_head head; - struct hlist_node *t; + struct hlist_node *t, *n; int save_ftrace_enabled; cycle_t start, stop; int i; @@ -637,18 +638,33 @@ static int __ftrace_update_code(void *ignore) /* No locks needed, the machine is stopped! */ for (i = 0; i < FTRACE_HASHSIZE; i++) { - if (hlist_empty(&ftrace_hash[i])) - continue; + /* all CPUS are stopped, we are safe to modify code */ + hlist_for_each_entry_safe(p, t, n, &ftrace_hash[i], node) { + /* Skip over failed records which have not been + * freed. */ + if (p->flags & FTRACE_FL_FAILED) + continue; - head = ftrace_hash[i]; - INIT_HLIST_HEAD(&ftrace_hash[i]); + /* Unconverted records are always at the head of the + * hash bucket. Once we encounter a converted record, + * simply skip over to the next bucket. Saves ftraced + * some processor cycles (ftrace does its bid for + * global warming :-p ). */ + if (p->flags & (FTRACE_FL_CONVERTED)) + break; - /* all CPUS are stopped, we are safe to modify code */ - hlist_for_each_entry(p, t, &head, node) { - if (ftrace_code_disable(p)) + if (ftrace_code_disable(p)) { + p->flags |= FTRACE_FL_CONVERTED; ftrace_update_cnt++; - } + } else { + if ((system_state == SYSTEM_BOOTING) || + !kernel_text_address(p->ip)) { + ftrace_del_hash(p); + ftrace_free_rec(p); + } + } + } } stop = ftrace_now(raw_smp_processor_id()); -- cgit v1.2.3 From 9985b0bab332289f14837eff3c6e0bcc658b58f7 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Thu, 5 Jun 2008 12:57:11 -0700 Subject: sched: prevent bound kthreads from changing cpus_allowed Kthreads that have called kthread_bind() are bound to specific cpus, so other tasks should not be able to change their cpus_allowed from under them. Otherwise, it is possible to move kthreads, such as the migration or software watchdog threads, so they are not allowed access to the cpu they work on. Cc: Peter Zijlstra Cc: Paul Menage Cc: Paul Jackson Signed-off-by: David Rientjes Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 + kernel/cpuset.c | 14 +++++++++++++- kernel/kthread.c | 1 + kernel/sched.c | 6 ++++++ 4 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index d25acf600a32..2db1485f865d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1486,6 +1486,7 @@ static inline void put_task_struct(struct task_struct *t) #define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */ #define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */ #define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */ +#define PF_THREAD_BOUND 0x04000000 /* Thread bound to specific cpu */ #define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ #define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ #define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */ diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 6090d18b58a9..b84354f4de36 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1190,6 +1190,15 @@ static int cpuset_can_attach(struct cgroup_subsys *ss, if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed)) return -ENOSPC; + if (tsk->flags & PF_THREAD_BOUND) { + cpumask_t mask; + + mutex_lock(&callback_mutex); + mask = cs->cpus_allowed; + mutex_unlock(&callback_mutex); + if (!cpus_equal(tsk->cpus_allowed, mask)) + return -EINVAL; + } return security_task_setscheduler(tsk, 0, NULL); } @@ -1203,11 +1212,14 @@ static void cpuset_attach(struct cgroup_subsys *ss, struct mm_struct *mm; struct cpuset *cs = cgroup_cs(cont); struct cpuset *oldcs = cgroup_cs(oldcont); + int err; mutex_lock(&callback_mutex); guarantee_online_cpus(cs, &cpus); - set_cpus_allowed_ptr(tsk, &cpus); + err = set_cpus_allowed_ptr(tsk, &cpus); mutex_unlock(&callback_mutex); + if (err) + return; from = oldcs->mems_allowed; to = cs->mems_allowed; diff --git a/kernel/kthread.c b/kernel/kthread.c index bd1b9ea024e1..97747cdd37c9 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -180,6 +180,7 @@ void kthread_bind(struct task_struct *k, unsigned int cpu) set_task_cpu(k, cpu); k->cpus_allowed = cpumask_of_cpu(cpu); k->rt.nr_cpus_allowed = 1; + k->flags |= PF_THREAD_BOUND; } EXPORT_SYMBOL(kthread_bind); diff --git a/kernel/sched.c b/kernel/sched.c index e9c24a128655..164fe7fe0d89 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5563,6 +5563,12 @@ int set_cpus_allowed_ptr(struct task_struct *p, const cpumask_t *new_mask) goto out; } + if (unlikely((p->flags & PF_THREAD_BOUND) && p != current && + !cpus_equal(p->cpus_allowed, *new_mask))) { + ret = -EINVAL; + goto out; + } + if (p->sched_class->set_cpus_allowed) p->sched_class->set_cpus_allowed(p, new_mask); else { -- cgit v1.2.3 From 19792a0859f96e9fc8ce87d97b269bcb895389e5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 5 May 2008 21:25:47 +0300 Subject: PCI: drivers/pci/pci.c: add prototypes This patch adds prototypes for pcibios_disable_device() and pcibios_set_pcie_reset_state() in include/linux/pci.h While I was at it, I also removed the unneeded "extern" from the prototype of pcibios_add_platform_entries(). Signed-off-by: Adrian Bunk Signed-off-by: Jesse Barnes --- include/linux/pci.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 509159bcd4e7..aaa9f333fb44 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1059,7 +1059,10 @@ extern int pci_pci_problems; extern unsigned long pci_cardbus_io_size; extern unsigned long pci_cardbus_mem_size; -extern int pcibios_add_platform_entries(struct pci_dev *dev); +int pcibios_add_platform_entries(struct pci_dev *dev); +void pcibios_disable_device(struct pci_dev *dev); +int pcibios_set_pcie_reset_state(struct pci_dev *dev, + enum pcie_reset_state state); #ifdef CONFIG_PCI_MMCONFIG extern void __init pci_mmcfg_early_init(void); -- cgit v1.2.3 From e1a2a51e684bfe9d6165992d4a065439617a3107 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 May 2008 21:51:31 +0200 Subject: Suspend/Resume bug in PCI layer wrt quirks Some quirks should be called with interrupt disabled, we can't directly call them in .resume_early. Also the patch introduces pci_fixup_resume_early and pci_fixup_suspend, which matches current device core callbacks (.suspend/.resume_early). TBD: Somebody knows why we need quirk resume should double check if a quirk should be called in resume or resume_early. I changed some per my understanding, but can't make sure I fixed all. Signed-off-by: Shaohua Li Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 6 +- drivers/pci/quirks.c | 118 ++++++++++++++++++++++++++------------ include/asm-generic/vmlinux.lds.h | 6 ++ include/linux/pci.h | 10 +++- 4 files changed, 101 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 72cf61ed8f96..677fd9d6db12 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -292,6 +292,9 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) if (pci_dev->current_state == PCI_D0) pci_dev->current_state = PCI_UNKNOWN; } + + pci_fixup_device(pci_fixup_suspend, pci_dev); + return i; } @@ -337,6 +340,7 @@ static int pci_device_resume(struct device * dev) error = drv->resume(pci_dev); else error = pci_default_resume(pci_dev); + pci_fixup_device(pci_fixup_resume, pci_dev); return error; } @@ -346,7 +350,7 @@ static int pci_device_resume_early(struct device * dev) struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume, pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); if (drv && drv->resume_early) error = drv->resume_early(pci_dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index dabb563f51d9..44aabb1519a9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -556,7 +556,7 @@ static void quirk_via_ioapic(struct pci_dev *dev) pci_write_config_byte (dev, 0x58, tmp); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); /* * VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit. @@ -576,7 +576,7 @@ static void quirk_via_vt8237_bypass_apic_deassert(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); /* * The AMD io apic can hang the box when an apic irq is masked. @@ -622,7 +622,7 @@ static void quirk_amd_8131_ioapic(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); #endif /* CONFIG_X86_IO_APIC */ /* @@ -774,7 +774,7 @@ static void quirk_cardbus_legacy(struct pci_dev *dev) pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0); } DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); -DECLARE_PCI_FIXUP_RESUME(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); /* * Following the PCI ordering rules is optional on the AMD762. I'm not @@ -797,7 +797,7 @@ static void quirk_amd_ordering(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); /* * DreamWorks provided workaround for Dunord I-3000 problem @@ -865,7 +865,7 @@ static void quirk_disable_pxb(struct pci_dev *pdev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) { @@ -885,9 +885,9 @@ static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); /* * Serverworks CSB5 IDE does not fully support native mode @@ -1093,31 +1093,61 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asu DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); -static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev) +/* It appears we just have one such device. If not, we have a warning */ +static void __iomem *asus_rcba_base; +static void asus_hides_smbus_lpc_ich6_suspend(struct pci_dev *dev) { - u32 val, rcba; - void __iomem *base; + u32 rcba; if (likely(!asus_hides_smbus)) return; + WARN_ON(asus_rcba_base); + pci_read_config_dword(dev, 0xF0, &rcba); - base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */ - if (base == NULL) return; - val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */ - writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */ - iounmap(base); + /* use bits 31:14, 16 kB aligned */ + asus_rcba_base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); + if (asus_rcba_base == NULL) + return; +} + +static void asus_hides_smbus_lpc_ich6_resume_early(struct pci_dev *dev) +{ + u32 val; + + if (likely(!asus_hides_smbus || !asus_rcba_base)) + return; + /* read the Function Disable register, dword mode only */ + val = readl(asus_rcba_base + 0x3418); + writel(val & 0xFFFFFFF7, asus_rcba_base + 0x3418); /* enable the SMBus device */ +} + +static void asus_hides_smbus_lpc_ich6_resume(struct pci_dev *dev) +{ + if (likely(!asus_hides_smbus || !asus_rcba_base)) + return; + iounmap(asus_rcba_base); + asus_rcba_base = NULL; dev_info(&dev->dev, "Enabled ICH6/i801 SMBus device\n"); } + +static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev) +{ + asus_hides_smbus_lpc_ich6_suspend(dev); + asus_hides_smbus_lpc_ich6_resume_early(dev); + asus_hides_smbus_lpc_ich6_resume(dev); +} DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6); +DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_suspend); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume_early); /* * SiS 96x south bridge: BIOS typically hides SMBus device... @@ -1135,10 +1165,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); /* * ... This is further complicated by the fact that some SiS96x south @@ -1172,7 +1202,7 @@ static void quirk_sis_503(struct pci_dev *dev) quirk_sis_96x_smbus(dev); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); /* @@ -1205,7 +1235,7 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); #if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE) @@ -1270,12 +1300,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, qui DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); #endif @@ -1521,6 +1551,10 @@ extern struct pci_fixup __start_pci_fixups_enable[]; extern struct pci_fixup __end_pci_fixups_enable[]; extern struct pci_fixup __start_pci_fixups_resume[]; extern struct pci_fixup __end_pci_fixups_resume[]; +extern struct pci_fixup __start_pci_fixups_resume_early[]; +extern struct pci_fixup __end_pci_fixups_resume_early[]; +extern struct pci_fixup __start_pci_fixups_suspend[]; +extern struct pci_fixup __end_pci_fixups_suspend[]; void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) @@ -1553,6 +1587,16 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) end = __end_pci_fixups_resume; break; + case pci_fixup_resume_early: + start = __start_pci_fixups_resume_early; + end = __end_pci_fixups_resume_early; + break; + + case pci_fixup_suspend: + start = __start_pci_fixups_suspend; + end = __end_pci_fixups_suspend; + break; + default: /* stupid compiler warning, you would think with an enum... */ return; @@ -1629,7 +1673,7 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, quirk_nvidia_ck804_pcie_aer_ext_cap); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, quirk_nvidia_ck804_pcie_aer_ext_cap); static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index f054778e916c..cf108a3c7f59 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -84,6 +84,12 @@ VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \ *(.pci_fixup_resume) \ VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .; \ + *(.pci_fixup_resume_early) \ + VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ + *(.pci_fixup_suspend) \ + VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \ } \ \ /* RapidIO route ops */ \ diff --git a/include/linux/pci.h b/include/linux/pci.h index aaa9f333fb44..700704ef70f3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1013,7 +1013,9 @@ enum pci_fixup_pass { pci_fixup_header, /* After reading configuration header */ pci_fixup_final, /* Final phase of device fixups */ pci_fixup_enable, /* pci_enable_device() time */ - pci_fixup_resume, /* pci_enable_device() time */ + pci_fixup_resume, /* pci_device_resume() */ + pci_fixup_suspend, /* pci_device_suspend */ + pci_fixup_resume_early, /* pci_device_resume_early() */ }; /* Anonymous variables would be nice... */ @@ -1035,6 +1037,12 @@ enum pci_fixup_pass { #define DECLARE_PCI_FIXUP_RESUME(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume, \ resume##vendor##device##hook, vendor, device, hook) +#define DECLARE_PCI_FIXUP_RESUME_EARLY(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early, \ + resume_early##vendor##device##hook, vendor, device, hook) +#define DECLARE_PCI_FIXUP_SUSPEND(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ + suspend##vendor##device##hook, vendor, device, hook) void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); -- cgit v1.2.3 From 1eede070a59e1cc73da51e1aaa00d9ab86572cfc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 May 2008 23:00:01 +0200 Subject: Introduce new top level suspend and hibernation callbacks Introduce 'struct pm_ops' and 'struct pm_ext_ops' ('ext' meaning 'extended') representing suspend and hibernation operations for bus types, device classes, device types and device drivers. Modify the PM core to use 'struct pm_ops' and 'struct pm_ext_ops' objects, if defined, instead of the ->suspend(), ->resume(), ->suspend_late(), and ->resume_early() callbacks (the old callbacks will be considered as legacy and gradually phased out). The main purpose of doing this is to separate suspend (aka S2RAM and standby) callbacks from hibernation callbacks in such a way that the new callbacks won't take arguments and the semantics of each of them will be clearly specified. This has been requested for multiple times by many people, including Linus himself, and the reason is that within the current scheme if ->resume() is called, for example, it's difficult to say why it's been called (ie. is it a resume from RAM or from hibernation or a suspend/hibernation failure etc.?). The second purpose is to make the suspend/hibernation callbacks more flexible so that device drivers can handle more than they can within the current scheme. For example, some drivers may need to prevent new children of the device from being registered before their ->suspend() callbacks are executed or they may want to carry out some operations requiring the availability of some other devices, not directly bound via the parent-child relationship, in order to prepare for the execution of ->suspend(), etc. Ultimately, we'd like to stop using the freezing of tasks for suspend and therefore the drivers' suspend/hibernation code will have to take care of the handling of the user space during suspend/hibernation. That, in turn, would be difficult within the current scheme, without the new ->prepare() and ->complete() callbacks. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- arch/x86/kernel/apm_32.c | 8 +- drivers/base/power/main.c | 675 +++++++++++++++++++++++++++++++++++---------- drivers/base/power/power.h | 2 +- drivers/base/power/trace.c | 4 +- include/linux/device.h | 9 + include/linux/pm.h | 314 +++++++++++++++++++-- kernel/power/disk.c | 22 +- kernel/power/main.c | 6 +- 8 files changed, 845 insertions(+), 195 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index bf9290e29013..c1735f61a2c0 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -1211,9 +1211,9 @@ static int suspend(int vetoable) if (err != APM_SUCCESS) apm_error("suspend", err); err = (err == APM_SUCCESS) ? 0 : -EIO; - device_power_up(); + device_power_up(PMSG_RESUME); local_irq_enable(); - device_resume(); + device_resume(PMSG_RESUME); queue_event(APM_NORMAL_RESUME, NULL); spin_lock(&user_list_lock); for (as = user_list; as != NULL; as = as->next) { @@ -1238,7 +1238,7 @@ static void standby(void) apm_error("standby", err); local_irq_disable(); - device_power_up(); + device_power_up(PMSG_RESUME); local_irq_enable(); } @@ -1324,7 +1324,7 @@ static void check_events(void) ignore_bounce = 1; if ((event != APM_NORMAL_RESUME) || (ignore_normal_resume == 0)) { - device_resume(); + device_resume(PMSG_RESUME); queue_event(event, NULL); } ignore_normal_resume = 0; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 45cc3d9eacb8..d571204aaff7 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -12,11 +12,9 @@ * and add it to the list of power-controlled devices. sysfs entries for * controlling device power management will also be added. * - * A different set of lists than the global subsystem list are used to - * keep track of power info because we use different lists to hold - * devices based on what stage of the power management process they - * are in. The power domain dependencies may also differ from the - * ancestral dependencies that the subsystem list maintains. + * A separate list is used for keeping track of power info, because the power + * domain dependencies may differ from the ancestral dependencies that the + * subsystem list maintains. */ #include @@ -30,31 +28,40 @@ #include "power.h" /* - * The entries in the dpm_active list are in a depth first order, simply + * The entries in the dpm_list list are in a depth first order, simply * because children are guaranteed to be discovered after parents, and * are inserted at the back of the list on discovery. * - * All the other lists are kept in the same order, for consistency. - * However the lists aren't always traversed in the same order. - * Semaphores must be acquired from the top (i.e., front) down - * and released in the opposite order. Devices must be suspended - * from the bottom (i.e., end) up and resumed in the opposite order. - * That way no parent will be suspended while it still has an active - * child. - * * Since device_pm_add() may be called with a device semaphore held, * we must never try to acquire a device semaphore while holding * dpm_list_mutex. */ -LIST_HEAD(dpm_active); -static LIST_HEAD(dpm_off); -static LIST_HEAD(dpm_off_irq); +LIST_HEAD(dpm_list); static DEFINE_MUTEX(dpm_list_mtx); -/* 'true' if all devices have been suspended, protected by dpm_list_mtx */ -static bool all_sleeping; +/* + * Set once the preparation of devices for a PM transition has started, reset + * before starting to resume devices. Protected by dpm_list_mtx. + */ +static bool transition_started; + +/** + * device_pm_lock - lock the list of active devices used by the PM core + */ +void device_pm_lock(void) +{ + mutex_lock(&dpm_list_mtx); +} + +/** + * device_pm_unlock - unlock the list of active devices used by the PM core + */ +void device_pm_unlock(void) +{ + mutex_unlock(&dpm_list_mtx); +} /** * device_pm_add - add a device to the list of active devices @@ -68,17 +75,25 @@ int device_pm_add(struct device *dev) dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); - if ((dev->parent && dev->parent->power.sleeping) || all_sleeping) { - if (dev->parent->power.sleeping) - dev_warn(dev, "parent %s is sleeping\n", + if (dev->parent) { + if (dev->parent->power.status >= DPM_SUSPENDING) { + dev_warn(dev, "parent %s is sleeping, will not add\n", dev->parent->bus_id); - else - dev_warn(dev, "all devices are sleeping\n"); + WARN_ON(true); + } + } else if (transition_started) { + /* + * We refuse to register parentless devices while a PM + * transition is in progress in order to avoid leaving them + * unhandled down the road + */ WARN_ON(true); } error = dpm_sysfs_add(dev); - if (!error) - list_add_tail(&dev->power.entry, &dpm_active); + if (!error) { + dev->power.status = DPM_ON; + list_add_tail(&dev->power.entry, &dpm_list); + } mutex_unlock(&dpm_list_mtx); return error; } @@ -100,73 +115,243 @@ void device_pm_remove(struct device *dev) mutex_unlock(&dpm_list_mtx); } +/** + * pm_op - execute the PM operation appropiate for given PM event + * @dev: Device. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. + */ +static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state) +{ + int error = 0; + + switch (state.event) { +#ifdef CONFIG_SUSPEND + case PM_EVENT_SUSPEND: + if (ops->suspend) { + error = ops->suspend(dev); + suspend_report_result(ops->suspend, error); + } + break; + case PM_EVENT_RESUME: + if (ops->resume) { + error = ops->resume(dev); + suspend_report_result(ops->resume, error); + } + break; +#endif /* CONFIG_SUSPEND */ +#ifdef CONFIG_HIBERNATION + case PM_EVENT_FREEZE: + case PM_EVENT_QUIESCE: + if (ops->freeze) { + error = ops->freeze(dev); + suspend_report_result(ops->freeze, error); + } + break; + case PM_EVENT_HIBERNATE: + if (ops->poweroff) { + error = ops->poweroff(dev); + suspend_report_result(ops->poweroff, error); + } + break; + case PM_EVENT_THAW: + case PM_EVENT_RECOVER: + if (ops->thaw) { + error = ops->thaw(dev); + suspend_report_result(ops->thaw, error); + } + break; + case PM_EVENT_RESTORE: + if (ops->restore) { + error = ops->restore(dev); + suspend_report_result(ops->restore, error); + } + break; +#endif /* CONFIG_HIBERNATION */ + default: + error = -EINVAL; + } + return error; +} + +/** + * pm_noirq_op - execute the PM operation appropiate for given PM event + * @dev: Device. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. + * + * The operation is executed with interrupts disabled by the only remaining + * functional CPU in the system. + */ +static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops, + pm_message_t state) +{ + int error = 0; + + switch (state.event) { +#ifdef CONFIG_SUSPEND + case PM_EVENT_SUSPEND: + if (ops->suspend_noirq) { + error = ops->suspend_noirq(dev); + suspend_report_result(ops->suspend_noirq, error); + } + break; + case PM_EVENT_RESUME: + if (ops->resume_noirq) { + error = ops->resume_noirq(dev); + suspend_report_result(ops->resume_noirq, error); + } + break; +#endif /* CONFIG_SUSPEND */ +#ifdef CONFIG_HIBERNATION + case PM_EVENT_FREEZE: + case PM_EVENT_QUIESCE: + if (ops->freeze_noirq) { + error = ops->freeze_noirq(dev); + suspend_report_result(ops->freeze_noirq, error); + } + break; + case PM_EVENT_HIBERNATE: + if (ops->poweroff_noirq) { + error = ops->poweroff_noirq(dev); + suspend_report_result(ops->poweroff_noirq, error); + } + break; + case PM_EVENT_THAW: + case PM_EVENT_RECOVER: + if (ops->thaw_noirq) { + error = ops->thaw_noirq(dev); + suspend_report_result(ops->thaw_noirq, error); + } + break; + case PM_EVENT_RESTORE: + if (ops->restore_noirq) { + error = ops->restore_noirq(dev); + suspend_report_result(ops->restore_noirq, error); + } + break; +#endif /* CONFIG_HIBERNATION */ + default: + error = -EINVAL; + } + return error; +} + +static char *pm_verb(int event) +{ + switch (event) { + case PM_EVENT_SUSPEND: + return "suspend"; + case PM_EVENT_RESUME: + return "resume"; + case PM_EVENT_FREEZE: + return "freeze"; + case PM_EVENT_QUIESCE: + return "quiesce"; + case PM_EVENT_HIBERNATE: + return "hibernate"; + case PM_EVENT_THAW: + return "thaw"; + case PM_EVENT_RESTORE: + return "restore"; + case PM_EVENT_RECOVER: + return "recover"; + default: + return "(unknown PM event)"; + } +} + +static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) +{ + dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), + ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? + ", may wakeup" : ""); +} + +static void pm_dev_err(struct device *dev, pm_message_t state, char *info, + int error) +{ + printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", + kobject_name(&dev->kobj), pm_verb(state.event), info, error); +} + /*------------------------- Resume routines -------------------------*/ /** - * resume_device_early - Power on one device (early resume). + * resume_device_noirq - Power on one device (early resume). * @dev: Device. + * @state: PM transition of the system being carried out. * * Must be called with interrupts disabled. */ -static int resume_device_early(struct device *dev) +static int resume_device_noirq(struct device *dev, pm_message_t state) { int error = 0; TRACE_DEVICE(dev); TRACE_RESUME(0); - if (dev->bus && dev->bus->resume_early) { - dev_dbg(dev, "EARLY resume\n"); + if (!dev->bus) + goto End; + + if (dev->bus->pm) { + pm_dev_dbg(dev, state, "EARLY "); + error = pm_noirq_op(dev, dev->bus->pm, state); + } else if (dev->bus->resume_early) { + pm_dev_dbg(dev, state, "legacy EARLY "); error = dev->bus->resume_early(dev); } - + End: TRACE_RESUME(error); return error; } /** * dpm_power_up - Power on all regular (non-sysdev) devices. + * @state: PM transition of the system being carried out. * - * Walk the dpm_off_irq list and power each device up. This - * is used for devices that required they be powered down with - * interrupts disabled. As devices are powered on, they are moved - * to the dpm_off list. + * Execute the appropriate "noirq resume" callback for all devices marked + * as DPM_OFF_IRQ. * * Must be called with interrupts disabled and only one CPU running. */ -static void dpm_power_up(void) +static void dpm_power_up(pm_message_t state) { + struct device *dev; - while (!list_empty(&dpm_off_irq)) { - struct list_head *entry = dpm_off_irq.next; - struct device *dev = to_device(entry); + list_for_each_entry(dev, &dpm_list, power.entry) + if (dev->power.status > DPM_OFF) { + int error; - list_move_tail(entry, &dpm_off); - resume_device_early(dev); - } + dev->power.status = DPM_OFF; + error = resume_device_noirq(dev, state); + if (error) + pm_dev_err(dev, state, " early", error); + } } /** * device_power_up - Turn on all devices that need special attention. + * @state: PM transition of the system being carried out. * * Power on system devices, then devices that required we shut them down * with interrupts disabled. * * Must be called with interrupts disabled. */ -void device_power_up(void) +void device_power_up(pm_message_t state) { sysdev_resume(); - dpm_power_up(); + dpm_power_up(state); } EXPORT_SYMBOL_GPL(device_power_up); /** * resume_device - Restore state for one device. * @dev: Device. - * + * @state: PM transition of the system being carried out. */ -static int resume_device(struct device *dev) +static int resume_device(struct device *dev, pm_message_t state) { int error = 0; @@ -175,21 +360,40 @@ static int resume_device(struct device *dev) down(&dev->sem); - if (dev->bus && dev->bus->resume) { - dev_dbg(dev,"resuming\n"); - error = dev->bus->resume(dev); + if (dev->bus) { + if (dev->bus->pm) { + pm_dev_dbg(dev, state, ""); + error = pm_op(dev, &dev->bus->pm->base, state); + } else if (dev->bus->resume) { + pm_dev_dbg(dev, state, "legacy "); + error = dev->bus->resume(dev); + } + if (error) + goto End; } - if (!error && dev->type && dev->type->resume) { - dev_dbg(dev,"resuming\n"); - error = dev->type->resume(dev); + if (dev->type) { + if (dev->type->pm) { + pm_dev_dbg(dev, state, "type "); + error = pm_op(dev, dev->type->pm, state); + } else if (dev->type->resume) { + pm_dev_dbg(dev, state, "legacy type "); + error = dev->type->resume(dev); + } + if (error) + goto End; } - if (!error && dev->class && dev->class->resume) { - dev_dbg(dev,"class resume\n"); - error = dev->class->resume(dev); + if (dev->class) { + if (dev->class->pm) { + pm_dev_dbg(dev, state, "class "); + error = pm_op(dev, dev->class->pm, state); + } else if (dev->class->resume) { + pm_dev_dbg(dev, state, "legacy class "); + error = dev->class->resume(dev); + } } - + End: up(&dev->sem); TRACE_RESUME(error); @@ -198,78 +402,161 @@ static int resume_device(struct device *dev) /** * dpm_resume - Resume every device. + * @state: PM transition of the system being carried out. * - * Resume the devices that have either not gone through - * the late suspend, or that did go through it but also - * went through the early resume. + * Execute the appropriate "resume" callback for all devices the status of + * which indicates that they are inactive. + */ +static void dpm_resume(pm_message_t state) +{ + struct list_head list; + + INIT_LIST_HEAD(&list); + mutex_lock(&dpm_list_mtx); + transition_started = false; + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.next); + + get_device(dev); + if (dev->power.status >= DPM_OFF) { + int error; + + dev->power.status = DPM_RESUMING; + mutex_unlock(&dpm_list_mtx); + + error = resume_device(dev, state); + + mutex_lock(&dpm_list_mtx); + if (error) + pm_dev_err(dev, state, "", error); + } else if (dev->power.status == DPM_SUSPENDING) { + /* Allow new children of the device to be registered */ + dev->power.status = DPM_RESUMING; + } + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &list); + put_device(dev); + } + list_splice(&list, &dpm_list); + mutex_unlock(&dpm_list_mtx); +} + +/** + * complete_device - Complete a PM transition for given device + * @dev: Device. + * @state: PM transition of the system being carried out. + */ +static void complete_device(struct device *dev, pm_message_t state) +{ + down(&dev->sem); + + if (dev->class && dev->class->pm && dev->class->pm->complete) { + pm_dev_dbg(dev, state, "completing class "); + dev->class->pm->complete(dev); + } + + if (dev->type && dev->type->pm && dev->type->pm->complete) { + pm_dev_dbg(dev, state, "completing type "); + dev->type->pm->complete(dev); + } + + if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) { + pm_dev_dbg(dev, state, "completing "); + dev->bus->pm->base.complete(dev); + } + + up(&dev->sem); +} + +/** + * dpm_complete - Complete a PM transition for all devices. + * @state: PM transition of the system being carried out. * - * Take devices from the dpm_off_list, resume them, - * and put them on the dpm_locked list. + * Execute the ->complete() callbacks for all devices that are not marked + * as DPM_ON. */ -static void dpm_resume(void) +static void dpm_complete(pm_message_t state) { + struct list_head list; + + INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - all_sleeping = false; - while(!list_empty(&dpm_off)) { - struct list_head *entry = dpm_off.next; - struct device *dev = to_device(entry); + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.prev); - list_move_tail(entry, &dpm_active); - dev->power.sleeping = false; - mutex_unlock(&dpm_list_mtx); - resume_device(dev); - mutex_lock(&dpm_list_mtx); + get_device(dev); + if (dev->power.status > DPM_ON) { + dev->power.status = DPM_ON; + mutex_unlock(&dpm_list_mtx); + + complete_device(dev, state); + + mutex_lock(&dpm_list_mtx); + } + if (!list_empty(&dev->power.entry)) + list_move(&dev->power.entry, &list); + put_device(dev); } + list_splice(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); } /** * device_resume - Restore state of each device in system. + * @state: PM transition of the system being carried out. * * Resume all the devices, unlock them all, and allow new * devices to be registered once again. */ -void device_resume(void) +void device_resume(pm_message_t state) { might_sleep(); - dpm_resume(); + dpm_resume(state); + dpm_complete(state); } EXPORT_SYMBOL_GPL(device_resume); /*------------------------- Suspend routines -------------------------*/ -static inline char *suspend_verb(u32 event) +/** + * resume_event - return a PM message representing the resume event + * corresponding to given sleep state. + * @sleep_state: PM message representing a sleep state. + */ +static pm_message_t resume_event(pm_message_t sleep_state) { - switch (event) { - case PM_EVENT_SUSPEND: return "suspend"; - case PM_EVENT_FREEZE: return "freeze"; - case PM_EVENT_PRETHAW: return "prethaw"; - default: return "(unknown suspend event)"; + switch (sleep_state.event) { + case PM_EVENT_SUSPEND: + return PMSG_RESUME; + case PM_EVENT_FREEZE: + case PM_EVENT_QUIESCE: + return PMSG_RECOVER; + case PM_EVENT_HIBERNATE: + return PMSG_RESTORE; } -} - -static void -suspend_device_dbg(struct device *dev, pm_message_t state, char *info) -{ - dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ? - ", may wakeup" : ""); + return PMSG_ON; } /** - * suspend_device_late - Shut down one device (late suspend). + * suspend_device_noirq - Shut down one device (late suspend). * @dev: Device. - * @state: Power state device is entering. + * @state: PM transition of the system being carried out. * * This is called with interrupts off and only a single CPU running. */ -static int suspend_device_late(struct device *dev, pm_message_t state) +static int suspend_device_noirq(struct device *dev, pm_message_t state) { int error = 0; - if (dev->bus && dev->bus->suspend_late) { - suspend_device_dbg(dev, state, "LATE "); + if (!dev->bus) + return 0; + + if (dev->bus->pm) { + pm_dev_dbg(dev, state, "LATE "); + error = pm_noirq_op(dev, dev->bus->pm, state); + } else if (dev->bus->suspend_late) { + pm_dev_dbg(dev, state, "legacy LATE "); error = dev->bus->suspend_late(dev, state); suspend_report_result(dev->bus->suspend_late, error); } @@ -278,37 +565,30 @@ static int suspend_device_late(struct device *dev, pm_message_t state) /** * device_power_down - Shut down special devices. - * @state: Power state to enter. + * @state: PM transition of the system being carried out. * - * Power down devices that require interrupts to be disabled - * and move them from the dpm_off list to the dpm_off_irq list. + * Power down devices that require interrupts to be disabled. * Then power down system devices. * * Must be called with interrupts disabled and only one CPU running. */ int device_power_down(pm_message_t state) { + struct device *dev; int error = 0; - while (!list_empty(&dpm_off)) { - struct list_head *entry = dpm_off.prev; - struct device *dev = to_device(entry); - - error = suspend_device_late(dev, state); + list_for_each_entry_reverse(dev, &dpm_list, power.entry) { + error = suspend_device_noirq(dev, state); if (error) { - printk(KERN_ERR "Could not power down device %s: " - "error %d\n", - kobject_name(&dev->kobj), error); + pm_dev_err(dev, state, " late", error); break; } - if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &dpm_off_irq); + dev->power.status = DPM_OFF_IRQ; } - if (!error) error = sysdev_suspend(state); if (error) - dpm_power_up(); + dpm_power_up(resume_event(state)); return error; } EXPORT_SYMBOL_GPL(device_power_down); @@ -316,7 +596,7 @@ EXPORT_SYMBOL_GPL(device_power_down); /** * suspend_device - Save state of one device. * @dev: Device. - * @state: Power state device is entering. + * @state: PM transition of the system being carried out. */ static int suspend_device(struct device *dev, pm_message_t state) { @@ -324,24 +604,43 @@ static int suspend_device(struct device *dev, pm_message_t state) down(&dev->sem); - if (dev->class && dev->class->suspend) { - suspend_device_dbg(dev, state, "class "); - error = dev->class->suspend(dev, state); - suspend_report_result(dev->class->suspend, error); + if (dev->class) { + if (dev->class->pm) { + pm_dev_dbg(dev, state, "class "); + error = pm_op(dev, dev->class->pm, state); + } else if (dev->class->suspend) { + pm_dev_dbg(dev, state, "legacy class "); + error = dev->class->suspend(dev, state); + suspend_report_result(dev->class->suspend, error); + } + if (error) + goto End; } - if (!error && dev->type && dev->type->suspend) { - suspend_device_dbg(dev, state, "type "); - error = dev->type->suspend(dev, state); - suspend_report_result(dev->type->suspend, error); + if (dev->type) { + if (dev->type->pm) { + pm_dev_dbg(dev, state, "type "); + error = pm_op(dev, dev->type->pm, state); + } else if (dev->type->suspend) { + pm_dev_dbg(dev, state, "legacy type "); + error = dev->type->suspend(dev, state); + suspend_report_result(dev->type->suspend, error); + } + if (error) + goto End; } - if (!error && dev->bus && dev->bus->suspend) { - suspend_device_dbg(dev, state, ""); - error = dev->bus->suspend(dev, state); - suspend_report_result(dev->bus->suspend, error); + if (dev->bus) { + if (dev->bus->pm) { + pm_dev_dbg(dev, state, ""); + error = pm_op(dev, &dev->bus->pm->base, state); + } else if (dev->bus->suspend) { + pm_dev_dbg(dev, state, "legacy "); + error = dev->bus->suspend(dev, state); + suspend_report_result(dev->bus->suspend, error); + } } - + End: up(&dev->sem); return error; @@ -349,67 +648,141 @@ static int suspend_device(struct device *dev, pm_message_t state) /** * dpm_suspend - Suspend every device. - * @state: Power state to put each device in. + * @state: PM transition of the system being carried out. * - * Walk the dpm_locked list. Suspend each device and move it - * to the dpm_off list. - * - * (For historical reasons, if it returns -EAGAIN, that used to mean - * that the device would be called again with interrupts disabled. - * These days, we use the "suspend_late()" callback for that, so we - * print a warning and consider it an error). + * Execute the appropriate "suspend" callbacks for all devices. */ static int dpm_suspend(pm_message_t state) { + struct list_head list; int error = 0; + INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - while (!list_empty(&dpm_active)) { - struct list_head *entry = dpm_active.prev; - struct device *dev = to_device(entry); - - WARN_ON(dev->parent && dev->parent->power.sleeping); + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.prev); - dev->power.sleeping = true; + get_device(dev); mutex_unlock(&dpm_list_mtx); + error = suspend_device(dev, state); + mutex_lock(&dpm_list_mtx); if (error) { - printk(KERN_ERR "Could not suspend device %s: " - "error %d%s\n", - kobject_name(&dev->kobj), - error, - (error == -EAGAIN ? - " (please convert to suspend_late)" : - "")); - dev->power.sleeping = false; + pm_dev_err(dev, state, "", error); + put_device(dev); break; } + dev->power.status = DPM_OFF; if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &dpm_off); + list_move(&dev->power.entry, &list); + put_device(dev); } - if (!error) - all_sleeping = true; + list_splice(&list, dpm_list.prev); mutex_unlock(&dpm_list_mtx); + return error; +} + +/** + * prepare_device - Execute the ->prepare() callback(s) for given device. + * @dev: Device. + * @state: PM transition of the system being carried out. + */ +static int prepare_device(struct device *dev, pm_message_t state) +{ + int error = 0; + + down(&dev->sem); + + if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) { + pm_dev_dbg(dev, state, "preparing "); + error = dev->bus->pm->base.prepare(dev); + suspend_report_result(dev->bus->pm->base.prepare, error); + if (error) + goto End; + } + + if (dev->type && dev->type->pm && dev->type->pm->prepare) { + pm_dev_dbg(dev, state, "preparing type "); + error = dev->type->pm->prepare(dev); + suspend_report_result(dev->type->pm->prepare, error); + if (error) + goto End; + } + + if (dev->class && dev->class->pm && dev->class->pm->prepare) { + pm_dev_dbg(dev, state, "preparing class "); + error = dev->class->pm->prepare(dev); + suspend_report_result(dev->class->pm->prepare, error); + } + End: + up(&dev->sem); + + return error; +} +/** + * dpm_prepare - Prepare all devices for a PM transition. + * @state: PM transition of the system being carried out. + * + * Execute the ->prepare() callback for all devices. + */ +static int dpm_prepare(pm_message_t state) +{ + struct list_head list; + int error = 0; + + INIT_LIST_HEAD(&list); + mutex_lock(&dpm_list_mtx); + transition_started = true; + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.next); + + get_device(dev); + dev->power.status = DPM_PREPARING; + mutex_unlock(&dpm_list_mtx); + + error = prepare_device(dev, state); + + mutex_lock(&dpm_list_mtx); + if (error) { + dev->power.status = DPM_ON; + if (error == -EAGAIN) { + put_device(dev); + continue; + } + printk(KERN_ERR "PM: Failed to prepare device %s " + "for power transition: error %d\n", + kobject_name(&dev->kobj), error); + put_device(dev); + break; + } + dev->power.status = DPM_SUSPENDING; + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &list); + put_device(dev); + } + list_splice(&list, &dpm_list); + mutex_unlock(&dpm_list_mtx); return error; } /** * device_suspend - Save state and stop all devices in system. - * @state: new power management state + * @state: PM transition of the system being carried out. * - * Prevent new devices from being registered, then lock all devices - * and suspend them. + * Prepare and suspend all devices. */ int device_suspend(pm_message_t state) { int error; might_sleep(); - error = dpm_suspend(state); + error = dpm_prepare(state); + if (!error) + error = dpm_suspend(state); if (error) - device_resume(); + device_resume(resume_event(state)); return error; } EXPORT_SYMBOL_GPL(device_suspend); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index a6894f2a4b99..a3252c0e2887 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -4,7 +4,7 @@ * main.c */ -extern struct list_head dpm_active; /* The active device list */ +extern struct list_head dpm_list; /* The active device list */ static inline struct device *to_device(struct list_head *entry) { diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 2b4b392dcbc1..8c1e656b5f8b 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -188,9 +188,9 @@ static int show_file_hash(unsigned int value) static int show_dev_hash(unsigned int value) { int match = 0; - struct list_head * entry = dpm_active.prev; + struct list_head *entry = dpm_list.prev; - while (entry != &dpm_active) { + while (entry != &dpm_list) { struct device * dev = to_device(entry); unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH); if (hash == value) { diff --git a/include/linux/device.h b/include/linux/device.h index 6a2d04c011bc..f71a78d123ae 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -68,6 +68,8 @@ struct bus_type { int (*resume_early)(struct device *dev); int (*resume)(struct device *dev); + struct pm_ext_ops *pm; + struct bus_type_private *p; }; @@ -131,6 +133,8 @@ struct device_driver { int (*resume) (struct device *dev); struct attribute_group **groups; + struct pm_ops *pm; + struct driver_private *p; }; @@ -197,6 +201,8 @@ struct class { int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + + struct pm_ops *pm; }; extern int __must_check class_register(struct class *class); @@ -248,8 +254,11 @@ struct device_type { struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); void (*release)(struct device *dev); + int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + + struct pm_ops *pm; }; /* interface for exporting device attributes */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 39a7ee859b67..4ad9de94449a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -112,7 +112,9 @@ typedef struct pm_message { int event; } pm_message_t; -/* +/** + * struct pm_ops - device PM callbacks + * * Several driver power state transitions are externally visible, affecting * the state of pending I/O queues and (for drivers that touch hardware) * interrupts, wakeups, DMA, and other hardware state. There may also be @@ -120,6 +122,284 @@ typedef struct pm_message { * to the rest of the driver stack (such as a driver that's ON gating off * clocks which are not in active use). * + * The externally visible transitions are handled with the help of the following + * callbacks included in this structure: + * + * @prepare: Prepare the device for the upcoming transition, but do NOT change + * its hardware state. Prevent new children of the device from being + * registered after @prepare() returns (the driver's subsystem and + * generally the rest of the kernel is supposed to prevent new calls to the + * probe method from being made too once @prepare() has succeeded). If + * @prepare() detects a situation it cannot handle (e.g. registration of a + * child already in progress), it may return -EAGAIN, so that the PM core + * can execute it once again (e.g. after the new child has been registered) + * to recover from the race condition. This method is executed for all + * kinds of suspend transitions and is followed by one of the suspend + * callbacks: @suspend(), @freeze(), or @poweroff(). + * The PM core executes @prepare() for all devices before starting to + * execute suspend callbacks for any of them, so drivers may assume all of + * the other devices to be present and functional while @prepare() is being + * executed. In particular, it is safe to make GFP_KERNEL memory + * allocations from within @prepare(). However, drivers may NOT assume + * anything about the availability of the user space at that time and it + * is not correct to request firmware from within @prepare() (it's too + * late to do that). [To work around this limitation, drivers may + * register suspend and hibernation notifiers that are executed before the + * freezing of tasks.] + * + * @complete: Undo the changes made by @prepare(). This method is executed for + * all kinds of resume transitions, following one of the resume callbacks: + * @resume(), @thaw(), @restore(). Also called if the state transition + * fails before the driver's suspend callback (@suspend(), @freeze(), + * @poweroff()) can be executed (e.g. if the suspend callback fails for one + * of the other devices that the PM core has unsuccessfully attempted to + * suspend earlier). + * The PM core executes @complete() after it has executed the appropriate + * resume callback for all devices. + * + * @suspend: Executed before putting the system into a sleep state in which the + * contents of main memory are preserved. Quiesce the device, put it into + * a low power state appropriate for the upcoming system state (such as + * PCI_D3hot), and enable wakeup events as appropriate. + * + * @resume: Executed after waking the system up from a sleep state in which the + * contents of main memory were preserved. Put the device into the + * appropriate state, according to the information saved in memory by the + * preceding @suspend(). The driver starts working again, responding to + * hardware events and software requests. The hardware may have gone + * through a power-off reset, or it may have maintained state from the + * previous suspend() which the driver may rely on while resuming. On most + * platforms, there are no restrictions on availability of resources like + * clocks during @resume(). + * + * @freeze: Hibernation-specific, executed before creating a hibernation image. + * Quiesce operations so that a consistent image can be created, but do NOT + * otherwise put the device into a low power device state and do NOT emit + * system wakeup events. Save in main memory the device settings to be + * used by @restore() during the subsequent resume from hibernation or by + * the subsequent @thaw(), if the creation of the image or the restoration + * of main memory contents from it fails. + * + * @thaw: Hibernation-specific, executed after creating a hibernation image OR + * if the creation of the image fails. Also executed after a failing + * attempt to restore the contents of main memory from such an image. + * Undo the changes made by the preceding @freeze(), so the device can be + * operated in the same way as immediately before the call to @freeze(). + * + * @poweroff: Hibernation-specific, executed after saving a hibernation image. + * Quiesce the device, put it into a low power state appropriate for the + * upcoming system state (such as PCI_D3hot), and enable wakeup events as + * appropriate. + * + * @restore: Hibernation-specific, executed after restoring the contents of main + * memory from a hibernation image. Driver starts working again, + * responding to hardware events and software requests. Drivers may NOT + * make ANY assumptions about the hardware state right prior to @restore(). + * On most platforms, there are no restrictions on availability of + * resources like clocks during @restore(). + * + * All of the above callbacks, except for @complete(), return error codes. + * However, the error codes returned by the resume operations, @resume(), + * @thaw(), and @restore(), do not cause the PM core to abort the resume + * transition during which they are returned. The error codes returned in + * that cases are only printed by the PM core to the system logs for debugging + * purposes. Still, it is recommended that drivers only return error codes + * from their resume methods in case of an unrecoverable failure (i.e. when the + * device being handled refuses to resume and becomes unusable) to allow us to + * modify the PM core in the future, so that it can avoid attempting to handle + * devices that failed to resume and their children. + * + * It is allowed to unregister devices while the above callbacks are being + * executed. However, it is not allowed to unregister a device from within any + * of its own callbacks. + */ + +struct pm_ops { + int (*prepare)(struct device *dev); + void (*complete)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + int (*freeze)(struct device *dev); + int (*thaw)(struct device *dev); + int (*poweroff)(struct device *dev); + int (*restore)(struct device *dev); +}; + +/** + * struct pm_ext_ops - extended device PM callbacks + * + * Some devices require certain operations related to suspend and hibernation + * to be carried out with interrupts disabled. Thus, 'struct pm_ext_ops' below + * is defined, adding callbacks to be executed with interrupts disabled to + * 'struct pm_ops'. + * + * The following callbacks included in 'struct pm_ext_ops' are executed with + * the nonboot CPUs switched off and with interrupts disabled on the only + * functional CPU. They also are executed with the PM core list of devices + * locked, so they must NOT unregister any devices. + * + * @suspend_noirq: Complete the operations of ->suspend() by carrying out any + * actions required for suspending the device that need interrupts to be + * disabled + * + * @resume_noirq: Prepare for the execution of ->resume() by carrying out any + * actions required for resuming the device that need interrupts to be + * disabled + * + * @freeze_noirq: Complete the operations of ->freeze() by carrying out any + * actions required for freezing the device that need interrupts to be + * disabled + * + * @thaw_noirq: Prepare for the execution of ->thaw() by carrying out any + * actions required for thawing the device that need interrupts to be + * disabled + * + * @poweroff_noirq: Complete the operations of ->poweroff() by carrying out any + * actions required for handling the device that need interrupts to be + * disabled + * + * @restore_noirq: Prepare for the execution of ->restore() by carrying out any + * actions required for restoring the operations of the device that need + * interrupts to be disabled + * + * All of the above callbacks return error codes, but the error codes returned + * by the resume operations, @resume_noirq(), @thaw_noirq(), and + * @restore_noirq(), do not cause the PM core to abort the resume transition + * during which they are returned. The error codes returned in that cases are + * only printed by the PM core to the system logs for debugging purposes. + * Still, as stated above, it is recommended that drivers only return error + * codes from their resume methods if the device being handled fails to resume + * and is not usable any more. + */ + +struct pm_ext_ops { + struct pm_ops base; + int (*suspend_noirq)(struct device *dev); + int (*resume_noirq)(struct device *dev); + int (*freeze_noirq)(struct device *dev); + int (*thaw_noirq)(struct device *dev); + int (*poweroff_noirq)(struct device *dev); + int (*restore_noirq)(struct device *dev); +}; + +/** + * PM_EVENT_ messages + * + * The following PM_EVENT_ messages are defined for the internal use of the PM + * core, in order to provide a mechanism allowing the high level suspend and + * hibernation code to convey the necessary information to the device PM core + * code: + * + * ON No transition. + * + * FREEZE System is going to hibernate, call ->prepare() and ->freeze() + * for all devices. + * + * SUSPEND System is going to suspend, call ->prepare() and ->suspend() + * for all devices. + * + * HIBERNATE Hibernation image has been saved, call ->prepare() and + * ->poweroff() for all devices. + * + * QUIESCE Contents of main memory are going to be restored from a (loaded) + * hibernation image, call ->prepare() and ->freeze() for all + * devices. + * + * RESUME System is resuming, call ->resume() and ->complete() for all + * devices. + * + * THAW Hibernation image has been created, call ->thaw() and + * ->complete() for all devices. + * + * RESTORE Contents of main memory have been restored from a hibernation + * image, call ->restore() and ->complete() for all devices. + * + * RECOVER Creation of a hibernation image or restoration of the main + * memory contents from a hibernation image has failed, call + * ->thaw() and ->complete() for all devices. + */ + +#define PM_EVENT_ON 0x0000 +#define PM_EVENT_FREEZE 0x0001 +#define PM_EVENT_SUSPEND 0x0002 +#define PM_EVENT_HIBERNATE 0x0004 +#define PM_EVENT_QUIESCE 0x0008 +#define PM_EVENT_RESUME 0x0010 +#define PM_EVENT_THAW 0x0020 +#define PM_EVENT_RESTORE 0x0040 +#define PM_EVENT_RECOVER 0x0080 + +#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) + +#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) +#define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) +#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) +#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, }) +#define PMSG_RESUME ((struct pm_message){ .event = PM_EVENT_RESUME, }) +#define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) +#define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) +#define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) +#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) + +/** + * Device power management states + * + * These state labels are used internally by the PM core to indicate the current + * status of a device with respect to the PM core operations. + * + * DPM_ON Device is regarded as operational. Set this way + * initially and when ->complete() is about to be called. + * Also set when ->prepare() fails. + * + * DPM_PREPARING Device is going to be prepared for a PM transition. Set + * when ->prepare() is about to be called. + * + * DPM_RESUMING Device is going to be resumed. Set when ->resume(), + * ->thaw(), or ->restore() is about to be called. + * + * DPM_SUSPENDING Device has been prepared for a power transition. Set + * when ->prepare() has just succeeded. + * + * DPM_OFF Device is regarded as inactive. Set immediately after + * ->suspend(), ->freeze(), or ->poweroff() has succeeded. + * Also set when ->resume()_noirq, ->thaw_noirq(), or + * ->restore_noirq() is about to be called. + * + * DPM_OFF_IRQ Device is in a "deep sleep". Set immediately after + * ->suspend_noirq(), ->freeze_noirq(), or + * ->poweroff_noirq() has just succeeded. + */ + +enum dpm_state { + DPM_INVALID, + DPM_ON, + DPM_PREPARING, + DPM_RESUMING, + DPM_SUSPENDING, + DPM_OFF, + DPM_OFF_IRQ, +}; + +struct dev_pm_info { + pm_message_t power_state; + unsigned can_wakeup:1; + unsigned should_wakeup:1; + enum dpm_state status; /* Owned by the PM core */ +#ifdef CONFIG_PM_SLEEP + struct list_head entry; +#endif +}; + +/* + * The PM_EVENT_ messages are also used by drivers implementing the legacy + * suspend framework, based on the ->suspend() and ->resume() callbacks common + * for suspend and hibernation transitions, according to the rules below. + */ + +/* Necessary, because several drivers use PM_EVENT_PRETHAW */ +#define PM_EVENT_PRETHAW PM_EVENT_QUIESCE + +/* * One transition is triggered by resume(), after a suspend() call; the * message is implicit: * @@ -164,35 +444,13 @@ typedef struct pm_message { * or from system low-power states such as standby or suspend-to-RAM. */ -#define PM_EVENT_ON 0 -#define PM_EVENT_FREEZE 1 -#define PM_EVENT_SUSPEND 2 -#define PM_EVENT_HIBERNATE 4 -#define PM_EVENT_PRETHAW 8 - -#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) - -#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) -#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, }) -#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) -#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, }) -#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) - -struct dev_pm_info { - pm_message_t power_state; - unsigned can_wakeup:1; - unsigned should_wakeup:1; - bool sleeping:1; /* Owned by the PM core */ -#ifdef CONFIG_PM_SLEEP - struct list_head entry; -#endif -}; +#ifdef CONFIG_PM_SLEEP +extern void device_pm_lock(void); +extern void device_power_up(pm_message_t state); +extern void device_resume(pm_message_t state); +extern void device_pm_unlock(void); extern int device_power_down(pm_message_t state); -extern void device_power_up(void); -extern void device_resume(void); - -#ifdef CONFIG_PM_SLEEP extern int device_suspend(pm_message_t state); extern int device_prepare_suspend(pm_message_t state); diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 14a656cdc652..d416be0efa8a 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -193,6 +193,7 @@ static int create_image(int platform_mode) if (error) return error; + device_pm_lock(); local_irq_disable(); /* At this point, device_suspend() has been called, but *not* * device_power_down(). We *must* call device_power_down() now. @@ -224,9 +225,11 @@ static int create_image(int platform_mode) /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ - device_power_up(); + device_power_up(in_suspend ? + (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); Enable_irqs: local_irq_enable(); + device_pm_unlock(); return error; } @@ -280,7 +283,8 @@ int hibernation_snapshot(int platform_mode) Finish: platform_finish(platform_mode); Resume_devices: - device_resume(); + device_resume(in_suspend ? + (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); Resume_console: resume_console(); Close: @@ -300,8 +304,9 @@ static int resume_target_kernel(void) { int error; + device_pm_lock(); local_irq_disable(); - error = device_power_down(PMSG_PRETHAW); + error = device_power_down(PMSG_QUIESCE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting resume\n"); @@ -329,9 +334,10 @@ static int resume_target_kernel(void) swsusp_free(); restore_processor_state(); touch_softlockup_watchdog(); - device_power_up(); + device_power_up(PMSG_RECOVER); Enable_irqs: local_irq_enable(); + device_pm_unlock(); return error; } @@ -350,7 +356,7 @@ int hibernation_restore(int platform_mode) pm_prepare_console(); suspend_console(); - error = device_suspend(PMSG_PRETHAW); + error = device_suspend(PMSG_QUIESCE); if (error) goto Finish; @@ -362,7 +368,7 @@ int hibernation_restore(int platform_mode) enable_nonboot_cpus(); } platform_restore_cleanup(platform_mode); - device_resume(); + device_resume(PMSG_RECOVER); Finish: resume_console(); pm_restore_console(); @@ -403,6 +409,7 @@ int hibernation_platform_enter(void) if (error) goto Finish; + device_pm_lock(); local_irq_disable(); error = device_power_down(PMSG_HIBERNATE); if (!error) { @@ -411,6 +418,7 @@ int hibernation_platform_enter(void) while (1); } local_irq_enable(); + device_pm_unlock(); /* * We don't need to reenable the nonboot CPUs or resume consoles, since @@ -419,7 +427,7 @@ int hibernation_platform_enter(void) Finish: hibernation_ops->finish(); Resume_devices: - device_resume(); + device_resume(PMSG_RESTORE); Resume_console: resume_console(); Close: diff --git a/kernel/power/main.c b/kernel/power/main.c index 6a6d5eb3524e..d023b6b584e5 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -228,6 +228,7 @@ static int suspend_enter(suspend_state_t state) { int error = 0; + device_pm_lock(); arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled()); @@ -239,10 +240,11 @@ static int suspend_enter(suspend_state_t state) if (!suspend_test(TEST_CORE)) error = suspend_ops->enter(state); - device_power_up(); + device_power_up(PMSG_RESUME); Done: arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); + device_pm_unlock(); return error; } @@ -291,7 +293,7 @@ int suspend_devices_and_enter(suspend_state_t state) if (suspend_ops->finish) suspend_ops->finish(); Resume_devices: - device_resume(); + device_resume(PMSG_RESUME); Resume_console: resume_console(); Close: -- cgit v1.2.3 From bbb44d9f23d868a2837c6b22b8dfb123d8e7800c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 May 2008 00:49:04 +0200 Subject: PCI: implement new suspend/resume callbacks Implement new suspend and hibernation callbacks for the PCI bus type. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 392 +++++++++++++++++++++++++++++++++++++++++------ include/linux/pci.h | 2 +- 2 files changed, 347 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 677fd9d6db12..8eb8a3091dc7 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -274,7 +274,57 @@ static int pci_device_remove(struct device * dev) return 0; } -static int pci_device_suspend(struct device * dev, pm_message_t state) +static void pci_device_shutdown(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + + if (drv && drv->shutdown) + drv->shutdown(pci_dev); + pci_msi_shutdown(pci_dev); + pci_msix_shutdown(pci_dev); +} + +#ifdef CONFIG_PM_SLEEP + +/* + * Default "suspend" method for devices that have no driver provided suspend, + * or not even a driver at all. + */ +static void pci_default_pm_suspend(struct pci_dev *pci_dev) +{ + pci_save_state(pci_dev); + /* + * mark its power state as "unknown", since we don't know if + * e.g. the BIOS will change its device state when we suspend. + */ + if (pci_dev->current_state == PCI_D0) + pci_dev->current_state = PCI_UNKNOWN; +} + +/* + * Default "resume" method for devices that have no driver provided resume, + * or not even a driver at all. + */ +static int pci_default_pm_resume(struct pci_dev *pci_dev) +{ + int retval = 0; + + /* restore the PCI config space */ + pci_restore_state(pci_dev); + /* if the device was enabled before suspend, reenable */ + retval = pci_reenable_device(pci_dev); + /* + * if the device was busmaster before the suspend, make it busmaster + * again + */ + if (pci_dev->is_busmaster) + pci_set_master(pci_dev); + + return retval; +} + +static int pci_legacy_suspend(struct device *dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; @@ -284,21 +334,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); } else { - pci_save_state(pci_dev); - /* - * mark its power state as "unknown", since we don't know if - * e.g. the BIOS will change its device state when we suspend. - */ - if (pci_dev->current_state == PCI_D0) - pci_dev->current_state = PCI_UNKNOWN; + pci_default_pm_suspend(pci_dev); } - - pci_fixup_device(pci_fixup_suspend, pci_dev); - return i; } -static int pci_device_suspend_late(struct device * dev, pm_message_t state) +static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; @@ -311,26 +352,7 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state) return i; } -/* - * Default resume method for devices that have no driver provided resume, - * or not even a driver at all. - */ -static int pci_default_resume(struct pci_dev *pci_dev) -{ - int retval = 0; - - /* restore the PCI config space */ - pci_restore_state(pci_dev); - /* if the device was enabled before suspend, reenable */ - retval = pci_reenable_device(pci_dev); - /* if the device was busmaster before the suspend, make it busmaster again */ - if (pci_dev->is_busmaster) - pci_set_master(pci_dev); - - return retval; -} - -static int pci_device_resume(struct device * dev) +static int pci_legacy_resume(struct device *dev) { int error; struct pci_dev * pci_dev = to_pci_dev(dev); @@ -339,35 +361,313 @@ static int pci_device_resume(struct device * dev) if (drv && drv->resume) error = drv->resume(pci_dev); else - error = pci_default_resume(pci_dev); - pci_fixup_device(pci_fixup_resume, pci_dev); + error = pci_default_pm_resume(pci_dev); return error; } -static int pci_device_resume_early(struct device * dev) +static int pci_legacy_resume_early(struct device *dev) { int error = 0; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume_early, pci_dev); - if (drv && drv->resume_early) error = drv->resume_early(pci_dev); return error; } -static void pci_device_shutdown(struct device *dev) +static int pci_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm && drv->pm->prepare) + error = drv->pm->prepare(dev); + + return error; +} + +static void pci_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +static int pci_pm_suspend(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->suspend) { + error = drv->pm->suspend(dev); + suspend_report_result(drv->pm->suspend, error); + } else { + pci_default_pm_suspend(pci_dev); + } + } else { + error = pci_legacy_suspend(dev, PMSG_SUSPEND); + } + pci_fixup_device(pci_fixup_suspend, pci_dev); + + return error; +} + +static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + int error = 0; - if (drv && drv->shutdown) - drv->shutdown(pci_dev); - pci_msi_shutdown(pci_dev); - pci_msix_shutdown(pci_dev); + if (drv && drv->pm) { + if (drv->pm->suspend_noirq) { + error = drv->pm->suspend_noirq(dev); + suspend_report_result(drv->pm->suspend_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); + } + + return error; +} + +static int pci_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error; + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (drv && drv->pm) { + error = drv->pm->resume ? drv->pm->resume(dev) : + pci_default_pm_resume(pci_dev); + } else { + error = pci_legacy_resume(dev); + } + + return error; +} + +static int pci_pm_resume_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + if (drv && drv->pm) { + if (drv->pm->resume_noirq) + error = drv->pm->resume_noirq(dev); + } else { + error = pci_legacy_resume_early(dev); + } + + return error; +} + +#else /* !CONFIG_SUSPEND */ + +#define pci_pm_suspend NULL +#define pci_pm_suspend_noirq NULL +#define pci_pm_resume NULL +#define pci_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int pci_pm_freeze(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->freeze) { + error = drv->pm->freeze(dev); + suspend_report_result(drv->pm->freeze, error); + } else { + pci_default_pm_suspend(pci_dev); + } + } else { + error = pci_legacy_suspend(dev, PMSG_FREEZE); + pci_fixup_device(pci_fixup_suspend, pci_dev); + } + + return error; +} + +static int pci_pm_freeze_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->freeze_noirq) { + error = drv->pm->freeze_noirq(dev); + suspend_report_result(drv->pm->freeze_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_FREEZE); + } + + return error; +} + +static int pci_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw) + error = drv->pm->thaw(dev); + } else { + pci_fixup_device(pci_fixup_resume, to_pci_dev(dev)); + error = pci_legacy_resume(dev); + } + + return error; +} + +static int pci_pm_thaw_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw_noirq) + error = drv->pm->thaw_noirq(dev); + } else { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + error = pci_legacy_resume_early(dev); + } + + return error; +} + +static int pci_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); + + if (drv && drv->pm) { + if (drv->pm->poweroff) { + error = drv->pm->poweroff(dev); + suspend_report_result(drv->pm->poweroff, error); + } + } else { + error = pci_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return error; +} + +static int pci_pm_poweroff_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->poweroff_noirq) { + error = drv->pm->poweroff_noirq(dev); + suspend_report_result(drv->pm->poweroff_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE); + } + + return error; +} + +static int pci_pm_restore(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error; + + if (drv && drv->pm) { + error = drv->pm->restore ? drv->pm->restore(dev) : + pci_default_pm_resume(pci_dev); + } else { + error = pci_legacy_resume(dev); + } + pci_fixup_device(pci_fixup_resume, pci_dev); + + return error; +} + +static int pci_pm_restore_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (drv && drv->pm) { + if (drv->pm->restore_noirq) + error = drv->pm->restore_noirq(dev); + } else { + error = pci_legacy_resume_early(dev); + } + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + return error; } +#else /* !CONFIG_HIBERNATION */ + +#define pci_pm_freeze NULL +#define pci_pm_freeze_noirq NULL +#define pci_pm_thaw NULL +#define pci_pm_thaw_noirq NULL +#define pci_pm_poweroff NULL +#define pci_pm_poweroff_noirq NULL +#define pci_pm_restore NULL +#define pci_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +struct pm_ext_ops pci_pm_ops = { + .base = { + .prepare = pci_pm_prepare, + .complete = pci_pm_complete, + .suspend = pci_pm_suspend, + .resume = pci_pm_resume, + .freeze = pci_pm_freeze, + .thaw = pci_pm_thaw, + .poweroff = pci_pm_poweroff, + .restore = pci_pm_restore, + }, + .suspend_noirq = pci_pm_suspend_noirq, + .resume_noirq = pci_pm_resume_noirq, + .freeze_noirq = pci_pm_freeze_noirq, + .thaw_noirq = pci_pm_thaw_noirq, + .poweroff_noirq = pci_pm_poweroff_noirq, + .restore_noirq = pci_pm_restore_noirq, +}; + +#define PCI_PM_OPS_PTR &pci_pm_ops + +#else /* !CONFIG_PM_SLEEP */ + +#define PCI_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + /** * __pci_register_driver - register a new pci driver * @drv: the driver structure to register @@ -390,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.owner = owner; drv->driver.mod_name = mod_name; + if (drv->pm) + drv->driver.pm = &drv->pm->base; + spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -515,12 +818,9 @@ struct bus_type pci_bus_type = { .uevent = pci_uevent, .probe = pci_device_probe, .remove = pci_device_remove, - .suspend = pci_device_suspend, - .suspend_late = pci_device_suspend_late, - .resume_early = pci_device_resume_early, - .resume = pci_device_resume, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, + .pm = PCI_PM_OPS_PTR, }; static int __init pci_driver_init(void) diff --git a/include/linux/pci.h b/include/linux/pci.h index 700704ef70f3..507ee52323cd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -389,7 +389,7 @@ struct pci_driver { int (*resume_early) (struct pci_dev *dev); int (*resume) (struct pci_dev *dev); /* Device woken up */ void (*shutdown) (struct pci_dev *dev); - + struct pm_ext_ops *pm; struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; -- cgit v1.2.3 From 25e18499e08cb097cbbfeab5de25d094d5312ee5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 May 2008 01:40:43 +0200 Subject: Implement new suspend and hibernation callbacks for platform busses Implement new suspend and hibernation callbacks for the platform bus type. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Greg KH Signed-off-by: Jesse Barnes --- drivers/base/platform.c | 296 ++++++++++++++++++++++++++++++++++++++-- include/linux/platform_device.h | 1 + 2 files changed, 289 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 911ec600fe71..3f940393d6c7 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -453,6 +453,8 @@ int platform_driver_register(struct platform_driver *drv) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; + if (drv->pm) + drv->driver.pm = &drv->pm->base; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register); @@ -560,7 +562,9 @@ static int platform_match(struct device *dev, struct device_driver *drv) return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); } -static int platform_suspend(struct device *dev, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP + +static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) { int ret = 0; @@ -570,7 +574,7 @@ static int platform_suspend(struct device *dev, pm_message_t mesg) return ret; } -static int platform_suspend_late(struct device *dev, pm_message_t mesg) +static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg) { struct platform_driver *drv = to_platform_driver(dev->driver); struct platform_device *pdev; @@ -583,7 +587,7 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg) return ret; } -static int platform_resume_early(struct device *dev) +static int platform_legacy_resume_early(struct device *dev) { struct platform_driver *drv = to_platform_driver(dev->driver); struct platform_device *pdev; @@ -596,7 +600,7 @@ static int platform_resume_early(struct device *dev) return ret; } -static int platform_resume(struct device *dev) +static int platform_legacy_resume(struct device *dev) { int ret = 0; @@ -606,15 +610,291 @@ static int platform_resume(struct device *dev) return ret; } +static int platform_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm && drv->pm->prepare) + ret = drv->pm->prepare(dev); + + return ret; +} + +static void platform_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +static int platform_pm_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->suspend) + ret = drv->pm->suspend(dev); + } else { + ret = platform_legacy_suspend(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int platform_pm_suspend_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->suspend_noirq) + ret = pdrv->pm->suspend_noirq(dev); + } else { + ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int platform_pm_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->resume) + ret = drv->pm->resume(dev); + } else { + ret = platform_legacy_resume(dev); + } + + return ret; +} + +static int platform_pm_resume_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->resume_noirq) + ret = pdrv->pm->resume_noirq(dev); + } else { + ret = platform_legacy_resume_early(dev); + } + + return ret; +} + +#else /* !CONFIG_SUSPEND */ + +#define platform_pm_suspend NULL +#define platform_pm_resume NULL +#define platform_pm_suspend_noirq NULL +#define platform_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int platform_pm_freeze(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze) + ret = drv->pm->freeze(dev); + } else { + ret = platform_legacy_suspend(dev, PMSG_FREEZE); + } + + return ret; +} + +static int platform_pm_freeze_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->freeze_noirq) + ret = pdrv->pm->freeze_noirq(dev); + } else { + ret = platform_legacy_suspend_late(dev, PMSG_FREEZE); + } + + return ret; +} + +static int platform_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw) + ret = drv->pm->thaw(dev); + } else { + ret = platform_legacy_resume(dev); + } + + return ret; +} + +static int platform_pm_thaw_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->thaw_noirq) + ret = pdrv->pm->thaw_noirq(dev); + } else { + ret = platform_legacy_resume_early(dev); + } + + return ret; +} + +static int platform_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->poweroff) + ret = drv->pm->poweroff(dev); + } else { + ret = platform_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int platform_pm_poweroff_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->poweroff_noirq) + ret = pdrv->pm->poweroff_noirq(dev); + } else { + ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int platform_pm_restore(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->restore) + ret = drv->pm->restore(dev); + } else { + ret = platform_legacy_resume(dev); + } + + return ret; +} + +static int platform_pm_restore_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->restore_noirq) + ret = pdrv->pm->restore_noirq(dev); + } else { + ret = platform_legacy_resume_early(dev); + } + + return ret; +} + +#else /* !CONFIG_HIBERNATION */ + +#define platform_pm_freeze NULL +#define platform_pm_thaw NULL +#define platform_pm_poweroff NULL +#define platform_pm_restore NULL +#define platform_pm_freeze_noirq NULL +#define platform_pm_thaw_noirq NULL +#define platform_pm_poweroff_noirq NULL +#define platform_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +struct pm_ext_ops platform_pm_ops = { + .base = { + .prepare = platform_pm_prepare, + .complete = platform_pm_complete, + .suspend = platform_pm_suspend, + .resume = platform_pm_resume, + .freeze = platform_pm_freeze, + .thaw = platform_pm_thaw, + .poweroff = platform_pm_poweroff, + .restore = platform_pm_restore, + }, + .suspend_noirq = platform_pm_suspend_noirq, + .resume_noirq = platform_pm_resume_noirq, + .freeze_noirq = platform_pm_freeze_noirq, + .thaw_noirq = platform_pm_thaw_noirq, + .poweroff_noirq = platform_pm_poweroff_noirq, + .restore_noirq = platform_pm_restore_noirq, +}; + +#define PLATFORM_PM_OPS_PTR &platform_pm_ops + +#else /* !CONFIG_PM_SLEEP */ + +#define PLATFORM_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, - .suspend = platform_suspend, - .suspend_late = platform_suspend_late, - .resume_early = platform_resume_early, - .resume = platform_resume, + .pm = PLATFORM_PM_OPS_PTR, }; EXPORT_SYMBOL_GPL(platform_bus_type); diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 3261681c82a4..95ac21ab3a09 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -53,6 +53,7 @@ struct platform_driver { int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); + struct pm_ext_ops *pm; struct device_driver driver; }; -- cgit v1.2.3 From ac9c052d10d8d6f46a30cb46c0d6d753997c299f Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 28 May 2008 15:01:03 +0900 Subject: shpchp: check firmware before taking control Fix the following problems of shpchp driver about getting hotplug control from firmware. - The shpchp driver must not control the hotplug controller if it fails to get control from the firmware. But current shpchp controls the hotplug controller regardless the result, because it doesn't check the return value of get_hp_hw_control_from_firmware(). - Current shpchp driver doesn't support _OSC. The pciehp driver already have the code for evaluating _OSC and OSHP and shpchp and pciehp can share it. So this patch move that code from pciehp to acpi_pcihp.c. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 85 +++++++++++++++++++++++++++++++++++++-- drivers/pci/hotplug/pciehp.h | 10 +++-- drivers/pci/hotplug/pciehp_hpc.c | 69 ------------------------------- drivers/pci/hotplug/shpchp.h | 14 ++++--- drivers/pci/hotplug/shpchp_core.c | 15 +++---- drivers/pci/hotplug/shpchp_hpc.c | 1 - include/linux/pci_hotplug.h | 2 +- 7 files changed, 105 insertions(+), 91 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index f8c187a763bd..93e37f0666ab 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -299,7 +300,7 @@ free_and_return: * * @handle - the handle of the hotplug controller. */ -acpi_status acpi_run_oshp(acpi_handle handle) +static acpi_status acpi_run_oshp(acpi_handle handle) { acpi_status status; struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle) kfree(string.pointer); return status; } -EXPORT_SYMBOL_GPL(acpi_run_oshp); - - /* acpi_get_hp_params_from_firmware * @@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, } EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); +/** + * acpi_get_hp_hw_control_from_firmware + * @dev: the pci_dev of the bridge that has a hotplug controller + * @flags: requested control bits for _OSC + * + * Attempt to take hotplug control from firmware. + */ +int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) +{ + acpi_status status; + acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); + struct pci_dev *pdev = dev; + struct pci_bus *parent; + struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; + + flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | + OSC_SHPC_NATIVE_HP_CONTROL | + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); + if (!flags) { + err("Invalid flags %u specified!\n", flags); + return -EINVAL; + } + + /* + * Per PCI firmware specification, we should run the ACPI _OSC + * method to get control of hotplug hardware before using it. If + * an _OSC is missing, we look for an OSHP to do the same thing. + * To handle different BIOS behavior, we look for _OSC and OSHP + * within the scope of the hotplug controller and its parents, + * upto the host bridge under which this controller exists. + */ + while (!handle) { + /* + * This hotplug controller was not listed in the ACPI name + * space at all. Try to get acpi handle of parent pci bus. + */ + if (!pdev || !pdev->bus->parent) + break; + parent = pdev->bus->parent; + dbg("Could not find %s in acpi namespace, trying parent\n", + pci_name(pdev)); + if (!parent->self) + /* Parent must be a host bridge */ + handle = acpi_get_pci_rootbridge_handle( + pci_domain_nr(parent), + parent->number); + else + handle = DEVICE_ACPI_HANDLE(&(parent->self->dev)); + pdev = parent->self; + } + + while (handle) { + acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); + dbg("Trying to get hotplug control for %s \n", + (char *)string.pointer); + status = pci_osc_control_set(handle, flags); + if (status == AE_NOT_FOUND) + status = acpi_run_oshp(handle); + if (ACPI_SUCCESS(status)) { + dbg("Gained control for hotplug HW for pci %s (%s)\n", + pci_name(dev), (char *)string.pointer); + kfree(string.pointer); + return 0; + } + if (acpi_root_bridge(handle)) + break; + chandle = handle; + status = acpi_get_parent(chandle, &handle); + if (ACPI_FAILURE(status)) + break; + } + + dbg("Cannot get control of hotplug hardware for pci %s\n", + pci_name(dev)); + + kfree(string.pointer); + return -ENODEV; +} +EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); /* acpi_root_bridge - check to see if this acpi object is a root bridge * diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 084b73efacb3..8492fab800cc 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -202,9 +202,13 @@ struct hpc_ops { #include #include -extern int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev); -#define pciehp_get_hp_hw_control_from_firmware(dev) \ - pciehp_acpi_get_hp_hw_control_from_firmware(dev) +static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) +{ + u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); + return acpi_get_hp_hw_control_from_firmware(dev, flags); +} + static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7e3a3d17c334..a48021d85f22 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -1009,75 +1009,6 @@ static struct hpc_ops pciehp_hpc_ops = { .check_lnk_status = hpc_check_lnk_status, }; -#ifdef CONFIG_ACPI -int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) -{ - acpi_status status; - acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); - struct pci_dev *pdev = dev; - struct pci_bus *parent; - struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; - - /* - * Per PCI firmware specification, we should run the ACPI _OSC - * method to get control of hotplug hardware before using it. - * If an _OSC is missing, we look for an OSHP to do the same thing. - * To handle different BIOS behavior, we look for _OSC and OSHP - * within the scope of the hotplug controller and its parents, upto - * the host bridge under which this controller exists. - */ - while (!handle) { - /* - * This hotplug controller was not listed in the ACPI name - * space at all. Try to get acpi handle of parent pci bus. - */ - if (!pdev || !pdev->bus->parent) - break; - parent = pdev->bus->parent; - dbg("Could not find %s in acpi namespace, trying parent\n", - pci_name(pdev)); - if (!parent->self) - /* Parent must be a host bridge */ - handle = acpi_get_pci_rootbridge_handle( - pci_domain_nr(parent), - parent->number); - else - handle = DEVICE_ACPI_HANDLE( - &(parent->self->dev)); - pdev = parent->self; - } - - while (handle) { - acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); - dbg("Trying to get hotplug control for %s \n", - (char *)string.pointer); - status = pci_osc_control_set(handle, - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL | - OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); - if (status == AE_NOT_FOUND) - status = acpi_run_oshp(handle); - if (ACPI_SUCCESS(status)) { - dbg("Gained control for hotplug HW for pci %s (%s)\n", - pci_name(dev), (char *)string.pointer); - kfree(string.pointer); - return 0; - } - if (acpi_root_bridge(handle)) - break; - chandle = handle; - status = acpi_get_parent(chandle, &handle); - if (ACPI_FAILURE(status)) - break; - } - - dbg("Cannot get control of hotplug hardware for pci %s\n", - pci_name(dev)); - - kfree(string.pointer); - return -1; -} -#endif - static int pcie_init_hardware_part1(struct controller *ctrl, struct pcie_device *dev) { diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index f66e8d6315ab..8a026f750deb 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -170,6 +170,7 @@ extern void shpchp_queue_pushbutton_work(struct work_struct *work); extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev); #ifdef CONFIG_ACPI +#include static inline int get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { @@ -177,14 +178,15 @@ static inline int get_hp_params_from_firmware(struct pci_dev *dev, return -ENODEV; return 0; } -#define get_hp_hw_control_from_firmware(pdev) \ - do { \ - if (DEVICE_ACPI_HANDLE(&(pdev->dev))) \ - acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\ - } while (0) + +static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) +{ + u32 flags = OSC_SHPC_NATIVE_HP_CONTROL; + return acpi_get_hp_hw_control_from_firmware(dev, flags); +} #else #define get_hp_params_from_firmware(dev, hpp) (-ENODEV) -#define get_hp_hw_control_from_firmware(dev) do { } while (0) +#define get_hp_hw_control_from_firmware(dev) (0) #endif struct ctrl_reg { diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 0066b0be0928..df41ecc4e7fc 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -330,13 +330,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp static int is_shpc_capable(struct pci_dev *dev) { - if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == - PCI_DEVICE_ID_AMD_GOLAM_7450)) - return 1; - if (pci_find_capability(dev, PCI_CAP_ID_SHPC)) - return 1; - - return 0; + if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == + PCI_DEVICE_ID_AMD_GOLAM_7450)) + return 1; + if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) + return 0; + if (get_hp_hw_control_from_firmware(dev)) + return 0; + return 1; } static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 7d770b2cd889..7a0bff364cd4 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -1084,7 +1084,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev) dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq); - get_hp_hw_control_from_firmware(pdev); /* * If this is the first controller to be initialized, diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 8f67e8f2a3cc..dbdcd1ad3c6a 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -227,9 +227,9 @@ struct hotplug_params { #include #include #include -extern acpi_status acpi_run_oshp(acpi_handle handle); extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, struct hotplug_params *hpp); +int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags); int acpi_root_bridge(acpi_handle handle); #endif #endif -- cgit v1.2.3 From f46753c5e354b857b20ab8e0fe7b2579831dc369 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Tue, 10 Jun 2008 15:28:50 -0600 Subject: PCI: introduce pci_slot Currently, /sys/bus/pci/slots/ only exposes hotplug attributes when a hotplug driver is loaded, but PCI slots have attributes such as address, speed, width, etc. that are not related to hotplug at all. Introduce pci_slot as the primary data structure and kobject model. Hotplug attributes described in hotplug_slot become a secondary structure associated with the pci_slot. This patch only creates the infrastructure that allows the separation of PCI slot attributes and hotplug attributes. In this patch, the PCI hotplug core remains the only user of this infrastructure, and thus, /sys/bus/pci/slots/ will still only become populated when a hotplug driver is loaded. A later patch in this series will add a second user of this new infrastructure and demonstrate splitting the task of exposing pci_slot attributes from hotplug_slot attributes. - Make pci_slot the primary sysfs entity. hotplug_slot becomes a subsidiary structure. o pci_create_slot() creates and registers a slot with the PCI core o pci_slot_add_hotplug() gives it hotplug capability - Change the prototype of pci_hp_register() to take the bus and slot number (on parent bus) as parameters. - Remove all the ->get_address methods since this functionality is now handled by pci_slot directly. [achiang@hp.com: rpaphp-correctly-pci_hp_register-for-empty-pci-slots] Tested-by: Badari Pulavarty Acked-by: Benjamin Herrenschmidt [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: make headers_check happy] [akpm@linux-foundation.org: nuther build fix] [akpm@linux-foundation.org: fix typo in #include] Signed-off-by: Alex Chiang Signed-off-by: Matthew Wilcox Cc: Greg KH Cc: Kristen Carlson Accardi Cc: Len Brown Acked-by: Kenji Kaneshige Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/Makefile | 2 +- drivers/pci/hotplug/acpiphp.h | 1 - drivers/pci/hotplug/acpiphp_core.c | 25 +--- drivers/pci/hotplug/acpiphp_glue.c | 23 +-- drivers/pci/hotplug/acpiphp_ibm.c | 6 +- drivers/pci/hotplug/cpci_hotplug_core.c | 2 +- drivers/pci/hotplug/cpqphp_core.c | 4 +- drivers/pci/hotplug/fakephp.c | 2 +- drivers/pci/hotplug/ibmphp_ebda.c | 3 +- drivers/pci/hotplug/pci_hotplug_core.c | 258 ++++++++++++-------------------- drivers/pci/hotplug/pciehp_core.c | 32 ++-- drivers/pci/hotplug/rpadlpar_sysfs.c | 5 +- drivers/pci/hotplug/rpaphp_slot.c | 44 +----- drivers/pci/hotplug/sgi_hotplug.c | 10 +- drivers/pci/hotplug/shpchp_core.c | 20 +-- drivers/pci/pci.h | 13 ++ drivers/pci/probe.c | 1 + drivers/pci/slot.c | 233 ++++++++++++++++++++++++++++ include/linux/pci.h | 19 ++- include/linux/pci_hotplug.h | 12 +- 20 files changed, 422 insertions(+), 293 deletions(-) create mode 100644 drivers/pci/slot.c (limited to 'include/linux') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4d1ce2e7361e..7d63f8ced24b 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -2,7 +2,7 @@ # Makefile for the PCI bus specific drivers. # -obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \ +obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 7a29164d4b32..eecf7cbf4139 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -215,7 +215,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); -extern u32 acpiphp_get_address (struct acpiphp_slot *slot); /* variables */ extern int acpiphp_debug; diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 7af68ba27903..0e496e866a84 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -70,7 +70,6 @@ static int disable_slot (struct hotplug_slot *slot); static int set_attention_status (struct hotplug_slot *slot, u8 value); static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); -static int get_address (struct hotplug_slot *slot, u32 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); @@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_address = get_address, }; @@ -274,23 +272,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } - -/** - * get_address - get pci address of a slot - * @hotplug_slot: slot to get status - * @value: pointer to struct pci_busdev (seg, bus, dev) - */ -static int get_address(struct hotplug_slot *hotplug_slot, u32 *value) -{ - struct slot *slot = hotplug_slot->private; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); - - *value = acpiphp_get_address(slot->acpi_slot); - - return 0; -} - static int __init init_acpi(void) { int retval; @@ -357,7 +338,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot) acpiphp_slot->slot = slot; snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun); - retval = pci_hp_register(slot->hotplug_slot); + retval = pci_hp_register(slot->hotplug_slot, + acpiphp_slot->bridge->pci_bus, + acpiphp_slot->device); + if (retval == -EBUSY) + goto error_hpslot; if (retval) { err("pci_hp_register failed with error %d\n", retval); goto error_hpslot; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 648596d469f6..9342c848db29 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -258,7 +258,12 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) bridge->pci_bus->number, slot->device); retval = acpiphp_register_hotplug_slot(slot); if (retval) { - warn("acpiphp_register_hotplug_slot failed(err code = 0x%x)\n", retval); + if (retval == -EBUSY) + warn("Slot %d already registered by another " + "hotplug driver\n", slot->sun); + else + warn("acpiphp_register_hotplug_slot failed " + "(err code = 0x%x)\n", retval); goto err_exit; } } @@ -1867,19 +1872,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) return (sta == 0) ? 0 : 1; } - - -/* - * pci address (seg/bus/dev) - */ -u32 acpiphp_get_address(struct acpiphp_slot *slot) -{ - u32 address; - struct pci_bus *pci_bus = slot->bridge->pci_bus; - - address = (pci_domain_nr(pci_bus) << 16) | - (pci_bus->number << 8) | - slot->device; - - return address; -} diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index ede9051fdb5d..2b7c45e39370 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -33,8 +33,10 @@ #include #include #include +#include #include "acpiphp.h" +#include "../pci.h" #define DRIVER_VERSION "1.0.1" #define DRIVER_AUTHOR "Irene Zubarev , Vernon Mauery " @@ -430,7 +432,7 @@ static int __init ibm_acpiphp_init(void) int retval = 0; acpi_status status; struct acpi_device *device; - struct kobject *sysdir = &pci_hotplug_slots_kset->kobj; + struct kobject *sysdir = &pci_slots_kset->kobj; dbg("%s\n", __func__); @@ -477,7 +479,7 @@ init_return: static void __exit ibm_acpiphp_exit(void) { acpi_status status; - struct kobject *sysdir = &pci_hotplug_slots_kset->kobj; + struct kobject *sysdir = &pci_slots_kset->kobj; dbg("%s\n", __func__); diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index d8a6b80ab42a..935947991dc9 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) info->attention_status = cpci_get_attention_status(slot); dbg("registering slot %s", slot->hotplug_slot->name); - status = pci_hp_register(slot->hotplug_slot); + status = pci_hp_register(slot->hotplug_slot, bus, i); if (status) { err("pci_hp_register failed with error %d", status); goto error_name; diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 36b115b27b0b..54defec51d08 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl, slot->bus, slot->device, slot->number, ctrl->slot_device_offset, slot_number); - result = pci_hp_register(hotplug_slot); + result = pci_hp_register(hotplug_slot, + ctrl->pci_dev->subordinate, + slot->device); if (result) { err("pci_hp_register failed with error %d\n", result); goto error_name; diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index b57223f7e830..40337a06c18a 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -126,7 +126,7 @@ static int add_slot(struct pci_dev *dev) slot->release = &dummy_release; slot->private = dslot; - retval = pci_hp_register(slot); + retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn)); if (retval) { err("pci_hp_register failed with error %d\n", retval); goto error_dslot; diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index dca7efc14be2..8467d0287325 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -1001,7 +1001,8 @@ static int __init ebda_rsrc_controller (void) tmp_slot = list_entry (list, struct slot, ibm_slot_list); snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot)); - pci_hp_register (tmp_slot->hotplug_slot); + pci_hp_register(tmp_slot->hotplug_slot, + pci_find_bus(0, tmp_slot->bus), tmp_slot->device); } print_ebda_hpc (); diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index a11021e8ce37..4df31f375197 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -40,6 +40,7 @@ #include #include #include +#include "../pci.h" #define MY_NAME "pci_hotplug" @@ -60,41 +61,7 @@ static int debug; ////////////////////////////////////////////////////////////////// static LIST_HEAD(pci_hotplug_slot_list); - -struct kset *pci_hotplug_slots_kset; - -static ssize_t hotplug_slot_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct hotplug_slot *slot = to_hotplug_slot(kobj); - struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); - return attribute->show ? attribute->show(slot, buf) : -EIO; -} - -static ssize_t hotplug_slot_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t len) -{ - struct hotplug_slot *slot = to_hotplug_slot(kobj); - struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); - return attribute->store ? attribute->store(slot, buf, len) : -EIO; -} - -static struct sysfs_ops hotplug_slot_sysfs_ops = { - .show = hotplug_slot_attr_show, - .store = hotplug_slot_attr_store, -}; - -static void hotplug_slot_release(struct kobject *kobj) -{ - struct hotplug_slot *slot = to_hotplug_slot(kobj); - if (slot->release) - slot->release(slot); -} - -static struct kobj_type hotplug_slot_ktype = { - .sysfs_ops = &hotplug_slot_sysfs_ops, - .release = &hotplug_slot_release, -}; +static DEFINE_SPINLOCK(pci_hotplug_slot_list_lock); /* these strings match up with the values in pci_bus_speed */ static char *pci_bus_speed_strings[] = { @@ -149,16 +116,15 @@ GET_STATUS(power_status, u8) GET_STATUS(attention_status, u8) GET_STATUS(latch_status, u8) GET_STATUS(adapter_status, u8) -GET_STATUS(address, u32) GET_STATUS(max_bus_speed, enum pci_bus_speed) GET_STATUS(cur_bus_speed, enum pci_bus_speed) -static ssize_t power_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t power_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_power_status (slot, &value); + retval = get_power_status(slot->hotplug, &value); if (retval) goto exit; retval = sprintf (buf, "%d\n", value); @@ -166,9 +132,10 @@ exit: return retval; } -static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf, +static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { + struct hotplug_slot *slot = pci_slot->hotplug; unsigned long lpower; u8 power; int retval = 0; @@ -204,29 +171,30 @@ exit: return count; } -static struct hotplug_slot_attribute hotplug_slot_attr_power = { +static struct pci_slot_attribute hotplug_slot_attr_power = { .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, .show = power_read_file, .store = power_write_file }; -static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t attention_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_attention_status (slot, &value); + retval = get_attention_status(slot->hotplug, &value); if (retval) goto exit; - retval = sprintf (buf, "%d\n", value); + retval = sprintf(buf, "%d\n", value); exit: return retval; } -static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, +static ssize_t attention_write_file(struct pci_slot *slot, const char *buf, size_t count) { + struct hotplug_slot_ops *ops = slot->hotplug->ops; unsigned long lattention; u8 attention; int retval = 0; @@ -235,13 +203,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, attention = (u8)(lattention & 0xff); dbg (" - attention = %d\n", attention); - if (!try_module_get(slot->ops->owner)) { + if (!try_module_get(ops->owner)) { retval = -ENODEV; goto exit; } - if (slot->ops->set_attention_status) - retval = slot->ops->set_attention_status(slot, attention); - module_put(slot->ops->owner); + if (ops->set_attention_status) + retval = ops->set_attention_status(slot->hotplug, attention); + module_put(ops->owner); exit: if (retval) @@ -249,18 +217,18 @@ exit: return count; } -static struct hotplug_slot_attribute hotplug_slot_attr_attention = { +static struct pci_slot_attribute hotplug_slot_attr_attention = { .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, .show = attention_read_file, .store = attention_write_file }; -static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t latch_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_latch_status (slot, &value); + retval = get_latch_status(slot->hotplug, &value); if (retval) goto exit; retval = sprintf (buf, "%d\n", value); @@ -269,17 +237,17 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_latch = { +static struct pci_slot_attribute hotplug_slot_attr_latch = { .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, .show = latch_read_file, }; -static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t presence_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_adapter_status (slot, &value); + retval = get_adapter_status(slot->hotplug, &value); if (retval) goto exit; retval = sprintf (buf, "%d\n", value); @@ -288,42 +256,20 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_presence = { +static struct pci_slot_attribute hotplug_slot_attr_presence = { .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, .show = presence_read_file, }; -static ssize_t address_read_file (struct hotplug_slot *slot, char *buf) -{ - int retval; - u32 address; - - retval = get_address (slot, &address); - if (retval) - goto exit; - retval = sprintf (buf, "%04x:%02x:%02x\n", - (address >> 16) & 0xffff, - (address >> 8) & 0xff, - address & 0xff); - -exit: - return retval; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_address = { - .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, - .show = address_read_file, -}; - static char *unknown_speed = "Unknown bus speed"; -static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf) { char *speed_string; int retval; enum pci_bus_speed value; - retval = get_max_bus_speed (slot, &value); + retval = get_max_bus_speed(slot->hotplug, &value); if (retval) goto exit; @@ -338,18 +284,18 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = { +static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = { .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO}, .show = max_bus_speed_read_file, }; -static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf) { char *speed_string; int retval; enum pci_bus_speed value; - retval = get_cur_bus_speed (slot, &value); + retval = get_cur_bus_speed(slot->hotplug, &value); if (retval) goto exit; @@ -364,14 +310,15 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = { +static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = { .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO}, .show = cur_bus_speed_read_file, }; -static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf, +static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { + struct hotplug_slot *slot = pci_slot->hotplug; unsigned long ltest; u32 test; int retval = 0; @@ -394,13 +341,14 @@ exit: return count; } -static struct hotplug_slot_attribute hotplug_slot_attr_test = { +static struct pci_slot_attribute hotplug_slot_attr_test = { .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, .store = test_write_file }; -static int has_power_file (struct hotplug_slot *slot) +static int has_power_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if ((slot->ops->enable_slot) || @@ -410,8 +358,9 @@ static int has_power_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_attention_file (struct hotplug_slot *slot) +static int has_attention_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if ((slot->ops->set_attention_status) || @@ -420,8 +369,9 @@ static int has_attention_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_latch_file (struct hotplug_slot *slot) +static int has_latch_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_latch_status) @@ -429,8 +379,9 @@ static int has_latch_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_adapter_file (struct hotplug_slot *slot) +static int has_adapter_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_adapter_status) @@ -438,17 +389,9 @@ static int has_adapter_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_address_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->get_address) - return 0; - return -ENOENT; -} - -static int has_max_bus_speed_file (struct hotplug_slot *slot) +static int has_max_bus_speed_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_max_bus_speed) @@ -456,8 +399,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_cur_bus_speed_file (struct hotplug_slot *slot) +static int has_cur_bus_speed_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_cur_bus_speed) @@ -465,8 +409,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_test_file (struct hotplug_slot *slot) +static int has_test_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->hardware_test) @@ -474,7 +419,7 @@ static int has_test_file (struct hotplug_slot *slot) return -ENOENT; } -static int fs_add_slot (struct hotplug_slot *slot) +static int fs_add_slot(struct pci_slot *slot) { int retval = 0; @@ -505,13 +450,6 @@ static int fs_add_slot (struct hotplug_slot *slot) goto exit_adapter; } - if (has_address_file(slot) == 0) { - retval = sysfs_create_file(&slot->kobj, - &hotplug_slot_attr_address.attr); - if (retval) - goto exit_address; - } - if (has_max_bus_speed_file(slot) == 0) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); @@ -544,10 +482,6 @@ exit_cur_speed: sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); exit_max_speed: - if (has_address_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); - -exit_address: if (has_adapter_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); @@ -567,7 +501,7 @@ exit: return retval; } -static void fs_remove_slot (struct hotplug_slot *slot) +static void fs_remove_slot(struct pci_slot *slot) { if (has_power_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); @@ -581,9 +515,6 @@ static void fs_remove_slot (struct hotplug_slot *slot) if (has_adapter_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - if (has_address_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); - if (has_max_bus_speed_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); @@ -599,12 +530,16 @@ static struct hotplug_slot *get_slot_from_name (const char *name) struct hotplug_slot *slot; struct list_head *tmp; + spin_lock(&pci_hotplug_slot_list_lock); list_for_each (tmp, &pci_hotplug_slot_list) { slot = list_entry (tmp, struct hotplug_slot, slot_list); if (strcmp(slot->name, name) == 0) - return slot; + goto out; } - return NULL; + slot = NULL; +out: + spin_unlock(&pci_hotplug_slot_list_lock); + return slot; } /** @@ -616,9 +551,10 @@ static struct hotplug_slot *get_slot_from_name (const char *name) * * Returns 0 if successful, anything else for an error. */ -int pci_hp_register (struct hotplug_slot *slot) +int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr) { int result; + struct pci_slot *pci_slot; struct hotplug_slot *tmp; if (slot == NULL) @@ -636,19 +572,28 @@ int pci_hp_register (struct hotplug_slot *slot) if (tmp) return -EEXIST; - slot->kobj.kset = pci_hotplug_slots_kset; - result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL, - "%s", slot->name); - if (result) { - err("Unable to register kobject '%s'", slot->name); - return -EINVAL; + pci_slot = pci_create_slot(bus, slot_nr, slot->name); + if (IS_ERR(pci_slot)) + return PTR_ERR(pci_slot); + + if (pci_slot->hotplug) { + dbg("%s: already claimed\n", __func__); + pci_destroy_slot(pci_slot); + return -EBUSY; } - list_add (&slot->slot_list, &pci_hotplug_slot_list); + slot->pci_slot = pci_slot; + pci_slot->hotplug = slot; + + spin_lock(&pci_hotplug_slot_list_lock); + list_add(&slot->slot_list, &pci_hotplug_slot_list); + spin_unlock(&pci_hotplug_slot_list_lock); + + result = fs_add_slot(pci_slot); + kobject_uevent(&pci_slot->kobj, KOBJ_ADD); + dbg("Added slot %s to the list\n", slot->name); + - result = fs_add_slot (slot); - kobject_uevent(&slot->kobj, KOBJ_ADD); - dbg ("Added slot %s to the list\n", slot->name); return result; } @@ -661,22 +606,30 @@ int pci_hp_register (struct hotplug_slot *slot) * * Returns 0 if successful, anything else for an error. */ -int pci_hp_deregister (struct hotplug_slot *slot) +int pci_hp_deregister(struct hotplug_slot *hotplug) { struct hotplug_slot *temp; + struct pci_slot *slot; - if (slot == NULL) + if (!hotplug) return -ENODEV; - temp = get_slot_from_name (slot->name); - if (temp != slot) { + temp = get_slot_from_name(hotplug->name); + if (temp != hotplug) return -ENODEV; - } - list_del (&slot->slot_list); - fs_remove_slot (slot); - dbg ("Removed slot %s from the list\n", slot->name); - kobject_put(&slot->kobj); + spin_lock(&pci_hotplug_slot_list_lock); + list_del(&hotplug->slot_list); + spin_unlock(&pci_hotplug_slot_list_lock); + + slot = hotplug->pci_slot; + fs_remove_slot(slot); + dbg("Removed slot %s from the list\n", hotplug->name); + + hotplug->release(hotplug); + slot->hotplug = NULL; + pci_destroy_slot(slot); + return 0; } @@ -690,13 +643,15 @@ int pci_hp_deregister (struct hotplug_slot *slot) * * Returns 0 if successful, anything else for an error. */ -int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, +int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug, struct hotplug_slot_info *info) { - if ((slot == NULL) || (info == NULL)) + struct pci_slot *slot; + if (!hotplug || !info) return -ENODEV; + slot = hotplug->pci_slot; - memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); + memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info)); return 0; } @@ -704,36 +659,22 @@ int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, static int __init pci_hotplug_init (void) { int result; - struct kset *pci_bus_kset; - pci_bus_kset = bus_get_kset(&pci_bus_type); - - pci_hotplug_slots_kset = kset_create_and_add("slots", NULL, - &pci_bus_kset->kobj); - if (!pci_hotplug_slots_kset) { - result = -ENOMEM; - err("Register subsys error\n"); - goto exit; - } result = cpci_hotplug_init(debug); if (result) { err ("cpci_hotplug_init with error %d\n", result); - goto err_subsys; + goto err_cpci; } info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); - goto exit; -err_subsys: - kset_unregister(pci_hotplug_slots_kset); -exit: +err_cpci: return result; } static void __exit pci_hotplug_exit (void) { cpci_hotplug_exit(); - kset_unregister(pci_hotplug_slots_kset); } module_init(pci_hotplug_init); @@ -745,7 +686,6 @@ MODULE_LICENSE("GPL"); module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -EXPORT_SYMBOL_GPL(pci_hotplug_slots_kset); EXPORT_SYMBOL_GPL(pci_hp_register); EXPORT_SYMBOL_GPL(pci_hp_deregister); EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 49414e9100de..7b21c86e4bff 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -72,7 +72,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_address (struct hotplug_slot *slot, u32 *value); static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); @@ -85,7 +84,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_address = get_address, .get_max_bus_speed = get_max_bus_speed, .get_cur_bus_speed = get_cur_bus_speed, }; @@ -252,7 +250,9 @@ static int init_slots(struct controller *ctrl) dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x " "slot_device_offset=%x\n", slot->bus, slot->device, slot->hp_slot, slot->number, ctrl->slot_device_offset); - retval = pci_hp_register(hotplug_slot); + retval = pci_hp_register(hotplug_slot, + ctrl->pci_dev->subordinate, + slot->device); if (retval) { err("pci_hp_register failed with error %d\n", retval); if (retval == -EEXIST) @@ -263,7 +263,7 @@ static int init_slots(struct controller *ctrl) } /* create additional sysfs entries */ if (EMI(ctrl)) { - retval = sysfs_create_file(&hotplug_slot->kobj, + retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj, &hotplug_slot_attr_lock.attr); if (retval) { pci_hp_deregister(hotplug_slot); @@ -296,7 +296,7 @@ static void cleanup_slots(struct controller *ctrl) slot = list_entry(tmp, struct slot, slot_list); list_del(&slot->slot_list); if (EMI(ctrl)) - sysfs_remove_file(&slot->hotplug_slot->kobj, + sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj, &hotplug_slot_attr_lock.attr); cancel_delayed_work(&slot->work); flush_scheduled_work(); @@ -398,19 +398,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_address(struct hotplug_slot *hotplug_slot, u32 *value) -{ - struct slot *slot = hotplug_slot->private; - struct pci_bus *bus = slot->ctrl->pci_dev->subordinate; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); - - *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device; - - return 0; -} - -static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, + enum pci_bus_speed *value) { struct slot *slot = hotplug_slot->private; int retval; @@ -474,7 +463,12 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_ /* Setup the slot information structures */ rc = init_slots(ctrl); if (rc) { - err("%s: slot initialization failed\n", PCIE_MODULE_NAME); + if (rc == -EBUSY) + warn("%s: slot already registered by another " + "hotplug driver\n", PCIE_MODULE_NAME); + else + err("%s: slot initialization failed\n", + PCIE_MODULE_NAME); goto err_out_release_ctlr; } diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c index 779c5db71be4..a796301ea03f 100644 --- a/drivers/pci/hotplug/rpadlpar_sysfs.c +++ b/drivers/pci/hotplug/rpadlpar_sysfs.c @@ -14,8 +14,10 @@ */ #include #include +#include #include #include "rpadlpar.h" +#include "../pci.h" #define DLPAR_KOBJ_NAME "control" @@ -27,7 +29,6 @@ #define MAX_DRC_NAME_LEN 64 - static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t nbytes) { @@ -112,7 +113,7 @@ int dlpar_sysfs_init(void) int error; dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME, - &pci_hotplug_slots_kset->kobj); + &pci_slots_kset->kobj); if (!dlpar_kobj) return -EINVAL; diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 56197b600d36..9b714ea93d20 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -33,33 +33,6 @@ #include #include "rpaphp.h" -static ssize_t address_read_file (struct hotplug_slot *php_slot, char *buf) -{ - int retval; - struct slot *slot = (struct slot *)php_slot->private; - struct pci_bus *bus; - - if (!slot) - return -ENOENT; - - bus = slot->bus; - if (!bus) - return -ENOENT; - - if (bus->self) - retval = sprintf(buf, pci_name(bus->self)); - else - retval = sprintf(buf, "%04x:%02x:00.0", - pci_domain_nr(bus), bus->number); - - return retval; -} - -static struct hotplug_slot_attribute php_attr_address = { - .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, - .show = address_read_file, -}; - /* free up the memory used by a slot */ static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot) { @@ -135,9 +108,6 @@ int rpaphp_deregister_slot(struct slot *slot) list_del(&slot->rpaphp_slot_list); - /* remove "address" file */ - sysfs_remove_file(&php_slot->kobj, &php_attr_address.attr); - retval = pci_hp_deregister(php_slot); if (retval) err("Problem unregistering a slot %s\n", slot->name); @@ -151,6 +121,7 @@ int rpaphp_register_slot(struct slot *slot) { struct hotplug_slot *php_slot = slot->hotplug_slot; int retval; + int slotno; dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n", __func__, slot->dn->full_name, slot->index, slot->name, @@ -162,19 +133,16 @@ int rpaphp_register_slot(struct slot *slot) return -EAGAIN; } - retval = pci_hp_register(php_slot); + if (slot->dn->child) + slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn); + else + slotno = -1; + retval = pci_hp_register(php_slot, slot->bus, slotno); if (retval) { err("pci_hp_register failed with error %d\n", retval); return retval; } - /* create "address" file */ - retval = sysfs_create_file(&php_slot->kobj, &php_attr_address.attr); - if (retval) { - err("sysfs_create_file failed with error %d\n", retval); - goto sysfs_fail; - } - /* add slot to our internal list */ list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head); info("Slot [%s] registered\n", slot->name); diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 2fe37cd85b69..2036a43ff2fd 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -197,13 +197,15 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, static struct hotplug_slot * sn_hp_destroy(void) { struct slot *slot; + struct pci_slot *pci_slot; struct hotplug_slot *bss_hotplug_slot = NULL; list_for_each_entry(slot, &sn_hp_list, hp_list) { bss_hotplug_slot = slot->hotplug_slot; + pci_slot = bss_hotplug_slot->pci_slot; list_del(&((struct slot *)bss_hotplug_slot->private)-> hp_list); - sysfs_remove_file(&bss_hotplug_slot->kobj, + sysfs_remove_file(&pci_slot->kobj, &sn_slot_path_attr.attr); break; } @@ -614,6 +616,7 @@ static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) static int sn_hotplug_slot_register(struct pci_bus *pci_bus) { int device; + struct pci_slot *pci_slot; struct hotplug_slot *bss_hotplug_slot; int rc = 0; @@ -650,11 +653,12 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) bss_hotplug_slot->ops = &sn_hotplug_slot_ops; bss_hotplug_slot->release = &sn_release_slot; - rc = pci_hp_register(bss_hotplug_slot); + rc = pci_hp_register(bss_hotplug_slot, pci_bus, device); if (rc) goto register_err; - rc = sysfs_create_file(&bss_hotplug_slot->kobj, + pci_slot = bss_hotplug_slot->pci_slot; + rc = sysfs_create_file(&pci_slot->kobj, &sn_slot_path_attr.attr); if (rc) goto register_err; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index df41ecc4e7fc..a8cbd039b85b 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -68,7 +68,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_address (struct hotplug_slot *slot, u32 *value); static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); @@ -81,7 +80,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_address = get_address, .get_max_bus_speed = get_max_bus_speed, .get_cur_bus_speed = get_cur_bus_speed, }; @@ -159,7 +157,8 @@ static int init_slots(struct controller *ctrl) dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x " "slot_device_offset=%x\n", slot->bus, slot->device, slot->hp_slot, slot->number, ctrl->slot_device_offset); - retval = pci_hp_register(slot->hotplug_slot); + retval = pci_hp_register(slot->hotplug_slot, + ctrl->pci_dev->subordinate, slot->device); if (retval) { err("pci_hp_register failed with error %d\n", retval); if (retval == -EEXIST) @@ -288,19 +287,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_address (struct hotplug_slot *hotplug_slot, u32 *value) -{ - struct slot *slot = get_slot(hotplug_slot); - struct pci_bus *bus = slot->ctrl->pci_dev->subordinate; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); - - *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device; - - return 0; -} - -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, + enum pci_bus_speed *value) { struct slot *slot = get_slot(hotplug_slot); int retval; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0a497c1b4227..e1d7bbf079b4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -106,3 +106,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) } struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev); + +/* PCI slot sysfs helper code */ +#define to_pci_slot(s) container_of(s, struct pci_slot, kobj) + +extern struct kset *pci_slots_kset; + +struct pci_slot_attribute { + struct attribute attr; + ssize_t (*show)(struct pci_slot *, char *); + ssize_t (*store)(struct pci_slot *, const char *, size_t); +}; +#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr) + diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index aebab71abebf..4562827b7e22 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -414,6 +414,7 @@ static struct pci_bus * pci_alloc_bus(void) INIT_LIST_HEAD(&b->node); INIT_LIST_HEAD(&b->children); INIT_LIST_HEAD(&b->devices); + INIT_LIST_HEAD(&b->slots); } return b; } diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c new file mode 100644 index 000000000000..7e5b85cbd948 --- /dev/null +++ b/drivers/pci/slot.c @@ -0,0 +1,233 @@ +/* + * drivers/pci/slot.c + * Copyright (C) 2006 Matthew Wilcox + * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P. + * Alex Chiang + */ + +#include +#include +#include +#include "pci.h" + +struct kset *pci_slots_kset; +EXPORT_SYMBOL_GPL(pci_slots_kset); + +static ssize_t pci_slot_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct pci_slot *slot = to_pci_slot(kobj); + struct pci_slot_attribute *attribute = to_pci_slot_attr(attr); + return attribute->show ? attribute->show(slot, buf) : -EIO; +} + +static ssize_t pci_slot_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct pci_slot *slot = to_pci_slot(kobj); + struct pci_slot_attribute *attribute = to_pci_slot_attr(attr); + return attribute->store ? attribute->store(slot, buf, len) : -EIO; +} + +static struct sysfs_ops pci_slot_sysfs_ops = { + .show = pci_slot_attr_show, + .store = pci_slot_attr_store, +}; + +static ssize_t address_read_file(struct pci_slot *slot, char *buf) +{ + if (slot->number == 0xff) + return sprintf(buf, "%04x:%02x\n", + pci_domain_nr(slot->bus), + slot->bus->number); + else + return sprintf(buf, "%04x:%02x:%02x\n", + pci_domain_nr(slot->bus), + slot->bus->number, + slot->number); +} + +static void pci_slot_release(struct kobject *kobj) +{ + struct pci_slot *slot = to_pci_slot(kobj); + + pr_debug("%s: releasing pci_slot on %x:%d\n", __func__, + slot->bus->number, slot->number); + + list_del(&slot->list); + + kfree(slot); +} + +static struct pci_slot_attribute pci_slot_attr_address = + __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL); + +static struct attribute *pci_slot_default_attrs[] = { + &pci_slot_attr_address.attr, + NULL, +}; + +static struct kobj_type pci_slot_ktype = { + .sysfs_ops = &pci_slot_sysfs_ops, + .release = &pci_slot_release, + .default_attrs = pci_slot_default_attrs, +}; + +/** + * pci_create_slot - create or increment refcount for physical PCI slot + * @parent: struct pci_bus of parent bridge + * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder + * @name: user visible string presented in /sys/bus/pci/slots/ + * + * PCI slots have first class attributes such as address, speed, width, + * and a &struct pci_slot is used to manage them. This interface will + * either return a new &struct pci_slot to the caller, or if the pci_slot + * already exists, its refcount will be incremented. + * + * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple. + * + * Placeholder slots: + * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify + * a slot. There is one notable exception - pSeries (rpaphp), where the + * @slot_nr cannot be determined until a device is actually inserted into + * the slot. In this scenario, the caller may pass -1 for @slot_nr. + * + * The following semantics are imposed when the caller passes @slot_nr == + * -1. First, the check for existing %struct pci_slot is skipped, as the + * caller may know about several unpopulated slots on a given %struct + * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for + * these slots is then determined by the @name parameter. We expect + * kobject_init_and_add() to warn us if the caller attempts to create + * multiple slots with the same name. The other change in semantics is + * user-visible, which is the 'address' parameter presented in sysfs will + * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the + * %struct pci_bus and bb is the bus number. In other words, the devfn of + * the 'placeholder' slot will not be displayed. + */ + +struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, + const char *name) +{ + struct pci_slot *slot; + int err; + + down_write(&pci_bus_sem); + + if (slot_nr == -1) + goto placeholder; + + /* If we've already created this slot, bump refcount and return. */ + list_for_each_entry(slot, &parent->slots, list) { + if (slot->number == slot_nr) { + kobject_get(&slot->kobj); + pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n", + __func__, + atomic_read(&slot->kobj.kref.refcount), + pci_domain_nr(parent), parent->number, + slot_nr); + goto out; + } + } + +placeholder: + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) { + slot = ERR_PTR(-ENOMEM); + goto out; + } + + slot->bus = parent; + slot->number = slot_nr; + + slot->kobj.kset = pci_slots_kset; + err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL, + "%s", name); + if (err) { + printk(KERN_ERR "Unable to register kobject %s\n", name); + goto err; + } + + INIT_LIST_HEAD(&slot->list); + list_add(&slot->list, &parent->slots); + + /* Don't care if debug printk has a -1 for slot_nr */ + pr_debug("%s: created pci_slot on %04x:%02x:%02x\n", + __func__, pci_domain_nr(parent), parent->number, slot_nr); + + out: + up_write(&pci_bus_sem); + return slot; + err: + kfree(slot); + slot = ERR_PTR(err); + goto out; +} +EXPORT_SYMBOL_GPL(pci_create_slot); + +/** + * pci_update_slot_number - update %struct pci_slot -> number + * @slot - %struct pci_slot to update + * @slot_nr - new number for slot + * + * The primary purpose of this interface is to allow callers who earlier + * created a placeholder slot in pci_create_slot() by passing a -1 as + * slot_nr, to update their %struct pci_slot with the correct @slot_nr. + */ + +void pci_update_slot_number(struct pci_slot *slot, int slot_nr) +{ + int name_count = 0; + struct pci_slot *tmp; + + down_write(&pci_bus_sem); + + list_for_each_entry(tmp, &slot->bus->slots, list) { + WARN_ON(tmp->number == slot_nr); + if (!strcmp(kobject_name(&tmp->kobj), kobject_name(&slot->kobj))) + name_count++; + } + + if (name_count > 1) + printk(KERN_WARNING "pci_update_slot_number found %d slots with the same name: %s\n", name_count, kobject_name(&slot->kobj)); + + slot->number = slot_nr; + up_write(&pci_bus_sem); +} +EXPORT_SYMBOL_GPL(pci_update_slot_number); + +/** + * pci_destroy_slot - decrement refcount for physical PCI slot + * @slot: struct pci_slot to decrement + * + * %struct pci_slot is refcounted, so destroying them is really easy; we + * just call kobject_put on its kobj and let our release methods do the + * rest. + */ + +void pci_destroy_slot(struct pci_slot *slot) +{ + pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__, + atomic_read(&slot->kobj.kref.refcount) - 1, + pci_domain_nr(slot->bus), slot->bus->number, slot->number); + + down_write(&pci_bus_sem); + kobject_put(&slot->kobj); + up_write(&pci_bus_sem); +} +EXPORT_SYMBOL_GPL(pci_destroy_slot); + +static int pci_slot_init(void) +{ + struct kset *pci_bus_kset; + + pci_bus_kset = bus_get_kset(&pci_bus_type); + pci_slots_kset = kset_create_and_add("slots", NULL, + &pci_bus_kset->kobj); + if (!pci_slots_kset) { + printk(KERN_ERR "PCI: Slot initialization failure\n"); + return -ENOMEM; + } + return 0; +} + +subsys_initcall(pci_slot_init); diff --git a/include/linux/pci.h b/include/linux/pci.h index 507ee52323cd..f1f73f79a180 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -17,8 +17,7 @@ #ifndef LINUX_PCI_H #define LINUX_PCI_H -/* Include the pci register defines */ -#include +#include /* The pci register defines */ /* * The PCI interface treats multi-function devices as independent @@ -49,12 +48,22 @@ #include #include #include +#include #include #include /* Include the ID list */ #include +/* pci_slot represents a physical slot */ +struct pci_slot { + struct pci_bus *bus; /* The bus this slot is on */ + struct list_head list; /* node in list of slots on this bus */ + struct hotplug_slot *hotplug; /* Hotplug info (migrate over time) */ + unsigned char number; /* PCI_SLOT(pci_dev->devfn) */ + struct kobject kobj; +}; + /* File state for mmap()s on /proc/bus/pci/X/Y */ enum pci_mmap_state { pci_mmap_io, @@ -142,6 +151,7 @@ struct pci_dev { void *sysdata; /* hook for sys-specific extension */ struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */ + struct pci_slot *slot; /* Physical slot this device is in */ unsigned int devfn; /* encoded device & function index */ unsigned short vendor; @@ -266,6 +276,7 @@ struct pci_bus { struct list_head children; /* list of child buses */ struct list_head devices; /* list of devices on this bus */ struct pci_dev *self; /* bridge device as seen by parent */ + struct list_head slots; /* list of slots on this bus */ struct resource *resource[PCI_BUS_NUM_RESOURCES]; /* address space routed to this bus */ @@ -488,6 +499,10 @@ struct pci_bus *pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata); struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr); +struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, + const char *name); +void pci_destroy_slot(struct pci_slot *slot); +void pci_update_slot_number(struct pci_slot *slot, int slot_nr); int pci_scan_slot(struct pci_bus *bus, int devfn); struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn); void pci_device_add(struct pci_dev *dev, struct pci_bus *bus); diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index dbdcd1ad3c6a..a08cd06b541a 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -95,9 +95,6 @@ struct hotplug_slot_attribute { * @get_adapter_status: Called to get see if an adapter is present in the slot or not. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. - * @get_address: Called to get pci address of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * @get_max_bus_speed: Called to get the max bus speed for a slot. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. @@ -120,7 +117,6 @@ struct hotplug_slot_ops { int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); - int (*get_address) (struct hotplug_slot *slot, u32 *value); int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); }; @@ -140,7 +136,6 @@ struct hotplug_slot_info { u8 attention_status; u8 latch_status; u8 adapter_status; - u32 address; enum pci_bus_speed max_bus_speed; enum pci_bus_speed cur_bus_speed; }; @@ -166,15 +161,14 @@ struct hotplug_slot { /* Variables below this are for use only by the hotplug pci core. */ struct list_head slot_list; - struct kobject kobj; + struct pci_slot *pci_slot; }; #define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj) -extern int pci_hp_register (struct hotplug_slot *slot); -extern int pci_hp_deregister (struct hotplug_slot *slot); +extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr); +extern int pci_hp_deregister(struct hotplug_slot *slot); extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info); -extern struct kset *pci_hotplug_slots_kset; /* PCI Setting Record (Type 0) */ struct hpp_type0 { -- cgit v1.2.3 From 12c03f59c3909159010b87a926f5626d4380d441 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Mon, 9 Jun 2008 16:33:55 -0700 Subject: smc911x: introduce platform data flags This patch adds a new header file for platform data information together with code that adds run time bus width and irq flag support. Signed-off-by: Magnus Damm Cc: Jeff Garzik Cc: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/net/smc911x.c | 17 ++++++++++- drivers/net/smc911x.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/smc911x.h | 12 ++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 include/linux/smc911x.h (limited to 'include/linux') diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 2ca4db85f938..fc605f276c00 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -1819,6 +1819,7 @@ static int __init smc911x_probe(struct net_device *dev) int i, retval; unsigned int val, chip_id, revision; const char *version_string; + unsigned long irq_flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); @@ -1985,9 +1986,15 @@ static int __init smc911x_probe(struct net_device *dev) lp->ctl_rfduplx = 1; lp->ctl_rspeed = 100; +#ifdef SMC_DYNAMIC_BUS_CONFIG + irq_flags = lp->cfg.irq_flags; +#else + irq_flags = IRQF_SHARED | SMC_IRQ_SENSE; +#endif + /* Grab the IRQ */ retval = request_irq(dev->irq, &smc911x_interrupt, - IRQF_SHARED | SMC_IRQ_SENSE, dev->name, dev); + irq_flags, dev->name, dev); if (retval) goto err_out; @@ -2057,6 +2064,7 @@ err_out: */ static int smc911x_drv_probe(struct platform_device *pdev) { + struct smc91x_platdata *pd = pdev->dev.platform_data; struct net_device *ndev; struct resource *res; struct smc911x_local *lp; @@ -2090,6 +2098,13 @@ static int smc911x_drv_probe(struct platform_device *pdev) ndev->irq = platform_get_irq(pdev, 0); lp = netdev_priv(ndev); lp->netdev = ndev; +#ifdef SMC_DYNAMIC_BUS_CONFIG + if (!pd) { + ret = -EINVAL; + goto release_both; + } + memcpy(&lp->cfg, pd, sizeof(lp->cfg)); +#endif addr = ioremap(res->start, SMC911X_IO_EXTENT); if (!addr) { diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h index 271a3e8cf683..266232271a72 100644 --- a/drivers/net/smc911x.h +++ b/drivers/net/smc911x.h @@ -29,6 +29,7 @@ #ifndef _SMC911X_H_ #define _SMC911X_H_ +#include /* * Use the DMA feature on PXA chips */ @@ -42,6 +43,12 @@ #define SMC_USE_16BIT 0 #define SMC_USE_32BIT 1 #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW +#else +/* + * Default configuration + */ + +#define SMC_DYNAMIC_BUS_CONFIG #endif /* store this information for the driver.. */ @@ -92,12 +99,84 @@ struct smc911x_local { struct device *dev; #endif void __iomem *base; +#ifdef SMC_DYNAMIC_BUS_CONFIG + struct smc911x_platdata cfg; +#endif }; /* * Define the bus width specific IO macros */ +#ifdef SMC_DYNAMIC_BUS_CONFIG +static inline unsigned int SMC_inl(struct smc911x_local *lp, int reg) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) + return readl(ioaddr); + + if (lp->cfg.flags & SMC911X_USE_16BIT) + return readw(ioaddr) | (readw(ioaddr + 2) << 16); + + BUG(); +} + +static inline void SMC_outl(unsigned int value, struct smc911x_local *lp, + int reg) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) { + writel(value, ioaddr); + return; + } + + if (lp->cfg.flags & SMC911X_USE_16BIT) { + writew(value & 0xffff, ioaddr); + writew(value >> 16, ioaddr + 2); + return; + } + + BUG(); +} + +static inline void SMC_insl(struct smc911x_local *lp, int reg, + void *addr, unsigned int count) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) { + readsl(ioaddr, addr, count); + return; + } + + if (lp->cfg.flags & SMC911X_USE_16BIT) { + readsw(ioaddr, addr, count * 2); + return; + } + + BUG(); +} + +static inline void SMC_outsl(struct smc911x_local *lp, int reg, + void *addr, unsigned int count) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) { + writesl(ioaddr, addr, count); + return; + } + + if (lp->cfg.flags & SMC911X_USE_16BIT) { + writesw(ioaddr, addr, count * 2); + return; + } + + BUG(); +} +#else #if SMC_USE_16BIT #define SMC_inl(lp, r) ((readw((lp)->base + (r)) & 0xFFFF) + (readw((lp)->base + (r) + 2) << 16)) #define SMC_outl(v, lp, r) \ @@ -115,6 +194,8 @@ struct smc911x_local { #define SMC_outsl(lp, r, p, l) writesl((int*)((lp)->base + (r)), p, l) #endif /* SMC_USE_16BIT */ +#endif /* SMC_DYNAMIC_BUS_CONFIG */ + #ifdef SMC_USE_PXA_DMA #define SMC_USE_DMA diff --git a/include/linux/smc911x.h b/include/linux/smc911x.h new file mode 100644 index 000000000000..b58f54c24183 --- /dev/null +++ b/include/linux/smc911x.h @@ -0,0 +1,12 @@ +#ifndef __SMC911X_H__ +#define __SMC911X_H__ + +#define SMC911X_USE_16BIT (1 << 0) +#define SMC911X_USE_32BIT (1 << 1) + +struct smc911x_platdata { + unsigned long flags; + unsigned long irq_flags; /* IRQF_... */ +}; + +#endif /* __SMC911X_H__ */ -- cgit v1.2.3 From 0b040829952d84bf2a62526f0e24b624e0699447 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 10 Jun 2008 22:46:50 -0700 Subject: net: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 2 -- include/linux/if_ppp.h | 2 -- include/linux/if_tun.h | 2 -- include/linux/ip6_tunnel.h | 4 ---- include/linux/ppp-comp.h | 2 -- include/linux/ppp_defs.h | 2 -- include/linux/sunrpc/auth_gss.h | 2 -- include/linux/sunrpc/gss_api.h | 2 -- include/linux/sunrpc/svcauth_gss.h | 3 --- include/net/inetpeer.h | 2 -- include/net/ip6_tunnel.h | 4 ---- include/net/ipconfig.h | 2 -- include/net/ipv6.h | 2 -- include/net/snmp.h | 2 -- net/bluetooth/bnep/bnep.h | 4 ---- net/bluetooth/bnep/core.c | 4 ---- net/bluetooth/bnep/netdev.c | 4 ---- net/bluetooth/bnep/sock.c | 4 ---- net/bluetooth/rfcomm/core.c | 2 -- net/bluetooth/rfcomm/sock.c | 2 -- net/bluetooth/rfcomm/tty.c | 2 -- net/bridge/br.c | 2 -- net/bridge/br_device.c | 2 -- net/bridge/br_fdb.c | 2 -- net/bridge/br_forward.c | 2 -- net/bridge/br_if.c | 2 -- net/bridge/br_input.c | 2 -- net/bridge/br_ioctl.c | 2 -- net/bridge/br_notify.c | 2 -- net/bridge/br_private.h | 2 -- net/bridge/br_private_stp.h | 2 -- net/bridge/br_stp.c | 2 -- net/bridge/br_stp_bpdu.c | 2 -- net/bridge/br_stp_if.c | 2 -- net/bridge/br_stp_timer.c | 2 -- net/core/skbuff.c | 2 -- net/core/sock.c | 2 -- net/ipv4/af_inet.c | 2 -- net/ipv4/arp.c | 2 -- net/ipv4/devinet.c | 2 -- net/ipv4/fib_frontend.c | 2 -- net/ipv4/fib_hash.c | 2 -- net/ipv4/fib_semantics.c | 2 -- net/ipv4/fib_trie.c | 2 -- net/ipv4/icmp.c | 2 -- net/ipv4/igmp.c | 2 -- net/ipv4/inet_diag.c | 2 -- net/ipv4/inetpeer.c | 2 -- net/ipv4/ip_forward.c | 2 -- net/ipv4/ip_fragment.c | 2 -- net/ipv4/ip_input.c | 2 -- net/ipv4/ip_options.c | 2 -- net/ipv4/ip_output.c | 2 -- net/ipv4/ip_sockglue.c | 2 -- net/ipv4/ipconfig.c | 2 -- net/ipv4/ipip.c | 2 -- net/ipv4/ipmr.c | 2 -- net/ipv4/ipvs/ip_vs_app.c | 2 -- net/ipv4/ipvs/ip_vs_conn.c | 2 -- net/ipv4/ipvs/ip_vs_core.c | 2 -- net/ipv4/ipvs/ip_vs_ctl.c | 2 -- net/ipv4/ipvs/ip_vs_dh.c | 2 -- net/ipv4/ipvs/ip_vs_est.c | 2 -- net/ipv4/ipvs/ip_vs_ftp.c | 2 -- net/ipv4/ipvs/ip_vs_lblc.c | 2 -- net/ipv4/ipvs/ip_vs_lblcr.c | 2 -- net/ipv4/ipvs/ip_vs_lc.c | 2 -- net/ipv4/ipvs/ip_vs_nq.c | 2 -- net/ipv4/ipvs/ip_vs_proto.c | 2 -- net/ipv4/ipvs/ip_vs_proto_ah.c | 2 -- net/ipv4/ipvs/ip_vs_proto_esp.c | 2 -- net/ipv4/ipvs/ip_vs_proto_tcp.c | 2 -- net/ipv4/ipvs/ip_vs_proto_udp.c | 2 -- net/ipv4/ipvs/ip_vs_rr.c | 2 -- net/ipv4/ipvs/ip_vs_sched.c | 2 -- net/ipv4/ipvs/ip_vs_sed.c | 2 -- net/ipv4/ipvs/ip_vs_sh.c | 2 -- net/ipv4/ipvs/ip_vs_sync.c | 2 -- net/ipv4/ipvs/ip_vs_wlc.c | 2 -- net/ipv4/ipvs/ip_vs_wrr.c | 2 -- net/ipv4/ipvs/ip_vs_xmit.c | 2 -- net/ipv4/proc.c | 2 -- net/ipv4/protocol.c | 2 -- net/ipv4/raw.c | 2 -- net/ipv4/route.c | 2 -- net/ipv4/syncookies.c | 2 -- net/ipv4/sysctl_net_ipv4.c | 2 -- net/ipv4/tcp.c | 2 -- net/ipv4/tcp_diag.c | 2 -- net/ipv4/tcp_input.c | 2 -- net/ipv4/tcp_ipv4.c | 2 -- net/ipv4/tcp_minisocks.c | 2 -- net/ipv4/tcp_output.c | 2 -- net/ipv4/tcp_timer.c | 2 -- net/ipv4/udp.c | 2 -- net/ipv4/udplite.c | 2 -- net/ipv6/addrconf.c | 2 -- net/ipv6/af_inet6.c | 2 -- net/ipv6/datagram.c | 2 -- net/ipv6/exthdrs.c | 2 -- net/ipv6/icmp.c | 2 -- net/ipv6/ip6_fib.c | 2 -- net/ipv6/ip6_input.c | 2 -- net/ipv6/ip6_output.c | 2 -- net/ipv6/ip6_tunnel.c | 2 -- net/ipv6/ipv6_sockglue.c | 2 -- net/ipv6/mcast.c | 2 -- net/ipv6/proc.c | 2 -- net/ipv6/protocol.c | 2 -- net/ipv6/raw.c | 2 -- net/ipv6/reassembly.c | 2 -- net/ipv6/route.c | 2 -- net/ipv6/sit.c | 2 -- net/ipv6/tcp_ipv6.c | 2 -- net/ipv6/udp.c | 2 -- net/ipv6/udplite.c | 2 -- net/packet/af_packet.c | 2 -- net/sched/sch_htb.c | 2 -- net/sunrpc/auth_gss/auth_gss.c | 2 -- net/sysctl_net.c | 1 - net/unix/af_unix.c | 2 -- 121 files changed, 254 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 950e13d09e06..6badb3e2c4e4 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -4,8 +4,6 @@ * Authors: * Lennert Buytenhek * - * $Id: if_bridge.h,v 1.1 2000/02/18 16:47:01 davem Exp $ - * * 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 diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h index 0f2f70d4e48c..c3b1f8562709 100644 --- a/include/linux/if_ppp.h +++ b/include/linux/if_ppp.h @@ -1,5 +1,3 @@ -/* $Id: if_ppp.h,v 1.21 2000/03/27 06:03:36 paulus Exp $ */ - /* * if_ppp.h - Point-to-Point Protocol definitions. * diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 8c71fe2fb1f5..18f31b6187a3 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -11,8 +11,6 @@ * 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. - * - * $Id: if_tun.h,v 1.2 2001/06/01 18:39:47 davem Exp $ */ #ifndef __IF_TUN_H diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h index af3f4a70f3df..1e7cc4af40de 100644 --- a/include/linux/ip6_tunnel.h +++ b/include/linux/ip6_tunnel.h @@ -1,7 +1,3 @@ -/* - * $Id$ - */ - #ifndef _IP6_TUNNEL_H #define _IP6_TUNNEL_H diff --git a/include/linux/ppp-comp.h b/include/linux/ppp-comp.h index e86a7a5cf355..b8d4ddd22736 100644 --- a/include/linux/ppp-comp.h +++ b/include/linux/ppp-comp.h @@ -23,8 +23,6 @@ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. - * - * $Id: ppp-comp.h,v 1.6 1997/11/27 06:04:44 paulus Exp $ */ /* diff --git a/include/linux/ppp_defs.h b/include/linux/ppp_defs.h index c6b13ff85028..6e8adc77522c 100644 --- a/include/linux/ppp_defs.h +++ b/include/linux/ppp_defs.h @@ -1,5 +1,3 @@ -/* $Id: ppp_defs.h,v 1.2 1994/09/21 01:31:06 paulus Exp $ */ - /* * ppp_defs.h - PPP definitions. * diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h index fec6899bf355..d48d4e605f74 100644 --- a/include/linux/sunrpc/auth_gss.h +++ b/include/linux/sunrpc/auth_gss.h @@ -7,8 +7,6 @@ * Andy Adamson * Bruce Fields * Copyright (c) 2000 The Regents of the University of Michigan - * - * $Id$ */ #ifndef _LINUX_SUNRPC_AUTH_GSS_H diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h index 459c5fc11d51..03f33330ece2 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h @@ -7,8 +7,6 @@ * Andy Adamson * Bruce Fields * Copyright (c) 2000 The Regents of the University of Michigan - * - * $Id$ */ #ifndef _LINUX_SUNRPC_GSS_API_H diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h index 417a1def56db..c9165d9771a8 100644 --- a/include/linux/sunrpc/svcauth_gss.h +++ b/include/linux/sunrpc/svcauth_gss.h @@ -3,9 +3,6 @@ * * Bruce Fields * Copyright (c) 2002 The Regents of the Unviersity of Michigan - * - * $Id$ - * */ #ifndef _LINUX_SUNRPC_SVCAUTH_GSS_H diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index ad8404b56113..15e1f8fe4c1f 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -1,8 +1,6 @@ /* * INETPEER - A storage for permanent information about peers * - * Version: $Id: inetpeer.h,v 1.2 2002/01/12 07:54:56 davem Exp $ - * * Authors: Andrey V. Savochkin */ diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 3780592ebe8e..83b4e008b16d 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -1,7 +1,3 @@ -/* - * $Id$ - */ - #ifndef _NET_IP6_TUNNEL_H #define _NET_IP6_TUNNEL_H diff --git a/include/net/ipconfig.h b/include/net/ipconfig.h index 3924d7d2cb11..c74cc1bd5a02 100644 --- a/include/net/ipconfig.h +++ b/include/net/ipconfig.h @@ -1,6 +1,4 @@ /* - * $Id: ipconfig.h,v 1.4 2001/04/30 04:51:46 davem Exp $ - * * Copyright (C) 1997 Martin Mares * * Automatic IP Layer Configuration diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e0a612bc9c4e..7f7db8d57934 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -4,8 +4,6 @@ * Authors: * Pedro Roque * - * $Id: ipv6.h,v 1.1 2002/05/20 15:13:07 jgrimm Exp $ - * * 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 diff --git a/include/net/snmp.h b/include/net/snmp.h index ce2f48507510..57c93628695f 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -14,8 +14,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: snmp.h,v 1.19 2001/06/14 13:40:46 davem Exp $ - * */ #ifndef _SNMP_H diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h index e69244dd8de8..b69bf4e7c48b 100644 --- a/net/bluetooth/bnep/bnep.h +++ b/net/bluetooth/bnep/bnep.h @@ -16,10 +16,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * $Id: bnep.h,v 1.5 2002/08/04 21:23:58 maxk Exp $ - */ - #ifndef _BNEP_H #define _BNEP_H diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index f85d94643aaf..1d98a1b80da7 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -25,10 +25,6 @@ SOFTWARE IS DISCLAIMED. */ -/* - * $Id: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $ - */ - #include #include diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 95e3837e4312..d9fa0ab2c87f 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -25,10 +25,6 @@ SOFTWARE IS DISCLAIMED. */ -/* - * $Id: netdev.c,v 1.8 2002/08/04 21:23:58 maxk Exp $ - */ - #include #include diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 201e5b1ce473..8ffb57f2303a 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -24,10 +24,6 @@ SOFTWARE IS DISCLAIMED. */ -/* - * $Id: sock.c,v 1.4 2002/08/04 21:23:58 maxk Exp $ - */ - #include #include diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 0c2c93735e93..b4fb84e398e5 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -23,8 +23,6 @@ /* * Bluetooth RFCOMM core. - * - * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ */ #include diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 5083adcbfae5..c9054487670a 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -23,8 +23,6 @@ /* * RFCOMM sockets. - * - * $Id: sock.c,v 1.24 2002/10/03 01:00:34 maxk Exp $ */ #include diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c9191871c1e0..be84f4fc1477 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -23,8 +23,6 @@ /* * RFCOMM TTY. - * - * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $ */ #include diff --git a/net/bridge/br.c b/net/bridge/br.c index 8f3c58e5f7a5..cede010f4ddd 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br.c,v 1.47 2001/12/24 00:56:41 davem Exp $ - * * 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 diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 626c7795ae30..a6ffc6c2a69f 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_device.c,v 1.6 2001/12/24 00:59:55 davem Exp $ - * * 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 diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 72c5976a5ce3..4de74cdd091d 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_fdb.c,v 1.6 2002/01/17 00:57:07 davem Exp $ - * * 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 diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index a4711674b3df..512645727f51 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_forward.c,v 1.4 2001/08/14 22:05:57 davem Exp $ - * * 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 diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index c2397f503b0f..143c954681b8 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_if.c,v 1.7 2001/12/24 00:59:55 davem Exp $ - * * 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 diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index fa0f5711a996..0145e9416714 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_input.c,v 1.10 2001/12/24 04:50:20 davem Exp $ - * * 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 diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 0655a5f07f58..eeee218eed80 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_ioctl.c,v 1.4 2000/11/08 05:16:40 davem Exp $ - * * 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 diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 00644a544e3c..88d8ec7b3142 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_notify.c,v 1.2 2000/02/21 15:51:34 davem Exp $ - * * 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 diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0243cb489edd..83ff5861c2d2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -4,8 +4,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_private.h,v 1.7 2001/12/24 00:59:55 davem Exp $ - * * 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 diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index e29f01ac1adf..8b650f7fbfa0 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -4,8 +4,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_private_stp.h,v 1.3 2001/02/05 06:03:47 davem Exp $ - * * 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 diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index e38034aa56f5..284d1b2fa1ff 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp.c,v 1.4 2000/06/19 10:13:35 davem Exp $ - * * 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 diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index ddeb6e5d45d6..9dc2de656965 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp_bpdu.c,v 1.3 2001/11/10 02:35:25 davem Exp $ - * * 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 diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 1a430eccec9b..1a4e5c37a0cf 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp_if.c,v 1.4 2001/04/14 21:14:39 davem Exp $ - * * 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 diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index 77f5255e6915..772a140bfdf0 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp_timer.c,v 1.3 2000/05/05 02:17:17 davem Exp $ - * * 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 diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1e556d312117..3e18f8525e82 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4,8 +4,6 @@ * Authors: Alan Cox * Florian La Roche * - * Version: $Id: skbuff.c,v 1.90 2001/11/07 05:56:19 davem Exp $ - * * Fixes: * Alan Cox : Fixed the worst of the load * balancer bugs. diff --git a/net/core/sock.c b/net/core/sock.c index 88094cb09c06..3879bf65897e 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -7,8 +7,6 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.117 2002/02/01 22:01:03 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Florian La Roche, diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 24eca23c2db3..42bd24b64b57 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,8 +5,6 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.137 2002/02/01 22:01:03 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Florian La Roche, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 9b539fa9fe18..20c515a1be28 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1,6 +1,4 @@ /* linux/net/ipv4/arp.c - * - * Version: $Id: arp.c,v 1.99 2001/08/30 22:55:42 davem Exp $ * * Copyright (C) 1994 by Florian La Roche * diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 61011e1d580e..f8c0b0aea93a 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1,8 +1,6 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.44 2001/10/31 21:55:54 davem Exp $ - * * 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 diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 0b2ac6a3d903..5ad01d63f83b 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -5,8 +5,6 @@ * * IPv4 Forwarding Information Base: FIB frontend. * - * Version: $Id: fib_frontend.c,v 1.26 2001/10/31 21:55:54 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index 2e2fc3376ac9..eeec4bf982b8 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -5,8 +5,6 @@ * * IPv4 FIB: lookup engine and maintenance routines. * - * Version: $Id: fib_hash.c,v 1.13 2001/10/31 21:55:54 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3b83c34019fc..9335eba683c3 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -5,8 +5,6 @@ * * IPv4 Forwarding Information Base: semantics. * - * Version: $Id: fib_semantics.c,v 1.19 2002/01/12 07:54:56 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 4b02d14e7ab9..394db9c941a1 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -22,8 +22,6 @@ * IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson * IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999 * - * Version: $Id: fib_trie.c,v 1.3 2005/06/08 14:20:01 robert Exp $ - * * * Code from fib_hash has been reused which includes the following header: * diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 87397351ddac..aa7cf46853b7 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -3,8 +3,6 @@ * * Alan Cox, * - * Version: $Id: icmp.c,v 1.85 2002/02/01 22:01:03 davem Exp $ - * * 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 diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 2769dc4a4c84..68e84a933e90 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -8,8 +8,6 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.47 2002/02/01 22:01:03 davem Exp $ - * * Authors: * Alan Cox * diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index da97695e7096..c10036e7a463 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -1,8 +1,6 @@ /* * inet_diag.c Module for monitoring INET transport protocols sockets. * - * Version: $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index af995198f643..a456ceeac3f2 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -3,8 +3,6 @@ * * This source is covered by the GNU GPL, the same as all kernel sources. * - * Version: $Id: inetpeer.c,v 1.7 2001/09/20 21:22:50 davem Exp $ - * * Authors: Andrey V. Savochkin */ diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 4813c39b438b..37d36a3f33cd 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -5,8 +5,6 @@ * * The IP forwarding functionality. * - * Version: $Id: ip_forward.c,v 1.48 2000/12/13 18:31:48 davem Exp $ - * * Authors: see ip.c * * Fixes: diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index be1cb89a8d5a..91e321407313 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -5,8 +5,6 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.59 2002/01/12 07:54:56 davem Exp $ - * * Authors: Fred N. van Kempen * Alan Cox * diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index ff77a4a7f9ec..7c26428ea67b 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -5,8 +5,6 @@ * * The Internet Protocol (IP) module. * - * Version: $Id: ip_input.c,v 1.55 2002/01/12 07:39:45 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 33126ad2cfdc..be3f18a7a40e 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -5,8 +5,6 @@ * * The options processing module for ip.c * - * Version: $Id: ip_options.c,v 1.21 2001/09/01 00:31:50 davem Exp $ - * * Authors: A.N.Kuznetsov * */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e527628f56cf..f1278eecf56d 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -5,8 +5,6 @@ * * The Internet Protocol (IP) output module. * - * Version: $Id: ip_output.c,v 1.100 2002/02/01 22:01:03 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index e0514e82308e..105d92a039b9 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -5,8 +5,6 @@ * * The IP to API glue. * - * Version: $Id: ip_sockglue.c,v 1.62 2002/02/01 22:01:04 davem Exp $ - * * Authors: see ip.c * * Fixes: diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index ed45037ce9be..b88aa9afa42e 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1,6 +1,4 @@ /* - * $Id: ipconfig.c,v 1.46 2002/02/01 22:01:04 davem Exp $ - * * Automatic Configuration of IP -- use DHCP, BOOTP, RARP, or * user-supplied information to configure own IP address and routes. * diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 86d8836551b9..4c6d2caf9203 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -1,8 +1,6 @@ /* * Linux NET3: IP/IP protocol decoder. * - * Version: $Id: ipip.c,v 1.50 2001/10/02 02:22:36 davem Exp $ - * * Authors: * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 * diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a34da4977c73..300ab0c2919e 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -9,8 +9,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: ipmr.c,v 1.65 2001/10/31 21:55:54 davem Exp $ - * * Fixes: * Michael Chastain : Incorrect size of copying. * Alan Cox : Added the cache manager code diff --git a/net/ipv4/ipvs/ip_vs_app.c b/net/ipv4/ipvs/ip_vs_app.c index 535abe0c45e7..1f1897a1a702 100644 --- a/net/ipv4/ipvs/ip_vs_app.c +++ b/net/ipv4/ipvs/ip_vs_app.c @@ -1,8 +1,6 @@ /* * ip_vs_app.c: Application module support for IPVS * - * Version: $Id: ip_vs_app.c,v 1.17 2003/03/22 06:31:21 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c index 65f1ba112752..f8bdae47a77f 100644 --- a/net/ipv4/ipvs/ip_vs_conn.c +++ b/net/ipv4/ipvs/ip_vs_conn.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_conn.c,v 1.31 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * Julian Anastasov diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c index 963981a9d501..bcf6276ba4b2 100644 --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_core.c,v 1.34 2003/05/10 03:05:23 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * Julian Anastasov diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index 94c5767c8e01..9a5ace0b4dd6 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_ctl.c,v 1.36 2003/06/08 09:31:19 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * Julian Anastasov diff --git a/net/ipv4/ipvs/ip_vs_dh.c b/net/ipv4/ipvs/ip_vs_dh.c index dcf5d46aaa5e..8afc1503ed20 100644 --- a/net/ipv4/ipvs/ip_vs_dh.c +++ b/net/ipv4/ipvs/ip_vs_dh.c @@ -1,8 +1,6 @@ /* * IPVS: Destination Hashing scheduling module * - * Version: $Id: ip_vs_dh.c,v 1.5 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * Inspired by the consistent hashing scheduler patch from diff --git a/net/ipv4/ipvs/ip_vs_est.c b/net/ipv4/ipvs/ip_vs_est.c index dfa0d713c801..bc04eedd6dbb 100644 --- a/net/ipv4/ipvs/ip_vs_est.c +++ b/net/ipv4/ipvs/ip_vs_est.c @@ -1,8 +1,6 @@ /* * ip_vs_est.c: simple rate estimator for IPVS * - * Version: $Id: ip_vs_est.c,v 1.4 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_ftp.c b/net/ipv4/ipvs/ip_vs_ftp.c index 59aa166b7678..c1c758e4f733 100644 --- a/net/ipv4/ipvs/ip_vs_ftp.c +++ b/net/ipv4/ipvs/ip_vs_ftp.c @@ -1,8 +1,6 @@ /* * ip_vs_ftp.c: IPVS ftp application module * - * Version: $Id: ip_vs_ftp.c,v 1.13 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * Changes: diff --git a/net/ipv4/ipvs/ip_vs_lblc.c b/net/ipv4/ipvs/ip_vs_lblc.c index 3888642706ad..0efa3db4b180 100644 --- a/net/ipv4/ipvs/ip_vs_lblc.c +++ b/net/ipv4/ipvs/ip_vs_lblc.c @@ -1,8 +1,6 @@ /* * IPVS: Locality-Based Least-Connection scheduling module * - * Version: $Id: ip_vs_lblc.c,v 1.10 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_lblcr.c b/net/ipv4/ipvs/ip_vs_lblcr.c index daa260eb21cf..8e3bbeb45138 100644 --- a/net/ipv4/ipvs/ip_vs_lblcr.c +++ b/net/ipv4/ipvs/ip_vs_lblcr.c @@ -1,8 +1,6 @@ /* * IPVS: Locality-Based Least-Connection with Replication scheduler * - * Version: $Id: ip_vs_lblcr.c,v 1.11 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_lc.c b/net/ipv4/ipvs/ip_vs_lc.c index d88fef90a641..ac9f08e065d5 100644 --- a/net/ipv4/ipvs/ip_vs_lc.c +++ b/net/ipv4/ipvs/ip_vs_lc.c @@ -1,8 +1,6 @@ /* * IPVS: Least-Connection Scheduling module * - * Version: $Id: ip_vs_lc.c,v 1.10 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_nq.c b/net/ipv4/ipvs/ip_vs_nq.c index bc2a9e5f2a7b..a46bf258d420 100644 --- a/net/ipv4/ipvs/ip_vs_nq.c +++ b/net/ipv4/ipvs/ip_vs_nq.c @@ -1,8 +1,6 @@ /* * IPVS: Never Queue scheduling module * - * Version: $Id: ip_vs_nq.c,v 1.2 2003/06/08 09:31:19 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_proto.c b/net/ipv4/ipvs/ip_vs_proto.c index 4b1c16cbb16b..876714f23d65 100644 --- a/net/ipv4/ipvs/ip_vs_proto.c +++ b/net/ipv4/ipvs/ip_vs_proto.c @@ -1,8 +1,6 @@ /* * ip_vs_proto.c: transport protocol load balancing support for IPVS * - * Version: $Id: ip_vs_proto.c,v 1.2 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/ipvs/ip_vs_proto_ah.c b/net/ipv4/ipvs/ip_vs_proto_ah.c index 4bf835e1d86d..73e0ea87c1f5 100644 --- a/net/ipv4/ipvs/ip_vs_proto_ah.c +++ b/net/ipv4/ipvs/ip_vs_proto_ah.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_ah.c: AH IPSec load balancing support for IPVS * - * Version: $Id: ip_vs_proto_ah.c,v 1.1 2003/07/04 15:04:37 wensong Exp $ - * * Authors: Julian Anastasov , February 2002 * Wensong Zhang * diff --git a/net/ipv4/ipvs/ip_vs_proto_esp.c b/net/ipv4/ipvs/ip_vs_proto_esp.c index db6a6b7b1a0b..21d70c8ffa54 100644 --- a/net/ipv4/ipvs/ip_vs_proto_esp.c +++ b/net/ipv4/ipvs/ip_vs_proto_esp.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_esp.c: ESP IPSec load balancing support for IPVS * - * Version: $Id: ip_vs_proto_esp.c,v 1.1 2003/07/04 15:04:37 wensong Exp $ - * * Authors: Julian Anastasov , February 2002 * Wensong Zhang * diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index b83dc14b0a4d..d0ea467986a0 100644 --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_tcp.c: TCP load balancing support for IPVS * - * Version: $Id: ip_vs_proto_tcp.c,v 1.3 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index 75771cb3cd6f..c6be5d56823f 100644 --- a/net/ipv4/ipvs/ip_vs_proto_udp.c +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_udp.c: UDP load balancing support for IPVS * - * Version: $Id: ip_vs_proto_udp.c,v 1.3 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/ipvs/ip_vs_rr.c b/net/ipv4/ipvs/ip_vs_rr.c index 433f8a947924..c8db12d39e61 100644 --- a/net/ipv4/ipvs/ip_vs_rr.c +++ b/net/ipv4/ipvs/ip_vs_rr.c @@ -1,8 +1,6 @@ /* * IPVS: Round-Robin Scheduling module * - * Version: $Id: ip_vs_rr.c,v 1.9 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * diff --git a/net/ipv4/ipvs/ip_vs_sched.c b/net/ipv4/ipvs/ip_vs_sched.c index 121a32b1b756..b64767309855 100644 --- a/net/ipv4/ipvs/ip_vs_sched.c +++ b/net/ipv4/ipvs/ip_vs_sched.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_sched.c,v 1.13 2003/05/10 03:05:23 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * diff --git a/net/ipv4/ipvs/ip_vs_sed.c b/net/ipv4/ipvs/ip_vs_sed.c index dd7c128f9db3..2a7d31358181 100644 --- a/net/ipv4/ipvs/ip_vs_sed.c +++ b/net/ipv4/ipvs/ip_vs_sed.c @@ -1,8 +1,6 @@ /* * IPVS: Shortest Expected Delay scheduling module * - * Version: $Id: ip_vs_sed.c,v 1.1 2003/05/10 03:06:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_sh.c b/net/ipv4/ipvs/ip_vs_sh.c index 1b25b00ef1e1..b8fdfac65001 100644 --- a/net/ipv4/ipvs/ip_vs_sh.c +++ b/net/ipv4/ipvs/ip_vs_sh.c @@ -1,8 +1,6 @@ /* * IPVS: Source Hashing scheduling module * - * Version: $Id: ip_vs_sh.c,v 1.5 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c index eff54efe0351..2d4a86f73325 100644 --- a/net/ipv4/ipvs/ip_vs_sync.c +++ b/net/ipv4/ipvs/ip_vs_sync.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_sync.c,v 1.13 2003/06/08 09:31:19 wensong Exp $ - * * Authors: Wensong Zhang * * ip_vs_sync: sync connection info from master load balancer to backups diff --git a/net/ipv4/ipvs/ip_vs_wlc.c b/net/ipv4/ipvs/ip_vs_wlc.c index 8a9d913261d8..772c3cb4eca1 100644 --- a/net/ipv4/ipvs/ip_vs_wlc.c +++ b/net/ipv4/ipvs/ip_vs_wlc.c @@ -1,8 +1,6 @@ /* * IPVS: Weighted Least-Connection Scheduling module * - * Version: $Id: ip_vs_wlc.c,v 1.13 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * diff --git a/net/ipv4/ipvs/ip_vs_wrr.c b/net/ipv4/ipvs/ip_vs_wrr.c index 85c680add6df..1d6932d7dc97 100644 --- a/net/ipv4/ipvs/ip_vs_wrr.c +++ b/net/ipv4/ipvs/ip_vs_wrr.c @@ -1,8 +1,6 @@ /* * IPVS: Weighted Round-Robin Scheduling module * - * Version: $Id: ip_vs_wrr.c,v 1.12 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_xmit.c b/net/ipv4/ipvs/ip_vs_xmit.c index f63006caea03..9892d4aca42e 100644 --- a/net/ipv4/ipvs/ip_vs_xmit.c +++ b/net/ipv4/ipvs/ip_vs_xmit.c @@ -1,8 +1,6 @@ /* * ip_vs_xmit.c: various packet transmitters for IPVS * - * Version: $Id: ip_vs_xmit.c,v 1.2 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 552169b41b16..eb5cee279c5f 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -7,8 +7,6 @@ * PROC file system. It is mainly used for debugging and * statistics. * - * Version: $Id: proc.c,v 1.45 2001/05/16 16:45:35 davem Exp $ - * * Authors: Fred N. van Kempen, * Gerald J. Heim, * Fred Baumgarten, diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 971ab9356e51..ea50da0649fd 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -5,8 +5,6 @@ * * INET protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.14 2001/05/18 02:25:49 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index e7e091d365ff..1d0c97c8712d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -5,8 +5,6 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: $Id: raw.c,v 1.64 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 96be336064fb..fe3a02237286 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,8 +5,6 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.103 2002/01/12 07:44:09 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Alan Cox, diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 73ba98921d64..6317d3c8dc0d 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -8,8 +8,6 @@ * 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. - * - * $Id: syncookies.c,v 1.18 2002/02/01 22:01:04 davem Exp $ */ #include diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index c437f804ee38..901607003205 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,8 +1,6 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.50 2001/10/20 00:00:11 davem Exp $ - * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ab66683b8043..ad66b09e0bcd 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.216 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 2fbcc7d1b1a0..838d491dfda7 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -1,8 +1,6 @@ /* * tcp_diag.c Module for monitoring TCP transport protocols sockets. * - * Version: $Id: tcp_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index eba873e9b560..b68c3c7d906b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.243 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index cd601a866c2f..f2926ae1de57 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.240 2002/02/01 22:01:04 davem Exp $ - * * IPv4 specific functions * * diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 019c8c16e5cc..1276cab85e3e 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_minisocks.c,v 1.15 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ad993ecb4810..b171ac65ccab 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.146 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 4de68cf5f2aa..e77e7ae0bf2c 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.88 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 56fcda3694ba..355e6d62d483 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,8 +5,6 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.102 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Arnt Gulbrandsen, diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 72ce26b6c4d3..4ad16b6d5138 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -1,8 +1,6 @@ /* * UDPLITE An implementation of the UDP-Lite protocol (RFC 3828). * - * Version: $Id: udplite.c,v 1.25 2006/10/19 07:22:36 gerrit Exp $ - * * Authors: Gerrit Renker * * Changes: diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 147588f4c7c0..deb38bf03376 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6,8 +6,6 @@ * Pedro Roque * Alexey Kuznetsov * - * $Id: addrconf.c,v 1.69 2001/10/31 21:55:54 davem Exp $ - * * 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 diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e84b3fd17fb4..350457c761e6 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,8 +7,6 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.66 2002/02/01 22:01:04 davem Exp $ - * * Fixes: * piggy, Karl Knutson : Socket protocol table * Hideaki YOSHIFUJI : sin6_scope_id support diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index b9c2de84a8a2..8cdb6b65ee96 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: datagram.c,v 1.24 2002/02/01 22:01:04 davem Exp $ - * * 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 diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 3cd1c993d52b..602ea826f0a5 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -7,8 +7,6 @@ * Andi Kleen * Alexey Kuznetsov * - * $Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $ - * * 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 diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index d42dd16d3487..399d41f65437 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: icmp.c,v 1.38 2002/02/08 03:57:19 davem Exp $ - * * Based on net/ipv4/icmp.c * * RFC 1885 diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 1ee4fa17c129..4de2b9efcacb 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: ip6_fib.c,v 1.25 2001/10/31 21:55:55 davem Exp $ - * * 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 diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 4e5c8615832c..f77a6011c302 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -6,8 +6,6 @@ * Pedro Roque * Ian P. Morris * - * $Id: ip6_input.c,v 1.19 2000/12/13 18:31:50 davem Exp $ - * * Based in linux/net/ipv4/ip_input.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 48cdce9c696c..40a2813a63d1 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: ip6_output.c,v 1.34 2002/02/01 22:01:04 davem Exp $ - * * Based on linux/net/ipv4/ip_output.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 37814810ac49..17c7b098cdb0 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -6,8 +6,6 @@ * Ville Nuorvala * Yasuyuki Kozakai * - * $Id$ - * * Based on: * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c * diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 26b83e512a09..237ebbb9383b 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,8 +7,6 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.41 2002/02/01 22:01:04 davem Exp $ - * * 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 diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index fd632dd7f98d..fbb2d12c41bc 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $ - * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index df0736a4cafa..cbc7e514d3ec 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -7,8 +7,6 @@ * PROC file system. This is very similar to the IPv4 version, * except it reports the sockets in the INET6 address family. * - * Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $ - * * Authors: David S. Miller (davem@caip.rutgers.edu) * YOSHIFUJI Hideaki * diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index f929f47b925e..9ab789159913 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -5,8 +5,6 @@ * * PF_INET6 protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.10 2001/05/18 02:25:49 davem Exp $ - * * Authors: Pedro Roque * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 8fee9a15b2d3..e03c1898ab2e 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,8 +7,6 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.51 2002/02/01 22:01:04 davem Exp $ - * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 9391a6949b96..13509f906d89 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: reassembly.c,v 1.26 2001/03/07 22:00:57 davem Exp $ - * * Based on: net/ipv4/ip_fragment.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 220cffe9e63b..edae81319b51 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: route.c,v 1.56 2001/10/31 21:55:55 davem Exp $ - * * 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 diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 6b8f0583b637..b0c5080420a8 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -6,8 +6,6 @@ * Pedro Roque * Alexey Kuznetsov * - * $Id: sit.c,v 1.53 2001/09/25 05:09:53 davem Exp $ - * * 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 diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 715965f0fac0..155499197fc5 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.144 2002/02/01 22:01:04 davem Exp $ - * * Based on: * linux/net/ipv4/tcp.c * linux/net/ipv4/tcp_input.c diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index dd309626ae9a..e0693fffc9bd 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,8 +7,6 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.65 2002/02/01 22:01:04 davem Exp $ - * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 491efd00a866..f6cdcb348e05 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -2,8 +2,6 @@ * UDPLITEv6 An implementation of the UDP-Lite protocol over IPv6. * See also net/ipv4/udplite.c * - * Version: $Id: udplite.c,v 1.9 2006/10/19 08:28:10 gerrit Exp $ - * * Authors: Gerrit Renker * * Changes: diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 2cee87da4441..beca6402f1cf 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -5,8 +5,6 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.61 2002/02/08 03:57:19 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Alan Cox, diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 5bc1ed490180..213071859030 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -24,8 +24,6 @@ * Jiri Fojtasek * fixed requeue routine * and many others. thanks. - * - * $Id: sch_htb.c,v 1.25 2003/12/07 11:08:25 devik Exp devik $ */ #include #include diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index cc12d5f5d5da..019d4b4478c9 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -33,8 +33,6 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ */ diff --git a/net/sysctl_net.c b/net/sysctl_net.c index d8e79162724c..007c1a6708ee 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -4,7 +4,6 @@ * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net directories for each protocol family. [MS] * - * $Log: sysctl_net.c,v $ * Revision 1.2 1996/05/08 20:24:40 shaver * Added bits for NET_BRIDGE and the NET_IPV4_ARP stuff and * NET_IPV4_IP_FORWARD. diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e18cd3628db4..392e80e3268d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,8 +8,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.133 2002/02/08 03:57:19 davem Exp $ - * * Fixes: * Linus Torvalds : Assorted bug cures. * Niibe Yutaka : async I/O support. -- cgit v1.2.3 From fc32b0e28df6655a15b488aaddfc1339f82dc13a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 2 Jun 2008 00:28:40 +0200 Subject: mv643xx_eth: general cleanup General cleanup of the mv643xx_eth driver. Mainly fixes coding style / indentation issues, get rid of some useless 'volatile's, kill some more superfluous comments, and such. Signed-off-by: Lennert Buytenhek Acked-by: Dale Farnsworth --- drivers/net/mv643xx_eth.c | 949 ++++++++++++++++++++------------------------ include/linux/mv643xx_eth.h | 59 ++- 2 files changed, 479 insertions(+), 529 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index ff6460124307..cf18419f96ab 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -67,8 +67,6 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define MAX_DESCS_PER_SKB 1 #endif -#define ETH_HW_IP_ALIGN 2 - /* * Registers shared between all ports. */ @@ -158,12 +156,6 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define DEFAULT_RX_QUEUE_SIZE 400 #define DEFAULT_TX_QUEUE_SIZE 800 -/* SMI reg */ -#define SMI_BUSY 0x10000000 /* 0 - Write, 1 - Read */ -#define SMI_READ_VALID 0x08000000 /* 0 - Write, 1 - Read */ -#define SMI_OPCODE_WRITE 0 /* Completion of Read */ -#define SMI_OPCODE_READ 0x04000000 /* Operation is in progress */ - /* * RX/TX descriptors. @@ -231,13 +223,24 @@ struct tx_desc { /* global *******************************************************************/ struct mv643xx_eth_shared_private { + /* + * Ethernet controller base address. + */ void __iomem *base; - /* used to protect SMI_REG, which is shared across ports */ + /* + * Protects access to SMI_REG, which is shared between ports. + */ spinlock_t phy_lock; + /* + * Per-port MBUS window access register value. + */ u32 win_protect; + /* + * Hardware-specific parameters. + */ unsigned int t_clk; }; @@ -306,16 +309,17 @@ struct tx_queue { struct mv643xx_eth_private { struct mv643xx_eth_shared_private *shared; - int port_num; /* User Ethernet port number */ + int port_num; - struct mv643xx_eth_shared_private *shared_smi; + struct net_device *dev; - struct work_struct tx_timeout_task; + struct mv643xx_eth_shared_private *shared_smi; + int phy_addr; - struct net_device *dev; - struct mib_counters mib_counters; spinlock_t lock; + struct mib_counters mib_counters; + struct work_struct tx_timeout_task; struct mii_if_info mii; /* @@ -450,7 +454,12 @@ static void rxq_refill(struct rx_queue *rxq) RX_ENABLE_INTERRUPT; wmb(); - skb_reserve(skb, ETH_HW_IP_ALIGN); + /* + * The hardware automatically prepends 2 bytes of + * dummy data to each received packet, so that the + * IP header ends up 16-byte aligned. + */ + skb_reserve(skb, 2); } if (rxq->rx_desc_count == 0) { @@ -474,9 +483,9 @@ static int rxq_process(struct rx_queue *rxq, int budget) rx = 0; while (rx < budget) { - struct sk_buff *skb; - volatile struct rx_desc *rx_desc; + struct rx_desc *rx_desc; unsigned int cmd_sts; + struct sk_buff *skb; unsigned long flags; spin_lock_irqsave(&mp->lock, flags); @@ -497,34 +506,40 @@ static int rxq_process(struct rx_queue *rxq, int budget) spin_unlock_irqrestore(&mp->lock, flags); - dma_unmap_single(NULL, rx_desc->buf_ptr + ETH_HW_IP_ALIGN, - mp->dev->mtu + 24, DMA_FROM_DEVICE); + dma_unmap_single(NULL, rx_desc->buf_ptr + 2, + mp->dev->mtu + 24, DMA_FROM_DEVICE); rxq->rx_desc_count--; rx++; /* * Update statistics. - * Note byte count includes 4 byte CRC count + * + * Note that the descriptor byte count includes 2 dummy + * bytes automatically inserted by the hardware at the + * start of the packet (which we don't count), and a 4 + * byte CRC at the end of the packet (which we do count). */ stats->rx_packets++; - stats->rx_bytes += rx_desc->byte_cnt - ETH_HW_IP_ALIGN; + stats->rx_bytes += rx_desc->byte_cnt - 2; /* - * In case received a packet without first / last bits on OR - * the error summary bit is on, the packets needs to be dropeed. + * In case we received a packet without first / last bits + * on, or the error summary bit is set, the packet needs + * to be dropped. */ if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != (RX_FIRST_DESC | RX_LAST_DESC)) || (cmd_sts & ERROR_SUMMARY)) { stats->rx_dropped++; + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != (RX_FIRST_DESC | RX_LAST_DESC)) { if (net_ratelimit()) - printk(KERN_ERR - "%s: Received packet spread " - "on multiple descriptors\n", - mp->dev->name); + dev_printk(KERN_ERR, &mp->dev->dev, + "received packet spanning " + "multiple descriptors\n"); } + if (cmd_sts & ERROR_SUMMARY) stats->rx_errors++; @@ -534,7 +549,7 @@ static int rxq_process(struct rx_queue *rxq, int budget) * The -4 is for the CRC in the trailer of the * received packet */ - skb_put(skb, rx_desc->byte_cnt - ETH_HW_IP_ALIGN - 4); + skb_put(skb, rx_desc->byte_cnt - 2 - 4); if (cmd_sts & LAYER_4_CHECKSUM_OK) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -548,8 +563,10 @@ static int rxq_process(struct rx_queue *rxq, int budget) netif_rx(skb); #endif } + mp->dev->last_rx = jiffies; } + rxq_refill(rxq); return rx; @@ -716,7 +733,7 @@ static void txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) txq->tx_desc_count += nr_frags + 1; } -static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) +static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; @@ -727,8 +744,9 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { stats->tx_dropped++; - printk(KERN_DEBUG "%s: failed to linearize tiny " - "unaligned fragment\n", dev->name); + dev_printk(KERN_DEBUG, &dev->dev, + "failed to linearize skb with tiny " + "unaligned fragment\n"); return NETDEV_TX_BUSY; } @@ -758,13 +776,15 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) /* mii management interface *************************************************/ -static int phy_addr_get(struct mv643xx_eth_private *mp); +#define SMI_BUSY 0x10000000 +#define SMI_READ_VALID 0x08000000 +#define SMI_OPCODE_READ 0x04000000 +#define SMI_OPCODE_WRITE 0x00000000 -static void read_smi_reg(struct mv643xx_eth_private *mp, - unsigned int phy_reg, unsigned int *value) +static void smi_reg_read(struct mv643xx_eth_private *mp, unsigned int addr, + unsigned int reg, unsigned int *value) { void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; - int phy_addr = phy_addr_get(mp); unsigned long flags; int i; @@ -780,7 +800,7 @@ static void read_smi_reg(struct mv643xx_eth_private *mp, udelay(10); } - writel((phy_addr << 16) | (phy_reg << 21) | SMI_OPCODE_READ, smi_reg); + writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg); /* now wait for the data to be valid */ for (i = 0; !(readl(smi_reg) & SMI_READ_VALID); i++) { @@ -796,11 +816,11 @@ out: spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); } -static void write_smi_reg(struct mv643xx_eth_private *mp, - unsigned int phy_reg, unsigned int value) +static void smi_reg_write(struct mv643xx_eth_private *mp, + unsigned int addr, + unsigned int reg, unsigned int value) { void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; - int phy_addr = phy_addr_get(mp); unsigned long flags; int i; @@ -816,65 +836,63 @@ static void write_smi_reg(struct mv643xx_eth_private *mp, udelay(10); } - writel((phy_addr << 16) | (phy_reg << 21) | - SMI_OPCODE_WRITE | (value & 0xffff), smi_reg); + writel(SMI_OPCODE_WRITE | (reg << 21) | + (addr << 16) | (value & 0xffff), smi_reg); out: spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); } /* mib counters *************************************************************/ -static void clear_mib_counters(struct mv643xx_eth_private *mp) +static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset) { - unsigned int port_num = mp->port_num; - int i; - - /* Perform dummy reads from MIB counters */ - for (i = 0; i < 0x80; i += 4) - rdl(mp, MIB_COUNTERS(port_num) + i); + return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); } -static inline u32 read_mib(struct mv643xx_eth_private *mp, int offset) +static void mib_counters_clear(struct mv643xx_eth_private *mp) { - return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); + int i; + + for (i = 0; i < 0x80; i += 4) + mib_read(mp, i); } -static void update_mib_counters(struct mv643xx_eth_private *mp) +static void mib_counters_update(struct mv643xx_eth_private *mp) { struct mib_counters *p = &mp->mib_counters; - p->good_octets_received += read_mib(mp, 0x00); - p->good_octets_received += (u64)read_mib(mp, 0x04) << 32; - p->bad_octets_received += read_mib(mp, 0x08); - p->internal_mac_transmit_err += read_mib(mp, 0x0c); - p->good_frames_received += read_mib(mp, 0x10); - p->bad_frames_received += read_mib(mp, 0x14); - p->broadcast_frames_received += read_mib(mp, 0x18); - p->multicast_frames_received += read_mib(mp, 0x1c); - p->frames_64_octets += read_mib(mp, 0x20); - p->frames_65_to_127_octets += read_mib(mp, 0x24); - p->frames_128_to_255_octets += read_mib(mp, 0x28); - p->frames_256_to_511_octets += read_mib(mp, 0x2c); - p->frames_512_to_1023_octets += read_mib(mp, 0x30); - p->frames_1024_to_max_octets += read_mib(mp, 0x34); - p->good_octets_sent += read_mib(mp, 0x38); - p->good_octets_sent += (u64)read_mib(mp, 0x3c) << 32; - p->good_frames_sent += read_mib(mp, 0x40); - p->excessive_collision += read_mib(mp, 0x44); - p->multicast_frames_sent += read_mib(mp, 0x48); - p->broadcast_frames_sent += read_mib(mp, 0x4c); - p->unrec_mac_control_received += read_mib(mp, 0x50); - p->fc_sent += read_mib(mp, 0x54); - p->good_fc_received += read_mib(mp, 0x58); - p->bad_fc_received += read_mib(mp, 0x5c); - p->undersize_received += read_mib(mp, 0x60); - p->fragments_received += read_mib(mp, 0x64); - p->oversize_received += read_mib(mp, 0x68); - p->jabber_received += read_mib(mp, 0x6c); - p->mac_receive_error += read_mib(mp, 0x70); - p->bad_crc_event += read_mib(mp, 0x74); - p->collision += read_mib(mp, 0x78); - p->late_collision += read_mib(mp, 0x7c); + p->good_octets_received += mib_read(mp, 0x00); + p->good_octets_received += (u64)mib_read(mp, 0x04) << 32; + p->bad_octets_received += mib_read(mp, 0x08); + p->internal_mac_transmit_err += mib_read(mp, 0x0c); + p->good_frames_received += mib_read(mp, 0x10); + p->bad_frames_received += mib_read(mp, 0x14); + p->broadcast_frames_received += mib_read(mp, 0x18); + p->multicast_frames_received += mib_read(mp, 0x1c); + p->frames_64_octets += mib_read(mp, 0x20); + p->frames_65_to_127_octets += mib_read(mp, 0x24); + p->frames_128_to_255_octets += mib_read(mp, 0x28); + p->frames_256_to_511_octets += mib_read(mp, 0x2c); + p->frames_512_to_1023_octets += mib_read(mp, 0x30); + p->frames_1024_to_max_octets += mib_read(mp, 0x34); + p->good_octets_sent += mib_read(mp, 0x38); + p->good_octets_sent += (u64)mib_read(mp, 0x3c) << 32; + p->good_frames_sent += mib_read(mp, 0x40); + p->excessive_collision += mib_read(mp, 0x44); + p->multicast_frames_sent += mib_read(mp, 0x48); + p->broadcast_frames_sent += mib_read(mp, 0x4c); + p->unrec_mac_control_received += mib_read(mp, 0x50); + p->fc_sent += mib_read(mp, 0x54); + p->good_fc_received += mib_read(mp, 0x58); + p->bad_fc_received += mib_read(mp, 0x5c); + p->undersize_received += mib_read(mp, 0x60); + p->fragments_received += mib_read(mp, 0x64); + p->oversize_received += mib_read(mp, 0x68); + p->jabber_received += mib_read(mp, 0x6c); + p->mac_receive_error += mib_read(mp, 0x70); + p->bad_crc_event += mib_read(mp, 0x74); + p->collision += mib_read(mp, 0x78); + p->late_collision += mib_read(mp, 0x7c); } @@ -944,7 +962,9 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd * err = mii_ethtool_gset(&mp->mii, cmd); spin_unlock_irq(&mp->lock); - /* The PHY may support 1000baseT_Half, but the mv643xx does not */ + /* + * The MAC does not support 1000baseT_Half. + */ cmd->supported &= ~SUPPORTED_1000baseT_Half; cmd->advertising &= ~ADVERTISED_1000baseT_Half; @@ -956,6 +976,11 @@ static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd * struct mv643xx_eth_private *mp = netdev_priv(dev); int err; + /* + * The MAC does not support 1000baseT_Half. + */ + cmd->advertising &= ~ADVERTISED_1000baseT_Half; + spin_lock_irq(&mp->lock); err = mii_ethtool_sset(&mp->mii, cmd); spin_unlock_irq(&mp->lock); @@ -963,17 +988,17 @@ static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd * return err; } -static void mv643xx_eth_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) +static void mv643xx_eth_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) { strncpy(drvinfo->driver, mv643xx_eth_driver_name, 32); strncpy(drvinfo->version, mv643xx_eth_driver_version, 32); strncpy(drvinfo->fw_version, "N/A", 32); - strncpy(drvinfo->bus_info, "mv643xx", 32); + strncpy(drvinfo->bus_info, "platform", 32); drvinfo->n_stats = ARRAY_SIZE(mv643xx_eth_stats); } -static int mv643xx_eth_nway_restart(struct net_device *dev) +static int mv643xx_eth_nway_reset(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); @@ -987,29 +1012,28 @@ static u32 mv643xx_eth_get_link(struct net_device *dev) return mii_link_ok(&mp->mii); } -static void mv643xx_eth_get_strings(struct net_device *netdev, uint32_t stringset, - uint8_t *data) +static void mv643xx_eth_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *data) { int i; - switch(stringset) { - case ETH_SS_STATS: - for (i=0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { + if (stringset == ETH_SS_STATS) { + for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { memcpy(data + i * ETH_GSTRING_LEN, mv643xx_eth_stats[i].stat_string, ETH_GSTRING_LEN); } - break; } } -static void mv643xx_eth_get_ethtool_stats(struct net_device *netdev, - struct ethtool_stats *stats, uint64_t *data) +static void mv643xx_eth_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, + uint64_t *data) { - struct mv643xx_eth_private *mp = netdev->priv; + struct mv643xx_eth_private *mp = dev->priv; int i; - update_mib_counters(mp); + mib_counters_update(mp); for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { const struct mv643xx_eth_stats *stat; @@ -1027,38 +1051,35 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *netdev, } } -static int mv643xx_eth_get_sset_count(struct net_device *netdev, int sset) +static int mv643xx_eth_get_sset_count(struct net_device *dev, int sset) { - switch (sset) { - case ETH_SS_STATS: + if (sset == ETH_SS_STATS) return ARRAY_SIZE(mv643xx_eth_stats); - default: - return -EOPNOTSUPP; - } + + return -EOPNOTSUPP; } static const struct ethtool_ops mv643xx_eth_ethtool_ops = { - .get_settings = mv643xx_eth_get_settings, - .set_settings = mv643xx_eth_set_settings, - .get_drvinfo = mv643xx_eth_get_drvinfo, - .get_link = mv643xx_eth_get_link, + .get_settings = mv643xx_eth_get_settings, + .set_settings = mv643xx_eth_set_settings, + .get_drvinfo = mv643xx_eth_get_drvinfo, + .nway_reset = mv643xx_eth_nway_reset, + .get_link = mv643xx_eth_get_link, .set_sg = ethtool_op_set_sg, + .get_strings = mv643xx_eth_get_strings, + .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, .get_sset_count = mv643xx_eth_get_sset_count, - .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, - .get_strings = mv643xx_eth_get_strings, - .nway_reset = mv643xx_eth_nway_restart, }; /* address handling *********************************************************/ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) { - unsigned int port_num = mp->port_num; unsigned int mac_h; unsigned int mac_l; - mac_h = rdl(mp, MAC_ADDR_HIGH(port_num)); - mac_l = rdl(mp, MAC_ADDR_LOW(port_num)); + mac_h = rdl(mp, MAC_ADDR_HIGH(mp->port_num)); + mac_l = rdl(mp, MAC_ADDR_LOW(mp->port_num)); addr[0] = (mac_h >> 24) & 0xff; addr[1] = (mac_h >> 16) & 0xff; @@ -1070,72 +1091,54 @@ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) static void init_mac_tables(struct mv643xx_eth_private *mp) { - unsigned int port_num = mp->port_num; - int table_index; - - /* Clear DA filter unicast table (Ex_dFUT) */ - for (table_index = 0; table_index <= 0xC; table_index += 4) - wrl(mp, UNICAST_TABLE(port_num) + table_index, 0); + int i; - for (table_index = 0; table_index <= 0xFC; table_index += 4) { - /* Clear DA filter special multicast table (Ex_dFSMT) */ - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + table_index, 0); - /* Clear DA filter other multicast table (Ex_dFOMT) */ - wrl(mp, OTHER_MCAST_TABLE(port_num) + table_index, 0); + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); } + + for (i = 0; i < 0x10; i += 4) + wrl(mp, UNICAST_TABLE(mp->port_num) + i, 0); } static void set_filter_table_entry(struct mv643xx_eth_private *mp, - int table, unsigned char entry) + int table, unsigned char entry) { unsigned int table_reg; - unsigned int tbl_offset; - unsigned int reg_offset; - - tbl_offset = (entry / 4) * 4; /* Register offset of DA table entry */ - reg_offset = entry % 4; /* Entry offset within the register */ /* Set "accepts frame bit" at specified table entry */ - table_reg = rdl(mp, table + tbl_offset); - table_reg |= 0x01 << (8 * reg_offset); - wrl(mp, table + tbl_offset, table_reg); + table_reg = rdl(mp, table + (entry & 0xfc)); + table_reg |= 0x01 << (8 * (entry & 3)); + wrl(mp, table + (entry & 0xfc), table_reg); } static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr) { - unsigned int port_num = mp->port_num; unsigned int mac_h; unsigned int mac_l; int table; - mac_l = (addr[4] << 8) | (addr[5]); - mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | - (addr[3] << 0); + mac_l = (addr[4] << 8) | addr[5]; + mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; - wrl(mp, MAC_ADDR_LOW(port_num), mac_l); - wrl(mp, MAC_ADDR_HIGH(port_num), mac_h); + wrl(mp, MAC_ADDR_LOW(mp->port_num), mac_l); + wrl(mp, MAC_ADDR_HIGH(mp->port_num), mac_h); - /* Accept frames with this address */ - table = UNICAST_TABLE(port_num); + table = UNICAST_TABLE(mp->port_num); set_filter_table_entry(mp, table, addr[5] & 0x0f); } -static void mv643xx_eth_update_mac_address(struct net_device *dev) +static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) { struct mv643xx_eth_private *mp = netdev_priv(dev); + /* +2 is for the offset of the HW addr type */ + memcpy(dev->dev_addr, addr + 2, 6); + init_mac_tables(mp); uc_addr_set(mp, dev->dev_addr); -} - -static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) -{ - int i; - for (i = 0; i < 6; i++) - /* +2 is for the offset of the HW addr type */ - dev->dev_addr[i] = ((unsigned char *)addr)[i + 2]; - mv643xx_eth_update_mac_address(dev); return 0; } @@ -1157,95 +1160,53 @@ static int addr_crc(unsigned char *addr) return crc; } -static void mc_addr(struct mv643xx_eth_private *mp, unsigned char *addr) +static void mv643xx_eth_set_rx_mode(struct net_device *dev) { - unsigned int port_num = mp->port_num; - int table; - int crc; - - if ((addr[0] == 0x01) && (addr[1] == 0x00) && - (addr[2] == 0x5E) && (addr[3] == 0x00) && (addr[4] == 0x00)) { - table = SPECIAL_MCAST_TABLE(port_num); - set_filter_table_entry(mp, table, addr[5]); - return; - } - - crc = addr_crc(addr); - - table = OTHER_MCAST_TABLE(port_num); - set_filter_table_entry(mp, table, crc); -} + struct mv643xx_eth_private *mp = netdev_priv(dev); + u32 port_config; + struct dev_addr_list *addr; + int i; -static void set_multicast_list(struct net_device *dev) -{ + port_config = rdl(mp, PORT_CONFIG(mp->port_num)); + if (dev->flags & IFF_PROMISC) + port_config |= UNICAST_PROMISCUOUS_MODE; + else + port_config &= ~UNICAST_PROMISCUOUS_MODE; + wrl(mp, PORT_CONFIG(mp->port_num), port_config); - struct dev_mc_list *mc_list; - int i; - int table_index; - struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; + if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + int port_num = mp->port_num; + u32 accept = 0x01010101; - /* If the device is in promiscuous mode or in all multicast mode, - * we will fully populate both multicast tables with accept. - * This is guaranteed to yield a match on all multicast addresses... - */ - if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI)) { - for (table_index = 0; table_index <= 0xFC; table_index += 4) { - /* Set all entries in DA filter special multicast - * table (Ex_dFSMT) - * Set for ETH_Q0 for now - * Bits - * 0 Accept=1, Drop=0 - * 3-1 Queue ETH_Q0=0 - * 7-4 Reserved = 0; - */ - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + table_index, 0x01010101); - - /* Set all entries in DA filter other multicast - * table (Ex_dFOMT) - * Set for ETH_Q0 for now - * Bits - * 0 Accept=1, Drop=0 - * 3-1 Queue ETH_Q0=0 - * 7-4 Reserved = 0; - */ - wrl(mp, OTHER_MCAST_TABLE(port_num) + table_index, 0x01010101); + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept); + wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept); } return; } - /* We will clear out multicast tables every time we get the list. - * Then add the entire new list... - */ - for (table_index = 0; table_index <= 0xFC; table_index += 4) { - /* Clear DA filter special multicast table (Ex_dFSMT) */ - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + table_index, 0); - - /* Clear DA filter other multicast table (Ex_dFOMT) */ - wrl(mp, OTHER_MCAST_TABLE(port_num) + table_index, 0); + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); } - /* Get pointer to net_device multicast list and add each one... */ - for (i = 0, mc_list = dev->mc_list; - (i < 256) && (mc_list != NULL) && (i < dev->mc_count); - i++, mc_list = mc_list->next) - if (mc_list->dmi_addrlen == 6) - mc_addr(mp, mc_list->dmi_addr); -} + for (addr = dev->mc_list; addr != NULL; addr = addr->next) { + u8 *a = addr->da_addr; + int table; -static void mv643xx_eth_set_rx_mode(struct net_device *dev) -{ - struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 config_reg; + if (addr->da_addrlen != 6) + continue; - config_reg = rdl(mp, PORT_CONFIG(mp->port_num)); - if (dev->flags & IFF_PROMISC) - config_reg |= UNICAST_PROMISCUOUS_MODE; - else - config_reg &= ~UNICAST_PROMISCUOUS_MODE; - wrl(mp, PORT_CONFIG(mp->port_num), config_reg); + if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) { + table = SPECIAL_MCAST_TABLE(mp->port_num); + set_filter_table_entry(mp, table, a[5]); + } else { + int crc = addr_crc(a); - set_multicast_list(dev); + table = OTHER_MCAST_TABLE(mp->port_num); + set_filter_table_entry(mp, table, crc); + } + } } @@ -1483,10 +1444,7 @@ static void txq_deinit(struct tx_queue *txq) /* netdev ops and related ***************************************************/ -static void port_reset(struct mv643xx_eth_private *mp); - -static void mv643xx_eth_update_pscr(struct mv643xx_eth_private *mp, - struct ethtool_cmd *ecmd) +static void update_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) { u32 pscr_o; u32 pscr_n; @@ -1499,15 +1457,15 @@ static void mv643xx_eth_update_pscr(struct mv643xx_eth_private *mp, SET_FULL_DUPLEX_MODE | MAX_RX_PACKET_MASK); - if (ecmd->speed == SPEED_1000) { + if (speed == SPEED_1000) { pscr_n |= SET_GMII_SPEED_TO_1000 | MAX_RX_PACKET_9700BYTE; } else { - if (ecmd->speed == SPEED_100) + if (speed == SPEED_100) pscr_n |= SET_MII_SPEED_TO_100; pscr_n |= MAX_RX_PACKET_1522BYTE; } - if (ecmd->duplex == DUPLEX_FULL) + if (duplex == DUPLEX_FULL) pscr_n |= SET_FULL_DUPLEX_MODE; if (pscr_n != pscr_o) { @@ -1524,27 +1482,30 @@ static void mv643xx_eth_update_pscr(struct mv643xx_eth_private *mp, } } -static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id) +static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 int_cause, int_cause_ext = 0; + u32 int_cause; + u32 int_cause_ext; - /* Read interrupt cause registers */ int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & (INT_RX | INT_EXT); + if (int_cause == 0) + return IRQ_NONE; + + int_cause_ext = 0; if (int_cause & INT_EXT) { int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)) & (INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext); } - /* PHY status changed */ - if (int_cause_ext & (INT_EXT_LINK | INT_EXT_PHY)) { + if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) { if (mii_link_ok(&mp->mii)) { struct ethtool_cmd cmd; mii_ethtool_gset(&mp->mii, &cmd); - mv643xx_eth_update_pscr(mp, &cmd); + update_pscr(mp, cmd.speed, cmd.duplex); txq_enable(mp->txq); if (!netif_carrier_ok(dev)) { netif_carrier_on(dev); @@ -1558,10 +1519,7 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id) #ifdef MV643XX_ETH_NAPI if (int_cause & INT_RX) { - /* schedule the NAPI poll routine to maintain port */ wrl(mp, INT_MASK(mp->port_num), 0x00000000); - - /* wait for previous write to complete */ rdl(mp, INT_MASK(mp->port_num)); netif_rx_schedule(dev, &mp->napi); @@ -1570,40 +1528,31 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id) if (int_cause & INT_RX) rxq_process(mp->rxq, INT_MAX); #endif + if (int_cause_ext & INT_EXT_TX) { txq_reclaim(mp->txq, 0); __txq_maybe_wake(mp->txq); } - /* - * If no real interrupt occured, exit. - * This can happen when using gigE interrupt coalescing mechanism. - */ - if ((int_cause == 0x0) && (int_cause_ext == 0x0)) - return IRQ_NONE; - return IRQ_HANDLED; } static void phy_reset(struct mv643xx_eth_private *mp) { - unsigned int phy_reg_data; + unsigned int data; - /* Reset the PHY */ - read_smi_reg(mp, 0, &phy_reg_data); - phy_reg_data |= 0x8000; /* Set bit 15 to reset the PHY */ - write_smi_reg(mp, 0, phy_reg_data); + smi_reg_read(mp, mp->phy_addr, 0, &data); + data |= 0x8000; + smi_reg_write(mp, mp->phy_addr, 0, data); - /* wait for PHY to come out of reset */ do { udelay(1); - read_smi_reg(mp, 0, &phy_reg_data); - } while (phy_reg_data & 0x8000); + smi_reg_read(mp, mp->phy_addr, 0, &data); + } while (data & 0x8000); } -static void port_start(struct net_device *dev) +static void port_start(struct mv643xx_eth_private *mp) { - struct mv643xx_eth_private *mp = netdev_priv(dev); u32 pscr; struct ethtool_cmd ethtool_cmd; int i; @@ -1625,9 +1574,9 @@ static void port_start(struct net_device *dev) wrl(mp, SDMA_CONFIG(mp->port_num), PORT_SDMA_CONFIG_DEFAULT_VALUE); - mv643xx_eth_get_settings(dev, ðtool_cmd); + mv643xx_eth_get_settings(mp->dev, ðtool_cmd); phy_reset(mp); - mv643xx_eth_set_settings(dev, ðtool_cmd); + mv643xx_eth_set_settings(mp->dev, ðtool_cmd); /* * Configure TX path and queues. @@ -1643,8 +1592,10 @@ static void port_start(struct net_device *dev) wrl(mp, off, addr); } - /* Add the assigned Ethernet address to the port's address table */ - uc_addr_set(mp, dev->dev_addr); + /* + * Add configured unicast address to address filter table. + */ + uc_addr_set(mp, mp->dev->dev_addr); /* * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast @@ -1675,13 +1626,14 @@ static void port_start(struct net_device *dev) static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int delay) { - unsigned int port_num = mp->port_num; unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64; - /* Set RX Coalescing mechanism */ - wrl(mp, SDMA_CONFIG(port_num), + if (coal > 0x3fff) + coal = 0x3fff; + + wrl(mp, SDMA_CONFIG(mp->port_num), ((coal & 0x3fff) << 8) | - (rdl(mp, SDMA_CONFIG(port_num)) + (rdl(mp, SDMA_CONFIG(mp->port_num)) & 0xffc000ff)); } @@ -1689,68 +1641,59 @@ static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay) { unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64; - /* Set TX Coalescing mechanism */ - wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), coal << 4); -} - -static void port_init(struct mv643xx_eth_private *mp) -{ - port_reset(mp); - - init_mac_tables(mp); + if (coal > 0x3fff) + coal = 0x3fff; + wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4); } static int mv643xx_eth_open(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; int err; - /* Clear any pending ethernet port interrupts */ - wrl(mp, INT_CAUSE(port_num), 0); - wrl(mp, INT_CAUSE_EXT(port_num), 0); - /* wait for previous write to complete */ - rdl(mp, INT_CAUSE_EXT(port_num)); + wrl(mp, INT_CAUSE(mp->port_num), 0); + wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); + rdl(mp, INT_CAUSE_EXT(mp->port_num)); - err = request_irq(dev->irq, mv643xx_eth_int_handler, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev); + err = request_irq(dev->irq, mv643xx_eth_irq, + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + dev->name, dev); if (err) { - printk(KERN_ERR "%s: Can not assign IRQ\n", dev->name); + dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n"); return -EAGAIN; } - port_init(mp); + init_mac_tables(mp); err = rxq_init(mp); if (err) - goto out_free_irq; + goto out; rxq_refill(mp->rxq); err = txq_init(mp); if (err) - goto out_free_rx_skb; + goto out_free; #ifdef MV643XX_ETH_NAPI napi_enable(&mp->napi); #endif - port_start(dev); + port_start(mp); set_rx_coal(mp, 0); set_tx_coal(mp, 0); - /* Unmask phy and link status changes interrupts */ - wrl(mp, INT_MASK_EXT(port_num), INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); + wrl(mp, INT_MASK_EXT(mp->port_num), + INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); - /* Unmask RX buffer and TX end interrupt */ - wrl(mp, INT_MASK(port_num), INT_RX | INT_EXT); + wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_EXT); return 0; -out_free_rx_skb: +out_free: rxq_deinit(mp->rxq); -out_free_irq: +out: free_irq(dev->irq, dev); return err; @@ -1758,34 +1701,27 @@ out_free_irq: static void port_reset(struct mv643xx_eth_private *mp) { - unsigned int port_num = mp->port_num; - unsigned int reg_data; + unsigned int data; txq_disable(mp->txq); rxq_disable(mp->rxq); while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) udelay(10); - /* Clear all MIB counters */ - clear_mib_counters(mp); - /* Reset the Enable bit in the Configuration Register */ - reg_data = rdl(mp, PORT_SERIAL_CONTROL(port_num)); - reg_data &= ~(SERIAL_PORT_ENABLE | - DO_NOT_FORCE_LINK_FAIL | - FORCE_LINK_PASS); - wrl(mp, PORT_SERIAL_CONTROL(port_num), reg_data); + data = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + data &= ~(SERIAL_PORT_ENABLE | + DO_NOT_FORCE_LINK_FAIL | + FORCE_LINK_PASS); + wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), data); } static int mv643xx_eth_stop(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; - /* Mask all interrupts on ethernet port */ - wrl(mp, INT_MASK(port_num), 0x00000000); - /* wait for previous write to complete */ - rdl(mp, INT_MASK(port_num)); + wrl(mp, INT_MASK(mp->port_num), 0x00000000); + rdl(mp, INT_MASK(mp->port_num)); #ifdef MV643XX_ETH_NAPI napi_disable(&mp->napi); @@ -1793,17 +1729,18 @@ static int mv643xx_eth_stop(struct net_device *dev) netif_carrier_off(dev); netif_stop_queue(dev); + free_irq(dev->irq, dev); + port_reset(mp); + mib_counters_update(mp); txq_deinit(mp->txq); rxq_deinit(mp->rxq); - free_irq(dev->irq, dev); - return 0; } -static int mv643xx_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); @@ -1812,7 +1749,7 @@ static int mv643xx_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int c static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu > 9500) || (new_mtu < 64)) + if (new_mtu < 64 || new_mtu > 9500) return -EINVAL; dev->mtu = new_mtu; @@ -1823,73 +1760,70 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) * Stop and then re-open the interface. This will allocate RX * skbs of the new MTU. * There is a possible danger that the open will not succeed, - * due to memory being full, which might fail the open function. + * due to memory being full. */ mv643xx_eth_stop(dev); if (mv643xx_eth_open(dev)) { - printk(KERN_ERR "%s: Fatal error on opening device\n", - dev->name); + dev_printk(KERN_ERR, &dev->dev, + "fatal error on re-opening device after " + "MTU change\n"); } return 0; } -static void mv643xx_eth_tx_timeout_task(struct work_struct *ugly) +static void tx_timeout_task(struct work_struct *ugly) { - struct mv643xx_eth_private *mp = container_of(ugly, struct mv643xx_eth_private, - tx_timeout_task); - struct net_device *dev = mp->dev; - - if (!netif_running(dev)) - return; + struct mv643xx_eth_private *mp; - netif_stop_queue(dev); + mp = container_of(ugly, struct mv643xx_eth_private, tx_timeout_task); + if (netif_running(mp->dev)) { + netif_stop_queue(mp->dev); - port_reset(mp); - port_start(dev); + port_reset(mp); + port_start(mp); - __txq_maybe_wake(mp->txq); + __txq_maybe_wake(mp->txq); + } } static void mv643xx_eth_tx_timeout(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - printk(KERN_INFO "%s: TX timeout ", dev->name); + dev_printk(KERN_INFO, &dev->dev, "tx timeout\n"); - /* Do the reset outside of interrupt context */ schedule_work(&mp->tx_timeout_task); } #ifdef CONFIG_NET_POLL_CONTROLLER -static void mv643xx_eth_netpoll(struct net_device *netdev) +static void mv643xx_eth_netpoll(struct net_device *dev) { - struct mv643xx_eth_private *mp = netdev_priv(netdev); - int port_num = mp->port_num; + struct mv643xx_eth_private *mp = netdev_priv(dev); - wrl(mp, INT_MASK(port_num), 0x00000000); - /* wait for previous write to complete */ - rdl(mp, INT_MASK(port_num)); + wrl(mp, INT_MASK(mp->port_num), 0x00000000); + rdl(mp, INT_MASK(mp->port_num)); - mv643xx_eth_int_handler(netdev->irq, netdev); + mv643xx_eth_irq(dev->irq, dev); - wrl(mp, INT_MASK(port_num), INT_RX | INT_CAUSE_EXT); + wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_CAUSE_EXT); } #endif -static int mv643xx_eth_mdio_read(struct net_device *dev, int phy_id, int location) +static int mv643xx_eth_mdio_read(struct net_device *dev, int addr, int reg) { struct mv643xx_eth_private *mp = netdev_priv(dev); int val; - read_smi_reg(mp, location, &val); + smi_reg_read(mp, addr, reg, &val); + return val; } -static void mv643xx_eth_mdio_write(struct net_device *dev, int phy_id, int location, int val) +static void mv643xx_eth_mdio_write(struct net_device *dev, int addr, int reg, int val) { struct mv643xx_eth_private *mp = netdev_priv(dev); - write_smi_reg(mp, location, val); + smi_reg_write(mp, addr, reg, val); } @@ -1956,9 +1890,6 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) goto out_free; spin_lock_init(&msp->phy_lock); - msp->t_clk = (pd != NULL && pd->t_clk != 0) ? pd->t_clk : 133000000; - - platform_set_drvdata(pdev, msp); /* * (Re-)program MBUS remapping windows if we are asked to. @@ -1966,6 +1897,13 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (pd != NULL && pd->dram != NULL) mv643xx_eth_conf_mbus_windows(msp, pd->dram); + /* + * Detect hardware parameters. + */ + msp->t_clk = (pd != NULL && pd->t_clk != 0) ? pd->t_clk : 133000000; + + platform_set_drvdata(pdev, msp); + return 0; out_free: @@ -1985,104 +1923,158 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) } static struct platform_driver mv643xx_eth_shared_driver = { - .probe = mv643xx_eth_shared_probe, - .remove = mv643xx_eth_shared_remove, + .probe = mv643xx_eth_shared_probe, + .remove = mv643xx_eth_shared_remove, .driver = { - .name = MV643XX_ETH_SHARED_NAME, + .name = MV643XX_ETH_SHARED_NAME, .owner = THIS_MODULE, }, }; static void phy_addr_set(struct mv643xx_eth_private *mp, int phy_addr) { - u32 reg_data; int addr_shift = 5 * mp->port_num; + u32 data; - reg_data = rdl(mp, PHY_ADDR); - reg_data &= ~(0x1f << addr_shift); - reg_data |= (phy_addr & 0x1f) << addr_shift; - wrl(mp, PHY_ADDR, reg_data); + data = rdl(mp, PHY_ADDR); + data &= ~(0x1f << addr_shift); + data |= (phy_addr & 0x1f) << addr_shift; + wrl(mp, PHY_ADDR, data); } static int phy_addr_get(struct mv643xx_eth_private *mp) { - unsigned int reg_data; + unsigned int data; + + data = rdl(mp, PHY_ADDR); + + return (data >> (5 * mp->port_num)) & 0x1f; +} + +static void set_params(struct mv643xx_eth_private *mp, + struct mv643xx_eth_platform_data *pd) +{ + struct net_device *dev = mp->dev; + + if (is_valid_ether_addr(pd->mac_addr)) + memcpy(dev->dev_addr, pd->mac_addr, 6); + else + uc_addr_get(mp, dev->dev_addr); + + if (pd->phy_addr == -1) { + mp->shared_smi = NULL; + mp->phy_addr = -1; + } else { + mp->shared_smi = mp->shared; + if (pd->shared_smi != NULL) + mp->shared_smi = platform_get_drvdata(pd->shared_smi); + + if (pd->force_phy_addr || pd->phy_addr) { + mp->phy_addr = pd->phy_addr & 0x3f; + phy_addr_set(mp, mp->phy_addr); + } else { + mp->phy_addr = phy_addr_get(mp); + } + } - reg_data = rdl(mp, PHY_ADDR); + mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE; + if (pd->rx_queue_size) + mp->default_rx_ring_size = pd->rx_queue_size; + mp->rx_desc_sram_addr = pd->rx_sram_addr; + mp->rx_desc_sram_size = pd->rx_sram_size; - return ((reg_data >> (5 * mp->port_num)) & 0x1f); + mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; + if (pd->tx_queue_size) + mp->default_tx_ring_size = pd->tx_queue_size; + mp->tx_desc_sram_addr = pd->tx_sram_addr; + mp->tx_desc_sram_size = pd->tx_sram_size; } static int phy_detect(struct mv643xx_eth_private *mp) { - unsigned int phy_reg_data0; - int auto_neg; + unsigned int data; + unsigned int data2; + + smi_reg_read(mp, mp->phy_addr, 0, &data); + smi_reg_write(mp, mp->phy_addr, 0, data ^ 0x1000); - read_smi_reg(mp, 0, &phy_reg_data0); - auto_neg = phy_reg_data0 & 0x1000; - phy_reg_data0 ^= 0x1000; /* invert auto_neg */ - write_smi_reg(mp, 0, phy_reg_data0); + smi_reg_read(mp, mp->phy_addr, 0, &data2); + if (((data ^ data2) & 0x1000) == 0) + return -ENODEV; - read_smi_reg(mp, 0, &phy_reg_data0); - if ((phy_reg_data0 & 0x1000) == auto_neg) - return -ENODEV; /* change didn't take */ + smi_reg_write(mp, mp->phy_addr, 0, data); - phy_reg_data0 ^= 0x1000; - write_smi_reg(mp, 0, phy_reg_data0); return 0; } -static void mv643xx_init_ethtool_cmd(struct net_device *dev, int phy_address, - int speed, int duplex, - struct ethtool_cmd *cmd) +static int phy_init(struct mv643xx_eth_private *mp, + struct mv643xx_eth_platform_data *pd) { - struct mv643xx_eth_private *mp = netdev_priv(dev); + struct ethtool_cmd cmd; + int err; - memset(cmd, 0, sizeof(*cmd)); + err = phy_detect(mp); + if (err) { + dev_printk(KERN_INFO, &mp->dev->dev, + "no PHY detected at addr %d\n", mp->phy_addr); + return err; + } + phy_reset(mp); + + mp->mii.phy_id = mp->phy_addr; + mp->mii.phy_id_mask = 0x3f; + mp->mii.reg_num_mask = 0x1f; + mp->mii.dev = mp->dev; + mp->mii.mdio_read = mv643xx_eth_mdio_read; + mp->mii.mdio_write = mv643xx_eth_mdio_write; - cmd->port = PORT_MII; - cmd->transceiver = XCVR_INTERNAL; - cmd->phy_address = phy_address; + mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii); - if (speed == 0) { - cmd->autoneg = AUTONEG_ENABLE; - /* mii lib checks, but doesn't use speed on AUTONEG_ENABLE */ - cmd->speed = SPEED_100; - cmd->advertising = ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full; + memset(&cmd, 0, sizeof(cmd)); + + cmd.port = PORT_MII; + cmd.transceiver = XCVR_INTERNAL; + cmd.phy_address = mp->phy_addr; + if (pd->speed == 0) { + cmd.autoneg = AUTONEG_ENABLE; + cmd.speed = SPEED_100; + cmd.advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full; if (mp->mii.supports_gmii) - cmd->advertising |= ADVERTISED_1000baseT_Full; + cmd.advertising |= ADVERTISED_1000baseT_Full; } else { - cmd->autoneg = AUTONEG_DISABLE; - cmd->speed = speed; - cmd->duplex = duplex; + cmd.autoneg = AUTONEG_DISABLE; + cmd.speed = pd->speed; + cmd.duplex = pd->duplex; } + + update_pscr(mp, cmd.speed, cmd.duplex); + mv643xx_eth_set_settings(mp->dev, &cmd); + + return 0; } static int mv643xx_eth_probe(struct platform_device *pdev) { struct mv643xx_eth_platform_data *pd; - int port_num; struct mv643xx_eth_private *mp; struct net_device *dev; - u8 *p; struct resource *res; - int err; - struct ethtool_cmd cmd; - int duplex = DUPLEX_HALF; - int speed = 0; /* default to auto-negotiation */ DECLARE_MAC_BUF(mac); + int err; pd = pdev->dev.platform_data; if (pd == NULL) { - printk(KERN_ERR "No mv643xx_eth_platform_data\n"); + dev_printk(KERN_ERR, &pdev->dev, + "no mv643xx_eth_platform_data\n"); return -ENODEV; } if (pd->shared == NULL) { - printk(KERN_ERR "No mv643xx_eth_platform_data->shared\n"); + dev_printk(KERN_ERR, &pdev->dev, + "no mv643xx_eth_platform_data->shared\n"); return -ENODEV; } @@ -2090,145 +2082,80 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - platform_set_drvdata(pdev, dev); - mp = netdev_priv(dev); + platform_set_drvdata(pdev, mp); + + mp->shared = platform_get_drvdata(pd->shared); + mp->port_num = pd->port_number; + mp->dev = dev; #ifdef MV643XX_ETH_NAPI netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 64); #endif + set_params(mp, pd); + + spin_lock_init(&mp->lock); + + mib_counters_clear(mp); + INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); + + err = phy_init(mp, pd); + if (err) + goto out; + SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); BUG_ON(!res); dev->irq = res->start; + dev->hard_start_xmit = mv643xx_eth_xmit; dev->open = mv643xx_eth_open; dev->stop = mv643xx_eth_stop; - dev->hard_start_xmit = mv643xx_eth_start_xmit; - dev->set_mac_address = mv643xx_eth_set_mac_address; dev->set_multicast_list = mv643xx_eth_set_rx_mode; - - /* No need to Tx Timeout */ + dev->set_mac_address = mv643xx_eth_set_mac_address; + dev->do_ioctl = mv643xx_eth_ioctl; + dev->change_mtu = mv643xx_eth_change_mtu; dev->tx_timeout = mv643xx_eth_tx_timeout; - #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = mv643xx_eth_netpoll; #endif - dev->watchdog_timeo = 2 * HZ; dev->base_addr = 0; - dev->change_mtu = mv643xx_eth_change_mtu; - dev->do_ioctl = mv643xx_eth_do_ioctl; - SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); #ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX -#ifdef MAX_SKB_FRAGS /* * Zero copy can only work if we use Discovery II memory. Else, we will * have to map the buffers to ISA memory which is only 16 MB */ dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; -#endif #endif - /* Configure the timeout task */ - INIT_WORK(&mp->tx_timeout_task, mv643xx_eth_tx_timeout_task); - - spin_lock_init(&mp->lock); - - mp->shared = platform_get_drvdata(pd->shared); - port_num = mp->port_num = pd->port_number; + SET_NETDEV_DEV(dev, &pdev->dev); if (mp->shared->win_protect) - wrl(mp, WINDOW_PROTECT(port_num), mp->shared->win_protect); - - mp->shared_smi = mp->shared; - if (pd->shared_smi != NULL) - mp->shared_smi = platform_get_drvdata(pd->shared_smi); - - /* set default config values */ - uc_addr_get(mp, dev->dev_addr); - - if (is_valid_ether_addr(pd->mac_addr)) - memcpy(dev->dev_addr, pd->mac_addr, 6); - - if (pd->phy_addr || pd->force_phy_addr) - phy_addr_set(mp, pd->phy_addr); - - mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE; - if (pd->rx_queue_size) - mp->default_rx_ring_size = pd->rx_queue_size; - - mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; - if (pd->tx_queue_size) - mp->default_tx_ring_size = pd->tx_queue_size; - - if (pd->tx_sram_size) { - mp->tx_desc_sram_size = pd->tx_sram_size; - mp->tx_desc_sram_addr = pd->tx_sram_addr; - } - - if (pd->rx_sram_size) { - mp->rx_desc_sram_addr = pd->rx_sram_addr; - mp->rx_desc_sram_size = pd->rx_sram_size; - } - - duplex = pd->duplex; - speed = pd->speed; + wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect); - /* Hook up MII support for ethtool */ - mp->mii.dev = dev; - mp->mii.mdio_read = mv643xx_eth_mdio_read; - mp->mii.mdio_write = mv643xx_eth_mdio_write; - mp->mii.phy_id = phy_addr_get(mp); - mp->mii.phy_id_mask = 0x3f; - mp->mii.reg_num_mask = 0x1f; - - err = phy_detect(mp); - if (err) { - pr_debug("%s: No PHY detected at addr %d\n", - dev->name, phy_addr_get(mp)); - goto out; - } - - phy_reset(mp); - mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii); - mv643xx_init_ethtool_cmd(dev, mp->mii.phy_id, speed, duplex, &cmd); - mv643xx_eth_update_pscr(mp, &cmd); - mv643xx_eth_set_settings(dev, &cmd); - - SET_NETDEV_DEV(dev, &pdev->dev); err = register_netdev(dev); if (err) goto out; - p = dev->dev_addr; - printk(KERN_NOTICE - "%s: port %d with MAC address %s\n", - dev->name, port_num, print_mac(mac, p)); + dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n", + mp->port_num, print_mac(mac, dev->dev_addr)); if (dev->features & NETIF_F_SG) - printk(KERN_NOTICE "%s: Scatter Gather Enabled\n", dev->name); + dev_printk(KERN_NOTICE, &dev->dev, "scatter/gather enabled\n"); if (dev->features & NETIF_F_IP_CSUM) - printk(KERN_NOTICE "%s: TX TCP/IP Checksumming Supported\n", - dev->name); - -#ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX - printk(KERN_NOTICE "%s: RX TCP/UDP Checksum Offload ON \n", dev->name); -#endif - -#ifdef MV643XX_ETH_COAL - printk(KERN_NOTICE "%s: TX and RX Interrupt Coalescing ON \n", - dev->name); -#endif + dev_printk(KERN_NOTICE, &dev->dev, "tx checksum offload\n"); #ifdef MV643XX_ETH_NAPI - printk(KERN_NOTICE "%s: RX NAPI Enabled \n", dev->name); + dev_printk(KERN_NOTICE, &dev->dev, "napi enabled\n"); #endif if (mp->tx_desc_sram_size > 0) - printk(KERN_NOTICE "%s: Using SRAM\n", dev->name); + dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n"); return 0; @@ -2240,35 +2167,35 @@ out: static int mv643xx_eth_remove(struct platform_device *pdev) { - struct net_device *dev = platform_get_drvdata(pdev); + struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); - unregister_netdev(dev); + unregister_netdev(mp->dev); flush_scheduled_work(); + free_netdev(mp->dev); - free_netdev(dev); platform_set_drvdata(pdev, NULL); + return 0; } static void mv643xx_eth_shutdown(struct platform_device *pdev) { - struct net_device *dev = platform_get_drvdata(pdev); - struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; + struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); /* Mask all interrupts on ethernet port */ - wrl(mp, INT_MASK(port_num), 0); - rdl(mp, INT_MASK(port_num)); + wrl(mp, INT_MASK(mp->port_num), 0); + rdl(mp, INT_MASK(mp->port_num)); - port_reset(mp); + if (netif_running(mp->dev)) + port_reset(mp); } static struct platform_driver mv643xx_eth_driver = { - .probe = mv643xx_eth_probe, - .remove = mv643xx_eth_remove, - .shutdown = mv643xx_eth_shutdown, + .probe = mv643xx_eth_probe, + .remove = mv643xx_eth_remove, + .shutdown = mv643xx_eth_shutdown, .driver = { - .name = MV643XX_ETH_NAME, + .name = MV643XX_ETH_NAME, .owner = THIS_MODULE, }, }; @@ -2283,21 +2210,21 @@ static int __init mv643xx_eth_init_module(void) if (rc) platform_driver_unregister(&mv643xx_eth_shared_driver); } + return rc; } +module_init(mv643xx_eth_init_module); static void __exit mv643xx_eth_cleanup_module(void) { platform_driver_unregister(&mv643xx_eth_driver); platform_driver_unregister(&mv643xx_eth_shared_driver); } - -module_init(mv643xx_eth_init_module); module_exit(mv643xx_eth_cleanup_module); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR( "Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, Manish Lachwani" - " and Dale Farnsworth"); +MODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, Manish Lachwani " + "and Dale Farnsworth"); MODULE_DESCRIPTION("Ethernet driver for Marvell MV643XX"); -MODULE_ALIAS("platform:" MV643XX_ETH_NAME); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" MV643XX_ETH_SHARED_NAME); +MODULE_ALIAS("platform:" MV643XX_ETH_NAME); diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index a15cdd4a8e58..646177660495 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -17,30 +17,53 @@ struct mv643xx_eth_shared_platform_data { struct mbus_dram_target_info *dram; - unsigned int t_clk; + unsigned int t_clk; }; struct mv643xx_eth_platform_data { + /* + * Pointer back to our parent instance, and our port number. + */ struct platform_device *shared; - int port_number; + int port_number; + /* + * Whether a PHY is present, and if yes, at which address. + */ struct platform_device *shared_smi; + int force_phy_addr; + int phy_addr; - u16 force_phy_addr; /* force override if phy_addr == 0 */ - u16 phy_addr; - - /* If speed is 0, then speed and duplex are autonegotiated. */ - int speed; /* 0, SPEED_10, SPEED_100, SPEED_1000 */ - int duplex; /* DUPLEX_HALF or DUPLEX_FULL */ - - /* non-zero values of the following fields override defaults */ - u32 tx_queue_size; - u32 rx_queue_size; - u32 tx_sram_addr; - u32 tx_sram_size; - u32 rx_sram_addr; - u32 rx_sram_size; - u8 mac_addr[6]; /* mac address if non-zero*/ + /* + * Use this MAC address if it is valid, overriding the + * address that is already in the hardware. + */ + u8 mac_addr[6]; + + /* + * If speed is 0, autonegotiation is enabled. + * Valid values for speed: 0, SPEED_10, SPEED_100, SPEED_1000. + * Valid values for duplex: DUPLEX_HALF, DUPLEX_FULL. + */ + int speed; + int duplex; + + /* + * Override default RX/TX queue sizes if nonzero. + */ + int rx_queue_size; + int tx_queue_size; + + /* + * Use on-chip SRAM for RX/TX descriptors if size is nonzero + * and sufficient to contain all descriptors for the requested + * ring sizes. + */ + unsigned long rx_sram_addr; + int rx_sram_size; + unsigned long tx_sram_addr; + int tx_sram_size; }; -#endif /* __LINUX_MV643XX_ETH_H */ + +#endif -- cgit v1.2.3 From 64da80a29c7455321a7df7b47e27d639e3944c1a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 2 Jun 2008 01:01:26 +0200 Subject: mv643xx_eth: allow multiple RX queues Allow the platform code to specify that we are running on hardware that is capable of supporting multiple RX queues. If this option is used, initialise all of the given RX queues instead of just RX queue zero. Signed-off-by: Lennert Buytenhek Acked-by: Dale Farnsworth --- drivers/net/mv643xx_eth.c | 99 +++++++++++++++++++++++++++++++++------------ include/linux/mv643xx_eth.h | 5 +++ 2 files changed, 79 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 9ce7be09e295..3c8591853999 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -96,7 +96,7 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define TX_BW_MTU(p) (0x0458 + ((p) << 10)) #define TX_BW_BURST(p) (0x045c + ((p) << 10)) #define INT_CAUSE(p) (0x0460 + ((p) << 10)) -#define INT_RX 0x00000804 +#define INT_RX 0x0007fbfc #define INT_EXT 0x00000002 #define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) #define INT_EXT_LINK 0x00100000 @@ -107,7 +107,7 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define INT_MASK(p) (0x0468 + ((p) << 10)) #define INT_MASK_EXT(p) (0x046c + ((p) << 10)) #define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10)) -#define RXQ_CURRENT_DESC_PTR(p) (0x060c + ((p) << 10)) +#define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4)) #define RXQ_COMMAND(p) (0x0680 + ((p) << 10)) #define TXQ_CURRENT_DESC_PTR(p) (0x06c0 + ((p) << 10)) #define TXQ_BW_TOKENS(p) (0x0700 + ((p) << 10)) @@ -286,6 +286,8 @@ struct mib_counters { }; struct rx_queue { + int index; + int rx_ring_size; int rx_desc_count; @@ -334,8 +336,10 @@ struct mv643xx_eth_private { int default_rx_ring_size; unsigned long rx_desc_sram_addr; int rx_desc_sram_size; + u8 rxq_mask; + int rxq_primary; struct napi_struct napi; - struct rx_queue rxq[1]; + struct rx_queue rxq[8]; /* * TX state. @@ -365,7 +369,7 @@ static inline void wrl(struct mv643xx_eth_private *mp, int offset, u32 data) /* rxq/txq helper functions *************************************************/ static struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq) { - return container_of(rxq, struct mv643xx_eth_private, rxq[0]); + return container_of(rxq, struct mv643xx_eth_private, rxq[rxq->index]); } static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) @@ -376,13 +380,13 @@ static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) static void rxq_enable(struct rx_queue *rxq) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - wrl(mp, RXQ_COMMAND(mp->port_num), 1); + wrl(mp, RXQ_COMMAND(mp->port_num), 1 << rxq->index); } static void rxq_disable(struct rx_queue *rxq) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - u8 mask = 1; + u8 mask = 1 << rxq->index; wrl(mp, RXQ_COMMAND(mp->port_num), mask << 8); while (rdl(mp, RXQ_COMMAND(mp->port_num)) & mask) @@ -583,6 +587,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) { struct mv643xx_eth_private *mp; int rx; + int i; mp = container_of(napi, struct mv643xx_eth_private, napi); @@ -593,7 +598,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) } #endif - rx = rxq_process(mp->rxq, budget); + rx = 0; + for (i = 7; rx < budget && i >= 0; i--) + if (mp->rxq_mask & (1 << i)) + rx += rxq_process(mp->rxq + i, budget - rx); if (rx < budget) { netif_rx_complete(mp->dev, napi); @@ -1306,13 +1314,15 @@ static void mv643xx_eth_set_rx_mode(struct net_device *dev) /* rx/tx queue initialisation ***********************************************/ -static int rxq_init(struct mv643xx_eth_private *mp) +static int rxq_init(struct mv643xx_eth_private *mp, int index) { - struct rx_queue *rxq = mp->rxq; + struct rx_queue *rxq = mp->rxq + index; struct rx_desc *rx_desc; int size; int i; + rxq->index = index; + rxq->rx_ring_size = mp->default_rx_ring_size; rxq->rx_desc_count = 0; @@ -1321,7 +1331,7 @@ static int rxq_init(struct mv643xx_eth_private *mp) size = rxq->rx_ring_size * sizeof(struct rx_desc); - if (size <= mp->rx_desc_sram_size) { + if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) { rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr, mp->rx_desc_sram_size); rxq->rx_desc_dma = mp->rx_desc_sram_addr; @@ -1362,7 +1372,7 @@ static int rxq_init(struct mv643xx_eth_private *mp) out_free: - if (size <= mp->rx_desc_sram_size) + if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else dma_free_coherent(NULL, size, @@ -1395,7 +1405,8 @@ static void rxq_deinit(struct rx_queue *rxq) rxq->rx_desc_count); } - if (rxq->rx_desc_area_size <= mp->rx_desc_sram_size) + if (rxq->index == mp->rxq_primary && + rxq->rx_desc_area_size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else dma_free_coherent(NULL, rxq->rx_desc_area_size, @@ -1612,6 +1623,9 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) } } + /* + * RxBuffer or RxError set for any of the 8 queues? + */ #ifdef MV643XX_ETH_NAPI if (int_cause & INT_RX) { wrl(mp, INT_MASK(mp->port_num), 0x00000000); @@ -1620,8 +1634,13 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) netif_rx_schedule(dev, &mp->napi); } #else - if (int_cause & INT_RX) - rxq_process(mp->rxq, INT_MAX); + if (int_cause & INT_RX) { + int i; + + for (i = 7; i >= 0; i--) + if (mp->rxq_mask & (1 << i)) + rxq_process(mp->rxq + i, INT_MAX); + } #endif if (int_cause_ext & INT_EXT_TX) { @@ -1707,13 +1726,16 @@ static void port_start(struct mv643xx_eth_private *mp) wrl(mp, PORT_CONFIG_EXT(mp->port_num), 0x00000000); /* - * Enable the receive queue. + * Enable the receive queues. */ - for (i = 0; i < 1; i++) { - struct rx_queue *rxq = mp->rxq; - int off = RXQ_CURRENT_DESC_PTR(mp->port_num); + for (i = 0; i < 8; i++) { + struct rx_queue *rxq = mp->rxq + i; + int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i); u32 addr; + if ((mp->rxq_mask & (1 << i)) == 0) + continue; + addr = (u32)rxq->rx_desc_dma; addr += rxq->rx_curr_desc * sizeof(struct rx_desc); wrl(mp, off, addr); @@ -1748,6 +1770,7 @@ static int mv643xx_eth_open(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); int err; + int i; wrl(mp, INT_CAUSE(mp->port_num), 0); wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); @@ -1763,10 +1786,20 @@ static int mv643xx_eth_open(struct net_device *dev) init_mac_tables(mp); - err = rxq_init(mp); - if (err) - goto out; - rxq_refill(mp->rxq); + for (i = 0; i < 8; i++) { + if ((mp->rxq_mask & (1 << i)) == 0) + continue; + + err = rxq_init(mp, i); + if (err) { + while (--i >= 0) + if (mp->rxq_mask & (1 << i)) + rxq_deinit(mp->rxq + i); + goto out; + } + + rxq_refill(mp->rxq + i); + } err = txq_init(mp); if (err) @@ -1790,7 +1823,9 @@ static int mv643xx_eth_open(struct net_device *dev) out_free: - rxq_deinit(mp->rxq); + for (i = 0; i < 8; i++) + if (mp->rxq_mask & (1 << i)) + rxq_deinit(mp->rxq + i); out: free_irq(dev->irq, dev); @@ -1800,9 +1835,13 @@ out: static void port_reset(struct mv643xx_eth_private *mp) { unsigned int data; + int i; + for (i = 0; i < 8; i++) { + if (mp->rxq_mask & (1 << i)) + rxq_disable(mp->rxq + i); + } txq_disable(mp->txq); - rxq_disable(mp->rxq); while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) udelay(10); @@ -1817,6 +1856,7 @@ static void port_reset(struct mv643xx_eth_private *mp) static int mv643xx_eth_stop(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); + int i; wrl(mp, INT_MASK(mp->port_num), 0x00000000); rdl(mp, INT_MASK(mp->port_num)); @@ -1832,8 +1872,11 @@ static int mv643xx_eth_stop(struct net_device *dev) port_reset(mp); mib_counters_update(mp); + for (i = 0; i < 8; i++) { + if (mp->rxq_mask & (1 << i)) + rxq_deinit(mp->rxq + i); + } txq_deinit(mp->txq); - rxq_deinit(mp->rxq); return 0; } @@ -2085,6 +2128,12 @@ static void set_params(struct mv643xx_eth_private *mp, mp->rx_desc_sram_addr = pd->rx_sram_addr; mp->rx_desc_sram_size = pd->rx_sram_size; + if (pd->rx_queue_mask) + mp->rxq_mask = pd->rx_queue_mask; + else + mp->rxq_mask = 0x01; + mp->rxq_primary = fls(mp->rxq_mask) - 1; + mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; if (pd->tx_queue_size) mp->default_tx_ring_size = pd->tx_queue_size; diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 646177660495..1afd7ba6d303 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -48,6 +48,11 @@ struct mv643xx_eth_platform_data { int speed; int duplex; + /* + * Which RX queues to use. + */ + int rx_queue_mask; + /* * Override default RX/TX queue sizes if nonzero. */ -- cgit v1.2.3 From 3d6b35bc5090cf8d8b7e62eca1f9c21ca56fc6c7 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 2 Jun 2008 01:28:22 +0200 Subject: mv643xx_eth: allow multiple TX queues As with the multiple RX queue support, allow the platform code to specify that the hardware we are running on supports multiple TX queues. This patch only uses the highest-numbered enabled queue to send packets to for now, this can be extended later to enable QoS and such. Signed-off-by: Lennert Buytenhek Acked-by: Dale Farnsworth --- drivers/net/mv643xx_eth.c | 146 +++++++++++++++++++++++++++++++------------- include/linux/mv643xx_eth.h | 3 +- 2 files changed, 105 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 3c8591853999..287155ea9ce1 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -103,16 +103,16 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define INT_EXT_PHY 0x00010000 #define INT_EXT_TX_ERROR_0 0x00000100 #define INT_EXT_TX_0 0x00000001 -#define INT_EXT_TX 0x00000101 +#define INT_EXT_TX 0x0000ffff #define INT_MASK(p) (0x0468 + ((p) << 10)) #define INT_MASK_EXT(p) (0x046c + ((p) << 10)) #define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10)) #define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4)) #define RXQ_COMMAND(p) (0x0680 + ((p) << 10)) -#define TXQ_CURRENT_DESC_PTR(p) (0x06c0 + ((p) << 10)) -#define TXQ_BW_TOKENS(p) (0x0700 + ((p) << 10)) -#define TXQ_BW_CONF(p) (0x0704 + ((p) << 10)) -#define TXQ_BW_WRR_CONF(p) (0x0708 + ((p) << 10)) +#define TXQ_CURRENT_DESC_PTR(p, q) (0x06c0 + ((p) << 10) + ((q) << 2)) +#define TXQ_BW_TOKENS(p, q) (0x0700 + ((p) << 10) + ((q) << 4)) +#define TXQ_BW_CONF(p, q) (0x0704 + ((p) << 10) + ((q) << 4)) +#define TXQ_BW_WRR_CONF(p, q) (0x0708 + ((p) << 10) + ((q) << 4)) #define MIB_COUNTERS(p) (0x1000 + ((p) << 7)) #define SPECIAL_MCAST_TABLE(p) (0x1400 + ((p) << 10)) #define OTHER_MCAST_TABLE(p) (0x1500 + ((p) << 10)) @@ -303,6 +303,8 @@ struct rx_queue { }; struct tx_queue { + int index; + int tx_ring_size; int tx_desc_count; @@ -347,7 +349,9 @@ struct mv643xx_eth_private { int default_tx_ring_size; unsigned long tx_desc_sram_addr; int tx_desc_sram_size; - struct tx_queue txq[1]; + u8 txq_mask; + int txq_primary; + struct tx_queue txq[8]; #ifdef MV643XX_ETH_TX_FAST_REFILL int tx_clean_threshold; #endif @@ -374,7 +378,7 @@ static struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq) static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) { - return container_of(txq, struct mv643xx_eth_private, txq[0]); + return container_of(txq, struct mv643xx_eth_private, txq[txq->index]); } static void rxq_enable(struct rx_queue *rxq) @@ -396,13 +400,13 @@ static void rxq_disable(struct rx_queue *rxq) static void txq_enable(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - wrl(mp, TXQ_COMMAND(mp->port_num), 1); + wrl(mp, TXQ_COMMAND(mp->port_num), 1 << txq->index); } static void txq_disable(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - u8 mask = 1; + u8 mask = 1 << txq->index; wrl(mp, TXQ_COMMAND(mp->port_num), mask << 8); while (rdl(mp, TXQ_COMMAND(mp->port_num)) & mask) @@ -413,6 +417,12 @@ static void __txq_maybe_wake(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); + /* + * netif_{stop,wake}_queue() flow control only applies to + * the primary queue. + */ + BUG_ON(txq->index != mp->txq_primary); + if (txq->tx_ring_size - txq->tx_desc_count >= MAX_DESCS_PER_SKB) netif_wake_queue(mp->dev); } @@ -593,8 +603,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) #ifdef MV643XX_ETH_TX_FAST_REFILL if (++mp->tx_clean_threshold > 5) { - txq_reclaim(mp->txq, 0); mp->tx_clean_threshold = 0; + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_reclaim(mp->txq + i, 0); } #endif @@ -754,8 +766,6 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) struct tx_queue *txq; unsigned long flags; - BUG_ON(netif_queue_stopped(dev)); - if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { stats->tx_dropped++; dev_printk(KERN_DEBUG, &dev->dev, @@ -766,13 +776,15 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&mp->lock, flags); - txq = mp->txq; + txq = mp->txq + mp->txq_primary; if (txq->tx_ring_size - txq->tx_desc_count < MAX_DESCS_PER_SKB) { - printk(KERN_ERR "%s: transmit with queue full\n", dev->name); - netif_stop_queue(dev); spin_unlock_irqrestore(&mp->lock, flags); - return NETDEV_TX_BUSY; + if (txq->index == mp->txq_primary && net_ratelimit()) + dev_printk(KERN_ERR, &dev->dev, + "primary tx queue full?!\n"); + kfree_skb(skb); + return NETDEV_TX_OK; } txq_submit_skb(txq, skb); @@ -780,8 +792,13 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_packets++; dev->trans_start = jiffies; - if (txq->tx_ring_size - txq->tx_desc_count < MAX_DESCS_PER_SKB) - netif_stop_queue(dev); + if (txq->index == mp->txq_primary) { + int entries_left; + + entries_left = txq->tx_ring_size - txq->tx_desc_count; + if (entries_left < MAX_DESCS_PER_SKB) + netif_stop_queue(dev); + } spin_unlock_irqrestore(&mp->lock, flags); @@ -831,8 +848,8 @@ static void txq_set_rate(struct tx_queue *txq, int rate, int burst) if (bucket_size > 65535) bucket_size = 65535; - wrl(mp, TXQ_BW_TOKENS(mp->port_num), token_rate << 14); - wrl(mp, TXQ_BW_CONF(mp->port_num), + wrl(mp, TXQ_BW_TOKENS(mp->port_num, txq->index), token_rate << 14); + wrl(mp, TXQ_BW_CONF(mp->port_num, txq->index), (bucket_size << 10) | token_rate); } @@ -848,7 +865,7 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq) off = TXQ_FIX_PRIO_CONF(mp->port_num); val = rdl(mp, off); - val |= 1; + val |= 1 << txq->index; wrl(mp, off, val); } @@ -864,13 +881,13 @@ static void txq_set_wrr(struct tx_queue *txq, int weight) off = TXQ_FIX_PRIO_CONF(mp->port_num); val = rdl(mp, off); - val &= ~1; + val &= ~(1 << txq->index); wrl(mp, off, val); /* * Configure WRR weight for this queue. */ - off = TXQ_BW_WRR_CONF(mp->port_num); + off = TXQ_BW_WRR_CONF(mp->port_num, txq->index); val = rdl(mp, off); val = (val & ~0xff) | (weight & 0xff); @@ -1415,13 +1432,15 @@ static void rxq_deinit(struct rx_queue *rxq) kfree(rxq->rx_skb); } -static int txq_init(struct mv643xx_eth_private *mp) +static int txq_init(struct mv643xx_eth_private *mp, int index) { - struct tx_queue *txq = mp->txq; + struct tx_queue *txq = mp->txq + index; struct tx_desc *tx_desc; int size; int i; + txq->index = index; + txq->tx_ring_size = mp->default_tx_ring_size; txq->tx_desc_count = 0; @@ -1430,7 +1449,7 @@ static int txq_init(struct mv643xx_eth_private *mp) size = txq->tx_ring_size * sizeof(struct tx_desc); - if (size <= mp->tx_desc_sram_size) { + if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) { txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr, mp->tx_desc_sram_size); txq->tx_desc_dma = mp->tx_desc_sram_addr; @@ -1467,7 +1486,7 @@ static int txq_init(struct mv643xx_eth_private *mp) out_free: - if (size <= mp->tx_desc_sram_size) + if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else dma_free_coherent(NULL, size, @@ -1539,7 +1558,8 @@ static void txq_deinit(struct tx_queue *txq) BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); - if (txq->tx_desc_area_size <= mp->tx_desc_sram_size) + if (txq->index == mp->txq_primary && + txq->tx_desc_area_size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else dma_free_coherent(NULL, txq->tx_desc_area_size, @@ -1578,12 +1598,20 @@ static void update_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) if ((pscr_o & SERIAL_PORT_ENABLE) == 0) wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); else { - txq_disable(mp->txq); + int i; + + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_disable(mp->txq + i); + pscr_o &= ~SERIAL_PORT_ENABLE; wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_o); wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); - txq_enable(mp->txq); + + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_enable(mp->txq + i); } } } @@ -1609,13 +1637,17 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) { if (mii_link_ok(&mp->mii)) { struct ethtool_cmd cmd; + int i; mii_ethtool_gset(&mp->mii, &cmd); update_pscr(mp, cmd.speed, cmd.duplex); - txq_enable(mp->txq); + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_enable(mp->txq + i); + if (!netif_carrier_ok(dev)) { netif_carrier_on(dev); - __txq_maybe_wake(mp->txq); + __txq_maybe_wake(mp->txq + mp->txq_primary); } } else if (netif_carrier_ok(dev)) { netif_stop_queue(dev); @@ -1643,9 +1675,17 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) } #endif + /* + * TxBuffer or TxError set for any of the 8 queues? + */ if (int_cause_ext & INT_EXT_TX) { - txq_reclaim(mp->txq, 0); - __txq_maybe_wake(mp->txq); + int i; + + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_reclaim(mp->txq + i, 0); + + __txq_maybe_wake(mp->txq + mp->txq_primary); } return IRQ_HANDLED; @@ -1696,11 +1736,14 @@ static void port_start(struct mv643xx_eth_private *mp) * Configure TX path and queues. */ tx_set_rate(mp, 1000000000, 16777216); - for (i = 0; i < 1; i++) { - struct tx_queue *txq = mp->txq; - int off = TXQ_CURRENT_DESC_PTR(mp->port_num); + for (i = 0; i < 8; i++) { + struct tx_queue *txq = mp->txq + i; + int off = TXQ_CURRENT_DESC_PTR(mp->port_num, i); u32 addr; + if ((mp->txq_mask & (1 << i)) == 0) + continue; + addr = (u32)txq->tx_desc_dma; addr += txq->tx_curr_desc * sizeof(struct tx_desc); wrl(mp, off, addr); @@ -1801,9 +1844,18 @@ static int mv643xx_eth_open(struct net_device *dev) rxq_refill(mp->rxq + i); } - err = txq_init(mp); - if (err) - goto out_free; + for (i = 0; i < 8; i++) { + if ((mp->txq_mask & (1 << i)) == 0) + continue; + + err = txq_init(mp, i); + if (err) { + while (--i >= 0) + if (mp->txq_mask & (1 << i)) + txq_deinit(mp->txq + i); + goto out_free; + } + } #ifdef MV643XX_ETH_NAPI napi_enable(&mp->napi); @@ -1840,8 +1892,9 @@ static void port_reset(struct mv643xx_eth_private *mp) for (i = 0; i < 8; i++) { if (mp->rxq_mask & (1 << i)) rxq_disable(mp->rxq + i); + if (mp->txq_mask & (1 << i)) + txq_disable(mp->txq + i); } - txq_disable(mp->txq); while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) udelay(10); @@ -1875,8 +1928,9 @@ static int mv643xx_eth_stop(struct net_device *dev) for (i = 0; i < 8; i++) { if (mp->rxq_mask & (1 << i)) rxq_deinit(mp->rxq + i); + if (mp->txq_mask & (1 << i)) + txq_deinit(mp->txq + i); } - txq_deinit(mp->txq); return 0; } @@ -1928,7 +1982,7 @@ static void tx_timeout_task(struct work_struct *ugly) port_reset(mp); port_start(mp); - __txq_maybe_wake(mp->txq); + __txq_maybe_wake(mp->txq + mp->txq_primary); } } @@ -2139,6 +2193,12 @@ static void set_params(struct mv643xx_eth_private *mp, mp->default_tx_ring_size = pd->tx_queue_size; mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_size = pd->tx_sram_size; + + if (pd->tx_queue_mask) + mp->txq_mask = pd->tx_queue_mask; + else + mp->txq_mask = 0x01; + mp->txq_primary = fls(mp->txq_mask) - 1; } static int phy_detect(struct mv643xx_eth_private *mp) diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 1afd7ba6d303..12078577aef6 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -49,9 +49,10 @@ struct mv643xx_eth_platform_data { int duplex; /* - * Which RX queues to use. + * Which RX/TX queues to use. */ int rx_queue_mask; + int tx_queue_mask; /* * Override default RX/TX queue sizes if nonzero. -- cgit v1.2.3 From d8f3de0d2412bb91639cfefc5b3c79dbf3812212 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Jun 2008 23:24:06 +0200 Subject: Suspend-related patches for 2.6.27 ACPI PM: Add possibility to change suspend sequence There are some systems out there that don't work correctly with our current suspend/hibernation code ordering. Provide a workaround for these systems allowing them to pass 'acpi_sleep=old_ordering' in the kernel command line so that it will use the pre-ACPI 2.0 ("old") suspend code ordering. Unfortunately, this requires us to add a platform hook to the resuming of devices for recovering the platform in case one of the device drivers' .suspend() routines returns error code. Namely, ACPI 1.0 specifies that _PTS should be called before suspending devices, but _WAK still should be called before resuming them in order to undo the changes made by _PTS. However, if there is an error during suspending devices, they are automatically resumed without returning control to the PM core, so the _WAK has to be called from within device_resume() in that cases. The patch also reorders and refactors the ACPI suspend/hibernation code to avoid duplication as far as reasonably possible. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 6 +- arch/x86/kernel/acpi/sleep.c | 2 + drivers/acpi/sleep/main.c | 276 +++++++++++++++++++++--------------- drivers/base/power/main.c | 2 - include/linux/acpi.h | 3 + include/linux/suspend.h | 14 +- kernel/power/disk.c | 28 +++- kernel/power/main.c | 10 +- 8 files changed, 215 insertions(+), 126 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9cf7b34f2db0..18d793ea0dd3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -147,10 +147,14 @@ and is between 256 and 4096 characters. It is defined in the file default: 0 acpi_sleep= [HW,ACPI] Sleep options - Format: { s3_bios, s3_mode, s3_beep } + Format: { s3_bios, s3_mode, s3_beep, old_ordering } See Documentation/power/video.txt for s3_bios and s3_mode. s3_beep is for debugging; it makes the PC's speaker beep as soon as the kernel's real-mode entry point is called. + old_ordering causes the ACPI 1.0 ordering of the _PTS + control method, wrt putting devices into low power + states, to be enforced (the ACPI 2.0 ordering of _PTS is + used by default). acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode Format: { level | edge | high | low } diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index afc25ee9964b..882e970032d5 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -124,6 +124,8 @@ static int __init acpi_sleep_setup(char *str) acpi_realmode_flags |= 2; if (strncmp(str, "s3_beep", 7) == 0) acpi_realmode_flags |= 4; + if (strncmp(str, "old_ordering", 12) == 0) + acpi_old_suspend_ordering(); str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 0f2caea2fc83..4addf8ad50ae 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -24,10 +24,6 @@ u8 sleep_states[ACPI_S_STATE_COUNT]; -#ifdef CONFIG_PM_SLEEP -static u32 acpi_target_sleep_state = ACPI_STATE_S0; -#endif - static int acpi_sleep_prepare(u32 acpi_state) { #ifdef CONFIG_ACPI_SLEEP @@ -50,9 +46,96 @@ static int acpi_sleep_prepare(u32 acpi_state) return 0; } -#ifdef CONFIG_SUSPEND -static struct platform_suspend_ops acpi_suspend_ops; +#ifdef CONFIG_PM_SLEEP +static u32 acpi_target_sleep_state = ACPI_STATE_S0; + +/* + * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the + * user to request that behavior by using the 'acpi_old_suspend_ordering' + * kernel command line option that causes the following variable to be set. + */ +static bool old_suspend_ordering; + +void __init acpi_old_suspend_ordering(void) +{ + old_suspend_ordering = true; +} + +/** + * acpi_pm_disable_gpes - Disable the GPEs. + */ +static int acpi_pm_disable_gpes(void) +{ + acpi_hw_disable_all_gpes(); + return 0; +} + +/** + * __acpi_pm_prepare - Prepare the platform to enter the target state. + * + * If necessary, set the firmware waking vector and do arch-specific + * nastiness to get the wakeup code to the waking vector. + */ +static int __acpi_pm_prepare(void) +{ + int error = acpi_sleep_prepare(acpi_target_sleep_state); + + if (error) + acpi_target_sleep_state = ACPI_STATE_S0; + return error; +} + +/** + * acpi_pm_prepare - Prepare the platform to enter the target sleep + * state and disable the GPEs. + */ +static int acpi_pm_prepare(void) +{ + int error = __acpi_pm_prepare(); + + if (!error) + acpi_hw_disable_all_gpes(); + return error; +} + +/** + * acpi_pm_finish - Instruct the platform to leave a sleep state. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ +static void acpi_pm_finish(void) +{ + u32 acpi_state = acpi_target_sleep_state; + + if (acpi_state == ACPI_STATE_S0) + return; + printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n", + acpi_state); + acpi_disable_wakeup_device(acpi_state); + acpi_leave_sleep_state(acpi_state); + + /* reset firmware waking vector */ + acpi_set_firmware_waking_vector((acpi_physical_address) 0); + + acpi_target_sleep_state = ACPI_STATE_S0; +} + +/** + * acpi_pm_end - Finish up suspend sequence. + */ +static void acpi_pm_end(void) +{ + /* + * This is necessary in case acpi_pm_finish() is not called during a + * failing transition to a sleep state. + */ + acpi_target_sleep_state = ACPI_STATE_S0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_SUSPEND extern void do_suspend_lowlevel(void); static u32 acpi_suspend_states[] = { @@ -66,7 +149,6 @@ static u32 acpi_suspend_states[] = { * acpi_suspend_begin - Set the target system sleep state to the state * associated with given @pm_state, if supported. */ - static int acpi_suspend_begin(suspend_state_t pm_state) { u32 acpi_state = acpi_suspend_states[pm_state]; @@ -82,25 +164,6 @@ static int acpi_suspend_begin(suspend_state_t pm_state) return error; } -/** - * acpi_suspend_prepare - Do preliminary suspend work. - * - * If necessary, set the firmware waking vector and do arch-specific - * nastiness to get the wakeup code to the waking vector. - */ - -static int acpi_suspend_prepare(void) -{ - int error = acpi_sleep_prepare(acpi_target_sleep_state); - - if (error) { - acpi_target_sleep_state = ACPI_STATE_S0; - return error; - } - - return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; -} - /** * acpi_suspend_enter - Actually enter a sleep state. * @pm_state: ignored @@ -109,7 +172,6 @@ static int acpi_suspend_prepare(void) * assembly, which in turn call acpi_enter_sleep_state(). * It's unfortunate, but it works. Please fix if you're feeling frisky. */ - static int acpi_suspend_enter(suspend_state_t pm_state) { acpi_status status = AE_OK; @@ -166,39 +228,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state) return ACPI_SUCCESS(status) ? 0 : -EFAULT; } -/** - * acpi_suspend_finish - Instruct the platform to leave a sleep state. - * - * This is called after we wake back up (or if entering the sleep state - * failed). - */ - -static void acpi_suspend_finish(void) -{ - u32 acpi_state = acpi_target_sleep_state; - - acpi_disable_wakeup_device(acpi_state); - acpi_leave_sleep_state(acpi_state); - - /* reset firmware waking vector */ - acpi_set_firmware_waking_vector((acpi_physical_address) 0); - - acpi_target_sleep_state = ACPI_STATE_S0; -} - -/** - * acpi_suspend_end - Finish up suspend sequence. - */ - -static void acpi_suspend_end(void) -{ - /* - * This is necessary in case acpi_suspend_finish() is not called during a - * failing transition to a sleep state. - */ - acpi_target_sleep_state = ACPI_STATE_S0; -} - static int acpi_suspend_state_valid(suspend_state_t pm_state) { u32 acpi_state; @@ -218,10 +247,39 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state) static struct platform_suspend_ops acpi_suspend_ops = { .valid = acpi_suspend_state_valid, .begin = acpi_suspend_begin, - .prepare = acpi_suspend_prepare, + .prepare = acpi_pm_prepare, + .enter = acpi_suspend_enter, + .finish = acpi_pm_finish, + .end = acpi_pm_end, +}; + +/** + * acpi_suspend_begin_old - Set the target system sleep state to the + * state associated with given @pm_state, if supported, and + * execute the _PTS control method. This function is used if the + * pre-ACPI 2.0 suspend ordering has been requested. + */ +static int acpi_suspend_begin_old(suspend_state_t pm_state) +{ + int error = acpi_suspend_begin(pm_state); + + if (!error) + error = __acpi_pm_prepare(); + return error; +} + +/* + * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has + * been requested. + */ +static struct platform_suspend_ops acpi_suspend_ops_old = { + .valid = acpi_suspend_state_valid, + .begin = acpi_suspend_begin_old, + .prepare = acpi_pm_disable_gpes, .enter = acpi_suspend_enter, - .finish = acpi_suspend_finish, - .end = acpi_suspend_end, + .finish = acpi_pm_finish, + .end = acpi_pm_end, + .recover = acpi_pm_finish, }; #endif /* CONFIG_SUSPEND */ @@ -229,22 +287,9 @@ static struct platform_suspend_ops acpi_suspend_ops = { static int acpi_hibernation_begin(void) { acpi_target_sleep_state = ACPI_STATE_S4; - return 0; } -static int acpi_hibernation_prepare(void) -{ - int error = acpi_sleep_prepare(ACPI_STATE_S4); - - if (error) { - acpi_target_sleep_state = ACPI_STATE_S0; - return error; - } - - return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; -} - static int acpi_hibernation_enter(void) { acpi_status status = AE_OK; @@ -274,52 +319,55 @@ static void acpi_hibernation_leave(void) acpi_leave_sleep_state_prep(ACPI_STATE_S4); } -static void acpi_hibernation_finish(void) +static void acpi_pm_enable_gpes(void) { - acpi_disable_wakeup_device(ACPI_STATE_S4); - acpi_leave_sleep_state(ACPI_STATE_S4); - - /* reset firmware waking vector */ - acpi_set_firmware_waking_vector((acpi_physical_address) 0); - - acpi_target_sleep_state = ACPI_STATE_S0; + acpi_hw_enable_all_runtime_gpes(); } -static void acpi_hibernation_end(void) -{ - /* - * This is necessary in case acpi_hibernation_finish() is not called - * during a failing transition to the sleep state. - */ - acpi_target_sleep_state = ACPI_STATE_S0; -} +static struct platform_hibernation_ops acpi_hibernation_ops = { + .begin = acpi_hibernation_begin, + .end = acpi_pm_end, + .pre_snapshot = acpi_pm_prepare, + .finish = acpi_pm_finish, + .prepare = acpi_pm_prepare, + .enter = acpi_hibernation_enter, + .leave = acpi_hibernation_leave, + .pre_restore = acpi_pm_disable_gpes, + .restore_cleanup = acpi_pm_enable_gpes, +}; -static int acpi_hibernation_pre_restore(void) +/** + * acpi_hibernation_begin_old - Set the target system sleep state to + * ACPI_STATE_S4 and execute the _PTS control method. This + * function is used if the pre-ACPI 2.0 suspend ordering has been + * requested. + */ +static int acpi_hibernation_begin_old(void) { - acpi_status status; - - status = acpi_hw_disable_all_gpes(); - - return ACPI_SUCCESS(status) ? 0 : -EFAULT; -} + int error = acpi_sleep_prepare(ACPI_STATE_S4); -static void acpi_hibernation_restore_cleanup(void) -{ - acpi_hw_enable_all_runtime_gpes(); + if (!error) + acpi_target_sleep_state = ACPI_STATE_S4; + return error; } -static struct platform_hibernation_ops acpi_hibernation_ops = { - .begin = acpi_hibernation_begin, - .end = acpi_hibernation_end, - .pre_snapshot = acpi_hibernation_prepare, - .finish = acpi_hibernation_finish, - .prepare = acpi_hibernation_prepare, +/* + * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has + * been requested. + */ +static struct platform_hibernation_ops acpi_hibernation_ops_old = { + .begin = acpi_hibernation_begin_old, + .end = acpi_pm_end, + .pre_snapshot = acpi_pm_disable_gpes, + .finish = acpi_pm_finish, + .prepare = acpi_pm_disable_gpes, .enter = acpi_hibernation_enter, .leave = acpi_hibernation_leave, - .pre_restore = acpi_hibernation_pre_restore, - .restore_cleanup = acpi_hibernation_restore_cleanup, + .pre_restore = acpi_pm_disable_gpes, + .restore_cleanup = acpi_pm_enable_gpes, + .recover = acpi_pm_finish, }; -#endif /* CONFIG_HIBERNATION */ +#endif /* CONFIG_HIBERNATION */ int acpi_suspend(u32 acpi_state) { @@ -461,13 +509,15 @@ int __init acpi_sleep_init(void) } } - suspend_set_ops(&acpi_suspend_ops); + suspend_set_ops(old_suspend_ordering ? + &acpi_suspend_ops_old : &acpi_suspend_ops); #endif #ifdef CONFIG_HIBERNATION status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); if (ACPI_SUCCESS(status)) { - hibernation_set_ops(&acpi_hibernation_ops); + hibernation_set_ops(old_suspend_ordering ? + &acpi_hibernation_ops_old : &acpi_hibernation_ops); sleep_states[ACPI_STATE_S4] = 1; printk(" S4"); } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index d571204aaff7..3250c5257b74 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -781,8 +781,6 @@ int device_suspend(pm_message_t state) error = dpm_prepare(state); if (!error) error = dpm_suspend(state); - if (error) - device_resume(resume_event(state)); return error; } EXPORT_SYMBOL_GPL(device_suspend); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 41f7ce7edd7a..33adcf91ef41 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -234,6 +234,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n, int acpi_check_mem_region(resource_size_t start, resource_size_t n, const char *name); +#ifdef CONFIG_PM_SLEEP +void __init acpi_old_suspend_ordering(void); +#endif /* CONFIG_PM_SLEEP */ #else /* CONFIG_ACPI */ static inline int early_acpi_boot_init(void) diff --git a/include/linux/suspend.h b/include/linux/suspend.h index a6977423baf7..e8e69159af71 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -86,6 +86,11 @@ typedef int __bitwise suspend_state_t; * that implement @begin(), but platforms implementing @begin() should * also provide a @end() which cleans up transitions aborted before * @enter(). + * + * @recover: Recover the platform from a suspend failure. + * Called by the PM core if the suspending of devices fails. + * This callback is optional and should only be implemented by platforms + * which require special recovery actions in that situation. */ struct platform_suspend_ops { int (*valid)(suspend_state_t state); @@ -94,6 +99,7 @@ struct platform_suspend_ops { int (*enter)(suspend_state_t state); void (*finish)(void); void (*end)(void); + void (*recover)(void); }; #ifdef CONFIG_SUSPEND @@ -149,7 +155,7 @@ extern void mark_free_pages(struct zone *zone); * The methods in this structure allow a platform to carry out special * operations required by it during a hibernation transition. * - * All the methods below must be implemented. + * All the methods below, except for @recover(), must be implemented. * * @begin: Tell the platform driver that we're starting hibernation. * Called right after shrinking memory and before freezing devices. @@ -189,6 +195,11 @@ extern void mark_free_pages(struct zone *zone); * @restore_cleanup: Clean up after a failing image restoration. * Called right after the nonboot CPUs have been enabled and before * thawing devices (runs with IRQs on). + * + * @recover: Recover the platform from a failure to suspend devices. + * Called by the PM core if the suspending of devices during hibernation + * fails. This callback is optional and should only be implemented by + * platforms which require special recovery actions in that situation. */ struct platform_hibernation_ops { int (*begin)(void); @@ -200,6 +211,7 @@ struct platform_hibernation_ops { void (*leave)(void); int (*pre_restore)(void); void (*restore_cleanup)(void); + void (*recover)(void); }; #ifdef CONFIG_HIBERNATION diff --git a/kernel/power/disk.c b/kernel/power/disk.c index d416be0efa8a..f011e0870b52 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -179,6 +179,17 @@ static void platform_restore_cleanup(int platform_mode) hibernation_ops->restore_cleanup(); } +/** + * platform_recover - recover the platform from a failure to suspend + * devices. + */ + +static void platform_recover(int platform_mode) +{ + if (platform_mode && hibernation_ops && hibernation_ops->recover) + hibernation_ops->recover(); +} + /** * create_image - freeze devices that need to be frozen with interrupts * off, create the hibernation image and thaw those devices. Control @@ -258,10 +269,10 @@ int hibernation_snapshot(int platform_mode) suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) - goto Resume_console; + goto Recover_platform; if (hibernation_test(TEST_DEVICES)) - goto Resume_devices; + goto Recover_platform; error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) @@ -285,11 +296,14 @@ int hibernation_snapshot(int platform_mode) Resume_devices: device_resume(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); - Resume_console: resume_console(); Close: platform_end(platform_mode); return error; + + Recover_platform: + platform_recover(platform_mode); + goto Resume_devices; } /** @@ -398,8 +412,11 @@ int hibernation_platform_enter(void) suspend_console(); error = device_suspend(PMSG_HIBERNATE); - if (error) - goto Resume_console; + if (error) { + if (hibernation_ops->recover) + hibernation_ops->recover(); + goto Resume_devices; + } error = hibernation_ops->prepare(); if (error) @@ -428,7 +445,6 @@ int hibernation_platform_enter(void) hibernation_ops->finish(); Resume_devices: device_resume(PMSG_RESTORE); - Resume_console: resume_console(); Close: hibernation_ops->end(); diff --git a/kernel/power/main.c b/kernel/power/main.c index d023b6b584e5..3398f4651aa1 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -269,11 +269,11 @@ int suspend_devices_and_enter(suspend_state_t state) error = device_suspend(PMSG_SUSPEND); if (error) { printk(KERN_ERR "PM: Some devices failed to suspend\n"); - goto Resume_console; + goto Recover_platform; } if (suspend_test(TEST_DEVICES)) - goto Resume_devices; + goto Recover_platform; if (suspend_ops->prepare) { error = suspend_ops->prepare(); @@ -294,12 +294,16 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_ops->finish(); Resume_devices: device_resume(PMSG_RESUME); - Resume_console: resume_console(); Close: if (suspend_ops->end) suspend_ops->end(); return error; + + Recover_platform: + if (suspend_ops->recover) + suspend_ops->recover(); + goto Resume_devices; } /** -- cgit v1.2.3 From c50cbb05a05cf1f9ca3592272eff053c847727d8 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 4 Jun 2008 21:47:29 -0700 Subject: cpu topology: always define CPU topology information This can result in an empty topology directory in sysfs, and requires in-kernel users to protect all uses with #ifdef - see . The documentation of CPU topology specifies what the defaults should be if only partial information is available from the hardware. So we can provide these defaults as a fallback. This patch: - Adds default definitions of the 4 topology macros to - Changes drivers/base/topology.c to use the topology macros unconditionally and to cope with definitions that aren't lvalues - Updates documentation accordingly [ From: Andrew Morton - fold now-duplicated code - fix layout ] Signed-off-by: Ben Hutchings Cc: Vegard Nossum Cc: Nick Piggin Cc: Chandra Seetharaman Cc: Suresh Siddha Cc: Mike Travis Cc: Christoph Lameter Cc: John Hawkes Cc: Zhang, Yanmin Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar --- Documentation/cputopology.txt | 26 +++++++++----------------- drivers/base/topology.c | 38 ++++++++++---------------------------- include/linux/topology.h | 13 +++++++++++++ 3 files changed, 32 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/Documentation/cputopology.txt b/Documentation/cputopology.txt index b61cb9564023..bd699da24666 100644 --- a/Documentation/cputopology.txt +++ b/Documentation/cputopology.txt @@ -14,9 +14,8 @@ represent the thread siblings to cpu X in the same physical package; To implement it in an architecture-neutral way, a new source file, drivers/base/topology.c, is to export the 4 attributes. -If one architecture wants to support this feature, it just needs to -implement 4 defines, typically in file include/asm-XXX/topology.h. -The 4 defines are: +For an architecture to support this feature, it must define some of +these macros in include/asm-XXX/topology.h: #define topology_physical_package_id(cpu) #define topology_core_id(cpu) #define topology_thread_siblings(cpu) @@ -25,17 +24,10 @@ The 4 defines are: The type of **_id is int. The type of siblings is cpumask_t. -To be consistent on all architectures, the 4 attributes should have -default values if their values are unavailable. Below is the rule. -1) physical_package_id: If cpu has no physical package id, -1 is the -default value. -2) core_id: If cpu doesn't support multi-core, its core id is 0. -3) thread_siblings: Just include itself, if the cpu doesn't support -HT/multi-thread. -4) core_siblings: Just include itself, if the cpu doesn't support -multi-core and HT/Multi-thread. - -So be careful when declaring the 4 defines in include/asm-XXX/topology.h. - -If an attribute isn't defined on an architecture, it won't be exported. - +To be consistent on all architectures, include/linux/topology.h +provides default definitions for any of the above macros that are +not defined by include/asm-XXX/topology.h: +1) physical_package_id: -1 +2) core_id: 0 +3) thread_siblings: just the given CPU +4) core_siblings: just the given CPU diff --git a/drivers/base/topology.c b/drivers/base/topology.c index fdf4044d2e74..24d29a9fc25b 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -59,60 +59,42 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) static inline ssize_t show_##name(struct sys_device *dev, char *buf) \ { \ unsigned int cpu = dev->id; \ - return show_cpumap(0, &(topology_##name(cpu)), buf); \ + cpumask_t siblings = topology_##name(cpu); \ + return show_cpumap(0, &siblings, buf); \ } #define define_siblings_show_list(name) \ static inline ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ { \ unsigned int cpu = dev->id; \ - return show_cpumap(1, &(topology_##name(cpu)), buf); \ + cpumask_t siblings = topology_##name(cpu); \ + return show_cpumap(1, &siblings, buf); \ } #define define_siblings_show_func(name) \ define_siblings_show_map(name); define_siblings_show_list(name) -#ifdef topology_physical_package_id define_id_show_func(physical_package_id); define_one_ro(physical_package_id); -#define ref_physical_package_id_attr &attr_physical_package_id.attr, -#else -#define ref_physical_package_id_attr -#endif -#ifdef topology_core_id define_id_show_func(core_id); define_one_ro(core_id); -#define ref_core_id_attr &attr_core_id.attr, -#else -#define ref_core_id_attr -#endif -#ifdef topology_thread_siblings define_siblings_show_func(thread_siblings); define_one_ro(thread_siblings); define_one_ro(thread_siblings_list); -#define ref_thread_siblings_attr \ - &attr_thread_siblings.attr, &attr_thread_siblings_list.attr, -#else -#define ref_thread_siblings_attr -#endif -#ifdef topology_core_siblings define_siblings_show_func(core_siblings); define_one_ro(core_siblings); define_one_ro(core_siblings_list); -#define ref_core_siblings_attr \ - &attr_core_siblings.attr, &attr_core_siblings_list.attr, -#else -#define ref_core_siblings_attr -#endif static struct attribute *default_attrs[] = { - ref_physical_package_id_attr - ref_core_id_attr - ref_thread_siblings_attr - ref_core_siblings_attr + &attr_physical_package_id.attr, + &attr_core_id.attr, + &attr_thread_siblings.attr, + &attr_thread_siblings_list.attr, + &attr_core_siblings.attr, + &attr_core_siblings_list.attr, NULL }; diff --git a/include/linux/topology.h b/include/linux/topology.h index 24f3d2282e11..2158fc0d5a56 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -179,4 +179,17 @@ void arch_update_cpu_topology(void); #endif #endif /* CONFIG_NUMA */ +#ifndef topology_physical_package_id +#define topology_physical_package_id(cpu) ((void)(cpu), -1) +#endif +#ifndef topology_core_id +#define topology_core_id(cpu) ((void)(cpu), 0) +#endif +#ifndef topology_thread_siblings +#define topology_thread_siblings(cpu) cpumask_of_cpu(cpu) +#endif +#ifndef topology_core_siblings +#define topology_core_siblings(cpu) cpumask_of_cpu(cpu) +#endif + #endif /* _LINUX_TOPOLOGY_H */ -- cgit v1.2.3 From 564d9bdd89df4ceece5d126ff3b7db506ae06548 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Tue, 10 Jun 2008 14:04:14 +0200 Subject: nl80211: Fix comment merge error The comments ended up in the wrong place due to a merge error. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ea6517e58b04..aa8411e2a160 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -122,13 +122,13 @@ enum nl80211_commands { NL80211_CMD_NEW_STATION, NL80211_CMD_DEL_STATION, - /* add commands here */ - NL80211_CMD_GET_MPATH, NL80211_CMD_SET_MPATH, NL80211_CMD_NEW_MPATH, NL80211_CMD_DEL_MPATH, + /* add commands here */ + /* used to define NL80211_CMD_MAX below */ __NL80211_CMD_AFTER_LAST, NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 @@ -230,13 +230,13 @@ enum nl80211_attrs { NL80211_ATTR_MNTR_FLAGS, - /* add attributes here, update the policy in nl80211.c */ - NL80211_ATTR_MESH_ID, NL80211_ATTR_STA_PLINK_ACTION, NL80211_ATTR_MPATH_NEXT_HOP, NL80211_ATTR_MPATH_INFO, + /* add attributes here, update the policy in nl80211.c */ + __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; -- cgit v1.2.3 From 963f55178b25cb673ab438edaae4127b1d014bc1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 12 Jun 2008 09:47:00 +0800 Subject: iwlwifi: remove redundant flags regarding to FAT channel This patch removes redundant flags regarding to FAT channel. Use mac80211's flag instead. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 42 +++++++++-------------------- drivers/net/wireless/iwlwifi/iwl-dev.h | 14 ---------- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 19 ++++++++----- drivers/net/wireless/iwlwifi/iwl4965-base.c | 6 +++-- include/linux/ieee80211.h | 1 + 5 files changed, 31 insertions(+), 51 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 6c7617c1bb91..fad26f71d7ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -478,24 +478,7 @@ static int iwlcore_init_geos(struct iwl_priv *priv) if (ch->flags & EEPROM_CHANNEL_RADAR) geo_ch->flags |= IEEE80211_CHAN_RADAR; - switch (ch->fat_extension_channel) { - case HT_IE_EXT_CHANNEL_ABOVE: - /* only above is allowed, disable below */ - geo_ch->flags |= IEEE80211_CHAN_NO_FAT_BELOW; - break; - case HT_IE_EXT_CHANNEL_BELOW: - /* only below is allowed, disable above */ - geo_ch->flags |= IEEE80211_CHAN_NO_FAT_ABOVE; - break; - case HT_IE_EXT_CHANNEL_NONE: - /* fat not allowed: disable both*/ - geo_ch->flags |= (IEEE80211_CHAN_NO_FAT_ABOVE | - IEEE80211_CHAN_NO_FAT_BELOW); - break; - case HT_IE_EXT_CHANNEL_MAX: - /* both above and below are permitted */ - break; - } + geo_ch->flags |= ch->fat_extension_channel; if (ch->max_power_avg > priv->max_channel_txpower_limit) priv->max_channel_txpower_limit = @@ -507,7 +490,7 @@ static int iwlcore_init_geos(struct iwl_priv *priv) /* Save flags for reg domain usage */ geo_ch->orig_flags = geo_ch->flags; - IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0%X\n", + IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, geo_ch->center_freq, is_channel_a_band(ch) ? "5.2" : "2.4", geo_ch->flags & IEEE80211_CHAN_DISABLED ? @@ -552,6 +535,7 @@ static u8 is_single_rx_stream(struct iwl_priv *priv) (priv->current_ht_config.supp_mcs_set[2] == 0)) || priv->ps_mode == IWL_MIMO_PS_STATIC; } + static u8 iwl_is_channel_extension(struct iwl_priv *priv, enum ieee80211_band band, u16 channel, u8 extension_chan_offset) @@ -562,12 +546,12 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, if (!is_channel_valid(ch_info)) return 0; - if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE) - return 0; - - if ((ch_info->fat_extension_channel == extension_chan_offset) || - (ch_info->fat_extension_channel == HT_IE_EXT_CHANNEL_MAX)) - return 1; + if (extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) + return !(ch_info->fat_extension_channel & + IEEE80211_CHAN_NO_FAT_ABOVE); + else if (extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) + return !(ch_info->fat_extension_channel & + IEEE80211_CHAN_NO_FAT_BELOW); return 0; } @@ -579,7 +563,7 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, if ((!iwl_ht_conf->is_ht) || (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) || - (iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE)) + (iwl_ht_conf->extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE)) return 0; if (sta_ht_inf) { @@ -619,13 +603,13 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) /* Note: control channel is opposite of extension channel */ switch (ht_info->extension_chan_offset) { - case IWL_EXT_CHANNEL_OFFSET_ABOVE: + case IEEE80211_HT_IE_CHA_SEC_ABOVE: rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); break; - case IWL_EXT_CHANNEL_OFFSET_BELOW: + case IEEE80211_HT_IE_CHA_SEC_BELOW: rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; - case IWL_EXT_CHANNEL_OFFSET_NONE: + case IEEE80211_HT_IE_CHA_SEC_NONE: default: rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK; break; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index fa35193e5a7a..daa14d9e5a80 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -179,15 +179,6 @@ struct iwl4965_scan_power_info { s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */ }; -/* For fat_extension_channel */ -enum { - HT_IE_EXT_CHANNEL_NONE = 0, - HT_IE_EXT_CHANNEL_ABOVE, - HT_IE_EXT_CHANNEL_INVALID, - HT_IE_EXT_CHANNEL_BELOW, - HT_IE_EXT_CHANNEL_MAX -}; - /* * One for each channel, holds all channel setup data * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant @@ -782,11 +773,6 @@ struct iwl_kw { #define IWL_OPERATION_MODE_MIXED 2 #define IWL_OPERATION_MODE_20MHZ 3 -#define IWL_EXT_CHANNEL_OFFSET_NONE 0 -#define IWL_EXT_CHANNEL_OFFSET_ABOVE 1 -#define IWL_EXT_CHANNEL_OFFSET_RESERVE1 2 -#define IWL_EXT_CHANNEL_OFFSET_BELOW 3 - #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 11f9d9557a0e..cbb812f9f620 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -470,6 +470,11 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* Copy the run-time flags so they are there even on * invalid channels */ ch_info->flags = eeprom_ch_info[ch].flags; + /* First write that fat is not enabled, and then enable + * one by one */ + ch_info->fat_extension_channel = + (IEEE80211_CHAN_NO_FAT_ABOVE | + IEEE80211_CHAN_NO_FAT_BELOW); if (!(is_channel_valid(ch_info))) { IWL_DEBUG_INFO("Ch. %d Flags %x [%sGHz] - " @@ -534,12 +539,14 @@ int iwl_init_channel_map(struct iwl_priv *priv) for (ch = 0; ch < eeprom_ch_count; ch++) { if ((band == 6) && - ((eeprom_ch_index[ch] == 5) || - (eeprom_ch_index[ch] == 6) || - (eeprom_ch_index[ch] == 7))) - fat_extension_chan = HT_IE_EXT_CHANNEL_MAX; + ((eeprom_ch_index[ch] == 5) || + (eeprom_ch_index[ch] == 6) || + (eeprom_ch_index[ch] == 7))) + /* both are allowed: above and below */ + fat_extension_chan = 0; else - fat_extension_chan = HT_IE_EXT_CHANNEL_ABOVE; + fat_extension_chan = + IEEE80211_CHAN_NO_FAT_BELOW; /* Set up driver's info for lower half */ iwl_set_fat_chan_info(priv, ieeeband, @@ -551,7 +558,7 @@ int iwl_init_channel_map(struct iwl_priv *priv) iwl_set_fat_chan_info(priv, ieeeband, (eeprom_ch_index[ch] + 4), &(eeprom_ch_info[ch]), - HT_IE_EXT_CHANNEL_BELOW); + IEEE80211_CHAN_NO_FAT_ABOVE); } } diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 0acd42bd43c5..de40e893f09e 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -664,9 +664,11 @@ static void iwl4965_ht_conf(struct iwl_priv *priv, iwl_conf->extension_chan_offset = ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET; /* If no above or below channel supplied disable FAT channel */ - if (iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_ABOVE && - iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_BELOW) + if (iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_ABOVE && + iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_BELOW) { + iwl_conf->extension_chan_offset = IEEE80211_HT_IE_CHA_SEC_NONE; iwl_conf->supported_chan_width = 0; + } iwl_conf->tx_mimo_ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 9300f37cd7e8..8f2c20b4a15d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -320,6 +320,7 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10 /* 802.11n HT IE masks */ #define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_IE_CHA_SEC_NONE 0x00 #define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01 #define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03 #define IEEE80211_HT_IE_CHA_WIDTH 0x04 -- cgit v1.2.3 From fd7c8a40b2a63863f749e4d17f0d94d2e5ab1331 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 11 Jun 2008 14:21:56 -0700 Subject: mac80211: add helpers for frame control testing A few general categories: 1) ieee80211_has_* tests if particular fctl bits are set, the helpers are de in the same order as the fctl defines: A combined _has_a4 was also added to test when both FROMDS and TODS are set. 2) ieee80211_is_* is meant to test whether the frame control is of a certain ftype - data, mgmt, ctl, and two special helpers _is_data_qos, _is_data_pres which also test a subset of the stype space. When testing for a particular stype applicable only to one ftype, functions like ieee80211_is_ack have been added. Note that the ftype is also being checked in these helpers. They have been added for all mgmt and ctl stypes in the same order as the STYPE defines. 3) ieee80211_get_* is meant to take a struct ieee80211_hdr * and returns a pointer to somewhere in the struct, see get_SA, get_DA, get_qos_ctl. The intel wireless drivers had helpers that used this namespace, convert the all to use the new helpers and remove the byteshifting as they were defined in cpu-order rather than little-endian. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 36 +-- drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 17 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 28 +- drivers/net/wireless/iwlwifi/iwl-5000.c | 6 +- drivers/net/wireless/iwlwifi/iwl-helpers.h | 109 -------- drivers/net/wireless/iwlwifi/iwl-tx.c | 49 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 33 ++- include/linux/ieee80211.h | 392 ++++++++++++++++++++++++++-- 8 files changed, 457 insertions(+), 213 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 0ba6889dfd41..63f20370032d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -388,7 +388,7 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, u32 print_dump = 0; /* set to 1 to dump all frames' contents */ u32 hundred = 0; u32 dataframe = 0; - u16 fc; + __le16 fc; u16 seq_ctl; u16 channel; u16 phy_flags; @@ -407,7 +407,7 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, u8 *data = IWL_RX_DATA(pkt); /* MAC header */ - fc = le16_to_cpu(header->frame_control); + fc = header->frame_control; seq_ctl = le16_to_cpu(header->seq_ctrl); /* metadata */ @@ -431,8 +431,8 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, /* if data frame is to us and all is good, * (optionally) print summary for only 1 out of every 100 */ - if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) == - (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { + if (to_us && (fc & ~cpu_to_le16(IEEE80211_FCTL_PROTECTED)) == + cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { dataframe = 1; if (!group100) print_summary = 1; /* print each frame */ @@ -455,13 +455,13 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, if (hundred) title = "100Frames"; - else if (fc & IEEE80211_FCTL_RETRY) + else if (ieee80211_has_retry(fc)) title = "Retry"; - else if (ieee80211_is_assoc_response(fc)) + else if (ieee80211_is_assoc_resp(fc)) title = "AscRsp"; - else if (ieee80211_is_reassoc_response(fc)) + else if (ieee80211_is_reassoc_resp(fc)) title = "RasRsp"; - else if (ieee80211_is_probe_response(fc)) { + else if (ieee80211_is_probe_resp(fc)) { title = "PrbRsp"; print_dump = 1; /* dump frame contents */ } else if (ieee80211_is_beacon(fc)) { @@ -490,14 +490,14 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, if (dataframe) IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, " "len=%u, rssi=%d, chnl=%d, rate=%u, \n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], length, rssi, channel, rate); else { /* src/dst addresses assume managed mode */ IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, " "src=0x%02x, rssi=%u, tim=%lu usec, " "phy=0x%02x, chnl=%d\n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], header->addr3[5], rssi, tsf_low - priv->scan_start_tsf, phy_flags, channel); @@ -971,7 +971,7 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, u8 rts_retry_limit; u8 data_retry_limit; __le32 tx_flags; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; rate = iwl3945_rates[rate_index].plcp; tx_flags = cmd->cmd.tx.tx_flags; @@ -996,7 +996,7 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, else rts_retry_limit = 7; - if (ieee80211_is_probe_response(fc)) { + if (ieee80211_is_probe_resp(fc)) { data_retry_limit = 3; if (data_retry_limit < rts_retry_limit) rts_retry_limit = data_retry_limit; @@ -1006,12 +1006,12 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, if (priv->data_retry_limit != -1) data_retry_limit = priv->data_retry_limit; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_AUTH: - case IEEE80211_STYPE_DEAUTH: - case IEEE80211_STYPE_ASSOC_REQ: - case IEEE80211_STYPE_REASSOC_REQ: + if (ieee80211_is_mgmt(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): if (tx_flags & TX_CMD_FLG_RTS_MSK) { tx_flags &= ~TX_CMD_FLG_RTS_MSK; tx_flags |= TX_CMD_FLG_CTS_MSK; diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c index d601d31e0de9..202303117285 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c @@ -281,11 +281,11 @@ static u8 rs_tl_add_packet(struct iwl4965_lq_sta *lq_data, u32 time_diff; s32 index; struct iwl4965_traffic_load *tl = NULL; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; u8 tid; - if (ieee80211_is_qos_data(fc)) { - u8 *qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } else return MAX_TID_COUNT; @@ -794,7 +794,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev, struct iwl4965_scale_tbl_info tbl_type; struct iwl4965_scale_tbl_info *curr_tbl, *search_tbl; u8 active_index = 0; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; s32 tpt = 0; IWL_DEBUG_RATE_LIMIT("get frame ack response, update rate scale window\n"); @@ -1651,7 +1651,8 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, int high_tpt = IWL_INVALID_VALUE; u32 fail_count; s8 scale_action = 0; - u16 fc, rate_mask; + __le16 fc; + u16 rate_mask; u8 update_lq = 0; struct iwl4965_lq_sta *lq_sta; struct iwl4965_scale_tbl_info *tbl, *tbl1; @@ -1666,7 +1667,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) { /* Send management frames and broadcast/multicast data using * lowest rate. */ @@ -2100,7 +2101,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, struct ieee80211_conf *conf = &local->hw.conf; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct sta_info *sta; - u16 fc; + __le16 fc; struct iwl_priv *priv = (struct iwl_priv *)priv_rate; struct iwl4965_lq_sta *lq_sta; @@ -2112,7 +2113,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, /* Send management frames and broadcast/multicast data using lowest * rate. */ - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) || !sta || !sta->rate_ctrl_priv) { sel->rate_idx = rate_lowest_index(local, sband, sta); diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index df345cb6fd7d..ab5027345a01 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2388,7 +2388,7 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, u32 print_dump = 0; /* set to 1 to dump all frames' contents */ u32 hundred = 0; u32 dataframe = 0; - u16 fc; + __le16 fc; u16 seq_ctl; u16 channel; u16 phy_flags; @@ -2411,7 +2411,7 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, return; /* MAC header */ - fc = le16_to_cpu(header->frame_control); + fc = header->frame_control; seq_ctl = le16_to_cpu(header->seq_ctrl); /* metadata */ @@ -2436,8 +2436,8 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, /* if data frame is to us and all is good, * (optionally) print summary for only 1 out of every 100 */ - if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) == - (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { + if (to_us && (fc & ~cpu_to_le16(IEEE80211_FCTL_PROTECTED)) == + cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { dataframe = 1; if (!group100) print_summary = 1; /* print each frame */ @@ -2461,13 +2461,13 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, if (hundred) title = "100Frames"; - else if (fc & IEEE80211_FCTL_RETRY) + else if (ieee80211_has_retry(fc)) title = "Retry"; - else if (ieee80211_is_assoc_response(fc)) + else if (ieee80211_is_assoc_resp(fc)) title = "AscRsp"; - else if (ieee80211_is_reassoc_response(fc)) + else if (ieee80211_is_reassoc_resp(fc)) title = "RasRsp"; - else if (ieee80211_is_probe_response(fc)) { + else if (ieee80211_is_probe_resp(fc)) { title = "PrbRsp"; print_dump = 1; /* dump frame contents */ } else if (ieee80211_is_beacon(fc)) { @@ -2496,14 +2496,14 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, if (dataframe) IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, " "len=%u, rssi=%d, chnl=%d, rate=%u, \n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], length, rssi, channel, bitrate); else { /* src/dst addresses assume managed mode */ IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, " "src=0x%02x, rssi=%u, tim=%lu usec, " "phy=0x%02x, chnl=%d\n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], header->addr3[5], rssi, tsf_low - priv->scan_start_tsf, phy_flags, channel); @@ -3219,7 +3219,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le32_to_cpu(tx_resp->u.status); int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION; - u16 fc; + __le16 fc; struct ieee80211_hdr *hdr; u8 *qc = NULL; @@ -3235,9 +3235,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, memset(&info->status, 0, sizeof(info->status)); hdr = iwl_tx_queue_get_hdr(priv, txq_id, index); - fc = le16_to_cpu(hdr->frame_control); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 8c466b02bdff..438c3812c390 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1252,7 +1252,6 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, struct iwl5000_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le16_to_cpu(tx_resp->status.status); int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION; - u16 fc; struct ieee80211_hdr *hdr; u8 *qc = NULL; @@ -1268,9 +1267,8 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, memset(&info->status, 0, sizeof(info->status)); hdr = iwl_tx_queue_get_hdr(priv, txq_id, index); - fc = le16_to_cpu(hdr->frame_control); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(hdr->frame_control)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index eec423a98fe6..41eed6793328 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -145,115 +145,6 @@ static inline struct ieee80211_conf *ieee80211_get_hw_conf( return &hw->conf; } -#define QOS_CONTROL_LEN 2 - - -static inline int ieee80211_is_management(u16 fc) -{ - return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT; -} - -static inline int ieee80211_is_control(u16 fc) -{ - return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL; -} - -static inline int ieee80211_is_data(u16 fc) -{ - return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA; -} - -static inline int ieee80211_is_back_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ); -} - -static inline int ieee80211_is_probe_response(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP); -} - -static inline int ieee80211_is_probe_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_REQ); -} - -static inline int ieee80211_is_beacon(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON); -} - -static inline int ieee80211_is_atim(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ATIM); -} - -static inline int ieee80211_is_assoc_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_assoc_response(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_RESP); -} - -static inline int ieee80211_is_auth(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_deauth(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_disassoc(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_reassoc_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ); -} - -static inline int ieee80211_is_reassoc_response(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_RESP); -} - -static inline int ieee80211_is_qos_data(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_QOS_DATA); -} -/** - * ieee80211_get_qos_ctrl - get pointer to the QoS control field - * - * This function returns the pointer to 802.11 header QoS field (2 bytes) - * This function doesn't check whether hdr is a QoS hdr, use with care - * @hdr: struct ieee80211_hdr *hdr - * @hdr_len: header length - */ - -static inline u8 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr, int hdr_len) -{ - return ((u8 *) hdr + hdr_len - QOS_CONTROL_LEN); -} - static inline int iwl_check_bits(unsigned long field, unsigned long mask) { return ((field & mask) == mask) ? 1 : 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index e804bf8aea80..98c434c52a64 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -569,15 +569,15 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, struct ieee80211_hdr *hdr, int is_unicast, u8 std_id) { - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; __le32 tx_flags = tx_cmd->tx_flags; tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + if (ieee80211_is_mgmt(fc)) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - if (ieee80211_is_probe_response(fc) && + if (ieee80211_is_probe_resp(fc) && !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) tx_flags |= TX_CMD_FLG_TSF_MSK; } else { @@ -585,7 +585,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } - if (ieee80211_is_back_request(fc)) + if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; @@ -593,8 +593,8 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, if (ieee80211_get_morefrag(hdr)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - if (ieee80211_is_qos_data(fc)) { - u8 *qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { @@ -613,9 +613,8 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ || - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); else tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); @@ -634,7 +633,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, - u16 fc, int sta_id, + __le16 fc, int sta_id, int is_hcca) { u8 rts_retry_limit = 0; @@ -655,7 +654,7 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; - if (ieee80211_is_probe_response(fc)) { + if (ieee80211_is_probe_resp(fc)) { data_retry_limit = 3; if (data_retry_limit < rts_retry_limit) rts_retry_limit = data_retry_limit; @@ -670,11 +669,11 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; } else { - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_AUTH: - case IEEE80211_STYPE_DEAUTH: - case IEEE80211_STYPE_ASSOC_REQ: - case IEEE80211_STYPE_REASSOC_REQ: + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): if (tx_cmd->tx_flags & TX_CMD_FLG_RTS_MSK) { tx_cmd->tx_flags &= ~TX_CMD_FLG_RTS_MSK; tx_cmd->tx_flags |= TX_CMD_FLG_CTS_MSK; @@ -771,7 +770,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) u16 seq_number = 0; u8 id, hdr_len, unicast; u8 sta_id; - u16 fc; + __le16 fc; u8 wait_write_ptr = 0; u8 tid = 0; u8 *qc = NULL; @@ -798,19 +797,19 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) unicast = !is_multicast_ether_addr(hdr->addr1); id = 0; - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; #ifdef CONFIG_IWLWIFI_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX("Sending AUTH frame\n"); - else if (ieee80211_is_assoc_request(fc)) + else if (ieee80211_is_assoc_req(fc)) IWL_DEBUG_TX("Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_request(fc)) + else if (ieee80211_is_reassoc_req(fc)) IWL_DEBUG_TX("Sending REASSOC frame\n"); #endif /* drop all data frame if we are not associated */ - if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && + if (ieee80211_is_data(fc) && (!iwl_is_associated(priv) || ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && !priv->assoc_id) || !priv->assoc_station_added)) { @@ -820,7 +819,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); - hdr_len = ieee80211_get_hdrlen(fc); + hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); /* Find (or create) index into station table for destination station */ sta_id = iwl_get_sta_id(priv, hdr); @@ -834,8 +833,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX("station Id %d\n", sta_id); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, hdr_len); + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; @@ -938,7 +937,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* set is_hcca to 0; it probably will never be implemented */ iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0); - iwl_update_tx_stats(priv, fc, len); + iwl_update_tx_stats(priv, le16_to_cpu(fc), len); scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + offsetof(struct iwl_tx_cmd, scratch); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d0084bcb1eca..0a5bbe9ee938 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2431,15 +2431,15 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, struct ieee80211_hdr *hdr, int is_unicast, u8 std_id) { - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; __le32 tx_flags = cmd->cmd.tx.tx_flags; cmd->cmd.tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + if (ieee80211_is_mgmt(fc)) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - if (ieee80211_is_probe_response(fc) && + if (ieee80211_is_probe_resp(fc) && !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) tx_flags |= TX_CMD_FLG_TSF_MSK; } else { @@ -2451,8 +2451,8 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, if (ieee80211_get_morefrag(hdr)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - if (ieee80211_is_qos_data(fc)) { - u8 *qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); cmd->cmd.tx.tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { @@ -2471,9 +2471,8 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ || - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) cmd->cmd.tx.timeout.pm_frame_timeout = cpu_to_le16(3); else cmd->cmd.tx.timeout.pm_frame_timeout = cpu_to_le16(2); @@ -2564,7 +2563,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) u8 sta_id; u8 tid = 0; u16 seq_number = 0; - u16 fc; + __le16 fc; u8 wait_write_ptr = 0; u8 *qc = NULL; unsigned long flags; @@ -2589,28 +2588,28 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) unicast = !is_multicast_ether_addr(hdr->addr1); id = 0; - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; #ifdef CONFIG_IWL3945_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX("Sending AUTH frame\n"); - else if (ieee80211_is_assoc_request(fc)) + else if (ieee80211_is_assoc_req(fc)) IWL_DEBUG_TX("Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_request(fc)) + else if (ieee80211_is_reassoc_req(fc)) IWL_DEBUG_TX("Sending REASSOC frame\n"); #endif /* drop all data frame if we are not associated */ if ((!iwl3945_is_associated(priv) || ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && !priv->assoc_id)) && - ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) { + ieee80211_is_data(fc)) { IWL_DEBUG_DROP("Dropping - !iwl3945_is_associated\n"); goto drop_unlock; } spin_unlock_irqrestore(&priv->lock, flags); - hdr_len = ieee80211_get_hdrlen(fc); + hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); /* Find (or create) index into station table for destination station */ sta_id = iwl3945_get_sta_id(priv, hdr); @@ -2624,8 +2623,8 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) IWL_DEBUG_RATE("station Id %d\n", sta_id); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, hdr_len); + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; @@ -2746,7 +2745,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) sizeof(out_cmd->cmd.tx)); iwl3945_print_hex_dump(IWL_DL_TX, (u8 *)out_cmd->cmd.tx.hdr, - ieee80211_get_hdrlen(fc)); + ieee80211_get_hdrlen(le16_to_cpu(fc))); /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 8f2c20b4a15d..371237b0d8b9 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -98,6 +98,7 @@ #define IEEE80211_MAX_SSID_LEN 32 #define IEEE80211_MAX_MESH_ID_LEN 32 +#define IEEE80211_QOS_CTL_LEN 2 struct ieee80211_hdr { __le16 frame_control; @@ -109,6 +110,355 @@ struct ieee80211_hdr { u8 addr4[6]; } __attribute__ ((packed)); +/** + * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_tods(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0; +} + +/** + * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_fromds(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0; +} + +/** + * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_a4(__le16 fc) +{ + __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + return (fc & tmp) == tmp; +} + +/** + * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_morefrags(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0; +} + +/** + * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_retry(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0; +} + +/** + * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_pm(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0; +} + +/** + * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_moredata(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0; +} + +/** + * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_protected(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0; +} + +/** + * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_order(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0; +} + +/** + * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_mgmt(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT); +} + +/** + * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ctl(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL); +} + +/** + * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_qos(__le16 fc) +{ + /* + * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need + * to check the one bit + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); +} + +/** + * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_present(__le16 fc) +{ + /* + * mask with 0x40 and test that that bit is clear to only return true + * for the data-containing substypes. + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | 0x40)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); +} + +/** + * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); +} + +/** + * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); +} + +/** + * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); +} + +/** + * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); +} + +/** + * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); +} + +/** + * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_beacon(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); +} + +/** + * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_atim(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); +} + +/** + * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_disassoc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); +} + +/** + * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_auth(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); +} + +/** + * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_deauth(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); +} + +/** + * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_action(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); +} + +/** + * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_back_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + +/** + * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_back(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); +} + +/** + * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_pspoll(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); +} + +/** + * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_rts(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); +} + +/** + * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cts(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); +} + +/** + * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ack(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); +} + +/** + * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cfend(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); +} + +/** + * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cfendack(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); +} + +/** + * ieee80211_is_nullfunc - check if FTYPE=IEEE80211_FTYPE_DATA and STYPE=IEEE80211_STYPE_NULLFUNC + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_nullfunc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); +} struct ieee80211s_hdr { u8 flags; @@ -552,49 +902,55 @@ enum ieee80211_back_parties { #define WLAN_MAX_KEY_LEN 32 +/** + * ieee80211_get_qos_ctl - get pointer to qos control bytes + * @hdr: the frame + * + * The qos ctrl bytes come after the frame_control, duration, seq_num + * and 3 or 4 addresses of length ETH_ALEN. + * 3 addr: 2 + 2 + 2 + 3*6 = 24 + * 4 addr: 2 + 2 + 2 + 4*6 = 30 + */ +static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *)hdr + 30; + else + return (u8 *)hdr + 24; +} + /** * ieee80211_get_SA - get pointer to SA + * @hdr: the frame * * Given an 802.11 frame, this function returns the offset * to the source address (SA). It does not verify that the * header is long enough to contain the address, and the * header must be long enough to contain the frame control * field. - * - * @hdr: the frame */ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) { - __le16 fc = hdr->frame_control; - fc &= cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); - - switch (fc) { - case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): - return hdr->addr3; - case __constant_cpu_to_le16(IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS): + if (ieee80211_has_a4(hdr->frame_control)) return hdr->addr4; - default: - return hdr->addr2; - } + if (ieee80211_has_fromds(hdr->frame_control)) + return hdr->addr3; + return hdr->addr2; } /** * ieee80211_get_DA - get pointer to DA + * @hdr: the frame * * Given an 802.11 frame, this function returns the offset * to the destination address (DA). It does not verify that * the header is long enough to contain the address, and the * header must be long enough to contain the frame control * field. - * - * @hdr: the frame */ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) { - __le16 fc = hdr->frame_control; - fc &= cpu_to_le16(IEEE80211_FCTL_TODS); - - if (fc) + if (ieee80211_has_tods(hdr->frame_control)) return hdr->addr3; else return hdr->addr1; -- cgit v1.2.3 From 8b7b1e05b0454f232b8ae1e6ee134b7f0b38abfb Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 11 Jun 2008 14:21:56 -0700 Subject: mac80211: remove ieee80211_get_morefrag Replaced by the new helper ieee80211_has_morefrags which is more consistent with the intent of the function. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 4 ++-- drivers/net/wireless/rt2x00/rt2x00queue.c | 2 +- drivers/net/wireless/rtl8187_dev.c | 2 +- include/linux/ieee80211.h | 14 -------------- 5 files changed, 6 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 98c434c52a64..7296e2846ec3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -590,7 +590,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, tx_cmd->sta_id = std_id; - if (ieee80211_get_morefrag(hdr)) + if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { @@ -944,7 +944,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = iwl_get_dma_hi_address(scratch_phys); - if (!ieee80211_get_morefrag(hdr)) { + if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; if (qc) priv->stations[sta_id].tid[tid].seq_number = seq_number; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 0a5bbe9ee938..47cf4b997f50 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2448,7 +2448,7 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, } cmd->cmd.tx.sta_id = std_id; - if (ieee80211_get_morefrag(hdr)) + if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { @@ -2731,7 +2731,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; - if (!ieee80211_get_morefrag(hdr)) { + if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; if (qc) { priv->stations[sta_id].tid[tid].seq_number = seq_number; diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 2f3dd1d91a12..7b52039b01a6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -130,7 +130,7 @@ void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, /* * Check if more fragments are pending */ - if (ieee80211_get_morefrag(hdr)) { + if (ieee80211_has_morefrags(hdr->frame_control)) { __set_bit(ENTRY_TXD_BURST, &txdesc->flags); __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); } diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 0078c7e9918c..bec96d762c6c 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -181,7 +181,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) flags |= RTL8187_TX_FLAG_NO_ENCRYPT; flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; - if (ieee80211_get_morefrag((struct ieee80211_hdr *)skb->data)) + if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) flags |= RTL8187_TX_FLAG_MORE_FRAG; if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) { flags |= RTL8187_TX_FLAG_RTS; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 371237b0d8b9..2998e3b5f166 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -956,18 +956,4 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) return hdr->addr1; } -/** - * ieee80211_get_morefrag - determine whether the MOREFRAGS bit is set - * - * This function determines whether the "more fragments" bit is set - * in the frame. - * - * @hdr: the frame - */ -static inline int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) -{ - __le16 fc = hdr->frame_control; - return !!(fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)); -} - #endif /* IEEE80211_H */ -- cgit v1.2.3 From d6266281f8175e3ad68c28b20a609b278b47ade5 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 16 Jun 2008 17:11:50 -0700 Subject: udp: introduce a udp_hashfn function Currently the chain to store a UDP socket is calculated with simple (x & (UDP_HTABLE_SIZE - 1)). But taking net into account would make this calculation a bit more complex, so moving it into a function would help. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/udp.h | 5 +++++ net/ipv4/udp.c | 12 ++++++------ net/ipv6/udp.c | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 581ca2c14c52..9c94312b2de5 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -46,6 +46,11 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) #define UDP_HTABLE_SIZE 128 +static inline int udp_hashfn(const unsigned num) +{ + return num & (UDP_HTABLE_SIZE - 1); +} + struct udp_sock { /* inet_sock has to be the first member */ struct inet_sock inet; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index eba790dcd16b..d8f527d15701 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -134,7 +134,7 @@ static inline int __udp_lib_lport_inuse(struct net *net, __u16 num, struct sock *sk; struct hlist_node *node; - sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)]) + sk_for_each(sk, node, &udptable[udp_hashfn(num)]) if (net_eq(sock_net(sk), net) && sk->sk_hash == num) return 1; return 0; @@ -174,7 +174,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, for (i = 0; i < UDP_HTABLE_SIZE; i++) { int size = 0; - head = &udptable[rover & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[udp_hashfn(rover)]; if (hlist_empty(head)) goto gotit; @@ -211,7 +211,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, gotit: snum = rover; } else { - head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[udp_hashfn(snum)]; sk_for_each(sk2, node, head) if (sk2->sk_hash == snum && @@ -227,7 +227,7 @@ gotit: inet_sk(sk)->num = snum; sk->sk_hash = snum; if (sk_unhashed(sk)) { - head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[udp_hashfn(snum)]; sk_add_node(sk, head); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); } @@ -264,7 +264,7 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -1068,7 +1068,7 @@ static int __udp4_lib_mcast_deliver(struct sk_buff *skb, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]); + sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); dif = skb->dev->ifindex; sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (sk) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 09687f7a8564..6e4a822ba651 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -65,7 +65,7 @@ static struct sock *__udp6_lib_lookup(struct net *net, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -361,7 +361,7 @@ static int __udp6_lib_mcast_deliver(struct sk_buff *skb, struct in6_addr *saddr, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]); + sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); dif = inet6_iif(skb); sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (!sk) { -- cgit v1.2.3 From 19c7578fb22b0aef103222cae9b522f03ae489d6 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 16 Jun 2008 17:12:29 -0700 Subject: udp: add struct net argument to udp_hashfn Every caller already has this one. The new argument is currently unused, but this will be fixed shortly. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/udp.h | 2 +- net/ipv4/udp.c | 12 ++++++------ net/ipv6/udp.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 9c94312b2de5..3deccac2e815 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -46,7 +46,7 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) #define UDP_HTABLE_SIZE 128 -static inline int udp_hashfn(const unsigned num) +static inline int udp_hashfn(struct net *net, const unsigned num) { return num & (UDP_HTABLE_SIZE - 1); } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6b0acb438f34..11eabf136144 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -134,7 +134,7 @@ static inline int __udp_lib_lport_inuse(struct net *net, __u16 num, struct sock *sk; struct hlist_node *node; - sk_for_each(sk, node, &udptable[udp_hashfn(num)]) + sk_for_each(sk, node, &udptable[udp_hashfn(net, num)]) if (net_eq(sock_net(sk), net) && sk->sk_hash == num) return 1; return 0; @@ -174,7 +174,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, for (i = 0; i < UDP_HTABLE_SIZE; i++) { int size = 0; - head = &udptable[udp_hashfn(rover)]; + head = &udptable[udp_hashfn(net, rover)]; if (hlist_empty(head)) goto gotit; @@ -211,7 +211,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, gotit: snum = rover; } else { - head = &udptable[udp_hashfn(snum)]; + head = &udptable[udp_hashfn(net, snum)]; sk_for_each(sk2, node, head) if (sk2->sk_hash == snum && @@ -227,7 +227,7 @@ gotit: inet_sk(sk)->num = snum; sk->sk_hash = snum; if (sk_unhashed(sk)) { - head = &udptable[udp_hashfn(snum)]; + head = &udptable[udp_hashfn(net, snum)]; sk_add_node(sk, head); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); } @@ -264,7 +264,7 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -1068,7 +1068,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); + sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); dif = skb->dev->ifindex; sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (sk) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 80fb72c48976..432edaa882f6 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -65,7 +65,7 @@ static struct sock *__udp6_lib_lookup(struct net *net, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -362,7 +362,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); + sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); dif = inet6_iif(skb); sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (!sk) { -- cgit v1.2.3 From 0b4419162aa6c4204843f3a13b48d9ab821d3167 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 16 Jun 2008 17:14:11 -0700 Subject: netns: introduce the net_hash_mix "salt" for hashes There are many possible ways to add this "salt", thus I made this patch to be the last in the series to change it if required. Currently I propose to use the struct net pointer itself as this salt, but since this pointer is most often cache-line aligned, shift this right to eliminate the bits, that are most often zeroed. After this, simply add this mix to prepared hashfn-s. For CONFIG_NET_NS=n case this salt is 0 and no changes in hashfn appear. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/udp.h | 3 ++- include/net/inet6_hashtables.h | 3 ++- include/net/inet_hashtables.h | 5 +++-- include/net/inet_sock.h | 3 ++- include/net/netns/hash.h | 21 +++++++++++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 include/net/netns/hash.h (limited to 'include/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 3deccac2e815..0cf5c4c0ec81 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -38,6 +38,7 @@ struct udphdr { #ifdef __KERNEL__ #include #include +#include static inline struct udphdr *udp_hdr(const struct sk_buff *skb) { @@ -48,7 +49,7 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) static inline int udp_hashfn(struct net *net, const unsigned num) { - return num & (UDP_HTABLE_SIZE - 1); + return (num + net_hash_mix(net)) & (UDP_HTABLE_SIZE - 1); } struct udp_sock { diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 72f13a9928e4..e48989f04c24 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -24,6 +24,7 @@ #include #include +#include struct inet_hashinfo; @@ -36,7 +37,7 @@ static inline unsigned int inet6_ehashfn(struct net *net, return jhash_3words((__force u32)laddr->s6_addr32[3], (__force u32)faddr->s6_addr32[3], - ports, inet_ehash_secret); + ports, inet_ehash_secret + net_hash_mix(net)); } static inline int inet6_sk_ehashfn(const struct sock *sk) diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 26336cdcdc11..bb619d80f2e2 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -204,7 +205,7 @@ extern void inet_bind_bucket_destroy(struct kmem_cache *cachep, static inline int inet_bhashfn(struct net *net, const __u16 lport, const int bhash_size) { - return lport & (bhash_size - 1); + return (lport + net_hash_mix(net)) & (bhash_size - 1); } extern void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, @@ -213,7 +214,7 @@ extern void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, /* These can have wildcards, don't try too hard. */ static inline int inet_lhashfn(struct net *net, const unsigned short num) { - return num & (INET_LHTABLE_SIZE - 1); + return (num + net_hash_mix(net)) & (INET_LHTABLE_SIZE - 1); } static inline int inet_sk_listen_hashfn(const struct sock *sk) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 85bb420c5d86..643e26be058e 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -25,6 +25,7 @@ #include #include #include +#include /** struct ip_options - IP Options * @@ -178,7 +179,7 @@ static inline unsigned int inet_ehashfn(struct net *net, return jhash_3words((__force __u32) laddr, (__force __u32) faddr, ((__u32) lport) << 16 | (__force __u32)fport, - inet_ehash_secret); + inet_ehash_secret + net_hash_mix(net)); } static inline int inet_sk_ehashfn(const struct sock *sk) diff --git a/include/net/netns/hash.h b/include/net/netns/hash.h new file mode 100644 index 000000000000..548d78f2cc47 --- /dev/null +++ b/include/net/netns/hash.h @@ -0,0 +1,21 @@ +#ifndef __NET_NS_HASH_H__ +#define __NET_NS_HASH_H__ + +#include + +struct net; + +static inline unsigned net_hash_mix(struct net *net) +{ +#ifdef CONFIG_NET_NS + /* + * shift this right to eliminate bits, that are + * always zeroed + */ + + return (unsigned)(((unsigned long)net) >> L1_CACHE_SHIFT); +#else + return 0; +#endif +} +#endif -- cgit v1.2.3 From 87de87d5e47f94b4ea647a5bd1bc8dc1f7930db4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jun 2008 09:14:03 -0700 Subject: wext: Dispatch and handle compat ioctls entirely in net/wireless/wext.c Next we can kill the hacks in fs/compat_ioctl.c and also dispatch compat ioctls down into the driver and 80211 protocol helper layers in order to handle iw_point objects embedded in stream replies which need to be translated. Signed-off-by: David S. Miller --- fs/compat_ioctl.c | 6 --- include/linux/wireless.h | 13 ++++++ include/net/wext.h | 7 ++++ net/socket.c | 10 +++++ net/wireless/wext.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 97dba0d92348..8ab850bf2eee 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1757,12 +1757,6 @@ static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd, unsigned long a return sys_ioctl(fd, cmd, (unsigned long)tdata); } -struct compat_iw_point { - compat_caddr_t pointer; - __u16 length; - __u16 flags; -}; - static int do_wireless_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct iwreq __user *iwr; diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 4a95a0e5eeca..79d846875825 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -677,6 +677,19 @@ struct iw_point __u16 flags; /* Optional params */ }; +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT + +#include + +struct compat_iw_point { + compat_caddr_t pointer; + __u16 length; + __u16 flags; +}; +#endif +#endif + /* * A frequency * For numbers lower than 10^9, we encode the number in 'm' and diff --git a/include/net/wext.h b/include/net/wext.h index 80b31d826b7a..6d76a39a9c5b 100644 --- a/include/net/wext.h +++ b/include/net/wext.h @@ -12,6 +12,8 @@ extern int wext_proc_init(struct net *net); extern void wext_proc_exit(struct net *net); extern int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg); +extern int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg); #else static inline int wext_proc_init(struct net *net) { @@ -26,6 +28,11 @@ static inline int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned { return -EINVAL; } +static inline int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} #endif #endif /* __NET_WEXT_H */ diff --git a/net/socket.c b/net/socket.c index 66c4a8cf6db9..81fe82513046 100644 --- a/net/socket.c +++ b/net/socket.c @@ -90,6 +90,7 @@ #include #include +#include #include #include @@ -2210,10 +2211,19 @@ static long compat_sock_ioctl(struct file *file, unsigned cmd, { struct socket *sock = file->private_data; int ret = -ENOIOCTLCMD; + struct sock *sk; + struct net *net; + + sk = sock->sk; + net = sock_net(sk); if (sock->ops->compat_ioctl) ret = sock->ops->compat_ioctl(sock, cmd, arg); + if (ret == -ENOIOCTLCMD && + (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)) + ret = compat_wext_handle_ioctl(net, cmd, arg); + return ret; } #endif diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 09022cbb58ba..1a4636a9fcde 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1112,6 +1112,110 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, return ret; } +#ifdef CONFIG_COMPAT +static int compat_standard_call(struct net_device *dev, + struct iwreq *iwr, + unsigned int cmd, + iw_handler handler) +{ + const struct iw_ioctl_description *descr; + struct compat_iw_point *iwp_compat; + struct iw_request_info info; + struct iw_point iwp; + int err; + + descr = standard_ioctl + (cmd - SIOCIWFIRST); + + if (descr->header_type != IW_HEADER_TYPE_POINT) + return ioctl_standard_call(dev, iwr, cmd, handler); + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + info.cmd = cmd; + info.flags = 0; + + err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, &info); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + + return err; +} + +static int compat_private_call(struct net_device *dev, struct iwreq *iwr, + unsigned int cmd, iw_handler handler) +{ + const struct iw_priv_args *descr; + struct iw_request_info info; + int ret, extra_size; + + extra_size = get_priv_descr_and_size(dev, cmd, &descr); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + struct compat_iw_point *iwp_compat; + struct iw_point iwp; + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + ret = ioctl_private_iw_point(&iwp, cmd, descr, + handler, dev, &info, extra_size); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct iwreq iwr; + char *colon; + int ret; + + if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) + return -EFAULT; + + iwr.ifr_name[IFNAMSIZ-1] = 0; + colon = strchr(iwr.ifr_name, ':'); + if (colon) + *colon = 0; + + ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, + compat_standard_call, + compat_private_call); + + if (ret >= 0 && + IW_IS_GET(cmd) && + copy_to_user(argp, &iwr, sizeof(struct iwreq))) + return -EFAULT; + + return ret; +} +#endif + /************************* EVENT PROCESSING *************************/ /* * Process events generated by the wireless layer or the driver. -- cgit v1.2.3 From ccc580571cf0799d0460a085a7632b77753f083e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 16 Jun 2008 18:50:49 -0700 Subject: wext: Emit event stream entries correctly when compat. Three major portions to this change: 1) Add IW_EV_COMPAT_LCP_LEN, IW_EV_COMPAT_POINT_OFF, and IW_EV_COMPAT_POINT_LEN helper defines. 2) Delete iw_stream_check_add_*(), they are unused. 3) Add iw_request_info argument to iwe_stream_add_*(), and use it to size the event and pointer lengths correctly depending upon whether IW_REQUEST_FLAG_COMPAT is set or not. 4) The mechanical transformations to the drivers and wireless stack bits to get the iw_request_info passed down into the routines modified in #3. Also, explicit references to IW_EV_LCP_LEN are replaced with iwe_stream_lcp_len(info). With a lot of help and bug fixes from Masakazu Mokuno. Signed-off-by: David S. Miller --- drivers/net/ps3_gelic_wireless.c | 30 +++--- drivers/net/wireless/airo.c | 43 ++++++--- drivers/net/wireless/atmel.c | 24 +++-- drivers/net/wireless/hostap/hostap.h | 3 +- drivers/net/wireless/hostap/hostap_ap.c | 32 +++---- drivers/net/wireless/hostap/hostap_ioctl.c | 58 +++++------ drivers/net/wireless/libertas/scan.c | 36 +++---- drivers/net/wireless/orinoco.c | 30 +++--- drivers/net/wireless/prism54/isl_ioctl.c | 49 +++++----- drivers/net/wireless/rndis_wlan.c | 32 ++++--- drivers/net/wireless/wl3501_cs.c | 10 +- drivers/net/wireless/zd1201.c | 21 ++-- include/linux/wireless.h | 15 +++ include/net/iw_handler.h | 149 ++++++++++------------------- net/ieee80211/ieee80211_wx.c | 48 ++++++---- net/mac80211/ieee80211_i.h | 5 +- net/mac80211/mlme.c | 66 +++++++------ net/mac80211/wext.c | 2 +- 18 files changed, 345 insertions(+), 308 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index aa963ac1e37b..6b2dee0cf3a9 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -571,6 +571,7 @@ static void gelic_wl_parse_ie(u8 *data, size_t len, * independent format */ static char *gelic_wl_translate_scan(struct net_device *netdev, + struct iw_request_info *info, char *ev, char *stop, struct gelic_wl_scan_info *network) @@ -588,26 +589,26 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN); - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_ADDR_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN); /* ESSID */ iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = strnlen(scan->essid, 32); - ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid); + ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); /* FREQUENCY */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = be16_to_cpu(scan->channel); iwe.u.freq.e = 0; /* table value in MHz */ iwe.u.freq.i = 0; - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_FREQ_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_FREQ_LEN); /* RATES */ iwe.cmd = SIOCGIWRATE; iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; /* to stuff multiple values in one event */ - tmp = ev + IW_EV_LCP_LEN; + tmp = ev + iwe_stream_lcp_len(info); /* put them in ascendant order (older is first) */ i = 0; j = 0; @@ -620,16 +621,16 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, else rate = scan->rate[i++] & 0x7f; iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */ - tmp = iwe_stream_add_value(ev, tmp, stop, &iwe, + tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, IW_EV_PARAM_LEN); } while (j < network->rate_ext_len) { iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000; - tmp = iwe_stream_add_value(ev, tmp, stop, &iwe, + tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any rate */ - if (IW_EV_LCP_LEN < (tmp - ev)) + if (iwe_stream_lcp_len(info) < (tmp - ev)) ev = tmp; /* ENCODE */ @@ -639,7 +640,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid); + ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); /* MODE */ iwe.cmd = SIOCGIWMODE; @@ -649,7 +650,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_UINT_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_UINT_LEN); } /* QUAL */ @@ -659,7 +660,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, iwe.u.qual.level = be16_to_cpu(scan->rssi); iwe.u.qual.qual = be16_to_cpu(scan->rssi); iwe.u.qual.noise = 0; - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_QUAL_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_QUAL_LEN); /* RSN */ memset(&iwe, 0, sizeof(iwe)); @@ -669,7 +670,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, if (len) { iwe.cmd = IWEVGENIE; iwe.u.data.length = len; - ev = iwe_stream_add_point(ev, stop, &iwe, buf); + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); } } else { /* this scan info has IE data */ @@ -684,7 +685,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, memcpy(buf, ie_info.wpa.data, ie_info.wpa.len); iwe.cmd = IWEVGENIE; iwe.u.data.length = ie_info.wpa.len; - ev = iwe_stream_add_point(ev, stop, &iwe, buf); + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); } if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) { @@ -692,7 +693,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, memcpy(buf, ie_info.rsn.data, ie_info.rsn.len); iwe.cmd = IWEVGENIE; iwe.u.data.length = ie_info.rsn.len; - ev = iwe_stream_add_point(ev, stop, &iwe, buf); + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); } } @@ -737,7 +738,8 @@ static int gelic_wl_get_scan(struct net_device *netdev, if (wl->scan_age == 0 || time_after(scan_info->last_scanned + wl->scan_age, this_time)) - ev = gelic_wl_translate_scan(netdev, ev, stop, + ev = gelic_wl_translate_scan(netdev, info, + ev, stop, scan_info); else pr_debug("%s:entry too old\n", __func__); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index e30f8b79ea89..73d66a80c4a3 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -7156,6 +7156,7 @@ out: * format that the Wireless Tools will understand - Jean II */ static inline char *airo_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *current_ev, char *end_buf, BSSListRid *bss) @@ -7172,7 +7173,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* Other entries will be displayed in the order we give them */ @@ -7182,7 +7184,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -7192,7 +7195,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } /* Add frequency */ @@ -7203,7 +7207,8 @@ static inline char *airo_translate_scan(struct net_device *dev, */ iwe.u.freq.m = frequency_list[iwe.u.freq.m - 1] * 100000; iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); dBm = le16_to_cpu(bss->dBm); @@ -7223,7 +7228,8 @@ static inline char *airo_translate_scan(struct net_device *dev, | IW_QUAL_DBM; } iwe.u.qual.noise = ai->wstats.qual.noise; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -7232,11 +7238,12 @@ static inline char *airo_translate_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ - current_val = current_ev + IW_EV_LCP_LEN; + current_val = current_ev + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ @@ -7249,10 +7256,12 @@ static inline char *airo_translate_scan(struct net_device *dev, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, current_ev, + current_val, end_buf, + &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; /* Beacon interval */ @@ -7261,7 +7270,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "bcn_int=%d", bss->beaconInterval); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); kfree(buf); } @@ -7295,8 +7305,10 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = IWEVGENIE; iwe.u.data.length = min(info_element->len + 2, MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, (char *) info_element); + current_ev = iwe_stream_add_point( + info, current_ev, + end_buf, &iwe, + (char *) info_element); } break; @@ -7304,8 +7316,9 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = IWEVGENIE; iwe.u.data.length = min(info_element->len + 2, MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, (char *) info_element); + current_ev = iwe_stream_add_point( + info, current_ev, end_buf, + &iwe, (char *) info_element); break; default: @@ -7344,7 +7357,7 @@ static int airo_get_scan(struct net_device *dev, list_for_each_entry (net, &ai->network_list, list) { /* Translate to WE format this entry */ - current_ev = airo_translate_scan(dev, current_ev, + current_ev = airo_translate_scan(dev, info, current_ev, extra + dwrq->length, &net->bss); diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 7bb2646ae0ef..28b6ff3eaa37 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -2310,30 +2310,40 @@ static int atmel_get_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, 6); - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_ADDR_LEN); iwe.u.data.length = priv->BSSinfo[i].SSIDsize; if (iwe.u.data.length > 32) iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, priv->BSSinfo[i].SSID); + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, priv->BSSinfo[i].SSID); iwe.cmd = SIOCGIWMODE; iwe.u.mode = priv->BSSinfo[i].BSStype; - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_UINT_LEN); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = priv->BSSinfo[i].channel; iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.cmd = IWEVQUAL; iwe.u.qual.level = priv->BSSinfo[i].RSSI; iwe.u.qual.qual = iwe.u.qual.level; /* iwe.u.qual.noise = SOMETHING */ - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA , &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_QUAL_LEN); iwe.cmd = SIOCGIWENCODE; @@ -2342,7 +2352,9 @@ static int atmel_get_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL); + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, NULL); } /* Length of data */ diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h index 547ba84dc797..3a386a636cca 100644 --- a/drivers/net/wireless/hostap/hostap.h +++ b/drivers/net/wireless/hostap/hostap.h @@ -67,7 +67,8 @@ void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], struct iw_quality qual[], int buf_size, int aplist); -int prism2_ap_translate_scan(struct net_device *dev, char *buffer); +int prism2_ap_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *buffer); int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param); diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index 0acd9589c48c..06b23df8f69b 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -2420,7 +2420,8 @@ int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], /* Translate our list of Access Points & Stations to a card independant * format that the Wireless Tools will understand - Jean II */ -int prism2_ap_translate_scan(struct net_device *dev, char *buffer) +int prism2_ap_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *buffer) { struct hostap_interface *iface; local_info_t *local; @@ -2449,8 +2450,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); iwe.len = IW_EV_ADDR_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* Use the mode to indicate if it's a station or * an Access Point */ @@ -2461,8 +2462,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) else iwe.u.mode = IW_MODE_INFRA; iwe.len = IW_EV_UINT_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); /* Some quality */ memset(&iwe, 0, sizeof(iwe)); @@ -2477,8 +2478,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); iwe.u.qual.updated = sta->last_rx_updated; iwe.len = IW_EV_QUAL_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT if (sta->ap) { @@ -2486,8 +2487,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) iwe.cmd = SIOCGIWESSID; iwe.u.data.length = sta->u.ap.ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, sta->u.ap.ssid); memset(&iwe, 0, sizeof(iwe)); @@ -2497,10 +2498,9 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; else iwe.u.data.flags = IW_ENCODE_DISABLED; - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, - sta->u.ap.ssid - /* 0 byte memcpy */); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, + sta->u.ap.ssid); if (sta->u.ap.channel > 0 && sta->u.ap.channel <= FREQ_COUNT) { @@ -2510,7 +2510,7 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event( - current_ev, end_buf, &iwe, + info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); } @@ -2519,8 +2519,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) sprintf(buf, "beacon_interval=%d", sta->listen_interval); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); } #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index 0ca0bfeb0ada..ed52d98317cd 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -1793,6 +1793,7 @@ static int prism2_ioctl_siwscan(struct net_device *dev, #ifndef PRISM2_NO_STATION_MODES static char * __prism2_translate_scan(local_info_t *local, + struct iw_request_info *info, struct hfa384x_hostscan_result *scan, struct hostap_bss_info *bss, char *current_ev, char *end_buf) @@ -1823,7 +1824,7 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); /* Other entries will be displayed in the order we give them */ @@ -1832,7 +1833,8 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = SIOCGIWESSID; iwe.u.data.length = ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ssid); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWMODE; @@ -1847,8 +1849,8 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } memset(&iwe, 0, sizeof(iwe)); @@ -1864,8 +1866,8 @@ static char * __prism2_translate_scan(local_info_t *local, if (chan > 0) { iwe.u.freq.m = freq_list[chan - 1] * 100000; iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); } if (scan) { @@ -1884,8 +1886,8 @@ static char * __prism2_translate_scan(local_info_t *local, | IW_QUAL_NOISE_UPDATED | IW_QUAL_QUAL_INVALID | IW_QUAL_DBM; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); } memset(&iwe, 0, sizeof(iwe)); @@ -1895,13 +1897,13 @@ static char * __prism2_translate_scan(local_info_t *local, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); /* TODO: add SuppRates into BSS table */ if (scan) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWRATE; - current_val = current_ev + IW_EV_LCP_LEN; + current_val = current_ev + iwe_stream_lcp_len(info); pos = scan->sup_rates; for (i = 0; i < sizeof(scan->sup_rates); i++) { if (pos[i] == 0) @@ -1909,11 +1911,11 @@ static char * __prism2_translate_scan(local_info_t *local, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000); current_val = iwe_stream_add_value( - current_ev, current_val, end_buf, &iwe, + info, current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; } @@ -1924,15 +1926,15 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = IWEVCUSTOM; sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); if (local->last_scan_type == PRISM2_HOSTSCAN && (capabilities & WLAN_CAPABILITY_IBSS)) { @@ -1940,8 +1942,8 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = IWEVCUSTOM; sprintf(buf, "atim=%d", le16_to_cpu(scan->atim)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); } } kfree(buf); @@ -1950,16 +1952,16 @@ static char * __prism2_translate_scan(local_info_t *local, memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - current_ev = iwe_stream_add_point( - current_ev, end_buf, &iwe, bss->wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->wpa_ie); } if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - current_ev = iwe_stream_add_point( - current_ev, end_buf, &iwe, bss->rsn_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->rsn_ie); } return current_ev; @@ -1969,6 +1971,7 @@ static char * __prism2_translate_scan(local_info_t *local, /* Translate scan data returned from the card to a card independant * format that the Wireless Tools will understand - Jean II */ static inline int prism2_translate_scan(local_info_t *local, + struct iw_request_info *info, char *buffer, int buflen) { struct hfa384x_hostscan_result *scan; @@ -1999,13 +2002,14 @@ static inline int prism2_translate_scan(local_info_t *local, if (memcmp(bss->bssid, scan->bssid, ETH_ALEN) == 0) { bss->included = 1; current_ev = __prism2_translate_scan( - local, scan, bss, current_ev, end_buf); + local, info, scan, bss, current_ev, + end_buf); found++; } } if (!found) { current_ev = __prism2_translate_scan( - local, scan, NULL, current_ev, end_buf); + local, info, scan, NULL, current_ev, end_buf); } /* Check if there is space for one more entry */ if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { @@ -2023,7 +2027,7 @@ static inline int prism2_translate_scan(local_info_t *local, bss = list_entry(ptr, struct hostap_bss_info, list); if (bss->included) continue; - current_ev = __prism2_translate_scan(local, NULL, bss, + current_ev = __prism2_translate_scan(local, info, NULL, bss, current_ev, end_buf); /* Check if there is space for one more entry */ if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { @@ -2070,7 +2074,7 @@ static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, } local->scan_timestamp = 0; - res = prism2_translate_scan(local, extra, data->length); + res = prism2_translate_scan(local, info, extra, data->length); if (res >= 0) { data->length = res; @@ -2103,7 +2107,7 @@ static int prism2_ioctl_giwscan(struct net_device *dev, * Jean II */ /* Translate to WE format */ - res = prism2_ap_translate_scan(dev, extra); + res = prism2_ap_translate_scan(dev, info, extra); if (res >= 0) { printk(KERN_DEBUG "Scan result translation succeeded " "(length=%d)\n", res); diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c index d448c9702a0f..343ed38f772d 100644 --- a/drivers/net/wireless/libertas/scan.c +++ b/drivers/net/wireless/libertas/scan.c @@ -776,8 +776,9 @@ out: #define MAX_CUSTOM_LEN 64 static inline char *lbs_translate_scan(struct lbs_private *priv, - char *start, char *stop, - struct bss_descriptor *bss) + struct iw_request_info *info, + char *start, char *stop, + struct bss_descriptor *bss) { struct chan_freq_power *cfp; char *current_val; /* For rates */ @@ -801,24 +802,24 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); /* SSID */ iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IW_ESSID_MAX_SIZE); - start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); + start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid); /* Mode */ iwe.cmd = SIOCGIWMODE; iwe.u.mode = bss->mode; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); /* Frequency */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = (long)cfp->freq * 100000; iwe.u.freq.e = 1; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.cmd = IWEVQUAL; @@ -852,7 +853,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, nf = priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; iwe.u.qual.level = CAL_RSSI(snr, nf); } - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -862,9 +863,9 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, iwe.u.data.flags = IW_ENCODE_DISABLED; } iwe.u.data.length = 0; - start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); + start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid); - current_val = start + IW_EV_LCP_LEN; + current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; iwe.u.bitrate.fixed = 0; @@ -874,19 +875,19 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) { /* Bit rate given in 500 kb/s units */ iwe.u.bitrate.value = bss->rates[j] * 500000; - current_val = iwe_stream_add_value(start, current_val, - stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } if ((bss->mode == IW_MODE_ADHOC) && priv->adhoccreate && !lbs_ssid_cmp(priv->curbssparams.ssid, priv->curbssparams.ssid_len, bss->ssid, bss->ssid_len)) { iwe.u.bitrate.value = 22 * 500000; - current_val = iwe_stream_add_value(start, current_val, + current_val = iwe_stream_add_value(info, start, current_val, stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if((current_val - start) > IW_EV_LCP_LEN) + if ((current_val - start) > iwe_stream_lcp_len(info)) start = current_val; memset(&iwe, 0, sizeof(iwe)); @@ -895,7 +896,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, memcpy(buf, bss->wpa_ie, bss->wpa_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } memset(&iwe, 0, sizeof(iwe)); @@ -904,7 +905,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, memcpy(buf, bss->rsn_ie, bss->rsn_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } if (bss->mesh) { @@ -915,7 +916,8 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, p += snprintf(p, MAX_CUSTOM_LEN, "mesh-type: olpc"); iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, + &iwe, custom); } out: @@ -1036,7 +1038,7 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, } /* Translate to WE format this entry */ - next_ev = lbs_translate_scan(priv, ev, stop, iter_bss); + next_ev = lbs_translate_scan(priv, info, ev, stop, iter_bss); if (next_ev == NULL) continue; ev = next_ev; diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 6d13a0d15a0c..b047306bf386 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -4046,6 +4046,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev, * format that the Wireless Tools will understand - Jean II * Return message length or -errno for fatal errors */ static inline char *orinoco_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *current_ev, char *end_buf, union hermes_scan_info *bss, @@ -4062,7 +4063,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* Other entries will be displayed in the order we give them */ @@ -4072,7 +4074,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->a.essid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->a.essid); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -4082,7 +4085,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } channel = bss->s.channel; @@ -4091,7 +4095,7 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = channel_frequency[channel-1] * 100000; iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(current_ev, end_buf, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); } @@ -4106,7 +4110,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; else iwe.u.qual.qual = 0; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -4115,7 +4120,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->a.essid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->a.essid); /* Add EXTRA: Age to display seconds since last beacon/probe response * for given network. */ @@ -4126,11 +4132,12 @@ static inline char *orinoco_translate_scan(struct net_device *dev, jiffies_to_msecs(jiffies - last_scanned)); iwe.u.data.length = p - custom; if (iwe.u.data.length) - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, custom); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); /* Bit rate is not available in Lucent/Agere firmwares */ if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { - char *current_val = current_ev + IW_EV_LCP_LEN; + char *current_val = current_ev + iwe_stream_lcp_len(info); int i; int step; @@ -4149,12 +4156,13 @@ static inline char *orinoco_translate_scan(struct net_device *dev, break; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((bss->p.rates[i] & 0x7f) * 500000); - current_val = iwe_stream_add_value(current_ev, current_val, + current_val = iwe_stream_add_value(info, current_ev, + current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; } @@ -4190,7 +4198,7 @@ static int orinoco_ioctl_getscan(struct net_device *dev, list_for_each_entry(bss, &priv->bss_list, list) { /* Translate to WE format this entry */ - current_ev = orinoco_translate_scan(dev, current_ev, + current_ev = orinoco_translate_scan(dev, info, current_ev, extra + srq->length, &bss->bss, bss->last_scanned); diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 5b375b289036..97fa14e0a479 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -571,8 +571,9 @@ prism54_set_scan(struct net_device *dev, struct iw_request_info *info, */ static char * -prism54_translate_bss(struct net_device *ndev, char *current_ev, - char *end_buf, struct obj_bss *bss, char noise) +prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, + char *current_ev, char *end_buf, struct obj_bss *bss, + char noise) { struct iw_event iwe; /* Temporary buffer */ short cap; @@ -584,8 +585,8 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, memcpy(iwe.u.ap_addr.sa_data, bss->address, 6); iwe.u.ap_addr.sa_family = ARPHRD_ETHER; iwe.cmd = SIOCGIWAP; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* The following entries will be displayed in the same order we give them */ @@ -593,7 +594,7 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, iwe.u.data.length = bss->ssid.length; iwe.u.data.flags = 1; iwe.cmd = SIOCGIWESSID; - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, bss->ssid.octets); /* Capabilities */ @@ -610,9 +611,8 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, iwe.u.mode = IW_MODE_ADHOC; iwe.cmd = SIOCGIWMODE; if (iwe.u.mode) - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); /* Encryption capability */ if (cap & CAP_CRYPT) @@ -621,14 +621,15 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; iwe.cmd = SIOCGIWENCODE; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, NULL); /* Add frequency. (short) bss->channel is the frequency in MHz */ iwe.u.freq.m = bss->channel; iwe.u.freq.e = 6; iwe.cmd = SIOCGIWFREQ; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.u.qual.level = bss->rssi; @@ -636,20 +637,20 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, /* do a simple SNR for quality */ iwe.u.qual.qual = bss->rssi - noise; iwe.cmd = IWEVQUAL; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); /* Add WPA/RSN Information Element, if any */ wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie); if (wpa_ie_len > 0) { iwe.cmd = IWEVGENIE; iwe.u.data.length = min(wpa_ie_len, (size_t)MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, wpa_ie); } /* Do the bitrates */ { - char * current_val = current_ev + IW_EV_LCP_LEN; + char *current_val = current_ev + iwe_stream_lcp_len(info); int i; int mask; @@ -662,14 +663,14 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, for(i = 0; i < sizeof(scan_rate_list); i++) { if(bss->rates & mask) { iwe.u.bitrate.value = (scan_rate_list[i] * 500000); - current_val = iwe_stream_add_value(current_ev, current_val, - end_buf, &iwe, - IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value( + info, current_ev, current_val, + end_buf, &iwe, IW_EV_PARAM_LEN); } mask <<= 1; } /* Check if we added any event */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; } @@ -710,7 +711,7 @@ prism54_get_scan(struct net_device *ndev, struct iw_request_info *info, /* ok now, scan the list and translate its info */ for (i = 0; i < (int) bsslist->nr; i++) { - current_ev = prism54_translate_bss(ndev, current_ev, + current_ev = prism54_translate_bss(ndev, info, current_ev, extra + dwrq->length, &(bsslist->bsslist[i]), noise); @@ -2704,6 +2705,7 @@ prism2_ioctl_scan_req(struct net_device *ndev, struct prism2_hostapd_param *param) { islpci_private *priv = netdev_priv(ndev); + struct iw_request_info info; int i, rvalue; struct obj_bsslist *bsslist; u32 noise = 0; @@ -2727,9 +2729,12 @@ prism2_ioctl_scan_req(struct net_device *ndev, rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r); bsslist = r.ptr; + info.cmd = PRISM54_HOSTAPD; + info.flags = 0; + /* ok now, scan the list and translate its info */ for (i = 0; i < min(IW_MAX_AP, (int) bsslist->nr); i++) - current_ev = prism54_translate_bss(ndev, current_ev, + current_ev = prism54_translate_bss(ndev, &info, current_ev, extra + IW_SCAN_MAX_DATA, &(bsslist->bsslist[i]), noise); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index a36d2c85e26e..65c50025c88f 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -1648,7 +1648,9 @@ static int rndis_iw_set_scan(struct net_device *dev, static char *rndis_translate_scan(struct net_device *dev, - char *cev, char *end_buf, struct ndis_80211_bssid_ex *bssid) + struct iw_request_info *info, char *cev, + char *end_buf, + struct ndis_80211_bssid_ex *bssid) { #ifdef DEBUG struct usbnet *usbdev = dev->priv; @@ -1667,14 +1669,14 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN); - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_ADDR_LEN); devdbg(usbdev, "SSID(%d) %s", le32_to_cpu(bssid->ssid.length), bssid->ssid.essid); iwe.cmd = SIOCGIWESSID; iwe.u.essid.length = le32_to_cpu(bssid->ssid.length); iwe.u.essid.flags = 1; - cev = iwe_stream_add_point(cev, end_buf, &iwe, bssid->ssid.essid); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, bssid->ssid.essid); devdbg(usbdev, "MODE %d", le32_to_cpu(bssid->net_infra)); iwe.cmd = SIOCGIWMODE; @@ -1690,12 +1692,12 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.u.mode = IW_MODE_AUTO; break; } - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_UINT_LEN); devdbg(usbdev, "FREQ %d kHz", le32_to_cpu(bssid->config.ds_config)); iwe.cmd = SIOCGIWFREQ; dsconfig_to_freq(le32_to_cpu(bssid->config.ds_config), &iwe.u.freq); - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_FREQ_LEN); devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->rssi)); iwe.cmd = IWEVQUAL; @@ -1704,7 +1706,7 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_QUAL_LEN); devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->privacy)); iwe.cmd = SIOCGIWENCODE; @@ -1714,10 +1716,10 @@ static char *rndis_translate_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); devdbg(usbdev, "RATES:"); - current_val = cev + IW_EV_LCP_LEN; + current_val = cev + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; for (i = 0; i < sizeof(bssid->rates); i++) { if (bssid->rates[i] & 0x7f) { @@ -1725,13 +1727,13 @@ static char *rndis_translate_scan(struct net_device *dev, ((bssid->rates[i] & 0x7f) * 500000); devdbg(usbdev, " %d", iwe.u.bitrate.value); - current_val = iwe_stream_add_value(cev, + current_val = iwe_stream_add_value(info, cev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } } - if ((current_val - cev) > IW_EV_LCP_LEN) + if ((current_val - cev) > iwe_stream_lcp_len(info)) cev = current_val; beacon = le32_to_cpu(bssid->config.beacon_period); @@ -1739,14 +1741,14 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.cmd = IWEVCUSTOM; snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon); iwe.u.data.length = strlen(sbuf); - cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf); atim = le32_to_cpu(bssid->config.atim_window); devdbg(usbdev, "ATIM %d", atim); iwe.cmd = IWEVCUSTOM; snprintf(sbuf, sizeof(sbuf), "atim=%u", atim); iwe.u.data.length = strlen(sbuf); - cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf); ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); ie_len = min(bssid_len - (int)sizeof(*bssid), @@ -1760,7 +1762,7 @@ static char *rndis_translate_scan(struct net_device *dev, (ie->id == MFIE_TYPE_RSN) ? 2 : 1); iwe.cmd = IWEVGENIE; iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN); - cev = iwe_stream_add_point(cev, end_buf, &iwe, + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, (u8 *)ie); } @@ -1803,8 +1805,8 @@ static int rndis_iw_get_scan(struct net_device *dev, devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count); while (count && ((void *)bssid + bssid_len) <= (buf + len)) { - cev = rndis_translate_scan(dev, cev, extra + IW_SCAN_MAX_DATA, - bssid); + cev = rndis_translate_scan(dev, info, cev, + extra + IW_SCAN_MAX_DATA, bssid); bssid = (void *)bssid + bssid_len; bssid_len = le32_to_cpu(bssid->length); count--; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 42a36b3f3ff7..377141995e36 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1624,25 +1624,25 @@ static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, this->bss_set[i].bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, + current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_ADDR_LEN); iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = this->bss_set[i].ssid.el.len; - current_ev = iwe_stream_add_point(current_ev, + current_ev = iwe_stream_add_point(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, this->bss_set[i].ssid.essid); iwe.cmd = SIOCGIWMODE; iwe.u.mode = this->bss_set[i].bss_type; - current_ev = iwe_stream_add_event(current_ev, + current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_UINT_LEN); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = this->bss_set[i].ds_pset.chan; iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, + current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_FREQ_LEN); iwe.cmd = SIOCGIWENCODE; @@ -1651,7 +1651,7 @@ static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, + current_ev = iwe_stream_add_point(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL); } diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index d5c0c66188ca..07e4d1f73207 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -1152,32 +1152,36 @@ static int zd1201_get_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6); - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_ADDR_LEN); iwe.cmd = SIOCGIWESSID; iwe.u.data.length = zd->rxdata[i+16]; iwe.u.data.flags = 1; - cev = iwe_stream_add_point(cev, end_buf, &iwe, zd->rxdata+i+18); + cev = iwe_stream_add_point(info, cev, end_buf, + &iwe, zd->rxdata+i+18); iwe.cmd = SIOCGIWMODE; if (zd->rxdata[i+14]&0x01) iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_UINT_LEN); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = zd->rxdata[i+0]; iwe.u.freq.e = 0; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_FREQ_LEN); iwe.cmd = SIOCGIWRATE; iwe.u.bitrate.fixed = 0; iwe.u.bitrate.disabled = 0; for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) { iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000; - cev=iwe_stream_add_event(cev, end_buf, &iwe, - IW_EV_PARAM_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_PARAM_LEN); } iwe.cmd = SIOCGIWENCODE; @@ -1186,14 +1190,15 @@ static int zd1201_get_scan(struct net_device *dev, iwe.u.data.flags = IW_ENCODE_ENABLED; else iwe.u.data.flags = IW_ENCODE_DISABLED; - cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); iwe.cmd = IWEVQUAL; iwe.u.qual.qual = zd->rxdata[i+4]; iwe.u.qual.noise= zd->rxdata[i+2]/10-100; iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100; iwe.u.qual.updated = 7; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_QUAL_LEN); } if (!enabled_save) diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 79d846875825..d7958f9b52cb 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -1113,6 +1113,21 @@ struct iw_event #define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ IW_EV_POINT_OFF) +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT +struct __compat_iw_event { + __u16 len; /* Real length of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + compat_caddr_t pointer; +}; +#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) +#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) +#define IW_EV_COMPAT_POINT_LEN \ + (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ + IW_EV_COMPAT_POINT_OFF) +#endif +#endif + /* Size of the Event prefix when packed in stream */ #define IW_EV_LCP_PK_LEN (4) /* Size of the various events when packed in stream */ diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index c99a8eec84e7..51b9a37de991 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -478,105 +478,58 @@ extern void wireless_spy_update(struct net_device * dev, * Function that are so simple that it's more efficient inlining them */ -/*------------------------------------------------------------------*/ -/* - * Wrapper to add an Wireless Event to a stream of events. - */ -static inline char * -iwe_stream_add_event(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len) /* Real size of payload */ +static inline int iwe_stream_lcp_len(struct iw_request_info *info) { - /* Check if it's possible */ - if(likely((stream + event_len) < ends)) { - iwe->len = event_len; - /* Beware of alignement issues on 64 bits */ - memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN, - event_len - IW_EV_LCP_LEN); - stream += event_len; - } - return stream; +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) + return IW_EV_COMPAT_LCP_LEN; +#endif + return IW_EV_LCP_LEN; } -/*------------------------------------------------------------------*/ -/* - * Wrapper to add an short Wireless Event containing a pointer to a - * stream of events. - */ -static inline char * -iwe_stream_add_point(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload length + flags */ - char * extra) /* More payload */ +static inline int iwe_stream_point_len(struct iw_request_info *info) { - int event_len = IW_EV_POINT_LEN + iwe->u.data.length; - /* Check if it's possible */ - if(likely((stream + event_len) < ends)) { - iwe->len = event_len; - memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF, - IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); - stream += event_len; - } - return stream; +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) + return IW_EV_COMPAT_POINT_LEN; +#endif + return IW_EV_POINT_LEN; } -/*------------------------------------------------------------------*/ -/* - * Wrapper to add a value to a Wireless Event in a stream of events. - * Be careful, this one is tricky to use properly : - * At the first run, you need to have (value = event + IW_EV_LCP_LEN). - */ -static inline char * -iwe_stream_add_value(char * event, /* Event in the stream */ - char * value, /* Value in event */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len) /* Real size of payload */ +static inline int iwe_stream_event_len_adjust(struct iw_request_info *info, + int event_len) { - /* Don't duplicate LCP */ - event_len -= IW_EV_LCP_LEN; - - /* Check if it's possible */ - if(likely((value + event_len) < ends)) { - /* Add new value */ - memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); - value += event_len; - /* Patch LCP */ - iwe->len = value - event; - memcpy(event, (char *) iwe, IW_EV_LCP_LEN); +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) { + event_len -= IW_EV_LCP_LEN; + event_len += IW_EV_COMPAT_LCP_LEN; } - return value; +#endif + + return event_len; } /*------------------------------------------------------------------*/ /* * Wrapper to add an Wireless Event to a stream of events. - * Same as above, with explicit error check... */ static inline char * -iwe_stream_check_add_event(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len, /* Size of payload */ - int * perr) /* Error report */ +iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends, + struct iw_event *iwe, int event_len) { - /* Check if it's possible, set error if not */ + int lcp_len = iwe_stream_lcp_len(info); + + event_len = iwe_stream_event_len_adjust(info, event_len); + + /* Check if it's possible */ if(likely((stream + event_len) < ends)) { iwe->len = event_len; /* Beware of alignement issues on 64 bits */ memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN, - event_len - IW_EV_LCP_LEN); + memcpy(stream + lcp_len, &iwe->u, + event_len - lcp_len); stream += event_len; - } else - *perr = -E2BIG; + } return stream; } @@ -584,27 +537,25 @@ iwe_stream_check_add_event(char * stream, /* Stream of events */ /* * Wrapper to add an short Wireless Event containing a pointer to a * stream of events. - * Same as above, with explicit error check... */ static inline char * -iwe_stream_check_add_point(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload length + flags */ - char * extra, /* More payload */ - int * perr) /* Error report */ +iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends, + struct iw_event *iwe, char *extra) { - int event_len = IW_EV_POINT_LEN + iwe->u.data.length; + int event_len = iwe_stream_point_len(info) + iwe->u.data.length; + int point_len = iwe_stream_point_len(info); + int lcp_len = iwe_stream_lcp_len(info); + /* Check if it's possible */ if(likely((stream + event_len) < ends)) { iwe->len = event_len; memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF, + memcpy(stream + lcp_len, + ((char *) &iwe->u) + IW_EV_POINT_OFF, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); + memcpy(stream + point_len, extra, iwe->u.data.length); stream += event_len; - } else - *perr = -E2BIG; + } return stream; } @@ -613,29 +564,25 @@ iwe_stream_check_add_point(char * stream, /* Stream of events */ * Wrapper to add a value to a Wireless Event in a stream of events. * Be careful, this one is tricky to use properly : * At the first run, you need to have (value = event + IW_EV_LCP_LEN). - * Same as above, with explicit error check... */ static inline char * -iwe_stream_check_add_value(char * event, /* Event in the stream */ - char * value, /* Value in event */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len, /* Size of payload */ - int * perr) /* Error report */ +iwe_stream_add_value(struct iw_request_info *info, char *event, char *value, + char *ends, struct iw_event *iwe, int event_len) { + int lcp_len = iwe_stream_lcp_len(info); + /* Don't duplicate LCP */ event_len -= IW_EV_LCP_LEN; /* Check if it's possible */ if(likely((value + event_len) < ends)) { /* Add new value */ - memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); + memcpy(value, &iwe->u, event_len); value += event_len; /* Patch LCP */ iwe->len = value - event; - memcpy(event, (char *) iwe, IW_EV_LCP_LEN); - } else - *perr = -E2BIG; + memcpy(event, (char *) iwe, lcp_len); + } return value; } diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index 822606b615ca..973832dd7faf 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c @@ -43,8 +43,9 @@ static const char *ieee80211_modes[] = { #define MAX_CUSTOM_LEN 64 static char *ieee80211_translate_scan(struct ieee80211_device *ieee, - char *start, char *stop, - struct ieee80211_network *network) + char *start, char *stop, + struct ieee80211_network *network, + struct iw_request_info *info) { char custom[MAX_CUSTOM_LEN]; char *p; @@ -57,7 +58,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); /* Remaining entries will be displayed in the order we provide them */ @@ -66,17 +67,19 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.data.flags = 1; if (network->flags & NETWORK_EMPTY_ESSID) { iwe.u.data.length = sizeof(""); - start = iwe_stream_add_point(start, stop, &iwe, ""); + start = iwe_stream_add_point(info, start, stop, + &iwe, ""); } else { iwe.u.data.length = min(network->ssid_len, (u8) 32); - start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); } /* Add the protocol name */ iwe.cmd = SIOCGIWNAME; snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -86,7 +89,8 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, else iwe.u.mode = IW_MODE_ADHOC; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + start = iwe_stream_add_event(info, start, stop, + &iwe, IW_EV_UINT_LEN); } /* Add channel and frequency */ @@ -95,7 +99,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel); iwe.u.freq.e = 6; iwe.u.freq.i = 0; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -104,12 +108,13 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); /* Add basic and extended rates */ /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ - current_val = start + IW_EV_LCP_LEN; + current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; @@ -124,17 +129,19 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } for (; j < network->rates_ex_len; j++) { rate = network->rates_ex[j] & 0x7F; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any rate */ - if((current_val - start) > IW_EV_LCP_LEN) + if ((current_val - start) > iwe_stream_lcp_len(info)) start = current_val; /* Add quality statistics */ @@ -181,14 +188,14 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.qual.level = network->stats.signal; } - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); iwe.cmd = IWEVCUSTOM; p = custom; iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); memset(&iwe, 0, sizeof(iwe)); if (network->wpa_ie_len) { @@ -196,7 +203,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, memcpy(buf, network->wpa_ie, network->wpa_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->wpa_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } memset(&iwe, 0, sizeof(iwe)); @@ -205,7 +212,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, memcpy(buf, network->rsn_ie, network->rsn_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->rsn_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } /* Add EXTRA: Age to display seconds since last beacon/probe response @@ -217,7 +224,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, jiffies_to_msecs(jiffies - network->last_scanned)); iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); /* Add spectrum management information */ iwe.cmd = -1; @@ -238,7 +245,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, if (iwe.cmd == IWEVCUSTOM) { iwe.u.data.length = p - custom; - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); } return start; @@ -272,7 +279,8 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, if (ieee->scan_age == 0 || time_after(network->last_scanned + ieee->scan_age, jiffies)) - ev = ieee80211_translate_scan(ieee, ev, stop, network); + ev = ieee80211_translate_scan(ieee, ev, stop, network, + info); else IEEE80211_DEBUG_SCAN("Not showing network '%s (" "%s)' due to age (%dms).\n", diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 14fccf16b80f..80a9e7c07b47 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "key.h" #include "sta_info.h" @@ -867,7 +868,9 @@ int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid); int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); void ieee80211_sta_req_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta); -int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); +int ieee80211_sta_scan_results(struct net_device *dev, + struct iw_request_info *info, + char *buf, size_t len); ieee80211_rx_result ieee80211_sta_rx_scan( struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 55659a730dc1..e06d6450f215 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4087,6 +4087,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) static char * ieee80211_sta_scan_result(struct net_device *dev, + struct iw_request_info *info, struct ieee80211_sta_bss *bss, char *current_ev, char *end_buf) { @@ -4101,7 +4102,7 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); memset(&iwe, 0, sizeof(iwe)); @@ -4109,13 +4110,13 @@ ieee80211_sta_scan_result(struct net_device *dev, if (bss_mesh_cfg(bss)) { iwe.u.data.length = bss_mesh_id_len(bss); iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss_mesh_id(bss)); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss_mesh_id(bss)); } else { iwe.u.data.length = bss->ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); } if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) @@ -4128,22 +4129,22 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = bss->freq; iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVQUAL; @@ -4151,7 +4152,7 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.u.qual.level = bss->signal; iwe.u.qual.noise = bss->noise; iwe.u.qual.updated = local->wstats_flags; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); memset(&iwe, 0, sizeof(iwe)); @@ -4161,35 +4162,36 @@ ieee80211_sta_scan_result(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ""); if (bss && bss->wpa_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->wpa_ie); } if (bss && bss->rsn_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->rsn_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->rsn_ie); } if (bss && bss->ht_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->ht_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->ht_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ht_ie); } if (bss && bss->supp_rates_len > 0) { /* display all supported rates in readable format */ - char *p = current_ev + IW_EV_LCP_LEN; + char *p = current_ev + iwe_stream_lcp_len(info); int i; memset(&iwe, 0, sizeof(iwe)); @@ -4200,7 +4202,7 @@ ieee80211_sta_scan_result(struct net_device *dev, for (i = 0; i < bss->supp_rates_len; i++) { iwe.u.bitrate.value = ((bss->supp_rates[i] & 0x7f) * 500000); - p = iwe_stream_add_value(current_ev, p, + p = iwe_stream_add_value(info, current_ev, p, end_buf, &iwe, IW_EV_PARAM_LEN); } current_ev = p; @@ -4214,7 +4216,8 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); kfree(buf); } @@ -4229,31 +4232,36 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "Mesh network (version %d)", cfg[0]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Path Selection Protocol ID: " "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], cfg[4]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Path Selection Metric ID: " "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], cfg[8]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Congestion Control Mode ID: " "0x%02X%02X%02X%02X", cfg[9], cfg[10], cfg[11], cfg[12]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Channel Precedence: " "0x%02X%02X%02X%02X", cfg[13], cfg[14], cfg[15], cfg[16]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); kfree(buf); } @@ -4263,7 +4271,9 @@ ieee80211_sta_scan_result(struct net_device *dev, } -int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) +int ieee80211_sta_scan_results(struct net_device *dev, + struct iw_request_info *info, + char *buf, size_t len) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); char *current_ev = buf; @@ -4276,8 +4286,8 @@ int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) spin_unlock_bh(&local->sta_bss_lock); return -E2BIG; } - current_ev = ieee80211_sta_scan_result(dev, bss, current_ev, - end_buf); + current_ev = ieee80211_sta_scan_result(dev, info, bss, + current_ev, end_buf); } spin_unlock_bh(&local->sta_bss_lock); return current_ev - buf; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5af3862e7191..f47d13bdf7f7 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -567,7 +567,7 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev, if (local->sta_sw_scanning || local->sta_hw_scanning) return -EAGAIN; - res = ieee80211_sta_scan_results(dev, extra, data->length); + res = ieee80211_sta_scan_results(dev, info, extra, data->length); if (res >= 0) { data->length = res; return 0; -- cgit v1.2.3 From df3bc8bd8f8fd17e9b22859d82af38fa702e75b7 Mon Sep 17 00:00:00 2001 From: Chas Williams Date: Tue, 17 Jun 2008 16:19:24 -0700 Subject: atm: [suni] add support for setting loopback and framing modes Signed-off-by: Chas Williams Signed-off-by: David S. Miller --- drivers/atm/suni.c | 121 +++++++++++++++++++++++++++++++++++++++++++------- drivers/atm/suni.h | 31 +++++++++++-- include/linux/sonet.h | 2 +- 3 files changed, 133 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c index 6097f6877a44..6dd3f5919968 100644 --- a/drivers/atm/suni.c +++ b/drivers/atm/suni.c @@ -1,8 +1,14 @@ -/* drivers/atm/suni.c - PMC PM5346 SUNI (PHY) driver */ +/* + * drivers/atm/suni.c - S/UNI PHY driver + * + * Supports the following: + * PMC PM5346 S/UNI LITE + * PMC PM5350 S/UNI 155 ULTRA + * PMC PM5355 S/UNI 622 + */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - #include #include #include @@ -146,25 +152,105 @@ static int get_diag(struct atm_dev *dev,void __user *arg) static int set_loopback(struct atm_dev *dev,int mode) { unsigned char control; + int reg, dle, lle; + + if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { + reg = SUNI_MCM; + dle = SUNI_MCM_DLE; + lle = SUNI_MCM_LLE; + } else { + reg = SUNI_MCT; + dle = SUNI_MCT_DLE; + lle = SUNI_MCT_LLE; + } - control = GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE); + control = dev->ops->phy_get(dev, reg) & ~(dle | lle); switch (mode) { case ATM_LM_NONE: break; case ATM_LM_LOC_PHY: - control |= SUNI_MCT_DLE; + control |= dle; break; case ATM_LM_RMT_PHY: - control |= SUNI_MCT_LLE; + control |= lle; break; default: return -EINVAL; } - PUT(control,MCT); + dev->ops->phy_put(dev, control, reg); PRIV(dev)->loop_mode = mode; return 0; } +/* + * SONET vs. SDH Configuration + * + * Z0INS (register 0x06): 0 for SONET, 1 for SDH + * ENSS (register 0x3D): 0 for SONET, 1 for SDH + * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) + * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) + * S[1:0] (register 0x46): 00 for SONET, 10 for SDH + */ + +static int set_sonet(struct atm_dev *dev) +{ + if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { + PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC); + PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); + PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); + } + + REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, + SUNI_TPOP_S_SONET, TPOP_APM); + + return 0; +} + +static int set_sdh(struct atm_dev *dev) +{ + if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { + PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC); + PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); + PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); + } + + REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, + SUNI_TPOP_S_SDH, TPOP_APM); + + return 0; +} + + +static int get_framing(struct atm_dev *dev, void __user *arg) +{ + int framing; + unsigned char s; + + + s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT; + if (s == SUNI_TPOP_S_SONET) + framing = SONET_FRAME_SONET; + else + framing = SONET_FRAME_SDH; + + return put_user(framing, (int __user *) arg) ? -EFAULT : 0; +} + +static int set_framing(struct atm_dev *dev, void __user *arg) +{ + int mode; + + if (get_user(mode, (int __user *) arg)) + return -EFAULT; + + if (mode == SONET_FRAME_SONET) + return set_sonet(dev); + else if (mode == SONET_FRAME_SDH) + return set_sdh(dev); + + return -EINVAL; +} + static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) { @@ -179,14 +265,16 @@ static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) case SONET_GETDIAG: return get_diag(dev,arg); case SONET_SETFRAMING: - if ((int)(unsigned long)arg != SONET_FRAME_SONET) return -EINVAL; - return 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + return set_framing(dev, arg); case SONET_GETFRAMING: - return put_user(SONET_FRAME_SONET,(int __user *)arg) ? - -EFAULT : 0; + return get_framing(dev, arg); case SONET_GETFRSENSE: return -EINVAL; case ATM_SETLOOP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; return set_loopback(dev,(int)(unsigned long)arg); case ATM_GETLOOP: return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? @@ -220,10 +308,6 @@ static int suni_start(struct atm_dev *dev) unsigned long flags; int first; - if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL))) - return -ENOMEM; - - PRIV(dev)->dev = dev; spin_lock_irqsave(&sunis_lock,flags); first = !sunis; PRIV(dev)->next = sunis; @@ -284,16 +368,21 @@ int suni_init(struct atm_dev *dev) { unsigned char mri; + if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL))) + return -ENOMEM; + PRIV(dev)->dev = dev; + mri = GET(MRI); /* reset SUNI */ + PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT; PUT(mri | SUNI_MRI_RESET,MRI); PUT(mri,MRI); PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */ - REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET, - TPOP_APM); /* use SONET */ + set_sonet(dev); REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP, TACP_IUCHP); /* idle cells */ PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP); dev->phy = &suni_ops; + return 0; } diff --git a/drivers/atm/suni.h b/drivers/atm/suni.h index efa79bfae75b..7e3e656b3993 100644 --- a/drivers/atm/suni.h +++ b/drivers/atm/suni.h @@ -1,7 +1,8 @@ -/* drivers/atm/suni.h - PMC PM5346 SUNI (PHY) declarations */ +/* + * drivers/atm/suni.h - S/UNI PHY driver + */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - #ifndef DRIVER_ATM_SUNI_H #define DRIVER_ATM_SUNI_H @@ -39,7 +40,8 @@ #define SUNI_RLOP_LFM 0x1F /* RLOP Line FEBE MSB */ #define SUNI_TLOP_CTRL 0x20 /* TLOP Control */ #define SUNI_TLOP_DIAG 0x21 /* TLOP Diagnostic */ - /* 0x22-0x2F reserved */ + /* 0x22-0x27 reserved */ +#define SUNI_SSTB_CTRL 0x28 #define SUNI_RPOP_SC 0x30 /* RPOP Status/Control */ #define SUNI_RPOP_IS 0x31 /* RPOP Interrupt Status */ /* 0x32 reserved */ @@ -52,6 +54,7 @@ #define SUNI_RPOP_PFM 0x3B /* RPOP Path FEBE MSB */ /* 0x3C reserved */ #define SUNI_RPOP_PBC 0x3D /* RPOP Path BIP-8 Configuration */ +#define SUNI_RPOP_RC 0x3D /* RPOP Ring Control (PM5355) */ /* 0x3E-0x3F reserved */ #define SUNI_TPOP_CD 0x40 /* TPOP Control/Diagnostic */ #define SUNI_TPOP_PC 0x41 /* TPOP Pointer Control */ @@ -82,7 +85,8 @@ #define SUNI_TACP_TCC 0x65 /* TACP Transmit Cell Counter */ #define SUNI_TACP_TCCM 0x66 /* TACP Transmit Cell Counter MSB */ #define SUNI_TACP_CFG 0x67 /* TACP Configuration */ - /* 0x68-0x7F reserved */ +#define SUNI_SPTB_CTRL 0x68 /* SPTB Control */ + /* 0x69-0x7F reserved */ #define SUNI_MT 0x80 /* Master Test */ /* 0x81-0xFF reserved */ @@ -94,9 +98,18 @@ #define SUNI_MRI_ID_SHIFT 0 #define SUNI_MRI_TYPE 0x70 /* R, SUNI type (lite is 011) */ #define SUNI_MRI_TYPE_SHIFT 4 +#define SUNI_MRI_TYPE_PM5346 0x3 /* S/UNI 155 LITE */ +#define SUNI_MRI_TYPE_PM5347 0x4 /* S/UNI 155 PLUS */ +#define SUNI_MRI_TYPE_PM5350 0x7 /* S/UNI 155 ULTRA */ +#define SUNI_MRI_TYPE_PM5355 0x1 /* S/UNI 622 */ #define SUNI_MRI_RESET 0x80 /* RW, reset & power down chip 0: normal operation 1: reset & low power */ + +/* MCM is reg 0x4 */ +#define SUNI_MCM_LLE 0x20 /* line loopback (PM5355) */ +#define SUNI_MCM_DLE 0x10 /* diagnostic loopback (PM5355) */ + /* MCT is reg 5 */ #define SUNI_MCT_LOOPT 0x01 /* RW, timing source, 0: from TRCLK+/- */ @@ -144,6 +157,12 @@ /* TLOP_DIAG is reg 0x21 */ #define SUNI_TLOP_DIAG_DBIP 0x01 /* insert line BIP err (continuously) */ +/* SSTB_CTRL is reg 0x28 */ +#define SUNI_SSTB_CTRL_LEN16 0x01 /* path trace message length bit */ + +/* RPOP_RC is reg 0x3D (PM5355) */ +#define SUNI_RPOP_RC_ENSS 0x40 /* enable size bit */ + /* TPOP_DIAG is reg 0x40 */ #define SUNI_TPOP_DIAG_PAIS 0x01 /* insert STS path alarm ind (cont) */ #define SUNI_TPOP_DIAG_DB3 0x02 /* insert path BIP err (continuously) */ @@ -191,6 +210,9 @@ pattern */ #define SUNI_TACP_IUCHP_GFC_SHIFT 4 +/* SPTB_CTRL is reg 0x68 */ +#define SUNI_SPTB_CTRL_LEN16 0x01 /* path trace message length */ + /* MT is reg 0x80 */ #define SUNI_MT_HIZIO 0x01 /* RW, all but data bus & MP interface tri-state */ @@ -208,6 +230,7 @@ struct suni_priv { struct k_sonet_stats sonet_stats; /* link diagnostics */ int loop_mode; /* loopback mode */ + int type; /* phy type */ struct atm_dev *dev; /* device back-pointer */ struct suni_priv *next; /* next SUNI */ }; diff --git a/include/linux/sonet.h b/include/linux/sonet.h index 753680296e17..67ad11fcf88b 100644 --- a/include/linux/sonet.h +++ b/include/linux/sonet.h @@ -34,7 +34,7 @@ struct sonet_stats { /* clear error insertion */ #define SONET_GETDIAG _IOR('a',ATMIOC_PHYTYP+4,int) /* query error insertion */ -#define SONET_SETFRAMING _IO('a',ATMIOC_PHYTYP+5) +#define SONET_SETFRAMING _IOW('a',ATMIOC_PHYTYP+5,int) /* set framing mode (SONET/SDH) */ #define SONET_GETFRAMING _IOR('a',ATMIOC_PHYTYP+6,int) /* get framing mode */ -- cgit v1.2.3 From c1da4ac752b8b0411791d26c678fcf23d2eed242 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Fri, 13 Jun 2008 18:12:00 -0700 Subject: net/core: add NETDEV_BONDING_FAILOVER event Add NETDEV_BONDING_FAILOVER event to be used in a successive patch by bonding to announce fail-over for the active-backup mode through the netdev events notifier chain mechanism. Such an event can be of use for the RDMA CM (communication manager) to let native RDMA ULPs (eg NFS-RDMA, iSER) always be aligned with the IP stack, in the sense that they use the same ports/links as the stack does. More usages can be done to allow monitoring tools based on netlink events being aware to bonding fail-over. Signed-off-by: Or Gerlitz Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik --- include/linux/netdevice.h | 1 + include/linux/notifier.h | 1 + net/core/dev.c | 6 ++++++ 3 files changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f27fd2009334..e92fc839ab1d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1479,6 +1479,7 @@ extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct extern void dev_set_promiscuity(struct net_device *dev, int inc); extern void dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); +extern void netdev_bonding_change(struct net_device *dev); extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 0ff6224d172a..bd3d72ddf333 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -197,6 +197,7 @@ static inline int notifier_to_errno(int ret) #define NETDEV_GOING_DOWN 0x0009 #define NETDEV_CHANGENAME 0x000A #define NETDEV_FEAT_CHANGE 0x000B +#define NETDEV_BONDING_FAILOVER 0x000C #define SYS_DOWN 0x0001 /* Notify of system down */ #define SYS_RESTART SYS_DOWN diff --git a/net/core/dev.c b/net/core/dev.c index 68d8df0992ab..0e45742e7158 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -961,6 +961,12 @@ void netdev_state_change(struct net_device *dev) } } +void netdev_bonding_change(struct net_device *dev) +{ + call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, dev); +} +EXPORT_SYMBOL(netdev_bonding_change); + /** * dev_load - load a network module * @net: the applicable net namespace -- cgit v1.2.3 From b8a9787eddb0e4665f31dd1d64584732b2b5d051 Mon Sep 17 00:00:00 2001 From: Jay Vosburgh Date: Fri, 13 Jun 2008 18:12:04 -0700 Subject: bonding: Allow setting max_bonds to zero Permit bonding to function rationally if max_bonds is set to zero. This will load the module, but create no master devices (which can be created via sysfs). Requires some change to bond_create_sysfs; currently, the netdev sysfs directory is determined from the first bonding device created, but this is no longer possible. Instead, an interface from net/core is created to create and destroy files in net_class. Based on a patch submitted by Phil Oester . Modified by Jay Vosburgh to fix the sysfs issue mentioned above and to update the documentation. Signed-off-by: Phil Oester Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik --- Documentation/networking/bonding.txt | 3 ++- drivers/net/bonding/bond_main.c | 6 +++--- drivers/net/bonding/bond_sysfs.c | 22 +++------------------- include/linux/netdevice.h | 3 +++ net/core/net-sysfs.c | 13 +++++++++++++ 5 files changed, 24 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index 370b7da73ab4..7fa7fe71d7a8 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -376,7 +376,8 @@ max_bonds Specifies the number of bonding devices to create for this instance of the bonding driver. E.g., if max_bonds is 3, and the bonding driver is not already loaded, then bond0, bond1 - and bond2 will be created. The default value is 1. + and bond2 will be created. The default value is 1. Specifying + a value of 0 will load bonding, but will not create any devices. miimon diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 3b6d66a8ab98..d57b65dc2c72 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4750,11 +4750,11 @@ static int bond_check_params(struct bond_params *params) } } - if (max_bonds < 1 || max_bonds > INT_MAX) { + if (max_bonds < 0 || max_bonds > INT_MAX) { printk(KERN_WARNING DRV_NAME ": Warning: max_bonds (%d) not in range %d-%d, so it " "was reset to BOND_DEFAULT_MAX_BONDS (%d)\n", - max_bonds, 1, INT_MAX, BOND_DEFAULT_MAX_BONDS); + max_bonds, 0, INT_MAX, BOND_DEFAULT_MAX_BONDS); max_bonds = BOND_DEFAULT_MAX_BONDS; } @@ -4953,7 +4953,7 @@ static int bond_check_params(struct bond_params *params) printk("\n"); - } else { + } else if (max_bonds) { /* miimon and arp_interval not set, we need one so things * work as expected, see bonding.txt for details */ diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index dd265c69b0df..6caac0ffb2f2 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -53,7 +53,6 @@ extern struct bond_parm_tbl arp_validate_tbl[]; extern struct bond_parm_tbl fail_over_mac_tbl[]; static int expected_refcount = -1; -static struct class *netdev_class; /*--------------------------- Data Structures -----------------------------*/ /* Bonding sysfs lock. Why can't we just use the subsystem lock? @@ -1447,19 +1446,9 @@ static struct attribute_group bonding_group = { */ int bond_create_sysfs(void) { - int ret = 0; - struct bonding *firstbond; - - /* get the netdev class pointer */ - firstbond = container_of(bond_dev_list.next, struct bonding, bond_list); - if (!firstbond) - return -ENODEV; - - netdev_class = firstbond->dev->dev.class; - if (!netdev_class) - return -ENODEV; + int ret; - ret = class_create_file(netdev_class, &class_attr_bonding_masters); + ret = netdev_class_create_file(&class_attr_bonding_masters); /* * Permit multiple loads of the module by ignoring failures to * create the bonding_masters sysfs file. Bonding devices @@ -1478,10 +1467,6 @@ int bond_create_sysfs(void) printk(KERN_ERR "network device named %s already exists in sysfs", class_attr_bonding_masters.attr.name); - else { - netdev_class = NULL; - return 0; - } } return ret; @@ -1493,8 +1478,7 @@ int bond_create_sysfs(void) */ void bond_destroy_sysfs(void) { - if (netdev_class) - class_remove_file(netdev_class, &class_attr_bonding_masters); + netdev_class_remove_file(&class_attr_bonding_masters); } /* diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e92fc839ab1d..9ccbfac3fd95 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1506,6 +1506,9 @@ extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos); extern void dev_seq_stop(struct seq_file *seq, void *v); #endif +extern int netdev_class_create_file(struct class_attribute *class_attr); +extern void netdev_class_remove_file(struct class_attribute *class_attr); + extern void linkwatch_run_queue(void); extern int netdev_compute_features(unsigned long all, unsigned long one); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index dccd737ea2e3..3f7941319217 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -468,6 +468,19 @@ int netdev_register_kobject(struct net_device *net) return device_add(dev); } +int netdev_class_create_file(struct class_attribute *class_attr) +{ + return class_create_file(&net_class, class_attr); +} + +void netdev_class_remove_file(struct class_attribute *class_attr) +{ + class_remove_file(&net_class, class_attr); +} + +EXPORT_SYMBOL(netdev_class_create_file); +EXPORT_SYMBOL(netdev_class_remove_file); + void netdev_initialize_kobject(struct net_device *net) { struct device *device = &(net->dev); -- cgit v1.2.3 From 9d45abe1c2949183e5d9cb25721bf1c42c7b5e3b Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Tue, 17 Jun 2008 21:12:48 -0700 Subject: netdevice: change net_device->promiscuity/allmulti to unsigned int The comments of dev_set_allmulti/promiscuity() is that "While the count in the device remains above zero...". So negative count is useless. Fix the type of the counter from "int" to "unsigned int". Signed-off-by: Wang Chen Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f27fd2009334..06d8ea5992df 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -595,8 +595,8 @@ struct net_device int uc_promisc; struct dev_addr_list *mc_list; /* Multicast mac addresses */ int mc_count; /* Number of installed mcasts */ - int promiscuity; - int allmulti; + unsigned int promiscuity; + unsigned int allmulti; /* Protocol specific pointers */ -- cgit v1.2.3 From dad9b335c6940de2746a9788eb456d09cf102f81 Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Wed, 18 Jun 2008 01:48:28 -0700 Subject: netdevice: Fix promiscuity and allmulti overflow Max of promiscuity and allmulti plus positive @inc can cause overflow. Fox example: when allmulti=0xFFFFFFFF, any caller give dev_set_allmulti() a positive @inc will cause allmulti be off. This is not what we want, though it's rare case. The fix is that only negative @inc will cause allmulti or promiscuity be off and when any caller makes the counters touch the roof, we return error. Change of v2: Change void function dev_set_promiscuity/allmulti to return int. So callers can get the overflow error. Caller's fix will be done later. Change of v3: 1. Since we return error to caller, we don't need to print KERN_ERROR, KERN_WARNING is enough. 2. In dev_set_promiscuity(), if __dev_set_promiscuity() failed, we return at once. Signed-off-by: Wang Chen Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 55 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4bf613cd9e2d..45dce2b58d4c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1476,8 +1476,8 @@ extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *ad extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); -extern void dev_set_promiscuity(struct net_device *dev, int inc); -extern void dev_set_allmulti(struct net_device *dev, int inc); +extern int dev_set_promiscuity(struct net_device *dev, int inc); +extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); extern void netdev_bonding_change(struct net_device *dev); extern void netdev_features_change(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 0e45742e7158..a495f712d38c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2771,16 +2771,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) return 0; } -static void __dev_set_promiscuity(struct net_device *dev, int inc) +static int __dev_set_promiscuity(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; ASSERT_RTNL(); - if ((dev->promiscuity += inc) == 0) - dev->flags &= ~IFF_PROMISC; - else - dev->flags |= IFF_PROMISC; + dev->flags |= IFF_PROMISC; + dev->promiscuity += inc; + if (dev->promiscuity == 0) { + /* + * Avoid overflow. + * If inc causes overflow, untouch promisc and return error. + */ + if (inc < 0) + dev->flags &= ~IFF_PROMISC; + else { + dev->promiscuity -= inc; + printk(KERN_WARNING "%s: promiscuity touches roof, " + "set promiscuity failed, promiscuity feature " + "of device might be broken.\n", dev->name); + return -EOVERFLOW; + } + } if (dev->flags != old_flags) { printk(KERN_INFO "device %s %s promiscuous mode\n", dev->name, (dev->flags & IFF_PROMISC) ? "entered" : @@ -2798,6 +2811,7 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) if (dev->change_rx_flags) dev->change_rx_flags(dev, IFF_PROMISC); } + return 0; } /** @@ -2809,14 +2823,19 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) * remains above zero the interface remains promiscuous. Once it hits zero * the device reverts back to normal filtering operation. A negative inc * value is used to drop promiscuity on the device. + * Return 0 if successful or a negative errno code on error. */ -void dev_set_promiscuity(struct net_device *dev, int inc) +int dev_set_promiscuity(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; + int err; - __dev_set_promiscuity(dev, inc); + err = __dev_set_promiscuity(dev, inc); + if (!err) + return err; if (dev->flags != old_flags) dev_set_rx_mode(dev); + return err; } /** @@ -2829,22 +2848,38 @@ void dev_set_promiscuity(struct net_device *dev, int inc) * to all interfaces. Once it hits zero the device reverts back to normal * filtering operation. A negative @inc value is used to drop the counter * when releasing a resource needing all multicasts. + * Return 0 if successful or a negative errno code on error. */ -void dev_set_allmulti(struct net_device *dev, int inc) +int dev_set_allmulti(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; ASSERT_RTNL(); dev->flags |= IFF_ALLMULTI; - if ((dev->allmulti += inc) == 0) - dev->flags &= ~IFF_ALLMULTI; + dev->allmulti += inc; + if (dev->allmulti == 0) { + /* + * Avoid overflow. + * If inc causes overflow, untouch allmulti and return error. + */ + if (inc < 0) + dev->flags &= ~IFF_ALLMULTI; + else { + dev->allmulti -= inc; + printk(KERN_WARNING "%s: allmulti touches roof, " + "set allmulti failed, allmulti feature of " + "device might be broken.\n", dev->name); + return -EOVERFLOW; + } + } if (dev->flags ^ old_flags) { if (dev->change_rx_flags) dev->change_rx_flags(dev, IFF_ALLMULTI); dev_set_rx_mode(dev); } + return 0; } /* -- cgit v1.2.3 From e17ba73b0ee6c0f24393c48b455e0d8db761782c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 12 May 2008 15:44:40 +0200 Subject: x86, generic: mark early_printk as asmlinkage It's not explicitly marked as asmlinkage, but invoked from x86_32 startup code with parameters on stack. No other architectures define early_printk and none of them are affected by this change, since defines asmlinkage as empty token. Signed-off-by: Jiri Slaby Cc: H. Peter Anvin Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar --- arch/x86/kernel/early_printk.c | 2 +- include/linux/kernel.h | 2 +- kernel/printk.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index 643fd861b724..ff9e7350da54 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -196,7 +196,7 @@ static struct console simnow_console = { static struct console *early_console = &early_vga_console; static int early_console_initialized; -void early_printk(const char *fmt, ...) +asmlinkage void early_printk(const char *fmt, ...) { char buf[512]; int n; diff --git a/include/linux/kernel.h b/include/linux/kernel.h index f2a668c195bf..4cb8d3df414e 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -207,7 +207,7 @@ static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \ { return false; } #endif -extern void __attribute__((format(printf, 1, 2))) +extern void asmlinkage __attribute__((format(printf, 1, 2))) early_printk(const char *fmt, ...); unsigned long int_sqrt(unsigned long); diff --git a/kernel/printk.c b/kernel/printk.c index 70cfa5ac75ce..de1a4f4470c3 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -38,7 +38,7 @@ /* * Architectures can override it: */ -void __attribute__((weak)) early_printk(const char *fmt, ...) +void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) { } -- cgit v1.2.3 From 59ea746337c69f6a5f1bc4d5e8544b3cbf12f801 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 12 Jun 2008 13:56:40 +0200 Subject: MM: virtual address debug Add some (configurable) expensive sanity checking to catch wrong address translations on x86. - create linux/mmdebug.h file to be able include this file in asm headers to not get unsolvable loops in header files - __phys_addr on x86_32 became a function in ioremap.c since PAGE_OFFSET, is_vmalloc_addr and VMALLOC_* non-constasts are undefined if declared in page_32.h - add __phys_addr_const for initializing doublefault_tss.__cr3 Tested on 386, 386pae, x86_64 and x86_64 numa=fake=2. Contains Andi's enable numa virtual address debug patch. Signed-off-by: Jiri Slaby Cc: Andi Kleen Signed-off-by: Ingo Molnar --- arch/x86/kernel/doublefault_32.c | 2 +- arch/x86/mm/ioremap.c | 31 ++++++++++++++++++++++++------- include/asm-x86/mmzone_64.h | 2 +- include/asm-x86/page_32.h | 3 ++- include/linux/mm.h | 7 +------ include/linux/mmdebug.h | 18 ++++++++++++++++++ lib/Kconfig.debug | 9 +++++++++ mm/vmalloc.c | 5 +++++ 8 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 include/linux/mmdebug.h (limited to 'include/linux') diff --git a/arch/x86/kernel/doublefault_32.c b/arch/x86/kernel/doublefault_32.c index a47798b59f07..395acb12b0d1 100644 --- a/arch/x86/kernel/doublefault_32.c +++ b/arch/x86/kernel/doublefault_32.c @@ -66,6 +66,6 @@ struct tss_struct doublefault_tss __cacheline_aligned = { .ds = __USER_DS, .fs = __KERNEL_PERCPU, - .__cr3 = __pa(swapper_pg_dir) + .__cr3 = __phys_addr_const((unsigned long)swapper_pg_dir) } }; diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 2b2bb3f9b683..a78ffef62a2b 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -23,18 +23,26 @@ #ifdef CONFIG_X86_64 -unsigned long __phys_addr(unsigned long x) +static inline int phys_addr_valid(unsigned long addr) { - if (x >= __START_KERNEL_map) - return x - __START_KERNEL_map + phys_base; - return x - PAGE_OFFSET; + return addr < (1UL << boot_cpu_data.x86_phys_bits); } -EXPORT_SYMBOL(__phys_addr); -static inline int phys_addr_valid(unsigned long addr) +unsigned long __phys_addr(unsigned long x) { - return addr < (1UL << boot_cpu_data.x86_phys_bits); + if (x >= __START_KERNEL_map) { + x -= __START_KERNEL_map; + VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE); + x += phys_base; + } else { + VIRTUAL_BUG_ON(x < PAGE_OFFSET); + x -= PAGE_OFFSET; + VIRTUAL_BUG_ON(system_state == SYSTEM_BOOTING ? x > MAXMEM : + !phys_addr_valid(x)); + } + return x; } +EXPORT_SYMBOL(__phys_addr); #else @@ -43,6 +51,15 @@ static inline int phys_addr_valid(unsigned long addr) return 1; } +unsigned long __phys_addr(unsigned long x) +{ + /* VMALLOC_* aren't constants; not available at the boot time */ + VIRTUAL_BUG_ON(x < PAGE_OFFSET || (system_state != SYSTEM_BOOTING && + is_vmalloc_addr((void *)x))); + return x - PAGE_OFFSET; +} +EXPORT_SYMBOL(__phys_addr); + #endif int page_is_ram(unsigned long pagenr) diff --git a/include/asm-x86/mmzone_64.h b/include/asm-x86/mmzone_64.h index 594bd0dc1d08..facde3e5314f 100644 --- a/include/asm-x86/mmzone_64.h +++ b/include/asm-x86/mmzone_64.h @@ -7,7 +7,7 @@ #ifdef CONFIG_NUMA -#define VIRTUAL_BUG_ON(x) +#include #include diff --git a/include/asm-x86/page_32.h b/include/asm-x86/page_32.h index 424e82f8ae27..9159bfb9dcf9 100644 --- a/include/asm-x86/page_32.h +++ b/include/asm-x86/page_32.h @@ -64,7 +64,8 @@ typedef struct page *pgtable_t; #endif #ifndef __ASSEMBLY__ -#define __phys_addr(x) ((x) - PAGE_OFFSET) +#define __phys_addr_const(x) ((x) - PAGE_OFFSET) +extern unsigned long __phys_addr(unsigned long); #define __phys_reloc_hide(x) RELOC_HIDE((x), 0) #ifdef CONFIG_FLATMEM diff --git a/include/linux/mm.h b/include/linux/mm.h index 586a943cab01..3414a8813e97 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -210,12 +211,6 @@ struct inode; */ #include -#ifdef CONFIG_DEBUG_VM -#define VM_BUG_ON(cond) BUG_ON(cond) -#else -#define VM_BUG_ON(condition) do { } while(0) -#endif - /* * Methods to modify the page usage count. * diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h new file mode 100644 index 000000000000..860ed1a71bbe --- /dev/null +++ b/include/linux/mmdebug.h @@ -0,0 +1,18 @@ +#ifndef LINUX_MM_DEBUG_H +#define LINUX_MM_DEBUG_H 1 + +#include + +#ifdef CONFIG_DEBUG_VM +#define VM_BUG_ON(cond) BUG_ON(cond) +#else +#define VM_BUG_ON(cond) do { } while(0) +#endif + +#ifdef CONFIG_DEBUG_VIRTUAL +#define VIRTUAL_BUG_ON(cond) BUG_ON(cond) +#else +#define VIRTUAL_BUG_ON(cond) do { } while(0) +#endif + +#endif diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d2099f41aa1e..9d9dc0ddf13a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -469,6 +469,15 @@ config DEBUG_VM If unsure, say N. +config DEBUG_VIRTUAL + bool "Debug VM translations" + depends on DEBUG_KERNEL && X86 + help + Enable some costly sanity checks in virtual to page code. This can + catch mistakes with virt_to_page() and friends. + + If unsure, say N. + config DEBUG_WRITECOUNT bool "Debug filesystem writers count" depends on DEBUG_KERNEL diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 6e45b0f3d125..dc41e9c8ca6f 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -180,6 +180,11 @@ struct page *vmalloc_to_page(const void *vmalloc_addr) pmd_t *pmd; pte_t *ptep, pte; + /* XXX we might need to change this if we add VIRTUAL_BUG_ON for + * architectures that do not vmalloc module space */ + VIRTUAL_BUG_ON(!is_vmalloc_addr(vmalloc_addr) && + !is_module_address(addr)); + if (!pgd_none(*pgd)) { pud = pud_offset(pgd, addr); if (!pud_none(*pud)) { -- cgit v1.2.3 From 7aa413def76146f7b3784228556d9e4bc562eab3 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 19 Jun 2008 13:28:11 +0200 Subject: x86, MM: virtual address debug, cleanups Signed-off-by: Ingo Molnar --- include/linux/mmdebug.h | 4 ++-- mm/vmalloc.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h index 860ed1a71bbe..8a5509877192 100644 --- a/include/linux/mmdebug.h +++ b/include/linux/mmdebug.h @@ -6,13 +6,13 @@ #ifdef CONFIG_DEBUG_VM #define VM_BUG_ON(cond) BUG_ON(cond) #else -#define VM_BUG_ON(cond) do { } while(0) +#define VM_BUG_ON(cond) do { } while (0) #endif #ifdef CONFIG_DEBUG_VIRTUAL #define VIRTUAL_BUG_ON(cond) BUG_ON(cond) #else -#define VIRTUAL_BUG_ON(cond) do { } while(0) +#define VIRTUAL_BUG_ON(cond) do { } while (0) #endif #endif diff --git a/mm/vmalloc.c b/mm/vmalloc.c index dc41e9c8ca6f..830a5580c5d7 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -180,8 +180,10 @@ struct page *vmalloc_to_page(const void *vmalloc_addr) pmd_t *pmd; pte_t *ptep, pte; - /* XXX we might need to change this if we add VIRTUAL_BUG_ON for - * architectures that do not vmalloc module space */ + /* + * XXX we might need to change this if we add VIRTUAL_BUG_ON for + * architectures that do not vmalloc module space + */ VIRTUAL_BUG_ON(!is_vmalloc_addr(vmalloc_addr) && !is_module_address(addr)); -- cgit v1.2.3 From 0187bdfb05674147774ca79a79942537f3ad54bd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 19 Jun 2008 16:15:47 -0700 Subject: net: Disable LRO on devices that are forwarding Large Receive Offload (LRO) is only appropriate for packets that are destined for the host, and should be disabled if received packets may be forwarded. It can also confuse the GSO on output. Add dev_disable_lro() function which uses the appropriate ethtool ops to disable LRO if enabled. Add calls to dev_disable_lro() in br_add_if() and functions that enable IPv4 and IPv6 forwarding. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/bridge/br_if.c | 1 + net/core/dev.c | 24 ++++++++++++++++++++++++ net/ipv4/devinet.c | 21 ++++++++++++++++----- net/ipv6/addrconf.c | 6 ++++++ 5 files changed, 48 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 45dce2b58d4c..1304ad2d7105 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -886,6 +886,7 @@ extern struct net_device *__dev_get_by_name(struct net *net, const char *name); extern int dev_alloc_name(struct net_device *dev, const char *name); extern int dev_open(struct net_device *dev); extern int dev_close(struct net_device *dev); +extern void dev_disable_lro(struct net_device *dev); extern int dev_queue_xmit(struct sk_buff *skb); extern int register_netdevice(struct net_device *dev); extern void unregister_netdevice(struct net_device *dev); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 143c954681b8..832a561500d9 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -387,6 +387,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) goto err2; rcu_assign_pointer(dev->br_port, p); + dev_disable_lro(dev); dev_set_promiscuity(dev, 1); list_add_rcu(&p->list, &br->port_list); diff --git a/net/core/dev.c b/net/core/dev.c index a495f712d38c..f6944ecd5b2e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -90,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -1123,6 +1124,29 @@ int dev_close(struct net_device *dev) } +/** + * dev_disable_lro - disable Large Receive Offload on a device + * @dev: device + * + * Disable Large Receive Offload (LRO) on a net device. Must be + * called under RTNL. This is needed if received packets may be + * forwarded to another interface. + */ +void dev_disable_lro(struct net_device *dev) +{ + if (dev->ethtool_ops && dev->ethtool_ops->get_flags && + dev->ethtool_ops->set_flags) { + u32 flags = dev->ethtool_ops->get_flags(dev); + if (flags & ETH_FLAG_LRO) { + flags &= ~ETH_FLAG_LRO; + dev->ethtool_ops->set_flags(dev, flags); + } + } + WARN_ON(dev->features & NETIF_F_LRO); +} +EXPORT_SYMBOL(dev_disable_lro); + + static int dev_boot_phase = 1; /* diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f8c0b0aea93a..9de2514946ca 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -168,6 +168,8 @@ static struct in_device *inetdev_init(struct net_device *dev) in_dev->dev = dev; if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) goto out_kfree; + if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) + dev_disable_lro(dev); /* Reference in_dev->dev */ dev_hold(dev); /* Account for reference dev->ip_ptr (below) */ @@ -1241,6 +1243,8 @@ static void inet_forward_change(struct net *net) read_lock(&dev_base_lock); for_each_netdev(net, dev) { struct in_device *in_dev; + if (on) + dev_disable_lro(dev); rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev) @@ -1248,8 +1252,6 @@ static void inet_forward_change(struct net *net) rcu_read_unlock(); } read_unlock(&dev_base_lock); - - rt_cache_flush(0); } static int devinet_conf_proc(ctl_table *ctl, int write, @@ -1335,10 +1337,19 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, if (write && *valp != val) { struct net *net = ctl->extra2; - if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) - inet_forward_change(net); - else if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) + if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { + rtnl_lock(); + if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { + inet_forward_change(net); + } else if (*valp) { + struct ipv4_devconf *cnf = ctl->extra1; + struct in_device *idev = + container_of(cnf, struct in_device, cnf); + dev_disable_lro(idev->dev); + } + rtnl_unlock(); rt_cache_flush(0); + } } return ret; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9be6be3a7ff3..84127d854cfc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -348,6 +348,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) kfree(ndev); return NULL; } + if (ndev->cnf.forwarding) + dev_disable_lro(dev); /* We refer to the device */ dev_hold(dev); @@ -442,6 +444,8 @@ static void dev_forward_change(struct inet6_dev *idev) if (!idev) return; dev = idev->dev; + if (idev->cnf.forwarding) + dev_disable_lro(dev); if (dev && (dev->flags & IFF_MULTICAST)) { if (idev->cnf.forwarding) ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); @@ -487,12 +491,14 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) if (p == &net->ipv6.devconf_dflt->forwarding) return; + rtnl_lock(); if (p == &net->ipv6.devconf_all->forwarding) { __s32 newf = net->ipv6.devconf_all->forwarding; net->ipv6.devconf_dflt->forwarding = newf; addrconf_forward_change(net, newf); } else if ((!*p) ^ (!old)) dev_forward_change((struct inet6_dev *)table->extra1); + rtnl_unlock(); if (*p) rt6_purge_dflt_routers(net); -- cgit v1.2.3 From 4497b0763cb1afae463f5e144c28b5d806e28b60 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 19 Jun 2008 16:22:28 -0700 Subject: net: Discard and warn about LRO'd skbs received for forwarding Add skb_warn_if_lro() to test whether an skb was received with LRO and warn if so. Change br_forward(), ip_forward() and ip6_forward() to call it) and discard the skb if it returns true. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/skbuff.h | 14 ++++++++++++++ net/bridge/br_forward.c | 2 +- net/core/skbuff.c | 8 ++++++++ net/ipv4/ip_forward.c | 3 +++ net/ipv6/ip6_output.c | 3 +++ 5 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 299ec4b31412..2220b9e2dab0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1702,6 +1702,20 @@ static inline int skb_is_gso_v6(const struct sk_buff *skb) return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; } +extern void __skb_warn_lro_forwarding(const struct sk_buff *skb); + +static inline bool skb_warn_if_lro(const struct sk_buff *skb) +{ + /* LRO sets gso_size but not gso_type, whereas if GSO is really + * wanted then gso_type will be set. */ + struct skb_shared_info *shinfo = skb_shinfo(skb); + if (shinfo->gso_size != 0 && unlikely(shinfo->gso_type == 0)) { + __skb_warn_lro_forwarding(skb); + return true; + } + return false; +} + static inline void skb_forward_csum(struct sk_buff *skb) { /* Unfortunately we don't support this one. Any brave souls? */ diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 512645727f51..bdd9ccea17ce 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -89,7 +89,7 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) /* called with rcu_read_lock */ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb) { - if (should_deliver(to, skb)) { + if (!skb_warn_if_lro(skb) && should_deliver(to, skb)) { __br_forward(to, skb); return; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 3e18f8525e82..2df012be973d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2583,6 +2583,13 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off) return true; } +void __skb_warn_lro_forwarding(const struct sk_buff *skb) +{ + if (net_ratelimit()) + pr_warning("%s: received packets cannot be forwarded" + " while LRO is enabled\n", skb->dev->name); +} + EXPORT_SYMBOL(___pskb_trim); EXPORT_SYMBOL(__kfree_skb); EXPORT_SYMBOL(kfree_skb); @@ -2616,6 +2623,7 @@ EXPORT_SYMBOL(skb_seq_read); EXPORT_SYMBOL(skb_abort_seq_read); EXPORT_SYMBOL(skb_find_text); EXPORT_SYMBOL(skb_append_datato_frags); +EXPORT_SYMBOL(__skb_warn_lro_forwarding); EXPORT_SYMBOL_GPL(skb_to_sgvec); EXPORT_SYMBOL_GPL(skb_cow_data); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 37d36a3f33cd..da14725916d3 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -56,6 +56,9 @@ int ip_forward(struct sk_buff *skb) struct rtable *rt; /* Route we use */ struct ip_options * opt = &(IPCB(skb)->opt); + if (skb_warn_if_lro(skb)) + goto drop; + if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) goto drop; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 40a2813a63d1..fd7cd1bfe151 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -407,6 +407,9 @@ int ip6_forward(struct sk_buff *skb) if (ipv6_devconf.forwarding == 0) goto error; + if (skb_warn_if_lro(skb)) + goto drop; + if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) { IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS); goto drop; -- cgit v1.2.3 From 443cd507ce7f78c6f8742b72736585c031d5a921 Mon Sep 17 00:00:00 2001 From: "Huang, Ying" Date: Fri, 20 Jun 2008 16:39:21 +0800 Subject: lockdep: add lock_class information to lock_chain and output it This patch records array of lock_class into lock_chain, and export lock_chain information via /proc/lockdep_chains. It is based on x86/master branch of git-x86 tree, and has been tested on x86_64 platform. Signed-off-by: Huang Ying Cc: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 3 ++ kernel/lockdep.c | 38 +++++++++++++++++-- kernel/lockdep_internals.h | 6 +++ kernel/lockdep_proc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 4c4d236ded18..b26fbc715a50 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -182,6 +182,9 @@ struct lock_list { * We record lock dependency chains, so that we can cache them: */ struct lock_chain { + u8 irq_context; + u8 depth; + u16 base; struct list_head entry; u64 chain_key; }; diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 81a4e4a3f087..a796f1f38ac5 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -1458,7 +1458,14 @@ out_bug: } unsigned long nr_lock_chains; -static struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; +struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; +atomic_t nr_chain_hlocks; +static u16 chain_hlocks[MAX_LOCKDEP_CHAIN_HLOCKS]; + +struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i) +{ + return lock_classes + chain_hlocks[chain->base + i]; +} /* * Look up a dependency chain. If the key is not present yet then @@ -1466,10 +1473,15 @@ static struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; * validated. If the key is already hashed, return 0. * (On return with 1 graph_lock is held.) */ -static inline int lookup_chain_cache(u64 chain_key, struct lock_class *class) +static inline int lookup_chain_cache(struct task_struct *curr, + struct held_lock *hlock, + u64 chain_key) { + struct lock_class *class = hlock->class; struct list_head *hash_head = chainhashentry(chain_key); struct lock_chain *chain; + struct held_lock *hlock_curr, *hlock_next; + int i, j, n; if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) return 0; @@ -1517,6 +1529,26 @@ cache_hit: } chain = lock_chains + nr_lock_chains++; chain->chain_key = chain_key; + chain->irq_context = hlock->irq_context; + /* Find the first held_lock of current chain */ + hlock_next = hlock; + for (i = curr->lockdep_depth - 1; i >= 0; i--) { + hlock_curr = curr->held_locks + i; + if (hlock_curr->irq_context != hlock_next->irq_context) + break; + hlock_next = hlock; + } + i++; + chain->depth = curr->lockdep_depth + 1 - i; + n = atomic_add_return(chain->depth, &nr_chain_hlocks); + if (unlikely(n < MAX_LOCKDEP_CHAIN_HLOCKS)) { + chain->base = n - chain->depth; + for (j = 0; j < chain->depth - 1; j++, i++) { + int lock_id = curr->held_locks[i].class - lock_classes; + chain_hlocks[chain->base + j] = lock_id; + } + chain_hlocks[chain->base + j] = class - lock_classes; + } list_add_tail_rcu(&chain->entry, hash_head); debug_atomic_inc(&chain_lookup_misses); inc_chains(); @@ -1538,7 +1570,7 @@ static int validate_chain(struct task_struct *curr, struct lockdep_map *lock, * graph_lock for us) */ if (!hlock->trylock && (hlock->check == 2) && - lookup_chain_cache(chain_key, hlock->class)) { + lookup_chain_cache(curr, hlock, chain_key)) { /* * Check whether last held lock: * diff --git a/kernel/lockdep_internals.h b/kernel/lockdep_internals.h index 8ce09bc4613d..db09b176dd34 100644 --- a/kernel/lockdep_internals.h +++ b/kernel/lockdep_internals.h @@ -23,6 +23,8 @@ #define MAX_LOCKDEP_CHAINS_BITS 14 #define MAX_LOCKDEP_CHAINS (1UL << MAX_LOCKDEP_CHAINS_BITS) +#define MAX_LOCKDEP_CHAIN_HLOCKS (MAX_LOCKDEP_CHAINS*5) + /* * Stack-trace: tightly packed array of stack backtrace * addresses. Protected by the hash_lock. @@ -30,15 +32,19 @@ #define MAX_STACK_TRACE_ENTRIES 262144UL extern struct list_head all_lock_classes; +extern struct lock_chain lock_chains[]; extern void get_usage_chars(struct lock_class *class, char *c1, char *c2, char *c3, char *c4); extern const char * __get_key_name(struct lockdep_subclass_key *key, char *str); +struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i); + extern unsigned long nr_lock_classes; extern unsigned long nr_list_entries; extern unsigned long nr_lock_chains; +extern atomic_t nr_chain_hlocks; extern unsigned long nr_stack_trace_entries; extern unsigned int nr_hardirq_chains; diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c index 688c5f1940bd..14d052c8a835 100644 --- a/kernel/lockdep_proc.c +++ b/kernel/lockdep_proc.c @@ -178,6 +178,93 @@ static const struct file_operations proc_lockdep_operations = { .release = seq_release, }; +static void *lc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct lock_chain *chain; + + (*pos)++; + + if (v == SEQ_START_TOKEN) + chain = m->private; + else { + chain = v; + + if (*pos < nr_lock_chains) + chain = lock_chains + *pos; + else + chain = NULL; + } + + return chain; +} + +static void *lc_start(struct seq_file *m, loff_t *pos) +{ + if (*pos == 0) + return SEQ_START_TOKEN; + + if (*pos < nr_lock_chains) + return lock_chains + *pos; + + return NULL; +} + +static void lc_stop(struct seq_file *m, void *v) +{ +} + +static int lc_show(struct seq_file *m, void *v) +{ + struct lock_chain *chain = v; + struct lock_class *class; + int i; + + if (v == SEQ_START_TOKEN) { + seq_printf(m, "all lock chains:\n"); + return 0; + } + + seq_printf(m, "irq_context: %d\n", chain->irq_context); + + for (i = 0; i < chain->depth; i++) { + class = lock_chain_get_class(chain, i); + seq_printf(m, "[%p] ", class->key); + print_name(m, class); + seq_puts(m, "\n"); + } + seq_puts(m, "\n"); + + return 0; +} + +static const struct seq_operations lockdep_chains_ops = { + .start = lc_start, + .next = lc_next, + .stop = lc_stop, + .show = lc_show, +}; + +static int lockdep_chains_open(struct inode *inode, struct file *file) +{ + int res = seq_open(file, &lockdep_chains_ops); + if (!res) { + struct seq_file *m = file->private_data; + + if (nr_lock_chains) + m->private = lock_chains; + else + m->private = NULL; + } + return res; +} + +static const struct file_operations proc_lockdep_chains_operations = { + .open = lockdep_chains_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + static void lockdep_stats_debug_show(struct seq_file *m) { #ifdef CONFIG_DEBUG_LOCKDEP @@ -294,6 +381,8 @@ static int lockdep_stats_show(struct seq_file *m, void *v) #ifdef CONFIG_PROVE_LOCKING seq_printf(m, " dependency chains: %11lu [max: %lu]\n", nr_lock_chains, MAX_LOCKDEP_CHAINS); + seq_printf(m, " dependency chain hlocks: %11d [max: %lu]\n", + atomic_read(&nr_chain_hlocks), MAX_LOCKDEP_CHAIN_HLOCKS); #endif #ifdef CONFIG_TRACE_IRQFLAGS @@ -661,6 +750,8 @@ static const struct file_operations proc_lock_stat_operations = { static int __init lockdep_proc_init(void) { proc_create("lockdep", S_IRUSR, NULL, &proc_lockdep_operations); + proc_create("lockdep_chains", S_IRUSR, NULL, + &proc_lockdep_chains_operations); proc_create("lockdep_stats", S_IRUSR, NULL, &proc_lockdep_stats_operations); -- cgit v1.2.3 From 0b2806768899dba5967bcd4a3b93eaed9a1dc4f3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sun, 18 May 2008 14:27:41 -0600 Subject: Add cycle_kernel_lock() A number of driver functions are so obviously trivial that they do not need the big kernel lock - at least not overtly. It turns out that the acquisition of the BKL in driver open() functions can perform a sort of poor-hacker's serialization function, delaying the open operation until the driver is certain to have completed its initialization. Add a simple cycle_kernel_lock() function for these cases to make it clear that there is no need to *hold* the BKL, just to be sure that we can acquire it. Signed-off-by: Jonathan Corbet --- include/linux/smp_lock.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/smp_lock.h b/include/linux/smp_lock.h index aab3a4cff4e1..813be59bf345 100644 --- a/include/linux/smp_lock.h +++ b/include/linux/smp_lock.h @@ -27,11 +27,24 @@ static inline int reacquire_kernel_lock(struct task_struct *task) extern void __lockfunc lock_kernel(void) __acquires(kernel_lock); extern void __lockfunc unlock_kernel(void) __releases(kernel_lock); +/* + * Various legacy drivers don't really need the BKL in a specific + * function, but they *do* need to know that the BKL became available. + * This function just avoids wrapping a bunch of lock/unlock pairs + * around code which doesn't really need it. + */ +static inline void cycle_kernel_lock(void) +{ + lock_kernel(); + unlock_kernel(); +} + #else #define lock_kernel() do { } while(0) #define unlock_kernel() do { } while(0) #define release_kernel_lock(task) do { } while(0) +#define cycle_kernel_lock() do { } while(0) #define reacquire_kernel_lock(task) 0 #define kernel_locked() 1 -- cgit v1.2.3 From 20d4fdc1a788e4ca0aaf2422772ba668e7e10839 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 9 Jun 2008 16:40:36 -0700 Subject: [patch 2/4] fs: make struct file arg to d_path const Signed-off-by: Jan Engelhardt Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- fs/dcache.c | 2 +- include/linux/dcache.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/dcache.c b/fs/dcache.c index 3ee588d5f585..c4c9072d810c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1847,7 +1847,7 @@ Elong: * * "buflen" should be positive. Caller holds the dcache_lock. */ -char *d_path(struct path *path, char *buf, int buflen) +char *d_path(const struct path *path, char *buf, int buflen) { char *res; struct path root; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 2a6639407c80..d982eb89c77d 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -300,7 +300,7 @@ extern int d_validate(struct dentry *, struct dentry *); extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *__d_path(const struct path *path, struct path *root, char *, int); -extern char *d_path(struct path *, char *, int); +extern char *d_path(const struct path *, char *, int); extern char *dentry_path(struct dentry *, char *, int); /* Allocation counts.. */ -- cgit v1.2.3 From f9f48ec72bfc9489a30bc6ddbfcf27d86a8bc651 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 9 Jun 2008 16:40:38 -0700 Subject: [patch 4/4] flock: remove unused fields from file_lock_operations fl_insert and fl_remove are not used right now in the kernel. Remove them. Signed-off-by: Denis V. Lunev Cc: Matthew Wilcox Cc: Alexander Viro Cc: "J. Bruce Fields" Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- fs/locks.c | 6 ------ include/linux/fs.h | 2 -- 2 files changed, 8 deletions(-) (limited to 'include/linux') diff --git a/fs/locks.c b/fs/locks.c index 11dbf08651b7..dce8c747371c 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -561,9 +561,6 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) /* insert into file's list */ fl->fl_next = *pos; *pos = fl; - - if (fl->fl_ops && fl->fl_ops->fl_insert) - fl->fl_ops->fl_insert(fl); } /* @@ -586,9 +583,6 @@ static void locks_delete_lock(struct file_lock **thisfl_p) fl->fl_fasync = NULL; } - if (fl->fl_ops && fl->fl_ops->fl_remove) - fl->fl_ops->fl_remove(fl); - if (fl->fl_nspid) { put_pid(fl->fl_nspid); fl->fl_nspid = NULL; diff --git a/include/linux/fs.h b/include/linux/fs.h index d490779f18d9..7c1080826832 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -894,8 +894,6 @@ static inline int file_check_writeable(struct file *filp) typedef struct files_struct *fl_owner_t; struct file_lock_operations { - void (*fl_insert)(struct file_lock *); /* lock insertion callback */ - void (*fl_remove)(struct file_lock *); /* lock removal callback */ void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_release_private)(struct file_lock *); }; -- cgit v1.2.3 From 7c11337d9d81cde0a08a0da63cbfb20653890fa1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 4 Jun 2008 18:50:06 -0400 Subject: nfsd: remove three unused NFS4_ACE_* defines These flag bits aren't used by either the protocol or our implementation, so I don't know why they were here. Thanks to Johann Dahm for running across these. Signed-off-by: J. Bruce Fields Cc: Johann Dahm --- include/linux/nfs4.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 8726491de154..ea0366769484 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -65,9 +65,6 @@ #define NFS4_ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010 #define NFS4_ACE_FAILED_ACCESS_ACE_FLAG 0x00000020 #define NFS4_ACE_IDENTIFIER_GROUP 0x00000040 -#define NFS4_ACE_OWNER 0x00000080 -#define NFS4_ACE_GROUP 0x00000100 -#define NFS4_ACE_EVERYONE 0x00000200 #define NFS4_ACE_READ_DATA 0x00000001 #define NFS4_ACE_LIST_DIRECTORY 0x00000001 -- cgit v1.2.3 From a5e561fee651eb03086ca21e9aa78e1fffa4581a Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Tue, 10 Jun 2008 12:59:07 +0300 Subject: nfsd: eliminate unused nfs4_callback.cb_program The cb_program member of struct nfs4_callback unused since commit ff7d9756 nfsd: use static memory for callback program and stats Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- include/linux/nfsd/state.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index db348f749376..06e9686d9dea 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -98,7 +98,6 @@ struct nfs4_callback { u32 cb_ident; /* RPC client info */ atomic_t cb_set; /* successful CB_NULL call */ - struct rpc_program cb_program; struct rpc_stat cb_stat; struct rpc_clnt * cb_client; }; -- cgit v1.2.3 From 0d169ca136357d51a65d686f3c84866a8ba20ae9 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Tue, 10 Jun 2008 13:39:43 +0300 Subject: nfsd: eliminate unused nfs4_callback.cb_stat The cb_stat member of struct nfs4_callback is unused since commit ff7d9756 nfsd: use static memory for callback program and stats Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- include/linux/nfsd/state.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 06e9686d9dea..d0fe2e378452 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -98,7 +98,6 @@ struct nfs4_callback { u32 cb_ident; /* RPC client info */ atomic_t cb_set; /* successful CB_NULL call */ - struct rpc_stat cb_stat; struct rpc_clnt * cb_client; }; -- cgit v1.2.3 From bedbdd8bada194a690d2901801bf8451965086b3 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Tue, 10 Jun 2008 08:40:35 -0400 Subject: knfsd: Replace lock_kernel with a mutex for nfsd thread startup/shutdown locking. This removes the BKL from the RPC service creation codepath. The BKL really isn't adequate for this job since some of this info needs protection across sleeps. Also, add some comments to try and clarify how the locking should work and to make it clear that the BKL isn't necessary as long as there is adequate locking between tasks when touching the svc_serv fields. Signed-off-by: Neil Brown Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 37 +++++++++++++++++++++++-------------- fs/nfsd/nfssvc.c | 45 ++++++++++++++++++++++++++++++++------------- include/linux/nfsd/nfsd.h | 1 + net/sunrpc/svc.c | 15 +++++++++------ 4 files changed, 65 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 5ac00c4fee91..049d2a9c7715 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -450,22 +450,26 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) int i; int rv; int len; - int npools = nfsd_nrpools(); + int npools; int *nthreads; + mutex_lock(&nfsd_mutex); + npools = nfsd_nrpools(); if (npools == 0) { /* * NFS is shut down. The admin can start it by * writing to the threads file but NOT the pool_threads * file, sorry. Report zero threads. */ + mutex_unlock(&nfsd_mutex); strcpy(buf, "0\n"); return strlen(buf); } nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); + rv = -ENOMEM; if (nthreads == NULL) - return -ENOMEM; + goto out_free; if (size > 0) { for (i = 0; i < npools; i++) { @@ -496,10 +500,12 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) mesg += len; } + mutex_unlock(&nfsd_mutex); return (mesg-buf); out_free: kfree(nthreads); + mutex_unlock(&nfsd_mutex); return rv; } @@ -566,14 +572,13 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size) return len; } -static ssize_t write_ports(struct file *file, char *buf, size_t size) +static ssize_t __write_ports(struct file *file, char *buf, size_t size) { if (size == 0) { int len = 0; - lock_kernel(); + if (nfsd_serv) len = svc_xprt_names(nfsd_serv, buf, 0); - unlock_kernel(); return len; } /* Either a single 'fd' number is written, in which @@ -603,9 +608,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) /* Decrease the count, but don't shutdown the * the service */ - lock_kernel(); nfsd_serv->sv_nrthreads--; - unlock_kernel(); } return err < 0 ? err : 0; } @@ -614,10 +617,8 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) int len = 0; if (!toclose) return -ENOMEM; - lock_kernel(); if (nfsd_serv) len = svc_sock_names(buf, nfsd_serv, toclose); - unlock_kernel(); if (len >= 0) lockd_down(); kfree(toclose); @@ -655,7 +656,6 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) { if (port == 0) return -EINVAL; - lock_kernel(); if (nfsd_serv) { xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port); @@ -666,13 +666,22 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) } else err = -ENOTCONN; } - unlock_kernel(); return err < 0 ? err : 0; } } return -EINVAL; } +static ssize_t write_ports(struct file *file, char *buf, size_t size) +{ + ssize_t rv; + mutex_lock(&nfsd_mutex); + rv = __write_ports(file, buf, size); + mutex_unlock(&nfsd_mutex); + return rv; +} + + int nfsd_max_blksize; static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) @@ -691,13 +700,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) if (bsize > NFSSVC_MAXBLKSIZE) bsize = NFSSVC_MAXBLKSIZE; bsize &= ~(1024-1); - lock_kernel(); + mutex_lock(&nfsd_mutex); if (nfsd_serv && nfsd_serv->sv_nrthreads) { - unlock_kernel(); + mutex_unlock(&nfsd_mutex); return -EBUSY; } nfsd_max_blksize = bsize; - unlock_kernel(); + mutex_unlock(&nfsd_mutex); } return sprintf(buf, "%d\n", nfsd_max_blksize); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 941041f4b136..512bd04c6dda 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -53,11 +53,27 @@ extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); struct timeval nfssvc_boot; - struct svc_serv *nfsd_serv; static atomic_t nfsd_busy; static unsigned long nfsd_last_call; static DEFINE_SPINLOCK(nfsd_call_lock); +/* + * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members + * of the svc_serv struct. In particular, ->sv_nrthreads but also to some + * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt + * + * If (out side the lock) nfsd_serv is non-NULL, then it must point to a + * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number + * of nfsd threads must exist and each must listed in ->sp_all_threads in each + * entry of ->sv_pools[]. + * + * Transitions of the thread count between zero and non-zero are of particular + * interest since the svc_serv needs to be created and initialized at that + * point, or freed. + */ +DEFINE_MUTEX(nfsd_mutex); +struct svc_serv *nfsd_serv; + #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) static struct svc_stat nfsd_acl_svcstats; static struct svc_version * nfsd_acl_version[] = { @@ -190,13 +206,14 @@ void nfsd_reset_versions(void) } } + int nfsd_create_serv(void) { int err = 0; - lock_kernel(); + + WARN_ON(!mutex_is_locked(&nfsd_mutex)); if (nfsd_serv) { svc_get(nfsd_serv); - unlock_kernel(); return 0; } if (nfsd_max_blksize == 0) { @@ -223,7 +240,7 @@ int nfsd_create_serv(void) nfsd, SIG_NOCLEAN, THIS_MODULE); if (nfsd_serv == NULL) err = -ENOMEM; - unlock_kernel(); + do_gettimeofday(&nfssvc_boot); /* record boot time */ return err; } @@ -282,6 +299,8 @@ int nfsd_set_nrthreads(int n, int *nthreads) int tot = 0; int err = 0; + WARN_ON(!mutex_is_locked(&nfsd_mutex)); + if (nfsd_serv == NULL || n <= 0) return 0; @@ -316,7 +335,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) nthreads[0] = 1; /* apply the new numbers */ - lock_kernel(); svc_get(nfsd_serv); for (i = 0; i < n; i++) { err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], @@ -325,7 +343,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) break; } svc_destroy(nfsd_serv); - unlock_kernel(); return err; } @@ -334,8 +351,8 @@ int nfsd_svc(unsigned short port, int nrservs) { int error; - - lock_kernel(); + + mutex_lock(&nfsd_mutex); dprintk("nfsd: creating service\n"); error = -EINVAL; if (nrservs <= 0) @@ -363,7 +380,7 @@ nfsd_svc(unsigned short port, int nrservs) failure: svc_destroy(nfsd_serv); /* Release server */ out: - unlock_kernel(); + mutex_unlock(&nfsd_mutex); return error; } @@ -399,7 +416,7 @@ nfsd(struct svc_rqst *rqstp) sigset_t shutdown_mask, allowed_mask; /* Lock module and set up kernel thread */ - lock_kernel(); + mutex_lock(&nfsd_mutex); daemonize("nfsd"); /* After daemonize() this kernel thread shares current->fs @@ -417,11 +434,13 @@ nfsd(struct svc_rqst *rqstp) siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); siginitsetinv(&allowed_mask, ALLOWED_SIGS); + nfsdstats.th_cnt++; rqstp->rq_task = current; - unlock_kernel(); + mutex_unlock(&nfsd_mutex); + /* * We want less throttling in balance_dirty_pages() so that nfs to @@ -477,7 +496,7 @@ nfsd(struct svc_rqst *rqstp) /* Clear signals before calling svc_exit_thread() */ flush_signals(current); - lock_kernel(); + mutex_lock(&nfsd_mutex); nfsdstats.th_cnt --; @@ -486,7 +505,7 @@ out: svc_exit_thread(rqstp); /* Release module */ - unlock_kernel(); + mutex_unlock(&nfsd_mutex); module_put_and_exit(0); } diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 41d30c9c9de6..88d85b964429 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -54,6 +54,7 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int); extern struct svc_program nfsd_program; extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4; +extern struct mutex nfsd_mutex; extern struct svc_serv *nfsd_serv; extern struct seq_operations nfs_exports_op; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 01c7e311b904..7bffaff2a3ab 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -461,7 +461,8 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, EXPORT_SYMBOL(svc_create_pooled); /* - * Destroy an RPC service. Should be called with the BKL held + * Destroy an RPC service. Should be called with appropriate locking to + * protect the sv_nrthreads, sv_permsocks and sv_tempsocks. */ void svc_destroy(struct svc_serv *serv) @@ -578,9 +579,10 @@ out_enomem: EXPORT_SYMBOL(svc_prepare_thread); /* - * Create a thread in the given pool. Caller must hold BKL. - * On a NUMA or SMP machine, with a multi-pool serv, the thread - * will be restricted to run on the cpus belonging to the pool. + * Create a thread in the given pool. Caller must hold BKL or another lock to + * serialize access to the svc_serv struct. On a NUMA or SMP machine, with a + * multi-pool serv, the thread will be restricted to run on the cpus belonging + * to the pool. */ static int __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, @@ -674,7 +676,7 @@ found_pool: * of threads the given number. If `pool' is non-NULL, applies * only to threads in that pool, otherwise round-robins between * all pools. Must be called with a svc_get() reference and - * the BKL held. + * the BKL or another lock to protect access to svc_serv fields. * * Destroying threads relies on the service threads filling in * rqstp->rq_task, which only the nfs ones do. Assumes the serv @@ -722,7 +724,8 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) EXPORT_SYMBOL(svc_set_num_threads); /* - * Called from a server thread as it's exiting. Caller must hold BKL. + * Called from a server thread as it's exiting. Caller must hold the BKL or + * the "service mutex", whichever is appropriate for the service. */ void svc_exit_thread(struct svc_rqst *rqstp) -- cgit v1.2.3 From 9867d76ca16b3f455f9ca83861f4ce5c94a25928 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 10 Jun 2008 08:40:38 -0400 Subject: knfsd: convert knfsd to kthread API This patch is rather large, but I couldn't figure out a way to break it up that would remain bisectable. It does several things: - change svc_thread_fn typedef to better match what kthread_create expects - change svc_pool_map_set_cpumask to be more kthread friendly. Make it take a task arg and and get rid of the "oldmask" - have svc_set_num_threads call kthread_create directly - eliminate __svc_create_thread Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfssvc.c | 45 ++++++++++++-------- include/linux/sunrpc/svc.h | 2 +- net/sunrpc/svc.c | 100 +++++++++++++++------------------------------ 3 files changed, 64 insertions(+), 83 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 6339cb70a08d..9e2156813710 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ #define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT)) extern struct svc_program nfsd_program; -static void nfsd(struct svc_rqst *rqstp); +static int nfsd(void *vrqstp); struct timeval nfssvc_boot; static atomic_t nfsd_busy; static unsigned long nfsd_last_call; @@ -407,18 +408,19 @@ update_thread_usage(int busy_threads) /* * This is the NFS server kernel thread */ -static void -nfsd(struct svc_rqst *rqstp) +static int +nfsd(void *vrqstp) { + struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; struct fs_struct *fsp; - int err; sigset_t shutdown_mask, allowed_mask; + int err, preverr = 0; + unsigned int signo; /* Lock module and set up kernel thread */ mutex_lock(&nfsd_mutex); - daemonize("nfsd"); - /* After daemonize() this kernel thread shares current->fs + /* At this point, the thread shares current->fs * with the init process. We need to create files with a * umask of 0 instead of init's umask. */ fsp = copy_fs_struct(current->fs); @@ -433,14 +435,18 @@ nfsd(struct svc_rqst *rqstp) siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); siginitsetinv(&allowed_mask, ALLOWED_SIGS); + /* + * thread is spawned with all signals set to SIG_IGN, re-enable + * the ones that matter + */ + for (signo = 1; signo <= _NSIG; signo++) { + if (!sigismember(&shutdown_mask, signo)) + allow_signal(signo); + } nfsdstats.th_cnt++; - - rqstp->rq_task = current; - mutex_unlock(&nfsd_mutex); - /* * We want less throttling in balance_dirty_pages() so that nfs to * localhost doesn't cause nfsd to lock up due to all the client's @@ -462,15 +468,25 @@ nfsd(struct svc_rqst *rqstp) */ while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN) ; - if (err < 0) + if (err == -EINTR) break; + else if (err < 0) { + if (err != preverr) { + printk(KERN_WARNING "%s: unexpected error " + "from svc_recv (%d)\n", __func__, -err); + preverr = err; + } + schedule_timeout_uninterruptible(HZ); + continue; + } + update_thread_usage(atomic_read(&nfsd_busy)); atomic_inc(&nfsd_busy); /* Lock the export hash tables for reading. */ exp_readlock(); - /* Process request with signals blocked. */ + /* Process request with signals blocked. */ sigprocmask(SIG_SETMASK, &allowed_mask, NULL); svc_process(rqstp); @@ -481,14 +497,10 @@ nfsd(struct svc_rqst *rqstp) atomic_dec(&nfsd_busy); } - if (err != -EINTR) - printk(KERN_WARNING "nfsd: terminating on error %d\n", -err); - /* Clear signals before calling svc_exit_thread() */ flush_signals(current); mutex_lock(&nfsd_mutex); - nfsdstats.th_cnt --; out: @@ -498,6 +510,7 @@ out: /* Release module */ mutex_unlock(&nfsd_mutex); module_put_and_exit(0); + return 0; } static __be32 map_new_errors(u32 vers, __be32 nfserr) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 4b54c5fdcfd9..011d6d8100d8 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -22,7 +22,7 @@ /* * This is the RPC server thread function prototype */ -typedef void (*svc_thread_fn)(struct svc_rqst *); +typedef int (*svc_thread_fn)(void *); /* * diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 7bffaff2a3ab..03a9f1a9e75c 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -291,15 +292,14 @@ svc_pool_map_put(void) /* - * Set the current thread's cpus_allowed mask so that it + * Set the given thread's cpus_allowed mask so that it * will only run on cpus in the given pool. - * - * Returns 1 and fills in oldmask iff a cpumask was applied. */ -static inline int -svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) +static inline void +svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx) { struct svc_pool_map *m = &svc_pool_map; + unsigned int node = m->pool_to[pidx]; /* * The caller checks for sv_nrpools > 1, which @@ -307,26 +307,17 @@ svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) */ BUG_ON(m->count == 0); - switch (m->mode) - { - default: - return 0; + switch (m->mode) { case SVC_POOL_PERCPU: { - unsigned int cpu = m->pool_to[pidx]; - - *oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - return 1; + set_cpus_allowed_ptr(task, &cpumask_of_cpu(node)); + break; } case SVC_POOL_PERNODE: { - unsigned int node = m->pool_to[pidx]; node_to_cpumask_ptr(nodecpumask, node); - - *oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, nodecpumask); - return 1; + set_cpus_allowed_ptr(task, nodecpumask); + break; } } } @@ -578,47 +569,6 @@ out_enomem: } EXPORT_SYMBOL(svc_prepare_thread); -/* - * Create a thread in the given pool. Caller must hold BKL or another lock to - * serialize access to the svc_serv struct. On a NUMA or SMP machine, with a - * multi-pool serv, the thread will be restricted to run on the cpus belonging - * to the pool. - */ -static int -__svc_create_thread(svc_thread_fn func, struct svc_serv *serv, - struct svc_pool *pool) -{ - struct svc_rqst *rqstp; - int error = -ENOMEM; - int have_oldmask = 0; - cpumask_t uninitialized_var(oldmask); - - rqstp = svc_prepare_thread(serv, pool); - if (IS_ERR(rqstp)) { - error = PTR_ERR(rqstp); - goto out; - } - - if (serv->sv_nrpools > 1) - have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask); - - error = kernel_thread((int (*)(void *)) func, rqstp, 0); - - if (have_oldmask) - set_cpus_allowed(current, oldmask); - - if (error < 0) - goto out_thread; - svc_sock_update_bufs(serv); - error = 0; -out: - return error; - -out_thread: - svc_exit_thread(rqstp); - goto out; -} - /* * Choose a pool in which to create a new thread, for svc_set_num_threads */ @@ -688,7 +638,9 @@ found_pool: int svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) { - struct task_struct *victim; + struct svc_rqst *rqstp; + struct task_struct *task; + struct svc_pool *chosen_pool; int error = 0; unsigned int state = serv->sv_nrthreads-1; @@ -704,18 +656,34 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) /* create new threads */ while (nrservs > 0) { nrservs--; + chosen_pool = choose_pool(serv, pool, &state); + + rqstp = svc_prepare_thread(serv, chosen_pool); + if (IS_ERR(rqstp)) { + error = PTR_ERR(rqstp); + break; + } + __module_get(serv->sv_module); - error = __svc_create_thread(serv->sv_function, serv, - choose_pool(serv, pool, &state)); - if (error < 0) { + task = kthread_create(serv->sv_function, rqstp, serv->sv_name); + if (IS_ERR(task)) { + error = PTR_ERR(task); module_put(serv->sv_module); + svc_exit_thread(rqstp); break; } + + rqstp->rq_task = task; + if (serv->sv_nrpools > 1) + svc_pool_map_set_cpumask(task, chosen_pool->sp_id); + + svc_sock_update_bufs(serv); + wake_up_process(task); } /* destroy old threads */ while (nrservs < 0 && - (victim = choose_victim(serv, pool, &state)) != NULL) { - send_sig(serv->sv_kill_signal, victim, 1); + (task = choose_victim(serv, pool, &state)) != NULL) { + send_sig(serv->sv_kill_signal, task, 1); nrservs++; } -- cgit v1.2.3 From a75c5d01e4235a7dd785548ac756f248b1b40107 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 10 Jun 2008 08:40:39 -0400 Subject: sunrpc: remove sv_kill_signal field from svc_serv struct Since we no longer make any distinction between shutdown signals with nfsd, then it becomes easier to just standardize on a particular signal to use to bring it down (SIGINT, in this case). Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfssvc.c | 3 +-- include/linux/sunrpc/svc.h | 5 ++--- net/sunrpc/svc.c | 5 ++--- 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 9e2156813710..26c81149d49a 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -236,8 +236,7 @@ int nfsd_create_serv(void) atomic_set(&nfsd_busy, 0); nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, - nfsd_last_thread, nfsd, SIGINT, - THIS_MODULE); + nfsd_last_thread, nfsd, THIS_MODULE); if (nfsd_serv == NULL) err = -ENOMEM; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 011d6d8100d8..dc69068d94c7 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -80,7 +80,6 @@ struct svc_serv { struct module * sv_module; /* optional module to count when * adding threads */ svc_thread_fn sv_function; /* main function for threads */ - int sv_kill_signal; /* signal to kill threads */ }; /* @@ -388,8 +387,8 @@ struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv*), - svc_thread_fn, int sig, struct module *); + void (*shutdown)(struct svc_serv*), svc_thread_fn, + struct module *); int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); void svc_destroy(struct svc_serv *); int svc_process(struct svc_rqst *); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 03a9f1a9e75c..5a32cb7c4bb4 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -434,7 +434,7 @@ EXPORT_SYMBOL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, void (*shutdown)(struct svc_serv *serv), - svc_thread_fn func, int sig, struct module *mod) + svc_thread_fn func, struct module *mod) { struct svc_serv *serv; unsigned int npools = svc_pool_map_get(); @@ -443,7 +443,6 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, if (serv != NULL) { serv->sv_function = func; - serv->sv_kill_signal = sig; serv->sv_module = mod; } @@ -683,7 +682,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) /* destroy old threads */ while (nrservs < 0 && (task = choose_victim(serv, pool, &state)) != NULL) { - send_sig(serv->sv_kill_signal, task, 1); + send_sig(SIGINT, task, 1); nrservs++; } -- cgit v1.2.3 From 8837abcab3d16608bd2c7fac051a839d48f2f30c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 16 Jun 2008 13:20:29 +0200 Subject: nfsd: rename MAY_ flags Rename nfsd_permission() specific MAY_* flags to NFSD_MAY_* to make it clear, that these are not used outside nfsd, and to avoid name and number space conflicts with the VFS. [comment from hch: rename MAY_READ, MAY_WRITE and MAY_EXEC as well] Signed-off-by: Miklos Szeredi Signed-off-by: J. Bruce Fields --- fs/nfsd/lockd.c | 2 +- fs/nfsd/nfs2acl.c | 7 +-- fs/nfsd/nfs3acl.c | 5 +- fs/nfsd/nfs3proc.c | 8 ++-- fs/nfsd/nfs4proc.c | 23 ++++++---- fs/nfsd/nfs4state.c | 6 +-- fs/nfsd/nfsfh.c | 2 +- fs/nfsd/nfsproc.c | 8 ++-- fs/nfsd/vfs.c | 115 ++++++++++++++++++++++++---------------------- include/linux/nfsd/nfsd.h | 26 +++++------ 10 files changed, 105 insertions(+), 97 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 9e4a568a5013..6b6225ac4926 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -35,7 +35,7 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) fh.fh_export = NULL; exp_readlock(); - nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp); + nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp); fh_put(&fh); rqstp->rq_client = NULL; exp_readunlock(); diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 1c3b7654e966..4e3219e84116 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -40,7 +40,8 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp, dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); - if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (nfserr) RETURN_STATUS(nfserr); if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) @@ -107,7 +108,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (!nfserr) { nfserr = nfserrno( nfsd_set_posix_acl( @@ -134,7 +135,7 @@ static __be32 nfsacld_proc_getattr(struct svc_rqst * rqstp, dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - return fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + return fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); } /* diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index b647f2f872dc..9981dbb377a3 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -36,7 +36,8 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst * rqstp, __be32 nfserr = 0; fh = fh_copy(&resp->fh, &argp->fh); - if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (nfserr) RETURN_STATUS(nfserr); if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) @@ -101,7 +102,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp, __be32 nfserr = 0; fh = fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (!nfserr) { nfserr = nfserrno( nfsd_set_posix_acl( diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index c721a1e6e9dd..4d617ea28cfc 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -63,7 +63,7 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); if (nfserr) RETURN_STATUS(nfserr); @@ -242,7 +242,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, attr = &argp->attrs; /* Get the directory inode */ - nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE); + nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_CREATE); if (nfserr) RETURN_STATUS(nfserr); @@ -558,7 +558,7 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, resp->f_maxfilesize = ~(u32) 0; resp->f_properties = NFS3_FSF_DEFAULT; - nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); /* Check special features of the file system. May request * different read/write sizes for file systems known to have @@ -597,7 +597,7 @@ nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, resp->p_case_insensitive = 0; resp->p_case_preserving = 1; - nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); if (nfserr == 0) { struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 313484380a9b..5c3683cfd59e 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -71,11 +71,11 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs return nfserr_inval; if (open->op_share_access & NFS4_SHARE_ACCESS_READ) - accmode |= MAY_READ; + accmode |= NFSD_MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - accmode |= (MAY_WRITE | MAY_TRUNC); + accmode |= (NFSD_MAY_WRITE | NFSD_MAY_TRUNC); if (open->op_share_deny & NFS4_SHARE_DENY_WRITE) - accmode |= MAY_WRITE; + accmode |= NFSD_MAY_WRITE; status = fh_verify(rqstp, current_fh, S_IFREG, accmode); @@ -126,7 +126,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o &resfh.fh_handle.fh_base, resfh.fh_handle.fh_size); if (!created) - status = do_open_permission(rqstp, current_fh, open, MAY_NOP); + status = do_open_permission(rqstp, current_fh, open, + NFSD_MAY_NOP); out: fh_put(&resfh); @@ -157,7 +158,8 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && (open->op_iattr.ia_size == 0); - status = do_open_permission(rqstp, current_fh, open, MAY_OWNER_OVERRIDE); + status = do_open_permission(rqstp, current_fh, open, + NFSD_MAY_OWNER_OVERRIDE); return status; } @@ -186,7 +188,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cstate->current_fh.fh_handle.fh_size = rp->rp_openfh_len; memcpy(&cstate->current_fh.fh_handle.fh_base, rp->rp_openfh, rp->rp_openfh_len); - status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) dprintk("nfsd4_open: replay failed" " restoring previous filehandle\n"); @@ -285,7 +287,7 @@ nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen; memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval, putfh->pf_fhlen); - return fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + return fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); } static __be32 @@ -363,7 +365,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, fh_init(&resfh, NFS4_FHSIZE); - status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, MAY_CREATE); + status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, + NFSD_MAY_CREATE); if (status == nfserr_symlink) status = nfserr_notdir; if (status) @@ -445,7 +448,7 @@ nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; - status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) return status; @@ -730,7 +733,7 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, int count; __be32 status; - status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) return status; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bf11d6879ab4..eca8aaa450f1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1722,9 +1722,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf /* Stateid was not found, this is a new OPEN */ int flags = 0; if (open->op_share_access & NFS4_SHARE_ACCESS_READ) - flags |= MAY_READ; + flags |= NFSD_MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - flags |= MAY_WRITE; + flags |= NFSD_MAY_WRITE; status = nfs4_new_open(rqstp, &stp, dp, current_fh, flags); if (status) goto out; @@ -2610,7 +2610,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfserr_inval; if ((status = fh_verify(rqstp, &cstate->current_fh, - S_IFREG, MAY_LOCK))) { + S_IFREG, NFSD_MAY_LOCK))) { dprintk("NFSD: nfsd4_lock: permission denied!\n"); return status; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 100ae5641162..c7b0fdaeac96 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -279,7 +279,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (error) goto out; - if (!(access & MAY_LOCK)) { + if (!(access & NFSD_MAY_LOCK)) { /* * pseudoflavor restrictions are not enforced on NLM, * which clients virtually always use auth_sys for, diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index b5a20c486712..0766f95d236a 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -65,7 +65,7 @@ nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); return nfsd_return_attrs(nfserr, resp); } @@ -215,11 +215,11 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, SVCFH_fmt(dirfhp), argp->len, argp->name); /* First verify the parent file handle */ - nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC); + nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC); if (nfserr) goto done; /* must fh_put dirfhp even on error */ - /* Check for MAY_WRITE in nfsd_create if necessary */ + /* Check for NFSD_MAY_WRITE in nfsd_create if necessary */ nfserr = nfserr_acces; if (!argp->len) @@ -281,7 +281,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, nfserr = nfsd_permission(rqstp, newfhp->fh_export, newfhp->fh_dentry, - MAY_WRITE|MAY_LOCAL_ACCESS); + NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS); if (nfserr && nfserr != nfserr_rofs) goto out_unlock; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index a3a291f771f4..5e05ddda4560 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -144,7 +144,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (err) return err; @@ -262,14 +262,14 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, { struct dentry *dentry; struct inode *inode; - int accmode = MAY_SATTR; + int accmode = NFSD_MAY_SATTR; int ftype = 0; __be32 err; int host_err; int size_change = 0; if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) - accmode |= MAY_WRITE|MAY_OWNER_OVERRIDE; + accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; if (iap->ia_valid & ATTR_SIZE) ftype = S_IFREG; @@ -331,7 +331,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { - err = nfsd_permission(rqstp, fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); + err = nfsd_permission(rqstp, fhp->fh_export, dentry, + NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; } @@ -462,7 +463,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int flags = 0; /* Get inode */ - error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); + error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR); if (error) return error; @@ -563,20 +564,20 @@ struct accessmap { int how; }; static struct accessmap nfs3_regaccess[] = { - { NFS3_ACCESS_READ, MAY_READ }, - { NFS3_ACCESS_EXECUTE, MAY_EXEC }, - { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_TRUNC }, - { NFS3_ACCESS_EXTEND, MAY_WRITE }, + { NFS3_ACCESS_READ, NFSD_MAY_READ }, + { NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC }, + { NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC }, + { NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE }, { 0, 0 } }; static struct accessmap nfs3_diraccess[] = { - { NFS3_ACCESS_READ, MAY_READ }, - { NFS3_ACCESS_LOOKUP, MAY_EXEC }, - { NFS3_ACCESS_MODIFY, MAY_EXEC|MAY_WRITE|MAY_TRUNC }, - { NFS3_ACCESS_EXTEND, MAY_EXEC|MAY_WRITE }, - { NFS3_ACCESS_DELETE, MAY_REMOVE }, + { NFS3_ACCESS_READ, NFSD_MAY_READ }, + { NFS3_ACCESS_LOOKUP, NFSD_MAY_EXEC }, + { NFS3_ACCESS_MODIFY, NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC}, + { NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE }, + { NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE }, { 0, 0 } }; @@ -589,10 +590,10 @@ static struct accessmap nfs3_anyaccess[] = { * mainly at mode bits, and we make sure to ignore read-only * filesystem checks */ - { NFS3_ACCESS_READ, MAY_READ }, - { NFS3_ACCESS_EXECUTE, MAY_EXEC }, - { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_LOCAL_ACCESS }, - { NFS3_ACCESS_EXTEND, MAY_WRITE|MAY_LOCAL_ACCESS }, + { NFS3_ACCESS_READ, NFSD_MAY_READ }, + { NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC }, + { NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS }, + { NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS }, { 0, 0 } }; @@ -606,7 +607,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor u32 query, result = 0, sresult = 0; __be32 error; - error = fh_verify(rqstp, fhp, 0, MAY_NOP); + error = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); if (error) goto out; @@ -678,7 +679,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * and (hopefully) checked permission - so allow OWNER_OVERRIDE * in case a chmod has now revoked permission. */ - err = fh_verify(rqstp, fhp, type, access | MAY_OWNER_OVERRIDE); + err = fh_verify(rqstp, fhp, type, access | NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; @@ -689,7 +690,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * or any access when mandatory locking enabled */ err = nfserr_perm; - if (IS_APPEND(inode) && (access & MAY_WRITE)) + if (IS_APPEND(inode) && (access & NFSD_MAY_WRITE)) goto out; /* * We must ignore files (but only files) which might have mandatory @@ -706,14 +707,14 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * Check to see if there are any leases on this file. * This may block while leases are broken. */ - host_err = break_lease(inode, O_NONBLOCK | ((access & MAY_WRITE) ? FMODE_WRITE : 0)); + host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? FMODE_WRITE : 0)); if (host_err == -EWOULDBLOCK) host_err = -ETIMEDOUT; if (host_err) /* NOMEM or WOULDBLOCK */ goto out_nfserr; - if (access & MAY_WRITE) { - if (access & MAY_READ) + if (access & NFSD_MAY_WRITE) { + if (access & NFSD_MAY_READ) flags = O_RDWR|O_LARGEFILE; else flags = O_WRONLY|O_LARGEFILE; @@ -1069,12 +1070,12 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (file) { err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, - MAY_READ|MAY_OWNER_OVERRIDE); + NFSD_MAY_READ|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); } else { - err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file); + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); if (err) goto out; err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); @@ -1098,13 +1099,13 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (file) { err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, - MAY_WRITE|MAY_OWNER_OVERRIDE); + NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt, stablep); } else { - err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file); + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file); if (err) goto out; @@ -1136,7 +1137,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, if ((u64)count > ~(u64)offset) return nfserr_inval; - if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0) + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file); + if (err) return err; if (EX_ISSYNC(fhp->fh_export)) { if (file->f_op && file->f_op->fsync) { @@ -1197,7 +1199,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (isdotent(fname, flen)) goto out; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; @@ -1334,7 +1336,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; @@ -1471,7 +1473,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) __be32 err; int host_err; - err = fh_verify(rqstp, fhp, S_IFLNK, MAY_NOP); + err = fh_verify(rqstp, fhp, S_IFLNK, NFSD_MAY_NOP); if (err) goto out; @@ -1526,7 +1528,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, if (isdotent(fname, flen)) goto out; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; fh_lock(fhp); @@ -1591,10 +1593,10 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, __be32 err; int host_err; - err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; - err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP); + err = fh_verify(rqstp, tfhp, -S_IFDIR, NFSD_MAY_NOP); if (err) goto out; @@ -1661,10 +1663,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, __be32 err; int host_err; - err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE); + err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE); if (err) goto out; - err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, tfhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; @@ -1768,7 +1770,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, err = nfserr_acces; if (!flen || isdotent(fname, flen)) goto out; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_REMOVE); if (err) goto out; @@ -1834,7 +1836,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, struct file *file; loff_t offset = *offsetp; - err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file); + err = nfsd_open(rqstp, fhp, S_IFDIR, NFSD_MAY_READ, &file); if (err) goto out; @@ -1875,7 +1877,7 @@ out: __be32 nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) { - __be32 err = fh_verify(rqstp, fhp, 0, MAY_NOP); + __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); if (!err && vfs_statfs(fhp->fh_dentry,stat)) err = nfserr_io; return err; @@ -1896,18 +1898,18 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, struct inode *inode = dentry->d_inode; int err; - if (acc == MAY_NOP) + if (acc == NFSD_MAY_NOP) return 0; #if 0 dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n", acc, - (acc & MAY_READ)? " read" : "", - (acc & MAY_WRITE)? " write" : "", - (acc & MAY_EXEC)? " exec" : "", - (acc & MAY_SATTR)? " sattr" : "", - (acc & MAY_TRUNC)? " trunc" : "", - (acc & MAY_LOCK)? " lock" : "", - (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "", + (acc & NFSD_MAY_READ)? " read" : "", + (acc & NFSD_MAY_WRITE)? " write" : "", + (acc & NFSD_MAY_EXEC)? " exec" : "", + (acc & NFSD_MAY_SATTR)? " sattr" : "", + (acc & NFSD_MAY_TRUNC)? " trunc" : "", + (acc & NFSD_MAY_LOCK)? " lock" : "", + (acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "", inode->i_mode, IS_IMMUTABLE(inode)? " immut" : "", IS_APPEND(inode)? " append" : "", @@ -1920,18 +1922,18 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, * system. But if it is IRIX doing check on write-access for a * device special file, we ignore rofs. */ - if (!(acc & MAY_LOCAL_ACCESS)) - if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { + if (!(acc & NFSD_MAY_LOCAL_ACCESS)) + if (acc & (NFSD_MAY_WRITE | NFSD_MAY_SATTR | NFSD_MAY_TRUNC)) { if (exp_rdonly(rqstp, exp) || __mnt_is_readonly(exp->ex_path.mnt)) return nfserr_rofs; - if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) + if (/* (acc & NFSD_MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; } - if ((acc & MAY_TRUNC) && IS_APPEND(inode)) + if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode)) return nfserr_perm; - if (acc & MAY_LOCK) { + if (acc & NFSD_MAY_LOCK) { /* If we cannot rely on authentication in NLM requests, * just allow locks, otherwise require read permission, or * ownership @@ -1939,7 +1941,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, if (exp->ex_flags & NFSEXP_NOAUTHNLM) return 0; else - acc = MAY_READ | MAY_OWNER_OVERRIDE; + acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE; } /* * The file owner always gets access permission for accesses that @@ -1955,15 +1957,16 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, * We must trust the client to do permission checking - using "ACCESS" * with NFSv3. */ - if ((acc & MAY_OWNER_OVERRIDE) && + if ((acc & NFSD_MAY_OWNER_OVERRIDE) && inode->i_uid == current->fsuid) return 0; + /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */ err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL); /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && - acc == (MAY_READ | MAY_OWNER_OVERRIDE)) + acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) err = permission(inode, MAY_EXEC, NULL); return err? nfserrno(err) : 0; diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 88d85b964429..a2861d95ecc3 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -28,20 +28,20 @@ #define NFSD_SUPPORTED_MINOR_VERSION 0 /* - * Special flags for nfsd_permission. These must be different from MAY_READ, - * MAY_WRITE, and MAY_EXEC. + * Flags for nfsd_permission */ -#define MAY_NOP 0 -#define MAY_SATTR 8 -#define MAY_TRUNC 16 -#define MAY_LOCK 32 -#define MAY_OWNER_OVERRIDE 64 -#define MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ -#if (MAY_SATTR | MAY_TRUNC | MAY_LOCK | MAY_OWNER_OVERRIDE | MAY_LOCAL_ACCESS) & (MAY_READ | MAY_WRITE | MAY_EXEC) -# error "please use a different value for MAY_SATTR or MAY_TRUNC or MAY_LOCK or MAY_LOCAL_ACCESS or MAY_OWNER_OVERRIDE." -#endif -#define MAY_CREATE (MAY_EXEC|MAY_WRITE) -#define MAY_REMOVE (MAY_EXEC|MAY_WRITE|MAY_TRUNC) +#define NFSD_MAY_NOP 0 +#define NFSD_MAY_EXEC 1 /* == MAY_EXEC */ +#define NFSD_MAY_WRITE 2 /* == MAY_WRITE */ +#define NFSD_MAY_READ 4 /* == MAY_READ */ +#define NFSD_MAY_SATTR 8 +#define NFSD_MAY_TRUNC 16 +#define NFSD_MAY_LOCK 32 +#define NFSD_MAY_OWNER_OVERRIDE 64 +#define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ + +#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) +#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) /* * Callback function for readdir -- cgit v1.2.3 From d00953a53e9a2edbe005c1e596f1e96a8a293401 Mon Sep 17 00:00:00 2001 From: Kevin Coffman Date: Wed, 30 Apr 2008 12:45:53 -0400 Subject: gss_krb5: create a define for token header size and clean up ptr location cleanup: Document token header size with a #define instead of open-coding it. Don't needlessly increment "ptr" past the beginning of the header which makes the values passed to functions more understandable and eliminates the need for extra "krb5_hdr" pointer. Clean up some intersecting white-space issues flagged by checkpatch.pl. This leaves the checksum length hard-coded at 8 for DES. A later patch cleans that up. Signed-off-by: Kevin Coffman Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/gss_krb5.h | 3 +++ net/sunrpc/auth_gss/gss_krb5_seal.c | 26 +++++++++--------- net/sunrpc/auth_gss/gss_krb5_unseal.c | 16 +++++------ net/sunrpc/auth_gss/gss_krb5_wrap.c | 50 +++++++++++++++++------------------ 4 files changed, 49 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index a10f1fb0bf7c..e7bbdba474d5 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -51,6 +51,9 @@ struct krb5_ctx { extern spinlock_t krb5_seq_lock; +/* The length of the Kerberos GSS token header */ +#define GSS_KRB5_TOK_HDR_LEN (16) + #define KG_TOK_MIC_MSG 0x0101 #define KG_TOK_WRAP_MSG 0x0201 diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 5f1d36dfbcf7..b8f42ef7178e 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -78,7 +78,7 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, struct krb5_ctx *ctx = gss_ctx->internal_ctx_id; char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; - unsigned char *ptr, *krb5_hdr, *msg_start; + unsigned char *ptr, *msg_start; s32 now; u32 seq_send; @@ -87,36 +87,36 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, now = get_seconds(); - token->len = g_token_size(&ctx->mech_used, 24); + token->len = g_token_size(&ctx->mech_used, GSS_KRB5_TOK_HDR_LEN + 8); ptr = token->data; - g_make_token_header(&ctx->mech_used, 24, &ptr); + g_make_token_header(&ctx->mech_used, GSS_KRB5_TOK_HDR_LEN + 8, &ptr); - *ptr++ = (unsigned char) ((KG_TOK_MIC_MSG>>8)&0xff); - *ptr++ = (unsigned char) (KG_TOK_MIC_MSG&0xff); + /* ptr now at header described in rfc 1964, section 1.2.1: */ + ptr[0] = (unsigned char) ((KG_TOK_MIC_MSG >> 8) & 0xff); + ptr[1] = (unsigned char) (KG_TOK_MIC_MSG & 0xff); - /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */ - krb5_hdr = ptr - 2; - msg_start = krb5_hdr + 24; + msg_start = ptr + GSS_KRB5_TOK_HDR_LEN + 8; - *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5); - memset(krb5_hdr + 4, 0xff, 4); + *(__be16 *)(ptr + 2) = htons(SGN_ALG_DES_MAC_MD5); + memset(ptr + 4, 0xff, 4); - if (make_checksum("md5", krb5_hdr, 8, text, 0, &md5cksum)) + if (make_checksum("md5", ptr, 8, text, 0, &md5cksum)) return GSS_S_FAILURE; if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, md5cksum.len)) return GSS_S_FAILURE; - memcpy(krb5_hdr + 16, md5cksum.data + md5cksum.len - 8, 8); + memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8); spin_lock(&krb5_seq_lock); seq_send = ctx->seq_send++; spin_unlock(&krb5_seq_lock); if (krb5_make_seq_num(ctx->seq, ctx->initiate ? 0 : 0xff, - seq_send, krb5_hdr + 16, krb5_hdr + 8)) + seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, + ptr + 8)) return GSS_S_FAILURE; return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index d91a5d004803..066ec73c84d6 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -92,30 +92,30 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx, read_token->len)) return GSS_S_DEFECTIVE_TOKEN; - if ((*ptr++ != ((KG_TOK_MIC_MSG>>8)&0xff)) || - (*ptr++ != ( KG_TOK_MIC_MSG &0xff)) ) + if ((ptr[0] != ((KG_TOK_MIC_MSG >> 8) & 0xff)) || + (ptr[1] != (KG_TOK_MIC_MSG & 0xff))) return GSS_S_DEFECTIVE_TOKEN; /* XXX sanity-check bodysize?? */ - signalg = ptr[0] + (ptr[1] << 8); + signalg = ptr[2] + (ptr[3] << 8); if (signalg != SGN_ALG_DES_MAC_MD5) return GSS_S_DEFECTIVE_TOKEN; - sealalg = ptr[2] + (ptr[3] << 8); + sealalg = ptr[4] + (ptr[5] << 8); if (sealalg != SEAL_ALG_NONE) return GSS_S_DEFECTIVE_TOKEN; - if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) + if ((ptr[6] != 0xff) || (ptr[7] != 0xff)) return GSS_S_DEFECTIVE_TOKEN; - if (make_checksum("md5", ptr - 2, 8, message_buffer, 0, &md5cksum)) + if (make_checksum("md5", ptr, 8, message_buffer, 0, &md5cksum)) return GSS_S_FAILURE; if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, 16)) return GSS_S_FAILURE; - if (memcmp(md5cksum.data + 8, ptr + 14, 8)) + if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8)) return GSS_S_BAD_SIG; /* it got through unscathed. Make sure the context is unexpired */ @@ -127,7 +127,7 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx, /* do sequencing checks */ - if (krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum)) + if (krb5_get_seq_num(ctx->seq, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8, &direction, &seqnum)) return GSS_S_FAILURE; if ((ctx->initiate && direction != 0xff) || diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index b00b1b426301..283cb25c6237 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -122,7 +122,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; int blocksize = 0, plainlen; - unsigned char *ptr, *krb5_hdr, *msg_start; + unsigned char *ptr, *msg_start; s32 now; int headlen; struct page **tmp_pages; @@ -149,26 +149,26 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, buf->len += headlen; BUG_ON((buf->len - offset - headlen) % blocksize); - g_make_token_header(&kctx->mech_used, 24 + plainlen, &ptr); + g_make_token_header(&kctx->mech_used, + GSS_KRB5_TOK_HDR_LEN + 8 + plainlen, &ptr); - *ptr++ = (unsigned char) ((KG_TOK_WRAP_MSG>>8)&0xff); - *ptr++ = (unsigned char) (KG_TOK_WRAP_MSG&0xff); + /* ptr now at header described in rfc 1964, section 1.2.1: */ + ptr[0] = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff); + ptr[1] = (unsigned char) (KG_TOK_WRAP_MSG & 0xff); - /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */ - krb5_hdr = ptr - 2; - msg_start = krb5_hdr + 24; + msg_start = ptr + 24; - *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5); - memset(krb5_hdr + 4, 0xff, 4); - *(__be16 *)(krb5_hdr + 4) = htons(SEAL_ALG_DES); + *(__be16 *)(ptr + 2) = htons(SGN_ALG_DES_MAC_MD5); + memset(ptr + 4, 0xff, 4); + *(__be16 *)(ptr + 4) = htons(SEAL_ALG_DES); make_confounder(msg_start, blocksize); /* XXXJBF: UGH!: */ tmp_pages = buf->pages; buf->pages = pages; - if (make_checksum("md5", krb5_hdr, 8, buf, + if (make_checksum("md5", ptr, 8, buf, offset + headlen - blocksize, &md5cksum)) return GSS_S_FAILURE; buf->pages = tmp_pages; @@ -176,7 +176,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, md5cksum.data, md5cksum.len)) return GSS_S_FAILURE; - memcpy(krb5_hdr + 16, md5cksum.data + md5cksum.len - 8, 8); + memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8); spin_lock(&krb5_seq_lock); seq_send = kctx->seq_send++; @@ -185,7 +185,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, /* XXX would probably be more efficient to compute checksum * and encrypt at the same time: */ if ((krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff, - seq_send, krb5_hdr + 16, krb5_hdr + 8))) + seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8))) return GSS_S_FAILURE; if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize, @@ -219,38 +219,38 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) buf->len - offset)) return GSS_S_DEFECTIVE_TOKEN; - if ((*ptr++ != ((KG_TOK_WRAP_MSG>>8)&0xff)) || - (*ptr++ != (KG_TOK_WRAP_MSG &0xff)) ) + if ((ptr[0] != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) || + (ptr[1] != (KG_TOK_WRAP_MSG & 0xff))) return GSS_S_DEFECTIVE_TOKEN; /* XXX sanity-check bodysize?? */ /* get the sign and seal algorithms */ - signalg = ptr[0] + (ptr[1] << 8); + signalg = ptr[2] + (ptr[3] << 8); if (signalg != SGN_ALG_DES_MAC_MD5) return GSS_S_DEFECTIVE_TOKEN; - sealalg = ptr[2] + (ptr[3] << 8); + sealalg = ptr[4] + (ptr[5] << 8); if (sealalg != SEAL_ALG_DES) return GSS_S_DEFECTIVE_TOKEN; - if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) + if ((ptr[6] != 0xff) || (ptr[7] != 0xff)) return GSS_S_DEFECTIVE_TOKEN; if (gss_decrypt_xdr_buf(kctx->enc, buf, - ptr + 22 - (unsigned char *)buf->head[0].iov_base)) + ptr + GSS_KRB5_TOK_HDR_LEN + 8 - (unsigned char *)buf->head[0].iov_base)) return GSS_S_DEFECTIVE_TOKEN; - if (make_checksum("md5", ptr - 2, 8, buf, - ptr + 22 - (unsigned char *)buf->head[0].iov_base, &md5cksum)) + if (make_checksum("md5", ptr, 8, buf, + ptr + GSS_KRB5_TOK_HDR_LEN + 8 - (unsigned char *)buf->head[0].iov_base, &md5cksum)) return GSS_S_FAILURE; if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, md5cksum.data, md5cksum.len)) return GSS_S_FAILURE; - if (memcmp(md5cksum.data + 8, ptr + 14, 8)) + if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8)) return GSS_S_BAD_SIG; /* it got through unscathed. Make sure the context is unexpired */ @@ -262,8 +262,8 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) /* do sequencing checks */ - if (krb5_get_seq_num(kctx->seq, ptr + 14, ptr + 6, &direction, - &seqnum)) + if (krb5_get_seq_num(kctx->seq, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8, + &direction, &seqnum)) return GSS_S_BAD_SIG; if ((kctx->initiate && direction != 0xff) || @@ -274,7 +274,7 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) * better to copy and encrypt at the same time. */ blocksize = crypto_blkcipher_blocksize(kctx->enc); - data_start = ptr + 22 + blocksize; + data_start = ptr + GSS_KRB5_TOK_HDR_LEN + 8 + blocksize; orig_start = buf->head[0].iov_base + offset; data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start; memmove(orig_start, data_start, data_len); -- cgit v1.2.3 From 395a59d0f8e86bb39cd700c3d185d30c670bb958 Mon Sep 17 00:00:00 2001 From: Abhishek Sagar Date: Sat, 21 Jun 2008 23:47:27 +0530 Subject: ftrace: store mcount address in rec->ip Record the address of the mcount call-site. Currently all archs except sparc64 record the address of the instruction following the mcount call-site. Some general cleanups are entailed. Storing mcount addresses in rec->ip enables looking them up in the kprobe hash table later on to check if they're kprobe'd. Signed-off-by: Abhishek Sagar Cc: davem@davemloft.net Cc: Steven Rostedt Signed-off-by: Ingo Molnar --- arch/arm/kernel/armksyms.c | 10 +++++----- arch/arm/kernel/entry-common.S | 4 ++++ arch/arm/kernel/ftrace.c | 16 +++++++--------- arch/powerpc/kernel/entry_32.S | 4 ++++ arch/powerpc/kernel/entry_64.S | 5 ++++- arch/powerpc/kernel/ftrace.c | 21 +++++++-------------- arch/sparc64/kernel/ftrace.c | 10 ++++++---- arch/sparc64/kernel/sparc64_ksyms.c | 2 +- arch/x86/kernel/entry_32.S | 4 ++++ arch/x86/kernel/entry_64.S | 4 ++++ arch/x86/kernel/ftrace.c | 26 +++++++++----------------- arch/x86/kernel/i386_ksyms_32.c | 2 +- arch/x86/kernel/x8664_ksyms_64.c | 2 +- include/asm-arm/ftrace.h | 14 ++++++++++++++ include/asm-powerpc/ftrace.h | 8 ++++++++ include/asm-sparc64/ftrace.h | 14 ++++++++++++++ include/asm-x86/ftrace.h | 14 ++++++++++++++ include/linux/ftrace.h | 3 +-- kernel/trace/ftrace.c | 3 ++- 19 files changed, 110 insertions(+), 56 deletions(-) create mode 100644 include/asm-arm/ftrace.h create mode 100644 include/asm-sparc64/ftrace.h create mode 100644 include/asm-x86/ftrace.h (limited to 'include/linux') diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 3b132215cbf8..cc7b246e9652 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -18,6 +18,7 @@ #include #include #include +#include /* * libgcc functions - functions that are used internally by the @@ -48,11 +49,6 @@ extern void __aeabi_ulcmp(void); extern void fpundefinstr(void); extern void fp_enter(void); -#ifdef CONFIG_FTRACE -extern void mcount(void); -EXPORT_SYMBOL(mcount); -#endif - /* * This has a special calling convention; it doesn't * modify any of the usual registers, except for LR. @@ -186,3 +182,7 @@ EXPORT_SYMBOL(_find_next_bit_be); #endif EXPORT_SYMBOL(copy_page); + +#ifdef CONFIG_FTRACE +EXPORT_SYMBOL(mcount); +#endif diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 8f79a4789ed4..84694e88b428 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -9,6 +9,7 @@ */ #include +#include #include #include "entry-header.S" @@ -104,6 +105,7 @@ ENTRY(ret_from_fork) ENTRY(mcount) stmdb sp!, {r0-r3, lr} mov r0, lr + sub r0, r0, #MCOUNT_INSN_SIZE .globl mcount_call mcount_call: @@ -114,6 +116,7 @@ ENTRY(ftrace_caller) stmdb sp!, {r0-r3, lr} ldr r1, [fp, #-4] mov r0, lr + sub r0, r0, #MCOUNT_INSN_SIZE .globl ftrace_call ftrace_call: @@ -134,6 +137,7 @@ ENTRY(mcount) trace: ldr r1, [fp, #-4] mov r0, lr + sub r0, r0, #MCOUNT_INSN_SIZE mov lr, pc mov pc, r2 ldmia sp!, {r0-r3, pc} diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 22f3d6e309f9..76d50e6091bc 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c @@ -12,9 +12,10 @@ */ #include + #include +#include -#define INSN_SIZE 4 #define PC_OFFSET 8 #define BL_OPCODE 0xeb000000 #define BL_OFFSET_MASK 0x00ffffff @@ -32,10 +33,10 @@ unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr) { long offset; - offset = (long)addr - (long)(pc - INSN_SIZE + PC_OFFSET); + offset = (long)addr - (long)(pc + PC_OFFSET); if (unlikely(offset < -33554432 || offset > 33554428)) { /* Can't generate branches that far (from ARM ARM). Ftrace - * doesn't generate branches outside of core kernel text. + * doesn't generate branches outside of kernel text. */ WARN_ON_ONCE(1); return NULL; @@ -52,7 +53,6 @@ int ftrace_modify_code(unsigned long pc, unsigned char *old_code, old = *(unsigned long *)old_code; new = *(unsigned long *)new_code; - pc -= INSN_SIZE; __asm__ __volatile__ ( "1: ldr %1, [%2] \n" @@ -77,7 +77,7 @@ int ftrace_modify_code(unsigned long pc, unsigned char *old_code, : "memory"); if (!err && (replaced == old)) - flush_icache_range(pc, pc + INSN_SIZE); + flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); return err; } @@ -89,8 +89,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func) unsigned char *new; pc = (unsigned long)&ftrace_call; - pc += INSN_SIZE; - memcpy(&old, &ftrace_call, INSN_SIZE); + memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(pc, (unsigned long)func); ret = ftrace_modify_code(pc, (unsigned char *)&old, new); return ret; @@ -103,8 +102,7 @@ int ftrace_mcount_set(unsigned long *data) unsigned char *new; pc = (unsigned long)&mcount_call; - pc += INSN_SIZE; - memcpy(&old, &mcount_call, INSN_SIZE); + memcpy(&old, &mcount_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(pc, *addr); *addr = ftrace_modify_code(pc, (unsigned char *)&old, new); return 0; diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 3b1dd29d9f91..7231a708af0d 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -30,6 +30,7 @@ #include #include #include +#include #undef SHOW_SYSCALLS #undef SHOW_SYSCALLS_TASK @@ -1053,6 +1054,7 @@ _GLOBAL(_mcount) stw r10,40(r1) stw r3, 44(r1) stw r5, 8(r1) + subi r3, r3, MCOUNT_INSN_SIZE .globl mcount_call mcount_call: bl ftrace_stub @@ -1090,6 +1092,7 @@ _GLOBAL(ftrace_caller) stw r10,40(r1) stw r3, 44(r1) stw r5, 8(r1) + subi r3, r3, MCOUNT_INSN_SIZE .globl ftrace_call ftrace_call: bl ftrace_stub @@ -1128,6 +1131,7 @@ _GLOBAL(_mcount) stw r3, 44(r1) stw r5, 8(r1) + subi r3, r3, MCOUNT_INSN_SIZE LOAD_REG_ADDR(r5, ftrace_trace_function) lwz r5,0(r5) diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 2c4d9e056ead..2f511a969d2c 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -31,6 +31,7 @@ #include #include #include +#include /* * System calls. @@ -879,6 +880,7 @@ _GLOBAL(_mcount) mflr r3 stdu r1, -112(r1) std r3, 128(r1) + subi r3, r3, MCOUNT_INSN_SIZE .globl mcount_call mcount_call: bl ftrace_stub @@ -895,6 +897,7 @@ _GLOBAL(ftrace_caller) stdu r1, -112(r1) std r3, 128(r1) ld r4, 16(r11) + subi r3, r3, MCOUNT_INSN_SIZE .globl ftrace_call ftrace_call: bl ftrace_stub @@ -916,7 +919,7 @@ _GLOBAL(_mcount) std r3, 128(r1) ld r4, 16(r11) - + subi r3, r3, MCOUNT_INSN_SIZE LOAD_REG_ADDR(r5,ftrace_trace_function) ld r5,0(r5) ld r5,0(r5) diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index e12c593ab9ca..3855ceb937b0 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c @@ -15,8 +15,8 @@ #include #include +#include -#define CALL_BACK 4 static unsigned int ftrace_nop = 0x60000000; @@ -27,9 +27,10 @@ static unsigned int ftrace_nop = 0x60000000; # define GET_ADDR(addr) *(unsigned long *)addr #endif + static unsigned int notrace ftrace_calc_offset(long ip, long addr) { - return (int)((addr + CALL_BACK) - ip); + return (int)(addr - ip); } notrace unsigned char *ftrace_nop_replace(void) @@ -76,9 +77,6 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, unsigned new = *(unsigned *)new_code; int faulted = 0; - /* move the IP back to the start of the call */ - ip -= CALL_BACK; - /* * Note: Due to modules and __init, code can * disappear and change, we need to protect against faulting @@ -118,12 +116,10 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, notrace int ftrace_update_ftrace_func(ftrace_func_t func) { unsigned long ip = (unsigned long)(&ftrace_call); - unsigned char old[4], *new; + unsigned char old[MCOUNT_INSN_SIZE], *new; int ret; - ip += CALL_BACK; - - memcpy(old, &ftrace_call, 4); + memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(ip, (unsigned long)func); ret = ftrace_modify_code(ip, old, new); @@ -134,16 +130,13 @@ notrace int ftrace_mcount_set(unsigned long *data) { unsigned long ip = (long)(&mcount_call); unsigned long *addr = data; - unsigned char old[4], *new; - - /* ip is at the location, but modify code will subtact this */ - ip += CALL_BACK; + unsigned char old[MCOUNT_INSN_SIZE], *new; /* * Replace the mcount stub with a pointer to the * ip recorder function. */ - memcpy(old, &mcount_call, 4); + memcpy(old, &mcount_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(ip, *addr); *addr = ftrace_modify_code(ip, old, new); diff --git a/arch/sparc64/kernel/ftrace.c b/arch/sparc64/kernel/ftrace.c index c17373195b1e..4298d0aee713 100644 --- a/arch/sparc64/kernel/ftrace.c +++ b/arch/sparc64/kernel/ftrace.c @@ -5,6 +5,8 @@ #include #include +#include + static const u32 ftrace_nop = 0x01000000; notrace unsigned char *ftrace_nop_replace(void) @@ -60,9 +62,9 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, notrace int ftrace_update_ftrace_func(ftrace_func_t func) { unsigned long ip = (unsigned long)(&ftrace_call); - unsigned char old[4], *new; + unsigned char old[MCOUNT_INSN_SIZE], *new; - memcpy(old, &ftrace_call, 4); + memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(ip, (unsigned long)func); return ftrace_modify_code(ip, old, new); } @@ -71,13 +73,13 @@ notrace int ftrace_mcount_set(unsigned long *data) { unsigned long ip = (long)(&mcount_call); unsigned long *addr = data; - unsigned char old[4], *new; + unsigned char old[MCOUNT_INSN_SIZE], *new; /* * Replace the mcount stub with a pointer to the * ip recorder function. */ - memcpy(old, &mcount_call, 4); + memcpy(old, &mcount_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(ip, *addr); *addr = ftrace_modify_code(ip, old, new); diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 8ac0b99f2c55..b80d982a29c6 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -53,6 +53,7 @@ #include #include #include +#include struct poll { int fd; @@ -112,7 +113,6 @@ EXPORT_SYMBOL(smp_call_function); #endif /* CONFIG_SMP */ #if defined(CONFIG_MCOUNT) -extern void _mcount(void); EXPORT_SYMBOL(_mcount); #endif diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 04ea83ccb979..95e6bbe3665e 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -51,6 +51,7 @@ #include #include #include +#include #include "irq_vectors.h" /* @@ -1118,6 +1119,7 @@ ENTRY(mcount) pushl %ecx pushl %edx movl 0xc(%esp), %eax + subl $MCOUNT_INSN_SIZE, %eax .globl mcount_call mcount_call: @@ -1136,6 +1138,7 @@ ENTRY(ftrace_caller) pushl %edx movl 0xc(%esp), %eax movl 0x4(%ebp), %edx + subl $MCOUNT_INSN_SIZE, %eax .globl ftrace_call ftrace_call: @@ -1166,6 +1169,7 @@ trace: pushl %edx movl 0xc(%esp), %eax movl 0x4(%ebp), %edx + subl $MCOUNT_INSN_SIZE, %eax call *ftrace_trace_function diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index fe25e5febca3..b0f7308f78a6 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -51,6 +51,7 @@ #include #include #include +#include .code64 @@ -68,6 +69,7 @@ ENTRY(mcount) movq %r9, 48(%rsp) movq 0x38(%rsp), %rdi + subq $MCOUNT_INSN_SIZE, %rdi .globl mcount_call mcount_call: @@ -99,6 +101,7 @@ ENTRY(ftrace_caller) movq 0x38(%rsp), %rdi movq 8(%rbp), %rsi + subq $MCOUNT_INSN_SIZE, %rdi .globl ftrace_call ftrace_call: @@ -139,6 +142,7 @@ trace: movq 0x38(%rsp), %rdi movq 8(%rbp), %rsi + subq $MCOUNT_INSN_SIZE, %rdi call *ftrace_trace_function diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 55828149e01e..ab115cd15fdf 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -17,20 +17,21 @@ #include #include +#include -#define CALL_BACK 5 /* Long is fine, even if it is only 4 bytes ;-) */ static long *ftrace_nop; union ftrace_code_union { - char code[5]; + char code[MCOUNT_INSN_SIZE]; struct { char e8; int offset; } __attribute__((packed)); }; + static int notrace ftrace_calc_offset(long ip, long addr) { return (int)(addr - ip); @@ -46,7 +47,7 @@ notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) static union ftrace_code_union calc; calc.e8 = 0xe8; - calc.offset = ftrace_calc_offset(ip, addr); + calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr); /* * No locking needed, this must be called via kstop_machine @@ -65,9 +66,6 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, unsigned char newch = new_code[4]; int faulted = 0; - /* move the IP back to the start of the call */ - ip -= CALL_BACK; - /* * Note: Due to modules and __init, code can * disappear and change, we need to protect against faulting @@ -102,12 +100,10 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, notrace int ftrace_update_ftrace_func(ftrace_func_t func) { unsigned long ip = (unsigned long)(&ftrace_call); - unsigned char old[5], *new; + unsigned char old[MCOUNT_INSN_SIZE], *new; int ret; - ip += CALL_BACK; - - memcpy(old, &ftrace_call, 5); + memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(ip, (unsigned long)func); ret = ftrace_modify_code(ip, old, new); @@ -118,16 +114,13 @@ notrace int ftrace_mcount_set(unsigned long *data) { unsigned long ip = (long)(&mcount_call); unsigned long *addr = data; - unsigned char old[5], *new; - - /* ip is at the location, but modify code will subtact this */ - ip += CALL_BACK; + unsigned char old[MCOUNT_INSN_SIZE], *new; /* * Replace the mcount stub with a pointer to the * ip recorder function. */ - memcpy(old, &mcount_call, 5); + memcpy(old, &mcount_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(ip, *addr); *addr = ftrace_modify_code(ip, old, new); @@ -142,8 +135,7 @@ int __init ftrace_dyn_arch_init(void *data) ftrace_mcount_set(data); - ftrace_nop = (unsigned long *)noptable[CALL_BACK]; + ftrace_nop = (unsigned long *)noptable[MCOUNT_INSN_SIZE]; return 0; } - diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c index 29999dbb754c..dd7ebee446af 100644 --- a/arch/x86/kernel/i386_ksyms_32.c +++ b/arch/x86/kernel/i386_ksyms_32.c @@ -1,9 +1,9 @@ -#include #include #include #include #include +#include #ifdef CONFIG_FTRACE /* mcount is defined in assembly */ diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c index 122885bc5f3b..16ff4bf418d9 100644 --- a/arch/x86/kernel/x8664_ksyms_64.c +++ b/arch/x86/kernel/x8664_ksyms_64.c @@ -1,7 +1,6 @@ /* Exports for assembly files. All C exports should go in the respective C files. */ -#include #include #include @@ -11,6 +10,7 @@ #include #include #include +#include #ifdef CONFIG_FTRACE /* mcount is defined in assembly */ diff --git a/include/asm-arm/ftrace.h b/include/asm-arm/ftrace.h new file mode 100644 index 000000000000..584ef9a8e5a5 --- /dev/null +++ b/include/asm-arm/ftrace.h @@ -0,0 +1,14 @@ +#ifndef _ASM_ARM_FTRACE +#define _ASM_ARM_FTRACE + +#ifdef CONFIG_FTRACE +#define MCOUNT_ADDR ((long)(mcount)) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ + +#ifndef __ASSEMBLY__ +extern void mcount(void); +#endif + +#endif + +#endif /* _ASM_ARM_FTRACE */ diff --git a/include/asm-powerpc/ftrace.h b/include/asm-powerpc/ftrace.h index b1bfa704b6e5..de921326cca8 100644 --- a/include/asm-powerpc/ftrace.h +++ b/include/asm-powerpc/ftrace.h @@ -1,6 +1,14 @@ #ifndef _ASM_POWERPC_FTRACE #define _ASM_POWERPC_FTRACE +#ifdef CONFIG_FTRACE +#define MCOUNT_ADDR ((long)(_mcount)) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ + +#ifndef __ASSEMBLY__ extern void _mcount(void); +#endif #endif + +#endif /* _ASM_POWERPC_FTRACE */ diff --git a/include/asm-sparc64/ftrace.h b/include/asm-sparc64/ftrace.h new file mode 100644 index 000000000000..f76a40a338bb --- /dev/null +++ b/include/asm-sparc64/ftrace.h @@ -0,0 +1,14 @@ +#ifndef _ASM_SPARC64_FTRACE +#define _ASM_SPARC64_FTRACE + +#ifdef CONFIG_FTRACE +#define MCOUNT_ADDR ((long)(_mcount)) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ + +#ifndef __ASSEMBLY__ +extern void _mcount(void); +#endif + +#endif + +#endif /* _ASM_SPARC64_FTRACE */ diff --git a/include/asm-x86/ftrace.h b/include/asm-x86/ftrace.h new file mode 100644 index 000000000000..c184441133f2 --- /dev/null +++ b/include/asm-x86/ftrace.h @@ -0,0 +1,14 @@ +#ifndef _ASM_X86_FTRACE +#define _ASM_SPARC64_FTRACE + +#ifdef CONFIG_FTRACE +#define MCOUNT_ADDR ((long)(mcount)) +#define MCOUNT_INSN_SIZE 5 /* sizeof mcount call */ + +#ifndef __ASSEMBLY__ +extern void mcount(void); +#endif + +#endif /* CONFIG_FTRACE */ + +#endif /* _ASM_X86_FTRACE */ diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 20e14d0093c7..366098d591de 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -31,7 +31,6 @@ int unregister_ftrace_function(struct ftrace_ops *ops); void clear_ftrace_function(void); extern void ftrace_stub(unsigned long a0, unsigned long a1); -extern void mcount(void); #else /* !CONFIG_FTRACE */ # define register_ftrace_function(ops) do { } while (0) @@ -54,7 +53,7 @@ enum { struct dyn_ftrace { struct hlist_node node; - unsigned long ip; + unsigned long ip; /* address of mcount call-site */ unsigned long flags; }; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 0d5bcf69952d..f1e9e5c74e64 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -27,6 +27,8 @@ #include #include +#include + #include "trace.h" /* ftrace_enabled is a method to turn ftrace on or off */ @@ -329,7 +331,6 @@ ftrace_record_ip(unsigned long ip) } #define FTRACE_ADDR ((long)(ftrace_caller)) -#define MCOUNT_ADDR ((long)(mcount)) static int __ftrace_replace_code(struct dyn_ftrace *rec, -- cgit v1.2.3 From 785656a41f9a9c0e843a23d1ae05d900b5158f8f Mon Sep 17 00:00:00 2001 From: Abhishek Sagar Date: Sat, 21 Jun 2008 23:47:39 +0530 Subject: kprobes: enable clean usage of get_kprobe Allow clean use of get_kprobe() outside of core kprobe code. Ftrace makes use of get_kprobe to identify probes installed on mcount call-sites. Signed-off-by: Abhishek Sagar Acked-by: Ananth N Mavinakayanahalli Cc: Masami Hiramatsu Cc: jkenisto@us.ibm.com Cc: Steven Rostedt Signed-off-by: Ingo Molnar --- include/linux/kprobes.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 1036631ff4fa..04a3556bdea6 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -259,6 +259,10 @@ void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head); struct jprobe; struct kretprobe; +static inline struct kprobe *get_kprobe(void *addr) +{ + return NULL; +} static inline struct kprobe *kprobe_running(void) { return NULL; -- cgit v1.2.3 From ecea656d1d5e912d2f3d332657ea4a6d8380f891 Mon Sep 17 00:00:00 2001 From: Abhishek Sagar Date: Sat, 21 Jun 2008 23:47:53 +0530 Subject: ftrace: freeze kprobe'd records Let records identified as being kprobe'd be marked as "frozen". The trouble with records which have a kprobe installed on their mcount call-site is that they don't get updated. So if such a function which is currently being traced gets its tracing disabled due to a new filter rule (or because it was added to the notrace list) then it won't be updated and continue being traced. This patch allows scanning of all frozen records during tracing to check if they should be traced. Signed-off-by: Abhishek Sagar Cc: Steven Rostedt Signed-off-by: Ingo Molnar --- include/linux/ftrace.h | 6 ++++- kernel/trace/ftrace.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++- kernel/trace/trace.c | 3 +++ 3 files changed, 79 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 366098d591de..3121b95443d9 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -49,6 +49,7 @@ enum { FTRACE_FL_ENABLED = (1 << 3), FTRACE_FL_NOTRACE = (1 << 4), FTRACE_FL_CONVERTED = (1 << 5), + FTRACE_FL_FROZEN = (1 << 6), }; struct dyn_ftrace { @@ -73,15 +74,18 @@ extern void ftrace_caller(void); extern void ftrace_call(void); extern void mcount_call(void); +extern int skip_trace(unsigned long ip); + void ftrace_disable_daemon(void); void ftrace_enable_daemon(void); #else +# define skip_trace(ip) ({ 0; }) # define ftrace_force_update() ({ 0; }) # define ftrace_set_filter(buf, len, reset) do { } while (0) # define ftrace_disable_daemon() do { } while (0) # define ftrace_enable_daemon() do { } while (0) -#endif +#endif /* CONFIG_DYNAMIC_FTRACE */ /* totally disable ftrace - can not re-enable after this */ void ftrace_kill(void); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f1e9e5c74e64..d1238163155f 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -163,6 +163,8 @@ enum { }; static int ftrace_filtered; +static int tracing_on; +static int frozen_record_count; static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; @@ -195,6 +197,71 @@ static int ftrace_record_suspend; static struct dyn_ftrace *ftrace_free_records; + +#ifdef CONFIG_KPROBES +static inline void freeze_record(struct dyn_ftrace *rec) +{ + if (!(rec->flags & FTRACE_FL_FROZEN)) { + rec->flags |= FTRACE_FL_FROZEN; + frozen_record_count++; + } +} + +static inline void unfreeze_record(struct dyn_ftrace *rec) +{ + if (rec->flags & FTRACE_FL_FROZEN) { + rec->flags &= ~FTRACE_FL_FROZEN; + frozen_record_count--; + } +} + +static inline int record_frozen(struct dyn_ftrace *rec) +{ + return rec->flags & FTRACE_FL_FROZEN; +} +#else +# define freeze_record(rec) ({ 0; }) +# define unfreeze_record(rec) ({ 0; }) +# define record_frozen(rec) ({ 0; }) +#endif /* CONFIG_KPROBES */ + +int skip_trace(unsigned long ip) +{ + unsigned long fl; + struct dyn_ftrace *rec; + struct hlist_node *t; + struct hlist_head *head; + + if (frozen_record_count == 0) + return 0; + + head = &ftrace_hash[hash_long(ip, FTRACE_HASHBITS)]; + hlist_for_each_entry_rcu(rec, t, head, node) { + if (rec->ip == ip) { + if (record_frozen(rec)) { + if (rec->flags & FTRACE_FL_FAILED) + return 1; + + if (!(rec->flags & FTRACE_FL_CONVERTED)) + return 1; + + if (!tracing_on || !ftrace_enabled) + return 1; + + if (ftrace_filtered) { + fl = rec->flags & (FTRACE_FL_FILTER | + FTRACE_FL_NOTRACE); + if (!fl || (fl & FTRACE_FL_NOTRACE)) + return 1; + } + } + break; + } + } + + return 0; +} + static inline int ftrace_ip_in_hash(unsigned long ip, unsigned long key) { @@ -489,8 +556,11 @@ static int __ftrace_modify_code(void *data) */ __ftrace_update_code(NULL); ftrace_replace_code(1); - } else if (*command & FTRACE_DISABLE_CALLS) + tracing_on = 1; + } else if (*command & FTRACE_DISABLE_CALLS) { ftrace_replace_code(0); + tracing_on = 0; + } if (*command & FTRACE_UPDATE_TRACE_FUNC) ftrace_update_ftrace_func(ftrace_trace_function); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6e9dae7eb418..9ade79369bfb 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -988,6 +988,9 @@ function_trace_call(unsigned long ip, unsigned long parent_ip) if (unlikely(!tracer_enabled)) return; + if (skip_trace(ip)) + return; + local_irq_save(flags); cpu = raw_smp_processor_id(); data = tr->data[cpu]; -- cgit v1.2.3 From 3da757daf86e498872855f0b5e101f763ba79499 Mon Sep 17 00:00:00 2001 From: Alok Kataria Date: Fri, 20 Jun 2008 15:06:33 -0700 Subject: x86: use cpu_khz for loops_per_jiffy calculation On the x86 platform we can use the value of tsc_khz computed during tsc calibration to calculate the loops_per_jiffy value. Its very important to keep the error in lpj values to minimum as any error in that may result in kernel panic in check_timer. In virtualization environment, On a highly overloaded host the guest delay calibration may sometimes result in errors beyond the ~50% that timer_irq_works can handle, resulting in the guest panicking. Does some formating changes to lpj_setup code to now have a single printk to print the bogomips value. We do this only for the boot processor because the AP's can have different base frequencies or the BIOS might boot a AP at a different frequency. Signed-off-by: Alok N Kataria Cc: Arjan van de Ven Cc: Daniel Hecht Cc: Tim Mann Cc: Zach Amsden Cc: Sahil Rihan Signed-off-by: Ingo Molnar --- arch/x86/kernel/time_64.c | 2 ++ arch/x86/kernel/tsc_32.c | 5 +++++ include/linux/delay.h | 1 + init/calibrate.c | 36 +++++++++++++++++++----------------- 4 files changed, 27 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index c737849e2ef7..12b4a71bd074 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -123,6 +123,8 @@ void __init time_init(void) (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)) cpu_khz = calculate_cpu_khz(); + lpj_tsc = ((unsigned long)tsc_khz * 1000)/HZ; + if (unsynchronized_tsc()) mark_tsc_unstable("TSCs unsynchronized"); diff --git a/arch/x86/kernel/tsc_32.c b/arch/x86/kernel/tsc_32.c index 068759db63dd..be729035b30b 100644 --- a/arch/x86/kernel/tsc_32.c +++ b/arch/x86/kernel/tsc_32.c @@ -401,6 +401,7 @@ static inline void check_geode_tsc_reliable(void) { } void __init tsc_init(void) { int cpu; + u64 lpj; if (!cpu_has_tsc || tsc_disabled) { /* Disable the TSC in case of !cpu_has_tsc */ @@ -421,6 +422,10 @@ void __init tsc_init(void) return; } + lpj = ((u64)tsc_khz * 1000); + do_div(lpj, HZ); + lpj_tsc = lpj; + printk("Detected %lu.%03lu MHz processor.\n", (unsigned long)cpu_khz / 1000, (unsigned long)cpu_khz % 1000); diff --git a/include/linux/delay.h b/include/linux/delay.h index 54552d21296e..01aec60590ab 100644 --- a/include/linux/delay.h +++ b/include/linux/delay.h @@ -41,6 +41,7 @@ static inline void ndelay(unsigned long x) #define ndelay(x) ndelay(x) #endif +extern unsigned long lpj_tsc; void calibrate_delay(void); void msleep(unsigned int msecs); unsigned long msleep_interruptible(unsigned int msecs); diff --git a/init/calibrate.c b/init/calibrate.c index ecb3822d4f70..86286974dada 100644 --- a/init/calibrate.c +++ b/init/calibrate.c @@ -8,7 +8,9 @@ #include #include #include +#include +unsigned long lpj_tsc; unsigned long preset_lpj; static int __init lpj_setup(char *str) { @@ -108,6 +110,10 @@ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;} * This is the number of bits of precision for the loops_per_jiffy. Each * bit takes on average 1.5/HZ seconds. This (like the original) is a little * better than 1% + * For the boot cpu we can skip the delay calibration and assign it a value + * calculated based on the tsc frequency. + * For the rest of the CPUs we cannot assume that the tsc frequency is same as + * the cpu frequency, hence do the calibration for those. */ #define LPS_PREC 8 @@ -118,20 +124,20 @@ void __cpuinit calibrate_delay(void) if (preset_lpj) { loops_per_jiffy = preset_lpj; - printk("Calibrating delay loop (skipped)... " - "%lu.%02lu BogoMIPS preset\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100); + printk(KERN_INFO + "Calibrating delay loop (skipped) preset value.. "); + } else if ((smp_processor_id() == 0) && lpj_tsc) { + loops_per_jiffy = lpj_tsc; + printk(KERN_INFO + "Calibrating delay loop (skipped), " + "using tsc calculated value.. "); } else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) { - printk("Calibrating delay using timer specific routine.. "); - printk("%lu.%02lu BogoMIPS (lpj=%lu)\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100, - loops_per_jiffy); + printk(KERN_INFO + "Calibrating delay using timer specific routine.. "); } else { loops_per_jiffy = (1<<12); - printk(KERN_DEBUG "Calibrating delay loop... "); + printk(KERN_INFO "Calibrating delay loop... "); while ((loops_per_jiffy <<= 1) != 0) { /* wait for "start of" clock tick */ ticks = jiffies; @@ -161,12 +167,8 @@ void __cpuinit calibrate_delay(void) if (jiffies != ticks) /* longer than 1 tick */ loops_per_jiffy &= ~loopbit; } - - /* Round the value and print it */ - printk("%lu.%02lu BogoMIPS (lpj=%lu)\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100, - loops_per_jiffy); } - + printk(KERN_INFO "%lu.%02lu BogoMIPS (lpj=%lu)\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100, loops_per_jiffy); } -- cgit v1.2.3 From 961ccddd59d627b89bd3dc284b6517833bbdf25d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 23 Jun 2008 13:55:38 +1000 Subject: sched: add new API sched_setscheduler_nocheck: add a flag to control access checks Hidehiro Kawai noticed that sched_setscheduler() can fail in stop_machine: it calls sched_setscheduler() from insmod, which can have CAP_SYS_MODULE without CAP_SYS_NICE. Two cases could have failed, so are changed to sched_setscheduler_nocheck: kernel/softirq.c:cpu_callback() - CPU hotplug callback kernel/stop_machine.c:__stop_machine_run() - Called from various places, including modprobe() Signed-off-by: Rusty Russell Cc: Jeremy Fitzhardinge Cc: Hidehiro Kawai Cc: Andrew Morton Cc: linux-mm@kvack.org Cc: sugita Cc: Satoshi OSHIMA Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 ++ kernel/sched.c | 48 ++++++++++++++++++++++++++++++++++++------------ kernel/softirq.c | 2 +- kernel/stop_machine.c | 2 +- 4 files changed, 40 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index c5d3f847ca8d..fe3b9b5d7390 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1655,6 +1655,8 @@ extern int can_nice(const struct task_struct *p, const int nice); extern int task_curr(const struct task_struct *p); extern int idle_cpu(int cpu); extern int sched_setscheduler(struct task_struct *, int, struct sched_param *); +extern int sched_setscheduler_nocheck(struct task_struct *, int, + struct sched_param *); extern struct task_struct *idle_task(int cpu); extern struct task_struct *curr_task(int cpu); extern void set_curr_task(int cpu, struct task_struct *p); diff --git a/kernel/sched.c b/kernel/sched.c index b048ad8a11af..8d7c246ab864 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4746,16 +4746,8 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio) set_load_weight(p); } -/** - * sched_setscheduler - change the scheduling policy and/or RT priority of a thread. - * @p: the task in question. - * @policy: new policy. - * @param: structure containing the new RT priority. - * - * NOTE that the task may be already dead. - */ -int sched_setscheduler(struct task_struct *p, int policy, - struct sched_param *param) +static int __sched_setscheduler(struct task_struct *p, int policy, + struct sched_param *param, bool user) { int retval, oldprio, oldpolicy = -1, on_rq, running; unsigned long flags; @@ -4787,7 +4779,7 @@ recheck: /* * Allow unprivileged RT tasks to decrease priority: */ - if (!capable(CAP_SYS_NICE)) { + if (user && !capable(CAP_SYS_NICE)) { if (rt_policy(policy)) { unsigned long rlim_rtprio; @@ -4823,7 +4815,8 @@ recheck: * Do not allow realtime tasks into groups that have no runtime * assigned. */ - if (rt_policy(policy) && task_group(p)->rt_bandwidth.rt_runtime == 0) + if (user + && rt_policy(policy) && task_group(p)->rt_bandwidth.rt_runtime == 0) return -EPERM; #endif @@ -4872,8 +4865,39 @@ recheck: return 0; } + +/** + * sched_setscheduler - change the scheduling policy and/or RT priority of a thread. + * @p: the task in question. + * @policy: new policy. + * @param: structure containing the new RT priority. + * + * NOTE that the task may be already dead. + */ +int sched_setscheduler(struct task_struct *p, int policy, + struct sched_param *param) +{ + return __sched_setscheduler(p, policy, param, true); +} EXPORT_SYMBOL_GPL(sched_setscheduler); +/** + * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace. + * @p: the task in question. + * @policy: new policy. + * @param: structure containing the new RT priority. + * + * Just like sched_setscheduler, only don't bother checking if the + * current context has permission. For example, this is needed in + * stop_machine(): we create temporary high priority worker threads, + * but our caller might not have that capability. + */ +int sched_setscheduler_nocheck(struct task_struct *p, int policy, + struct sched_param *param) +{ + return __sched_setscheduler(p, policy, param, false); +} + static int do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param) { diff --git a/kernel/softirq.c b/kernel/softirq.c index 36e061740047..afd9120c2fc4 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -645,7 +645,7 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb, p = per_cpu(ksoftirqd, hotcpu); per_cpu(ksoftirqd, hotcpu) = NULL; - sched_setscheduler(p, SCHED_FIFO, ¶m); + sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m); kthread_stop(p); takeover_tasklets(hotcpu); break; diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index b7350bbfb076..ba9b2054ecbd 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -187,7 +187,7 @@ struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; /* One high-prio thread per cpu. We'll do this one. */ - sched_setscheduler(p, SCHED_FIFO, ¶m); + sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m); kthread_bind(p, cpu); wake_up_process(p); wait_for_completion(&smdata.done); -- cgit v1.2.3 From 34f80b04f325078ff21123579343d99756ad8d0e Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Mon, 23 Jun 2008 20:33:01 -0700 Subject: bnx2x: Add support for BCM57711 HW Supporting the 57711 and 57711E - refers to in the code as E1H. The 57710 is referred to as E1. To support the new members in the family, the bnx2x structure was divided to 3 parts: common, port and function. These changes caused some rearrangement in the bnx2x.h file. A set of accessories macros were added to make access to the bnx2x structure more readable Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/Kconfig | 1 + drivers/net/bnx2x.h | 739 ++++---- drivers/net/bnx2x_fw_defs.h | 483 ++++-- drivers/net/bnx2x_hsi.h | 708 +++++--- drivers/net/bnx2x_init.h | 20 +- drivers/net/bnx2x_link.c | 11 +- drivers/net/bnx2x_main.c | 3919 +++++++++++++++++++++++++++---------------- drivers/net/bnx2x_reg.h | 66 +- include/linux/pci_ids.h | 2 + 9 files changed, 3774 insertions(+), 2175 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0e0f6696ccac..287d0873c60d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2600,6 +2600,7 @@ config BNX2X tristate "Broadcom NetXtremeII 10Gb support" depends on PCI select ZLIB_INFLATE + select LIBCRC32C help This driver supports Broadcom NetXtremeII 10 gigabit Ethernet cards. To compile this driver as a module, choose M here: the module diff --git a/drivers/net/bnx2x.h b/drivers/net/bnx2x.h index 0979ca0ae408..e08b9439a933 100644 --- a/drivers/net/bnx2x.h +++ b/drivers/net/bnx2x.h @@ -14,39 +14,46 @@ #ifndef BNX2X_H #define BNX2X_H +/* compilation time flags */ + +/* define this to make the driver freeze on error to allow getting debug info + * (you will need to reboot afterwards) */ +/* #define BNX2X_STOP_ON_ERROR */ + /* error/debug prints */ -#define DRV_MODULE_NAME "bnx2x" -#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_NAME "bnx2x" +#define PFX DRV_MODULE_NAME ": " /* for messages that are currently off */ -#define BNX2X_MSG_OFF 0 -#define BNX2X_MSG_MCP 0x10000 /* was: NETIF_MSG_HW */ -#define BNX2X_MSG_STATS 0x20000 /* was: NETIF_MSG_TIMER */ -#define NETIF_MSG_NVM 0x40000 /* was: NETIF_MSG_HW */ -#define NETIF_MSG_DMAE 0x80000 /* was: NETIF_MSG_HW */ +#define BNX2X_MSG_OFF 0 +#define BNX2X_MSG_MCP 0x010000 /* was: NETIF_MSG_HW */ +#define BNX2X_MSG_STATS 0x020000 /* was: NETIF_MSG_TIMER */ +#define BNX2X_MSG_NVM 0x040000 /* was: NETIF_MSG_HW */ +#define BNX2X_MSG_DMAE 0x080000 /* was: NETIF_MSG_HW */ #define BNX2X_MSG_SP 0x100000 /* was: NETIF_MSG_INTR */ #define BNX2X_MSG_FP 0x200000 /* was: NETIF_MSG_INTR */ -#define DP_LEVEL KERN_NOTICE /* was: KERN_DEBUG */ +#define DP_LEVEL KERN_NOTICE /* was: KERN_DEBUG */ /* regular debug print */ #define DP(__mask, __fmt, __args...) do { \ if (bp->msglevel & (__mask)) \ - printk(DP_LEVEL "[%s:%d(%s)]" __fmt, __FUNCTION__, \ - __LINE__, bp->dev?(bp->dev->name):"?", ##__args); \ + printk(DP_LEVEL "[%s:%d(%s)]" __fmt, __func__, __LINE__, \ + bp->dev?(bp->dev->name):"?", ##__args); \ } while (0) -/* for errors (never masked) */ -#define BNX2X_ERR(__fmt, __args...) do { \ - printk(KERN_ERR "[%s:%d(%s)]" __fmt, __FUNCTION__, \ - __LINE__, bp->dev?(bp->dev->name):"?", ##__args); \ +/* errors debug print */ +#define BNX2X_DBG_ERR(__fmt, __args...) do { \ + if (bp->msglevel & NETIF_MSG_PROBE) \ + printk(KERN_ERR "[%s:%d(%s)]" __fmt, __func__, __LINE__, \ + bp->dev?(bp->dev->name):"?", ##__args); \ } while (0) -/* for logging (never masked) */ -#define BNX2X_LOG(__fmt, __args...) do { \ - printk(KERN_NOTICE "[%s:%d(%s)]" __fmt, __FUNCTION__, \ - __LINE__, bp->dev?(bp->dev->name):"?", ##__args); \ +/* for errors (never masked) */ +#define BNX2X_ERR(__fmt, __args...) do { \ + printk(KERN_ERR "[%s:%d(%s)]" __fmt, __func__, __LINE__, \ + bp->dev?(bp->dev->name):"?", ##__args); \ } while (0) /* before we have a dev->name use dev_info() */ @@ -60,7 +67,7 @@ #define bnx2x_panic() do { \ bp->panic = 1; \ BNX2X_ERR("driver assert\n"); \ - bnx2x_disable_int(bp); \ + bnx2x_int_disable(bp); \ bnx2x_panic_dump(bp); \ } while (0) #else @@ -71,24 +78,29 @@ #endif -#define U64_LO(x) (((u64)x) & 0xffffffff) -#define U64_HI(x) (((u64)x) >> 32) -#define HILO_U64(hi, lo) (((u64)hi << 32) + lo) +#ifdef NETIF_F_HW_VLAN_TX +#define BCM_VLAN 1 +#endif + +#define U64_LO(x) (u32)(((u64)(x)) & 0xffffffff) +#define U64_HI(x) (u32)(((u64)(x)) >> 32) +#define HILO_U64(hi, lo) ((((u64)(hi)) << 32) + (lo)) -#define REG_ADDR(bp, offset) (bp->regview + offset) -#define REG_RD(bp, offset) readl(REG_ADDR(bp, offset)) -#define REG_RD8(bp, offset) readb(REG_ADDR(bp, offset)) -#define REG_RD64(bp, offset) readq(REG_ADDR(bp, offset)) +#define REG_ADDR(bp, offset) (bp->regview + offset) -#define REG_WR(bp, offset, val) writel((u32)val, REG_ADDR(bp, offset)) +#define REG_RD(bp, offset) readl(REG_ADDR(bp, offset)) +#define REG_RD8(bp, offset) readb(REG_ADDR(bp, offset)) +#define REG_RD64(bp, offset) readq(REG_ADDR(bp, offset)) + +#define REG_WR(bp, offset, val) writel((u32)val, REG_ADDR(bp, offset)) #define REG_WR8(bp, offset, val) writeb((u8)val, REG_ADDR(bp, offset)) -#define REG_WR16(bp, offset, val) writew((u16)val, REG_ADDR(bp, offset)) -#define REG_WR32(bp, offset, val) REG_WR(bp, offset, val) +#define REG_WR16(bp, offset, val) writew((u16)val, REG_ADDR(bp, offset)) +#define REG_WR32(bp, offset, val) REG_WR(bp, offset, val) -#define REG_RD_IND(bp, offset) bnx2x_reg_rd_ind(bp, offset) -#define REG_WR_IND(bp, offset, val) bnx2x_reg_wr_ind(bp, offset, val) +#define REG_RD_IND(bp, offset) bnx2x_reg_rd_ind(bp, offset) +#define REG_WR_IND(bp, offset, val) bnx2x_reg_wr_ind(bp, offset, val) #define REG_RD_DMAE(bp, offset, valp, len32) \ do { \ @@ -96,28 +108,28 @@ memcpy(valp, bnx2x_sp(bp, wb_data[0]), len32 * 4); \ } while (0) -#define REG_WR_DMAE(bp, offset, val, len32) \ +#define REG_WR_DMAE(bp, offset, valp, len32) \ do { \ - memcpy(bnx2x_sp(bp, wb_data[0]), val, len32 * 4); \ + memcpy(bnx2x_sp(bp, wb_data[0]), valp, len32 * 4); \ bnx2x_write_dmae(bp, bnx2x_sp_mapping(bp, wb_data), \ offset, len32); \ } while (0) -#define SHMEM_RD(bp, type) \ - REG_RD(bp, bp->shmem_base + offsetof(struct shmem_region, type)) -#define SHMEM_WR(bp, type, val) \ - REG_WR(bp, bp->shmem_base + offsetof(struct shmem_region, type), val) +#define SHMEM_ADDR(bp, field) (bp->common.shmem_base + \ + offsetof(struct shmem_region, field)) +#define SHMEM_RD(bp, field) REG_RD(bp, SHMEM_ADDR(bp, field)) +#define SHMEM_WR(bp, field, val) REG_WR(bp, SHMEM_ADDR(bp, field), val) #define NIG_WR(reg, val) REG_WR(bp, reg, val) -#define EMAC_WR(reg, val) REG_WR(bp, emac_base + reg, val) -#define BMAC_WR(reg, val) REG_WR(bp, GRCBASE_NIG + bmac_addr + reg, val) +#define EMAC_WR(reg, val) REG_WR(bp, emac_base + reg, val) +#define BMAC_WR(reg, val) REG_WR(bp, GRCBASE_NIG + bmac_addr + reg, val) -#define for_each_queue(bp, var) for (var = 0; var < bp->num_queues; var++) +#define for_each_queue(bp, var) for (var = 0; var < bp->num_queues; var++) #define for_each_nondefault_queue(bp, var) \ for (var = 1; var < bp->num_queues; var++) -#define is_multi(bp) (bp->num_queues > 1) +#define is_multi(bp) (bp->num_queues > 1) struct regp { @@ -358,210 +370,122 @@ struct bnx2x_eth_stats { u32 number_of_bugs_found_in_stats_spec; /* just kidding */ }; -#define MAC_STX_NA 0xffffffff - -#ifdef BNX2X_MULTI -#define MAX_CONTEXT 16 -#else -#define MAX_CONTEXT 1 -#endif - -union cdu_context { - struct eth_context eth; - char pad[1024]; -}; - -#define MAX_DMAE_C 5 - -/* DMA memory not used in fastpath */ -struct bnx2x_slowpath { - union cdu_context context[MAX_CONTEXT]; - struct eth_stats_query fw_stats; - struct mac_configuration_cmd mac_config; - struct mac_configuration_cmd mcast_config; - - /* used by dmae command executer */ - struct dmae_command dmae[MAX_DMAE_C]; - - union mac_stats mac_stats; - struct nig_stats nig; - struct bnx2x_eth_stats eth_stats; - - u32 wb_comp; -#define BNX2X_WB_COMP_VAL 0xe0d0d0ae - u32 wb_data[4]; -}; - -#define bnx2x_sp(bp, var) (&bp->slowpath->var) #define bnx2x_sp_check(bp, var) ((bp->slowpath) ? (&bp->slowpath->var) : NULL) -#define bnx2x_sp_mapping(bp, var) \ - (bp->slowpath_mapping + offsetof(struct bnx2x_slowpath, var)) - - struct sw_rx_bd { - struct sk_buff *skb; + struct sk_buff *skb; DECLARE_PCI_UNMAP_ADDR(mapping) }; struct sw_tx_bd { - struct sk_buff *skb; - u16 first_bd; + struct sk_buff *skb; + u16 first_bd; }; struct bnx2x_fastpath { - struct napi_struct napi; + struct napi_struct napi; struct host_status_block *status_blk; - dma_addr_t status_blk_mapping; + dma_addr_t status_blk_mapping; - struct eth_tx_db_data *hw_tx_prods; - dma_addr_t tx_prods_mapping; + struct eth_tx_db_data *hw_tx_prods; + dma_addr_t tx_prods_mapping; - struct sw_tx_bd *tx_buf_ring; + struct sw_tx_bd *tx_buf_ring; struct eth_tx_bd *tx_desc_ring; - dma_addr_t tx_desc_mapping; + dma_addr_t tx_desc_mapping; struct sw_rx_bd *rx_buf_ring; struct eth_rx_bd *rx_desc_ring; - dma_addr_t rx_desc_mapping; + dma_addr_t rx_desc_mapping; union eth_rx_cqe *rx_comp_ring; - dma_addr_t rx_comp_mapping; - - int state; -#define BNX2X_FP_STATE_CLOSED 0 -#define BNX2X_FP_STATE_IRQ 0x80000 -#define BNX2X_FP_STATE_OPENING 0x90000 -#define BNX2X_FP_STATE_OPEN 0xa0000 -#define BNX2X_FP_STATE_HALTING 0xb0000 -#define BNX2X_FP_STATE_HALTED 0xc0000 - - int index; - - u16 tx_pkt_prod; - u16 tx_pkt_cons; - u16 tx_bd_prod; - u16 tx_bd_cons; - u16 *tx_cons_sb; - - u16 fp_c_idx; - u16 fp_u_idx; - - u16 rx_bd_prod; - u16 rx_bd_cons; - u16 rx_comp_prod; - u16 rx_comp_cons; - u16 *rx_cons_sb; - - unsigned long tx_pkt, + dma_addr_t rx_comp_mapping; + + int state; +#define BNX2X_FP_STATE_CLOSED 0 +#define BNX2X_FP_STATE_IRQ 0x80000 +#define BNX2X_FP_STATE_OPENING 0x90000 +#define BNX2X_FP_STATE_OPEN 0xa0000 +#define BNX2X_FP_STATE_HALTING 0xb0000 +#define BNX2X_FP_STATE_HALTED 0xc0000 + + u8 index; /* number in fp array */ + u8 cl_id; /* eth client id */ + u8 sb_id; /* status block number in HW */ +#define FP_IDX(fp) (fp->index) +#define FP_CL_ID(fp) (fp->cl_id) +#define BP_CL_ID(bp) (bp->fp[0].cl_id) +#define FP_SB_ID(fp) (fp->sb_id) +#define CNIC_SB_ID 0 + + u16 tx_pkt_prod; + u16 tx_pkt_cons; + u16 tx_bd_prod; + u16 tx_bd_cons; + u16 *tx_cons_sb; + + u16 fp_c_idx; + u16 fp_u_idx; + + u16 rx_bd_prod; + u16 rx_bd_cons; + u16 rx_comp_prod; + u16 rx_comp_cons; + u16 *rx_cons_sb; + + unsigned long tx_pkt, rx_pkt, rx_calls; - struct bnx2x *bp; /* parent */ -}; - -#define bnx2x_fp(bp, nr, var) (bp->fp[nr].var) - - -/* attn group wiring */ -#define MAX_DYNAMIC_ATTN_GRPS 8 - -struct attn_route { - u32 sig[4]; + struct bnx2x *bp; /* parent */ }; -struct bnx2x { - /* Fields used in the tx and intr/napi performance paths - * are grouped together in the beginning of the structure - */ - struct bnx2x_fastpath *fp; - void __iomem *regview; - void __iomem *doorbells; - - struct net_device *dev; - struct pci_dev *pdev; - - atomic_t intr_sem; - struct msix_entry msix_table[MAX_CONTEXT+1]; - - int tx_ring_size; +#define bnx2x_fp(bp, nr, var) (bp->fp[nr].var) +/* This is needed for determening of last_max */ +#define SUB_S16(a, b) (s16)((s16)(a) - (s16)(b)) -#ifdef BCM_VLAN - struct vlan_group *vlgrp; -#endif - - u32 rx_csum; - u32 rx_offset; - u32 rx_buf_use_size; /* useable size */ - u32 rx_buf_size; /* with alignment */ -#define ETH_OVREHEAD (ETH_HLEN + 8) /* 8 for CRC + VLAN */ -#define ETH_MIN_PACKET_SIZE 60 -#define ETH_MAX_PACKET_SIZE 1500 -#define ETH_MAX_JUMBO_PACKET_SIZE 9600 +/* stuff added to make the code fit 80Col */ - struct host_def_status_block *def_status_blk; -#define DEF_SB_ID 16 - u16 def_c_idx; - u16 def_u_idx; - u16 def_t_idx; - u16 def_x_idx; - u16 def_att_idx; - u32 attn_state; - struct attn_route attn_group[MAX_DYNAMIC_ATTN_GRPS]; - u32 aeu_mask; - u32 nig_mask; +#define CQE_TYPE(cqe_fp_flags) ((cqe_fp_flags) & ETH_FAST_PATH_RX_CQE_TYPE) - /* slow path ring */ - struct eth_spe *spq; - dma_addr_t spq_mapping; - u16 spq_prod_idx; - struct eth_spe *spq_prod_bd; - struct eth_spe *spq_last_bd; - u16 *dsb_sp_prod; - u16 spq_left; /* serialize spq */ - spinlock_t spq_lock; - - /* Flag for marking that there is either - * STAT_QUERY or CFC DELETE ramrod pending - */ - u8 stat_pending; +#define ETH_RX_ERROR_FALGS (ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG | \ + ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG | \ + ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG) - /* End of fields used in the performance code paths */ - int panic; - int msglevel; +#define U_SB_ETH_RX_CQ_INDEX HC_INDEX_U_ETH_RX_CQ_CONS +#define U_SB_ETH_RX_BD_INDEX HC_INDEX_U_ETH_RX_BD_CONS +#define C_SB_ETH_TX_CQ_INDEX HC_INDEX_C_ETH_TX_CQ_CONS - u32 flags; -#define PCIX_FLAG 1 -#define PCI_32BIT_FLAG 2 -#define ONE_TDMA_FLAG 4 /* no longer used */ -#define NO_WOL_FLAG 8 -#define USING_DAC_FLAG 0x10 -#define USING_MSIX_FLAG 0x20 -#define ASF_ENABLE_FLAG 0x40 +#define BNX2X_RX_SB_INDEX \ + (&fp->status_blk->u_status_block.index_values[U_SB_ETH_RX_CQ_INDEX]) - int port; +#define BNX2X_RX_SB_BD_INDEX \ + (&fp->status_blk->u_status_block.index_values[U_SB_ETH_RX_BD_INDEX]) - int pm_cap; - int pcie_cap; +#define BNX2X_RX_SB_INDEX_NUM \ + (((U_SB_ETH_RX_CQ_INDEX << \ + USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT) & \ + USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER) | \ + ((U_SB_ETH_RX_BD_INDEX << \ + USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER_SHIFT) & \ + USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER)) - struct work_struct sp_task; - struct work_struct reset_task; +#define BNX2X_TX_SB_INDEX \ + (&fp->status_blk->c_status_block.index_values[C_SB_ETH_TX_CQ_INDEX]) - struct timer_list timer; - int timer_interval; - int current_interval; +/* common */ - u32 shmem_base; +struct bnx2x_common { u32 chip_id; /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ -#define CHIP_ID(bp) (bp->chip_id & 0xfffffff0) +#define CHIP_ID(bp) (bp->common.chip_id & 0xfffffff0) -#define CHIP_NUM(bp) (bp->chip_id >> 16) +#define CHIP_NUM(bp) (bp->common.chip_id >> 16) #define CHIP_NUM_57710 0x164e #define CHIP_NUM_57711 0x164f #define CHIP_NUM_57711E 0x1650 @@ -572,7 +496,7 @@ struct bnx2x { CHIP_IS_57711E(bp)) #define IS_E1H_OFFSET CHIP_IS_E1H(bp) -#define CHIP_REV(bp) (bp->chip_id & 0x0000f000) +#define CHIP_REV(bp) (bp->common.chip_id & 0x0000f000) #define CHIP_REV_Ax 0x00000000 /* assume maximum 5 revisions */ #define CHIP_REV_IS_SLOW(bp) (CHIP_REV(bp) > 0x00005000) @@ -586,86 +510,250 @@ struct bnx2x { #define CHIP_TIME(bp) ((CHIP_REV_IS_EMUL(bp)) ? 2000 : \ ((CHIP_REV_IS_FPGA(bp)) ? 200 : 1)) -#define CHIP_METAL(bp) (bp->chip_id & 0x00000ff0) -#define CHIP_BOND_ID(bp) (bp->chip_id & 0x0000000f) +#define CHIP_METAL(bp) (bp->common.chip_id & 0x00000ff0) +#define CHIP_BOND_ID(bp) (bp->common.chip_id & 0x0000000f) - u16 fw_seq; - u16 fw_drv_pulse_wr_seq; - u32 fw_mb; + int flash_size; +#define NVRAM_1MB_SIZE 0x20000 /* 1M bit in bytes */ +#define NVRAM_TIMEOUT_COUNT 30000 +#define NVRAM_PAGE_SIZE 256 - u32 hw_config; + u32 shmem_base; + + u32 hw_config; u32 board; - struct link_params link_params; + u32 bc_ver; + + char *name; +}; - struct link_vars link_vars; + +/* end of common */ + +/* port */ + +struct bnx2x_port { + u32 pmf; u32 link_config; - u32 supported; + u32 supported; +/* link settings - missing defines */ +#define SUPPORTED_2500baseX_Full (1 << 15) + + u32 advertising; /* link settings - missing defines */ -#define SUPPORTED_2500baseT_Full (1 << 15) +#define ADVERTISED_2500baseX_Full (1 << 15) - u32 phy_addr; + u32 phy_addr; /* used to synchronize phy accesses */ struct mutex phy_mutex; - u32 phy_id; + u32 port_stx; + struct nig_stats old_nig_stats; +}; - u32 advertising; -/* link settings - missing defines */ -#define ADVERTISED_2500baseT_Full (1 << 15) +/* end of port */ + +#define MAC_STX_NA 0xffffffff + +#ifdef BNX2X_MULTI +#define MAX_CONTEXT 16 +#else +#define MAX_CONTEXT 1 +#endif + +union cdu_context { + struct eth_context eth; + char pad[1024]; +}; + +#define MAX_DMAE_C 6 + +/* DMA memory not used in fastpath */ +struct bnx2x_slowpath { + union cdu_context context[MAX_CONTEXT]; + struct eth_stats_query fw_stats; + struct mac_configuration_cmd mac_config; + struct mac_configuration_cmd mcast_config; + + /* used by dmae command executer */ + struct dmae_command dmae[MAX_DMAE_C]; + + union mac_stats mac_stats; + struct nig_stats nig; + struct bnx2x_eth_stats eth_stats; + + u32 wb_comp; +#define BNX2X_WB_COMP_VAL 0xe0d0d0ae + u32 wb_data[4]; +}; + +#define bnx2x_sp(bp, var) (&bp->slowpath->var) +#define bnx2x_sp_mapping(bp, var) \ + (bp->slowpath_mapping + offsetof(struct bnx2x_slowpath, var)) + + +/* attn group wiring */ +#define MAX_DYNAMIC_ATTN_GRPS 8 + +struct attn_route { + u32 sig[4]; +}; + +struct bnx2x { + /* Fields used in the tx and intr/napi performance paths + * are grouped together in the beginning of the structure + */ + struct bnx2x_fastpath fp[MAX_CONTEXT]; + void __iomem *regview; + void __iomem *doorbells; +#define BNX2X_DB_SIZE (16*2048) + + struct net_device *dev; + struct pci_dev *pdev; + + atomic_t intr_sem; + struct msix_entry msix_table[MAX_CONTEXT+1]; + + int tx_ring_size; + +#ifdef BCM_VLAN + struct vlan_group *vlgrp; +#endif + u32 rx_csum; + u32 rx_offset; + u32 rx_buf_use_size; /* useable size */ + u32 rx_buf_size; /* with alignment */ +#define ETH_OVREHEAD (ETH_HLEN + 8) /* 8 for CRC + VLAN */ +#define ETH_MIN_PACKET_SIZE 60 +#define ETH_MAX_PACKET_SIZE 1500 +#define ETH_MAX_JUMBO_PACKET_SIZE 9600 - u32 bc_ver; + struct host_def_status_block *def_status_blk; +#define DEF_SB_ID 16 + u16 def_c_idx; + u16 def_u_idx; + u16 def_x_idx; + u16 def_t_idx; + u16 def_att_idx; + u32 attn_state; + struct attn_route attn_group[MAX_DYNAMIC_ATTN_GRPS]; + u32 aeu_mask; + u32 nig_mask; + + /* slow path ring */ + struct eth_spe *spq; + dma_addr_t spq_mapping; + u16 spq_prod_idx; + struct eth_spe *spq_prod_bd; + struct eth_spe *spq_last_bd; + u16 *dsb_sp_prod; + u16 spq_left; /* serialize spq */ + /* used to synchronize spq accesses */ + spinlock_t spq_lock; + + /* Flag for marking that there is either + * STAT_QUERY or CFC DELETE ramrod pending + */ + u8 stat_pending; + + /* End of fileds used in the performance code paths */ + + int panic; + int msglevel; + + u32 flags; +#define PCIX_FLAG 1 +#define PCI_32BIT_FLAG 2 +#define ONE_TDMA_FLAG 4 /* no longer used */ +#define NO_WOL_FLAG 8 +#define USING_DAC_FLAG 0x10 +#define USING_MSIX_FLAG 0x20 +#define ASF_ENABLE_FLAG 0x40 +#define NO_MCP_FLAG 0x100 +#define BP_NOMCP(bp) (bp->flags & NO_MCP_FLAG) + + int func; +#define BP_PORT(bp) (bp->func % PORT_MAX) +#define BP_FUNC(bp) (bp->func) +#define BP_E1HVN(bp) (bp->func >> 1) +#define BP_L_ID(bp) (BP_E1HVN(bp) << 2) +/* assorted E1HVN */ +#define IS_E1HMF(bp) (bp->e1hmf != 0) +#define BP_MAX_QUEUES(bp) (IS_E1HMF(bp) ? 4 : 16) + + int pm_cap; + int pcie_cap; + + struct work_struct sp_task; + struct work_struct reset_task; + + struct timer_list timer; + int timer_interval; + int current_interval; + + u16 fw_seq; + u16 fw_drv_pulse_wr_seq; + u32 func_stx; + + struct link_params link_params; + struct link_vars link_vars; - int flash_size; -#define NVRAM_1MB_SIZE 0x20000 /* 1M bit in bytes */ -#define NVRAM_TIMEOUT_COUNT 30000 -#define NVRAM_PAGE_SIZE 256 + struct bnx2x_common common; + struct bnx2x_port port; + + u32 mf_config; + u16 e1hov; + u8 e1hmf; u8 wol; - int rx_ring_size; + int rx_ring_size; - u16 tx_quick_cons_trip_int; - u16 tx_quick_cons_trip; - u16 tx_ticks_int; - u16 tx_ticks; + u16 tx_quick_cons_trip_int; + u16 tx_quick_cons_trip; + u16 tx_ticks_int; + u16 tx_ticks; - u16 rx_quick_cons_trip_int; - u16 rx_quick_cons_trip; - u16 rx_ticks_int; - u16 rx_ticks; + u16 rx_quick_cons_trip_int; + u16 rx_quick_cons_trip; + u16 rx_ticks_int; + u16 rx_ticks; - u32 stats_ticks; + u32 stats_ticks; + u32 lin_cnt; - int state; -#define BNX2X_STATE_CLOSED 0x0 -#define BNX2X_STATE_OPENING_WAIT4_LOAD 0x1000 -#define BNX2X_STATE_OPENING_WAIT4_PORT 0x2000 + int state; +#define BNX2X_STATE_CLOSED 0x0 +#define BNX2X_STATE_OPENING_WAIT4_LOAD 0x1000 +#define BNX2X_STATE_OPENING_WAIT4_PORT 0x2000 #define BNX2X_STATE_OPEN 0x3000 -#define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000 +#define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000 #define BNX2X_STATE_CLOSING_WAIT4_DELETE 0x5000 #define BNX2X_STATE_CLOSING_WAIT4_UNLOAD 0x6000 -#define BNX2X_STATE_ERROR 0xF000 +#define BNX2X_STATE_DISABLED 0xd000 +#define BNX2X_STATE_DIAG 0xe000 +#define BNX2X_STATE_ERROR 0xf000 - int num_queues; + int num_queues; - u32 rx_mode; -#define BNX2X_RX_MODE_NONE 0 -#define BNX2X_RX_MODE_NORMAL 1 -#define BNX2X_RX_MODE_ALLMULTI 2 -#define BNX2X_RX_MODE_PROMISC 3 -#define BNX2X_MAX_MULTICAST 64 -#define BNX2X_MAX_EMUL_MULTI 16 + u32 rx_mode; +#define BNX2X_RX_MODE_NONE 0 +#define BNX2X_RX_MODE_NORMAL 1 +#define BNX2X_RX_MODE_ALLMULTI 2 +#define BNX2X_RX_MODE_PROMISC 3 +#define BNX2X_MAX_MULTICAST 64 +#define BNX2X_MAX_EMUL_MULTI 16 - dma_addr_t def_status_blk_mapping; + dma_addr_t def_status_blk_mapping; - struct bnx2x_slowpath *slowpath; - dma_addr_t slowpath_mapping; + struct bnx2x_slowpath *slowpath; + dma_addr_t slowpath_mapping; #ifdef BCM_ISCSI void *t1; @@ -742,8 +830,10 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); /* MC hsi */ #define RX_COPY_THRESH 92 -#define BCM_PAGE_BITS 12 -#define BCM_PAGE_SIZE (1 << BCM_PAGE_BITS) +#define BCM_PAGE_SHIFT 12 +#define BCM_PAGE_SIZE (1 << BCM_PAGE_SHIFT) +#define BCM_PAGE_MASK (~(BCM_PAGE_SIZE - 1)) +#define BCM_PAGE_ALIGN(addr) (((addr) + BCM_PAGE_SIZE - 1) & BCM_PAGE_MASK) #define NUM_TX_RINGS 16 #define TX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_tx_bd)) @@ -795,26 +885,11 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); /* must be used on a CID before placing it on a HW ring */ -#define HW_CID(bp, x) (x | (bp->port << 23)) +#define HW_CID(bp, x) ((BP_PORT(bp) << 23) | (BP_E1HVN(bp) << 17) | x) #define SP_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_spe)) #define MAX_SP_DESC_CNT (SP_DESC_CNT - 1) -#define ATTN_NIG_FOR_FUNC (1L << 8) -#define ATTN_SW_TIMER_4_FUNC (1L << 9) -#define GPIO_2_FUNC (1L << 10) -#define GPIO_3_FUNC (1L << 11) -#define GPIO_4_FUNC (1L << 12) -#define ATTN_GENERAL_ATTN_1 (1L << 13) -#define ATTN_GENERAL_ATTN_2 (1L << 14) -#define ATTN_GENERAL_ATTN_3 (1L << 15) -#define ATTN_GENERAL_ATTN_4 (1L << 13) -#define ATTN_GENERAL_ATTN_5 (1L << 14) -#define ATTN_GENERAL_ATTN_6 (1L << 15) - -#define ATTN_HARD_WIRED_MASK 0xff00 -#define ATTENTION_ID 4 - #define BNX2X_BTR 3 #define MAX_SPQ_PENDING 8 @@ -831,6 +906,31 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); DPM_TRIGER_TYPE); \ } while (0) +static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, + int wait) +{ + u32 val; + + do { + val = REG_RD(bp, reg); + if (val == expected) + break; + ms -= wait; + msleep(wait); + + } while (ms > 0); + + return val; +} + + +/* load/unload mode */ +#define LOAD_NORMAL 0 +#define LOAD_OPEN 1 +#define LOAD_DIAG 2 +#define UNLOAD_NORMAL 0 +#define UNLOAD_CLOSE 1 + /* DMAE command defines */ #define DMAE_CMD_SRC_PCI 0 #define DMAE_CMD_SRC_GRC DMAE_COMMAND_SRC @@ -877,23 +977,48 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); #define pbd_tcp_flags(skb) (ntohl(tcp_flag_word(tcp_hdr(skb)))>>16 & 0xff) -/* stuff added to make the code fit 80Col */ +/* must be used on a CID before placing it on a HW ring */ -#define TPA_TYPE_START ETH_FAST_PATH_RX_CQE_START_FLG -#define TPA_TYPE_END ETH_FAST_PATH_RX_CQE_END_FLG -#define TPA_TYPE(cqe) (cqe->fast_path_cqe.error_type_flags & \ - (TPA_TYPE_START | TPA_TYPE_END)) #define BNX2X_RX_SUM_OK(cqe) \ (!(cqe->fast_path_cqe.status_flags & \ (ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG | \ ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG))) -#define BNX2X_RX_SUM_FIX(cqe) \ - ((le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) & \ - PARSING_FLAGS_OVER_ETHERNET_PROTOCOL) == \ - (1 << PARSING_FLAGS_OVER_ETHERNET_PROTOCOL_SHIFT)) +/* CMNG constants + derived from lab experiments, and not from system spec calculations !!! */ +#define DEF_MIN_RATE 100 +/* resolution of the rate shaping timer - 100 usec */ +#define RS_PERIODIC_TIMEOUT_USEC 100 +/* resolution of fairness algorithm in usecs - + coefficient for clauclating the actuall t fair */ +#define T_FAIR_COEF 10000000 +/* number of bytes in single QM arbitration cycle - + coeffiecnt for calculating the fairness timer */ +#define QM_ARB_BYTES 40000 +#define FAIR_MEM 2 + + +#define ATTN_NIG_FOR_FUNC (1L << 8) +#define ATTN_SW_TIMER_4_FUNC (1L << 9) +#define GPIO_2_FUNC (1L << 10) +#define GPIO_3_FUNC (1L << 11) +#define GPIO_4_FUNC (1L << 12) +#define ATTN_GENERAL_ATTN_1 (1L << 13) +#define ATTN_GENERAL_ATTN_2 (1L << 14) +#define ATTN_GENERAL_ATTN_3 (1L << 15) +#define ATTN_GENERAL_ATTN_4 (1L << 13) +#define ATTN_GENERAL_ATTN_5 (1L << 14) +#define ATTN_GENERAL_ATTN_6 (1L << 15) + +#define ATTN_HARD_WIRED_MASK 0xff00 +#define ATTENTION_ID 4 +/* stuff added to make the code fit 80Col */ + +#define BNX2X_PMF_LINK_ASSERT \ + GENERAL_ATTEN_OFFSET(LINK_SYNC_ATTENTION_BIT_FUNC_0 + BP_FUNC(bp)) + #define BNX2X_MC_ASSERT_BITS \ (GENERAL_ATTEN_OFFSET(TSTORM_FATAL_ASSERT_ATTENTION_BIT) | \ GENERAL_ATTEN_OFFSET(USTORM_FATAL_ASSERT_ATTENTION_BIT) | \ @@ -906,12 +1031,20 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); #define BNX2X_DOORQ_ASSERT \ AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT +#define BNX2X_GRC_TIMEOUT GENERAL_ATTEN_OFFSET(LATCHED_ATTN_TIMEOUT_GRC) +#define BNX2X_GRC_RSV (GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCR) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCT) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCN) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCU) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCP) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RSVD_GRC)) + #define HW_INTERRUT_ASSERT_SET_0 \ (AEU_INPUTS_ATTN_BITS_TSDM_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_TCM_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_TSEMI_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_PBF_HW_INTERRUPT) -#define HW_PRTY_ASSERT_SET_0 (AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR | \ +#define HW_PRTY_ASSERT_SET_0 (AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR |\ @@ -928,7 +1061,7 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); AEU_INPUTS_ATTN_BITS_UPB_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_CSDM_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_CCM_HW_INTERRUPT) -#define HW_PRTY_ASSERT_SET_1 (AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR |\ +#define HW_PRTY_ASSERT_SET_1 (AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR |\ AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR | \ @@ -945,7 +1078,7 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); AEU_INPUTS_ATTN_BITS_DMAE_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_HW_INTERRUPT |\ AEU_INPUTS_ATTN_BITS_MISC_HW_INTERRUPT) -#define HW_PRTY_ASSERT_SET_2 (AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR | \ +#define HW_PRTY_ASSERT_SET_2 (AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR |\ AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR | \ @@ -954,42 +1087,44 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR) -#define ETH_RX_ERROR_FALGS (ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG | \ - ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG | \ - ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG) - - #define MULTI_FLAGS \ - (TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE) + (TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE) -#define MULTI_MASK 0x7f +#define MULTI_MASK 0x7f -#define U_SB_ETH_RX_CQ_INDEX HC_INDEX_U_ETH_RX_CQ_CONS -#define C_SB_ETH_TX_CQ_INDEX HC_INDEX_C_ETH_TX_CQ_CONS -#define C_DEF_SB_SP_INDEX HC_INDEX_DEF_C_ETH_SLOW_PATH - -#define BNX2X_RX_SB_INDEX \ - &fp->status_blk->u_status_block.index_values[U_SB_ETH_RX_CQ_INDEX] +#define DEF_USB_FUNC_OFF (2 + 2*HC_USTORM_DEF_SB_NUM_INDICES) +#define DEF_CSB_FUNC_OFF (2 + 2*HC_CSTORM_DEF_SB_NUM_INDICES) +#define DEF_XSB_FUNC_OFF (2 + 2*HC_XSTORM_DEF_SB_NUM_INDICES) +#define DEF_TSB_FUNC_OFF (2 + 2*HC_TSTORM_DEF_SB_NUM_INDICES) -#define BNX2X_TX_SB_INDEX \ - &fp->status_blk->c_status_block.index_values[C_SB_ETH_TX_CQ_INDEX] +#define C_DEF_SB_SP_INDEX HC_INDEX_DEF_C_ETH_SLOW_PATH #define BNX2X_SP_DSB_INDEX \ -&bp->def_status_blk->c_def_status_block.index_values[C_DEF_SB_SP_INDEX] +(&bp->def_status_blk->c_def_status_block.index_values[C_DEF_SB_SP_INDEX]) #define CAM_IS_INVALID(x) \ (x.target_table_entry.flags == TSTORM_CAM_TARGET_TABLE_ENTRY_ACTION_TYPE) #define CAM_INVALIDATE(x) \ -x.target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_ACTION_TYPE + (x.target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_ACTION_TYPE) + + +/* Number of u32 elements in MC hash array */ +#define MC_HASH_SIZE 8 +#define MC_HASH_OFFSET(bp, i) (BAR_TSTRORM_INTMEM + \ + TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(BP_FUNC(bp)) + i*4) +#ifndef PXP2_REG_PXP2_INT_STS +#define PXP2_REG_PXP2_INT_STS PXP2_REG_PXP2_INT_STS_0 +#endif + /* MISC_REG_RESET_REG - this is here for the hsi to work don't touch */ #endif /* bnx2x.h */ diff --git a/drivers/net/bnx2x_fw_defs.h b/drivers/net/bnx2x_fw_defs.h index 3b968904ca65..e3da7f69d27b 100644 --- a/drivers/net/bnx2x_fw_defs.h +++ b/drivers/net/bnx2x_fw_defs.h @@ -8,191 +8,390 @@ */ -#define CSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x1922 + (port * 0x40) + (index * 0x4)) -#define CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1900 + (port * 0x40)) -#define CSTORM_HC_BTR_OFFSET(port)\ - (0x1984 + (port * 0xc0)) -#define CSTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index)\ - (0x141a + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define CSTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index)\ - (0x1418 + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define CSTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id)\ - (0x1400 + (port * 0x280) + (cpu_id * 0x28)) -#define CSTORM_STATS_FLAGS_OFFSET(port) (0x5108 + (port * 0x8)) -#define TSTORM_CLIENT_CONFIG_OFFSET(port, client_id)\ - (0x1510 + (port * 0x240) + (client_id * 0x20)) -#define TSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x138a + (port * 0x28) + (index * 0x4)) -#define TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1370 + (port * 0x28)) -#define TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(port)\ - (0x4b70 + (port * 0x8)) -#define TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(function)\ - (0x1418 + (function * 0x30)) -#define TSTORM_HC_BTR_OFFSET(port)\ - (0x13c4 + (port * 0x18)) -#define TSTORM_INDIRECTION_TABLE_OFFSET(port)\ - (0x22c8 + (port * 0x80)) -#define TSTORM_INDIRECTION_TABLE_SIZE 0x80 -#define TSTORM_MAC_FILTER_CONFIG_OFFSET(port)\ - (0x1420 + (port * 0x30)) -#define TSTORM_RCQ_PROD_OFFSET(port, client_id)\ - (0x1508 + (port * 0x240) + (client_id * 0x20)) -#define TSTORM_STATS_FLAGS_OFFSET(port) (0x4b90 + (port * 0x8)) -#define USTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x191a + (port * 0x28) + (index * 0x4)) -#define USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1900 + (port * 0x28)) -#define USTORM_HC_BTR_OFFSET(port)\ - (0x1954 + (port * 0xb8)) -#define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(port)\ - (0x5408 + (port * 0x8)) -#define USTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index)\ - (0x141a + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define USTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index)\ - (0x1418 + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define USTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id)\ - (0x1400 + (port * 0x280) + (cpu_id * 0x28)) -#define XSTORM_ASSERT_LIST_INDEX_OFFSET 0x1000 -#define XSTORM_ASSERT_LIST_OFFSET(idx) (0x1020 + (idx * 0x10)) -#define XSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x141a + (port * 0x28) + (index * 0x4)) -#define XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1400 + (port * 0x28)) -#define XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(port)\ - (0x5408 + (port * 0x8)) -#define XSTORM_HC_BTR_OFFSET(port)\ - (0x1454 + (port * 0x18)) -#define XSTORM_SPQ_PAGE_BASE_OFFSET(port)\ - (0x5328 + (port * 0x18)) -#define XSTORM_SPQ_PROD_OFFSET(port)\ - (0x5330 + (port * 0x18)) -#define XSTORM_STATS_FLAGS_OFFSET(port) (0x53f8 + (port * 0x8)) +#define CSTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0x7000 : 0x1000) +#define CSTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0x7020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define CSTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0x8522 + ((function>>1) * 0x40) + ((function&1) \ + * 0x100) + (index * 0x4)) : (0x1922 + (function * 0x40) + (index \ + * 0x4))) +#define CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x8500 + ((function>>1) * 0x40) + ((function&1) \ + * 0x100)) : (0x1900 + (function * 0x40))) +#define CSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0x8508 + ((function>>1) * 0x40) + ((function&1) \ + * 0x100)) : (0x1908 + (function * 0x40))) +#define CSTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x11e8 : 0xffffffff) +#define CSTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0x8704 + (port * 0xf0)) : (0x1984 + (port * 0xc0))) +#define CSTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x801a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x141a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define CSTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x8018 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x1418 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define CSTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x8000 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1400 + (port * 0x280) + (cpu_id * 0x28))) +#define CSTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x8008 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1408 + (port * 0x280) + (cpu_id * 0x28))) +#define CSTORM_STATS_FLAGS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x1108 + (function * 0x8)) : (0x5108 + \ + (function * 0x8))) +#define TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(function) \ + (IS_E1H_OFFSET? (0x31c0 + (function * 0x20)) : 0xffffffff) +#define TSTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0xa000 : 0x1000) +#define TSTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0xa020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define TSTORM_CLIENT_CONFIG_OFFSET(port, client_id) \ + (IS_E1H_OFFSET? (0x3358 + (port * 0x3e8) + (client_id * 0x28)) : \ + (0x9c8 + (port * 0x2f8) + (client_id * 0x28))) +#define TSTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0xb01a + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0) + (index * 0x4)) : (0x141a + (function * 0x28) + (index * \ + 0x4))) +#define TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0xb000 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1400 + (function * 0x28))) +#define TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0xb008 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1408 + (function * 0x28))) +#define TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2b80 + (function * 0x8)) : (0x4b68 + \ + (function * 0x8))) +#define TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(function) \ + (IS_E1H_OFFSET? (0x3000 + (function * 0x38)) : (0x1500 + \ + (function * 0x38))) +#define TSTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x1ad0 : 0xffffffff) +#define TSTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0xb144 + (port * 0x30)) : (0x1454 + (port * 0x18))) +#define TSTORM_INDIRECTION_TABLE_OFFSET(function) \ + (IS_E1H_OFFSET? (0x12c8 + (function * 0x80)) : (0x22c8 + \ + (function * 0x80))) +#define TSTORM_INDIRECTION_TABLE_SIZE 0x80 +#define TSTORM_MAC_FILTER_CONFIG_OFFSET(function) \ + (IS_E1H_OFFSET? (0x3008 + (function * 0x38)) : (0x1508 + \ + (function * 0x38))) +#define TSTORM_RX_PRODS_OFFSET(port, client_id) \ + (IS_E1H_OFFSET? (0x3350 + (port * 0x3e8) + (client_id * 0x28)) : \ + (0x9c0 + (port * 0x2f8) + (client_id * 0x28))) +#define TSTORM_STATS_FLAGS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2c00 + (function * 0x8)) : (0x4b88 + \ + (function * 0x8))) +#define TSTORM_TPA_EXIST_OFFSET (IS_E1H_OFFSET? 0x3b30 : 0x1c20) +#define USTORM_AGG_DATA_OFFSET (IS_E1H_OFFSET? 0xa040 : 0x2c10) +#define USTORM_AGG_DATA_SIZE (IS_E1H_OFFSET? 0x2440 : 0x1200) +#define USTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0x8000 : 0x1000) +#define USTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0x8020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define USTORM_CQE_PAGE_BASE_OFFSET(port, clientId) \ + (IS_E1H_OFFSET? (0x3298 + (port * 0x258) + (clientId * 0x18)) : \ + (0x5450 + (port * 0x1c8) + (clientId * 0x18))) +#define USTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0x951a + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0) + (index * 0x4)) : (0x191a + (function * 0x28) + (index * \ + 0x4))) +#define USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x9500 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1900 + (function * 0x28))) +#define USTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0x9508 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1908 + (function * 0x28))) +#define USTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x2448 : 0xffffffff) +#define USTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0x9644 + (port * 0xd0)) : (0x1954 + (port * 0xb8))) +#define USTORM_MAX_AGG_SIZE_OFFSET(port, clientId) \ + (IS_E1H_OFFSET? (0x3290 + (port * 0x258) + (clientId * 0x18)) : \ + (0x5448 + (port * 0x1c8) + (clientId * 0x18))) +#define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2408 + (function * 0x8)) : (0x5408 + \ + (function * 0x8))) +#define USTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x901a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x141a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define USTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x9018 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x1418 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define USTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x9000 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1400 + (port * 0x280) + (cpu_id * 0x28))) +#define USTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x9008 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1408 + (port * 0x280) + (cpu_id * 0x28))) +#define XSTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0x9000 : 0x1000) +#define XSTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0x9020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) \ + (IS_E1H_OFFSET? (0x24a8 + (port * 0x40)) : (0x3ba0 + (port * 0x40))) +#define XSTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0xa01a + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0) + (index * 0x4)) : (0x141a + (function * 0x28) + (index * \ + 0x4))) +#define XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0xa000 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1400 + (function * 0x28))) +#define XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0xa008 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1408 + (function * 0x28))) +#define XSTORM_E1HOV_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2ab8 + (function * 0x2)) : 0xffffffff) +#define XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2418 + (function * 0x8)) : (0x3b70 + \ + (function * 0x8))) +#define XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2568 + (function * 0x70)) : (0x3c60 + \ + (function * 0x70))) +#define XSTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x2ac8 : 0xffffffff) +#define XSTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0xa144 + (port * 0x30)) : (0x1454 + (port * 0x18))) +#define XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2528 + (function * 0x70)) : (0x3c20 + \ + (function * 0x70))) +#define XSTORM_SPQ_PAGE_BASE_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2000 + (function * 0x10)) : (0x3328 + \ + (function * 0x10))) +#define XSTORM_SPQ_PROD_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2008 + (function * 0x10)) : (0x3330 + \ + (function * 0x10))) +#define XSTORM_STATS_FLAGS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x23d8 + (function * 0x8)) : (0x3b60 + \ + (function * 0x8))) #define COMMON_ASM_INVALID_ASSERT_OPCODE 0x0 /** * This file defines HSI constatnts for the ETH flow */ - -/* hash types */ -#define DEFAULT_HASH_TYPE 0 -#define IPV4_HASH_TYPE 1 -#define TCP_IPV4_HASH_TYPE 2 -#define IPV6_HASH_TYPE 3 -#define TCP_IPV6_HASH_TYPE 4 +#ifdef _EVEREST_MICROCODE +#include "microcode_constants.h" +#include "eth_rx_bd.h" +#include "eth_tx_bd.h" +#include "eth_rx_cqe.h" +#include "eth_rx_sge.h" +#include "eth_rx_cqe_next_page.h" +#endif + +/* RSS hash types */ +#define DEFAULT_HASH_TYPE 0 +#define IPV4_HASH_TYPE 1 +#define TCP_IPV4_HASH_TYPE 2 +#define IPV6_HASH_TYPE 3 +#define TCP_IPV6_HASH_TYPE 4 + +/* Ethernet Ring parmaters */ +#define X_ETH_LOCAL_RING_SIZE 13 +#define FIRST_BD_IN_PKT 0 +#define PARSE_BD_INDEX 1 +#define NUM_OF_ETH_BDS_IN_PAGE \ + ((PAGE_SIZE) / (STRUCT_SIZE(eth_tx_bd)/8)) + + +/* Rx ring params */ +#define U_ETH_LOCAL_BD_RING_SIZE (16) +#define U_ETH_LOCAL_SGE_RING_SIZE (12) +#define U_ETH_SGL_SIZE (8) + + +#define U_ETH_BDS_PER_PAGE_MASK \ + ((PAGE_SIZE/(STRUCT_SIZE(eth_rx_bd)/8))-1) +#define U_ETH_CQE_PER_PAGE_MASK \ + ((PAGE_SIZE/(STRUCT_SIZE(eth_rx_cqe)/8))-1) +#define U_ETH_SGES_PER_PAGE_MASK \ + ((PAGE_SIZE/(STRUCT_SIZE(eth_rx_sge)/8))-1) + +#define U_ETH_SGES_PER_PAGE_INVERSE_MASK \ + (0xFFFF - ((PAGE_SIZE/((STRUCT_SIZE(eth_rx_sge))/8))-1)) + + +#define TU_ETH_CQES_PER_PAGE \ + (PAGE_SIZE/(STRUCT_SIZE(eth_rx_cqe_next_page)/8)) +#define U_ETH_BDS_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_bd)/8)) +#define U_ETH_SGES_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_sge)/8)) + +#define U_ETH_UNDEFINED_Q 0xFF /* values of command IDs in the ramrod message */ -#define RAMROD_CMD_ID_ETH_PORT_SETUP (80) -#define RAMROD_CMD_ID_ETH_CLIENT_SETUP (85) -#define RAMROD_CMD_ID_ETH_STAT_QUERY (90) -#define RAMROD_CMD_ID_ETH_UPDATE (100) -#define RAMROD_CMD_ID_ETH_HALT (105) -#define RAMROD_CMD_ID_ETH_SET_MAC (110) -#define RAMROD_CMD_ID_ETH_CFC_DEL (115) -#define RAMROD_CMD_ID_ETH_PORT_DEL (120) -#define RAMROD_CMD_ID_ETH_FORWARD_SETUP (125) +#define RAMROD_CMD_ID_ETH_PORT_SETUP (80) +#define RAMROD_CMD_ID_ETH_CLIENT_SETUP (85) +#define RAMROD_CMD_ID_ETH_STAT_QUERY (90) +#define RAMROD_CMD_ID_ETH_UPDATE (100) +#define RAMROD_CMD_ID_ETH_HALT (105) +#define RAMROD_CMD_ID_ETH_SET_MAC (110) +#define RAMROD_CMD_ID_ETH_CFC_DEL (115) +#define RAMROD_CMD_ID_ETH_PORT_DEL (120) +#define RAMROD_CMD_ID_ETH_FORWARD_SETUP (125) /* command values for set mac command */ -#define T_ETH_MAC_COMMAND_SET 0 -#define T_ETH_MAC_COMMAND_INVALIDATE 1 +#define T_ETH_MAC_COMMAND_SET 0 +#define T_ETH_MAC_COMMAND_INVALIDATE 1 + +#define T_ETH_INDIRECTION_TABLE_SIZE 128 -#define T_ETH_INDIRECTION_TABLE_SIZE 128 +/*The CRC32 seed, that is used for the hash(reduction) multicast address */ +#define T_ETH_CRC32_HASH_SEED 0x00000000 /* Maximal L2 clients supported */ -#define ETH_MAX_RX_CLIENTS (18) +#define ETH_MAX_RX_CLIENTS_E1 19 +#define ETH_MAX_RX_CLIENTS_E1H 25 + +/* Maximal aggregation queues supported */ +#define ETH_MAX_AGGREGATION_QUEUES_E1 (32) +#define ETH_MAX_AGGREGATION_QUEUES_E1H (64) + /** * This file defines HSI constatnts common to all microcode flows */ /* Connection types */ -#define ETH_CONNECTION_TYPE 0 +#define ETH_CONNECTION_TYPE 0 +#define TOE_CONNECTION_TYPE 1 +#define RDMA_CONNECTION_TYPE 2 +#define ISCSI_CONNECTION_TYPE 3 +#define FCOE_CONNECTION_TYPE 4 +#define RESERVED_CONNECTION_TYPE_0 5 +#define RESERVED_CONNECTION_TYPE_1 6 +#define RESERVED_CONNECTION_TYPE_2 7 + -#define PROTOCOL_STATE_BIT_OFFSET 6 +#define PROTOCOL_STATE_BIT_OFFSET 6 -#define ETH_STATE (ETH_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define ETH_STATE (ETH_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define TOE_STATE (TOE_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define RDMA_STATE (RDMA_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define ISCSI_STATE \ + (ISCSI_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define FCOE_STATE (FCOE_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) /* microcode fixed page page size 4K (chains and ring segments) */ -#define MC_PAGE_SIZE (4096) +#define MC_PAGE_SIZE (4096) -/* Host coalescing constants */ -/* IGU constants */ -#define IGU_PORT_BASE 0x0400 - -#define IGU_ADDR_MSIX 0x0000 -#define IGU_ADDR_INT_ACK 0x0200 -#define IGU_ADDR_PROD_UPD 0x0201 -#define IGU_ADDR_ATTN_BITS_UPD 0x0202 -#define IGU_ADDR_ATTN_BITS_SET 0x0203 -#define IGU_ADDR_ATTN_BITS_CLR 0x0204 -#define IGU_ADDR_COALESCE_NOW 0x0205 -#define IGU_ADDR_SIMD_MASK 0x0206 -#define IGU_ADDR_SIMD_NOMASK 0x0207 -#define IGU_ADDR_MSI_CTL 0x0210 -#define IGU_ADDR_MSI_ADDR_LO 0x0211 -#define IGU_ADDR_MSI_ADDR_HI 0x0212 -#define IGU_ADDR_MSI_DATA 0x0213 - -#define IGU_INT_ENABLE 0 -#define IGU_INT_DISABLE 1 -#define IGU_INT_NOP 2 -#define IGU_INT_NOP2 3 +/* Host coalescing constants */ /* index numbers */ -#define HC_USTORM_DEF_SB_NUM_INDICES 4 -#define HC_CSTORM_DEF_SB_NUM_INDICES 8 -#define HC_XSTORM_DEF_SB_NUM_INDICES 4 -#define HC_TSTORM_DEF_SB_NUM_INDICES 4 -#define HC_USTORM_SB_NUM_INDICES 4 -#define HC_CSTORM_SB_NUM_INDICES 4 +#define HC_USTORM_DEF_SB_NUM_INDICES 4 +#define HC_CSTORM_DEF_SB_NUM_INDICES 8 +#define HC_XSTORM_DEF_SB_NUM_INDICES 4 +#define HC_TSTORM_DEF_SB_NUM_INDICES 4 +#define HC_USTORM_SB_NUM_INDICES 4 +#define HC_CSTORM_SB_NUM_INDICES 4 /* index values - which counterto update */ -#define HC_INDEX_U_ETH_RX_CQ_CONS 1 +#define HC_INDEX_U_TOE_RX_CQ_CONS 0 +#define HC_INDEX_U_ETH_RX_CQ_CONS 1 +#define HC_INDEX_U_ETH_RX_BD_CONS 2 +#define HC_INDEX_U_FCOE_EQ_CONS 3 + +#define HC_INDEX_C_TOE_TX_CQ_CONS 0 +#define HC_INDEX_C_ETH_TX_CQ_CONS 1 +#define HC_INDEX_C_ISCSI_EQ_CONS 2 + +#define HC_INDEX_DEF_X_SPQ_CONS 0 -#define HC_INDEX_C_ETH_TX_CQ_CONS 1 +#define HC_INDEX_DEF_C_RDMA_EQ_CONS 0 +#define HC_INDEX_DEF_C_RDMA_NAL_PROD 1 +#define HC_INDEX_DEF_C_ETH_FW_TX_CQ_CONS 2 +#define HC_INDEX_DEF_C_ETH_SLOW_PATH 3 +#define HC_INDEX_DEF_C_ETH_RDMA_CQ_CONS 4 +#define HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS 5 -#define HC_INDEX_DEF_X_SPQ_CONS 0 +#define HC_INDEX_DEF_U_ETH_RDMA_RX_CQ_CONS 0 +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS 1 +#define HC_INDEX_DEF_U_ETH_RDMA_RX_BD_CONS 2 +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_BD_CONS 3 -#define HC_INDEX_DEF_C_ETH_FW_TX_CQ_CONS 2 -#define HC_INDEX_DEF_C_ETH_SLOW_PATH 3 /* used by the driver to get the SB offset */ -#define USTORM_ID 0 -#define CSTORM_ID 1 -#define XSTORM_ID 2 -#define TSTORM_ID 3 -#define ATTENTION_ID 4 +#define USTORM_ID 0 +#define CSTORM_ID 1 +#define XSTORM_ID 2 +#define TSTORM_ID 3 +#define ATTENTION_ID 4 /* max number of slow path commands per port */ -#define MAX_RAMRODS_PER_PORT (8) +#define MAX_RAMRODS_PER_PORT (8) /* values for RX ETH CQE type field */ -#define RX_ETH_CQE_TYPE_ETH_FASTPATH (0) -#define RX_ETH_CQE_TYPE_ETH_RAMROD (1) - -/* MAC address list size */ -#define T_MAC_ADDRESS_LIST_SIZE (96) - +#define RX_ETH_CQE_TYPE_ETH_FASTPATH (0) +#define RX_ETH_CQE_TYPE_ETH_RAMROD (1) + + +/**** DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/ +#define EMULATION_FREQUENCY_FACTOR (1600) +#define FPGA_FREQUENCY_FACTOR (100) + +#define TIMERS_TICK_SIZE_CHIP (1e-3) +#define TIMERS_TICK_SIZE_EMUL \ + ((TIMERS_TICK_SIZE_CHIP)/((EMULATION_FREQUENCY_FACTOR))) +#define TIMERS_TICK_SIZE_FPGA \ + ((TIMERS_TICK_SIZE_CHIP)/((FPGA_FREQUENCY_FACTOR))) + +#define TSEMI_CLK1_RESUL_CHIP (1e-3) +#define TSEMI_CLK1_RESUL_EMUL \ + ((TSEMI_CLK1_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define TSEMI_CLK1_RESUL_FPGA \ + ((TSEMI_CLK1_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + +#define USEMI_CLK1_RESUL_CHIP \ + (TIMERS_TICK_SIZE_CHIP) +#define USEMI_CLK1_RESUL_EMUL \ + (TIMERS_TICK_SIZE_EMUL) +#define USEMI_CLK1_RESUL_FPGA \ + (TIMERS_TICK_SIZE_FPGA) + +#define XSEMI_CLK1_RESUL_CHIP (1e-3) +#define XSEMI_CLK1_RESUL_EMUL \ + ((XSEMI_CLK1_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define XSEMI_CLK1_RESUL_FPGA \ + ((XSEMI_CLK1_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + +#define XSEMI_CLK2_RESUL_CHIP (1e-6) +#define XSEMI_CLK2_RESUL_EMUL \ + ((XSEMI_CLK2_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define XSEMI_CLK2_RESUL_FPGA \ + ((XSEMI_CLK2_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + +#define SDM_TIMER_TICK_RESUL_CHIP (4*(1e-6)) +#define SDM_TIMER_TICK_RESUL_EMUL \ + ((SDM_TIMER_TICK_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define SDM_TIMER_TICK_RESUL_FPGA \ + ((SDM_TIMER_TICK_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + + +/**** END DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/ #define XSTORM_IP_ID_ROLL_HALF 0x8000 #define XSTORM_IP_ID_ROLL_ALL 0 -#define FW_LOG_LIST_SIZE (50) +#define FW_LOG_LIST_SIZE (50) + +#define NUM_OF_PROTOCOLS 4 +#define MAX_COS_NUMBER 16 +#define MAX_T_STAT_COUNTER_ID 18 +#define MAX_X_STAT_COUNTER_ID 18 -#define NUM_OF_PROTOCOLS 4 -#define MAX_COS_NUMBER 16 -#define MAX_T_STAT_COUNTER_ID 18 +#define UNKNOWN_ADDRESS 0 +#define UNICAST_ADDRESS 1 +#define MULTICAST_ADDRESS 2 +#define BROADCAST_ADDRESS 3 -#define T_FAIR 1 -#define FAIR_MEM 2 -#define RS_PERIODIC_TIMEOUT_IN_SDM_TICS 25 +#define SINGLE_FUNCTION 0 +#define MULTI_FUNCTION 1 -#define UNKNOWN_ADDRESS 0 -#define UNICAST_ADDRESS 1 -#define MULTICAST_ADDRESS 2 -#define BROADCAST_ADDRESS 3 +#define IP_V4 0 +#define IP_V6 1 diff --git a/drivers/net/bnx2x_hsi.h b/drivers/net/bnx2x_hsi.h index 96208ace1466..e515d68ea20f 100644 --- a/drivers/net/bnx2x_hsi.h +++ b/drivers/net/bnx2x_hsi.h @@ -132,6 +132,12 @@ struct shared_hw_cfg { /* NVRAM Offset */ #define SHARED_HW_CFG_BOARD_TYPE_BCM957710T1003G 0x00000008 #define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G 0x00000009 #define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1021G 0x0000000a +#define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1023G 0x0000000b +#define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1033G 0x0000000c +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711T1101 0x0000000d +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711ET1201 0x0000000e +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711A1133G 0x0000000f +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711EA1233G 0x00000010 #define SHARED_HW_CFG_BOARD_VER_MASK 0xffff0000 #define SHARED_HW_CFG_BOARD_VER_SHIFT 16 @@ -313,6 +319,7 @@ struct shared_feat_cfg { /* NVRAM Offset */ u32 config; /* 0x450 */ #define SHARED_FEATURE_BMC_ECHO_MODE_EN 0x00000001 +#define SHARED_FEATURE_MF_MODE_DISABLED 0x00000100 }; @@ -502,20 +509,20 @@ struct port_feat_cfg { /* port 0: 0x454 port 1: 0x4c8 */ }; -/***************************************************************************** - * Device Information * - *****************************************************************************/ -struct dev_info { /* size */ +/**************************************************************************** + * Device Information * + ****************************************************************************/ +struct dev_info { /* size */ - u32 bc_rev; /* 8 bits each: major, minor, build */ /* 4 */ + u32 bc_rev; /* 8 bits each: major, minor, build */ /* 4 */ - struct shared_hw_cfg shared_hw_config; /* 40 */ + struct shared_hw_cfg shared_hw_config; /* 40 */ - struct port_hw_cfg port_hw_config[PORT_MAX]; /* 400*2=800 */ + struct port_hw_cfg port_hw_config[PORT_MAX]; /* 400*2=800 */ - struct shared_feat_cfg shared_feature_config; /* 4 */ + struct shared_feat_cfg shared_feature_config; /* 4 */ - struct port_feat_cfg port_feature_config[PORT_MAX]; /* 116*2=232 */ + struct port_feat_cfg port_feature_config[PORT_MAX];/* 116*2=232 */ }; @@ -632,7 +639,9 @@ struct drv_port_mb { #define LINK_STATUS_LINK_PARTNER_15GXFD_CAPABLE 0x08000000 #define LINK_STATUS_LINK_PARTNER_16GXFD_CAPABLE 0x10000000 - u32 reserved[3]; + u32 port_stx; + + u32 reserved[2]; }; @@ -655,6 +664,11 @@ struct drv_func_mb { #define DRV_MSG_CODE_GET_MANUF_KEY 0x82000000 #define DRV_MSG_CODE_LOAD_L2B_PRAM 0x90000000 +#define BIOS_MSG_CODE_LIC_CHALLENGE 0xff010000 +#define BIOS_MSG_CODE_LIC_RESPONSE 0xff020000 +#define BIOS_MSG_CODE_VIRT_MAC_PRIM 0xff030000 +#define BIOS_MSG_CODE_VIRT_MAC_ISCSI 0xff040000 + #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; @@ -684,6 +698,11 @@ struct drv_func_mb { #define FW_MSG_CODE_L2B_PRAM_X_LOAD_FAILURE 0x90230000 #define FW_MSG_CODE_L2B_PRAM_U_LOAD_FAILURE 0x90240000 +#define FW_MSG_CODE_LIC_CHALLENGE 0xff010000 +#define FW_MSG_CODE_LIC_RESPONSE 0xff020000 +#define FW_MSG_CODE_VIRT_MAC_PRIM 0xff030000 +#define FW_MSG_CODE_VIRT_MAC_ISCSI 0xff040000 + #define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 fw_mb_param; @@ -709,7 +728,13 @@ struct drv_func_mb { u32 iscsi_boot_signature; u32 iscsi_boot_block_offset; - u32 reserved[3]; + u32 drv_status; +#define DRV_STATUS_PMF 0x00000001 + + u32 virt_mac_upper; +#define VIRT_MAC_SIGN_MASK 0xffff0000 +#define VIRT_MAC_SIGNATURE 0x564d0000 + u32 virt_mac_lower; }; @@ -725,6 +750,92 @@ struct mgmtfw_state { }; +/**************************************************************************** + * Multi-Function configuration * + ****************************************************************************/ +struct shared_mf_cfg { + + u32 clp_mb; +#define SHARED_MF_CLP_SET_DEFAULT 0x00000000 + /* set by CLP */ +#define SHARED_MF_CLP_EXIT 0x00000001 + /* set by MCP */ +#define SHARED_MF_CLP_EXIT_DONE 0x00010000 + +}; + +struct port_mf_cfg { + + u32 dynamic_cfg; /* device control channel */ +#define PORT_MF_CFG_OUTER_VLAN_TAG_MASK 0x0000ffff +#define PORT_MF_CFG_OUTER_VLAN_TAG_SHIFT 0 +#define PORT_MF_CFG_DYNAMIC_CFG_ENABLED 0x00010000 +#define PORT_MF_CFG_DYNAMIC_CFG_DEFAULT 0x00000000 + + u32 reserved[3]; + +}; + +struct func_mf_cfg { + + u32 config; + /* E/R/I/D */ + /* function 0 of each port cannot be hidden */ +#define FUNC_MF_CFG_FUNC_HIDE 0x00000001 + +#define FUNC_MF_CFG_PROTOCOL_MASK 0x00000007 +#define FUNC_MF_CFG_PROTOCOL_ETHERNET 0x00000002 +#define FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA 0x00000004 +#define FUNC_MF_CFG_PROTOCOL_ISCSI 0x00000006 +#define FUNC_MF_CFG_PROTOCOL_DEFAULT\ + FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA + +#define FUNC_MF_CFG_FUNC_DISABLED 0x00000008 + + /* PRI */ + /* 0 - low priority, 3 - high priority */ +#define FUNC_MF_CFG_TRANSMIT_PRIORITY_MASK 0x00000300 +#define FUNC_MF_CFG_TRANSMIT_PRIORITY_SHIFT 8 +#define FUNC_MF_CFG_TRANSMIT_PRIORITY_DEFAULT 0x00000000 + + /* MINBW, MAXBW */ + /* value range - 0..100, increments in 100Mbps */ +#define FUNC_MF_CFG_MIN_BW_MASK 0x00ff0000 +#define FUNC_MF_CFG_MIN_BW_SHIFT 16 +#define FUNC_MF_CFG_MIN_BW_DEFAULT 0x00000000 +#define FUNC_MF_CFG_MAX_BW_MASK 0xff000000 +#define FUNC_MF_CFG_MAX_BW_SHIFT 24 +#define FUNC_MF_CFG_MAX_BW_DEFAULT 0x64000000 + + u32 mac_upper; /* MAC */ +#define FUNC_MF_CFG_UPPERMAC_MASK 0x0000ffff +#define FUNC_MF_CFG_UPPERMAC_SHIFT 0 +#define FUNC_MF_CFG_UPPERMAC_DEFAULT FUNC_MF_CFG_UPPERMAC_MASK + u32 mac_lower; +#define FUNC_MF_CFG_LOWERMAC_DEFAULT 0xffffffff + + u32 e1hov_tag; /* VNI */ +#define FUNC_MF_CFG_E1HOV_TAG_MASK 0x0000ffff +#define FUNC_MF_CFG_E1HOV_TAG_SHIFT 0 +#define FUNC_MF_CFG_E1HOV_TAG_DEFAULT FUNC_MF_CFG_E1HOV_TAG_MASK + + u32 reserved[2]; + +}; + +struct mf_cfg { + + struct shared_mf_cfg shared_mf_config; + struct port_mf_cfg port_mf_config[PORT_MAX]; +#if defined(b710) + struct func_mf_cfg func_mf_config[E1_FUNC_MAX]; +#else + struct func_mf_cfg func_mf_config[E1H_FUNC_MAX]; +#endif + +}; + + /**************************************************************************** * Shared Memory Region * ****************************************************************************/ @@ -760,18 +871,18 @@ struct shmem_region { /* SharedMem Offset (size) */ struct mgmtfw_state mgmtfw_state; /* 0x4ac (0x1b8) */ struct drv_port_mb port_mb[PORT_MAX]; /* 0x664 (16*2=0x20) */ -#if defined(b710) - struct drv_func_mb func_mb[E1_FUNC_MAX]; /* 0x684 (44*2=0x58) */ -#else struct drv_func_mb func_mb[E1H_FUNC_MAX]; -#endif + + struct mf_cfg mf_cfg; }; /* 0x6dc */ + + #define BCM_5710_FW_MAJOR_VERSION 4 -#define BCM_5710_FW_MINOR_VERSION 0 -#define BCM_5710_FW_REVISION_VERSION 14 +#define BCM_5710_FW_MINOR_VERSION 5 +#define BCM_5710_FW_REVISION_VERSION 1 #define BCM_5710_FW_COMPILE_FLAGS 1 @@ -810,7 +921,7 @@ struct doorbell_hdr { }; /* - * doorbell message send to the chip + * doorbell message sent to the chip */ struct doorbell { #if defined(__BIG_ENDIAN) @@ -866,8 +977,10 @@ struct parsing_flags { u16 flags; #define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE (0x1<<0) #define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE_SHIFT 0 -#define PARSING_FLAGS_NUMBER_OF_NESTED_VLANS (0x3<<1) -#define PARSING_FLAGS_NUMBER_OF_NESTED_VLANS_SHIFT 1 +#define PARSING_FLAGS_VLAN (0x1<<1) +#define PARSING_FLAGS_VLAN_SHIFT 1 +#define PARSING_FLAGS_EXTRA_VLAN (0x1<<2) +#define PARSING_FLAGS_EXTRA_VLAN_SHIFT 2 #define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL (0x3<<3) #define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL_SHIFT 3 #define PARSING_FLAGS_IP_OPTIONS (0x1<<5) @@ -891,6 +1004,12 @@ struct parsing_flags { }; +struct regpair { + u32 lo; + u32 hi; +}; + + /* * dmae command structure */ @@ -971,72 +1090,107 @@ struct double_regpair { /* - * The eth Rx Buffer Descriptor + * The eth storm context of Ustorm (configuration part) */ -struct eth_rx_bd { - u32 addr_lo; - u32 addr_hi; -}; - -/* - * The eth storm context of Ustorm - */ -struct ustorm_eth_st_context { +struct ustorm_eth_st_context_config { #if defined(__BIG_ENDIAN) - u8 sb_index_number; + u8 flags; +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT (0x1<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC (0x1<<1) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC_SHIFT 1 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA (0x1<<2) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA_SHIFT 2 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING (0x1<<3) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING_SHIFT 3 +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0 (0xF<<4) +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0_SHIFT 4 u8 status_block_id; - u8 __local_rx_bd_cons; - u8 __local_rx_bd_prod; + u8 clientId; + u8 sb_index_numbers; +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER (0xF<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER (0xF<<4) +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER_SHIFT 4 #elif defined(__LITTLE_ENDIAN) - u8 __local_rx_bd_prod; - u8 __local_rx_bd_cons; + u8 sb_index_numbers; +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER (0xF<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER (0xF<<4) +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER_SHIFT 4 + u8 clientId; u8 status_block_id; - u8 sb_index_number; + u8 flags; +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT (0x1<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC (0x1<<1) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC_SHIFT 1 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA (0x1<<2) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA_SHIFT 2 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING (0x1<<3) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING_SHIFT 3 +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0 (0xF<<4) +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0_SHIFT 4 #endif #if defined(__BIG_ENDIAN) - u16 rcq_cons; - u16 rx_bd_cons; + u16 bd_buff_size; + u16 mc_alignment_size; #elif defined(__LITTLE_ENDIAN) - u16 rx_bd_cons; - u16 rcq_cons; + u16 mc_alignment_size; + u16 bd_buff_size; #endif - u32 rx_bd_page_base_lo; - u32 rx_bd_page_base_hi; - u32 rcq_base_address_lo; - u32 rcq_base_address_hi; #if defined(__BIG_ENDIAN) - u16 __num_of_returned_cqes; - u8 num_rss; - u8 flags; -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT (0x1<<0) -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT_SHIFT 0 -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC (0x1<<1) -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC_SHIFT 1 -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA (0x1<<2) -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA_SHIFT 2 -#define __USTORM_ETH_ST_CONTEXT_RESERVED0 (0x1F<<3) -#define __USTORM_ETH_ST_CONTEXT_RESERVED0_SHIFT 3 + u8 __local_sge_prod; + u8 __local_bd_prod; + u16 sge_buff_size; #elif defined(__LITTLE_ENDIAN) - u8 flags; -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT (0x1<<0) -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT_SHIFT 0 -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC (0x1<<1) -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC_SHIFT 1 -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA (0x1<<2) -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA_SHIFT 2 -#define __USTORM_ETH_ST_CONTEXT_RESERVED0 (0x1F<<3) -#define __USTORM_ETH_ST_CONTEXT_RESERVED0_SHIFT 3 - u8 num_rss; - u16 __num_of_returned_cqes; + u16 sge_buff_size; + u8 __local_bd_prod; + u8 __local_sge_prod; #endif #if defined(__BIG_ENDIAN) - u16 mc_alignment_size; - u16 agg_threshold; + u16 __bd_cons; + u16 __sge_cons; #elif defined(__LITTLE_ENDIAN) - u16 agg_threshold; - u16 mc_alignment_size; + u16 __sge_cons; + u16 __bd_cons; #endif + u32 bd_page_base_lo; + u32 bd_page_base_hi; + u32 sge_page_base_lo; + u32 sge_page_base_hi; +}; + +/* + * The eth Rx Buffer Descriptor + */ +struct eth_rx_bd { + u32 addr_lo; + u32 addr_hi; +}; + +/* + * The eth Rx SGE Descriptor + */ +struct eth_rx_sge { + u32 addr_lo; + u32 addr_hi; +}; + +/* + * Local BDs and SGEs rings (in ETH) + */ +struct eth_local_rx_rings { struct eth_rx_bd __local_bd_ring[16]; + struct eth_rx_sge __local_sge_ring[12]; +}; + +/* + * The eth storm context of Ustorm + */ +struct ustorm_eth_st_context { + struct ustorm_eth_st_context_config common; + struct eth_local_rx_rings __rings; }; /* @@ -1107,9 +1261,9 @@ struct xstorm_eth_extra_ag_context_section { #if defined(__BIG_ENDIAN) u16 __reserved3; u8 __reserved2; - u8 __agg_misc7; + u8 __da_only_cnt; #elif defined(__LITTLE_ENDIAN) - u8 __agg_misc7; + u8 __da_only_cnt; u8 __reserved2; u16 __reserved3; #endif @@ -1387,7 +1541,13 @@ struct timers_block_context { u32 __reserved_0; u32 __reserved_1; u32 __reserved_2; - u32 __reserved_flags; + u32 flags; +#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS (0x3<<0) +#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS_SHIFT 0 +#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG (0x1<<2) +#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG_SHIFT 2 +#define __TIMERS_BLOCK_CONTEXT_RESERVED0 (0x1FFFFFFF<<3) +#define __TIMERS_BLOCK_CONTEXT_RESERVED0_SHIFT 3 }; /* @@ -1497,11 +1657,19 @@ struct xstorm_eth_st_context { u32 tx_bd_page_base_hi; #if defined(__BIG_ENDIAN) u16 tx_bd_cons; - u8 __reserved0; + u8 statistics_data; +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID (0x7F<<0) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID_SHIFT 0 +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE (0x1<<7) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE_SHIFT 7 u8 __local_tx_bd_prod; #elif defined(__LITTLE_ENDIAN) u8 __local_tx_bd_prod; - u8 __reserved0; + u8 statistics_data; +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID (0x7F<<0) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID_SHIFT 0 +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE (0x1<<7) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE_SHIFT 7 u16 tx_bd_cons; #endif u32 db_data_addr_lo; @@ -1578,7 +1746,7 @@ struct eth_tx_doorbell { struct ustorm_def_status_block { u16 index_values[HC_USTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1589,7 +1757,7 @@ struct ustorm_def_status_block { struct cstorm_def_status_block { u16 index_values[HC_CSTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1600,7 +1768,7 @@ struct cstorm_def_status_block { struct xstorm_def_status_block { u16 index_values[HC_XSTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1611,7 +1779,7 @@ struct xstorm_def_status_block { struct tstorm_def_status_block { u16 index_values[HC_TSTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1634,7 +1802,7 @@ struct host_def_status_block { struct ustorm_status_block { u16 index_values[HC_USTORM_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1645,7 +1813,7 @@ struct ustorm_status_block { struct cstorm_status_block { u16 index_values[HC_CSTORM_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1683,20 +1851,21 @@ struct eth_dynamic_hc_config { * regular eth FP CQE parameters struct */ struct eth_fast_path_rx_cqe { - u8 type; - u8 error_type_flags; -#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<0) -#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 0 -#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<1) -#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 1 -#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<2) -#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 2 -#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<3) -#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 3 -#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<4) -#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 4 -#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x7<<5) -#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 5 + u8 type_error_flags; +#define ETH_FAST_PATH_RX_CQE_TYPE (0x1<<0) +#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<1) +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 1 +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<2) +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 2 +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x3<<6) +#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 6 u8 status_flags; #define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0) #define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0 @@ -1711,11 +1880,13 @@ struct eth_fast_path_rx_cqe { #define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7) #define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7 u8 placement_offset; + u8 queue_index; u32 rss_hash_result; u16 vlan_tag; u16 pkt_len; - u16 queue_index; + u16 len_on_bd; struct parsing_flags pars_flags; + u16 sgl[8]; }; @@ -1728,6 +1899,23 @@ struct eth_halt_ramrod_data { }; +/* + * The data for statistics query ramrod + */ +struct eth_query_ramrod_data { +#if defined(__BIG_ENDIAN) + u8 reserved0; + u8 collect_port_1b; + u16 drv_counter; +#elif defined(__LITTLE_ENDIAN) + u16 drv_counter; + u8 collect_port_1b; + u8 reserved0; +#endif + u32 ctr_id_vector; +}; + + /* * Place holder for ramrods protocol specific data */ @@ -1758,15 +1946,20 @@ struct eth_rx_bd_next_page { * Eth Rx Cqe structure- general structure for ramrods */ struct common_ramrod_eth_rx_cqe { - u8 type; + u8 ramrod_type; +#define COMMON_RAMROD_ETH_RX_CQE_TYPE (0x1<<0) +#define COMMON_RAMROD_ETH_RX_CQE_TYPE_SHIFT 0 +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0 (0x7F<<1) +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0_SHIFT 1 u8 conn_type_3b; - u16 reserved; + u16 reserved1; u32 conn_and_cmd_data; #define COMMON_RAMROD_ETH_RX_CQE_CID (0xFFFFFF<<0) #define COMMON_RAMROD_ETH_RX_CQE_CID_SHIFT 0 #define COMMON_RAMROD_ETH_RX_CQE_CMD_ID (0xFF<<24) #define COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT 24 struct ramrod_data protocol_data; + u32 reserved2[4]; }; /* @@ -1775,8 +1968,7 @@ struct common_ramrod_eth_rx_cqe { struct eth_rx_cqe_next_page { u32 addr_lo; u32 addr_hi; - u32 reserved0; - u32 reserved1; + u32 reserved[6]; }; /* @@ -1806,11 +1998,6 @@ struct spe_hdr { u16 reserved; }; -struct regpair { - u32 lo; - u32 hi; -}; - /* * ethernet slow path element */ @@ -1821,6 +2008,7 @@ union eth_specific_data { struct eth_halt_ramrod_data halt_ramrod_data; struct regpair leading_cqe_addr; struct regpair update_data_addr; + struct eth_query_ramrod_data query_ramrod_data; }; /* @@ -1843,10 +2031,13 @@ struct eth_tx_db_data { /* - * Common configuration parameters per port in Tstorm + * Common configuration parameters per function in Tstorm */ struct tstorm_eth_function_common_config { - u32 config_flags; +#if defined(__BIG_ENDIAN) + u8 leading_client_id; + u8 rss_result_mask; + u16 config_flags; #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0) #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0 #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1) @@ -1859,17 +2050,32 @@ struct tstorm_eth_function_common_config { #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE_SHIFT 4 #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE (0x1<<5) #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE_SHIFT 5 -#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x3FFFFFF<<6) -#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 6 -#if defined(__BIG_ENDIAN) - u16 __secondary_vlan_id; - u8 leading_client_id; - u8 rss_result_mask; +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM (0x1<<6) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM_SHIFT 6 +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x1FF<<7) +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 7 #elif defined(__LITTLE_ENDIAN) + u16 config_flags; +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY_SHIFT 1 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY (0x1<<2) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY_SHIFT 2 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY (0x1<<3) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY_SHIFT 3 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE (0x1<<4) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE_SHIFT 4 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE (0x1<<5) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE_SHIFT 5 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM (0x1<<6) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM_SHIFT 6 +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x1FF<<7) +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 7 u8 rss_result_mask; u8 leading_client_id; - u16 __secondary_vlan_id; #endif + u16 vlan_id[2]; }; /* @@ -1887,7 +2093,7 @@ struct eth_update_ramrod_data { struct mac_configuration_hdr { u8 length_6b; u8 offset; - u16 reserved0; + u16 client_id; u32 reserved1; }; @@ -1943,16 +2149,56 @@ struct mac_configuration_cmd { }; +/* + * MAC address in list for ramrod + */ +struct mac_configuration_entry_e1h { + u16 lsb_mac_addr; + u16 middle_mac_addr; + u16 msb_mac_addr; + u16 vlan_id; + u16 e1hov_id; + u8 client_id; + u8 flags; +#define MAC_CONFIGURATION_ENTRY_E1H_PORT (0x1<<0) +#define MAC_CONFIGURATION_ENTRY_E1H_PORT_SHIFT 0 +#define MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE (0x1<<1) +#define MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE_SHIFT 1 +#define MAC_CONFIGURATION_ENTRY_E1H_RDMA_MAC (0x1<<2) +#define MAC_CONFIGURATION_ENTRY_E1H_RDMA_MAC_SHIFT 2 +#define MAC_CONFIGURATION_ENTRY_E1H_RESERVED0 (0x1F<<3) +#define MAC_CONFIGURATION_ENTRY_E1H_RESERVED0_SHIFT 3 +}; + +/* + * MAC filtering configuration command + */ +struct mac_configuration_cmd_e1h { + struct mac_configuration_hdr hdr; + struct mac_configuration_entry_e1h config_table[32]; +}; + + +/* + * approximate-match multicast filtering for E1H per function in Tstorm + */ +struct tstorm_eth_approximate_match_multicast_filtering { + u32 mcast_add_hash_bit_array[8]; +}; + + /* * Configuration parameters per client in Tstorm */ struct tstorm_eth_client_config { #if defined(__BIG_ENDIAN) - u16 statistics_counter_id; + u8 max_sges_for_packet; + u8 statistics_counter_id; u16 mtu; #elif defined(__LITTLE_ENDIAN) u16 mtu; - u16 statistics_counter_id; + u8 statistics_counter_id; + u8 max_sges_for_packet; #endif #if defined(__BIG_ENDIAN) u16 drop_flags; @@ -1960,42 +2206,42 @@ struct tstorm_eth_client_config { #define TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR_SHIFT 1 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR (0x1<<2) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR_SHIFT 2 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<3) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 3 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<4) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 4 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0x7FF<<5) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 5 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<3) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 3 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0xFFF<<4) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 4 u16 config_flags; #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE (0x1<<0) #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE_SHIFT 1 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x3FFF<<2) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING_SHIFT 2 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x1FFF<<3) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 3 #elif defined(__LITTLE_ENDIAN) u16 config_flags; #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE (0x1<<0) #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE_SHIFT 1 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x3FFF<<2) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING_SHIFT 2 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x1FFF<<3) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 3 u16 drop_flags; #define TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR (0x1<<0) #define TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR_SHIFT 1 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR (0x1<<2) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR_SHIFT 2 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<3) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 3 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<4) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 4 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0x7FF<<5) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 5 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<3) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 3 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0xFFF<<4) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 4 #endif }; @@ -2011,96 +2257,112 @@ struct tstorm_eth_mac_filter_config { u32 bcast_drop_all; u32 bcast_accept_all; u32 strict_vlan; - u32 __secondary_vlan_clients; + u32 vlan_filter[2]; + u32 reserved; }; -struct rate_shaping_per_protocol { +/* + * Three RX producers for ETH + */ +struct tstorm_eth_rx_producers { #if defined(__BIG_ENDIAN) - u16 reserved0; - u16 protocol_rate; + u16 bd_prod; + u16 cqe_prod; #elif defined(__LITTLE_ENDIAN) - u16 protocol_rate; - u16 reserved0; + u16 cqe_prod; + u16 bd_prod; #endif - u32 protocol_quota; - s32 current_credit; - u32 reserved; -}; - -struct rate_shaping_vars { - struct rate_shaping_per_protocol protocol_vars[NUM_OF_PROTOCOLS]; - u32 pause_mask; - u32 periodic_stop; - u32 rs_periodic_timeout; - u32 rs_threshold; - u32 last_periodic_time; - u32 reserved; -}; - -struct fairness_per_protocol { - u32 credit_delta; - s32 fair_credit; #if defined(__BIG_ENDIAN) - u16 reserved0; - u8 state; - u8 weight; + u16 reserved; + u16 sge_prod; #elif defined(__LITTLE_ENDIAN) - u8 weight; - u8 state; - u16 reserved0; + u16 sge_prod; + u16 reserved; #endif - u32 reserved1; }; -struct fairness_vars { - struct fairness_per_protocol protocol_vars[NUM_OF_PROTOCOLS]; - u32 upper_bound; - u32 port_rate; - u32 pause_mask; - u32 fair_threshold; -}; -struct safc_struct { - u32 cur_pause_mask; - u32 expire_time; +/* + * common flag to indicate existance of TPA. + */ +struct tstorm_eth_tpa_exist { #if defined(__BIG_ENDIAN) - u16 reserved0; - u8 cur_cos_types; - u8 safc_timeout_usec; + u16 reserved1; + u8 reserved0; + u8 tpa_exist; #elif defined(__LITTLE_ENDIAN) - u8 safc_timeout_usec; - u8 cur_cos_types; - u16 reserved0; + u8 tpa_exist; + u8 reserved0; + u16 reserved1; #endif - u32 reserved1; + u32 reserved2; }; -struct demo_struct { + +/* + * per-port SAFC demo variables + */ +struct cmng_flags_per_port { u8 con_number[NUM_OF_PROTOCOLS]; #if defined(__BIG_ENDIAN) - u8 reserved1; u8 fairness_enable; u8 rate_shaping_enable; - u8 cmng_enable; + u8 cmng_protocol_enable; + u8 cmng_vn_enable; #elif defined(__LITTLE_ENDIAN) - u8 cmng_enable; + u8 cmng_vn_enable; + u8 cmng_protocol_enable; u8 rate_shaping_enable; u8 fairness_enable; - u8 reserved1; #endif }; -struct cmng_struct { - struct rate_shaping_vars rs_vars; - struct fairness_vars fair_vars; - struct safc_struct safc_vars; - struct demo_struct demo_vars; + +/* + * per-port rate shaping variables + */ +struct rate_shaping_vars_per_port { + u32 rs_periodic_timeout; + u32 rs_threshold; +}; + + +/* + * per-port fairness variables + */ +struct fairness_vars_per_port { + u32 upper_bound; + u32 fair_threshold; + u32 fairness_timeout; +}; + + +/* + * per-port SAFC variables + */ +struct safc_struct_per_port { +#if defined(__BIG_ENDIAN) + u16 __reserved0; + u8 cur_cos_types; + u8 safc_timeout_usec; +#elif defined(__LITTLE_ENDIAN) + u8 safc_timeout_usec; + u8 cur_cos_types; + u16 __reserved0; +#endif + u8 cos_to_protocol[MAX_COS_NUMBER]; }; -struct cos_to_protocol { - u8 mask[MAX_COS_NUMBER]; +/* + * Per-port congestion management variables + */ +struct cmng_struct_per_port { + struct rate_shaping_vars_per_port rs_vars; + struct fairness_vars_per_port fair_vars; + struct safc_struct_per_port safc_vars; + struct cmng_flags_per_port flags; }; @@ -2161,6 +2423,16 @@ struct eth_stats_query { }; +/* + * per-vnic fairness variables + */ +struct fairness_vars_per_vn { + u32 protocol_credit_delta[NUM_OF_PROTOCOLS]; + u32 vn_credit_delta; + u32 __reserved0; +}; + + /* * FW version stored in the Xstorm RAM */ @@ -2179,8 +2451,10 @@ struct fw_version { #define FW_VERSION_OPTIMIZED_SHIFT 0 #define FW_VERSION_BIG_ENDIEN (0x1<<1) #define FW_VERSION_BIG_ENDIEN_SHIFT 1 -#define __FW_VERSION_RESERVED (0x3FFFFFFF<<2) -#define __FW_VERSION_RESERVED_SHIFT 2 +#define FW_VERSION_CHIP_VERSION (0x3<<2) +#define FW_VERSION_CHIP_VERSION_SHIFT 2 +#define __FW_VERSION_RESERVED (0xFFFFFFF<<4) +#define __FW_VERSION_RESERVED_SHIFT 4 }; @@ -2188,15 +2462,9 @@ struct fw_version { * FW version stored in first line of pram */ struct pram_fw_version { -#if defined(__BIG_ENDIAN) - u16 patch; - u8 primary; - u8 client; -#elif defined(__LITTLE_ENDIAN) u8 client; u8 primary; u16 patch; -#endif u8 flags; #define PRAM_FW_VERSION_OPTIMIZED (0x1<<0) #define PRAM_FW_VERSION_OPTIMIZED_SHIFT 0 @@ -2204,8 +2472,34 @@ struct pram_fw_version { #define PRAM_FW_VERSION_STORM_ID_SHIFT 1 #define PRAM_FW_VERSION_BIG_ENDIEN (0x1<<3) #define PRAM_FW_VERSION_BIG_ENDIEN_SHIFT 3 -#define __PRAM_FW_VERSION_RESERVED0 (0xF<<4) -#define __PRAM_FW_VERSION_RESERVED0_SHIFT 4 +#define PRAM_FW_VERSION_CHIP_VERSION (0x3<<4) +#define PRAM_FW_VERSION_CHIP_VERSION_SHIFT 4 +#define __PRAM_FW_VERSION_RESERVED0 (0x3<<6) +#define __PRAM_FW_VERSION_RESERVED0_SHIFT 6 +}; + + +/* + * a single rate shaping counter. can be used as protocol or vnic counter + */ +struct rate_shaping_counter { + u32 quota; +#if defined(__BIG_ENDIAN) + u16 __reserved0; + u16 rate; +#elif defined(__LITTLE_ENDIAN) + u16 rate; + u16 __reserved0; +#endif +}; + + +/* + * per-vnic rate shaping variables + */ +struct rate_shaping_vars_per_vn { + struct rate_shaping_counter protocol_counters[NUM_OF_PROTOCOLS]; + struct rate_shaping_counter vn_counter; }; diff --git a/drivers/net/bnx2x_init.h b/drivers/net/bnx2x_init.h index 5a4e82b9e7bf..4c7750789b62 100644 --- a/drivers/net/bnx2x_init.h +++ b/drivers/net/bnx2x_init.h @@ -226,28 +226,28 @@ static const u32 *bnx2x_sel_blob(u32 addr, const u32 *data, int is_e1) tsem_int_table_data_e1h; else IF_IS_INT_TABLE_ADDR(CSEM_REG_INT_TABLE, addr) - data = is_e1 ? csem_int_table_data_e1 : - csem_int_table_data_e1h; + data = is_e1 ? csem_int_table_data_e1 : + csem_int_table_data_e1h; else IF_IS_INT_TABLE_ADDR(USEM_REG_INT_TABLE, addr) - data = is_e1 ? usem_int_table_data_e1 : - usem_int_table_data_e1h; + data = is_e1 ? usem_int_table_data_e1 : + usem_int_table_data_e1h; else IF_IS_INT_TABLE_ADDR(XSEM_REG_INT_TABLE, addr) - data = is_e1 ? xsem_int_table_data_e1 : - xsem_int_table_data_e1h; + data = is_e1 ? xsem_int_table_data_e1 : + xsem_int_table_data_e1h; else IF_IS_PRAM_ADDR(TSEM_REG_PRAM, addr) - data = is_e1 ? tsem_pram_data_e1 : tsem_pram_data_e1h; + data = is_e1 ? tsem_pram_data_e1 : tsem_pram_data_e1h; else IF_IS_PRAM_ADDR(CSEM_REG_PRAM, addr) - data = is_e1 ? csem_pram_data_e1 : csem_pram_data_e1h; + data = is_e1 ? csem_pram_data_e1 : csem_pram_data_e1h; else IF_IS_PRAM_ADDR(USEM_REG_PRAM, addr) - data = is_e1 ? usem_pram_data_e1 : usem_pram_data_e1h; + data = is_e1 ? usem_pram_data_e1 : usem_pram_data_e1h; else IF_IS_PRAM_ADDR(XSEM_REG_PRAM, addr) - data = is_e1 ? xsem_pram_data_e1 : xsem_pram_data_e1h; + data = is_e1 ? xsem_pram_data_e1 : xsem_pram_data_e1h; return data; } diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index 22586ebd7b1e..ff2743db10d9 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -3572,7 +3572,8 @@ u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, LED_BLINK_RATE_VAL); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1); - if (((speed == SPEED_2500) || + if (!CHIP_IS_E1H(bp) && + ((speed == SPEED_2500) || (speed == SPEED_1000) || (speed == SPEED_100) || (speed == SPEED_10))) { @@ -3753,6 +3754,14 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars) vars->duplex = DUPLEX_FULL; vars->flow_ctrl = FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); + /* enable on E1.5 FPGA */ + if (CHIP_IS_E1H(bp)) { + vars->flow_ctrl |= + (FLOW_CTRL_TX | FLOW_CTRL_RX); + vars->link_status |= + (LINK_STATUS_TX_FLOW_CONTROL_ENABLED | + LINK_STATUS_RX_FLOW_CONTROL_ENABLED); + } bnx2x_emac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index efa942688f84..90b54e4c5c3b 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -1,4 +1,4 @@ -/* bnx2x.c: Broadcom Everest network driver. +/* bnx2x_main.c: Broadcom Everest network driver. * * Copyright (c) 2007-2008 Broadcom Corporation * @@ -15,12 +15,6 @@ * */ -/* define this to make the driver freeze on error - * to allow getting debug info - * (you will need to reboot afterwards) - */ -/*#define BNX2X_STOP_ON_ERROR*/ - #include #include #include @@ -46,16 +40,17 @@ #include #ifdef NETIF_F_HW_VLAN_TX #include - #define BCM_VLAN 1 #endif #include #include #include +#include +#include #include #include +#include #include #include -#include #include #include "bnx2x_reg.h" @@ -67,13 +62,13 @@ #define DRV_MODULE_VERSION "1.42.4" #define DRV_MODULE_RELDATE "2008/4/9" -#define BNX2X_BC_VER 0x040200 +#define BNX2X_BC_VER 0x040200 -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (5*HZ) +/* Time in jiffies before concluding the transmitter is hung */ +#define TX_TIMEOUT (5*HZ) static char version[] __devinitdata = - "Broadcom NetXtreme II 5771X 10Gigabit Ethernet Driver " + "Broadcom NetXtreme II 5771x 10Gigabit Ethernet Driver " DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Eliezer Tamir"); @@ -83,20 +78,19 @@ MODULE_VERSION(DRV_MODULE_VERSION); static int use_inta; static int poll; -static int onefunc; -static int nomcp; static int debug; +static int nomcp; +static int load_count[3]; /* 0-common, 1-port0, 2-port1 */ static int use_multi; module_param(use_inta, int, 0); module_param(poll, int, 0); -module_param(onefunc, int, 0); module_param(debug, int, 0); +module_param(nomcp, int, 0); MODULE_PARM_DESC(use_inta, "use INT#A instead of MSI-X"); MODULE_PARM_DESC(poll, "use polling (for debug)"); -MODULE_PARM_DESC(onefunc, "enable only first function"); -MODULE_PARM_DESC(nomcp, "ignore management CPU (Implies onefunc)"); MODULE_PARM_DESC(debug, "default debug msglevel"); +MODULE_PARM_DESC(nomcp, "ignore management CPU"); #ifdef BNX2X_MULTI module_param(use_multi, int, 0); @@ -105,18 +99,27 @@ MODULE_PARM_DESC(use_multi, "use per-CPU queues"); enum bnx2x_board_type { BCM57710 = 0, + BCM57711 = 1, + BCM57711E = 2, }; -/* indexed by board_t, above */ +/* indexed by board_type, above */ static struct { char *name; } board_info[] __devinitdata = { - { "Broadcom NetXtreme II BCM57710 XGb" } + { "Broadcom NetXtreme II BCM57710 XGb" }, + { "Broadcom NetXtreme II BCM57711 XGb" }, + { "Broadcom NetXtreme II BCM57711E XGb" } }; + static const struct pci_device_id bnx2x_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57710 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57711 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57711E }, { 0 } }; @@ -201,7 +204,8 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr, #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif - (bp->port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0)); + (BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | + (BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(dma_addr); dmae->src_addr_hi = U64_HI(dma_addr); dmae->dst_addr_lo = dst_addr >> 2; @@ -224,7 +228,7 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr, *wb_comp = 0; - bnx2x_post_dmae(bp, dmae, (bp->port)*MAX_DMAE_C_PER_PORT); + bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp)); udelay(5); @@ -277,7 +281,8 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32) #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif - (bp->port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0)); + (BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | + (BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = src_addr >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_data)); @@ -297,7 +302,7 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32) *wb_comp = 0; - bnx2x_post_dmae(bp, dmae, (bp->port)*MAX_DMAE_C_PER_PORT); + bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp)); udelay(5); @@ -345,47 +350,122 @@ static u64 bnx2x_wb_rd(struct bnx2x *bp, int reg) static int bnx2x_mc_assert(struct bnx2x *bp) { - int i, j, rc = 0; char last_idx; - const char storm[] = {"XTCU"}; - const u32 intmem_base[] = { - BAR_XSTRORM_INTMEM, - BAR_TSTRORM_INTMEM, - BAR_CSTRORM_INTMEM, - BAR_USTRORM_INTMEM - }; - - /* Go through all instances of all SEMIs */ - for (i = 0; i < 4; i++) { - last_idx = REG_RD8(bp, XSTORM_ASSERT_LIST_INDEX_OFFSET + - intmem_base[i]); - if (last_idx) - BNX2X_LOG("DATA %cSTORM_ASSERT_LIST_INDEX 0x%x\n", - storm[i], last_idx); - - /* print the asserts */ - for (j = 0; j < STROM_ASSERT_ARRAY_SIZE; j++) { - u32 row0, row1, row2, row3; - - row0 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + - intmem_base[i]); - row1 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + 4 + - intmem_base[i]); - row2 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + 8 + - intmem_base[i]); - row3 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + 12 + - intmem_base[i]); - - if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { - BNX2X_LOG("DATA %cSTORM_ASSERT_INDEX 0x%x =" - " 0x%08x 0x%08x 0x%08x 0x%08x\n", - storm[i], j, row3, row2, row1, row0); - rc++; - } else { - break; - } + int i, rc = 0; + u32 row0, row1, row2, row3; + + /* XSTORM */ + last_idx = REG_RD8(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("XSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("XSTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; + } + } + + /* TSTORM */ + last_idx = REG_RD8(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("TSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("TSTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; + } + } + + /* CSTORM */ + last_idx = REG_RD8(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("CSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("CSTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; + } + } + + /* USTORM */ + last_idx = REG_RD8(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("USTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("USTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; } } + return rc; } @@ -428,14 +508,16 @@ static void bnx2x_panic_dump(struct bnx2x *bp) struct eth_tx_db_data *hw_prods = fp->hw_tx_prods; BNX2X_ERR("queue[%d]: tx_pkt_prod(%x) tx_pkt_cons(%x)" - " tx_bd_prod(%x) tx_bd_cons(%x) *tx_cons_sb(%x)" - " *rx_cons_sb(%x) rx_comp_prod(%x)" - " rx_comp_cons(%x) fp_c_idx(%x) fp_u_idx(%x)" - " bd data(%x,%x)\n", + " tx_bd_prod(%x) tx_bd_cons(%x) *tx_cons_sb(%x)\n", i, fp->tx_pkt_prod, fp->tx_pkt_cons, fp->tx_bd_prod, - fp->tx_bd_cons, *fp->tx_cons_sb, *fp->rx_cons_sb, - fp->rx_comp_prod, fp->rx_comp_cons, fp->fp_c_idx, - fp->fp_u_idx, hw_prods->packets_prod, + fp->tx_bd_cons, le16_to_cpu(*fp->tx_cons_sb)); + BNX2X_ERR(" rx_comp_prod(%x) rx_comp_cons(%x)" + " *rx_cons_sb(%x)\n", + fp->rx_comp_prod, fp->rx_comp_cons, + le16_to_cpu(*fp->rx_cons_sb)); + BNX2X_ERR(" fp_c_idx(%x) fp_u_idx(%x)" + " bd data(%x,%x)\n", + fp->fp_c_idx, fp->fp_u_idx, hw_prods->packets_prod, hw_prods->bds_prod); start = TX_BD(le16_to_cpu(*fp->tx_cons_sb) - 10); @@ -463,7 +545,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) struct sw_rx_bd *sw_bd = &fp->rx_buf_ring[j]; BNX2X_ERR("rx_bd[%x]=[%x:%x] sw_bd=[%p]\n", - j, rx_bd[0], rx_bd[1], sw_bd->skb); + j, rx_bd[1], rx_bd[0], sw_bd->skb); } start = RCQ_BD(fp->rx_comp_cons - 10); @@ -482,7 +564,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) bp->def_c_idx, bp->def_u_idx, bp->def_x_idx, bp->def_t_idx, bp->def_att_idx, bp->attn_state, bp->spq_prod_idx); - + bnx2x_fw_dump(bp); bnx2x_mc_assert(bp); BNX2X_ERR("end crash dump -----------------\n"); @@ -492,7 +574,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) static void bnx2x_int_enable(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); u32 addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; u32 val = REG_RD(bp, addr); int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0; @@ -507,7 +589,6 @@ static void bnx2x_int_enable(struct bnx2x *bp) HC_CONFIG_0_REG_INT_LINE_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); - /* Errata A0.158 workaround */ DP(NETIF_MSG_INTR, "write %x to HC %d (addr 0x%x) MSI-X %d\n", val, port, addr, msix); @@ -520,11 +601,25 @@ static void bnx2x_int_enable(struct bnx2x *bp) val, port, addr, msix); REG_WR(bp, addr, val); + + if (CHIP_IS_E1H(bp)) { + /* init leading/trailing edge */ + if (IS_E1HMF(bp)) { + val = (0xfe0f | (1 << (BP_E1HVN(bp) + 4))); + if (bp->port.pmf) + /* enable nig attention */ + val |= 0x0100; + } else + val = 0xffff; + + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val); + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val); + } } static void bnx2x_int_disable(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); u32 addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; u32 val = REG_RD(bp, addr); @@ -543,10 +638,10 @@ static void bnx2x_int_disable(struct bnx2x *bp) static void bnx2x_int_disable_sync(struct bnx2x *bp) { - int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0; int i; + /* disable interrupt handling */ atomic_inc(&bp->intr_sem); /* prevent the HW from sending interrupts */ bnx2x_int_disable(bp); @@ -563,30 +658,29 @@ static void bnx2x_int_disable_sync(struct bnx2x *bp) /* make sure sp_task is not running */ cancel_work_sync(&bp->sp_task); - } -/* fast path code */ +/* fast path */ /* - * general service functions + * General service functions */ -static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 id, +static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 sb_id, u8 storm, u16 index, u8 op, u8 update) { - u32 igu_addr = (IGU_ADDR_INT_ACK + IGU_PORT_BASE * bp->port) * 8; + u32 igu_addr = (IGU_ADDR_INT_ACK + IGU_FUNC_BASE * BP_FUNC(bp)) * 8; struct igu_ack_register igu_ack; igu_ack.status_block_index = index; igu_ack.sb_id_and_flags = - ((id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | + ((sb_id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) | (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) | (op << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT)); -/* DP(NETIF_MSG_INTR, "write 0x%08x to IGU addr 0x%x\n", - (*(u32 *)&igu_ack), BAR_IGU_INTMEM + igu_addr); */ + DP(BNX2X_MSG_OFF, "write 0x%08x to IGU addr 0x%x\n", + (*(u32 *)&igu_ack), BAR_IGU_INTMEM + igu_addr); REG_WR(bp, BAR_IGU_INTMEM + igu_addr, (*(u32 *)&igu_ack)); } @@ -614,8 +708,9 @@ static inline int bnx2x_has_work(struct bnx2x_fastpath *fp) if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) rx_cons_sb++; - if ((rx_cons_sb != fp->rx_comp_cons) || - (le16_to_cpu(*fp->tx_cons_sb) != fp->tx_pkt_cons)) + if ((fp->rx_comp_cons != rx_cons_sb) || + (fp->tx_pkt_prod != le16_to_cpu(*fp->tx_cons_sb)) || + (fp->tx_pkt_prod != fp->tx_pkt_cons)) return 1; return 0; @@ -623,11 +718,11 @@ static inline int bnx2x_has_work(struct bnx2x_fastpath *fp) static u16 bnx2x_ack_int(struct bnx2x *bp) { - u32 igu_addr = (IGU_ADDR_SIMD_MASK + IGU_PORT_BASE * bp->port) * 8; + u32 igu_addr = (IGU_ADDR_SIMD_MASK + IGU_FUNC_BASE * BP_FUNC(bp)) * 8; u32 result = REG_RD(bp, BAR_IGU_INTMEM + igu_addr); -/* DP(NETIF_MSG_INTR, "read 0x%08x from IGU addr 0x%x\n", - result, BAR_IGU_INTMEM + igu_addr); */ + DP(BNX2X_MSG_OFF, "read 0x%08x from IGU addr 0x%x\n", + result, BAR_IGU_INTMEM + igu_addr); #ifdef IGU_DEBUG #warning IGU_DEBUG active @@ -653,7 +748,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, struct sw_tx_bd *tx_buf = &fp->tx_buf_ring[idx]; struct eth_tx_bd *tx_bd; struct sk_buff *skb = tx_buf->skb; - u16 bd_idx = tx_buf->first_bd; + u16 bd_idx = TX_BD(tx_buf->first_bd), new_cons; int nbd; DP(BNX2X_MSG_OFF, "pkt_idx %d buff @(%p)->skb %p\n", @@ -666,9 +761,10 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, BD_UNMAP_LEN(tx_bd), PCI_DMA_TODEVICE); nbd = le16_to_cpu(tx_bd->nbd) - 1; + new_cons = nbd + tx_buf->first_bd; #ifdef BNX2X_STOP_ON_ERROR if (nbd > (MAX_SKB_FRAGS + 2)) { - BNX2X_ERR("bad nbd!\n"); + BNX2X_ERR("BAD nbd!\n"); bnx2x_panic(); } #endif @@ -708,32 +804,30 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, tx_buf->first_bd = 0; tx_buf->skb = NULL; - return bd_idx; + return new_cons; } -static inline u32 bnx2x_tx_avail(struct bnx2x_fastpath *fp) +static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp) { - u16 used; - u32 prod; - u32 cons; + s16 used; + u16 prod; + u16 cons; - /* Tell compiler that prod and cons can change */ - barrier(); + barrier(); /* Tell compiler that prod and cons can change */ prod = fp->tx_bd_prod; cons = fp->tx_bd_cons; - used = (NUM_TX_BD - NUM_TX_RINGS + prod - cons + - (cons / TX_DESC_CNT) - (prod / TX_DESC_CNT)); - - if (prod >= cons) { - /* used = prod - cons - prod/size + cons/size */ - used -= NUM_TX_BD - NUM_TX_RINGS; - } + /* NUM_TX_RINGS = number of "next-page" entries + It will be used as a threshold */ + used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS; +#ifdef BNX2X_STOP_ON_ERROR + BUG_TRAP(used >= 0); BUG_TRAP(used <= fp->bp->tx_ring_size); BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL); +#endif - return (fp->bp->tx_ring_size - used); + return (s16)(fp->bp->tx_ring_size) - used; } static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work) @@ -757,10 +851,10 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work) /* prefetch(bp->tx_buf_ring[pkt_cons].skb); */ - DP(NETIF_MSG_TX_DONE, "hw_cons %u sw_cons %u pkt_cons %d\n", + DP(NETIF_MSG_TX_DONE, "hw_cons %u sw_cons %u pkt_cons %u\n", hw_cons, sw_cons, pkt_cons); -/* if (NEXT_TX_IDX(sw_cons) != hw_cons) { +/* if (NEXT_TX_IDX(sw_cons) != hw_cons) { rmb(); prefetch(fp->tx_buf_ring[NEXT_TX_IDX(sw_cons)].skb); } @@ -793,7 +887,6 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work) netif_wake_queue(bp->dev); netif_tx_unlock(bp->dev); - } } @@ -804,13 +897,14 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, int cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data); int command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data); - DP(NETIF_MSG_RX_STATUS, + DP(BNX2X_MSG_SP, "fp %d cid %d got ramrod #%d state is %x type is %d\n", - fp->index, cid, command, bp->state, rr_cqe->ramrod_cqe.type); + FP_IDX(fp), cid, command, bp->state, + rr_cqe->ramrod_cqe.ramrod_type); bp->spq_left++; - if (fp->index) { + if (FP_IDX(fp)) { switch (command | fp->state) { case (RAMROD_CMD_ID_ETH_CLIENT_SETUP | BNX2X_FP_STATE_OPENING): @@ -826,10 +920,11 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, break; default: - BNX2X_ERR("unexpected MC reply(%d) state is %x\n", - command, fp->state); + BNX2X_ERR("unexpected MC reply (%d) " + "fp->state is %x\n", command, fp->state); + break; } - mb(); /* force bnx2x_wait_ramrod to see the change */ + mb(); /* force bnx2x_wait_ramrod() to see the change */ return; } @@ -846,25 +941,25 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, break; case (RAMROD_CMD_ID_ETH_CFC_DEL | BNX2X_STATE_CLOSING_WAIT4_HALT): - DP(NETIF_MSG_IFDOWN, "got delete ramrod for MULTI[%d]\n", - cid); + DP(NETIF_MSG_IFDOWN, "got delete ramrod for MULTI[%d]\n", cid); bnx2x_fp(bp, cid, state) = BNX2X_FP_STATE_CLOSED; break; case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_OPEN): + case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DIAG): DP(NETIF_MSG_IFUP, "got set mac ramrod\n"); break; case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_CLOSING_WAIT4_HALT): - DP(NETIF_MSG_IFUP, "got (un)set mac ramrod\n"); + DP(NETIF_MSG_IFDOWN, "got (un)set mac ramrod\n"); break; default: - BNX2X_ERR("unexpected ramrod (%d) state is %x\n", + BNX2X_ERR("unexpected MC reply (%d) bp->state is %x\n", command, bp->state); + break; } - - mb(); /* force bnx2x_wait_ramrod to see the change */ + mb(); /* force bnx2x_wait_ramrod() to see the change */ } static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, @@ -882,7 +977,6 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size, PCI_DMA_FROMDEVICE); if (unlikely(dma_mapping_error(mapping))) { - dev_kfree_skb(skb); return -ENOMEM; } @@ -924,7 +1018,7 @@ static void bnx2x_reuse_rx_skb(struct bnx2x_fastpath *fp, static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) { struct bnx2x *bp = fp->bp; - u16 bd_cons, bd_prod, comp_ring_cons; + u16 bd_cons, bd_prod, bd_prod_fw, comp_ring_cons; u16 hw_comp_cons, sw_comp_cons, sw_comp_prod; int rx_pkt = 0; @@ -933,12 +1027,15 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) return 0; #endif + /* CQ "next element" is of the size of the regular element, + that's why it's ok here */ hw_comp_cons = le16_to_cpu(*fp->rx_cons_sb); if ((hw_comp_cons & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) hw_comp_cons++; bd_cons = fp->rx_bd_cons; bd_prod = fp->rx_bd_prod; + bd_prod_fw = bd_prod; sw_comp_cons = fp->rx_comp_cons; sw_comp_prod = fp->rx_comp_prod; @@ -949,34 +1046,31 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) DP(NETIF_MSG_RX_STATUS, "queue[%d]: hw_comp_cons %u sw_comp_cons %u\n", - fp->index, hw_comp_cons, sw_comp_cons); + FP_IDX(fp), hw_comp_cons, sw_comp_cons); while (sw_comp_cons != hw_comp_cons) { - unsigned int len, pad; - struct sw_rx_bd *rx_buf; + struct sw_rx_bd *rx_buf = NULL; struct sk_buff *skb; union eth_rx_cqe *cqe; + u8 cqe_fp_flags; + u16 len, pad; comp_ring_cons = RCQ_BD(sw_comp_cons); bd_prod = RX_BD(bd_prod); bd_cons = RX_BD(bd_cons); cqe = &fp->rx_comp_ring[comp_ring_cons]; + cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; - DP(NETIF_MSG_RX_STATUS, "hw_comp_cons %u sw_comp_cons %u" - " comp_ring (%u) bd_ring (%u,%u)\n", - hw_comp_cons, sw_comp_cons, - comp_ring_cons, bd_prod, bd_cons); DP(NETIF_MSG_RX_STATUS, "CQE type %x err %x status %x" - " queue %x vlan %x len %x\n", - cqe->fast_path_cqe.type, - cqe->fast_path_cqe.error_type_flags, - cqe->fast_path_cqe.status_flags, + " queue %x vlan %x len %u\n", CQE_TYPE(cqe_fp_flags), + cqe_fp_flags, cqe->fast_path_cqe.status_flags, cqe->fast_path_cqe.rss_hash_result, - cqe->fast_path_cqe.vlan_tag, cqe->fast_path_cqe.pkt_len); + le16_to_cpu(cqe->fast_path_cqe.vlan_tag), + le16_to_cpu(cqe->fast_path_cqe.pkt_len)); /* is this a slowpath msg? */ - if (unlikely(cqe->fast_path_cqe.type)) { + if (unlikely(CQE_TYPE(cqe_fp_flags))) { bnx2x_sp_event(fp, cqe); goto next_cqe; @@ -984,7 +1078,6 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) } else { rx_buf = &fp->rx_buf_ring[bd_cons]; skb = rx_buf->skb; - len = le16_to_cpu(cqe->fast_path_cqe.pkt_len); pad = cqe->fast_path_cqe.placement_offset; @@ -996,13 +1089,11 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) prefetch(((char *)(skb)) + 128); /* is this an error packet? */ - if (unlikely(cqe->fast_path_cqe.error_type_flags & - ETH_RX_ERROR_FALGS)) { + if (unlikely(cqe_fp_flags & ETH_RX_ERROR_FALGS)) { /* do we sometimes forward error packets anyway? */ DP(NETIF_MSG_RX_ERR, - "ERROR flags(%u) Rx packet(%u)\n", - cqe->fast_path_cqe.error_type_flags, - sw_comp_cons); + "ERROR flags %x rx packet %u\n", + cqe_fp_flags, sw_comp_cons); /* TBD make sure MC counts this as a drop */ goto reuse_rx; } @@ -1018,7 +1109,7 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) len + pad); if (new_skb == NULL) { DP(NETIF_MSG_RX_ERR, - "ERROR packet dropped " + "ERROR packet dropped " "because of alloc failure\n"); /* TBD count this as a drop? */ goto reuse_rx; @@ -1044,7 +1135,7 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) } else { DP(NETIF_MSG_RX_ERR, - "ERROR packet dropped because " + "ERROR packet dropped because " "of alloc failure\n"); reuse_rx: bnx2x_reuse_rx_skb(fp, skb, bd_cons, bd_prod); @@ -1061,14 +1152,14 @@ reuse_rx: } #ifdef BCM_VLAN - if ((le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) - & PARSING_FLAGS_NUMBER_OF_NESTED_VLANS) - && (bp->vlgrp != NULL)) + if ((bp->vlgrp != NULL) && + (le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) & + PARSING_FLAGS_VLAN)) vlan_hwaccel_receive_skb(skb, bp->vlgrp, le16_to_cpu(cqe->fast_path_cqe.vlan_tag)); else #endif - netif_receive_skb(skb); + netif_receive_skb(skb); bp->dev->last_rx = jiffies; @@ -1077,22 +1168,25 @@ next_rx: bd_cons = NEXT_RX_IDX(bd_cons); bd_prod = NEXT_RX_IDX(bd_prod); + bd_prod_fw = NEXT_RX_IDX(bd_prod_fw); + rx_pkt++; next_cqe: sw_comp_prod = NEXT_RCQ_IDX(sw_comp_prod); sw_comp_cons = NEXT_RCQ_IDX(sw_comp_cons); - rx_pkt++; - if ((rx_pkt == budget)) + if (rx_pkt == budget) break; } /* while */ fp->rx_bd_cons = bd_cons; - fp->rx_bd_prod = bd_prod; + fp->rx_bd_prod = bd_prod_fw; fp->rx_comp_cons = sw_comp_cons; fp->rx_comp_prod = sw_comp_prod; REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_RCQ_PROD_OFFSET(bp->port, fp->index), sw_comp_prod); + TSTORM_RX_PRODS_OFFSET(BP_PORT(bp), FP_CL_ID(fp)), + sw_comp_prod); + mmiowb(); /* keep prod updates ordered */ @@ -1107,10 +1201,11 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) struct bnx2x_fastpath *fp = fp_cookie; struct bnx2x *bp = fp->bp; struct net_device *dev = bp->dev; - int index = fp->index; + int index = FP_IDX(fp); - DP(NETIF_MSG_INTR, "got an msix interrupt on [%d]\n", index); - bnx2x_ack_sb(bp, index, USTORM_ID, 0, IGU_INT_DISABLE, 0); + DP(BNX2X_MSG_FP, "got an MSI-X interrupt on IDX:SB [%d:%d]\n", + index, FP_SB_ID(fp)); + bnx2x_ack_sb(bp, FP_SB_ID(fp), USTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) @@ -1123,6 +1218,7 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) prefetch(&fp->status_blk->u_status_block.status_block_index); netif_rx_schedule(dev, &bnx2x_fp(bp, index, napi)); + return IRQ_HANDLED; } @@ -1131,26 +1227,28 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) struct net_device *dev = dev_instance; struct bnx2x *bp = netdev_priv(dev); u16 status = bnx2x_ack_int(bp); + u16 mask; + /* Return here if interrupt is shared and it's not for us */ if (unlikely(status == 0)) { DP(NETIF_MSG_INTR, "not our interrupt!\n"); return IRQ_NONE; } - - DP(NETIF_MSG_INTR, "got an interrupt status is %u\n", status); + DP(NETIF_MSG_INTR, "got an interrupt status %u\n", status); #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) return IRQ_HANDLED; #endif - /* Return here if interrupt is shared and is disabled */ + /* Return here if interrupt is disabled */ if (unlikely(atomic_read(&bp->intr_sem) != 0)) { DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n"); return IRQ_HANDLED; } - if (status & 0x2) { + mask = 0x2 << bp->fp[0].sb_id; + if (status & mask) { struct bnx2x_fastpath *fp = &bp->fp[0]; prefetch(fp->rx_cons_sb); @@ -1160,13 +1258,11 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) netif_rx_schedule(dev, &bnx2x_fp(bp, 0, napi)); - status &= ~0x2; - if (!status) - return IRQ_HANDLED; + status &= ~mask; } - if (unlikely(status & 0x1)) { + if (unlikely(status & 0x1)) { schedule_work(&bp->sp_task); status &= ~0x1; @@ -1174,8 +1270,9 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) return IRQ_HANDLED; } - DP(NETIF_MSG_INTR, "got an unknown interrupt! (status is %u)\n", - status); + if (status) + DP(NETIF_MSG_INTR, "got an unknown interrupt! (status %u)\n", + status); return IRQ_HANDLED; } @@ -1193,7 +1290,7 @@ static int bnx2x_hw_lock(struct bnx2x *bp, u32 resource) { u32 lock_status; u32 resource_bit = (1 << resource); - u8 port = bp->port; + u8 port = BP_PORT(bp); int cnt; /* Validating that the resource is within range */ @@ -1231,7 +1328,7 @@ static int bnx2x_hw_unlock(struct bnx2x *bp, u32 resource) { u32 lock_status; u32 resource_bit = (1 << resource); - u8 port = bp->port; + u8 port = BP_PORT(bp); /* Validating that the resource is within range */ if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { @@ -1258,7 +1355,7 @@ static void bnx2x_phy_hw_lock(struct bnx2x *bp) { u32 ext_phy_type = XGXS_EXT_PHY_TYPE(bp->link_params.ext_phy_config); - mutex_lock(&bp->phy_mutex); + mutex_lock(&bp->port.phy_mutex); if ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) @@ -1273,14 +1370,14 @@ static void bnx2x_phy_hw_unlock(struct bnx2x *bp) (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) bnx2x_hw_unlock(bp, HW_LOCK_RESOURCE_8072_MDIO); - mutex_unlock(&bp->phy_mutex); + mutex_unlock(&bp->port.phy_mutex); } int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode) { /* The GPIO should be swapped if swap register is set and active */ int gpio_port = (REG_RD(bp, NIG_REG_PORT_SWAP) && - REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ bp->port; + REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ BP_PORT(bp); int gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); u32 gpio_mask = (1 << gpio_shift); @@ -1379,18 +1476,18 @@ static void bnx2x_calc_fc_adv(struct bnx2x *bp) { switch (bp->link_vars.ieee_fc) { case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE: - bp->advertising &= ~(ADVERTISED_Asym_Pause | + bp->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH: - bp->advertising |= (ADVERTISED_Asym_Pause | + bp->port.advertising |= (ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC: - bp->advertising |= ADVERTISED_Asym_Pause; + bp->port.advertising |= ADVERTISED_Asym_Pause; break; default: - bp->advertising &= ~(ADVERTISED_Asym_Pause | + bp->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; } @@ -1443,6 +1540,7 @@ static u8 bnx2x_initial_phy_init(struct bnx2x *bp) bnx2x_link_report(bp); bnx2x_calc_fc_adv(bp); + return rc; } @@ -1473,15 +1571,261 @@ static u8 bnx2x_link_test(struct bnx2x *bp) return rc; } +/* Calculates the sum of vn_min_rates. + It's needed for further normalizing of the min_rates. + + Returns: + sum of vn_min_rates + or + 0 - if all the min_rates are 0. + In the later case fainess algorithm should be deactivated. + If not all min_rates are zero then those that are zeroes will + be set to 1. + */ +static u32 bnx2x_calc_vn_wsum(struct bnx2x *bp) +{ + int i, port = BP_PORT(bp); + u32 wsum = 0; + int all_zero = 1; + + for (i = 0; i < E1HVN_MAX; i++) { + u32 vn_cfg = + SHMEM_RD(bp, mf_cfg.func_mf_config[2*i + port].config); + u32 vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> + FUNC_MF_CFG_MIN_BW_SHIFT) * 100; + if (!(vn_cfg & FUNC_MF_CFG_FUNC_HIDE)) { + /* If min rate is zero - set it to 1 */ + if (!vn_min_rate) + vn_min_rate = DEF_MIN_RATE; + else + all_zero = 0; + + wsum += vn_min_rate; + } + } + + /* ... only if all min rates are zeros - disable FAIRNESS */ + if (all_zero) + return 0; + + return wsum; +} + +static void bnx2x_init_port_minmax(struct bnx2x *bp, + int en_fness, + u16 port_rate, + struct cmng_struct_per_port *m_cmng_port) +{ + u32 r_param = port_rate / 8; + int port = BP_PORT(bp); + int i; + + memset(m_cmng_port, 0, sizeof(struct cmng_struct_per_port)); + + /* Enable minmax only if we are in e1hmf mode */ + if (IS_E1HMF(bp)) { + u32 fair_periodic_timeout_usec; + u32 t_fair; + + /* Enable rate shaping and fairness */ + m_cmng_port->flags.cmng_vn_enable = 1; + m_cmng_port->flags.fairness_enable = en_fness ? 1 : 0; + m_cmng_port->flags.rate_shaping_enable = 1; + + if (!en_fness) + DP(NETIF_MSG_IFUP, "All MIN values are zeroes" + " fairness will be disabled\n"); + + /* 100 usec in SDM ticks = 25 since each tick is 4 usec */ + m_cmng_port->rs_vars.rs_periodic_timeout = + RS_PERIODIC_TIMEOUT_USEC / 4; + + /* this is the threshold below which no timer arming will occur + 1.25 coefficient is for the threshold to be a little bigger + than the real time, to compensate for timer in-accuracy */ + m_cmng_port->rs_vars.rs_threshold = + (RS_PERIODIC_TIMEOUT_USEC * r_param * 5) / 4; + + /* resolution of fairness timer */ + fair_periodic_timeout_usec = QM_ARB_BYTES / r_param; + /* for 10G it is 1000usec. for 1G it is 10000usec. */ + t_fair = T_FAIR_COEF / port_rate; + + /* this is the threshold below which we won't arm + the timer anymore */ + m_cmng_port->fair_vars.fair_threshold = QM_ARB_BYTES; + + /* we multiply by 1e3/8 to get bytes/msec. + We don't want the credits to pass a credit + of the T_FAIR*FAIR_MEM (algorithm resolution) */ + m_cmng_port->fair_vars.upper_bound = + r_param * t_fair * FAIR_MEM; + /* since each tick is 4 usec */ + m_cmng_port->fair_vars.fairness_timeout = + fair_periodic_timeout_usec / 4; + + } else { + /* Disable rate shaping and fairness */ + m_cmng_port->flags.cmng_vn_enable = 0; + m_cmng_port->flags.fairness_enable = 0; + m_cmng_port->flags.rate_shaping_enable = 0; + + DP(NETIF_MSG_IFUP, + "Single function mode minmax will be disabled\n"); + } + + /* Store it to internal memory */ + for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) + REG_WR(bp, BAR_XSTRORM_INTMEM + + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i * 4, + ((u32 *)(m_cmng_port))[i]); +} + +static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func, + u32 wsum, u16 port_rate, + struct cmng_struct_per_port *m_cmng_port) +{ + struct rate_shaping_vars_per_vn m_rs_vn; + struct fairness_vars_per_vn m_fair_vn; + u32 vn_cfg = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); + u16 vn_min_rate, vn_max_rate; + int i; + + /* If function is hidden - set min and max to zeroes */ + if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) { + vn_min_rate = 0; + vn_max_rate = 0; + + } else { + vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> + FUNC_MF_CFG_MIN_BW_SHIFT) * 100; + /* If FAIRNESS is enabled (not all min rates are zeroes) and + if current min rate is zero - set it to 1. + This is a requirment of the algorithm. */ + if ((vn_min_rate == 0) && wsum) + vn_min_rate = DEF_MIN_RATE; + vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> + FUNC_MF_CFG_MAX_BW_SHIFT) * 100; + } + + DP(NETIF_MSG_IFUP, "func %d: vn_min_rate=%d vn_max_rate=%d " + "wsum=%d\n", func, vn_min_rate, vn_max_rate, wsum); + + memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn)); + memset(&m_fair_vn, 0, sizeof(struct fairness_vars_per_vn)); + + /* global vn counter - maximal Mbps for this vn */ + m_rs_vn.vn_counter.rate = vn_max_rate; + + /* quota - number of bytes transmitted in this period */ + m_rs_vn.vn_counter.quota = + (vn_max_rate * RS_PERIODIC_TIMEOUT_USEC) / 8; + +#ifdef BNX2X_PER_PROT_QOS + /* per protocol counter */ + for (protocol = 0; protocol < NUM_OF_PROTOCOLS; protocol++) { + /* maximal Mbps for this protocol */ + m_rs_vn.protocol_counters[protocol].rate = + protocol_max_rate[protocol]; + /* the quota in each timer period - + number of bytes transmitted in this period */ + m_rs_vn.protocol_counters[protocol].quota = + (u32)(rs_periodic_timeout_usec * + ((double)m_rs_vn. + protocol_counters[protocol].rate/8)); + } +#endif + + if (wsum) { + /* credit for each period of the fairness algorithm: + number of bytes in T_FAIR (the vn share the port rate). + wsum should not be larger than 10000, thus + T_FAIR_COEF / (8 * wsum) will always be grater than zero */ + m_fair_vn.vn_credit_delta = + max((u64)(vn_min_rate * (T_FAIR_COEF / (8 * wsum))), + (u64)(m_cmng_port->fair_vars.fair_threshold * 2)); + DP(NETIF_MSG_IFUP, "m_fair_vn.vn_credit_delta=%d\n", + m_fair_vn.vn_credit_delta); + } + +#ifdef BNX2X_PER_PROT_QOS + do { + u32 protocolWeightSum = 0; + + for (protocol = 0; protocol < NUM_OF_PROTOCOLS; protocol++) + protocolWeightSum += + drvInit.protocol_min_rate[protocol]; + /* per protocol counter - + NOT NEEDED IF NO PER-PROTOCOL CONGESTION MANAGEMENT */ + if (protocolWeightSum > 0) { + for (protocol = 0; + protocol < NUM_OF_PROTOCOLS; protocol++) + /* credit for each period of the + fairness algorithm - number of bytes in + T_FAIR (the protocol share the vn rate) */ + m_fair_vn.protocol_credit_delta[protocol] = + (u32)((vn_min_rate / 8) * t_fair * + protocol_min_rate / protocolWeightSum); + } + } while (0); +#endif + + /* Store it to internal memory */ + for (i = 0; i < sizeof(struct rate_shaping_vars_per_vn)/4; i++) + REG_WR(bp, BAR_XSTRORM_INTMEM + + XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(func) + i * 4, + ((u32 *)(&m_rs_vn))[i]); + + for (i = 0; i < sizeof(struct fairness_vars_per_vn)/4; i++) + REG_WR(bp, BAR_XSTRORM_INTMEM + + XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func) + i * 4, + ((u32 *)(&m_fair_vn))[i]); +} + /* This function is called upon link interrupt */ static void bnx2x_link_attn(struct bnx2x *bp) { + int vn; + bnx2x_phy_hw_lock(bp); bnx2x_link_update(&bp->link_params, &bp->link_vars); bnx2x_phy_hw_unlock(bp); /* indicate link status */ bnx2x_link_report(bp); + + if (IS_E1HMF(bp)) { + int func; + + for (vn = VN_0; vn < E1HVN_MAX; vn++) { + if (vn == BP_E1HVN(bp)) + continue; + + func = ((vn << 1) | BP_PORT(bp)); + + /* Set the attention towards other drivers + on the same port */ + REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_0 + + (LINK_SYNC_ATTENTION_BIT_FUNC_0 + func)*4, 1); + } + } + + if (CHIP_IS_E1H(bp) && (bp->link_vars.line_speed > 0)) { + struct cmng_struct_per_port m_cmng_port; + u32 wsum; + int port = BP_PORT(bp); + + /* Init RATE SHAPING and FAIRNESS contexts */ + wsum = bnx2x_calc_vn_wsum(bp); + bnx2x_init_port_minmax(bp, (int)wsum, + bp->link_vars.line_speed, + &m_cmng_port); + if (IS_E1HMF(bp)) + for (vn = VN_0; vn < E1HVN_MAX; vn++) + bnx2x_init_vn_minmax(bp, 2*vn + port, + wsum, bp->link_vars.line_speed, + &m_cmng_port); + } } static void bnx2x__link_status_update(struct bnx2x *bp) @@ -1495,6 +1839,20 @@ static void bnx2x__link_status_update(struct bnx2x *bp) bnx2x_link_report(bp); } +static void bnx2x_pmf_update(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + u32 val; + + bp->port.pmf = 1; + DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf); + + /* enable nig attention */ + val = (0xff0f | (1 << (BP_E1HVN(bp) + 4))); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val); + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val); +} + /* end of Link */ /* slow path */ @@ -1507,10 +1865,10 @@ static void bnx2x__link_status_update(struct bnx2x *bp) static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, u32 data_hi, u32 data_lo, int common) { - int port = bp->port; + int func = BP_FUNC(bp); - DP(NETIF_MSG_TIMER, - "spe (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n", + DP(BNX2X_MSG_SP/*NETIF_MSG_TIMER*/, + "SPQE (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n", (u32)U64_HI(bp->spq_mapping), (u32)(U64_LO(bp->spq_mapping) + (void *)bp->spq_prod_bd - (void *)bp->spq), command, HW_CID(bp, cid), data_hi, data_lo, bp->spq_left); @@ -1520,11 +1878,11 @@ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, return -EIO; #endif - spin_lock(&bp->spq_lock); + spin_lock_bh(&bp->spq_lock); if (!bp->spq_left) { BNX2X_ERR("BUG! SPQ ring full!\n"); - spin_unlock(&bp->spq_lock); + spin_unlock_bh(&bp->spq_lock); bnx2x_panic(); return -EBUSY; } @@ -1553,18 +1911,18 @@ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, bp->spq_prod_idx++; } - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(port), + REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func), bp->spq_prod_idx); - spin_unlock(&bp->spq_lock); + spin_unlock_bh(&bp->spq_lock); return 0; } /* acquire split MCP access lock register */ static int bnx2x_lock_alr(struct bnx2x *bp) { - int rc = 0; u32 i, j, val; + int rc = 0; might_sleep(); i = 100; @@ -1577,10 +1935,8 @@ static int bnx2x_lock_alr(struct bnx2x *bp) msleep(5); } - if (!(val & (1L << 31))) { BNX2X_ERR("Cannot acquire nvram interface\n"); - rc = -EBUSY; } @@ -1631,8 +1987,9 @@ static inline u16 bnx2x_update_dsb_idx(struct bnx2x *bp) static void bnx2x_attn_int_asserted(struct bnx2x *bp, u32 asserted) { - int port = bp->port; - u32 igu_addr = (IGU_ADDR_ATTN_BITS_SET + IGU_PORT_BASE * port) * 8; + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + u32 igu_addr = (IGU_ADDR_ATTN_BITS_SET + IGU_FUNC_BASE * func) * 8; u32 aeu_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; u32 nig_int_mask_addr = port ? NIG_REG_MASK_INTERRUPT_PORT1 : @@ -1716,14 +2073,14 @@ static void bnx2x_attn_int_asserted(struct bnx2x *bp, u32 asserted) static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) { - int port = bp->port; + int port = BP_PORT(bp); int reg_offset; u32 val; - if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) { + reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : + MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); - reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : - MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); + if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) { val = REG_RD(bp, reg_offset); val &= ~AEU_INPUTS_ATTN_BITS_SPIO5; @@ -1731,7 +2088,7 @@ static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) BNX2X_ERR("SPIO5 hw attention\n"); - switch (bp->board & SHARED_HW_CFG_BOARD_TYPE_MASK) { + switch (bp->common.board & SHARED_HW_CFG_BOARD_TYPE_MASK) { case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: /* Fan failure attention */ @@ -1762,6 +2119,17 @@ static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) break; } } + + if (attn & HW_INTERRUT_ASSERT_SET_0) { + + val = REG_RD(bp, reg_offset); + val &= ~(attn & HW_INTERRUT_ASSERT_SET_0); + REG_WR(bp, reg_offset, val); + + BNX2X_ERR("FATAL HW block attention set0 0x%x\n", + (attn & HW_INTERRUT_ASSERT_SET_0)); + bnx2x_panic(); + } } static inline void bnx2x_attn_int_deasserted1(struct bnx2x *bp, u32 attn) @@ -1776,6 +2144,23 @@ static inline void bnx2x_attn_int_deasserted1(struct bnx2x *bp, u32 attn) if (val & 0x2) BNX2X_ERR("FATAL error from DORQ\n"); } + + if (attn & HW_INTERRUT_ASSERT_SET_1) { + + int port = BP_PORT(bp); + int reg_offset; + + reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_1 : + MISC_REG_AEU_ENABLE1_FUNC_0_OUT_1); + + val = REG_RD(bp, reg_offset); + val &= ~(attn & HW_INTERRUT_ASSERT_SET_1); + REG_WR(bp, reg_offset, val); + + BNX2X_ERR("FATAL HW block attention set1 0x%x\n", + (attn & HW_INTERRUT_ASSERT_SET_1)); + bnx2x_panic(); + } } static inline void bnx2x_attn_int_deasserted2(struct bnx2x *bp, u32 attn) @@ -1799,13 +2184,41 @@ static inline void bnx2x_attn_int_deasserted2(struct bnx2x *bp, u32 attn) if (val & 0x18000) BNX2X_ERR("FATAL error from PXP\n"); } + + if (attn & HW_INTERRUT_ASSERT_SET_2) { + + int port = BP_PORT(bp); + int reg_offset; + + reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_2 : + MISC_REG_AEU_ENABLE1_FUNC_0_OUT_2); + + val = REG_RD(bp, reg_offset); + val &= ~(attn & HW_INTERRUT_ASSERT_SET_2); + REG_WR(bp, reg_offset, val); + + BNX2X_ERR("FATAL HW block attention set2 0x%x\n", + (attn & HW_INTERRUT_ASSERT_SET_2)); + bnx2x_panic(); + } } static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn) { + u32 val; + if (attn & EVEREST_GEN_ATTN_IN_USE_MASK) { - if (attn & BNX2X_MC_ASSERT_BITS) { + if (attn & BNX2X_PMF_LINK_ASSERT) { + int func = BP_FUNC(bp); + + REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0); + bnx2x__link_status_update(bp); + if (SHMEM_RD(bp, func_mb[func].drv_status) & + DRV_STATUS_PMF) + bnx2x_pmf_update(bp); + + } else if (attn & BNX2X_MC_ASSERT_BITS) { BNX2X_ERR("MC assert!\n"); REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_10, 0); @@ -1818,16 +2231,25 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn) BNX2X_ERR("MCP assert!\n"); REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_11, 0); - bnx2x_mc_assert(bp); + bnx2x_fw_dump(bp); } else BNX2X_ERR("Unknown HW assert! (attn 0x%x)\n", attn); } if (attn & EVEREST_LATCHED_ATTN_IN_USE_MASK) { - + BNX2X_ERR("LATCHED attention 0x%08x (masked)\n", attn); + if (attn & BNX2X_GRC_TIMEOUT) { + val = CHIP_IS_E1H(bp) ? + REG_RD(bp, MISC_REG_GRC_TIMEOUT_ATTN) : 0; + BNX2X_ERR("GRC time-out 0x%08x\n", val); + } + if (attn & BNX2X_GRC_RSV) { + val = CHIP_IS_E1H(bp) ? + REG_RD(bp, MISC_REG_GRC_RSV_ATTN) : 0; + BNX2X_ERR("GRC reserved 0x%08x\n", val); + } REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x7ff); - BNX2X_ERR("LATCHED attention 0x%x (masked)\n", attn); } } @@ -1835,7 +2257,7 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) { struct attn_route attn; struct attn_route group_mask; - int port = bp->port; + int port = BP_PORT(bp); int index; u32 reg_addr; u32 val; @@ -1848,14 +2270,16 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) attn.sig[1] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 + port*4); attn.sig[2] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 + port*4); attn.sig[3] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 + port*4); - DP(NETIF_MSG_HW, "attn %llx\n", (unsigned long long)attn.sig[0]); + DP(NETIF_MSG_HW, "attn: %08x %08x %08x %08x\n", + attn.sig[0], attn.sig[1], attn.sig[2], attn.sig[3]); for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { if (deasserted & (1 << index)) { group_mask = bp->attn_group[index]; - DP(NETIF_MSG_HW, "group[%d]: %llx\n", index, - (unsigned long long)group_mask.sig[0]); + DP(NETIF_MSG_HW, "group[%d]: %08x %08x %08x %08x\n", + index, group_mask.sig[0], group_mask.sig[1], + group_mask.sig[2], group_mask.sig[3]); bnx2x_attn_int_deasserted3(bp, attn.sig[3] & group_mask.sig[3]); @@ -1866,22 +2290,6 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) bnx2x_attn_int_deasserted0(bp, attn.sig[0] & group_mask.sig[0]); - if ((attn.sig[0] & group_mask.sig[0] & - HW_INTERRUT_ASSERT_SET_0) || - (attn.sig[1] & group_mask.sig[1] & - HW_INTERRUT_ASSERT_SET_1) || - (attn.sig[2] & group_mask.sig[2] & - HW_INTERRUT_ASSERT_SET_2)) - BNX2X_ERR("FATAL HW block attention" - " set0 0x%x set1 0x%x" - " set2 0x%x\n", - (attn.sig[0] & group_mask.sig[0] & - HW_INTERRUT_ASSERT_SET_0), - (attn.sig[1] & group_mask.sig[1] & - HW_INTERRUT_ASSERT_SET_1), - (attn.sig[2] & group_mask.sig[2] & - HW_INTERRUT_ASSERT_SET_2)); - if ((attn.sig[0] & group_mask.sig[0] & HW_PRTY_ASSERT_SET_0) || (attn.sig[1] & group_mask.sig[1] & @@ -1894,17 +2302,17 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) bnx2x_unlock_alr(bp); - reg_addr = (IGU_ADDR_ATTN_BITS_CLR + IGU_PORT_BASE * port) * 8; + reg_addr = (IGU_ADDR_ATTN_BITS_CLR + IGU_FUNC_BASE * BP_FUNC(bp)) * 8; val = ~deasserted; -/* DP(NETIF_MSG_INTR, "write 0x%08x to IGU addr 0x%x\n", +/* DP(NETIF_MSG_INTR, "write 0x%08x to IGU addr 0x%x\n", val, BAR_IGU_INTMEM + reg_addr); */ REG_WR(bp, BAR_IGU_INTMEM + reg_addr, val); if (bp->aeu_mask & (deasserted & 0xff)) - BNX2X_ERR("IGU BUG\n"); + BNX2X_ERR("IGU BUG!\n"); if (~bp->attn_state & deasserted) - BNX2X_ERR("IGU BUG\n"); + BNX2X_ERR("IGU BUG!\n"); reg_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; @@ -1936,7 +2344,7 @@ static void bnx2x_attn_int(struct bnx2x *bp) attn_bits, attn_ack, asserted, deasserted); if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state)) - BNX2X_ERR("bad attention state\n"); + BNX2X_ERR("BAD attention state\n"); /* handle bits that were raised */ if (asserted) @@ -1951,6 +2359,7 @@ static void bnx2x_sp_task(struct work_struct *work) struct bnx2x *bp = container_of(work, struct bnx2x, sp_task); u16 status; + /* Return here if interrupt is disabled */ if (unlikely(atomic_read(&bp->intr_sem) != 0)) { DP(BNX2X_MSG_SP, "called but intr_sem not 0, returning\n"); @@ -1958,19 +2367,15 @@ static void bnx2x_sp_task(struct work_struct *work) } status = bnx2x_update_dsb_idx(bp); - if (status == 0) - BNX2X_ERR("spurious slowpath interrupt!\n"); +/* if (status == 0) */ +/* BNX2X_ERR("spurious slowpath interrupt!\n"); */ - DP(NETIF_MSG_INTR, "got a slowpath interrupt (updated %x)\n", status); + DP(BNX2X_MSG_SP, "got a slowpath interrupt (updated %x)\n", status); /* HW attentions */ if (status & 0x1) bnx2x_attn_int(bp); - /* CStorm events: query_stats, port delete ramrod */ - if (status & 0x2) - bp->stat_pending = 0; - bnx2x_ack_sb(bp, DEF_SB_ID, ATTENTION_ID, bp->def_att_idx, IGU_INT_NOP, 1); bnx2x_ack_sb(bp, DEF_SB_ID, USTORM_ID, le16_to_cpu(bp->def_u_idx), @@ -2109,13 +2514,13 @@ static inline long bnx2x_hilo(u32 *hiref) static void bnx2x_init_mac_stats(struct bnx2x *bp) { struct dmae_command *dmae; - int port = bp->port; + int port = BP_PORT(bp); int loader_idx = port * 8; u32 opcode; u32 mac_addr; bp->executer_idx = 0; - if (bp->fw_mb) { + if (bp->func_stx) { /* MCP */ opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | @@ -2135,7 +2540,7 @@ static void bnx2x_init_mac_stats(struct bnx2x *bp) sizeof(u32)); dmae->src_addr_hi = U64_HI(bnx2x_sp_mapping(bp, eth_stats) + sizeof(u32)); - dmae->dst_addr_lo = bp->fw_mb >> 2; + dmae->dst_addr_lo = bp->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = (offsetof(struct bnx2x_eth_stats, mac_stx_end) - sizeof(u32)) >> 2; @@ -2280,7 +2685,7 @@ static void bnx2x_init_mac_stats(struct bnx2x *bp) static void bnx2x_init_stats(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); bp->stats_state = STATS_STATE_DISABLE; bp->executer_idx = 0; @@ -2641,8 +3046,6 @@ static void bnx2x_update_net_stats(struct bnx2x *bp) static void bnx2x_update_stats(struct bnx2x *bp) { - int i; - if (!bnx2x_update_storm_stats(bp)) { if (bp->link_vars.mac_type == MAC_TYPE_BMAC) { @@ -2662,6 +3065,7 @@ static void bnx2x_update_stats(struct bnx2x *bp) if (bp->msglevel & NETIF_MSG_TIMER) { struct bnx2x_eth_stats *estats = bnx2x_sp(bp, eth_stats); struct net_device_stats *nstats = &bp->dev->stats; + int i; printk(KERN_DEBUG "%s:\n", bp->dev->name); printk(KERN_DEBUG " tx avail (%4x) tx hc idx (%x)" @@ -2707,7 +3111,7 @@ static void bnx2x_update_stats(struct bnx2x *bp) /* loader */ if (bp->executer_idx) { struct dmae_command *dmae = &bp->dmae; - int port = bp->port; + int port = BP_PORT(bp); int loader_idx = port * 8; memset(dmae, 0, sizeof(struct dmae_command)); @@ -2766,8 +3170,8 @@ static void bnx2x_timer(unsigned long data) rc = bnx2x_rx_int(fp, 1000); } - if (!nomcp) { - int port = bp->port; + if (!BP_NOMCP(bp)) { + int func = BP_FUNC(bp); u32 drv_pulse; u32 mcp_pulse; @@ -2775,9 +3179,9 @@ static void bnx2x_timer(unsigned long data) bp->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK; /* TBD - add SYSTEM_TIME */ drv_pulse = bp->fw_drv_pulse_wr_seq; - SHMEM_WR(bp, func_mb[port].drv_pulse_mb, drv_pulse); + SHMEM_WR(bp, func_mb[func].drv_pulse_mb, drv_pulse); - mcp_pulse = (SHMEM_RD(bp, func_mb[port].mcp_pulse_mb) & + mcp_pulse = (SHMEM_RD(bp, func_mb[func].mcp_pulse_mb) & MCP_PULSE_SEQ_MASK); /* The delta between driver pulse and mcp response * should be 1 (before mcp response) or 0 (after mcp response) @@ -2807,58 +3211,89 @@ timer_restart: * nic init service functions */ -static void bnx2x_init_sb(struct bnx2x *bp, struct host_status_block *sb, - dma_addr_t mapping, int id) +static void bnx2x_zero_sb(struct bnx2x *bp, int sb_id) { - int port = bp->port; - u64 section; + int port = BP_PORT(bp); + + bnx2x_init_fill(bp, BAR_USTRORM_INTMEM + + USTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, sb_id), 0, + sizeof(struct ustorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_CSTRORM_INTMEM + + CSTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, sb_id), 0, + sizeof(struct cstorm_def_status_block)/4); +} + +static void bnx2x_init_sb(struct bnx2x *bp, int sb_id, + struct host_status_block *sb, dma_addr_t mapping) +{ + int port = BP_PORT(bp); int index; + u64 section; /* USTORM */ section = ((u64)mapping) + offsetof(struct host_status_block, u_status_block); - sb->u_status_block.status_block_id = id; + sb->u_status_block.status_block_id = sb_id; REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HOST_SB_ADDR_OFFSET(port, id), U64_LO(section)); + USTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id), U64_LO(section)); REG_WR(bp, BAR_USTRORM_INTMEM + - ((USTORM_SB_HOST_SB_ADDR_OFFSET(port, id)) + 4), + ((USTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id)) + 4), U64_HI(section)); for (index = 0; index < HC_USTORM_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HC_DISABLE_OFFSET(port, id, index), 0x1); + USTORM_SB_HC_DISABLE_OFFSET(port, sb_id, index), 1); /* CSTORM */ section = ((u64)mapping) + offsetof(struct host_status_block, c_status_block); - sb->c_status_block.status_block_id = id; + sb->c_status_block.status_block_id = sb_id; REG_WR(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HOST_SB_ADDR_OFFSET(port, id), U64_LO(section)); + CSTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id), U64_LO(section)); REG_WR(bp, BAR_CSTRORM_INTMEM + - ((CSTORM_SB_HOST_SB_ADDR_OFFSET(port, id)) + 4), + ((CSTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id)) + 4), U64_HI(section)); for (index = 0; index < HC_CSTORM_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HC_DISABLE_OFFSET(port, id, index), 0x1); + CSTORM_SB_HC_DISABLE_OFFSET(port, sb_id, index), 1); + + bnx2x_ack_sb(bp, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); +} + +static void bnx2x_zero_def_sb(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); - bnx2x_ack_sb(bp, id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_init_fill(bp, BAR_USTRORM_INTMEM + + USTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct ustorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_CSTRORM_INTMEM + + CSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct cstorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_XSTRORM_INTMEM + + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct xstorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_TSTRORM_INTMEM + + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct tstorm_def_status_block)/4); } static void bnx2x_init_def_sb(struct bnx2x *bp, struct host_def_status_block *def_sb, - dma_addr_t mapping, int id) + dma_addr_t mapping, int sb_id) { - int port = bp->port; + int port = BP_PORT(bp); + int func = BP_FUNC(bp); int index, val, reg_offset; u64 section; /* ATTN */ section = ((u64)mapping) + offsetof(struct host_def_status_block, atten_status_block); - def_sb->atten_status_block.status_block_id = id; + def_sb->atten_status_block.status_block_id = sb_id; bp->def_att_idx = 0; bp->attn_state = 0; @@ -2866,7 +3301,7 @@ static void bnx2x_init_def_sb(struct bnx2x *bp, reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); - for (index = 0; index < 3; index++) { + for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { bp->attn_group[index].sig[0] = REG_RD(bp, reg_offset + 0x10*index); bp->attn_group[index].sig[1] = REG_RD(bp, @@ -2889,116 +3324,123 @@ static void bnx2x_init_def_sb(struct bnx2x *bp, reg_offset = (port ? HC_REG_ATTN_NUM_P1 : HC_REG_ATTN_NUM_P0); val = REG_RD(bp, reg_offset); - val |= id; + val |= sb_id; REG_WR(bp, reg_offset, val); /* USTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, u_def_status_block); - def_sb->u_def_status_block.status_block_id = id; + def_sb->u_def_status_block.status_block_id = sb_id; bp->def_u_idx = 0; REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_USTRORM_INTMEM + - ((USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_USTRORM_INTMEM + USTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_USTRORM_INTMEM + DEF_USB_FUNC_OFF + + USTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_USTRORM_INTMEM + USTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_USTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_USTRORM_INTMEM + - USTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); + USTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* CSTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, c_def_status_block); - def_sb->c_def_status_block.status_block_id = id; + def_sb->c_def_status_block.status_block_id = sb_id; bp->def_c_idx = 0; REG_WR(bp, BAR_CSTRORM_INTMEM + - CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_CSTRORM_INTMEM + - ((CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_CSTRORM_INTMEM + DEF_CSB_FUNC_OFF + + CSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_CSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_CSTRORM_INTMEM + - CSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); + CSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* TSTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, t_def_status_block); - def_sb->t_def_status_block.status_block_id = id; + def_sb->t_def_status_block.status_block_id = sb_id; bp->def_t_idx = 0; REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_TSTRORM_INTMEM + - ((TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_TSTRORM_INTMEM + TSTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_TSTRORM_INTMEM + DEF_TSB_FUNC_OFF + + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_TSTRORM_INTMEM + TSTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_TSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_TSTRORM_INTMEM + - TSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); + TSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* XSTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, x_def_status_block); - def_sb->x_def_status_block.status_block_id = id; + def_sb->x_def_status_block.status_block_id = sb_id; bp->def_x_idx = 0; REG_WR(bp, BAR_XSTRORM_INTMEM + - XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_XSTRORM_INTMEM + - ((XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_XSTRORM_INTMEM + DEF_XSB_FUNC_OFF + + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_XSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_XSTRORM_INTMEM + - XSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); - - bp->stat_pending = 0; + XSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); - bnx2x_ack_sb(bp, id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_ack_sb(bp, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); } static void bnx2x_update_coalesce(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int i; for_each_queue(bp, i) { + int sb_id = bp->fp[i].sb_id; /* HC_INDEX_U_ETH_RX_CQ_CONS */ REG_WR8(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HC_TIMEOUT_OFFSET(port, i, + USTORM_SB_HC_TIMEOUT_OFFSET(port, sb_id, HC_INDEX_U_ETH_RX_CQ_CONS), - bp->rx_ticks_int/12); + bp->rx_ticks/12); REG_WR16(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HC_DISABLE_OFFSET(port, i, + USTORM_SB_HC_DISABLE_OFFSET(port, sb_id, HC_INDEX_U_ETH_RX_CQ_CONS), - bp->rx_ticks_int ? 0 : 1); + bp->rx_ticks ? 0 : 1); /* HC_INDEX_C_ETH_TX_CQ_CONS */ REG_WR8(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HC_TIMEOUT_OFFSET(port, i, + CSTORM_SB_HC_TIMEOUT_OFFSET(port, sb_id, HC_INDEX_C_ETH_TX_CQ_CONS), - bp->tx_ticks_int/12); + bp->tx_ticks/12); REG_WR16(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HC_DISABLE_OFFSET(port, i, + CSTORM_SB_HC_DISABLE_OFFSET(port, sb_id, HC_INDEX_C_ETH_TX_CQ_CONS), - bp->tx_ticks_int ? 0 : 1); + bp->tx_ticks ? 0 : 1); } } @@ -3006,7 +3448,6 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) { u16 ring_prod; int i, j; - int port = bp->port; bp->rx_buf_use_size = bp->dev->mtu; @@ -3025,13 +3466,13 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) rx_bd = &fp->rx_desc_ring[RX_DESC_CNT * i - 2]; rx_bd->addr_hi = cpu_to_le32(U64_HI(fp->rx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); + BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); rx_bd->addr_lo = cpu_to_le32(U64_LO(fp->rx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); - + BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); } + /* CQ ring */ for (i = 1; i <= NUM_RCQ_RINGS; i++) { struct eth_rx_cqe_next_page *nextpg; @@ -3039,10 +3480,10 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) &fp->rx_comp_ring[RCQ_DESC_CNT * i - 1]; nextpg->addr_hi = cpu_to_le32(U64_HI(fp->rx_comp_mapping + - BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); + BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); nextpg->addr_lo = cpu_to_le32(U64_LO(fp->rx_comp_mapping + - BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); + BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); } /* rx completion queue */ @@ -3064,15 +3505,16 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) /* Warning! this will generate an interrupt (to the TSTORM) */ /* must only be done when chip is initialized */ REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_RCQ_PROD_OFFSET(port, j), ring_prod); + TSTORM_RX_PRODS_OFFSET(BP_PORT(bp), FP_CL_ID(fp)), + ring_prod); if (j != 0) continue; REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(port), + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(BP_PORT(bp)), U64_LO(fp->rx_comp_mapping)); REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(port) + 4, + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(BP_PORT(bp)) + 4, U64_HI(fp->rx_comp_mapping)); } } @@ -3090,10 +3532,10 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp) tx_bd->addr_hi = cpu_to_le32(U64_HI(fp->tx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); + BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); tx_bd->addr_lo = cpu_to_le32(U64_LO(fp->tx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); + BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); } fp->tx_pkt_prod = 0; @@ -3107,7 +3549,7 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp) static void bnx2x_init_sp_ring(struct bnx2x *bp) { - int port = bp->port; + int func = BP_FUNC(bp); spin_lock_init(&bp->spq_lock); @@ -3117,12 +3559,13 @@ static void bnx2x_init_sp_ring(struct bnx2x *bp) bp->spq_prod_bd = bp->spq; bp->spq_last_bd = bp->spq_prod_bd + MAX_SP_DESC_CNT; - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PAGE_BASE_OFFSET(port), + REG_WR(bp, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func), U64_LO(bp->spq_mapping)); - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PAGE_BASE_OFFSET(port) + 4, + REG_WR(bp, + XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func) + 4, U64_HI(bp->spq_mapping)); - REG_WR(bp, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PROD_OFFSET(port), + REG_WR(bp, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PROD_OFFSET(func), bp->spq_prod_idx); } @@ -3133,6 +3576,7 @@ static void bnx2x_init_context(struct bnx2x *bp) for_each_queue(bp, i) { struct eth_context *context = bnx2x_sp(bp, context[i].eth); struct bnx2x_fastpath *fp = &bp->fp[i]; + u8 sb_id = FP_SB_ID(fp); context->xstorm_st_context.tx_bd_page_base_hi = U64_HI(fp->tx_desc_mapping); @@ -3142,26 +3586,25 @@ static void bnx2x_init_context(struct bnx2x *bp) U64_HI(fp->tx_prods_mapping); context->xstorm_st_context.db_data_addr_lo = U64_LO(fp->tx_prods_mapping); - - context->ustorm_st_context.rx_bd_page_base_hi = + context->xstorm_st_context.statistics_data = (BP_CL_ID(bp) | + XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE); + + context->ustorm_st_context.common.sb_index_numbers = + BNX2X_RX_SB_INDEX_NUM; + context->ustorm_st_context.common.clientId = FP_CL_ID(fp); + context->ustorm_st_context.common.status_block_id = sb_id; + context->ustorm_st_context.common.flags = + USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT; + context->ustorm_st_context.common.mc_alignment_size = 64; + context->ustorm_st_context.common.bd_buff_size = + bp->rx_buf_use_size; + context->ustorm_st_context.common.bd_page_base_hi = U64_HI(fp->rx_desc_mapping); - context->ustorm_st_context.rx_bd_page_base_lo = + context->ustorm_st_context.common.bd_page_base_lo = U64_LO(fp->rx_desc_mapping); - context->ustorm_st_context.status_block_id = i; - context->ustorm_st_context.sb_index_number = - HC_INDEX_U_ETH_RX_CQ_CONS; - context->ustorm_st_context.rcq_base_address_hi = - U64_HI(fp->rx_comp_mapping); - context->ustorm_st_context.rcq_base_address_lo = - U64_LO(fp->rx_comp_mapping); - context->ustorm_st_context.flags = - USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT; - context->ustorm_st_context.mc_alignment_size = 64; - context->ustorm_st_context.num_rss = bp->num_queues; - context->cstorm_st_context.sb_index_number = HC_INDEX_C_ETH_TX_CQ_CONS; - context->cstorm_st_context.status_block_id = i; + context->cstorm_st_context.status_block_id = sb_id; context->xstorm_ag_context.cdu_reserved = CDU_RSRVD_VALUE_TYPE_A(HW_CID(bp, i), @@ -3176,14 +3619,16 @@ static void bnx2x_init_context(struct bnx2x *bp) static void bnx2x_init_ind_table(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int i; if (!is_multi(bp)) return; + DP(NETIF_MSG_IFUP, "Initializing indirection table\n"); for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++) - REG_WR8(bp, TSTORM_INDIRECTION_TABLE_OFFSET(port) + i, + REG_WR8(bp, BAR_TSTRORM_INTMEM + + TSTORM_INDIRECTION_TABLE_OFFSET(port) + i, i % bp->num_queues); REG_WR(bp, PRS_REG_A_PRSU_20, 0xf); @@ -3191,77 +3636,74 @@ static void bnx2x_init_ind_table(struct bnx2x *bp) static void bnx2x_set_client_config(struct bnx2x *bp) { -#ifdef BCM_VLAN - int mode = bp->rx_mode; -#endif - int i, port = bp->port; struct tstorm_eth_client_config tstorm_client = {0}; + int port = BP_PORT(bp); + int i; - tstorm_client.mtu = bp->dev->mtu; + tstorm_client.mtu = bp->dev->mtu + ETH_OVREHEAD; tstorm_client.statistics_counter_id = 0; tstorm_client.config_flags = TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE; #ifdef BCM_VLAN - if (mode && bp->vlgrp) { + if (bp->rx_mode && bp->vlgrp) { tstorm_client.config_flags |= TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE; DP(NETIF_MSG_IFUP, "vlan removal enabled\n"); } #endif - if (mode != BNX2X_RX_MODE_PROMISC) - tstorm_client.drop_flags = - TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR; for_each_queue(bp, i) { REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_CLIENT_CONFIG_OFFSET(port, i), + TSTORM_CLIENT_CONFIG_OFFSET(port, bp->fp[i].cl_id), ((u32 *)&tstorm_client)[0]); REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_CLIENT_CONFIG_OFFSET(port, i) + 4, + TSTORM_CLIENT_CONFIG_OFFSET(port, bp->fp[i].cl_id) + 4, ((u32 *)&tstorm_client)[1]); } -/* DP(NETIF_MSG_IFUP, "tstorm_client: 0x%08x 0x%08x\n", - ((u32 *)&tstorm_client)[0], ((u32 *)&tstorm_client)[1]); */ + DP(BNX2X_MSG_OFF, "tstorm_client: 0x%08x 0x%08x\n", + ((u32 *)&tstorm_client)[0], ((u32 *)&tstorm_client)[1]); } static void bnx2x_set_storm_rx_mode(struct bnx2x *bp) { - int mode = bp->rx_mode; - int port = bp->port; struct tstorm_eth_mac_filter_config tstorm_mac_filter = {0}; + int mode = bp->rx_mode; + int mask = (1 << BP_L_ID(bp)); + int func = BP_FUNC(bp); int i; DP(NETIF_MSG_RX_STATUS, "rx mode is %d\n", mode); switch (mode) { case BNX2X_RX_MODE_NONE: /* no Rx */ - tstorm_mac_filter.ucast_drop_all = 1; - tstorm_mac_filter.mcast_drop_all = 1; - tstorm_mac_filter.bcast_drop_all = 1; + tstorm_mac_filter.ucast_drop_all = mask; + tstorm_mac_filter.mcast_drop_all = mask; + tstorm_mac_filter.bcast_drop_all = mask; break; case BNX2X_RX_MODE_NORMAL: - tstorm_mac_filter.bcast_accept_all = 1; + tstorm_mac_filter.bcast_accept_all = mask; break; case BNX2X_RX_MODE_ALLMULTI: - tstorm_mac_filter.mcast_accept_all = 1; - tstorm_mac_filter.bcast_accept_all = 1; + tstorm_mac_filter.mcast_accept_all = mask; + tstorm_mac_filter.bcast_accept_all = mask; break; case BNX2X_RX_MODE_PROMISC: - tstorm_mac_filter.ucast_accept_all = 1; - tstorm_mac_filter.mcast_accept_all = 1; - tstorm_mac_filter.bcast_accept_all = 1; + tstorm_mac_filter.ucast_accept_all = mask; + tstorm_mac_filter.mcast_accept_all = mask; + tstorm_mac_filter.bcast_accept_all = mask; break; default: - BNX2X_ERR("bad rx mode (%d)\n", mode); + BNX2X_ERR("BAD rx mode (%d)\n", mode); + break; } for (i = 0; i < sizeof(struct tstorm_eth_mac_filter_config)/4; i++) { REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_MAC_FILTER_CONFIG_OFFSET(port) + i * 4, + TSTORM_MAC_FILTER_CONFIG_OFFSET(func) + i * 4, ((u32 *)&tstorm_mac_filter)[i]); -/* DP(NETIF_MSG_IFUP, "tstorm_mac_filter[%d]: 0x%08x\n", i, +/* DP(NETIF_MSG_IFUP, "tstorm_mac_filter[%d]: 0x%08x\n", i, ((u32 *)&tstorm_mac_filter)[i]); */ } @@ -3271,26 +3713,30 @@ static void bnx2x_set_storm_rx_mode(struct bnx2x *bp) static void bnx2x_init_internal(struct bnx2x *bp) { - int port = bp->port; struct tstorm_eth_function_common_config tstorm_config = {0}; struct stats_indication_flags stats_flags = {0}; + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + int i; if (is_multi(bp)) { tstorm_config.config_flags = MULTI_FLAGS; tstorm_config.rss_result_mask = MULTI_MASK; } + tstorm_config.leading_client_id = BP_L_ID(bp); + REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(port), + TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(func), (*(u32 *)&tstorm_config)); -/* DP(NETIF_MSG_IFUP, "tstorm_config: 0x%08x\n", +/* DP(NETIF_MSG_IFUP, "tstorm_config: 0x%08x\n", (*(u32 *)&tstorm_config)); */ bp->rx_mode = BNX2X_RX_MODE_NONE; /* no rx until link is up */ bnx2x_set_storm_rx_mode(bp); - stats_flags.collect_eth = cpu_to_le32(1); + stats_flags.collect_eth = 1; REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(port), ((u32 *)&stats_flags)[0]); @@ -3307,8 +3753,28 @@ static void bnx2x_init_internal(struct bnx2x *bp) REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(port) + 4, ((u32 *)&stats_flags)[1]); -/* DP(NETIF_MSG_IFUP, "stats_flags: 0x%08x 0x%08x\n", +/* DP(NETIF_MSG_IFUP, "stats_flags: 0x%08x 0x%08x\n", ((u32 *)&stats_flags)[0], ((u32 *)&stats_flags)[1]); */ + + if (CHIP_IS_E1H(bp)) { + REG_WR8(bp, BAR_XSTRORM_INTMEM + XSTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + REG_WR8(bp, BAR_TSTRORM_INTMEM + TSTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + REG_WR8(bp, BAR_USTRORM_INTMEM + USTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + + REG_WR16(bp, BAR_XSTRORM_INTMEM + + XSTORM_E1HOV_OFFSET(func), bp->e1hov); + } + + /* Zero this manualy as its initialization is + currently missing in the initTool */ + for (i = 0; i < USTORM_AGG_DATA_SIZE >> 2; i++) + REG_WR(bp, BAR_USTRORM_INTMEM + + USTORM_AGG_DATA_OFFSET + 4*i, 0); } static void bnx2x_nic_init(struct bnx2x *bp) @@ -3318,15 +3784,20 @@ static void bnx2x_nic_init(struct bnx2x *bp) for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; + fp->bp = bp; fp->state = BNX2X_FP_STATE_CLOSED; - DP(NETIF_MSG_IFUP, "bnx2x_init_sb(%p,%p,%d);\n", - bp, fp->status_blk, i); fp->index = i; - bnx2x_init_sb(bp, fp->status_blk, fp->status_blk_mapping, i); + fp->cl_id = BP_L_ID(bp) + i; + fp->sb_id = fp->cl_id; + DP(NETIF_MSG_IFUP, + "bnx2x_init_sb(%p,%p) index %d cl_id %d sb %d\n", + bp, fp->status_blk, i, FP_CL_ID(fp), FP_SB_ID(fp)); + bnx2x_init_sb(bp, FP_SB_ID(fp), fp->status_blk, + fp->status_blk_mapping); } bnx2x_init_def_sb(bp, bp->def_status_blk, - bp->def_status_blk_mapping, 0x10); + bp->def_status_blk_mapping, DEF_SB_ID); bnx2x_update_coalesce(bp); bnx2x_init_rx_rings(bp); bnx2x_init_tx_ring(bp); @@ -3336,7 +3807,6 @@ static void bnx2x_nic_init(struct bnx2x *bp) bnx2x_init_stats(bp); bnx2x_init_ind_table(bp); bnx2x_int_enable(bp); - } /* end of nic init */ @@ -3374,7 +3844,7 @@ gunzip_nomem2: gunzip_nomem1: printk(KERN_ERR PFX "%s: Cannot allocate firmware buffer for" - " uncompression\n", bp->dev->name); + " un-compression\n", bp->dev->name); return -ENOMEM; } @@ -3402,7 +3872,7 @@ static int bnx2x_gunzip(struct bnx2x *bp, u8 *zbuf, int len) n = 10; -#define FNAME 0x8 +#define FNAME 0x8 if (zbuf[3] & FNAME) while ((zbuf[n++] != 0) && (n < len)); @@ -3439,41 +3909,25 @@ static int bnx2x_gunzip(struct bnx2x *bp, u8 *zbuf, int len) /* nic load/unload */ /* - * general service functions + * General service functions */ /* send a NIG loopback debug packet */ static void bnx2x_lb_pckt(struct bnx2x *bp) { -#ifdef USE_DMAE u32 wb_write[3]; -#endif /* Ethernet source and destination addresses */ -#ifdef USE_DMAE wb_write[0] = 0x55555555; wb_write[1] = 0x55555555; - wb_write[2] = 0x20; /* SOP */ + wb_write[2] = 0x20; /* SOP */ REG_WR_DMAE(bp, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); -#else - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB, 0x55555555); - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); - /* SOP */ - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 8, 0x20); -#endif /* NON-IP protocol */ -#ifdef USE_DMAE wb_write[0] = 0x09000000; wb_write[1] = 0x55555555; - wb_write[2] = 0x10; /* EOP, eop_bvalid = 0 */ + wb_write[2] = 0x10; /* EOP, eop_bvalid = 0 */ REG_WR_DMAE(bp, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); -#else - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB, 0x09000000); - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); - /* EOP, eop_bvalid = 0 */ - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 8, 0x10); -#endif } /* some of the internal memories @@ -3511,13 +3965,9 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) /* Wait until NIG register shows 1 packet of size 0x10 */ count = 1000 * factor; while (count) { -#ifdef BNX2X_DMAE_RD + bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); val = *bnx2x_sp(bp, wb_data[0]); -#else - val = REG_RD(bp, NIG_REG_STAT2_BRB_OCTET); - REG_RD(bp, NIG_REG_STAT2_BRB_OCTET + 4); -#endif if (val == 0x10) break; @@ -3533,7 +3983,6 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) count = 1000 * factor; while (count) { val = REG_RD(bp, PRS_REG_NUM_OF_PACKETS); - if (val == 1) break; @@ -3546,9 +3995,9 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) } /* Reset and init BRB, PRS */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x3); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x03); msleep(50); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x3); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03); msleep(50); bnx2x_init_block(bp, BRB1_COMMON_START, BRB1_COMMON_END); bnx2x_init_block(bp, PRS_COMMON_START, PRS_COMMON_END); @@ -3572,13 +4021,9 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) packets of size 11*0x10 = 0xb0 */ count = 1000 * factor; while (count) { -#ifdef BNX2X_DMAE_RD + bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); val = *bnx2x_sp(bp, wb_data[0]); -#else - val = REG_RD(bp, NIG_REG_STAT2_BRB_OCTET); - REG_RD(bp, NIG_REG_STAT2_BRB_OCTET + 4); -#endif if (val == 0xb0) break; @@ -3648,85 +4093,75 @@ static void enable_blocks_attention(struct bnx2x *bp) REG_WR(bp, XSDM_REG_XSDM_INT_MASK_0, 0); REG_WR(bp, XSDM_REG_XSDM_INT_MASK_1, 0); REG_WR(bp, XCM_REG_XCM_INT_MASK, 0); -/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_0, 0); */ -/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_1, 0); */ +/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_0, 0); */ +/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_1, 0); */ REG_WR(bp, USDM_REG_USDM_INT_MASK_0, 0); REG_WR(bp, USDM_REG_USDM_INT_MASK_1, 0); REG_WR(bp, UCM_REG_UCM_INT_MASK, 0); -/* REG_WR(bp, USEM_REG_USEM_INT_MASK_0, 0); */ -/* REG_WR(bp, USEM_REG_USEM_INT_MASK_1, 0); */ +/* REG_WR(bp, USEM_REG_USEM_INT_MASK_0, 0); */ +/* REG_WR(bp, USEM_REG_USEM_INT_MASK_1, 0); */ REG_WR(bp, GRCBASE_UPB + PB_REG_PB_INT_MASK, 0); REG_WR(bp, CSDM_REG_CSDM_INT_MASK_0, 0); REG_WR(bp, CSDM_REG_CSDM_INT_MASK_1, 0); REG_WR(bp, CCM_REG_CCM_INT_MASK, 0); -/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_0, 0); */ -/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_1, 0); */ - REG_WR(bp, PXP2_REG_PXP2_INT_MASK, 0x480000); +/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_0, 0); */ +/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_1, 0); */ + if (CHIP_REV_IS_FPGA(bp)) + REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0, 0x580000); + else + REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0, 0x480000); REG_WR(bp, TSDM_REG_TSDM_INT_MASK_0, 0); REG_WR(bp, TSDM_REG_TSDM_INT_MASK_1, 0); REG_WR(bp, TCM_REG_TCM_INT_MASK, 0); -/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_0, 0); */ -/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_1, 0); */ +/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_0, 0); */ +/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_1, 0); */ REG_WR(bp, CDU_REG_CDU_INT_MASK, 0); REG_WR(bp, DMAE_REG_DMAE_INT_MASK, 0); -/* REG_WR(bp, MISC_REG_MISC_INT_MASK, 0); */ - REG_WR(bp, PBF_REG_PBF_INT_MASK, 0X18); /* bit 3,4 masked */ +/* REG_WR(bp, MISC_REG_MISC_INT_MASK, 0); */ + REG_WR(bp, PBF_REG_PBF_INT_MASK, 0X18); /* bit 3,4 masked */ } -static int bnx2x_function_init(struct bnx2x *bp, int mode) + +static int bnx2x_init_common(struct bnx2x *bp) { - int func = bp->port; - int port = func ? PORT1 : PORT0; u32 val, i; -#ifdef USE_DMAE - u32 wb_write[2]; -#endif - - DP(BNX2X_MSG_MCP, "function is %d mode is %x\n", func, mode); - if ((func != 0) && (func != 1)) { - BNX2X_ERR("BAD function number (%d)\n", func); - return -ENODEV; - } - bnx2x_gunzip_init(bp); + DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_FUNC(bp)); - if (mode & 0x1) { /* init common */ - DP(BNX2X_MSG_MCP, "starting common init func %d mode %x\n", - func, mode); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, - 0xffffffff); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, - 0xfffc); - bnx2x_init_block(bp, MISC_COMMON_START, MISC_COMMON_END); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, 0xfffc); - REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x100); - msleep(30); - REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x0); + bnx2x_init_block(bp, MISC_COMMON_START, MISC_COMMON_END); + if (CHIP_IS_E1H(bp)) + REG_WR(bp, MISC_REG_E1HMF_MODE, IS_E1HMF(bp)); - bnx2x_init_block(bp, PXP_COMMON_START, PXP_COMMON_END); - bnx2x_init_block(bp, PXP2_COMMON_START, PXP2_COMMON_END); + REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x100); + msleep(30); + REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x0); - bnx2x_init_pxp(bp); + bnx2x_init_block(bp, PXP_COMMON_START, PXP_COMMON_END); + if (CHIP_IS_E1(bp)) { + /* enable HW interrupt from PXP on USDM overflow + bit 16 on INT_MASK_0 */ + REG_WR(bp, PXP_REG_PXP_INT_MASK_0, 0); + } - if (CHIP_REV(bp) == CHIP_REV_Ax) { - /* enable HW interrupt from PXP on USDM - overflow bit 16 on INT_MASK_0 */ - REG_WR(bp, PXP_REG_PXP_INT_MASK_0, 0); - } + bnx2x_init_block(bp, PXP2_COMMON_START, PXP2_COMMON_END); + bnx2x_init_pxp(bp); #ifdef __BIG_ENDIAN - REG_WR(bp, PXP2_REG_RQ_QM_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_TM_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_SRC_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_CDU_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_DBG_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_HC_ENDIAN_M, 1); - -/* REG_WR(bp, PXP2_REG_RD_PBF_SWAP_MODE, 1); */ - REG_WR(bp, PXP2_REG_RD_QM_SWAP_MODE, 1); - REG_WR(bp, PXP2_REG_RD_TM_SWAP_MODE, 1); - REG_WR(bp, PXP2_REG_RD_SRC_SWAP_MODE, 1); - REG_WR(bp, PXP2_REG_RD_CDURD_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RQ_QM_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_TM_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_SRC_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_CDU_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_DBG_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_HC_ENDIAN_M, 1); + +/* REG_WR(bp, PXP2_REG_RD_PBF_SWAP_MODE, 1); */ + REG_WR(bp, PXP2_REG_RD_QM_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RD_TM_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RD_SRC_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RD_CDURD_SWAP_MODE, 1); #endif #ifndef BCM_ISCSI @@ -3734,92 +4169,105 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) REG_WR(bp, PRS_REG_NIC_MODE, 1); #endif - REG_WR(bp, PXP2_REG_RQ_CDU_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_CDU_P_SIZE, 2); #ifdef BCM_ISCSI - REG_WR(bp, PXP2_REG_RQ_TM_P_SIZE, 5); - REG_WR(bp, PXP2_REG_RQ_QM_P_SIZE, 5); - REG_WR(bp, PXP2_REG_RQ_SRC_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_TM_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_QM_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_SRC_P_SIZE, 5); #endif - bnx2x_init_block(bp, DMAE_COMMON_START, DMAE_COMMON_END); + if (CHIP_REV_IS_FPGA(bp) && CHIP_IS_E1H(bp)) + REG_WR(bp, PXP2_REG_PGL_TAGS_LIMIT, 0x1); - /* let the HW do it's magic ... */ - msleep(100); - /* finish PXP init - (can be moved up if we want to use the DMAE) */ - val = REG_RD(bp, PXP2_REG_RQ_CFG_DONE); - if (val != 1) { - BNX2X_ERR("PXP2 CFG failed\n"); - return -EBUSY; - } + /* let the HW do it's magic ... */ + msleep(100); + /* finish PXP init */ + val = REG_RD(bp, PXP2_REG_RQ_CFG_DONE); + if (val != 1) { + BNX2X_ERR("PXP2 CFG failed\n"); + return -EBUSY; + } + val = REG_RD(bp, PXP2_REG_RD_INIT_DONE); + if (val != 1) { + BNX2X_ERR("PXP2 RD_INIT failed\n"); + return -EBUSY; + } - val = REG_RD(bp, PXP2_REG_RD_INIT_DONE); - if (val != 1) { - BNX2X_ERR("PXP2 RD_INIT failed\n"); - return -EBUSY; - } + REG_WR(bp, PXP2_REG_RQ_DISABLE_INPUTS, 0); + REG_WR(bp, PXP2_REG_RD_DISABLE_INPUTS, 0); - REG_WR(bp, PXP2_REG_RQ_DISABLE_INPUTS, 0); - REG_WR(bp, PXP2_REG_RD_DISABLE_INPUTS, 0); + bnx2x_init_block(bp, DMAE_COMMON_START, DMAE_COMMON_END); - bnx2x_init_fill(bp, TSEM_REG_PRAM, 0, 8); + /* clean the DMAE memory */ + bp->dmae_ready = 1; + bnx2x_init_fill(bp, TSEM_REG_PRAM, 0, 8); - bnx2x_init_block(bp, TCM_COMMON_START, TCM_COMMON_END); - bnx2x_init_block(bp, UCM_COMMON_START, UCM_COMMON_END); - bnx2x_init_block(bp, CCM_COMMON_START, CCM_COMMON_END); - bnx2x_init_block(bp, XCM_COMMON_START, XCM_COMMON_END); + bnx2x_init_block(bp, TCM_COMMON_START, TCM_COMMON_END); + bnx2x_init_block(bp, UCM_COMMON_START, UCM_COMMON_END); + bnx2x_init_block(bp, CCM_COMMON_START, CCM_COMMON_END); + bnx2x_init_block(bp, XCM_COMMON_START, XCM_COMMON_END); -#ifdef BNX2X_DMAE_RD - bnx2x_read_dmae(bp, XSEM_REG_PASSIVE_BUFFER, 3); - bnx2x_read_dmae(bp, CSEM_REG_PASSIVE_BUFFER, 3); - bnx2x_read_dmae(bp, TSEM_REG_PASSIVE_BUFFER, 3); - bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3); -#else - REG_RD(bp, XSEM_REG_PASSIVE_BUFFER); - REG_RD(bp, XSEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, XSEM_REG_PASSIVE_BUFFER + 8); - REG_RD(bp, CSEM_REG_PASSIVE_BUFFER); - REG_RD(bp, CSEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, CSEM_REG_PASSIVE_BUFFER + 8); - REG_RD(bp, TSEM_REG_PASSIVE_BUFFER); - REG_RD(bp, TSEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, TSEM_REG_PASSIVE_BUFFER + 8); - REG_RD(bp, USEM_REG_PASSIVE_BUFFER); - REG_RD(bp, USEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, USEM_REG_PASSIVE_BUFFER + 8); -#endif - bnx2x_init_block(bp, QM_COMMON_START, QM_COMMON_END); - /* soft reset pulse */ - REG_WR(bp, QM_REG_SOFT_RESET, 1); - REG_WR(bp, QM_REG_SOFT_RESET, 0); + bnx2x_read_dmae(bp, XSEM_REG_PASSIVE_BUFFER, 3); + bnx2x_read_dmae(bp, CSEM_REG_PASSIVE_BUFFER, 3); + bnx2x_read_dmae(bp, TSEM_REG_PASSIVE_BUFFER, 3); + bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3); + + bnx2x_init_block(bp, QM_COMMON_START, QM_COMMON_END); + /* soft reset pulse */ + REG_WR(bp, QM_REG_SOFT_RESET, 1); + REG_WR(bp, QM_REG_SOFT_RESET, 0); #ifdef BCM_ISCSI - bnx2x_init_block(bp, TIMERS_COMMON_START, TIMERS_COMMON_END); + bnx2x_init_block(bp, TIMERS_COMMON_START, TIMERS_COMMON_END); #endif - bnx2x_init_block(bp, DQ_COMMON_START, DQ_COMMON_END); - REG_WR(bp, DORQ_REG_DPM_CID_OFST, BCM_PAGE_BITS); - if (CHIP_REV(bp) == CHIP_REV_Ax) { - /* enable hw interrupt from doorbell Q */ - REG_WR(bp, DORQ_REG_DORQ_INT_MASK, 0); - } - bnx2x_init_block(bp, BRB1_COMMON_START, BRB1_COMMON_END); + bnx2x_init_block(bp, DQ_COMMON_START, DQ_COMMON_END); + REG_WR(bp, DORQ_REG_DPM_CID_OFST, BCM_PAGE_SHIFT); + if (!CHIP_REV_IS_SLOW(bp)) { + /* enable hw interrupt from doorbell Q */ + REG_WR(bp, DORQ_REG_DORQ_INT_MASK, 0); + } - if (CHIP_REV_IS_SLOW(bp)) { - /* fix for emulation and FPGA for no pause */ - REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_0, 513); - REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_1, 513); - REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_0, 0); - REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_1, 0); - } + bnx2x_init_block(bp, BRB1_COMMON_START, BRB1_COMMON_END); + if (CHIP_REV_IS_SLOW(bp)) { + /* fix for emulation and FPGA for no pause */ + REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_0, 513); + REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_1, 513); + REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_0, 0); + REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_1, 0); + } - bnx2x_init_block(bp, PRS_COMMON_START, PRS_COMMON_END); + bnx2x_init_block(bp, PRS_COMMON_START, PRS_COMMON_END); + if (CHIP_IS_E1H(bp)) + REG_WR(bp, PRS_REG_E1HOV_MODE, IS_E1HMF(bp)); - bnx2x_init_block(bp, TSDM_COMMON_START, TSDM_COMMON_END); - bnx2x_init_block(bp, CSDM_COMMON_START, CSDM_COMMON_END); - bnx2x_init_block(bp, USDM_COMMON_START, USDM_COMMON_END); - bnx2x_init_block(bp, XSDM_COMMON_START, XSDM_COMMON_END); + bnx2x_init_block(bp, TSDM_COMMON_START, TSDM_COMMON_END); + bnx2x_init_block(bp, CSDM_COMMON_START, CSDM_COMMON_END); + bnx2x_init_block(bp, USDM_COMMON_START, USDM_COMMON_END); + bnx2x_init_block(bp, XSDM_COMMON_START, XSDM_COMMON_END); + if (CHIP_IS_E1H(bp)) { + bnx2x_init_fill(bp, TSTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + TSTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, CSTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + CSTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, XSTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + XSTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, USTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + USTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + } else { /* E1 */ bnx2x_init_fill(bp, TSTORM_INTMEM_ADDR, 0, STORM_INTMEM_SIZE_E1); bnx2x_init_fill(bp, CSTORM_INTMEM_ADDR, 0, @@ -3828,157 +4276,141 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) STORM_INTMEM_SIZE_E1); bnx2x_init_fill(bp, USTORM_INTMEM_ADDR, 0, STORM_INTMEM_SIZE_E1); + } - bnx2x_init_block(bp, TSEM_COMMON_START, TSEM_COMMON_END); - bnx2x_init_block(bp, USEM_COMMON_START, USEM_COMMON_END); - bnx2x_init_block(bp, CSEM_COMMON_START, CSEM_COMMON_END); - bnx2x_init_block(bp, XSEM_COMMON_START, XSEM_COMMON_END); - - /* sync semi rtc */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, - 0x80000000); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, - 0x80000000); - - bnx2x_init_block(bp, UPB_COMMON_START, UPB_COMMON_END); - bnx2x_init_block(bp, XPB_COMMON_START, XPB_COMMON_END); - bnx2x_init_block(bp, PBF_COMMON_START, PBF_COMMON_END); - - REG_WR(bp, SRC_REG_SOFT_RST, 1); - for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4) { - REG_WR(bp, i, 0xc0cac01a); - /* TODO: replace with something meaningful */ - } - /* SRCH COMMON comes here */ - REG_WR(bp, SRC_REG_SOFT_RST, 0); - - if (sizeof(union cdu_context) != 1024) { - /* we currently assume that a context is 1024 bytes */ - printk(KERN_ALERT PFX "please adjust the size of" - " cdu_context(%ld)\n", - (long)sizeof(union cdu_context)); - } - val = (4 << 24) + (0 << 12) + 1024; - REG_WR(bp, CDU_REG_CDU_GLOBAL_PARAMS, val); - bnx2x_init_block(bp, CDU_COMMON_START, CDU_COMMON_END); - - bnx2x_init_block(bp, CFC_COMMON_START, CFC_COMMON_END); - REG_WR(bp, CFC_REG_INIT_REG, 0x7FF); - - bnx2x_init_block(bp, HC_COMMON_START, HC_COMMON_END); - bnx2x_init_block(bp, MISC_AEU_COMMON_START, - MISC_AEU_COMMON_END); - /* RXPCS COMMON comes here */ - /* EMAC0 COMMON comes here */ - /* EMAC1 COMMON comes here */ - /* DBU COMMON comes here */ - /* DBG COMMON comes here */ - bnx2x_init_block(bp, NIG_COMMON_START, NIG_COMMON_END); + bnx2x_init_block(bp, TSEM_COMMON_START, TSEM_COMMON_END); + bnx2x_init_block(bp, USEM_COMMON_START, USEM_COMMON_END); + bnx2x_init_block(bp, CSEM_COMMON_START, CSEM_COMMON_END); + bnx2x_init_block(bp, XSEM_COMMON_START, XSEM_COMMON_END); - if (CHIP_REV_IS_SLOW(bp)) - msleep(200); + /* sync semi rtc */ + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0x80000000); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, + 0x80000000); - /* finish CFC init */ - val = REG_RD(bp, CFC_REG_LL_INIT_DONE); - if (val != 1) { - BNX2X_ERR("CFC LL_INIT failed\n"); - return -EBUSY; - } + bnx2x_init_block(bp, UPB_COMMON_START, UPB_COMMON_END); + bnx2x_init_block(bp, XPB_COMMON_START, XPB_COMMON_END); + bnx2x_init_block(bp, PBF_COMMON_START, PBF_COMMON_END); - val = REG_RD(bp, CFC_REG_AC_INIT_DONE); - if (val != 1) { - BNX2X_ERR("CFC AC_INIT failed\n"); - return -EBUSY; - } + REG_WR(bp, SRC_REG_SOFT_RST, 1); + for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4) { + REG_WR(bp, i, 0xc0cac01a); + /* TODO: replace with something meaningful */ + } + if (CHIP_IS_E1H(bp)) + bnx2x_init_block(bp, SRCH_COMMON_START, SRCH_COMMON_END); + REG_WR(bp, SRC_REG_SOFT_RST, 0); - val = REG_RD(bp, CFC_REG_CAM_INIT_DONE); - if (val != 1) { - BNX2X_ERR("CFC CAM_INIT failed\n"); - return -EBUSY; - } + if (sizeof(union cdu_context) != 1024) + /* we currently assume that a context is 1024 bytes */ + printk(KERN_ALERT PFX "please adjust the size of" + " cdu_context(%ld)\n", (long)sizeof(union cdu_context)); - REG_WR(bp, CFC_REG_DEBUG0, 0); + bnx2x_init_block(bp, CDU_COMMON_START, CDU_COMMON_END); + val = (4 << 24) + (0 << 12) + 1024; + REG_WR(bp, CDU_REG_CDU_GLOBAL_PARAMS, val); + if (CHIP_IS_E1(bp)) { + /* !!! fix pxp client crdit until excel update */ + REG_WR(bp, CDU_REG_CDU_DEBUG, 0x264); + REG_WR(bp, CDU_REG_CDU_DEBUG, 0); + } - /* read NIG statistic - to see if this is our first up since powerup */ -#ifdef BNX2X_DMAE_RD - bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); - val = *bnx2x_sp(bp, wb_data[0]); -#else - val = REG_RD(bp, NIG_REG_STAT2_BRB_OCTET); - REG_RD(bp, NIG_REG_STAT2_BRB_OCTET + 4); -#endif - /* do internal memory self test */ - if ((val == 0) && bnx2x_int_mem_test(bp)) { - BNX2X_ERR("internal mem selftest failed\n"); - return -EBUSY; - } + bnx2x_init_block(bp, CFC_COMMON_START, CFC_COMMON_END); + REG_WR(bp, CFC_REG_INIT_REG, 0x7FF); - /* clear PXP2 attentions */ - REG_RD(bp, PXP2_REG_PXP2_INT_STS_CLR); + bnx2x_init_block(bp, HC_COMMON_START, HC_COMMON_END); + bnx2x_init_block(bp, MISC_AEU_COMMON_START, MISC_AEU_COMMON_END); - enable_blocks_attention(bp); - /* enable_blocks_parity(bp); */ + /* PXPCS COMMON comes here */ + /* Reset PCIE errors for debug */ + REG_WR(bp, 0x2814, 0xffffffff); + REG_WR(bp, 0x3820, 0xffffffff); - switch (bp->board & SHARED_HW_CFG_BOARD_TYPE_MASK) { - case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: - /* Fan failure is indicated by SPIO 5 */ - bnx2x_set_spio(bp, MISC_REGISTERS_SPIO_5, - MISC_REGISTERS_SPIO_INPUT_HI_Z); + /* EMAC0 COMMON comes here */ + /* EMAC1 COMMON comes here */ + /* DBU COMMON comes here */ + /* DBG COMMON comes here */ + + bnx2x_init_block(bp, NIG_COMMON_START, NIG_COMMON_END); + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, NIG_REG_LLH_MF_MODE, IS_E1HMF(bp)); + REG_WR(bp, NIG_REG_LLH_E1HOV_MODE, IS_E1HMF(bp)); + } + + if (CHIP_REV_IS_SLOW(bp)) + msleep(200); + + /* finish CFC init */ + val = reg_poll(bp, CFC_REG_LL_INIT_DONE, 1, 100, 10); + if (val != 1) { + BNX2X_ERR("CFC LL_INIT failed\n"); + return -EBUSY; + } + val = reg_poll(bp, CFC_REG_AC_INIT_DONE, 1, 100, 10); + if (val != 1) { + BNX2X_ERR("CFC AC_INIT failed\n"); + return -EBUSY; + } + val = reg_poll(bp, CFC_REG_CAM_INIT_DONE, 1, 100, 10); + if (val != 1) { + BNX2X_ERR("CFC CAM_INIT failed\n"); + return -EBUSY; + } + REG_WR(bp, CFC_REG_DEBUG0, 0); - /* set to active low mode */ - val = REG_RD(bp, MISC_REG_SPIO_INT); - val |= ((1 << MISC_REGISTERS_SPIO_5) << + /* read NIG statistic + to see if this is our first up since powerup */ + bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); + val = *bnx2x_sp(bp, wb_data[0]); + + /* do internal memory self test */ + if ((CHIP_IS_E1(bp)) && (val == 0) && bnx2x_int_mem_test(bp)) { + BNX2X_ERR("internal mem self test failed\n"); + return -EBUSY; + } + + switch (bp->common.board & SHARED_HW_CFG_BOARD_TYPE_MASK) { + case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: + /* Fan failure is indicated by SPIO 5 */ + bnx2x_set_spio(bp, MISC_REGISTERS_SPIO_5, + MISC_REGISTERS_SPIO_INPUT_HI_Z); + + /* set to active low mode */ + val = REG_RD(bp, MISC_REG_SPIO_INT); + val |= ((1 << MISC_REGISTERS_SPIO_5) << MISC_REGISTERS_SPIO_INT_OLD_SET_POS); - REG_WR(bp, MISC_REG_SPIO_INT, val); + REG_WR(bp, MISC_REG_SPIO_INT, val); - /* enable interrupt to signal the IGU */ - val = REG_RD(bp, MISC_REG_SPIO_EVENT_EN); - val |= (1 << MISC_REGISTERS_SPIO_5); - REG_WR(bp, MISC_REG_SPIO_EVENT_EN, val); - break; + /* enable interrupt to signal the IGU */ + val = REG_RD(bp, MISC_REG_SPIO_EVENT_EN); + val |= (1 << MISC_REGISTERS_SPIO_5); + REG_WR(bp, MISC_REG_SPIO_EVENT_EN, val); + break; - default: - break; - } + default: + break; + } - } /* end of common init */ + /* clear PXP2 attentions */ + REG_RD(bp, PXP2_REG_PXP2_INT_STS_CLR_0); - /* per port init */ + enable_blocks_attention(bp); - /* the phys address is shifted right 12 bits and has an added - 1=valid bit added to the 53rd bit - then since this is a wide register(TM) - we split it into two 32 bit writes - */ -#define RQ_ONCHIP_AT_PORT_SIZE 384 -#define ONCHIP_ADDR1(x) ((u32)(((u64)x >> 12) & 0xFFFFFFFF)) -#define ONCHIP_ADDR2(x) ((u32)((1 << 20) | ((u64)x >> 44))) -#define PXP_ONE_ILT(x) ((x << 10) | x) + return 0; +} - DP(BNX2X_MSG_MCP, "starting per-function init port is %x\n", func); +static int bnx2x_init_port(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + u32 val; - REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + func*4, 0); + DP(BNX2X_MSG_MCP, "starting port init port %x\n", port); + + REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0); /* Port PXP comes here */ /* Port PXP2 comes here */ - - /* Offset is - * Port0 0 - * Port1 384 */ - i = func * RQ_ONCHIP_AT_PORT_SIZE; -#ifdef USE_DMAE - wb_write[0] = ONCHIP_ADDR1(bnx2x_sp_mapping(bp, context)); - wb_write[1] = ONCHIP_ADDR2(bnx2x_sp_mapping(bp, context)); - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); -#else - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, - ONCHIP_ADDR1(bnx2x_sp_mapping(bp, context))); - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT + i*8 + 4, - ONCHIP_ADDR2(bnx2x_sp_mapping(bp, context))); -#endif - REG_WR(bp, PXP2_REG_PSWRQ_CDU0_L2P + func*4, PXP_ONE_ILT(i)); - #ifdef BCM_ISCSI /* Port0 1 * Port1 385 */ @@ -4004,30 +4436,9 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); REG_WR(bp, PXP2_REG_PSWRQ_SRC0_L2P + func*4, PXP_ONE_ILT(i)); #endif - - /* Port TCM comes here */ - /* Port UCM comes here */ - /* Port CCM comes here */ - bnx2x_init_block(bp, func ? XCM_PORT1_START : XCM_PORT0_START, - func ? XCM_PORT1_END : XCM_PORT0_END); - -#ifdef USE_DMAE - wb_write[0] = 0; - wb_write[1] = 0; -#endif - for (i = 0; i < 32; i++) { - REG_WR(bp, QM_REG_BASEADDR + (func*32 + i)*4, 1024 * 4 * i); -#ifdef USE_DMAE - REG_WR_DMAE(bp, QM_REG_PTRTBL + (func*32 + i)*8, wb_write, 2); -#else - REG_WR_IND(bp, QM_REG_PTRTBL + (func*32 + i)*8, 0); - REG_WR_IND(bp, QM_REG_PTRTBL + (func*32 + i)*8 + 4, 0); -#endif - } - REG_WR(bp, QM_REG_CONNNUM_0 + func*4, 1024/16 - 1); + /* Port CMs come here */ /* Port QM comes here */ - #ifdef BCM_ISCSI REG_WR(bp, TM_REG_LIN0_SCAN_TIME + func*4, 1024/64*20); REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + func*4, 31); @@ -4042,31 +4453,32 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) /* Port CSDM comes here */ /* Port USDM comes here */ /* Port XSDM comes here */ - bnx2x_init_block(bp, func ? TSEM_PORT1_START : TSEM_PORT0_START, - func ? TSEM_PORT1_END : TSEM_PORT0_END); - bnx2x_init_block(bp, func ? USEM_PORT1_START : USEM_PORT0_START, - func ? USEM_PORT1_END : USEM_PORT0_END); - bnx2x_init_block(bp, func ? CSEM_PORT1_START : CSEM_PORT0_START, - func ? CSEM_PORT1_END : CSEM_PORT0_END); - bnx2x_init_block(bp, func ? XSEM_PORT1_START : XSEM_PORT0_START, - func ? XSEM_PORT1_END : XSEM_PORT0_END); + bnx2x_init_block(bp, port ? TSEM_PORT1_START : TSEM_PORT0_START, + port ? TSEM_PORT1_END : TSEM_PORT0_END); + bnx2x_init_block(bp, port ? USEM_PORT1_START : USEM_PORT0_START, + port ? USEM_PORT1_END : USEM_PORT0_END); + bnx2x_init_block(bp, port ? CSEM_PORT1_START : CSEM_PORT0_START, + port ? CSEM_PORT1_END : CSEM_PORT0_END); + bnx2x_init_block(bp, port ? XSEM_PORT1_START : XSEM_PORT0_START, + port ? XSEM_PORT1_END : XSEM_PORT0_END); /* Port UPB comes here */ - /* Port XSDM comes here */ - bnx2x_init_block(bp, func ? PBF_PORT1_START : PBF_PORT0_START, - func ? PBF_PORT1_END : PBF_PORT0_END); + /* Port XPB comes here */ + + bnx2x_init_block(bp, port ? PBF_PORT1_START : PBF_PORT0_START, + port ? PBF_PORT1_END : PBF_PORT0_END); /* configure PBF to work without PAUSE mtu 9000 */ - REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + func*4, 0); + REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); /* update threshold */ - REG_WR(bp, PBF_REG_P0_ARB_THRSH + func*4, (9040/16)); + REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, (9040/16)); /* update init credit */ - REG_WR(bp, PBF_REG_P0_INIT_CRD + func*4, (9040/16) + 553 - 22); + REG_WR(bp, PBF_REG_P0_INIT_CRD + port*4, (9040/16) + 553 - 22); /* probe changes */ - REG_WR(bp, PBF_REG_INIT_P0 + func*4, 1); + REG_WR(bp, PBF_REG_INIT_P0 + port*4, 1); msleep(5); - REG_WR(bp, PBF_REG_INIT_P0 + func*4, 0); + REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0); #ifdef BCM_ISCSI /* tell the searcher where the T2 table is */ @@ -4084,23 +4496,57 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) #endif /* Port CDU comes here */ /* Port CFC comes here */ - bnx2x_init_block(bp, func ? HC_PORT1_START : HC_PORT0_START, - func ? HC_PORT1_END : HC_PORT0_END); - bnx2x_init_block(bp, func ? MISC_AEU_PORT1_START : + + if (CHIP_IS_E1(bp)) { + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); + } + bnx2x_init_block(bp, port ? HC_PORT1_START : HC_PORT0_START, + port ? HC_PORT1_END : HC_PORT0_END); + + bnx2x_init_block(bp, port ? MISC_AEU_PORT1_START : MISC_AEU_PORT0_START, - func ? MISC_AEU_PORT1_END : MISC_AEU_PORT0_END); + port ? MISC_AEU_PORT1_END : MISC_AEU_PORT0_END); + /* init aeu_mask_attn_func_0/1: + * - SF mode: bits 3-7 are masked. only bits 0-2 are in use + * - MF mode: bit 3 is masked. bits 0-2 are in use as in SF + * bits 4-7 are used for "per vn group attention" */ + REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, + (IS_E1HMF(bp) ? 0xF7 : 0x7)); + /* Port PXPCS comes here */ /* Port EMAC0 comes here */ /* Port EMAC1 comes here */ /* Port DBU comes here */ /* Port DBG comes here */ - bnx2x_init_block(bp, func ? NIG_PORT1_START : NIG_PORT0_START, - func ? NIG_PORT1_END : NIG_PORT0_END); - REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + func*4, 1); + bnx2x_init_block(bp, port ? NIG_PORT1_START : NIG_PORT0_START, + port ? NIG_PORT1_END : NIG_PORT0_END); + + REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1); + + if (CHIP_IS_E1H(bp)) { + u32 wsum; + struct cmng_struct_per_port m_cmng_port; + int vn; + + /* 0x2 disable e1hov, 0x1 enable */ + REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK_MF + port*4, + (IS_E1HMF(bp) ? 0x1 : 0x2)); + + /* Init RATE SHAPING and FAIRNESS contexts. + Initialize as if there is 10G link. */ + wsum = bnx2x_calc_vn_wsum(bp); + bnx2x_init_port_minmax(bp, (int)wsum, 10000, &m_cmng_port); + if (IS_E1HMF(bp)) + for (vn = VN_0; vn < E1HVN_MAX; vn++) + bnx2x_init_vn_minmax(bp, 2*vn + port, + wsum, 10000, &m_cmng_port); + } + /* Port MCP comes here */ /* Port DMAE comes here */ - switch (bp->board & SHARED_HW_CFG_BOARD_TYPE_MASK) { + switch (bp->common.board & SHARED_HW_CFG_BOARD_TYPE_MASK) { case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: /* add SPIO 5 to group 0 */ val = REG_RD(bp, MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); @@ -4114,48 +4560,150 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) bnx2x__link_reset(bp); + return 0; +} + +#define ILT_PER_FUNC (768/2) +#define FUNC_ILT_BASE(func) (func * ILT_PER_FUNC) +/* the phys address is shifted right 12 bits and has an added + 1=valid bit added to the 53rd bit + then since this is a wide register(TM) + we split it into two 32 bit writes + */ +#define ONCHIP_ADDR1(x) ((u32)(((u64)x >> 12) & 0xFFFFFFFF)) +#define ONCHIP_ADDR2(x) ((u32)((1 << 20) | ((u64)x >> 44))) +#define PXP_ONE_ILT(x) (((x) << 10) | x) +#define PXP_ILT_RANGE(f, l) (((l) << 10) | f) + +#define CNIC_ILT_LINES 0 + +static void bnx2x_ilt_wr(struct bnx2x *bp, u32 index, dma_addr_t addr) +{ + int reg; + + if (CHIP_IS_E1H(bp)) + reg = PXP2_REG_RQ_ONCHIP_AT_B0 + index*8; + else /* E1 */ + reg = PXP2_REG_RQ_ONCHIP_AT + index*8; + + bnx2x_wb_wr(bp, reg, ONCHIP_ADDR1(addr), ONCHIP_ADDR2(addr)); +} + +static int bnx2x_init_func(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + int i; + + DP(BNX2X_MSG_MCP, "starting func init func %x\n", func); + + i = FUNC_ILT_BASE(func); + + bnx2x_ilt_wr(bp, i, bnx2x_sp_mapping(bp, context)); + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, PXP2_REG_RQ_CDU_FIRST_ILT, i); + REG_WR(bp, PXP2_REG_RQ_CDU_LAST_ILT, i + CNIC_ILT_LINES); + } else /* E1 */ + REG_WR(bp, PXP2_REG_PSWRQ_CDU0_L2P + func*4, + PXP_ILT_RANGE(i, i + CNIC_ILT_LINES)); + + + if (CHIP_IS_E1H(bp)) { + for (i = 0; i < 9; i++) + bnx2x_init_block(bp, + cm_start[func][i], cm_end[func][i]); + + REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1); + REG_WR(bp, NIG_REG_LLH0_FUNC_VLAN_ID + port*8, bp->e1hov); + } + + /* HC init per function */ + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0); + + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); + } + bnx2x_init_block(bp, hc_limits[func][0], hc_limits[func][1]); + + if (CHIP_IS_E1H(bp)) + REG_WR(bp, HC_REG_FUNC_NUM_P0 + port*4, func); + /* Reset PCIE errors for debug */ REG_WR(bp, 0x2114, 0xffffffff); REG_WR(bp, 0x2120, 0xffffffff); - REG_WR(bp, 0x2814, 0xffffffff); - /* !!! move to init_values.h */ - REG_WR(bp, XSDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); - REG_WR(bp, USDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); - REG_WR(bp, CSDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); - REG_WR(bp, TSDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); + return 0; +} + +static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code) +{ + int i, rc = 0; - REG_WR(bp, DBG_REG_PCI_REQ_CREDIT, 0x1); - REG_WR(bp, TM_REG_PCIARB_CRDCNT_VAL, 0x1); - REG_WR(bp, CDU_REG_CDU_DEBUG, 0x264); - REG_WR(bp, CDU_REG_CDU_DEBUG, 0x0); + DP(BNX2X_MSG_MCP, "function %d load_code %x\n", + BP_FUNC(bp), load_code); - bnx2x_gunzip_end(bp); + bp->dmae_ready = 0; + mutex_init(&bp->dmae_mutex); + bnx2x_gunzip_init(bp); - if (!nomcp) { - port = bp->port; + switch (load_code) { + case FW_MSG_CODE_DRV_LOAD_COMMON: + rc = bnx2x_init_common(bp); + if (rc) + goto init_hw_err; + /* no break */ + + case FW_MSG_CODE_DRV_LOAD_PORT: + bp->dmae_ready = 1; + rc = bnx2x_init_port(bp); + if (rc) + goto init_hw_err; + /* no break */ + + case FW_MSG_CODE_DRV_LOAD_FUNCTION: + bp->dmae_ready = 1; + rc = bnx2x_init_func(bp); + if (rc) + goto init_hw_err; + break; + + default: + BNX2X_ERR("Unknown load_code (0x%x) from MCP\n", load_code); + break; + } + + if (!BP_NOMCP(bp)) { + int func = BP_FUNC(bp); bp->fw_drv_pulse_wr_seq = - (SHMEM_RD(bp, func_mb[port].drv_pulse_mb) & + (SHMEM_RD(bp, func_mb[func].drv_pulse_mb) & DRV_PULSE_SEQ_MASK); - bp->fw_mb = SHMEM_RD(bp, func_mb[port].fw_mb_param); - DP(BNX2X_MSG_MCP, "drv_pulse 0x%x fw_mb 0x%x\n", - bp->fw_drv_pulse_wr_seq, bp->fw_mb); - } else { - bp->fw_mb = 0; - } + bp->func_stx = SHMEM_RD(bp, func_mb[func].fw_mb_param); + DP(BNX2X_MSG_MCP, "drv_pulse 0x%x func_stx 0x%x\n", + bp->fw_drv_pulse_wr_seq, bp->func_stx); + } else + bp->func_stx = 0; - return 0; + /* this needs to be done before gunzip end */ + bnx2x_zero_def_sb(bp); + for_each_queue(bp, i) + bnx2x_zero_sb(bp, BP_L_ID(bp) + i); + +init_hw_err: + bnx2x_gunzip_end(bp); + + return rc; } /* send the MCP a request, block until there is a reply */ static u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) { - int port = bp->port; + int func = BP_FUNC(bp); u32 seq = ++bp->fw_seq; u32 rc = 0; - SHMEM_WR(bp, func_mb[port].drv_mb_header, (command | seq)); + SHMEM_WR(bp, func_mb[func].drv_mb_header, (command | seq)); DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB\n", (command | seq)); /* let the FW do it's magic ... */ @@ -4164,7 +4712,7 @@ static u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) if (CHIP_REV_IS_SLOW(bp)) msleep(900); - rc = SHMEM_RD(bp, func_mb[port].fw_mb_header); + rc = SHMEM_RD(bp, func_mb[func].fw_mb_header); DP(BNX2X_MSG_MCP, "read (%x) seq is (%x) from FW MB\n", rc, seq); /* is this a reply to our command? */ @@ -4229,15 +4777,13 @@ static void bnx2x_free_mem(struct bnx2x *bp) NUM_RCQ_BD); } - BNX2X_FREE(bp->fp); - /* end of fastpath */ BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping, - (sizeof(struct host_def_status_block))); + sizeof(struct host_def_status_block)); BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping, - (sizeof(struct bnx2x_slowpath))); + sizeof(struct bnx2x_slowpath)); #ifdef BCM_ISCSI BNX2X_PCI_FREE(bp->t1, bp->t1_mapping, 64*1024); @@ -4273,8 +4819,6 @@ static int bnx2x_alloc_mem(struct bnx2x *bp) int i; /* fastpath */ - BNX2X_ALLOC(bp->fp, sizeof(struct bnx2x_fastpath) * bp->num_queues); - for_each_queue(bp, i) { bnx2x_fp(bp, i, bp) = bp; @@ -4370,8 +4914,6 @@ static void bnx2x_free_tx_skbs(struct bnx2x *bp) u16 sw_prod = fp->tx_pkt_prod; u16 sw_cons = fp->tx_pkt_cons; - BUG_TRAP(fp->tx_buf_ring != NULL); - while (sw_cons != sw_prod) { bd_cons = bnx2x_free_tx_pkt(bp, fp, TX_BD(sw_cons)); sw_cons++; @@ -4386,8 +4928,6 @@ static void bnx2x_free_rx_skbs(struct bnx2x *bp) for_each_queue(bp, j) { struct bnx2x_fastpath *fp = &bp->fp[j]; - BUG_TRAP(fp->rx_buf_ring != NULL); - for (i = 0; i < NUM_RX_BD; i++) { struct sw_rx_bd *rx_buf = &fp->rx_buf_ring[i]; struct sk_buff *skb = rx_buf->skb; @@ -4414,7 +4954,7 @@ static void bnx2x_free_skbs(struct bnx2x *bp) static void bnx2x_free_msix_irqs(struct bnx2x *bp) { - int i; + int i, offset = 1; free_irq(bp->msix_table[0].vector, bp->dev); DP(NETIF_MSG_IFDOWN, "released sp irq (%d)\n", @@ -4422,26 +4962,22 @@ static void bnx2x_free_msix_irqs(struct bnx2x *bp) for_each_queue(bp, i) { DP(NETIF_MSG_IFDOWN, "about to release fp #%d->%d irq " - "state(%x)\n", i, bp->msix_table[i + 1].vector, + "state %x\n", i, bp->msix_table[i + offset].vector, bnx2x_fp(bp, i, state)); if (bnx2x_fp(bp, i, state) != BNX2X_FP_STATE_CLOSED) BNX2X_ERR("IRQ of fp #%d being freed while " "state != closed\n", i); - free_irq(bp->msix_table[i + 1].vector, &bp->fp[i]); + free_irq(bp->msix_table[i + offset].vector, &bp->fp[i]); } - } static void bnx2x_free_irq(struct bnx2x *bp) { - if (bp->flags & USING_MSIX_FLAG) { - bnx2x_free_msix_irqs(bp); pci_disable_msix(bp->pdev); - bp->flags &= ~USING_MSIX_FLAG; } else @@ -4450,87 +4986,87 @@ static void bnx2x_free_irq(struct bnx2x *bp) static int bnx2x_enable_msix(struct bnx2x *bp) { - - int i; + int i, rc, offset; bp->msix_table[0].entry = 0; - for_each_queue(bp, i) - bp->msix_table[i + 1].entry = i + 1; + offset = 1; + DP(NETIF_MSG_IFUP, "msix_table[0].entry = 0 (slowpath)\n"); - if (pci_enable_msix(bp->pdev, &bp->msix_table[0], - bp->num_queues + 1)){ - BNX2X_LOG("failed to enable MSI-X\n"); - return -1; + for_each_queue(bp, i) { + int igu_vec = offset + i + BP_L_ID(bp); + bp->msix_table[i + offset].entry = igu_vec; + DP(NETIF_MSG_IFUP, "msix_table[%d].entry = %d " + "(fastpath #%u)\n", i + offset, igu_vec, i); } + rc = pci_enable_msix(bp->pdev, &bp->msix_table[0], + bp->num_queues + offset); + if (rc) { + DP(NETIF_MSG_IFUP, "MSI-X is not attainable\n"); + return -1; + } bp->flags |= USING_MSIX_FLAG; return 0; - } - static int bnx2x_req_msix_irqs(struct bnx2x *bp) { - - int i, rc; + int i, rc, offset = 1; rc = request_irq(bp->msix_table[0].vector, bnx2x_msix_sp_int, 0, bp->dev->name, bp->dev); - if (rc) { BNX2X_ERR("request sp irq failed\n"); return -EBUSY; } for_each_queue(bp, i) { - rc = request_irq(bp->msix_table[i + 1].vector, + rc = request_irq(bp->msix_table[i + offset].vector, bnx2x_msix_fp_int, 0, bp->dev->name, &bp->fp[i]); - if (rc) { - BNX2X_ERR("request fp #%d irq failed " - "rc %d\n", i, rc); + BNX2X_ERR("request fp #%d irq failed rc %d\n", + i + offset, rc); bnx2x_free_msix_irqs(bp); return -EBUSY; } bnx2x_fp(bp, i, state) = BNX2X_FP_STATE_IRQ; - } return 0; - } static int bnx2x_req_irq(struct bnx2x *bp) { + int rc; - int rc = request_irq(bp->pdev->irq, bnx2x_interrupt, - IRQF_SHARED, bp->dev->name, bp->dev); + rc = request_irq(bp->pdev->irq, bnx2x_interrupt, IRQF_SHARED, + bp->dev->name, bp->dev); if (!rc) bnx2x_fp(bp, 0, state) = BNX2X_FP_STATE_IRQ; return rc; - } /* * Init service functions */ -static void bnx2x_set_mac_addr(struct bnx2x *bp) +static void bnx2x_set_mac_addr_e1(struct bnx2x *bp) { struct mac_configuration_cmd *config = bnx2x_sp(bp, mac_config); + int port = BP_PORT(bp); /* CAM allocation * unicasts 0-31:port0 32-63:port1 * multicast 64-127:port0 128-191:port1 */ config->hdr.length_6b = 2; - config->hdr.offset = bp->port ? 31 : 0; - config->hdr.reserved0 = 0; + config->hdr.offset = port ? 31 : 0; + config->hdr.client_id = BP_CL_ID(bp); config->hdr.reserved1 = 0; /* primary MAC */ @@ -4540,7 +5076,7 @@ static void bnx2x_set_mac_addr(struct bnx2x *bp) swab16(*(u16 *)&bp->dev->dev_addr[2]); config->config_table[0].cam_entry.lsb_mac_addr = swab16(*(u16 *)&bp->dev->dev_addr[4]); - config->config_table[0].cam_entry.flags = cpu_to_le16(bp->port); + config->config_table[0].cam_entry.flags = cpu_to_le16(port); config->config_table[0].target_table_entry.flags = 0; config->config_table[0].target_table_entry.client_id = 0; config->config_table[0].target_table_entry.vlan_id = 0; @@ -4554,7 +5090,7 @@ static void bnx2x_set_mac_addr(struct bnx2x *bp) config->config_table[1].cam_entry.msb_mac_addr = 0xffff; config->config_table[1].cam_entry.middle_mac_addr = 0xffff; config->config_table[1].cam_entry.lsb_mac_addr = 0xffff; - config->config_table[1].cam_entry.flags = cpu_to_le16(bp->port); + config->config_table[1].cam_entry.flags = cpu_to_le16(port); config->config_table[1].target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST; config->config_table[1].target_table_entry.client_id = 0; @@ -4565,64 +5101,105 @@ static void bnx2x_set_mac_addr(struct bnx2x *bp) U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0); } +static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp) +{ + struct mac_configuration_cmd_e1h *config = + (struct mac_configuration_cmd_e1h *)bnx2x_sp(bp, mac_config); + + if (bp->state != BNX2X_STATE_OPEN) { + DP(NETIF_MSG_IFUP, "state is %x, returning\n", bp->state); + return; + } + + /* CAM allocation for E1H + * unicasts: by func number + * multicast: 20+FUNC*20, 20 each + */ + config->hdr.length_6b = 1; + config->hdr.offset = BP_FUNC(bp); + config->hdr.client_id = BP_CL_ID(bp); + config->hdr.reserved1 = 0; + + /* primary MAC */ + config->config_table[0].msb_mac_addr = + swab16(*(u16 *)&bp->dev->dev_addr[0]); + config->config_table[0].middle_mac_addr = + swab16(*(u16 *)&bp->dev->dev_addr[2]); + config->config_table[0].lsb_mac_addr = + swab16(*(u16 *)&bp->dev->dev_addr[4]); + config->config_table[0].client_id = BP_L_ID(bp); + config->config_table[0].vlan_id = 0; + config->config_table[0].e1hov_id = cpu_to_le16(bp->e1hov); + config->config_table[0].flags = BP_PORT(bp); + + DP(NETIF_MSG_IFUP, "setting MAC (%04x:%04x:%04x) E1HOV %d CLID %d\n", + config->config_table[0].msb_mac_addr, + config->config_table[0].middle_mac_addr, + config->config_table[0].lsb_mac_addr, bp->e1hov, BP_L_ID(bp)); + + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, + U64_HI(bnx2x_sp_mapping(bp, mac_config)), + U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0); +} + static int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx, int *state_p, int poll) { /* can take a while if any port is running */ - int timeout = 500; + int cnt = 500; DP(NETIF_MSG_IFUP, "%s for state to become %x on IDX [%d]\n", poll ? "polling" : "waiting", state, idx); might_sleep(); - - while (timeout) { - + while (cnt--) { if (poll) { bnx2x_rx_int(bp->fp, 10); - /* If index is different from 0 - * The reply for some commands will + /* if index is different from 0 + * the reply for some commands will * be on the none default queue */ if (idx) bnx2x_rx_int(&bp->fp[idx], 10); } - - mb(); /* state is changed by bnx2x_sp_event()*/ + mb(); /* state is changed by bnx2x_sp_event() */ if (*state_p == state) return 0; - timeout--; msleep(1); - } /* timeout! */ BNX2X_ERR("timeout %s for state %x on IDX [%d]\n", poll ? "polling" : "waiting", state, idx); +#ifdef BNX2X_STOP_ON_ERROR + bnx2x_panic(); +#endif return -EBUSY; } static int bnx2x_setup_leading(struct bnx2x *bp) { + int rc; /* reset IGU state */ - bnx2x_ack_sb(bp, DEF_SB_ID, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_ack_sb(bp, bp->fp[0].sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* SETUP ramrod */ bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_PORT_SETUP, 0, 0, 0, 0); - return bnx2x_wait_ramrod(bp, BNX2X_STATE_OPEN, 0, &(bp->state), 0); + /* Wait for completion */ + rc = bnx2x_wait_ramrod(bp, BNX2X_STATE_OPEN, 0, &(bp->state), 0); + return rc; } static int bnx2x_setup_multi(struct bnx2x *bp, int index) { - /* reset IGU state */ - bnx2x_ack_sb(bp, index, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_ack_sb(bp, bp->fp[index].sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* SETUP ramrod */ bp->fp[index].state = BNX2X_FP_STATE_OPENING; @@ -4631,82 +5208,107 @@ static int bnx2x_setup_multi(struct bnx2x *bp, int index) /* Wait for completion */ return bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_OPEN, index, &(bp->fp[index].state), 0); - } - static int bnx2x_poll(struct napi_struct *napi, int budget); static void bnx2x_set_rx_mode(struct net_device *dev); -static int bnx2x_nic_load(struct bnx2x *bp, int req_irq) +/* must be called with rtnl_lock */ +static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) { u32 load_code; - int i; + int i, rc; + +#ifdef BNX2X_STOP_ON_ERROR + if (unlikely(bp->panic)) + return -EPERM; +#endif bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD; - /* Send LOAD_REQUEST command to MCP. - Returns the type of LOAD command: if it is the - first port to be initialized common blocks should be - initialized, otherwise - not. + /* Send LOAD_REQUEST command to MCP + Returns the type of LOAD command: + if it is the first port to be initialized + common blocks should be initialized, otherwise - not */ - if (!nomcp) { + if (!BP_NOMCP(bp)) { load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ); if (!load_code) { BNX2X_ERR("MCP response failure, unloading\n"); return -EBUSY; } - if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) { - BNX2X_ERR("MCP refused load request, unloading\n"); + if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) return -EBUSY; /* other port in diagnostic mode */ - } + } else { - load_code = FW_MSG_CODE_DRV_LOAD_COMMON; + DP(NETIF_MSG_IFUP, "NO MCP load counts before us %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + load_count[0]++; + load_count[1 + BP_PORT(bp)]++; + DP(NETIF_MSG_IFUP, "NO MCP new load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + if (load_count[0] == 1) + load_code = FW_MSG_CODE_DRV_LOAD_COMMON; + else if (load_count[1 + BP_PORT(bp)] == 1) + load_code = FW_MSG_CODE_DRV_LOAD_PORT; + else + load_code = FW_MSG_CODE_DRV_LOAD_FUNCTION; } - /* if we can't use msix we only need one fp, - * so try to enable msix with the requested number of fp's + if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) || + (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) + bp->port.pmf = 1; + else + bp->port.pmf = 0; + DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf); + + /* if we can't use MSI-X we only need one fp, + * so try to enable MSI-X with the requested number of fp's * and fallback to inta with one fp */ - if (req_irq) { - if (use_inta) { + if (use_inta) { + bp->num_queues = 1; + + } else { + if ((use_multi > 1) && (use_multi <= BP_MAX_QUEUES(bp))) + /* user requested number */ + bp->num_queues = use_multi; + + else if (use_multi) + bp->num_queues = min_t(u32, num_online_cpus(), + BP_MAX_QUEUES(bp)); + else bp->num_queues = 1; - } else { - if ((use_multi > 1) && (use_multi <= 16)) - /* user requested number */ - bp->num_queues = use_multi; - else if (use_multi == 1) - bp->num_queues = num_online_cpus(); - else - bp->num_queues = 1; - - if (bnx2x_enable_msix(bp)) { - /* failed to enable msix */ - bp->num_queues = 1; - if (use_multi) - BNX2X_ERR("Multi requested but failed" - " to enable MSI-X\n"); - } + + if (bnx2x_enable_msix(bp)) { + /* failed to enable MSI-X */ + bp->num_queues = 1; + if (use_multi) + BNX2X_ERR("Multi requested but failed" + " to enable MSI-X\n"); } } - - DP(NETIF_MSG_IFUP, "set number of queues to %d\n", bp->num_queues); + DP(NETIF_MSG_IFUP, + "set number of queues to %d\n", bp->num_queues); if (bnx2x_alloc_mem(bp)) return -ENOMEM; - if (req_irq) { - if (bp->flags & USING_MSIX_FLAG) { - if (bnx2x_req_msix_irqs(bp)) { - pci_disable_msix(bp->pdev); - goto load_error; - } + /* Disable interrupt handling until HW is initialized */ + atomic_set(&bp->intr_sem, 1); - } else { - if (bnx2x_req_irq(bp)) { - BNX2X_ERR("IRQ request failed, aborting\n"); - goto load_error; - } + if (bp->flags & USING_MSIX_FLAG) { + rc = bnx2x_req_msix_irqs(bp); + if (rc) { + pci_disable_msix(bp->pdev); + goto load_error; + } + } else { + bnx2x_ack_int(bp); + rc = bnx2x_req_irq(bp); + if (rc) { + BNX2X_ERR("IRQ request failed, aborting\n"); + goto load_error; } } @@ -4714,26 +5316,25 @@ static int bnx2x_nic_load(struct bnx2x *bp, int req_irq) netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, 128); - /* Initialize HW */ - if (bnx2x_function_init(bp, - (load_code == FW_MSG_CODE_DRV_LOAD_COMMON))) { + rc = bnx2x_init_hw(bp, load_code); + if (rc) { BNX2X_ERR("HW init failed, aborting\n"); goto load_error; } - + /* Enable interrupt handling */ atomic_set(&bp->intr_sem, 0); - /* Setup NIC internals and enable interrupts */ bnx2x_nic_init(bp); /* Send LOAD_DONE command to MCP */ - if (!nomcp) { + if (!BP_NOMCP(bp)) { load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_DONE); if (!load_code) { BNX2X_ERR("MCP response failure, unloading\n"); + rc = -EBUSY; goto load_int_disable; } } @@ -4745,33 +5346,68 @@ static int bnx2x_nic_load(struct bnx2x *bp, int req_irq) for_each_queue(bp, i) napi_enable(&bnx2x_fp(bp, i, napi)); - if (bnx2x_setup_leading(bp)) + rc = bnx2x_setup_leading(bp); + if (rc) { +#ifdef BNX2X_STOP_ON_ERROR + bp->panic = 1; +#endif goto load_stop_netif; + } - for_each_nondefault_queue(bp, i) - if (bnx2x_setup_multi(bp, i)) - goto load_stop_netif; + if (CHIP_IS_E1H(bp)) + if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) { + BNX2X_ERR("!!! mf_cfg function disabled\n"); + bp->state = BNX2X_STATE_DISABLED; + } - bnx2x_set_mac_addr(bp); + if (bp->state == BNX2X_STATE_OPEN) + for_each_nondefault_queue(bp, i) { + rc = bnx2x_setup_multi(bp, i); + if (rc) + goto load_stop_netif; + } - bnx2x_initial_phy_init(bp); + if (CHIP_IS_E1(bp)) + bnx2x_set_mac_addr_e1(bp); + else + bnx2x_set_mac_addr_e1h(bp); + + if (bp->port.pmf) + bnx2x_initial_phy_init(bp); /* Start fast path */ - if (req_irq) { /* IRQ is only requested from bnx2x_open */ + switch (load_mode) { + case LOAD_NORMAL: + /* Tx queue should be only reenabled */ + netif_wake_queue(bp->dev); + bnx2x_set_rx_mode(bp->dev); + break; + + case LOAD_OPEN: + /* IRQ is only requested from bnx2x_open */ netif_start_queue(bp->dev); + bnx2x_set_rx_mode(bp->dev); if (bp->flags & USING_MSIX_FLAG) printk(KERN_INFO PFX "%s: using MSI-X\n", bp->dev->name); + break; - /* Otherwise Tx queue should be only reenabled */ - } else if (netif_running(bp->dev)) { - netif_wake_queue(bp->dev); + case LOAD_DIAG: bnx2x_set_rx_mode(bp->dev); + bp->state = BNX2X_STATE_DIAG; + break; + + default: + break; } + if (!bp->port.pmf) + bnx2x__link_status_update(bp); + /* start the timer */ mod_timer(&bp->timer, jiffies + bp->current_interval); + return 0; load_stop_netif: @@ -4781,7 +5417,7 @@ load_stop_netif: load_int_disable: bnx2x_int_disable_sync(bp); - bnx2x_free_skbs(bp); + /* Release IRQs */ bnx2x_free_irq(bp); load_error: @@ -4789,95 +5425,50 @@ load_error: /* TBD we really need to reset the chip if we want to recover from this */ - return -EBUSY; -} - - -static void bnx2x_reset_chip(struct bnx2x *bp, u32 reset_code) -{ - int port = bp->port; -#ifdef USE_DMAE - u32 wb_write[2]; -#endif - int base, i; - - DP(NETIF_MSG_IFDOWN, "reset called with code %x\n", reset_code); - - /* Do not rcv packets to BRB */ - REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK + port*4, 0x0); - /* Do not direct rcv packets that are not for MCP to the BRB */ - REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_NOT_MCP : - NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); - - /* Configure IGU and AEU */ - REG_WR(bp, HC_REG_CONFIG_0 + port*4, 0x1000); - REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, 0); - - /* TODO: Close Doorbell port? */ - - /* Clear ILT */ -#ifdef USE_DMAE - wb_write[0] = 0; - wb_write[1] = 0; -#endif - base = port * RQ_ONCHIP_AT_PORT_SIZE; - for (i = base; i < base + RQ_ONCHIP_AT_PORT_SIZE; i++) { -#ifdef USE_DMAE - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); -#else - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT, 0); - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT + 4, 0); -#endif - } - - if (reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) { - /* reset_common */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, - 0xd3ffff7f); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, - 0x1403); - } + return rc; } static int bnx2x_stop_multi(struct bnx2x *bp, int index) { - int rc; /* halt the connection */ bp->fp[index].state = BNX2X_FP_STATE_HALTING; bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, index, 0, 0, 0); - + /* Wait for completion */ rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, index, - &(bp->fp[index].state), 1); + &(bp->fp[index].state), 1); if (rc) /* timeout */ return rc; /* delete cfc entry */ bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_CFC_DEL, index, 0, 0, 1); - return bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_CLOSED, index, - &(bp->fp[index].state), 1); - + /* Wait for completion */ + rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_CLOSED, index, + &(bp->fp[index].state), 1); + return rc; } - static void bnx2x_stop_leading(struct bnx2x *bp) { u16 dsb_sp_prod_idx; /* if the other port is handling traffic, this can take a lot of time */ - int timeout = 500; + int cnt = 500; + int rc; might_sleep(); /* Send HALT ramrod */ bp->fp[0].state = BNX2X_FP_STATE_HALTING; - bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, 0, 0, 0, 0); + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, 0, 0, BP_CL_ID(bp), 0); - if (bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, 0, - &(bp->fp[0].state), 1)) + /* Wait for completion */ + rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, 0, + &(bp->fp[0].state), 1); + if (rc) /* timeout */ return; dsb_sp_prod_idx = *bp->dsb_sp_prod; @@ -4889,29 +5480,110 @@ static void bnx2x_stop_leading(struct bnx2x *bp) we are going to reset the chip anyway so there is not much to do if this times out */ - while ((dsb_sp_prod_idx == *bp->dsb_sp_prod) && timeout) { - timeout--; + while (dsb_sp_prod_idx == *bp->dsb_sp_prod) { msleep(1); - } - if (!timeout) { - DP(NETIF_MSG_IFDOWN, "timeout polling for completion " - "dsb_sp_prod 0x%x != dsb_sp_prod_idx 0x%x\n", - *bp->dsb_sp_prod, dsb_sp_prod_idx); + if (!cnt) { + DP(NETIF_MSG_IFDOWN, "timeout waiting for port del " + "dsb_sp_prod 0x%x != dsb_sp_prod_idx 0x%x\n", + *bp->dsb_sp_prod, dsb_sp_prod_idx); +#ifdef BNX2X_STOP_ON_ERROR + bnx2x_panic(); +#endif + break; + } + cnt--; } bp->state = BNX2X_STATE_CLOSING_WAIT4_UNLOAD; bp->fp[0].state = BNX2X_FP_STATE_CLOSED; } +static void bnx2x_reset_func(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + int base, i; + + /* Configure IGU */ + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); + + REG_WR(bp, HC_REG_CONFIG_0 + port*4, 0x1000); + + /* Clear ILT */ + base = FUNC_ILT_BASE(func); + for (i = base; i < base + ILT_PER_FUNC; i++) + bnx2x_ilt_wr(bp, i, 0); +} + +static void bnx2x_reset_port(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + u32 val; + + REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0); + + /* Do not rcv packets to BRB */ + REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK + port*4, 0x0); + /* Do not direct rcv packets that are not for MCP to the BRB */ + REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_NOT_MCP : + NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); + + /* Configure AEU */ + REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, 0); + + msleep(100); + /* Check for BRB port occupancy */ + val = REG_RD(bp, BRB1_REG_PORT_NUM_OCC_BLOCKS_0 + port*4); + if (val) + DP(NETIF_MSG_IFDOWN, + "BRB1 is not empty %d blooks are occupied\n", val); + + /* TODO: Close Doorbell port? */ +} + +static void bnx2x_reset_common(struct bnx2x *bp) +{ + /* reset_common */ + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0xd3ffff7f); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403); +} + +static void bnx2x_reset_chip(struct bnx2x *bp, u32 reset_code) +{ + DP(BNX2X_MSG_MCP, "function %d reset_code %x\n", + BP_FUNC(bp), reset_code); + + switch (reset_code) { + case FW_MSG_CODE_DRV_UNLOAD_COMMON: + bnx2x_reset_port(bp); + bnx2x_reset_func(bp); + bnx2x_reset_common(bp); + break; + + case FW_MSG_CODE_DRV_UNLOAD_PORT: + bnx2x_reset_port(bp); + bnx2x_reset_func(bp); + break; + + case FW_MSG_CODE_DRV_UNLOAD_FUNCTION: + bnx2x_reset_func(bp); + break; -static int bnx2x_nic_unload(struct bnx2x *bp, int free_irq) + default: + BNX2X_ERR("Unknown reset_code (0x%x) from MCP\n", reset_code); + break; + } +} + +/* msut be called with rtnl_lock */ +static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) { u32 reset_code = 0; - int i, timeout; + int i, cnt; bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; - del_timer_sync(&bp->timer); - bp->rx_mode = BNX2X_RX_MODE_NONE; bnx2x_set_storm_rx_mode(bp); @@ -4920,21 +5592,44 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int free_irq) bp->dev->trans_start = jiffies; /* prevent tx timeout */ } + del_timer_sync(&bp->timer); + SHMEM_WR(bp, func_mb[BP_FUNC(bp)].drv_pulse_mb, + (DRV_PULSE_ALWAYS_ALIVE | bp->fw_drv_pulse_wr_seq)); + /* Wait until all fast path tasks complete */ for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; - timeout = 1000; - while (bnx2x_has_work(fp) && (timeout--)) +#ifdef BNX2X_STOP_ON_ERROR +#ifdef __powerpc64__ + DP(NETIF_MSG_RX_STATUS, "fp->tpa_queue_used = 0x%lx\n", +#else + DP(NETIF_MSG_IFDOWN, "fp->tpa_queue_used = 0x%llx\n", +#endif + fp->tpa_queue_used); +#endif + cnt = 1000; + smp_rmb(); + while (bnx2x_has_work(fp)) { msleep(1); - if (!timeout) - BNX2X_ERR("timeout waiting for queue[%d]\n", i); + if (!cnt) { + BNX2X_ERR("timeout waiting for queue[%d]\n", + i); +#ifdef BNX2X_STOP_ON_ERROR + bnx2x_panic(); + return -EBUSY; +#else + break; +#endif + } + cnt--; + smp_rmb(); + } } - /* Wait until stat ramrod returns and all SP tasks complete */ - timeout = 1000; - while ((bp->stat_pending || (bp->spq_left != MAX_SPQ_PENDING)) && - (timeout--)) + /* Wait until all slow path tasks complete */ + cnt = 1000; + while ((bp->spq_left != MAX_SPQ_PENDING) && cnt--) msleep(1); for_each_queue(bp, i) @@ -4942,59 +5637,84 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int free_irq) /* Disable interrupts after Tx and Rx are disabled on stack level */ bnx2x_int_disable_sync(bp); + /* Release IRQs */ + bnx2x_free_irq(bp); + if (bp->flags & NO_WOL_FLAG) reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP; else if (bp->wol) { - u32 emac_base = bp->port ? GRCBASE_EMAC0 : GRCBASE_EMAC1; + u32 emac_base = BP_PORT(bp) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; u8 *mac_addr = bp->dev->dev_addr; - u32 val = (EMAC_MODE_MPKT | EMAC_MODE_MPKT_RCVD | - EMAC_MODE_ACPI_RCVD); - - EMAC_WR(EMAC_REG_EMAC_MODE, val); + u32 val; + /* The mac address is written to entries 1-4 to + preserve entry 0 which is used by the PMF */ val = (mac_addr[0] << 8) | mac_addr[1]; - EMAC_WR(EMAC_REG_EMAC_MAC_MATCH, val); + EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + (BP_E1HVN(bp) + 1)*8, val); val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | (mac_addr[4] << 8) | mac_addr[5]; - EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + 4, val); + EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + (BP_E1HVN(bp) + 1)*8 + 4, + val); reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_EN; } else reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; - /* Close multi and leading connections */ + /* Close multi and leading connections + Completions for ramrods are collected in a synchronous way */ for_each_nondefault_queue(bp, i) if (bnx2x_stop_multi(bp, i)) goto unload_error; - bnx2x_stop_leading(bp); + if (CHIP_IS_E1H(bp)) + REG_WR(bp, NIG_REG_LLH0_FUNC_EN + BP_PORT(bp)*8, 0); + + bnx2x_stop_leading(bp); +#ifdef BNX2X_STOP_ON_ERROR + /* If ramrod completion timed out - break here! */ + if (bp->panic) { + BNX2X_ERR("Stop leading failed!\n"); + return -EBUSY; + } +#endif + if ((bp->state != BNX2X_STATE_CLOSING_WAIT4_UNLOAD) || (bp->fp[0].state != BNX2X_FP_STATE_CLOSED)) { - DP(NETIF_MSG_IFDOWN, "failed to close leading properly!" - "state 0x%x fp[0].state 0x%x", + DP(NETIF_MSG_IFDOWN, "failed to close leading properly! " + "state 0x%x fp[0].state 0x%x\n", bp->state, bp->fp[0].state); } unload_error: - bnx2x__link_reset(bp); - - if (!nomcp) + if (!BP_NOMCP(bp)) reset_code = bnx2x_fw_command(bp, reset_code); - else - reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON; + else { + DP(NETIF_MSG_IFDOWN, "NO MCP load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + load_count[0]--; + load_count[1 + BP_PORT(bp)]--; + DP(NETIF_MSG_IFDOWN, "NO MCP new load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + if (load_count[0] == 0) + reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON; + else if (load_count[1 + BP_PORT(bp)] == 0) + reset_code = FW_MSG_CODE_DRV_UNLOAD_PORT; + else + reset_code = FW_MSG_CODE_DRV_UNLOAD_FUNCTION; + } - /* Release IRQs */ - if (free_irq) - bnx2x_free_irq(bp); + if ((reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) || + (reset_code == FW_MSG_CODE_DRV_UNLOAD_PORT)) + bnx2x__link_reset(bp); /* Reset the chip */ bnx2x_reset_chip(bp, reset_code); /* Report UNLOAD_DONE to MCP */ - if (!nomcp) + if (!BP_NOMCP(bp)) bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE); /* Free SKBs and driver internals */ @@ -5008,6 +5728,29 @@ unload_error: return 0; } +static void bnx2x_reset_task(struct work_struct *work) +{ + struct bnx2x *bp = container_of(work, struct bnx2x, reset_task); + +#ifdef BNX2X_STOP_ON_ERROR + BNX2X_ERR("reset task called but STOP_ON_ERROR defined" + " so reset not done to allow debug dump,\n" + KERN_ERR " you will need to reboot when done\n"); + return; +#endif + + rtnl_lock(); + + if (!netif_running(bp->dev)) + goto reset_task_exit; + + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + bnx2x_nic_load(bp, LOAD_NORMAL); + +reset_task_exit: + rtnl_unlock(); +} + /* end of nic load/unload */ /* ethtool_ops */ @@ -5016,9 +5759,139 @@ unload_error: * Init service functions */ -static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) +static void __devinit bnx2x_undi_unload(struct bnx2x *bp) +{ + u32 val; + + /* Check if there is any driver already loaded */ + val = REG_RD(bp, MISC_REG_UNPREPARED); + if (val == 0x1) { + /* Check if it is the UNDI driver + * UNDI driver initializes CID offset for normal bell to 0x7 + */ + val = REG_RD(bp, DORQ_REG_NORM_CID_OFST); + if (val == 0x7) { + u32 reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; + /* save our func and fw_seq */ + int func = BP_FUNC(bp); + u16 fw_seq = bp->fw_seq; + + BNX2X_DEV_INFO("UNDI is active! reset device\n"); + + /* try unload UNDI on port 0 */ + bp->func = 0; + bp->fw_seq = (SHMEM_RD(bp, + func_mb[bp->func].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK); + + reset_code = bnx2x_fw_command(bp, reset_code); + bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE); + + /* if UNDI is loaded on the other port */ + if (reset_code != FW_MSG_CODE_DRV_UNLOAD_COMMON) { + + bp->func = 1; + bp->fw_seq = (SHMEM_RD(bp, + func_mb[bp->func].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK); + + bnx2x_fw_command(bp, + DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS); + bnx2x_fw_command(bp, + DRV_MSG_CODE_UNLOAD_DONE); + + /* restore our func and fw_seq */ + bp->func = func; + bp->fw_seq = fw_seq; + } + + /* reset device */ + REG_WR(bp, + GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0xd3ffff7f); + REG_WR(bp, + GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, + 0x1403); + } + } +} + +static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) +{ + u32 val, val2, val3, val4, id; + + /* Get the chip revision id and number. */ + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ + val = REG_RD(bp, MISC_REG_CHIP_NUM); + id = ((val & 0xffff) << 16); + val = REG_RD(bp, MISC_REG_CHIP_REV); + id |= ((val & 0xf) << 12); + val = REG_RD(bp, MISC_REG_CHIP_METAL); + id |= ((val & 0xff) << 4); + REG_RD(bp, MISC_REG_BOND_ID); + id |= (val & 0xf); + bp->common.chip_id = id; + bp->link_params.chip_id = bp->common.chip_id; + BNX2X_DEV_INFO("chip ID is 0x%x\n", id); + + val = REG_RD(bp, MCP_REG_MCPR_NVM_CFG4); + bp->common.flash_size = (NVRAM_1MB_SIZE << + (val & MCPR_NVM_CFG4_FLASH_SIZE)); + BNX2X_DEV_INFO("flash_size 0x%x (%d)\n", + bp->common.flash_size, bp->common.flash_size); + + bp->common.shmem_base = REG_RD(bp, MISC_REG_SHARED_MEM_ADDR); + bp->link_params.shmem_base = bp->common.shmem_base; + BNX2X_DEV_INFO("shmem offset is 0x%x\n", bp->common.shmem_base); + + if (!bp->common.shmem_base || + (bp->common.shmem_base < 0xA0000) || + (bp->common.shmem_base >= 0xC0000)) { + BNX2X_DEV_INFO("MCP not active\n"); + bp->flags |= NO_MCP_FLAG; + return; + } + + val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]); + if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) + != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) + BNX2X_ERR("BAD MCP validity signature\n"); + + bp->common.hw_config = SHMEM_RD(bp, dev_info.shared_hw_config.config); + bp->common.board = SHMEM_RD(bp, dev_info.shared_hw_config.board); + + BNX2X_DEV_INFO("hw_config 0x%08x board 0x%08x\n", + bp->common.hw_config, bp->common.board); + + bp->link_params.hw_led_mode = ((bp->common.hw_config & + SHARED_HW_CFG_LED_MODE_MASK) >> + SHARED_HW_CFG_LED_MODE_SHIFT); + + val = SHMEM_RD(bp, dev_info.bc_rev) >> 8; + bp->common.bc_ver = val; + BNX2X_DEV_INFO("bc_ver %X\n", val); + if (val < BNX2X_BC_VER) { + /* for now only warn + * later we might need to enforce this */ + BNX2X_ERR("This driver needs bc_ver %X but found %X," + " please upgrade BC\n", BNX2X_BC_VER, val); + } + BNX2X_DEV_INFO("%sWoL Capable\n", + (bp->flags & NO_WOL_FLAG)? "Not " : ""); + + val = SHMEM_RD(bp, dev_info.shared_hw_config.part_num); + val2 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[4]); + val3 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[8]); + val4 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[12]); + + printk(KERN_INFO PFX "part number %X-%X-%X-%X\n", + val, val2, val3, val4); +} + +static void __devinit bnx2x_link_settings_supported(struct bnx2x *bp, + u32 switch_cfg) { - int port = bp->port; + int port = BP_PORT(bp); u32 ext_phy_type; switch (switch_cfg) { @@ -5032,31 +5905,33 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) BNX2X_DEV_INFO("ext_phy_type 0x%x (Direct)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_2500baseX_Full | - SUPPORTED_TP | SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_2500baseX_Full | + SUPPORTED_TP | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: BNX2X_DEV_INFO("ext_phy_type 0x%x (5482)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_TP | SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_TP | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; default: @@ -5066,9 +5941,9 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) return; } - bp->phy_addr = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR + - port*0x10); - BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->phy_addr); + bp->port.phy_addr = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR + + port*0x10); + BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr); break; case SWITCH_CFG_10G: @@ -5081,75 +5956,75 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) BNX2X_DEV_INFO("ext_phy_type 0x%x (Direct)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_2500baseX_Full | - SUPPORTED_10000baseT_Full | - SUPPORTED_TP | SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_2500baseX_Full | + SUPPORTED_10000baseT_Full | + SUPPORTED_TP | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: BNX2X_DEV_INFO("ext_phy_type 0x%x (8705)\n", - ext_phy_type); + ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: BNX2X_DEV_INFO("ext_phy_type 0x%x (8706)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_FIBRE | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: BNX2X_DEV_INFO("ext_phy_type 0x%x (8072)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: BNX2X_DEV_INFO("ext_phy_type 0x%x (8073)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_2500baseX_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_2500baseX_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: BNX2X_DEV_INFO("ext_phy_type 0x%x (SFX7101)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_TP | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_TP | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: @@ -5164,61 +6039,61 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) return; } - bp->phy_addr = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR + - port*0x18); - BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->phy_addr); + bp->port.phy_addr = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR + + port*0x18); + BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr); break; default: BNX2X_ERR("BAD switch_cfg link_config 0x%x\n", - bp->link_config); + bp->port.link_config); return; } - bp->link_params.phy_addr = bp->phy_addr; + bp->link_params.phy_addr = bp->port.phy_addr; /* mask what we support according to speed_cap_mask */ if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)) - bp->supported &= ~SUPPORTED_10baseT_Half; + bp->port.supported &= ~SUPPORTED_10baseT_Half; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL)) - bp->supported &= ~SUPPORTED_10baseT_Full; + bp->port.supported &= ~SUPPORTED_10baseT_Full; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)) - bp->supported &= ~SUPPORTED_100baseT_Half; + bp->port.supported &= ~SUPPORTED_100baseT_Half; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL)) - bp->supported &= ~SUPPORTED_100baseT_Full; + bp->port.supported &= ~SUPPORTED_100baseT_Full; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) - bp->supported &= ~(SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full); + bp->port.supported &= ~(SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full); if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G)) - bp->supported &= ~SUPPORTED_2500baseX_Full; + bp->port.supported &= ~SUPPORTED_2500baseX_Full; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) - bp->supported &= ~SUPPORTED_10000baseT_Full; + bp->port.supported &= ~SUPPORTED_10000baseT_Full; - BNX2X_DEV_INFO("supported 0x%x\n", bp->supported); + BNX2X_DEV_INFO("supported 0x%x\n", bp->port.supported); } -static void bnx2x_link_settings_requested(struct bnx2x *bp) +static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp) { bp->link_params.req_duplex = DUPLEX_FULL; - switch (bp->link_config & PORT_FEATURE_LINK_SPEED_MASK) { + switch (bp->port.link_config & PORT_FEATURE_LINK_SPEED_MASK) { case PORT_FEATURE_LINK_SPEED_AUTO: - if (bp->supported & SUPPORTED_Autoneg) { + if (bp->port.supported & SUPPORTED_Autoneg) { bp->link_params.req_line_speed = SPEED_AUTO_NEG; - bp->advertising = bp->supported; + bp->port.advertising = bp->port.supported; } else { u32 ext_phy_type = XGXS_EXT_PHY_TYPE(bp->link_params.ext_phy_config); @@ -5229,7 +6104,7 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706)) { /* force 10G, no AN */ bp->link_params.req_line_speed = SPEED_10000; - bp->advertising = + bp->port.advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); break; @@ -5237,98 +6112,98 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " Autoneg not supported\n", - bp->link_config); + bp->port.link_config); return; } break; case PORT_FEATURE_LINK_SPEED_10M_FULL: - if (bp->supported & SUPPORTED_10baseT_Full) { + if (bp->port.supported & SUPPORTED_10baseT_Full) { bp->link_params.req_line_speed = SPEED_10; - bp->advertising = (ADVERTISED_10baseT_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_10baseT_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_10M_HALF: - if (bp->supported & SUPPORTED_10baseT_Half) { + if (bp->port.supported & SUPPORTED_10baseT_Half) { bp->link_params.req_line_speed = SPEED_10; bp->link_params.req_duplex = DUPLEX_HALF; - bp->advertising = (ADVERTISED_10baseT_Half | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_10baseT_Half | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_100M_FULL: - if (bp->supported & SUPPORTED_100baseT_Full) { + if (bp->port.supported & SUPPORTED_100baseT_Full) { bp->link_params.req_line_speed = SPEED_100; - bp->advertising = (ADVERTISED_100baseT_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_100baseT_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_100M_HALF: - if (bp->supported & SUPPORTED_100baseT_Half) { + if (bp->port.supported & SUPPORTED_100baseT_Half) { bp->link_params.req_line_speed = SPEED_100; bp->link_params.req_duplex = DUPLEX_HALF; - bp->advertising = (ADVERTISED_100baseT_Half | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_100baseT_Half | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_1G: - if (bp->supported & SUPPORTED_1000baseT_Full) { + if (bp->port.supported & SUPPORTED_1000baseT_Full) { bp->link_params.req_line_speed = SPEED_1000; - bp->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_1000baseT_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_2_5G: - if (bp->supported & SUPPORTED_2500baseX_Full) { + if (bp->port.supported & SUPPORTED_2500baseX_Full) { bp->link_params.req_line_speed = SPEED_2500; - bp->advertising = (ADVERTISED_2500baseX_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_2500baseX_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } @@ -5337,15 +6212,15 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) case PORT_FEATURE_LINK_SPEED_10G_CX4: case PORT_FEATURE_LINK_SPEED_10G_KX4: case PORT_FEATURE_LINK_SPEED_10G_KR: - if (bp->supported & SUPPORTED_10000baseT_Full) { + if (bp->port.supported & SUPPORTED_10000baseT_Full) { bp->link_params.req_line_speed = SPEED_10000; - bp->advertising = (ADVERTISED_10000baseT_Full | - ADVERTISED_FIBRE); + bp->port.advertising = (ADVERTISED_10000baseT_Full | + ADVERTISED_FIBRE); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } @@ -5354,64 +6229,33 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) default: BNX2X_ERR("NVRAM config error. " "BAD link speed link_config 0x%x\n", - bp->link_config); + bp->port.link_config); bp->link_params.req_line_speed = SPEED_AUTO_NEG; - bp->advertising = bp->supported; + bp->port.advertising = bp->port.supported; break; } - bp->link_params.req_flow_ctrl = (bp->link_config & - PORT_FEATURE_FLOW_CONTROL_MASK); + bp->link_params.req_flow_ctrl = (bp->port.link_config & + PORT_FEATURE_FLOW_CONTROL_MASK); if ((bp->link_params.req_flow_ctrl == FLOW_CTRL_AUTO) && - (!bp->supported & SUPPORTED_Autoneg)) + (!bp->port.supported & SUPPORTED_Autoneg)) bp->link_params.req_flow_ctrl = FLOW_CTRL_NONE; BNX2X_DEV_INFO("req_line_speed %d req_duplex %d req_flow_ctrl 0x%x" " advertising 0x%x\n", bp->link_params.req_line_speed, bp->link_params.req_duplex, - bp->link_params.req_flow_ctrl, bp->advertising); + bp->link_params.req_flow_ctrl, bp->port.advertising); } -static void bnx2x_get_hwinfo(struct bnx2x *bp) +static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) { - u32 val, val2, val3, val4, id; - int port = bp->port; - - bp->shmem_base = REG_RD(bp, MISC_REG_SHARED_MEM_ADDR); - BNX2X_DEV_INFO("shmem offset is %x\n", bp->shmem_base); - - /* Get the chip revision id and number. */ - /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ - val = REG_RD(bp, MISC_REG_CHIP_NUM); - id = ((val & 0xffff) << 16); - val = REG_RD(bp, MISC_REG_CHIP_REV); - id |= ((val & 0xf) << 12); - val = REG_RD(bp, MISC_REG_CHIP_METAL); - id |= ((val & 0xff) << 4); - REG_RD(bp, MISC_REG_BOND_ID); - id |= (val & 0xf); - bp->chip_id = id; - BNX2X_DEV_INFO("chip ID is %x\n", id); + int port = BP_PORT(bp); + u32 val, val2; bp->link_params.bp = bp; + bp->link_params.port = port; - if (!bp->shmem_base || (bp->shmem_base != 0xAF900)) { - BNX2X_DEV_INFO("MCP not active\n"); - nomcp = 1; - goto set_mac; - } - - val = SHMEM_RD(bp, validity_map[port]); - if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - BNX2X_ERR("BAD MCP validity signature\n"); - - bp->fw_seq = (SHMEM_RD(bp, func_mb[port].drv_mb_header) & - DRV_MSG_SEQ_NUMBER_MASK); - - bp->hw_config = SHMEM_RD(bp, dev_info.shared_hw_config.config); - bp->board = SHMEM_RD(bp, dev_info.shared_hw_config.board); bp->link_params.serdes_config = SHMEM_RD(bp, dev_info.port_hw_config[port].serdes_config); bp->link_params.lane_config = @@ -5423,19 +6267,18 @@ static void bnx2x_get_hwinfo(struct bnx2x *bp) SHMEM_RD(bp, dev_info.port_hw_config[port].speed_capability_mask); - bp->link_config = + bp->port.link_config = SHMEM_RD(bp, dev_info.port_feature_config[port].link_config); - BNX2X_DEV_INFO("serdes_config (%08x) lane_config (%08x)\n" - KERN_INFO " ext_phy_config (%08x) speed_cap_mask (%08x)" - " link_config (%08x)\n", + BNX2X_DEV_INFO("serdes_config 0x%08x lane_config 0x%08x\n" + KERN_INFO " ext_phy_config 0x%08x speed_cap_mask 0x%08x" + " link_config 0x%08x\n", bp->link_params.serdes_config, bp->link_params.lane_config, bp->link_params.ext_phy_config, - bp->link_params.speed_cap_mask, - bp->link_config); + bp->link_params.speed_cap_mask, bp->port.link_config); - bp->link_params.switch_cfg = (bp->link_config & + bp->link_params.switch_cfg = (bp->port.link_config & PORT_FEATURE_CONNECTED_SWITCH_MASK); bnx2x_link_settings_supported(bp, bp->link_params.switch_cfg); @@ -5451,43 +6294,126 @@ static void bnx2x_get_hwinfo(struct bnx2x *bp) bp->dev->dev_addr[5] = (u8)(val & 0xff); memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN); memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN); +} + +static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); + u32 val, val2; + int rc = 0; + bnx2x_get_common_hwinfo(bp); + bp->e1hov = 0; + bp->e1hmf = 0; + if (CHIP_IS_E1H(bp)) { + bp->mf_config = + SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); - val = SHMEM_RD(bp, dev_info.shared_hw_config.part_num); - val2 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[4]); - val3 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[8]); - val4 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[12]); + val = + (SHMEM_RD(bp, mf_cfg.func_mf_config[func].e1hov_tag) & + FUNC_MF_CFG_E1HOV_TAG_MASK); + if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) { - printk(KERN_INFO PFX "part number %X-%X-%X-%X\n", - val, val2, val3, val4); + bp->e1hov = val; + bp->e1hmf = 1; + BNX2X_DEV_INFO("MF mode E1HOV for func %d is %d " + "(0x%04x)\n", + func, bp->e1hov, bp->e1hov); + } else { + BNX2X_DEV_INFO("Single function mode\n"); + if (BP_E1HVN(bp)) { + BNX2X_ERR("!!! No valid E1HOV for func %d," + " aborting\n", func); + rc = -EPERM; + } + } + } - /* bc ver */ - if (!nomcp) { - bp->bc_ver = val = ((SHMEM_RD(bp, dev_info.bc_rev)) >> 8); - BNX2X_DEV_INFO("bc_ver %X\n", val); - if (val < BNX2X_BC_VER) { - /* for now only warn - * later we might need to enforce this */ - BNX2X_ERR("This driver needs bc_ver %X but found %X," - " please upgrade BC\n", BNX2X_BC_VER, val); + if (!BP_NOMCP(bp)) { + bnx2x_get_port_hwinfo(bp); + + bp->fw_seq = (SHMEM_RD(bp, func_mb[func].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK); + BNX2X_DEV_INFO("fw_seq 0x%08x\n", bp->fw_seq); + } + + if (IS_E1HMF(bp)) { + val2 = SHMEM_RD(bp, mf_cfg.func_mf_config[func].mac_upper); + val = SHMEM_RD(bp, mf_cfg.func_mf_config[func].mac_lower); + if ((val2 != FUNC_MF_CFG_UPPERMAC_DEFAULT) && + (val != FUNC_MF_CFG_LOWERMAC_DEFAULT)) { + bp->dev->dev_addr[0] = (u8)(val2 >> 8 & 0xff); + bp->dev->dev_addr[1] = (u8)(val2 & 0xff); + bp->dev->dev_addr[2] = (u8)(val >> 24 & 0xff); + bp->dev->dev_addr[3] = (u8)(val >> 16 & 0xff); + bp->dev->dev_addr[4] = (u8)(val >> 8 & 0xff); + bp->dev->dev_addr[5] = (u8)(val & 0xff); + memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, + ETH_ALEN); + memcpy(bp->dev->perm_addr, bp->dev->dev_addr, + ETH_ALEN); } - } else { - bp->bc_ver = 0; + + return rc; } - val = REG_RD(bp, MCP_REG_MCPR_NVM_CFG4); - bp->flash_size = (NVRAM_1MB_SIZE << (val & MCPR_NVM_CFG4_FLASH_SIZE)); - BNX2X_DEV_INFO("flash_size 0x%x (%d)\n", - bp->flash_size, bp->flash_size); + if (BP_NOMCP(bp)) { + /* only supposed to happen on emulation/FPGA */ + BNX2X_ERR("warning rendom MAC workaround active\n"); + random_ether_addr(bp->dev->dev_addr); + memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN); + } - return; + return rc; +} + +static int __devinit bnx2x_init_bp(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); + int rc; + + if (nomcp) + bp->flags |= NO_MCP_FLAG; -set_mac: /* only supposed to happen on emulation/FPGA */ - BNX2X_ERR("warning rendom MAC workaround active\n"); - random_ether_addr(bp->dev->dev_addr); - memcpy(bp->dev->perm_addr, bp->dev->dev_addr, 6); + mutex_init(&bp->port.phy_mutex); + INIT_WORK(&bp->sp_task, bnx2x_sp_task); + INIT_WORK(&bp->reset_task, bnx2x_reset_task); + + rc = bnx2x_get_hwinfo(bp); + + /* need to reset chip if undi was active */ + if (!BP_NOMCP(bp)) + bnx2x_undi_unload(bp); + + if (CHIP_REV_IS_FPGA(bp)) + printk(KERN_ERR PFX "FPGA detected\n"); + + if (BP_NOMCP(bp) && (func == 0)) + printk(KERN_ERR PFX + "MCP disabled, must load devices in order!\n"); + + bp->tx_ring_size = MAX_TX_AVAIL; + bp->rx_ring_size = MAX_RX_AVAIL; + + bp->rx_csum = 1; + bp->rx_offset = 0; + + bp->tx_ticks = 50; + bp->rx_ticks = 25; + + bp->stats_ticks = 1000000 & 0xffff00; + + bp->timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ); + bp->current_interval = (poll ? poll : bp->timer_interval); + + init_timer(&bp->timer); + bp->timer.expires = jiffies + bp->current_interval; + bp->timer.data = (unsigned long) bp; + bp->timer.function = bnx2x_timer; + + return rc; } /* @@ -5500,8 +6426,8 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct bnx2x *bp = netdev_priv(dev); - cmd->supported = bp->supported; - cmd->advertising = bp->advertising; + cmd->supported = bp->port.supported; + cmd->advertising = bp->port.advertising; if (netif_carrier_ok(dev)) { cmd->speed = bp->link_vars.line_speed; @@ -5510,6 +6436,14 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->speed = bp->link_params.req_line_speed; cmd->duplex = bp->link_params.req_duplex; } + if (IS_E1HMF(bp)) { + u16 vn_max_rate; + + vn_max_rate = ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >> + FUNC_MF_CFG_MAX_BW_SHIFT) * 100; + if (vn_max_rate < cmd->speed) + cmd->speed = vn_max_rate; + } if (bp->link_params.switch_cfg == SWITCH_CFG_10G) { u32 ext_phy_type = @@ -5541,7 +6475,7 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) } else cmd->port = PORT_TP; - cmd->phy_address = bp->phy_addr; + cmd->phy_address = bp->port.phy_addr; cmd->transceiver = XCVR_INTERNAL; if (bp->link_params.req_line_speed == SPEED_AUTO_NEG) @@ -5568,6 +6502,9 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct bnx2x *bp = netdev_priv(dev); u32 advertising; + if (IS_E1HMF(bp)) + return 0; + DP(NETIF_MSG_LINK, "ethtool_cmd: cmd %d\n" DP_LEVEL " supported 0x%x advertising 0x%x speed %d\n" DP_LEVEL " duplex %d port %d phy_address %d transceiver %d\n" @@ -5577,24 +6514,25 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->autoneg, cmd->maxtxpkt, cmd->maxrxpkt); if (cmd->autoneg == AUTONEG_ENABLE) { - if (!(bp->supported & SUPPORTED_Autoneg)) { - DP(NETIF_MSG_LINK, "Aotoneg not supported\n"); + if (!(bp->port.supported & SUPPORTED_Autoneg)) { + DP(NETIF_MSG_LINK, "Autoneg not supported\n"); return -EINVAL; } /* advertise the requested speed and duplex if supported */ - cmd->advertising &= bp->supported; + cmd->advertising &= bp->port.supported; bp->link_params.req_line_speed = SPEED_AUTO_NEG; bp->link_params.req_duplex = DUPLEX_FULL; - bp->advertising |= (ADVERTISED_Autoneg | cmd->advertising); + bp->port.advertising |= (ADVERTISED_Autoneg | + cmd->advertising); } else { /* forced speed */ /* advertise the requested speed and duplex if supported */ switch (cmd->speed) { case SPEED_10: if (cmd->duplex == DUPLEX_FULL) { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_10baseT_Full)) { DP(NETIF_MSG_LINK, "10M full not supported\n"); @@ -5604,7 +6542,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) advertising = (ADVERTISED_10baseT_Full | ADVERTISED_TP); } else { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_10baseT_Half)) { DP(NETIF_MSG_LINK, "10M half not supported\n"); @@ -5618,7 +6556,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) case SPEED_100: if (cmd->duplex == DUPLEX_FULL) { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_100baseT_Full)) { DP(NETIF_MSG_LINK, "100M full not supported\n"); @@ -5628,7 +6566,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) advertising = (ADVERTISED_100baseT_Full | ADVERTISED_TP); } else { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_100baseT_Half)) { DP(NETIF_MSG_LINK, "100M half not supported\n"); @@ -5646,7 +6584,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return -EINVAL; } - if (!(bp->supported & SUPPORTED_1000baseT_Full)) { + if (!(bp->port.supported & SUPPORTED_1000baseT_Full)) { DP(NETIF_MSG_LINK, "1G full not supported\n"); return -EINVAL; } @@ -5662,7 +6600,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return -EINVAL; } - if (!(bp->supported & SUPPORTED_2500baseX_Full)) { + if (!(bp->port.supported & SUPPORTED_2500baseX_Full)) { DP(NETIF_MSG_LINK, "2.5G full not supported\n"); return -EINVAL; @@ -5678,7 +6616,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return -EINVAL; } - if (!(bp->supported & SUPPORTED_10000baseT_Full)) { + if (!(bp->port.supported & SUPPORTED_10000baseT_Full)) { DP(NETIF_MSG_LINK, "10G full not supported\n"); return -EINVAL; } @@ -5694,16 +6632,18 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) bp->link_params.req_line_speed = cmd->speed; bp->link_params.req_duplex = cmd->duplex; - bp->advertising = advertising; + bp->port.advertising = advertising; } DP(NETIF_MSG_LINK, "req_line_speed %d\n" DP_LEVEL " req_duplex %d advertising 0x%x\n", bp->link_params.req_line_speed, bp->link_params.req_duplex, - bp->advertising); + bp->port.advertising); - bnx2x_stop_stats(bp); - bnx2x_link_set(bp); + if (netif_running(dev)) { + bnx2x_stop_stats(bp); + bnx2x_link_set(bp); + } return 0; } @@ -5720,21 +6660,23 @@ static void bnx2x_get_drvinfo(struct net_device *dev, strcpy(info->version, DRV_MODULE_VERSION); phy_fw_ver[0] = '\0'; - bnx2x_phy_hw_lock(bp); - bnx2x_get_ext_phy_fw_version(&bp->link_params, - (bp->state != BNX2X_STATE_CLOSED), - phy_fw_ver, PHY_FW_VER_LEN); - bnx2x_phy_hw_unlock(bp); + if (bp->port.pmf) { + bnx2x_phy_hw_lock(bp); + bnx2x_get_ext_phy_fw_version(&bp->link_params, + (bp->state != BNX2X_STATE_CLOSED), + phy_fw_ver, PHY_FW_VER_LEN); + bnx2x_phy_hw_unlock(bp); + } snprintf(info->fw_version, 32, "%d.%d.%d:%d BC:%x%s%s", BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION, BCM_5710_FW_REVISION_VERSION, - BCM_5710_FW_COMPILE_FLAGS, bp->bc_ver, + BCM_5710_FW_COMPILE_FLAGS, bp->common.bc_ver, ((phy_fw_ver[0] != '\0')? " PHY:":""), phy_fw_ver); strcpy(info->bus_info, pci_name(bp->pdev)); info->n_stats = BNX2X_NUM_STATS; info->testinfo_len = BNX2X_NUM_TESTS; - info->eedump_len = bp->flash_size; + info->eedump_len = bp->common.flash_size; info->regdump_len = 0; } @@ -5767,9 +6709,9 @@ static int bnx2x_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) return -EINVAL; bp->wol = 1; - } else { + } else bp->wol = 0; - } + return 0; } @@ -5792,13 +6734,13 @@ static int bnx2x_nway_reset(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); - if (bp->state != BNX2X_STATE_OPEN) { - DP(NETIF_MSG_PROBE, "state is %x, returning\n", bp->state); - return -EAGAIN; - } + if (!bp->port.pmf) + return 0; - bnx2x_stop_stats(bp); - bnx2x_link_set(bp); + if (netif_running(dev)) { + bnx2x_stop_stats(bp); + bnx2x_link_set(bp); + } return 0; } @@ -5807,12 +6749,12 @@ static int bnx2x_get_eeprom_len(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); - return bp->flash_size; + return bp->common.flash_size; } static int bnx2x_acquire_nvram_lock(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int count, i; u32 val = 0; @@ -5834,7 +6776,7 @@ static int bnx2x_acquire_nvram_lock(struct bnx2x *bp) } if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) { - DP(NETIF_MSG_NVM, "cannot get access to nvram interface\n"); + DP(BNX2X_MSG_NVM, "cannot get access to nvram interface\n"); return -EBUSY; } @@ -5843,7 +6785,7 @@ static int bnx2x_acquire_nvram_lock(struct bnx2x *bp) static int bnx2x_release_nvram_lock(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int count, i; u32 val = 0; @@ -5865,7 +6807,7 @@ static int bnx2x_release_nvram_lock(struct bnx2x *bp) } if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) { - DP(NETIF_MSG_NVM, "cannot free access to nvram interface\n"); + DP(BNX2X_MSG_NVM, "cannot free access to nvram interface\n"); return -EBUSY; } @@ -5929,7 +6871,6 @@ static int bnx2x_nvram_read_dword(struct bnx2x *bp, u32 offset, u32 *ret_val, if (val & MCPR_NVM_COMMAND_DONE) { val = REG_RD(bp, MCP_REG_MCPR_NVM_READ); - DP(NETIF_MSG_NVM, "val 0x%08x\n", val); /* we read nvram data in cpu order * but ethtool sees it as an array of bytes * converting to big-endian will do the work */ @@ -5951,16 +6892,16 @@ static int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf, u32 val; if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { - DP(NETIF_MSG_NVM, + DP(BNX2X_MSG_NVM, "Invalid parameter: offset 0x%x buf_size 0x%x\n", offset, buf_size); return -EINVAL; } - if (offset + buf_size > bp->flash_size) { - DP(NETIF_MSG_NVM, "Invalid parameter: offset (0x%x) +" + if (offset + buf_size > bp->common.flash_size) { + DP(BNX2X_MSG_NVM, "Invalid parameter: offset (0x%x) +" " buf_size (0x%x) > flash_size (0x%x)\n", - offset, buf_size, bp->flash_size); + offset, buf_size, bp->common.flash_size); return -EINVAL; } @@ -6004,7 +6945,7 @@ static int bnx2x_get_eeprom(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); int rc; - DP(NETIF_MSG_NVM, "ethtool_eeprom: cmd %d\n" + DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n" DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n", eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset, eeprom->len, eeprom->len); @@ -6066,10 +7007,10 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf, u32 align_offset; u32 val; - if (offset + buf_size > bp->flash_size) { - DP(NETIF_MSG_NVM, "Invalid parameter: offset (0x%x) +" + if (offset + buf_size > bp->common.flash_size) { + DP(BNX2X_MSG_NVM, "Invalid parameter: offset (0x%x) +" " buf_size (0x%x) > flash_size (0x%x)\n", - offset, buf_size, bp->flash_size); + offset, buf_size, bp->common.flash_size); return -EINVAL; } @@ -6093,8 +7034,6 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf, * convert it back to cpu order */ val = be32_to_cpu(val); - DP(NETIF_MSG_NVM, "val 0x%08x\n", val); - rc = bnx2x_nvram_write_dword(bp, align_offset, val, cmd_flags); } @@ -6114,21 +7053,20 @@ static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf, u32 val; u32 written_so_far; - if (buf_size == 1) { /* ethtool */ + if (buf_size == 1) /* ethtool */ return bnx2x_nvram_write1(bp, offset, data_buf, buf_size); - } if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { - DP(NETIF_MSG_NVM, + DP(BNX2X_MSG_NVM, "Invalid parameter: offset 0x%x buf_size 0x%x\n", offset, buf_size); return -EINVAL; } - if (offset + buf_size > bp->flash_size) { - DP(NETIF_MSG_NVM, "Invalid parameter: offset (0x%x) +" + if (offset + buf_size > bp->common.flash_size) { + DP(BNX2X_MSG_NVM, "Invalid parameter: offset (0x%x) +" " buf_size (0x%x) > flash_size (0x%x)\n", - offset, buf_size, bp->flash_size); + offset, buf_size, bp->common.flash_size); return -EINVAL; } @@ -6151,7 +7089,6 @@ static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf, cmd_flags |= MCPR_NVM_COMMAND_FIRST; memcpy(&val, data_buf, 4); - DP(NETIF_MSG_NVM, "val 0x%08x\n", val); rc = bnx2x_nvram_write_dword(bp, offset, val, cmd_flags); @@ -6175,7 +7112,7 @@ static int bnx2x_set_eeprom(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); int rc; - DP(NETIF_MSG_NVM, "ethtool_eeprom: cmd %d\n" + DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n" DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n", eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset, eeprom->len, eeprom->len); @@ -6183,20 +7120,23 @@ static int bnx2x_set_eeprom(struct net_device *dev, /* parameters already validated in ethtool_set_eeprom */ /* If the magic number is PHY (0x00504859) upgrade the PHY FW */ - if (eeprom->magic == 0x00504859) { - - bnx2x_phy_hw_lock(bp); - rc = bnx2x_flash_download(bp, bp->port, - bp->link_params.ext_phy_config, - (bp->state != BNX2X_STATE_CLOSED), - eebuf, eeprom->len); - rc |= bnx2x_link_reset(&bp->link_params, - &bp->link_vars); - rc |= bnx2x_phy_init(&bp->link_params, - &bp->link_vars); - bnx2x_phy_hw_unlock(bp); - - } else + if (eeprom->magic == 0x00504859) + if (bp->port.pmf) { + + bnx2x_phy_hw_lock(bp); + rc = bnx2x_flash_download(bp, BP_PORT(bp), + bp->link_params.ext_phy_config, + (bp->state != BNX2X_STATE_CLOSED), + eebuf, eeprom->len); + rc |= bnx2x_link_reset(&bp->link_params, + &bp->link_vars); + rc |= bnx2x_phy_init(&bp->link_params, + &bp->link_vars); + bnx2x_phy_hw_unlock(bp); + + } else /* Only the PMF can access the PHY */ + return -EINVAL; + else rc = bnx2x_nvram_write(bp, eeprom->offset, eebuf, eeprom->len); return rc; @@ -6234,7 +7174,7 @@ static int bnx2x_set_coalesce(struct net_device *dev, bp->stats_ticks = 0xffff00; bp->stats_ticks &= 0xffff00; - if (netif_running(bp->dev)) + if (netif_running(dev)) bnx2x_update_coalesce(bp); return 0; @@ -6261,6 +7201,7 @@ static int bnx2x_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { struct bnx2x *bp = netdev_priv(dev); + int rc = 0; if ((ering->rx_pending > MAX_RX_AVAIL) || (ering->tx_pending > MAX_TX_AVAIL) || @@ -6270,12 +7211,12 @@ static int bnx2x_set_ringparam(struct net_device *dev, bp->rx_ring_size = ering->rx_pending; bp->tx_ring_size = ering->tx_pending; - if (netif_running(bp->dev)) { - bnx2x_nic_unload(bp, 0); - bnx2x_nic_load(bp, 0); + if (netif_running(dev)) { + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + rc = bnx2x_nic_load(bp, LOAD_NORMAL); } - return 0; + return rc; } static void bnx2x_get_pauseparam(struct net_device *dev, @@ -6301,6 +7242,9 @@ static int bnx2x_set_pauseparam(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); + if (IS_E1HMF(bp)) + return 0; + DP(NETIF_MSG_LINK, "ethtool_pauseparam: cmd %d\n" DP_LEVEL " autoneg %d rx_pause %d tx_pause %d\n", epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause); @@ -6317,7 +7261,7 @@ static int bnx2x_set_pauseparam(struct net_device *dev, bp->link_params.req_flow_ctrl = FLOW_CTRL_NONE; if (epause->autoneg) { - if (!(bp->supported & SUPPORTED_Autoneg)) { + if (!(bp->port.supported & SUPPORTED_Autoneg)) { DP(NETIF_MSG_LINK, "Autoneg not supported\n"); return -EINVAL; } @@ -6328,8 +7272,11 @@ static int bnx2x_set_pauseparam(struct net_device *dev, DP(NETIF_MSG_LINK, "req_flow_ctrl 0x%x\n", bp->link_params.req_flow_ctrl); - bnx2x_stop_stats(bp); - bnx2x_link_set(bp); + + if (netif_running(dev)) { + bnx2x_stop_stats(bp); + bnx2x_link_set(bp); + } return 0; } @@ -6531,18 +7478,25 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, static int bnx2x_phys_id(struct net_device *dev, u32 data) { struct bnx2x *bp = netdev_priv(dev); + int port = BP_PORT(bp); int i; + if (!netif_running(dev)) + return 0; + + if (!bp->port.pmf) + return 0; + if (data == 0) data = 2; for (i = 0; i < (data * 2); i++) { if ((i % 2) == 0) - bnx2x_set_led(bp, bp->port, LED_MODE_OPER, SPEED_1000, + bnx2x_set_led(bp, port, LED_MODE_OPER, SPEED_1000, bp->link_params.hw_led_mode, bp->link_params.chip_id); else - bnx2x_set_led(bp, bp->port, LED_MODE_OFF, 0, + bnx2x_set_led(bp, port, LED_MODE_OFF, 0, bp->link_params.hw_led_mode, bp->link_params.chip_id); @@ -6552,7 +7506,7 @@ static int bnx2x_phys_id(struct net_device *dev, u32 data) } if (bp->link_vars.link_up) - bnx2x_set_led(bp, bp->port, LED_MODE_OPER, + bnx2x_set_led(bp, port, LED_MODE_OPER, bp->link_vars.line_speed, bp->link_params.hw_led_mode, bp->link_params.chip_id); @@ -6609,117 +7563,40 @@ static int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state) switch (state) { case PCI_D0: - pci_write_config_word(bp->pdev, - bp->pm_cap + PCI_PM_CTRL, + pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, ((pmcsr & ~PCI_PM_CTRL_STATE_MASK) | PCI_PM_CTRL_PME_STATUS)); if (pmcsr & PCI_PM_CTRL_STATE_MASK) /* delay required during transition out of D3hot */ msleep(20); - break; - - case PCI_D3hot: - pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - pmcsr |= 3; - - if (bp->wol) - pmcsr |= PCI_PM_CTRL_PME_ENABLE; - - pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, - pmcsr); - - /* No more memory access after this point until - * device is brought back to D0. - */ - break; - - default: - return -EINVAL; - } - return 0; -} - -/* - * net_device service functions - */ - -/* called with netif_tx_lock from set_multicast */ -static void bnx2x_set_rx_mode(struct net_device *dev) -{ - struct bnx2x *bp = netdev_priv(dev); - u32 rx_mode = BNX2X_RX_MODE_NORMAL; - - DP(NETIF_MSG_IFUP, "called dev->flags = %x\n", dev->flags); - - if (dev->flags & IFF_PROMISC) - rx_mode = BNX2X_RX_MODE_PROMISC; - - else if ((dev->flags & IFF_ALLMULTI) || - (dev->mc_count > BNX2X_MAX_MULTICAST)) - rx_mode = BNX2X_RX_MODE_ALLMULTI; - - else { /* some multicasts */ - int i, old, offset; - struct dev_mc_list *mclist; - struct mac_configuration_cmd *config = - bnx2x_sp(bp, mcast_config); - - for (i = 0, mclist = dev->mc_list; - mclist && (i < dev->mc_count); - i++, mclist = mclist->next) { - - config->config_table[i].cam_entry.msb_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[0]); - config->config_table[i].cam_entry.middle_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[2]); - config->config_table[i].cam_entry.lsb_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[4]); - config->config_table[i].cam_entry.flags = - cpu_to_le16(bp->port); - config->config_table[i].target_table_entry.flags = 0; - config->config_table[i].target_table_entry. - client_id = 0; - config->config_table[i].target_table_entry. - vlan_id = 0; - - DP(NETIF_MSG_IFUP, - "setting MCAST[%d] (%04x:%04x:%04x)\n", - i, config->config_table[i].cam_entry.msb_mac_addr, - config->config_table[i].cam_entry.middle_mac_addr, - config->config_table[i].cam_entry.lsb_mac_addr); - } - old = config->hdr.length_6b; - if (old > i) { - for (; i < old; i++) { - if (CAM_IS_INVALID(config->config_table[i])) { - i--; /* already invalidated */ - break; - } - /* invalidate */ - CAM_INVALIDATE(config->config_table[i]); - } - } + break; - if (CHIP_REV_IS_SLOW(bp)) - offset = BNX2X_MAX_EMUL_MULTI*(1 + bp->port); - else - offset = BNX2X_MAX_MULTICAST*(1 + bp->port); + case PCI_D3hot: + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= 3; - config->hdr.length_6b = i; - config->hdr.offset = offset; - config->hdr.reserved0 = 0; - config->hdr.reserved1 = 0; + if (bp->wol) + pmcsr |= PCI_PM_CTRL_PME_ENABLE; - bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, - U64_HI(bnx2x_sp_mapping(bp, mcast_config)), - U64_LO(bnx2x_sp_mapping(bp, mcast_config)), 0); - } + pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, + pmcsr); - bp->rx_mode = rx_mode; - bnx2x_set_storm_rx_mode(bp); + /* No more memory access after this point until + * device is brought back to D0. + */ + break; + + default: + return -EINVAL; + } + return 0; } +/* + * net_device service functions + */ + static int bnx2x_poll(struct napi_struct *napi, int budget) { struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath, @@ -6729,7 +7606,7 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) - goto out_panic; + goto poll_panic; #endif prefetch(fp->tx_buf_ring[TX_BD(fp->tx_pkt_cons)].skb); @@ -6738,30 +7615,28 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) bnx2x_update_fpsb_idx(fp); - if (le16_to_cpu(*fp->tx_cons_sb) != fp->tx_pkt_cons) + if ((fp->tx_pkt_prod != le16_to_cpu(*fp->tx_cons_sb)) || + (fp->tx_pkt_prod != fp->tx_pkt_cons)) bnx2x_tx_int(fp, budget); - if (le16_to_cpu(*fp->rx_cons_sb) != fp->rx_comp_cons) work_done = bnx2x_rx_int(fp, budget); - rmb(); /* bnx2x_has_work() reads the status block */ /* must not complete if we consumed full budget */ if ((work_done < budget) && !bnx2x_has_work(fp)) { #ifdef BNX2X_STOP_ON_ERROR -out_panic: +poll_panic: #endif netif_rx_complete(bp->dev, napi); - bnx2x_ack_sb(bp, fp->index, USTORM_ID, + bnx2x_ack_sb(bp, FP_SB_ID(fp), USTORM_ID, le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1); - bnx2x_ack_sb(bp, fp->index, CSTORM_ID, + bnx2x_ack_sb(bp, FP_SB_ID(fp), CSTORM_ID, le16_to_cpu(fp->fp_c_idx), IGU_INT_ENABLE, 1); } - return work_done; } @@ -7055,18 +7930,145 @@ static int bnx2x_close(struct net_device *dev) return 0; } -/* Called with rtnl_lock */ +/* called with netif_tx_lock from set_multicast */ +static void bnx2x_set_rx_mode(struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + u32 rx_mode = BNX2X_RX_MODE_NORMAL; + int port = BP_PORT(bp); + + if (bp->state != BNX2X_STATE_OPEN) { + DP(NETIF_MSG_IFUP, "state is %x, returning\n", bp->state); + return; + } + + DP(NETIF_MSG_IFUP, "dev->flags = %x\n", dev->flags); + + if (dev->flags & IFF_PROMISC) + rx_mode = BNX2X_RX_MODE_PROMISC; + + else if ((dev->flags & IFF_ALLMULTI) || + ((dev->mc_count > BNX2X_MAX_MULTICAST) && CHIP_IS_E1(bp))) + rx_mode = BNX2X_RX_MODE_ALLMULTI; + + else { /* some multicasts */ + if (CHIP_IS_E1(bp)) { + int i, old, offset; + struct dev_mc_list *mclist; + struct mac_configuration_cmd *config = + bnx2x_sp(bp, mcast_config); + + for (i = 0, mclist = dev->mc_list; + mclist && (i < dev->mc_count); + i++, mclist = mclist->next) { + + config->config_table[i]. + cam_entry.msb_mac_addr = + swab16(*(u16 *)&mclist->dmi_addr[0]); + config->config_table[i]. + cam_entry.middle_mac_addr = + swab16(*(u16 *)&mclist->dmi_addr[2]); + config->config_table[i]. + cam_entry.lsb_mac_addr = + swab16(*(u16 *)&mclist->dmi_addr[4]); + config->config_table[i].cam_entry.flags = + cpu_to_le16(port); + config->config_table[i]. + target_table_entry.flags = 0; + config->config_table[i]. + target_table_entry.client_id = 0; + config->config_table[i]. + target_table_entry.vlan_id = 0; + + DP(NETIF_MSG_IFUP, + "setting MCAST[%d] (%04x:%04x:%04x)\n", i, + config->config_table[i]. + cam_entry.msb_mac_addr, + config->config_table[i]. + cam_entry.middle_mac_addr, + config->config_table[i]. + cam_entry.lsb_mac_addr); + } + old = config->hdr.length_6b; + if (old > i) { + for (; i < old; i++) { + if (CAM_IS_INVALID(config-> + config_table[i])) { + i--; /* already invalidated */ + break; + } + /* invalidate */ + CAM_INVALIDATE(config-> + config_table[i]); + } + } + + if (CHIP_REV_IS_SLOW(bp)) + offset = BNX2X_MAX_EMUL_MULTI*(1 + port); + else + offset = BNX2X_MAX_MULTICAST*(1 + port); + + config->hdr.length_6b = i; + config->hdr.offset = offset; + config->hdr.client_id = BP_CL_ID(bp); + config->hdr.reserved1 = 0; + + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, + U64_HI(bnx2x_sp_mapping(bp, mcast_config)), + U64_LO(bnx2x_sp_mapping(bp, mcast_config)), + 0); + } else { /* E1H */ + /* Accept one or more multicasts */ + struct dev_mc_list *mclist; + u32 mc_filter[MC_HASH_SIZE]; + u32 crc, bit, regidx; + int i; + + memset(mc_filter, 0, 4 * MC_HASH_SIZE); + + for (i = 0, mclist = dev->mc_list; + mclist && (i < dev->mc_count); + i++, mclist = mclist->next) { + + DP(NETIF_MSG_IFUP, "Adding mcast MAC: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + mclist->dmi_addr[0], mclist->dmi_addr[1], + mclist->dmi_addr[2], mclist->dmi_addr[3], + mclist->dmi_addr[4], mclist->dmi_addr[5]); + + crc = crc32c_le(0, mclist->dmi_addr, ETH_ALEN); + bit = (crc >> 24) & 0xff; + regidx = bit >> 5; + bit &= 0x1f; + mc_filter[regidx] |= (1 << bit); + } + + for (i = 0; i < MC_HASH_SIZE; i++) + REG_WR(bp, MC_HASH_OFFSET(bp, i), + mc_filter[i]); + } + } + + bp->rx_mode = rx_mode; + bnx2x_set_storm_rx_mode(bp); +} + +/* called with rtnl_lock */ static int bnx2x_change_mac_addr(struct net_device *dev, void *p) { struct sockaddr *addr = p; struct bnx2x *bp = netdev_priv(dev); - if (!is_valid_ether_addr(addr->sa_data)) + if (!is_valid_ether_addr((u8 *)(addr->sa_data))) return -EINVAL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - if (netif_running(dev)) - bnx2x_set_mac_addr(bp); + if (netif_running(dev)) { + if (CHIP_IS_E1(bp)) + bnx2x_set_mac_addr_e1(bp); + else + bnx2x_set_mac_addr_e1h(bp); + } return 0; } @@ -7080,7 +8082,7 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCGMIIPHY: - data->phy_id = bp->phy_addr; + data->phy_id = bp->port.phy_addr; /* fallthrough */ @@ -7090,12 +8092,12 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!netif_running(dev)) return -EAGAIN; - mutex_lock(&bp->phy_mutex); - err = bnx2x_cl45_read(bp, bp->port, 0, bp->phy_addr, + mutex_lock(&bp->port.phy_mutex); + err = bnx2x_cl45_read(bp, BP_PORT(bp), 0, bp->port.phy_addr, DEFAULT_PHY_DEV_ADDR, (data->reg_num & 0x1f), &mii_regval); data->val_out = mii_regval; - mutex_unlock(&bp->phy_mutex); + mutex_unlock(&bp->port.phy_mutex); return err; } @@ -7106,11 +8108,11 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!netif_running(dev)) return -EAGAIN; - mutex_lock(&bp->phy_mutex); - err = bnx2x_cl45_write(bp, bp->port, 0, bp->phy_addr, + mutex_lock(&bp->port.phy_mutex); + err = bnx2x_cl45_write(bp, BP_PORT(bp), 0, bp->port.phy_addr, DEFAULT_PHY_DEV_ADDR, (data->reg_num & 0x1f), data->val_in); - mutex_unlock(&bp->phy_mutex); + mutex_unlock(&bp->port.phy_mutex); return err; default: @@ -7121,10 +8123,11 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EOPNOTSUPP; } -/* Called with rtnl_lock */ +/* called with rtnl_lock */ static int bnx2x_change_mtu(struct net_device *dev, int new_mtu) { struct bnx2x *bp = netdev_priv(dev); + int rc = 0; if ((new_mtu > ETH_MAX_JUMBO_PACKET_SIZE) || ((new_mtu + ETH_HLEN) < ETH_MIN_PACKET_SIZE)) @@ -7137,10 +8140,11 @@ static int bnx2x_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; if (netif_running(dev)) { - bnx2x_nic_unload(bp, 0); - bnx2x_nic_load(bp, 0); + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + rc = bnx2x_nic_load(bp, LOAD_NORMAL); } - return 0; + + return rc; } static void bnx2x_tx_timeout(struct net_device *dev) @@ -7156,7 +8160,7 @@ static void bnx2x_tx_timeout(struct net_device *dev) } #ifdef BCM_VLAN -/* Called with rtnl_lock */ +/* called with rtnl_lock */ static void bnx2x_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp) { @@ -7166,6 +8170,7 @@ static void bnx2x_vlan_rx_register(struct net_device *dev, if (netif_running(dev)) bnx2x_set_client_config(bp); } + #endif #if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) @@ -7179,36 +8184,8 @@ static void poll_bnx2x(struct net_device *dev) } #endif -static void bnx2x_reset_task(struct work_struct *work) -{ - struct bnx2x *bp = container_of(work, struct bnx2x, reset_task); - -#ifdef BNX2X_STOP_ON_ERROR - BNX2X_ERR("reset task called but STOP_ON_ERROR defined" - " so reset not done to allow debug dump,\n" - KERN_ERR " you will need to reboot when done\n"); - return; -#endif - - if (!netif_running(bp->dev)) - return; - - rtnl_lock(); - - if (bp->state != BNX2X_STATE_OPEN) { - DP(NETIF_MSG_TX_ERR, "state is %x, returning\n", bp->state); - goto reset_task_exit; - } - - bnx2x_nic_unload(bp, 0); - bnx2x_nic_load(bp, 0); - -reset_task_exit: - rtnl_unlock(); -} - -static int __devinit bnx2x_init_board(struct pci_dev *pdev, - struct net_device *dev) +static int __devinit bnx2x_init_dev(struct pci_dev *pdev, + struct net_device *dev) { struct bnx2x *bp; int rc; @@ -7216,8 +8193,10 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); + bp->dev = dev; + bp->pdev = pdev; bp->flags = 0; - bp->port = PCI_FUNC(pdev->devfn); + bp->func = PCI_FUNC(pdev->devfn); rc = pci_enable_device(pdev); if (rc) { @@ -7239,14 +8218,17 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, goto err_out_disable; } - rc = pci_request_regions(pdev, DRV_MODULE_NAME); - if (rc) { - printk(KERN_ERR PFX "Cannot obtain PCI resources," - " aborting\n"); - goto err_out_disable; - } + if (atomic_read(&pdev->enable_cnt) == 1) { + rc = pci_request_regions(pdev, DRV_MODULE_NAME); + if (rc) { + printk(KERN_ERR PFX "Cannot obtain PCI resources," + " aborting\n"); + goto err_out_disable; + } - pci_set_master(pdev); + pci_set_master(pdev); + pci_save_state(pdev); + } bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); if (bp->pm_cap == 0) { @@ -7280,13 +8262,9 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, goto err_out_release; } - bp->dev = dev; - bp->pdev = pdev; - - INIT_WORK(&bp->reset_task, bnx2x_reset_task); - INIT_WORK(&bp->sp_task, bnx2x_sp_task); - - dev->base_addr = pci_resource_start(pdev, 0); + dev->mem_start = pci_resource_start(pdev, 0); + dev->base_addr = dev->mem_start; + dev->mem_end = pci_resource_end(pdev, 0); dev->irq = pdev->irq; @@ -7298,8 +8276,9 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, goto err_out_release; } - bp->doorbells = ioremap_nocache(pci_resource_start(pdev , 2), - pci_resource_len(pdev, 2)); + bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2), + min_t(u64, BNX2X_DB_SIZE, + pci_resource_len(pdev, 2))); if (!bp->doorbells) { printk(KERN_ERR PFX "Cannot map doorbell space, aborting\n"); rc = -ENOMEM; @@ -7308,47 +8287,43 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, bnx2x_set_power_state(bp, PCI_D0); - bnx2x_get_hwinfo(bp); - - - if (nomcp) { - printk(KERN_ERR PFX "MCP disabled, will only" - " init first device\n"); - onefunc = 1; - } - - if (onefunc && bp->port) { - printk(KERN_ERR PFX "Second device disabled, exiting\n"); - rc = -ENODEV; - goto err_out_unmap; - } - - bp->tx_ring_size = MAX_TX_AVAIL; - bp->rx_ring_size = MAX_RX_AVAIL; - - bp->rx_csum = 1; - - bp->rx_offset = 0; - - bp->tx_quick_cons_trip_int = 0xff; - bp->tx_quick_cons_trip = 0xff; - bp->tx_ticks_int = 50; - bp->tx_ticks = 50; + /* clean indirect addresses */ + pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, + PCICFG_VENDOR_ID_OFFSET); + REG_WR(bp, PXP2_REG_PGL_ADDR_88_F0 + BP_PORT(bp)*16, 0); + REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F0 + BP_PORT(bp)*16, 0); + REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(bp)*16, 0); + REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(bp)*16, 0); - bp->rx_quick_cons_trip_int = 0xff; - bp->rx_quick_cons_trip = 0xff; - bp->rx_ticks_int = 25; - bp->rx_ticks = 25; + dev->hard_start_xmit = bnx2x_start_xmit; + dev->watchdog_timeo = TX_TIMEOUT; - bp->stats_ticks = 1000000 & 0xffff00; + dev->ethtool_ops = &bnx2x_ethtool_ops; + dev->open = bnx2x_open; + dev->stop = bnx2x_close; + dev->set_multicast_list = bnx2x_set_rx_mode; + dev->set_mac_address = bnx2x_change_mac_addr; + dev->do_ioctl = bnx2x_ioctl; + dev->change_mtu = bnx2x_change_mtu; + dev->tx_timeout = bnx2x_tx_timeout; +#ifdef BCM_VLAN + dev->vlan_rx_register = bnx2x_vlan_rx_register; +#endif +#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) + dev->poll_controller = poll_bnx2x; +#endif + dev->features |= NETIF_F_SG; + dev->features |= NETIF_F_HW_CSUM; + if (bp->flags & USING_DAC_FLAG) + dev->features |= NETIF_F_HIGHDMA; +#ifdef BCM_VLAN + dev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); +#endif + dev->features |= (NETIF_F_TSO | NETIF_F_TSO_ECN); bp->timer_interval = HZ; bp->current_interval = (poll ? poll : HZ); - init_timer(&bp->timer); - bp->timer.expires = jiffies + bp->current_interval; - bp->timer.data = (unsigned long) bp; - bp->timer.function = bnx2x_timer; return 0; @@ -7357,14 +8332,14 @@ err_out_unmap: iounmap(bp->regview); bp->regview = NULL; } - if (bp->doorbells) { iounmap(bp->doorbells); bp->doorbells = NULL; } err_out_release: - pci_release_regions(pdev); + if (atomic_read(&pdev->enable_cnt) == 1) + pci_release_regions(pdev); err_out_disable: pci_disable_device(pdev); @@ -7398,7 +8373,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, struct net_device *dev = NULL; struct bnx2x *bp; int rc; - int port = PCI_FUNC(pdev->devfn); DECLARE_MAC_BUF(mac); if (version_printed++ == 0) @@ -7406,78 +8380,62 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, /* dev zeroed in init_etherdev */ dev = alloc_etherdev(sizeof(*bp)); - if (!dev) + if (!dev) { + printk(KERN_ERR PFX "Cannot allocate net device\n"); return -ENOMEM; + } netif_carrier_off(dev); bp = netdev_priv(dev); bp->msglevel = debug; - if (port && onefunc) { - printk(KERN_ERR PFX "second function disabled. exiting\n"); - free_netdev(dev); - return 0; - } - - rc = bnx2x_init_board(pdev, dev); + rc = bnx2x_init_dev(pdev, dev); if (rc < 0) { free_netdev(dev); return rc; } - dev->hard_start_xmit = bnx2x_start_xmit; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->ethtool_ops = &bnx2x_ethtool_ops; - dev->open = bnx2x_open; - dev->stop = bnx2x_close; - dev->set_multicast_list = bnx2x_set_rx_mode; - dev->set_mac_address = bnx2x_change_mac_addr; - dev->do_ioctl = bnx2x_ioctl; - dev->change_mtu = bnx2x_change_mtu; - dev->tx_timeout = bnx2x_tx_timeout; -#ifdef BCM_VLAN - dev->vlan_rx_register = bnx2x_vlan_rx_register; -#endif -#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) - dev->poll_controller = poll_bnx2x; -#endif - dev->features |= NETIF_F_SG; - if (bp->flags & USING_DAC_FLAG) - dev->features |= NETIF_F_HIGHDMA; - dev->features |= NETIF_F_IP_CSUM; -#ifdef BCM_VLAN - dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; -#endif - dev->features |= NETIF_F_TSO | NETIF_F_TSO_ECN; - rc = register_netdev(dev); if (rc) { dev_err(&pdev->dev, "Cannot register net device\n"); - if (bp->regview) - iounmap(bp->regview); - if (bp->doorbells) - iounmap(bp->doorbells); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - free_netdev(dev); - return rc; + goto init_one_exit; } pci_set_drvdata(pdev, dev); - bp->name = board_info[ent->driver_data].name; + rc = bnx2x_init_bp(bp); + if (rc) { + unregister_netdev(dev); + goto init_one_exit; + } + + bp->common.name = board_info[ent->driver_data].name; printk(KERN_INFO "%s: %s (%c%d) PCI-E x%d %s found at mem %lx," - " IRQ %d, ", dev->name, bp->name, - ((CHIP_ID(bp) & 0xf000) >> 12) + 'A', - ((CHIP_ID(bp) & 0x0ff0) >> 4), + " IRQ %d, ", dev->name, bp->common.name, + (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), bnx2x_get_pcie_width(bp), (bnx2x_get_pcie_speed(bp) == 2) ? "5GHz (Gen2)" : "2.5GHz", dev->base_addr, bp->pdev->irq); printk(KERN_CONT "node addr %s\n", print_mac(mac, dev->dev_addr)); return 0; + +init_one_exit: + if (bp->regview) + iounmap(bp->regview); + + if (bp->doorbells) + iounmap(bp->doorbells); + + free_netdev(dev); + + if (atomic_read(&pdev->enable_cnt) == 1) + pci_release_regions(pdev); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + return rc; } static void __devexit bnx2x_remove_one(struct pci_dev *pdev) @@ -7486,11 +8444,9 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev) struct bnx2x *bp; if (!dev) { - /* we get here if init_one() fails */ printk(KERN_ERR PFX "BAD net device from bnx2x_init_one\n"); return; } - bp = netdev_priv(dev); unregister_netdev(dev); @@ -7502,7 +8458,10 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev) iounmap(bp->doorbells); free_netdev(dev); - pci_release_regions(pdev); + + if (atomic_read(&pdev->enable_cnt) == 1) + pci_release_regions(pdev); + pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } @@ -7512,21 +8471,29 @@ static int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state) struct net_device *dev = pci_get_drvdata(pdev); struct bnx2x *bp; - if (!dev) - return 0; + if (!dev) { + printk(KERN_ERR PFX "BAD net device from bnx2x_init_one\n"); + return -ENODEV; + } + bp = netdev_priv(dev); - if (!netif_running(dev)) - return 0; + rtnl_lock(); - bp = netdev_priv(dev); + pci_save_state(pdev); - bnx2x_nic_unload(bp, 0); + if (!netif_running(dev)) { + rtnl_unlock(); + return 0; + } netif_device_detach(dev); - pci_save_state(pdev); + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + bnx2x_set_power_state(bp, pci_choose_state(pdev, state)); + rtnl_unlock(); + return 0; } @@ -7540,21 +8507,25 @@ static int bnx2x_resume(struct pci_dev *pdev) printk(KERN_ERR PFX "BAD net device from bnx2x_init_one\n"); return -ENODEV; } - - if (!netif_running(dev)) - return 0; - bp = netdev_priv(dev); + rtnl_lock(); + pci_restore_state(pdev); + + if (!netif_running(dev)) { + rtnl_unlock(); + return 0; + } + bnx2x_set_power_state(bp, PCI_D0); netif_device_attach(dev); - rc = bnx2x_nic_load(bp, 0); - if (rc) - return rc; + rc = bnx2x_nic_load(bp, LOAD_NORMAL); - return 0; + rtnl_unlock(); + + return rc; } static struct pci_driver bnx2x_pci_driver = { diff --git a/drivers/net/bnx2x_reg.h b/drivers/net/bnx2x_reg.h index 8707c0d05d9a..15c9a9946724 100644 --- a/drivers/net/bnx2x_reg.h +++ b/drivers/net/bnx2x_reg.h @@ -38,21 +38,19 @@ was asserted. */ #define BRB1_REG_NUM_OF_FULL_CYCLES_0 0x600c8 #define BRB1_REG_NUM_OF_FULL_CYCLES_1 0x600cc -#define BRB1_REG_NUM_OF_FULL_CYCLES_2 0x600d0 -#define BRB1_REG_NUM_OF_FULL_CYCLES_3 0x600d4 #define BRB1_REG_NUM_OF_FULL_CYCLES_4 0x600d8 /* [ST 32] The number of cycles that the pause signal towards MAC #0 was asserted. */ #define BRB1_REG_NUM_OF_PAUSE_CYCLES_0 0x600b8 #define BRB1_REG_NUM_OF_PAUSE_CYCLES_1 0x600bc -#define BRB1_REG_NUM_OF_PAUSE_CYCLES_2 0x600c0 -#define BRB1_REG_NUM_OF_PAUSE_CYCLES_3 0x600c4 /* [RW 10] Write client 0: De-assert pause threshold. */ #define BRB1_REG_PAUSE_HIGH_THRESHOLD_0 0x60078 #define BRB1_REG_PAUSE_HIGH_THRESHOLD_1 0x6007c /* [RW 10] Write client 0: Assert pause threshold. */ #define BRB1_REG_PAUSE_LOW_THRESHOLD_0 0x60068 #define BRB1_REG_PAUSE_LOW_THRESHOLD_1 0x6006c +/* [R 24] The number of full blocks occpied by port. */ +#define BRB1_REG_PORT_NUM_OCC_BLOCKS_0 0x60094 /* [RW 1] Reset the design by software. */ #define BRB1_REG_SOFT_RESET 0x600dc /* [R 5] Used to read the value of the XX protection CAM occupancy counter. */ @@ -513,7 +511,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define CSEM_REG_INT_TABLE 0x200400 -#define CSEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define CSEM_REG_MSG_NUM_FIC0 0x200000 @@ -587,13 +584,10 @@ #define DBG_REG_DBG_PRTY_MASK 0xc0a8 /* [R 1] Parity register #0 read */ #define DBG_REG_DBG_PRTY_STS 0xc09c -/* [RW 2] debug only: These bits indicate the credit for PCI request type 4 - interface; MUST be configured AFTER pci_ext_buffer_strt_addr_lsb/msb are - configured */ -#define DBG_REG_PCI_REQ_CREDIT 0xc120 /* [RW 32] Commands memory. The address to command X; row Y is to calculated as 14*X+Y. */ #define DMAE_REG_CMD_MEM 0x102400 +#define DMAE_REG_CMD_MEM_SIZE 224 /* [RW 1] If 0 - the CRC-16c initial value is all zeroes; if 1 - the CRC-16c initial value is all ones. */ #define DMAE_REG_CRC16C_INIT 0x10201c @@ -1626,7 +1620,7 @@ is reset to 0x080; giving a default blink period of approximately 8Hz. */ #define NIG_REG_LED_CONTROL_BLINK_RATE_P0 0x10310 /* [RW 1] Port0: If set along with the - nig_registers_led_control_override_traffic_p0.led_control_override_traffic_p0 + ~nig_registers_led_control_override_traffic_p0.led_control_override_traffic_p0 bit and ~nig_registers_led_control_traffic_p0.led_control_traffic_p0 LED bit; the Traffic LED will blink with the blink rate specified in ~nig_registers_led_control_blink_rate_p0.led_control_blink_rate_p0 and @@ -1733,9 +1727,21 @@ /* [R 32] Rx statistics : In user packets discarded due to BRB backpressure for port0 */ #define NIG_REG_STAT0_BRB_DISCARD 0x105f0 +/* [WB_R 36] Tx statistics : Number of packets from emac0 or bmac0 that + between 1024 and 1522 bytes for port0 */ +#define NIG_REG_STAT0_EGRESS_MAC_PKT0 0x10750 +/* [WB_R 36] Tx statistics : Number of packets from emac0 or bmac0 that + between 1523 bytes and above for port0 */ +#define NIG_REG_STAT0_EGRESS_MAC_PKT1 0x10760 /* [R 32] Rx statistics : In user packets discarded due to BRB backpressure for port1 */ #define NIG_REG_STAT1_BRB_DISCARD 0x10628 +/* [WB_R 36] Tx statistics : Number of packets from emac1 or bmac1 that + between 1024 and 1522 bytes for port1 */ +#define NIG_REG_STAT1_EGRESS_MAC_PKT0 0x107a0 +/* [WB_R 36] Tx statistics : Number of packets from emac1 or bmac1 that + between 1523 bytes and above for port1 */ +#define NIG_REG_STAT1_EGRESS_MAC_PKT1 0x107b0 /* [WB_R 64] Rx statistics : User octets received for LP */ #define NIG_REG_STAT2_BRB_OCTET 0x107e0 #define NIG_REG_STATUS_INTERRUPT_PORT0 0x10328 @@ -1849,7 +1855,6 @@ #define PRS_REG_CFC_SEARCH_INITIAL_CREDIT 0x4011c /* [RW 24] CID for port 0 if no match */ #define PRS_REG_CID_PORT_0 0x400fc -#define PRS_REG_CID_PORT_1 0x40100 /* [RW 32] The CM header for flush message where 'load existed' bit in CFC load response is reset and packet type is 0. Used in packet start message to TCM. */ @@ -1957,6 +1962,10 @@ #define PXP2_REG_HST_DATA_FIFO_STATUS 0x12047c /* [R 7] Debug only: Number of used entries in the header FIFO */ #define PXP2_REG_HST_HEADER_FIFO_STATUS 0x120478 +#define PXP2_REG_PGL_ADDR_88_F0 0x120534 +#define PXP2_REG_PGL_ADDR_8C_F0 0x120538 +#define PXP2_REG_PGL_ADDR_90_F0 0x12053c +#define PXP2_REG_PGL_ADDR_94_F0 0x120540 #define PXP2_REG_PGL_CONTROL0 0x120490 #define PXP2_REG_PGL_CONTROL1 0x120514 /* [RW 32] third dword data of expansion rom request. this register is @@ -2060,12 +2069,13 @@ #define PXP2_REG_PSWRQ_SRC0_L2P 0x120054 #define PXP2_REG_PSWRQ_TM0_L2P 0x12001c #define PXP2_REG_PSWRQ_TSDM0_L2P 0x1200e0 -/* [RW 25] Interrupt mask register #0 read/write */ -#define PXP2_REG_PXP2_INT_MASK 0x120578 -/* [R 25] Interrupt register #0 read */ -#define PXP2_REG_PXP2_INT_STS 0x12056c -/* [RC 25] Interrupt register #0 read clear */ -#define PXP2_REG_PXP2_INT_STS_CLR 0x120570 +/* [RW 32] Interrupt mask register #0 read/write */ +#define PXP2_REG_PXP2_INT_MASK_0 0x120578 +/* [R 32] Interrupt register #0 read */ +#define PXP2_REG_PXP2_INT_STS_0 0x12056c +#define PXP2_REG_PXP2_INT_STS_1 0x120608 +/* [RC 32] Interrupt register #0 read clear */ +#define PXP2_REG_PXP2_INT_STS_CLR_0 0x120570 /* [RW 32] Parity mask register #0 read/write */ #define PXP2_REG_PXP2_PRTY_MASK_0 0x120588 #define PXP2_REG_PXP2_PRTY_MASK_1 0x120598 @@ -2811,22 +2821,6 @@ #define QM_REG_QVOQIDX_97 0x16e490 #define QM_REG_QVOQIDX_98 0x16e494 #define QM_REG_QVOQIDX_99 0x16e498 -/* [R 24] Remaining pause timeout for queues 15-0 */ -#define QM_REG_REMAINPAUSETM0 0x168418 -/* [R 24] Remaining pause timeout for queues 31-16 */ -#define QM_REG_REMAINPAUSETM1 0x16841c -/* [R 24] Remaining pause timeout for queues 47-32 */ -#define QM_REG_REMAINPAUSETM2 0x16e69c -/* [R 24] Remaining pause timeout for queues 63-48 */ -#define QM_REG_REMAINPAUSETM3 0x16e6a0 -/* [R 24] Remaining pause timeout for queues 79-64 */ -#define QM_REG_REMAINPAUSETM4 0x16e6a4 -/* [R 24] Remaining pause timeout for queues 95-80 */ -#define QM_REG_REMAINPAUSETM5 0x16e6a8 -/* [R 24] Remaining pause timeout for queues 111-96 */ -#define QM_REG_REMAINPAUSETM6 0x16e6ac -/* [R 24] Remaining pause timeout for queues 127-112 */ -#define QM_REG_REMAINPAUSETM7 0x16e6b0 /* [RW 1] Initialization bit command */ #define QM_REG_SOFT_RESET 0x168428 /* [RW 8] The credit cost per every task in the QM. A value per each VOQ */ @@ -3826,7 +3820,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define TSEM_REG_INT_TABLE 0x180400 -#define TSEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define TSEM_REG_MSG_NUM_FIC0 0x180000 @@ -4283,7 +4276,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define USEM_REG_INT_TABLE 0x300400 -#define USEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define USEM_REG_MSG_NUM_FIC0 0x300000 @@ -4802,7 +4794,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define XSEM_REG_INT_TABLE 0x280400 -#define XSEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define XSEM_REG_MSG_NUM_FIC0 0x280000 @@ -4930,10 +4921,7 @@ #define EMAC_MDIO_MODE_CLOCK_CNT (0x3fL<<16) #define EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT 16 #define EMAC_MODE_25G_MODE (1L<<5) -#define EMAC_MODE_ACPI_RCVD (1L<<20) #define EMAC_MODE_HALF_DUPLEX (1L<<1) -#define EMAC_MODE_MPKT (1L<<18) -#define EMAC_MODE_MPKT_RCVD (1L<<19) #define EMAC_MODE_PORT_GMII (2L<<2) #define EMAC_MODE_PORT_MII (1L<<2) #define EMAC_MODE_PORT_MII_10M (3L<<2) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index caa000596b25..e74b14acf8e0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1949,6 +1949,8 @@ #define PCI_DEVICE_ID_NX2_5708 0x164c #define PCI_DEVICE_ID_TIGON3_5702FE 0x164d #define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 #define PCI_DEVICE_ID_TIGON3_5705 0x1653 #define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 #define PCI_DEVICE_ID_TIGON3_5720 0x1658 -- cgit v1.2.3 From a033c332e047397904ed74816946b2edd9b0d5cd Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 23 Jun 2008 10:52:42 +0800 Subject: lockdep: remove duplicate definition of STATIC_LOCKDEP_MAP_INIT STATIC_LOCKDEP_MAP_INIT is defined twice in lockdep.h. I guess it's a copy & paste. Signed-off-by: Li Zefan Cc: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index b26fbc715a50..2486eb4edbf1 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -278,14 +278,6 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name, lockdep_init_map(&(lock)->dep_map, #lock, \ (lock)->dep_map.key, sub) -/* - * To initialize a lockdep_map statically use this macro. - * Note that _name must not be NULL. - */ -#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \ - { .name = (_name), .key = (void *)(_key), } - - /* * Acquire a lock. * -- cgit v1.2.3 From f3f3149f35b9195ef4b761b1353fc0766b5f53be Mon Sep 17 00:00:00 2001 From: Alok Kataria Date: Mon, 23 Jun 2008 18:21:56 -0700 Subject: x86: use cpu_khz for loops_per_jiffy calculation, cleanup As suggested by Ingo, remove all references to tsc from init/calibrate.c TSC is x86 specific, and using tsc in variable names in a generic file should be avoided. lpj_tsc is now called lpj_fine, since it is related to fine tuning of lpj value. Also tsc_rate_* is called timer_rate_* Signed-off-by: Alok N Kataria Cc: Arjan van de Ven Cc: Daniel Hecht Cc: Tim Mann Cc: Zach Amsden Cc: Sahil Rihan Signed-off-by: Ingo Molnar --- arch/x86/kernel/time_64.c | 2 +- arch/x86/kernel/tsc_32.c | 2 +- include/linux/delay.h | 2 +- init/calibrate.c | 36 +++++++++++++++++++----------------- 4 files changed, 22 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 12b4a71bd074..39ae8511a137 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -123,7 +123,7 @@ void __init time_init(void) (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)) cpu_khz = calculate_cpu_khz(); - lpj_tsc = ((unsigned long)tsc_khz * 1000)/HZ; + lpj_fine = ((unsigned long)tsc_khz * 1000)/HZ; if (unsynchronized_tsc()) mark_tsc_unstable("TSCs unsynchronized"); diff --git a/arch/x86/kernel/tsc_32.c b/arch/x86/kernel/tsc_32.c index 0af49fb533e1..048baab77268 100644 --- a/arch/x86/kernel/tsc_32.c +++ b/arch/x86/kernel/tsc_32.c @@ -425,7 +425,7 @@ void __init tsc_init(void) lpj = ((u64)tsc_khz * 1000); do_div(lpj, HZ); - lpj_tsc = lpj; + lpj_fine = lpj; printk("Detected %lu.%03lu MHz processor.\n", (unsigned long)cpu_khz / 1000, diff --git a/include/linux/delay.h b/include/linux/delay.h index 01aec60590ab..fd832c6d419e 100644 --- a/include/linux/delay.h +++ b/include/linux/delay.h @@ -41,7 +41,7 @@ static inline void ndelay(unsigned long x) #define ndelay(x) ndelay(x) #endif -extern unsigned long lpj_tsc; +extern unsigned long lpj_fine; void calibrate_delay(void); void msleep(unsigned int msecs); unsigned long msleep_interruptible(unsigned int msecs); diff --git a/init/calibrate.c b/init/calibrate.c index 86286974dada..7963e3fc51d9 100644 --- a/init/calibrate.c +++ b/init/calibrate.c @@ -10,7 +10,7 @@ #include #include -unsigned long lpj_tsc; +unsigned long lpj_fine; unsigned long preset_lpj; static int __init lpj_setup(char *str) { @@ -35,9 +35,9 @@ static unsigned long __cpuinit calibrate_delay_direct(void) unsigned long pre_start, start, post_start; unsigned long pre_end, end, post_end; unsigned long start_jiffies; - unsigned long tsc_rate_min, tsc_rate_max; - unsigned long good_tsc_sum = 0; - unsigned long good_tsc_count = 0; + unsigned long timer_rate_min, timer_rate_max; + unsigned long good_timer_sum = 0; + unsigned long good_timer_count = 0; int i; if (read_current_timer(&pre_start) < 0 ) @@ -81,22 +81,24 @@ static unsigned long __cpuinit calibrate_delay_direct(void) } read_current_timer(&post_end); - tsc_rate_max = (post_end - pre_start) / DELAY_CALIBRATION_TICKS; - tsc_rate_min = (pre_end - post_start) / DELAY_CALIBRATION_TICKS; + timer_rate_max = (post_end - pre_start) / + DELAY_CALIBRATION_TICKS; + timer_rate_min = (pre_end - post_start) / + DELAY_CALIBRATION_TICKS; /* - * If the upper limit and lower limit of the tsc_rate is + * If the upper limit and lower limit of the timer_rate is * >= 12.5% apart, redo calibration. */ if (pre_start != 0 && pre_end != 0 && - (tsc_rate_max - tsc_rate_min) < (tsc_rate_max >> 3)) { - good_tsc_count++; - good_tsc_sum += tsc_rate_max; + (timer_rate_max - timer_rate_min) < (timer_rate_max >> 3)) { + good_timer_count++; + good_timer_sum += timer_rate_max; } } - if (good_tsc_count) - return (good_tsc_sum/good_tsc_count); + if (good_timer_count) + return (good_timer_sum/good_timer_count); printk(KERN_WARNING "calibrate_delay_direct() failed to get a good " "estimate for loops_per_jiffy.\nProbably due to long platform interrupts. Consider using \"lpj=\" boot option.\n"); @@ -111,8 +113,8 @@ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;} * bit takes on average 1.5/HZ seconds. This (like the original) is a little * better than 1% * For the boot cpu we can skip the delay calibration and assign it a value - * calculated based on the tsc frequency. - * For the rest of the CPUs we cannot assume that the tsc frequency is same as + * calculated based on the timer frequency. + * For the rest of the CPUs we cannot assume that the timer frequency is same as * the cpu frequency, hence do the calibration for those. */ #define LPS_PREC 8 @@ -126,11 +128,11 @@ void __cpuinit calibrate_delay(void) loops_per_jiffy = preset_lpj; printk(KERN_INFO "Calibrating delay loop (skipped) preset value.. "); - } else if ((smp_processor_id() == 0) && lpj_tsc) { - loops_per_jiffy = lpj_tsc; + } else if ((smp_processor_id() == 0) && lpj_fine) { + loops_per_jiffy = lpj_fine; printk(KERN_INFO "Calibrating delay loop (skipped), " - "using tsc calculated value.. "); + "value calculated using timer frequency.. "); } else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) { printk(KERN_INFO "Calibrating delay using timer specific routine.. "); -- cgit v1.2.3 From f8dd0ecbb74d4b220b105d77c0633945ebb5453e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 24 Jun 2008 22:16:04 +0100 Subject: DM9000: Allow the use of the NSR register to get link status. The DM9000's internal PHY reports a copy of the link status in the NSR register of the chip. Reading the status when polling for link status is faster as it eliminates the need to sleep, but does not print as much information. Add an platform flag to force this behaviour, and a Kconfig option to allow it to be forced to the faster method always. Signed-off-by: Ben Dooks Signed-off-by: Jeff Garzik --- drivers/net/Kconfig | 9 +++++++++ drivers/net/dm9000.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/linux/dm9000.h | 1 + 3 files changed, 49 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 287d0873c60d..4d69474b6125 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -938,6 +938,15 @@ config DM9000 To compile this driver as a module, choose M here. The module will be called dm9000. +config DM9000_FORCE_SIMPLE_PHY_POLL + bool "Force simple NSR based PHY polling" + depends on DM9000 + ---help--- + This configuration forces the DM9000 to use the NSR's LinkStatus + bit to determine if the link is up or down instead of the more + costly MII PHY reads. Note, this will not work if the chip is + operating with an external PHY. + config ENC28J60 tristate "ENC28J60 support" depends on EXPERIMENTAL && SPI && NET_ETHERNET diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 7c38f6129b55..5ad2ec537684 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -552,15 +552,48 @@ static const struct ethtool_ops dm9000_ethtool_ops = { .set_eeprom = dm9000_set_eeprom, }; +static void dm9000_show_carrier(board_info_t *db, + unsigned carrier, unsigned nsr) +{ + struct net_device *ndev = db->ndev; + unsigned ncr = dm9000_read_locked(db, DM9000_NCR); + + if (carrier) + dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA\n", + ndev->name, (nsr & NSR_SPEED) ? 10 : 100, + (ncr & NCR_FDX) ? "full" : "half"); + else + dev_info(db->dev, "%s: link down\n", ndev->name); +} + static void dm9000_poll_work(struct work_struct *w) { struct delayed_work *dw = container_of(w, struct delayed_work, work); board_info_t *db = container_of(dw, board_info_t, phy_poll); + struct net_device *ndev = db->ndev; + + if (db->flags & DM9000_PLATF_SIMPLE_PHY && + !(db->flags & DM9000_PLATF_EXT_PHY)) { + unsigned nsr = dm9000_read_locked(db, DM9000_NSR); + unsigned old_carrier = netif_carrier_ok(ndev) ? 1 : 0; + unsigned new_carrier; - mii_check_media(&db->mii, netif_msg_link(db), 0); + new_carrier = (nsr & NSR_LINKST) ? 1 : 0; + + if (old_carrier != new_carrier) { + if (netif_msg_link(db)) + dm9000_show_carrier(db, new_carrier, nsr); + + if (!new_carrier) + netif_carrier_off(ndev); + else + netif_carrier_on(ndev); + } + } else + mii_check_media(&db->mii, netif_msg_link(db), 0); - if (netif_running(db->ndev)) + if (netif_running(ndev)) dm9000_schedule_poll(db); } @@ -1267,6 +1300,10 @@ dm9000_probe(struct platform_device *pdev) db->flags = pdata->flags; } +#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL + db->flags |= DM9000_PLATF_SIMPLE_PHY; +#endif + dm9000_reset(db); /* try multiple times, DM9000 sometimes gets the read wrong */ diff --git a/include/linux/dm9000.h b/include/linux/dm9000.h index a3750462f9e3..fc82446b6425 100644 --- a/include/linux/dm9000.h +++ b/include/linux/dm9000.h @@ -21,6 +21,7 @@ #define DM9000_PLATF_32BITONLY (0x0004) #define DM9000_PLATF_EXT_PHY (0x0008) #define DM9000_PLATF_NO_EEPROM (0x0010) +#define DM9000_PLATF_SIMPLE_PHY (0x0020) /* Use NSR to find LinkStatus */ /* platfrom data for platfrom device structure's platfrom_data field */ -- cgit v1.2.3 From d8de72473effd674a3c1fe9621821f406f5587c9 Mon Sep 17 00:00:00 2001 From: Peng Haitao Date: Tue, 20 May 2008 09:13:02 +0800 Subject: [PATCH] remove useless argument type in audit_filter_user() The second argument "type" is not used in audit_filter_user(), so I think that type can be removed. If I'm wrong, please tell me. Signed-off-by: Peng Haitao Signed-off-by: Al Viro --- include/linux/audit.h | 2 +- kernel/audit.c | 2 +- kernel/auditfilter.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 63c3bb98558f..8b82974bdc12 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -571,7 +571,7 @@ extern void audit_log_lost(const char *message); extern int audit_update_lsm_rules(void); /* Private API (for audit.c only) */ -extern int audit_filter_user(struct netlink_skb_parms *cb, int type); +extern int audit_filter_user(struct netlink_skb_parms *cb); extern int audit_filter_type(int type); extern int audit_receive_filter(int type, int pid, int uid, int seq, void *data, size_t datasz, uid_t loginuid, diff --git a/kernel/audit.c b/kernel/audit.c index 56f30287e24c..e092f1c0ce30 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -738,7 +738,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (!audit_enabled && msg_type != AUDIT_USER_AVC) return 0; - err = audit_filter_user(&NETLINK_CB(skb), msg_type); + err = audit_filter_user(&NETLINK_CB(skb)); if (err == 1) { err = 0; if (msg_type == AUDIT_USER_TTY) { diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 75cdf262851d..98c50cc671bb 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1721,7 +1721,7 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb, return 1; } -int audit_filter_user(struct netlink_skb_parms *cb, int type) +int audit_filter_user(struct netlink_skb_parms *cb) { enum audit_state state = AUDIT_DISABLED; struct audit_entry *e; -- cgit v1.2.3 From 16d752397301b95abaa95cbaf9e785d221872311 Mon Sep 17 00:00:00 2001 From: Rene Herman Date: Tue, 24 Jun 2008 19:38:56 +0200 Subject: thermal: Create CONFIG_THERMAL_HWMON=n A bug in libsensors <= 2.10.6 is exposed when this new hwmon I/F is enabled. Create CONFIG_THERMAL_HWMON=n until some time after libsensors 2.10.7 ships so those users can run the latest kernel. libsensors 3.x is already fixed -- those users can use CONFIG_THERMAL_HWMON=y now. Signed-off-by: Rene Herman Acked-by: Mark M. Hoffman Signed-off-by: Len Brown --- Documentation/feature-removal-schedule.txt | 9 +++++++++ drivers/thermal/Kconfig | 9 +++++++++ drivers/thermal/thermal_sys.c | 4 ++-- include/linux/thermal.h | 6 ++---- 4 files changed, 22 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 5b3f31faed56..46ece3fba6f9 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -312,3 +312,12 @@ When: 2.6.26 Why: Implementation became generic; users should now include linux/semaphore.h instead. Who: Matthew Wilcox + +--------------------------- + +What: CONFIG_THERMAL_HWMON +When: January 2009 +Why: This option was introduced just to allow older lm-sensors userspace + to keep working over the upgrade to 2.6.26. At the scheduled time of + removal fixed lm-sensors (2.x or 3.x) should be readily available. +Who: Rene Herman diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 4b628526df09..a86e952ed4ca 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -12,3 +12,12 @@ menuconfig THERMAL cooling devices. All platforms with ACPI thermal support can use this driver. If you want this support, you should say Y or M here. + +config THERMAL_HWMON + bool "Hardware monitoring support" + depends on HWMON=y || HWMON=THERMAL + help + The generic thermal sysfs driver's hardware monitoring support + requires a 2.10.7/3.0.2 or later lm-sensors userspace. + + Say Y if your user-space is new enough. diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6098787341f3..fe07462d5947 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -295,8 +295,8 @@ thermal_cooling_device_trip_point_show(struct device *dev, /* Device management */ -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) + /* hwmon sys I/F */ #include static LIST_HEAD(thermal_hwmon_list); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 06d3e6eb9ca8..917707e6151d 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -66,8 +66,7 @@ struct thermal_cooling_device { ((long)t-2732+5)/10 : ((long)t-2732-5)/10) #define CELSIUS_TO_KELVIN(t) ((t)*10+2732) -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) /* thermal zone devices with the same type share one hwmon device */ struct thermal_hwmon_device { char type[THERMAL_NAME_LENGTH]; @@ -94,8 +93,7 @@ struct thermal_zone_device { struct idr idr; struct mutex lock; /* protect cooling devices list */ struct list_head node; -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) struct list_head hwmon_node; struct thermal_hwmon_device *hwmon; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ -- cgit v1.2.3 From 3d4422332711ef48ef0f132f1fcbfcbd56c7f3d1 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 26 Jun 2008 11:21:34 +0200 Subject: Add generic helpers for arch IPI function calls This adds kernel/smp.c which contains helpers for IPI function calls. In addition to supporting the existing smp_call_function() in a more efficient manner, it also adds a more scalable variant called smp_call_function_single() for calling a given function on a single CPU only. The core of this is based on the x86-64 patch from Nick Piggin, lots of changes since then. "Alan D. Brunelle" has contributed lots of fixes and suggestions as well. Also thanks to Paul E. McKenney for reviewing RCU usage and getting rid of the data allocation fallback deadlock. Acked-by: Ingo Molnar Reviewed-by: Paul E. McKenney Signed-off-by: Jens Axboe --- arch/Kconfig | 3 + arch/sparc64/kernel/smp.c | 11 +- include/linux/smp.h | 35 ++++- init/main.c | 2 + kernel/Makefile | 1 + kernel/smp.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 428 insertions(+), 7 deletions(-) create mode 100644 kernel/smp.c (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index 3ea332b009e5..ad89a33d8c6e 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -39,3 +39,6 @@ config HAVE_KRETPROBES config HAVE_DMA_ATTRS def_bool n + +config USE_GENERIC_SMP_HELPERS + def_bool n diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index fa63c68a1819..b82d017a1744 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -816,8 +816,9 @@ extern unsigned long xcall_call_function; * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -static int smp_call_function_mask(void (*func)(void *info), void *info, - int nonatomic, int wait, cpumask_t mask) +static int sparc64_smp_call_function_mask(void (*func)(void *info), void *info, + int nonatomic, int wait, + cpumask_t mask) { struct call_data_struct data; int cpus; @@ -855,8 +856,8 @@ out_unlock: int smp_call_function(void (*func)(void *info), void *info, int nonatomic, int wait) { - return smp_call_function_mask(func, info, nonatomic, wait, - cpu_online_map); + return sparc64_smp_call_function_mask(func, info, nonatomic, wait, + cpu_online_map); } void smp_call_function_client(int irq, struct pt_regs *regs) @@ -893,7 +894,7 @@ static void tsb_sync(void *info) void smp_tsb_sync(struct mm_struct *mm) { - smp_call_function_mask(tsb_sync, mm, 0, 1, mm->cpu_vm_mask); + sparc64_smp_call_function_mask(tsb_sync, mm, 0, 1, mm->cpu_vm_mask); } extern unsigned long xcall_flush_tlb_mm; diff --git a/include/linux/smp.h b/include/linux/smp.h index 55232ccf9cfd..eac3e062250f 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -7,9 +7,19 @@ */ #include +#include +#include +#include extern void cpu_idle(void); +struct call_single_data { + struct list_head list; + void (*func) (void *info); + void *info; + unsigned int flags; +}; + #ifdef CONFIG_SMP #include @@ -53,9 +63,28 @@ extern void smp_cpus_done(unsigned int max_cpus); * Call a function on all other processors */ int smp_call_function(void(*func)(void *info), void *info, int retry, int wait); - +int smp_call_function_mask(cpumask_t mask, void(*func)(void *info), void *info, + int wait); int smp_call_function_single(int cpuid, void (*func) (void *info), void *info, int retry, int wait); +void __smp_call_function_single(int cpuid, struct call_single_data *data); + +/* + * Generic and arch helpers + */ +#ifdef CONFIG_USE_GENERIC_SMP_HELPERS +void generic_smp_call_function_single_interrupt(void); +void generic_smp_call_function_interrupt(void); +void init_call_single_data(void); +void ipi_call_lock(void); +void ipi_call_unlock(void); +void ipi_call_lock_irq(void); +void ipi_call_unlock_irq(void); +#else +static inline void init_call_single_data(void) +{ +} +#endif /* * Call a function on all processors @@ -112,7 +141,9 @@ static inline void smp_send_reschedule(int cpu) { } }) #define smp_call_function_mask(mask, func, info, wait) \ (up_smp_call_function(func, info)) - +static inline void init_call_single_data(void) +{ +} #endif /* !SMP */ /* diff --git a/init/main.c b/init/main.c index f7fb20021d48..1efcccff1bdb 100644 --- a/init/main.c +++ b/init/main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -779,6 +780,7 @@ static void __init do_pre_smp_initcalls(void) { extern int spawn_ksoftirqd(void); + init_call_single_data(); migration_init(); spawn_ksoftirqd(); if (!nosoftlockup) diff --git a/kernel/Makefile b/kernel/Makefile index 1c9938addb9d..9fa57976f252 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o spinlock.o +obj-$(CONFIG_USE_GENERIC_SMP_HELPERS) += smp.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o diff --git a/kernel/smp.c b/kernel/smp.c new file mode 100644 index 000000000000..f77b75c027ad --- /dev/null +++ b/kernel/smp.c @@ -0,0 +1,383 @@ +/* + * Generic helpers for smp ipi calls + * + * (C) Jens Axboe 2008 + * + */ +#include +#include +#include +#include +#include + +static DEFINE_PER_CPU(struct call_single_queue, call_single_queue); +static LIST_HEAD(call_function_queue); +__cacheline_aligned_in_smp DEFINE_SPINLOCK(call_function_lock); + +enum { + CSD_FLAG_WAIT = 0x01, + CSD_FLAG_ALLOC = 0x02, +}; + +struct call_function_data { + struct call_single_data csd; + spinlock_t lock; + unsigned int refs; + cpumask_t cpumask; + struct rcu_head rcu_head; +}; + +struct call_single_queue { + struct list_head list; + spinlock_t lock; +}; + +void __cpuinit init_call_single_data(void) +{ + int i; + + for_each_possible_cpu(i) { + struct call_single_queue *q = &per_cpu(call_single_queue, i); + + spin_lock_init(&q->lock); + INIT_LIST_HEAD(&q->list); + } +} + +static void csd_flag_wait(struct call_single_data *data) +{ + /* Wait for response */ + do { + /* + * We need to see the flags store in the IPI handler + */ + smp_mb(); + if (!(data->flags & CSD_FLAG_WAIT)) + break; + cpu_relax(); + } while (1); +} + +/* + * Insert a previously allocated call_single_data element for execution + * on the given CPU. data must already have ->func, ->info, and ->flags set. + */ +static void generic_exec_single(int cpu, struct call_single_data *data) +{ + struct call_single_queue *dst = &per_cpu(call_single_queue, cpu); + int wait = data->flags & CSD_FLAG_WAIT, ipi; + unsigned long flags; + + spin_lock_irqsave(&dst->lock, flags); + ipi = list_empty(&dst->list); + list_add_tail(&data->list, &dst->list); + spin_unlock_irqrestore(&dst->lock, flags); + + if (ipi) + arch_send_call_function_single_ipi(cpu); + + if (wait) + csd_flag_wait(data); +} + +static void rcu_free_call_data(struct rcu_head *head) +{ + struct call_function_data *data; + + data = container_of(head, struct call_function_data, rcu_head); + + kfree(data); +} + +/* + * Invoked by arch to handle an IPI for call function. Must be called with + * interrupts disabled. + */ +void generic_smp_call_function_interrupt(void) +{ + struct call_function_data *data; + int cpu = get_cpu(); + + /* + * It's ok to use list_for_each_rcu() here even though we may delete + * 'pos', since list_del_rcu() doesn't clear ->next + */ + rcu_read_lock(); + list_for_each_entry_rcu(data, &call_function_queue, csd.list) { + int refs; + + if (!cpu_isset(cpu, data->cpumask)) + continue; + + data->csd.func(data->csd.info); + + spin_lock(&data->lock); + cpu_clear(cpu, data->cpumask); + WARN_ON(data->refs == 0); + data->refs--; + refs = data->refs; + spin_unlock(&data->lock); + + if (refs) + continue; + + spin_lock(&call_function_lock); + list_del_rcu(&data->csd.list); + spin_unlock(&call_function_lock); + + if (data->csd.flags & CSD_FLAG_WAIT) { + /* + * serialize stores to data with the flag clear + * and wakeup + */ + smp_wmb(); + data->csd.flags &= ~CSD_FLAG_WAIT; + } else + call_rcu(&data->rcu_head, rcu_free_call_data); + } + rcu_read_unlock(); + + put_cpu(); +} + +/* + * Invoked by arch to handle an IPI for call function single. Must be called + * from the arch with interrupts disabled. + */ +void generic_smp_call_function_single_interrupt(void) +{ + struct call_single_queue *q = &__get_cpu_var(call_single_queue); + LIST_HEAD(list); + + /* + * Need to see other stores to list head for checking whether + * list is empty without holding q->lock + */ + smp_mb(); + while (!list_empty(&q->list)) { + unsigned int data_flags; + + spin_lock(&q->lock); + list_replace_init(&q->list, &list); + spin_unlock(&q->lock); + + while (!list_empty(&list)) { + struct call_single_data *data; + + data = list_entry(list.next, struct call_single_data, + list); + list_del(&data->list); + + /* + * 'data' can be invalid after this call if + * flags == 0 (when called through + * generic_exec_single(), so save them away before + * making the call. + */ + data_flags = data->flags; + + data->func(data->info); + + if (data_flags & CSD_FLAG_WAIT) { + smp_wmb(); + data->flags &= ~CSD_FLAG_WAIT; + } else if (data_flags & CSD_FLAG_ALLOC) + kfree(data); + } + /* + * See comment on outer loop + */ + smp_mb(); + } +} + +/* + * smp_call_function_single - Run a function on a specific CPU + * @func: The function to run. This must be fast and non-blocking. + * @info: An arbitrary pointer to pass to the function. + * @retry: Unused + * @wait: If true, wait until function has completed on other CPUs. + * + * Returns 0 on success, else a negative status code. Note that @wait + * will be implicitly turned on in case of allocation failures, since + * we fall back to on-stack allocation. + */ +int smp_call_function_single(int cpu, void (*func) (void *info), void *info, + int retry, int wait) +{ + struct call_single_data d; + unsigned long flags; + /* prevent preemption and reschedule on another processor */ + int me = get_cpu(); + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + if (cpu == me) { + local_irq_save(flags); + func(info); + local_irq_restore(flags); + } else { + struct call_single_data *data = NULL; + + if (!wait) { + data = kmalloc(sizeof(*data), GFP_ATOMIC); + if (data) + data->flags = CSD_FLAG_ALLOC; + } + if (!data) { + data = &d; + data->flags = CSD_FLAG_WAIT; + } + + data->func = func; + data->info = info; + generic_exec_single(cpu, data); + } + + put_cpu(); + return 0; +} +EXPORT_SYMBOL(smp_call_function_single); + +/** + * __smp_call_function_single(): Run a function on another CPU + * @cpu: The CPU to run on. + * @data: Pre-allocated and setup data structure + * + * Like smp_call_function_single(), but allow caller to pass in a pre-allocated + * data structure. Useful for embedding @data inside other structures, for + * instance. + * + */ +void __smp_call_function_single(int cpu, struct call_single_data *data) +{ + /* Can deadlock when called with interrupts disabled */ + WARN_ON((data->flags & CSD_FLAG_WAIT) && irqs_disabled()); + + generic_exec_single(cpu, data); +} + +/** + * smp_call_function_mask(): Run a function on a set of other CPUs. + * @mask: The set of cpus to run on. + * @func: The function to run. This must be fast and non-blocking. + * @info: An arbitrary pointer to pass to the function. + * @wait: If true, wait (atomically) until function has completed on other CPUs. + * + * Returns 0 on success, else a negative status code. + * + * If @wait is true, then returns once @func has returned. Note that @wait + * will be implicitly turned on in case of allocation failures, since + * we fall back to on-stack allocation. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler or from a bottom half handler. Preemption + * must be disabled when calling this function. + */ +int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, + int wait) +{ + struct call_function_data d; + struct call_function_data *data = NULL; + cpumask_t allbutself; + unsigned long flags; + int cpu, num_cpus; + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + cpu = smp_processor_id(); + allbutself = cpu_online_map; + cpu_clear(cpu, allbutself); + cpus_and(mask, mask, allbutself); + num_cpus = cpus_weight(mask); + + /* + * If zero CPUs, return. If just a single CPU, turn this request + * into a targetted single call instead since it's faster. + */ + if (!num_cpus) + return 0; + else if (num_cpus == 1) { + cpu = first_cpu(mask); + return smp_call_function_single(cpu, func, info, 0, wait); + } + + if (!wait) { + data = kmalloc(sizeof(*data), GFP_ATOMIC); + if (data) + data->csd.flags = CSD_FLAG_ALLOC; + } + if (!data) { + data = &d; + data->csd.flags = CSD_FLAG_WAIT; + } + + spin_lock_init(&data->lock); + data->csd.func = func; + data->csd.info = info; + data->refs = num_cpus; + data->cpumask = mask; + + spin_lock_irqsave(&call_function_lock, flags); + list_add_tail_rcu(&data->csd.list, &call_function_queue); + spin_unlock_irqrestore(&call_function_lock, flags); + + /* Send a message to all CPUs in the map */ + arch_send_call_function_ipi(mask); + + /* optionally wait for the CPUs to complete */ + if (wait) + csd_flag_wait(&data->csd); + + return 0; +} +EXPORT_SYMBOL(smp_call_function_mask); + +/** + * smp_call_function(): Run a function on all other CPUs. + * @func: The function to run. This must be fast and non-blocking. + * @info: An arbitrary pointer to pass to the function. + * @natomic: Unused + * @wait: If true, wait (atomically) until function has completed on other CPUs. + * + * Returns 0 on success, else a negative status code. + * + * If @wait is true, then returns once @func has returned; otherwise + * it returns just before the target cpu calls @func. In case of allocation + * failure, @wait will be implicitly turned on. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler or from a bottom half handler. + */ +int smp_call_function(void (*func)(void *), void *info, int natomic, int wait) +{ + int ret; + + preempt_disable(); + ret = smp_call_function_mask(cpu_online_map, func, info, wait); + preempt_enable(); + return ret; +} +EXPORT_SYMBOL(smp_call_function); + +void ipi_call_lock(void) +{ + spin_lock(&call_function_lock); +} + +void ipi_call_unlock(void) +{ + spin_unlock(&call_function_lock); +} + +void ipi_call_lock_irq(void) +{ + spin_lock_irq(&call_function_lock); +} + +void ipi_call_unlock_irq(void) +{ + spin_unlock_irq(&call_function_lock); +} -- cgit v1.2.3 From 8691e5a8f691cc2a4fda0651e8d307aaba0e7d68 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 6 Jun 2008 11:18:06 +0200 Subject: smp_call_function: get rid of the unused nonatomic/retry argument It's never used and the comments refer to nonatomic and retry interchangably. So get rid of it. Acked-by: Jeremy Fitzhardinge Signed-off-by: Jens Axboe --- arch/alpha/kernel/core_marvel.c | 2 +- arch/alpha/kernel/smp.c | 6 +++--- arch/alpha/oprofile/common.c | 6 +++--- arch/arm/oprofile/op_model_mpcore.c | 2 +- arch/arm/vfp/vfpmodule.c | 2 +- arch/cris/arch-v32/kernel/smp.c | 5 ++--- arch/ia64/kernel/mca.c | 2 +- arch/ia64/kernel/palinfo.c | 2 +- arch/ia64/kernel/perfmon.c | 2 +- arch/ia64/kernel/process.c | 2 +- arch/ia64/kernel/smpboot.c | 2 +- arch/ia64/kernel/uncached.c | 5 ++--- arch/ia64/sn/kernel/sn2/sn_hwperf.c | 2 +- arch/m32r/kernel/smp.c | 4 ++-- arch/mips/kernel/smp.c | 4 ++-- arch/mips/mm/c-r4k.c | 18 +++++++++--------- arch/mips/pmc-sierra/yosemite/prom.c | 2 +- arch/mips/sibyte/cfe/setup.c | 2 +- arch/mips/sibyte/sb1250/prom.c | 2 +- arch/powerpc/kernel/smp.c | 2 +- arch/s390/appldata/appldata_base.c | 4 ++-- arch/s390/kernel/smp.c | 16 ++++++---------- arch/s390/kernel/time.c | 4 ++-- arch/sh/kernel/smp.c | 10 +++++----- arch/sparc64/kernel/smp.c | 12 ++++-------- arch/um/kernel/smp.c | 3 +-- arch/x86/kernel/cpu/mtrr/main.c | 4 ++-- arch/x86/kernel/cpuid.c | 2 +- arch/x86/kernel/ldt.c | 2 +- arch/x86/kernel/nmi_32.c | 2 +- arch/x86/kernel/nmi_64.c | 2 +- arch/x86/kernel/smp.c | 2 +- arch/x86/kernel/vsyscall_64.c | 2 +- arch/x86/kvm/vmx.c | 2 +- arch/x86/kvm/x86.c | 2 +- arch/x86/lib/msr-on-cpu.c | 8 ++++---- arch/x86/mach-voyager/voyager_smp.c | 2 +- arch/x86/xen/smp.c | 2 +- drivers/acpi/processor_idle.c | 2 +- drivers/cpuidle/cpuidle.c | 2 +- include/asm-alpha/smp.h | 2 +- include/asm-sparc/smp.h | 2 +- include/linux/smp.h | 8 ++++---- kernel/smp.c | 6 ++---- kernel/softirq.c | 2 +- kernel/time/tick-broadcast.c | 2 +- net/core/flow.c | 2 +- net/iucv/iucv.c | 14 +++++++------- virt/kvm/kvm_main.c | 6 +++--- 49 files changed, 95 insertions(+), 108 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/core_marvel.c b/arch/alpha/kernel/core_marvel.c index ced4aae8b804..04dcc5e5d4c1 100644 --- a/arch/alpha/kernel/core_marvel.c +++ b/arch/alpha/kernel/core_marvel.c @@ -662,7 +662,7 @@ __marvel_rtc_io(u8 b, unsigned long addr, int write) if (smp_processor_id() != boot_cpuid) smp_call_function_single(boot_cpuid, __marvel_access_rtc, - &rtc_access, 1, 1); + &rtc_access, 1); else __marvel_access_rtc(&rtc_access); #else diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 95c905be9154..44114c8dbb2a 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -710,7 +710,7 @@ flush_tlb_mm(struct mm_struct *mm) } } - if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) { + if (smp_call_function(ipi_flush_tlb_mm, mm, 1)) { printk(KERN_CRIT "flush_tlb_mm: timed out\n"); } @@ -763,7 +763,7 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) data.mm = mm; data.addr = addr; - if (smp_call_function(ipi_flush_tlb_page, &data, 1, 1)) { + if (smp_call_function(ipi_flush_tlb_page, &data, 1)) { printk(KERN_CRIT "flush_tlb_page: timed out\n"); } @@ -815,7 +815,7 @@ flush_icache_user_range(struct vm_area_struct *vma, struct page *page, } } - if (smp_call_function(ipi_flush_icache_page, mm, 1, 1)) { + if (smp_call_function(ipi_flush_icache_page, mm, 1)) { printk(KERN_CRIT "flush_icache_page: timed out\n"); } diff --git a/arch/alpha/oprofile/common.c b/arch/alpha/oprofile/common.c index 9fc0eeb4f0ab..7c3d5ec6ec67 100644 --- a/arch/alpha/oprofile/common.c +++ b/arch/alpha/oprofile/common.c @@ -65,7 +65,7 @@ op_axp_setup(void) model->reg_setup(®, ctr, &sys); /* Configure the registers on all cpus. */ - (void)smp_call_function(model->cpu_setup, ®, 0, 1); + (void)smp_call_function(model->cpu_setup, ®, 1); model->cpu_setup(®); return 0; } @@ -86,7 +86,7 @@ op_axp_cpu_start(void *dummy) static int op_axp_start(void) { - (void)smp_call_function(op_axp_cpu_start, NULL, 0, 1); + (void)smp_call_function(op_axp_cpu_start, NULL, 1); op_axp_cpu_start(NULL); return 0; } @@ -101,7 +101,7 @@ op_axp_cpu_stop(void *dummy) static void op_axp_stop(void) { - (void)smp_call_function(op_axp_cpu_stop, NULL, 0, 1); + (void)smp_call_function(op_axp_cpu_stop, NULL, 1); op_axp_cpu_stop(NULL); } diff --git a/arch/arm/oprofile/op_model_mpcore.c b/arch/arm/oprofile/op_model_mpcore.c index 74fae6045650..4458705021e0 100644 --- a/arch/arm/oprofile/op_model_mpcore.c +++ b/arch/arm/oprofile/op_model_mpcore.c @@ -201,7 +201,7 @@ static int em_call_function(int (*fn)(void)) data.ret = 0; preempt_disable(); - smp_call_function(em_func, &data, 1, 1); + smp_call_function(em_func, &data, 1); em_func(&data); preempt_enable(); diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 32455c633f1c..c0d2c9bb952b 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -352,7 +352,7 @@ static int __init vfp_init(void) else if (vfpsid & FPSID_NODOUBLE) { printk("no double precision support\n"); } else { - smp_call_function(vfp_enable, NULL, 1, 1); + smp_call_function(vfp_enable, NULL, 1); VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ printk("implementor %02x architecture %d part %02x variant %x rev %x\n", diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c index a9c3334e46c9..952a24b2f5a9 100644 --- a/arch/cris/arch-v32/kernel/smp.c +++ b/arch/cris/arch-v32/kernel/smp.c @@ -194,7 +194,7 @@ void stop_this_cpu(void* dummy) /* Other calls */ void smp_send_stop(void) { - smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_call_function(stop_this_cpu, NULL, 0); } int setup_profiling_timer(unsigned int multiplier) @@ -316,8 +316,7 @@ int send_ipi(int vector, int wait, cpumask_t cpu_mask) * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -int smp_call_function(void (*func)(void *info), void *info, - int nonatomic, int wait) +int smp_call_function(void (*func)(void *info), void *info, int wait) { cpumask_t cpu_mask = CPU_MASK_ALL; struct call_data_struct data; diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 705176b434b3..9cd818cc7008 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -1881,7 +1881,7 @@ static int __cpuinit mca_cpu_callback(struct notifier_block *nfb, case CPU_ONLINE: case CPU_ONLINE_FROZEN: smp_call_function_single(hotcpu, ia64_mca_cmc_vector_adjust, - NULL, 1, 0); + NULL, 0); break; } return NOTIFY_OK; diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index 9dc00f7fe10e..e5c57f413ca2 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -921,7 +921,7 @@ int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page) /* will send IPI to other CPU and wait for completion of remote call */ - if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 0, 1))) { + if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 1))) { printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: " "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret); return 0; diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 7714a97b0104..9baa48255c12 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -1820,7 +1820,7 @@ pfm_syswide_cleanup_other_cpu(pfm_context_t *ctx) int ret; DPRINT(("calling CPU%d for cleanup\n", ctx->ctx_cpu)); - ret = smp_call_function_single(ctx->ctx_cpu, pfm_syswide_force_stop, ctx, 0, 1); + ret = smp_call_function_single(ctx->ctx_cpu, pfm_syswide_force_stop, ctx, 1); DPRINT(("called CPU%d for cleanup ret=%d\n", ctx->ctx_cpu, ret)); } #endif /* CONFIG_SMP */ diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index a3a34b4eb038..fabaf08d9a69 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -286,7 +286,7 @@ void cpu_idle_wait(void) { smp_mb(); /* kick all the CPUs so that they exit out of pm_idle */ - smp_call_function(do_nothing, NULL, 0, 1); + smp_call_function(do_nothing, NULL, 1); } EXPORT_SYMBOL_GPL(cpu_idle_wait); diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index eaa1b6795a13..9d1d429c6c59 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -317,7 +317,7 @@ ia64_sync_itc (unsigned int master) go[MASTER] = 1; - if (smp_call_function_single(master, sync_master, NULL, 1, 0) < 0) { + if (smp_call_function_single(master, sync_master, NULL, 0) < 0) { printk(KERN_ERR "sync_itc: failed to get attention of CPU %u!\n", master); return; } diff --git a/arch/ia64/kernel/uncached.c b/arch/ia64/kernel/uncached.c index e77995a6e3ed..8eff8c1d40a6 100644 --- a/arch/ia64/kernel/uncached.c +++ b/arch/ia64/kernel/uncached.c @@ -123,8 +123,7 @@ static int uncached_add_chunk(struct uncached_pool *uc_pool, int nid) status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); if (status == PAL_VISIBILITY_OK_REMOTE_NEEDED) { atomic_set(&uc_pool->status, 0); - status = smp_call_function(uncached_ipi_visibility, uc_pool, - 0, 1); + status = smp_call_function(uncached_ipi_visibility, uc_pool, 1); if (status || atomic_read(&uc_pool->status)) goto failed; } else if (status != PAL_VISIBILITY_OK) @@ -146,7 +145,7 @@ static int uncached_add_chunk(struct uncached_pool *uc_pool, int nid) if (status != PAL_STATUS_SUCCESS) goto failed; atomic_set(&uc_pool->status, 0); - status = smp_call_function(uncached_ipi_mc_drain, uc_pool, 0, 1); + status = smp_call_function(uncached_ipi_mc_drain, uc_pool, 1); if (status || atomic_read(&uc_pool->status)) goto failed; diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c index 8cc0c4753d89..636588e7e068 100644 --- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c +++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c @@ -629,7 +629,7 @@ static int sn_hwperf_op_cpu(struct sn_hwperf_op_info *op_info) if (use_ipi) { /* use an interprocessor interrupt to call SAL */ smp_call_function_single(cpu, sn_hwperf_call_sal, - op_info, 1, 1); + op_info, 1); } else { /* migrate the task before calling SAL */ diff --git a/arch/m32r/kernel/smp.c b/arch/m32r/kernel/smp.c index 74eb7bcd5a40..7577f971ea4e 100644 --- a/arch/m32r/kernel/smp.c +++ b/arch/m32r/kernel/smp.c @@ -212,7 +212,7 @@ void smp_flush_tlb_all(void) local_irq_save(flags); __flush_tlb_all(); local_irq_restore(flags); - smp_call_function(flush_tlb_all_ipi, NULL, 1, 1); + smp_call_function(flush_tlb_all_ipi, NULL, 1); preempt_enable(); } @@ -505,7 +505,7 @@ void smp_invalidate_interrupt(void) *==========================================================================*/ void smp_send_stop(void) { - smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_call_function(stop_this_cpu, NULL, 0); } /*==========================================================================* diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index c75b26cb61df..7a9ae830be86 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -167,7 +167,7 @@ static void stop_this_cpu(void *dummy) void smp_send_stop(void) { - smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_call_function(stop_this_cpu, NULL, 0); } void __init smp_cpus_done(unsigned int max_cpus) @@ -266,7 +266,7 @@ static void flush_tlb_mm_ipi(void *mm) static inline void smp_on_other_tlbs(void (*func) (void *info), void *info) { #ifndef CONFIG_MIPS_MT_SMTC - smp_call_function(func, info, 1, 1); + smp_call_function(func, info, 1); #endif } diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 27096751ddce..71df3390c07b 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -43,12 +43,12 @@ * primary cache. */ static inline void r4k_on_each_cpu(void (*func) (void *info), void *info, - int retry, int wait) + int wait) { preempt_disable(); #if !defined(CONFIG_MIPS_MT_SMP) && !defined(CONFIG_MIPS_MT_SMTC) - smp_call_function(func, info, retry, wait); + smp_call_function(func, info, wait); #endif func(info); preempt_enable(); @@ -350,7 +350,7 @@ static inline void local_r4k___flush_cache_all(void * args) static void r4k___flush_cache_all(void) { - r4k_on_each_cpu(local_r4k___flush_cache_all, NULL, 1, 1); + r4k_on_each_cpu(local_r4k___flush_cache_all, NULL, 1); } static inline int has_valid_asid(const struct mm_struct *mm) @@ -397,7 +397,7 @@ static void r4k_flush_cache_range(struct vm_area_struct *vma, int exec = vma->vm_flags & VM_EXEC; if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) - r4k_on_each_cpu(local_r4k_flush_cache_range, vma, 1, 1); + r4k_on_each_cpu(local_r4k_flush_cache_range, vma, 1); } static inline void local_r4k_flush_cache_mm(void * args) @@ -429,7 +429,7 @@ static void r4k_flush_cache_mm(struct mm_struct *mm) if (!cpu_has_dc_aliases) return; - r4k_on_each_cpu(local_r4k_flush_cache_mm, mm, 1, 1); + r4k_on_each_cpu(local_r4k_flush_cache_mm, mm, 1); } struct flush_cache_page_args { @@ -521,7 +521,7 @@ static void r4k_flush_cache_page(struct vm_area_struct *vma, args.addr = addr; args.pfn = pfn; - r4k_on_each_cpu(local_r4k_flush_cache_page, &args, 1, 1); + r4k_on_each_cpu(local_r4k_flush_cache_page, &args, 1); } static inline void local_r4k_flush_data_cache_page(void * addr) @@ -535,7 +535,7 @@ static void r4k_flush_data_cache_page(unsigned long addr) local_r4k_flush_data_cache_page((void *)addr); else r4k_on_each_cpu(local_r4k_flush_data_cache_page, (void *) addr, - 1, 1); + 1); } struct flush_icache_range_args { @@ -571,7 +571,7 @@ static void r4k_flush_icache_range(unsigned long start, unsigned long end) args.start = start; args.end = end; - r4k_on_each_cpu(local_r4k_flush_icache_range, &args, 1, 1); + r4k_on_each_cpu(local_r4k_flush_icache_range, &args, 1); instruction_hazard(); } @@ -672,7 +672,7 @@ static void local_r4k_flush_cache_sigtramp(void * arg) static void r4k_flush_cache_sigtramp(unsigned long addr) { - r4k_on_each_cpu(local_r4k_flush_cache_sigtramp, (void *) addr, 1, 1); + r4k_on_each_cpu(local_r4k_flush_cache_sigtramp, (void *) addr, 1); } static void r4k_flush_icache_all(void) diff --git a/arch/mips/pmc-sierra/yosemite/prom.c b/arch/mips/pmc-sierra/yosemite/prom.c index 35dc435846a6..cf4c868715ac 100644 --- a/arch/mips/pmc-sierra/yosemite/prom.c +++ b/arch/mips/pmc-sierra/yosemite/prom.c @@ -64,7 +64,7 @@ static void prom_exit(void) #ifdef CONFIG_SMP if (smp_processor_id()) /* CPU 1 */ - smp_call_function(prom_cpu0_exit, NULL, 1, 1); + smp_call_function(prom_cpu0_exit, NULL, 1); #endif prom_cpu0_exit(NULL); } diff --git a/arch/mips/sibyte/cfe/setup.c b/arch/mips/sibyte/cfe/setup.c index 33fce826f8bf..fd9604d5555a 100644 --- a/arch/mips/sibyte/cfe/setup.c +++ b/arch/mips/sibyte/cfe/setup.c @@ -74,7 +74,7 @@ static void __noreturn cfe_linux_exit(void *arg) if (!reboot_smp) { /* Get CPU 0 to do the cfe_exit */ reboot_smp = 1; - smp_call_function(cfe_linux_exit, arg, 1, 0); + smp_call_function(cfe_linux_exit, arg, 0); } } else { printk("Passing control back to CFE...\n"); diff --git a/arch/mips/sibyte/sb1250/prom.c b/arch/mips/sibyte/sb1250/prom.c index cf8f6b3de86c..65b1af66b674 100644 --- a/arch/mips/sibyte/sb1250/prom.c +++ b/arch/mips/sibyte/sb1250/prom.c @@ -66,7 +66,7 @@ static void prom_linux_exit(void) { #ifdef CONFIG_SMP if (smp_processor_id()) { - smp_call_function(prom_cpu0_exit, NULL, 1, 1); + smp_call_function(prom_cpu0_exit, NULL, 1); } #endif while(1); diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 37a5ab410dcc..5191b46a611e 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -168,7 +168,7 @@ static void stop_this_cpu(void *dummy) void smp_send_stop(void) { - smp_call_function(stop_this_cpu, NULL, 0, 0); + smp_call_function(stop_this_cpu, NULL, 0); } extern struct gettimeofday_struct do_gtod; diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index ad40729bec3d..837a3b3e7759 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -209,7 +209,7 @@ __appldata_vtimer_setup(int cmd) per_cpu(appldata_timer, i).expires = per_cpu_interval; smp_call_function_single(i, add_virt_timer_periodic, &per_cpu(appldata_timer, i), - 0, 1); + 1); } appldata_timer_active = 1; P_INFO("Monitoring timer started.\n"); @@ -236,7 +236,7 @@ __appldata_vtimer_setup(int cmd) args.timer = &per_cpu(appldata_timer, i); args.expires = per_cpu_interval; smp_call_function_single(i, __appldata_mod_vtimer_wrap, - &args, 0, 1); + &args, 1); } } } diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 5d4fa4b1c74c..276b105fb2a4 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -109,7 +109,7 @@ static void do_call_function(void) } static void __smp_call_function_map(void (*func) (void *info), void *info, - int nonatomic, int wait, cpumask_t map) + int wait, cpumask_t map) { struct call_data_struct data; int cpu, local = 0; @@ -162,7 +162,6 @@ out: * smp_call_function: * @func: the function to run; this must be fast and non-blocking * @info: an arbitrary pointer to pass to the function - * @nonatomic: unused * @wait: if true, wait (atomically) until function has completed on other CPUs * * Run a function on all other CPUs. @@ -170,15 +169,14 @@ out: * You must not call this function with disabled interrupts, from a * hardware interrupt handler or from a bottom half. */ -int smp_call_function(void (*func) (void *info), void *info, int nonatomic, - int wait) +int smp_call_function(void (*func) (void *info), void *info, int wait) { cpumask_t map; spin_lock(&call_lock); map = cpu_online_map; cpu_clear(smp_processor_id(), map); - __smp_call_function_map(func, info, nonatomic, wait, map); + __smp_call_function_map(func, info, wait, map); spin_unlock(&call_lock); return 0; } @@ -189,7 +187,6 @@ EXPORT_SYMBOL(smp_call_function); * @cpu: the CPU where func should run * @func: the function to run; this must be fast and non-blocking * @info: an arbitrary pointer to pass to the function - * @nonatomic: unused * @wait: if true, wait (atomically) until function has completed on other CPUs * * Run a function on one processor. @@ -198,11 +195,10 @@ EXPORT_SYMBOL(smp_call_function); * hardware interrupt handler or from a bottom half. */ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, - int nonatomic, int wait) + int wait) { spin_lock(&call_lock); - __smp_call_function_map(func, info, nonatomic, wait, - cpumask_of_cpu(cpu)); + __smp_call_function_map(func, info, wait, cpumask_of_cpu(cpu)); spin_unlock(&call_lock); return 0; } @@ -228,7 +224,7 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, { spin_lock(&call_lock); cpu_clear(smp_processor_id(), mask); - __smp_call_function_map(func, info, 0, wait, mask); + __smp_call_function_map(func, info, wait, mask); spin_unlock(&call_lock); return 0; } diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 7aec676fefd5..bf7bf2c2236a 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -690,7 +690,7 @@ static int etr_sync_clock(struct etr_aib *aib, int port) */ memset(&etr_sync, 0, sizeof(etr_sync)); preempt_disable(); - smp_call_function(etr_sync_cpu_start, NULL, 0, 0); + smp_call_function(etr_sync_cpu_start, NULL, 0); local_irq_disable(); etr_enable_sync_clock(); @@ -729,7 +729,7 @@ static int etr_sync_clock(struct etr_aib *aib, int port) rc = -EAGAIN; } local_irq_enable(); - smp_call_function(etr_sync_cpu_end,NULL,0,0); + smp_call_function(etr_sync_cpu_end,NULL,0); preempt_enable(); return rc; } diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 2ed8dceb297b..71781ba2675b 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -168,7 +168,7 @@ static void stop_this_cpu(void *unused) void smp_send_stop(void) { - smp_call_function(stop_this_cpu, 0, 1, 0); + smp_call_function(stop_this_cpu, 0, 0); } void arch_send_call_function_ipi(cpumask_t mask) @@ -223,7 +223,7 @@ void flush_tlb_mm(struct mm_struct *mm) preempt_disable(); if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { - smp_call_function(flush_tlb_mm_ipi, (void *)mm, 1, 1); + smp_call_function(flush_tlb_mm_ipi, (void *)mm, 1); } else { int i; for (i = 0; i < num_online_cpus(); i++) @@ -260,7 +260,7 @@ void flush_tlb_range(struct vm_area_struct *vma, fd.vma = vma; fd.addr1 = start; fd.addr2 = end; - smp_call_function(flush_tlb_range_ipi, (void *)&fd, 1, 1); + smp_call_function(flush_tlb_range_ipi, (void *)&fd, 1); } else { int i; for (i = 0; i < num_online_cpus(); i++) @@ -303,7 +303,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) fd.vma = vma; fd.addr1 = page; - smp_call_function(flush_tlb_page_ipi, (void *)&fd, 1, 1); + smp_call_function(flush_tlb_page_ipi, (void *)&fd, 1); } else { int i; for (i = 0; i < num_online_cpus(); i++) @@ -327,6 +327,6 @@ void flush_tlb_one(unsigned long asid, unsigned long vaddr) fd.addr1 = asid; fd.addr2 = vaddr; - smp_call_function(flush_tlb_one_ipi, (void *)&fd, 1, 1); + smp_call_function(flush_tlb_one_ipi, (void *)&fd, 1); local_flush_tlb_one(asid, vaddr); } diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index b82d017a1744..c099d96f1239 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -807,7 +807,6 @@ extern unsigned long xcall_call_function; * smp_call_function(): Run a function on all other CPUs. * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. - * @nonatomic: currently unused. * @wait: If true, wait (atomically) until function has completed on other CPUs. * * Returns 0 on success, else a negative status code. Does not return until @@ -817,8 +816,7 @@ extern unsigned long xcall_call_function; * hardware interrupt handler or from a bottom half handler. */ static int sparc64_smp_call_function_mask(void (*func)(void *info), void *info, - int nonatomic, int wait, - cpumask_t mask) + int wait, cpumask_t mask) { struct call_data_struct data; int cpus; @@ -853,11 +851,9 @@ out_unlock: return 0; } -int smp_call_function(void (*func)(void *info), void *info, - int nonatomic, int wait) +int smp_call_function(void (*func)(void *info), void *info, int wait) { - return sparc64_smp_call_function_mask(func, info, nonatomic, wait, - cpu_online_map); + return sparc64_smp_call_function_mask(func, info, wait, cpu_online_map); } void smp_call_function_client(int irq, struct pt_regs *regs) @@ -894,7 +890,7 @@ static void tsb_sync(void *info) void smp_tsb_sync(struct mm_struct *mm) { - sparc64_smp_call_function_mask(tsb_sync, mm, 0, 1, mm->cpu_vm_mask); + sparc64_smp_call_function_mask(tsb_sync, mm, 1, mm->cpu_vm_mask); } extern unsigned long xcall_flush_tlb_mm; diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c index e1062ec36d40..be2d50c3aa95 100644 --- a/arch/um/kernel/smp.c +++ b/arch/um/kernel/smp.c @@ -214,8 +214,7 @@ void smp_call_function_slave(int cpu) atomic_inc(&scf_finished); } -int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, - int wait) +int smp_call_function(void (*_func)(void *info), void *_info, int wait) { int cpus = num_online_cpus() - 1; int i; diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index 6a1e278d9323..290652cefddb 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c @@ -222,7 +222,7 @@ static void set_mtrr(unsigned int reg, unsigned long base, atomic_set(&data.gate,0); /* Start the ball rolling on other CPUs */ - if (smp_call_function(ipi_handler, &data, 1, 0) != 0) + if (smp_call_function(ipi_handler, &data, 0) != 0) panic("mtrr: timed out waiting for other CPUs\n"); local_irq_save(flags); @@ -822,7 +822,7 @@ void mtrr_ap_init(void) */ void mtrr_save_state(void) { - smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1, 1); + smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1); } static int __init mtrr_init_finialize(void) diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index daff52a62248..336dd43c9158 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -95,7 +95,7 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, for (; count; count -= 16) { cmd.eax = pos; cmd.ecx = pos >> 32; - smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1, 1); + smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); if (copy_to_user(tmp, &cmd, 16)) return -EFAULT; tmp += 16; diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c index 0224c3637c73..cb0a6398c64b 100644 --- a/arch/x86/kernel/ldt.c +++ b/arch/x86/kernel/ldt.c @@ -68,7 +68,7 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) load_LDT(pc); mask = cpumask_of_cpu(smp_processor_id()); if (!cpus_equal(current->mm->cpu_vm_mask, mask)) - smp_call_function(flush_ldt, NULL, 1, 1); + smp_call_function(flush_ldt, NULL, 1); preempt_enable(); #else load_LDT(pc); diff --git a/arch/x86/kernel/nmi_32.c b/arch/x86/kernel/nmi_32.c index 84160f74eeb0..5562dab0bd20 100644 --- a/arch/x86/kernel/nmi_32.c +++ b/arch/x86/kernel/nmi_32.c @@ -87,7 +87,7 @@ int __init check_nmi_watchdog(void) #ifdef CONFIG_SMP if (nmi_watchdog == NMI_LOCAL_APIC) - smp_call_function(nmi_cpu_busy, (void *)&endflag, 0, 0); + smp_call_function(nmi_cpu_busy, (void *)&endflag, 0); #endif for_each_possible_cpu(cpu) diff --git a/arch/x86/kernel/nmi_64.c b/arch/x86/kernel/nmi_64.c index 5a29ded994fa..2f1e4f503c9e 100644 --- a/arch/x86/kernel/nmi_64.c +++ b/arch/x86/kernel/nmi_64.c @@ -96,7 +96,7 @@ int __init check_nmi_watchdog(void) #ifdef CONFIG_SMP if (nmi_watchdog == NMI_LOCAL_APIC) - smp_call_function(nmi_cpu_busy, (void *)&endflag, 0, 0); + smp_call_function(nmi_cpu_busy, (void *)&endflag, 0); #endif for (cpu = 0; cpu < NR_CPUS; cpu++) diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 575aa3d7248a..56546e8a13ac 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -164,7 +164,7 @@ static void native_smp_send_stop(void) if (reboot_force) return; - smp_call_function(stop_this_cpu, NULL, 0, 0); + smp_call_function(stop_this_cpu, NULL, 0); local_irq_save(flags); disable_local_APIC(); local_irq_restore(flags); diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 61efa2f7d564..0a03d57f9b3b 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -278,7 +278,7 @@ cpu_vsyscall_notifier(struct notifier_block *n, unsigned long action, void *arg) { long cpu = (long)arg; if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN) - smp_call_function_single(cpu, cpu_vsyscall_init, NULL, 0, 1); + smp_call_function_single(cpu, cpu_vsyscall_init, NULL, 1); return NOTIFY_DONE; } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 540e95179074..5534fe59b5fc 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -335,7 +335,7 @@ static void vcpu_clear(struct vcpu_vmx *vmx) { if (vmx->vcpu.cpu == -1) return; - smp_call_function_single(vmx->vcpu.cpu, __vcpu_clear, vmx, 0, 1); + smp_call_function_single(vmx->vcpu.cpu, __vcpu_clear, vmx, 1); vmx->launched = 0; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 63a77caa59f1..0faa2546b1cd 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4044,6 +4044,6 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu) * So need not to call smp_call_function_single() in that case. */ if (vcpu->guest_mode && vcpu->cpu != cpu) - smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0, 0); + smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0); put_cpu(); } diff --git a/arch/x86/lib/msr-on-cpu.c b/arch/x86/lib/msr-on-cpu.c index 57d043fa893e..d5a2b39f882b 100644 --- a/arch/x86/lib/msr-on-cpu.c +++ b/arch/x86/lib/msr-on-cpu.c @@ -30,10 +30,10 @@ static int _rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h, int safe) rv.msr_no = msr_no; if (safe) { - smp_call_function_single(cpu, __rdmsr_safe_on_cpu, &rv, 0, 1); + smp_call_function_single(cpu, __rdmsr_safe_on_cpu, &rv, 1); err = rv.err; } else { - smp_call_function_single(cpu, __rdmsr_on_cpu, &rv, 0, 1); + smp_call_function_single(cpu, __rdmsr_on_cpu, &rv, 1); } *l = rv.l; *h = rv.h; @@ -64,10 +64,10 @@ static int _wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h, int safe) rv.l = l; rv.h = h; if (safe) { - smp_call_function_single(cpu, __wrmsr_safe_on_cpu, &rv, 0, 1); + smp_call_function_single(cpu, __wrmsr_safe_on_cpu, &rv, 1); err = rv.err; } else { - smp_call_function_single(cpu, __wrmsr_on_cpu, &rv, 0, 1); + smp_call_function_single(cpu, __wrmsr_on_cpu, &rv, 1); } return err; diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index cb34407a9930..04f596eab749 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c @@ -1113,7 +1113,7 @@ int safe_smp_processor_id(void) /* broadcast a halt to all other CPUs */ static void voyager_smp_send_stop(void) { - smp_call_function(smp_stop_cpu_function, NULL, 1, 1); + smp_call_function(smp_stop_cpu_function, NULL, 1); } /* this function is triggered in time.c when a clock tick fires diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index b3786e749b8e..a1651d029ea8 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -331,7 +331,7 @@ static void stop_self(void *v) void xen_smp_send_stop(void) { - smp_call_function(stop_self, NULL, 0, 0); + smp_call_function(stop_self, NULL, 0); } void xen_smp_send_reschedule(int cpu) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 556ee1585192..4976e5db2b3f 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1339,7 +1339,7 @@ static void smp_callback(void *v) static int acpi_processor_latency_notify(struct notifier_block *b, unsigned long l, void *v) { - smp_call_function(smp_callback, NULL, 0, 1); + smp_call_function(smp_callback, NULL, 1); return NOTIFY_OK; } diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 23554b676d6e..5405769020a1 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -340,7 +340,7 @@ static void smp_callback(void *v) static int cpuidle_latency_notify(struct notifier_block *b, unsigned long l, void *v) { - smp_call_function(smp_callback, NULL, 0, 1); + smp_call_function(smp_callback, NULL, 1); return NOTIFY_OK; } diff --git a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h index 2f60a362d75e..544c69af8168 100644 --- a/include/asm-alpha/smp.h +++ b/include/asm-alpha/smp.h @@ -53,7 +53,7 @@ extern void arch_send_call_function_ipi(cpumask_t mask); #else /* CONFIG_SMP */ #define hard_smp_processor_id() 0 -#define smp_call_function_on_cpu(func,info,retry,wait,cpu) ({ 0; }) +#define smp_call_function_on_cpu(func,info,wait,cpu) ({ 0; }) #endif /* CONFIG_SMP */ diff --git a/include/asm-sparc/smp.h b/include/asm-sparc/smp.h index e6d561599726..b61e74bea06a 100644 --- a/include/asm-sparc/smp.h +++ b/include/asm-sparc/smp.h @@ -72,7 +72,7 @@ static inline void xc5(smpfunc_t func, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { smp_cross_call(func, arg1, arg2, arg3, arg4, arg5); } -static inline int smp_call_function(void (*func)(void *info), void *info, int nonatomic, int wait) +static inline int smp_call_function(void (*func)(void *info), void *info, int wait) { xc1((smpfunc_t)func, (unsigned long)info); return 0; diff --git a/include/linux/smp.h b/include/linux/smp.h index eac3e062250f..338cad1b9548 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -62,11 +62,11 @@ extern void smp_cpus_done(unsigned int max_cpus); /* * Call a function on all other processors */ -int smp_call_function(void(*func)(void *info), void *info, int retry, int wait); +int smp_call_function(void(*func)(void *info), void *info, int wait); int smp_call_function_mask(cpumask_t mask, void(*func)(void *info), void *info, int wait); int smp_call_function_single(int cpuid, void (*func) (void *info), void *info, - int retry, int wait); + int wait); void __smp_call_function_single(int cpuid, struct call_single_data *data); /* @@ -119,7 +119,7 @@ static inline int up_smp_call_function(void (*func)(void *), void *info) { return 0; } -#define smp_call_function(func, info, retry, wait) \ +#define smp_call_function(func, info, wait) \ (up_smp_call_function(func, info)) #define on_each_cpu(func,info,retry,wait) \ ({ \ @@ -131,7 +131,7 @@ static inline int up_smp_call_function(void (*func)(void *), void *info) static inline void smp_send_reschedule(int cpu) { } #define num_booting_cpus() 1 #define smp_prepare_boot_cpu() do {} while (0) -#define smp_call_function_single(cpuid, func, info, retry, wait) \ +#define smp_call_function_single(cpuid, func, info, wait) \ ({ \ WARN_ON(cpuid != 0); \ local_irq_disable(); \ diff --git a/kernel/smp.c b/kernel/smp.c index f77b75c027ad..7e0432a4a0e2 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -195,7 +195,6 @@ void generic_smp_call_function_single_interrupt(void) * smp_call_function_single - Run a function on a specific CPU * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. - * @retry: Unused * @wait: If true, wait until function has completed on other CPUs. * * Returns 0 on success, else a negative status code. Note that @wait @@ -203,7 +202,7 @@ void generic_smp_call_function_single_interrupt(void) * we fall back to on-stack allocation. */ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, - int retry, int wait) + int wait) { struct call_single_data d; unsigned long flags; @@ -339,7 +338,6 @@ EXPORT_SYMBOL(smp_call_function_mask); * smp_call_function(): Run a function on all other CPUs. * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. - * @natomic: Unused * @wait: If true, wait (atomically) until function has completed on other CPUs. * * Returns 0 on success, else a negative status code. @@ -351,7 +349,7 @@ EXPORT_SYMBOL(smp_call_function_mask); * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -int smp_call_function(void (*func)(void *), void *info, int natomic, int wait) +int smp_call_function(void (*func)(void *), void *info, int wait) { int ret; diff --git a/kernel/softirq.c b/kernel/softirq.c index 36e061740047..d73afb4764ef 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -679,7 +679,7 @@ int on_each_cpu(void (*func) (void *info), void *info, int retry, int wait) int ret = 0; preempt_disable(); - ret = smp_call_function(func, info, retry, wait); + ret = smp_call_function(func, info, wait); local_irq_disable(); func(info); local_irq_enable(); diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 57a1f02e5ec0..75e718539dcb 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -266,7 +266,7 @@ void tick_broadcast_on_off(unsigned long reason, int *oncpu) "offline CPU #%d\n", *oncpu); else smp_call_function_single(*oncpu, tick_do_broadcast_on_off, - &reason, 1, 1); + &reason, 1); } /* diff --git a/net/core/flow.c b/net/core/flow.c index 19991175fdeb..5cf81052d044 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -298,7 +298,7 @@ void flow_cache_flush(void) init_completion(&info.completion); local_bh_disable(); - smp_call_function(flow_cache_flush_per_cpu, &info, 1, 0); + smp_call_function(flow_cache_flush_per_cpu, &info, 0); flow_cache_flush_tasklet((unsigned long)&info); local_bh_enable(); diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 918970762131..94d5a45c3a57 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -480,7 +480,7 @@ static void iucv_setmask_mp(void) if (cpu_isset(cpu, iucv_buffer_cpumask) && !cpu_isset(cpu, iucv_irq_cpumask)) smp_call_function_single(cpu, iucv_allow_cpu, - NULL, 0, 1); + NULL, 1); preempt_enable(); } @@ -498,7 +498,7 @@ static void iucv_setmask_up(void) cpumask = iucv_irq_cpumask; cpu_clear(first_cpu(iucv_irq_cpumask), cpumask); for_each_cpu_mask(cpu, cpumask) - smp_call_function_single(cpu, iucv_block_cpu, NULL, 0, 1); + smp_call_function_single(cpu, iucv_block_cpu, NULL, 1); } /** @@ -523,7 +523,7 @@ static int iucv_enable(void) rc = -EIO; preempt_disable(); for_each_online_cpu(cpu) - smp_call_function_single(cpu, iucv_declare_cpu, NULL, 0, 1); + smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); preempt_enable(); if (cpus_empty(iucv_buffer_cpumask)) /* No cpu could declare an iucv buffer. */ @@ -580,7 +580,7 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, case CPU_ONLINE_FROZEN: case CPU_DOWN_FAILED: case CPU_DOWN_FAILED_FROZEN: - smp_call_function_single(cpu, iucv_declare_cpu, NULL, 0, 1); + smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); break; case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: @@ -589,10 +589,10 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, if (cpus_empty(cpumask)) /* Can't offline last IUCV enabled cpu. */ return NOTIFY_BAD; - smp_call_function_single(cpu, iucv_retrieve_cpu, NULL, 0, 1); + smp_call_function_single(cpu, iucv_retrieve_cpu, NULL, 1); if (cpus_empty(iucv_irq_cpumask)) smp_call_function_single(first_cpu(iucv_buffer_cpumask), - iucv_allow_cpu, NULL, 0, 1); + iucv_allow_cpu, NULL, 1); break; } return NOTIFY_OK; @@ -652,7 +652,7 @@ static void iucv_cleanup_queue(void) * pending interrupts force them to the work queue by calling * an empty function on all cpus. */ - smp_call_function(__iucv_cleanup_queue, NULL, 0, 1); + smp_call_function(__iucv_cleanup_queue, NULL, 1); spin_lock_irq(&iucv_queue_lock); list_for_each_entry_safe(p, n, &iucv_task_queue, list) { /* Remove stale work items from the task queue. */ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 2d29e260da3d..ea1f595f8a87 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1266,12 +1266,12 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val, case CPU_UP_CANCELED: printk(KERN_INFO "kvm: disabling virtualization on CPU%d\n", cpu); - smp_call_function_single(cpu, hardware_disable, NULL, 0, 1); + smp_call_function_single(cpu, hardware_disable, NULL, 1); break; case CPU_ONLINE: printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n", cpu); - smp_call_function_single(cpu, hardware_enable, NULL, 0, 1); + smp_call_function_single(cpu, hardware_enable, NULL, 1); break; } return NOTIFY_OK; @@ -1474,7 +1474,7 @@ int kvm_init(void *opaque, unsigned int vcpu_size, for_each_online_cpu(cpu) { smp_call_function_single(cpu, kvm_arch_check_processor_compat, - &r, 0, 1); + &r, 1); if (r < 0) goto out_free_1; } -- cgit v1.2.3 From 15c8b6c1aaaf1c4edd67e2f02e4d8e1bd1a51c0d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 9 May 2008 09:39:44 +0200 Subject: on_each_cpu(): kill unused 'retry' parameter It's not even passed on to smp_call_function() anymore, since that was removed. So kill it. Acked-by: Jeremy Fitzhardinge Reviewed-by: Paul E. McKenney Signed-off-by: Jens Axboe --- arch/alpha/kernel/process.c | 2 +- arch/alpha/kernel/smp.c | 4 ++-- arch/arm/kernel/smp.c | 6 +++--- arch/ia64/kernel/mca.c | 4 ++-- arch/ia64/kernel/perfmon.c | 4 ++-- arch/ia64/kernel/smp.c | 4 ++-- arch/mips/kernel/irq-rm9000.c | 4 ++-- arch/mips/kernel/smp.c | 4 ++-- arch/mips/oprofile/common.c | 6 +++--- arch/parisc/kernel/cache.c | 6 +++--- arch/parisc/kernel/smp.c | 2 +- arch/parisc/mm/init.c | 2 +- arch/powerpc/kernel/rtas.c | 2 +- arch/powerpc/kernel/tau_6xx.c | 4 ++-- arch/powerpc/kernel/time.c | 2 +- arch/powerpc/mm/slice.c | 2 +- arch/powerpc/oprofile/common.c | 6 +++--- arch/s390/kernel/smp.c | 6 +++--- arch/s390/kernel/time.c | 2 +- arch/sh/kernel/smp.c | 4 ++-- arch/sparc64/mm/hugetlbpage.c | 2 +- arch/x86/kernel/cpu/mcheck/mce_64.c | 6 +++--- arch/x86/kernel/cpu/mcheck/non-fatal.c | 2 +- arch/x86/kernel/cpu/perfctr-watchdog.c | 4 ++-- arch/x86/kernel/io_apic_32.c | 2 +- arch/x86/kernel/io_apic_64.c | 2 +- arch/x86/kernel/nmi_32.c | 4 ++-- arch/x86/kernel/nmi_64.c | 4 ++-- arch/x86/kernel/tlb_32.c | 2 +- arch/x86/kernel/tlb_64.c | 2 +- arch/x86/kernel/vsyscall_64.c | 2 +- arch/x86/kvm/vmx.c | 2 +- arch/x86/mach-voyager/voyager_smp.c | 2 +- arch/x86/mm/pageattr.c | 4 ++-- arch/x86/oprofile/nmi_int.c | 10 +++++----- drivers/char/agp/generic.c | 2 +- drivers/lguest/x86/core.c | 4 ++-- fs/buffer.c | 2 +- include/linux/smp.h | 4 ++-- kernel/hrtimer.c | 2 +- kernel/profile.c | 6 +++--- kernel/rcupdate.c | 2 +- kernel/softirq.c | 2 +- mm/page_alloc.c | 2 +- mm/slab.c | 4 ++-- mm/slub.c | 2 +- net/iucv/iucv.c | 2 +- virt/kvm/kvm_main.c | 8 ++++---- 48 files changed, 84 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 96ed82fd9eef..351407e07e71 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -160,7 +160,7 @@ common_shutdown(int mode, char *restart_cmd) struct halt_info args; args.mode = mode; args.restart_cmd = restart_cmd; - on_each_cpu(common_shutdown_1, &args, 1, 0); + on_each_cpu(common_shutdown_1, &args, 0); } void diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 44114c8dbb2a..83df541650fc 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -657,7 +657,7 @@ void smp_imb(void) { /* Must wait other processors to flush their icache before continue. */ - if (on_each_cpu(ipi_imb, NULL, 1, 1)) + if (on_each_cpu(ipi_imb, NULL, 1)) printk(KERN_CRIT "smp_imb: timed out\n"); } EXPORT_SYMBOL(smp_imb); @@ -673,7 +673,7 @@ flush_tlb_all(void) { /* Although we don't have any data to pass, we do want to synchronize with the other processors. */ - if (on_each_cpu(ipi_flush_tlb_all, NULL, 1, 1)) { + if (on_each_cpu(ipi_flush_tlb_all, NULL, 1)) { printk(KERN_CRIT "flush_tlb_all: timed out\n"); } } diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 6344466b2113..5a7c09564d13 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -604,7 +604,7 @@ static inline void ipi_flush_tlb_kernel_range(void *arg) void flush_tlb_all(void) { - on_each_cpu(ipi_flush_tlb_all, NULL, 1, 1); + on_each_cpu(ipi_flush_tlb_all, NULL, 1); } void flush_tlb_mm(struct mm_struct *mm) @@ -631,7 +631,7 @@ void flush_tlb_kernel_page(unsigned long kaddr) ta.ta_start = kaddr; - on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1, 1); + on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); } void flush_tlb_range(struct vm_area_struct *vma, @@ -654,5 +654,5 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) ta.ta_start = start; ta.ta_end = end; - on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1, 1); + on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); } diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 9cd818cc7008..7dd96c127177 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -707,7 +707,7 @@ ia64_mca_cmc_vector_enable (void *dummy) static void ia64_mca_cmc_vector_disable_keventd(struct work_struct *unused) { - on_each_cpu(ia64_mca_cmc_vector_disable, NULL, 1, 0); + on_each_cpu(ia64_mca_cmc_vector_disable, NULL, 0); } /* @@ -719,7 +719,7 @@ ia64_mca_cmc_vector_disable_keventd(struct work_struct *unused) static void ia64_mca_cmc_vector_enable_keventd(struct work_struct *unused) { - on_each_cpu(ia64_mca_cmc_vector_enable, NULL, 1, 0); + on_each_cpu(ia64_mca_cmc_vector_enable, NULL, 0); } /* diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 9baa48255c12..19d4493c6193 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -6508,7 +6508,7 @@ pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) } /* save the current system wide pmu states */ - ret = on_each_cpu(pfm_alt_save_pmu_state, NULL, 0, 1); + ret = on_each_cpu(pfm_alt_save_pmu_state, NULL, 1); if (ret) { DPRINT(("on_each_cpu() failed: %d\n", ret)); goto cleanup_reserve; @@ -6553,7 +6553,7 @@ pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) pfm_alt_intr_handler = NULL; - ret = on_each_cpu(pfm_alt_restore_pmu_state, NULL, 0, 1); + ret = on_each_cpu(pfm_alt_restore_pmu_state, NULL, 1); if (ret) { DPRINT(("on_each_cpu() failed: %d\n", ret)); } diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index 19152dcbf6e4..3676468612b6 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -285,7 +285,7 @@ smp_flush_tlb_cpumask(cpumask_t xcpumask) void smp_flush_tlb_all (void) { - on_each_cpu((void (*)(void *))local_flush_tlb_all, NULL, 1, 1); + on_each_cpu((void (*)(void *))local_flush_tlb_all, NULL, 1); } void @@ -308,7 +308,7 @@ smp_flush_tlb_mm (struct mm_struct *mm) * anyhow, and once a CPU is interrupted, the cost of local_flush_tlb_all() is * rather trivial. */ - on_each_cpu((void (*)(void *))local_finish_flush_tlb_mm, mm, 1, 1); + on_each_cpu((void (*)(void *))local_finish_flush_tlb_mm, mm, 1); } void arch_send_call_function_single_ipi(int cpu) diff --git a/arch/mips/kernel/irq-rm9000.c b/arch/mips/kernel/irq-rm9000.c index ed9febe63d72..b47e4615ec12 100644 --- a/arch/mips/kernel/irq-rm9000.c +++ b/arch/mips/kernel/irq-rm9000.c @@ -49,7 +49,7 @@ static void local_rm9k_perfcounter_irq_startup(void *args) static unsigned int rm9k_perfcounter_irq_startup(unsigned int irq) { - on_each_cpu(local_rm9k_perfcounter_irq_startup, (void *) irq, 0, 1); + on_each_cpu(local_rm9k_perfcounter_irq_startup, (void *) irq, 1); return 0; } @@ -66,7 +66,7 @@ static void local_rm9k_perfcounter_irq_shutdown(void *args) static void rm9k_perfcounter_irq_shutdown(unsigned int irq) { - on_each_cpu(local_rm9k_perfcounter_irq_shutdown, (void *) irq, 0, 1); + on_each_cpu(local_rm9k_perfcounter_irq_shutdown, (void *) irq, 1); } static struct irq_chip rm9k_irq_controller = { diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 7a9ae830be86..4410f172b8ab 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -246,7 +246,7 @@ static void flush_tlb_all_ipi(void *info) void flush_tlb_all(void) { - on_each_cpu(flush_tlb_all_ipi, NULL, 1, 1); + on_each_cpu(flush_tlb_all_ipi, NULL, 1); } static void flush_tlb_mm_ipi(void *mm) @@ -366,7 +366,7 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) .addr2 = end, }; - on_each_cpu(flush_tlb_kernel_range_ipi, &fd, 1, 1); + on_each_cpu(flush_tlb_kernel_range_ipi, &fd, 1); } static void flush_tlb_page_ipi(void *info) diff --git a/arch/mips/oprofile/common.c b/arch/mips/oprofile/common.c index b5f6f71b27bc..dd2fbd6645c1 100644 --- a/arch/mips/oprofile/common.c +++ b/arch/mips/oprofile/common.c @@ -27,7 +27,7 @@ static int op_mips_setup(void) model->reg_setup(ctr); /* Configure the registers on all cpus. */ - on_each_cpu(model->cpu_setup, NULL, 0, 1); + on_each_cpu(model->cpu_setup, NULL, 1); return 0; } @@ -58,7 +58,7 @@ static int op_mips_create_files(struct super_block * sb, struct dentry * root) static int op_mips_start(void) { - on_each_cpu(model->cpu_start, NULL, 0, 1); + on_each_cpu(model->cpu_start, NULL, 1); return 0; } @@ -66,7 +66,7 @@ static int op_mips_start(void) static void op_mips_stop(void) { /* Disable performance monitoring for all counters. */ - on_each_cpu(model->cpu_stop, NULL, 0, 1); + on_each_cpu(model->cpu_stop, NULL, 1); } int __init oprofile_arch_init(struct oprofile_operations *ops) diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index e10d25d2d9c9..5259d8c20676 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -51,12 +51,12 @@ static struct pdc_btlb_info btlb_info __read_mostly; void flush_data_cache(void) { - on_each_cpu(flush_data_cache_local, NULL, 1, 1); + on_each_cpu(flush_data_cache_local, NULL, 1); } void flush_instruction_cache(void) { - on_each_cpu(flush_instruction_cache_local, NULL, 1, 1); + on_each_cpu(flush_instruction_cache_local, NULL, 1); } #endif @@ -515,7 +515,7 @@ static void cacheflush_h_tmp_function(void *dummy) void flush_cache_all(void) { - on_each_cpu(cacheflush_h_tmp_function, NULL, 1, 1); + on_each_cpu(cacheflush_h_tmp_function, NULL, 1); } void flush_cache_mm(struct mm_struct *mm) diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index 126105c76a44..d47f3975c9c6 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c @@ -292,7 +292,7 @@ void arch_send_call_function_single_ipi(int cpu) void smp_flush_tlb_all(void) { - on_each_cpu(flush_tlb_all_local, NULL, 1, 1); + on_each_cpu(flush_tlb_all_local, NULL, 1); } /* diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index ce0da689a89d..b4d6c8777ed0 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -1053,7 +1053,7 @@ void flush_tlb_all(void) do_recycle++; } spin_unlock(&sid_lock); - on_each_cpu(flush_tlb_all_local, NULL, 1, 1); + on_each_cpu(flush_tlb_all_local, NULL, 1); if (do_recycle) { spin_lock(&sid_lock); recycle_sids(recycle_ndirty,recycle_dirty_array); diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 34843c318419..647f3e8677dc 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -747,7 +747,7 @@ static int rtas_ibm_suspend_me(struct rtas_args *args) /* Call function on all CPUs. One of us will make the * rtas call */ - if (on_each_cpu(rtas_percpu_suspend_me, &data, 1, 0)) + if (on_each_cpu(rtas_percpu_suspend_me, &data, 0)) data.error = -EINVAL; wait_for_completion(&done); diff --git a/arch/powerpc/kernel/tau_6xx.c b/arch/powerpc/kernel/tau_6xx.c index 368a4934f7ee..c3a56d65c5a9 100644 --- a/arch/powerpc/kernel/tau_6xx.c +++ b/arch/powerpc/kernel/tau_6xx.c @@ -192,7 +192,7 @@ static void tau_timeout_smp(unsigned long unused) /* schedule ourselves to be run again */ mod_timer(&tau_timer, jiffies + shrink_timer) ; - on_each_cpu(tau_timeout, NULL, 1, 0); + on_each_cpu(tau_timeout, NULL, 0); } /* @@ -234,7 +234,7 @@ int __init TAU_init(void) tau_timer.expires = jiffies + shrink_timer; add_timer(&tau_timer); - on_each_cpu(TAU_init_smp, NULL, 1, 0); + on_each_cpu(TAU_init_smp, NULL, 0); printk("Thermal assist unit "); #ifdef CONFIG_TAU_INT diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 73401e83739a..f1a38a6c1e2d 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -322,7 +322,7 @@ void snapshot_timebases(void) { if (!cpu_has_feature(CPU_FTR_PURR)) return; - on_each_cpu(snapshot_tb_and_purr, NULL, 0, 1); + on_each_cpu(snapshot_tb_and_purr, NULL, 1); } /* diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c index ad928edafb0a..2bd12d965db1 100644 --- a/arch/powerpc/mm/slice.c +++ b/arch/powerpc/mm/slice.c @@ -218,7 +218,7 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz mb(); /* XXX this is sub-optimal but will do for now */ - on_each_cpu(slice_flush_segments, mm, 0, 1); + on_each_cpu(slice_flush_segments, mm, 1); #ifdef CONFIG_SPU_BASE spu_flush_all_slbs(mm); #endif diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c index 4908dc98f9ca..17807acb05d9 100644 --- a/arch/powerpc/oprofile/common.c +++ b/arch/powerpc/oprofile/common.c @@ -65,7 +65,7 @@ static int op_powerpc_setup(void) /* Configure the registers on all cpus. If an error occurs on one * of the cpus, op_per_cpu_rc will be set to the error */ - on_each_cpu(op_powerpc_cpu_setup, NULL, 0, 1); + on_each_cpu(op_powerpc_cpu_setup, NULL, 1); out: if (op_per_cpu_rc) { /* error on setup release the performance counter hardware */ @@ -100,7 +100,7 @@ static int op_powerpc_start(void) if (model->global_start) return model->global_start(ctr); if (model->start) { - on_each_cpu(op_powerpc_cpu_start, NULL, 0, 1); + on_each_cpu(op_powerpc_cpu_start, NULL, 1); return op_per_cpu_rc; } return -EIO; /* No start function is defined for this @@ -115,7 +115,7 @@ static inline void op_powerpc_cpu_stop(void *dummy) static void op_powerpc_stop(void) { if (model->stop) - on_each_cpu(op_powerpc_cpu_stop, NULL, 0, 1); + on_each_cpu(op_powerpc_cpu_stop, NULL, 1); if (model->global_stop) model->global_stop(); } diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 276b105fb2a4..b6781030cfbd 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -299,7 +299,7 @@ static void smp_ptlb_callback(void *info) void smp_ptlb_all(void) { - on_each_cpu(smp_ptlb_callback, NULL, 0, 1); + on_each_cpu(smp_ptlb_callback, NULL, 1); } EXPORT_SYMBOL(smp_ptlb_all); #endif /* ! CONFIG_64BIT */ @@ -347,7 +347,7 @@ void smp_ctl_set_bit(int cr, int bit) memset(&parms.orvals, 0, sizeof(parms.orvals)); memset(&parms.andvals, 0xff, sizeof(parms.andvals)); parms.orvals[cr] = 1 << bit; - on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); + on_each_cpu(smp_ctl_bit_callback, &parms, 1); } EXPORT_SYMBOL(smp_ctl_set_bit); @@ -361,7 +361,7 @@ void smp_ctl_clear_bit(int cr, int bit) memset(&parms.orvals, 0, sizeof(parms.orvals)); memset(&parms.andvals, 0xff, sizeof(parms.andvals)); parms.andvals[cr] = ~(1L << bit); - on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); + on_each_cpu(smp_ctl_bit_callback, &parms, 1); } EXPORT_SYMBOL(smp_ctl_clear_bit); diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index bf7bf2c2236a..6037ed2b7471 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -909,7 +909,7 @@ static void etr_work_fn(struct work_struct *work) if (!eacr.ea) { /* Both ports offline. Reset everything. */ eacr.dp = eacr.es = eacr.sl = 0; - on_each_cpu(etr_disable_sync_clock, NULL, 0, 1); + on_each_cpu(etr_disable_sync_clock, NULL, 1); del_timer_sync(&etr_timer); etr_update_eacr(eacr); set_bit(ETR_FLAG_EACCES, &etr_flags); diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 71781ba2675b..60c50841143e 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -197,7 +197,7 @@ static void flush_tlb_all_ipi(void *info) void flush_tlb_all(void) { - on_each_cpu(flush_tlb_all_ipi, 0, 1, 1); + on_each_cpu(flush_tlb_all_ipi, 0, 1); } static void flush_tlb_mm_ipi(void *mm) @@ -284,7 +284,7 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) fd.addr1 = start; fd.addr2 = end; - on_each_cpu(flush_tlb_kernel_range_ipi, (void *)&fd, 1, 1); + on_each_cpu(flush_tlb_kernel_range_ipi, (void *)&fd, 1); } static void flush_tlb_page_ipi(void *info) diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index 6cfab2e4d340..ebefd2a14375 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -344,7 +344,7 @@ void hugetlb_prefault_arch_hook(struct mm_struct *mm) * also executing in this address space. */ mm->context.sparc64_ctx_val = ctx; - on_each_cpu(context_reload, mm, 0, 0); + on_each_cpu(context_reload, mm, 0); } spin_unlock(&ctx_alloc_lock); } diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index e07e8c068ae0..43b7cb594912 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c @@ -363,7 +363,7 @@ static void mcheck_check_cpu(void *info) static void mcheck_timer(struct work_struct *work) { - on_each_cpu(mcheck_check_cpu, NULL, 1, 1); + on_each_cpu(mcheck_check_cpu, NULL, 1); /* * Alert userspace if needed. If we logged an MCE, reduce the @@ -612,7 +612,7 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, * Collect entries that were still getting written before the * synchronize. */ - on_each_cpu(collect_tscs, cpu_tsc, 1, 1); + on_each_cpu(collect_tscs, cpu_tsc, 1); for (i = next; i < MCE_LOG_LEN; i++) { if (mcelog.entry[i].finished && mcelog.entry[i].tsc < cpu_tsc[mcelog.entry[i].cpu]) { @@ -737,7 +737,7 @@ static void mce_restart(void) if (next_interval) cancel_delayed_work(&mcheck_work); /* Timer race is harmless here */ - on_each_cpu(mce_init, NULL, 1, 1); + on_each_cpu(mce_init, NULL, 1); next_interval = check_interval * HZ; if (next_interval) schedule_delayed_work(&mcheck_work, diff --git a/arch/x86/kernel/cpu/mcheck/non-fatal.c b/arch/x86/kernel/cpu/mcheck/non-fatal.c index 00ccb6c14ec2..cc1fccdd31e0 100644 --- a/arch/x86/kernel/cpu/mcheck/non-fatal.c +++ b/arch/x86/kernel/cpu/mcheck/non-fatal.c @@ -59,7 +59,7 @@ static DECLARE_DELAYED_WORK(mce_work, mce_work_fn); static void mce_work_fn(struct work_struct *work) { - on_each_cpu(mce_checkregs, NULL, 1, 1); + on_each_cpu(mce_checkregs, NULL, 1); schedule_delayed_work(&mce_work, round_jiffies_relative(MCE_RATE)); } diff --git a/arch/x86/kernel/cpu/perfctr-watchdog.c b/arch/x86/kernel/cpu/perfctr-watchdog.c index f9ae93adffe5..58043f06d7e2 100644 --- a/arch/x86/kernel/cpu/perfctr-watchdog.c +++ b/arch/x86/kernel/cpu/perfctr-watchdog.c @@ -180,7 +180,7 @@ void disable_lapic_nmi_watchdog(void) if (atomic_read(&nmi_active) <= 0) return; - on_each_cpu(stop_apic_nmi_watchdog, NULL, 0, 1); + on_each_cpu(stop_apic_nmi_watchdog, NULL, 1); wd_ops->unreserve(); BUG_ON(atomic_read(&nmi_active) != 0); @@ -202,7 +202,7 @@ void enable_lapic_nmi_watchdog(void) return; } - on_each_cpu(setup_apic_nmi_watchdog, NULL, 0, 1); + on_each_cpu(setup_apic_nmi_watchdog, NULL, 1); touch_nmi_watchdog(); } diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c index 4dc8600d9d20..720640ff36ca 100644 --- a/arch/x86/kernel/io_apic_32.c +++ b/arch/x86/kernel/io_apic_32.c @@ -1565,7 +1565,7 @@ void /*__init*/ print_local_APIC(void * dummy) void print_all_local_APICs (void) { - on_each_cpu(print_local_APIC, NULL, 1, 1); + on_each_cpu(print_local_APIC, NULL, 1); } void /*__init*/ print_PIC(void) diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index ef1a8dfcc529..4504c7f50012 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -1146,7 +1146,7 @@ void __apicdebuginit print_local_APIC(void * dummy) void print_all_local_APICs (void) { - on_each_cpu(print_local_APIC, NULL, 1, 1); + on_each_cpu(print_local_APIC, NULL, 1); } void __apicdebuginit print_PIC(void) diff --git a/arch/x86/kernel/nmi_32.c b/arch/x86/kernel/nmi_32.c index 5562dab0bd20..11008e0857c0 100644 --- a/arch/x86/kernel/nmi_32.c +++ b/arch/x86/kernel/nmi_32.c @@ -218,7 +218,7 @@ static void __acpi_nmi_enable(void *__unused) void acpi_nmi_enable(void) { if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC) - on_each_cpu(__acpi_nmi_enable, NULL, 0, 1); + on_each_cpu(__acpi_nmi_enable, NULL, 1); } static void __acpi_nmi_disable(void *__unused) @@ -232,7 +232,7 @@ static void __acpi_nmi_disable(void *__unused) void acpi_nmi_disable(void) { if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC) - on_each_cpu(__acpi_nmi_disable, NULL, 0, 1); + on_each_cpu(__acpi_nmi_disable, NULL, 1); } void setup_apic_nmi_watchdog(void *unused) diff --git a/arch/x86/kernel/nmi_64.c b/arch/x86/kernel/nmi_64.c index 2f1e4f503c9e..bbdcb17b3dfe 100644 --- a/arch/x86/kernel/nmi_64.c +++ b/arch/x86/kernel/nmi_64.c @@ -225,7 +225,7 @@ static void __acpi_nmi_enable(void *__unused) void acpi_nmi_enable(void) { if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC) - on_each_cpu(__acpi_nmi_enable, NULL, 0, 1); + on_each_cpu(__acpi_nmi_enable, NULL, 1); } static void __acpi_nmi_disable(void *__unused) @@ -239,7 +239,7 @@ static void __acpi_nmi_disable(void *__unused) void acpi_nmi_disable(void) { if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC) - on_each_cpu(__acpi_nmi_disable, NULL, 0, 1); + on_each_cpu(__acpi_nmi_disable, NULL, 1); } void setup_apic_nmi_watchdog(void *unused) diff --git a/arch/x86/kernel/tlb_32.c b/arch/x86/kernel/tlb_32.c index 9bb2363851af..fec1ecedc9b7 100644 --- a/arch/x86/kernel/tlb_32.c +++ b/arch/x86/kernel/tlb_32.c @@ -238,6 +238,6 @@ static void do_flush_tlb_all(void *info) void flush_tlb_all(void) { - on_each_cpu(do_flush_tlb_all, NULL, 1, 1); + on_each_cpu(do_flush_tlb_all, NULL, 1); } diff --git a/arch/x86/kernel/tlb_64.c b/arch/x86/kernel/tlb_64.c index a1f07d793202..184a367516d3 100644 --- a/arch/x86/kernel/tlb_64.c +++ b/arch/x86/kernel/tlb_64.c @@ -270,5 +270,5 @@ static void do_flush_tlb_all(void *info) void flush_tlb_all(void) { - on_each_cpu(do_flush_tlb_all, NULL, 1, 1); + on_each_cpu(do_flush_tlb_all, NULL, 1); } diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 0a03d57f9b3b..0dcae19ed627 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -301,7 +301,7 @@ static int __init vsyscall_init(void) #ifdef CONFIG_SYSCTL register_sysctl_table(kernel_root_table2); #endif - on_each_cpu(cpu_vsyscall_init, NULL, 0, 1); + on_each_cpu(cpu_vsyscall_init, NULL, 1); hotcpu_notifier(cpu_vsyscall_notifier, 0); return 0; } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 5534fe59b5fc..10ce6ee4c491 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2968,7 +2968,7 @@ static void vmx_free_vmcs(struct kvm_vcpu *vcpu) struct vcpu_vmx *vmx = to_vmx(vcpu); if (vmx->vmcs) { - on_each_cpu(__vcpu_clear, vmx, 0, 1); + on_each_cpu(__vcpu_clear, vmx, 1); free_vmcs(vmx->vmcs); vmx->vmcs = NULL; } diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index 04f596eab749..abea08459a73 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c @@ -1072,7 +1072,7 @@ static void do_flush_tlb_all(void *info) /* flush the TLB of every active CPU in the system */ void flush_tlb_all(void) { - on_each_cpu(do_flush_tlb_all, 0, 1, 1); + on_each_cpu(do_flush_tlb_all, 0, 1); } /* used to set up the trampoline for other CPUs when the memory manager diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 60bcb5b6a37e..9b836ba9dedd 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -106,7 +106,7 @@ static void cpa_flush_all(unsigned long cache) { BUG_ON(irqs_disabled()); - on_each_cpu(__cpa_flush_all, (void *) cache, 1, 1); + on_each_cpu(__cpa_flush_all, (void *) cache, 1); } static void __cpa_flush_range(void *arg) @@ -127,7 +127,7 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache) BUG_ON(irqs_disabled()); WARN_ON(PAGE_ALIGN(start) != start); - on_each_cpu(__cpa_flush_range, NULL, 1, 1); + on_each_cpu(__cpa_flush_range, NULL, 1); if (!cache) return; diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index cc48d3fde545..3238ad32ffd8 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -218,8 +218,8 @@ static int nmi_setup(void) } } - on_each_cpu(nmi_save_registers, NULL, 0, 1); - on_each_cpu(nmi_cpu_setup, NULL, 0, 1); + on_each_cpu(nmi_save_registers, NULL, 1); + on_each_cpu(nmi_cpu_setup, NULL, 1); nmi_enabled = 1; return 0; } @@ -271,7 +271,7 @@ static void nmi_shutdown(void) { struct op_msrs *msrs = &__get_cpu_var(cpu_msrs); nmi_enabled = 0; - on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1); + on_each_cpu(nmi_cpu_shutdown, NULL, 1); unregister_die_notifier(&profile_exceptions_nb); model->shutdown(msrs); free_msrs(); @@ -285,7 +285,7 @@ static void nmi_cpu_start(void *dummy) static int nmi_start(void) { - on_each_cpu(nmi_cpu_start, NULL, 0, 1); + on_each_cpu(nmi_cpu_start, NULL, 1); return 0; } @@ -297,7 +297,7 @@ static void nmi_cpu_stop(void *dummy) static void nmi_stop(void) { - on_each_cpu(nmi_cpu_stop, NULL, 0, 1); + on_each_cpu(nmi_cpu_stop, NULL, 1); } struct op_counter_config counter_config[OP_MAX_COUNTER]; diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 564daaa6c7d0..eaa1a355bb32 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1249,7 +1249,7 @@ static void ipi_handler(void *null) void global_cache_flush(void) { - if (on_each_cpu(ipi_handler, NULL, 1, 1) != 0) + if (on_each_cpu(ipi_handler, NULL, 1) != 0) panic(PFX "timed out waiting for the other CPUs!\n"); } EXPORT_SYMBOL(global_cache_flush); diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 2e554a4ab337..95dfda52b4f9 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -478,7 +478,7 @@ void __init lguest_arch_host_init(void) cpu_had_pge = 1; /* adjust_pge is a helper function which sets or unsets the PGE * bit on its CPU, depending on the argument (0 == unset). */ - on_each_cpu(adjust_pge, (void *)0, 0, 1); + on_each_cpu(adjust_pge, (void *)0, 1); /* Turn off the feature in the global feature set. */ clear_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability); } @@ -493,7 +493,7 @@ void __exit lguest_arch_host_fini(void) if (cpu_had_pge) { set_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability); /* adjust_pge's argument "1" means set PGE. */ - on_each_cpu(adjust_pge, (void *)1, 0, 1); + on_each_cpu(adjust_pge, (void *)1, 1); } put_online_cpus(); } diff --git a/fs/buffer.c b/fs/buffer.c index a073f3f4f013..5c23ef560d01 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1464,7 +1464,7 @@ static void invalidate_bh_lru(void *arg) void invalidate_bh_lrus(void) { - on_each_cpu(invalidate_bh_lru, NULL, 1, 1); + on_each_cpu(invalidate_bh_lru, NULL, 1); } EXPORT_SYMBOL_GPL(invalidate_bh_lrus); diff --git a/include/linux/smp.h b/include/linux/smp.h index 338cad1b9548..55261101d09a 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -89,7 +89,7 @@ static inline void init_call_single_data(void) /* * Call a function on all processors */ -int on_each_cpu(void (*func) (void *info), void *info, int retry, int wait); +int on_each_cpu(void (*func) (void *info), void *info, int wait); #define MSG_ALL_BUT_SELF 0x8000 /* Assume <32768 CPU's */ #define MSG_ALL 0x8001 @@ -121,7 +121,7 @@ static inline int up_smp_call_function(void (*func)(void *), void *info) } #define smp_call_function(func, info, wait) \ (up_smp_call_function(func, info)) -#define on_each_cpu(func,info,retry,wait) \ +#define on_each_cpu(func,info,wait) \ ({ \ local_irq_disable(); \ func(info); \ diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 421be5fe5cc7..50e8616d7955 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -623,7 +623,7 @@ static void retrigger_next_event(void *arg) void clock_was_set(void) { /* Retrigger the CPU local events everywhere */ - on_each_cpu(retrigger_next_event, NULL, 0, 1); + on_each_cpu(retrigger_next_event, NULL, 1); } /* diff --git a/kernel/profile.c b/kernel/profile.c index ae7ead82cbc9..58926411eb2a 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -252,7 +252,7 @@ static void profile_flip_buffers(void) mutex_lock(&profile_flip_mutex); j = per_cpu(cpu_profile_flip, get_cpu()); put_cpu(); - on_each_cpu(__profile_flip_buffers, NULL, 0, 1); + on_each_cpu(__profile_flip_buffers, NULL, 1); for_each_online_cpu(cpu) { struct profile_hit *hits = per_cpu(cpu_profile_hits, cpu)[j]; for (i = 0; i < NR_PROFILE_HIT; ++i) { @@ -275,7 +275,7 @@ static void profile_discard_flip_buffers(void) mutex_lock(&profile_flip_mutex); i = per_cpu(cpu_profile_flip, get_cpu()); put_cpu(); - on_each_cpu(__profile_flip_buffers, NULL, 0, 1); + on_each_cpu(__profile_flip_buffers, NULL, 1); for_each_online_cpu(cpu) { struct profile_hit *hits = per_cpu(cpu_profile_hits, cpu)[i]; memset(hits, 0, NR_PROFILE_HIT*sizeof(struct profile_hit)); @@ -558,7 +558,7 @@ static int __init create_hash_tables(void) out_cleanup: prof_on = 0; smp_mb(); - on_each_cpu(profile_nop, NULL, 0, 1); + on_each_cpu(profile_nop, NULL, 1); for_each_online_cpu(cpu) { struct page *page; diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index c09605f8d16c..6addab5e6d88 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -127,7 +127,7 @@ void rcu_barrier(void) * until all the callbacks are queued. */ rcu_read_lock(); - on_each_cpu(rcu_barrier_func, NULL, 0, 1); + on_each_cpu(rcu_barrier_func, NULL, 1); rcu_read_unlock(); wait_for_completion(&rcu_barrier_completion); mutex_unlock(&rcu_barrier_mutex); diff --git a/kernel/softirq.c b/kernel/softirq.c index d73afb4764ef..c159fd094772 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -674,7 +674,7 @@ __init int spawn_ksoftirqd(void) /* * Call a function on all processors */ -int on_each_cpu(void (*func) (void *info), void *info, int retry, int wait) +int on_each_cpu(void (*func) (void *info), void *info, int wait) { int ret = 0; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2f552955a02f..53242344a774 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -918,7 +918,7 @@ void drain_local_pages(void *arg) */ void drain_all_pages(void) { - on_each_cpu(drain_local_pages, NULL, 0, 1); + on_each_cpu(drain_local_pages, NULL, 1); } #ifdef CONFIG_HIBERNATION diff --git a/mm/slab.c b/mm/slab.c index 046607f05f3e..0772abb412b9 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2454,7 +2454,7 @@ static void drain_cpu_caches(struct kmem_cache *cachep) struct kmem_list3 *l3; int node; - on_each_cpu(do_drain, cachep, 1, 1); + on_each_cpu(do_drain, cachep, 1); check_irq_on(); for_each_online_node(node) { l3 = cachep->nodelists[node]; @@ -3939,7 +3939,7 @@ static int do_tune_cpucache(struct kmem_cache *cachep, int limit, } new->cachep = cachep; - on_each_cpu(do_ccupdate_local, (void *)new, 1, 1); + on_each_cpu(do_ccupdate_local, (void *)new, 1); check_irq_on(); cachep->batchcount = batchcount; diff --git a/mm/slub.c b/mm/slub.c index 0987d1cd943c..44715eb70c06 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1497,7 +1497,7 @@ static void flush_cpu_slab(void *d) static void flush_all(struct kmem_cache *s) { #ifdef CONFIG_SMP - on_each_cpu(flush_cpu_slab, s, 1, 1); + on_each_cpu(flush_cpu_slab, s, 1); #else unsigned long flags; diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 94d5a45c3a57..a178e27e7b1a 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -545,7 +545,7 @@ out: */ static void iucv_disable(void) { - on_each_cpu(iucv_retrieve_cpu, NULL, 0, 1); + on_each_cpu(iucv_retrieve_cpu, NULL, 1); kfree(iucv_path_table); } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index ea1f595f8a87..d4eae6af0738 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1286,7 +1286,7 @@ static int kvm_reboot(struct notifier_block *notifier, unsigned long val, * in vmx root mode. */ printk(KERN_INFO "kvm: exiting hardware virtualization\n"); - on_each_cpu(hardware_disable, NULL, 0, 1); + on_each_cpu(hardware_disable, NULL, 1); } return NOTIFY_OK; } @@ -1479,7 +1479,7 @@ int kvm_init(void *opaque, unsigned int vcpu_size, goto out_free_1; } - on_each_cpu(hardware_enable, NULL, 0, 1); + on_each_cpu(hardware_enable, NULL, 1); r = register_cpu_notifier(&kvm_cpu_notifier); if (r) goto out_free_2; @@ -1525,7 +1525,7 @@ out_free_3: unregister_reboot_notifier(&kvm_reboot_notifier); unregister_cpu_notifier(&kvm_cpu_notifier); out_free_2: - on_each_cpu(hardware_disable, NULL, 0, 1); + on_each_cpu(hardware_disable, NULL, 1); out_free_1: kvm_arch_hardware_unsetup(); out_free_0: @@ -1547,7 +1547,7 @@ void kvm_exit(void) sysdev_class_unregister(&kvm_sysdev_class); unregister_reboot_notifier(&kvm_reboot_notifier); unregister_cpu_notifier(&kvm_cpu_notifier); - on_each_cpu(hardware_disable, NULL, 0, 1); + on_each_cpu(hardware_disable, NULL, 1); kvm_arch_hardware_unsetup(); kvm_arch_exit(); kvm_exit_debug(); -- cgit v1.2.3 From f3146aff7f283c8699e0c97df6307a705786eeba Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:56 -0300 Subject: rfkill: clarify meaning of rfkill states rfkill really should have been named rfswitch. As it is, one can get confused whether RFKILL_STATE_ON means the KILL switch is on (and therefore, the radio is being *blocked* from operating), or whether it means the RADIO rf output is on. Clearly state that RFKILL_STATE_ON means the radio is *unblocked* from operating (i.e. there is no rf killing going on). Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- Documentation/rfkill.txt | 7 +++++++ include/linux/rfkill.h | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index a83ff23cd68c..ec75d6d34785 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -8,6 +8,13 @@ rfkill - RF switch subsystem support =============================================================================== 1: Implementation details +The rfkill switch subsystem exists to add a generic interface to circuitry that +can enable or disable the RF output of a radio *transmitter* of any type. + +When a rfkill switch is in the RFKILL_STATE_ON, the radio transmitter is +*enabled*. When the rfkill switch is in the RFKILL_STATE_OFF, the radio +transmitter is *disabled*. + The rfkill switch subsystem offers support for keys often found on laptops to enable wireless devices like WiFi and Bluetooth. diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index e3ab21d7fc7f..ca89ae1b0219 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -44,8 +44,8 @@ enum rfkill_type { }; enum rfkill_state { - RFKILL_STATE_OFF = 0, - RFKILL_STATE_ON = 1, + RFKILL_STATE_OFF = 0, /* Radio output blocked */ + RFKILL_STATE_ON = 1, /* Radio output active */ }; /** @@ -53,7 +53,7 @@ enum rfkill_state { * @name: Name of the switch. * @type: Radio type which the button controls, the value stored * here should be a value from enum rfkill_type. - * @state: State of the switch (on/off). + * @state: State of the switch, "ON" means radio can operate. * @user_claim_unsupported: Whether the hardware supports exclusive * RF-kill control by userspace. Set this before registering. * @user_claim: Set when the switch is controlled exlusively by userspace. -- cgit v1.2.3 From 801e49af4c1a9b988ba0d25de2b368c99c3bf2b3 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:00 -0300 Subject: rfkill: add read-write rfkill switch support Currently, rfkill support for read/write rfkill switches is hacked through a round-trip over the input layer and rfkill-input to let a driver sync rfkill->state to hardware changes. This is buggy and sub-optimal. It causes real problems. It is best to think of the rfkill class as supporting only write-only switches at the moment. In order to implement the read/write functionality properly: Add a get_state() hook that is called by the class every time it needs to fetch the current state of the switch. Add a call to this hook every time the *current* state of the radio plays a role in a decision. Also add a force_state() method that can be used to forcefully syncronize the class' idea of the current state of the switch. This allows for a faster implementation of the read/write functionality, as a driver which get events on switch changes can avoid the need for a get_state() hook. If the get_state() hook is left as NULL, current behaviour is maintained, so this change is fully backwards compatible with the current rfkill drivers. For hardware that issues events when the rfkill state changes, leave get_state() NULL in the rfkill struct, set the initial state properly before registering with the rfkill class, and use the force_state() method in the driver to keep the rfkill interface up-to-date. get_state() can be called by the class from atomic context. It must not sleep. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- include/linux/rfkill.h | 5 +++++ net/rfkill/rfkill.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index ca89ae1b0219..844e96114861 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -61,6 +61,8 @@ enum rfkill_state { * @data: Pointer to the RF button drivers private data which will be * passed along when toggling radio state. * @toggle_radio(): Mandatory handler to control state of the radio. + * @get_state(): handler to read current radio state from hardware, + * may be called from atomic context, should return 0 on success. * @led_trigger: A LED trigger for this button's LED. * @dev: Device structure integrating the switch into device tree. * @node: Used to place switch into list of all switches known to the @@ -80,6 +82,7 @@ struct rfkill { void *data; int (*toggle_radio)(void *data, enum rfkill_state state); + int (*get_state)(void *data, enum rfkill_state *state); #ifdef CONFIG_RFKILL_LEDS struct led_trigger led_trigger; @@ -95,6 +98,8 @@ void rfkill_free(struct rfkill *rfkill); int rfkill_register(struct rfkill *rfkill); void rfkill_unregister(struct rfkill *rfkill); +int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); + /** * rfkill_get_led_name - Get the LED trigger name for the button's LED. * This function might return a NULL pointer if registering of the diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3edc585dcfa6..4ae4486c77ea 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -62,19 +62,39 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void update_rfkill_state(struct rfkill *rfkill) +{ + enum rfkill_state newstate; + + if (rfkill->get_state) { + mutex_lock(&rfkill->mutex); + if (!rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; + mutex_unlock(&rfkill->mutex); + } +} + static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state) { int retval = 0; + enum rfkill_state oldstate, newstate; + + oldstate = rfkill->state; + + if (rfkill->get_state && + !rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; if (state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) { + if (!retval) rfkill->state = state; - rfkill_led_trigger(rfkill, state); - } } + if (rfkill->state != oldstate) + rfkill_led_trigger(rfkill, rfkill->state); + return retval; } @@ -105,6 +125,32 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_force_state - Force the internal rfkill radio state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current radio state the class should be forced to. + * + * This function updates the internal state of the radio cached + * by the rfkill class. It should be used when the driver gets + * a notification by the firmware/hardware of the current *real* + * state of the radio rfkill switch. + * + * It may not be called from an atomic context. + */ +int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) +{ + if (state != RFKILL_STATE_OFF && + state != RFKILL_STATE_ON) + return -EINVAL; + + mutex_lock(&rfkill->mutex); + rfkill->state = state; + mutex_unlock(&rfkill->mutex); + + return 0; +} +EXPORT_SYMBOL(rfkill_force_state); + static ssize_t rfkill_name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -147,6 +193,7 @@ static ssize_t rfkill_state_show(struct device *dev, { struct rfkill *rfkill = to_rfkill(dev); + update_rfkill_state(rfkill); return sprintf(buf, "%d\n", rfkill->state); } -- cgit v1.2.3 From 477576a073699783abb53ae14993d5d41c66301d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:01 -0300 Subject: rfkill: add the WWAN radio type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately, instead of adding a generic Wireless WAN type, a technology- specific type (WiMAX) was added. That's useless for other WWAN devices, such as EDGE, UMTS, X-RTT and other such radios. Add a WWAN rfkill type for generic wireless WAN devices. No keys are added as most devices really want to use KEY_WLAN for WWAN control (in a cycle of none, WLAN, WWAN, WLAN+WWAN) and need no specific keycode added. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Iñaky Pérez-González Cc: David S. Miller Signed-off-by: John W. Linville --- include/linux/rfkill.h | 2 ++ net/rfkill/rfkill-input.c | 4 ++++ net/rfkill/rfkill.c | 3 +++ 3 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 844e96114861..c0cab7d37828 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -34,12 +34,14 @@ * RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. * RFKILL_TYPE_UWB: switch is on a ultra wideband device. * RFKILL_TYPE_WIMAX: switch is on a WiMAX device. + * RFKILL_TYPE_WWAN: switch is on a wireless WAN device. */ enum rfkill_type { RFKILL_TYPE_WLAN , RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_UWB, RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, RFKILL_TYPE_MAX, }; diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 9d6c9255bf2c..29c13d308b31 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -101,6 +101,7 @@ static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); +static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN); static void rfkill_event(struct input_handle *handle, unsigned int type, unsigned int code, int data) @@ -126,6 +127,9 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, switch (code) { case SW_RFKILL_ALL: /* EVERY radio type. data != 0 means radios ON */ + rfkill_schedule_set(&rfkill_wwan, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); rfkill_schedule_set(&rfkill_wimax, (data)? RFKILL_STATE_ON: RFKILL_STATE_OFF); diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 4ae4486c77ea..79f3bbb027ff 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -180,6 +180,9 @@ static ssize_t rfkill_type_show(struct device *dev, case RFKILL_TYPE_WIMAX: type = "wimax"; break; + case RFKILL_TYPE_WWAN: + type = "wwan"; + break; default: BUG(); } -- cgit v1.2.3 From 79399a8d1908f6a406e82d23c5a9937e1722ed3a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:03 -0300 Subject: rfkill: add notifier chains support Add a notifier chain for use by the rfkill class. This notifier chain signals the following events (more to be added when needed): 1. rfkill: rfkill device state has changed A pointer to the rfkill struct will be passed as a parameter. The notifier message types have been added to include/linux/rfkill.h instead of to include/linux/notifier.h in order to avoid the madness of modifying a header used globally (and that triggers an almost full tree rebuild every time it is touched) with information that is of interest only to code that includes the rfkill.h header. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- include/linux/rfkill.h | 7 +++++ net/rfkill/rfkill.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index c0cab7d37828..98667becdee4 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -117,4 +117,11 @@ static inline char *rfkill_get_led_name(struct rfkill *rfkill) #endif } +/* rfkill notification chain */ +#define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill + switch has changed */ + +int register_rfkill_notifier(struct notifier_block *nb); +int unregister_rfkill_notifier(struct notifier_block *nb); + #endif /* RFKILL_H */ diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index fb566902030a..a561e350a70a 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -46,6 +46,49 @@ MODULE_PARM_DESC(default_state, static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; +static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); + + +/** + * register_rfkill_notifier - Add notifier to rfkill notifier chain + * @nb: pointer to the new entry to add to the chain + * + * See blocking_notifier_chain_register() for return value and further + * observations. + * + * Adds a notifier to the rfkill notifier chain. The chain will be + * called with a pointer to the relevant rfkill structure as a parameter, + * refer to include/linux/rfkill.h for the possible events. + * + * Notifiers added to this chain are to always return NOTIFY_DONE. This + * chain is a blocking notifier chain: notifiers can sleep. + * + * Calls to this chain may have been done through a workqueue. One must + * assume unordered asynchronous behaviour, there is no way to know if + * actions related to the event that generated the notification have been + * carried out already. + */ +int register_rfkill_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&rfkill_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(register_rfkill_notifier); + +/** + * unregister_rfkill_notifier - remove notifier from rfkill notifier chain + * @nb: pointer to the entry to remove from the chain + * + * See blocking_notifier_chain_unregister() for return value and further + * observations. + * + * Removes a notifier from the rfkill notifier chain. + */ +int unregister_rfkill_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(unregister_rfkill_notifier); + static void rfkill_led_trigger(struct rfkill *rfkill, enum rfkill_state state) @@ -62,14 +105,25 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void notify_rfkill_state_change(struct rfkill *rfkill) +{ + blocking_notifier_call_chain(&rfkill_notifier_list, + RFKILL_STATE_CHANGED, + rfkill); +} + static void update_rfkill_state(struct rfkill *rfkill) { - enum rfkill_state newstate; + enum rfkill_state newstate, oldstate; if (rfkill->get_state) { mutex_lock(&rfkill->mutex); - if (!rfkill->get_state(rfkill->data, &newstate)) + if (!rfkill->get_state(rfkill->data, &newstate)) { + oldstate = rfkill->state; rfkill->state = newstate; + if (oldstate != newstate) + notify_rfkill_state_change(rfkill); + } mutex_unlock(&rfkill->mutex); } } @@ -93,8 +147,10 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, rfkill->state = state; } - if (force || rfkill->state != oldstate) + if (force || rfkill->state != oldstate) { rfkill_led_trigger(rfkill, rfkill->state); + notify_rfkill_state_change(rfkill); + } return retval; } @@ -139,12 +195,20 @@ EXPORT_SYMBOL(rfkill_switch_all); */ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { + enum rfkill_state oldstate; + if (state != RFKILL_STATE_OFF && state != RFKILL_STATE_ON) return -EINVAL; mutex_lock(&rfkill->mutex); + + oldstate = rfkill->state; rfkill->state = state; + + if (state != oldstate) + notify_rfkill_state_change(rfkill); + mutex_unlock(&rfkill->mutex); return 0; -- cgit v1.2.3 From 5005657cbd0fd6f277f807c0612a6b6d4396a02c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:46:42 -0300 Subject: rfkill: rename the rfkill_state states and add block-locked state The current naming of rfkill_state causes a lot of confusion: not only the "kill" in rfkill suggests negative logic, but also the fact that rfkill cannot turn anything on (it can just force something off or stop forcing something off) is often forgotten. Rename RFKILL_STATE_OFF to RFKILL_STATE_SOFT_BLOCKED (transmitter is blocked and will not operate; state can be changed by a toggle_radio request), and RFKILL_STATE_ON to RFKILL_STATE_UNBLOCKED (transmitter is not blocked, and may operate). Also, add a new third state, RFKILL_STATE_HARD_BLOCKED (transmitter is blocked and will not operate; state cannot be changed through a toggle_radio request), which is used by drivers to indicate a wireless transmiter was blocked by a hardware rfkill line that accepts no overrides. Keep the old names as #defines, but document them as deprecated. This way, drivers can be converted to the new names *and* verified to actually use rfkill correctly one by one. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- Documentation/rfkill.txt | 56 +++++++++++++++++++++++++++++------ include/linux/rfkill.h | 32 ++++++++++++++++++-- net/rfkill/rfkill-input.c | 29 +++++++++--------- net/rfkill/rfkill.c | 75 ++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 152 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index cf230c1ad9ef..5316cea95ce0 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -60,9 +60,20 @@ The second option provides an rfkill input handler. This handler will listen to all rfkill key events and will toggle the radio accordingly. With this option enabled userspace could either do nothing or simply perform monitoring tasks. -When a rfkill switch is in the RFKILL_STATE_ON, the wireless transmitter (radio -TX circuit for example) is *enabled*. When the rfkill switch is in the -RFKILL_STATE_OFF, the wireless transmitter is to be *blocked* from operating. +When a rfkill switch is in the RFKILL_STATE_UNBLOCKED, the wireless transmitter +(radio TX circuit for example) is *enabled*. When the rfkill switch is in the +RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the wireless +transmitter is to be *blocked* from operating. + +RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change +that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio() +will not be able to change the state and will return with a suitable error if +attempts are made to set the state to RFKILL_STATE_UNBLOCKED. + +RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is +locked in the BLOCKED state by a hardwire rfkill line (typically an input pin +that, when active, forces the transmitter to be disabled) which the driver +CANNOT override. Full rfkill functionality requires two different subsystems to cooperate: the input layer and the rfkill class. The input layer issues *commands* to the @@ -122,10 +133,10 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input): action). * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0 (power off all transmitters) in a special way: it ignores any - overrides and local state cache and forces all transmitters to - the OFF state (including those which are already supposed to be - OFF). Note that the opposite event (power on all transmitters) - is handled normally. + overrides and local state cache and forces all transmitters to the + RFKILL_STATE_SOFT_BLOCKED state (including those which are already + supposed to be BLOCKED). Note that the opposite event (power on all + transmitters) is handled normally. Userspace uevent handler or kernel platform-specific drivers hooked to the rfkill notifier chain: @@ -284,6 +295,19 @@ You should: YOU CAN ACCESS state DIRECTLY) - rfkill_register() +The only way to set a device to the RFKILL_STATE_HARD_BLOCKED state is through +a suitable return of get_state() or through rfkill_force_state(). + +When a device is in the RFKILL_STATE_HARD_BLOCKED state, the only way to switch +it to a different state is through a suitable return of get_state() or through +rfkill_force_state(). + +If toggle_radio() is called to set a device to state RFKILL_STATE_SOFT_BLOCKED +when that device is already at the RFKILL_STATE_HARD_BLOCKED state, it should +not return an error. Instead, it should try to double-block the transmitter, +so that its state will change from RFKILL_STATE_HARD_BLOCKED to +RFKILL_STATE_SOFT_BLOCKED should the hardware blocking cease. + Please refer to the source for more documentation. =============================================================================== @@ -322,13 +346,27 @@ change by writing to the "state" attribute in order for anything to happen. Take particular care to implement EV_SW SW_RFKILL_ALL properly. When that switch is set to OFF, *every* rfkill device *MUST* be immediately put into the -OFF state, no questions asked. +RFKILL_STATE_SOFT_BLOCKED state, no questions asked. The following sysfs entries will be created: name: Name assigned by driver to this key (interface or driver name). type: Name of the key type ("wlan", "bluetooth", etc). - state: Current state of the key. 1: On, 0: Off. + state: Current state of the transmitter + 0: RFKILL_STATE_SOFT_BLOCKED + transmitter is forced off, but you can override it + by a write to the state attribute, or through input + events (if rfkill-input is loaded). + 1: RFKILL_STATE_UNBLOCKED + transmiter is NOT forced off, and may operate if + all other conditions for such operation are met + (such as interface is up and configured, etc). + 2: RFKILL_STATE_HARD_BLOCKED + transmitter is forced off by something outside of + the driver's control. + + You cannot set a device to this state through + writes to the state attribute. claim: 1: Userspace handles events, 0: Kernel handles events Both the "state" and "claim" entries are also writable. For the "state" entry diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 98667becdee4..c5f6e54ec6ae 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -46,16 +46,25 @@ enum rfkill_type { }; enum rfkill_state { - RFKILL_STATE_OFF = 0, /* Radio output blocked */ - RFKILL_STATE_ON = 1, /* Radio output active */ + RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */ + RFKILL_STATE_UNBLOCKED = 1, /* Radio output allowed */ + RFKILL_STATE_HARD_BLOCKED = 2, /* Output blocked, non-overrideable */ }; +/* + * These are DEPRECATED, drivers using them should be verified to + * comply with the rfkill usage guidelines in Documentation/rfkill.txt + * and then converted to use the new names for rfkill_state + */ +#define RFKILL_STATE_OFF RFKILL_STATE_SOFT_BLOCKED +#define RFKILL_STATE_ON RFKILL_STATE_UNBLOCKED + /** * struct rfkill - rfkill control structure. * @name: Name of the switch. * @type: Radio type which the button controls, the value stored * here should be a value from enum rfkill_type. - * @state: State of the switch, "ON" means radio can operate. + * @state: State of the switch, "UNBLOCKED" means radio can operate. * @user_claim_unsupported: Whether the hardware supports exclusive * RF-kill control by userspace. Set this before registering. * @user_claim: Set when the switch is controlled exlusively by userspace. @@ -63,8 +72,12 @@ enum rfkill_state { * @data: Pointer to the RF button drivers private data which will be * passed along when toggling radio state. * @toggle_radio(): Mandatory handler to control state of the radio. + * only RFKILL_STATE_SOFT_BLOCKED and RFKILL_STATE_UNBLOCKED are + * valid parameters. * @get_state(): handler to read current radio state from hardware, * may be called from atomic context, should return 0 on success. + * Either this handler OR judicious use of rfkill_force_state() is + * MANDATORY for any driver capable of RFKILL_STATE_HARD_BLOCKED. * @led_trigger: A LED trigger for this button's LED. * @dev: Device structure integrating the switch into device tree. * @node: Used to place switch into list of all switches known to the @@ -102,6 +115,19 @@ void rfkill_unregister(struct rfkill *rfkill); int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); +/** + * rfkill_state_complement - return complementar state + * @state: state to return the complement of + * + * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED, + * returns RFKILL_STATE_UNBLOCKED otherwise. + */ +static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state) +{ + return (state == RFKILL_STATE_UNBLOCKED) ? + RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED; +} + /** * rfkill_get_led_name - Get the LED trigger name for the button's LED. * This function might return a NULL pointer if registering of the diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 5d4c8b2446f7..8aa822730145 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { - task->desired_state = !task->desired_state; + task->desired_state = + rfkill_state_complement(task->desired_state); task->last = jiffies; schedule_work(&task->work); } @@ -92,14 +93,14 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_unlock_irqrestore(&task->lock, flags); } -#define DEFINE_RFKILL_TASK(n, t) \ - struct rfkill_task n = { \ - .work = __WORK_INITIALIZER(n.work, \ - rfkill_task_handler), \ - .type = t, \ - .mutex = __MUTEX_INITIALIZER(n.mutex), \ - .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ - .desired_state = RFKILL_STATE_ON, \ +#define DEFINE_RFKILL_TASK(n, t) \ + struct rfkill_task n = { \ + .work = __WORK_INITIALIZER(n.work, \ + rfkill_task_handler), \ + .type = t, \ + .mutex = __MUTEX_INITIALIZER(n.mutex), \ + .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ + .desired_state = RFKILL_STATE_UNBLOCKED, \ } static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); @@ -135,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, /* handle EPO (emergency power off) through shortcut */ if (data) { rfkill_schedule_set(&rfkill_wwan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wimax, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_uwb, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_bt, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wlan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); } else rfkill_schedule_epo(); break; diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 7d07175c407f..ce0e23148cdd 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_mutex); -static unsigned int rfkill_default_state = RFKILL_STATE_ON; +static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; module_param_named(default_state, rfkill_default_state, uint, 0444); MODULE_PARM_DESC(default_state, "Default initial state for all radio types, 0 = radio off"); @@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill, if (!led->name) return; - if (state == RFKILL_STATE_OFF) + if (state != RFKILL_STATE_UNBLOCKED) led_trigger_event(led, LED_OFF); else led_trigger_event(led, LED_FULL); @@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill) } } +/** + * rfkill_toggle_radio - wrapper for toggle_radio hook + * calls toggle_radio taking into account a lot of "small" + * details. + * @rfkill: the rfkill struct to use + * @force: calls toggle_radio even if cache says it is not needed, + * and also makes sure notifications of the state will be + * sent even if it didn't change + * @state: the new state to call toggle_radio() with + * + * This wrappen protects and enforces the API for toggle_radio + * calls. Note that @force cannot override a (possibly cached) + * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of + * RFKILL_STATE_HARD_BLOCKED implements either get_state() or + * rfkill_force_state(), so the cache either is bypassed or valid. + * + * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED + * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to + * give the driver a hint that it should double-BLOCK the transmitter. + * + * Caller must have aquired rfkill_mutex. + */ static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state, int force) @@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, !rfkill->get_state(rfkill->data, &newstate)) rfkill->state = newstate; + switch (state) { + case RFKILL_STATE_HARD_BLOCKED: + /* typically happens when refreshing hardware state, + * such as on resume */ + state = RFKILL_STATE_SOFT_BLOCKED; + break; + case RFKILL_STATE_UNBLOCKED: + /* force can't override this, only rfkill_force_state() can */ + if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) + return -EPERM; + break; + case RFKILL_STATE_SOFT_BLOCKED: + /* nothing to do, we want to give drivers the hint to double + * BLOCK even a transmitter that is already in state + * RFKILL_STATE_HARD_BLOCKED */ + break; + } + if (force || state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) + /* never allow a HARD->SOFT downgrade! */ + if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) rfkill->state = state; } @@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all); /** * rfkill_epo - emergency power off all transmitters * - * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring + * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring * everything in its path but rfkill_mutex. */ void rfkill_epo(void) @@ -193,7 +234,7 @@ void rfkill_epo(void) mutex_lock(&rfkill_mutex); list_for_each_entry(rfkill, &rfkill_list, node) { - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); } mutex_unlock(&rfkill_mutex); } @@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { enum rfkill_state oldstate; - if (state != RFKILL_STATE_OFF && - state != RFKILL_STATE_ON) + if (state != RFKILL_STATE_SOFT_BLOCKED && + state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_HARD_BLOCKED) return -EINVAL; mutex_lock(&rfkill->mutex); @@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_SOFT_BLOCKED) + return -EINVAL; + if (mutex_lock_interruptible(&rfkill->mutex)) return -ERESTARTSYS; - error = rfkill_toggle_radio(rfkill, - state ? RFKILL_STATE_ON : RFKILL_STATE_OFF, - 0); + error = rfkill_toggle_radio(rfkill, state, 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) update_rfkill_state(rfkill); mutex_lock(&rfkill->mutex); - rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); + rfkill->toggle_radio(rfkill->data, + RFKILL_STATE_SOFT_BLOCKED); mutex_unlock(&rfkill->mutex); } @@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); mutex_unlock(&rfkill_mutex); } @@ -610,8 +656,9 @@ static int __init rfkill_init(void) int error; int i; - if (rfkill_default_state != RFKILL_STATE_OFF && - rfkill_default_state != RFKILL_STATE_ON) + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && + rfkill_default_state != RFKILL_STATE_UNBLOCKED) return -EINVAL; for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) -- cgit v1.2.3 From f2df38596a81b6c24f4586b0b4befeaebf3e02db Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Sun, 15 Jun 2008 18:23:29 +0300 Subject: mac80211: 11h Infrastructure - Parsing This patch introduces parsing of 11h and 11d related elements from incoming management frames. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 54 +++++++++++++++++++++++++++++++++++++++++++--- net/mac80211/ieee80211_i.h | 9 ++++++++ net/mac80211/mlme.c | 19 ++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 2998e3b5f166..8546f09e462c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -469,6 +469,40 @@ struct ieee80211s_hdr { u8 eaddr3[6]; } __attribute__ ((packed)); +/** + * struct ieee80211_quiet_ie + * + * This structure refers to "Quiet information element" + */ +struct ieee80211_quiet_ie { + u8 count; + u8 period; + __le16 duration; + __le16 offset; +} __attribute__ ((packed)); + +/** + * struct ieee80211_msrment_ie + * + * This structure refers to "Measurement Request/Report information element" + */ +struct ieee80211_msrment_ie { + u8 token; + u8 mode; + u8 type; + u8 request[0]; +} __attribute__ ((packed)); + +/** + * struct ieee80211_channel_sw_ie + * + * This structure refers to "Channel Switch Announcement information element" + */ +struct ieee80211_channel_sw_ie { + u8 mode; + u8 new_ch_num; + u8 count; +} __attribute__ ((packed)); struct ieee80211_mgmt { __le16 frame_control; @@ -544,10 +578,15 @@ struct ieee80211_mgmt { u8 action_code; u8 element_id; u8 length; - u8 switch_mode; - u8 new_chan; - u8 switch_count; + struct ieee80211_channel_sw_ie sw_elem; } __attribute__((packed)) chan_switch; + struct{ + u8 action_code; + u8 dialog_token; + u8 element_id; + u8 length; + struct ieee80211_msrment_ie msr_elem; + } __attribute__((packed)) measurement; struct{ u8 action_code; u8 dialog_token; @@ -875,6 +914,15 @@ enum ieee80211_category { WLAN_CATEGORY_WMM = 17, }; +/* SPECTRUM_MGMT action code */ +enum ieee80211_spectrum_mgmt_actioncode { + WLAN_ACTION_SPCT_MSR_REQ = 0, + WLAN_ACTION_SPCT_MSR_RPRT = 1, + WLAN_ACTION_SPCT_TPC_REQ = 2, + WLAN_ACTION_SPCT_TPC_RPRT = 3, + WLAN_ACTION_SPCT_CHL_SWITCH = 4, +}; + /* BACK action code */ enum ieee80211_back_actioncode { WLAN_ACTION_ADDBA_REQ = 0, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 80a9e7c07b47..af352c05c983 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -791,6 +791,10 @@ struct ieee802_11_elems { u8 *preq; u8 *prep; u8 *perr; + u8 *ch_switch_elem; + u8 *country_elem; + u8 *pwr_constr_elem; + u8 *quiet_elem; /* first quite element */ /* length of them, respectively */ u8 ssid_len; @@ -815,6 +819,11 @@ struct ieee802_11_elems { u8 preq_len; u8 prep_len; u8 perr_len; + u8 ch_switch_elem_len; + u8 country_elem_len; + u8 pwr_constr_elem_len; + u8 quiet_elem_len; + u8 num_of_quiet_elem; /* can be more the one */ }; static inline struct ieee80211_local *hw_to_local( diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e06d6450f215..32453561fe32 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -204,6 +204,25 @@ void ieee802_11_parse_elems(u8 *start, size_t len, elems->perr = pos; elems->perr_len = elen; break; + case WLAN_EID_CHANNEL_SWITCH: + elems->ch_switch_elem = pos; + elems->ch_switch_elem_len = elen; + break; + case WLAN_EID_QUIET: + if (!elems->quiet_elem) { + elems->quiet_elem = pos; + elems->quiet_elem_len = elen; + } + elems->num_of_quiet_elem++; + break; + case WLAN_EID_COUNTRY: + elems->country_elem = pos; + elems->country_elem_len = elen; + break; + case WLAN_EID_PWR_CONSTRAINT: + elems->pwr_constr_elem = pos; + elems->pwr_constr_elem_len = elen; + break; default: break; } -- cgit v1.2.3 From b662348662f9661f9259c7186c1bdb65620045f1 Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Mon, 16 Jun 2008 16:09:49 +0300 Subject: mac80211: 11h - Handling measurement request This patch handles the 11h measurement request information element. This is minimal requested implementation - refuse measurement. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 10 ++++++ net/mac80211/mlme.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 8546f09e462c..cffd6d0094f9 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -739,11 +739,21 @@ struct ieee80211_ht_addt_info { #define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) #define WLAN_CAPABILITY_PBCC (1<<6) #define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) + /* 802.11h */ #define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) #define WLAN_CAPABILITY_QOS (1<<9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) #define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +/* measurement */ +#define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) +#define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) +#define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2) + +#define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 + /* 802.11g ERP information element */ #define WLAN_ERP_NON_ERP_PRESENT (1<<0) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 32453561fe32..6d9a4facf9dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1720,6 +1720,71 @@ void ieee80211_sta_tear_down_BA_sessions(struct net_device *dev, u8 *addr) } } +static void ieee80211_send_refuse_measurement_request(struct net_device *dev, + struct ieee80211_msrment_ie *request_ie, + const u8 *da, const u8 *bssid, + u8 dialog_token) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *msr_report; + + skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + + sizeof(struct ieee80211_msrment_ie)); + + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer for " + "measurement report frame\n", dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); + memset(msr_report, 0, 24); + memcpy(msr_report->da, da, ETH_ALEN); + memcpy(msr_report->sa, dev->dev_addr, ETH_ALEN); + memcpy(msr_report->bssid, bssid, ETH_ALEN); + msr_report->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); + msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; + msr_report->u.action.u.measurement.action_code = + WLAN_ACTION_SPCT_MSR_RPRT; + msr_report->u.action.u.measurement.dialog_token = dialog_token; + + msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; + msr_report->u.action.u.measurement.length = + sizeof(struct ieee80211_msrment_ie); + + memset(&msr_report->u.action.u.measurement.msr_elem, 0, + sizeof(struct ieee80211_msrment_ie)); + msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; + msr_report->u.action.u.measurement.msr_elem.mode |= + IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; + msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; + + ieee80211_sta_tx(dev, skb, 0); +} + +static void ieee80211_sta_process_measurement_req(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + /* + * Ignoring measurement request is spec violation. + * Mandatory measurements must be reported optional + * measurements might be refused or reported incapable + * For now just refuse + * TODO: Answer basic measurement as unmeasured + */ + ieee80211_send_refuse_measurement_request(dev, + &mgmt->u.action.u.measurement.msr_elem, + mgmt->sa, mgmt->bssid, + mgmt->u.action.u.measurement.dialog_token); +} + + static void ieee80211_rx_mgmt_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, @@ -3044,11 +3109,24 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev, struct ieee80211_rx_status *rx_status) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (len < IEEE80211_MIN_ACTION_SIZE) return; switch (mgmt->u.action.category) { + case WLAN_CATEGORY_SPECTRUM_MGMT: + if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) + break; + switch (mgmt->u.action.u.chan_switch.action_code) { + case WLAN_ACTION_SPCT_MSR_REQ: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.measurement))) + break; + ieee80211_sta_process_measurement_req(dev, mgmt, len); + break; + } + break; case WLAN_CATEGORY_BACK: switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: -- cgit v1.2.3 From 1bdad606338debc6384b2844f1b53cc436b3ac90 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Tue, 3 Jun 2008 14:09:53 +0100 Subject: [GFS2] Remove remote lock dropping code There are several reasons why this is undesirable: 1. It never happens during normal operation anyway 2. If it does happen it causes performance to be very, very poor 3. It isn't likely to solve the original problem (memory shortage on remote DLM node) it was supposed to solve 4. It uses a bunch of arbitrary constants which are unlikely to be correct for any particular situation and for which the tuning seems to be a black art. 5. In an N node cluster, only 1/N of the dropped locked will actually contribute to solving the problem on average. So all in all we are better off without it. This also makes merging the lock_dlm module into GFS2 a bit easier. Signed-off-by: Steven Whitehouse --- fs/gfs2/gfs2.h | 5 ----- fs/gfs2/glock.c | 12 +++--------- fs/gfs2/glock.h | 2 +- fs/gfs2/locking/dlm/lock_dlm.h | 3 --- fs/gfs2/locking/dlm/mount.c | 3 --- fs/gfs2/locking/dlm/sysfs.c | 13 ------------- fs/gfs2/locking/dlm/thread.c | 19 ------------------- fs/gfs2/ops_fstype.c | 2 +- fs/gfs2/ops_super.c | 2 +- fs/gfs2/sys.c | 14 -------------- include/linux/lm_interface.h | 4 ---- 11 files changed, 6 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/fs/gfs2/gfs2.h b/fs/gfs2/gfs2.h index 3bb11c0f8b56..ef606e3a5cf4 100644 --- a/fs/gfs2/gfs2.h +++ b/fs/gfs2/gfs2.h @@ -15,11 +15,6 @@ enum { CREATE = 1, }; -enum { - NO_WAIT = 0, - WAIT = 1, -}; - enum { NO_FORCE = 0, FORCE = 1, diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index be7ed503f012..8d5450f3c3ef 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1316,11 +1316,6 @@ void gfs2_glock_cb(void *cb_data, unsigned int type, void *data) wake_up_process(sdp->sd_recoverd_process); return; - case LM_CB_DROPLOCKS: - gfs2_gl_hash_clear(sdp, NO_WAIT); - gfs2_quota_scan(sdp); - return; - default: gfs2_assert_warn(sdp, 0); return; @@ -1508,11 +1503,10 @@ static void clear_glock(struct gfs2_glock *gl) * @sdp: the filesystem * @wait: wait until it's all gone * - * Called when unmounting the filesystem, or when inter-node lock manager - * requests DROPLOCKS because it is running out of capacity. + * Called when unmounting the filesystem. */ -void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait) +void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) { unsigned long t; unsigned int x; @@ -1527,7 +1521,7 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait) cont = 1; } - if (!wait || !cont) + if (!cont) break; if (time_after_eq(jiffies, diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 7389f8ef0a31..971d92af70fc 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -132,7 +132,7 @@ void gfs2_lvb_unhold(struct gfs2_glock *gl); void gfs2_glock_cb(void *cb_data, unsigned int type, void *data); void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); void gfs2_reclaim_glock(struct gfs2_sbd *sdp); -void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait); +void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); int __init gfs2_glock_init(void); void gfs2_glock_exit(void); diff --git a/fs/gfs2/locking/dlm/lock_dlm.h b/fs/gfs2/locking/dlm/lock_dlm.h index ad944c64eab1..845a27fd303e 100644 --- a/fs/gfs2/locking/dlm/lock_dlm.h +++ b/fs/gfs2/locking/dlm/lock_dlm.h @@ -79,9 +79,6 @@ struct gdlm_ls { wait_queue_head_t wait_control; struct task_struct *thread; wait_queue_head_t thread_wait; - unsigned long drop_time; - int drop_locks_count; - int drop_locks_period; }; enum { diff --git a/fs/gfs2/locking/dlm/mount.c b/fs/gfs2/locking/dlm/mount.c index 0628520a445f..fa31c54c2e67 100644 --- a/fs/gfs2/locking/dlm/mount.c +++ b/fs/gfs2/locking/dlm/mount.c @@ -22,8 +22,6 @@ static struct gdlm_ls *init_gdlm(lm_callback_t cb, struct gfs2_sbd *sdp, if (!ls) return NULL; - ls->drop_locks_count = GDLM_DROP_COUNT; - ls->drop_locks_period = GDLM_DROP_PERIOD; ls->fscb = cb; ls->sdp = sdp; ls->fsflags = flags; @@ -33,7 +31,6 @@ static struct gdlm_ls *init_gdlm(lm_callback_t cb, struct gfs2_sbd *sdp, INIT_LIST_HEAD(&ls->all_locks); init_waitqueue_head(&ls->thread_wait); init_waitqueue_head(&ls->wait_control); - ls->drop_time = jiffies; ls->jid = -1; strncpy(buf, table_name, 256); diff --git a/fs/gfs2/locking/dlm/sysfs.c b/fs/gfs2/locking/dlm/sysfs.c index a4ff271df9ee..4ec571c3d8a9 100644 --- a/fs/gfs2/locking/dlm/sysfs.c +++ b/fs/gfs2/locking/dlm/sysfs.c @@ -114,17 +114,6 @@ static ssize_t recover_status_show(struct gdlm_ls *ls, char *buf) return sprintf(buf, "%d\n", ls->recover_jid_status); } -static ssize_t drop_count_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->drop_locks_count); -} - -static ssize_t drop_count_store(struct gdlm_ls *ls, const char *buf, size_t len) -{ - ls->drop_locks_count = simple_strtol(buf, NULL, 0); - return len; -} - struct gdlm_attr { struct attribute attr; ssize_t (*show)(struct gdlm_ls *, char *); @@ -144,7 +133,6 @@ GDLM_ATTR(first_done, 0444, first_done_show, NULL); GDLM_ATTR(recover, 0644, recover_show, recover_store); GDLM_ATTR(recover_done, 0444, recover_done_show, NULL); GDLM_ATTR(recover_status, 0444, recover_status_show, NULL); -GDLM_ATTR(drop_count, 0644, drop_count_show, drop_count_store); static struct attribute *gdlm_attrs[] = { &gdlm_attr_proto_name.attr, @@ -157,7 +145,6 @@ static struct attribute *gdlm_attrs[] = { &gdlm_attr_recover.attr, &gdlm_attr_recover_done.attr, &gdlm_attr_recover_status.attr, - &gdlm_attr_drop_count.attr, NULL, }; diff --git a/fs/gfs2/locking/dlm/thread.c b/fs/gfs2/locking/dlm/thread.c index f30350abd62f..38823efd698c 100644 --- a/fs/gfs2/locking/dlm/thread.c +++ b/fs/gfs2/locking/dlm/thread.c @@ -20,19 +20,6 @@ static inline int no_work(struct gdlm_ls *ls) return ret; } -static inline int check_drop(struct gdlm_ls *ls) -{ - if (!ls->drop_locks_count) - return 0; - - if (time_after(jiffies, ls->drop_time + ls->drop_locks_period * HZ)) { - ls->drop_time = jiffies; - if (ls->all_locks_count >= ls->drop_locks_count) - return 1; - } - return 0; -} - static int gdlm_thread(void *data) { struct gdlm_ls *ls = (struct gdlm_ls *) data; @@ -52,12 +39,6 @@ static int gdlm_thread(void *data) gdlm_do_lock(lp); spin_lock(&ls->async_lock); } - /* Does this ever happen these days? I hope not anyway */ - if (check_drop(ls)) { - spin_unlock(&ls->async_lock); - ls->fscb(ls->sdp, LM_CB_DROPLOCKS, NULL); - spin_lock(&ls->async_lock); - } spin_unlock(&ls->async_lock); } diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 9bd97c5543bd..6ba69dd1a729 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -874,7 +874,7 @@ fail_sb: fail_locking: init_locking(sdp, &mount_gh, UNDO); fail_lm: - gfs2_gl_hash_clear(sdp, WAIT); + gfs2_gl_hash_clear(sdp); gfs2_lm_unmount(sdp); while (invalidate_inodes(sb)) yield(); diff --git a/fs/gfs2/ops_super.c b/fs/gfs2/ops_super.c index 66907922109f..f66ea0f7a356 100644 --- a/fs/gfs2/ops_super.c +++ b/fs/gfs2/ops_super.c @@ -126,7 +126,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_clear_rgrpd(sdp); gfs2_jindex_free(sdp); /* Take apart glock structures and buffer lists */ - gfs2_gl_hash_clear(sdp, WAIT); + gfs2_gl_hash_clear(sdp); /* Unmount the locking protocol */ gfs2_lm_unmount(sdp); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 9ab9fc85ecd0..6f7e2e5858e0 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -110,18 +110,6 @@ static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf, return len; } -static ssize_t shrink_store(struct gfs2_sbd *sdp, const char *buf, size_t len) -{ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (simple_strtol(buf, NULL, 0) != 1) - return -EINVAL; - - gfs2_gl_hash_clear(sdp, NO_WAIT); - return len; -} - static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { @@ -175,7 +163,6 @@ static struct gfs2_attr gfs2_attr_##name = __ATTR(name, mode, show, store) GFS2_ATTR(id, 0444, id_show, NULL); GFS2_ATTR(fsname, 0444, fsname_show, NULL); GFS2_ATTR(freeze, 0644, freeze_show, freeze_store); -GFS2_ATTR(shrink, 0200, NULL, shrink_store); GFS2_ATTR(withdraw, 0644, withdraw_show, withdraw_store); GFS2_ATTR(statfs_sync, 0200, NULL, statfs_sync_store); GFS2_ATTR(quota_sync, 0200, NULL, quota_sync_store); @@ -186,7 +173,6 @@ static struct attribute *gfs2_attrs[] = { &gfs2_attr_id.attr, &gfs2_attr_fsname.attr, &gfs2_attr_freeze.attr, - &gfs2_attr_shrink.attr, &gfs2_attr_withdraw.attr, &gfs2_attr_statfs_sync.attr, &gfs2_attr_quota_sync.attr, diff --git a/include/linux/lm_interface.h b/include/linux/lm_interface.h index f274997bc283..d0a7112b9719 100644 --- a/include/linux/lm_interface.h +++ b/include/linux/lm_interface.h @@ -138,9 +138,6 @@ typedef void (*lm_callback_t) (void *ptr, unsigned int type, void *data); * LM_CB_NEED_RECOVERY * The given journal needs to be recovered. * - * LM_CB_DROPLOCKS - * Reduce the number of cached locks. - * * LM_CB_ASYNC * The given lock has been granted. */ @@ -149,7 +146,6 @@ typedef void (*lm_callback_t) (void *ptr, unsigned int type, void *data); #define LM_CB_NEED_D 258 #define LM_CB_NEED_S 259 #define LM_CB_NEED_RECOVERY 260 -#define LM_CB_DROPLOCKS 261 #define LM_CB_ASYNC 262 /* -- cgit v1.2.3 From b2cad26cfc2091050574a460b304ed103a35dbda Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Tue, 3 Jun 2008 14:34:14 +0100 Subject: [GFS2] Remove obsolete conversion deadlock avoidance code This is only used by GFS1 so can be removed. Signed-off-by: Steven Whitehouse --- fs/gfs2/locking/dlm/lock.c | 23 +---------------------- include/linux/lm_interface.h | 2 -- 2 files changed, 1 insertion(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/gfs2/locking/dlm/lock.c b/fs/gfs2/locking/dlm/lock.c index 871ffc9578f2..894df4567a03 100644 --- a/fs/gfs2/locking/dlm/lock.c +++ b/fs/gfs2/locking/dlm/lock.c @@ -80,7 +80,6 @@ static void process_complete(struct gdlm_lock *lp) { struct gdlm_ls *ls = lp->ls; struct lm_async_cb acb; - s16 prev_mode = lp->cur; memset(&acb, 0, sizeof(acb)); @@ -160,15 +159,7 @@ static void process_complete(struct gdlm_lock *lp) lp->lksb.sb_status, lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number, lp->flags); - if (lp->lksb.sb_status == -EDEADLOCK && - lp->ls->fsflags & LM_MFLAG_CONV_NODROP) { - lp->req = lp->cur; - acb.lc_ret |= LM_OUT_CONV_DEADLK; - if (lp->cur == DLM_LOCK_IV) - lp->lksb.sb_lkid = 0; - goto out; - } else - return; + return; } /* @@ -268,10 +259,6 @@ out: acb.lc_name = lp->lockname; acb.lc_ret |= gdlm_make_lmstate(lp->cur); - if (!test_and_clear_bit(LFL_NOCACHE, &lp->flags) && - (lp->cur > DLM_LOCK_NL) && (prev_mode > DLM_LOCK_NL)) - acb.lc_ret |= LM_OUT_CACHEABLE; - ls->fscb(ls->sdp, LM_CB_ASYNC, &acb); } @@ -376,14 +363,6 @@ static inline unsigned int make_flags(struct gdlm_lock *lp, if (lp->lksb.sb_lkid != 0) { lkf |= DLM_LKF_CONVERT; - - /* Conversion deadlock avoidance by DLM */ - - if (!(lp->ls->fsflags & LM_MFLAG_CONV_NODROP) && - !test_bit(LFL_FORCE_PROMOTE, &lp->flags) && - !(lkf & DLM_LKF_NOQUEUE) && - cur > DLM_LOCK_NL && req > DLM_LOCK_NL && cur != req) - lkf |= DLM_LKF_CONVDEADLK; } if (lp->lvb) diff --git a/include/linux/lm_interface.h b/include/linux/lm_interface.h index d0a7112b9719..2ed8fa1b762b 100644 --- a/include/linux/lm_interface.h +++ b/include/linux/lm_interface.h @@ -122,11 +122,9 @@ typedef void (*lm_callback_t) (void *ptr, unsigned int type, void *data); */ #define LM_OUT_ST_MASK 0x00000003 -#define LM_OUT_CACHEABLE 0x00000004 #define LM_OUT_CANCELED 0x00000008 #define LM_OUT_ASYNC 0x00000080 #define LM_OUT_ERROR 0x00000100 -#define LM_OUT_CONV_DEADLK 0x00000200 /* * lm_callback_t types -- cgit v1.2.3 From c09595f63bb1909c5dc4dca288f4fe818561b5f3 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 27 Jun 2008 13:41:14 +0200 Subject: sched: revert revert of: fair-group: SMP-nice for group scheduling Try again.. Initial commit: 18d95a2832c1392a2d63227a7a6d433cb9f2037e Revert: 6363ca57c76b7b83639ca8c83fc285fa26a7880e Signed-off-by: Peter Zijlstra Cc: Srivatsa Vaddagiri Cc: Mike Galbraith Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 + kernel/sched.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++---- kernel/sched_debug.c | 5 + kernel/sched_fair.c | 124 +++++++++------ kernel/sched_rt.c | 4 + 5 files changed, 489 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index eaf821072dbd..97a58b622ee1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -765,6 +765,7 @@ struct sched_domain { struct sched_domain *child; /* bottom domain must be null terminated */ struct sched_group *groups; /* the balancing groups of the domain */ cpumask_t span; /* span of all CPUs in this domain */ + int first_cpu; /* cache of the first cpu in this domain */ unsigned long min_interval; /* Minimum balance interval ms */ unsigned long max_interval; /* Maximum balance interval ms */ unsigned int busy_factor; /* less balancing by factor if busy */ diff --git a/kernel/sched.c b/kernel/sched.c index f653af684fb3..874b6da15430 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -403,6 +403,43 @@ struct cfs_rq { */ struct list_head leaf_cfs_rq_list; struct task_group *tg; /* group that "owns" this runqueue */ + +#ifdef CONFIG_SMP + unsigned long task_weight; + unsigned long shares; + /* + * We need space to build a sched_domain wide view of the full task + * group tree, in order to avoid depending on dynamic memory allocation + * during the load balancing we place this in the per cpu task group + * hierarchy. This limits the load balancing to one instance per cpu, + * but more should not be needed anyway. + */ + struct aggregate_struct { + /* + * load = weight(cpus) * f(tg) + * + * Where f(tg) is the recursive weight fraction assigned to + * this group. + */ + unsigned long load; + + /* + * part of the group weight distributed to this span. + */ + unsigned long shares; + + /* + * The sum of all runqueue weights within this span. + */ + unsigned long rq_weight; + + /* + * Weight contributed by tasks; this is the part we can + * influence by moving tasks around. + */ + unsigned long task_weight; + } aggregate; +#endif #endif }; @@ -1484,6 +1521,326 @@ static unsigned long source_load(int cpu, int type); static unsigned long target_load(int cpu, int type); static unsigned long cpu_avg_load_per_task(int cpu); static int task_hot(struct task_struct *p, u64 now, struct sched_domain *sd); + +#ifdef CONFIG_FAIR_GROUP_SCHED + +/* + * Group load balancing. + * + * We calculate a few balance domain wide aggregate numbers; load and weight. + * Given the pictures below, and assuming each item has equal weight: + * + * root 1 - thread + * / | \ A - group + * A 1 B + * /|\ / \ + * C 2 D 3 4 + * | | + * 5 6 + * + * load: + * A and B get 1/3-rd of the total load. C and D get 1/3-rd of A's 1/3-rd, + * which equals 1/9-th of the total load. + * + * shares: + * The weight of this group on the selected cpus. + * + * rq_weight: + * Direct sum of all the cpu's their rq weight, e.g. A would get 3 while + * B would get 2. + * + * task_weight: + * Part of the rq_weight contributed by tasks; all groups except B would + * get 1, B gets 2. + */ + +static inline struct aggregate_struct * +aggregate(struct task_group *tg, struct sched_domain *sd) +{ + return &tg->cfs_rq[sd->first_cpu]->aggregate; +} + +typedef void (*aggregate_func)(struct task_group *, struct sched_domain *); + +/* + * Iterate the full tree, calling @down when first entering a node and @up when + * leaving it for the final time. + */ +static +void aggregate_walk_tree(aggregate_func down, aggregate_func up, + struct sched_domain *sd) +{ + struct task_group *parent, *child; + + rcu_read_lock(); + parent = &root_task_group; +down: + (*down)(parent, sd); + list_for_each_entry_rcu(child, &parent->children, siblings) { + parent = child; + goto down; + +up: + continue; + } + (*up)(parent, sd); + + child = parent; + parent = parent->parent; + if (parent) + goto up; + rcu_read_unlock(); +} + +/* + * Calculate the aggregate runqueue weight. + */ +static +void aggregate_group_weight(struct task_group *tg, struct sched_domain *sd) +{ + unsigned long rq_weight = 0; + unsigned long task_weight = 0; + int i; + + for_each_cpu_mask(i, sd->span) { + rq_weight += tg->cfs_rq[i]->load.weight; + task_weight += tg->cfs_rq[i]->task_weight; + } + + aggregate(tg, sd)->rq_weight = rq_weight; + aggregate(tg, sd)->task_weight = task_weight; +} + +/* + * Compute the weight of this group on the given cpus. + */ +static +void aggregate_group_shares(struct task_group *tg, struct sched_domain *sd) +{ + unsigned long shares = 0; + int i; + + for_each_cpu_mask(i, sd->span) + shares += tg->cfs_rq[i]->shares; + + if ((!shares && aggregate(tg, sd)->rq_weight) || shares > tg->shares) + shares = tg->shares; + + aggregate(tg, sd)->shares = shares; +} + +/* + * Compute the load fraction assigned to this group, relies on the aggregate + * weight and this group's parent's load, i.e. top-down. + */ +static +void aggregate_group_load(struct task_group *tg, struct sched_domain *sd) +{ + unsigned long load; + + if (!tg->parent) { + int i; + + load = 0; + for_each_cpu_mask(i, sd->span) + load += cpu_rq(i)->load.weight; + + } else { + load = aggregate(tg->parent, sd)->load; + + /* + * shares is our weight in the parent's rq so + * shares/parent->rq_weight gives our fraction of the load + */ + load *= aggregate(tg, sd)->shares; + load /= aggregate(tg->parent, sd)->rq_weight + 1; + } + + aggregate(tg, sd)->load = load; +} + +static void __set_se_shares(struct sched_entity *se, unsigned long shares); + +/* + * Calculate and set the cpu's group shares. + */ +static void +__update_group_shares_cpu(struct task_group *tg, struct sched_domain *sd, + int tcpu) +{ + int boost = 0; + unsigned long shares; + unsigned long rq_weight; + + if (!tg->se[tcpu]) + return; + + rq_weight = tg->cfs_rq[tcpu]->load.weight; + + /* + * If there are currently no tasks on the cpu pretend there is one of + * average load so that when a new task gets to run here it will not + * get delayed by group starvation. + */ + if (!rq_weight) { + boost = 1; + rq_weight = NICE_0_LOAD; + } + + /* + * \Sum shares * rq_weight + * shares = ----------------------- + * \Sum rq_weight + * + */ + shares = aggregate(tg, sd)->shares * rq_weight; + shares /= aggregate(tg, sd)->rq_weight + 1; + + /* + * record the actual number of shares, not the boosted amount. + */ + tg->cfs_rq[tcpu]->shares = boost ? 0 : shares; + + if (shares < MIN_SHARES) + shares = MIN_SHARES; + else if (shares > MAX_SHARES) + shares = MAX_SHARES; + + __set_se_shares(tg->se[tcpu], shares); +} + +/* + * Re-adjust the weights on the cpu the task came from and on the cpu the + * task went to. + */ +static void +__move_group_shares(struct task_group *tg, struct sched_domain *sd, + int scpu, int dcpu) +{ + unsigned long shares; + + shares = tg->cfs_rq[scpu]->shares + tg->cfs_rq[dcpu]->shares; + + __update_group_shares_cpu(tg, sd, scpu); + __update_group_shares_cpu(tg, sd, dcpu); + + /* + * ensure we never loose shares due to rounding errors in the + * above redistribution. + */ + shares -= tg->cfs_rq[scpu]->shares + tg->cfs_rq[dcpu]->shares; + if (shares) + tg->cfs_rq[dcpu]->shares += shares; +} + +/* + * Because changing a group's shares changes the weight of the super-group + * we need to walk up the tree and change all shares until we hit the root. + */ +static void +move_group_shares(struct task_group *tg, struct sched_domain *sd, + int scpu, int dcpu) +{ + while (tg) { + __move_group_shares(tg, sd, scpu, dcpu); + tg = tg->parent; + } +} + +static +void aggregate_group_set_shares(struct task_group *tg, struct sched_domain *sd) +{ + unsigned long shares = aggregate(tg, sd)->shares; + int i; + + for_each_cpu_mask(i, sd->span) { + struct rq *rq = cpu_rq(i); + unsigned long flags; + + spin_lock_irqsave(&rq->lock, flags); + __update_group_shares_cpu(tg, sd, i); + spin_unlock_irqrestore(&rq->lock, flags); + } + + aggregate_group_shares(tg, sd); + + /* + * ensure we never loose shares due to rounding errors in the + * above redistribution. + */ + shares -= aggregate(tg, sd)->shares; + if (shares) { + tg->cfs_rq[sd->first_cpu]->shares += shares; + aggregate(tg, sd)->shares += shares; + } +} + +/* + * Calculate the accumulative weight and recursive load of each task group + * while walking down the tree. + */ +static +void aggregate_get_down(struct task_group *tg, struct sched_domain *sd) +{ + aggregate_group_weight(tg, sd); + aggregate_group_shares(tg, sd); + aggregate_group_load(tg, sd); +} + +/* + * Rebalance the cpu shares while walking back up the tree. + */ +static +void aggregate_get_up(struct task_group *tg, struct sched_domain *sd) +{ + aggregate_group_set_shares(tg, sd); +} + +static DEFINE_PER_CPU(spinlock_t, aggregate_lock); + +static void __init init_aggregate(void) +{ + int i; + + for_each_possible_cpu(i) + spin_lock_init(&per_cpu(aggregate_lock, i)); +} + +static int get_aggregate(struct sched_domain *sd) +{ + if (!spin_trylock(&per_cpu(aggregate_lock, sd->first_cpu))) + return 0; + + aggregate_walk_tree(aggregate_get_down, aggregate_get_up, sd); + return 1; +} + +static void put_aggregate(struct sched_domain *sd) +{ + spin_unlock(&per_cpu(aggregate_lock, sd->first_cpu)); +} + +static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares) +{ + cfs_rq->shares = shares; +} + +#else + +static inline void init_aggregate(void) +{ +} + +static inline int get_aggregate(struct sched_domain *sd) +{ + return 0; +} + +static inline void put_aggregate(struct sched_domain *sd) +{ +} +#endif + #endif #include "sched_stats.h" @@ -1498,26 +1855,14 @@ static int task_hot(struct task_struct *p, u64 now, struct sched_domain *sd); #define for_each_class(class) \ for (class = sched_class_highest; class; class = class->next) -static inline void inc_load(struct rq *rq, const struct task_struct *p) -{ - update_load_add(&rq->load, p->se.load.weight); -} - -static inline void dec_load(struct rq *rq, const struct task_struct *p) -{ - update_load_sub(&rq->load, p->se.load.weight); -} - -static void inc_nr_running(struct task_struct *p, struct rq *rq) +static void inc_nr_running(struct rq *rq) { rq->nr_running++; - inc_load(rq, p); } -static void dec_nr_running(struct task_struct *p, struct rq *rq) +static void dec_nr_running(struct rq *rq) { rq->nr_running--; - dec_load(rq, p); } static void set_load_weight(struct task_struct *p) @@ -1609,7 +1954,7 @@ static void activate_task(struct rq *rq, struct task_struct *p, int wakeup) rq->nr_uninterruptible--; enqueue_task(rq, p, wakeup); - inc_nr_running(p, rq); + inc_nr_running(rq); } /* @@ -1621,7 +1966,7 @@ static void deactivate_task(struct rq *rq, struct task_struct *p, int sleep) rq->nr_uninterruptible++; dequeue_task(rq, p, sleep); - dec_nr_running(p, rq); + dec_nr_running(rq); } /** @@ -2274,7 +2619,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) * management (if any): */ p->sched_class->task_new(rq, p); - inc_nr_running(p, rq); + inc_nr_running(rq); } check_preempt_curr(rq, p); #ifdef CONFIG_SMP @@ -3265,9 +3610,12 @@ static int load_balance(int this_cpu, struct rq *this_rq, unsigned long imbalance; struct rq *busiest; unsigned long flags; + int unlock_aggregate; cpus_setall(*cpus); + unlock_aggregate = get_aggregate(sd); + /* * When power savings policy is enabled for the parent domain, idle * sibling can pick up load irrespective of busy siblings. In this case, @@ -3383,8 +3731,9 @@ redo: if (!ld_moved && !sd_idle && sd->flags & SD_SHARE_CPUPOWER && !test_sd_parent(sd, SD_POWERSAVINGS_BALANCE)) - return -1; - return ld_moved; + ld_moved = -1; + + goto out; out_balanced: schedstat_inc(sd, lb_balanced[idle]); @@ -3399,8 +3748,13 @@ out_one_pinned: if (!sd_idle && sd->flags & SD_SHARE_CPUPOWER && !test_sd_parent(sd, SD_POWERSAVINGS_BALANCE)) - return -1; - return 0; + ld_moved = -1; + else + ld_moved = 0; +out: + if (unlock_aggregate) + put_aggregate(sd); + return ld_moved; } /* @@ -4588,10 +4942,8 @@ void set_user_nice(struct task_struct *p, long nice) goto out_unlock; } on_rq = p->se.on_rq; - if (on_rq) { + if (on_rq) dequeue_task(rq, p, 0); - dec_load(rq, p); - } p->static_prio = NICE_TO_PRIO(nice); set_load_weight(p); @@ -4601,7 +4953,6 @@ void set_user_nice(struct task_struct *p, long nice) if (on_rq) { enqueue_task(rq, p, 0); - inc_load(rq, p); /* * If the task increased its priority or is running and * lowered its priority, then reschedule its CPU: @@ -7016,6 +7367,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, ALLNODES); set_domain_attribute(sd, attr); sd->span = *cpu_map; + sd->first_cpu = first_cpu(sd->span); cpu_to_allnodes_group(i, cpu_map, &sd->groups, tmpmask); p = sd; sd_allnodes = 1; @@ -7026,6 +7378,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, NODE); set_domain_attribute(sd, attr); sched_domain_node_span(cpu_to_node(i), &sd->span); + sd->first_cpu = first_cpu(sd->span); sd->parent = p; if (p) p->child = sd; @@ -7037,6 +7390,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, CPU); set_domain_attribute(sd, attr); sd->span = *nodemask; + sd->first_cpu = first_cpu(sd->span); sd->parent = p; if (p) p->child = sd; @@ -7048,6 +7402,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, MC); set_domain_attribute(sd, attr); sd->span = cpu_coregroup_map(i); + sd->first_cpu = first_cpu(sd->span); cpus_and(sd->span, sd->span, *cpu_map); sd->parent = p; p->child = sd; @@ -7060,6 +7415,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, SIBLING); set_domain_attribute(sd, attr); sd->span = per_cpu(cpu_sibling_map, i); + sd->first_cpu = first_cpu(sd->span); cpus_and(sd->span, sd->span, *cpu_map); sd->parent = p; p->child = sd; @@ -7757,6 +8113,7 @@ void __init sched_init(void) } #ifdef CONFIG_SMP + init_aggregate(); init_defrootdomain(); #endif @@ -8322,14 +8679,11 @@ void sched_move_task(struct task_struct *tsk) #endif /* CONFIG_GROUP_SCHED */ #ifdef CONFIG_FAIR_GROUP_SCHED -static void set_se_shares(struct sched_entity *se, unsigned long shares) +static void __set_se_shares(struct sched_entity *se, unsigned long shares) { struct cfs_rq *cfs_rq = se->cfs_rq; - struct rq *rq = cfs_rq->rq; int on_rq; - spin_lock_irq(&rq->lock); - on_rq = se->on_rq; if (on_rq) dequeue_entity(cfs_rq, se, 0); @@ -8339,8 +8693,17 @@ static void set_se_shares(struct sched_entity *se, unsigned long shares) if (on_rq) enqueue_entity(cfs_rq, se, 0); +} - spin_unlock_irq(&rq->lock); +static void set_se_shares(struct sched_entity *se, unsigned long shares) +{ + struct cfs_rq *cfs_rq = se->cfs_rq; + struct rq *rq = cfs_rq->rq; + unsigned long flags; + + spin_lock_irqsave(&rq->lock, flags); + __set_se_shares(se, shares); + spin_unlock_irqrestore(&rq->lock, flags); } static DEFINE_MUTEX(shares_mutex); @@ -8379,8 +8742,13 @@ int sched_group_set_shares(struct task_group *tg, unsigned long shares) * w/o tripping rebalance_share or load_balance_fair. */ tg->shares = shares; - for_each_possible_cpu(i) + for_each_possible_cpu(i) { + /* + * force a rebalance + */ + cfs_rq_set_shares(tg->cfs_rq[i], 0); set_se_shares(tg->se[i], shares); + } /* * Enable load balance activity on this group, by inserting it back on diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c index 8e077b9c91cb..04394ccac88d 100644 --- a/kernel/sched_debug.c +++ b/kernel/sched_debug.c @@ -167,6 +167,11 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) #endif SEQ_printf(m, " .%-30s: %ld\n", "nr_spread_over", cfs_rq->nr_spread_over); +#ifdef CONFIG_FAIR_GROUP_SCHED +#ifdef CONFIG_SMP + SEQ_printf(m, " .%-30s: %lu\n", "shares", cfs_rq->shares); +#endif +#endif } void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 2e197b8e43f1..183388c4dead 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -567,10 +567,27 @@ update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se) * Scheduling class queueing methods: */ +#if defined CONFIG_SMP && defined CONFIG_FAIR_GROUP_SCHED +static void +add_cfs_task_weight(struct cfs_rq *cfs_rq, unsigned long weight) +{ + cfs_rq->task_weight += weight; +} +#else +static inline void +add_cfs_task_weight(struct cfs_rq *cfs_rq, unsigned long weight) +{ +} +#endif + static void account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se) { update_load_add(&cfs_rq->load, se->load.weight); + if (!parent_entity(se)) + inc_cpu_load(rq_of(cfs_rq), se->load.weight); + if (entity_is_task(se)) + add_cfs_task_weight(cfs_rq, se->load.weight); cfs_rq->nr_running++; se->on_rq = 1; list_add(&se->group_node, &cfs_rq->tasks); @@ -580,6 +597,10 @@ static void account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) { update_load_sub(&cfs_rq->load, se->load.weight); + if (!parent_entity(se)) + dec_cpu_load(rq_of(cfs_rq), se->load.weight); + if (entity_is_task(se)) + add_cfs_task_weight(cfs_rq, -se->load.weight); cfs_rq->nr_running--; se->on_rq = 0; list_del_init(&se->group_node); @@ -1372,75 +1393,90 @@ static struct task_struct *load_balance_next_fair(void *arg) return __load_balance_iterator(cfs_rq, cfs_rq->balance_iterator); } -#ifdef CONFIG_FAIR_GROUP_SCHED -static int cfs_rq_best_prio(struct cfs_rq *cfs_rq) +static unsigned long +__load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest, + unsigned long max_load_move, struct sched_domain *sd, + enum cpu_idle_type idle, int *all_pinned, int *this_best_prio, + struct cfs_rq *cfs_rq) { - struct sched_entity *curr; - struct task_struct *p; - - if (!cfs_rq->nr_running || !first_fair(cfs_rq)) - return MAX_PRIO; - - curr = cfs_rq->curr; - if (!curr) - curr = __pick_next_entity(cfs_rq); + struct rq_iterator cfs_rq_iterator; - p = task_of(curr); + cfs_rq_iterator.start = load_balance_start_fair; + cfs_rq_iterator.next = load_balance_next_fair; + cfs_rq_iterator.arg = cfs_rq; - return p->prio; + return balance_tasks(this_rq, this_cpu, busiest, + max_load_move, sd, idle, all_pinned, + this_best_prio, &cfs_rq_iterator); } -#endif +#ifdef CONFIG_FAIR_GROUP_SCHED static unsigned long load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest, unsigned long max_load_move, struct sched_domain *sd, enum cpu_idle_type idle, int *all_pinned, int *this_best_prio) { - struct cfs_rq *busy_cfs_rq; long rem_load_move = max_load_move; - struct rq_iterator cfs_rq_iterator; - - cfs_rq_iterator.start = load_balance_start_fair; - cfs_rq_iterator.next = load_balance_next_fair; + int busiest_cpu = cpu_of(busiest); + struct task_group *tg; - for_each_leaf_cfs_rq(busiest, busy_cfs_rq) { -#ifdef CONFIG_FAIR_GROUP_SCHED - struct cfs_rq *this_cfs_rq; + rcu_read_lock(); + list_for_each_entry(tg, &task_groups, list) { long imbalance; - unsigned long maxload; + unsigned long this_weight, busiest_weight; + long rem_load, max_load, moved_load; + + /* + * empty group + */ + if (!aggregate(tg, sd)->task_weight) + continue; + + rem_load = rem_load_move * aggregate(tg, sd)->rq_weight; + rem_load /= aggregate(tg, sd)->load + 1; + + this_weight = tg->cfs_rq[this_cpu]->task_weight; + busiest_weight = tg->cfs_rq[busiest_cpu]->task_weight; - this_cfs_rq = cpu_cfs_rq(busy_cfs_rq, this_cpu); + imbalance = (busiest_weight - this_weight) / 2; - imbalance = busy_cfs_rq->load.weight - this_cfs_rq->load.weight; - /* Don't pull if this_cfs_rq has more load than busy_cfs_rq */ - if (imbalance <= 0) + if (imbalance < 0) + imbalance = busiest_weight; + + max_load = max(rem_load, imbalance); + moved_load = __load_balance_fair(this_rq, this_cpu, busiest, + max_load, sd, idle, all_pinned, this_best_prio, + tg->cfs_rq[busiest_cpu]); + + if (!moved_load) continue; - /* Don't pull more than imbalance/2 */ - imbalance /= 2; - maxload = min(rem_load_move, imbalance); + move_group_shares(tg, sd, busiest_cpu, this_cpu); - *this_best_prio = cfs_rq_best_prio(this_cfs_rq); -#else -# define maxload rem_load_move -#endif - /* - * pass busy_cfs_rq argument into - * load_balance_[start|next]_fair iterators - */ - cfs_rq_iterator.arg = busy_cfs_rq; - rem_load_move -= balance_tasks(this_rq, this_cpu, busiest, - maxload, sd, idle, all_pinned, - this_best_prio, - &cfs_rq_iterator); + moved_load *= aggregate(tg, sd)->load; + moved_load /= aggregate(tg, sd)->rq_weight + 1; - if (rem_load_move <= 0) + rem_load_move -= moved_load; + if (rem_load_move < 0) break; } + rcu_read_unlock(); return max_load_move - rem_load_move; } +#else +static unsigned long +load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest, + unsigned long max_load_move, + struct sched_domain *sd, enum cpu_idle_type idle, + int *all_pinned, int *this_best_prio) +{ + return __load_balance_fair(this_rq, this_cpu, busiest, + max_load_move, sd, idle, all_pinned, + this_best_prio, &busiest->cfs); +} +#endif static int move_one_task_fair(struct rq *this_rq, int this_cpu, struct rq *busiest, diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index 6b4a6b5a4167..765932d0399d 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -670,6 +670,8 @@ static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int wakeup) rt_se->timeout = 0; enqueue_rt_entity(rt_se); + + inc_cpu_load(rq, p->se.load.weight); } static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int sleep) @@ -678,6 +680,8 @@ static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int sleep) update_curr_rt(rq); dequeue_rt_entity(rt_se); + + dec_cpu_load(rq, p->se.load.weight); } /* -- cgit v1.2.3 From b6a86c746f5b708012809958462234d19e9c8177 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 27 Jun 2008 13:41:18 +0200 Subject: sched: fix sched_domain aggregation Keeping the aggregate on the first cpu of the sched domain has two problems: - it could collide between different sched domains on different cpus - it could slow things down because of the remote accesses Signed-off-by: Peter Zijlstra Cc: Srivatsa Vaddagiri Cc: Mike Galbraith Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 - kernel/sched.c | 113 ++++++++++++++++++++++++-------------------------- kernel/sched_fair.c | 12 +++--- 3 files changed, 60 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 97a58b622ee1..eaf821072dbd 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -765,7 +765,6 @@ struct sched_domain { struct sched_domain *child; /* bottom domain must be null terminated */ struct sched_group *groups; /* the balancing groups of the domain */ cpumask_t span; /* span of all CPUs in this domain */ - int first_cpu; /* cache of the first cpu in this domain */ unsigned long min_interval; /* Minimum balance interval ms */ unsigned long max_interval; /* Maximum balance interval ms */ unsigned int busy_factor; /* less balancing by factor if busy */ diff --git a/kernel/sched.c b/kernel/sched.c index 7d282c52bd42..160d3c209b8f 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1480,12 +1480,12 @@ static int task_hot(struct task_struct *p, u64 now, struct sched_domain *sd); */ static inline struct aggregate_struct * -aggregate(struct task_group *tg, struct sched_domain *sd) +aggregate(struct task_group *tg, int cpu) { - return &tg->cfs_rq[sd->first_cpu]->aggregate; + return &tg->cfs_rq[cpu]->aggregate; } -typedef void (*aggregate_func)(struct task_group *, struct sched_domain *); +typedef void (*aggregate_func)(struct task_group *, int, struct sched_domain *); /* * Iterate the full tree, calling @down when first entering a node and @up when @@ -1493,14 +1493,14 @@ typedef void (*aggregate_func)(struct task_group *, struct sched_domain *); */ static void aggregate_walk_tree(aggregate_func down, aggregate_func up, - struct sched_domain *sd) + int cpu, struct sched_domain *sd) { struct task_group *parent, *child; rcu_read_lock(); parent = &root_task_group; down: - (*down)(parent, sd); + (*down)(parent, cpu, sd); list_for_each_entry_rcu(child, &parent->children, siblings) { parent = child; goto down; @@ -1508,7 +1508,7 @@ down: up: continue; } - (*up)(parent, sd); + (*up)(parent, cpu, sd); child = parent; parent = parent->parent; @@ -1520,8 +1520,8 @@ up: /* * Calculate the aggregate runqueue weight. */ -static -void aggregate_group_weight(struct task_group *tg, struct sched_domain *sd) +static void +aggregate_group_weight(struct task_group *tg, int cpu, struct sched_domain *sd) { unsigned long rq_weight = 0; unsigned long task_weight = 0; @@ -1532,15 +1532,15 @@ void aggregate_group_weight(struct task_group *tg, struct sched_domain *sd) task_weight += tg->cfs_rq[i]->task_weight; } - aggregate(tg, sd)->rq_weight = rq_weight; - aggregate(tg, sd)->task_weight = task_weight; + aggregate(tg, cpu)->rq_weight = rq_weight; + aggregate(tg, cpu)->task_weight = task_weight; } /* * Compute the weight of this group on the given cpus. */ -static -void aggregate_group_shares(struct task_group *tg, struct sched_domain *sd) +static void +aggregate_group_shares(struct task_group *tg, int cpu, struct sched_domain *sd) { unsigned long shares = 0; int i; @@ -1548,18 +1548,18 @@ void aggregate_group_shares(struct task_group *tg, struct sched_domain *sd) for_each_cpu_mask(i, sd->span) shares += tg->cfs_rq[i]->shares; - if ((!shares && aggregate(tg, sd)->rq_weight) || shares > tg->shares) + if ((!shares && aggregate(tg, cpu)->rq_weight) || shares > tg->shares) shares = tg->shares; - aggregate(tg, sd)->shares = shares; + aggregate(tg, cpu)->shares = shares; } /* * Compute the load fraction assigned to this group, relies on the aggregate * weight and this group's parent's load, i.e. top-down. */ -static -void aggregate_group_load(struct task_group *tg, struct sched_domain *sd) +static void +aggregate_group_load(struct task_group *tg, int cpu, struct sched_domain *sd) { unsigned long load; @@ -1571,17 +1571,17 @@ void aggregate_group_load(struct task_group *tg, struct sched_domain *sd) load += cpu_rq(i)->load.weight; } else { - load = aggregate(tg->parent, sd)->load; + load = aggregate(tg->parent, cpu)->load; /* * shares is our weight in the parent's rq so * shares/parent->rq_weight gives our fraction of the load */ - load *= aggregate(tg, sd)->shares; - load /= aggregate(tg->parent, sd)->rq_weight + 1; + load *= aggregate(tg, cpu)->shares; + load /= aggregate(tg->parent, cpu)->rq_weight + 1; } - aggregate(tg, sd)->load = load; + aggregate(tg, cpu)->load = load; } static void __set_se_shares(struct sched_entity *se, unsigned long shares); @@ -1590,8 +1590,8 @@ static void __set_se_shares(struct sched_entity *se, unsigned long shares); * Calculate and set the cpu's group shares. */ static void -__update_group_shares_cpu(struct task_group *tg, struct sched_domain *sd, - int tcpu) +__update_group_shares_cpu(struct task_group *tg, int cpu, + struct sched_domain *sd, int tcpu) { int boost = 0; unsigned long shares; @@ -1618,8 +1618,8 @@ __update_group_shares_cpu(struct task_group *tg, struct sched_domain *sd, * \Sum rq_weight * */ - shares = aggregate(tg, sd)->shares * rq_weight; - shares /= aggregate(tg, sd)->rq_weight + 1; + shares = aggregate(tg, cpu)->shares * rq_weight; + shares /= aggregate(tg, cpu)->rq_weight + 1; /* * record the actual number of shares, not the boosted amount. @@ -1639,15 +1639,15 @@ __update_group_shares_cpu(struct task_group *tg, struct sched_domain *sd, * task went to. */ static void -__move_group_shares(struct task_group *tg, struct sched_domain *sd, +__move_group_shares(struct task_group *tg, int cpu, struct sched_domain *sd, int scpu, int dcpu) { unsigned long shares; shares = tg->cfs_rq[scpu]->shares + tg->cfs_rq[dcpu]->shares; - __update_group_shares_cpu(tg, sd, scpu); - __update_group_shares_cpu(tg, sd, dcpu); + __update_group_shares_cpu(tg, cpu, sd, scpu); + __update_group_shares_cpu(tg, cpu, sd, dcpu); /* * ensure we never loose shares due to rounding errors in the @@ -1663,19 +1663,19 @@ __move_group_shares(struct task_group *tg, struct sched_domain *sd, * we need to walk up the tree and change all shares until we hit the root. */ static void -move_group_shares(struct task_group *tg, struct sched_domain *sd, +move_group_shares(struct task_group *tg, int cpu, struct sched_domain *sd, int scpu, int dcpu) { while (tg) { - __move_group_shares(tg, sd, scpu, dcpu); + __move_group_shares(tg, cpu, sd, scpu, dcpu); tg = tg->parent; } } -static -void aggregate_group_set_shares(struct task_group *tg, struct sched_domain *sd) +static void +aggregate_group_set_shares(struct task_group *tg, int cpu, struct sched_domain *sd) { - unsigned long shares = aggregate(tg, sd)->shares; + unsigned long shares = aggregate(tg, cpu)->shares; int i; for_each_cpu_mask(i, sd->span) { @@ -1683,20 +1683,20 @@ void aggregate_group_set_shares(struct task_group *tg, struct sched_domain *sd) unsigned long flags; spin_lock_irqsave(&rq->lock, flags); - __update_group_shares_cpu(tg, sd, i); + __update_group_shares_cpu(tg, cpu, sd, i); spin_unlock_irqrestore(&rq->lock, flags); } - aggregate_group_shares(tg, sd); + aggregate_group_shares(tg, cpu, sd); /* * ensure we never loose shares due to rounding errors in the * above redistribution. */ - shares -= aggregate(tg, sd)->shares; + shares -= aggregate(tg, cpu)->shares; if (shares) { - tg->cfs_rq[sd->first_cpu]->shares += shares; - aggregate(tg, sd)->shares += shares; + tg->cfs_rq[cpu]->shares += shares; + aggregate(tg, cpu)->shares += shares; } } @@ -1704,21 +1704,21 @@ void aggregate_group_set_shares(struct task_group *tg, struct sched_domain *sd) * Calculate the accumulative weight and recursive load of each task group * while walking down the tree. */ -static -void aggregate_get_down(struct task_group *tg, struct sched_domain *sd) +static void +aggregate_get_down(struct task_group *tg, int cpu, struct sched_domain *sd) { - aggregate_group_weight(tg, sd); - aggregate_group_shares(tg, sd); - aggregate_group_load(tg, sd); + aggregate_group_weight(tg, cpu, sd); + aggregate_group_shares(tg, cpu, sd); + aggregate_group_load(tg, cpu, sd); } /* * Rebalance the cpu shares while walking back up the tree. */ -static -void aggregate_get_up(struct task_group *tg, struct sched_domain *sd) +static void +aggregate_get_up(struct task_group *tg, int cpu, struct sched_domain *sd) { - aggregate_group_set_shares(tg, sd); + aggregate_group_set_shares(tg, cpu, sd); } static DEFINE_PER_CPU(spinlock_t, aggregate_lock); @@ -1731,18 +1731,18 @@ static void __init init_aggregate(void) spin_lock_init(&per_cpu(aggregate_lock, i)); } -static int get_aggregate(struct sched_domain *sd) +static int get_aggregate(int cpu, struct sched_domain *sd) { - if (!spin_trylock(&per_cpu(aggregate_lock, sd->first_cpu))) + if (!spin_trylock(&per_cpu(aggregate_lock, cpu))) return 0; - aggregate_walk_tree(aggregate_get_down, aggregate_get_up, sd); + aggregate_walk_tree(aggregate_get_down, aggregate_get_up, cpu, sd); return 1; } -static void put_aggregate(struct sched_domain *sd) +static void put_aggregate(int cpu, struct sched_domain *sd) { - spin_unlock(&per_cpu(aggregate_lock, sd->first_cpu)); + spin_unlock(&per_cpu(aggregate_lock, cpu)); } static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares) @@ -1756,12 +1756,12 @@ static inline void init_aggregate(void) { } -static inline int get_aggregate(struct sched_domain *sd) +static inline int get_aggregate(int cpu, struct sched_domain *sd) { return 0; } -static inline void put_aggregate(struct sched_domain *sd) +static inline void put_aggregate(int cpu, struct sched_domain *sd) { } #endif @@ -3539,7 +3539,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, cpus_setall(*cpus); - unlock_aggregate = get_aggregate(sd); + unlock_aggregate = get_aggregate(this_cpu, sd); /* * When power savings policy is enabled for the parent domain, idle @@ -3678,7 +3678,7 @@ out_one_pinned: ld_moved = 0; out: if (unlock_aggregate) - put_aggregate(sd); + put_aggregate(this_cpu, sd); return ld_moved; } @@ -7292,7 +7292,6 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, ALLNODES); set_domain_attribute(sd, attr); sd->span = *cpu_map; - sd->first_cpu = first_cpu(sd->span); cpu_to_allnodes_group(i, cpu_map, &sd->groups, tmpmask); p = sd; sd_allnodes = 1; @@ -7303,7 +7302,6 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, NODE); set_domain_attribute(sd, attr); sched_domain_node_span(cpu_to_node(i), &sd->span); - sd->first_cpu = first_cpu(sd->span); sd->parent = p; if (p) p->child = sd; @@ -7315,7 +7313,6 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, CPU); set_domain_attribute(sd, attr); sd->span = *nodemask; - sd->first_cpu = first_cpu(sd->span); sd->parent = p; if (p) p->child = sd; @@ -7327,7 +7324,6 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, MC); set_domain_attribute(sd, attr); sd->span = cpu_coregroup_map(i); - sd->first_cpu = first_cpu(sd->span); cpus_and(sd->span, sd->span, *cpu_map); sd->parent = p; p->child = sd; @@ -7340,7 +7336,6 @@ static int __build_sched_domains(const cpumask_t *cpu_map, SD_INIT(sd, SIBLING); set_domain_attribute(sd, attr); sd->span = per_cpu(cpu_sibling_map, i); - sd->first_cpu = first_cpu(sd->span); cpus_and(sd->span, sd->span, *cpu_map); sd->parent = p; p->child = sd; diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 509092af0330..40cf24ab4de8 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1429,11 +1429,11 @@ load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest, /* * empty group */ - if (!aggregate(tg, sd)->task_weight) + if (!aggregate(tg, this_cpu)->task_weight) continue; - rem_load = rem_load_move * aggregate(tg, sd)->rq_weight; - rem_load /= aggregate(tg, sd)->load + 1; + rem_load = rem_load_move * aggregate(tg, this_cpu)->rq_weight; + rem_load /= aggregate(tg, this_cpu)->load + 1; this_weight = tg->cfs_rq[this_cpu]->task_weight; busiest_weight = tg->cfs_rq[busiest_cpu]->task_weight; @@ -1451,10 +1451,10 @@ load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest, if (!moved_load) continue; - move_group_shares(tg, sd, busiest_cpu, this_cpu); + move_group_shares(tg, this_cpu, sd, busiest_cpu, this_cpu); - moved_load *= aggregate(tg, sd)->load; - moved_load /= aggregate(tg, sd)->rq_weight + 1; + moved_load *= aggregate(tg, this_cpu)->load; + moved_load /= aggregate(tg, this_cpu)->rq_weight + 1; rem_load_move -= moved_load; if (rem_load_move < 0) -- cgit v1.2.3 From 2398f2c6d34b43025f274fc42eaca34d23ec2320 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 27 Jun 2008 13:41:35 +0200 Subject: sched: update shares on wakeup We found that the affine wakeup code needs rather accurate load figures to be effective. The trouble is that updating the load figures is fairly expensive with group scheduling. Therefore ratelimit the updating. Signed-off-by: Peter Zijlstra Cc: Srivatsa Vaddagiri Cc: Mike Galbraith Signed-off-by: Ingo Molnar --- include/linux/sched.h | 3 +++ kernel/sched.c | 30 +++++++++++++++++++++++++++++- kernel/sched_features.h | 3 ++- kernel/sysctl.c | 8 ++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index eaf821072dbd..835b6c6fcc56 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -783,6 +783,8 @@ struct sched_domain { unsigned int balance_interval; /* initialise to 1. units in ms. */ unsigned int nr_balance_failed; /* initialise to 0 */ + u64 last_update; + #ifdef CONFIG_SCHEDSTATS /* load_balance() stats */ unsigned int lb_count[CPU_MAX_IDLE_TYPES]; @@ -1605,6 +1607,7 @@ extern unsigned int sysctl_sched_child_runs_first; extern unsigned int sysctl_sched_features; extern unsigned int sysctl_sched_migration_cost; extern unsigned int sysctl_sched_nr_migrate; +extern unsigned int sysctl_sched_shares_ratelimit; int sched_nr_latency_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, diff --git a/kernel/sched.c b/kernel/sched.c index 1cff969f6646..62db0891025a 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -777,6 +777,12 @@ late_initcall(sched_init_debug); */ const_debug unsigned int sysctl_sched_nr_migrate = 32; +/* + * ratelimit for updating the group shares. + * default: 0.5ms + */ +const_debug unsigned int sysctl_sched_shares_ratelimit = 500000; + /* * period over which we measure -rt task cpu usage in us. * default: 1s @@ -1590,7 +1596,13 @@ tg_nop(struct task_group *tg, int cpu, struct sched_domain *sd) static void update_shares(struct sched_domain *sd) { - walk_tg_tree(tg_nop, tg_shares_up, 0, sd); + u64 now = cpu_clock(raw_smp_processor_id()); + s64 elapsed = now - sd->last_update; + + if (elapsed >= (s64)(u64)sysctl_sched_shares_ratelimit) { + sd->last_update = now; + walk_tg_tree(tg_nop, tg_shares_up, 0, sd); + } } static void update_shares_locked(struct rq *rq, struct sched_domain *sd) @@ -2199,6 +2211,22 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync) if (!sched_feat(SYNC_WAKEUPS)) sync = 0; +#ifdef CONFIG_SMP + if (sched_feat(LB_WAKEUP_UPDATE)) { + struct sched_domain *sd; + + this_cpu = raw_smp_processor_id(); + cpu = task_cpu(p); + + for_each_domain(this_cpu, sd) { + if (cpu_isset(cpu, sd->span)) { + update_shares(sd); + break; + } + } + } +#endif + smp_wmb(); rq = task_rq_lock(p, &flags); old_state = p->state; diff --git a/kernel/sched_features.h b/kernel/sched_features.h index d56e3053e746..7d616d2a2a3f 100644 --- a/kernel/sched_features.h +++ b/kernel/sched_features.h @@ -8,4 +8,5 @@ SCHED_FEAT(SYNC_WAKEUPS, 1) SCHED_FEAT(HRTICK, 1) SCHED_FEAT(DOUBLE_TICK, 0) SCHED_FEAT(ASYM_GRAN, 1) -SCHED_FEAT(LB_BIAS, 0) \ No newline at end of file +SCHED_FEAT(LB_BIAS, 0) +SCHED_FEAT(LB_WAKEUP_UPDATE, 1) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 29116652dca8..fe8cdc80ff02 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -264,6 +264,14 @@ static struct ctl_table kern_table[] = { .extra1 = &min_wakeup_granularity_ns, .extra2 = &max_wakeup_granularity_ns, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "sched_shares_ratelimit", + .data = &sysctl_sched_shares_ratelimit, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = CTL_UNNUMBERED, .procname = "sched_child_runs_first", -- cgit v1.2.3 From f225763a7d6c92c4932dbd528437997078496fcc Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 20 Jun 2008 11:50:29 +0200 Subject: ssb, b43, b43legacy, b44: Rewrite SSB DMA API This is a rewrite of the DMA API for SSB devices. This is needed, because the old (non-existing) "API" made too many bad assumptions on the API of the host-bus (PCI). This introduces an almost complete SSB-DMA-API that maps to the lowlevel bus-API based on the bustype. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/b44.c | 140 +++++++++++++++++----------------- drivers/net/wireless/b43/dma.c | 65 ++++++++-------- drivers/net/wireless/b43legacy/dma.c | 63 ++++++++------- drivers/ssb/Kconfig | 2 +- drivers/ssb/main.c | 75 ++++++++++++++---- include/linux/ssb/ssb.h | 143 ++++++++++++++++++++++++++++++++++- 6 files changed, 336 insertions(+), 152 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 59dce6aa0865..c3bda5ce67c4 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -148,9 +148,9 @@ static inline void b44_sync_dma_desc_for_device(struct ssb_device *sdev, unsigned long offset, enum dma_data_direction dir) { - dma_sync_single_range_for_device(sdev->dma_dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, dir); + ssb_dma_sync_single_range_for_device(sdev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, dir); } static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev, @@ -158,9 +158,9 @@ static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev, unsigned long offset, enum dma_data_direction dir) { - dma_sync_single_range_for_cpu(sdev->dma_dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, dir); + ssb_dma_sync_single_range_for_cpu(sdev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, dir); } static inline unsigned long br32(const struct b44 *bp, unsigned long reg) @@ -613,10 +613,10 @@ static void b44_tx(struct b44 *bp) BUG_ON(skb == NULL); - dma_unmap_single(bp->sdev->dma_dev, - rp->mapping, - skb->len, - DMA_TO_DEVICE); + ssb_dma_unmap_single(bp->sdev, + rp->mapping, + skb->len, + DMA_TO_DEVICE); rp->skb = NULL; dev_kfree_skb_irq(skb); } @@ -653,29 +653,29 @@ static int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) if (skb == NULL) return -ENOMEM; - mapping = dma_map_single(bp->sdev->dma_dev, skb->data, - RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); + mapping = ssb_dma_map_single(bp->sdev, skb->data, + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); /* Hardware bug work-around, the chip is unable to do PCI DMA to/from anything above 1GB :-( */ - if (dma_mapping_error(mapping) || + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { /* Sigh... */ - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, - RX_PKT_BUF_SZ, DMA_FROM_DEVICE); + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, + RX_PKT_BUF_SZ, DMA_FROM_DEVICE); dev_kfree_skb_any(skb); skb = __netdev_alloc_skb(bp->dev, RX_PKT_BUF_SZ, GFP_ATOMIC|GFP_DMA); if (skb == NULL) return -ENOMEM; - mapping = dma_map_single(bp->sdev->dma_dev, skb->data, - RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); - if (dma_mapping_error(mapping) || + mapping = ssb_dma_map_single(bp->sdev, skb->data, + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE); + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE); dev_kfree_skb_any(skb); return -ENOMEM; } @@ -750,9 +750,9 @@ static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) dest_idx * sizeof(dest_desc), DMA_BIDIRECTIONAL); - dma_sync_single_for_device(bp->sdev->dma_dev, le32_to_cpu(src_desc->addr), - RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); + ssb_dma_sync_single_for_device(bp->sdev, le32_to_cpu(src_desc->addr), + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); } static int b44_rx(struct b44 *bp, int budget) @@ -772,7 +772,7 @@ static int b44_rx(struct b44 *bp, int budget) struct rx_header *rh; u16 len; - dma_sync_single_for_cpu(bp->sdev->dma_dev, map, + ssb_dma_sync_single_for_cpu(bp->sdev, map, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); rh = (struct rx_header *) skb->data; @@ -806,8 +806,8 @@ static int b44_rx(struct b44 *bp, int budget) skb_size = b44_alloc_rx_skb(bp, cons, bp->rx_prod); if (skb_size < 0) goto drop_it; - dma_unmap_single(bp->sdev->dma_dev, map, - skb_size, DMA_FROM_DEVICE); + ssb_dma_unmap_single(bp->sdev, map, + skb_size, DMA_FROM_DEVICE); /* Leave out rx_header */ skb_put(skb, len + RX_PKT_OFFSET); skb_pull(skb, RX_PKT_OFFSET); @@ -966,25 +966,25 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) goto err_out; } - mapping = dma_map_single(bp->sdev->dma_dev, skb->data, len, DMA_TO_DEVICE); - if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { + mapping = ssb_dma_map_single(bp->sdev, skb->data, len, DMA_TO_DEVICE); + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + len > DMA_30BIT_MASK) { struct sk_buff *bounce_skb; /* Chip can't handle DMA to/from >1GB, use bounce buffer */ - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, len, - DMA_TO_DEVICE); + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, len, + DMA_TO_DEVICE); bounce_skb = __dev_alloc_skb(len, GFP_ATOMIC | GFP_DMA); if (!bounce_skb) goto err_out; - mapping = dma_map_single(bp->sdev->dma_dev, bounce_skb->data, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, - len, DMA_TO_DEVICE); + mapping = ssb_dma_map_single(bp->sdev, bounce_skb->data, + len, DMA_TO_DEVICE); + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + len > DMA_30BIT_MASK) { + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, + len, DMA_TO_DEVICE); dev_kfree_skb_any(bounce_skb); goto err_out; } @@ -1082,8 +1082,8 @@ static void b44_free_rings(struct b44 *bp) if (rp->skb == NULL) continue; - dma_unmap_single(bp->sdev->dma_dev, rp->mapping, RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); + ssb_dma_unmap_single(bp->sdev, rp->mapping, RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } @@ -1094,8 +1094,8 @@ static void b44_free_rings(struct b44 *bp) if (rp->skb == NULL) continue; - dma_unmap_single(bp->sdev->dma_dev, rp->mapping, rp->skb->len, - DMA_TO_DEVICE); + ssb_dma_unmap_single(bp->sdev, rp->mapping, rp->skb->len, + DMA_TO_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } @@ -1117,14 +1117,14 @@ static void b44_init_rings(struct b44 *bp) memset(bp->tx_ring, 0, B44_TX_RING_BYTES); if (bp->flags & B44_FLAG_RX_RING_HACK) - dma_sync_single_for_device(bp->sdev->dma_dev, bp->rx_ring_dma, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + ssb_dma_sync_single_for_device(bp->sdev, bp->rx_ring_dma, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); if (bp->flags & B44_FLAG_TX_RING_HACK) - dma_sync_single_for_device(bp->sdev->dma_dev, bp->tx_ring_dma, - DMA_TABLE_BYTES, - DMA_TO_DEVICE); + ssb_dma_sync_single_for_device(bp->sdev, bp->tx_ring_dma, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); for (i = 0; i < bp->rx_pending; i++) { if (b44_alloc_rx_skb(bp, -1, i) < 0) @@ -1144,25 +1144,27 @@ static void b44_free_consistent(struct b44 *bp) bp->tx_buffers = NULL; if (bp->rx_ring) { if (bp->flags & B44_FLAG_RX_RING_HACK) { - dma_unmap_single(bp->sdev->dma_dev, bp->rx_ring_dma, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + ssb_dma_unmap_single(bp->sdev, bp->rx_ring_dma, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); kfree(bp->rx_ring); } else - dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES, - bp->rx_ring, bp->rx_ring_dma); + ssb_dma_free_consistent(bp->sdev, DMA_TABLE_BYTES, + bp->rx_ring, bp->rx_ring_dma, + GFP_KERNEL); bp->rx_ring = NULL; bp->flags &= ~B44_FLAG_RX_RING_HACK; } if (bp->tx_ring) { if (bp->flags & B44_FLAG_TX_RING_HACK) { - dma_unmap_single(bp->sdev->dma_dev, bp->tx_ring_dma, - DMA_TABLE_BYTES, - DMA_TO_DEVICE); + ssb_dma_unmap_single(bp->sdev, bp->tx_ring_dma, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); kfree(bp->tx_ring); } else - dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES, - bp->tx_ring, bp->tx_ring_dma); + ssb_dma_free_consistent(bp->sdev, DMA_TABLE_BYTES, + bp->tx_ring, bp->tx_ring_dma, + GFP_KERNEL); bp->tx_ring = NULL; bp->flags &= ~B44_FLAG_TX_RING_HACK; } @@ -1187,7 +1189,7 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) goto out_err; size = DMA_TABLE_BYTES; - bp->rx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size, &bp->rx_ring_dma, gfp); + bp->rx_ring = ssb_dma_alloc_consistent(bp->sdev, size, &bp->rx_ring_dma, gfp); if (!bp->rx_ring) { /* Allocation may have failed due to pci_alloc_consistent insisting on use of GFP_DMA, which is more restrictive @@ -1199,11 +1201,11 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) if (!rx_ring) goto out_err; - rx_ring_dma = dma_map_single(bp->sdev->dma_dev, rx_ring, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + rx_ring_dma = ssb_dma_map_single(bp->sdev, rx_ring, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); - if (dma_mapping_error(rx_ring_dma) || + if (ssb_dma_mapping_error(bp->sdev, rx_ring_dma) || rx_ring_dma + size > DMA_30BIT_MASK) { kfree(rx_ring); goto out_err; @@ -1214,9 +1216,9 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) bp->flags |= B44_FLAG_RX_RING_HACK; } - bp->tx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size, &bp->tx_ring_dma, gfp); + bp->tx_ring = ssb_dma_alloc_consistent(bp->sdev, size, &bp->tx_ring_dma, gfp); if (!bp->tx_ring) { - /* Allocation may have failed due to dma_alloc_coherent + /* Allocation may have failed due to ssb_dma_alloc_consistent insisting on use of GFP_DMA, which is more restrictive than necessary... */ struct dma_desc *tx_ring; @@ -1226,11 +1228,11 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) if (!tx_ring) goto out_err; - tx_ring_dma = dma_map_single(bp->sdev->dma_dev, tx_ring, + tx_ring_dma = ssb_dma_map_single(bp->sdev, tx_ring, DMA_TABLE_BYTES, DMA_TO_DEVICE); - if (dma_mapping_error(tx_ring_dma) || + if (ssb_dma_mapping_error(bp->sdev, tx_ring_dma) || tx_ring_dma + size > DMA_30BIT_MASK) { kfree(tx_ring); goto out_err; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 8a09a1db08db..098f886976f6 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -328,11 +328,11 @@ static inline dma_addr_t dmaaddr; if (tx) { - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, DMA_TO_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, DMA_TO_DEVICE); } else { - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, DMA_FROM_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, DMA_FROM_DEVICE); } return dmaaddr; @@ -343,11 +343,11 @@ static inline dma_addr_t addr, size_t len, int tx) { if (tx) { - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, DMA_TO_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, DMA_TO_DEVICE); } else { - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } } @@ -356,8 +356,8 @@ static inline dma_addr_t addr, size_t len) { B43_WARN_ON(ring->tx); - dma_sync_single_for_cpu(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_cpu(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -365,8 +365,8 @@ static inline dma_addr_t addr, size_t len) { B43_WARN_ON(ring->tx); - dma_sync_single_for_device(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_device(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -381,7 +381,6 @@ static inline static int alloc_ringmemory(struct b43_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; gfp_t flags = GFP_KERNEL; /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K @@ -392,11 +391,14 @@ static int alloc_ringmemory(struct b43_dmaring *ring) * For unknown reasons - possibly a hardware error - the BCM4311 rev * 02, which uses 64-bit DMA, needs the ring buffer in very low memory, * which accounts for the GFP_DMA flag below. + * + * The flags here must match the flags in free_ringmemory below! */ if (ring->type == B43_DMA_64BIT) flags |= GFP_DMA; - ring->descbase = dma_alloc_coherent(dma_dev, B43_DMA_RINGMEMSIZE, - &(ring->dmabase), flags); + ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev, + B43_DMA_RINGMEMSIZE, + &(ring->dmabase), flags); if (!ring->descbase) { b43err(ring->dev->wl, "DMA ringmemory allocation failed\n"); return -ENOMEM; @@ -408,10 +410,13 @@ static int alloc_ringmemory(struct b43_dmaring *ring) static void free_ringmemory(struct b43_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; + gfp_t flags = GFP_KERNEL; + + if (ring->type == B43_DMA_64BIT) + flags |= GFP_DMA; - dma_free_coherent(dma_dev, B43_DMA_RINGMEMSIZE, - ring->descbase, ring->dmabase); + ssb_dma_free_consistent(ring->dev->dev, B43_DMA_RINGMEMSIZE, + ring->descbase, ring->dmabase, flags); } /* Reset the RX DMA channel */ @@ -518,7 +523,7 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring, dma_addr_t addr, size_t buffersize, bool dma_to_device) { - if (unlikely(dma_mapping_error(addr))) + if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr))) return 1; switch (ring->type) { @@ -844,10 +849,10 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, goto err_kfree_meta; /* test for ability to dma to txhdr_cache */ - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - b43_txhdr_size(dev), - DMA_TO_DEVICE); + dma_test = ssb_dma_map_single(dev->dev, + ring->txhdr_cache, + b43_txhdr_size(dev), + DMA_TO_DEVICE); if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev), 1)) { @@ -859,10 +864,10 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, if (!ring->txhdr_cache) goto err_kfree_meta; - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - b43_txhdr_size(dev), - DMA_TO_DEVICE); + dma_test = ssb_dma_map_single(dev->dev, + ring->txhdr_cache, + b43_txhdr_size(dev), + DMA_TO_DEVICE); if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev), 1)) { @@ -873,9 +878,9 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, } } - dma_unmap_single(dev->dev->dma_dev, - dma_test, b43_txhdr_size(dev), - DMA_TO_DEVICE); + ssb_dma_unmap_single(dev->dev, + dma_test, b43_txhdr_size(dev), + DMA_TO_DEVICE); } err = alloc_ringmemory(ring); diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 33cc256c5baf..9736b2f56a75 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -393,13 +393,13 @@ dma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, dma_addr_t dmaaddr; if (tx) - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, - DMA_TO_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, + DMA_TO_DEVICE); else - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, - DMA_FROM_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, + DMA_FROM_DEVICE); return dmaaddr; } @@ -411,13 +411,13 @@ void unmap_descbuffer(struct b43legacy_dmaring *ring, int tx) { if (tx) - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, - DMA_TO_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, + DMA_TO_DEVICE); else - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, - DMA_FROM_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, + DMA_FROM_DEVICE); } static inline @@ -427,8 +427,8 @@ void sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, { B43legacy_WARN_ON(ring->tx); - dma_sync_single_for_cpu(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_cpu(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -438,8 +438,8 @@ void sync_descbuffer_for_device(struct b43legacy_dmaring *ring, { B43legacy_WARN_ON(ring->tx); - dma_sync_single_for_device(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_device(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -458,10 +458,11 @@ void free_descriptor_buffer(struct b43legacy_dmaring *ring, static int alloc_ringmemory(struct b43legacy_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; - - ring->descbase = dma_alloc_coherent(dma_dev, B43legacy_DMA_RINGMEMSIZE, - &(ring->dmabase), GFP_KERNEL); + /* GFP flags must match the flags in free_ringmemory()! */ + ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev, + B43legacy_DMA_RINGMEMSIZE, + &(ring->dmabase), + GFP_KERNEL); if (!ring->descbase) { b43legacyerr(ring->dev->wl, "DMA ringmemory allocation" " failed\n"); @@ -474,10 +475,8 @@ static int alloc_ringmemory(struct b43legacy_dmaring *ring) static void free_ringmemory(struct b43legacy_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; - - dma_free_coherent(dma_dev, B43legacy_DMA_RINGMEMSIZE, - ring->descbase, ring->dmabase); + ssb_dma_free_consistent(ring->dev->dev, B43legacy_DMA_RINGMEMSIZE, + ring->descbase, ring->dmabase, GFP_KERNEL); } /* Reset the RX DMA channel */ @@ -589,7 +588,7 @@ static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, size_t buffersize, bool dma_to_device) { - if (unlikely(dma_mapping_error(addr))) + if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr))) return 1; switch (ring->type) { @@ -893,9 +892,9 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, goto err_kfree_meta; /* test for ability to dma to txhdr_cache */ - dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, - sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); + dma_test = ssb_dma_map_single(dev->dev, ring->txhdr_cache, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); if (b43legacy_dma_mapping_error(ring, dma_test, sizeof(struct b43legacy_txhdr_fw3), 1)) { @@ -907,7 +906,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, if (!ring->txhdr_cache) goto err_kfree_meta; - dma_test = dma_map_single(dev->dev->dma_dev, + dma_test = ssb_dma_map_single(dev->dev, ring->txhdr_cache, sizeof(struct b43legacy_txhdr_fw3), DMA_TO_DEVICE); @@ -917,9 +916,9 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, goto err_kfree_txhdr_cache; } - dma_unmap_single(dev->dev->dma_dev, - dma_test, sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); + ssb_dma_unmap_single(dev->dev, dma_test, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); } ring->dev = dev; diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index cd845b8acd17..307b1f62d949 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -2,7 +2,7 @@ menu "Sonics Silicon Backplane" config SSB_POSSIBLE bool - depends on HAS_IOMEM + depends on HAS_IOMEM && HAS_DMA default y config SSB diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index d184f2aea78d..d831a2beff39 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -462,18 +462,15 @@ static int ssb_devices_register(struct ssb_bus *bus) #ifdef CONFIG_SSB_PCIHOST sdev->irq = bus->host_pci->irq; dev->parent = &bus->host_pci->dev; - sdev->dma_dev = &bus->host_pci->dev; #endif break; case SSB_BUSTYPE_PCMCIA: #ifdef CONFIG_SSB_PCMCIAHOST sdev->irq = bus->host_pcmcia->irq.AssignedIRQ; dev->parent = &bus->host_pcmcia->dev; - sdev->dma_dev = &bus->host_pcmcia->dev; #endif break; case SSB_BUSTYPE_SSB: - sdev->dma_dev = dev; break; } @@ -1156,36 +1153,82 @@ u32 ssb_dma_translation(struct ssb_device *dev) { switch (dev->bus->bustype) { case SSB_BUSTYPE_SSB: - case SSB_BUSTYPE_PCMCIA: return 0; case SSB_BUSTYPE_PCI: return SSB_PCI_DMA; + default: + __ssb_dma_not_implemented(dev); } return 0; } EXPORT_SYMBOL(ssb_dma_translation); -int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask) +int ssb_dma_set_mask(struct ssb_device *dev, u64 mask) { - struct device *dma_dev = ssb_dev->dma_dev; - int err = 0; + int err; -#ifdef CONFIG_SSB_PCIHOST - if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI) { - err = pci_set_dma_mask(ssb_dev->bus->host_pci, mask); + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + err = pci_set_dma_mask(dev->bus->host_pci, mask); if (err) return err; - err = pci_set_consistent_dma_mask(ssb_dev->bus->host_pci, mask); + err = pci_set_consistent_dma_mask(dev->bus->host_pci, mask); return err; + case SSB_BUSTYPE_SSB: + return dma_set_mask(dev->dev, mask); + default: + __ssb_dma_not_implemented(dev); } -#endif - dma_dev->coherent_dma_mask = mask; - dma_dev->dma_mask = &dma_dev->coherent_dma_mask; - - return err; + return -ENOSYS; } EXPORT_SYMBOL(ssb_dma_set_mask); +void * ssb_dma_alloc_consistent(struct ssb_device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp_flags) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + if (gfp_flags & GFP_DMA) { + /* Workaround: The PCI API does not support passing + * a GFP flag. */ + return dma_alloc_coherent(&dev->bus->host_pci->dev, + size, dma_handle, gfp_flags); + } + return pci_alloc_consistent(dev->bus->host_pci, size, dma_handle); + case SSB_BUSTYPE_SSB: + return dma_alloc_coherent(dev->dev, size, dma_handle, gfp_flags); + default: + __ssb_dma_not_implemented(dev); + } + return NULL; +} +EXPORT_SYMBOL(ssb_dma_alloc_consistent); + +void ssb_dma_free_consistent(struct ssb_device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + gfp_t gfp_flags) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + if (gfp_flags & GFP_DMA) { + /* Workaround: The PCI API does not support passing + * a GFP flag. */ + dma_free_coherent(&dev->bus->host_pci->dev, + size, vaddr, dma_handle); + return; + } + pci_free_consistent(dev->bus->host_pci, size, + vaddr, dma_handle); + return; + case SSB_BUSTYPE_SSB: + dma_free_coherent(dev->dev, size, vaddr, dma_handle); + return; + default: + __ssb_dma_not_implemented(dev); + } +} +EXPORT_SYMBOL(ssb_dma_free_consistent); + int ssb_bus_may_powerdown(struct ssb_bus *bus) { struct ssb_chipcommon *cc; diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 50dfd0dc4093..0fe5a0ded3ea 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -137,9 +137,6 @@ struct ssb_device { const struct ssb_bus_ops *ops; struct device *dev; - /* Pointer to the device that has to be used for - * any DMA related operation. */ - struct device *dma_dev; struct ssb_bus *bus; struct ssb_device_id id; @@ -399,13 +396,151 @@ static inline void ssb_block_write(struct ssb_device *dev, const void *buffer, #endif /* CONFIG_SSB_BLOCKIO */ +/* The SSB DMA API. Use this API for any DMA operation on the device. + * This API basically is a wrapper that calls the correct DMA API for + * the host device type the SSB device is attached to. */ + /* Translation (routing) bits that need to be ORed to DMA * addresses before they are given to a device. */ extern u32 ssb_dma_translation(struct ssb_device *dev); #define SSB_DMA_TRANSLATION_MASK 0xC0000000 #define SSB_DMA_TRANSLATION_SHIFT 30 -extern int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask); +extern int ssb_dma_set_mask(struct ssb_device *dev, u64 mask); + +extern void * ssb_dma_alloc_consistent(struct ssb_device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp_flags); +extern void ssb_dma_free_consistent(struct ssb_device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + gfp_t gfp_flags); + +static inline void __cold __ssb_dma_not_implemented(struct ssb_device *dev) +{ +#ifdef CONFIG_SSB_DEBUG + printk(KERN_ERR "SSB: BUG! Calling DMA API for " + "unsupported bustype %d\n", dev->bus->bustype); +#endif /* DEBUG */ +} + +static inline int ssb_dma_mapping_error(struct ssb_device *dev, dma_addr_t addr) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + return pci_dma_mapping_error(addr); + case SSB_BUSTYPE_SSB: + return dma_mapping_error(addr); + default: + __ssb_dma_not_implemented(dev); + } + return -ENOSYS; +} + +static inline dma_addr_t ssb_dma_map_single(struct ssb_device *dev, void *p, + size_t size, enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + return pci_map_single(dev->bus->host_pci, p, size, dir); + case SSB_BUSTYPE_SSB: + return dma_map_single(dev->dev, p, size, dir); + default: + __ssb_dma_not_implemented(dev); + } + return 0; +} + +static inline void ssb_dma_unmap_single(struct ssb_device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + pci_unmap_single(dev->bus->host_pci, dma_addr, size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_unmap_single(dev->dev, dma_addr, size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_for_cpu(struct ssb_device *dev, + dma_addr_t dma_addr, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + pci_dma_sync_single_for_cpu(dev->bus->host_pci, dma_addr, + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_for_cpu(dev->dev, dma_addr, size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_for_device(struct ssb_device *dev, + dma_addr_t dma_addr, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + pci_dma_sync_single_for_device(dev->bus->host_pci, dma_addr, + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_for_device(dev->dev, dma_addr, size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_range_for_cpu(struct ssb_device *dev, + dma_addr_t dma_addr, + unsigned long offset, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + /* Just sync everything. That's all the PCI API can do. */ + pci_dma_sync_single_for_cpu(dev->bus->host_pci, dma_addr, + offset + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_range_for_cpu(dev->dev, dma_addr, offset, + size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_range_for_device(struct ssb_device *dev, + dma_addr_t dma_addr, + unsigned long offset, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + /* Just sync everything. That's all the PCI API can do. */ + pci_dma_sync_single_for_device(dev->bus->host_pci, dma_addr, + offset + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_range_for_device(dev->dev, dma_addr, offset, + size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} #ifdef CONFIG_SSB_PCIHOST -- cgit v1.2.3 From ffd7891dc909b3648e87f7cf8f84a6dc12fc1cc6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 21 Jun 2008 10:02:46 -0400 Subject: mac80211: Let drivers have access to TKIP key offets for TX and RX MIC Some drivers may want to to use the TKIP key offsets for TX and RX MIC so lets move this out. Lets also clear up a bit how this is used internally in mac80211. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 ++++- include/net/mac80211.h | 7 ++++++- net/mac80211/key.h | 37 ++++++++++++------------------------- net/mac80211/tkip.c | 10 +++++----- net/mac80211/wpa.c | 20 ++++++++++++++------ 5 files changed, 41 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index aa8411e2a160..2be7c63bc0f2 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -241,7 +241,10 @@ enum nl80211_attrs { NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; -#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 /** * enum nl80211_iftype - (virtual) interface types diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7ab4ff6159a2..19f1e412a0f0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -595,7 +595,12 @@ enum ieee80211_key_flags { * @flags: key flags, see &enum ieee80211_key_flags. * @keyidx: the key index (0-3) * @keylen: key material length - * @key: key material + * @key: key material. For ALG_TKIP the key is encoded as a 256-bit (32 byte) + * data block: + * - Temporal Encryption Key (128 bits) + * - Temporal Authenticator Tx MIC Key (64 bits) + * - Temporal Authenticator Rx MIC Key (64 bits) + * */ struct ieee80211_key_conf { enum ieee80211_key_alg alg; diff --git a/net/mac80211/key.h b/net/mac80211/key.h index a0f774aafa45..425816e0996c 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -16,31 +16,18 @@ #include #include -/* ALG_TKIP - * struct ieee80211_key::key is encoded as a 256-bit (32 byte) data block: - * Temporal Encryption Key (128 bits) - * Temporal Authenticator Tx MIC Key (64 bits) - * Temporal Authenticator Rx MIC Key (64 bits) - */ - -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 - -#define ALG_TKIP_KEY_LEN 32 -/* Starting offsets for each key */ -#define ALG_TKIP_TEMP_ENCR_KEY 0 -#define ALG_TKIP_TEMP_AUTH_TX_MIC_KEY 16 -#define ALG_TKIP_TEMP_AUTH_RX_MIC_KEY 24 -#define TKIP_IV_LEN 8 -#define TKIP_ICV_LEN 4 - -#define ALG_CCMP_KEY_LEN 16 -#define CCMP_HDR_LEN 8 -#define CCMP_MIC_LEN 8 -#define CCMP_TK_LEN 16 -#define CCMP_PN_LEN 6 - -#define NUM_RX_DATA_QUEUES 17 +#define WEP_IV_LEN 4 +#define WEP_ICV_LEN 4 +#define ALG_TKIP_KEY_LEN 32 +#define ALG_CCMP_KEY_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 +#define TKIP_IV_LEN 8 +#define TKIP_ICV_LEN 4 + +#define NUM_RX_DATA_QUEUES 17 struct ieee80211_local; struct ieee80211_sub_if_data; diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 699807889988..995f7af3d25e 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -164,7 +164,7 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, iv16 = data[2] | (data[0] << 8); iv32 = get_unaligned_le32(&data[4]); - tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; ctx = &key->u.tkip.tx; #ifdef CONFIG_MAC80211_TKIP_DEBUG @@ -205,7 +205,7 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, { u8 rc4key[16]; struct tkip_ctx *ctx = &key->u.tkip.tx; - const u8 *tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; /* Calculate per-packet key */ if (ctx->iv16 == 0 || !ctx->initialized) @@ -231,7 +231,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, u32 iv16; u8 rc4key[16], keyid, *pos = payload; int res; - const u8 *tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; if (payload_len < 12) return -1; @@ -286,13 +286,13 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, #ifdef CONFIG_MAC80211_TKIP_DEBUG { int i; + u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY; DECLARE_MAC_BUF(mac); printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s" " TK=", print_mac(mac, ta)); for (i = 0; i < 16; i++) printk("%02x ", - key->conf.key[ - ALG_TKIP_TEMP_ENCR_KEY + i]); + key->conf.key[key_offset + i]); printk("\n"); printk(KERN_DEBUG "TKIP decrypt: P1K="); for (i = 0; i < 5; i++) diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 345e10e9b313..f809761fbfb5 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -49,7 +49,7 @@ static int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da, ieee80211_tx_result ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) { - u8 *data, *sa, *da, *key, *mic, qos_tid; + u8 *data, *sa, *da, *key, *mic, qos_tid, key_offset; size_t data_len; u16 fc; struct sk_buff *skb = tx->skb; @@ -88,8 +88,12 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) #else authenticator = 1; #endif - key = &tx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY : - ALG_TKIP_TEMP_AUTH_RX_MIC_KEY]; + /* At this point we know we're using ALG_TKIP. To get the MIC key + * we now will rely on the offset from the ieee80211_key_conf::key */ + key_offset = authenticator ? + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY : + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + key = &tx->key->conf.key[key_offset]; mic = skb_put(skb, MICHAEL_MIC_LEN); michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); @@ -100,7 +104,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) ieee80211_rx_result ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) { - u8 *data, *sa, *da, *key = NULL, qos_tid; + u8 *data, *sa, *da, *key = NULL, qos_tid, key_offset; size_t data_len; u16 fc; u8 mic[MICHAEL_MIC_LEN]; @@ -131,8 +135,12 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) #else authenticator = 1; #endif - key = &rx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY : - ALG_TKIP_TEMP_AUTH_TX_MIC_KEY]; + /* At this point we know we're using ALG_TKIP. To get the MIC key + * we now will rely on the offset from the ieee80211_key_conf::key */ + key_offset = authenticator ? + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY : + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key = &rx->key->conf.key[key_offset]; michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { if (!(rx->flags & IEEE80211_RX_RA_MATCH)) -- cgit v1.2.3 From 9433f6dd3a4677e9b92c6e1cd7f98b11598b7c2c Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Thu, 26 Jun 2008 10:50:04 +0800 Subject: PCI: Fix comment of pci_dynids struct pci_driver has no field of driver_data. It's in pci_device_id. Signed-off-by: Wang Chen Signed-off-by: Jesse Barnes --- include/linux/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index f1f73f79a180..76c9a4a96152 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -338,7 +338,7 @@ struct pci_bus_region { struct pci_dynids { spinlock_t lock; /* protects list, index */ struct list_head list; /* for IDs added at runtime */ - unsigned int use_driver_data:1; /* pci_driver->driver_data is used */ + unsigned int use_driver_data:1; /* pci_device_id->driver_data is used */ }; /* ---------------------------------------------------------------- */ -- cgit v1.2.3 From b660398101cd0622325480a67ac88bb4d33d553a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Jun 2008 14:39:42 +0100 Subject: kbuild: fix a.out.h export to userspace with O= build. We need to check for existence of the a.out.h header in the source tree, not the object tree, if we want it to get the right answer with O=. Signed-off-by: David Woodhouse Signed-off-by: Sam Ravnborg --- include/asm-generic/Kbuild.asm | 2 +- include/asm-powerpc/Kbuild | 1 - include/linux/Kbuild | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index 92a6d91d0c1a..7cd25b8e7c9a 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -1,6 +1,6 @@ header-y += kvm.h -ifeq ($(wildcard include/asm-$(SRCARCH)/a.out.h),include/asm-$(SRCARCH)/a.out.h) +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h endif unifdef-y += auxvec.h diff --git a/include/asm-powerpc/Kbuild b/include/asm-powerpc/Kbuild index 7381916dfcbb..bca352e033c3 100644 --- a/include/asm-powerpc/Kbuild +++ b/include/asm-powerpc/Kbuild @@ -1,6 +1,5 @@ include include/asm-generic/Kbuild.asm -header-y += a.out.h header-y += auxvec.h header-y += ioctls.h header-y += mman.h diff --git a/include/linux/Kbuild b/include/linux/Kbuild index b6fbb2573e88..71d70d1fbce2 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -166,7 +166,7 @@ unifdef-y += acct.h unifdef-y += adb.h unifdef-y += adfs_fs.h unifdef-y += agpgart.h -ifeq ($(wildcard include/asm-$(SRCARCH)/a.out.h),include/asm-$(SRCARCH)/a.out.h) +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h endif unifdef-y += apm_bios.h -- cgit v1.2.3 From a0da84f35b25875870270d16b6eccda4884d61a7 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:22 +1000 Subject: Improve setting of "events_cleared" for write-intent bitmaps. When an array is degraded, bits in the write-intent bitmap are not cleared, so that if the missing device is re-added, it can be synced by only updated those parts of the device that have changed since it was removed. The enable this a 'events_cleared' value is stored. It is the event counter for the array the last time that any bits were cleared. Sometimes - if a device disappears from an array while it is 'clean' - the events_cleared value gets updated incorrectly (there are subtle ordering issues between updateing events in the main metadata and the bitmap metadata) resulting in the missing device appearing to require a full resync when it is re-added. With this patch, we update events_cleared precisely when we are about to clear a bit in the bitmap. We record events_cleared when we clear the bit internally, and copy that to the superblock which is written out before the bit on storage. This makes it more "obviously correct". We also need to update events_cleared when the event_count is going backwards (as happens on a dirty->clean transition of a non-degraded array). Thanks to Mike Snitzer for identifying this problem and testing early "fixes". Cc: "Mike Snitzer" Signed-off-by: Neil Brown --- drivers/md/bitmap.c | 29 ++++++++++++++++++++++++----- include/linux/raid/bitmap.h | 1 + 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index b26927ce889c..dedba16d42f7 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -454,8 +454,11 @@ void bitmap_update_sb(struct bitmap *bitmap) spin_unlock_irqrestore(&bitmap->lock, flags); sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); sb->events = cpu_to_le64(bitmap->mddev->events); - if (!bitmap->mddev->degraded) - sb->events_cleared = cpu_to_le64(bitmap->mddev->events); + if (bitmap->mddev->events < bitmap->events_cleared) { + /* rocking back to read-only */ + bitmap->events_cleared = bitmap->mddev->events; + sb->events_cleared = cpu_to_le64(bitmap->events_cleared); + } kunmap_atomic(sb, KM_USER0); write_page(bitmap, bitmap->sb_page, 1); } @@ -1085,9 +1088,19 @@ void bitmap_daemon_work(struct bitmap *bitmap) } else spin_unlock_irqrestore(&bitmap->lock, flags); lastpage = page; -/* - printk("bitmap clean at page %lu\n", j); -*/ + + /* We are possibly going to clear some bits, so make + * sure that events_cleared is up-to-date. + */ + if (bitmap->need_sync) { + bitmap_super_t *sb; + bitmap->need_sync = 0; + sb = kmap_atomic(bitmap->sb_page, KM_USER0); + sb->events_cleared = + cpu_to_le64(bitmap->events_cleared); + kunmap_atomic(sb, KM_USER0); + write_page(bitmap, bitmap->sb_page, 1); + } spin_lock_irqsave(&bitmap->lock, flags); clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); } @@ -1257,6 +1270,12 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto return; } + if (success && + bitmap->events_cleared < bitmap->mddev->events) { + bitmap->events_cleared = bitmap->mddev->events; + bitmap->need_sync = 1; + } + if (!success && ! (*bmc & NEEDED_MASK)) *bmc |= NEEDED_MASK; diff --git a/include/linux/raid/bitmap.h b/include/linux/raid/bitmap.h index 78bfdea24a8e..e98900671ca9 100644 --- a/include/linux/raid/bitmap.h +++ b/include/linux/raid/bitmap.h @@ -221,6 +221,7 @@ struct bitmap { unsigned long syncchunk; __u64 events_cleared; + int need_sync; /* bitmap spinlock */ spinlock_t lock; -- cgit v1.2.3 From 5e96ee65c8bd629ce093da67a066d3946468298a Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:24 +1000 Subject: Allow setting start point for requested check/repair This makes it possible to just resync a small part of an array. e.g. if a drive reports that it has questionable sectors, a 'repair' of just the region covering those sectors will cause them to be read and, if there is an error, re-written with correct data. Signed-off-by: Neil Brown --- drivers/md/md.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/raid/md_k.h | 2 ++ 2 files changed, 44 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 2580ac1b9b0f..261322722c19 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -278,6 +278,7 @@ static mddev_t * mddev_find(dev_t unit) init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); new->reshape_position = MaxSector; + new->resync_min = 0; new->resync_max = MaxSector; new->level = LEVEL_NONE; @@ -3074,6 +3075,36 @@ sync_completed_show(mddev_t *mddev, char *page) static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); +static ssize_t +min_sync_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", + (unsigned long long)mddev->resync_min); +} +static ssize_t +min_sync_store(mddev_t *mddev, const char *buf, size_t len) +{ + unsigned long long min; + if (strict_strtoull(buf, 10, &min)) + return -EINVAL; + if (min > mddev->resync_max) + return -EINVAL; + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + return -EBUSY; + + /* Must be a multiple of chunk_size */ + if (mddev->chunk_size) { + if (min & (sector_t)((mddev->chunk_size>>9)-1)) + return -EINVAL; + } + mddev->resync_min = min; + + return len; +} + +static struct md_sysfs_entry md_min_sync = +__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store); + static ssize_t max_sync_show(mddev_t *mddev, char *page) { @@ -3089,9 +3120,10 @@ max_sync_store(mddev_t *mddev, const char *buf, size_t len) if (strncmp(buf, "max", 3) == 0) mddev->resync_max = MaxSector; else { - char *ep; - unsigned long long max = simple_strtoull(buf, &ep, 10); - if (ep == buf || (*ep != 0 && *ep != '\n')) + unsigned long long max; + if (strict_strtoull(buf, 10, &max)) + return -EINVAL; + if (max < mddev->resync_min) return -EINVAL; if (max < mddev->resync_max && test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) @@ -3222,6 +3254,7 @@ static struct attribute *md_redundancy_attrs[] = { &md_sync_speed.attr, &md_sync_force_parallel.attr, &md_sync_completed.attr, + &md_min_sync.attr, &md_max_sync.attr, &md_suspend_lo.attr, &md_suspend_hi.attr, @@ -3777,6 +3810,7 @@ static int do_md_stop(mddev_t * mddev, int mode) mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; mddev->reshape_position = MaxSector; mddev->external = 0; @@ -5625,9 +5659,11 @@ void md_do_sync(mddev_t *mddev) max_sectors = mddev->resync_max_sectors; mddev->resync_mismatches = 0; /* we don't use the checkpoint if there's a bitmap */ - if (!mddev->bitmap && - !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + j = mddev->resync_min; + else if (!mddev->bitmap) j = mddev->recovery_cp; + } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) max_sectors = mddev->size << 1; else { @@ -5796,6 +5832,7 @@ void md_do_sync(mddev_t *mddev) skip: mddev->curr_resync = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; sysfs_notify(&mddev->kobj, NULL, "sync_completed"); wake_up(&resync_wait); diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 3dea9f545c8f..780e0613e6d5 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -227,6 +227,8 @@ struct mddev_s atomic_t recovery_active; /* blocks scheduled, but not written */ wait_queue_head_t recovery_wait; sector_t recovery_cp; + sector_t resync_min; /* user requested sync + * starts here */ sector_t resync_max; /* resync should pause * when it gets here */ -- cgit v1.2.3 From 72a23c211e4587859d5bf61ac4962d76e593fb02 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:41 +1000 Subject: Make sure all changes to md/sync_action are notified. When the 'resync' thread starts or stops, when we explicitly set sync_action, or when we determine that there is definitely nothing to do, we notify sync_action. To stop "sync_action" from occasionally showing the wrong value, we introduce a new flags - MD_RECOVERY_RECOVER - to say that a recovery is probably needed or happening, and we make sure that we set MD_RECOVERY_RUNNING before clearing MD_RECOVERY_NEEDED. Signed-off-by: Neil Brown --- Documentation/md.txt | 6 ++++++ drivers/md/md.c | 34 ++++++++++++++++++++++++++++------ include/linux/raid/md_k.h | 2 ++ 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/Documentation/md.txt b/Documentation/md.txt index dca97ba4944a..c05bfb55659e 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -386,6 +386,12 @@ also have 'check' and 'repair' will start the appropriate process providing the current state is 'idle'. + This file responds to select/poll. Any important change in the value + triggers a poll event. Sometimes the value will briefly be + "recover" if a recovery seems to be needed, but cannot be + achieved. In that case, the transition to "recover" isn't + notified, but the transition away is. + mismatch_count When performing 'check' and 'repair', and possibly when performing 'resync', md will count the number of errors that are diff --git a/drivers/md/md.c b/drivers/md/md.c index 5b9d4fe4e6e4..c26dcad8a3ac 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -169,7 +169,6 @@ void md_new_event(mddev_t *mddev) { atomic_inc(&md_event_count); wake_up(&md_event_waiters); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); } EXPORT_SYMBOL_GPL(md_new_event); @@ -2936,7 +2935,7 @@ action_show(mddev_t *mddev, char *page) type = "check"; else type = "repair"; - } else + } else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery)) type = "recover"; } return sprintf(page, "%s\n", type); @@ -2958,9 +2957,12 @@ action_store(mddev_t *mddev, const char *page, size_t len) } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) return -EBUSY; - else if (cmd_match(page, "resync") || cmd_match(page, "recover")) + else if (cmd_match(page, "resync")) + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + else if (cmd_match(page, "recover")) { + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - else if (cmd_match(page, "reshape")) { + } else if (cmd_match(page, "reshape")) { int err; if (mddev->pers->start_reshape == NULL) return -EINVAL; @@ -2977,6 +2979,7 @@ action_store(mddev_t *mddev, const char *page, size_t len) } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); return len; } @@ -3682,6 +3685,7 @@ static int do_md_run(mddev_t * mddev) mddev->changed = 1; md_new_event(mddev); sysfs_notify(&mddev->kobj, NULL, "array_state"); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); kobject_uevent(&mddev->gendisk->dev.kobj, KOBJ_CHANGE); return 0; } @@ -4252,6 +4256,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) export_rdev(rdev); md_update_sb(mddev, 1); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); return err; @@ -5105,6 +5111,8 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -6055,13 +6063,18 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); goto unlock; } + /* Set RUNNING before clearing NEEDED to avoid + * any transients in the value of "sync_action". + */ + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); /* Clear some bits that don't mean anything, but * might be left set */ - clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); clear_bit(MD_RECOVERY_INTR, &mddev->recovery); clear_bit(MD_RECOVERY_DONE, &mddev->recovery); @@ -6079,17 +6092,19 @@ void md_check_recovery(mddev_t *mddev) /* Cannot proceed */ goto unlock; set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if ((spares = remove_and_add_spares(mddev))) { clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (mddev->recovery_cp < MaxSector) { set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) /* nothing to be done ... */ goto unlock; if (mddev->pers->sync_request) { - set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); if (spares && mddev->bitmap && ! mddev->bitmap->file) { /* We are adding a device or devices to an array * which has the bitmap stored on all devices. @@ -6108,9 +6123,16 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; } else md_wakeup_thread(mddev->sync_thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); } unlock: + if (!mddev->sync_thread) { + clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (test_and_clear_bit(MD_RECOVERY_RECOVER, + &mddev->recovery)) + sysfs_notify(&mddev->kobj, NULL, "sync_action"); + } mddev_unlock(mddev); } } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 780e0613e6d5..62aa9c9a6ddc 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -188,6 +188,7 @@ struct mddev_s * NEEDED: we might need to start a resync/recover * RUNNING: a thread is running, or about to be started * SYNC: actually doing a resync, not a recovery + * RECOVER: doing recovery, or need to try it. * INTR: resync needs to be aborted for some reason * DONE: thread is done and is waiting to be reaped * REQUEST: user-space has requested a sync (used with SYNC) @@ -198,6 +199,7 @@ struct mddev_s */ #define MD_RECOVERY_RUNNING 0 #define MD_RECOVERY_SYNC 1 +#define MD_RECOVERY_RECOVER 2 #define MD_RECOVERY_INTR 3 #define MD_RECOVERY_DONE 4 #define MD_RECOVERY_NEEDED 5 -- cgit v1.2.3 From 526647320e696f434647f38421a6ecf65b859c43 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:44 +1000 Subject: Make sure all changes to md/dev-XX/state are notified The important state change happens during an interrupt in md_error. So just set a flag there and call sysfs_notify later in process context. Signed-off-by: Neil Brown --- Documentation/md.txt | 10 ++++++++++ drivers/md/md.c | 14 +++++++++++++- include/linux/raid/md_k.h | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/md.txt b/Documentation/md.txt index eb6e69e3732e..e06cc59437e4 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -297,6 +297,10 @@ Each directory contains: writemostly - device will only be subject to read requests if there are no other options. This applies only to raid1 arrays. + blocked - device has failed, metadata is "external", + and the failure hasn't been acknowledged yet. + Writes that would write to this device if + it were not faulty are blocked. spare - device is working, but not a full member. This includes spares that are in the process of being recovered to @@ -306,6 +310,12 @@ Each directory contains: Writing "remove" removes the device from the array. Writing "writemostly" sets the writemostly flag. Writing "-writemostly" clears the writemostly flag. + Writing "blocked" sets the "blocked" flag. + Writing "-blocked" clear the "blocked" flag and allows writes + to complete. + + This file responds to select/poll. Any change to 'faulty' + or 'blocked' causes an event. errors An approximate count of read errors that have been detected on diff --git a/drivers/md/md.c b/drivers/md/md.c index 60d4cad88c20..dc99d95a1b6d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1886,6 +1886,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len) err = 0; } + if (!err) + sysfs_notify(&rdev->kobj, NULL, "state"); return err ? err : len; } static struct rdev_sysfs_entry rdev_state = @@ -1979,7 +1981,8 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) if (err) { rdev->raid_disk = -1; return err; - } + } else + sysfs_notify(&rdev->kobj, NULL, "state"); sprintf(nm, "rd%d", rdev->raid_disk); if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm)) printk(KERN_WARNING @@ -1996,6 +1999,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) clear_bit(Faulty, &rdev->flags); clear_bit(WriteMostly, &rdev->flags); set_bit(In_sync, &rdev->flags); + sysfs_notify(&rdev->kobj, NULL, "state"); } return len; } @@ -3525,6 +3529,7 @@ static int do_md_run(mddev_t * mddev) return -EINVAL; } } + sysfs_notify(&rdev->kobj, NULL, "state"); } md_probe(mddev->unit, NULL, NULL); @@ -4256,6 +4261,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) } if (err) export_rdev(rdev); + else + sysfs_notify(&rdev->kobj, NULL, "state"); md_update_sb(mddev, 1); if (mddev->degraded) @@ -5115,6 +5122,7 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) mddev->pers->error_handler(mddev,rdev); if (mddev->degraded) set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); + set_bit(StateChanged, &rdev->flags); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -6037,6 +6045,10 @@ void md_check_recovery(mddev_t *mddev) if (mddev->flags) md_update_sb(mddev, 0); + rdev_for_each(rdev, rtmp, mddev) + if (test_and_clear_bit(StateChanged, &rdev->flags)) + sysfs_notify(&rdev->kobj, NULL, "state"); + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) { diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 62aa9c9a6ddc..df30c4395875 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -87,6 +87,9 @@ struct mdk_rdev_s #define Blocked 8 /* An error occured on an externally * managed array, don't allow writes * until it is cleared */ +#define StateChanged 9 /* Faulty or Blocked has changed during + * interrupt, so it needs to be + * notified by the thread */ wait_queue_head_t blocked_wait; int desc_nr; /* descriptor index in the superblock */ -- cgit v1.2.3 From b203886edbcaac3ca427cf4dbcb50b18bdb346fd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:50 +1000 Subject: md: kill STRIPE_OP_MOD_DMA in raid5 offload From: Dan Williams This micro-optimization allowed the raid code to skip a re-read of the parity block after checking parity. It took advantage of the fact that xor-offload-engines have their own internal result buffer and can check parity without writing to memory. Remove it for the following reasons: 1/ It is a layering violation for MD to need to manage the DMA and non-DMA paths within async_xor_zero_sum 2/ Bad precedent to toggle the 'ops' flags outside the lock 3/ Hard to realize a performance gain as reads will not need an updated parity block and writes will dirty it anyways. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 10 ---------- include/linux/raid/raid5.h | 2 -- 2 files changed, 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8c4e6149daea..60e61d2464b5 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -837,15 +837,10 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, static void ops_complete_check(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; - int pd_idx = sh->pd_idx; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - if (test_and_clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending) && - sh->ops.zero_sum_result == 0) - set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); @@ -873,11 +868,6 @@ static void ops_run_check(struct stripe_head *sh) tx = async_xor_zero_sum(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &sh->ops.zero_sum_result, 0, NULL, NULL, NULL); - if (tx) - set_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - else - clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - atomic_inc(&sh->count); tx = async_trigger_callback(ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, tx, ops_complete_check, sh); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index f0827d31ae6f..4ecae31a3dcb 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -267,10 +267,8 @@ struct r6_state { /* modifiers to the base operations * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back - * STRIPE_OP_MOD_DMA_CHECK - parity is not corrupted by the check */ #define STRIPE_OP_MOD_REPAIR_PD 7 -#define STRIPE_OP_MOD_DMA_CHECK 8 /* * Plugging: -- cgit v1.2.3 From 2b7497f0e0a0b9cf21d822e427d5399b2056501a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:52 +1000 Subject: md: kill STRIPE_OP_IO flag From: Dan Williams The R5_Want{Read,Write} flags already gate i/o. So, this flag is superfluous and we can unconditionally call ops_run_io(). Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 32 +++++--------------------------- include/linux/raid/raid5.h | 1 - 2 files changed, 5 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 60e61d2464b5..cac97080b278 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -373,8 +373,6 @@ static unsigned long get_stripe_work(struct stripe_head *sh) test_and_ack_op(STRIPE_OP_BIODRAIN, pending); test_and_ack_op(STRIPE_OP_POSTXOR, pending); test_and_ack_op(STRIPE_OP_CHECK, pending); - if (test_and_clear_bit(STRIPE_OP_IO, &sh->ops.pending)) - ack++; sh->ops.count -= ack; if (unlikely(sh->ops.count < 0)) { @@ -399,7 +397,6 @@ static void ops_run_io(struct stripe_head *sh) might_sleep(); - set_bit(STRIPE_IO_STARTED, &sh->state); for (i = disks; i--; ) { int rw; struct bio *bi; @@ -433,6 +430,8 @@ static void ops_run_io(struct stripe_head *sh) test_bit(STRIPE_EXPAND_READY, &sh->state)) md_sync_acct(rdev->bdev, STRIPE_SECTORS); + set_bit(STRIPE_IO_STARTED, &sh->state); + bi->bi_bdev = rdev->bdev; pr_debug("%s: for %llu schedule op %ld on disc %d\n", __func__, (unsigned long long)sh->sector, @@ -900,9 +899,6 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) if (test_bit(STRIPE_OP_CHECK, &pending)) ops_run_check(sh); - if (test_bit(STRIPE_OP_IO, &pending)) - ops_run_io(sh); - if (overlap_clear) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -2013,8 +2009,6 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, */ set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; pr_debug("Reading block %d (sync=%d)\n", disk_idx, s->syncing); @@ -2208,9 +2202,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for r-m-w\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2234,9 +2225,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for Reconstruct\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2444,8 +2432,6 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; clear_bit(STRIPE_DEGRADED, &sh->state); s->locked++; @@ -2801,9 +2787,6 @@ static void handle_stripe5(struct stripe_head *sh) (i == sh->pd_idx || dev->written)) { pr_debug("Writing block %d\n", i); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; if (prexor) continue; if (!test_bit(R5_Insync, &dev->flags) || @@ -2857,16 +2840,12 @@ static void handle_stripe5(struct stripe_head *sh) dev = &sh->dev[s.failed_num]; if (!test_bit(R5_ReWrite, &dev->flags)) { set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_ReWrite, &dev->flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; } else { /* let's read it back */ set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_LOCKED, &dev->flags); s.locked++; } @@ -2884,13 +2863,10 @@ static void handle_stripe5(struct stripe_head *sh) clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - for (i = conf->raid_disks; i--; ) { + for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; - } } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && @@ -2926,6 +2902,8 @@ static void handle_stripe5(struct stripe_head *sh) if (pending) raid5_run_ops(sh, pending); + ops_run_io(sh); + return_io(return_bi); } diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 4ecae31a3dcb..1301195abf4b 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -263,7 +263,6 @@ struct r6_state { #define STRIPE_OP_BIODRAIN 3 #define STRIPE_OP_POSTXOR 4 #define STRIPE_OP_CHECK 5 -#define STRIPE_OP_IO 6 /* modifiers to the base operations * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back -- cgit v1.2.3 From ecc65c9b3f9b9d740a5deade3d85b39be56401b6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:57 +1000 Subject: md: replace STRIPE_OP_CHECK with 'check_states' From: Dan Williams The STRIPE_OP_* flags record the state of stripe operations which are performed outside the stripe lock. Their use in indicating which operations need to be run is straightforward; however, interpolating what the next state of the stripe should be based on a given combination of these flags is not straightforward, and has led to bugs. An easier to read implementation with minimal degrees of freedom is needed. Towards this goal, this patch introduces explicit states to replace what was previously interpolated from the STRIPE_OP_* flags. For now this only converts the handle_parity_checks5 path, removing a user of the ops.{pending,ack,complete,count} fields of struct stripe_operations. This conversion also found a remaining issue with the current code. There is a small window for a drive to fail between when we schedule a repair and when the parity calculation for that repair completes. When this happens we will writeback to 'failed_num' when we really want to write back to 'pd_idx'. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 172 ++++++++++++++++++++++----------------------- include/linux/raid/raid5.h | 46 ++++++++++-- 2 files changed, 123 insertions(+), 95 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6f3dd12dd3a4..544e1600f208 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -605,7 +605,11 @@ static void ops_complete_compute5(void *stripe_head_ref) set_bit(R5_UPTODATE, &tgt->flags); BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); clear_bit(R5_Wantcompute, &tgt->flags); - set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); + clear_bit(STRIPE_COMPUTE_RUN, &sh->state); + if (sh->check_state == check_state_compute_run) + sh->check_state = check_state_compute_result; + else + set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -838,7 +842,7 @@ static void ops_complete_check(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); + sh->check_state = check_state_check_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -870,7 +874,8 @@ static void ops_run_check(struct stripe_head *sh) ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) +static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, + unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; @@ -880,7 +885,8 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) overlap_clear++; } - if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending)) + if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending) || + test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) tx = ops_run_compute5(sh, pending); if (test_bit(STRIPE_OP_PREXOR, &pending)) @@ -894,7 +900,7 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) if (test_bit(STRIPE_OP_POSTXOR, &pending)) ops_run_postxor(sh, tx, pending); - if (test_bit(STRIPE_OP_CHECK, &pending)) + if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); if (overlap_clear) @@ -1961,8 +1967,7 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, /* don't schedule compute operations or reads on the parity block while * a check is in flight */ - if ((disk_idx == sh->pd_idx) && - test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) + if (disk_idx == sh->pd_idx && sh->check_state) return ~0; /* is the data in this block needed, and can we get it? */ @@ -1983,9 +1988,8 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, * 3/ We hold off parity block re-reads until check operations * have quiesced. */ - if ((s->uptodate == disks - 1) && - (s->failed && disk_idx == s->failed_num) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { + if ((s->uptodate == disks - 1) && !sh->check_state && + (s->failed && disk_idx == s->failed_num)) { set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); set_bit(R5_Wantcompute, &dev->flags); sh->ops.target = disk_idx; @@ -2021,12 +2025,8 @@ static void handle_issuing_new_read_requests5(struct stripe_head *sh, { int i; - /* Clear completed compute operations. Parity recovery - * (STRIPE_OP_MOD_REPAIR_PD) implies a write-back which is handled - * later on in this routine - */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { + /* Clear completed compute operations */ + if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete)) { clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); @@ -2350,90 +2350,85 @@ static void handle_issuing_new_write_requests6(raid5_conf_t *conf, static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, int disks) { - int canceled_check = 0; + struct r5dev *dev = NULL; set_bit(STRIPE_HANDLE, &sh->state); - /* complete a check operation */ - if (test_and_clear_bit(STRIPE_OP_CHECK, &sh->ops.complete)) { - clear_bit(STRIPE_OP_CHECK, &sh->ops.ack); - clear_bit(STRIPE_OP_CHECK, &sh->ops.pending); + switch (sh->check_state) { + case check_state_idle: + /* start a new check operation if there are no failures */ if (s->failed == 0) { - if (sh->ops.zero_sum_result == 0) - /* parity is correct (on disc, - * not in buffer any more) - */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - conf->mddev->resync_mismatches += - STRIPE_SECTORS; - if (test_bit( - MD_RECOVERY_CHECK, &conf->mddev->recovery)) - /* don't try to repair!! */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - set_bit(STRIPE_OP_COMPUTE_BLK, - &sh->ops.pending); - set_bit(STRIPE_OP_MOD_REPAIR_PD, - &sh->ops.pending); - set_bit(R5_Wantcompute, - &sh->dev[sh->pd_idx].flags); - sh->ops.target = sh->pd_idx; - sh->ops.count++; - s->uptodate++; - } - } - } else - canceled_check = 1; /* STRIPE_INSYNC is not set */ - } - - /* start a new check operation if there are no failures, the stripe is - * not insync, and a repair is not in flight - */ - if (s->failed == 0 && - !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - if (!test_and_set_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { BUG_ON(s->uptodate != disks); + sh->check_state = check_state_run; + set_bit(STRIPE_OP_CHECK, &s->ops_request); clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); - sh->ops.count++; s->uptodate--; + break; } - } - - /* check if we can clear a parity disk reconstruct */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - - clear_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); - } - + dev = &sh->dev[s->failed_num]; + /* fall through */ + case check_state_compute_result: + sh->check_state = check_state_idle; + if (!dev) + dev = &sh->dev[sh->pd_idx]; + + /* check that a write has not made the stripe insync */ + if (test_bit(STRIPE_INSYNC, &sh->state)) + break; - /* Wait for check parity and compute block operations to complete - * before write-back. If a failure occurred while the check operation - * was in flight we need to cycle this stripe through handle_stripe - * since the parity block may not be uptodate - */ - if (!canceled_check && !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending) && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) { - struct r5dev *dev; /* either failed parity check, or recovery is happening */ - if (s->failed == 0) - s->failed_num = sh->pd_idx; - dev = &sh->dev[s->failed_num]; BUG_ON(!test_bit(R5_UPTODATE, &dev->flags)); BUG_ON(s->uptodate != disks); set_bit(R5_LOCKED, &dev->flags); + s->locked++; set_bit(R5_Wantwrite, &dev->flags); clear_bit(STRIPE_DEGRADED, &sh->state); - s->locked++; set_bit(STRIPE_INSYNC, &sh->state); + break; + case check_state_run: + break; /* we will be called again upon completion */ + case check_state_check_result: + sh->check_state = check_state_idle; + + /* if a failure occurred during the check operation, leave + * STRIPE_INSYNC not set and let the stripe be handled again + */ + if (s->failed) + break; + + /* handle a successful check operation, if parity is correct + * we are done. Otherwise update the mismatch count and repair + * parity if !MD_RECOVERY_CHECK + */ + if (sh->ops.zero_sum_result == 0) + /* parity is correct (on disc, + * not in buffer any more) + */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + conf->mddev->resync_mismatches += STRIPE_SECTORS; + if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) + /* don't try to repair!! */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + sh->check_state = check_state_compute_run; + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + set_bit(R5_Wantcompute, + &sh->dev[sh->pd_idx].flags); + sh->ops.target = sh->pd_idx; + s->uptodate++; + } + } + break; + case check_state_compute_run: + break; + default: + printk(KERN_ERR "%s: unknown check_state: %d sector: %llu\n", + __func__, sh->check_state, + (unsigned long long) sh->sector); + BUG(); } } @@ -2807,7 +2802,7 @@ static void handle_stripe5(struct stripe_head *sh) * block. */ if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) + !sh->check_state) handle_issuing_new_write_requests5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe @@ -2815,11 +2810,10 @@ static void handle_stripe5(struct stripe_head *sh) * data is available. The parity check is held off while parity * dependent operations are in flight. */ - if ((s.syncing && s.locked == 0 && + if (sh->check_state || + (s.syncing && s.locked == 0 && !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending) && - !test_bit(STRIPE_INSYNC, &sh->state)) || - test_bit(STRIPE_OP_CHECK, &sh->ops.pending) || - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) + !test_bit(STRIPE_INSYNC, &sh->state))) handle_parity_checks5(conf, sh, &s, disks); if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) { @@ -2897,8 +2891,8 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending) - raid5_run_ops(sh, pending); + if (pending || s.ops_request) + raid5_run_ops(sh, pending, s.ops_request); ops_run_io(sh, &s); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 1301195abf4b..2c96d5fd54bf 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -158,6 +158,41 @@ * the compute block completes. */ +/* + * Operations state - intermediate states that are visible outside of sh->lock + * In general _idle indicates nothing is running, _run indicates a data + * processing operation is active, and _result means the data processing result + * is stable and can be acted upon. For simple operations like biofill and + * compute that only have an _idle and _run state they are indicated with + * sh->state flags (STRIPE_BIOFILL_RUN and STRIPE_COMPUTE_RUN) + */ +/** + * enum check_states - handles syncing / repairing a stripe + * @check_state_idle - check operations are quiesced + * @check_state_run - check operation is running + * @check_state_result - set outside lock when check result is valid + * @check_state_compute_run - check failed and we are repairing + * @check_state_compute_result - set outside lock when compute result is valid + */ +enum check_states { + check_state_idle = 0, + check_state_run, /* parity check */ + check_state_check_result, + check_state_compute_run, /* parity repair */ + check_state_compute_result, +}; + +/** + * enum reconstruct_states - handles writing or expanding a stripe + */ +enum reconstruct_states { + reconstruct_state_idle = 0, + reconstruct_state_drain_run, /* write */ + reconstruct_state_run, /* expand */ + reconstruct_state_drain_result, + reconstruct_state_result, +}; + struct stripe_head { struct hlist_node hash; struct list_head lru; /* inactive_list or handle_list */ @@ -169,6 +204,7 @@ struct stripe_head { spinlock_t lock; int bm_seq; /* sequence number for bitmap flushes */ int disks; /* disks in stripe */ + enum check_states check_state; /* stripe_operations * @pending - pending ops flags (set for request->issue->complete) * @ack - submitted ops flags (set for issue->complete) @@ -202,6 +238,7 @@ struct stripe_head_state { int locked, uptodate, to_read, to_write, failed, written; int to_fill, compute, req_compute, non_overwrite; int failed_num; + unsigned long ops_request; }; /* r6_state - extra state data only relevant to r6 */ @@ -254,8 +291,10 @@ struct r6_state { #define STRIPE_EXPAND_READY 11 #define STRIPE_IO_STARTED 12 /* do not count towards 'bypass_count' */ #define STRIPE_FULL_WRITE 13 /* all blocks are set to be overwritten */ +#define STRIPE_BIOFILL_RUN 14 +#define STRIPE_COMPUTE_RUN 15 /* - * Operations flags (in issue order) + * Operation request flags */ #define STRIPE_OP_BIOFILL 0 #define STRIPE_OP_COMPUTE_BLK 1 @@ -264,11 +303,6 @@ struct r6_state { #define STRIPE_OP_POSTXOR 4 #define STRIPE_OP_CHECK 5 -/* modifiers to the base operations - * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back - */ -#define STRIPE_OP_MOD_REPAIR_PD 7 - /* * Plugging: * -- cgit v1.2.3 From 600aa10993012ff2dd5617720dac081e4f992017 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:32:05 +1000 Subject: md: replace STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} with 'reconstruct_states' From: Dan Williams Track the state of reconstruct operations (recalculating the parity block usually due to incoming writes, or as part of array expansion) Reduces the scope of the STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} flags to only tracking whether a reconstruct operation has been requested via the ops_request field of struct stripe_head_state. This is the final step in the removal of ops.{pending,ack,complete,count}, i.e. the STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} flags only request an operation and do not track the state of the operation. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 204 ++++++++++++++------------------------------- include/linux/raid/raid5.h | 9 +- 2 files changed, 63 insertions(+), 150 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 835046bf384e..b9159367491a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -122,6 +122,13 @@ static void return_io(struct bio *return_bi) static void print_raid5_conf (raid5_conf_t *conf); +static int stripe_operations_active(struct stripe_head *sh) +{ + return sh->check_state || sh->reconstruct_state || + test_bit(STRIPE_BIOFILL_RUN, &sh->state) || + test_bit(STRIPE_COMPUTE_RUN, &sh->state); +} + static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) { if (atomic_dec_and_test(&sh->count)) { @@ -141,7 +148,7 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) } md_wakeup_thread(conf->mddev->thread); } else { - BUG_ON(sh->ops.pending); + BUG_ON(stripe_operations_active(sh)); if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) @@ -243,7 +250,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int BUG_ON(atomic_read(&sh->count) != 0); BUG_ON(test_bit(STRIPE_HANDLE, &sh->state)); - BUG_ON(sh->ops.pending || sh->ops.ack || sh->ops.complete); + BUG_ON(stripe_operations_active(sh)); CHECK_DEVLOCK(); pr_debug("init_stripe called, stripe %llu\n", @@ -344,47 +351,6 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector return sh; } -/* test_and_ack_op() ensures that we only dequeue an operation once */ -#define test_and_ack_op(op, pend) \ -do { \ - if (test_bit(op, &sh->ops.pending) && \ - !test_bit(op, &sh->ops.complete)) { \ - if (test_and_set_bit(op, &sh->ops.ack)) \ - clear_bit(op, &pend); \ - else \ - ack++; \ - } else \ - clear_bit(op, &pend); \ -} while (0) - -/* find new work to run, do not resubmit work that is already - * in flight - */ -static unsigned long get_stripe_work(struct stripe_head *sh) -{ - unsigned long pending; - int ack = 0; - - pending = sh->ops.pending; - - test_and_ack_op(STRIPE_OP_BIOFILL, pending); - test_and_ack_op(STRIPE_OP_COMPUTE_BLK, pending); - test_and_ack_op(STRIPE_OP_PREXOR, pending); - test_and_ack_op(STRIPE_OP_BIODRAIN, pending); - test_and_ack_op(STRIPE_OP_POSTXOR, pending); - test_and_ack_op(STRIPE_OP_CHECK, pending); - - sh->ops.count -= ack; - if (unlikely(sh->ops.count < 0)) { - printk(KERN_ERR "pending: %#lx ops.pending: %#lx ops.ack: %#lx " - "ops.complete: %#lx\n", pending, sh->ops.pending, - sh->ops.ack, sh->ops.complete); - BUG(); - } - - return pending; -} - static void raid5_end_read_request(struct bio *bi, int error); static void @@ -609,7 +575,7 @@ static void ops_complete_compute5(void *stripe_head_ref) } static struct dma_async_tx_descriptor * -ops_run_compute5(struct stripe_head *sh, unsigned long pending) +ops_run_compute5(struct stripe_head *sh, unsigned long ops_request) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -640,7 +606,7 @@ ops_run_compute5(struct stripe_head *sh, unsigned long pending) ops_complete_compute5, sh); /* ack now if postxor is not set to be run */ - if (tx && !test_bit(STRIPE_OP_POSTXOR, &pending)) + if (tx && !test_bit(STRIPE_OP_POSTXOR, &ops_request)) async_tx_ack(tx); return tx; @@ -652,8 +618,6 @@ static void ops_complete_prexor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - - set_bit(STRIPE_OP_PREXOR, &sh->ops.complete); } static struct dma_async_tx_descriptor * @@ -686,7 +650,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) static struct dma_async_tx_descriptor * ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) + unsigned long ops_request) { int disks = sh->disks; int pd_idx = sh->pd_idx, i; @@ -694,7 +658,7 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (Wantprexor) */ - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -744,7 +708,7 @@ static void ops_complete_postxor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); + sh->reconstruct_state = reconstruct_state_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -763,16 +727,14 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - + sh->reconstruct_state = reconstruct_state_drain_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) + unsigned long ops_request) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -780,7 +742,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); unsigned long flags; dma_async_tx_callback callback; @@ -807,7 +769,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &pending) ? + callback = test_bit(STRIPE_OP_BIODRAIN, &ops_request) ? ops_complete_write : ops_complete_postxor; /* 1/ if we prexor'd then the dest is reused as a source @@ -868,8 +830,7 @@ static void ops_run_check(struct stripe_head *sh) ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, - unsigned long ops_request) +static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; @@ -880,18 +841,18 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, } if (test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) - tx = ops_run_compute5(sh, pending); + tx = ops_run_compute5(sh, ops_request); - if (test_bit(STRIPE_OP_PREXOR, &pending)) + if (test_bit(STRIPE_OP_PREXOR, &ops_request)) tx = ops_run_prexor(sh, tx); - if (test_bit(STRIPE_OP_BIODRAIN, &pending)) { - tx = ops_run_biodrain(sh, tx, pending); + if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { + tx = ops_run_biodrain(sh, tx, ops_request); overlap_clear++; } - if (test_bit(STRIPE_OP_POSTXOR, &pending)) - ops_run_postxor(sh, tx, pending); + if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) + ops_run_postxor(sh, tx, ops_request); if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); @@ -1684,11 +1645,11 @@ static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) } } -static int -handle_write_operations5(struct stripe_head *sh, int rcw, int expand) +static void +handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, + int rcw, int expand) { int i, pd_idx = sh->pd_idx, disks = sh->disks; - int locked = 0; if (rcw) { /* if we are not expanding this is a proper write request, and @@ -1696,12 +1657,12 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) * stripe cache */ if (!expand) { - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - sh->ops.count++; - } + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + } else + sh->reconstruct_state = reconstruct_state_run; - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - sh->ops.count++; + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1710,21 +1671,20 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) set_bit(R5_LOCKED, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } - if (locked + 1 == disks) + if (s->locked + 1 == disks) if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state)) atomic_inc(&sh->raid_conf->pending_full_writes); } else { BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - set_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - - sh->ops.count += 3; + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_PREXOR, &s->ops_request); + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1742,7 +1702,7 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) set_bit(R5_Wantprexor, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } } @@ -1752,13 +1712,11 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) */ set_bit(R5_LOCKED, &sh->dev[pd_idx].flags); clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - locked++; + s->locked++; - pr_debug("%s: stripe %llu locked: %d pending: %lx\n", + pr_debug("%s: stripe %llu locked: %d ops_request: %lx\n", __func__, (unsigned long long)sh->sector, - locked, sh->ops.pending); - - return locked; + s->locked, s->ops_request); } /* @@ -2005,8 +1963,7 @@ static void handle_issuing_new_read_requests5(struct stripe_head *sh, * midst of changing due to a write */ if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state && - !test_bit(STRIPE_OP_PREXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { for (i = disks; i--; ) if (__handle_issuing_new_read_requests5( sh, s, i, disks) == 0) @@ -2211,7 +2168,7 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) && (s->locked == 0 && (rcw == 0 || rmw == 0) && !test_bit(STRIPE_BIT_DELAY, &sh->state))) - s->locked += handle_write_operations5(sh, rcw == 0, 0); + handle_write_operations5(sh, s, rcw == 0, 0); } static void handle_issuing_new_write_requests6(raid5_conf_t *conf, @@ -2581,15 +2538,14 @@ static void handle_stripe5(struct stripe_head *sh) struct bio *return_bi = NULL; struct stripe_head_state s; struct r5dev *dev; - unsigned long pending = 0; mdk_rdev_t *blocked_rdev = NULL; int prexor; memset(&s, 0, sizeof(s)); - pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d " - "ops=%lx:%lx:%lx\n", (unsigned long long)sh->sector, sh->state, - atomic_read(&sh->count), sh->pd_idx, - sh->ops.pending, sh->ops.ack, sh->ops.complete); + pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d " + "reconstruct:%d\n", (unsigned long long)sh->sector, sh->state, + atomic_read(&sh->count), sh->pd_idx, sh->check_state, + sh->reconstruct_state); spin_lock(&sh->lock); clear_bit(STRIPE_HANDLE, &sh->state); @@ -2703,34 +2659,12 @@ static void handle_stripe5(struct stripe_head *sh) /* Now we check to see if any write operations have recently * completed */ - - /* leave prexor set until postxor is done, allows us to distinguish - * a rmw from a rcw during biodrain - */ prexor = 0; - if (test_bit(STRIPE_OP_PREXOR, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - prexor = 1; - clear_bit(STRIPE_OP_PREXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - + if (sh->reconstruct_state == reconstruct_state_drain_result) { + sh->reconstruct_state = reconstruct_state_idle; for (i = disks; i--; ) - clear_bit(R5_Wantprexor, &sh->dev[i].flags); - } - - /* if only POSTXOR is set then this is an 'expand' postxor */ - if (test_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.ack); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); + prexor += test_and_clear_bit(R5_Wantprexor, + &sh->dev[i].flags); /* All the 'written' buffers and the parity block are ready to * be written back to disk @@ -2763,8 +2697,7 @@ static void handle_stripe5(struct stripe_head *sh) * 2/ A 'check' operation is in flight, as it may clobber the parity * block. */ - if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !sh->check_state) + if (s.to_write && !sh->reconstruct_state && !sh->check_state) handle_issuing_new_write_requests5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe @@ -2805,18 +2738,10 @@ static void handle_stripe5(struct stripe_head *sh) } } - /* Finish postxor operations initiated by the expansion - * process - */ - if (test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete) && - !test_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending)) { - + /* Finish reconstruct operations initiated by the expansion process */ + if (sh->reconstruct_state == reconstruct_state_result) { + sh->reconstruct_state = reconstruct_state_idle; clear_bit(STRIPE_EXPANDING, &sh->state); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); @@ -2824,15 +2749,13 @@ static void handle_stripe5(struct stripe_head *sh) } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { /* Need to write out all blocks after computing parity */ sh->disks = conf->raid_disks; sh->pd_idx = stripe_to_pdidx(sh->sector, conf, conf->raid_disks); - s.locked += handle_write_operations5(sh, 1, 1); - } else if (s.expanded && - s.locked == 0 && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + handle_write_operations5(sh, &s, 1, 1); + } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); wake_up(&conf->wait_for_overlap); @@ -2843,9 +2766,6 @@ static void handle_stripe5(struct stripe_head *sh) !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) handle_stripe_expansion(conf, sh, NULL); - if (sh->ops.count) - pending = get_stripe_work(sh); - unlock: spin_unlock(&sh->lock); @@ -2853,8 +2773,8 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending || s.ops_request) - raid5_run_ops(sh, pending, s.ops_request); + if (s.ops_request) + raid5_run_ops(sh, s.ops_request); ops_run_io(sh, &s); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 2c96d5fd54bf..5f3e674b87dd 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -205,19 +205,12 @@ struct stripe_head { int bm_seq; /* sequence number for bitmap flushes */ int disks; /* disks in stripe */ enum check_states check_state; + enum reconstruct_states reconstruct_state; /* stripe_operations - * @pending - pending ops flags (set for request->issue->complete) - * @ack - submitted ops flags (set for issue->complete) - * @complete - completed ops flags (set for complete) * @target - STRIPE_OP_COMPUTE_BLK target - * @count - raid5_runs_ops is set to run when this is non-zero */ struct stripe_operations { - unsigned long pending; - unsigned long ack; - unsigned long complete; int target; - int count; u32 zero_sum_result; } ops; struct r5dev { -- cgit v1.2.3 From d8ee0728b5b30d7a6f62c399a95e953616d31f23 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:32:06 +1000 Subject: md: replace R5_WantPrexor with R5_WantDrain, add 'prexor' reconstruct_states From: Dan Williams Currently ops_run_biodrain and other locations have extra logic to determine which blocks are processed in the prexor and non-prexor cases. This can be eliminated if handle_write_operations5 flags the blocks to be processed in all cases via R5_Wantdrain. The presence of the prexor operation is tracked in sh->reconstruct_state. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 89 +++++++++++++++------------------------------- include/linux/raid/raid5.h | 6 ++-- 2 files changed, 32 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index b9159367491a..c71246061c0e 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -637,7 +637,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; /* Only process blocks that are known to be uptodate */ - if (dev->towrite && test_bit(R5_Wantprexor, &dev->flags)) + if (test_bit(R5_Wantdrain, &dev->flags)) xor_srcs[count++] = dev->page; } @@ -649,16 +649,10 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) } static struct dma_async_tx_descriptor * -ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long ops_request) +ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { int disks = sh->disks; - int pd_idx = sh->pd_idx, i; - - /* check if prexor is active which means only process blocks - * that are part of a read-modify-write (Wantprexor) - */ - int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); + int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -666,20 +660,8 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; struct bio *chosen; - int towrite; - towrite = 0; - if (prexor) { /* rmw */ - if (dev->towrite && - test_bit(R5_Wantprexor, &dev->flags)) - towrite = 1; - } else { /* rcw */ - if (i != pd_idx && dev->towrite && - test_bit(R5_LOCKED, &dev->flags)) - towrite = 1; - } - - if (towrite) { + if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) { struct bio *wbi; spin_lock(&sh->lock); @@ -702,18 +684,6 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } static void ops_complete_postxor(void *stripe_head_ref) -{ - struct stripe_head *sh = stripe_head_ref; - - pr_debug("%s: stripe %llu\n", __func__, - (unsigned long long)sh->sector); - - sh->reconstruct_state = reconstruct_state_result; - set_bit(STRIPE_HANDLE, &sh->state); - release_stripe(sh); -} - -static void ops_complete_write(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; int disks = sh->disks, i, pd_idx = sh->pd_idx; @@ -727,14 +697,21 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - sh->reconstruct_state = reconstruct_state_drain_result; + if (sh->reconstruct_state == reconstruct_state_drain_run) + sh->reconstruct_state = reconstruct_state_drain_result; + else if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) + sh->reconstruct_state = reconstruct_state_prexor_drain_result; + else { + BUG_ON(sh->reconstruct_state != reconstruct_state_run); + sh->reconstruct_state = reconstruct_state_result; + } + set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void -ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long ops_request) +ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -742,9 +719,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); + int prexor = 0; unsigned long flags; - dma_async_tx_callback callback; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -752,7 +728,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (written) */ - if (prexor) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) { + prexor = 1; xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -768,10 +745,6 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } } - /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &ops_request) ? - ops_complete_write : ops_complete_postxor; - /* 1/ if we prexor'd then the dest is reused as a source * 2/ if we did not prexor then we are redoing the parity * set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST @@ -785,10 +758,10 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, if (unlikely(count == 1)) { flags &= ~(ASYNC_TX_XOR_DROP_DST | ASYNC_TX_XOR_ZERO_DST); tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } else tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } static void ops_complete_check(void *stripe_head_ref) @@ -847,12 +820,12 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) tx = ops_run_prexor(sh, tx); if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { - tx = ops_run_biodrain(sh, tx, ops_request); + tx = ops_run_biodrain(sh, tx); overlap_clear++; } if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) - ops_run_postxor(sh, tx, ops_request); + ops_run_postxor(sh, tx); if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); @@ -1669,6 +1642,7 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, if (dev->towrite) { set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_Wantdrain, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); s->locked++; @@ -1681,7 +1655,7 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - sh->reconstruct_state = reconstruct_state_drain_run; + sh->reconstruct_state = reconstruct_state_prexor_drain_run; set_bit(STRIPE_OP_PREXOR, &s->ops_request); set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); set_bit(STRIPE_OP_POSTXOR, &s->ops_request); @@ -1691,15 +1665,10 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, if (i == pd_idx) continue; - /* For a read-modify write there may be blocks that are - * locked for reading while others are ready to be - * written so we distinguish these blocks by the - * R5_Wantprexor bit - */ if (dev->towrite && (test_bit(R5_UPTODATE, &dev->flags) || - test_bit(R5_Wantcompute, &dev->flags))) { - set_bit(R5_Wantprexor, &dev->flags); + test_bit(R5_Wantcompute, &dev->flags))) { + set_bit(R5_Wantdrain, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); s->locked++; @@ -2660,11 +2629,11 @@ static void handle_stripe5(struct stripe_head *sh) * completed */ prexor = 0; - if (sh->reconstruct_state == reconstruct_state_drain_result) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_result) + prexor = 1; + if (sh->reconstruct_state == reconstruct_state_drain_result || + sh->reconstruct_state == reconstruct_state_prexor_drain_result) { sh->reconstruct_state = reconstruct_state_idle; - for (i = disks; i--; ) - prexor += test_and_clear_bit(R5_Wantprexor, - &sh->dev[i].flags); /* All the 'written' buffers and the parity block are ready to * be written back to disk diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 5f3e674b87dd..3b2672792457 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -187,8 +187,10 @@ enum check_states { */ enum reconstruct_states { reconstruct_state_idle = 0, + reconstruct_state_prexor_drain_run, /* prexor-write */ reconstruct_state_drain_run, /* write */ reconstruct_state_run, /* expand */ + reconstruct_state_prexor_drain_result, reconstruct_state_drain_result, reconstruct_state_result, }; @@ -258,9 +260,7 @@ struct r6_state { #define R5_Wantfill 12 /* dev->toread contains a bio that needs * filling */ -#define R5_Wantprexor 13 /* distinguish blocks ready for rmw from - * other "towrites" - */ +#define R5_Wantdrain 13 /* dev->towrite needs to be drained */ /* * Write method */ -- cgit v1.2.3 From c88e6f51c2154c7606f7e281bcca2d1a2c89d7b2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 27 Jun 2008 19:54:54 -0700 Subject: include/linux/netdevice.h: don't export MAX_HEADER to userspace Due to the CONFIG_'s the value is anyway not correct in userspace. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f27fd2009334..25f87102ab66 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -88,6 +88,8 @@ struct wireless_dev; #define NETDEV_TX_BUSY 1 /* driver tx path was busy*/ #define NETDEV_TX_LOCKED -1 /* driver tx lock was already taken */ +#ifdef __KERNEL__ + /* * Compute the worst case header length according to the protocols * used. @@ -114,6 +116,8 @@ struct wireless_dev; #define MAX_HEADER (LL_MAX_HEADER + 48) #endif +#endif /* __KERNEL__ */ + struct net_device_subqueue { /* Give a control state for each queue. This struct may contain -- cgit v1.2.3 From 251a4b320f2352598f84e4452ab538aa8064af52 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Fri, 27 Jun 2008 20:09:00 -0700 Subject: net/inet_lro: remove setting skb->ip_summed when not LRO-able When an SKB cannot be chained to a session, the current code attempts to "restore" its ip_summed field from lro_mgr->ip_summed. However, lro_mgr->ip_summed does not hold the original value; in fact, we'd better not touch skb->ip_summed since it is not modified by the code in the path leading to a failure to chain it. Also use a cleaer comment to the describe the ip_summed field of struct net_lro_mgr. Issue raised by Or Gerlitz Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- include/linux/inet_lro.h | 6 +++++- net/ipv4/inet_lro.c | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/inet_lro.h b/include/linux/inet_lro.h index 80335b7d77c5..c4335faebb63 100644 --- a/include/linux/inet_lro.h +++ b/include/linux/inet_lro.h @@ -84,7 +84,11 @@ struct net_lro_mgr { from received packets and eth protocol is still ETH_P_8021Q */ - u32 ip_summed; /* Set in non generated SKBs in page mode */ + /* + * Set for generated SKBs that are not added to + * the frag list in fragmented mode + */ + u32 ip_summed; u32 ip_summed_aggr; /* Set in aggregated SKBs: CHECKSUM_UNNECESSARY * or CHECKSUM_NONE */ diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c index 4a4d49fca1f2..cfd034a2b96e 100644 --- a/net/ipv4/inet_lro.c +++ b/net/ipv4/inet_lro.c @@ -383,8 +383,7 @@ static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, out2: /* send aggregated SKBs to stack */ lro_flush(lro_mgr, lro_desc); -out: /* Original SKB has to be posted to stack */ - skb->ip_summed = lro_mgr->ip_summed; +out: return 1; } -- cgit v1.2.3 From 818727badc14ce57dc099a075b05505d50b7956e Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Wed, 18 Jun 2008 15:40:12 +0300 Subject: rndis_host: pass buffer length to rndis_command Pass buffer length to rndis_command so that rndis_command can read full response buffer from device instead of max CONTROL_BUFFER_SIZE bytes. Signed-off-by: Jussi Kivilinna Signed-off-by: Jeff Garzik --- drivers/net/usb/rndis_host.c | 14 +++++++------- drivers/net/wireless/rndis_wlan.c | 4 ++-- include/linux/usb/rndis_host.h | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index ae467f182c40..61c98beb4d17 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -74,7 +74,7 @@ EXPORT_SYMBOL_GPL(rndis_status); * Call context is likely probe(), before interface name is known, * which is why we won't try to use it in the diagnostics. */ -int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) +int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) { struct cdc_state *info = (void *) &dev->data; int master_ifnum; @@ -121,7 +121,7 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, master_ifnum, - buf, CONTROL_BUFFER_SIZE, + buf, buflen, RNDIS_CONTROL_TIMEOUT_MS); if (likely(retval >= 8)) { msg_len = le32_to_cpu(buf->msg_len); @@ -239,7 +239,7 @@ static int rndis_query(struct usbnet *dev, struct usb_interface *intf, u.get->len = cpu_to_le32(in_len); u.get->offset = ccpu2(20); - retval = rndis_command(dev, u.header); + retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", oid, retval); @@ -328,7 +328,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); net->change_mtu = NULL; - retval = rndis_command(dev, u.header); + retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); @@ -409,7 +409,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) u.set->offset = ccpu2((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER; - retval = rndis_command(dev, u.header); + retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto halt_fail_and_release; @@ -424,7 +424,7 @@ halt_fail_and_release: memset(u.halt, 0, sizeof *u.halt); u.halt->msg_type = RNDIS_MSG_HALT; u.halt->msg_len = ccpu2(sizeof *u.halt); - (void) rndis_command(dev, (void *)u.halt); + (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); @@ -449,7 +449,7 @@ void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) if (halt) { halt->msg_type = RNDIS_MSG_HALT; halt->msg_len = ccpu2(sizeof *halt); - (void) rndis_command(dev, (void *)halt); + (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE); kfree(halt); } diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index a36d2c85e26e..f001f2afd05e 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -448,7 +448,7 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) u.get->msg_len = ccpu2(sizeof *u.get); u.get->oid = oid; - ret = rndis_command(dev, u.header); + ret = rndis_command(dev, u.header, buflen); if (ret == 0) { ret = le32_to_cpu(u.get_c->len); *len = (*len > ret) ? ret : *len; @@ -498,7 +498,7 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) u.set->handle = ccpu2(0); memcpy(u.buf + sizeof(*u.set), data, len); - ret = rndis_command(dev, u.header); + ret = rndis_command(dev, u.header, buflen); if (ret == 0) ret = rndis_error_status(u.set_c->status); diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h index 29d6458ecb8d..0a6e6d4b929a 100644 --- a/include/linux/usb/rndis_host.h +++ b/include/linux/usb/rndis_host.h @@ -260,7 +260,8 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */ extern void rndis_status(struct usbnet *dev, struct urb *urb); -extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf); +extern int +rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen); extern int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags); extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf); -- cgit v1.2.3 From 4bbff7e408a54cce88d26191191e8bcda2a60d55 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 26 Jun 2008 09:13:48 -0400 Subject: Input: add KEY_MEDIA_REPEAT definition This patch adds the Repeat key to the input layer. The usage in the HUT is 0xBC (listed under "15.7 Transport Controls"). Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index e075c4b762fb..d150c57e5f0a 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -534,8 +534,8 @@ struct input_absinfo { #define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ #define KEY_FRAMEFORWARD 0x1b5 - #define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ #define KEY_DEL_EOL 0x1c0 #define KEY_DEL_EOS 0x1c1 -- cgit v1.2.3 From 3cadd2d98972f806165c634553ac4918b2b7920c Mon Sep 17 00:00:00 2001 From: Richard Lemon Date: Thu, 26 Jun 2008 10:10:41 -0400 Subject: Input: Add driver for iNexio serial touchscreen. Signed-off-by: Richard Lemon Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 +++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/inexio.c | 207 +++++++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 221 insertions(+) create mode 100644 drivers/input/touchscreen/inexio.c (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 030a89734855..4085791e7be2 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -103,6 +103,18 @@ config TOUCHSCREEN_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouch. +config TOUCHSCREEN_INEXIO + tristate "iNexio serial touchscreens" + select SERIO + help + Say Y here if you have an iNexio serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called inexio. + config TOUCHSCREEN_MK712 tristate "ICS MicroClock MK712 touchscreen" help diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 64a11f443dce..d63bcdcd28ec 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c new file mode 100644 index 000000000000..192ade0a0fb9 --- /dev/null +++ b/drivers/input/touchscreen/inexio.c @@ -0,0 +1,207 @@ +/* + * iNexio serial touchscreen driver + * + * Copyright (c) 2008 Richard Lemon + * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman + * + */ + +/* + * 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. + */ + +/* + * 2008/06/19 Richard Lemon + * Copied mtouch.c and edited for iNexio protocol + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "iNexio serial touchscreen driver" + +MODULE_AUTHOR("Richard Lemon "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define INEXIO_FORMAT_TOUCH_BIT 0x01 +#define INEXIO_FORMAT_LENGTH 5 +#define INEXIO_RESPONSE_BEGIN_BYTE 0x80 + +/* todo: check specs for max length of all responses */ +#define INEXIO_MAX_LENGTH 16 + +#define INEXIO_MIN_XC 0 +#define INEXIO_MAX_XC 0x3fff +#define INEXIO_MIN_YC 0 +#define INEXIO_MAX_YC 0x3fff + +#define INEXIO_GET_XC(data) (((data[1])<<7) | data[2]) +#define INEXIO_GET_YC(data) (((data[3])<<7) | data[4]) +#define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct inexio { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[INEXIO_MAX_LENGTH]; + char phys[32]; +}; + +static void inexio_process_data(struct inexio *pinexio) +{ + struct input_dev *dev = pinexio->dev; + + if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) { + input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data)); + input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data)); + input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data)); + input_sync(dev); + + pinexio->idx = 0; + } +} + +static irqreturn_t inexio_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct inexio* pinexio = serio_get_drvdata(serio); + + pinexio->data[pinexio->idx] = data; + + if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0]) + inexio_process_data(pinexio); + else + printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]); + + return IRQ_HANDLED; +} + +/* + * inexio_disconnect() is the opposite of inexio_connect() + */ + +static void inexio_disconnect(struct serio *serio) +{ + struct inexio* pinexio = serio_get_drvdata(serio); + + input_get_device(pinexio->dev); + input_unregister_device(pinexio->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pinexio->dev); + kfree(pinexio); +} + +/* + * inexio_connect() is the routine that is called when someone adds a + * new serio device that supports iNexio protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int inexio_connect(struct serio *serio, struct serio_driver *drv) +{ + struct inexio *pinexio; + struct input_dev *input_dev; + int err; + + pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pinexio || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pinexio->serio = serio; + pinexio->dev = input_dev; + snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys); + + input_dev->name = "iNexio Serial TouchScreen"; + input_dev->phys = pinexio->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_INEXIO; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0); + input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0); + + serio_set_drvdata(serio, pinexio); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pinexio->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pinexio); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id inexio_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_INEXIO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, inexio_serio_ids); + +static struct serio_driver inexio_drv = { + .driver = { + .name = "inexio", + }, + .description = DRIVER_DESC, + .id_table = inexio_serio_ids, + .interrupt = inexio_interrupt, + .connect = inexio_connect, + .disconnect = inexio_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init inexio_init(void) +{ + return serio_register_driver(&inexio_drv); +} + +static void __exit inexio_exit(void) +{ + serio_unregister_driver(&inexio_drv); +} + +module_init(inexio_init); +module_exit(inexio_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 95674d97dabd..36c6ab81c7e0 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -212,5 +212,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_TAOSEVM 0x34 #define SERIO_FUJITSU 0x35 #define SERIO_ZHENHUA 0x36 +#define SERIO_INEXIO 0x37 #endif -- cgit v1.2.3 From 80be038593dba7aa46fb24a14f0ba83e5ade0edb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 30 Jun 2008 11:35:53 -0700 Subject: PCI: add stub for pci_set_consistent_dma_mask() When CONFIG_PCI=n, there is no stub for pci_set_consistent_dma_mask(), so add one like other similar stubs. Otherwise there can be build errors, as here: linux-next-20080630/drivers/ssb/main.c:1175: error: implicit declaration of function 'pci_set_consistent_dma_mask' Signed-off-by: Randy Dunlap Signed-off-by: Jesse Barnes --- include/linux/pci.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 76c9a4a96152..96ebaa8d80e3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -853,6 +853,11 @@ static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask) return -EIO; } +static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) +{ + return -EIO; +} + static inline int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size) { -- cgit v1.2.3 From b5470dc5fc18a8ff6517c3bb538d1479e58ecb02 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 27 Jun 2008 21:44:04 -0700 Subject: md: resolve external metadata handling deadlock in md_allow_write md_allow_write() marks the metadata dirty while holding mddev->lock and then waits for the write to complete. For externally managed metadata this causes a deadlock as userspace needs to take the lock to communicate that the metadata update has completed. Change md_allow_write() in the 'external' case to start the 'mark active' operation and then return -EAGAIN. The expected side effects while waiting for userspace to write 'active' to 'array_state' are holding off reshape (code currently handles -ENOMEM), cause some 'stripe_cache_size' change requests to fail, cause some GET_BITMAP_FILE ioctl requests to fall back to GFP_NOIO, and cause updates to 'raid_disks' to fail. Except for 'stripe_cache_size' changes these failures can be mitigated by coordinating with mdmon. md_write_start() still prevents writes from occurring until the metadata handler has had a chance to take action as it unconditionally waits for MD_CHANGE_CLEAN to be cleared. [neilb@suse.de: return -EAGAIN, try GFP_NOIO] Signed-off-by: Dan Williams --- drivers/md/md.c | 27 ++++++++++++++++----------- drivers/md/raid1.c | 6 ++++-- drivers/md/raid5.c | 12 +++++++++--- include/linux/raid/md.h | 2 +- 4 files changed, 30 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index df1230af02cd..43d033d9a05a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4172,9 +4172,11 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg) char *ptr, *buf = NULL; int err = -ENOMEM; - md_allow_write(mddev); + if (md_allow_write(mddev)) + file = kmalloc(sizeof(*file), GFP_NOIO); + else + file = kmalloc(sizeof(*file), GFP_KERNEL); - file = kmalloc(sizeof(*file), GFP_KERNEL); if (!file) goto out; @@ -5667,15 +5669,18 @@ void md_write_end(mddev_t *mddev) * may proceed without blocking. It is important to call this before * attempting a GFP_KERNEL allocation while holding the mddev lock. * Must be called with mddev_lock held. + * + * In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock + * is dropped, so return -EAGAIN after notifying userspace. */ -void md_allow_write(mddev_t *mddev) +int md_allow_write(mddev_t *mddev) { if (!mddev->pers) - return; + return 0; if (mddev->ro) - return; + return 0; if (!mddev->pers->sync_request) - return; + return 0; spin_lock_irq(&mddev->write_lock); if (mddev->in_sync) { @@ -5686,14 +5691,14 @@ void md_allow_write(mddev_t *mddev) mddev->safemode = 1; spin_unlock_irq(&mddev->write_lock); md_update_sb(mddev, 0); - sysfs_notify(&mddev->kobj, NULL, "array_state"); - /* wait for the dirty state to be recorded in the metadata */ - wait_event(mddev->sb_wait, - !test_bit(MD_CHANGE_CLEAN, &mddev->flags) && - !test_bit(MD_CHANGE_PENDING, &mddev->flags)); } else spin_unlock_irq(&mddev->write_lock); + + if (test_bit(MD_CHANGE_CLEAN, &mddev->flags)) + return -EAGAIN; + else + return 0; } EXPORT_SYMBOL_GPL(md_allow_write); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index f05d5983efb6..491dc2d4ad5f 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2136,7 +2136,7 @@ static int raid1_reshape(mddev_t *mddev) conf_t *conf = mddev_to_conf(mddev); int cnt, raid_disks; unsigned long flags; - int d, d2; + int d, d2, err; /* Cannot change chunk_size, layout, or level */ if (mddev->chunk_size != mddev->new_chunk || @@ -2148,7 +2148,9 @@ static int raid1_reshape(mddev_t *mddev) return -EINVAL; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; raid_disks = mddev->raid_disks + mddev->delta_disks; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 442622067cae..8f4c70a53210 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -911,14 +911,16 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) struct stripe_head *osh, *nsh; LIST_HEAD(newstripes); struct disk_info *ndisks; - int err = 0; + int err; struct kmem_cache *sc; int i; if (newsize <= conf->pool_size) return 0; /* never bother to shrink */ - md_allow_write(conf->mddev); + err = md_allow_write(conf->mddev); + if (err) + return err; /* Step 1 */ sc = kmem_cache_create(conf->cache_name[1-conf->active_name], @@ -3843,6 +3845,8 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) { raid5_conf_t *conf = mddev_to_conf(mddev); unsigned long new; + int err; + if (len >= PAGE_SIZE) return -EINVAL; if (!conf) @@ -3858,7 +3862,9 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) else break; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; while (new > conf->max_nr_stripes) { if (grow_one_stripe(conf)) conf->max_nr_stripes++; diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index b7386ae9d288..dc0e3fcb9f28 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -95,7 +95,7 @@ extern int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct page *page, int rw); extern void md_do_sync(mddev_t *mddev); extern void md_new_event(mddev_t *mddev); -extern void md_allow_write(mddev_t *mddev); +extern int md_allow_write(mddev_t *mddev); extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev); #endif /* CONFIG_MD */ -- cgit v1.2.3 From 9b4a8dd2e9f8af206eb39e3d99c442b0d6158953 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 24 Jun 2008 03:46:57 +1000 Subject: drivers/macintosh: Various cleanups This contains the following cleanups: - make the following needlessly global code static: - adb.c: adb_controller - adb.c: adb_init() - adbhid.c: adb_to_linux_keycodes[] (also make it const) - via-pmu68k.c: backlight_level - via-pmu68k.c: backlight_enabled - remove the following unused code: - via-pmu68k.c: sleep_notifier_list Signed-off-by: Adrian Bunk Acked-by: Geert Uytterhoeven Acked-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- drivers/macintosh/adb.c | 5 ++--- drivers/macintosh/adbhid.c | 2 +- drivers/macintosh/via-pmu68k.c | 5 ++--- include/linux/adb.h | 1 - 4 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index dbaad39020a1..61b62a6f681b 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -46,7 +46,6 @@ #endif -EXPORT_SYMBOL(adb_controller); EXPORT_SYMBOL(adb_client_list); extern struct adb_driver via_macii_driver; @@ -80,7 +79,7 @@ static struct adb_driver *adb_driver_list[] = { static struct class *adb_dev_class; -struct adb_driver *adb_controller; +static struct adb_driver *adb_controller; BLOCKING_NOTIFIER_HEAD(adb_client_list); static int adb_got_sleep; static int adb_inited; @@ -290,7 +289,7 @@ static int adb_resume(struct platform_device *dev) } #endif /* CONFIG_PM */ -int __init adb_init(void) +static int __init adb_init(void) { struct adb_driver *driver; int i; diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c index ef4c117ea35f..59ea520a5d7a 100644 --- a/drivers/macintosh/adbhid.c +++ b/drivers/macintosh/adbhid.c @@ -75,7 +75,7 @@ static struct notifier_block adbhid_adb_notifier = { #define ADB_KEY_POWER_OLD 0x7e #define ADB_KEY_POWER 0x7f -u16 adb_to_linux_keycodes[128] = { +static const u16 adb_to_linux_keycodes[128] = { /* 0x00 */ KEY_A, /* 30 */ /* 0x01 */ KEY_S, /* 31 */ /* 0x02 */ KEY_D, /* 32 */ diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c index e2f84da09e7c..b64741c95ac4 100644 --- a/drivers/macintosh/via-pmu68k.c +++ b/drivers/macintosh/via-pmu68k.c @@ -101,7 +101,6 @@ static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited; int asleep; -BLOCKING_NOTIFIER_HEAD(sleep_notifier_list); static int pmu_probe(void); static int pmu_init(void); @@ -741,8 +740,8 @@ pmu_handle_data(unsigned char *data, int len) } } -int backlight_level = -1; -int backlight_enabled = 0; +static int backlight_level = -1; +static int backlight_enabled = 0; #define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1)) diff --git a/include/linux/adb.h b/include/linux/adb.h index 64d8878e1444..63bca502fa55 100644 --- a/include/linux/adb.h +++ b/include/linux/adb.h @@ -84,7 +84,6 @@ enum adb_message { ADB_MSG_PRE_RESET, /* Called before resetting the bus */ ADB_MSG_POST_RESET /* Called after resetting the bus (re-do init & register) */ }; -extern struct adb_driver *adb_controller; extern struct blocking_notifier_head adb_client_list; int adb_request(struct adb_request *req, void (*done)(struct adb_request *), -- cgit v1.2.3 From f3e909c2750eb20536bacacc867dc9047b70546a Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Tue, 1 Jul 2008 14:01:39 +1000 Subject: powerpc: Update for VSX core file and ptrace This correctly hooks the VSX dump into Roland McGrath core file infrastructure. It adds the VSX dump information as an additional elf note in the core file (after talking more to the tool chain/gdb guys). This also ensures the formats are consistent between signals, ptrace and core files. Signed-off-by: Michael Neuling Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/process.c | 19 +------------------ arch/powerpc/kernel/ptrace.c | 21 +++++++++++++-------- include/asm-powerpc/elf.h | 4 ++-- include/linux/elf.h | 1 + 4 files changed, 17 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index d52ded366f14..1924b57bd241 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -162,7 +162,7 @@ void flush_altivec_to_thread(struct task_struct *tsk) } } -int dump_task_altivec(struct task_struct *tsk, elf_vrreg_t *vrregs) +int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs) { /* ELF_NVRREG includes the VSCR and VRSAVE which we need to save * separately, see below */ @@ -249,23 +249,6 @@ int dump_task_vsx(struct task_struct *tsk, elf_vrreg_t *vrregs) } #endif /* CONFIG_VSX */ -int dump_task_vector(struct task_struct *tsk, elf_vrregset_t *vrregs) -{ - int rc = 0; - elf_vrreg_t *regs = (elf_vrreg_t *)vrregs; -#ifdef CONFIG_ALTIVEC - rc = dump_task_altivec(tsk, regs); - if (rc) - return rc; - regs += ELF_NVRREG; -#endif - -#ifdef CONFIG_VSX - rc = dump_task_vsx(tsk, regs); -#endif - return rc; -} - #ifdef CONFIG_SPE void enable_kernel_spe(void) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 4e203a89e189..8feb93e7890c 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -368,13 +368,15 @@ static int vsr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { - int ret; + double buf[32]; + int ret, i; flush_vsx_to_thread(target); + for (i = 0; i < 32 ; i++) + buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - target->thread.fpr, 0, - 32 * sizeof(vector128)); + buf, 0, 32 * sizeof(double)); return ret; } @@ -383,13 +385,16 @@ static int vsr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { - int ret; + double buf[32]; + int ret,i; flush_vsx_to_thread(target); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - target->thread.fpr, 0, - 32 * sizeof(vector128)); + buf, 0, 32 * sizeof(double)); + for (i = 0; i < 32 ; i++) + current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; + return ret; } @@ -499,8 +504,8 @@ static const struct user_regset native_regsets[] = { #endif #ifdef CONFIG_VSX [REGSET_VSX] = { - .n = 32, - .size = sizeof(vector128), .align = sizeof(vector128), + .core_note_type = NT_PPC_VSX, .n = 32, + .size = sizeof(double), .align = sizeof(double), .active = vsr_active, .get = vsr_get, .set = vsr_set }, #endif diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h index b6a874db801d..38a51728406f 100644 --- a/include/asm-powerpc/elf.h +++ b/include/asm-powerpc/elf.h @@ -221,8 +221,8 @@ extern int dump_task_fpu(struct task_struct *, elf_fpregset_t *); typedef elf_vrregset_t elf_fpxregset_t; #ifdef CONFIG_ALTIVEC -extern int dump_task_vector(struct task_struct *, elf_vrregset_t *vrregs); -#define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_vector(tsk, regs) +extern int dump_task_altivec(struct task_struct *, elf_vrregset_t *vrregs); +#define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_altivec(tsk, regs) #define ELF_CORE_XFPREG_TYPE NT_PPC_VMX #endif diff --git a/include/linux/elf.h b/include/linux/elf.h index ff9fbed90123..edc3dac3f02f 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -358,6 +358,7 @@ typedef struct elf64_shdr { #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ -- cgit v1.2.3 From 18ce3751ccd488c78d3827e9f6bf54e6322676fb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 1 Jul 2008 09:07:34 +0200 Subject: Properly notify block layer of sync writes fsync_buffers_list() and sync_dirty_buffer() both issue async writes and then immediately wait on them. Conceptually, that makes them sync writes and we should treat them as such so that the IO schedulers can handle them appropriately. This patch fixes a write starvation issue that Lin Ming reported, where xx is stuck for more than 2 minutes because of a large number of synchronous IO in the system: INFO: task kjournald:20558 blocked for more than 120 seconds. "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kjournald D ffff810010820978 6712 20558 2 ffff81022ddb1d10 0000000000000046 ffff81022e7baa10 ffffffff803ba6f2 ffff81022ecd0000 ffff8101e6dc9160 ffff81022ecd0348 000000008048b6cb 0000000000000086 ffff81022c4e8d30 0000000000000000 ffffffff80247537 Call Trace: [] kobject_get+0x12/0x17 [] getnstimeofday+0x2f/0x83 [] sync_buffer+0x0/0x3f [] io_schedule+0x5d/0x9f [] sync_buffer+0x3b/0x3f [] __wait_on_bit+0x40/0x6f [] sync_buffer+0x0/0x3f [] out_of_line_wait_on_bit+0x6c/0x78 [] wake_bit_function+0x0/0x23 [] sync_dirty_buffer+0x98/0xcb [] journal_commit_transaction+0x97d/0xcb6 [] lock_timer_base+0x26/0x4b [] kjournald+0xc1/0x1fb [] autoremove_wake_function+0x0/0x2e [] kjournald+0x0/0x1fb [] kthread+0x47/0x74 [] schedule_tail+0x28/0x5d [] child_rip+0xa/0x12 [] kthread+0x0/0x74 [] child_rip+0x0/0x12 Lin Ming confirms that this patch fixes the issue. I've run tests with it for the past week and no ill effects have been observed, so I'm proposing it for inclusion into 2.6.26. Signed-off-by: Jens Axboe --- fs/buffer.c | 13 ++++++++----- include/linux/fs.h | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index a073f3f4f013..0f51c0f7c266 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -821,7 +821,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) * contents - it is a noop if I/O is still in * flight on potentially older contents. */ - ll_rw_block(SWRITE, 1, &bh); + ll_rw_block(SWRITE_SYNC, 1, &bh); brelse(bh); spin_lock(lock); } @@ -2940,16 +2940,19 @@ void ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) for (i = 0; i < nr; i++) { struct buffer_head *bh = bhs[i]; - if (rw == SWRITE) + if (rw == SWRITE || rw == SWRITE_SYNC) lock_buffer(bh); else if (test_set_buffer_locked(bh)) continue; - if (rw == WRITE || rw == SWRITE) { + if (rw == WRITE || rw == SWRITE || rw == SWRITE_SYNC) { if (test_clear_buffer_dirty(bh)) { bh->b_end_io = end_buffer_write_sync; get_bh(bh); - submit_bh(WRITE, bh); + if (rw == SWRITE_SYNC) + submit_bh(WRITE_SYNC, bh); + else + submit_bh(WRITE, bh); continue; } } else { @@ -2978,7 +2981,7 @@ int sync_dirty_buffer(struct buffer_head *bh) if (test_clear_buffer_dirty(bh)) { get_bh(bh); bh->b_end_io = end_buffer_write_sync; - ret = submit_bh(WRITE, bh); + ret = submit_bh(WRITE_SYNC, bh); wait_on_buffer(bh); if (buffer_eopnotsupp(bh)) { clear_buffer_eopnotsupp(bh); diff --git a/include/linux/fs.h b/include/linux/fs.h index 7c1080826832..d8e2762ed14d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -83,6 +83,7 @@ extern int dir_notify_enable; #define READ_SYNC (READ | (1 << BIO_RW_SYNC)) #define READ_META (READ | (1 << BIO_RW_META)) #define WRITE_SYNC (WRITE | (1 << BIO_RW_SYNC)) +#define SWRITE_SYNC (SWRITE | (1 << BIO_RW_SYNC)) #define WRITE_BARRIER ((1 << BIO_RW) | (1 << BIO_RW_BARRIER)) #define SEL_IN 1 -- cgit v1.2.3 From 8e29da9ee8958cc17e27f4053420f1c982614793 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Jul 2008 22:38:18 +0200 Subject: i2c: Fix bad hint about irqs in i2c.h i2c.h mentions -1 as a not-issued irq. This false hint was taken by of_i2c and caused crashes. Don't give any advice as 'no irq' is not consistent across all architectures yet and it is not needed internally by the i2c-core. Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare --- include/linux/i2c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index fb9af6a0fe9c..8dc730132192 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -171,7 +171,7 @@ struct i2c_client { struct i2c_adapter *adapter; /* the adapter we sit on */ struct i2c_driver *driver; /* and our access routines */ struct device dev; /* the device structure */ - int irq; /* irq issued by device (or -1) */ + int irq; /* irq issued by device */ struct list_head list; /* DEPRECATED */ struct completion released; }; -- cgit v1.2.3 From 0853ad66b14feb12acde7ac13b7c3b75770a0adc Mon Sep 17 00:00:00 2001 From: Santwona Behera Date: Wed, 2 Jul 2008 03:47:41 -0700 Subject: netdev: Add support for rx flow hash configuration, using ethtool. Added new interfaces to ethtool to configure receive network flow distribution across multiple rx rings using hashing. Signed-off-by: Santwona Behera Signed-off-by: David S. Miller --- include/linux/ethtool.h | 33 +++++++++++++++++++++++++++++++++ net/core/ethtool.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index c8d216357865..8bb5e87df365 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -272,6 +272,12 @@ enum ethtool_flags { ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ }; +struct ethtool_rxnfc { + __u32 cmd; + __u32 flow_type; + __u64 data; +}; + #ifdef __KERNEL__ struct net_device; @@ -396,6 +402,8 @@ struct ethtool_ops { /* the following hooks are obsolete */ int (*self_test_count)(struct net_device *);/* use get_sset_count */ int (*get_stats_count)(struct net_device *);/* use get_sset_count */ + int (*get_rxhash)(struct net_device *, struct ethtool_rxnfc *); + int (*set_rxhash)(struct net_device *, struct ethtool_rxnfc *); }; #endif /* __KERNEL__ */ @@ -442,6 +450,9 @@ struct ethtool_ops { #define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ #define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ +#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ +#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ + /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET @@ -528,4 +539,26 @@ struct ethtool_ops { #define WAKE_MAGIC (1 << 5) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ +/* L3-L4 network traffic flow types */ +#define TCP_V4_FLOW 0x01 +#define UDP_V4_FLOW 0x02 +#define SCTP_V4_FLOW 0x03 +#define AH_ESP_V4_FLOW 0x04 +#define TCP_V6_FLOW 0x05 +#define UDP_V6_FLOW 0x06 +#define SCTP_V6_FLOW 0x07 +#define AH_ESP_V6_FLOW 0x08 + +/* L3-L4 network traffic flow hash options */ +#define RXH_DEV_PORT (1 << 0) +#define RXH_L2DA (1 << 1) +#define RXH_VLAN (1 << 2) +#define RXH_L3_PROTO (1 << 3) +#define RXH_IP_SRC (1 << 4) +#define RXH_IP_DST (1 << 5) +#define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ +#define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_DISCARD (1 << 31) + + #endif /* _LINUX_ETHTOOL_H */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 0133b5ebd545..14ada537f895 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -209,6 +209,36 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) return 0; } +static int ethtool_set_rxhash(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_rxnfc cmd; + + if (!dev->ethtool_ops->set_rxhash) + return -EOPNOTSUPP; + + if (copy_from_user(&cmd, useraddr, sizeof(cmd))) + return -EFAULT; + + return dev->ethtool_ops->set_rxhash(dev, &cmd); +} + +static int ethtool_get_rxhash(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_rxnfc info; + + if (!dev->ethtool_ops->get_rxhash) + return -EOPNOTSUPP; + + if (copy_from_user(&info, useraddr, sizeof(info))) + return -EFAULT; + + dev->ethtool_ops->get_rxhash(dev, &info); + + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; +} + static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) { struct ethtool_regs regs; @@ -826,6 +856,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GGSO: case ETHTOOL_GFLAGS: case ETHTOOL_GPFLAGS: + case ETHTOOL_GRXFH: break; default: if (!capable(CAP_NET_ADMIN)) @@ -977,6 +1008,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) rc = ethtool_set_value(dev, useraddr, dev->ethtool_ops->set_priv_flags); break; + case ETHTOOL_GRXFH: + rc = ethtool_get_rxhash(dev, useraddr); + break; + case ETHTOOL_SRXFH: + rc = ethtool_set_rxhash(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.2.3 From ab96dddbedf4bb8a7a0fe44012efc1d99598c36f Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 28 May 2008 13:54:04 -0500 Subject: svcrdma: Add a type for keeping NFS RPC mapping Create a new data structure to hold the remote client address space to local server address space mapping. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 27 +++++++++++++++++++++++++++ net/sunrpc/xprtrdma/svc_rdma.c | 19 +++++++++++++++++++ net/sunrpc/xprtrdma/svc_rdma_transport.c | 26 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 05eb4664d0dd..bd8749cc8084 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -86,6 +86,31 @@ struct svc_rdma_op_ctxt { struct page *pages[RPCSVC_MAXPAGES]; }; +/* + * NFS_ requests are mapped on the client side by the chunk lists in + * the RPCRDMA header. During the fetching of the RPC from the client + * and the writing of the reply to the client, the memory in the + * client and the memory in the server must be mapped as contiguous + * vaddr/len for access by the hardware. These data strucures keep + * these mappings. + * + * For an RDMA_WRITE, the 'sge' maps the RPC REPLY. For RDMA_READ, the + * 'sge' in the svc_rdma_req_map maps the server side RPC reply and the + * 'ch' field maps the read-list of the RPCRDMA header to the 'sge' + * mapping of the reply. + */ +struct svc_rdma_chunk_sge { + int start; /* sge no for this chunk */ + int count; /* sge count for this chunk */ +}; +struct svc_rdma_req_map { + unsigned long count; + union { + struct kvec sge[RPCSVC_MAXPAGES]; + struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES]; + }; +}; + #define RDMACTXT_F_LAST_CTXT 2 struct svcxprt_rdma { @@ -173,6 +198,8 @@ extern int svc_rdma_post_recv(struct svcxprt_rdma *); extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *); extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); +extern struct svc_rdma_req_map *svc_rdma_get_req_map(void); +extern void svc_rdma_put_req_map(struct svc_rdma_req_map *); extern void svc_sq_reap(struct svcxprt_rdma *); extern void svc_rq_reap(struct svcxprt_rdma *); extern struct svc_xprt_class svc_rdma_class; diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index 88c0ca20bb1e..171f2053e90c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma.c +++ b/net/sunrpc/xprtrdma/svc_rdma.c @@ -69,6 +69,9 @@ atomic_t rdma_stat_rq_prod; atomic_t rdma_stat_sq_poll; atomic_t rdma_stat_sq_prod; +/* Temporary NFS request map cache */ +struct kmem_cache *svc_rdma_map_cachep; + /* * This function implements reading and resetting an atomic_t stat * variable through read/write to a proc file. Any write to the file @@ -241,6 +244,7 @@ void svc_rdma_cleanup(void) svcrdma_table_header = NULL; } svc_unreg_xprt_class(&svc_rdma_class); + kmem_cache_destroy(svc_rdma_map_cachep); } int svc_rdma_init(void) @@ -255,9 +259,24 @@ int svc_rdma_init(void) svcrdma_table_header = register_sysctl_table(svcrdma_root_table); + /* Create the temporary map cache */ + svc_rdma_map_cachep = kmem_cache_create("svc_rdma_map_cache", + sizeof(struct svc_rdma_req_map), + 0, + SLAB_HWCACHE_ALIGN, + NULL); + if (!svc_rdma_map_cachep) { + printk(KERN_INFO "Could not allocate map cache.\n"); + goto err; + } + /* Register RDMA with the SVC transport switch */ svc_reg_xprt_class(&svc_rdma_class); return 0; + + err: + unregister_sysctl_table(svcrdma_table_header); + return -ENOMEM; } MODULE_AUTHOR("Tom Tucker "); MODULE_DESCRIPTION("SVC RDMA Transport"); diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index e132509d1db0..ae90758d8e9b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -173,6 +173,32 @@ void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages) atomic_dec(&xprt->sc_ctxt_used); } +/* Temporary NFS request map cache. Created in svc_rdma.c */ +extern struct kmem_cache *svc_rdma_map_cachep; + +/* + * Temporary NFS req mappings are shared across all transport + * instances. These are short lived and should be bounded by the number + * of concurrent server threads * depth of the SQ. + */ +struct svc_rdma_req_map *svc_rdma_get_req_map(void) +{ + struct svc_rdma_req_map *map; + while (1) { + map = kmem_cache_alloc(svc_rdma_map_cachep, GFP_KERNEL); + if (map) + break; + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + } + map->count = 0; + return map; +} + +void svc_rdma_put_req_map(struct svc_rdma_req_map *map) +{ + kmem_cache_free(svc_rdma_map_cachep, map); +} + /* ib_cq event handler */ static void cq_event_handler(struct ib_event *event, void *context) { -- cgit v1.2.3 From f820c57ebf5493d4602cc00577c8b0fadd27a7b8 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Tue, 27 May 2008 17:03:14 -0500 Subject: svcrdma: Use reply and chunk map for RDMA_READ processing Modify the RDMA_READ processing to use the reply and chunk list mapping data types. Also add a special purpose 'hdr_count' field in in the context to hold the header page count instead of overloading the SGE length field and corrupting the DMA map length. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 83 +++++++++++++++------------------ 2 files changed, 39 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index bd8749cc8084..fd5e8a1c17de 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -72,6 +72,7 @@ extern atomic_t rdma_stat_sq_prod; */ struct svc_rdma_op_ctxt { struct svc_rdma_op_ctxt *read_hdr; + int hdr_count; struct list_head free_list; struct xdr_buf arg; struct list_head dto_q; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 06ab4841537b..d25971b42a74 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -112,11 +112,6 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, rqstp->rq_arg.tail[0].iov_len = 0; } -struct chunk_sge { - int start; /* sge no for this chunk */ - int count; /* sge count for this chunk */ -}; - /* Encode a read-chunk-list as an array of IB SGE * * Assumptions: @@ -134,8 +129,8 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, struct svc_rdma_op_ctxt *head, struct rpcrdma_msg *rmsgp, - struct ib_sge *sge, - struct chunk_sge *ch_sge_ary, + struct svc_rdma_req_map *rpl_map, + struct svc_rdma_req_map *chl_map, int ch_count, int byte_count) { @@ -156,22 +151,18 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, head->arg.head[0] = rqstp->rq_arg.head[0]; head->arg.tail[0] = rqstp->rq_arg.tail[0]; head->arg.pages = &head->pages[head->count]; - head->sge[0].length = head->count; /* save count of hdr pages */ + head->hdr_count = head->count; /* save count of hdr pages */ head->arg.page_base = 0; head->arg.page_len = ch_bytes; head->arg.len = rqstp->rq_arg.len + ch_bytes; head->arg.buflen = rqstp->rq_arg.buflen + ch_bytes; head->count++; - ch_sge_ary[0].start = 0; + chl_map->ch[0].start = 0; while (byte_count) { + rpl_map->sge[sge_no].iov_base = + page_address(rqstp->rq_arg.pages[page_no]) + page_off; sge_bytes = min_t(int, PAGE_SIZE-page_off, ch_bytes); - sge[sge_no].addr = - ib_dma_map_page(xprt->sc_cm_id->device, - rqstp->rq_arg.pages[page_no], - page_off, sge_bytes, - DMA_FROM_DEVICE); - sge[sge_no].length = sge_bytes; - sge[sge_no].lkey = xprt->sc_phys_mr->lkey; + rpl_map->sge[sge_no].iov_len = sge_bytes; /* * Don't bump head->count here because the same page * may be used by multiple SGE. @@ -187,11 +178,11 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, * SGE, move to the next SGE */ if (ch_bytes == 0) { - ch_sge_ary[ch_no].count = - sge_no - ch_sge_ary[ch_no].start; + chl_map->ch[ch_no].count = + sge_no - chl_map->ch[ch_no].start; ch_no++; ch++; - ch_sge_ary[ch_no].start = sge_no; + chl_map->ch[ch_no].start = sge_no; ch_bytes = ch->rc_target.rs_length; /* If bytes remaining account for next chunk */ if (byte_count) { @@ -220,18 +211,24 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, return sge_no; } -static void rdma_set_ctxt_sge(struct svc_rdma_op_ctxt *ctxt, - struct ib_sge *sge, +static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, + struct svc_rdma_op_ctxt *ctxt, + struct kvec *vec, u64 *sgl_offset, int count) { int i; ctxt->count = count; + ctxt->direction = DMA_FROM_DEVICE; for (i = 0; i < count; i++) { - ctxt->sge[i].addr = sge[i].addr; - ctxt->sge[i].length = sge[i].length; - *sgl_offset = *sgl_offset + sge[i].length; + ctxt->sge[i].addr = + ib_dma_map_single(xprt->sc_cm_id->device, + vec[i].iov_base, vec[i].iov_len, + DMA_FROM_DEVICE); + ctxt->sge[i].length = vec[i].iov_len; + ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey; + *sgl_offset = *sgl_offset + vec[i].iov_len; } } @@ -282,34 +279,29 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, struct ib_send_wr read_wr; int err = 0; int ch_no; - struct ib_sge *sge; int ch_count; int byte_count; int sge_count; u64 sgl_offset; struct rpcrdma_read_chunk *ch; struct svc_rdma_op_ctxt *ctxt = NULL; - struct svc_rdma_op_ctxt *tmp_sge_ctxt; - struct svc_rdma_op_ctxt *tmp_ch_ctxt; - struct chunk_sge *ch_sge_ary; + struct svc_rdma_req_map *rpl_map; + struct svc_rdma_req_map *chl_map; /* If no read list is present, return 0 */ ch = svc_rdma_get_read_chunk(rmsgp); if (!ch) return 0; - /* Allocate temporary contexts to keep SGE */ - BUG_ON(sizeof(struct ib_sge) < sizeof(struct chunk_sge)); - tmp_sge_ctxt = svc_rdma_get_context(xprt); - sge = tmp_sge_ctxt->sge; - tmp_ch_ctxt = svc_rdma_get_context(xprt); - ch_sge_ary = (struct chunk_sge *)tmp_ch_ctxt->sge; + /* Allocate temporary reply and chunk maps */ + rpl_map = svc_rdma_get_req_map(); + chl_map = svc_rdma_get_req_map(); svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count); if (ch_count > RPCSVC_MAXPAGES) return -EINVAL; sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp, - sge, ch_sge_ary, + rpl_map, chl_map, ch_count, byte_count); sgl_offset = 0; ch_no = 0; @@ -331,14 +323,15 @@ next_sge: read_wr.wr.rdma.remote_addr = get_unaligned(&(ch->rc_target.rs_offset)) + sgl_offset; - read_wr.sg_list = &sge[ch_sge_ary[ch_no].start]; + read_wr.sg_list = ctxt->sge; read_wr.num_sge = - rdma_read_max_sge(xprt, ch_sge_ary[ch_no].count); - rdma_set_ctxt_sge(ctxt, &sge[ch_sge_ary[ch_no].start], + rdma_read_max_sge(xprt, chl_map->ch[ch_no].count); + rdma_set_ctxt_sge(xprt, ctxt, + &rpl_map->sge[chl_map->ch[ch_no].start], &sgl_offset, read_wr.num_sge); if (((ch+1)->rc_discrim == 0) && - (read_wr.num_sge == ch_sge_ary[ch_no].count)) { + (read_wr.num_sge == chl_map->ch[ch_no].count)) { /* * Mark the last RDMA_READ with a bit to * indicate all RPC data has been fetched from @@ -358,9 +351,9 @@ next_sge: } atomic_inc(&rdma_stat_read); - if (read_wr.num_sge < ch_sge_ary[ch_no].count) { - ch_sge_ary[ch_no].count -= read_wr.num_sge; - ch_sge_ary[ch_no].start += read_wr.num_sge; + if (read_wr.num_sge < chl_map->ch[ch_no].count) { + chl_map->ch[ch_no].count -= read_wr.num_sge; + chl_map->ch[ch_no].start += read_wr.num_sge; goto next_sge; } sgl_offset = 0; @@ -368,8 +361,8 @@ next_sge: } out: - svc_rdma_put_context(tmp_sge_ctxt, 0); - svc_rdma_put_context(tmp_ch_ctxt, 0); + svc_rdma_put_req_map(rpl_map); + svc_rdma_put_req_map(chl_map); /* Detach arg pages. svc_recv will replenish them */ for (ch_no = 0; &rqstp->rq_pages[ch_no] < rqstp->rq_respages; ch_no++) @@ -399,7 +392,7 @@ static int rdma_read_complete(struct svc_rqst *rqstp, rqstp->rq_pages[page_no] = head->pages[page_no]; } /* Point rq_arg.pages past header */ - rqstp->rq_arg.pages = &rqstp->rq_pages[head->sge[0].length]; + rqstp->rq_arg.pages = &rqstp->rq_pages[head->hdr_count]; rqstp->rq_arg.page_len = head->arg.page_len; rqstp->rq_arg.page_base = head->arg.page_base; -- cgit v1.2.3 From 87295b6c5c7fd7bbc0ce3e7f42d2adbbac7352b9 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 28 May 2008 13:17:44 -0500 Subject: svcrdma: Add dma map count and WARN_ON Add a dma map count in order to verify that all DMA mapping resources have been freed when the transport is closed. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 1 + net/sunrpc/xprtrdma/svc_rdma_sendto.c | 3 +++ net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 +++++ 4 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index fd5e8a1c17de..ab93afc03c43 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -130,6 +130,7 @@ struct svcxprt_rdma { struct ib_pd *sc_pd; + atomic_t sc_dma_used; atomic_t sc_ctxt_used; struct list_head sc_ctxt_free; int sc_ctxt_cnt; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index d25971b42a74..b4b17f44cb29 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -222,6 +222,7 @@ static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, ctxt->count = count; ctxt->direction = DMA_FROM_DEVICE; for (i = 0; i < count; i++) { + atomic_inc(&xprt->sc_dma_used); ctxt->sge[i].addr = ib_dma_map_single(xprt->sc_cm_id->device, vec[i].iov_base, vec[i].iov_len, diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index bdc11a30e937..a19b22b452a3 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -163,6 +163,7 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, sge_bytes = min((size_t)bc, (size_t)(vec->sge[xdr_sge_no].iov_len-sge_off)); sge[sge_no].length = sge_bytes; + atomic_inc(&xprt->sc_dma_used); sge[sge_no].addr = ib_dma_map_single(xprt->sc_cm_id->device, (void *) @@ -385,6 +386,7 @@ static int send_reply(struct svcxprt_rdma *rdma, ctxt->count = 1; /* Prepare the SGE for the RPCRDMA Header */ + atomic_inc(&rdma->sc_dma_used); ctxt->sge[0].addr = ib_dma_map_page(rdma->sc_cm_id->device, page, 0, PAGE_SIZE, DMA_TO_DEVICE); @@ -396,6 +398,7 @@ static int send_reply(struct svcxprt_rdma *rdma, for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) { sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count); byte_count -= sge_bytes; + atomic_inc(&rdma->sc_dma_used); ctxt->sge[sge_no].addr = ib_dma_map_single(rdma->sc_cm_id->device, vec->sge[sge_no].iov_base, diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 7e8ee66458ea..6fddd588c031 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -155,6 +155,7 @@ static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) struct svcxprt_rdma *xprt = ctxt->xprt; int i; for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) { + atomic_dec(&xprt->sc_dma_used); ib_dma_unmap_single(xprt->sc_cm_id->device, ctxt->sge[i].addr, ctxt->sge[i].length, @@ -519,6 +520,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, cma_xprt->sc_max_requests = svcrdma_max_requests; cma_xprt->sc_sq_depth = svcrdma_max_requests * RPCRDMA_SQ_DEPTH_MULT; atomic_set(&cma_xprt->sc_sq_count, 0); + atomic_set(&cma_xprt->sc_ctxt_used, 0); if (!listener) { int reqs = cma_xprt->sc_max_requests; @@ -569,6 +571,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt) BUG_ON(sge_no >= xprt->sc_max_sge); page = svc_rdma_get_page(); ctxt->pages[sge_no] = page; + atomic_inc(&xprt->sc_dma_used); pa = ib_dma_map_page(xprt->sc_cm_id->device, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); @@ -1049,6 +1052,7 @@ static void __svc_rdma_free(struct work_struct *work) /* Warn if we leaked a resource or under-referenced */ WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0); + WARN_ON(atomic_read(&rdma->sc_dma_used) != 0); /* Destroy the QP if present (not a listener) */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) @@ -1169,6 +1173,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp, length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va); /* Prepare SGE for local address */ + atomic_inc(&xprt->sc_dma_used); sge.addr = ib_dma_map_page(xprt->sc_cm_id->device, p, 0, PAGE_SIZE, DMA_FROM_DEVICE); sge.lkey = xprt->sc_phys_mr->lkey; -- cgit v1.2.3 From 779a48577ba88b6a7e9748a04b0b739f36c5e6f6 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Mon, 19 May 2008 10:17:09 -0500 Subject: svcrdma: Remove unused wait q from svcrdma_xprt structure The sc_read_wait queue head is no longer used. Remove it. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index ab93afc03c43..d8d74c4ab504 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -119,7 +119,6 @@ struct svcxprt_rdma { struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ struct list_head sc_accept_q; /* Conn. waiting accept */ int sc_ord; /* RDMA read limit */ - wait_queue_head_t sc_read_wait; int sc_max_sge; int sc_sq_depth; /* Depth of SQ */ -- cgit v1.2.3 From 8948896c9e098c6fd31a6a698a598a7cbd7fa40e Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 28 May 2008 15:14:02 -0500 Subject: svcrdma: Change WR context get/put to use the kmem cache Change the WR context pool to be shared across mount points. This reduces the RDMA transport memory footprint significantly since idle mounts don't consume WR context memory. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 6 -- net/sunrpc/xprtrdma/svc_rdma_transport.c | 121 +++---------------------------- 2 files changed, 12 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index d8d74c4ab504..ef2e3a20bf3b 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -73,7 +73,6 @@ extern atomic_t rdma_stat_sq_prod; struct svc_rdma_op_ctxt { struct svc_rdma_op_ctxt *read_hdr; int hdr_count; - struct list_head free_list; struct xdr_buf arg; struct list_head dto_q; enum ib_wr_opcode wr_op; @@ -131,11 +130,6 @@ struct svcxprt_rdma { atomic_t sc_dma_used; atomic_t sc_ctxt_used; - struct list_head sc_ctxt_free; - int sc_ctxt_cnt; - int sc_ctxt_bump; - int sc_ctxt_max; - spinlock_t sc_ctxt_lock; struct list_head sc_rq_dto_q; spinlock_t sc_rq_dto_lock; struct ib_qp *sc_qp; diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 80104f4999d5..19ddc382b777 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -84,69 +84,23 @@ struct svc_xprt_class svc_rdma_class = { .xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP, }; -static int rdma_bump_context_cache(struct svcxprt_rdma *xprt) -{ - int target; - int at_least_one = 0; - struct svc_rdma_op_ctxt *ctxt; - - target = min(xprt->sc_ctxt_cnt + xprt->sc_ctxt_bump, - xprt->sc_ctxt_max); - - spin_lock_bh(&xprt->sc_ctxt_lock); - while (xprt->sc_ctxt_cnt < target) { - xprt->sc_ctxt_cnt++; - spin_unlock_bh(&xprt->sc_ctxt_lock); - - ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); - - spin_lock_bh(&xprt->sc_ctxt_lock); - if (ctxt) { - at_least_one = 1; - INIT_LIST_HEAD(&ctxt->free_list); - list_add(&ctxt->free_list, &xprt->sc_ctxt_free); - } else { - /* kmalloc failed...give up for now */ - xprt->sc_ctxt_cnt--; - break; - } - } - spin_unlock_bh(&xprt->sc_ctxt_lock); - dprintk("svcrdma: sc_ctxt_max=%d, sc_ctxt_cnt=%d\n", - xprt->sc_ctxt_max, xprt->sc_ctxt_cnt); - return at_least_one; -} +/* WR context cache. Created in svc_rdma.c */ +extern struct kmem_cache *svc_rdma_ctxt_cachep; struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) { struct svc_rdma_op_ctxt *ctxt; while (1) { - spin_lock_bh(&xprt->sc_ctxt_lock); - if (unlikely(list_empty(&xprt->sc_ctxt_free))) { - /* Try to bump my cache. */ - spin_unlock_bh(&xprt->sc_ctxt_lock); - - if (rdma_bump_context_cache(xprt)) - continue; - - printk(KERN_INFO "svcrdma: sleeping waiting for " - "context memory on xprt=%p\n", - xprt); - schedule_timeout_uninterruptible(msecs_to_jiffies(500)); - continue; - } - ctxt = list_entry(xprt->sc_ctxt_free.next, - struct svc_rdma_op_ctxt, - free_list); - list_del_init(&ctxt->free_list); - spin_unlock_bh(&xprt->sc_ctxt_lock); - ctxt->xprt = xprt; - INIT_LIST_HEAD(&ctxt->dto_q); - ctxt->count = 0; - atomic_inc(&xprt->sc_ctxt_used); - break; + ctxt = kmem_cache_alloc(svc_rdma_ctxt_cachep, GFP_KERNEL); + if (ctxt) + break; + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); } + ctxt->xprt = xprt; + INIT_LIST_HEAD(&ctxt->dto_q); + ctxt->count = 0; + atomic_inc(&xprt->sc_ctxt_used); return ctxt; } @@ -174,9 +128,7 @@ void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages) for (i = 0; i < ctxt->count; i++) put_page(ctxt->pages[i]); - spin_lock_bh(&xprt->sc_ctxt_lock); - list_add(&ctxt->free_list, &xprt->sc_ctxt_free); - spin_unlock_bh(&xprt->sc_ctxt_lock); + kmem_cache_free(svc_rdma_ctxt_cachep, ctxt); atomic_dec(&xprt->sc_ctxt_used); } @@ -461,40 +413,6 @@ static void sq_comp_handler(struct ib_cq *cq, void *cq_context) tasklet_schedule(&dto_tasklet); } -static void create_context_cache(struct svcxprt_rdma *xprt, - int ctxt_count, int ctxt_bump, int ctxt_max) -{ - struct svc_rdma_op_ctxt *ctxt; - int i; - - xprt->sc_ctxt_max = ctxt_max; - xprt->sc_ctxt_bump = ctxt_bump; - xprt->sc_ctxt_cnt = 0; - atomic_set(&xprt->sc_ctxt_used, 0); - - INIT_LIST_HEAD(&xprt->sc_ctxt_free); - for (i = 0; i < ctxt_count; i++) { - ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); - if (ctxt) { - INIT_LIST_HEAD(&ctxt->free_list); - list_add(&ctxt->free_list, &xprt->sc_ctxt_free); - xprt->sc_ctxt_cnt++; - } - } -} - -static void destroy_context_cache(struct svcxprt_rdma *xprt) -{ - while (!list_empty(&xprt->sc_ctxt_free)) { - struct svc_rdma_op_ctxt *ctxt; - ctxt = list_entry(xprt->sc_ctxt_free.next, - struct svc_rdma_op_ctxt, - free_list); - list_del_init(&ctxt->free_list); - kfree(ctxt); - } -} - static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, int listener) { @@ -511,7 +429,6 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_read_complete_lock); - spin_lock_init(&cma_xprt->sc_ctxt_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); cma_xprt->sc_ord = svcrdma_ord; @@ -522,20 +439,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, atomic_set(&cma_xprt->sc_sq_count, 0); atomic_set(&cma_xprt->sc_ctxt_used, 0); - if (!listener) { - int reqs = cma_xprt->sc_max_requests; - create_context_cache(cma_xprt, - reqs << 1, /* starting size */ - reqs, /* bump amount */ - reqs + - cma_xprt->sc_sq_depth + - RPCRDMA_MAX_THREADS + 1); /* max */ - if (list_empty(&cma_xprt->sc_ctxt_free)) { - kfree(cma_xprt); - return NULL; - } - clear_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags); - } else + if (listener) set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags); return cma_xprt; @@ -1077,7 +981,6 @@ static void __svc_rdma_free(struct work_struct *work) /* Destroy the CM ID */ rdma_destroy_id(rdma->sc_cm_id); - destroy_context_cache(rdma); kfree(rdma); } -- cgit v1.2.3 From 9465efc9e96135a2cec8154c0c766fa59984a298 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 27 Jun 2008 11:05:24 +0200 Subject: Remove BKL from remote_llseek v2 - Replace remote_llseek with generic_file_llseek_unlocked (to force compilation failures in all users) - Change all users to either use generic_file_llseek_unlocked directly or take the BKL around. I changed the file systems who don't use the BKL for anything (CIFS, GFS) to call it directly. NCPFS and SMBFS and NFS take the BKL, but explicitely in their own source now. I moved them all over in a single patch to avoid unbisectable sections. Open problem: 32bit kernels can corrupt fpos because its modification is not atomic, but they can do that anyways because there's other paths who modify it without BKL. Do we need a special lock for the pos/f_version = 0 checks? Trond says the NFS BKL is likely not needed, but keep it for now until his full audit. v2: Use generic_file_llseek_unlocked instead of remote_llseek_unlocked and factor duplicated code (suggested by hch) Cc: Trond.Myklebust@netapp.com Cc: swhiteho@redhat.com Cc: sfrench@samba.org Cc: vandrove@vc.cvut.cz Signed-off-by: Andi Kleen Signed-off-by: Andi Kleen Signed-off-by: Jonathan Corbet --- fs/cifs/cifsfs.c | 2 +- fs/gfs2/ops_file.c | 4 ++-- fs/ncpfs/file.c | 12 +++++++++++- fs/nfs/file.c | 6 +++++- fs/read_write.c | 38 +++++++++++--------------------------- fs/smbfs/file.c | 11 ++++++++++- include/linux/fs.h | 3 ++- 7 files changed, 42 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 427a7c695896..aeff0fe5b6b9 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -581,7 +581,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) if (retval < 0) return (loff_t)retval; } - return remote_llseek(file, offset, origin); + return generic_file_llseek_unlocked(file, offset, origin); } struct file_system_type cifs_fs_type = { diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index e1b7d525a066..24dd59450088 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -62,11 +62,11 @@ static loff_t gfs2_llseek(struct file *file, loff_t offset, int origin) error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); if (!error) { - error = remote_llseek(file, offset, origin); + error = generic_file_llseek_unlocked(file, offset, origin); gfs2_glock_dq_uninit(&i_gh); } } else - error = remote_llseek(file, offset, origin); + error = generic_file_llseek_unlocked(file, offset, origin); return error; } diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 2b145de45b39..6a7d901f1936 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "ncplib_kernel.h" @@ -281,9 +282,18 @@ static int ncp_release(struct inode *inode, struct file *file) { return 0; } +static loff_t ncp_remote_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t ret; + lock_kernel(); + ret = generic_file_llseek_unlocked(file, offset, origin); + unlock_kernel(); + return ret; +} + const struct file_operations ncp_file_operations = { - .llseek = remote_llseek, + .llseek = ncp_remote_llseek, .read = ncp_file_read, .write = ncp_file_write, .ioctl = ncp_ioctl, diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 3536b01164f9..a34eb78989f5 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -170,6 +170,7 @@ force_reval: static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) { + loff_t loff; /* origin == SEEK_END => we must revalidate the cached file length */ if (origin == SEEK_END) { struct inode *inode = filp->f_mapping->host; @@ -177,7 +178,10 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) if (retval < 0) return (loff_t)retval; } - return remote_llseek(filp, offset, origin); + lock_kernel(); /* BKL needed? */ + loff = generic_file_llseek_unlocked(filp, offset, origin); + unlock_kernel(); + return loff; } /* diff --git a/fs/read_write.c b/fs/read_write.c index f0d1240a5c69..9ba495d5a29b 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -31,12 +31,12 @@ const struct file_operations generic_ro_fops = { EXPORT_SYMBOL(generic_ro_fops); -loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) +loff_t +generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) { loff_t retval; struct inode *inode = file->f_mapping->host; - mutex_lock(&inode->i_mutex); switch (origin) { case SEEK_END: offset += inode->i_size; @@ -46,42 +46,26 @@ loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) } retval = -EINVAL; if (offset>=0 && offset<=inode->i_sb->s_maxbytes) { + /* Special lock needed here? */ if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } - mutex_unlock(&inode->i_mutex); return retval; } +EXPORT_SYMBOL(generic_file_llseek_unlocked); -EXPORT_SYMBOL(generic_file_llseek); - -loff_t remote_llseek(struct file *file, loff_t offset, int origin) +loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) { - loff_t retval; - - lock_kernel(); - switch (origin) { - case SEEK_END: - offset += i_size_read(file->f_path.dentry->d_inode); - break; - case SEEK_CUR: - offset += file->f_pos; - } - retval = -EINVAL; - if (offset>=0 && offset<=file->f_path.dentry->d_inode->i_sb->s_maxbytes) { - if (offset != file->f_pos) { - file->f_pos = offset; - file->f_version = 0; - } - retval = offset; - } - unlock_kernel(); - return retval; + loff_t n; + mutex_lock(&file->f_dentry->d_inode->i_mutex); + n = generic_file_llseek_unlocked(file, offset, origin); + mutex_unlock(&file->f_dentry->d_inode->i_mutex); + return n; } -EXPORT_SYMBOL(remote_llseek); +EXPORT_SYMBOL(generic_file_llseek); loff_t no_llseek(struct file *file, loff_t offset, int origin) { diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index efbe29af3d7a..2294783320cb 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -422,9 +422,18 @@ smb_file_permission(struct inode *inode, int mask, struct nameidata *nd) return error; } +static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t ret; + lock_kernel(); + ret = generic_file_llseek_unlocked(file, offset, origin); + unlock_kernel(); + return ret; +} + const struct file_operations smb_file_operations = { - .llseek = remote_llseek, + .llseek = smb_remote_llseek, .read = do_sync_read, .aio_read = smb_file_aio_read, .write = do_sync_write, diff --git a/include/linux/fs.h b/include/linux/fs.h index f413085f748e..b158e5161bca 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1871,7 +1871,8 @@ extern void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); extern loff_t no_llseek(struct file *file, loff_t offset, int origin); extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin); -extern loff_t remote_llseek(struct file *file, loff_t offset, int origin); +extern loff_t generic_file_llseek_unlocked(struct file *file, loff_t offset, + int origin); extern int generic_file_open(struct inode * inode, struct file * filp); extern int nonseekable_open(struct inode * inode, struct file * filp); -- cgit v1.2.3 From 778d80be52699596bf70e0eb0761cf5e1e46088d Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Sat, 28 Jun 2008 14:17:11 +0900 Subject: ipv6: Add disable_ipv6 sysctl to disable IPv6 operaion on specific interface. Signed-off-by: YOSHIFUJI Hideaki --- Documentation/networking/ip-sysctl.txt | 4 ++++ include/linux/ipv6.h | 2 ++ net/ipv6/addrconf.c | 11 +++++++++++ net/ipv6/ip6_input.c | 3 ++- net/ipv6/ip6_output.c | 7 +++++++ 5 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 71c7bea97160..dae980e8f1b9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1025,6 +1025,10 @@ max_addresses - INTEGER autoconfigured addresses. Default: 16 +disable_ipv6 - BOOLEAN + Disable IPv6 operation. + Default: FALSE (enable IPv6 operation) + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index cde056e08181..d9d7f9b69eb4 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -163,6 +163,7 @@ struct ipv6_devconf { #ifdef CONFIG_IPV6_MROUTE __s32 mc_forwarding; #endif + __s32 disable_ipv6; void *sysctl; }; @@ -194,6 +195,7 @@ enum { DEVCONF_OPTIMISTIC_DAD, DEVCONF_ACCEPT_SOURCE_ROUTE, DEVCONF_MC_FORWARDING, + DEVCONF_DISABLE_IPV6, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8b6875f02039..8c5cff50bbed 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -183,6 +183,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = { #endif .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ + .disable_ipv6 = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -215,6 +216,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { #endif .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ + .disable_ipv6 = 0, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -3657,6 +3659,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, #ifdef CONFIG_IPV6_MROUTE array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; #endif + array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; } static inline size_t inet6_if_nlmsg_size(void) @@ -4215,6 +4218,14 @@ static struct addrconf_sysctl_table .proc_handler = &proc_dointvec, }, #endif + { + .ctl_name = CTL_UNNUMBERED, + .procname = "disable_ipv6", + .data = &ipv6_devconf.disable_ipv6, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0, /* sentinel */ } diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 34e5a96623ae..ea81c614dde2 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -71,7 +71,8 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt IP6_INC_STATS_BH(idev, IPSTATS_MIB_INRECEIVES); - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { + if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || + !idev || unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS); rcu_read_unlock(); goto out; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 871bdec09edb..0981c1ef3057 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -173,6 +173,13 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb) int ip6_output(struct sk_buff *skb) { + struct inet6_dev *idev = ip6_dst_idev(skb->dst); + if (unlikely(idev->cnf.disable_ipv6)) { + IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + return 0; + } + if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || dst_allfrag(skb->dst)) return ip6_fragment(skb, ip6_output2); -- cgit v1.2.3 From 1b34be74cbf18f5d58cc85c7c4afcd9f7d74accd Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Sat, 28 Jun 2008 14:18:38 +0900 Subject: ipv6 addrconf: add accept_dad sysctl to control DAD operation. - If 0, disable DAD. - If 1, perform DAD (default). - If >1, perform DAD and disable IPv6 operation if DAD for MAC-based link-local address has been failed (RFC4862 5.4.5). We do not follow RFC4862 by default. Refer to the netdev thread entitled "Linux IPv6 DAD not full conform to RFC 4862 ?" http://www.spinics.net/lists/netdev/msg52027.html Signed-off-by: YOSHIFUJI Hideaki --- Documentation/networking/ip-sysctl.txt | 7 +++++++ include/linux/ipv6.h | 2 ++ net/ipv6/addrconf.c | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index dae980e8f1b9..72f6d52e52e6 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1029,6 +1029,13 @@ disable_ipv6 - BOOLEAN Disable IPv6 operation. Default: FALSE (enable IPv6 operation) +accept_dad - INTEGER + Whether to accept DAD (Duplicate Address Detection). + 0: Disable DAD + 1: Enable DAD (default) + 2: Enable DAD, and disable IPv6 operation if MAC-based duplicate + link-local address has been found. + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index d9d7f9b69eb4..391ad0843a46 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -164,6 +164,7 @@ struct ipv6_devconf { __s32 mc_forwarding; #endif __s32 disable_ipv6; + __s32 accept_dad; void *sysctl; }; @@ -196,6 +197,7 @@ enum { DEVCONF_ACCEPT_SOURCE_ROUTE, DEVCONF_MC_FORWARDING, DEVCONF_DISABLE_IPV6, + DEVCONF_ACCEPT_DAD, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8c5cff50bbed..2ec73e62202c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -119,6 +119,7 @@ static void ipv6_regen_rndid(unsigned long data); static int desync_factor = MAX_DESYNC_FACTOR * HZ; #endif +static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); static int ipv6_count_addresses(struct inet6_dev *idev); /* @@ -184,6 +185,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = { .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, + .accept_dad = 1, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -217,6 +219,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, + .accept_dad = 1, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -380,6 +383,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) */ in6_dev_hold(ndev); + if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) + ndev->cnf.accept_dad = -1; + #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { printk(KERN_INFO @@ -1421,6 +1427,20 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp) void addrconf_dad_failure(struct inet6_ifaddr *ifp) { + struct inet6_dev *idev = ifp->idev; + if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { + struct in6_addr addr; + + addr.s6_addr32[0] = htonl(0xfe800000); + addr.s6_addr32[1] = 0; + + if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) && + ipv6_addr_equal(&ifp->addr, &addr)) { + /* DAD failed for link-local based on MAC address */ + idev->cnf.disable_ipv6 = 1; + } + } + if (net_ratelimit()) printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name); addrconf_dad_stop(ifp); @@ -2753,6 +2773,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) spin_lock_bh(&ifp->lock); if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || + idev->cnf.accept_dad < 1 || !(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) { ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); @@ -2800,6 +2821,11 @@ static void addrconf_dad_timer(unsigned long data) read_unlock_bh(&idev->lock); goto out; } + if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) { + read_unlock_bh(&idev->lock); + addrconf_dad_failure(ifp); + return; + } spin_lock_bh(&ifp->lock); if (ifp->probes == 0) { /* @@ -3660,6 +3686,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; #endif array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; + array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; } static inline size_t inet6_if_nlmsg_size(void) @@ -4226,6 +4253,14 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "accept_dad", + .data = &ipv6_devconf.accept_dad, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0, /* sentinel */ } -- cgit v1.2.3 From 623d1a1af77bd52a389c6eda5920e28eb2ee468b Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Thu, 3 Jul 2008 12:13:30 +0800 Subject: ipv6: Do cleanup for ip6_mr_init. If do not do it, we will get following issues: 1. Leaving junks after inet6_init failing halfway. 2. Leaving proc and notifier junks after ipv6 modules unloading. Signed-off-by: Wang Chen Signed-off-by: YOSHIFUJI Hideaki --- include/linux/mroute6.h | 3 ++- net/ipv6/af_inet6.c | 11 ++++++++++- net/ipv6/ip6mr.c | 38 +++++++++++++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index e7989593142b..4c4d6f57d5c5 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -135,7 +135,8 @@ extern int ip6_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip6_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ip6_mr_input(struct sk_buff *skb); extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg); -extern void ip6_mr_init(void); +extern int ip6_mr_init(void); +extern void ip6_mr_cleanup(void); struct mif_device { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3ce8d2f318c6..6b39af1acb5e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -953,7 +953,9 @@ static int __init inet6_init(void) if (err) goto icmp_fail; #ifdef CONFIG_IPV6_MROUTE - ip6_mr_init(); + err = ip6_mr_init(); + if (err) + goto ipmr_fail; #endif err = ndisc_init(); if (err) @@ -1057,6 +1059,10 @@ netfilter_fail: igmp_fail: ndisc_cleanup(); ndisc_fail: +#ifdef CONFIG_IPV6_MROUTE + ip6_mr_cleanup(); +ipmr_fail: +#endif icmpv6_cleanup(); icmp_fail: unregister_pernet_subsys(&inet6_net_ops); @@ -1111,6 +1117,9 @@ static void __exit inet6_exit(void) ipv6_netfilter_fini(); igmp6_cleanup(); ndisc_cleanup(); +#ifdef CONFIG_IPV6_MROUTE + ip6_mr_cleanup(); +#endif icmpv6_cleanup(); rawv6_exit(); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 90e763073dc5..cfac26d674ed 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -948,23 +948,51 @@ static struct notifier_block ip6_mr_notifier = { * Setup for IP multicast routing */ -void __init ip6_mr_init(void) +int __init ip6_mr_init(void) { + int err; + mrt_cachep = kmem_cache_create("ip6_mrt_cache", sizeof(struct mfc6_cache), 0, SLAB_HWCACHE_ALIGN, NULL); if (!mrt_cachep) - panic("cannot allocate ip6_mrt_cache"); + return -ENOMEM; setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0); - register_netdevice_notifier(&ip6_mr_notifier); + err = register_netdevice_notifier(&ip6_mr_notifier); + if (err) + goto reg_notif_fail; +#ifdef CONFIG_PROC_FS + err = -ENOMEM; + if (!proc_net_fops_create(&init_net, "ip6_mr_vif", 0, &ip6mr_vif_fops)) + goto proc_vif_fail; + if (!proc_net_fops_create(&init_net, "ip6_mr_cache", + 0, &ip6mr_mfc_fops)) + goto proc_cache_fail; +#endif + return 0; +reg_notif_fail: + kmem_cache_destroy(mrt_cachep); #ifdef CONFIG_PROC_FS - proc_net_fops_create(&init_net, "ip6_mr_vif", 0, &ip6mr_vif_fops); - proc_net_fops_create(&init_net, "ip6_mr_cache", 0, &ip6mr_mfc_fops); +proc_vif_fail: + unregister_netdevice_notifier(&ip6_mr_notifier); +proc_cache_fail: + proc_net_remove(&init_net, "ip6_mr_vif"); #endif + return err; } +void ip6_mr_cleanup(void) +{ +#ifdef CONFIG_PROC_FS + proc_net_remove(&init_net, "ip6_mr_cache"); + proc_net_remove(&init_net, "ip6_mr_vif"); +#endif + unregister_netdevice_notifier(&ip6_mr_notifier); + del_timer(&ipmr_expire_timer); + kmem_cache_destroy(mrt_cachep); +} static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock) { -- cgit v1.2.3 From 03d2f897e9fb3218989baa2139a951ce7f5414bf Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Thu, 3 Jul 2008 12:13:36 +0800 Subject: ipv4: Do cleanup for ip_mr_init Same as ip6_mr_init(), make ip_mr_init() return errno if fails. But do not do error handling in inet_init(), just print a msg. Signed-off-by: Wang Chen Signed-off-by: YOSHIFUJI Hideaki --- include/linux/igmp.h | 1 - include/linux/mroute.h | 3 +-- net/ipv4/af_inet.c | 5 +++-- net/ipv4/ipmr.c | 28 ++++++++++++++++++++++++---- 4 files changed, 28 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/igmp.h b/include/linux/igmp.h index f5a1a0db2e8e..7bb3c095c15b 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -228,7 +228,6 @@ extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, struct group_filter __user *optval, int __user *optlen); extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, int dif); -extern void ip_mr_init(void); extern void ip_mc_init_dev(struct in_device *); extern void ip_mc_destroy_dev(struct in_device *); extern void ip_mc_up(struct in_device *); diff --git a/include/linux/mroute.h b/include/linux/mroute.h index de4decfa1bfc..df8efd42bf8a 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -147,8 +147,7 @@ static inline int ip_mroute_opt(int opt) extern int ip_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); -extern void ip_mr_init(void); - +extern int ip_mr_init(void); struct vif_device { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 42bd24b64b57..dc411335c14f 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1479,14 +1479,15 @@ static int __init inet_init(void) * Initialise the multicast router */ #if defined(CONFIG_IP_MROUTE) - ip_mr_init(); + if (ip_mr_init()) + printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n"); #endif /* * Initialise per-cpu ipv4 mibs */ if (init_ipv4_mibs()) - printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ; + printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ipv4_proc_init(); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 300ab0c2919e..438fab9c62a0 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1878,16 +1878,36 @@ static struct net_protocol pim_protocol = { * Setup for IP multicast routing */ -void __init ip_mr_init(void) +int __init ip_mr_init(void) { + int err; + mrt_cachep = kmem_cache_create("ip_mrt_cache", sizeof(struct mfc_cache), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + if (!mrt_cachep) + return -ENOMEM; + setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0); - register_netdevice_notifier(&ip_mr_notifier); + err = register_netdevice_notifier(&ip_mr_notifier); + if (err) + goto reg_notif_fail; #ifdef CONFIG_PROC_FS - proc_net_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops); - proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops); + err = -ENOMEM; + if (!proc_net_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops)) + goto proc_vif_fail; + if (!proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops)) + goto proc_cache_fail; #endif + return 0; +reg_notif_fail: + kmem_cache_destroy(mrt_cachep); +#ifdef CONFIG_PROC_FS +proc_vif_fail: + unregister_netdevice_notifier(&ip_mr_notifier); +proc_cache_fail: + proc_net_remove(&init_net, "ip_mr_vif"); +#endif + return err; } -- cgit v1.2.3 From e0835f8fa56d2d308486f8a34cf1c4480cd27f4e Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Thu, 3 Jul 2008 16:51:22 +0900 Subject: ipv4,ipv6 mroute: Add some helper inline functions to remove ugly ifdefs. ip{,v6}_mroute_{set,get}sockopt() should not matter by optimization but it would be better not to depend on optimization semantically. Signed-off-by: YOSHIFUJI Hideaki --- include/linux/mroute.h | 27 +++++++++++++++++++++++++++ include/linux/mroute6.h | 32 ++++++++++++++++++++++++++++++++ net/ipv6/af_inet6.c | 8 -------- 3 files changed, 59 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index df8efd42bf8a..07112ee9293a 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -144,10 +144,37 @@ static inline int ip_mroute_opt(int opt) } #endif +#ifdef CONFIG_IP_MROUTE extern int ip_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); extern int ip_mr_init(void); +#else +static inline +int ip_mroute_setsockopt(struct sock *sock, + int optname, char __user *optval, int optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ip_mroute_getsockopt(struct sock *sock, + int optname, char __user *optval, int __user *optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) +{ + return -ENOIOCTLCMD; +} + +static inline int ip_mr_init(void) +{ + return 0; +} +#endif struct vif_device { diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 4c4d6f57d5c5..5cf50473a10f 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -131,12 +131,44 @@ static inline int ip6_mroute_opt(int opt) struct sock; +#ifdef CONFIG_IPV6_MROUTE extern int ip6_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip6_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ip6_mr_input(struct sk_buff *skb); extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg); extern int ip6_mr_init(void); extern void ip6_mr_cleanup(void); +#else +static inline +int ip6_mroute_setsockopt(struct sock *sock, + int optname, char __user *optval, int optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ip6_mroute_getsockopt(struct sock *sock, + int optname, char __user *optval, int __user *optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) +{ + return -ENOIOCTLCMD; +} + +static inline int ip6_mr_init(void) +{ + return 0; +} + +static inline void ip6_mr_cleanup(void) +{ + return; +} +#endif struct mif_device { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 6b39af1acb5e..3d828bc4b1cf 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -59,9 +59,7 @@ #include #include -#ifdef CONFIG_IPV6_MROUTE #include -#endif MODULE_AUTHOR("Cast of dozens"); MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); @@ -952,11 +950,9 @@ static int __init inet6_init(void) err = icmpv6_init(); if (err) goto icmp_fail; -#ifdef CONFIG_IPV6_MROUTE err = ip6_mr_init(); if (err) goto ipmr_fail; -#endif err = ndisc_init(); if (err) goto ndisc_fail; @@ -1059,10 +1055,8 @@ netfilter_fail: igmp_fail: ndisc_cleanup(); ndisc_fail: -#ifdef CONFIG_IPV6_MROUTE ip6_mr_cleanup(); ipmr_fail: -#endif icmpv6_cleanup(); icmp_fail: unregister_pernet_subsys(&inet6_net_ops); @@ -1117,9 +1111,7 @@ static void __exit inet6_exit(void) ipv6_netfilter_fini(); igmp6_cleanup(); ndisc_cleanup(); -#ifdef CONFIG_IPV6_MROUTE ip6_mr_cleanup(); -#endif icmpv6_cleanup(); rawv6_exit(); -- cgit v1.2.3 From 07240fd0902c872f044f523893364a1a24c9f278 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jul 2008 03:45:32 -0700 Subject: tun: Interface to query tun/tap features. The problem with introducing checksum offload and gso to tun is they need to set dev->features to enable GSO and/or checksumming, which is supposed to be done before register_netdevice(), ie. as part of TUNSETIFF. Unfortunately, TUNSETIFF has always just ignored flags it doesn't understand, so there's no good way of detecting whether the kernel supports new IFF_ flags. This patch implements a TUNGETFEATURES ioctl which returns all the valid IFF flags. It could be extended later to include other features. Here's an example program which uses it: #include #include #include #include #include #include #include static struct { unsigned int flag; const char *name; } known_flags[] = { { IFF_TUN, "TUN" }, { IFF_TAP, "TAP" }, { IFF_NO_PI, "NO_PI" }, { IFF_ONE_QUEUE, "ONE_QUEUE" }, }; int main() { unsigned int features, i; int netfd = open("/dev/net/tun", O_RDWR); if (netfd < 0) err(1, "Opening /dev/net/tun"); if (ioctl(netfd, TUNGETFEATURES, &features) != 0) { printf("Kernel does not support TUNGETFEATURES, guessing\n"); features = (IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE); } printf("Available features are: "); for (i = 0; i < sizeof(known_flags)/sizeof(known_flags[0]); i++) { if (features & known_flags[i].flag) { features &= ~known_flags[i].flag; printf("%s ", known_flags[i].name); } } if (features) printf("(UNKNOWN %#x)", features); printf("\n"); return 0; } Signed-off-by: Rusty Russell Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 8 ++++++++ include/linux/if_tun.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 7ab94c825b57..3bb991fd2b51 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -640,6 +640,14 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, return 0; } + if (cmd == TUNGETFEATURES) { + /* Currently this just means: "what IFF flags are valid?". + * This is needed because we never checked for invalid flags on + * TUNSETIFF. */ + return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE, + (unsigned int __user*)argp); + } + if (!tun) return -EBADFD; diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 18f31b6187a3..94f76a112303 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -40,6 +40,7 @@ #define TUNSETOWNER _IOW('T', 204, int) #define TUNSETLINK _IOW('T', 205, int) #define TUNSETGROUP _IOW('T', 206, int) +#define TUNGETFEATURES _IOR('T', 207, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3 From 5228ddc98fa49b3cedab4024e269d62410a0d806 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jul 2008 03:46:16 -0700 Subject: tun: TUNSETFEATURES to set gso features. ethtool is useful for setting (some) device fields, but it's root-only. Finer feature control is available through a tun-specific ioctl. (Includes Mark McLoughlin 's fix to hold rtnl sem). Signed-off-by: Rusty Russell Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_tun.h | 7 +++++++ 2 files changed, 56 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3bb991fd2b51..a314955e6994 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -611,6 +611,46 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) return err; } +/* This is like a cut-down ethtool ops, except done via tun fd so no + * privs required. */ +static int set_offload(struct net_device *dev, unsigned long arg) +{ + unsigned int old_features, features; + + old_features = dev->features; + /* Unset features, set them as we chew on the arg. */ + features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST + |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6)); + + if (arg & TUN_F_CSUM) { + features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; + arg &= ~TUN_F_CSUM; + + if (arg & (TUN_F_TSO4|TUN_F_TSO6)) { + if (arg & TUN_F_TSO_ECN) { + features |= NETIF_F_TSO_ECN; + arg &= ~TUN_F_TSO_ECN; + } + if (arg & TUN_F_TSO4) + features |= NETIF_F_TSO; + if (arg & TUN_F_TSO6) + features |= NETIF_F_TSO6; + arg &= ~(TUN_F_TSO4|TUN_F_TSO6); + } + } + + /* This gives the user a way to test for new features in future by + * trying to set them. */ + if (arg) + return -EINVAL; + + dev->features = features; + if (old_features != dev->features) + netdev_features_change(dev); + + return 0; +} + static int tun_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -715,6 +755,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; #endif + case TUNSETOFFLOAD: + { + int ret; + rtnl_lock(); + ret = set_offload(tun->dev, arg); + rtnl_unlock(); + return ret; + } + case SIOCGIFFLAGS: ifr.ifr_flags = tun->if_flags; if (copy_to_user( argp, &ifr, sizeof ifr)) diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 94f76a112303..3f0a0995d449 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -41,6 +41,7 @@ #define TUNSETLINK _IOW('T', 205, int) #define TUNSETGROUP _IOW('T', 206, int) #define TUNGETFEATURES _IOR('T', 207, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 @@ -48,6 +49,12 @@ #define IFF_NO_PI 0x1000 #define IFF_ONE_QUEUE 0x2000 +/* Features for GSO (TUNSETOFFLOAD). */ +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ + struct tun_pi { unsigned short flags; __be16 proto; -- cgit v1.2.3 From f43798c27684ab925adde7d8acc34c78c6e50df8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jul 2008 03:48:02 -0700 Subject: tun: Allow GSO using virtio_net_hdr Add a IFF_VNET_HDR flag. This uses the same ABI as virtio_net (ie. prepending struct virtio_net_hdr to packets) to indicate GSO and checksum information. Signed-off-by: Rusty Russell Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++-- include/linux/if_tun.h | 2 ++ 2 files changed, 94 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a314955e6994..aa4ee4439f04 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) }; struct sk_buff *skb; size_t len = count, align = 0; + struct virtio_net_hdr gso = { 0 }; if (!(tun->flags & TUN_NO_PI)) { if ((len -= sizeof(pi)) > count) @@ -292,6 +294,17 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, return -EFAULT; } + if (tun->flags & TUN_VNET_HDR) { + if ((len -= sizeof(gso)) > count) + return -EINVAL; + + if (memcpy_fromiovec((void *)&gso, iv, sizeof(gso))) + return -EFAULT; + + if (gso.hdr_len > len) + return -EINVAL; + } + if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { align = NET_IP_ALIGN; if (unlikely(len < ETH_HLEN)) @@ -311,6 +324,16 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, return -EFAULT; } + if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + if (!skb_partial_csum_set(skb, gso.csum_start, + gso.csum_offset)) { + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } + } else if (tun->flags & TUN_NOCHECKSUM) + skb->ip_summed = CHECKSUM_UNNECESSARY; + switch (tun->flags & TUN_TYPE_MASK) { case TUN_TUN_DEV: if (tun->flags & TUN_NO_PI) { @@ -337,8 +360,35 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, break; }; - if (tun->flags & TUN_NOCHECKSUM) - skb->ip_summed = CHECKSUM_UNNECESSARY; + if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { + pr_debug("GSO!\n"); + switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { + case VIRTIO_NET_HDR_GSO_TCPV4: + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + break; + case VIRTIO_NET_HDR_GSO_TCPV6: + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + break; + default: + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } + + if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + + skb_shinfo(skb)->gso_size = gso.gso_size; + if (skb_shinfo(skb)->gso_size == 0) { + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } + + /* Header must be checked, and gso_segs computed. */ + skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; + skb_shinfo(skb)->gso_segs = 0; + } netif_rx_ni(skb); tun->dev->last_rx = jiffies; @@ -384,6 +434,39 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, total += sizeof(pi); } + if (tun->flags & TUN_VNET_HDR) { + struct virtio_net_hdr gso = { 0 }; /* no info leak */ + if ((len -= sizeof(gso)) < 0) + return -EINVAL; + + if (skb_is_gso(skb)) { + struct skb_shared_info *sinfo = skb_shinfo(skb); + + /* This is a hint as to how much should be linear. */ + gso.hdr_len = skb_headlen(skb); + gso.gso_size = sinfo->gso_size; + if (sinfo->gso_type & SKB_GSO_TCPV4) + gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + else if (sinfo->gso_type & SKB_GSO_TCPV6) + gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else + BUG(); + if (sinfo->gso_type & SKB_GSO_TCP_ECN) + gso.gso_type |= VIRTIO_NET_HDR_GSO_ECN; + } else + gso.gso_type = VIRTIO_NET_HDR_GSO_NONE; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + gso.csum_start = skb->csum_start - skb_headroom(skb); + gso.csum_offset = skb->csum_offset; + } /* else everything is zero */ + + if (unlikely(memcpy_toiovec(iv, (void *)&gso, sizeof(gso)))) + return -EFAULT; + total += sizeof(gso); + } + len = min_t(int, skb->len, len); skb_copy_datagram_iovec(skb, 0, iv, len); @@ -598,6 +681,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) else tun->flags &= ~TUN_ONE_QUEUE; + if (ifr->ifr_flags & IFF_VNET_HDR) + tun->flags |= TUN_VNET_HDR; + else + tun->flags &= ~TUN_VNET_HDR; + file->private_data = tun; tun->attached = 1; get_net(dev_net(tun->dev)); @@ -684,7 +772,8 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, /* Currently this just means: "what IFF flags are valid?". * This is needed because we never checked for invalid flags on * TUNSETIFF. */ - return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE, + return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | + IFF_VNET_HDR, (unsigned int __user*)argp); } diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 3f0a0995d449..563fae542da6 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -31,6 +31,7 @@ #define TUN_NO_PI 0x0040 #define TUN_ONE_QUEUE 0x0080 #define TUN_PERSIST 0x0100 +#define TUN_VNET_HDR 0x0200 /* Ioctl defines */ #define TUNSETNOCSUM _IOW('T', 200, int) @@ -48,6 +49,7 @@ #define IFF_TAP 0x0002 #define IFF_NO_PI 0x1000 #define IFF_ONE_QUEUE 0x2000 +#define IFF_VNET_HDR 0x4000 /* Features for GSO (TUNSETOFFLOAD). */ #define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ -- cgit v1.2.3 From 02c62304e6af60f1963695c6bc1bbffe619aa585 Mon Sep 17 00:00:00 2001 From: "Alan D. Brunelle" Date: Wed, 11 Jun 2008 09:12:52 +0200 Subject: Added in user-injected messages into blk traces This allows a user to annotate the blk trace stream: writing a suitable message to {/sys/kernel/debug}/block//msg will have it propagated into the trace stream. Signed-off-by: Alan D. Brunelle Signed-off-by: Jens Axboe --- block/blktrace.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/blktrace_api.h | 1 + 2 files changed, 46 insertions(+) (limited to 'include/linux') diff --git a/block/blktrace.c b/block/blktrace.c index 8d3a27780260..eb9651ccb241 100644 --- a/block/blktrace.c +++ b/block/blktrace.c @@ -244,6 +244,7 @@ err: static void blk_trace_cleanup(struct blk_trace *bt) { relay_close(bt->rchan); + debugfs_remove(bt->msg_file); debugfs_remove(bt->dropped_file); blk_remove_tree(bt->dir); free_percpu(bt->sequence); @@ -291,6 +292,44 @@ static const struct file_operations blk_dropped_fops = { .read = blk_dropped_read, }; +static int blk_msg_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + + return 0; +} + +static ssize_t blk_msg_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char *msg; + struct blk_trace *bt; + + if (count > BLK_TN_MAX_MSG) + return -EINVAL; + + msg = kmalloc(count, GFP_KERNEL); + if (msg == NULL) + return -ENOMEM; + + if (copy_from_user(msg, buffer, count)) { + kfree(msg); + return -EFAULT; + } + + bt = filp->private_data; + __trace_note_message(bt, "%s", msg); + kfree(msg); + + return count; +} + +static const struct file_operations blk_msg_fops = { + .owner = THIS_MODULE, + .open = blk_msg_open, + .write = blk_msg_write, +}; + /* * Keep track of how many times we encountered a full subbuffer, to aid * the user space app in telling how many lost events there were. @@ -380,6 +419,10 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, if (!bt->dropped_file) goto err; + bt->msg_file = debugfs_create_file("msg", 0222, dir, bt, &blk_msg_fops); + if (!bt->msg_file) + goto err; + bt->rchan = relay_open("trace", dir, buts->buf_size, buts->buf_nr, &blk_relay_callbacks, bt); if (!bt->rchan) @@ -409,6 +452,8 @@ err: if (dir) blk_remove_tree(dir); if (bt) { + if (bt->msg_file) + debugfs_remove(bt->msg_file); if (bt->dropped_file) debugfs_remove(bt->dropped_file); free_percpu(bt->sequence); diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index e3ef903aae88..d084b8d227a5 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -129,6 +129,7 @@ struct blk_trace { u32 dev; struct dentry *dir; struct dentry *dropped_file; + struct dentry *msg_file; atomic_t dropped; }; -- cgit v1.2.3 From 244b4d56f85bcd11b21ab0b94845a3dabeed5c10 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 12 Jun 2008 20:12:36 +0200 Subject: block: kill request_queue_t Everything was moved to struct request_queue a few kernel revisions ago, maintaining the deprecated typedef to avoid breaking things. Now the time has come to get rid of that typedef. Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d2a1b71e93c3..6a3da6717135 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -23,7 +23,6 @@ struct scsi_ioctl_command; struct request_queue; -typedef struct request_queue request_queue_t __deprecated; struct elevator_queue; typedef struct elevator_queue elevator_t; struct request_pm_state; -- cgit v1.2.3 From 51d654e1d885607a6edd02b337105fa5c28b6d33 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 17 Jun 2008 18:59:56 +0200 Subject: block: Globalize bio_set and bio_vec_slab Move struct bio_set and biovec_slab definitions to bio.h so they can be used outside of bio.c. Signed-off-by: Martin K. Petersen Reviewed-by: Jeff Moyer Signed-off-by: Jens Axboe --- fs/bio.c | 30 ++---------------------------- include/linux/bio.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/fs/bio.c b/fs/bio.c index 78562574cb52..7a6598abc967 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -28,25 +28,10 @@ #include #include /* for struct sg_iovec */ -#define BIO_POOL_SIZE 2 - static struct kmem_cache *bio_slab __read_mostly; -#define BIOVEC_NR_POOLS 6 - -/* - * a small number of entries is fine, not going to be performance critical. - * basically we just need to survive - */ -#define BIO_SPLIT_ENTRIES 2 mempool_t *bio_split_pool __read_mostly; -struct biovec_slab { - int nr_vecs; - char *name; - struct kmem_cache *slab; -}; - /* * if you change this list, also change bvec_alloc or things will * break badly! cannot be bigger than what you can fit into an @@ -59,24 +44,13 @@ static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { }; #undef BV -/* - * bio_set is used to allow other portions of the IO system to - * allocate their own private memory pools for bio and iovec structures. - * These memory pools in turn all allocate from the bio_slab - * and the bvec_slabs[]. - */ -struct bio_set { - mempool_t *bio_pool; - mempool_t *bvec_pools[BIOVEC_NR_POOLS]; -}; - /* * fs_bio_set is the bio_set containing bio and iovec memory pools used by * IO code that does not need private memory pools. */ -static struct bio_set *fs_bio_set; +struct bio_set *fs_bio_set; -static inline struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs) +struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs) { struct bio_vec *bvl; diff --git a/include/linux/bio.h b/include/linux/bio.h index 61c15eaf3fb3..49dfb3cb7460 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -333,6 +333,35 @@ extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, int, int); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); +extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); + +/* + * bio_set is used to allow other portions of the IO system to + * allocate their own private memory pools for bio and iovec structures. + * These memory pools in turn all allocate from the bio_slab + * and the bvec_slabs[]. + */ +#define BIO_POOL_SIZE 2 +#define BIOVEC_NR_POOLS 6 + +struct bio_set { + mempool_t *bio_pool; + mempool_t *bvec_pools[BIOVEC_NR_POOLS]; +}; + +struct biovec_slab { + int nr_vecs; + char *name; + struct kmem_cache *slab; +}; + +extern struct bio_set *fs_bio_set; + +/* + * a small number of entries is fine, not going to be performance critical. + * basically we just need to survive + */ +#define BIO_SPLIT_ENTRIES 2 #ifdef CONFIG_HIGHMEM /* -- cgit v1.2.3 From 7ba1ba12eeef0aa7113beb16410ef8b7c748e18b Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Mon, 30 Jun 2008 20:04:41 +0200 Subject: block: Block layer data integrity support Some block devices support verifying the integrity of requests by way of checksums or other protection information that is submitted along with the I/O. This patch implements support for generating and verifying integrity metadata, as well as correctly merging, splitting and cloning bios and requests that have this extra information attached. See Documentation/block/data-integrity.txt for more information. Signed-off-by: Martin K. Petersen Signed-off-by: Jens Axboe --- block/Kconfig | 12 + block/Makefile | 1 + block/blk-core.c | 7 + block/blk-integrity.c | 382 ++++++++++++++++++++++++++ block/blk-merge.c | 3 + block/blk.h | 8 + block/elevator.c | 6 + fs/Makefile | 1 + fs/bio-integrity.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/bio.c | 32 ++- include/linux/bio.h | 94 ++++++- include/linux/blkdev.h | 105 ++++++++ include/linux/genhd.h | 3 + 13 files changed, 1355 insertions(+), 7 deletions(-) create mode 100644 block/blk-integrity.c create mode 100644 fs/bio-integrity.c (limited to 'include/linux') diff --git a/block/Kconfig b/block/Kconfig index 3e97f2bc446f..1ab7c15c8d7a 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -81,6 +81,18 @@ config BLK_DEV_BSG If unsure, say N. +config BLK_DEV_INTEGRITY + bool "Block layer data integrity support" + ---help--- + Some storage devices allow extra information to be + stored/retrieved to help protect the data. The block layer + data integrity option provides hooks which can be used by + filesystems to ensure better data integrity. + + Say yes here if you have a storage device that provides the + T10/SCSI Data Integrity Field or the T13/ATA External Path + Protection. If in doubt, say N. + endif # BLOCK config BLOCK_COMPAT diff --git a/block/Makefile b/block/Makefile index 5a43c7d79594..045f7b62e4bb 100644 --- a/block/Makefile +++ b/block/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o +obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/block/blk-core.c b/block/blk-core.c index 1905aaba49fb..e0fb0bcc0c17 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -143,6 +143,10 @@ static void req_bio_endio(struct request *rq, struct bio *bio, bio->bi_size -= nbytes; bio->bi_sector += (nbytes >> 9); + + if (bio_integrity(bio)) + bio_integrity_advance(bio, nbytes); + if (bio->bi_size == 0) bio_endio(bio, error); } else { @@ -1381,6 +1385,9 @@ end_io: */ blk_partition_remap(bio); + if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) + goto end_io; + if (old_sector != -1) blk_add_trace_remap(q, bio, old_dev, bio->bi_sector, old_sector); diff --git a/block/blk-integrity.c b/block/blk-integrity.c new file mode 100644 index 000000000000..65f23ef38bbe --- /dev/null +++ b/block/blk-integrity.c @@ -0,0 +1,382 @@ +/* + * blk-integrity.c - Block layer data integrity extensions + * + * Copyright (C) 2007, 2008 Oracle Corporation + * Written by: Martin K. Petersen + * + * 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. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include +#include +#include +#include + +#include "blk.h" + +static struct kmem_cache *integrity_cachep; + +/** + * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements + * @rq: request with integrity metadata attached + * + * Description: Returns the number of elements required in a + * scatterlist corresponding to the integrity metadata in a request. + */ +int blk_rq_count_integrity_sg(struct request *rq) +{ + struct bio_vec *iv, *ivprv; + struct req_iterator iter; + unsigned int segments; + + ivprv = NULL; + segments = 0; + + rq_for_each_integrity_segment(iv, rq, iter) { + + if (!ivprv || !BIOVEC_PHYS_MERGEABLE(ivprv, iv)) + segments++; + + ivprv = iv; + } + + return segments; +} +EXPORT_SYMBOL(blk_rq_count_integrity_sg); + +/** + * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist + * @rq: request with integrity metadata attached + * @sglist: target scatterlist + * + * Description: Map the integrity vectors in request into a + * scatterlist. The scatterlist must be big enough to hold all + * elements. I.e. sized using blk_rq_count_integrity_sg(). + */ +int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist) +{ + struct bio_vec *iv, *ivprv; + struct req_iterator iter; + struct scatterlist *sg; + unsigned int segments; + + ivprv = NULL; + sg = NULL; + segments = 0; + + rq_for_each_integrity_segment(iv, rq, iter) { + + if (ivprv) { + if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv)) + goto new_segment; + + sg->length += iv->bv_len; + } else { +new_segment: + if (!sg) + sg = sglist; + else { + sg->page_link &= ~0x02; + sg = sg_next(sg); + } + + sg_set_page(sg, iv->bv_page, iv->bv_len, iv->bv_offset); + segments++; + } + + ivprv = iv; + } + + if (sg) + sg_mark_end(sg); + + return segments; +} +EXPORT_SYMBOL(blk_rq_map_integrity_sg); + +/** + * blk_integrity_compare - Compare integrity profile of two block devices + * @b1: Device to compare + * @b2: Device to compare + * + * Description: Meta-devices like DM and MD need to verify that all + * sub-devices use the same integrity format before advertising to + * upper layers that they can send/receive integrity metadata. This + * function can be used to check whether two block devices have + * compatible integrity formats. + */ +int blk_integrity_compare(struct block_device *bd1, struct block_device *bd2) +{ + struct blk_integrity *b1 = bd1->bd_disk->integrity; + struct blk_integrity *b2 = bd2->bd_disk->integrity; + + BUG_ON(bd1->bd_disk == NULL); + BUG_ON(bd2->bd_disk == NULL); + + if (!b1 || !b2) + return 0; + + if (b1->sector_size != b2->sector_size) { + printk(KERN_ERR "%s: %s/%s sector sz %u != %u\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->sector_size, b2->sector_size); + return -1; + } + + if (b1->tuple_size != b2->tuple_size) { + printk(KERN_ERR "%s: %s/%s tuple sz %u != %u\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->tuple_size, b2->tuple_size); + return -1; + } + + if (b1->tag_size && b2->tag_size && (b1->tag_size != b2->tag_size)) { + printk(KERN_ERR "%s: %s/%s tag sz %u != %u\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->tag_size, b2->tag_size); + return -1; + } + + if (strcmp(b1->name, b2->name)) { + printk(KERN_ERR "%s: %s/%s type %s != %s\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->name, b2->name); + return -1; + } + + return 0; +} +EXPORT_SYMBOL(blk_integrity_compare); + +struct integrity_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct blk_integrity *, char *); + ssize_t (*store)(struct blk_integrity *, const char *, size_t); +}; + +static ssize_t integrity_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct blk_integrity *bi = + container_of(kobj, struct blk_integrity, kobj); + struct integrity_sysfs_entry *entry = + container_of(attr, struct integrity_sysfs_entry, attr); + + return entry->show(bi, page); +} + +static ssize_t integrity_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t count) +{ + struct blk_integrity *bi = + container_of(kobj, struct blk_integrity, kobj); + struct integrity_sysfs_entry *entry = + container_of(attr, struct integrity_sysfs_entry, attr); + ssize_t ret = 0; + + if (entry->store) + ret = entry->store(bi, page, count); + + return ret; +} + +static ssize_t integrity_format_show(struct blk_integrity *bi, char *page) +{ + if (bi != NULL && bi->name != NULL) + return sprintf(page, "%s\n", bi->name); + else + return sprintf(page, "none\n"); +} + +static ssize_t integrity_tag_size_show(struct blk_integrity *bi, char *page) +{ + if (bi != NULL) + return sprintf(page, "%u\n", bi->tag_size); + else + return sprintf(page, "0\n"); +} + +static ssize_t integrity_read_store(struct blk_integrity *bi, + const char *page, size_t count) +{ + char *p = (char *) page; + unsigned long val = simple_strtoul(p, &p, 10); + + if (val) + set_bit(INTEGRITY_FLAG_READ, &bi->flags); + else + clear_bit(INTEGRITY_FLAG_READ, &bi->flags); + + return count; +} + +static ssize_t integrity_read_show(struct blk_integrity *bi, char *page) +{ + return sprintf(page, "%d\n", + test_bit(INTEGRITY_FLAG_READ, &bi->flags) ? 1 : 0); +} + +static ssize_t integrity_write_store(struct blk_integrity *bi, + const char *page, size_t count) +{ + char *p = (char *) page; + unsigned long val = simple_strtoul(p, &p, 10); + + if (val) + set_bit(INTEGRITY_FLAG_WRITE, &bi->flags); + else + clear_bit(INTEGRITY_FLAG_WRITE, &bi->flags); + + return count; +} + +static ssize_t integrity_write_show(struct blk_integrity *bi, char *page) +{ + return sprintf(page, "%d\n", + test_bit(INTEGRITY_FLAG_WRITE, &bi->flags) ? 1 : 0); +} + +static struct integrity_sysfs_entry integrity_format_entry = { + .attr = { .name = "format", .mode = S_IRUGO }, + .show = integrity_format_show, +}; + +static struct integrity_sysfs_entry integrity_tag_size_entry = { + .attr = { .name = "tag_size", .mode = S_IRUGO }, + .show = integrity_tag_size_show, +}; + +static struct integrity_sysfs_entry integrity_read_entry = { + .attr = { .name = "read_verify", .mode = S_IRUGO | S_IWUSR }, + .show = integrity_read_show, + .store = integrity_read_store, +}; + +static struct integrity_sysfs_entry integrity_write_entry = { + .attr = { .name = "write_generate", .mode = S_IRUGO | S_IWUSR }, + .show = integrity_write_show, + .store = integrity_write_store, +}; + +static struct attribute *integrity_attrs[] = { + &integrity_format_entry.attr, + &integrity_tag_size_entry.attr, + &integrity_read_entry.attr, + &integrity_write_entry.attr, + NULL, +}; + +static struct sysfs_ops integrity_ops = { + .show = &integrity_attr_show, + .store = &integrity_attr_store, +}; + +static int __init blk_dev_integrity_init(void) +{ + integrity_cachep = kmem_cache_create("blkdev_integrity", + sizeof(struct blk_integrity), + 0, SLAB_PANIC, NULL); + return 0; +} +subsys_initcall(blk_dev_integrity_init); + +static void blk_integrity_release(struct kobject *kobj) +{ + struct blk_integrity *bi = + container_of(kobj, struct blk_integrity, kobj); + + kmem_cache_free(integrity_cachep, bi); +} + +static struct kobj_type integrity_ktype = { + .default_attrs = integrity_attrs, + .sysfs_ops = &integrity_ops, + .release = blk_integrity_release, +}; + +/** + * blk_integrity_register - Register a gendisk as being integrity-capable + * @disk: struct gendisk pointer to make integrity-aware + * @template: integrity profile + * + * Description: When a device needs to advertise itself as being able + * to send/receive integrity metadata it must use this function to + * register the capability with the block layer. The template is a + * blk_integrity struct with values appropriate for the underlying + * hardware. See Documentation/block/data-integrity.txt. + */ +int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template) +{ + struct blk_integrity *bi; + + BUG_ON(disk == NULL); + BUG_ON(template == NULL); + + if (disk->integrity == NULL) { + bi = kmem_cache_alloc(integrity_cachep, GFP_KERNEL | __GFP_ZERO); + if (!bi) + return -1; + + if (kobject_init_and_add(&bi->kobj, &integrity_ktype, + &disk->dev.kobj, "%s", "integrity")) { + kmem_cache_free(integrity_cachep, bi); + return -1; + } + + kobject_uevent(&bi->kobj, KOBJ_ADD); + + set_bit(INTEGRITY_FLAG_READ, &bi->flags); + set_bit(INTEGRITY_FLAG_WRITE, &bi->flags); + bi->sector_size = disk->queue->hardsect_size; + disk->integrity = bi; + } else + bi = disk->integrity; + + /* Use the provided profile as template */ + bi->name = template->name; + bi->generate_fn = template->generate_fn; + bi->verify_fn = template->verify_fn; + bi->tuple_size = template->tuple_size; + bi->set_tag_fn = template->set_tag_fn; + bi->get_tag_fn = template->get_tag_fn; + bi->tag_size = template->tag_size; + + return 0; +} +EXPORT_SYMBOL(blk_integrity_register); + +/** + * blk_integrity_unregister - Remove block integrity profile + * @disk: disk whose integrity profile to deallocate + * + * Description: This function frees all memory used by the block + * integrity profile. To be called at device teardown. + */ +void blk_integrity_unregister(struct gendisk *disk) +{ + struct blk_integrity *bi; + + if (!disk || !disk->integrity) + return; + + bi = disk->integrity; + + kobject_uevent(&bi->kobj, KOBJ_REMOVE); + kobject_del(&bi->kobj); + kobject_put(&disk->dev.kobj); + kmem_cache_free(integrity_cachep, bi); +} +EXPORT_SYMBOL(blk_integrity_unregister); diff --git a/block/blk-merge.c b/block/blk-merge.c index 651136aae76e..5efc9e7a68b7 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -441,6 +441,9 @@ static int attempt_merge(struct request_queue *q, struct request *req, || next->special) return 0; + if (blk_integrity_rq(req) != blk_integrity_rq(next)) + return 0; + /* * If we are allowed to merge, then append bio list * from next to rq and release next. merge_requests_fn diff --git a/block/blk.h b/block/blk.h index 59776ab4742a..c79f30e1df52 100644 --- a/block/blk.h +++ b/block/blk.h @@ -51,4 +51,12 @@ static inline int queue_congestion_off_threshold(struct request_queue *q) return q->nr_congestion_off; } +#if defined(CONFIG_BLK_DEV_INTEGRITY) + +#define rq_for_each_integrity_segment(bvl, _rq, _iter) \ + __rq_for_each_bio(_iter.bio, _rq) \ + bip_for_each_vec(bvl, _iter.bio->bi_integrity, _iter.i) + +#endif /* BLK_DEV_INTEGRITY */ + #endif diff --git a/block/elevator.c b/block/elevator.c index 902dd1344d56..1f5bfe696026 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -86,6 +86,12 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio) if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special) return 0; + /* + * only merge integrity protected bio into ditto rq + */ + if (bio_integrity(bio) != blk_integrity_rq(rq)) + return 0; + if (!elv_iosched_allow_merge(rq, bio)) return 0; diff --git a/fs/Makefile b/fs/Makefile index 1e7a11bd4da1..277b079dec9e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -19,6 +19,7 @@ else obj-y += no-block.o endif +obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_INOTIFY_USER) += inotify_user.o obj-$(CONFIG_EPOLL) += eventpoll.o diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c new file mode 100644 index 000000000000..31b08878913d --- /dev/null +++ b/fs/bio-integrity.c @@ -0,0 +1,708 @@ +/* + * bio-integrity.c - bio data integrity extensions + * + * Copyright (C) 2007, 2008 Oracle Corporation + * Written by: Martin K. Petersen + * + * 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. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include +#include +#include +#include + +static struct kmem_cache *bio_integrity_slab __read_mostly; +static struct workqueue_struct *kintegrityd_wq; + +/** + * bio_integrity_alloc_bioset - Allocate integrity payload and attach it to bio + * @bio: bio to attach integrity metadata to + * @gfp_mask: Memory allocation mask + * @nr_vecs: Number of integrity metadata scatter-gather elements + * @bs: bio_set to allocate from + * + * Description: This function prepares a bio for attaching integrity + * metadata. nr_vecs specifies the maximum number of pages containing + * integrity metadata that can be attached. + */ +struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *bio, gfp_t gfp_mask, unsigned int nr_vecs, struct bio_set *bs) +{ + struct bio_integrity_payload *bip; + struct bio_vec *iv; + unsigned long idx; + + BUG_ON(bio == NULL); + + bip = mempool_alloc(bs->bio_integrity_pool, gfp_mask); + if (unlikely(bip == NULL)) { + printk(KERN_ERR "%s: could not alloc bip\n", __func__); + return NULL; + } + + memset(bip, 0, sizeof(*bip)); + + iv = bvec_alloc_bs(gfp_mask, nr_vecs, &idx, bs); + if (unlikely(iv == NULL)) { + printk(KERN_ERR "%s: could not alloc bip_vec\n", __func__); + mempool_free(bip, bs->bio_integrity_pool); + return NULL; + } + + bip->bip_pool = idx; + bip->bip_vec = iv; + bip->bip_bio = bio; + bio->bi_integrity = bip; + + return bip; +} +EXPORT_SYMBOL(bio_integrity_alloc_bioset); + +/** + * bio_integrity_alloc - Allocate integrity payload and attach it to bio + * @bio: bio to attach integrity metadata to + * @gfp_mask: Memory allocation mask + * @nr_vecs: Number of integrity metadata scatter-gather elements + * + * Description: This function prepares a bio for attaching integrity + * metadata. nr_vecs specifies the maximum number of pages containing + * integrity metadata that can be attached. + */ +struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, gfp_t gfp_mask, unsigned int nr_vecs) +{ + return bio_integrity_alloc_bioset(bio, gfp_mask, nr_vecs, fs_bio_set); +} +EXPORT_SYMBOL(bio_integrity_alloc); + +/** + * bio_integrity_free - Free bio integrity payload + * @bio: bio containing bip to be freed + * @bs: bio_set this bio was allocated from + * + * Description: Used to free the integrity portion of a bio. Usually + * called from bio_free(). + */ +void bio_integrity_free(struct bio *bio, struct bio_set *bs) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + + BUG_ON(bip == NULL); + + /* A cloned bio doesn't own the integrity metadata */ + if (!bio_flagged(bio, BIO_CLONED) && bip->bip_buf != NULL) + kfree(bip->bip_buf); + + mempool_free(bip->bip_vec, bs->bvec_pools[bip->bip_pool]); + mempool_free(bip, bs->bio_integrity_pool); + + bio->bi_integrity = NULL; +} +EXPORT_SYMBOL(bio_integrity_free); + +/** + * bio_integrity_add_page - Attach integrity metadata + * @bio: bio to update + * @page: page containing integrity metadata + * @len: number of bytes of integrity metadata in page + * @offset: start offset within page + * + * Description: Attach a page containing integrity metadata to bio. + */ +int bio_integrity_add_page(struct bio *bio, struct page *page, + unsigned int len, unsigned int offset) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_vec *iv; + + if (bip->bip_vcnt >= bvec_nr_vecs(bip->bip_pool)) { + printk(KERN_ERR "%s: bip_vec full\n", __func__); + return 0; + } + + iv = bip_vec_idx(bip, bip->bip_vcnt); + BUG_ON(iv == NULL); + BUG_ON(iv->bv_page != NULL); + + iv->bv_page = page; + iv->bv_len = len; + iv->bv_offset = offset; + bip->bip_vcnt++; + + return len; +} +EXPORT_SYMBOL(bio_integrity_add_page); + +/** + * bio_integrity_enabled - Check whether integrity can be passed + * @bio: bio to check + * + * Description: Determines whether bio_integrity_prep() can be called + * on this bio or not. bio data direction and target device must be + * set prior to calling. The functions honors the write_generate and + * read_verify flags in sysfs. + */ +int bio_integrity_enabled(struct bio *bio) +{ + /* Already protected? */ + if (bio_integrity(bio)) + return 0; + + return bdev_integrity_enabled(bio->bi_bdev, bio_data_dir(bio)); +} +EXPORT_SYMBOL(bio_integrity_enabled); + +/** + * bio_integrity_hw_sectors - Convert 512b sectors to hardware ditto + * @bi: blk_integrity profile for device + * @sectors: Number of 512 sectors to convert + * + * Description: The block layer calculates everything in 512 byte + * sectors but integrity metadata is done in terms of the hardware + * sector size of the storage device. Convert the block layer sectors + * to physical sectors. + */ +static inline unsigned int bio_integrity_hw_sectors(struct blk_integrity *bi, unsigned int sectors) +{ + /* At this point there are only 512b or 4096b DIF/EPP devices */ + if (bi->sector_size == 4096) + return sectors >>= 3; + + return sectors; +} + +/** + * bio_integrity_tag_size - Retrieve integrity tag space + * @bio: bio to inspect + * + * Description: Returns the maximum number of tag bytes that can be + * attached to this bio. Filesystems can use this to determine how + * much metadata to attach to an I/O. + */ +unsigned int bio_integrity_tag_size(struct bio *bio) +{ + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + + BUG_ON(bio->bi_size == 0); + + return bi->tag_size * (bio->bi_size / bi->sector_size); +} +EXPORT_SYMBOL(bio_integrity_tag_size); + +int bio_integrity_tag(struct bio *bio, void *tag_buf, unsigned int len, int set) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + unsigned int nr_sectors; + + BUG_ON(bip->bip_buf == NULL); + + if (bi->tag_size == 0) + return -1; + + nr_sectors = bio_integrity_hw_sectors(bi, DIV_ROUND_UP(len, bi->tag_size)); + + if (nr_sectors * bi->tuple_size > bip->bip_size) { + printk(KERN_ERR "%s: tag too big for bio: %u > %u\n", + __func__, nr_sectors * bi->tuple_size, bip->bip_size); + return -1; + } + + if (set) + bi->set_tag_fn(bip->bip_buf, tag_buf, nr_sectors); + else + bi->get_tag_fn(bip->bip_buf, tag_buf, nr_sectors); + + return 0; +} + +/** + * bio_integrity_set_tag - Attach a tag buffer to a bio + * @bio: bio to attach buffer to + * @tag_buf: Pointer to a buffer containing tag data + * @len: Length of the included buffer + * + * Description: Use this function to tag a bio by leveraging the extra + * space provided by devices formatted with integrity protection. The + * size of the integrity buffer must be <= to the size reported by + * bio_integrity_tag_size(). + */ +int bio_integrity_set_tag(struct bio *bio, void *tag_buf, unsigned int len) +{ + BUG_ON(bio_data_dir(bio) != WRITE); + + return bio_integrity_tag(bio, tag_buf, len, 1); +} +EXPORT_SYMBOL(bio_integrity_set_tag); + +/** + * bio_integrity_get_tag - Retrieve a tag buffer from a bio + * @bio: bio to retrieve buffer from + * @tag_buf: Pointer to a buffer for the tag data + * @len: Length of the target buffer + * + * Description: Use this function to retrieve the tag buffer from a + * completed I/O. The size of the integrity buffer must be <= to the + * size reported by bio_integrity_tag_size(). + */ +int bio_integrity_get_tag(struct bio *bio, void *tag_buf, unsigned int len) +{ + BUG_ON(bio_data_dir(bio) != READ); + + return bio_integrity_tag(bio, tag_buf, len, 0); +} +EXPORT_SYMBOL(bio_integrity_get_tag); + +/** + * bio_integrity_generate - Generate integrity metadata for a bio + * @bio: bio to generate integrity metadata for + * + * Description: Generates integrity metadata for a bio by calling the + * block device's generation callback function. The bio must have a + * bip attached with enough room to accommodate the generated + * integrity metadata. + */ +static void bio_integrity_generate(struct bio *bio) +{ + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + struct blk_integrity_exchg bix; + struct bio_vec *bv; + sector_t sector = bio->bi_sector; + unsigned int i, sectors, total; + void *prot_buf = bio->bi_integrity->bip_buf; + + total = 0; + bix.disk_name = bio->bi_bdev->bd_disk->disk_name; + bix.sector_size = bi->sector_size; + + bio_for_each_segment(bv, bio, i) { + void *kaddr = kmap_atomic(bv->bv_page, KM_USER0); + bix.data_buf = kaddr + bv->bv_offset; + bix.data_size = bv->bv_len; + bix.prot_buf = prot_buf; + bix.sector = sector; + + bi->generate_fn(&bix); + + sectors = bv->bv_len / bi->sector_size; + sector += sectors; + prot_buf += sectors * bi->tuple_size; + total += sectors * bi->tuple_size; + BUG_ON(total > bio->bi_integrity->bip_size); + + kunmap_atomic(kaddr, KM_USER0); + } +} + +/** + * bio_integrity_prep - Prepare bio for integrity I/O + * @bio: bio to prepare + * + * Description: Allocates a buffer for integrity metadata, maps the + * pages and attaches them to a bio. The bio must have data + * direction, target device and start sector set priot to calling. In + * the WRITE case, integrity metadata will be generated using the + * block device's integrity function. In the READ case, the buffer + * will be prepared for DMA and a suitable end_io handler set up. + */ +int bio_integrity_prep(struct bio *bio) +{ + struct bio_integrity_payload *bip; + struct blk_integrity *bi; + struct request_queue *q; + void *buf; + unsigned long start, end; + unsigned int len, nr_pages; + unsigned int bytes, offset, i; + unsigned int sectors; + + bi = bdev_get_integrity(bio->bi_bdev); + q = bdev_get_queue(bio->bi_bdev); + BUG_ON(bi == NULL); + BUG_ON(bio_integrity(bio)); + + sectors = bio_integrity_hw_sectors(bi, bio_sectors(bio)); + + /* Allocate kernel buffer for protection data */ + len = sectors * blk_integrity_tuple_size(bi); + buf = kmalloc(len, GFP_NOIO | __GFP_NOFAIL | q->bounce_gfp); + if (unlikely(buf == NULL)) { + printk(KERN_ERR "could not allocate integrity buffer\n"); + return -EIO; + } + + end = (((unsigned long) buf) + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + start = ((unsigned long) buf) >> PAGE_SHIFT; + nr_pages = end - start; + + /* Allocate bio integrity payload and integrity vectors */ + bip = bio_integrity_alloc(bio, GFP_NOIO, nr_pages); + if (unlikely(bip == NULL)) { + printk(KERN_ERR "could not allocate data integrity bioset\n"); + kfree(buf); + return -EIO; + } + + bip->bip_buf = buf; + bip->bip_size = len; + bip->bip_sector = bio->bi_sector; + + /* Map it */ + offset = offset_in_page(buf); + for (i = 0 ; i < nr_pages ; i++) { + int ret; + bytes = PAGE_SIZE - offset; + + if (len <= 0) + break; + + if (bytes > len) + bytes = len; + + ret = bio_integrity_add_page(bio, virt_to_page(buf), + bytes, offset); + + if (ret == 0) + return 0; + + if (ret < bytes) + break; + + buf += bytes; + len -= bytes; + offset = 0; + } + + /* Install custom I/O completion handler if read verify is enabled */ + if (bio_data_dir(bio) == READ) { + bip->bip_end_io = bio->bi_end_io; + bio->bi_end_io = bio_integrity_endio; + } + + /* Auto-generate integrity metadata if this is a write */ + if (bio_data_dir(bio) == WRITE) + bio_integrity_generate(bio); + + return 0; +} +EXPORT_SYMBOL(bio_integrity_prep); + +/** + * bio_integrity_verify - Verify integrity metadata for a bio + * @bio: bio to verify + * + * Description: This function is called to verify the integrity of a + * bio. The data in the bio io_vec is compared to the integrity + * metadata returned by the HBA. + */ +static int bio_integrity_verify(struct bio *bio) +{ + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + struct blk_integrity_exchg bix; + struct bio_vec *bv; + sector_t sector = bio->bi_integrity->bip_sector; + unsigned int i, sectors, total, ret; + void *prot_buf = bio->bi_integrity->bip_buf; + + ret = total = 0; + bix.disk_name = bio->bi_bdev->bd_disk->disk_name; + bix.sector_size = bi->sector_size; + + bio_for_each_segment(bv, bio, i) { + void *kaddr = kmap_atomic(bv->bv_page, KM_USER0); + bix.data_buf = kaddr + bv->bv_offset; + bix.data_size = bv->bv_len; + bix.prot_buf = prot_buf; + bix.sector = sector; + + ret = bi->verify_fn(&bix); + + if (ret) { + kunmap_atomic(kaddr, KM_USER0); + break; + } + + sectors = bv->bv_len / bi->sector_size; + sector += sectors; + prot_buf += sectors * bi->tuple_size; + total += sectors * bi->tuple_size; + BUG_ON(total > bio->bi_integrity->bip_size); + + kunmap_atomic(kaddr, KM_USER0); + } + + return ret; +} + +/** + * bio_integrity_verify_fn - Integrity I/O completion worker + * @work: Work struct stored in bio to be verified + * + * Description: This workqueue function is called to complete a READ + * request. The function verifies the transferred integrity metadata + * and then calls the original bio end_io function. + */ +static void bio_integrity_verify_fn(struct work_struct *work) +{ + struct bio_integrity_payload *bip = + container_of(work, struct bio_integrity_payload, bip_work); + struct bio *bio = bip->bip_bio; + int error = bip->bip_error; + + if (bio_integrity_verify(bio)) { + clear_bit(BIO_UPTODATE, &bio->bi_flags); + error = -EIO; + } + + /* Restore original bio completion handler */ + bio->bi_end_io = bip->bip_end_io; + + if (bio->bi_end_io) + bio->bi_end_io(bio, error); +} + +/** + * bio_integrity_endio - Integrity I/O completion function + * @bio: Protected bio + * @error: Pointer to errno + * + * Description: Completion for integrity I/O + * + * Normally I/O completion is done in interrupt context. However, + * verifying I/O integrity is a time-consuming task which must be run + * in process context. This function postpones completion + * accordingly. + */ +void bio_integrity_endio(struct bio *bio, int error) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + + BUG_ON(bip->bip_bio != bio); + + bip->bip_error = error; + INIT_WORK(&bip->bip_work, bio_integrity_verify_fn); + queue_work(kintegrityd_wq, &bip->bip_work); +} +EXPORT_SYMBOL(bio_integrity_endio); + +/** + * bio_integrity_mark_head - Advance bip_vec skip bytes + * @bip: Integrity vector to advance + * @skip: Number of bytes to advance it + */ +void bio_integrity_mark_head(struct bio_integrity_payload *bip, unsigned int skip) +{ + struct bio_vec *iv; + unsigned int i; + + bip_for_each_vec(iv, bip, i) { + if (skip == 0) { + bip->bip_idx = i; + return; + } else if (skip >= iv->bv_len) { + skip -= iv->bv_len; + } else { /* skip < iv->bv_len) */ + iv->bv_offset += skip; + iv->bv_len -= skip; + bip->bip_idx = i; + return; + } + } +} + +/** + * bio_integrity_mark_tail - Truncate bip_vec to be len bytes long + * @bip: Integrity vector to truncate + * @len: New length of integrity vector + */ +void bio_integrity_mark_tail(struct bio_integrity_payload *bip, unsigned int len) +{ + struct bio_vec *iv; + unsigned int i; + + bip_for_each_vec(iv, bip, i) { + if (len == 0) { + bip->bip_vcnt = i; + return; + } else if (len >= iv->bv_len) { + len -= iv->bv_len; + } else { /* len < iv->bv_len) */ + iv->bv_len = len; + len = 0; + } + } +} + +/** + * bio_integrity_advance - Advance integrity vector + * @bio: bio whose integrity vector to update + * @bytes_done: number of data bytes that have been completed + * + * Description: This function calculates how many integrity bytes the + * number of completed data bytes correspond to and advances the + * integrity vector accordingly. + */ +void bio_integrity_advance(struct bio *bio, unsigned int bytes_done) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + unsigned int nr_sectors; + + BUG_ON(bip == NULL); + BUG_ON(bi == NULL); + + nr_sectors = bio_integrity_hw_sectors(bi, bytes_done >> 9); + bio_integrity_mark_head(bip, nr_sectors * bi->tuple_size); +} +EXPORT_SYMBOL(bio_integrity_advance); + +/** + * bio_integrity_trim - Trim integrity vector + * @bio: bio whose integrity vector to update + * @offset: offset to first data sector + * @sectors: number of data sectors + * + * Description: Used to trim the integrity vector in a cloned bio. + * The ivec will be advanced corresponding to 'offset' data sectors + * and the length will be truncated corresponding to 'len' data + * sectors. + */ +void bio_integrity_trim(struct bio *bio, unsigned int offset, unsigned int sectors) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + unsigned int nr_sectors; + + BUG_ON(bip == NULL); + BUG_ON(bi == NULL); + BUG_ON(!bio_flagged(bio, BIO_CLONED)); + + nr_sectors = bio_integrity_hw_sectors(bi, sectors); + bip->bip_sector = bip->bip_sector + offset; + bio_integrity_mark_head(bip, offset * bi->tuple_size); + bio_integrity_mark_tail(bip, sectors * bi->tuple_size); +} +EXPORT_SYMBOL(bio_integrity_trim); + +/** + * bio_integrity_split - Split integrity metadata + * @bio: Protected bio + * @bp: Resulting bio_pair + * @sectors: Offset + * + * Description: Splits an integrity page into a bio_pair. + */ +void bio_integrity_split(struct bio *bio, struct bio_pair *bp, int sectors) +{ + struct blk_integrity *bi; + struct bio_integrity_payload *bip = bio->bi_integrity; + unsigned int nr_sectors; + + if (bio_integrity(bio) == 0) + return; + + bi = bdev_get_integrity(bio->bi_bdev); + BUG_ON(bi == NULL); + BUG_ON(bip->bip_vcnt != 1); + + nr_sectors = bio_integrity_hw_sectors(bi, sectors); + + bp->bio1.bi_integrity = &bp->bip1; + bp->bio2.bi_integrity = &bp->bip2; + + bp->iv1 = bip->bip_vec[0]; + bp->iv2 = bip->bip_vec[0]; + + bp->bip1.bip_vec = &bp->iv1; + bp->bip2.bip_vec = &bp->iv2; + + bp->iv1.bv_len = sectors * bi->tuple_size; + bp->iv2.bv_offset += sectors * bi->tuple_size; + bp->iv2.bv_len -= sectors * bi->tuple_size; + + bp->bip1.bip_sector = bio->bi_integrity->bip_sector; + bp->bip2.bip_sector = bio->bi_integrity->bip_sector + nr_sectors; + + bp->bip1.bip_vcnt = bp->bip2.bip_vcnt = 1; + bp->bip1.bip_idx = bp->bip2.bip_idx = 0; +} +EXPORT_SYMBOL(bio_integrity_split); + +/** + * bio_integrity_clone - Callback for cloning bios with integrity metadata + * @bio: New bio + * @bio_src: Original bio + * @bs: bio_set to allocate bip from + * + * Description: Called to allocate a bip when cloning a bio + */ +int bio_integrity_clone(struct bio *bio, struct bio *bio_src, struct bio_set *bs) +{ + struct bio_integrity_payload *bip_src = bio_src->bi_integrity; + struct bio_integrity_payload *bip; + + BUG_ON(bip_src == NULL); + + bip = bio_integrity_alloc_bioset(bio, GFP_NOIO, bip_src->bip_vcnt, bs); + + if (bip == NULL) + return -EIO; + + memcpy(bip->bip_vec, bip_src->bip_vec, + bip_src->bip_vcnt * sizeof(struct bio_vec)); + + bip->bip_sector = bip_src->bip_sector; + bip->bip_vcnt = bip_src->bip_vcnt; + bip->bip_idx = bip_src->bip_idx; + + return 0; +} +EXPORT_SYMBOL(bio_integrity_clone); + +int bioset_integrity_create(struct bio_set *bs, int pool_size) +{ + bs->bio_integrity_pool = mempool_create_slab_pool(pool_size, + bio_integrity_slab); + if (!bs->bio_integrity_pool) + return -1; + + return 0; +} +EXPORT_SYMBOL(bioset_integrity_create); + +void bioset_integrity_free(struct bio_set *bs) +{ + if (bs->bio_integrity_pool) + mempool_destroy(bs->bio_integrity_pool); +} +EXPORT_SYMBOL(bioset_integrity_free); + +void __init bio_integrity_init_slab(void) +{ + bio_integrity_slab = KMEM_CACHE(bio_integrity_payload, + SLAB_HWCACHE_ALIGN|SLAB_PANIC); +} +EXPORT_SYMBOL(bio_integrity_init_slab); + +static int __init integrity_init(void) +{ + kintegrityd_wq = create_workqueue("kintegrityd"); + + if (!kintegrityd_wq) + panic("Failed to create kintegrityd\n"); + + return 0; +} +subsys_initcall(integrity_init); diff --git a/fs/bio.c b/fs/bio.c index 7a6598abc967..7761c84c7032 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -50,6 +50,11 @@ static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { */ struct bio_set *fs_bio_set; +unsigned int bvec_nr_vecs(unsigned short idx) +{ + return bvec_slabs[idx].nr_vecs; +} + struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs) { struct bio_vec *bvl; @@ -91,6 +96,9 @@ void bio_free(struct bio *bio, struct bio_set *bio_set) mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]); } + if (bio_integrity(bio)) + bio_integrity_free(bio, bio_set); + mempool_free(bio, bio_set->bio_pool); } @@ -249,9 +257,19 @@ struct bio *bio_clone(struct bio *bio, gfp_t gfp_mask) { struct bio *b = bio_alloc_bioset(gfp_mask, bio->bi_max_vecs, fs_bio_set); - if (b) { - b->bi_destructor = bio_fs_destructor; - __bio_clone(b, bio); + if (!b) + return NULL; + + b->bi_destructor = bio_fs_destructor; + __bio_clone(b, bio); + + if (bio_integrity(bio)) { + int ret; + + ret = bio_integrity_clone(b, bio, fs_bio_set); + + if (ret < 0) + return NULL; } return b; @@ -1223,6 +1241,9 @@ struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors) bp->bio1.bi_private = bi; bp->bio2.bi_private = pool; + if (bio_integrity(bi)) + bio_integrity_split(bi, bp, first_sectors); + return bp; } @@ -1264,6 +1285,7 @@ void bioset_free(struct bio_set *bs) if (bs->bio_pool) mempool_destroy(bs->bio_pool); + bioset_integrity_free(bs); biovec_free_pools(bs); kfree(bs); @@ -1280,6 +1302,9 @@ struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size) if (!bs->bio_pool) goto bad; + if (bioset_integrity_create(bs, bio_pool_size)) + goto bad; + if (!biovec_create_pools(bs, bvec_pool_size)) return bs; @@ -1306,6 +1331,7 @@ static int __init init_bio(void) { bio_slab = KMEM_CACHE(bio, SLAB_HWCACHE_ALIGN|SLAB_PANIC); + bio_integrity_init_slab(); biovec_init_slabs(); fs_bio_set = bioset_create(BIO_POOL_SIZE, 2); diff --git a/include/linux/bio.h b/include/linux/bio.h index 49dfb3cb7460..6bfc3e8d9d89 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -64,6 +64,7 @@ struct bio_vec { struct bio_set; struct bio; +struct bio_integrity_payload; typedef void (bio_end_io_t) (struct bio *, int); typedef void (bio_destructor_t) (struct bio *); @@ -112,6 +113,9 @@ struct bio { atomic_t bi_cnt; /* pin count */ void *bi_private; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + struct bio_integrity_payload *bi_integrity; /* data integrity */ +#endif bio_destructor_t *bi_destructor; /* destructor */ }; @@ -271,6 +275,29 @@ static inline void *bio_data(struct bio *bio) */ #define bio_get(bio) atomic_inc(&(bio)->bi_cnt) +#if defined(CONFIG_BLK_DEV_INTEGRITY) +/* + * bio integrity payload + */ +struct bio_integrity_payload { + struct bio *bip_bio; /* parent bio */ + struct bio_vec *bip_vec; /* integrity data vector */ + + sector_t bip_sector; /* virtual start sector */ + + void *bip_buf; /* generated integrity data */ + bio_end_io_t *bip_end_io; /* saved I/O completion fn */ + + int bip_error; /* saved I/O error */ + unsigned int bip_size; + + unsigned short bip_pool; /* pool the ivec came from */ + unsigned short bip_vcnt; /* # of integrity bio_vecs */ + unsigned short bip_idx; /* current bip_vec index */ + + struct work_struct bip_work; /* I/O completion */ +}; +#endif /* CONFIG_BLK_DEV_INTEGRITY */ /* * A bio_pair is used when we need to split a bio. @@ -283,10 +310,14 @@ static inline void *bio_data(struct bio *bio) * in bio2.bi_private */ struct bio_pair { - struct bio bio1, bio2; - struct bio_vec bv1, bv2; - atomic_t cnt; - int error; + struct bio bio1, bio2; + struct bio_vec bv1, bv2; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + struct bio_integrity_payload bip1, bip2; + struct bio_vec iv1, iv2; +#endif + atomic_t cnt; + int error; }; extern struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors); @@ -334,6 +365,7 @@ extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); +extern unsigned int bvec_nr_vecs(unsigned short idx); /* * bio_set is used to allow other portions of the IO system to @@ -346,6 +378,9 @@ extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set struct bio_set { mempool_t *bio_pool; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + mempool_t *bio_integrity_pool; +#endif mempool_t *bvec_pools[BIOVEC_NR_POOLS]; }; @@ -410,5 +445,56 @@ static inline char *__bio_kmap_irq(struct bio *bio, unsigned short idx, __bio_kmap_irq((bio), (bio)->bi_idx, (flags)) #define bio_kunmap_irq(buf,flags) __bio_kunmap_irq(buf, flags) +#if defined(CONFIG_BLK_DEV_INTEGRITY) + +#define bip_vec_idx(bip, idx) (&(bip->bip_vec[(idx)])) +#define bip_vec(bip) bip_vec_idx(bip, 0) + +#define __bip_for_each_vec(bvl, bip, i, start_idx) \ + for (bvl = bip_vec_idx((bip), (start_idx)), i = (start_idx); \ + i < (bip)->bip_vcnt; \ + bvl++, i++) + +#define bip_for_each_vec(bvl, bip, i) \ + __bip_for_each_vec(bvl, bip, i, (bip)->bip_idx) + +#define bio_integrity(bio) ((bio)->bi_integrity ? 1 : 0) + +extern struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *, gfp_t, unsigned int, struct bio_set *); +extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int); +extern void bio_integrity_free(struct bio *, struct bio_set *); +extern int bio_integrity_add_page(struct bio *, struct page *, unsigned int, unsigned int); +extern int bio_integrity_enabled(struct bio *bio); +extern int bio_integrity_set_tag(struct bio *, void *, unsigned int); +extern int bio_integrity_get_tag(struct bio *, void *, unsigned int); +extern int bio_integrity_prep(struct bio *); +extern void bio_integrity_endio(struct bio *, int); +extern void bio_integrity_advance(struct bio *, unsigned int); +extern void bio_integrity_trim(struct bio *, unsigned int, unsigned int); +extern void bio_integrity_split(struct bio *, struct bio_pair *, int); +extern int bio_integrity_clone(struct bio *, struct bio *, struct bio_set *); +extern int bioset_integrity_create(struct bio_set *, int); +extern void bioset_integrity_free(struct bio_set *); +extern void bio_integrity_init_slab(void); + +#else /* CONFIG_BLK_DEV_INTEGRITY */ + +#define bio_integrity(a) (0) +#define bioset_integrity_create(a, b) (0) +#define bio_integrity_prep(a) (0) +#define bio_integrity_enabled(a) (0) +#define bio_integrity_clone(a, b, c) (0) +#define bioset_integrity_free(a) do { } while (0) +#define bio_integrity_free(a, b) do { } while (0) +#define bio_integrity_endio(a, b) do { } while (0) +#define bio_integrity_advance(a, b) do { } while (0) +#define bio_integrity_trim(a, b, c) do { } while (0) +#define bio_integrity_split(a, b, c) do { } while (0) +#define bio_integrity_set_tag(a, b, c) do { } while (0) +#define bio_integrity_get_tag(a, b, c) do { } while (0) +#define bio_integrity_init_slab(a) do { } while (0) + +#endif /* CONFIG_BLK_DEV_INTEGRITY */ + #endif /* CONFIG_BLOCK */ #endif /* __LINUX_BIO_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 6a3da6717135..4a9ed45270ff 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -112,6 +112,7 @@ enum rq_flag_bits { __REQ_ALLOCED, /* request came from our alloc pool */ __REQ_RW_META, /* metadata io request */ __REQ_COPY_USER, /* contains copies of user pages */ + __REQ_INTEGRITY, /* integrity metadata has been remapped */ __REQ_NR_BITS, /* stops here */ }; @@ -134,6 +135,7 @@ enum rq_flag_bits { #define REQ_ALLOCED (1 << __REQ_ALLOCED) #define REQ_RW_META (1 << __REQ_RW_META) #define REQ_COPY_USER (1 << __REQ_COPY_USER) +#define REQ_INTEGRITY (1 << __REQ_INTEGRITY) #define BLK_MAX_CDB 16 @@ -865,6 +867,109 @@ void kblockd_flush_work(struct work_struct *work); MODULE_ALIAS("block-major-" __stringify(major) "-*") +#if defined(CONFIG_BLK_DEV_INTEGRITY) + +#define INTEGRITY_FLAG_READ 1 /* verify data integrity on read */ +#define INTEGRITY_FLAG_WRITE 2 /* generate data integrity on write */ + +struct blk_integrity_exchg { + void *prot_buf; + void *data_buf; + sector_t sector; + unsigned int data_size; + unsigned short sector_size; + const char *disk_name; +}; + +typedef void (integrity_gen_fn) (struct blk_integrity_exchg *); +typedef int (integrity_vrfy_fn) (struct blk_integrity_exchg *); +typedef void (integrity_set_tag_fn) (void *, void *, unsigned int); +typedef void (integrity_get_tag_fn) (void *, void *, unsigned int); + +struct blk_integrity { + integrity_gen_fn *generate_fn; + integrity_vrfy_fn *verify_fn; + integrity_set_tag_fn *set_tag_fn; + integrity_get_tag_fn *get_tag_fn; + + unsigned short flags; + unsigned short tuple_size; + unsigned short sector_size; + unsigned short tag_size; + + const char *name; + + struct kobject kobj; +}; + +extern int blk_integrity_register(struct gendisk *, struct blk_integrity *); +extern void blk_integrity_unregister(struct gendisk *); +extern int blk_integrity_compare(struct block_device *, struct block_device *); +extern int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); +extern int blk_rq_count_integrity_sg(struct request *); + +static inline unsigned short blk_integrity_tuple_size(struct blk_integrity *bi) +{ + if (bi) + return bi->tuple_size; + + return 0; +} + +static inline struct blk_integrity *bdev_get_integrity(struct block_device *bdev) +{ + return bdev->bd_disk->integrity; +} + +static inline unsigned int bdev_get_tag_size(struct block_device *bdev) +{ + struct blk_integrity *bi = bdev_get_integrity(bdev); + + if (bi) + return bi->tag_size; + + return 0; +} + +static inline int bdev_integrity_enabled(struct block_device *bdev, int rw) +{ + struct blk_integrity *bi = bdev_get_integrity(bdev); + + if (bi == NULL) + return 0; + + if (rw == READ && bi->verify_fn != NULL && + test_bit(INTEGRITY_FLAG_READ, &bi->flags)) + return 1; + + if (rw == WRITE && bi->generate_fn != NULL && + test_bit(INTEGRITY_FLAG_WRITE, &bi->flags)) + return 1; + + return 0; +} + +static inline int blk_integrity_rq(struct request *rq) +{ + BUG_ON(rq->bio == NULL); + + return bio_integrity(rq->bio); +} + +#else /* CONFIG_BLK_DEV_INTEGRITY */ + +#define blk_integrity_rq(rq) (0) +#define blk_rq_count_integrity_sg(a) (0) +#define blk_rq_map_integrity_sg(a, b) (0) +#define bdev_get_integrity(a) (0) +#define bdev_get_tag_size(a) (0) +#define blk_integrity_compare(a, b) (0) +#define blk_integrity_register(a, b) (0) +#define blk_integrity_unregister(a) do { } while (0); + +#endif /* CONFIG_BLK_DEV_INTEGRITY */ + + #else /* CONFIG_BLOCK */ /* * stubs for when the block layer is configured out diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ae7aec3cabee..524ec96f5a23 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -141,6 +141,9 @@ struct gendisk { struct disk_stats dkstats; #endif struct work_struct async_notify; +#ifdef CONFIG_BLK_DEV_INTEGRITY + struct blk_integrity *integrity; +#endif }; /* -- cgit v1.2.3 From da9cbc87395308a21465bd25441297bbba0477e1 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 30 Jun 2008 20:42:08 +0200 Subject: block: blkdev.h cleanup, move iocontext stuff to iocontext.h Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 17 ----------------- include/linux/iocontext.h | 18 ++++++++++++++++++ kernel/exit.c | 1 + kernel/fork.c | 1 + 4 files changed, 20 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4a9ed45270ff..443df75d2cde 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -33,12 +33,6 @@ struct sg_io_hdr; #define BLKDEV_MIN_RQ 4 #define BLKDEV_MAX_RQ 128 /* Default maximum */ -int put_io_context(struct io_context *ioc); -void exit_io_context(void); -struct io_context *get_io_context(gfp_t gfp_flags, int node); -struct io_context *alloc_io_context(gfp_t gfp_flags, int node); -void copy_io_context(struct io_context **pdst, struct io_context **psrc); - struct request; typedef void (rq_end_io_fn)(struct request *, int); @@ -981,17 +975,6 @@ static inline long nr_blockdev_pages(void) return 0; } -static inline void exit_io_context(void) -{ -} - -struct io_context; -static inline int put_io_context(struct io_context *ioc) -{ - return 1; -} - - #endif /* CONFIG_BLOCK */ #endif diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 2b7a1187cb29..08b987bccf89 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -99,4 +99,22 @@ static inline struct io_context *ioc_task_link(struct io_context *ioc) return NULL; } +#ifdef CONFIG_BLOCK +int put_io_context(struct io_context *ioc); +void exit_io_context(void); +struct io_context *get_io_context(gfp_t gfp_flags, int node); +struct io_context *alloc_io_context(gfp_t gfp_flags, int node); +void copy_io_context(struct io_context **pdst, struct io_context **psrc); +#else +static inline void exit_io_context(void) +{ +} + +struct io_context; +static inline int put_io_context(struct io_context *ioc) +{ + return 1; +} +#endif + #endif diff --git a/kernel/exit.c b/kernel/exit.c index 8f6185e69b69..ceb258782835 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/kernel/fork.c b/kernel/fork.c index 19908b26cf80..b71ccd09fc8d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 6e2401ad6f33de15ff00f78b88159f00a14f3b35 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 18 Jun 2008 10:15:02 +0200 Subject: block: integrity cleanups - No need to check for NULL bio, we'll get an immediate oops anyway. - Make bio_integrity() a proper function. Signed-off-by: Jens Axboe --- include/linux/bio.h | 9 ++++++++- include/linux/blkdev.h | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bio.h b/include/linux/bio.h index 6bfc3e8d9d89..0933a14e6414 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -458,7 +458,14 @@ static inline char *__bio_kmap_irq(struct bio *bio, unsigned short idx, #define bip_for_each_vec(bvl, bip, i) \ __bip_for_each_vec(bvl, bip, i, (bip)->bip_idx) -#define bio_integrity(bio) ((bio)->bi_integrity ? 1 : 0) +static inline int bio_integrity(struct bio *bio) +{ +#if defined(CONFIG_BLK_DEV_INTEGRITY) + return bio->bi_integrity != NULL; +#else + return 0; +#endif +} extern struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *, gfp_t, unsigned int, struct bio_set *); extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 443df75d2cde..d3ae9ad97213 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -860,7 +860,6 @@ void kblockd_flush_work(struct work_struct *work); #define MODULE_ALIAS_BLOCKDEV_MAJOR(major) \ MODULE_ALIAS("block-major-" __stringify(major) "-*") - #if defined(CONFIG_BLK_DEV_INTEGRITY) #define INTEGRITY_FLAG_READ 1 /* verify data integrity on read */ @@ -945,8 +944,6 @@ static inline int bdev_integrity_enabled(struct block_device *bdev, int rw) static inline int blk_integrity_rq(struct request *rq) { - BUG_ON(rq->bio == NULL); - return bio_integrity(rq->bio); } @@ -963,7 +960,6 @@ static inline int blk_integrity_rq(struct request *rq) #endif /* CONFIG_BLK_DEV_INTEGRITY */ - #else /* CONFIG_BLOCK */ /* * stubs for when the block layer is configured out -- cgit v1.2.3 From 0b07de85a76e1346e675f0e98437378932473df7 Mon Sep 17 00:00:00 2001 From: Adel Gadllah Date: Thu, 26 Jun 2008 13:48:27 +0200 Subject: allow userspace to modify scsi command filter on per device basis This patch exports the per-gendisk command filter to user space through sysfs, so it can be changed by the system administrator. All users of the old cmd filter have been converted to use the new one. Original patch from Peter Jones. Signed-off-by: Adel Gadllah Signed-off-by: Peter Jones Signed-off-by: Jens Axboe --- block/Makefile | 3 +- block/bsg.c | 38 ++++-- block/cmd-filter.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++ block/genhd.c | 2 + block/scsi_ioctl.c | 121 +----------------- drivers/scsi/sg.c | 40 ++---- include/linux/blkdev.h | 10 +- include/linux/genhd.h | 9 ++ 8 files changed, 389 insertions(+), 159 deletions(-) create mode 100644 block/cmd-filter.c (limited to 'include/linux') diff --git a/block/Makefile b/block/Makefile index 045f7b62e4bb..208000b0750d 100644 --- a/block/Makefile +++ b/block/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \ blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \ - blk-exec.o blk-merge.o ioctl.o genhd.o scsi_ioctl.o + blk-exec.o blk-merge.o ioctl.o genhd.o scsi_ioctl.o \ + cmd-filter.o obj-$(CONFIG_BLK_DEV_BSG) += bsg.o obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o diff --git a/block/bsg.c b/block/bsg.c index f0b7cd343216..439940c3a1ff 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -44,11 +44,12 @@ struct bsg_device { char name[BUS_ID_SIZE]; int max_queue; unsigned long flags; + struct blk_scsi_cmd_filter *cmd_filter; + mode_t *f_mode; }; enum { BSG_F_BLOCK = 1, - BSG_F_WRITE_PERM = 2, }; #define BSG_DEFAULT_CMDS 64 @@ -172,7 +173,7 @@ unlock: } static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, - struct sg_io_v4 *hdr, int has_write_perm) + struct sg_io_v4 *hdr, struct bsg_device *bd) { if (hdr->request_len > BLK_MAX_CDB) { rq->cmd = kzalloc(hdr->request_len, GFP_KERNEL); @@ -185,7 +186,8 @@ static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, return -EFAULT; if (hdr->subprotocol == BSG_SUB_PROTOCOL_SCSI_CMD) { - if (blk_verify_command(rq->cmd, has_write_perm)) + if (blk_cmd_filter_verify_command(bd->cmd_filter, rq->cmd, + bd->f_mode)) return -EPERM; } else if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -263,8 +265,7 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) rq = blk_get_request(q, rw, GFP_KERNEL); if (!rq) return ERR_PTR(-ENOMEM); - ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, - &bd->flags)); + ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, bd); if (ret) goto out; @@ -566,12 +567,23 @@ static inline void bsg_set_block(struct bsg_device *bd, struct file *file) set_bit(BSG_F_BLOCK, &bd->flags); } -static inline void bsg_set_write_perm(struct bsg_device *bd, struct file *file) +static void bsg_set_cmd_filter(struct bsg_device *bd, + struct file *file) { - if (file->f_mode & FMODE_WRITE) - set_bit(BSG_F_WRITE_PERM, &bd->flags); - else - clear_bit(BSG_F_WRITE_PERM, &bd->flags); + struct inode *inode; + struct gendisk *disk; + + if (!file) + return; + + inode = file->f_dentry->d_inode; + if (!inode) + return; + + disk = inode->i_bdev->bd_disk; + + bd->cmd_filter = &disk->cmd_filter; + bd->f_mode = &file->f_mode; } /* @@ -595,6 +607,8 @@ bsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) dprintk("%s: read %Zd bytes\n", bd->name, count); bsg_set_block(bd, file); + bsg_set_cmd_filter(bd, file); + bytes_read = 0; ret = __bsg_read(buf, count, bd, NULL, &bytes_read); *ppos = bytes_read; @@ -668,7 +682,7 @@ bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) dprintk("%s: write %Zd bytes\n", bd->name, count); bsg_set_block(bd, file); - bsg_set_write_perm(bd, file); + bsg_set_cmd_filter(bd, file); bytes_written = 0; ret = __bsg_write(bd, buf, count, &bytes_written); @@ -771,7 +785,9 @@ static struct bsg_device *bsg_add_device(struct inode *inode, } bd->queue = rq; + bsg_set_block(bd, file); + bsg_set_cmd_filter(bd, file); atomic_set(&bd->ref_count, 1); mutex_lock(&bsg_mutex); diff --git a/block/cmd-filter.c b/block/cmd-filter.c new file mode 100644 index 000000000000..35e327ceaa97 --- /dev/null +++ b/block/cmd-filter.c @@ -0,0 +1,325 @@ +/* + * Copyright 2004 Peter M. Jones + * + * 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. + * + * 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 Licens + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, + unsigned char *cmd, mode_t *f_mode) +{ + /* root can do any command. */ + if (capable(CAP_SYS_RAWIO)) + return 0; + + /* if there's no filter set, assume we're filtering everything out */ + if (!filter) + return -EPERM; + + /* Anybody who can open the device can do a read-safe command */ + if (test_bit(cmd[0], filter->read_ok)) + return 0; + + /* Write-safe commands require a writable open */ + if (test_bit(cmd[0], filter->write_ok) && (*f_mode & FMODE_WRITE)) + return 0; + + return -EPERM; +} +EXPORT_SYMBOL(blk_cmd_filter_verify_command); + +int blk_verify_command(struct file *file, unsigned char *cmd) +{ + struct gendisk *disk; + struct inode *inode; + + if (!file) + return -EINVAL; + + inode = file->f_dentry->d_inode; + if (!inode) + return -EINVAL; + + disk = inode->i_bdev->bd_disk; + + return blk_cmd_filter_verify_command(&disk->cmd_filter, + cmd, &file->f_mode); +} +EXPORT_SYMBOL(blk_verify_command); + +/* and now, the sysfs stuff */ +static ssize_t rcf_cmds_show(struct blk_scsi_cmd_filter *filter, char *page, + int rw) +{ + char *npage = page; + unsigned long *okbits; + int i; + + if (rw == READ) + okbits = filter->read_ok; + else + okbits = filter->write_ok; + + for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) { + if (test_bit(i, okbits)) { + sprintf(npage, "%02x", i); + npage += 2; + if (i < BLK_SCSI_MAX_CMDS - 1) + sprintf(npage++, " "); + } + } + + if (npage != page) + npage += sprintf(npage, "\n"); + + return npage - page; +} + +static ssize_t rcf_readcmds_show(struct blk_scsi_cmd_filter *filter, char *page) +{ + return rcf_cmds_show(filter, page, READ); +} + +static ssize_t rcf_writecmds_show(struct blk_scsi_cmd_filter *filter, + char *page) +{ + return rcf_cmds_show(filter, page, WRITE); +} + +static ssize_t rcf_cmds_store(struct blk_scsi_cmd_filter *filter, + const char *page, size_t count, int rw) +{ + ssize_t ret = 0; + unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits; + int cmd, status, len; + substring_t ss; + + memset(&okbits, 0, sizeof(okbits)); + + for (len = strlen(page); len > 0; len -= 3) { + if (len < 2) + break; + ss.from = (char *) page + ret; + ss.to = (char *) page + ret + 2; + ret += 3; + status = match_hex(&ss, &cmd); + /* either of these cases means invalid input, so do nothing. */ + if (status || cmd >= BLK_SCSI_MAX_CMDS) + return -EINVAL; + + __set_bit(cmd, okbits); + } + + if (rw == READ) + target_okbits = filter->read_ok; + else + target_okbits = filter->write_ok; + + memmove(target_okbits, okbits, sizeof(okbits)); + return count; +} + +static ssize_t rcf_readcmds_store(struct blk_scsi_cmd_filter *filter, + const char *page, size_t count) +{ + return rcf_cmds_store(filter, page, count, READ); +} + +static ssize_t rcf_writecmds_store(struct blk_scsi_cmd_filter *filter, + const char *page, size_t count) +{ + return rcf_cmds_store(filter, page, count, WRITE); +} + +struct rcf_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct blk_scsi_cmd_filter *, char *); + ssize_t (*store)(struct blk_scsi_cmd_filter *, const char *, size_t); +}; + +static struct rcf_sysfs_entry rcf_readcmds_entry = { + .attr = { .name = "read_table", .mode = S_IRUGO | S_IWUSR }, + .show = rcf_readcmds_show, + .store = rcf_readcmds_store, +}; + +static struct rcf_sysfs_entry rcf_writecmds_entry = { + .attr = {.name = "write_table", .mode = S_IRUGO | S_IWUSR }, + .show = rcf_writecmds_show, + .store = rcf_writecmds_store, +}; + +static struct attribute *default_attrs[] = { + &rcf_readcmds_entry.attr, + &rcf_writecmds_entry.attr, + NULL, +}; + +#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr) + +static ssize_t +rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page) +{ + struct rcf_sysfs_entry *entry = to_rcf(attr); + struct blk_scsi_cmd_filter *filter; + + filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); + if (entry->show) + return entry->show(filter, page); + + return 0; +} + +static ssize_t +rcf_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct rcf_sysfs_entry *entry = to_rcf(attr); + struct blk_scsi_cmd_filter *filter; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (!entry->store) + return -EINVAL; + + filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); + return entry->store(filter, page, length); +} + +static struct sysfs_ops rcf_sysfs_ops = { + .show = rcf_attr_show, + .store = rcf_attr_store, +}; + +static struct kobj_type rcf_ktype = { + .sysfs_ops = &rcf_sysfs_ops, + .default_attrs = default_attrs, +}; + +static void rcf_set_defaults(struct blk_scsi_cmd_filter *filter) +{ + /* Basic read-only commands */ + __set_bit(TEST_UNIT_READY, filter->read_ok); + __set_bit(REQUEST_SENSE, filter->read_ok); + __set_bit(READ_6, filter->read_ok); + __set_bit(READ_10, filter->read_ok); + __set_bit(READ_12, filter->read_ok); + __set_bit(READ_16, filter->read_ok); + __set_bit(READ_BUFFER, filter->read_ok); + __set_bit(READ_DEFECT_DATA, filter->read_ok); + __set_bit(READ_LONG, filter->read_ok); + __set_bit(INQUIRY, filter->read_ok); + __set_bit(MODE_SENSE, filter->read_ok); + __set_bit(MODE_SENSE_10, filter->read_ok); + __set_bit(LOG_SENSE, filter->read_ok); + __set_bit(START_STOP, filter->read_ok); + __set_bit(GPCMD_VERIFY_10, filter->read_ok); + __set_bit(VERIFY_16, filter->read_ok); + __set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok); + + /* Audio CD commands */ + __set_bit(GPCMD_PLAY_CD, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok); + __set_bit(GPCMD_PAUSE_RESUME, filter->read_ok); + + /* CD/DVD data reading */ + __set_bit(GPCMD_READ_CD, filter->read_ok); + __set_bit(GPCMD_READ_CD_MSF, filter->read_ok); + __set_bit(GPCMD_READ_DISC_INFO, filter->read_ok); + __set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok); + __set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok); + __set_bit(GPCMD_READ_HEADER, filter->read_ok); + __set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok); + __set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok); + __set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok); + __set_bit(GPCMD_REPORT_KEY, filter->read_ok); + __set_bit(GPCMD_SCAN, filter->read_ok); + __set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok); + __set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok); + __set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok); + __set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok); + __set_bit(GPCMD_SEEK, filter->read_ok); + __set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok); + + /* Basic writing commands */ + __set_bit(WRITE_6, filter->write_ok); + __set_bit(WRITE_10, filter->write_ok); + __set_bit(WRITE_VERIFY, filter->write_ok); + __set_bit(WRITE_12, filter->write_ok); + __set_bit(WRITE_VERIFY_12, filter->write_ok); + __set_bit(WRITE_16, filter->write_ok); + __set_bit(WRITE_LONG, filter->write_ok); + __set_bit(WRITE_LONG_2, filter->write_ok); + __set_bit(ERASE, filter->write_ok); + __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); + __set_bit(MODE_SELECT, filter->write_ok); + __set_bit(LOG_SELECT, filter->write_ok); + __set_bit(GPCMD_BLANK, filter->write_ok); + __set_bit(GPCMD_CLOSE_TRACK, filter->write_ok); + __set_bit(GPCMD_FLUSH_CACHE, filter->write_ok); + __set_bit(GPCMD_FORMAT_UNIT, filter->write_ok); + __set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok); + __set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok); + __set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok); + __set_bit(GPCMD_SEND_EVENT, filter->write_ok); + __set_bit(GPCMD_SEND_KEY, filter->write_ok); + __set_bit(GPCMD_SEND_OPC, filter->write_ok); + __set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok); + __set_bit(GPCMD_SET_SPEED, filter->write_ok); + __set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok); + __set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok); + __set_bit(GPCMD_SET_STREAMING, filter->write_ok); +} + +int blk_register_filter(struct gendisk *disk) +{ + int ret; + struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; + struct kobject *parent = kobject_get(disk->holder_dir->parent); + + if (!parent) + return -ENODEV; + + ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, parent, + "%s", "cmd_filter"); + + if (ret < 0) + return ret; + + rcf_set_defaults(filter); + return 0; +} + +void blk_unregister_filter(struct gendisk *disk) +{ + struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; + + kobject_put(&filter->kobj); + kobject_put(disk->holder_dir->parent); +} + diff --git a/block/genhd.c b/block/genhd.c index 43e468ee5993..9074f384b097 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -189,6 +189,7 @@ void add_disk(struct gendisk *disk) disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); blk_register_queue(disk); + blk_register_filter(disk); bdi = &disk->queue->backing_dev_info; bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); @@ -200,6 +201,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ void unlink_gendisk(struct gendisk *disk) { + blk_unregister_filter(disk); sysfs_remove_link(&disk->dev.kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 78199c08ec92..c5b9bcfc0a6d 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -105,120 +105,12 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) return put_user(1, p); } -#define CMD_READ_SAFE 0x01 -#define CMD_WRITE_SAFE 0x02 -#define CMD_WARNED 0x04 -#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE -#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE - -int blk_verify_command(unsigned char *cmd, int has_write_perm) -{ - static unsigned char cmd_type[256] = { - - /* Basic read-only commands */ - safe_for_read(TEST_UNIT_READY), - safe_for_read(REQUEST_SENSE), - safe_for_read(READ_6), - safe_for_read(READ_10), - safe_for_read(READ_12), - safe_for_read(READ_16), - safe_for_read(READ_BUFFER), - safe_for_read(READ_DEFECT_DATA), - safe_for_read(READ_LONG), - safe_for_read(INQUIRY), - safe_for_read(MODE_SENSE), - safe_for_read(MODE_SENSE_10), - safe_for_read(LOG_SENSE), - safe_for_read(START_STOP), - safe_for_read(GPCMD_VERIFY_10), - safe_for_read(VERIFY_16), - - /* Audio CD commands */ - safe_for_read(GPCMD_PLAY_CD), - safe_for_read(GPCMD_PLAY_AUDIO_10), - safe_for_read(GPCMD_PLAY_AUDIO_MSF), - safe_for_read(GPCMD_PLAY_AUDIO_TI), - safe_for_read(GPCMD_PAUSE_RESUME), - - /* CD/DVD data reading */ - safe_for_read(GPCMD_READ_BUFFER_CAPACITY), - safe_for_read(GPCMD_READ_CD), - safe_for_read(GPCMD_READ_CD_MSF), - safe_for_read(GPCMD_READ_DISC_INFO), - safe_for_read(GPCMD_READ_CDVD_CAPACITY), - safe_for_read(GPCMD_READ_DVD_STRUCTURE), - safe_for_read(GPCMD_READ_HEADER), - safe_for_read(GPCMD_READ_TRACK_RZONE_INFO), - safe_for_read(GPCMD_READ_SUBCHANNEL), - safe_for_read(GPCMD_READ_TOC_PMA_ATIP), - safe_for_read(GPCMD_REPORT_KEY), - safe_for_read(GPCMD_SCAN), - safe_for_read(GPCMD_GET_CONFIGURATION), - safe_for_read(GPCMD_READ_FORMAT_CAPACITIES), - safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION), - safe_for_read(GPCMD_GET_PERFORMANCE), - safe_for_read(GPCMD_SEEK), - safe_for_read(GPCMD_STOP_PLAY_SCAN), - - /* Basic writing commands */ - safe_for_write(WRITE_6), - safe_for_write(WRITE_10), - safe_for_write(WRITE_VERIFY), - safe_for_write(WRITE_12), - safe_for_write(WRITE_VERIFY_12), - safe_for_write(WRITE_16), - safe_for_write(WRITE_LONG), - safe_for_write(WRITE_LONG_2), - safe_for_write(ERASE), - safe_for_write(GPCMD_MODE_SELECT_10), - safe_for_write(MODE_SELECT), - safe_for_write(LOG_SELECT), - safe_for_write(GPCMD_BLANK), - safe_for_write(GPCMD_CLOSE_TRACK), - safe_for_write(GPCMD_FLUSH_CACHE), - safe_for_write(GPCMD_FORMAT_UNIT), - safe_for_write(GPCMD_REPAIR_RZONE_TRACK), - safe_for_write(GPCMD_RESERVE_RZONE_TRACK), - safe_for_write(GPCMD_SEND_DVD_STRUCTURE), - safe_for_write(GPCMD_SEND_EVENT), - safe_for_write(GPCMD_SEND_KEY), - safe_for_write(GPCMD_SEND_OPC), - safe_for_write(GPCMD_SEND_CUE_SHEET), - safe_for_write(GPCMD_SET_SPEED), - safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL), - safe_for_write(GPCMD_LOAD_UNLOAD), - safe_for_write(GPCMD_SET_STREAMING), - }; - unsigned char type = cmd_type[cmd[0]]; - - /* Anybody who can open the device can do a read-safe command */ - if (type & CMD_READ_SAFE) - return 0; - - /* Write-safe commands just require a writable open.. */ - if ((type & CMD_WRITE_SAFE) && has_write_perm) - return 0; - - /* And root can do any command.. */ - if (capable(CAP_SYS_RAWIO)) - return 0; - - if (!type) { - cmd_type[cmd[0]] = CMD_WARNED; - printk(KERN_WARNING "scsi: unknown opcode 0x%02x\n", cmd[0]); - } - - /* Otherwise fail it with an "Operation not permitted" */ - return -EPERM; -} -EXPORT_SYMBOL_GPL(blk_verify_command); - static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, - struct sg_io_hdr *hdr, int has_write_perm) + struct sg_io_hdr *hdr, struct file *file) { if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len)) return -EFAULT; - if (blk_verify_command(rq->cmd, has_write_perm)) + if (blk_verify_command(file, rq->cmd)) return -EPERM; /* @@ -287,7 +179,7 @@ static int sg_io(struct file *file, struct request_queue *q, struct gendisk *bd_disk, struct sg_io_hdr *hdr) { unsigned long start_time; - int writing = 0, ret = 0, has_write_perm = 0; + int writing = 0, ret = 0; struct request *rq; char sense[SCSI_SENSE_BUFFERSIZE]; struct bio *bio; @@ -316,10 +208,7 @@ static int sg_io(struct file *file, struct request_queue *q, if (!rq) return -ENOMEM; - if (file) - has_write_perm = file->f_mode & FMODE_WRITE; - - if (blk_fill_sghdr_rq(q, rq, hdr, has_write_perm)) { + if (blk_fill_sghdr_rq(q, rq, hdr, file)) { blk_put_request(rq); return -EFAULT; } @@ -451,7 +340,7 @@ int sg_scsi_ioctl(struct file *file, struct request_queue *q, if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) goto error; - err = blk_verify_command(rq->cmd, file->f_mode & FMODE_WRITE); + err = blk_verify_command(file, rq->cmd); if (err) goto error; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ea0edd1b2e76..f7abccaffaec 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -182,8 +182,9 @@ static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize); static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp); -static ssize_t sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count, - int blocking, int read_only, Sg_request ** o_srp); +static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, + const char __user *buf, size_t count, int blocking, + int read_only, Sg_request **o_srp); static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking); static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, @@ -204,7 +205,6 @@ static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id); static Sg_request *sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); -static int sg_allow_access(unsigned char opcode, char dev_type); static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len); static Sg_device *sg_get_dev(int dev); #ifdef CONFIG_SCSI_PROC_FS @@ -544,7 +544,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return -EFAULT; blocking = !(filp->f_flags & O_NONBLOCK); if (old_hdr.reply_len < 0) - return sg_new_write(sfp, buf, count, blocking, 0, NULL); + return sg_new_write(sfp, filp, buf, count, blocking, 0, NULL); if (count < (SZ_SG_HEADER + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ @@ -621,8 +621,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) } static ssize_t -sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count, - int blocking, int read_only, Sg_request ** o_srp) +sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, + size_t count, int blocking, int read_only, + Sg_request **o_srp) { int k; Sg_request *srp; @@ -678,8 +679,7 @@ sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count, sg_remove_request(sfp, srp); return -EFAULT; } - if (read_only && - (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) { + if (read_only && (!blk_verify_command(file, cmnd))) { sg_remove_request(sfp, srp); return -EPERM; } @@ -799,7 +799,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) return -EFAULT; result = - sg_new_write(sfp, p, SZ_SG_IO_HDR, + sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, blocking, read_only, &srp); if (result < 0) return result; @@ -1048,7 +1048,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (copy_from_user(&opcode, siocp->data, 1)) return -EFAULT; - if (!sg_allow_access(opcode, sdp->device->type)) + if (!blk_verify_command(filp, &opcode)) return -EPERM; } return sg_scsi_ioctl(filp, sdp->device->request_queue, NULL, p); @@ -2506,26 +2506,6 @@ sg_page_free(struct page *page, int size) #define MAINTENANCE_IN_CMD 0xa3 #endif -static unsigned char allow_ops[] = { TEST_UNIT_READY, REQUEST_SENSE, - INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12, - READ_16, MODE_SENSE, MODE_SENSE_10, LOG_SENSE, REPORT_LUNS, - SERVICE_ACTION_IN, RECEIVE_DIAGNOSTIC, READ_LONG, MAINTENANCE_IN_CMD -}; - -static int -sg_allow_access(unsigned char opcode, char dev_type) -{ - int k; - - if (TYPE_SCANNER == dev_type) /* TYPE_ROM maybe burner */ - return 1; - for (k = 0; k < sizeof (allow_ops); ++k) { - if (opcode == allow_ops[k]) - return 1; - } - return 0; -} - #ifdef CONFIG_SCSI_PROC_FS static int sg_idr_max_id(int id, void *p, void *data) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d3ae9ad97213..a842b776d099 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -671,7 +671,6 @@ extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, struct request *, int, rq_end_io_fn *); -extern int blk_verify_command(unsigned char *, int); extern void blk_unplug(struct request_queue *q); static inline struct request_queue *bdev_get_queue(struct block_device *bdev) @@ -797,6 +796,15 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt, extern int blkdev_issue_flush(struct block_device *, sector_t *); +/* +* command filter functions +*/ +extern int blk_verify_command(struct file *file, unsigned char *cmd); +extern int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, + unsigned char *cmd, mode_t *f_mode); +extern int blk_register_filter(struct gendisk *disk); +extern void blk_unregister_filter(struct gendisk *disk); + #define MAX_PHYS_SEGMENTS 128 #define MAX_HW_SEGMENTS 128 #define SAFE_MAX_SECTORS 255 diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 524ec96f5a23..e8787417f65a 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -110,6 +110,14 @@ struct hd_struct { #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 #define GENHD_FL_FAIL 64 +#define BLK_SCSI_MAX_CMDS (256) +#define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) + +struct blk_scsi_cmd_filter { + unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; + unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; + struct kobject kobj; +}; struct gendisk { int major; /* major number of driver */ @@ -120,6 +128,7 @@ struct gendisk { struct hd_struct **part; /* [indexed by minor] */ struct block_device_operations *fops; struct request_queue *queue; + struct blk_scsi_cmd_filter cmd_filter; void *private_data; sector_t capacity; -- cgit v1.2.3 From b24498d477a14680fc3bb3ad884fa9fa76a2d237 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 27 Jun 2008 09:12:09 +0200 Subject: block: integrity flags can't use bit ops on unsigned short Just use normal open coded bit operations instead, they need not be atomic. Signed-off-by: Jens Axboe --- block/blk-integrity.c | 17 +++++++---------- include/linux/blkdev.h | 8 ++++---- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/block/blk-integrity.c b/block/blk-integrity.c index 4ffa3814f6a9..3f1a8478cc38 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -217,17 +217,16 @@ static ssize_t integrity_read_store(struct blk_integrity *bi, unsigned long val = simple_strtoul(p, &p, 10); if (val) - set_bit(INTEGRITY_FLAG_READ, &bi->flags); + bi->flags |= INTEGRITY_FLAG_READ; else - clear_bit(INTEGRITY_FLAG_READ, &bi->flags); + bi->flags &= ~INTEGRITY_FLAG_READ; return count; } static ssize_t integrity_read_show(struct blk_integrity *bi, char *page) { - return sprintf(page, "%d\n", - test_bit(INTEGRITY_FLAG_READ, &bi->flags) ? 1 : 0); + return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_READ) != 0); } static ssize_t integrity_write_store(struct blk_integrity *bi, @@ -237,17 +236,16 @@ static ssize_t integrity_write_store(struct blk_integrity *bi, unsigned long val = simple_strtoul(p, &p, 10); if (val) - set_bit(INTEGRITY_FLAG_WRITE, &bi->flags); + bi->flags |= INTEGRITY_FLAG_WRITE; else - clear_bit(INTEGRITY_FLAG_WRITE, &bi->flags); + bi->flags &= ~INTEGRITY_FLAG_WRITE; return count; } static ssize_t integrity_write_show(struct blk_integrity *bi, char *page) { - return sprintf(page, "%d\n", - test_bit(INTEGRITY_FLAG_WRITE, &bi->flags) ? 1 : 0); + return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_WRITE) != 0); } static struct integrity_sysfs_entry integrity_format_entry = { @@ -340,8 +338,7 @@ int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template) kobject_uevent(&bi->kobj, KOBJ_ADD); - set_bit(INTEGRITY_FLAG_READ, &bi->flags); - set_bit(INTEGRITY_FLAG_WRITE, &bi->flags); + bi->flags |= INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE; bi->sector_size = disk->queue->hardsect_size; disk->integrity = bi; } else diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index a842b776d099..7ab8acad5b6e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -870,8 +870,8 @@ void kblockd_flush_work(struct work_struct *work); #if defined(CONFIG_BLK_DEV_INTEGRITY) -#define INTEGRITY_FLAG_READ 1 /* verify data integrity on read */ -#define INTEGRITY_FLAG_WRITE 2 /* generate data integrity on write */ +#define INTEGRITY_FLAG_READ 2 /* verify data integrity on read */ +#define INTEGRITY_FLAG_WRITE 4 /* generate data integrity on write */ struct blk_integrity_exchg { void *prot_buf; @@ -940,11 +940,11 @@ static inline int bdev_integrity_enabled(struct block_device *bdev, int rw) return 0; if (rw == READ && bi->verify_fn != NULL && - test_bit(INTEGRITY_FLAG_READ, &bi->flags)) + (bi->flags & INTEGRITY_FLAG_READ)) return 1; if (rw == WRITE && bi->generate_fn != NULL && - test_bit(INTEGRITY_FLAG_WRITE, &bi->flags)) + (bi->flags & INTEGRITY_FLAG_WRITE)) return 1; return 0; -- cgit v1.2.3 From cc371e66e340f35eed8dc4651c7c18e754c7fb26 Mon Sep 17 00:00:00 2001 From: Alasdair G Kergon Date: Thu, 3 Jul 2008 09:53:43 +0200 Subject: Add bvec_merge_data to handle stacked devices and ->merge_bvec() When devices are stacked, one device's merge_bvec_fn may need to perform the mapping and then call one or more functions for its underlying devices. The following bio fields are used: bio->bi_sector bio->bi_bdev bio->bi_size bio->bi_rw using bio_data_dir() This patch creates a new struct bvec_merge_data holding a copy of those fields to avoid having to change them directly in the struct bio when going down the stack only to have to change them back again on the way back up. (And then when the bio gets mapped for real, the whole exercise gets repeated, but that's a problem for another day...) Signed-off-by: Alasdair G Kergon Cc: Neil Brown Cc: Milan Broz Signed-off-by: Jens Axboe --- drivers/block/pktcdvd.c | 9 +++++---- drivers/md/linear.c | 10 ++++++---- drivers/md/raid0.c | 10 ++++++---- drivers/md/raid10.c | 15 ++++++++------- drivers/md/raid5.c | 10 ++++++---- fs/bio.c | 26 +++++++++++++++++++++----- include/linux/blkdev.h | 9 ++++++++- 7 files changed, 60 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 3ba1df93e9e3..589850cff359 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2633,11 +2633,12 @@ end_io: -static int pkt_merge_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *bvec) +static int pkt_merge_bvec(struct request_queue *q, struct bvec_merge_data *bmd, + struct bio_vec *bvec) { struct pktcdvd_device *pd = q->queuedata; - sector_t zone = ZONE(bio->bi_sector, pd); - int used = ((bio->bi_sector - zone) << 9) + bio->bi_size; + sector_t zone = ZONE(bmd->bi_sector, pd); + int used = ((bmd->bi_sector - zone) << 9) + bmd->bi_size; int remaining = (pd->settings.size << 9) - used; int remaining2; @@ -2645,7 +2646,7 @@ static int pkt_merge_bvec(struct request_queue *q, struct bio *bio, struct bio_v * A bio <= PAGE_SIZE must be allowed. If it crosses a packet * boundary, pkt_make_request() will split the bio. */ - remaining2 = PAGE_SIZE - bio->bi_size; + remaining2 = PAGE_SIZE - bmd->bi_size; remaining = max(remaining, remaining2); BUG_ON(remaining < 0); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 10748240cb2f..6a866d7c8ae5 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -50,17 +50,19 @@ static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector) /** * linear_mergeable_bvec -- tell bio layer if two requests can be merged * @q: request queue - * @bio: the buffer head that's been built up so far + * @bvm: properties of new bio * @biovec: the request that could be merged to it. * * Return amount of bytes we can take at this offset */ -static int linear_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec) +static int linear_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; dev_info_t *dev0; - unsigned long maxsectors, bio_sectors = bio->bi_size >> 9; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9; + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); dev0 = which_dev(mddev, sector); maxsectors = (dev0->size << 1) - (sector - (dev0->offset<<1)); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 914c04ddec7c..bcbb82594a19 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -241,18 +241,20 @@ static int create_strip_zones (mddev_t *mddev) /** * raid0_mergeable_bvec -- tell bio layer if a two requests can be merged * @q: request queue - * @bio: the buffer head that's been built up so far + * @bvm: properties of new bio * @biovec: the request that could be merged to it. * * Return amount of bytes we can accept at this offset */ -static int raid0_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec) +static int raid0_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); int max; unsigned int chunk_sectors = mddev->chunk_size >> 9; - unsigned int bio_sectors = bio->bi_size >> 9; + unsigned int bio_sectors = bvm->bi_size >> 9; max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; if (max < 0) max = 0; /* bio_add cannot handle a negative return */ diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index a71277b640ab..22bb2b1b886d 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -439,26 +439,27 @@ static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev) /** * raid10_mergeable_bvec -- tell bio layer if a two requests can be merged * @q: request queue - * @bio: the buffer head that's been built up so far + * @bvm: properties of new bio * @biovec: the request that could be merged to it. * * Return amount of bytes we can accept at this offset * If near_copies == raid_disk, there are no striping issues, * but in that case, the function isn't called at all. */ -static int raid10_mergeable_bvec(struct request_queue *q, struct bio *bio, - struct bio_vec *bio_vec) +static int raid10_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); int max; unsigned int chunk_sectors = mddev->chunk_size >> 9; - unsigned int bio_sectors = bio->bi_size >> 9; + unsigned int bio_sectors = bvm->bi_size >> 9; max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; if (max < 0) max = 0; /* bio_add cannot handle a negative return */ - if (max <= bio_vec->bv_len && bio_sectors == 0) - return bio_vec->bv_len; + if (max <= biovec->bv_len && bio_sectors == 0) + return biovec->bv_len; else return max; } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 54c8ee28fcc4..9b00675dc64f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3319,15 +3319,17 @@ static int raid5_congested(void *data, int bits) /* We want read requests to align with chunks where possible, * but write requests don't need to. */ -static int raid5_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec) +static int raid5_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); int max; unsigned int chunk_sectors = mddev->chunk_size >> 9; - unsigned int bio_sectors = bio->bi_size >> 9; + unsigned int bio_sectors = bvm->bi_size >> 9; - if (bio_data_dir(bio) == WRITE) + if ((bvm->bi_rw & 1) == WRITE) return biovec->bv_len; /* always allow writes to be mergeable */ max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; diff --git a/fs/bio.c b/fs/bio.c index 7761c84c7032..88322b066acb 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -325,10 +325,19 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page if (page == prev->bv_page && offset == prev->bv_offset + prev->bv_len) { prev->bv_len += len; - if (q->merge_bvec_fn && - q->merge_bvec_fn(q, bio, prev) < len) { - prev->bv_len -= len; - return 0; + + if (q->merge_bvec_fn) { + struct bvec_merge_data bvm = { + .bi_bdev = bio->bi_bdev, + .bi_sector = bio->bi_sector, + .bi_size = bio->bi_size, + .bi_rw = bio->bi_rw, + }; + + if (q->merge_bvec_fn(q, &bvm, prev) < len) { + prev->bv_len -= len; + return 0; + } } goto done; @@ -369,11 +378,18 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page * queue to get further control */ if (q->merge_bvec_fn) { + struct bvec_merge_data bvm = { + .bi_bdev = bio->bi_bdev, + .bi_sector = bio->bi_sector, + .bi_size = bio->bi_size, + .bi_rw = bio->bi_rw, + }; + /* * merge_bvec_fn() returns number of bytes it can accept * at this offset */ - if (q->merge_bvec_fn(q, bio, bvec) < len) { + if (q->merge_bvec_fn(q, &bvm, bvec) < len) { bvec->bv_page = NULL; bvec->bv_len = 0; bvec->bv_offset = 0; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 7ab8acad5b6e..ff9d0bdf2a16 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -254,7 +254,14 @@ typedef int (prep_rq_fn) (struct request_queue *, struct request *); typedef void (unplug_fn) (struct request_queue *); struct bio_vec; -typedef int (merge_bvec_fn) (struct request_queue *, struct bio *, struct bio_vec *); +struct bvec_merge_data { + struct block_device *bi_bdev; + sector_t bi_sector; + unsigned bi_size; + unsigned long bi_rw; +}; +typedef int (merge_bvec_fn) (struct request_queue *, struct bvec_merge_data *, + struct bio_vec *); typedef void (prepare_flush_fn) (struct request_queue *, struct request *); typedef void (softirq_done_fn)(struct request *); typedef int (dma_drain_needed_fn)(struct request *); -- cgit v1.2.3 From e48ec69005f02b70b7ecfde1bc39a599086d16ef Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 3 Jul 2008 13:18:54 +0200 Subject: block: extend queue_flag bitops Add test_and_clear and test_and_set. Signed-off-by: Jens Axboe --- block/blk-core.c | 12 ++++-------- include/linux/blkdev.h | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index e0fb0bcc0c17..dbc7f42b5d2b 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -205,8 +205,7 @@ void blk_plug_device(struct request_queue *q) if (blk_queue_stopped(q)) return; - if (!test_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags)) { - __set_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags); + if (!queue_flag_test_and_set(QUEUE_FLAG_PLUGGED, q)) { mod_timer(&q->unplug_timer, jiffies + q->unplug_delay); blk_add_trace_generic(q, NULL, 0, BLK_TA_PLUG); } @@ -221,10 +220,9 @@ int blk_remove_plug(struct request_queue *q) { WARN_ON(!irqs_disabled()); - if (!test_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags)) + if (!queue_flag_test_and_clear(QUEUE_FLAG_PLUGGED, q)) return 0; - queue_flag_clear(QUEUE_FLAG_PLUGGED, q); del_timer(&q->unplug_timer); return 1; } @@ -328,8 +326,7 @@ void blk_start_queue(struct request_queue *q) * one level of recursion is ok and is much faster than kicking * the unplug handling */ - if (!test_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) { - queue_flag_set(QUEUE_FLAG_REENTER, q); + if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { @@ -394,8 +391,7 @@ void __blk_run_queue(struct request_queue *q) * handling reinvoke the handler shortly if we already got there. */ if (!elv_queue_empty(q)) { - if (!test_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) { - queue_flag_set(QUEUE_FLAG_REENTER, q); + if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ff9d0bdf2a16..e04c4ac8a7cf 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -428,6 +428,32 @@ static inline void queue_flag_set_unlocked(unsigned int flag, __set_bit(flag, &q->queue_flags); } +static inline int queue_flag_test_and_clear(unsigned int flag, + struct request_queue *q) +{ + WARN_ON_ONCE(!queue_is_locked(q)); + + if (test_bit(flag, &q->queue_flags)) { + __clear_bit(flag, &q->queue_flags); + return 1; + } + + return 0; +} + +static inline int queue_flag_test_and_set(unsigned int flag, + struct request_queue *q) +{ + WARN_ON_ONCE(!queue_is_locked(q)); + + if (!test_bit(flag, &q->queue_flags)) { + __set_bit(flag, &q->queue_flags); + return 0; + } + + return 1; +} + static inline void queue_flag_set(unsigned int flag, struct request_queue *q) { WARN_ON_ONCE(!queue_is_locked(q)); -- cgit v1.2.3 From 42796d37da6ef4fd851dc6d5d0387baf7e2b0c3c Mon Sep 17 00:00:00 2001 From: eric miao Date: Mon, 14 Apr 2008 09:35:08 +0100 Subject: [ARM] pxa: add generic PWM backlight driver Patch mostly from Eric Miao, with minor edits by rmk to convert Eric's driver to a generic PWM-based backlight driver. Signed-off-by: eric miao Signed-off-by: Russell King --- drivers/video/backlight/Kconfig | 7 ++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/pwm_bl.c | 160 +++++++++++++++++++++++++++++++++++++++ include/linux/pwm_backlight.h | 14 ++++ 4 files changed, 182 insertions(+) create mode 100644 drivers/video/backlight/pwm_bl.c create mode 100644 include/linux/pwm_backlight.h (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index dcd8073c2369..30bf7f2f1635 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -112,3 +112,10 @@ config BACKLIGHT_CARILLO_RANCH help If you have a Intel LE80578 (Carillo Ranch) say Y to enable the backlight driver. + +config BACKLIGHT_PWM + tristate "Generic PWM based Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && HAVE_PWM + help + If you have a LCD backlight adjustable by PWM, say Y to enable + this driver. diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 33f6c7cecc73..b51a7cd12500 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o +obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c new file mode 100644 index 000000000000..9637f5e08cb6 --- /dev/null +++ b/drivers/video/backlight/pwm_bl.c @@ -0,0 +1,160 @@ +/* + * linux/drivers/video/backlight/pwm_bl.c + * + * simple PWM based backlight control, board code has to setup + * 1) pin configuration so PWM waveforms can output + * 2) platform_data casts to the PWM id (0/1/2/3 on PXA) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct pwm_bl_data { + struct pwm_device *pwm; + unsigned int period; +}; + +static int pwm_backlight_update_status(struct backlight_device *bl) +{ + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + int brightness = bl->props.brightness; + int max = bl->props.max_brightness; + + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + if (brightness == 0) { + pwm_config(pb->pwm, 0, pb->period); + pwm_disable(pb->pwm); + } else { + pwm_config(pb->pwm, brightness * pb->period / max, pb->period); + pwm_enable(pb->pwm); + } + return 0; +} + +static int pwm_backlight_get_brightness(struct backlight_device *bl) +{ + return bl->props.brightness; +} + +static struct backlight_ops pwm_backlight_ops = { + .update_status = pwm_backlight_update_status, + .get_brightness = pwm_backlight_get_brightness, +}; + +static int pwm_backlight_probe(struct platform_device *pdev) +{ + struct platform_pwm_backlight_data *data = pdev->dev.platform_data; + struct backlight_device *bl; + struct pwm_bl_data *pb; + + if (!data) + return -EINVAL; + + pb = kzalloc(sizeof(*pb), GFP_KERNEL); + if (!pb) + return -ENOMEM; + + pb->period = data->pwm_period_ns; + + pb->pwm = pwm_request(data->pwm_id, "backlight"); + if (pb->pwm == NULL) { + dev_err(&pdev->dev, "unable to request PWM for backlight\n"); + kfree(pb); + return -EBUSY; + } + + bl = backlight_device_register(pdev->name, &pdev->dev, + pb, &pwm_backlight_ops); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + pwm_free(pb->pwm); + kfree(pb); + return PTR_ERR(bl); + } + + bl->props.max_brightness = data->max_brightness; + bl->props.brightness = data->dft_brightness; + backlight_update_status(bl); + + platform_set_drvdata(pdev, bl); + return 0; +} + +static int pwm_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + + backlight_device_unregister(bl); + pwm_config(pb->pwm, 0, pb->period); + pwm_disable(pb->pwm); + pwm_free(pb->pwm); + kfree(pb); + return 0; +} + +#ifdef CONFIG_PM +static int pwm_backlight_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + + pwm_config(pb->pwm, 0, pb->period); + pwm_disable(pb->pwm); + return 0; +} + +static int pwm_backlight_resume(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + + backlight_update_status(bl); + return 0; +} +#else +#define pwm_backlight_suspend NULL +#define pwm_backlight_resume NULL +#endif + +static struct platform_driver pwm_backlight_driver = { + .driver = { + .name = "pwm-backlight", + .owner = THIS_MODULE, + }, + .probe = pwm_backlight_probe, + .remove = pwm_backlight_remove, + .suspend = pwm_backlight_suspend, + .resume = pwm_backlight_resume, +}; + +static int __init pwm_backlight_init(void) +{ + return platform_driver_register(&pwm_backlight_driver); +} +module_init(pwm_backlight_init); + +static void __exit pwm_backlight_exit(void) +{ + platform_driver_unregister(&pwm_backlight_driver); +} +module_exit(pwm_backlight_exit); + +MODULE_DESCRIPTION("PWM based Backlight Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h new file mode 100644 index 000000000000..aeeffedbe822 --- /dev/null +++ b/include/linux/pwm_backlight.h @@ -0,0 +1,14 @@ +/* + * Generic PWM backlight driver data - see drivers/video/backlight/pwm_bl.c + */ +#ifndef __LINUX_PWM_BACKLIGHT_H +#define __LINUX_PWM_BACKLIGHT_H + +struct platform_pwm_backlight_data { + int pwm_id; + unsigned int max_brightness; + unsigned int dft_brightness; + unsigned int pwm_period_ns; +}; + +#endif -- cgit v1.2.3 From 3b73125af69f93972625f4b655675f42ca4274eb Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 22 May 2008 14:18:40 +0100 Subject: [ARM] 5044/1: pwm_bl: add init/notify/exit callbacks This allows platform code to manipulate GPIOs and brightness level as needed. Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/video/backlight/pwm_bl.c | 39 ++++++++++++++++++++++++++++++++------- include/linux/pwm_backlight.h | 3 +++ 2 files changed, 35 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 9637f5e08cb6..8346dfc01cf6 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -23,6 +23,7 @@ struct pwm_bl_data { struct pwm_device *pwm; unsigned int period; + int (*notify)(int brightness); }; static int pwm_backlight_update_status(struct backlight_device *bl) @@ -37,6 +38,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl) if (bl->props.fb_blank != FB_BLANK_UNBLANK) brightness = 0; + if (pb->notify) + brightness = pb->notify(brightness); + if (brightness == 0) { pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); @@ -62,30 +66,39 @@ static int pwm_backlight_probe(struct platform_device *pdev) struct platform_pwm_backlight_data *data = pdev->dev.platform_data; struct backlight_device *bl; struct pwm_bl_data *pb; + int ret; if (!data) return -EINVAL; + if (data->init) { + ret = data->init(&pdev->dev); + if (ret < 0) + return ret; + } + pb = kzalloc(sizeof(*pb), GFP_KERNEL); - if (!pb) - return -ENOMEM; + if (!pb) { + ret = -ENOMEM; + goto err_alloc; + } pb->period = data->pwm_period_ns; + pb->notify = data->notify; pb->pwm = pwm_request(data->pwm_id, "backlight"); if (pb->pwm == NULL) { dev_err(&pdev->dev, "unable to request PWM for backlight\n"); - kfree(pb); - return -EBUSY; + ret = -EBUSY; + goto err_pwm; } bl = backlight_device_register(pdev->name, &pdev->dev, pb, &pwm_backlight_ops); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - pwm_free(pb->pwm); - kfree(pb); - return PTR_ERR(bl); + ret = PTR_ERR(bl); + goto err_bl; } bl->props.max_brightness = data->max_brightness; @@ -94,10 +107,20 @@ static int pwm_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); return 0; + +err_bl: + pwm_free(pb->pwm); +err_pwm: + kfree(pb); +err_alloc: + if (data->exit) + data->exit(&pdev->dev); + return ret; } static int pwm_backlight_remove(struct platform_device *pdev) { + struct platform_pwm_backlight_data *data = pdev->dev.platform_data; struct backlight_device *bl = platform_get_drvdata(pdev); struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); @@ -106,6 +129,8 @@ static int pwm_backlight_remove(struct platform_device *pdev) pwm_disable(pb->pwm); pwm_free(pb->pwm); kfree(pb); + if (data->exit) + data->exit(&pdev->dev); return 0; } diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h index aeeffedbe822..7a9754c96775 100644 --- a/include/linux/pwm_backlight.h +++ b/include/linux/pwm_backlight.h @@ -9,6 +9,9 @@ struct platform_pwm_backlight_data { unsigned int max_brightness; unsigned int dft_brightness; unsigned int pwm_period_ns; + int (*init)(struct device *dev); + int (*notify)(int brightness); + void (*exit)(struct device *dev); }; #endif -- cgit v1.2.3 From 41d54d3bf83f62d3ff5948cb788fe6007e66a0d0 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 3 Jul 2008 09:14:26 -0500 Subject: slub: Do not use 192 byte sized cache if minimum alignment is 128 byte The 192 byte cache is not necessary if we have a basic alignment of 128 byte. If it would be used then the 192 would be aligned to the next 128 byte boundary which would result in another 256 byte cache. Two 256 kmalloc caches cause sysfs to complain about a duplicate entry. MIPS needs 128 byte aligned kmalloc caches and spits out warnings on boot without this patch. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slub_def.h | 2 ++ mm/slub.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 71e43a12ebbb..cef6f8fddd7d 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -137,10 +137,12 @@ static __always_inline int kmalloc_index(size_t size) if (size <= KMALLOC_MIN_SIZE) return KMALLOC_SHIFT_LOW; +#if KMALLOC_MIN_SIZE <= 64 if (size > 64 && size <= 96) return 1; if (size > 128 && size <= 192) return 2; +#endif if (size <= 8) return 3; if (size <= 16) return 4; if (size <= 32) return 5; diff --git a/mm/slub.c b/mm/slub.c index 0987d1cd943c..2c9a62d1f429 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2995,8 +2995,6 @@ void __init kmem_cache_init(void) create_kmalloc_cache(&kmalloc_caches[1], "kmalloc-96", 96, GFP_KERNEL); caches++; - } - if (KMALLOC_MIN_SIZE <= 128) { create_kmalloc_cache(&kmalloc_caches[2], "kmalloc-192", 192, GFP_KERNEL); caches++; @@ -3026,6 +3024,16 @@ void __init kmem_cache_init(void) for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) size_index[(i - 1) / 8] = KMALLOC_SHIFT_LOW; + if (KMALLOC_MIN_SIZE == 128) { + /* + * The 192 byte sized cache is not used if the alignment + * is 128 byte. Redirect kmalloc to use the 256 byte cache + * instead. + */ + for (i = 128 + 8; i <= 192; i += 8) + size_index[(i - 1) / 8] = 8; + } + slab_state = UP; /* Provide the correct kmalloc names now that the caches are up */ -- cgit v1.2.3 From 27f8221af406e43b529a5425bc99c9b1e9bdf521 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 4 Jul 2008 09:30:03 +0200 Subject: block: add blk_queue_update_dma_pad This adds blk_queue_update_dma_pad to prevent LLDs from overwriting the dma pad mask wrongly (we added blk_queue_update_dma_alignment due to the same reason). This also converts libata to use blk_queue_update_dma_pad instead of blk_queue_dma_pad. Signed-off-by: FUJITA Tomonori Cc: Tejun Heo Cc: Bartlomiej Zolnierkiewicz Cc: Thomas Bogendoerfer Cc: James Bottomley Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- block/blk-settings.c | 24 ++++++++++++++++++++---- drivers/ata/libata-scsi.c | 3 ++- include/linux/blkdev.h | 1 + 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/block/blk-settings.c b/block/blk-settings.c index 8dd86418f35d..dfc77012843f 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -302,11 +302,10 @@ EXPORT_SYMBOL(blk_queue_stack_limits); * @q: the request queue for the device * @mask: pad mask * - * Set pad mask. Direct IO requests are padded to the mask specified. + * Set dma pad mask. * - * Appending pad buffer to a request modifies ->data_len such that it - * includes the pad buffer. The original requested data length can be - * obtained using blk_rq_raw_data_len(). + * Appending pad buffer to a request modifies the last entry of a + * scatter list such that it includes the pad buffer. **/ void blk_queue_dma_pad(struct request_queue *q, unsigned int mask) { @@ -314,6 +313,23 @@ void blk_queue_dma_pad(struct request_queue *q, unsigned int mask) } EXPORT_SYMBOL(blk_queue_dma_pad); +/** + * blk_queue_update_dma_pad - update pad mask + * @q: the request queue for the device + * @mask: pad mask + * + * Update dma pad mask. + * + * Appending pad buffer to a request modifies the last entry of a + * scatter list such that it includes the pad buffer. + **/ +void blk_queue_update_dma_pad(struct request_queue *q, unsigned int mask) +{ + if (mask > q->dma_pad_mask) + q->dma_pad_mask = mask; +} +EXPORT_SYMBOL(blk_queue_update_dma_pad); + /** * blk_queue_dma_drain - Set up a drain buffer for excess dma. * @q: the request queue for the device diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 57a43649a461..499ccc628d81 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -885,7 +885,8 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, /* set the min alignment and padding */ blk_queue_update_dma_alignment(sdev->request_queue, ATA_DMA_PAD_SZ - 1); - blk_queue_dma_pad(sdev->request_queue, ATA_DMA_PAD_SZ - 1); + blk_queue_update_dma_pad(sdev->request_queue, + ATA_DMA_PAD_SZ - 1); /* configure draining */ buf = kmalloc(ATAPI_MAX_DRAIN, q->bounce_gfp | GFP_KERNEL); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e04c4ac8a7cf..1ffd8bfdc4c9 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -776,6 +776,7 @@ extern void blk_queue_max_segment_size(struct request_queue *, unsigned int); extern void blk_queue_hardsect_size(struct request_queue *, unsigned short); extern void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b); extern void blk_queue_dma_pad(struct request_queue *, unsigned int); +extern void blk_queue_update_dma_pad(struct request_queue *, unsigned int); extern int blk_queue_dma_drain(struct request_queue *q, dma_drain_needed_fn *dma_drain_needed, void *buf, unsigned int size); -- cgit v1.2.3 From ba8dd03ac09f51a69c154b8cb508b701d713a2cd Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 4 Jul 2008 11:26:40 +0200 Subject: generic-ipi: fix s390 build bug forgot to remove #include from linux/smp.h while fixing the original s390 build bug. Patch below fixes this build bug caused by header inclusion dependencies: CC kernel/timer.o In file included from include/linux/spinlock.h:87, from include/linux/smp.h:11, from include/linux/kernel_stat.h:4, from kernel/timer.c:22: include/asm/spinlock.h: In function '__raw_spin_lock': include/asm/spinlock.h:69: error: implicit declaration of function 'smp_processor_id' Signed-off-by: Heiko Carstens Signed-off-by: Ingo Molnar --- include/linux/smp.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/smp.h b/include/linux/smp.h index 55261101d09a..48262f86c969 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -8,7 +8,6 @@ #include #include -#include #include extern void cpu_idle(void); -- cgit v1.2.3 From 198191c4a7ce4daba379608fb38b9bc5a4eedc61 Mon Sep 17 00:00:00 2001 From: Krzysztof Halasa Date: Mon, 30 Jun 2008 23:26:53 +0200 Subject: WAN: convert drivers to use built-in netdev_stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in using separate net_device_stats structs when the one in struct net_device is present. Compiles. Signed-off-by: Krzysztof Hałasa Signed-off-by: Jeff Garzik --- drivers/char/pcmcia/synclink_cs.c | 28 +++++++-------- drivers/char/synclink.c | 33 +++++++++--------- drivers/char/synclink_gt.c | 28 +++++++-------- drivers/char/synclinkmp.c | 31 ++++++++--------- drivers/net/wan/c101.c | 6 ++-- drivers/net/wan/dscc4.c | 22 ++++++------ drivers/net/wan/farsync.c | 70 +++++++++++++++++--------------------- drivers/net/wan/hd6457x.c | 33 +++++++++--------- drivers/net/wan/hdlc.c | 2 +- drivers/net/wan/hdlc_cisco.c | 4 +-- drivers/net/wan/hdlc_fr.c | 29 ++++++---------- drivers/net/wan/hdlc_raw_eth.c | 2 +- drivers/net/wan/hdlc_x25.c | 6 ++-- drivers/net/wan/pc300_drv.c | 71 +++++++++++++++++---------------------- drivers/net/wan/pc300_tty.c | 6 ++-- drivers/net/wan/wanxl.c | 26 +++++++------- include/linux/hdlc.h | 7 ---- 17 files changed, 174 insertions(+), 230 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 1dd0e992c83d..fb2fb159faa3 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -3886,9 +3886,8 @@ static bool rx_get_frame(MGSLPC_INFO *info) framesize = 0; #if SYNCLINK_GENERIC_HDLC { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif } else @@ -4144,7 +4143,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { MGSLPC_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) @@ -4159,8 +4157,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) info->tx_put = info->tx_count = skb->len; /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -4376,14 +4374,13 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { MGSLPC_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_tx_timeout(%s)\n",dev->name); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->lock,flags); tx_stop(info); @@ -4416,27 +4413,26 @@ static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_rx(%s)\n",dev->name); if (skb == NULL) { printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - stats->rx_dropped++; + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index ac5080df2565..9d247d8a87a3 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -6640,9 +6640,8 @@ static bool mgsl_get_rx_frame(struct mgsl_struct *info) framesize = 0; #if SYNCLINK_GENERIC_HDLC { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif } else @@ -7753,7 +7752,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { struct mgsl_struct *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) @@ -7767,8 +7765,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) mgsl_load_tx_dma_buffer(info, skb->data, skb->len); /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -7984,14 +7982,13 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { struct mgsl_struct *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_tx_timeout(%s)\n",dev->name); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->irq_spinlock,flags); usc_stop_transmitter(info); @@ -8024,27 +8021,27 @@ static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_rx(%s)\n",dev->name); + printk("hdlcdev_rx(%s)\n", dev->name); if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - stats->rx_dropped++; + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", + dev->name); + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 55c1653be00c..d88a607e34b7 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1544,7 +1544,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { struct slgt_info *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; DBGINFO(("%s hdlc_xmit\n", dev->name)); @@ -1557,8 +1556,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) tx_load(info, skb->data, skb->len); /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -1775,13 +1774,12 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { struct slgt_info *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->lock,flags); tx_stop(info); @@ -1814,26 +1812,25 @@ static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); DBGINFO(("%s hdlcdev_rx\n", dev->name)); if (skb == NULL) { DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); - stats->rx_dropped++; + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** @@ -4577,9 +4574,8 @@ check_again: #if SYNCLINK_GENERIC_HDLC if (framesize == 0) { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index bec54866e0bb..10241ed86100 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -1678,7 +1678,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { SLMP_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) @@ -1692,8 +1691,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) tx_load_dma_buffer(info, skb->data, skb->len); /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -1909,14 +1908,13 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { SLMP_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_tx_timeout(%s)\n",dev->name); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->lock,flags); tx_stop(info); @@ -1949,27 +1947,27 @@ static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_rx(%s)\n",dev->name); if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - stats->rx_dropped++; + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", + dev->name); + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** @@ -4983,9 +4981,8 @@ CheckAgain: framesize = 0; #if SYNCLINK_GENERIC_HDLC { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif } diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index c2cc42f723d5..c8e563106a4a 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -133,9 +133,9 @@ static void sca_msci_intr(port_t *port) sca_out(stat & (ST1_UDRN | ST1_CDCD), MSCI0_OFFSET + ST1, port); if (stat & ST1_UDRN) { - struct net_device_stats *stats = hdlc_stats(port_to_dev(port)); - stats->tx_errors++; /* TX Underrun error detected */ - stats->tx_fifo_errors++; + /* TX Underrun error detected */ + port_to_dev(port)->stats.tx_errors++; + port_to_dev(port)->stats.tx_fifo_errors++; } stat = sca_in(MSCI1_OFFSET + ST1, port); /* read MSCI1 ST1 status */ diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index c6f26e28e376..50ef5b4efd6d 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -642,7 +642,6 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev) { struct RxFD *rx_fd = dpriv->rx_fd + dpriv->rx_current%RX_RING_SIZE; - struct net_device_stats *stats = hdlc_stats(dev); struct pci_dev *pdev = dpriv->pci_priv->pdev; struct sk_buff *skb; int pkt_len; @@ -656,8 +655,8 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, pci_unmap_single(pdev, le32_to_cpu(rx_fd->data), RX_MAX(HDLC_MAX_MRU), PCI_DMA_FROMDEVICE); if ((skb->data[--pkt_len] & FrameOk) == FrameOk) { - stats->rx_packets++; - stats->rx_bytes += pkt_len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; skb_put(skb, pkt_len); if (netif_running(dev)) skb->protocol = hdlc_type_trans(skb, dev); @@ -665,13 +664,13 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, netif_rx(skb); } else { if (skb->data[pkt_len] & FrameRdo) - stats->rx_fifo_errors++; + dev->stats.rx_fifo_errors++; else if (!(skb->data[pkt_len] | ~FrameCrc)) - stats->rx_crc_errors++; + dev->stats.rx_crc_errors++; else if (!(skb->data[pkt_len] | ~(FrameVfr | FrameRab))) - stats->rx_length_errors++; + dev->stats.rx_length_errors++; else - stats->rx_errors++; + dev->stats.rx_errors++; dev_kfree_skb_irq(skb); } refill: @@ -1569,7 +1568,6 @@ try: if (state & SccEvt) { if (state & Alls) { - struct net_device_stats *stats = hdlc_stats(dev); struct sk_buff *skb; struct TxFD *tx_fd; @@ -1586,8 +1584,8 @@ try: pci_unmap_single(ppriv->pdev, le32_to_cpu(tx_fd->data), skb->len, PCI_DMA_TODEVICE); if (tx_fd->state & FrameEnd) { - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; } dev_kfree_skb_irq(skb); dpriv->tx_skbuff[cur] = NULL; @@ -1698,7 +1696,7 @@ try: } if (state & Err) { printk(KERN_INFO "%s: Tx ERR\n", dev->name); - hdlc_stats(dev)->tx_errors++; + dev->stats.tx_errors++; state &= ~Err; } } @@ -1834,7 +1832,7 @@ try: if (!(rx_fd->state2 & DataComplete)) break; if (rx_fd->state2 & FrameAborted) { - hdlc_stats(dev)->rx_over_errors++; + dev->stats.rx_over_errors++; rx_fd->state1 |= Hold; rx_fd->state2 = 0x00000000; rx_fd->end = cpu_to_le32(0xbabeface); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 547368e9633d..754f00809e3e 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -845,7 +845,6 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, int len, int txpos) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); /* * Everything is now set, just tell the card to go @@ -853,8 +852,8 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, dbg(DBG_TX, "fst_tx_dma_complete\n"); FST_WRB(card, txDescrRing[port->index][txpos].bits, DMA_OWN | TX_STP | TX_ENP); - stats->tx_packets++; - stats->tx_bytes += len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += len; dev->trans_start = jiffies; } @@ -876,7 +875,6 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, int len, struct sk_buff *skb, int rxp) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); int pi; int rx_status; @@ -888,8 +886,8 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); /* Update stats */ - stats->rx_packets++; - stats->rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; /* Push upstream */ dbg(DBG_RX, "Pushing the frame up the stack\n"); @@ -900,7 +898,7 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, rx_status = netif_rx(skb); fst_process_rx_status(rx_status, port_to_dev(port)->name); if (rx_status == NET_RX_DROP) - stats->rx_dropped++; + dev->stats.rx_dropped++; dev->last_rx = jiffies; } @@ -1163,29 +1161,28 @@ fst_log_rx_error(struct fst_card_info *card, struct fst_port_info *port, unsigned char dmabits, int rxp, unsigned short len) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); - /* + /* * Increment the appropriate error counter */ - stats->rx_errors++; + dev->stats.rx_errors++; if (dmabits & RX_OFLO) { - stats->rx_fifo_errors++; + dev->stats.rx_fifo_errors++; dbg(DBG_ASS, "Rx fifo error on card %d port %d buffer %d\n", card->card_no, port->index, rxp); } if (dmabits & RX_CRC) { - stats->rx_crc_errors++; + dev->stats.rx_crc_errors++; dbg(DBG_ASS, "Rx crc error on card %d port %d\n", card->card_no, port->index); } if (dmabits & RX_FRAM) { - stats->rx_frame_errors++; + dev->stats.rx_frame_errors++; dbg(DBG_ASS, "Rx frame error on card %d port %d\n", card->card_no, port->index); } if (dmabits == (RX_STP | RX_ENP)) { - stats->rx_length_errors++; + dev->stats.rx_length_errors++; dbg(DBG_ASS, "Rx length error (%d) on card %d port %d\n", len, card->card_no, port->index); } @@ -1242,7 +1239,6 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) unsigned short len; struct sk_buff *skb; struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); /* Check we have a buffer to process */ pi = port->index; @@ -1291,7 +1287,7 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) if ((skb = dev_alloc_skb(len)) == NULL) { dbg(DBG_RX, "intr_rx: can't allocate buffer\n"); - stats->rx_dropped++; + dev->stats.rx_dropped++; /* Return descriptor to card */ FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); @@ -1316,8 +1312,8 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); /* Update stats */ - stats->rx_packets++; - stats->rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; /* Push upstream */ dbg(DBG_RX, "Pushing frame up the stack\n"); @@ -1327,9 +1323,8 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) skb->protocol = hdlc_type_trans(skb, dev); rx_status = netif_rx(skb); fst_process_rx_status(rx_status, port_to_dev(port)->name); - if (rx_status == NET_RX_DROP) { - stats->rx_dropped++; - } + if (rx_status == NET_RX_DROP) + dev->stats.rx_dropped++; dev->last_rx = jiffies; } else { card->dma_skb_rx = skb; @@ -1361,7 +1356,6 @@ do_bottom_half_tx(struct fst_card_info *card) struct sk_buff *skb; unsigned long flags; struct net_device *dev; - struct net_device_stats *stats; /* * Find a free buffer for the transmit @@ -1373,12 +1367,10 @@ do_bottom_half_tx(struct fst_card_info *card) if (!port->run) continue; - dev = port_to_dev(port); - stats = hdlc_stats(dev); - while (! - (FST_RDB(card, txDescrRing[pi][port->txpos].bits) & - DMA_OWN) - && !(card->dmatx_in_progress)) { + dev = port_to_dev(port); + while (!(FST_RDB(card, txDescrRing[pi][port->txpos].bits) & + DMA_OWN) + && !(card->dmatx_in_progress)) { /* * There doesn't seem to be a txdone event per-se * We seem to have to deduce it, by checking the DMA_OWN @@ -1422,8 +1414,8 @@ do_bottom_half_tx(struct fst_card_info *card) txDescrRing[pi][port->txpos]. bits, DMA_OWN | TX_STP | TX_ENP); - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; dev->trans_start = jiffies; } else { /* Or do it through dma */ @@ -1628,8 +1620,8 @@ fst_intr(int dummy, void *dev_id) * always load up the entire packet for DMA. */ dbg(DBG_TX, "Tx underflow port %d\n", port->index); - hdlc_stats(port_to_dev(port))->tx_errors++; - hdlc_stats(port_to_dev(port))->tx_fifo_errors++; + port_to_dev(port)->stats.tx_errors++; + port_to_dev(port)->stats.tx_fifo_errors++; dbg(DBG_ASS, "Tx underflow on card %d port %d\n", card->card_no, port->index); break; @@ -2292,12 +2284,11 @@ fst_tx_timeout(struct net_device *dev) { struct fst_port_info *port; struct fst_card_info *card; - struct net_device_stats *stats = hdlc_stats(dev); port = dev_to_port(dev); card = port->card; - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; dbg(DBG_ASS, "Tx timeout card %d port %d\n", card->card_no, port->index); fst_issue_cmd(port, ABORTTX); @@ -2312,7 +2303,6 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct fst_card_info *card; struct fst_port_info *port; - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; int txq_length; @@ -2323,8 +2313,8 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Drop packet with error if we don't have carrier */ if (!netif_carrier_ok(dev)) { dev_kfree_skb(skb); - stats->tx_errors++; - stats->tx_carrier_errors++; + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; dbg(DBG_ASS, "Tried to transmit but no carrier on card %d port %d\n", card->card_no, port->index); @@ -2336,7 +2326,7 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) dbg(DBG_ASS, "Packet too large %d vs %d\n", skb->len, LEN_TX_BUFFER); dev_kfree_skb(skb); - stats->tx_errors++; + dev->stats.tx_errors++; return 0; } @@ -2368,7 +2358,7 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) * This shouldn't have happened but such is life */ dev_kfree_skb(skb); - stats->tx_errors++; + dev->stats.tx_errors++; dbg(DBG_ASS, "Tx queue overflow card %d port %d\n", card->card_no, port->index); return 0; diff --git a/drivers/net/wan/hd6457x.c b/drivers/net/wan/hd6457x.c index 8d0a1f2f00e5..591fb45a7c68 100644 --- a/drivers/net/wan/hd6457x.c +++ b/drivers/net/wan/hd6457x.c @@ -271,9 +271,9 @@ static inline void sca_msci_intr(port_t *port) sca_out(stat & (ST1_UDRN | ST1_CDCD), msci + ST1, card); if (stat & ST1_UDRN) { - struct net_device_stats *stats = hdlc_stats(port_to_dev(port)); - stats->tx_errors++; /* TX Underrun error detected */ - stats->tx_fifo_errors++; + /* TX Underrun error detected */ + port_to_dev(port)->stats.tx_errors++; + port_to_dev(port)->stats.tx_fifo_errors++; } if (stat & ST1_CDCD) @@ -286,7 +286,6 @@ static inline void sca_msci_intr(port_t *port) static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u16 rxin) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); struct sk_buff *skb; u16 len; u32 buff; @@ -298,7 +297,7 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u1 len = readw(&desc->len); skb = dev_alloc_skb(len); if (!skb) { - stats->rx_dropped++; + dev->stats.rx_dropped++; return; } @@ -327,8 +326,8 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u1 printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len); debug_frame(skb); #endif - stats->rx_packets++; - stats->rx_bytes += skb->len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; dev->last_rx = jiffies; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); @@ -339,17 +338,18 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u1 /* Receive DMA interrupt service */ static inline void sca_rx_intr(port_t *port) { + struct net_device *dev = port_to_dev(port); u16 dmac = get_dmac_rx(port); card_t *card = port_to_card(port); u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */ - struct net_device_stats *stats = hdlc_stats(port_to_dev(port)); /* Reset DSR status bits */ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE, DSR_RX(phy_node(port)), card); if (stat & DSR_BOF) - stats->rx_over_errors++; /* Dropped one or more frames */ + /* Dropped one or more frames */ + dev->stats.rx_over_errors++; while (1) { u32 desc_off = desc_offset(port, port->rxin, 0); @@ -364,12 +364,14 @@ static inline void sca_rx_intr(port_t *port) if (!(stat & ST_RX_EOM)) port->rxpart = 1; /* partial frame received */ else if ((stat & ST_ERROR_MASK) || port->rxpart) { - stats->rx_errors++; - if (stat & ST_RX_OVERRUN) stats->rx_fifo_errors++; + dev->stats.rx_errors++; + if (stat & ST_RX_OVERRUN) + dev->stats.rx_fifo_errors++; else if ((stat & (ST_RX_SHORT | ST_RX_ABORT | ST_RX_RESBIT)) || port->rxpart) - stats->rx_frame_errors++; - else if (stat & ST_RX_CRC) stats->rx_crc_errors++; + dev->stats.rx_frame_errors++; + else if (stat & ST_RX_CRC) + dev->stats.rx_crc_errors++; if (stat & ST_RX_EOM) port->rxpart = 0; /* received last fragment */ } else @@ -390,7 +392,6 @@ static inline void sca_rx_intr(port_t *port) static inline void sca_tx_intr(port_t *port) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); u16 dmac = get_dmac_tx(port); card_t* card = port_to_card(port); u8 stat; @@ -412,8 +413,8 @@ static inline void sca_tx_intr(port_t *port) break; /* Transmitter is/will_be sending this frame */ desc = desc_address(port, port->txlast, 1); - stats->tx_packets++; - stats->tx_bytes += readw(&desc->len); + dev->stats.tx_packets++; + dev->stats.tx_bytes += readw(&desc->len); writeb(0, &desc->stat); /* Free descriptor */ port->txlast = next_desc(port, port->txlast, 1); } diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 7f984895b0d5..e3a536477c7e 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -57,7 +57,7 @@ static int hdlc_change_mtu(struct net_device *dev, int new_mtu) static struct net_device_stats *hdlc_get_stats(struct net_device *dev) { - return hdlc_stats(dev); + return &dev->stats; } diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 762d21c1c703..849819c2552d 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -252,8 +252,8 @@ static int cisco_rx(struct sk_buff *skb) dev_kfree_skb_any(skb); return NET_RX_DROP; - rx_error: - dev_to_hdlc(dev)->stats.rx_errors++; /* Mark error */ +rx_error: + dev->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); return NET_RX_DROP; } diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 520bb0b1a9a2..cb1b415053eb 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -136,7 +136,6 @@ typedef struct pvc_device_struct { }pvc_device; struct pvc_desc { - struct net_device_stats stats; pvc_device *pvc; }; @@ -184,11 +183,6 @@ static inline struct pvc_desc* pvcdev_to_desc(struct net_device *dev) return dev->priv; } -static inline struct net_device_stats* pvc_get_stats(struct net_device *dev) -{ - return &pvcdev_to_desc(dev)->stats; -} - static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) { pvc_device *pvc = state(hdlc)->first_pvc; @@ -425,7 +419,6 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) { pvc_device *pvc = pvcdev_to_desc(dev)->pvc; - struct net_device_stats *stats = pvc_get_stats(dev); if (pvc->state.active) { if (dev->type == ARPHRD_ETHER) { @@ -435,7 +428,7 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) if (skb_tailroom(skb) < pad) if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) { - stats->tx_dropped++; + dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; } @@ -445,17 +438,17 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = __constant_htons(ETH_P_802_3); } if (!fr_hard_header(&skb, pvc->dlci)) { - stats->tx_bytes += skb->len; - stats->tx_packets++; + dev->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; if (pvc->state.fecn) /* TX Congestion counter */ - stats->tx_compressed++; + dev->stats.tx_compressed++; skb->dev = pvc->frad; dev_queue_xmit(skb); return 0; } } - stats->tx_dropped++; + dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; } @@ -955,7 +948,7 @@ static int fr_rx(struct sk_buff *skb) if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - dev_to_hdlc(frad)->stats.rx_dropped++; + frad->stats.rx_dropped++; return NET_RX_DROP; } @@ -1003,11 +996,10 @@ static int fr_rx(struct sk_buff *skb) } if (dev) { - struct net_device_stats *stats = pvc_get_stats(dev); - stats->rx_packets++; /* PVC traffic */ - stats->rx_bytes += skb->len; + dev->stats.rx_packets++; /* PVC traffic */ + dev->stats.rx_bytes += skb->len; if (pvc->state.becn) - stats->rx_compressed++; + dev->stats.rx_compressed++; netif_rx(skb); return NET_RX_SUCCESS; } else { @@ -1016,7 +1008,7 @@ static int fr_rx(struct sk_buff *skb) } rx_error: - dev_to_hdlc(frad)->stats.rx_errors++; /* Mark error */ + frad->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); return NET_RX_DROP; } @@ -1122,7 +1114,6 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) dlci_to_q922(dev->broadcast, dlci); } dev->hard_start_xmit = pvc_xmit; - dev->get_stats = pvc_get_stats; dev->open = pvc_open; dev->stop = pvc_close; dev->do_ioctl = pvc_ioctl; diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c index d20c685f6711..26dee600506f 100644 --- a/drivers/net/wan/hdlc_raw_eth.c +++ b/drivers/net/wan/hdlc_raw_eth.c @@ -33,7 +33,7 @@ static int eth_tx(struct sk_buff *skb, struct net_device *dev) int len = skb->len; if (skb_tailroom(skb) < pad) if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) { - hdlc_stats(dev)->tx_dropped++; + dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; } diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c index c15cc11e399b..e808720030ef 100644 --- a/drivers/net/wan/hdlc_x25.c +++ b/drivers/net/wan/hdlc_x25.c @@ -164,17 +164,15 @@ static void x25_close(struct net_device *dev) static int x25_rx(struct sk_buff *skb) { - struct hdlc_device *hdlc = dev_to_hdlc(skb->dev); - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - hdlc->stats.rx_dropped++; + skb->dev->stats.rx_dropped++; return NET_RX_DROP; } if (lapb_data_received(skb->dev, skb) == LAPB_OK) return NET_RX_SUCCESS; - hdlc->stats.rx_errors++; + skb->dev->stats.rx_errors++; dev_kfree_skb_any(skb); return NET_RX_DROP; } diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 57914fbd41d3..334170527755 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -285,7 +285,6 @@ static void rx_dma_buf_init(pc300_t *, int); static void tx_dma_buf_check(pc300_t *, int); static void rx_dma_buf_check(pc300_t *, int); static irqreturn_t cpc_intr(int, void *); -static struct net_device_stats *cpc_get_stats(struct net_device *); static int clock_rate_calc(uclong, uclong, int *); static uclong detect_ram(pc300_t *); static void plx_init(pc300_t *); @@ -1775,13 +1774,12 @@ static void cpc_tx_timeout(struct net_device *dev) pc300dev_t *d = (pc300dev_t *) dev->priv; pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - struct net_device_stats *stats = hdlc_stats(dev); int ch = chan->channel; unsigned long flags; ucchar ilar; - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; CPC_LOCK(card, flags); if ((ilar = cpc_readb(card->hw.scabase + ILAR)) != 0) { printk("%s: ILAR=0x%x\n", dev->name, ilar); @@ -1803,7 +1801,6 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) pc300dev_t *d = (pc300dev_t *) dev->priv; pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - struct net_device_stats *stats = hdlc_stats(dev); int ch = chan->channel; unsigned long flags; #ifdef PC300_DEBUG_TX @@ -1817,13 +1814,13 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) } else if (!netif_carrier_ok(dev)) { /* DCD must be OFF: drop packet */ dev_kfree_skb(skb); - stats->tx_errors++; - stats->tx_carrier_errors++; + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; return 0; } else if (cpc_readb(card->hw.scabase + M_REG(ST3, ch)) & ST3_DCD) { printk("%s: DCD is OFF. Going administrative down.\n", dev->name); - stats->tx_errors++; - stats->tx_carrier_errors++; + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; dev_kfree_skb(skb); netif_carrier_off(dev); CPC_LOCK(card, flags); @@ -1843,8 +1840,8 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) // printk("%s: write error. Dropping TX packet.\n", dev->name); netif_stop_queue(dev); dev_kfree_skb(skb); - stats->tx_errors++; - stats->tx_dropped++; + dev->stats.tx_errors++; + dev->stats.tx_dropped++; return 0; } #ifdef PC300_DEBUG_TX @@ -1886,7 +1883,6 @@ static void cpc_net_rx(struct net_device *dev) pc300dev_t *d = (pc300dev_t *) dev->priv; pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - struct net_device_stats *stats = hdlc_stats(dev); int ch = chan->channel; #ifdef PC300_DEBUG_RX int i; @@ -1922,24 +1918,24 @@ static void cpc_net_rx(struct net_device *dev) #endif if ((skb == NULL) && (rxb > 0)) { /* rxb > dev->mtu */ - stats->rx_errors++; - stats->rx_length_errors++; + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; continue; } if (rxb < 0) { /* Invalid frame */ rxb = -rxb; if (rxb & DST_OVR) { - stats->rx_errors++; - stats->rx_fifo_errors++; + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; } if (rxb & DST_CRC) { - stats->rx_errors++; - stats->rx_crc_errors++; + dev->stats.rx_errors++; + dev->stats.rx_crc_errors++; } if (rxb & (DST_RBIT | DST_SHRT | DST_ABT)) { - stats->rx_errors++; - stats->rx_frame_errors++; + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; } } if (skb) { @@ -1948,7 +1944,7 @@ static void cpc_net_rx(struct net_device *dev) continue; } - stats->rx_bytes += rxb; + dev->stats.rx_bytes += rxb; #ifdef PC300_DEBUG_RX printk("%s R:", dev->name); @@ -1959,7 +1955,7 @@ static void cpc_net_rx(struct net_device *dev) if (d->trace_on) { cpc_trace(dev, skb, 'R'); } - stats->rx_packets++; + dev->stats.rx_packets++; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); } @@ -1974,16 +1970,15 @@ static void sca_tx_intr(pc300dev_t *dev) pc300_t *card = (pc300_t *)chan->card; int ch = chan->channel; volatile pcsca_bd_t __iomem * ptdescr; - struct net_device_stats *stats = hdlc_stats(dev->dev); /* Clean up descriptors from previous transmission */ ptdescr = (card->hw.rambase + TX_BD_ADDR(ch,chan->tx_first_bd)); - while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != - TX_BD_ADDR(ch,chan->tx_first_bd)) && - (cpc_readb(&ptdescr->status) & DST_OSB)) { - stats->tx_packets++; - stats->tx_bytes += cpc_readw(&ptdescr->len); + while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != + TX_BD_ADDR(ch,chan->tx_first_bd)) && + (cpc_readb(&ptdescr->status) & DST_OSB)) { + dev->dev->stats.tx_packets++; + dev->dev->stats.tx_bytes += cpc_readw(&ptdescr->len); cpc_writeb(&ptdescr->status, DST_OSB); cpc_writew(&ptdescr->len, 0); chan->nfree_tx_bd++; @@ -2048,8 +2043,8 @@ static void sca_intr(pc300_t * card) } cpc_net_rx(dev); /* Discard invalid frames */ - hdlc_stats(dev)->rx_errors++; - hdlc_stats(dev)->rx_over_errors++; + dev->stats.rx_errors++; + dev->stats.rx_over_errors++; chan->rx_first_bd = 0; chan->rx_last_bd = N_DMA_RX_BUF - 1; rx_dma_start(card, ch); @@ -2115,8 +2110,8 @@ static void sca_intr(pc300_t * card) card->hw.cpld_reg2) & ~ (CPLD_REG2_FALC_LED1 << (2 * ch))); } - hdlc_stats(dev)->tx_errors++; - hdlc_stats(dev)->tx_fifo_errors++; + dev->stats.tx_errors++; + dev->stats.tx_fifo_errors++; sca_tx_intr(d); } } @@ -2604,7 +2599,7 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SIOCGPC300UTILSTATS: { if (!arg) { /* clear statistics */ - memset(hdlc_stats(dev), 0, sizeof(struct net_device_stats)); + memset(&dev->stats, 0, sizeof(dev->stats)); if (card->hw.type == PC300_TE) { memset(&chan->falc, 0, sizeof(falc_t)); } @@ -2615,8 +2610,8 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) pc300stats.hw_type = card->hw.type; pc300stats.line_on = card->chan[ch].d.line_on; pc300stats.line_off = card->chan[ch].d.line_off; - memcpy(&pc300stats.gen_stats, hdlc_stats(dev), - sizeof(struct net_device_stats)); + memcpy(&pc300stats.gen_stats, &dev->stats, + sizeof(dev->stats)); if (card->hw.type == PC300_TE) memcpy(&pc300stats.te_stats,&chan->falc,sizeof(falc_t)); if (copy_to_user(arg, &pc300stats, sizeof(pc300stats_t))) @@ -2823,11 +2818,6 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } } -static struct net_device_stats *cpc_get_stats(struct net_device *dev) -{ - return hdlc_stats(dev); -} - static int clock_rate_calc(uclong rate, uclong clock, int *br_io) { int br, tc; @@ -3394,7 +3384,6 @@ static void cpc_init_card(pc300_t * card) dev->stop = cpc_close; dev->tx_timeout = cpc_tx_timeout; dev->watchdog_timeo = PC300_TX_TIMEOUT; - dev->get_stats = cpc_get_stats; dev->set_multicast_list = NULL; dev->set_mac_address = NULL; dev->change_mtu = cpc_change_mtu; diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index e03eef2f2282..eae94ab6b818 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -458,7 +458,7 @@ static int cpc_tty_write(struct tty_struct *tty, const unsigned char *buf, int c CPC_TTY_DBG("%s: cpc_tty_write data len=%i\n",cpc_tty->name,count); pc300chan = (pc300ch_t *)((pc300dev_t*)cpc_tty->pc300dev)->chan; - stats = hdlc_stats(((pc300dev_t*)cpc_tty->pc300dev)->dev); + stats = &cpc_tty->pc300dev->dev->stats; card = (pc300_t *) pc300chan->card; ch = pc300chan->channel; @@ -743,7 +743,7 @@ void cpc_tty_receive(pc300dev_t *pc300dev) pc300_t *card = (pc300_t *)pc300chan->card; int ch = pc300chan->channel; volatile pcsca_bd_t __iomem * ptdescr; - struct net_device_stats *stats = hdlc_stats(pc300dev->dev); + struct net_device_stats *stats = &pc300dev->dev->stats; int rx_len, rx_aux; volatile unsigned char status; unsigned short first_bd = pc300chan->rx_first_bd; @@ -917,7 +917,7 @@ static int cpc_tty_send_to_card(pc300dev_t *dev,void* buf, int len) pc300ch_t *chan = (pc300ch_t *)dev->chan; pc300_t *card = (pc300_t *)chan->card; int ch = chan->channel; - struct net_device_stats *stats = hdlc_stats(dev->dev); + struct net_device_stats *stats = &dev->dev->stats; unsigned long flags; volatile pcsca_bd_t __iomem *ptdescr; int i, nchar; diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index d4aab8a28b61..a8a5ca0ee6c2 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -161,7 +161,6 @@ static inline void wanxl_cable_intr(port_t *port) static inline void wanxl_tx_intr(port_t *port) { struct net_device *dev = port->dev; - struct net_device_stats *stats = hdlc_stats(dev); while (1) { desc_t *desc = &get_status(port)->tx_descs[port->tx_in]; struct sk_buff *skb = port->tx_skbs[port->tx_in]; @@ -173,13 +172,13 @@ static inline void wanxl_tx_intr(port_t *port) return; case PACKET_UNDERRUN: - stats->tx_errors++; - stats->tx_fifo_errors++; + dev->stats.tx_errors++; + dev->stats.tx_fifo_errors++; break; default: - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; } desc->stat = PACKET_EMPTY; /* Free descriptor */ pci_unmap_single(port->card->pdev, desc->address, skb->len, @@ -205,10 +204,9 @@ static inline void wanxl_rx_intr(card_t *card) port_t *port = &card->ports[desc->stat & PACKET_PORT_MASK]; struct net_device *dev = port->dev; - struct net_device_stats *stats = hdlc_stats(dev); if (!skb) - stats->rx_dropped++; + dev->stats.rx_dropped++; else { pci_unmap_single(card->pdev, desc->address, BUFFER_LENGTH, @@ -220,8 +218,8 @@ static inline void wanxl_rx_intr(card_t *card) skb->len); debug_frame(skb); #endif - stats->rx_packets++; - stats->rx_bytes += skb->len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; dev->last_rx = jiffies; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); @@ -468,13 +466,13 @@ static int wanxl_close(struct net_device *dev) static struct net_device_stats *wanxl_get_stats(struct net_device *dev) { - struct net_device_stats *stats = hdlc_stats(dev); port_t *port = dev_to_port(dev); - stats->rx_over_errors = get_status(port)->rx_overruns; - stats->rx_frame_errors = get_status(port)->rx_frame_errors; - stats->rx_errors = stats->rx_over_errors + stats->rx_frame_errors; - return stats; + dev->stats.rx_over_errors = get_status(port)->rx_overruns; + dev->stats.rx_frame_errors = get_status(port)->rx_frame_errors; + dev->stats.rx_errors = dev->stats.rx_over_errors + + dev->stats.rx_frame_errors; + return &dev->stats; } diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index 6115545a5b9c..c59769693bee 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -45,7 +45,6 @@ struct hdlc_proto { /* Pointed to by dev->priv */ typedef struct hdlc_device { - struct net_device_stats stats; /* used by HDLC layer to take control over HDLC device from hw driver*/ int (*attach)(struct net_device *dev, unsigned short encoding, unsigned short parity); @@ -109,12 +108,6 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, /* May be used by hardware driver to gain control over HDLC device */ void detach_hdlc_protocol(struct net_device *dev); -static __inline__ struct net_device_stats *hdlc_stats(struct net_device *dev) -{ - return &dev_to_hdlc(dev)->stats; -} - - static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb, struct net_device *dev) { -- cgit v1.2.3 From cde53535991fbb5c34a1566f25955297c1487b8d Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 4 Jul 2008 09:59:22 -0700 Subject: Christoph has moved Remove all clameter@sgi.com addresses from the kernel tree since they will become invalid on June 27th. Change my maintainer email address for the slab allocators to cl@linux-foundation.org (which will be the new email address for the future). Signed-off-by: Christoph Lameter Signed-off-by: Christoph Lameter Cc: Pekka Enberg Cc: Stephen Rothwell Cc: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/slabinfo.c | 4 ++-- Documentation/vm/slub.txt | 2 +- MAINTAINERS | 2 +- include/asm-generic/atomic.h | 2 +- include/linux/slab.h | 2 +- include/linux/slub_def.h | 2 +- kernel/workqueue.c | 2 +- lib/radix-tree.c | 2 +- mm/allocpercpu.c | 2 +- mm/migrate.c | 2 +- mm/slub.c | 2 +- mm/sparse-vmemmap.c | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/Documentation/vm/slabinfo.c b/Documentation/vm/slabinfo.c index e4230ed16ee7..df3227605d59 100644 --- a/Documentation/vm/slabinfo.c +++ b/Documentation/vm/slabinfo.c @@ -1,7 +1,7 @@ /* * Slabinfo: Tool to get reports about slabs * - * (C) 2007 sgi, Christoph Lameter + * (C) 2007 sgi, Christoph Lameter * * Compile by: * @@ -99,7 +99,7 @@ void fatal(const char *x, ...) void usage(void) { - printf("slabinfo 5/7/2007. (c) 2007 sgi. clameter@sgi.com\n\n" + printf("slabinfo 5/7/2007. (c) 2007 sgi.\n\n" "slabinfo [-ahnpvtsz] [-d debugopts] [slab-regexp]\n" "-a|--aliases Show aliases\n" "-A|--activity Most active slabs first\n" diff --git a/Documentation/vm/slub.txt b/Documentation/vm/slub.txt index 7c13f22a0c9e..bb1f5c6e28b3 100644 --- a/Documentation/vm/slub.txt +++ b/Documentation/vm/slub.txt @@ -266,4 +266,4 @@ of other objects. slub_debug=FZ,dentry -Christoph Lameter, , May 30, 2007 +Christoph Lameter, May 30, 2007 diff --git a/MAINTAINERS b/MAINTAINERS index 460e699fd280..13b7b19692e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3672,7 +3672,7 @@ S: Maintained SLAB ALLOCATOR P: Christoph Lameter -M: clameter@sgi.com +M: cl@linux-foundation.org P: Pekka Enberg M: penberg@cs.helsinki.fi P: Matt Mackall diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h index 85fd0aa27a8c..4ec0a296bdec 100644 --- a/include/asm-generic/atomic.h +++ b/include/asm-generic/atomic.h @@ -2,7 +2,7 @@ #define _ASM_GENERIC_ATOMIC_H /* * Copyright (C) 2005 Silicon Graphics, Inc. - * Christoph Lameter + * Christoph Lameter * * Allows to provide arch independent atomic definitions without the need to * edit all arch specific atomic.h files. diff --git a/include/linux/slab.h b/include/linux/slab.h index c2ad35016599..9aa90a6f20e0 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1,7 +1,7 @@ /* * Written by Mark Hemment, 1996 (markhe@nextd.demon.co.uk). * - * (C) SGI 2006, Christoph Lameter + * (C) SGI 2006, Christoph Lameter * Cleaned up and restructured to ease the addition of alternative * implementations of SLAB allocators. */ diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index cef6f8fddd7d..d117ea2825a9 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -4,7 +4,7 @@ /* * SLUB : A Slab allocator without object queues. * - * (C) 2007 SGI, Christoph Lameter + * (C) 2007 SGI, Christoph Lameter */ #include #include diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 29fc39f1029c..ce7799540c91 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -13,7 +13,7 @@ * Kai Petzke * Theodore Ts'o * - * Made to use alloc_percpu by Christoph Lameter . + * Made to use alloc_percpu by Christoph Lameter. */ #include diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 169a2f8dabcc..56ec21a7f73d 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2001 Momchil Velikov * Portions Copyright (C) 2001 Christoph Hellwig - * Copyright (C) 2005 SGI, Christoph Lameter + * Copyright (C) 2005 SGI, Christoph Lameter * Copyright (C) 2006 Nick Piggin * * This program is free software; you can redistribute it and/or diff --git a/mm/allocpercpu.c b/mm/allocpercpu.c index f4026bae6eed..05f2b4009ccc 100644 --- a/mm/allocpercpu.c +++ b/mm/allocpercpu.c @@ -1,7 +1,7 @@ /* * linux/mm/allocpercpu.c * - * Separated from slab.c August 11, 2006 Christoph Lameter + * Separated from slab.c August 11, 2006 Christoph Lameter */ #include #include diff --git a/mm/migrate.c b/mm/migrate.c index 112bcaeaa104..55bd355d170d 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -9,7 +9,7 @@ * IWAMOTO Toshihiro * Hirokazu Takahashi * Dave Hansen - * Christoph Lameter + * Christoph Lameter */ #include diff --git a/mm/slub.c b/mm/slub.c index 2c9a62d1f429..1a427c0ae83b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5,7 +5,7 @@ * The allocator synchronizes using per slab locks and only * uses a centralized lock to manage a pool of partial slabs. * - * (C) 2007 SGI, Christoph Lameter + * (C) 2007 SGI, Christoph Lameter */ #include diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 99c4f36eb8a3..a91b5f8fcaf6 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -1,7 +1,7 @@ /* * Virtual Memory Map support * - * (C) 2007 sgi. Christoph Lameter . + * (C) 2007 sgi. Christoph Lameter. * * Virtual memory maps allow VM primitives pfn_to_page, page_to_pfn, * virt_to_page, page_address() to be implemented as a base offset -- cgit v1.2.3 From 69d44a1835ec8163a82c4ee57367f87ae0f85c2e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 4 Jul 2008 09:59:27 -0700 Subject: firmware: fix the request_firmware() dummy > the build (.config attached) failed, make ends with : > ... > UPD include/linux/compile.h > CC init/version.o > LD init/built-in.o > LD vmlinux > drivers/built-in.o: In function `sas_request_addr': > (.text+0x33bab): undefined reference to `request_firmware' > drivers/built-in.o: In function `sas_request_addr': > (.text+0x33c3f): undefined reference to `release_firmware' > make: *** [vmlinux] Error 1 There's a slight fault in the stub logic. It fails for FW_LOADER=m and the user =y. This should fix it. This patch fixes the following 2.6.26-rc regression: http://bugzilla.kernel.org/show_bug.cgi?id=10730 Reviewed-by: Toralf Foerster Signed-off-by: Adrian Bunk Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/firmware.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 4d10c7328d2d..6c7eff2ebada 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -13,7 +13,7 @@ struct firmware { struct device; -#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +#if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE)) int request_firmware(const struct firmware **fw, const char *name, struct device *device); int request_firmware_nowait( -- cgit v1.2.3 From 450c622e9ff19888818d4e2c4d31adb97a5242b2 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 4 Jul 2008 09:59:33 -0700 Subject: Miguel Ojeda has moved Signed-off-by: Miguel Ojeda Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- CREDITS | 5 +++-- Documentation/auxdisplay/cfag12864b | 4 ++-- Documentation/auxdisplay/cfag12864b-example.c | 2 +- Documentation/auxdisplay/ks0108 | 4 ++-- MAINTAINERS | 20 ++++++++++++-------- drivers/auxdisplay/Kconfig | 2 +- drivers/auxdisplay/cfag12864b.c | 4 ++-- drivers/auxdisplay/cfag12864bfb.c | 4 ++-- drivers/auxdisplay/ks0108.c | 4 ++-- include/linux/cfag12864b.h | 2 +- include/linux/ks0108.h | 2 +- 11 files changed, 29 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/CREDITS b/CREDITS index 8fec7b3f96d5..e97bea06b59f 100644 --- a/CREDITS +++ b/CREDITS @@ -2611,8 +2611,9 @@ S: Perth, Western Australia S: Australia N: Miguel Ojeda Sandonis -E: maxextreme@gmail.com -W: http://maxextreme.googlepages.com/ +E: miguel.ojeda.sandonis@gmail.com +W: http://miguelojeda.es +W: http://jair.lab.fi.uva.es/~migojed/ D: Author of the ks0108, cfag12864b and cfag12864bfb auxiliary display drivers. D: Maintainer of the auxiliary display drivers tree (drivers/auxdisplay/*) S: C/ Mieses 20, 9-B diff --git a/Documentation/auxdisplay/cfag12864b b/Documentation/auxdisplay/cfag12864b index b714183d4125..eb7be393a510 100644 --- a/Documentation/auxdisplay/cfag12864b +++ b/Documentation/auxdisplay/cfag12864b @@ -3,7 +3,7 @@ =================================== License: GPLv2 -Author & Maintainer: Miguel Ojeda Sandonis +Author & Maintainer: Miguel Ojeda Sandonis Date: 2006-10-27 @@ -22,7 +22,7 @@ Date: 2006-10-27 1. DRIVER INFORMATION --------------------- -This driver support one cfag12864b display at time. +This driver supports a cfag12864b LCD. --------------------- diff --git a/Documentation/auxdisplay/cfag12864b-example.c b/Documentation/auxdisplay/cfag12864b-example.c index 7bfac354d4c9..2caeea5e4993 100644 --- a/Documentation/auxdisplay/cfag12864b-example.c +++ b/Documentation/auxdisplay/cfag12864b-example.c @@ -4,7 +4,7 @@ * Description: cfag12864b LCD userspace example program * License: GPLv2 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify diff --git a/Documentation/auxdisplay/ks0108 b/Documentation/auxdisplay/ks0108 index 92b03b60c613..8ddda0c8ceef 100644 --- a/Documentation/auxdisplay/ks0108 +++ b/Documentation/auxdisplay/ks0108 @@ -3,7 +3,7 @@ ========================================== License: GPLv2 -Author & Maintainer: Miguel Ojeda Sandonis +Author & Maintainer: Miguel Ojeda Sandonis Date: 2006-10-27 @@ -21,7 +21,7 @@ Date: 2006-10-27 1. DRIVER INFORMATION --------------------- -This driver support the ks0108 LCD controller. +This driver supports the ks0108 LCD controller. --------------------- diff --git a/MAINTAINERS b/MAINTAINERS index 13b7b19692e6..ba7ac13aba97 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -763,9 +763,10 @@ S: Maintained AUXILIARY DISPLAY DRIVERS P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained AVR32 ARCHITECTURE @@ -1055,16 +1056,18 @@ S: Supported CFAG12864B LCD DRIVER P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained CFAG12864BFB LCD FRAMEBUFFER DRIVER P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained CFG80211 and NL80211 @@ -2428,9 +2431,10 @@ S: Maintained KS0108 LCD CONTROLLER DRIVER P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained LAPB module diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 043353bd0600..14b9d5f4c203 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -64,7 +64,7 @@ config KS0108_DELAY Amount of time the ks0108 should wait between each control write to the parallel port. - If your driver seems to miss random writings, increment this. + If your LCD seems to miss random writings, increment this. If you don't know what I'm talking about, ignore it. diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c index 80bb06105387..683509f013ab 100644 --- a/drivers/auxdisplay/cfag12864b.c +++ b/drivers/auxdisplay/cfag12864b.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: ks0108 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -398,5 +398,5 @@ module_init(cfag12864b_init); module_exit(cfag12864b_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis "); +MODULE_AUTHOR("Miguel Ojeda Sandonis "); MODULE_DESCRIPTION("cfag12864b LCD driver"); diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c index 307c190699e0..fe3a865be4e5 100644 --- a/drivers/auxdisplay/cfag12864bfb.c +++ b/drivers/auxdisplay/cfag12864bfb.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: cfag12864b * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -186,5 +186,5 @@ module_init(cfag12864bfb_init); module_exit(cfag12864bfb_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis "); +MODULE_AUTHOR("Miguel Ojeda Sandonis "); MODULE_DESCRIPTION("cfag12864b LCD framebuffer driver"); diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c index e6c3646ef18c..5b93852392b8 100644 --- a/drivers/auxdisplay/ks0108.c +++ b/drivers/auxdisplay/ks0108.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: parport * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -173,6 +173,6 @@ module_init(ks0108_init); module_exit(ks0108_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis "); +MODULE_AUTHOR("Miguel Ojeda Sandonis "); MODULE_DESCRIPTION("ks0108 LCD Controller driver"); diff --git a/include/linux/cfag12864b.h b/include/linux/cfag12864b.h index 1605dd8aa646..6f9f19d66591 100644 --- a/include/linux/cfag12864b.h +++ b/include/linux/cfag12864b.h @@ -4,7 +4,7 @@ * Description: cfag12864b LCD driver header * License: GPLv2 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-12 * * This program is free software; you can redistribute it and/or modify diff --git a/include/linux/ks0108.h b/include/linux/ks0108.h index a2c54acceb4e..cb311798e0bc 100644 --- a/include/linux/ks0108.h +++ b/include/linux/ks0108.h @@ -4,7 +4,7 @@ * Description: ks0108 LCD Controller driver header * License: GPLv2 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify -- cgit v1.2.3 From 93921f5c2ce7427cc30341c86882527d1d1d8770 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 4 Jul 2008 09:59:48 -0700 Subject: Introduce rculist.h In linux-next there is a commit ("rcu: split list.h and move rcu-protected lists into rculist.h") that moved the rcu related list iterators from list.h to rculist.h. Add a trivial version of the file now so that various subsystem trees can start using it now for -next changes and so reduce the build errors caused by adding uses of the moved functions. Cc: Franck Bui-Huu Acked-by: Paul E. McKenney Cc: Josh Triplett Acked-by: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rculist.h | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 include/linux/rculist.h (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h new file mode 100644 index 000000000000..bde4586f4382 --- /dev/null +++ b/include/linux/rculist.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_RCULIST_H +#define _LINUX_RCULIST_H + +#include + +#endif /* _LINUX_RCULIST_H */ -- cgit v1.2.3 From 086f7316f0d400806d76323beefae996bb3849b1 Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Fri, 4 Jul 2008 09:59:58 -0700 Subject: security: filesystem capabilities: fix fragile setuid fixup code This commit includes a bugfix for the fragile setuid fixup code in the case that filesystem capabilities are supported (in access()). The effect of this fix is gated on filesystem capability support because changing securebits is only supported when filesystem capabilities support is configured.) [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Andrew G. Morgan Acked-by: Serge Hallyn Acked-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/open.c | 37 ++++++++++++++++++++++--------------- include/linux/capability.h | 2 ++ include/linux/securebits.h | 15 ++++++++------- kernel/capability.c | 21 +++++++++++++++++++++ 4 files changed, 53 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/fs/open.c b/fs/open.c index a1450086e92f..a99ad09c3197 100644 --- a/fs/open.c +++ b/fs/open.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -425,7 +426,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { struct nameidata nd; int old_fsuid, old_fsgid; - kernel_cap_t old_cap; + kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */ int res; if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ @@ -433,23 +434,27 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) old_fsuid = current->fsuid; old_fsgid = current->fsgid; - old_cap = current->cap_effective; current->fsuid = current->uid; current->fsgid = current->gid; - /* - * Clear the capabilities if we switch to a non-root user - * - * FIXME: There is a race here against sys_capset. The - * capabilities can change yet we will restore the old - * value below. We should hold task_capabilities_lock, - * but we cannot because user_path_walk can sleep. - */ - if (current->uid) - cap_clear(current->cap_effective); - else - current->cap_effective = current->cap_permitted; + if (!issecure(SECURE_NO_SETUID_FIXUP)) { + /* + * Clear the capabilities if we switch to a non-root user + */ +#ifndef CONFIG_SECURITY_FILE_CAPABILITIES + /* + * FIXME: There is a race here against sys_capset. The + * capabilities can change yet we will restore the old + * value below. We should hold task_capabilities_lock, + * but we cannot because user_path_walk can sleep. + */ +#endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */ + if (current->uid) + old_cap = cap_set_effective(__cap_empty_set); + else + old_cap = cap_set_effective(current->cap_permitted); + } res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); if (res) @@ -478,7 +483,9 @@ out_path_release: out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; - current->cap_effective = old_cap; + + if (!issecure(SECURE_NO_SETUID_FIXUP)) + cap_set_effective(old_cap); return res; } diff --git a/include/linux/capability.h b/include/linux/capability.h index fa830f8de032..02673846d205 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -501,6 +501,8 @@ extern const kernel_cap_t __cap_empty_set; extern const kernel_cap_t __cap_full_set; extern const kernel_cap_t __cap_init_eff_set; +kernel_cap_t cap_set_effective(const kernel_cap_t pE_new); + int capable(int cap); int __capable(struct task_struct *t, int cap); diff --git a/include/linux/securebits.h b/include/linux/securebits.h index c1f19dbceb05..92f09bdf1175 100644 --- a/include/linux/securebits.h +++ b/include/linux/securebits.h @@ -7,14 +7,15 @@ inheritance of root-permissions and suid-root executable under compatibility mode. We raise the effective and inheritable bitmasks *of the executable file* if the effective uid of the new process is - 0. If the real uid is 0, we raise the inheritable bitmask of the + 0. If the real uid is 0, we raise the effective (legacy) bit of the executable file. */ #define SECURE_NOROOT 0 #define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ -/* When set, setuid to/from uid 0 does not trigger capability-"fixes" - to be compatible with old programs relying on set*uid to loose - privileges. When unset, setuid doesn't change privileges. */ +/* When set, setuid to/from uid 0 does not trigger capability-"fixup". + When unset, to provide compatiblility with old programs relying on + set*uid to gain/lose privilege, transitions to/from uid 0 cause + capabilities to be gained/lost. */ #define SECURE_NO_SETUID_FIXUP 2 #define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ @@ -26,10 +27,10 @@ #define SECURE_KEEP_CAPS 4 #define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ -/* Each securesetting is implemented using two bits. One bit specify +/* Each securesetting is implemented using two bits. One bit specifies whether the setting is on or off. The other bit specify whether the - setting is fixed or not. A setting which is fixed cannot be changed - from user-level. */ + setting is locked or not. A setting which is locked cannot be + changed from user-level. */ #define issecure_mask(X) (1 << (X)) #define issecure(X) (issecure_mask(X) & current->securebits) diff --git a/kernel/capability.c b/kernel/capability.c index cfbe44299488..901e0fdc3fff 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -121,6 +121,27 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy) * uninteresting and/or not to be changed. */ +/* + * Atomically modify the effective capabilities returning the original + * value. No permission check is performed here - it is assumed that the + * caller is permitted to set the desired effective capabilities. + */ +kernel_cap_t cap_set_effective(const kernel_cap_t pE_new) +{ + kernel_cap_t pE_old; + + spin_lock(&task_capability_lock); + + pE_old = current->cap_effective; + current->cap_effective = pE_new; + + spin_unlock(&task_capability_lock); + + return pE_old; +} + +EXPORT_SYMBOL(cap_set_effective); + /** * sys_capget - get the capabilities of a given process. * @header: pointer to struct that contains capability version and -- cgit v1.2.3 From e08c1694d9e2138204f2b79b73f0f159074ce2f5 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 4 Jul 2008 10:00:03 -0700 Subject: olpc: sdhci: add quirk for the Marvell CaFe's vdd/powerup issue This has been sitting around unloved for way too long.. The Marvell CaFe chip's SD implementation chokes during card insertion if one attempts to set the voltage and power up in the same SDHCI_POWER_CONTROL register write. This adds a quirk that does that particular dance in two steps. It also adds an entry to pci_ids.h for the CaFe chip's SD device. Signed-off-by: Andres Salomon Cc: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/sdhci.c | 18 ++++++++++++++++++ include/linux/pci_ids.h | 1 + 2 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 07c2048b230b..5b74c8cf4409 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -55,6 +55,8 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7) /* Controller needs to be reset after each request to stay stable */ #define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) +/* Controller needs voltage and power writes to happen separately */ +#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -127,6 +129,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, }, + { + .vendor = PCI_VENDOR_ID_MARVELL, + .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER, + }, + { .vendor = PCI_VENDOR_ID_JMICRON, .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD, @@ -774,6 +784,14 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) BUG(); } + /* + * At least the CaFe chip gets confused if we set the voltage + * and set turn on power at the same time, so set the voltage first. + */ + if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) + writeb(pwr & ~SDHCI_POWER_ON, + host->ioaddr + SDHCI_POWER_CONTROL); + writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); out: diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index eafc9d6d2b35..65953822c9cb 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1520,6 +1520,7 @@ #define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 #define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_CAFE_SD 0x4101 #define PCI_VENDOR_ID_V3 0x11b0 #define PCI_DEVICE_ID_V3_V960 0x0001 -- cgit v1.2.3 From acb7669c125676e63cf96582455509216c39745e Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 4 Jul 2008 10:00:05 -0700 Subject: cpumask: introduce new APIs In linux-next there is a commit ("x86: Add performance variants of cpumask operators") which, as part of the 4096 cpu support work adds some new APIs for dealing with cpu masks. Add trivial versions of these now so that subsystems can update in a timely manner and avoid conflicts in linux-next and the next merge window. Cc: Mike Travis Cc: Thomas Gleixner Cc: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpumask.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 5df3db58fcc6..c24875bd9c5b 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -353,6 +353,10 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) #endif /* NR_CPUS */ +#define next_cpu_nr(n, src) next_cpu(n, src) +#define cpus_weight_nr(cpumask) cpus_weight(cpumask) +#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) + /* * The following particular system cpumasks and operations manage * possible, present and online cpus. Each of them is a fixed size -- cgit v1.2.3 From ca31e146d5c2fe51498e619eb3a64782d02e310a Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Sat, 5 Jul 2008 12:14:23 +0300 Subject: Move _RET_IP_ and _THIS_IP_ to include/linux/kernel.h These two macros are useful beyond lock debugging. Moved definitions from include/linux/debug_locks.h to include/linux/kernel.h, so code that needs them does not have to include the former, which would have been a less intuitive choice of a header. Signed-off-by: Eduard - Gabriel Munteanu Acked-by: Pekka Enberg Signed-off-by: Linus Torvalds --- include/linux/debug_locks.h | 10 ++-------- include/linux/kernel.h | 3 +++ 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h index f4a5871767f5..4aaa4afb1cb9 100644 --- a/include/linux/debug_locks.h +++ b/include/linux/debug_locks.h @@ -1,6 +1,8 @@ #ifndef __LINUX_DEBUG_LOCKING_H #define __LINUX_DEBUG_LOCKING_H +#include + struct task_struct; extern int debug_locks; @@ -11,14 +13,6 @@ extern int debug_locks_silent; */ extern int debug_locks_off(void); -/* - * In the debug case we carry the caller's instruction pointer into - * other functions, but we dont want the function argument overhead - * in the nondebug case - hence these macros: - */ -#define _RET_IP_ (unsigned long)__builtin_return_address(0) -#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) - #define DEBUG_LOCKS_WARN_ON(c) \ ({ \ int __ret = 0; \ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 792bf0aa779b..2e70006c7fa8 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -46,6 +46,9 @@ extern const char linux_proc_banner[]; #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define _RET_IP_ (unsigned long)__builtin_return_address(0) +#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) + #ifdef CONFIG_LBD # include # define sector_div(a, b) do_div(a, b) -- cgit v1.2.3 From eca9ebac651f774d8b10fce7c5d173c3c3d3394f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 5 Jul 2008 21:26:13 -0700 Subject: net: Add GARP applicant-only participant Add an implementation of the GARP (Generic Attribute Registration Protocol) applicant-only participant. This will be used by the following patch to add GVRP support to the VLAN code. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 + include/net/garp.h | 127 ++++++++++ net/802/Kconfig | 4 + net/802/Makefile | 1 + net/802/garp.c | 633 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 767 insertions(+) create mode 100644 include/net/garp.h create mode 100644 net/802/garp.c (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 56dadb528f67..e009c6fbf5cd 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -740,6 +740,8 @@ struct net_device struct net_bridge_port *br_port; /* macvlan */ struct macvlan_port *macvlan_port; + /* GARP */ + struct garp_port *garp_port; /* class/net/name entry */ struct device dev; diff --git a/include/net/garp.h b/include/net/garp.h new file mode 100644 index 000000000000..73c772395f5b --- /dev/null +++ b/include/net/garp.h @@ -0,0 +1,127 @@ +#ifndef _NET_GARP_H +#define _NET_GARP_H + +#include + +#define GARP_PROTOCOL_ID 0x1 +#define GARP_END_MARK 0x0 + +struct garp_pdu_hdr { + __be16 protocol; +}; + +struct garp_msg_hdr { + u8 attrtype; +}; + +enum garp_attr_event { + GARP_LEAVE_ALL, + GARP_JOIN_EMPTY, + GARP_JOIN_IN, + GARP_LEAVE_EMPTY, + GARP_LEAVE_IN, + GARP_EMPTY, +}; + +struct garp_attr_hdr { + u8 len; + u8 event; + u8 data[]; +}; + +struct garp_skb_cb { + u8 cur_type; +}; + +static inline struct garp_skb_cb *garp_cb(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct garp_skb_cb) > + FIELD_SIZEOF(struct sk_buff, cb)); + return (struct garp_skb_cb *)skb->cb; +} + +enum garp_applicant_state { + GARP_APPLICANT_INVALID, + GARP_APPLICANT_VA, + GARP_APPLICANT_AA, + GARP_APPLICANT_QA, + GARP_APPLICANT_LA, + GARP_APPLICANT_VP, + GARP_APPLICANT_AP, + GARP_APPLICANT_QP, + GARP_APPLICANT_VO, + GARP_APPLICANT_AO, + GARP_APPLICANT_QO, + __GARP_APPLICANT_MAX +}; +#define GARP_APPLICANT_MAX (__GARP_APPLICANT_MAX - 1) + +enum garp_event { + GARP_EVENT_REQ_JOIN, + GARP_EVENT_REQ_LEAVE, + GARP_EVENT_R_JOIN_IN, + GARP_EVENT_R_JOIN_EMPTY, + GARP_EVENT_R_EMPTY, + GARP_EVENT_R_LEAVE_IN, + GARP_EVENT_R_LEAVE_EMPTY, + GARP_EVENT_TRANSMIT_PDU, + __GARP_EVENT_MAX +}; +#define GARP_EVENT_MAX (__GARP_EVENT_MAX - 1) + +enum garp_action { + GARP_ACTION_NONE, + GARP_ACTION_S_JOIN_IN, + GARP_ACTION_S_LEAVE_EMPTY, +}; + +struct garp_attr { + struct rb_node node; + enum garp_applicant_state state; + u8 type; + u8 dlen; + unsigned char data[]; +}; + +enum garp_applications { + __GARP_APPLICATION_MAX +}; +#define GARP_APPLICATION_MAX (__GARP_APPLICATION_MAX - 1) + +struct garp_application { + enum garp_applications type; + unsigned int maxattr; + struct stp_proto proto; +}; + +struct garp_applicant { + struct garp_application *app; + struct net_device *dev; + struct timer_list join_timer; + + spinlock_t lock; + struct sk_buff_head queue; + struct sk_buff *pdu; + struct rb_root gid; +}; + +struct garp_port { + struct garp_applicant *applicants[GARP_APPLICATION_MAX + 1]; +}; + +extern int garp_register_application(struct garp_application *app); +extern void garp_unregister_application(struct garp_application *app); + +extern int garp_init_applicant(struct net_device *dev, + struct garp_application *app); +extern void garp_uninit_applicant(struct net_device *dev, + struct garp_application *app); + +extern int garp_request_join(const struct net_device *dev, + const struct garp_application *app, + const void *data, u8 len, u8 type); +extern void garp_request_leave(const struct net_device *dev, + const struct garp_application *app, + const void *data, u8 len, u8 type); + +#endif /* _NET_GARP_H */ diff --git a/net/802/Kconfig b/net/802/Kconfig index 01cb0943626b..be33d27c8e69 100644 --- a/net/802/Kconfig +++ b/net/802/Kconfig @@ -1,3 +1,7 @@ config STP tristate select LLC + +config GARP + tristate + select STP diff --git a/net/802/Makefile b/net/802/Makefile index c441d895ac25..7893d679910c 100644 --- a/net/802/Makefile +++ b/net/802/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_HIPPI) += hippi.o obj-$(CONFIG_IPX) += p8022.o psnap.o p8023.o obj-$(CONFIG_ATALK) += p8022.o psnap.o obj-$(CONFIG_STP) += stp.o +obj-$(CONFIG_GARP) += garp.o diff --git a/net/802/garp.c b/net/802/garp.c new file mode 100644 index 000000000000..3b78f7b74fd4 --- /dev/null +++ b/net/802/garp.c @@ -0,0 +1,633 @@ +/* + * IEEE 802.1D Generic Attribute Registration Protocol (GARP) + * + * Copyright (c) 2008 Patrick McHardy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int garp_join_time __read_mostly = 200; +module_param(garp_join_time, uint, 0644); +MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)"); +MODULE_LICENSE("GPL"); + +static const struct garp_state_trans { + u8 state; + u8 action; +} garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = { + [GARP_APPLICANT_VA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, + }, + [GARP_APPLICANT_AA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, + }, + [GARP_APPLICANT_QA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, + }, + [GARP_APPLICANT_LA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO, + .action = GARP_ACTION_S_LEAVE_EMPTY }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, + [GARP_APPLICANT_VP] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO }, + }, + [GARP_APPLICANT_AP] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO }, + }, + [GARP_APPLICANT_QP] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO }, + }, + [GARP_APPLICANT_VO] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, + [GARP_APPLICANT_AO] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, + [GARP_APPLICANT_QO] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, +}; + +static int garp_attr_cmp(const struct garp_attr *attr, + const void *data, u8 len, u8 type) +{ + if (attr->type != type) + return attr->type - type; + if (attr->dlen != len) + return attr->dlen - len; + return memcmp(attr->data, data, len); +} + +static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app, + const void *data, u8 len, u8 type) +{ + struct rb_node *parent = app->gid.rb_node; + struct garp_attr *attr; + int d; + + while (parent) { + attr = rb_entry(parent, struct garp_attr, node); + d = garp_attr_cmp(attr, data, len, type); + if (d < 0) + parent = parent->rb_left; + else if (d > 0) + parent = parent->rb_right; + else + return attr; + } + return NULL; +} + +static void garp_attr_insert(struct garp_applicant *app, struct garp_attr *new) +{ + struct rb_node *parent = NULL, **p = &app->gid.rb_node; + struct garp_attr *attr; + int d; + + while (*p) { + parent = *p; + attr = rb_entry(parent, struct garp_attr, node); + d = garp_attr_cmp(attr, new->data, new->dlen, new->type); + if (d < 0) + p = &parent->rb_left; + else if (d > 0) + p = &parent->rb_right; + } + rb_link_node(&new->node, parent, p); + rb_insert_color(&new->node, &app->gid); +} + +static struct garp_attr *garp_attr_create(struct garp_applicant *app, + const void *data, u8 len, u8 type) +{ + struct garp_attr *attr; + + attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); + if (!attr) + return attr; + attr->state = GARP_APPLICANT_VO; + attr->type = type; + attr->dlen = len; + memcpy(attr->data, data, len); + garp_attr_insert(app, attr); + return attr; +} + +static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr) +{ + rb_erase(&attr->node, &app->gid); + kfree(attr); +} + +static int garp_pdu_init(struct garp_applicant *app) +{ + struct sk_buff *skb; + struct garp_pdu_hdr *gp; + +#define LLC_RESERVE sizeof(struct llc_pdu_un) + skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev), + GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + skb->dev = app->dev; + skb->protocol = htons(ETH_P_802_2); + skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE); + + gp = (struct garp_pdu_hdr *)__skb_put(skb, sizeof(*gp)); + put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol); + + app->pdu = skb; + return 0; +} + +static int garp_pdu_append_end_mark(struct garp_applicant *app) +{ + if (skb_tailroom(app->pdu) < sizeof(u8)) + return -1; + *(u8 *)__skb_put(app->pdu, sizeof(u8)) = GARP_END_MARK; + return 0; +} + +static void garp_pdu_queue(struct garp_applicant *app) +{ + if (!app->pdu) + return; + + garp_pdu_append_end_mark(app); + garp_pdu_append_end_mark(app); + + llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, + LLC_SAP_BSPAN, LLC_PDU_CMD); + llc_pdu_init_as_ui_cmd(app->pdu); + llc_mac_hdr_init(app->pdu, app->dev->dev_addr, + app->app->proto.group_address); + + skb_queue_tail(&app->queue, app->pdu); + app->pdu = NULL; +} + +static void garp_queue_xmit(struct garp_applicant *app) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&app->queue))) + dev_queue_xmit(skb); +} + +static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype) +{ + struct garp_msg_hdr *gm; + + if (skb_tailroom(app->pdu) < sizeof(*gm)) + return -1; + gm = (struct garp_msg_hdr *)__skb_put(app->pdu, sizeof(*gm)); + gm->attrtype = attrtype; + garp_cb(app->pdu)->cur_type = attrtype; + return 0; +} + +static int garp_pdu_append_attr(struct garp_applicant *app, + const struct garp_attr *attr, + enum garp_attr_event event) +{ + struct garp_attr_hdr *ga; + unsigned int len; + int err; +again: + if (!app->pdu) { + err = garp_pdu_init(app); + if (err < 0) + return err; + } + + if (garp_cb(app->pdu)->cur_type != attr->type) { + if (garp_cb(app->pdu)->cur_type && + garp_pdu_append_end_mark(app) < 0) + goto queue; + if (garp_pdu_append_msg(app, attr->type) < 0) + goto queue; + } + + len = sizeof(*ga) + attr->dlen; + if (skb_tailroom(app->pdu) < len) + goto queue; + ga = (struct garp_attr_hdr *)__skb_put(app->pdu, len); + ga->len = len; + ga->event = event; + memcpy(ga->data, attr->data, attr->dlen); + return 0; + +queue: + garp_pdu_queue(app); + goto again; +} + +static void garp_attr_event(struct garp_applicant *app, + struct garp_attr *attr, enum garp_event event) +{ + enum garp_applicant_state state; + + state = garp_applicant_state_table[attr->state][event].state; + if (state == GARP_APPLICANT_INVALID) + return; + + switch (garp_applicant_state_table[attr->state][event].action) { + case GARP_ACTION_NONE: + break; + case GARP_ACTION_S_JOIN_IN: + garp_pdu_append_attr(app, attr, GARP_JOIN_IN); + break; + case GARP_ACTION_S_LEAVE_EMPTY: + garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY); + /* As a pure applicant, sending a leave message implies that + * the attribute was unregistered and can be destroyed. */ + garp_attr_destroy(app, attr); + return; + default: + WARN_ON(1); + } + + attr->state = state; +} + +int garp_request_join(const struct net_device *dev, + const struct garp_application *appl, + const void *data, u8 len, u8 type) +{ + struct garp_port *port = dev->garp_port; + struct garp_applicant *app = port->applicants[appl->type]; + struct garp_attr *attr; + + spin_lock_bh(&app->lock); + attr = garp_attr_create(app, data, len, type); + if (!attr) { + spin_unlock_bh(&app->lock); + return -ENOMEM; + } + garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN); + spin_unlock_bh(&app->lock); + return 0; +} +EXPORT_SYMBOL_GPL(garp_request_join); + +void garp_request_leave(const struct net_device *dev, + const struct garp_application *appl, + const void *data, u8 len, u8 type) +{ + struct garp_port *port = dev->garp_port; + struct garp_applicant *app = port->applicants[appl->type]; + struct garp_attr *attr; + + spin_lock_bh(&app->lock); + attr = garp_attr_lookup(app, data, len, type); + if (!attr) { + spin_unlock_bh(&app->lock); + return; + } + garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE); + spin_unlock_bh(&app->lock); +} +EXPORT_SYMBOL_GPL(garp_request_leave); + +static void garp_gid_event(struct garp_applicant *app, enum garp_event event) +{ + struct rb_node *node, *next; + struct garp_attr *attr; + + for (node = rb_first(&app->gid); + next = node ? rb_next(node) : NULL, node != NULL; + node = next) { + attr = rb_entry(node, struct garp_attr, node); + garp_attr_event(app, attr, event); + } +} + +static void garp_join_timer_arm(struct garp_applicant *app) +{ + unsigned long delay; + + delay = (u64)msecs_to_jiffies(garp_join_time) * net_random() >> 32; + mod_timer(&app->join_timer, jiffies + delay); +} + +static void garp_join_timer(unsigned long data) +{ + struct garp_applicant *app = (struct garp_applicant *)data; + + spin_lock(&app->lock); + garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); + garp_pdu_queue(app); + spin_unlock(&app->lock); + + garp_queue_xmit(app); + garp_join_timer_arm(app); +} + +static int garp_pdu_parse_end_mark(struct sk_buff *skb) +{ + if (!pskb_may_pull(skb, sizeof(u8))) + return -1; + if (*skb->data == GARP_END_MARK) { + skb_pull(skb, sizeof(u8)); + return -1; + } + return 0; +} + +static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, + u8 attrtype) +{ + const struct garp_attr_hdr *ga; + struct garp_attr *attr; + enum garp_event event; + unsigned int dlen; + + if (!pskb_may_pull(skb, sizeof(*ga))) + return -1; + ga = (struct garp_attr_hdr *)skb->data; + if (ga->len < sizeof(*ga)) + return -1; + + if (!pskb_may_pull(skb, ga->len)) + return -1; + skb_pull(skb, ga->len); + dlen = sizeof(*ga) - ga->len; + + if (attrtype > app->app->maxattr) + return 0; + + switch (ga->event) { + case GARP_LEAVE_ALL: + if (dlen != 0) + return -1; + garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY); + return 0; + case GARP_JOIN_EMPTY: + event = GARP_EVENT_R_JOIN_EMPTY; + break; + case GARP_JOIN_IN: + event = GARP_EVENT_R_JOIN_IN; + break; + case GARP_LEAVE_EMPTY: + event = GARP_EVENT_R_LEAVE_EMPTY; + break; + case GARP_EMPTY: + event = GARP_EVENT_R_EMPTY; + break; + default: + return 0; + } + + if (dlen == 0) + return -1; + attr = garp_attr_lookup(app, ga->data, dlen, attrtype); + if (attr == NULL) + return 0; + garp_attr_event(app, attr, event); + return 0; +} + +static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) +{ + const struct garp_msg_hdr *gm; + + if (!pskb_may_pull(skb, sizeof(*gm))) + return -1; + gm = (struct garp_msg_hdr *)skb->data; + if (gm->attrtype == 0) + return -1; + skb_pull(skb, sizeof(*gm)); + + while (skb->len > 0) { + if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0) + return -1; + if (garp_pdu_parse_end_mark(skb) < 0) + break; + } + return 0; +} + +static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb, + struct net_device *dev) +{ + struct garp_application *appl = proto->data; + struct garp_port *port; + struct garp_applicant *app; + const struct garp_pdu_hdr *gp; + + port = rcu_dereference(dev->garp_port); + if (!port) + goto err; + app = rcu_dereference(port->applicants[appl->type]); + if (!app) + goto err; + + if (!pskb_may_pull(skb, sizeof(*gp))) + goto err; + gp = (struct garp_pdu_hdr *)skb->data; + if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID)) + goto err; + skb_pull(skb, sizeof(*gp)); + + spin_lock(&app->lock); + while (skb->len > 0) { + if (garp_pdu_parse_msg(app, skb) < 0) + break; + if (garp_pdu_parse_end_mark(skb) < 0) + break; + } + spin_unlock(&app->lock); +err: + kfree_skb(skb); +} + +static int garp_init_port(struct net_device *dev) +{ + struct garp_port *port; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + rcu_assign_pointer(dev->garp_port, port); + return 0; +} + +static void garp_release_port(struct net_device *dev) +{ + struct garp_port *port = dev->garp_port; + unsigned int i; + + for (i = 0; i <= GARP_APPLICATION_MAX; i++) { + if (port->applicants[i]) + return; + } + rcu_assign_pointer(dev->garp_port, NULL); + synchronize_rcu(); + kfree(port); +} + +int garp_init_applicant(struct net_device *dev, struct garp_application *appl) +{ + struct garp_applicant *app; + int err; + + ASSERT_RTNL(); + + if (!dev->garp_port) { + err = garp_init_port(dev); + if (err < 0) + goto err1; + } + + err = -ENOMEM; + app = kzalloc(sizeof(*app), GFP_KERNEL); + if (!app) + goto err2; + + err = dev_mc_add(dev, appl->proto.group_address, ETH_ALEN, 0); + if (err < 0) + goto err3; + + app->dev = dev; + app->app = appl; + app->gid = RB_ROOT; + spin_lock_init(&app->lock); + skb_queue_head_init(&app->queue); + rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); + setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app); + garp_join_timer_arm(app); + return 0; + +err3: + kfree(app); +err2: + garp_release_port(dev); +err1: + return err; +} +EXPORT_SYMBOL_GPL(garp_init_applicant); + +void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) +{ + struct garp_port *port = dev->garp_port; + struct garp_applicant *app = port->applicants[appl->type]; + + ASSERT_RTNL(); + + rcu_assign_pointer(port->applicants[appl->type], NULL); + synchronize_rcu(); + + /* Delete timer and generate a final TRANSMIT_PDU event to flush out + * all pending messages before the applicant is gone. */ + del_timer_sync(&app->join_timer); + garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); + garp_pdu_queue(app); + garp_queue_xmit(app); + + dev_mc_delete(dev, appl->proto.group_address, ETH_ALEN, 0); + kfree(app); + garp_release_port(dev); +} +EXPORT_SYMBOL_GPL(garp_uninit_applicant); + +int garp_register_application(struct garp_application *appl) +{ + appl->proto.rcv = garp_pdu_rcv; + appl->proto.data = appl; + return stp_proto_register(&appl->proto); +} +EXPORT_SYMBOL_GPL(garp_register_application); + +void garp_unregister_application(struct garp_application *appl) +{ + stp_proto_unregister(&appl->proto); +} +EXPORT_SYMBOL_GPL(garp_unregister_application); -- cgit v1.2.3 From 70c03b49b80ba3634958acc31853771019c0ebd3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 5 Jul 2008 21:26:57 -0700 Subject: vlan: Add GVRP support Add GVRP support for dynamically registering VLANs with switches. By default GVRP is disabled because we only support the applicant-only participant model, which means it should not be enabled on vlans that are members of a bridge. Since there is currently no way to cleanly determine that, the user is responsible for enabling it. The code is pretty small and low impact, its wrapped in a config option though because it depends on the GARP implementation and the STP core. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 1 + include/net/garp.h | 1 + net/8021q/Kconfig | 10 ++++++++ net/8021q/Makefile | 9 +++---- net/8021q/vlan.c | 23 ++++++++++++++--- net/8021q/vlan.h | 16 ++++++++++++ net/8021q/vlan_dev.c | 18 +++++++++++-- net/8021q/vlan_gvrp.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ net/8021q/vlan_netlink.c | 3 ++- 9 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 net/8021q/vlan_gvrp.c (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 15ace02b7b24..5190452ac7dc 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -402,6 +402,7 @@ enum vlan_ioctl_cmds { enum vlan_flags { VLAN_FLAG_REORDER_HDR = 0x1, + VLAN_FLAG_GVRP = 0x2, }; enum vlan_name_types { diff --git a/include/net/garp.h b/include/net/garp.h index 73c772395f5b..825f172caba9 100644 --- a/include/net/garp.h +++ b/include/net/garp.h @@ -84,6 +84,7 @@ struct garp_attr { }; enum garp_applications { + GARP_APPLICATION_GVRP, __GARP_APPLICATION_MAX }; #define GARP_APPLICATION_MAX (__GARP_APPLICATION_MAX - 1) diff --git a/net/8021q/Kconfig b/net/8021q/Kconfig index c4a382e450e2..fa073a54963e 100644 --- a/net/8021q/Kconfig +++ b/net/8021q/Kconfig @@ -17,3 +17,13 @@ config VLAN_8021Q will be called 8021q. If unsure, say N. + +config VLAN_8021Q_GVRP + bool "GVRP (GARP VLAN Registration Protocol) support" + depends on VLAN_8021Q + select GARP + help + Select this to enable GVRP end-system support. GVRP is used for + automatic propagation of registered VLANs to switches. + + If unsure, say N. diff --git a/net/8021q/Makefile b/net/8021q/Makefile index 10ca7f486c3a..3006e9ed7b08 100644 --- a/net/8021q/Makefile +++ b/net/8021q/Makefile @@ -4,9 +4,6 @@ obj-$(CONFIG_VLAN_8021Q) += 8021q.o -8021q-objs := vlan.o vlan_dev.o vlan_netlink.o - -ifeq ($(CONFIG_PROC_FS),y) -8021q-objs += vlanproc.o -endif - +8021q-y := vlan.o vlan_dev.o vlan_netlink.o +8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o +8021q-$(CONFIG_PROC_FS) += vlanproc.o diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 8cae2daeb1cc..b529110c9355 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -169,6 +169,8 @@ void unregister_vlan_dev(struct net_device *dev) /* If the group is now empty, kill off the group. */ if (grp->nr_vlans == 0) { + vlan_gvrp_uninit_applicant(real_dev); + if (real_dev->features & NETIF_F_HW_VLAN_RX) real_dev->vlan_rx_register(real_dev, NULL); @@ -249,15 +251,18 @@ int register_vlan_dev(struct net_device *dev) ngrp = grp = vlan_group_alloc(real_dev); if (!grp) return -ENOBUFS; + err = vlan_gvrp_init_applicant(real_dev); + if (err < 0) + goto out_free_group; } err = vlan_group_prealloc_vid(grp, vlan_id); if (err < 0) - goto out_free_group; + goto out_uninit_applicant; err = register_netdevice(dev); if (err < 0) - goto out_free_group; + goto out_uninit_applicant; /* Account for reference in struct vlan_dev_info */ dev_hold(real_dev); @@ -278,6 +283,9 @@ int register_vlan_dev(struct net_device *dev) return 0; +out_uninit_applicant: + if (ngrp) + vlan_gvrp_uninit_applicant(real_dev); out_free_group: if (ngrp) vlan_group_free(ngrp); @@ -713,14 +721,20 @@ static int __init vlan_proto_init(void) if (err < 0) goto err2; - err = vlan_netlink_init(); + err = vlan_gvrp_init(); if (err < 0) goto err3; + err = vlan_netlink_init(); + if (err < 0) + goto err4; + dev_add_pack(&vlan_packet_type); vlan_ioctl_set(vlan_ioctl_handler); return 0; +err4: + vlan_gvrp_uninit(); err3: unregister_netdevice_notifier(&vlan_notifier_block); err2: @@ -745,8 +759,9 @@ static void __exit vlan_cleanup_module(void) BUG_ON(!hlist_empty(&vlan_group_hash[i])); unregister_pernet_gen_device(vlan_net_id, &vlan_net_ops); - synchronize_net(); + + vlan_gvrp_uninit(); } module_init(vlan_proto_init); diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 639e2544a804..097b2e04c928 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -37,6 +37,22 @@ void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev); +#ifdef CONFIG_VLAN_8021Q_GVRP +extern int vlan_gvrp_request_join(const struct net_device *dev); +extern void vlan_gvrp_request_leave(const struct net_device *dev); +extern int vlan_gvrp_init_applicant(struct net_device *dev); +extern void vlan_gvrp_uninit_applicant(struct net_device *dev); +extern int vlan_gvrp_init(void); +extern void vlan_gvrp_uninit(void); +#else +static inline int vlan_gvrp_request_join(const struct net_device *dev) { return 0; } +static inline void vlan_gvrp_request_leave(const struct net_device *dev) {} +static inline int vlan_gvrp_init_applicant(struct net_device *dev) { return 0; } +static inline void vlan_gvrp_uninit_applicant(struct net_device *dev) {} +static inline int vlan_gvrp_init(void) { return 0; } +static inline void vlan_gvrp_uninit(void) {} +#endif + int vlan_netlink_init(void); void vlan_netlink_fini(void); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 76c665cdab66..a0617bf7cec6 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -512,10 +512,17 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) struct vlan_dev_info *vlan = vlan_dev_info(dev); u32 old_flags = vlan->flags; - if (mask & ~VLAN_FLAG_REORDER_HDR) + if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP)) return -EINVAL; vlan->flags = (old_flags & ~mask) | (flags & mask); + + if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) { + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_join(dev); + else + vlan_gvrp_request_leave(dev); + } return 0; } @@ -550,12 +557,19 @@ static int vlan_dev_open(struct net_device *dev) if (dev->flags & IFF_PROMISC) dev_set_promiscuity(real_dev, 1); + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_join(dev); + return 0; } static int vlan_dev_stop(struct net_device *dev) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct net_device *real_dev = vlan->real_dev; + + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_leave(dev); dev_mc_unsync(real_dev, dev); dev_unicast_unsync(real_dev, dev); diff --git a/net/8021q/vlan_gvrp.c b/net/8021q/vlan_gvrp.c new file mode 100644 index 000000000000..db9781608362 --- /dev/null +++ b/net/8021q/vlan_gvrp.c @@ -0,0 +1,66 @@ +/* + * IEEE 802.1Q GARP VLAN Registration Protocol (GVRP) + * + * Copyright (c) 2008 Patrick McHardy + * + * 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 +#include +#include +#include "vlan.h" + +#define GARP_GVRP_ADDRESS { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x21 } + +enum gvrp_attributes { + GVRP_ATTR_INVALID, + GVRP_ATTR_VID, + __GVRP_ATTR_MAX +}; +#define GVRP_ATTR_MAX (__GVRP_ATTR_MAX - 1) + +static struct garp_application vlan_gvrp_app __read_mostly = { + .proto.group_address = GARP_GVRP_ADDRESS, + .maxattr = GVRP_ATTR_MAX, + .type = GARP_APPLICATION_GVRP, +}; + +int vlan_gvrp_request_join(const struct net_device *dev) +{ + const struct vlan_dev_info *vlan = vlan_dev_info(dev); + __be16 vid = htons(vlan->vlan_id); + + return garp_request_join(vlan->real_dev, &vlan_gvrp_app, + &vid, sizeof(vid), GVRP_ATTR_VID); +} + +void vlan_gvrp_request_leave(const struct net_device *dev) +{ + const struct vlan_dev_info *vlan = vlan_dev_info(dev); + __be16 vid = htons(vlan->vlan_id); + + garp_request_leave(vlan->real_dev, &vlan_gvrp_app, + &vid, sizeof(vid), GVRP_ATTR_VID); +} + +int vlan_gvrp_init_applicant(struct net_device *dev) +{ + return garp_init_applicant(dev, &vlan_gvrp_app); +} + +void vlan_gvrp_uninit_applicant(struct net_device *dev) +{ + garp_uninit_applicant(dev, &vlan_gvrp_app); +} + +int __init vlan_gvrp_init(void) +{ + return garp_register_application(&vlan_gvrp_app); +} + +void vlan_gvrp_uninit(void) +{ + garp_unregister_application(&vlan_gvrp_app); +} diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index fd7cb195d53f..e9c91dcecc9b 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -59,7 +59,8 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) } if (data[IFLA_VLAN_FLAGS]) { flags = nla_data(data[IFLA_VLAN_FLAGS]); - if ((flags->flags & flags->mask) & ~VLAN_FLAG_REORDER_HDR) + if ((flags->flags & flags->mask) & + ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP)) return -EINVAL; } -- cgit v1.2.3 From 9982fbface82893e77d211fbabfbd229da6bdde6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 6 Jul 2008 14:24:08 +0200 Subject: Revert "cpumask: introduce new APIs" This reverts commit acb7669c125676e63cf96582455509216c39745e. the wrappers are not needed anymore. Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 47418b1b4103..80226e776143 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -394,10 +394,6 @@ int __next_cpu_nr(int n, const cpumask_t *srcp); #endif /* NR_CPUS > 64 */ -#define next_cpu_nr(n, src) next_cpu(n, src) -#define cpus_weight_nr(cpumask) cpus_weight(cpumask) -#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) - /* * The following particular system cpumasks and operations manage * possible, present and online cpus. Each of them is a fixed size -- cgit v1.2.3 From d6315949ac5527efd00d48283a9e33361c86e8e9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 22 Jun 2008 12:01:58 +0100 Subject: [ARM] 5096/2: Support Toshiba TC6393XB Mobile I/O Controller. Add support for Toshiba TC6393XB companion chip. Currently only GPIO and part of IRQ features of the device are supported. Signed-off-by: Dmitry Baryshkov Signed-off-by: Russell King --- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 2 + drivers/mfd/tc6393xb.c | 537 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tc6393xb.h | 47 ++++ 4 files changed, 592 insertions(+) create mode 100644 drivers/mfd/tc6393xb.c create mode 100644 include/linux/mfd/tc6393xb.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2566479937c9..1a1ac262fc87 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -38,6 +38,12 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. +config MFD_TC6393XB + bool "Support Toshiba TC6393XB" + depends on HAVE_GPIO_LIB + help + Support for Toshiba Mobile IO Controller TC6393XB + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index eef4e26807df..b4168442d53c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o +obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c new file mode 100644 index 000000000000..4d7192edefe1 --- /dev/null +++ b/drivers/mfd/tc6393xb.c @@ -0,0 +1,537 @@ +/* + * Toshiba TC6393XB SoC support + * + * Copyright(c) 2005-2006 Chris Humbert + * Copyright(c) 2005 Dirk Opfer + * Copyright(c) 2005 Ian Molton + * Copyright(c) 2007 Dmitry Baryshkov + * + * Based on code written by Sharp/Lineo for 2.4 kernels + * Based on locomo.c + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCR_REVID 0x08 /* b Revision ID */ +#define SCR_ISR 0x50 /* b Interrupt Status */ +#define SCR_IMR 0x52 /* b Interrupt Mask */ +#define SCR_IRR 0x54 /* b Interrupt Routing */ +#define SCR_GPER 0x60 /* w GP Enable */ +#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ +#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ +#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ +#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ +#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ +#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ +#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ +#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ +#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ +#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ +#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ +#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ +#define SCR_CCR 0x98 /* w Clock Control */ +#define SCR_PLL2CR 0x9a /* w PLL2 Control */ +#define SCR_PLL1CR 0x9c /* l PLL1 Control */ +#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ +#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ +#define SCR_FER 0xe0 /* b Function Enable */ +#define SCR_MCR 0xe4 /* w Mode Control */ +#define SCR_CONFIG 0xfc /* b Configuration Control */ +#define SCR_DEBUG 0xff /* b Debug */ + +#define SCR_CCR_CK32K BIT(0) +#define SCR_CCR_USBCK BIT(1) +#define SCR_CCR_UNK1 BIT(4) +#define SCR_CCR_MCLK_MASK (7 << 8) +#define SCR_CCR_MCLK_OFF (0 << 8) +#define SCR_CCR_MCLK_12 (1 << 8) +#define SCR_CCR_MCLK_24 (2 << 8) +#define SCR_CCR_MCLK_48 (3 << 8) +#define SCR_CCR_HCLK_MASK (3 << 12) +#define SCR_CCR_HCLK_24 (0 << 12) +#define SCR_CCR_HCLK_48 (1 << 12) + +#define SCR_FER_USBEN BIT(0) /* USB host enable */ +#define SCR_FER_LCDCVEN BIT(1) /* polysilicon TFT enable */ +#define SCR_FER_SLCDEN BIT(2) /* SLCD enable */ + +#define SCR_MCR_RDY_MASK (3 << 0) +#define SCR_MCR_RDY_OPENDRAIN (0 << 0) +#define SCR_MCR_RDY_TRISTATE (1 << 0) +#define SCR_MCR_RDY_PUSHPULL (2 << 0) +#define SCR_MCR_RDY_UNK BIT(2) +#define SCR_MCR_RDY_EN BIT(3) +#define SCR_MCR_INT_MASK (3 << 4) +#define SCR_MCR_INT_OPENDRAIN (0 << 4) +#define SCR_MCR_INT_TRISTATE (1 << 4) +#define SCR_MCR_INT_PUSHPULL (2 << 4) +#define SCR_MCR_INT_UNK BIT(6) +#define SCR_MCR_INT_EN BIT(7) +/* bits 8 - 16 are unknown */ + +#define TC_GPIO_BIT(i) (1 << (i & 0x7)) + +/*--------------------------------------------------------------------------*/ + +struct tc6393xb { + void __iomem *scr; + + struct gpio_chip gpio; + + struct clk *clk; /* 3,6 Mhz */ + + spinlock_t lock; /* protects RMW cycles */ + + struct { + u8 fer; + u16 ccr; + u8 gpi_bcr[3]; + u8 gpo_dsr[3]; + u8 gpo_doecr[3]; + } suspend_state; + + struct resource rscr; + struct resource *iomem; + int irq; + int irq_base; +}; + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + + /* XXX: does dsr also represent inputs? */ + return ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)) + & TC_GPIO_BIT(offset); +} + +static void __tc6393xb_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + u8 dsr; + + dsr = ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)); + if (value) + dsr |= TC_GPIO_BIT(offset); + else + dsr &= ~TC_GPIO_BIT(offset); + + iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8)); +} + +static void tc6393xb_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + __tc6393xb_gpio_set(chip, offset, value); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + u8 doecr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr &= ~TC_GPIO_BIT(offset); + iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + u8 doecr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + __tc6393xb_gpio_set(chip, offset, value); + + doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr |= TC_GPIO_BIT(offset); + iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base) +{ + tc6393xb->gpio.label = "tc6393xb"; + tc6393xb->gpio.base = gpio_base; + tc6393xb->gpio.ngpio = 16; + tc6393xb->gpio.set = tc6393xb_gpio_set; + tc6393xb->gpio.get = tc6393xb_gpio_get; + tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input; + tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output; + + return gpiochip_add(&tc6393xb->gpio); +} + +/*--------------------------------------------------------------------------*/ + +static void +tc6393xb_irq(unsigned int irq, struct irq_desc *desc) +{ + struct tc6393xb *tc6393xb = get_irq_data(irq); + unsigned int isr; + unsigned int i, irq_base; + + irq_base = tc6393xb->irq_base; + + while ((isr = ioread8(tc6393xb->scr + SCR_ISR) & + ~ioread8(tc6393xb->scr + SCR_IMR))) + for (i = 0; i < TC6393XB_NR_IRQS; i++) { + if (isr & (1 << i)) + generic_handle_irq(irq_base + i); + } +} + +static void tc6393xb_irq_ack(unsigned int irq) +{ +} + +static void tc6393xb_irq_mask(unsigned int irq) +{ + struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + imr = ioread8(tc6393xb->scr + SCR_IMR); + imr |= 1 << (irq - tc6393xb->irq_base); + iowrite8(imr, tc6393xb->scr + SCR_IMR); + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static void tc6393xb_irq_unmask(unsigned int irq) +{ + struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + imr = ioread8(tc6393xb->scr + SCR_IMR); + imr &= ~(1 << (irq - tc6393xb->irq_base)); + iowrite8(imr, tc6393xb->scr + SCR_IMR); + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static struct irq_chip tc6393xb_chip = { + .name = "tc6393xb", + .ack = tc6393xb_irq_ack, + .mask = tc6393xb_irq_mask, + .unmask = tc6393xb_irq_unmask, +}; + +static void tc6393xb_attach_irq(struct platform_device *dev) +{ + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = tc6393xb->irq_base; + + for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { + set_irq_chip(irq, &tc6393xb_chip); + set_irq_chip_data(irq, tc6393xb); + set_irq_handler(irq, handle_edge_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + set_irq_type(tc6393xb->irq, IRQT_FALLING); + set_irq_data(tc6393xb->irq, tc6393xb); + set_irq_chained_handler(tc6393xb->irq, tc6393xb_irq); +} + +static void tc6393xb_detach_irq(struct platform_device *dev) +{ + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + set_irq_chained_handler(tc6393xb->irq, NULL); + set_irq_data(tc6393xb->irq, NULL); + + irq_base = tc6393xb->irq_base; + + for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { + set_irq_flags(irq, 0); + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } +} + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_hw_init(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int i; + + iowrite8(tc6393xb->suspend_state.fer, tc6393xb->scr + SCR_FER); + iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR); + iowrite16(tc6393xb->suspend_state.ccr, tc6393xb->scr + SCR_CCR); + iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN | + SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN | + BIT(15), tc6393xb->scr + SCR_MCR); + iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER); + iowrite8(0, tc6393xb->scr + SCR_IRR); + iowrite8(0xbf, tc6393xb->scr + SCR_IMR); + + for (i = 0; i < 3; i++) { + iowrite8(tc6393xb->suspend_state.gpo_dsr[i], + tc6393xb->scr + SCR_GPO_DSR(i)); + iowrite8(tc6393xb->suspend_state.gpo_doecr[i], + tc6393xb->scr + SCR_GPO_DOECR(i)); + iowrite8(tc6393xb->suspend_state.gpi_bcr[i], + tc6393xb->scr + SCR_GPI_BCR(i)); + } + + return 0; +} + +static int __devinit tc6393xb_probe(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb; + struct resource *iomem; + struct resource *rscr; + int retval, temp; + int i; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) + return -EINVAL; + + tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL); + if (!tc6393xb) { + retval = -ENOMEM; + goto err_kzalloc; + } + + spin_lock_init(&tc6393xb->lock); + + platform_set_drvdata(dev, tc6393xb); + tc6393xb->iomem = iomem; + tc6393xb->irq = platform_get_irq(dev, 0); + tc6393xb->irq_base = tcpd->irq_base; + + tc6393xb->clk = clk_get(&dev->dev, "GPIO27_CLK" /* "CK3P6MI" */); + if (IS_ERR(tc6393xb->clk)) { + retval = PTR_ERR(tc6393xb->clk); + goto err_clk_get; + } + + rscr = &tc6393xb->rscr; + rscr->name = "tc6393xb-core"; + rscr->start = iomem->start; + rscr->end = iomem->start + 0xff; + rscr->flags = IORESOURCE_MEM; + + retval = request_resource(iomem, rscr); + if (retval) + goto err_request_scr; + + tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); + if (!tc6393xb->scr) { + retval = -ENOMEM; + goto err_ioremap; + } + + retval = clk_enable(tc6393xb->clk); + if (retval) + goto err_clk_enable; + + retval = tcpd->enable(dev); + if (retval) + goto err_enable; + + tc6393xb->suspend_state.fer = 0; + for (i = 0; i < 3; i++) { + tc6393xb->suspend_state.gpo_dsr[i] = + (tcpd->scr_gpo_dsr >> (8 * i)) & 0xff; + tc6393xb->suspend_state.gpo_doecr[i] = + (tcpd->scr_gpo_doecr >> (8 * i)) & 0xff; + } + /* + * It may be necessary to change this back to + * platform-dependant code + */ + tc6393xb->suspend_state.ccr = SCR_CCR_UNK1 | + SCR_CCR_HCLK_48; + + retval = tc6393xb_hw_init(dev); + if (retval) + goto err_hw_init; + + printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", + ioread8(tc6393xb->scr + SCR_REVID), + (unsigned long) iomem->start, tc6393xb->irq); + + tc6393xb->gpio.base = -1; + + if (tcpd->gpio_base >= 0) { + retval = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base); + if (retval) + goto err_gpio_add; + } + + if (tc6393xb->irq) + tc6393xb_attach_irq(dev); + + return 0; + + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + +err_gpio_add: + if (tc6393xb->gpio.base != -1) + temp = gpiochip_remove(&tc6393xb->gpio); +err_hw_init: + tcpd->disable(dev); +err_clk_enable: + clk_disable(tc6393xb->clk); +err_enable: + iounmap(tc6393xb->scr); +err_ioremap: + release_resource(&tc6393xb->rscr); +err_request_scr: + clk_put(tc6393xb->clk); +err_clk_get: + kfree(tc6393xb); +err_kzalloc: + return retval; +} + +static int __devexit tc6393xb_remove(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int ret; + + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + + if (tc6393xb->gpio.base != -1) { + ret = gpiochip_remove(&tc6393xb->gpio); + if (ret) { + dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret); + return ret; + } + } + + ret = tcpd->disable(dev); + + clk_disable(tc6393xb->clk); + + iounmap(tc6393xb->scr); + + release_resource(&tc6393xb->rscr); + + platform_set_drvdata(dev, NULL); + + clk_put(tc6393xb->clk); + + kfree(tc6393xb); + + return ret; +} + +#ifdef CONFIG_PM +static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int i; + + + tc6393xb->suspend_state.ccr = ioread16(tc6393xb->scr + SCR_CCR); + tc6393xb->suspend_state.fer = ioread8(tc6393xb->scr + SCR_FER); + + for (i = 0; i < 3; i++) { + tc6393xb->suspend_state.gpo_dsr[i] = + ioread8(tc6393xb->scr + SCR_GPO_DSR(i)); + tc6393xb->suspend_state.gpo_doecr[i] = + ioread8(tc6393xb->scr + SCR_GPO_DOECR(i)); + tc6393xb->suspend_state.gpi_bcr[i] = + ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); + } + + return tcpd->suspend(dev); +} + +static int tc6393xb_resume(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + int ret = tcpd->resume(dev); + + if (ret) + return ret; + + return tc6393xb_hw_init(dev); +} +#else +#define tc6393xb_suspend NULL +#define tc6393xb_resume NULL +#endif + +static struct platform_driver tc6393xb_driver = { + .probe = tc6393xb_probe, + .remove = __devexit_p(tc6393xb_remove), + .suspend = tc6393xb_suspend, + .resume = tc6393xb_resume, + + .driver = { + .name = "tc6393xb", + .owner = THIS_MODULE, + }, +}; + +static int __init tc6393xb_init(void) +{ + return platform_driver_register(&tc6393xb_driver); +} + +static void __exit tc6393xb_exit(void) +{ + platform_driver_unregister(&tc6393xb_driver); +} + +subsys_initcall(tc6393xb_init); +module_exit(tc6393xb_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); +MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); +MODULE_ALIAS("platform:tc6393xb"); diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h new file mode 100644 index 000000000000..0e3dd4ca523b --- /dev/null +++ b/include/linux/mfd/tc6393xb.h @@ -0,0 +1,47 @@ +/* + * Toshiba TC6393XB SoC support + * + * Copyright(c) 2005-2006 Chris Humbert + * Copyright(c) 2005 Dirk Opfer + * Copyright(c) 2005 Ian Molton + * Copyright(c) 2007 Dmitry Baryshkov + * + * Based on code written by Sharp/Lineo for 2.4 kernels + * Based on locomo.c + * + * 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 TC6393XB_H +#define TC6393XB_H + +/* Also one should provide the CK3P6MI clock */ +struct tc6393xb_platform_data { + u16 scr_pll2cr; /* PLL2 Control */ + u16 scr_gper; /* GP Enable */ + u32 scr_gpo_doecr; /* GPO Data OE Control */ + u32 scr_gpo_dsr; /* GPO Data Set */ + + int (*enable)(struct platform_device *dev); + int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); + + int irq_base; /* a base for cascaded irq */ + int gpio_base; +}; + +/* + * Relative to irq_base + */ +#define IRQ_TC6393_NAND 0 +#define IRQ_TC6393_MMC 1 +#define IRQ_TC6393_OHCI 2 +#define IRQ_TC6393_SERIAL 3 +#define IRQ_TC6393_FB 4 + +#define TC6393XB_NR_IRQS 8 + +#endif -- cgit v1.2.3 From aa613de676986f136fa6f48a4d709b5d264f4f38 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Jun 2008 10:37:19 +0100 Subject: [ARM] 5127/1: Core MFD support This patch provides a common subdevice registration system for MFD type chips, using platfrom device. Signed-off-by: Ian Molton Signed-off-by: Dmitry Baryshkov Signed-off-by: Russell King --- drivers/mfd/Kconfig | 4 ++ drivers/mfd/Makefile | 2 + drivers/mfd/mfd-core.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/core.h | 55 +++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 drivers/mfd/mfd-core.c create mode 100644 include/linux/mfd/core.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1a1ac262fc87..8ebc0be10953 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -5,6 +5,10 @@ menu "Multifunction device drivers" depends on HAS_IOMEM +config MFD_CORE + tristate + default n + config MFD_SM501 tristate "Support for Silicon Motion SM501" ---help--- diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b4168442d53c..33daa2f45dd8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o +obj-$(CONFIG_MFD_CORE) += mfd-core.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c new file mode 100644 index 000000000000..d7d88ce053a6 --- /dev/null +++ b/drivers/mfd/mfd-core.c @@ -0,0 +1,114 @@ +/* + * drivers/mfd/mfd-core.c + * + * core MFD support + * Copyright (c) 2006 Ian Molton + * Copyright (c) 2007,2008 Dmitry Baryshkov + * + * 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 +#include +#include + +static int mfd_add_device(struct platform_device *parent, + const struct mfd_cell *cell, + struct resource *mem_base, + int irq_base) +{ + struct resource res[cell->num_resources]; + struct platform_device *pdev; + int ret = -ENOMEM; + int r; + + pdev = platform_device_alloc(cell->name, parent->id); + if (!pdev) + goto fail_alloc; + + pdev->dev.parent = &parent->dev; + + ret = platform_device_add_data(pdev, + cell, sizeof(struct mfd_cell)); + if (ret) + goto fail_device; + + memzero(res, sizeof(res)); + for (r = 0; r < cell->num_resources; r++) { + res[r].name = cell->resources[r].name; + res[r].flags = cell->resources[r].flags; + + /* Find out base to use */ + if (cell->resources[r].flags & IORESOURCE_MEM) { + res[r].parent = mem_base; + res[r].start = mem_base->start + + cell->resources[r].start; + res[r].end = mem_base->start + + cell->resources[r].end; + } else if (cell->resources[r].flags & IORESOURCE_IRQ) { + res[r].start = irq_base + + cell->resources[r].start; + res[r].end = irq_base + + cell->resources[r].end; + } else { + res[r].parent = cell->resources[r].parent; + res[r].start = cell->resources[r].start; + res[r].end = cell->resources[r].end; + } + } + + platform_device_add_resources(pdev, res, cell->num_resources); + + ret = platform_device_add(pdev); + if (ret) + goto fail_device; + + return 0; + +/* platform_device_del(pdev); */ +fail_device: + platform_device_put(pdev); +fail_alloc: + return ret; +} + +int mfd_add_devices( + struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base) +{ + int i; + int ret = 0; + + for (i = 0; i < n_devs; i++) { + ret = mfd_add_device(parent, cells + i, mem_base, irq_base); + if (ret) + break; + } + + if (ret) + mfd_remove_devices(parent); + + return ret; +} +EXPORT_SYMBOL(mfd_add_devices); + +static int mfd_remove_devices_fn(struct device *dev, void *unused) +{ + platform_device_unregister( + container_of(dev, struct platform_device, dev)); + return 0; +} + +void mfd_remove_devices(struct platform_device *parent) +{ + device_for_each_child(&parent->dev, NULL, mfd_remove_devices_fn); +} +EXPORT_SYMBOL(mfd_remove_devices); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h new file mode 100644 index 000000000000..bb3dd0545928 --- /dev/null +++ b/include/linux/mfd/core.h @@ -0,0 +1,55 @@ +#ifndef MFD_CORE_H +#define MFD_CORE_H +/* + * drivers/mfd/mfd-core.h + * + * core MFD support + * Copyright (c) 2006 Ian Molton + * Copyright (c) 2007 Dmitry Baryshkov + * + * 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 + +/* + * This struct describes the MFD part ("cell"). + * After registration the copy of this structure will become the platform data + * of the resulting platform_device + */ +struct mfd_cell { + const char *name; + + int (*enable)(struct platform_device *dev); + int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); + + void *driver_data; /* driver-specific data */ + + /* + * This resources can be specified relatievly to the parent device. + * For accessing device you should use resources from device + */ + int num_resources; + const struct resource *resources; +}; + +static inline struct mfd_cell * +mfd_get_cell(struct platform_device *pdev) +{ + return (struct mfd_cell *)pdev->dev.platform_data; +} + +extern int mfd_add_devices( + struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base); + +extern void mfd_remove_devices(struct platform_device *parent); + +#endif -- cgit v1.2.3 From f024ff10b1ab13da4d626366019fd05c49721af7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Jun 2008 10:37:57 +0100 Subject: [ARM] 5128/1: tc6393xb: tmio-nand support Signed-off-by: Dmitry Baryshkov Signed-off-by: Russell King --- drivers/mfd/Kconfig | 1 + drivers/mfd/tc6393xb.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tc6393xb.h | 2 ++ include/linux/mfd/tmio.h | 17 ++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 include/linux/mfd/tmio.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8ebc0be10953..7dff105e8f83 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -45,6 +45,7 @@ config HTC_PASIC3 config MFD_TC6393XB bool "Support Toshiba TC6393XB" depends on HAVE_GPIO_LIB + select MFD_CORE help Support for Toshiba Mobile IO Controller TC6393XB diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 4d7192edefe1..2d87501b6fd4 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -108,6 +110,59 @@ struct tc6393xb { int irq_base; }; +enum { + TC6393XB_CELL_NAND, +}; + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_nand_enable(struct platform_device *nand) +{ + struct platform_device *dev = to_platform_device(nand->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + /* SMD buffer on */ + dev_dbg(&dev->dev, "SMD buffer on\n"); + iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static struct resource __devinitdata tc6393xb_nand_resources[] = { + { + .name = TMIO_NAND_CONFIG, + .start = 0x0100, + .end = 0x01ff, + .flags = IORESOURCE_MEM, + }, + { + .name = TMIO_NAND_CONTROL, + .start = 0x1000, + .end = 0x1007, + .flags = IORESOURCE_MEM, + }, + { + .name = TMIO_NAND_IRQ, + .start = IRQ_TC6393_NAND, + .end = IRQ_TC6393_NAND, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell __devinitdata tc6393xb_cells[] = { + [TC6393XB_CELL_NAND] = { + .name = "tmio-nand", + .enable = tc6393xb_nand_enable, + .num_resources = ARRAY_SIZE(tc6393xb_nand_resources), + .resources = tc6393xb_nand_resources, + }, +}; + /*--------------------------------------------------------------------------*/ static int tc6393xb_gpio_get(struct gpio_chip *chip, @@ -410,6 +465,12 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) if (tc6393xb->irq) tc6393xb_attach_irq(dev); + tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; + + retval = mfd_add_devices(dev, + tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), + iomem, tcpd->irq_base); + return 0; if (tc6393xb->irq) @@ -440,6 +501,8 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) struct tc6393xb *tc6393xb = platform_get_drvdata(dev); int ret; + mfd_remove_devices(dev); + if (tc6393xb->irq) tc6393xb_detach_irq(dev); diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h index 0e3dd4ca523b..7cc824a58f7c 100644 --- a/include/linux/mfd/tc6393xb.h +++ b/include/linux/mfd/tc6393xb.h @@ -31,6 +31,8 @@ struct tc6393xb_platform_data { int irq_base; /* a base for cascaded irq */ int gpio_base; + + struct tmio_nand_data *nand_data; }; /* diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h new file mode 100644 index 000000000000..9438d8c9ac1c --- /dev/null +++ b/include/linux/mfd/tmio.h @@ -0,0 +1,17 @@ +#ifndef MFD_TMIO_H +#define MFD_TMIO_H + +/* + * data for the NAND controller + */ +struct tmio_nand_data { + struct nand_bbt_descr *badblock_pattern; + struct mtd_partition *partition; + unsigned int num_partitions; +}; + +#define TMIO_NAND_CONFIG "tmio-nand-config" +#define TMIO_NAND_CONTROL "tmio-nand-control" +#define TMIO_NAND_IRQ "tmio-nand" + +#endif -- cgit v1.2.3 From 73422392734bb68c8ff8bc74ce1bbdc32f1b639a Mon Sep 17 00:00:00 2001 From: Claudio Nieder Date: Mon, 7 Jul 2008 11:56:30 -0400 Subject: Input: add driver for Tabletkiosk Sahara TouchIT-213 touchscreen Signed-off-by: Claudio Nieder Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 11 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/touchit213.c | 234 +++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 247 insertions(+) create mode 100644 drivers/input/touchscreen/touchit213.c (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4085791e7be2..e57366521572 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -351,4 +351,15 @@ config TOUCHSCREEN_USB_GOTOP bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_TOUCHIT213 + tristate "Sahara TouchIT-213 touchscreen" + select SERIO + help + Say Y here if you have a Sahara TouchIT-213 Tablet PC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchit213. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d63bcdcd28ec..39a804cd80f1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o diff --git a/drivers/input/touchscreen/touchit213.c b/drivers/input/touchscreen/touchit213.c new file mode 100644 index 000000000000..d1297ba19daf --- /dev/null +++ b/drivers/input/touchscreen/touchit213.c @@ -0,0 +1,234 @@ +/* + * Sahara TouchIT-213 serial touchscreen driver + * + * Copyright (c) 2007-2008 Claudio Nieder + * + * Based on Touchright driver (drivers/input/touchscreen/touchright.c) + * Copyright (c) 2006 Rick Koch + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Sahara TouchIT-213 serial touchscreen driver" + +MODULE_AUTHOR("Claudio Nieder "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +/* + * Data is received through COM1 at 9600bit/s,8bit,no parity in packets + * of 5 byte each. + * + * +--------+ +--------+ +--------+ +--------+ +--------+ + * |1000000p| |0xxxxxxx| |0xxxxxxx| |0yyyyyyy| |0yyyyyyy| + * +--------+ +--------+ +--------+ +--------+ +--------+ + * MSB LSB MSB LSB + * + * The value of p is 1 as long as the screen is touched and 0 when + * reporting the location where touching stopped, e.g. where the pen was + * lifted from the screen. + * + * When holding the screen in landscape mode as the BIOS text output is + * presented, x is the horizontal axis with values growing from left to + * right and y is the vertical axis with values growing from top to + * bottom. + * + * When holding the screen in portrait mode with the Sahara logo in its + * correct position, x ist the vertical axis with values growing from + * top to bottom and y is the horizontal axis with values growing from + * right to left. + */ + +#define T213_FORMAT_TOUCH_BIT 0x01 +#define T213_FORMAT_STATUS_BYTE 0x80 +#define T213_FORMAT_STATUS_MASK ~T213_FORMAT_TOUCH_BIT + +/* + * On my Sahara Touch-IT 213 I have observed x values from 0 to 0x7f0 + * and y values from 0x1d to 0x7e9, so the actual measurement is + * probably done with an 11 bit precision. + */ +#define T213_MIN_XC 0 +#define T213_MAX_XC 0x07ff +#define T213_MIN_YC 0 +#define T213_MAX_YC 0x07ff + +/* + * Per-touchscreen data. + */ + +struct touchit213 { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char csum; + unsigned char data[5]; + char phys[32]; +}; + +static irqreturn_t touchit213_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct touchit213 *touchit213 = serio_get_drvdata(serio); + struct input_dev *dev = touchit213->dev; + + touchit213->data[touchit213->idx] = data; + + switch (touchit213->idx++) { + case 0: + if ((touchit213->data[0] & T213_FORMAT_STATUS_MASK) != + T213_FORMAT_STATUS_BYTE) { + pr_debug("unsynchronized data: 0x%02x\n", data); + touchit213->idx = 0; + } + break; + + case 4: + touchit213->idx = 0; + input_report_abs(dev, ABS_X, + (touchit213->data[1] << 7) | touchit213->data[2]); + input_report_abs(dev, ABS_Y, + (touchit213->data[3] << 7) | touchit213->data[4]); + input_report_key(dev, BTN_TOUCH, + touchit213->data[0] & T213_FORMAT_TOUCH_BIT); + input_sync(dev); + break; + } + + return IRQ_HANDLED; +} + +/* + * touchit213_disconnect() is the opposite of touchit213_connect() + */ + +static void touchit213_disconnect(struct serio *serio) +{ + struct touchit213 *touchit213 = serio_get_drvdata(serio); + + input_get_device(touchit213->dev); + input_unregister_device(touchit213->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(touchit213->dev); + kfree(touchit213); +} + +/* + * touchit213_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchright protocol and registers it as + * an input device. + */ + +static int touchit213_connect(struct serio *serio, struct serio_driver *drv) +{ + struct touchit213 *touchit213; + struct input_dev *input_dev; + int err; + + touchit213 = kzalloc(sizeof(struct touchit213), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!touchit213 || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + touchit213->serio = serio; + touchit213->dev = input_dev; + snprintf(touchit213->phys, sizeof(touchit213->phys), + "%s/input0", serio->phys); + + input_dev->name = "Sahara Touch-iT213 Serial TouchScreen"; + input_dev->phys = touchit213->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHIT213; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(touchit213->dev, ABS_X, + T213_MIN_XC, T213_MAX_XC, 0, 0); + input_set_abs_params(touchit213->dev, ABS_Y, + T213_MIN_YC, T213_MAX_YC, 0, 0); + + serio_set_drvdata(serio, touchit213); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(touchit213->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(touchit213); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id touchit213_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHIT213, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, touchit213_serio_ids); + +static struct serio_driver touchit213_drv = { + .driver = { + .name = "touchit213", + }, + .description = DRIVER_DESC, + .id_table = touchit213_serio_ids, + .interrupt = touchit213_interrupt, + .connect = touchit213_connect, + .disconnect = touchit213_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init touchit213_init(void) +{ + return serio_register_driver(&touchit213_drv); +} + +static void __exit touchit213_exit(void) +{ + serio_unregister_driver(&touchit213_drv); +} + +module_init(touchit213_init); +module_exit(touchit213_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 36c6ab81c7e0..48defc4d181e 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -213,5 +213,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_FUJITSU 0x35 #define SERIO_ZHENHUA 0x36 #define SERIO_INEXIO 0x37 +#define SERIO_TOUCHIT213 0x37 #endif -- cgit v1.2.3 From c6c4f070a61b2b6e5cd317a5fbf25255878688a2 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Thu, 3 Jul 2008 09:49:39 -0700 Subject: PCI: make pci_name use dev_name Also fixes up the sparc code that was assuming this is not a constant. Acked-by: David S. Miller Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jesse Barnes --- arch/sparc64/kernel/pci.c | 2 +- include/linux/pci.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 112b09f16f36..d00a3656c287 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -408,7 +408,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, dev->class = class >> 8; dev->revision = class & 0xff; - sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), + sprintf(dev->dev.bus_id, "%04x:%02x:%02x.%d", pci_domain_nr(bus), dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); if (ofpci_verbose) diff --git a/include/linux/pci.h b/include/linux/pci.h index 96ebaa8d80e3..4c80dc3f2990 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -996,9 +996,9 @@ static inline void pci_set_drvdata(struct pci_dev *pdev, void *data) /* If you want to know what to call your pci_dev, ask this function. * Again, it's a wrapper around the generic device. */ -static inline char *pci_name(struct pci_dev *pdev) +static inline const char *pci_name(struct pci_dev *pdev) { - return pdev->dev.bus_id; + return dev_name(&pdev->dev); } -- cgit v1.2.3 From eb9d0fe40e313c0a74115ef456a2e43a6c8da72f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jul 2008 03:34:48 +0200 Subject: PCI ACPI: Rework PCI handling of wake-up * Introduce function acpi_pm_device_sleep_wake() for enabling and disabling the system wake-up capability of devices that are power manageable by ACPI. * Introduce function acpi_bus_can_wakeup() allowing other (dependent) subsystems to check if ACPI is able to enable the system wake-up capability of given device. * Introduce callback .sleep_wake() in struct pci_platform_pm_ops and for the ACPI PCI 'driver' make it use acpi_pm_device_sleep_wake(). * Introduce callback .can_wakeup() in struct pci_platform_pm_ops and for the ACPI 'driver' make it use acpi_bus_can_wakeup(). * Move the PME# handlig code out of pci_enable_wake() and split it into two functions, pci_pme_capable() and pci_pme_active(), allowing the caller to check if given device is capable of generating PME# from given power state and to enable/disable the device's PME# functionality, respectively. * Modify pci_enable_wake() to use the new ACPI callbacks and the new PME#-related functions. * Drop the generic .platform_enable_wakeup() callback that is not used any more. * Introduce device_set_wakeup_capable() that will set the power.can_wakeup flag of given device. * Rework PCI device PM initialization so that, if given device is capable of generating wake-up events, either natively through the PME# mechanism, or with the help of the platform, its power.can_wakeup flag is set and its power.should_wakeup flag is unset as appropriate. * Make ACPI set the power.can_wakeup flag for devices found to be wake-up capable by it. * Make the ACPI wake-up code enable/disable GPEs for devices that have the wakeup.flags.prepared flag set (which means that their wake-up power has been enabled). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/acpi/bus.c | 11 +++ drivers/acpi/glue.c | 2 + drivers/acpi/sleep/main.c | 25 +++++++ drivers/acpi/sleep/wakeup.c | 11 +-- drivers/base/power/sysfs.c | 3 - drivers/pci/pci-acpi.c | 20 ++++++ drivers/pci/pci.c | 169 ++++++++++++++++++++++++++++++++------------ drivers/pci/pci.h | 8 +++ drivers/pci/probe.c | 47 +----------- include/acpi/acpi_bus.h | 2 + include/linux/pm_wakeup.h | 26 ++----- 11 files changed, 208 insertions(+), 116 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index b9b69d9629b5..fc1110d6a078 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -306,6 +306,17 @@ bool acpi_bus_power_manageable(acpi_handle handle) EXPORT_SYMBOL(acpi_bus_power_manageable); +bool acpi_bus_can_wakeup(acpi_handle handle) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? false : device->wakeup.flags.valid; +} + +EXPORT_SYMBOL(acpi_bus_can_wakeup); + /* -------------------------------------------------------------------------- Event Management -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 06f8634fe58b..87c5d456e180 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -166,6 +166,8 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) "firmware_node"); ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, "physical_node"); + if (acpi_dev->wakeup.flags.valid) + device_set_wakeup_capable(dev, true); } return 0; diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 4addf8ad50ae..af7f4663deaa 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -468,6 +468,31 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) *d_min_p = d_min; return d_max; } + +/** + * acpi_pm_device_sleep_wake - enable or disable the system wake-up + * capability of given device + * @dev: device to handle + * @enable: 'true' - enable, 'false' - disable the wake-up capability + */ +int acpi_pm_device_sleep_wake(struct device *dev, bool enable) +{ + acpi_handle handle; + struct acpi_device *adev; + + if (!device_may_wakeup(dev)) + return -EINVAL; + + handle = DEVICE_ACPI_HANDLE(dev); + if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { + printk(KERN_DEBUG "ACPI handle has no context!\n"); + return -ENODEV; + } + + return enable ? + acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) : + acpi_disable_wakeup_device_power(adev); +} #endif static void acpi_power_off_prepare(void) diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index 7422a2213944..38655eb132dc 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -66,13 +66,15 @@ void acpi_enable_wakeup_device(u8 sleep_state) list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); + if (!dev->wakeup.flags.valid) continue; + /* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ - if (!dev->wakeup.state.enabled || - sleep_state > (u32) dev->wakeup.sleep_state) { + if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) + || sleep_state > (u32) dev->wakeup.sleep_state) { if (dev->wakeup.flags.run_wake) { spin_unlock(&acpi_device_lock); /* set_gpe_type will disable GPE, leave it like that */ @@ -110,8 +112,9 @@ void acpi_disable_wakeup_device(u8 sleep_state) if (!dev->wakeup.flags.valid) continue; - if (!dev->wakeup.state.enabled || - sleep_state > (u32) dev->wakeup.sleep_state) { + + if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) + || sleep_state > (u32) dev->wakeup.sleep_state) { if (dev->wakeup.flags.run_wake) { spin_unlock(&acpi_device_lock); acpi_set_gpe_type(dev->wakeup.gpe_device, diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index d11f74b038db..596aeecfdffe 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -6,9 +6,6 @@ #include #include "power.h" -int (*platform_enable_wakeup)(struct device *dev, int is_on); - - /* * wakeup - Report/change current wakeup option for device * diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 6bc0d8c870af..7764768b6a0e 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -299,10 +299,30 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) return error; } +static bool acpi_pci_can_wakeup(struct pci_dev *dev) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev); + + return handle ? acpi_bus_can_wakeup(handle) : false; +} + +static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) +{ + int error = acpi_pm_device_sleep_wake(&dev->dev, enable); + + if (!error) + dev_printk(KERN_INFO, &dev->dev, + "wake-up capability %s by ACPI\n", + enable ? "enabled" : "disabled"); + return error; +} + static struct pci_platform_pm_ops acpi_pci_platform_pm = { .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .choose_state = acpi_pci_choose_state, + .can_wakeup = acpi_pci_can_wakeup, + .sleep_wake = acpi_pci_sleep_wake, }; /* ACPI bus type */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 20e28077b96d..a6b1b6f96abc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -380,7 +380,8 @@ static struct pci_platform_pm_ops *pci_platform_pm; int pci_set_platform_pm(struct pci_platform_pm_ops *ops) { - if (!ops->is_manageable || !ops->set_state || !ops->choose_state) + if (!ops->is_manageable || !ops->set_state || !ops->choose_state + || !ops->sleep_wake || !ops->can_wakeup) return -EINVAL; pci_platform_pm = ops; return 0; @@ -403,6 +404,17 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; } +static inline bool platform_pci_can_wakeup(struct pci_dev *dev) +{ + return pci_platform_pm ? pci_platform_pm->can_wakeup(dev) : false; +} + +static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) +{ + return pci_platform_pm ? + pci_platform_pm->sleep_wake(dev, enable) : -ENODEV; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -1035,6 +1047,56 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) return pcibios_set_pcie_reset_state(dev, state); } +/** + * pci_pme_capable - check the capability of PCI device to generate PME# + * @dev: PCI device to handle. + * @pm: PCI PM capability offset of the device. + * @state: PCI state from which device will issue PME#. + */ +static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state) +{ + u16 pmc; + + if (!pm) + return false; + + /* Check device's ability to generate PME# from given state */ + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); + + pmc &= PCI_PM_CAP_PME_MASK; + pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ + + return !!(pmc & (1 << state)); +} + +/** + * pci_pme_active - enable or disable PCI device's PME# function + * @dev: PCI device to handle. + * @pm: PCI PM capability offset of the device. + * @enable: 'true' to enable PME# generation; 'false' to disable it. + * + * The caller must verify that the device is capable of generating PME# before + * calling this function with @enable equal to 'true'. + */ +static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) +{ + u16 pmcsr; + + if (!pm) + return; + + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + /* Clear PME_Status by writing 1 to it and enable PME# */ + pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; + if (!enable) + pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; + + pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + + dev_printk(KERN_INFO, &dev->dev, "PME# %s\n", + enable ? "enabled" : "disabled"); +} + /** * pci_enable_wake - enable PCI device as wakeup event source * @dev: PCI device affected @@ -1046,66 +1108,83 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) * called automatically by this routine. * * Devices with legacy power management (no standard PCI PM capabilities) - * always require such platform hooks. Depending on the platform, devices - * supporting the standard PCI PME# signal may require such platform hooks; - * they always update bits in config space to allow PME# generation. + * always require such platform hooks. * - * -EIO is returned if the device can't ever be a wakeup event source. - * -EINVAL is returned if the device can't generate wakeup events from - * the specified PCI state. Returns zero if the operation is successful. + * RETURN VALUE: + * 0 is returned on success + * -EINVAL is returned if device is not supposed to wake up the system + * Error code depending on the platform is returned if both the platform and + * the native mechanism fail to enable the generation of wake-up events */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { int pm; - int status; - u16 value; - - /* Note that drivers should verify device_may_wakeup(&dev->dev) - * before calling this function. Platform code should report - * errors when drivers try to enable wakeup on devices that - * can't issue wakeups, or on which wakeups were disabled by - * userspace updating the /sys/devices.../power/wakeup file. - */ + int error = 0; + bool pme_done = false; - status = call_platform_enable_wakeup(&dev->dev, enable); - - /* find PCI PM capability in list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!device_may_wakeup(&dev->dev)) + return -EINVAL; - /* If device doesn't support PM Capabilities, but caller wants to - * disable wake events, it's a NOP. Otherwise fail unless the - * platform hooks handled this legacy device already. + /* + * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don + * Anderson we should be doing PME# wake enable followed by ACPI wake + * enable. To disable wake-up we call the platform first, for symmetry. */ - if (!pm) - return enable ? status : 0; - /* Check device's ability to generate PME# */ - pci_read_config_word(dev,pm+PCI_PM_PMC,&value); + if (!enable && platform_pci_can_wakeup(dev)) + error = platform_pci_sleep_wake(dev, false); - value &= PCI_PM_CAP_PME_MASK; - value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ - - /* Check if it can generate PME# from requested state. */ - if (!value || !(value & (1 << state))) { - /* if it can't, revert what the platform hook changed, - * always reporting the base "EINVAL, can't PME#" error - */ - if (enable) - call_platform_enable_wakeup(&dev->dev, 0); - return enable ? -EINVAL : 0; + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!enable || pci_pme_capable(dev, pm, state)) { + pci_pme_active(dev, pm, enable); + pme_done = true; } - pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); + if (enable && platform_pci_can_wakeup(dev)) + error = platform_pci_sleep_wake(dev, true); - /* Clear PME_Status by writing 1 to it and enable PME# */ - value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; + return pme_done ? 0 : error; +} - if (!enable) - value &= ~PCI_PM_CTRL_PME_ENABLE; +/** + * pci_pm_init - Initialize PM functions of given PCI device + * @dev: PCI device to handle. + */ +void pci_pm_init(struct pci_dev *dev) +{ + int pm; + u16 pmc; - pci_write_config_word(dev, pm + PCI_PM_CTRL, value); + /* find PCI PM capability in list */ + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!pm) + return; + /* Check device's ability to generate PME# */ + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - return 0; + if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { + dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", + pmc & PCI_PM_CAP_VER_MASK); + return; + } + + if (pmc & PCI_PM_CAP_PME_MASK) { + dev_printk(KERN_INFO, &dev->dev, + "PME# supported from%s%s%s%s%s\n", + (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "", + (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "", + (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "", + (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "", + (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : ""); + /* + * Make device's PM flags reflect the wake-up capability, but + * let the user space enable it to wake up the system as needed. + */ + device_set_wakeup_capable(&dev->dev, true); + device_set_wakeup_enable(&dev->dev, false); + /* Disable the PME# generation functionality */ + pci_pme_active(dev, pm, false); + } } int diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0cd2e719933b..b08dfc9746af 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -17,6 +17,11 @@ extern void pci_cleanup_rom(struct pci_dev *dev); * platform; to be used during system-wide transitions from a * sleeping state to the working state and vice versa * + * @can_wakeup - returns 'true' if given device is capable of waking up the + * system from a sleeping state + * + * @sleep_wake - enables/disables the system wake up capability of given device + * * If given platform is generally capable of power managing PCI devices, all of * these callbacks are mandatory. */ @@ -24,9 +29,12 @@ struct pci_platform_pm_ops { bool (*is_manageable)(struct pci_dev *dev); int (*set_state)(struct pci_dev *dev, pci_power_t state); pci_power_t (*choose_state)(struct pci_dev *dev); + bool (*can_wakeup)(struct pci_dev *dev); + int (*sleep_wake)(struct pci_dev *dev, bool enable); }; extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); +extern void pci_pm_init(struct pci_dev *dev); extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2f0ae70710d6..b1724cf31b66 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -860,49 +860,6 @@ int pci_cfg_space_size_ext(struct pci_dev *dev) return PCI_CFG_SPACE_SIZE; } -/** - * pci_disable_pme - Disable the PME function of PCI device - * @dev: PCI device affected - * -EINVAL is returned if PCI device doesn't support PME. - * Zero is returned if the PME is supported and can be disabled. - */ -static int pci_disable_pme(struct pci_dev *dev) -{ - int pm; - u16 value; - - /* find PCI PM capability in list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - - /* If device doesn't support PM Capabilities, it means that PME is - * not supported. - */ - if (!pm) - return -EINVAL; - /* Check device's ability to generate PME# */ - pci_read_config_word(dev, pm + PCI_PM_PMC, &value); - - value &= PCI_PM_CAP_PME_MASK; - /* Check if it can generate PME# */ - if (!value) { - /* - * If it is zero, it means that PME is still unsupported - * although there exists the PM capability. - */ - return -EINVAL; - } - - pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); - - /* Clear PME_Status by writing 1 to it */ - value |= PCI_PM_CTRL_PME_STATUS ; - /* Disable PME enable bit */ - value &= ~PCI_PM_CTRL_PME_ENABLE; - pci_write_config_word(dev, pm + PCI_PM_CTRL, value); - - return 0; -} - int pci_cfg_space_size(struct pci_dev *dev) { int pos; @@ -1010,7 +967,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) } pci_vpd_pci22_init(dev); - pci_disable_pme(dev); return dev; } @@ -1031,6 +987,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* Initialize power management of the device */ + pci_pm_init(dev); + /* * Add the device to our list of discovered devices * and the bus list for fixup functions, etc. diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 071daf8db600..7ab5a611e43f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -337,6 +337,7 @@ int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_get_power(acpi_handle handle, int *state); int acpi_bus_set_power(acpi_handle handle, int state); bool acpi_bus_power_manageable(acpi_handle handle); +bool acpi_bus_can_wakeup(acpi_handle handle); #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); @@ -379,6 +380,7 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); #ifdef CONFIG_PM_SLEEP int acpi_pm_device_sleep_state(struct device *, int *); +int acpi_pm_device_sleep_wake(struct device *, bool); #else /* !CONFIG_PM_SLEEP */ static inline int acpi_pm_device_sleep_state(struct device *d, int *p) { diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index f0d0b2cb8d20..3af0c8d05cdc 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -35,6 +35,11 @@ static inline void device_init_wakeup(struct device *dev, int val) dev->power.can_wakeup = dev->power.should_wakeup = !!val; } +static inline void device_set_wakeup_capable(struct device *dev, int val) +{ + dev->power.can_wakeup = !!val; +} + static inline int device_can_wakeup(struct device *dev) { return dev->power.can_wakeup; @@ -47,21 +52,7 @@ static inline void device_set_wakeup_enable(struct device *dev, int val) static inline int device_may_wakeup(struct device *dev) { - return dev->power.can_wakeup & dev->power.should_wakeup; -} - -/* - * Platform hook to activate device wakeup capability, if that's not already - * handled by enable_irq_wake() etc. - * Returns zero on success, else negative errno - */ -extern int (*platform_enable_wakeup)(struct device *dev, int is_on); - -static inline int call_platform_enable_wakeup(struct device *dev, int is_on) -{ - if (platform_enable_wakeup) - return (*platform_enable_wakeup)(dev, is_on); - return 0; + return dev->power.can_wakeup && dev->power.should_wakeup; } #else /* !CONFIG_PM */ @@ -80,11 +71,6 @@ static inline int device_can_wakeup(struct device *dev) #define device_set_wakeup_enable(dev, val) do {} while (0) #define device_may_wakeup(dev) 0 -static inline int call_platform_enable_wakeup(struct device *dev, int is_on) -{ - return 0; -} - #endif /* !CONFIG_PM */ #endif /* _LINUX_PM_WAKEUP_H */ -- cgit v1.2.3 From 404cc2d8ce41ed4031958fba8e633767e8a2e028 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jul 2008 03:35:26 +0200 Subject: PCI PM: Introduce pci_prepare_to_sleep and pci_back_from_sleep Introduce functions pci_prepare_to_sleep() and pci_back_from_sleep(), to be used by the PCI drivers that want to place their devices into the lowest power state appropiate for them (PCI_D3hot, if the device is not supposed to wake up the system, or the deepest state from which the wake-up is possible, otherwise) while the system is being prepared to go into a sleeping state and to put them back into D0 during the subsequent transition to the working state. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 85 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a6b1b6f96abc..1b0ec45e9934 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1146,6 +1146,87 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) return pme_done ? 0 : error; } +/** + * pci_prepare_to_sleep - prepare PCI device for system-wide transition into + * a sleep state + * @dev: Device to handle. + * + * Choose the power state appropriate for the device depending on whether + * it can wake up the system and/or is power manageable by the platform + * (PCI_D3hot is the default) and put the device into that state. + */ +int pci_prepare_to_sleep(struct pci_dev *dev) +{ + pci_power_t target_state = PCI_D3hot; + int pm = pci_find_capability(dev, PCI_CAP_ID_PM); + int error; + + if (platform_pci_power_manageable(dev)) { + /* + * Call the platform to choose the target state of the device + * and enable wake-up from this state if supported. + */ + pci_power_t state = platform_pci_choose_state(dev); + + switch (state) { + case PCI_POWER_ERROR: + case PCI_UNKNOWN: + break; + case PCI_D1: + case PCI_D2: + if (pci_no_d1d2(dev)) + break; + default: + target_state = state; + pci_enable_wake(dev, target_state, true); + } + } else if (device_may_wakeup(&dev->dev)) { + /* + * Find the deepest state from which the device can generate + * wake-up events, make it the target state and enable device + * to generate PME#. + */ + u16 pmc; + + if (!pm) + return -EIO; + + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); + if (pmc & PCI_PM_CAP_PME_MASK) { + if (!(pmc & PCI_PM_CAP_PME_D3)) { + /* Device cannot generate PME# from D3_hot */ + if (pmc & PCI_PM_CAP_PME_D2) + target_state = PCI_D2; + else if (pmc & PCI_PM_CAP_PME_D1) + target_state = PCI_D1; + else + target_state = PCI_D0; + } + pci_pme_active(dev, pm, true); + } + } + + error = pci_set_power_state(dev, target_state); + + if (error) + pci_enable_wake(dev, target_state, false); + + return error; +} + +/** + * pci_back_from_sleep - turn PCI device on during system-wide transition into + * the working state a sleep state + * @dev: Device to handle. + * + * Disable device's sytem wake-up capability and put it into D0. + */ +int pci_back_from_sleep(struct pci_dev *dev) +{ + pci_enable_wake(dev, PCI_D0, false); + return pci_set_power_state(dev, PCI_D0); +} + /** * pci_pm_init - Initialize PM functions of given PCI device * @dev: PCI device to handle. @@ -1853,5 +1934,7 @@ EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); EXPORT_SYMBOL(pci_enable_wake); +EXPORT_SYMBOL(pci_prepare_to_sleep); +EXPORT_SYMBOL(pci_back_from_sleep); EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); diff --git a/include/linux/pci.h b/include/linux/pci.h index 4c80dc3f2990..52ac06dcce98 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -632,6 +632,8 @@ int pci_restore_state(struct pci_dev *dev); int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); +int pci_prepare_to_sleep(struct pci_dev *dev); +int pci_back_from_sleep(struct pci_dev *dev); /* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); -- cgit v1.2.3 From 337001b6c42938f49a880b1b8306c3ed771a7e61 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jul 2008 03:36:24 +0200 Subject: PCI: Simplify PCI device PM code If the offset of PCI device's PM capability in its configuration space, the mask of states that the device supports PME# from and the D1 and D2 support bits are cached in the corresponding struct pci_dev, the PCI device PM code can be simplified quite a bit. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 118 +++++++++++++++++++++-------------------------- include/linux/pci.h | 8 +++- include/linux/pci_regs.h | 1 + 3 files changed, 60 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1b0ec45e9934..e632a58ba5d0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -419,7 +419,6 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. * * RETURN VALUE: @@ -430,12 +429,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * 0 if device's power state has been successfully changed. */ static int -pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) +pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) { - u16 pmcsr, pmc; + u16 pmcsr; bool need_restore = false; - if (!pm) + if (!dev->pm_cap) return -EIO; if (state < PCI_D0 || state > PCI_D3hot) @@ -455,20 +454,12 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) return -EINVAL; } - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - - if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { - dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", - pmc & PCI_PM_CAP_VER_MASK); - return -EIO; - } - /* check if this device supports the desired state */ - if ((state == PCI_D1 && !(pmc & PCI_PM_CAP_D1)) - || (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))) + if ((state == PCI_D1 && !dev->d1_support) + || (state == PCI_D2 && !dev->d2_support)) return -EIO; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); /* If we're (effectively) in D3, force entire word to 0. * This doesn't affect PME_Status, disables PME_En, and @@ -492,7 +483,7 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) } /* enter specified state */ - pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); /* Mandatory power management transition delays */ /* see PCI PM 1.1 5.6.1 table 18 */ @@ -528,14 +519,13 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) * pci_update_current_state - Read PCI power state of given device from its * PCI PM registers and cache it * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. */ -static void pci_update_current_state(struct pci_dev *dev, int pm) +static void pci_update_current_state(struct pci_dev *dev) { - if (pm) { + if (dev->pm_cap) { u16 pmcsr; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } } @@ -557,7 +547,7 @@ static void pci_update_current_state(struct pci_dev *dev, int pm) */ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) { - int pm, error; + int error; /* bound the state we're entering */ if (state > PCI_D3hot) @@ -572,9 +562,6 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ return 0; - /* Find PCI PM capability in the list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (state == PCI_D0 && platform_pci_power_manageable(dev)) { /* * Allow the platform to change the state, for example via ACPI @@ -582,16 +569,16 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ int ret = platform_pci_set_power_state(dev, PCI_D0); if (!ret) - pci_update_current_state(dev, pm); + pci_update_current_state(dev); } - error = pci_raw_set_power_state(dev, pm, state); + error = pci_raw_set_power_state(dev, state); if (state > PCI_D0 && platform_pci_power_manageable(dev)) { /* Allow the platform to finalize the transition */ int ret = platform_pci_set_power_state(dev, state); if (!ret) { - pci_update_current_state(dev, pm); + pci_update_current_state(dev); error = 0; } } @@ -1050,48 +1037,38 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. * @state: PCI state from which device will issue PME#. */ -static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state) +static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) { - u16 pmc; - - if (!pm) + if (!dev->pm_cap) return false; - /* Check device's ability to generate PME# from given state */ - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - - pmc &= PCI_PM_CAP_PME_MASK; - pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ - - return !!(pmc & (1 << state)); + return !!(dev->pme_support & (1 << state)); } /** * pci_pme_active - enable or disable PCI device's PME# function * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. * @enable: 'true' to enable PME# generation; 'false' to disable it. * * The caller must verify that the device is capable of generating PME# before * calling this function with @enable equal to 'true'. */ -static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) +static void pci_pme_active(struct pci_dev *dev, bool enable) { u16 pmcsr; - if (!pm) + if (!dev->pm_cap) return; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); /* Clear PME_Status by writing 1 to it and enable PME# */ pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; if (!enable) pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; - pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); dev_printk(KERN_INFO, &dev->dev, "PME# %s\n", enable ? "enabled" : "disabled"); @@ -1118,7 +1095,6 @@ static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { - int pm; int error = 0; bool pme_done = false; @@ -1134,9 +1110,8 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) if (!enable && platform_pci_can_wakeup(dev)) error = platform_pci_sleep_wake(dev, false); - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (!enable || pci_pme_capable(dev, pm, state)) { - pci_pme_active(dev, pm, enable); + if (!enable || pci_pme_capable(dev, state)) { + pci_pme_active(dev, enable); pme_done = true; } @@ -1158,7 +1133,6 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) int pci_prepare_to_sleep(struct pci_dev *dev) { pci_power_t target_state = PCI_D3hot; - int pm = pci_find_capability(dev, PCI_CAP_ID_PM); int error; if (platform_pci_power_manageable(dev)) { @@ -1186,23 +1160,14 @@ int pci_prepare_to_sleep(struct pci_dev *dev) * wake-up events, make it the target state and enable device * to generate PME#. */ - u16 pmc; - - if (!pm) + if (!dev->pm_cap) return -EIO; - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - if (pmc & PCI_PM_CAP_PME_MASK) { - if (!(pmc & PCI_PM_CAP_PME_D3)) { - /* Device cannot generate PME# from D3_hot */ - if (pmc & PCI_PM_CAP_PME_D2) - target_state = PCI_D2; - else if (pmc & PCI_PM_CAP_PME_D1) - target_state = PCI_D1; - else - target_state = PCI_D0; - } - pci_pme_active(dev, pm, true); + if (dev->pme_support) { + while (target_state + && !(dev->pme_support & (1 << target_state))) + target_state--; + pci_pme_active(dev, true); } } @@ -1236,6 +1201,8 @@ void pci_pm_init(struct pci_dev *dev) int pm; u16 pmc; + dev->pm_cap = 0; + /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); if (!pm) @@ -1249,7 +1216,23 @@ void pci_pm_init(struct pci_dev *dev) return; } - if (pmc & PCI_PM_CAP_PME_MASK) { + dev->pm_cap = pm; + + dev->d1_support = false; + dev->d2_support = false; + if (!pci_no_d1d2(dev)) { + if (pmc & PCI_PM_CAP_D1) { + dev_printk(KERN_DEBUG, &dev->dev, "supports D1\n"); + dev->d1_support = true; + } + if (pmc & PCI_PM_CAP_D2) { + dev_printk(KERN_DEBUG, &dev->dev, "supports D2\n"); + dev->d2_support = true; + } + } + + pmc &= PCI_PM_CAP_PME_MASK; + if (pmc) { dev_printk(KERN_INFO, &dev->dev, "PME# supported from%s%s%s%s%s\n", (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "", @@ -1257,6 +1240,7 @@ void pci_pm_init(struct pci_dev *dev) (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "", (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "", (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : ""); + dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT; /* * Make device's PM flags reflect the wake-up capability, but * let the user space enable it to wake up the system as needed. @@ -1264,7 +1248,9 @@ void pci_pm_init(struct pci_dev *dev) device_set_wakeup_capable(&dev->dev, true); device_set_wakeup_enable(&dev->dev, false); /* Disable the PME# generation functionality */ - pci_pme_active(dev, pm, false); + pci_pme_active(dev, false); + } else { + dev->pme_support = 0; } } diff --git a/include/linux/pci.h b/include/linux/pci.h index 52ac06dcce98..68a29f0f2748 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -177,6 +177,13 @@ struct pci_dev { pci_power_t current_state; /* Current operating state. In ACPI-speak, this is D0-D3, D0 being fully functional, and D3 being off. */ + int pm_cap; /* PM capability offset in the + configuration space */ + unsigned int pme_support:5; /* Bitmask of states from which PME# + can be generated */ + unsigned int d1_support:1; /* Low power state D1 is supported */ + unsigned int d2_support:1; /* Low power state D2 is supported */ + unsigned int no_d1d2:1; /* Only allow D0 and D3 */ #ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state; /* ASPM link state. */ @@ -201,7 +208,6 @@ struct pci_dev { unsigned int is_added:1; unsigned int is_busmaster:1; /* device is busmaster */ unsigned int no_msi:1; /* device may not use msi */ - unsigned int no_d1d2:1; /* only allow d0 or d3 */ unsigned int block_ucfg_access:1; /* userspace config space access is blocked */ unsigned int broken_parity_status:1; /* Device generates false positive parity */ unsigned int msi_enabled:1; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index c0c1223c9194..19958b929905 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -231,6 +231,7 @@ #define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ #define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ #define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ #define PCI_PM_CTRL 4 /* PM control and status register */ #define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ #define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ -- cgit v1.2.3 From d2dbf343329dc777d77488743465f7be4245971d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 13 Jun 2008 02:00:56 -0700 Subject: x86: clean up reserve_bootmem_generic() and port it to 32-bit 1. add reserve_bootmem_generic for 32bit 2. change len to unsigned long 3. make early_res_to_bootmem to use it Signed-off-by: Yinghai Lu Signed-off-by: Ingo Molnar --- arch/x86/kernel/e820.c | 2 +- arch/x86/kernel/mpparse.c | 18 ++++++------------ arch/x86/mm/init_32.c | 6 ++++++ arch/x86/mm/init_64.c | 3 ++- include/asm-x86/proto.h | 2 -- include/linux/bootmem.h | 2 ++ 6 files changed, 17 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 4f2cd5d179e2..774063f11be0 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -635,7 +635,7 @@ void __init early_res_to_bootmem(u64 start, u64 end) continue; printk(KERN_INFO " early res: %d [%llx-%llx] %s\n", i, final_start, final_end - 1, r->name); - reserve_bootmem(final_start, final_end - final_start, + reserve_bootmem_generic(final_start, final_end - final_start, BOOTMEM_DEFAULT); } } diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c index 7ac1b689b70a..b62ac6ba1410 100644 --- a/arch/x86/kernel/mpparse.c +++ b/arch/x86/kernel/mpparse.c @@ -859,10 +859,11 @@ static int __init smp_scan_config(unsigned long base, unsigned long length, if (!reserve) return 1; -#ifdef CONFIG_X86_32 - reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE, + reserve_bootmem_generic(virt_to_phys(mpf), PAGE_SIZE, BOOTMEM_DEFAULT); if (mpf->mpf_physptr) { + unsigned long size = PAGE_SIZE; +#ifdef CONFIG_X86_32 /* * We cannot access to MPC table to compute * table size yet, as only few megabytes from @@ -872,22 +873,15 @@ static int __init smp_scan_config(unsigned long base, unsigned long length, * PAGE_SIZE from mpg->mpf_physptr yields BUG() * in reserve_bootmem. */ - unsigned long size = PAGE_SIZE; unsigned long end = max_low_pfn * PAGE_SIZE; if (mpf->mpf_physptr + size > end) size = end - mpf->mpf_physptr; - reserve_bootmem(mpf->mpf_physptr, size, +#endif + reserve_bootmem_generic(mpf->mpf_physptr, size, BOOTMEM_DEFAULT); } -#else - reserve_bootmem_generic(virt_to_phys(mpf), PAGE_SIZE, - BOOTMEM_DEFAULT); - if (mpf->mpf_physptr) - reserve_bootmem_generic(mpf->mpf_physptr, - PAGE_SIZE, BOOTMEM_DEFAULT); -#endif - return 1; + return 1; } bp += 4; length -= 16; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 0e7bb5e81670..abadb1da70df 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -785,3 +785,9 @@ void free_initrd_mem(unsigned long start, unsigned long end) free_init_pages("initrd memory", start, end); } #endif + +int __init reserve_bootmem_generic(unsigned long phys, unsigned long len, + int flags) +{ + return reserve_bootmem(phys, len, flags); +} diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index bf7bf1de6c25..b8c2c1ef7ad5 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -799,7 +799,8 @@ void free_initrd_mem(unsigned long start, unsigned long end) } #endif -int __init reserve_bootmem_generic(unsigned long phys, unsigned len, int flags) +int __init reserve_bootmem_generic(unsigned long phys, unsigned long len, + int flags) { #ifdef CONFIG_NUMA int nid, next_nid; diff --git a/include/asm-x86/proto.h b/include/asm-x86/proto.h index a9f51472521e..3dd458c385c0 100644 --- a/include/asm-x86/proto.h +++ b/include/asm-x86/proto.h @@ -14,8 +14,6 @@ extern void ia32_syscall(void); extern void ia32_cstar_target(void); extern void ia32_sysenter_target(void); -extern int reserve_bootmem_generic(unsigned long phys, unsigned len, int flags); - extern void syscall32_cpu_init(void); extern void check_efer(void); diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 686895bacd9d..a1d9b79078ea 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -84,6 +84,8 @@ extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); __alloc_bootmem_low(x, PAGE_SIZE, 0) #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ +extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, + int flags); extern unsigned long free_all_bootmem(void); extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); extern void *__alloc_bootmem_node(pg_data_t *pgdat, -- cgit v1.2.3 From cc1050bafebfb1d7935331282e948b5016318192 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 13 Jun 2008 19:08:52 -0700 Subject: x86: replace shrink_active_range() with remove_active_range() in case we have kva before ramdisk on a node, we still need to use those ranges. v2: reserve_early kva ram area, in case there are holes in highmem, to avoid those area could be treat as free high pages. Signed-off-by: Yinghai Lu Signed-off-by: Ingo Molnar --- arch/x86/mm/discontig_32.c | 45 ++++++++++++++++++++++++--------------------- include/linux/mm.h | 3 ++- mm/page_alloc.c | 29 +++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index accc7c6c57fc..c3f119e99e0d 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -230,8 +230,8 @@ static unsigned long calculate_numa_remap_pages(void) unsigned long size, reserve_pages = 0; for_each_online_node(nid) { - u64 node_end_target; - u64 node_end_final; + u64 node_kva_target; + u64 node_kva_final; /* * The acpi/srat node info can show hot-add memroy zones @@ -254,42 +254,45 @@ static unsigned long calculate_numa_remap_pages(void) /* now the roundup is correct, convert to PAGE_SIZE pages */ size = size * PTRS_PER_PTE; - node_end_target = round_down(node_end_pfn[nid] - size, + node_kva_target = round_down(node_end_pfn[nid] - size, PTRS_PER_PTE); - node_end_target <<= PAGE_SHIFT; + node_kva_target <<= PAGE_SHIFT; do { - node_end_final = find_e820_area(node_end_target, + node_kva_final = find_e820_area(node_kva_target, ((u64)node_end_pfn[nid])<>PAGE_SHIFT) > (node_start_pfn[nid])); + node_kva_target -= LARGE_PAGE_BYTES; + } while (node_kva_final == -1ULL && + (node_kva_target>>PAGE_SHIFT) > (node_start_pfn[nid])); - if (node_end_final == -1ULL) + if (node_kva_final == -1ULL) panic("Can not get kva ram\n"); - printk("Reserving %ld pages of KVA for lmem_map of node %d\n", - size, nid); node_remap_size[nid] = size; node_remap_offset[nid] = reserve_pages; reserve_pages += size; - printk("Shrinking node %d from %ld pages to %lld pages\n", - nid, node_end_pfn[nid], node_end_final>>PAGE_SHIFT); + printk("Reserving %ld pages of KVA for lmem_map of node %d at %llx\n", + size, nid, node_kva_final>>PAGE_SHIFT); /* * prevent kva address below max_low_pfn want it on system * with less memory later. * layout will be: KVA address , KVA RAM + * + * we are supposed to only record the one less then max_low_pfn + * but we could have some hole in high memory, and it will only + * check page_is_ram(pfn) && !page_is_reserved_early(pfn) to decide + * to use it as free. + * So reserve_early here, hope we don't run out of that array */ - if ((node_end_final>>PAGE_SHIFT) < max_low_pfn) - reserve_early(node_end_final, - node_end_final+(((u64)size)<>PAGE_SHIFT; - node_remap_start_pfn[nid] = node_end_pfn[nid]; - shrink_active_range(nid, node_end_pfn[nid]); + reserve_early(node_kva_final, + node_kva_final+(((u64)size)<>PAGE_SHIFT; + remove_active_range(nid, node_remap_start_pfn[nid], + node_remap_start_pfn[nid] + size); } printk("Reserving total of %ld pages for numa KVA remap\n", reserve_pages); diff --git a/include/linux/mm.h b/include/linux/mm.h index ce8e397a61f6..034a3156d2f0 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -998,7 +998,8 @@ extern void free_area_init_node(int nid, pg_data_t *pgdat, extern void free_area_init_nodes(unsigned long *max_zone_pfn); extern void add_active_range(unsigned int nid, unsigned long start_pfn, unsigned long end_pfn); -extern void shrink_active_range(unsigned int nid, unsigned long new_end_pfn); +extern void remove_active_range(unsigned int nid, unsigned long start_pfn, + unsigned long end_pfn); extern void push_node_boundaries(unsigned int nid, unsigned long start_pfn, unsigned long end_pfn); extern void remove_all_active_ranges(void); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index eee5ba7509c1..d80e1868e570 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3552,30 +3552,47 @@ void __init add_active_range(unsigned int nid, unsigned long start_pfn, } /** - * shrink_active_range - Shrink an existing registered range of PFNs + * remove_active_range - Shrink an existing registered range of PFNs * @nid: The node id the range is on that should be shrunk - * @new_end_pfn: The new PFN of the range + * @start_pfn: The new PFN of the range + * @end_pfn: The new PFN of the range * * i386 with NUMA use alloc_remap() to store a node_mem_map on a local node. * The map is kept near the end physical page range that has already been * registered. This function allows an arch to shrink an existing registered * range. */ -void __init shrink_active_range(unsigned int nid, unsigned long new_end_pfn) +void __init remove_active_range(unsigned int nid, unsigned long start_pfn, + unsigned long end_pfn) { int i, j; int removed = 0; + printk(KERN_DEBUG "remove_active_range (%d, %lu, %lu)\n", + nid, start_pfn, end_pfn); + /* Find the old active region end and shrink */ for_each_active_range_index_in_nid(i, nid) { - if (early_node_map[i].start_pfn >= new_end_pfn) { + if (early_node_map[i].start_pfn >= start_pfn && + early_node_map[i].end_pfn <= end_pfn) { /* clear it */ + early_node_map[i].start_pfn = 0; early_node_map[i].end_pfn = 0; removed = 1; continue; } - if (early_node_map[i].end_pfn > new_end_pfn) { - early_node_map[i].end_pfn = new_end_pfn; + if (early_node_map[i].start_pfn < start_pfn && + early_node_map[i].end_pfn > start_pfn) { + unsigned long temp_end_pfn = early_node_map[i].end_pfn; + early_node_map[i].end_pfn = start_pfn; + if (temp_end_pfn > end_pfn) + add_active_range(nid, end_pfn, temp_end_pfn); + continue; + } + if (early_node_map[i].start_pfn >= start_pfn && + early_node_map[i].end_pfn > end_pfn && + early_node_map[i].start_pfn < end_pfn) { + early_node_map[i].start_pfn = end_pfn; continue; } } -- cgit v1.2.3 From b5bc6c0e55000dab86b73f838f5ad02908b23755 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 14 Jun 2008 18:32:52 -0700 Subject: x86, mm: use add_highpages_with_active_regions() for high pages init v2 use early_node_map to init high pages, so we can remove page_is_ram() and page_is_reserved_early() in the big loop with add_one_highpage also remove page_is_reserved_early(), it is not needed anymore. v2: fix the build of other platforms Signed-off-by: Yinghai Lu Signed-off-by: Ingo Molnar --- arch/x86/kernel/e820.c | 11 -------- arch/x86/mm/discontig_32.c | 19 ++++++-------- arch/x86/mm/init_32.c | 62 ++++++++++++++++++++++++++++++++++++++-------- include/asm-x86/e820.h | 1 - include/asm-x86/highmem.h | 3 +++ include/linux/mm.h | 2 ++ mm/page_alloc.c | 8 ++++++ 7 files changed, 71 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 5051ce744b4e..ed46b7a6bc13 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -612,17 +612,6 @@ void __init free_early(u64 start, u64 end) early_res[j - 1].end = 0; } -int __init page_is_reserved_early(unsigned long pagenr) -{ - u64 start = (u64)pagenr << PAGE_SHIFT; - int i; - struct early_res *r; - - i = find_overlapped_early(start, start + PAGE_SIZE); - r = &early_res[i]; - return (i < MAX_EARLY_RES && r->end); -} - void __init early_res_to_bootmem(u64 start, u64 end) { int i; diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index c3f119e99e0d..7c4d0255f8d8 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -100,7 +100,6 @@ unsigned long node_memmap_size_bytes(int nid, unsigned long start_pfn, #endif extern unsigned long find_max_low_pfn(void); -extern void add_one_highpage_init(struct page *, int, int); extern unsigned long highend_pfn, highstart_pfn; #define LARGE_PAGE_BYTES (PTRS_PER_PTE * PAGE_SIZE) @@ -432,10 +431,10 @@ void __init set_highmem_pages_init(int bad_ppro) { #ifdef CONFIG_HIGHMEM struct zone *zone; - struct page *page; + int nid; for_each_zone(zone) { - unsigned long node_pfn, zone_start_pfn, zone_end_pfn; + unsigned long zone_start_pfn, zone_end_pfn; if (!is_highmem(zone)) continue; @@ -443,16 +442,12 @@ void __init set_highmem_pages_init(int bad_ppro) zone_start_pfn = zone->zone_start_pfn; zone_end_pfn = zone_start_pfn + zone->spanned_pages; + nid = zone_to_nid(zone); printk("Initializing %s for node %d (%08lx:%08lx)\n", - zone->name, zone_to_nid(zone), - zone_start_pfn, zone_end_pfn); - - for (node_pfn = zone_start_pfn; node_pfn < zone_end_pfn; node_pfn++) { - if (!pfn_valid(node_pfn)) - continue; - page = pfn_to_page(node_pfn); - add_one_highpage_init(page, node_pfn, bad_ppro); - } + zone->name, nid, zone_start_pfn, zone_end_pfn); + + add_highpages_with_active_regions(nid, zone_start_pfn, + zone_end_pfn, bad_ppro); } totalram_pages += totalhigh_pages; #endif diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index abadb1da70df..ba07a489230e 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -287,10 +287,10 @@ static void __init permanent_kmaps_init(pgd_t *pgd_base) pkmap_page_table = pte; } -void __init add_one_highpage_init(struct page *page, int pfn, int bad_ppro) +static void __init +add_one_highpage_init(struct page *page, int pfn, int bad_ppro) { - if (page_is_ram(pfn) && !(bad_ppro && page_kills_ppro(pfn)) && - !page_is_reserved_early(pfn)) { + if (!(bad_ppro && page_kills_ppro(pfn))) { ClearPageReserved(page); init_page_count(page); __free_page(page); @@ -299,18 +299,58 @@ void __init add_one_highpage_init(struct page *page, int pfn, int bad_ppro) SetPageReserved(page); } +struct add_highpages_data { + unsigned long start_pfn; + unsigned long end_pfn; + int bad_ppro; +}; + +static void __init add_highpages_work_fn(unsigned long start_pfn, + unsigned long end_pfn, void *datax) +{ + int node_pfn; + struct page *page; + unsigned long final_start_pfn, final_end_pfn; + struct add_highpages_data *data; + int bad_ppro; + + data = (struct add_highpages_data *)datax; + bad_ppro = data->bad_ppro; + + final_start_pfn = max(start_pfn, data->start_pfn); + final_end_pfn = min(end_pfn, data->end_pfn); + if (final_start_pfn >= final_end_pfn) + return; + + for (node_pfn = final_start_pfn; node_pfn < final_end_pfn; + node_pfn++) { + if (!pfn_valid(node_pfn)) + continue; + page = pfn_to_page(node_pfn); + add_one_highpage_init(page, node_pfn, bad_ppro); + } + +} + +void __init add_highpages_with_active_regions(int nid, unsigned long start_pfn, + unsigned long end_pfn, + int bad_ppro) +{ + struct add_highpages_data data; + + data.start_pfn = start_pfn; + data.end_pfn = end_pfn; + data.bad_ppro = bad_ppro; + + work_with_active_regions(nid, add_highpages_work_fn, &data); +} + #ifndef CONFIG_NUMA static void __init set_highmem_pages_init(int bad_ppro) { - int pfn; + add_highpages_with_active_regions(0, highstart_pfn, highend_pfn, + bad_ppro); - for (pfn = highstart_pfn; pfn < highend_pfn; pfn++) { - /* - * Holes under sparsemem might not have no mem_map[]: - */ - if (pfn_valid(pfn)) - add_one_highpage_init(pfn_to_page(pfn), pfn, bad_ppro); - } totalram_pages += totalhigh_pages; } #endif /* !CONFIG_NUMA */ diff --git a/include/asm-x86/e820.h b/include/asm-x86/e820.h index 6b0ce745a60c..55d310596907 100644 --- a/include/asm-x86/e820.h +++ b/include/asm-x86/e820.h @@ -86,7 +86,6 @@ extern u64 find_e820_area_size(u64 start, u64 *sizep, u64 align); extern void reserve_early(u64 start, u64 end, char *name); extern void free_early(u64 start, u64 end); extern void early_res_to_bootmem(u64 start, u64 end); -extern int page_is_reserved_early(unsigned long pagenr); extern u64 early_reserve_e820(u64 startt, u64 sizet, u64 align); extern unsigned long e820_end_of_ram(void); diff --git a/include/asm-x86/highmem.h b/include/asm-x86/highmem.h index e153f3b44774..85c4fea41ff6 100644 --- a/include/asm-x86/highmem.h +++ b/include/asm-x86/highmem.h @@ -74,6 +74,9 @@ struct page *kmap_atomic_to_page(void *ptr); #define flush_cache_kmaps() do { } while (0) +extern void add_highpages_with_active_regions(int nid, unsigned long start_pfn, + unsigned long end_pfn, int bad_ppro); + #endif /* __KERNEL__ */ #endif /* _ASM_HIGHMEM_H */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 034a3156d2f0..e4de460907c1 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1011,6 +1011,8 @@ extern unsigned long find_min_pfn_with_active_regions(void); extern unsigned long find_max_pfn_with_active_regions(void); extern void free_bootmem_with_active_regions(int nid, unsigned long max_low_pfn); +typedef void (*work_fn_t)(unsigned long, unsigned long, void *); +extern void work_with_active_regions(int nid, work_fn_t work_fn, void *data); extern void sparse_memory_present_with_active_regions(int nid); #ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID extern int early_pfn_to_nid(unsigned long pfn); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d80e1868e570..41c6e3aa059f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2929,6 +2929,14 @@ void __init free_bootmem_with_active_regions(int nid, } } +void __init work_with_active_regions(int nid, work_fn_t work_fn, void *data) +{ + int i; + + for_each_active_range_index_in_nid(i, nid) + work_fn(early_node_map[i].start_pfn, early_node_map[i].end_pfn, + data); +} /** * sparse_memory_present_with_active_regions - Call memory_present for each active range * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used. -- cgit v1.2.3 From 3461b0af025251bbc6b3d56c821c6ac2de6f7209 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 12 May 2008 21:21:13 +0200 Subject: x86: remove static boot_cpu_pda array v2 * Remove the boot_cpu_pda array and pointer table from the data section. Allocate the pointer table and array during init. do_boot_cpu() will reallocate the pda in node local memory and if the cpu is being brought up before the bootmem array is released (after_bootmem = 0), then it will free the initial pda. This will happen for all cpus present at system startup. This removes 512k + 32k bytes from the data section. For inclusion into sched-devel/latest tree. Based on: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + sched-devel/latest .../mingo/linux-2.6-sched-devel.git Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- arch/x86/kernel/head64.c | 26 +++++++++++++++-- arch/x86/kernel/setup.c | 73 ++++++++++++++++++++++++++++++++++++----------- arch/x86/kernel/setup64.c | 8 ++++-- arch/x86/kernel/smpboot.c | 59 +++++++++++++++++++++++++++++--------- include/asm-x86/pda.h | 6 ++-- include/linux/mm.h | 1 + 6 files changed, 135 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index e25c57b8aa84..0ab59edd7067 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -25,6 +25,24 @@ #include #include +/* boot cpu pda */ +static struct x8664_pda _boot_cpu_pda __read_mostly; + +#ifdef CONFIG_SMP +#ifdef CONFIG_DEBUG_PER_CPU_MAPS +/* + * We install an empty cpu_pda pointer table to trap references before + * the actual cpu_pda pointer table is created in setup_cpu_pda_map(). + */ +static struct x8664_pda *__cpu_pda[NR_CPUS] __initdata; +#else +static struct x8664_pda *__cpu_pda[1] __read_mostly; +#endif + +#else /* !CONFIG_SMP (NR_CPUS will be 1) */ +static struct x8664_pda *__cpu_pda[NR_CPUS] __read_mostly; +#endif + static void __init zap_identity_mappings(void) { pgd_t *pgd = pgd_offset_k(0UL); @@ -156,10 +174,12 @@ void __init x86_64_start_kernel(char * real_mode_data) early_printk("Kernel alive\n"); - for (i = 0; i < NR_CPUS; i++) - cpu_pda(i) = &boot_cpu_pda[i]; - + _cpu_pda = __cpu_pda; + cpu_pda(0) = &_boot_cpu_pda; pda_init(0); + + early_printk("Kernel really alive\n"); + copy_bootdata(__va(real_mode_data)); reserve_early(__pa_symbol(&_text), __pa_symbol(&_end), "TEXT DATA BSS"); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 913af838c3c5..dd12c1c84a8f 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -101,6 +101,50 @@ static inline void setup_cpumask_of_cpu(void) { } */ unsigned long __per_cpu_offset[NR_CPUS] __read_mostly; EXPORT_SYMBOL(__per_cpu_offset); +static inline void setup_cpu_pda_map(void) { } + +#elif !defined(CONFIG_SMP) +static inline void setup_cpu_pda_map(void) { } + +#else /* CONFIG_SMP && CONFIG_X86_64 */ + +/* + * Allocate cpu_pda pointer table and array via alloc_bootmem. + */ +static void __init setup_cpu_pda_map(void) +{ + char *pda; + struct x8664_pda **new_cpu_pda; + unsigned long size; + int cpu; + + size = roundup(sizeof(struct x8664_pda), cache_line_size()); + + /* allocate cpu_pda array and pointer table */ + { + unsigned long tsize = nr_cpu_ids * sizeof(void *); + unsigned long asize = size * (nr_cpu_ids - 1); + + tsize = roundup(tsize, cache_line_size()); + new_cpu_pda = alloc_bootmem(tsize + asize); + pda = (char *)new_cpu_pda + tsize; + } + + /* initialize pointer table to static pda's */ + for_each_possible_cpu(cpu) { + if (cpu == 0) { + /* leave boot cpu pda in place */ + new_cpu_pda[0] = cpu_pda(0); + continue; + } + new_cpu_pda[cpu] = (struct x8664_pda *)pda; + new_cpu_pda[cpu]->in_bootmem = 1; + pda += size; + } + + /* point to new pointer table */ + _cpu_pda = new_cpu_pda; +} #endif /* @@ -110,46 +154,43 @@ EXPORT_SYMBOL(__per_cpu_offset); */ void __init setup_per_cpu_areas(void) { - int i, highest_cpu = 0; - unsigned long size; + ssize_t size = PERCPU_ENOUGH_ROOM; + char *ptr; + int cpu; #ifdef CONFIG_HOTPLUG_CPU prefill_possible_map(); +#else + nr_cpu_ids = num_processors; #endif + /* Setup cpu_pda map */ + setup_cpu_pda_map(); + /* Copy section for each CPU (we discard the original) */ size = PERCPU_ENOUGH_ROOM; printk(KERN_INFO "PERCPU: Allocating %lu bytes of per cpu data\n", size); - for_each_possible_cpu(i) { - char *ptr; + for_each_possible_cpu(cpu) { #ifndef CONFIG_NEED_MULTIPLE_NODES ptr = alloc_bootmem_pages(size); #else - int node = early_cpu_to_node(i); + int node = early_cpu_to_node(cpu); if (!node_online(node) || !NODE_DATA(node)) { ptr = alloc_bootmem_pages(size); printk(KERN_INFO "cpu %d has no node %d or node-local memory\n", - i, node); + cpu, node); } else ptr = alloc_bootmem_pages_node(NODE_DATA(node), size); #endif - if (!ptr) - panic("Cannot allocate cpu data for CPU %d\n", i); -#ifdef CONFIG_X86_64 - cpu_pda(i)->data_offset = ptr - __per_cpu_start; -#else - __per_cpu_offset[i] = ptr - __per_cpu_start; -#endif + per_cpu_offset(cpu) = ptr - __per_cpu_start; memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); - highest_cpu = i; } - nr_cpu_ids = highest_cpu + 1; printk(KERN_DEBUG "NR_CPUS: %d, nr_cpu_ids: %d, nr_node_ids %d\n", NR_CPUS, nr_cpu_ids, nr_node_ids); @@ -199,7 +240,7 @@ void __cpuinit numa_set_node(int cpu, int node) { int *cpu_to_node_map = early_per_cpu_ptr(x86_cpu_to_node_map); - if (node != NUMA_NO_NODE) + if (cpu_pda(cpu) && node != NUMA_NO_NODE) cpu_pda(cpu)->nodenumber = node; if (cpu_to_node_map) diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c index aee0e8200777..631ea6cc01d8 100644 --- a/arch/x86/kernel/setup64.c +++ b/arch/x86/kernel/setup64.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -34,9 +35,8 @@ struct boot_params boot_params; cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE; -struct x8664_pda *_cpu_pda[NR_CPUS] __read_mostly; +struct x8664_pda **_cpu_pda __read_mostly; EXPORT_SYMBOL(_cpu_pda); -struct x8664_pda boot_cpu_pda[NR_CPUS] __cacheline_aligned; struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table }; @@ -114,8 +114,10 @@ void pda_init(int cpu) __get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER); if (!pda->irqstackptr) panic("cannot allocate irqstack for cpu %d", cpu); - } + if (pda->nodenumber == 0 && cpu_to_node(cpu) != NUMA_NO_NODE) + pda->nodenumber = cpu_to_node(cpu); + } pda->irqstackptr += IRQSTACKSIZE-64; } diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 036604d3daed..bf0833487455 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -816,6 +816,43 @@ static void __cpuinit do_fork_idle(struct work_struct *work) complete(&c_idle->done); } +/* + * Allocate node local memory for the AP pda. + * + * Must be called after the _cpu_pda pointer table is initialized. + */ +static int __cpuinit get_local_pda(int cpu) +{ + struct x8664_pda *oldpda, *newpda; + unsigned long size = sizeof(struct x8664_pda); + int node = cpu_to_node(cpu); + + if (cpu_pda(cpu) && !cpu_pda(cpu)->in_bootmem) + return 0; + + oldpda = cpu_pda(cpu); + newpda = kmalloc_node(size, GFP_ATOMIC, node); + if (!newpda) { + printk(KERN_ERR "Could not allocate node local PDA " + "for CPU %d on node %d\n", cpu, node); + + if (oldpda) + return 0; /* have a usable pda */ + else + return -1; + } + + if (oldpda) { + memcpy(newpda, oldpda, size); + if (!after_bootmem) + free_bootmem((unsigned long)oldpda, size); + } + + newpda->in_bootmem = 0; + cpu_pda(cpu) = newpda; + return 0; +} + static int __cpuinit do_boot_cpu(int apicid, int cpu) /* * NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad @@ -841,19 +878,11 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu) } /* Allocate node local memory for AP pdas */ - if (cpu_pda(cpu) == &boot_cpu_pda[cpu]) { - struct x8664_pda *newpda, *pda; - int node = cpu_to_node(cpu); - pda = cpu_pda(cpu); - newpda = kmalloc_node(sizeof(struct x8664_pda), GFP_ATOMIC, - node); - if (newpda) { - memcpy(newpda, pda, sizeof(struct x8664_pda)); - cpu_pda(cpu) = newpda; - } else - printk(KERN_ERR - "Could not allocate node local PDA for CPU %d on node %d\n", - cpu, node); + if (cpu > 0) { + boot_error = get_local_pda(cpu); + if (boot_error) + goto restore_state; + /* if can't get pda memory, can't start cpu */ } #endif @@ -972,6 +1001,8 @@ do_rest: } } +restore_state: + if (boot_error) { /* Try to put things back the way they were before ... */ unmap_cpu_to_logical_apicid(cpu); @@ -1347,6 +1378,8 @@ __init void prefill_possible_map(void) for (i = 0; i < possible; i++) cpu_set(i, cpu_possible_map); + + nr_cpu_ids = possible; } static void __ref remove_cpu_from_maps(int cpu) diff --git a/include/asm-x86/pda.h b/include/asm-x86/pda.h index de2ad9ac35a9..b34e9a7cc80b 100644 --- a/include/asm-x86/pda.h +++ b/include/asm-x86/pda.h @@ -22,7 +22,8 @@ struct x8664_pda { offset 40!!! */ #endif char *irqstackptr; - int nodenumber; /* number of current node */ + short nodenumber; /* number of current node (32k max) */ + short in_bootmem; /* pda lives in bootmem */ unsigned int __softirq_pending; unsigned int __nmi_count; /* number of NMI on this CPUs */ short mmu_state; @@ -38,8 +39,7 @@ struct x8664_pda { unsigned irq_spurious_count; } ____cacheline_aligned_in_smp; -extern struct x8664_pda *_cpu_pda[]; -extern struct x8664_pda boot_cpu_pda[]; +extern struct x8664_pda **_cpu_pda; extern void pda_init(int); #define cpu_pda(i) (_cpu_pda[i]) diff --git a/include/linux/mm.h b/include/linux/mm.h index 586a943cab01..0ea48a5af823 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1024,6 +1024,7 @@ extern void mem_init(void); extern void show_mem(void); extern void si_meminfo(struct sysinfo * val); extern void si_meminfo_node(struct sysinfo *val, int nid); +extern int after_bootmem; #ifdef CONFIG_NUMA extern void setup_per_cpu_pageset(void); -- cgit v1.2.3 From cdf060a5d3c2afc7998af94b26a6c5182419e071 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 8 Jul 2008 02:36:40 -0700 Subject: netfilter: cleanup netfilter_ipv6.h userspace header Kernel functions are not for userspace. Signed-off-by: Adrian Bunk Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv6.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index fd50988b83ec..d654873aa25a 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -70,6 +70,8 @@ enum nf_ip6_hook_priorities { NF_IP6_PRI_LAST = INT_MAX, }; +#ifdef __KERNEL__ + #ifdef CONFIG_NETFILTER extern int ip6_route_me_harder(struct sk_buff *skb); extern __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, @@ -82,4 +84,6 @@ static inline int ipv6_netfilter_init(void) { return 0; } static inline void ipv6_netfilter_fini(void) { return; } #endif /* CONFIG_NETFILTER */ +#endif /* __KERNEL__ */ + #endif /*__LINUX_IP6_NETFILTER_H*/ -- cgit v1.2.3 From b9c796783151678d08b1ec1ef410685b2515ba51 Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 8 Jul 2008 02:37:31 -0700 Subject: textsearch: support for case insensitive searching The function textsearch_prepare has a new flag to support case insensitive searching. Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/textsearch.h | 10 +++++----- lib/textsearch.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h index 6f371f24160b..6c34cf28b7aa 100644 --- a/include/linux/textsearch.h +++ b/include/linux/textsearch.h @@ -10,10 +10,8 @@ struct ts_config; -/** - * TS_AUTOLOAD - Automatically load textsearch modules when needed - */ -#define TS_AUTOLOAD 1 +#define TS_AUTOLOAD 1 /* Automatically load textsearch modules when needed */ +#define TS_IGNORECASE 2 /* Searches string case insensitively */ /** * struct ts_state - search state @@ -39,7 +37,7 @@ struct ts_state struct ts_ops { const char *name; - struct ts_config * (*init)(const void *, unsigned int, gfp_t); + struct ts_config * (*init)(const void *, unsigned int, gfp_t, int); unsigned int (*find)(struct ts_config *, struct ts_state *); void (*destroy)(struct ts_config *); @@ -52,12 +50,14 @@ struct ts_ops /** * struct ts_config - search configuration * @ops: operations of chosen algorithm + * @flags: flags * @get_next_block: callback to fetch the next block to search in * @finish: callback to finalize a search */ struct ts_config { struct ts_ops *ops; + int flags; /** * get_next_block - fetch next block of data diff --git a/lib/textsearch.c b/lib/textsearch.c index be8bda3862f5..b451fcc9354c 100644 --- a/lib/textsearch.c +++ b/lib/textsearch.c @@ -54,10 +54,13 @@ * USAGE * * Before a search can be performed, a configuration must be created - * by calling textsearch_prepare() specyfing the searching algorithm and - * the pattern to look for. The returned configuration may then be used - * for an arbitary amount of times and even in parallel as long as a - * separate struct ts_state variable is provided to every instance. + * by calling textsearch_prepare() specifying the searching algorithm, + * the pattern to look for and flags. As a flag, you can set TS_IGNORECASE + * to perform case insensitive matching. But it might slow down + * performance of algorithm, so you should use it at own your risk. + * The returned configuration may then be used for an arbitary + * amount of times and even in parallel as long as a separate struct + * ts_state variable is provided to every instance. * * The actual search is performed by either calling textsearch_find_- * continuous() for linear data or by providing an own get_next_block() @@ -89,7 +92,6 @@ * panic("Oh my god, dancing chickens at %d\n", pos); * * textsearch_destroy(conf); - * * ========================================================================== */ @@ -279,7 +281,7 @@ struct ts_config *textsearch_prepare(const char *algo, const void *pattern, if (ops == NULL) goto errout; - conf = ops->init(pattern, len, gfp_mask); + conf = ops->init(pattern, len, gfp_mask, flags); if (IS_ERR(conf)) { err = PTR_ERR(conf); goto errout; -- cgit v1.2.3 From dde77e604497dada6f224a685278dfb27747ae52 Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 8 Jul 2008 02:38:40 -0700 Subject: textsearch: convert kmalloc + memset to kzalloc convert kmalloc + memset to kzalloc for alloc_ts_config Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/textsearch.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h index 6c34cf28b7aa..d9a85d616385 100644 --- a/include/linux/textsearch.h +++ b/include/linux/textsearch.h @@ -162,11 +162,10 @@ static inline struct ts_config *alloc_ts_config(size_t payload, { struct ts_config *conf; - conf = kmalloc(TS_PRIV_ALIGN(sizeof(*conf)) + payload, gfp_mask); + conf = kzalloc(TS_PRIV_ALIGN(sizeof(*conf)) + payload, gfp_mask); if (conf == NULL) return ERR_PTR(-ENOMEM); - memset(conf, 0, TS_PRIV_ALIGN(sizeof(*conf)) + payload); return conf; } -- cgit v1.2.3 From 4ad3f26162ece5aca3045fd45e15dd99acea4a0e Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 8 Jul 2008 02:38:56 -0700 Subject: netfilter: fix string extension for case insensitive pattern matching The flag XT_STRING_FLAG_IGNORECASE indicates case insensitive string matching. netfilter can find cmd.exe, Cmd.exe, cMd.exe and etc easily. A new revision 1 was added, in the meantime invert of xt_string_info was moved into flags as a flag. If revision is 1, The flag XT_STRING_FLAG_INVERT indicates invert matching. Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/xt_string.h | 15 ++++++++++++++- net/netfilter/xt_string.c | 38 +++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_string.h b/include/linux/netfilter/xt_string.h index bb21dd1aee2d..8a6ba7bbef9f 100644 --- a/include/linux/netfilter/xt_string.h +++ b/include/linux/netfilter/xt_string.h @@ -4,6 +4,11 @@ #define XT_STRING_MAX_PATTERN_SIZE 128 #define XT_STRING_MAX_ALGO_NAME_SIZE 16 +enum { + XT_STRING_FLAG_INVERT = 0x01, + XT_STRING_FLAG_IGNORECASE = 0x02 +}; + struct xt_string_info { u_int16_t from_offset; @@ -11,7 +16,15 @@ struct xt_string_info char algo[XT_STRING_MAX_ALGO_NAME_SIZE]; char pattern[XT_STRING_MAX_PATTERN_SIZE]; u_int8_t patlen; - u_int8_t invert; + union { + struct { + u_int8_t invert; + } v0; + + struct { + u_int8_t flags; + } v1; + } u; /* Used internally by the kernel */ struct ts_config __attribute__((aligned(8))) *config; diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index 72f694d947f4..4903182a062b 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -29,12 +29,16 @@ string_mt(const struct sk_buff *skb, const struct net_device *in, { const struct xt_string_info *conf = matchinfo; struct ts_state state; + int invert; memset(&state, 0, sizeof(struct ts_state)); + invert = (match->revision == 0 ? conf->u.v0.invert : + conf->u.v1.flags & XT_STRING_FLAG_INVERT); + return (skb_find_text((struct sk_buff *)skb, conf->from_offset, conf->to_offset, conf->config, &state) - != UINT_MAX) ^ conf->invert; + != UINT_MAX) ^ invert; } #define STRING_TEXT_PRIV(m) ((struct xt_string_info *)(m)) @@ -46,6 +50,7 @@ string_mt_check(const char *tablename, const void *ip, { struct xt_string_info *conf = matchinfo; struct ts_config *ts_conf; + int flags = TS_AUTOLOAD; /* Damn, can't handle this case properly with iptables... */ if (conf->from_offset > conf->to_offset) @@ -54,8 +59,15 @@ string_mt_check(const char *tablename, const void *ip, return false; if (conf->patlen > XT_STRING_MAX_PATTERN_SIZE) return false; + if (match->revision == 1) { + if (conf->u.v1.flags & + ~(XT_STRING_FLAG_IGNORECASE | XT_STRING_FLAG_INVERT)) + return false; + if (conf->u.v1.flags & XT_STRING_FLAG_IGNORECASE) + flags |= TS_IGNORECASE; + } ts_conf = textsearch_prepare(conf->algo, conf->pattern, conf->patlen, - GFP_KERNEL, TS_AUTOLOAD); + GFP_KERNEL, flags); if (IS_ERR(ts_conf)) return false; @@ -72,6 +84,17 @@ static void string_mt_destroy(const struct xt_match *match, void *matchinfo) static struct xt_match string_mt_reg[] __read_mostly = { { .name = "string", + .revision = 0, + .family = AF_INET, + .checkentry = string_mt_check, + .match = string_mt, + .destroy = string_mt_destroy, + .matchsize = sizeof(struct xt_string_info), + .me = THIS_MODULE + }, + { + .name = "string", + .revision = 1, .family = AF_INET, .checkentry = string_mt_check, .match = string_mt, @@ -81,6 +104,17 @@ static struct xt_match string_mt_reg[] __read_mostly = { }, { .name = "string", + .revision = 0, + .family = AF_INET6, + .checkentry = string_mt_check, + .match = string_mt, + .destroy = string_mt_destroy, + .matchsize = sizeof(struct xt_string_info), + .me = THIS_MODULE + }, + { + .name = "string", + .revision = 1, .family = AF_INET6, .checkentry = string_mt_check, .match = string_mt, -- cgit v1.2.3 From 2c693610fe923764fe41b846fb86938a2010da6e Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Tue, 8 Jul 2008 03:03:01 -0700 Subject: net: remove padding from struct socket on 64bit & increase objects/cache remove padding from struct socket reducing its size by 8 bytes. This allows more objects/cache in sock_inode_cache 12 objects/cache when cacheline size is 128 (generic x86_64) Signed-off-by: Richard Kennedy Signed-off-by: David S. Miller --- include/linux/net.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 71f7dd559285..150a48c68d52 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -106,23 +106,23 @@ enum sock_shutdown_cmd { /** * struct socket - general BSD socket * @state: socket state (%SS_CONNECTED, etc) + * @type: socket type (%SOCK_STREAM, etc) * @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc) * @ops: protocol specific socket operations * @fasync_list: Asynchronous wake up list * @file: File back pointer for gc * @sk: internal networking protocol agnostic socket representation * @wait: wait queue for several uses - * @type: socket type (%SOCK_STREAM, etc) */ struct socket { socket_state state; + short type; unsigned long flags; const struct proto_ops *ops; struct fasync_struct *fasync_list; struct file *file; struct sock *sk; wait_queue_head_t wait; - short type; }; struct vm_area_struct; -- cgit v1.2.3 From acc81e1465d29e0284008770cc4b8bc90bd93bd7 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:21:27 -0700 Subject: vlan: fix network_header/mac_header adjustments Lennert Buytenhek points out that the VLAN code incorrectly adjusts skb->network_header to point in the middle of the VLAN header and additionally tries to adjust skb->mac_header without checking for validity. The network_header should not be touched at all since we're only adding headers in front of it, mac_header adjustments are not necessary at all. Based on patch by Lennert Buytenhek . Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 2 -- net/8021q/vlan_dev.c | 1 - 2 files changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 5190452ac7dc..8f5bf9b676aa 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -279,8 +279,6 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short veth->h_vlan_TCI = htons(tag); skb->protocol = htons(ETH_P_8021Q); - skb->mac_header -= VLAN_HLEN; - skb->network_header -= VLAN_HLEN; return skb; } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a0617bf7cec6..4a8525927c27 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -308,7 +308,6 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, vhdr->h_vlan_encapsulated_proto = htons(len); skb->protocol = htons(ETH_P_8021Q); - skb_reset_network_header(skb); } /* Before delegating work to the lower layer, enter our MAC-address */ -- cgit v1.2.3 From 7750f403cbe56971336d575b354365190b4e3227 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:23:36 -0700 Subject: vlan: uninline __vlan_hwaccel_rx The function is huge and included at least once in every VLAN acceleration capable driver. Uninline it; to avoid having drivers depend on the VLAN module, the function is always built in statically when VLAN is enabled. With all VLAN acceleration capable drivers that build on x86_64 enabled, this results in: text data bss dec hex filename 6515227 854044 343968 7713239 75b1d7 vmlinux.inlined 6505637 854044 343968 7703649 758c61 vmlinux.uninlined ---------------------------------------------------------- -9590 Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 64 +++++++------------------------------------------ net/8021q/Makefile | 9 +++---- net/8021q/vlan.h | 8 +++++++ net/8021q/vlan_core.c | 48 +++++++++++++++++++++++++++++++++++++ net/Makefile | 4 +++- 5 files changed, 72 insertions(+), 61 deletions(-) create mode 100644 net/8021q/vlan_core.c (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8f5bf9b676aa..594cd35b0074 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -150,15 +150,6 @@ static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) return netdev_priv(dev); } -/* inline functions */ -static inline __u32 vlan_get_ingress_priority(struct net_device *dev, - unsigned short vlan_tag) -{ - struct vlan_dev_info *vip = vlan_dev_info(dev); - - return vip->ingress_priority_map[(vlan_tag >> 13) & 0x7]; -} - /* VLAN tx hw acceleration helpers. */ struct vlan_skb_tx_cookie { u32 magic; @@ -171,56 +162,17 @@ struct vlan_skb_tx_cookie { (VLAN_TX_SKB_CB(__skb)->magic == VLAN_TX_COOKIE_MAGIC) #define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag) -/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */ -static inline int __vlan_hwaccel_rx(struct sk_buff *skb, - struct vlan_group *grp, +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, + unsigned short vlan_tag, int polling); +#else +static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, unsigned short vlan_tag, int polling) { - struct net_device_stats *stats; - - if (skb_bond_should_drop(skb)) { - dev_kfree_skb_any(skb); - return NET_RX_DROP; - } - - skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK); - if (skb->dev == NULL) { - dev_kfree_skb_any(skb); - - /* Not NET_RX_DROP, this is not being dropped - * due to congestion. - */ - return 0; - } - - skb->dev->last_rx = jiffies; - - stats = &skb->dev->stats; - stats->rx_packets++; - stats->rx_bytes += skb->len; - - skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag); - switch (skb->pkt_type) { - case PACKET_BROADCAST: - break; - - case PACKET_MULTICAST: - stats->multicast++; - break; - - case PACKET_OTHERHOST: - /* Our lower layer thinks this is not local, let's make sure. - * This allows the VLAN to have a different MAC than the underlying - * device, and still route correctly. - */ - if (!compare_ether_addr(eth_hdr(skb)->h_dest, - skb->dev->dev_addr)) - skb->pkt_type = PACKET_HOST; - break; - }; - - return (polling ? netif_receive_skb(skb) : netif_rx(skb)); + BUG(); + return NET_XMIT_SUCCESS; } +#endif static inline int vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, diff --git a/net/8021q/Makefile b/net/8021q/Makefile index 3006e9ed7b08..9f4f174ead1c 100644 --- a/net/8021q/Makefile +++ b/net/8021q/Makefile @@ -1,9 +1,10 @@ # # Makefile for the Linux VLAN layer. # +obj-$(subst m,y,$(CONFIG_VLAN_8021Q)) += vlan_core.o +obj-$(CONFIG_VLAN_8021Q) += 8021q.o -obj-$(CONFIG_VLAN_8021Q) += 8021q.o +8021q-y := vlan.o vlan_dev.o vlan_netlink.o +8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o +8021q-$(CONFIG_PROC_FS) += vlanproc.o -8021q-y := vlan.o vlan_dev.o vlan_netlink.o -8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o -8021q-$(CONFIG_PROC_FS) += vlanproc.o diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 097b2e04c928..7cc1a97c42fc 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -37,6 +37,14 @@ void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev); +static inline u32 vlan_get_ingress_priority(struct net_device *dev, + unsigned short vlan_tag) +{ + struct vlan_dev_info *vip = vlan_dev_info(dev); + + return vip->ingress_priority_map[(vlan_tag >> 13) & 0x7]; +} + #ifdef CONFIG_VLAN_8021Q_GVRP extern int vlan_gvrp_request_join(const struct net_device *dev); extern void vlan_gvrp_request_leave(const struct net_device *dev); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c new file mode 100644 index 000000000000..85c94edb000f --- /dev/null +++ b/net/8021q/vlan_core.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "vlan.h" + +/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */ +int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, + unsigned short vlan_tag, int polling) +{ + struct net_device_stats *stats; + + if (skb_bond_should_drop(skb)) { + dev_kfree_skb_any(skb); + return NET_RX_DROP; + } + + skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK); + if (skb->dev == NULL) { + dev_kfree_skb_any(skb); + /* Not NET_RX_DROP, this is not being dropped + * due to congestion. */ + return NET_RX_SUCCESS; + } + skb->dev->last_rx = jiffies; + + stats = &skb->dev->stats; + stats->rx_packets++; + stats->rx_bytes += skb->len; + + skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag); + switch (skb->pkt_type) { + case PACKET_BROADCAST: + break; + case PACKET_MULTICAST: + stats->multicast++; + break; + case PACKET_OTHERHOST: + /* Our lower layer thinks this is not local, let's make sure. + * This allows the VLAN to have a different MAC than the + * underlying device, and still route correctly. */ + if (!compare_ether_addr(eth_hdr(skb)->h_dest, + skb->dev->dev_addr)) + skb->pkt_type = PACKET_HOST; + break; + }; + return (polling ? netif_receive_skb(skb) : netif_rx(skb)); +} +EXPORT_SYMBOL(__vlan_hwaccel_rx); diff --git a/net/Makefile b/net/Makefile index b7a13643b549..4f43e7f874f3 100644 --- a/net/Makefile +++ b/net/Makefile @@ -42,7 +42,9 @@ obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ -obj-$(CONFIG_VLAN_8021Q) += 8021q/ +ifneq ($(CONFIG_VLAN_8021Q),) +obj-y += 8021q/ +endif obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-y += wireless/ -- cgit v1.2.3 From 22d1ba74bbafa96d3f425cc12714d3fe8675183f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:23:57 -0700 Subject: vlan: move struct vlan_dev_info to private header Hide struct vlan_dev_info from drivers to prevent them from growing more creative ways to use it. Provide accessors for the two drivers that currently use it. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- drivers/net/cxgb3/l2t.c | 2 +- drivers/s390/net/qeth_l3_main.c | 4 +-- include/linux/if_vlan.h | 56 +++++++++++------------------------------ net/8021q/vlan.c | 4 +-- net/8021q/vlan.h | 50 +++++++++++++++++++++++++++++++++++- net/8021q/vlan_core.c | 12 +++++++++ net/8021q/vlan_dev.c | 5 ---- 7 files changed, 80 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c index f510140885ae..825e510bd9ed 100644 --- a/drivers/net/cxgb3/l2t.c +++ b/drivers/net/cxgb3/l2t.c @@ -337,7 +337,7 @@ struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh, atomic_set(&e->refcnt, 1); neigh_replace(e, neigh); if (neigh->dev->priv_flags & IFF_802_1Q_VLAN) - e->vlan = vlan_dev_info(neigh->dev)->vlan_id; + e->vlan = vlan_dev_vlan_id(neigh->dev); else e->vlan = VLAN_NONE; spin_unlock(&e->lock); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 999552c83bbe..85be40abdda9 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2020,7 +2020,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev, } } - if (rc && !(netdev_priv(vlan_dev_info(dev)->real_dev) == (void *)card)) + if (rc && !(netdev_priv(vlan_dev_real_dev(dev)) == (void *)card)) return 0; return rc; @@ -2056,7 +2056,7 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) if (rc == QETH_REAL_CARD) card = netdev_priv(dev); else if (rc == QETH_VLAN_CARD) - card = netdev_priv(vlan_dev_info(dev)->real_dev); + card = netdev_priv(vlan_dev_real_dev(dev)); if (card && card->options.layer2) card = NULL; QETH_DBF_TEXT_(TRACE, 4, "%d", rc); diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 594cd35b0074..cb2e6b480882 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -109,47 +109,6 @@ static inline void vlan_group_set_device(struct vlan_group *vg, array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] = dev; } -struct vlan_priority_tci_mapping { - u32 priority; - unsigned short vlan_qos; /* This should be shifted when first set, so we only do it - * at provisioning time. - * ((skb->priority << 13) & 0xE000) - */ - struct vlan_priority_tci_mapping *next; -}; - -/* Holds information that makes sense if this device is a VLAN device. */ -struct vlan_dev_info { - /** This will be the mapping that correlates skb->priority to - * 3 bits of VLAN QOS tags... - */ - unsigned int nr_ingress_mappings; - u32 ingress_priority_map[8]; - - unsigned int nr_egress_mappings; - struct vlan_priority_tci_mapping *egress_priority_map[16]; /* hash table */ - - unsigned short vlan_id; /* The VLAN Identifier for this interface. */ - unsigned short flags; /* (1 << 0) re_order_header This option will cause the - * VLAN code to move around the ethernet header on - * ingress to make the skb look **exactly** like it - * came in from an ethernet port. This destroys some of - * the VLAN information in the skb, but it fixes programs - * like DHCP that use packet-filtering and don't understand - * 802.1Q - */ - struct net_device *real_dev; /* the underlying device/interface */ - unsigned char real_dev_addr[ETH_ALEN]; - struct proc_dir_entry *dent; /* Holds the proc data */ - unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */ - unsigned long cnt_encap_on_xmit; /* How many times did we have to encapsulate the skb on TX. */ -}; - -static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) -{ - return netdev_priv(dev); -} - /* VLAN tx hw acceleration helpers. */ struct vlan_skb_tx_cookie { u32 magic; @@ -163,9 +122,24 @@ struct vlan_skb_tx_cookie { #define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag) #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); +extern u16 vlan_dev_vlan_id(const struct net_device *dev); + extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, unsigned short vlan_tag, int polling); #else +static inline struct net_device *vlan_dev_real_dev(const struct net_device *dev) +{ + BUG(); + return NULL; +} + +static inline u16 vlan_dev_vlan_id(const struct net_device *dev) +{ + BUG(); + return 0; +} + static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, unsigned short vlan_tag, int polling) { diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 8141e2dc510b..7a2625d2f9a0 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -543,7 +543,6 @@ static struct notifier_block vlan_notifier_block __read_mostly = { static int vlan_ioctl_handler(struct net *net, void __user *arg) { int err; - unsigned short vid = 0; struct vlan_ioctl_args args; struct net_device *dev = NULL; @@ -644,8 +643,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) case GET_VLAN_VID_CMD: err = 0; - vlan_dev_get_vid(dev, &vid); - args.u.VID = vid; + args.u.VID = vlan_dev_vlan_id(dev); if (copy_to_user(arg, &args, sizeof(struct vlan_ioctl_args))) err = -EFAULT; diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 7cc1a97c42fc..14c421e033f2 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -3,6 +3,55 @@ #include + +/** + * struct vlan_priority_tci_mapping - vlan egress priority mappings + * @priority: skb priority + * @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000 + * @next: pointer to next struct + */ +struct vlan_priority_tci_mapping { + u32 priority; + unsigned short vlan_qos; + struct vlan_priority_tci_mapping *next; +}; + +/** + * struct vlan_dev_info - VLAN private device data + * @nr_ingress_mappings: number of ingress priority mappings + * @ingress_priority_map: ingress priority mappings + * @nr_egress_mappings: number of egress priority mappings + * @egress_priority_map: hash of egress priority mappings + * @vlan_id: VLAN identifier + * @flags: device flags + * @real_dev: underlying netdevice + * @real_dev_addr: address of underlying netdevice + * @dent: proc dir entry + * @cnt_inc_headroom_on_tx: statistic - number of skb expansions on TX + * @cnt_encap_on_xmit: statistic - number of skb encapsulations on TX + */ +struct vlan_dev_info { + unsigned int nr_ingress_mappings; + u32 ingress_priority_map[8]; + unsigned int nr_egress_mappings; + struct vlan_priority_tci_mapping *egress_priority_map[16]; + + unsigned short vlan_id; + unsigned short flags; + + struct net_device *real_dev; + unsigned char real_dev_addr[ETH_ALEN]; + + struct proc_dir_entry *dent; + unsigned long cnt_inc_headroom_on_tx; + unsigned long cnt_encap_on_xmit; +}; + +static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) +{ + return netdev_priv(dev); +} + #define VLAN_GRP_HASH_SHIFT 5 #define VLAN_GRP_HASH_SIZE (1 << VLAN_GRP_HASH_SHIFT) #define VLAN_GRP_HASH_MASK (VLAN_GRP_HASH_SIZE - 1) @@ -30,7 +79,6 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, short vlan_prio); int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask); void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); -void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result); int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); void vlan_setup(struct net_device *dev); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 85c94edb000f..f980b9154cc3 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -46,3 +46,15 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return (polling ? netif_receive_skb(skb) : netif_rx(skb)); } EXPORT_SYMBOL(__vlan_hwaccel_rx); + +struct net_device *vlan_dev_real_dev(const struct net_device *dev) +{ + return vlan_dev_info(dev)->real_dev; +} +EXPORT_SYMBOL_GPL(vlan_dev_real_dev); + +u16 vlan_dev_vlan_id(const struct net_device *dev) +{ + return vlan_dev_info(dev)->vlan_id; +} +EXPORT_SYMBOL_GPL(vlan_dev_vlan_id); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 722697d31e4f..2aab294c5744 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -531,11 +531,6 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) strncpy(result, vlan_dev_info(dev)->real_dev->name, 23); } -void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result) -{ - *result = vlan_dev_info(dev)->vlan_id; -} - static int vlan_dev_open(struct net_device *dev) { struct vlan_dev_info *vlan = vlan_dev_info(dev); -- cgit v1.2.3 From df6b6a0cf62afeacdeb4c1a35b8fba21fda54399 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:24:14 -0700 Subject: vlan: remove useless struct hlist_node declaration from if_vlan.h Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index cb2e6b480882..8e68b05b13df 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -14,10 +14,6 @@ #define _LINUX_IF_VLAN_H_ #ifdef __KERNEL__ - -/* externally defined structs */ -struct hlist_node; - #include #include -- cgit v1.2.3 From 9bb8582efb555521c7eec595ebd34e835ddc34b8 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:24:44 -0700 Subject: vlan: TCI related type and naming cleanups The VLAN code contains multiple spots that use tag, id and tci as identifiers for arguments and variables incorrectly and they actually contain or are expected to contain something different. Additionally types are used inconsistently (unsigned short vs u16) and identifiers are sometimes capitalized. - consistently use u16 for storing TCI, ID or QoS values - consistently use vlan_id and vlan_tci for storing the respective values - remove capitalization - add kdoc comment to netif_hwaccel_{rx,receive_skb} Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 84 ++++++++++++++++++++++++++++--------------------- net/8021q/vlan.c | 34 ++++++++++---------- net/8021q/vlan.h | 19 ++++++----- net/8021q/vlan_core.c | 6 ++-- net/8021q/vlan_dev.c | 47 ++++++++++++++------------- net/8021q/vlan_gvrp.c | 8 ++--- 6 files changed, 103 insertions(+), 95 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8e68b05b13df..d36515dae62f 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -87,7 +87,7 @@ struct vlan_group { }; static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, - unsigned int vlan_id) + u16 vlan_id) { struct net_device **array; array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN]; @@ -95,7 +95,7 @@ static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, } static inline void vlan_group_set_device(struct vlan_group *vg, - unsigned int vlan_id, + u16 vlan_id, struct net_device *dev) { struct net_device **array; @@ -122,7 +122,7 @@ extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); extern u16 vlan_dev_vlan_id(const struct net_device *dev); extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag, int polling); + u16 vlan_tci, int polling); #else static inline struct net_device *vlan_dev_real_dev(const struct net_device *dev) { @@ -137,39 +137,51 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev) } static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag, int polling) + u16 vlan_tci, int polling) { BUG(); return NET_XMIT_SUCCESS; } #endif +/** + * vlan_hwaccel_rx - netif_rx wrapper for VLAN RX acceleration + * @skb: buffer + * @grp: vlan group + * @vlan_tci: VLAN TCI as received from the card + */ static inline int vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag) + u16 vlan_tci) { - return __vlan_hwaccel_rx(skb, grp, vlan_tag, 0); + return __vlan_hwaccel_rx(skb, grp, vlan_tci, 0); } +/** + * vlan_hwaccel_receive_skb - netif_receive_skb wrapper for VLAN RX acceleration + * @skb: buffer + * @grp: vlan group + * @vlan_tci: VLAN TCI as received from the card + */ static inline int vlan_hwaccel_receive_skb(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag) + u16 vlan_tci) { - return __vlan_hwaccel_rx(skb, grp, vlan_tag, 1); + return __vlan_hwaccel_rx(skb, grp, vlan_tci, 1); } /** * __vlan_put_tag - regular VLAN tag inserting * @skb: skbuff to tag - * @tag: VLAN tag to insert + * @vlan_tci: VLAN TCI to insert * * Inserts the VLAN tag into @skb as part of the payload * Returns a VLAN tagged skb. If a new skb is created, @skb is freed. - * + * * Following the skb_unshare() example, in case of error, the calling function * doesn't have to worry about freeing the original skb. */ -static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short tag) +static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; @@ -197,8 +209,8 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short /* first, the ethernet type */ veth->h_vlan_proto = htons(ETH_P_8021Q); - /* now, the tag */ - veth->h_vlan_TCI = htons(tag); + /* now, the TCI */ + veth->h_vlan_TCI = htons(vlan_tci); skb->protocol = htons(ETH_P_8021Q); @@ -208,17 +220,18 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short /** * __vlan_hwaccel_put_tag - hardware accelerated VLAN inserting * @skb: skbuff to tag - * @tag: VLAN tag to insert + * @vlan_tci: VLAN TCI to insert * - * Puts the VLAN tag in @skb->cb[] and lets the device do the rest + * Puts the VLAN TCI in @skb->cb[] and lets the device do the rest */ -static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, unsigned short tag) +static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, + u16 vlan_tci) { struct vlan_skb_tx_cookie *cookie; cookie = VLAN_TX_SKB_CB(skb); cookie->magic = VLAN_TX_COOKIE_MAGIC; - cookie->vlan_tag = tag; + cookie->vlan_tag = vlan_tci; return skb; } @@ -228,28 +241,28 @@ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, unsign /** * vlan_put_tag - inserts VLAN tag according to device features * @skb: skbuff to tag - * @tag: VLAN tag to insert + * @vlan_tci: VLAN TCI to insert * * Assumes skb->dev is the target that will xmit this frame. * Returns a VLAN tagged skb. */ -static inline struct sk_buff *vlan_put_tag(struct sk_buff *skb, unsigned short tag) +static inline struct sk_buff *vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) { if (skb->dev->features & NETIF_F_HW_VLAN_TX) { - return __vlan_hwaccel_put_tag(skb, tag); + return __vlan_hwaccel_put_tag(skb, vlan_tci); } else { - return __vlan_put_tag(skb, tag); + return __vlan_put_tag(skb, vlan_tci); } } /** * __vlan_get_tag - get the VLAN ID that is part of the payload * @skb: skbuff to query - * @tag: buffer to store vlaue - * + * @vlan_tci: buffer to store vlaue + * * Returns error if the skb is not of VLAN type */ -static inline int __vlan_get_tag(const struct sk_buff *skb, unsigned short *tag) +static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) { struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data; @@ -257,29 +270,28 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, unsigned short *tag) return -EINVAL; } - *tag = ntohs(veth->h_vlan_TCI); - + *vlan_tci = ntohs(veth->h_vlan_TCI); return 0; } /** * __vlan_hwaccel_get_tag - get the VLAN ID that is in @skb->cb[] * @skb: skbuff to query - * @tag: buffer to store vlaue - * + * @vlan_tci: buffer to store vlaue + * * Returns error if @skb->cb[] is not set correctly */ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb, - unsigned short *tag) + u16 *vlan_tci) { struct vlan_skb_tx_cookie *cookie; cookie = VLAN_TX_SKB_CB(skb); if (cookie->magic == VLAN_TX_COOKIE_MAGIC) { - *tag = cookie->vlan_tag; + *vlan_tci = cookie->vlan_tag; return 0; } else { - *tag = 0; + *vlan_tci = 0; return -EINVAL; } } @@ -289,16 +301,16 @@ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb, /** * vlan_get_tag - get the VLAN ID from the skb * @skb: skbuff to query - * @tag: buffer to store vlaue - * + * @vlan_tci: buffer to store vlaue + * * Returns error if the skb is not VLAN tagged */ -static inline int vlan_get_tag(const struct sk_buff *skb, unsigned short *tag) +static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) { if (skb->dev->features & NETIF_F_HW_VLAN_TX) { - return __vlan_hwaccel_get_tag(skb, tag); + return __vlan_hwaccel_get_tag(skb, vlan_tci); } else { - return __vlan_get_tag(skb, tag); + return __vlan_get_tag(skb, vlan_tci); } } diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 7a2625d2f9a0..68bdcf4a795c 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -83,13 +83,12 @@ static struct vlan_group *__vlan_find_group(struct net_device *real_dev) * * Must be invoked with RCU read lock (no preempt) */ -struct net_device *__find_vlan_dev(struct net_device *real_dev, - unsigned short VID) +struct net_device *__find_vlan_dev(struct net_device *real_dev, u16 vlan_id) { struct vlan_group *grp = __vlan_find_group(real_dev); if (grp) - return vlan_group_get_device(grp, VID); + return vlan_group_get_device(grp, vlan_id); return NULL; } @@ -117,14 +116,14 @@ static struct vlan_group *vlan_group_alloc(struct net_device *real_dev) return grp; } -static int vlan_group_prealloc_vid(struct vlan_group *vg, int vid) +static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) { struct net_device **array; unsigned int size; ASSERT_RTNL(); - array = vg->vlan_devices_arrays[vid / VLAN_GROUP_ARRAY_PART_LEN]; + array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN]; if (array != NULL) return 0; @@ -133,7 +132,7 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, int vid) if (array == NULL) return -ENOBUFS; - vg->vlan_devices_arrays[vid / VLAN_GROUP_ARRAY_PART_LEN] = array; + vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN] = array; return 0; } @@ -147,7 +146,7 @@ void unregister_vlan_dev(struct net_device *dev) struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; struct vlan_group *grp; - unsigned short vlan_id = vlan->vlan_id; + u16 vlan_id = vlan->vlan_id; ASSERT_RTNL(); @@ -205,7 +204,7 @@ static void vlan_transfer_operstate(const struct net_device *dev, } } -int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id) +int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) { char *name = real_dev->name; @@ -242,7 +241,7 @@ int register_vlan_dev(struct net_device *dev) { struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; - unsigned short vlan_id = vlan->vlan_id; + u16 vlan_id = vlan->vlan_id; struct vlan_group *grp, *ngrp = NULL; int err; @@ -295,8 +294,7 @@ out_free_group: /* Attach a VLAN device to a mac address (ie Ethernet Card). * Returns 0 if the device was created or a negative error code otherwise. */ -static int register_vlan_device(struct net_device *real_dev, - unsigned short VLAN_ID) +static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) { struct net_device *new_dev; struct net *net = dev_net(real_dev); @@ -304,10 +302,10 @@ static int register_vlan_device(struct net_device *real_dev, char name[IFNAMSIZ]; int err; - if (VLAN_ID >= VLAN_VID_MASK) + if (vlan_id >= VLAN_VID_MASK) return -ERANGE; - err = vlan_check_real_dev(real_dev, VLAN_ID); + err = vlan_check_real_dev(real_dev, vlan_id); if (err < 0) return err; @@ -315,26 +313,26 @@ static int register_vlan_device(struct net_device *real_dev, switch (vn->name_type) { case VLAN_NAME_TYPE_RAW_PLUS_VID: /* name will look like: eth1.0005 */ - snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID); + snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: vlan5 */ - snprintf(name, IFNAMSIZ, "vlan%i", VLAN_ID); + snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); break; case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: eth0.5 */ - snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, VLAN_ID); + snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID: /* Put our vlan.VID in the name. * Name will look like: vlan0005 */ default: - snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID); + snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); } new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name, @@ -349,7 +347,7 @@ static int register_vlan_device(struct net_device *real_dev, */ new_dev->mtu = real_dev->mtu; - vlan_dev_info(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */ + vlan_dev_info(new_dev)->vlan_id = vlan_id; vlan_dev_info(new_dev)->real_dev = real_dev; vlan_dev_info(new_dev)->dent = NULL; vlan_dev_info(new_dev)->flags = VLAN_FLAG_REORDER_HDR; diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 14c421e033f2..a6603a4d917f 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -12,7 +12,7 @@ */ struct vlan_priority_tci_mapping { u32 priority; - unsigned short vlan_qos; + u16 vlan_qos; struct vlan_priority_tci_mapping *next; }; @@ -36,8 +36,8 @@ struct vlan_dev_info { unsigned int nr_egress_mappings; struct vlan_priority_tci_mapping *egress_priority_map[16]; - unsigned short vlan_id; - unsigned short flags; + u16 vlan_id; + u16 flags; struct net_device *real_dev; unsigned char real_dev_addr[ETH_ALEN]; @@ -67,30 +67,29 @@ static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) * Must be invoked with rcu_read_lock (ie preempt disabled) * or with RTNL. */ -struct net_device *__find_vlan_dev(struct net_device *real_dev, - unsigned short VID); /* vlan.c */ +struct net_device *__find_vlan_dev(struct net_device *real_dev, u16 vlan_id); /* found in vlan_dev.c */ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev); void vlan_dev_set_ingress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio); + u32 skb_prio, u16 vlan_prio); int vlan_dev_set_egress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio); + u32 skb_prio, u16 vlan_prio); int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask); void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); -int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); +int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id); void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev); static inline u32 vlan_get_ingress_priority(struct net_device *dev, - unsigned short vlan_tag) + u16 vlan_tci) { struct vlan_dev_info *vip = vlan_dev_info(dev); - return vip->ingress_priority_map[(vlan_tag >> 13) & 0x7]; + return vip->ingress_priority_map[(vlan_tci >> 13) & 0x7]; } #ifdef CONFIG_VLAN_8021Q_GVRP diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index f980b9154cc3..68df12d3664b 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -5,7 +5,7 @@ /* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag, int polling) + u16 vlan_tci, int polling) { struct net_device_stats *stats; @@ -14,7 +14,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return NET_RX_DROP; } - skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK); + skb->dev = vlan_group_get_device(grp, vlan_tci & VLAN_VID_MASK); if (skb->dev == NULL) { dev_kfree_skb_any(skb); /* Not NET_RX_DROP, this is not being dropped @@ -27,7 +27,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, stats->rx_packets++; stats->rx_bytes += skb->len; - skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag); + skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); switch (skb->pkt_type) { case PACKET_BROADCAST: break; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 2aab294c5744..2ccac6bea57e 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -150,9 +150,9 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { struct vlan_hdr *vhdr; - unsigned short vid; struct net_device_stats *stats; - unsigned short vlan_TCI; + u16 vlan_id; + u16 vlan_tci; skb = skb_share_check(skb, GFP_ATOMIC); if (skb == NULL) @@ -162,14 +162,14 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, goto err_free; vhdr = (struct vlan_hdr *)skb->data; - vlan_TCI = ntohs(vhdr->h_vlan_TCI); - vid = (vlan_TCI & VLAN_VID_MASK); + vlan_tci = ntohs(vhdr->h_vlan_TCI); + vlan_id = vlan_tci & VLAN_VID_MASK; rcu_read_lock(); - skb->dev = __find_vlan_dev(dev, vid); + skb->dev = __find_vlan_dev(dev, vlan_id); if (!skb->dev) { pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n", - __func__, (unsigned int)vid, dev->name); + __func__, vlan_id, dev->name); goto err_unlock; } @@ -181,11 +181,10 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, skb_pull_rcsum(skb, VLAN_HLEN); - skb->priority = vlan_get_ingress_priority(skb->dev, - ntohs(vhdr->h_vlan_TCI)); + skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); pr_debug("%s: priority: %u for TCI: %hu\n", - __func__, skb->priority, ntohs(vhdr->h_vlan_TCI)); + __func__, skb->priority, vlan_tci); switch (skb->pkt_type) { case PACKET_BROADCAST: /* Yeah, stats collect these together.. */ @@ -228,7 +227,7 @@ err_free: return NET_RX_DROP; } -static inline unsigned short +static inline u16 vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) { struct vlan_priority_tci_mapping *mp; @@ -260,7 +259,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned int len) { struct vlan_hdr *vhdr; - unsigned short veth_TCI = 0; + u16 vlan_tci = 0; int rc = 0; int build_vlan_header = 0; struct net_device *vdev = dev; @@ -292,10 +291,10 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, * VLAN ID 12 bits (low bits) * */ - veth_TCI = vlan_dev_info(dev)->vlan_id; - veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); + vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); - vhdr->h_vlan_TCI = htons(veth_TCI); + vhdr->h_vlan_TCI = htons(vlan_tci); /* * Set the protocol type. For a packet of type ETH_P_802_3 we @@ -373,7 +372,7 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (veth->h_vlan_proto != htons(ETH_P_8021Q) || vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR) { int orig_headroom = skb_headroom(skb); - unsigned short veth_TCI; + u16 vlan_tci; /* This is not a VLAN frame...but we can fix that! */ vlan_dev_info(dev)->cnt_encap_on_xmit++; @@ -386,10 +385,10 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * CFI 1 bit * VLAN ID 12 bits (low bits) */ - veth_TCI = vlan_dev_info(dev)->vlan_id; - veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); + vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); - skb = __vlan_put_tag(skb, veth_TCI); + skb = __vlan_put_tag(skb, vlan_tci); if (!skb) { stats->tx_dropped++; return 0; @@ -422,7 +421,7 @@ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = &dev->stats; - unsigned short veth_TCI; + u16 vlan_tci; /* Construct the second two bytes. This field looks something * like: @@ -430,9 +429,9 @@ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, * CFI 1 bit * VLAN ID 12 bits (low bits) */ - veth_TCI = vlan_dev_info(dev)->vlan_id; - veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); - skb = __vlan_hwaccel_put_tag(skb, veth_TCI); + vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); + skb = __vlan_hwaccel_put_tag(skb, vlan_tci); stats->tx_packets++; stats->tx_bytes += skb->len; @@ -457,7 +456,7 @@ static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) } void vlan_dev_set_ingress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio) + u32 skb_prio, u16 vlan_prio) { struct vlan_dev_info *vlan = vlan_dev_info(dev); @@ -470,7 +469,7 @@ void vlan_dev_set_ingress_priority(const struct net_device *dev, } int vlan_dev_set_egress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio) + u32 skb_prio, u16 vlan_prio) { struct vlan_dev_info *vlan = vlan_dev_info(dev); struct vlan_priority_tci_mapping *mp = NULL; diff --git a/net/8021q/vlan_gvrp.c b/net/8021q/vlan_gvrp.c index db9781608362..061ceceeef12 100644 --- a/net/8021q/vlan_gvrp.c +++ b/net/8021q/vlan_gvrp.c @@ -30,19 +30,19 @@ static struct garp_application vlan_gvrp_app __read_mostly = { int vlan_gvrp_request_join(const struct net_device *dev) { const struct vlan_dev_info *vlan = vlan_dev_info(dev); - __be16 vid = htons(vlan->vlan_id); + __be16 vlan_id = htons(vlan->vlan_id); return garp_request_join(vlan->real_dev, &vlan_gvrp_app, - &vid, sizeof(vid), GVRP_ATTR_VID); + &vlan_id, sizeof(vlan_id), GVRP_ATTR_VID); } void vlan_gvrp_request_leave(const struct net_device *dev) { const struct vlan_dev_info *vlan = vlan_dev_info(dev); - __be16 vid = htons(vlan->vlan_id); + __be16 vlan_id = htons(vlan->vlan_id); garp_request_leave(vlan->real_dev, &vlan_gvrp_app, - &vid, sizeof(vid), GVRP_ATTR_VID); + &vlan_id, sizeof(vlan_id), GVRP_ATTR_VID); } int vlan_gvrp_init_applicant(struct net_device *dev) -- cgit v1.2.3 From a7bf0bd5e6af7fe69342dabf2a3b721f0163469a Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 May 2008 15:02:14 +0100 Subject: build: add __page_aligned_data and __page_aligned_bss Making a variable page-aligned by using __attribute__((section(".data.page_aligned"))) is fragile because if sizeof(variable) is not also a multiple of page size, it leaves variables in the remainder of the section unaligned. This patch introduces two new qualifiers, __page_aligned_data and __page_aligned_bss to set the section *and* the alignment of variables. This makes page-aligned variables more robust because the linker will make sure they're aligned properly. Unfortunately it requires *all* page-aligned data to use these macros... Signed-off-by: Ingo Molnar --- arch/x86/kernel/setup64.c | 2 +- arch/x86/mm/ioremap.c | 3 +-- include/linux/linkage.h | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c index 631ea6cc01d8..fc1a56da8240 100644 --- a/arch/x86/kernel/setup64.c +++ b/arch/x86/kernel/setup64.c @@ -40,7 +40,7 @@ EXPORT_SYMBOL(_cpu_pda); struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table }; -char boot_cpu_stack[IRQSTACKSIZE] __attribute__((section(".bss.page_aligned"))); +char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss; unsigned long __supported_pte_mask __read_mostly = ~0UL; EXPORT_SYMBOL_GPL(__supported_pte_mask); diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 416ea415f5c2..0561fde56a51 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -394,8 +394,7 @@ static int __init early_ioremap_debug_setup(char *str) early_param("early_ioremap_debug", early_ioremap_debug_setup); static __initdata int after_paging_init; -static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] - __section(.bss.page_aligned); +static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] __page_aligned_bss; static inline pmd_t * __init early_ioremap_pmd(unsigned long addr) { diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 2119610b24f8..9fd1f859021b 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -1,6 +1,7 @@ #ifndef _LINUX_LINKAGE_H #define _LINUX_LINKAGE_H +#include #include #ifdef __cplusplus @@ -17,6 +18,9 @@ # define asmregparm #endif +#define __page_aligned_data __section(.data.page_aligned) __aligned(PAGE_SIZE) +#define __page_aligned_bss __section(.bss.page_aligned) __aligned(PAGE_SIZE) + /* * This is used by architectures to keep arguments on the stack * untouched by the compiler by keeping them live until the end. -- cgit v1.2.3 From d52d53b8a5b258bfaab9223a5e7284fcfdd48577 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 16 Jun 2008 20:10:55 -0700 Subject: RFC x86: try to remove arch_get_ram_range want to remove arch_get_ram_range, and use early_node_map instead. Signed-off-by: Yinghai Lu Signed-off-by: Ingo Molnar --- arch/x86/mm/init_32.c | 6 ++++-- drivers/pci/intel-iommu.c | 51 ++++++++++++++++++++++++++++++++++------------- include/linux/mm.h | 2 +- mm/page_alloc.c | 10 +++++++--- 4 files changed, 49 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 65d55056b6e7..a0484adbf59d 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -298,7 +298,7 @@ struct add_highpages_data { unsigned long end_pfn; }; -static void __init add_highpages_work_fn(unsigned long start_pfn, +static int __init add_highpages_work_fn(unsigned long start_pfn, unsigned long end_pfn, void *datax) { int node_pfn; @@ -311,7 +311,7 @@ static void __init add_highpages_work_fn(unsigned long start_pfn, final_start_pfn = max(start_pfn, data->start_pfn); final_end_pfn = min(end_pfn, data->end_pfn); if (final_start_pfn >= final_end_pfn) - return; + return 0; for (node_pfn = final_start_pfn; node_pfn < final_end_pfn; node_pfn++) { @@ -321,6 +321,8 @@ static void __init add_highpages_work_fn(unsigned long start_pfn, add_one_highpage_init(page, node_pfn); } + return 0; + } void __init add_highpages_with_active_regions(int nid, unsigned long start_pfn, diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 66c0fd21894b..bb0642318a95 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1637,12 +1637,43 @@ static inline int iommu_prepare_rmrr_dev(struct dmar_rmrr_unit *rmrr, } #ifdef CONFIG_DMAR_GFX_WA -extern int arch_get_ram_range(int slot, u64 *addr, u64 *size); +struct iommu_prepare_data { + struct pci_dev *pdev; + int ret; +}; + +static int __init iommu_prepare_work_fn(unsigned long start_pfn, + unsigned long end_pfn, void *datax) +{ + struct iommu_prepare_data *data; + + data = (struct iommu_prepare_data *)datax; + + data->ret = iommu_prepare_identity_map(data->pdev, + start_pfn<ret; + +} + +static int __init iommu_prepare_with_active_regions(struct pci_dev *pdev) +{ + int nid; + struct iommu_prepare_data data; + + data.pdev = pdev; + data.ret = 0; + + for_each_online_node(nid) { + work_with_active_regions(nid, iommu_prepare_work_fn, &data); + if (data.ret) + return data.ret; + } + return data.ret; +} + static void __init iommu_prepare_gfx_mapping(void) { struct pci_dev *pdev = NULL; - u64 base, size; - int slot; int ret; for_each_pci_dev(pdev) { @@ -1651,17 +1682,9 @@ static void __init iommu_prepare_gfx_mapping(void) continue; printk(KERN_INFO "IOMMU: gfx device %s 1-1 mapping\n", pci_name(pdev)); - slot = arch_get_ram_range(0, &base, &size); - while (slot >= 0) { - ret = iommu_prepare_identity_map(pdev, - base, base + size); - if (ret) - goto error; - slot = arch_get_ram_range(slot, &base, &size); - } - continue; -error: - printk(KERN_ERR "IOMMU: mapping reserved region failed\n"); + ret = iommu_prepare_with_active_regions(pdev); + if (ret) + printk(KERN_ERR "IOMMU: mapping reserved region failed\n"); } } #endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 3d647b24041f..cf1cd3a2ed78 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1011,7 +1011,7 @@ extern unsigned long find_min_pfn_with_active_regions(void); extern unsigned long find_max_pfn_with_active_regions(void); extern void free_bootmem_with_active_regions(int nid, unsigned long max_low_pfn); -typedef void (*work_fn_t)(unsigned long, unsigned long, void *); +typedef int (*work_fn_t)(unsigned long, unsigned long, void *); extern void work_with_active_regions(int nid, work_fn_t work_fn, void *data); extern void sparse_memory_present_with_active_regions(int nid); #ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 41c6e3aa059f..e25b6b24f844 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2932,10 +2932,14 @@ void __init free_bootmem_with_active_regions(int nid, void __init work_with_active_regions(int nid, work_fn_t work_fn, void *data) { int i; + int ret; - for_each_active_range_index_in_nid(i, nid) - work_fn(early_node_map[i].start_pfn, early_node_map[i].end_pfn, - data); + for_each_active_range_index_in_nid(i, nid) { + ret = work_fn(early_node_map[i].start_pfn, + early_node_map[i].end_pfn, data); + if (ret) + break; + } } /** * sparse_memory_present_with_active_regions - Call memory_present for each active range -- cgit v1.2.3 From 3c999f142665265afd0fe9190204dd051f17e505 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 20 Jun 2008 16:11:20 -0700 Subject: x86: check command line when CONFIG_X86_MPPARSE is not set, v2 if acpi=off, acpi=noirq and pci=noacpi, we need to disable apic. Signed-off-by: Yinghai Lu Cc: Andrew Morton Cc: "Maciej W. Rozycki" Signed-off-by: Ingo Molnar --- arch/x86/kernel/acpi/boot.c | 14 ++++++++++++++ arch/x86/kernel/apic_32.c | 2 +- arch/x86/kernel/setup.c | 4 ++++ arch/x86/kernel/setup_32.c | 5 +++++ arch/x86/kernel/setup_64.c | 9 +++++++++ include/asm-x86/apic.h | 6 +++++- include/linux/acpi.h | 6 ++++++ 7 files changed, 44 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 6516359922ba..5c0107602b62 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1787,6 +1787,20 @@ static int __init parse_pci(char *arg) } early_param("pci", parse_pci); +int __init acpi_mps_check(void) +{ +#if defined(CONFIG_X86_LOCAL_APIC) && !defined(CONFIG_X86_MPPARSE) +/* mptable code is not built-in*/ + if (acpi_disabled || acpi_noirq) { + printk(KERN_WARNING "MPS support code is not built-in.\n" + "Using acpi=off or acpi=noirq or pci=noacpi " + "may have problem\n"); + return 1; + } +#endif + return 0; +} + #ifdef CONFIG_X86_IO_APIC static int __init parse_acpi_skip_timer_override(char *arg) { diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c index dd8de26b2786..4932d7813bcd 100644 --- a/arch/x86/kernel/apic_32.c +++ b/arch/x86/kernel/apic_32.c @@ -57,7 +57,7 @@ unsigned long mp_lapic_addr; * * -1=force-disable, +1=force-enable */ -static int enable_local_apic __initdata; +int enable_local_apic; /* Local APIC timer verification ok */ static int local_apic_timer_verify_ok; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index c5330f601b68..56aee55cf8dc 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -161,6 +161,10 @@ void __init setup_per_cpu_areas(void) char *ptr; int cpu; + /* no processor from mptable or madt */ + if (!num_processors) + num_processors = 1; + #ifdef CONFIG_HOTPLUG_CPU prefill_possible_map(); #else diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c index 369d0fe1ff9c..cad4e893df05 100644 --- a/arch/x86/kernel/setup_32.c +++ b/arch/x86/kernel/setup_32.c @@ -677,6 +677,11 @@ void __init setup_arch(char **cmdline_p) parse_early_param(); + if (acpi_mps_check()){ + enable_local_apic = -1; + clear_cpu_cap(&boot_cpu_data, X86_FEATURE_APIC); + } + finish_e820_parsing(); probe_roms(); diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index a93300de4da9..175c696ec536 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c @@ -302,6 +302,11 @@ void __init setup_arch(char **cmdline_p) parse_early_param(); + if (acpi_mps_check()) { + disable_apic = 1; + clear_cpu_cap(&boot_cpu_data, X86_FEATURE_APIC); + } + #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT if (init_ohci1394_dma_early) init_ohci1394_dma_on_all_controllers(); @@ -723,6 +728,10 @@ static void __cpuinit early_identify_cpu(struct cpuinfo_x86 *c) cpu_devs[c->x86_vendor]->c_early_init(c); validate_pat_support(c); + + /* early_param could clear that, but recall get it set again */ + if (disable_apic) + clear_cpu_cap(c, X86_FEATURE_APIC); } /* diff --git a/include/asm-x86/apic.h b/include/asm-x86/apic.h index 313bcaf4b6c3..9fe941cd843d 100644 --- a/include/asm-x86/apic.h +++ b/include/asm-x86/apic.h @@ -39,8 +39,12 @@ extern int apic_verbosity; extern int local_apic_timer_c2_ok; extern int ioapic_force; -extern int disable_apic; +#ifdef CONFIG_X86_64 +extern int disable_apic; +#else +extern int enable_local_apic; +#endif /* * Basic functions accessing APICs. */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 41f7ce7edd7a..0601075d09a1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -82,6 +82,7 @@ char * __acpi_map_table (unsigned long phys_addr, unsigned long size); int early_acpi_boot_init(void); int acpi_boot_init (void); int acpi_boot_table_init (void); +int acpi_mps_check (void); int acpi_numa_init (void); int acpi_table_init (void); @@ -250,6 +251,11 @@ static inline int acpi_boot_table_init(void) return 0; } +static inline int acpi_mps_check(void) +{ + return 0; +} + static inline int acpi_check_resource_conflict(struct resource *res) { return 0; -- cgit v1.2.3 From 429a380571a6e6b8525b93161544eafc9b227e44 Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Tue, 1 Jul 2008 14:16:03 +0300 Subject: mac80211: add block ack request capability This patch adds block ack request capability Signed-off-by: Ester Kummer Signed-off-by: Tomas Winkler Signed-off-by: Ron Rindjunsky Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 4 ++++ include/net/mac80211.h | 3 +++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/main.c | 21 +++++++++++++++++++-- net/mac80211/mlme.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index cffd6d0094f9..aa603c3d76d1 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -658,6 +658,10 @@ struct ieee80211_bar { __le16 start_seq_num; } __attribute__((packed)); +/* 802.11 BAR control masks */ +#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 + /** * struct ieee80211_ht_cap - HT capabilities * diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3a204acad901..0a5de3ef527a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -235,6 +235,8 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_STAT_ACK: Frame was acknowledged * @IEEE80211_TX_STAT_AMPDU: The frame was aggregated, so status * is for the whole aggregation. + * @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned, + * so consider using block ack request (BAR). */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -260,6 +262,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_TX_FILTERED = BIT(20), IEEE80211_TX_STAT_ACK = BIT(21), IEEE80211_TX_STAT_AMPDU = BIT(22), + IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(23), }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f90da1bbec49..175cbdd36d7f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -904,6 +904,7 @@ void ieee80211_send_addba_request(struct net_device *dev, const u8 *da, u16 agg_size, u16 timeout); void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, u16 initiator, u16 reason_code); +void ieee80211_send_bar(struct net_device *dev, u8 *ra, u16 tid, u16 ssn); void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da, u16 tid, u16 initiator, u16 reason); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f18cfd727872..074f71a62a6d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1404,14 +1404,15 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u16 frag, type; + __le16 fc; struct ieee80211_tx_status_rtap_hdr *rthdr; struct ieee80211_sub_if_data *sdata; struct net_device *prev_dev = NULL; + struct sta_info *sta; rcu_read_lock(); if (info->status.excessive_retries) { - struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { if (test_sta_flags(sta, WLAN_STA_PS)) { @@ -1426,8 +1427,24 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } } + fc = hdr->frame_control; + + if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) && + (ieee80211_is_data_qos(fc))) { + u16 tid, ssn; + u8 *qc; + sta = sta_info_get(local, hdr->addr1); + if (sta) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) + & IEEE80211_SCTL_SEQ); + ieee80211_send_bar(sta->sdata->dev, hdr->addr1, + tid, ssn); + } + } + if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { - struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { ieee80211_handle_filtered_frame(local, sta, skb); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 86abdf96390c..e080482b63cd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1536,6 +1536,35 @@ void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, ieee80211_sta_tx(dev, skb, 0); } +void ieee80211_send_bar(struct net_device *dev, u8 *ra, u16 tid, u16 ssn) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_bar *bar; + u16 bar_control = 0; + + skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer for " + "bar frame\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar)); + memset(bar, 0, sizeof(*bar)); + bar->frame_control = IEEE80211_FC(IEEE80211_FTYPE_CTL, + IEEE80211_STYPE_BACK_REQ); + memcpy(bar->ra, ra, ETH_ALEN); + memcpy(bar->ta, dev->dev_addr, ETH_ALEN); + bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; + bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; + bar_control |= (u16)(tid << 12); + bar->control = cpu_to_le16(bar_control); + bar->start_seq_num = cpu_to_le16(ssn); + + ieee80211_sta_tx(dev, skb, 0); +} + void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid, u16 initiator, u16 reason) { -- cgit v1.2.3 From f3d1eb19abdcb1e740d8ba0e06d606c1d4165438 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Tue, 8 Jul 2008 10:30:27 -0400 Subject: Input: serio - trivial documentation fix In include/linux/serio.h two different define-series are documented as "Serio types". However the second series contains defines for the different protocols. Signed-off-by: Niels de Vos Signed-off-by: Dmitry Torokhov --- include/linux/serio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/serio.h b/include/linux/serio.h index 48defc4d181e..e72716cca577 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -175,7 +175,7 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_8042_XL 0x06 /* - * Serio types + * Serio protocols */ #define SERIO_UNKNOWN 0x00 #define SERIO_MSC 0x01 -- cgit v1.2.3 From 69ac9cd629ca96e59f34eb4ccd12d00b2c8276a7 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Fri, 27 Jun 2008 13:12:54 +0200 Subject: sysfs: add /sys/firmware/memmap This patch adds /sys/firmware/memmap interface that represents the BIOS (or Firmware) provided memory map. The tree looks like: /sys/firmware/memmap/0/start (hex number) end (hex number) type (string) ... /1/start end type With the following shell snippet one can print the memory map in the same form the kernel prints itself when booting on x86 (the E820 map). --------- 8< -------------------------- #!/bin/sh cd /sys/firmware/memmap for dir in * ; do start=$(cat $dir/start) end=$(cat $dir/end) type=$(cat $dir/type) printf "%016x-%016x (%s)\n" $start $[ $end +1] "$type" done --------- >8 -------------------------- That patch only provides the needed interface: 1. The sysfs interface. 2. The structure and enumeration definition. 3. The function firmware_map_add() and firmware_map_add_early() that should be called from architecture code (E820/EFI, for example) to add the contents to the interface. If the kernel is compiled without CONFIG_FIRMWARE_MEMMAP, the interface does nothing without cluttering the architecture-specific code with #ifdef's. The purpose of the new interface is kexec: While /proc/iomem represents the *used* memory map (e.g. modified via kernel parameters like 'memmap' and 'mem'), the /sys/firmware/memmap tree represents the unmodified memory map provided via the firmware. So kexec can: - use the original memory map for rebooting, - use the /proc/iomem for setting up the ELF core headers for kdump case that should only represent the memory of the system. The patch has been tested on i386 and x86_64. Signed-off-by: Bernhard Walle Acked-by: Greg KH Acked-by: Vivek Goyal Cc: kexec@lists.infradead.org Cc: yhlu.kernel@gmail.com Signed-off-by: Ingo Molnar --- Documentation/ABI/testing/sysfs-firmware-memmap | 71 ++++++++ drivers/firmware/Kconfig | 10 ++ drivers/firmware/Makefile | 1 + drivers/firmware/memmap.c | 205 ++++++++++++++++++++++++ include/linux/firmware-map.h | 74 +++++++++ 5 files changed, 361 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-firmware-memmap create mode 100644 drivers/firmware/memmap.c create mode 100644 include/linux/firmware-map.h (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-firmware-memmap b/Documentation/ABI/testing/sysfs-firmware-memmap new file mode 100644 index 000000000000..0d99ee6ae02e --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-memmap @@ -0,0 +1,71 @@ +What: /sys/firmware/memmap/ +Date: June 2008 +Contact: Bernhard Walle +Description: + On all platforms, the firmware provides a memory map which the + kernel reads. The resources from that memory map are registered + in the kernel resource tree and exposed to userspace via + /proc/iomem (together with other resources). + + However, on most architectures that firmware-provided memory + map is modified afterwards by the kernel itself, either because + the kernel merges that memory map with other information or + just because the user overwrites that memory map via command + line. + + kexec needs the raw firmware-provided memory map to setup the + parameter segment of the kernel that should be booted with + kexec. Also, the raw memory map is useful for debugging. For + that reason, /sys/firmware/memmap is an interface that provides + the raw memory map to userspace. + + The structure is as follows: Under /sys/firmware/memmap there + are subdirectories with the number of the entry as their name: + + /sys/firmware/memmap/0 + /sys/firmware/memmap/1 + /sys/firmware/memmap/2 + /sys/firmware/memmap/3 + ... + + The maximum depends on the number of memory map entries provided + by the firmware. The order is just the order that the firmware + provides. + + Each directory contains three files: + + start : The start address (as hexadecimal number with the + '0x' prefix). + end : The end address, inclusive (regardless whether the + firmware provides inclusive or exclusive ranges). + type : Type of the entry as string. See below for a list of + valid types. + + So, for example: + + /sys/firmware/memmap/0/start + /sys/firmware/memmap/0/end + /sys/firmware/memmap/0/type + /sys/firmware/memmap/1/start + ... + + Currently following types exist: + + - System RAM + - ACPI Tables + - ACPI Non-volatile Storage + - reserved + + Following shell snippet can be used to display that memory + map in a human-readable format: + + -------------------- 8< ---------------------------------------- + #!/bin/bash + cd /sys/firmware/memmap + for dir in * ; do + start=$(cat $dir/start) + end=$(cat $dir/end) + type=$(cat $dir/type) + printf "%016x-%016x (%s)\n" $start $[ $end +1] "$type" + done + -------------------- >8 ---------------------------------------- diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index dc2cec6127d1..ebb9e51deb0c 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -26,6 +26,16 @@ config EDD_OFF kernel. Say N if you want EDD enabled by default. EDD can be dynamically set using the kernel parameter 'edd={on|skipmbr|off}'. +config FIRMWARE_MEMMAP + bool "Add firmware-provided memory map to sysfs" if EMBEDDED + default (X86_64 || X86_32) + help + Add the firmware-provided (unmodified) memory map to /sys/firmware/memmap. + That memory map is used for example by kexec to set up parameter area + for the next kernel, but can also be used for debugging purposes. + + See also Documentation/ABI/testing/sysfs-firmware-memmap. + config EFI_VARS tristate "EFI Variable Support via sysfs" depends on EFI diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 4c9147154df8..1c3c17343dbe 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_DCDBAS) += dcdbas.o obj-$(CONFIG_DMIID) += dmi-id.o obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o +obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c new file mode 100644 index 000000000000..e23399c7f773 --- /dev/null +++ b/drivers/firmware/memmap.c @@ -0,0 +1,205 @@ +/* + * linux/drivers/firmware/memmap.c + * Copyright (C) 2008 SUSE LINUX Products GmbH + * by Bernhard Walle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * Data types ------------------------------------------------------------------ + */ + +/* + * Firmware map entry. Because firmware memory maps are flat and not + * hierarchical, it's ok to organise them in a linked list. No parent + * information is necessary as for the resource tree. + */ +struct firmware_map_entry { + resource_size_t start; /* start of the memory range */ + resource_size_t end; /* end of the memory range (incl.) */ + const char *type; /* type of the memory range */ + struct list_head list; /* entry for the linked list */ + struct kobject kobj; /* kobject for each entry */ +}; + +/* + * Forward declarations -------------------------------------------------------- + */ +static ssize_t memmap_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf); +static ssize_t start_show(struct firmware_map_entry *entry, char *buf); +static ssize_t end_show(struct firmware_map_entry *entry, char *buf); +static ssize_t type_show(struct firmware_map_entry *entry, char *buf); + +/* + * Static data ----------------------------------------------------------------- + */ + +struct memmap_attribute { + struct attribute attr; + ssize_t (*show)(struct firmware_map_entry *entry, char *buf); +}; + +struct memmap_attribute memmap_start_attr = __ATTR_RO(start); +struct memmap_attribute memmap_end_attr = __ATTR_RO(end); +struct memmap_attribute memmap_type_attr = __ATTR_RO(type); + +/* + * These are default attributes that are added for every memmap entry. + */ +static struct attribute *def_attrs[] = { + &memmap_start_attr.attr, + &memmap_end_attr.attr, + &memmap_type_attr.attr, + NULL +}; + +static struct sysfs_ops memmap_attr_ops = { + .show = memmap_attr_show, +}; + +static struct kobj_type memmap_ktype = { + .sysfs_ops = &memmap_attr_ops, + .default_attrs = def_attrs, +}; + +/* + * Registration functions ------------------------------------------------------ + */ + +/* + * Firmware memory map entries + */ +static LIST_HEAD(map_entries); + +/** + * Common implementation of firmware_map_add() and firmware_map_add_early() + * which expects a pre-allocated struct firmware_map_entry. + * + * @start: Start of the memory range. + * @end: End of the memory range (inclusive). + * @type: Type of the memory range. + * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised + * entry. + */ +static int firmware_map_add_entry(resource_size_t start, resource_size_t end, + const char *type, + struct firmware_map_entry *entry) +{ + BUG_ON(start > end); + + entry->start = start; + entry->end = end; + entry->type = type; + INIT_LIST_HEAD(&entry->list); + kobject_init(&entry->kobj, &memmap_ktype); + + list_add_tail(&entry->list, &map_entries); + + return 0; +} + +/* + * See for documentation. + */ +int firmware_map_add(resource_size_t start, resource_size_t end, + const char *type) +{ + struct firmware_map_entry *entry; + + entry = kmalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); + WARN_ON(!entry); + if (!entry) + return -ENOMEM; + + return firmware_map_add_entry(start, end, type, entry); +} + +/* + * See for documentation. + */ +int __init firmware_map_add_early(resource_size_t start, resource_size_t end, + const char *type) +{ + struct firmware_map_entry *entry; + + entry = alloc_bootmem_low(sizeof(struct firmware_map_entry)); + WARN_ON(!entry); + if (!entry) + return -ENOMEM; + + return firmware_map_add_entry(start, end, type, entry); +} + +/* + * Sysfs functions ------------------------------------------------------------- + */ + +static ssize_t start_show(struct firmware_map_entry *entry, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->start); +} + +static ssize_t end_show(struct firmware_map_entry *entry, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->end); +} + +static ssize_t type_show(struct firmware_map_entry *entry, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", entry->type); +} + +#define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr) +#define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj) + +static ssize_t memmap_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct firmware_map_entry *entry = to_memmap_entry(kobj); + struct memmap_attribute *memmap_attr = to_memmap_attr(attr); + + return memmap_attr->show(entry, buf); +} + +/* + * Initialises stuff and adds the entries in the map_entries list to + * sysfs. Important is that firmware_map_add() and firmware_map_add_early() + * must be called before late_initcall. + */ +static int __init memmap_init(void) +{ + int i = 0; + struct firmware_map_entry *entry; + struct kset *memmap_kset; + + memmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj); + WARN_ON(!memmap_kset); + if (!memmap_kset) + return -ENOMEM; + + list_for_each_entry(entry, &map_entries, list) { + entry->kobj.kset = memmap_kset; + kobject_add(&entry->kobj, NULL, "%d", i++); + } + + return 0; +} +late_initcall(memmap_init); + diff --git a/include/linux/firmware-map.h b/include/linux/firmware-map.h new file mode 100644 index 000000000000..acbdbcc16051 --- /dev/null +++ b/include/linux/firmware-map.h @@ -0,0 +1,74 @@ +/* + * include/linux/firmware-map.h: + * Copyright (C) 2008 SUSE LINUX Products GmbH + * by Bernhard Walle + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * 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. + * + */ +#ifndef _LINUX_FIRMWARE_MAP_H +#define _LINUX_FIRMWARE_MAP_H + +#include +#include + +/* + * provide a dummy interface if CONFIG_FIRMWARE_MEMMAP is disabled + */ +#ifdef CONFIG_FIRMWARE_MEMMAP + +/** + * Adds a firmware mapping entry. This function uses kmalloc() for memory + * allocation. Use firmware_map_add_early() if you want to use the bootmem + * allocator. + * + * That function must be called before late_initcall. + * + * @start: Start of the memory range. + * @end: End of the memory range (inclusive). + * @type: Type of the memory range. + * + * Returns 0 on success, or -ENOMEM if no memory could be allocated. + */ +int firmware_map_add(resource_size_t start, resource_size_t end, + const char *type); + +/** + * Adds a firmware mapping entry. This function uses the bootmem allocator + * for memory allocation. Use firmware_map_add() if you want to use kmalloc(). + * + * That function must be called before late_initcall. + * + * @start: Start of the memory range. + * @end: End of the memory range (inclusive). + * @type: Type of the memory range. + * + * Returns 0 on success, or -ENOMEM if no memory could be allocated. + */ +int firmware_map_add_early(resource_size_t start, resource_size_t end, + const char *type); + +#else /* CONFIG_FIRMWARE_MEMMAP */ + +static inline int firmware_map_add(resource_size_t start, resource_size_t end, + const char *type) +{ + return 0; +} + +static inline int firmware_map_add_early(resource_size_t start, + resource_size_t end, const char *type) +{ + return 0; +} + +#endif /* CONFIG_FIRMWARE_MEMMAP */ + +#endif /* _LINUX_FIRMWARE_MAP_H */ -- cgit v1.2.3 From a861beb1401d65e3f095fee074c13645ab06490e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 8 Jul 2008 19:27:22 +0200 Subject: ide: add __ide_default_irq() inline helper Add __ide_default_irq() inline helper and use it instead of ide_default_irq() in ide-probe.c and ns87415.c (all host drivers except IDE PCI ones always setup hwif->irq so it is enough to check only for I/O bases 0x1f0 and 0x170). This fixes post-2.6.25 regression since ide_default_irq() define could shadow ide_default_irq() inline. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 8 ++------ drivers/ide/pci/ns87415.c | 6 +----- include/linux/ide.h | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index d27061b39324..26e68b65b7cf 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1218,16 +1218,12 @@ static void drive_release_dev (struct device *dev) complete(&drive->gendev_rel_comp); } -#ifndef ide_default_irq -#define ide_default_irq(irq) 0 -#endif - static int hwif_init(ide_hwif_t *hwif) { int old_irq; if (!hwif->irq) { - hwif->irq = ide_default_irq(hwif->io_ports.data_addr); + hwif->irq = __ide_default_irq(hwif->io_ports.data_addr); if (!hwif->irq) { printk("%s: DISABLED, NO IRQ\n", hwif->name); return 0; @@ -1257,7 +1253,7 @@ static int hwif_init(ide_hwif_t *hwif) * It failed to initialise. Find the default IRQ for * this port and try that. */ - hwif->irq = ide_default_irq(hwif->io_ports.data_addr); + hwif->irq = __ide_default_irq(hwif->io_ports.data_addr); if (!hwif->irq) { printk("%s: Disabled unable to get IRQ %d.\n", hwif->name, old_irq); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index fec4955f449b..a7a41bb82778 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -225,10 +225,6 @@ static int ns87415_dma_setup(ide_drive_t *drive) return 1; } -#ifndef ide_default_irq -#define ide_default_irq(irq) 0 -#endif - static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); @@ -288,7 +284,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) } if (!using_inta) - hwif->irq = ide_default_irq(hwif->io_ports.data_addr); + hwif->irq = __ide_default_irq(hwif->io_ports.data_addr); else if (!hwif->irq && hwif->mate && hwif->mate->irq) hwif->irq = hwif->mate->irq; /* share IRQ with mate */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 9918772bf274..eddb6daadf4a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -189,6 +189,21 @@ static inline void ide_std_init_ports(hw_regs_t *hw, hw->io_ports.ctl_addr = ctl_addr; } +/* for IDE PCI controllers in legacy mode, temporary */ +static inline int __ide_default_irq(unsigned long base) +{ + switch (base) { +#ifdef CONFIG_IA64 + case 0x1f0: return isa_irq_to_vector(14); + case 0x170: return isa_irq_to_vector(15); +#else + case 0x1f0: return 14; + case 0x170: return 15; +#endif + } + return 0; +} + #include #if !defined(MAX_HWIFS) || defined(CONFIG_EMBEDDED) -- cgit v1.2.3 From 238f74a227fd7de8ea1bc66dcbbd36cf9920d1cb Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 2 Jul 2008 11:05:34 -0700 Subject: mac80211: move QOS control helpers into ieee80211.h Also remove the WLAN_IS_QOS_DATA inline after removing the last two users. This starts moving away from using rx->fc to using the header frame_control directly. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 2 ++ net/mac80211/rx.c | 28 ++++++++++++++-------------- net/mac80211/wme.c | 4 ++-- net/mac80211/wme.h | 8 -------- 4 files changed, 18 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index aa603c3d76d1..a1630ba0b87c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -99,6 +99,8 @@ #define IEEE80211_MAX_SSID_LEN 32 #define IEEE80211_MAX_MESH_ID_LEN 32 #define IEEE80211_QOS_CTL_LEN 2 +#define IEEE80211_QOS_CTL_TID_MASK 0x000F +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 struct ieee80211_hdr { __le16 frame_control; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6a88e8f9bff0..a3a26e557274 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -321,20 +321,20 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) { - u8 *data = rx->skb->data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; int tid; /* does the frame have a qos control field? */ - if (WLAN_FC_IS_QOS_DATA(rx->fc)) { - u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); /* frame has qos control */ - tid = qc[0] & QOS_CONTROL_TID_MASK; - if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) rx->flags |= IEEE80211_RX_AMSDU; else rx->flags &= ~IEEE80211_RX_AMSDU; } else { - if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { + if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { /* Separate TID for management frames */ tid = NUM_RX_DATA_QUEUES - 1; } else { @@ -1037,19 +1037,19 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) static ieee80211_rx_result debug_noinline ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) { - u16 fc = rx->fc; u8 *data = rx->skb->data; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; - if (!WLAN_FC_IS_QOS_DATA(fc)) + if (!ieee80211_is_data_qos(hdr->frame_control)) return RX_CONTINUE; /* remove the qos control field, update frame type and meta-data */ - memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2); - hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2); + memmove(data + IEEE80211_QOS_CTL_LEN, data, + ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN); + hdr = (struct ieee80211_hdr *)skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN); /* change frame type to non QOS */ - rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA; - hdr->frame_control = cpu_to_le16(fc); + rx->fc &= ~IEEE80211_STYPE_QOS_DATA; + hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA); return RX_CONTINUE; } @@ -2044,7 +2044,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, if (!ieee80211_is_data_qos(hdr->frame_control)) goto end_reorder; - tid = *ieee80211_get_qos_ctl(hdr) & QOS_CONTROL_TID_MASK; + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) goto end_reorder; diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index a1a53a4f2b99..5c666f7eda8f 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -154,7 +154,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) queue = skb_get_queue_mapping(skb); rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); - tid = skb->priority & QOS_CONTROL_TAG1D_MASK; + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; if (sta) { int ampdu_queue = sta->tid_to_tx_q[tid]; if ((ampdu_queue < QD_NUM(hw)) && @@ -181,7 +181,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); u8 ack_policy = 0; - tid = skb->priority & QOS_CONTROL_TAG1D_MASK; + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; if (local->wifi_wme_noack_test) ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << QOS_CONTROL_ACK_POLICY_SHIFT; diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index bbdb53344817..1aca609eccfc 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -19,18 +19,10 @@ #define QOS_CONTROL_ACK_POLICY_NORMAL 0 #define QOS_CONTROL_ACK_POLICY_NOACK 1 -#define QOS_CONTROL_TID_MASK 0x0f #define QOS_CONTROL_ACK_POLICY_SHIFT 5 -#define QOS_CONTROL_TAG1D_MASK 0x07 - extern const int ieee802_1d_to_ac[8]; -static inline int WLAN_FC_IS_QOS_DATA(u16 fc) -{ - return (fc & 0x8C) == 0x88; -} - #ifdef CONFIG_MAC80211_QOS void ieee80211_install_qdisc(struct net_device *dev); int ieee80211_qdisc_installed(struct net_device *dev); -- cgit v1.2.3 From 7cc5bf9a3a84e5a02e23e5739fb894790b37c101 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 8 Jul 2008 11:58:21 -0700 Subject: dmaengine: track the number of clients using a channel Haavard's dma-slave interface would like to test for exclusive access to a channel. The standard channel refcounting is not sufficient in that it tracks more than just client references, it is also inaccurate as reference counts are percpu until the channel is removed. This change also enables a future fix to deallocate resources when a client declines to use a capable channel. Acked-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 14 ++++++++++---- include/linux/dmaengine.h | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 99c22b42bada..10de69eb1a3e 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -183,9 +183,10 @@ static void dma_client_chan_alloc(struct dma_client *client) /* we are done once this client rejects * an available resource */ - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_get(chan); - else if (ack == DMA_NAK) + chan->client_count++; + } else if (ack == DMA_NAK) return; } } @@ -272,8 +273,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) /* client was holding resources for this channel so * free it */ - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_put(chan); + chan->client_count--; + } } mutex_unlock(&dma_list_mutex); @@ -313,8 +316,10 @@ void dma_async_client_unregister(struct dma_client *client) ack = client->event_callback(client, chan, DMA_RESOURCE_REMOVED); - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_put(chan); + chan->client_count--; + } } list_del(&client->global_node); @@ -394,6 +399,7 @@ int dma_async_device_register(struct dma_device *device) kref_get(&device->refcount); kref_get(&device->refcount); kref_init(&chan->refcount); + chan->client_count = 0; chan->slow_ref = 0; INIT_RCU_HEAD(&chan->rcu); } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d08a5c5eb928..6432b8343220 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -139,6 +139,7 @@ struct dma_chan_percpu { * @rcu: the DMA channel's RCU head * @device_node: used to add this to the device chan list * @local: per-cpu pointer to a struct dma_chan_percpu + * @client-count: how many clients are using this channel */ struct dma_chan { struct dma_device *device; @@ -154,6 +155,7 @@ struct dma_chan { struct list_head device_node; struct dma_chan_percpu *local; + int client_count; }; #define to_dma_chan(p) container_of(p, struct dma_chan, dev) -- cgit v1.2.3 From 848c536a37b8db4e461f14ca15fe29850151c822 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 8 Jul 2008 11:58:58 -0700 Subject: dmaengine: Add dma_client parameter to device_alloc_chan_resources A DMA controller capable of doing slave transfers may need to know a few things about the slave when preparing the channel. We don't want to add this information to struct dma_channel since the channel hasn't yet been bound to a client at this point. Instead, pass a reference to the client requesting the channel to the driver's device_alloc_chan_resources hook so that it can pick the necessary information from the dma_client struct by itself. [dan.j.williams@intel.com: fixed up fsldma and mv_xor] Acked-by: Maciej Sosnowski Signed-off-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 3 ++- drivers/dma/fsldma.c | 7 ++++--- drivers/dma/ioat_dma.c | 5 +++-- drivers/dma/iop-adma.c | 7 ++++--- drivers/dma/mv_xor.c | 7 ++++--- include/linux/dmaengine.h | 3 ++- 6 files changed, 19 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 10de69eb1a3e..7344f5dbd501 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -174,7 +174,8 @@ static void dma_client_chan_alloc(struct dma_client *client) if (!dma_chan_satisfies_mask(chan, client->cap_mask)) continue; - desc = chan->device->device_alloc_chan_resources(chan); + desc = chan->device->device_alloc_chan_resources( + chan, client); if (desc >= 0) { ack = client->event_callback(client, chan, diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 724f6fdd0af6..c0059ca58340 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -366,7 +366,8 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( * * Return - The number of descriptors allocated. */ -static int fsl_dma_alloc_chan_resources(struct dma_chan *chan) +static int fsl_dma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan); LIST_HEAD(tmp_list); @@ -819,7 +820,7 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) chan = &fsl_chan->common; - if (fsl_dma_alloc_chan_resources(chan) < 1) { + if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { dev_err(fsl_chan->dev, "selftest: Cannot alloc resources for DMA\n"); err = -ENODEV; @@ -847,7 +848,7 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) /* Test free and re-alloc channel resources */ fsl_dma_free_chan_resources(chan); - if (fsl_dma_alloc_chan_resources(chan) < 1) { + if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { dev_err(fsl_chan->dev, "selftest: Cannot alloc resources for DMA\n"); err = -ENODEV; diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 318e8a22d814..90e5b0a28cbf 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -452,7 +452,8 @@ static void ioat2_dma_massage_chan_desc(struct ioat_dma_chan *ioat_chan) * ioat_dma_alloc_chan_resources - returns the number of allocated descriptors * @chan: the channel to be filled out */ -static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) +static int ioat_dma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); struct ioat_desc_sw *desc; @@ -1049,7 +1050,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (device->common.device_alloc_chan_resources(dma_chan) < 1) { + if (device->common.device_alloc_chan_resources(dma_chan, NULL) < 1) { dev_err(&device->pdev->dev, "selftest cannot allocate chan resource\n"); err = -ENODEV; diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 4e6b052c0654..b57564dd0232 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -444,7 +444,8 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan); static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan); /* returns the number of allocated descriptors */ -static int iop_adma_alloc_chan_resources(struct dma_chan *chan) +static int iop_adma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { char *hw_desc; int idx; @@ -838,7 +839,7 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } @@ -936,7 +937,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index f0c123ce8ae0..8239cfdbc2e6 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -588,7 +588,8 @@ submit_done: } /* returns the number of allocated descriptors */ -static int mv_xor_alloc_chan_resources(struct dma_chan *chan) +static int mv_xor_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { char *hw_desc; int idx; @@ -938,7 +939,7 @@ static int __devinit mv_xor_memcpy_self_test(struct mv_xor_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (mv_xor_alloc_chan_resources(dma_chan) < 1) { + if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } @@ -1033,7 +1034,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (mv_xor_alloc_chan_resources(dma_chan) < 1) { + if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 6432b8343220..ba89b0f5056e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -281,7 +281,8 @@ struct dma_device { int dev_id; struct device *dev; - int (*device_alloc_chan_resources)(struct dma_chan *chan); + int (*device_alloc_chan_resources)(struct dma_chan *chan, + struct dma_client *client); void (*device_free_chan_resources)(struct dma_chan *chan); struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)( -- cgit v1.2.3 From e1d181efb14a93cf263d6c588a5395518edf3294 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 4 Jul 2008 00:13:40 -0700 Subject: dmaengine: add DMA_COMPL_SKIP_{SRC,DEST}_UNMAP flags to control dma unmap In some cases client code may need the dma-driver to skip the unmap of source and/or destination buffers. Setting these flags indicates to the driver to skip the unmap step. In this regard async_xor is currently broken in that it allows the destination buffer to be unmapped while an operation is still in progress, i.e. when the number of sources exceeds the hardware channel's maximum (fixed in a subsequent patch). Acked-by: Saeed Bishara Acked-by: Maciej Sosnowski Acked-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/ioat_dma.c | 48 +++++++++++++++++++++++------------------------ drivers/dma/iop-adma.c | 29 +++++++++++++++++----------- drivers/dma/mv_xor.c | 22 ++++++++++++++++------ include/linux/dmaengine.h | 4 ++++ 4 files changed, 61 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 90e5b0a28cbf..171cad69f318 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -757,6 +757,27 @@ static void ioat_dma_cleanup_tasklet(unsigned long data) chan->reg_base + IOAT_CHANCTRL_OFFSET); } +static void +ioat_dma_unmap(struct ioat_dma_chan *ioat_chan, struct ioat_desc_sw *desc) +{ + /* + * yes we are unmapping both _page and _single + * alloc'd regions with unmap_page. Is this + * *really* that bad? + */ + if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) + pci_unmap_page(ioat_chan->device->pdev, + pci_unmap_addr(desc, dst), + pci_unmap_len(desc, len), + PCI_DMA_FROMDEVICE); + + if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) + pci_unmap_page(ioat_chan->device->pdev, + pci_unmap_addr(desc, src), + pci_unmap_len(desc, len), + PCI_DMA_TODEVICE); +} + /** * ioat_dma_memcpy_cleanup - cleanup up finished descriptors * @chan: ioat channel to be cleaned up @@ -817,21 +838,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) */ if (desc->async_tx.cookie) { cookie = desc->async_tx.cookie; - - /* - * yes we are unmapping both _page and _single - * alloc'd regions with unmap_page. Is this - * *really* that bad? - */ - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - + ioat_dma_unmap(ioat_chan, desc); if (desc->async_tx.callback) { desc->async_tx.callback(desc->async_tx.callback_param); desc->async_tx.callback = NULL; @@ -890,16 +897,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) if (desc->async_tx.cookie) { cookie = desc->async_tx.cookie; desc->async_tx.cookie = 0; - - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - + ioat_dma_unmap(ioat_chan, desc); if (desc->async_tx.callback) { desc->async_tx.callback(desc->async_tx.callback_param); desc->async_tx.callback = NULL; diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index b57564dd0232..434013d41288 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -82,17 +82,24 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc, struct device *dev = &iop_chan->device->pdev->dev; u32 len = unmap->unmap_len; - u32 src_cnt = unmap->unmap_src_cnt; - dma_addr_t addr = iop_desc_get_dest_addr(unmap, - iop_chan); - - dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); - while (src_cnt--) { - addr = iop_desc_get_src_addr(unmap, - iop_chan, - src_cnt); - dma_unmap_page(dev, addr, len, - DMA_TO_DEVICE); + enum dma_ctrl_flags flags = desc->async_tx.flags; + u32 src_cnt; + dma_addr_t addr; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + addr = iop_desc_get_dest_addr(unmap, iop_chan); + dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + src_cnt = unmap->unmap_src_cnt; + while (src_cnt--) { + addr = iop_desc_get_src_addr(unmap, + iop_chan, + src_cnt); + dma_unmap_page(dev, addr, len, + DMA_TO_DEVICE); + } } desc->group_head = NULL; } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 8239cfdbc2e6..a4e4494663bf 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -308,13 +308,23 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc, struct device *dev = &mv_chan->device->pdev->dev; u32 len = unmap->unmap_len; - u32 src_cnt = unmap->unmap_src_cnt; - dma_addr_t addr = mv_desc_get_dest_addr(unmap); + enum dma_ctrl_flags flags = desc->async_tx.flags; + u32 src_cnt; + dma_addr_t addr; - dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); - while (src_cnt--) { - addr = mv_desc_get_src_addr(unmap, src_cnt); - dma_unmap_page(dev, addr, len, DMA_TO_DEVICE); + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + addr = mv_desc_get_dest_addr(unmap); + dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + src_cnt = unmap->unmap_src_cnt; + while (src_cnt--) { + addr = mv_desc_get_src_addr(unmap, + src_cnt); + dma_unmap_page(dev, addr, len, + DMA_TO_DEVICE); + } } desc->group_head = NULL; } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ba89b0f5056e..b058d6360383 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -102,10 +102,14 @@ enum dma_transaction_type { * @DMA_CTRL_ACK - the descriptor cannot be reused until the client * acknowledges receipt, i.e. has has a chance to establish any * dependency chains + * @DMA_COMPL_SKIP_SRC_UNMAP - set to disable dma-unmapping the source buffer(s) + * @DMA_COMPL_SKIP_DEST_UNMAP - set to disable dma-unmapping the destination(s) */ enum dma_ctrl_flags { DMA_PREP_INTERRUPT = (1 << 0), DMA_CTRL_ACK = (1 << 1), + DMA_COMPL_SKIP_SRC_UNMAP = (1 << 2), + DMA_COMPL_SKIP_DEST_UNMAP = (1 << 3), }; /** -- cgit v1.2.3 From dc0ee6435cb92ccc81b14ff28d163fecc5a7f120 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 8 Jul 2008 11:59:35 -0700 Subject: dmaengine: Add slave DMA interface This patch adds the necessary interfaces to the DMA Engine framework to use functionality found on most embedded DMA controllers: DMA from and to I/O registers with hardware handshaking. In this context, hardware hanshaking means that the peripheral that owns the I/O registers in question is able to tell the DMA controller when more data is available for reading, or when there is room for more data to be written. This usually happens internally on the chip, but these signals may also be exported outside the chip for things like IDE DMA, etc. A new struct dma_slave is introduced. This contains information that the DMA engine driver needs to set up slave transfers to and from a slave device. Most engines supporting DMA slave transfers will want to extend this structure with controller-specific parameters. This additional information is usually passed from the platform/board code through the client driver. A "slave" pointer is added to the dma_client struct. This must point to a valid dma_slave structure iff the DMA_SLAVE capability is requested. The DMA engine driver may use this information in its device_alloc_chan_resources hook to configure the DMA controller for slave transfers from and to the given slave device. A new operation for preparing slave DMA transfers is added to struct dma_device. This takes a scatterlist and returns a single descriptor representing the whole transfer. Another new operation for terminating all pending transfers is added as well. The latter is needed because there may be errors outside the scope of the DMA Engine framework that may require DMA operations to be terminated prematurely. DMA Engine drivers may extend the dma_device, dma_chan and/or dma_slave_descriptor structures to allow controller-specific operations. The client driver can detect such extensions by looking at the DMA Engine's struct device, or it can request a specific DMA Engine device by setting the dma_dev field in struct dma_slave. dmaslave interface changes since v4: * Fix checkpatch errors * Fix changelog (there are no slave descriptors anymore) dmaslave interface changes since v3: * Use dma_data_direction instead of a new enum * Submit slave transfers as scatterlists * Remove the DMA slave descriptor struct dmaslave interface changes since v2: * Add a dma_dev field to struct dma_slave. If set, the client can only be bound to the DMA controller that corresponds to this device. This allows controller-specific extensions of the dma_slave structure; if the device matches, the controller may safely assume its extensions are present. * Move reg_width into struct dma_slave as there are currently no users that need to be able to set the width on a per-transfer basis. dmaslave interface changes since v1: * Drop the set_direction and set_width descriptor hooks. Pass the direction and width to the prep function instead. * Declare a dma_slave struct with fixed information about a slave, i.e. register addresses, handshake interfaces and such. * Add pointer to a dma_slave struct to dma_client. Can be NULL if the DMA_SLAVE capability isn't requested. * Drop the set_slave device hook since the alloc_chan_resources hook now has enough information to set up the channel for slave transfers. Acked-by: Maciej Sosnowski Signed-off-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 16 ++++++++++++++- include/linux/dmaengine.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 7344f5dbd501..dc003a3a787d 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -169,7 +169,12 @@ static void dma_client_chan_alloc(struct dma_client *client) enum dma_state_client ack; /* Find a channel */ - list_for_each_entry(device, &dma_device_list, global_node) + list_for_each_entry(device, &dma_device_list, global_node) { + /* Does the client require a specific DMA controller? */ + if (client->slave && client->slave->dma_dev + && client->slave->dma_dev != device->dev) + continue; + list_for_each_entry(chan, &device->channels, device_node) { if (!dma_chan_satisfies_mask(chan, client->cap_mask)) continue; @@ -191,6 +196,7 @@ static void dma_client_chan_alloc(struct dma_client *client) return; } } + } } enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) @@ -289,6 +295,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) */ void dma_async_client_register(struct dma_client *client) { + /* validate client data */ + BUG_ON(dma_has_cap(DMA_SLAVE, client->cap_mask) && + !client->slave); + mutex_lock(&dma_list_mutex); list_add_tail(&client->global_node, &dma_client_list); mutex_unlock(&dma_list_mutex); @@ -365,6 +375,10 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && !device->device_prep_dma_interrupt); + BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && + !device->device_prep_slave_sg); + BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && + !device->device_terminate_all); BUG_ON(!device->device_alloc_chan_resources); BUG_ON(!device->device_free_chan_resources); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b058d6360383..9b91d341e1fa 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -89,10 +89,23 @@ enum dma_transaction_type { DMA_MEMSET, DMA_MEMCPY_CRC32C, DMA_INTERRUPT, + DMA_SLAVE, }; /* last transaction type for creation of the capabilities mask */ -#define DMA_TX_TYPE_END (DMA_INTERRUPT + 1) +#define DMA_TX_TYPE_END (DMA_SLAVE + 1) + +/** + * enum dma_slave_width - DMA slave register access width. + * @DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses + * @DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses + * @DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses + */ +enum dma_slave_width { + DMA_SLAVE_WIDTH_8BIT, + DMA_SLAVE_WIDTH_16BIT, + DMA_SLAVE_WIDTH_32BIT, +}; /** * enum dma_ctrl_flags - DMA flags to augment operation preparation, @@ -118,6 +131,32 @@ enum dma_ctrl_flags { */ typedef struct { DECLARE_BITMAP(bits, DMA_TX_TYPE_END); } dma_cap_mask_t; +/** + * struct dma_slave - Information about a DMA slave + * @dev: device acting as DMA slave + * @dma_dev: required DMA master device. If non-NULL, the client can not be + * bound to other masters than this. + * @tx_reg: physical address of data register used for + * memory-to-peripheral transfers + * @rx_reg: physical address of data register used for + * peripheral-to-memory transfers + * @reg_width: peripheral register width + * + * If dma_dev is non-NULL, the client can not be bound to other DMA + * masters than the one corresponding to this device. The DMA master + * driver may use this to determine if there is controller-specific + * data wrapped around this struct. Drivers of platform code that sets + * the dma_dev field must therefore make sure to use an appropriate + * controller-specific dma slave structure wrapping this struct. + */ +struct dma_slave { + struct device *dev; + struct device *dma_dev; + dma_addr_t tx_reg; + dma_addr_t rx_reg; + enum dma_slave_width reg_width; +}; + /** * struct dma_chan_percpu - the per-CPU part of struct dma_chan * @refcount: local_t used for open-coded "bigref" counting @@ -208,11 +247,14 @@ typedef enum dma_state_client (*dma_event_callback) (struct dma_client *client, * @event_callback: func ptr to call when something happens * @cap_mask: only return channels that satisfy the requested capabilities * a value of zero corresponds to any capability + * @slave: data for preparing slave transfer. Must be non-NULL iff the + * DMA_SLAVE capability is requested. * @global_node: list_head for global dma_client_list */ struct dma_client { dma_event_callback event_callback; dma_cap_mask_t cap_mask; + struct dma_slave *slave; struct list_head global_node; }; @@ -269,6 +311,8 @@ struct dma_async_tx_descriptor { * @device_prep_dma_zero_sum: prepares a zero_sum operation * @device_prep_dma_memset: prepares a memset operation * @device_prep_dma_interrupt: prepares an end of chain interrupt operation + * @device_prep_slave_sg: prepares a slave dma operation + * @device_terminate_all: terminate all pending operations * @device_issue_pending: push pending transactions to hardware */ struct dma_device { @@ -304,6 +348,12 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)( struct dma_chan *chan, unsigned long flags); + struct dma_async_tx_descriptor *(*device_prep_slave_sg)( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction direction, + unsigned long flags); + void (*device_terminate_all)(struct dma_chan *chan); + enum dma_status (*device_is_tx_complete)(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used); -- cgit v1.2.3 From 3bfb1d20b547a5071d01344581eac5846ea84491 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 8 Jul 2008 11:59:42 -0700 Subject: dmaengine: Driver for the Synopsys DesignWare DMA controller This adds a driver for the Synopsys DesignWare DMA controller (aka DMACA on AVR32 systems.) This DMA controller can be found integrated on the AT32AP7000 chip and is primarily meant for peripheral DMA transfer, but can also be used for memory-to-memory transfers. This patch is based on a driver from David Brownell which was based on an older version of the DMA Engine framework. It also implements the proposed extensions to the DMA Engine API for slave DMA operations. The dmatest client shows no problems, but there may still be room for improvement performance-wise. DMA slave transfer performance is definitely "good enough"; reading 100 MiB from an SD card running at ~20 MHz yields ~7.2 MiB/s average transfer rate. Full documentation for this controller can be found in the Synopsys DW AHB DMAC Databook: http://www.synopsys.com/designware/docs/iip/DW_ahb_dmac/latest/doc/dw_ahb_dmac_db.pdf The controller has lots of implementation options, so it's usually a good idea to check the data sheet of the chip it's intergrated on as well. The AT32AP7000 data sheet can be found here: http://www.atmel.com/dyn/products/datasheets.asp?family_id=682 Changes since v4: * Use client_count instead of dma_chan_is_in_use() * Add missing include * Unmap buffers unless client told us not to Changes since v3: * Update to latest DMA engine and DMA slave APIs * Embed the hw descriptor into the sw descriptor * Clean up and update MODULE_DESCRIPTION, copyright date, etc. Changes since v2: * Dequeue all pending transfers in terminate_all() * Rename dw_dmac.h -> dw_dmac_regs.h * Define and use controller-specific dma_slave data * Fix up a few outdated comments * Define hardware registers as structs (doesn't generate better code, unfortunately, but it looks nicer.) * Get number of channels from platform_data instead of hardcoding it based on CONFIG_WHATEVER_CPU. * Give slave clients exclusive access to the channel Acked-by: Maciej Sosnowski , Signed-off-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- arch/avr32/mach-at32ap/at32ap700x.c | 27 +- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/dw_dmac.c | 1122 ++++++++++++++++++++++++++++ drivers/dma/dw_dmac_regs.h | 225 ++++++ include/asm-avr32/arch-at32ap/at32ap700x.h | 16 + include/linux/dw_dmac.h | 62 ++ 7 files changed, 1449 insertions(+), 13 deletions(-) create mode 100644 drivers/dma/dw_dmac.c create mode 100644 drivers/dma/dw_dmac_regs.h create mode 100644 include/linux/dw_dmac.h (limited to 'include/linux') diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 0f24b4f85c17..892e27e0d583 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -7,6 +7,7 @@ */ #include #include +#include #include #include #include @@ -599,6 +600,17 @@ static void __init genclk_init_parent(struct clk *clk) clk->parent = parent; } +static struct dw_dma_platform_data dw_dmac0_data = { + .nr_channels = 3, +}; + +static struct resource dw_dmac0_resource[] = { + PBMEM(0xff200000), + IRQ(2), +}; +DEFINE_DEV_DATA(dw_dmac, 0); +DEV_CLK(hclk, dw_dmac0, hsb, 10); + /* -------------------------------------------------------------------- * System peripherals * -------------------------------------------------------------------- */ @@ -705,17 +717,6 @@ static struct clk pico_clk = { .users = 1, }; -static struct resource dmaca0_resource[] = { - { - .start = 0xff200000, - .end = 0xff20ffff, - .flags = IORESOURCE_MEM, - }, - IRQ(2), -}; -DEFINE_DEV(dmaca, 0); -DEV_CLK(hclk, dmaca0, hsb, 10); - /* -------------------------------------------------------------------- * HMATRIX * -------------------------------------------------------------------- */ @@ -828,7 +829,7 @@ void __init at32_add_system_devices(void) platform_device_register(&at32_eic0_device); platform_device_register(&smc0_device); platform_device_register(&pdc_device); - platform_device_register(&dmaca0_device); + platform_device_register(&dw_dmac0_device); platform_device_register(&at32_tcb0_device); platform_device_register(&at32_tcb1_device); @@ -1891,7 +1892,7 @@ struct clk *at32_clock_list[] = { &smc0_mck, &pdc_hclk, &pdc_pclk, - &dmaca0_hclk, + &dw_dmac0_hclk, &pico_clk, &pio0_mck, &pio1_mck, diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 4b6bd3d099cf..cd303901eb5b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -38,6 +38,15 @@ config INTEL_IOP_ADMA help Enable support for the Intel(R) IOP Series RAID engines. +config DW_DMAC + tristate "Synopsys DesignWare AHB DMA support" + depends on AVR32 + select DMA_ENGINE + default y if CPU_AT32AP7000 + help + Support the Synopsys DesignWare AHB DMA controller. This + can be integrated in chips such as the Atmel AT32ap7000. + config FSL_DMA bool "Freescale MPC85xx/MPC83xx DMA support" depends on PPC diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 181e3646fbfe..14f59527d4f6 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -6,3 +6,4 @@ ioatdma-objs := ioat.o ioat_dma.o ioat_dca.o obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MV_XOR) += mv_xor.o +obj-$(CONFIG_DW_DMAC) += dw_dmac.o diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c new file mode 100644 index 000000000000..94df91771243 --- /dev/null +++ b/drivers/dma/dw_dmac.c @@ -0,0 +1,1122 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on + * AVR32 systems.) + * + * Copyright (C) 2007-2008 Atmel Corporation + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dw_dmac_regs.h" + +/* + * This supports the Synopsys "DesignWare AHB Central DMA Controller", + * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all + * of which use ARM any more). See the "Databook" from Synopsys for + * information beyond what licensees probably provide. + * + * The driver has currently been tested only with the Atmel AT32AP7000, + * which does not support descriptor writeback. + */ + +/* NOTE: DMS+SMS is system-specific. We should get this information + * from the platform code somehow. + */ +#define DWC_DEFAULT_CTLLO (DWC_CTLL_DST_MSIZE(0) \ + | DWC_CTLL_SRC_MSIZE(0) \ + | DWC_CTLL_DMS(0) \ + | DWC_CTLL_SMS(1) \ + | DWC_CTLL_LLP_D_EN \ + | DWC_CTLL_LLP_S_EN) + +/* + * This is configuration-dependent and usually a funny size like 4095. + * Let's round it down to the nearest power of two. + * + * Note that this is a transfer count, i.e. if we transfer 32-bit + * words, we can do 8192 bytes per descriptor. + * + * This parameter is also system-specific. + */ +#define DWC_MAX_COUNT 2048U + +/* + * Number of descriptors to allocate for each channel. This should be + * made configurable somehow; preferably, the clients (at least the + * ones using slave transfers) should be able to give us a hint. + */ +#define NR_DESCS_PER_CHANNEL 64 + +/*----------------------------------------------------------------------*/ + +/* + * Because we're not relying on writeback from the controller (it may not + * even be configured into the core!) we don't need to use dma_pool. These + * descriptors -- and associated data -- are cacheable. We do need to make + * sure their dcache entries are written back before handing them off to + * the controller, though. + */ + +static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) +{ + return list_entry(dwc->active_list.next, struct dw_desc, desc_node); +} + +static struct dw_desc *dwc_first_queued(struct dw_dma_chan *dwc) +{ + return list_entry(dwc->queue.next, struct dw_desc, desc_node); +} + +static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + struct dw_desc *ret = NULL; + unsigned int i = 0; + + spin_lock_bh(&dwc->lock); + list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(&dwc->chan.dev, "desc %p not ACKed\n", desc); + i++; + } + spin_unlock_bh(&dwc->lock); + + dev_vdbg(&dwc->chan.dev, "scanned %u descriptors on freelist\n", i); + + return ret; +} + +static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + struct dw_desc *child; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dma_sync_single_for_cpu(dwc->chan.dev.parent, + child->txd.phys, sizeof(child->lli), + DMA_TO_DEVICE); + dma_sync_single_for_cpu(dwc->chan.dev.parent, + desc->txd.phys, sizeof(desc->lli), + DMA_TO_DEVICE); +} + +/* + * Move a descriptor, including any children, to the free list. + * `desc' must not be on any lists. + */ +static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + if (desc) { + struct dw_desc *child; + + dwc_sync_desc_for_cpu(dwc, desc); + + spin_lock_bh(&dwc->lock); + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dev_vdbg(&dwc->chan.dev, + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->txd.tx_list, &dwc->free_list); + dev_vdbg(&dwc->chan.dev, "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &dwc->free_list); + spin_unlock_bh(&dwc->lock); + } +} + +/* Called with dwc->lock held and bh disabled */ +static dma_cookie_t +dwc_assign_cookie(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + dma_cookie_t cookie = dwc->chan.cookie; + + if (++cookie < 0) + cookie = 1; + + dwc->chan.cookie = cookie; + desc->txd.cookie = cookie; + + return cookie; +} + +/*----------------------------------------------------------------------*/ + +/* Called with dwc->lock held and bh disabled */ +static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(&dwc->chan.dev, + "BUG: Attempted to start non-idle channel\n"); + dev_err(&dwc->chan.dev, + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); + + /* The tasklet will hopefully advance the queue... */ + return; + } + + channel_writel(dwc, LLP, first->txd.phys); + channel_writel(dwc, CTL_LO, + DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + channel_set_bit(dw, CH_EN, dwc->mask); +} + +/*----------------------------------------------------------------------*/ + +static void +dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + dma_async_tx_callback callback; + void *param; + struct dma_async_tx_descriptor *txd = &desc->txd; + + dev_vdbg(&dwc->chan.dev, "descriptor %u complete\n", txd->cookie); + + dwc->completed = txd->cookie; + callback = txd->callback; + param = txd->callback_param; + + dwc_sync_desc_for_cpu(dwc, desc); + list_splice_init(&txd->tx_list, &dwc->free_list); + list_move(&desc->desc_node, &dwc->free_list); + + /* + * We use dma_unmap_page() regardless of how the buffers were + * mapped before they were submitted... + */ + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) + dma_unmap_page(dwc->chan.dev.parent, desc->lli.dar, desc->len, + DMA_FROM_DEVICE); + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) + dma_unmap_page(dwc->chan.dev.parent, desc->lli.sar, desc->len, + DMA_TO_DEVICE); + + /* + * The API requires that no submissions are done from a + * callback, so we don't need to drop the lock here + */ + if (callback) + callback(param); +} + +static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(&dwc->chan.dev, + "BUG: XFER bit set, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + } + + /* + * Submit queued descriptors ASAP, i.e. before we go through + * the completed ones. + */ + if (!list_empty(&dwc->queue)) + dwc_dostart(dwc, dwc_first_queued(dwc)); + list_splice_init(&dwc->active_list, &list); + list_splice_init(&dwc->queue, &dwc->active_list); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc); +} + +static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + dma_addr_t llp; + struct dw_desc *desc, *_desc; + struct dw_desc *child; + u32 status_xfer; + + /* + * Clear block interrupt flag before scanning so that we don't + * miss any, and read LLP before RAW_XFER to ensure it is + * valid if we decide to scan the list. + */ + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + llp = channel_readl(dwc, LLP); + status_xfer = dma_readl(dw, RAW.XFER); + + if (status_xfer & dwc->mask) { + /* Everything we've submitted is done */ + dma_writel(dw, CLEAR.XFER, dwc->mask); + dwc_complete_all(dw, dwc); + return; + } + + dev_vdbg(&dwc->chan.dev, "scan_descriptors: llp=0x%x\n", llp); + + list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + if (desc->lli.llp == llp) + /* This one is currently in progress */ + return; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + if (child->lli.llp == llp) + /* Currently in progress */ + return; + + /* + * No descriptors so far seem to be in progress, i.e. + * this one must be done. + */ + dwc_descriptor_complete(dwc, desc); + } + + dev_err(&dwc->chan.dev, + "BUG: All descriptors done, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + if (!list_empty(&dwc->queue)) { + dwc_dostart(dwc, dwc_first_queued(dwc)); + list_splice_init(&dwc->queue, &dwc->active_list); + } +} + +static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) +{ + dev_printk(KERN_CRIT, &dwc->chan.dev, + " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, + lli->ctlhi, lli->ctllo); +} + +static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *bad_desc; + struct dw_desc *child; + + dwc_scan_descriptors(dw, dwc); + + /* + * The descriptor currently at the head of the active list is + * borked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = dwc_first_active(dwc); + list_del_init(&bad_desc->desc_node); + list_splice_init(&dwc->queue, dwc->active_list.prev); + + /* Clear the error flag and try to restart the controller */ + dma_writel(dw, CLEAR.ERROR, dwc->mask); + if (!list_empty(&dwc->active_list)) + dwc_dostart(dwc, dwc_first_active(dwc)); + + /* + * KERN_CRITICAL may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_printk(KERN_CRIT, &dwc->chan.dev, + "Bad descriptor submitted for DMA!\n"); + dev_printk(KERN_CRIT, &dwc->chan.dev, + " cookie: %d\n", bad_desc->txd.cookie); + dwc_dump_lli(dwc, &bad_desc->lli); + list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + dwc_dump_lli(dwc, &child->lli); + + /* Pretend the descriptor completed successfully */ + dwc_descriptor_complete(dwc, bad_desc); +} + +static void dw_dma_tasklet(unsigned long data) +{ + struct dw_dma *dw = (struct dw_dma *)data; + struct dw_dma_chan *dwc; + u32 status_block; + u32 status_xfer; + u32 status_err; + int i; + + status_block = dma_readl(dw, RAW.BLOCK); + status_xfer = dma_readl(dw, RAW.BLOCK); + status_err = dma_readl(dw, RAW.ERROR); + + dev_vdbg(dw->dma.dev, "tasklet: status_block=%x status_err=%x\n", + status_block, status_err); + + for (i = 0; i < dw->dma.chancnt; i++) { + dwc = &dw->chan[i]; + spin_lock(&dwc->lock); + if (status_err & (1 << i)) + dwc_handle_error(dw, dwc); + else if ((status_block | status_xfer) & (1 << i)) + dwc_scan_descriptors(dw, dwc); + spin_unlock(&dwc->lock); + } + + /* + * Re-enable interrupts. Block Complete interrupts are only + * enabled if the INT_EN bit in the descriptor is set. This + * will trigger a scan before the whole list is done. + */ + channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_set_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); +} + +static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) +{ + struct dw_dma *dw = dev_id; + u32 status; + + dev_vdbg(dw->dma.dev, "interrupt: status=0x%x\n", + dma_readl(dw, STATUS_INT)); + + /* + * Just disable the interrupts. We'll turn them back on in the + * softirq handler. + */ + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + status = dma_readl(dw, STATUS_INT); + if (status) { + dev_err(dw->dma.dev, + "BUG: Unexpected interrupts pending: 0x%x\n", + status); + + /* Try to recover */ + channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); + channel_clear_bit(dw, MASK.BLOCK, (1 << 8) - 1); + channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); + } + + tasklet_schedule(&dw->tasklet); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct dw_desc *desc = txd_to_dw_desc(tx); + struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); + dma_cookie_t cookie; + + spin_lock_bh(&dwc->lock); + cookie = dwc_assign_cookie(dwc, desc); + + /* + * REVISIT: We should attempt to chain as many descriptors as + * possible, perhaps even appending to those already submitted + * for DMA. But this is hard to do in a race-free manner. + */ + if (list_empty(&dwc->active_list)) { + dev_vdbg(&tx->chan->dev, "tx_submit: started %u\n", + desc->txd.cookie); + dwc_dostart(dwc, desc); + list_add_tail(&desc->desc_node, &dwc->active_list); + } else { + dev_vdbg(&tx->chan->dev, "tx_submit: queued %u\n", + desc->txd.cookie); + + list_add_tail(&desc->desc_node, &dwc->queue); + } + + spin_unlock_bh(&dwc->lock); + + return cookie; +} + +static struct dma_async_tx_descriptor * +dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_desc *desc; + struct dw_desc *first; + struct dw_desc *prev; + size_t xfer_count; + size_t offset; + unsigned int src_width; + unsigned int dst_width; + u32 ctllo; + + dev_vdbg(&chan->dev, "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n", + dest, src, len, flags); + + if (unlikely(!len)) { + dev_dbg(&chan->dev, "prep_dma_memcpy: length is zero!\n"); + return NULL; + } + + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!((src | dest | len) & 3)) + src_width = dst_width = 2; + else if (!((src | dest | len) & 1)) + src_width = dst_width = 1; + else + src_width = dst_width = 0; + + ctllo = DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(dst_width) + | DWC_CTLL_SRC_WIDTH(src_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2M; + prev = first = NULL; + + for (offset = 0; offset < len; offset += xfer_count << src_width) { + xfer_count = min_t(size_t, (len - offset) >> src_width, + DWC_MAX_COUNT); + + desc = dwc_desc_get(dwc); + if (!desc) + goto err_desc_get; + + desc->lli.sar = src + offset; + desc->lli.dar = dest + offset; + desc->lli.ctllo = ctllo; + desc->lli.ctlhi = xfer_count; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + } + + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + + first->txd.flags = flags; + first->len = len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static struct dma_async_tx_descriptor * +dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction direction, + unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_slave *dws = dwc->dws; + struct dw_desc *prev; + struct dw_desc *first; + u32 ctllo; + dma_addr_t reg; + unsigned int reg_width; + unsigned int mem_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(&chan->dev, "prep_dma_slave\n"); + + if (unlikely(!dws || !sg_len)) + return NULL; + + reg_width = dws->slave.reg_width; + prev = first = NULL; + + sg_len = dma_map_sg(chan->dev.parent, sgl, sg_len, direction); + + switch (direction) { + case DMA_TO_DEVICE: + ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2P); + reg = dws->slave.tx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len; + u32 mem; + + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(&chan->dev, + "not enough descriptors available\n"); + goto err_desc_get; + } + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.sar = mem; + desc->lli.dar = reg; + desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); + desc->lli.ctlhi = len >> mem_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, + sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + case DMA_FROM_DEVICE: + ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX + | DWC_CTLL_FC_P2M); + + reg = dws->slave.rx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len; + u32 mem; + + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(&chan->dev, + "not enough descriptors available\n"); + goto err_desc_get; + } + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.sar = reg; + desc->lli.dar = mem; + desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); + desc->lli.ctlhi = len >> reg_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, + sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + default: + return NULL; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + + first->len = total_len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static void dwc_terminate_all(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + /* + * This is only called when something went wrong elsewhere, so + * we don't really care about the data. Just disable the + * channel. We still have to poll the channel enable bit due + * to AHB/HSB limitations. + */ + spin_lock_bh(&dwc->lock); + + channel_clear_bit(dw, CH_EN, dwc->mask); + + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + /* active_list entries will end up before queued entries */ + list_splice_init(&dwc->queue, &list); + list_splice_init(&dwc->active_list, &list); + + spin_unlock_bh(&dwc->lock); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc); +} + +static enum dma_status +dwc_is_tx_complete(struct dma_chan *chan, + dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + int ret; + + last_complete = dwc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret != DMA_SUCCESS) { + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + + last_complete = dwc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + } + + if (done) + *done = last_complete; + if (used) + *used = last_used; + + return ret; +} + +static void dwc_issue_pending(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + spin_lock_bh(&dwc->lock); + if (!list_empty(&dwc->queue)) + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + spin_unlock_bh(&dwc->lock); +} + +static int dwc_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + struct dma_slave *slave; + struct dw_dma_slave *dws; + int i; + u32 cfghi; + u32 cfglo; + + dev_vdbg(&chan->dev, "alloc_chan_resources\n"); + + /* Channels doing slave DMA can only handle one client. */ + if (dwc->dws || client->slave) { + if (chan->client_count) + return -EBUSY; + } + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_dbg(&chan->dev, "DMA channel not idle?\n"); + return -EIO; + } + + dwc->completed = chan->cookie = 1; + + cfghi = DWC_CFGH_FIFO_MODE; + cfglo = 0; + + slave = client->slave; + if (slave) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!slave->dma_dev || slave->dma_dev != dw->dma.dev); + + dws = container_of(slave, struct dw_dma_slave, slave); + + dwc->dws = dws; + cfghi = dws->cfg_hi; + cfglo = dws->cfg_lo; + } else { + dwc->dws = NULL; + } + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); + + /* + * NOTE: some controllers may have additional features that we + * need to initialize here, like "scatter-gather" (which + * doesn't mean what you think it means), and status writeback. + */ + + spin_lock_bh(&dwc->lock); + i = dwc->descs_allocated; + while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + spin_unlock_bh(&dwc->lock); + + desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + if (!desc) { + dev_info(&chan->dev, + "only allocated %d descriptors\n", i); + spin_lock_bh(&dwc->lock); + break; + } + + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.tx_submit = dwc_tx_submit; + desc->txd.flags = DMA_CTRL_ACK; + INIT_LIST_HEAD(&desc->txd.tx_list); + desc->txd.phys = dma_map_single(chan->dev.parent, &desc->lli, + sizeof(desc->lli), DMA_TO_DEVICE); + dwc_desc_put(dwc, desc); + + spin_lock_bh(&dwc->lock); + i = ++dwc->descs_allocated; + } + + /* Enable interrupts */ + channel_set_bit(dw, MASK.XFER, dwc->mask); + channel_set_bit(dw, MASK.BLOCK, dwc->mask); + channel_set_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + dev_dbg(&chan->dev, + "alloc_chan_resources allocated %d descriptors\n", i); + + return i; +} + +static void dwc_free_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + dev_dbg(&chan->dev, "free_chan_resources (descs allocated=%u)\n", + dwc->descs_allocated); + + /* ASSERT: channel is idle */ + BUG_ON(!list_empty(&dwc->active_list)); + BUG_ON(!list_empty(&dwc->queue)); + BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); + + spin_lock_bh(&dwc->lock); + list_splice_init(&dwc->free_list, &list); + dwc->descs_allocated = 0; + dwc->dws = NULL; + + /* Disable interrupts */ + channel_clear_bit(dw, MASK.XFER, dwc->mask); + channel_clear_bit(dw, MASK.BLOCK, dwc->mask); + channel_clear_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) { + dev_vdbg(&chan->dev, " freeing descriptor %p\n", desc); + dma_unmap_single(chan->dev.parent, desc->txd.phys, + sizeof(desc->lli), DMA_TO_DEVICE); + kfree(desc); + } + + dev_vdbg(&chan->dev, "free_chan_resources done\n"); +} + +/*----------------------------------------------------------------------*/ + +static void dw_dma_off(struct dw_dma *dw) +{ + dma_writel(dw, CFG, 0); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) + cpu_relax(); +} + +static int __init dw_probe(struct platform_device *pdev) +{ + struct dw_dma_platform_data *pdata; + struct resource *io; + struct dw_dma *dw; + size_t size; + int irq; + int err; + int i; + + pdata = pdev->dev.platform_data; + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + size = sizeof(struct dw_dma); + size += pdata->nr_channels * sizeof(struct dw_dma_chan); + dw = kzalloc(size, GFP_KERNEL); + if (!dw) + return -ENOMEM; + + if (!request_mem_region(io->start, DW_REGLEN, pdev->dev.driver->name)) { + err = -EBUSY; + goto err_kfree; + } + + memset(dw, 0, sizeof *dw); + + dw->regs = ioremap(io->start, DW_REGLEN); + if (!dw->regs) { + err = -ENOMEM; + goto err_release_r; + } + + dw->clk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dw->clk)) { + err = PTR_ERR(dw->clk); + goto err_clk; + } + clk_enable(dw->clk); + + /* force dma off, just in case */ + dw_dma_off(dw); + + err = request_irq(irq, dw_dma_interrupt, 0, "dw_dmac", dw); + if (err) + goto err_irq; + + platform_set_drvdata(pdev, dw); + + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); + + dw->all_chan_mask = (1 << pdata->nr_channels) - 1; + + INIT_LIST_HEAD(&dw->dma.channels); + for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) { + struct dw_dma_chan *dwc = &dw->chan[i]; + + dwc->chan.device = &dw->dma; + dwc->chan.cookie = dwc->completed = 1; + dwc->chan.chan_id = i; + list_add_tail(&dwc->chan.device_node, &dw->dma.channels); + + dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; + spin_lock_init(&dwc->lock); + dwc->mask = 1 << i; + + INIT_LIST_HEAD(&dwc->active_list); + INIT_LIST_HEAD(&dwc->queue); + INIT_LIST_HEAD(&dwc->free_list); + + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + /* Clear/disable all interrupts on all channels. */ + dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); + dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); + dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); + dw->dma.dev = &pdev->dev; + dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; + dw->dma.device_free_chan_resources = dwc_free_chan_resources; + + dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; + + dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_terminate_all = dwc_terminate_all; + + dw->dma.device_is_tx_complete = dwc_is_tx_complete; + dw->dma.device_issue_pending = dwc_issue_pending; + + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", + pdev->dev.bus_id, dw->dma.chancnt); + + dma_async_device_register(&dw->dma); + + return 0; + +err_irq: + clk_disable(dw->clk); + clk_put(dw->clk); +err_clk: + iounmap(dw->regs); + dw->regs = NULL; +err_release_r: + release_resource(io); +err_kfree: + kfree(dw); + return err; +} + +static int __exit dw_remove(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + struct dw_dma_chan *dwc, *_dwc; + struct resource *io; + + dw_dma_off(dw); + dma_async_device_unregister(&dw->dma); + + free_irq(platform_get_irq(pdev, 0), dw); + tasklet_kill(&dw->tasklet); + + list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, + chan.device_node) { + list_del(&dwc->chan.device_node); + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + clk_disable(dw->clk); + clk_put(dw->clk); + + iounmap(dw->regs); + dw->regs = NULL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(io->start, DW_REGLEN); + + kfree(dw); + + return 0; +} + +static void dw_shutdown(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(platform_get_drvdata(pdev)); + clk_disable(dw->clk); +} + +static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(platform_get_drvdata(pdev)); + clk_disable(dw->clk); + return 0; +} + +static int dw_resume_early(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + clk_enable(dw->clk); + dma_writel(dw, CFG, DW_CFG_DMA_EN); + return 0; + +} + +static struct platform_driver dw_driver = { + .remove = __exit_p(dw_remove), + .shutdown = dw_shutdown, + .suspend_late = dw_suspend_late, + .resume_early = dw_resume_early, + .driver = { + .name = "dw_dmac", + }, +}; + +static int __init dw_init(void) +{ + return platform_driver_probe(&dw_driver, dw_probe); +} +module_init(dw_init); + +static void __exit dw_exit(void) +{ + platform_driver_unregister(&dw_driver); +} +module_exit(dw_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen "); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h new file mode 100644 index 000000000000..00fdd187bb0c --- /dev/null +++ b/drivers/dma/dw_dmac_regs.h @@ -0,0 +1,225 @@ +/* + * Driver for the Synopsys DesignWare AHB DMA Controller + * + * Copyright (C) 2005-2007 Atmel Corporation + * + * 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 + +#define DW_DMA_MAX_NR_CHANNELS 8 + +/* + * Redefine this macro to handle differences between 32- and 64-bit + * addressing, big vs. little endian, etc. + */ +#define DW_REG(name) u32 name; u32 __pad_##name + +/* Hardware register definitions. */ +struct dw_dma_chan_regs { + DW_REG(SAR); /* Source Address Register */ + DW_REG(DAR); /* Destination Address Register */ + DW_REG(LLP); /* Linked List Pointer */ + u32 CTL_LO; /* Control Register Low */ + u32 CTL_HI; /* Control Register High */ + DW_REG(SSTAT); + DW_REG(DSTAT); + DW_REG(SSTATAR); + DW_REG(DSTATAR); + u32 CFG_LO; /* Configuration Register Low */ + u32 CFG_HI; /* Configuration Register High */ + DW_REG(SGR); + DW_REG(DSR); +}; + +struct dw_dma_irq_regs { + DW_REG(XFER); + DW_REG(BLOCK); + DW_REG(SRC_TRAN); + DW_REG(DST_TRAN); + DW_REG(ERROR); +}; + +struct dw_dma_regs { + /* per-channel registers */ + struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; + + /* irq handling */ + struct dw_dma_irq_regs RAW; /* r */ + struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ + struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ + struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ + + DW_REG(STATUS_INT); /* r */ + + /* software handshaking */ + DW_REG(REQ_SRC); + DW_REG(REQ_DST); + DW_REG(SGL_REQ_SRC); + DW_REG(SGL_REQ_DST); + DW_REG(LAST_SRC); + DW_REG(LAST_DST); + + /* miscellaneous */ + DW_REG(CFG); + DW_REG(CH_EN); + DW_REG(ID); + DW_REG(TEST); + + /* optional encoded params, 0x3c8..0x3 */ +}; + +/* Bitfields in CTL_LO */ +#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ +#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ +#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) +#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ +#define DWC_CTLL_DST_DEC (1<<7) +#define DWC_CTLL_DST_FIX (2<<7) +#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ +#define DWC_CTLL_SRC_DEC (1<<9) +#define DWC_CTLL_SRC_FIX (2<<9) +#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ +#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) +#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ +#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ +#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ +#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ +#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ +/* plus 4 transfer types for peripheral-as-flow-controller */ +#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ +#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ +#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ +#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ + +/* Bitfields in CTL_HI */ +#define DWC_CTLH_DONE 0x00001000 +#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff + +/* Bitfields in CFG_LO. Platform-configurable bits are in */ +#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ +#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ +#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ +#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_MAX_BURST(x) ((x) << 20) +#define DWC_CFGL_RELOAD_SAR (1 << 30) +#define DWC_CFGL_RELOAD_DAR (1 << 31) + +/* Bitfields in CFG_HI. Platform-configurable bits are in */ +#define DWC_CFGH_DS_UPD_EN (1 << 5) +#define DWC_CFGH_SS_UPD_EN (1 << 6) + +/* Bitfields in SGR */ +#define DWC_SGR_SGI(x) ((x) << 0) +#define DWC_SGR_SGC(x) ((x) << 20) + +/* Bitfields in DSR */ +#define DWC_DSR_DSI(x) ((x) << 0) +#define DWC_DSR_DSC(x) ((x) << 20) + +/* Bitfields in CFG */ +#define DW_CFG_DMA_EN (1 << 0) + +#define DW_REGLEN 0x400 + +struct dw_dma_chan { + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + + spinlock_t lock; + + /* these other elements are all protected by lock */ + dma_cookie_t completed; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + + struct dw_dma_slave *dws; + + unsigned int descs_allocated; +}; + +static inline struct dw_dma_chan_regs __iomem * +__dwc_regs(struct dw_dma_chan *dwc) +{ + return dwc->ch_regs; +} + +#define channel_readl(dwc, name) \ + __raw_readl(&(__dwc_regs(dwc)->name)) +#define channel_writel(dwc, name, val) \ + __raw_writel((val), &(__dwc_regs(dwc)->name)) + +static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct dw_dma_chan, chan); +} + + +struct dw_dma { + struct dma_device dma; + void __iomem *regs; + struct tasklet_struct tasklet; + struct clk *clk; + + u8 all_chan_mask; + + struct dw_dma_chan chan[0]; +}; + +static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) +{ + return dw->regs; +} + +#define dma_readl(dw, name) \ + __raw_readl(&(__dw_regs(dw)->name)) +#define dma_writel(dw, name, val) \ + __raw_writel((val), &(__dw_regs(dw)->name)) + +#define channel_set_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | (mask)) +#define channel_clear_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | 0) + +static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct dw_dma, dma); +} + +/* LLI == Linked List Item; a.k.a. DMA block descriptor */ +struct dw_lli { + /* values that are not changed by hardware */ + dma_addr_t sar; + dma_addr_t dar; + dma_addr_t llp; /* chain to next lli */ + u32 ctllo; + /* values that may get written back: */ + u32 ctlhi; + /* sstat and dstat can snapshot peripheral register state. + * silicon config may discard either or both... + */ + u32 sstat; + u32 dstat; +}; + +struct dw_desc { + /* FIRST values the hardware uses */ + struct dw_lli lli; + + /* THEN values for driver housekeeping */ + struct list_head desc_node; + struct dma_async_tx_descriptor txd; + size_t len; +}; + +static inline struct dw_desc * +txd_to_dw_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct dw_desc, txd); +} diff --git a/include/asm-avr32/arch-at32ap/at32ap700x.h b/include/asm-avr32/arch-at32ap/at32ap700x.h index 31e48b0e7324..d18a3053be0d 100644 --- a/include/asm-avr32/arch-at32ap/at32ap700x.h +++ b/include/asm-avr32/arch-at32ap/at32ap700x.h @@ -30,4 +30,20 @@ #define GPIO_PIN_PD(N) (GPIO_PIOD_BASE + (N)) #define GPIO_PIN_PE(N) (GPIO_PIOE_BASE + (N)) + +/* + * DMAC peripheral hardware handshaking interfaces, used with dw_dmac + */ +#define DMAC_MCI_RX 0 +#define DMAC_MCI_TX 1 +#define DMAC_DAC_TX 2 +#define DMAC_AC97_A_RX 3 +#define DMAC_AC97_A_TX 4 +#define DMAC_AC97_B_RX 5 +#define DMAC_AC97_B_TX 6 +#define DMAC_DMAREQ_0 7 +#define DMAC_DMAREQ_1 8 +#define DMAC_DMAREQ_2 9 +#define DMAC_DMAREQ_3 10 + #endif /* __ASM_ARCH_AT32AP700X_H__ */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h new file mode 100644 index 000000000000..04d217b442bf --- /dev/null +++ b/include/linux/dw_dmac.h @@ -0,0 +1,62 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on + * AVR32 systems.) + * + * Copyright (C) 2007 Atmel Corporation + * + * 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 DW_DMAC_H +#define DW_DMAC_H + +#include + +/** + * struct dw_dma_platform_data - Controller configuration parameters + * @nr_channels: Number of channels supported by hardware (max 8) + */ +struct dw_dma_platform_data { + unsigned int nr_channels; +}; + +/** + * struct dw_dma_slave - Controller-specific information about a slave + * @slave: Generic information about the slave + * @ctl_lo: Platform-specific initializer for the CTL_LO register + * @cfg_hi: Platform-specific initializer for the CFG_HI register + * @cfg_lo: Platform-specific initializer for the CFG_LO register + */ +struct dw_dma_slave { + struct dma_slave slave; + u32 cfg_hi; + u32 cfg_lo; +}; + +/* Platform-configurable bits in CFG_HI */ +#define DWC_CFGH_FCMODE (1 << 0) +#define DWC_CFGH_FIFO_MODE (1 << 1) +#define DWC_CFGH_PROTCTL(x) ((x) << 2) +#define DWC_CFGH_SRC_PER(x) ((x) << 7) +#define DWC_CFGH_DST_PER(x) ((x) << 11) + +/* Platform-configurable bits in CFG_LO */ +#define DWC_CFGL_PRIO(x) ((x) << 5) /* priority */ +#define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */ +#define DWC_CFGL_LOCK_CH_BLOCK (1 << 12) +#define DWC_CFGL_LOCK_CH_XACT (2 << 12) +#define DWC_CFGL_LOCK_BUS_XFER (0 << 14) /* scope of LOCK_BUS */ +#define DWC_CFGL_LOCK_BUS_BLOCK (1 << 14) +#define DWC_CFGL_LOCK_BUS_XACT (2 << 14) +#define DWC_CFGL_LOCK_CH (1 << 15) /* channel lockout */ +#define DWC_CFGL_LOCK_BUS (1 << 16) /* busmaster lockout */ +#define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */ +#define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */ + +static inline struct dw_dma_slave *to_dw_dma_slave(struct dma_slave *slave) +{ + return container_of(slave, struct dw_dma_slave, slave); +} + +#endif /* DW_DMAC_H */ -- cgit v1.2.3 From 11a100f844f6096787ab20e19f17d72abc957a8f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 15:36:57 -0700 Subject: vlan: avoid header copying and linearisation where possible - vlan_dev_reorder_header() is only called on the receive path after calling skb_share_check(). This means we can use skb_cow() since all we need is a writable header. - vlan_dev_hard_header() includes a work-around for some apparently broken out of tree MPLS code. The hard_header functions can expect to always have a headroom of at least there own hard_header_len available, so the reallocation check is unnecessary. - __vlan_put_tag() can use skb_cow_head() to avoid the skb_unshare() copy when the header is writable. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 18 +++--------------- net/8021q/vlan_dev.c | 34 +++++----------------------------- 2 files changed, 8 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index d36515dae62f..93f5d9b0e9f9 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -185,22 +185,10 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; - if (skb_headroom(skb) < VLAN_HLEN) { - struct sk_buff *sk_tmp = skb; - skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN); - kfree_skb(sk_tmp); - if (!skb) { - printk(KERN_ERR "vlan: failed to realloc headroom\n"); - return NULL; - } - } else { - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "vlan: failed to unshare skbuff\n"); - return NULL; - } + if (skb_cow_head(skb, VLAN_HLEN) < 0) { + kfree_skb(skb); + return NULL; } - veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); /* Move the mac addresses to the beginning of the new header. */ diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 2ccac6bea57e..b6e52c025fd8 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -74,11 +74,8 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb) static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) { if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { - if (skb_shared(skb) || skb_cloned(skb)) { - struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); - kfree_skb(skb); - skb = nskb; - } + if (skb_cow(skb, skb_headroom(skb)) < 0) + skb = NULL; if (skb) { /* Lifted from Gleb's VLAN code... */ memmove(skb->data - ETH_HLEN, @@ -262,12 +259,14 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, u16 vlan_tci = 0; int rc = 0; int build_vlan_header = 0; - struct net_device *vdev = dev; pr_debug("%s: skb: %p type: %hx len: %u vlan_id: %hx, daddr: %p\n", __func__, skb, type, len, vlan_dev_info(dev)->vlan_id, daddr); + if (WARN_ON(skb_headroom(skb) < dev->hard_header_len)) + return -ENOSPC; + /* build vlan header only if re_order_header flag is NOT set. This * fixes some programs that get confused when they see a VLAN device * sending a frame that is VLAN encoded (the consensus is that the VLAN @@ -316,29 +315,6 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, dev = vlan_dev_info(dev)->real_dev; - /* MPLS can send us skbuffs w/out enough space. This check will grow - * the skb if it doesn't have enough headroom. Not a beautiful solution, - * so I'll tick a counter so that users can know it's happening... - * If they care... - */ - - /* NOTE: This may still break if the underlying device is not the final - * device (and thus there are more headers to add...) It should work for - * good-ole-ethernet though. - */ - if (skb_headroom(skb) < dev->hard_header_len) { - struct sk_buff *sk_tmp = skb; - skb = skb_realloc_headroom(sk_tmp, dev->hard_header_len); - kfree_skb(sk_tmp); - if (skb == NULL) { - struct net_device_stats *stats = &vdev->stats; - stats->tx_dropped++; - return -ENOMEM; - } - vlan_dev_info(vdev)->cnt_inc_headroom_on_tx++; - pr_debug("%s: %s: had to grow skb\n", __func__, vdev->name); - } - if (build_vlan_header) { /* Now make the underlying real hard header */ rc = dev_hard_header(skb, dev, ETH_P_8021Q, daddr, saddr, -- cgit v1.2.3 From bb949fbd1878973c3539d9aecff52f284482a937 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 16:55:56 -0700 Subject: netdev: Create netdev_queue abstraction. A netdev_queue is an entity managed by a qdisc. Currently there is one RX and one TX queue, and a netdev_queue merely contains a backpointer to the net_device. The Qdisc struct is augmented with a netdev_queue pointer as well. Eventually the 'dev' Qdisc member will go away and we will have the resulting hierarchy: net_device --> netdev_queue --> Qdisc Also, qdisc_alloc() and qdisc_create_dflt() now take a netdev_queue pointer argument. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 7 +++++++ include/net/sch_generic.h | 6 +++++- net/core/dev.c | 8 ++++++++ net/mac80211/wme.c | 6 ++++-- net/sched/sch_api.c | 12 +++++++----- net/sched/sch_atm.c | 6 ++++-- net/sched/sch_cbq.c | 9 ++++++--- net/sched/sch_dsmark.c | 6 ++++-- net/sched/sch_fifo.c | 3 ++- net/sched/sch_generic.c | 14 ++++++++++---- net/sched/sch_hfsc.c | 9 ++++++--- net/sched/sch_htb.c | 11 +++++++---- net/sched/sch_netem.c | 3 ++- net/sched/sch_prio.c | 3 ++- 14 files changed, 74 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e009c6fbf5cd..515fd25bf0fc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -448,6 +448,10 @@ static inline void napi_synchronize(const struct napi_struct *n) # define napi_synchronize(n) barrier() #endif +struct netdev_queue { + struct net_device *dev; +}; + /* * The DEVICE structure. * Actually, this whole structure is a big mistake. It mixes I/O @@ -624,6 +628,9 @@ struct net_device unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ + struct netdev_queue rx_queue; + struct netdev_queue tx_queue; + /* ingress path synchronizer */ spinlock_t ingress_lock; struct Qdisc *qdisc_ingress; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 073f2580b83b..0ab53c575f87 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -37,6 +37,7 @@ struct Qdisc u32 parent; atomic_t refcnt; struct sk_buff_head q; + struct netdev_queue *dev_queue; struct net_device *dev; struct list_head list; @@ -216,8 +217,11 @@ extern void dev_deactivate(struct net_device *dev); extern void qdisc_reset(struct Qdisc *qdisc); extern void qdisc_destroy(struct Qdisc *qdisc); extern void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n); -extern struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops); +extern struct Qdisc *qdisc_alloc(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc_ops *ops); extern struct Qdisc *qdisc_create_dflt(struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc_ops *ops, u32 parentid); extern void tcf_destroy(struct tcf_proto *tp); extern void tcf_destroy_chain(struct tcf_proto **fl); diff --git a/net/core/dev.c b/net/core/dev.c index 75933932463d..9b281c906eb0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4072,6 +4072,12 @@ static struct net_device_stats *internal_stats(struct net_device *dev) return &dev->stats; } +static void netdev_init_queues(struct net_device *dev) +{ + dev->rx_queue.dev = dev; + dev->tx_queue.dev = dev; +} + /** * alloc_netdev_mq - allocate network device * @sizeof_priv: size of private data to allocate space for @@ -4124,6 +4130,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev->egress_subqueue_count = queue_count; dev->gso_max_size = GSO_MAX_SIZE; + netdev_init_queues(dev); + dev->get_stats = internal_stats; netpoll_netdev_init(dev); setup(dev); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 5c666f7eda8f..770f1c09b793 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -359,7 +359,8 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) /* create child queues */ for (i = 0; i < QD_NUM(hw); i++) { skb_queue_head_init(&q->requeued[i]); - q->queues[i] = qdisc_create_dflt(qd->dev, &pfifo_qdisc_ops, + q->queues[i] = qdisc_create_dflt(qd->dev, qd->dev_queue, + &pfifo_qdisc_ops, qd->handle); if (!q->queues[i]) { q->queues[i] = &noop_qdisc; @@ -575,7 +576,8 @@ void ieee80211_install_qdisc(struct net_device *dev) { struct Qdisc *qdisc; - qdisc = qdisc_create_dflt(dev, &wme_qdisc_ops, TC_H_ROOT); + qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + &wme_qdisc_ops, TC_H_ROOT); if (!qdisc) { printk(KERN_ERR "%s: qdisc installation failed\n", dev->name); return; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 69e918bb4278..b86c98bd06a3 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -552,8 +552,8 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, */ static struct Qdisc * -qdisc_create(struct net_device *dev, u32 parent, u32 handle, - struct nlattr **tca, int *errp) +qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, + u32 parent, u32 handle, struct nlattr **tca, int *errp) { int err; struct nlattr *kind = tca[TCA_KIND]; @@ -593,7 +593,7 @@ qdisc_create(struct net_device *dev, u32 parent, u32 handle, if (ops == NULL) goto err_out; - sch = qdisc_alloc(dev, ops); + sch = qdisc_alloc(dev, dev_queue, ops); if (IS_ERR(sch)) { err = PTR_ERR(sch); goto err_out2; @@ -892,10 +892,12 @@ create_n_graft: if (!(n->nlmsg_flags&NLM_F_CREATE)) return -ENOENT; if (clid == TC_H_INGRESS) - q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_parent, + q = qdisc_create(dev, &dev->rx_queue, + tcm->tcm_parent, tcm->tcm_parent, tca, &err); else - q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_handle, + q = qdisc_create(dev, &dev->tx_queue, + tcm->tcm_parent, tcm->tcm_handle, tca, &err); if (q == NULL) { if (err == -EAGAIN) diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index db0e23ae85f8..3dddab531d5a 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -296,7 +296,8 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, goto err_out; } flow->filter_list = NULL; - flow->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid); + flow->q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid); if (!flow->q) flow->q = &noop_qdisc; pr_debug("atm_tc_change: qdisc %p\n", flow->q); @@ -555,7 +556,8 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt); p->flows = &p->link; - p->link.q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, sch->handle); + p->link.q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (!p->link.q) p->link.q = &noop_qdisc; pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q); diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 968b4c73c9c1..d360dcd0818b 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1401,7 +1401,8 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt) q->link.sibling = &q->link; q->link.common.classid = sch->handle; q->link.qdisc = sch; - if (!(q->link.q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + if (!(q->link.q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle))) q->link.q = &noop_qdisc; @@ -1645,7 +1646,8 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (cl) { if (new == NULL) { - new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, cl->common.classid); if (new == NULL) return -ENOBUFS; @@ -1877,7 +1879,8 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t cl->R_tab = rtab; rtab = NULL; cl->refcnt = 1; - if (!(cl->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid))) + if (!(cl->q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid))) cl->q = &noop_qdisc; cl->common.classid = classid; cl->tparent = parent; diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index c4c1317cd47d..c955ba24e5cf 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -60,7 +60,8 @@ static int dsmark_graft(struct Qdisc *sch, unsigned long arg, sch, p, new, old); if (new == NULL) { - new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (new == NULL) new = &noop_qdisc; @@ -390,7 +391,8 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt) p->default_index = default_index; p->set_tc_index = nla_get_flag(tb[TCA_DSMARK_SET_TC_INDEX]); - p->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, sch->handle); + p->q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (p->q == NULL) p->q = &noop_qdisc; diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index 82d7d7bbbb16..779eae85faf0 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -137,7 +137,8 @@ struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops, struct Qdisc *q; int err = -ENOMEM; - q = qdisc_create_dflt(sch->dev, ops, TC_H_MAKE(sch->handle, 1)); + q = qdisc_create_dflt(sch->dev, sch->dev_queue, + ops, TC_H_MAKE(sch->handle, 1)); if (q) { err = fifo_set_limit(q, limit); if (err < 0) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 13afa7214392..d97086480893 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -440,7 +440,9 @@ static struct Qdisc_ops pfifo_fast_ops __read_mostly = { .owner = THIS_MODULE, }; -struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops) +struct Qdisc *qdisc_alloc(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc_ops *ops) { void *p; struct Qdisc *sch; @@ -462,6 +464,7 @@ struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops) sch->ops = ops; sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; + sch->dev_queue = dev_queue; sch->dev = dev; dev_hold(dev); atomic_set(&sch->refcnt, 1); @@ -471,12 +474,14 @@ errout: return ERR_PTR(err); } -struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops, +struct Qdisc * qdisc_create_dflt(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc_ops *ops, unsigned int parentid) { struct Qdisc *sch; - sch = qdisc_alloc(dev, ops); + sch = qdisc_alloc(dev, dev_queue, ops); if (IS_ERR(sch)) goto errout; sch->stats_lock = &dev->queue_lock; @@ -545,7 +550,8 @@ void dev_activate(struct net_device *dev) if (dev->qdisc_sleeping == &noop_qdisc) { struct Qdisc *qdisc; if (dev->tx_queue_len) { - qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops, + qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + &pfifo_fast_ops, TC_H_ROOT); if (qdisc == NULL) { printk(KERN_INFO "%s: activation failed\n", dev->name); diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 3a8267246a4f..5a22fec4eadd 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1083,7 +1083,8 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, cl->refcnt = 1; cl->sched = q; cl->cl_parent = parent; - cl->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid); + cl->qdisc = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid); if (cl->qdisc == NULL) cl->qdisc = &noop_qdisc; INIT_LIST_HEAD(&cl->children); @@ -1201,7 +1202,8 @@ hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (cl->level > 0) return -EINVAL; if (new == NULL) { - new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, cl->cl_common.classid); if (new == NULL) new = &noop_qdisc; @@ -1443,7 +1445,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt) q->root.cl_common.classid = sch->handle; q->root.refcnt = 1; q->root.sched = q; - q->root.qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + q->root.qdisc = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (q->root.qdisc == NULL) q->root.qdisc = &noop_qdisc; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index ee8b4ffe110c..956a67f66b9c 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1129,7 +1129,8 @@ static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (cl && !cl->level) { if (new == NULL && - (new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + (new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, cl->common.classid)) == NULL) return -ENOBUFS; @@ -1256,8 +1257,9 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg) return -EBUSY; if (!cl->level && htb_parent_last_child(cl)) { - new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, - cl->parent->common.classid); + new_q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, + cl->parent->common.classid); last_child = 1; } @@ -1376,7 +1378,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, /* create leaf qdisc early because it uses kmalloc(GFP_KERNEL) so that can't be used inside of sch_tree_lock -- thanks to Karlis Peisenieks */ - new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid); + new_q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid); sch_tree_lock(sch); if (parent && !parent->level) { unsigned int qlen = parent->un.leaf.q->q.qlen; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 24697667247c..aa7a04e32ae9 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -536,7 +536,8 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt) qdisc_watchdog_init(&q->watchdog, sch); - q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops, + q->qdisc = qdisc_create_dflt(sch->dev, sch->dev_queue, + &tfifo_qdisc_ops, TC_H_MAKE(sch->handle, 1)); if (!q->qdisc) { pr_debug("netem: qdisc create failed\n"); diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 5532f1031ab5..ca58a039208e 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -281,7 +281,8 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt) for (i=0; ibands; i++) { if (q->queues[i] == &noop_qdisc) { struct Qdisc *child; - child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + child = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, TC_H_MAKE(sch->handle, i + 1)); if (child) { sch_tree_lock(sch); -- cgit v1.2.3 From dc2b48475a0a36f8b3bbb2da60d3a006dc5c2c84 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 17:18:23 -0700 Subject: netdev: Move queue_lock into struct netdev_queue. The lock is now an attribute of the device queue. One thing to notice is that "suspicious" places emerge which will need specific training about multiple queue handling. They are so marked with explicit "netdev->rx_queue" and "netdev->tx_queue" references. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 8 ++++---- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 33 ++++++++++++++++++++++----------- net/mac80211/main.c | 10 +++++----- net/mac80211/wme.c | 2 +- net/sched/sch_api.c | 2 +- net/sched/sch_cbq.c | 8 ++++---- net/sched/sch_generic.c | 40 ++++++++++++++++++++-------------------- net/sched/sch_hfsc.c | 4 ++-- net/sched/sch_htb.c | 16 ++++++++-------- net/sched/sch_netem.c | 4 ++-- net/sched/sch_teql.c | 4 ++-- 12 files changed, 73 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index af233b591534..bc3de272a829 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -229,12 +229,12 @@ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); /* - * dev_ifb->queue_lock is usually taken after dev->ingress_lock, + * dev_ifb->tx_queue.lock is usually taken after dev->ingress_lock, * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev->queue_lock with dev_ifb->ingress_lock. + * ifb doesn't take dev->tx_queue.lock with dev_ifb->ingress_lock. * But lockdep should know that ifb has different locks from dev. */ -static struct lock_class_key ifb_queue_lock_key; +static struct lock_class_key ifb_tx_queue_lock_key; static struct lock_class_key ifb_ingress_lock_key; @@ -258,7 +258,7 @@ static int __init ifb_init_one(int index) if (err < 0) goto err; - lockdep_set_class(&dev_ifb->queue_lock, &ifb_queue_lock_key); + lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); lockdep_set_class(&dev_ifb->ingress_lock, &ifb_ingress_lock_key); return 0; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 515fd25bf0fc..e835acacb479 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -449,6 +449,7 @@ static inline void napi_synchronize(const struct napi_struct *n) #endif struct netdev_queue { + spinlock_t lock; struct net_device *dev; }; @@ -629,7 +630,7 @@ struct net_device unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ struct netdev_queue rx_queue; - struct netdev_queue tx_queue; + struct netdev_queue tx_queue ____cacheline_aligned_in_smp; /* ingress path synchronizer */ spinlock_t ingress_lock; @@ -639,7 +640,6 @@ struct net_device * Cache line mostly used on queue transmit path (qdisc) */ /* device queue lock */ - spinlock_t queue_lock ____cacheline_aligned_in_smp; struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; diff --git a/net/core/dev.c b/net/core/dev.c index 9b281c906eb0..05011048b86c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1667,6 +1667,7 @@ out_kfree_skb: int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; + struct netdev_queue *txq; struct Qdisc *q; int rc = -ENOMEM; @@ -1699,14 +1700,15 @@ int dev_queue_xmit(struct sk_buff *skb) } gso: - spin_lock_prefetch(&dev->queue_lock); + txq = &dev->tx_queue; + spin_lock_prefetch(&txq->lock); /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ rcu_read_lock_bh(); - /* Updates of qdisc are serialized by queue_lock. + /* Updates of qdisc are serialized by queue->lock. * The struct Qdisc which is pointed to by qdisc is now a * rcu structure - it may be accessed without acquiring * a lock (but the structure may be stale.) The freeing of the @@ -1714,7 +1716,7 @@ gso: * more references to it. * * If the qdisc has an enqueue function, we still need to - * hold the queue_lock before calling it, since queue_lock + * hold the queue->lock before calling it, since queue->lock * also serializes access to the device queue. */ @@ -1724,19 +1726,19 @@ gso: #endif if (q->enqueue) { /* Grab device queue */ - spin_lock(&dev->queue_lock); + spin_lock(&txq->lock); q = dev->qdisc; if (q->enqueue) { /* reset queue_mapping to zero */ skb_set_queue_mapping(skb, 0); rc = q->enqueue(skb, q); qdisc_run(dev); - spin_unlock(&dev->queue_lock); + spin_unlock(&txq->lock); rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; goto out; } - spin_unlock(&dev->queue_lock); + spin_unlock(&txq->lock); } /* The device has no queue. Common case for software devices: @@ -1919,14 +1921,17 @@ static void net_tx_action(struct softirq_action *h) while (head) { struct net_device *dev = head; + struct netdev_queue *txq; head = head->next_sched; + txq = &dev->tx_queue; + smp_mb__before_clear_bit(); clear_bit(__LINK_STATE_SCHED, &dev->state); - if (spin_trylock(&dev->queue_lock)) { + if (spin_trylock(&txq->lock)) { qdisc_run(dev); - spin_unlock(&dev->queue_lock); + spin_unlock(&txq->lock); } else { netif_schedule(dev); } @@ -3787,7 +3792,6 @@ int register_netdevice(struct net_device *dev) BUG_ON(!dev_net(dev)); net = dev_net(dev); - spin_lock_init(&dev->queue_lock); spin_lock_init(&dev->_xmit_lock); netdev_set_lockdep_class(&dev->_xmit_lock, dev->type); dev->xmit_lock_owner = -1; @@ -4072,10 +4076,17 @@ static struct net_device_stats *internal_stats(struct net_device *dev) return &dev->stats; } +static void netdev_init_one_queue(struct net_device *dev, + struct netdev_queue *queue) +{ + spin_lock_init(&queue->lock); + queue->dev = dev; +} + static void netdev_init_queues(struct net_device *dev) { - dev->rx_queue.dev = dev; - dev->tx_queue.dev = dev; + netdev_init_one_queue(dev, &dev->rx_queue); + netdev_init_one_queue(dev, &dev->tx_queue); } /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index cf477ad39dac..12aeaf78ae75 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -636,7 +636,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) /* ensure that TX flow won't interrupt us * until the end of the call to requeue function */ - spin_lock_bh(&local->mdev->queue_lock); + spin_lock_bh(&local->mdev->tx_queue.lock); /* create a new queue for this aggregation */ ret = ieee80211_ht_agg_queue_add(local, sta, tid); @@ -675,7 +675,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) /* Will put all the packets in the new SW queue */ ieee80211_requeue(local, ieee802_1d_to_ac[tid]); - spin_unlock_bh(&local->mdev->queue_lock); + spin_unlock_bh(&local->mdev->tx_queue.lock); spin_unlock_bh(&sta->lock); /* send an addBA request */ @@ -701,7 +701,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) err_unlock_queue: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; - spin_unlock_bh(&local->mdev->queue_lock); + spin_unlock_bh(&local->mdev->tx_queue.lock); ret = -EBUSY; err_unlock_sta: spin_unlock_bh(&sta->lock); @@ -875,10 +875,10 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) /* avoid ordering issues: we are the only one that can modify * the content of the qdiscs */ - spin_lock_bh(&local->mdev->queue_lock); + spin_lock_bh(&local->mdev->tx_queue.lock); /* remove the queue for this aggregation */ ieee80211_ht_agg_queue_remove(local, sta, tid, 1); - spin_unlock_bh(&local->mdev->queue_lock); + spin_unlock_bh(&local->mdev->tx_queue.lock); /* we just requeued the all the frames that were in the removed * queue, and since we might miss a softirq we do netif_schedule. diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 2fbc171130bf..59ed9cae66b9 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -648,7 +648,7 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, } /** - * the caller needs to hold local->mdev->queue_lock + * the caller needs to hold local->mdev->tx_queue.lock */ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid, diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 1f893082a4f6..2a1834f8c7d8 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -606,7 +606,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, sch->stats_lock = &dev->ingress_lock; handle = TC_H_MAKE(TC_H_INGRESS, 0); } else { - sch->stats_lock = &dev->queue_lock; + sch->stats_lock = &dev_queue->lock; if (handle == 0) { handle = qdisc_alloc_handle(dev); err = -ENOMEM; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 9f2ace585fd6..99ce3da2b0a4 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1746,10 +1746,10 @@ static void cbq_put(struct Qdisc *sch, unsigned long arg) #ifdef CONFIG_NET_CLS_ACT struct cbq_sched_data *q = qdisc_priv(sch); - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); if (q->rx_class == cl) q->rx_class = NULL; - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); #endif cbq_destroy_class(sch, cl); @@ -1828,7 +1828,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t if (tca[TCA_RATE]) gen_replace_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE]); return 0; } @@ -1919,7 +1919,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t if (tca[TCA_RATE]) gen_new_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, tca[TCA_RATE]); + &sch->dev_queue->lock, tca[TCA_RATE]); *arg = (unsigned long)cl; return 0; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index b626a4f32b6b..ee8f9f78a095 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -29,31 +29,31 @@ /* Main transmission queue. */ /* Modifications to data participating in scheduling must be protected with - * dev->queue_lock spinlock. + * queue->lock spinlock. * * The idea is the following: * - enqueue, dequeue are serialized via top level device - * spinlock dev->queue_lock. + * spinlock queue->lock. * - ingress filtering is serialized via top level device * spinlock dev->ingress_lock. * - updates to tree and tree walking are only done under the rtnl mutex. */ void qdisc_lock_tree(struct net_device *dev) - __acquires(dev->queue_lock) + __acquires(dev->tx_queue.lock) __acquires(dev->ingress_lock) { - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); spin_lock(&dev->ingress_lock); } EXPORT_SYMBOL(qdisc_lock_tree); void qdisc_unlock_tree(struct net_device *dev) __releases(dev->ingress_lock) - __releases(dev->queue_lock) + __releases(dev->tx_queue.lock) { spin_unlock(&dev->ingress_lock); - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); } EXPORT_SYMBOL(qdisc_unlock_tree); @@ -118,15 +118,15 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, } /* - * NOTE: Called under dev->queue_lock with locally disabled BH. + * NOTE: Called under queue->lock with locally disabled BH. * * __LINK_STATE_QDISC_RUNNING guarantees only one CPU can process this - * device at a time. dev->queue_lock serializes queue accesses for + * device at a time. queue->lock serializes queue accesses for * this device AND dev->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. * - * dev->queue_lock and netif_tx_lock are mutually exclusive, + * queue->lock and netif_tx_lock are mutually exclusive, * if one is grabbed, another must be free. * * Note, that this procedure can be called by a watchdog timer @@ -148,14 +148,14 @@ static inline int qdisc_restart(struct net_device *dev) /* And release queue */ - spin_unlock(&dev->queue_lock); + spin_unlock(&q->dev_queue->lock); HARD_TX_LOCK(dev, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) ret = dev_hard_start_xmit(skb, dev); HARD_TX_UNLOCK(dev); - spin_lock(&dev->queue_lock); + spin_lock(&q->dev_queue->lock); q = dev->qdisc; switch (ret) { @@ -482,7 +482,7 @@ struct Qdisc * qdisc_create_dflt(struct net_device *dev, sch = qdisc_alloc(dev_queue, ops); if (IS_ERR(sch)) goto errout; - sch->stats_lock = &dev->queue_lock; + sch->stats_lock = &dev_queue->lock; sch->parent = parentid; if (!ops->init || ops->init(sch, NULL) == 0) @@ -494,7 +494,7 @@ errout: } EXPORT_SYMBOL(qdisc_create_dflt); -/* Under dev->queue_lock and BH! */ +/* Under queue->lock and BH! */ void qdisc_reset(struct Qdisc *qdisc) { @@ -514,7 +514,7 @@ static void __qdisc_destroy(struct rcu_head *head) kfree((char *) qdisc - qdisc->padded); } -/* Under dev->queue_lock and BH! */ +/* Under queue->lock and BH! */ void qdisc_destroy(struct Qdisc *qdisc) { @@ -566,13 +566,13 @@ void dev_activate(struct net_device *dev) /* Delay activation until next carrier-on event */ return; - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping); if (dev->qdisc != &noqueue_qdisc) { dev->trans_start = jiffies; dev_watchdog_up(dev); } - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); } void dev_deactivate(struct net_device *dev) @@ -581,7 +581,7 @@ void dev_deactivate(struct net_device *dev) struct sk_buff *skb; int running; - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); qdisc = dev->qdisc; dev->qdisc = &noop_qdisc; @@ -589,7 +589,7 @@ void dev_deactivate(struct net_device *dev) skb = dev->gso_skb; dev->gso_skb = NULL; - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); kfree_skb(skb); @@ -607,9 +607,9 @@ void dev_deactivate(struct net_device *dev) * Double-check inside queue lock to ensure that all effects * of the queue run are visible when we return. */ - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); running = test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); /* * The running flag should never be set at this point because diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 333525422f45..997d520ca580 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1045,7 +1045,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (tca[TCA_RATE]) gen_replace_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE]); return 0; } @@ -1104,7 +1104,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (tca[TCA_RATE]) gen_new_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, tca[TCA_RATE]); + &sch->dev_queue->lock, tca[TCA_RATE]); *arg = (unsigned long)cl; return 0; } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 31f7d1536e6d..c8ca54cc26b0 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1043,7 +1043,7 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); gopt.direct_pkts = q->direct_pkts; gopt.version = HTB_VER; @@ -1057,11 +1057,11 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) NLA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt); nla_nest_end(skb, nest); - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); return skb->len; nla_put_failure: - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); nla_nest_cancel(skb, nest); return -1; } @@ -1073,7 +1073,7 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, struct nlattr *nest; struct tc_htb_opt opt; - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); tcm->tcm_parent = cl->parent ? cl->parent->common.classid : TC_H_ROOT; tcm->tcm_handle = cl->common.classid; if (!cl->level && cl->un.leaf.q) @@ -1095,11 +1095,11 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, NLA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt); nla_nest_end(skb, nest); - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); return skb->len; nla_put_failure: - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); nla_nest_cancel(skb, nest); return -1; } @@ -1365,7 +1365,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, goto failure; gen_new_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE] ? : &est.nla); cl->refcnt = 1; cl->children = 0; @@ -1420,7 +1420,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, } else { if (tca[TCA_RATE]) gen_replace_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE]); sch_tree_lock(sch); } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 790582960444..71b73c528f9b 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -333,9 +333,9 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) for (i = 0; i < n; i++) d->table[i] = data[i]; - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); d = xchg(&q->delay_dist, d); - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); kfree(d); return 0; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index b3fc82623fc6..4f3054e8e1ab 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -154,9 +154,9 @@ teql_destroy(struct Qdisc* sch) master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { master->slaves = NULL; - spin_lock_bh(&master->dev->queue_lock); + spin_lock_bh(&master->dev->tx_queue.lock); qdisc_reset(master->dev->qdisc); - spin_unlock_bh(&master->dev->queue_lock); + spin_unlock_bh(&master->dev->tx_queue.lock); } } skb_queue_purge(&dat->q); -- cgit v1.2.3 From 555353cfa1aee293de445bfa6de43276138ddd82 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 17:33:13 -0700 Subject: netdev: The ingress_lock member is no longer needed. Every qdisc is assosciated with a queue, and in the case of ingress qdiscs that will now be netdev->rx_queue so using that queue's lock is the thing to do. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 8 ++++---- include/linux/netdevice.h | 2 -- net/core/dev.c | 12 +++++++----- net/sched/sch_api.c | 3 +-- net/sched/sch_generic.c | 10 +++++----- 5 files changed, 17 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index bc3de272a829..ccbd6554f6eb 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -229,13 +229,13 @@ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); /* - * dev_ifb->tx_queue.lock is usually taken after dev->ingress_lock, + * dev_ifb->tx_queue.lock is usually taken after dev->rx_queue.lock, * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev->tx_queue.lock with dev_ifb->ingress_lock. + * ifb doesn't take dev->tx_queue.lock with dev_ifb->rx_queue.lock. * But lockdep should know that ifb has different locks from dev. */ static struct lock_class_key ifb_tx_queue_lock_key; -static struct lock_class_key ifb_ingress_lock_key; +static struct lock_class_key ifb_rx_queue_lock_key; static int __init ifb_init_one(int index) @@ -259,7 +259,7 @@ static int __init ifb_init_one(int index) goto err; lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); - lockdep_set_class(&dev_ifb->ingress_lock, &ifb_ingress_lock_key); + lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); return 0; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e835acacb479..633a44c6fa5e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -632,8 +632,6 @@ struct net_device struct netdev_queue rx_queue; struct netdev_queue tx_queue ____cacheline_aligned_in_smp; - /* ingress path synchronizer */ - spinlock_t ingress_lock; struct Qdisc *qdisc_ingress; /* diff --git a/net/core/dev.c b/net/core/dev.c index 05011048b86c..2322fb69fd53 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2014,10 +2014,11 @@ static inline struct sk_buff *handle_macvlan(struct sk_buff *skb, */ static int ing_filter(struct sk_buff *skb) { - struct Qdisc *q; struct net_device *dev = skb->dev; - int result = TC_ACT_OK; u32 ttl = G_TC_RTTL(skb->tc_verd); + struct netdev_queue *rxq; + int result = TC_ACT_OK; + struct Qdisc *q; if (MAX_RED_LOOP < ttl++) { printk(KERN_WARNING @@ -2029,10 +2030,12 @@ static int ing_filter(struct sk_buff *skb) skb->tc_verd = SET_TC_RTTL(skb->tc_verd, ttl); skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS); - spin_lock(&dev->ingress_lock); + rxq = &dev->rx_queue; + + spin_lock(&rxq->lock); if ((q = dev->qdisc_ingress) != NULL) result = q->enqueue(skb, q); - spin_unlock(&dev->ingress_lock); + spin_unlock(&rxq->lock); return result; } @@ -3795,7 +3798,6 @@ int register_netdevice(struct net_device *dev) spin_lock_init(&dev->_xmit_lock); netdev_set_lockdep_class(&dev->_xmit_lock, dev->type); dev->xmit_lock_owner = -1; - spin_lock_init(&dev->ingress_lock); dev->iflink = -1; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2a1834f8c7d8..570cef2a9c5f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -601,12 +601,11 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, sch->parent = parent; + sch->stats_lock = &dev_queue->lock; if (handle == TC_H_INGRESS) { sch->flags |= TCQ_F_INGRESS; - sch->stats_lock = &dev->ingress_lock; handle = TC_H_MAKE(TC_H_INGRESS, 0); } else { - sch->stats_lock = &dev_queue->lock; if (handle == 0) { handle = qdisc_alloc_handle(dev); err = -ENOMEM; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index ee8f9f78a095..804d44b00348 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -35,24 +35,24 @@ * - enqueue, dequeue are serialized via top level device * spinlock queue->lock. * - ingress filtering is serialized via top level device - * spinlock dev->ingress_lock. + * spinlock dev->rx_queue.lock. * - updates to tree and tree walking are only done under the rtnl mutex. */ void qdisc_lock_tree(struct net_device *dev) __acquires(dev->tx_queue.lock) - __acquires(dev->ingress_lock) + __acquires(dev->rx_queue.lock) { spin_lock_bh(&dev->tx_queue.lock); - spin_lock(&dev->ingress_lock); + spin_lock(&dev->rx_queue.lock); } EXPORT_SYMBOL(qdisc_lock_tree); void qdisc_unlock_tree(struct net_device *dev) - __releases(dev->ingress_lock) + __releases(dev->rx_queue.lock) __releases(dev->tx_queue.lock) { - spin_unlock(&dev->ingress_lock); + spin_unlock(&dev->rx_queue.lock); spin_unlock_bh(&dev->tx_queue.lock); } EXPORT_SYMBOL(qdisc_unlock_tree); -- cgit v1.2.3 From b0e1e6462df3c5944010b3328a546d8fe5d932cd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 17:42:10 -0700 Subject: netdev: Move rest of qdisc state into struct netdev_queue Now qdisc, qdisc_sleeping, and qdisc_list also live there. Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_net.c | 2 +- include/linux/netdevice.h | 10 ++--- include/net/irda/irda_device.h | 2 +- net/core/dev.c | 4 +- net/core/link_watch.c | 8 +++- net/core/rtnetlink.c | 6 ++- net/ipv6/addrconf.c | 3 +- net/mac80211/wme.c | 20 ++++++---- net/sched/cls_api.c | 7 +++- net/sched/sch_api.c | 34 ++++++++++------ net/sched/sch_generic.c | 90 ++++++++++++++++++++++++++---------------- net/sched/sch_netem.c | 2 +- net/sched/sch_teql.c | 14 ++++--- 13 files changed, 125 insertions(+), 77 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index ef1a300068dc..457bbd119f9b 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -287,7 +287,7 @@ isdn_net_unbind_channel(isdn_net_local * lp) BEWARE! This chunk of code cannot be called from hardware interrupt handler. I hope it is true. --ANK */ - qdisc_reset(lp->netdev->dev->qdisc); + qdisc_reset(lp->netdev->dev->tx_queue.qdisc); } lp->dialstate = 0; dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 633a44c6fa5e..df702a7b3db5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -451,6 +451,9 @@ static inline void napi_synchronize(const struct napi_struct *n) struct netdev_queue { spinlock_t lock; struct net_device *dev; + struct Qdisc *qdisc; + struct Qdisc *qdisc_sleeping; + struct list_head qdisc_list; }; /* @@ -634,13 +637,6 @@ struct net_device struct Qdisc *qdisc_ingress; -/* - * Cache line mostly used on queue transmit path (qdisc) - */ - /* device queue lock */ - struct Qdisc *qdisc; - struct Qdisc *qdisc_sleeping; - struct list_head qdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ /* Partially transmitted GSO packet. */ diff --git a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h index f70e9b39ebaf..16fbf672e0b2 100644 --- a/include/net/irda/irda_device.h +++ b/include/net/irda/irda_device.h @@ -223,7 +223,7 @@ int irda_device_is_receiving(struct net_device *dev); /* Interface for internal use */ static inline int irda_device_txqueue_empty(const struct net_device *dev) { - return skb_queue_empty(&dev->qdisc->q); + return skb_queue_empty(&dev->tx_queue.qdisc->q); } int irda_device_set_raw_mode(struct net_device* self, int status); struct net_device *alloc_irdadev(int sizeof_priv); diff --git a/net/core/dev.c b/net/core/dev.c index 2322fb69fd53..ce79c28d739d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1720,14 +1720,14 @@ gso: * also serializes access to the device queue. */ - q = rcu_dereference(dev->qdisc); + q = rcu_dereference(txq->qdisc); #ifdef CONFIG_NET_CLS_ACT skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS); #endif if (q->enqueue) { /* Grab device queue */ spin_lock(&txq->lock); - q = dev->qdisc; + q = txq->qdisc; if (q->enqueue) { /* reset queue_mapping to zero */ skb_set_queue_mapping(skb, 0); diff --git a/net/core/link_watch.c b/net/core/link_watch.c index a5e372b9ec4d..50218218445b 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -79,8 +79,10 @@ static void rfc2863_policy(struct net_device *dev) static int linkwatch_urgent_event(struct net_device *dev) { + struct netdev_queue *txq = &dev->tx_queue; + return netif_running(dev) && netif_carrier_ok(dev) && - dev->qdisc != dev->qdisc_sleeping; + txq->qdisc != txq->qdisc_sleeping; } @@ -181,7 +183,9 @@ static void __linkwatch_run_queue(int urgent_only) rfc2863_policy(dev); if (dev->flags & IFF_UP) { if (netif_carrier_ok(dev)) { - WARN_ON(dev->qdisc_sleeping == &noop_qdisc); + struct netdev_queue *txq = &dev->tx_queue; + + WARN_ON(txq->qdisc_sleeping == &noop_qdisc); dev_activate(dev); } else dev_deactivate(dev); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 6c8d7f0ea01a..8ef9f1db610e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -605,6 +605,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags) { + struct netdev_queue *txq; struct ifinfomsg *ifm; struct nlmsghdr *nlh; struct net_device_stats *stats; @@ -635,8 +636,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (dev->master) NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); - if (dev->qdisc_sleeping) - NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc_sleeping->ops->id); + txq = &dev->tx_queue; + if (txq->qdisc_sleeping) + NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); if (1) { struct rtnl_link_ifmap map = { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8572cb05fc21..5c84c798331d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -231,7 +231,8 @@ const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTER /* Check if a valid qdisc is available */ static inline int addrconf_qdisc_ok(struct net_device *dev) { - return (dev->qdisc != &noop_qdisc); + struct netdev_queue *txq = &dev->tx_queue; + return (txq->qdisc != &noop_qdisc); } /* Check if a route is valid prefix route */ diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 59ed9cae66b9..6ae43a3c7726 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -574,9 +574,10 @@ static struct Qdisc_ops wme_qdisc_ops __read_mostly = void ieee80211_install_qdisc(struct net_device *dev) { + struct netdev_queue *txq = &dev->tx_queue; struct Qdisc *qdisc; - qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + qdisc = qdisc_create_dflt(dev, txq, &wme_qdisc_ops, TC_H_ROOT); if (!qdisc) { printk(KERN_ERR "%s: qdisc installation failed\n", dev->name); @@ -587,15 +588,17 @@ void ieee80211_install_qdisc(struct net_device *dev) qdisc->handle = 0x80010000; qdisc_lock_tree(dev); - list_add_tail(&qdisc->list, &dev->qdisc_list); - dev->qdisc_sleeping = qdisc; + list_add_tail(&qdisc->list, &txq->qdisc_list); + txq->qdisc_sleeping = qdisc; qdisc_unlock_tree(dev); } int ieee80211_qdisc_installed(struct net_device *dev) { - return dev->qdisc_sleeping->ops == &wme_qdisc_ops; + struct netdev_queue *txq = &dev->tx_queue; + + return txq->qdisc_sleeping->ops == &wme_qdisc_ops; } @@ -614,8 +617,9 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { int i; + struct netdev_queue *txq = &local->mdev->tx_queue; struct ieee80211_sched_data *q = - qdisc_priv(local->mdev->qdisc_sleeping); + qdisc_priv(txq->qdisc_sleeping); DECLARE_MAC_BUF(mac); /* prepare the filter and save it for the SW queue @@ -655,8 +659,9 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, u8 requeue) { struct ieee80211_hw *hw = &local->hw; + struct netdev_queue *txq = &local->mdev->tx_queue; struct ieee80211_sched_data *q = - qdisc_priv(local->mdev->qdisc_sleeping); + qdisc_priv(txq->qdisc_sleeping); int agg_queue = sta->tid_to_tx_q[tid]; /* return the qdisc to the pool */ @@ -671,7 +676,8 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, void ieee80211_requeue(struct ieee80211_local *local, int queue) { - struct Qdisc *root_qd = local->mdev->qdisc_sleeping; + struct netdev_queue *txq = &local->mdev->tx_queue; + struct Qdisc *root_qd = txq->qdisc_sleeping; struct ieee80211_sched_data *q = qdisc_priv(root_qd); struct Qdisc *qdisc = q->queues[queue]; struct sk_buff *skb = NULL; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index e2389f161e46..b483bbea6118 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -166,7 +166,8 @@ replay: /* Find qdisc */ if (!parent) { - q = dev->qdisc_sleeping; + struct netdev_queue *dev_queue = &dev->tx_queue; + q = dev_queue->qdisc_sleeping; parent = q->handle; } else { q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent)); @@ -390,6 +391,7 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); + struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -408,8 +410,9 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return skb->len; + dev_queue = &dev->tx_queue; if (!tcm->tcm_parent) - q = dev->qdisc_sleeping; + q = dev_queue->qdisc_sleeping; else q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); if (!q) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 570cef2a9c5f..2313fa7c97be 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -185,9 +185,10 @@ EXPORT_SYMBOL(unregister_qdisc); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { + struct netdev_queue *dev_queue = &dev->tx_queue; struct Qdisc *q; - list_for_each_entry(q, &dev->qdisc_list, list) { + list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (q->handle == handle) return q; } @@ -441,6 +442,7 @@ static u32 qdisc_alloc_handle(struct net_device *dev) static struct Qdisc * dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) { + struct netdev_queue *dev_queue; struct Qdisc *oqdisc; if (dev->flags & IFF_UP) @@ -459,8 +461,8 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) } } else { - - oqdisc = dev->qdisc_sleeping; + dev_queue = &dev->tx_queue; + oqdisc = dev_queue->qdisc_sleeping; /* Prune old scheduler */ if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) @@ -469,8 +471,8 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) /* ... and graft new one */ if (qdisc == NULL) qdisc = &noop_qdisc; - dev->qdisc_sleeping = qdisc; - dev->qdisc = &noop_qdisc; + dev_queue->qdisc_sleeping = qdisc; + dev_queue->qdisc = &noop_qdisc; } qdisc_unlock_tree(dev); @@ -633,7 +635,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, } } qdisc_lock_tree(dev); - list_add_tail(&sch->list, &dev->qdisc_list); + list_add_tail(&sch->list, &dev_queue->qdisc_list); qdisc_unlock_tree(dev); return sch; @@ -740,7 +742,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) q = dev->qdisc_ingress; } } else { - q = dev->qdisc_sleeping; + struct netdev_queue *dev_queue = &dev->tx_queue; + q = dev_queue->qdisc_sleeping; } if (!q) return -ENOENT; @@ -814,7 +817,8 @@ replay: q = dev->qdisc_ingress; } } else { - q = dev->qdisc_sleeping; + struct netdev_queue *dev_queue = &dev->tx_queue; + q = dev_queue->qdisc_sleeping; } /* It may be default qdisc, ignore it */ @@ -1015,12 +1019,14 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { + struct netdev_queue *dev_queue; if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { + dev_queue = &dev->tx_queue; + list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; continue; @@ -1054,6 +1060,7 @@ done: static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { struct net *net = sock_net(skb->sk); + struct netdev_queue *dev_queue; struct tcmsg *tcm = NLMSG_DATA(n); struct nlattr *tca[TCA_MAX + 1]; struct net_device *dev; @@ -1091,6 +1098,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Step 1. Determine qdisc handle X:0 */ + dev_queue = &dev->tx_queue; if (pid != TC_H_ROOT) { u32 qid1 = TC_H_MAJ(pid); @@ -1101,7 +1109,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) } else if (qid1) { qid = qid1; } else if (qid == 0) - qid = dev->qdisc_sleeping->handle; + qid = dev_queue->qdisc_sleeping->handle; /* Now qid is genuine qdisc handle consistent both with parent and child. @@ -1112,7 +1120,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) pid = TC_H_MAKE(qid, pid); } else { if (qid == 0) - qid = dev->qdisc_sleeping->handle; + qid = dev_queue->qdisc_sleeping->handle; } /* OK. Locate qdisc */ @@ -1248,6 +1256,7 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); + struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -1266,7 +1275,8 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { + dev_queue = &dev->tx_queue; + list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 804d44b00348..3223e5ba76aa 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -122,7 +122,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * * __LINK_STATE_QDISC_RUNNING guarantees only one CPU can process this * device at a time. queue->lock serializes queue accesses for - * this device AND dev->qdisc pointer itself. + * this device AND txq->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. * @@ -138,7 +138,8 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, */ static inline int qdisc_restart(struct net_device *dev) { - struct Qdisc *q = dev->qdisc; + struct netdev_queue *txq = &dev->tx_queue; + struct Qdisc *q = txq->qdisc; struct sk_buff *skb; int ret = NETDEV_TX_BUSY; @@ -148,15 +149,15 @@ static inline int qdisc_restart(struct net_device *dev) /* And release queue */ - spin_unlock(&q->dev_queue->lock); + spin_unlock(&txq->lock); HARD_TX_LOCK(dev, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) ret = dev_hard_start_xmit(skb, dev); HARD_TX_UNLOCK(dev); - spin_lock(&q->dev_queue->lock); - q = dev->qdisc; + spin_lock(&txq->lock); + q = txq->qdisc; switch (ret) { case NETDEV_TX_OK: @@ -207,9 +208,10 @@ void __qdisc_run(struct net_device *dev) static void dev_watchdog(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; + struct netdev_queue *txq = &dev->tx_queue; netif_tx_lock(dev); - if (dev->qdisc != &noop_qdisc) { + if (txq->qdisc != &noop_qdisc) { if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) { @@ -539,53 +541,63 @@ EXPORT_SYMBOL(qdisc_destroy); void dev_activate(struct net_device *dev) { + struct netdev_queue *txq = &dev->tx_queue; + /* No queueing discipline is attached to device; create default one i.e. pfifo_fast for devices, which need queueing and noqueue_qdisc for virtual interfaces */ - if (dev->qdisc_sleeping == &noop_qdisc) { + if (txq->qdisc_sleeping == &noop_qdisc) { struct Qdisc *qdisc; if (dev->tx_queue_len) { - qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + qdisc = qdisc_create_dflt(dev, txq, &pfifo_fast_ops, TC_H_ROOT); if (qdisc == NULL) { printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - list_add_tail(&qdisc->list, &dev->qdisc_list); + list_add_tail(&qdisc->list, &txq->qdisc_list); } else { qdisc = &noqueue_qdisc; } - dev->qdisc_sleeping = qdisc; + txq->qdisc_sleeping = qdisc; } if (!netif_carrier_ok(dev)) /* Delay activation until next carrier-on event */ return; - spin_lock_bh(&dev->tx_queue.lock); - rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping); - if (dev->qdisc != &noqueue_qdisc) { + spin_lock_bh(&txq->lock); + rcu_assign_pointer(txq->qdisc, txq->qdisc_sleeping); + if (txq->qdisc != &noqueue_qdisc) { dev->trans_start = jiffies; dev_watchdog_up(dev); } - spin_unlock_bh(&dev->tx_queue.lock); + spin_unlock_bh(&txq->lock); +} + +static void dev_deactivate_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc *qdisc_default) +{ + struct Qdisc *qdisc = dev_queue->qdisc; + + if (qdisc) { + dev_queue->qdisc = qdisc_default; + qdisc_reset(qdisc); + } } void dev_deactivate(struct net_device *dev) { - struct Qdisc *qdisc; struct sk_buff *skb; int running; spin_lock_bh(&dev->tx_queue.lock); - qdisc = dev->qdisc; - dev->qdisc = &noop_qdisc; - - qdisc_reset(qdisc); + dev_deactivate_queue(dev, &dev->tx_queue, &noop_qdisc); skb = dev->gso_skb; dev->gso_skb = NULL; @@ -622,32 +634,44 @@ void dev_deactivate(struct net_device *dev) } while (WARN_ON_ONCE(running)); } +static void dev_init_scheduler_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc *qdisc) +{ + dev_queue->qdisc = qdisc; + dev_queue->qdisc_sleeping = qdisc; + INIT_LIST_HEAD(&dev_queue->qdisc_list); +} + void dev_init_scheduler(struct net_device *dev) { qdisc_lock_tree(dev); - dev->qdisc = &noop_qdisc; - dev->qdisc_sleeping = &noop_qdisc; - INIT_LIST_HEAD(&dev->qdisc_list); + dev_init_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); + dev_init_scheduler_queue(dev, &dev->rx_queue, NULL); qdisc_unlock_tree(dev); setup_timer(&dev->watchdog_timer, dev_watchdog, (unsigned long)dev); } -void dev_shutdown(struct net_device *dev) +static void dev_shutdown_scheduler_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc *qdisc_default) { - struct Qdisc *qdisc; + struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + + if (qdisc) { + dev_queue->qdisc = qdisc_default; + dev_queue->qdisc_sleeping = qdisc_default; - qdisc_lock_tree(dev); - qdisc = dev->qdisc_sleeping; - dev->qdisc = &noop_qdisc; - dev->qdisc_sleeping = &noop_qdisc; - qdisc_destroy(qdisc); -#if defined(CONFIG_NET_SCH_INGRESS) || defined(CONFIG_NET_SCH_INGRESS_MODULE) - if ((qdisc = dev->qdisc_ingress) != NULL) { - dev->qdisc_ingress = NULL; qdisc_destroy(qdisc); } -#endif +} + +void dev_shutdown(struct net_device *dev) +{ + qdisc_lock_tree(dev); + dev_shutdown_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); + dev_shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); BUG_TRAP(!timer_pending(&dev->watchdog_timer)); qdisc_unlock_tree(dev); } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 71b73c528f9b..4093f1eaaf60 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -180,7 +180,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) * skb will be queued. */ if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { - struct Qdisc *rootq = qdisc_dev(sch)->qdisc; + struct Qdisc *rootq = qdisc_dev(sch)->tx_queue.qdisc; u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ q->duplicate = 0; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 4f3054e8e1ab..8ac05981be20 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -107,17 +107,19 @@ static struct sk_buff * teql_dequeue(struct Qdisc* sch) { struct teql_sched_data *dat = qdisc_priv(sch); + struct netdev_queue *dat_queue; struct sk_buff *skb; skb = __skb_dequeue(&dat->q); + dat_queue = &dat->m->dev->tx_queue; if (skb == NULL) { - struct net_device *m = qdisc_dev(dat->m->dev->qdisc); + struct net_device *m = qdisc_dev(dat_queue->qdisc); if (m) { dat->m->slaves = sch; netif_wake_queue(m); } } - sch->q.qlen = dat->q.qlen + dat->m->dev->qdisc->q.qlen; + sch->q.qlen = dat->q.qlen + dat_queue->qdisc->q.qlen; return skb; } @@ -155,7 +157,7 @@ teql_destroy(struct Qdisc* sch) if (q == master->slaves) { master->slaves = NULL; spin_lock_bh(&master->dev->tx_queue.lock); - qdisc_reset(master->dev->qdisc); + qdisc_reset(master->dev->tx_queue.qdisc); spin_unlock_bh(&master->dev->tx_queue.lock); } } @@ -216,7 +218,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt) static int __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - struct teql_sched_data *q = qdisc_priv(dev->qdisc); + struct teql_sched_data *q = qdisc_priv(dev->tx_queue.qdisc); struct neighbour *mn = skb->dst->neighbour; struct neighbour *n = q->ncache; @@ -252,7 +254,7 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device * static inline int teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - if (dev->qdisc == &noop_qdisc) + if (dev->tx_queue.qdisc == &noop_qdisc) return -ENODEV; if (dev->header_ops == NULL || @@ -284,7 +286,7 @@ restart: do { struct net_device *slave = qdisc_dev(q); - if (slave->qdisc_sleeping != q) + if (slave->tx_queue.qdisc_sleeping != q) continue; if (netif_queue_stopped(slave) || __netif_subqueue_stopped(slave, subq) || -- cgit v1.2.3 From 816f3258e70db38d6d92c8d871377179fd69160f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 22:49:00 -0700 Subject: netdev: Kill qdisc_ingress, use netdev->rx_queue.qdisc instead. Now that our qdisc management is bi-directional, per-queue, and fully orthogonal, there is no reason to have a special ingress qdisc pointer in struct net_device. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 --- net/core/dev.c | 4 ++-- net/sched/sch_api.c | 11 ++++++----- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index df702a7b3db5..e7c49246fd88 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -634,9 +634,6 @@ struct net_device struct netdev_queue rx_queue; struct netdev_queue tx_queue ____cacheline_aligned_in_smp; - - struct Qdisc *qdisc_ingress; - unsigned long tx_queue_len; /* Max frames per queue allowed */ /* Partially transmitted GSO packet. */ diff --git a/net/core/dev.c b/net/core/dev.c index ce79c28d739d..ab760a954d99 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2033,7 +2033,7 @@ static int ing_filter(struct sk_buff *skb) rxq = &dev->rx_queue; spin_lock(&rxq->lock); - if ((q = dev->qdisc_ingress) != NULL) + if ((q = rxq->qdisc) != NULL) result = q->enqueue(skb, q); spin_unlock(&rxq->lock); @@ -2044,7 +2044,7 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, struct net_device *orig_dev) { - if (!skb->dev->qdisc_ingress) + if (!skb->dev->rx_queue.qdisc) goto out; if (*pt_prev) { diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2313fa7c97be..4003c280b69f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -450,14 +450,15 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) qdisc_lock_tree(dev); if (qdisc && qdisc->flags&TCQ_F_INGRESS) { - oqdisc = dev->qdisc_ingress; + dev_queue = &dev->rx_queue; + oqdisc = dev_queue->qdisc; /* Prune old scheduler */ if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) { /* delete */ qdisc_reset(oqdisc); - dev->qdisc_ingress = NULL; + dev_queue->qdisc = NULL; } else { /* new */ - dev->qdisc_ingress = qdisc; + dev_queue->qdisc = qdisc; } } else { @@ -739,7 +740,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) return -ENOENT; q = qdisc_leaf(p, clid); } else { /* ingress */ - q = dev->qdisc_ingress; + q = dev->rx_queue.qdisc; } } else { struct netdev_queue *dev_queue = &dev->tx_queue; @@ -814,7 +815,7 @@ replay: return -ENOENT; q = qdisc_leaf(p, clid); } else { /*ingress */ - q = dev->qdisc_ingress; + q = dev->rx_queue.qdisc; } } else { struct netdev_queue *dev_queue = &dev->tx_queue; -- cgit v1.2.3 From ee609cb36220d18c0cf476b066a5ab7e6f6d3a69 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 22:58:37 -0700 Subject: netdev: Move next_sched into struct netdev_queue. We schedule queues, not the device, for output queue processing in BH. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 ++--- net/core/dev.c | 15 +++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e7c49246fd88..1379c822e51d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -454,6 +454,7 @@ struct netdev_queue { struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; + struct netdev_queue *next_sched; }; /* @@ -545,8 +546,6 @@ struct net_device #define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM) #define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) - struct net_device *next_sched; - /* Interface index. Unique device identifier */ int ifindex; int iflink; @@ -940,7 +939,7 @@ static inline int unregister_gifconf(unsigned int family) */ struct softnet_data { - struct net_device *output_queue; + struct netdev_queue *output_queue; struct sk_buff_head input_pkt_queue; struct list_head poll_list; struct sk_buff *completion_queue; diff --git a/net/core/dev.c b/net/core/dev.c index ab760a954d99..d6b8d3c3e6ec 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1323,13 +1323,14 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) void __netif_schedule(struct net_device *dev) { if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { + struct netdev_queue *txq = &dev->tx_queue; unsigned long flags; struct softnet_data *sd; local_irq_save(flags); sd = &__get_cpu_var(softnet_data); - dev->next_sched = sd->output_queue; - sd->output_queue = dev; + txq->next_sched = sd->output_queue; + sd->output_queue = txq; raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_restore(flags); } @@ -1912,7 +1913,7 @@ static void net_tx_action(struct softirq_action *h) } if (sd->output_queue) { - struct net_device *head; + struct netdev_queue *head; local_irq_disable(); head = sd->output_queue; @@ -1920,12 +1921,10 @@ static void net_tx_action(struct softirq_action *h) local_irq_enable(); while (head) { - struct net_device *dev = head; - struct netdev_queue *txq; + struct netdev_queue *txq = head; + struct net_device *dev = txq->dev; head = head->next_sched; - txq = &dev->tx_queue; - smp_mb__before_clear_bit(); clear_bit(__LINK_STATE_SCHED, &dev->state); @@ -4346,7 +4345,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, void *ocpu) { struct sk_buff **list_skb; - struct net_device **list_net; + struct netdev_queue **list_net; struct sk_buff *skb; unsigned int cpu, oldcpu = (unsigned long)ocpu; struct softnet_data *sd, *oldsd; -- cgit v1.2.3 From 970565bbad0c7b98db0d14131a69e5a0f4445d49 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:10:33 -0700 Subject: netdev: Move gso_skb into netdev_queue. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 +--- net/sched/sch_generic.c | 42 +++++++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1379c822e51d..aae6c6d153f2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -452,6 +452,7 @@ struct netdev_queue { spinlock_t lock; struct net_device *dev; struct Qdisc *qdisc; + struct sk_buff *gso_skb; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; struct netdev_queue *next_sched; @@ -635,9 +636,6 @@ struct net_device struct netdev_queue tx_queue ____cacheline_aligned_in_smp; unsigned long tx_queue_len; /* Max frames per queue allowed */ - /* Partially transmitted GSO packet. */ - struct sk_buff *gso_skb; - /* * One part is mostly used on xmit path (device) */ diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index dda78ee314ec..8247a406a401 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -63,10 +63,11 @@ static inline int qdisc_qlen(struct Qdisc *q) } static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc *q) { if (unlikely(skb->next)) - dev->gso_skb = skb; + dev_queue->gso_skb = skb; else q->ops->requeue(skb, q); @@ -75,12 +76,13 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, } static inline struct sk_buff *dev_dequeue_skb(struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc *q) { struct sk_buff *skb; - if ((skb = dev->gso_skb)) - dev->gso_skb = NULL; + if ((skb = dev_queue->gso_skb)) + dev_queue->gso_skb = NULL; else skb = q->dequeue(q); @@ -89,6 +91,7 @@ static inline struct sk_buff *dev_dequeue_skb(struct net_device *dev, static inline int handle_dev_cpu_collision(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc *q) { int ret; @@ -111,7 +114,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * some time. */ __get_cpu_var(netdev_rx_stat).cpu_collision++; - ret = dev_requeue_skb(skb, dev, q); + ret = dev_requeue_skb(skb, dev, dev_queue, q); } return ret; @@ -144,7 +147,7 @@ static inline int qdisc_restart(struct net_device *dev) int ret = NETDEV_TX_BUSY; /* Dequeue packet */ - if (unlikely((skb = dev_dequeue_skb(dev, q)) == NULL)) + if (unlikely((skb = dev_dequeue_skb(dev, txq, q)) == NULL)) return 0; @@ -167,7 +170,7 @@ static inline int qdisc_restart(struct net_device *dev) case NETDEV_TX_LOCKED: /* Driver try lock failed */ - ret = handle_dev_cpu_collision(skb, dev, q); + ret = handle_dev_cpu_collision(skb, dev, txq, q); break; default: @@ -176,7 +179,7 @@ static inline int qdisc_restart(struct net_device *dev) printk(KERN_WARNING "BUG %s code %d qlen %d\n", dev->name, ret, q->q.qlen); - ret = dev_requeue_skb(skb, dev, q); + ret = dev_requeue_skb(skb, dev, txq, q); break; } @@ -578,31 +581,32 @@ void dev_activate(struct net_device *dev) spin_unlock_bh(&txq->lock); } -static void dev_deactivate_queue(struct net_device *dev, - struct netdev_queue *dev_queue, +static void dev_deactivate_queue(struct netdev_queue *dev_queue, struct Qdisc *qdisc_default) { - struct Qdisc *qdisc = dev_queue->qdisc; + struct Qdisc *qdisc; + struct sk_buff *skb; + + spin_lock_bh(&dev_queue->lock); + qdisc = dev_queue->qdisc; if (qdisc) { dev_queue->qdisc = qdisc_default; qdisc_reset(qdisc); } + skb = dev_queue->gso_skb; + dev_queue->gso_skb = NULL; + + spin_unlock_bh(&dev_queue->lock); + + kfree_skb(skb); } void dev_deactivate(struct net_device *dev) { - struct sk_buff *skb; int running; - spin_lock_bh(&dev->tx_queue.lock); - dev_deactivate_queue(dev, &dev->tx_queue, &noop_qdisc); - - skb = dev->gso_skb; - dev->gso_skb = NULL; - spin_unlock_bh(&dev->tx_queue.lock); - - kfree_skb(skb); + dev_deactivate_queue(&dev->tx_queue, &noop_qdisc); dev_watchdog_down(dev); -- cgit v1.2.3 From 86d804e10a37cd86f16bf72386c37e843a98a74b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:11:25 -0700 Subject: netdev: Make netif_schedule() routines work with netdev_queue objects. Only plain netif_schedule() remains taking a net_device, mostly as a compatability item while we transition the rest of these interfaces. Everything else calls netif_schedule_queue() or __netif_schedule(), both of which take a netdev_queue pointer. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 17 ++++++++++++----- net/core/dev.c | 9 +++++---- net/mac80211/main.c | 4 ++-- net/sched/sch_api.c | 4 ++-- net/sched/sch_cbq.c | 2 +- net/sched/sch_generic.c | 10 +++++----- 6 files changed, 27 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index aae6c6d153f2..28aa8e77cee9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -952,12 +952,19 @@ DECLARE_PER_CPU(struct softnet_data,softnet_data); #define HAVE_NETIF_QUEUE -extern void __netif_schedule(struct net_device *dev); +extern void __netif_schedule(struct netdev_queue *txq); -static inline void netif_schedule(struct net_device *dev) +static inline void netif_schedule_queue(struct netdev_queue *txq) { + struct net_device *dev = txq->dev; + if (!test_bit(__LINK_STATE_XOFF, &dev->state)) - __netif_schedule(dev); + __netif_schedule(txq); +} + +static inline void netif_schedule(struct net_device *dev) +{ + netif_schedule_queue(&dev->tx_queue); } /** @@ -987,7 +994,7 @@ static inline void netif_wake_queue(struct net_device *dev) } #endif if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) - __netif_schedule(dev); + __netif_schedule(&dev->tx_queue); } /** @@ -1103,7 +1110,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) #endif if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) - __netif_schedule(dev); + __netif_schedule(&dev->tx_queue); #endif } diff --git a/net/core/dev.c b/net/core/dev.c index d6b8d3c3e6ec..0dc888ad4217 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1320,12 +1320,13 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) } -void __netif_schedule(struct net_device *dev) +void __netif_schedule(struct netdev_queue *txq) { + struct net_device *dev = txq->dev; + if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { - struct netdev_queue *txq = &dev->tx_queue; - unsigned long flags; struct softnet_data *sd; + unsigned long flags; local_irq_save(flags); sd = &__get_cpu_var(softnet_data); @@ -1932,7 +1933,7 @@ static void net_tx_action(struct softirq_action *h) qdisc_run(dev); spin_unlock(&txq->lock); } else { - netif_schedule(dev); + netif_schedule_queue(txq); } } } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2968baa66b91..1c4d3ba6b878 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -885,10 +885,10 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) spin_unlock_bh(&txq->lock); /* we just requeued the all the frames that were in the removed - * queue, and since we might miss a softirq we do netif_schedule. + * queue, and since we might miss a softirq we do netif_schedule_queue. * ieee80211_wake_queue is not used here as this queue is not * necessarily stopped */ - netif_schedule(local->mdev); + netif_schedule_queue(txq); spin_lock_bh(&sta->lock); *state = HT_AGG_STATE_IDLE; sta->ampdu_mlme.addba_req_num[tid] = 0; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index e73bd68aa7ae..95873f8dd37c 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -282,11 +282,11 @@ static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, timer); - struct net_device *dev = qdisc_dev(wd->qdisc); + struct netdev_queue *txq = wd->qdisc->dev_queue; wd->qdisc->flags &= ~TCQ_F_THROTTLED; smp_wmb(); - netif_schedule(dev); + netif_schedule_queue(txq); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 99ce3da2b0a4..4efc836cbf38 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -650,7 +650,7 @@ static enum hrtimer_restart cbq_undelay(struct hrtimer *timer) } sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule(qdisc_dev(sch)); + netif_schedule_queue(sch->dev_queue); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 8247a406a401..407dfdb142a4 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -62,7 +62,7 @@ static inline int qdisc_qlen(struct Qdisc *q) return q->q.qlen; } -static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, +static inline int dev_requeue_skb(struct sk_buff *skb, struct netdev_queue *dev_queue, struct Qdisc *q) { @@ -71,7 +71,7 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, else q->ops->requeue(skb, q); - netif_schedule(dev); + netif_schedule_queue(dev_queue); return 0; } @@ -114,7 +114,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * some time. */ __get_cpu_var(netdev_rx_stat).cpu_collision++; - ret = dev_requeue_skb(skb, dev, dev_queue, q); + ret = dev_requeue_skb(skb, dev_queue, q); } return ret; @@ -179,7 +179,7 @@ static inline int qdisc_restart(struct net_device *dev) printk(KERN_WARNING "BUG %s code %d qlen %d\n", dev->name, ret, q->q.qlen); - ret = dev_requeue_skb(skb, dev, txq, q); + ret = dev_requeue_skb(skb, txq, q); break; } @@ -200,7 +200,7 @@ void __qdisc_run(struct net_device *dev) * 2. we've been doing it for too long. */ if (need_resched() || jiffies != start_time) { - netif_schedule(dev); + netif_schedule_queue(&dev->tx_queue); break; } } -- cgit v1.2.3 From c773e847ea8f6812804e40f52399c6921a00eab1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:13:53 -0700 Subject: netdev: Move _xmit_lock and xmit_lock_owner into netdev_queue. Accesses are mostly structured such that when there are multiple TX queues the code transformations will be a little bit simpler. Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 13 ++++++- drivers/net/hamradio/bpqether.c | 12 ++++++- drivers/net/macvlan.c | 14 +++++++- drivers/net/wireless/hostap/hostap_hw.c | 12 ++++++- include/linux/netdevice.h | 62 +++++++++++++++++++++------------ net/8021q/vlan_dev.c | 15 ++++++-- net/core/dev.c | 28 ++++++++++----- net/netrom/af_netrom.c | 12 ++++++- net/rose/af_rose.c | 12 ++++++- net/sched/sch_generic.c | 9 +++-- 10 files changed, 145 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d57b65dc2c72..dc733d75a5e9 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5019,6 +5019,17 @@ static int bond_check_params(struct bond_params *params) static struct lock_class_key bonding_netdev_xmit_lock_key; +static void bond_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, + &bonding_netdev_xmit_lock_key); +} + +static void bond_set_lockdep_class(struct net_device *dev) +{ + bond_set_lockdep_class_one(&dev->tx_queue); +} + /* Create a new bond based on the specified name and bonding parameters. * If name is NULL, obtain a suitable "bond%d" name for us. * Caller must NOT hold rtnl_lock; we need to release it here before we @@ -5076,7 +5087,7 @@ int bond_create(char *name, struct bond_params *params) goto out_bond; } - lockdep_set_class(&bond_dev->_xmit_lock, &bonding_netdev_xmit_lock_key); + bond_set_lockdep_class(bond_dev); netif_carrier_off(bond_dev); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 5f4b4c6c9f76..fb186b8c3d4d 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -124,6 +124,16 @@ static LIST_HEAD(bpq_devices); */ static struct lock_class_key bpq_netdev_xmit_lock_key; +static void bpq_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, &bpq_netdev_xmit_lock_key); +} + +static void bpq_set_lockdep_class(struct net_device *dev) +{ + bpq_set_lockdep_class_one(&dev->tx_queue); +} + /* ------------------------------------------------------------------------ */ @@ -523,7 +533,7 @@ static int bpq_new_device(struct net_device *edev) err = register_netdevice(ndev); if (err) goto error; - lockdep_set_class(&ndev->_xmit_lock, &bpq_netdev_xmit_lock_key); + bpq_set_lockdep_class(ndev); /* List protected by RTNL */ list_add_rcu(&bpq->bpq_list, &bpq_devices); diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c36a03ae9bfb..c02ceaa4a216 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -277,6 +277,17 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key; #define MACVLAN_STATE_MASK \ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) +static void macvlan_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, + &macvlan_netdev_xmit_lock_key); +} + +static void macvlan_set_lockdep_class(struct net_device *dev) +{ + macvlan_set_lockdep_class_one(&dev->tx_queue); +} + static int macvlan_init(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); @@ -287,7 +298,8 @@ static int macvlan_init(struct net_device *dev) dev->features = lowerdev->features & MACVLAN_FEATURES; dev->iflink = lowerdev->ifindex; - lockdep_set_class(&dev->_xmit_lock, &macvlan_netdev_xmit_lock_key); + macvlan_set_lockdep_class(dev); + return 0; } diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 09004a632ae7..c1f4bb005d92 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3102,6 +3102,16 @@ static void prism2_clear_set_tim_queue(local_info_t *local) */ static struct lock_class_key hostap_netdev_xmit_lock_key; +static void prism2_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, + &hostap_netdev_xmit_lock_key); +} + +static void prism2_set_lockdep_class(struct net_device *dev) +{ + prism2_set_lockdep_class_one(&dev->tx_queue); +} static struct net_device * prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx, @@ -3268,7 +3278,7 @@ while (0) if (ret >= 0) ret = register_netdevice(dev); - lockdep_set_class(&dev->_xmit_lock, &hostap_netdev_xmit_lock_key); + prism2_set_lockdep_class(dev); rtnl_unlock(); if (ret < 0) { printk(KERN_WARNING "%s: register netdevice failed!\n", diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 28aa8e77cee9..c8d5f128858d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -453,6 +453,8 @@ struct netdev_queue { struct net_device *dev; struct Qdisc *qdisc; struct sk_buff *gso_skb; + spinlock_t _xmit_lock; + int xmit_lock_owner; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; struct netdev_queue *next_sched; @@ -639,12 +641,6 @@ struct net_device /* * One part is mostly used on xmit path (device) */ - /* hard_start_xmit synchronizer */ - spinlock_t _xmit_lock ____cacheline_aligned_in_smp; - /* cpu id of processor entered to hard_start_xmit or -1, - if nobody entered there. - */ - int xmit_lock_owner; void *priv; /* pointer to private data */ int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev); @@ -1402,52 +1398,72 @@ static inline void netif_rx_complete(struct net_device *dev, * * Get network device transmit lock */ -static inline void __netif_tx_lock(struct net_device *dev, int cpu) +static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) { - spin_lock(&dev->_xmit_lock); - dev->xmit_lock_owner = cpu; + spin_lock(&txq->_xmit_lock); + txq->xmit_lock_owner = cpu; } static inline void netif_tx_lock(struct net_device *dev) { - __netif_tx_lock(dev, smp_processor_id()); + __netif_tx_lock(&dev->tx_queue, smp_processor_id()); +} + +static inline void __netif_tx_lock_bh(struct netdev_queue *txq) +{ + spin_lock_bh(&txq->_xmit_lock); + txq->xmit_lock_owner = smp_processor_id(); } static inline void netif_tx_lock_bh(struct net_device *dev) { - spin_lock_bh(&dev->_xmit_lock); - dev->xmit_lock_owner = smp_processor_id(); + __netif_tx_lock_bh(&dev->tx_queue); } -static inline int netif_tx_trylock(struct net_device *dev) +static inline int __netif_tx_trylock(struct netdev_queue *txq) { - int ok = spin_trylock(&dev->_xmit_lock); + int ok = spin_trylock(&txq->_xmit_lock); if (likely(ok)) - dev->xmit_lock_owner = smp_processor_id(); + txq->xmit_lock_owner = smp_processor_id(); return ok; } +static inline int netif_tx_trylock(struct net_device *dev) +{ + return __netif_tx_trylock(&dev->tx_queue); +} + +static inline void __netif_tx_unlock(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock(&txq->_xmit_lock); +} + static inline void netif_tx_unlock(struct net_device *dev) { - dev->xmit_lock_owner = -1; - spin_unlock(&dev->_xmit_lock); + __netif_tx_unlock(&dev->tx_queue); +} + +static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock_bh(&txq->_xmit_lock); } static inline void netif_tx_unlock_bh(struct net_device *dev) { - dev->xmit_lock_owner = -1; - spin_unlock_bh(&dev->_xmit_lock); + __netif_tx_unlock_bh(&dev->tx_queue); } -#define HARD_TX_LOCK(dev, cpu) { \ +#define HARD_TX_LOCK(dev, txq, cpu) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ - __netif_tx_lock(dev, cpu); \ + __netif_tx_lock(txq, cpu); \ } \ } -#define HARD_TX_UNLOCK(dev) { \ +#define HARD_TX_UNLOCK(dev, txq) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ - netif_tx_unlock(dev); \ + __netif_tx_unlock(txq); \ } \ } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index b6e52c025fd8..8efa399823e3 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -627,6 +627,18 @@ static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) */ static struct lock_class_key vlan_netdev_xmit_lock_key; +static void vlan_dev_set_lockdep_one(struct netdev_queue *txq, + int subclass) +{ + lockdep_set_class_and_subclass(&txq->_xmit_lock, + &vlan_netdev_xmit_lock_key, subclass); +} + +static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) +{ + vlan_dev_set_lockdep_one(&dev->tx_queue, subclass); +} + static const struct header_ops vlan_header_ops = { .create = vlan_dev_hard_header, .rebuild = vlan_dev_rebuild_header, @@ -668,8 +680,7 @@ static int vlan_dev_init(struct net_device *dev) if (is_vlan_dev(real_dev)) subclass = 1; - lockdep_set_class_and_subclass(&dev->_xmit_lock, - &vlan_netdev_xmit_lock_key, subclass); + vlan_dev_set_lockdep_class(dev, subclass); return 0; } diff --git a/net/core/dev.c b/net/core/dev.c index 0218b0b9be80..a29a359b15d1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -258,7 +258,7 @@ DEFINE_PER_CPU(struct softnet_data, softnet_data); #ifdef CONFIG_DEBUG_LOCK_ALLOC /* - * register_netdevice() inits dev->_xmit_lock and sets lockdep class + * register_netdevice() inits txq->_xmit_lock and sets lockdep class * according to dev->type */ static const unsigned short netdev_lock_type[] = @@ -1758,19 +1758,19 @@ gso: if (dev->flags & IFF_UP) { int cpu = smp_processor_id(); /* ok because BHs are off */ - if (dev->xmit_lock_owner != cpu) { + if (txq->xmit_lock_owner != cpu) { - HARD_TX_LOCK(dev, cpu); + HARD_TX_LOCK(dev, txq, cpu); if (!netif_queue_stopped(dev) && !netif_subqueue_stopped(dev, skb)) { rc = 0; if (!dev_hard_start_xmit(skb, dev)) { - HARD_TX_UNLOCK(dev); + HARD_TX_UNLOCK(dev, txq); goto out; } } - HARD_TX_UNLOCK(dev); + HARD_TX_UNLOCK(dev, txq); if (net_ratelimit()) printk(KERN_CRIT "Virtual device %s asks to " "queue packet!\n", dev->name); @@ -3761,6 +3761,20 @@ static void rollback_registered(struct net_device *dev) dev_put(dev); } +static void __netdev_init_queue_locks_one(struct netdev_queue *dev_queue, + struct net_device *dev) +{ + spin_lock_init(&dev_queue->_xmit_lock); + netdev_set_lockdep_class(&dev_queue->_xmit_lock, dev->type); + dev_queue->xmit_lock_owner = -1; +} + +static void netdev_init_queue_locks(struct net_device *dev) +{ + __netdev_init_queue_locks_one(&dev->tx_queue, dev); + __netdev_init_queue_locks_one(&dev->rx_queue, dev); +} + /** * register_netdevice - register a network device * @dev: device to register @@ -3795,9 +3809,7 @@ int register_netdevice(struct net_device *dev) BUG_ON(!dev_net(dev)); net = dev_net(dev); - spin_lock_init(&dev->_xmit_lock); - netdev_set_lockdep_class(&dev->_xmit_lock, dev->type); - dev->xmit_lock_owner = -1; + netdev_init_queue_locks(dev); dev->iflink = -1; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 74884f4a6255..819afc449e1e 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -74,6 +74,16 @@ static const struct proto_ops nr_proto_ops; */ static struct lock_class_key nr_netdev_xmit_lock_key; +static void nr_set_lockdep_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key); +} + +static void nr_set_lockdep_key(struct net_device *dev) +{ + nr_set_lockdep_one(&dev->tx_queue); +} + /* * Socket removal during an interrupt is now safe. */ @@ -1430,7 +1440,7 @@ static int __init nr_proto_init(void) free_netdev(dev); goto fail; } - lockdep_set_class(&dev->_xmit_lock, &nr_netdev_xmit_lock_key); + nr_set_lockdep_key(dev); dev_nr[i] = dev; } diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 46461a69cd0f..7dbbc0891623 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -75,6 +75,16 @@ ax25_address rose_callsign; */ static struct lock_class_key rose_netdev_xmit_lock_key; +static void rose_set_lockdep_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key); +} + +static void rose_set_lockdep_key(struct net_device *dev) +{ + rose_set_lockdep_one(&dev->tx_queue); +} + /* * Convert a ROSE address into text. */ @@ -1576,7 +1586,7 @@ static int __init rose_proto_init(void) free_netdev(dev); goto fail; } - lockdep_set_class(&dev->_xmit_lock, &rose_netdev_xmit_lock_key); + rose_set_lockdep_key(dev); dev_rose[i] = dev; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index fcc7533f0bcc..b6a36d394663 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -92,10 +92,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, struct netdev_queue *dev_queue, struct Qdisc *q) { - struct net_device *dev = dev_queue->dev; int ret; - if (unlikely(dev->xmit_lock_owner == smp_processor_id())) { + if (unlikely(dev_queue->xmit_lock_owner == smp_processor_id())) { /* * Same CPU holding the lock. It may be a transient * configuration error, when hard_start_xmit() recurses. We @@ -105,7 +104,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, kfree_skb(skb); if (net_ratelimit()) printk(KERN_WARNING "Dead loop on netdevice %s, " - "fix it urgently!\n", dev->name); + "fix it urgently!\n", dev_queue->dev->name); ret = qdisc_qlen(q); } else { /* @@ -155,10 +154,10 @@ static inline int qdisc_restart(struct netdev_queue *txq) dev = txq->dev; - HARD_TX_LOCK(dev, smp_processor_id()); + HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) ret = dev_hard_start_xmit(skb, dev); - HARD_TX_UNLOCK(dev); + HARD_TX_UNLOCK(dev, txq); spin_lock(&txq->lock); q = txq->qdisc; -- cgit v1.2.3 From b19fa1fa91845234961c64dbd564671aa7c0fd27 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:14:24 -0700 Subject: net: Delete NETDEVICES_MULTIQUEUE kconfig option. Multiple TX queue support is a core networking feature. Signed-off-by: David S. Miller --- Documentation/networking/multiqueue.txt | 79 +-------------------------------- drivers/net/Kconfig | 8 ---- drivers/net/cpmac.c | 14 ------ drivers/net/ixgbe/ixgbe_ethtool.c | 6 --- drivers/net/ixgbe/ixgbe_main.c | 40 ----------------- drivers/net/s2io.c | 47 ++++---------------- include/linux/netdevice.h | 14 ------ include/linux/skbuff.h | 10 ----- net/mac80211/Kconfig | 3 -- 9 files changed, 9 insertions(+), 212 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt index ea5a42e8f79f..e6dc1ee9e8f1 100644 --- a/Documentation/networking/multiqueue.txt +++ b/Documentation/networking/multiqueue.txt @@ -3,19 +3,11 @@ =========================================== Section 1: Base driver requirements for implementing multiqueue support -Section 2: Qdisc support for multiqueue devices -Section 3: Brief howto using PRIO or RR for multiqueue devices - Intro: Kernel support for multiqueue devices --------------------------------------------------------- -Kernel support for multiqueue devices is only an API that is presented to the -netdevice layer for base drivers to implement. This feature is part of the -core networking stack, and all network devices will be running on the -multiqueue-aware stack. If a base driver only has one queue, then these -changes are transparent to that driver. - +Kernel support for multiqueue devices is always present. Section 1: Base driver requirements for implementing multiqueue support ----------------------------------------------------------------------- @@ -43,73 +35,4 @@ bitmap on device initialization. Below is an example from e1000: netdev->features |= NETIF_F_MULTI_QUEUE; #endif - -Section 2: Qdisc support for multiqueue devices ------------------------------------------------ - -Currently two qdiscs support multiqueue devices. A new round-robin qdisc, -sch_rr, and sch_prio. The qdisc is responsible for classifying the skb's to -bands and queues, and will store the queue mapping into skb->queue_mapping. -Use this field in the base driver to determine which queue to send the skb -to. - -sch_rr has been added for hardware that doesn't want scheduling policies from -software, so it's a straight round-robin qdisc. It uses the same syntax and -classification priomap that sch_prio uses, so it should be intuitive to -configure for people who've used sch_prio. - -In order to utilitize the multiqueue features of the qdiscs, the network -device layer needs to enable multiple queue support. This can be done by -selecting NETDEVICES_MULTIQUEUE under Drivers. - -The PRIO qdisc naturally plugs into a multiqueue device. If -NETDEVICES_MULTIQUEUE is selected, then on qdisc load, the number of -bands requested is compared to the number of queues on the hardware. If they -are equal, it sets a one-to-one mapping up between the queues and bands. If -they're not equal, it will not load the qdisc. This is the same behavior -for RR. Once the association is made, any skb that is classified will have -skb->queue_mapping set, which will allow the driver to properly queue skb's -to multiple queues. - - -Section 3: Brief howto using PRIO and RR for multiqueue devices ---------------------------------------------------------------- - -The userspace command 'tc,' part of the iproute2 package, is used to configure -qdiscs. To add the PRIO qdisc to your network device, assuming the device is -called eth0, run the following command: - -# tc qdisc add dev eth0 root handle 1: prio bands 4 multiqueue - -This will create 4 bands, 0 being highest priority, and associate those bands -to the queues on your NIC. Assuming eth0 has 4 Tx queues, the band mapping -would look like: - -band 0 => queue 0 -band 1 => queue 1 -band 2 => queue 2 -band 3 => queue 3 - -Traffic will begin flowing through each queue if your TOS values are assigning -traffic across the various bands. For example, ssh traffic will always try to -go out band 0 based on TOS -> Linux priority conversion (realtime traffic), -so it will be sent out queue 0. ICMP traffic (pings) fall into the "normal" -traffic classification, which is band 1. Therefore pings will be send out -queue 1 on the NIC. - -Note the use of the multiqueue keyword. This is only in versions of iproute2 -that support multiqueue networking devices; if this is omitted when loading -a qdisc onto a multiqueue device, the qdisc will load and operate the same -if it were loaded onto a single-queue device (i.e. - sends all traffic to -queue 0). - -Another alternative to multiqueue band allocation can be done by using the -multiqueue option and specify 0 bands. If this is the case, the qdisc will -allocate the number of bands to equal the number of queues that the device -reports, and bring the qdisc online. - -The behavior of tc filters remains the same, where it will override TOS priority -classification. - - Author: Peter P. Waskiewicz Jr. diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index ef733abc857d..4675c1bd6fb9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -26,14 +26,6 @@ menuconfig NETDEVICES # that for each of the symbols. if NETDEVICES -config NETDEVICES_MULTIQUEUE - bool "Netdevice multiple hardware queue support" - ---help--- - Say Y here if you want to allow the network stack to use multiple - hardware TX queues on an ethernet device. - - Most people will say N here. - config IFB tristate "Intermediate Functional Block support" depends on NET_CLS_ACT diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 7f3f62e1b113..d630e2a72f42 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -569,11 +569,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) len = max(skb->len, ETH_ZLEN); queue = skb_get_queue_mapping(skb); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(dev, queue); -#else - netif_stop_queue(dev); -#endif desc = &priv->desc_ring[queue]; if (unlikely(desc->dataflags & CPMAC_OWN)) { @@ -626,24 +622,14 @@ static void cpmac_end_xmit(struct net_device *dev, int queue) dev_kfree_skb_irq(desc->skb); desc->skb = NULL; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (netif_subqueue_stopped(dev, queue)) netif_wake_subqueue(dev, queue); -#else - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -#endif } else { if (netif_msg_tx_err(priv) && net_ratelimit()) printk(KERN_WARNING "%s: end_xmit: spurious interrupt\n", dev->name); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (netif_subqueue_stopped(dev, queue)) netif_wake_subqueue(dev, queue); -#else - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -#endif } } diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 12990b1fe7e4..81b769093d22 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -252,21 +252,15 @@ static int ixgbe_set_tso(struct net_device *netdev, u32 data) netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; } else { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE struct ixgbe_adapter *adapter = netdev_priv(netdev); int i; -#endif netif_stop_queue(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_stop_subqueue(netdev, i); -#endif netdev->features &= ~NETIF_F_TSO; netdev->features &= ~NETIF_F_TSO6; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_start_subqueue(netdev, i); -#endif netif_start_queue(netdev); } return 0; diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index b37d618d8e2a..10a1c8c5cda1 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -266,28 +266,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_adapter *adapter, * sees the new next_to_clean. */ smp_mb(); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) && !test_bit(__IXGBE_DOWN, &adapter->state)) { netif_wake_subqueue(netdev, tx_ring->queue_index); adapter->restart_queue++; } -#else - if (netif_queue_stopped(netdev) && - !test_bit(__IXGBE_DOWN, &adapter->state)) { - netif_wake_queue(netdev); - adapter->restart_queue++; - } -#endif } if (adapter->detect_tx_hung) if (ixgbe_check_tx_hang(adapter, tx_ring, eop, eop_desc)) -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(netdev, tx_ring->queue_index); -#else - netif_stop_queue(netdev); -#endif if (total_tx_packets >= tx_ring->work_limit) IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, tx_ring->eims_value); @@ -2192,11 +2180,7 @@ static void __devinit ixgbe_set_num_queues(struct ixgbe_adapter *adapter) case (IXGBE_FLAG_RSS_ENABLED): rss_m = 0xF; nrq = rss_i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE ntq = rss_i; -#else - ntq = 1; -#endif break; case 0: default: @@ -2370,10 +2354,8 @@ try_msi: } out: -#ifdef CONFIG_NETDEVICES_MULTIQUEUE /* Notify the stack of the (possibly) reduced Tx Queue count. */ adapter->netdev->egress_subqueue_count = adapter->num_tx_queues; -#endif return err; } @@ -2910,9 +2892,7 @@ static void ixgbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; bool link_up; u32 link_speed = 0; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE int i; -#endif adapter->hw.mac.ops.check_link(&adapter->hw, &(link_speed), &link_up); @@ -2934,10 +2914,8 @@ static void ixgbe_watchdog(unsigned long data) netif_carrier_on(netdev); netif_wake_queue(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_wake_subqueue(netdev, i); -#endif } else { /* Force detection of hung controller */ adapter->detect_tx_hung = true; @@ -3264,11 +3242,7 @@ static int __ixgbe_maybe_stop_tx(struct net_device *netdev, { struct ixgbe_adapter *adapter = netdev_priv(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(netdev, tx_ring->queue_index); -#else - netif_stop_queue(netdev); -#endif /* Herbert's original patch had: * smp_mb__after_netif_stop_queue(); * but since that doesn't exist yet, just open code it. */ @@ -3280,11 +3254,7 @@ static int __ixgbe_maybe_stop_tx(struct net_device *netdev, return -EBUSY; /* A reprieve! - use start_queue because it doesn't call schedule */ -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_wake_subqueue(netdev, tx_ring->queue_index); -#else - netif_wake_queue(netdev); -#endif ++adapter->restart_queue; return 0; } @@ -3312,9 +3282,7 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) unsigned int f; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; len -= skb->data_len; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE r_idx = (adapter->num_tx_queues - 1) & skb->queue_mapping; -#endif tx_ring = &adapter->tx_ring[r_idx]; @@ -3502,11 +3470,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_save_state(pdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), MAX_TX_QUEUES); -#else - netdev = alloc_etherdev(sizeof(struct ixgbe_adapter)); -#endif if (!netdev) { err = -ENOMEM; goto err_alloc_etherdev; @@ -3598,9 +3562,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netdev->features |= NETIF_F_MULTI_QUEUE; -#endif /* make sure the EEPROM is good */ if (ixgbe_validate_eeprom_checksum(hw, NULL) < 0) { @@ -3668,10 +3630,8 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, netif_carrier_off(netdev); netif_stop_queue(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_stop_subqueue(netdev, i); -#endif ixgbe_napi_add_all(adapter); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index e7a3dbec674c..51a91154125d 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -546,13 +546,10 @@ static struct pci_driver s2io_driver = { static inline void s2io_stop_all_tx_queue(struct s2io_nic *sp) { int i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { for (i = 0; i < sp->config.tx_fifo_num; i++) netif_stop_subqueue(sp->dev, i); - } else -#endif - { + } else { for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_STOP; netif_stop_queue(sp->dev); @@ -561,12 +558,9 @@ static inline void s2io_stop_all_tx_queue(struct s2io_nic *sp) static inline void s2io_stop_tx_queue(struct s2io_nic *sp, int fifo_no) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) netif_stop_subqueue(sp->dev, fifo_no); - else -#endif - { + else { sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_STOP; netif_stop_queue(sp->dev); @@ -576,13 +570,10 @@ static inline void s2io_stop_tx_queue(struct s2io_nic *sp, int fifo_no) static inline void s2io_start_all_tx_queue(struct s2io_nic *sp) { int i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { for (i = 0; i < sp->config.tx_fifo_num; i++) netif_start_subqueue(sp->dev, i); - } else -#endif - { + } else { for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; netif_start_queue(sp->dev); @@ -591,12 +582,9 @@ static inline void s2io_start_all_tx_queue(struct s2io_nic *sp) static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) netif_start_subqueue(sp->dev, fifo_no); - else -#endif - { + else { sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_START; netif_start_queue(sp->dev); @@ -606,13 +594,10 @@ static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no) static inline void s2io_wake_all_tx_queue(struct s2io_nic *sp) { int i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { for (i = 0; i < sp->config.tx_fifo_num; i++) netif_wake_subqueue(sp->dev, i); - } else -#endif - { + } else { for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; netif_wake_queue(sp->dev); @@ -623,13 +608,10 @@ static inline void s2io_wake_tx_queue( struct fifo_info *fifo, int cnt, u8 multiq) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (multiq) { if (cnt && __netif_subqueue_stopped(fifo->dev, fifo->fifo_no)) netif_wake_subqueue(fifo->dev, fifo->fifo_no); - } else -#endif - if (cnt && (fifo->queue_state == FIFO_QUEUE_STOP)) { + } else if (cnt && (fifo->queue_state == FIFO_QUEUE_STOP)) { if (netif_queue_stopped(fifo->dev)) { fifo->queue_state = FIFO_QUEUE_START; netif_wake_queue(fifo->dev); @@ -4189,15 +4171,12 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_LOCKED; } -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { if (__netif_subqueue_stopped(dev, fifo->fifo_no)) { spin_unlock_irqrestore(&fifo->tx_lock, flags); return NETDEV_TX_BUSY; } - } else -#endif - if (unlikely(fifo->queue_state == FIFO_QUEUE_STOP)) { + } else if (unlikely(fifo->queue_state == FIFO_QUEUE_STOP)) { if (netif_queue_stopped(dev)) { spin_unlock_irqrestore(&fifo->tx_lock, flags); return NETDEV_TX_BUSY; @@ -7633,12 +7612,6 @@ static int s2io_verify_parm(struct pci_dev *pdev, u8 *dev_intr_type, DBG_PRINT(ERR_DBG, "tx fifos\n"); } -#ifndef CONFIG_NETDEVICES_MULTIQUEUE - if (multiq) { - DBG_PRINT(ERR_DBG, "s2io: Multiqueue support not enabled\n"); - multiq = 0; - } -#endif if (multiq) *dev_multiq = multiq; @@ -7783,12 +7756,10 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) pci_disable_device(pdev); return -ENODEV; } -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (dev_multiq) dev = alloc_etherdev_mq(sizeof(struct s2io_nic), tx_fifo_num); else -#endif - dev = alloc_etherdev(sizeof(struct s2io_nic)); + dev = alloc_etherdev(sizeof(struct s2io_nic)); if (dev == NULL) { DBG_PRINT(ERR_DBG, "Device allocation failed\n"); pci_disable_device(pdev); @@ -7979,10 +7950,8 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) dev->features |= NETIF_F_UFO; dev->features |= NETIF_F_HW_CSUM; } -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (config->multiq) dev->features |= NETIF_F_MULTI_QUEUE; -#endif dev->tx_timeout = &s2io_tx_watchdog; dev->watchdog_timeo = WATCH_DOG_TIMEOUT; INIT_WORK(&sp->rst_timer_task, s2io_restart_nic); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c8d5f128858d..e2d931f9b700 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1043,9 +1043,7 @@ static inline int netif_running(const struct net_device *dev) */ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); -#endif } /** @@ -1057,13 +1055,11 @@ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) */ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; #endif set_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); -#endif } /** @@ -1076,12 +1072,8 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE return test_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); -#else - return 0; -#endif } static inline int netif_subqueue_stopped(const struct net_device *dev, @@ -1099,7 +1091,6 @@ static inline int netif_subqueue_stopped(const struct net_device *dev, */ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; @@ -1107,7 +1098,6 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) __netif_schedule(&dev->tx_queue); -#endif } /** @@ -1119,11 +1109,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) */ static inline int netif_is_multiqueue(const struct net_device *dev) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE return (!!(NETIF_F_MULTI_QUEUE & dev->features)); -#else - return 0; -#endif } /* Use this variant when it is known for sure that it diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2220b9e2dab0..8f10e3d08fd9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -305,9 +305,7 @@ struct sk_buff { #endif int iif; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE __u16 queue_mapping; -#endif #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ #ifdef CONFIG_NET_CLS_ACT @@ -1671,25 +1669,17 @@ static inline void skb_init_secmark(struct sk_buff *skb) static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE skb->queue_mapping = queue_mapping; -#endif } static inline u16 skb_get_queue_mapping(struct sk_buff *skb) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE return skb->queue_mapping; -#else - return 0; -#endif } static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_buff *from) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE to->queue_mapping = from->queue_mapping; -#endif } static inline int skb_is_gso(const struct sk_buff *skb) diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 40f1add17753..d2038418e2bd 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -15,14 +15,11 @@ config MAC80211_QOS def_bool y depends on MAC80211 depends on NET_SCHED - depends on NETDEVICES_MULTIQUEUE comment "QoS/HT support disabled" depends on MAC80211 && !MAC80211_QOS comment "QoS/HT support needs CONFIG_NET_SCHED" depends on MAC80211 && !NET_SCHED -comment "QoS/HT support needs CONFIG_NETDEVICES_MULTIQUEUE" - depends on MAC80211 && !NETDEVICES_MULTIQUEUE menu "Rate control algorithm selection" depends on MAC80211 != n -- cgit v1.2.3 From 79d16385c7f287a33ea771c4dbe60ae43f791b49 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:14:46 -0700 Subject: netdev: Move atomic queue state bits into netdev_queue. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 55 +++++++++++++++++++++++++++++++++-------------- include/net/pkt_sched.h | 2 +- net/sched/sch_generic.c | 20 +++++++++-------- 3 files changed, 51 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e2d931f9b700..203c5504fe43 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -281,14 +281,12 @@ struct header_ops { enum netdev_state_t { - __LINK_STATE_XOFF=0, __LINK_STATE_START, __LINK_STATE_PRESENT, __LINK_STATE_SCHED, __LINK_STATE_NOCARRIER, __LINK_STATE_LINKWATCH_PENDING, __LINK_STATE_DORMANT, - __LINK_STATE_QDISC_RUNNING, }; @@ -448,10 +446,17 @@ static inline void napi_synchronize(const struct napi_struct *n) # define napi_synchronize(n) barrier() #endif +enum netdev_queue_state_t +{ + __QUEUE_STATE_XOFF, + __QUEUE_STATE_QDISC_RUNNING, +}; + struct netdev_queue { spinlock_t lock; struct net_device *dev; struct Qdisc *qdisc; + unsigned long state; struct sk_buff *gso_skb; spinlock_t _xmit_lock; int xmit_lock_owner; @@ -952,9 +957,7 @@ extern void __netif_schedule(struct netdev_queue *txq); static inline void netif_schedule_queue(struct netdev_queue *txq) { - struct net_device *dev = txq->dev; - - if (!test_bit(__LINK_STATE_XOFF, &dev->state)) + if (!test_bit(__QUEUE_STATE_XOFF, &txq->state)) __netif_schedule(txq); } @@ -969,9 +972,14 @@ static inline void netif_schedule(struct net_device *dev) * * Allow upper layers to call the device hard_start_xmit routine. */ +static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) +{ + clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + static inline void netif_start_queue(struct net_device *dev) { - clear_bit(__LINK_STATE_XOFF, &dev->state); + netif_tx_start_queue(&dev->tx_queue); } /** @@ -981,16 +989,21 @@ static inline void netif_start_queue(struct net_device *dev) * Allow upper layers to call the device hard_start_xmit routine. * Used for flow control when transmit resources are available. */ -static inline void netif_wake_queue(struct net_device *dev) +static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) { #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) { - clear_bit(__LINK_STATE_XOFF, &dev->state); + clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); return; } #endif - if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) - __netif_schedule(&dev->tx_queue); + if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state)) + __netif_schedule(dev_queue); +} + +static inline void netif_wake_queue(struct net_device *dev) +{ + netif_tx_wake_queue(&dev->tx_queue); } /** @@ -1000,9 +1013,14 @@ static inline void netif_wake_queue(struct net_device *dev) * Stop upper layers calling the device hard_start_xmit routine. * Used for flow control when transmit resources are unavailable. */ +static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) +{ + set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + static inline void netif_stop_queue(struct net_device *dev) { - set_bit(__LINK_STATE_XOFF, &dev->state); + netif_tx_stop_queue(&dev->tx_queue); } /** @@ -1011,9 +1029,14 @@ static inline void netif_stop_queue(struct net_device *dev) * * Test if transmit queue on device is currently unable to send. */ +static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) +{ + return test_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + static inline int netif_queue_stopped(const struct net_device *dev) { - return test_bit(__LINK_STATE_XOFF, &dev->state); + return netif_tx_queue_stopped(&dev->tx_queue); } /** @@ -1043,7 +1066,7 @@ static inline int netif_running(const struct net_device *dev) */ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) { - clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); } /** @@ -1059,7 +1082,7 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) if (netpoll_trap()) return; #endif - set_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + set_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); } /** @@ -1072,7 +1095,7 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { - return test_bit(__LINK_STATE_XOFF, + return test_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); } @@ -1095,7 +1118,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) if (netpoll_trap()) return; #endif - if (test_and_clear_bit(__LINK_STATE_XOFF, + if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) __netif_schedule(&dev->tx_queue); } diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 2311d242bb35..d58c1a5eb845 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -91,7 +91,7 @@ static inline void qdisc_run(struct netdev_queue *txq) struct net_device *dev = txq->dev; if (!netif_queue_stopped(dev) && - !test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + !test_and_set_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state)) __qdisc_run(txq); } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index b6a36d394663..243de935b182 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -121,9 +121,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, /* * NOTE: Called under queue->lock with locally disabled BH. * - * __LINK_STATE_QDISC_RUNNING guarantees only one CPU can process this - * device at a time. queue->lock serializes queue accesses for - * this device AND txq->qdisc pointer itself. + * __QUEUE_STATE_QDISC_RUNNING guarantees only one CPU can process + * this queue at a time. queue->lock serializes queue accesses for + * this queue AND txq->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. * @@ -206,7 +206,7 @@ void __qdisc_run(struct netdev_queue *txq) } } - clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); + clear_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state); } static void dev_watchdog(unsigned long arg) @@ -605,9 +605,10 @@ static void dev_deactivate_queue(struct netdev_queue *dev_queue, void dev_deactivate(struct net_device *dev) { + struct netdev_queue *dev_queue = &dev->tx_queue; int running; - dev_deactivate_queue(&dev->tx_queue, &noop_qdisc); + dev_deactivate_queue(dev_queue, &noop_qdisc); dev_watchdog_down(dev); @@ -616,16 +617,17 @@ void dev_deactivate(struct net_device *dev) /* Wait for outstanding qdisc_run calls. */ do { - while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + while (test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state)) yield(); /* * Double-check inside queue lock to ensure that all effects * of the queue run are visible when we return. */ - spin_lock_bh(&dev->tx_queue.lock); - running = test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); - spin_unlock_bh(&dev->tx_queue.lock); + spin_lock_bh(&dev_queue->lock); + running = test_bit(__QUEUE_STATE_QDISC_RUNNING, + &dev_queue->state); + spin_unlock_bh(&dev_queue->lock); /* * The running flag should never be set at this point because -- cgit v1.2.3 From b845f313d78e4e259ec449909e3bbadf77b53a6d Mon Sep 17 00:00:00 2001 From: Dave Kleikamp Date: Tue, 8 Jul 2008 00:28:51 +1000 Subject: mm: Allow architectures to define additional protection bits This patch allows architectures to define functions to deal with additional protections bits for mmap() and mprotect(). arch_calc_vm_prot_bits() maps additonal protection bits to vm_flags arch_vm_get_page_prot() maps additional vm_flags to the vma's vm_page_prot arch_validate_prot() checks for valid values of the protection bits Note: vm_get_page_prot() is now pretty ugly, but the generated code should be identical for architectures that don't define additional protection bits. Signed-off-by: Dave Kleikamp Acked-by: Andrew Morton Acked-by: Hugh Dickins Signed-off-by: Benjamin Herrenschmidt --- include/linux/mman.h | 29 ++++++++++++++++++++++++++++- mm/mmap.c | 5 +++-- mm/mprotect.c | 2 +- 3 files changed, 32 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mman.h b/include/linux/mman.h index dab8892e6ff1..30d1073bac3b 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -33,6 +33,32 @@ static inline void vm_unacct_memory(long pages) vm_acct_memory(-pages); } +/* + * Allow architectures to handle additional protection bits + */ + +#ifndef arch_calc_vm_prot_bits +#define arch_calc_vm_prot_bits(prot) 0 +#endif + +#ifndef arch_vm_get_page_prot +#define arch_vm_get_page_prot(vm_flags) __pgprot(0) +#endif + +#ifndef arch_validate_prot +/* + * This is called from mprotect(). PROT_GROWSDOWN and PROT_GROWSUP have + * already been masked out. + * + * Returns true if the prot flags are valid + */ +static inline int arch_validate_prot(unsigned long prot) +{ + return (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM)) == 0; +} +#define arch_validate_prot arch_validate_prot +#endif + /* * Optimisation macro. It is equivalent to: * (x & bit1) ? bit2 : 0 @@ -51,7 +77,8 @@ calc_vm_prot_bits(unsigned long prot) { return _calc_vm_trans(prot, PROT_READ, VM_READ ) | _calc_vm_trans(prot, PROT_WRITE, VM_WRITE) | - _calc_vm_trans(prot, PROT_EXEC, VM_EXEC ); + _calc_vm_trans(prot, PROT_EXEC, VM_EXEC) | + arch_calc_vm_prot_bits(prot); } /* diff --git a/mm/mmap.c b/mm/mmap.c index 3354fdd83d4b..1d102b956fd8 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -72,8 +72,9 @@ pgprot_t protection_map[16] = { pgprot_t vm_get_page_prot(unsigned long vm_flags) { - return protection_map[vm_flags & - (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]; + return __pgprot(pgprot_val(protection_map[vm_flags & + (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]) | + pgprot_val(arch_vm_get_page_prot(vm_flags))); } EXPORT_SYMBOL(vm_get_page_prot); diff --git a/mm/mprotect.c b/mm/mprotect.c index a5bf31c27375..ecfaa5844b5f 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -239,7 +239,7 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot) end = start + len; if (end <= start) return -ENOMEM; - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM)) + if (!arch_validate_prot(prot)) return -EINVAL; reqprot = prot; -- cgit v1.2.3 From aba46c5027cb59d98052231b36efcbbde9c77a1d Mon Sep 17 00:00:00 2001 From: Dave Kleikamp Date: Tue, 8 Jul 2008 00:28:52 +1000 Subject: powerpc/mm: Define flags for Strong Access Ordering This patch defines: - PROT_SAO, which is passed into mmap() and mprotect() in the prot field - VM_SAO in vma->vm_flags, and - _PAGE_SAO, the combination of WIMG bits in the pte that enables strong access ordering for the page. Signed-off-by: Dave Kleikamp Signed-off-by: Benjamin Herrenschmidt --- include/asm-powerpc/mman.h | 2 ++ include/asm-powerpc/pgtable-ppc64.h | 3 +++ include/linux/mm.h | 1 + 3 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/asm-powerpc/mman.h b/include/asm-powerpc/mman.h index 24cf664a8295..0c46bf2c7d5f 100644 --- a/include/asm-powerpc/mman.h +++ b/include/asm-powerpc/mman.h @@ -10,6 +10,8 @@ * 2 of the License, or (at your option) any later version. */ +#define PROT_SAO 0x10 /* Strong Access Ordering */ + #define MAP_RENAME MAP_ANONYMOUS /* In SunOS terminology */ #define MAP_NORESERVE 0x40 /* don't reserve swap pages */ #define MAP_LOCKED 0x80 diff --git a/include/asm-powerpc/pgtable-ppc64.h b/include/asm-powerpc/pgtable-ppc64.h index b2754d46be44..d09599cccb35 100644 --- a/include/asm-powerpc/pgtable-ppc64.h +++ b/include/asm-powerpc/pgtable-ppc64.h @@ -93,6 +93,9 @@ #define _PAGE_RW 0x0200 /* software: user write access allowed */ #define _PAGE_BUSY 0x0800 /* software: PTE & hash are busy */ +/* Strong Access Ordering */ +#define _PAGE_SAO (_PAGE_WRITETHRU | _PAGE_NO_CACHE | _PAGE_COHERENT) + #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT) #define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY) diff --git a/include/linux/mm.h b/include/linux/mm.h index 586a943cab01..689184446fc6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -108,6 +108,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_CAN_NONLINEAR 0x08000000 /* Has ->fault & does nonlinear pages */ #define VM_MIXEDMAP 0x10000000 /* Can contain "struct page" and pure PFN pages */ +#define VM_SAO 0x20000000 /* Strong Access Ordering (powerpc) */ #ifndef VM_STACK_DEFAULT_FLAGS /* arch can override this */ #define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS -- cgit v1.2.3 From 2116271a347d1181b5497602c2bfada1de8fd53b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 May 2008 19:34:39 -0400 Subject: NFS: Add correct bounds checking to NFSv2 locks NFSv2 file locking currently fails the Connectathon tests, because the calls to the VFS locking code do not return an EINVAL error if the struct file_lock overflows the 32-bit boundaries. The problem is due to the fact that we occasionally call helpers from fs/locks.c in order to avoid RPC calls to the server when we know that a local process holds the lock. These helpers are, of course, always 64-bit enabled, so EINVAL is not returned in cases when it would if the call had gone to the NLM code. For consistency, we therefore add support for a bounds-checking helper. Signed-off-by: Trond Myklebust --- fs/nfs/file.c | 20 +++++++++++++++----- fs/nfs/proc.c | 24 ++++++++++++++++++++++++ include/linux/nfs_xdr.h | 1 + 3 files changed, 40 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d84a3d8f32af..7c73f06692b6 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -593,6 +593,7 @@ out: static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) { struct inode * inode = filp->f_mapping->host; + int ret = -ENOLCK; dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n", inode->i_sb->s_id, inode->i_ino, @@ -602,13 +603,22 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) /* No mandatory locks over NFS */ if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) - return -ENOLCK; + goto out_err; + + if (NFS_PROTO(inode)->lock_check_bounds != NULL) { + ret = NFS_PROTO(inode)->lock_check_bounds(fl); + if (ret < 0) + goto out_err; + } if (IS_GETLK(cmd)) - return do_getlk(filp, cmd, fl); - if (fl->fl_type == F_UNLCK) - return do_unlk(filp, cmd, fl); - return do_setlk(filp, cmd, fl); + ret = do_getlk(filp, cmd, fl); + else if (fl->fl_type == F_UNLCK) + ret = do_unlk(filp, cmd, fl); + else + ret = do_setlk(filp, cmd, fl); +out_err: + return ret; } /* diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 03599bfe81cf..5c35b02857f3 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -598,6 +598,29 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); } +/* Helper functions for NFS lock bounds checking */ +#define NFS_LOCK32_OFFSET_MAX ((__s32)0x7fffffffUL) +static int nfs_lock_check_bounds(const struct file_lock *fl) +{ + __s32 start, end; + + start = (__s32)fl->fl_start; + if ((loff_t)start != fl->fl_start) + goto out_einval; + + if (fl->fl_end != OFFSET_MAX) { + end = (__s32)fl->fl_end; + if ((loff_t)end != fl->fl_end) + goto out_einval; + } else + end = NFS_LOCK32_OFFSET_MAX; + + if (start < 0 || start > end) + goto out_einval; + return 0; +out_einval: + return -EINVAL; +} const struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ @@ -633,4 +656,5 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .file_open = nfs_open, .file_release = nfs_release, .lock = nfs_proc_lock, + .lock_check_bounds = nfs_lock_check_bounds, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 24263bb8e0be..8d780de371f0 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -832,6 +832,7 @@ struct nfs_rpc_ops { int (*file_open) (struct inode *, struct file *); int (*file_release) (struct inode *, struct file *); int (*lock)(struct file *, int, struct file_lock *); + int (*lock_check_bounds)(const struct file_lock *); void (*clear_acl_cache)(struct inode *); }; -- cgit v1.2.3 From b6b6152c46861dd914d0e6cea9c27df057d6e235 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Mon, 9 Jun 2008 16:51:31 -0400 Subject: rpc: bring back cl_chatty The cl_chatty flag alows us to control whether a given rpc client leaves "server X not responding, timed out" messages in the syslog. Such messages make sense for ordinary nfs clients (where an unresponsive server means applications on the mountpoint are probably hanging), but not for the callback client (which can fail more commonly, with the only result just of disabling some optimizations). Previously cl_chatty was removed, do to lack of users; reinstate it, and use it for the nfsd's callback client. Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- fs/nfsd/nfs4callback.c | 2 +- include/linux/sunrpc/clnt.h | 4 +++- net/sunrpc/clnt.c | 16 +++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 4d4760e687c3..702fa577aa6e 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -381,7 +381,7 @@ static int do_probe_callback(void *data) .program = &cb_program, .version = nfs_cb_version[1]->number, .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */ - .flags = (RPC_CLNT_CREATE_NOPING), + .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), }; struct rpc_message msg = { .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 6fff7f82ef12..764fd4c286e0 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -42,7 +42,8 @@ struct rpc_clnt { unsigned int cl_softrtry : 1,/* soft timeouts */ cl_discrtry : 1,/* disconnect before retry */ - cl_autobind : 1;/* use getport() */ + cl_autobind : 1,/* use getport() */ + cl_chatty : 1;/* be verbose */ struct rpc_rtt * cl_rtt; /* RTO estimator data */ const struct rpc_timeout *cl_timeout; /* Timeout strategy */ @@ -114,6 +115,7 @@ struct rpc_create_args { #define RPC_CLNT_CREATE_NONPRIVPORT (1UL << 3) #define RPC_CLNT_CREATE_NOPING (1UL << 4) #define RPC_CLNT_CREATE_DISCRTRY (1UL << 5) +#define RPC_CLNT_CREATE_QUIET (1UL << 6) struct rpc_clnt *rpc_create(struct rpc_create_args *args); struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 0530eea37b59..09631f6e30e9 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -324,6 +324,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) clnt->cl_autobind = 1; if (args->flags & RPC_CLNT_CREATE_DISCRTRY) clnt->cl_discrtry = 1; + if (!(args->flags & RPC_CLNT_CREATE_QUIET)) + clnt->cl_chatty = 1; return clnt; } @@ -1149,7 +1151,8 @@ call_status(struct rpc_task *task) rpc_exit(task, status); break; default: - printk("%s: RPC call returned error %d\n", + if (clnt->cl_chatty) + printk("%s: RPC call returned error %d\n", clnt->cl_protname, -status); rpc_exit(task, status); } @@ -1174,7 +1177,8 @@ call_timeout(struct rpc_task *task) task->tk_timeouts++; if (RPC_IS_SOFT(task)) { - printk(KERN_NOTICE "%s: server %s not responding, timed out\n", + if (clnt->cl_chatty) + printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); rpc_exit(task, -EIO); return; @@ -1182,7 +1186,8 @@ call_timeout(struct rpc_task *task) if (!(task->tk_flags & RPC_CALL_MAJORSEEN)) { task->tk_flags |= RPC_CALL_MAJORSEEN; - printk(KERN_NOTICE "%s: server %s not responding, still trying\n", + if (clnt->cl_chatty) + printk(KERN_NOTICE "%s: server %s not responding, still trying\n", clnt->cl_protname, clnt->cl_server); } rpc_force_rebind(clnt); @@ -1213,8 +1218,9 @@ call_decode(struct rpc_task *task) task->tk_pid, task->tk_status); if (task->tk_flags & RPC_CALL_MAJORSEEN) { - printk(KERN_NOTICE "%s: server %s OK\n", - clnt->cl_protname, clnt->cl_server); + if (clnt->cl_chatty) + printk(KERN_NOTICE "%s: server %s OK\n", + clnt->cl_protname, clnt->cl_server); task->tk_flags &= ~RPC_CALL_MAJORSEEN; } -- cgit v1.2.3 From a486aeda9b2b0d944aecce7871b3186379b898de Mon Sep 17 00:00:00 2001 From: "\\\\\\\"J. Bruce Fields\\\\\\" Date: Mon, 9 Jun 2008 16:51:35 -0400 Subject: rpc: minor cleanup of scheduler callback code Try to make the comment here a little more clear and concise. Also, this macro definition seems unnecessary. Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 1 - net/sunrpc/sched.c | 14 +++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index d1a5c8c1a0f1..64981a2f1cae 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -135,7 +135,6 @@ struct rpc_task_setup { #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) #define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS) #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) -#define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL) #define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT) #define RPC_TASK_RUNNING 0 diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6eab9bf94baf..6288af05c20f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -626,19 +626,15 @@ static void __rpc_execute(struct rpc_task *task) /* * Execute any pending callback. */ - if (RPC_DO_CALLBACK(task)) { - /* Define a callback save pointer */ + if (task->tk_callback) { void (*save_callback)(struct rpc_task *); /* - * If a callback exists, save it, reset it, - * call it. - * The save is needed to stop from resetting - * another callback set within the callback handler - * - Dave + * We set tk_callback to NULL before calling it, + * in case it sets the tk_callback field itself: */ - save_callback=task->tk_callback; - task->tk_callback=NULL; + save_callback = task->tk_callback; + task->tk_callback = NULL; save_callback(task); } -- cgit v1.2.3 From 46cb650c224bb8e64a749090105d74b9e8eda669 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 11 Jun 2008 16:32:46 -0400 Subject: NFS: Remove the redundant file_open entry from struct nfs_rpc_ops All instances are set to nfs_open(), so we should just remove the redundant indirection. Ditto for the file_release op Signed-off-by: Trond Myklebust --- fs/nfs/file.c | 4 ++-- fs/nfs/nfs3proc.c | 2 -- fs/nfs/nfs4proc.c | 2 -- fs/nfs/proc.c | 2 -- include/linux/nfs_xdr.h | 2 -- 5 files changed, 2 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 78b26f875eee..509dcb58959e 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -129,7 +129,7 @@ nfs_file_open(struct inode *inode, struct file *filp) nfs_inc_stats(inode, NFSIOS_VFSOPEN); lock_kernel(); - res = NFS_PROTO(inode)->file_open(inode, filp); + res = nfs_open(inode, filp); unlock_kernel(); return res; } @@ -147,7 +147,7 @@ nfs_file_release(struct inode *inode, struct file *filp) if (filp->f_mode & FMODE_WRITE) nfs_wb_all(dentry->d_inode); nfs_inc_stats(inode, NFSIOS_VFSRELEASE); - return NFS_PROTO(inode)->file_release(inode, filp); + return nfs_release(inode, filp); } /** diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index b9c2d995332b..1e750e4574a9 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -816,8 +816,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, .commit_done = nfs3_commit_done, - .file_open = nfs_open, - .file_release = nfs_release, .lock = nfs3_proc_lock, .clear_acl_cache = nfs3_forget_cached_acls, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 31a7e4c54a12..dfdd19a6d17e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3714,8 +3714,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .write_done = nfs4_write_done, .commit_setup = nfs4_proc_commit_setup, .commit_done = nfs4_commit_done, - .file_open = nfs_open, - .file_release = nfs_release, .lock = nfs4_proc_lock, .clear_acl_cache = nfs4_zap_acl_attr, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index c7605587d0eb..4dbb84df1b68 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -655,8 +655,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .write_setup = nfs_proc_write_setup, .write_done = nfs_write_done, .commit_setup = nfs_proc_commit_setup, - .file_open = nfs_open, - .file_release = nfs_release, .lock = nfs_proc_lock, .lock_check_bounds = nfs_lock_check_bounds, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8d780de371f0..8c77c11224d1 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -829,8 +829,6 @@ struct nfs_rpc_ops { int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_write_data *, struct rpc_message *); int (*commit_done) (struct rpc_task *, struct nfs_write_data *); - int (*file_open) (struct inode *, struct file *); - int (*file_release) (struct inode *, struct file *); int (*lock)(struct file *, int, struct file_lock *); int (*lock_check_bounds)(const struct file_lock *); void (*clear_acl_cache)(struct inode *); -- cgit v1.2.3 From 34e8f92831cb5c40b3137e47a3daf4c09016ef02 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 12 Jun 2008 12:32:25 -0400 Subject: NFS: Move fs/nfs/iostat.h to include/linux The fs/nfs/iostat.h header has definitions that were designed to be exposed to user space. Move these definitions under include/linux so user space can use the definitions in applications that read /proc/self/mountstats. Also address a handful of coding style issues called out by checkpatch.pl in fs/nfs/iostat.h. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/iostat.h | 119 +++++---------------------------------------- include/linux/nfs_iostat.h | 119 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 106 deletions(-) create mode 100644 include/linux/nfs_iostat.h (limited to 'include/linux') diff --git a/fs/nfs/iostat.h b/fs/nfs/iostat.h index 6350ecbde589..2ec65e12bfed 100644 --- a/fs/nfs/iostat.h +++ b/fs/nfs/iostat.h @@ -5,135 +5,41 @@ * * Copyright (C) 2005, 2006 Chuck Lever * - * NFS client per-mount statistics provide information about the health of - * the NFS client and the health of each NFS mount point. Generally these - * are not for detailed problem diagnosis, but simply to indicate that there - * is a problem. - * - * These counters are not meant to be human-readable, but are meant to be - * integrated into system monitoring tools such as "sar" and "iostat". As - * such, the counters are sampled by the tools over time, and are never - * zeroed after a file system is mounted. Moving averages can be computed - * by the tools by taking the difference between two instantaneous samples - * and dividing that by the time between the samples. */ #ifndef _NFS_IOSTAT #define _NFS_IOSTAT -#define NFS_IOSTAT_VERS "1.0" - -/* - * NFS byte counters - * - * 1. SERVER - the number of payload bytes read from or written to the - * server by the NFS client via an NFS READ or WRITE request. - * - * 2. NORMAL - the number of bytes read or written by applications via - * the read(2) and write(2) system call interfaces. - * - * 3. DIRECT - the number of bytes read or written from files opened - * with the O_DIRECT flag. - * - * These counters give a view of the data throughput into and out of the NFS - * client. Comparing the number of bytes requested by an application with the - * number of bytes the client requests from the server can provide an - * indication of client efficiency (per-op, cache hits, etc). - * - * These counters can also help characterize which access methods are in - * use. DIRECT by itself shows whether there is any O_DIRECT traffic. - * NORMAL + DIRECT shows how much data is going through the system call - * interface. A large amount of SERVER traffic without much NORMAL or - * DIRECT traffic shows that applications are using mapped files. - * - * NFS page counters - * - * These count the number of pages read or written via nfs_readpage(), - * nfs_readpages(), or their write equivalents. - */ -enum nfs_stat_bytecounters { - NFSIOS_NORMALREADBYTES = 0, - NFSIOS_NORMALWRITTENBYTES, - NFSIOS_DIRECTREADBYTES, - NFSIOS_DIRECTWRITTENBYTES, - NFSIOS_SERVERREADBYTES, - NFSIOS_SERVERWRITTENBYTES, - NFSIOS_READPAGES, - NFSIOS_WRITEPAGES, - __NFSIOS_BYTESMAX, -}; - -/* - * NFS event counters - * - * These counters provide a low-overhead way of monitoring client activity - * without enabling NFS trace debugging. The counters show the rate at - * which VFS requests are made, and how often the client invalidates its - * data and attribute caches. This allows system administrators to monitor - * such things as how close-to-open is working, and answer questions such - * as "why are there so many GETATTR requests on the wire?" - * - * They also count anamolous events such as short reads and writes, silly - * renames due to close-after-delete, and operations that change the size - * of a file (such operations can often be the source of data corruption - * if applications aren't using file locking properly). - */ -enum nfs_stat_eventcounters { - NFSIOS_INODEREVALIDATE = 0, - NFSIOS_DENTRYREVALIDATE, - NFSIOS_DATAINVALIDATE, - NFSIOS_ATTRINVALIDATE, - NFSIOS_VFSOPEN, - NFSIOS_VFSLOOKUP, - NFSIOS_VFSACCESS, - NFSIOS_VFSUPDATEPAGE, - NFSIOS_VFSREADPAGE, - NFSIOS_VFSREADPAGES, - NFSIOS_VFSWRITEPAGE, - NFSIOS_VFSWRITEPAGES, - NFSIOS_VFSGETDENTS, - NFSIOS_VFSSETATTR, - NFSIOS_VFSFLUSH, - NFSIOS_VFSFSYNC, - NFSIOS_VFSLOCK, - NFSIOS_VFSRELEASE, - NFSIOS_CONGESTIONWAIT, - NFSIOS_SETATTRTRUNC, - NFSIOS_EXTENDWRITE, - NFSIOS_SILLYRENAME, - NFSIOS_SHORTREAD, - NFSIOS_SHORTWRITE, - NFSIOS_DELAY, - __NFSIOS_COUNTSMAX, -}; - -#ifdef __KERNEL__ - #include #include +#include struct nfs_iostats { unsigned long long bytes[__NFSIOS_BYTESMAX]; unsigned long events[__NFSIOS_COUNTSMAX]; } ____cacheline_aligned; -static inline void nfs_inc_server_stats(struct nfs_server *server, enum nfs_stat_eventcounters stat) +static inline void nfs_inc_server_stats(struct nfs_server *server, + enum nfs_stat_eventcounters stat) { struct nfs_iostats *iostats; int cpu; cpu = get_cpu(); iostats = per_cpu_ptr(server->io_stats, cpu); - iostats->events[stat] ++; + iostats->events[stat]++; put_cpu_no_resched(); } -static inline void nfs_inc_stats(struct inode *inode, enum nfs_stat_eventcounters stat) +static inline void nfs_inc_stats(struct inode *inode, + enum nfs_stat_eventcounters stat) { nfs_inc_server_stats(NFS_SERVER(inode), stat); } -static inline void nfs_add_server_stats(struct nfs_server *server, enum nfs_stat_bytecounters stat, unsigned long addend) +static inline void nfs_add_server_stats(struct nfs_server *server, + enum nfs_stat_bytecounters stat, + unsigned long addend) { struct nfs_iostats *iostats; int cpu; @@ -144,7 +50,9 @@ static inline void nfs_add_server_stats(struct nfs_server *server, enum nfs_stat put_cpu_no_resched(); } -static inline void nfs_add_stats(struct inode *inode, enum nfs_stat_bytecounters stat, unsigned long addend) +static inline void nfs_add_stats(struct inode *inode, + enum nfs_stat_bytecounters stat, + unsigned long addend) { nfs_add_server_stats(NFS_SERVER(inode), stat, addend); } @@ -160,5 +68,4 @@ static inline void nfs_free_iostats(struct nfs_iostats *stats) free_percpu(stats); } -#endif -#endif +#endif /* _NFS_IOSTAT */ diff --git a/include/linux/nfs_iostat.h b/include/linux/nfs_iostat.h new file mode 100644 index 000000000000..1cb9a3fed2b3 --- /dev/null +++ b/include/linux/nfs_iostat.h @@ -0,0 +1,119 @@ +/* + * User-space visible declarations for NFS client per-mount + * point statistics + * + * Copyright (C) 2005, 2006 Chuck Lever + * + * NFS client per-mount statistics provide information about the + * health of the NFS client and the health of each NFS mount point. + * Generally these are not for detailed problem diagnosis, but + * simply to indicate that there is a problem. + * + * These counters are not meant to be human-readable, but are meant + * to be integrated into system monitoring tools such as "sar" and + * "iostat". As such, the counters are sampled by the tools over + * time, and are never zeroed after a file system is mounted. + * Moving averages can be computed by the tools by taking the + * difference between two instantaneous samples and dividing that + * by the time between the samples. + */ + +#ifndef _LINUX_NFS_IOSTAT +#define _LINUX_NFS_IOSTAT + +#define NFS_IOSTAT_VERS "1.0" + +/* + * NFS byte counters + * + * 1. SERVER - the number of payload bytes read from or written + * to the server by the NFS client via an NFS READ or WRITE + * request. + * + * 2. NORMAL - the number of bytes read or written by applications + * via the read(2) and write(2) system call interfaces. + * + * 3. DIRECT - the number of bytes read or written from files + * opened with the O_DIRECT flag. + * + * These counters give a view of the data throughput into and out + * of the NFS client. Comparing the number of bytes requested by + * an application with the number of bytes the client requests from + * the server can provide an indication of client efficiency + * (per-op, cache hits, etc). + * + * These counters can also help characterize which access methods + * are in use. DIRECT by itself shows whether there is any O_DIRECT + * traffic. NORMAL + DIRECT shows how much data is going through + * the system call interface. A large amount of SERVER traffic + * without much NORMAL or DIRECT traffic shows that applications + * are using mapped files. + * + * NFS page counters + * + * These count the number of pages read or written via nfs_readpage(), + * nfs_readpages(), or their write equivalents. + * + * NB: When adding new byte counters, please include the measured + * units in the name of each byte counter to help users of this + * interface determine what exactly is being counted. + */ +enum nfs_stat_bytecounters { + NFSIOS_NORMALREADBYTES = 0, + NFSIOS_NORMALWRITTENBYTES, + NFSIOS_DIRECTREADBYTES, + NFSIOS_DIRECTWRITTENBYTES, + NFSIOS_SERVERREADBYTES, + NFSIOS_SERVERWRITTENBYTES, + NFSIOS_READPAGES, + NFSIOS_WRITEPAGES, + __NFSIOS_BYTESMAX, +}; + +/* + * NFS event counters + * + * These counters provide a low-overhead way of monitoring client + * activity without enabling NFS trace debugging. The counters + * show the rate at which VFS requests are made, and how often the + * client invalidates its data and attribute caches. This allows + * system administrators to monitor such things as how close-to-open + * is working, and answer questions such as "why are there so many + * GETATTR requests on the wire?" + * + * They also count anamolous events such as short reads and writes, + * silly renames due to close-after-delete, and operations that + * change the size of a file (such operations can often be the + * source of data corruption if applications aren't using file + * locking properly). + */ +enum nfs_stat_eventcounters { + NFSIOS_INODEREVALIDATE = 0, + NFSIOS_DENTRYREVALIDATE, + NFSIOS_DATAINVALIDATE, + NFSIOS_ATTRINVALIDATE, + NFSIOS_VFSOPEN, + NFSIOS_VFSLOOKUP, + NFSIOS_VFSACCESS, + NFSIOS_VFSUPDATEPAGE, + NFSIOS_VFSREADPAGE, + NFSIOS_VFSREADPAGES, + NFSIOS_VFSWRITEPAGE, + NFSIOS_VFSWRITEPAGES, + NFSIOS_VFSGETDENTS, + NFSIOS_VFSSETATTR, + NFSIOS_VFSFLUSH, + NFSIOS_VFSFSYNC, + NFSIOS_VFSLOCK, + NFSIOS_VFSRELEASE, + NFSIOS_CONGESTIONWAIT, + NFSIOS_SETATTRTRUNC, + NFSIOS_EXTENDWRITE, + NFSIOS_SILLYRENAME, + NFSIOS_SHORTREAD, + NFSIOS_SHORTWRITE, + NFSIOS_DELAY, + __NFSIOS_COUNTSMAX, +}; + +#endif /* _LINUX_NFS_IOSTAT */ -- cgit v1.2.3 From e468bae97d243fe0e1515abaa1f7d0edf1476ad0 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 13 Jun 2008 13:25:22 -0400 Subject: NFS: Allow redirtying of a completed unstable write. Currently, if an unstable write completes, we cannot redirty the page in order to reflect a new change in the page data until after we've sent a COMMIT request. This patch allows a page rewrite to proceed without the unnecessary COMMIT step, putting it immediately back onto the dirty page list, undoing the VM unstable write accounting, and removing the NFS_PAGE_TAG_COMMIT tag from the NFS radix tree. Signed-off-by: Trond Myklebust --- fs/nfs/write.c | 65 ++++++++++++++++++++++++------------------------ include/linux/nfs_page.h | 9 ++++--- 2 files changed, 38 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 04f51e52e184..feca8c648766 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -242,12 +242,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, return ret; spin_lock(&inode->i_lock); } - if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { - /* This request is marked for commit */ + if (test_bit(PG_CLEAN, &req->wb_flags)) { spin_unlock(&inode->i_lock); - nfs_clear_page_tag_locked(req); - nfs_pageio_complete(pgio); - return 0; + BUG(); } if (nfs_set_page_writeback(page) != 0) { spin_unlock(&inode->i_lock); @@ -391,19 +388,6 @@ nfs_mark_request_dirty(struct nfs_page *req) __set_page_dirty_nobuffers(req->wb_page); } -/* - * Check if a request is dirty - */ -static inline int -nfs_dirty_request(struct nfs_page *req) -{ - struct page *page = req->wb_page; - - if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags)) - return 0; - return !PageWriteback(page); -} - #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) /* * Add a request to the inode's commit list. @@ -416,7 +400,7 @@ nfs_mark_request_commit(struct nfs_page *req) spin_lock(&inode->i_lock); nfsi->ncommit++; - set_bit(PG_NEED_COMMIT, &(req)->wb_flags); + set_bit(PG_CLEAN, &(req)->wb_flags); radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_COMMIT); @@ -426,6 +410,19 @@ nfs_mark_request_commit(struct nfs_page *req) __mark_inode_dirty(inode, I_DIRTY_DATASYNC); } +static int +nfs_clear_request_commit(struct nfs_page *req) +{ + struct page *page = req->wb_page; + + if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { + dec_zone_page_state(page, NR_UNSTABLE_NFS); + dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); + return 1; + } + return 0; +} + static inline int nfs_write_need_commit(struct nfs_write_data *data) { @@ -435,7 +432,7 @@ int nfs_write_need_commit(struct nfs_write_data *data) static inline int nfs_reschedule_unstable_write(struct nfs_page *req) { - if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { + if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { nfs_mark_request_commit(req); return 1; } @@ -451,6 +448,12 @@ nfs_mark_request_commit(struct nfs_page *req) { } +static inline int +nfs_clear_request_commit(struct nfs_page *req) +{ + return 0; +} + static inline int nfs_write_need_commit(struct nfs_write_data *data) { @@ -508,11 +511,8 @@ static void nfs_cancel_commit_list(struct list_head *head) while(!list_empty(head)) { req = nfs_list_entry(head->next); - dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); - dec_bdi_stat(req->wb_page->mapping->backing_dev_info, - BDI_RECLAIMABLE); nfs_list_remove_request(req); - clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); + nfs_clear_request_commit(req); nfs_inode_remove_request(req); nfs_unlock_request(req); } @@ -584,8 +584,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, * Note: nfs_flush_incompatible() will already * have flushed out requests having wrong owners. */ - if (!nfs_dirty_request(req) - || offset > rqend + if (offset > rqend || end < req->wb_offset) goto out_flushme; @@ -601,6 +600,10 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, spin_lock(&inode->i_lock); } + if (nfs_clear_request_commit(req)) + radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree, + req->wb_index, NFS_PAGE_TAG_COMMIT); + /* Okay, the request matches. Update the region */ if (offset < req->wb_offset) { req->wb_offset = offset; @@ -682,8 +685,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page) req = nfs_page_find_request(page); if (req == NULL) return 0; - do_flush = req->wb_page != page || req->wb_context != ctx - || !nfs_dirty_request(req); + do_flush = req->wb_page != page || req->wb_context != ctx; nfs_release_request(req); if (!do_flush) return 0; @@ -1288,10 +1290,7 @@ static void nfs_commit_release(void *calldata) while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); - clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); - dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); - dec_bdi_stat(req->wb_page->mapping->backing_dev_info, - BDI_RECLAIMABLE); + nfs_clear_request_commit(req); dprintk("NFS: commit (%s/%lld %d@%lld)", req->wb_context->path.dentry->d_inode->i_sb->s_id, @@ -1467,7 +1466,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page) req = nfs_page_find_request(page); if (req == NULL) goto out; - if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { + if (test_bit(PG_CLEAN, &req->wb_flags)) { nfs_release_request(req); break; } diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index a1676e19e491..3c60685d972b 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -27,9 +27,12 @@ /* * Valid flags for a dirty buffer */ -#define PG_BUSY 0 -#define PG_NEED_COMMIT 1 -#define PG_NEED_RESCHED 2 +enum { + PG_BUSY = 0, + PG_CLEAN, + PG_NEED_COMMIT, + PG_NEED_RESCHED, +}; struct nfs_inode; struct nfs_page { -- cgit v1.2.3 From ce3b7e1906ebbe96753fe090b36de6ffb8e0e0e7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 23 Jun 2008 12:36:53 -0400 Subject: NFS: Add string length argument to nfs_parse_server_address To make nfs_parse_server_address() more generally useful, allow it to accept input strings that are not terminated with '\0'. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 100 +++++++++++++++++++++++++++++++++++---------------- include/linux/inet.h | 7 ++++ 2 files changed, 77 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index ea4abd266a7a..d27aa1db0074 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -705,38 +705,76 @@ static int nfs_verify_server_address(struct sockaddr *addr) return 0; } -/* - * Parse string addresses passed in via a mount option, - * and construct a sockaddr based on the result. - * - * If address parsing fails, set the sockaddr's address - * family to AF_UNSPEC to force nfs_verify_server_address() - * to punt the mount. - */ -static void nfs_parse_server_address(char *value, - struct sockaddr *sap, - size_t *len) +static void nfs_parse_ipv4_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) { - if (strchr(value, ':')) { - struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap; - u8 *addr = (u8 *)&ap->sin6_addr.in6_u; + struct sockaddr_in *sin = (struct sockaddr_in *)sap; + u8 *addr = (u8 *)&sin->sin_addr.s_addr; - ap->sin6_family = AF_INET6; - *len = sizeof(*ap); - if (in6_pton(value, -1, addr, '\0', NULL)) + if (str_len <= INET_ADDRSTRLEN) { + dfprintk(MOUNT, "NFS: parsing IPv4 address %*s\n", + (int)str_len, string); + + sin->sin_family = AF_INET; + *addr_len = sizeof(*sin); + if (in4_pton(string, str_len, addr, '\0', NULL)) return; - } else { - struct sockaddr_in *ap = (struct sockaddr_in *)sap; - u8 *addr = (u8 *)&ap->sin_addr.s_addr; + } + + sap->sa_family = AF_UNSPEC; + *addr_len = 0; +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static void nfs_parse_ipv6_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; + + if (str_len <= INET6_ADDRSTRLEN) { + dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n", + (int)str_len, string); - ap->sin_family = AF_INET; - *len = sizeof(*ap); - if (in4_pton(value, -1, addr, '\0', NULL)) + sin6->sin6_family = AF_INET6; + *addr_len = sizeof(*sin6); + if (in6_pton(string, str_len, addr, '\0', NULL)) return; } sap->sa_family = AF_UNSPEC; - *len = 0; + *addr_len = 0; +} +#else +static void nfs_parse_ipv6_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) +{ + sap->sa_family = AF_UNSPEC; + *addr_len = 0; +} +#endif + +/* + * Construct a sockaddr based on the contents of a string that contains + * an IP address in presentation format. + * + * If there is a problem constructing the new sockaddr, set the address + * family to AF_UNSPEC. + */ +static void nfs_parse_ip_address(char *string, size_t str_len, + struct sockaddr *sap, size_t *addr_len) +{ + unsigned int i, colons; + + colons = 0; + for (i = 0; i < str_len; i++) + if (string[i] == ':') + colons++; + + if (colons >= 2) + nfs_parse_ipv6_address(string, str_len, sap, addr_len); + else + nfs_parse_ipv4_address(string, str_len, sap, addr_len); } /* @@ -1070,9 +1108,10 @@ static int nfs_parse_mount_options(char *raw, string = match_strdup(args); if (string == NULL) goto out_nomem; - nfs_parse_server_address(string, (struct sockaddr *) - &mnt->nfs_server.address, - &mnt->nfs_server.addrlen); + nfs_parse_ip_address(string, strlen(string), + (struct sockaddr *) + &mnt->nfs_server.address, + &mnt->nfs_server.addrlen); kfree(string); break; case Opt_clientaddr: @@ -1093,9 +1132,10 @@ static int nfs_parse_mount_options(char *raw, string = match_strdup(args); if (string == NULL) goto out_nomem; - nfs_parse_server_address(string, (struct sockaddr *) - &mnt->mount_server.address, - &mnt->mount_server.addrlen); + nfs_parse_ip_address(string, strlen(string), + (struct sockaddr *) + &mnt->mount_server.address, + &mnt->mount_server.addrlen); kfree(string); break; diff --git a/include/linux/inet.h b/include/linux/inet.h index 1354080cf8cf..4cca05c9678e 100644 --- a/include/linux/inet.h +++ b/include/linux/inet.h @@ -44,6 +44,13 @@ #include +/* + * These mimic similar macros defined in user-space for inet_ntop(3). + * See /usr/include/netinet/in.h . + */ +#define INET_ADDRSTRLEN (16) +#define INET6_ADDRSTRLEN (48) + extern __be32 in_aton(const char *str); extern int in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end); extern int in6_pton(const char *src, int srclen, u8 *dst, int delim, const char **end); -- cgit v1.2.3 From 259875efed06d6936f54c9a264e868937f1bc217 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 2 Jul 2008 14:43:47 -0400 Subject: NFS: set transport defaults after mount option parsing is finished Move the UDP/TCP default timeo/retrans settings for text mounts to nfs_init_timeout_values(), which was were they were always being initialised (and sanity checked) for binary mounts. Document the default timeout values using appropriate #defines. Ensure that we initialise and sanity check the transport protocols that may have been specified by the user. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 13 ++++++----- fs/nfs/super.c | 60 +++++++++++++++++++++++++++++++++++--------------- include/linux/nfs_fs.h | 5 +++++ 3 files changed, 55 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index f2a092ca69b5..5ee23e7058b3 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -431,14 +431,14 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, { to->to_initval = timeo * HZ / 10; to->to_retries = retrans; - if (!to->to_retries) - to->to_retries = 2; switch (proto) { case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_RDMA: + if (to->to_retries == 0) + to->to_retries = NFS_DEF_TCP_RETRANS; if (to->to_initval == 0) - to->to_initval = 60 * HZ; + to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; if (to->to_initval > NFS_MAX_TCP_TIMEOUT) to->to_initval = NFS_MAX_TCP_TIMEOUT; to->to_increment = to->to_initval; @@ -450,14 +450,17 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, to->to_exponential = 0; break; case XPRT_TRANSPORT_UDP: - default: + if (to->to_retries == 0) + to->to_retries = NFS_DEF_UDP_RETRANS; if (!to->to_initval) - to->to_initval = 11 * HZ / 10; + to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; if (to->to_initval > NFS_MAX_UDP_TIMEOUT) to->to_initval = NFS_MAX_UDP_TIMEOUT; to->to_maxval = NFS_MAX_UDP_TIMEOUT; to->to_exponential = 1; break; + default: + BUG(); } } diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 73a8e5970f02..9c1a960f5b94 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -816,6 +816,43 @@ static void nfs_parse_ip_address(char *string, size_t str_len, nfs_parse_ipv4_address(string, str_len, sap, addr_len); } +/* + * Sanity check the NFS transport protocol. + * + */ +static void nfs_validate_transport_protocol(struct nfs_parsed_mount_data *mnt) +{ + switch (mnt->nfs_server.protocol) { + case XPRT_TRANSPORT_UDP: + case XPRT_TRANSPORT_TCP: + case XPRT_TRANSPORT_RDMA: + break; + default: + mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; + } +} + +/* + * For text based NFSv2/v3 mounts, the mount protocol transport default + * settings should depend upon the specified NFS transport. + */ +static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt) +{ + nfs_validate_transport_protocol(mnt); + + if (mnt->mount_server.protocol == XPRT_TRANSPORT_UDP || + mnt->mount_server.protocol == XPRT_TRANSPORT_TCP) + return; + switch (mnt->nfs_server.protocol) { + case XPRT_TRANSPORT_UDP: + mnt->mount_server.protocol = XPRT_TRANSPORT_UDP; + break; + case XPRT_TRANSPORT_TCP: + case XPRT_TRANSPORT_RDMA: + mnt->mount_server.protocol = XPRT_TRANSPORT_TCP; + } +} + /* * Error-check and convert a string of mount options from user space into * a data structure @@ -896,20 +933,14 @@ static int nfs_parse_mount_options(char *raw, case Opt_udp: mnt->flags &= ~NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; - mnt->timeo = 7; - mnt->retrans = 5; break; case Opt_tcp: mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; - mnt->timeo = 600; - mnt->retrans = 2; break; case Opt_rdma: mnt->flags |= NFS_MOUNT_TCP; /* for side protocols */ mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; - mnt->timeo = 600; - mnt->retrans = 2; break; case Opt_acl: mnt->flags &= ~NFS_MOUNT_NOACL; @@ -1103,21 +1134,15 @@ static int nfs_parse_mount_options(char *raw, case Opt_xprt_udp: mnt->flags &= ~NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; - mnt->timeo = 7; - mnt->retrans = 5; break; case Opt_xprt_tcp: mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; - mnt->timeo = 600; - mnt->retrans = 2; break; case Opt_xprt_rdma: /* vector side protocols to TCP */ mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; - mnt->timeo = 600; - mnt->retrans = 2; break; default: goto out_unrec_xprt; @@ -1438,14 +1463,11 @@ static int nfs_validate_mount_data(void *options, args->flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP); args->rsize = NFS_MAX_FILE_IO_SIZE; args->wsize = NFS_MAX_FILE_IO_SIZE; - args->timeo = 600; - args->retrans = 2; args->acregmin = 3; args->acregmax = 60; args->acdirmin = 30; args->acdirmax = 60; args->mount_server.port = 0; /* autobind unless user sets port */ - args->mount_server.protocol = XPRT_TRANSPORT_UDP; args->nfs_server.port = 0; /* autobind unless user sets port */ args->nfs_server.protocol = XPRT_TRANSPORT_TCP; @@ -1546,6 +1568,8 @@ static int nfs_validate_mount_data(void *options, &args->nfs_server.address)) goto out_no_address; + nfs_set_mount_transport_protocol(args); + status = nfs_parse_devname(dev_name, &args->nfs_server.hostname, PAGE_SIZE, @@ -2095,14 +2119,11 @@ static int nfs4_validate_mount_data(void *options, args->rsize = NFS_MAX_FILE_IO_SIZE; args->wsize = NFS_MAX_FILE_IO_SIZE; - args->timeo = 600; - args->retrans = 2; args->acregmin = 3; args->acregmax = 60; args->acdirmin = 30; args->acdirmax = 60; args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */ - args->nfs_server.protocol = XPRT_TRANSPORT_TCP; switch (data->version) { case 1: @@ -2163,6 +2184,7 @@ static int nfs4_validate_mount_data(void *options, args->acdirmin = data->acdirmin; args->acdirmax = data->acdirmax; args->nfs_server.protocol = data->proto; + nfs_validate_transport_protocol(args); break; default: { @@ -2175,6 +2197,8 @@ static int nfs4_validate_mount_data(void *options, &args->nfs_server.address)) return -EINVAL; + nfs_validate_transport_protocol(args); + switch (args->auth_flavor_len) { case 0: args->auth_flavors[0] = RPC_AUTH_UNIX; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 27d6a8d98cef..830d9cc8cdce 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -12,6 +12,11 @@ #include /* Default timeout values */ +#define NFS_DEF_UDP_TIMEO (11) +#define NFS_DEF_UDP_RETRANS (3) +#define NFS_DEF_TCP_TIMEO (600) +#define NFS_DEF_TCP_RETRANS (2) + #define NFS_MAX_UDP_TIMEOUT (60*HZ) #define NFS_MAX_TCP_TIMEOUT (600*HZ) -- cgit v1.2.3 From 0e0cab744b17a70ef0f08d818d66935feade7cad Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 26 Jun 2008 17:47:12 -0400 Subject: NFS: use documenting macro constants for initializing ac{reg, dir}{min, max} Clean up. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfsroot.c | 8 ++++---- fs/nfs/super.c | 24 ++++++++++++------------ include/linux/nfs_fs.h | 5 +++++ 3 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 15afe3a6c5be..46763d1cd397 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -295,10 +295,10 @@ static int __init root_nfs_name(char *name) nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ nfs_data.rsize = NFS_DEF_FILE_IO_SIZE; nfs_data.wsize = NFS_DEF_FILE_IO_SIZE; - nfs_data.acregmin = 3; - nfs_data.acregmax = 60; - nfs_data.acdirmin = 30; - nfs_data.acdirmax = 60; + nfs_data.acregmin = NFS_DEF_ACREGMIN; + nfs_data.acregmax = NFS_DEF_ACREGMAX; + nfs_data.acdirmin = NFS_DEF_ACDIRMIN; + nfs_data.acdirmax = NFS_DEF_ACDIRMAX; strcpy(buf, NFS_ROOT); /* Process options received from the remote server */ diff --git a/fs/nfs/super.c b/fs/nfs/super.c index de424d2f3155..ebed63e0ff8e 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -518,13 +518,13 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, if (nfss->bsize != 0) seq_printf(m, ",bsize=%u", nfss->bsize); seq_printf(m, ",namlen=%u", nfss->namelen); - if (nfss->acregmin != 3*HZ || showdefaults) + if (nfss->acregmin != NFS_DEF_ACREGMIN*HZ || showdefaults) seq_printf(m, ",acregmin=%u", nfss->acregmin/HZ); - if (nfss->acregmax != 60*HZ || showdefaults) + if (nfss->acregmax != NFS_DEF_ACREGMAX*HZ || showdefaults) seq_printf(m, ",acregmax=%u", nfss->acregmax/HZ); - if (nfss->acdirmin != 30*HZ || showdefaults) + if (nfss->acdirmin != NFS_DEF_ACDIRMIN*HZ || showdefaults) seq_printf(m, ",acdirmin=%u", nfss->acdirmin/HZ); - if (nfss->acdirmax != 60*HZ || showdefaults) + if (nfss->acdirmax != NFS_DEF_ACDIRMAX*HZ || showdefaults) seq_printf(m, ",acdirmax=%u", nfss->acdirmax/HZ); for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) { if (nfss->flags & nfs_infop->flag) @@ -1460,10 +1460,10 @@ static int nfs_validate_mount_data(void *options, args->flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP); args->rsize = NFS_MAX_FILE_IO_SIZE; args->wsize = NFS_MAX_FILE_IO_SIZE; - args->acregmin = 3; - args->acregmax = 60; - args->acdirmin = 30; - args->acdirmax = 60; + args->acregmin = NFS_DEF_ACREGMIN; + args->acregmax = NFS_DEF_ACREGMAX; + args->acdirmin = NFS_DEF_ACDIRMIN; + args->acdirmax = NFS_DEF_ACDIRMAX; args->mount_server.port = 0; /* autobind unless user sets port */ args->nfs_server.port = 0; /* autobind unless user sets port */ args->nfs_server.protocol = XPRT_TRANSPORT_TCP; @@ -2119,10 +2119,10 @@ static int nfs4_validate_mount_data(void *options, args->rsize = NFS_MAX_FILE_IO_SIZE; args->wsize = NFS_MAX_FILE_IO_SIZE; - args->acregmin = 3; - args->acregmax = 60; - args->acdirmin = 30; - args->acdirmax = 60; + args->acregmin = NFS_DEF_ACREGMIN; + args->acregmax = NFS_DEF_ACREGMAX; + args->acdirmin = NFS_DEF_ACDIRMIN; + args->acdirmax = NFS_DEF_ACDIRMAX; args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */ switch (data->version) { diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 830d9cc8cdce..29d261918734 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -20,6 +20,11 @@ #define NFS_MAX_UDP_TIMEOUT (60*HZ) #define NFS_MAX_TCP_TIMEOUT (600*HZ) +#define NFS_DEF_ACREGMIN (3) +#define NFS_DEF_ACREGMAX (60) +#define NFS_DEF_ACDIRMIN (30) +#define NFS_DEF_ACDIRMAX (60) + /* * When flushing a cluster of dirty pages, there can be different * strategies: -- cgit v1.2.3 From 004a403c2e954734090a69aedc7f4f822bdcc142 Mon Sep 17 00:00:00 2001 From: Loc Ho Date: Wed, 14 May 2008 20:41:47 +0800 Subject: [CRYPTO] hash: Add asynchronous hash support This patch adds asynchronous hash and digest support. Signed-off-by: Loc Ho Signed-off-by: Herbert Xu --- crypto/Makefile | 1 + crypto/ahash.c | 106 +++++++++++++++++++++++++++ crypto/api.c | 8 ++- crypto/digest.c | 81 +++++++++++++++++++++ crypto/hash.c | 102 +++++++++++++++++++++++--- crypto/internal.h | 1 + include/crypto/algapi.h | 36 ++++++++++ include/linux/crypto.h | 187 ++++++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 507 insertions(+), 15 deletions(-) create mode 100644 crypto/ahash.c (limited to 'include/linux') diff --git a/crypto/Makefile b/crypto/Makefile index 807656b64e02..d4f3ed857df0 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_CRYPTO_BLKCIPHER) += crypto_blkcipher.o obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o crypto_hash-objs := hash.o +crypto_hash-objs += ahash.o obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o diff --git a/crypto/ahash.c b/crypto/ahash.c new file mode 100644 index 000000000000..a83e035d9a3f --- /dev/null +++ b/crypto/ahash.c @@ -0,0 +1,106 @@ +/* + * Asynchronous Cryptographic Hash operations. + * + * This is the asynchronous version of hash.c with notification of + * completion via a callback. + * + * Copyright (c) 2008 Loc Ho + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen) +{ + struct ahash_alg *ahash = crypto_ahash_alg(tfm); + unsigned long alignmask = crypto_ahash_alignmask(tfm); + int ret; + u8 *buffer, *alignbuffer; + unsigned long absize; + + absize = keylen + alignmask; + buffer = kmalloc(absize, GFP_ATOMIC); + if (!buffer) + return -ENOMEM; + + alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); + memcpy(alignbuffer, key, keylen); + ret = ahash->setkey(tfm, alignbuffer, keylen); + memset(alignbuffer, 0, keylen); + kfree(buffer); + return ret; +} + +static int ahash_setkey(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen) +{ + struct ahash_alg *ahash = crypto_ahash_alg(tfm); + unsigned long alignmask = crypto_ahash_alignmask(tfm); + + if ((unsigned long)key & alignmask) + return ahash_setkey_unaligned(tfm, key, keylen); + + return ahash->setkey(tfm, key, keylen); +} + +static unsigned int crypto_ahash_ctxsize(struct crypto_alg *alg, u32 type, + u32 mask) +{ + return alg->cra_ctxsize; +} + +static int crypto_init_ahash_ops(struct crypto_tfm *tfm, u32 type, u32 mask) +{ + struct ahash_alg *alg = &tfm->__crt_alg->cra_ahash; + struct ahash_tfm *crt = &tfm->crt_ahash; + + if (alg->digestsize > crypto_tfm_alg_blocksize(tfm)) + return -EINVAL; + + crt->init = alg->init; + crt->update = alg->update; + crt->final = alg->final; + crt->digest = alg->digest; + crt->setkey = ahash_setkey; + crt->base = __crypto_ahash_cast(tfm); + crt->digestsize = alg->digestsize; + + return 0; +} + +static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg) + __attribute__ ((unused)); +static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg) +{ + seq_printf(m, "type : ahash\n"); + seq_printf(m, "async : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ? + "yes" : "no"); + seq_printf(m, "blocksize : %u\n", alg->cra_blocksize); + seq_printf(m, "digestsize : %u\n", alg->cra_hash.digestsize); +} + +const struct crypto_type crypto_ahash_type = { + .ctxsize = crypto_ahash_ctxsize, + .init = crypto_init_ahash_ops, +#ifdef CONFIG_PROC_FS + .show = crypto_ahash_show, +#endif +}; +EXPORT_SYMBOL_GPL(crypto_ahash_type); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Asynchronous cryptographic hash type"); diff --git a/crypto/api.c b/crypto/api.c index 0a0f41ef255f..d06e33270abe 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -235,8 +235,12 @@ static int crypto_init_ops(struct crypto_tfm *tfm, u32 type, u32 mask) return crypto_init_cipher_ops(tfm); case CRYPTO_ALG_TYPE_DIGEST: - return crypto_init_digest_ops(tfm); - + if ((mask & CRYPTO_ALG_TYPE_HASH_MASK) != + CRYPTO_ALG_TYPE_HASH_MASK) + return crypto_init_digest_ops_async(tfm); + else + return crypto_init_digest_ops(tfm); + case CRYPTO_ALG_TYPE_COMPRESS: return crypto_init_compress_ops(tfm); diff --git a/crypto/digest.c b/crypto/digest.c index b526cc348b79..025c9aea24ed 100644 --- a/crypto/digest.c +++ b/crypto/digest.c @@ -157,3 +157,84 @@ int crypto_init_digest_ops(struct crypto_tfm *tfm) void crypto_exit_digest_ops(struct crypto_tfm *tfm) { } + +static int digest_async_nosetkey(struct crypto_ahash *tfm_async, const u8 *key, + unsigned int keylen) +{ + crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK); + return -ENOSYS; +} + +static int digest_async_setkey(struct crypto_ahash *tfm_async, const u8 *key, + unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_ahash_tfm(tfm_async); + struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; + + crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK); + return dalg->dia_setkey(tfm, key, keylen); +} + +static int digest_async_init(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; + + dalg->dia_init(tfm); + return 0; +} + +static int digest_async_update(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + update(&desc, req->src, req->nbytes); + return 0; +} + +static int digest_async_final(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + final(&desc, req->result); + return 0; +} + +static int digest_async_digest(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + return digest(&desc, req->src, req->nbytes, req->result); +} + +int crypto_init_digest_ops_async(struct crypto_tfm *tfm) +{ + struct ahash_tfm *crt = &tfm->crt_ahash; + struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; + + if (dalg->dia_digestsize > crypto_tfm_alg_blocksize(tfm)) + return -EINVAL; + + crt->init = digest_async_init; + crt->update = digest_async_update; + crt->final = digest_async_final; + crt->digest = digest_async_digest; + crt->setkey = dalg->dia_setkey ? digest_async_setkey : + digest_async_nosetkey; + crt->digestsize = dalg->dia_digestsize; + crt->base = __crypto_ahash_cast(tfm); + + return 0; +} diff --git a/crypto/hash.c b/crypto/hash.c index 7dcff671c19b..f9400a014e74 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -59,24 +59,108 @@ static int hash_setkey(struct crypto_hash *crt, const u8 *key, return alg->setkey(crt, key, keylen); } -static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask) +static int hash_async_setkey(struct crypto_ahash *tfm_async, const u8 *key, + unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_ahash_tfm(tfm_async); + struct crypto_hash *tfm_hash = __crypto_hash_cast(tfm); + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + + return alg->setkey(tfm_hash, key, keylen); +} + +static int hash_async_init(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + return alg->init(&desc); +} + +static int hash_async_update(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + return alg->update(&desc, req->src, req->nbytes); +} + +static int hash_async_final(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + return alg->final(&desc, req->result); +} + +static int hash_async_digest(struct ahash_request *req) +{ + struct crypto_tfm *tfm = req->base.tfm; + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + struct hash_desc desc = { + .tfm = __crypto_hash_cast(tfm), + .flags = req->base.flags, + }; + + return alg->digest(&desc, req->src, req->nbytes, req->result); +} + +static int crypto_init_hash_ops_async(struct crypto_tfm *tfm) +{ + struct ahash_tfm *crt = &tfm->crt_ahash; + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + + crt->init = hash_async_init; + crt->update = hash_async_update; + crt->final = hash_async_final; + crt->digest = hash_async_digest; + crt->setkey = hash_async_setkey; + crt->digestsize = alg->digestsize; + crt->base = __crypto_ahash_cast(tfm); + + return 0; +} + +static int crypto_init_hash_ops_sync(struct crypto_tfm *tfm) { struct hash_tfm *crt = &tfm->crt_hash; struct hash_alg *alg = &tfm->__crt_alg->cra_hash; - if (alg->digestsize > crypto_tfm_alg_blocksize(tfm)) - return -EINVAL; - - crt->init = alg->init; - crt->update = alg->update; - crt->final = alg->final; - crt->digest = alg->digest; - crt->setkey = hash_setkey; + crt->init = alg->init; + crt->update = alg->update; + crt->final = alg->final; + crt->digest = alg->digest; + crt->setkey = hash_setkey; crt->digestsize = alg->digestsize; return 0; } +static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask) +{ + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + + if (alg->digestsize > crypto_tfm_alg_blocksize(tfm)) + return -EINVAL; + + if ((mask & CRYPTO_ALG_TYPE_HASH_MASK) != CRYPTO_ALG_TYPE_HASH_MASK) + return crypto_init_hash_ops_async(tfm); + else + return crypto_init_hash_ops_sync(tfm); +} + static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg) __attribute__ ((unused)); static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg) diff --git a/crypto/internal.h b/crypto/internal.h index 32f4c2145603..683fcb2d91f4 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -86,6 +86,7 @@ struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type, u32 mask); struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask); int crypto_init_digest_ops(struct crypto_tfm *tfm); +int crypto_init_digest_ops_async(struct crypto_tfm *tfm); int crypto_init_cipher_ops(struct crypto_tfm *tfm); int crypto_init_compress_ops(struct crypto_tfm *tfm); diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index 60d06e784be3..fef272a8ceeb 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -98,6 +98,7 @@ extern const struct crypto_type crypto_ablkcipher_type; extern const struct crypto_type crypto_aead_type; extern const struct crypto_type crypto_blkcipher_type; extern const struct crypto_type crypto_hash_type; +extern const struct crypto_type crypto_ahash_type; void crypto_mod_put(struct crypto_alg *alg); @@ -314,5 +315,40 @@ static inline int crypto_requires_sync(u32 type, u32 mask) return (type ^ CRYPTO_ALG_ASYNC) & mask & CRYPTO_ALG_ASYNC; } +static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm) +{ + return crypto_tfm_ctx(&tfm->base); +} + +static inline struct ahash_alg *crypto_ahash_alg( + struct crypto_ahash *tfm) +{ + return &crypto_ahash_tfm(tfm)->__crt_alg->cra_ahash; +} + +static inline int ahash_enqueue_request(struct crypto_queue *queue, + struct ahash_request *request) +{ + return crypto_enqueue_request(queue, &request->base); +} + +static inline struct ahash_request *ahash_dequeue_request( + struct crypto_queue *queue) +{ + return ahash_request_cast(crypto_dequeue_request(queue)); +} + +static inline void *ahash_request_ctx(struct ahash_request *req) +{ + return req->__ctx; +} + +static inline int ahash_tfm_in_queue(struct crypto_queue *queue, + struct crypto_ahash *tfm) +{ + return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm)); +} + + #endif /* _CRYPTO_ALGAPI_H */ diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 425824bd49f3..b6efe569128d 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -30,15 +30,17 @@ */ #define CRYPTO_ALG_TYPE_MASK 0x0000000f #define CRYPTO_ALG_TYPE_CIPHER 0x00000001 -#define CRYPTO_ALG_TYPE_DIGEST 0x00000002 -#define CRYPTO_ALG_TYPE_HASH 0x00000003 +#define CRYPTO_ALG_TYPE_COMPRESS 0x00000002 +#define CRYPTO_ALG_TYPE_AEAD 0x00000003 #define CRYPTO_ALG_TYPE_BLKCIPHER 0x00000004 #define CRYPTO_ALG_TYPE_ABLKCIPHER 0x00000005 #define CRYPTO_ALG_TYPE_GIVCIPHER 0x00000006 -#define CRYPTO_ALG_TYPE_COMPRESS 0x00000008 -#define CRYPTO_ALG_TYPE_AEAD 0x00000009 +#define CRYPTO_ALG_TYPE_DIGEST 0x00000008 +#define CRYPTO_ALG_TYPE_HASH 0x00000009 +#define CRYPTO_ALG_TYPE_AHASH 0x0000000a #define CRYPTO_ALG_TYPE_HASH_MASK 0x0000000e +#define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000c #define CRYPTO_ALG_TYPE_BLKCIPHER_MASK 0x0000000c #define CRYPTO_ALG_LARVAL 0x00000010 @@ -102,6 +104,7 @@ struct crypto_async_request; struct crypto_aead; struct crypto_blkcipher; struct crypto_hash; +struct crypto_ahash; struct crypto_tfm; struct crypto_type; struct aead_givcrypt_request; @@ -131,6 +134,18 @@ struct ablkcipher_request { void *__ctx[] CRYPTO_MINALIGN_ATTR; }; +struct ahash_request { + struct crypto_async_request base; + + void *info; + + unsigned int nbytes; + struct scatterlist *src; + u8 *result; + + void *__ctx[] CRYPTO_MINALIGN_ATTR; +}; + /** * struct aead_request - AEAD request * @base: Common attributes for async crypto requests @@ -195,6 +210,17 @@ struct ablkcipher_alg { unsigned int ivsize; }; +struct ahash_alg { + int (*init)(struct ahash_request *req); + int (*update)(struct ahash_request *req); + int (*final)(struct ahash_request *req); + int (*digest)(struct ahash_request *req); + int (*setkey)(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen); + + unsigned int digestsize; +}; + struct aead_alg { int (*setkey)(struct crypto_aead *tfm, const u8 *key, unsigned int keylen); @@ -272,6 +298,7 @@ struct compress_alg { #define cra_cipher cra_u.cipher #define cra_digest cra_u.digest #define cra_hash cra_u.hash +#define cra_ahash cra_u.ahash #define cra_compress cra_u.compress struct crypto_alg { @@ -298,6 +325,7 @@ struct crypto_alg { struct cipher_alg cipher; struct digest_alg digest; struct hash_alg hash; + struct ahash_alg ahash; struct compress_alg compress; } cra_u; @@ -383,6 +411,19 @@ struct hash_tfm { unsigned int digestsize; }; +struct ahash_tfm { + int (*init)(struct ahash_request *req); + int (*update)(struct ahash_request *req); + int (*final)(struct ahash_request *req); + int (*digest)(struct ahash_request *req); + int (*setkey)(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen); + + unsigned int digestsize; + struct crypto_ahash *base; + unsigned int reqsize; +}; + struct compress_tfm { int (*cot_compress)(struct crypto_tfm *tfm, const u8 *src, unsigned int slen, @@ -397,6 +438,7 @@ struct compress_tfm { #define crt_blkcipher crt_u.blkcipher #define crt_cipher crt_u.cipher #define crt_hash crt_u.hash +#define crt_ahash crt_u.ahash #define crt_compress crt_u.compress struct crypto_tfm { @@ -409,6 +451,7 @@ struct crypto_tfm { struct blkcipher_tfm blkcipher; struct cipher_tfm cipher; struct hash_tfm hash; + struct ahash_tfm ahash; struct compress_tfm compress; } crt_u; @@ -441,6 +484,10 @@ struct crypto_hash { struct crypto_tfm base; }; +struct crypto_ahash { + struct crypto_tfm base; +}; + enum { CRYPTOA_UNSPEC, CRYPTOA_ALG, @@ -1264,5 +1311,137 @@ static inline int crypto_comp_decompress(struct crypto_comp *tfm, src, slen, dst, dlen); } +static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm) +{ + return (struct crypto_ahash *)tfm; +} + +static inline struct crypto_ahash *crypto_alloc_ahash(const char *alg_name, + u32 type, u32 mask) +{ + type &= ~CRYPTO_ALG_TYPE_MASK; + mask &= ~CRYPTO_ALG_TYPE_MASK; + type |= CRYPTO_ALG_TYPE_AHASH; + mask |= CRYPTO_ALG_TYPE_AHASH_MASK; + + return __crypto_ahash_cast(crypto_alloc_base(alg_name, type, mask)); +} + +static inline struct crypto_tfm *crypto_ahash_tfm(struct crypto_ahash *tfm) +{ + return &tfm->base; +} + +static inline void crypto_free_ahash(struct crypto_ahash *tfm) +{ + crypto_free_tfm(crypto_ahash_tfm(tfm)); +} + +static inline unsigned int crypto_ahash_alignmask( + struct crypto_ahash *tfm) +{ + return crypto_tfm_alg_alignmask(crypto_ahash_tfm(tfm)); +} + +static inline struct ahash_tfm *crypto_ahash_crt(struct crypto_ahash *tfm) +{ + return &crypto_ahash_tfm(tfm)->crt_ahash; +} + +static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash *tfm) +{ + return crypto_ahash_crt(tfm)->digestsize; +} + +static inline u32 crypto_ahash_get_flags(struct crypto_ahash *tfm) +{ + return crypto_tfm_get_flags(crypto_ahash_tfm(tfm)); +} + +static inline void crypto_ahash_set_flags(struct crypto_ahash *tfm, u32 flags) +{ + crypto_tfm_set_flags(crypto_ahash_tfm(tfm), flags); +} + +static inline void crypto_ahash_clear_flags(struct crypto_ahash *tfm, u32 flags) +{ + crypto_tfm_clear_flags(crypto_ahash_tfm(tfm), flags); +} + +static inline struct crypto_ahash *crypto_ahash_reqtfm( + struct ahash_request *req) +{ + return __crypto_ahash_cast(req->base.tfm); +} + +static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm) +{ + return crypto_ahash_crt(tfm)->reqsize; +} + +static inline int crypto_ahash_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct ahash_tfm *crt = crypto_ahash_crt(tfm); + + return crt->setkey(crt->base, key, keylen); +} + +static inline int crypto_ahash_digest(struct ahash_request *req) +{ + struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req)); + return crt->digest(req); +} + +static inline void ahash_request_set_tfm(struct ahash_request *req, + struct crypto_ahash *tfm) +{ + req->base.tfm = crypto_ahash_tfm(crypto_ahash_crt(tfm)->base); +} + +static inline struct ahash_request *ahash_request_alloc( + struct crypto_ahash *tfm, gfp_t gfp) +{ + struct ahash_request *req; + + req = kmalloc(sizeof(struct ahash_request) + + crypto_ahash_reqsize(tfm), gfp); + + if (likely(req)) + ahash_request_set_tfm(req, tfm); + + return req; +} + +static inline void ahash_request_free(struct ahash_request *req) +{ + kfree(req); +} + +static inline struct ahash_request *ahash_request_cast( + struct crypto_async_request *req) +{ + return container_of(req, struct ahash_request, base); +} + +static inline void ahash_request_set_callback(struct ahash_request *req, + u32 flags, + crypto_completion_t complete, + void *data) +{ + req->base.complete = complete; + req->base.data = data; + req->base.flags = flags; +} + +static inline void ahash_request_set_crypt(struct ahash_request *req, + struct scatterlist *src, u8 *result, + unsigned int nbytes) +{ + req->src = src; + req->nbytes = nbytes; + req->result = result; +} + #endif /* _LINUX_CRYPTO_H */ -- cgit v1.2.3 From 166247f46a9c866e6f7f7d2212be875fb82212a1 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 7 Jul 2008 20:54:35 +0800 Subject: crypto: hash - Removed vestigial ahash fields The base field in ahash_tfm appears to have been cut-n-pasted from ablkcipher. It isn't needed here at all. Similarly, the info field in ahash_request also appears to have originated from its cipher counter-part and is vestigial. Signed-off-by: Herbert Xu --- crypto/ahash.c | 1 - crypto/digest.c | 1 - crypto/hash.c | 1 - include/linux/crypto.h | 7 ++----- 4 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/crypto/ahash.c b/crypto/ahash.c index 8c1f918a6878..e6e5906ca80a 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -76,7 +76,6 @@ static int crypto_init_ahash_ops(struct crypto_tfm *tfm, u32 type, u32 mask) crt->final = alg->final; crt->digest = alg->digest; crt->setkey = ahash_setkey; - crt->base = __crypto_ahash_cast(tfm); crt->digestsize = alg->digestsize; return 0; diff --git a/crypto/digest.c b/crypto/digest.c index d63d5d96feec..bf332982c50d 100644 --- a/crypto/digest.c +++ b/crypto/digest.c @@ -234,7 +234,6 @@ int crypto_init_digest_ops_async(struct crypto_tfm *tfm) crt->setkey = dalg->dia_setkey ? digest_async_setkey : digest_async_nosetkey; crt->digestsize = dalg->dia_digestsize; - crt->base = __crypto_ahash_cast(tfm); return 0; } diff --git a/crypto/hash.c b/crypto/hash.c index 0d7caa9ab748..140a75565f15 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -128,7 +128,6 @@ static int crypto_init_hash_ops_async(struct crypto_tfm *tfm) crt->digest = hash_async_digest; crt->setkey = hash_async_setkey; crt->digestsize = alg->digestsize; - crt->base = __crypto_ahash_cast(tfm); return 0; } diff --git a/include/linux/crypto.h b/include/linux/crypto.h index b6efe569128d..68ef293644d3 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -137,8 +137,6 @@ struct ablkcipher_request { struct ahash_request { struct crypto_async_request base; - void *info; - unsigned int nbytes; struct scatterlist *src; u8 *result; @@ -420,7 +418,6 @@ struct ahash_tfm { unsigned int keylen); unsigned int digestsize; - struct crypto_ahash *base; unsigned int reqsize; }; @@ -1384,7 +1381,7 @@ static inline int crypto_ahash_setkey(struct crypto_ahash *tfm, { struct ahash_tfm *crt = crypto_ahash_crt(tfm); - return crt->setkey(crt->base, key, keylen); + return crt->setkey(tfm, key, keylen); } static inline int crypto_ahash_digest(struct ahash_request *req) @@ -1396,7 +1393,7 @@ static inline int crypto_ahash_digest(struct ahash_request *req) static inline void ahash_request_set_tfm(struct ahash_request *req, struct crypto_ahash *tfm) { - req->base.tfm = crypto_ahash_tfm(crypto_ahash_crt(tfm)->base); + req->base.tfm = crypto_ahash_tfm(tfm); } static inline struct ahash_request *ahash_request_alloc( -- cgit v1.2.3 From 18e33e6d5cc0495826f5245777cd267732815e01 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jul 2008 16:01:22 +0800 Subject: crypto: hash - Move ahash functions into crypto/hash.h All new crypto interfaces should go into individual files as much as possible in order to ensure that crypto.h does not collapse under its own weight. This patch moves the ahash code into crypto/hash.h and crypto/internal/hash.h respectively. Signed-off-by: Herbert Xu --- crypto/cryptd.c | 1 + crypto/digest.c | 1 + crypto/hash.c | 1 + crypto/tcrypt.c | 1 + include/crypto/algapi.h | 36 ---------- include/crypto/hash.h | 154 +++++++++++++++++++++++++++++++++++++++++ include/crypto/internal/hash.h | 37 ++++++++++ include/linux/crypto.h | 136 ------------------------------------ 8 files changed, 195 insertions(+), 172 deletions(-) create mode 100644 include/crypto/hash.h (limited to 'include/linux') diff --git a/crypto/cryptd.c b/crypto/cryptd.c index d3ecd7e73b7e..d29e06b350ff 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include diff --git a/crypto/digest.c b/crypto/digest.c index bf332982c50d..ac0919460d14 100644 --- a/crypto/digest.c +++ b/crypto/digest.c @@ -12,6 +12,7 @@ * */ +#include #include #include #include diff --git a/crypto/hash.c b/crypto/hash.c index 140a75565f15..cb86b19fd105 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -9,6 +9,7 @@ * any later version. */ +#include #include #include #include diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index 87f08f9b0904..59821a22d752 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -15,6 +15,7 @@ * */ +#include #include #include #include diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index fef272a8ceeb..60d06e784be3 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -98,7 +98,6 @@ extern const struct crypto_type crypto_ablkcipher_type; extern const struct crypto_type crypto_aead_type; extern const struct crypto_type crypto_blkcipher_type; extern const struct crypto_type crypto_hash_type; -extern const struct crypto_type crypto_ahash_type; void crypto_mod_put(struct crypto_alg *alg); @@ -315,40 +314,5 @@ static inline int crypto_requires_sync(u32 type, u32 mask) return (type ^ CRYPTO_ALG_ASYNC) & mask & CRYPTO_ALG_ASYNC; } -static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm) -{ - return crypto_tfm_ctx(&tfm->base); -} - -static inline struct ahash_alg *crypto_ahash_alg( - struct crypto_ahash *tfm) -{ - return &crypto_ahash_tfm(tfm)->__crt_alg->cra_ahash; -} - -static inline int ahash_enqueue_request(struct crypto_queue *queue, - struct ahash_request *request) -{ - return crypto_enqueue_request(queue, &request->base); -} - -static inline struct ahash_request *ahash_dequeue_request( - struct crypto_queue *queue) -{ - return ahash_request_cast(crypto_dequeue_request(queue)); -} - -static inline void *ahash_request_ctx(struct ahash_request *req) -{ - return req->__ctx; -} - -static inline int ahash_tfm_in_queue(struct crypto_queue *queue, - struct crypto_ahash *tfm) -{ - return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm)); -} - - #endif /* _CRYPTO_ALGAPI_H */ diff --git a/include/crypto/hash.h b/include/crypto/hash.h new file mode 100644 index 000000000000..d12498ec8a4e --- /dev/null +++ b/include/crypto/hash.h @@ -0,0 +1,154 @@ +/* + * Hash: Hash algorithms under the crypto API + * + * Copyright (c) 2008 Herbert Xu + * + * 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. + * + */ + +#ifndef _CRYPTO_HASH_H +#define _CRYPTO_HASH_H + +#include + +struct crypto_ahash { + struct crypto_tfm base; +}; + +static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm) +{ + return (struct crypto_ahash *)tfm; +} + +static inline struct crypto_ahash *crypto_alloc_ahash(const char *alg_name, + u32 type, u32 mask) +{ + type &= ~CRYPTO_ALG_TYPE_MASK; + mask &= ~CRYPTO_ALG_TYPE_MASK; + type |= CRYPTO_ALG_TYPE_AHASH; + mask |= CRYPTO_ALG_TYPE_AHASH_MASK; + + return __crypto_ahash_cast(crypto_alloc_base(alg_name, type, mask)); +} + +static inline struct crypto_tfm *crypto_ahash_tfm(struct crypto_ahash *tfm) +{ + return &tfm->base; +} + +static inline void crypto_free_ahash(struct crypto_ahash *tfm) +{ + crypto_free_tfm(crypto_ahash_tfm(tfm)); +} + +static inline unsigned int crypto_ahash_alignmask( + struct crypto_ahash *tfm) +{ + return crypto_tfm_alg_alignmask(crypto_ahash_tfm(tfm)); +} + +static inline struct ahash_tfm *crypto_ahash_crt(struct crypto_ahash *tfm) +{ + return &crypto_ahash_tfm(tfm)->crt_ahash; +} + +static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash *tfm) +{ + return crypto_ahash_crt(tfm)->digestsize; +} + +static inline u32 crypto_ahash_get_flags(struct crypto_ahash *tfm) +{ + return crypto_tfm_get_flags(crypto_ahash_tfm(tfm)); +} + +static inline void crypto_ahash_set_flags(struct crypto_ahash *tfm, u32 flags) +{ + crypto_tfm_set_flags(crypto_ahash_tfm(tfm), flags); +} + +static inline void crypto_ahash_clear_flags(struct crypto_ahash *tfm, u32 flags) +{ + crypto_tfm_clear_flags(crypto_ahash_tfm(tfm), flags); +} + +static inline struct crypto_ahash *crypto_ahash_reqtfm( + struct ahash_request *req) +{ + return __crypto_ahash_cast(req->base.tfm); +} + +static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm) +{ + return crypto_ahash_crt(tfm)->reqsize; +} + +static inline int crypto_ahash_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct ahash_tfm *crt = crypto_ahash_crt(tfm); + + return crt->setkey(tfm, key, keylen); +} + +static inline int crypto_ahash_digest(struct ahash_request *req) +{ + struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req)); + return crt->digest(req); +} + +static inline void ahash_request_set_tfm(struct ahash_request *req, + struct crypto_ahash *tfm) +{ + req->base.tfm = crypto_ahash_tfm(tfm); +} + +static inline struct ahash_request *ahash_request_alloc( + struct crypto_ahash *tfm, gfp_t gfp) +{ + struct ahash_request *req; + + req = kmalloc(sizeof(struct ahash_request) + + crypto_ahash_reqsize(tfm), gfp); + + if (likely(req)) + ahash_request_set_tfm(req, tfm); + + return req; +} + +static inline void ahash_request_free(struct ahash_request *req) +{ + kfree(req); +} + +static inline struct ahash_request *ahash_request_cast( + struct crypto_async_request *req) +{ + return container_of(req, struct ahash_request, base); +} + +static inline void ahash_request_set_callback(struct ahash_request *req, + u32 flags, + crypto_completion_t complete, + void *data) +{ + req->base.complete = complete; + req->base.data = data; + req->base.flags = flags; +} + +static inline void ahash_request_set_crypt(struct ahash_request *req, + struct scatterlist *src, u8 *result, + unsigned int nbytes) +{ + req->src = src; + req->nbytes = nbytes; + req->result = result; +} + +#endif /* _CRYPTO_HASH_H */ diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 93ac42280221..917ae57bad4a 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -14,6 +14,7 @@ #define _CRYPTO_INTERNAL_HASH_H #include +#include struct ahash_request; struct scatterlist; @@ -33,9 +34,45 @@ struct crypto_hash_walk { unsigned int flags; }; +extern const struct crypto_type crypto_ahash_type; + int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err); int crypto_hash_walk_first(struct ahash_request *req, struct crypto_hash_walk *walk); +static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm) +{ + return crypto_tfm_ctx(&tfm->base); +} + +static inline struct ahash_alg *crypto_ahash_alg( + struct crypto_ahash *tfm) +{ + return &crypto_ahash_tfm(tfm)->__crt_alg->cra_ahash; +} + +static inline int ahash_enqueue_request(struct crypto_queue *queue, + struct ahash_request *request) +{ + return crypto_enqueue_request(queue, &request->base); +} + +static inline struct ahash_request *ahash_dequeue_request( + struct crypto_queue *queue) +{ + return ahash_request_cast(crypto_dequeue_request(queue)); +} + +static inline void *ahash_request_ctx(struct ahash_request *req) +{ + return req->__ctx; +} + +static inline int ahash_tfm_in_queue(struct crypto_queue *queue, + struct crypto_ahash *tfm) +{ + return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm)); +} + #endif /* _CRYPTO_INTERNAL_HASH_H */ diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 68ef293644d3..c43dc47fdf75 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -481,10 +481,6 @@ struct crypto_hash { struct crypto_tfm base; }; -struct crypto_ahash { - struct crypto_tfm base; -}; - enum { CRYPTOA_UNSPEC, CRYPTOA_ALG, @@ -1308,137 +1304,5 @@ static inline int crypto_comp_decompress(struct crypto_comp *tfm, src, slen, dst, dlen); } -static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm) -{ - return (struct crypto_ahash *)tfm; -} - -static inline struct crypto_ahash *crypto_alloc_ahash(const char *alg_name, - u32 type, u32 mask) -{ - type &= ~CRYPTO_ALG_TYPE_MASK; - mask &= ~CRYPTO_ALG_TYPE_MASK; - type |= CRYPTO_ALG_TYPE_AHASH; - mask |= CRYPTO_ALG_TYPE_AHASH_MASK; - - return __crypto_ahash_cast(crypto_alloc_base(alg_name, type, mask)); -} - -static inline struct crypto_tfm *crypto_ahash_tfm(struct crypto_ahash *tfm) -{ - return &tfm->base; -} - -static inline void crypto_free_ahash(struct crypto_ahash *tfm) -{ - crypto_free_tfm(crypto_ahash_tfm(tfm)); -} - -static inline unsigned int crypto_ahash_alignmask( - struct crypto_ahash *tfm) -{ - return crypto_tfm_alg_alignmask(crypto_ahash_tfm(tfm)); -} - -static inline struct ahash_tfm *crypto_ahash_crt(struct crypto_ahash *tfm) -{ - return &crypto_ahash_tfm(tfm)->crt_ahash; -} - -static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash *tfm) -{ - return crypto_ahash_crt(tfm)->digestsize; -} - -static inline u32 crypto_ahash_get_flags(struct crypto_ahash *tfm) -{ - return crypto_tfm_get_flags(crypto_ahash_tfm(tfm)); -} - -static inline void crypto_ahash_set_flags(struct crypto_ahash *tfm, u32 flags) -{ - crypto_tfm_set_flags(crypto_ahash_tfm(tfm), flags); -} - -static inline void crypto_ahash_clear_flags(struct crypto_ahash *tfm, u32 flags) -{ - crypto_tfm_clear_flags(crypto_ahash_tfm(tfm), flags); -} - -static inline struct crypto_ahash *crypto_ahash_reqtfm( - struct ahash_request *req) -{ - return __crypto_ahash_cast(req->base.tfm); -} - -static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm) -{ - return crypto_ahash_crt(tfm)->reqsize; -} - -static inline int crypto_ahash_setkey(struct crypto_ahash *tfm, - const u8 *key, unsigned int keylen) -{ - struct ahash_tfm *crt = crypto_ahash_crt(tfm); - - return crt->setkey(tfm, key, keylen); -} - -static inline int crypto_ahash_digest(struct ahash_request *req) -{ - struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req)); - return crt->digest(req); -} - -static inline void ahash_request_set_tfm(struct ahash_request *req, - struct crypto_ahash *tfm) -{ - req->base.tfm = crypto_ahash_tfm(tfm); -} - -static inline struct ahash_request *ahash_request_alloc( - struct crypto_ahash *tfm, gfp_t gfp) -{ - struct ahash_request *req; - - req = kmalloc(sizeof(struct ahash_request) + - crypto_ahash_reqsize(tfm), gfp); - - if (likely(req)) - ahash_request_set_tfm(req, tfm); - - return req; -} - -static inline void ahash_request_free(struct ahash_request *req) -{ - kfree(req); -} - -static inline struct ahash_request *ahash_request_cast( - struct crypto_async_request *req) -{ - return container_of(req, struct ahash_request, base); -} - -static inline void ahash_request_set_callback(struct ahash_request *req, - u32 flags, - crypto_completion_t complete, - void *data) -{ - req->base.complete = complete; - req->base.data = data; - req->base.flags = flags; -} - -static inline void ahash_request_set_crypt(struct ahash_request *req, - struct scatterlist *src, u8 *result, - unsigned int nbytes) -{ - req->src = src; - req->nbytes = nbytes; - req->result = result; -} - #endif /* _LINUX_CRYPTO_H */ -- cgit v1.2.3 From b7a39bd0afc4021e8ad2b1189e884551e147427f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 23 May 2008 18:38:49 +0100 Subject: firmware: make fw->data const In preparation for supporting firmware files linked into the static kernel, make fw->data const to ensure that users aren't modifying it (so that we can pass a pointer to the original in-kernel copy, rather than having to copy it). Signed-off-by: David Woodhouse --- drivers/base/firmware_class.c | 2 +- include/linux/firmware.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 9fd4a8534146..264b3a2cd860 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -257,7 +257,7 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr, if (retval) goto out; - memcpy(fw->data + offset, buffer, count); + memcpy((u8 *)fw->data + offset, buffer, count); fw->size = max_t(size_t, offset + count, fw->size); retval = count; diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 6c7eff2ebada..88718d60153c 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -8,7 +8,7 @@ struct firmware { size_t size; - u8 *data; + const u8 *data; }; struct device; -- cgit v1.2.3 From 5658c769443d543728b6c5c673dffc2df8676317 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 23 May 2008 13:52:42 +0100 Subject: firmware: allow firmware files to be built into kernel image Some drivers have their own hacks to bypass the kernel's firmware loader and build their firmware into the kernel; this renders those unnecessary. Other drivers don't use the firmware loader at all, because they always want the firmware to be available. This allows them to start using the firmware loader. A third set of drivers already use the firmware loader, but can't be used without help from userspace, which sometimes requires an initrd. This allows them to work in a static kernel. Signed-off-by: David Woodhouse --- drivers/base/firmware_class.c | 33 +++++++++++++++++++++++++++++++-- include/asm-generic/vmlinux.lds.h | 7 +++++++ include/linux/firmware.h | 21 +++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 264b3a2cd860..b0be1d18fee2 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -49,6 +49,14 @@ struct firmware_priv { struct timer_list timeout; }; +#ifdef CONFIG_FW_LOADER +extern struct builtin_fw __start_builtin_fw[]; +extern struct builtin_fw __end_builtin_fw[]; +#else /* Module case. Avoid ifdefs later; it'll all optimise out */ +static struct builtin_fw *__start_builtin_fw; +static struct builtin_fw *__end_builtin_fw; +#endif + static void fw_load_abort(struct firmware_priv *fw_priv) { @@ -391,13 +399,12 @@ _request_firmware(const struct firmware **firmware_p, const char *name, struct device *f_dev; struct firmware_priv *fw_priv; struct firmware *firmware; + struct builtin_fw *builtin; int retval; if (!firmware_p) return -EINVAL; - printk(KERN_INFO "firmware: requesting %s\n", name); - *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n", @@ -406,6 +413,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } + for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; + builtin++) { + if (strcmp(name, builtin->name)) + continue; + printk(KERN_INFO "firmware: using built-in firmware %s\n", + name); + firmware->size = builtin->size; + firmware->data = builtin->data; + return 0; + } + + if (uevent) + printk(KERN_INFO "firmware: requesting %s\n", name); + retval = fw_setup_device(firmware, &f_dev, name, device, uevent); if (retval) goto error_kfree_fw; @@ -473,8 +494,16 @@ request_firmware(const struct firmware **firmware_p, const char *name, void release_firmware(const struct firmware *fw) { + struct builtin_fw *builtin; + if (fw) { + for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; + builtin++) { + if (fw->data == builtin->data) + goto free_fw; + } vfree(fw->data); + free_fw: kfree(fw); } } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index f054778e916c..8d71a40625f3 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -86,6 +86,13 @@ VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \ } \ \ + /* Built-in firmware blobs */ \ + .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start_builtin_fw) = .; \ + *(.builtin_fw) \ + VMLINUX_SYMBOL(__end_builtin_fw) = .; \ + } \ + \ /* RapidIO route ops */ \ .rio_route : AT(ADDR(.rio_route) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start_rio_route_ops) = .; \ diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 88718d60153c..c8ecf5b2a207 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -1,7 +1,10 @@ #ifndef _LINUX_FIRMWARE_H #define _LINUX_FIRMWARE_H + #include #include +#include + #define FIRMWARE_NAME_MAX 30 #define FW_ACTION_NOHOTPLUG 0 #define FW_ACTION_HOTPLUG 1 @@ -13,6 +16,24 @@ struct firmware { struct device; +struct builtin_fw { + char *name; + void *data; + unsigned long size; +}; + +/* We have to play tricks here much like stringify() to get the + __COUNTER__ macro to be expanded as we want it */ +#define __fw_concat1(x, y) x##y +#define __fw_concat(x, y) __fw_concat1(x, y) + +#define DECLARE_BUILTIN_FIRMWARE(name, blob) \ + DECLARE_BUILTIN_FIRMWARE_SIZE(name, &(blob), sizeof(blob)) + +#define DECLARE_BUILTIN_FIRMWARE_SIZE(name, blob, size) \ + static const struct builtin_fw __fw_concat(__builtin_fw,__COUNTER__) \ + __used __section(.builtin_fw) = { name, blob, size } + #if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE)) int request_firmware(const struct firmware **fw, const char *name, struct device *device); -- cgit v1.2.3 From bacfe09dd7545467965e8d8f1eab20bc62dce00d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 30 May 2008 13:57:27 +0300 Subject: ihex.h: binary representation of ihex records Some devices need their firmware as a set of {address, len, data...} records in some specific order rather than a simple blob. The normal way of doing this kind of thing is 'ihex', which is a text format and not entirely suitable for use in the kernel. This provides a binary representation which is very similar, but much more compact -- and a helper routine to skip to the next record, because the alignment constraints mean that everybody will screw it up for themselves otherwise. Also a helper function which can verify that a 'struct firmware' contains a valid set of ihex records, and that following them won't run off the end of the loaded data. Signed-off-by: David Woodhouse --- include/linux/ihex.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 include/linux/ihex.h (limited to 'include/linux') diff --git a/include/linux/ihex.h b/include/linux/ihex.h new file mode 100644 index 000000000000..df89edd890ae --- /dev/null +++ b/include/linux/ihex.h @@ -0,0 +1,50 @@ +/* + * Compact binary representation of ihex records. Some devices need their + * firmware loaded in strange orders rather than a single big blob, but + * actually parsing ihex-as-text within the kernel seems silly. Thus,... + */ + +#ifndef __LINUX_IHEX_H__ +#define __LINUX_IHEX_H__ + +#include +#include + +/* Intel HEX files actually limit the length to 256 bytes, but we have + drivers which would benefit from using separate records which are + longer than that, so we extend to 16 bits of length */ +struct ihex_binrec { + __be32 addr; + __be16 len; + uint8_t data[0]; +} __attribute__((aligned(4))); + +/* Find the next record, taking into account the 4-byte alignment */ +static inline const struct ihex_binrec * +ihex_next_binrec(const struct ihex_binrec *rec) +{ + int next = ((be16_to_cpu(rec->len) + 5) & ~3) - 2; + rec = (void *)&rec->data[next]; + + return be16_to_cpu(rec->len) ? rec : NULL; +} + +/* Check that ihex_next_binrec() won't take us off the end of the image... */ +static inline int ihex_validate_fw(const struct firmware *fw) +{ + const struct ihex_binrec *rec; + size_t ofs = 0; + + while (ofs <= fw->size - sizeof(*rec)) { + rec = (void *)&fw->data[ofs]; + + /* Zero length marks end of records */ + if (!be16_to_cpu(rec->len)) + return 0; + + /* Point to next record... */ + ofs += (sizeof(*rec) + be16_to_cpu(rec->len) + 3) & ~3; + } + return -EINVAL; +} +#endif /* __LINUX_IHEX_H__ */ -- cgit v1.2.3 From f1485f3deb89e6ae10c4d34662ec9e692855ab5d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 31 May 2008 15:20:37 +0300 Subject: ihex: request_ihex_firmware() function to load and validate firmware Provide a helper to load the file and validate it in one call, to simplify error handling in the drivers which are going to use it. Signed-off-by: David Woodhouse --- include/linux/ihex.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ihex.h b/include/linux/ihex.h index df89edd890ae..2baace2788a7 100644 --- a/include/linux/ihex.h +++ b/include/linux/ihex.h @@ -9,6 +9,7 @@ #include #include +#include /* Intel HEX files actually limit the length to 256 bytes, but we have drivers which would benefit from using separate records which are @@ -47,4 +48,27 @@ static inline int ihex_validate_fw(const struct firmware *fw) } return -EINVAL; } + +/* Request firmware and validate it so that we can trust we won't + * run off the end while reading records... */ +static inline int request_ihex_firmware(const struct firmware **fw, + const char *fw_name, + struct device *dev) +{ + const struct firmware *lfw; + int ret; + + ret = request_firmware(&lfw, fw_name, dev); + if (ret) + return ret; + ret = ihex_validate_fw(lfw); + if (ret) { + dev_err(dev, "Firmware \"%s\" not valid IHEX records\n", + fw_name); + release_firmware(lfw); + return ret; + } + *fw = lfw; + return 0; +} #endif /* __LINUX_IHEX_H__ */ -- cgit v1.2.3 From ccf9b3b83d0e56fbf20c00a08b15031ce13204a7 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 10 Jul 2008 16:55:37 -0700 Subject: xfrm: Add a XFRM_STATE_AF_UNSPEC flag to xfrm_usersa_info Add a XFRM_STATE_AF_UNSPEC flag to handle the AF_UNSPEC behavior for the selector family. Userspace applications can set this flag to leave the selector family of the xfrm_state unspecified. This can be used to to handle inter family tunnels if the selector is not set from userspace. Signed-off-by: Steffen Klassert Acked-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/xfrm.h | 1 + net/xfrm/xfrm_user.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 2ca6bae88721..fb0c215a3051 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -339,6 +339,7 @@ struct xfrm_usersa_info { #define XFRM_STATE_NOPMTUDISC 4 #define XFRM_STATE_WILDRECV 8 #define XFRM_STATE_ICMP 16 +#define XFRM_STATE_AF_UNSPEC 32 }; struct xfrm_usersa_id { diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index b976d9ed10e4..04c41504f84c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -277,9 +277,8 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info * memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); x->props.flags = p->flags; - if (!x->sel.family) + if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) x->sel.family = p->family; - } /* -- cgit v1.2.3 From d8156534040996f6a93a24d3592d5d587f2587e5 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 8 Jul 2008 15:13:05 -0700 Subject: net: add netif_napi_del function to allow for removal of napistructs Adds netif_napi_del function which is used to remove the napi struct from the netdev napi_list in cases where CONFIG_NETPOLL was enabled. The motivation for adding this is to handle the case in which the number of queues on a device changes due to a configuration change. Previously the napi structs for each queue would be left in the list until the netdev was freed. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik --- include/linux/netdevice.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 203c5504fe43..b54ec16dfbda 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -828,6 +828,19 @@ static inline void netif_napi_add(struct net_device *dev, set_bit(NAPI_STATE_SCHED, &napi->state); } +/** + * netif_napi_del - remove a napi context + * @napi: napi context + * + * netif_napi_del() removes a napi context from the network device napi list + */ +static inline void netif_napi_del(struct napi_struct *napi) +{ +#ifdef CONFIG_NETPOLL + list_del(&napi->dev_list); +#endif +} + struct packet_type { __be16 type; /* This is really htons(ether_type). */ struct net_device *dev; /* NULL is wildcarded here */ -- cgit v1.2.3 From 0f420358e3a2abc028320ace7783e2e38cae77bf Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 11 Jul 2008 22:02:23 +1000 Subject: md: Turn rdev->sb_offset into a sector-based quantity. Rename it to sb_start to make sure all users have been converted. Signed-off-by: Andre Noll Signed-off-by: Neil Brown --- drivers/md/bitmap.c | 10 +++--- drivers/md/md.c | 81 ++++++++++++++++++++++------------------------- include/linux/raid/md_k.h | 2 +- 3 files changed, 44 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index dedba16d42f7..eba83e25b678 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -225,7 +225,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde || test_bit(Faulty, &rdev->flags)) continue; - target = (rdev->sb_offset << 1) + offset + index * (PAGE_SIZE/512); + target = rdev->sb_start + offset + index * (PAGE_SIZE/512); if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) { page->index = index; @@ -262,12 +262,12 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) /* bitmap runs in to metadata */ return -EINVAL; if (rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2 + bitmap->offset) + > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ return -EINVAL; - } else if (rdev->sb_offset*2 < rdev->data_offset) { + } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ - if (rdev->sb_offset*2 + if (rdev->sb_start + bitmap->offset + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) @@ -277,7 +277,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) /* DATA METADATA BITMAP - no problems */ } md_super_write(mddev, rdev, - (rdev->sb_offset<<1) + bitmap->offset + rdev->sb_start + bitmap->offset + page->index * (PAGE_SIZE/512), size, page); diff --git a/drivers/md/md.c b/drivers/md/md.c index 3276edde7576..5590cb54b584 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -356,7 +356,7 @@ static inline sector_t calc_dev_sboffset(struct block_device *bdev) static sector_t calc_num_sectors(mdk_rdev_t *rdev, unsigned chunk_size) { - sector_t num_sectors = rdev->sb_offset * 2; + sector_t num_sectors = rdev->sb_start; if (chunk_size) num_sectors &= ~((sector_t)chunk_size/512 - 1); @@ -383,7 +383,7 @@ static void free_disk_sb(mdk_rdev_t * rdev) put_page(rdev->sb_page); rdev->sb_loaded = 0; rdev->sb_page = NULL; - rdev->sb_offset = 0; + rdev->sb_start = 0; rdev->size = 0; } } @@ -529,7 +529,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size) return 0; - if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, size, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_start, size, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -666,16 +666,14 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; mdp_super_t *sb; int ret; - sector_t sb_offset; /* - * Calculate the position of the superblock, + * Calculate the position of the superblock (512byte sectors), * it's at the end of the disk. * * It also happens to be a multiple of 4Kb. */ - sb_offset = calc_dev_sboffset(rdev->bdev) / 2; - rdev->sb_offset = sb_offset; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); ret = read_disk_sb(rdev, MD_SB_BYTES); if (ret) return ret; @@ -1007,10 +1005,10 @@ super_90_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) size *= 2; /* convert to sectors */ if (rdev->mddev->bitmap_offset) return 0; /* can't move bitmap */ - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; - if (!size || size > rdev->sb_offset*2) - size = rdev->sb_offset*2; - md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + rdev->sb_start = calc_dev_sboffset(rdev->bdev); + if (!size || size > rdev->sb_start) + size = rdev->sb_start; + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); md_super_wait(rdev->mddev); return size/2; /* kB for sysfs */ @@ -1048,12 +1046,12 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { struct mdp_superblock_1 *sb; int ret; - sector_t sb_offset; + sector_t sb_start; char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; int bmask; /* - * Calculate the position of the superblock. + * Calculate the position of the superblock in 512byte sectors. * It is always aligned to a 4K boundary and * depeding on minor_version, it can be: * 0: At least 8K, but less than 12K, from end of device @@ -1062,22 +1060,20 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) */ switch(minor_version) { case 0: - sb_offset = rdev->bdev->bd_inode->i_size >> 9; - sb_offset -= 8*2; - sb_offset &= ~(sector_t)(4*2-1); - /* convert from sectors to K */ - sb_offset /= 2; + sb_start = rdev->bdev->bd_inode->i_size >> 9; + sb_start -= 8*2; + sb_start &= ~(sector_t)(4*2-1); break; case 1: - sb_offset = 0; + sb_start = 0; break; case 2: - sb_offset = 4; + sb_start = 8; break; default: return -EINVAL; } - rdev->sb_offset = sb_offset; + rdev->sb_start = sb_start; /* superblock is rarely larger than 1K, but it can be larger, * and it is safe to read 4k, so we do that @@ -1091,7 +1087,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || sb->major_version != cpu_to_le32(1) || le32_to_cpu(sb->max_dev) > (4096-256)/2 || - le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + le64_to_cpu(sb->super_offset) != rdev->sb_start || (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0) return -EINVAL; @@ -1127,7 +1123,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) rdev->sb_size = (rdev->sb_size | bmask) + 1; if (minor_version - && rdev->data_offset < sb_offset + (rdev->sb_size/512)) + && rdev->data_offset < sb_start + (rdev->sb_size/512)) return -EINVAL; if (sb->level == cpu_to_le32(LEVEL_MULTIPATH)) @@ -1163,7 +1159,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (minor_version) rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; else - rdev->size = rdev->sb_offset; + rdev->size = rdev->sb_start / 2; if (rdev->size < le64_to_cpu(sb->data_size)/2) return -EINVAL; rdev->size = le64_to_cpu(sb->data_size)/2; @@ -1350,7 +1346,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) if (size && size < rdev->mddev->size) return 0; /* component must fit device */ size *= 2; /* convert to sectors */ - if (rdev->sb_offset < rdev->data_offset/2) { + if (rdev->sb_start < rdev->data_offset) { /* minor versions 1 and 2; superblock before data */ max_size = (rdev->bdev->bd_inode->i_size >> 9); max_size -= rdev->data_offset; @@ -1361,19 +1357,19 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) return 0; } else { /* minor version 0; superblock after data */ - sector_t sb_offset; - sb_offset = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; - sb_offset &= ~(sector_t)(4*2 - 1); - max_size = rdev->size*2 + sb_offset - rdev->sb_offset*2; + sector_t sb_start; + sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; + sb_start &= ~(sector_t)(4*2 - 1); + max_size = rdev->size*2 + sb_start - rdev->sb_start; if (!size || size > max_size) size = max_size; - rdev->sb_offset = sb_offset/2; + rdev->sb_start = sb_start; } sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page); sb->data_size = cpu_to_le64(size); - sb->super_offset = rdev->sb_offset*2; + sb->super_offset = rdev->sb_start; sb->sb_csum = calc_sb_1_csum(sb); - md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); md_super_wait(rdev->mddev); return size/2; /* kB for sysfs */ @@ -1810,11 +1806,11 @@ repeat: dprintk("%s ", bdevname(rdev->bdev,b)); if (!test_bit(Faulty, &rdev->flags)) { md_super_write(mddev,rdev, - rdev->sb_offset<<1, rdev->sb_size, + rdev->sb_start, rdev->sb_size, rdev->sb_page); dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdevname(rdev->bdev,b), - (unsigned long long)rdev->sb_offset); + (unsigned long long)rdev->sb_start); rdev->sb_events = mddev->events; } else @@ -3577,16 +3573,16 @@ static int do_md_run(mddev_t * mddev) * We don't want the data to overlap the metadata, * Internal Bitmap issues has handled elsewhere. */ - if (rdev->data_offset < rdev->sb_offset) { + if (rdev->data_offset < rdev->sb_start) { if (mddev->size && rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2) { + > rdev->sb_start) { printk("md: %s: data overlaps metadata\n", mdname(mddev)); return -EINVAL; } } else { - if (rdev->sb_offset*2 + rdev->sb_size/512 + if (rdev->sb_start + rdev->sb_size/512 > rdev->data_offset) { printk("md: %s: metadata overlaps data\n", mdname(mddev)); @@ -4355,9 +4351,9 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); - rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; } else - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; err = bind_rdev_to_array(rdev, mddev); @@ -4424,10 +4420,9 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) } if (mddev->persistent) - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); else - rdev->sb_offset = - rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; @@ -4628,7 +4623,7 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) * linear and raid0 always use whatever space is available. We can only * consider changing this number if no resync or reconstruction is * happening, and if the new size is acceptable. It must fit before the - * sb_offset or, if that is Date: Fri, 11 Jul 2008 22:02:23 +1000 Subject: md: Remove some unused macros. Signed-off-by: Andre Noll Signed-off-by: Neil Brown --- include/linux/raid/md_p.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index 3f2cd98c508b..8b4de4a41ff1 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h @@ -43,14 +43,11 @@ */ #define MD_RESERVED_BYTES (64 * 1024) #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) -#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) -#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) #define MD_SB_BYTES 4096 #define MD_SB_WORDS (MD_SB_BYTES / 4) -#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) #define MD_SB_SECTORS (MD_SB_BYTES / 512) /* -- cgit v1.2.3 From a2bb6a3d85ef3124cd336403a95abc0540d3fbe2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 10 Jul 2008 20:58:15 -0400 Subject: ftrace: add ftrace_kill_atomic It has been suggested that I add a way to disable the function tracer on an oops. This code adds a ftrace_kill_atomic. It is not meant to be used in normal situations. It will disable the ftrace tracer, but will not perform the nice shutdown that requires scheduling. Signed-off-by: Steven Rostedt Cc: Steven Rostedt Cc: Peter Zijlstra Cc: Andrew Morton Signed-off-by: Ingo Molnar --- include/linux/ftrace.h | 1 + kernel/trace/ftrace.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 3121b95443d9..f368d041e02d 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -89,6 +89,7 @@ void ftrace_enable_daemon(void); /* totally disable ftrace - can not re-enable after this */ void ftrace_kill(void); +void ftrace_kill_atomic(void); static inline void tracer_disable(void) { diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 0f271c45cd02..1359632668a4 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1601,6 +1601,21 @@ core_initcall(ftrace_dynamic_init); # define ftrace_force_shutdown() do { } while (0) #endif /* CONFIG_DYNAMIC_FTRACE */ +/** + * ftrace_kill_atomic - kill ftrace from critical sections + * + * This function should be used by panic code. It stops ftrace + * but in a not so nice way. If you need to simply kill ftrace + * from a non-atomic section, use ftrace_kill. + */ +void ftrace_kill_atomic(void) +{ + ftrace_disabled = 1; + ftrace_enabled = 0; + ftraced_suspend = -1; + clear_ftrace_function(); +} + /** * ftrace_kill - totally shutdown ftrace * -- cgit v1.2.3 From af52a90a14cdaa54ecbfb6e6982abb13466a4b56 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 7 Jul 2008 14:16:52 -0400 Subject: sched_clock: stop maximum check on NO HZ Working with ftrace I would get large jumps of 11 millisecs or more with the clock tracer. This killed the latencing timings of ftrace and also caused the irqoff self tests to fail. What was happening is with NO_HZ the idle would stop the jiffy counter and before the jiffy counter was updated the sched_clock would have a bad delta jiffies to compare with the gtod with the maximum. The jiffies would stop and the last sched_tick would record the last gtod. On wakeup, the sched clock update would compare the gtod + delta jiffies (which would be zero) and compare it to the TSC. The TSC would have correctly (with a stable TSC) moved forward several jiffies. But because the jiffies has not been updated yet the clock would be prevented from moving forward because it would appear that the TSC jumped too far ahead. The clock would then virtually stop, until the jiffies are updated. Then the next sched clock update would see that the clock was very much behind since the delta jiffies is now correct. This would then jump the clock forward by several jiffies. This caused ftrace to report several milliseconds of interrupts off latency at every resume from NO_HZ idle. This patch adds hooks into the nohz code to disable the checking of the maximum clock update when nohz is in effect. It resumes the max check when nohz has updated the jiffies again. Signed-off-by: Steven Rostedt Cc: Steven Rostedt Cc: Peter Zijlstra Cc: Andrew Morton Signed-off-by: Ingo Molnar --- include/linux/sched.h | 17 ++++++++++++++++- kernel/sched_clock.c | 39 ++++++++++++++++++++++++++++++++++++++- kernel/time/tick-sched.c | 2 ++ 3 files changed, 56 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index c5d3f847ca8d..33a8f42041fa 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1573,13 +1573,28 @@ static inline void sched_clock_idle_sleep_event(void) static inline void sched_clock_idle_wakeup_event(u64 delta_ns) { } -#else + +#ifdef CONFIG_NO_HZ +static inline void sched_clock_tick_stop(int cpu) +{ +} + +static inline void sched_clock_tick_start(int cpu) +{ +} +#endif + +#else /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */ extern void sched_clock_init(void); extern u64 sched_clock_cpu(int cpu); extern void sched_clock_tick(void); extern void sched_clock_idle_sleep_event(void); extern void sched_clock_idle_wakeup_event(u64 delta_ns); +#ifdef CONFIG_NO_HZ +extern void sched_clock_tick_stop(int cpu); +extern void sched_clock_tick_start(int cpu); #endif +#endif /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */ /* * For kernel-internal use: high-speed (but slightly incorrect) per-cpu diff --git a/kernel/sched_clock.c b/kernel/sched_clock.c index 42b81fa38cbd..97159e225a77 100644 --- a/kernel/sched_clock.c +++ b/kernel/sched_clock.c @@ -45,6 +45,9 @@ struct sched_clock_data { u64 tick_raw; u64 tick_gtod; u64 clock; +#ifdef CONFIG_NO_HZ + int check_max; +#endif }; static DEFINE_PER_CPU_SHARED_ALIGNED(struct sched_clock_data, sched_clock_data); @@ -76,11 +79,45 @@ void sched_clock_init(void) scd->tick_raw = 0; scd->tick_gtod = ktime_now; scd->clock = ktime_now; +#ifdef CONFIG_NO_HZ + scd->check_max = 1; +#endif } sched_clock_running = 1; } +#ifdef CONFIG_NO_HZ +/* + * The dynamic ticks makes the delta jiffies inaccurate. This + * prevents us from checking the maximum time update. + * Disable the maximum check during stopped ticks. + */ +void sched_clock_tick_stop(int cpu) +{ + struct sched_clock_data *scd = cpu_sdc(cpu); + + scd->check_max = 0; +} + +void sched_clock_tick_start(int cpu) +{ + struct sched_clock_data *scd = cpu_sdc(cpu); + + scd->check_max = 1; +} + +static int check_max(struct sched_clock_data *scd) +{ + return scd->check_max; +} +#else +static int check_max(struct sched_clock_data *scd) +{ + return 1; +} +#endif /* CONFIG_NO_HZ */ + /* * update the percpu scd from the raw @now value * @@ -112,7 +149,7 @@ static void __update_sched_clock(struct sched_clock_data *scd, u64 now) */ max_clock = scd->tick_gtod + (2 + delta_jiffies) * TICK_NSEC; - if (unlikely(clock + delta > max_clock)) { + if (unlikely(clock + delta > max_clock) && check_max(scd)) { if (clock < max_clock) clock = max_clock; else diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index b854a895591e..d63008b09a4c 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -276,6 +276,7 @@ void tick_nohz_stop_sched_tick(void) ts->tick_stopped = 1; ts->idle_jiffies = last_jiffies; rcu_enter_nohz(); + sched_clock_tick_stop(cpu); } /* @@ -375,6 +376,7 @@ void tick_nohz_restart_sched_tick(void) select_nohz_load_balancer(0); now = ktime_get(); tick_do_update_jiffies64(now); + sched_clock_tick_start(cpu); cpu_clear(cpu, nohz_cpu_mask); /* -- cgit v1.2.3 From 736603ab297506f4396cb5af592004499950fcfd Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: jbd2: Add commit time into the commit block Carlo Wood has demonstrated that it's possible to recover deleted files from the journal. Something that will make this easier is if we can put the time of the commit into commit block. Signed-off-by: "Theodore Ts'o" --- fs/jbd2/commit.c | 3 +++ include/linux/jbd2.h | 2 ++ 2 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index a2ed72f7ceee..92b6ac3df8ab 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -112,6 +112,7 @@ static int journal_submit_commit_record(journal_t *journal, struct buffer_head *bh; int ret; int barrier_done = 0; + struct timespec now = current_kernel_time(); if (is_journal_aborted(journal)) return 0; @@ -126,6 +127,8 @@ static int journal_submit_commit_record(journal_t *journal, tmp->h_magic = cpu_to_be32(JBD2_MAGIC_NUMBER); tmp->h_blocktype = cpu_to_be32(JBD2_COMMIT_BLOCK); tmp->h_sequence = cpu_to_be32(commit_transaction->t_tid); + tmp->h_commit_sec = cpu_to_be64(now.tv_sec); + tmp->h_commit_nsec = cpu_to_be32(now.tv_nsec); if (JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM)) { diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index d147f0f90360..ec9cadf58227 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -168,6 +168,8 @@ struct commit_header { unsigned char h_chksum_size; unsigned char h_padding[2]; __be32 h_chksum[JBD2_CHECKSUM_BYTES]; + __be64 h_commit_sec; + __be32 h_commit_nsec; }; /* -- cgit v1.2.3 From f4c0a0fdfae708f7aa438c27a380ed4071294e11 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: vfs: export filemap_fdatawrite_range() Make filemap_fdatawrite_range() function public, so that it can later be used in ordered mode rewrite by JBD/JBD2. Signed-off-by: Jan Kara --- include/linux/fs.h | 2 ++ mm/filemap.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index d8e2762ed14d..97f992adc62d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1740,6 +1740,8 @@ extern int wait_on_page_writeback_range(struct address_space *mapping, pgoff_t start, pgoff_t end); extern int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start, loff_t end, int sync_mode); +extern int filemap_fdatawrite_range(struct address_space *mapping, + loff_t start, loff_t end); extern long do_fsync(struct file *file, int datasync); extern void sync_supers(void); diff --git a/mm/filemap.c b/mm/filemap.c index 1e6a7d34874f..65d9d9e2b755 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -236,11 +236,12 @@ int filemap_fdatawrite(struct address_space *mapping) } EXPORT_SYMBOL(filemap_fdatawrite); -static int filemap_fdatawrite_range(struct address_space *mapping, loff_t start, +int filemap_fdatawrite_range(struct address_space *mapping, loff_t start, loff_t end) { return __filemap_fdatawrite_range(mapping, start, end, WB_SYNC_ALL); } +EXPORT_SYMBOL(filemap_fdatawrite_range); /** * filemap_flush - mostly a non-blocking flush -- cgit v1.2.3 From c851ed540173736e60d48b53b91a16ea5c903896 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: jbd2: Implement data=ordered mode handling via inodes This patch adds necessary framework into JBD2 to be able to track inodes with each transaction and write-out their dirty data during transaction commit time. This new ordered mode brings all sorts of advantages such as possibility to get rid of journal heads and buffer heads for data buffers in ordered mode, better ordering of writes on transaction commit, simplification of some JBD code, no more anonymous pages when truncate of data being committed happens. Also with this new ordered mode, delayed allocation on ordered mode is much simpler. Signed-off-by: Jan Kara --- fs/jbd2/commit.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/jbd2/journal.c | 52 +++++++++++++++++++++++++++++ fs/jbd2/transaction.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/jbd2.h | 42 ++++++++++++++++++++++++ 4 files changed, 270 insertions(+) (limited to 'include/linux') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 92b6ac3df8ab..3ca107b5c86b 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -355,6 +355,81 @@ write_out_data: journal_do_submit_data(wbuf, bufs); } +/* + * Submit all the data buffers of inode associated with the transaction to + * disk. + * + * We are in a committing transaction. Therefore no new inode can be added to + * our inode list. We use JI_COMMIT_RUNNING flag to protect inode we currently + * operate on from being released while we write out pages. + */ +static int journal_submit_inode_data_buffers(journal_t *journal, + transaction_t *commit_transaction) +{ + struct jbd2_inode *jinode; + int err, ret = 0; + struct address_space *mapping; + + spin_lock(&journal->j_list_lock); + list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) { + mapping = jinode->i_vfs_inode->i_mapping; + jinode->i_flags |= JI_COMMIT_RUNNING; + spin_unlock(&journal->j_list_lock); + err = filemap_fdatawrite_range(mapping, 0, + i_size_read(jinode->i_vfs_inode)); + if (!ret) + ret = err; + spin_lock(&journal->j_list_lock); + J_ASSERT(jinode->i_transaction == commit_transaction); + jinode->i_flags &= ~JI_COMMIT_RUNNING; + wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING); + } + spin_unlock(&journal->j_list_lock); + return ret; +} + +/* + * Wait for data submitted for writeout, refile inodes to proper + * transaction if needed. + * + */ +static int journal_finish_inode_data_buffers(journal_t *journal, + transaction_t *commit_transaction) +{ + struct jbd2_inode *jinode, *next_i; + int err, ret = 0; + + /* For locking, see the comment in journal_submit_inode_data_buffers() */ + spin_lock(&journal->j_list_lock); + list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) { + jinode->i_flags |= JI_COMMIT_RUNNING; + spin_unlock(&journal->j_list_lock); + err = filemap_fdatawait(jinode->i_vfs_inode->i_mapping); + if (!ret) + ret = err; + spin_lock(&journal->j_list_lock); + jinode->i_flags &= ~JI_COMMIT_RUNNING; + wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING); + } + + /* Now refile inode to proper lists */ + list_for_each_entry_safe(jinode, next_i, + &commit_transaction->t_inode_list, i_list) { + list_del(&jinode->i_list); + if (jinode->i_next_transaction) { + jinode->i_transaction = jinode->i_next_transaction; + jinode->i_next_transaction = NULL; + list_add(&jinode->i_list, + &jinode->i_transaction->t_inode_list); + } else { + jinode->i_transaction = NULL; + } + } + spin_unlock(&journal->j_list_lock); + + return ret; +} + static __u32 jbd2_checksum_data(__u32 crc32_sum, struct buffer_head *bh) { struct page *page = bh->b_page; @@ -529,6 +604,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) */ err = 0; journal_submit_data_buffers(journal, commit_transaction); + err = journal_submit_inode_data_buffers(journal, commit_transaction); + if (err) + jbd2_journal_abort(journal, err); /* * Wait for all previously submitted IO to complete if commit @@ -760,6 +838,17 @@ start_journal_io: __jbd2_journal_abort_hard(journal); } + /* + * This is the right place to wait for data buffers both for ASYNC + * and !ASYNC commit. If commit is ASYNC, we need to wait only after + * the commit block went to disk (which happens above). If commit is + * SYNC, we need to wait for data buffers before we start writing + * commit block, which happens below in such setting. + */ + err = journal_finish_inode_data_buffers(journal, commit_transaction); + if (err) + jbd2_journal_abort(journal, err); + /* Lo and behold: we have just managed to send a transaction to the log. Before we can commit it, wait for the IO so far to complete. Control buffers being written are on the @@ -880,6 +969,7 @@ wait_for_iobuf: jbd_debug(3, "JBD: commit phase 7\n"); J_ASSERT(commit_transaction->t_sync_datalist == NULL); + J_ASSERT(list_empty(&commit_transaction->t_inode_list)); J_ASSERT(commit_transaction->t_buffers == NULL); J_ASSERT(commit_transaction->t_checkpoint_list == NULL); J_ASSERT(commit_transaction->t_iobuf_list == NULL); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 2e24567c4a79..78cf7bd7f604 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -82,6 +82,10 @@ EXPORT_SYMBOL(jbd2_journal_blocks_per_page); EXPORT_SYMBOL(jbd2_journal_invalidatepage); EXPORT_SYMBOL(jbd2_journal_try_to_free_buffers); EXPORT_SYMBOL(jbd2_journal_force_commit); +EXPORT_SYMBOL(jbd2_journal_file_inode); +EXPORT_SYMBOL(jbd2_journal_init_jbd_inode); +EXPORT_SYMBOL(jbd2_journal_release_jbd_inode); +EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate); static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *); static void __journal_abort_soft (journal_t *journal, int errno); @@ -2194,6 +2198,54 @@ void jbd2_journal_put_journal_head(struct journal_head *jh) jbd_unlock_bh_journal_head(bh); } +/* + * Initialize jbd inode head + */ +void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode) +{ + jinode->i_transaction = NULL; + jinode->i_next_transaction = NULL; + jinode->i_vfs_inode = inode; + jinode->i_flags = 0; + INIT_LIST_HEAD(&jinode->i_list); +} + +/* + * Function to be called before we start removing inode from memory (i.e., + * clear_inode() is a fine place to be called from). It removes inode from + * transaction's lists. + */ +void jbd2_journal_release_jbd_inode(journal_t *journal, + struct jbd2_inode *jinode) +{ + int writeout = 0; + + if (!journal) + return; +restart: + spin_lock(&journal->j_list_lock); + /* Is commit writing out inode - we have to wait */ + if (jinode->i_flags & JI_COMMIT_RUNNING) { + wait_queue_head_t *wq; + DEFINE_WAIT_BIT(wait, &jinode->i_flags, __JI_COMMIT_RUNNING); + wq = bit_waitqueue(&jinode->i_flags, __JI_COMMIT_RUNNING); + prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE); + spin_unlock(&journal->j_list_lock); + schedule(); + finish_wait(wq, &wait.wait); + goto restart; + } + + /* Do we need to wait for data writeback? */ + if (journal->j_committing_transaction == jinode->i_transaction) + writeout = 1; + if (jinode->i_transaction) { + list_del(&jinode->i_list); + jinode->i_transaction = NULL; + } + spin_unlock(&journal->j_list_lock); +} + /* * debugfs tunables */ diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index ba620c4493d2..98b596d23705 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -51,6 +51,7 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) transaction->t_tid = journal->j_transaction_sequence++; transaction->t_expires = jiffies + journal->j_commit_interval; spin_lock_init(&transaction->t_handle_lock); + INIT_LIST_HEAD(&transaction->t_inode_list); /* Set up the commit timer for the new transaction. */ journal->j_commit_timer.expires = round_jiffies(transaction->t_expires); @@ -2195,3 +2196,88 @@ void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh) spin_unlock(&journal->j_list_lock); __brelse(bh); } + +/* + * File inode in the inode list of the handle's transaction + */ +int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode) +{ + transaction_t *transaction = handle->h_transaction; + journal_t *journal = transaction->t_journal; + + if (is_handle_aborted(handle)) + return -EIO; + + jbd_debug(4, "Adding inode %lu, tid:%d\n", jinode->i_vfs_inode->i_ino, + transaction->t_tid); + + /* + * First check whether inode isn't already on the transaction's + * lists without taking the lock. Note that this check is safe + * without the lock as we cannot race with somebody removing inode + * from the transaction. The reason is that we remove inode from the + * transaction only in journal_release_jbd_inode() and when we commit + * the transaction. We are guarded from the first case by holding + * a reference to the inode. We are safe against the second case + * because if jinode->i_transaction == transaction, commit code + * cannot touch the transaction because we hold reference to it, + * and if jinode->i_next_transaction == transaction, commit code + * will only file the inode where we want it. + */ + if (jinode->i_transaction == transaction || + jinode->i_next_transaction == transaction) + return 0; + + spin_lock(&journal->j_list_lock); + + if (jinode->i_transaction == transaction || + jinode->i_next_transaction == transaction) + goto done; + + /* On some different transaction's list - should be + * the committing one */ + if (jinode->i_transaction) { + J_ASSERT(jinode->i_next_transaction == NULL); + J_ASSERT(jinode->i_transaction == + journal->j_committing_transaction); + jinode->i_next_transaction = transaction; + goto done; + } + /* Not on any transaction list... */ + J_ASSERT(!jinode->i_next_transaction); + jinode->i_transaction = transaction; + list_add(&jinode->i_list, &transaction->t_inode_list); +done: + spin_unlock(&journal->j_list_lock); + + return 0; +} + +/* + * This function must be called when inode is journaled in ordered mode + * before truncation happens. It starts writeout of truncated part in + * case it is in the committing transaction so that we stand to ordered + * mode consistency guarantees. + */ +int jbd2_journal_begin_ordered_truncate(struct jbd2_inode *inode, + loff_t new_size) +{ + journal_t *journal; + transaction_t *commit_trans; + int ret = 0; + + if (!inode->i_transaction && !inode->i_next_transaction) + goto out; + journal = inode->i_transaction->t_journal; + spin_lock(&journal->j_state_lock); + commit_trans = journal->j_committing_transaction; + spin_unlock(&journal->j_state_lock); + if (inode->i_transaction == commit_trans) { + ret = filemap_fdatawrite_range(inode->i_vfs_inode->i_mapping, + new_size, LLONG_MAX); + if (ret) + jbd2_journal_abort(journal, ret); + } +out: + return ret; +} diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index ec9cadf58227..622c3d8ca4ed 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -381,6 +381,38 @@ static inline void jbd_unlock_bh_journal_head(struct buffer_head *bh) bit_spin_unlock(BH_JournalHead, &bh->b_state); } +/* Flags in jbd_inode->i_flags */ +#define __JI_COMMIT_RUNNING 0 +/* Commit of the inode data in progress. We use this flag to protect us from + * concurrent deletion of inode. We cannot use reference to inode for this + * since we cannot afford doing last iput() on behalf of kjournald + */ +#define JI_COMMIT_RUNNING (1 << __JI_COMMIT_RUNNING) + +/** + * struct jbd_inode is the structure linking inodes in ordered mode + * present in a transaction so that we can sync them during commit. + */ +struct jbd2_inode { + /* Which transaction does this inode belong to? Either the running + * transaction or the committing one. [j_list_lock] */ + transaction_t *i_transaction; + + /* Pointer to the running transaction modifying inode's data in case + * there is already a committing transaction touching it. [j_list_lock] */ + transaction_t *i_next_transaction; + + /* List of inodes in the i_transaction [j_list_lock] */ + struct list_head i_list; + + /* VFS inode this inode belongs to [constant during the lifetime + * of the structure] */ + struct inode *i_vfs_inode; + + /* Flags of inode [j_list_lock] */ + unsigned int i_flags; +}; + struct jbd2_revoke_table_s; /** @@ -566,6 +598,12 @@ struct transaction_s */ struct journal_head *t_log_list; + /* + * List of inodes whose data we've modified in data=ordered mode. + * [j_list_lock] + */ + struct list_head t_inode_list; + /* * Protects info related to handles */ @@ -1046,6 +1084,10 @@ extern void jbd2_journal_ack_err (journal_t *); extern int jbd2_journal_clear_err (journal_t *); extern int jbd2_journal_bmap(journal_t *, unsigned long, unsigned long long *); extern int jbd2_journal_force_commit(journal_t *); +extern int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *inode); +extern int jbd2_journal_begin_ordered_truncate(struct jbd2_inode *inode, loff_t new_size); +extern void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode); +extern void jbd2_journal_release_jbd_inode(journal_t *journal, struct jbd2_inode *jinode); /* * journal_head management -- cgit v1.2.3 From 87c89c232c8f7b3820c33c3b9bc803e9358027da Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: jbd2: Remove data=ordered mode support using jbd buffer heads Signed-off-by: Jan Kara --- fs/jbd2/checkpoint.c | 1 - fs/jbd2/commit.c | 221 ++------------------------------------------------ fs/jbd2/journal.c | 1 - fs/jbd2/transaction.c | 217 ++----------------------------------------------- include/linux/jbd2.h | 29 ++----- 5 files changed, 21 insertions(+), 448 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 6914598022ce..91389c8aee8a 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -688,7 +688,6 @@ void __jbd2_journal_drop_transaction(journal_t *journal, transaction_t *transact J_ASSERT(transaction->t_state == T_FINISHED); J_ASSERT(transaction->t_buffers == NULL); - J_ASSERT(transaction->t_sync_datalist == NULL); J_ASSERT(transaction->t_forget == NULL); J_ASSERT(transaction->t_iobuf_list == NULL); J_ASSERT(transaction->t_shadow_list == NULL); diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 3ca107b5c86b..483183d15ed5 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -37,8 +37,8 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate) } /* - * When an ext3-ordered file is truncated, it is possible that many pages are - * not sucessfully freed, because they are attached to a committing transaction. + * When an ext4 file is truncated, it is possible that some pages are not + * successfully freed, because they are attached to a committing transaction. * After the transaction commits, these pages are left on the LRU, with no * ->mapping, and with attached buffers. These pages are trivially reclaimable * by the VM, but their apparent absence upsets the VM accounting, and it makes @@ -79,21 +79,6 @@ nope: __brelse(bh); } -/* - * Try to acquire jbd_lock_bh_state() against the buffer, when j_list_lock is - * held. For ranking reasons we must trylock. If we lose, schedule away and - * return 0. j_list_lock is dropped in this case. - */ -static int inverted_lock(journal_t *journal, struct buffer_head *bh) -{ - if (!jbd_trylock_bh_state(bh)) { - spin_unlock(&journal->j_list_lock); - schedule(); - return 0; - } - return 1; -} - /* * Done it all: now submit the commit record. We should have * cleaned up our previous buffers by now, so if we are in abort @@ -199,162 +184,6 @@ static int journal_wait_on_commit_record(struct buffer_head *bh) return ret; } -/* - * Wait for all submitted IO to complete. - */ -static int journal_wait_on_locked_list(journal_t *journal, - transaction_t *commit_transaction) -{ - int ret = 0; - struct journal_head *jh; - - while (commit_transaction->t_locked_list) { - struct buffer_head *bh; - - jh = commit_transaction->t_locked_list->b_tprev; - bh = jh2bh(jh); - get_bh(bh); - if (buffer_locked(bh)) { - spin_unlock(&journal->j_list_lock); - wait_on_buffer(bh); - if (unlikely(!buffer_uptodate(bh))) - ret = -EIO; - spin_lock(&journal->j_list_lock); - } - if (!inverted_lock(journal, bh)) { - put_bh(bh); - spin_lock(&journal->j_list_lock); - continue; - } - if (buffer_jbd(bh) && jh->b_jlist == BJ_Locked) { - __jbd2_journal_unfile_buffer(jh); - jbd_unlock_bh_state(bh); - jbd2_journal_remove_journal_head(bh); - put_bh(bh); - } else { - jbd_unlock_bh_state(bh); - } - put_bh(bh); - cond_resched_lock(&journal->j_list_lock); - } - return ret; - } - -static void journal_do_submit_data(struct buffer_head **wbuf, int bufs) -{ - int i; - - for (i = 0; i < bufs; i++) { - wbuf[i]->b_end_io = end_buffer_write_sync; - /* We use-up our safety reference in submit_bh() */ - submit_bh(WRITE, wbuf[i]); - } -} - -/* - * Submit all the data buffers to disk - */ -static void journal_submit_data_buffers(journal_t *journal, - transaction_t *commit_transaction) -{ - struct journal_head *jh; - struct buffer_head *bh; - int locked; - int bufs = 0; - struct buffer_head **wbuf = journal->j_wbuf; - - /* - * Whenever we unlock the journal and sleep, things can get added - * onto ->t_sync_datalist, so we have to keep looping back to - * write_out_data until we *know* that the list is empty. - * - * Cleanup any flushed data buffers from the data list. Even in - * abort mode, we want to flush this out as soon as possible. - */ -write_out_data: - cond_resched(); - spin_lock(&journal->j_list_lock); - - while (commit_transaction->t_sync_datalist) { - jh = commit_transaction->t_sync_datalist; - bh = jh2bh(jh); - locked = 0; - - /* Get reference just to make sure buffer does not disappear - * when we are forced to drop various locks */ - get_bh(bh); - /* If the buffer is dirty, we need to submit IO and hence - * we need the buffer lock. We try to lock the buffer without - * blocking. If we fail, we need to drop j_list_lock and do - * blocking lock_buffer(). - */ - if (buffer_dirty(bh)) { - if (test_set_buffer_locked(bh)) { - BUFFER_TRACE(bh, "needs blocking lock"); - spin_unlock(&journal->j_list_lock); - /* Write out all data to prevent deadlocks */ - journal_do_submit_data(wbuf, bufs); - bufs = 0; - lock_buffer(bh); - spin_lock(&journal->j_list_lock); - } - locked = 1; - } - /* We have to get bh_state lock. Again out of order, sigh. */ - if (!inverted_lock(journal, bh)) { - jbd_lock_bh_state(bh); - spin_lock(&journal->j_list_lock); - } - /* Someone already cleaned up the buffer? */ - if (!buffer_jbd(bh) - || jh->b_transaction != commit_transaction - || jh->b_jlist != BJ_SyncData) { - jbd_unlock_bh_state(bh); - if (locked) - unlock_buffer(bh); - BUFFER_TRACE(bh, "already cleaned up"); - put_bh(bh); - continue; - } - if (locked && test_clear_buffer_dirty(bh)) { - BUFFER_TRACE(bh, "needs writeout, adding to array"); - wbuf[bufs++] = bh; - __jbd2_journal_file_buffer(jh, commit_transaction, - BJ_Locked); - jbd_unlock_bh_state(bh); - if (bufs == journal->j_wbufsize) { - spin_unlock(&journal->j_list_lock); - journal_do_submit_data(wbuf, bufs); - bufs = 0; - goto write_out_data; - } - } else if (!locked && buffer_locked(bh)) { - __jbd2_journal_file_buffer(jh, commit_transaction, - BJ_Locked); - jbd_unlock_bh_state(bh); - put_bh(bh); - } else { - BUFFER_TRACE(bh, "writeout complete: unfile"); - __jbd2_journal_unfile_buffer(jh); - jbd_unlock_bh_state(bh); - if (locked) - unlock_buffer(bh); - jbd2_journal_remove_journal_head(bh); - /* Once for our safety reference, once for - * jbd2_journal_remove_journal_head() */ - put_bh(bh); - put_bh(bh); - } - - if (need_resched() || spin_needbreak(&journal->j_list_lock)) { - spin_unlock(&journal->j_list_lock); - goto write_out_data; - } - } - spin_unlock(&journal->j_list_lock); - journal_do_submit_data(wbuf, bufs); -} - /* * Submit all the data buffers of inode associated with the transaction to * disk. @@ -602,24 +431,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) * Now start flushing things to disk, in the order they appear * on the transaction lists. Data blocks go first. */ - err = 0; - journal_submit_data_buffers(journal, commit_transaction); err = journal_submit_inode_data_buffers(journal, commit_transaction); - if (err) - jbd2_journal_abort(journal, err); - - /* - * Wait for all previously submitted IO to complete if commit - * record is to be written synchronously. - */ - spin_lock(&journal->j_list_lock); - if (!JBD2_HAS_INCOMPAT_FEATURE(journal, - JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) - err = journal_wait_on_locked_list(journal, - commit_transaction); - - spin_unlock(&journal->j_list_lock); - if (err) jbd2_journal_abort(journal, err); @@ -627,16 +439,6 @@ void jbd2_journal_commit_transaction(journal_t *journal) jbd_debug(3, "JBD: commit phase 2\n"); - /* - * If we found any dirty or locked buffers, then we should have - * looped back up to the write_out_data label. If there weren't - * any then journal_clean_data_list should have wiped the list - * clean by now, so check that it is in fact empty. - */ - J_ASSERT (commit_transaction->t_sync_datalist == NULL); - - jbd_debug (3, "JBD: commit phase 3\n"); - /* * Way to go: we have now written out all of the data for a * transaction! Now comes the tricky part: we need to write out @@ -655,6 +457,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) J_ASSERT(commit_transaction->t_nr_buffers <= commit_transaction->t_outstanding_credits); + err = 0; descriptor = NULL; bufs = 0; while (commit_transaction->t_buffers) { @@ -829,13 +632,6 @@ start_journal_io: &cbh, crc32_sum); if (err) __jbd2_journal_abort_hard(journal); - - spin_lock(&journal->j_list_lock); - err = journal_wait_on_locked_list(journal, - commit_transaction); - spin_unlock(&journal->j_list_lock); - if (err) - __jbd2_journal_abort_hard(journal); } /* @@ -860,7 +656,7 @@ start_journal_io: so we incur less scheduling load. */ - jbd_debug(3, "JBD: commit phase 4\n"); + jbd_debug(3, "JBD: commit phase 3\n"); /* * akpm: these are BJ_IO, and j_list_lock is not needed. @@ -919,7 +715,7 @@ wait_for_iobuf: J_ASSERT (commit_transaction->t_shadow_list == NULL); - jbd_debug(3, "JBD: commit phase 5\n"); + jbd_debug(3, "JBD: commit phase 4\n"); /* Here we wait for the revoke record and descriptor record buffers */ wait_for_ctlbuf: @@ -946,7 +742,7 @@ wait_for_iobuf: /* AKPM: bforget here */ } - jbd_debug(3, "JBD: commit phase 6\n"); + jbd_debug(3, "JBD: commit phase 5\n"); if (!JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) { @@ -966,9 +762,8 @@ wait_for_iobuf: transaction can be removed from any checkpoint list it was on before. */ - jbd_debug(3, "JBD: commit phase 7\n"); + jbd_debug(3, "JBD: commit phase 6\n"); - J_ASSERT(commit_transaction->t_sync_datalist == NULL); J_ASSERT(list_empty(&commit_transaction->t_inode_list)); J_ASSERT(commit_transaction->t_buffers == NULL); J_ASSERT(commit_transaction->t_checkpoint_list == NULL); @@ -1090,7 +885,7 @@ restart_loop: /* Done with this transaction! */ - jbd_debug(3, "JBD: commit phase 8\n"); + jbd_debug(3, "JBD: commit phase 7\n"); J_ASSERT(commit_transaction->t_state == T_COMMIT); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 78cf7bd7f604..b26c6d9fe6ae 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -50,7 +50,6 @@ EXPORT_SYMBOL(jbd2_journal_unlock_updates); EXPORT_SYMBOL(jbd2_journal_get_write_access); EXPORT_SYMBOL(jbd2_journal_get_create_access); EXPORT_SYMBOL(jbd2_journal_get_undo_access); -EXPORT_SYMBOL(jbd2_journal_dirty_data); EXPORT_SYMBOL(jbd2_journal_dirty_metadata); EXPORT_SYMBOL(jbd2_journal_release_buffer); EXPORT_SYMBOL(jbd2_journal_forget); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 98b596d23705..4f7cadbb19fa 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -942,183 +942,6 @@ out: return err; } -/** - * int jbd2_journal_dirty_data() - mark a buffer as containing dirty data which - * needs to be flushed before we can commit the - * current transaction. - * @handle: transaction - * @bh: bufferhead to mark - * - * The buffer is placed on the transaction's data list and is marked as - * belonging to the transaction. - * - * Returns error number or 0 on success. - * - * jbd2_journal_dirty_data() can be called via page_launder->ext3_writepage - * by kswapd. - */ -int jbd2_journal_dirty_data(handle_t *handle, struct buffer_head *bh) -{ - journal_t *journal = handle->h_transaction->t_journal; - int need_brelse = 0; - struct journal_head *jh; - - if (is_handle_aborted(handle)) - return 0; - - jh = jbd2_journal_add_journal_head(bh); - JBUFFER_TRACE(jh, "entry"); - - /* - * The buffer could *already* be dirty. Writeout can start - * at any time. - */ - jbd_debug(4, "jh: %p, tid:%d\n", jh, handle->h_transaction->t_tid); - - /* - * What if the buffer is already part of a running transaction? - * - * There are two cases: - * 1) It is part of the current running transaction. Refile it, - * just in case we have allocated it as metadata, deallocated - * it, then reallocated it as data. - * 2) It is part of the previous, still-committing transaction. - * If all we want to do is to guarantee that the buffer will be - * written to disk before this new transaction commits, then - * being sure that the *previous* transaction has this same - * property is sufficient for us! Just leave it on its old - * transaction. - * - * In case (2), the buffer must not already exist as metadata - * --- that would violate write ordering (a transaction is free - * to write its data at any point, even before the previous - * committing transaction has committed). The caller must - * never, ever allow this to happen: there's nothing we can do - * about it in this layer. - */ - jbd_lock_bh_state(bh); - spin_lock(&journal->j_list_lock); - - /* Now that we have bh_state locked, are we really still mapped? */ - if (!buffer_mapped(bh)) { - JBUFFER_TRACE(jh, "unmapped buffer, bailing out"); - goto no_journal; - } - - if (jh->b_transaction) { - JBUFFER_TRACE(jh, "has transaction"); - if (jh->b_transaction != handle->h_transaction) { - JBUFFER_TRACE(jh, "belongs to older transaction"); - J_ASSERT_JH(jh, jh->b_transaction == - journal->j_committing_transaction); - - /* @@@ IS THIS TRUE ? */ - /* - * Not any more. Scenario: someone does a write() - * in data=journal mode. The buffer's transaction has - * moved into commit. Then someone does another - * write() to the file. We do the frozen data copyout - * and set b_next_transaction to point to j_running_t. - * And while we're in that state, someone does a - * writepage() in an attempt to pageout the same area - * of the file via a shared mapping. At present that - * calls jbd2_journal_dirty_data(), and we get right here. - * It may be too late to journal the data. Simply - * falling through to the next test will suffice: the - * data will be dirty and wil be checkpointed. The - * ordering comments in the next comment block still - * apply. - */ - //J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - - /* - * If we're journalling data, and this buffer was - * subject to a write(), it could be metadata, forget - * or shadow against the committing transaction. Now, - * someone has dirtied the same darn page via a mapping - * and it is being writepage()'d. - * We *could* just steal the page from commit, with some - * fancy locking there. Instead, we just skip it - - * don't tie the page's buffers to the new transaction - * at all. - * Implication: if we crash before the writepage() data - * is written into the filesystem, recovery will replay - * the write() data. - */ - if (jh->b_jlist != BJ_None && - jh->b_jlist != BJ_SyncData && - jh->b_jlist != BJ_Locked) { - JBUFFER_TRACE(jh, "Not stealing"); - goto no_journal; - } - - /* - * This buffer may be undergoing writeout in commit. We - * can't return from here and let the caller dirty it - * again because that can cause the write-out loop in - * commit to never terminate. - */ - if (buffer_dirty(bh)) { - get_bh(bh); - spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); - need_brelse = 1; - sync_dirty_buffer(bh); - jbd_lock_bh_state(bh); - spin_lock(&journal->j_list_lock); - /* Since we dropped the lock... */ - if (!buffer_mapped(bh)) { - JBUFFER_TRACE(jh, "buffer got unmapped"); - goto no_journal; - } - /* The buffer may become locked again at any - time if it is redirtied */ - } - - /* journal_clean_data_list() may have got there first */ - if (jh->b_transaction != NULL) { - JBUFFER_TRACE(jh, "unfile from commit"); - __jbd2_journal_temp_unlink_buffer(jh); - /* It still points to the committing - * transaction; move it to this one so - * that the refile assert checks are - * happy. */ - jh->b_transaction = handle->h_transaction; - } - /* The buffer will be refiled below */ - - } - /* - * Special case --- the buffer might actually have been - * allocated and then immediately deallocated in the previous, - * committing transaction, so might still be left on that - * transaction's metadata lists. - */ - if (jh->b_jlist != BJ_SyncData && jh->b_jlist != BJ_Locked) { - JBUFFER_TRACE(jh, "not on correct data list: unfile"); - J_ASSERT_JH(jh, jh->b_jlist != BJ_Shadow); - __jbd2_journal_temp_unlink_buffer(jh); - jh->b_transaction = handle->h_transaction; - JBUFFER_TRACE(jh, "file as data"); - __jbd2_journal_file_buffer(jh, handle->h_transaction, - BJ_SyncData); - } - } else { - JBUFFER_TRACE(jh, "not on a transaction"); - __jbd2_journal_file_buffer(jh, handle->h_transaction, BJ_SyncData); - } -no_journal: - spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); - if (need_brelse) { - BUFFER_TRACE(bh, "brelse"); - __brelse(bh); - } - JBUFFER_TRACE(jh, "exit"); - jbd2_journal_put_journal_head(jh); - return 0; -} - /** * int jbd2_journal_dirty_metadata() - mark a buffer as containing dirty metadata * @handle: transaction to add buffer to. @@ -1541,10 +1364,10 @@ __blist_del_buffer(struct journal_head **list, struct journal_head *jh) * Remove a buffer from the appropriate transaction list. * * Note that this function can *change* the value of - * bh->b_transaction->t_sync_datalist, t_buffers, t_forget, - * t_iobuf_list, t_shadow_list, t_log_list or t_reserved_list. If the caller - * is holding onto a copy of one of thee pointers, it could go bad. - * Generally the caller needs to re-read the pointer from the transaction_t. + * bh->b_transaction->t_buffers, t_forget, t_iobuf_list, t_shadow_list, + * t_log_list or t_reserved_list. If the caller is holding onto a copy of one + * of these pointers, it could go bad. Generally the caller needs to re-read + * the pointer from the transaction_t. * * Called under j_list_lock. The journal may not be locked. */ @@ -1566,9 +1389,6 @@ void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh) switch (jh->b_jlist) { case BJ_None: return; - case BJ_SyncData: - list = &transaction->t_sync_datalist; - break; case BJ_Metadata: transaction->t_nr_buffers--; J_ASSERT_JH(jh, transaction->t_nr_buffers >= 0); @@ -1589,9 +1409,6 @@ void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh) case BJ_Reserved: list = &transaction->t_reserved_list; break; - case BJ_Locked: - list = &transaction->t_locked_list; - break; } __blist_del_buffer(list, jh); @@ -1634,15 +1451,7 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh) goto out; spin_lock(&journal->j_list_lock); - if (jh->b_transaction != NULL && jh->b_cp_transaction == NULL) { - if (jh->b_jlist == BJ_SyncData || jh->b_jlist == BJ_Locked) { - /* A written-back ordered data buffer */ - JBUFFER_TRACE(jh, "release data"); - __jbd2_journal_unfile_buffer(jh); - jbd2_journal_remove_journal_head(bh); - __brelse(bh); - } - } else if (jh->b_cp_transaction != NULL && jh->b_transaction == NULL) { + if (jh->b_cp_transaction != NULL && jh->b_transaction == NULL) { /* written-back checkpointed metadata buffer */ if (jh->b_jlist == BJ_None) { JBUFFER_TRACE(jh, "remove from checkpoint list"); @@ -1878,6 +1687,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) if (!buffer_jbd(bh)) goto zap_buffer_unlocked; + /* OK, we have data buffer in journaled mode */ spin_lock(&journal->j_state_lock); jbd_lock_bh_state(bh); spin_lock(&journal->j_list_lock); @@ -1941,15 +1751,6 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) } } else if (transaction == journal->j_committing_transaction) { JBUFFER_TRACE(jh, "on committing transaction"); - if (jh->b_jlist == BJ_Locked) { - /* - * The buffer is on the committing transaction's locked - * list. We have the buffer locked, so I/O has - * completed. So we can nail the buffer now. - */ - may_free = __dispose_buffer(jh, transaction); - goto zap_buffer; - } /* * If it is committing, we simply cannot touch it. We * can remove it's next_transaction pointer from the @@ -2082,9 +1883,6 @@ void __jbd2_journal_file_buffer(struct journal_head *jh, J_ASSERT_JH(jh, !jh->b_committed_data); J_ASSERT_JH(jh, !jh->b_frozen_data); return; - case BJ_SyncData: - list = &transaction->t_sync_datalist; - break; case BJ_Metadata: transaction->t_nr_buffers++; list = &transaction->t_buffers; @@ -2104,9 +1902,6 @@ void __jbd2_journal_file_buffer(struct journal_head *jh, case BJ_Reserved: list = &transaction->t_reserved_list; break; - case BJ_Locked: - list = &transaction->t_locked_list; - break; } __blist_add_buffer(list, jh); diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 622c3d8ca4ed..3dd209007098 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -542,24 +542,12 @@ struct transaction_s */ struct journal_head *t_reserved_list; - /* - * Doubly-linked circular list of all buffers under writeout during - * commit [j_list_lock] - */ - struct journal_head *t_locked_list; - /* * Doubly-linked circular list of all metadata buffers owned by this * transaction [j_list_lock] */ struct journal_head *t_buffers; - /* - * Doubly-linked circular list of all data buffers still to be - * flushed before this transaction can be committed [j_list_lock] - */ - struct journal_head *t_sync_datalist; - /* * Doubly-linked circular list of all forget buffers (superseded * buffers which we can un-checkpoint once this transaction commits) @@ -1044,7 +1032,6 @@ extern int jbd2_journal_extend (handle_t *, int nblocks); extern int jbd2_journal_get_write_access(handle_t *, struct buffer_head *); extern int jbd2_journal_get_create_access (handle_t *, struct buffer_head *); extern int jbd2_journal_get_undo_access(handle_t *, struct buffer_head *); -extern int jbd2_journal_dirty_data (handle_t *, struct buffer_head *); extern int jbd2_journal_dirty_metadata (handle_t *, struct buffer_head *); extern void jbd2_journal_release_buffer (handle_t *, struct buffer_head *); extern int jbd2_journal_forget (handle_t *, struct buffer_head *); @@ -1223,15 +1210,13 @@ static inline int jbd_space_needed(journal_t *journal) /* journaling buffer types */ #define BJ_None 0 /* Not journaled */ -#define BJ_SyncData 1 /* Normal data: flush before commit */ -#define BJ_Metadata 2 /* Normal journaled metadata */ -#define BJ_Forget 3 /* Buffer superseded by this transaction */ -#define BJ_IO 4 /* Buffer is for temporary IO use */ -#define BJ_Shadow 5 /* Buffer contents being shadowed to the log */ -#define BJ_LogCtl 6 /* Buffer contains log descriptors */ -#define BJ_Reserved 7 /* Buffer is reserved for access by journal */ -#define BJ_Locked 8 /* Locked for I/O during commit */ -#define BJ_Types 9 +#define BJ_Metadata 1 /* Normal journaled metadata */ +#define BJ_Forget 2 /* Buffer superseded by this transaction */ +#define BJ_IO 3 /* Buffer is for temporary IO use */ +#define BJ_Shadow 4 /* Buffer contents being shadowed to the log */ +#define BJ_LogCtl 5 /* Buffer contains log descriptors */ +#define BJ_Reserved 6 /* Buffer is reserved for access by journal */ +#define BJ_Types 7 extern int jbd_blocks_per_page(struct inode *inode); -- cgit v1.2.3 From 29a814d2ee0e43c2980f33f91c1311ec06c0aa35 Mon Sep 17 00:00:00 2001 From: Alex Tomas Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: vfs: add hooks for ext4's delayed allocation support Export mpage_bio_submit() and __mpage_writepage() for the benefit of ext4's delayed allocation support. Also change __block_write_full_page so that if buffers that have the BH_Delay flag set it will call get_block() to get the physical block allocated, just as in the !BH_Mapped case. Signed-off-by: Alex Tomas Signed-off-by: "Theodore Ts'o" --- fs/buffer.c | 7 +++++-- fs/mpage.c | 14 +++++--------- include/linux/mpage.h | 10 ++++++++++ 3 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index f4b033237a02..5fa1512cd9a2 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1691,11 +1691,13 @@ static int __block_write_full_page(struct inode *inode, struct page *page, */ clear_buffer_dirty(bh); set_buffer_uptodate(bh); - } else if (!buffer_mapped(bh) && buffer_dirty(bh)) { + } else if ((!buffer_mapped(bh) || buffer_delay(bh)) && + buffer_dirty(bh)) { WARN_ON(bh->b_size != blocksize); err = get_block(inode, block, bh, 1); if (err) goto recover; + clear_buffer_delay(bh); if (buffer_new(bh)) { /* blockdev mappings never come here */ clear_buffer_new(bh); @@ -1774,7 +1776,8 @@ recover: bh = head; /* Recovery: lock and submit the mapped buffers */ do { - if (buffer_mapped(bh) && buffer_dirty(bh)) { + if (buffer_mapped(bh) && buffer_dirty(bh) && + !buffer_delay(bh)) { lock_buffer(bh); mark_buffer_async_write(bh); } else { diff --git a/fs/mpage.c b/fs/mpage.c index 235e4d3873a8..dbcc7af76a15 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -82,7 +82,7 @@ static void mpage_end_io_write(struct bio *bio, int err) bio_put(bio); } -static struct bio *mpage_bio_submit(int rw, struct bio *bio) +struct bio *mpage_bio_submit(int rw, struct bio *bio) { bio->bi_end_io = mpage_end_io_read; if (rw == WRITE) @@ -90,6 +90,7 @@ static struct bio *mpage_bio_submit(int rw, struct bio *bio) submit_bio(rw, bio); return NULL; } +EXPORT_SYMBOL(mpage_bio_submit); static struct bio * mpage_alloc(struct block_device *bdev, @@ -435,15 +436,9 @@ EXPORT_SYMBOL(mpage_readpage); * written, so it can intelligently allocate a suitably-sized BIO. For now, * just allocate full-size (16-page) BIOs. */ -struct mpage_data { - struct bio *bio; - sector_t last_block_in_bio; - get_block_t *get_block; - unsigned use_writepage; -}; -static int __mpage_writepage(struct page *page, struct writeback_control *wbc, - void *data) +int __mpage_writepage(struct page *page, struct writeback_control *wbc, + void *data) { struct mpage_data *mpd = data; struct bio *bio = mpd->bio; @@ -651,6 +646,7 @@ out: mpd->bio = bio; return ret; } +EXPORT_SYMBOL(__mpage_writepage); /** * mpage_writepages - walk the list of dirty pages of the given address space & writepage() all of them diff --git a/include/linux/mpage.h b/include/linux/mpage.h index 068a0c9946af..5c42821da2d1 100644 --- a/include/linux/mpage.h +++ b/include/linux/mpage.h @@ -11,11 +11,21 @@ */ #ifdef CONFIG_BLOCK +struct mpage_data { + struct bio *bio; + sector_t last_block_in_bio; + get_block_t *get_block; + unsigned use_writepage; +}; + struct writeback_control; +struct bio *mpage_bio_submit(int rw, struct bio *bio); int mpage_readpages(struct address_space *mapping, struct list_head *pages, unsigned nr_pages, get_block_t get_block); int mpage_readpage(struct page *page, get_block_t get_block); +int __mpage_writepage(struct page *page, struct writeback_control *wbc, + void *data); int mpage_writepages(struct address_space *mapping, struct writeback_control *wbc, get_block_t get_block); int mpage_writepage(struct page *page, get_block_t *get_block, -- cgit v1.2.3 From e8ced39d5e8911c662d4d69a342b9d053eaaac4e Mon Sep 17 00:00:00 2001 From: Mingming Cao Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: percpu_counter: new function percpu_counter_sum_and_set Delayed allocation need to check free blocks at every write time. percpu_counter_read_positive() is not quit accurate. delayed allocation need a more accurate accounting, but using percpu_counter_sum_positive() is frequently is quite expensive. This patch added a new function to update center counter when sum per-cpu counter, to increase the accurate rate for next percpu_counter_read() and require less calling expensive percpu_counter_sum(). Signed-off-by: Mingming Cao Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 2 +- include/linux/percpu_counter.h | 12 +++++++++--- lib/percpu_counter.c | 7 ++++++- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 25f63d8c1b3d..6369bacf0dcb 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -1621,7 +1621,7 @@ ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi, #ifdef CONFIG_SMP if (free_blocks - root_blocks < FBC_BATCH) free_blocks = - percpu_counter_sum_positive(&sbi->s_freeblocks_counter); + percpu_counter_sum_and_set(&sbi->s_freeblocks_counter); #endif if (free_blocks - root_blocks < nblocks) return free_blocks - root_blocks; diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 9007ccdfc112..208388835357 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -35,7 +35,7 @@ int percpu_counter_init_irq(struct percpu_counter *fbc, s64 amount); void percpu_counter_destroy(struct percpu_counter *fbc); void percpu_counter_set(struct percpu_counter *fbc, s64 amount); void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch); -s64 __percpu_counter_sum(struct percpu_counter *fbc); +s64 __percpu_counter_sum(struct percpu_counter *fbc, int set); static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount) { @@ -44,13 +44,19 @@ static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount) static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc) { - s64 ret = __percpu_counter_sum(fbc); + s64 ret = __percpu_counter_sum(fbc, 0); return ret < 0 ? 0 : ret; } +static inline s64 percpu_counter_sum_and_set(struct percpu_counter *fbc) +{ + return __percpu_counter_sum(fbc, 1); +} + + static inline s64 percpu_counter_sum(struct percpu_counter *fbc) { - return __percpu_counter_sum(fbc); + return __percpu_counter_sum(fbc, 0); } static inline s64 percpu_counter_read(struct percpu_counter *fbc) diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 119174494cb5..4a8ba4bf5f6f 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -52,7 +52,7 @@ EXPORT_SYMBOL(__percpu_counter_add); * Add up all the per-cpu counts, return the result. This is a more accurate * but much slower version of percpu_counter_read_positive() */ -s64 __percpu_counter_sum(struct percpu_counter *fbc) +s64 __percpu_counter_sum(struct percpu_counter *fbc, int set) { s64 ret; int cpu; @@ -62,7 +62,12 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc) for_each_online_cpu(cpu) { s32 *pcount = per_cpu_ptr(fbc->counters, cpu); ret += *pcount; + if (set) + *pcount = 0; } + if (set) + fbc->count = ret; + spin_unlock(&fbc->lock); return ret; } -- cgit v1.2.3 From 06d6cf6959d22037fcec598f4f954db5db3d7356 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Fri, 11 Jul 2008 19:27:31 -0400 Subject: mm: Add range_cont mode for writeback Filesystems like ext4 needs to start a new transaction in the writepages for block allocation. This happens with delayed allocation and there is limit to how many credits we can request from the journal layer. So we call write_cache_pages multiple times with wbc->nr_to_write set to the maximum possible value limitted by the max journal credits available. Add a new mode to writeback that enables us to handle this behaviour. In the new mode we update the wbc->range_start to point to the new offset to be written. Next call to call to write_cache_pages will start writeout from specified range_start offset. In the new mode we also limit writing to the specified wbc->range_end. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Mingming Cao Acked-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- include/linux/writeback.h | 1 + mm/page-writeback.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/writeback.h b/include/linux/writeback.h index f462439cc288..0d8573e6b9ec 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -63,6 +63,7 @@ struct writeback_control { unsigned for_writepages:1; /* This is a writepages() call */ unsigned range_cyclic:1; /* range_start is cyclic */ unsigned more_io:1; /* more io to be dispatched */ + unsigned range_cont:1; }; /* diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 789b6adbef37..ded57d528060 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -956,6 +956,9 @@ retry: } if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = index; + + if (wbc->range_cont) + wbc->range_start = index << PAGE_CACHE_SHIFT; return ret; } EXPORT_SYMBOL(write_cache_pages); -- cgit v1.2.3 From 1886e8a90a580f3ad343f2065c84c1b9e1dac9ef Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:37 -0700 Subject: x64, x2apic/intr-remap: code re-structuring, to be used by both DMA and Interrupt remapping Allocate the iommu during the parse of DMA remapping hardware definition structures. And also, introduce routines for device scope initialization which will be explicitly called during dma-remapping initialization. These will be used for enabling interrupt remapping separately from the existing DMA-remapping enabling sequence. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 89 ++++++++++++++++++++++++++++++++++++++--------- drivers/pci/intel-iommu.c | 10 +++--- drivers/pci/intel-iommu.h | 2 +- include/linux/dmar.h | 10 +++++- 4 files changed, 88 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 1a59423a8eda..158bc5bfcf75 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -174,19 +174,37 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) struct acpi_dmar_hardware_unit *drhd; struct dmar_drhd_unit *dmaru; int ret = 0; - static int include_all; dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); if (!dmaru) return -ENOMEM; + dmaru->hdr = header; drhd = (struct acpi_dmar_hardware_unit *)header; dmaru->reg_base_addr = drhd->address; dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ + ret = alloc_iommu(dmaru); + if (ret) { + kfree(dmaru); + return ret; + } + dmar_register_drhd_unit(dmaru); + return 0; +} + +static int __init +dmar_parse_dev(struct dmar_drhd_unit *dmaru) +{ + struct acpi_dmar_hardware_unit *drhd; + static int include_all; + int ret; + + drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr; + if (!dmaru->include_all) ret = dmar_parse_dev_scope((void *)(drhd + 1), - ((void *)drhd) + header->length, + ((void *)drhd) + drhd->header.length, &dmaru->devices_cnt, &dmaru->devices, drhd->segment); else { @@ -199,10 +217,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) include_all = 1; } - if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) + if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) { + list_del(&dmaru->list); kfree(dmaru); - else - dmar_register_drhd_unit(dmaru); + } return ret; } @@ -211,23 +229,35 @@ dmar_parse_one_rmrr(struct acpi_dmar_header *header) { struct acpi_dmar_reserved_memory *rmrr; struct dmar_rmrr_unit *rmrru; - int ret = 0; rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL); if (!rmrru) return -ENOMEM; + rmrru->hdr = header; rmrr = (struct acpi_dmar_reserved_memory *)header; rmrru->base_address = rmrr->base_address; rmrru->end_address = rmrr->end_address; + + dmar_register_rmrr_unit(rmrru); + return 0; +} + +static int __init +rmrr_parse_dev(struct dmar_rmrr_unit *rmrru) +{ + struct acpi_dmar_reserved_memory *rmrr; + int ret; + + rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr; ret = dmar_parse_dev_scope((void *)(rmrr + 1), - ((void *)rmrr) + header->length, + ((void *)rmrr) + rmrr->header.length, &rmrru->devices_cnt, &rmrru->devices, rmrr->segment); - if (ret || (rmrru->devices_cnt == 0)) + if (ret || (rmrru->devices_cnt == 0)) { + list_del(&rmrru->list); kfree(rmrru); - else - dmar_register_rmrr_unit(rmrru); + } return ret; } @@ -333,15 +363,42 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) return NULL; } +int __init dmar_dev_scope_init(void) +{ + struct dmar_drhd_unit *drhd; + struct dmar_rmrr_unit *rmrr; + int ret = -ENODEV; + + for_each_drhd_unit(drhd) { + ret = dmar_parse_dev(drhd); + if (ret) + return ret; + } + + for_each_rmrr_units(rmrr) { + ret = rmrr_parse_dev(rmrr); + if (ret) + return ret; + } + + return ret; +} + int __init dmar_table_init(void) { - + static int dmar_table_initialized; int ret; + if (dmar_table_initialized) + return 0; + + dmar_table_initialized = 1; + ret = parse_dmar_table(); if (ret) { - printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); + if (ret != -ENODEV) + printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); return ret; } @@ -377,7 +434,7 @@ int __init early_dmar_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } -struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) +int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; int map_size; @@ -386,7 +443,7 @@ struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); if (!iommu) - return NULL; + return -ENOMEM; iommu->seq_id = iommu_allocated++; @@ -419,10 +476,10 @@ struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) spin_lock_init(&iommu->register_lock); drhd->iommu = iommu; - return iommu; + return 0; error: kfree(iommu); - return NULL; + return -1; } void free_iommu(struct intel_iommu *iommu) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4d59a6a1f4dd..218a1f357b4d 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1665,11 +1665,8 @@ int __init init_dmars(void) for_each_drhd_unit(drhd) { if (drhd->ignored) continue; - iommu = alloc_iommu(drhd); - if (!iommu) { - ret = -ENOMEM; - goto error; - } + + iommu = drhd->iommu; ret = iommu_init_domains(iommu); if (ret) @@ -2324,6 +2321,9 @@ int __init intel_iommu_init(void) if (dmar_table_init()) return -ENODEV; + if (dmar_dev_scope_init()) + return -ENODEV; + iommu_init_mempool(); dmar_init_reserved_ranges(); diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 75c63f65b3f5..371e3b9caf32 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -199,7 +199,7 @@ struct intel_iommu { extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); -extern struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd); +extern int alloc_iommu(struct dmar_drhd_unit *drhd); extern void free_iommu(struct intel_iommu *iommu); #endif diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 56c73b847551..3ab07e425583 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -46,12 +46,14 @@ extern int intel_iommu_init(void); extern int dmar_table_init(void); extern int early_dmar_detect(void); +extern int dmar_dev_scope_init(void); extern struct list_head dmar_drhd_units; extern struct list_head dmar_rmrr_units; struct dmar_drhd_unit { struct list_head list; /* list of drhd units */ + struct acpi_dmar_header *hdr; /* ACPI header */ u64 reg_base_addr; /* register base address*/ struct pci_dev **devices; /* target device array */ int devices_cnt; /* target device count */ @@ -62,6 +64,7 @@ struct dmar_drhd_unit { struct dmar_rmrr_unit { struct list_head list; /* list of rmrr units */ + struct acpi_dmar_header *hdr; /* ACPI header */ u64 base_address; /* reserved base address*/ u64 end_address; /* reserved end address */ struct pci_dev **devices; /* target devices */ @@ -72,6 +75,8 @@ struct dmar_rmrr_unit { list_for_each_entry(drhd, &dmar_drhd_units, list) #define for_each_rmrr_units(rmrr) \ list_for_each_entry(rmrr, &dmar_rmrr_units, list) + +extern int alloc_iommu(struct dmar_drhd_unit *); #else static inline void detect_intel_iommu(void) { @@ -81,6 +86,9 @@ static inline int intel_iommu_init(void) { return -ENODEV; } - +static inline int dmar_table_init(void) +{ + return -ENODEV; +} #endif /* !CONFIG_DMAR */ #endif /* __DMAR_H__ */ -- cgit v1.2.3 From ad3ad3f6a2caebf56869b83b69e23eb9fa5e0ab6 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:40 -0700 Subject: x64, x2apic/intr-remap: parse ioapic scope under vt-d structures Parse the vt-d device scope structures to find the mapping between IO-APICs and the interrupt remapping hardware units. This will be used later for enabling Interrupt-remapping for IOAPIC devices. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/Makefile | 2 ++ drivers/pci/dmar.c | 3 ++ drivers/pci/intel-iommu.h | 2 ++ drivers/pci/intr_remapping.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/intr_remapping.h | 6 ++++ include/linux/dmar.h | 1 + 6 files changed, 84 insertions(+) create mode 100644 drivers/pci/intr_remapping.c create mode 100644 drivers/pci/intr_remapping.h (limited to 'include/linux') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4d1ce2e7361e..1c409c7b0d56 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_HT_IRQ) += htirq.o # Build Intel IOMMU support obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o +obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o + # # Some architectures use the generic PCI setup functions # diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 903f1701edff..127764cfbe27 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -423,6 +423,9 @@ int __init dmar_table_init(void) printk(KERN_INFO PREFIX "No RMRR found\n"); #endif +#ifdef CONFIG_INTR_REMAP + parse_ioapics_under_ir(); +#endif return 0; } diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 371e3b9caf32..eb167e39b464 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -114,6 +114,8 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define ecap_max_iotlb_offset(e) \ (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16) #define ecap_coherent(e) ((e) & 0x1) +#define ecap_eim_support(e) ((e >> 4) & 0x1) +#define ecap_ir_support(e) ((e >> 3) & 0x1) /* IOTLB_REG */ diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c new file mode 100644 index 000000000000..a80b87921c68 --- /dev/null +++ b/drivers/pci/intr_remapping.c @@ -0,0 +1,70 @@ +#include +#include +#include "intel-iommu.h" +#include "intr_remapping.h" + +static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; +static int ir_ioapic_num; + +static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, + struct intel_iommu *iommu) +{ + struct acpi_dmar_hardware_unit *drhd; + struct acpi_dmar_device_scope *scope; + void *start, *end; + + drhd = (struct acpi_dmar_hardware_unit *)header; + + start = (void *)(drhd + 1); + end = ((void *)drhd) + header->length; + + while (start < end) { + scope = start; + if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) { + if (ir_ioapic_num == MAX_IO_APICS) { + printk(KERN_WARNING "Exceeded Max IO APICS\n"); + return -1; + } + + printk(KERN_INFO "IOAPIC id %d under DRHD base" + " 0x%Lx\n", scope->enumeration_id, + drhd->address); + + ir_ioapic[ir_ioapic_num].iommu = iommu; + ir_ioapic[ir_ioapic_num].id = scope->enumeration_id; + ir_ioapic_num++; + } + start += scope->length; + } + + return 0; +} + +/* + * Finds the assocaition between IOAPIC's and its Interrupt-remapping + * hardware unit. + */ +int __init parse_ioapics_under_ir(void) +{ + struct dmar_drhd_unit *drhd; + int ir_supported = 0; + + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu = drhd->iommu; + + if (ecap_ir_support(iommu->ecap)) { + if (ir_parse_ioapic_scope(drhd->hdr, iommu)) + return -1; + + ir_supported = 1; + } + } + + if (ir_supported && ir_ioapic_num != nr_ioapics) { + printk(KERN_WARNING + "Not all IO-APIC's listed under remapping hardware\n"); + return -1; + } + + return ir_supported; +} diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h new file mode 100644 index 000000000000..c4a40b2f33fa --- /dev/null +++ b/drivers/pci/intr_remapping.h @@ -0,0 +1,6 @@ +#include "intel-iommu.h" + +struct ioapic_scope { + struct intel_iommu *iommu; + unsigned int id; +}; diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 3ab07e425583..c4e96eb29617 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -47,6 +47,7 @@ extern int intel_iommu_init(void); extern int dmar_table_init(void); extern int early_dmar_detect(void); extern int dmar_dev_scope_init(void); +extern int parse_ioapics_under_ir(void); extern struct list_head dmar_drhd_units; extern struct list_head dmar_rmrr_units; -- cgit v1.2.3 From 2ae21010694e56461a63bfc80e960090ce0a5ed9 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:43 -0700 Subject: x64, x2apic/intr-remap: Interrupt remapping infrastructure Interrupt remapping (part of Intel Virtualization Tech for directed I/O) infrastructure. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dma_remapping.h | 2 + drivers/pci/dmar.c | 16 +++++ drivers/pci/intel-iommu.c | 21 +++---- drivers/pci/intel-iommu.h | 24 +++++++- drivers/pci/intr_remapping.c | 137 +++++++++++++++++++++++++++++++++++++++++++ drivers/pci/intr_remapping.h | 2 + include/linux/dmar.h | 120 ++++++++++++++++++++++++++----------- 7 files changed, 272 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h index 05aac8ef96c7..bff5c65f81dc 100644 --- a/drivers/pci/dma_remapping.h +++ b/drivers/pci/dma_remapping.h @@ -145,6 +145,8 @@ struct device_domain_info { extern int init_dmars(void); extern void free_dmar_iommu(struct intel_iommu *iommu); +extern int dmar_disabled; + #ifndef CONFIG_DMAR_GFX_WA static inline void iommu_prepare_gfx_mapping(void) { diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index aba151ca6d26..23a119e6485e 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -449,6 +449,22 @@ int __init early_dmar_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } +void __init detect_intel_iommu(void) +{ + int ret; + + ret = early_dmar_detect(); + +#ifdef CONFIG_DMAR + { + if (ret && !no_iommu && !iommu_detected && !swiotlb && + !dmar_disabled) + iommu_detected = 1; + } +#endif +} + + int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 347bf2e47168..ffccf2341b98 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -76,7 +76,7 @@ static long list_size; static void domain_remove_dev_info(struct dmar_domain *domain); -static int dmar_disabled; +int dmar_disabled; static int __initdata dmar_map_gfx = 1; static int dmar_forcedac; static int intel_iommu_strict; @@ -2238,15 +2238,6 @@ static void __init iommu_exit_mempool(void) } -void __init detect_intel_iommu(void) -{ - if (swiotlb || no_iommu || iommu_detected || dmar_disabled) - return; - if (early_dmar_detect()) { - iommu_detected = 1; - } -} - static void __init init_no_remapping_devices(void) { struct dmar_drhd_unit *drhd; @@ -2293,15 +2284,19 @@ int __init intel_iommu_init(void) { int ret = 0; - if (no_iommu || swiotlb || dmar_disabled) - return -ENODEV; - if (dmar_table_init()) return -ENODEV; if (dmar_dev_scope_init()) return -ENODEV; + /* + * Check the need for DMA-remapping initialization now. + * Above initialization will also be used by Interrupt-remapping. + */ + if (no_iommu || swiotlb || dmar_disabled) + return -ENODEV; + iommu_init_mempool(); dmar_init_reserved_ranges(); diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 2983ce895353..a81a74e2bd9e 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -56,6 +56,7 @@ #define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */ #define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */ #define DMAR_ICS_REG 0x98 /* Invalidation complete status register */ +#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */ #define OFFSET_STRIDE (9) /* @@ -157,16 +158,20 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_GCMD_SRTP (((u32)1) << 30) #define DMA_GCMD_SFL (((u32)1) << 29) #define DMA_GCMD_EAFL (((u32)1) << 28) -#define DMA_GCMD_QIE (((u32)1) << 26) #define DMA_GCMD_WBF (((u32)1) << 27) +#define DMA_GCMD_QIE (((u32)1) << 26) +#define DMA_GCMD_SIRTP (((u32)1) << 24) +#define DMA_GCMD_IRE (((u32) 1) << 25) /* GSTS_REG */ #define DMA_GSTS_TES (((u32)1) << 31) #define DMA_GSTS_RTPS (((u32)1) << 30) #define DMA_GSTS_FLS (((u32)1) << 29) #define DMA_GSTS_AFLS (((u32)1) << 28) -#define DMA_GSTS_QIES (((u32)1) << 26) #define DMA_GSTS_WBFS (((u32)1) << 27) +#define DMA_GSTS_QIES (((u32)1) << 26) +#define DMA_GSTS_IRTPS (((u32)1) << 24) +#define DMA_GSTS_IRES (((u32)1) << 25) /* CCMD_REG */ #define DMA_CCMD_ICC (((u64)1) << 63) @@ -245,6 +250,16 @@ struct q_inval { int free_cnt; }; +#ifdef CONFIG_INTR_REMAP +/* 1MB - maximum possible interrupt remapping table size */ +#define INTR_REMAP_PAGE_ORDER 8 +#define INTR_REMAP_TABLE_REG_SIZE 0xf + +struct ir_table { + struct irte *base; +}; +#endif + struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ u64 cap; @@ -266,6 +281,9 @@ struct intel_iommu { struct sys_device sysdev; #endif struct q_inval *qi; /* Queued invalidation info */ +#ifdef CONFIG_INTR_REMAP + struct ir_table *ir_table; /* Interrupt remapping info */ +#endif }; static inline void __iommu_flush_cache( @@ -279,5 +297,7 @@ extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); extern int alloc_iommu(struct dmar_drhd_unit *drhd); extern void free_iommu(struct intel_iommu *iommu); +extern int dmar_enable_qi(struct intel_iommu *iommu); +extern void qi_global_iec(struct intel_iommu *iommu); #endif diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index a80b87921c68..3d10cdc90314 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -1,10 +1,147 @@ #include +#include +#include +#include #include #include "intel-iommu.h" #include "intr_remapping.h" static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static int ir_ioapic_num; +int intr_remapping_enabled; + +static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) +{ + u64 addr; + u32 cmd, sts; + unsigned long flags; + + addr = virt_to_phys((void *)iommu->ir_table->base); + + spin_lock_irqsave(&iommu->register_lock, flags); + + dmar_writeq(iommu->reg + DMAR_IRTA_REG, + (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE); + + /* Set interrupt-remapping table pointer */ + cmd = iommu->gcmd | DMA_GCMD_SIRTP; + writel(cmd, iommu->reg + DMAR_GCMD_REG); + + IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, + readl, (sts & DMA_GSTS_IRTPS), sts); + spin_unlock_irqrestore(&iommu->register_lock, flags); + + /* + * global invalidation of interrupt entry cache before enabling + * interrupt-remapping. + */ + qi_global_iec(iommu); + + spin_lock_irqsave(&iommu->register_lock, flags); + + /* Enable interrupt-remapping */ + cmd = iommu->gcmd | DMA_GCMD_IRE; + iommu->gcmd |= DMA_GCMD_IRE; + writel(cmd, iommu->reg + DMAR_GCMD_REG); + + IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, + readl, (sts & DMA_GSTS_IRES), sts); + + spin_unlock_irqrestore(&iommu->register_lock, flags); +} + + +static int setup_intr_remapping(struct intel_iommu *iommu, int mode) +{ + struct ir_table *ir_table; + struct page *pages; + + ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table), + GFP_KERNEL); + + if (!iommu->ir_table) + return -ENOMEM; + + pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER); + + if (!pages) { + printk(KERN_ERR "failed to allocate pages of order %d\n", + INTR_REMAP_PAGE_ORDER); + kfree(iommu->ir_table); + return -ENOMEM; + } + + ir_table->base = page_address(pages); + + iommu_set_intr_remapping(iommu, mode); + return 0; +} + +int __init enable_intr_remapping(int eim) +{ + struct dmar_drhd_unit *drhd; + int setup = 0; + + /* + * check for the Interrupt-remapping support + */ + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu = drhd->iommu; + + if (!ecap_ir_support(iommu->ecap)) + continue; + + if (eim && !ecap_eim_support(iommu->ecap)) { + printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, " + " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap); + return -1; + } + } + + /* + * Enable queued invalidation for all the DRHD's. + */ + for_each_drhd_unit(drhd) { + int ret; + struct intel_iommu *iommu = drhd->iommu; + ret = dmar_enable_qi(iommu); + + if (ret) { + printk(KERN_ERR "DRHD %Lx: failed to enable queued, " + " invalidation, ecap %Lx, ret %d\n", + drhd->reg_base_addr, iommu->ecap, ret); + return -1; + } + } + + /* + * Setup Interrupt-remapping for all the DRHD's now. + */ + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu = drhd->iommu; + + if (!ecap_ir_support(iommu->ecap)) + continue; + + if (setup_intr_remapping(iommu, eim)) + goto error; + + setup = 1; + } + + if (!setup) + goto error; + + intr_remapping_enabled = 1; + + return 0; + +error: + /* + * handle error condition gracefully here! + */ + return -1; +} static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, struct intel_iommu *iommu) diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h index c4a40b2f33fa..05f2635bbe4e 100644 --- a/drivers/pci/intr_remapping.h +++ b/drivers/pci/intr_remapping.h @@ -4,3 +4,5 @@ struct ioapic_scope { struct intel_iommu *iommu; unsigned int id; }; + +#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) diff --git a/include/linux/dmar.h b/include/linux/dmar.h index c4e96eb29617..8a0238dd2c11 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -25,9 +25,85 @@ #include #include -#ifdef CONFIG_DMAR +#if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP) struct intel_iommu; +struct dmar_drhd_unit { + struct list_head list; /* list of drhd units */ + struct acpi_dmar_header *hdr; /* ACPI header */ + u64 reg_base_addr; /* register base address*/ + struct pci_dev **devices; /* target device array */ + int devices_cnt; /* target device count */ + u8 ignored:1; /* ignore drhd */ + u8 include_all:1; + struct intel_iommu *iommu; +}; + +extern struct list_head dmar_drhd_units; + +#define for_each_drhd_unit(drhd) \ + list_for_each_entry(drhd, &dmar_drhd_units, list) + +extern int dmar_table_init(void); +extern int early_dmar_detect(void); +extern int dmar_dev_scope_init(void); + +/* Intel IOMMU detection */ +extern void detect_intel_iommu(void); + + +extern int parse_ioapics_under_ir(void); +extern int alloc_iommu(struct dmar_drhd_unit *); +#else +static inline void detect_intel_iommu(void) +{ + return; +} + +static inline int dmar_table_init(void) +{ + return -ENODEV; +} +#endif /* !CONFIG_DMAR && !CONFIG_INTR_REMAP */ + +#ifdef CONFIG_INTR_REMAP +extern int intr_remapping_enabled; +extern int enable_intr_remapping(int); + +struct irte { + union { + struct { + __u64 present : 1, + fpd : 1, + dst_mode : 1, + redir_hint : 1, + trigger_mode : 1, + dlvry_mode : 3, + avail : 4, + __reserved_1 : 4, + vector : 8, + __reserved_2 : 8, + dest_id : 32; + }; + __u64 low; + }; + + union { + struct { + __u64 sid : 16, + sq : 2, + svt : 2, + __reserved_3 : 44; + }; + __u64 high; + }; +}; +#else +#define enable_intr_remapping(mode) (-1) +#define intr_remapping_enabled (0) +#endif + +#ifdef CONFIG_DMAR extern const char *dmar_get_fault_reason(u8 fault_reason); /* Can't use the common MSI interrupt functions @@ -40,29 +116,8 @@ extern void dmar_msi_write(int irq, struct msi_msg *msg); extern int dmar_set_interrupt(struct intel_iommu *iommu); extern int arch_setup_dmar_msi(unsigned int irq); -/* Intel IOMMU detection and initialization functions */ -extern void detect_intel_iommu(void); -extern int intel_iommu_init(void); - -extern int dmar_table_init(void); -extern int early_dmar_detect(void); -extern int dmar_dev_scope_init(void); -extern int parse_ioapics_under_ir(void); - -extern struct list_head dmar_drhd_units; +extern int iommu_detected, no_iommu; extern struct list_head dmar_rmrr_units; - -struct dmar_drhd_unit { - struct list_head list; /* list of drhd units */ - struct acpi_dmar_header *hdr; /* ACPI header */ - u64 reg_base_addr; /* register base address*/ - struct pci_dev **devices; /* target device array */ - int devices_cnt; /* target device count */ - u8 ignored:1; /* ignore drhd */ - u8 include_all:1; - struct intel_iommu *iommu; -}; - struct dmar_rmrr_unit { struct list_head list; /* list of rmrr units */ struct acpi_dmar_header *hdr; /* ACPI header */ @@ -72,24 +127,19 @@ struct dmar_rmrr_unit { int devices_cnt; /* target device count */ }; -#define for_each_drhd_unit(drhd) \ - list_for_each_entry(drhd, &dmar_drhd_units, list) #define for_each_rmrr_units(rmrr) \ list_for_each_entry(rmrr, &dmar_rmrr_units, list) - -extern int alloc_iommu(struct dmar_drhd_unit *); +/* Intel DMAR initialization functions */ +extern int intel_iommu_init(void); +extern int dmar_disabled; #else -static inline void detect_intel_iommu(void) -{ - return; -} static inline int intel_iommu_init(void) { +#ifdef CONFIG_INTR_REMAP + return dmar_dev_scope_init(); +#else return -ENODEV; -} -static inline int dmar_table_init(void) -{ - return -ENODEV; +#endif } #endif /* !CONFIG_DMAR */ #endif /* __DMAR_H__ */ -- cgit v1.2.3 From b6fcb33ad6c05f152a672f7c96c1fab006527b80 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:44 -0700 Subject: x64, x2apic/intr-remap: routines managing Interrupt remapping table entries. Routines handling the management of interrupt remapping table entries. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/intel-iommu.h | 4 + drivers/pci/intr_remapping.c | 243 +++++++++++++++++++++++++++++++++++++++++++ include/linux/dmar.h | 12 +++ 3 files changed, 259 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index a81a74e2bd9e..2142c01e0143 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -123,6 +123,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define ecap_qis(e) ((e) & 0x2) #define ecap_eim_support(e) ((e >> 4) & 0x1) #define ecap_ir_support(e) ((e >> 3) & 0x1) +#define ecap_max_handle_mask(e) ((e >> 20) & 0xf) /* IOTLB_REG */ @@ -255,6 +256,8 @@ struct q_inval { #define INTR_REMAP_PAGE_ORDER 8 #define INTR_REMAP_TABLE_REG_SIZE 0xf +#define INTR_REMAP_TABLE_ENTRIES 65536 + struct ir_table { struct irte *base; }; @@ -300,4 +303,5 @@ extern void free_iommu(struct intel_iommu *iommu); extern int dmar_enable_qi(struct intel_iommu *iommu); extern void qi_global_iec(struct intel_iommu *iommu); +extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); #endif diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 3d10cdc90314..bddb4b19b6c7 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "intel-iommu.h" #include "intr_remapping.h" @@ -10,6 +11,248 @@ static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static int ir_ioapic_num; int intr_remapping_enabled; +static struct { + struct intel_iommu *iommu; + u16 irte_index; + u16 sub_handle; + u8 irte_mask; +} irq_2_iommu[NR_IRQS]; + +static DEFINE_SPINLOCK(irq_2_ir_lock); + +int irq_remapped(int irq) +{ + if (irq > NR_IRQS) + return 0; + + if (!irq_2_iommu[irq].iommu) + return 0; + + return 1; +} + +int get_irte(int irq, struct irte *entry) +{ + int index; + + if (!entry || irq > NR_IRQS) + return -1; + + spin_lock(&irq_2_ir_lock); + if (!irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + *entry = *(irq_2_iommu[irq].iommu->ir_table->base + index); + + spin_unlock(&irq_2_ir_lock); + return 0; +} + +int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) +{ + struct ir_table *table = iommu->ir_table; + u16 index, start_index; + unsigned int mask = 0; + int i; + + if (!count) + return -1; + + /* + * start the IRTE search from index 0. + */ + index = start_index = 0; + + if (count > 1) { + count = __roundup_pow_of_two(count); + mask = ilog2(count); + } + + if (mask > ecap_max_handle_mask(iommu->ecap)) { + printk(KERN_ERR + "Requested mask %x exceeds the max invalidation handle" + " mask value %Lx\n", mask, + ecap_max_handle_mask(iommu->ecap)); + return -1; + } + + spin_lock(&irq_2_ir_lock); + do { + for (i = index; i < index + count; i++) + if (table->base[i].present) + break; + /* empty index found */ + if (i == index + count) + break; + + index = (index + count) % INTR_REMAP_TABLE_ENTRIES; + + if (index == start_index) { + spin_unlock(&irq_2_ir_lock); + printk(KERN_ERR "can't allocate an IRTE\n"); + return -1; + } + } while (1); + + for (i = index; i < index + count; i++) + table->base[i].present = 1; + + irq_2_iommu[irq].iommu = iommu; + irq_2_iommu[irq].irte_index = index; + irq_2_iommu[irq].sub_handle = 0; + irq_2_iommu[irq].irte_mask = mask; + + spin_unlock(&irq_2_ir_lock); + + return index; +} + +static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask) +{ + struct qi_desc desc; + + desc.low = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask) + | QI_IEC_SELECTIVE; + desc.high = 0; + + qi_submit_sync(&desc, iommu); +} + +int map_irq_to_irte_handle(int irq, u16 *sub_handle) +{ + int index; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + *sub_handle = irq_2_iommu[irq].sub_handle; + index = irq_2_iommu[irq].irte_index; + spin_unlock(&irq_2_ir_lock); + return index; +} + +int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle) +{ + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + irq_2_iommu[irq].iommu = iommu; + irq_2_iommu[irq].irte_index = index; + irq_2_iommu[irq].sub_handle = subhandle; + irq_2_iommu[irq].irte_mask = 0; + + spin_unlock(&irq_2_ir_lock); + + return 0; +} + +int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index) +{ + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + irq_2_iommu[irq].iommu = NULL; + irq_2_iommu[irq].irte_index = 0; + irq_2_iommu[irq].sub_handle = 0; + irq_2_iommu[irq].irte_mask = 0; + + spin_unlock(&irq_2_ir_lock); + + return 0; +} + +int modify_irte(int irq, struct irte *irte_modified) +{ + int index; + struct irte *irte; + struct intel_iommu *iommu; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + iommu = irq_2_iommu[irq].iommu; + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + irte = &iommu->ir_table->base[index]; + + set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1)); + __iommu_flush_cache(iommu, irte, sizeof(*irte)); + + qi_flush_iec(iommu, index, 0); + + spin_unlock(&irq_2_ir_lock); + return 0; +} + +int flush_irte(int irq) +{ + int index; + struct intel_iommu *iommu; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + iommu = irq_2_iommu[irq].iommu; + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + + qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask); + spin_unlock(&irq_2_ir_lock); + + return 0; +} + +int free_irte(int irq) +{ + int index, i; + struct irte *irte; + struct intel_iommu *iommu; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + iommu = irq_2_iommu[irq].iommu; + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + irte = &iommu->ir_table->base[index]; + + if (!irq_2_iommu[irq].sub_handle) { + for (i = 0; i < (1 << irq_2_iommu[irq].irte_mask); i++) + set_64bit((unsigned long *)irte, 0); + qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask); + } + + irq_2_iommu[irq].iommu = NULL; + irq_2_iommu[irq].irte_index = 0; + irq_2_iommu[irq].sub_handle = 0; + irq_2_iommu[irq].irte_mask = 0; + + spin_unlock(&irq_2_ir_lock); + + return 0; +} + static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) { u64 addr; diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 8a0238dd2c11..324bbca85a26 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -98,7 +98,19 @@ struct irte { __u64 high; }; }; +extern int get_irte(int irq, struct irte *entry); +extern int modify_irte(int irq, struct irte *irte_modified); +extern int alloc_irte(struct intel_iommu *iommu, int irq, u16 count); +extern int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, + u16 sub_handle); +extern int map_irq_to_irte_handle(int irq, u16 *sub_handle); +extern int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index); +extern int flush_irte(int irq); +extern int free_irte(int irq); + +extern int irq_remapped(int irq); #else +#define irq_remapped(irq) (0) #define enable_intr_remapping(mode) (-1) #define intr_remapping_enabled (0) #endif -- cgit v1.2.3 From 72b1e22dfcad1daca6906148fd956ffe404bb0bc Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:45 -0700 Subject: x64, x2apic/intr-remap: generic irq migration support from process context Generic infrastructure for migrating the irq from the process context in the presence of CONFIG_GENERIC_PENDING_IRQ. This will be used later for migrating irq in the presence of interrupt-remapping. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- include/linux/irq.h | 1 + kernel/irq/manage.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/irq.h b/include/linux/irq.h index 552e0ec269c9..c211984b55e5 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -62,6 +62,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_MOVE_PENDING 0x00200000 /* need to re-target IRQ destination */ #define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ #define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */ +#define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #ifdef CONFIG_IRQ_PER_CPU # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 46d6611a33bb..628b5572a7c2 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -87,7 +87,14 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask) set_balance_irq_affinity(irq, cpumask); #ifdef CONFIG_GENERIC_PENDING_IRQ - set_pending_irq(irq, cpumask); + if (desc->status & IRQ_MOVE_PCNTXT) { + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + desc->chip->set_affinity(irq, cpumask); + spin_unlock_irqrestore(&desc->lock, flags); + } else + set_pending_irq(irq, cpumask); #else desc->affinity = cpumask; desc->chip->set_affinity(irq, cpumask); -- cgit v1.2.3 From 89027d35aa5b8f45ce0f7fa0911db85b46563da0 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:56 -0700 Subject: x64, x2apic/intr-remap: IO-APIC support for interrupt-remapping IO-APIC support in the presence of interrupt-remapping infrastructure. IO-APIC RTE will be programmed with interrupt-remapping table entry(IRTE) index and the IRTE will contain information about the vector, cpu destination, trigger mode etc, which traditionally was present in the IO-APIC RTE. Introduce a new irq_chip for cleaner irq migration (in the process context as opposed to the current irq migration in the context of an interrupt. interrupt-remapping infrastructure will help us achieve this cleanly). For edge triggered, irq migration is a simple atomic update(of vector and cpu destination) of IRTE and flush the hardware cache. For level triggered, we need to modify the io-apic RTE aswell with the update vector information, along with modifying IRTE with vector and cpu destination. So irq migration for level triggered is little bit more complex compared to edge triggered migration. But the good news is, we use the same algorithm for level triggered migration as we have today, only difference being, we now initiate the irq migration from process context instead of the interrupt context. In future, when we do a directed EOI (combined with cpu EOI broadcast suppression) to the IO-APIC, level triggered irq migration will also be as simple as edge triggered migration and we can do the irq migration with a simple atomic update to IO-APIC RTE. TBD: some tests/changes needed in the presence of fixup_irqs() for level triggered irq migration. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/apic_64.c | 1 + arch/x86/kernel/io_apic_64.c | 300 +++++++++++++++++++++++++++++++++++++--- drivers/pci/intr_remapping.c | 10 ++ include/asm-x86/apic.h | 9 ++ include/asm-x86/io_apic.h | 14 ++ include/asm-x86/irq_remapping.h | 8 ++ include/linux/dmar.h | 1 + 7 files changed, 321 insertions(+), 22 deletions(-) create mode 100644 include/asm-x86/irq_remapping.h (limited to 'include/linux') diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index a969ef78e12a..d5c06917b5b1 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -46,6 +46,7 @@ static int disable_apic_timer __cpuinitdata; static int apic_calibrate_pmtmr __initdata; int disable_apic; +int x2apic; /* Local APIC timer works in C2 */ int local_apic_timer_c2_ok; diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index b62d42ef9283..9bd02ef049a0 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -37,6 +37,7 @@ #include #endif #include +#include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include @@ -312,7 +314,12 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) pin = entry->pin; if (pin == -1) break; - io_apic_write(apic, 0x11 + pin*2, dest); + /* + * With interrupt-remapping, destination information comes + * from interrupt-remapping table entry. + */ + if (!irq_remapped(irq)) + io_apic_write(apic, 0x11 + pin*2, dest); reg = io_apic_read(apic, 0x10 + pin*2); reg &= ~IO_APIC_REDIR_VECTOR_MASK; reg |= vector; @@ -906,18 +913,98 @@ void setup_vector_irq(int cpu) static struct irq_chip ioapic_chip; +#ifdef CONFIG_INTR_REMAP +static struct irq_chip ir_ioapic_chip; +#endif static void ioapic_register_intr(int irq, unsigned long trigger) { - if (trigger) { + if (trigger) irq_desc[irq].status |= IRQ_LEVEL; - set_irq_chip_and_handler_name(irq, &ioapic_chip, - handle_fasteoi_irq, "fasteoi"); - } else { + else irq_desc[irq].status &= ~IRQ_LEVEL; + +#ifdef CONFIG_INTR_REMAP + if (irq_remapped(irq)) { + irq_desc[irq].status |= IRQ_MOVE_PCNTXT; + if (trigger) + set_irq_chip_and_handler_name(irq, &ir_ioapic_chip, + handle_fasteoi_irq, + "fasteoi"); + else + set_irq_chip_and_handler_name(irq, &ir_ioapic_chip, + handle_edge_irq, "edge"); + return; + } +#endif + if (trigger) + set_irq_chip_and_handler_name(irq, &ioapic_chip, + handle_fasteoi_irq, + "fasteoi"); + else set_irq_chip_and_handler_name(irq, &ioapic_chip, handle_edge_irq, "edge"); +} + +static int setup_ioapic_entry(int apic, int irq, + struct IO_APIC_route_entry *entry, + unsigned int destination, int trigger, + int polarity, int vector) +{ + /* + * add it to the IO-APIC irq-routing table: + */ + memset(entry,0,sizeof(*entry)); + +#ifdef CONFIG_INTR_REMAP + if (intr_remapping_enabled) { + struct intel_iommu *iommu = map_ioapic_to_ir(apic); + struct irte irte; + struct IR_IO_APIC_route_entry *ir_entry = + (struct IR_IO_APIC_route_entry *) entry; + int index; + + if (!iommu) + panic("No mapping iommu for ioapic %d\n", apic); + + index = alloc_irte(iommu, irq, 1); + if (index < 0) + panic("Failed to allocate IRTE for ioapic %d\n", apic); + + memset(&irte, 0, sizeof(irte)); + + irte.present = 1; + irte.dst_mode = INT_DEST_MODE; + irte.trigger_mode = trigger; + irte.dlvry_mode = INT_DELIVERY_MODE; + irte.vector = vector; + irte.dest_id = IRTE_DEST(destination); + + modify_irte(irq, &irte); + + ir_entry->index2 = (index >> 15) & 0x1; + ir_entry->zero = 0; + ir_entry->format = 1; + ir_entry->index = (index & 0x7fff); + } else +#endif + { + entry->delivery_mode = INT_DELIVERY_MODE; + entry->dest_mode = INT_DEST_MODE; + entry->dest = destination; } + + entry->mask = 0; /* enable IRQ */ + entry->trigger = trigger; + entry->polarity = polarity; + entry->vector = vector; + + /* Mask level triggered irqs. + * Use IRQ_DELAYED_DISABLE for edge triggered irqs. + */ + if (trigger) + entry->mask = 1; + return 0; } static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq, @@ -942,24 +1029,15 @@ static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq, apic, mp_ioapics[apic].mp_apicid, pin, cfg->vector, irq, trigger, polarity); - /* - * add it to the IO-APIC irq-routing table: - */ - memset(&entry,0,sizeof(entry)); - - entry.delivery_mode = INT_DELIVERY_MODE; - entry.dest_mode = INT_DEST_MODE; - entry.dest = cpu_mask_to_apicid(mask); - entry.mask = 0; /* enable IRQ */ - entry.trigger = trigger; - entry.polarity = polarity; - entry.vector = cfg->vector; - /* Mask level triggered irqs. - * Use IRQ_DELAYED_DISABLE for edge triggered irqs. - */ - if (trigger) - entry.mask = 1; + if (setup_ioapic_entry(mp_ioapics[apic].mp_apicid, irq, &entry, + cpu_mask_to_apicid(mask), trigger, polarity, + cfg->vector)) { + printk("Failed to setup ioapic entry for ioapic %d, pin %d\n", + mp_ioapics[apic].mp_apicid, pin); + __clear_irq_vector(irq); + return; + } ioapic_register_intr(irq, trigger); if (irq < 16) @@ -1011,6 +1089,9 @@ static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin, { struct IO_APIC_route_entry entry; + if (intr_remapping_enabled) + return; + memset(&entry, 0, sizeof(entry)); /* @@ -1466,6 +1547,147 @@ static int ioapic_retrigger_irq(unsigned int irq) */ #ifdef CONFIG_SMP + +#ifdef CONFIG_INTR_REMAP +static void ir_irq_migration(struct work_struct *work); + +static DECLARE_DELAYED_WORK(ir_migration_work, ir_irq_migration); + +/* + * Migrate the IO-APIC irq in the presence of intr-remapping. + * + * For edge triggered, irq migration is a simple atomic update(of vector + * and cpu destination) of IRTE and flush the hardware cache. + * + * For level triggered, we need to modify the io-apic RTE aswell with the update + * vector information, along with modifying IRTE with vector and destination. + * So irq migration for level triggered is little bit more complex compared to + * edge triggered migration. But the good news is, we use the same algorithm + * for level triggered migration as we have today, only difference being, + * we now initiate the irq migration from process context instead of the + * interrupt context. + * + * In future, when we do a directed EOI (combined with cpu EOI broadcast + * suppression) to the IO-APIC, level triggered irq migration will also be + * as simple as edge triggered migration and we can do the irq migration + * with a simple atomic update to IO-APIC RTE. + */ +static void migrate_ioapic_irq(int irq, cpumask_t mask) +{ + struct irq_cfg *cfg = irq_cfg + irq; + struct irq_desc *desc = irq_desc + irq; + cpumask_t tmp, cleanup_mask; + struct irte irte; + int modify_ioapic_rte = desc->status & IRQ_LEVEL; + unsigned int dest; + unsigned long flags; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + return; + + if (get_irte(irq, &irte)) + return; + + if (assign_irq_vector(irq, mask)) + return; + + cpus_and(tmp, cfg->domain, mask); + dest = cpu_mask_to_apicid(tmp); + + if (modify_ioapic_rte) { + spin_lock_irqsave(&ioapic_lock, flags); + __target_IO_APIC_irq(irq, dest, cfg->vector); + spin_unlock_irqrestore(&ioapic_lock, flags); + } + + irte.vector = cfg->vector; + irte.dest_id = IRTE_DEST(dest); + + /* + * Modified the IRTE and flushes the Interrupt entry cache. + */ + modify_irte(irq, &irte); + + if (cfg->move_in_progress) { + cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); + cfg->move_cleanup_count = cpus_weight(cleanup_mask); + send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); + cfg->move_in_progress = 0; + } + + irq_desc[irq].affinity = mask; +} + +static int migrate_irq_remapped_level(int irq) +{ + int ret = -1; + + mask_IO_APIC_irq(irq); + + if (io_apic_level_ack_pending(irq)) { + /* + * Interrupt in progress. Migrating irq now will change the + * vector information in the IO-APIC RTE and that will confuse + * the EOI broadcast performed by cpu. + * So, delay the irq migration to the next instance. + */ + schedule_delayed_work(&ir_migration_work, 1); + goto unmask; + } + + /* everthing is clear. we have right of way */ + migrate_ioapic_irq(irq, irq_desc[irq].pending_mask); + + ret = 0; + irq_desc[irq].status &= ~IRQ_MOVE_PENDING; + cpus_clear(irq_desc[irq].pending_mask); + +unmask: + unmask_IO_APIC_irq(irq); + return ret; +} + +static void ir_irq_migration(struct work_struct *work) +{ + int irq; + + for (irq = 0; irq < NR_IRQS; irq++) { + struct irq_desc *desc = irq_desc + irq; + if (desc->status & IRQ_MOVE_PENDING) { + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->chip->set_affinity || + !(desc->status & IRQ_MOVE_PENDING)) { + desc->status &= ~IRQ_MOVE_PENDING; + spin_unlock_irqrestore(&desc->lock, flags); + continue; + } + + desc->chip->set_affinity(irq, + irq_desc[irq].pending_mask); + spin_unlock_irqrestore(&desc->lock, flags); + } + } +} + +/* + * Migrates the IRQ destination in the process context. + */ +static void set_ir_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) +{ + if (irq_desc[irq].status & IRQ_LEVEL) { + irq_desc[irq].status |= IRQ_MOVE_PENDING; + irq_desc[irq].pending_mask = mask; + migrate_irq_remapped_level(irq); + return; + } + + migrate_ioapic_irq(irq, mask); +} +#endif + asmlinkage void smp_irq_move_cleanup_interrupt(void) { unsigned vector, me; @@ -1522,6 +1744,17 @@ static void irq_complete_move(unsigned int irq) #else static inline void irq_complete_move(unsigned int irq) {} #endif +#ifdef CONFIG_INTR_REMAP +static void ack_x2apic_level(unsigned int irq) +{ + ack_x2APIC_irq(); +} + +static void ack_x2apic_edge(unsigned int irq) +{ + ack_x2APIC_irq(); +} +#endif static void ack_apic_edge(unsigned int irq) { @@ -1596,6 +1829,21 @@ static struct irq_chip ioapic_chip __read_mostly = { .retrigger = ioapic_retrigger_irq, }; +#ifdef CONFIG_INTR_REMAP +static struct irq_chip ir_ioapic_chip __read_mostly = { + .name = "IR-IO-APIC", + .startup = startup_ioapic_irq, + .mask = mask_IO_APIC_irq, + .unmask = unmask_IO_APIC_irq, + .ack = ack_x2apic_edge, + .eoi = ack_x2apic_level, +#ifdef CONFIG_SMP + .set_affinity = set_ir_ioapic_affinity_irq, +#endif + .retrigger = ioapic_retrigger_irq, +}; +#endif + static inline void init_IO_APIC_traps(void) { int irq; @@ -1783,6 +2031,8 @@ static inline void __init check_timer(void) * 8259A. */ if (pin1 == -1) { + if (intr_remapping_enabled) + panic("BIOS bug: timer not connected to IO-APIC"); pin1 = pin2; apic1 = apic2; no_pin1 = 1; @@ -1809,6 +2059,8 @@ static inline void __init check_timer(void) clear_IO_APIC_pin(0, pin1); goto out; } + if (intr_remapping_enabled) + panic("timer doesn't work through Interrupt-remapped IO-APIC"); clear_IO_APIC_pin(apic1, pin1); if (!no_pin1) apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: " @@ -2401,6 +2653,10 @@ void __init setup_ioapic_dest(void) setup_IO_APIC_irq(ioapic, pin, irq, irq_trigger(irq_entry), irq_polarity(irq_entry)); +#ifdef CONFIG_INTR_REMAP + else if (intr_remapping_enabled) + set_ir_ioapic_affinity_irq(irq, TARGET_CPUS); +#endif else set_ioapic_affinity_irq(irq, TARGET_CPUS); } diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index bddb4b19b6c7..32e55c7a9805 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -220,6 +220,16 @@ int flush_irte(int irq) return 0; } +struct intel_iommu *map_ioapic_to_ir(int apic) +{ + int i; + + for (i = 0; i < MAX_IO_APICS; i++) + if (ir_ioapic[i].id == apic) + return ir_ioapic[i].iommu; + return NULL; +} + int free_irte(int irq) { int index, i; diff --git a/include/asm-x86/apic.h b/include/asm-x86/apic.h index bb54928373ca..aa746704a5c9 100644 --- a/include/asm-x86/apic.h +++ b/include/asm-x86/apic.h @@ -134,6 +134,15 @@ extern int get_physical_broadcast(void); # define apic_write_around(x, y) apic_write_atomic((x), (y)) #endif +#ifdef CONFIG_X86_64 +static inline void ack_x2APIC_irq(void) +{ + /* Docs say use 0 for future compatibility */ + native_apic_msr_write(APIC_EOI, 0); +} +#endif + + static inline void ack_APIC_irq(void) { /* diff --git a/include/asm-x86/io_apic.h b/include/asm-x86/io_apic.h index 1c4a99d882f5..8dc2622714c8 100644 --- a/include/asm-x86/io_apic.h +++ b/include/asm-x86/io_apic.h @@ -107,6 +107,20 @@ struct IO_APIC_route_entry { } __attribute__ ((packed)); +struct IR_IO_APIC_route_entry { + __u64 vector : 8, + zero : 3, + index2 : 1, + delivery_status : 1, + polarity : 1, + irr : 1, + trigger : 1, + mask : 1, + reserved : 31, + format : 1, + index : 15; +} __attribute__ ((packed)); + #ifdef CONFIG_X86_IO_APIC /* diff --git a/include/asm-x86/irq_remapping.h b/include/asm-x86/irq_remapping.h new file mode 100644 index 000000000000..78242c6ffa58 --- /dev/null +++ b/include/asm-x86/irq_remapping.h @@ -0,0 +1,8 @@ +#ifndef _ASM_IRQ_REMAPPING_H +#define _ASM_IRQ_REMAPPING_H + +extern int x2apic; + +#define IRTE_DEST(dest) ((x2apic) ? dest : dest << 8) + +#endif diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 324bbca85a26..bf41ffa74705 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -109,6 +109,7 @@ extern int flush_irte(int irq); extern int free_irte(int irq); extern int irq_remapped(int irq); +extern struct intel_iommu *map_ioapic_to_ir(int apic); #else #define irq_remapped(irq) (0) #define enable_intr_remapping(mode) (-1) -- cgit v1.2.3 From 75c46fa61bc5b4ccd20a168ff325c58771248fcd Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:57 -0700 Subject: x64, x2apic/intr-remap: MSI and MSI-X support for interrupt remapping infrastructure MSI and MSI-X support for interrupt remapping infrastructure. MSI address register will be programmed with interrupt-remapping table entry(IRTE) index and the IRTE will contain information about the vector, cpu destination, etc. For MSI-X, all the IRTE's will be consecutively allocated in the table, and the address registers will contain the starting index to the block and the data register will contain the subindex with in that block. This also introduces a new irq_chip for cleaner irq migration (in the process context as opposed to the current irq migration in the context of an interrupt. interrupt-remapping infrastructure will help us achieve this). As MSI is edge triggered, irq migration is a simple atomic update(of vector and cpu destination) of IRTE and flushing the hardware cache. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/io_apic_64.c | 230 +++++++++++++++++++++++++++++++++++++++++-- drivers/pci/intr_remapping.c | 11 +++ include/asm-x86/msidef.h | 4 + include/linux/dmar.h | 1 + 4 files changed, 238 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index 9bd02ef049a0..877aa2e9d7e8 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -2297,6 +2297,9 @@ void destroy_irq(unsigned int irq) dynamic_irq_cleanup(irq); +#ifdef CONFIG_INTR_REMAP + free_irte(irq); +#endif spin_lock_irqsave(&vector_lock, flags); __clear_irq_vector(irq); spin_unlock_irqrestore(&vector_lock, flags); @@ -2315,10 +2318,41 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms tmp = TARGET_CPUS; err = assign_irq_vector(irq, tmp); - if (!err) { - cpus_and(tmp, cfg->domain, tmp); - dest = cpu_mask_to_apicid(tmp); + if (err) + return err; + + cpus_and(tmp, cfg->domain, tmp); + dest = cpu_mask_to_apicid(tmp); + +#ifdef CONFIG_INTR_REMAP + if (irq_remapped(irq)) { + struct irte irte; + int ir_index; + u16 sub_handle; + + ir_index = map_irq_to_irte_handle(irq, &sub_handle); + BUG_ON(ir_index == -1); + + memset (&irte, 0, sizeof(irte)); + + irte.present = 1; + irte.dst_mode = INT_DEST_MODE; + irte.trigger_mode = 0; /* edge */ + irte.dlvry_mode = INT_DELIVERY_MODE; + irte.vector = cfg->vector; + irte.dest_id = IRTE_DEST(dest); + + modify_irte(irq, &irte); + msg->address_hi = MSI_ADDR_BASE_HI; + msg->data = sub_handle; + msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT | + MSI_ADDR_IR_SHV | + MSI_ADDR_IR_INDEX1(ir_index) | + MSI_ADDR_IR_INDEX2(ir_index); + } else +#endif + { msg->address_hi = MSI_ADDR_BASE_HI; msg->address_lo = MSI_ADDR_BASE_LO | @@ -2369,6 +2403,55 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) write_msi_msg(irq, &msg); irq_desc[irq].affinity = mask; } + +#ifdef CONFIG_INTR_REMAP +/* + * Migrate the MSI irq to another cpumask. This migration is + * done in the process context using interrupt-remapping hardware. + */ +static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask) +{ + struct irq_cfg *cfg = irq_cfg + irq; + unsigned int dest; + cpumask_t tmp, cleanup_mask; + struct irte irte; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + return; + + if (get_irte(irq, &irte)) + return; + + if (assign_irq_vector(irq, mask)) + return; + + cpus_and(tmp, cfg->domain, mask); + dest = cpu_mask_to_apicid(tmp); + + irte.vector = cfg->vector; + irte.dest_id = IRTE_DEST(dest); + + /* + * atomically update the IRTE with the new destination and vector. + */ + modify_irte(irq, &irte); + + /* + * After this point, all the interrupts will start arriving + * at the new destination. So, time to cleanup the previous + * vector allocation. + */ + if (cfg->move_in_progress) { + cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); + cfg->move_cleanup_count = cpus_weight(cleanup_mask); + send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); + cfg->move_in_progress = 0; + } + + irq_desc[irq].affinity = mask; +} +#endif #endif /* CONFIG_SMP */ /* @@ -2386,26 +2469,157 @@ static struct irq_chip msi_chip = { .retrigger = ioapic_retrigger_irq, }; -int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) +#ifdef CONFIG_INTR_REMAP +static struct irq_chip msi_ir_chip = { + .name = "IR-PCI-MSI", + .unmask = unmask_msi_irq, + .mask = mask_msi_irq, + .ack = ack_x2apic_edge, +#ifdef CONFIG_SMP + .set_affinity = ir_set_msi_irq_affinity, +#endif + .retrigger = ioapic_retrigger_irq, +}; + +/* + * Map the PCI dev to the corresponding remapping hardware unit + * and allocate 'nvec' consecutive interrupt-remapping table entries + * in it. + */ +static int msi_alloc_irte(struct pci_dev *dev, int irq, int nvec) +{ + struct intel_iommu *iommu; + int index; + + iommu = map_dev_to_ir(dev); + if (!iommu) { + printk(KERN_ERR + "Unable to map PCI %s to iommu\n", pci_name(dev)); + return -ENOENT; + } + + index = alloc_irte(iommu, irq, nvec); + if (index < 0) { + printk(KERN_ERR + "Unable to allocate %d IRTE for PCI %s\n", nvec, + pci_name(dev)); + return -ENOSPC; + } + return index; +} +#endif + +static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc, int irq) { + int ret; struct msi_msg msg; + + ret = msi_compose_msg(dev, irq, &msg); + if (ret < 0) + return ret; + + set_irq_msi(irq, desc); + write_msi_msg(irq, &msg); + +#ifdef CONFIG_INTR_REMAP + if (irq_remapped(irq)) { + struct irq_desc *desc = irq_desc + irq; + /* + * irq migration in process context + */ + desc->status |= IRQ_MOVE_PCNTXT; + set_irq_chip_and_handler_name(irq, &msi_ir_chip, handle_edge_irq, "edge"); + } else +#endif + set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge"); + + return 0; +} + +int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) +{ int irq, ret; + irq = create_irq(); if (irq < 0) return irq; - ret = msi_compose_msg(dev, irq, &msg); +#ifdef CONFIG_INTR_REMAP + if (!intr_remapping_enabled) + goto no_ir; + + ret = msi_alloc_irte(dev, irq, 1); + if (ret < 0) + goto error; +no_ir: +#endif + ret = setup_msi_irq(dev, desc, irq); if (ret < 0) { destroy_irq(irq); return ret; } + return 0; - set_irq_msi(irq, desc); - write_msi_msg(irq, &msg); +#ifdef CONFIG_INTR_REMAP +error: + destroy_irq(irq); + return ret; +#endif +} - set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge"); +int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + int irq, ret, sub_handle; + struct msi_desc *desc; +#ifdef CONFIG_INTR_REMAP + struct intel_iommu *iommu = 0; + int index = 0; +#endif + sub_handle = 0; + list_for_each_entry(desc, &dev->msi_list, list) { + irq = create_irq(); + if (irq < 0) + return irq; +#ifdef CONFIG_INTR_REMAP + if (!intr_remapping_enabled) + goto no_ir; + + if (!sub_handle) { + /* + * allocate the consecutive block of IRTE's + * for 'nvec' + */ + index = msi_alloc_irte(dev, irq, nvec); + if (index < 0) { + ret = index; + goto error; + } + } else { + iommu = map_dev_to_ir(dev); + if (!iommu) { + ret = -ENOENT; + goto error; + } + /* + * setup the mapping between the irq and the IRTE + * base index, the sub_handle pointing to the + * appropriate interrupt remap table entry. + */ + set_irte_irq(irq, iommu, index, sub_handle); + } +no_ir: +#endif + ret = setup_msi_irq(dev, desc, irq); + if (ret < 0) + goto error; + sub_handle++; + } return 0; + +error: + destroy_irq(irq); + return ret; } void arch_teardown_msi_irq(unsigned int irq) diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 32e55c7a9805..bb642cc5e18c 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -230,6 +230,17 @@ struct intel_iommu *map_ioapic_to_ir(int apic) return NULL; } +struct intel_iommu *map_dev_to_ir(struct pci_dev *dev) +{ + struct dmar_drhd_unit *drhd; + + drhd = dmar_find_matched_drhd_unit(dev); + if (!drhd) + return NULL; + + return drhd->iommu; +} + int free_irte(int irq) { int index, i; diff --git a/include/asm-x86/msidef.h b/include/asm-x86/msidef.h index 296f29ce426d..57fd85935e5a 100644 --- a/include/asm-x86/msidef.h +++ b/include/asm-x86/msidef.h @@ -48,4 +48,8 @@ #define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & \ MSI_ADDR_DEST_ID_MASK) +#define MSI_ADDR_IR_EXT_INT (1 << 4) +#define MSI_ADDR_IR_SHV (1 << 3) +#define MSI_ADDR_IR_INDEX1(index) ((index & 0x8000) >> 13) +#define MSI_ADDR_IR_INDEX2(index) ((index & 0x7fff) << 5) #endif /* ASM_MSIDEF_H */ diff --git a/include/linux/dmar.h b/include/linux/dmar.h index bf41ffa74705..c360c558e59e 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -109,6 +109,7 @@ extern int flush_irte(int irq); extern int free_irte(int irq); extern int irq_remapped(int irq); +extern struct intel_iommu *map_dev_to_ir(struct pci_dev *dev); extern struct intel_iommu *map_ioapic_to_ir(int apic); #else #define irq_remapped(irq) (0) -- cgit v1.2.3 From f11f594edba7f689af9792a5673ed59d660ad371 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 25 Jun 2008 11:22:42 -0400 Subject: [SCSI] lib: Add support for the T10 (SCSI) Data Integrity Field CRC The SCSI Block Protocol uses this 16-bit CRC to verify the integrity of each data sector. crc_t10dif() is used by sd_dif.c when performing I/O to or from disks formatted with protection information. Signed-off-by: Martin K. Petersen Signed-off-by: James Bottomley --- include/linux/crc-t10dif.h | 8 ++++++ lib/Kconfig | 7 +++++ lib/Makefile | 1 + lib/crc-t10dif.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 include/linux/crc-t10dif.h create mode 100644 lib/crc-t10dif.c (limited to 'include/linux') diff --git a/include/linux/crc-t10dif.h b/include/linux/crc-t10dif.h new file mode 100644 index 000000000000..a9c96d865ee7 --- /dev/null +++ b/include/linux/crc-t10dif.h @@ -0,0 +1,8 @@ +#ifndef _LINUX_CRC_T10DIF_H +#define _LINUX_CRC_T10DIF_H + +#include + +__u16 crc_t10dif(unsigned char const *, size_t); + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index 8cc8e8722a3f..c7ad7a5b3535 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -29,6 +29,13 @@ config CRC16 the kernel tree does. Such modules that use library CRC16 functions require M here. +config CRC_T10DIF + tristate "CRC calculation for the T10 Data Integrity Field" + help + This option is only needed if a module that's not in the + kernel tree needs to calculate CRC checks for use with the + SCSI data integrity subsystem. + config CRC_ITU_T tristate "CRC ITU-T V.41 functions" help diff --git a/lib/Makefile b/lib/Makefile index 74b0cfb1fcc3..237a8298f8cb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -45,6 +45,7 @@ endif obj-$(CONFIG_BITREVERSE) += bitrev.o obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o obj-$(CONFIG_CRC16) += crc16.o +obj-$(CONFIG_CRC_T10DIF)+= crc-t10dif.o obj-$(CONFIG_CRC_ITU_T) += crc-itu-t.o obj-$(CONFIG_CRC32) += crc32.o obj-$(CONFIG_CRC7) += crc7.o diff --git a/lib/crc-t10dif.c b/lib/crc-t10dif.c new file mode 100644 index 000000000000..fbbd66ed86cd --- /dev/null +++ b/lib/crc-t10dif.c @@ -0,0 +1,67 @@ +/* + * T10 Data Integrity Field CRC16 calculation + * + * Copyright (c) 2007 Oracle Corporation. All rights reserved. + * Written by Martin K. Petersen + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include + +/* Table generated using the following polynomium: + * x^16 + x^15 + x^11 + x^9 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + * gt: 0x8bb7 + */ +static const __u16 t10_dif_crc_table[256] = { + 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, + 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, + 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, + 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, + 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, + 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, + 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, + 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, + 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, + 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, + 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, + 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, + 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, + 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, + 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, + 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, + 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, + 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, + 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, + 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, + 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, + 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, + 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, + 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, + 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, + 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, + 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, + 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, + 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, + 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, + 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, + 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 +}; + +__u16 crc_t10dif(const unsigned char *buffer, size_t len) +{ + __u16 crc = 0; + unsigned int i; + + for (i = 0 ; i < len ; i++) + crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff]; + + return crc; +} +EXPORT_SYMBOL(crc_t10dif); + +MODULE_DESCRIPTION("T10 DIF CRC calculation"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d280eadc4fba0bf99fb1c3b60e8c5e007f7da02c Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 6 Jun 2008 17:13:02 +0800 Subject: [NET] smc91x: remove "irq_flags" from "struct smc91x_platdata" IRQ trigger type can be specified in the IRQ resource definition by IORESOURCE_IRQ_*, we need only one way to specify this. This also fixes the following small issue: To allow dynamic support for multiple platforms, when those relevant macros are not defined for one specific platform, the default case will be: - SMC_DYNAMIC_BUS_CONFIG defined - and SMC_IRQ_FLAGS = IRQF_TRIGGER_RISING While if "irq_flags" is missing when defining the smc91x_platdata, usually as follows: static struct smc91x_platdata xxxx_smc91x_data = { .flags = SMC91X_USE_XXBIT, }; The lp->cfg.irq_flags will always be overriden by the above structure (due to a memcpy), thus rendering lp->cfg.irq_flags to be "0" always. (regardless of the default SMC_IRQ_FLAGS or IORESOURCE_IRQ_* flags) Fixes this by forcing to use IORESOURCE_IRQ_* flags if present, and make the only user of smc91x_platdata.irq_flags (renesas/migor) to use IORESOURCE_IRQ_*. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- arch/sh/boards/renesas/migor/setup.c | 3 +-- drivers/net/smc91x.c | 9 +++++---- include/linux/smc91x.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/arch/sh/boards/renesas/migor/setup.c b/arch/sh/boards/renesas/migor/setup.c index 01af44245b57..963c99322095 100644 --- a/arch/sh/boards/renesas/migor/setup.c +++ b/arch/sh/boards/renesas/migor/setup.c @@ -30,7 +30,6 @@ static struct smc91x_platdata smc91x_info = { .flags = SMC91X_USE_16BIT, - .irq_flags = IRQF_TRIGGER_HIGH, }; static struct resource smc91x_eth_resources[] = { @@ -42,7 +41,7 @@ static struct resource smc91x_eth_resources[] = { }, [1] = { .start = 32, /* IRQ0 */ - .flags = IORESOURCE_IRQ, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, }, }; diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index f2051b209da2..e4a6c361995a 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2123,6 +2123,7 @@ static int smc_drv_probe(struct platform_device *pdev) struct net_device *ndev; struct resource *res, *ires; unsigned int __iomem *addr; + unsigned long irq_flags = SMC_IRQ_FLAGS; int ret; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); @@ -2152,7 +2153,6 @@ static int smc_drv_probe(struct platform_device *pdev) */ lp = netdev_priv(ndev); - lp->cfg.irq_flags = SMC_IRQ_FLAGS; #ifdef SMC_DYNAMIC_BUS_CONFIG if (pd) @@ -2177,8 +2177,9 @@ static int smc_drv_probe(struct platform_device *pdev) } ndev->irq = ires->start; - if (SMC_IRQ_FLAGS == -1) - lp->cfg.irq_flags = ires->flags & IRQF_TRIGGER_MASK; + + if (ires->flags & IRQF_TRIGGER_MASK) + irq_flags = ires->flags & IRQF_TRIGGER_MASK; ret = smc_request_attrib(pdev); if (ret) @@ -2205,7 +2206,7 @@ static int smc_drv_probe(struct platform_device *pdev) } #endif - ret = smc_probe(ndev, addr, lp->cfg.irq_flags); + ret = smc_probe(ndev, addr, irq_flags); if (ret != 0) goto out_iounmap; diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 8e0556b8781c..fc7682f04d89 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -7,7 +7,6 @@ struct smc91x_platdata { unsigned long flags; - unsigned long irq_flags; /* IRQF_... */ }; #endif /* __SMC91X_H__ */ -- cgit v1.2.3 From c4f0e76747e80578a8f7fddd82fd0ce8127bd2f8 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 19 Jun 2008 17:39:03 +0800 Subject: [NET] smc91x: add SMC91X_NOWAIT flag to platform data And also favors the usage of SMC91X_NOWAIT over the hardcoded SMC_NOWAIT by converting "nowait" (module parameter overridable) to platform flag. There are several possibilities: 1. platform data present - preferred and use as is 2. platform data absent - use "nowait", it can be: a. SMC_NOWAIT if defined b. default to 0 if SMC_NOWAIT isn't defined c. overriden by module parameter Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 3 ++- include/linux/smc91x.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 1b19022b6c7c..de7a913c487c 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -308,7 +308,7 @@ static void smc_reset(struct net_device *dev) * can't handle it then there will be no recovery except for * a hard reset or power cycle */ - if (nowait) + if (lp->cfg.flags & SMC91X_NOWAIT) cfg |= CONFIG_NO_WAIT; /* @@ -2160,6 +2160,7 @@ static int smc_drv_probe(struct platform_device *pdev) lp->cfg.flags |= (SMC_CAN_USE_8BIT) ? SMC91X_USE_8BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0; + lp->cfg.flags |= (nowait) ? SMC91X_NOWAIT : 0; } ndev->dma = (unsigned char)-1; diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index fc7682f04d89..90434db72db2 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -5,6 +5,8 @@ #define SMC91X_USE_16BIT (1 << 1) #define SMC91X_USE_32BIT (1 << 2) +#define SMC91X_NOWAIT (1 << 3) + struct smc91x_platdata { unsigned long flags; }; -- cgit v1.2.3 From 159198862adad7109bb347bb30a620f67beac45f Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 24 Jun 2008 13:38:50 +0800 Subject: [NET] smc91x: prepare for SMC_IO_SHIFT to be a platform configurable variable Now one can use the following code #define SMC_IO_SHIFT lp->io_shift to make SMC_IO_SHIFT a variable. This, however, will slightly increase the CPU overhead and have negative impact on the network performance. The tradeoff is, this can be specified in the smc91x platform data so that multiple boards support can be built in a single zImage. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 57 +++++++++++++++++++++++++++----------------------- drivers/net/smc91x.h | 3 +++ include/linux/smc91x.h | 7 +++++++ 3 files changed, 41 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index de7a913c487c..34bfc60e8074 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2050,9 +2050,11 @@ static int smc_enable_device(struct platform_device *pdev) return 0; } -static int smc_request_attrib(struct platform_device *pdev) +static int smc_request_attrib(struct platform_device *pdev, + struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp = netdev_priv(ndev); if (!res) return 0; @@ -2063,9 +2065,11 @@ static int smc_request_attrib(struct platform_device *pdev) return 0; } -static void smc_release_attrib(struct platform_device *pdev) +static void smc_release_attrib(struct platform_device *pdev, + struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp = netdev_priv(ndev); if (res) release_mem_region(res->start, ATTRIB_SIZE); @@ -2126,25 +2130,11 @@ static int smc_drv_probe(struct platform_device *pdev) unsigned long irq_flags = SMC_IRQ_FLAGS; int ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto out; - } - - - if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { - ret = -EBUSY; - goto out; - } - ndev = alloc_etherdev(sizeof(struct smc_local)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); ret = -ENOMEM; - goto out_release_io; + goto out; } SET_NETDEV_DEV(ndev, &pdev->dev); @@ -2154,9 +2144,10 @@ static int smc_drv_probe(struct platform_device *pdev) lp = netdev_priv(ndev); - if (pd) + if (pd) { memcpy(&lp->cfg, pd, sizeof(lp->cfg)); - else { + lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags); + } else { lp->cfg.flags |= (SMC_CAN_USE_8BIT) ? SMC91X_USE_8BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0; @@ -2165,10 +2156,24 @@ static int smc_drv_probe(struct platform_device *pdev) ndev->dma = (unsigned char)-1; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto out_free_netdev; + } + + + if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { + ret = -EBUSY; + goto out_free_netdev; + } + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!ires) { ret = -ENODEV; - goto out_free_netdev; + goto out_release_io; } ndev->irq = ires->start; @@ -2176,9 +2181,9 @@ static int smc_drv_probe(struct platform_device *pdev) if (ires->flags & IRQF_TRIGGER_MASK) irq_flags = ires->flags & IRQF_TRIGGER_MASK; - ret = smc_request_attrib(pdev); + ret = smc_request_attrib(pdev, ndev); if (ret) - goto out_free_netdev; + goto out_release_io; #if defined(CONFIG_SA1100_ASSABET) NCR_0 |= NCR_ENET_OSC_EN; #endif @@ -2213,11 +2218,11 @@ static int smc_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); iounmap(addr); out_release_attrib: - smc_release_attrib(pdev); - out_free_netdev: - free_netdev(ndev); + smc_release_attrib(pdev, ndev); out_release_io: release_mem_region(res->start, SMC_IO_EXTENT); + out_free_netdev: + free_netdev(ndev); out: printk("%s: not found (%d).\n", CARDNAME, ret); @@ -2243,7 +2248,7 @@ static int smc_drv_remove(struct platform_device *pdev) iounmap(lp->base); smc_release_datacs(pdev,ndev); - smc_release_attrib(pdev); + smc_release_attrib(pdev,ndev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); if (!res) diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 8310f1a073d8..80fb80f39200 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -500,6 +500,9 @@ struct smc_local { void __iomem *base; void __iomem *datacs; + /* the low address lines on some platforms aren't connected... */ + int io_shift; + struct smc91x_platdata cfg; }; diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 90434db72db2..0dea9459a8e4 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -7,6 +7,13 @@ #define SMC91X_NOWAIT (1 << 3) +/* two bits for IO_SHIFT, let's hope later designs will keep this sane */ +#define SMC91X_IO_SHIFT_0 (0 << 4) +#define SMC91X_IO_SHIFT_1 (1 << 4) +#define SMC91X_IO_SHIFT_2 (2 << 4) +#define SMC91X_IO_SHIFT_3 (3 << 4) +#define SMC91X_IO_SHIFT(x) (((x) >> 4) & 0x3) + struct smc91x_platdata { unsigned long flags; }; -- cgit v1.2.3 From 52256c0e06e4a4df67134b951a21b50c713a9588 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 24 Jun 2008 15:36:05 +0800 Subject: [NET] smc91x: prepare SMC_USE_PXA_DMA to be specified in platform data Now that the original SMC_USE_PXA_DMA specific code will always being built if CONFIG_ARCH_PXA is defined, so to make this part of the code to be PXA public, and still prevent it from being built if support of PXA is not selected. A SMC91X_USE_DMA flag is added to the platform data to allow platform to choose its usage of DMA. Note this flag itself is so named to be generic enough (assuming other platforms can also use DMA). It keeps backward compatibility to set the SMC91X_USE_DMA flag if SMC_USE_PXA_DMA is still defined. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 13 ++++++++----- drivers/net/smc91x.h | 6 +++--- include/linux/smc91x.h | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 34bfc60e8074..2040965d7724 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1939,8 +1939,11 @@ static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, if (retval) goto err_out; -#ifdef SMC_USE_PXA_DMA - { +#ifdef CONFIG_ARCH_PXA +# ifdef SMC_USE_PXA_DMA + lp->cfg.flags |= SMC91X_USE_DMA; +# endif + if (lp->cfg.flags & SMC91X_USE_DMA) { int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW, smc_pxa_dma_irq, NULL); if (dma >= 0) @@ -1980,7 +1983,7 @@ static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, } err_out: -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA if (retval && dev->dma != (unsigned char)-1) pxa_free_dma(dev->dma); #endif @@ -2198,7 +2201,7 @@ static int smc_drv_probe(struct platform_device *pdev) goto out_release_attrib; } -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA { struct smc_local *lp = netdev_priv(ndev); lp->device = &pdev->dev; @@ -2241,7 +2244,7 @@ static int smc_drv_remove(struct platform_device *pdev) free_irq(ndev->irq, ndev); -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA if (ndev->dma != (unsigned char)-1) pxa_free_dma(ndev->dma); #endif diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 80fb80f39200..f02cc6ac248b 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -492,7 +492,7 @@ struct smc_local { spinlock_t lock; -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA /* DMA needs the physical address of the chip */ u_long physaddr; struct device *device; @@ -510,7 +510,7 @@ struct smc_local { #define SMC_16BIT(p) ((p)->cfg.flags & SMC91X_USE_16BIT) #define SMC_32BIT(p) ((p)->cfg.flags & SMC91X_USE_32BIT) -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA /* * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is * always happening in irq context so no need to worry about races. TX is @@ -604,7 +604,7 @@ smc_pxa_dma_irq(int dma, void *dummy) { DCSR(dma) = 0; } -#endif /* SMC_USE_PXA_DMA */ +#endif /* CONFIG_ARCH_PXA */ /* diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 0dea9459a8e4..3827b922ba1f 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -14,6 +14,8 @@ #define SMC91X_IO_SHIFT_3 (3 << 4) #define SMC91X_IO_SHIFT(x) (((x) >> 4) & 0x3) +#define SMC91X_USE_DMA (1 << 6) + struct smc91x_platdata { unsigned long flags; }; -- cgit v1.2.3 From 5b5d0e704880addfd979c262e6441f126708539c Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 13 Jul 2008 11:51:40 +0100 Subject: dccp: Upgrade NDP count from 3 to 6 bytes RFC 4340, 7.7 specifies up to 6 bytes for the NDP Count option, whereas the code is currently limited to up to 3 bytes. This seems to be a relict of an earlier draft version and is brought up to date by the patch. Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 6 ++---- net/dccp/ccids/ccid3.c | 2 +- net/dccp/ccids/lib/packet_history.c | 6 +++--- net/dccp/ccids/lib/packet_history.h | 6 +++--- net/dccp/options.c | 14 ++++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index aa0737019e37..6080449fbec9 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -364,8 +364,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 -#define DCCP_NDP_LIMIT 0xFFFFFF - /** * struct dccp_minisock - Minimal DCCP connection representation * @@ -437,7 +435,7 @@ extern int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, struct sk_buff *skb); struct dccp_options_received { - u32 dccpor_ndp; /* only 24 bits */ + u64 dccpor_ndp:48; u32 dccpor_timestamp; u32 dccpor_timestamp_echo; u32 dccpor_elapsed_time; @@ -533,7 +531,7 @@ struct dccp_sock { __u16 dccps_r_ack_ratio; __u16 dccps_pcslen; __u16 dccps_pcrlen; - unsigned long dccps_ndp_count; + __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; struct dccp_minisock dccps_minisock; struct dccp_ackvec *dccps_hc_rx_ackvec; diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index a1929f33d703..523db262c18f 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -794,7 +794,7 @@ static void ccid3_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) { struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); enum ccid3_fback_type do_feedback = CCID3_FBACK_NONE; - const u32 ndp = dccp_sk(sk)->dccps_options_received.dccpor_ndp; + const u64 ndp = dccp_sk(sk)->dccps_options_received.dccpor_ndp; const bool is_data_packet = dccp_data_packet(skb); if (unlikely(hcrx->ccid3hcrx_state == TFRC_RSTATE_NO_DATA)) { diff --git a/net/dccp/ccids/lib/packet_history.c b/net/dccp/ccids/lib/packet_history.c index 8b5c41ec7ee1..712930564c38 100644 --- a/net/dccp/ccids/lib/packet_history.c +++ b/net/dccp/ccids/lib/packet_history.c @@ -153,7 +153,7 @@ void tfrc_rx_packet_history_exit(void) static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, const struct sk_buff *skb, - const u32 ndp) + const u64 ndp) { const struct dccp_hdr *dh = dccp_hdr(skb); @@ -166,7 +166,7 @@ static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, const struct sk_buff *skb, - const u32 ndp) + const u64 ndp) { struct tfrc_rx_hist_entry *entry = tfrc_rx_hist_last_rcv(h); @@ -356,7 +356,7 @@ static void __three_after_loss(struct tfrc_rx_hist *h) */ int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, struct tfrc_loss_hist *lh, - struct sk_buff *skb, u32 ndp, + struct sk_buff *skb, const u64 ndp, u32 (*calc_first_li)(struct sock *), struct sock *sk) { int is_new_loss = 0; diff --git a/net/dccp/ccids/lib/packet_history.h b/net/dccp/ccids/lib/packet_history.h index c7eeda49cb20..6976156cda3c 100644 --- a/net/dccp/ccids/lib/packet_history.h +++ b/net/dccp/ccids/lib/packet_history.h @@ -64,7 +64,7 @@ struct tfrc_rx_hist_entry { u64 tfrchrx_seqno:48, tfrchrx_ccval:4, tfrchrx_type:4; - u32 tfrchrx_ndp; /* In fact it is from 8 to 24 bits */ + u64 tfrchrx_ndp:48; ktime_t tfrchrx_tstamp; }; @@ -145,14 +145,14 @@ static inline int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, } extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, - const struct sk_buff *skb, const u32 ndp); + const struct sk_buff *skb, const u64 ndp); extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); struct tfrc_loss_hist; extern int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, struct tfrc_loss_hist *lh, - struct sk_buff *skb, u32 ndp, + struct sk_buff *skb, const u64 ndp, u32 (*first_li)(struct sock *sk), struct sock *sk); extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, diff --git a/net/dccp/options.c b/net/dccp/options.c index 43bc24e761d0..dc7c158a2f4b 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -124,12 +124,12 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, mandatory = 1; break; case DCCPO_NDP_COUNT: - if (len > 3) + if (len > 6) goto out_invalid_option; opt_recv->dccpor_ndp = dccp_decode_value_var(value, len); - dccp_pr_debug("%s rx opt: NDP count=%d\n", dccp_role(sk), - opt_recv->dccpor_ndp); + dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk), + (unsigned long long)opt_recv->dccpor_ndp); break; case DCCPO_CHANGE_L: /* fall through */ @@ -307,9 +307,11 @@ static void dccp_encode_value_var(const u32 value, unsigned char *to, *to++ = (value & 0xFF); } -static inline int dccp_ndp_len(const int ndp) +static inline u8 dccp_ndp_len(const u64 ndp) { - return likely(ndp <= 0xFF) ? 1 : ndp <= 0xFFFF ? 2 : 3; + if (likely(ndp <= 0xFF)) + return 1; + return likely(ndp <= USHORT_MAX) ? 2 : (ndp <= UINT_MAX ? 4 : 6); } int dccp_insert_option(struct sock *sk, struct sk_buff *skb, @@ -336,7 +338,7 @@ EXPORT_SYMBOL_GPL(dccp_insert_option); static int dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); - int ndp = dp->dccps_ndp_count; + u64 ndp = dp->dccps_ndp_count; if (dccp_non_data_packet(skb)) ++dp->dccps_ndp_count; -- cgit v1.2.3 From afc1246f917c664b0df98b3c22fa62db74d2ca33 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 11 Jul 2008 17:20:49 -0700 Subject: file lock: reorder struct file_lock to save space on 64 bit builds Reduce sizeof struct file_lock by 8 on 64 bit builds allowing +1 objects per slab in the file_lock_cache Signed-off-by: Richard Kennedy Signed-off-by: Andrew Morton Signed-off-by: J. Bruce Fields --- include/linux/fs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 7c1080826832..87f89bd0f6ee 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -917,12 +917,12 @@ struct file_lock { struct list_head fl_link; /* doubly linked list of all locks */ struct list_head fl_block; /* circular list of blocked processes */ fl_owner_t fl_owner; + unsigned char fl_flags; + unsigned char fl_type; unsigned int fl_pid; struct pid *fl_nspid; wait_queue_head_t fl_wait; struct file *fl_file; - unsigned char fl_flags; - unsigned char fl_type; loff_t fl_start; loff_t fl_end; -- cgit v1.2.3 From 006ebb40d3d65338bd74abb03b945f8d60e362bd Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 19 May 2008 08:32:49 -0400 Subject: Security: split proc ptrace checking into read vs. attach Enable security modules to distinguish reading of process state via proc from full ptrace access by renaming ptrace_may_attach to ptrace_may_access and adding a mode argument indicating whether only read access or full attach access is requested. This allows security modules to permit access to reading process state without granting full ptrace access. The base DAC/capability checking remains unchanged. Read access to /proc/pid/mem continues to apply a full ptrace attach check since check_mem_permission() already requires the current task to already be ptracing the target. The other ptrace checks within proc for elements like environ, maps, and fds are changed to pass the read mode instead of attach. In the SELinux case, we model such reading of process state as a reading of a proc file labeled with the target process' label. This enables SELinux policy to permit such reading of process state without permitting control or manipulation of the target process, as there are a number of cases where programs probe for such information via proc but do not need to be able to control the target (e.g. procps, lsof, PolicyKit, ConsoleKit). At present we have to choose between allowing full ptrace in policy (more permissive than required/desired) or breaking functionality (or in some cases just silencing the denials via dontaudit rules but this can hide genuine attacks). This version of the patch incorporates comments from Casey Schaufler (change/replace existing ptrace_may_attach interface, pass access mode), and Chris Wright (provide greater consistency in the checking). Note that like their predecessors __ptrace_may_attach and ptrace_may_attach, the __ptrace_may_access and ptrace_may_access interfaces use different return value conventions from each other (0 or -errno vs. 1 or 0). I retained this difference to avoid any changes to the caller logic but made the difference clearer by changing the latter interface to return a bool rather than an int and by adding a comment about it to ptrace.h for any future callers. Signed-off-by: Stephen Smalley Acked-by: Chris Wright Signed-off-by: James Morris --- fs/proc/base.c | 9 +++++---- fs/proc/task_mmu.c | 6 +++--- fs/proc/task_nommu.c | 2 +- include/linux/ptrace.h | 8 ++++++-- include/linux/security.h | 16 +++++++++++----- kernel/ptrace.c | 15 ++++++++------- security/commoncap.c | 3 ++- security/dummy.c | 3 ++- security/security.c | 5 +++-- security/selinux/hooks.c | 13 +++++++++++-- security/smack/smack_lsm.c | 5 +++-- 11 files changed, 55 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index 3b455371e7ff..58c3e6a8e15e 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -233,7 +233,7 @@ static int check_mem_permission(struct task_struct *task) */ if (task->parent == current && (task->ptrace & PT_PTRACED) && task_is_stopped_or_traced(task) && - ptrace_may_attach(task)) + ptrace_may_access(task, PTRACE_MODE_ATTACH)) return 0; /* @@ -251,7 +251,8 @@ struct mm_struct *mm_for_maps(struct task_struct *task) task_lock(task); if (task->mm != mm) goto out; - if (task->mm != current->mm && __ptrace_may_attach(task) < 0) + if (task->mm != current->mm && + __ptrace_may_access(task, PTRACE_MODE_READ) < 0) goto out; task_unlock(task); return mm; @@ -518,7 +519,7 @@ static int proc_fd_access_allowed(struct inode *inode) */ task = get_proc_task(inode); if (task) { - allowed = ptrace_may_attach(task); + allowed = ptrace_may_access(task, PTRACE_MODE_READ); put_task_struct(task); } return allowed; @@ -904,7 +905,7 @@ static ssize_t environ_read(struct file *file, char __user *buf, if (!task) goto out_no_task; - if (!ptrace_may_attach(task)) + if (!ptrace_may_access(task, PTRACE_MODE_READ)) goto out; ret = -ENOMEM; diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index c492449f3b45..164bd9f9ede3 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -210,7 +210,7 @@ static int show_map(struct seq_file *m, void *v) dev_t dev = 0; int len; - if (maps_protect && !ptrace_may_attach(task)) + if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ)) return -EACCES; if (file) { @@ -646,7 +646,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, goto out; ret = -EACCES; - if (!ptrace_may_attach(task)) + if (!ptrace_may_access(task, PTRACE_MODE_READ)) goto out_task; ret = -EINVAL; @@ -747,7 +747,7 @@ static int show_numa_map_checked(struct seq_file *m, void *v) struct proc_maps_private *priv = m->private; struct task_struct *task = priv->task; - if (maps_protect && !ptrace_may_attach(task)) + if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ)) return -EACCES; return show_numa_map(m, v); diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 4b4f9cc2f186..5d84e7121df8 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -113,7 +113,7 @@ static int show_map(struct seq_file *m, void *_vml) struct proc_maps_private *priv = m->private; struct task_struct *task = priv->task; - if (maps_protect && !ptrace_may_attach(task)) + if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ)) return -EACCES; return nommu_vma_show(m, vml->vma); diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index f98501ba557e..c6f5f9dd0cee 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -95,8 +95,12 @@ extern void __ptrace_link(struct task_struct *child, struct task_struct *new_parent); extern void __ptrace_unlink(struct task_struct *child); extern void ptrace_untrace(struct task_struct *child); -extern int ptrace_may_attach(struct task_struct *task); -extern int __ptrace_may_attach(struct task_struct *task); +#define PTRACE_MODE_READ 1 +#define PTRACE_MODE_ATTACH 2 +/* Returns 0 on success, -errno on denial. */ +extern int __ptrace_may_access(struct task_struct *task, unsigned int mode); +/* Returns true on success, false on denial. */ +extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); static inline int ptrace_reparented(struct task_struct *child) { diff --git a/include/linux/security.h b/include/linux/security.h index 50737c70e78e..62bd80cb7f87 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -46,7 +46,8 @@ struct audit_krule; */ extern int cap_capable(struct task_struct *tsk, int cap); extern int cap_settime(struct timespec *ts, struct timezone *tz); -extern int cap_ptrace(struct task_struct *parent, struct task_struct *child); +extern int cap_ptrace(struct task_struct *parent, struct task_struct *child, + unsigned int mode); extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); @@ -1170,6 +1171,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * attributes would be changed by the execve. * @parent contains the task_struct structure for parent process. * @child contains the task_struct structure for child process. + * @mode contains the PTRACE_MODE flags indicating the form of access. * Return 0 if permission is granted. * @capget: * Get the @effective, @inheritable, and @permitted capability sets for @@ -1295,7 +1297,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) struct security_operations { char name[SECURITY_NAME_MAX + 1]; - int (*ptrace) (struct task_struct *parent, struct task_struct *child); + int (*ptrace) (struct task_struct *parent, struct task_struct *child, + unsigned int mode); int (*capget) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); @@ -1573,7 +1576,8 @@ extern struct dentry *securityfs_create_dir(const char *name, struct dentry *par extern void securityfs_remove(struct dentry *dentry); /* Security operations */ -int security_ptrace(struct task_struct *parent, struct task_struct *child); +int security_ptrace(struct task_struct *parent, struct task_struct *child, + unsigned int mode); int security_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, @@ -1755,9 +1759,11 @@ static inline int security_init(void) return 0; } -static inline int security_ptrace(struct task_struct *parent, struct task_struct *child) +static inline int security_ptrace(struct task_struct *parent, + struct task_struct *child, + unsigned int mode) { - return cap_ptrace(parent, child); + return cap_ptrace(parent, child, mode); } static inline int security_capget(struct task_struct *target, diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 6c19e94fd0a5..e337390fce01 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -121,7 +121,7 @@ int ptrace_check_attach(struct task_struct *child, int kill) return ret; } -int __ptrace_may_attach(struct task_struct *task) +int __ptrace_may_access(struct task_struct *task, unsigned int mode) { /* May we inspect the given task? * This check is used both for attaching with ptrace @@ -148,16 +148,16 @@ int __ptrace_may_attach(struct task_struct *task) if (!dumpable && !capable(CAP_SYS_PTRACE)) return -EPERM; - return security_ptrace(current, task); + return security_ptrace(current, task, mode); } -int ptrace_may_attach(struct task_struct *task) +bool ptrace_may_access(struct task_struct *task, unsigned int mode) { int err; task_lock(task); - err = __ptrace_may_attach(task); + err = __ptrace_may_access(task, mode); task_unlock(task); - return !err; + return (!err ? true : false); } int ptrace_attach(struct task_struct *task) @@ -195,7 +195,7 @@ repeat: /* the same process cannot be attached many times */ if (task->ptrace & PT_PTRACED) goto bad; - retval = __ptrace_may_attach(task); + retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); if (retval) goto bad; @@ -494,7 +494,8 @@ int ptrace_traceme(void) */ task_lock(current); if (!(current->ptrace & PT_PTRACED)) { - ret = security_ptrace(current->parent, current); + ret = security_ptrace(current->parent, current, + PTRACE_MODE_ATTACH); /* * Set the ptrace bit in the process ptrace flags. */ diff --git a/security/commoncap.c b/security/commoncap.c index 33d343308413..0b6537a3672d 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -63,7 +63,8 @@ int cap_settime(struct timespec *ts, struct timezone *tz) return 0; } -int cap_ptrace (struct task_struct *parent, struct task_struct *child) +int cap_ptrace (struct task_struct *parent, struct task_struct *child, + unsigned int mode) { /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ if (!cap_issubset(child->cap_permitted, parent->cap_permitted) && diff --git a/security/dummy.c b/security/dummy.c index b8916883b77f..1db712d99dc7 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -30,7 +30,8 @@ #include #include -static int dummy_ptrace (struct task_struct *parent, struct task_struct *child) +static int dummy_ptrace (struct task_struct *parent, struct task_struct *child, + unsigned int mode) { return 0; } diff --git a/security/security.c b/security/security.c index 59838a99b80e..c4507ce2a5a0 100644 --- a/security/security.c +++ b/security/security.c @@ -161,9 +161,10 @@ int mod_reg_security(const char *name, struct security_operations *ops) /* Security operations */ -int security_ptrace(struct task_struct *parent, struct task_struct *child) +int security_ptrace(struct task_struct *parent, struct task_struct *child, + unsigned int mode) { - return security_ops->ptrace(parent, child); + return security_ops->ptrace(parent, child, mode); } int security_capget(struct task_struct *target, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index eca70f42e678..4be156334b22 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1686,14 +1686,23 @@ static inline u32 file_to_av(struct file *file) /* Hook functions begin here. */ -static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) +static int selinux_ptrace(struct task_struct *parent, + struct task_struct *child, + unsigned int mode) { int rc; - rc = secondary_ops->ptrace(parent, child); + rc = secondary_ops->ptrace(parent, child, mode); if (rc) return rc; + if (mode == PTRACE_MODE_READ) { + struct task_security_struct *tsec = parent->security; + struct task_security_struct *csec = child->security; + return avc_has_perm(tsec->sid, csec->sid, + SECCLASS_FILE, FILE__READ, NULL); + } + return task_has_perm(parent, child, PROCESS__PTRACE); } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 4a09293efa00..3c7150b3493d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -95,11 +95,12 @@ struct inode_smack *new_inode_smack(char *smack) * * Do the capability checks, and require read and write. */ -static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp) +static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp, + unsigned int mode) { int rc; - rc = cap_ptrace(ptp, ctp); + rc = cap_ptrace(ptp, ctp, mode); if (rc != 0) return rc; -- cgit v1.2.3 From 2069f457848f846cb31149c9aa29b330a6b66d1b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 4 Jul 2008 09:47:13 +1000 Subject: LSM/SELinux: show LSM mount options in /proc/mounts This patch causes SELinux mount options to show up in /proc/mounts. As with other code in the area seq_put errors are ignored. Other LSM's will not have their mount options displayed until they fill in their own security_sb_show_options() function. Signed-off-by: Eric Paris Signed-off-by: Miklos Szeredi Signed-off-by: James Morris --- fs/namespace.c | 14 +++++++++--- include/linux/security.h | 9 ++++++++ security/dummy.c | 6 ++++++ security/security.c | 5 +++++ security/selinux/hooks.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 85 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/namespace.c b/fs/namespace.c index 4fc302c2a0e0..4f6f7635b59c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -750,7 +750,7 @@ struct proc_fs_info { const char *str; }; -static void show_sb_opts(struct seq_file *m, struct super_block *sb) +static int show_sb_opts(struct seq_file *m, struct super_block *sb) { static const struct proc_fs_info fs_info[] = { { MS_SYNCHRONOUS, ",sync" }, @@ -764,6 +764,8 @@ static void show_sb_opts(struct seq_file *m, struct super_block *sb) if (sb->s_flags & fs_infop->flag) seq_puts(m, fs_infop->str); } + + return security_sb_show_options(m, sb); } static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) @@ -806,11 +808,14 @@ static int show_vfsmnt(struct seq_file *m, void *v) seq_putc(m, ' '); show_type(m, mnt->mnt_sb); seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); - show_sb_opts(m, mnt->mnt_sb); + err = show_sb_opts(m, mnt->mnt_sb); + if (err) + goto out; show_mnt_opts(m, mnt); if (mnt->mnt_sb->s_op->show_options) err = mnt->mnt_sb->s_op->show_options(m, mnt); seq_puts(m, " 0 0\n"); +out: return err; } @@ -865,10 +870,13 @@ static int show_mountinfo(struct seq_file *m, void *v) seq_putc(m, ' '); mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); - show_sb_opts(m, sb); + err = show_sb_opts(m, sb); + if (err) + goto out; if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt); seq_putc(m, '\n'); +out: return err; } diff --git a/include/linux/security.h b/include/linux/security.h index 62bd80cb7f87..c8ad8ec684b4 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -80,6 +80,7 @@ struct xfrm_selector; struct xfrm_policy; struct xfrm_state; struct xfrm_user_sec_ctx; +struct seq_file; extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); extern int cap_netlink_recv(struct sk_buff *skb, int cap); @@ -1331,6 +1332,7 @@ struct security_operations { void (*sb_free_security) (struct super_block *sb); int (*sb_copy_data) (char *orig, char *copy); int (*sb_kern_mount) (struct super_block *sb, void *data); + int (*sb_show_options) (struct seq_file *m, struct super_block *sb); int (*sb_statfs) (struct dentry *dentry); int (*sb_mount) (char *dev_name, struct path *path, char *type, unsigned long flags, void *data); @@ -1610,6 +1612,7 @@ int security_sb_alloc(struct super_block *sb); void security_sb_free(struct super_block *sb); int security_sb_copy_data(char *orig, char *copy); int security_sb_kern_mount(struct super_block *sb, void *data); +int security_sb_show_options(struct seq_file *m, struct super_block *sb); int security_sb_statfs(struct dentry *dentry); int security_sb_mount(char *dev_name, struct path *path, char *type, unsigned long flags, void *data); @@ -1887,6 +1890,12 @@ static inline int security_sb_kern_mount(struct super_block *sb, void *data) return 0; } +static inline int security_sb_show_options(struct seq_file *m, + struct super_block *sb) +{ + return 0; +} + static inline int security_sb_statfs(struct dentry *dentry) { return 0; diff --git a/security/dummy.c b/security/dummy.c index 1db712d99dc7..c155f08e9dd8 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -194,6 +194,11 @@ static int dummy_sb_kern_mount (struct super_block *sb, void *data) return 0; } +static int dummy_sb_show_options(struct seq_file *m, struct super_block *sb) +{ + return 0; +} + static int dummy_sb_statfs (struct dentry *dentry) { return 0; @@ -1088,6 +1093,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, sb_free_security); set_to_dummy_if_null(ops, sb_copy_data); set_to_dummy_if_null(ops, sb_kern_mount); + set_to_dummy_if_null(ops, sb_show_options); set_to_dummy_if_null(ops, sb_statfs); set_to_dummy_if_null(ops, sb_mount); set_to_dummy_if_null(ops, sb_check_sb); diff --git a/security/security.c b/security/security.c index 2c0a5876b939..de74fdccde26 100644 --- a/security/security.c +++ b/security/security.c @@ -292,6 +292,11 @@ int security_sb_kern_mount(struct super_block *sb, void *data) return security_ops->sb_kern_mount(sb, data); } +int security_sb_show_options(struct seq_file *m, struct super_block *sb) +{ + return security_ops->sb_show_options(m, sb); +} + int security_sb_statfs(struct dentry *dentry) { return security_ops->sb_statfs(dentry); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 85f74f665765..33dee83fdd2f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -9,7 +9,8 @@ * James Morris * * Copyright (C) 2001,2002 Networks Associates Technology, Inc. - * Copyright (C) 2003 Red Hat, Inc., James Morris + * Copyright (C) 2003-2008 Red Hat, Inc., James Morris + * Eric Paris * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. @@ -970,6 +971,57 @@ out_err: return rc; } +void selinux_write_opts(struct seq_file *m, struct security_mnt_opts *opts) +{ + int i; + char *prefix; + + for (i = 0; i < opts->num_mnt_opts; i++) { + char *has_comma = strchr(opts->mnt_opts[i], ','); + + switch (opts->mnt_opts_flags[i]) { + case CONTEXT_MNT: + prefix = CONTEXT_STR; + break; + case FSCONTEXT_MNT: + prefix = FSCONTEXT_STR; + break; + case ROOTCONTEXT_MNT: + prefix = ROOTCONTEXT_STR; + break; + case DEFCONTEXT_MNT: + prefix = DEFCONTEXT_STR; + break; + default: + BUG(); + }; + /* we need a comma before each option */ + seq_putc(m, ','); + seq_puts(m, prefix); + if (has_comma) + seq_putc(m, '\"'); + seq_puts(m, opts->mnt_opts[i]); + if (has_comma) + seq_putc(m, '\"'); + } +} + +static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb) +{ + struct security_mnt_opts opts; + int rc; + + rc = selinux_get_mnt_opts(sb, &opts); + if (rc) + return rc; + + selinux_write_opts(m, &opts); + + security_free_mnt_opts(&opts); + + return rc; +} + static inline u16 inode_mode_to_security_class(umode_t mode) { switch (mode & S_IFMT) { @@ -5365,6 +5417,7 @@ static struct security_operations selinux_ops = { .sb_free_security = selinux_sb_free_security, .sb_copy_data = selinux_sb_copy_data, .sb_kern_mount = selinux_sb_kern_mount, + .sb_show_options = selinux_sb_show_options, .sb_statfs = selinux_sb_statfs, .sb_mount = selinux_mount, .sb_umount = selinux_umount, -- cgit v1.2.3 From b478a9f9889c81e88077d1495daadee64c0af541 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 3 Jul 2008 20:56:04 +0200 Subject: security: remove unused sb_get_mnt_opts hook The sb_get_mnt_opts() hook is unused, and is superseded by the sb_show_options() hook. Signed-off-by: Miklos Szeredi Acked-by: James Morris --- include/linux/security.h | 14 -------------- security/dummy.c | 8 -------- security/security.c | 6 ------ security/selinux/hooks.c | 1 - 4 files changed, 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index c8ad8ec684b4..43c6357568a3 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -291,10 +291,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Update module state after a successful pivot. * @old_path contains the path for the old root. * @new_path contains the path for the new root. - * @sb_get_mnt_opts: - * Get the security relevant mount options used for a superblock - * @sb the superblock to get security mount options from - * @opts binary data structure containing all lsm mount data * @sb_set_mnt_opts: * Set the security relevant mount options used for a superblock * @sb the superblock to set security mount options for @@ -1348,8 +1344,6 @@ struct security_operations { struct path *new_path); void (*sb_post_pivotroot) (struct path *old_path, struct path *new_path); - int (*sb_get_mnt_opts) (const struct super_block *sb, - struct security_mnt_opts *opts); int (*sb_set_mnt_opts) (struct super_block *sb, struct security_mnt_opts *opts); void (*sb_clone_mnt_opts) (const struct super_block *oldsb, @@ -1624,8 +1618,6 @@ void security_sb_post_remount(struct vfsmount *mnt, unsigned long flags, void *d void security_sb_post_addmount(struct vfsmount *mnt, struct path *mountpoint); int security_sb_pivotroot(struct path *old_path, struct path *new_path); void security_sb_post_pivotroot(struct path *old_path, struct path *new_path); -int security_sb_get_mnt_opts(const struct super_block *sb, - struct security_mnt_opts *opts); int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts); void security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); @@ -1942,12 +1934,6 @@ static inline int security_sb_pivotroot(struct path *old_path, static inline void security_sb_post_pivotroot(struct path *old_path, struct path *new_path) { } -static inline int security_sb_get_mnt_opts(const struct super_block *sb, - struct security_mnt_opts *opts) -{ - security_init_mnt_opts(opts); - return 0; -} static inline int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts) diff --git a/security/dummy.c b/security/dummy.c index c155f08e9dd8..793856691641 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -252,13 +252,6 @@ static void dummy_sb_post_pivotroot (struct path *old_path, struct path *new_pat return; } -static int dummy_sb_get_mnt_opts(const struct super_block *sb, - struct security_mnt_opts *opts) -{ - security_init_mnt_opts(opts); - return 0; -} - static int dummy_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts) { @@ -1104,7 +1097,6 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, sb_post_addmount); set_to_dummy_if_null(ops, sb_pivotroot); set_to_dummy_if_null(ops, sb_post_pivotroot); - set_to_dummy_if_null(ops, sb_get_mnt_opts); set_to_dummy_if_null(ops, sb_set_mnt_opts); set_to_dummy_if_null(ops, sb_clone_mnt_opts); set_to_dummy_if_null(ops, sb_parse_opts_str); diff --git a/security/security.c b/security/security.c index de74fdccde26..28b2860c1129 100644 --- a/security/security.c +++ b/security/security.c @@ -348,12 +348,6 @@ void security_sb_post_pivotroot(struct path *old_path, struct path *new_path) security_ops->sb_post_pivotroot(old_path, new_path); } -int security_sb_get_mnt_opts(const struct super_block *sb, - struct security_mnt_opts *opts) -{ - return security_ops->sb_get_mnt_opts(sb, opts); -} - int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 33dee83fdd2f..745a69e74e38 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5421,7 +5421,6 @@ static struct security_operations selinux_ops = { .sb_statfs = selinux_sb_statfs, .sb_mount = selinux_mount, .sb_umount = selinux_umount, - .sb_get_mnt_opts = selinux_get_mnt_opts, .sb_set_mnt_opts = selinux_set_mnt_opts, .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, .sb_parse_opts_str = selinux_parse_opts_str, -- cgit v1.2.3 From 6f0f0fd496333777d53daff21a4e3b28c4d03a6d Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 10 Jul 2008 17:02:07 +0900 Subject: security: remove register_security hook The register security hook is no longer required, as the capability module is always registered. LSMs wishing to stack capability as a secondary module should do so explicitly. Signed-off-by: James Morris Acked-by: Stephen Smalley Acked-by: Greg Kroah-Hartman --- include/linux/security.h | 10 ---------- security/capability.c | 7 ------- security/root_plug.c | 9 --------- security/security.c | 29 ----------------------------- security/selinux/hooks.c | 32 +++++--------------------------- security/smack/smack_lsm.c | 23 ----------------------- 6 files changed, 5 insertions(+), 105 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 43c6357568a3..31c8851ec5d0 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1239,11 +1239,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @pages contains the number of pages. * Return 0 if permission is granted. * - * @register_security: - * allow module stacking. - * @name contains the name of the security module being stacked. - * @ops contains a pointer to the struct security_operations of the module to stack. - * * @secid_to_secctx: * Convert secid to security context. * @secid contains the security ID. @@ -1471,10 +1466,6 @@ struct security_operations { int (*netlink_send) (struct sock *sk, struct sk_buff *skb); int (*netlink_recv) (struct sk_buff *skb, int cap); - /* allow module stacking */ - int (*register_security) (const char *name, - struct security_operations *ops); - void (*d_instantiate) (struct dentry *dentry, struct inode *inode); int (*getprocattr) (struct task_struct *p, char *name, char **value); @@ -1564,7 +1555,6 @@ struct security_operations { extern int security_init(void); extern int security_module_enable(struct security_operations *ops); extern int register_security(struct security_operations *ops); -extern int mod_reg_security(const char *name, struct security_operations *ops); extern struct dentry *securityfs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); diff --git a/security/capability.c b/security/capability.c index 6e0671c82018..5b01c0b02422 100644 --- a/security/capability.c +++ b/security/capability.c @@ -721,12 +721,6 @@ static int cap_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall) } #endif /* CONFIG_SECURITY_NETWORK_XFRM */ -static int cap_register_security(const char *name, - struct security_operations *ops) -{ - return -EINVAL; -} - static void cap_d_instantiate(struct dentry *dentry, struct inode *inode) { } @@ -940,7 +934,6 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, sem_semop); set_to_cap_if_null(ops, netlink_send); set_to_cap_if_null(ops, netlink_recv); - set_to_cap_if_null(ops, register_security); set_to_cap_if_null(ops, d_instantiate); set_to_cap_if_null(ops, getprocattr); set_to_cap_if_null(ops, setprocattr); diff --git a/security/root_plug.c b/security/root_plug.c index a41cf42a4fa0..be0ebec2580b 100644 --- a/security/root_plug.c +++ b/security/root_plug.c @@ -28,9 +28,6 @@ #include #include -/* flag to keep track of how we were registered */ -static int secondary; - /* default is a generic type of usb to serial converter */ static int vendor_id = 0x0557; static int product_id = 0x2008; @@ -97,13 +94,7 @@ static int __init rootplug_init (void) if (register_security (&rootplug_security_ops)) { printk (KERN_INFO "Failure registering Root Plug module with the kernel\n"); - /* try registering with primary module */ - if (mod_reg_security (MY_NAME, &rootplug_security_ops)) { - printk (KERN_INFO "Failure registering Root Plug " - " module with primary security module.\n"); return -EINVAL; - } - secondary = 1; } printk (KERN_INFO "Root Plug module initialized, " "vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id); diff --git a/security/security.c b/security/security.c index 30b0278de394..59f23b5918b3 100644 --- a/security/security.c +++ b/security/security.c @@ -125,35 +125,6 @@ int register_security(struct security_operations *ops) return 0; } -/** - * mod_reg_security - allows security modules to be "stacked" - * @name: a pointer to a string with the name of the security_options to be registered - * @ops: a pointer to the struct security_options that is to be registered - * - * This function allows security modules to be stacked if the currently loaded - * security module allows this to happen. It passes the @name and @ops to the - * register_security function of the currently loaded security module. - * - * The return value depends on the currently loaded security module, with 0 as - * success. - */ -int mod_reg_security(const char *name, struct security_operations *ops) -{ - if (verify(ops)) { - printk(KERN_INFO "%s could not verify " - "security operations.\n", __func__); - return -EINVAL; - } - - if (ops == security_ops) { - printk(KERN_INFO "%s security operations " - "already registered.\n", __func__); - return -EINVAL; - } - - return security_ops->register_security(name, ops); -} - /* Security operations */ int security_ptrace(struct task_struct *parent, struct task_struct *child, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 745a69e74e38..91200feb3f9c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -126,13 +126,11 @@ __setup("selinux=", selinux_enabled_setup); int selinux_enabled = 1; #endif -/* Original (dummy) security module. */ -static struct security_operations *original_ops; -/* Minimal support for a secondary security module, - just to allow the use of the dummy or capability modules. - The owlsm module can alternatively be used as a secondary - module as long as CONFIG_OWLSM_FD is not enabled. */ +/* + * Minimal support for a secondary security module, + * just to allow the use of the capability module. + */ static struct security_operations *secondary_ops; /* Lists of inode and superblock security structures initialized @@ -5115,24 +5113,6 @@ static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) *secid = isec->sid; } -/* module stacking operations */ -static int selinux_register_security(const char *name, struct security_operations *ops) -{ - if (secondary_ops != original_ops) { - printk(KERN_ERR "%s: There is already a secondary security " - "module registered.\n", __func__); - return -EINVAL; - } - - secondary_ops = ops; - - printk(KERN_INFO "%s: Registering secondary module %s\n", - __func__, - name); - - return 0; -} - static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) { if (inode) @@ -5517,8 +5497,6 @@ static struct security_operations selinux_ops = { .sem_semctl = selinux_sem_semctl, .sem_semop = selinux_sem_semop, - .register_security = selinux_register_security, - .d_instantiate = selinux_d_instantiate, .getprocattr = selinux_getprocattr, @@ -5612,7 +5590,7 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); - original_ops = secondary_ops = security_ops; + secondary_ops = security_ops; if (!secondary_ops) panic("SELinux: No initial security operations\n"); if (register_security(&selinux_ops)) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 3c7150b3493d..ee5a51cbc5eb 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1822,27 +1822,6 @@ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) *secid = smack_to_secid(smack); } -/* module stacking operations */ - -/** - * smack_register_security - stack capability module - * @name: module name - * @ops: module operations - ignored - * - * Allow the capability module to register. - */ -static int smack_register_security(const char *name, - struct security_operations *ops) -{ - if (strcmp(name, "capability") != 0) - return -EINVAL; - - printk(KERN_INFO "%s: Registering secondary module %s\n", - __func__, name); - - return 0; -} - /** * smack_d_instantiate - Make sure the blob is correct on an inode * @opt_dentry: unused @@ -2673,8 +2652,6 @@ struct security_operations smack_ops = { .netlink_send = cap_netlink_send, .netlink_recv = cap_netlink_recv, - .register_security = smack_register_security, - .d_instantiate = smack_d_instantiate, .getprocattr = smack_getprocattr, -- cgit v1.2.3 From 7e9db9eaefdb8798730790214ff1b7746006ec98 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 14 Jul 2008 09:58:44 +0200 Subject: [S390] cio: Introduce modalias for css bus. Add modalias and subchannel type attributes for all subchannels. I/O subchannel specific attributes are now created in io_subchannel_probe(). modalias and subchannel type are also added to the uevent for the css bus. Also make the css modalias known. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- Documentation/ABI/testing/sysfs-bus-css | 35 +++++++++++++++++ drivers/s390/cio/cio.h | 1 + drivers/s390/cio/css.c | 69 ++++++++++++++++++++++++++++++--- drivers/s390/cio/css.h | 2 - drivers/s390/cio/device.c | 47 ++++++++++++++-------- include/linux/mod_devicetable.h | 9 +++++ scripts/mod/file2alias.c | 12 ++++++ 7 files changed, 151 insertions(+), 24 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-css (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-css b/Documentation/ABI/testing/sysfs-bus-css new file mode 100644 index 000000000000..b585ec258a08 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-css @@ -0,0 +1,35 @@ +What: /sys/bus/css/devices/.../type +Date: March 2008 +Contact: Cornelia Huck + linux-s390@vger.kernel.org +Description: Contains the subchannel type, as reported by the hardware. + This attribute is present for all subchannel types. + +What: /sys/bus/css/devices/.../modalias +Date: March 2008 +Contact: Cornelia Huck + linux-s390@vger.kernel.org +Description: Contains the module alias as reported with uevents. + It is of the format css:t and present for all + subchannel types. + +What: /sys/bus/css/drivers/io_subchannel/.../chpids +Date: December 2002 +Contact: Cornelia Huck + linux-s390@vger.kernel.org +Description: Contains the ids of the channel paths used by this + subchannel, as reported by the channel subsystem + during subchannel recognition. + Note: This is an I/O-subchannel specific attribute. +Users: s390-tools, HAL + +What: /sys/bus/css/drivers/io_subchannel/.../pimpampom +Date: December 2002 +Contact: Cornelia Huck + linux-s390@vger.kernel.org +Description: Contains the PIM/PAM/POM values, as reported by the + channel subsystem when last queried by the common I/O + layer (this implies that this attribute is not neccessarily + in sync with the values current in the channel subsystem). + Note: This is an I/O-subchannel specific attribute. +Users: s390-tools, HAL diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 6e933aebe013..4062748e8346 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "chsc.h" #include "schid.h" diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index b7f4b52c5a9a..53e7496dc90c 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -2,8 +2,7 @@ * drivers/s390/cio/css.c * driver for channel subsystem * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation + * Copyright IBM Corp. 2002,2008 * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) */ @@ -210,6 +209,41 @@ void css_update_ssd_info(struct subchannel *sch) } } +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct subchannel *sch = to_subchannel(dev); + + return sprintf(buf, "%01x\n", sch->st); +} + +static DEVICE_ATTR(type, 0444, type_show, NULL); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct subchannel *sch = to_subchannel(dev); + + return sprintf(buf, "css:t%01X\n", sch->st); +} + +static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); + +static struct attribute *subch_attrs[] = { + &dev_attr_type.attr, + &dev_attr_modalias.attr, + NULL, +}; + +static struct attribute_group subch_attr_group = { + .attrs = subch_attrs, +}; + +static struct attribute_group *default_subch_attr_groups[] = { + &subch_attr_group, + NULL, +}; + static int css_register_subchannel(struct subchannel *sch) { int ret; @@ -218,16 +252,17 @@ static int css_register_subchannel(struct subchannel *sch) sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; - sch->dev.groups = subch_attr_groups; + sch->dev.groups = default_subch_attr_groups; /* * We don't want to generate uevents for I/O subchannels that don't * have a working ccw device behind them since they will be * unregistered before they can be used anyway, so we delay the add * uevent until after device recognition was successful. + * Note that we suppress the uevent for all subchannel types; + * the subchannel driver can decide itself when it wants to inform + * userspace of its existence. */ - if (!cio_is_console(sch->schid)) - /* Console is special, no need to suppress. */ - sch->dev.uevent_suppress = 1; + sch->dev.uevent_suppress = 1; css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); @@ -236,6 +271,15 @@ static int css_register_subchannel(struct subchannel *sch) sch->schid.ssid, sch->schid.sch_no, ret); return ret; } + if (!sch->driver) { + /* + * No driver matched. Generate the uevent now so that + * a fitting driver module may be loaded based on the + * modalias. + */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); + } return ret; } @@ -926,12 +970,25 @@ static void css_shutdown(struct device *dev) sch->driver->shutdown(sch); } +static int css_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct subchannel *sch = to_subchannel(dev); + int ret; + + ret = add_uevent_var(env, "ST=%01X", sch->st); + if (ret) + return ret; + ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st); + return ret; +} + struct bus_type css_bus_type = { .name = "css", .match = css_bus_match, .probe = css_probe, .remove = css_remove, .shutdown = css_shutdown, + .uevent = css_uevent, }; /** diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index bfe0ada43f2c..e0fc7b499784 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -143,6 +143,4 @@ int css_sch_is_valid(struct schib *); extern struct workqueue_struct *slow_path_wq; void css_wait_for_slow_path(void); - -extern struct attribute_group *subch_attr_groups[]; #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0ed5a81260bc..23b129fd4d8d 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -585,19 +585,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(availability, 0444, available_show, NULL); -static struct attribute * subch_attrs[] = { +static struct attribute *io_subchannel_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, NULL, }; -static struct attribute_group subch_attr_group = { - .attrs = subch_attrs, -}; - -struct attribute_group *subch_attr_groups[] = { - &subch_attr_group, - NULL, +static struct attribute_group io_subchannel_attr_group = { + .attrs = io_subchannel_attrs, }; static struct attribute * ccwdev_attrs[] = { @@ -1157,11 +1152,21 @@ static int io_subchannel_probe(struct subchannel *sch) cdev = sch_get_cdev(sch); if (cdev) { + rc = sysfs_create_group(&sch->dev.kobj, + &io_subchannel_attr_group); + if (rc) + CIO_MSG_EVENT(0, "Failed to create io subchannel " + "attributes for subchannel " + "0.%x.%04x (rc=%d)\n", + sch->schid.ssid, sch->schid.sch_no, rc); /* * This subchannel already has an associated ccw_device. - * Register it and exit. This happens for all early - * device, e.g. the console. + * Throw the delayed uevent for the subchannel, register + * the ccw_device and exit. This happens for all early + * devices, e.g. the console. */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); @@ -1184,11 +1189,17 @@ static int io_subchannel_probe(struct subchannel *sch) */ dev_id.devno = sch->schib.pmcw.dev; dev_id.ssid = sch->schid.ssid; + rc = sysfs_create_group(&sch->dev.kobj, + &io_subchannel_attr_group); + if (rc) + return rc; /* Allocate I/O subchannel private data. */ sch->private = kzalloc(sizeof(struct io_subchannel_private), GFP_KERNEL | GFP_DMA); - if (!sch->private) - return -ENOMEM; + if (!sch->private) { + rc = -ENOMEM; + goto out_err; + } cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); if (!cdev) cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), @@ -1207,8 +1218,8 @@ static int io_subchannel_probe(struct subchannel *sch) } cdev = io_subchannel_create_ccwdev(sch); if (IS_ERR(cdev)) { - kfree(sch->private); - return PTR_ERR(cdev); + rc = PTR_ERR(cdev); + goto out_err; } rc = io_subchannel_recog(cdev, sch); if (rc) { @@ -1217,9 +1228,12 @@ static int io_subchannel_probe(struct subchannel *sch) spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); - kfree(sch->private); + goto out_err; } - + return 0; +out_err: + kfree(sch->private); + sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return rc; } @@ -1240,6 +1254,7 @@ io_subchannel_remove (struct subchannel *sch) ccw_device_unregister(cdev); put_device(&cdev->dev); kfree(sch->private); + sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return 0; } diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 69b2342d5ebb..1fd03e732e07 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -159,6 +159,15 @@ struct ap_device_id { #define AP_DEVICE_ID_MATCH_DEVICE_TYPE 0x01 +/* s390 css bus devices (subchannels) */ +struct css_device_id { + __u8 type; /* subchannel type */ + __u8 pad1; + __u16 pad2; + __u32 pad3; + kernel_ulong_t driver_data; +}; + #define ACPI_ID_LEN 16 /* only 9 bytes needed here, 16 bytes are used */ /* to workaround crosscompile issues */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index cea4a790e1e9..37d5c363fbcd 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -304,6 +304,14 @@ static int do_ap_entry(const char *filename, return 1; } +/* looks like: "css:tN" */ +static int do_css_entry(const char *filename, + struct css_device_id *id, char *alias) +{ + sprintf(alias, "css:t%01X", id->type); + return 1; +} + /* Looks like: "serio:tyNprNidNexN" */ static int do_serio_entry(const char *filename, struct serio_device_id *id, char *alias) @@ -680,6 +688,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, do_table(symval, sym->st_size, sizeof(struct ap_device_id), "ap", do_ap_entry, mod); + else if (sym_is(symname, "__mod_css_device_table")) + do_table(symval, sym->st_size, + sizeof(struct css_device_id), "css", + do_css_entry, mod); else if (sym_is(symname, "__mod_serio_device_table")) do_table(symval, sym->st_size, sizeof(struct serio_device_id), "serio", -- cgit v1.2.3 From f08adc008d84f6b03d377ede951e29ed169e76e2 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 14 Jul 2008 09:59:03 +0200 Subject: [S390] css: Use css_device_id for bus matching. css_device_id exists, so use it for determining the right driver (and add a match_flags which is always 1 for valid types). Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/css.c | 15 ++++++--------- drivers/s390/cio/css.h | 2 +- drivers/s390/cio/device.c | 8 +++++++- include/linux/mod_devicetable.h | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 45ba07c0a286..4e2f2bbf4ba5 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -850,19 +850,16 @@ int sch_is_pseudo_sch(struct subchannel *sch) return sch == to_css(sch->dev.parent)->pseudo_subchannel; } -/* - * find a driver for a subchannel. They identify by the subchannel - * type with the exception that the console subchannel driver has its own - * subchannel type although the device is an i/o subchannel - */ -static int -css_bus_match (struct device *dev, struct device_driver *drv) +static int css_bus_match(struct device *dev, struct device_driver *drv) { struct subchannel *sch = to_subchannel(dev); struct css_driver *driver = to_cssdriver(drv); + struct css_device_id *id; - if (sch->st == driver->subchannel_type) - return 1; + for (id = driver->subchannel_type; id->match_flags; id++) { + if (sch->st == id->type) + return 1; + } return 0; } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 38bf9ddb8412..58020bf41ed0 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -75,7 +75,7 @@ struct chp_link; */ struct css_driver { struct module *owner; - unsigned int subchannel_type; + struct css_device_id *subchannel_type; struct device_driver drv; void (*irq)(struct subchannel *); int (*chp_event)(struct subchannel *, struct chp_link *, int); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 522d47afc950..c904cb84d75e 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -131,9 +131,15 @@ static int io_subchannel_sch_event(struct subchannel *, int); static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, int); +static struct css_device_id io_subchannel_ids[] = { + { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(css, io_subchannel_ids); + static struct css_driver io_subchannel_driver = { .owner = THIS_MODULE, - .subchannel_type = SUBCHANNEL_TYPE_IO, + .subchannel_type = io_subchannel_ids, .name = "io_subchannel", .irq = io_subchannel_irq, .sch_event = io_subchannel_sch_event, diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 1fd03e732e07..c4db5827963d 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -161,8 +161,8 @@ struct ap_device_id { /* s390 css bus devices (subchannels) */ struct css_device_id { + __u8 match_flags; __u8 type; /* subchannel type */ - __u8 pad1; __u16 pad2; __u32 pad3; kernel_ulong_t driver_data; -- cgit v1.2.3 From 2f3804edf971d2080243d2b4552bfd61ddfbf969 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 2 Jul 2008 01:36:15 -0500 Subject: powerpc/85xx: Add support for MPC8536DS Add support for the MPC8536 process and MPC8536DS reference board. The MPC8536 is an e500v2 based SoC which eTSEC, USB, SATA, PCI, and PCIe. The USB and SATA IP blocks are similiar to those on the PQ2 Pro SoCs and thus use the same drivers. Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc8536ds.dts | 420 ++++++++ arch/powerpc/configs/mpc8536_ds_defconfig | 1637 +++++++++++++++++++++++++++++ arch/powerpc/platforms/85xx/Kconfig | 6 + arch/powerpc/platforms/85xx/Makefile | 1 + arch/powerpc/platforms/85xx/mpc8536_ds.c | 125 +++ arch/powerpc/sysdev/fsl_pci.c | 2 + include/linux/pci_ids.h | 2 + 7 files changed, 2193 insertions(+) create mode 100644 arch/powerpc/boot/dts/mpc8536ds.dts create mode 100644 arch/powerpc/configs/mpc8536_ds_defconfig create mode 100644 arch/powerpc/platforms/85xx/mpc8536_ds.c (limited to 'include/linux') diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts new file mode 100644 index 000000000000..98ad27a2ddee --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8536ds.dts @@ -0,0 +1,420 @@ +/* + * MPC8536 DS Device Tree Source + * + * Copyright 2008 Freescale Semiconductor, Inc. + * + * 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. + */ + +/dts-v1/; + +/ { + model = "fsl,mpc8536ds"; + compatible = "fsl,mpc8536ds"; + #address-cells = <1>; + #size-cells = <1>; + + aliases { + ethernet0 = &enet0; + ethernet1 = &enet1; + serial0 = &serial0; + serial1 = &serial1; + pci0 = &pci0; + pci1 = &pci1; + pci2 = &pci2; + pci3 = &pci3; + }; + + cpus { + #cpus = <1>; + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,8536@0 { + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; + }; + }; + + memory { + device_type = "memory"; + reg = <00000000 00000000>; // Filled by U-Boot + }; + + soc@ffe00000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + ranges = <0x0 0xffe00000 0x100000>; + reg = <0xffe00000 0x1000>; + bus-frequency = <0>; // Filled out by uboot. + + memory-controller@2000 { + compatible = "fsl,mpc8536-memory-controller"; + reg = <0x2000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <18 0x2>; + }; + + L2: l2-cache-controller@20000 { + compatible = "fsl,mpc8536-l2-cache-controller"; + reg = <0x20000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <16 0x2>; + }; + + i2c@3000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + compatible = "fsl-i2c"; + reg = <0x3000 0x100>; + interrupts = <43 0x2>; + interrupt-parent = <&mpic>; + dfsrr; + }; + + i2c@3100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + compatible = "fsl-i2c"; + reg = <0x3100 0x100>; + interrupts = <43 0x2>; + interrupt-parent = <&mpic>; + dfsrr; + rtc@68 { + compatible = "dallas,ds3232"; + reg = <0x68>; + }; + }; + + dma@21300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8536-dma", "fsl,eloplus-dma"; + reg = <0x21300 4>; + ranges = <0 0x21100 0x200>; + cell-index = <0>; + dma-channel@0 { + compatible = "fsl,mpc8536-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupt-parent = <&mpic>; + interrupts = <14 0x2>; + }; + dma-channel@80 { + compatible = "fsl,mpc8536-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupt-parent = <&mpic>; + interrupts = <15 0x2>; + }; + dma-channel@100 { + compatible = "fsl,mpc8536-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupt-parent = <&mpic>; + interrupts = <16 0x2>; + }; + dma-channel@180 { + compatible = "fsl,mpc8536-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupt-parent = <&mpic>; + interrupts = <17 0x2>; + }; + }; + + mdio@24520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-mdio"; + reg = <0x24520 0x20>; + + phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <10 0x1>; + reg = <0>; + device_type = "ethernet-phy"; + }; + phy1: ethernet-phy@1 { + interrupt-parent = <&mpic>; + interrupts = <10 0x1>; + reg = <1>; + device_type = "ethernet-phy"; + }; + }; + + usb@22000 { + compatible = "fsl,mpc8536-usb2-mph", "fsl-usb2-mph"; + reg = <0x22000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&mpic>; + interrupts = <28 0x2>; + phy_type = "ulpi"; + }; + + usb@23000 { + compatible = "fsl,mpc8536-usb2-mph", "fsl-usb2-mph"; + reg = <0x23000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&mpic>; + interrupts = <46 0x2>; + phy_type = "ulpi"; + }; + + enet0: ethernet@24000 { + cell-index = <0>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <0x24000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <29 2 30 2 34 2>; + interrupt-parent = <&mpic>; + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; + + enet1: ethernet@26000 { + cell-index = <1>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <0x26000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <31 2 32 2 33 2>; + interrupt-parent = <&mpic>; + phy-handle = <&phy0>; + phy-connection-type = "rgmii-id"; + }; + + usb@2b000 { + compatible = "fsl,mpc8536-usb2-dr", "fsl-usb2-dr"; + reg = <0x2b000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&mpic>; + interrupts = <60 0x2>; + dr_mode = "peripheral"; + phy_type = "ulpi"; + }; + + serial0: serial@4500 { + cell-index = <0>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x4500 0x100>; + clock-frequency = <0>; + interrupts = <42 0x2>; + interrupt-parent = <&mpic>; + }; + + serial1: serial@4600 { + cell-index = <1>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x4600 0x100>; + clock-frequency = <0>; + interrupts = <42 0x2>; + interrupt-parent = <&mpic>; + }; + + sata@18000 { + compatible = "fsl,mpc8536-sata", "fsl,pq-sata"; + reg = <0x18000 0x1000>; + cell-index = <1>; + interrupts = <74 0x2>; + interrupt-parent = <&mpic>; + }; + + sata@19000 { + compatible = "fsl,mpc8536-sata", "fsl,pq-sata"; + reg = <0x19000 0x1000>; + cell-index = <2>; + interrupts = <41 0x2>; + interrupt-parent = <&mpic>; + }; + + global-utilities@e0000 { //global utilities block + compatible = "fsl,mpc8548-guts"; + reg = <0xe0000 0x1000>; + fsl,has-rstcr; + }; + + mpic: pic@40000 { + clock-frequency = <0>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0x40000 0x40000>; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + big-endian; + }; + + msi@41600 { + compatible = "fsl,mpc8536-msi", "fsl,mpic-msi"; + reg = <0x41600 0x80>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xe0 0 + 0xe1 0 + 0xe2 0 + 0xe3 0 + 0xe4 0 + 0xe5 0 + 0xe6 0 + 0xe7 0>; + interrupt-parent = <&mpic>; + }; + }; + + pci0: pci@ffe08000 { + cell-index = <0>; + compatible = "fsl,mpc8540-pci"; + device_type = "pci"; + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + + /* IDSEL 0x11 J17 Slot 1 */ + 0x8800 0 0 1 &mpic 1 1 + 0x8800 0 0 2 &mpic 2 1 + 0x8800 0 0 3 &mpic 3 1 + 0x8800 0 0 4 &mpic 4 1>; + + interrupt-parent = <&mpic>; + interrupts = <24 0x2>; + bus-range = <0 0xff>; + ranges = <0x02000000 0 0x80000000 0x80000000 0 0x10000000 + 0x01000000 0 0x00000000 0xffc00000 0 0x00010000>; + clock-frequency = <66666666>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe08000 0x1000>; + }; + + pci1: pcie@ffe09000 { + cell-index = <1>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe09000 0x1000>; + bus-range = <0 0xff>; + ranges = <0x02000000 0 0x98000000 0x98000000 0 0x08000000 + 0x01000000 0 0x00000000 0xffc20000 0 0x00010000>; + clock-frequency = <33333333>; + interrupt-parent = <&mpic>; + interrupts = <25 0x2>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 4 1 + 0000 0 0 2 &mpic 5 1 + 0000 0 0 3 &mpic 6 1 + 0000 0 0 4 &mpic 7 1 + >; + pcie@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x02000000 0 0x98000000 + 0x02000000 0 0x98000000 + 0 0x08000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; + + pci2: pcie@ffe0a000 { + cell-index = <2>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe0a000 0x1000>; + bus-range = <0 0xff>; + ranges = <0x02000000 0 0x90000000 0x90000000 0 0x08000000 + 0x01000000 0 0x00000000 0xffc10000 0 0x00010000>; + clock-frequency = <33333333>; + interrupt-parent = <&mpic>; + interrupts = <26 0x2>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 0 1 + 0000 0 0 2 &mpic 1 1 + 0000 0 0 3 &mpic 2 1 + 0000 0 0 4 &mpic 3 1 + >; + pcie@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x02000000 0 0x90000000 + 0x02000000 0 0x90000000 + 0 0x08000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; + + pci3: pcie@ffe0b000 { + cell-index = <3>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe0b000 0x1000>; + bus-range = <0 0xff>; + ranges = <0x02000000 0 0xa0000000 0xa0000000 0 0x20000000 + 0x01000000 0 0x00000000 0xffc30000 0 0x00010000>; + clock-frequency = <33333333>; + interrupt-parent = <&mpic>; + interrupts = <27 0x2>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 8 1 + 0000 0 0 2 &mpic 9 1 + 0000 0 0 3 &mpic 10 1 + 0000 0 0 4 &mpic 11 1 + >; + + pcie@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x02000000 0 0xa0000000 + 0x02000000 0 0xa0000000 + 0 0x20000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00100000>; + }; + }; +}; diff --git a/arch/powerpc/configs/mpc8536_ds_defconfig b/arch/powerpc/configs/mpc8536_ds_defconfig new file mode 100644 index 000000000000..f1e2931de5da --- /dev/null +++ b/arch/powerpc/configs/mpc8536_ds_defconfig @@ -0,0 +1,1637 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.26-rc8 +# Wed Jul 2 01:34:26 2008 +# +# CONFIG_PPC64 is not set + +# +# Processor support +# +# CONFIG_6xx is not set +CONFIG_PPC_85xx=y +# CONFIG_PPC_8xx is not set +# CONFIG_40x is not set +# CONFIG_44x is not set +# CONFIG_E200 is not set +CONFIG_E500=y +# CONFIG_PPC_E500MC is not set +CONFIG_BOOKE=y +CONFIG_FSL_BOOKE=y +CONFIG_FSL_EMB_PERFMON=y +# CONFIG_PHYS_64BIT is not set +CONFIG_SPE=y +# CONFIG_PPC_MM_SLICES is not set +CONFIG_PPC32=y +CONFIG_WORD_SIZE=32 +CONFIG_PPC_MERGE=y +CONFIG_MMU=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_HAVE_SETUP_PER_CPU_AREA is not set +CONFIG_IRQ_PER_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_ILOG2_U32=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +# CONFIG_ARCH_NO_VIRT_TO_BUS is not set +CONFIG_PPC=y +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_NVRAM=y +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_PPC_OF=y +CONFIG_OF=y +CONFIG_PPC_UDBG_16550=y +# CONFIG_GENERIC_TBSYNC is not set +CONFIG_AUDIT_ARCH=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFAULT_UIMAGE=y +# CONFIG_PPC_DCR_NATIVE is not set +# CONFIG_PPC_DCR_MMIO is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_GROUP_SCHED=y +# CONFIG_FAIR_GROUP_SCHED is not set +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_COMPAT_BRK=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +# CONFIG_HAVE_DMA_ATTRS is not set +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +CONFIG_LBD=y +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_CLASSIC_RCU=y + +# +# Platform support +# +# CONFIG_PPC_MPC512x is not set +# CONFIG_PPC_MPC5121 is not set +# CONFIG_PPC_CELL is not set +# CONFIG_PPC_CELL_NATIVE is not set +# CONFIG_PQ2ADS is not set +CONFIG_MPC85xx=y +# CONFIG_MPC8540_ADS is not set +# CONFIG_MPC8560_ADS is not set +# CONFIG_MPC85xx_CDS is not set +# CONFIG_MPC85xx_MDS is not set +CONFIG_MPC8536_DS=y +# CONFIG_MPC85xx_DS is not set +# CONFIG_KSI8560 is not set +# CONFIG_STX_GP3 is not set +# CONFIG_TQM8540 is not set +# CONFIG_TQM8541 is not set +# CONFIG_TQM8548 is not set +# CONFIG_TQM8555 is not set +# CONFIG_TQM8560 is not set +# CONFIG_SBC8548 is not set +# CONFIG_SBC8560 is not set +# CONFIG_IPIC is not set +CONFIG_MPIC=y +# CONFIG_MPIC_WEIRD is not set +# CONFIG_PPC_I8259 is not set +# CONFIG_PPC_RTAS is not set +# CONFIG_MMIO_NVRAM is not set +# CONFIG_PPC_MPC106 is not set +# CONFIG_PPC_970_NAP is not set +# CONFIG_PPC_INDIRECT_IO is not set +# CONFIG_GENERIC_IOMAP is not set +# CONFIG_CPU_FREQ is not set +# CONFIG_CPM2 is not set +# CONFIG_FSL_ULI1575 is not set + +# +# Kernel options +# +CONFIG_HIGHMEM=y +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +# CONFIG_SCHED_HRTICK is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=m +CONFIG_MATH_EMULATION=y +# CONFIG_IOMMU_HELPER is not set +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y +CONFIG_ARCH_HAS_WALK_MEMORY=y +CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_POPULATES_NODE_MAP=y +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_PROC_DEVICETREE=y +# CONFIG_CMDLINE_BOOL is not set +# CONFIG_PM is not set +CONFIG_SECCOMP=y +CONFIG_ISA_DMA_API=y + +# +# Bus options +# +CONFIG_ZONE_DMA=y +CONFIG_PPC_INDIRECT_PCI=y +CONFIG_FSL_SOC=y +CONFIG_FSL_PCI=y +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_SYSCALL=y +# CONFIG_PCIEPORTBUS is not set +CONFIG_ARCH_SUPPORTS_MSI=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY=y +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCCARD is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HAS_RAPIDIO is not set + +# +# Advanced setup +# +# CONFIG_ADVANCED_OPTIONS is not set + +# +# Default settings for advanced configuration options are used +# +CONFIG_LOWMEM_SIZE=0x30000000 +CONFIG_PAGE_OFFSET=0xc0000000 +CONFIG_KERNEL_START=0xc0000000 +CONFIG_PHYSICAL_START=0x00000000 +CONFIG_PHYSICAL_ALIGN=0x10000000 +CONFIG_TASK_SIZE=0xc0000000 + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=m +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +CONFIG_OF_DEVICE=y +CONFIG_OF_I2C=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=524288 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_PHANTOM is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_TIFM_CORE is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +CONFIG_SCSI_LOGGING=y +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_LPFC is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_SRP is not set +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +CONFIG_SATA_PMP=y +# CONFIG_SATA_AHCI is not set +CONFIG_SATA_SIL24=y +CONFIG_SATA_FSL=y +CONFIG_ATA_SFF=y +# CONFIG_SATA_SVW is not set +# CONFIG_ATA_PIIX is not set +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_SX4 is not set +CONFIG_SATA_SIL=y +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_SCH is not set +# CONFIG_MD is not set +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# Enable only one of the two stacks, unless you know what you are doing +# +# CONFIG_FIREWIRE is not set +# CONFIG_IEEE1394 is not set +# CONFIG_I2O is not set +# CONFIG_MACINTOSH_DRIVERS is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +CONFIG_VITESSE_PHY=y +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_CASSINI is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_NET_PCI is not set +# CONFIG_B44 is not set +CONFIG_NETDEV_1000=y +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_E1000E_ENABLED is not set +# CONFIG_IP1000 is not set +# CONFIG_IGB is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SIS190 is not set +CONFIG_SKGE=y +# CONFIG_SKGE_DEBUG is not set +CONFIG_SKY2=y +# CONFIG_SKY2_DEBUG is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set +# CONFIG_BNX2 is not set +CONFIG_GIANFAR=y +CONFIG_GFAR_NAPI=y +# CONFIG_QLA3XXX is not set +# CONFIG_ATL1 is not set +CONFIG_NETDEV_10000=y +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_IXGBE is not set +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NIU is not set +# CONFIG_MLX4_CORE is not set +# CONFIG_TEHUTI is not set +# CONFIG_BNX2X is not set +# CONFIG_SFC is not set +# CONFIG_TR is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_IWLWIFI_LEDS is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_PCIPS2 is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_NOZOMI is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_NR_UARTS=2 +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_OF_PLATFORM is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_DEVPORT=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_PIIX4 is not set +CONFIG_I2C_MPC=y +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_PLATFORM is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +CONFIG_SENSORS_EEPROM=y +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +# CONFIG_SPI is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +CONFIG_THERMAL=y +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_PASIC3 is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +CONFIG_DVB_CORE=m +CONFIG_VIDEO_MEDIA=m + +# +# Multimedia drivers +# +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=m +# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set +CONFIG_MEDIA_TUNER_SIMPLE=m +CONFIG_MEDIA_TUNER_TDA8290=m +CONFIG_MEDIA_TUNER_TDA9887=m +CONFIG_MEDIA_TUNER_TEA5761=m +CONFIG_MEDIA_TUNER_TEA5767=m +CONFIG_MEDIA_TUNER_MT20XX=m +CONFIG_MEDIA_TUNER_XC2028=m +CONFIG_MEDIA_TUNER_XC5000=m +CONFIG_DVB_CAPTURE_DRIVERS=y + +# +# Supported SAA7146 based PCI Adapters +# +# CONFIG_TTPCI_EEPROM is not set +# CONFIG_DVB_BUDGET_CORE is not set + +# +# Supported USB Adapters +# +# CONFIG_DVB_USB is not set +# CONFIG_DVB_TTUSB_BUDGET is not set +# CONFIG_DVB_TTUSB_DEC is not set +# CONFIG_DVB_CINERGYT2 is not set + +# +# Supported FlexCopII (B2C2) Adapters +# +# CONFIG_DVB_B2C2_FLEXCOP is not set + +# +# Supported BT878 Adapters +# + +# +# Supported Pluto2 Adapters +# +# CONFIG_DVB_PLUTO2 is not set + +# +# Supported DVB Frontends +# + +# +# Customise DVB Frontends +# +# CONFIG_DVB_FE_CUSTOMISE is not set + +# +# DVB-S (satellite) frontends +# +# CONFIG_DVB_CX24110 is not set +# CONFIG_DVB_CX24123 is not set +# CONFIG_DVB_MT312 is not set +# CONFIG_DVB_S5H1420 is not set +# CONFIG_DVB_STV0299 is not set +# CONFIG_DVB_TDA8083 is not set +# CONFIG_DVB_TDA10086 is not set +# CONFIG_DVB_VES1X93 is not set +# CONFIG_DVB_TUNER_ITD1000 is not set +# CONFIG_DVB_TDA826X is not set +# CONFIG_DVB_TUA6100 is not set + +# +# DVB-T (terrestrial) frontends +# +# CONFIG_DVB_SP8870 is not set +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_CX22700 is not set +# CONFIG_DVB_CX22702 is not set +# CONFIG_DVB_L64781 is not set +# CONFIG_DVB_TDA1004X is not set +# CONFIG_DVB_NXT6000 is not set +# CONFIG_DVB_MT352 is not set +# CONFIG_DVB_ZL10353 is not set +# CONFIG_DVB_DIB3000MB is not set +# CONFIG_DVB_DIB3000MC is not set +# CONFIG_DVB_DIB7000M is not set +# CONFIG_DVB_DIB7000P is not set +# CONFIG_DVB_TDA10048 is not set + +# +# DVB-C (cable) frontends +# +# CONFIG_DVB_VES1820 is not set +# CONFIG_DVB_TDA10021 is not set +# CONFIG_DVB_TDA10023 is not set +# CONFIG_DVB_STV0297 is not set + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# +# CONFIG_DVB_NXT200X is not set +# CONFIG_DVB_OR51211 is not set +# CONFIG_DVB_OR51132 is not set +# CONFIG_DVB_BCM3510 is not set +# CONFIG_DVB_LGDT330X is not set +# CONFIG_DVB_S5H1409 is not set +# CONFIG_DVB_AU8522 is not set +# CONFIG_DVB_S5H1411 is not set + +# +# Digital terrestrial only tuners/PLL +# +# CONFIG_DVB_PLL is not set +# CONFIG_DVB_TUNER_DIB0070 is not set + +# +# SEC control devices for DVB-S +# +# CONFIG_DVB_LNBP21 is not set +# CONFIG_DVB_ISL6405 is not set +# CONFIG_DVB_ISL6421 is not set +CONFIG_DAB=y +# CONFIG_USB_DABUSB is not set + +# +# Graphics support +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VGACON_SOFT_SCROLLBACK is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_AC97_CODEC=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# PCI devices +# +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_OXYGEN is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HIFIER is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +CONFIG_SND_INTEL8X0=y +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_AC97_POWER_SAVE is not set + +# +# ALSA PowerMac devices +# + +# +# ALSA PowerPC devices +# + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_CAIAQ is not set + +# +# System on Chip audio support +# +# CONFIG_SND_SOC is not set + +# +# ALSA SoC audio for Freescale SOCs +# + +# +# SoC Audio for the Texas Instruments OMAP +# + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=y +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_FSL=y +CONFIG_USB_EHCI_HCD_PPC_OF=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PPC_OF=y +CONFIG_USB_OHCI_HCD_PPC_OF_BE=y +CONFIG_USB_OHCI_HCD_PPC_OF_LE=y +CONFIG_USB_OHCI_HCD_PCI=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +CONFIG_USB_MON=y + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_GADGET is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_PPC is not set +CONFIG_DMADEVICES=y + +# +# DMA Devices +# +CONFIG_FSL_DMA=y +CONFIG_DMA_ENGINE=y + +# +# DMA Clients +# +# CONFIG_NET_DMA is not set +# CONFIG_UIO is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=y +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +CONFIG_CRAMFS=y +CONFIG_VXFS_FS=m +# CONFIG_MINIX_FS is not set +CONFIG_HPFS_FS=m +CONFIG_QNX4FS_FS=m +# CONFIG_ROMFS_FS is not set +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_UFS_DEBUG is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_V4 is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +# CONFIG_SUNRPC_BIND34 is not set +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=m +# CONFIG_DLM is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_GENERIC_FIND_FIRST_BIT is not set +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_HAVE_LMB=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_SAMPLES is not set +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUGGER is not set +# CONFIG_CODE_PATCHING_SELFTEST is not set +# CONFIG_FTR_FIXUP_SELFTEST is not set +# CONFIG_IRQSTACKS is not set +# CONFIG_VIRQ_DEBUG is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_PPC_EARLY_DEBUG is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_MANAGER=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +CONFIG_CRYPTO_SHA1=m +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +# CONFIG_PPC_CLOCK is not set +# CONFIG_VIRTUALIZATION is not set diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 9cb8e29987a3..cebea5cadbc1 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -38,6 +38,12 @@ config MPC85xx_MDS help This option enables support for the MPC85xx MDS board +config MPC8536_DS + bool "Freescale MPC8536 DS" + select DEFAULT_UIMAGE + help + This option enables support for the MPC8536 DS board + config MPC85xx_DS bool "Freescale MPC85xx DS" select PPC_I8259 diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 6cea185f62b2..cb3054e1001d 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o +obj-$(CONFIG_MPC8536_DS) += mpc8536_ds.o obj-$(CONFIG_MPC85xx_DS) += mpc85xx_ds.o obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o obj-$(CONFIG_STX_GP3) += stx_gp3.o diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c new file mode 100644 index 000000000000..71d7c038961f --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c @@ -0,0 +1,125 @@ +/* + * MPC8536 DS Board Setup + * + * Copyright 2008 Freescale Semiconductor, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void __init mpc8536_ds_pic_init(void) +{ + struct mpic *mpic; + struct resource r; + struct device_node *np; + + np = of_find_node_by_type(np, "open-pic"); + if (np == NULL) { + printk(KERN_ERR "Could not find open-pic node\n"); + return; + } + + if (of_address_to_resource(np, 0, &r)) { + printk(KERN_ERR "Failed to map mpic register space\n"); + of_node_put(np); + return; + } + + mpic = mpic_alloc(np, r.start, + MPIC_PRIMARY | MPIC_WANTS_RESET | + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + of_node_put(np); + + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +static void __init mpc8536_ds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("mpc8536_ds_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8540-pci") || + of_device_is_compatible(np, "fsl,mpc8548-pcie")) { + struct resource rsrc; + of_address_to_resource(np, 0, &rsrc); + if ((rsrc.start & 0xfffff) == 0x8000) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + } + } + +#endif + + printk("MPC8536 DS board from Freescale Semiconductor\n"); +} + +static struct of_device_id __initdata mpc8536_ds_ids[] = { + { .type = "soc", }, + { .compatible = "soc", }, + {}, +}; + +static int __init mpc8536_ds_publish_devices(void) +{ + return of_platform_bus_probe(NULL, mpc8536_ds_ids, NULL); +} +machine_device_initcall(mpc8536_ds, mpc8536_ds_publish_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc8536_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc8536ds"); +} + +define_machine(mpc8536_ds) { + .name = "MPC8536 DS", + .probe = mpc8536_ds_probe, + .setup_arch = mpc8536_ds_setup_arch, + .init_IRQ = mpc8536_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 489ca5a397b1..87b0aa13ab48 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -243,6 +243,8 @@ DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544E, quirk_fsl_pcie_header); DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544, quirk_fsl_pcie_header); DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572E, quirk_fsl_pcie_header); DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8536E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8536, quirk_fsl_pcie_header); DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641, quirk_fsl_pcie_header); DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641D, quirk_fsl_pcie_header); DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8610, quirk_fsl_pcie_header); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 65953822c9cb..1cf4084b51e8 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2171,6 +2171,8 @@ #define PCI_DEVICE_ID_MPC8544 0x0033 #define PCI_DEVICE_ID_MPC8572E 0x0040 #define PCI_DEVICE_ID_MPC8572 0x0041 +#define PCI_DEVICE_ID_MPC8536E 0x0050 +#define PCI_DEVICE_ID_MPC8536 0x0051 #define PCI_DEVICE_ID_MPC8641 0x7010 #define PCI_DEVICE_ID_MPC8641D 0x7011 #define PCI_DEVICE_ID_MPC8610 0x7018 -- cgit v1.2.3 From 4ee6afd34409d296782a5b667d7991b1050e910a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 7 May 2008 21:01:30 +0300 Subject: VFS: export sync_sb_inodes This patch exports the 'sync_sb_inodes()' which is needed for UBIFS because it has to force write-back from time to time. Namely, the UBIFS budgeting subsystem forces write-back when its pessimistic callculations show that there is no free space on the media. Signed-off-by: Artem Bityutskiy --- fs/fs-writeback.c | 11 +++++++++-- include/linux/fs.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 16519fe1399c..25adfc3c693a 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -439,8 +439,8 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * on the writer throttling path, and we get decent balancing between many * throttled threads: we don't want them all piling up on inode_sync_wait. */ -static void -sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) +void generic_sync_sb_inodes(struct super_block *sb, + struct writeback_control *wbc) { const unsigned long start = jiffies; /* livelock avoidance */ @@ -526,6 +526,13 @@ sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) spin_unlock(&inode_lock); return; /* Leave any unwritten inodes on s_io */ } +EXPORT_SYMBOL_GPL(generic_sync_sb_inodes); + +static void sync_sb_inodes(struct super_block *sb, + struct writeback_control *wbc) +{ + generic_sync_sb_inodes(sb, wbc); +} /* * Start writeback of dirty pagecache data against all unlocked inodes. diff --git a/include/linux/fs.h b/include/linux/fs.h index d8e2762ed14d..f9d2aab47eda 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1729,6 +1729,8 @@ static inline void invalidate_remote_inode(struct inode *inode) extern int invalidate_inode_pages2(struct address_space *mapping); extern int invalidate_inode_pages2_range(struct address_space *mapping, pgoff_t start, pgoff_t end); +extern void generic_sync_sb_inodes(struct super_block *sb, + struct writeback_control *wbc); extern int write_inode_now(struct inode *, int); extern int filemap_fdatawrite(struct address_space *); extern int filemap_flush(struct address_space *); -- cgit v1.2.3 From 9c0c7a429a0cf02c2ac1998d5cf4c26f6be5c989 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Wed, 9 Jul 2008 15:48:45 +0200 Subject: ssb: Include dma-mapping.h ssb.h implements DMA mapping functions, so it should include dma-mapping.h. This fixes compile failures on certain architectures. Reported-by: Stephen Rothwell Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 0fe5a0ded3ea..4bf8cade9dbc 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -7,6 +7,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 341c2c958ec7bdd9f54733a8b0b432fe76842a82 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 May 2008 02:17:51 +0900 Subject: libata: consistently use msecs for time durations libata has been using mix of jiffies and msecs for time druations. This is getting confusing. As writing sub HZ values in jiffies is PITA and msecs_to_jiffies() can't be used as initializer, unify unit for all time durations to msecs. So, durations are in msecs and deadlines are in jiffies. ata_deadline() is added to compute deadline from a start time and duration in msecs. While at it, drop now superflous _msec suffix from arguments and rename @timeout to @deadline if it represents a fixed point in time rather than duration. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 44 +++++++++++++++++++++----------------------- drivers/ata/libata-eh.c | 33 +++++++++++++++++---------------- drivers/ata/libata-pmp.c | 3 ++- drivers/ata/libata-sff.c | 15 ++++++++------- drivers/ata/pata_bf54x.c | 6 +++--- drivers/ata/pata_scc.c | 2 +- include/linux/libata.h | 26 ++++++++++++++++---------- 7 files changed, 68 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 303fc0d2b978..c5c3b1b516e1 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -145,7 +144,7 @@ static int libata_dma_mask = ATA_DMA_MASK_ATA|ATA_DMA_MASK_ATAPI|ATA_DMA_MASK_CF module_param_named(dma, libata_dma_mask, int, 0444); MODULE_PARM_DESC(dma, "DMA enable/disable (0x1==ATA, 0x2==ATAPI, 0x4==CF)"); -static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ; +static int ata_probe_timeout = ATA_TMOUT_INTERNAL / 1000; module_param(ata_probe_timeout, int, 0444); MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); @@ -1533,7 +1532,7 @@ unsigned long ata_id_xfermask(const u16 *id) * @ap: The ata_port to queue port_task for * @fn: workqueue function to be scheduled * @data: data for @fn to use - * @delay: delay time for workqueue function + * @delay: delay time in msecs for workqueue function * * Schedule @fn(@data) for execution after @delay jiffies using * port_task. There is one port_task per port and it's the @@ -1552,7 +1551,7 @@ void ata_pio_queue_task(struct ata_port *ap, void *data, unsigned long delay) ap->port_task_data = data; /* may fail if ata_port_flush_task() in progress */ - queue_delayed_work(ata_wq, &ap->port_task, delay); + queue_delayed_work(ata_wq, &ap->port_task, msecs_to_jiffies(delay)); } /** @@ -1685,7 +1684,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, spin_unlock_irqrestore(ap->lock, flags); if (!timeout) - timeout = ata_probe_timeout * 1000 / HZ; + timeout = ata_probe_timeout * 1000; rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout)); @@ -3319,7 +3318,7 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)) { unsigned long start = jiffies; - unsigned long nodev_deadline = start + ATA_TMOUT_FF_WAIT; + unsigned long nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT); int warned = 0; if (time_after(nodev_deadline, deadline)) @@ -3387,7 +3386,7 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline, int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)) { - msleep(ATA_WAIT_AFTER_RESET_MSECS); + msleep(ATA_WAIT_AFTER_RESET); return ata_wait_ready(link, deadline, check_ready); } @@ -3417,13 +3416,13 @@ int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, int sata_link_debounce(struct ata_link *link, const unsigned long *params, unsigned long deadline) { - unsigned long interval_msec = params[0]; - unsigned long duration = msecs_to_jiffies(params[1]); + unsigned long interval = params[0]; + unsigned long duration = params[1]; unsigned long last_jiffies, t; u32 last, cur; int rc; - t = jiffies + msecs_to_jiffies(params[2]); + t = ata_deadline(jiffies, params[2]); if (time_before(t, deadline)) deadline = t; @@ -3435,7 +3434,7 @@ int sata_link_debounce(struct ata_link *link, const unsigned long *params, last_jiffies = jiffies; while (1) { - msleep(interval_msec); + msleep(interval); if ((rc = sata_scr_read(link, SCR_STATUS, &cur))) return rc; cur &= 0xf; @@ -3444,7 +3443,8 @@ int sata_link_debounce(struct ata_link *link, const unsigned long *params, if (cur == last) { if (cur == 1 && time_before(jiffies, deadline)) continue; - if (time_after(jiffies, last_jiffies + duration)) + if (time_after(jiffies, + ata_deadline(last_jiffies, duration))) return 0; continue; } @@ -3636,7 +3636,8 @@ int sata_link_hardreset(struct ata_link *link, const unsigned long *timing, if (check_ready) { unsigned long pmp_deadline; - pmp_deadline = jiffies + ATA_TMOUT_PMP_SRST_WAIT; + pmp_deadline = ata_deadline(jiffies, + ATA_TMOUT_PMP_SRST_WAIT); if (time_after(pmp_deadline, deadline)) pmp_deadline = deadline; ata_wait_ready(link, pmp_deadline, check_ready); @@ -6073,8 +6074,6 @@ static void __init ata_parse_force_param(void) static int __init ata_init(void) { - ata_probe_timeout *= HZ; - ata_parse_force_param(); ata_wq = create_workqueue("ata"); @@ -6127,8 +6126,8 @@ int ata_ratelimit(void) * @reg: IO-mapped register * @mask: Mask to apply to read register value * @val: Wait condition - * @interval_msec: polling interval in milliseconds - * @timeout_msec: timeout in milliseconds + * @interval: polling interval in milliseconds + * @timeout: timeout in milliseconds * * Waiting for some bits of register to change is a common * operation for ATA controllers. This function reads 32bit LE @@ -6146,10 +6145,9 @@ int ata_ratelimit(void) * The final register value. */ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, - unsigned long interval_msec, - unsigned long timeout_msec) + unsigned long interval, unsigned long timeout) { - unsigned long timeout; + unsigned long deadline; u32 tmp; tmp = ioread32(reg); @@ -6158,10 +6156,10 @@ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, * preceding writes reach the controller before starting to * eat away the timeout. */ - timeout = jiffies + (timeout_msec * HZ) / 1000; + deadline = ata_deadline(jiffies, timeout); - while ((tmp & mask) == val && time_before(jiffies, timeout)) { - msleep(interval_msec); + while ((tmp & mask) == val && time_before(jiffies, deadline)) { + msleep(interval); tmp = ioread32(reg); } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 7894d83ea1eb..08dd07f10008 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -66,15 +66,14 @@ enum { ATA_ECAT_DUBIOUS_TOUT_HSM = 6, ATA_ECAT_DUBIOUS_UNK_DEV = 7, ATA_ECAT_NR = 8, -}; -/* Waiting in ->prereset can never be reliable. It's sometimes nice - * to wait there but it can't be depended upon; otherwise, we wouldn't - * be resetting. Just give it enough time for most drives to spin up. - */ -enum { - ATA_EH_PRERESET_TIMEOUT = 10 * HZ, - ATA_EH_FASTDRAIN_INTERVAL = 3 * HZ, + /* Waiting in ->prereset can never be reliable. It's + * sometimes nice to wait there but it can't be depended upon; + * otherwise, we wouldn't be resetting. Just give it enough + * time for most drives to spin up. + */ + ATA_EH_PRERESET_TIMEOUT = 10000, + ATA_EH_FASTDRAIN_INTERVAL = 3000, }; /* The following table determines how we sequence resets. Each entry @@ -84,10 +83,10 @@ enum { * are mostly for error handling, hotplug and retarded devices. */ static const unsigned long ata_eh_reset_timeouts[] = { - 10 * HZ, /* most drives spin up by 10sec */ - 10 * HZ, /* > 99% working drives spin up before 20sec */ - 35 * HZ, /* give > 30 secs of idleness for retarded devices */ - 5 * HZ, /* and sweet one last chance */ + 10000, /* most drives spin up by 10sec */ + 10000, /* > 99% working drives spin up before 20sec */ + 35000, /* give > 30 secs of idleness for retarded devices */ + 5000, /* and sweet one last chance */ /* > 1 min has elapsed, give up */ }; @@ -641,7 +640,7 @@ void ata_eh_fastdrain_timerfn(unsigned long arg) /* some qcs have finished, give it another chance */ ap->fastdrain_cnt = cnt; ap->fastdrain_timer.expires = - jiffies + ATA_EH_FASTDRAIN_INTERVAL; + ata_deadline(jiffies, ATA_EH_FASTDRAIN_INTERVAL); add_timer(&ap->fastdrain_timer); } @@ -681,7 +680,8 @@ static void ata_eh_set_pending(struct ata_port *ap, int fastdrain) /* activate fast drain */ ap->fastdrain_cnt = cnt; - ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL; + ap->fastdrain_timer.expires = + ata_deadline(jiffies, ATA_EH_FASTDRAIN_INTERVAL); add_timer(&ap->fastdrain_timer); } @@ -2125,7 +2125,8 @@ int ata_eh_reset(struct ata_link *link, int classify, } if (prereset) { - rc = prereset(link, jiffies + ATA_EH_PRERESET_TIMEOUT); + rc = prereset(link, + ata_deadline(jiffies, ATA_EH_PRERESET_TIMEOUT)); if (rc) { if (rc == -ENOENT) { ata_link_printk(link, KERN_DEBUG, @@ -2160,7 +2161,7 @@ int ata_eh_reset(struct ata_link *link, int classify, if (ata_is_host_link(link)) ata_eh_freeze_port(ap); - deadline = jiffies + ata_eh_reset_timeouts[try++]; + deadline = ata_deadline(jiffies, ata_eh_reset_timeouts[try++]); if (reset) { if (verbose) diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 7daf4c0f6216..63691d77ac43 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -785,7 +785,8 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap) * SError.N working. */ sata_link_hardreset(link, sata_deb_timing_normal, - jiffies + ATA_TMOUT_INTERNAL_QUICK, NULL, NULL); + ata_deadline(jiffies, ATA_TMOUT_INTERNAL_QUICK), + NULL, NULL); /* unconditionally clear SError.N */ rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG); diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index c0908c225483..304fdc6f1dc2 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -345,8 +345,8 @@ void ata_sff_dma_pause(struct ata_port *ap) /** * ata_sff_busy_sleep - sleep until BSY clears, or timeout * @ap: port containing status register to be polled - * @tmout_pat: impatience timeout - * @tmout: overall timeout + * @tmout_pat: impatience timeout in msecs + * @tmout: overall timeout in msecs * * Sleep until ATA Status register bit BSY clears, * or a timeout occurs. @@ -365,7 +365,7 @@ int ata_sff_busy_sleep(struct ata_port *ap, status = ata_sff_busy_wait(ap, ATA_BUSY, 300); timer_start = jiffies; - timeout = timer_start + tmout_pat; + timeout = ata_deadline(timer_start, tmout_pat); while (status != 0xff && (status & ATA_BUSY) && time_before(jiffies, timeout)) { msleep(50); @@ -377,7 +377,7 @@ int ata_sff_busy_sleep(struct ata_port *ap, "port is slow to respond, please be patient " "(Status 0x%x)\n", status); - timeout = timer_start + tmout; + timeout = ata_deadline(timer_start, tmout); while (status != 0xff && (status & ATA_BUSY) && time_before(jiffies, timeout)) { msleep(50); @@ -390,7 +390,7 @@ int ata_sff_busy_sleep(struct ata_port *ap, if (status & ATA_BUSY) { ata_port_printk(ap, KERN_ERR, "port failed to respond " "(%lu secs, Status 0x%x)\n", - tmout / HZ, status); + DIV_ROUND_UP(tmout, 1000), status); return -EBUSY; } @@ -1888,7 +1888,7 @@ int ata_sff_wait_after_reset(struct ata_link *link, unsigned int devmask, unsigned int dev1 = devmask & (1 << 1); int rc, ret = 0; - msleep(ATA_WAIT_AFTER_RESET_MSECS); + msleep(ATA_WAIT_AFTER_RESET); /* always check readiness of the master device */ rc = ata_sff_wait_ready(link, deadline); @@ -2371,7 +2371,8 @@ void ata_bus_reset(struct ata_port *ap) /* issue bus reset */ if (ap->flags & ATA_FLAG_SRST) { - rc = ata_bus_softreset(ap, devmask, jiffies + 40 * HZ); + rc = ata_bus_softreset(ap, devmask, + ata_deadline(jiffies, 40000)); if (rc && rc != -ENODEV) goto err_out; } diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index 55516103626a..d3932901a3b3 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -1011,7 +1011,7 @@ static void bfin_bus_post_reset(struct ata_port *ap, unsigned int devmask) void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; unsigned int dev0 = devmask & (1 << 0); unsigned int dev1 = devmask & (1 << 1); - unsigned long timeout; + unsigned long deadline; /* if device 0 was found in ata_devchk, wait for its * BSY bit to clear @@ -1022,7 +1022,7 @@ static void bfin_bus_post_reset(struct ata_port *ap, unsigned int devmask) /* if device 1 was found in ata_devchk, wait for * register access, then wait for BSY to clear */ - timeout = jiffies + ATA_TMOUT_BOOT; + deadline = ata_deadline(jiffies, ATA_TMOUT_BOOT); while (dev1) { u8 nsect, lbal; @@ -1031,7 +1031,7 @@ static void bfin_bus_post_reset(struct ata_port *ap, unsigned int devmask) lbal = read_atapi_register(base, ATA_REG_LBAL); if ((nsect == 1) && (lbal == 1)) break; - if (time_after(jiffies, timeout)) { + if (time_after(jiffies, deadline)) { dev1 = 0; break; } diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index bbf5aa345e68..16673d168573 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -696,7 +696,7 @@ static void scc_bmdma_stop (struct ata_queued_cmd *qc) if (reg & INTSTS_BMSINT) { unsigned int classes; - unsigned long deadline = jiffies + ATA_TMOUT_BOOT; + unsigned long deadline = ata_deadline(jiffies, ATA_TMOUT_BOOT); printk(KERN_WARNING "%s: Internal Bus Error\n", DRV_NAME); out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMSINT); /* TBD: SW reset */ diff --git a/include/linux/libata.h b/include/linux/libata.h index e57e5d08312d..94110b652b30 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -27,6 +27,7 @@ #define __LINUX_LIBATA_H__ #include +#include #include #include #include @@ -115,7 +116,7 @@ enum { /* tag ATA_MAX_QUEUE - 1 is reserved for internal commands */ ATA_MAX_QUEUE = 32, ATA_TAG_INTERNAL = ATA_MAX_QUEUE - 1, - ATA_SHORT_PAUSE = (HZ >> 6) + 1, + ATA_SHORT_PAUSE = 16, ATAPI_MAX_DRAIN = 16 << 10, @@ -234,17 +235,17 @@ enum { /* bits 24:31 of host->flags are reserved for LLD specific flags */ /* various lengths of time */ - ATA_TMOUT_BOOT = 30 * HZ, /* heuristic */ - ATA_TMOUT_BOOT_QUICK = 7 * HZ, /* heuristic */ - ATA_TMOUT_INTERNAL = 30 * HZ, - ATA_TMOUT_INTERNAL_QUICK = 5 * HZ, + ATA_TMOUT_BOOT = 30000, /* heuristic */ + ATA_TMOUT_BOOT_QUICK = 7000, /* heuristic */ + ATA_TMOUT_INTERNAL = 30000, + ATA_TMOUT_INTERNAL_QUICK = 5000, /* FIXME: GoVault needs 2s but we can't afford that without * parallel probing. 800ms is enough for iVDR disk * HHD424020F7SV00. Increase to 2secs when parallel probing * is in place. */ - ATA_TMOUT_FF_WAIT = 4 * HZ / 5, + ATA_TMOUT_FF_WAIT = 800, /* Spec mandates to wait for ">= 2ms" before checking status * after reset. We wait 150ms, because that was the magic @@ -256,14 +257,14 @@ enum { * * Old drivers/ide uses the 2mS rule and then waits for ready. */ - ATA_WAIT_AFTER_RESET_MSECS = 150, + ATA_WAIT_AFTER_RESET = 150, /* If PMP is supported, we have to do follow-up SRST. As some * PMPs don't send D2H Reg FIS after hardreset, LLDs are * advised to wait only for the following duration before * doing SRST. */ - ATA_TMOUT_PMP_SRST_WAIT = 1 * HZ, + ATA_TMOUT_PMP_SRST_WAIT = 1000, /* ATA bus states */ BUS_UNKNOWN = 0, @@ -895,8 +896,7 @@ extern void ata_host_resume(struct ata_host *host); #endif extern int ata_ratelimit(void); extern u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, - unsigned long interval_msec, - unsigned long timeout_msec); + unsigned long interval, unsigned long timeout); extern int atapi_cmd_type(u8 opcode); extern void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis); @@ -1389,6 +1389,12 @@ static inline int ata_check_ready(u8 status) return 0; } +static inline unsigned long ata_deadline(unsigned long from_jiffies, + unsigned long timeout_msecs) +{ + return from_jiffies + msecs_to_jiffies(timeout_msecs); +} + /************************************************************************** * PMP - drivers/ata/libata-pmp.c -- cgit v1.2.3 From 0a2c0f56159999e20015241d3b8fa89b1ab14309 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 May 2008 02:17:52 +0900 Subject: libata: improve EH retry delay handling EH retries were delayed by 5 seconds to ensure that resets don't occur back-to-back. However, this 5 second delay is superflous or excessive in many cases. For example, after IDENTIFY times out, there's no reason to wait five more seconds before retrying. This patch adds ehc->last_reset timestamp and record the timestamp for the last reset trial or success and uses it to space resets by ATA_EH_RESET_COOL_DOWN which is 5 secs and removes unconditional 5 sec sleeps. As this change makes inter-try waits often shorter and they're redundant in nature, this patch also removes the "retrying..." messages. While at it, convert explicit rounding up division to DIV_ROUND_UP(). This change speeds up EH in many cases w/o sacrificing robustness. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 38 ++++++++++++++++++++------------------ drivers/ata/libata-pmp.c | 10 ---------- include/linux/libata.h | 2 ++ 3 files changed, 22 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 08dd07f10008..5b5ae631ed03 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -67,6 +67,9 @@ enum { ATA_ECAT_DUBIOUS_UNK_DEV = 7, ATA_ECAT_NR = 8, + /* always put at least this amount of time between resets */ + ATA_EH_RESET_COOL_DOWN = 5000, + /* Waiting in ->prereset can never be reliable. It's * sometimes nice to wait there but it can't be depended upon; * otherwise, we wouldn't be resetting. Just give it enough @@ -485,6 +488,9 @@ void ata_scsi_error(struct Scsi_Host *host) if (ata_ncq_enabled(dev)) ehc->saved_ncq_enabled |= 1 << devno; } + + /* set last reset timestamp to some time in the past */ + ehc->last_reset = jiffies - 60 * HZ; } ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; @@ -2088,11 +2094,17 @@ int ata_eh_reset(struct ata_link *link, int classify, /* * Prepare to reset */ + now = jiffies; + deadline = ata_deadline(ehc->last_reset, ATA_EH_RESET_COOL_DOWN); + if (time_before(now, deadline)) + schedule_timeout_uninterruptible(deadline - now); + spin_lock_irqsave(ap->lock, flags); ap->pflags |= ATA_PFLAG_RESETTING; spin_unlock_irqrestore(ap->lock, flags); ata_eh_about_to_do(link, NULL, ATA_EH_RESET); + ehc->last_reset = jiffies; ata_link_for_each_dev(dev, link) { /* If we issue an SRST then an ATA drive (not ATAPI) @@ -2158,6 +2170,7 @@ int ata_eh_reset(struct ata_link *link, int classify, /* * Perform reset */ + ehc->last_reset = jiffies; if (ata_is_host_link(link)) ata_eh_freeze_port(ap); @@ -2278,6 +2291,7 @@ int ata_eh_reset(struct ata_link *link, int classify, /* reset successful, schedule revalidation */ ata_eh_done(link, NULL, ATA_EH_RESET); + ehc->last_reset = jiffies; ehc->i.action |= ATA_EH_REVALIDATE; rc = 0; @@ -2304,9 +2318,9 @@ int ata_eh_reset(struct ata_link *link, int classify, if (time_before(now, deadline)) { unsigned long delta = deadline - now; - ata_link_printk(link, KERN_WARNING, "reset failed " - "(errno=%d), retrying in %u secs\n", - rc, (jiffies_to_msecs(delta) + 999) / 1000); + ata_link_printk(link, KERN_WARNING, + "reset failed (errno=%d), retrying in %u secs\n", + rc, DIV_ROUND_UP(jiffies_to_msecs(delta), 1000)); while (delta) delta = schedule_timeout_uninterruptible(delta); @@ -2623,7 +2637,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, { struct ata_link *link; struct ata_device *dev; - int nr_failed_devs, nr_disabled_devs; + int nr_failed_devs; int rc; unsigned long flags; @@ -2666,7 +2680,6 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, retry: rc = 0; nr_failed_devs = 0; - nr_disabled_devs = 0; /* if UNLOADING, finish immediately */ if (ap->pflags & ATA_PFLAG_UNLOADING) @@ -2733,8 +2746,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, dev_fail: nr_failed_devs++; - if (ata_eh_handle_dev_fail(dev, rc)) - nr_disabled_devs++; + ata_eh_handle_dev_fail(dev, rc); if (ap->pflags & ATA_PFLAG_FROZEN) { /* PMP reset requires working host port. @@ -2746,18 +2758,8 @@ dev_fail: } } - if (nr_failed_devs) { - if (nr_failed_devs != nr_disabled_devs) { - ata_port_printk(ap, KERN_WARNING, "failed to recover " - "some devices, retrying in 5 secs\n"); - ssleep(5); - } else { - /* no device left to recover, repeat fast */ - msleep(500); - } - + if (nr_failed_devs) goto retry; - } out: if (rc && r_failed_link) diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 63691d77ac43..b65db309c181 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -727,19 +727,12 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap, } if (tries) { - int sleep = ehc->i.flags & ATA_EHI_DID_RESET; - /* consecutive revalidation failures? speed down */ if (reval_failed) sata_down_spd_limit(link); else reval_failed = 1; - ata_dev_printk(dev, KERN_WARNING, - "retrying reset%s\n", - sleep ? " in 5 secs" : ""); - if (sleep) - ssleep(5); ehc->i.action |= ATA_EH_RESET; goto retry; } else { @@ -991,10 +984,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap) goto retry; if (--pmp_tries) { - ata_port_printk(ap, KERN_WARNING, - "failed to recover PMP, retrying in 5 secs\n"); pmp_ehc->i.action |= ATA_EH_RESET; - ssleep(5); goto retry; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 94110b652b30..9058c2a325a9 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -602,6 +602,8 @@ struct ata_eh_context { unsigned int did_probe_mask; unsigned int saved_ncq_enabled; u8 saved_xfer_mode[ATA_MAX_DEVICES]; + /* timestamp for the last reset attempt or success */ + unsigned long last_reset; }; struct ata_acpi_drive -- cgit v1.2.3 From 87fbc5a060faf2394bee88a93519f9b9d434727c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 May 2008 02:17:54 +0900 Subject: libata: improve EH internal command timeout handling ATA_TMOUT_INTERNAL which was 30secs were used for all internal commands which is way too long when something goes wrong. This patch implements command type based stepped timeouts. Different command types can use different timeouts and each command type can use different timeout values after timeouts. ie. the initial timeout is set to a value which should cover most of the cases but not too long so that run away cases don't delay things too much. After the first try times out, the second try can use longer timeout and if that one times out too, it can go for full 30sec timeout. IDENTIFYs use 5s - 10s - 30s timeout and all other commands use 5s - 10s timeouts. This patch significantly cuts down the needed time to handle failure cases while still allowing libata to work with nut job devices through retries. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 16 ++++-- drivers/ata/libata-eh.c | 121 +++++++++++++++++++++++++++++++++++++++++++++- drivers/ata/libata.h | 2 + include/linux/libata.h | 8 ++- 4 files changed, 142 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c5c3b1b516e1..9bef1a84fe3f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -144,7 +144,7 @@ static int libata_dma_mask = ATA_DMA_MASK_ATA|ATA_DMA_MASK_ATAPI|ATA_DMA_MASK_CF module_param_named(dma, libata_dma_mask, int, 0444); MODULE_PARM_DESC(dma, "DMA enable/disable (0x1==ATA, 0x2==ATAPI, 0x4==CF)"); -static int ata_probe_timeout = ATA_TMOUT_INTERNAL / 1000; +static int ata_probe_timeout; module_param(ata_probe_timeout, int, 0444); MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); @@ -1611,6 +1611,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, struct ata_link *link = dev->link; struct ata_port *ap = link->ap; u8 command = tf->command; + int auto_timeout = 0; struct ata_queued_cmd *qc; unsigned int tag, preempted_tag; u32 preempted_sactive, preempted_qc_active; @@ -1683,8 +1684,14 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, spin_unlock_irqrestore(ap->lock, flags); - if (!timeout) - timeout = ata_probe_timeout * 1000; + if (!timeout) { + if (ata_probe_timeout) + timeout = ata_probe_timeout * 1000; + else { + timeout = ata_internal_cmd_timeout(dev, command); + auto_timeout = 1; + } + } rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout)); @@ -1760,6 +1767,9 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, spin_unlock_irqrestore(ap->lock, flags); + if ((err_mask & AC_ERR_TIMEOUT) && auto_timeout) + ata_internal_cmd_timed_out(dev, command); + return err_mask; } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 83d1451fa714..d5f03a6e3334 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -67,6 +67,8 @@ enum { ATA_ECAT_DUBIOUS_UNK_DEV = 7, ATA_ECAT_NR = 8, + ATA_EH_CMD_DFL_TIMEOUT = 5000, + /* always put at least this amount of time between resets */ ATA_EH_RESET_COOL_DOWN = 5000, @@ -93,6 +95,53 @@ static const unsigned long ata_eh_reset_timeouts[] = { ULONG_MAX, /* > 1 min has elapsed, give up */ }; +static const unsigned long ata_eh_identify_timeouts[] = { + 5000, /* covers > 99% of successes and not too boring on failures */ + 10000, /* combined time till here is enough even for media access */ + 30000, /* for true idiots */ + ULONG_MAX, +}; + +static const unsigned long ata_eh_other_timeouts[] = { + 5000, /* same rationale as identify timeout */ + 10000, /* ditto */ + /* but no merciful 30sec for other commands, it just isn't worth it */ + ULONG_MAX, +}; + +struct ata_eh_cmd_timeout_ent { + const u8 *commands; + const unsigned long *timeouts; +}; + +/* The following table determines timeouts to use for EH internal + * commands. Each table entry is a command class and matches the + * commands the entry applies to and the timeout table to use. + * + * On the retry after a command timed out, the next timeout value from + * the table is used. If the table doesn't contain further entries, + * the last value is used. + * + * ehc->cmd_timeout_idx keeps track of which timeout to use per + * command class, so if SET_FEATURES times out on the first try, the + * next try will use the second timeout value only for that class. + */ +#define CMDS(cmds...) (const u8 []){ cmds, 0 } +static const struct ata_eh_cmd_timeout_ent +ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { + { .commands = CMDS(ATA_CMD_ID_ATA, ATA_CMD_ID_ATAPI), + .timeouts = ata_eh_identify_timeouts, }, + { .commands = CMDS(ATA_CMD_READ_NATIVE_MAX, ATA_CMD_READ_NATIVE_MAX_EXT), + .timeouts = ata_eh_other_timeouts, }, + { .commands = CMDS(ATA_CMD_SET_MAX, ATA_CMD_SET_MAX_EXT), + .timeouts = ata_eh_other_timeouts, }, + { .commands = CMDS(ATA_CMD_SET_FEATURES), + .timeouts = ata_eh_other_timeouts, }, + { .commands = CMDS(ATA_CMD_INIT_DEV_PARAMS), + .timeouts = ata_eh_other_timeouts, }, +}; +#undef CMDS + static void __ata_port_freeze(struct ata_port *ap); #ifdef CONFIG_PM static void ata_eh_handle_port_suspend(struct ata_port *ap); @@ -238,6 +287,73 @@ void ata_port_pbar_desc(struct ata_port *ap, int bar, ssize_t offset, #endif /* CONFIG_PCI */ +static int ata_lookup_timeout_table(u8 cmd) +{ + int i; + + for (i = 0; i < ATA_EH_CMD_TIMEOUT_TABLE_SIZE; i++) { + const u8 *cur; + + for (cur = ata_eh_cmd_timeout_table[i].commands; *cur; cur++) + if (*cur == cmd) + return i; + } + + return -1; +} + +/** + * ata_internal_cmd_timeout - determine timeout for an internal command + * @dev: target device + * @cmd: internal command to be issued + * + * Determine timeout for internal command @cmd for @dev. + * + * LOCKING: + * EH context. + * + * RETURNS: + * Determined timeout. + */ +unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd) +{ + struct ata_eh_context *ehc = &dev->link->eh_context; + int ent = ata_lookup_timeout_table(cmd); + int idx; + + if (ent < 0) + return ATA_EH_CMD_DFL_TIMEOUT; + + idx = ehc->cmd_timeout_idx[dev->devno][ent]; + return ata_eh_cmd_timeout_table[ent].timeouts[idx]; +} + +/** + * ata_internal_cmd_timed_out - notification for internal command timeout + * @dev: target device + * @cmd: internal command which timed out + * + * Notify EH that internal command @cmd for @dev timed out. This + * function should be called only for commands whose timeouts are + * determined using ata_internal_cmd_timeout(). + * + * LOCKING: + * EH context. + */ +void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd) +{ + struct ata_eh_context *ehc = &dev->link->eh_context; + int ent = ata_lookup_timeout_table(cmd); + int idx; + + if (ent < 0) + return; + + idx = ehc->cmd_timeout_idx[dev->devno][ent]; + if (ata_eh_cmd_timeout_table[ent].timeouts[idx + 1] != ULONG_MAX) + ehc->cmd_timeout_idx[dev->devno][ent]++; +} + static void ata_ering_record(struct ata_ering *ering, unsigned int eflags, unsigned int err_mask) { @@ -2600,8 +2716,11 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err) ata_eh_detach_dev(dev); /* schedule probe if necessary */ - if (ata_eh_schedule_probe(dev)) + if (ata_eh_schedule_probe(dev)) { ehc->tries[dev->devno] = ATA_EH_DEV_TRIES; + memset(ehc->cmd_timeout_idx[dev->devno], 0, + sizeof(ehc->cmd_timeout_idx[dev->devno])); + } return 1; } else { diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1cf803adbc95..f6f9c28ec7f8 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -151,6 +151,8 @@ extern void ata_scsi_dev_rescan(struct work_struct *work); extern int ata_bus_probe(struct ata_port *ap); /* libata-eh.c */ +extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); +extern void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd); extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); extern void ata_port_wait_eh(struct ata_port *ap); diff --git a/include/linux/libata.h b/include/linux/libata.h index 9058c2a325a9..035f8e1cd0ac 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -237,7 +237,6 @@ enum { /* various lengths of time */ ATA_TMOUT_BOOT = 30000, /* heuristic */ ATA_TMOUT_BOOT_QUICK = 7000, /* heuristic */ - ATA_TMOUT_INTERNAL = 30000, ATA_TMOUT_INTERNAL_QUICK = 5000, /* FIXME: GoVault needs 2s but we can't afford that without @@ -341,6 +340,11 @@ enum { SATA_PMP_RW_TIMEOUT = 3000, /* PMP read/write timeout */ + /* This should match the actual table size of + * ata_eh_cmd_timeout_table in libata-eh.c. + */ + ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 5, + /* Horkage types. May be set by libata or controller on drives (some horkage may be drive/controller pair dependant */ @@ -598,6 +602,8 @@ struct ata_eh_info { struct ata_eh_context { struct ata_eh_info i; int tries[ATA_MAX_DEVICES]; + int cmd_timeout_idx[ATA_MAX_DEVICES] + [ATA_EH_CMD_TIMEOUT_TABLE_SIZE]; unsigned int classes[ATA_MAX_DEVICES]; unsigned int did_probe_mask; unsigned int saved_ncq_enabled; -- cgit v1.2.3 From 18f7ba4c2f4be6b37d925931f04d6cc28d88d1ee Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Tue, 3 Jun 2008 10:33:55 -0700 Subject: libata/ahci: enclosure management support Add Enclosure Management support to libata and ahci. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 321 +++++++++++++++++++++++++++++++++++++++++++++- drivers/ata/libata-scsi.c | 79 ++++++++++++ include/linux/libata.h | 21 +++ 3 files changed, 419 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 5e6468a7ca4b..65d4e968feb4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -56,6 +56,12 @@ MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip) static int ahci_enable_alpm(struct ata_port *ap, enum link_pm policy); static void ahci_disable_alpm(struct ata_port *ap); +static ssize_t ahci_led_show(struct ata_port *ap, char *buf); +static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, + size_t size); +static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size); +#define MAX_SLOTS 8 enum { AHCI_PCI_BAR = 5, @@ -98,6 +104,8 @@ enum { HOST_IRQ_STAT = 0x08, /* interrupt status */ HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ + HOST_EM_LOC = 0x1c, /* Enclosure Management location */ + HOST_EM_CTL = 0x20, /* Enclosure Management Control */ /* HOST_CTL bits */ HOST_RESET = (1 << 0), /* reset controller; self-clear */ @@ -105,6 +113,7 @@ enum { HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ /* HOST_CAP bits */ + HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */ HOST_CAP_SSC = (1 << 14), /* Slumber capable */ HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ HOST_CAP_CLO = (1 << 24), /* Command List Override support */ @@ -202,6 +211,11 @@ enum { ATA_FLAG_IPM, ICH_MAP = 0x90, /* ICH MAP register */ + + /* em_ctl bits */ + EM_CTL_RST = (1 << 9), /* Reset */ + EM_CTL_TM = (1 << 8), /* Transmit Message */ + EM_CTL_ALHD = (1 << 26), /* Activity LED */ }; struct ahci_cmd_hdr { @@ -219,12 +233,21 @@ struct ahci_sg { __le32 flags_size; }; +struct ahci_em_priv { + enum sw_activity blink_policy; + struct timer_list timer; + unsigned long saved_activity; + unsigned long activity; + unsigned long led_state; +}; + struct ahci_host_priv { unsigned int flags; /* AHCI_HFLAG_* */ u32 cap; /* cap to use */ u32 port_map; /* port map to use */ u32 saved_cap; /* saved initial cap */ u32 saved_port_map; /* saved initial port_map */ + u32 em_loc; /* enclosure management location */ }; struct ahci_port_priv { @@ -240,6 +263,8 @@ struct ahci_port_priv { unsigned int ncq_saw_dmas:1; unsigned int ncq_saw_sdb:1; u32 intr_mask; /* interrupts to enable */ + struct ahci_em_priv em_priv[MAX_SLOTS];/* enclosure management info + * per PM slot */ }; static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); @@ -277,9 +302,20 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int ahci_pci_device_resume(struct pci_dev *pdev); #endif +static ssize_t ahci_activity_show(struct ata_device *dev, char *buf); +static ssize_t ahci_activity_store(struct ata_device *dev, + enum sw_activity val); +static void ahci_init_sw_activity(struct ata_link *link); static struct device_attribute *ahci_shost_attrs[] = { &dev_attr_link_power_management_policy, + &dev_attr_em_message_type, + &dev_attr_em_message, + NULL +}; + +static struct device_attribute *ahci_sdev_attrs[] = { + &dev_attr_sw_activity, NULL }; @@ -289,6 +325,7 @@ static struct scsi_host_template ahci_sht = { .sg_tablesize = AHCI_MAX_SG, .dma_boundary = AHCI_DMA_BOUNDARY, .shost_attrs = ahci_shost_attrs, + .sdev_attrs = ahci_sdev_attrs, }; static struct ata_port_operations ahci_ops = { @@ -316,6 +353,10 @@ static struct ata_port_operations ahci_ops = { .enable_pm = ahci_enable_alpm, .disable_pm = ahci_disable_alpm, + .em_show = ahci_led_show, + .em_store = ahci_led_store, + .sw_activity_show = ahci_activity_show, + .sw_activity_store = ahci_activity_store, #ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume, @@ -561,6 +602,11 @@ static struct pci_driver ahci_pci_driver = { #endif }; +static int ahci_em_messages = 1; +module_param(ahci_em_messages, int, 0444); +/* add other LED protocol types when they become supported */ +MODULE_PARM_DESC(ahci_em_messages, + "Set AHCI Enclosure Management Message type (0 = disabled, 1 = LED"); static inline int ahci_nr_ports(u32 cap) { @@ -1031,11 +1077,28 @@ static void ahci_power_down(struct ata_port *ap) static void ahci_start_port(struct ata_port *ap) { + struct ahci_port_priv *pp = ap->private_data; + struct ata_link *link; + struct ahci_em_priv *emp; + /* enable FIS reception */ ahci_start_fis_rx(ap); /* enable DMA */ ahci_start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { + ata_port_for_each_link(link, ap) { + emp = &pp->em_priv[link->pmp]; + ahci_transmit_led_message(ap, emp->led_state, 4); + } + } + + if (ap->flags & ATA_FLAG_SW_ACTIVITY) + ata_port_for_each_link(link, ap) + ahci_init_sw_activity(link); + } static int ahci_deinit_port(struct ata_port *ap, const char **emsg) @@ -1116,6 +1179,230 @@ static int ahci_reset_controller(struct ata_host *host) return 0; } +static void ahci_sw_activity(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + + if (!(link->flags & ATA_LFLAG_SW_ACTIVITY)) + return; + + emp->activity++; + if (!timer_pending(&emp->timer)) + mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10)); +} + +static void ahci_sw_activity_blink(unsigned long arg) +{ + struct ata_link *link = (struct ata_link *)arg; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + unsigned long led_message = emp->led_state; + u32 activity_led_state; + + led_message &= 0xffff0000; + led_message |= ap->port_no | (link->pmp << 8); + + /* check to see if we've had activity. If so, + * toggle state of LED and reset timer. If not, + * turn LED to desired idle state. + */ + if (emp->saved_activity != emp->activity) { + emp->saved_activity = emp->activity; + /* get the current LED state */ + activity_led_state = led_message & 0x00010000; + + if (activity_led_state) + activity_led_state = 0; + else + activity_led_state = 1; + + /* clear old state */ + led_message &= 0xfff8ffff; + + /* toggle state */ + led_message |= (activity_led_state << 16); + mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100)); + } else { + /* switch to idle */ + led_message &= 0xfff8ffff; + if (emp->blink_policy == BLINK_OFF) + led_message |= (1 << 16); + } + ahci_transmit_led_message(ap, led_message, 4); +} + +static void ahci_init_sw_activity(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + + /* init activity stats, setup timer */ + emp->saved_activity = emp->activity = 0; + setup_timer(&emp->timer, ahci_sw_activity_blink, (unsigned long)link); + + /* check our blink policy and set flag for link if it's enabled */ + if (emp->blink_policy) + link->flags |= ATA_LFLAG_SW_ACTIVITY; +} + +static int ahci_reset_em(struct ata_host *host) +{ + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + u32 em_ctl; + + em_ctl = readl(mmio + HOST_EM_CTL); + if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST)) + return -EINVAL; + + writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL); + return 0; +} + +static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; + u32 em_ctl; + u32 message[] = {0, 0}; + unsigned int flags; + int pmp; + struct ahci_em_priv *emp; + + /* get the slot number from the message */ + pmp = (state & 0x0000ff00) >> 8; + if (pmp < MAX_SLOTS) + emp = &pp->em_priv[pmp]; + else + return -EINVAL; + + spin_lock_irqsave(ap->lock, flags); + + /* + * if we are still busy transmitting a previous message, + * do not allow + */ + em_ctl = readl(mmio + HOST_EM_CTL); + if (em_ctl & EM_CTL_TM) { + spin_unlock_irqrestore(ap->lock, flags); + return -EINVAL; + } + + /* + * create message header - this is all zero except for + * the message size, which is 4 bytes. + */ + message[0] |= (4 << 8); + + /* ignore 0:4 of byte zero, fill in port info yourself */ + message[1] = ((state & 0xfffffff0) | ap->port_no); + + /* write message to EM_LOC */ + writel(message[0], mmio + hpriv->em_loc); + writel(message[1], mmio + hpriv->em_loc+4); + + /* save off new led state for port/slot */ + emp->led_state = message[1]; + + /* + * tell hardware to transmit the message + */ + writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); + + spin_unlock_irqrestore(ap->lock, flags); + return size; +} + +static ssize_t ahci_led_show(struct ata_port *ap, char *buf) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ata_link *link; + struct ahci_em_priv *emp; + int rc = 0; + + ata_port_for_each_link(link, ap) { + emp = &pp->em_priv[link->pmp]; + rc += sprintf(buf, "%lx\n", emp->led_state); + } + return rc; +} + +static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, + size_t size) +{ + int state; + int pmp; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp; + + state = simple_strtoul(buf, NULL, 0); + + /* get the slot number from the message */ + pmp = (state & 0x0000ff00) >> 8; + if (pmp < MAX_SLOTS) + emp = &pp->em_priv[pmp]; + else + return -EINVAL; + + /* mask off the activity bits if we are in sw_activity + * mode, user should turn off sw_activity before setting + * activity led through em_message + */ + if (emp->blink_policy) + state &= 0xfff8ffff; + + return ahci_transmit_led_message(ap, state, size); +} + +static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) +{ + struct ata_link *link = dev->link; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + u32 port_led_state = emp->led_state; + + /* save the desired Activity LED behavior */ + if (val == OFF) { + /* clear LFLAG */ + link->flags &= ~(ATA_LFLAG_SW_ACTIVITY); + + /* set the LED to OFF */ + port_led_state &= 0xfff80000; + port_led_state |= (ap->port_no | (link->pmp << 8)); + ahci_transmit_led_message(ap, port_led_state, 4); + } else { + link->flags |= ATA_LFLAG_SW_ACTIVITY; + if (val == BLINK_OFF) { + /* set LED to ON for idle */ + port_led_state &= 0xfff80000; + port_led_state |= (ap->port_no | (link->pmp << 8)); + port_led_state |= 0x00010000; /* check this */ + ahci_transmit_led_message(ap, port_led_state, 4); + } + } + emp->blink_policy = val; + return 0; +} + +static ssize_t ahci_activity_show(struct ata_device *dev, char *buf) +{ + struct ata_link *link = dev->link; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + + /* display the saved value of activity behavior for this + * disk. + */ + return sprintf(buf, "%d\n", emp->blink_policy); +} + static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap, int port_no, void __iomem *mmio, void __iomem *port_mmio) @@ -1848,6 +2135,8 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + ahci_sw_activity(qc->dev->link); + return 0; } @@ -2154,7 +2443,8 @@ static void ahci_print_info(struct ata_host *host) dev_printk(KERN_INFO, &pdev->dev, "flags: " "%s%s%s%s%s%s%s" - "%s%s%s%s%s%s%s\n" + "%s%s%s%s%s%s%s" + "%s\n" , cap & (1 << 31) ? "64bit " : "", @@ -2171,7 +2461,8 @@ static void ahci_print_info(struct ata_host *host) cap & (1 << 17) ? "pmp " : "", cap & (1 << 15) ? "pio " : "", cap & (1 << 14) ? "slum " : "", - cap & (1 << 13) ? "part " : "" + cap & (1 << 13) ? "part " : "", + cap & (1 << 6) ? "ems ": "" ); } @@ -2291,6 +2582,24 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (hpriv->cap & HOST_CAP_PMP) pi.flags |= ATA_FLAG_PMP; + if (ahci_em_messages && (hpriv->cap & HOST_CAP_EMS)) { + u8 messages; + void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; + u32 em_loc = readl(mmio + HOST_EM_LOC); + u32 em_ctl = readl(mmio + HOST_EM_CTL); + + messages = (em_ctl & 0x000f0000) >> 16; + + /* we only support LED message type right now */ + if ((messages & 0x01) && (ahci_em_messages == 1)) { + /* store em_loc */ + hpriv->em_loc = ((em_loc >> 16) * 4); + pi.flags |= ATA_FLAG_EM; + if (!(em_ctl & EM_CTL_ALHD)) + pi.flags |= ATA_FLAG_SW_ACTIVITY; + } + } + /* CAP.NP sometimes indicate the index of the last enabled * port, at other times, that of the last possible port, so * determining the maximum port number requires looking at @@ -2304,6 +2613,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) host->iomap = pcim_iomap_table(pdev); host->private_data = hpriv; + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; @@ -2314,6 +2626,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* set initial link pm policy */ ap->pm_policy = NOT_AVAILABLE; + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = ahci_em_messages; + + /* disabled/not-implemented port */ if (!(hpriv->port_map & (1 << i))) ap->ops = &ata_dummy_port_ops; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 57a43649a461..b578b11caa7b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -190,6 +190,85 @@ static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); } +static ssize_t +ata_scsi_em_message_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + if (ap->ops->em_store && (ap->flags & ATA_FLAG_EM)) + return ap->ops->em_store(ap, buf, count); + return -EINVAL; +} + +static ssize_t +ata_scsi_em_message_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + + if (ap->ops->em_show && (ap->flags & ATA_FLAG_EM)) + return ap->ops->em_show(ap, buf); + return -EINVAL; +} +DEVICE_ATTR(em_message, S_IRUGO | S_IWUGO, + ata_scsi_em_message_show, ata_scsi_em_message_store); +EXPORT_SYMBOL_GPL(dev_attr_em_message); + +static ssize_t +ata_scsi_em_message_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + + return snprintf(buf, 23, "%d\n", ap->em_message_type); +} +DEVICE_ATTR(em_message_type, S_IRUGO, + ata_scsi_em_message_type_show, NULL); +EXPORT_SYMBOL_GPL(dev_attr_em_message_type); + +static ssize_t +ata_scsi_activity_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ata_port *ap = ata_shost_to_port(sdev->host); + struct ata_device *atadev = ata_scsi_find_dev(ap, sdev); + + if (ap->ops->sw_activity_show && (ap->flags & ATA_FLAG_SW_ACTIVITY)) + return ap->ops->sw_activity_show(atadev, buf); + return -EINVAL; +} + +static ssize_t +ata_scsi_activity_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ata_port *ap = ata_shost_to_port(sdev->host); + struct ata_device *atadev = ata_scsi_find_dev(ap, sdev); + enum sw_activity val; + int rc; + + if (ap->ops->sw_activity_store && (ap->flags & ATA_FLAG_SW_ACTIVITY)) { + val = simple_strtoul(buf, NULL, 0); + switch (val) { + case OFF: case BLINK_ON: case BLINK_OFF: + rc = ap->ops->sw_activity_store(atadev, val); + if (!rc) + return count; + else + return rc; + } + } + return -EINVAL; +} +DEVICE_ATTR(sw_activity, S_IWUGO | S_IRUGO, ata_scsi_activity_show, + ata_scsi_activity_store); +EXPORT_SYMBOL_GPL(dev_attr_sw_activity); + static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 035f8e1cd0ac..5b247b8a6b3b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -169,6 +169,7 @@ enum { ATA_LFLAG_ASSUME_CLASS = ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB, ATA_LFLAG_NO_RETRY = (1 << 5), /* don't retry this link */ ATA_LFLAG_DISABLED = (1 << 6), /* link is disabled */ + ATA_LFLAG_SW_ACTIVITY = (1 << 7), /* keep activity stats */ /* struct ata_port flags */ ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ @@ -191,6 +192,10 @@ enum { ATA_FLAG_AN = (1 << 18), /* controller supports AN */ ATA_FLAG_PMP = (1 << 19), /* controller supports PMP */ ATA_FLAG_IPM = (1 << 20), /* driver can handle IPM */ + ATA_FLAG_EM = (1 << 21), /* driver supports enclosure + * management */ + ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity + * led */ /* The following flag belongs to ap->pflags but is kept in * ap->flags because it's referenced in many LLDs and will be @@ -446,6 +451,15 @@ enum link_pm { MEDIUM_POWER, }; extern struct device_attribute dev_attr_link_power_management_policy; +extern struct device_attribute dev_attr_em_message_type; +extern struct device_attribute dev_attr_em_message; +extern struct device_attribute dev_attr_sw_activity; + +enum sw_activity { + OFF, + BLINK_ON, + BLINK_OFF, +}; #ifdef CONFIG_ATA_SFF struct ata_ioports { @@ -701,6 +715,7 @@ struct ata_port { struct timer_list fastdrain_timer; unsigned long fastdrain_cnt; + int em_message_type; void *private_data; #ifdef CONFIG_ATA_ACPI @@ -792,6 +807,12 @@ struct ata_port_operations { u8 (*bmdma_status)(struct ata_port *ap); #endif /* CONFIG_ATA_SFF */ + ssize_t (*em_show)(struct ata_port *ap, char *buf); + ssize_t (*em_store)(struct ata_port *ap, const char *message, + size_t size); + ssize_t (*sw_activity_show)(struct ata_device *dev, char *buf); + ssize_t (*sw_activity_store)(struct ata_device *dev, + enum sw_activity val); /* * Obsolete */ -- cgit v1.2.3 From 20a9b6e7c303f2a6f9afe17c0997bc9a3c734442 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 14 Jul 2008 22:38:22 +0200 Subject: i2c: Remove 3 deprecated bus drivers This patch contains the scheduled removal of i2c-i810, i2c-prosavage and i2c-savage4. Signed-off-by: Adrian Bunk Signed-off-by: Jean Delvare --- Documentation/feature-removal-schedule.txt | 7 - Documentation/i2c/busses/i2c-i810 | 47 ----- Documentation/i2c/busses/i2c-prosavage | 23 -- Documentation/i2c/busses/i2c-savage4 | 26 --- drivers/i2c/busses/Kconfig | 52 ----- drivers/i2c/busses/Makefile | 3 - drivers/i2c/busses/i2c-i810.c | 260 ----------------------- drivers/i2c/busses/i2c-prosavage.c | 325 ----------------------------- drivers/i2c/busses/i2c-savage4.c | 185 ---------------- include/linux/i2c-id.h | 1 - 10 files changed, 929 deletions(-) delete mode 100644 Documentation/i2c/busses/i2c-i810 delete mode 100644 Documentation/i2c/busses/i2c-prosavage delete mode 100644 Documentation/i2c/busses/i2c-savage4 delete mode 100644 drivers/i2c/busses/i2c-i810.c delete mode 100644 drivers/i2c/busses/i2c-prosavage.c delete mode 100644 drivers/i2c/busses/i2c-savage4.c (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 46ece3fba6f9..65a1482457a8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -222,13 +222,6 @@ Who: Thomas Gleixner --------------------------- -What: i2c-i810, i2c-prosavage and i2c-savage4 -When: May 2008 -Why: These drivers are superseded by i810fb, intelfb and savagefb. -Who: Jean Delvare - ---------------------------- - What (Why): - include/linux/netfilter_ipv4/ipt_TOS.h ipt_tos.h header files (superseded by xt_TOS/xt_tos target & match) diff --git a/Documentation/i2c/busses/i2c-i810 b/Documentation/i2c/busses/i2c-i810 deleted file mode 100644 index 778210ee1583..000000000000 --- a/Documentation/i2c/busses/i2c-i810 +++ /dev/null @@ -1,47 +0,0 @@ -Kernel driver i2c-i810 - -Supported adapters: - * Intel 82810, 82810-DC100, 82810E, and 82815 (GMCH) - * Intel 82845G (GMCH) - -Authors: - Frodo Looijaard , - Philip Edelbrock , - Kyösti Mälkki , - Ralph Metzler , - Mark D. Studebaker - -Main contact: Mark Studebaker - -Description ------------ - -WARNING: If you have an '810' or '815' motherboard, your standard I2C -temperature sensors are most likely on the 801's I2C bus. You want the -i2c-i801 driver for those, not this driver. - -Now for the i2c-i810... - -The GMCH chip contains two I2C interfaces. - -The first interface is used for DDC (Data Display Channel) which is a -serial channel through the VGA monitor connector to a DDC-compliant -monitor. This interface is defined by the Video Electronics Standards -Association (VESA). The standards are available for purchase at -http://www.vesa.org . - -The second interface is a general-purpose I2C bus. It may be connected to a -TV-out chip such as the BT869 or possibly to a digital flat-panel display. - -Features --------- - -Both busses use the i2c-algo-bit driver for 'bit banging' -and support for specific transactions is provided by i2c-algo-bit. - -Issues ------- - -If you enable bus testing in i2c-algo-bit (insmod i2c-algo-bit bit_test=1), -the test may fail; if so, the i2c-i810 driver won't be inserted. However, -we think this has been fixed. diff --git a/Documentation/i2c/busses/i2c-prosavage b/Documentation/i2c/busses/i2c-prosavage deleted file mode 100644 index 703687902511..000000000000 --- a/Documentation/i2c/busses/i2c-prosavage +++ /dev/null @@ -1,23 +0,0 @@ -Kernel driver i2c-prosavage - -Supported adapters: - - S3/VIA KM266/VT8375 aka ProSavage8 - S3/VIA KM133/VT8365 aka Savage4 - -Author: Henk Vergonet - -Description ------------ - -The Savage4 chips contain two I2C interfaces (aka a I2C 'master' or -'host'). - -The first interface is used for DDC (Data Display Channel) which is a -serial channel through the VGA monitor connector to a DDC-compliant -monitor. This interface is defined by the Video Electronics Standards -Association (VESA). The standards are available for purchase at -http://www.vesa.org . The second interface is a general-purpose I2C bus. - -Usefull for gaining access to the TV Encoder chips. - diff --git a/Documentation/i2c/busses/i2c-savage4 b/Documentation/i2c/busses/i2c-savage4 deleted file mode 100644 index 6ecceab618d3..000000000000 --- a/Documentation/i2c/busses/i2c-savage4 +++ /dev/null @@ -1,26 +0,0 @@ -Kernel driver i2c-savage4 - -Supported adapters: - * Savage4 - * Savage2000 - -Authors: - Alexander Wold , - Mark D. Studebaker - -Description ------------ - -The Savage4 chips contain two I2C interfaces (aka a I2C 'master' -or 'host'). - -The first interface is used for DDC (Data Display Channel) which is a -serial channel through the VGA monitor connector to a DDC-compliant -monitor. This interface is defined by the Video Electronics Standards -Association (VESA). The standards are available for purchase at -http://www.vesa.org . The DDC bus is not yet supported because its register -is not directly memory-mapped. - -The second interface is a general-purpose I2C bus. This is the only -interface supported by the driver at the moment. - diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 00d76e13588f..b7cce9211838 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -186,26 +186,6 @@ config I2C_I801 This driver can also be built as a module. If so, the module will be called i2c-i801. -config I2C_I810 - tristate "Intel 810/815 (DEPRECATED)" - default n - depends on PCI - select I2C_ALGOBIT - help - If you say yes to this option, support will be included for the Intel - 810/815 family of mainboard I2C interfaces. Specifically, the - following versions of the chipset are supported: - i810AA - i810AB - i810E - i815 - i845G - - This driver is deprecated in favor of the i810fb and intelfb drivers. - - This driver can also be built as a module. If so, the module - will be called i2c-i810. - config I2C_PXA tristate "Intel PXA2XX I2C adapter (EXPERIMENTAL)" depends on EXPERIMENTAL && ARCH_PXA @@ -402,24 +382,6 @@ config I2C_PASEMI help Supports the PA Semi PWRficient on-chip SMBus interfaces. -config I2C_PROSAVAGE - tristate "S3/VIA (Pro)Savage (DEPRECATED)" - default n - depends on PCI - select I2C_ALGOBIT - help - If you say yes to this option, support will be included for the - I2C bus and DDC bus of the S3VIA embedded Savage4 and ProSavage8 - graphics processors. - chipsets supported: - S3/VIA KM266/VT8375 aka ProSavage8 - S3/VIA KM133/VT8365 aka Savage4 - - This driver is deprecated in favor of the savagefb driver. - - This support is also available as a module. If so, the module - will be called i2c-prosavage. - config I2C_S3C2410 tristate "S3C2410 I2C Driver" depends on ARCH_S3C2410 @@ -427,20 +389,6 @@ config I2C_S3C2410 Say Y here to include support for I2C controller in the Samsung S3C2410 based System-on-Chip devices. -config I2C_SAVAGE4 - tristate "S3 Savage 4 (DEPRECATED)" - default n - depends on PCI - select I2C_ALGOBIT - help - If you say yes to this option, support will be included for the - S3 Savage 4 I2C interface. - - This driver is deprecated in favor of the savagefb driver. - - This driver can also be built as a module. If so, the module - will be called i2c-savage4. - config I2C_SIBYTE tristate "SiByte SMBus interface" depends on SIBYTE_SB1xxx_SOC diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 8b0a8c257905..81bb407d24cc 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o obj-$(CONFIG_I2C_I801) += i2c-i801.o -obj-$(CONFIG_I2C_I810) += i2c-i810.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o @@ -35,10 +34,8 @@ obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o obj-$(CONFIG_I2C_PNX) += i2c-pnx.o -obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o -obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c deleted file mode 100644 index 42e8d94c276f..000000000000 --- a/drivers/i2c/busses/i2c-i810.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999, 2000 Frodo Looijaard , - Philip Edelbrock , - Ralph Metzler , and - Mark D. Studebaker - - Based on code written by Ralph Metzler and - Simon Vogl - - 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. - - 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -/* - This interfaces to the I810/I815 to provide access to - the DDC Bus and the I2C Bus. - - SUPPORTED DEVICES PCI ID - i810AA 7121 - i810AB 7123 - i810E 7125 - i815 1132 - i845G 2562 -*/ - -#include -#include -#include -#include -#include -#include -#include - -/* GPIO register locations */ -#define I810_IOCONTROL_OFFSET 0x5000 -#define I810_HVSYNC 0x00 /* not used */ -#define I810_GPIOA 0x10 -#define I810_GPIOB 0x14 - -/* bit locations in the registers */ -#define SCL_DIR_MASK 0x0001 -#define SCL_DIR 0x0002 -#define SCL_VAL_MASK 0x0004 -#define SCL_VAL_OUT 0x0008 -#define SCL_VAL_IN 0x0010 -#define SDA_DIR_MASK 0x0100 -#define SDA_DIR 0x0200 -#define SDA_VAL_MASK 0x0400 -#define SDA_VAL_OUT 0x0800 -#define SDA_VAL_IN 0x1000 - -/* initialization states */ -#define INIT1 0x1 -#define INIT2 0x2 -#define INIT3 0x4 - -/* delays */ -#define CYCLE_DELAY 10 -#define TIMEOUT (HZ / 2) - -static void __iomem *ioaddr; - -/* The i810 GPIO registers have individual masks for each bit - so we never have to read before writing. Nice. */ - -static void bit_i810i2c_setscl(void *data, int val) -{ - writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, - ioaddr + I810_GPIOB); - readl(ioaddr + I810_GPIOB); /* flush posted write */ -} - -static void bit_i810i2c_setsda(void *data, int val) -{ - writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, - ioaddr + I810_GPIOB); - readl(ioaddr + I810_GPIOB); /* flush posted write */ -} - -/* The GPIO pins are open drain, so the pins could always remain outputs. - However, some chip versions don't latch the inputs unless they - are set as inputs. - We rely on the i2c-algo-bit routines to set the pins high before - reading the input from other chips. Following guidance in the 815 - prog. ref. guide, we do a "dummy write" of 0 to the register before - reading which forces the input value to be latched. We presume this - applies to the 810 as well; shouldn't hurt anyway. This is necessary to get - i2c_algo_bit bit_test=1 to pass. */ - -static int bit_i810i2c_getscl(void *data) -{ - writel(SCL_DIR_MASK, ioaddr + I810_GPIOB); - writel(0, ioaddr + I810_GPIOB); - return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN)); -} - -static int bit_i810i2c_getsda(void *data) -{ - writel(SDA_DIR_MASK, ioaddr + I810_GPIOB); - writel(0, ioaddr + I810_GPIOB); - return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN)); -} - -static void bit_i810ddc_setscl(void *data, int val) -{ - writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, - ioaddr + I810_GPIOA); - readl(ioaddr + I810_GPIOA); /* flush posted write */ -} - -static void bit_i810ddc_setsda(void *data, int val) -{ - writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, - ioaddr + I810_GPIOA); - readl(ioaddr + I810_GPIOA); /* flush posted write */ -} - -static int bit_i810ddc_getscl(void *data) -{ - writel(SCL_DIR_MASK, ioaddr + I810_GPIOA); - writel(0, ioaddr + I810_GPIOA); - return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN)); -} - -static int bit_i810ddc_getsda(void *data) -{ - writel(SDA_DIR_MASK, ioaddr + I810_GPIOA); - writel(0, ioaddr + I810_GPIOA); - return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN)); -} - -static int config_i810(struct pci_dev *dev) -{ - unsigned long cadr; - - /* map I810 memory */ - cadr = dev->resource[1].start; - cadr += I810_IOCONTROL_OFFSET; - cadr &= PCI_BASE_ADDRESS_MEM_MASK; - ioaddr = ioremap_nocache(cadr, 0x1000); - if (ioaddr) { - bit_i810i2c_setscl(NULL, 1); - bit_i810i2c_setsda(NULL, 1); - bit_i810ddc_setscl(NULL, 1); - bit_i810ddc_setsda(NULL, 1); - return 0; - } - return -ENODEV; -} - -static struct i2c_algo_bit_data i810_i2c_bit_data = { - .setsda = bit_i810i2c_setsda, - .setscl = bit_i810i2c_setscl, - .getsda = bit_i810i2c_getsda, - .getscl = bit_i810i2c_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT, -}; - -static struct i2c_adapter i810_i2c_adapter = { - .owner = THIS_MODULE, - .id = I2C_HW_B_I810, - .name = "I810/I815 I2C Adapter", - .algo_data = &i810_i2c_bit_data, -}; - -static struct i2c_algo_bit_data i810_ddc_bit_data = { - .setsda = bit_i810ddc_setsda, - .setscl = bit_i810ddc_setscl, - .getsda = bit_i810ddc_getsda, - .getscl = bit_i810ddc_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT, -}; - -static struct i2c_adapter i810_ddc_adapter = { - .owner = THIS_MODULE, - .id = I2C_HW_B_I810, - .name = "I810/I815 DDC Adapter", - .algo_data = &i810_ddc_bit_data, -}; - -static struct pci_device_id i810_ids[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) }, - { 0, }, -}; - -MODULE_DEVICE_TABLE (pci, i810_ids); - -static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int retval; - - retval = config_i810(dev); - if (retval) - return retval; - dev_info(&dev->dev, "i810/i815 i2c device found.\n"); - - /* set up the sysfs linkage to our parent device */ - i810_i2c_adapter.dev.parent = &dev->dev; - i810_ddc_adapter.dev.parent = &dev->dev; - - retval = i2c_bit_add_bus(&i810_i2c_adapter); - if (retval) - return retval; - retval = i2c_bit_add_bus(&i810_ddc_adapter); - if (retval) - i2c_del_adapter(&i810_i2c_adapter); - return retval; -} - -static void __devexit i810_remove(struct pci_dev *dev) -{ - i2c_del_adapter(&i810_ddc_adapter); - i2c_del_adapter(&i810_i2c_adapter); - iounmap(ioaddr); -} - -static struct pci_driver i810_driver = { - .name = "i810_smbus", - .id_table = i810_ids, - .probe = i810_probe, - .remove = __devexit_p(i810_remove), -}; - -static int __init i2c_i810_init(void) -{ - return pci_register_driver(&i810_driver); -} - -static void __exit i2c_i810_exit(void) -{ - pci_unregister_driver(&i810_driver); -} - -MODULE_AUTHOR("Frodo Looijaard , " - "Philip Edelbrock , " - "Ralph Metzler , " - "and Mark D. Studebaker "); -MODULE_DESCRIPTION("I810/I815 I2C/DDC driver"); -MODULE_LICENSE("GPL"); - -module_init(i2c_i810_init); -module_exit(i2c_i810_exit); diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c deleted file mode 100644 index 07c1f1e27df1..000000000000 --- a/drivers/i2c/busses/i2c-prosavage.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * kernel/busses/i2c-prosavage.c - * - * i2c bus driver for S3/VIA 8365/8375 graphics processor. - * Copyright (c) 2003 Henk Vergonet - * Based on code written by: - * Frodo Looijaard , - * Philip Edelbrock , - * Ralph Metzler , and - * Mark D. Studebaker - * Simon Vogl - * and others - * - * Please read the lm_sensors documentation for details on use. - * - * 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. - * - * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -/* 18-05-2003 HVE - created - * 14-06-2003 HVE - adapted for lm_sensors2 - * 17-06-2003 HVE - linux 2.5.xx compatible - * 18-06-2003 HVE - codingstyle - * 21-06-2003 HVE - compatibility lm_sensors2 and linux 2.5.xx - * codingstyle, mmio enabled - * - * This driver interfaces to the I2C bus of the VIA north bridge embedded - * ProSavage4/8 devices. Usefull for gaining access to the TV Encoder chips. - * - * Graphics cores: - * S3/VIA KM266/VT8375 aka ProSavage8 - * S3/VIA KM133/VT8365 aka Savage4 - * - * Two serial busses are implemented: - * SERIAL1 - I2C serial communications interface - * SERIAL2 - DDC2 monitor communications interface - * - * Tested on a FX41 mainboard, see http://www.shuttle.com - * - * - * TODO: - * - integration with prosavage framebuffer device - * (Additional documentation needed :( - */ - -#include -#include -#include -#include -#include -#include - -/* - * driver configuration - */ -#define MAX_BUSSES 2 - -struct s_i2c_bus { - void __iomem *mmvga; - int i2c_reg; - int adap_ok; - struct i2c_adapter adap; - struct i2c_algo_bit_data algo; -}; - -struct s_i2c_chip { - void __iomem *mmio; - struct s_i2c_bus i2c_bus[MAX_BUSSES]; -}; - - -/* - * i2c configuration - */ -#define CYCLE_DELAY 10 -#define TIMEOUT (HZ / 2) - - -/* - * S3/VIA 8365/8375 registers - */ -#define VGA_CR_IX 0x3d4 -#define VGA_CR_DATA 0x3d5 - -#define CR_SERIAL1 0xa0 /* I2C serial communications interface */ -#define MM_SERIAL1 0xff20 -#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ - -/* based on vt8365 documentation */ -#define I2C_ENAB 0x10 -#define I2C_SCL_OUT 0x01 -#define I2C_SDA_OUT 0x02 -#define I2C_SCL_IN 0x04 -#define I2C_SDA_IN 0x08 - -#define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX) -#define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA) -#define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA) - - -/* - * Serial bus line handling - * - * serial communications register as parameter in private data - * - * TODO: locks with other code sections accessing video registers? - */ -static void bit_s3via_setscl(void *bus, int val) -{ - struct s_i2c_bus *p = (struct s_i2c_bus *)bus; - unsigned int r; - - SET_CR_IX(p, p->i2c_reg); - r = GET_CR_DATA(p); - r |= I2C_ENAB; - if (val) { - r |= I2C_SCL_OUT; - } else { - r &= ~I2C_SCL_OUT; - } - SET_CR_DATA(p, r); -} - -static void bit_s3via_setsda(void *bus, int val) -{ - struct s_i2c_bus *p = (struct s_i2c_bus *)bus; - unsigned int r; - - SET_CR_IX(p, p->i2c_reg); - r = GET_CR_DATA(p); - r |= I2C_ENAB; - if (val) { - r |= I2C_SDA_OUT; - } else { - r &= ~I2C_SDA_OUT; - } - SET_CR_DATA(p, r); -} - -static int bit_s3via_getscl(void *bus) -{ - struct s_i2c_bus *p = (struct s_i2c_bus *)bus; - - SET_CR_IX(p, p->i2c_reg); - return (0 != (GET_CR_DATA(p) & I2C_SCL_IN)); -} - -static int bit_s3via_getsda(void *bus) -{ - struct s_i2c_bus *p = (struct s_i2c_bus *)bus; - - SET_CR_IX(p, p->i2c_reg); - return (0 != (GET_CR_DATA(p) & I2C_SDA_IN)); -} - - -/* - * adapter initialisation - */ -static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg) -{ - int ret; - p->adap.owner = THIS_MODULE; - p->adap.id = I2C_HW_B_S3VIA; - p->adap.algo_data = &p->algo; - p->adap.dev.parent = &dev->dev; - p->algo.setsda = bit_s3via_setsda; - p->algo.setscl = bit_s3via_setscl; - p->algo.getsda = bit_s3via_getsda; - p->algo.getscl = bit_s3via_getscl; - p->algo.udelay = CYCLE_DELAY; - p->algo.timeout = TIMEOUT; - p->algo.data = p; - p->mmvga = mmvga; - p->i2c_reg = i2c_reg; - - ret = i2c_bit_add_bus(&p->adap); - if (ret) { - return ret; - } - - p->adap_ok = 1; - return 0; -} - - -/* - * Cleanup stuff - */ -static void prosavage_remove(struct pci_dev *dev) -{ - struct s_i2c_chip *chip; - int i, ret; - - chip = (struct s_i2c_chip *)pci_get_drvdata(dev); - - if (!chip) { - return; - } - for (i = MAX_BUSSES - 1; i >= 0; i--) { - if (chip->i2c_bus[i].adap_ok == 0) - continue; - - ret = i2c_del_adapter(&chip->i2c_bus[i].adap); - if (ret) { - dev_err(&dev->dev, "%s not removed\n", - chip->i2c_bus[i].adap.name); - } - } - if (chip->mmio) { - iounmap(chip->mmio); - } - kfree(chip); -} - - -/* - * Detect chip and initialize it - */ -static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int ret; - unsigned long base, len; - struct s_i2c_chip *chip; - struct s_i2c_bus *bus; - - pci_set_drvdata(dev, kzalloc(sizeof(struct s_i2c_chip), GFP_KERNEL)); - chip = (struct s_i2c_chip *)pci_get_drvdata(dev); - if (chip == NULL) { - return -ENOMEM; - } - - base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK; - len = dev->resource[0].end - base + 1; - chip->mmio = ioremap_nocache(base, len); - - if (chip->mmio == NULL) { - dev_err(&dev->dev, "ioremap failed\n"); - prosavage_remove(dev); - return -ENODEV; - } - - - /* - * Chip initialisation - */ - /* Unlock Extended IO Space ??? */ - - - /* - * i2c bus registration - */ - bus = &chip->i2c_bus[0]; - snprintf(bus->adap.name, sizeof(bus->adap.name), - "ProSavage I2C bus at %02x:%02x.%x", - dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1); - if (ret) { - goto err_adap; - } - /* - * ddc bus registration - */ - bus = &chip->i2c_bus[1]; - snprintf(bus->adap.name, sizeof(bus->adap.name), - "ProSavage DDC bus at %02x:%02x.%x", - dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2); - if (ret) { - goto err_adap; - } - return 0; -err_adap: - dev_err(&dev->dev, "%s failed\n", bus->adap.name); - prosavage_remove(dev); - return ret; -} - - -/* - * Data for PCI driver interface - */ -static struct pci_device_id prosavage_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) }, - { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) }, - { 0, }, -}; - -MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl); - -static struct pci_driver prosavage_driver = { - .name = "prosavage_smbus", - .id_table = prosavage_pci_tbl, - .probe = prosavage_probe, - .remove = prosavage_remove, -}; - -static int __init i2c_prosavage_init(void) -{ - return pci_register_driver(&prosavage_driver); -} - -static void __exit i2c_prosavage_exit(void) -{ - pci_unregister_driver(&prosavage_driver); -} - -MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl); -MODULE_AUTHOR("Henk Vergonet"); -MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver"); -MODULE_LICENSE("GPL"); - -module_init (i2c_prosavage_init); -module_exit (i2c_prosavage_exit); diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c deleted file mode 100644 index 8adf4abaa035..000000000000 --- a/drivers/i2c/busses/i2c-savage4.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (C) 1998-2003 The LM Sensors Team - Alexander Wold - Mark D. Studebaker - - Based on i2c-voodoo3.c. - - 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. - - 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* This interfaces to the I2C bus of the Savage4 to gain access to - the BT869 and possibly other I2C devices. The DDC bus is not - yet supported because its register is not memory-mapped. -*/ - -#include -#include -#include -#include -#include -#include -#include - -/* device IDs */ -#define PCI_CHIP_SAVAGE4 0x8A22 -#define PCI_CHIP_SAVAGE2000 0x9102 - -#define REG 0xff20 /* Serial Port 1 Register */ - -/* bit locations in the register */ -#define I2C_ENAB 0x00000020 -#define I2C_SCL_OUT 0x00000001 -#define I2C_SDA_OUT 0x00000002 -#define I2C_SCL_IN 0x00000008 -#define I2C_SDA_IN 0x00000010 - -/* delays */ -#define CYCLE_DELAY 10 -#define TIMEOUT (HZ / 2) - - -static void __iomem *ioaddr; - -/* The sav GPIO registers don't have individual masks for each bit - so we always have to read before writing. */ - -static void bit_savi2c_setscl(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if(val) - r |= I2C_SCL_OUT; - else - r &= ~I2C_SCL_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static void bit_savi2c_setsda(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if(val) - r |= I2C_SDA_OUT; - else - r &= ~I2C_SDA_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -/* The GPIO pins are open drain, so the pins always remain outputs. - We rely on the i2c-algo-bit routines to set the pins high before - reading the input from other chips. */ - -static int bit_savi2c_getscl(void *data) -{ - return (0 != (readl(ioaddr + REG) & I2C_SCL_IN)); -} - -static int bit_savi2c_getsda(void *data) -{ - return (0 != (readl(ioaddr + REG) & I2C_SDA_IN)); -} - -/* Configures the chip */ - -static int config_s4(struct pci_dev *dev) -{ - unsigned long cadr; - - /* map memory */ - cadr = dev->resource[0].start; - cadr &= PCI_BASE_ADDRESS_MEM_MASK; - ioaddr = ioremap_nocache(cadr, 0x0080000); - if (ioaddr) { - /* writel(0x8160, ioaddr + REG2); */ - writel(0x00000020, ioaddr + REG); - dev_info(&dev->dev, "Using Savage4 at %p\n", ioaddr); - return 0; - } - return -ENODEV; -} - -static struct i2c_algo_bit_data sav_i2c_bit_data = { - .setsda = bit_savi2c_setsda, - .setscl = bit_savi2c_setscl, - .getsda = bit_savi2c_getsda, - .getscl = bit_savi2c_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT -}; - -static struct i2c_adapter savage4_i2c_adapter = { - .owner = THIS_MODULE, - .id = I2C_HW_B_SAVAGE, - .name = "I2C Savage4 adapter", - .algo_data = &sav_i2c_bit_data, -}; - -static struct pci_device_id savage4_ids[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4) }, - { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000) }, - { 0, } -}; - -MODULE_DEVICE_TABLE (pci, savage4_ids); - -static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int retval; - - retval = config_s4(dev); - if (retval) - return retval; - - /* set up the sysfs linkage to our parent device */ - savage4_i2c_adapter.dev.parent = &dev->dev; - - return i2c_bit_add_bus(&savage4_i2c_adapter); -} - -static void __devexit savage4_remove(struct pci_dev *dev) -{ - i2c_del_adapter(&savage4_i2c_adapter); - iounmap(ioaddr); -} - -static struct pci_driver savage4_driver = { - .name = "savage4_smbus", - .id_table = savage4_ids, - .probe = savage4_probe, - .remove = __devexit_p(savage4_remove), -}; - -static int __init i2c_savage4_init(void) -{ - return pci_register_driver(&savage4_driver); -} - -static void __exit i2c_savage4_exit(void) -{ - pci_unregister_driver(&savage4_driver); -} - -MODULE_AUTHOR("Alexander Wold " - "and Mark D. Studebaker "); -MODULE_DESCRIPTION("Savage4 I2C/SMBus driver"); -MODULE_LICENSE("GPL"); - -module_init(i2c_savage4_init); -module_exit(i2c_savage4_exit); diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 580acc93903e..988e566d3ed5 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -111,7 +111,6 @@ #define I2C_HW_B_RIVA 0x010010 /* Riva based graphics cards */ #define I2C_HW_B_IOC 0x010011 /* IOC bit-wiggling */ #define I2C_HW_B_IXP2000 0x010016 /* GPIO on IXP2000 systems */ -#define I2C_HW_B_S3VIA 0x010018 /* S3Via ProSavage adapter */ #define I2C_HW_B_ZR36067 0x010019 /* Zoran-36057/36067 based boards */ #define I2C_HW_B_PCILYNX 0x01001a /* TI PCILynx I2C adapter */ #define I2C_HW_B_CX2388x 0x01001b /* connexant 2388x based tv cards */ -- cgit v1.2.3 From 67c2e66571c383404a5acd08189194da660da942 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:23 +0200 Subject: i2c: Delete unused function i2c_smbus_write_quick Function i2c_smbus_write_quick has no users left, so we can delete it. Also update the list of these helper functions which are gone but could be added back if needed. Signed-off-by: Jean Delvare --- Documentation/i2c/smbus-protocol | 4 ++-- Documentation/i2c/writing-clients | 14 +++++++------- drivers/i2c/i2c-core.c | 7 ------- include/linux/i2c.h | 1 - 4 files changed, 9 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol index 03f08fb491cc..24bfb65da17d 100644 --- a/Documentation/i2c/smbus-protocol +++ b/Documentation/i2c/smbus-protocol @@ -42,8 +42,8 @@ Count (8 bits): A data byte containing the length of a block operation. [..]: Data sent by I2C device, as opposed to data sent by the host adapter. -SMBus Quick Command: i2c_smbus_write_quick() -============================================= +SMBus Quick Command +=================== This sends a single bit to the device, at the place of the Rd/Wr bit. diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index ba5d1971f35f..63722d3c9cdf 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -569,7 +569,6 @@ SMBus communication in terms of it. Never use this function directly! - extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); extern s32 i2c_smbus_read_byte(struct i2c_client * client); extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); @@ -578,20 +577,21 @@ SMBus communication extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); extern s32 i2c_smbus_write_word_data(struct i2c_client * client, u8 command, u16 value); + extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); extern s32 i2c_smbus_write_block_data(struct i2c_client * client, u8 command, u8 length, u8 *values); extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, u8 command, u8 length, u8 *values); - -These ones were removed in Linux 2.6.10 because they had no users, but could -be added back later if needed: - - extern s32 i2c_smbus_read_block_data(struct i2c_client * client, - u8 command, u8 *values); extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, u8 command, u8 length, u8 *values); + +These ones were removed from i2c-core because they had no users, but could +be added back later if needed: + + extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); extern s32 i2c_smbus_process_call(struct i2c_client * client, u8 command, u16 value); extern s32 i2c_smbus_block_process_call(struct i2c_client *client, diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 937f1dcbf3d7..3695a4a1ab77 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1303,13 +1303,6 @@ static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) return 0; } -s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value) -{ - return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - value,0,I2C_SMBUS_QUICK,NULL); -} -EXPORT_SYMBOL(i2c_smbus_write_quick); - s32 i2c_smbus_read_byte(struct i2c_client *client) { union i2c_smbus_data data; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 8dc730132192..b3695f353f79 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -71,7 +71,6 @@ extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, /* Now follow the 'nice' access routines. These also document the calling conventions of smbus_access. */ -extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); extern s32 i2c_smbus_read_byte(struct i2c_client * client); extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); -- cgit v1.2.3 From ae7193f7fa3e1735ab70807eb6e35a2a6575623f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:24 +0200 Subject: i2c: Update stray references to smbus_access That function is actually named i2c_smbus_xfer. Signed-off-by: Jean Delvare --- include/linux/i2c.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index b3695f353f79..7c36d5188d39 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -69,7 +69,7 @@ extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, union i2c_smbus_data * data); /* Now follow the 'nice' access routines. These also document the calling - conventions of smbus_access. */ + conventions of i2c_smbus_xfer. */ extern s32 i2c_smbus_read_byte(struct i2c_client * client); extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); @@ -536,7 +536,7 @@ union i2c_smbus_data { /* and one more for user-space compatibility */ }; -/* smbus_access read or write markers */ +/* i2c_smbus_xfer read or write markers */ #define I2C_SMBUS_READ 1 #define I2C_SMBUS_WRITE 0 -- cgit v1.2.3 From c1b6b4f2342d073698dfc2547240c35045a1d00e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:28 +0200 Subject: i2c: Let framebuffer drivers set their I2C bus class to DDC Let framebuffer drivers set their I2C bus class to DDC. Once this is done, we will be able to tell the eeprom driver to only probe for EDID EEPROMs on these buses. Signed-off-by: Jean Delvare --- drivers/video/fb_ddc.c | 1 + drivers/video/intelfb/intelfb_i2c.c | 12 +++++++----- drivers/video/matrox/i2c-matroxfb.c | 20 +++++++++++++++----- include/linux/i2c.h | 2 +- 4 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/fb_ddc.c b/drivers/video/fb_ddc.c index a0df63289b5f..0cf96eb8a60f 100644 --- a/drivers/video/fb_ddc.c +++ b/drivers/video/fb_ddc.c @@ -106,6 +106,7 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter) algo_data->setsda(algo_data->data, 1); algo_data->setscl(algo_data->data, 1); + adapter->class |= I2C_CLASS_DDC; return edid; } diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c index ca95f09d8b43..fcf9fadbf572 100644 --- a/drivers/video/intelfb/intelfb_i2c.c +++ b/drivers/video/intelfb/intelfb_i2c.c @@ -100,7 +100,8 @@ static int intelfb_gpio_getsda(void *data) static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, struct intelfb_i2c_chan *chan, - const u32 reg, const char *name) + const u32 reg, const char *name, + int class) { int rc; @@ -108,6 +109,7 @@ static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, chan->reg = reg; snprintf(chan->adapter.name, sizeof(chan->adapter.name), "intelfb %s", name); + chan->adapter.class = class; chan->adapter.owner = THIS_MODULE; chan->adapter.id = I2C_HW_B_INTELFB; chan->adapter.algo_data = &chan->algo; @@ -145,7 +147,7 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo) /* setup the DDC bus for analog output */ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, - "CRTDDC_A"); + "CRTDDC_A", I2C_CLASS_DDC); i++; /* need to add the output busses for each device @@ -159,9 +161,9 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo) case INTEL_865G: dinfo->output[i].type = INTELFB_OUTPUT_DVO; intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, - GPIOD, "DVODDC_D"); + GPIOD, "DVODDC_D", I2C_CLASS_DDC); intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, - GPIOE, "DVOI2C_E"); + GPIOE, "DVOI2C_E", 0); i++; break; case INTEL_915G: @@ -174,7 +176,7 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo) /* SDVO ports have a single control bus - 2 devices */ dinfo->output[i].type = INTELFB_OUTPUT_SDVO; intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, - GPIOE, "SDVOCTRL_E"); + GPIOE, "SDVOCTRL_E", 0); /* TODO: initialize the SDVO */ /* I830SDVOInit(pScrn, i, DVOB); */ i++; diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c index 4baab7be58de..75ee5a12e549 100644 --- a/drivers/video/matrox/i2c-matroxfb.c +++ b/drivers/video/matrox/i2c-matroxfb.c @@ -104,7 +104,9 @@ static struct i2c_algo_bit_data matrox_i2c_algo_template = }; static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, - unsigned int data, unsigned int clock, const char* name) { + unsigned int data, unsigned int clock, const char *name, + int class) +{ int err; b->minfo = minfo; @@ -114,6 +116,7 @@ static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, snprintf(b->adapter.name, sizeof(b->adapter.name), name, minfo->fbcon.node); i2c_set_adapdata(&b->adapter, b); + b->adapter.class = class; b->adapter.algo_data = &b->bac; b->adapter.dev.parent = &ACCESS_FBINFO(pcidev)->dev; b->bac = matrox_i2c_algo_template; @@ -159,22 +162,29 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { switch (ACCESS_FBINFO(chip)) { case MGA_2064: case MGA_2164: - err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1B_DATA, DDC1B_CLK, "DDC:fb%u #0"); + err = i2c_bus_reg(&m2info->ddc1, minfo, + DDC1B_DATA, DDC1B_CLK, + "DDC:fb%u #0", I2C_CLASS_DDC); break; default: - err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1_DATA, DDC1_CLK, "DDC:fb%u #0"); + err = i2c_bus_reg(&m2info->ddc1, minfo, + DDC1_DATA, DDC1_CLK, + "DDC:fb%u #0", I2C_CLASS_DDC); break; } if (err) goto fail_ddc1; if (ACCESS_FBINFO(devflags.dualhead)) { - err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1"); + err = i2c_bus_reg(&m2info->ddc2, minfo, + DDC2_DATA, DDC2_CLK, + "DDC:fb%u #1", I2C_CLASS_DDC); if (err == -ENODEV) { printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); } else if (err) printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); /* Register maven bus even on G450/G550 */ - err = i2c_bus_reg(&m2info->maven, minfo, MAT_DATA, MAT_CLK, "MAVEN:fb%u"); + err = i2c_bus_reg(&m2info->maven, minfo, + MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); if (err) printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); } diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 7c36d5188d39..145797fe6a31 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -349,7 +349,7 @@ static inline void i2c_set_adapdata (struct i2c_adapter *dev, void *data) #define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */ #define I2C_CLASS_TV_ANALOG (1<<1) /* bttv + friends */ #define I2C_CLASS_TV_DIGITAL (1<<2) /* dvb cards */ -#define I2C_CLASS_DDC (1<<3) /* i2c-matroxfb ? */ +#define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */ #define I2C_CLASS_CAM_ANALOG (1<<4) /* camera with analog CCD */ #define I2C_CLASS_CAM_DIGITAL (1<<5) /* most webcams */ #define I2C_CLASS_SOUND (1<<6) /* sound devices */ -- cgit v1.2.3 From 3401b2fff38fbb8b73ea6bcc69a8370ae5d2a7a0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:29 +0200 Subject: i2c: Let bus drivers add SPD to their class Let general purpose I2C/SMBus bus drivers add SPD to their class. Once this is done, we will be able to tell the eeprom driver to only probe for SPD EEPROMs and similar on these buses. Note that I took a conservative approach here, adding I2C_CLASS_SPD to many drivers that have no idea whether they can host SPD EEPROMs or not. This is to make sure that the eeprom driver doesn't stop probing buses where SPD EEPROMs or equivalent live. So, bus driver maintainers and users should feel free to remove the SPD class from drivers those buses never have SPD EEPROMs or they don't want the eeprom driver to bind to them. Likewise, feel free to add the SPD class to any bus driver I might have missed. Signed-off-by: Jean Delvare --- drivers/i2c/busses/i2c-ali1535.c | 2 +- drivers/i2c/busses/i2c-ali1563.c | 2 +- drivers/i2c/busses/i2c-ali15x3.c | 2 +- drivers/i2c/busses/i2c-amd756.c | 2 +- drivers/i2c/busses/i2c-amd8111.c | 2 +- drivers/i2c/busses/i2c-cpm.c | 2 +- drivers/i2c/busses/i2c-elektor.c | 2 +- drivers/i2c/busses/i2c-gpio.c | 2 +- drivers/i2c/busses/i2c-i801.c | 2 +- drivers/i2c/busses/i2c-ibm_iic.c | 4 ++-- drivers/i2c/busses/i2c-iop3xx.c | 2 +- drivers/i2c/busses/i2c-isch.c | 2 +- drivers/i2c/busses/i2c-mpc.c | 2 +- drivers/i2c/busses/i2c-mv64xxx.c | 2 +- drivers/i2c/busses/i2c-nforce2.c | 2 +- drivers/i2c/busses/i2c-ocores.c | 2 +- drivers/i2c/busses/i2c-pasemi.c | 2 +- drivers/i2c/busses/i2c-piix4.c | 2 +- drivers/i2c/busses/i2c-pmcmsp.c | 2 +- drivers/i2c/busses/i2c-s3c2410.c | 2 +- drivers/i2c/busses/i2c-sibyte.c | 4 ++-- drivers/i2c/busses/i2c-sis5595.c | 2 +- drivers/i2c/busses/i2c-sis630.c | 2 +- drivers/i2c/busses/i2c-sis96x.c | 2 +- drivers/i2c/busses/i2c-stub.c | 2 +- drivers/i2c/busses/i2c-via.c | 2 +- drivers/i2c/busses/i2c-viapro.c | 2 +- drivers/i2c/busses/scx200_acb.c | 2 +- include/linux/i2c.h | 1 + 29 files changed, 31 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index 704436cdec8e..8d1d90ab3a90 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -473,7 +473,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali1535_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_ALI1535, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index da5a382eee93..4b55ae19db8d 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -382,7 +382,7 @@ static const struct i2c_algorithm ali1563_algorithm = { static struct i2c_adapter ali1563_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_ALI1563, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &ali1563_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 7b029b147a8e..e922c3950fcd 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -471,7 +471,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali15x3_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_ALI15X3, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index f0baea62067d..bd4f6380fabe 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -301,7 +301,7 @@ static const struct i2c_algorithm smbus_algorithm = { struct i2c_adapter amd756_smbus = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_AMD756, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index a4f687915de1..0e18fe846010 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -383,7 +383,7 @@ static int __devinit amd8111_probe(struct pci_dev *dev, snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), "SMBus2 AMD8111 adapter at %04x", smbus->base); smbus->adapter.id = I2C_HW_SMBUS_AMD8111; - smbus->adapter.class = I2C_CLASS_HWMON; + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 53af744a91c1..8164de1f4d72 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -423,7 +423,7 @@ static const struct i2c_adapter cpm_ops = { .owner = THIS_MODULE, .name = "i2c-cpm", .algo = &cpm_i2c_algo, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, }; static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm) diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c index b7a9977b025f..c251cf21a62b 100644 --- a/drivers/i2c/busses/i2c-elektor.c +++ b/drivers/i2c/busses/i2c-elektor.c @@ -202,7 +202,7 @@ static struct i2c_algo_pcf_data pcf_isa_data = { static struct i2c_adapter pcf_isa_ops = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .id = I2C_HW_P_ELEK, .algo_data = &pcf_isa_data, .name = "i2c-elektor", diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 7c1b762aa681..79b455a1f090 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -140,7 +140,7 @@ static int __init i2c_gpio_probe(struct platform_device *pdev) adap->owner = THIS_MODULE; snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); adap->algo_data = bit_data; - adap->class = I2C_CLASS_HWMON; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->dev.parent = &pdev->dev; /* diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 213119211e58..9717ffe12921 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -573,7 +573,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter i801_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_I801, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 85dbf34382e1..6f7bfdec3c69 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -740,7 +740,7 @@ static int __devinit iic_probe(struct ocp_device *ocp){ strcpy(adap->name, "IBM IIC"); i2c_set_adapdata(adap, dev); adap->id = I2C_HW_OCP; - adap->class = I2C_CLASS_HWMON; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = &iic_algo; adap->client_register = NULL; adap->client_unregister = NULL; @@ -934,7 +934,7 @@ static int __devinit iic_probe(struct of_device *ofdev, strlcpy(adap->name, "IBM IIC", sizeof(adap->name)); i2c_set_adapdata(adap, dev); adap->id = I2C_HW_OCP; - adap->class = I2C_CLASS_HWMON; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = &iic_algo; adap->timeout = 1; adap->nr = dev->idx; diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 39884e797594..fc2714ac0c0f 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -482,7 +482,7 @@ iop3xx_i2c_probe(struct platform_device *pdev) memcpy(new_adapter->name, pdev->name, strlen(pdev->name)); new_adapter->id = I2C_HW_IOP3XX; new_adapter->owner = THIS_MODULE; - new_adapter->class = I2C_CLASS_HWMON; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; new_adapter->dev.parent = &pdev->dev; new_adapter->nr = pdev->id; diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index c9cd46b22692..8d648911a7f5 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -251,7 +251,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sch_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index a076129de7e8..10b9342a36c2 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -311,7 +311,7 @@ static struct i2c_adapter mpc_ops = { .name = "MPC adapter", .id = I2C_HW_MPC107, .algo = &mpc_algo, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .timeout = 1, }; diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 036e6a883e67..9e8118d2fe64 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -530,7 +530,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) drv_data->adapter.id = I2C_HW_MV64XXX; drv_data->adapter.algo = &mv64xxx_i2c_algo; drv_data->adapter.owner = THIS_MODULE; - drv_data->adapter.class = I2C_CLASS_HWMON; + drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; drv_data->adapter.timeout = pdata->timeout; drv_data->adapter.nr = pd->id; platform_set_drvdata(pd, drv_data); diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 081fdf3393f4..2654f20d3a62 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -350,7 +350,7 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar, } smbus->adapter.owner = THIS_MODULE; smbus->adapter.id = I2C_HW_SMBUS_NFORCE2; - smbus->adapter.class = I2C_CLASS_HWMON; + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; smbus->adapter.dev.parent = &dev->dev; diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index f145692cbb76..51ca79bf6480 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -205,7 +205,7 @@ static const struct i2c_algorithm ocores_algorithm = { static struct i2c_adapter ocores_adapter = { .owner = THIS_MODULE, .name = "i2c-ocores", - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &ocores_algorithm, }; diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c index 1603c81e39d4..adf0fbb902f0 100644 --- a/drivers/i2c/busses/i2c-pasemi.c +++ b/drivers/i2c/busses/i2c-pasemi.c @@ -365,7 +365,7 @@ static int __devinit pasemi_smb_probe(struct pci_dev *dev, smbus->adapter.owner = THIS_MODULE; snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), "PA Semi SMBus adapter at 0x%lx", smbus->base); - smbus->adapter.class = I2C_CLASS_HWMON; + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; smbus->adapter.nr = PCI_FUNC(dev->devfn); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 2bde47509e1a..85d69f3e624f 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -402,7 +402,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter piix4_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_PIIX4, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c index 63b3e2c11cff..dcf2045b5222 100644 --- a/drivers/i2c/busses/i2c-pmcmsp.c +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -622,7 +622,7 @@ static struct i2c_algorithm pmcmsptwi_algo = { static struct i2c_adapter pmcmsptwi_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &pmcmsptwi_algo, .name = DRV_NAME, }; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 9e8c875437be..007390ad9810 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -590,7 +590,7 @@ static struct s3c24xx_i2c s3c24xx_i2c = { .owner = THIS_MODULE, .algo = &s3c24xx_i2c_algorithm, .retries = 2, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, }, }; diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c index 114634da6c6e..ac8822e7a5b4 100644 --- a/drivers/i2c/busses/i2c-sibyte.c +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -156,7 +156,7 @@ static struct i2c_adapter sibyte_board_adapter[2] = { { .owner = THIS_MODULE, .id = I2C_HW_SIBYTE, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = NULL, .algo_data = &sibyte_board_data[0], .name = "SiByte SMBus 0", @@ -164,7 +164,7 @@ static struct i2c_adapter sibyte_board_adapter[2] = { { .owner = THIS_MODULE, .id = I2C_HW_SIBYTE, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = NULL, .algo_data = &sibyte_board_data[1], .name = "SiByte SMBus 1", diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 328441bb5470..f76944b384f5 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -362,7 +362,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis5595_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_SIS5595, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index d7e6ff3e0187..eb2b2181fed7 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -462,7 +462,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis630_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_SIS630, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index cde8e5880368..413e9e477723 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -244,7 +244,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis96x_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_SIS96X, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c index e37ccd80f77a..1b7b2af94036 100644 --- a/drivers/i2c/busses/i2c-stub.c +++ b/drivers/i2c/busses/i2c-stub.c @@ -140,7 +140,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter stub_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, .name = "SMBus stub driver", }; diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c index 61716f6b14dc..6517f8a6d911 100644 --- a/drivers/i2c/busses/i2c-via.c +++ b/drivers/i2c/busses/i2c-via.c @@ -87,7 +87,7 @@ static struct i2c_algo_bit_data bit_data = { static struct i2c_adapter vt586b_adapter = { .owner = THIS_MODULE, .id = I2C_HW_B_VIA, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .name = "VIA i2c", .algo_data = &bit_data, }; diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index c611905df009..7957ce515891 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -311,7 +311,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter vt596_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_VIA2, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 61abe0f33255..ed794b145a11 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -442,7 +442,7 @@ static __init struct scx200_acb_iface *scx200_create_iface(const char *text, adapter->owner = THIS_MODULE; adapter->id = I2C_HW_SMBUS_SCX200; adapter->algo = &scx200_acb_algorithm; - adapter->class = I2C_CLASS_HWMON; + adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->dev.parent = dev; mutex_init(&iface->mutex); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 145797fe6a31..839d0ea3dca3 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -353,6 +353,7 @@ static inline void i2c_set_adapdata (struct i2c_adapter *dev, void *data) #define I2C_CLASS_CAM_ANALOG (1<<4) /* camera with analog CCD */ #define I2C_CLASS_CAM_DIGITAL (1<<5) /* most webcams */ #define I2C_CLASS_SOUND (1<<6) /* sound devices */ +#define I2C_CLASS_SPD (1<<7) /* SPD EEPROMs and similar */ #define I2C_CLASS_ALL (UINT_MAX) /* all of the above */ /* i2c_client_address_data is the struct for holding default client -- cgit v1.2.3 From 0573d11b2bbd0e4774f33f4c1959c1939c055e96 Mon Sep 17 00:00:00 2001 From: Eric Brower Date: Mon, 14 Jul 2008 22:38:31 +0200 Subject: i2c-algo-pcf: Multi-master lost-arbitration improvement Improve lost-arbitration handling of PCF8584. This is necessary for support of a currently out-of-kernel driver for Sun Microsystems E250 environmental management; perhaps others. Signed-off-by: Eric Brower Acked-by: Dan Smolik Signed-off-by: Jean Delvare --- drivers/i2c/algos/i2c-algo-pcf.c | 48 ++++++++++++++++++++++++++-------------- include/linux/i2c-algo-pcf.h | 6 +++++ 2 files changed, 37 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c index 8907b0191677..1e328d19cd6d 100644 --- a/drivers/i2c/algos/i2c-algo-pcf.c +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -78,6 +78,36 @@ static void i2c_stop(struct i2c_algo_pcf_data *adap) set_pcf(adap, 1, I2C_PCF_STOP); } +static void handle_lab(struct i2c_algo_pcf_data *adap, const int *status) +{ + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n", + *status)); + + /* Cleanup from LAB -- reset and enable ESO. + * This resets the PCF8584; since we've lost the bus, no + * further attempts should be made by callers to clean up + * (no i2c_stop() etc.) + */ + set_pcf(adap, 1, I2C_PCF_PIN); + set_pcf(adap, 1, I2C_PCF_ESO); + + /* We pause for a time period sufficient for any running + * I2C transaction to complete -- the arbitration logic won't + * work properly until the next START is seen. + * It is assumed the bus driver or client has set a proper value. + * + * REVISIT: should probably use msleep instead of mdelay if we + * know we can sleep. + */ + if (adap->lab_mdelay) + mdelay(adap->lab_mdelay); + + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n", + get_pcf(adap, 1))); +} + static int wait_for_bb(struct i2c_algo_pcf_data *adap) { int timeout = DEF_TIMEOUT; @@ -109,23 +139,7 @@ static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { *status = get_pcf(adap, 1); } if (*status & I2C_PCF_LAB) { - DEB2(printk(KERN_INFO - "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n", - *status)); - /* Cleanup from LAB-- reset and enable ESO. - * This resets the PCF8584; since we've lost the bus, no - * further attempts should be made by callers to clean up - * (no i2c_stop() etc.) - */ - set_pcf(adap, 1, I2C_PCF_PIN); - set_pcf(adap, 1, I2C_PCF_ESO); - /* TODO: we should pause for a time period sufficient for any - * running I2C transaction to complete-- the arbitration - * logic won't work properly until the next START is seen. - */ - DEB2(printk(KERN_INFO - "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n", - get_pcf(adap,1))); + handle_lab(adap, status); return(-EINTR); } #endif diff --git a/include/linux/i2c-algo-pcf.h b/include/linux/i2c-algo-pcf.h index 77afbb60fd11..74fb6f889a77 100644 --- a/include/linux/i2c-algo-pcf.h +++ b/include/linux/i2c-algo-pcf.h @@ -36,6 +36,12 @@ struct i2c_algo_pcf_data { /* local settings */ int udelay; int timeout; + + /* Multi-master lost arbitration back-off delay (msecs) + * This should be set by the bus adapter or knowledgable client + * if bus is multi-mastered, else zero + */ + unsigned long lab_mdelay; }; int i2c_pcf_add_bus(struct i2c_adapter *); -- cgit v1.2.3 From e3e7fc3c401a5d53f0599a357b3cf65d6a4f52e3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:31 +0200 Subject: i2c-algo-pcf: Drop unused struct members Struct members udelay and timeout aren't used anywhere, so drop them. Signed-off-by: Jean Delvare Acked-by: Eric Brower --- drivers/i2c/busses/i2c-elektor.c | 2 -- include/linux/i2c-algo-pcf.h | 4 ---- 2 files changed, 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c index c251cf21a62b..7f38c01fb3a0 100644 --- a/drivers/i2c/busses/i2c-elektor.c +++ b/drivers/i2c/busses/i2c-elektor.c @@ -196,8 +196,6 @@ static struct i2c_algo_pcf_data pcf_isa_data = { .getown = pcf_isa_getown, .getclock = pcf_isa_getclock, .waitforpin = pcf_isa_waitforpin, - .udelay = 10, - .timeout = 100, }; static struct i2c_adapter pcf_isa_ops = { diff --git a/include/linux/i2c-algo-pcf.h b/include/linux/i2c-algo-pcf.h index 74fb6f889a77..0177d280f733 100644 --- a/include/linux/i2c-algo-pcf.h +++ b/include/linux/i2c-algo-pcf.h @@ -33,10 +33,6 @@ struct i2c_algo_pcf_data { int (*getclock) (void *data); void (*waitforpin) (void); - /* local settings */ - int udelay; - int timeout; - /* Multi-master lost arbitration back-off delay (msecs) * This should be set by the bus adapter or knowledgable client * if bus is multi-mastered, else zero -- cgit v1.2.3 From f6a7110520037ba786f17b53790c6eb8a3d4ef55 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:34 +0200 Subject: i2c-dev: Delete empty detach_client callback Implementing detach_client is optional, so there is no point in an empty implementation. Likewise, i2c driver IDs are optional, and we don't need one. Signed-off-by: Jean Delvare --- drivers/i2c/i2c-dev.c | 7 ------- include/linux/i2c-id.h | 2 -- 2 files changed, 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index e96d98696782..50df53640c78 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -548,19 +548,12 @@ static int i2cdev_detach_adapter(struct i2c_adapter *adap) return 0; } -static int i2cdev_detach_client(struct i2c_client *client) -{ - return 0; -} - static struct i2c_driver i2cdev_driver = { .driver = { .name = "dev_driver", }, - .id = I2C_DRIVERID_I2CDEV, .attach_adapter = i2cdev_attach_adapter, .detach_adapter = i2cdev_detach_adapter, - .detach_client = i2cdev_detach_client, }; /* ------------------------------------------------------------------------- */ diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 988e566d3ed5..ef13b7c66df3 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -91,8 +91,6 @@ #define I2C_DRIVERID_M52790 95 /* Mitsubishi M52790SP/FP AV switch */ #define I2C_DRIVERID_CS5345 96 /* cs5345 audio processor */ -#define I2C_DRIVERID_I2CDEV 900 - #define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */ /* -- cgit v1.2.3 From e9ca9eb9d7fc7bf3dc3cec5ba7edb089c4625f7b Mon Sep 17 00:00:00 2001 From: Jon Smirl Date: Mon, 14 Jul 2008 22:38:35 +0200 Subject: i2c: Export the i2c_bus_type symbol Export the root of the i2c bus so that PowerPC device tree code can iterate over devices on the i2c bus. Signed-off-by: Jon Smirl Signed-off-by: Jean Delvare --- drivers/i2c/i2c-core.c | 3 ++- include/linux/i2c.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d6cc58abf3ff..e45bb2838f42 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -201,7 +201,7 @@ static struct device_attribute i2c_dev_attrs[] = { { }, }; -static struct bus_type i2c_bus_type = { +struct bus_type i2c_bus_type = { .name = "i2c", .dev_attrs = i2c_dev_attrs, .match = i2c_device_match, @@ -212,6 +212,7 @@ static struct bus_type i2c_bus_type = { .suspend = i2c_device_suspend, .resume = i2c_device_resume, }; +EXPORT_SYMBOL_GPL(i2c_bus_type); /** diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 839d0ea3dca3..50cbab4b62b0 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -35,6 +35,8 @@ #include /* for completion */ #include +extern struct bus_type i2c_bus_type; + /* --- General options ------------------------------------------------ */ struct i2c_msg; -- cgit v1.2.3 From 2b7a5056a0a7ff17d5d2004c29c852a92a6bd632 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 14 Jul 2008 22:38:35 +0200 Subject: i2c: New-style EEPROM driver using device IDs Add a new-style driver for most I2C EEPROMs, giving sysfs read/write access to their data. Tested with various chips and clock rates. Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare --- drivers/i2c/chips/Kconfig | 26 ++ drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/at24.c | 583 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/at24.h | 28 +++ 4 files changed, 638 insertions(+) create mode 100644 drivers/i2c/chips/at24.c create mode 100644 include/linux/i2c/at24.h (limited to 'include/linux') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 6326468d5f0b..50e0a4653741 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -14,6 +14,32 @@ config DS1682 This driver can also be built as a module. If so, the module will be called ds1682. +config AT24 + tristate "EEPROMs from most vendors" + depends on SYSFS && EXPERIMENTAL + help + Enable this driver to get read/write support to most I2C EEPROMs, + after you configure the driver to know about each EEPROM on + your target board. Use these generic chip names, instead of + vendor-specific ones like at24c64 or 24lc02: + + 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024 + + Unless you like data loss puzzles, always be sure that any chip + you configure as a 24c32 (32 kbit) or larger is NOT really a + 24c16 (16 kbit) or smaller, and vice versa. Marking the chip + as read-only won't help recover from this. Also, if your chip + has any software write-protect mechanism you may want to review the + code to make sure this driver won't turn it on by accident. + + If you use this with an SMBus adapter instead of an I2C adapter, + full functionality is not available. Only smaller devices are + supported (24c16 and below, max 4 kByte). + + This driver can also be built as a module. If so, the module + will be called at24. + config SENSORS_EEPROM tristate "EEPROM reader" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index e47aca0ca5ae..39e3e69ed125 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -10,6 +10,7 @@ # obj-$(CONFIG_DS1682) += ds1682.o +obj-$(CONFIG_AT24) += at24.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_MAX6875) += max6875.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o diff --git a/drivers/i2c/chips/at24.c b/drivers/i2c/chips/at24.c new file mode 100644 index 000000000000..e764c94f3e3d --- /dev/null +++ b/drivers/i2c/chips/at24.c @@ -0,0 +1,583 @@ +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * Accordingly, explicit board-specific configuration data should be used + * in almost all cases. (One partial exception is an SMBus used to access + * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_data { + struct at24_platform_data chip; + bool use_smbus; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + + u8 *writebuf; + unsigned write_max; + unsigned num_addresses; + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us, and we'll use them with SMBus calls. + */ + struct i2c_client *client[]; +}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned io_limit = 128; +module_param(io_limit, uint, 0); +MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned write_timeout = 25; +module_param(write_timeout, uint, 0); +MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); + +#define AT24_SIZE_BYTELEN 5 +#define AT24_SIZE_FLAGS 8 + +#define AT24_BITMASK(x) (BIT(x) - 1) + +/* create non-zero magic value for given eeprom parameters */ +#define AT24_DEVICE_MAGIC(_len, _flags) \ + ((1 << AT24_SIZE_FLAGS | (_flags)) \ + << AT24_SIZE_BYTELEN | ilog2(_len)) + +static const struct i2c_device_id at24_ids[] = { + /* needs 8 addresses as A0-A2 are ignored */ + { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, + /* old variants can't be handled with this generic entry! */ + { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, + { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + /* spd is a 24c02 in memory DIMMs */ + { "spd", AT24_DEVICE_MAGIC(2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, + { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + /* 24rf08 quirk is handled at i2c-core */ + { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, + { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, + { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, + { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, + { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, + { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "at24", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +/*-------------------------------------------------------------------------*/ + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + */ +static struct i2c_client *at24_translate_offset(struct at24_data *at24, + unsigned *offset) +{ + unsigned i; + + if (at24->chip.flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return at24->client[i]; +} + +static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + struct i2c_client *client; + int status, i; + + memset(msg, 0, sizeof(msg)); + + /* + * REVISIT some multi-address chips don't rollover page reads to + * the next slave address, so we may need to truncate the count. + * Those chips might need another quirk flag. + * + * If the real hardware used four adjacent 24c02 chips and that + * were misconfigured as one 24c08, that would be a similar effect: + * one "eeprom" file not four, but larger reads would fail when + * they crossed certain pages. + */ + + /* + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ + client = at24_translate_offset(at24, &offset); + + if (count > io_limit) + count = io_limit; + + /* Smaller eeproms can work given some SMBus extension calls */ + if (at24->use_smbus) { + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + dev_dbg(&client->dev, "smbus read %zd@%d --> %d\n", + count, offset, status); + return (status < 0) ? -EIO : status; + } + + /* + * When we have a better choice than SMBus calls, use a combined + * I2C message. Write address; then read up to io_limit data bytes. + * Note that read page rollover helps us here (unlike writes). + * msgbuf is u8 and will cast to our needs. + */ + i = 0; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msgbuf[i++] = offset >> 8; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + status = i2c_transfer(client->adapter, msg, 2); + dev_dbg(&client->dev, "i2c read %zd@%d --> %d\n", + count, offset, status); + + if (status == 2) + return count; + else if (status >= 0) + return -EIO; + else + return status; +} + +static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct at24_data *at24; + ssize_t retval = 0; + + at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + if (unlikely(!count)) + return count; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ssize_t status; + + status = at24_eeprom_read(at24, buf, off, count); + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&at24->lock); + + return retval; +} + + +/* + * REVISIT: export at24_bin{read,write}() to let other kernel code use + * eeprom data. For example, it might hold a board's Ethernet address, or + * board-specific calibration data generated on the manufacturing floor. + */ + + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. This routine + * writes at most one page. + */ +static ssize_t at24_eeprom_write(struct at24_data *at24, char *buf, + unsigned offset, size_t count) +{ + struct i2c_client *client; + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned next_page; + + /* Get corresponding I2C address and adjust offset */ + client = at24_translate_offset(at24, &offset); + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->chip.page_size); + if (offset + count > next_page) + count = next_page - offset; + + /* If we'll use I2C calls for I/O, set up the message */ + if (!at24->use_smbus) { + int i = 0; + + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = at24->writebuf; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msg.buf[i++] = offset >> 8; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + } + + /* + * Writes fail if the previous one didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + if (at24->use_smbus) { + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + } else { + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + } + dev_dbg(&client->dev, "write %zd@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct at24_data *at24; + ssize_t retval = 0; + + at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + if (unlikely(!count)) + return count; + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ssize_t status; + + status = at24_eeprom_write(at24, buf, off, count); + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&at24->lock); + + return retval; +} + +/*-------------------------------------------------------------------------*/ + +static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct at24_platform_data chip; + bool writable; + bool use_smbus = false; + struct at24_data *at24; + int err; + unsigned i, num_addresses; + kernel_ulong_t magic; + + if (client->dev.platform_data) { + chip = *(struct at24_platform_data *)client->dev.platform_data; + } else { + if (!id->driver_data) { + err = -ENODEV; + goto err_out; + } + magic = id->driver_data; + chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); + magic >>= AT24_SIZE_BYTELEN; + chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip.page_size = 1; + } + + if (!is_power_of_2(chip.byte_len)) + dev_warn(&client->dev, + "byte_len looks suspicious (no power of 2)!\n"); + if (!is_power_of_2(chip.page_size)) + dev_warn(&client->dev, + "page_size looks suspicious (no power of 2)!\n"); + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (chip.flags & AT24_FLAG_ADDR16) { + err = -EPFNOSUPPORT; + goto err_out; + } + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + err = -EPFNOSUPPORT; + goto err_out; + } + use_smbus = true; + } + + if (chip.flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(chip.byte_len, + (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + + at24 = kzalloc(sizeof(struct at24_data) + + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); + if (!at24) { + err = -ENOMEM; + goto err_out; + } + + mutex_init(&at24->lock); + at24->use_smbus = use_smbus; + at24->chip = chip; + at24->num_addresses = num_addresses; + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + at24->bin.attr.name = "eeprom"; + at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; + at24->bin.attr.owner = THIS_MODULE; + at24->bin.read = at24_bin_read; + at24->bin.size = chip.byte_len; + + writable = !(chip.flags & AT24_FLAG_READONLY); + if (writable) { + if (!use_smbus || i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + + unsigned write_max = chip.page_size; + + at24->bin.write = at24_bin_write; + at24->bin.attr.mode |= S_IWUSR; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + at24->write_max = write_max; + + /* buffer (data + address at the beginning) */ + at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!at24->writebuf) { + err = -ENOMEM; + goto err_struct; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + } + + at24->client[0] = client; + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + at24->client[i] = i2c_new_dummy(client->adapter, + client->addr + i); + if (!at24->client[i]) { + dev_err(&client->dev, "address 0x%02x unavailable\n", + client->addr + i); + err = -EADDRINUSE; + goto err_clients; + } + } + + err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); + if (err) + goto err_clients; + + i2c_set_clientdata(client, at24); + + dev_info(&client->dev, "%Zd byte %s EEPROM %s\n", + at24->bin.size, client->name, + writable ? "(writable)" : "(read-only)"); + dev_dbg(&client->dev, + "page_size %d, num_addresses %d, write_max %d%s\n", + chip.page_size, num_addresses, + at24->write_max, + use_smbus ? ", use_smbus" : ""); + + return 0; + +err_clients: + for (i = 1; i < num_addresses; i++) + if (at24->client[i]) + i2c_unregister_device(at24->client[i]); + + kfree(at24->writebuf); +err_struct: + kfree(at24); +err_out: + dev_dbg(&client->dev, "probe error %d\n", err); + return err; +} + +static int __devexit at24_remove(struct i2c_client *client) +{ + struct at24_data *at24; + int i; + + at24 = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &at24->bin); + + for (i = 1; i < at24->num_addresses; i++) + i2c_unregister_device(at24->client[i]); + + kfree(at24->writebuf); + kfree(at24); + i2c_set_clientdata(client, NULL); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver at24_driver = { + .driver = { + .name = "at24", + .owner = THIS_MODULE, + }, + .probe = at24_probe, + .remove = __devexit_p(at24_remove), + .id_table = at24_ids, +}; + +static int __init at24_init(void) +{ + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&at24_driver); +} +module_init(at24_init); + +static void __exit at24_exit(void) +{ + i2c_del_driver(&at24_driver); +} +module_exit(at24_exit); + +MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); +MODULE_AUTHOR("David Brownell and Wolfram Sang"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/at24.h b/include/linux/i2c/at24.h new file mode 100644 index 000000000000..f6edd522a929 --- /dev/null +++ b/include/linux/i2c/at24.h @@ -0,0 +1,28 @@ +#ifndef _LINUX_AT24_H +#define _LINUX_AT24_H + +#include + +/* + * As seen through Linux I2C, differences between the most common types of I2C + * memory include: + * - How much memory is available (usually specified in bit)? + * - What write page size does it support? + * - Special flags (16 bit addresses, read_only, world readable...)? + * + * If you set up a custom eeprom type, please double-check the parameters. + * Especially page_size needs extra care, as you risk data loss if your value + * is bigger than what the chip actually supports! + */ + +struct at24_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; +#define AT24_FLAG_ADDR16 0x80 /* address pointer is 16 bit */ +#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ +#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ +#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ +}; + +#endif /* _LINUX_AT24_H */ -- cgit v1.2.3 From 4735c98f8447acb1c8977e2b8024640f7bf36dd6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Jul 2008 22:38:36 +0200 Subject: i2c: Add detection capability to new-style drivers Add a mechanism to let new-style i2c drivers optionally autodetect devices they would support on selected buses and ask i2c-core to instantiate them. This is a replacement for legacy i2c drivers, much cleaner. Where drivers had to implement both a legacy i2c_driver and a new-style i2c_driver so far, this mechanism makes it possible to get rid of the legacy i2c_driver and implement both enumerated and detected device support with just one (new-style) i2c_driver. Here is a quick conversion guide for these drivers, step by step: * Delete the legacy driver definition, registration and removal. Delete the attach_adapter and detach_client methods of the legacy driver. * Change the prototype of the legacy detect function from static int foo_detect(struct i2c_adapter *adapter, int address, int kind); to static int foo_detect(struct i2c_client *client, int kind, struct i2c_board_info *info); * Set the new-style driver detect callback to this new function, and set its address_data to &addr_data (addr_data is generally provided by I2C_CLIENT_INSMOD.) * Add the appropriate class to the new-style driver. This is typically the class the legacy attach_adapter method was checking for. Class checking is now mandatory (done by i2c-core.) See for the list of available classes. * Remove the i2c_client allocation and freeing from the detect function. A pre-allocated client is now handed to you by i2c-core, and is freed automatically. * Make the detect function fill the type field of the i2c_board_info structure it was passed as a parameter, and return 0, on success. If the detection fails, return -ENODEV. Signed-off-by: Jean Delvare --- Documentation/i2c/writing-clients | 29 +++++ drivers/i2c/i2c-core.c | 223 ++++++++++++++++++++++++++++++++++++-- include/linux/i2c.h | 36 +++++- 3 files changed, 272 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index 63722d3c9cdf..6b61b3a2e90b 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -44,6 +44,10 @@ static struct i2c_driver foo_driver = { .id_table = foo_ids, .probe = foo_probe, .remove = foo_remove, + /* if device autodetection is needed: */ + .class = I2C_CLASS_SOMETHING, + .detect = foo_detect, + .address_data = &addr_data, /* else, driver uses "legacy" binding model: */ .attach_adapter = foo_attach_adapter, @@ -217,6 +221,31 @@ in the I2C bus driver. You may want to save the returned i2c_client reference for later use. +Device Detection (Standard driver model) +---------------------------------------- + +Sometimes you do not know in advance which I2C devices are connected to +a given I2C bus. This is for example the case of hardware monitoring +devices on a PC's SMBus. In that case, you may want to let your driver +detect supported devices automatically. This is how the legacy model +was working, and is now available as an extension to the standard +driver model (so that we can finally get rid of the legacy model.) + +You simply have to define a detect callback which will attempt to +identify supported devices (returning 0 for supported ones and -ENODEV +for unsupported ones), a list of addresses to probe, and a device type +(or class) so that only I2C buses which may have that type of device +connected (and not otherwise enumerated) will be probed. The i2c +core will then call you back as needed and will instantiate a device +for you for every successful detection. + +Note that this mechanism is purely optional and not suitable for all +devices. You need some reliable way to identify the supported devices +(typically using device-specific, dedicated identification registers), +otherwise misdetections are likely to occur and things can get wrong +quickly. + + Device Deletion (Standard driver model) --------------------------------------- diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 5e249d758828..0a79f7661017 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -42,7 +42,9 @@ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); -#define is_newstyle_driver(d) ((d)->probe || (d)->remove) +#define is_newstyle_driver(d) ((d)->probe || (d)->remove || (d)->detect) + +static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); /* ------------------------------------------------------------------------- */ @@ -418,6 +420,10 @@ static int i2c_do_add_adapter(struct device_driver *d, void *data) struct i2c_driver *driver = to_i2c_driver(d); struct i2c_adapter *adap = data; + /* Detect supported devices on that bus, and instantiate them */ + i2c_detect(adap, driver); + + /* Let legacy drivers scan this bus for matching devices */ if (driver->attach_adapter) { /* We ignore the return code; if it fails, too bad */ driver->attach_adapter(adap); @@ -457,7 +463,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); - /* let legacy drivers scan this bus for matching devices */ + /* Notify drivers */ dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, i2c_do_add_adapter); @@ -563,8 +569,19 @@ static int i2c_do_del_adapter(struct device_driver *d, void *data) { struct i2c_driver *driver = to_i2c_driver(d); struct i2c_adapter *adapter = data; + struct i2c_client *client, *_n; int res; + /* Remove the devices we created ourselves */ + list_for_each_entry_safe(client, _n, &driver->clients, detected) { + if (client->adapter == adapter) { + dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", + client->name, client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + } + if (!driver->detach_adapter) return 0; res = driver->detach_adapter(adapter); @@ -651,7 +668,11 @@ static int __attach_adapter(struct device *dev, void *data) struct i2c_adapter *adapter = to_i2c_adapter(dev); struct i2c_driver *driver = data; - driver->attach_adapter(adapter); + i2c_detect(adapter, driver); + + /* Legacy drivers scan i2c busses directly */ + if (driver->attach_adapter) + driver->attach_adapter(adapter); return 0; } @@ -695,10 +716,9 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); - /* legacy drivers scan i2c busses directly */ - if (driver->attach_adapter) - class_for_each_device(&i2c_adapter_class, driver, - __attach_adapter); + INIT_LIST_HEAD(&driver->clients); + /* Walk the adapters that are already present */ + class_for_each_device(&i2c_adapter_class, driver, __attach_adapter); mutex_unlock(&core_lock); return 0; @@ -709,6 +729,17 @@ static int __detach_adapter(struct device *dev, void *data) { struct i2c_adapter *adapter = to_i2c_adapter(dev); struct i2c_driver *driver = data; + struct i2c_client *client, *_n; + + list_for_each_entry_safe(client, _n, &driver->clients, detected) { + dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", + client->name, client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + + if (is_newstyle_driver(driver)) + return 0; /* Have a look at each adapter, if clients of this driver are still * attached. If so, detach them to be able to kill the driver @@ -747,10 +778,7 @@ void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - /* legacy driver? */ - if (!is_newstyle_driver(driver)) - class_for_each_device(&i2c_adapter_class, driver, - __detach_adapter); + class_for_each_device(&i2c_adapter_class, driver, __detach_adapter); driver_unregister(&driver->driver); pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); @@ -1205,6 +1233,179 @@ int i2c_probe(struct i2c_adapter *adapter, } EXPORT_SYMBOL(i2c_probe); +/* Separate detection function for new-style drivers */ +static int i2c_detect_address(struct i2c_client *temp_client, int kind, + struct i2c_driver *driver) +{ + struct i2c_board_info info; + struct i2c_adapter *adapter = temp_client->adapter; + int addr = temp_client->addr; + int err; + + /* Make sure the address is valid */ + if (addr < 0x03 || addr > 0x77) { + dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", + addr); + return -EINVAL; + } + + /* Skip if already in use */ + if (i2c_check_addr(adapter, addr)) + return 0; + + /* Make sure there is something at this address, unless forced */ + if (kind < 0) { + if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, + I2C_SMBUS_QUICK, NULL) < 0) + return 0; + + /* prevent 24RF08 corruption */ + if ((addr & ~0x0f) == 0x50) + i2c_smbus_xfer(adapter, addr, 0, 0, 0, + I2C_SMBUS_QUICK, NULL); + } + + /* Finally call the custom detection function */ + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = addr; + err = driver->detect(temp_client, kind, &info); + if (err) { + /* -ENODEV is returned if the detection fails. We catch it + here as this isn't an error. */ + return err == -ENODEV ? 0 : err; + } + + /* Consistency check */ + if (info.type[0] == '\0') { + dev_err(&adapter->dev, "%s detection function provided " + "no name for 0x%x\n", driver->driver.name, + addr); + } else { + struct i2c_client *client; + + /* Detection succeeded, instantiate the device */ + dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", + info.type, info.addr); + client = i2c_new_device(adapter, &info); + if (client) + list_add_tail(&client->detected, &driver->clients); + else + dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", + info.type, info.addr); + } + return 0; +} + +static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) +{ + const struct i2c_client_address_data *address_data; + struct i2c_client *temp_client; + int i, err = 0; + int adap_id = i2c_adapter_id(adapter); + + address_data = driver->address_data; + if (!driver->detect || !address_data) + return 0; + + /* Set up a temporary client to help detect callback */ + temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!temp_client) + return -ENOMEM; + temp_client->adapter = adapter; + + /* Force entries are done first, and are not affected by ignore + entries */ + if (address_data->forces) { + const unsigned short * const *forces = address_data->forces; + int kind; + + for (kind = 0; forces[kind]; kind++) { + for (i = 0; forces[kind][i] != I2C_CLIENT_END; + i += 2) { + if (forces[kind][i] == adap_id + || forces[kind][i] == ANY_I2C_BUS) { + dev_dbg(&adapter->dev, "found force " + "parameter for adapter %d, " + "addr 0x%02x, kind %d\n", + adap_id, forces[kind][i + 1], + kind); + temp_client->addr = forces[kind][i + 1]; + err = i2c_detect_address(temp_client, + kind, driver); + if (err) + goto exit_free; + } + } + } + } + + /* Stop here if we can't use SMBUS_QUICK */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) { + if (address_data->probe[0] == I2C_CLIENT_END + && address_data->normal_i2c[0] == I2C_CLIENT_END) + goto exit_free; + + dev_warn(&adapter->dev, "SMBus Quick command not supported, " + "can't probe for chips\n"); + err = -EOPNOTSUPP; + goto exit_free; + } + + /* Stop here if the classes do not match */ + if (!(adapter->class & driver->class)) + goto exit_free; + + /* Probe entries are done second, and are not affected by ignore + entries either */ + for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) { + if (address_data->probe[i] == adap_id + || address_data->probe[i] == ANY_I2C_BUS) { + dev_dbg(&adapter->dev, "found probe parameter for " + "adapter %d, addr 0x%02x\n", adap_id, + address_data->probe[i + 1]); + temp_client->addr = address_data->probe[i + 1]; + err = i2c_detect_address(temp_client, -1, driver); + if (err) + goto exit_free; + } + } + + /* Normal entries are done last, unless shadowed by an ignore entry */ + for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) { + int j, ignore; + + ignore = 0; + for (j = 0; address_data->ignore[j] != I2C_CLIENT_END; + j += 2) { + if ((address_data->ignore[j] == adap_id || + address_data->ignore[j] == ANY_I2C_BUS) + && address_data->ignore[j + 1] + == address_data->normal_i2c[i]) { + dev_dbg(&adapter->dev, "found ignore " + "parameter for adapter %d, " + "addr 0x%02x\n", adap_id, + address_data->ignore[j + 1]); + ignore = 1; + break; + } + } + if (ignore) + continue; + + dev_dbg(&adapter->dev, "found normal entry for adapter %d, " + "addr 0x%02x\n", adap_id, + address_data->normal_i2c[i]); + temp_client->addr = address_data->normal_i2c[i]; + err = i2c_detect_address(temp_client, -1, driver); + if (err) + goto exit_free; + } + + exit_free: + kfree(temp_client); + return err; +} + struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap, struct i2c_board_info *info, diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 50cbab4b62b0..08be0d21864c 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -45,6 +45,7 @@ struct i2c_adapter; struct i2c_client; struct i2c_driver; union i2c_smbus_data; +struct i2c_board_info; /* * The master routines are the ones normally used to transmit data to devices @@ -94,15 +95,33 @@ extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, u8 command, u8 length, const u8 *values); -/* - * A driver is capable of handling one or more physical devices present on - * I2C adapters. This information is used to inform the driver of adapter - * events. +/** + * struct i2c_driver - represent an I2C device driver + * @class: What kind of i2c device we instantiate (for detect) + * @detect: Callback for device detection + * @address_data: The I2C addresses to probe, ignore or force (for detect) + * @clients: List of detected clients we created (for i2c-core use only) * * The driver.owner field should be set to the module owner of this driver. * The driver.name field should be set to the name of this driver. + * + * For automatic device detection, both @detect and @address_data must + * be defined. @class should also be set, otherwise only devices forced + * with module parameters will be created. The detect function must + * fill at least the name field of the i2c_board_info structure it is + * handed upon successful detection, and possibly also the flags field. + * + * If @detect is missing, the driver will still work fine for enumerated + * devices. Detected devices simply won't be supported. This is expected + * for the many I2C/SMBus devices which can't be detected reliably, and + * the ones which can always be enumerated in practice. + * + * The i2c_client structure which is handed to the @detect callback is + * not a real i2c_client. It is initialized just enough so that you can + * call i2c_smbus_read_byte_data and friends on it. Don't do anything + * else with it. In particular, calling dev_dbg and friends on it is + * not allowed. */ - struct i2c_driver { int id; unsigned int class; @@ -142,6 +161,11 @@ struct i2c_driver { struct device_driver driver; const struct i2c_device_id *id_table; + + /* Device detection callback for automatic device creation */ + int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *); + const struct i2c_client_address_data *address_data; + struct list_head clients; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) @@ -157,6 +181,7 @@ struct i2c_driver { * @dev: Driver model device node for the slave. * @irq: indicates the IRQ generated by this device (if any) * @list: list of active/busy clients (DEPRECATED) + * @detected: member of an i2c_driver.clients list * @released: used to synchronize client releases & detaches and references * * An i2c_client identifies a single device (i.e. chip) connected to an @@ -174,6 +199,7 @@ struct i2c_client { struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head list; /* DEPRECATED */ + struct list_head detected; struct completion released; }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) -- cgit v1.2.3 From 11c3b79218390a139f2d474ee1e983a672d5839a Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 12 Jun 2008 14:00:18 -0700 Subject: configfs: Allow ->make_item() and ->make_group() to return detailed errors. The configfs operations ->make_item() and ->make_group() currently return a new item/group. A return of NULL signifies an error. Because of this, -ENOMEM is the only return code bubbled up the stack. Multiple folks have requested the ability to return specific error codes when these operations fail. This patch adds that ability by changing the ->make_item/group() ops to return an int. Also updated are the in-kernel users of configfs. Signed-off-by: Joel Becker --- Documentation/filesystems/configfs/configfs.txt | 10 +++-- .../filesystems/configfs/configfs_example.c | 14 ++++--- drivers/net/netconsole.c | 10 +++-- fs/configfs/dir.c | 13 +++---- fs/dlm/config.c | 45 ++++++++++++++-------- fs/ocfs2/cluster/heartbeat.c | 17 ++++---- fs/ocfs2/cluster/nodemanager.c | 45 ++++++++++++++-------- include/linux/configfs.h | 4 +- 8 files changed, 94 insertions(+), 64 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index 44c97e6accb2..15838d706ea2 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -233,10 +233,12 @@ accomplished via the group operations specified on the group's config_item_type. struct configfs_group_operations { - struct config_item *(*make_item)(struct config_group *group, - const char *name); - struct config_group *(*make_group)(struct config_group *group, - const char *name); + int (*make_item)(struct config_group *group, + const char *name, + struct config_item **new_item); + int (*make_group)(struct config_group *group, + const char *name, + struct config_group **new_group); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 25151fd5c2c6..0b422acd470c 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -273,13 +273,13 @@ static inline struct simple_children *to_simple_children(struct config_item *ite return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; } -static struct config_item *simple_children_make_item(struct config_group *group, const char *name) +static int simple_children_make_item(struct config_group *group, const char *name, struct config_item **new_item) { struct simple_child *simple_child; simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); if (!simple_child) - return NULL; + return -ENOMEM; config_item_init_type_name(&simple_child->item, name, @@ -287,7 +287,8 @@ static struct config_item *simple_children_make_item(struct config_group *group, simple_child->storeme = 0; - return &simple_child->item; + *new_item = &simple_child->item; + return 0; } static struct configfs_attribute simple_children_attr_description = { @@ -359,20 +360,21 @@ static struct configfs_subsystem simple_children_subsys = { * children of its own. */ -static struct config_group *group_children_make_group(struct config_group *group, const char *name) +static int group_children_make_group(struct config_group *group, const char *name, struct config_group **new_group) { struct simple_children *simple_children; simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); if (!simple_children) - return NULL; + return -ENOMEM; config_group_init_type_name(&simple_children->group, name, &simple_children_type); - return &simple_children->group; + *new_group = &simple_children->group; + return 0; } static struct configfs_attribute group_children_attr_description = { diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 665341e43055..387a13395015 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -585,8 +585,9 @@ static struct config_item_type netconsole_target_type = { * Group operations and type for netconsole_subsys. */ -static struct config_item *make_netconsole_target(struct config_group *group, - const char *name) +static int make_netconsole_target(struct config_group *group, + const char *name, + struct config_item **new_item) { unsigned long flags; struct netconsole_target *nt; @@ -598,7 +599,7 @@ static struct config_item *make_netconsole_target(struct config_group *group, nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) { printk(KERN_ERR "netconsole: failed to allocate memory\n"); - return NULL; + return -ENOMEM; } nt->np.name = "netconsole"; @@ -615,7 +616,8 @@ static struct config_item *make_netconsole_target(struct config_group *group, list_add(&nt->list, &target_list); spin_unlock_irqrestore(&target_list_lock, flags); - return &nt->item; + *new_item = &nt->item; + return 0; } static void drop_netconsole_target(struct config_group *group, diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 614e382a6049..0e64312a084c 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1073,25 +1073,24 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) group = NULL; item = NULL; if (type->ct_group_ops->make_group) { - group = type->ct_group_ops->make_group(to_config_group(parent_item), name); - if (group) { + ret = type->ct_group_ops->make_group(to_config_group(parent_item), name, &group); + if (!ret) { link_group(to_config_group(parent_item), group); item = &group->cg_item; } } else { - item = type->ct_group_ops->make_item(to_config_group(parent_item), name); - if (item) + ret = type->ct_group_ops->make_item(to_config_group(parent_item), name, &item); + if (!ret) link_obj(parent_item, item); } mutex_unlock(&subsys->su_mutex); kfree(name); - if (!item) { + if (ret) { /* - * If item == NULL, then link_obj() was never called. + * If ret != 0, then link_obj() was never called. * There are no extra references to clean up. */ - ret = -ENOMEM; goto out_put; } diff --git a/fs/dlm/config.c b/fs/dlm/config.c index eac23bd288b2..492d8caaaf25 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -41,16 +41,20 @@ struct comm; struct nodes; struct node; -static struct config_group *make_cluster(struct config_group *, const char *); +static int make_cluster(struct config_group *, const char *, + struct config_group **); static void drop_cluster(struct config_group *, struct config_item *); static void release_cluster(struct config_item *); -static struct config_group *make_space(struct config_group *, const char *); +static int make_space(struct config_group *, const char *, + struct config_group **); static void drop_space(struct config_group *, struct config_item *); static void release_space(struct config_item *); -static struct config_item *make_comm(struct config_group *, const char *); +static int make_comm(struct config_group *, const char *, + struct config_item **); static void drop_comm(struct config_group *, struct config_item *); static void release_comm(struct config_item *); -static struct config_item *make_node(struct config_group *, const char *); +static int make_node(struct config_group *, const char *, + struct config_item **); static void drop_node(struct config_group *, struct config_item *); static void release_node(struct config_item *); @@ -392,8 +396,8 @@ static struct node *to_node(struct config_item *i) return i ? container_of(i, struct node, item) : NULL; } -static struct config_group *make_cluster(struct config_group *g, - const char *name) +static int make_cluster(struct config_group *g, const char *name, + struct config_group **new_g) { struct cluster *cl = NULL; struct spaces *sps = NULL; @@ -431,14 +435,15 @@ static struct config_group *make_cluster(struct config_group *g, space_list = &sps->ss_group; comm_list = &cms->cs_group; - return &cl->group; + *new_g = &cl->group; + return 0; fail: kfree(cl); kfree(gps); kfree(sps); kfree(cms); - return NULL; + return -ENOMEM; } static void drop_cluster(struct config_group *g, struct config_item *i) @@ -466,7 +471,8 @@ static void release_cluster(struct config_item *i) kfree(cl); } -static struct config_group *make_space(struct config_group *g, const char *name) +static int make_space(struct config_group *g, const char *name, + struct config_group **new_g) { struct space *sp = NULL; struct nodes *nds = NULL; @@ -489,13 +495,14 @@ static struct config_group *make_space(struct config_group *g, const char *name) INIT_LIST_HEAD(&sp->members); mutex_init(&sp->members_lock); sp->members_count = 0; - return &sp->group; + *new_g = &sp->group; + return 0; fail: kfree(sp); kfree(gps); kfree(nds); - return NULL; + return -ENOMEM; } static void drop_space(struct config_group *g, struct config_item *i) @@ -522,19 +529,21 @@ static void release_space(struct config_item *i) kfree(sp); } -static struct config_item *make_comm(struct config_group *g, const char *name) +static int make_comm(struct config_group *g, const char *name, + struct config_item **new_i) { struct comm *cm; cm = kzalloc(sizeof(struct comm), GFP_KERNEL); if (!cm) - return NULL; + return -ENOMEM; config_item_init_type_name(&cm->item, name, &comm_type); cm->nodeid = -1; cm->local = 0; cm->addr_count = 0; - return &cm->item; + *new_i = &cm->item; + return 0; } static void drop_comm(struct config_group *g, struct config_item *i) @@ -554,14 +563,15 @@ static void release_comm(struct config_item *i) kfree(cm); } -static struct config_item *make_node(struct config_group *g, const char *name) +static int make_node(struct config_group *g, const char *name, + struct config_item **new_i) { struct space *sp = to_space(g->cg_item.ci_parent); struct node *nd; nd = kzalloc(sizeof(struct node), GFP_KERNEL); if (!nd) - return NULL; + return -ENOMEM; config_item_init_type_name(&nd->item, name, &node_type); nd->nodeid = -1; @@ -573,7 +583,8 @@ static struct config_item *make_node(struct config_group *g, const char *name) sp->members_count++; mutex_unlock(&sp->members_lock); - return &nd->item; + *new_i = &nd->item; + return 0; } static void drop_node(struct config_group *g, struct config_item *i) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f02ccb34604d..443d108211ab 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1489,25 +1489,28 @@ static struct o2hb_heartbeat_group *to_o2hb_heartbeat_group(struct config_group : NULL; } -static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *group, - const char *name) +static int o2hb_heartbeat_group_make_item(struct config_group *group, + const char *name, + struct config_item **new_item) { struct o2hb_region *reg = NULL; - struct config_item *ret = NULL; + int ret = 0; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); - if (reg == NULL) - goto out; /* ENOMEM */ + if (reg == NULL) { + ret = -ENOMEM; + goto out; + } config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - ret = ®->hr_item; + *new_item = ®->hr_item; spin_lock(&o2hb_live_lock); list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); out: - if (ret == NULL) + if (ret) kfree(reg); return ret; diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index cfdb08b484ed..b364b7052e46 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -644,27 +644,32 @@ out: return ret; } -static struct config_item *o2nm_node_group_make_item(struct config_group *group, - const char *name) +static int o2nm_node_group_make_item(struct config_group *group, + const char *name, + struct config_item **new_item) { struct o2nm_node *node = NULL; - struct config_item *ret = NULL; + int ret = 0; - if (strlen(name) > O2NM_MAX_NAME_LEN) - goto out; /* ENAMETOOLONG */ + if (strlen(name) > O2NM_MAX_NAME_LEN) { + ret = -ENAMETOOLONG; + goto out; + } node = kzalloc(sizeof(struct o2nm_node), GFP_KERNEL); - if (node == NULL) - goto out; /* ENOMEM */ + if (node == NULL) { + ret = -ENOMEM; + goto out; + } strcpy(node->nd_name, name); /* use item.ci_namebuf instead? */ config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); - ret = &node->nd_item; + *new_item = &node->nd_item; out: - if (ret == NULL) + if (ret) kfree(node); return ret; @@ -751,25 +756,31 @@ static struct o2nm_cluster_group *to_o2nm_cluster_group(struct config_group *gro } #endif -static struct config_group *o2nm_cluster_group_make_group(struct config_group *group, - const char *name) +static int o2nm_cluster_group_make_group(struct config_group *group, + const char *name, + struct config_group **new_group) { struct o2nm_cluster *cluster = NULL; struct o2nm_node_group *ns = NULL; - struct config_group *o2hb_group = NULL, *ret = NULL; + struct config_group *o2hb_group = NULL; void *defs = NULL; + int ret = 0; /* this runs under the parent dir's i_mutex; there can be only * one caller in here at a time */ - if (o2nm_single_cluster) - goto out; /* ENOSPC */ + if (o2nm_single_cluster) { + ret = -ENOSPC; + goto out; + } cluster = kzalloc(sizeof(struct o2nm_cluster), GFP_KERNEL); ns = kzalloc(sizeof(struct o2nm_node_group), GFP_KERNEL); defs = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL); o2hb_group = o2hb_alloc_hb_set(); - if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) + if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) { + ret = -ENOMEM; goto out; + } config_group_init_type_name(&cluster->cl_group, name, &o2nm_cluster_type); @@ -786,11 +797,11 @@ static struct config_group *o2nm_cluster_group_make_group(struct config_group *g cluster->cl_idle_timeout_ms = O2NET_IDLE_TIMEOUT_MS_DEFAULT; cluster->cl_keepalive_delay_ms = O2NET_KEEPALIVE_DELAY_MS_DEFAULT; - ret = &cluster->cl_group; + *new_group = &cluster->cl_group; o2nm_single_cluster = cluster; out: - if (ret == NULL) { + if (ret) { kfree(cluster); kfree(ns); o2hb_free_hb_set(o2hb_group); diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 3ae65b1bf90f..0488f937634a 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -165,8 +165,8 @@ struct configfs_item_operations { }; struct configfs_group_operations { - struct config_item *(*make_item)(struct config_group *group, const char *name); - struct config_group *(*make_group)(struct config_group *group, const char *name); + int (*make_item)(struct config_group *group, const char *name, struct config_item **new_item); + int (*make_group)(struct config_group *group, const char *name, struct config_group **new_group); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); -- cgit v1.2.3 From c300bd2fb583afb6d68804afd38bc90b31310d95 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 10 Jul 2008 02:16:44 +0200 Subject: PCI: include linux/pm_wakeup.h for device_set_wakeup_capable drivers/pci/pci.c needs pm_wakeup.h since it uses device_set_wakup_capable(). The latter also needs to be stubbed out for !CONFIG_PM. Signed-off-by: Stephen Rothwell Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 1 + include/linux/pm_wakeup.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ace518116cc5..44a46c92b721 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* isa_dma_bridge_buggy */ #include "pci.h" diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 3af0c8d05cdc..0aae7776185e 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -63,6 +63,8 @@ static inline void device_init_wakeup(struct device *dev, int val) dev->power.can_wakeup = !!val; } +static inline void device_set_wakeup_capable(struct device *dev, int val) { } + static inline int device_can_wakeup(struct device *dev) { return dev->power.can_wakeup; -- cgit v1.2.3 From 72d9794f444734af56ef12833b496326643e2964 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 20:36:32 -0700 Subject: net-sched: cls_flow: add perturbation support Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/pkt_cls.h | 1 + net/sched/cls_flow.c | 52 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index 99efbed81fa2..7cf7824df778 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -374,6 +374,7 @@ enum TCA_FLOW_ACT, TCA_FLOW_POLICE, TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, __TCA_FLOW_MAX }; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 971b867e0484..8f63a1a94014 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -36,6 +36,8 @@ struct flow_filter { struct list_head list; struct tcf_exts exts; struct tcf_ematch_tree ematches; + struct timer_list perturb_timer; + u32 perturb_period; u32 handle; u32 nkeys; @@ -47,11 +49,9 @@ struct flow_filter { u32 addend; u32 divisor; u32 baseclass; + u32 hashrnd; }; -static u32 flow_hashrnd __read_mostly; -static int flow_hashrnd_initted __read_mostly; - static const struct tcf_ext_map flow_ext_map = { .action = TCA_FLOW_ACT, .police = TCA_FLOW_POLICE, @@ -348,7 +348,7 @@ static int flow_classify(struct sk_buff *skb, struct tcf_proto *tp, } if (f->mode == FLOW_MODE_HASH) - classid = jhash2(keys, f->nkeys, flow_hashrnd); + classid = jhash2(keys, f->nkeys, f->hashrnd); else { classid = keys[0]; classid = (classid & f->mask) ^ f->xor; @@ -369,6 +369,15 @@ static int flow_classify(struct sk_buff *skb, struct tcf_proto *tp, return -1; } +static void flow_perturbation(unsigned long arg) +{ + struct flow_filter *f = (struct flow_filter *)arg; + + get_random_bytes(&f->hashrnd, 4); + if (f->perturb_period) + mod_timer(&f->perturb_timer, jiffies + f->perturb_period); +} + static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_KEYS] = { .type = NLA_U32 }, [TCA_FLOW_MODE] = { .type = NLA_U32 }, @@ -381,6 +390,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_ACT] = { .type = NLA_NESTED }, [TCA_FLOW_POLICE] = { .type = NLA_NESTED }, [TCA_FLOW_EMATCHES] = { .type = NLA_NESTED }, + [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, }; static int flow_change(struct tcf_proto *tp, unsigned long base, @@ -394,6 +404,7 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, struct tcf_exts e; struct tcf_ematch_tree t; unsigned int nkeys = 0; + unsigned int perturb_period = 0; u32 baseclass = 0; u32 keymask = 0; u32 mode; @@ -442,6 +453,14 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, mode = nla_get_u32(tb[TCA_FLOW_MODE]); if (mode != FLOW_MODE_HASH && nkeys > 1) goto err2; + + if (mode == FLOW_MODE_HASH) + perturb_period = f->perturb_period; + if (tb[TCA_FLOW_PERTURB]) { + if (mode != FLOW_MODE_HASH) + goto err2; + perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; + } } else { err = -EINVAL; if (!handle) @@ -455,6 +474,12 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, if (mode != FLOW_MODE_HASH && nkeys > 1) goto err2; + if (tb[TCA_FLOW_PERTURB]) { + if (mode != FLOW_MODE_HASH) + goto err2; + perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; + } + if (TC_H_MAJ(baseclass) == 0) baseclass = TC_H_MAKE(tp->q->handle, baseclass); if (TC_H_MIN(baseclass) == 0) @@ -467,6 +492,11 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, f->handle = handle; f->mask = ~0U; + + get_random_bytes(&f->hashrnd, 4); + f->perturb_timer.function = flow_perturbation; + f->perturb_timer.data = (unsigned long)f; + init_timer_deferrable(&f->perturb_timer); } tcf_exts_change(tp, &f->exts, &e); @@ -495,6 +525,11 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, if (baseclass) f->baseclass = baseclass; + f->perturb_period = perturb_period; + del_timer(&f->perturb_timer); + if (perturb_period) + mod_timer(&f->perturb_timer, jiffies + perturb_period); + if (*arg == 0) list_add_tail(&f->list, &head->filters); @@ -512,6 +547,7 @@ err1: static void flow_destroy_filter(struct tcf_proto *tp, struct flow_filter *f) { + del_timer_sync(&f->perturb_timer); tcf_exts_destroy(tp, &f->exts); tcf_em_tree_destroy(tp, &f->ematches); kfree(f); @@ -532,11 +568,6 @@ static int flow_init(struct tcf_proto *tp) { struct flow_head *head; - if (!flow_hashrnd_initted) { - get_random_bytes(&flow_hashrnd, 4); - flow_hashrnd_initted = 1; - } - head = kzalloc(sizeof(*head), GFP_KERNEL); if (head == NULL) return -ENOBUFS; @@ -605,6 +636,9 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh, if (f->baseclass) NLA_PUT_U32(skb, TCA_FLOW_BASECLASS, f->baseclass); + if (f->perturb_period) + NLA_PUT_U32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ); + if (tcf_exts_dump(skb, &f->exts, &flow_ext_map) < 0) goto nla_put_failure; #ifdef CONFIG_NET_EMATCH -- cgit v1.2.3 From f271b2cc78f09c93ccd00a2056d3237134bf994c Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Mon, 14 Jul 2008 22:18:19 -0700 Subject: tun: Fix/rewrite packet filtering logic Please see the following thread to get some context on this http://marc.info/?l=linux-netdev&m=121564433018903&w=2 Basically the issue is that current multi-cast filtering stuff in the TUN/TAP driver is seriously broken. Original patch went in without proper review and ACK. It was broken and confusing to start with and subsequent patches broke it completely. To give you an idea of what's broken here are some of the issues: - Very confusing comments throughout the code that imply that the character device is a network interface in its own right, and that packets are passed between the two nics. Which is completely wrong. - Wrong set of ioctls is used for setting up filters. They look like shortcuts for manipulating state of the tun/tap network interface but in reality manipulate the state of the TX filter. - ioctls that were originally used for setting address of the the TX filter got "fixed" and now set the address of the network interface itself. Which made filter totaly useless. - Filtering is done too late. Instead of filtering early on, to avoid unnecessary wakeups, filtering is done in the read() call. The list goes on and on :) So the patch cleans all that up. It introduces simple and clean interface for setting up TX filters (TUNSETTXFILTER + tun_filter spec) and does filtering before enqueuing the packets. TX filtering is useful in the scenarios where TAP is part of a bridge, in which case it gets all broadcast, multicast and potentially other packets when the bridge is learning. So for example Ethernet tunnelling app may want to setup TX filters to avoid tunnelling multicast traffic. QEMU and other hypervisors can push RX filtering that is currently done in the guest into the host context therefore saving wakeups and unnecessary data transfer. Signed-off-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 316 ++++++++++++++++++++++++------------------------- include/linux/if_tun.h | 24 +++- 2 files changed, 174 insertions(+), 166 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 2693f883ecda..901551c8ca0e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -18,15 +18,11 @@ /* * Changes: * - * Brian Braunstein 2007/03/23 - * Fixed hw address handling. Now net_device.dev_addr is kept consistent - * with tun.dev_addr when the address is set by this module. - * * Mike Kershaw 2005/08/14 * Add TUNSETLINK ioctl to set the link encapsulation * * Mark Smith - * Use random_ether_addr() for tap MAC address. + * Use random_ether_addr() for tap MAC address. * * Harald Roelle 2004/04/20 * Fixes in packet dropping, queue length setting and queue wakeup. @@ -83,9 +79,16 @@ static int debug; #define DBG1( a... ) #endif +#define FLT_EXACT_COUNT 8 +struct tap_filter { + unsigned int count; /* Number of addrs. Zero means disabled */ + u32 mask[2]; /* Mask of the hashed addrs */ + unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN]; +}; + struct tun_struct { struct list_head list; - unsigned long flags; + unsigned int flags; int attached; uid_t owner; gid_t group; @@ -94,19 +97,119 @@ struct tun_struct { struct sk_buff_head readq; struct net_device *dev; + struct fasync_struct *fasync; - struct fasync_struct *fasync; - - unsigned long if_flags; - u8 dev_addr[ETH_ALEN]; - u32 chr_filter[2]; - u32 net_filter[2]; + struct tap_filter txflt; #ifdef TUN_DEBUG int debug; #endif }; +/* TAP filterting */ +static void addr_hash_set(u32 *mask, const u8 *addr) +{ + int n = ether_crc(ETH_ALEN, addr) >> 26; + mask[n >> 5] |= (1 << (n & 31)); +} + +static unsigned int addr_hash_test(const u32 *mask, const u8 *addr) +{ + int n = ether_crc(ETH_ALEN, addr) >> 26; + return mask[n >> 5] & (1 << (n & 31)); +} + +static int update_filter(struct tap_filter *filter, void __user *arg) +{ + struct { u8 u[ETH_ALEN]; } *addr; + struct tun_filter uf; + int err, alen, n, nexact; + + if (copy_from_user(&uf, arg, sizeof(uf))) + return -EFAULT; + + if (!uf.count) { + /* Disabled */ + filter->count = 0; + return 0; + } + + alen = ETH_ALEN * uf.count; + addr = kmalloc(alen, GFP_KERNEL); + if (!addr) + return -ENOMEM; + + if (copy_from_user(addr, arg + sizeof(uf), alen)) { + err = -EFAULT; + goto done; + } + + /* The filter is updated without holding any locks. Which is + * perfectly safe. We disable it first and in the worst + * case we'll accept a few undesired packets. */ + filter->count = 0; + wmb(); + + /* Use first set of addresses as an exact filter */ + for (n = 0; n < uf.count && n < FLT_EXACT_COUNT; n++) + memcpy(filter->addr[n], addr[n].u, ETH_ALEN); + + nexact = n; + + /* The rest is hashed */ + memset(filter->mask, 0, sizeof(filter->mask)); + for (; n < uf.count; n++) + addr_hash_set(filter->mask, addr[n].u); + + /* For ALLMULTI just set the mask to all ones. + * This overrides the mask populated above. */ + if ((uf.flags & TUN_FLT_ALLMULTI)) + memset(filter->mask, ~0, sizeof(filter->mask)); + + /* Now enable the filter */ + wmb(); + filter->count = nexact; + + /* Return the number of exact filters */ + err = nexact; + +done: + kfree(addr); + return err; +} + +/* Returns: 0 - drop, !=0 - accept */ +static int run_filter(struct tap_filter *filter, const struct sk_buff *skb) +{ + /* Cannot use eth_hdr(skb) here because skb_mac_hdr() is incorrect + * at this point. */ + struct ethhdr *eh = (struct ethhdr *) skb->data; + int i; + + /* Exact match */ + for (i = 0; i < filter->count; i++) + if (!compare_ether_addr(eh->h_dest, filter->addr[i])) + return 1; + + /* Inexact match (multicast only) */ + if (is_multicast_ether_addr(eh->h_dest)) + return addr_hash_test(filter->mask, eh->h_dest); + + return 0; +} + +/* + * Checks whether the packet is accepted or not. + * Returns: 0 - drop, !=0 - accept + */ +static int check_filter(struct tap_filter *filter, const struct sk_buff *skb) +{ + if (!filter->count) + return 1; + + return run_filter(filter, skb); +} + /* Network device part of the driver */ static unsigned int tun_net_id; @@ -141,7 +244,12 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) if (!tun->attached) goto drop; - /* Packet dropping */ + /* Drop if the filter does not like it. + * This is a noop if the filter is disabled. + * Filter can be enabled only for the TAP devices. */ + if (!check_filter(&tun->txflt, skb)) + goto drop; + if (skb_queue_len(&tun->readq) >= dev->tx_queue_len) { if (!(tun->flags & TUN_ONE_QUEUE)) { /* Normal queueing mode. */ @@ -158,7 +266,7 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) } } - /* Queue packet */ + /* Enqueue packet */ skb_queue_tail(&tun->readq, skb); dev->trans_start = jiffies; @@ -174,41 +282,14 @@ drop: return 0; } -/** Add the specified Ethernet address to this multicast filter. */ -static void -add_multi(u32* filter, const u8* addr) -{ - int bit_nr = ether_crc(ETH_ALEN, addr) >> 26; - filter[bit_nr >> 5] |= 1 << (bit_nr & 31); -} - -/** Remove the specified Ethernet addres from this multicast filter. */ -static void -del_multi(u32* filter, const u8* addr) +static void tun_net_mclist(struct net_device *dev) { - int bit_nr = ether_crc(ETH_ALEN, addr) >> 26; - filter[bit_nr >> 5] &= ~(1 << (bit_nr & 31)); -} - -/** Update the list of multicast groups to which the network device belongs. - * This list is used to filter packets being sent from the character device to - * the network device. */ -static void -tun_net_mclist(struct net_device *dev) -{ - struct tun_struct *tun = netdev_priv(dev); - const struct dev_mc_list *mclist; - int i; - DECLARE_MAC_BUF(mac); - DBG(KERN_DEBUG "%s: tun_net_mclist: mc_count %d\n", - dev->name, dev->mc_count); - memset(tun->chr_filter, 0, sizeof tun->chr_filter); - for (i = 0, mclist = dev->mc_list; i < dev->mc_count && mclist != NULL; - i++, mclist = mclist->next) { - add_multi(tun->net_filter, mclist->dmi_addr); - DBG(KERN_DEBUG "%s: tun_net_mclist: %s\n", - dev->name, print_mac(mac, mclist->dmi_addr)); - } + /* + * This callback is supposed to deal with mc filter in + * _rx_ path and has nothing to do with the _tx_ path. + * In rx path we always accept everything userspace gives us. + */ + return; } #define MIN_MTU 68 @@ -244,13 +325,11 @@ static void tun_net_init(struct net_device *dev) case TUN_TAP_DEV: /* Ethernet TAP Device */ - dev->set_multicast_list = tun_net_mclist; - ether_setup(dev); - dev->change_mtu = tun_net_change_mtu; + dev->change_mtu = tun_net_change_mtu; + dev->set_multicast_list = tun_net_mclist; - /* random address already created for us by tun_set_iff, use it */ - memcpy(dev->dev_addr, tun->dev_addr, min(sizeof(tun->dev_addr), sizeof(dev->dev_addr)) ); + random_ether_addr(dev->dev_addr); dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; @@ -486,7 +565,6 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, DECLARE_WAITQUEUE(wait, current); struct sk_buff *skb; ssize_t len, ret = 0; - DECLARE_MAC_BUF(mac); if (!tun) return -EBADFD; @@ -499,10 +577,6 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, add_wait_queue(&tun->read_wait, &wait); while (len) { - const u8 ones[ ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - u8 addr[ ETH_ALEN]; - int bit_nr; - current->state = TASK_INTERRUPTIBLE; /* Read frames from the queue */ @@ -522,36 +596,9 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, } netif_wake_queue(tun->dev); - /** Decide whether to accept this packet. This code is designed to - * behave identically to an Ethernet interface. Accept the packet if - * - we are promiscuous. - * - the packet is addressed to us. - * - the packet is broadcast. - * - the packet is multicast and - * - we are multicast promiscous. - * - we belong to the multicast group. - */ - skb_copy_from_linear_data(skb, addr, min_t(size_t, sizeof addr, - skb->len)); - bit_nr = ether_crc(sizeof addr, addr) >> 26; - if ((tun->if_flags & IFF_PROMISC) || - memcmp(addr, tun->dev_addr, sizeof addr) == 0 || - memcmp(addr, ones, sizeof addr) == 0 || - (((addr[0] == 1 && addr[1] == 0 && addr[2] == 0x5e) || - (addr[0] == 0x33 && addr[1] == 0x33)) && - ((tun->if_flags & IFF_ALLMULTI) || - (tun->chr_filter[bit_nr >> 5] & (1 << (bit_nr & 31)))))) { - DBG(KERN_DEBUG "%s: tun_chr_readv: accepted: %s\n", - tun->dev->name, print_mac(mac, addr)); - ret = tun_put_user(tun, skb, (struct iovec *) iv, len); - kfree_skb(skb); - break; - } else { - DBG(KERN_DEBUG "%s: tun_chr_readv: rejected: %s\n", - tun->dev->name, print_mac(mac, addr)); - kfree_skb(skb); - continue; - } + ret = tun_put_user(tun, skb, (struct iovec *) iv, len); + kfree_skb(skb); + break; } current->state = TASK_RUNNING; @@ -647,12 +694,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun = netdev_priv(dev); tun->dev = dev; tun->flags = flags; - /* Be promiscuous by default to maintain previous behaviour. */ - tun->if_flags = IFF_PROMISC; - /* Generate random Ethernet address. */ - *(__be16 *)tun->dev_addr = htons(0x00FF); - get_random_bytes(tun->dev_addr + sizeof(u16), 4); - memset(tun->chr_filter, 0, sizeof tun->chr_filter); + tun->txflt.count = 0; tun_net_init(dev); @@ -751,6 +793,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, struct tun_struct *tun = file->private_data; void __user* argp = (void __user*)arg; struct ifreq ifr; + int ret; DECLARE_MAC_BUF(mac); if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) @@ -826,9 +869,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; case TUNSETLINK: - { - int ret; - /* Only allow setting the type when the interface is down */ rtnl_lock(); if (tun->dev->flags & IFF_UP) { @@ -842,94 +882,44 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, } rtnl_unlock(); return ret; - } #ifdef TUN_DEBUG case TUNSETDEBUG: tun->debug = arg; break; #endif - case TUNSETOFFLOAD: - { - int ret; rtnl_lock(); ret = set_offload(tun->dev, arg); rtnl_unlock(); return ret; - } - case SIOCGIFFLAGS: - ifr.ifr_flags = tun->if_flags; - if (copy_to_user( argp, &ifr, sizeof ifr)) - return -EFAULT; - return 0; - - case SIOCSIFFLAGS: - /** Set the character device's interface flags. Currently only - * IFF_PROMISC and IFF_ALLMULTI are used. */ - tun->if_flags = ifr.ifr_flags; - DBG(KERN_INFO "%s: interface flags 0x%lx\n", - tun->dev->name, tun->if_flags); - return 0; + case TUNSETTXFILTER: + /* Can be set only for TAPs */ + if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV) + return -EINVAL; + rtnl_lock(); + ret = update_filter(&tun->txflt, (void *) __user arg); + rtnl_unlock(); + return ret; case SIOCGIFHWADDR: - /* Note: the actual net device's address may be different */ - memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr, - min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); - if (copy_to_user( argp, &ifr, sizeof ifr)) + /* Get hw addres */ + memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN); + ifr.ifr_hwaddr.sa_family = tun->dev->type; + if (copy_to_user(argp, &ifr, sizeof ifr)) return -EFAULT; return 0; case SIOCSIFHWADDR: - { - /* try to set the actual net device's hw address */ - int ret; + /* Set hw address */ + DBG(KERN_DEBUG "%s: set hw address: %s\n", + tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data)); rtnl_lock(); ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); rtnl_unlock(); - - if (ret == 0) { - /** Set the character device's hardware address. This is used when - * filtering packets being sent from the network device to the character - * device. */ - memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data, - min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); - DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n", - tun->dev->name, - tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2], - tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]); - } - - return ret; - } - - case SIOCADDMULTI: - /** Add the specified group to the character device's multicast filter - * list. */ - rtnl_lock(); - netif_tx_lock_bh(tun->dev); - add_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data); - netif_tx_unlock_bh(tun->dev); - rtnl_unlock(); - - DBG(KERN_DEBUG "%s: add multi: %s\n", - tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data)); - return 0; - - case SIOCDELMULTI: - /** Remove the specified group from the character device's multicast - * filter list. */ - rtnl_lock(); - netif_tx_lock_bh(tun->dev); - del_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data); - netif_tx_unlock_bh(tun->dev); - rtnl_unlock(); - - DBG(KERN_DEBUG "%s: del multi: %s\n", - tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data)); - return 0; + return ret; default: return -EINVAL; diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 563fae542da6..4c6307ad9fdb 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -17,6 +17,7 @@ #define __IF_TUN_H #include +#include /* Read queue size */ #define TUN_READQ_SIZE 500 @@ -42,7 +43,8 @@ #define TUNSETLINK _IOW('T', 205, int) #define TUNSETGROUP _IOW('T', 206, int) #define TUNGETFEATURES _IOR('T', 207, unsigned int) -#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNSETTXFILTER _IOW('T', 209, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 @@ -57,10 +59,26 @@ #define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ #define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ +#define TUN_PKT_STRIP 0x0001 struct tun_pi { - unsigned short flags; + __u16 flags; __be16 proto; }; -#define TUN_PKT_STRIP 0x0001 + +/* + * Filter spec (used for SETXXFILTER ioctls) + * This stuff is applicable only to the TAP (Ethernet) devices. + * If the count is zero the filter is disabled and the driver accepts + * all packets (promisc mode). + * If the filter is enabled in order to accept broadcast packets + * broadcast addr must be explicitly included in the addr list. + */ +#define TUN_FLT_ALLMULTI 0x0001 /* Accept all multicast packets */ +struct tun_filter { + __u16 flags; /* TUN_FLT_ flags see above */ + __u16 count; /* Number of addresses */ + __u8 addr[0][ETH_ALEN]; +}; #endif /* __IF_TUN_H */ -- cgit v1.2.3 From 6aa895b047720f71ec4eb11452f7c3ce8426941f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:49:06 -0700 Subject: vlan: Don't store VLAN tag in cb Use a real skb member to store the skb to avoid clashes with qdiscs, which are allowed to use the cb area themselves. As currently only real devices that consume the skb set the NETIF_F_HW_VLAN_TX flag, no explicit invalidation is neccessary. The new member fills a hole on 64 bit, the skb layout changes from: __u32 mark; /* 172 4 */ sk_buff_data_t transport_header; /* 176 4 */ sk_buff_data_t network_header; /* 180 4 */ sk_buff_data_t mac_header; /* 184 4 */ sk_buff_data_t tail; /* 188 4 */ /* --- cacheline 3 boundary (192 bytes) --- */ sk_buff_data_t end; /* 192 4 */ /* XXX 4 bytes hole, try to pack */ to __u32 mark; /* 172 4 */ __u16 vlan_tci; /* 176 2 */ /* XXX 2 bytes hole, try to pack */ sk_buff_data_t transport_header; /* 180 4 */ sk_buff_data_t network_header; /* 184 4 */ Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 31 +++++++------------------------ include/linux/skbuff.h | 3 +++ net/core/skbuff.c | 3 +++ 3 files changed, 13 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 93f5d9b0e9f9..9e7b49b8062d 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -105,17 +105,8 @@ static inline void vlan_group_set_device(struct vlan_group *vg, array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] = dev; } -/* VLAN tx hw acceleration helpers. */ -struct vlan_skb_tx_cookie { - u32 magic; - u32 vlan_tag; -}; - -#define VLAN_TX_COOKIE_MAGIC 0x564c414e /* "VLAN" in ascii. */ -#define VLAN_TX_SKB_CB(__skb) ((struct vlan_skb_tx_cookie *)&((__skb)->cb[0])) -#define vlan_tx_tag_present(__skb) \ - (VLAN_TX_SKB_CB(__skb)->magic == VLAN_TX_COOKIE_MAGIC) -#define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag) +#define vlan_tx_tag_present(__skb) ((__skb)->vlan_tci) +#define vlan_tx_tag_get(__skb) ((__skb)->vlan_tci) #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); @@ -210,17 +201,12 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) * @skb: skbuff to tag * @vlan_tci: VLAN TCI to insert * - * Puts the VLAN TCI in @skb->cb[] and lets the device do the rest + * Puts the VLAN TCI in @skb->vlan_tci and lets the device do the rest */ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, u16 vlan_tci) { - struct vlan_skb_tx_cookie *cookie; - - cookie = VLAN_TX_SKB_CB(skb); - cookie->magic = VLAN_TX_COOKIE_MAGIC; - cookie->vlan_tag = vlan_tci; - + skb->vlan_tci = vlan_tci; return skb; } @@ -267,16 +253,13 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) * @skb: skbuff to query * @vlan_tci: buffer to store vlaue * - * Returns error if @skb->cb[] is not set correctly + * Returns error if @skb->vlan_tci is not set correctly */ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb, u16 *vlan_tci) { - struct vlan_skb_tx_cookie *cookie; - - cookie = VLAN_TX_SKB_CB(skb); - if (cookie->magic == VLAN_TX_COOKIE_MAGIC) { - *vlan_tci = cookie->vlan_tag; + if (vlan_tx_tag_present(skb)) { + *vlan_tci = skb->vlan_tci; return 0; } else { *vlan_tci = 0; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8f10e3d08fd9..7ea44f6621f2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -246,6 +246,7 @@ typedef unsigned char *sk_buff_data_t; * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions * @secmark: security marking + * @vlan_tci: vlan tag control information */ struct sk_buff { @@ -326,6 +327,8 @@ struct sk_buff { __u32 mark; + __u16 vlan_tci; + sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7c571560e9d2..50a853f7cd8e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -459,6 +459,8 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->tc_verd = old->tc_verd; #endif #endif + new->vlan_tci = old->vlan_tci; + skb_copy_secmark(new, old); } @@ -2286,6 +2288,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features) skb_copy_queue_mapping(nskb, skb); nskb->priority = skb->priority; nskb->protocol = skb->protocol; + nskb->vlan_tci = skb->vlan_tci; nskb->dst = dst_clone(skb->dst); memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); nskb->pkt_type = skb->pkt_type; -- cgit v1.2.3 From bc1d0411b804ad190cdadabac48a10067f17b9e6 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:49:30 -0700 Subject: vlan: deliver packets received with VLAN acceleration to network taps When VLAN header stripping is used, packets currently bypass packet sockets (and other network taps) completely. For locally existing VLANs, they appear directly on the VLAN device, for unknown VLANs they are silently dropped. Add a new function netif_nit_deliver() to deliver incoming packets to all network interface taps and use it in __vlan_hwaccel_rx() to make VLAN packets visible on the underlying device. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/8021q/vlan_core.c | 4 ++++ net/core/dev.c | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b54ec16dfbda..ba5c4639ea91 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1165,6 +1165,7 @@ extern int netif_rx(struct sk_buff *skb); extern int netif_rx_ni(struct sk_buff *skb); #define HAVE_NETIF_RECEIVE_SKB 1 extern int netif_receive_skb(struct sk_buff *skb); +extern void netif_nit_deliver(struct sk_buff *skb); extern int dev_valid_name(const char *name); extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); extern int dev_ethtool(struct net *net, struct ifreq *); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 68df12d3664b..916061f681b6 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -14,6 +14,9 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return NET_RX_DROP; } + skb->vlan_tci = vlan_tci; + netif_nit_deliver(skb); + skb->dev = vlan_group_get_device(grp, vlan_tci & VLAN_VID_MASK); if (skb->dev == NULL) { dev_kfree_skb_any(skb); @@ -22,6 +25,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return NET_RX_SUCCESS; } skb->dev->last_rx = jiffies; + skb->vlan_tci = 0; stats = &skb->dev->stats; stats->rx_packets++; diff --git a/net/core/dev.c b/net/core/dev.c index a29a359b15d1..feaab4898a5b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2068,6 +2068,33 @@ out: } #endif +/* + * netif_nit_deliver - deliver received packets to network taps + * @skb: buffer + * + * This function is used to deliver incoming packets to network + * taps. It should be used when the normal netif_receive_skb path + * is bypassed, for example because of VLAN acceleration. + */ +void netif_nit_deliver(struct sk_buff *skb) +{ + struct packet_type *ptype; + + if (list_empty(&ptype_all)) + return; + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->mac_len = skb->network_header - skb->mac_header; + + rcu_read_lock(); + list_for_each_entry_rcu(ptype, &ptype_all, list) { + if (!ptype->dev || ptype->dev == skb->dev) + deliver_skb(skb, ptype, skb->dev); + } + rcu_read_unlock(); +} + /** * netif_receive_skb - process receive buffer from network * @skb: buffer to process -- cgit v1.2.3 From bbd6ef87c544d88c30e4b762b1b61ef267a7d279 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:50:15 -0700 Subject: packet: support extensible, 64 bit clean mmaped ring structure The tpacket_hdr is not 64 bit clean due to use of an unsigned long and can't be extended because the following struct sockaddr_ll needs to be at a fixed offset. Add support for a version 2 tpacket protocol that removes these limitations. Userspace can query the header size through a new getsockopt option and change the protocol version through a setsockopt option. The changes needed to switch to the new protocol version are: 1. replace struct tpacket_hdr by struct tpacket2_hdr 2. query header len and save 3. set protocol version to 2 - set up ring as usual 4. for getting the sockaddr_ll, use (void *)hdr + TPACKET_ALIGN(hdrlen) instead of (void *)hdr + TPACKET_ALIGN(sizeof(struct tpacket_hdr)) Steps 2 and 4 can be omitted if the struct sockaddr_ll isn't needed. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 21 ++++++ net/packet/af_packet.c | 179 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 167 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index ad09609227ff..d4d3c82448f5 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -43,6 +43,8 @@ struct sockaddr_ll #define PACKET_COPY_THRESH 7 #define PACKET_AUXDATA 8 #define PACKET_ORIGDEV 9 +#define PACKET_VERSION 10 +#define PACKET_HDRLEN 11 struct tpacket_stats { @@ -79,6 +81,25 @@ struct tpacket_hdr #define TPACKET_ALIGN(x) (((x)+TPACKET_ALIGNMENT-1)&~(TPACKET_ALIGNMENT-1)) #define TPACKET_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + sizeof(struct sockaddr_ll)) +struct tpacket2_hdr +{ + __u32 tp_status; + __u32 tp_len; + __u32 tp_snaplen; + __u16 tp_mac; + __u16 tp_net; + __u32 tp_sec; + __u32 tp_nsec; +}; + +#define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) + +enum tpacket_versions +{ + TPACKET_V1, + TPACKET_V2, +}; + /* Frame structure: diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9f2269166687..4f059775d48f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -186,6 +186,8 @@ struct packet_sock { unsigned int pg_vec_order; unsigned int pg_vec_pages; unsigned int pg_vec_len; + enum tpacket_versions tp_version; + unsigned int tp_hdrlen; #endif }; @@ -201,14 +203,52 @@ struct packet_skb_cb { #ifdef CONFIG_PACKET_MMAP -static inline struct tpacket_hdr *packet_lookup_frame(struct packet_sock *po, unsigned int position) +static void *packet_lookup_frame(struct packet_sock *po, unsigned int position, + int status) { unsigned int pg_vec_pos, frame_offset; + union { + struct tpacket_hdr *h1; + struct tpacket2_hdr *h2; + void *raw; + } h; pg_vec_pos = position / po->frames_per_block; frame_offset = position % po->frames_per_block; - return (struct tpacket_hdr *)(po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size)); + h.raw = po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size); + switch (po->tp_version) { + case TPACKET_V1: + if (status != h.h1->tp_status ? TP_STATUS_USER : + TP_STATUS_KERNEL) + return NULL; + break; + case TPACKET_V2: + if (status != h.h2->tp_status ? TP_STATUS_USER : + TP_STATUS_KERNEL) + return NULL; + break; + } + return h.raw; +} + +static void __packet_set_status(struct packet_sock *po, void *frame, int status) +{ + union { + struct tpacket_hdr *h1; + struct tpacket2_hdr *h2; + void *raw; + } h; + + h.raw = frame; + switch (po->tp_version) { + case TPACKET_V1: + h.h1->tp_status = status; + break; + case TPACKET_V2: + h.h2->tp_status = status; + break; + } } #endif @@ -551,14 +591,19 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe struct sock *sk; struct packet_sock *po; struct sockaddr_ll *sll; - struct tpacket_hdr *h; + union { + struct tpacket_hdr *h1; + struct tpacket2_hdr *h2; + void *raw; + } h; u8 * skb_head = skb->data; int skb_len = skb->len; unsigned int snaplen, res; unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER; - unsigned short macoff, netoff; + unsigned short macoff, netoff, hdrlen; struct sk_buff *copy_skb = NULL; struct timeval tv; + struct timespec ts; if (skb->pkt_type == PACKET_LOOPBACK) goto drop; @@ -590,10 +635,11 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe snaplen = res; if (sk->sk_type == SOCK_DGRAM) { - macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16; + macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16; } else { unsigned maclen = skb_network_offset(skb); - netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen)); + netoff = TPACKET_ALIGN(po->tp_hdrlen + + (maclen < 16 ? 16 : maclen)); macoff = netoff - maclen; } @@ -616,9 +662,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe } spin_lock(&sk->sk_receive_queue.lock); - h = packet_lookup_frame(po, po->head); - - if (h->tp_status) + h.raw = packet_lookup_frame(po, po->head, TP_STATUS_KERNEL); + if (!h.raw) goto ring_is_full; po->head = po->head != po->frame_max ? po->head+1 : 0; po->stats.tp_packets++; @@ -630,20 +675,40 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe status &= ~TP_STATUS_LOSING; spin_unlock(&sk->sk_receive_queue.lock); - skb_copy_bits(skb, 0, (u8*)h + macoff, snaplen); + skb_copy_bits(skb, 0, h.raw + macoff, snaplen); - h->tp_len = skb->len; - h->tp_snaplen = snaplen; - h->tp_mac = macoff; - h->tp_net = netoff; - if (skb->tstamp.tv64) - tv = ktime_to_timeval(skb->tstamp); - else - do_gettimeofday(&tv); - h->tp_sec = tv.tv_sec; - h->tp_usec = tv.tv_usec; + switch (po->tp_version) { + case TPACKET_V1: + h.h1->tp_len = skb->len; + h.h1->tp_snaplen = snaplen; + h.h1->tp_mac = macoff; + h.h1->tp_net = netoff; + if (skb->tstamp.tv64) + tv = ktime_to_timeval(skb->tstamp); + else + do_gettimeofday(&tv); + h.h1->tp_sec = tv.tv_sec; + h.h1->tp_usec = tv.tv_usec; + hdrlen = sizeof(*h.h1); + break; + case TPACKET_V2: + h.h2->tp_len = skb->len; + h.h2->tp_snaplen = snaplen; + h.h2->tp_mac = macoff; + h.h2->tp_net = netoff; + if (skb->tstamp.tv64) + ts = ktime_to_timespec(skb->tstamp); + else + getnstimeofday(&ts); + h.h2->tp_sec = ts.tv_sec; + h.h2->tp_nsec = ts.tv_nsec; + hdrlen = sizeof(*h.h2); + break; + default: + BUG(); + } - sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h))); + sll = h.raw + TPACKET_ALIGN(hdrlen); sll->sll_halen = dev_parse_header(skb, sll->sll_addr); sll->sll_family = AF_PACKET; sll->sll_hatype = dev->type; @@ -654,14 +719,14 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe else sll->sll_ifindex = dev->ifindex; - h->tp_status = status; + __packet_set_status(po, h.raw, status); smp_mb(); { struct page *p_start, *p_end; - u8 *h_end = (u8 *)h + macoff + snaplen - 1; + u8 *h_end = h.raw + macoff + snaplen - 1; - p_start = virt_to_page(h); + p_start = virt_to_page(h.raw); p_end = virt_to_page(h_end); while (p_start <= p_end) { flush_dcache_page(p_start); @@ -1362,6 +1427,25 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv pkt_sk(sk)->copy_thresh = val; return 0; } + case PACKET_VERSION: + { + int val; + + if (optlen != sizeof(val)) + return -EINVAL; + if (po->pg_vec) + return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + switch (val) { + case TPACKET_V1: + case TPACKET_V2: + po->tp_version = val; + return 0; + default: + return -EINVAL; + } + } #endif case PACKET_AUXDATA: { @@ -1437,6 +1521,31 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, data = &val; break; +#ifdef CONFIG_PACKET_MMAP + case PACKET_VERSION: + if (len > sizeof(int)) + len = sizeof(int); + val = po->tp_version; + data = &val; + break; + case PACKET_HDRLEN: + if (len > sizeof(int)) + len = sizeof(int); + if (copy_from_user(&val, optval, len)) + return -EFAULT; + switch (val) { + case TPACKET_V1: + val = sizeof(struct tpacket_hdr); + break; + case TPACKET_V2: + val = sizeof(struct tpacket2_hdr); + break; + default: + return -EINVAL; + } + data = &val; + break; +#endif default: return -ENOPROTOOPT; } @@ -1570,11 +1679,8 @@ static unsigned int packet_poll(struct file * file, struct socket *sock, spin_lock_bh(&sk->sk_receive_queue.lock); if (po->pg_vec) { unsigned last = po->head ? po->head-1 : po->frame_max; - struct tpacket_hdr *h; - - h = packet_lookup_frame(po, last); - if (h->tp_status) + if (packet_lookup_frame(po, last, TP_STATUS_USER)) mask |= POLLIN | POLLRDNORM; } spin_unlock_bh(&sk->sk_receive_queue.lock); @@ -1669,11 +1775,20 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing if (unlikely(po->pg_vec)) return -EBUSY; + switch (po->tp_version) { + case TPACKET_V1: + po->tp_hdrlen = TPACKET_HDRLEN; + break; + case TPACKET_V2: + po->tp_hdrlen = TPACKET2_HDRLEN; + break; + } + if (unlikely((int)req->tp_block_size <= 0)) return -EINVAL; if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) return -EINVAL; - if (unlikely(req->tp_frame_size < TPACKET_HDRLEN)) + if (unlikely(req->tp_frame_size < po->tp_hdrlen)) return -EINVAL; if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1))) return -EINVAL; @@ -1692,13 +1807,11 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing goto out; for (i = 0; i < req->tp_block_nr; i++) { - char *ptr = pg_vec[i]; - struct tpacket_hdr *header; + void *ptr = pg_vec[i]; int k; for (k = 0; k < po->frames_per_block; k++) { - header = (struct tpacket_hdr *) ptr; - header->tp_status = TP_STATUS_KERNEL; + __packet_set_status(po, ptr, TP_STATUS_KERNEL); ptr += req->tp_frame_size; } } -- cgit v1.2.3 From 393e52e33c6c26ec7db290dab803bac1bed962d4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:50:39 -0700 Subject: packet: deliver VLAN TCI to userspace Store the VLAN tag in the auxillary data/tpacket2_hdr so userspace can properly deal with hardware VLAN tagging/stripping. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 2 ++ net/packet/af_packet.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index d4d3c82448f5..a630295b255f 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -59,6 +59,7 @@ struct tpacket_auxdata __u32 tp_snaplen; __u16 tp_mac; __u16 tp_net; + __u16 tp_vlan_tci; }; struct tpacket_hdr @@ -90,6 +91,7 @@ struct tpacket2_hdr __u16 tp_net; __u32 tp_sec; __u32 tp_nsec; + __u16 tp_vlan_tci; }; #define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4f059775d48f..db792e02a37f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -702,6 +702,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe getnstimeofday(&ts); h.h2->tp_sec = ts.tv_sec; h.h2->tp_nsec = ts.tv_nsec; + h.h2->tp_vlan_tci = skb->vlan_tci; hdrlen = sizeof(*h.h2); break; default: @@ -1172,6 +1173,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, aux.tp_snaplen = skb->len; aux.tp_mac = 0; aux.tp_net = skb_network_offset(skb); + aux.tp_vlan_tci = skb->vlan_tci; put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } -- cgit v1.2.3 From 521e575b9a7324a0bca762622139f69582a042bf Mon Sep 17 00:00:00 2001 From: Ron Livne Date: Mon, 14 Jul 2008 23:48:48 -0700 Subject: IB/mlx4: Add support for blocking multicast loopback packets Add support for handling the IB_QP_CREATE_MULTICAST_BLOCK_LOOPBACK flag by using the per-multicast group loopback blocking feature of mlx4 hardware. Signed-off-by: Ron Livne Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/main.c | 7 +++++-- drivers/infiniband/hw/mlx4/mlx4_ib.h | 3 ++- drivers/infiniband/hw/mlx4/qp.c | 21 ++++++++++++++++++--- drivers/net/mlx4/mcg.c | 17 +++++++++++++---- include/linux/mlx4/device.h | 3 ++- 5 files changed, 40 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 4d61e32866c6..bcf50648fa18 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -90,7 +90,8 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->device_cap_flags = IB_DEVICE_CHANGE_PHY_PORT | IB_DEVICE_PORT_ACTIVE_EVENT | IB_DEVICE_SYS_IMAGE_GUID | - IB_DEVICE_RC_RNR_NAK_GEN; + IB_DEVICE_RC_RNR_NAK_GEN | + IB_DEVICE_BLOCK_MULTICAST_LOOPBACK; if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_BAD_PKEY_CNTR) props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR; if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_BAD_QKEY_CNTR) @@ -437,7 +438,9 @@ static int mlx4_ib_dealloc_pd(struct ib_pd *pd) static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { return mlx4_multicast_attach(to_mdev(ibqp->device)->dev, - &to_mqp(ibqp)->mqp, gid->raw); + &to_mqp(ibqp)->mqp, gid->raw, + !!(to_mqp(ibqp)->flags & + MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK)); } static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 5cf994794d25..c4cf5b69eefa 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -101,7 +101,8 @@ struct mlx4_ib_wq { }; enum mlx4_ib_qp_flags { - MLX4_IB_QP_LSO = 1 << 0 + MLX4_IB_QP_LSO = 1 << 0, + MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK = 1 << 1, }; struct mlx4_ib_qp { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 44bbd6c2e315..91590e7fba0c 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -511,6 +511,9 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, } else { qp->sq_no_prefetch = 0; + if (init_attr->create_flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK) + qp->flags |= MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK; + if (init_attr->create_flags & IB_QP_CREATE_IPOIB_UD_LSO) qp->flags |= MLX4_IB_QP_LSO; @@ -684,10 +687,15 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd, struct mlx4_ib_qp *qp; int err; - /* We only support LSO, and only for kernel UD QPs. */ - if (init_attr->create_flags & ~IB_QP_CREATE_IPOIB_UD_LSO) + /* + * We only support LSO and multicast loopback blocking, and + * only for kernel UD QPs. + */ + if (init_attr->create_flags & ~(IB_QP_CREATE_IPOIB_UD_LSO | + IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK)) return ERR_PTR(-EINVAL); - if (init_attr->create_flags & IB_QP_CREATE_IPOIB_UD_LSO && + + if (init_attr->create_flags && (pd->uobject || init_attr->qp_type != IB_QPT_UD)) return ERR_PTR(-EINVAL); @@ -1844,6 +1852,13 @@ done: qp_init_attr->cap = qp_attr->cap; + qp_init_attr->create_flags = 0; + if (qp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK) + qp_init_attr->create_flags |= IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK; + + if (qp->flags & MLX4_IB_QP_LSO) + qp_init_attr->create_flags |= IB_QP_CREATE_IPOIB_UD_LSO; + out: mutex_unlock(&qp->mutex); return err; diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c index 57f7f1f0d4ec..b4b57870ddfd 100644 --- a/drivers/net/mlx4/mcg.c +++ b/drivers/net/mlx4/mcg.c @@ -38,6 +38,9 @@ #include "mlx4.h" +#define MGM_QPN_MASK 0x00FFFFFF +#define MGM_BLCK_LB_BIT 30 + struct mlx4_mgm { __be32 next_gid_index; __be32 members_count; @@ -153,7 +156,8 @@ static int find_mgm(struct mlx4_dev *dev, return err; } -int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]) +int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + int block_mcast_loopback) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_cmd_mailbox *mailbox; @@ -202,13 +206,18 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]) } for (i = 0; i < members_count; ++i) - if (mgm->qp[i] == cpu_to_be32(qp->qpn)) { + if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) { mlx4_dbg(dev, "QP %06x already a member of MGM\n", qp->qpn); err = 0; goto out; } - mgm->qp[members_count++] = cpu_to_be32(qp->qpn); + if (block_mcast_loopback) + mgm->qp[members_count++] = cpu_to_be32((qp->qpn & MGM_QPN_MASK) | + (1 << MGM_BLCK_LB_BIT)); + else + mgm->qp[members_count++] = cpu_to_be32(qp->qpn & MGM_QPN_MASK); + mgm->members_count = cpu_to_be32(members_count); err = mlx4_WRITE_MCG(dev, index, mailbox); @@ -283,7 +292,7 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]) members_count = be32_to_cpu(mgm->members_count); for (loc = -1, i = 0; i < members_count; ++i) - if (mgm->qp[i] == cpu_to_be32(qp->qpn)) + if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) loc = i; if (loc == -1) { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index a744383d16e9..81b3dd5206e0 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -398,7 +398,8 @@ int mlx4_srq_query(struct mlx4_dev *dev, struct mlx4_srq *srq, int *limit_waterm int mlx4_INIT_PORT(struct mlx4_dev *dev, int port); int mlx4_CLOSE_PORT(struct mlx4_dev *dev, int port); -int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]); +int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + int block_mcast_loopback); int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]); int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list, -- cgit v1.2.3 From f1f28aa3510ddb84c966bac65611bb866c77a092 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 00:08:33 -0700 Subject: netdev: Add addr_list_lock to struct net_device. This will be used to protect the per-device unicast and multicast address lists, as well as the callbacks into the drivers which configure such state such as ->set_rx_mode() and ->set_multicast_list(). Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ba5c4639ea91..fd0365219181 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -609,6 +609,7 @@ struct net_device unsigned char addr_len; /* hardware address length */ unsigned short dev_id; /* for shared network cards */ + spinlock_t addr_list_lock; struct dev_addr_list *uc_list; /* Secondary unicast mac addresses */ int uc_count; /* Number of installed ucasts */ int uc_promisc; diff --git a/net/core/dev.c b/net/core/dev.c index feaab4898a5b..d933d1bfa6fa 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3836,6 +3836,7 @@ int register_netdevice(struct net_device *dev) BUG_ON(!dev_net(dev)); net = dev_net(dev); + spin_lock_init(&dev->addr_list_lock); netdev_init_queue_locks(dev); dev->iflink = -1; -- cgit v1.2.3 From e308a5d806c852f56590ffdd3834d0df0cbed8d7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 00:13:44 -0700 Subject: netdev: Add netdev->addr_list_lock protection. Add netif_addr_{lock,unlock}{,_bh}() helpers. Use them to protect operations that operate on or read the network device unicast and multicast address lists. Also use them in cases where the code simply wants to block calls into the driver's ->set_rx_mode() and ->set_multicast_list() methods. Signed-off-by: David S. Miller --- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 2 ++ drivers/media/dvb/dvb-core/dvb_net.c | 2 ++ drivers/net/bonding/bond_main.c | 8 ++++++++ drivers/net/forcedeth.c | 16 ++++++++++++++++ drivers/net/hamradio/6pack.c | 2 ++ drivers/net/hamradio/mkiss.c | 2 ++ drivers/net/ibm_newemac/core.c | 4 ++++ drivers/net/sfc/efx.c | 2 ++ drivers/net/wireless/libertas/main.c | 2 ++ include/linux/netdevice.h | 20 ++++++++++++++++++++ net/core/dev.c | 14 ++++++++++++++ net/core/dev_mcast.c | 12 ++++++++++++ net/mac80211/main.c | 4 ++++ net/mac80211/mlme.c | 4 ++++ 14 files changed, 94 insertions(+) (limited to 'include/linux') diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 3f663fb852c1..261ab7150431 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -775,6 +775,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) local_irq_save(flags); netif_tx_lock(dev); + netif_addr_lock(dev); spin_lock(&priv->lock); /* @@ -851,6 +852,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) } spin_unlock(&priv->lock); + netif_addr_unlock(dev); netif_tx_unlock(dev); local_irq_restore(flags); diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index c2334aef4143..809d18c663bc 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -1134,6 +1134,7 @@ static void wq_set_multicast_list (struct work_struct *work) dvb_net_feed_stop(dev); priv->rx_mode = RX_MODE_UNI; netif_tx_lock_bh(dev); + netif_addr_lock(dev); if (dev->flags & IFF_PROMISC) { dprintk("%s: promiscuous mode\n", dev->name); @@ -1158,6 +1159,7 @@ static void wq_set_multicast_list (struct work_struct *work) } } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); dvb_net_feed_start(dev); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8ae7ff313218..ea71abd6f728 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1568,10 +1568,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); /* upload master's mc_list to new slave */ for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) { dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); } + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); } @@ -1937,7 +1939,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) /* flush master's mc_list from slave */ netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); bond_mc_list_flush(bond_dev, slave_dev); + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); } @@ -2060,7 +2064,9 @@ static int bond_release_all(struct net_device *bond_dev) /* flush master's mc_list from slave */ netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); bond_mc_list_flush(bond_dev, slave_dev); + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); } @@ -4674,7 +4680,9 @@ static void bond_free_all(void) bond_work_cancel_all(bond); netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); bond_mc_list_destroy(bond); + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); /* Release the bonded slaves */ bond_release_all(bond_dev); diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 786d668c612e..4ed89fa9ae46 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -2831,6 +2831,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) */ nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); @@ -2855,6 +2856,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) /* restart rx engine */ nv_start_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); nv_enable_irq(dev); } @@ -2891,6 +2893,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr) if (netif_running(dev)) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock_irq(&np->lock); /* stop rx engine */ @@ -2902,6 +2905,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr) /* restart rx engine */ nv_start_rx(dev); spin_unlock_irq(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } else { nv_copy_mac_to_hw(dev); @@ -3971,6 +3975,7 @@ static void nv_do_nic_poll(unsigned long data) printk(KERN_INFO "forcedeth: MAC in recoverable error state\n"); if (netif_running(dev)) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); @@ -3995,6 +4000,7 @@ static void nv_do_nic_poll(unsigned long data) /* restart rx engine */ nv_start_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } } @@ -4202,6 +4208,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); /* with plain spinlock lockdep complains */ spin_lock_irqsave(&np->lock, flags); /* stop engines */ @@ -4215,6 +4222,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) */ nv_stop_rxtx(dev); spin_unlock_irqrestore(&np->lock, flags); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } @@ -4360,10 +4368,12 @@ static int nv_nway_reset(struct net_device *dev) if (netif_running(dev)) { nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); printk(KERN_INFO "%s: link down.\n", dev->name); } @@ -4471,6 +4481,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri if (netif_running(dev)) { nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); @@ -4519,6 +4530,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri /* restart engines */ nv_start_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); nv_enable_irq(dev); } @@ -4556,10 +4568,12 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam* if (netif_running(dev)) { nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } @@ -4946,6 +4960,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 napi_disable(&np->napi); #endif netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock_irq(&np->lock); nv_disable_hw_interrupts(dev, np->irqmask); if (!(np->msi_flags & NV_MSI_X_ENABLED)) { @@ -4959,6 +4974,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 /* drain rx queue */ nv_drain_rxtx(dev); spin_unlock_irq(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 06ad9f302b5a..ffc937f5d15d 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -300,7 +300,9 @@ static int sp_set_mac_address(struct net_device *dev, void *addr) struct sockaddr_ax25 *sa = addr; netif_tx_lock_bh(dev); + netif_addr_lock(dev); memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 65166035aca0..b8740e6a5cec 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -356,7 +356,9 @@ static int ax_set_mac_address(struct net_device *dev, void *addr) struct sockaddr_ax25 *sa = addr; netif_tx_lock_bh(dev); + netif_addr_lock(dev); memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index babc79ad490b..9ca57d365599 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -295,7 +295,9 @@ static void emac_rx_disable(struct emac_instance *dev) static inline void emac_netif_stop(struct emac_instance *dev) { netif_tx_lock_bh(dev->ndev); + netif_addr_lock(dev->ndev); dev->no_mcast = 1; + netif_addr_unlock(dev->ndev); netif_tx_unlock_bh(dev->ndev); dev->ndev->trans_start = jiffies; /* prevent tx timeout */ mal_poll_disable(dev->mal, &dev->commac); @@ -305,9 +307,11 @@ static inline void emac_netif_stop(struct emac_instance *dev) static inline void emac_netif_start(struct emac_instance *dev) { netif_tx_lock_bh(dev->ndev); + netif_addr_lock(dev->ndev); dev->no_mcast = 0; if (dev->mcast_pending && netif_running(dev->ndev)) __emac_set_multicast_list(dev); + netif_addr_unlock(dev->ndev); netif_tx_unlock_bh(dev->ndev); netif_wake_queue(dev->ndev); diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 74265d8553b8..e1257e556e48 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -697,6 +697,8 @@ static void efx_stop_port(struct efx_nic *efx) /* Serialise against efx_set_multicast_list() */ if (efx_dev_registered(efx)) { netif_tx_lock_bh(efx->net_dev); + netif_addr_lock(efx->net_dev); + netif_addr_unlock(efx->net_dev); netif_tx_unlock_bh(efx->net_dev); } } diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index abd6d9ed8f4b..42e9b2771eab 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -594,6 +594,7 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, return nr_addrs; netif_tx_lock_bh(dev); + netif_addr_lock(dev); for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) { if (mac_in_list(cmd->maclist, nr_addrs, mc_list->dmi_addr)) { lbs_deb_net("mcast address %s:%s skipped\n", dev->name, @@ -608,6 +609,7 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, print_mac(mac, mc_list->dmi_addr)); i++; } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); if (mc_list) return -EOVERFLOW; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fd0365219181..570cf7affa72 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1498,6 +1498,26 @@ static inline void netif_tx_disable(struct net_device *dev) netif_tx_unlock_bh(dev); } +static inline void netif_addr_lock(struct net_device *dev) +{ + spin_lock(&dev->addr_list_lock); +} + +static inline void netif_addr_lock_bh(struct net_device *dev) +{ + spin_lock_bh(&dev->addr_list_lock); +} + +static inline void netif_addr_unlock(struct net_device *dev) +{ + spin_unlock(&dev->addr_list_lock); +} + +static inline void netif_addr_unlock_bh(struct net_device *dev) +{ + spin_unlock_bh(&dev->addr_list_lock); +} + /* These functions live elsewhere (drivers/net/net_init.c, but related) */ extern void ether_setup(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index d933d1bfa6fa..ef1502d71f25 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2982,7 +2982,9 @@ void __dev_set_rx_mode(struct net_device *dev) void dev_set_rx_mode(struct net_device *dev) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } @@ -3062,9 +3064,11 @@ int dev_unicast_delete(struct net_device *dev, void *addr, int alen) ASSERT_RTNL(); netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_delete(&dev->uc_list, &dev->uc_count, addr, alen, 0); if (!err) __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -3088,9 +3092,11 @@ int dev_unicast_add(struct net_device *dev, void *addr, int alen) ASSERT_RTNL(); netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_add(&dev->uc_list, &dev->uc_count, addr, alen, 0); if (!err) __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -3159,10 +3165,12 @@ int dev_unicast_sync(struct net_device *to, struct net_device *from) int err = 0; netif_tx_lock_bh(to); + netif_addr_lock(to); err = __dev_addr_sync(&to->uc_list, &to->uc_count, &from->uc_list, &from->uc_count); if (!err) __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); return err; } @@ -3180,13 +3188,17 @@ EXPORT_SYMBOL(dev_unicast_sync); void dev_unicast_unsync(struct net_device *to, struct net_device *from) { netif_tx_lock_bh(from); + netif_addr_lock(from); netif_tx_lock_bh(to); + netif_addr_lock(to); __dev_addr_unsync(&to->uc_list, &to->uc_count, &from->uc_list, &from->uc_count); __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); + netif_addr_unlock(from); netif_tx_unlock_bh(from); } EXPORT_SYMBOL(dev_unicast_unsync); @@ -3208,6 +3220,7 @@ static void __dev_addr_discard(struct dev_addr_list **list) static void dev_addr_discard(struct net_device *dev) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); __dev_addr_discard(&dev->uc_list); dev->uc_count = 0; @@ -3215,6 +3228,7 @@ static void dev_addr_discard(struct net_device *dev) __dev_addr_discard(&dev->mc_list); dev->mc_count = 0; + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index f8a3455f4493..b6b2a129971a 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -73,6 +73,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) int err; netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_delete(&dev->mc_list, &dev->mc_count, addr, alen, glbl); if (!err) { @@ -83,6 +84,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) __dev_set_rx_mode(dev); } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -96,9 +98,11 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) int err; netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_add(&dev->mc_list, &dev->mc_count, addr, alen, glbl); if (!err) __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -120,10 +124,12 @@ int dev_mc_sync(struct net_device *to, struct net_device *from) int err = 0; netif_tx_lock_bh(to); + netif_addr_lock(to); err = __dev_addr_sync(&to->mc_list, &to->mc_count, &from->mc_list, &from->mc_count); if (!err) __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); return err; @@ -144,13 +150,17 @@ EXPORT_SYMBOL(dev_mc_sync); void dev_mc_unsync(struct net_device *to, struct net_device *from) { netif_tx_lock_bh(from); + netif_addr_lock(from); netif_tx_lock_bh(to); + netif_addr_lock(to); __dev_addr_unsync(&to->mc_list, &to->mc_count, &from->mc_list, &from->mc_count); __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); + netif_addr_unlock(from); netif_tx_unlock_bh(from); } EXPORT_SYMBOL(dev_mc_unsync); @@ -165,6 +175,7 @@ static int dev_mc_seq_show(struct seq_file *seq, void *v) return 0; netif_tx_lock_bh(dev); + netif_addr_lock(dev); for (m = dev->mc_list; m; m = m->next) { int i; @@ -176,6 +187,7 @@ static int dev_mc_seq_show(struct seq_file *seq, void *v) seq_putc(seq, '\n'); } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 36859e794928..095b7d928d64 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -292,7 +292,9 @@ static int ieee80211_open(struct net_device *dev) local->fif_other_bss++; netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); ieee80211_configure_filter(local); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); break; case IEEE80211_IF_TYPE_STA: @@ -491,7 +493,9 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); ieee80211_configure_filter(local); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); break; case IEEE80211_IF_TYPE_MESH_POINT: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8f51375317dd..1232ba25e1e9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3869,6 +3869,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; local->ops->configure_filter(local_to_hw(local), FIF_BCN_PRBRESP_PROMISC, @@ -3876,6 +3877,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) local->mdev->mc_count, local->mdev->mc_list); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); rcu_read_lock(); @@ -4063,12 +4065,14 @@ static int ieee80211_sta_start_scan(struct net_device *dev, local->scan_dev = dev; netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; local->ops->configure_filter(local_to_hw(local), FIF_BCN_PRBRESP_PROMISC, &local->filter_flags, local->mdev->mc_count, local->mdev->mc_list); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); /* TODO: start scan as soon as all nullfunc frames are ACKed */ -- cgit v1.2.3 From 4489428ab5a49a6f443d9aa17f1d891417787d7b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 4 Apr 2008 19:36:59 +0200 Subject: sdhci: support JMicron secondary interface JMicron chips sometimes have two interfaces to work around limitations in Microsoft's sdhci driver. This patch allows us to use either interface. Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci-pci.c | 127 ++++++++++++++++++++++++++++++++++++++++++- include/linux/pci_ids.h | 1 + 2 files changed, 126 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index ef77ed1bd114..5dcb4958e47b 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -39,12 +39,18 @@ #define MAX_SLOTS 8 struct sdhci_pci_chip; +struct sdhci_pci_slot; struct sdhci_pci_fixes { unsigned int quirks; int (*probe)(struct sdhci_pci_chip*); + int (*probe_slot)(struct sdhci_pci_slot*); + void (*remove_slot)(struct sdhci_pci_slot*); + + int (*suspend)(struct sdhci_pci_chip*, + pm_message_t); int (*resume)(struct sdhci_pci_chip*); }; @@ -132,6 +138,38 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) { int ret; + /* + * JMicron chips can have two interfaces to the same hardware + * in order to work around limitations in Microsoft's driver. + * We need to make sure we only bind to one of them. + * + * This code assumes two things: + * + * 1. The PCI code adds subfunctions in order. + * + * 2. The MMC interface has a lower subfunction number + * than the SD interface. + */ + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) { + struct pci_dev *sd_dev; + + sd_dev = NULL; + while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, + PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) { + if ((PCI_SLOT(chip->pdev->devfn) == + PCI_SLOT(sd_dev->devfn)) && + (chip->pdev->bus == sd_dev->bus)) + break; + } + + if (sd_dev) { + pci_dev_put(sd_dev); + dev_info(&chip->pdev->dev, "Refusing to bind to " + "secondary interface.\n"); + return -ENODEV; + } + } + /* * JMicron chips need a bit of a nudge to enable the power * output pins. @@ -145,9 +183,58 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) return 0; } +static void jmicron_enable_mmc(struct sdhci_host *host, int on) +{ + u8 scratch; + + scratch = readb(host->ioaddr + 0xC0); + + if (on) + scratch |= 0x01; + else + scratch &= ~0x01; + + writeb(scratch, host->ioaddr + 0xC0); +} + +static int jmicron_probe_slot(struct sdhci_pci_slot *slot) +{ + /* + * The secondary interface requires a bit set to get the + * interrupts. + */ + if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) + jmicron_enable_mmc(slot->host, 1); + + return 0; +} + +static void jmicron_remove_slot(struct sdhci_pci_slot *slot) +{ + if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) + jmicron_enable_mmc(slot->host, 0); +} + +static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) +{ + int i; + + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { + for (i = 0;i < chip->num_slots;i++) + jmicron_enable_mmc(chip->slots[i]->host, 0); + } + + return 0; +} + static int jmicron_resume(struct sdhci_pci_chip *chip) { - int ret; + int ret, i; + + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { + for (i = 0;i < chip->num_slots;i++) + jmicron_enable_mmc(chip->slots[i]->host, 1); + } ret = jmicron_pmos(chip, 1); if (ret) { @@ -165,6 +252,10 @@ static const struct sdhci_pci_fixes sdhci_jmicron = { .probe = jmicron_probe, + .probe_slot = jmicron_probe_slot, + .remove_slot = jmicron_remove_slot, + + .suspend = jmicron_suspend, .resume = jmicron_resume, }; @@ -225,6 +316,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = (kernel_ulong_t)&sdhci_jmicron, }, + { + .vendor = PCI_VENDOR_ID_JMICRON, + .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_jmicron, + }, + { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, @@ -301,6 +400,15 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) } } + if (chip->fixes && chip->fixes->suspend) { + ret = chip->fixes->suspend(chip, state); + if (ret) { + for (i = chip->num_slots - 1;i >= 0;i--) + sdhci_resume_host(chip->slots[i]->host); + return ret; + } + } + pci_save_state(pdev); pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); pci_disable_device(pdev); @@ -418,12 +526,22 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( goto release; } + if (chip->fixes && chip->fixes->probe_slot) { + ret = chip->fixes->probe_slot(slot); + if (ret) + goto unmap; + } + ret = sdhci_add_host(host); if (ret) - goto unmap; + goto remove; return slot; +remove: + if (chip->fixes && chip->fixes->remove_slot) + chip->fixes->remove_slot(slot); + unmap: iounmap(host->ioaddr); @@ -437,7 +555,12 @@ release: static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) { sdhci_remove_host(slot->host); + + if (slot->chip->fixes && slot->chip->fixes->remove_slot) + slot->chip->fixes->remove_slot(slot); + pci_release_region(slot->chip->pdev, slot->pci_bar); + sdhci_free_host(slot->host); } diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 65953822c9cb..30153473bc37 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2188,6 +2188,7 @@ #define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 #define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 #define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 #define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383 #define PCI_VENDOR_ID_KORENIX 0x1982 -- cgit v1.2.3 From 150a55683b6b0ccb66aae75a10a3a514340c7c03 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 00:57:27 +0300 Subject: include/linux/mmc/mmc.h: remove CVS tags This patch removes a CVS tag that wasn't updated for a long time. Signed-off-by: Adrian Bunk Signed-off-by: Pierre Ossman --- include/linux/mmc/mmc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 4236fbf0b6fb..14b81f3e5232 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -16,7 +16,6 @@ * Based strongly on code by: * * Author: Yong-iL Joh - * Date : $Date: 2002/06/18 12:37:30 $ * * Author: Andrew Christian * 15 May 2002 -- cgit v1.2.3 From 28f52482b41edc88cdf575aa6ed414c6e116ce10 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Jun 2008 18:17:15 +0400 Subject: mmc: add support for card-detection polling Some hosts (and boards that use mmc_spi) do not use interrupts on the CD line, so they can't trigger mmc_detect_change. We want to poll the card and see if there was a change. 1 second poll interval seems resonable. This patch also implements .get_cd() host operation, that could be used by the hosts that are able to report card-detect status without need to talk MMC. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/core/core.c | 12 +++++++++--- include/linux/mmc/host.h | 11 +++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 01ced4c5a61d..ede5d1e2e20d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -638,6 +638,9 @@ void mmc_rescan(struct work_struct *work) */ mmc_bus_put(host); + if (host->ops->get_cd && host->ops->get_cd(host) == 0) + goto out; + mmc_claim_host(host); mmc_power_up(host); @@ -652,7 +655,7 @@ void mmc_rescan(struct work_struct *work) if (!err) { if (mmc_attach_sdio(host, ocr)) mmc_power_off(host); - return; + goto out; } /* @@ -662,7 +665,7 @@ void mmc_rescan(struct work_struct *work) if (!err) { if (mmc_attach_sd(host, ocr)) mmc_power_off(host); - return; + goto out; } /* @@ -672,7 +675,7 @@ void mmc_rescan(struct work_struct *work) if (!err) { if (mmc_attach_mmc(host, ocr)) mmc_power_off(host); - return; + goto out; } mmc_release_host(host); @@ -683,6 +686,9 @@ void mmc_rescan(struct work_struct *work) mmc_bus_put(host); } +out: + if (host->caps & MMC_CAP_NEEDS_POLL) + mmc_schedule_delayed_work(&host->detect, HZ); } void mmc_start_host(struct mmc_host *host) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 7ab962fa1d73..6188e19d2331 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -51,8 +51,18 @@ struct mmc_ios { struct mmc_host_ops { void (*request)(struct mmc_host *host, struct mmc_request *req); + /* + * Avoid calling these three functions too often or in a "fast path", + * since underlaying controller might implement them in an expensive + * and/or slow way. + * + * Also note that these functions might sleep, so don't call them + * in the atomic contexts! + */ void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); int (*get_ro)(struct mmc_host *host); + int (*get_cd)(struct mmc_host *host); + void (*enable_sdio_irq)(struct mmc_host *host, int enable); }; @@ -94,6 +104,7 @@ struct mmc_host { #define MMC_CAP_SD_HIGHSPEED (1 << 3) /* Can do SD high-speed timing */ #define MMC_CAP_SDIO_IRQ (1 << 4) /* Can signal pending SDIO IRQs */ #define MMC_CAP_SPI (1 << 5) /* Talks only SPI protocols */ +#define MMC_CAP_NEEDS_POLL (1 << 6) /* Needs polling for card-detection */ /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ -- cgit v1.2.3 From 619ef4b42128709de4d89d209b2c874f560deecd Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Jun 2008 18:17:21 +0400 Subject: mmc_spi: add support for card-detection polling This patch adds new platform data variable "caps", so platforms could pass theirs capabilities into MMC core (for example, platforms without interrupt on the CD line will most probably want to pass MMC_CAP_NEEDS_POLL). New platform get_cd() callback provided to optimize polling. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/mmc_spi.c | 19 +++++++++++++++++-- include/linux/spi/mmc_spi.h | 9 +++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 35508584ac2a..547eb857b1b3 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1131,11 +1131,20 @@ static int mmc_spi_get_ro(struct mmc_host *mmc) return 0; } +static int mmc_spi_get_cd(struct mmc_host *mmc) +{ + struct mmc_spi_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_cd) + return !!host->pdata->get_cd(mmc->parent); + return -ENOSYS; +} static const struct mmc_host_ops mmc_spi_ops = { .request = mmc_spi_request, .set_ios = mmc_spi_set_ios, .get_ro = mmc_spi_get_ro, + .get_cd = mmc_spi_get_cd, }; @@ -1319,17 +1328,23 @@ static int mmc_spi_probe(struct spi_device *spi) goto fail_glue_init; } + /* pass platform capabilities, if any */ + if (host->pdata) + mmc->caps |= host->pdata->caps; + status = mmc_add_host(mmc); if (status != 0) goto fail_add_host; - dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n", + dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n", mmc->class_dev.bus_id, host->dma_dev ? "" : ", no DMA", (host->pdata && host->pdata->get_ro) ? "" : ", no WP", (host->pdata && host->pdata->setpower) - ? "" : ", no poweroff"); + ? "" : ", no poweroff", + (mmc->caps & MMC_CAP_NEEDS_POLL) + ? ", cd polling" : ""); return 0; fail_add_host: diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h index d5ca78b93a3b..a3626aedaec9 100644 --- a/include/linux/spi/mmc_spi.h +++ b/include/linux/spi/mmc_spi.h @@ -23,6 +23,15 @@ struct mmc_spi_platform_data { /* sense switch on sd cards */ int (*get_ro)(struct device *); + /* + * If board does not use CD interrupts, driver can optimize polling + * using this function. + */ + int (*get_cd)(struct device *); + + /* Capabilities to pass into mmc core (e.g. MMC_CAP_NEEDS_POLL). */ + unsigned long caps; + /* how long to debounce card detect, in msecs */ u16 detect_delay; -- cgit v1.2.3 From 08f80bb5196517a0dfe50dc7c10f234c0ff2f0e8 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Jun 2008 18:17:39 +0400 Subject: mmc: change .get_ro() callback semantics Now get_ro() callback must return 0/1 values for its logical states, and negative errno values in case of error. If particular host instance doesn't support RO/WP switch, it should return -ENOSYS. This patch changes some hosts in two ways: 1. Now functions should be smart to not return negative values in "RO asserted" case (particularly gpio_ calls could return negative values for the outermost GPIOs). Also, board code usually passes get_ro() callbacks that directly return gpioreg & bit result, so at91_mci, imxmmc, pxamci and mmc_spi's get_ro() handlers need take special care when returning platform's values to the mmc core. 2. In case of host instance didn't implement get_ro() callback, it should really return -ENOSYS and let the mmc core decide what to do about it (mmc core thinks the same way as the hosts, so it isn't functional change). Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/core/sd.c | 4 ++-- drivers/mmc/host/at91_mci.c | 18 +++++++----------- drivers/mmc/host/imxmmc.c | 9 ++++++--- drivers/mmc/host/mmc_spi.c | 9 ++++++--- drivers/mmc/host/pxamci.c | 9 ++++++--- drivers/mmc/host/wbsd.c | 2 +- include/linux/mmc/host.h | 12 ++++++++++++ 7 files changed, 40 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7ef3b15c5e3d..b122eb9ea453 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -494,13 +494,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, * Check if read-only switch is active. */ if (!oldcard) { - if (!host->ops->get_ro) { + if (!host->ops->get_ro || host->ops->get_ro(host) < 0) { printk(KERN_WARNING "%s: host does not " "support reading read-only " "switch. assuming write-enable.\n", mmc_hostname(host)); } else { - if (host->ops->get_ro(host)) + if (host->ops->get_ro(host) > 0) mmc_card_set_readonly(card); } } diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 8979ad330a4d..b9d4ed6b29b1 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -793,19 +793,15 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host) static int at91_mci_get_ro(struct mmc_host *mmc) { - int read_only = 0; struct at91mci_host *host = mmc_priv(mmc); - if (host->board->wp_pin) { - read_only = gpio_get_value(host->board->wp_pin); - printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc), - (read_only ? "read-only" : "read-write") ); - } - else { - printk(KERN_WARNING "%s: host does not support reading read-only " - "switch. Assuming write-enable.\n", mmc_hostname(mmc)); - } - return read_only; + if (host->board->wp_pin) + return !!gpio_get_value(host->board->wp_pin); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; } static const struct mmc_host_ops at91_mci_ops = { diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index eed211b2ac70..5e880c0f1349 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -892,9 +892,12 @@ static int imxmci_get_ro(struct mmc_host *mmc) struct imxmci_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->get_ro) - return host->pdata->get_ro(mmc_dev(mmc)); - /* Host doesn't support read only detection so assume writeable */ - return 0; + return !!host->pdata->get_ro(mmc_dev(mmc)); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 547eb857b1b3..4e82f64a96bc 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1126,9 +1126,12 @@ static int mmc_spi_get_ro(struct mmc_host *mmc) struct mmc_spi_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->get_ro) - return host->pdata->get_ro(mmc->parent); - /* board doesn't support read only detection; assume writeable */ - return 0; + return !!host->pdata->get_ro(mmc->parent); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; } static int mmc_spi_get_cd(struct mmc_host *mmc) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index d89475d36988..d39f59738866 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -374,9 +374,12 @@ static int pxamci_get_ro(struct mmc_host *mmc) struct pxamci_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->get_ro) - return host->pdata->get_ro(mmc_dev(mmc)); - /* Host doesn't support read only detection so assume writeable */ - return 0; + return !!host->pdata->get_ro(mmc_dev(mmc)); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; } static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index c303e7f57ab4..67e5a9b80f53 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -939,7 +939,7 @@ static int wbsd_get_ro(struct mmc_host *mmc) spin_unlock_bh(&host->lock); - return csr & WBSD_WRPT; + return !!(csr & WBSD_WRPT); } static const struct mmc_host_ops wbsd_ops = { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6188e19d2331..753b7231b887 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -58,6 +58,18 @@ struct mmc_host_ops { * * Also note that these functions might sleep, so don't call them * in the atomic contexts! + * + * Return values for the get_ro callback should be: + * 0 for a read/write card + * 1 for a read-only card + * -ENOSYS when not supported (equal to NULL callback) + * or a negative errno value when something bad happened + * + * Return values for the get_ro callback should be: + * 0 for a absent card + * 1 for a present card + * -ENOSYS when not supported (equal to NULL callback) + * or a negative errno value when something bad happened */ void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); int (*get_ro)(struct mmc_host *host); -- cgit v1.2.3 From ad3868b2ec96ec14a1549c9e33f5f9a2a3c6ab15 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Jun 2008 12:52:45 +0200 Subject: mmc,sdio: helper function for transfer padding There are a lot of crappy controllers out there that cannot handle all the request sizes that the MMC/SD/SDIO specifications require. In case the card driver can pad the data to overcome the problems, this commit adds a helper that calculates how much that padding should be. A corresponding helper is also added for SDIO, but it can also deal with all the complexities of splitting up a large transfer efficiently. Signed-off-by: Pierre Ossman --- drivers/mmc/core/core.c | 29 +++++++++- drivers/mmc/core/sdio_io.c | 99 ++++++++++++++++++++++++++++++++- drivers/net/wireless/libertas/if_sdio.c | 20 +++---- include/linux/mmc/core.h | 1 + include/linux/mmc/sdio_func.h | 4 +- 5 files changed, 137 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ede5d1e2e20d..3ee5b8c3b5ce 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -3,7 +3,7 @@ * * Copyright (C) 2003-2004 Russell King, All Rights Reserved. * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. - * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -294,6 +294,33 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) } EXPORT_SYMBOL(mmc_set_data_timeout); +/** + * mmc_align_data_size - pads a transfer size to a more optimal value + * @card: the MMC card associated with the data transfer + * @sz: original transfer size + * + * Pads the original data size with a number of extra bytes in + * order to avoid controller bugs and/or performance hits + * (e.g. some controllers revert to PIO for certain sizes). + * + * Returns the improved size, which might be unmodified. + * + * Note that this function is only relevant when issuing a + * single scatter gather entry. + */ +unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) +{ + /* + * FIXME: We don't have a system for the controller to tell + * the core about its problems yet, so for now we just 32-bit + * align the size. + */ + sz = ((sz + 3) / 4) * 4; + + return sz; +} +EXPORT_SYMBOL(mmc_align_data_size); + /** * __mmc_claim_host - exclusively claim a host * @host: mmc host to claim diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 625b92ce9cef..6ee7861fceae 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/core/sdio_io.c * - * Copyright 2007 Pierre Ossman + * Copyright 2007-2008 Pierre Ossman * * 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 @@ -189,6 +189,103 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz) EXPORT_SYMBOL_GPL(sdio_set_block_size); +/** + * sdio_align_size - pads a transfer size to a more optimal value + * @func: SDIO function + * @sz: original transfer size + * + * Pads the original data size with a number of extra bytes in + * order to avoid controller bugs and/or performance hits + * (e.g. some controllers revert to PIO for certain sizes). + * + * If possible, it will also adjust the size so that it can be + * handled in just a single request. + * + * Returns the improved size, which might be unmodified. + */ +unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz) +{ + unsigned int orig_sz; + unsigned int blk_sz, byte_sz; + unsigned chunk_sz; + + orig_sz = sz; + + /* + * Do a first check with the controller, in case it + * wants to increase the size up to a point where it + * might need more than one block. + */ + sz = mmc_align_data_size(func->card, sz); + + /* + * If we can still do this with just a byte transfer, then + * we're done. + */ + if ((sz <= func->cur_blksize) && (sz <= 512)) + return sz; + + if (func->card->cccr.multi_block) { + /* + * Check if the transfer is already block aligned + */ + if ((sz % func->cur_blksize) == 0) + return sz; + + /* + * Realign it so that it can be done with one request, + * and recheck if the controller still likes it. + */ + blk_sz = ((sz + func->cur_blksize - 1) / + func->cur_blksize) * func->cur_blksize; + blk_sz = mmc_align_data_size(func->card, blk_sz); + + /* + * This value is only good if it is still just + * one request. + */ + if ((blk_sz % func->cur_blksize) == 0) + return blk_sz; + + /* + * We failed to do one request, but at least try to + * pad the remainder properly. + */ + byte_sz = mmc_align_data_size(func->card, + sz % func->cur_blksize); + if ((byte_sz <= func->cur_blksize) && (byte_sz <= 512)) { + blk_sz = sz / func->cur_blksize; + return blk_sz * func->cur_blksize + byte_sz; + } + } else { + /* + * We need multiple requests, so first check that the + * controller can handle the chunk size; + */ + chunk_sz = mmc_align_data_size(func->card, + min(func->cur_blksize, 512u)); + if (chunk_sz == min(func->cur_blksize, 512u)) { + /* + * Fix up the size of the remainder (if any) + */ + byte_sz = orig_sz % chunk_sz; + if (byte_sz) { + byte_sz = mmc_align_data_size(func->card, + byte_sz); + } + + return (orig_sz / chunk_sz) * chunk_sz + byte_sz; + } + } + + /* + * The controller is simply incapable of transferring the size + * we want in decent manner, so just return the original size. + */ + return orig_sz; +} +EXPORT_SYMBOL_GPL(sdio_align_size); + /* Split an arbitrarily sized data transfer into several * IO_RW_EXTENDED commands. */ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 3dd537be87d8..b54e2ea8346b 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -1,7 +1,7 @@ /* * linux/drivers/net/wireless/libertas/if_sdio.c * - * Copyright 2007 Pierre Ossman + * Copyright 2007-2008 Pierre Ossman * * Inspired by if_cs.c, Copyright 2007 Holger Schurig * @@ -266,13 +266,10 @@ static int if_sdio_card_to_host(struct if_sdio_card *card) /* * The transfer must be in one transaction or the firmware - * goes suicidal. + * goes suicidal. There's no way to guarantee that for all + * controllers, but we can at least try. */ - chunk = size; - if ((chunk > card->func->cur_blksize) || (chunk > 512)) { - chunk = (chunk + card->func->cur_blksize - 1) / - card->func->cur_blksize * card->func->cur_blksize; - } + chunk = sdio_align_size(card->func, size); ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); if (ret) @@ -696,13 +693,10 @@ static int if_sdio_host_to_card(struct lbs_private *priv, /* * The transfer must be in one transaction or the firmware - * goes suicidal. + * goes suicidal. There's no way to guarantee that for all + * controllers, but we can at least try. */ - size = nb + 4; - if ((size > card->func->cur_blksize) || (size > 512)) { - size = (size + card->func->cur_blksize - 1) / - card->func->cur_blksize * card->func->cur_blksize; - } + size = sdio_align_size(card->func, nb + 4); packet = kzalloc(sizeof(struct if_sdio_packet) + size, GFP_ATOMIC); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index d0c3abed74c2..143cebf0586f 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -135,6 +135,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); +extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); extern void mmc_release_host(struct mmc_host *host); diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index b050f4d7b41f..f57f22b3be88 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -1,7 +1,7 @@ /* * include/linux/mmc/sdio_func.h * - * Copyright 2007 Pierre Ossman + * Copyright 2007-2008 Pierre Ossman * * 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 @@ -120,6 +120,8 @@ extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz); extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler); extern int sdio_release_irq(struct sdio_func *func); +extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); + extern unsigned char sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); extern unsigned short sdio_readw(struct sdio_func *func, -- cgit v1.2.3 From 6d37333163025b46afbcad434ec9a5f2e88e7254 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 30 Jun 2008 10:50:24 +0300 Subject: mmc: fix sdio_io sparse errors This patch fixes sdio_io sparse errors. This fix changes signature of API functions, changing unsigned char -> u8 unsigned short -> u16 unsigned long -> u32 - this was probably a bug in 64 bit platforms Signed-off-by: Tomas Winkler Signed-off-by: Pierre Ossman --- drivers/mmc/core/sdio_io.c | 42 ++++++++++++++++-------------------------- include/linux/mmc/sdio_func.h | 15 ++++++--------- 2 files changed, 22 insertions(+), 35 deletions(-) mode change 100644 => 100755 drivers/mmc/core/sdio_io.c mode change 100644 => 100755 include/linux/mmc/sdio_func.h (limited to 'include/linux') diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c old mode 100644 new mode 100755 index cc42a41ff6ab..3ccf6919877c --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -167,10 +167,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz) return -EINVAL; if (blksz == 0) { - blksz = min(min( - func->max_blksize, - func->card->host->max_blk_size), - 512u); + blksz = min(func->max_blksize, func->card->host->max_blk_size); + blksz = min(blksz, 512u); } ret = mmc_io_rw_direct(func->card, 1, 0, @@ -311,10 +309,9 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, /* Blocks per command is limited by host count, host transfer * size (we only use a single sg entry) and the maximum for * IO_RW_EXTENDED of 511 blocks. */ - max_blocks = min(min( - func->card->host->max_blk_count, - func->card->host->max_seg_size / func->cur_blksize), - 511u); + max_blocks = min(func->card->host->max_blk_count, + func->card->host->max_seg_size / func->cur_blksize); + max_blocks = min(max_blocks, 511u); while (remainder > func->cur_blksize) { unsigned blocks; @@ -364,11 +361,10 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, * function. If there is a problem reading the address, 0xff * is returned and @err_ret will contain the error code. */ -unsigned char sdio_readb(struct sdio_func *func, unsigned int addr, - int *err_ret) +u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; - unsigned char val; + u8 val; BUG_ON(!func); @@ -397,8 +393,7 @@ EXPORT_SYMBOL_GPL(sdio_readb); * function. @err_ret will contain the status of the actual * transfer. */ -void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, - int *err_ret) +void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret) { int ret; @@ -459,7 +454,6 @@ int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, { return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count); } - EXPORT_SYMBOL_GPL(sdio_readsb); /** @@ -489,8 +483,7 @@ EXPORT_SYMBOL_GPL(sdio_writesb); * function. If there is a problem reading the address, 0xffff * is returned and @err_ret will contain the error code. */ -unsigned short sdio_readw(struct sdio_func *func, unsigned int addr, - int *err_ret) +u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; @@ -504,7 +497,7 @@ unsigned short sdio_readw(struct sdio_func *func, unsigned int addr, return 0xFFFF; } - return le16_to_cpu(*(u16*)func->tmpbuf); + return le16_to_cpup((__le16 *)func->tmpbuf); } EXPORT_SYMBOL_GPL(sdio_readw); @@ -519,12 +512,11 @@ EXPORT_SYMBOL_GPL(sdio_readw); * function. @err_ret will contain the status of the actual * transfer. */ -void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr, - int *err_ret) +void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret) { int ret; - *(u16*)func->tmpbuf = cpu_to_le16(b); + *(__le16 *)func->tmpbuf = cpu_to_le16(b); ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2); if (err_ret) @@ -543,8 +535,7 @@ EXPORT_SYMBOL_GPL(sdio_writew); * 0xffffffff is returned and @err_ret will contain the error * code. */ -unsigned long sdio_readl(struct sdio_func *func, unsigned int addr, - int *err_ret) +u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; @@ -558,7 +549,7 @@ unsigned long sdio_readl(struct sdio_func *func, unsigned int addr, return 0xFFFFFFFF; } - return le32_to_cpu(*(u32*)func->tmpbuf); + return le32_to_cpup((__le32 *)func->tmpbuf); } EXPORT_SYMBOL_GPL(sdio_readl); @@ -573,12 +564,11 @@ EXPORT_SYMBOL_GPL(sdio_readl); * function. @err_ret will contain the status of the actual * transfer. */ -void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr, - int *err_ret) +void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret) { int ret; - *(u32*)func->tmpbuf = cpu_to_le32(b); + *(__le32 *)func->tmpbuf = cpu_to_le32(b); ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4); if (err_ret) diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h old mode 100644 new mode 100755 index f57f22b3be88..28fb0a33acf8 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -122,23 +122,20 @@ extern int sdio_release_irq(struct sdio_func *func); extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); -extern unsigned char sdio_readb(struct sdio_func *func, - unsigned int addr, int *err_ret); -extern unsigned short sdio_readw(struct sdio_func *func, - unsigned int addr, int *err_ret); -extern unsigned long sdio_readl(struct sdio_func *func, - unsigned int addr, int *err_ret); +extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); +extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret); +extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret); extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst, unsigned int addr, int count); extern int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, int count); -extern void sdio_writeb(struct sdio_func *func, unsigned char b, +extern void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret); -extern void sdio_writew(struct sdio_func *func, unsigned short b, +extern void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret); -extern void sdio_writel(struct sdio_func *func, unsigned long b, +extern void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret); extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, -- cgit v1.2.3 From 23af60398af2f5033e2f53665538a09f498dbc03 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 6 Jul 2008 01:10:27 +0200 Subject: mmc: remove multiwrite capability Relax requirements on host controllers and only require that they do not report a transfer count than is larger than the actual one (i.e. a lower value is okay). This is how many other parts of the kernel behaves so upper layers should already be prepared to handle that scenario. This gives us a performance boost on MMC cards. Signed-off-by: Pierre Ossman --- drivers/mmc/card/block.c | 47 ++++++++++++++++++-------------------------- drivers/mmc/host/at91_mci.c | 2 +- drivers/mmc/host/atmel-mci.c | 2 +- drivers/mmc/host/mmc_spi.c | 5 +---- drivers/mmc/host/mmci.c | 1 - drivers/mmc/host/omap.c | 2 +- drivers/mmc/host/tifm_sd.c | 2 +- drivers/mmc/host/wbsd.c | 2 +- include/linux/mmc/host.h | 11 +++++------ 9 files changed, 30 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4b0f8220f153..66e5a5487c20 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -237,17 +237,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) if (brq.data.blocks > card->host->max_blk_count) brq.data.blocks = card->host->max_blk_count; - /* - * If the host doesn't support multiple block writes, force - * block writes to single block. SD cards are excepted from - * this rule as they support querying the number of - * successfully written sectors. - */ - if (rq_data_dir(req) != READ && - !(card->host->caps & MMC_CAP_MULTIWRITE) && - !mmc_card_sd(card)) - brq.data.blocks = 1; - if (brq.data.blocks > 1) { /* SPI multiblock writes terminate using a special * token, not a STOP_TRANSMISSION request. @@ -367,30 +356,32 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) * mark the known good sectors as ok. * * If the card is not SD, we can still ok written sectors - * if the controller can do proper error reporting. + * as reported by the controller (which might be less than + * the real number of written sectors, but never more). * * For reads we just fail the entire chunk as that should * be safe in all cases. */ - if (rq_data_dir(req) != READ && mmc_card_sd(card)) { - u32 blocks; - unsigned int bytes; - - blocks = mmc_sd_num_wr_blocks(card); - if (blocks != (u32)-1) { - if (card->csd.write_partial) - bytes = blocks << md->block_bits; - else - bytes = blocks << 9; + if (rq_data_dir(req) != READ) { + if (mmc_card_sd(card)) { + u32 blocks; + unsigned int bytes; + + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + if (card->csd.write_partial) + bytes = blocks << md->block_bits; + else + bytes = blocks << 9; + spin_lock_irq(&md->lock); + ret = __blk_end_request(req, 0, bytes); + spin_unlock_irq(&md->lock); + } + } else { spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, bytes); + ret = __blk_end_request(req, 0, brq.data.bytes_xfered); spin_unlock_irq(&md->lock); } - } else if (rq_data_dir(req) != READ && - (card->host->caps & MMC_CAP_MULTIWRITE)) { - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, brq.data.bytes_xfered); - spin_unlock_irq(&md->lock); } mmc_release_host(card->host); diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 16df235fcc2e..f15e2064305c 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -995,7 +995,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_SDIO_IRQ; + mmc->caps = MMC_CAP_SDIO_IRQ; mmc->max_blk_size = 4095; mmc->max_blk_count = mmc->max_req_size; diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 25d5324ab7e2..cce873c5a149 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -849,7 +849,7 @@ static int __init atmci_probe(struct platform_device *pdev) mmc->f_min = (host->bus_hz + 511) / 512; mmc->f_max = host->bus_hz / 2; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; + mmc->caps |= MMC_CAP_4_BIT_DATA; mmc->max_hw_segs = 64; mmc->max_phys_segs = 64; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 4e82f64a96bc..41cc63360e43 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1252,10 +1252,7 @@ static int mmc_spi_probe(struct spi_device *spi) mmc->ops = &mmc_spi_ops; mmc->max_blk_size = MMC_SPI_BLOCKSIZE; - /* As long as we keep track of the number of successfully - * transmitted blocks, we're good for multiwrite. - */ - mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE; + mmc->caps = MMC_CAP_SPI; /* SPI doesn't need the lowspeed device identification thing for * MMC or SD cards, since it never comes up in open drain mode. diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index da5fecad74d9..696cf3647ceb 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -535,7 +535,6 @@ static int mmci_probe(struct amba_device *dev, void *id) mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); mmc->ocr_avail = plat->ocr_mask; - mmc->caps = MMC_CAP_MULTIWRITE; /* * We can do SGIO diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 549517c35675..dbc26eb6a89e 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1317,7 +1317,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) host->slots[id] = slot; - mmc->caps = MMC_CAP_MULTIWRITE; + mmc->caps = 0; if (host->pdata->conf.wire4) mmc->caps |= MMC_CAP_4_BIT_DATA; diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 1c14a186f000..13844843e8de 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -973,7 +973,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) mmc->ops = &tifm_sd_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; + mmc->caps = MMC_CAP_4_BIT_DATA; mmc->f_min = 20000000 / 60; mmc->f_max = 24000000; diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index f7dcd8ec0d7f..adda37952032 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1219,7 +1219,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) mmc->f_min = 375000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; + mmc->caps = MMC_CAP_4_BIT_DATA; spin_lock_init(&host->lock); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 753b7231b887..10a2080086ca 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -111,12 +111,11 @@ struct mmc_host { unsigned long caps; /* Host capabilities */ #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ -#define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */ -#define MMC_CAP_MMC_HIGHSPEED (1 << 2) /* Can do MMC high-speed timing */ -#define MMC_CAP_SD_HIGHSPEED (1 << 3) /* Can do SD high-speed timing */ -#define MMC_CAP_SDIO_IRQ (1 << 4) /* Can signal pending SDIO IRQs */ -#define MMC_CAP_SPI (1 << 5) /* Talks only SPI protocols */ -#define MMC_CAP_NEEDS_POLL (1 << 6) /* Needs polling for card-detection */ +#define MMC_CAP_MMC_HIGHSPEED (1 << 1) /* Can do MMC high-speed timing */ +#define MMC_CAP_SD_HIGHSPEED (1 << 2) /* Can do SD high-speed timing */ +#define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */ +#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ +#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ -- cgit v1.2.3 From 62a7573ee9f31d4fdb330b3e68ebf6efaba1d57c Mon Sep 17 00:00:00 2001 From: Benzi Zbit Date: Thu, 10 Jul 2008 02:41:43 +0300 Subject: sdio: fix the use of hard coded timeout value. This adds reading and using of enable_timeout from the CIS Signed-off-by: Benzi Zbit Signed-off-by: Tomas Winkler Signed-off-by: Pierre Ossman --- drivers/mmc/core/sdio_cis.c | 6 ++++++ drivers/mmc/core/sdio_io.c | 6 +----- include/linux/mmc/sdio_func.h | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index d5e51b1c7b3f..956bd7677502 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -129,6 +129,12 @@ static int cistpl_funce_func(struct sdio_func *func, /* TPLFE_MAX_BLK_SIZE */ func->max_blksize = buf[12] | (buf[13] << 8); + /* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */ + if (vsn > SDIO_SDIO_REV_1_00) + func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10; + else + func->enable_timeout = jiffies_to_msecs(HZ); + return 0; } diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 3ccf6919877c..0888df64581f 100755 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -76,11 +76,7 @@ int sdio_enable_func(struct sdio_func *func) if (ret) goto err; - /* - * FIXME: This should timeout based on information in the CIS, - * but we don't have card to parse that yet. - */ - timeout = jiffies + HZ; + timeout = jiffies + msecs_to_jiffies(func->enable_timeout); while (1) { ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®); diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 28fb0a33acf8..07bee4a0d457 100755 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -46,6 +46,8 @@ struct sdio_func { unsigned max_blksize; /* maximum block size */ unsigned cur_blksize; /* current block size */ + unsigned enable_timeout; /* max enable timeout in msec */ + unsigned int state; /* function state */ #define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ -- cgit v1.2.3 From 124cafc5eb973e748c4ce3dc1caad29274e64613 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 15 Jul 2008 21:21:44 +0200 Subject: ide: remove ide_init_drive_cmd ide_init_drive_cmd just calls blk_rq_init. This converts the users of ide_init_drive_cmd to use blk_rq_init directly and removes ide_init_drive_cmd. Signed-off-by: FUJITA Tomonori Cc: Borislav Petkov Cc: Jens Axboe Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 2 +- drivers/ide/ide-floppy.c | 2 +- drivers/ide/ide-io.c | 17 ----------------- drivers/scsi/ide-scsi.c | 4 ++-- include/linux/ide.h | 2 -- 5 files changed, 4 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 792a3cf73d6e..7917cd576446 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -193,7 +193,7 @@ void ide_cd_init_rq(ide_drive_t *drive, struct request *rq) { struct cdrom_info *cd = drive->driver_data; - ide_init_drive_cmd(rq); + blk_rq_init(NULL, rq); rq->cmd_type = REQ_TYPE_ATA_PC; rq->rq_disk = cd->disk; } diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index b10e9a813cde..9161cd92a842 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -286,7 +286,7 @@ static void idefloppy_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, { struct ide_floppy_obj *floppy = drive->driver_data; - ide_init_drive_cmd(rq); + blk_rq_init(NULL, rq); rq->buffer = (char *) pc; rq->cmd_type = REQ_TYPE_SPECIAL; rq->cmd_flags |= REQ_PREEMPT; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 29f5cc863f6e..d8b4d9f81ae2 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -1538,23 +1538,6 @@ irqreturn_t ide_intr (int irq, void *dev_id) return IRQ_HANDLED; } -/** - * ide_init_drive_cmd - initialize a drive command request - * @rq: request object - * - * Initialize a request before we fill it in and send it down to - * ide_do_drive_cmd. Commands must be set up by this function. Right - * now it doesn't do a lot, but if that changes abusers will have a - * nasty surprise. - */ - -void ide_init_drive_cmd (struct request *rq) -{ - blk_rq_init(NULL, rq); -} - -EXPORT_SYMBOL(ide_init_drive_cmd); - /** * ide_do_drive_cmd - issue IDE special command * @drive: device to issue command diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 89ecf0132191..da261806d62a 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -228,7 +228,7 @@ static int idescsi_check_condition(ide_drive_t *drive, kfree(pc); return -ENOMEM; } - ide_init_drive_cmd(rq); + blk_rq_init(NULL, rq); rq->special = (char *) pc; pc->rq = rq; pc->buf = buf; @@ -786,7 +786,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, } } - ide_init_drive_cmd (rq); + blk_rq_init(NULL, rq); rq->special = (char *) pc; rq->cmd_type = REQ_TYPE_SPECIAL; spin_unlock_irq(host->host_lock); diff --git a/include/linux/ide.h b/include/linux/ide.h index eddb6daadf4a..3261c6691759 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -857,8 +857,6 @@ int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long); extern ide_startstop_t ide_do_reset (ide_drive_t *); -extern void ide_init_drive_cmd (struct request *rq); - /* * "action" parameter type for ide_do_drive_cmd() below. */ -- cgit v1.2.3 From 681a561b7ec7fdcd8f35b68e44ac6d6c70aecc04 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 15 Jul 2008 21:21:45 +0200 Subject: block: unexport blk_end_sync_rq All the users of blk_end_sync_rq has gone (they are converted to use blk_execute_rq). This unexports blk_end_sync_rq. Signed-off-by: FUJITA Tomonori Cc: Borislav Petkov Signed-off-by: Jens Axboe Signed-off-by: Bartlomiej Zolnierkiewicz --- block/blk-exec.c | 3 +-- include/linux/blkdev.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/block/blk-exec.c b/block/blk-exec.c index 4f52f2792059..9bceff7674f2 100644 --- a/block/blk-exec.c +++ b/block/blk-exec.c @@ -18,7 +18,7 @@ * @rq: request to complete * @error: end io status of the request */ -void blk_end_sync_rq(struct request *rq, int error) +static void blk_end_sync_rq(struct request *rq, int error) { struct completion *waiting = rq->end_io_data; @@ -31,7 +31,6 @@ void blk_end_sync_rq(struct request *rq, int error) */ complete(waiting); } -EXPORT_SYMBOL(blk_end_sync_rq); /** * blk_execute_rq_nowait - insert a request into queue for execution diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d2a1b71e93c3..1171abd7eb17 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -623,7 +623,6 @@ extern void generic_make_request(struct bio *bio); extern void blk_rq_init(struct request_queue *q, struct request *rq); extern void blk_put_request(struct request *); extern void __blk_put_request(struct request_queue *, struct request *); -extern void blk_end_sync_rq(struct request *rq, int error); extern struct request *blk_get_request(struct request_queue *, int, gfp_t); extern void blk_insert_request(struct request_queue *, struct request *, int, void *); extern void blk_requeue_request(struct request_queue *, struct request *); -- cgit v1.2.3 From 30e5ee4d1a651a0c66e86c6612c003034bd20ba2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:46 +0200 Subject: ide: remove obsoleted "idebus=" kernel parameter * Remove obsoleted "idebus=" kernel parameter. * Remove no longer needed ide_system_bus_speed() and system_bus_clock() (together with idebus_parameter and system_bus_speed variables). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide.c | 71 +------------------------------------------- drivers/ide/legacy/ali14xx.c | 2 +- drivers/ide/legacy/ht6560b.c | 2 +- drivers/ide/legacy/qd65xx.c | 4 +-- drivers/ide/pci/aec62xx.c | 2 +- drivers/ide/pci/alim15x3.c | 2 +- drivers/ide/pci/amd74xx.c | 2 +- drivers/ide/pci/cmd640.c | 8 ++--- drivers/ide/pci/cmd64x.c | 4 +-- drivers/ide/pci/cy82c693.c | 2 +- drivers/ide/pci/via82cxxx.c | 2 +- include/linux/ide.h | 2 -- 12 files changed, 15 insertions(+), 88 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 8823df1b8716..f65be738b16a 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -86,9 +86,6 @@ static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR }; -static int idebus_parameter; /* holds the "idebus=" parameter */ -static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */ - DEFINE_MUTEX(ide_cfg_mtx); __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); @@ -189,38 +186,6 @@ static void __init init_ide_data (void) } } -/** - * ide_system_bus_speed - guess bus speed - * - * ide_system_bus_speed() returns what we think is the system VESA/PCI - * bus speed (in MHz). This is used for calculating interface PIO timings. - * The default is 40 for known PCI systems, 50 otherwise. - * The "idebus=xx" parameter can be used to override this value. - * The actual value to be used is computed/displayed the first time - * through. Drivers should only use this as a last resort. - * - * Returns a guessed speed in MHz. - */ - -static int ide_system_bus_speed(void) -{ -#ifdef CONFIG_PCI - static struct pci_device_id pci_default[] = { - { PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) }, - { } - }; -#else -#define pci_default 0 -#endif /* CONFIG_PCI */ - - /* user supplied value */ - if (idebus_parameter) - return idebus_parameter; - - /* safe default value for PCI or VESA and PCI*/ - return pci_dev_present(pci_default) ? 33 : 50; -} - void ide_remove_port_from_hwgroup(ide_hwif_t *hwif) { ide_hwgroup_t *hwgroup = hwif->hwgroup; @@ -540,20 +505,6 @@ static int set_unmaskirq(ide_drive_t *drive, int arg) return 0; } -/** - * system_bus_clock - clock guess - * - * External version of the bus clock guess used by very old IDE drivers - * for things like VLB timings. Should not be used. - */ - -int system_bus_clock (void) -{ - return system_bus_speed; -} - -EXPORT_SYMBOL(system_bus_clock); - static int generic_ide_suspend(struct device *dev, pm_message_t mesg) { ide_drive_t *drive = dev->driver_data; @@ -851,7 +802,7 @@ static int __init ide_setup(char *s) if (strncmp(s,"hd",2) == 0 && s[2] == '=') /* hd= is for hd.c */ return 0; /* driver and not us */ - if (strncmp(s,"ide",3) && strncmp(s,"idebus",6) && strncmp(s,"hd",2)) + if (strncmp(s, "ide", 3) && strncmp(s, "hd", 2)) return 0; printk(KERN_INFO "ide_setup: %s", s); @@ -951,21 +902,6 @@ static int __init ide_setup(char *s) } } - if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e') - goto bad_option; - /* - * Look for bus speed option: "idebus=" - */ - if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') { - if (match_parm(&s[6], NULL, vals, 1) != 1) - goto bad_option; - if (vals[0] >= 20 && vals[0] <= 66) { - idebus_parameter = vals[0]; - } else - printk(" -- BAD BUS SPEED! Expected value from 20 to 66"); - goto obsolete_option; - } - bad_option: printk(" -- BAD OPTION\n"); return 1; @@ -1287,11 +1223,6 @@ static int __init ide_init(void) int ret; printk(KERN_INFO "Uniform Multi-Platform E-IDE driver\n"); - system_bus_speed = ide_system_bus_speed(); - - printk(KERN_INFO "ide: Assuming %dMHz system bus speed " - "for PIO modes%s\n", system_bus_speed, - idebus_parameter ? "" : "; override with idebus=xx"); ret = bus_register(&ide_bus_type); if (ret < 0) { diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c index 90c65cf97448..052125fafcfa 100644 --- a/drivers/ide/legacy/ali14xx.c +++ b/drivers/ide/legacy/ali14xx.c @@ -116,7 +116,7 @@ static void ali14xx_set_pio_mode(ide_drive_t *drive, const u8 pio) int time1, time2; u8 param1, param2, param3, param4; unsigned long flags; - int bus_speed = ide_vlb_clk ? ide_vlb_clk : system_bus_clock(); + int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; /* calculate timing, according to PIO mode */ time1 = ide_pio_cycle_time(drive, pio); diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index 4fe516df9f74..dd6dfb32e853 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -212,7 +212,7 @@ static u8 ht_pio2timings(ide_drive_t *drive, const u8 pio) { int active_time, recovery_time; int active_cycles, recovery_cycles; - int bus_speed = ide_vlb_clk ? ide_vlb_clk : system_bus_clock(); + int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; if (pio) { unsigned int cycle_time; diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 6424af154325..51dba82f8812 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -110,7 +110,7 @@ static void qd65xx_select(ide_drive_t *drive) static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time) { - int clk = ide_vlb_clk ? ide_vlb_clk : system_bus_clock(); + int clk = ide_vlb_clk ? ide_vlb_clk : 50; u8 act_cyc, rec_cyc; if (clk <= 33) { @@ -132,7 +132,7 @@ static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery static u8 qd6580_compute_timing (int active_time, int recovery_time) { - int clk = ide_vlb_clk ? ide_vlb_clk : system_bus_clock(); + int clk = ide_vlb_clk ? ide_vlb_clk : 50; u8 act_cyc, rec_cyc; act_cyc = 17 - IDE_IN(active_time * clk / 1000 + 1, 2, 17); diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index 7f46c224b7c4..ae7a4329a581 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -140,7 +140,7 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio) static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name) { - int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock(); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; if (bus_speed <= 33) pci_set_drvdata(dev, (void *) aec6xxx_33_base); diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index f2129d5e07f2..f2de00adf147 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -72,7 +72,7 @@ static void ali_set_pio_mode(ide_drive_t *drive, const u8 pio) int s_time, a_time, c_time; u8 s_clc, a_clc, r_clc; unsigned long flags; - int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock(); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; int port = hwif->channel ? 0x5c : 0x58; int portFIFO = hwif->channel ? 0x55 : 0x54; u8 cd_dma_fifo = 0; diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index a373101747b6..ad222206a429 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -179,7 +179,7 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, * Determine the system bus clock. */ - amd_clock = (ide_pci_clk ? ide_pci_clk : system_bus_clock()) * 1000; + amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000; switch (amd_clock) { case 33000: amd_clock = 33333; break; diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index b38a1980dcd5..cd1ba14984ab 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -525,12 +525,10 @@ static void cmd640_set_mode(ide_drive_t *drive, unsigned int index, u8 setup_count, active_count, recovery_count, recovery_count2, cycle_count; int bus_speed; - if (cmd640_vlb && ide_vlb_clk) - bus_speed = ide_vlb_clk; - else if (!cmd640_vlb && ide_pci_clk) - bus_speed = ide_pci_clk; + if (cmd640_vlb) + bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; else - bus_speed = system_bus_clock(); + bus_speed = ide_pci_clk ? ide_pci_clk : 33; if (pio_mode > 5) pio_mode = 5; diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index 08674711d089..ca4774aa27ee 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -69,7 +69,7 @@ static u8 quantize_timing(int timing, int quant) static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_time) { struct pci_dev *dev = to_pci_dev(drive->hwif->dev); - int clock_time = 1000 / (ide_pci_clk ? ide_pci_clk : system_bus_clock()); + int clock_time = 1000 / (ide_pci_clk ? ide_pci_clk : 33); u8 cycle_count, active_count, recovery_count, drwtim; static const u8 recovery_values[] = {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}; @@ -128,7 +128,7 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio) ide_pio_timings[pio].active_time); setup_count = quantize_timing(ide_pio_timings[pio].setup_time, - 1000 / (ide_pci_clk ? ide_pci_clk : system_bus_clock())); + 1000 / (ide_pci_clk ? ide_pci_clk : 33)); /* * The primary channel has individual address setup timing registers diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index 77cc22c2ad45..8c534afcb6c8 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -134,7 +134,7 @@ static int calc_clk(int time, int bus_speed) static void compute_clocks(u8 pio, pio_clocks_t *p_pclk) { int clk1, clk2; - int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock(); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; /* we don't check against CY82C693's min and max speed, * so you can play with the idebus=xx parameter diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index e8c2570003ff..3ed9728abd24 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -340,7 +340,7 @@ static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const * Determine system bus clock. */ - via_clock = (ide_pci_clk ? ide_pci_clk : system_bus_clock()) * 1000; + via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000; switch (via_clock) { case 33000: via_clock = 33333; break; diff --git a/include/linux/ide.h b/include/linux/ide.h index 3261c6691759..dad535659249 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -994,8 +994,6 @@ int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long); int ide_cmd_ioctl(ide_drive_t *, unsigned int, unsigned long); int ide_task_ioctl(ide_drive_t *, unsigned int, unsigned long); -extern int system_bus_clock(void); - extern int ide_driveid_update(ide_drive_t *); extern int ide_config_drive_speed(ide_drive_t *, u8); extern u8 eighty_ninty_three (ide_drive_t *); -- cgit v1.2.3 From 931ee0dc5c69e8113233d21942681ab8fecde7f9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:47 +0200 Subject: ide: remove obsoleted "ide=" kernel parameters * Remove obsoleted "ide=" kernel parameters. * Remove no longer needed: - ide_setup() - parse_options() - __setup("", ...) - module_param(options, ...) * Use module_{init,exit}() for MODULE=y case and remove MODULE ifdef. * Make ide_*acpi* and ide_doubler variables static. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-acpi.c | 6 +-- drivers/ide/ide-dma.c | 2 +- drivers/ide/ide.c | 91 +++------------------------------------------- drivers/ide/legacy/gayle.c | 4 +- include/linux/ide.h | 4 -- 5 files changed, 10 insertions(+), 97 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index 9d3601fa5680..6f704628c27d 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -60,15 +60,15 @@ struct ide_acpi_hwif_link { #define DEBPRINT(fmt, args...) do {} while (0) #endif /* DEBUGGING */ -int ide_noacpi; +static int ide_noacpi; module_param_named(noacpi, ide_noacpi, bool, 0); MODULE_PARM_DESC(noacpi, "disable IDE ACPI support"); -int ide_acpigtf; +static int ide_acpigtf; module_param_named(acpigtf, ide_acpigtf, bool, 0); MODULE_PARM_DESC(acpigtf, "enable IDE ACPI _GTF support"); -int ide_acpionboot; +static int ide_acpionboot; module_param_named(acpionboot, ide_acpionboot, bool, 0); MODULE_PARM_DESC(acpionboot, "call IDE ACPI methods on boot"); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 653b1ade13d3..174f4704614c 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -692,7 +692,7 @@ static int ide_tune_dma(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 speed; - if (noautodma || drive->nodma || (drive->id->capability & 1) == 0) + if (drive->nodma || (drive->id->capability & 1) == 0) return 0; /* consult the list of known "bad" drives */ diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index e8c88ff2f6b6..1defba3eefe7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -87,9 +87,9 @@ static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE8_MAJOR, IDE9_MAJOR }; DEFINE_MUTEX(ide_cfg_mtx); - __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); -int noautodma = 0; +__cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); +EXPORT_SYMBOL(ide_lock); ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ @@ -698,59 +698,6 @@ set_val: EXPORT_SYMBOL(generic_ide_ioctl); -/* - * ide_setup() gets called VERY EARLY during initialization, - * to handle kernel "command line" strings beginning with "ide". - * - * Remember to update Documentation/ide/ide.txt if you change something here. - */ -static int __init ide_setup(char *s) -{ - printk(KERN_INFO "ide_setup: %s", s); - -#ifdef CONFIG_BLK_DEV_IDEDOUBLER - if (!strcmp(s, "ide=doubler")) { - extern int ide_doubler; - - printk(" : Enabled support for IDE doublers\n"); - ide_doubler = 1; - goto obsolete_option; - } -#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ - - if (!strcmp(s, "ide=nodma")) { - printk(" : Prevented DMA\n"); - noautodma = 1; - goto obsolete_option; - } - -#ifdef CONFIG_BLK_DEV_IDEACPI - if (!strcmp(s, "ide=noacpi")) { - //printk(" : Disable IDE ACPI support.\n"); - ide_noacpi = 1; - goto obsolete_option; - } - if (!strcmp(s, "ide=acpigtf")) { - //printk(" : Enable IDE ACPI _GTF support.\n"); - ide_acpigtf = 1; - goto obsolete_option; - } - if (!strcmp(s, "ide=acpionboot")) { - //printk(" : Call IDE ACPI methods on boot.\n"); - ide_acpionboot = 1; - goto obsolete_option; - } -#endif /* CONFIG_BLK_DEV_IDEACPI */ - - printk(" -- BAD OPTION\n"); - return 1; -obsolete_option: - printk(" -- OBSOLETE OPTION, WILL BE REMOVED SOON!\n"); - return 1; -} - -EXPORT_SYMBOL(ide_lock); - static int ide_bus_match(struct device *dev, struct device_driver *drv) { return 1; @@ -1087,32 +1034,7 @@ out_port_class: return ret; } -#ifdef MODULE -static char *options = NULL; -module_param(options, charp, 0); -MODULE_LICENSE("GPL"); - -static void __init parse_options (char *line) -{ - char *next = line; - - if (line == NULL || !*line) - return; - while ((line = next) != NULL) { - if ((next = strchr(line,' ')) != NULL) - *next++ = 0; - if (!ide_setup(line)) - printk (KERN_INFO "Unknown option '%s'\n", line); - } -} - -int __init init_module (void) -{ - parse_options(options); - return ide_init(); -} - -void __exit cleanup_module (void) +static void __exit ide_exit(void) { proc_ide_destroy(); @@ -1121,10 +1043,7 @@ void __exit cleanup_module (void) bus_unregister(&ide_bus_type); } -#else /* !MODULE */ - -__setup("", ide_setup); - module_init(ide_init); +module_exit(ide_exit); -#endif /* MODULE */ +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index fed7d812761c..b78941680c32 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -64,9 +64,7 @@ #define GAYLE_HAS_CONTROL_REG (!ide_doubler) #define GAYLE_IDEREG_SIZE (ide_doubler ? 0x1000 : 0x2000) -int ide_doubler = 0; /* support IDE doublers? */ -EXPORT_SYMBOL_GPL(ide_doubler); - +static int ide_doubler; module_param_named(doubler, ide_doubler, bool, 0); MODULE_PARM_DESC(doubler, "enable support for IDE doublers"); #endif /* CONFIG_BLK_DEV_IDEDOUBLER */ diff --git a/include/linux/ide.h b/include/linux/ide.h index dad535659249..0fa1812d0438 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -813,10 +813,6 @@ int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsig #ifndef _IDE_C extern ide_hwif_t ide_hwifs[]; /* master data repository */ #endif -extern int ide_noacpi; -extern int ide_acpigtf; -extern int ide_acpionboot; -extern int noautodma; extern int ide_vlb_clk; extern int ide_pci_clk; -- cgit v1.2.3 From 9a410e79b552bacb4481f85618aa7333b7776ed7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:48 +0200 Subject: ide: remove IDE_TFLAG_NO_SELECT_MASK taskfile flag Always call SELECT_MASK(..., 0) in ide_tf_load() (needs to be done to match ide_set_irq(..., 1)) and then remove IDE_TFLAG_NO_SELECT_MASK taskfile flag. This change should only affect hpt366 and icside host drivers since ->maskproc(..., 0) for sgiioc4 is equivalent to ide_set_irq(..., 1). Cc: Sergei Shtylyov Cc: Russell King Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 4 ++-- drivers/ide/ide-disk.c | 3 +-- drivers/ide/ide-floppy.c | 3 +-- drivers/ide/ide-iops.c | 4 +--- drivers/ide/ide-tape.c | 3 +-- drivers/scsi/ide-scsi.c | 2 +- include/linux/ide.h | 1 - 7 files changed, 7 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index ac542ffffa49..0fbc2d8d0d53 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -530,8 +530,8 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, info->dma = !hwif->dma_ops->dma_setup(drive); /* set up the controller registers */ - ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL | - IDE_TFLAG_NO_SELECT_MASK, xferlen, info->dma); + ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL, + xferlen, info->dma); if (info->cd_flags & IDE_CD_FLAG_DRQ_INTERRUPT) { /* waiting for CDB interrupt, not DMA yet. */ diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index c5f22ef8ed24..5f49a4ae9dd8 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -198,8 +198,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, } memset(&task, 0, sizeof(task)); - task.tf_flags = IDE_TFLAG_NO_SELECT_MASK; /* FIXME? */ - task.tf_flags |= (IDE_TFLAG_TF | IDE_TFLAG_DEVICE); + task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; if (drive->select.b.lba) { if (lba48) { diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 9161cd92a842..1852008d9ee4 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -667,8 +667,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, if ((pc->flags & PC_FLAG_DMA_RECOMMENDED) && drive->using_dma) dma = !hwif->dma_ops->dma_setup(drive); - ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK | - IDE_TFLAG_OUT_DEVICE, bcount, dma); + ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma); if (dma) { /* Begin DMA, if necessary */ diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 2de4c8f581eb..9f9916fe6c2f 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -121,9 +121,7 @@ static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) HIHI = 0xFF; ide_set_irq(drive, 1); - - if ((task->tf_flags & IDE_TFLAG_NO_SELECT_MASK) == 0) - SELECT_MASK(drive, 0); + SELECT_MASK(drive, 0); if (task->tf_flags & IDE_TFLAG_OUT_DATA) { u16 data = (tf->hob_data << 8) | tf->data; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index a5f0b774527b..cc7991c7c252 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1046,8 +1046,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, if ((pc->flags & PC_FLAG_DMA_RECOMMENDED) && drive->using_dma) dma_ok = !hwif->dma_ops->dma_setup(drive); - ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK | - IDE_TFLAG_OUT_DEVICE, bcount, dma_ok); + ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma_ok); if (dma_ok) /* Will begin DMA later */ diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index da261806d62a..3222aa589dbf 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -564,7 +564,7 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive, hwif->sg_mapped = 0; } - ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK, bcount, dma); + ide_pktcmd_tf_load(drive, 0, bcount, dma); if (dma) pc->flags |= PC_FLAG_DMA_OK; diff --git a/include/linux/ide.h b/include/linux/ide.h index 0fa1812d0438..d4a910cdb907 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -869,7 +869,6 @@ extern void ide_end_drive_cmd(ide_drive_t *, u8, u8); enum { IDE_TFLAG_LBA48 = (1 << 0), - IDE_TFLAG_NO_SELECT_MASK = (1 << 1), IDE_TFLAG_FLAGGED = (1 << 2), IDE_TFLAG_OUT_DATA = (1 << 3), IDE_TFLAG_OUT_HOB_FEATURE = (1 << 4), -- cgit v1.2.3 From ed4af48fd660176680da905817f6e40d51436e4c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:48 +0200 Subject: ide: move IRQ unmasking out from ->tf_load method Move IRQ unmasking out from ->tf_load method to its users. There should be no functional changes caused by this patch (SELECT_MASK() is NOP except for hpt366, icside and sgiioc4). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/h8300/ide-h8300.c | 2 -- drivers/ide/ide-io.c | 2 ++ drivers/ide/ide-iops.c | 5 +---- drivers/ide/ide-taskfile.c | 2 ++ drivers/ide/pci/scc_pata.c | 2 -- include/linux/ide.h | 1 + 6 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index ecf53bb0d2aa..d5afc28eaae7 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -52,8 +52,6 @@ static void h8300_tf_load(ide_drive_t *drive, ide_task_t *task) if (task->tf_flags & IDE_TFLAG_FLAGGED) HIHI = 0xFF; - ide_set_irq(drive, 1); - if (task->tf_flags & IDE_TFLAG_OUT_DATA) mm_outw((tf->hob_data << 8) | tf->data, io_ports->data_addr); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index c22a337ced4e..2083cc08b2ce 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -1579,6 +1579,8 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); + ide_set_irq(drive, 1); + SELECT_MASK(drive, 0); drive->hwif->tf_load(drive, &task); } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 9f9916fe6c2f..491980aab866 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -95,7 +95,7 @@ void SELECT_DRIVE (ide_drive_t *drive) hwif->OUTB(drive->select.all, hwif->io_ports.device_addr); } -static void SELECT_MASK(ide_drive_t *drive, int mask) +void SELECT_MASK(ide_drive_t *drive, int mask) { const struct ide_port_ops *port_ops = drive->hwif->port_ops; @@ -120,9 +120,6 @@ static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) if (task->tf_flags & IDE_TFLAG_FLAGGED) HIHI = 0xFF; - ide_set_irq(drive, 1); - SELECT_MASK(drive, 0); - if (task->tf_flags & IDE_TFLAG_OUT_DATA) { u16 data = (tf->hob_data << 8) | tf->data; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index b6a1c4b51129..6a17ab54f801 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -109,6 +109,8 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); + ide_set_irq(drive, 1); + SELECT_MASK(drive, 0); hwif->tf_load(drive, task); } diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 910fb00deb71..37e8cfcabb4b 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -662,8 +662,6 @@ static void scc_tf_load(ide_drive_t *drive, ide_task_t *task) if (task->tf_flags & IDE_TFLAG_FLAGGED) HIHI = 0xFF; - ide_set_irq(drive, 1); - if (task->tf_flags & IDE_TFLAG_OUT_DATA) out_be32((void *)io_ports->data_addr, (tf->hob_data << 8) | tf->data); diff --git a/include/linux/ide.h b/include/linux/ide.h index d4a910cdb907..56d0bc2dffee 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -973,6 +973,7 @@ typedef struct ide_task_s { void ide_tf_dump(const char *, struct ide_taskfile *); extern void SELECT_DRIVE(ide_drive_t *); +void SELECT_MASK(ide_drive_t *, int); extern int drive_is_ready(ide_drive_t *); -- cgit v1.2.3 From 135721446144af005109c25eeacca4fdddcd9a66 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:49 +0200 Subject: ide: remove ->mmio flag from ide_hwif_t Since scc_pata host driver no longer uses IDE PCI layer / ide_dma_setup() and all other ->mmio users set also IDE_HFLAG_MMIO host flag we can safely remove ->mmio flag. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/palm_bk3710.c | 1 - drivers/ide/ide-dma.c | 2 +- drivers/ide/pci/scc_pata.c | 1 - drivers/ide/pci/siimage.c | 25 +++++++++++++------------ drivers/ide/setup-pci.c | 4 ++-- include/linux/ide.h | 1 - 6 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 74a05dc6d1e6..3839f5722985 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -405,7 +405,6 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) ide_init_port_data(hwif, i); ide_init_port_hw(hwif, &hw); - hwif->mmio = 1; default_hwif_mmiops(hwif); idx[0] = i; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 174f4704614c..7ee44f86bc54 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -463,7 +463,7 @@ int ide_dma_setup(ide_drive_t *drive) } /* PRD table */ - if (hwif->mmio) + if (hwif->host_flags & IDE_HFLAG_MMIO) writel(hwif->dmatable_dma, (void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS)); else diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 37e8cfcabb4b..133053c7a480 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -793,7 +793,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->dma_base = dma_base; hwif->config_data = ports->ctl; - hwif->mmio = 1; } /** diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 0006b9e58567..b75e9bb390a7 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -94,7 +94,7 @@ static unsigned long siimage_selreg(ide_hwif_t *hwif, int r) unsigned long base = (unsigned long)hwif->hwif_data; base += 0xA0 + r; - if (hwif->mmio) + if (hwif->host_flags & IDE_HFLAG_MMIO) base += hwif->channel << 6; else base += hwif->channel << 4; @@ -117,7 +117,7 @@ static inline unsigned long siimage_seldev(ide_drive_t *drive, int r) unsigned long base = (unsigned long)hwif->hwif_data; base += 0xA0 + r; - if (hwif->mmio) + if (hwif->host_flags & IDE_HFLAG_MMIO) base += hwif->channel << 6; else base += hwif->channel << 4; @@ -190,7 +190,9 @@ static u8 sil_pata_udma_filter(ide_drive_t *drive) unsigned long base = (unsigned long)hwif->hwif_data; u8 scsc, mask = 0; - scsc = sil_ioread8(dev, base + (hwif->mmio ? 0x4A : 0x8A)); + base += (hwif->host_flags & IDE_HFLAG_MMIO) ? 0x4A : 0x8A; + + scsc = sil_ioread8(dev, base); switch (scsc & 0x30) { case 0x10: /* 133 */ @@ -238,8 +240,9 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio) unsigned long tfaddr = siimage_selreg(hwif, 0x02); unsigned long base = (unsigned long)hwif->hwif_data; u8 tf_pio = pio; - u8 addr_mask = hwif->channel ? (hwif->mmio ? 0xF4 : 0x84) - : (hwif->mmio ? 0xB4 : 0x80); + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84) + : (mmio ? 0xB4 : 0x80); u8 mode = 0; u8 unit = drive->select.b.unit; @@ -290,13 +293,13 @@ static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed) u16 ultra = 0, multi = 0; u8 mode = 0, unit = drive->select.b.unit; unsigned long base = (unsigned long)hwif->hwif_data; - u8 scsc = 0, addr_mask = hwif->channel ? - (hwif->mmio ? 0xF4 : 0x84) : - (hwif->mmio ? 0xB4 : 0x80); + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 scsc = 0, addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84) + : (mmio ? 0xB4 : 0x80); unsigned long ma = siimage_seldev(drive, 0x08); unsigned long ua = siimage_seldev(drive, 0x0C); - scsc = sil_ioread8 (dev, base + (hwif->mmio ? 0x4A : 0x8A)); + scsc = sil_ioread8 (dev, base + (mmio ? 0x4A : 0x8A)); mode = sil_ioread8 (dev, base + addr_mask); multi = sil_ioread16(dev, ma); ultra = sil_ioread16(dev, ua); @@ -391,7 +394,7 @@ static int siimage_mmio_dma_test_irq(ide_drive_t *drive) static int siimage_dma_test_irq(ide_drive_t *drive) { - if (drive->hwif->mmio) + if (drive->hwif->host_flags & IDE_HFLAG_MMIO) return siimage_mmio_dma_test_irq(drive); else return siimage_io_dma_test_irq(drive); @@ -640,8 +643,6 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) hwif->irq = dev->irq; hwif->dma_base = (unsigned long)addr + (ch ? 0x08 : 0x00); - - hwif->mmio = 1; } static int is_dev_seagate_sata(ide_drive_t *drive) diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 5171601fb255..abcfb1739d4d 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -87,7 +87,7 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) unsigned long dma_base = 0; u8 dma_stat = 0; - if (hwif->mmio) + if (hwif->host_flags & IDE_HFLAG_MMIO) return hwif->dma_base; if (hwif->mate && hwif->mate->dma_base) { @@ -374,7 +374,7 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (base == 0 || ide_pci_set_master(dev, d->name) < 0) return -1; - if (hwif->mmio) + if (hwif->host_flags & IDE_HFLAG_MMIO) printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); else printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", diff --git a/include/linux/ide.h b/include/linux/ide.h index 56d0bc2dffee..b01b102be4de 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -532,7 +532,6 @@ typedef struct hwif_s { unsigned serialized : 1; /* serialized all channel operation */ unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */ unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */ - unsigned mmio : 1; /* host uses MMIO */ struct device gendev; struct device *portdev; -- cgit v1.2.3 From f8c4bd0ab2b8783c0f080957781e9f70bee48eaa Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:49 +0200 Subject: ide: pass 'hwif *' instead of 'drive *' to ->OUTBSYNC method There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 2 +- drivers/ide/ide-iops.c | 18 +++++++++--------- drivers/ide/ide-probe.c | 6 +++--- drivers/ide/ide-taskfile.c | 2 +- drivers/ide/pci/scc_pata.c | 5 +---- drivers/ide/ppc/pmac.c | 6 +++--- drivers/scsi/ide-scsi.c | 2 +- include/linux/ide.h | 2 +- 8 files changed, 20 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 2083cc08b2ce..c28fcdf0ee9e 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -437,7 +437,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(drive, WIN_IDLEIMMEDIATE, + hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, hwif->io_ports.command_addr); if (rq->errors >= ERROR_MAX) { diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 491980aab866..4c32cf0b623c 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -42,7 +42,7 @@ static void ide_outb (u8 val, unsigned long port) outb(val, port); } -static void ide_outbsync (ide_drive_t *drive, u8 addr, unsigned long port) +static void ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) { outb(addr, port); } @@ -68,7 +68,7 @@ static void ide_mm_outb (u8 value, unsigned long port) writeb(value, (void __iomem *) port); } -static void ide_mm_outbsync (ide_drive_t *drive, u8 value, unsigned long port) +static void ide_mm_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) { writeb(value, (void __iomem *) port); } @@ -686,7 +686,7 @@ int ide_driveid_update(ide_drive_t *drive) SELECT_MASK(drive, 1); ide_set_irq(drive, 0); msleep(50); - hwif->OUTBSYNC(drive, WIN_IDENTIFY, hwif->io_ports.command_addr); + hwif->OUTBSYNC(hwif, WIN_IDENTIFY, hwif->io_ports.command_addr); timeout = jiffies + WAIT_WORSTCASE; do { if (time_after(jiffies, timeout)) { @@ -773,7 +773,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) ide_set_irq(drive, 0); hwif->OUTB(speed, io_ports->nsect_addr); hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr); - hwif->OUTBSYNC(drive, WIN_SETFEATURES, io_ports->command_addr); + hwif->OUTBSYNC(hwif, WIN_SETFEATURES, io_ports->command_addr); if (drive->quirk_list == 2) ide_set_irq(drive, 1); @@ -881,7 +881,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, spin_lock_irqsave(&ide_lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - hwif->OUTBSYNC(drive, cmd, hwif->io_ports.command_addr); + hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); /* * Drive takes 400nS to respond, we must avoid the IRQ being * serviced before that. @@ -899,7 +899,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - hwif->OUTBSYNC(drive, WIN_PACKETCMD, hwif->io_ports.command_addr); + hwif->OUTBSYNC(hwif, WIN_PACKETCMD, hwif->io_ports.command_addr); ndelay(400); spin_unlock_irqrestore(&ide_lock, flags); } @@ -1094,7 +1094,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->OUTBSYNC(drive, WIN_SRST, io_ports->command_addr); + hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; @@ -1125,14 +1125,14 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * recover from reset very quickly, saving us the first 50ms wait time. */ /* set SRST and nIEN */ - hwif->OUTBSYNC(drive, drive->ctl|6, io_ports->ctl_addr); + hwif->OUTBSYNC(hwif, drive->ctl | 6, io_ports->ctl_addr); /* more than enough time */ udelay(10); if (drive->quirk_list == 2) ctl = drive->ctl; /* clear SRST and nIEN */ else ctl = drive->ctl | 2; /* clear SRST, leave nIEN */ - hwif->OUTBSYNC(drive, ctl, io_ports->ctl_addr); + hwif->OUTBSYNC(hwif, ctl, io_ports->ctl_addr); /* more than enough time */ udelay(10); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 12513c45d700..b010633eb5ba 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -293,7 +293,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) hwif->OUTB(0, io_ports->feature_addr); /* ask drive for ID */ - hwif->OUTBSYNC(drive, cmd, io_ports->command_addr); + hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; @@ -480,7 +480,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(drive, WIN_SRST, io_ports->command_addr); + hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } @@ -516,7 +516,7 @@ static void enable_nest (ide_drive_t *drive) printk("%s: enabling %s -- ", hwif->name, drive->id->model); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(drive, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr); + hwif->OUTBSYNC(hwif, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr); if (ide_busy_sleep(hwif)) { printk(KERN_CONT "failed (timeout)\n"); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 6a17ab54f801..cf55a48a7dd2 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -117,7 +117,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: - hwif->OUTBSYNC(drive, tf->command, hwif->io_ports.command_addr); + hwif->OUTBSYNC(hwif, tf->command, hwif->io_ports.command_addr); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 133053c7a480..32eb0877fce2 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -148,11 +148,8 @@ static void scc_ide_outb(u8 addr, unsigned long port) out_be32((void*)port, addr); } -static void -scc_ide_outbsync(ide_drive_t * drive, u8 addr, unsigned long port) +static void scc_ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) { - ide_hwif_t *hwif = HWIF(drive); - out_be32((void*)port, addr); eieio(); in_be32((void*)(hwif->dma_base + 0x01c)); diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ba2d58727964..dcb2c466bb97 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -480,13 +480,13 @@ pmac_ide_do_update_timings(ide_drive_t *drive) pmac_ide_selectproc(drive); } -static void -pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port) +static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) { u32 tmp; writeb(value, (void __iomem *) port); - tmp = readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); + tmp = readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); } /* diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 3222aa589dbf..d7fd5e550a25 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -257,7 +257,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(drive, WIN_IDLEIMMEDIATE, + hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, hwif->io_ports.command_addr); rq->errors++; diff --git a/include/linux/ide.h b/include/linux/ide.h index b01b102be4de..1c3431469649 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -493,7 +493,7 @@ typedef struct hwif_s { void (*ide_dma_clear_irq)(ide_drive_t *drive); void (*OUTB)(u8 addr, unsigned long port); - void (*OUTBSYNC)(ide_drive_t *drive, u8 addr, unsigned long port); + void (*OUTBSYNC)(struct hwif_s *hwif, u8 addr, unsigned long port); u8 (*INB)(unsigned long port); -- cgit v1.2.3 From 0fd04dcc2ebb6ec9088c24b368b0ce1f42a98ef5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:50 +0200 Subject: ide: use ->OUTBSYNC in ide_set_irq() Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 1c3431469649..4d1c9714f1d9 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1340,7 +1340,8 @@ static inline void ide_set_irq(ide_drive_t *drive, int on) { ide_hwif_t *hwif = drive->hwif; - hwif->OUTB(drive->ctl | (on ? 0 : 2), hwif->io_ports.ctl_addr); + hwif->OUTBSYNC(hwif, drive->ctl | (on ? 0 : 2), + hwif->io_ports.ctl_addr); } static inline u8 ide_read_status(ide_drive_t *drive) -- cgit v1.2.3 From ff07488346702f554aaeb6aae982540aa0302373 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:50 +0200 Subject: ide: remove drive->ctl Remove drive->ctl (it is always equal to 0x08 after init time). While at it: * Use ATA_DEVCTL_OBS define. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/h8300/ide-h8300.c | 4 ++-- drivers/ide/ide-iops.c | 10 +++++----- drivers/ide/ide-probe.c | 2 +- drivers/ide/ide.c | 1 - drivers/ide/pci/hpt366.c | 3 +-- drivers/ide/pci/ns87415.c | 4 ++-- drivers/ide/pci/scc_pata.c | 4 ++-- drivers/ide/pci/sgiioc4.c | 2 +- include/linux/ide.h | 3 +-- 9 files changed, 15 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index d5afc28eaae7..ae37ee58bae2 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -96,7 +96,7 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task) } /* be sure we're looking at the low order bits */ - outb(drive->ctl & ~0x80, io_ports->ctl_addr); + outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); @@ -110,7 +110,7 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task) tf->device = inb(io_ports->device_addr); if (task->tf_flags & IDE_TFLAG_LBA48) { - outb(drive->ctl | 0x80, io_ports->ctl_addr); + outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = inb(io_ports->feature_addr); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 4c32cf0b623c..80ad4f234f3f 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -186,7 +186,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) } /* be sure we're looking at the low order bits */ - tf_outb(drive->ctl & ~0x80, io_ports->ctl_addr); + tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tf_inb(io_ports->nsect_addr); @@ -200,7 +200,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) tf->device = tf_inb(io_ports->device_addr); if (task->tf_flags & IDE_TFLAG_LBA48) { - tf_outb(drive->ctl | 0x80, io_ports->ctl_addr); + tf_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = tf_inb(io_ports->feature_addr); @@ -1125,13 +1125,13 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * recover from reset very quickly, saving us the first 50ms wait time. */ /* set SRST and nIEN */ - hwif->OUTBSYNC(hwif, drive->ctl | 6, io_ports->ctl_addr); + hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | 6, io_ports->ctl_addr); /* more than enough time */ udelay(10); if (drive->quirk_list == 2) - ctl = drive->ctl; /* clear SRST and nIEN */ + ctl = ATA_DEVCTL_OBS; /* clear SRST and nIEN */ else - ctl = drive->ctl | 2; /* clear SRST, leave nIEN */ + ctl = ATA_DEVCTL_OBS | 2; /* clear SRST, leave nIEN */ hwif->OUTBSYNC(hwif, ctl, io_ports->ctl_addr); /* more than enough time */ udelay(10); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 809362b13c99..d21e51a02c3e 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1065,7 +1065,7 @@ static int init_irq (ide_hwif_t *hwif) if (io_ports->ctl_addr) /* clear nIEN */ - hwif->OUTBSYNC(hwif, 0x08, io_ports->ctl_addr); + hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS, io_ports->ctl_addr); if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) goto out_unlink; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 1defba3eefe7..2b8453510e09 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -136,7 +136,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif) drive->media = ide_disk; drive->select.all = (unit<<4)|0xa0; drive->hwif = hwif; - drive->ctl = 0x08; drive->ready_stat = READY_STAT; drive->bad_wstat = BAD_W_STAT; drive->special.b.recalibrate = 1; diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index c929dadaaaff..397c6cbe953c 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -759,8 +759,7 @@ static void hpt3xx_maskproc(ide_drive_t *drive, int mask) enable_irq (hwif->irq); } } else - outb(mask ? (drive->ctl | 2) : (drive->ctl & ~2), - hwif->io_ports.ctl_addr); + outb(ATA_DEVCTL_OBS | (mask ? 2 : 0), hwif->io_ports.ctl_addr); } /* diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index a7a41bb82778..45ba71a7182f 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -76,7 +76,7 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) } /* be sure we're looking at the low order bits */ - outb(drive->ctl & ~0x80, io_ports->ctl_addr); + outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); @@ -90,7 +90,7 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) tf->device = superio_ide_inb(io_ports->device_addr); if (task->tf_flags & IDE_TFLAG_LBA48) { - outb(drive->ctl | 0x80, io_ports->ctl_addr); + outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = inb(io_ports->feature_addr); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 32eb0877fce2..1584ebb6a185 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -703,7 +703,7 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task) } /* be sure we're looking at the low order bits */ - scc_ide_outb(drive->ctl & ~0x80, io_ports->ctl_addr); + scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = scc_ide_inb(io_ports->nsect_addr); @@ -717,7 +717,7 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task) tf->device = scc_ide_inb(io_ports->device_addr); if (task->tf_flags & IDE_TFLAG_LBA48) { - scc_ide_outb(drive->ctl | 0x80, io_ports->ctl_addr); + scc_ide_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = scc_ide_inb(io_ports->feature_addr); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index c1b667c53f2b..24513e3dcd6b 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -111,7 +111,7 @@ sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port, static void sgiioc4_maskproc(ide_drive_t * drive, int mask) { - writeb(mask ? (drive->ctl | 2) : (drive->ctl & ~2), + writeb(ATA_DEVCTL_OBS | (mask ? 2 : 0), (void __iomem *)drive->hwif->io_ports.ctl_addr); } diff --git a/include/linux/ide.h b/include/linux/ide.h index 4d1c9714f1d9..d8c86f0362c4 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -364,7 +364,6 @@ typedef struct ide_drive_s { u8 wcache; /* status of write cache */ u8 acoustic; /* acoustic management */ u8 media; /* disk, cdrom, tape, floppy, ... */ - u8 ctl; /* "normal" value for Control register */ u8 ready_stat; /* min status value for drive ready */ u8 mult_count; /* current multiple sector setting */ u8 mult_req; /* requested multiple sector setting */ @@ -1340,7 +1339,7 @@ static inline void ide_set_irq(ide_drive_t *drive, int on) { ide_hwif_t *hwif = drive->hwif; - hwif->OUTBSYNC(hwif, drive->ctl | (on ? 0 : 2), + hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | (on ? 0 : 2), hwif->io_ports.ctl_addr); } -- cgit v1.2.3 From 63f5abb0959337db0d5bece9cefba03cdcadec51 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 15 Jul 2008 21:21:51 +0200 Subject: ide: remove action argument in ide_do_drive_cmd ide_do_drive_cmd is called only with ide_preempt action argument. So we can remove the action argument in ide_do_drive_cmd and ide_action_t typedef. This patch also includes two minor cleanups: 1) ide_do_drive_cmd always succeeds so we don't need the return value; 2) the callers use blk_rq_init before ide_do_drive_cmd so there is no need to initialize rq->errors. Signed-off-by: FUJITA Tomonori Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 2 +- drivers/ide/ide-floppy.c | 2 +- drivers/ide/ide-io.c | 40 +++++++++------------------------------- drivers/ide/ide-tape.c | 2 +- drivers/scsi/ide-scsi.c | 3 ++- include/linux/ide.h | 12 +----------- 6 files changed, 15 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 0fbc2d8d0d53..043129c422fe 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -213,7 +213,7 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense, /* NOTE! Save the failed command in "rq->buffer" */ rq->buffer = (void *) failed_command; - (void) ide_do_drive_cmd(drive, rq, ide_preempt); + ide_do_drive_cmd(drive, rq); } static void cdrom_end_request(ide_drive_t *drive, int uptodate) diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 1852008d9ee4..53209a473937 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -291,7 +291,7 @@ static void idefloppy_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, rq->cmd_type = REQ_TYPE_SPECIAL; rq->cmd_flags |= REQ_PREEMPT; rq->rq_disk = floppy->disk; - (void) ide_do_drive_cmd(drive, rq, ide_preempt); + ide_do_drive_cmd(drive, rq); } static struct ide_atapi_pc *idefloppy_next_pc_storage(ide_drive_t *drive) diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index c28fcdf0ee9e..28057747c1f8 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -1520,49 +1520,27 @@ irqreturn_t ide_intr (int irq, void *dev_id) * ide_do_drive_cmd - issue IDE special command * @drive: device to issue command * @rq: request to issue - * @action: action for processing * * This function issues a special IDE device request * onto the request queue. * - * If action is ide_wait, then the rq is queued at the end of the - * request queue, and the function sleeps until it has been processed. - * This is for use when invoked from an ioctl handler. - * - * If action is ide_preempt, then the rq is queued at the head of - * the request queue, displacing the currently-being-processed - * request and this function returns immediately without waiting - * for the new rq to be completed. This is VERY DANGEROUS, and is - * intended for careful use by the ATAPI tape/cdrom driver code. - * - * If action is ide_end, then the rq is queued at the end of the - * request queue, and the function returns immediately without waiting - * for the new rq to be completed. This is again intended for careful - * use by the ATAPI tape/cdrom driver code. + * the rq is queued at the head of the request queue, displacing + * the currently-being-processed request and this function + * returns immediately without waiting for the new rq to be + * completed. This is VERY DANGEROUS, and is intended for + * careful use by the ATAPI tape/cdrom driver code. */ - -int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action) + +void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq) { unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); - int where = ELEVATOR_INSERT_BACK; - - rq->errors = 0; - - if (action == ide_preempt) - where = ELEVATOR_INSERT_FRONT; spin_lock_irqsave(&ide_lock, flags); - if (action == ide_preempt) - hwgroup->rq = NULL; - __elv_add_request(drive->queue, rq, where, 1); + hwgroup->rq = NULL; + __elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 1); __generic_unplug_device(drive->queue); - /* the queue is stopped so it won't be plugged+unplugged */ - if (blk_pm_resume_request(rq)) - do_ide_request(drive->queue); spin_unlock_irqrestore(&ide_lock, flags); - - return 0; } EXPORT_SYMBOL(ide_do_drive_cmd); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index cc7991c7c252..a562df820777 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -691,7 +691,7 @@ static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, rq->cmd_flags |= REQ_PREEMPT; rq->buffer = (char *) pc; rq->rq_disk = tape->disk; - (void) ide_do_drive_cmd(drive, rq, ide_preempt); + ide_do_drive_cmd(drive, rq); } /* diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 58e30efe7a74..569ffde6d047 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -245,7 +245,8 @@ static int idescsi_check_condition(ide_drive_t *drive, ide_scsi_hex_dump(pc->c, 6); } rq->rq_disk = scsi->disk; - return ide_do_drive_cmd(drive, rq, ide_preempt); + ide_do_drive_cmd(drive, rq); + return 0; } static int idescsi_end_request(ide_drive_t *, int, int); diff --git a/include/linux/ide.h b/include/linux/ide.h index d8c86f0362c4..04267dc1edf2 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -851,17 +851,7 @@ int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long); extern ide_startstop_t ide_do_reset (ide_drive_t *); -/* - * "action" parameter type for ide_do_drive_cmd() below. - */ -typedef enum { - ide_wait, /* insert rq at end of list, and wait for it */ - ide_preempt, /* insert rq in front of current request */ - ide_head_wait, /* insert rq in front of current request and wait for it */ - ide_end /* insert rq at end of list, but don't wait for it */ -} ide_action_t; - -extern int ide_do_drive_cmd(ide_drive_t *, struct request *, ide_action_t); +extern void ide_do_drive_cmd(ide_drive_t *, struct request *); extern void ide_end_drive_cmd(ide_drive_t *, u8, u8); -- cgit v1.2.3 From 92f5daff2b8439fa4c57c57f47823ffc459c3bd9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:55 +0200 Subject: ide-tape: make pc->idetape_callback void There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-tape.c | 13 +++++++------ include/linux/ide.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index d387aaf0eb39..88d26efdf844 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -619,7 +619,7 @@ static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects) return 0; } -static ide_startstop_t ide_tape_callback(ide_drive_t *drive) +static void ide_tape_callback(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = tape->pc; @@ -675,8 +675,6 @@ static ide_startstop_t ide_tape_callback(ide_drive_t *drive) } idetape_end_request(drive, uptodate, 0); - - return ide_stopped; } static void idetape_init_pc(struct ide_atapi_pc *pc) @@ -843,7 +841,8 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) if (tape->failed_pc == pc) tape->failed_pc = NULL; /* Command finished - Call the callback function */ - return pc->idetape_callback(drive); + pc->idetape_callback(drive); + return ide_stopped; } if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { @@ -1035,7 +1034,8 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->error = IDETAPE_ERROR_GENERAL; } tape->failed_pc = NULL; - return pc->idetape_callback(drive); + pc->idetape_callback(drive); + return ide_stopped; } debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]); @@ -1120,7 +1120,8 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) pc->error = IDETAPE_ERROR_GENERAL; tape->failed_pc = NULL; } - return pc->idetape_callback(drive); + pc->idetape_callback(drive); + return ide_stopped; } static void idetape_create_read_cmd(idetape_tape_t *tape, diff --git a/include/linux/ide.h b/include/linux/ide.h index 04267dc1edf2..8936b21a7030 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -641,7 +641,7 @@ struct ide_atapi_pc { */ u8 pc_buf[256]; void (*idefloppy_callback) (ide_drive_t *); - ide_startstop_t (*idetape_callback) (ide_drive_t *); + void (*idetape_callback) (ide_drive_t *); /* idetape only */ struct idetape_bh *bh; -- cgit v1.2.3 From 1b06e92aa03018e4b3ba281e03a7711d9b71a998 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:56 +0200 Subject: ide-{floppy,tape}: merge pc->idefloppy_callback and pc->idetape_callback Merge pc->idefloppy_callback and pc->idetape_callback into pc->callback. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 6 +++--- drivers/ide/ide-tape.c | 8 ++++---- include/linux/ide.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 2058a6f3f331..a9f3127a74ed 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -354,7 +354,7 @@ static void idefloppy_init_pc(struct ide_atapi_pc *pc) pc->req_xfer = 0; pc->buf = pc->pc_buf; pc->buf_size = IDEFLOPPY_PC_BUFFER_SIZE; - pc->idefloppy_callback = &ide_floppy_callback; + pc->callback = ide_floppy_callback; } static void idefloppy_create_request_sense_cmd(struct ide_atapi_pc *pc) @@ -438,7 +438,7 @@ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) if (floppy->failed_pc == pc) floppy->failed_pc = NULL; /* Command finished - Call the callback function */ - pc->idefloppy_callback(drive); + pc->callback(drive); return ide_stopped; } @@ -612,7 +612,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->error = IDEFLOPPY_ERROR_GENERAL; floppy->failed_pc = NULL; - pc->idefloppy_callback(drive); + pc->callback(drive); return ide_stopped; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 88d26efdf844..ce9b6d327528 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -687,7 +687,7 @@ static void idetape_init_pc(struct ide_atapi_pc *pc) pc->buf_size = IDETAPE_PC_BUFFER_SIZE; pc->bh = NULL; pc->b_data = NULL; - pc->idetape_callback = ide_tape_callback; + pc->callback = ide_tape_callback; } static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc) @@ -841,7 +841,7 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) if (tape->failed_pc == pc) tape->failed_pc = NULL; /* Command finished - Call the callback function */ - pc->idetape_callback(drive); + pc->callback(drive); return ide_stopped; } @@ -1034,7 +1034,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->error = IDETAPE_ERROR_GENERAL; } tape->failed_pc = NULL; - pc->idetape_callback(drive); + pc->callback(drive); return ide_stopped; } debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]); @@ -1120,7 +1120,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) pc->error = IDETAPE_ERROR_GENERAL; tape->failed_pc = NULL; } - pc->idetape_callback(drive); + pc->callback(drive); return ide_stopped; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 8936b21a7030..f079456adfdb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -640,8 +640,8 @@ struct ide_atapi_pc { * to change/removal later. */ u8 pc_buf[256]; - void (*idefloppy_callback) (ide_drive_t *); - void (*idetape_callback) (ide_drive_t *); + + void (*callback)(ide_drive_t *); /* idetape only */ struct idetape_bh *bh; -- cgit v1.2.3 From 5e3310958204912f3f00be2592c945fbc37db6ae Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:56 +0200 Subject: ide-{floppy,tape}: PC_FLAG_DMA_RECOMMENDED -> PC_FLAG_DMA_OK * Use PC_FLAG_DMA_OK flag instead of PC_FLAG_DMA_RECOMMENDED one. * Remove no longer used PC_FLAG_DMA_RECOMMENDED flag. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 6 +++--- drivers/ide/ide-tape.c | 6 +++--- include/linux/ide.h | 9 ++++----- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index a9f3127a74ed..dbefe35c1396 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -630,7 +630,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, } dma = 0; - if ((pc->flags & PC_FLAG_DMA_RECOMMENDED) && drive->using_dma) + if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) dma = !hwif->dma_ops->dma_setup(drive); ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma); @@ -755,7 +755,7 @@ static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy, pc->flags |= PC_FLAG_WRITING; pc->buf = NULL; pc->req_xfer = pc->buf_size = blocks * floppy->block_size; - pc->flags |= PC_FLAG_DMA_RECOMMENDED; + pc->flags |= PC_FLAG_DMA_OK; } static void idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy, @@ -769,7 +769,7 @@ static void idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy, pc->flags |= PC_FLAG_WRITING; pc->buf = rq->data; if (rq->bio) - pc->flags |= PC_FLAG_DMA_RECOMMENDED; + pc->flags |= PC_FLAG_DMA_OK; /* * possibly problematic, doesn't look like ide-floppy correctly * handled scattered requests if dma fails... diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index ce9b6d327528..e8a5852fa2d4 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1050,7 +1050,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->flags &= ~PC_FLAG_DMA_ERROR; ide_dma_off(drive); } - if ((pc->flags & PC_FLAG_DMA_RECOMMENDED) && drive->using_dma) + if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) dma_ok = !hwif->dma_ops->dma_setup(drive); ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma_ok); @@ -1138,7 +1138,7 @@ static void idetape_create_read_cmd(idetape_tape_t *tape, pc->buf_size = length * tape->blk_size; pc->req_xfer = pc->buf_size; if (pc->req_xfer == tape->buffer_size) - pc->flags |= PC_FLAG_DMA_RECOMMENDED; + pc->flags |= PC_FLAG_DMA_OK; } static void idetape_create_write_cmd(idetape_tape_t *tape, @@ -1157,7 +1157,7 @@ static void idetape_create_write_cmd(idetape_tape_t *tape, pc->buf_size = length * tape->blk_size; pc->req_xfer = pc->buf_size; if (pc->req_xfer == tape->buffer_size) - pc->flags |= PC_FLAG_DMA_RECOMMENDED; + pc->flags |= PC_FLAG_DMA_OK; } static ide_startstop_t idetape_do_request(ide_drive_t *drive, diff --git a/include/linux/ide.h b/include/linux/ide.h index f079456adfdb..63cee2947f60 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -602,12 +602,11 @@ enum { PC_FLAG_SUPPRESS_ERROR = (1 << 1), PC_FLAG_WAIT_FOR_DSC = (1 << 2), PC_FLAG_DMA_OK = (1 << 3), - PC_FLAG_DMA_RECOMMENDED = (1 << 4), - PC_FLAG_DMA_IN_PROGRESS = (1 << 5), - PC_FLAG_DMA_ERROR = (1 << 6), - PC_FLAG_WRITING = (1 << 7), + PC_FLAG_DMA_IN_PROGRESS = (1 << 4), + PC_FLAG_DMA_ERROR = (1 << 5), + PC_FLAG_WRITING = (1 << 6), /* command timed out */ - PC_FLAG_TIMEDOUT = (1 << 8), + PC_FLAG_TIMEDOUT = (1 << 7), }; struct ide_atapi_pc { -- cgit v1.2.3 From 5d41893c0f9caf94b449eada0279a08c86f0212e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:57 +0200 Subject: ide: add PC_FLAG_ZIP_DRIVE pc flag Add PC_FLAG_ZIP_DRIVE pc flag, set it in idefloppy_do_request() and check for it (instead of checking for IDEFLOPPY_FLAG_ZIP_DRIVE) in idefloppy_transfer_pc(). This is a preparation for adding generic ide_transfer_pc() helper. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 8 ++++++-- include/linux/ide.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 1df6a3143596..cff90c4b217e 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -559,7 +559,7 @@ static ide_startstop_t idefloppy_transfer_pc1(ide_drive_t *drive) * 40 and 50msec work well. idefloppy_pc_intr will not be actually * used until after the packet is moved in about 50 msec. */ - if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) { + if (pc->flags & PC_FLAG_ZIP_DRIVE) { timeout = floppy->ticks; expiry = &idefloppy_transfer_pc2; } else { @@ -575,7 +575,7 @@ static ide_startstop_t idefloppy_transfer_pc1(ide_drive_t *drive) hwif->dma_ops->dma_start(drive); } - if ((floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) == 0) + if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) /* Send the actual packet */ hwif->output_data(drive, NULL, floppy->pc->c, 12); @@ -826,7 +826,11 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive, return ide_stopped; } + if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) + pc->flags |= PC_FLAG_ZIP_DRIVE; + pc->rq = rq; + return idefloppy_issue_pc(drive, pc); } diff --git a/include/linux/ide.h b/include/linux/ide.h index 63cee2947f60..89feaea9e20b 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -607,6 +607,7 @@ enum { PC_FLAG_WRITING = (1 << 6), /* command timed out */ PC_FLAG_TIMEDOUT = (1 << 7), + PC_FLAG_ZIP_DRIVE = (1 << 8), }; struct ide_atapi_pc { -- cgit v1.2.3 From 594c16d8dd54cd7b1c5ef1ec3ac0f6bf34301dad Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:58 +0200 Subject: ide: add ide_transfer_pc() helper * Add ide-atapi.c file for generic ATAPI support together with CONFIG_IDE_ATAPI config option. * Add generic ide_transfer_pc() helper to ide-atapi.c and then convert ide-{floppy,tape,scsi} device drivers to use it. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 6 +++++ drivers/ide/Makefile | 1 + drivers/ide/ide-atapi.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-floppy.c | 28 +------------------ drivers/ide/ide-tape.c | 56 ++------------------------------------ drivers/scsi/ide-scsi.c | 30 ++------------------- include/linux/ide.h | 3 +++ 7 files changed, 85 insertions(+), 109 deletions(-) create mode 100644 drivers/ide/ide-atapi.c (limited to 'include/linux') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 1607536ff5fb..cf707c8f08d4 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -98,6 +98,9 @@ if BLK_DEV_IDE comment "Please see Documentation/ide/ide.txt for help/info on IDE drives" +config IDE_ATAPI + bool + config BLK_DEV_IDE_SATA bool "Support for SATA (deprecated; conflicts with libata SATA driver)" default n @@ -201,6 +204,7 @@ config BLK_DEV_IDECD_VERBOSE_ERRORS config BLK_DEV_IDETAPE tristate "Include IDE/ATAPI TAPE support" + select IDE_ATAPI help If you have an IDE tape drive using the ATAPI protocol, say Y. ATAPI is a newer protocol used by IDE tape and CD-ROM drives, @@ -223,6 +227,7 @@ config BLK_DEV_IDETAPE config BLK_DEV_IDEFLOPPY tristate "Include IDE/ATAPI FLOPPY support" + select IDE_ATAPI ---help--- If you have an IDE floppy drive which uses the ATAPI protocol, answer Y. ATAPI is a newer protocol used by IDE CD-ROM/tape/floppy @@ -246,6 +251,7 @@ config BLK_DEV_IDEFLOPPY config BLK_DEV_IDESCSI tristate "SCSI emulation support" depends on SCSI + select IDE_ATAPI ---help--- WARNING: ide-scsi is no longer needed for cd writing applications! The 2.6 kernel supports direct writing to ide-cd, which eliminates diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index f94b679b611e..a2b3f84d710d 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -14,6 +14,7 @@ EXTRA_CFLAGS += -Idrivers/ide ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o # core IDE code +ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c new file mode 100644 index 000000000000..25939bc60402 --- /dev/null +++ b/drivers/ide/ide-atapi.c @@ -0,0 +1,70 @@ +/* + * ATAPI support. + */ + +#include +#include +#include + +static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) +{ + ide_hwif_t *hwif = drive->hwif; + int retries = 100; + + while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) { + printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " + "a packet command, retrying\n", drive->name); + udelay(100); + ireason = hwif->INB(hwif->io_ports.nsect_addr); + if (retries == 0) { + printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " + "a packet command, ignoring\n", + drive->name); + ireason |= CD; + ireason &= ~IO; + } + } + + return ireason; +} + +ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, + ide_handler_t *handler, unsigned int timeout, + ide_expiry_t *expiry) +{ + ide_hwif_t *hwif = drive->hwif; + ide_startstop_t startstop; + u8 ireason; + + if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) { + printk(KERN_ERR "%s: Strange, packet command initiated yet " + "DRQ isn't asserted\n", drive->name); + return startstop; + } + + ireason = hwif->INB(hwif->io_ports.nsect_addr); + if (drive->media == ide_tape && !drive->scsi) + ireason = ide_wait_ireason(drive, ireason); + + if ((ireason & CD) == 0 || (ireason & IO)) { + printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " + "a packet command\n", drive->name); + return ide_do_reset(drive); + } + + /* Set the interrupt routine */ + ide_set_handler(drive, handler, timeout, expiry); + + /* Begin DMA, if necessary */ + if (pc->flags & PC_FLAG_DMA_OK) { + pc->flags |= PC_FLAG_DMA_IN_PROGRESS; + hwif->dma_ops->dma_start(drive); + } + + /* Send the actual packet */ + if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) + hwif->output_data(drive, NULL, pc->c, 12); + + return ide_started; +} +EXPORT_SYMBOL_GPL(ide_transfer_pc); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index a7c138dc324c..e7a1025c03c4 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -532,25 +532,11 @@ static int idefloppy_transfer_pc2(ide_drive_t *drive) static ide_startstop_t idefloppy_transfer_pc1(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; idefloppy_floppy_t *floppy = drive->driver_data; struct ide_atapi_pc *pc = floppy->pc; ide_expiry_t *expiry; unsigned int timeout; - ide_startstop_t startstop; - u8 ireason; - if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) { - printk(KERN_ERR "%s: Strange, packet command initiated yet " - "DRQ isn't asserted\n", drive->name); - return startstop; - } - ireason = hwif->INB(hwif->io_ports.nsect_addr); - if ((ireason & CD) == 0 || (ireason & IO)) { - printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " - "a packet command\n", drive->name); - return ide_do_reset(drive); - } /* * The following delay solves a problem with ATAPI Zip 100 drives * where the Busy flag was apparently being deasserted before the @@ -567,19 +553,7 @@ static ide_startstop_t idefloppy_transfer_pc1(ide_drive_t *drive) expiry = NULL; } - ide_set_handler(drive, &idefloppy_pc_intr, timeout, expiry); - - /* Begin DMA, if necessary */ - if (pc->flags & PC_FLAG_DMA_OK) { - pc->flags |= PC_FLAG_DMA_IN_PROGRESS; - hwif->dma_ops->dma_start(drive); - } - - if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) - /* Send the actual packet */ - hwif->output_data(drive, NULL, floppy->pc->c, 12); - - return ide_started; + return ide_transfer_pc(drive, pc, idefloppy_pc_intr, timeout, expiry); } static void ide_floppy_report_error(idefloppy_floppy_t *floppy, diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 2a362138f973..5adc2c9ae418 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -947,64 +947,12 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) * again, the callback function will be called and then we will handle the next * request. */ - -static u8 ide_tape_wait_ireason(ide_drive_t *drive, u8 ireason) -{ - ide_hwif_t *hwif = drive->hwif; - int retries = 100; - - while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) { - printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " - "a packet command, retrying\n", drive->name); - udelay(100); - ireason = hwif->INB(hwif->io_ports.nsect_addr); - if (retries == 0) { - printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " - "a packet command, ignoring\n", - drive->name); - ireason |= CD; - ireason &= ~IO; - } - } - - return ireason; -} - static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; - struct ide_atapi_pc *pc = tape->pc; - ide_startstop_t startstop; - u8 ireason; - - if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) { - printk(KERN_ERR "%s: Strange, packet command initiated yet " - "DRQ isn't asserted\n", drive->name); - return startstop; - } - - ireason = hwif->INB(hwif->io_ports.nsect_addr); - ireason = ide_tape_wait_ireason(drive, ireason); - if ((ireason & CD) == 0 || (ireason & IO)) { - printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " - "a packet command\n", drive->name); - return ide_do_reset(drive); - } - /* Set the interrupt routine */ - ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); - - /* Begin DMA, if necessary */ - if (pc->flags & PC_FLAG_DMA_OK) { - pc->flags |= PC_FLAG_DMA_IN_PROGRESS; - hwif->dma_ops->dma_start(drive); - } - - /* Send the actual packet */ - hwif->output_data(drive, NULL, pc->c, 12); - - return ide_started; + return ide_transfer_pc(drive, tape->pc, idetape_pc_intr, + IDETAPE_WAIT_CMD, NULL); } static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index c9fdf60c9dcf..d41348f2245e 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -453,36 +453,10 @@ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; idescsi_scsi_t *scsi = drive_to_idescsi(drive); - struct ide_atapi_pc *pc = scsi->pc; - ide_startstop_t startstop; - u8 ireason; - - if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { - printk(KERN_ERR "%s: Strange, packet command initiated yet " - "DRQ isn't asserted\n", drive->name); - return startstop; - } - ireason = hwif->INB(hwif->io_ports.nsect_addr); - if ((ireason & CD) == 0 || (ireason & IO)) { - printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " - "a packet command\n", drive->name); - return ide_do_reset (drive); - } - /* Set the interrupt routine */ - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); - - if (pc->flags & PC_FLAG_DMA_OK) { - pc->flags |= PC_FLAG_DMA_IN_PROGRESS; - hwif->dma_ops->dma_start(drive); - } - - /* Send the actual packet */ - hwif->output_data(drive, NULL, scsi->pc->c, 12); - - return ide_started; + return ide_transfer_pc(drive, scsi->pc, idescsi_pc_intr, + get_timeout(scsi->pc), idescsi_expiry); } static inline int idescsi_set_direction(struct ide_atapi_pc *pc) diff --git a/include/linux/ide.h b/include/linux/ide.h index 89feaea9e20b..bed3c58798ae 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -967,6 +967,9 @@ extern int drive_is_ready(ide_drive_t *); void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); +ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *, + ide_handler_t *, unsigned int, ide_expiry_t *); + ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); void task_end_request(ide_drive_t *, struct request *, u8); -- cgit v1.2.3 From 28c7214bd8c2bbd4873b8f1e7f58d86d3731124f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:21:59 +0200 Subject: ide: add PC_FLAG_DRQ_INTERRUPT pc flag Add PC_FLAG_DRQ_INTERRUPT pc flag, set it in ide*_do_request() and check for it (instead of checking for IDE*_FLAG_DRQ_INTERRUPT) in ide*_issue_pc(). This is a preparation for adding generic ide_issue_pc() helper. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 5 ++++- drivers/ide/ide-tape.c | 11 ++++++++--- drivers/scsi/ide-scsi.c | 6 +++++- include/linux/ide.h | 1 + 4 files changed, 18 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index e7a1025c03c4..13f650fa2125 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -619,7 +619,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma); - if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT) { + if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { /* Issue the packet command */ ide_execute_command(drive, WIN_PACKETCMD, &idefloppy_transfer_pc1, @@ -800,6 +800,9 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive, return ide_stopped; } + if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT) + pc->flags |= PC_FLAG_DRQ_INTERRUPT; + if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) pc->flags |= PC_FLAG_ZIP_DRIVE; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 5adc2c9ae418..cba18a675506 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1020,7 +1020,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma_ok); - if (test_bit(IDETAPE_FLAG_DRQ_INTERRUPT, &tape->flags)) { + if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { ide_execute_command(drive, WIN_PACKETCMD, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL); return ide_started; @@ -1143,8 +1143,10 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, } /* Retry a failed packet command */ - if (tape->failed_pc && tape->pc->c[0] == REQUEST_SENSE) - return idetape_issue_pc(drive, tape->failed_pc); + if (tape->failed_pc && tape->pc->c[0] == REQUEST_SENSE) { + pc = tape->failed_pc; + goto out; + } if (postponed_rq != NULL) if (rq != postponed_rq) { @@ -1216,6 +1218,9 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, } BUG(); out: + if (test_bit(IDETAPE_FLAG_DRQ_INTERRUPT, &tape->flags)) + pc->flags |= PC_FLAG_DRQ_INTERRUPT; + return idetape_issue_pc(drive, pc); } diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 1d261298d61a..b7c5e8391575 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -525,7 +525,7 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive, ide_pktcmd_tf_load(drive, 0, bcount, dma); - if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags)) { + if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { ide_execute_command(drive, WIN_PACKETCMD, &idescsi_transfer_pc, get_timeout(pc), idescsi_expiry); return ide_started; @@ -548,6 +548,10 @@ static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *r if (blk_sense_request(rq) || blk_special_request(rq)) { struct ide_atapi_pc *pc = (struct ide_atapi_pc *)rq->special; + idescsi_scsi_t *scsi = drive_to_idescsi(drive); + + if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags)) + pc->flags |= PC_FLAG_DRQ_INTERRUPT; if (drive->using_dma && !idescsi_map_sg(drive, pc)) pc->flags |= PC_FLAG_DMA_OK; diff --git a/include/linux/ide.h b/include/linux/ide.h index bed3c58798ae..c2274ad44b2e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -608,6 +608,7 @@ enum { /* command timed out */ PC_FLAG_TIMEDOUT = (1 << 7), PC_FLAG_ZIP_DRIVE = (1 << 8), + PC_FLAG_DRQ_INTERRUPT = (1 << 9), }; struct ide_atapi_pc { -- cgit v1.2.3 From 6bf1641ca1c7554f0da54aaf89788731b541bacc Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:22:00 +0200 Subject: ide: add ide_issue_pc() helper Add generic ide_issue_pc() helper to ide-atapi.c and then convert ide-{floppy,tape,scsi} device drivers to use it. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-floppy.c | 35 ++-------------------------------- drivers/ide/ide-tape.c | 30 ++--------------------------- drivers/scsi/ide-scsi.c | 30 ++--------------------------- include/linux/ide.h | 2 ++ 5 files changed, 57 insertions(+), 89 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 25939bc60402..932a83abaf06 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -68,3 +68,52 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, return ide_started; } EXPORT_SYMBOL_GPL(ide_transfer_pc); + +ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, + ide_handler_t *handler, unsigned int timeout, + ide_expiry_t *expiry) +{ + ide_hwif_t *hwif = drive->hwif; + u16 bcount; + u8 dma = 0; + + /* We haven't transferred any data yet */ + pc->xferred = 0; + pc->cur_pos = pc->buf; + + /* Request to transfer the entire buffer at once */ + if (drive->media == ide_tape && !drive->scsi) + bcount = pc->req_xfer; + else + bcount = min(pc->req_xfer, 63 * 1024); + + if (pc->flags & PC_FLAG_DMA_ERROR) { + pc->flags &= ~PC_FLAG_DMA_ERROR; + ide_dma_off(drive); + } + + if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) { + if (drive->scsi) + hwif->sg_mapped = 1; + dma = !hwif->dma_ops->dma_setup(drive); + if (drive->scsi) + hwif->sg_mapped = 0; + } + + if (!dma) + pc->flags &= ~PC_FLAG_DMA_OK; + + ide_pktcmd_tf_load(drive, drive->scsi ? 0 : IDE_TFLAG_OUT_DEVICE, + bcount, dma); + + /* Issue the packet command */ + if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { + ide_execute_command(drive, WIN_PACKETCMD, handler, + timeout, NULL); + return ide_started; + } else { + ide_execute_pkt_cmd(drive); + return (*handler)(drive); + } +} +EXPORT_SYMBOL_GPL(ide_issue_pc); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 13f650fa2125..e658aafc51da 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -576,9 +576,6 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc) { idefloppy_floppy_t *floppy = drive->driver_data; - ide_hwif_t *hwif = drive->hwif; - u16 bcount; - u8 dma; if (floppy->failed_pc == NULL && pc->c[0] != GPCMD_REQUEST_SENSE) @@ -600,37 +597,9 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, debug_log("Retry number - %d\n", pc->retries); pc->retries++; - /* We haven't transferred any data yet */ - pc->xferred = 0; - pc->cur_pos = pc->buf; - bcount = min(pc->req_xfer, 63 * 1024); - - if (pc->flags & PC_FLAG_DMA_ERROR) { - pc->flags &= ~PC_FLAG_DMA_ERROR; - ide_dma_off(drive); - } - dma = 0; - if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) - dma = !hwif->dma_ops->dma_setup(drive); - - if (!dma) - pc->flags &= ~PC_FLAG_DMA_OK; - - ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma); - - if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { - /* Issue the packet command */ - ide_execute_command(drive, WIN_PACKETCMD, - &idefloppy_transfer_pc1, - IDEFLOPPY_WAIT_CMD, - NULL); - return ide_started; - } else { - /* Issue the packet command */ - ide_execute_pkt_cmd(drive); - return idefloppy_transfer_pc1(drive); - } + return ide_issue_pc(drive, pc, idefloppy_transfer_pc1, + IDEFLOPPY_WAIT_CMD, NULL); } static void idefloppy_create_prevent_cmd(struct ide_atapi_pc *pc, int prevent) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index cba18a675506..7907a1e41918 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -958,10 +958,7 @@ static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc) { - ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; - int dma_ok = 0; - u16 bcount; if (tape->pc->c[0] == REQUEST_SENSE && pc->c[0] == REQUEST_SENSE) { @@ -1002,32 +999,9 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]); pc->retries++; - /* We haven't transferred any data yet */ - pc->xferred = 0; - pc->cur_pos = pc->buf; - /* Request to transfer the entire buffer at once */ - bcount = pc->req_xfer; - - if (pc->flags & PC_FLAG_DMA_ERROR) { - pc->flags &= ~PC_FLAG_DMA_ERROR; - ide_dma_off(drive); - } - if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) - dma_ok = !hwif->dma_ops->dma_setup(drive); - - if (!dma_ok) - pc->flags &= ~PC_FLAG_DMA_OK; - - ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_DEVICE, bcount, dma_ok); - if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { - ide_execute_command(drive, WIN_PACKETCMD, &idetape_transfer_pc, - IDETAPE_WAIT_CMD, NULL); - return ide_started; - } else { - ide_execute_pkt_cmd(drive); - return idetape_transfer_pc(drive); - } + return ide_issue_pc(drive, pc, idetape_transfer_pc, + IDETAPE_WAIT_CMD, NULL); } /* A mode sense command is used to "sense" tape parameters. */ diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index b7c5e8391575..32415466fbfe 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -502,38 +502,12 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc) { idescsi_scsi_t *scsi = drive_to_idescsi(drive); - ide_hwif_t *hwif = drive->hwif; - u16 bcount; - u8 dma = 0; /* Set the current packet command */ scsi->pc = pc; - /* We haven't transferred any data yet */ - pc->xferred = 0; - pc->cur_pos = pc->buf; - /* Request to transfer the entire buffer at once */ - bcount = min(pc->req_xfer, 63 * 1024); - - if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) { - hwif->sg_mapped = 1; - dma = !hwif->dma_ops->dma_setup(drive); - hwif->sg_mapped = 0; - } - - if (!dma) - pc->flags &= ~PC_FLAG_DMA_OK; - ide_pktcmd_tf_load(drive, 0, bcount, dma); - - if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { - ide_execute_command(drive, WIN_PACKETCMD, &idescsi_transfer_pc, - get_timeout(pc), idescsi_expiry); - return ide_started; - } else { - /* Issue the packet command */ - ide_execute_pkt_cmd(drive); - return idescsi_transfer_pc(drive); - } + return ide_issue_pc(drive, pc, idescsi_transfer_pc, + get_timeout(pc), idescsi_expiry); } /* diff --git a/include/linux/ide.h b/include/linux/ide.h index c2274ad44b2e..fee07a7edb19 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -970,6 +970,8 @@ void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *, ide_handler_t *, unsigned int, ide_expiry_t *); +ide_startstop_t ide_issue_pc(ide_drive_t *, struct ide_atapi_pc *, + ide_handler_t *, unsigned int, ide_expiry_t *); ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); -- cgit v1.2.3 From 646c0cb6c430f8d3ad3769dd1518fe664ff0ce27 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 15 Jul 2008 21:22:03 +0200 Subject: ide: add ide_pc_intr() helper * ide-tape.c: add 'drive' argument to idetape_update_buffers(). * Add generic ide_pc_intr() helper to ide-atapi.c and then convert ide-{floppy,tape,scsi} device drivers to use it. * ide-tape.c: remove no longer needed DBG_PC_INTR. There should be no functional changes caused by this patch (unless the debugging is explicitely compiled in). Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-floppy.c | 128 +--------------------------------- drivers/ide/ide-tape.c | 132 ++--------------------------------- drivers/scsi/ide-scsi.c | 115 +----------------------------- include/linux/ide.h | 6 ++ 5 files changed, 195 insertions(+), 363 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 932a83abaf06..2802031de670 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -5,6 +5,183 @@ #include #include #include +#include + +#ifdef DEBUG +#define debug_log(fmt, args...) \ + printk(KERN_INFO "ide: " fmt, ## args) +#else +#define debug_log(fmt, args...) do {} while (0) +#endif + +/* TODO: unify the code thus making some arguments go away */ +ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, + ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, + void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), + void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *), + void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) +{ + ide_hwif_t *hwif = drive->hwif; + xfer_func_t *xferfunc; + unsigned int temp; + u16 bcount; + u8 stat, ireason, scsi = drive->scsi; + + debug_log("Enter %s - interrupt handler\n", __func__); + + if (pc->flags & PC_FLAG_TIMEDOUT) { + pc->callback(drive); + return ide_stopped; + } + + /* Clear the interrupt */ + stat = ide_read_status(drive); + + if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { + if (hwif->dma_ops->dma_end(drive) || + (drive->media == ide_tape && !scsi && (stat & ERR_STAT))) { + if (drive->media == ide_floppy && !scsi) + printk(KERN_ERR "%s: DMA %s error\n", + drive->name, rq_data_dir(pc->rq) + ? "write" : "read"); + pc->flags |= PC_FLAG_DMA_ERROR; + } else { + pc->xferred = pc->req_xfer; + if (update_buffers) + update_buffers(drive, pc); + } + debug_log("%s: DMA finished\n", drive->name); + } + + /* No more interrupts */ + if ((stat & DRQ_STAT) == 0) { + debug_log("Packet command completed, %d bytes transferred\n", + pc->xferred); + + pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; + + local_irq_enable_in_hardirq(); + + if (drive->media == ide_tape && !scsi && + (stat & ERR_STAT) && pc->c[0] == REQUEST_SENSE) + stat &= ~ERR_STAT; + if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) { + /* Error detected */ + debug_log("%s: I/O error\n", drive->name); + + if (drive->media != ide_tape || scsi) { + pc->rq->errors++; + if (scsi) + goto cmd_finished; + } + + if (pc->c[0] == REQUEST_SENSE) { + printk(KERN_ERR "%s: I/O error in request sense" + " command\n", drive->name); + return ide_do_reset(drive); + } + + debug_log("[cmd %x]: check condition\n", pc->c[0]); + + /* Retry operation */ + retry_pc(drive); + /* queued, but not started */ + return ide_stopped; + } +cmd_finished: + pc->error = 0; + if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && + (stat & SEEK_STAT) == 0) { + dsc_handle(drive); + return ide_stopped; + } + /* Command finished - Call the callback function */ + pc->callback(drive); + return ide_stopped; + } + + if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { + pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; + printk(KERN_ERR "%s: The device wants to issue more interrupts " + "in DMA mode\n", drive->name); + ide_dma_off(drive); + return ide_do_reset(drive); + } + /* Get the number of bytes to transfer on this interrupt. */ + bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | + hwif->INB(hwif->io_ports.lbam_addr); + + ireason = hwif->INB(hwif->io_ports.nsect_addr); + + if (ireason & CD) { + printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); + return ide_do_reset(drive); + } + if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) { + /* Hopefully, we will never get here */ + printk(KERN_ERR "%s: We wanted to %s, but the device wants us " + "to %s!\n", drive->name, + (ireason & IO) ? "Write" : "Read", + (ireason & IO) ? "Read" : "Write"); + return ide_do_reset(drive); + } + if (!(pc->flags & PC_FLAG_WRITING)) { + /* Reading - Check that we have enough space */ + temp = pc->xferred + bcount; + if (temp > pc->req_xfer) { + if (temp > pc->buf_size) { + printk(KERN_ERR "%s: The device wants to send " + "us more data than expected - " + "discarding data\n", + drive->name); + if (scsi) + temp = pc->buf_size - pc->xferred; + else + temp = 0; + if (temp) { + if (pc->sg) + io_buffers(drive, pc, temp, 0); + else + hwif->input_data(drive, NULL, + pc->cur_pos, temp); + printk(KERN_ERR "%s: transferred %d of " + "%d bytes\n", + drive->name, + temp, bcount); + } + pc->xferred += temp; + pc->cur_pos += temp; + ide_pad_transfer(drive, 0, bcount - temp); + ide_set_handler(drive, handler, timeout, + expiry); + return ide_started; + } + debug_log("The device wants to send us more data than " + "expected - allowing transfer\n"); + } + xferfunc = hwif->input_data; + } else + xferfunc = hwif->output_data; + + if ((drive->media == ide_floppy && !scsi && !pc->buf) || + (drive->media == ide_tape && !scsi && pc->bh) || + (scsi && pc->sg)) + io_buffers(drive, pc, bcount, !!(pc->flags & PC_FLAG_WRITING)); + else + xferfunc(drive, NULL, pc->cur_pos, bcount); + + /* Update the current position */ + pc->xferred += bcount; + pc->cur_pos += bcount; + + debug_log("[cmd %x] transferred %d bytes on that intr.\n", + pc->c[0], bcount); + + /* And set the interrupt handler again */ + ide_set_handler(drive, handler, timeout, expiry); + return ide_started; +} +EXPORT_SYMBOL_GPL(ide_pc_intr); static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) { diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 70aef97fb8bc..0f3602a5efb0 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -388,132 +388,10 @@ static void idefloppy_retry_pc(ide_drive_t *drive) static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; - ide_hwif_t *hwif = drive->hwif; - struct ide_atapi_pc *pc = floppy->pc; - struct request *rq = pc->rq; - xfer_func_t *xferfunc; - unsigned int temp; - int dma_error = 0; - u16 bcount; - u8 stat, ireason; - - debug_log("Enter %s - interrupt handler\n", __func__); - - /* Clear the interrupt */ - stat = ide_read_status(drive); - - if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - dma_error = hwif->dma_ops->dma_end(drive); - if (dma_error) { - printk(KERN_ERR "%s: DMA %s error\n", drive->name, - rq_data_dir(rq) ? "write" : "read"); - pc->flags |= PC_FLAG_DMA_ERROR; - } else { - pc->xferred = pc->req_xfer; - idefloppy_update_buffers(drive, pc); - } - debug_log("%s: DMA finished\n", drive->name); - } - - /* No more interrupts */ - if ((stat & DRQ_STAT) == 0) { - debug_log("Packet command completed, %d bytes transferred\n", - pc->xferred); - pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; - - local_irq_enable_in_hardirq(); - - if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) { - /* Error detected */ - debug_log("%s: I/O error\n", drive->name); - rq->errors++; - if (pc->c[0] == GPCMD_REQUEST_SENSE) { - printk(KERN_ERR "%s: I/O error in request sense" - " command\n", drive->name); - return ide_do_reset(drive); - } - - debug_log("[cmd %x]: check condition\n", pc->c[0]); - - /* Retry operation */ - idefloppy_retry_pc(drive); - /* queued, but not started */ - return ide_stopped; - } - pc->error = 0; - /* Command finished - Call the callback function */ - pc->callback(drive); - return ide_stopped; - } - - if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; - printk(KERN_ERR "%s: The device wants to issue more interrupts " - "in DMA mode\n", drive->name); - ide_dma_off(drive); - return ide_do_reset(drive); - } - - /* Get the number of bytes to transfer */ - bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | - hwif->INB(hwif->io_ports.lbam_addr); - /* on this interrupt */ - ireason = hwif->INB(hwif->io_ports.nsect_addr); - - if (ireason & CD) { - printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); - return ide_do_reset(drive); - } - if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) { - /* Hopefully, we will never get here */ - printk(KERN_ERR "%s: We wanted to %s, but the device wants us " - "to %s!\n", drive->name, - (ireason & IO) ? "Write" : "Read", - (ireason & IO) ? "Read" : "Write"); - return ide_do_reset(drive); - } - if (!(pc->flags & PC_FLAG_WRITING)) { - /* Reading - Check that we have enough space */ - temp = pc->xferred + bcount; - if (temp > pc->req_xfer) { - if (temp > pc->buf_size) { - printk(KERN_ERR "%s: The device wants to send " - "us more data than expected - " - "discarding data\n", - drive->name); - ide_pad_transfer(drive, 0, bcount); - - ide_set_handler(drive, - &idefloppy_pc_intr, - IDEFLOPPY_WAIT_CMD, - NULL); - return ide_started; - } - debug_log("The device wants to send us more data than " - "expected - allowing transfer\n"); - } - } - if (pc->flags & PC_FLAG_WRITING) - xferfunc = hwif->output_data; - else - xferfunc = hwif->input_data; - - if (pc->buf) - xferfunc(drive, NULL, pc->cur_pos, bcount); - else - ide_floppy_io_buffers(drive, pc, bcount, - !!(pc->flags & PC_FLAG_WRITING)); - - /* Update the current position */ - pc->xferred += bcount; - pc->cur_pos += bcount; - - debug_log("[cmd %x] transferred %d bytes on that intr.\n", - pc->c[0], bcount); - /* And set the interrupt handler again */ - ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); - return ide_started; + return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr, + IDEFLOPPY_WAIT_CMD, NULL, idefloppy_update_buffers, + idefloppy_retry_pc, NULL, ide_floppy_io_buffers); } /* diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 10f2d3336286..0afa109ec99a 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -56,8 +56,6 @@ enum { DBG_PROCS = (1 << 3), /* buffer alloc info (pc_stack & rq_stack) */ DBG_PCRQ_STACK = (1 << 4), - /* IRQ handler (always log debug info if debugging is on) */ - DBG_PC_INTR = (1 << 5), }; /* define to see debug info */ @@ -66,7 +64,7 @@ enum { #if IDETAPE_DEBUG_LOG #define debug_log(lvl, fmt, args...) \ { \ - if ((lvl & DBG_PC_INTR) || (tape->debug_mask & lvl)) \ + if (tape->debug_mask & lvl) \ printk(KERN_INFO "ide-tape: " fmt, ## args); \ } #else @@ -441,7 +439,7 @@ static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, } } -static void idetape_update_buffers(struct ide_atapi_pc *pc) +static void idetape_update_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc) { struct idetape_bh *bh = pc->bh; int count; @@ -526,7 +524,7 @@ static void idetape_analyze_error(ide_drive_t *drive, u8 *sense) pc->xferred = pc->req_xfer - tape->blk_size * get_unaligned_be32(&sense[3]); - idetape_update_buffers(pc); + idetape_update_buffers(drive, pc); } /* @@ -800,129 +798,11 @@ static void ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, */ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; - struct ide_atapi_pc *pc = tape->pc; - xfer_func_t *xferfunc; - unsigned int temp; - u16 bcount; - u8 stat, ireason; - - debug_log(DBG_PC_INTR, "Enter %s - interrupt handler\n", __func__); - - /* Clear the interrupt */ - stat = ide_read_status(drive); - - if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - if (hwif->dma_ops->dma_end(drive) || (stat & ERR_STAT)) { - pc->flags |= PC_FLAG_DMA_ERROR; - } else { - pc->xferred = pc->req_xfer; - idetape_update_buffers(pc); - } - debug_log(DBG_PC_INTR, "%s: DMA finished\n", drive->name); - } - - /* No more interrupts */ - if ((stat & DRQ_STAT) == 0) { - debug_log(DBG_PC_INTR, "Packet command completed, %d bytes" - " transferred\n", pc->xferred); - - pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; - local_irq_enable_in_hardirq(); - - if ((stat & ERR_STAT) && pc->c[0] == REQUEST_SENSE) - stat &= ~ERR_STAT; - if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) { - /* Error detected */ - debug_log(DBG_PC_INTR, "%s: I/O error\n", drive->name); - - if (pc->c[0] == REQUEST_SENSE) { - printk(KERN_ERR "%s: I/O error in request sense" - " command\n", drive->name); - return ide_do_reset(drive); - } - debug_log(DBG_PC_INTR, "[cmd %x]: check condition\n", - pc->c[0]); - - /* Retry operation */ - idetape_retry_pc(drive); - return ide_stopped; - } - pc->error = 0; - if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && - (stat & SEEK_STAT) == 0) { - ide_tape_handle_dsc(drive); - return ide_stopped; - } - /* Command finished - Call the callback function */ - pc->callback(drive); - return ide_stopped; - } - - if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; - printk(KERN_ERR "%s: The device wants to issue more interrupts " - "in DMA mode\n", drive->name); - ide_dma_off(drive); - return ide_do_reset(drive); - } - /* Get the number of bytes to transfer on this interrupt. */ - bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | - hwif->INB(hwif->io_ports.lbam_addr); - - ireason = hwif->INB(hwif->io_ports.nsect_addr); - - if (ireason & CD) { - printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); - return ide_do_reset(drive); - } - if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) { - /* Hopefully, we will never get here */ - printk(KERN_ERR "%s: We wanted to %s, but the device wants us " - "to %s!\n", drive->name, - (ireason & IO) ? "Write" : "Read", - (ireason & IO) ? "Read" : "Write"); - return ide_do_reset(drive); - } - if (!(pc->flags & PC_FLAG_WRITING)) { - /* Reading - Check that we have enough space */ - temp = pc->xferred + bcount; - if (temp > pc->req_xfer) { - if (temp > pc->buf_size) { - printk(KERN_ERR "%s: The device wants to send " - "us more data than expected - " - "discarding data\n", - drive->name); - ide_pad_transfer(drive, 0, bcount); - ide_set_handler(drive, &idetape_pc_intr, - IDETAPE_WAIT_CMD, NULL); - return ide_started; - } - debug_log(DBG_PC_INTR, "The device wants to send us more " - "data than expected - allowing transfer\n"); - } - xferfunc = hwif->input_data; - } else { - xferfunc = hwif->output_data; - } - - if (pc->bh) - ide_tape_io_buffers(drive, pc, bcount, - !!(pc->flags & PC_FLAG_WRITING)); - else - xferfunc(drive, NULL, pc->cur_pos, bcount); - - /* Update the current position */ - pc->xferred += bcount; - pc->cur_pos += bcount; - - debug_log(DBG_PC_INTR, "[cmd %x] transferred %d bytes on that intr.\n", - pc->c[0], bcount); - /* And set the interrupt handler again */ - ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); - return ide_started; + return ide_pc_intr(drive, tape->pc, idetape_pc_intr, IDETAPE_WAIT_CMD, + NULL, idetape_update_buffers, idetape_retry_pc, + ide_tape_handle_dsc, ide_tape_io_buffers); } /* diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index ada733ca6725..683bce375c74 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -356,120 +356,11 @@ static int idescsi_expiry(ide_drive_t *drive) static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { idescsi_scsi_t *scsi = drive_to_idescsi(drive); - ide_hwif_t *hwif = drive->hwif; struct ide_atapi_pc *pc = scsi->pc; - struct request *rq = pc->rq; - xfer_func_t *xferfunc; - unsigned int temp; - u16 bcount; - u8 stat, ireason; - - debug_log("Enter %s - interrupt handler\n", __func__); - - if (pc->flags & PC_FLAG_TIMEDOUT) { - pc->callback(drive); - return ide_stopped; - } - - /* Clear the interrupt */ - stat = ide_read_status(drive); - - if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - if (hwif->dma_ops->dma_end(drive)) - pc->flags |= PC_FLAG_DMA_ERROR; - else - pc->xferred = pc->req_xfer; - debug_log("%s: DMA finished\n", drive->name); - } - - if ((stat & DRQ_STAT) == 0) { - /* No more interrupts */ - debug_log("Packet command completed, %d bytes transferred\n", - pc->xferred); - pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; - local_irq_enable_in_hardirq(); - if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) { - /* Error detected */ - debug_log("%s: I/O error\n", drive->name); - - rq->errors++; - } - pc->callback(drive); - return ide_stopped; - } - if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; - printk(KERN_ERR "%s: The device wants to issue more interrupts " - "in DMA mode\n", drive->name); - ide_dma_off(drive); - return ide_do_reset(drive); - } - bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | - hwif->INB(hwif->io_ports.lbam_addr); - ireason = hwif->INB(hwif->io_ports.nsect_addr); - - if (ireason & CD) { - printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); - return ide_do_reset (drive); - } - if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) { - /* Hopefully, we will never get here */ - printk(KERN_ERR "%s: We wanted to %s, but the device wants us " - "to %s!\n", drive->name, - (ireason & IO) ? "Write" : "Read", - (ireason & IO) ? "Read" : "Write"); - return ide_do_reset(drive); - } - if (!(pc->flags & PC_FLAG_WRITING)) { - temp = pc->xferred + bcount; - if (temp > pc->req_xfer) { - if (temp > pc->buf_size) { - printk(KERN_ERR "%s: The device wants to send " - "us more data than expected - " - "discarding data\n", - drive->name); - temp = pc->buf_size - pc->xferred; - if (temp) { - if (pc->sg) - ide_scsi_io_buffers(drive, pc, - temp, 0); - else - hwif->input_data(drive, NULL, - pc->cur_pos, temp); - printk(KERN_ERR "%s: transferred %d of " - "%d bytes\n", - drive->name, - temp, bcount); - } - pc->xferred += temp; - pc->cur_pos += temp; - ide_pad_transfer(drive, 0, bcount - temp); - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); - return ide_started; - } - debug_log("The device wants to send us more data than " - "expected - allowing transfer\n"); - } - xferfunc = hwif->input_data; - } else - xferfunc = hwif->output_data; - - if (pc->sg) - ide_scsi_io_buffers(drive, pc, bcount, - !!(pc->flags & PC_FLAG_WRITING)); - else - xferfunc(drive, NULL, pc->cur_pos, bcount); - - /* Update the current position */ - pc->xferred += bcount; - pc->cur_pos += bcount; - - debug_log("[cmd %x] transferred %d bytes on that intr.\n", - pc->c[0], bcount); - /* And set the interrupt handler again */ - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); - return ide_started; + return ide_pc_intr(drive, pc, idescsi_pc_intr, get_timeout(pc), + idescsi_expiry, NULL, NULL, NULL, + ide_scsi_io_buffers); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) diff --git a/include/linux/ide.h b/include/linux/ide.h index fee07a7edb19..ac4eeb2932ef 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -968,6 +968,12 @@ extern int drive_is_ready(ide_drive_t *); void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); +ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, + ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, + void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), + void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *), + void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, + int)); ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *, ide_handler_t *, unsigned int, ide_expiry_t *); ide_startstop_t ide_issue_pc(ide_drive_t *, struct ide_atapi_pc *, -- cgit v1.2.3 From 8f920d5e29f86d3425a68e1c3bc264d1f6f55112 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 Jul 2008 14:06:48 -0400 Subject: lockd: eliminate duplicate nlmsvc_lookup_host call from nlmsvc_testlock nlmsvc_testlock calls nlmsvc_lookup_host to find a nlm_host struct. The callers of this functions, however, call nlmsvc_retrieve_args or nlm4svc_retrieve_args, which also return a nlm_host struct. Change nlmsvc_testlock to take a host arg instead of calling nlmsvc_lookup_host itself and change the callers to pass a pointer to the nlm_host they've already found. We take a reference to host in the place where nlmsvc_testlock() previous did a new lookup, so the reference counting is unchanged from before. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 +- fs/lockd/svclock.c | 12 +++--------- fs/lockd/svcproc.c | 2 +- include/linux/lockd/lockd.h | 3 ++- 4 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 006a832d46f2..8cfb9daa7c77 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -99,7 +99,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now check for conflicting locks */ - resp->status = nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie); + resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 81aca859bfde..f40afb3a0e69 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -460,8 +460,8 @@ out: */ __be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, struct nlm_lock *conflock, - struct nlm_cookie *cookie) + struct nlm_host *host, struct nlm_lock *lock, + struct nlm_lock *conflock, struct nlm_cookie *cookie) { struct nlm_block *block = NULL; int error; @@ -479,16 +479,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, if (block == NULL) { struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL); - struct nlm_host *host; if (conf == NULL) return nlm_granted; - /* Create host handle for callback */ - host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); - if (host == NULL) { - kfree(conf); - return nlm_lck_denied_nolocks; - } + nlm_get_host(host); block = nlmsvc_create_block(rqstp, host, file, lock, cookie); if (block == NULL) { kfree(conf); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index fce3d7039625..e099f589b61b 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -128,7 +128,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now check for conflicting locks */ - resp->status = cast_status(nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie)); + resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 102d928f7206..b27967034b53 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -203,7 +203,8 @@ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_lock *, int, struct nlm_cookie *); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, - struct nlm_lock *, struct nlm_lock *, struct nlm_cookie *); + struct nlm_host *, struct nlm_lock *, + struct nlm_lock *, struct nlm_cookie *); __be32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); unsigned long nlmsvc_retry_blocked(void); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, -- cgit v1.2.3 From 6cde4de80773497d8333985b135f472eda870904 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 Jul 2008 14:26:17 -0400 Subject: lockd: eliminate duplicate nlmsvc_lookup_host call from nlmsvc_lock nlmsvc_lock calls nlmsvc_lookup_host to find a nlm_host struct. The callers of this function, however, call nlmsvc_retrieve_args or nlm4svc_retrieve_args, which also return a nlm_host struct. Change nlmsvc_lock to take a host arg instead of calling nlmsvc_lookup_host itself and change the callers to pass a pointer to the nlm_host they've already found. Since nlmsvc_testlock() now just uses the caller's reference, we no longer need to get or release it. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 +- fs/lockd/svclock.c | 10 ++-------- fs/lockd/svcproc.c | 2 +- include/linux/lockd/lockd.h | 3 ++- 4 files changed, 6 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 8cfb9daa7c77..189b2ce01da6 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -145,7 +145,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, #endif /* Now try to lock the file */ - resp->status = nlmsvc_lock(rqstp, file, &argp->lock, + resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index f40afb3a0e69..bcf73f6e8226 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -358,10 +358,10 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) */ __be32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) + struct nlm_host *host, struct nlm_lock *lock, int wait, + struct nlm_cookie *cookie) { struct nlm_block *block = NULL; - struct nlm_host *host; int error; __be32 ret; @@ -373,11 +373,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, (long long)lock->fl.fl_end, wait); - /* Create host handle for callback */ - host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); - if (host == NULL) - return nlm_lck_denied_nolocks; - /* Lock file against concurrent access */ mutex_lock(&file->f_mutex); /* Get existing block (in case client is busy-waiting) @@ -450,7 +445,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, out: mutex_unlock(&file->f_mutex); nlmsvc_release_block(block); - nlm_release_host(host); dprintk("lockd: nlmsvc_lock returned %u\n", ret); return ret; } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index e099f589b61b..82dc9083ba67 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -175,7 +175,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, #endif /* Now try to lock the file */ - resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock, + resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index b27967034b53..f81f9dd5f147 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -200,7 +200,8 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); * Server-side lock handling */ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, - struct nlm_lock *, int, struct nlm_cookie *); + struct nlm_host *, struct nlm_lock *, int, + struct nlm_cookie *); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, -- cgit v1.2.3 From 367c8c7bd9a2882daad6c9cb607e1db8ef781ad4 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 30 Jun 2008 18:58:14 -0400 Subject: lockd: Pass "struct sockaddr *" to new failover-by-IP function Pass a more generic socket address type to nlmsvc_unlock_all_by_ip() to allow for future support of IPv6. Also provide additional sanity checking in failover_unlock_ip() when constructing the server's IP address. As an added bonus, provide clean kerneldoc comments on related NLM interfaces which were recently added. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/svcsubs.c | 32 +++++++++++++++++++++++--------- fs/nfsd/nfsctl.c | 15 ++++++++++----- include/linux/lockd/lockd.h | 2 +- 3 files changed, 34 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index d1c48b539df8..198b4e55b373 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -373,13 +373,16 @@ nlmsvc_free_host_resources(struct nlm_host *host) } } -/* - * Remove all locks held for clients +/** + * nlmsvc_invalidate_all - remove all locks held for clients + * + * Release all locks held by NFS clients. + * */ void nlmsvc_invalidate_all(void) { - /* Release all locks held by NFS clients. + /* * Previously, the code would call * nlmsvc_free_host_resources for each client in * turn, which is about as inefficient as it gets. @@ -396,6 +399,12 @@ nlmsvc_match_sb(void *datap, struct nlm_file *file) return sb == file->f_file->f_path.mnt->mnt_sb; } +/** + * nlmsvc_unlock_all_by_sb - release locks held on this file system + * @sb: super block + * + * Release all locks held by clients accessing this file system. + */ int nlmsvc_unlock_all_by_sb(struct super_block *sb) { @@ -409,17 +418,22 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) { - __be32 *server_addr = datap; - - return host->h_saddr.sin_addr.s_addr == *server_addr; + return nlm_cmp_addr(&host->h_saddr, datap); } +/** + * nlmsvc_unlock_all_by_ip - release local locks by IP address + * @server_addr: server's IP address as seen by clients + * + * Release all locks held by clients accessing this host + * via the passed in IP address. + */ int -nlmsvc_unlock_all_by_ip(__be32 server_addr) +nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr) { int ret; - ret = nlm_traverse_files(&server_addr, nlmsvc_match_ip, NULL); - return ret ? -EIO : 0; + ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL); + return ret ? -EIO : 0; } EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 2c2eb8796c10..1955a2702e60 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -310,9 +310,12 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) { - __be32 server_ip; - char *fo_path, c; + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; int b1, b2, b3, b4; + char c; + char *fo_path; /* sanity check */ if (size == 0) @@ -326,11 +329,13 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) return -EINVAL; /* get ipv4 address */ - if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4) + if (sscanf(fo_path, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) != 4) + return -EINVAL; + if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255) return -EINVAL; - server_ip = htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); + sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); - return nlmsvc_unlock_all_by_ip(server_ip); + return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin); } static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index f81f9dd5f147..dbb87ab282e8 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -226,7 +226,7 @@ void nlmsvc_invalidate_all(void); * Cluster failover support */ int nlmsvc_unlock_all_by_sb(struct super_block *sb); -int nlmsvc_unlock_all_by_ip(__be32 server_addr); +int nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr); static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) { -- cgit v1.2.3 From c2e1b09ff237c0a3687b9a804cc8bf489743cffc Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 14 Jul 2008 16:03:30 -0400 Subject: SUNRPC: Support registering IPv6 interfaces with local rpcbind daemon Introduce a new API to register RPC services on IPv6 interfaces to allow the NFS server and lockd to advertise on IPv6 networks. Unlike rpcb_register(), the new rpcb_v4_register() function uses rpcbind protocol version 4 to contact the local rpcbind daemon. The version 4 SET/UNSET procedures allow services to register address families besides AF_INET, register at specific network interfaces, and register transport protocols besides UDP and TCP. All of this functionality is exposed via the new rpcb_v4_register() kernel API. A user-space rpcbind daemon implementation that supports version 4 of the rpcbind protocol is required in order to make use of this new API. Note that rpcbind version 3 is sufficient to support the new rpcbind facilities listed above, but most extant implementations use version 4. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 3 + net/sunrpc/rpcb_clnt.c | 178 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 764fd4c286e0..e5bfe01ee305 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -125,6 +125,9 @@ void rpc_shutdown_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); int rpcb_register(u32, u32, int, unsigned short, int *); +int rpcb_v4_register(const u32 program, const u32 version, + const struct sockaddr *address, + const char *netid, int *result); int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int); void rpcb_getport_async(struct rpc_task *); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 8b75c306e661..24db2b4d12d3 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -87,6 +87,7 @@ struct rpcbind_args { static struct rpc_procinfo rpcb_procedures2[]; static struct rpc_procinfo rpcb_procedures3[]; +static struct rpc_procinfo rpcb_procedures4[]; struct rpcb_info { u32 rpc_vers; @@ -122,6 +123,12 @@ static const struct sockaddr_in rpcb_inaddr_loopback = { .sin_port = htons(RPCBIND_PORT), }; +static const struct sockaddr_in6 rpcb_in6addr_loopback = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LOOPBACK_INIT, + .sin6_port = htons(RPCBIND_PORT), +}; + static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr, size_t addrlen, u32 version) { @@ -196,13 +203,38 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * rpcb_register - set or unset a port registration with the local rpcbind svc * @prog: RPC program number to bind * @vers: RPC version number to bind - * @prot: transport protocol to use to make this request + * @prot: transport protocol to register * @port: port value to register - * @okay: result code + * @okay: OUT: result code + * + * RPC services invoke this function to advertise their contact + * information via the system's rpcbind daemon. RPC services + * invoke this function once for each [program, version, transport] + * tuple they wish to advertise. + * + * Callers may also unregister RPC services that are no longer + * available by setting the passed-in port to zero. This removes + * all registered transports for [program, version] from the local + * rpcbind database. + * + * Returns zero if the registration request was dispatched + * successfully and a reply was received. The rpcbind daemon's + * boolean result code is stored in *okay. * - * port == 0 means unregister, port != 0 means register. + * Returns an errno value and sets *result to zero if there was + * some problem that prevented the rpcbind request from being + * dispatched, or if the rpcbind daemon did not respond within + * the timeout. * - * This routine supports only rpcbind version 2. + * This function uses rpcbind protocol version 2 to contact the + * local rpcbind daemon. + * + * Registration works over both AF_INET and AF_INET6, and services + * registered via this function are advertised as available for any + * address. If the local rpcbind daemon is listening on AF_INET6, + * services registered via this function will be advertised on + * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6 + * addresses). */ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) { @@ -230,6 +262,144 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) RPCBVERS_2, &msg, okay); } +/* + * Fill in AF_INET family-specific arguments to register + */ +static int rpcb_register_netid4(struct sockaddr_in *address_to_register, + struct rpc_message *msg) +{ + struct rpcbind_args *map = msg->rpc_argp; + unsigned short port = ntohs(address_to_register->sin_port); + char buf[32]; + + /* Construct AF_INET universal address */ + snprintf(buf, sizeof(buf), + NIPQUAD_FMT".%u.%u", + NIPQUAD(address_to_register->sin_addr.s_addr), + port >> 8, port & 0xff); + map->r_addr = buf; + + dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " + "local rpcbind\n", (port ? "" : "un"), + map->r_prog, map->r_vers, + map->r_addr, map->r_netid); + + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; + if (port) + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET]; + + return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, + sizeof(rpcb_inaddr_loopback), + RPCBVERS_4, msg, msg->rpc_resp); +} + +/* + * Fill in AF_INET6 family-specific arguments to register + */ +static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, + struct rpc_message *msg) +{ + struct rpcbind_args *map = msg->rpc_argp; + unsigned short port = ntohs(address_to_register->sin6_port); + char buf[64]; + + /* Construct AF_INET6 universal address */ + snprintf(buf, sizeof(buf), + NIP6_FMT".%u.%u", + NIP6(address_to_register->sin6_addr), + port >> 8, port & 0xff); + map->r_addr = buf; + + dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " + "local rpcbind\n", (port ? "" : "un"), + map->r_prog, map->r_vers, + map->r_addr, map->r_netid); + + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; + if (port) + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET]; + + return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback, + sizeof(rpcb_in6addr_loopback), + RPCBVERS_4, msg, msg->rpc_resp); +} + +/** + * rpcb_v4_register - set or unset a port registration with the local rpcbind + * @program: RPC program number of service to (un)register + * @version: RPC version number of service to (un)register + * @address: address family, IP address, and port to (un)register + * @netid: netid of transport protocol to (un)register + * @result: result code from rpcbind RPC call + * + * RPC services invoke this function to advertise their contact + * information via the system's rpcbind daemon. RPC services + * invoke this function once for each [program, version, address, + * netid] tuple they wish to advertise. + * + * Callers may also unregister RPC services that are no longer + * available by setting the port number in the passed-in address + * to zero. Callers pass a netid of "" to unregister all + * transport netids associated with [program, version, address]. + * + * Returns zero if the registration request was dispatched + * successfully and a reply was received. The rpcbind daemon's + * result code is stored in *result. + * + * Returns an errno value and sets *result to zero if there was + * some problem that prevented the rpcbind request from being + * dispatched, or if the rpcbind daemon did not respond within + * the timeout. + * + * This function uses rpcbind protocol version 4 to contact the + * local rpcbind daemon. The local rpcbind daemon must support + * version 4 of the rpcbind protocol in order for these functions + * to register a service successfully. + * + * Supported netids include "udp" and "tcp" for UDP and TCP over + * IPv4, and "udp6" and "tcp6" for UDP and TCP over IPv6, + * respectively. + * + * The contents of @address determine the address family and the + * port to be registered. The usual practice is to pass INADDR_ANY + * as the raw address, but specifying a non-zero address is also + * supported by this API if the caller wishes to advertise an RPC + * service on a specific network interface. + * + * Note that passing in INADDR_ANY does not create the same service + * registration as IN6ADDR_ANY. The former advertises an RPC + * service on any IPv4 address, but not on IPv6. The latter + * advertises the service on all IPv4 and IPv6 addresses. + */ +int rpcb_v4_register(const u32 program, const u32 version, + const struct sockaddr *address, const char *netid, + int *result) +{ + struct rpcbind_args map = { + .r_prog = program, + .r_vers = version, + .r_netid = netid, + .r_owner = RPCB_OWNER_STRING, + }; + struct rpc_message msg = { + .rpc_argp = &map, + .rpc_resp = result, + }; + + *result = 0; + + switch (address->sa_family) { + case AF_INET: + return rpcb_register_netid4((struct sockaddr_in *)address, + &msg); + case AF_INET6: + return rpcb_register_netid6((struct sockaddr_in6 *)address, + &msg); + } + + return -EAFNOSUPPORT; +} + /** * rpcb_getport_sync - obtain the port for an RPC service on a given host * @sin: address of remote peer -- cgit v1.2.3 From 3be53f3f213223f50d8e29b5e1869685bf040a1e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:36 +0200 Subject: ide: move some bits from ide-timing.h to Move struct ide_timing and IDE_TIMING_* defines to from drivers/ide/ide-timing.h. While at it: - use u8/u16 instead of short for struct ide_timing fields - use enum for IDE_TIMING_* There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-timing.h | 23 ----------------------- include/linux/ide.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-timing.h b/drivers/ide/ide-timing.h index ebe884d76881..724879910ac8 100644 --- a/drivers/ide/ide-timing.h +++ b/drivers/ide/ide-timing.h @@ -28,18 +28,6 @@ #include #include -struct ide_timing { - u8 mode; - short setup; /* t1 */ - short act8b; /* t2 for 8-bit io */ - short rec8b; /* t2i for 8-bit io */ - short cyc8b; /* t0 for 8-bit io */ - short active; /* t2 or tD */ - short recover; /* t2i or tK */ - short cycle; /* t0 */ - short udma; /* t2CYCTYP/2 */ -}; - /* * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). * These were taken from ATA/ATAPI-6 standard, rev 0a, except @@ -79,17 +67,6 @@ static struct ide_timing ide_timing[] = { { 0xff } }; -#define IDE_TIMING_SETUP 0x01 -#define IDE_TIMING_ACT8B 0x02 -#define IDE_TIMING_REC8B 0x04 -#define IDE_TIMING_CYC8B 0x08 -#define IDE_TIMING_8BIT 0x0e -#define IDE_TIMING_ACTIVE 0x10 -#define IDE_TIMING_RECOVER 0x20 -#define IDE_TIMING_CYCLE 0x40 -#define IDE_TIMING_UDMA 0x80 -#define IDE_TIMING_ALL 0xff - #define ENOUGH(v,unit) (((v)-1)/(unit)+1) #define EZ(v,unit) ((v)?ENOUGH(v,unit):0) diff --git a/include/linux/ide.h b/include/linux/ide.h index ac4eeb2932ef..81c6ea436beb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1271,6 +1271,34 @@ static inline int ide_dev_is_sata(struct hd_driveid *id) u64 ide_get_lba_addr(struct ide_taskfile *, int); u8 ide_dump_status(ide_drive_t *, const char *, u8); +struct ide_timing { + u8 mode; + u8 setup; /* t1 */ + u16 act8b; /* t2 for 8-bit io */ + u16 rec8b; /* t2i for 8-bit io */ + u16 cyc8b; /* t0 for 8-bit io */ + u16 active; /* t2 or tD */ + u16 recover; /* t2i or tK */ + u16 cycle; /* t0 */ + u16 udma; /* t2CYCTYP/2 */ +}; + +enum { + IDE_TIMING_SETUP = (1 << 0), + IDE_TIMING_ACT8B = (1 << 1), + IDE_TIMING_REC8B = (1 << 2), + IDE_TIMING_CYC8B = (1 << 3), + IDE_TIMING_8BIT = IDE_TIMING_ACT8B | IDE_TIMING_REC8B | + IDE_TIMING_CYC8B, + IDE_TIMING_ACTIVE = (1 << 4), + IDE_TIMING_RECOVER = (1 << 5), + IDE_TIMING_CYCLE = (1 << 6), + IDE_TIMING_UDMA = (1 << 7), + IDE_TIMING_ALL = IDE_TIMING_SETUP | IDE_TIMING_8BIT | + IDE_TIMING_ACTIVE | IDE_TIMING_RECOVER | + IDE_TIMING_CYCLE | IDE_TIMING_UDMA, +}; + typedef struct ide_pio_timings_s { int setup_time; /* Address setup (ns) minimum */ int active_time; /* Active pulse (ns) minimum */ -- cgit v1.2.3 From f06ab3402aa2d6de060442c1053ea10b24b65076 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:37 +0200 Subject: ide: convert ide-timing.h to ide-timings.c library (take 2) * Don't include ide-timing.h in cs5535 and sis5513 host drivers (they don't need it currently). * Convert ide-timing.h to ide-timings.c library and add CONFIG_IDE_TIMINGS config option to be selected by host drivers using the library. While at it: - fix ide_timing_find_mode() placement v2: * Add missing EXPORT_SYMBOLs. (Stephen Rothwell ) There should be no functional changes caused by this patch. Cc: Stephen Rothwell Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 7 ++ drivers/ide/Makefile | 1 + drivers/ide/arm/palm_bk3710.c | 2 - drivers/ide/ide-timing.h | 183 ------------------------------------------ drivers/ide/ide-timings.c | 183 ++++++++++++++++++++++++++++++++++++++++++ drivers/ide/pci/amd74xx.c | 2 - drivers/ide/pci/cs5535.c | 2 - drivers/ide/pci/sis5513.c | 3 - drivers/ide/pci/via82cxxx.c | 2 - drivers/ide/ppc/pmac.c | 2 - include/linux/ide.h | 5 ++ 11 files changed, 196 insertions(+), 196 deletions(-) delete mode 100644 drivers/ide/ide-timing.h create mode 100644 drivers/ide/ide-timings.c (limited to 'include/linux') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index cd08dba8261b..994b6d39b559 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -98,6 +98,9 @@ if BLK_DEV_IDE comment "Please see Documentation/ide/ide.txt for help/info on IDE drives" +config IDE_TIMINGS + bool + config IDE_ATAPI bool @@ -469,6 +472,7 @@ config BLK_DEV_ALI15X3 config BLK_DEV_AMD74XX tristate "AMD and nVidia IDE support" depends on !ARM + select IDE_TIMINGS select BLK_DEV_IDEDMA_PCI help This driver adds explicit support for AMD-7xx and AMD-8111 chips @@ -725,6 +729,7 @@ config BLK_DEV_TRM290 config BLK_DEV_VIA82CXXX tristate "VIA82CXXX chipset support" + select IDE_TIMINGS select BLK_DEV_IDEDMA_PCI help This driver adds explicit support for VIA BusMastering IDE chips. @@ -751,6 +756,7 @@ endif config BLK_DEV_IDE_PMAC tristate "PowerMac on-board IDE support" depends on PPC_PMAC && IDE=y && BLK_DEV_IDE=y + select IDE_TIMINGS help This driver provides support for the on-board IDE controller on most of the recent Apple Power Macintoshes and PowerBooks. @@ -912,6 +918,7 @@ config BLK_DEV_Q40IDE config BLK_DEV_PALMCHIP_BK3710 tristate "Palmchip bk3710 IDE controller support" depends on ARCH_DAVINCI + select IDE_TIMINGS select BLK_DEV_IDEDMA_SFF help Say Y here if you want to support the onchip IDE controller on the diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index a2b3f84d710d..cb1350684c9a 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -14,6 +14,7 @@ EXTRA_CFLAGS += -Idrivers/ide ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o # core IDE code +ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 3839f5722985..9b8a45d2cf17 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -74,8 +74,6 @@ struct palm_bk3710_udmatiming { #define BK3710_IORDYTMP 0x78 #define BK3710_IORDYTMS 0x7C -#include "../ide-timing.h" - static unsigned ideclk_period; /* in nanoseconds */ static const struct palm_bk3710_udmatiming palm_bk3710_udmatimings[6] = { diff --git a/drivers/ide/ide-timing.h b/drivers/ide/ide-timing.h deleted file mode 100644 index 98e05f545450..000000000000 --- a/drivers/ide/ide-timing.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef _IDE_TIMING_H -#define _IDE_TIMING_H - -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include -#include - -/* - * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). - * These were taken from ATA/ATAPI-6 standard, rev 0a, except - * for PIO 5, which is a nonstandard extension and UDMA6, which - * is currently supported only by Maxtor drives. - */ - -static struct ide_timing ide_timing[] = { - - { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 }, - { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 }, - { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 }, - { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 }, - - { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 }, - { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, - { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, - - { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, - { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, - { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, - - { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 }, - { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, - { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, - - { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, - { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, - { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, - - { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 }, - { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 }, - { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 }, - - { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, - - { 0xff } -}; - -#define ENOUGH(v, unit) (((v) - 1) / (unit) + 1) -#define EZ(v, unit) ((v) ? ENOUGH(v, unit) : 0) - -static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, - int T, int UT) -{ - q->setup = EZ(t->setup * 1000, T); - q->act8b = EZ(t->act8b * 1000, T); - q->rec8b = EZ(t->rec8b * 1000, T); - q->cyc8b = EZ(t->cyc8b * 1000, T); - q->active = EZ(t->active * 1000, T); - q->recover = EZ(t->recover * 1000, T); - q->cycle = EZ(t->cycle * 1000, T); - q->udma = EZ(t->udma * 1000, UT); -} - -static void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, - struct ide_timing *m, unsigned int what) -{ - if (what & IDE_TIMING_SETUP) - m->setup = max(a->setup, b->setup); - if (what & IDE_TIMING_ACT8B) - m->act8b = max(a->act8b, b->act8b); - if (what & IDE_TIMING_REC8B) - m->rec8b = max(a->rec8b, b->rec8b); - if (what & IDE_TIMING_CYC8B) - m->cyc8b = max(a->cyc8b, b->cyc8b); - if (what & IDE_TIMING_ACTIVE) - m->active = max(a->active, b->active); - if (what & IDE_TIMING_RECOVER) - m->recover = max(a->recover, b->recover); - if (what & IDE_TIMING_CYCLE) - m->cycle = max(a->cycle, b->cycle); - if (what & IDE_TIMING_UDMA) - m->udma = max(a->udma, b->udma); -} - -static struct ide_timing *ide_timing_find_mode(u8 speed) -{ - struct ide_timing *t; - - for (t = ide_timing; t->mode != speed; t++) - if (t->mode == 0xff) - return NULL; - return t; -} - -static int ide_timing_compute(ide_drive_t *drive, u8 speed, - struct ide_timing *t, int T, int UT) -{ - struct hd_driveid *id = drive->id; - struct ide_timing *s, p; - - /* - * Find the mode. - */ - s = ide_timing_find_mode(speed); - if (s == NULL) - return -EINVAL; - - /* - * Copy the timing from the table. - */ - *t = *s; - - /* - * If the drive is an EIDE drive, it can tell us it needs extended - * PIO/MWDMA cycle timing. - */ - if (id && id->field_valid & 2) { /* EIDE drive */ - - memset(&p, 0, sizeof(p)); - - if (speed <= XFER_PIO_2) - p.cycle = p.cyc8b = id->eide_pio; - else if (speed <= XFER_PIO_5) - p.cycle = p.cyc8b = id->eide_pio_iordy; - else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) - p.cycle = id->eide_dma_min; - - ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B); - } - - /* - * Convert the timing to bus clock counts. - */ - ide_timing_quantize(t, t, T, UT); - - /* - * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, - * S.M.A.R.T and some other commands. We have to ensure that the - * DMA cycle timing is slower/equal than the fastest PIO timing. - */ - if (speed >= XFER_SW_DMA_0) { - u8 pio = ide_get_best_pio_mode(drive, 255, 5); - ide_timing_compute(drive, XFER_PIO_0 + pio, &p, T, UT); - ide_timing_merge(&p, t, t, IDE_TIMING_ALL); - } - - /* - * Lengthen active & recovery time so that cycle time is correct. - */ - if (t->act8b + t->rec8b < t->cyc8b) { - t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2; - t->rec8b = t->cyc8b - t->act8b; - } - - if (t->active + t->recover < t->cycle) { - t->active += (t->cycle - (t->active + t->recover)) / 2; - t->recover = t->cycle - t->active; - } - - return 0; -} - -#endif diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c new file mode 100644 index 000000000000..ebef6d4e3f63 --- /dev/null +++ b/drivers/ide/ide-timings.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include + +/* + * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). + * These were taken from ATA/ATAPI-6 standard, rev 0a, except + * for PIO 5, which is a nonstandard extension and UDMA6, which + * is currently supported only by Maxtor drives. + */ + +static struct ide_timing ide_timing[] = { + + { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 }, + { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 }, + { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 }, + { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 }, + + { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 }, + { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, + { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, + + { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, + { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, + { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, + + { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 }, + { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, + { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, + + { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, + { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, + { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, + + { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 }, + { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 }, + { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 }, + + { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, + + { 0xff } +}; + +struct ide_timing *ide_timing_find_mode(u8 speed) +{ + struct ide_timing *t; + + for (t = ide_timing; t->mode != speed; t++) + if (t->mode == 0xff) + return NULL; + return t; +} +EXPORT_SYMBOL_GPL(ide_timing_find_mode); + +#define ENOUGH(v, unit) (((v) - 1) / (unit) + 1) +#define EZ(v, unit) ((v) ? ENOUGH(v, unit) : 0) + +static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, + int T, int UT) +{ + q->setup = EZ(t->setup * 1000, T); + q->act8b = EZ(t->act8b * 1000, T); + q->rec8b = EZ(t->rec8b * 1000, T); + q->cyc8b = EZ(t->cyc8b * 1000, T); + q->active = EZ(t->active * 1000, T); + q->recover = EZ(t->recover * 1000, T); + q->cycle = EZ(t->cycle * 1000, T); + q->udma = EZ(t->udma * 1000, UT); +} + +void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, + struct ide_timing *m, unsigned int what) +{ + if (what & IDE_TIMING_SETUP) + m->setup = max(a->setup, b->setup); + if (what & IDE_TIMING_ACT8B) + m->act8b = max(a->act8b, b->act8b); + if (what & IDE_TIMING_REC8B) + m->rec8b = max(a->rec8b, b->rec8b); + if (what & IDE_TIMING_CYC8B) + m->cyc8b = max(a->cyc8b, b->cyc8b); + if (what & IDE_TIMING_ACTIVE) + m->active = max(a->active, b->active); + if (what & IDE_TIMING_RECOVER) + m->recover = max(a->recover, b->recover); + if (what & IDE_TIMING_CYCLE) + m->cycle = max(a->cycle, b->cycle); + if (what & IDE_TIMING_UDMA) + m->udma = max(a->udma, b->udma); +} +EXPORT_SYMBOL_GPL(ide_timing_merge); + +int ide_timing_compute(ide_drive_t *drive, u8 speed, + struct ide_timing *t, int T, int UT) +{ + struct hd_driveid *id = drive->id; + struct ide_timing *s, p; + + /* + * Find the mode. + */ + s = ide_timing_find_mode(speed); + if (s == NULL) + return -EINVAL; + + /* + * Copy the timing from the table. + */ + *t = *s; + + /* + * If the drive is an EIDE drive, it can tell us it needs extended + * PIO/MWDMA cycle timing. + */ + if (id && id->field_valid & 2) { /* EIDE drive */ + + memset(&p, 0, sizeof(p)); + + if (speed <= XFER_PIO_2) + p.cycle = p.cyc8b = id->eide_pio; + else if (speed <= XFER_PIO_5) + p.cycle = p.cyc8b = id->eide_pio_iordy; + else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) + p.cycle = id->eide_dma_min; + + ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B); + } + + /* + * Convert the timing to bus clock counts. + */ + ide_timing_quantize(t, t, T, UT); + + /* + * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, + * S.M.A.R.T and some other commands. We have to ensure that the + * DMA cycle timing is slower/equal than the fastest PIO timing. + */ + if (speed >= XFER_SW_DMA_0) { + u8 pio = ide_get_best_pio_mode(drive, 255, 5); + ide_timing_compute(drive, XFER_PIO_0 + pio, &p, T, UT); + ide_timing_merge(&p, t, t, IDE_TIMING_ALL); + } + + /* + * Lengthen active & recovery time so that cycle time is correct. + */ + if (t->act8b + t->rec8b < t->cyc8b) { + t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2; + t->rec8b = t->cyc8b - t->act8b; + } + + if (t->active + t->recover < t->cycle) { + t->active += (t->cycle - (t->active + t->recover)) / 2; + t->recover = t->cycle - t->active; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_timing_compute); diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index ad222206a429..0bfcdd0e77b3 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -21,8 +21,6 @@ #include #include -#include "ide-timing.h" - enum { AMD_IDE_CONFIG = 0x41, AMD_CABLE_DETECT = 0x42, diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index 2a2cb4911905..dc97c48623f3 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -26,8 +26,6 @@ #include #include -#include "ide-timing.h" - #define MSR_ATAC_BASE 0x51300000 #define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0) #define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01) diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index e127eb25ab63..2389945ca95d 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -52,8 +52,6 @@ #include #include -#include "ide-timing.h" - /* registers layout and init values are chipset family dependant */ #define ATA_16 0x01 @@ -616,7 +614,6 @@ MODULE_LICENSE("GPL"); /* * TODO: * - CLEANUP - * - Use drivers/ide/ide-timing.h ! * - More checks in the config registers (force values instead of * relying on the BIOS setting them correctly). * - Further optimisations ? diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 3ed9728abd24..e47384c70c40 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -35,8 +35,6 @@ #include #endif -#include "ide-timing.h" - #define VIA_IDE_ENABLE 0x40 #define VIA_IDE_CONFIG 0x41 #define VIA_FIFO_CONFIG 0x43 diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index dcb2c466bb97..5b91d23269d3 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -48,8 +48,6 @@ #include #endif -#include "../ide-timing.h" - #undef IDE_PMAC_DEBUG #define DMA_WAIT_TIMEOUT 50 diff --git a/include/linux/ide.h b/include/linux/ide.h index 81c6ea436beb..057001f6b1dc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1299,6 +1299,11 @@ enum { IDE_TIMING_CYCLE | IDE_TIMING_UDMA, }; +struct ide_timing *ide_timing_find_mode(u8); +void ide_timing_merge(struct ide_timing *, struct ide_timing *, + struct ide_timing *, unsigned int); +int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int); + typedef struct ide_pio_timings_s { int setup_time; /* Address setup (ns) minimum */ int active_time; /* Active pulse (ns) minimum */ -- cgit v1.2.3 From c9d6c1a2379373219bb3271bdcbdc0ab2edf349d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:39 +0200 Subject: ide: move ide_pio_cycle_time() to ide-timings.c All ide_pio_cycle_time() users already select CONFIG_IDE_TIMINGS so move the function from ide-lib.c to ide-timings.c. While at it: - convert ide_pio_cycle_time() to use ide_timing_find_mode() - cleanup ide_pio_cycle_time() a bit There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-lib.c | 23 ----------------------- drivers/ide/ide-timings.c | 22 ++++++++++++++++++++++ include/linux/ide.h | 2 +- 3 files changed, 23 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 7e053d217732..efa5bfa64d01 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -188,29 +188,6 @@ static int ide_scan_pio_blacklist (char *model) return -1; } -unsigned int ide_pio_cycle_time(ide_drive_t *drive, u8 pio) -{ - struct hd_driveid *id = drive->id; - int cycle_time = 0; - - if (id->field_valid & 2) { - if (id->capability & 8) - cycle_time = id->eide_pio_iordy; - else - cycle_time = id->eide_pio; - } - - /* conservative "downgrade" for all pre-ATA2 drives */ - if (pio < 3) { - if (cycle_time && cycle_time < ide_pio_timings[pio].cycle_time) - cycle_time = 0; /* use standard timing */ - } - - return cycle_time ? cycle_time : ide_pio_timings[pio].cycle_time; -} - -EXPORT_SYMBOL_GPL(ide_pio_cycle_time); - /** * ide_get_best_pio_mode - get PIO mode from drive * @drive: drive to consider diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c index ebef6d4e3f63..8c2f8327f487 100644 --- a/drivers/ide/ide-timings.c +++ b/drivers/ide/ide-timings.c @@ -1,5 +1,6 @@ /* * Copyright (c) 1999-2001 Vojtech Pavlik + * Copyright (c) 2007-2008 Bartlomiej Zolnierkiewicz * * 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 @@ -75,6 +76,27 @@ struct ide_timing *ide_timing_find_mode(u8 speed) } EXPORT_SYMBOL_GPL(ide_timing_find_mode); +u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio) +{ + struct hd_driveid *id = drive->id; + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + u16 cycle = 0; + + if (id->field_valid & 2) { + if (id->capability & 8) + cycle = id->eide_pio_iordy; + else + cycle = id->eide_pio; + + /* conservative "downgrade" for all pre-ATA2 drives */ + if (pio < 3 && cycle < t->cycle) + cycle = 0; /* use standard timing */ + } + + return cycle ? cycle : t->cycle; +} +EXPORT_SYMBOL_GPL(ide_pio_cycle_time); + #define ENOUGH(v, unit) (((v) - 1) / (unit) + 1) #define EZ(v, unit) ((v) ? ENOUGH(v, unit) : 0) diff --git a/include/linux/ide.h b/include/linux/ide.h index 057001f6b1dc..3899c761b302 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1300,6 +1300,7 @@ enum { }; struct ide_timing *ide_timing_find_mode(u8); +u16 ide_pio_cycle_time(ide_drive_t *, u8); void ide_timing_merge(struct ide_timing *, struct ide_timing *, struct ide_timing *, unsigned int); int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int); @@ -1311,7 +1312,6 @@ typedef struct ide_pio_timings_s { /* active + recovery (+ setup for some chips) */ } ide_pio_timings_t; -unsigned int ide_pio_cycle_time(ide_drive_t *, u8); u8 ide_get_best_pio_mode(ide_drive_t *, u8, u8); extern const ide_pio_timings_t ide_pio_timings[6]; -- cgit v1.2.3 From 3e153cfb5e38ae237ff27a10a833946ac95db8a4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:39 +0200 Subject: ide: remove no longer used ide_pio_timings[] Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-lib.c | 17 ----------------- include/linux/ide.h | 8 -------- 2 files changed, 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index efa5bfa64d01..3e12f229bd5f 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -75,23 +75,6 @@ static u8 ide_rate_filter(ide_drive_t *drive, u8 speed) return min(speed, mode); } -/* - * Standard (generic) timings for PIO modes, from ATA2 specification. - * These timings are for access to the IDE data port register *only*. - * Some drives may specify a mode, while also specifying a different - * value for cycle_time (from drive identification data). - */ -const ide_pio_timings_t ide_pio_timings[6] = { - { 70, 165, 600 }, /* PIO Mode 0 */ - { 50, 125, 383 }, /* PIO Mode 1 */ - { 30, 100, 240 }, /* PIO Mode 2 */ - { 30, 80, 180 }, /* PIO Mode 3 with IORDY */ - { 25, 70, 120 }, /* PIO Mode 4 with IORDY */ - { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */ -}; - -EXPORT_SYMBOL_GPL(ide_pio_timings); - /* * Shared data/functions for determining best PIO mode for an IDE drive. * Most of this stuff originally lived in cmd640.c, and changes to the diff --git a/include/linux/ide.h b/include/linux/ide.h index 3899c761b302..4e44525fa5ca 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1305,15 +1305,7 @@ void ide_timing_merge(struct ide_timing *, struct ide_timing *, struct ide_timing *, unsigned int); int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int); -typedef struct ide_pio_timings_s { - int setup_time; /* Address setup (ns) minimum */ - int active_time; /* Active pulse (ns) minimum */ - int cycle_time; /* Cycle time (ns) minimum = */ - /* active + recovery (+ setup for some chips) */ -} ide_pio_timings_t; - u8 ide_get_best_pio_mode(ide_drive_t *, u8, u8); -extern const ide_pio_timings_t ide_pio_timings[6]; int ide_set_pio_mode(ide_drive_t *, u8); int ide_set_dma_mode(ide_drive_t *, u8); -- cgit v1.2.3 From 9ad540937554a3779c5fe7af13aa390b1d2aeb3e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:39 +0200 Subject: ide: move PIO blacklist to ide-pio-blacklist.c Move PIO blacklist to ide-pio-blacklist.c. While at it: - fix comment - fix whitespace damage There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Makefile | 3 +- drivers/ide/ide-lib.c | 96 ----------------------------------------- drivers/ide/ide-pio-blacklist.c | 94 ++++++++++++++++++++++++++++++++++++++++ include/linux/ide.h | 2 + 4 files changed, 98 insertions(+), 97 deletions(-) create mode 100644 drivers/ide/ide-pio-blacklist.c (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index cb1350684c9a..8605536ea18f 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -11,7 +11,8 @@ EXTRA_CFLAGS += -Idrivers/ide -ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o +ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o \ + ide-pio-blacklist.o # core IDE code ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 3e12f229bd5f..13af72f09ec4 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -75,102 +75,6 @@ static u8 ide_rate_filter(ide_drive_t *drive, u8 speed) return min(speed, mode); } -/* - * Shared data/functions for determining best PIO mode for an IDE drive. - * Most of this stuff originally lived in cmd640.c, and changes to the - * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid - * breaking the fragile cmd640.c support. - */ - -/* - * Black list. Some drives incorrectly report their maximal PIO mode, - * at least in respect to CMD640. Here we keep info on some known drives. - */ -static struct ide_pio_info { - const char *name; - int pio; -} ide_pio_blacklist [] = { - { "Conner Peripherals 540MB - CFS540A", 3 }, - - { "WDC AC2700", 3 }, - { "WDC AC2540", 3 }, - { "WDC AC2420", 3 }, - { "WDC AC2340", 3 }, - { "WDC AC2250", 0 }, - { "WDC AC2200", 0 }, - { "WDC AC21200", 4 }, - { "WDC AC2120", 0 }, - { "WDC AC2850", 3 }, - { "WDC AC1270", 3 }, - { "WDC AC1170", 1 }, - { "WDC AC1210", 1 }, - { "WDC AC280", 0 }, - { "WDC AC31000", 3 }, - { "WDC AC31200", 3 }, - - { "Maxtor 7131 AT", 1 }, - { "Maxtor 7171 AT", 1 }, - { "Maxtor 7213 AT", 1 }, - { "Maxtor 7245 AT", 1 }, - { "Maxtor 7345 AT", 1 }, - { "Maxtor 7546 AT", 3 }, - { "Maxtor 7540 AV", 3 }, - - { "SAMSUNG SHD-3121A", 1 }, - { "SAMSUNG SHD-3122A", 1 }, - { "SAMSUNG SHD-3172A", 1 }, - - { "ST5660A", 3 }, - { "ST3660A", 3 }, - { "ST3630A", 3 }, - { "ST3655A", 3 }, - { "ST3391A", 3 }, - { "ST3390A", 1 }, - { "ST3600A", 1 }, - { "ST3290A", 0 }, - { "ST3144A", 0 }, - { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */ - /* drive) according to Seagates FIND-ATA program */ - - { "QUANTUM ELS127A", 0 }, - { "QUANTUM ELS170A", 0 }, - { "QUANTUM LPS240A", 0 }, - { "QUANTUM LPS210A", 3 }, - { "QUANTUM LPS270A", 3 }, - { "QUANTUM LPS365A", 3 }, - { "QUANTUM LPS540A", 3 }, - { "QUANTUM LIGHTNING 540A", 3 }, - { "QUANTUM LIGHTNING 730A", 3 }, - - { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */ - { "QUANTUM FIREBALL_640", 3 }, - { "QUANTUM FIREBALL_1080", 3 }, - { "QUANTUM FIREBALL_1280", 3 }, - { NULL, 0 } -}; - -/** - * ide_scan_pio_blacklist - check for a blacklisted drive - * @model: Drive model string - * - * This routine searches the ide_pio_blacklist for an entry - * matching the start/whole of the supplied model name. - * - * Returns -1 if no match found. - * Otherwise returns the recommended PIO mode from ide_pio_blacklist[]. - */ - -static int ide_scan_pio_blacklist (char *model) -{ - struct ide_pio_info *p; - - for (p = ide_pio_blacklist; p->name != NULL; p++) { - if (strncmp(p->name, model, strlen(p->name)) == 0) - return p->pio; - } - return -1; -} - /** * ide_get_best_pio_mode - get PIO mode from drive * @drive: drive to consider diff --git a/drivers/ide/ide-pio-blacklist.c b/drivers/ide/ide-pio-blacklist.c new file mode 100644 index 000000000000..a8c2c8f8660a --- /dev/null +++ b/drivers/ide/ide-pio-blacklist.c @@ -0,0 +1,94 @@ +/* + * PIO blacklist. Some drives incorrectly report their maximal PIO mode, + * at least in respect to CMD640. Here we keep info on some known drives. + * + * Changes to the ide_pio_blacklist[] should be made with EXTREME CAUTION + * to avoid breaking the fragile cmd640.c support. + */ + +#include + +static struct ide_pio_info { + const char *name; + int pio; +} ide_pio_blacklist [] = { + { "Conner Peripherals 540MB - CFS540A", 3 }, + + { "WDC AC2700", 3 }, + { "WDC AC2540", 3 }, + { "WDC AC2420", 3 }, + { "WDC AC2340", 3 }, + { "WDC AC2250", 0 }, + { "WDC AC2200", 0 }, + { "WDC AC21200", 4 }, + { "WDC AC2120", 0 }, + { "WDC AC2850", 3 }, + { "WDC AC1270", 3 }, + { "WDC AC1170", 1 }, + { "WDC AC1210", 1 }, + { "WDC AC280", 0 }, + { "WDC AC31000", 3 }, + { "WDC AC31200", 3 }, + + { "Maxtor 7131 AT", 1 }, + { "Maxtor 7171 AT", 1 }, + { "Maxtor 7213 AT", 1 }, + { "Maxtor 7245 AT", 1 }, + { "Maxtor 7345 AT", 1 }, + { "Maxtor 7546 AT", 3 }, + { "Maxtor 7540 AV", 3 }, + + { "SAMSUNG SHD-3121A", 1 }, + { "SAMSUNG SHD-3122A", 1 }, + { "SAMSUNG SHD-3172A", 1 }, + + { "ST5660A", 3 }, + { "ST3660A", 3 }, + { "ST3630A", 3 }, + { "ST3655A", 3 }, + { "ST3391A", 3 }, + { "ST3390A", 1 }, + { "ST3600A", 1 }, + { "ST3290A", 0 }, + { "ST3144A", 0 }, + { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on drive) + according to Seagate's FIND-ATA program */ + + { "QUANTUM ELS127A", 0 }, + { "QUANTUM ELS170A", 0 }, + { "QUANTUM LPS240A", 0 }, + { "QUANTUM LPS210A", 3 }, + { "QUANTUM LPS270A", 3 }, + { "QUANTUM LPS365A", 3 }, + { "QUANTUM LPS540A", 3 }, + { "QUANTUM LIGHTNING 540A", 3 }, + { "QUANTUM LIGHTNING 730A", 3 }, + + { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */ + { "QUANTUM FIREBALL_640", 3 }, + { "QUANTUM FIREBALL_1080", 3 }, + { "QUANTUM FIREBALL_1280", 3 }, + { NULL, 0 } +}; + +/** + * ide_scan_pio_blacklist - check for a blacklisted drive + * @model: Drive model string + * + * This routine searches the ide_pio_blacklist for an entry + * matching the start/whole of the supplied model name. + * + * Returns -1 if no match found. + * Otherwise returns the recommended PIO mode from ide_pio_blacklist[]. + */ + +int ide_scan_pio_blacklist(char *model) +{ + struct ide_pio_info *p; + + for (p = ide_pio_blacklist; p->name != NULL; p++) { + if (strncmp(p->name, model, strlen(p->name)) == 0) + return p->pio; + } + return -1; +} diff --git a/include/linux/ide.h b/include/linux/ide.h index 4e44525fa5ca..535c439fd8f0 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1305,6 +1305,8 @@ void ide_timing_merge(struct ide_timing *, struct ide_timing *, struct ide_timing *, unsigned int); int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int); +int ide_scan_pio_blacklist(char *); + u8 ide_get_best_pio_mode(ide_drive_t *, u8, u8); int ide_set_pio_mode(ide_drive_t *, u8); -- cgit v1.2.3 From 63b51c6d1d63276fd320615c042f1ff5d94ebab8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:40 +0200 Subject: ide: make ide_hwifs[] static Move ide_hwifs[] from ide.c to ide-probe.c and make it static. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 2 ++ drivers/ide/ide.c | 2 -- include/linux/ide.h | 12 ------------ 3 files changed, 2 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 592d424412ea..97dda05914a1 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -39,6 +39,8 @@ #include #include +static ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ + /** * generic_id - add a generic drive id * @drive: drive to make an ID block for diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 32d8ee281d56..b7855a13f0ef 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -91,8 +91,6 @@ DEFINE_MUTEX(ide_cfg_mtx); __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); EXPORT_SYMBOL(ide_lock); -ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ - static void ide_port_init_devices_data(ide_hwif_t *); /* diff --git a/include/linux/ide.h b/include/linux/ide.h index 535c439fd8f0..15d5668198a6 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -801,18 +801,6 @@ struct ide_driver_s { int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsigned, unsigned long); -/* - * ide_hwifs[] is the master data structure used to keep track - * of just about everything in ide.c. Whenever possible, routines - * should be using pointers to a drive (ide_drive_t *) or - * pointers to a hwif (ide_hwif_t *), rather than indexing this - * structure directly (the allocation/layout may change!). - * - */ -#ifndef _IDE_C -extern ide_hwif_t ide_hwifs[]; /* master data repository */ -#endif - extern int ide_vlb_clk; extern int ide_pci_clk; -- cgit v1.2.3 From c56c5648a3bd15ff14c50f284b261140cd5b5472 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:40 +0200 Subject: ide: set hwif->dev in ide_init_port_hw() (take 2) * Add 'parent' field to hw_regs_t for optional parent device pointer (needed by macio PMAC IDE controllers) and set hwif->dev in ide_init_port_hw(). * Update au1xxx-ide.c, sgiioc4.c, pmac.c and setup-pci.c accordingly. v2: * Update scc_pata.c. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide.c | 3 ++- drivers/ide/mips/au1xxx-ide.c | 2 -- drivers/ide/pci/scc_pata.c | 1 - drivers/ide/pci/sgiioc4.c | 2 -- drivers/ide/ppc/pmac.c | 6 ++---- drivers/ide/setup-pci.c | 2 -- include/linux/ide.h | 2 +- 7 files changed, 5 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index b7855a13f0ef..9240888e5efd 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -297,7 +297,8 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); hwif->irq = hw->irq; hwif->chipset = hw->chipset; - hwif->gendev.parent = hw->dev; + hwif->dev = hw->dev; + hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; hwif->ack_intr = hw->ack_intr; } EXPORT_SYMBOL_GPL(ide_init_port_hw); diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 1a6c27b32498..08c46c36b7e7 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -600,8 +600,6 @@ static int au_ide_probe(struct device *dev) ide_init_port_hw(hwif, &hw); - hwif->dev = dev; - /* If the user has selected DDMA assisted copies, then set up a few local I/O function entry points */ diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 1584ebb6a185..71bff3131b96 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -572,7 +572,6 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.dev = &dev->dev; hw.chipset = ide_pci; ide_init_port_hw(hwif, &hw); - hwif->dev = &dev->dev; idx[0] = hwif->index; diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 24513e3dcd6b..af91cc525789 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -625,8 +625,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) hw.dev = &dev->dev; ide_init_port_hw(hwif, &hw); - hwif->dev = &dev->dev; - /* The IOC4 uses MMIO rather than Port IO. */ default_hwif_mmiops(hwif); diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index cfa103a51b31..93fb9067c043 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1148,8 +1148,6 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) base = ioremap(macio_resource_start(mdev, 0), 0x400); regbase = (unsigned long) base; - hwif->dev = &mdev->bus->pdev->dev; - pmif->mdev = mdev; pmif->node = mdev->ofdev.node; pmif->regbase = regbase; @@ -1171,7 +1169,8 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) memset(&hw, 0, sizeof(hw)); pmac_ide_init_ports(&hw, pmif->regbase); hw.irq = irq; - hw.dev = &mdev->ofdev.dev; + hw.dev = &mdev->bus->pdev->dev; + hw.parent = &mdev->ofdev.dev; rc = pmac_ide_setup_device(pmif, hwif, &hw); if (rc != 0) { @@ -1271,7 +1270,6 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) goto out_free_pmif; } - hwif->dev = &pdev->dev; pmif->mdev = NULL; pmif->node = np; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index abcfb1739d4d..3dea5a59626e 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -346,8 +346,6 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, ide_init_port_hw(hwif, &hw); - hwif->dev = &dev->dev; - return hwif; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 15d5668198a6..a6a2eccb6526 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -171,7 +171,7 @@ typedef struct hw_regs_s { int irq; /* our irq number */ ide_ack_intr_t *ack_intr; /* acknowledge interrupt */ hwif_chipset_t chipset; - struct device *dev; + struct device *dev, *parent; } hw_regs_t; void ide_init_port_data(struct hwif_s *, unsigned int); -- cgit v1.2.3 From e6d95bd14928926d6658b5e4ace905e8b83ed27a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 16 Jul 2008 20:33:42 +0200 Subject: ide: ->port_init_devs -> ->init_dev Change ->port_init_devs method to take 'ide_drive_t *' as an argument instead of 'ide_hwif_t *' and rename it to ->init_dev. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 6 +++--- drivers/ide/legacy/ht6560b.c | 8 ++++---- drivers/ide/legacy/ide-4drives.c | 10 ++++------ drivers/ide/legacy/qd65xx.c | 16 ++++++++-------- include/linux/ide.h | 4 ++-- 5 files changed, 21 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 97dda05914a1..c5dc18849303 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1320,10 +1320,10 @@ static void ide_port_init_devices(ide_hwif_t *hwif) drive->unmask = 1; if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS) drive->no_unmask = 1; - } - if (port_ops && port_ops->port_init_devs) - port_ops->port_init_devs(hwif); + if (port_ops && port_ops->init_dev) + port_ops->init_dev(drive); + } } static void ide_init_port(ide_hwif_t *hwif, unsigned int port, diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index bd2f579946fa..7bc8fd59ea9e 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -310,16 +310,16 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio) #endif } -static void __init ht6560b_port_init_devs(ide_hwif_t *hwif) +static void __init ht6560b_init_dev(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; /* Setting default configurations for drives. */ int t = (HT_CONFIG_DEFAULT << 8) | HT_TIMING_DEFAULT; if (hwif->channel) t |= (HT_SECONDARY_IF << 8); - hwif->drives[0].drive_data = t; - hwif->drives[1].drive_data = t; + drive->drive_data = t; } static int probe_ht6560b; @@ -328,7 +328,7 @@ module_param_named(probe, probe_ht6560b, bool, 0); MODULE_PARM_DESC(probe, "probe for HT6560B chipset"); static const struct ide_port_ops ht6560b_port_ops = { - .port_init_devs = ht6560b_port_init_devs, + .init_dev = ht6560b_init_dev, .set_pio_mode = ht6560b_set_pio_mode, .selectproc = ht6560b_selectproc, }; diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 5cd6ce537eea..89c8ff0a4d08 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -11,16 +11,14 @@ static int probe_4drives; module_param_named(probe, probe_4drives, bool, 0); MODULE_PARM_DESC(probe, "probe for generic IDE chipset with 4 drives/port"); -static void ide_4drives_port_init_devs(ide_hwif_t *hwif) +static void ide_4drives_init_dev(ide_drive_t *drive) { - if (hwif->channel) { - hwif->drives[0].select.all ^= 0x20; - hwif->drives[1].select.all ^= 0x20; - } + if (drive->hwif->channel) + drive->select.all ^= 0x20; } static const struct ide_port_ops ide_4drives_port_ops = { - .port_init_devs = ide_4drives_port_init_devs, + .init_dev = ide_4drives_init_dev, }; static const struct ide_port_info ide_4drives_port_info = { diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 63f6c31d16ea..2338f344ea24 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -282,17 +282,18 @@ static int __init qd_testreg(int port) return (readreg != QD_TESTVAL); } -static void __init qd6500_port_init_devs(ide_hwif_t *hwif) +static void __init qd6500_init_dev(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; u8 base = (hwif->config_data & 0xff00) >> 8; u8 config = QD_CONFIG(hwif); - hwif->drives[0].drive_data = QD6500_DEF_DATA; - hwif->drives[1].drive_data = QD6500_DEF_DATA; + drive->drive_data = QD6500_DEF_DATA; } -static void __init qd6580_port_init_devs(ide_hwif_t *hwif) +static void __init qd6580_init_dev(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; u16 t1, t2; u8 base = (hwif->config_data & 0xff00) >> 8; u8 config = QD_CONFIG(hwif); @@ -303,18 +304,17 @@ static void __init qd6580_port_init_devs(ide_hwif_t *hwif) } else t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA; - hwif->drives[0].drive_data = t1; - hwif->drives[1].drive_data = t2; + drive->drive_data = drive->select.b.unit ? t2 : t1; } static const struct ide_port_ops qd6500_port_ops = { - .port_init_devs = qd6500_port_init_devs, + .init_dev = qd6500_init_dev, .set_pio_mode = qd6500_set_pio_mode, .selectproc = qd65xx_select, }; static const struct ide_port_ops qd6580_port_ops = { - .port_init_devs = qd6580_port_init_devs, + .init_dev = qd6580_init_dev, .set_pio_mode = qd6580_set_pio_mode, .selectproc = qd65xx_select, }; diff --git a/include/linux/ide.h b/include/linux/ide.h index a6a2eccb6526..f9cbe9350cad 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -405,8 +405,8 @@ typedef struct ide_drive_s { struct ide_port_info; struct ide_port_ops { - /* host specific initialization of devices on a port */ - void (*port_init_devs)(struct hwif_s *); + /* host specific initialization of a device */ + void (*init_dev)(ide_drive_t *); /* routine to program host for PIO mode */ void (*set_pio_mode)(ide_drive_t *, const u8); /* routine to program host for DMA mode */ -- cgit v1.2.3 From 79e36a9f54aaf4a52eb2d9520953aa3960e99294 Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Wed, 16 Jul 2008 20:33:48 +0200 Subject: IDE: Fix HDIO_DRIVE_RESET handling Currently, the code path executing an HDIO_DRIVE_RESET ioctl is broken in various ways. Most importantly, it is treated as an out of band request in an illegal way which may very likely lead to system lock ups. Use the drive's request queue to avoid this problem (and fix a locking issue for free along the way). Signed-off-by: Elias Oltmanns Cc: "Alan Cox" Cc: "Randy Dunlap" Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 23 ++++++++++++++++++++++- drivers/ide/ide-iops.c | 18 +++++++++++++----- drivers/ide/ide.c | 41 +++++++++++++++-------------------------- include/linux/ide.h | 6 ++++++ 4 files changed, 56 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 28057747c1f8..2b33c129740b 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -766,6 +766,18 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, return ide_stopped; } +static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) +{ + switch (rq->cmd[0]) { + case REQ_DRIVE_RESET: + return ide_do_reset(drive); + default: + blk_dump_rq_flags(rq, "ide_special_rq - bad request"); + ide_end_request(drive, 0, 0); + return ide_stopped; + } +} + static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) { struct request_pm_state *pm = rq->data; @@ -869,7 +881,16 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) pm->pm_step == ide_pm_state_completed) ide_complete_pm_request(drive, rq); return startstop; - } + } else if (!rq->rq_disk && blk_special_request(rq)) + /* + * TODO: Once all ULDs have been modified to + * check for specific op codes rather than + * blindly accepting any special request, the + * check for ->rq_disk above may be replaced + * by a more suitable mechanism or even + * dropped entirely. + */ + return ide_special_rq(drive, rq); drv = *(ide_driver_t **)rq->rq_disk->private_data; return drv->do_request(drive, rq, block); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 80ad4f234f3f..96f63eb12092 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -905,6 +905,14 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_execute_pkt_cmd); +static inline void ide_complete_drive_reset(ide_drive_t *drive) +{ + struct request *rq = drive->hwif->hwgroup->rq; + + if (rq && blk_special_request(rq) && rq->cmd[0] == REQ_DRIVE_RESET) + ide_end_request(drive, 1, 0); +} + /* needed below */ static ide_startstop_t do_reset1 (ide_drive_t *, int); @@ -940,7 +948,7 @@ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) } /* done polling */ hwgroup->polling = 0; - hwgroup->resetting = 0; + ide_complete_drive_reset(drive); return ide_stopped; } @@ -961,7 +969,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) if (port_ops->reset_poll(drive)) { printk(KERN_ERR "%s: host reset_poll failure for %s.\n", hwif->name, drive->name); - return ide_stopped; + goto out; } } @@ -1004,7 +1012,8 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } hwgroup->polling = 0; /* done polling */ - hwgroup->resetting = 0; /* done reset attempt */ +out: + ide_complete_drive_reset(drive); return ide_stopped; } @@ -1090,7 +1099,6 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) /* For an ATAPI device, first try an ATAPI SRST. */ if (drive->media != ide_disk && !do_not_try_atapi) { - hwgroup->resetting = 1; pre_reset(drive); SELECT_DRIVE(drive); udelay (20); @@ -1112,10 +1120,10 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) if (io_ports->ctl_addr == 0) { spin_unlock_irqrestore(&ide_lock, flags); + ide_complete_drive_reset(drive); return ide_stopped; } - hwgroup->resetting = 1; /* * Note that we also set nIEN while resetting the device, * to mask unwanted interrupts from the interface during the reset. diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 90ae00d4aaf5..1ec983b00511 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -529,6 +529,19 @@ static int generic_ide_resume(struct device *dev) return err; } +static void generic_drive_reset(ide_drive_t *drive) +{ + struct request *rq; + + rq = blk_get_request(drive->queue, READ, __GFP_WAIT); + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->cmd_len = 1; + rq->cmd[0] = REQ_DRIVE_RESET; + rq->cmd_flags |= REQ_SOFTBARRIER; + blk_execute_rq(drive->queue, NULL, rq, 1); + blk_put_request(rq); +} + int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device *bdev, unsigned int cmd, unsigned long arg) { @@ -603,33 +616,9 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device if (!capable(CAP_SYS_ADMIN)) return -EACCES; - /* - * Abort the current command on the - * group if there is one, taking - * care not to allow anything else - * to be queued and to die on the - * spot if we miss one somehow - */ - - spin_lock_irqsave(&ide_lock, flags); - - if (HWGROUP(drive)->resetting) { - spin_unlock_irqrestore(&ide_lock, flags); - return -EBUSY; - } - - ide_abort(drive, "drive reset"); - - BUG_ON(HWGROUP(drive)->handler); - - /* Ensure nothing gets queued after we - drop the lock. Reset will clear the busy */ - - HWGROUP(drive)->busy = 1; - spin_unlock_irqrestore(&ide_lock, flags); - (void) ide_do_reset(drive); - + generic_drive_reset(drive); return 0; + case HDIO_GET_BUSSTATE: if (!capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/include/linux/ide.h b/include/linux/ide.h index f9cbe9350cad..021710cc1b1c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -138,6 +138,12 @@ struct ide_io_ports { #define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */ #define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */ +/* + * Op codes for special requests to be handled by ide_special_rq(). + * Values should be in the range of 0x20 to 0x3f. + */ +#define REQ_DRIVE_RESET 0x20 + /* * Check for an interrupt and acknowledge the interrupt status */ -- cgit v1.2.3 From 3ef5eb424ebf0cd981192a416358fd707a9f959b Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Wed, 16 Jul 2008 20:33:48 +0200 Subject: IDE: Remove unused code Remove some code which has been made obsolete and hasn't worked properly before anyway. Part of the infrastructure may be reintroduced in a follow up patch to implement a working command aborting facility. Signed-off-by: Elias Oltmanns Cc: "Alan Cox" Cc: "Randy Dunlap" Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 1 - drivers/ide/ide-disk.c | 1 - drivers/ide/ide-floppy.c | 1 - drivers/ide/ide-io.c | 49 ------------------------------------------------ drivers/ide/ide-tape.c | 1 - drivers/scsi/ide-scsi.c | 14 -------------- include/linux/ide.h | 7 ------- 7 files changed, 74 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 2b3c69d1f53f..6e29dd532090 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1952,7 +1952,6 @@ static ide_driver_t ide_cdrom_driver = { .do_request = ide_cd_do_request, .end_request = ide_end_request, .error = __ide_error, - .abort = __ide_abort, #ifdef CONFIG_IDE_PROC_FS .proc = idecd_proc, #endif diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 5f49a4ae9dd8..3a2e80237c10 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -985,7 +985,6 @@ static ide_driver_t idedisk_driver = { .do_request = ide_do_rw_disk, .end_request = ide_end_request, .error = __ide_error, - .abort = __ide_abort, #ifdef CONFIG_IDE_PROC_FS .proc = idedisk_proc, #endif diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index c7e4433fcbcf..011d72011cc4 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -1129,7 +1129,6 @@ static ide_driver_t idefloppy_driver = { .do_request = idefloppy_do_request, .end_request = idefloppy_end_request, .error = __ide_error, - .abort = __ide_abort, #ifdef CONFIG_IDE_PROC_FS .proc = idefloppy_proc, #endif diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 2b33c129740b..661b75a89d4d 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -504,55 +504,6 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat) EXPORT_SYMBOL_GPL(ide_error); -ide_startstop_t __ide_abort(ide_drive_t *drive, struct request *rq) -{ - if (drive->media != ide_disk) - rq->errors |= ERROR_RESET; - - ide_kill_rq(drive, rq); - - return ide_stopped; -} - -EXPORT_SYMBOL_GPL(__ide_abort); - -/** - * ide_abort - abort pending IDE operations - * @drive: drive the error occurred on - * @msg: message to report - * - * ide_abort kills and cleans up when we are about to do a - * host initiated reset on active commands. Longer term we - * want handlers to have sensible abort handling themselves - * - * This differs fundamentally from ide_error because in - * this case the command is doing just fine when we - * blow it away. - */ - -ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg) -{ - struct request *rq; - - if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) - return ide_stopped; - - /* retry only "normal" I/O: */ - if (!blk_fs_request(rq)) { - rq->errors = 1; - ide_end_drive_cmd(drive, BUSY_STAT, 0); - return ide_stopped; - } - - if (rq->rq_disk) { - ide_driver_t *drv; - - drv = *(ide_driver_t **)rq->rq_disk->private_data; - return drv->abort(drive, rq); - } else - return __ide_abort(drive, rq); -} - static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) { tf->nsect = drive->sect; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index f9cf1670e4e1..b711ab96e287 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2591,7 +2591,6 @@ static ide_driver_t idetape_driver = { .do_request = idetape_do_request, .end_request = idetape_end_request, .error = __ide_error, - .abort = __ide_abort, #ifdef CONFIG_IDE_PROC_FS .proc = idetape_proc, #endif diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 683bce375c74..f843c1383a4b 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -258,19 +258,6 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) return ide_stopped; } -static ide_startstop_t -idescsi_atapi_abort(ide_drive_t *drive, struct request *rq) -{ - debug_log("%s called for %lu\n", __func__, - ((struct ide_atapi_pc *) rq->special)->scsi_cmd->serial_number); - - rq->errors |= ERROR_MAX; - - idescsi_end_request(drive, 0, 0); - - return ide_stopped; -} - static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) { idescsi_scsi_t *scsi = drive_to_idescsi(drive); @@ -524,7 +511,6 @@ static ide_driver_t idescsi_driver = { .do_request = idescsi_do_request, .end_request = idescsi_end_request, .error = idescsi_atapi_error, - .abort = idescsi_atapi_abort, #ifdef CONFIG_IDE_PROC_FS .proc = idescsi_proc, #endif diff --git a/include/linux/ide.h b/include/linux/ide.h index 021710cc1b1c..4726126f5a59 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -571,8 +571,6 @@ typedef struct hwgroup_s { unsigned int sleeping : 1; /* BOOL: polling active & poll_timeout field valid */ unsigned int polling : 1; - /* BOOL: in a polling reset situation. Must not trigger another reset yet */ - unsigned int resetting : 1; /* current drive */ ide_drive_t *drive; @@ -792,7 +790,6 @@ struct ide_driver_s { ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t); int (*end_request)(ide_drive_t *, int, int); ide_startstop_t (*error)(ide_drive_t *, struct request *rq, u8, u8); - ide_startstop_t (*abort)(ide_drive_t *, struct request *rq); struct device_driver gen_driver; int (*probe)(ide_drive_t *); void (*remove)(ide_drive_t *); @@ -834,10 +831,6 @@ ide_startstop_t __ide_error(ide_drive_t *, struct request *, u8, u8); ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat); -ide_startstop_t __ide_abort(ide_drive_t *, struct request *); - -extern ide_startstop_t ide_abort(ide_drive_t *, const char *); - extern void ide_fix_driveid(struct hd_driveid *); extern void ide_fixstring(u8 *, const int, const int); -- cgit v1.2.3 From ebb12db51f6c13b30752fcf506baad4c617b153c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 11 Jun 2008 22:04:29 +0200 Subject: Freezer: Introduce PF_FREEZER_NOSIG The freezer currently attempts to distinguish kernel threads from user space tasks by checking if their mm pointer is unset and it does not send fake signals to kernel threads. However, there are kernel threads, mostly related to networking, that behave like user space tasks and may want to be sent a fake signal to be frozen. Introduce the new process flag PF_FREEZER_NOSIG that will be set by default for all kernel threads and make the freezer only send fake signals to the tasks having PF_FREEZER_NOSIG unset. Provide the set_freezable_with_signal() function to be called by the kernel threads that want to be sent a fake signal for freezing. This patch should not change the freezer's observable behavior. Signed-off-by: Rafael J. Wysocki Signed-off-by: Andi Kleen Acked-by: Pavel Machek Signed-off-by: Len Brown --- include/linux/freezer.h | 10 +++++ include/linux/sched.h | 1 + kernel/kthread.c | 2 +- kernel/power/process.c | 97 +++++++++++++++++++++---------------------------- 4 files changed, 54 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 08934995c7ab..deddeedf3257 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -127,6 +127,15 @@ static inline void set_freezable(void) current->flags &= ~PF_NOFREEZE; } +/* + * Tell the freezer that the current task should be frozen by it and that it + * should send a fake signal to the task to freeze it. + */ +static inline void set_freezable_with_signal(void) +{ + current->flags &= ~(PF_NOFREEZE | PF_FREEZER_NOSIG); +} + /* * Freezer-friendly wrappers around wait_event_interruptible() and * wait_event_interruptible_timeout(), originally defined in @@ -174,6 +183,7 @@ static inline void freezer_do_not_count(void) {} static inline void freezer_count(void) {} static inline int freezer_should_skip(struct task_struct *p) { return 0; } static inline void set_freezable(void) {} +static inline void set_freezable_with_signal(void) {} #define wait_event_freezable(wq, condition) \ wait_event_interruptible(wq, condition) diff --git a/include/linux/sched.h b/include/linux/sched.h index 21349173d148..ba2f859c6e4f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1494,6 +1494,7 @@ static inline void put_task_struct(struct task_struct *t) #define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ #define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ #define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */ +#define PF_FREEZER_NOSIG 0x80000000 /* Freezer won't send signals to it */ /* * Only the _current_ task can read/write to tsk->flags, but other diff --git a/kernel/kthread.c b/kernel/kthread.c index 97747cdd37c9..ac3fb7326641 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -235,7 +235,7 @@ int kthreadd(void *unused) set_user_nice(tsk, KTHREAD_NICE_LEVEL); set_cpus_allowed(tsk, CPU_MASK_ALL); - current->flags |= PF_NOFREEZE; + current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG; for (;;) { set_current_state(TASK_INTERRUPTIBLE); diff --git a/kernel/power/process.c b/kernel/power/process.c index f1d0b345c9ba..5fb87652f214 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -19,9 +19,6 @@ */ #define TIMEOUT (20 * HZ) -#define FREEZER_KERNEL_THREADS 0 -#define FREEZER_USER_SPACE 1 - static inline int freezeable(struct task_struct * p) { if ((p == current) || @@ -84,63 +81,53 @@ static void fake_signal_wake_up(struct task_struct *p) spin_unlock_irqrestore(&p->sighand->siglock, flags); } -static int has_mm(struct task_struct *p) +static inline bool should_send_signal(struct task_struct *p) { - return (p->mm && !(p->flags & PF_BORROWED_MM)); + return !(p->flags & PF_FREEZER_NOSIG); } /** * freeze_task - send a freeze request to given task * @p: task to send the request to - * @with_mm_only: if set, the request will only be sent if the task has its - * own mm - * Return value: 0, if @with_mm_only is set and the task has no mm of its - * own or the task is frozen, 1, otherwise + * @sig_only: if set, the request will only be sent if the task has the + * PF_FREEZER_NOSIG flag unset + * Return value: 'false', if @sig_only is set and the task has + * PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise * - * The freeze request is sent by seting the tasks's TIF_FREEZE flag and + * The freeze request is sent by setting the tasks's TIF_FREEZE flag and * either sending a fake signal to it or waking it up, depending on whether - * or not it has its own mm (ie. it is a user land task). If @with_mm_only - * is set and the task has no mm of its own (ie. it is a kernel thread), - * its TIF_FREEZE flag should not be set. - * - * The task_lock() is necessary to prevent races with exit_mm() or - * use_mm()/unuse_mm() from occuring. + * or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task + * has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its + * TIF_FREEZE flag will not be set. */ -static int freeze_task(struct task_struct *p, int with_mm_only) +static bool freeze_task(struct task_struct *p, bool sig_only) { - int ret = 1; + /* + * We first check if the task is freezing and next if it has already + * been frozen to avoid the race with frozen_process() which first marks + * the task as frozen and next clears its TIF_FREEZE. + */ + if (!freezing(p)) { + rmb(); + if (frozen(p)) + return false; - task_lock(p); - if (freezing(p)) { - if (has_mm(p)) { - if (!signal_pending(p)) - fake_signal_wake_up(p); - } else { - if (with_mm_only) - ret = 0; - else - wake_up_state(p, TASK_INTERRUPTIBLE); - } + if (!sig_only || should_send_signal(p)) + set_freeze_flag(p); + else + return false; + } + + if (should_send_signal(p)) { + if (!signal_pending(p)) + fake_signal_wake_up(p); + } else if (sig_only) { + return false; } else { - rmb(); - if (frozen(p)) { - ret = 0; - } else { - if (has_mm(p)) { - set_freeze_flag(p); - fake_signal_wake_up(p); - } else { - if (with_mm_only) { - ret = 0; - } else { - set_freeze_flag(p); - wake_up_state(p, TASK_INTERRUPTIBLE); - } - } - } + wake_up_state(p, TASK_INTERRUPTIBLE); } - task_unlock(p); - return ret; + + return true; } static void cancel_freezing(struct task_struct *p) @@ -156,7 +143,7 @@ static void cancel_freezing(struct task_struct *p) } } -static int try_to_freeze_tasks(int freeze_user_space) +static int try_to_freeze_tasks(bool sig_only) { struct task_struct *g, *p; unsigned long end_time; @@ -175,7 +162,7 @@ static int try_to_freeze_tasks(int freeze_user_space) if (frozen(p) || !freezeable(p)) continue; - if (!freeze_task(p, freeze_user_space)) + if (!freeze_task(p, sig_only)) continue; /* @@ -235,13 +222,13 @@ int freeze_processes(void) int error; printk("Freezing user space processes ... "); - error = try_to_freeze_tasks(FREEZER_USER_SPACE); + error = try_to_freeze_tasks(true); if (error) goto Exit; printk("done.\n"); printk("Freezing remaining freezable tasks ... "); - error = try_to_freeze_tasks(FREEZER_KERNEL_THREADS); + error = try_to_freeze_tasks(false); if (error) goto Exit; printk("done."); @@ -251,7 +238,7 @@ int freeze_processes(void) return error; } -static void thaw_tasks(int thaw_user_space) +static void thaw_tasks(bool nosig_only) { struct task_struct *g, *p; @@ -260,7 +247,7 @@ static void thaw_tasks(int thaw_user_space) if (!freezeable(p)) continue; - if (!p->mm == thaw_user_space) + if (nosig_only && should_send_signal(p)) continue; thaw_process(p); @@ -271,8 +258,8 @@ static void thaw_tasks(int thaw_user_space) void thaw_processes(void) { printk("Restarting tasks ... "); - thaw_tasks(FREEZER_KERNEL_THREADS); - thaw_tasks(FREEZER_USER_SPACE); + thaw_tasks(true); + thaw_tasks(false); schedule(); printk("done.\n"); } -- cgit v1.2.3 From 20bfdbba7212d19613b93dcea93f26cb65af91fe Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:56:56 -0600 Subject: PNP: make pnp_{port,mem,etc}_start(), et al work for invalid resources Some callers use pnp_port_start() and similar functions without making sure the resource is valid. This patch makes us fall back to returning the initial values if the resource is not valid or not even present. This mostly preserves the previous behavior, where we would just return the initial values set by pnp_init_resource_table(). The original 2.6.25 code didn't range-check the "bar", so it would return garbage if the bar exceeded the table size. This code returns sensible values instead. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown Signed-off-by: Andi Kleen --- include/linux/pnp.h | 72 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 63b128d512fb..8b607aecd959 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -40,19 +40,31 @@ static inline resource_size_t pnp_resource_len(struct resource *res) static inline resource_size_t pnp_port_start(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_IO, bar)->start; + struct resource *res = pnp_get_resource(dev, IORESOURCE_IO, bar); + + if (pnp_resource_valid(res)) + return res->start; + return 0; } static inline resource_size_t pnp_port_end(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_IO, bar)->end; + struct resource *res = pnp_get_resource(dev, IORESOURCE_IO, bar); + + if (pnp_resource_valid(res)) + return res->end; + return 0; } static inline unsigned long pnp_port_flags(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_IO, bar)->flags; + struct resource *res = pnp_get_resource(dev, IORESOURCE_IO, bar); + + if (pnp_resource_valid(res)) + return res->flags; + return IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET; } static inline int pnp_port_valid(struct pnp_dev *dev, unsigned int bar) @@ -63,25 +75,41 @@ static inline int pnp_port_valid(struct pnp_dev *dev, unsigned int bar) static inline resource_size_t pnp_port_len(struct pnp_dev *dev, unsigned int bar) { - return pnp_resource_len(pnp_get_resource(dev, IORESOURCE_IO, bar)); + struct resource *res = pnp_get_resource(dev, IORESOURCE_IO, bar); + + if (pnp_resource_valid(res)) + return pnp_resource_len(res); + return 0; } static inline resource_size_t pnp_mem_start(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_MEM, bar)->start; + struct resource *res = pnp_get_resource(dev, IORESOURCE_MEM, bar); + + if (pnp_resource_valid(res)) + return res->start; + return 0; } static inline resource_size_t pnp_mem_end(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_MEM, bar)->end; + struct resource *res = pnp_get_resource(dev, IORESOURCE_MEM, bar); + + if (pnp_resource_valid(res)) + return res->end; + return 0; } static inline unsigned long pnp_mem_flags(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_MEM, bar)->flags; + struct resource *res = pnp_get_resource(dev, IORESOURCE_MEM, bar); + + if (pnp_resource_valid(res)) + return res->flags; + return IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET; } static inline int pnp_mem_valid(struct pnp_dev *dev, unsigned int bar) @@ -92,18 +120,30 @@ static inline int pnp_mem_valid(struct pnp_dev *dev, unsigned int bar) static inline resource_size_t pnp_mem_len(struct pnp_dev *dev, unsigned int bar) { - return pnp_resource_len(pnp_get_resource(dev, IORESOURCE_MEM, bar)); + struct resource *res = pnp_get_resource(dev, IORESOURCE_MEM, bar); + + if (pnp_resource_valid(res)) + return pnp_resource_len(res); + return 0; } static inline resource_size_t pnp_irq(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_IRQ, bar)->start; + struct resource *res = pnp_get_resource(dev, IORESOURCE_IRQ, bar); + + if (pnp_resource_valid(res)) + return res->start; + return -1; } static inline unsigned long pnp_irq_flags(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_IRQ, bar)->flags; + struct resource *res = pnp_get_resource(dev, IORESOURCE_IRQ, bar); + + if (pnp_resource_valid(res)) + return res->flags; + return IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET; } static inline int pnp_irq_valid(struct pnp_dev *dev, unsigned int bar) @@ -114,12 +154,20 @@ static inline int pnp_irq_valid(struct pnp_dev *dev, unsigned int bar) static inline resource_size_t pnp_dma(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_DMA, bar)->start; + struct resource *res = pnp_get_resource(dev, IORESOURCE_DMA, bar); + + if (pnp_resource_valid(res)) + return res->start; + return -1; } static inline unsigned long pnp_dma_flags(struct pnp_dev *dev, unsigned int bar) { - return pnp_get_resource(dev, IORESOURCE_DMA, bar)->flags; + struct resource *res = pnp_get_resource(dev, IORESOURCE_DMA, bar); + + if (pnp_resource_valid(res)) + return res->flags; + return IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET; } static inline int pnp_dma_valid(struct pnp_dev *dev, unsigned int bar) -- cgit v1.2.3 From aee3ad815dd291a7193ab01da0f1a30c84d00061 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:56:57 -0600 Subject: PNP: replace pnp_resource_table with dynamically allocated resources PNP used to have a fixed-size pnp_resource_table for tracking the resources used by a device. This table often overflowed, so we've had to increase the table size, which wastes memory because most devices have very few resources. This patch replaces the table with a linked list of resources where the entries are allocated on demand. This removes messages like these: pnpacpi: exceeded the max number of IO resources 00:01: too many I/O port resources References: http://bugzilla.kernel.org/show_bug.cgi?id=9535 http://bugzilla.kernel.org/show_bug.cgi?id=9740 http://lkml.org/lkml/2007/11/30/110 This patch also changes the way PNP uses the IORESOURCE_UNSET, IORESOURCE_AUTO, and IORESOURCE_DISABLED flags. Prior to this patch, the pnp_resource_table entries used the flags like this: IORESOURCE_UNSET This table entry is unused and available for use. When this flag is set, we shouldn't look at anything else in the resource structure. This flag is set when a resource table entry is initialized. IORESOURCE_AUTO This resource was assigned automatically by pnp_assign_{io,mem,etc}(). This flag is set when a resource table entry is initialized and cleared whenever we discover a resource setting by reading an ISAPNP config register, parsing a PNPBIOS resource data stream, parsing an ACPI _CRS list, or interpreting a sysfs "set" command. Resources marked IORESOURCE_AUTO are reinitialized and marked as IORESOURCE_UNSET by pnp_clean_resource_table() in these cases: - before we attempt to assign resources automatically, - if we fail to assign resources automatically, - after disabling a device IORESOURCE_DISABLED Set by pnp_assign_{io,mem,etc}() when automatic assignment fails. Also set by PNPBIOS and PNPACPI for: - invalid IRQs or GSI registration failures - invalid DMA channels - I/O ports above 0x10000 - mem ranges with negative length After this patch, there is no pnp_resource_table, and the resource list entries use the flags like this: IORESOURCE_UNSET This flag is no longer used in PNP. Instead of keeping IORESOURCE_UNSET entries in the resource list, we remove entries from the list and free them. IORESOURCE_AUTO No change in meaning: it still means the resource was assigned automatically by pnp_assign_{port,mem,etc}(), but these functions now set the bit explicitly. We still "clean" a device's resource list in the same places, but rather than reinitializing IORESOURCE_AUTO entries, we just remove them from the list. Note that IORESOURCE_AUTO entries are always at the end of the list, so removing them doesn't reorder other list entries. This is because non-IORESOURCE_AUTO entries are added by the ISAPNP, PNPBIOS, or PNPACPI "get resources" methods and by the sysfs "set" command. In each of these cases, we completely free the resource list first. IORESOURCE_DISABLED In addition to the cases where we used to set this flag, ISAPNP now adds an IORESOURCE_DISABLED resource when it reads a configuration register with a "disabled" value. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown Signed-off-by: Andi Kleen --- drivers/pnp/base.h | 18 +--- drivers/pnp/core.c | 25 +++-- drivers/pnp/interface.c | 60 +++++------ drivers/pnp/isapnp/core.c | 12 +-- drivers/pnp/manager.c | 220 +++++++++++------------------------------ drivers/pnp/pnpacpi/rsparser.c | 131 ++++++++++++++++-------- drivers/pnp/pnpbios/rsparser.c | 95 +++++++++++++----- drivers/pnp/quirks.c | 3 +- drivers/pnp/resource.c | 86 ++++------------ drivers/pnp/support.c | 79 +++++++-------- drivers/pnp/system.c | 4 +- include/linux/pnp.h | 20 ++-- 12 files changed, 331 insertions(+), 422 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index c91315826da2..1667ac3ca45b 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -46,27 +46,15 @@ int pnp_check_dma(struct pnp_dev *dev, struct resource *res); char *pnp_resource_type_name(struct resource *res); void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc); -void pnp_init_resource(struct resource *res); +void pnp_free_resources(struct pnp_dev *dev); int pnp_resource_type(struct resource *res); -struct pnp_resource *pnp_get_pnp_resource(struct pnp_dev *dev, - unsigned int type, unsigned int num); - -#define PNP_MAX_PORT 40 -#define PNP_MAX_MEM 24 -#define PNP_MAX_IRQ 2 -#define PNP_MAX_DMA 2 - struct pnp_resource { + struct list_head list; struct resource res; }; -struct pnp_resource_table { - struct pnp_resource port[PNP_MAX_PORT]; - struct pnp_resource mem[PNP_MAX_MEM]; - struct pnp_resource dma[PNP_MAX_DMA]; - struct pnp_resource irq[PNP_MAX_IRQ]; -}; +void pnp_free_resource(struct pnp_resource *pnp_res); struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, int flags); diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 20771b7d4482..7182da92aec3 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -99,6 +99,21 @@ static void pnp_free_ids(struct pnp_dev *dev) } } +void pnp_free_resource(struct pnp_resource *pnp_res) +{ + list_del(&pnp_res->list); + kfree(pnp_res); +} + +void pnp_free_resources(struct pnp_dev *dev) +{ + struct pnp_resource *pnp_res, *tmp; + + list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) { + pnp_free_resource(pnp_res); + } +} + static void pnp_release_device(struct device *dmdev) { struct pnp_dev *dev = to_pnp_dev(dmdev); @@ -106,7 +121,7 @@ static void pnp_release_device(struct device *dmdev) pnp_free_option(dev->independent); pnp_free_option(dev->dependent); pnp_free_ids(dev); - kfree(dev->res); + pnp_free_resources(dev); kfree(dev); } @@ -119,12 +134,7 @@ struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid if (!dev) return NULL; - dev->res = kzalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); - if (!dev->res) { - kfree(dev); - return NULL; - } - + INIT_LIST_HEAD(&dev->resources); dev->protocol = protocol; dev->number = id; dev->dma_mask = DMA_24BIT_MASK; @@ -140,7 +150,6 @@ struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid dev_id = pnp_add_id(dev, pnpid); if (!dev_id) { - kfree(dev->res); kfree(dev); return NULL; } diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 3f8007ab94e3..7fc86bbed88e 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -269,46 +269,38 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, pnp_printf(buffer, "disabled\n"); for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { - if (pnp_resource_valid(res)) { - pnp_printf(buffer, "io"); - if (res->flags & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " 0x%llx-0x%llx\n", - (unsigned long long) res->start, - (unsigned long long) res->end); - } + pnp_printf(buffer, "io"); + if (res->flags & IORESOURCE_DISABLED) + pnp_printf(buffer, " disabled\n"); + else + pnp_printf(buffer, " 0x%llx-0x%llx\n", + (unsigned long long) res->start, + (unsigned long long) res->end); } for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { - if (pnp_resource_valid(res)) { - pnp_printf(buffer, "mem"); - if (res->flags & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " 0x%llx-0x%llx\n", - (unsigned long long) res->start, - (unsigned long long) res->end); - } + pnp_printf(buffer, "mem"); + if (res->flags & IORESOURCE_DISABLED) + pnp_printf(buffer, " disabled\n"); + else + pnp_printf(buffer, " 0x%llx-0x%llx\n", + (unsigned long long) res->start, + (unsigned long long) res->end); } for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) { - if (pnp_resource_valid(res)) { - pnp_printf(buffer, "irq"); - if (res->flags & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " %lld\n", - (unsigned long long) res->start); - } + pnp_printf(buffer, "irq"); + if (res->flags & IORESOURCE_DISABLED) + pnp_printf(buffer, " disabled\n"); + else + pnp_printf(buffer, " %lld\n", + (unsigned long long) res->start); } for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) { - if (pnp_resource_valid(res)) { - pnp_printf(buffer, "dma"); - if (res->flags & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " %lld\n", - (unsigned long long) res->start); - } + pnp_printf(buffer, "dma"); + if (res->flags & IORESOURCE_DISABLED) + pnp_printf(buffer, " disabled\n"); + else + pnp_printf(buffer, " %lld\n", + (unsigned long long) res->start); } ret = (buffer->curr - buf); kfree(buffer); diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index 752b51fbaa6c..ca4457ec403b 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -973,8 +973,7 @@ static int isapnp_set_resources(struct pnp_dev *dev) dev->active = 1; for (tmp = 0; tmp < ISAPNP_MAX_PORT; tmp++) { res = pnp_get_resource(dev, IORESOURCE_IO, tmp); - if (res && pnp_resource_valid(res) && - !(res->flags & IORESOURCE_DISABLED)) { + if (pnp_resource_enabled(res)) { dev_dbg(&dev->dev, " set io %d to %#llx\n", tmp, (unsigned long long) res->start); isapnp_write_word(ISAPNP_CFG_PORT + (tmp << 1), @@ -983,8 +982,7 @@ static int isapnp_set_resources(struct pnp_dev *dev) } for (tmp = 0; tmp < ISAPNP_MAX_IRQ; tmp++) { res = pnp_get_resource(dev, IORESOURCE_IRQ, tmp); - if (res && pnp_resource_valid(res) && - !(res->flags & IORESOURCE_DISABLED)) { + if (pnp_resource_enabled(res)) { int irq = res->start; if (irq == 2) irq = 9; @@ -994,8 +992,7 @@ static int isapnp_set_resources(struct pnp_dev *dev) } for (tmp = 0; tmp < ISAPNP_MAX_DMA; tmp++) { res = pnp_get_resource(dev, IORESOURCE_DMA, tmp); - if (res && pnp_resource_valid(res) && - !(res->flags & IORESOURCE_DISABLED)) { + if (pnp_resource_enabled(res)) { dev_dbg(&dev->dev, " set dma %d to %lld\n", tmp, (unsigned long long) res->start); isapnp_write_byte(ISAPNP_CFG_DMA + tmp, res->start); @@ -1003,8 +1000,7 @@ static int isapnp_set_resources(struct pnp_dev *dev) } for (tmp = 0; tmp < ISAPNP_MAX_MEM; tmp++) { res = pnp_get_resource(dev, IORESOURCE_MEM, tmp); - if (res && pnp_resource_valid(res) && - !(res->flags & IORESOURCE_DISABLED)) { + if (pnp_resource_enabled(res)) { dev_dbg(&dev->dev, " set mem %d to %#llx\n", tmp, (unsigned long long) res->start); isapnp_write_word(ISAPNP_CFG_MEM + (tmp << 3), diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 90bd9cb65563..165b624081ad 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -19,40 +19,30 @@ DEFINE_MUTEX(pnp_res_mutex); static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) { - struct pnp_resource *pnp_res; - struct resource *res; + struct resource *res, local_res; - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_IO, idx); - if (!pnp_res) { - dev_err(&dev->dev, "too many I/O port resources\n"); - /* pretend we were successful so at least the manager won't try again */ - return 1; - } - - res = &pnp_res->res; - - /* check if this resource has been manually set, if so skip */ - if (!(res->flags & IORESOURCE_AUTO)) { + res = pnp_get_resource(dev, IORESOURCE_IO, idx); + if (res) { dev_dbg(&dev->dev, " io %d already set to %#llx-%#llx " "flags %#lx\n", idx, (unsigned long long) res->start, (unsigned long long) res->end, res->flags); return 1; } - /* set the initial values */ - res->flags |= rule->flags | IORESOURCE_IO; - res->flags &= ~IORESOURCE_UNSET; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = 0; + res->end = 0; if (!rule->size) { res->flags |= IORESOURCE_DISABLED; dev_dbg(&dev->dev, " io %d disabled\n", idx); - return 1; /* skip disabled resource requests */ + goto __add; } res->start = rule->min; res->end = res->start + rule->size - 1; - /* run through until pnp_check_port is happy */ while (!pnp_check_port(dev, res)) { res->start += rule->align; res->end = res->start + rule->size - 1; @@ -61,38 +51,29 @@ static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) return 0; } } - dev_dbg(&dev->dev, " assign io %d %#llx-%#llx\n", idx, - (unsigned long long) res->start, (unsigned long long) res->end); + +__add: + pnp_add_io_resource(dev, res->start, res->end, res->flags); return 1; } static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx) { - struct pnp_resource *pnp_res; - struct resource *res; + struct resource *res, local_res; - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_MEM, idx); - if (!pnp_res) { - dev_err(&dev->dev, "too many memory resources\n"); - /* pretend we were successful so at least the manager won't try again */ - return 1; - } - - res = &pnp_res->res; - - /* check if this resource has been manually set, if so skip */ - if (!(res->flags & IORESOURCE_AUTO)) { + res = pnp_get_resource(dev, IORESOURCE_MEM, idx); + if (res) { dev_dbg(&dev->dev, " mem %d already set to %#llx-%#llx " "flags %#lx\n", idx, (unsigned long long) res->start, (unsigned long long) res->end, res->flags); return 1; } - /* set the initial values */ - res->flags |= rule->flags | IORESOURCE_MEM; - res->flags &= ~IORESOURCE_UNSET; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = 0; + res->end = 0; - /* convert pnp flags to standard Linux flags */ if (!(rule->flags & IORESOURCE_MEM_WRITEABLE)) res->flags |= IORESOURCE_READONLY; if (rule->flags & IORESOURCE_MEM_CACHEABLE) @@ -105,13 +86,12 @@ static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx) if (!rule->size) { res->flags |= IORESOURCE_DISABLED; dev_dbg(&dev->dev, " mem %d disabled\n", idx); - return 1; /* skip disabled resource requests */ + goto __add; } res->start = rule->min; res->end = res->start + rule->size - 1; - /* run through until pnp_check_mem is happy */ while (!pnp_check_mem(dev, res)) { res->start += rule->align; res->end = res->start + rule->size - 1; @@ -120,15 +100,15 @@ static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx) return 0; } } - dev_dbg(&dev->dev, " assign mem %d %#llx-%#llx\n", idx, - (unsigned long long) res->start, (unsigned long long) res->end); + +__add: + pnp_add_mem_resource(dev, res->start, res->end, res->flags); return 1; } static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) { - struct pnp_resource *pnp_res; - struct resource *res; + struct resource *res, local_res; int i; /* IRQ priority: this table is good for i386 */ @@ -136,58 +116,48 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2 }; - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_IRQ, idx); - if (!pnp_res) { - dev_err(&dev->dev, "too many IRQ resources\n"); - /* pretend we were successful so at least the manager won't try again */ - return 1; - } - - res = &pnp_res->res; - - /* check if this resource has been manually set, if so skip */ - if (!(res->flags & IORESOURCE_AUTO)) { + res = pnp_get_resource(dev, IORESOURCE_IRQ, idx); + if (res) { dev_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n", idx, (int) res->start, res->flags); return 1; } - /* set the initial values */ - res->flags |= rule->flags | IORESOURCE_IRQ; - res->flags &= ~IORESOURCE_UNSET; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = -1; + res->end = -1; if (bitmap_empty(rule->map, PNP_IRQ_NR)) { res->flags |= IORESOURCE_DISABLED; dev_dbg(&dev->dev, " irq %d disabled\n", idx); - return 1; /* skip disabled resource requests */ + goto __add; } /* TBD: need check for >16 IRQ */ res->start = find_next_bit(rule->map, PNP_IRQ_NR, 16); if (res->start < PNP_IRQ_NR) { res->end = res->start; - dev_dbg(&dev->dev, " assign irq %d %d\n", idx, - (int) res->start); - return 1; + goto __add; } for (i = 0; i < 16; i++) { if (test_bit(xtab[i], rule->map)) { res->start = res->end = xtab[i]; - if (pnp_check_irq(dev, res)) { - dev_dbg(&dev->dev, " assign irq %d %d\n", idx, - (int) res->start); - return 1; - } + if (pnp_check_irq(dev, res)) + goto __add; } } dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx); return 0; + +__add: + pnp_add_irq_resource(dev, res->start, res->flags); + return 1; } static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) { - struct pnp_resource *pnp_res; - struct resource *res; + struct resource *res, local_res; int i; /* DMA priority: this table is good for i386 */ @@ -195,127 +165,47 @@ static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) 1, 3, 5, 6, 7, 0, 2, 4 }; - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_DMA, idx); - if (!pnp_res) { - dev_err(&dev->dev, "too many DMA resources\n"); - return; - } - - res = &pnp_res->res; - - /* check if this resource has been manually set, if so skip */ - if (!(res->flags & IORESOURCE_AUTO)) { + res = pnp_get_resource(dev, IORESOURCE_DMA, idx); + if (res) { dev_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n", idx, (int) res->start, res->flags); return; } - /* set the initial values */ - res->flags |= rule->flags | IORESOURCE_DMA; - res->flags &= ~IORESOURCE_UNSET; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = -1; + res->end = -1; for (i = 0; i < 8; i++) { if (rule->map & (1 << xtab[i])) { res->start = res->end = xtab[i]; - if (pnp_check_dma(dev, res)) { - dev_dbg(&dev->dev, " assign dma %d %d\n", idx, - (int) res->start); - return; - } + if (pnp_check_dma(dev, res)) + goto __add; } } #ifdef MAX_DMA_CHANNELS res->start = res->end = MAX_DMA_CHANNELS; #endif - res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; + res->flags |= IORESOURCE_DISABLED; dev_dbg(&dev->dev, " disable dma %d\n", idx); -} -void pnp_init_resource(struct resource *res) -{ - unsigned long type; - - type = res->flags & (IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_IRQ | IORESOURCE_DMA); - - res->name = NULL; - res->flags = type | IORESOURCE_AUTO | IORESOURCE_UNSET; - if (type == IORESOURCE_IRQ || type == IORESOURCE_DMA) { - res->start = -1; - res->end = -1; - } else { - res->start = 0; - res->end = 0; - } +__add: + pnp_add_dma_resource(dev, res->start, res->flags); } -/** - * pnp_init_resources - Resets a resource table to default values. - * @table: pointer to the desired resource table - */ void pnp_init_resources(struct pnp_dev *dev) { - struct resource *res; - int idx; - - for (idx = 0; idx < PNP_MAX_IRQ; idx++) { - res = &dev->res->irq[idx].res; - res->flags = IORESOURCE_IRQ; - pnp_init_resource(res); - } - for (idx = 0; idx < PNP_MAX_DMA; idx++) { - res = &dev->res->dma[idx].res; - res->flags = IORESOURCE_DMA; - pnp_init_resource(res); - } - for (idx = 0; idx < PNP_MAX_PORT; idx++) { - res = &dev->res->port[idx].res; - res->flags = IORESOURCE_IO; - pnp_init_resource(res); - } - for (idx = 0; idx < PNP_MAX_MEM; idx++) { - res = &dev->res->mem[idx].res; - res->flags = IORESOURCE_MEM; - pnp_init_resource(res); - } + pnp_free_resources(dev); } -/** - * pnp_clean_resources - clears resources that were not manually set - * @res: the resources to clean - */ static void pnp_clean_resource_table(struct pnp_dev *dev) { - struct resource *res; - int idx; - - for (idx = 0; idx < PNP_MAX_IRQ; idx++) { - res = &dev->res->irq[idx].res; - if (res->flags & IORESOURCE_AUTO) { - res->flags = IORESOURCE_IRQ; - pnp_init_resource(res); - } - } - for (idx = 0; idx < PNP_MAX_DMA; idx++) { - res = &dev->res->dma[idx].res; - if (res->flags & IORESOURCE_AUTO) { - res->flags = IORESOURCE_DMA; - pnp_init_resource(res); - } - } - for (idx = 0; idx < PNP_MAX_PORT; idx++) { - res = &dev->res->port[idx].res; - if (res->flags & IORESOURCE_AUTO) { - res->flags = IORESOURCE_IO; - pnp_init_resource(res); - } - } - for (idx = 0; idx < PNP_MAX_MEM; idx++) { - res = &dev->res->mem[idx].res; - if (res->flags & IORESOURCE_AUTO) { - res->flags = IORESOURCE_MEM; - pnp_init_resource(res); - } + struct pnp_resource *pnp_res, *tmp; + + list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) { + if (pnp_res->res.flags & IORESOURCE_AUTO) + pnp_free_resource(pnp_res); } } diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 46c791adb894..9a45c25b46d2 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -806,6 +806,13 @@ static void pnpacpi_encode_irq(struct pnp_dev *dev, struct acpi_resource_irq *irq = &resource->data.irq; int triggering, polarity, shareable; + if (!pnp_resource_enabled(p)) { + irq->interrupt_count = 0; + dev_dbg(&dev->dev, " encode irq (%s)\n", + p ? "disabled" : "missing"); + return; + } + decode_irq_flags(dev, p->flags, &triggering, &polarity, &shareable); irq->triggering = triggering; irq->polarity = polarity; @@ -828,6 +835,13 @@ static void pnpacpi_encode_ext_irq(struct pnp_dev *dev, struct acpi_resource_extended_irq *extended_irq = &resource->data.extended_irq; int triggering, polarity, shareable; + if (!pnp_resource_enabled(p)) { + extended_irq->interrupt_count = 0; + dev_dbg(&dev->dev, " encode extended irq (%s)\n", + p ? "disabled" : "missing"); + return; + } + decode_irq_flags(dev, p->flags, &triggering, &polarity, &shareable); extended_irq->producer_consumer = ACPI_CONSUMER; extended_irq->triggering = triggering; @@ -848,6 +862,13 @@ static void pnpacpi_encode_dma(struct pnp_dev *dev, { struct acpi_resource_dma *dma = &resource->data.dma; + if (!pnp_resource_enabled(p)) { + dma->channel_count = 0; + dev_dbg(&dev->dev, " encode dma (%s)\n", + p ? "disabled" : "missing"); + return; + } + /* Note: pnp_assign_dma will copy pnp_dma->flags into p->flags */ switch (p->flags & IORESOURCE_DMA_SPEED_MASK) { case IORESOURCE_DMA_TYPEA: @@ -889,17 +910,21 @@ static void pnpacpi_encode_io(struct pnp_dev *dev, { struct acpi_resource_io *io = &resource->data.io; - /* Note: pnp_assign_port will copy pnp_port->flags into p->flags */ - io->io_decode = (p->flags & PNP_PORT_FLAG_16BITADDR) ? - ACPI_DECODE_16 : ACPI_DECODE_10; - io->minimum = p->start; - io->maximum = p->end; - io->alignment = 0; /* Correct? */ - io->address_length = p->end - p->start + 1; - - dev_dbg(&dev->dev, " encode io %#llx-%#llx decode %#x\n", - (unsigned long long) p->start, (unsigned long long) p->end, - io->io_decode); + if (pnp_resource_enabled(p)) { + /* Note: pnp_assign_port copies pnp_port->flags into p->flags */ + io->io_decode = (p->flags & PNP_PORT_FLAG_16BITADDR) ? + ACPI_DECODE_16 : ACPI_DECODE_10; + io->minimum = p->start; + io->maximum = p->end; + io->alignment = 0; /* Correct? */ + io->address_length = p->end - p->start + 1; + } else { + io->minimum = 0; + io->address_length = 0; + } + + dev_dbg(&dev->dev, " encode io %#x-%#x decode %#x\n", io->minimum, + io->minimum + io->address_length - 1, io->io_decode); } static void pnpacpi_encode_fixed_io(struct pnp_dev *dev, @@ -908,11 +933,16 @@ static void pnpacpi_encode_fixed_io(struct pnp_dev *dev, { struct acpi_resource_fixed_io *fixed_io = &resource->data.fixed_io; - fixed_io->address = p->start; - fixed_io->address_length = p->end - p->start + 1; + if (pnp_resource_enabled(p)) { + fixed_io->address = p->start; + fixed_io->address_length = p->end - p->start + 1; + } else { + fixed_io->address = 0; + fixed_io->address_length = 0; + } - dev_dbg(&dev->dev, " encode fixed_io %#llx-%#llx\n", - (unsigned long long) p->start, (unsigned long long) p->end); + dev_dbg(&dev->dev, " encode fixed_io %#x-%#x\n", fixed_io->address, + fixed_io->address + fixed_io->address_length - 1); } static void pnpacpi_encode_mem24(struct pnp_dev *dev, @@ -921,17 +951,22 @@ static void pnpacpi_encode_mem24(struct pnp_dev *dev, { struct acpi_resource_memory24 *memory24 = &resource->data.memory24; - /* Note: pnp_assign_mem will copy pnp_mem->flags into p->flags */ - memory24->write_protect = - (p->flags & IORESOURCE_MEM_WRITEABLE) ? - ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; - memory24->minimum = p->start; - memory24->maximum = p->end; - memory24->alignment = 0; - memory24->address_length = p->end - p->start + 1; - - dev_dbg(&dev->dev, " encode mem24 %#llx-%#llx write_protect %#x\n", - (unsigned long long) p->start, (unsigned long long) p->end, + if (pnp_resource_enabled(p)) { + /* Note: pnp_assign_mem copies pnp_mem->flags into p->flags */ + memory24->write_protect = p->flags & IORESOURCE_MEM_WRITEABLE ? + ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; + memory24->minimum = p->start; + memory24->maximum = p->end; + memory24->alignment = 0; + memory24->address_length = p->end - p->start + 1; + } else { + memory24->minimum = 0; + memory24->address_length = 0; + } + + dev_dbg(&dev->dev, " encode mem24 %#x-%#x write_protect %#x\n", + memory24->minimum, + memory24->minimum + memory24->address_length - 1, memory24->write_protect); } @@ -941,16 +976,21 @@ static void pnpacpi_encode_mem32(struct pnp_dev *dev, { struct acpi_resource_memory32 *memory32 = &resource->data.memory32; - memory32->write_protect = - (p->flags & IORESOURCE_MEM_WRITEABLE) ? - ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; - memory32->minimum = p->start; - memory32->maximum = p->end; - memory32->alignment = 0; - memory32->address_length = p->end - p->start + 1; + if (pnp_resource_enabled(p)) { + memory32->write_protect = p->flags & IORESOURCE_MEM_WRITEABLE ? + ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; + memory32->minimum = p->start; + memory32->maximum = p->end; + memory32->alignment = 0; + memory32->address_length = p->end - p->start + 1; + } else { + memory32->minimum = 0; + memory32->alignment = 0; + } - dev_dbg(&dev->dev, " encode mem32 %#llx-%#llx write_protect %#x\n", - (unsigned long long) p->start, (unsigned long long) p->end, + dev_dbg(&dev->dev, " encode mem32 %#x-%#x write_protect %#x\n", + memory32->minimum, + memory32->minimum + memory32->address_length - 1, memory32->write_protect); } @@ -960,15 +1000,20 @@ static void pnpacpi_encode_fixed_mem32(struct pnp_dev *dev, { struct acpi_resource_fixed_memory32 *fixed_memory32 = &resource->data.fixed_memory32; - fixed_memory32->write_protect = - (p->flags & IORESOURCE_MEM_WRITEABLE) ? - ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; - fixed_memory32->address = p->start; - fixed_memory32->address_length = p->end - p->start + 1; + if (pnp_resource_enabled(p)) { + fixed_memory32->write_protect = + p->flags & IORESOURCE_MEM_WRITEABLE ? + ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; + fixed_memory32->address = p->start; + fixed_memory32->address_length = p->end - p->start + 1; + } else { + fixed_memory32->address = 0; + fixed_memory32->address_length = 0; + } - dev_dbg(&dev->dev, " encode fixed_mem32 %#llx-%#llx " - "write_protect %#x\n", - (unsigned long long) p->start, (unsigned long long) p->end, + dev_dbg(&dev->dev, " encode fixed_mem32 %#x-%#x write_protect %#x\n", + fixed_memory32->address, + fixed_memory32->address + fixed_memory32->address_length - 1, fixed_memory32->write_protect); } diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index 5ff9a4c0447e..01f0c3dd1b08 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -526,8 +526,16 @@ len_err: static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p, struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = res->end - res->start + 1; + } else { + base = 0; + len = 0; + } p[4] = (base >> 8) & 0xff; p[5] = ((base >> 8) >> 8) & 0xff; @@ -536,15 +544,22 @@ static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p, p[10] = (len >> 8) & 0xff; p[11] = ((len >> 8) >> 8) & 0xff; - dev_dbg(&dev->dev, " encode mem %#llx-%#llx\n", - (unsigned long long) res->start, (unsigned long long) res->end); + dev_dbg(&dev->dev, " encode mem %#lx-%#lx\n", base, base + len - 1); } static void pnpbios_encode_mem32(struct pnp_dev *dev, unsigned char *p, struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = res->end - res->start + 1; + } else { + base = 0; + len = 0; + } p[4] = base & 0xff; p[5] = (base >> 8) & 0xff; @@ -559,15 +574,22 @@ static void pnpbios_encode_mem32(struct pnp_dev *dev, unsigned char *p, p[18] = (len >> 16) & 0xff; p[19] = (len >> 24) & 0xff; - dev_dbg(&dev->dev, " encode mem32 %#llx-%#llx\n", - (unsigned long long) res->start, (unsigned long long) res->end); + dev_dbg(&dev->dev, " encode mem32 %#lx-%#lx\n", base, base + len - 1); } static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p, struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = res->end - res->start + 1; + } else { + base = 0; + len = 0; + } p[4] = base & 0xff; p[5] = (base >> 8) & 0xff; @@ -578,40 +600,54 @@ static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p, p[10] = (len >> 16) & 0xff; p[11] = (len >> 24) & 0xff; - dev_dbg(&dev->dev, " encode fixed_mem32 %#llx-%#llx\n", - (unsigned long long) res->start, (unsigned long long) res->end); + dev_dbg(&dev->dev, " encode fixed_mem32 %#lx-%#lx\n", base, + base + len - 1); } static void pnpbios_encode_irq(struct pnp_dev *dev, unsigned char *p, struct resource *res) { - unsigned long map = 0; + unsigned long map; + + if (pnp_resource_enabled(res)) + map = 1 << res->start; + else + map = 0; - map = 1 << res->start; p[1] = map & 0xff; p[2] = (map >> 8) & 0xff; - dev_dbg(&dev->dev, " encode irq %llu\n", - (unsigned long long)res->start); + dev_dbg(&dev->dev, " encode irq mask %#lx\n", map); } static void pnpbios_encode_dma(struct pnp_dev *dev, unsigned char *p, struct resource *res) { - unsigned long map = 0; + unsigned long map; + + if (pnp_resource_enabled(res)) + map = 1 << res->start; + else + map = 0; - map = 1 << res->start; p[1] = map & 0xff; - dev_dbg(&dev->dev, " encode dma %llu\n", - (unsigned long long)res->start); + dev_dbg(&dev->dev, " encode dma mask %#lx\n", map); } static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p, struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = res->end - res->start + 1; + } else { + base = 0; + len = 0; + } p[2] = base & 0xff; p[3] = (base >> 8) & 0xff; @@ -619,8 +655,7 @@ static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p, p[5] = (base >> 8) & 0xff; p[7] = len & 0xff; - dev_dbg(&dev->dev, " encode io %#llx-%#llx\n", - (unsigned long long) res->start, (unsigned long long) res->end); + dev_dbg(&dev->dev, " encode io %#lx-%#lx\n", base, base + len - 1); } static void pnpbios_encode_fixed_port(struct pnp_dev *dev, unsigned char *p, @@ -629,12 +664,20 @@ static void pnpbios_encode_fixed_port(struct pnp_dev *dev, unsigned char *p, unsigned long base = res->start; unsigned long len = res->end - res->start + 1; + if (pnp_resource_enabled(res)) { + base = res->start; + len = res->end - res->start + 1; + } else { + base = 0; + len = 0; + } + p[1] = base & 0xff; p[2] = (base >> 8) & 0xff; p[3] = len & 0xff; - dev_dbg(&dev->dev, " encode fixed_io %#llx-%#llx\n", - (unsigned long long) res->start, (unsigned long long) res->end); + dev_dbg(&dev->dev, " encode fixed_io %#lx-%#lx\n", base, + base + len - 1); } static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 1ff3bb585ab2..21acb54eff6d 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -248,8 +248,7 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) for (j = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); j++) { - if (res->flags & IORESOURCE_UNSET || - (res->start == 0 && res->end == 0)) + if (res->start == 0 && res->end == 0) continue; pnp_start = res->start; diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index cce341f743d4..0797a77bd042 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -237,7 +237,7 @@ void pnp_free_option(struct pnp_option *option) !((*(enda) < *(startb)) || (*(endb) < *(starta))) #define cannot_compare(flags) \ -((flags) & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) +((flags) & IORESOURCE_DISABLED) int pnp_check_port(struct pnp_dev *dev, struct resource *res) { @@ -505,81 +505,31 @@ int pnp_resource_type(struct resource *res) IORESOURCE_IRQ | IORESOURCE_DMA); } -struct pnp_resource *pnp_get_pnp_resource(struct pnp_dev *dev, - unsigned int type, unsigned int num) -{ - struct pnp_resource_table *res = dev->res; - - switch (type) { - case IORESOURCE_IO: - if (num >= PNP_MAX_PORT) - return NULL; - return &res->port[num]; - case IORESOURCE_MEM: - if (num >= PNP_MAX_MEM) - return NULL; - return &res->mem[num]; - case IORESOURCE_IRQ: - if (num >= PNP_MAX_IRQ) - return NULL; - return &res->irq[num]; - case IORESOURCE_DMA: - if (num >= PNP_MAX_DMA) - return NULL; - return &res->dma[num]; - } - return NULL; -} - struct resource *pnp_get_resource(struct pnp_dev *dev, unsigned int type, unsigned int num) { struct pnp_resource *pnp_res; + struct resource *res; - pnp_res = pnp_get_pnp_resource(dev, type, num); - if (pnp_res) - return &pnp_res->res; - + list_for_each_entry(pnp_res, &dev->resources, list) { + res = &pnp_res->res; + if (pnp_resource_type(res) == type && num-- == 0) + return res; + } return NULL; } EXPORT_SYMBOL(pnp_get_resource); -static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev, int type) +static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev) { struct pnp_resource *pnp_res; - int i; - switch (type) { - case IORESOURCE_IO: - for (i = 0; i < PNP_MAX_PORT; i++) { - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_IO, i); - if (pnp_res && !pnp_resource_valid(&pnp_res->res)) - return pnp_res; - } - break; - case IORESOURCE_MEM: - for (i = 0; i < PNP_MAX_MEM; i++) { - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_MEM, i); - if (pnp_res && !pnp_resource_valid(&pnp_res->res)) - return pnp_res; - } - break; - case IORESOURCE_IRQ: - for (i = 0; i < PNP_MAX_IRQ; i++) { - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_IRQ, i); - if (pnp_res && !pnp_resource_valid(&pnp_res->res)) - return pnp_res; - } - break; - case IORESOURCE_DMA: - for (i = 0; i < PNP_MAX_DMA; i++) { - pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_DMA, i); - if (pnp_res && !pnp_resource_valid(&pnp_res->res)) - return pnp_res; - } - break; - } - return NULL; + pnp_res = kzalloc(sizeof(struct pnp_resource), GFP_KERNEL); + if (!pnp_res) + return NULL; + + list_add_tail(&pnp_res->list, &dev->resources); + return pnp_res; } struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, @@ -589,7 +539,7 @@ struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, struct resource *res; static unsigned char warned; - pnp_res = pnp_new_resource(dev, IORESOURCE_IRQ); + pnp_res = pnp_new_resource(dev); if (!pnp_res) { if (!warned) { dev_err(&dev->dev, "can't add resource for IRQ %d\n", @@ -615,7 +565,7 @@ struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, struct resource *res; static unsigned char warned; - pnp_res = pnp_new_resource(dev, IORESOURCE_DMA); + pnp_res = pnp_new_resource(dev); if (!pnp_res) { if (!warned) { dev_err(&dev->dev, "can't add resource for DMA %d\n", @@ -642,7 +592,7 @@ struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, struct resource *res; static unsigned char warned; - pnp_res = pnp_new_resource(dev, IORESOURCE_IO); + pnp_res = pnp_new_resource(dev); if (!pnp_res) { if (!warned) { dev_err(&dev->dev, "can't add resource for IO " @@ -671,7 +621,7 @@ struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, struct resource *res; static unsigned char warned; - pnp_res = pnp_new_resource(dev, IORESOURCE_MEM); + pnp_res = pnp_new_resource(dev); if (!pnp_res) { if (!warned) { dev_err(&dev->dev, "can't add resource for MEM " diff --git a/drivers/pnp/support.c b/drivers/pnp/support.c index eb07345f5cf7..1566e4a73849 100644 --- a/drivers/pnp/support.c +++ b/drivers/pnp/support.c @@ -16,6 +16,10 @@ */ int pnp_is_active(struct pnp_dev *dev) { + /* + * I don't think this is very reliable because pnp_disable_dev() + * only clears out auto-assigned resources. + */ if (!pnp_port_start(dev, 0) && pnp_port_len(dev, 0) <= 1 && !pnp_mem_start(dev, 0) && pnp_mem_len(dev, 0) <= 1 && pnp_irq(dev, 0) == -1 && pnp_dma(dev, 0) == -1) @@ -70,54 +74,41 @@ char *pnp_resource_type_name(struct resource *res) void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc) { #ifdef DEBUG + char buf[128]; + int len = 0; + struct pnp_resource *pnp_res; struct resource *res; - int i; dev_dbg(&dev->dev, "current resources: %s\n", desc); + list_for_each_entry(pnp_res, &dev->resources, list) { + res = &pnp_res->res; - for (i = 0; i < PNP_MAX_IRQ; i++) { - res = pnp_get_resource(dev, IORESOURCE_IRQ, i); - if (res && !(res->flags & IORESOURCE_UNSET)) - dev_dbg(&dev->dev, " irq %lld flags %#lx%s%s\n", - (unsigned long long) res->start, res->flags, - res->flags & IORESOURCE_DISABLED ? - " DISABLED" : "", - res->flags & IORESOURCE_AUTO ? - " AUTO" : ""); - } - for (i = 0; i < PNP_MAX_DMA; i++) { - res = pnp_get_resource(dev, IORESOURCE_DMA, i); - if (res && !(res->flags & IORESOURCE_UNSET)) - dev_dbg(&dev->dev, " dma %lld flags %#lx%s%s\n", - (unsigned long long) res->start, res->flags, - res->flags & IORESOURCE_DISABLED ? - " DISABLED" : "", - res->flags & IORESOURCE_AUTO ? - " AUTO" : ""); - } - for (i = 0; i < PNP_MAX_PORT; i++) { - res = pnp_get_resource(dev, IORESOURCE_IO, i); - if (res && !(res->flags & IORESOURCE_UNSET)) - dev_dbg(&dev->dev, " io %#llx-%#llx flags %#lx" - "%s%s\n", - (unsigned long long) res->start, - (unsigned long long) res->end, res->flags, - res->flags & IORESOURCE_DISABLED ? - " DISABLED" : "", - res->flags & IORESOURCE_AUTO ? - " AUTO" : ""); - } - for (i = 0; i < PNP_MAX_MEM; i++) { - res = pnp_get_resource(dev, IORESOURCE_MEM, i); - if (res && !(res->flags & IORESOURCE_UNSET)) - dev_dbg(&dev->dev, " mem %#llx-%#llx flags %#lx" - "%s%s\n", - (unsigned long long) res->start, - (unsigned long long) res->end, res->flags, - res->flags & IORESOURCE_DISABLED ? - " DISABLED" : "", - res->flags & IORESOURCE_AUTO ? - " AUTO" : ""); + len += snprintf(buf + len, sizeof(buf) - len, " %-3s ", + pnp_resource_type_name(res)); + + if (res->flags & IORESOURCE_DISABLED) { + dev_dbg(&dev->dev, "%sdisabled\n", buf); + continue; + } + + switch (pnp_resource_type(res)) { + case IORESOURCE_IO: + case IORESOURCE_MEM: + len += snprintf(buf + len, sizeof(buf) - len, + "%#llx-%#llx flags %#lx", + (unsigned long long) res->start, + (unsigned long long) res->end, + res->flags); + break; + case IORESOURCE_IRQ: + case IORESOURCE_DMA: + len += snprintf(buf + len, sizeof(buf) - len, + "%lld flags %#lx", + (unsigned long long) res->start, + res->flags); + break; + } + dev_dbg(&dev->dev, "%s\n", buf); } #endif } diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index cf4e07b01d48..764f3a310685 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -60,7 +60,7 @@ static void reserve_resources_of_dev(struct pnp_dev *dev) int i; for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { - if (res->flags & IORESOURCE_UNSET) + if (res->flags & IORESOURCE_DISABLED) continue; if (res->start == 0) continue; /* disabled */ @@ -81,7 +81,7 @@ static void reserve_resources_of_dev(struct pnp_dev *dev) } for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { - if (res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) + if (res->flags & IORESOURCE_DISABLED) continue; reserve_range(dev, res->start, res->end, 0); diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 8b607aecd959..dfaa567e04a8 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -15,7 +15,6 @@ struct pnp_protocol; struct pnp_dev; -struct pnp_resource_table; /* * Resource Management @@ -24,7 +23,14 @@ struct resource *pnp_get_resource(struct pnp_dev *, unsigned int, unsigned int); static inline int pnp_resource_valid(struct resource *res) { - if (res && !(res->flags & IORESOURCE_UNSET)) + if (res) + return 1; + return 0; +} + +static inline int pnp_resource_enabled(struct resource *res) +{ + if (res && !(res->flags & IORESOURCE_DISABLED)) return 1; return 0; } @@ -64,7 +70,7 @@ static inline unsigned long pnp_port_flags(struct pnp_dev *dev, if (pnp_resource_valid(res)) return res->flags; - return IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET; + return IORESOURCE_IO | IORESOURCE_AUTO; } static inline int pnp_port_valid(struct pnp_dev *dev, unsigned int bar) @@ -109,7 +115,7 @@ static inline unsigned long pnp_mem_flags(struct pnp_dev *dev, unsigned int bar) if (pnp_resource_valid(res)) return res->flags; - return IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET; + return IORESOURCE_MEM | IORESOURCE_AUTO; } static inline int pnp_mem_valid(struct pnp_dev *dev, unsigned int bar) @@ -143,7 +149,7 @@ static inline unsigned long pnp_irq_flags(struct pnp_dev *dev, unsigned int bar) if (pnp_resource_valid(res)) return res->flags; - return IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET; + return IORESOURCE_IRQ | IORESOURCE_AUTO; } static inline int pnp_irq_valid(struct pnp_dev *dev, unsigned int bar) @@ -167,7 +173,7 @@ static inline unsigned long pnp_dma_flags(struct pnp_dev *dev, unsigned int bar) if (pnp_resource_valid(res)) return res->flags; - return IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET; + return IORESOURCE_DMA | IORESOURCE_AUTO; } static inline int pnp_dma_valid(struct pnp_dev *dev, unsigned int bar) @@ -296,7 +302,7 @@ struct pnp_dev { int capabilities; struct pnp_option *independent; struct pnp_option *dependent; - struct pnp_resource_table *res; + struct list_head resources; char name[PNP_NAME_LEN]; /* contains a human-readable name */ int flags; /* used by protocols */ -- cgit v1.2.3 From 57fd51a8be26921b56747ddd09d1d9e01c11c9e0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:57:01 -0600 Subject: PNP: add pnp_possible_config() -- can a device could be configured this way? As part of a heuristic to identify modem devices, 8250_pnp.c checks to see whether a device can be configured at any of the legacy COM port addresses. This patch moves the code that traverses the PNP "possible resource options" from 8250_pnp.c to the PNP subsystem. This encapsulation is important because a future patch will change the implementation of those resource options. Signed-off-by: Bjorn Helgaas Signed-off-by: Andi Kleen Acked-by: Rene Herman Signed-off-by: Len Brown --- drivers/pnp/resource.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/serial/8250_pnp.c | 24 ++++++------------ include/linux/pnp.h | 5 ++++ 3 files changed, 74 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index ff79aa6168cf..786fd356916d 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -624,6 +624,68 @@ struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, return pnp_res; } +static int pnp_possible_option(struct pnp_option *option, int type, + resource_size_t start, resource_size_t size) +{ + struct pnp_option *tmp; + struct pnp_port *port; + struct pnp_mem *mem; + struct pnp_irq *irq; + struct pnp_dma *dma; + + if (!option) + return 0; + + for (tmp = option; tmp; tmp = tmp->next) { + switch (type) { + case IORESOURCE_IO: + for (port = tmp->port; port; port = port->next) { + if (port->min == start && port->size == size) + return 1; + } + break; + case IORESOURCE_MEM: + for (mem = tmp->mem; mem; mem = mem->next) { + if (mem->min == start && mem->size == size) + return 1; + } + break; + case IORESOURCE_IRQ: + for (irq = tmp->irq; irq; irq = irq->next) { + if (start < PNP_IRQ_NR && + test_bit(start, irq->map)) + return 1; + } + break; + case IORESOURCE_DMA: + for (dma = tmp->dma; dma; dma = dma->next) { + if (dma->map & (1 << start)) + return 1; + } + break; + } + } + + return 0; +} + +/* + * Determine whether the specified resource is a possible configuration + * for this device. + */ +int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, + resource_size_t size) +{ + if (pnp_possible_option(dev->independent, type, start, size)) + return 1; + + if (pnp_possible_option(dev->dependent, type, start, size)) + return 1; + + return 0; +} +EXPORT_SYMBOL(pnp_possible_config); + /* format is: pnp_reserve_irq=irq1[,irq2] .... */ static int __init pnp_setup_reserve_irq(char *str) { diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index 97c68d021d28..638b68649e79 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -383,21 +383,14 @@ static int __devinit check_name(char *name) return 0; } -static int __devinit check_resources(struct pnp_option *option) +static int __devinit check_resources(struct pnp_dev *dev) { - struct pnp_option *tmp; - if (!option) - return 0; + resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; + int i; - for (tmp = option; tmp; tmp = tmp->next) { - struct pnp_port *port; - for (port = tmp->port; port; port = port->next) - if ((port->size == 8) && - ((port->min == 0x2f8) || - (port->min == 0x3f8) || - (port->min == 0x2e8) || - (port->min == 0x3e8))) - return 1; + for (i = 0; i < ARRAY_SIZE(base); i++) { + if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) + return 1; } return 0; @@ -420,10 +413,7 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) (dev->card && check_name(dev->card->name)))) return -ENODEV; - if (check_resources(dev->independent)) - return 0; - - if (check_resources(dev->dependent)) + if (check_resources(dev)) return 0; return -ENODEV; diff --git a/include/linux/pnp.h b/include/linux/pnp.h index dfaa567e04a8..e033e1b14c27 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -479,6 +479,8 @@ void pnp_unregister_card_driver(struct pnp_card_driver *drv); extern struct list_head pnp_cards; /* resource management */ +int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t base, + resource_size_t size); int pnp_auto_config_dev(struct pnp_dev *dev); int pnp_start_dev(struct pnp_dev *dev); int pnp_stop_dev(struct pnp_dev *dev); @@ -506,6 +508,9 @@ static inline int pnp_register_card_driver(struct pnp_card_driver *drv) { return static inline void pnp_unregister_card_driver(struct pnp_card_driver *drv) { } /* resource management */ +static inline int pnp_possible_config(struct pnp_dev *dev, int type, + resource_size_t base, + resource_size_t size) { return 0; } static inline int pnp_auto_config_dev(struct pnp_dev *dev) { return -ENODEV; } static inline int pnp_start_dev(struct pnp_dev *dev) { return -ENODEV; } static inline int pnp_stop_dev(struct pnp_dev *dev) { return -ENODEV; } -- cgit v1.2.3 From 08c9f262f268f7948be13bf3a5bda1d635c649b4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:57:03 -0600 Subject: PNP: define PNP-specific IORESOURCE_IO_* flags alongside IRQ, DMA, MEM PNP previously defined PNP_PORT_FLAG_16BITADDR and PNP_PORT_FLAG_FIXED in a private header file, but put those flags in struct resource.flags fields. Better to make them IORESOURCE_IO_* flags like the existing IRQ, DMA, and MEM flags. Signed-off-by: Bjorn Helgaas Signed-off-by: Andi Kleen Acked-by: Rene Herman Signed-off-by: Len Brown --- drivers/pnp/interface.c | 2 +- drivers/pnp/isapnp/core.c | 4 ++-- drivers/pnp/pnpacpi/rsparser.c | 10 +++++----- drivers/pnp/pnpbios/rsparser.c | 4 ++-- include/linux/ioport.h | 4 ++++ include/linux/pnp.h | 3 --- 6 files changed, 14 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 239923a300cd..c172b6de6b71 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -57,7 +57,7 @@ static void pnp_print_port(pnp_info_buffer_t * buffer, char *space, "%sport 0x%x-0x%x, align 0x%x, size 0x%x, %i-bit address decoding\n", space, port->min, port->max, port->align ? (port->align - 1) : 0, port->size, - port->flags & PNP_PORT_FLAG_16BITADDR ? 16 : 10); + port->flags & IORESOURCE_IO_16BIT_ADDR ? 16 : 10); } static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index ca4457ec403b..c5b92526963b 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -486,7 +486,7 @@ static void __init isapnp_parse_port_resource(struct pnp_dev *dev, port->max = (tmp[4] << 8) | tmp[3]; port->align = tmp[5]; port->size = tmp[6]; - port->flags = tmp[0] ? PNP_PORT_FLAG_16BITADDR : 0; + port->flags = tmp[0] ? IORESOURCE_IO_16BIT_ADDR : 0; pnp_register_port_resource(dev, option, port); } @@ -507,7 +507,7 @@ static void __init isapnp_parse_fixed_port_resource(struct pnp_dev *dev, port->min = port->max = (tmp[1] << 8) | tmp[0]; port->size = tmp[2]; port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; + port->flags = IORESOURCE_IO_FIXED; pnp_register_port_resource(dev, option, port); } diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 595252b65205..46069e64a6b2 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -180,7 +180,7 @@ static void pnpacpi_parse_allocated_ioresource(struct pnp_dev *dev, u64 start, u64 end = start + len - 1; if (io_decode == ACPI_DECODE_16) - flags |= PNP_PORT_FLAG_16BITADDR; + flags |= IORESOURCE_IO_16BIT_ADDR; if (len == 0 || end >= 0x10003) flags |= IORESOURCE_DISABLED; @@ -485,7 +485,7 @@ static __init void pnpacpi_parse_port_option(struct pnp_dev *dev, port->align = io->alignment; port->size = io->address_length; port->flags = ACPI_DECODE_16 == io->io_decode ? - PNP_PORT_FLAG_16BITADDR : 0; + IORESOURCE_IO_16BIT_ADDR : 0; pnp_register_port_resource(dev, option, port); } @@ -503,7 +503,7 @@ static __init void pnpacpi_parse_fixed_port_option(struct pnp_dev *dev, port->min = port->max = io->address; port->size = io->address_length; port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; + port->flags = IORESOURCE_IO_FIXED; pnp_register_port_resource(dev, option, port); } @@ -609,7 +609,7 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev, port->min = port->max = p->minimum; port->size = p->address_length; port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; + port->flags = IORESOURCE_IO_FIXED; pnp_register_port_resource(dev, option, port); } } @@ -946,7 +946,7 @@ static void pnpacpi_encode_io(struct pnp_dev *dev, if (pnp_resource_enabled(p)) { /* Note: pnp_assign_port copies pnp_port->flags into p->flags */ - io->io_decode = (p->flags & PNP_PORT_FLAG_16BITADDR) ? + io->io_decode = (p->flags & IORESOURCE_IO_16BIT_ADDR) ? ACPI_DECODE_16 : ACPI_DECODE_10; io->minimum = p->start; io->maximum = p->end; diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index 01f0c3dd1b08..489fec3b7974 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -310,7 +310,7 @@ static __init void pnpbios_parse_port_option(struct pnp_dev *dev, port->max = (p[5] << 8) | p[4]; port->align = p[6]; port->size = p[7]; - port->flags = p[1] ? PNP_PORT_FLAG_16BITADDR : 0; + port->flags = p[1] ? IORESOURCE_IO_16BIT_ADDR : 0; pnp_register_port_resource(dev, option, port); } @@ -326,7 +326,7 @@ static __init void pnpbios_parse_fixed_port_option(struct pnp_dev *dev, port->min = port->max = (p[2] << 8) | p[1]; port->size = p[3]; port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; + port->flags = IORESOURCE_IO_FIXED; pnp_register_port_resource(dev, option, port); } diff --git a/include/linux/ioport.h b/include/linux/ioport.h index c6801bffe76d..39db059ffb8b 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -88,6 +88,10 @@ struct resource_list { #define IORESOURCE_MEM_SHADOWABLE (1<<5) /* dup: IORESOURCE_SHADOWABLE */ #define IORESOURCE_MEM_EXPANSIONROM (1<<6) +/* PnP I/O specific bits (IORESOURCE_BITS) */ +#define IORESOURCE_IO_16BIT_ADDR (1<<0) +#define IORESOURCE_IO_FIXED (1<<1) + /* PCI ROM control bits (IORESOURCE_BITS) */ #define IORESOURCE_ROM_ENABLE (1<<0) /* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */ #define IORESOURCE_ROM_SHADOW (1<<1) /* ROM is copy at C000:0 */ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index e033e1b14c27..e1454dabde10 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -182,9 +182,6 @@ static inline int pnp_dma_valid(struct pnp_dev *dev, unsigned int bar) } -#define PNP_PORT_FLAG_16BITADDR (1<<0) -#define PNP_PORT_FLAG_FIXED (1<<1) - struct pnp_port { unsigned short min; /* min base number */ unsigned short max; /* max base number */ -- cgit v1.2.3 From a1802c42950403657d07e64558eff612d550ce16 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:57:04 -0600 Subject: PNP: make resource option structures private to PNP subsystem Nothing outside the PNP subsystem should need access to a device's resource options, so this patch moves the option structure declarations to a private header file. Signed-off-by: Bjorn Helgaas Signed-off-by: Andi Kleen Acked-by: Rene Herman Signed-off-by: Len Brown --- drivers/pnp/base.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pnp.h | 48 ------------------------------------------------ 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index 1667ac3ca45b..3126e4582008 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -19,6 +19,54 @@ void pnp_remove_card(struct pnp_card *card); int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev); void pnp_remove_card_device(struct pnp_dev *dev); +struct pnp_port { + unsigned short min; /* min base number */ + unsigned short max; /* max base number */ + unsigned char align; /* align boundary */ + unsigned char size; /* size of range */ + unsigned char flags; /* port flags */ + unsigned char pad; /* pad */ + struct pnp_port *next; /* next port */ +}; + +#define PNP_IRQ_NR 256 +struct pnp_irq { + DECLARE_BITMAP(map, PNP_IRQ_NR); /* bitmask for IRQ lines */ + unsigned char flags; /* IRQ flags */ + unsigned char pad; /* pad */ + struct pnp_irq *next; /* next IRQ */ +}; + +struct pnp_dma { + unsigned char map; /* bitmask for DMA channels */ + unsigned char flags; /* DMA flags */ + struct pnp_dma *next; /* next port */ +}; + +struct pnp_mem { + unsigned int min; /* min base number */ + unsigned int max; /* max base number */ + unsigned int align; /* align boundary */ + unsigned int size; /* size of range */ + unsigned char flags; /* memory flags */ + unsigned char pad; /* pad */ + struct pnp_mem *next; /* next memory resource */ +}; + +#define PNP_RES_PRIORITY_PREFERRED 0 +#define PNP_RES_PRIORITY_ACCEPTABLE 1 +#define PNP_RES_PRIORITY_FUNCTIONAL 2 +#define PNP_RES_PRIORITY_INVALID 65535 + +struct pnp_option { + unsigned short priority; /* priority */ + struct pnp_port *port; /* first port */ + struct pnp_irq *irq; /* first IRQ */ + struct pnp_dma *dma; /* first DMA */ + struct pnp_mem *mem; /* first memory resource */ + struct pnp_option *next; /* used to chain dependent resources */ +}; + struct pnp_option *pnp_build_option(int priority); struct pnp_option *pnp_register_independent_option(struct pnp_dev *dev); struct pnp_option *pnp_register_dependent_option(struct pnp_dev *dev, diff --git a/include/linux/pnp.h b/include/linux/pnp.h index e1454dabde10..785126ffcc11 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -182,54 +182,6 @@ static inline int pnp_dma_valid(struct pnp_dev *dev, unsigned int bar) } -struct pnp_port { - unsigned short min; /* min base number */ - unsigned short max; /* max base number */ - unsigned char align; /* align boundary */ - unsigned char size; /* size of range */ - unsigned char flags; /* port flags */ - unsigned char pad; /* pad */ - struct pnp_port *next; /* next port */ -}; - -#define PNP_IRQ_NR 256 -struct pnp_irq { - DECLARE_BITMAP(map, PNP_IRQ_NR); /* bitmask for IRQ lines */ - unsigned char flags; /* IRQ flags */ - unsigned char pad; /* pad */ - struct pnp_irq *next; /* next IRQ */ -}; - -struct pnp_dma { - unsigned char map; /* bitmask for DMA channels */ - unsigned char flags; /* DMA flags */ - struct pnp_dma *next; /* next port */ -}; - -struct pnp_mem { - unsigned int min; /* min base number */ - unsigned int max; /* max base number */ - unsigned int align; /* align boundary */ - unsigned int size; /* size of range */ - unsigned char flags; /* memory flags */ - unsigned char pad; /* pad */ - struct pnp_mem *next; /* next memory resource */ -}; - -#define PNP_RES_PRIORITY_PREFERRED 0 -#define PNP_RES_PRIORITY_ACCEPTABLE 1 -#define PNP_RES_PRIORITY_FUNCTIONAL 2 -#define PNP_RES_PRIORITY_INVALID 65535 - -struct pnp_option { - unsigned short priority; /* priority */ - struct pnp_port *port; /* first port */ - struct pnp_irq *irq; /* first IRQ */ - struct pnp_dma *dma; /* first DMA */ - struct pnp_mem *mem; /* first memory resource */ - struct pnp_option *next; /* used to chain dependent resources */ -}; - /* * Device Management */ -- cgit v1.2.3 From d5ebde6ef5c2d51828f975a81d7d0e58bccfd833 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:57:14 -0600 Subject: PNP: support optional IRQ resources This patch adds an IORESOURCE_IRQ_OPTIONAL flag for use when assigning resources to a device. If the flag is set and we are unable to assign an IRQ to the device, we can leave the IRQ disabled but allow the overall resource allocation to succeed. Some devices request an IRQ, but can run without an IRQ (possibly with degraded performance). This flag lets us run the device without the IRQ instead of just leaving the device disabled. This is a reimplementation of this previous change by Rene Herman : http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=3b73a223661ed137c5d3d2635f954382e94f5a43 I reimplemented this for two reasons: - to prepare for converting all resource options into a single linked list, as opposed to the per-resource-type lists we have now, and - to preserve the order and number of resource options. In PNPBIOS and ACPI, we configure a device by giving firmware a list of resource assignments. It is important that this list has exactly the same number of resources, in the same order, as the "template" list we got from the firmware in the first place. The problem of a sound card MPU401 being left disabled for want of an IRQ was reported by Uwe Bugla . Signed-off-by: Bjorn Helgaas Signed-off-by: Andi Kleen Acked-by: Rene Herman Signed-off-by: Len Brown --- drivers/pnp/interface.c | 2 ++ drivers/pnp/manager.c | 9 +++++++ drivers/pnp/quirks.c | 71 +++++++++++++++++++------------------------------ include/linux/ioport.h | 1 + 4 files changed, 39 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index b09f67de13d0..7a9fb5544b80 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -90,6 +90,8 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, pnp_printf(buffer, " High-Level"); if (irq->flags & IORESOURCE_IRQ_LOWLEVEL) pnp_printf(buffer, " Low-Level"); + if (irq->flags & IORESOURCE_IRQ_OPTIONAL) + pnp_printf(buffer, " (optional)"); pnp_printf(buffer, "\n"); } diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 7ea9e1e28003..a20accb5ef8f 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -153,6 +153,15 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) goto __add; } } + + if (rule->flags & IORESOURCE_IRQ_OPTIONAL) { + res->start = -1; + res->end = -1; + res->flags |= IORESOURCE_DISABLED; + dev_dbg(&dev->dev, " irq %d disabled (optional)\n", idx); + goto __add; + } + dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx); return -EBUSY; diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 48e60171b3ba..e8515ce0d296 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -121,34 +121,46 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) struct pnp_option *res; /* - * Build a functional IRQ-less variant of each MPU option. + * Build a functional IRQ-optional variant of each MPU option. */ for (res = dev->dependent; res; res = res->next) { struct pnp_option *curr; struct pnp_port *port; - struct pnp_port *copy; + struct pnp_port *copy_port; + struct pnp_irq *irq; + struct pnp_irq *copy_irq; port = res->port; - if (!port || !res->irq) + irq = res->irq; + if (!port || !irq) continue; - copy = pnp_alloc(sizeof *copy); - if (!copy) + copy_port = pnp_alloc(sizeof *copy_port); + if (!copy_port) + break; + + copy_irq = pnp_alloc(sizeof *copy_irq); + if (!copy_irq) { + kfree(copy_port); break; + } + + *copy_port = *port; + copy_port->next = NULL; - copy->min = port->min; - copy->max = port->max; - copy->align = port->align; - copy->size = port->size; - copy->flags = port->flags; + *copy_irq = *irq; + copy_irq->flags |= IORESOURCE_IRQ_OPTIONAL; + copy_irq->next = NULL; curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); if (!curr) { - kfree(copy); + kfree(copy_port); + kfree(copy_irq); break; } - curr->port = copy; + curr->port = copy_port; + curr->irq = copy_irq; if (prev) prev->next = curr; @@ -157,7 +169,7 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) prev = curr; } if (head) - dev_info(&dev->dev, "adding IRQ-less MPU options\n"); + dev_info(&dev->dev, "adding IRQ-optional MPU options\n"); return head; } @@ -167,10 +179,6 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) struct pnp_option *res; struct pnp_irq *irq; - /* - * Distribute the independent IRQ over the dependent options - */ - res = dev->independent; if (!res) return; @@ -179,33 +187,8 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) if (!irq || irq->next) return; - res = dev->dependent; - if (!res) - return; - - while (1) { - struct pnp_irq *copy; - - copy = pnp_alloc(sizeof *copy); - if (!copy) - break; - - bitmap_copy(copy->map.bits, irq->map.bits, PNP_IRQ_NR); - copy->flags = irq->flags; - - copy->next = res->irq; /* Yes, this is NULL */ - res->irq = copy; - - if (!res->next) - break; - res = res->next; - } - kfree(irq); - - res->next = quirk_isapnp_mpu_options(dev); - - res = dev->independent; - res->irq = NULL; + irq->flags |= IORESOURCE_IRQ_OPTIONAL; + dev_info(&dev->dev, "made independent IRQ optional\n"); } static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 39db059ffb8b..2cd07cc29687 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -59,6 +59,7 @@ struct resource_list { #define IORESOURCE_IRQ_HIGHLEVEL (1<<2) #define IORESOURCE_IRQ_LOWLEVEL (1<<3) #define IORESOURCE_IRQ_SHAREABLE (1<<4) +#define IORESOURCE_IRQ_OPTIONAL (1<<5) /* PnP DMA specific bits (IORESOURCE_BITS) */ #define IORESOURCE_DMA_TYPE_MASK (3<<0) -- cgit v1.2.3 From 1f32ca31e7409d37c1b25e5f81840fb184380cdf Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 27 Jun 2008 16:57:17 -0600 Subject: PNP: convert resource options to single linked list ISAPNP, PNPBIOS, and ACPI describe the "possible resource settings" of a device, i.e., the possibilities an OS bus driver has when it assigns I/O port, MMIO, and other resources to the device. PNP used to maintain this "possible resource setting" information in one independent option structure and a list of dependent option structures for each device. Each of these option structures had lists of I/O, memory, IRQ, and DMA resources, for example: dev independent options ind-io0 -> ind-io1 ... ind-mem0 -> ind-mem1 ... ... dependent option set 0 dep0-io0 -> dep0-io1 ... dep0-mem0 -> dep0-mem1 ... ... dependent option set 1 dep1-io0 -> dep1-io1 ... dep1-mem0 -> dep1-mem1 ... ... ... This data structure was designed for ISAPNP, where the OS configures device resource settings by writing directly to configuration registers. The OS can write the registers in arbitrary order much like it writes PCI BARs. However, for PNPBIOS and ACPI devices, the OS uses firmware interfaces that perform device configuration, and it is important to pass the desired settings to those interfaces in the correct order. The OS learns the correct order by using firmware interfaces that return the "current resource settings" and "possible resource settings," but the option structures above doesn't store the ordering information. This patch replaces the independent and dependent lists with a single list of options. For example, a device might have possible resource settings like this: dev options ind-io0 -> dep0-io0 -> dep1->io0 -> ind-io1 ... All the possible settings are in the same list, in the order they come from the firmware "possible resource settings" list. Each entry is tagged with an independent/dependent flag. Dependent entries also have a "set number" and an optional priority value. All dependent entries must be assigned from the same set. For example, the OS can use all the entries from dependent set 0, or all the entries from dependent set 1, but it cannot mix entries from set 0 with entries from set 1. Prior to this patch PNP didn't keep track of the order of this list, and it assigned all independent options first, then all dependent ones. Using the example above, that resulted in a "desired configuration" list like this: ind->io0 -> ind->io1 -> depN-io0 ... instead of the list the firmware expects, which looks like this: ind->io0 -> depN-io0 -> ind-io1 ... Signed-off-by: Bjorn Helgaas Signed-off-by: Andi Kleen Acked-by: Rene Herman Signed-off-by: Len Brown --- drivers/pnp/base.h | 93 +++++++++---- drivers/pnp/core.c | 4 +- drivers/pnp/interface.c | 75 +++++------ drivers/pnp/isapnp/core.c | 72 +++++----- drivers/pnp/manager.c | 145 +++++++-------------- drivers/pnp/pnpacpi/rsparser.c | 93 ++++++------- drivers/pnp/pnpbios/rsparser.c | 67 +++++----- drivers/pnp/quirks.c | 290 ++++++++++++++++++++++------------------- drivers/pnp/resource.c | 268 +++++++++---------------------------- drivers/pnp/support.c | 92 +++++++++++++ include/linux/pnp.h | 6 +- 11 files changed, 571 insertions(+), 634 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index 360c6385686c..e3fa9a2d9a3d 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -1,3 +1,8 @@ +/* + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + */ + extern spinlock_t pnp_lock; void *pnp_alloc(long size); @@ -25,8 +30,6 @@ struct pnp_port { resource_size_t align; /* align boundary */ resource_size_t size; /* size of range */ unsigned char flags; /* port flags */ - unsigned char pad; /* pad */ - struct pnp_port *next; /* next port */ }; #define PNP_IRQ_NR 256 @@ -35,14 +38,11 @@ typedef struct { DECLARE_BITMAP(bits, PNP_IRQ_NR); } pnp_irq_mask_t; struct pnp_irq { pnp_irq_mask_t map; /* bitmap for IRQ lines */ unsigned char flags; /* IRQ flags */ - unsigned char pad; /* pad */ - struct pnp_irq *next; /* next IRQ */ }; struct pnp_dma { unsigned char map; /* bitmask for DMA channels */ unsigned char flags; /* DMA flags */ - struct pnp_dma *next; /* next port */ }; struct pnp_mem { @@ -51,44 +51,91 @@ struct pnp_mem { resource_size_t align; /* align boundary */ resource_size_t size; /* size of range */ unsigned char flags; /* memory flags */ - unsigned char pad; /* pad */ - struct pnp_mem *next; /* next memory resource */ }; +#define PNP_OPTION_DEPENDENT 0x80000000 +#define PNP_OPTION_SET_MASK 0xffff +#define PNP_OPTION_SET_SHIFT 12 +#define PNP_OPTION_PRIORITY_MASK 0xfff +#define PNP_OPTION_PRIORITY_SHIFT 0 + #define PNP_RES_PRIORITY_PREFERRED 0 #define PNP_RES_PRIORITY_ACCEPTABLE 1 #define PNP_RES_PRIORITY_FUNCTIONAL 2 -#define PNP_RES_PRIORITY_INVALID 65535 +#define PNP_RES_PRIORITY_INVALID PNP_OPTION_PRIORITY_MASK struct pnp_option { - unsigned short priority; /* priority */ - struct pnp_port *port; /* first port */ - struct pnp_irq *irq; /* first IRQ */ - struct pnp_dma *dma; /* first DMA */ - struct pnp_mem *mem; /* first memory resource */ - struct pnp_option *next; /* used to chain dependent resources */ + struct list_head list; + unsigned int flags; /* independent/dependent, set, priority */ + + unsigned long type; /* IORESOURCE_{IO,MEM,IRQ,DMA} */ + union { + struct pnp_port port; + struct pnp_irq irq; + struct pnp_dma dma; + struct pnp_mem mem; + } u; }; -struct pnp_option *pnp_build_option(int priority); -struct pnp_option *pnp_register_independent_option(struct pnp_dev *dev); -struct pnp_option *pnp_register_dependent_option(struct pnp_dev *dev, - int priority); -int pnp_register_irq_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags, pnp_irq_mask_t *map, unsigned char flags); -int pnp_register_dma_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags, unsigned char map, unsigned char flags); -int pnp_register_port_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_port_resource(struct pnp_dev *dev, unsigned int option_flags, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t size, unsigned char flags); -int pnp_register_mem_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_mem_resource(struct pnp_dev *dev, unsigned int option_flags, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t size, unsigned char flags); + +static inline int pnp_option_is_dependent(struct pnp_option *option) +{ + return option->flags & PNP_OPTION_DEPENDENT ? 1 : 0; +} + +static inline unsigned int pnp_option_set(struct pnp_option *option) +{ + return (option->flags >> PNP_OPTION_SET_SHIFT) & PNP_OPTION_SET_MASK; +} + +static inline unsigned int pnp_option_priority(struct pnp_option *option) +{ + return (option->flags >> PNP_OPTION_PRIORITY_SHIFT) & + PNP_OPTION_PRIORITY_MASK; +} + +static inline unsigned int pnp_new_dependent_set(struct pnp_dev *dev, + int priority) +{ + unsigned int flags; + + if (priority > PNP_RES_PRIORITY_FUNCTIONAL) { + dev_warn(&dev->dev, "invalid dependent option priority %d " + "clipped to %d", priority, + PNP_RES_PRIORITY_INVALID); + priority = PNP_RES_PRIORITY_INVALID; + } + + flags = PNP_OPTION_DEPENDENT | + ((dev->num_dependent_sets & PNP_OPTION_SET_MASK) << + PNP_OPTION_SET_SHIFT) | + ((priority & PNP_OPTION_PRIORITY_MASK) << + PNP_OPTION_PRIORITY_SHIFT); + + dev->num_dependent_sets++; + + return flags; +} + +char *pnp_option_priority_name(struct pnp_option *option); +void dbg_pnp_show_option(struct pnp_dev *dev, struct pnp_option *option); + void pnp_init_resources(struct pnp_dev *dev); void pnp_fixup_device(struct pnp_dev *dev); -void pnp_free_option(struct pnp_option *option); +void pnp_free_options(struct pnp_dev *dev); int __pnp_add_device(struct pnp_dev *dev); void __pnp_remove_device(struct pnp_dev *dev); diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 7182da92aec3..a411582bcd72 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -118,10 +118,9 @@ static void pnp_release_device(struct device *dmdev) { struct pnp_dev *dev = to_pnp_dev(dmdev); - pnp_free_option(dev->independent); - pnp_free_option(dev->dependent); pnp_free_ids(dev); pnp_free_resources(dev); + pnp_free_options(dev); kfree(dev); } @@ -135,6 +134,7 @@ struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid return NULL; INIT_LIST_HEAD(&dev->resources); + INIT_LIST_HEAD(&dev->options); dev->protocol = protocol; dev->number = id; dev->dma_mask = DMA_24BIT_MASK; diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 7a9fb5544b80..a876ecf7028c 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -3,6 +3,8 @@ * * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela * Copyright 2002 Adam Belay + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas */ #include @@ -184,39 +186,22 @@ static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space, } static void pnp_print_option(pnp_info_buffer_t * buffer, char *space, - struct pnp_option *option, int dep) + struct pnp_option *option) { - char *s; - struct pnp_port *port; - struct pnp_irq *irq; - struct pnp_dma *dma; - struct pnp_mem *mem; - - if (dep) { - switch (option->priority) { - case PNP_RES_PRIORITY_PREFERRED: - s = "preferred"; - break; - case PNP_RES_PRIORITY_ACCEPTABLE: - s = "acceptable"; - break; - case PNP_RES_PRIORITY_FUNCTIONAL: - s = "functional"; - break; - default: - s = "invalid"; - } - pnp_printf(buffer, "Dependent: %02i - Priority %s\n", dep, s); + switch (option->type) { + case IORESOURCE_IO: + pnp_print_port(buffer, space, &option->u.port); + break; + case IORESOURCE_MEM: + pnp_print_mem(buffer, space, &option->u.mem); + break; + case IORESOURCE_IRQ: + pnp_print_irq(buffer, space, &option->u.irq); + break; + case IORESOURCE_DMA: + pnp_print_dma(buffer, space, &option->u.dma); + break; } - - for (port = option->port; port; port = port->next) - pnp_print_port(buffer, space, port); - for (irq = option->irq; irq; irq = irq->next) - pnp_print_irq(buffer, space, irq); - for (dma = option->dma; dma; dma = dma->next) - pnp_print_dma(buffer, space, dma); - for (mem = option->mem; mem; mem = mem->next) - pnp_print_mem(buffer, space, mem); } static ssize_t pnp_show_options(struct device *dmdev, @@ -224,9 +209,9 @@ static ssize_t pnp_show_options(struct device *dmdev, { struct pnp_dev *dev = to_pnp_dev(dmdev); pnp_info_buffer_t *buffer; - struct pnp_option *independent = dev->independent; - struct pnp_option *dependent = dev->dependent; - int ret, dep = 1; + struct pnp_option *option; + int ret, dep = 0, set = 0; + char *indent; buffer = pnp_alloc(sizeof(pnp_info_buffer_t)); if (!buffer) @@ -235,14 +220,24 @@ static ssize_t pnp_show_options(struct device *dmdev, buffer->len = PAGE_SIZE; buffer->buffer = buf; buffer->curr = buffer->buffer; - if (independent) - pnp_print_option(buffer, "", independent, 0); - while (dependent) { - pnp_print_option(buffer, " ", dependent, dep); - dependent = dependent->next; - dep++; + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option)) { + indent = " "; + if (!dep || pnp_option_set(option) != set) { + set = pnp_option_set(option); + dep = 1; + pnp_printf(buffer, "Dependent: %02i - " + "Priority %s\n", set, + pnp_option_priority_name(option)); + } + } else { + dep = 0; + indent = ""; + } + pnp_print_option(buffer, indent, option); } + ret = (buffer->curr - buf); kfree(buffer); return ret; diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index 53cc4d6133e6..101a835e8759 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -429,7 +429,7 @@ static struct pnp_dev *__init isapnp_parse_device(struct pnp_card *card, * Add IRQ resource to resources list. */ static void __init isapnp_parse_irq_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[3]; @@ -446,27 +446,27 @@ static void __init isapnp_parse_irq_resource(struct pnp_dev *dev, if (size > 2) flags = tmp[2]; - pnp_register_irq_resource(dev, option, &map, flags); + pnp_register_irq_resource(dev, option_flags, &map, flags); } /* * Add DMA resource to resources list. */ static void __init isapnp_parse_dma_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[2]; isapnp_peek(tmp, size); - pnp_register_dma_resource(dev, option, tmp[0], tmp[1]); + pnp_register_dma_resource(dev, option_flags, tmp[0], tmp[1]); } /* * Add port resource to resources list. */ static void __init isapnp_parse_port_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[7]; @@ -479,14 +479,15 @@ static void __init isapnp_parse_port_resource(struct pnp_dev *dev, align = tmp[5]; len = tmp[6]; flags = tmp[0] ? IORESOURCE_IO_16BIT_ADDR : 0; - pnp_register_port_resource(dev, option, min, max, align, len, flags); + pnp_register_port_resource(dev, option_flags, + min, max, align, len, flags); } /* * Add fixed port resource to resources list. */ static void __init isapnp_parse_fixed_port_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[3]; @@ -495,7 +496,7 @@ static void __init isapnp_parse_fixed_port_resource(struct pnp_dev *dev, isapnp_peek(tmp, size); base = (tmp[1] << 8) | tmp[0]; len = tmp[2]; - pnp_register_port_resource(dev, option, base, base, 0, len, + pnp_register_port_resource(dev, option_flags, base, base, 0, len, IORESOURCE_IO_FIXED); } @@ -503,7 +504,7 @@ static void __init isapnp_parse_fixed_port_resource(struct pnp_dev *dev, * Add memory resource to resources list. */ static void __init isapnp_parse_mem_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[9]; @@ -516,14 +517,15 @@ static void __init isapnp_parse_mem_resource(struct pnp_dev *dev, align = (tmp[6] << 8) | tmp[5]; len = ((tmp[8] << 8) | tmp[7]) << 8; flags = tmp[0]; - pnp_register_mem_resource(dev, option, min, max, align, len, flags); + pnp_register_mem_resource(dev, option_flags, + min, max, align, len, flags); } /* * Add 32-bit memory resource to resources list. */ static void __init isapnp_parse_mem32_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[17]; @@ -536,14 +538,15 @@ static void __init isapnp_parse_mem32_resource(struct pnp_dev *dev, align = (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; len = (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; flags = tmp[0]; - pnp_register_mem_resource(dev, option, min, max, align, len, flags); + pnp_register_mem_resource(dev, option_flags, + min, max, align, len, flags); } /* * Add 32-bit fixed memory resource to resources list. */ static void __init isapnp_parse_fixed_mem32_resource(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, int size) { unsigned char tmp[9]; @@ -554,7 +557,7 @@ static void __init isapnp_parse_fixed_mem32_resource(struct pnp_dev *dev, base = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; len = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; flags = tmp[0]; - pnp_register_mem_resource(dev, option, base, base, 0, len, flags); + pnp_register_mem_resource(dev, option_flags, base, base, 0, len, flags); } /* @@ -584,18 +587,14 @@ static int __init isapnp_create_device(struct pnp_card *card, { int number = 0, skip = 0, priority, compat = 0; unsigned char type, tmp[17]; - struct pnp_option *option, *option_independent; + unsigned int option_flags; struct pnp_dev *dev; u32 eisa_id; char id[8]; if ((dev = isapnp_parse_device(card, size, number++)) == NULL) return 1; - option_independent = option = pnp_register_independent_option(dev); - if (!option) { - kfree(dev); - return 1; - } + option_flags = 0; pnp_add_card_device(card, dev); while (1) { @@ -612,12 +611,7 @@ static int __init isapnp_create_device(struct pnp_card *card, return 1; size = 0; skip = 0; - option = pnp_register_independent_option(dev); - option_independent = option; - if (!option) { - kfree(dev); - return 1; - } + option_flags = 0; pnp_add_card_device(card, dev); } else { skip = 1; @@ -638,13 +632,13 @@ static int __init isapnp_create_device(struct pnp_card *card, case _STAG_IRQ: if (size < 2 || size > 3) goto __skip; - isapnp_parse_irq_resource(dev, option, size); + isapnp_parse_irq_resource(dev, option_flags, size); size = 0; break; case _STAG_DMA: if (size != 2) goto __skip; - isapnp_parse_dma_resource(dev, option, size); + isapnp_parse_dma_resource(dev, option_flags, size); size = 0; break; case _STAG_STARTDEP: @@ -656,29 +650,24 @@ static int __init isapnp_create_device(struct pnp_card *card, priority = tmp[0]; size = 0; } - option = pnp_register_dependent_option(dev, priority); - if (!option) - return 1; + option_flags = pnp_new_dependent_set(dev, priority); break; case _STAG_ENDDEP: if (size != 0) goto __skip; - if (option_independent == option) - dev_warn(&dev->dev, "missing " - "_STAG_STARTDEP tag\n"); - option = option_independent; - dev_dbg(&dev->dev, "end dependent options\n"); + option_flags = 0; break; case _STAG_IOPORT: if (size != 7) goto __skip; - isapnp_parse_port_resource(dev, option, size); + isapnp_parse_port_resource(dev, option_flags, size); size = 0; break; case _STAG_FIXEDIO: if (size != 3) goto __skip; - isapnp_parse_fixed_port_resource(dev, option, size); + isapnp_parse_fixed_port_resource(dev, option_flags, + size); size = 0; break; case _STAG_VENDOR: @@ -686,7 +675,7 @@ static int __init isapnp_create_device(struct pnp_card *card, case _LTAG_MEMRANGE: if (size != 9) goto __skip; - isapnp_parse_mem_resource(dev, option, size); + isapnp_parse_mem_resource(dev, option_flags, size); size = 0; break; case _LTAG_ANSISTR: @@ -701,13 +690,14 @@ static int __init isapnp_create_device(struct pnp_card *card, case _LTAG_MEM32RANGE: if (size != 17) goto __skip; - isapnp_parse_mem32_resource(dev, option, size); + isapnp_parse_mem32_resource(dev, option_flags, size); size = 0; break; case _LTAG_FIXEDMEM32RANGE: if (size != 9) goto __skip; - isapnp_parse_fixed_mem32_resource(dev, option, size); + isapnp_parse_fixed_mem32_resource(dev, option_flags, + size); size = 0; break; case _STAG_END: diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index a20accb5ef8f..b526eaad3f6c 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -3,6 +3,8 @@ * * based on isapnp.c resource management (c) Jaroslav Kysela * Copyright 2003 Adam Belay + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas */ #include @@ -228,102 +230,51 @@ static void pnp_clean_resource_table(struct pnp_dev *dev) /** * pnp_assign_resources - assigns resources to the device based on the specified dependent number * @dev: pointer to the desired device - * @depnum: the dependent function number - * - * Only set depnum to 0 if the device does not have dependent options. + * @set: the dependent function number */ -static int pnp_assign_resources(struct pnp_dev *dev, int depnum) +static int pnp_assign_resources(struct pnp_dev *dev, int set) { - struct pnp_port *port; - struct pnp_mem *mem; - struct pnp_irq *irq; - struct pnp_dma *dma; + struct pnp_option *option; int nport = 0, nmem = 0, nirq = 0, ndma = 0; + int ret = 0; - dbg_pnp_show_resources(dev, "before pnp_assign_resources"); + dev_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set); mutex_lock(&pnp_res_mutex); pnp_clean_resource_table(dev); - if (dev->independent) { - dev_dbg(&dev->dev, "assigning independent options\n"); - port = dev->independent->port; - mem = dev->independent->mem; - irq = dev->independent->irq; - dma = dev->independent->dma; - while (port) { - if (pnp_assign_port(dev, port, nport) < 0) - goto fail; - nport++; - port = port->next; - } - while (mem) { - if (pnp_assign_mem(dev, mem, nmem) < 0) - goto fail; - nmem++; - mem = mem->next; - } - while (irq) { - if (pnp_assign_irq(dev, irq, nirq) < 0) - goto fail; - nirq++; - irq = irq->next; - } - while (dma) { - if (pnp_assign_dma(dev, dma, ndma) < 0) - goto fail; - ndma++; - dma = dma->next; - } - } - if (depnum) { - struct pnp_option *dep; - int i; - - dev_dbg(&dev->dev, "assigning dependent option %d\n", depnum); - for (i = 1, dep = dev->dependent; i < depnum; - i++, dep = dep->next) - if (!dep) - goto fail; - port = dep->port; - mem = dep->mem; - irq = dep->irq; - dma = dep->dma; - while (port) { - if (pnp_assign_port(dev, port, nport) < 0) - goto fail; - nport++; - port = port->next; - } - while (mem) { - if (pnp_assign_mem(dev, mem, nmem) < 0) - goto fail; - nmem++; - mem = mem->next; - } - while (irq) { - if (pnp_assign_irq(dev, irq, nirq) < 0) - goto fail; - nirq++; - irq = irq->next; - } - while (dma) { - if (pnp_assign_dma(dev, dma, ndma) < 0) - goto fail; - ndma++; - dma = dma->next; + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option) && + pnp_option_set(option) != set) + continue; + + switch (option->type) { + case IORESOURCE_IO: + ret = pnp_assign_port(dev, &option->u.port, nport++); + break; + case IORESOURCE_MEM: + ret = pnp_assign_mem(dev, &option->u.mem, nmem++); + break; + case IORESOURCE_IRQ: + ret = pnp_assign_irq(dev, &option->u.irq, nirq++); + break; + case IORESOURCE_DMA: + ret = pnp_assign_dma(dev, &option->u.dma, ndma++); + break; + default: + ret = -EINVAL; + break; } - } else if (dev->dependent) - goto fail; - - mutex_unlock(&pnp_res_mutex); - dbg_pnp_show_resources(dev, "after pnp_assign_resources"); - return 1; + if (ret < 0) + break; + } -fail: - pnp_clean_resource_table(dev); mutex_unlock(&pnp_res_mutex); - dbg_pnp_show_resources(dev, "after pnp_assign_resources (failed)"); - return 0; + if (ret < 0) { + dev_dbg(&dev->dev, "pnp_assign_resources failed (%d)\n", ret); + pnp_clean_resource_table(dev); + } else + dbg_pnp_show_resources(dev, "pnp_assign_resources succeeded"); + return ret; } /** @@ -332,29 +283,25 @@ fail: */ int pnp_auto_config_dev(struct pnp_dev *dev) { - struct pnp_option *dep; - int i = 1; + int i, ret; if (!pnp_can_configure(dev)) { dev_dbg(&dev->dev, "configuration not supported\n"); return -ENODEV; } - if (!dev->dependent) { - if (pnp_assign_resources(dev, 0)) + ret = pnp_assign_resources(dev, 0); + if (ret == 0) + return 0; + + for (i = 1; i < dev->num_dependent_sets; i++) { + ret = pnp_assign_resources(dev, i); + if (ret == 0) return 0; - } else { - dep = dev->dependent; - do { - if (pnp_assign_resources(dev, i)) - return 0; - dep = dep->next; - i++; - } while (dep); } dev_err(&dev->dev, "unable to assign resources\n"); - return -EBUSY; + return ret; } /** diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index e114b3d2b933..c2f59f4d20bc 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -407,7 +407,7 @@ int pnpacpi_parse_allocated_resource(struct pnp_dev *dev) } static __init void pnpacpi_parse_dma_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_dma *p) { int i; @@ -420,11 +420,11 @@ static __init void pnpacpi_parse_dma_option(struct pnp_dev *dev, map |= 1 << p->channels[i]; flags = dma_flags(p->type, p->bus_master, p->transfer); - pnp_register_dma_resource(dev, option, map, flags); + pnp_register_dma_resource(dev, option_flags, map, flags); } static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_irq *p) { int i; @@ -440,11 +440,11 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev, __set_bit(p->interrupts[i], map.bits); flags = irq_flags(p->triggering, p->polarity, p->sharable); - pnp_register_irq_resource(dev, option, &map, flags); + pnp_register_irq_resource(dev, option_flags, &map, flags); } static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_extended_irq *p) { int i; @@ -467,11 +467,11 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev, } flags = irq_flags(p->triggering, p->polarity, p->sharable); - pnp_register_irq_resource(dev, option, &map, flags); + pnp_register_irq_resource(dev, option_flags, &map, flags); } static __init void pnpacpi_parse_port_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_io *io) { unsigned char flags = 0; @@ -481,23 +481,23 @@ static __init void pnpacpi_parse_port_option(struct pnp_dev *dev, if (io->io_decode == ACPI_DECODE_16) flags = IORESOURCE_IO_16BIT_ADDR; - pnp_register_port_resource(dev, option, io->minimum, io->maximum, + pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum, io->alignment, io->address_length, flags); } static __init void pnpacpi_parse_fixed_port_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_fixed_io *io) { if (io->address_length == 0) return; - pnp_register_port_resource(dev, option, io->address, io->address, 0, - io->address_length, IORESOURCE_IO_FIXED); + pnp_register_port_resource(dev, option_flags, io->address, io->address, + 0, io->address_length, IORESOURCE_IO_FIXED); } static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_memory24 *p) { unsigned char flags = 0; @@ -507,12 +507,12 @@ static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev, if (p->write_protect == ACPI_READ_WRITE_MEMORY) flags = IORESOURCE_MEM_WRITEABLE; - pnp_register_mem_resource(dev, option, p->minimum, p->maximum, + pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum, p->alignment, p->address_length, flags); } static __init void pnpacpi_parse_mem32_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_memory32 *p) { unsigned char flags = 0; @@ -522,12 +522,12 @@ static __init void pnpacpi_parse_mem32_option(struct pnp_dev *dev, if (p->write_protect == ACPI_READ_WRITE_MEMORY) flags = IORESOURCE_MEM_WRITEABLE; - pnp_register_mem_resource(dev, option, p->minimum, p->maximum, + pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum, p->alignment, p->address_length, flags); } static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource_fixed_memory32 *p) { unsigned char flags = 0; @@ -537,12 +537,12 @@ static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_dev *dev, if (p->write_protect == ACPI_READ_WRITE_MEMORY) flags = IORESOURCE_MEM_WRITEABLE; - pnp_register_mem_resource(dev, option, p->address, p->address, + pnp_register_mem_resource(dev, option_flags, p->address, p->address, 0, p->address_length, flags); } static __init void pnpacpi_parse_address_option(struct pnp_dev *dev, - struct pnp_option *option, + unsigned int option_flags, struct acpi_resource *r) { struct acpi_resource_address64 addr, *p = &addr; @@ -562,18 +562,18 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev, if (p->resource_type == ACPI_MEMORY_RANGE) { if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) flags = IORESOURCE_MEM_WRITEABLE; - pnp_register_mem_resource(dev, option, p->minimum, p->minimum, - 0, p->address_length, flags); + pnp_register_mem_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + flags); } else if (p->resource_type == ACPI_IO_RANGE) - pnp_register_port_resource(dev, option, p->minimum, p->minimum, - 0, p->address_length, + pnp_register_port_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, IORESOURCE_IO_FIXED); } struct acpipnp_parse_option_s { - struct pnp_option *option; - struct pnp_option *option_independent; struct pnp_dev *dev; + unsigned int option_flags; }; static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, @@ -582,15 +582,15 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, int priority; struct acpipnp_parse_option_s *parse_data = data; struct pnp_dev *dev = parse_data->dev; - struct pnp_option *option = parse_data->option; + unsigned int option_flags = parse_data->option_flags; switch (res->type) { case ACPI_RESOURCE_TYPE_IRQ: - pnpacpi_parse_irq_option(dev, option, &res->data.irq); + pnpacpi_parse_irq_option(dev, option_flags, &res->data.irq); break; case ACPI_RESOURCE_TYPE_DMA: - pnpacpi_parse_dma_option(dev, option, &res->data.dma); + pnpacpi_parse_dma_option(dev, option_flags, &res->data.dma); break; case ACPI_RESOURCE_TYPE_START_DEPENDENT: @@ -610,31 +610,19 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, priority = PNP_RES_PRIORITY_INVALID; break; } - /* TBD: Consider performance/robustness bits */ - option = pnp_register_dependent_option(dev, priority); - if (!option) - return AE_ERROR; - parse_data->option = option; + parse_data->option_flags = pnp_new_dependent_set(dev, priority); break; case ACPI_RESOURCE_TYPE_END_DEPENDENT: - /*only one EndDependentFn is allowed */ - if (!parse_data->option_independent) { - dev_warn(&dev->dev, "more than one EndDependentFn " - "in _PRS\n"); - return AE_ERROR; - } - parse_data->option = parse_data->option_independent; - parse_data->option_independent = NULL; - dev_dbg(&dev->dev, "end dependent options\n"); + parse_data->option_flags = 0; break; case ACPI_RESOURCE_TYPE_IO: - pnpacpi_parse_port_option(dev, option, &res->data.io); + pnpacpi_parse_port_option(dev, option_flags, &res->data.io); break; case ACPI_RESOURCE_TYPE_FIXED_IO: - pnpacpi_parse_fixed_port_option(dev, option, + pnpacpi_parse_fixed_port_option(dev, option_flags, &res->data.fixed_io); break; @@ -643,29 +631,31 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, break; case ACPI_RESOURCE_TYPE_MEMORY24: - pnpacpi_parse_mem24_option(dev, option, &res->data.memory24); + pnpacpi_parse_mem24_option(dev, option_flags, + &res->data.memory24); break; case ACPI_RESOURCE_TYPE_MEMORY32: - pnpacpi_parse_mem32_option(dev, option, &res->data.memory32); + pnpacpi_parse_mem32_option(dev, option_flags, + &res->data.memory32); break; case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - pnpacpi_parse_fixed_mem32_option(dev, option, + pnpacpi_parse_fixed_mem32_option(dev, option_flags, &res->data.fixed_memory32); break; case ACPI_RESOURCE_TYPE_ADDRESS16: case ACPI_RESOURCE_TYPE_ADDRESS32: case ACPI_RESOURCE_TYPE_ADDRESS64: - pnpacpi_parse_address_option(dev, option, res); + pnpacpi_parse_address_option(dev, option_flags, res); break; case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - pnpacpi_parse_ext_irq_option(dev, option, + pnpacpi_parse_ext_irq_option(dev, option_flags, &res->data.extended_irq); break; @@ -689,12 +679,9 @@ int __init pnpacpi_parse_resource_option_data(struct pnp_dev *dev) dev_dbg(&dev->dev, "parse resource options\n"); - parse_data.option = pnp_register_independent_option(dev); - if (!parse_data.option) - return -ENOMEM; - - parse_data.option_independent = parse_data.option; parse_data.dev = dev; + parse_data.option_flags = 0; + status = acpi_walk_resources(handle, METHOD_NAME__PRS, pnpacpi_option_resource, &parse_data); diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index db23ba78d39c..ca567671379e 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -216,7 +216,7 @@ len_err: static __init void pnpbios_parse_mem_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { resource_size_t min, max, align, len; unsigned char flags; @@ -226,12 +226,13 @@ static __init void pnpbios_parse_mem_option(struct pnp_dev *dev, align = (p[9] << 8) | p[8]; len = ((p[11] << 8) | p[10]) << 8; flags = p[3]; - pnp_register_mem_resource(dev, option, min, max, align, len, flags); + pnp_register_mem_resource(dev, option_flags, min, max, align, len, + flags); } static __init void pnpbios_parse_mem32_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { resource_size_t min, max, align, len; unsigned char flags; @@ -241,12 +242,13 @@ static __init void pnpbios_parse_mem32_option(struct pnp_dev *dev, align = (p[15] << 24) | (p[14] << 16) | (p[13] << 8) | p[12]; len = (p[19] << 24) | (p[18] << 16) | (p[17] << 8) | p[16]; flags = p[3]; - pnp_register_mem_resource(dev, option, min, max, align, len, flags); + pnp_register_mem_resource(dev, option_flags, min, max, align, len, + flags); } static __init void pnpbios_parse_fixed_mem32_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { resource_size_t base, len; unsigned char flags; @@ -254,12 +256,12 @@ static __init void pnpbios_parse_fixed_mem32_option(struct pnp_dev *dev, base = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; len = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; flags = p[3]; - pnp_register_mem_resource(dev, option, base, base, 0, len, flags); + pnp_register_mem_resource(dev, option_flags, base, base, 0, len, flags); } static __init void pnpbios_parse_irq_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { unsigned long bits; pnp_irq_mask_t map; @@ -273,19 +275,19 @@ static __init void pnpbios_parse_irq_option(struct pnp_dev *dev, if (size > 2) flags = p[3]; - pnp_register_irq_resource(dev, option, &map, flags); + pnp_register_irq_resource(dev, option_flags, &map, flags); } static __init void pnpbios_parse_dma_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { - pnp_register_dma_resource(dev, option, p[1], p[2]); + pnp_register_dma_resource(dev, option_flags, p[1], p[2]); } static __init void pnpbios_parse_port_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { resource_size_t min, max, align, len; unsigned char flags; @@ -295,38 +297,35 @@ static __init void pnpbios_parse_port_option(struct pnp_dev *dev, align = p[6]; len = p[7]; flags = p[1] ? IORESOURCE_IO_16BIT_ADDR : 0; - pnp_register_port_resource(dev, option, min, max, align, len, flags); + pnp_register_port_resource(dev, option_flags, min, max, align, len, + flags); } static __init void pnpbios_parse_fixed_port_option(struct pnp_dev *dev, unsigned char *p, int size, - struct pnp_option *option) + unsigned int option_flags) { resource_size_t base, len; base = (p[2] << 8) | p[1]; len = p[3]; - pnp_register_port_resource(dev, option, base, base, 0, len, + pnp_register_port_resource(dev, option_flags, base, base, 0, len, IORESOURCE_IO_FIXED); } static __init unsigned char * pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, - struct pnp_dev *dev) + struct pnp_dev *dev) { unsigned int len, tag; int priority; - struct pnp_option *option, *option_independent; + unsigned int option_flags; if (!p) return NULL; dev_dbg(&dev->dev, "parse resource options\n"); - - option_independent = option = pnp_register_independent_option(dev); - if (!option) - return NULL; - + option_flags = 0; while ((char *)p < (char *)end) { /* determine the type of tag */ @@ -343,37 +342,38 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, case LARGE_TAG_MEM: if (len != 9) goto len_err; - pnpbios_parse_mem_option(dev, p, len, option); + pnpbios_parse_mem_option(dev, p, len, option_flags); break; case LARGE_TAG_MEM32: if (len != 17) goto len_err; - pnpbios_parse_mem32_option(dev, p, len, option); + pnpbios_parse_mem32_option(dev, p, len, option_flags); break; case LARGE_TAG_FIXEDMEM32: if (len != 9) goto len_err; - pnpbios_parse_fixed_mem32_option(dev, p, len, option); + pnpbios_parse_fixed_mem32_option(dev, p, len, + option_flags); break; case SMALL_TAG_IRQ: if (len < 2 || len > 3) goto len_err; - pnpbios_parse_irq_option(dev, p, len, option); + pnpbios_parse_irq_option(dev, p, len, option_flags); break; case SMALL_TAG_DMA: if (len != 2) goto len_err; - pnpbios_parse_dma_option(dev, p, len, option); + pnpbios_parse_dma_option(dev, p, len, option_flags); break; case SMALL_TAG_PORT: if (len != 7) goto len_err; - pnpbios_parse_port_option(dev, p, len, option); + pnpbios_parse_port_option(dev, p, len, option_flags); break; case SMALL_TAG_VENDOR: @@ -383,7 +383,8 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, case SMALL_TAG_FIXEDPORT: if (len != 3) goto len_err; - pnpbios_parse_fixed_port_option(dev, p, len, option); + pnpbios_parse_fixed_port_option(dev, p, len, + option_flags); break; case SMALL_TAG_STARTDEP: @@ -392,19 +393,13 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, priority = PNP_RES_PRIORITY_ACCEPTABLE; if (len > 0) priority = p[1]; - option = pnp_register_dependent_option(dev, priority); - if (!option) - return NULL; + option_flags = pnp_new_dependent_set(dev, priority); break; case SMALL_TAG_ENDDEP: if (len != 0) goto len_err; - if (option_independent == option) - dev_warn(&dev->dev, "missing " - "SMALL_TAG_STARTDEP tag\n"); - option = option_independent; - dev_dbg(&dev->dev, "end dependent options\n"); + option_flags = 0; break; case SMALL_TAG_END: diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index e8515ce0d296..55f55ed72dc7 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -5,6 +5,8 @@ * when building up the resource structure for the first time. * * Copyright (c) 2000 Peter Denison + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas * * Heavily based on PCI quirks handling which is * @@ -20,189 +22,207 @@ #include #include "base.h" +static void quirk_awe32_add_ports(struct pnp_dev *dev, + struct pnp_option *option, + unsigned int offset) +{ + struct pnp_option *new_option; + + new_option = kmalloc(sizeof(struct pnp_option), GFP_KERNEL); + if (!new_option) { + dev_err(&dev->dev, "couldn't add ioport region to option set " + "%d\n", pnp_option_set(option)); + return; + } + + *new_option = *option; + new_option->u.port.min += offset; + new_option->u.port.max += offset; + list_add(&new_option->list, &option->list); + + dev_info(&dev->dev, "added ioport region %#llx-%#llx to set %d\n", + (unsigned long long) new_option->u.port.min, + (unsigned long long) new_option->u.port.max, + pnp_option_set(option)); +} + static void quirk_awe32_resources(struct pnp_dev *dev) { - struct pnp_port *port, *port2, *port3; - struct pnp_option *res = dev->dependent; + struct pnp_option *option; + unsigned int set = ~0; /* - * Unfortunately the isapnp_add_port_resource is too tightly bound - * into the PnP discovery sequence, and cannot be used. Link in the - * two extra ports (at offset 0x400 and 0x800 from the one given) by - * hand. + * Add two extra ioport regions (at offset 0x400 and 0x800 from the + * one given) to every dependent option set. */ - for (; res; res = res->next) { - port2 = pnp_alloc(sizeof(struct pnp_port)); - if (!port2) - return; - port3 = pnp_alloc(sizeof(struct pnp_port)); - if (!port3) { - kfree(port2); - return; + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option) && + pnp_option_set(option) != set) { + set = pnp_option_set(option); + quirk_awe32_add_ports(dev, option, 0x800); + quirk_awe32_add_ports(dev, option, 0x400); } - port = res->port; - memcpy(port2, port, sizeof(struct pnp_port)); - memcpy(port3, port, sizeof(struct pnp_port)); - port->next = port2; - port2->next = port3; - port2->min += 0x400; - port2->max += 0x400; - port3->min += 0x800; - port3->max += 0x800; - dev_info(&dev->dev, - "AWE32 quirk - added ioports 0x%lx and 0x%lx\n", - (unsigned long)port2->min, - (unsigned long)port3->min); } } static void quirk_cmi8330_resources(struct pnp_dev *dev) { - struct pnp_option *res = dev->dependent; - unsigned long tmp; - - for (; res; res = res->next) { - - struct pnp_irq *irq; - struct pnp_dma *dma; + struct pnp_option *option; + struct pnp_irq *irq; + struct pnp_dma *dma; - for (irq = res->irq; irq; irq = irq->next) { - /* Valid irqs are 5, 7, 10 */ - tmp = 0x04A0; - bitmap_copy(irq->map.bits, &tmp, 16); - } + list_for_each_entry(option, &dev->options, list) { + if (!pnp_option_is_dependent(option)) + continue; - for (dma = res->dma; dma; dma = dma->next) { - /* Valid 8bit dma channels are 1,3 */ + if (option->type == IORESOURCE_IRQ) { + irq = &option->u.irq; + bitmap_zero(irq->map.bits, PNP_IRQ_NR); + __set_bit(5, irq->map.bits); + __set_bit(7, irq->map.bits); + __set_bit(10, irq->map.bits); + dev_info(&dev->dev, "set possible IRQs in " + "option set %d to 5, 7, 10\n", + pnp_option_set(option)); + } else if (option->type == IORESOURCE_DMA) { + dma = &option->u.dma; if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == - IORESOURCE_DMA_8BIT) - dma->map = 0x000A; + IORESOURCE_DMA_8BIT && + dma->map != 0x0A) { + dev_info(&dev->dev, "changing possible " + "DMA channel mask in option set %d " + "from %#02x to 0x0A (1, 3)\n", + pnp_option_set(option), dma->map); + dma->map = 0x0A; + } } } - dev_info(&dev->dev, "CMI8330 quirk - forced possible IRQs to 5, 7, 10 " - "and DMA channels to 1, 3\n"); } static void quirk_sb16audio_resources(struct pnp_dev *dev) { + struct pnp_option *option; + unsigned int prev_option_flags = ~0, n = 0; struct pnp_port *port; - struct pnp_option *res = dev->dependent; - int changed = 0; /* - * The default range on the mpu port for these devices is 0x388-0x388. + * The default range on the OPL port for these devices is 0x388-0x388. * Here we increase that range so that two such cards can be * auto-configured. */ + list_for_each_entry(option, &dev->options, list) { + if (prev_option_flags != option->flags) { + prev_option_flags = option->flags; + n = 0; + } - for (; res; res = res->next) { - port = res->port; - if (!port) - continue; - port = port->next; - if (!port) - continue; - port = port->next; - if (!port) - continue; - if (port->min != port->max) - continue; - port->max += 0x70; - changed = 1; + if (pnp_option_is_dependent(option) && + option->type == IORESOURCE_IO) { + n++; + port = &option->u.port; + if (n == 3 && port->min == port->max) { + port->max += 0x70; + dev_info(&dev->dev, "increased option port " + "range from %#llx-%#llx to " + "%#llx-%#llx\n", + (unsigned long long) port->min, + (unsigned long long) port->min, + (unsigned long long) port->min, + (unsigned long long) port->max); + } + } } - if (changed) - dev_info(&dev->dev, "SB audio device quirk - increased port range\n"); } -static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) +static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, + unsigned int set) { - struct pnp_option *head = NULL; - struct pnp_option *prev = NULL; - struct pnp_option *res; - - /* - * Build a functional IRQ-optional variant of each MPU option. - */ - - for (res = dev->dependent; res; res = res->next) { - struct pnp_option *curr; - struct pnp_port *port; - struct pnp_port *copy_port; - struct pnp_irq *irq; - struct pnp_irq *copy_irq; - - port = res->port; - irq = res->irq; - if (!port || !irq) - continue; + struct pnp_option *tail = NULL, *first_new_option = NULL; + struct pnp_option *option, *new_option; + unsigned int flags; - copy_port = pnp_alloc(sizeof *copy_port); - if (!copy_port) - break; - - copy_irq = pnp_alloc(sizeof *copy_irq); - if (!copy_irq) { - kfree(copy_port); - break; - } + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option)) + tail = option; + } + if (!tail) { + dev_err(&dev->dev, "no dependent option sets\n"); + return NULL; + } - *copy_port = *port; - copy_port->next = NULL; + flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option) && + pnp_option_set(option) == set) { + new_option = kmalloc(sizeof(struct pnp_option), + GFP_KERNEL); + if (!new_option) { + dev_err(&dev->dev, "couldn't clone dependent " + "set %d\n", set); + return NULL; + } - *copy_irq = *irq; - copy_irq->flags |= IORESOURCE_IRQ_OPTIONAL; - copy_irq->next = NULL; + *new_option = *option; + new_option->flags = flags; + if (!first_new_option) + first_new_option = new_option; - curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); - if (!curr) { - kfree(copy_port); - kfree(copy_irq); - break; + list_add(&new_option->list, &tail->list); + tail = new_option; } - curr->port = copy_port; - curr->irq = copy_irq; - - if (prev) - prev->next = curr; - else - head = curr; - prev = curr; } - if (head) - dev_info(&dev->dev, "adding IRQ-optional MPU options\n"); - return head; + return first_new_option; } -static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) + +static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) { - struct pnp_option *res; + struct pnp_option *new_option; + unsigned int num_sets, i, set; struct pnp_irq *irq; - res = dev->independent; - if (!res) - return; + num_sets = dev->num_dependent_sets; + for (i = 0; i < num_sets; i++) { + new_option = pnp_clone_dependent_set(dev, i); + if (!new_option) + return; - irq = res->irq; - if (!irq || irq->next) - return; + set = pnp_option_set(new_option); + while (new_option && pnp_option_set(new_option) == set) { + if (new_option->type == IORESOURCE_IRQ) { + irq = &new_option->u.irq; + irq->flags |= IORESOURCE_IRQ_OPTIONAL; + } + dbg_pnp_show_option(dev, new_option); + new_option = list_entry(new_option->list.next, + struct pnp_option, list); + } - irq->flags |= IORESOURCE_IRQ_OPTIONAL; - dev_info(&dev->dev, "made independent IRQ optional\n"); + dev_info(&dev->dev, "added dependent option set %d (same as " + "set %d except IRQ optional)\n", set, i); + } } -static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) +static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) { - struct pnp_option *res; + struct pnp_option *option; + struct pnp_irq *irq = NULL; + unsigned int independent_irqs = 0; + + list_for_each_entry(option, &dev->options, list) { + if (option->type == IORESOURCE_IRQ && + !pnp_option_is_dependent(option)) { + independent_irqs++; + irq = &option->u.irq; + } + } - res = dev->dependent; - if (!res) + if (independent_irqs != 1) return; - while (res->next) - res = res->next; - - res->next = quirk_isapnp_mpu_options(dev); + irq->flags |= IORESOURCE_IRQ_OPTIONAL; + dev_info(&dev->dev, "made independent IRQ optional\n"); } #include @@ -297,10 +317,10 @@ static struct pnp_fixup pnp_fixups[] = { {"CTL0043", quirk_sb16audio_resources}, {"CTL0044", quirk_sb16audio_resources}, {"CTL0045", quirk_sb16audio_resources}, - /* Add IRQ-less MPU options */ + /* Add IRQ-optional MPU options */ {"ADS7151", quirk_ad1815_mpu_resources}, - {"ADS7181", quirk_isapnp_mpu_resources}, - {"AZT0002", quirk_isapnp_mpu_resources}, + {"ADS7181", quirk_add_irq_optional_dependent_sets}, + {"AZT0002", quirk_add_irq_optional_dependent_sets}, /* PnP resources that might overlap PCI BARs */ {"PNP0c01", quirk_system_pci_resources}, {"PNP0c02", quirk_system_pci_resources}, diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index a795864dc695..d6388970a1a4 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -3,6 +3,8 @@ * * based on isapnp.c resource management (c) Jaroslav Kysela * Copyright 2003 Adam Belay + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas */ #include @@ -28,78 +30,36 @@ static int pnp_reserve_mem[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some * option registration */ -struct pnp_option *pnp_build_option(int priority) +struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type, + unsigned int option_flags) { - struct pnp_option *option = pnp_alloc(sizeof(struct pnp_option)); + struct pnp_option *option; + option = kzalloc(sizeof(struct pnp_option), GFP_KERNEL); if (!option) return NULL; - option->priority = priority & 0xff; - /* make sure the priority is valid */ - if (option->priority > PNP_RES_PRIORITY_FUNCTIONAL) - option->priority = PNP_RES_PRIORITY_INVALID; - - return option; -} - -struct pnp_option *pnp_register_independent_option(struct pnp_dev *dev) -{ - struct pnp_option *option; - - option = pnp_build_option(PNP_RES_PRIORITY_PREFERRED); - - /* this should never happen but if it does we'll try to continue */ - if (dev->independent) - dev_err(&dev->dev, "independent resource already registered\n"); - dev->independent = option; - - dev_dbg(&dev->dev, "new independent option\n"); - return option; -} - -struct pnp_option *pnp_register_dependent_option(struct pnp_dev *dev, - int priority) -{ - struct pnp_option *option; + option->flags = option_flags; + option->type = type; - option = pnp_build_option(priority); - - if (dev->dependent) { - struct pnp_option *parent = dev->dependent; - while (parent->next) - parent = parent->next; - parent->next = option; - } else - dev->dependent = option; - - dev_dbg(&dev->dev, "new dependent option (priority %#x)\n", priority); + list_add_tail(&option->list, &dev->options); return option; } -int pnp_register_irq_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags, pnp_irq_mask_t *map, unsigned char flags) { - struct pnp_irq *irq, *ptr; -#ifdef DEBUG - char buf[PNP_IRQ_NR]; /* hex-encoded, so this is overkill but safe */ -#endif + struct pnp_option *option; + struct pnp_irq *irq; - irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL); - if (!irq) + option = pnp_build_option(dev, IORESOURCE_IRQ, option_flags); + if (!option) return -ENOMEM; + irq = &option->u.irq; irq->map = *map; irq->flags = flags; - ptr = option->irq; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = irq; - else - option->irq = irq; - #ifdef CONFIG_PCI { int i; @@ -110,163 +70,81 @@ int pnp_register_irq_resource(struct pnp_dev *dev, struct pnp_option *option, } #endif -#ifdef DEBUG - bitmap_scnprintf(buf, sizeof(buf), irq->map.bits, PNP_IRQ_NR); - dev_dbg(&dev->dev, " irq bitmask %s flags %#x\n", buf, - irq->flags); -#endif + dbg_pnp_show_option(dev, option); return 0; } -int pnp_register_dma_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags, unsigned char map, unsigned char flags) { - struct pnp_dma *dma, *ptr; + struct pnp_option *option; + struct pnp_dma *dma; - dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL); - if (!dma) + option = pnp_build_option(dev, IORESOURCE_DMA, option_flags); + if (!option) return -ENOMEM; + dma = &option->u.dma; dma->map = map; dma->flags = flags; - ptr = option->dma; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = dma; - else - option->dma = dma; - - dev_dbg(&dev->dev, " dma bitmask %#x flags %#x\n", dma->map, - dma->flags); + dbg_pnp_show_option(dev, option); return 0; } -int pnp_register_port_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_port_resource(struct pnp_dev *dev, unsigned int option_flags, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t size, unsigned char flags) { - struct pnp_port *port, *ptr; + struct pnp_option *option; + struct pnp_port *port; - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) + option = pnp_build_option(dev, IORESOURCE_IO, option_flags); + if (!option) return -ENOMEM; + port = &option->u.port; port->min = min; port->max = max; port->align = align; port->size = size; port->flags = flags; - ptr = option->port; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = port; - else - option->port = port; - - dev_dbg(&dev->dev, " io " - "min %#llx max %#llx align %lld size %lld flags %#x\n", - (unsigned long long) port->min, - (unsigned long long) port->max, - (unsigned long long) port->align, - (unsigned long long) port->size, port->flags); + dbg_pnp_show_option(dev, option); return 0; } -int pnp_register_mem_resource(struct pnp_dev *dev, struct pnp_option *option, +int pnp_register_mem_resource(struct pnp_dev *dev, unsigned int option_flags, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t size, unsigned char flags) { - struct pnp_mem *mem, *ptr; + struct pnp_option *option; + struct pnp_mem *mem; - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) + option = pnp_build_option(dev, IORESOURCE_MEM, option_flags); + if (!option) return -ENOMEM; + mem = &option->u.mem; mem->min = min; mem->max = max; mem->align = align; mem->size = size; mem->flags = flags; - ptr = option->mem; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = mem; - else - option->mem = mem; - - dev_dbg(&dev->dev, " mem " - "min %#llx max %#llx align %lld size %lld flags %#x\n", - (unsigned long long) mem->min, - (unsigned long long) mem->max, - (unsigned long long) mem->align, - (unsigned long long) mem->size, mem->flags); + dbg_pnp_show_option(dev, option); return 0; } -static void pnp_free_port(struct pnp_port *port) -{ - struct pnp_port *next; - - while (port) { - next = port->next; - kfree(port); - port = next; - } -} - -static void pnp_free_irq(struct pnp_irq *irq) -{ - struct pnp_irq *next; - - while (irq) { - next = irq->next; - kfree(irq); - irq = next; - } -} - -static void pnp_free_dma(struct pnp_dma *dma) -{ - struct pnp_dma *next; - - while (dma) { - next = dma->next; - kfree(dma); - dma = next; - } -} - -static void pnp_free_mem(struct pnp_mem *mem) +void pnp_free_options(struct pnp_dev *dev) { - struct pnp_mem *next; - - while (mem) { - next = mem->next; - kfree(mem); - mem = next; - } -} + struct pnp_option *option, *tmp; -void pnp_free_option(struct pnp_option *option) -{ - struct pnp_option *next; - - while (option) { - next = option->next; - pnp_free_port(option->port); - pnp_free_irq(option->irq); - pnp_free_dma(option->dma); - pnp_free_mem(option->mem); + list_for_each_entry_safe(option, tmp, &dev->options, list) { + list_del(&option->list); kfree(option); - option = next; } } @@ -668,66 +546,50 @@ struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, return pnp_res; } -static int pnp_possible_option(struct pnp_option *option, int type, - resource_size_t start, resource_size_t size) +/* + * Determine whether the specified resource is a possible configuration + * for this device. + */ +int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, + resource_size_t size) { - struct pnp_option *tmp; + struct pnp_option *option; struct pnp_port *port; struct pnp_mem *mem; struct pnp_irq *irq; struct pnp_dma *dma; - if (!option) - return 0; + list_for_each_entry(option, &dev->options, list) { + if (option->type != type) + continue; - for (tmp = option; tmp; tmp = tmp->next) { - switch (type) { + switch (option->type) { case IORESOURCE_IO: - for (port = tmp->port; port; port = port->next) { - if (port->min == start && port->size == size) - return 1; - } + port = &option->u.port; + if (port->min == start && port->size == size) + return 1; break; case IORESOURCE_MEM: - for (mem = tmp->mem; mem; mem = mem->next) { - if (mem->min == start && mem->size == size) - return 1; - } + mem = &option->u.mem; + if (mem->min == start && mem->size == size) + return 1; break; case IORESOURCE_IRQ: - for (irq = tmp->irq; irq; irq = irq->next) { - if (start < PNP_IRQ_NR && - test_bit(start, irq->map.bits)) - return 1; - } + irq = &option->u.irq; + if (start < PNP_IRQ_NR && + test_bit(start, irq->map.bits)) + return 1; break; case IORESOURCE_DMA: - for (dma = tmp->dma; dma; dma = dma->next) { - if (dma->map & (1 << start)) - return 1; - } + dma = &option->u.dma; + if (dma->map & (1 << start)) + return 1; break; } } return 0; } - -/* - * Determine whether the specified resource is a possible configuration - * for this device. - */ -int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, - resource_size_t size) -{ - if (pnp_possible_option(dev->independent, type, start, size)) - return 1; - - if (pnp_possible_option(dev->dependent, type, start, size)) - return 1; - - return 0; -} EXPORT_SYMBOL(pnp_possible_config); /* format is: pnp_reserve_irq=irq1[,irq2] .... */ diff --git a/drivers/pnp/support.c b/drivers/pnp/support.c index 0ad42db94884..bbf78ef4ba02 100644 --- a/drivers/pnp/support.c +++ b/drivers/pnp/support.c @@ -2,6 +2,8 @@ * support.c - standard functions for the use of pnp protocol drivers * * Copyright 2003 Adam Belay + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas */ #include @@ -117,3 +119,93 @@ void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc) } #endif } + +char *pnp_option_priority_name(struct pnp_option *option) +{ + switch (pnp_option_priority(option)) { + case PNP_RES_PRIORITY_PREFERRED: + return "preferred"; + case PNP_RES_PRIORITY_ACCEPTABLE: + return "acceptable"; + case PNP_RES_PRIORITY_FUNCTIONAL: + return "functional"; + } + return "invalid"; +} + +void dbg_pnp_show_option(struct pnp_dev *dev, struct pnp_option *option) +{ +#ifdef DEBUG + char buf[128]; + int len = 0, i; + struct pnp_port *port; + struct pnp_mem *mem; + struct pnp_irq *irq; + struct pnp_dma *dma; + + if (pnp_option_is_dependent(option)) + len += snprintf(buf + len, sizeof(buf) - len, + " dependent set %d (%s) ", + pnp_option_set(option), + pnp_option_priority_name(option)); + else + len += snprintf(buf + len, sizeof(buf) - len, " independent "); + + switch (option->type) { + case IORESOURCE_IO: + port = &option->u.port; + len += snprintf(buf + len, sizeof(buf) - len, "io min %#llx " + "max %#llx align %lld size %lld flags %#x", + (unsigned long long) port->min, + (unsigned long long) port->max, + (unsigned long long) port->align, + (unsigned long long) port->size, port->flags); + break; + case IORESOURCE_MEM: + mem = &option->u.mem; + len += snprintf(buf + len, sizeof(buf) - len, "mem min %#llx " + "max %#llx align %lld size %lld flags %#x", + (unsigned long long) mem->min, + (unsigned long long) mem->max, + (unsigned long long) mem->align, + (unsigned long long) mem->size, mem->flags); + break; + case IORESOURCE_IRQ: + irq = &option->u.irq; + len += snprintf(buf + len, sizeof(buf) - len, "irq"); + if (bitmap_empty(irq->map.bits, PNP_IRQ_NR)) + len += snprintf(buf + len, sizeof(buf) - len, + " "); + else { + for (i = 0; i < PNP_IRQ_NR; i++) + if (test_bit(i, irq->map.bits)) + len += snprintf(buf + len, + sizeof(buf) - len, + " %d", i); + } + len += snprintf(buf + len, sizeof(buf) - len, " flags %#x", + irq->flags); + if (irq->flags & IORESOURCE_IRQ_OPTIONAL) + len += snprintf(buf + len, sizeof(buf) - len, + " (optional)"); + break; + case IORESOURCE_DMA: + dma = &option->u.dma; + len += snprintf(buf + len, sizeof(buf) - len, "dma"); + if (!dma->map) + len += snprintf(buf + len, sizeof(buf) - len, + " "); + else { + for (i = 0; i < 8; i++) + if (dma->map & (1 << i)) + len += snprintf(buf + len, + sizeof(buf) - len, + " %d", i); + } + len += snprintf(buf + len, sizeof(buf) - len, " (bitmask %#x) " + "flags %#x", dma->map, dma->flags); + break; + } + dev_dbg(&dev->dev, "%s\n", buf); +#endif +} diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 785126ffcc11..1ce54b63085d 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -1,6 +1,8 @@ /* * Linux Plug and Play Support * Copyright by Adam Belay + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas */ #ifndef _LINUX_PNP_H @@ -249,9 +251,9 @@ struct pnp_dev { int active; int capabilities; - struct pnp_option *independent; - struct pnp_option *dependent; + unsigned int num_dependent_sets; struct list_head resources; + struct list_head options; char name[PNP_NAME_LEN]; /* contains a human-readable name */ int flags; /* used by protocols */ -- cgit v1.2.3 From d442cc44c0db56e84ef6aa244a88427d2efe06cd Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 16 Jul 2008 16:09:06 -0400 Subject: block: Trivial fix for blk_integrity_rq() Fail integrity check gracefully when request does not have a bio attached (BLOCK_PC). Signed-off-by: Martin K. Petersen Signed-off-by: Linus Torvalds --- include/linux/blkdev.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 32a441b05fd5..88d68081a0f1 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -985,6 +985,9 @@ static inline int bdev_integrity_enabled(struct block_device *bdev, int rw) static inline int blk_integrity_rq(struct request *rq) { + if (rq->bio == NULL) + return 0; + return bio_integrity(rq->bio); } -- cgit v1.2.3 From d49747bdfb2ddebea24d1580da55b79d093d48a9 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 9 Oct 2007 12:37:13 -0500 Subject: powerpc/mpc83xx: Power Management support Basic PM support for 83xx. Standby is implemented as sleep. Suspend-to-RAM is implemented as "deep sleep" (with the processor turned off) on 831x. Signed-off-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/Kconfig | 2 +- arch/powerpc/platforms/83xx/Makefile | 1 + arch/powerpc/platforms/83xx/suspend-asm.S | 533 ++++++++++++++++++++++++++++++ arch/powerpc/platforms/83xx/suspend.c | 388 ++++++++++++++++++++++ arch/powerpc/sysdev/fsl_soc.h | 1 + arch/powerpc/sysdev/ipic.c | 71 ++++ include/asm-powerpc/reg.h | 4 + include/linux/fsl_devices.h | 6 + 8 files changed, 1005 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/platforms/83xx/suspend-asm.S create mode 100644 arch/powerpc/platforms/83xx/suspend.c (limited to 'include/linux') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index de88972c5896..50be303e525d 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -199,7 +199,7 @@ config ARCH_HIBERNATION_POSSIBLE config ARCH_SUSPEND_POSSIBLE def_bool y - depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 + depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx config PPC_DCR_NATIVE bool diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile index f331fd7dd836..32c7ad13911c 100644 --- a/arch/powerpc/platforms/83xx/Makefile +++ b/arch/powerpc/platforms/83xx/Makefile @@ -3,6 +3,7 @@ # obj-y := misc.o usb.o obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_SUSPEND) += suspend.o suspend-asm.o obj-$(CONFIG_MPC831x_RDB) += mpc831x_rdb.o obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o diff --git a/arch/powerpc/platforms/83xx/suspend-asm.S b/arch/powerpc/platforms/83xx/suspend-asm.S new file mode 100644 index 000000000000..1930543c98d3 --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend-asm.S @@ -0,0 +1,533 @@ +/* + * Enter and leave deep sleep state on MPC83xx + * + * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. + * Author: Scott Wood + * + * 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 +#include +#include +#include + +#define SS_MEMSAVE 0x00 /* First 8 bytes of RAM */ +#define SS_HID 0x08 /* 3 HIDs */ +#define SS_IABR 0x14 /* 2 IABRs */ +#define SS_IBCR 0x1c +#define SS_DABR 0x20 /* 2 DABRs */ +#define SS_DBCR 0x28 +#define SS_SP 0x2c +#define SS_SR 0x30 /* 16 segment registers */ +#define SS_R2 0x70 +#define SS_MSR 0x74 +#define SS_SDR1 0x78 +#define SS_LR 0x7c +#define SS_SPRG 0x80 /* 4 SPRGs */ +#define SS_DBAT 0x90 /* 8 DBATs */ +#define SS_IBAT 0xd0 /* 8 IBATs */ +#define SS_TB 0x110 +#define SS_CR 0x118 +#define SS_GPREG 0x11c /* r12-r31 */ +#define STATE_SAVE_SIZE 0x16c + + .section .data + .align 5 + +mpc83xx_sleep_save_area: + .space STATE_SAVE_SIZE +immrbase: + .long 0 + + .section .text + .align 5 + + /* r3 = physical address of IMMR */ +_GLOBAL(mpc83xx_enter_deep_sleep) + lis r4, immrbase@ha + stw r3, immrbase@l(r4) + + /* The first 2 words of memory are used to communicate with the + * bootloader, to tell it how to resume. + * + * The first word is the magic number 0xf5153ae5, and the second + * is the pointer to mpc83xx_deep_resume. + * + * The original content of these two words is saved in SS_MEMSAVE. + */ + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + + lis r4, KERNELBASE@h + lwz r5, 0(r4) + lwz r6, 4(r4) + + stw r5, SS_MEMSAVE+0(r3) + stw r6, SS_MEMSAVE+4(r3) + + mfspr r5, SPRN_HID0 + mfspr r6, SPRN_HID1 + mfspr r7, SPRN_HID2 + + stw r5, SS_HID+0(r3) + stw r6, SS_HID+4(r3) + stw r7, SS_HID+8(r3) + + mfspr r4, SPRN_IABR + mfspr r5, SPRN_IABR2 + mfspr r6, SPRN_IBCR + mfspr r7, SPRN_DABR + mfspr r8, SPRN_DABR2 + mfspr r9, SPRN_DBCR + + stw r4, SS_IABR+0(r3) + stw r5, SS_IABR+4(r3) + stw r6, SS_IBCR(r3) + stw r7, SS_DABR+0(r3) + stw r8, SS_DABR+4(r3) + stw r9, SS_DBCR(r3) + + mfspr r4, SPRN_SPRG0 + mfspr r5, SPRN_SPRG1 + mfspr r6, SPRN_SPRG2 + mfspr r7, SPRN_SPRG3 + mfsdr1 r8 + + stw r4, SS_SPRG+0(r3) + stw r5, SS_SPRG+4(r3) + stw r6, SS_SPRG+8(r3) + stw r7, SS_SPRG+12(r3) + stw r8, SS_SDR1(r3) + + mfspr r4, SPRN_DBAT0U + mfspr r5, SPRN_DBAT0L + mfspr r6, SPRN_DBAT1U + mfspr r7, SPRN_DBAT1L + + stw r4, SS_DBAT+0x00(r3) + stw r5, SS_DBAT+0x04(r3) + stw r6, SS_DBAT+0x08(r3) + stw r7, SS_DBAT+0x0c(r3) + + mfspr r4, SPRN_DBAT2U + mfspr r5, SPRN_DBAT2L + mfspr r6, SPRN_DBAT3U + mfspr r7, SPRN_DBAT3L + + stw r4, SS_DBAT+0x10(r3) + stw r5, SS_DBAT+0x14(r3) + stw r6, SS_DBAT+0x18(r3) + stw r7, SS_DBAT+0x1c(r3) + + mfspr r4, SPRN_DBAT4U + mfspr r5, SPRN_DBAT4L + mfspr r6, SPRN_DBAT5U + mfspr r7, SPRN_DBAT5L + + stw r4, SS_DBAT+0x20(r3) + stw r5, SS_DBAT+0x24(r3) + stw r6, SS_DBAT+0x28(r3) + stw r7, SS_DBAT+0x2c(r3) + + mfspr r4, SPRN_DBAT6U + mfspr r5, SPRN_DBAT6L + mfspr r6, SPRN_DBAT7U + mfspr r7, SPRN_DBAT7L + + stw r4, SS_DBAT+0x30(r3) + stw r5, SS_DBAT+0x34(r3) + stw r6, SS_DBAT+0x38(r3) + stw r7, SS_DBAT+0x3c(r3) + + mfspr r4, SPRN_IBAT0U + mfspr r5, SPRN_IBAT0L + mfspr r6, SPRN_IBAT1U + mfspr r7, SPRN_IBAT1L + + stw r4, SS_IBAT+0x00(r3) + stw r5, SS_IBAT+0x04(r3) + stw r6, SS_IBAT+0x08(r3) + stw r7, SS_IBAT+0x0c(r3) + + mfspr r4, SPRN_IBAT2U + mfspr r5, SPRN_IBAT2L + mfspr r6, SPRN_IBAT3U + mfspr r7, SPRN_IBAT3L + + stw r4, SS_IBAT+0x10(r3) + stw r5, SS_IBAT+0x14(r3) + stw r6, SS_IBAT+0x18(r3) + stw r7, SS_IBAT+0x1c(r3) + + mfspr r4, SPRN_IBAT4U + mfspr r5, SPRN_IBAT4L + mfspr r6, SPRN_IBAT5U + mfspr r7, SPRN_IBAT5L + + stw r4, SS_IBAT+0x20(r3) + stw r5, SS_IBAT+0x24(r3) + stw r6, SS_IBAT+0x28(r3) + stw r7, SS_IBAT+0x2c(r3) + + mfspr r4, SPRN_IBAT6U + mfspr r5, SPRN_IBAT6L + mfspr r6, SPRN_IBAT7U + mfspr r7, SPRN_IBAT7L + + stw r4, SS_IBAT+0x30(r3) + stw r5, SS_IBAT+0x34(r3) + stw r6, SS_IBAT+0x38(r3) + stw r7, SS_IBAT+0x3c(r3) + + mfmsr r4 + mflr r5 + mfcr r6 + + stw r4, SS_MSR(r3) + stw r5, SS_LR(r3) + stw r6, SS_CR(r3) + stw r1, SS_SP(r3) + stw r2, SS_R2(r3) + +1: mftbu r4 + mftb r5 + mftbu r6 + cmpw r4, r6 + bne 1b + + stw r4, SS_TB+0(r3) + stw r5, SS_TB+4(r3) + + stmw r12, SS_GPREG(r3) + + li r4, 0 + addi r6, r3, SS_SR-4 +1: mfsrin r5, r4 + stwu r5, 4(r6) + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + /* Disable machine checks and critical exceptions */ + mfmsr r4 + rlwinm r4, r4, 0, ~MSR_CE + rlwinm r4, r4, 0, ~MSR_ME + mtmsr r4 + isync + +#define TMP_VIRT_IMMR 0xf0000000 +#define DEFAULT_IMMR_VALUE 0xff400000 +#define IMMRBAR_BASE 0x0000 + + lis r4, immrbase@ha + lwz r4, immrbase@l(r4) + + /* Use DBAT0 to address the current IMMR space */ + + ori r4, r4, 0x002a + mtspr SPRN_DBAT0L, r4 + lis r8, TMP_VIRT_IMMR@h + ori r4, r8, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT0U, r4 + isync + + /* Use DBAT1 to address the original IMMR space */ + + lis r4, DEFAULT_IMMR_VALUE@h + ori r4, r4, 0x002a + mtspr SPRN_DBAT1L, r4 + lis r9, (TMP_VIRT_IMMR + 0x01000000)@h + ori r4, r9, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT1U, r4 + isync + + /* Use DBAT2 to address the beginning of RAM. This isn't done + * using the normal virtual mapping, because with page debugging + * enabled it will be read-only. + */ + + li r4, 0x0002 + mtspr SPRN_DBAT2L, r4 + lis r4, KERNELBASE@h + ori r4, r4, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT2U, r4 + isync + + /* Flush the cache with our BAT, as there will be TLB misses + * otherwise if page debugging is enabled, and these misses + * will disturb the PLRU algorithm. + */ + + bl __flush_disable_L1 + + /* Keep the i-cache enabled, so the hack below for low-boot + * flash will work. + */ + mfspr r3, SPRN_HID0 + ori r3, r3, HID0_ICE + mtspr SPRN_HID0, r3 + isync + + lis r6, 0xf515 + ori r6, r6, 0x3ae5 + + lis r7, mpc83xx_deep_resume@h + ori r7, r7, mpc83xx_deep_resume@l + tophys(r7, r7) + + lis r5, KERNELBASE@h + stw r6, 0(r5) + stw r7, 4(r5) + + /* Reset BARs */ + + li r4, 0 + stw r4, 0x0024(r8) + stw r4, 0x002c(r8) + stw r4, 0x0034(r8) + stw r4, 0x003c(r8) + stw r4, 0x0064(r8) + stw r4, 0x006c(r8) + + /* Rev 1 of the 8313 has problems with wakeup events that are + * pending during the transition to deep sleep state (such as if + * the PCI host sets the state to D3 and then D0 in rapid + * succession). This check shrinks the race window somewhat. + * + * See erratum PCI23, though the problem is not limited + * to PCI. + */ + + lwz r3, 0x0b04(r8) + andi. r3, r3, 1 + bne- mpc83xx_deep_resume + + /* Move IMMR back to the default location, following the + * procedure specified in the MPC8313 manual. + */ + lwz r4, IMMRBAR_BASE(r8) + isync + lis r4, DEFAULT_IMMR_VALUE@h + stw r4, IMMRBAR_BASE(r8) + lis r4, KERNELBASE@h + lwz r4, 0(r4) + isync + lwz r4, IMMRBAR_BASE(r9) + mr r8, r9 + isync + + /* Check the Reset Configuration Word to see whether flash needs + * to be mapped at a low address or a high address. + */ + + lwz r4, 0x0904(r8) + andis. r4, r4, 0x0400 + li r4, 0 + beq boot_low + lis r4, 0xff80 +boot_low: + stw r4, 0x0020(r8) + lis r7, 0x8000 + ori r7, r7, 0x0016 + + mfspr r5, SPRN_HID0 + rlwinm r5, r5, 0, ~(HID0_DOZE | HID0_NAP) + oris r5, r5, HID0_SLEEP@h + mtspr SPRN_HID0, r5 + isync + + mfmsr r5 + oris r5, r5, MSR_POW@h + + /* Enable the flash mapping at the appropriate address. This + * mapping will override the RAM mapping if booting low, so there's + * no need to disable the latter. This must be done inside the same + * cache line as setting MSR_POW, so that no instruction fetches + * from RAM happen after the flash mapping is turned on. + */ + + .align 5 + stw r7, 0x0024(r8) + sync + isync + mtmsr r5 + isync +1: b 1b + +mpc83xx_deep_resume: + lis r4, 1f@h + ori r4, r4, 1f@l + tophys(r4, r4) + mtsrr0 r4 + + mfmsr r4 + rlwinm r4, r4, 0, ~(MSR_IR | MSR_DR) + mtsrr1 r4 + + rfi + +1: tlbia + bl __inval_enable_L1 + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + tophys(r3, r3) + + lwz r5, SS_MEMSAVE+0(r3) + lwz r6, SS_MEMSAVE+4(r3) + + stw r5, 0(0) + stw r6, 4(0) + + lwz r5, SS_HID+0(r3) + lwz r6, SS_HID+4(r3) + lwz r7, SS_HID+8(r3) + + mtspr SPRN_HID0, r5 + mtspr SPRN_HID1, r6 + mtspr SPRN_HID2, r7 + + lwz r4, SS_IABR+0(r3) + lwz r5, SS_IABR+4(r3) + lwz r6, SS_IBCR(r3) + lwz r7, SS_DABR+0(r3) + lwz r8, SS_DABR+4(r3) + lwz r9, SS_DBCR(r3) + + mtspr SPRN_IABR, r4 + mtspr SPRN_IABR2, r5 + mtspr SPRN_IBCR, r6 + mtspr SPRN_DABR, r7 + mtspr SPRN_DABR2, r8 + mtspr SPRN_DBCR, r9 + + li r4, 0 + addi r6, r3, SS_SR-4 +1: lwzu r5, 4(r6) + mtsrin r5, r4 + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + lwz r4, SS_DBAT+0x00(r3) + lwz r5, SS_DBAT+0x04(r3) + lwz r6, SS_DBAT+0x08(r3) + lwz r7, SS_DBAT+0x0c(r3) + + mtspr SPRN_DBAT0U, r4 + mtspr SPRN_DBAT0L, r5 + mtspr SPRN_DBAT1U, r6 + mtspr SPRN_DBAT1L, r7 + + lwz r4, SS_DBAT+0x10(r3) + lwz r5, SS_DBAT+0x14(r3) + lwz r6, SS_DBAT+0x18(r3) + lwz r7, SS_DBAT+0x1c(r3) + + mtspr SPRN_DBAT2U, r4 + mtspr SPRN_DBAT2L, r5 + mtspr SPRN_DBAT3U, r6 + mtspr SPRN_DBAT3L, r7 + + lwz r4, SS_DBAT+0x20(r3) + lwz r5, SS_DBAT+0x24(r3) + lwz r6, SS_DBAT+0x28(r3) + lwz r7, SS_DBAT+0x2c(r3) + + mtspr SPRN_DBAT4U, r4 + mtspr SPRN_DBAT4L, r5 + mtspr SPRN_DBAT5U, r6 + mtspr SPRN_DBAT5L, r7 + + lwz r4, SS_DBAT+0x30(r3) + lwz r5, SS_DBAT+0x34(r3) + lwz r6, SS_DBAT+0x38(r3) + lwz r7, SS_DBAT+0x3c(r3) + + mtspr SPRN_DBAT6U, r4 + mtspr SPRN_DBAT6L, r5 + mtspr SPRN_DBAT7U, r6 + mtspr SPRN_DBAT7L, r7 + + lwz r4, SS_IBAT+0x00(r3) + lwz r5, SS_IBAT+0x04(r3) + lwz r6, SS_IBAT+0x08(r3) + lwz r7, SS_IBAT+0x0c(r3) + + mtspr SPRN_IBAT0U, r4 + mtspr SPRN_IBAT0L, r5 + mtspr SPRN_IBAT1U, r6 + mtspr SPRN_IBAT1L, r7 + + lwz r4, SS_IBAT+0x10(r3) + lwz r5, SS_IBAT+0x14(r3) + lwz r6, SS_IBAT+0x18(r3) + lwz r7, SS_IBAT+0x1c(r3) + + mtspr SPRN_IBAT2U, r4 + mtspr SPRN_IBAT2L, r5 + mtspr SPRN_IBAT3U, r6 + mtspr SPRN_IBAT3L, r7 + + lwz r4, SS_IBAT+0x20(r3) + lwz r5, SS_IBAT+0x24(r3) + lwz r6, SS_IBAT+0x28(r3) + lwz r7, SS_IBAT+0x2c(r3) + + mtspr SPRN_IBAT4U, r4 + mtspr SPRN_IBAT4L, r5 + mtspr SPRN_IBAT5U, r6 + mtspr SPRN_IBAT5L, r7 + + lwz r4, SS_IBAT+0x30(r3) + lwz r5, SS_IBAT+0x34(r3) + lwz r6, SS_IBAT+0x38(r3) + lwz r7, SS_IBAT+0x3c(r3) + + mtspr SPRN_IBAT6U, r4 + mtspr SPRN_IBAT6L, r5 + mtspr SPRN_IBAT7U, r6 + mtspr SPRN_IBAT7L, r7 + + lwz r4, SS_SPRG+0(r3) + lwz r5, SS_SPRG+4(r3) + lwz r6, SS_SPRG+8(r3) + lwz r7, SS_SPRG+12(r3) + lwz r8, SS_SDR1(r3) + + mtspr SPRN_SPRG0, r4 + mtspr SPRN_SPRG1, r5 + mtspr SPRN_SPRG2, r6 + mtspr SPRN_SPRG3, r7 + mtsdr1 r8 + + lwz r4, SS_MSR(r3) + lwz r5, SS_LR(r3) + lwz r6, SS_CR(r3) + lwz r1, SS_SP(r3) + lwz r2, SS_R2(r3) + + mtsrr1 r4 + mtsrr0 r5 + mtcr r6 + + li r4, 0 + mtspr SPRN_TBWL, r4 + + lwz r4, SS_TB+0(r3) + lwz r5, SS_TB+4(r3) + + mtspr SPRN_TBWU, r4 + mtspr SPRN_TBWL, r5 + + lmw r12, SS_GPREG(r3) + + /* Kick decrementer */ + li r0, 1 + mtdec r0 + + rfi diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c new file mode 100644 index 000000000000..08e65fc8b98c --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -0,0 +1,388 @@ +/* + * MPC83xx suspend support + * + * Author: Scott Wood + * + * Copyright (c) 2006-2007 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ +#define PMCCR1_NEXT_STATE_SHIFT 2 +#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ +#define IMMR_RCW_OFFSET 0x900 +#define RCW_PCI_HOST 0x80000000 + +void mpc83xx_enter_deep_sleep(phys_addr_t immrbase); + +struct mpc83xx_pmc { + u32 config; +#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ +#define PMCCR_SLPEN 1 /* System low power enable */ + + u32 event; + u32 mask; +/* All but PMCI are deep-sleep only */ +#define PMCER_GPIO 0x100 +#define PMCER_PCI 0x080 +#define PMCER_USB 0x040 +#define PMCER_ETSEC1 0x020 +#define PMCER_ETSEC2 0x010 +#define PMCER_TIMER 0x008 +#define PMCER_INT1 0x004 +#define PMCER_INT2 0x002 +#define PMCER_PMCI 0x001 +#define PMCER_ALL 0x1FF + + /* deep-sleep only */ + u32 config1; +#define PMCCR1_USE_STATE 0x80000000 +#define PMCCR1_PME_EN 0x00000080 +#define PMCCR1_ASSERT_PME 0x00000040 +#define PMCCR1_POWER_OFF 0x00000020 + + /* deep-sleep only */ + u32 config2; +}; + +struct mpc83xx_rcw { + u32 rcwlr; + u32 rcwhr; +}; + +struct mpc83xx_clock { + u32 spmr; + u32 occr; + u32 sccr; +}; + +struct pmc_type { + int has_deep_sleep; +}; + +static struct of_device *pmc_dev; +static int has_deep_sleep, deep_sleeping; +static int pmc_irq; +static struct mpc83xx_pmc __iomem *pmc_regs; +static struct mpc83xx_clock __iomem *clock_regs; +static int is_pci_agent, wake_from_pci; +static phys_addr_t immrbase; +static int pci_pm_state; +static DECLARE_WAIT_QUEUE_HEAD(agent_wq); + +int fsl_deep_sleep(void) +{ + return deep_sleeping; +} + +static int mpc83xx_change_state(void) +{ + u32 curr_state; + u32 reg_cfg1 = in_be32(&pmc_regs->config1); + + if (is_pci_agent) { + pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> + PMCCR1_NEXT_STATE_SHIFT; + curr_state = reg_cfg1 & PMCCR1_CURR_STATE; + + if (curr_state != pci_pm_state) { + reg_cfg1 &= ~PMCCR1_CURR_STATE; + reg_cfg1 |= pci_pm_state; + out_be32(&pmc_regs->config1, reg_cfg1); + + wake_up(&agent_wq); + return 1; + } + } + + return 0; +} + +static irqreturn_t pmc_irq_handler(int irq, void *dev_id) +{ + u32 event = in_be32(&pmc_regs->event); + int ret = IRQ_NONE; + + if (mpc83xx_change_state()) + ret = IRQ_HANDLED; + + if (event) { + out_be32(&pmc_regs->event, event); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int mpc83xx_suspend_enter(suspend_state_t state) +{ + int ret = -EAGAIN; + + /* Don't go to sleep if there's a race where pci_pm_state changes + * between the agent thread checking it and the PM code disabling + * interrupts. + */ + if (wake_from_pci) { + if (pci_pm_state != (deep_sleeping ? 3 : 2)) + goto out; + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); + } + + /* Put the system into low-power mode and the RAM + * into self-refresh mode once the core goes to + * sleep. + */ + + out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); + + /* If it has deep sleep (i.e. it's an 831x or compatible), + * disable power to the core upon entering sleep mode. This will + * require going through the boot firmware upon a wakeup event. + */ + + if (deep_sleeping) { + out_be32(&pmc_regs->mask, PMCER_ALL); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); + + enable_kernel_fp(); + + mpc83xx_enter_deep_sleep(immrbase); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); + + out_be32(&pmc_regs->mask, PMCER_PMCI); + } else { + out_be32(&pmc_regs->mask, PMCER_PMCI); + + mpc6xx_enter_standby(); + } + + ret = 0; + +out: + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); + + return ret; +} + +static void mpc83xx_suspend_finish(void) +{ + deep_sleeping = 0; +} + +static int mpc83xx_suspend_valid(suspend_state_t state) +{ + return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; +} + +static int mpc83xx_suspend_begin(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + deep_sleeping = 0; + return 0; + + case PM_SUSPEND_MEM: + if (has_deep_sleep) + deep_sleeping = 1; + + return 0; + + default: + return -EINVAL; + } +} + +static int agent_thread_fn(void *data) +{ + while (1) { + wait_event_interruptible(agent_wq, pci_pm_state >= 2); + try_to_freeze(); + + if (signal_pending(current) || pci_pm_state < 2) + continue; + + /* With a preemptible kernel (or SMP), this could race with + * a userspace-driven suspend request. It's probably best + * to avoid mixing the two with such a configuration (or + * else fix it by adding a mutex to state_store that we can + * synchronize with). + */ + + wake_from_pci = 1; + + pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : + PM_SUSPEND_STANDBY); + + wake_from_pci = 0; + } + + return 0; +} + +static void mpc83xx_set_agent(void) +{ + out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); + out_be32(&pmc_regs->mask, PMCER_PMCI); + + kthread_run(agent_thread_fn, NULL, "PCI power mgt"); +} + +static int mpc83xx_is_pci_agent(void) +{ + struct mpc83xx_rcw __iomem *rcw_regs; + int ret; + + rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, + sizeof(struct mpc83xx_rcw)); + + if (!rcw_regs) + return -ENOMEM; + + ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); + + iounmap(rcw_regs); + return ret; +} + +static struct platform_suspend_ops mpc83xx_suspend_ops = { + .valid = mpc83xx_suspend_valid, + .begin = mpc83xx_suspend_begin, + .enter = mpc83xx_suspend_enter, + .finish = mpc83xx_suspend_finish, +}; + +static int pmc_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = ofdev->node; + struct resource res; + struct pmc_type *type = match->data; + int ret = 0; + + if (!of_device_is_available(np)) + return -ENODEV; + + has_deep_sleep = type->has_deep_sleep; + immrbase = get_immrbase(); + pmc_dev = ofdev; + + is_pci_agent = mpc83xx_is_pci_agent(); + if (is_pci_agent < 0) + return is_pci_agent; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return -ENODEV; + + pmc_irq = irq_of_parse_and_map(np, 0); + if (pmc_irq != NO_IRQ) { + ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, + "pmc", ofdev); + + if (ret) + return -EBUSY; + } + + pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!pmc_regs) { + ret = -ENOMEM; + goto out; + } + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + ret = -ENODEV; + goto out_pmc; + } + + clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!clock_regs) { + ret = -ENOMEM; + goto out_pmc; + } + + if (is_pci_agent) + mpc83xx_set_agent(); + + suspend_set_ops(&mpc83xx_suspend_ops); + return 0; + +out_pmc: + iounmap(pmc_regs); +out: + if (pmc_irq != NO_IRQ) + free_irq(pmc_irq, ofdev); + + return ret; +} + +static int pmc_remove(struct of_device *ofdev) +{ + return -EPERM; +}; + +static struct pmc_type pmc_types[] = { + { + .has_deep_sleep = 1, + }, + { + .has_deep_sleep = 0, + } +}; + +static struct of_device_id pmc_match[] = { + { + .compatible = "fsl,mpc8313-pmc", + .data = &pmc_types[0], + }, + { + .compatible = "fsl,mpc8349-pmc", + .data = &pmc_types[1], + }, + {} +}; + +static struct of_platform_driver pmc_driver = { + .name = "mpc83xx-pmc", + .match_table = pmc_match, + .probe = pmc_probe, + .remove = pmc_remove +}; + +static int pmc_init(void) +{ + return of_register_platform_driver(&pmc_driver); +} + +module_init(pmc_init); diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h index 52c831fa1886..024299887352 100644 --- a/arch/powerpc/sysdev/fsl_soc.h +++ b/arch/powerpc/sysdev/fsl_soc.h @@ -10,6 +10,7 @@ extern u32 get_baudrate(void); extern u32 fsl_get_sys_freq(void); struct spi_board_info; +struct device_node; extern int fsl_spi_init(struct spi_board_info *board_infos, unsigned int num_board_infos, diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index caba1c0be5a7..88a983ece5c9 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -889,8 +890,78 @@ unsigned int ipic_get_irq(void) return irq_linear_revmap(primary_ipic->irqhost, irq); } +#ifdef CONFIG_PM +static struct { + u32 sicfr; + u32 siprr[2]; + u32 simsr[2]; + u32 sicnr; + u32 smprr[2]; + u32 semsr; + u32 secnr; + u32 sermr; + u32 sercr; +} ipic_saved_state; + +static int ipic_suspend(struct sys_device *sdev, pm_message_t state) +{ + struct ipic *ipic = primary_ipic; + + ipic_saved_state.sicfr = ipic_read(ipic->regs, IPIC_SICFR); + ipic_saved_state.siprr[0] = ipic_read(ipic->regs, IPIC_SIPRR_A); + ipic_saved_state.siprr[1] = ipic_read(ipic->regs, IPIC_SIPRR_D); + ipic_saved_state.simsr[0] = ipic_read(ipic->regs, IPIC_SIMSR_H); + ipic_saved_state.simsr[1] = ipic_read(ipic->regs, IPIC_SIMSR_L); + ipic_saved_state.sicnr = ipic_read(ipic->regs, IPIC_SICNR); + ipic_saved_state.smprr[0] = ipic_read(ipic->regs, IPIC_SMPRR_A); + ipic_saved_state.smprr[1] = ipic_read(ipic->regs, IPIC_SMPRR_B); + ipic_saved_state.semsr = ipic_read(ipic->regs, IPIC_SEMSR); + ipic_saved_state.secnr = ipic_read(ipic->regs, IPIC_SECNR); + ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR); + ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR); + + if (fsl_deep_sleep()) { + /* In deep sleep, make sure there can be no + * pending interrupts, as this can cause + * problems on 831x. + */ + ipic_write(ipic->regs, IPIC_SIMSR_H, 0); + ipic_write(ipic->regs, IPIC_SIMSR_L, 0); + ipic_write(ipic->regs, IPIC_SEMSR, 0); + ipic_write(ipic->regs, IPIC_SERMR, 0); + } + + return 0; +} + +static int ipic_resume(struct sys_device *sdev) +{ + struct ipic *ipic = primary_ipic; + + ipic_write(ipic->regs, IPIC_SICFR, ipic_saved_state.sicfr); + ipic_write(ipic->regs, IPIC_SIPRR_A, ipic_saved_state.siprr[0]); + ipic_write(ipic->regs, IPIC_SIPRR_D, ipic_saved_state.siprr[1]); + ipic_write(ipic->regs, IPIC_SIMSR_H, ipic_saved_state.simsr[0]); + ipic_write(ipic->regs, IPIC_SIMSR_L, ipic_saved_state.simsr[1]); + ipic_write(ipic->regs, IPIC_SICNR, ipic_saved_state.sicnr); + ipic_write(ipic->regs, IPIC_SMPRR_A, ipic_saved_state.smprr[0]); + ipic_write(ipic->regs, IPIC_SMPRR_B, ipic_saved_state.smprr[1]); + ipic_write(ipic->regs, IPIC_SEMSR, ipic_saved_state.semsr); + ipic_write(ipic->regs, IPIC_SECNR, ipic_saved_state.secnr); + ipic_write(ipic->regs, IPIC_SERMR, ipic_saved_state.sermr); + ipic_write(ipic->regs, IPIC_SERCR, ipic_saved_state.sercr); + + return 0; +} +#else +#define ipic_suspend NULL +#define ipic_resume NULL +#endif + static struct sysdev_class ipic_sysclass = { .name = "ipic", + .suspend = ipic_suspend, + .resume = ipic_resume, }; static struct sys_device device_ipic = { diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index bbccadfee0d6..c6d1ab650778 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -155,10 +155,12 @@ #define CTRL_RUNLATCH 0x1 #define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ #define DABR_TRANSLATION (1UL << 2) +#define SPRN_DABR2 0x13D /* e300 */ #define SPRN_DABRX 0x3F7 /* Data Address Breakpoint Register Extension */ #define DABRX_USER (1UL << 0) #define DABRX_KERNEL (1UL << 1) #define SPRN_DAR 0x013 /* Data Address Register */ +#define SPRN_DBCR 0x136 /* e300 Data Breakpoint Control Reg */ #define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ #define DSISR_NOHPTE 0x40000000 /* no translation found */ #define DSISR_PROTFAULT 0x08000000 /* protection fault */ @@ -264,6 +266,8 @@ #define HID1_PS (1<<16) /* 750FX PLL selection */ #define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */ #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ +#define SPRN_IABR2 0x3FA /* 83xx */ +#define SPRN_IBCR 0x135 /* 83xx Insn Breakpoint Control Reg */ #define SPRN_HID4 0x3F4 /* 970 HID4 */ #define SPRN_HID5 0x3F6 /* 970 HID5 */ #define SPRN_HID6 0x3F9 /* BE HID 6 */ diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index c415a496de3a..0472877d7ea8 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -125,4 +125,10 @@ struct mpc8xx_pcmcia_ops { int(*voltage_set)(int slot, int vcc, int vpp); }; +/* Returns non-zero if the current suspend operation would + * lead to a deep sleep (i.e. power removed from the core, + * instead of just the clock). + */ +int fsl_deep_sleep(void); + #endif /* _FSL_DEVICE_H_ */ -- cgit v1.2.3 From d87eb12785c14de1586e3bad86ca2c0991300339 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 11 Jul 2008 18:04:45 -0500 Subject: gianfar: Add magic packet and suspend/resume support. Signed-off-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_soc.c | 3 ++ drivers/net/gianfar.c | 122 +++++++++++++++++++++++++++++++++++++++++- drivers/net/gianfar.h | 12 ++++- drivers/net/gianfar_ethtool.c | 41 +++++++++++++- include/linux/fsl_devices.h | 1 + 5 files changed, 173 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index ef4cb0d67a72..214388e11807 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -352,6 +352,9 @@ static int __init gfar_of_init(void) else gfar_data.interface = PHY_INTERFACE_MODE_MII; + if (of_get_property(np, "fsl,magic-packet", NULL)) + gfar_data.device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; + ph = of_get_property(np, "phy-handle", NULL); if (ph == NULL) { u32 *fixed_link; diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 25bdd0832df5..36f229ff52f2 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -143,6 +143,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l static void gfar_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp); void gfar_halt(struct net_device *dev); +#ifdef CONFIG_PM +static void gfar_halt_nodisable(struct net_device *dev); +#endif void gfar_start(struct net_device *dev); static void gfar_clear_exact_match(struct net_device *dev); static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); @@ -216,6 +219,7 @@ static int gfar_probe(struct platform_device *pdev) spin_lock_init(&priv->txlock); spin_lock_init(&priv->rxlock); + spin_lock_init(&priv->bflock); platform_set_drvdata(pdev, dev); @@ -393,6 +397,103 @@ static int gfar_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int gfar_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + + int magic_packet = priv->wol_en && + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + + netif_device_detach(dev); + + if (netif_running(dev)) { + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + + gfar_halt_nodisable(dev); + + /* Disable Tx, and Rx if wake-on-LAN is disabled. */ + tempval = gfar_read(&priv->regs->maccfg1); + + tempval &= ~MACCFG1_TX_EN; + + if (!magic_packet) + tempval &= ~MACCFG1_RX_EN; + + gfar_write(&priv->regs->maccfg1, tempval); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); + +#ifdef CONFIG_GFAR_NAPI + napi_disable(&priv->napi); +#endif + + if (magic_packet) { + /* Enable interrupt on Magic Packet */ + gfar_write(&priv->regs->imask, IMASK_MAG); + + /* Enable Magic Packet mode */ + tempval = gfar_read(&priv->regs->maccfg2); + tempval |= MACCFG2_MPEN; + gfar_write(&priv->regs->maccfg2, tempval); + } else { + phy_stop(priv->phydev); + } + } + + return 0; +} + +static int gfar_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + int magic_packet = priv->wol_en && + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + + if (!netif_running(dev)) { + netif_device_attach(dev); + return 0; + } + + if (!magic_packet && priv->phydev) + phy_start(priv->phydev); + + /* Disable Magic Packet mode, in case something + * else woke us up. + */ + + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + + tempval = gfar_read(&priv->regs->maccfg2); + tempval &= ~MACCFG2_MPEN; + gfar_write(&priv->regs->maccfg2, tempval); + + gfar_start(dev); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); + + netif_device_attach(dev); + +#ifdef CONFIG_GFAR_NAPI + napi_enable(&priv->napi); +#endif + + return 0; +} +#else +#define gfar_suspend NULL +#define gfar_resume NULL +#endif /* Reads the controller's registers to determine what interface * connects it to the PHY. @@ -549,8 +650,9 @@ static void init_registers(struct net_device *dev) } +#ifdef CONFIG_PM /* Halt the receive and transmit queues */ -void gfar_halt(struct net_device *dev) +static void gfar_halt_nodisable(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->regs; @@ -573,6 +675,15 @@ void gfar_halt(struct net_device *dev) (IEVENT_GRSC | IEVENT_GTSC))) cpu_relax(); } +} +#endif + +/* Halt the receive and transmit queues */ +void gfar_halt(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = priv->regs; + u32 tempval; /* Disable Rx and Tx */ tempval = gfar_read(®s->maccfg1); @@ -1969,7 +2080,12 @@ static irqreturn_t gfar_error(int irq, void *dev_id) u32 events = gfar_read(&priv->regs->ievent); /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); + gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK); + + /* Magic Packet is not an error. */ + if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + (events & IEVENT_MAG)) + events &= ~IEVENT_MAG; /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) @@ -2042,6 +2158,8 @@ MODULE_ALIAS("platform:fsl-gianfar"); static struct platform_driver gfar_driver = { .probe = gfar_probe, .remove = gfar_remove, + .suspend = gfar_suspend, + .resume = gfar_resume, .driver = { .name = "fsl-gianfar", .owner = THIS_MODULE, diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 27f37c81e52c..5ee518a8782b 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -168,6 +168,7 @@ extern const char gfar_driver_version[]; #define MACCFG2_GMII 0x00000200 #define MACCFG2_HUGEFRAME 0x00000020 #define MACCFG2_LENGTHCHECK 0x00000010 +#define MACCFG2_MPEN 0x00000008 #define ECNTRL_INIT_SETTINGS 0x00001000 #define ECNTRL_TBI_MODE 0x00000020 @@ -240,6 +241,7 @@ extern const char gfar_driver_version[]; #define IEVENT_CRL 0x00020000 #define IEVENT_XFUN 0x00010000 #define IEVENT_RXB0 0x00008000 +#define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 #define IEVENT_FIR 0x00000008 @@ -252,7 +254,8 @@ extern const char gfar_driver_version[]; #define IEVENT_ERR_MASK \ (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ - | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR) + | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \ + | IEVENT_MAG) #define IMASK_INIT_CLEAR 0x00000000 #define IMASK_BABR 0x80000000 @@ -270,6 +273,7 @@ extern const char gfar_driver_version[]; #define IMASK_CRL 0x00020000 #define IMASK_XFUN 0x00010000 #define IMASK_RXB0 0x00008000 +#define IMASK_MAG 0x00000800 #define IMASK_GTSC 0x00000100 #define IMASK_RXFEN0 0x00000080 #define IMASK_FIR 0x00000008 @@ -737,10 +741,14 @@ struct gfar_private { unsigned int fifo_starve; unsigned int fifo_starve_off; + /* Bitfield update lock */ + spinlock_t bflock; + unsigned char vlan_enable:1, rx_csum_enable:1, extended_hash:1, - bd_stash_en:1; + bd_stash_en:1, + wol_en:1; /* Wake-on-LAN enabled */ unsigned short padding; unsigned int interruptTransmit; diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 6007147cc1e9..fb7d3ccc0fdc 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -479,14 +479,13 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) { struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; int err = 0; if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; if (dev->flags & IFF_UP) { - unsigned long flags; - /* Halt TX and RX, and process the frames which * have already been received */ spin_lock_irqsave(&priv->txlock, flags); @@ -502,7 +501,9 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) stop_gfar(dev); } + spin_lock_irqsave(&priv->bflock, flags); priv->rx_csum_enable = data; + spin_unlock_irqrestore(&priv->bflock, flags); if (dev->flags & IFF_UP) err = startup_gfar(dev); @@ -564,6 +565,38 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data) priv->msg_enable = data; } +#ifdef CONFIG_PM +static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gfar_private *priv = netdev_priv(dev); + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { + wol->supported = WAKE_MAGIC; + wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; + } else { + wol->supported = wol->wolopts = 0; + } +} + +static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + wol->wolopts != 0) + return -EINVAL; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + spin_lock_irqsave(&priv->bflock, flags); + priv->wol_en = wol->wolopts & WAKE_MAGIC ? 1 : 0; + spin_unlock_irqrestore(&priv->bflock, flags); + + return 0; +} +#endif const struct ethtool_ops gfar_ethtool_ops = { .get_settings = gfar_gsettings, @@ -585,4 +618,8 @@ const struct ethtool_ops gfar_ethtool_ops = { .set_tx_csum = gfar_set_tx_csum, .get_msglevel = gfar_get_msglevel, .set_msglevel = gfar_set_msglevel, +#ifdef CONFIG_PM + .get_wol = gfar_get_wol, + .set_wol = gfar_set_wol, +#endif }; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 0472877d7ea8..4e625e0094c8 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -69,6 +69,7 @@ struct gianfar_mdio_data { #define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020 #define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040 #define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080 +#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100 /* Flags in gianfar_platform_data */ #define FSL_GIANFAR_BRD_HAS_PHY_INTR 0x00000001 /* set or use a timer */ -- cgit v1.2.3 From b219108cbacee5f2eaeca63cba013688eeba3bd4 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 12 Jun 2008 08:32:13 -0500 Subject: fs_enet: Remove !CONFIG_PPC_CPM_NEW_BINDING code Now that arch/ppc is gone we always define CONFIG_PPC_CPM_NEW_BINDING so we can remove all the code associated with !CONFIG_PPC_CPM_NEW_BINDING. Also fixed some asm/of_platform.h to linux/of_platform.h (and of_device.h) Signed-off-by: Kumar Gala --- drivers/net/fs_enet/Makefile | 5 - drivers/net/fs_enet/fs_enet-main.c | 310 +------------------------------------ drivers/net/fs_enet/fs_enet.h | 4 - drivers/net/fs_enet/mac-fcc.c | 67 +------- drivers/net/fs_enet/mac-fec.c | 23 +-- drivers/net/fs_enet/mac-scc.c | 37 +---- drivers/net/fs_enet/mii-bitbang.c | 107 ------------- drivers/net/fs_enet/mii-fec.c | 144 +---------------- include/linux/fs_enet_pd.h | 4 - 9 files changed, 5 insertions(+), 696 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/fs_enet/Makefile b/drivers/net/fs_enet/Makefile index 1ffbe0756a0c..d4a305ee3455 100644 --- a/drivers/net/fs_enet/Makefile +++ b/drivers/net/fs_enet/Makefile @@ -8,12 +8,7 @@ fs_enet-$(CONFIG_FS_ENET_HAS_SCC) += mac-scc.o fs_enet-$(CONFIG_FS_ENET_HAS_FEC) += mac-fec.o fs_enet-$(CONFIG_FS_ENET_HAS_FCC) += mac-fcc.o -ifeq ($(CONFIG_PPC_CPM_NEW_BINDING),y) obj-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o obj-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o -else -fs_enet-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o -fs_enet-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o -endif fs_enet-objs := fs_enet-main.o $(fs_enet-m) diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index 352574a3f056..ac2c48741d04 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -36,25 +36,17 @@ #include #include #include +#include #include #include #include #include -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" /*************************************************/ -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static char version[] __devinitdata = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")" "\n"; -#endif - MODULE_AUTHOR("Pantelis Antoniou "); MODULE_DESCRIPTION("Freescale Ethernet Driver"); MODULE_LICENSE("GPL"); @@ -957,190 +949,6 @@ static int fs_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) extern int fs_mii_connect(struct net_device *dev); extern void fs_mii_disconnect(struct net_device *dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static struct net_device *fs_init_instance(struct device *dev, - struct fs_platform_info *fpi) -{ - struct net_device *ndev = NULL; - struct fs_enet_private *fep = NULL; - int privsize, i, r, err = 0, registered = 0; - - fpi->fs_no = fs_get_id(fpi); - /* guard */ - if ((unsigned int)fpi->fs_no >= FS_MAX_INDEX) - return ERR_PTR(-EINVAL); - - privsize = sizeof(*fep) + (sizeof(struct sk_buff **) * - (fpi->rx_ring + fpi->tx_ring)); - - ndev = alloc_etherdev(privsize); - if (!ndev) { - err = -ENOMEM; - goto err; - } - - fep = netdev_priv(ndev); - - fep->dev = dev; - dev_set_drvdata(dev, ndev); - fep->fpi = fpi; - if (fpi->init_ioports) - fpi->init_ioports((struct fs_platform_info *)fpi); - -#ifdef CONFIG_FS_ENET_HAS_FEC - if (fs_get_fec_index(fpi->fs_no) >= 0) - fep->ops = &fs_fec_ops; -#endif - -#ifdef CONFIG_FS_ENET_HAS_SCC - if (fs_get_scc_index(fpi->fs_no) >=0) - fep->ops = &fs_scc_ops; -#endif - -#ifdef CONFIG_FS_ENET_HAS_FCC - if (fs_get_fcc_index(fpi->fs_no) >= 0) - fep->ops = &fs_fcc_ops; -#endif - - if (fep->ops == NULL) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s No matching ops found (%d).\n", - ndev->name, fpi->fs_no); - err = -EINVAL; - goto err; - } - - r = (*fep->ops->setup_data)(ndev); - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s setup_data failed\n", - ndev->name); - err = r; - goto err; - } - - /* point rx_skbuff, tx_skbuff */ - fep->rx_skbuff = (struct sk_buff **)&fep[1]; - fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring; - - /* init locks */ - spin_lock_init(&fep->lock); - spin_lock_init(&fep->tx_lock); - - /* - * Set the Ethernet address. - */ - for (i = 0; i < 6; i++) - ndev->dev_addr[i] = fpi->macaddr[i]; - - r = (*fep->ops->allocate_bd)(ndev); - - if (fep->ring_base == NULL) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s buffer descriptor alloc failed (%d).\n", ndev->name, r); - err = r; - goto err; - } - - /* - * Set receive and transmit descriptor base. - */ - fep->rx_bd_base = fep->ring_base; - fep->tx_bd_base = fep->rx_bd_base + fpi->rx_ring; - - /* initialize ring size variables */ - fep->tx_ring = fpi->tx_ring; - fep->rx_ring = fpi->rx_ring; - - /* - * The FEC Ethernet specific entries in the device structure. - */ - ndev->open = fs_enet_open; - ndev->hard_start_xmit = fs_enet_start_xmit; - ndev->tx_timeout = fs_timeout; - ndev->watchdog_timeo = 2 * HZ; - ndev->stop = fs_enet_close; - ndev->get_stats = fs_enet_get_stats; - ndev->set_multicast_list = fs_set_multicast_list; - -#ifdef CONFIG_NET_POLL_CONTROLLER - ndev->poll_controller = fs_enet_netpoll; -#endif - - netif_napi_add(ndev, &fep->napi, - fs_enet_rx_napi, fpi->napi_weight); - - ndev->ethtool_ops = &fs_ethtool_ops; - ndev->do_ioctl = fs_ioctl; - - init_timer(&fep->phy_timer_list); - - netif_carrier_off(ndev); - - err = register_netdev(ndev); - if (err != 0) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s register_netdev failed.\n", ndev->name); - goto err; - } - registered = 1; - - - return ndev; - -err: - if (ndev != NULL) { - if (registered) - unregister_netdev(ndev); - - if (fep && fep->ops) { - (*fep->ops->free_bd)(ndev); - (*fep->ops->cleanup_data)(ndev); - } - - free_netdev(ndev); - } - - dev_set_drvdata(dev, NULL); - - return ERR_PTR(err); -} - -static int fs_cleanup_instance(struct net_device *ndev) -{ - struct fs_enet_private *fep; - const struct fs_platform_info *fpi; - struct device *dev; - - if (ndev == NULL) - return -EINVAL; - - fep = netdev_priv(ndev); - if (fep == NULL) - return -EINVAL; - - fpi = fep->fpi; - - unregister_netdev(ndev); - - dma_free_coherent(fep->dev, (fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t), - (void __force *)fep->ring_base, fep->ring_mem_addr); - - /* reset it */ - (*fep->ops->cleanup_data)(ndev); - - dev = fep->dev; - if (dev != NULL) { - dev_set_drvdata(dev, NULL); - fep->dev = NULL; - } - - free_netdev(ndev); - - return 0; -} -#endif - /**************************************************************************************/ /* handy pointer to the immap */ @@ -1167,7 +975,6 @@ static void cleanup_immap(void) /**************************************************************************************/ -#ifdef CONFIG_PPC_CPM_NEW_BINDING static int __devinit find_phy(struct device_node *np, struct fs_platform_info *fpi) { @@ -1399,121 +1206,6 @@ static void __exit fs_cleanup(void) of_unregister_platform_driver(&fs_enet_driver); cleanup_immap(); } -#else -static int __devinit fs_enet_probe(struct device *dev) -{ - struct net_device *ndev; - - /* no fixup - no device */ - if (dev->platform_data == NULL) { - printk(KERN_INFO "fs_enet: " - "probe called with no platform data; " - "remove unused devices\n"); - return -ENODEV; - } - - ndev = fs_init_instance(dev, dev->platform_data); - if (IS_ERR(ndev)) - return PTR_ERR(ndev); - return 0; -} - -static int fs_enet_remove(struct device *dev) -{ - return fs_cleanup_instance(dev_get_drvdata(dev)); -} - -static struct device_driver fs_enet_fec_driver = { - .name = "fsl-cpm-fec", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static struct device_driver fs_enet_scc_driver = { - .name = "fsl-cpm-scc", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static struct device_driver fs_enet_fcc_driver = { - .name = "fsl-cpm-fcc", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static int __init fs_init(void) -{ - int r; - - printk(KERN_INFO - "%s", version); - - r = setup_immap(); - if (r != 0) - return r; - -#ifdef CONFIG_FS_ENET_HAS_FCC - /* let's insert mii stuff */ - r = fs_enet_mdio_bb_init(); - - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - "BB PHY init failed.\n"); - return r; - } - r = driver_register(&fs_enet_fcc_driver); - if (r != 0) - goto err; -#endif - -#ifdef CONFIG_FS_ENET_HAS_FEC - r = fs_enet_mdio_fec_init(); - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - "FEC PHY init failed.\n"); - return r; - } - - r = driver_register(&fs_enet_fec_driver); - if (r != 0) - goto err; -#endif - -#ifdef CONFIG_FS_ENET_HAS_SCC - r = driver_register(&fs_enet_scc_driver); - if (r != 0) - goto err; -#endif - - return 0; -err: - cleanup_immap(); - return r; -} - -static void __exit fs_cleanup(void) -{ - driver_unregister(&fs_enet_fec_driver); - driver_unregister(&fs_enet_fcc_driver); - driver_unregister(&fs_enet_scc_driver); - cleanup_immap(); -} -#endif #ifdef CONFIG_NET_POLL_CONTROLLER static void fs_enet_netpoll(struct net_device *dev) diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h index e05389c49bbb..db46d2e72329 100644 --- a/drivers/net/fs_enet/fs_enet.h +++ b/drivers/net/fs_enet/fs_enet.h @@ -138,10 +138,6 @@ struct fs_enet_private { }; /***************************************************************************/ -#ifndef CONFIG_PPC_CPM_NEW_BINDING -int fs_enet_mdio_bb_init(void); -int fs_enet_mdio_fec_init(void); -#endif void fs_init_bds(struct net_device *dev); void fs_cleanup_bds(struct net_device *dev); diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c index 8268b3535b30..0a97fc2d97ec 100644 --- a/drivers/net/fs_enet/mac-fcc.c +++ b/drivers/net/fs_enet/mac-fcc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -42,10 +43,6 @@ #include #include -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" /*************************************************/ @@ -87,7 +84,6 @@ static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); struct fs_platform_info *fpi = fep->fpi; int ret = -EINVAL; @@ -125,44 +121,6 @@ out_fccp: iounmap(fep->fcc.fccp); out: return ret; -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq(pdev, 0); - if (fep->interrupt < 0) - return -EINVAL; - - /* Attach the memory for the FCC Parameter RAM */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fcc_pram"); - fep->fcc.ep = ioremap(r->start, r->end - r->start + 1); - if (fep->fcc.ep == NULL) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fcc_regs"); - fep->fcc.fccp = ioremap(r->start, r->end - r->start + 1); - if (fep->fcc.fccp == NULL) - return -EINVAL; - - if (fep->fpi->fcc_regs_c) { - fep->fcc.fcccp = (void __iomem *)fep->fpi->fcc_regs_c; - } else { - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "fcc_regs_c"); - fep->fcc.fcccp = ioremap(r->start, - r->end - r->start + 1); - } - - if (fep->fcc.fcccp == NULL) - return -EINVAL; - - fep->fcc.mem = (void __iomem *)fep->fpi->mem_offset; - if (fep->fcc.mem == NULL) - return -EINVAL; - - return 0; -#endif } #define FCC_NAPI_RX_EVENT_MSK (FCC_ENET_RXF | FCC_ENET_RXB) @@ -173,17 +131,6 @@ out: static int setup_data(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING - struct fs_platform_info *fpi = fep->fpi; - - fpi->cp_command = (fpi->cp_page << 26) | - (fpi->cp_block << 21) | - (12 << 6); - - fep->fcc.idx = fs_get_fcc_index(fpi->fs_no); - if ((unsigned int)fep->fcc.idx >= 3) /* max 3 FCCs */ - return -EINVAL; -#endif if (do_pd_setup(fep) != 0) return -EINVAL; @@ -304,9 +251,6 @@ static void restart(struct net_device *dev) fcc_enet_t __iomem *ep = fep->fcc.ep; dma_addr_t rx_bd_base_phys, tx_bd_base_phys; u16 paddrh, paddrm, paddrl; -#ifndef CONFIG_PPC_CPM_NEW_BINDING - u16 mem_addr; -#endif const unsigned char *mac; int i; @@ -338,19 +282,10 @@ static void restart(struct net_device *dev) * this area. */ -#ifdef CONFIG_PPC_CPM_NEW_BINDING W16(ep, fen_genfcc.fcc_riptr, fpi->dpram_offset); W16(ep, fen_genfcc.fcc_tiptr, fpi->dpram_offset + 32); W16(ep, fen_padptr, fpi->dpram_offset + 64); -#else - mem_addr = (u32) fep->fcc.mem; /* de-fixup dpram offset */ - - W16(ep, fen_genfcc.fcc_riptr, (mem_addr & 0xffff)); - W16(ep, fen_genfcc.fcc_tiptr, ((mem_addr + 32) & 0xffff)); - - W16(ep, fen_padptr, mem_addr + 64); -#endif /* fill with special symbol... */ memset_io(fep->fcc.mem + fpi->dpram_offset + 64, 0x88, 32); diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c index 8a311d1e435b..0a7d1c5c6524 100644 --- a/drivers/net/fs_enet/mac-fec.c +++ b/drivers/net/fs_enet/mac-fec.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,6 @@ #include #endif -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" #include "fec.h" @@ -99,7 +96,6 @@ static int whack_reset(fec_t __iomem *fecp) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->node, 0, NULL); @@ -111,23 +107,6 @@ static int do_pd_setup(struct fs_enet_private *fep) return -EINVAL; return 0; -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq_byname(pdev,"interrupt"); - if (fep->interrupt < 0) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - fep->fec.fecp = ioremap(r->start, r->end - r->start + 1); - - if(fep->fec.fecp == NULL) - return -EINVAL; - - return 0; -#endif } #define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB) diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c index e3557eca7b6d..029b3c7ef29c 100644 --- a/drivers/net/fs_enet/mac-scc.c +++ b/drivers/net/fs_enet/mac-scc.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,6 @@ #include #endif -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" /*************************************************/ @@ -99,7 +96,6 @@ static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->node, 0, NULL); @@ -115,27 +111,6 @@ static int do_pd_setup(struct fs_enet_private *fep) iounmap(fep->scc.sccp); return -EINVAL; } -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq_byname(pdev, "interrupt"); - if (fep->interrupt < 0) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - fep->scc.sccp = ioremap(r->start, r->end - r->start + 1); - - if (fep->scc.sccp == NULL) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram"); - fep->scc.ep = ioremap(r->start, r->end - r->start + 1); - - if (fep->scc.ep == NULL) - return -EINVAL; -#endif return 0; } @@ -149,16 +124,6 @@ static int setup_data(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING - struct fs_platform_info *fpi = fep->fpi; - - fep->scc.idx = fs_get_scc_index(fpi->fs_no); - if ((unsigned int)fep->fcc.idx >= 4) /* max 4 SCCs */ - return -EINVAL; - - fpi->cp_command = fep->fcc.idx << 6; -#endif - do_pd_setup(fep); fep->scc.hthi = 0; diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index 1620030cd33c..be4b72f4f49a 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -22,10 +22,7 @@ #include #include #include - -#ifdef CONFIG_PPC_CPM_NEW_BINDING #include -#endif #include "fs_enet.h" @@ -110,7 +107,6 @@ static struct mdiobb_ops bb_ops = { .get_mdio_data = mdio_read, }; -#ifdef CONFIG_PPC_CPM_NEW_BINDING static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np) { @@ -271,106 +267,3 @@ static void fs_enet_mdio_bb_exit(void) module_init(fs_enet_mdio_bb_init); module_exit(fs_enet_mdio_bb_exit); -#else -static int __devinit fs_mii_bitbang_init(struct bb_info *bitbang, - struct fs_mii_bb_platform_info *fmpi) -{ - bitbang->dir = (u32 __iomem *)fmpi->mdio_dir.offset; - bitbang->dat = (u32 __iomem *)fmpi->mdio_dat.offset; - bitbang->mdio_msk = 1U << (31 - fmpi->mdio_dat.bit); - bitbang->mdc_msk = 1U << (31 - fmpi->mdc_dat.bit); - - return 0; -} - -static int __devinit fs_enet_mdio_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct fs_mii_bb_platform_info *pdata; - struct mii_bus *new_bus; - struct bb_info *bitbang; - int err = 0; - - if (NULL == dev) - return -EINVAL; - - bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); - - if (NULL == bitbang) - return -ENOMEM; - - bitbang->ctrl.ops = &bb_ops; - - new_bus = alloc_mdio_bitbang(&bitbang->ctrl); - - if (NULL == new_bus) - return -ENOMEM; - - new_bus->name = "BB MII Bus", - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); - - new_bus->phy_mask = ~0x9; - pdata = (struct fs_mii_bb_platform_info *)pdev->dev.platform_data; - - if (NULL == pdata) { - printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id); - return -ENODEV; - } - - /*set up workspace*/ - fs_mii_bitbang_init(bitbang, pdata); - - new_bus->priv = bitbang; - - new_bus->irq = pdata->irq; - - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); - - err = mdiobus_register(new_bus); - - if (0 != err) { - printk (KERN_ERR "%s: Cannot register as MDIO bus\n", - new_bus->name); - goto bus_register_fail; - } - - return 0; - -bus_register_fail: - free_mdio_bitbang(new_bus); - kfree(bitbang); - - return err; -} - -static int fs_enet_mdio_remove(struct device *dev) -{ - struct mii_bus *bus = dev_get_drvdata(dev); - - mdiobus_unregister(bus); - - dev_set_drvdata(dev, NULL); - - free_mdio_bitbang(bus); - - return 0; -} - -static struct device_driver fs_enet_bb_mdio_driver = { - .name = "fsl-bb-mdio", - .bus = &platform_bus_type, - .probe = fs_enet_mdio_probe, - .remove = fs_enet_mdio_remove, -}; - -int fs_enet_mdio_bb_init(void) -{ - return driver_register(&fs_enet_bb_mdio_driver); -} - -void fs_enet_mdio_bb_exit(void) -{ - driver_unregister(&fs_enet_bb_mdio_driver); -} -#endif diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index 8f6a43b0e0ff..695f74cc4398 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -31,15 +31,12 @@ #include #include #include +#include #include #include #include -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" #include "fec.h" @@ -51,52 +48,6 @@ #define FEC_MII_LOOPS 10000 -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static int match_has_phy (struct device *dev, void* data) -{ - struct platform_device* pdev = container_of(dev, struct platform_device, dev); - struct fs_platform_info* fpi; - if(strcmp(pdev->name, (char*)data)) - { - return 0; - } - - fpi = pdev->dev.platform_data; - if((fpi)&&(fpi->has_phy)) - return 1; - return 0; -} - -static int fs_mii_fec_init(struct fec_info* fec, struct fs_mii_fec_platform_info *fmpi) -{ - struct resource *r; - fec_t __iomem *fecp; - char* name = "fsl-cpm-fec"; - - /* we need fec in order to be useful */ - struct platform_device *fec_pdev = - container_of(bus_find_device(&platform_bus_type, NULL, name, match_has_phy), - struct platform_device, dev); - - if(fec_pdev == NULL) { - printk(KERN_ERR"Unable to find PHY for %s", name); - return -ENODEV; - } - - r = platform_get_resource_byname(fec_pdev, IORESOURCE_MEM, "regs"); - - fec->fecp = fecp = ioremap(r->start,sizeof(fec_t)); - fec->mii_speed = fmpi->mii_speed; - - setbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */ - setbits32(&fecp->fec_ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN); - out_be32(&fecp->fec_ievent, FEC_ENET_MII); - out_be32(&fecp->fec_mii_speed, fec->mii_speed); - - return 0; -} -#endif - static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location) { struct fec_info* fec = bus->priv; @@ -151,7 +102,6 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus) return 0; } -#ifdef CONFIG_PPC_CPM_NEW_BINDING static void __devinit add_phy(struct mii_bus *bus, struct device_node *np) { const u32 *data; @@ -286,95 +236,3 @@ static void fs_enet_mdio_fec_exit(void) module_init(fs_enet_mdio_fec_init); module_exit(fs_enet_mdio_fec_exit); -#else -static int __devinit fs_enet_fec_mdio_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct fs_mii_fec_platform_info *pdata; - struct mii_bus *new_bus; - struct fec_info *fec; - int err = 0; - if (NULL == dev) - return -EINVAL; - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); - - if (NULL == new_bus) - return -ENOMEM; - - fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL); - - if (NULL == fec) - return -ENOMEM; - - new_bus->name = "FEC MII Bus", - new_bus->read = &fs_enet_fec_mii_read, - new_bus->write = &fs_enet_fec_mii_write, - new_bus->reset = &fs_enet_fec_mii_reset, - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); - - pdata = (struct fs_mii_fec_platform_info *)pdev->dev.platform_data; - - if (NULL == pdata) { - printk(KERN_ERR "fs_enet FEC mdio %d: Missing platform data!\n", pdev->id); - return -ENODEV; - } - - /*set up workspace*/ - - fs_mii_fec_init(fec, pdata); - new_bus->priv = fec; - - new_bus->irq = pdata->irq; - - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); - - err = mdiobus_register(new_bus); - - if (0 != err) { - printk (KERN_ERR "%s: Cannot register as MDIO bus\n", - new_bus->name); - goto bus_register_fail; - } - - return 0; - -bus_register_fail: - kfree(new_bus); - - return err; -} - - -static int fs_enet_fec_mdio_remove(struct device *dev) -{ - struct mii_bus *bus = dev_get_drvdata(dev); - - mdiobus_unregister(bus); - - dev_set_drvdata(dev, NULL); - kfree(bus->priv); - - bus->priv = NULL; - kfree(bus); - - return 0; -} - -static struct device_driver fs_enet_fec_mdio_driver = { - .name = "fsl-cpm-fec-mdio", - .bus = &platform_bus_type, - .probe = fs_enet_fec_mdio_probe, - .remove = fs_enet_fec_mdio_remove, -}; - -int fs_enet_mdio_fec_init(void) -{ - return driver_register(&fs_enet_fec_mdio_driver); -} - -void fs_enet_mdio_fec_exit(void) -{ - driver_unregister(&fs_enet_fec_mdio_driver); -} -#endif diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 9bc045b8c478..0ba21ee0f58c 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -135,11 +135,7 @@ struct fs_platform_info { u32 device_flags; int phy_addr; /* the phy address (-1 no phy) */ -#ifdef CONFIG_PPC_CPM_NEW_BINDING char bus_id[16]; -#else - const char* bus_id; -#endif int phy_irq; /* the phy irq (if it exists) */ const struct fs_mii_bus_info *bus_info; -- cgit v1.2.3 From f470021adb9190819c03d6d8c5c860a17480aa6d Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Mon, 24 Mar 2008 18:36:23 -0700 Subject: ptrace children revamp ptrace no longer fiddles with the children/sibling links, and the old ptrace_children list is gone. Now ptrace, whether of one's own children or another's via PTRACE_ATTACH, just uses the new ptraced list instead. There should be no user-visible difference that matters. The only change is the order in which do_wait() sees multiple stopped children and stopped ptrace attachees. Since wait_task_stopped() was changed earlier so it no longer reorders the children list, we already know this won't cause any new problems. Signed-off-by: Roland McGrath --- include/linux/init_task.h | 4 +- include/linux/sched.h | 26 +++--- kernel/exit.c | 226 ++++++++++++++++++++++++---------------------- kernel/fork.c | 6 +- kernel/ptrace.c | 37 +++++--- 5 files changed, 160 insertions(+), 139 deletions(-) (limited to 'include/linux') diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 9927a88674a3..93c45acf249a 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -140,8 +140,8 @@ extern struct group_info init_groups; .nr_cpus_allowed = NR_CPUS, \ }, \ .tasks = LIST_HEAD_INIT(tsk.tasks), \ - .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ - .ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \ + .ptraced = LIST_HEAD_INIT(tsk.ptraced), \ + .ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \ .real_parent = &tsk, \ .parent = &tsk, \ .children = LIST_HEAD_INIT(tsk.children), \ diff --git a/include/linux/sched.h b/include/linux/sched.h index ba2f859c6e4f..1941d8b5cf11 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1062,12 +1062,6 @@ struct task_struct { #endif struct list_head tasks; - /* - * ptrace_list/ptrace_children forms the list of my children - * that were stolen by a ptracer. - */ - struct list_head ptrace_children; - struct list_head ptrace_list; struct mm_struct *mm, *active_mm; @@ -1089,18 +1083,25 @@ struct task_struct { /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with - * p->parent->pid) + * p->real_parent->pid) */ - struct task_struct *real_parent; /* real parent process (when being debugged) */ - struct task_struct *parent; /* parent process */ + struct task_struct *real_parent; /* real parent process */ + struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */ /* - * children/sibling forms the list of my children plus the - * tasks I'm ptracing. + * children/sibling forms the list of my natural children */ struct list_head children; /* list of my children */ struct list_head sibling; /* linkage in my parent's children list */ struct task_struct *group_leader; /* threadgroup leader */ + /* + * ptraced is the list of tasks this task is using ptrace on. + * This includes both natural children and PTRACE_ATTACH targets. + * p->ptrace_entry is p's link on the p->parent->ptraced list. + */ + struct list_head ptraced; + struct list_head ptrace_entry; + /* PID/PID hash table linkage. */ struct pid_link pids[PIDTYPE_MAX]; struct list_head thread_group; @@ -1876,9 +1877,6 @@ extern void wait_task_inactive(struct task_struct * p); #define wait_task_inactive(p) do { } while (0) #endif -#define remove_parent(p) list_del_init(&(p)->sibling) -#define add_parent(p) list_add_tail(&(p)->sibling,&(p)->parent->children) - #define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks) #define for_each_process(p) \ diff --git a/kernel/exit.c b/kernel/exit.c index 7453356a961f..1e909826a804 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -71,7 +71,7 @@ static void __unhash_process(struct task_struct *p) __get_cpu_var(process_counts)--; } list_del_rcu(&p->thread_group); - remove_parent(p); + list_del_init(&p->sibling); } /* @@ -152,6 +152,18 @@ static void delayed_put_task_struct(struct rcu_head *rhp) put_task_struct(container_of(rhp, struct task_struct, rcu)); } +/* + * Do final ptrace-related cleanup of a zombie being reaped. + * + * Called with write_lock(&tasklist_lock) held. + */ +static void ptrace_release_task(struct task_struct *p) +{ + BUG_ON(!list_empty(&p->ptraced)); + ptrace_unlink(p); + BUG_ON(!list_empty(&p->ptrace_entry)); +} + void release_task(struct task_struct * p) { struct task_struct *leader; @@ -160,8 +172,7 @@ repeat: atomic_dec(&p->user->processes); proc_flush_task(p); write_lock_irq(&tasklist_lock); - ptrace_unlink(p); - BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); + ptrace_release_task(p); __exit_signal(p); /* @@ -315,9 +326,8 @@ static void reparent_to_kthreadd(void) ptrace_unlink(current); /* Reparent to init */ - remove_parent(current); current->real_parent = current->parent = kthreadd_task; - add_parent(current); + list_move_tail(¤t->sibling, ¤t->real_parent->children); /* Set the exit signal to SIGCHLD so we signal init on exit */ current->exit_signal = SIGCHLD; @@ -692,37 +702,71 @@ static void exit_mm(struct task_struct * tsk) mmput(mm); } -static void -reparent_thread(struct task_struct *p, struct task_struct *father, int traced) +/* + * Detach all tasks we were using ptrace on. + * Any that need to be release_task'd are put on the @dead list. + * + * Called with write_lock(&tasklist_lock) held. + */ +static void ptrace_exit(struct task_struct *parent, struct list_head *dead) { - if (p->pdeath_signal) - /* We already hold the tasklist_lock here. */ - group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); + struct task_struct *p, *n; - /* Move the child from its dying parent to the new one. */ - if (unlikely(traced)) { - /* Preserve ptrace links if someone else is tracing this child. */ - list_del_init(&p->ptrace_list); - if (ptrace_reparented(p)) - list_add(&p->ptrace_list, &p->real_parent->ptrace_children); - } else { - /* If this child is being traced, then we're the one tracing it - * anyway, so let go of it. + list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) { + __ptrace_unlink(p); + + if (p->exit_state != EXIT_ZOMBIE) + continue; + + /* + * If it's a zombie, our attachedness prevented normal + * parent notification or self-reaping. Do notification + * now if it would have happened earlier. If it should + * reap itself, add it to the @dead list. We can't call + * release_task() here because we already hold tasklist_lock. + * + * If it's our own child, there is no notification to do. */ - p->ptrace = 0; - remove_parent(p); - p->parent = p->real_parent; - add_parent(p); + if (!task_detached(p) && thread_group_empty(p)) { + if (!same_thread_group(p->real_parent, parent)) + do_notify_parent(p, p->exit_signal); + } - if (task_is_traced(p)) { + if (task_detached(p)) { /* - * If it was at a trace stop, turn it into - * a normal stop since it's no longer being - * traced. + * Mark it as in the process of being reaped. */ - ptrace_untrace(p); + p->exit_state = EXIT_DEAD; + list_add(&p->ptrace_entry, dead); } } +} + +/* + * Finish up exit-time ptrace cleanup. + * + * Called without locks. + */ +static void ptrace_exit_finish(struct task_struct *parent, + struct list_head *dead) +{ + struct task_struct *p, *n; + + BUG_ON(!list_empty(&parent->ptraced)); + + list_for_each_entry_safe(p, n, dead, ptrace_entry) { + list_del_init(&p->ptrace_entry); + release_task(p); + } +} + +static void reparent_thread(struct task_struct *p, struct task_struct *father) +{ + if (p->pdeath_signal) + /* We already hold the tasklist_lock here. */ + group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); + + list_move_tail(&p->sibling, &p->real_parent->children); /* If this is a threaded reparent there is no need to * notify anyone anything has happened. @@ -737,7 +781,8 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced) /* If we'd notified the old parent about this child's death, * also notify the new parent. */ - if (!traced && p->exit_state == EXIT_ZOMBIE && + if (!ptrace_reparented(p) && + p->exit_state == EXIT_ZOMBIE && !task_detached(p) && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); @@ -754,12 +799,15 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced) static void forget_original_parent(struct task_struct *father) { struct task_struct *p, *n, *reaper = father; - struct list_head ptrace_dead; - - INIT_LIST_HEAD(&ptrace_dead); + LIST_HEAD(ptrace_dead); write_lock_irq(&tasklist_lock); + /* + * First clean up ptrace if we were using it. + */ + ptrace_exit(father, &ptrace_dead); + do { reaper = next_thread(reaper); if (reaper == father) { @@ -768,58 +816,19 @@ static void forget_original_parent(struct task_struct *father) } } while (reaper->flags & PF_EXITING); - /* - * There are only two places where our children can be: - * - * - in our child list - * - in our ptraced child list - * - * Search them and reparent children. - */ list_for_each_entry_safe(p, n, &father->children, sibling) { - int ptrace; - - ptrace = p->ptrace; - - /* if father isn't the real parent, then ptrace must be enabled */ - BUG_ON(father != p->real_parent && !ptrace); - - if (father == p->real_parent) { - /* reparent with a reaper, real father it's us */ - p->real_parent = reaper; - reparent_thread(p, father, 0); - } else { - /* reparent ptraced task to its real parent */ - __ptrace_unlink (p); - if (p->exit_state == EXIT_ZOMBIE && !task_detached(p) && - thread_group_empty(p)) - do_notify_parent(p, p->exit_signal); - } - - /* - * if the ptraced child is a detached zombie we must collect - * it before we exit, or it will remain zombie forever since - * we prevented it from self-reap itself while it was being - * traced by us, to be able to see it in wait4. - */ - if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && task_detached(p))) - list_add(&p->ptrace_list, &ptrace_dead); - } - - list_for_each_entry_safe(p, n, &father->ptrace_children, ptrace_list) { p->real_parent = reaper; - reparent_thread(p, father, 1); + if (p->parent == father) { + BUG_ON(p->ptrace); + p->parent = p->real_parent; + } + reparent_thread(p, father); } write_unlock_irq(&tasklist_lock); BUG_ON(!list_empty(&father->children)); - BUG_ON(!list_empty(&father->ptrace_children)); - - list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_list) { - list_del_init(&p->ptrace_list); - release_task(p); - } + ptrace_exit_finish(father, &ptrace_dead); } /* @@ -1180,13 +1189,6 @@ static int eligible_child(enum pid_type type, struct pid *pid, int options, return 0; } - /* - * Do not consider detached threads that are - * not ptraced: - */ - if (task_detached(p) && !p->ptrace) - return 0; - /* Wait for all children (clone and not) if __WALL is set; * otherwise, wait for clone children *only* if __WCLONE is * set; otherwise, wait for non-clone children *only*. (Note: @@ -1399,7 +1401,7 @@ static int wait_task_zombie(struct task_struct *p, int options, * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_stopped(struct task_struct *p, +static int wait_task_stopped(int ptrace, struct task_struct *p, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { @@ -1407,7 +1409,7 @@ static int wait_task_stopped(struct task_struct *p, uid_t uid = 0; /* unneeded, required by compiler */ pid_t pid; - if (!(p->ptrace & PT_PTRACED) && !(options & WUNTRACED)) + if (!(options & WUNTRACED)) return 0; exit_code = 0; @@ -1416,7 +1418,7 @@ static int wait_task_stopped(struct task_struct *p, if (unlikely(!task_is_stopped_or_traced(p))) goto unlock_sig; - if (!(p->ptrace & PT_PTRACED) && p->signal->group_stop_count > 0) + if (!ptrace && p->signal->group_stop_count > 0) /* * A group stop is in progress and this is the group leader. * We won't report until all threads have stopped. @@ -1445,7 +1447,7 @@ unlock_sig: */ get_task_struct(p); pid = task_pid_vnr(p); - why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED; + why = ptrace ? CLD_TRAPPED : CLD_STOPPED; read_unlock(&tasklist_lock); if (unlikely(options & WNOWAIT)) @@ -1536,7 +1538,7 @@ static int wait_task_continued(struct task_struct *p, int options, * Returns zero if the search for a child should continue; * then *@notask_error is 0 if @p is an eligible child, or still -ECHILD. */ -static int wait_consider_task(struct task_struct *parent, +static int wait_consider_task(struct task_struct *parent, int ptrace, struct task_struct *p, int *notask_error, enum pid_type type, struct pid *pid, int options, struct siginfo __user *infop, @@ -1546,6 +1548,15 @@ static int wait_consider_task(struct task_struct *parent, if (ret <= 0) return ret; + if (likely(!ptrace) && unlikely(p->ptrace)) { + /* + * This child is hidden by ptrace. + * We aren't allowed to see it now, but eventually we will. + */ + *notask_error = 0; + return 0; + } + if (p->exit_state == EXIT_DEAD) return 0; @@ -1562,7 +1573,8 @@ static int wait_consider_task(struct task_struct *parent, *notask_error = 0; if (task_is_stopped_or_traced(p)) - return wait_task_stopped(p, options, infop, stat_addr, ru); + return wait_task_stopped(ptrace, p, options, + infop, stat_addr, ru); return wait_task_continued(p, options, infop, stat_addr, ru); } @@ -1583,11 +1595,16 @@ static int do_wait_thread(struct task_struct *tsk, int *notask_error, struct task_struct *p; list_for_each_entry(p, &tsk->children, sibling) { - int ret = wait_consider_task(tsk, p, notask_error, - type, pid, options, - infop, stat_addr, ru); - if (ret) - return ret; + /* + * Do not consider detached threads. + */ + if (!task_detached(p)) { + int ret = wait_consider_task(tsk, 0, p, notask_error, + type, pid, options, + infop, stat_addr, ru); + if (ret) + return ret; + } } return 0; @@ -1601,21 +1618,16 @@ static int ptrace_do_wait(struct task_struct *tsk, int *notask_error, struct task_struct *p; /* - * If we never saw an eligile child, check for children stolen by - * ptrace. We don't leave -ECHILD in *@notask_error if there are any, - * because we will eventually be allowed to wait for them again. + * Traditionally we see ptrace'd stopped tasks regardless of options. */ - if (!*notask_error) - return 0; + options |= WUNTRACED; - list_for_each_entry(p, &tsk->ptrace_children, ptrace_list) { - int ret = eligible_child(type, pid, options, p); - if (unlikely(ret < 0)) + list_for_each_entry(p, &tsk->ptraced, ptrace_entry) { + int ret = wait_consider_task(tsk, 1, p, notask_error, + type, pid, options, + infop, stat_addr, ru); + if (ret) return ret; - if (ret) { - *notask_error = 0; - return 0; - } } return 0; diff --git a/kernel/fork.c b/kernel/fork.c index 4bd2f516401f..adefc1131f27 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1125,8 +1125,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, */ p->group_leader = p; INIT_LIST_HEAD(&p->thread_group); - INIT_LIST_HEAD(&p->ptrace_children); - INIT_LIST_HEAD(&p->ptrace_list); + INIT_LIST_HEAD(&p->ptrace_entry); + INIT_LIST_HEAD(&p->ptraced); /* Now that the task is set up, run cgroup callbacks if * necessary. We need to run them before the task is visible @@ -1198,7 +1198,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, } if (likely(p->pid)) { - add_parent(p); + list_add_tail(&p->sibling, &p->real_parent->children); if (unlikely(p->ptrace & PT_PTRACED)) __ptrace_link(p, current->parent); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index e337390fce01..8392a9da6450 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -33,13 +33,9 @@ */ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) { - BUG_ON(!list_empty(&child->ptrace_list)); - if (child->parent == new_parent) - return; - list_add(&child->ptrace_list, &child->parent->ptrace_children); - remove_parent(child); + BUG_ON(!list_empty(&child->ptrace_entry)); + list_add(&child->ptrace_entry, &new_parent->ptraced); child->parent = new_parent; - add_parent(child); } /* @@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct *child) BUG_ON(!child->ptrace); child->ptrace = 0; - if (ptrace_reparented(child)) { - list_del_init(&child->ptrace_list); - remove_parent(child); - child->parent = child->real_parent; - add_parent(child); - } + child->parent = child->real_parent; + list_del_init(&child->ptrace_entry); if (task_is_traced(child)) ptrace_untrace(child); @@ -492,15 +484,34 @@ int ptrace_traceme(void) /* * Are we already being traced? */ +repeat: task_lock(current); if (!(current->ptrace & PT_PTRACED)) { + /* + * See ptrace_attach() comments about the locking here. + */ + unsigned long flags; + if (!write_trylock_irqsave(&tasklist_lock, flags)) { + task_unlock(current); + do { + cpu_relax(); + } while (!write_can_lock(&tasklist_lock)); + goto repeat; + } + ret = security_ptrace(current->parent, current, PTRACE_MODE_ATTACH); + /* * Set the ptrace bit in the process ptrace flags. + * Then link us on our parent's ptraced list. */ - if (!ret) + if (!ret) { current->ptrace |= PT_PTRACED; + __ptrace_link(current, current->real_parent); + } + + write_unlock_irqrestore(&tasklist_lock, flags); } task_unlock(current); return ret; -- cgit v1.2.3 From f89ab8619e5320cc9c2576f5f8dcbaf6c0ba3950 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 17 Jul 2008 14:53:48 -0700 Subject: Revert "configfs: Allow ->make_item() and ->make_group() to return detailed errors." This reverts commit 11c3b79218390a139f2d474ee1e983a672d5839a. The code will move to PTR_ERR(). Signed-off-by: Joel Becker --- Documentation/filesystems/configfs/configfs.txt | 10 ++--- .../filesystems/configfs/configfs_example.c | 14 +++---- drivers/net/netconsole.c | 10 ++--- fs/configfs/dir.c | 13 ++++--- fs/dlm/config.c | 45 ++++++++-------------- fs/ocfs2/cluster/heartbeat.c | 17 ++++---- fs/ocfs2/cluster/nodemanager.c | 45 ++++++++-------------- include/linux/configfs.h | 4 +- 8 files changed, 64 insertions(+), 94 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index 15838d706ea2..44c97e6accb2 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -233,12 +233,10 @@ accomplished via the group operations specified on the group's config_item_type. struct configfs_group_operations { - int (*make_item)(struct config_group *group, - const char *name, - struct config_item **new_item); - int (*make_group)(struct config_group *group, - const char *name, - struct config_group **new_group); + struct config_item *(*make_item)(struct config_group *group, + const char *name); + struct config_group *(*make_group)(struct config_group *group, + const char *name); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 0b422acd470c..25151fd5c2c6 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -273,13 +273,13 @@ static inline struct simple_children *to_simple_children(struct config_item *ite return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; } -static int simple_children_make_item(struct config_group *group, const char *name, struct config_item **new_item) +static struct config_item *simple_children_make_item(struct config_group *group, const char *name) { struct simple_child *simple_child; simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); if (!simple_child) - return -ENOMEM; + return NULL; config_item_init_type_name(&simple_child->item, name, @@ -287,8 +287,7 @@ static int simple_children_make_item(struct config_group *group, const char *nam simple_child->storeme = 0; - *new_item = &simple_child->item; - return 0; + return &simple_child->item; } static struct configfs_attribute simple_children_attr_description = { @@ -360,21 +359,20 @@ static struct configfs_subsystem simple_children_subsys = { * children of its own. */ -static int group_children_make_group(struct config_group *group, const char *name, struct config_group **new_group) +static struct config_group *group_children_make_group(struct config_group *group, const char *name) { struct simple_children *simple_children; simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); if (!simple_children) - return -ENOMEM; + return NULL; config_group_init_type_name(&simple_children->group, name, &simple_children_type); - *new_group = &simple_children->group; - return 0; + return &simple_children->group; } static struct configfs_attribute group_children_attr_description = { diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 387a13395015..665341e43055 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -585,9 +585,8 @@ static struct config_item_type netconsole_target_type = { * Group operations and type for netconsole_subsys. */ -static int make_netconsole_target(struct config_group *group, - const char *name, - struct config_item **new_item) +static struct config_item *make_netconsole_target(struct config_group *group, + const char *name) { unsigned long flags; struct netconsole_target *nt; @@ -599,7 +598,7 @@ static int make_netconsole_target(struct config_group *group, nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) { printk(KERN_ERR "netconsole: failed to allocate memory\n"); - return -ENOMEM; + return NULL; } nt->np.name = "netconsole"; @@ -616,8 +615,7 @@ static int make_netconsole_target(struct config_group *group, list_add(&nt->list, &target_list); spin_unlock_irqrestore(&target_list_lock, flags); - *new_item = &nt->item; - return 0; + return &nt->item; } static void drop_netconsole_target(struct config_group *group, diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 0e64312a084c..614e382a6049 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1073,24 +1073,25 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) group = NULL; item = NULL; if (type->ct_group_ops->make_group) { - ret = type->ct_group_ops->make_group(to_config_group(parent_item), name, &group); - if (!ret) { + group = type->ct_group_ops->make_group(to_config_group(parent_item), name); + if (group) { link_group(to_config_group(parent_item), group); item = &group->cg_item; } } else { - ret = type->ct_group_ops->make_item(to_config_group(parent_item), name, &item); - if (!ret) + item = type->ct_group_ops->make_item(to_config_group(parent_item), name); + if (item) link_obj(parent_item, item); } mutex_unlock(&subsys->su_mutex); kfree(name); - if (ret) { + if (!item) { /* - * If ret != 0, then link_obj() was never called. + * If item == NULL, then link_obj() was never called. * There are no extra references to clean up. */ + ret = -ENOMEM; goto out_put; } diff --git a/fs/dlm/config.c b/fs/dlm/config.c index 492d8caaaf25..eac23bd288b2 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -41,20 +41,16 @@ struct comm; struct nodes; struct node; -static int make_cluster(struct config_group *, const char *, - struct config_group **); +static struct config_group *make_cluster(struct config_group *, const char *); static void drop_cluster(struct config_group *, struct config_item *); static void release_cluster(struct config_item *); -static int make_space(struct config_group *, const char *, - struct config_group **); +static struct config_group *make_space(struct config_group *, const char *); static void drop_space(struct config_group *, struct config_item *); static void release_space(struct config_item *); -static int make_comm(struct config_group *, const char *, - struct config_item **); +static struct config_item *make_comm(struct config_group *, const char *); static void drop_comm(struct config_group *, struct config_item *); static void release_comm(struct config_item *); -static int make_node(struct config_group *, const char *, - struct config_item **); +static struct config_item *make_node(struct config_group *, const char *); static void drop_node(struct config_group *, struct config_item *); static void release_node(struct config_item *); @@ -396,8 +392,8 @@ static struct node *to_node(struct config_item *i) return i ? container_of(i, struct node, item) : NULL; } -static int make_cluster(struct config_group *g, const char *name, - struct config_group **new_g) +static struct config_group *make_cluster(struct config_group *g, + const char *name) { struct cluster *cl = NULL; struct spaces *sps = NULL; @@ -435,15 +431,14 @@ static int make_cluster(struct config_group *g, const char *name, space_list = &sps->ss_group; comm_list = &cms->cs_group; - *new_g = &cl->group; - return 0; + return &cl->group; fail: kfree(cl); kfree(gps); kfree(sps); kfree(cms); - return -ENOMEM; + return NULL; } static void drop_cluster(struct config_group *g, struct config_item *i) @@ -471,8 +466,7 @@ static void release_cluster(struct config_item *i) kfree(cl); } -static int make_space(struct config_group *g, const char *name, - struct config_group **new_g) +static struct config_group *make_space(struct config_group *g, const char *name) { struct space *sp = NULL; struct nodes *nds = NULL; @@ -495,14 +489,13 @@ static int make_space(struct config_group *g, const char *name, INIT_LIST_HEAD(&sp->members); mutex_init(&sp->members_lock); sp->members_count = 0; - *new_g = &sp->group; - return 0; + return &sp->group; fail: kfree(sp); kfree(gps); kfree(nds); - return -ENOMEM; + return NULL; } static void drop_space(struct config_group *g, struct config_item *i) @@ -529,21 +522,19 @@ static void release_space(struct config_item *i) kfree(sp); } -static int make_comm(struct config_group *g, const char *name, - struct config_item **new_i) +static struct config_item *make_comm(struct config_group *g, const char *name) { struct comm *cm; cm = kzalloc(sizeof(struct comm), GFP_KERNEL); if (!cm) - return -ENOMEM; + return NULL; config_item_init_type_name(&cm->item, name, &comm_type); cm->nodeid = -1; cm->local = 0; cm->addr_count = 0; - *new_i = &cm->item; - return 0; + return &cm->item; } static void drop_comm(struct config_group *g, struct config_item *i) @@ -563,15 +554,14 @@ static void release_comm(struct config_item *i) kfree(cm); } -static int make_node(struct config_group *g, const char *name, - struct config_item **new_i) +static struct config_item *make_node(struct config_group *g, const char *name) { struct space *sp = to_space(g->cg_item.ci_parent); struct node *nd; nd = kzalloc(sizeof(struct node), GFP_KERNEL); if (!nd) - return -ENOMEM; + return NULL; config_item_init_type_name(&nd->item, name, &node_type); nd->nodeid = -1; @@ -583,8 +573,7 @@ static int make_node(struct config_group *g, const char *name, sp->members_count++; mutex_unlock(&sp->members_lock); - *new_i = &nd->item; - return 0; + return &nd->item; } static void drop_node(struct config_group *g, struct config_item *i) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 443d108211ab..f02ccb34604d 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1489,28 +1489,25 @@ static struct o2hb_heartbeat_group *to_o2hb_heartbeat_group(struct config_group : NULL; } -static int o2hb_heartbeat_group_make_item(struct config_group *group, - const char *name, - struct config_item **new_item) +static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *group, + const char *name) { struct o2hb_region *reg = NULL; - int ret = 0; + struct config_item *ret = NULL; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); - if (reg == NULL) { - ret = -ENOMEM; - goto out; - } + if (reg == NULL) + goto out; /* ENOMEM */ config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - *new_item = ®->hr_item; + ret = ®->hr_item; spin_lock(&o2hb_live_lock); list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); out: - if (ret) + if (ret == NULL) kfree(reg); return ret; diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index b364b7052e46..cfdb08b484ed 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -644,32 +644,27 @@ out: return ret; } -static int o2nm_node_group_make_item(struct config_group *group, - const char *name, - struct config_item **new_item) +static struct config_item *o2nm_node_group_make_item(struct config_group *group, + const char *name) { struct o2nm_node *node = NULL; - int ret = 0; + struct config_item *ret = NULL; - if (strlen(name) > O2NM_MAX_NAME_LEN) { - ret = -ENAMETOOLONG; - goto out; - } + if (strlen(name) > O2NM_MAX_NAME_LEN) + goto out; /* ENAMETOOLONG */ node = kzalloc(sizeof(struct o2nm_node), GFP_KERNEL); - if (node == NULL) { - ret = -ENOMEM; - goto out; - } + if (node == NULL) + goto out; /* ENOMEM */ strcpy(node->nd_name, name); /* use item.ci_namebuf instead? */ config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); - *new_item = &node->nd_item; + ret = &node->nd_item; out: - if (ret) + if (ret == NULL) kfree(node); return ret; @@ -756,31 +751,25 @@ static struct o2nm_cluster_group *to_o2nm_cluster_group(struct config_group *gro } #endif -static int o2nm_cluster_group_make_group(struct config_group *group, - const char *name, - struct config_group **new_group) +static struct config_group *o2nm_cluster_group_make_group(struct config_group *group, + const char *name) { struct o2nm_cluster *cluster = NULL; struct o2nm_node_group *ns = NULL; - struct config_group *o2hb_group = NULL; + struct config_group *o2hb_group = NULL, *ret = NULL; void *defs = NULL; - int ret = 0; /* this runs under the parent dir's i_mutex; there can be only * one caller in here at a time */ - if (o2nm_single_cluster) { - ret = -ENOSPC; - goto out; - } + if (o2nm_single_cluster) + goto out; /* ENOSPC */ cluster = kzalloc(sizeof(struct o2nm_cluster), GFP_KERNEL); ns = kzalloc(sizeof(struct o2nm_node_group), GFP_KERNEL); defs = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL); o2hb_group = o2hb_alloc_hb_set(); - if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) { - ret = -ENOMEM; + if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) goto out; - } config_group_init_type_name(&cluster->cl_group, name, &o2nm_cluster_type); @@ -797,11 +786,11 @@ static int o2nm_cluster_group_make_group(struct config_group *group, cluster->cl_idle_timeout_ms = O2NET_IDLE_TIMEOUT_MS_DEFAULT; cluster->cl_keepalive_delay_ms = O2NET_KEEPALIVE_DELAY_MS_DEFAULT; - *new_group = &cluster->cl_group; + ret = &cluster->cl_group; o2nm_single_cluster = cluster; out: - if (ret) { + if (ret == NULL) { kfree(cluster); kfree(ns); o2hb_free_hb_set(o2hb_group); diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 0488f937634a..3ae65b1bf90f 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -165,8 +165,8 @@ struct configfs_item_operations { }; struct configfs_group_operations { - int (*make_item)(struct config_group *group, const char *name, struct config_item **new_item); - int (*make_group)(struct config_group *group, const char *name, struct config_group **new_group); + struct config_item *(*make_item)(struct config_group *group, const char *name); + struct config_group *(*make_group)(struct config_group *group, const char *name); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); -- cgit v1.2.3 From a6795e9ebb420d87af43789174689af0d66d1d35 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 17 Jul 2008 15:21:29 -0700 Subject: configfs: Allow ->make_item() and ->make_group() to return detailed errors. The configfs operations ->make_item() and ->make_group() currently return a new item/group. A return of NULL signifies an error. Because of this, -ENOMEM is the only return code bubbled up the stack. Multiple folks have requested the ability to return specific error codes when these operations fail. This patch adds that ability by changing the ->make_item/group() ops to return ERR_PTR() values. These errors are bubbled up appropriately. NULL returns are changed to -ENOMEM for compatibility. Also updated are the in-kernel users of configfs. This is a rework of reverted commit 11c3b79218390a139f2d474ee1e983a672d5839a. Signed-off-by: Joel Becker --- .../filesystems/configfs/configfs_example.c | 4 ++-- drivers/net/netconsole.c | 2 +- fs/configfs/dir.c | 25 +++++++++++++--------- fs/dlm/config.c | 8 +++---- fs/ocfs2/cluster/heartbeat.c | 10 ++------- fs/ocfs2/cluster/nodemanager.c | 16 +++++--------- include/linux/configfs.h | 3 ++- 7 files changed, 31 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 25151fd5c2c6..039648791701 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -279,7 +279,7 @@ static struct config_item *simple_children_make_item(struct config_group *group, simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); if (!simple_child) - return NULL; + return ERR_PTR(-ENOMEM); config_item_init_type_name(&simple_child->item, name, @@ -366,7 +366,7 @@ static struct config_group *group_children_make_group(struct config_group *group simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); if (!simple_children) - return NULL; + return ERR_PTR(-ENOMEM); config_group_init_type_name(&simple_children->group, name, diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 665341e43055..e13966bb5f77 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -598,7 +598,7 @@ static struct config_item *make_netconsole_target(struct config_group *group, nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) { printk(KERN_ERR "netconsole: failed to allocate memory\n"); - return NULL; + return ERR_PTR(-ENOMEM); } nt->np.name = "netconsole"; diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 614e382a6049..179589be063a 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1027,9 +1027,10 @@ EXPORT_SYMBOL(configfs_undepend_item); static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - int ret, module_got = 0; - struct config_group *group; - struct config_item *item; + int ret = 0; + int module_got = 0; + struct config_group *group = NULL; + struct config_item *item = NULL; struct config_item *parent_item; struct configfs_subsystem *subsys; struct configfs_dirent *sd; @@ -1070,28 +1071,32 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name); mutex_lock(&subsys->su_mutex); - group = NULL; - item = NULL; if (type->ct_group_ops->make_group) { group = type->ct_group_ops->make_group(to_config_group(parent_item), name); - if (group) { + if (!group) + group = ERR_PTR(-ENOMEM); + if (!IS_ERR(group)) { link_group(to_config_group(parent_item), group); item = &group->cg_item; - } + } else + ret = PTR_ERR(group); } else { item = type->ct_group_ops->make_item(to_config_group(parent_item), name); - if (item) + if (!item) + item = ERR_PTR(-ENOMEM); + if (!IS_ERR(item)) link_obj(parent_item, item); + else + ret = PTR_ERR(item); } mutex_unlock(&subsys->su_mutex); kfree(name); - if (!item) { + if (ret) { /* * If item == NULL, then link_obj() was never called. * There are no extra references to clean up. */ - ret = -ENOMEM; goto out_put; } diff --git a/fs/dlm/config.c b/fs/dlm/config.c index eac23bd288b2..c4e7d721bd8d 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -438,7 +438,7 @@ static struct config_group *make_cluster(struct config_group *g, kfree(gps); kfree(sps); kfree(cms); - return NULL; + return ERR_PTR(-ENOMEM); } static void drop_cluster(struct config_group *g, struct config_item *i) @@ -495,7 +495,7 @@ static struct config_group *make_space(struct config_group *g, const char *name) kfree(sp); kfree(gps); kfree(nds); - return NULL; + return ERR_PTR(-ENOMEM); } static void drop_space(struct config_group *g, struct config_item *i) @@ -528,7 +528,7 @@ static struct config_item *make_comm(struct config_group *g, const char *name) cm = kzalloc(sizeof(struct comm), GFP_KERNEL); if (!cm) - return NULL; + return ERR_PTR(-ENOMEM); config_item_init_type_name(&cm->item, name, &comm_type); cm->nodeid = -1; @@ -561,7 +561,7 @@ static struct config_item *make_node(struct config_group *g, const char *name) nd = kzalloc(sizeof(struct node), GFP_KERNEL); if (!nd) - return NULL; + return ERR_PTR(-ENOMEM); config_item_init_type_name(&nd->item, name, &node_type); nd->nodeid = -1; diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f02ccb34604d..7dce1612553e 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1493,24 +1493,18 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g const char *name) { struct o2hb_region *reg = NULL; - struct config_item *ret = NULL; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); if (reg == NULL) - goto out; /* ENOMEM */ + return ERR_PTR(-ENOMEM); config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - ret = ®->hr_item; - spin_lock(&o2hb_live_lock); list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); -out: - if (ret == NULL) - kfree(reg); - return ret; + return ®->hr_item; } static void o2hb_heartbeat_group_drop_item(struct config_group *group, diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index cfdb08b484ed..816a3f61330c 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -648,26 +648,19 @@ static struct config_item *o2nm_node_group_make_item(struct config_group *group, const char *name) { struct o2nm_node *node = NULL; - struct config_item *ret = NULL; if (strlen(name) > O2NM_MAX_NAME_LEN) - goto out; /* ENAMETOOLONG */ + return ERR_PTR(-ENAMETOOLONG); node = kzalloc(sizeof(struct o2nm_node), GFP_KERNEL); if (node == NULL) - goto out; /* ENOMEM */ + return ERR_PTR(-ENOMEM); strcpy(node->nd_name, name); /* use item.ci_namebuf instead? */ config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); - ret = &node->nd_item; - -out: - if (ret == NULL) - kfree(node); - - return ret; + return &node->nd_item; } static void o2nm_node_group_drop_item(struct config_group *group, @@ -762,7 +755,7 @@ static struct config_group *o2nm_cluster_group_make_group(struct config_group *g /* this runs under the parent dir's i_mutex; there can be only * one caller in here at a time */ if (o2nm_single_cluster) - goto out; /* ENOSPC */ + return ERR_PTR(-ENOSPC); cluster = kzalloc(sizeof(struct o2nm_cluster), GFP_KERNEL); ns = kzalloc(sizeof(struct o2nm_node_group), GFP_KERNEL); @@ -795,6 +788,7 @@ out: kfree(ns); o2hb_free_hb_set(o2hb_group); kfree(defs); + ret = ERR_PTR(-ENOMEM); } return ret; diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 3ae65b1bf90f..d62c19ff041c 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -148,7 +148,8 @@ struct configfs_attribute { * items. If the item is a group, it may support mkdir(2). * Groups supply one of make_group() and make_item(). If the * group supports make_group(), one can create group children. If it - * supports make_item(), one can create config_item children. If it has + * supports make_item(), one can create config_item children. make_group() + * and make_item() return ERR_PTR() on errors. If it has * default_groups on group->default_groups, it has automatically created * group children. default_groups may coexist alongsize make_group() or * make_item(), but if the group wishes to have only default_groups -- cgit v1.2.3 From d2c52b7983b95bb3fc2a784e479f832f142d4523 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Jul 2008 17:59:55 -0700 Subject: async_tx: export async_tx_quiesce Replace open coded "wait and acknowledge" instances with async_tx_quiesce. Signed-off-by: Dan Williams --- crypto/async_tx/async_memcpy.c | 10 +--------- crypto/async_tx/async_memset.c | 10 +--------- crypto/async_tx/async_tx.c | 29 ++++++++++++++++++++--------- crypto/async_tx/async_xor.c | 37 ++----------------------------------- include/linux/async_tx.h | 2 ++ 5 files changed, 26 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c index a5eda80e8427..06a7f4be9736 100644 --- a/crypto/async_tx/async_memcpy.c +++ b/crypto/async_tx/async_memcpy.c @@ -73,15 +73,7 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, pr_debug("%s: (sync) len: %zu\n", __func__, len); /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); dest_buf = kmap_atomic(dest, KM_USER0) + dest_offset; src_buf = kmap_atomic(src, KM_USER1) + src_offset; diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c index 27a97dc90a7e..d48ed22ed1c3 100644 --- a/crypto/async_tx/async_memset.c +++ b/crypto/async_tx/async_memset.c @@ -72,15 +72,7 @@ async_memset(struct page *dest, int val, unsigned int offset, dest_buf = (void *) (((char *) page_address(dest)) + offset); /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); memset(dest_buf, val, len); diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index 9325c61208a0..78a61e7f631a 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -607,15 +607,7 @@ async_trigger_callback(enum async_tx_flags flags, pr_debug("%s: (sync)\n", __func__); /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } @@ -624,6 +616,25 @@ async_trigger_callback(enum async_tx_flags flags, } EXPORT_SYMBOL_GPL(async_trigger_callback); +/** + * async_tx_quiesce - ensure tx is complete and freeable upon return + * @tx - transaction to quiesce + */ +void async_tx_quiesce(struct dma_async_tx_descriptor **tx) +{ + if (*tx) { + /* if ack is already set then we cannot be sure + * we are referring to the correct operation + */ + BUG_ON(async_tx_test_ack(*tx)); + if (dma_wait_for_async_tx(*tx) == DMA_ERROR) + panic("DMA_ERROR waiting for transaction\n"); + async_tx_ack(*tx); + *tx = NULL; + } +} +EXPORT_SYMBOL_GPL(async_tx_quiesce); + module_init(async_tx_init); module_exit(async_tx_exit); diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 19d16e452bcc..689ecce73ee1 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -30,24 +30,6 @@ #include #include -/** - * async_tx_quiesce - ensure tx is complete and freeable upon return - * @tx - transaction to quiesce - */ -static void async_tx_quiesce(struct dma_async_tx_descriptor **tx) -{ - if (*tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(*tx)); - if (dma_wait_for_async_tx(*tx) == DMA_ERROR) - panic("DMA_ERROR waiting for transaction\n"); - async_tx_ack(*tx); - *tx = NULL; - } -} - /* do_async_xor - dma map the pages and perform the xor with an engine. * This routine is marked __always_inline so it can be compiled away * when CONFIG_DMA_ENGINE=n @@ -219,15 +201,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, } /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); do_sync_xor(dest, src_list, offset, src_cnt, len, flags, depend_tx, cb_fn, cb_param); @@ -309,17 +283,10 @@ async_xor_zero_sum(struct page *dest, struct page **src_list, tx = async_xor(dest, src_list, offset, src_cnt, len, xor_flags, depend_tx, NULL, NULL); - if (tx) { - if (dma_wait_for_async_tx(tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for tx\n", - __func__); - async_tx_ack(tx); - } + async_tx_quiesce(&tx); *result = page_is_zero(dest, offset, len) ? 0 : 1; - tx = NULL; - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } diff --git a/include/linux/async_tx.h b/include/linux/async_tx.h index eb640f0acfac..9f0e7bd5bdc9 100644 --- a/include/linux/async_tx.h +++ b/include/linux/async_tx.h @@ -152,4 +152,6 @@ struct dma_async_tx_descriptor * async_trigger_callback(enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_fn_param); + +void async_tx_quiesce(struct dma_async_tx_descriptor **tx); #endif /* _ASYNC_TX_H_ */ -- cgit v1.2.3 From 3dce01713723bbcc92562bd4488e8b840a4f786c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Jul 2008 17:59:55 -0700 Subject: async_tx: remove depend_tx from async_tx_sync_epilog All callers of async_tx_sync_epilog have called async_tx_quiesce on the depend_tx, so async_tx_sync_epilog need only call the callback to complete the operation. Signed-off-by: Dan Williams --- crypto/async_tx/async_memcpy.c | 2 +- crypto/async_tx/async_memset.c | 2 +- crypto/async_tx/async_tx.c | 2 +- crypto/async_tx/async_xor.c | 7 +++---- include/linux/async_tx.h | 9 +-------- 5 files changed, 7 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c index 06a7f4be9736..ddccfb01c416 100644 --- a/crypto/async_tx/async_memcpy.c +++ b/crypto/async_tx/async_memcpy.c @@ -83,7 +83,7 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, kunmap_atomic(dest_buf, KM_USER0); kunmap_atomic(src_buf, KM_USER1); - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c index d48ed22ed1c3..5b5eb99bb244 100644 --- a/crypto/async_tx/async_memset.c +++ b/crypto/async_tx/async_memset.c @@ -76,7 +76,7 @@ async_memset(struct page *dest, int val, unsigned int offset, memset(dest_buf, val, len); - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index 78a61e7f631a..35869a37a6f2 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -609,7 +609,7 @@ async_trigger_callback(enum async_tx_flags flags, /* wait for any prerequisite operations */ async_tx_quiesce(&depend_tx); - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 689ecce73ee1..65974c6d3d7a 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -121,7 +121,6 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, static void do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, enum async_tx_flags flags, - struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { int i; @@ -150,7 +149,7 @@ do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, src_off += xor_src_cnt; } - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } /** @@ -204,7 +203,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, async_tx_quiesce(&depend_tx); do_sync_xor(dest, src_list, offset, src_cnt, len, - flags, depend_tx, cb_fn, cb_param); + flags, cb_fn, cb_param); return NULL; } @@ -287,7 +286,7 @@ async_xor_zero_sum(struct page *dest, struct page **src_list, *result = page_is_zero(dest, offset, len) ? 0 : 1; - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/include/linux/async_tx.h b/include/linux/async_tx.h index 9f0e7bd5bdc9..0f50d4cc4360 100644 --- a/include/linux/async_tx.h +++ b/include/linux/async_tx.h @@ -101,21 +101,14 @@ async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx, /** * async_tx_sync_epilog - actions to take if an operation is run synchronously - * @flags: async_tx flags - * @depend_tx: transaction depends on depend_tx * @cb_fn: function to call when the transaction completes * @cb_fn_param: parameter to pass to the callback routine */ static inline void -async_tx_sync_epilog(unsigned long flags, - struct dma_async_tx_descriptor *depend_tx, - dma_async_tx_callback cb_fn, void *cb_fn_param) +async_tx_sync_epilog(dma_async_tx_callback cb_fn, void *cb_fn_param) { if (cb_fn) cb_fn(cb_fn_param); - - if (depend_tx && (flags & ASYNC_TX_DEP_ACK)) - async_tx_ack(depend_tx); } void -- cgit v1.2.3 From 0839875e0c197ded56bbae820e699f26d6fa2697 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Jul 2008 17:59:56 -0700 Subject: async_tx: make async_tx_test_ack a boolean routine Signed-off-by: Dan Williams --- include/linux/dmaengine.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9b91d341e1fa..adb0b084eb5a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -375,16 +375,14 @@ dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan, void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, struct dma_chan *chan); -static inline void -async_tx_ack(struct dma_async_tx_descriptor *tx) +static inline void async_tx_ack(struct dma_async_tx_descriptor *tx) { tx->flags |= DMA_CTRL_ACK; } -static inline int -async_tx_test_ack(struct dma_async_tx_descriptor *tx) +static inline bool async_tx_test_ack(struct dma_async_tx_descriptor *tx) { - return tx->flags & DMA_CTRL_ACK; + return (tx->flags & DMA_CTRL_ACK) == DMA_CTRL_ACK; } #define first_dma_cap(mask) __first_dma_cap(&(mask)) -- cgit v1.2.3 From e8a0464cc950972824e2e128028ae3db666ec1ed Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 00:34:19 -0700 Subject: netdev: Allocate multiple queues for TX. alloc_netdev_mq() now allocates an array of netdev_queue structures for TX, based upon the queue_count argument. Furthermore, all accesses to the TX queues are now vectored through the netdev_get_tx_queue() and netdev_for_each_tx_queue() interfaces. This makes it easy to grep the tree for all things that want to get to a TX queue of a net device. Problem spots which are not really multiqueue aware yet, and only work with one queue, can easily be spotted by grepping for all netdev_get_tx_queue() calls that pass in a zero index. Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 +- drivers/net/hamradio/bpqether.c | 6 +- drivers/net/ifb.c | 12 ++- drivers/net/macvlan.c | 6 +- drivers/net/wireless/hostap/hostap_hw.c | 6 +- include/linux/netdevice.h | 69 ++++++++----- include/net/sch_generic.h | 37 +++++-- net/8021q/vlan_dev.c | 10 +- net/core/dev.c | 40 +++++-- net/core/rtnetlink.c | 2 +- net/mac80211/main.c | 4 +- net/mac80211/wme.c | 12 +-- net/netrom/af_netrom.c | 6 +- net/rose/af_rose.c | 6 +- net/sched/cls_api.c | 4 +- net/sched/sch_api.c | 32 ++++-- net/sched/sch_generic.c | 178 +++++++++++++++++++++++--------- net/sched/sch_teql.c | 21 ++-- 18 files changed, 320 insertions(+), 137 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index fd87dbe7999a..9737c06045d6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5042,7 +5042,9 @@ static int bond_check_params(struct bond_params *params) static struct lock_class_key bonding_netdev_xmit_lock_key; -static void bond_set_lockdep_class_one(struct netdev_queue *txq) +static void bond_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &bonding_netdev_xmit_lock_key); @@ -5050,7 +5052,7 @@ static void bond_set_lockdep_class_one(struct netdev_queue *txq) static void bond_set_lockdep_class(struct net_device *dev) { - bond_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, bond_set_lockdep_class_one, NULL); } /* Create a new bond based on the specified name and bonding parameters. diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index fb186b8c3d4d..b6500b2aacf2 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -124,14 +124,16 @@ static LIST_HEAD(bpq_devices); */ static struct lock_class_key bpq_netdev_xmit_lock_key; -static void bpq_set_lockdep_class_one(struct netdev_queue *txq) +static void bpq_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &bpq_netdev_xmit_lock_key); } static void bpq_set_lockdep_class(struct net_device *dev) { - bpq_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, bpq_set_lockdep_class_one, NULL); } /* ------------------------------------------------------------------------ */ diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index ccbd6554f6eb..897b05e79ed0 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -229,14 +229,20 @@ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); /* - * dev_ifb->tx_queue.lock is usually taken after dev->rx_queue.lock, + * dev_ifb's TX queue lock is usually taken after dev->rx_queue.lock, * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev->tx_queue.lock with dev_ifb->rx_queue.lock. + * ifb doesn't take dev's TX queue lock with dev_ifb->rx_queue.lock. * But lockdep should know that ifb has different locks from dev. */ static struct lock_class_key ifb_tx_queue_lock_key; static struct lock_class_key ifb_rx_queue_lock_key; +static void set_tx_lockdep_key(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->lock, &ifb_tx_queue_lock_key); +} static int __init ifb_init_one(int index) { @@ -258,7 +264,7 @@ static int __init ifb_init_one(int index) if (err < 0) goto err; - lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); + netdev_for_each_tx_queue(dev_ifb, set_tx_lockdep_key, NULL); lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); return 0; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 980001c2cf96..72745ce588c6 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -285,7 +285,9 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key; #define MACVLAN_STATE_MASK \ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) -static void macvlan_set_lockdep_class_one(struct netdev_queue *txq) +static void macvlan_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &macvlan_netdev_xmit_lock_key); @@ -293,7 +295,7 @@ static void macvlan_set_lockdep_class_one(struct netdev_queue *txq) static void macvlan_set_lockdep_class(struct net_device *dev) { - macvlan_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, macvlan_set_lockdep_class_one, NULL); } static int macvlan_init(struct net_device *dev) diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index c1f4bb005d92..13d5882f1f21 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3102,7 +3102,9 @@ static void prism2_clear_set_tim_queue(local_info_t *local) */ static struct lock_class_key hostap_netdev_xmit_lock_key; -static void prism2_set_lockdep_class_one(struct netdev_queue *txq) +static void prism2_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &hostap_netdev_xmit_lock_key); @@ -3110,7 +3112,7 @@ static void prism2_set_lockdep_class_one(struct netdev_queue *txq) static void prism2_set_lockdep_class(struct net_device *dev) { - prism2_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL); } static struct net_device * diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 570cf7affa72..f25d4f5a31b0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -463,7 +463,7 @@ struct netdev_queue { struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; struct netdev_queue *next_sched; -}; +} ____cacheline_aligned_in_smp; /* * The DEVICE structure. @@ -641,7 +641,9 @@ struct net_device unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ struct netdev_queue rx_queue; - struct netdev_queue tx_queue ____cacheline_aligned_in_smp; + + struct netdev_queue *_tx ____cacheline_aligned_in_smp; + unsigned int num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ /* @@ -764,6 +766,25 @@ struct net_device #define NETDEV_ALIGN 32 #define NETDEV_ALIGN_CONST (NETDEV_ALIGN - 1) +static inline +struct netdev_queue *netdev_get_tx_queue(const struct net_device *dev, + unsigned int index) +{ + return &dev->_tx[index]; +} + +static inline void netdev_for_each_tx_queue(struct net_device *dev, + void (*f)(struct net_device *, + struct netdev_queue *, + void *), + void *arg) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) + f(dev, &dev->_tx[i], arg); +} + /* * Net namespace inlines */ @@ -977,7 +998,7 @@ static inline void netif_schedule_queue(struct netdev_queue *txq) static inline void netif_schedule(struct net_device *dev) { - netif_schedule_queue(&dev->tx_queue); + netif_schedule_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -993,7 +1014,7 @@ static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) static inline void netif_start_queue(struct net_device *dev) { - netif_tx_start_queue(&dev->tx_queue); + netif_tx_start_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -1017,7 +1038,7 @@ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) static inline void netif_wake_queue(struct net_device *dev) { - netif_tx_wake_queue(&dev->tx_queue); + netif_tx_wake_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -1034,7 +1055,7 @@ static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) static inline void netif_stop_queue(struct net_device *dev) { - netif_tx_stop_queue(&dev->tx_queue); + netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -1050,7 +1071,7 @@ static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) static inline int netif_queue_stopped(const struct net_device *dev) { - return netif_tx_queue_stopped(&dev->tx_queue); + return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); } /** @@ -1134,7 +1155,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) #endif if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) - __netif_schedule(&dev->tx_queue); + __netif_schedule(netdev_get_tx_queue(dev, 0)); } /** @@ -1430,18 +1451,19 @@ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) static inline void netif_tx_lock(struct net_device *dev) { - __netif_tx_lock(&dev->tx_queue, smp_processor_id()); -} + int cpu = smp_processor_id(); + unsigned int i; -static inline void __netif_tx_lock_bh(struct netdev_queue *txq) -{ - spin_lock_bh(&txq->_xmit_lock); - txq->xmit_lock_owner = smp_processor_id(); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + __netif_tx_lock(txq, cpu); + } } static inline void netif_tx_lock_bh(struct net_device *dev) { - __netif_tx_lock_bh(&dev->tx_queue); + local_bh_disable(); + netif_tx_lock(dev); } static inline int __netif_tx_trylock(struct netdev_queue *txq) @@ -1454,7 +1476,7 @@ static inline int __netif_tx_trylock(struct netdev_queue *txq) static inline int netif_tx_trylock(struct net_device *dev) { - return __netif_tx_trylock(&dev->tx_queue); + return __netif_tx_trylock(netdev_get_tx_queue(dev, 0)); } static inline void __netif_tx_unlock(struct netdev_queue *txq) @@ -1465,18 +1487,19 @@ static inline void __netif_tx_unlock(struct netdev_queue *txq) static inline void netif_tx_unlock(struct net_device *dev) { - __netif_tx_unlock(&dev->tx_queue); -} + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + __netif_tx_unlock(txq); + } -static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) -{ - txq->xmit_lock_owner = -1; - spin_unlock_bh(&txq->_xmit_lock); } static inline void netif_tx_unlock_bh(struct net_device *dev) { - __netif_tx_unlock_bh(&dev->tx_queue); + netif_tx_unlock(dev); + local_bh_enable(); } #define HARD_TX_LOCK(dev, txq, cpu) { \ diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 5ba66b555578..b47f556c66f8 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -230,32 +230,47 @@ extern void tcf_destroy_chain(struct tcf_proto **fl); /* Reset all TX qdiscs of a device. */ static inline void qdisc_reset_all_tx(struct net_device *dev) { - qdisc_reset(dev->tx_queue.qdisc); + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) + qdisc_reset(netdev_get_tx_queue(dev, i)->qdisc); } /* Are all TX queues of the device empty? */ static inline bool qdisc_all_tx_empty(const struct net_device *dev) { - const struct netdev_queue *txq = &dev->tx_queue; - const struct Qdisc *q = txq->qdisc; + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + const struct Qdisc *q = txq->qdisc; - return (q->q.qlen == 0); + if (q->q.qlen) + return false; + } + return true; } /* Are any of the TX qdiscs changing? */ static inline bool qdisc_tx_changing(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; - - return (txq->qdisc != txq->qdisc_sleeping); + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + if (txq->qdisc != txq->qdisc_sleeping) + return true; + } + return false; } -/* Is the device using the noop qdisc? */ +/* Is the device using the noop qdisc on all queues? */ static inline bool qdisc_tx_is_noop(const struct net_device *dev) { - const struct netdev_queue *txq = &dev->tx_queue; - - return (txq->qdisc == &noop_qdisc); + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + if (txq->qdisc != &noop_qdisc) + return false; + } + return true; } static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 6b985f23fd9f..f42bc2b26b85 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -570,16 +570,18 @@ static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) */ static struct lock_class_key vlan_netdev_xmit_lock_key; -static void vlan_dev_set_lockdep_one(struct netdev_queue *txq, - int subclass) +static void vlan_dev_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_subclass) { lockdep_set_class_and_subclass(&txq->_xmit_lock, - &vlan_netdev_xmit_lock_key, subclass); + &vlan_netdev_xmit_lock_key, + *(int *)_subclass); } static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) { - vlan_dev_set_lockdep_one(&dev->tx_queue, subclass); + netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass); } static const struct header_ops vlan_header_ops = { diff --git a/net/core/dev.c b/net/core/dev.c index 9b49f74a9820..69378f250695 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1666,6 +1666,12 @@ out_kfree_skb: * --BLG */ +static struct netdev_queue *dev_pick_tx(struct net_device *dev, + struct sk_buff *skb) +{ + return netdev_get_tx_queue(dev, 0); +} + int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; @@ -1702,7 +1708,7 @@ int dev_queue_xmit(struct sk_buff *skb) } gso: - txq = &dev->tx_queue; + txq = dev_pick_tx(dev, skb); spin_lock_prefetch(&txq->lock); /* Disable soft irqs for various locks below. Also @@ -3788,8 +3794,9 @@ static void rollback_registered(struct net_device *dev) dev_put(dev); } -static void __netdev_init_queue_locks_one(struct netdev_queue *dev_queue, - struct net_device *dev) +static void __netdev_init_queue_locks_one(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_unused) { spin_lock_init(&dev_queue->_xmit_lock); netdev_set_lockdep_class(&dev_queue->_xmit_lock, dev->type); @@ -3798,8 +3805,8 @@ static void __netdev_init_queue_locks_one(struct netdev_queue *dev_queue, static void netdev_init_queue_locks(struct net_device *dev) { - __netdev_init_queue_locks_one(&dev->tx_queue, dev); - __netdev_init_queue_locks_one(&dev->rx_queue, dev); + netdev_for_each_tx_queue(dev, __netdev_init_queue_locks_one, NULL); + __netdev_init_queue_locks_one(dev, &dev->rx_queue, NULL); } /** @@ -4119,7 +4126,8 @@ static struct net_device_stats *internal_stats(struct net_device *dev) } static void netdev_init_one_queue(struct net_device *dev, - struct netdev_queue *queue) + struct netdev_queue *queue, + void *_unused) { spin_lock_init(&queue->lock); queue->dev = dev; @@ -4127,8 +4135,8 @@ static void netdev_init_one_queue(struct net_device *dev, static void netdev_init_queues(struct net_device *dev) { - netdev_init_one_queue(dev, &dev->rx_queue); - netdev_init_one_queue(dev, &dev->tx_queue); + netdev_init_one_queue(dev, &dev->rx_queue, NULL); + netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); } /** @@ -4145,9 +4153,10 @@ static void netdev_init_queues(struct net_device *dev) struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, void (*setup)(struct net_device *), unsigned int queue_count) { - void *p; + struct netdev_queue *tx; struct net_device *dev; int alloc_size; + void *p; BUG_ON(strlen(name) >= sizeof(dev->name)); @@ -4167,11 +4176,22 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, return NULL; } + tx = kzalloc(sizeof(struct netdev_queue) * queue_count, GFP_KERNEL); + if (!tx) { + printk(KERN_ERR "alloc_netdev: Unable to allocate " + "tx qdiscs.\n"); + kfree(p); + return NULL; + } + dev = (struct net_device *) (((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST); dev->padded = (char *)dev - (char *)p; dev_net_set(dev, &init_net); + dev->_tx = tx; + dev->num_tx_queues = queue_count; + if (sizeof_priv) { dev->priv = ((char *)dev + ((sizeof(struct net_device) + @@ -4205,6 +4225,8 @@ void free_netdev(struct net_device *dev) { release_net(dev_net(dev)); + kfree(dev->_tx); + /* Compatibility with error handling in drivers */ if (dev->reg_state == NETREG_UNINITIALIZED) { kfree((char *)dev - dev->padded); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 8ef9f1db610e..71edb8b36341 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -636,7 +636,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (dev->master) NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); - txq = &dev->tx_queue; + txq = netdev_get_tx_queue(dev, 0); if (txq->qdisc_sleeping) NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index af0056e7e5b3..b486e634f4fe 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -621,7 +621,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) /* ensure that TX flow won't interrupt us * until the end of the call to requeue function */ - txq = &local->mdev->tx_queue; + txq = netdev_get_tx_queue(local->mdev, 0); spin_lock_bh(&txq->lock); /* create a new queue for this aggregation */ @@ -862,7 +862,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) /* avoid ordering issues: we are the only one that can modify * the content of the qdiscs */ - txq = &local->mdev->tx_queue; + txq = netdev_get_tx_queue(local->mdev, 0); spin_lock_bh(&txq->lock); /* remove the queue for this aggregation */ ieee80211_ht_agg_queue_remove(local, sta, tid, 1); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 6ae43a3c7726..f014cd38c2d0 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -574,7 +574,7 @@ static struct Qdisc_ops wme_qdisc_ops __read_mostly = void ieee80211_install_qdisc(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); struct Qdisc *qdisc; qdisc = qdisc_create_dflt(dev, txq, @@ -596,7 +596,7 @@ void ieee80211_install_qdisc(struct net_device *dev) int ieee80211_qdisc_installed(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); return txq->qdisc_sleeping->ops == &wme_qdisc_ops; } @@ -617,7 +617,7 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { int i; - struct netdev_queue *txq = &local->mdev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, 0); struct ieee80211_sched_data *q = qdisc_priv(txq->qdisc_sleeping); DECLARE_MAC_BUF(mac); @@ -652,14 +652,14 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, } /** - * the caller needs to hold local->mdev->tx_queue.lock + * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock */ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid, u8 requeue) { struct ieee80211_hw *hw = &local->hw; - struct netdev_queue *txq = &local->mdev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, 0); struct ieee80211_sched_data *q = qdisc_priv(txq->qdisc_sleeping); int agg_queue = sta->tid_to_tx_q[tid]; @@ -676,7 +676,7 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, void ieee80211_requeue(struct ieee80211_local *local, int queue) { - struct netdev_queue *txq = &local->mdev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, 0); struct Qdisc *root_qd = txq->qdisc_sleeping; struct ieee80211_sched_data *q = qdisc_priv(root_qd); struct Qdisc *qdisc = q->queues[queue]; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 819afc449e1e..d41be0d66eb0 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -74,14 +74,16 @@ static const struct proto_ops nr_proto_ops; */ static struct lock_class_key nr_netdev_xmit_lock_key; -static void nr_set_lockdep_one(struct netdev_queue *txq) +static void nr_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key); } static void nr_set_lockdep_key(struct net_device *dev) { - nr_set_lockdep_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, nr_set_lockdep_one, NULL); } /* diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 7dbbc0891623..f3a691f34909 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -75,14 +75,16 @@ ax25_address rose_callsign; */ static struct lock_class_key rose_netdev_xmit_lock_key; -static void rose_set_lockdep_one(struct netdev_queue *txq) +static void rose_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key); } static void rose_set_lockdep_key(struct net_device *dev) { - rose_set_lockdep_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, rose_set_lockdep_one, NULL); } /* diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b483bbea6118..d0b0a9b14394 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -166,7 +166,7 @@ replay: /* Find qdisc */ if (!parent) { - struct netdev_queue *dev_queue = &dev->tx_queue; + struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); q = dev_queue->qdisc_sleeping; parent = q->handle; } else { @@ -410,7 +410,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return skb->len; - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); if (!tcm->tcm_parent) q = dev_queue->qdisc_sleeping; else diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 95873f8dd37c..830ccc544a15 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -183,9 +183,8 @@ EXPORT_SYMBOL(unregister_qdisc); (root qdisc, all its children, children of children etc.) */ -struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) +static struct Qdisc *__qdisc_lookup(struct netdev_queue *dev_queue, u32 handle) { - struct netdev_queue *dev_queue = &dev->tx_queue; struct Qdisc *q; list_for_each_entry(q, &dev_queue->qdisc_list, list) { @@ -195,6 +194,19 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) return NULL; } +struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + struct Qdisc *q = __qdisc_lookup(txq, handle); + if (q) + return q; + } + return NULL; +} + static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) { unsigned long cl; @@ -462,7 +474,7 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) } } else { - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); oqdisc = dev_queue->qdisc_sleeping; /* Prune old scheduler */ @@ -742,7 +754,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) q = dev->rx_queue.qdisc; } } else { - struct netdev_queue *dev_queue = &dev->tx_queue; + struct netdev_queue *dev_queue; + dev_queue = netdev_get_tx_queue(dev, 0); q = dev_queue->qdisc_sleeping; } if (!q) @@ -817,7 +830,8 @@ replay: q = dev->rx_queue.qdisc; } } else { - struct netdev_queue *dev_queue = &dev->tx_queue; + struct netdev_queue *dev_queue; + dev_queue = netdev_get_tx_queue(dev, 0); q = dev_queue->qdisc_sleeping; } @@ -899,7 +913,7 @@ create_n_graft: tcm->tcm_parent, tcm->tcm_parent, tca, &err); else - q = qdisc_create(dev, &dev->tx_queue, + q = qdisc_create(dev, netdev_get_tx_queue(dev, 0), tcm->tcm_parent, tcm->tcm_handle, tca, &err); if (q == NULL) { @@ -1025,7 +1039,7 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) if (idx > s_idx) s_q_idx = 0; q_idx = 0; - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; @@ -1098,7 +1112,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Step 1. Determine qdisc handle X:0 */ - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); if (pid != TC_H_ROOT) { u32 qid1 = TC_H_MAJ(pid); @@ -1275,7 +1289,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 243de935b182..4e2b865cbba0 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -40,20 +40,30 @@ */ void qdisc_lock_tree(struct net_device *dev) - __acquires(dev->tx_queue.lock) __acquires(dev->rx_queue.lock) { - spin_lock_bh(&dev->tx_queue.lock); + unsigned int i; + + local_bh_disable(); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + spin_lock(&txq->lock); + } spin_lock(&dev->rx_queue.lock); } EXPORT_SYMBOL(qdisc_lock_tree); void qdisc_unlock_tree(struct net_device *dev) __releases(dev->rx_queue.lock) - __releases(dev->tx_queue.lock) { + unsigned int i; + spin_unlock(&dev->rx_queue.lock); - spin_unlock_bh(&dev->tx_queue.lock); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + spin_unlock(&txq->lock); + } + local_bh_enable(); } EXPORT_SYMBOL(qdisc_unlock_tree); @@ -212,22 +222,37 @@ void __qdisc_run(struct netdev_queue *txq) static void dev_watchdog(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; - struct netdev_queue *txq = &dev->tx_queue; netif_tx_lock(dev); - if (txq->qdisc != &noop_qdisc) { + if (!qdisc_tx_is_noop(dev)) { if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) { - if (netif_queue_stopped(dev) && - time_after(jiffies, dev->trans_start + dev->watchdog_timeo)) { + int some_queue_stopped = 0; + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq; + + txq = netdev_get_tx_queue(dev, i); + if (netif_tx_queue_stopped(txq)) { + some_queue_stopped = 1; + break; + } + } - printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", + if (some_queue_stopped && + time_after(jiffies, (dev->trans_start + + dev->watchdog_timeo))) { + printk(KERN_INFO "NETDEV WATCHDOG: %s: " + "transmit timed out\n", dev->name); dev->tx_timeout(dev); WARN_ON_ONCE(1); } - if (!mod_timer(&dev->watchdog_timer, round_jiffies(jiffies + dev->watchdog_timeo))) + if (!mod_timer(&dev->watchdog_timer, + round_jiffies(jiffies + + dev->watchdog_timeo))) dev_hold(dev); } } @@ -542,9 +567,55 @@ void qdisc_destroy(struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_destroy); +static bool dev_all_qdisc_sleeping_noop(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + + if (txq->qdisc_sleeping != &noop_qdisc) + return false; + } + return true; +} + +static void attach_one_default_qdisc(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_unused) +{ + struct Qdisc *qdisc; + + if (dev->tx_queue_len) { + qdisc = qdisc_create_dflt(dev, dev_queue, + &pfifo_fast_ops, TC_H_ROOT); + if (!qdisc) { + printk(KERN_INFO "%s: activation failed\n", dev->name); + return; + } + list_add_tail(&qdisc->list, &dev_queue->qdisc_list); + } else { + qdisc = &noqueue_qdisc; + } + dev_queue->qdisc_sleeping = qdisc; +} + +static void transition_one_qdisc(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_need_watchdog) +{ + int *need_watchdog_p = _need_watchdog; + + spin_lock_bh(&dev_queue->lock); + rcu_assign_pointer(dev_queue->qdisc, dev_queue->qdisc_sleeping); + if (dev_queue->qdisc != &noqueue_qdisc) + *need_watchdog_p = 1; + spin_unlock_bh(&dev_queue->lock); +} + void dev_activate(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; + int need_watchdog; /* No queueing discipline is attached to device; create default one i.e. pfifo_fast for devices, @@ -552,39 +623,27 @@ void dev_activate(struct net_device *dev) virtual interfaces */ - if (txq->qdisc_sleeping == &noop_qdisc) { - struct Qdisc *qdisc; - if (dev->tx_queue_len) { - qdisc = qdisc_create_dflt(dev, txq, - &pfifo_fast_ops, - TC_H_ROOT); - if (qdisc == NULL) { - printk(KERN_INFO "%s: activation failed\n", dev->name); - return; - } - list_add_tail(&qdisc->list, &txq->qdisc_list); - } else { - qdisc = &noqueue_qdisc; - } - txq->qdisc_sleeping = qdisc; - } + if (dev_all_qdisc_sleeping_noop(dev)) + netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); if (!netif_carrier_ok(dev)) /* Delay activation until next carrier-on event */ return; - spin_lock_bh(&txq->lock); - rcu_assign_pointer(txq->qdisc, txq->qdisc_sleeping); - if (txq->qdisc != &noqueue_qdisc) { + need_watchdog = 0; + netdev_for_each_tx_queue(dev, transition_one_qdisc, &need_watchdog); + + if (need_watchdog) { dev->trans_start = jiffies; dev_watchdog_up(dev); } - spin_unlock_bh(&txq->lock); } -static void dev_deactivate_queue(struct netdev_queue *dev_queue, - struct Qdisc *qdisc_default) +static void dev_deactivate_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_qdisc_default) { + struct Qdisc *qdisc_default = _qdisc_default; struct Qdisc *qdisc; struct sk_buff *skb; @@ -603,12 +662,35 @@ static void dev_deactivate_queue(struct netdev_queue *dev_queue, kfree_skb(skb); } +static bool some_qdisc_is_running(struct net_device *dev, int lock) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *dev_queue; + int val; + + dev_queue = netdev_get_tx_queue(dev, i); + + if (lock) + spin_lock_bh(&dev_queue->lock); + + val = test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state); + + if (lock) + spin_unlock_bh(&dev_queue->lock); + + if (val) + return true; + } + return false; +} + void dev_deactivate(struct net_device *dev) { - struct netdev_queue *dev_queue = &dev->tx_queue; - int running; + bool running; - dev_deactivate_queue(dev_queue, &noop_qdisc); + netdev_for_each_tx_queue(dev, dev_deactivate_queue, &noop_qdisc); dev_watchdog_down(dev); @@ -617,17 +699,14 @@ void dev_deactivate(struct net_device *dev) /* Wait for outstanding qdisc_run calls. */ do { - while (test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state)) + while (some_qdisc_is_running(dev, 0)) yield(); /* * Double-check inside queue lock to ensure that all effects * of the queue run are visible when we return. */ - spin_lock_bh(&dev_queue->lock); - running = test_bit(__QUEUE_STATE_QDISC_RUNNING, - &dev_queue->state); - spin_unlock_bh(&dev_queue->lock); + running = some_qdisc_is_running(dev, 1); /* * The running flag should never be set at this point because @@ -642,8 +721,10 @@ void dev_deactivate(struct net_device *dev) static void dev_init_scheduler_queue(struct net_device *dev, struct netdev_queue *dev_queue, - struct Qdisc *qdisc) + void *_qdisc) { + struct Qdisc *qdisc = _qdisc; + dev_queue->qdisc = qdisc; dev_queue->qdisc_sleeping = qdisc; INIT_LIST_HEAD(&dev_queue->qdisc_list); @@ -652,18 +733,19 @@ static void dev_init_scheduler_queue(struct net_device *dev, void dev_init_scheduler(struct net_device *dev) { qdisc_lock_tree(dev); - dev_init_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); + netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); dev_init_scheduler_queue(dev, &dev->rx_queue, NULL); qdisc_unlock_tree(dev); setup_timer(&dev->watchdog_timer, dev_watchdog, (unsigned long)dev); } -static void dev_shutdown_scheduler_queue(struct net_device *dev, - struct netdev_queue *dev_queue, - struct Qdisc *qdisc_default) +static void shutdown_scheduler_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_qdisc_default) { struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + struct Qdisc *qdisc_default = _qdisc_default; if (qdisc) { dev_queue->qdisc = qdisc_default; @@ -676,8 +758,8 @@ static void dev_shutdown_scheduler_queue(struct net_device *dev, void dev_shutdown(struct net_device *dev) { qdisc_lock_tree(dev); - dev_shutdown_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); - dev_shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); + netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); + shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); BUG_TRAP(!timer_pending(&dev->watchdog_timer)); qdisc_unlock_tree(dev); } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 8ac05981be20..44a2c3451f4d 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -111,7 +111,7 @@ teql_dequeue(struct Qdisc* sch) struct sk_buff *skb; skb = __skb_dequeue(&dat->q); - dat_queue = &dat->m->dev->tx_queue; + dat_queue = netdev_get_tx_queue(dat->m->dev, 0); if (skb == NULL) { struct net_device *m = qdisc_dev(dat_queue->qdisc); if (m) { @@ -155,10 +155,13 @@ teql_destroy(struct Qdisc* sch) if (q == master->slaves) { master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { + struct netdev_queue *txq; + + txq = netdev_get_tx_queue(master->dev, 0); master->slaves = NULL; - spin_lock_bh(&master->dev->tx_queue.lock); - qdisc_reset(master->dev->tx_queue.qdisc); - spin_unlock_bh(&master->dev->tx_queue.lock); + spin_lock_bh(&txq->lock); + qdisc_reset(txq->qdisc); + spin_unlock_bh(&txq->lock); } } skb_queue_purge(&dat->q); @@ -218,7 +221,8 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt) static int __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - struct teql_sched_data *q = qdisc_priv(dev->tx_queue.qdisc); + struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); + struct teql_sched_data *q = qdisc_priv(dev_queue->qdisc); struct neighbour *mn = skb->dst->neighbour; struct neighbour *n = q->ncache; @@ -254,7 +258,8 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device * static inline int teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - if (dev->tx_queue.qdisc == &noop_qdisc) + struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); + if (txq->qdisc == &noop_qdisc) return -ENODEV; if (dev->header_ops == NULL || @@ -285,8 +290,10 @@ restart: do { struct net_device *slave = qdisc_dev(q); + struct netdev_queue *slave_txq; - if (slave->tx_queue.qdisc_sleeping != q) + slave_txq = netdev_get_tx_queue(slave, 0); + if (slave_txq->qdisc_sleeping != q) continue; if (netif_queue_stopped(slave) || __netif_subqueue_stopped(slave, subq) || -- cgit v1.2.3 From 09e83b5d7d1878065e2453239b49b684cd0fe4e5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 01:52:12 -0700 Subject: netdev: Kill NETIF_F_MULTI_QUEUE. There is no need for a feature bit for something that can be tested by simply checking the TX queue count. Signed-off-by: David S. Miller --- Documentation/networking/multiqueue.txt | 11 ----------- drivers/net/cpmac.c | 1 - drivers/net/igb/igb_main.c | 2 -- drivers/net/ixgbe/ixgbe_main.c | 2 -- drivers/net/s2io.c | 2 -- include/linux/netdevice.h | 4 +--- net/mac80211/main.c | 3 --- 7 files changed, 1 insertion(+), 24 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt index e6dc1ee9e8f1..d391ea631141 100644 --- a/Documentation/networking/multiqueue.txt +++ b/Documentation/networking/multiqueue.txt @@ -24,15 +24,4 @@ netif_{start|stop|wake}_subqueue() functions to manage each queue while the device is still operational. netdev->queue_lock is still used when the device comes online or when it's completely shut down (unregister_netdev(), etc.). -Finally, the base driver should indicate that it is a multiqueue device. The -feature flag NETIF_F_MULTI_QUEUE should be added to the netdev->features -bitmap on device initialization. Below is an example from e1000: - -#ifdef CONFIG_E1000_MQ - if ( (adapter->hw.mac.type == e1000_82571) || - (adapter->hw.mac.type == e1000_82572) || - (adapter->hw.mac.type == e1000_80003es2lan)) - netdev->features |= NETIF_F_MULTI_QUEUE; -#endif - Author: Peter P. Waskiewicz Jr. diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index d630e2a72f42..7c7b54e4828e 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -1165,7 +1165,6 @@ static int __devinit cpmac_probe(struct platform_device *pdev) dev->set_multicast_list = cpmac_set_multicast_list; dev->tx_timeout = cpmac_tx_timeout; dev->ethtool_ops = &cpmac_ethtool_ops; - dev->features |= NETIF_F_MULTI_QUEUE; netif_napi_add(dev, &priv->napi, cpmac_poll, 64); diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 64a150a16f39..471c194cd54e 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1155,8 +1155,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - netdev->features |= NETIF_F_MULTI_QUEUE; - netdev->features |= NETIF_F_LLTX; adapter->en_mng_pt = igb_enable_mng_pass_thru(&adapter->hw); diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 7d8bf94d3783..e6df9233f5ef 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -3566,8 +3566,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - netdev->features |= NETIF_F_MULTI_QUEUE; - /* make sure the EEPROM is good */ if (ixgbe_validate_eeprom_checksum(hw, NULL) < 0) { dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n"); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 517425dcb77c..5f0fcb04afff 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -7966,8 +7966,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) dev->features |= NETIF_F_UFO; dev->features |= NETIF_F_HW_CSUM; } - if (config->multiq) - dev->features |= NETIF_F_MULTI_QUEUE; dev->tx_timeout = &s2io_tx_watchdog; dev->watchdog_timeo = WATCH_DOG_TIMEOUT; INIT_WORK(&sp->rst_timer_task, s2io_restart_nic); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f25d4f5a31b0..c02227b9dd7b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -533,7 +533,6 @@ struct net_device #define NETIF_F_LLTX 4096 /* LockLess TX - deprecated. Please */ /* do not use LLTX in new drivers */ #define NETIF_F_NETNS_LOCAL 8192 /* Does not change network namespaces */ -#define NETIF_F_MULTI_QUEUE 16384 /* Has multiple TX/RX queues */ #define NETIF_F_LRO 32768 /* large receive offload */ /* Segmentation offload features */ @@ -1163,11 +1162,10 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) * @dev: network device * * Check if device has multiple transmit queues - * Always falls if NETDEVICE_MULTIQUEUE is not configured */ static inline int netif_is_multiqueue(const struct net_device *dev) { - return (!!(NETIF_F_MULTI_QUEUE & dev->features)); + return (dev->num_tx_queues > 1); } /* Use this variant when it is known for sure that it diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b486e634f4fe..c74607eda1ee 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1678,9 +1678,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (!mdev) goto fail_mdev_alloc; - if (ieee80211_num_queues(hw) > 1) - mdev->features |= NETIF_F_MULTI_QUEUE; - mwdev = netdev_priv(mdev); mdev->ieee80211_ptr = mwdev; mwdev->wiphy = local->hw.wiphy; -- cgit v1.2.3 From 1d8ae3fdeb001b8f534a6782c261aba6ec1779f5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 02:52:19 -0700 Subject: pkt_sched: Remove RR scheduler. This actually fixes a bug added by the RR scheduler changes. The ->bands and ->prio2band parameters were being set outside of the sch_tree_lock() and thus could result in strange behavior and inconsistencies. It might be possible, in the new design (where there will be one qdisc per device TX queue) to allow similar functionality via a TX hash algorithm for RR but I really see no reason to export this aspect of how these multiqueue cards actually implement the scheduling of the the individual DMA TX rings and the single physical MAC/PHY port. Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 9 --- net/sched/sch_prio.c | 136 ++++++---------------------------------------- 2 files changed, 16 insertions(+), 129 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index dbb7ac37960d..87f4e0fa8f27 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -103,15 +103,6 @@ struct tc_prio_qopt __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ }; -enum -{ - TCA_PRIO_UNSPEC, - TCA_PRIO_MQ, - __TCA_PRIO_MAX -}; - -#define TCA_PRIO_MAX (__TCA_PRIO_MAX - 1) - /* TBF section */ struct tc_tbf_qopt diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 39157f7bc046..536ca474dc69 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -24,11 +24,9 @@ struct prio_sched_data { int bands; - int curband; /* for round-robin */ struct tcf_proto *filter_list; u8 prio2band[TC_PRIO_MAX+1]; struct Qdisc *queues[TCQ_PRIO_BANDS]; - int mq; }; @@ -55,17 +53,14 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) if (!q->filter_list || err < 0) { if (TC_H_MAJ(band)) band = 0; - band = q->prio2band[band&TC_PRIO_MAX]; - goto out; + return q->queues[q->prio2band[band&TC_PRIO_MAX]]; } band = res.classid; } band = TC_H_MIN(band) - 1; if (band >= q->bands) - band = q->prio2band[0]; -out: - if (q->mq) - skb_set_queue_mapping(skb, band); + return q->queues[q->prio2band[0]]; + return q->queues[band]; } @@ -123,68 +118,23 @@ prio_requeue(struct sk_buff *skb, struct Qdisc* sch) } -static struct sk_buff * -prio_dequeue(struct Qdisc* sch) +static struct sk_buff *prio_dequeue(struct Qdisc* sch) { - struct sk_buff *skb; struct prio_sched_data *q = qdisc_priv(sch); int prio; - struct Qdisc *qdisc; for (prio = 0; prio < q->bands; prio++) { - /* Check if the target subqueue is available before - * pulling an skb. This way we avoid excessive requeues - * for slower queues. - */ - if (!__netif_subqueue_stopped(qdisc_dev(sch), - (q->mq ? prio : 0))) { - qdisc = q->queues[prio]; - skb = qdisc->dequeue(qdisc); - if (skb) { - sch->q.qlen--; - return skb; - } + struct Qdisc *qdisc = q->queues[prio]; + struct sk_buff *skb = qdisc->dequeue(qdisc); + if (skb) { + sch->q.qlen--; + return skb; } } return NULL; } -static struct sk_buff *rr_dequeue(struct Qdisc* sch) -{ - struct sk_buff *skb; - struct prio_sched_data *q = qdisc_priv(sch); - struct Qdisc *qdisc; - int bandcount; - - /* Only take one pass through the queues. If nothing is available, - * return nothing. - */ - for (bandcount = 0; bandcount < q->bands; bandcount++) { - /* Check if the target subqueue is available before - * pulling an skb. This way we avoid excessive requeues - * for slower queues. If the queue is stopped, try the - * next queue. - */ - if (!__netif_subqueue_stopped(qdisc_dev(sch), - (q->mq ? q->curband : 0))) { - qdisc = q->queues[q->curband]; - skb = qdisc->dequeue(qdisc); - if (skb) { - sch->q.qlen--; - q->curband++; - if (q->curband >= q->bands) - q->curband = 0; - return skb; - } - } - q->curband++; - if (q->curband >= q->bands) - q->curband = 0; - } - return NULL; -} - static unsigned int prio_drop(struct Qdisc* sch) { struct prio_sched_data *q = qdisc_priv(sch); @@ -229,45 +179,22 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt) { struct prio_sched_data *q = qdisc_priv(sch); struct tc_prio_qopt *qopt; - struct nlattr *tb[TCA_PRIO_MAX + 1]; - int err; int i; - err = nla_parse_nested_compat(tb, TCA_PRIO_MAX, opt, NULL, qopt, - sizeof(*qopt)); - if (err < 0) - return err; - - q->bands = qopt->bands; - /* If we're multiqueue, make sure the number of incoming bands - * matches the number of queues on the device we're associating with. - * If the number of bands requested is zero, then set q->bands to - * dev->egress_subqueue_count. Also, the root qdisc must be the - * only one that is enabled for multiqueue, since it's the only one - * that interacts with the underlying device. - */ - q->mq = nla_get_flag(tb[TCA_PRIO_MQ]); - if (q->mq) { - if (sch->parent != TC_H_ROOT) - return -EINVAL; - if (netif_is_multiqueue(qdisc_dev(sch))) { - if (q->bands == 0) - q->bands = qdisc_dev(sch)->egress_subqueue_count; - else if (q->bands != qdisc_dev(sch)->egress_subqueue_count) - return -EINVAL; - } else - return -EOPNOTSUPP; - } + if (nla_len(opt) < sizeof(*qopt)) + return -EINVAL; + qopt = nla_data(opt); - if (q->bands > TCQ_PRIO_BANDS || q->bands < 2) + if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2) return -EINVAL; for (i=0; i<=TC_PRIO_MAX; i++) { - if (qopt->priomap[i] >= q->bands) + if (qopt->priomap[i] >= qopt->bands) return -EINVAL; } sch_tree_lock(sch); + q->bands = qopt->bands; memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1); for (i=q->bands; imq) { - if (nla_put_flag(skb, TCA_PRIO_MQ) < 0) - goto nla_put_failure; - } nla_nest_compat_end(skb, nest); return skb->len; @@ -509,44 +432,17 @@ static struct Qdisc_ops prio_qdisc_ops __read_mostly = { .owner = THIS_MODULE, }; -static struct Qdisc_ops rr_qdisc_ops __read_mostly = { - .next = NULL, - .cl_ops = &prio_class_ops, - .id = "rr", - .priv_size = sizeof(struct prio_sched_data), - .enqueue = prio_enqueue, - .dequeue = rr_dequeue, - .requeue = prio_requeue, - .drop = prio_drop, - .init = prio_init, - .reset = prio_reset, - .destroy = prio_destroy, - .change = prio_tune, - .dump = prio_dump, - .owner = THIS_MODULE, -}; - static int __init prio_module_init(void) { - int err; - - err = register_qdisc(&prio_qdisc_ops); - if (err < 0) - return err; - err = register_qdisc(&rr_qdisc_ops); - if (err < 0) - unregister_qdisc(&prio_qdisc_ops); - return err; + return register_qdisc(&prio_qdisc_ops); } static void __exit prio_module_exit(void) { unregister_qdisc(&prio_qdisc_ops); - unregister_qdisc(&rr_qdisc_ops); } module_init(prio_module_init) module_exit(prio_module_exit) MODULE_LICENSE("GPL"); -MODULE_ALIAS("sch_rr"); -- cgit v1.2.3 From fd2ea0a79faad824258af5dcec1927aa24d81c16 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 01:56:23 -0700 Subject: net: Use queue aware tests throughout. This effectively "flips the switch" by making the core networking and multiqueue-aware drivers use the new TX multiqueue structures. Non-multiqueue drivers need no changes. The interfaces they use such as netif_stop_queue() degenerate into an operation on TX queue zero. So everything "just works" for them. Code that really wants to do "X" to all TX queues now invokes a routine that does so, such as netif_tx_wake_all_queues(), netif_tx_stop_all_queues(), etc. pktgen and netpoll required a little bit more surgery than the others. In particular the pktgen changes, whilst functional, could be largely improved. The initial check in pktgen_xmit() will sometimes check the wrong queue, which is mostly harmless. The thing to do is probably to invoke fill_packet() earlier. The bulk of the netpoll changes is to make the code operate solely on the TX queue indicated by by the SKB queue mapping. Setting of the SKB queue mapping is entirely confined inside of net/core/dev.c:dev_pick_tx(). If we end up needing any kind of special semantics (drops, for example) it will be implemented here. Finally, we now have a "real_num_tx_queues" which is where the driver indicates how many TX queues are actually active. With IGB changes from Jeff Kirsher. Signed-off-by: David S. Miller --- drivers/net/cpmac.c | 20 ++++------ drivers/net/igb/igb_main.c | 19 +++------ drivers/net/ixgbe/ixgbe_ethtool.c | 10 +---- drivers/net/ixgbe/ixgbe_main.c | 15 +++---- drivers/net/s2io.c | 48 +++++++++-------------- include/linux/netdevice.h | 82 ++++++++++++++++++++++++++++++++++----- include/net/pkt_sched.h | 4 +- net/core/dev.c | 28 ++++++------- net/core/netpoll.c | 24 +++++++----- net/core/pktgen.c | 69 ++++++++++++++++++++------------ net/sched/sch_generic.c | 5 +-- net/sched/sch_teql.c | 6 +-- 12 files changed, 187 insertions(+), 143 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 7c7b54e4828e..fbd4280c102c 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -544,7 +544,7 @@ fatal_error: spin_unlock(&priv->rx_lock); netif_rx_complete(priv->dev, napi); - netif_stop_queue(priv->dev); + netif_tx_stop_all_queues(priv->dev); napi_disable(&priv->napi); atomic_inc(&priv->reset_pending); @@ -750,9 +750,7 @@ static void cpmac_hw_error(struct work_struct *work) barrier(); atomic_dec(&priv->reset_pending); - for (i = 0; i < CPMAC_QUEUES; i++) - netif_wake_subqueue(priv->dev, i); - netif_wake_queue(priv->dev); + netif_tx_wake_all_queues(priv->dev); cpmac_write(priv->regs, CPMAC_MAC_INT_ENABLE, 3); } @@ -781,7 +779,7 @@ static void cpmac_check_status(struct net_device *dev) dev->name, tx_code, tx_channel, macstatus); } - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); cpmac_hw_stop(dev); if (schedule_work(&priv->reset_work)) atomic_inc(&priv->reset_pending); @@ -842,9 +840,7 @@ static void cpmac_tx_timeout(struct net_device *dev) barrier(); atomic_dec(&priv->reset_pending); - netif_wake_queue(priv->dev); - for (i = 0; i < CPMAC_QUEUES; i++) - netif_wake_subqueue(dev, i); + netif_tx_wake_all_queues(priv->dev); } static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) @@ -935,7 +931,7 @@ static void cpmac_adjust_link(struct net_device *dev) spin_lock(&priv->lock); if (priv->phy->link) { - netif_start_queue(dev); + netif_tx_start_all_queues(dev); if (priv->phy->duplex != priv->oldduplex) { new_state = 1; priv->oldduplex = priv->phy->duplex; @@ -949,10 +945,10 @@ static void cpmac_adjust_link(struct net_device *dev) if (!priv->oldlink) { new_state = 1; priv->oldlink = 1; - netif_schedule(dev); + netif_tx_schedule_all(dev); } } else if (priv->oldlink) { - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); new_state = 1; priv->oldlink = 0; priv->oldspeed = 0; @@ -1072,7 +1068,7 @@ static int cpmac_stop(struct net_device *dev) struct cpmac_priv *priv = netdev_priv(dev); struct resource *mem; - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); cancel_work_sync(&priv->reset_work); napi_disable(&priv->napi); diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 471c194cd54e..81bba6983dde 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -533,7 +533,7 @@ msi_only: adapter->flags |= IGB_FLAG_HAS_MSI; /* Notify the stack of the (possibly) reduced Tx Queue count. */ - adapter->netdev->egress_subqueue_count = adapter->num_tx_queues; + adapter->netdev->real_num_tx_queues = adapter->num_tx_queues; return; } @@ -821,9 +821,7 @@ void igb_down(struct igb_adapter *adapter) wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN); /* flush and sleep below */ - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); /* disable transmits in the hardware */ tctl = rd32(E1000_TCTL); @@ -1266,9 +1264,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, /* tell the stack to leave us alone until igb_open() is called */ netif_carrier_off(netdev); - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); strcpy(netdev->name, "eth%d"); err = register_netdev(netdev); @@ -2315,7 +2311,6 @@ static void igb_watchdog_task(struct work_struct *work) struct e1000_mac_info *mac = &adapter->hw.mac; u32 link; s32 ret_val; - int i; if ((netif_carrier_ok(netdev)) && (rd32(E1000_STATUS) & E1000_STATUS_LU)) @@ -2371,9 +2366,7 @@ static void igb_watchdog_task(struct work_struct *work) } netif_carrier_on(netdev); - netif_wake_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_wake_subqueue(netdev, i); + netif_tx_wake_all_queues(netdev); if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->phy_info_timer, @@ -2385,9 +2378,7 @@ static void igb_watchdog_task(struct work_struct *work) adapter->link_duplex = 0; dev_info(&adapter->pdev->dev, "NIC Link is Down\n"); netif_carrier_off(netdev); - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 81b769093d22..3efe5dda10af 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -252,16 +252,10 @@ static int ixgbe_set_tso(struct net_device *netdev, u32 data) netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; } else { - struct ixgbe_adapter *adapter = netdev_priv(netdev); - int i; - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); netdev->features &= ~NETIF_F_TSO; netdev->features &= ~NETIF_F_TSO6; - for (i = 0; i < adapter->num_tx_queues; i++) - netif_start_subqueue(netdev, i); - netif_start_queue(netdev); + netif_tx_start_all_queues(netdev); } return 0; } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index e6df9233f5ef..6af8fb5c4b5f 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -2013,7 +2013,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter) del_timer_sync(&adapter->watchdog_timer); netif_carrier_off(netdev); - netif_stop_queue(netdev); + netif_tx_stop_all_queues(netdev); if (!pci_channel_offline(adapter->pdev)) ixgbe_reset(adapter); @@ -2359,7 +2359,7 @@ try_msi: out: /* Notify the stack of the (possibly) reduced Tx Queue count. */ - adapter->netdev->egress_subqueue_count = adapter->num_tx_queues; + adapter->netdev->real_num_tx_queues = adapter->num_tx_queues; return err; } @@ -2896,7 +2896,6 @@ static void ixgbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; bool link_up; u32 link_speed = 0; - int i; adapter->hw.mac.ops.check_link(&adapter->hw, &(link_speed), &link_up); @@ -2917,9 +2916,7 @@ static void ixgbe_watchdog(unsigned long data) (FLOW_TX ? "TX" : "None")))); netif_carrier_on(netdev); - netif_wake_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_wake_subqueue(netdev, i); + netif_tx_wake_all_queues(netdev); } else { /* Force detection of hung controller */ adapter->detect_tx_hung = true; @@ -2928,7 +2925,7 @@ static void ixgbe_watchdog(unsigned long data) if (netif_carrier_ok(netdev)) { DPRINTK(LINK, INFO, "NIC Link is Down\n"); netif_carrier_off(netdev); - netif_stop_queue(netdev); + netif_tx_stop_all_queues(netdev); } } @@ -3631,9 +3628,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, ixgbe_start_hw(hw); netif_carrier_off(netdev); - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); ixgbe_napi_add_all(adapter); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 5f0fcb04afff..9dae40ccf048 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -545,63 +545,53 @@ static struct pci_driver s2io_driver = { /* netqueue manipulation helper functions */ static inline void s2io_stop_all_tx_queue(struct s2io_nic *sp) { - int i; - if (sp->config.multiq) { - for (i = 0; i < sp->config.tx_fifo_num; i++) - netif_stop_subqueue(sp->dev, i); - } else { + if (!sp->config.multiq) { + int i; + for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_STOP; - netif_stop_queue(sp->dev); } + netif_tx_stop_all_queues(sp->dev); } static inline void s2io_stop_tx_queue(struct s2io_nic *sp, int fifo_no) { - if (sp->config.multiq) - netif_stop_subqueue(sp->dev, fifo_no); - else { + if (!sp->config.multiq) sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_STOP; - netif_stop_queue(sp->dev); - } + + netif_tx_stop_all_queues(sp->dev); } static inline void s2io_start_all_tx_queue(struct s2io_nic *sp) { - int i; - if (sp->config.multiq) { - for (i = 0; i < sp->config.tx_fifo_num; i++) - netif_start_subqueue(sp->dev, i); - } else { + if (!sp->config.multiq) { + int i; + for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; - netif_start_queue(sp->dev); } + netif_tx_start_all_queues(sp->dev); } static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no) { - if (sp->config.multiq) - netif_start_subqueue(sp->dev, fifo_no); - else { + if (!sp->config.multiq) sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_START; - netif_start_queue(sp->dev); - } + + netif_tx_start_all_queues(sp->dev); } static inline void s2io_wake_all_tx_queue(struct s2io_nic *sp) { - int i; - if (sp->config.multiq) { - for (i = 0; i < sp->config.tx_fifo_num; i++) - netif_wake_subqueue(sp->dev, i); - } else { + if (!sp->config.multiq) { + int i; + for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; - netif_wake_queue(sp->dev); } + netif_tx_wake_all_queues(sp->dev); } static inline void s2io_wake_tx_queue( @@ -8691,5 +8681,5 @@ static void s2io_io_resume(struct pci_dev *pdev) } netif_device_attach(netdev); - netif_wake_queue(netdev); + netif_tx_wake_all_queues(netdev); } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c02227b9dd7b..b5c1e7df64fc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -642,7 +642,13 @@ struct net_device struct netdev_queue rx_queue; struct netdev_queue *_tx ____cacheline_aligned_in_smp; + + /* Number of TX queues allocated at alloc_netdev_mq() time */ unsigned int num_tx_queues; + + /* Number of TX queues currently active in device */ + unsigned int real_num_tx_queues; + unsigned long tx_queue_len; /* Max frames per queue allowed */ /* @@ -1000,6 +1006,14 @@ static inline void netif_schedule(struct net_device *dev) netif_schedule_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_schedule_all(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) + netif_schedule_queue(netdev_get_tx_queue(dev, i)); +} + /** * netif_start_queue - allow transmit * @dev: network device @@ -1016,6 +1030,16 @@ static inline void netif_start_queue(struct net_device *dev) netif_tx_start_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_start_all_queues(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_start_queue(txq); + } +} + /** * netif_wake_queue - restart transmit * @dev: network device @@ -1040,6 +1064,16 @@ static inline void netif_wake_queue(struct net_device *dev) netif_tx_wake_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_wake_all_queues(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_wake_queue(txq); + } +} + /** * netif_stop_queue - stop transmitted packets * @dev: network device @@ -1057,6 +1091,16 @@ static inline void netif_stop_queue(struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_stop_all_queues(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_stop_queue(txq); + } +} + /** * netif_queue_stopped - test if transmit queue is flowblocked * @dev: network device @@ -1100,7 +1144,8 @@ static inline int netif_running(const struct net_device *dev) */ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) { - clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); + clear_bit(__QUEUE_STATE_XOFF, &txq->state); } /** @@ -1112,11 +1157,12 @@ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) */ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; #endif - set_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + set_bit(__QUEUE_STATE_XOFF, &txq->state); } /** @@ -1129,8 +1175,8 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { - return test_bit(__QUEUE_STATE_XOFF, - &dev->egress_subqueue[queue_index].state); + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); + return test_bit(__QUEUE_STATE_XOFF, &txq->state); } static inline int netif_subqueue_stopped(const struct net_device *dev, @@ -1148,13 +1194,13 @@ static inline int netif_subqueue_stopped(const struct net_device *dev, */ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; #endif - if (test_and_clear_bit(__QUEUE_STATE_XOFF, - &dev->egress_subqueue[queue_index].state)) - __netif_schedule(netdev_get_tx_queue(dev, 0)); + if (test_and_clear_bit(__QUEUE_STATE_XOFF, &txq->state)) + __netif_schedule(txq); } /** @@ -1198,7 +1244,8 @@ extern int dev_set_mtu(struct net_device *, int); extern int dev_set_mac_address(struct net_device *, struct sockaddr *); extern int dev_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev); + struct net_device *dev, + struct netdev_queue *txq); extern int netdev_budget; @@ -1447,6 +1494,12 @@ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) txq->xmit_lock_owner = cpu; } +static inline void __netif_tx_lock_bh(struct netdev_queue *txq) +{ + spin_lock_bh(&txq->_xmit_lock); + txq->xmit_lock_owner = smp_processor_id(); +} + static inline void netif_tx_lock(struct net_device *dev) { int cpu = smp_processor_id(); @@ -1483,6 +1536,12 @@ static inline void __netif_tx_unlock(struct netdev_queue *txq) spin_unlock(&txq->_xmit_lock); } +static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock_bh(&txq->_xmit_lock); +} + static inline void netif_tx_unlock(struct net_device *dev) { unsigned int i; @@ -1514,8 +1573,13 @@ static inline void netif_tx_unlock_bh(struct net_device *dev) static inline void netif_tx_disable(struct net_device *dev) { + unsigned int i; + netif_tx_lock_bh(dev); - netif_stop_queue(dev); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_stop_queue(txq); + } netif_tx_unlock_bh(dev); } diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index d58c1a5eb845..cb9527815606 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -88,9 +88,7 @@ extern void __qdisc_run(struct netdev_queue *txq); static inline void qdisc_run(struct netdev_queue *txq) { - struct net_device *dev = txq->dev; - - if (!netif_queue_stopped(dev) && + if (!netif_tx_queue_stopped(txq) && !test_and_set_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state)) __qdisc_run(txq); } diff --git a/net/core/dev.c b/net/core/dev.c index 69378f250695..f027a1ac4fbb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1598,7 +1598,8 @@ static int dev_gso_segment(struct sk_buff *skb) return 0; } -int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *txq) { if (likely(!skb->next)) { if (!list_empty(&ptype_all)) @@ -1627,9 +1628,7 @@ gso: skb->next = nskb; return rc; } - if (unlikely((netif_queue_stopped(dev) || - netif_subqueue_stopped(dev, skb)) && - skb->next)) + if (unlikely(netif_tx_queue_stopped(txq) && skb->next)) return NETDEV_TX_BUSY; } while (skb->next); @@ -1669,7 +1668,10 @@ out_kfree_skb: static struct netdev_queue *dev_pick_tx(struct net_device *dev, struct sk_buff *skb) { - return netdev_get_tx_queue(dev, 0); + u16 queue_index = 0; + + skb_set_queue_mapping(skb, queue_index); + return netdev_get_tx_queue(dev, queue_index); } int dev_queue_xmit(struct sk_buff *skb) @@ -1737,8 +1739,6 @@ gso: spin_lock(&txq->lock); q = txq->qdisc; if (q->enqueue) { - /* reset queue_mapping to zero */ - skb_set_queue_mapping(skb, 0); rc = q->enqueue(skb, q); qdisc_run(txq); spin_unlock(&txq->lock); @@ -1768,10 +1768,9 @@ gso: HARD_TX_LOCK(dev, txq, cpu); - if (!netif_queue_stopped(dev) && - !netif_subqueue_stopped(dev, skb)) { + if (!netif_tx_queue_stopped(txq)) { rc = 0; - if (!dev_hard_start_xmit(skb, dev)) { + if (!dev_hard_start_xmit(skb, dev, txq)) { HARD_TX_UNLOCK(dev, txq); goto out; } @@ -4160,8 +4159,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, BUG_ON(strlen(name) >= sizeof(dev->name)); - alloc_size = sizeof(struct net_device) + - sizeof(struct net_device_subqueue) * (queue_count - 1); + alloc_size = sizeof(struct net_device); if (sizeof_priv) { /* ensure 32-byte alignment of private area */ alloc_size = (alloc_size + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST; @@ -4191,16 +4189,14 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev->_tx = tx; dev->num_tx_queues = queue_count; + dev->real_num_tx_queues = queue_count; if (sizeof_priv) { dev->priv = ((char *)dev + - ((sizeof(struct net_device) + - (sizeof(struct net_device_subqueue) * - (queue_count - 1)) + NETDEV_ALIGN_CONST) + ((sizeof(struct net_device) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST)); } - dev->egress_subqueue_count = queue_count; dev->gso_max_size = GSO_MAX_SIZE; netdev_init_queues(dev); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 8fb134da0346..c12720895ecf 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -58,25 +58,27 @@ static void queue_process(struct work_struct *work) while ((skb = skb_dequeue(&npinfo->txq))) { struct net_device *dev = skb->dev; + struct netdev_queue *txq; if (!netif_device_present(dev) || !netif_running(dev)) { __kfree_skb(skb); continue; } + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + local_irq_save(flags); - netif_tx_lock(dev); - if ((netif_queue_stopped(dev) || - netif_subqueue_stopped(dev, skb)) || - dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) { + __netif_tx_lock(txq, smp_processor_id()); + if (netif_tx_queue_stopped(txq) || + dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) { skb_queue_head(&npinfo->txq, skb); - netif_tx_unlock(dev); + __netif_tx_unlock(txq); local_irq_restore(flags); schedule_delayed_work(&npinfo->tx_work, HZ/10); return; } - netif_tx_unlock(dev); + __netif_tx_unlock(txq); local_irq_restore(flags); } } @@ -278,17 +280,19 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) /* don't get messages out of order, and no recursion */ if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) { + struct netdev_queue *txq; unsigned long flags; + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + local_irq_save(flags); /* try until next clock tick */ for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; tries > 0; --tries) { - if (netif_tx_trylock(dev)) { - if (!netif_queue_stopped(dev) && - !netif_subqueue_stopped(dev, skb)) + if (__netif_tx_trylock(txq)) { + if (!netif_tx_queue_stopped(txq)) status = dev->hard_start_xmit(skb, dev); - netif_tx_unlock(dev); + __netif_tx_unlock(txq); if (status == NETDEV_TX_OK) break; diff --git a/net/core/pktgen.c b/net/core/pktgen.c index fdf537707e51..906802db4ed4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2123,6 +2123,24 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow) } } #endif +static void set_cur_queue_map(struct pktgen_dev *pkt_dev) +{ + if (pkt_dev->queue_map_min < pkt_dev->queue_map_max) { + __u16 t; + if (pkt_dev->flags & F_QUEUE_MAP_RND) { + t = random32() % + (pkt_dev->queue_map_max - + pkt_dev->queue_map_min + 1) + + pkt_dev->queue_map_min; + } else { + t = pkt_dev->cur_queue_map + 1; + if (t > pkt_dev->queue_map_max) + t = pkt_dev->queue_map_min; + } + pkt_dev->cur_queue_map = t; + } +} + /* Increment/randomize headers according to flags and current values * for IP src/dest, UDP src/dst port, MAC-Addr src/dst */ @@ -2325,19 +2343,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) pkt_dev->cur_pkt_size = t; } - if (pkt_dev->queue_map_min < pkt_dev->queue_map_max) { - __u16 t; - if (pkt_dev->flags & F_QUEUE_MAP_RND) { - t = random32() % - (pkt_dev->queue_map_max - pkt_dev->queue_map_min + 1) - + pkt_dev->queue_map_min; - } else { - t = pkt_dev->cur_queue_map + 1; - if (t > pkt_dev->queue_map_max) - t = pkt_dev->queue_map_min; - } - pkt_dev->cur_queue_map = t; - } + set_cur_queue_map(pkt_dev); pkt_dev->flows[flow].count++; } @@ -2458,7 +2464,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */ __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */ __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ - + u16 queue_map; if (pkt_dev->nr_labels) protocol = htons(ETH_P_MPLS_UC); @@ -2469,6 +2475,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, /* Update any of the values, used when we're incrementing various * fields. */ + queue_map = pkt_dev->cur_queue_map; mod_cur_headers(pkt_dev); datalen = (odev->hard_header_len + 16) & ~0xf; @@ -2507,7 +2514,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, skb->network_header = skb->tail; skb->transport_header = skb->network_header + sizeof(struct iphdr); skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr)); - skb_set_queue_mapping(skb, pkt_dev->cur_queue_map); + skb_set_queue_mapping(skb, queue_map); iph = ip_hdr(skb); udph = udp_hdr(skb); @@ -2797,6 +2804,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */ __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */ __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ + u16 queue_map; if (pkt_dev->nr_labels) protocol = htons(ETH_P_MPLS_UC); @@ -2807,6 +2815,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, /* Update any of the values, used when we're incrementing various * fields. */ + queue_map = pkt_dev->cur_queue_map; mod_cur_headers(pkt_dev); skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 + @@ -2844,7 +2853,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, skb->network_header = skb->tail; skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr)); - skb_set_queue_mapping(skb, pkt_dev->cur_queue_map); + skb_set_queue_mapping(skb, queue_map); iph = ipv6_hdr(skb); udph = udp_hdr(skb); @@ -3263,7 +3272,9 @@ static void pktgen_rem_thread(struct pktgen_thread *t) static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) { struct net_device *odev = NULL; + struct netdev_queue *txq; __u64 idle_start = 0; + u16 queue_map; int ret; odev = pkt_dev->odev; @@ -3285,9 +3296,15 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) } } - if ((netif_queue_stopped(odev) || - (pkt_dev->skb && - netif_subqueue_stopped(odev, pkt_dev->skb))) || + if (!pkt_dev->skb) { + set_cur_queue_map(pkt_dev); + queue_map = pkt_dev->cur_queue_map; + } else { + queue_map = skb_get_queue_mapping(pkt_dev->skb); + } + + txq = netdev_get_tx_queue(odev, queue_map); + if (netif_tx_queue_stopped(txq) || need_resched()) { idle_start = getCurUs(); @@ -3303,8 +3320,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->idle_acc += getCurUs() - idle_start; - if (netif_queue_stopped(odev) || - netif_subqueue_stopped(odev, pkt_dev->skb)) { + if (netif_tx_queue_stopped(txq)) { pkt_dev->next_tx_us = getCurUs(); /* TODO */ pkt_dev->next_tx_ns = 0; goto out; /* Try the next interface */ @@ -3331,9 +3347,12 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) } } - netif_tx_lock_bh(odev); - if (!netif_queue_stopped(odev) && - !netif_subqueue_stopped(odev, pkt_dev->skb)) { + /* fill_packet() might have changed the queue */ + queue_map = skb_get_queue_mapping(pkt_dev->skb); + txq = netdev_get_tx_queue(odev, queue_map); + + __netif_tx_lock_bh(txq); + if (!netif_tx_queue_stopped(txq)) { atomic_inc(&(pkt_dev->skb->users)); retry_now: @@ -3377,7 +3396,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->next_tx_ns = 0; } - netif_tx_unlock_bh(odev); + __netif_tx_unlock_bh(txq); /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 4e2b865cbba0..2f575b9017d1 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -166,7 +166,7 @@ static inline int qdisc_restart(struct netdev_queue *txq) HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) - ret = dev_hard_start_xmit(skb, dev); + ret = dev_hard_start_xmit(skb, dev, txq); HARD_TX_UNLOCK(dev, txq); spin_lock(&txq->lock); @@ -198,11 +198,10 @@ static inline int qdisc_restart(struct netdev_queue *txq) void __qdisc_run(struct netdev_queue *txq) { - struct net_device *dev = txq->dev; unsigned long start_time = jiffies; while (qdisc_restart(txq)) { - if (netif_queue_stopped(dev)) + if (netif_tx_queue_stopped(txq)) break; /* diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 44a2c3451f4d..ade3372221c7 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -295,8 +295,7 @@ restart: slave_txq = netdev_get_tx_queue(slave, 0); if (slave_txq->qdisc_sleeping != q) continue; - if (netif_queue_stopped(slave) || - __netif_subqueue_stopped(slave, subq) || + if (__netif_subqueue_stopped(slave, subq) || !netif_running(slave)) { busy = 1; continue; @@ -305,8 +304,7 @@ restart: switch (teql_resolve(skb, skb_res, slave)) { case 0: if (netif_tx_trylock(slave)) { - if (!netif_queue_stopped(slave) && - !__netif_subqueue_stopped(slave, subq) && + if (!__netif_subqueue_stopped(slave, subq) && slave->hard_start_xmit(skb, slave) == 0) { netif_tx_unlock(slave); master->slaves = NEXT_SLAVE(q); -- cgit v1.2.3 From 6b0fb1261a4655613bed5dac0e935e733969e999 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 02:58:10 -0700 Subject: netdev: Kill struct net_device_subqueue and netdev->egress_subqueue* No longer used. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b5c1e7df64fc..a1c2c2204498 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -118,14 +118,6 @@ struct wireless_dev; #endif /* __KERNEL__ */ -struct net_device_subqueue -{ - /* Give a control state for each queue. This struct may contain - * per-queue locks in the future. - */ - unsigned long state; -}; - /* * Network device statistics. Akin to the 2.0 ether stats but * with byte counters. @@ -761,10 +753,6 @@ struct net_device /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; - - /* The TX queue control structures */ - unsigned int egress_subqueue_count; - struct net_device_subqueue egress_subqueue[1]; }; #define to_net_dev(d) container_of(d, struct net_device, dev) -- cgit v1.2.3 From e3c50d5d25ac09efd9acbe2b2a3e365466de84ed Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 02:58:39 -0700 Subject: netdev: netdev_priv() can now be sane again. The private area of a netdev is now at a fixed offset once more. Unfortunately, some assumptions that netdev_priv() == netdev->priv crept back into the tree. In particular this happened in the loopback driver. Make it use netdev->ml_priv. Signed-off-by: David S. Miller --- drivers/net/loopback.c | 8 ++++---- include/linux/netdevice.h | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 41b774baac4d..49f6bc036a92 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -153,7 +153,7 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) dev->last_rx = jiffies; /* it's OK to use per_cpu_ptr() because BHs are off */ - pcpu_lstats = netdev_priv(dev); + pcpu_lstats = dev->ml_priv; lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id()); lb_stats->bytes += skb->len; lb_stats->packets++; @@ -171,7 +171,7 @@ static struct net_device_stats *get_stats(struct net_device *dev) unsigned long packets = 0; int i; - pcpu_lstats = netdev_priv(dev); + pcpu_lstats = dev->ml_priv; for_each_possible_cpu(i) { const struct pcpu_lstats *lb_stats; @@ -207,13 +207,13 @@ static int loopback_dev_init(struct net_device *dev) if (!lstats) return -ENOMEM; - dev->priv = lstats; + dev->ml_priv = lstats; return 0; } static void loopback_dev_free(struct net_device *dev) { - struct pcpu_lstats *lstats = netdev_priv(dev); + struct pcpu_lstats *lstats = dev->ml_priv; free_percpu(lstats); free_netdev(dev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a1c2c2204498..fdac1159253e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -808,7 +808,9 @@ void dev_net_set(struct net_device *dev, struct net *net) */ static inline void *netdev_priv(const struct net_device *dev) { - return dev->priv; + return (char *)dev + ((sizeof(struct net_device) + + NETDEV_ALIGN_CONST) + & ~NETDEV_ALIGN_CONST); } /* Set the sysfs physical device reference for the network logical device -- cgit v1.2.3 From eae792b722fef08dcf3aee88266ee7def9710757 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 03:03:33 -0700 Subject: netdev: Add netdev->select_queue() method. Devices or device layers can set this to control the queue selection performed by dev_pick_tx(). This function runs under RCU protection, which allows overriding functions to have some way of synchronizing with things like dynamic ->real_num_tx_queues adjustments. This makes the spinlock prefetch in dev_queue_xmit() a little bit less effective, but that's the price right now for correctness. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fdac1159253e..9464e6452967 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -724,6 +724,9 @@ struct net_device void (*poll_controller)(struct net_device *dev); #endif + u16 (*select_queue)(struct net_device *dev, + struct sk_buff *skb); + #ifdef CONFIG_NET_NS /* Network namespace this network device is inside */ struct net *nd_net; diff --git a/net/core/dev.c b/net/core/dev.c index f027a1ac4fbb..7ca9564d2f44 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1670,6 +1670,9 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev, { u16 queue_index = 0; + if (dev->select_queue) + queue_index = dev->select_queue(dev, skb); + skb_set_queue_mapping(skb, queue_index); return netdev_get_tx_queue(dev, queue_index); } @@ -1710,14 +1713,14 @@ int dev_queue_xmit(struct sk_buff *skb) } gso: - txq = dev_pick_tx(dev, skb); - spin_lock_prefetch(&txq->lock); - /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ rcu_read_lock_bh(); + txq = dev_pick_tx(dev, skb); + spin_lock_prefetch(&txq->lock); + /* Updates of qdisc are serialized by queue->lock. * The struct Qdisc which is pointed to by qdisc is now a * rcu structure - it may be accessed without acquiring -- cgit v1.2.3 From 92831bc395ac8390bf759775c50cb6f90c6eb03d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 03:48:01 -0700 Subject: netdev: Kill plain netif_schedule() No more users. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9464e6452967..787fbfc5aebb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -994,11 +994,6 @@ static inline void netif_schedule_queue(struct netdev_queue *txq) __netif_schedule(txq); } -static inline void netif_schedule(struct net_device *dev) -{ - netif_schedule_queue(netdev_get_tx_queue(dev, 0)); -} - static inline void netif_tx_schedule_all(struct net_device *dev) { unsigned int i; -- cgit v1.2.3 From d3b753db7c4f1f37a98b51974d484fda5d86dab5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 20:14:35 -0700 Subject: pkt_sched: Move gso_skb into Qdisc. We liberate any dangling gso_skb during qdisc destruction. It really only matters for the root qdisc. But when qdiscs can be shared by multiple netdev_queue objects, we can't have the gso_skb in the netdev_queue any more. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - include/net/sch_generic.h | 1 + net/sched/sch_generic.c | 19 +++++++++---------- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 787fbfc5aebb..0883fcf2d16a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -449,7 +449,6 @@ struct netdev_queue { struct net_device *dev; struct Qdisc *qdisc; unsigned long state; - struct sk_buff *gso_skb; spinlock_t _xmit_lock; int xmit_lock_owner; struct Qdisc *qdisc_sleeping; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index b47f556c66f8..b96c3d9e10a8 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -36,6 +36,7 @@ struct Qdisc u32 handle; u32 parent; atomic_t refcnt; + struct sk_buff *gso_skb; struct sk_buff_head q; struct netdev_queue *dev_queue; struct list_head list; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2f575b9017d1..2bd75befa066 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -77,7 +77,7 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { if (unlikely(skb->next)) - dev_queue->gso_skb = skb; + q->gso_skb = skb; else q->ops->requeue(skb, q); @@ -85,13 +85,12 @@ static inline int dev_requeue_skb(struct sk_buff *skb, return 0; } -static inline struct sk_buff *dequeue_skb(struct netdev_queue *dev_queue, - struct Qdisc *q) +static inline struct sk_buff *dequeue_skb(struct Qdisc *q) { struct sk_buff *skb; - if ((skb = dev_queue->gso_skb)) - dev_queue->gso_skb = NULL; + if ((skb = q->gso_skb)) + q->gso_skb = NULL; else skb = q->dequeue(q); @@ -155,10 +154,9 @@ static inline int qdisc_restart(struct netdev_queue *txq) struct sk_buff *skb; /* Dequeue packet */ - if (unlikely((skb = dequeue_skb(txq, q)) == NULL)) + if (unlikely((skb = dequeue_skb(q)) == NULL)) return 0; - /* And release queue */ spin_unlock(&txq->lock); @@ -643,8 +641,8 @@ static void dev_deactivate_queue(struct net_device *dev, void *_qdisc_default) { struct Qdisc *qdisc_default = _qdisc_default; + struct sk_buff *skb = NULL; struct Qdisc *qdisc; - struct sk_buff *skb; spin_lock_bh(&dev_queue->lock); @@ -652,9 +650,10 @@ static void dev_deactivate_queue(struct net_device *dev, if (qdisc) { dev_queue->qdisc = qdisc_default; qdisc_reset(qdisc); + + skb = qdisc->gso_skb; + qdisc->gso_skb = NULL; } - skb = dev_queue->gso_skb; - dev_queue->gso_skb = NULL; spin_unlock_bh(&dev_queue->lock); -- cgit v1.2.3 From e2627c8c2241bce45e368e150654d076b58a4595 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Jul 2008 00:56:32 -0700 Subject: pkt_sched: Make QDISC_RUNNING a qdisc state. Currently it is associated with a netdev_queue, but when we have qdisc sharing that no longer makes any sense. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - include/net/pkt_sched.h | 4 +++- include/net/sch_generic.h | 6 ++++++ net/sched/sch_generic.c | 18 ++++++++++-------- 4 files changed, 19 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0883fcf2d16a..9240a95793be 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -441,7 +441,6 @@ static inline void napi_synchronize(const struct napi_struct *n) enum netdev_queue_state_t { __QUEUE_STATE_XOFF, - __QUEUE_STATE_QDISC_RUNNING, }; struct netdev_queue { diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index cb9527815606..06a442d85186 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -88,8 +88,10 @@ extern void __qdisc_run(struct netdev_queue *txq); static inline void qdisc_run(struct netdev_queue *txq) { + struct Qdisc *q = txq->qdisc; + if (!netif_tx_queue_stopped(txq) && - !test_and_set_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state)) + !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) __qdisc_run(txq); } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index b96c3d9e10a8..bc2a09da21b1 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -23,6 +23,11 @@ struct qdisc_rate_table int refcnt; }; +enum qdisc_state_t +{ + __QDISC_STATE_RUNNING, +}; + struct Qdisc { int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev); @@ -36,6 +41,7 @@ struct Qdisc u32 handle; u32 parent; atomic_t refcnt; + unsigned long state; struct sk_buff *gso_skb; struct sk_buff_head q; struct netdev_queue *dev_queue; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2bd75befa066..ac208c2b2d10 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -130,8 +130,8 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, /* * NOTE: Called under queue->lock with locally disabled BH. * - * __QUEUE_STATE_QDISC_RUNNING guarantees only one CPU can process - * this queue at a time. queue->lock serializes queue accesses for + * __QDISC_STATE_RUNNING guarantees only one CPU can process + * this qdisc at a time. queue->lock serializes queue accesses for * this queue AND txq->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. @@ -146,9 +146,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * >0 - queue is not empty. * */ -static inline int qdisc_restart(struct netdev_queue *txq) +static inline int qdisc_restart(struct netdev_queue *txq, + struct Qdisc *q) { - struct Qdisc *q = txq->qdisc; int ret = NETDEV_TX_BUSY; struct net_device *dev; struct sk_buff *skb; @@ -168,7 +168,6 @@ static inline int qdisc_restart(struct netdev_queue *txq) HARD_TX_UNLOCK(dev, txq); spin_lock(&txq->lock); - q = txq->qdisc; switch (ret) { case NETDEV_TX_OK: @@ -197,8 +196,9 @@ static inline int qdisc_restart(struct netdev_queue *txq) void __qdisc_run(struct netdev_queue *txq) { unsigned long start_time = jiffies; + struct Qdisc *q = txq->qdisc; - while (qdisc_restart(txq)) { + while (qdisc_restart(txq, q)) { if (netif_tx_queue_stopped(txq)) break; @@ -213,7 +213,7 @@ void __qdisc_run(struct netdev_queue *txq) } } - clear_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state); + clear_bit(__QDISC_STATE_RUNNING, &q->state); } static void dev_watchdog(unsigned long arg) @@ -666,14 +666,16 @@ static bool some_qdisc_is_running(struct net_device *dev, int lock) for (i = 0; i < dev->num_tx_queues; i++) { struct netdev_queue *dev_queue; + struct Qdisc *q; int val; dev_queue = netdev_get_tx_queue(dev, i); + q = dev_queue->qdisc; if (lock) spin_lock_bh(&dev_queue->lock); - val = test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state); + val = test_bit(__QDISC_STATE_RUNNING, &q->state); if (lock) spin_unlock_bh(&dev_queue->lock); -- cgit v1.2.3 From 37437bb2e1ae8af470dfcd5b4ff454110894ccaf Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Jul 2008 02:15:04 -0700 Subject: pkt_sched: Schedule qdiscs instead of netdev_queue. When we have shared qdiscs, packets come out of the qdiscs for multiple transmit queues. Therefore it doesn't make any sense to schedule the transmit queue when logically we cannot know ahead of time the TX queue of the SKB that the qdisc->dequeue() will give us. Just for sanity I added a BUG check to make sure we never get into a state where the noop_qdisc is scheduled. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 12 ++++----- include/net/pkt_sched.h | 11 +++----- include/net/sch_generic.h | 2 ++ net/core/dev.c | 68 +++++++++++++++++++---------------------------- net/sched/sch_api.c | 3 +-- net/sched/sch_cbq.c | 2 +- net/sched/sch_generic.c | 30 ++++++++++----------- 7 files changed, 55 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9240a95793be..1e839fa01434 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -275,7 +275,6 @@ enum netdev_state_t { __LINK_STATE_START, __LINK_STATE_PRESENT, - __LINK_STATE_SCHED, __LINK_STATE_NOCARRIER, __LINK_STATE_LINKWATCH_PENDING, __LINK_STATE_DORMANT, @@ -452,7 +451,6 @@ struct netdev_queue { int xmit_lock_owner; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; - struct netdev_queue *next_sched; } ____cacheline_aligned_in_smp; /* @@ -969,7 +967,7 @@ static inline int unregister_gifconf(unsigned int family) */ struct softnet_data { - struct netdev_queue *output_queue; + struct Qdisc *output_queue; struct sk_buff_head input_pkt_queue; struct list_head poll_list; struct sk_buff *completion_queue; @@ -984,12 +982,12 @@ DECLARE_PER_CPU(struct softnet_data,softnet_data); #define HAVE_NETIF_QUEUE -extern void __netif_schedule(struct netdev_queue *txq); +extern void __netif_schedule(struct Qdisc *q); static inline void netif_schedule_queue(struct netdev_queue *txq) { if (!test_bit(__QUEUE_STATE_XOFF, &txq->state)) - __netif_schedule(txq); + __netif_schedule(txq->qdisc); } static inline void netif_tx_schedule_all(struct net_device *dev) @@ -1042,7 +1040,7 @@ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) } #endif if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state)) - __netif_schedule(dev_queue); + __netif_schedule(dev_queue->qdisc); } static inline void netif_wake_queue(struct net_device *dev) @@ -1186,7 +1184,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) return; #endif if (test_and_clear_bit(__QUEUE_STATE_XOFF, &txq->state)) - __netif_schedule(txq); + __netif_schedule(txq->qdisc); } /** diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 06a442d85186..e4e30052e4e2 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -84,15 +84,12 @@ extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab); extern void qdisc_put_rtab(struct qdisc_rate_table *tab); -extern void __qdisc_run(struct netdev_queue *txq); +extern void __qdisc_run(struct Qdisc *q); -static inline void qdisc_run(struct netdev_queue *txq) +static inline void qdisc_run(struct Qdisc *q) { - struct Qdisc *q = txq->qdisc; - - if (!netif_tx_queue_stopped(txq) && - !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) - __qdisc_run(txq); + if (!test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) + __qdisc_run(q); } extern int tc_classify_compat(struct sk_buff *skb, struct tcf_proto *tp, diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 92417825d387..3cc4b5cd8c6a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -26,6 +26,7 @@ struct qdisc_rate_table enum qdisc_state_t { __QDISC_STATE_RUNNING, + __QDISC_STATE_SCHED, }; struct Qdisc @@ -45,6 +46,7 @@ struct Qdisc struct sk_buff *gso_skb; struct sk_buff_head q; struct netdev_queue *dev_queue; + struct Qdisc *next_sched; struct list_head list; struct gnet_stats_basic bstats; diff --git a/net/core/dev.c b/net/core/dev.c index 467bfb325123..0b909b74f698 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1323,18 +1323,18 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) } -void __netif_schedule(struct netdev_queue *txq) +void __netif_schedule(struct Qdisc *q) { - struct net_device *dev = txq->dev; + BUG_ON(q == &noop_qdisc); - if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { + if (!test_and_set_bit(__QDISC_STATE_SCHED, &q->state)) { struct softnet_data *sd; unsigned long flags; local_irq_save(flags); sd = &__get_cpu_var(softnet_data); - txq->next_sched = sd->output_queue; - sd->output_queue = txq; + q->next_sched = sd->output_queue; + sd->output_queue = q; raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_restore(flags); } @@ -1771,37 +1771,23 @@ gso: rcu_read_lock_bh(); txq = dev_pick_tx(dev, skb); - spin_lock_prefetch(&txq->lock); - - /* Updates of qdisc are serialized by queue->lock. - * The struct Qdisc which is pointed to by qdisc is now a - * rcu structure - it may be accessed without acquiring - * a lock (but the structure may be stale.) The freeing of the - * qdisc will be deferred until it's known that there are no - * more references to it. - * - * If the qdisc has an enqueue function, we still need to - * hold the queue->lock before calling it, since queue->lock - * also serializes access to the device queue. - */ - q = rcu_dereference(txq->qdisc); + #ifdef CONFIG_NET_CLS_ACT skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS); #endif if (q->enqueue) { - /* Grab device queue */ - spin_lock(&txq->lock); - q = txq->qdisc; - if (q->enqueue) { - rc = q->enqueue(skb, q); - qdisc_run(txq); - spin_unlock(&txq->lock); - - rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; - goto out; - } - spin_unlock(&txq->lock); + spinlock_t *root_lock = qdisc_root_lock(q); + + spin_lock(root_lock); + + rc = q->enqueue(skb, q); + qdisc_run(q); + + spin_unlock(root_lock); + + rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; + goto out; } /* The device has no queue. Common case for software devices: @@ -1974,7 +1960,7 @@ static void net_tx_action(struct softirq_action *h) } if (sd->output_queue) { - struct netdev_queue *head; + struct Qdisc *head; local_irq_disable(); head = sd->output_queue; @@ -1982,18 +1968,20 @@ static void net_tx_action(struct softirq_action *h) local_irq_enable(); while (head) { - struct netdev_queue *txq = head; - struct net_device *dev = txq->dev; + struct Qdisc *q = head; + spinlock_t *root_lock; + head = head->next_sched; smp_mb__before_clear_bit(); - clear_bit(__LINK_STATE_SCHED, &dev->state); + clear_bit(__QDISC_STATE_SCHED, &q->state); - if (spin_trylock(&txq->lock)) { - qdisc_run(txq); - spin_unlock(&txq->lock); + root_lock = qdisc_root_lock(q); + if (spin_trylock(root_lock)) { + qdisc_run(q); + spin_unlock(root_lock); } else { - netif_schedule_queue(txq); + __netif_schedule(q); } } } @@ -4459,7 +4447,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, void *ocpu) { struct sk_buff **list_skb; - struct netdev_queue **list_net; + struct Qdisc **list_net; struct sk_buff *skb; unsigned int cpu, oldcpu = (unsigned long)ocpu; struct softnet_data *sd, *oldsd; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 19c244a00839..8e8c5becc348 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -294,11 +294,10 @@ static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, timer); - struct netdev_queue *txq = wd->qdisc->dev_queue; wd->qdisc->flags &= ~TCQ_F_THROTTLED; smp_wmb(); - netif_schedule_queue(txq); + __netif_schedule(wd->qdisc); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 37ae653db683..a3953bbe2d79 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -650,7 +650,7 @@ static enum hrtimer_restart cbq_undelay(struct hrtimer *timer) } sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule_queue(sch->dev_queue); + __netif_schedule(sch); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 739a8711ab30..dd5c4e70abe4 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -72,16 +72,14 @@ static inline int qdisc_qlen(struct Qdisc *q) return q->q.qlen; } -static inline int dev_requeue_skb(struct sk_buff *skb, - struct netdev_queue *dev_queue, - struct Qdisc *q) +static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { if (unlikely(skb->next)) q->gso_skb = skb; else q->ops->requeue(skb, q); - netif_schedule_queue(dev_queue); + __netif_schedule(q); return 0; } @@ -121,7 +119,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * some time. */ __get_cpu_var(netdev_rx_stat).cpu_collision++; - ret = dev_requeue_skb(skb, dev_queue, q); + ret = dev_requeue_skb(skb, q); } return ret; @@ -146,9 +144,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * >0 - queue is not empty. * */ -static inline int qdisc_restart(struct netdev_queue *txq, - struct Qdisc *q) +static inline int qdisc_restart(struct Qdisc *q) { + struct netdev_queue *txq; int ret = NETDEV_TX_BUSY; struct net_device *dev; spinlock_t *root_lock; @@ -163,7 +161,8 @@ static inline int qdisc_restart(struct netdev_queue *txq, /* And release qdisc */ spin_unlock(root_lock); - dev = txq->dev; + dev = qdisc_dev(q); + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) @@ -189,29 +188,28 @@ static inline int qdisc_restart(struct netdev_queue *txq, printk(KERN_WARNING "BUG %s code %d qlen %d\n", dev->name, ret, q->q.qlen); - ret = dev_requeue_skb(skb, txq, q); + ret = dev_requeue_skb(skb, q); break; } + if (ret && netif_tx_queue_stopped(txq)) + ret = 0; + return ret; } -void __qdisc_run(struct netdev_queue *txq) +void __qdisc_run(struct Qdisc *q) { unsigned long start_time = jiffies; - struct Qdisc *q = txq->qdisc; - - while (qdisc_restart(txq, q)) { - if (netif_tx_queue_stopped(txq)) - break; + while (qdisc_restart(q)) { /* * Postpone processing if * 1. another process needs the CPU; * 2. we've been doing it for too long. */ if (need_resched() || jiffies != start_time) { - netif_schedule_queue(txq); + __netif_schedule(q); break; } } -- cgit v1.2.3 From ead81cc5fc6d996db6afb20f211241612610a07a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 00:50:32 -0700 Subject: netdevice: Move qdisc_list back into net_device proper. And give it it's own lock. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 ++- net/core/dev.c | 2 ++ net/sched/sch_api.c | 31 +++++++------------------------ net/sched/sch_generic.c | 9 +++++++-- 4 files changed, 18 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1e839fa01434..3170bcef734b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -450,7 +450,6 @@ struct netdev_queue { spinlock_t _xmit_lock; int xmit_lock_owner; struct Qdisc *qdisc_sleeping; - struct list_head qdisc_list; } ____cacheline_aligned_in_smp; /* @@ -638,6 +637,8 @@ struct net_device unsigned int real_num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ + spinlock_t qdisc_list_lock; + struct list_head qdisc_list; /* * One part is mostly used on xmit path (device) diff --git a/net/core/dev.c b/net/core/dev.c index 0b909b74f698..6741e344ac59 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3886,6 +3886,8 @@ int register_netdevice(struct net_device *dev) net = dev_net(dev); spin_lock_init(&dev->addr_list_lock); + spin_lock_init(&dev->qdisc_list_lock); + INIT_LIST_HEAD(&dev->qdisc_list); netdev_init_queue_locks(dev); dev->iflink = -1; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 8e8c5becc348..6958fe7c9a77 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -183,30 +183,17 @@ EXPORT_SYMBOL(unregister_qdisc); (root qdisc, all its children, children of children etc.) */ -static struct Qdisc *__qdisc_lookup(struct netdev_queue *dev_queue, u32 handle) +struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { struct Qdisc *q; - list_for_each_entry(q, &dev_queue->qdisc_list, list) { + list_for_each_entry(q, &dev->qdisc_list, list) { if (q->handle == handle) return q; } return NULL; } -struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) -{ - unsigned int i; - - for (i = 0; i < dev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - struct Qdisc *q = __qdisc_lookup(txq, handle); - if (q) - return q; - } - return NULL; -} - static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) { unsigned long cl; @@ -645,9 +632,9 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, goto err_out3; } } - qdisc_lock_tree(dev); - list_add_tail(&sch->list, &dev_queue->qdisc_list); - qdisc_unlock_tree(dev); + spin_lock_bh(&dev->qdisc_list_lock); + list_add_tail(&sch->list, &dev->qdisc_list); + spin_unlock_bh(&dev->qdisc_list_lock); return sch; } @@ -1032,14 +1019,12 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { - struct netdev_queue *dev_queue; if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; - dev_queue = netdev_get_tx_queue(dev, 0); - list_for_each_entry(q, &dev_queue->qdisc_list, list) { + list_for_each_entry(q, &dev->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; continue; @@ -1269,7 +1254,6 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); - struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -1288,8 +1272,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - dev_queue = netdev_get_tx_queue(dev, 0); - list_for_each_entry(q, &dev_queue->qdisc_list, list) { + list_for_each_entry(q, &dev->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index efa418a1b34e..8cdf0b4a6a5a 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -563,11 +563,15 @@ static void __qdisc_destroy(struct rcu_head *head) void qdisc_destroy(struct Qdisc *qdisc) { + struct net_device *dev = qdisc_dev(qdisc); + if (qdisc->flags & TCQ_F_BUILTIN || !atomic_dec_and_test(&qdisc->refcnt)) return; + spin_lock_bh(&dev->qdisc_list_lock); list_del(&qdisc->list); + spin_unlock_bh(&dev->qdisc_list_lock); call_rcu(&qdisc->q_rcu, __qdisc_destroy); } @@ -599,7 +603,9 @@ static void attach_one_default_qdisc(struct net_device *dev, printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - list_add_tail(&qdisc->list, &dev_queue->qdisc_list); + spin_lock_bh(&dev->qdisc_list_lock); + list_add_tail(&qdisc->list, &dev->qdisc_list); + spin_unlock_bh(&dev->qdisc_list_lock); } else { qdisc = &noqueue_qdisc; } @@ -738,7 +744,6 @@ static void dev_init_scheduler_queue(struct net_device *dev, dev_queue->qdisc = qdisc; dev_queue->qdisc_sleeping = qdisc; - INIT_LIST_HEAD(&dev_queue->qdisc_list); } void dev_init_scheduler(struct net_device *dev) -- cgit v1.2.3 From 83874000929ed63aef30b44083a9f713135ff040 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 00:53:03 -0700 Subject: pkt_sched: Kill netdev_queue lock. We can simply use the qdisc->q.lock for all of the qdisc tree synchronization. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 20 -------------------- include/linux/netdevice.h | 1 - include/net/sch_generic.h | 7 ++++++- net/core/dev.c | 9 +++++---- net/mac80211/wme.c | 19 ++++++++++++------- net/sched/sch_generic.c | 32 +++++++++++++++----------------- net/sched/sch_teql.c | 7 +++++-- 7 files changed, 43 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 897b05e79ed0..0960e69b2da4 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -35,7 +35,6 @@ #include #include #include -#include #define TX_TIMEOUT (2*HZ) @@ -228,22 +227,6 @@ static struct rtnl_link_ops ifb_link_ops __read_mostly = { module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); -/* - * dev_ifb's TX queue lock is usually taken after dev->rx_queue.lock, - * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev's TX queue lock with dev_ifb->rx_queue.lock. - * But lockdep should know that ifb has different locks from dev. - */ -static struct lock_class_key ifb_tx_queue_lock_key; -static struct lock_class_key ifb_rx_queue_lock_key; - -static void set_tx_lockdep_key(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->lock, &ifb_tx_queue_lock_key); -} - static int __init ifb_init_one(int index) { struct net_device *dev_ifb; @@ -264,9 +247,6 @@ static int __init ifb_init_one(int index) if (err < 0) goto err; - netdev_for_each_tx_queue(dev_ifb, set_tx_lockdep_key, NULL); - lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); - return 0; err: diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3170bcef734b..9c5a68850114 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -443,7 +443,6 @@ enum netdev_queue_state_t }; struct netdev_queue { - spinlock_t lock; struct net_device *dev; struct Qdisc *qdisc; unsigned long state; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 1eef8d0c9990..2902a42564f0 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -163,6 +163,11 @@ struct tcf_proto struct tcf_proto_ops *ops; }; +static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc) +{ + return &qdisc->q.lock; +} + static inline struct Qdisc *qdisc_root(struct Qdisc *qdisc) { return qdisc->dev_queue->qdisc; @@ -172,7 +177,7 @@ static inline spinlock_t *qdisc_root_lock(struct Qdisc *qdisc) { struct Qdisc *root = qdisc_root(qdisc); - return &root->dev_queue->lock; + return qdisc_lock(root); } static inline struct net_device *qdisc_dev(struct Qdisc *qdisc) diff --git a/net/core/dev.c b/net/core/dev.c index 6741e344ac59..32a13772c1cb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2080,10 +2080,12 @@ static int ing_filter(struct sk_buff *skb) rxq = &dev->rx_queue; - spin_lock(&rxq->lock); - if ((q = rxq->qdisc) != NULL) + q = rxq->qdisc; + if (q) { + spin_lock(qdisc_lock(q)); result = q->enqueue(skb, q); - spin_unlock(&rxq->lock); + spin_unlock(qdisc_lock(q)); + } return result; } @@ -4173,7 +4175,6 @@ static void netdev_init_one_queue(struct net_device *dev, struct netdev_queue *queue, void *_unused) { - spin_lock_init(&queue->lock); queue->dev = dev; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index b21cfec4b6ce..6e8099e77043 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -237,12 +237,14 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, ieee80211_requeue(local, agg_queue); } else { struct netdev_queue *txq; + spinlock_t *root_lock; txq = netdev_get_tx_queue(local->mdev, agg_queue); + root_lock = qdisc_root_lock(txq->qdisc); - spin_lock_bh(&txq->lock); + spin_lock_bh(root_lock); qdisc_reset(txq->qdisc); - spin_unlock_bh(&txq->lock); + spin_unlock_bh(root_lock); } } @@ -250,6 +252,7 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) { struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue); struct sk_buff_head list; + spinlock_t *root_lock; struct Qdisc *qdisc; u32 len; @@ -261,14 +264,15 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) skb_queue_head_init(&list); - spin_lock(&txq->lock); + root_lock = qdisc_root_lock(qdisc); + spin_lock(root_lock); for (len = qdisc->q.qlen; len > 0; len--) { struct sk_buff *skb = qdisc->dequeue(qdisc); if (skb) __skb_queue_tail(&list, skb); } - spin_unlock(&txq->lock); + spin_unlock(root_lock); for (len = list.qlen; len > 0; len--) { struct sk_buff *skb = __skb_dequeue(&list); @@ -280,12 +284,13 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) txq = netdev_get_tx_queue(local->mdev, new_queue); - spin_lock(&txq->lock); qdisc = rcu_dereference(txq->qdisc); - qdisc->enqueue(skb, qdisc); + root_lock = qdisc_root_lock(qdisc); - spin_unlock(&txq->lock); + spin_lock(root_lock); + qdisc->enqueue(skb, qdisc); + spin_unlock(root_lock); } out_unlock: diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 3d53e92ad9c8..8fc580b3e173 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -96,15 +96,15 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, } /* - * NOTE: Called under queue->lock with locally disabled BH. + * NOTE: Called under qdisc_lock(q) with locally disabled BH. * * __QDISC_STATE_RUNNING guarantees only one CPU can process - * this qdisc at a time. queue->lock serializes queue accesses for - * this queue AND txq->qdisc pointer itself. + * this qdisc at a time. qdisc_lock(q) serializes queue accesses for + * this queue. * * netif_tx_lock serializes accesses to device driver. * - * queue->lock and netif_tx_lock are mutually exclusive, + * qdisc_lock(q) and netif_tx_lock are mutually exclusive, * if one is grabbed, another must be free. * * Note, that this procedure can be called by a watchdog timer @@ -317,7 +317,6 @@ struct Qdisc_ops noop_qdisc_ops __read_mostly = { }; static struct netdev_queue noop_netdev_queue = { - .lock = __SPIN_LOCK_UNLOCKED(noop_netdev_queue.lock), .qdisc = &noop_qdisc, }; @@ -327,6 +326,7 @@ struct Qdisc noop_qdisc = { .flags = TCQ_F_BUILTIN, .ops = &noop_qdisc_ops, .list = LIST_HEAD_INIT(noop_qdisc.list), + .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), .dev_queue = &noop_netdev_queue, }; EXPORT_SYMBOL(noop_qdisc); @@ -498,7 +498,7 @@ errout: } EXPORT_SYMBOL(qdisc_create_dflt); -/* Under queue->lock and BH! */ +/* Under qdisc_root_lock(qdisc) and BH! */ void qdisc_reset(struct Qdisc *qdisc) { @@ -526,10 +526,12 @@ static void __qdisc_destroy(struct rcu_head *head) module_put(ops->owner); dev_put(qdisc_dev(qdisc)); + kfree_skb(qdisc->gso_skb); + kfree((char *) qdisc - qdisc->padded); } -/* Under queue->lock and BH! */ +/* Under qdisc_root_lock(qdisc) and BH! */ void qdisc_destroy(struct Qdisc *qdisc) { @@ -586,13 +588,12 @@ static void transition_one_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_need_watchdog) { + struct Qdisc *new_qdisc = dev_queue->qdisc_sleeping; int *need_watchdog_p = _need_watchdog; - spin_lock_bh(&dev_queue->lock); - rcu_assign_pointer(dev_queue->qdisc, dev_queue->qdisc_sleeping); - if (dev_queue->qdisc != &noqueue_qdisc) + rcu_assign_pointer(dev_queue->qdisc, new_qdisc); + if (new_qdisc != &noqueue_qdisc) *need_watchdog_p = 1; - spin_unlock_bh(&dev_queue->lock); } void dev_activate(struct net_device *dev) @@ -629,19 +630,16 @@ static void dev_deactivate_queue(struct net_device *dev, struct sk_buff *skb = NULL; struct Qdisc *qdisc; - spin_lock_bh(&dev_queue->lock); - qdisc = dev_queue->qdisc; if (qdisc) { + spin_lock_bh(qdisc_lock(qdisc)); + dev_queue->qdisc = qdisc_default; qdisc_reset(qdisc); - skb = qdisc->gso_skb; - qdisc->gso_skb = NULL; + spin_unlock_bh(qdisc_lock(qdisc)); } - spin_unlock_bh(&dev_queue->lock); - kfree_skb(skb); } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index ade3372221c7..8b0ff345f9da 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -156,12 +156,15 @@ teql_destroy(struct Qdisc* sch) master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { struct netdev_queue *txq; + spinlock_t *root_lock; txq = netdev_get_tx_queue(master->dev, 0); master->slaves = NULL; - spin_lock_bh(&txq->lock); + + root_lock = qdisc_root_lock(txq->qdisc); + spin_lock_bh(root_lock); qdisc_reset(txq->qdisc); - spin_unlock_bh(&txq->lock); + spin_unlock_bh(root_lock); } } skb_queue_purge(&dat->q); -- cgit v1.2.3 From de05c557b24c7dffc6d392e3db120cf11c9f6ae7 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 18 Jul 2008 04:07:21 -0700 Subject: proc: consolidate per-net single_open callers There are already 7 of them - time to kill some duplicate code. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- fs/proc/proc_net.c | 24 +++++++++++++++++++ include/linux/seq_file_net.h | 2 ++ net/ipv4/fib_trie.c | 13 +--------- net/ipv4/proc.c | 57 +++----------------------------------------- net/ipv6/proc.c | 19 +-------------- net/ipv6/route.c | 26 ++------------------ 6 files changed, 33 insertions(+), 108 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 83f357b30d71..ab232ad2e6f9 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -51,6 +51,30 @@ int seq_open_net(struct inode *ino, struct file *f, } EXPORT_SYMBOL_GPL(seq_open_net); +int single_open_net(struct inode *inode, struct file *file, + int (*show)(struct seq_file *, void *)) +{ + int err; + struct net *net; + + err = -ENXIO; + net = get_proc_net(inode); + if (net == NULL) + goto err_net; + + err = single_open(file, show, net); + if (err < 0) + goto err_open; + + return 0; + +err_open: + put_net(net); +err_net: + return err; +} +EXPORT_SYMBOL_GPL(single_open_net); + int seq_release_net(struct inode *ino, struct file *f) { struct seq_file *seq; diff --git a/include/linux/seq_file_net.h b/include/linux/seq_file_net.h index 4ac52542a563..87dcc0ecf6eb 100644 --- a/include/linux/seq_file_net.h +++ b/include/linux/seq_file_net.h @@ -14,6 +14,8 @@ struct seq_net_private { int seq_open_net(struct inode *, struct file *, const struct seq_operations *, int); +int single_open_net(struct inode *, struct file *file, + int (*show)(struct seq_file *, void *)); int seq_release_net(struct inode *, struct file *); static inline struct net *seq_file_net(struct seq_file *seq) { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f155a66d6ebf..6009df238ed9 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2251,18 +2251,7 @@ static int fib_triestat_seq_show(struct seq_file *seq, void *v) static int fib_triestat_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - net = get_proc_net(inode); - if (net == NULL) - return -ENXIO; - err = single_open(file, fib_triestat_seq_show, net); - if (err < 0) { - put_net(net); - return err; - } - return 0; + return single_open_net(inode, file, fib_triestat_seq_show); } static int fib_triestat_seq_release(struct inode *ino, struct file *f) diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 554390431a4e..daf5d3c80cef 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -71,24 +71,7 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) static int sockstat_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, sockstat_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, sockstat_seq_show); } static int sockstat_seq_release(struct inode *inode, struct file *file) @@ -397,24 +380,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v) static int snmp_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, snmp_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, snmp_seq_show); } static int snmp_seq_release(struct inode *inode, struct file *file) @@ -469,24 +435,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v) static int netstat_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, netstat_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, netstat_seq_show); } static int netstat_seq_release(struct inode *inode, struct file *file) diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index cbc7e514d3ec..29c5a79444c2 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -183,24 +183,7 @@ static int snmp6_seq_show(struct seq_file *seq, void *v) static int sockstat6_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, sockstat6_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, sockstat6_seq_show); } static int sockstat6_seq_release(struct inode *inode, struct file *file) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5d6c166dfbb6..fb7ff8f0c6db 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2416,18 +2416,7 @@ static int ipv6_route_show(struct seq_file *m, void *v) static int ipv6_route_open(struct inode *inode, struct file *file) { - int err; - struct net *net = get_proc_net(inode); - if (!net) - return -ENXIO; - - err = single_open(file, ipv6_route_show, net); - if (err < 0) { - put_net(net); - return err; - } - - return 0; + return single_open_net(inode, file, ipv6_route_show); } static int ipv6_route_release(struct inode *inode, struct file *file) @@ -2463,18 +2452,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v) static int rt6_stats_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net = get_proc_net(inode); - if (!net) - return -ENXIO; - - err = single_open(file, rt6_stats_seq_show, net); - if (err < 0) { - put_net(net); - return err; - } - - return 0; + return single_open_net(inode, file, rt6_stats_seq_show); } static int rt6_stats_seq_release(struct inode *inode, struct file *file) -- cgit v1.2.3 From b6fcbdb4f283f7ba67cec3cda6be23da8e959031 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 18 Jul 2008 04:07:44 -0700 Subject: proc: consolidate per-net single-release callers They are symmetrical to single_open ones :) Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- fs/proc/proc_net.c | 8 ++++++++ include/linux/seq_file_net.h | 1 + net/ipv4/fib_trie.c | 9 +-------- net/ipv4/proc.c | 30 +++--------------------------- net/ipv6/proc.c | 10 +--------- net/ipv6/route.c | 20 ++------------------ 6 files changed, 16 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index ab232ad2e6f9..b224a28e0c15 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -87,6 +87,14 @@ int seq_release_net(struct inode *ino, struct file *f) } EXPORT_SYMBOL_GPL(seq_release_net); +int single_release_net(struct inode *ino, struct file *f) +{ + struct seq_file *seq = f->private_data; + put_net(seq->private); + return single_release(ino, f); +} +EXPORT_SYMBOL_GPL(single_release_net); + static struct net *get_proc_task_net(struct inode *dir) { struct task_struct *task; diff --git a/include/linux/seq_file_net.h b/include/linux/seq_file_net.h index 87dcc0ecf6eb..32c89bbe24a2 100644 --- a/include/linux/seq_file_net.h +++ b/include/linux/seq_file_net.h @@ -17,6 +17,7 @@ int seq_open_net(struct inode *, struct file *, int single_open_net(struct inode *, struct file *file, int (*show)(struct seq_file *, void *)); int seq_release_net(struct inode *, struct file *); +int single_release_net(struct inode *, struct file *); static inline struct net *seq_file_net(struct seq_file *seq) { #ifdef CONFIG_NET_NS diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 6009df238ed9..5cb72786a8af 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2254,19 +2254,12 @@ static int fib_triestat_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, fib_triestat_seq_show); } -static int fib_triestat_seq_release(struct inode *ino, struct file *f) -{ - struct seq_file *seq = f->private_data; - put_net(seq->private); - return single_release(ino, f); -} - static const struct file_operations fib_triestat_fops = { .owner = THIS_MODULE, .open = fib_triestat_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = fib_triestat_seq_release, + .release = single_release_net, }; static struct node *fib_trie_get_idx(struct seq_file *seq, loff_t pos) diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index daf5d3c80cef..834356ea99df 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -74,20 +74,12 @@ static int sockstat_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, sockstat_seq_show); } -static int sockstat_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations sockstat_seq_fops = { .owner = THIS_MODULE, .open = sockstat_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = sockstat_seq_release, + .release = single_release_net, }; /* snmp items */ @@ -383,20 +375,12 @@ static int snmp_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, snmp_seq_show); } -static int snmp_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations snmp_seq_fops = { .owner = THIS_MODULE, .open = snmp_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = snmp_seq_release, + .release = single_release_net, }; @@ -438,20 +422,12 @@ static int netstat_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, netstat_seq_show); } -static int netstat_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations netstat_seq_fops = { .owner = THIS_MODULE, .open = netstat_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = netstat_seq_release, + .release = single_release_net, }; static __net_init int ip_proc_init_net(struct net *net) diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 29c5a79444c2..70940b3654a1 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -186,20 +186,12 @@ static int sockstat6_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, sockstat6_seq_show); } -static int sockstat6_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations sockstat6_seq_fops = { .owner = THIS_MODULE, .open = sockstat6_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = sockstat6_seq_release, + .release = single_release_net, }; static int snmp6_seq_open(struct inode *inode, struct file *file) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fb7ff8f0c6db..cb8a51271b67 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2419,20 +2419,12 @@ static int ipv6_route_open(struct inode *inode, struct file *file) return single_open_net(inode, file, ipv6_route_show); } -static int ipv6_route_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct net *net = seq->private; - put_net(net); - return single_release(inode, file); -} - static const struct file_operations ipv6_route_proc_fops = { .owner = THIS_MODULE, .open = ipv6_route_open, .read = seq_read, .llseek = seq_lseek, - .release = ipv6_route_release, + .release = single_release_net, }; static int rt6_stats_seq_show(struct seq_file *seq, void *v) @@ -2455,20 +2447,12 @@ static int rt6_stats_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, rt6_stats_seq_show); } -static int rt6_stats_seq_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct net *net = (struct net *)seq->private; - put_net(net); - return single_release(inode, file); -} - static const struct file_operations rt6_stats_seq_fops = { .owner = THIS_MODULE, .open = rt6_stats_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = rt6_stats_seq_release, + .release = single_release_net, }; #endif /* CONFIG_PROC_FS */ -- cgit v1.2.3 From e761b7725234276a802322549cee5255305a0930 Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Tue, 15 Jul 2008 04:43:49 -0700 Subject: cpu hotplug, sched: Introduce cpu_active_map and redo sched domain managment (take 2) This is based on Linus' idea of creating cpu_active_map that prevents scheduler load balancer from migrating tasks to the cpu that is going down. It allows us to simplify domain management code and avoid unecessary domain rebuilds during cpu hotplug event handling. Please ignore the cpusets part for now. It needs some more work in order to avoid crazy lock nesting. Although I did simplfy and unify domain reinitialization logic. We now simply call partition_sched_domains() in all the cases. This means that we're using exact same code paths as in cpusets case and hence the test below cover cpusets too. Cpuset changes to make rebuild_sched_domains() callable from various contexts are in the separate patch (right next after this one). This not only boots but also easily handles while true; do make clean; make -j 8; done and while true; do on-off-cpu 1; done at the same time. (on-off-cpu 1 simple does echo 0/1 > /sys/.../cpu1/online thing). Suprisingly the box (dual-core Core2) is quite usable. In fact I'm typing this on right now in gnome-terminal and things are moving just fine. Also this is running with most of the debug features enabled (lockdep, mutex, etc) no BUG_ONs or lockdep complaints so far. I believe I addressed all of the Dmitry's comments for original Linus' version. I changed both fair and rt balancer to mask out non-active cpus. And replaced cpu_is_offline() with !cpu_active() in the main scheduler code where it made sense (to me). Signed-off-by: Max Krasnyanskiy Acked-by: Linus Torvalds Acked-by: Peter Zijlstra Acked-by: Gregory Haskins Cc: dmitry.adamushko@gmail.com Cc: pj@sgi.com Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 6 ++- include/linux/cpuset.h | 7 ++++ init/main.c | 7 ++++ kernel/cpu.c | 30 +++++++++++--- kernel/cpuset.c | 2 +- kernel/sched.c | 108 ++++++++++++++++++++---------------------------- kernel/sched_fair.c | 3 ++ kernel/sched_rt.c | 7 ++++ 8 files changed, 99 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index c24875bd9c5b..d614d2472798 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -359,13 +359,14 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, /* * The following particular system cpumasks and operations manage - * possible, present and online cpus. Each of them is a fixed size + * possible, present, active and online cpus. Each of them is a fixed size * bitmap of size NR_CPUS. * * #ifdef CONFIG_HOTPLUG_CPU * cpu_possible_map - has bit 'cpu' set iff cpu is populatable * cpu_present_map - has bit 'cpu' set iff cpu is populated * cpu_online_map - has bit 'cpu' set iff cpu available to scheduler + * cpu_active_map - has bit 'cpu' set iff cpu available to migration * #else * cpu_possible_map - has bit 'cpu' set iff cpu is populated * cpu_present_map - copy of cpu_possible_map @@ -416,6 +417,7 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, extern cpumask_t cpu_possible_map; extern cpumask_t cpu_online_map; extern cpumask_t cpu_present_map; +extern cpumask_t cpu_active_map; #if NR_CPUS > 1 #define num_online_cpus() cpus_weight(cpu_online_map) @@ -424,6 +426,7 @@ extern cpumask_t cpu_present_map; #define cpu_online(cpu) cpu_isset((cpu), cpu_online_map) #define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map) #define cpu_present(cpu) cpu_isset((cpu), cpu_present_map) +#define cpu_active(cpu) cpu_isset((cpu), cpu_active_map) #else #define num_online_cpus() 1 #define num_possible_cpus() 1 @@ -431,6 +434,7 @@ extern cpumask_t cpu_present_map; #define cpu_online(cpu) ((cpu) == 0) #define cpu_possible(cpu) ((cpu) == 0) #define cpu_present(cpu) ((cpu) == 0) +#define cpu_active(cpu) ((cpu) == 0) #endif #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 038578362b47..e8f450c499b0 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -78,6 +78,8 @@ extern void cpuset_track_online_nodes(void); extern int current_cpuset_is_being_rebound(void); +extern void rebuild_sched_domains(void); + #else /* !CONFIG_CPUSETS */ static inline int cpuset_init_early(void) { return 0; } @@ -156,6 +158,11 @@ static inline int current_cpuset_is_being_rebound(void) return 0; } +static inline void rebuild_sched_domains(void) +{ + partition_sched_domains(0, NULL, NULL); +} + #endif /* !CONFIG_CPUSETS */ #endif /* _LINUX_CPUSET_H */ diff --git a/init/main.c b/init/main.c index edeace036fd9..dd25259530ea 100644 --- a/init/main.c +++ b/init/main.c @@ -415,6 +415,13 @@ static void __init smp_init(void) { unsigned int cpu; + /* + * Set up the current CPU as possible to migrate to. + * The other ones will be done by cpu_up/cpu_down() + */ + cpu = smp_processor_id(); + cpu_set(cpu, cpu_active_map); + /* FIXME: This should be done in userspace --RR */ for_each_present_cpu(cpu) { if (num_online_cpus() >= setup_max_cpus) diff --git a/kernel/cpu.c b/kernel/cpu.c index cfb1d43ab801..a1ac7ea245d7 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -64,6 +64,8 @@ void __init cpu_hotplug_init(void) cpu_hotplug.refcount = 0; } +cpumask_t cpu_active_map; + #ifdef CONFIG_HOTPLUG_CPU void get_online_cpus(void) @@ -291,11 +293,20 @@ int __ref cpu_down(unsigned int cpu) int err = 0; cpu_maps_update_begin(); - if (cpu_hotplug_disabled) + + if (cpu_hotplug_disabled) { err = -EBUSY; - else - err = _cpu_down(cpu, 0); + goto out; + } + + cpu_clear(cpu, cpu_active_map); + + err = _cpu_down(cpu, 0); + + if (cpu_online(cpu)) + cpu_set(cpu, cpu_active_map); +out: cpu_maps_update_done(); return err; } @@ -355,11 +366,18 @@ int __cpuinit cpu_up(unsigned int cpu) } cpu_maps_update_begin(); - if (cpu_hotplug_disabled) + + if (cpu_hotplug_disabled) { err = -EBUSY; - else - err = _cpu_up(cpu, 0); + goto out; + } + err = _cpu_up(cpu, 0); + + if (cpu_online(cpu)) + cpu_set(cpu, cpu_active_map); + +out: cpu_maps_update_done(); return err; } diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 459d601947a8..3c3ef02f65f1 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -564,7 +564,7 @@ update_domain_attr(struct sched_domain_attr *dattr, struct cpuset *c) * partition_sched_domains(). */ -static void rebuild_sched_domains(void) +void rebuild_sched_domains(void) { struct kfifo *q; /* queue of cpusets to be scanned */ struct cpuset *cp; /* scans q */ diff --git a/kernel/sched.c b/kernel/sched.c index 1ee18dbb4516..c237624a8a04 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2881,7 +2881,7 @@ static void sched_migrate_task(struct task_struct *p, int dest_cpu) rq = task_rq_lock(p, &flags); if (!cpu_isset(dest_cpu, p->cpus_allowed) - || unlikely(cpu_is_offline(dest_cpu))) + || unlikely(!cpu_active(dest_cpu))) goto out; /* force the process onto the specified CPU */ @@ -3849,7 +3849,7 @@ int select_nohz_load_balancer(int stop_tick) /* * If we are going offline and still the leader, give up! */ - if (cpu_is_offline(cpu) && + if (!cpu_active(cpu) && atomic_read(&nohz.load_balancer) == cpu) { if (atomic_cmpxchg(&nohz.load_balancer, cpu, -1) != cpu) BUG(); @@ -5876,7 +5876,7 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) struct rq *rq_dest, *rq_src; int ret = 0, on_rq; - if (unlikely(cpu_is_offline(dest_cpu))) + if (unlikely(!cpu_active(dest_cpu))) return ret; rq_src = cpu_rq(src_cpu); @@ -7553,18 +7553,6 @@ void __attribute__((weak)) arch_update_cpu_topology(void) { } -/* - * Free current domain masks. - * Called after all cpus are attached to NULL domain. - */ -static void free_sched_domains(void) -{ - ndoms_cur = 0; - if (doms_cur != &fallback_doms) - kfree(doms_cur); - doms_cur = &fallback_doms; -} - /* * Set up scheduler domains and groups. Callers must hold the hotplug lock. * For now this just excludes isolated cpus, but could be used to @@ -7643,7 +7631,7 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur, * ownership of it and will kfree it when done with it. If the caller * failed the kmalloc call, then it can pass in doms_new == NULL, * and partition_sched_domains() will fallback to the single partition - * 'fallback_doms'. + * 'fallback_doms', it also forces the domains to be rebuilt. * * Call with hotplug lock held */ @@ -7657,12 +7645,8 @@ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, /* always unregister in case we don't destroy any domains */ unregister_sched_domain_sysctl(); - if (doms_new == NULL) { - ndoms_new = 1; - doms_new = &fallback_doms; - cpus_andnot(doms_new[0], cpu_online_map, cpu_isolated_map); - dattr_new = NULL; - } + if (doms_new == NULL) + ndoms_new = 0; /* Destroy deleted domains */ for (i = 0; i < ndoms_cur; i++) { @@ -7677,6 +7661,14 @@ match1: ; } + if (doms_new == NULL) { + ndoms_cur = 0; + ndoms_new = 1; + doms_new = &fallback_doms; + cpus_andnot(doms_new[0], cpu_online_map, cpu_isolated_map); + dattr_new = NULL; + } + /* Build new domains */ for (i = 0; i < ndoms_new; i++) { for (j = 0; j < ndoms_cur; j++) { @@ -7707,17 +7699,10 @@ match2: #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) int arch_reinit_sched_domains(void) { - int err; - get_online_cpus(); - mutex_lock(&sched_domains_mutex); - detach_destroy_domains(&cpu_online_map); - free_sched_domains(); - err = arch_init_sched_domains(&cpu_online_map); - mutex_unlock(&sched_domains_mutex); + rebuild_sched_domains(); put_online_cpus(); - - return err; + return 0; } static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) @@ -7783,14 +7768,30 @@ int sched_create_sysfs_power_savings_entries(struct sysdev_class *cls) } #endif /* CONFIG_SCHED_MC || CONFIG_SCHED_SMT */ +#ifndef CONFIG_CPUSETS /* - * Force a reinitialization of the sched domains hierarchy. The domains - * and groups cannot be updated in place without racing with the balancing - * code, so we temporarily attach all running cpus to the NULL domain - * which will prevent rebalancing while the sched domains are recalculated. + * Add online and remove offline CPUs from the scheduler domains. + * When cpusets are enabled they take over this function. */ static int update_sched_domains(struct notifier_block *nfb, unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + case CPU_DEAD: + case CPU_DEAD_FROZEN: + partition_sched_domains(0, NULL, NULL); + return NOTIFY_OK; + + default: + return NOTIFY_DONE; + } +} +#endif + +static int update_runtime(struct notifier_block *nfb, + unsigned long action, void *hcpu) { int cpu = (int)(long)hcpu; @@ -7798,44 +7799,18 @@ static int update_sched_domains(struct notifier_block *nfb, case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: disable_runtime(cpu_rq(cpu)); - /* fall-through */ - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - detach_destroy_domains(&cpu_online_map); - free_sched_domains(); return NOTIFY_OK; - case CPU_DOWN_FAILED: case CPU_DOWN_FAILED_FROZEN: case CPU_ONLINE: case CPU_ONLINE_FROZEN: enable_runtime(cpu_rq(cpu)); - /* fall-through */ - case CPU_UP_CANCELED: - case CPU_UP_CANCELED_FROZEN: - case CPU_DEAD: - case CPU_DEAD_FROZEN: - /* - * Fall through and re-initialise the domains. - */ - break; + return NOTIFY_OK; + default: return NOTIFY_DONE; } - -#ifndef CONFIG_CPUSETS - /* - * Create default domain partitioning if cpusets are disabled. - * Otherwise we let cpusets rebuild the domains based on the - * current setup. - */ - - /* The hotplug lock is already held by cpu_up/cpu_down */ - arch_init_sched_domains(&cpu_online_map); -#endif - - return NOTIFY_OK; } void __init sched_init_smp(void) @@ -7855,8 +7830,15 @@ void __init sched_init_smp(void) cpu_set(smp_processor_id(), non_isolated_cpus); mutex_unlock(&sched_domains_mutex); put_online_cpus(); + +#ifndef CONFIG_CPUSETS /* XXX: Theoretical race here - CPU may be hotplugged now */ hotcpu_notifier(update_sched_domains, 0); +#endif + + /* RT runtime code needs to handle some hotplug events */ + hotcpu_notifier(update_runtime, 0); + init_hrtick(); /* Move init over to a non-isolated CPU */ diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index f2aa987027d6..d924c679dfac 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1004,6 +1004,8 @@ static void yield_task_fair(struct rq *rq) * not idle and an idle cpu is available. The span of cpus to * search starts with cpus closest then further out as needed, * so we always favor a closer, idle cpu. + * Domains may include CPUs that are not usable for migration, + * hence we need to mask them out (cpu_active_map) * * Returns the CPU we should wake onto. */ @@ -1031,6 +1033,7 @@ static int wake_idle(int cpu, struct task_struct *p) || ((sd->flags & SD_WAKE_IDLE_FAR) && !task_hot(p, task_rq(p)->clock, sd))) { cpus_and(tmp, sd->span, p->cpus_allowed); + cpus_and(tmp, tmp, cpu_active_map); for_each_cpu_mask(i, tmp) { if (idle_cpu(i)) { if (i != task_cpu(p)) { diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index d3d1cccb3d7b..50735bb96149 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -933,6 +933,13 @@ static int find_lowest_rq(struct task_struct *task) if (!cpupri_find(&task_rq(task)->rd->cpupri, task, lowest_mask)) return -1; /* No targets found */ + /* + * Only consider CPUs that are usable for migration. + * I guess we might want to change cpupri_find() to ignore those + * in the first place. + */ + cpus_and(*lowest_mask, *lowest_mask, cpu_active_map); + /* * At this point we have built a mask of cpus representing the * lowest priority tasks in the system. Now we want to elect -- cgit v1.2.3 From 1b427c153a08fdbc092c2bdbf845b92fda58d857 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 18 Jul 2008 14:01:39 +0200 Subject: sched: fix build error, provide partition_sched_domains() unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit provide an empty partition_sched_domains() definition for the UP case: include/linux/cpuset.h: In function ‘rebuild_sched_domains': include/linux/cpuset.h:163: error: implicit declaration of function ‘partition_sched_domains' Signed-off-by: Ingo Molnar --- include/linux/sched.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1941d8b5cf11..26da921530fe 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -824,7 +824,16 @@ extern void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, struct sched_domain_attr *dattr_new); extern int arch_reinit_sched_domains(void); -#endif /* CONFIG_SMP */ +#else /* CONFIG_SMP */ + +struct sched_domain_attr; + +static inline void +partition_sched_domains(int ndoms_new, cpumask_t *doms_new, + struct sched_domain_attr *dattr_new) +{ +} +#endif /* !CONFIG_SMP */ struct io_context; /* See blkdev.h */ #define NGROUPS_SMALL 32 -- cgit v1.2.3 From 3cac97cbb14aed00d83eb33d4613b0fe3aaea863 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Sun, 6 Jul 2008 17:23:55 +0800 Subject: rcu classic: simplify the next pending batch use a batch number(rcp->pending) instead of a flag(rcp->next_pending) rcu_start_batch() need to change this flag, so mb()s is needed for memory-access safe. but(after this patch applied) rcu_start_batch() do not change this batch number(rcp->pending), rcp->pending is managed by __rcu_process_callbacks only, and troublesome mb()s are eliminated. And codes look simpler and clearer. Signed-off-by: Lai Jiangshan Cc: "Paul E. McKenney" Cc: Dipankar Sarma Cc: Gautham Shenoy Cc: Dhaval Giani Cc: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/rcuclassic.h | 2 +- kernel/rcuclassic.c | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index 8c774905dcfe..c847e59c6006 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -45,7 +45,7 @@ struct rcu_ctrlblk { long cur; /* Current batch number. */ long completed; /* Number of the last completed batch */ - int next_pending; /* Is the next batch already waiting? */ + long pending; /* Number of the last pending batch */ int signaled; diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index 16eeeaa9d618..03726eb95193 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -60,12 +60,14 @@ EXPORT_SYMBOL_GPL(rcu_lock_map); static struct rcu_ctrlblk rcu_ctrlblk = { .cur = -300, .completed = -300, + .pending = -300, .lock = __SPIN_LOCK_UNLOCKED(&rcu_ctrlblk.lock), .cpumask = CPU_MASK_NONE, }; static struct rcu_ctrlblk rcu_bh_ctrlblk = { .cur = -300, .completed = -300, + .pending = -300, .lock = __SPIN_LOCK_UNLOCKED(&rcu_bh_ctrlblk.lock), .cpumask = CPU_MASK_NONE, }; @@ -276,14 +278,8 @@ static void rcu_do_batch(struct rcu_data *rdp) */ static void rcu_start_batch(struct rcu_ctrlblk *rcp) { - if (rcp->next_pending && + if (rcp->cur != rcp->pending && rcp->completed == rcp->cur) { - rcp->next_pending = 0; - /* - * next_pending == 0 must be visible in - * __rcu_process_callbacks() before it can see new value of cur. - */ - smp_wmb(); rcp->cur++; /* @@ -441,16 +437,14 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp, /* determine batch number */ rdp->batch = rcp->cur + 1; - /* see the comment and corresponding wmb() in - * the rcu_start_batch() - */ - smp_rmb(); - if (!rcp->next_pending) { + if (rcu_batch_after(rdp->batch, rcp->pending)) { /* and start it/schedule start if it's a new batch */ spin_lock(&rcp->lock); - rcp->next_pending = 1; - rcu_start_batch(rcp); + if (rcu_batch_after(rdp->batch, rcp->pending)) { + rcp->pending = rdp->batch; + rcu_start_batch(rcp); + } spin_unlock(&rcp->lock); } } -- cgit v1.2.3 From 5127bed588a2f8f3a1f732de2a8a190b7df5dce3 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Sun, 6 Jul 2008 17:23:59 +0800 Subject: rcu classic: new algorithm for callbacks-processing(v2) This is v2, it's a little deference from v1 that I had send to lkml. use ACCESS_ONCE use rcu_batch_after/rcu_batch_before for batch # comparison. rcutorture test result: (hotplugs: do cpu-online/offline once per second) No CONFIG_NO_HZ: OK, 12hours No CONFIG_NO_HZ, hotplugs: OK, 12hours CONFIG_NO_HZ=y: OK, 24hours CONFIG_NO_HZ=y, hotplugs: Failed. (Failed also without my patch applied, exactly the same bug occurred, http://lkml.org/lkml/2008/7/3/24) v1's email thread: http://lkml.org/lkml/2008/6/2/539 v1's description: The code/algorithm of the implement of current callbacks-processing is very efficient and technical. But when I studied it and I found a disadvantage: In multi-CPU systems, when a new RCU callback is being queued(call_rcu[_bh]), this callback will be invoked after the grace period for the batch with batch number = rcp->cur+2 has completed very very likely in current implement. Actually, this callback can be invoked after the grace period for the batch with batch number = rcp->cur+1 has completed. The delay of invocation means that latency of synchronize_rcu() is extended. But more important thing is that the callbacks usually free memory, and these works are delayed too! it's necessary for reclaimer to free memory as soon as possible when left memory is few. A very simple way can solve this problem: a field(struct rcu_head::batch) is added to record the batch number for the RCU callback. And when a new RCU callback is being queued, we determine the batch number for this callback(head->batch = rcp->cur+1) and we move this callback to rdp->donelist if we find that head->batch <= rcp->completed when we process callbacks. This simple way reduces the wait time for invocation a lot. (about 2.5Grace Period -> 1.5Grace Period in average in multi-CPU systems) This is my algorithm. But I do not add any field for struct rcu_head in my implement. We just need to memorize the last 2 batches and their batch number, because these 2 batches include all entries that for whom the grace period hasn't completed. So we use a special linked-list rather than add a field. Please see the comment of struct rcu_data. Signed-off-by: Lai Jiangshan Cc: "Paul E. McKenney" Cc: Dipankar Sarma Cc: Gautham Shenoy Cc: Dhaval Giani Cc: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/rcuclassic.h | 26 +++++--- kernel/rcuclassic.c | 157 ++++++++++++++++++++++++++++----------------- 2 files changed, 114 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index c847e59c6006..04c728147be0 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -66,11 +66,7 @@ static inline int rcu_batch_after(long a, long b) return (a - b) > 0; } -/* - * Per-CPU data for Read-Copy UPdate. - * nxtlist - new callbacks are added here - * curlist - current batch for which quiescent cycle started if any - */ +/* Per-CPU data for Read-Copy UPdate. */ struct rcu_data { /* 1) quiescent state handling : */ long quiescbatch; /* Batch # for grace period */ @@ -78,12 +74,24 @@ struct rcu_data { int qs_pending; /* core waits for quiesc state */ /* 2) batch handling */ - long batch; /* Batch # for current RCU batch */ + /* + * if nxtlist is not NULL, then: + * batch: + * The batch # for the last entry of nxtlist + * [*nxttail[1], NULL = *nxttail[2]): + * Entries that batch # <= batch + * [*nxttail[0], *nxttail[1]): + * Entries that batch # <= batch - 1 + * [nxtlist, *nxttail[0]): + * Entries that batch # <= batch - 2 + * The grace period for these entries has completed, and + * the other grace-period-completed entries may be moved + * here temporarily in rcu_process_callbacks(). + */ + long batch; struct rcu_head *nxtlist; - struct rcu_head **nxttail; + struct rcu_head **nxttail[3]; long qlen; /* # of queued callbacks */ - struct rcu_head *curlist; - struct rcu_head **curtail; struct rcu_head *donelist; struct rcu_head **donetail; long blimit; /* Upper limit on a processed batch */ diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index 03726eb95193..d3553ee55f64 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -120,6 +120,43 @@ static inline void force_quiescent_state(struct rcu_data *rdp, } #endif +static void __call_rcu(struct rcu_head *head, struct rcu_ctrlblk *rcp, + struct rcu_data *rdp) +{ + long batch; + smp_mb(); /* reads the most recently updated value of rcu->cur. */ + + /* + * Determine the batch number of this callback. + * + * Using ACCESS_ONCE to avoid the following error when gcc eliminates + * local variable "batch" and emits codes like this: + * 1) rdp->batch = rcp->cur + 1 # gets old value + * ...... + * 2)rcu_batch_after(rcp->cur + 1, rdp->batch) # gets new value + * then [*nxttail[0], *nxttail[1]) may contain callbacks + * that batch# = rdp->batch, see the comment of struct rcu_data. + */ + batch = ACCESS_ONCE(rcp->cur) + 1; + + if (rdp->nxtlist && rcu_batch_after(batch, rdp->batch)) { + /* process callbacks */ + rdp->nxttail[0] = rdp->nxttail[1]; + rdp->nxttail[1] = rdp->nxttail[2]; + if (rcu_batch_after(batch - 1, rdp->batch)) + rdp->nxttail[0] = rdp->nxttail[2]; + } + + rdp->batch = batch; + *rdp->nxttail[2] = head; + rdp->nxttail[2] = &head->next; + + if (unlikely(++rdp->qlen > qhimark)) { + rdp->blimit = INT_MAX; + force_quiescent_state(rdp, &rcu_ctrlblk); + } +} + /** * call_rcu - Queue an RCU callback for invocation after a grace period. * @head: structure to be used for queueing the RCU updates. @@ -135,18 +172,11 @@ void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) { unsigned long flags; - struct rcu_data *rdp; head->func = func; head->next = NULL; local_irq_save(flags); - rdp = &__get_cpu_var(rcu_data); - *rdp->nxttail = head; - rdp->nxttail = &head->next; - if (unlikely(++rdp->qlen > qhimark)) { - rdp->blimit = INT_MAX; - force_quiescent_state(rdp, &rcu_ctrlblk); - } + __call_rcu(head, &rcu_ctrlblk, &__get_cpu_var(rcu_data)); local_irq_restore(flags); } EXPORT_SYMBOL_GPL(call_rcu); @@ -171,20 +201,11 @@ void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) { unsigned long flags; - struct rcu_data *rdp; head->func = func; head->next = NULL; local_irq_save(flags); - rdp = &__get_cpu_var(rcu_bh_data); - *rdp->nxttail = head; - rdp->nxttail = &head->next; - - if (unlikely(++rdp->qlen > qhimark)) { - rdp->blimit = INT_MAX; - force_quiescent_state(rdp, &rcu_bh_ctrlblk); - } - + __call_rcu(head, &rcu_bh_ctrlblk, &__get_cpu_var(rcu_bh_data)); local_irq_restore(flags); } EXPORT_SYMBOL_GPL(call_rcu_bh); @@ -213,12 +234,6 @@ EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); static inline void raise_rcu_softirq(void) { raise_softirq(RCU_SOFTIRQ); - /* - * The smp_mb() here is required to ensure that this cpu's - * __rcu_process_callbacks() reads the most recently updated - * value of rcu->cur. - */ - smp_mb(); } /* @@ -360,13 +375,15 @@ static void rcu_check_quiescent_state(struct rcu_ctrlblk *rcp, * which is dead and hence not processing interrupts. */ static void rcu_move_batch(struct rcu_data *this_rdp, struct rcu_head *list, - struct rcu_head **tail) + struct rcu_head **tail, long batch) { - local_irq_disable(); - *this_rdp->nxttail = list; - if (list) - this_rdp->nxttail = tail; - local_irq_enable(); + if (list) { + local_irq_disable(); + this_rdp->batch = batch; + *this_rdp->nxttail[2] = list; + this_rdp->nxttail[2] = tail; + local_irq_enable(); + } } static void __rcu_offline_cpu(struct rcu_data *this_rdp, @@ -380,9 +397,9 @@ static void __rcu_offline_cpu(struct rcu_data *this_rdp, if (rcp->cur != rcp->completed) cpu_quiet(rdp->cpu, rcp); spin_unlock_bh(&rcp->lock); - rcu_move_batch(this_rdp, rdp->donelist, rdp->donetail); - rcu_move_batch(this_rdp, rdp->curlist, rdp->curtail); - rcu_move_batch(this_rdp, rdp->nxtlist, rdp->nxttail); + /* spin_lock implies smp_mb() */ + rcu_move_batch(this_rdp, rdp->donelist, rdp->donetail, rcp->cur + 1); + rcu_move_batch(this_rdp, rdp->nxtlist, rdp->nxttail[2], rcp->cur + 1); local_irq_disable(); this_rdp->qlen += rdp->qlen; @@ -416,27 +433,37 @@ static void rcu_offline_cpu(int cpu) static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) { - if (rdp->curlist && !rcu_batch_before(rcp->completed, rdp->batch)) { - *rdp->donetail = rdp->curlist; - rdp->donetail = rdp->curtail; - rdp->curlist = NULL; - rdp->curtail = &rdp->curlist; - } - - if (rdp->nxtlist && !rdp->curlist) { + if (rdp->nxtlist) { local_irq_disable(); - rdp->curlist = rdp->nxtlist; - rdp->curtail = rdp->nxttail; - rdp->nxtlist = NULL; - rdp->nxttail = &rdp->nxtlist; - local_irq_enable(); /* - * start the next batch of callbacks + * move the other grace-period-completed entries to + * [rdp->nxtlist, *rdp->nxttail[0]) temporarily + */ + if (!rcu_batch_before(rcp->completed, rdp->batch)) + rdp->nxttail[0] = rdp->nxttail[1] = rdp->nxttail[2]; + else if (!rcu_batch_before(rcp->completed, rdp->batch - 1)) + rdp->nxttail[0] = rdp->nxttail[1]; + + /* + * the grace period for entries in + * [rdp->nxtlist, *rdp->nxttail[0]) has completed and + * move these entries to donelist */ + if (rdp->nxttail[0] != &rdp->nxtlist) { + *rdp->donetail = rdp->nxtlist; + rdp->donetail = rdp->nxttail[0]; + rdp->nxtlist = *rdp->nxttail[0]; + *rdp->donetail = NULL; + + if (rdp->nxttail[1] == rdp->nxttail[0]) + rdp->nxttail[1] = &rdp->nxtlist; + if (rdp->nxttail[2] == rdp->nxttail[0]) + rdp->nxttail[2] = &rdp->nxtlist; + rdp->nxttail[0] = &rdp->nxtlist; + } - /* determine batch number */ - rdp->batch = rcp->cur + 1; + local_irq_enable(); if (rcu_batch_after(rdp->batch, rcp->pending)) { /* and start it/schedule start if it's a new batch */ @@ -462,15 +489,26 @@ static void rcu_process_callbacks(struct softirq_action *unused) static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) { - /* This cpu has pending rcu entries and the grace period - * for them has completed. - */ - if (rdp->curlist && !rcu_batch_before(rcp->completed, rdp->batch)) - return 1; + if (rdp->nxtlist) { + /* + * This cpu has pending rcu entries and the grace period + * for them has completed. + */ + if (!rcu_batch_before(rcp->completed, rdp->batch)) + return 1; + if (!rcu_batch_before(rcp->completed, rdp->batch - 1) && + rdp->nxttail[0] != rdp->nxttail[1]) + return 1; + if (rdp->nxttail[0] != &rdp->nxtlist) + return 1; - /* This cpu has no pending entries, but there are new entries */ - if (!rdp->curlist && rdp->nxtlist) - return 1; + /* + * This cpu has pending rcu entries and the new batch + * for then hasn't been started nor scheduled start + */ + if (rcu_batch_after(rdp->batch, rcp->pending)) + return 1; + } /* This cpu has finished callbacks to invoke */ if (rdp->donelist) @@ -506,7 +544,7 @@ int rcu_needs_cpu(int cpu) struct rcu_data *rdp = &per_cpu(rcu_data, cpu); struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu); - return (!!rdp->curlist || !!rdp_bh->curlist || rcu_pending(cpu)); + return !!rdp->nxtlist || !!rdp_bh->nxtlist || rcu_pending(cpu); } void rcu_check_callbacks(int cpu, int user) @@ -553,8 +591,7 @@ static void rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp, struct rcu_data *rdp) { memset(rdp, 0, sizeof(*rdp)); - rdp->curtail = &rdp->curlist; - rdp->nxttail = &rdp->nxtlist; + rdp->nxttail[0] = rdp->nxttail[1] = rdp->nxttail[2] = &rdp->nxtlist; rdp->donetail = &rdp->donelist; rdp->quiescbatch = rcp->completed; rdp->qs_pending = 0; -- cgit v1.2.3 From b8f8c3cf0a4ac0632ec3f0e15e9dc0c29de917af Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 18 Jul 2008 17:27:28 +0200 Subject: nohz: prevent tick stop outside of the idle loop Jack Ren and Eric Miao tracked down the following long standing problem in the NOHZ code: scheduler switch to idle task enable interrupts Window starts here ----> interrupt happens (does not set NEED_RESCHED) irq_exit() stops the tick ----> interrupt happens (does set NEED_RESCHED) return from schedule() cpu_idle(): preempt_disable(); Window ends here The interrupts can happen at any point inside the race window. The first interrupt stops the tick, the second one causes the scheduler to rerun and switch away from idle again and we end up with the tick disabled. The fact that it needs two interrupts where the first one does not set NEED_RESCHED and the second one does made the bug obscure and extremly hard to reproduce and analyse. Kudos to Jack and Eric. Solution: Limit the NOHZ functionality to the idle loop to make sure that we can not run into such a situation ever again. cpu_idle() { preempt_disable(); while(1) { tick_nohz_stop_sched_tick(1); <- tell NOHZ code that we are in the idle loop while (!need_resched()) halt(); tick_nohz_restart_sched_tick(); <- disables NOHZ mode preempt_enable_no_resched(); schedule(); preempt_disable(); } } In hindsight we should have done this forever, but ... /me grabs a large brown paperbag. Debugged-by: Jack Ren , Debugged-by: eric miao Signed-off-by: Thomas Gleixner --- arch/arm/kernel/process.c | 2 +- arch/avr32/kernel/process.c | 2 +- arch/blackfin/kernel/process.c | 2 +- arch/mips/kernel/process.c | 2 +- arch/powerpc/kernel/idle.c | 2 +- arch/powerpc/platforms/iseries/setup.c | 4 ++-- arch/sh/kernel/process_32.c | 2 +- arch/sparc64/kernel/process.c | 2 +- arch/um/kernel/process.c | 2 +- arch/x86/kernel/process_32.c | 2 +- arch/x86/kernel/process_64.c | 2 +- include/linux/tick.h | 5 +++-- kernel/softirq.c | 2 +- kernel/time/tick-sched.c | 12 ++++++++++-- 14 files changed, 26 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 46bf2ede6128..84f5a4c778fb 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -164,7 +164,7 @@ void cpu_idle(void) if (!idle) idle = default_idle; leds_event(led_idle_start); - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) idle(); leds_event(led_idle_end); diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 6cf9df176274..ff820a9e743a 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -31,7 +31,7 @@ void cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) cpu_idle_sleep(); tick_nohz_restart_sched_tick(); diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 53c2cd255441..77800dd83e57 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -105,7 +105,7 @@ void cpu_idle(void) #endif if (!idle) idle = default_idle; - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) idle(); tick_nohz_restart_sched_tick(); diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 2c09a442e5e5..bdead3aad253 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -53,7 +53,7 @@ void __noreturn cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG extern void smtc_idle_loop_hook(void); diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c index c3cf0e8f3ac1..d308a9f70f1b 100644 --- a/arch/powerpc/kernel/idle.c +++ b/arch/powerpc/kernel/idle.c @@ -60,7 +60,7 @@ void cpu_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !cpu_should_die()) { ppc64_runlatch_off(); diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index b72120751bbe..70b688c1aefb 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -561,7 +561,7 @@ static void yield_shared_processor(void) static void iseries_shared_idle(void) { while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !hvlpevent_is_pending()) { local_irq_disable(); ppc64_runlatch_off(); @@ -591,7 +591,7 @@ static void iseries_dedicated_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); if (!need_resched()) { while (!need_resched()) { ppc64_runlatch_off(); diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index b98e37a1f54c..921892c351da 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -86,7 +86,7 @@ void cpu_idle(void) if (!idle) idle = default_idle; - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) idle(); tick_nohz_restart_sched_tick(); diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 2084f81a76e1..0798928ba361 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -97,7 +97,7 @@ void cpu_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while(1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !cpu_is_offline(cpu)) sparc64_yield(cpu); diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index 83603cfbde81..a1c6d07cac3e 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -243,7 +243,7 @@ void default_idle(void) if (need_resched()) schedule(); - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); nsecs = disable_timer(); idle_sleep(nsecs); tick_nohz_restart_sched_tick(); diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index f8476dfbb60d..1f5fa1cf16dd 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -166,7 +166,7 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { void (*idle)(void); diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index e2319f39988b..c0a5c2a687e6 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -148,7 +148,7 @@ void cpu_idle(void) current_thread_info()->status |= TS_POLLING; /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { void (*idle)(void); diff --git a/include/linux/tick.h b/include/linux/tick.h index a881c652f7e9..d3c02695dc5d 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -49,6 +49,7 @@ struct tick_sched { unsigned long check_clocks; enum tick_nohz_mode nohz_mode; ktime_t idle_tick; + int inidle; int tick_stopped; unsigned long idle_jiffies; unsigned long idle_calls; @@ -105,14 +106,14 @@ static inline int tick_check_oneshot_change(int allow_nohz) { return 0; } #endif /* !CONFIG_GENERIC_CLOCKEVENTS */ # ifdef CONFIG_NO_HZ -extern void tick_nohz_stop_sched_tick(void); +extern void tick_nohz_stop_sched_tick(int inidle); extern void tick_nohz_restart_sched_tick(void); extern void tick_nohz_update_jiffies(void); extern ktime_t tick_nohz_get_sleep_length(void); extern void tick_nohz_stop_idle(int cpu); extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); # else -static inline void tick_nohz_stop_sched_tick(void) { } +static inline void tick_nohz_stop_sched_tick(int inidle) { } static inline void tick_nohz_restart_sched_tick(void) { } static inline void tick_nohz_update_jiffies(void) { } static inline ktime_t tick_nohz_get_sleep_length(void) diff --git a/kernel/softirq.c b/kernel/softirq.c index 36e061740047..05f248039d77 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -312,7 +312,7 @@ void irq_exit(void) #ifdef CONFIG_NO_HZ /* Make sure that timer wheel updates are propagated */ if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched()) - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(0); rcu_irq_exit(); #endif preempt_enable_no_resched(); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 86baa4f0dfe4..ee962d11107b 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -195,7 +195,7 @@ u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time) * Called either from the idle loop or from irq_exit() when an idle period was * just interrupted by an interrupt which did not cause a reschedule. */ -void tick_nohz_stop_sched_tick(void) +void tick_nohz_stop_sched_tick(int inidle) { unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags; struct tick_sched *ts; @@ -224,6 +224,11 @@ void tick_nohz_stop_sched_tick(void) if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE)) goto end; + if (!inidle && !ts->inidle) + goto end; + + ts->inidle = 1; + if (need_resched()) goto end; @@ -372,11 +377,14 @@ void tick_nohz_restart_sched_tick(void) local_irq_disable(); tick_nohz_stop_idle(cpu); - if (!ts->tick_stopped) { + if (!ts->inidle || !ts->tick_stopped) { + ts->inidle = 0; local_irq_enable(); return; } + ts->inidle = 0; + rcu_exit_nohz(); /* Update jiffies first */ -- cgit v1.2.3 From 65c011845316d3c1381f478ca0d8265c43b3b039 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Tue, 15 Jul 2008 14:14:30 -0700 Subject: cpumask: Replace cpumask_of_cpu with cpumask_of_cpu_ptr * This patch replaces the dangerous lvalue version of cpumask_of_cpu with new cpumask_of_cpu_ptr macros. These are patterned after the node_to_cpumask_ptr macros. In general terms, if there is a cpumask_of_cpu_map[] then a pointer to the cpumask_of_cpu_map[cpu] entry is used. The cpumask_of_cpu_map is provided when there is a large NR_CPUS count, reducing greatly the amount of code generated and stack space used for cpumask_of_cpu(). The pointer to the cpumask_t value is needed for calling set_cpus_allowed_ptr() to reduce the amount of stack space needed to pass the cpumask_t value. If there isn't a cpumask_of_cpu_map[], then a temporary variable is declared and filled in with value from cpumask_of_cpu(cpu) as well as a pointer variable pointing to this temporary variable. Afterwards, the pointer is used to reference the cpumask value. The compiler will optimize out the extra dereference through the pointer as well as the stack space used for the pointer, resulting in identical code. A good example of the orthogonal usages is in net/sunrpc/svc.c: case SVC_POOL_PERCPU: { unsigned int cpu = m->pool_to[pidx]; cpumask_of_cpu_ptr(cpumask, cpu); *oldmask = current->cpus_allowed; set_cpus_allowed_ptr(current, cpumask); return 1; } case SVC_POOL_PERNODE: { unsigned int node = m->pool_to[pidx]; node_to_cpumask_ptr(nodecpumask, node); *oldmask = current->cpus_allowed; set_cpus_allowed_ptr(current, nodecpumask); return 1; } Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- arch/x86/kernel/acpi/cstate.c | 3 ++- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 10 +++++--- arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 15 +++++++---- arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c | 9 ++++--- arch/x86/kernel/cpu/cpufreq/speedstep-ich.c | 3 ++- arch/x86/kernel/cpu/intel_cacheinfo.c | 3 ++- arch/x86/kernel/microcode.c | 13 +++++++--- arch/x86/kernel/reboot.c | 14 +++++++---- drivers/acpi/processor_throttling.c | 11 +++++--- drivers/firmware/dcdbas.c | 3 ++- include/linux/cpumask.h | 32 ++++++++++++++++++++---- kernel/stop_machine.c | 3 ++- kernel/trace/trace_sysprof.c | 4 ++- net/sunrpc/svc.c | 3 ++- 14 files changed, 91 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c index c2502eb9aa83..9220cf46aa10 100644 --- a/arch/x86/kernel/acpi/cstate.c +++ b/arch/x86/kernel/acpi/cstate.c @@ -73,6 +73,7 @@ int acpi_processor_ffh_cstate_probe(unsigned int cpu, struct cpuinfo_x86 *c = &cpu_data(cpu); cpumask_t saved_mask; + cpumask_of_cpu_ptr(new_mask, cpu); int retval; unsigned int eax, ebx, ecx, edx; unsigned int edx_part; @@ -91,7 +92,7 @@ int acpi_processor_ffh_cstate_probe(unsigned int cpu, /* Make sure we are running on right CPU */ saved_mask = current->cpus_allowed; - retval = set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + retval = set_cpus_allowed_ptr(current, new_mask); if (retval) return -1; diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index dd097b835839..ff2fff56f0a8 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -200,10 +200,12 @@ static void drv_read(struct drv_cmd *cmd) static void drv_write(struct drv_cmd *cmd) { cpumask_t saved_mask = current->cpus_allowed; + cpumask_of_cpu_ptr_declare(cpu_mask); unsigned int i; for_each_cpu_mask_nr(i, cmd->mask) { - set_cpus_allowed_ptr(current, &cpumask_of_cpu(i)); + cpumask_of_cpu_ptr_next(cpu_mask, i); + set_cpus_allowed_ptr(current, cpu_mask); do_drv_write(cmd); } @@ -267,11 +269,12 @@ static unsigned int get_measured_perf(unsigned int cpu) } aperf_cur, mperf_cur; cpumask_t saved_mask; + cpumask_of_cpu_ptr(cpu_mask, cpu); unsigned int perf_percent; unsigned int retval; saved_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, cpu_mask); if (get_cpu() != cpu) { /* We were not able to run on requested processor */ put_cpu(); @@ -337,6 +340,7 @@ static unsigned int get_measured_perf(unsigned int cpu) static unsigned int get_cur_freq_on_cpu(unsigned int cpu) { + cpumask_of_cpu_ptr(cpu_mask, cpu); struct acpi_cpufreq_data *data = per_cpu(drv_data, cpu); unsigned int freq; unsigned int cached_freq; @@ -349,7 +353,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) } cached_freq = data->freq_table[data->acpi_data->state].frequency; - freq = extract_freq(get_cur_val(&cpumask_of_cpu(cpu)), data); + freq = extract_freq(get_cur_val(cpu_mask), data); if (freq != cached_freq) { /* * The dreaded BIOS frequency change behind our back. diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index c45ca6d4dce1..53c7b6936973 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -479,11 +479,12 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvi static int check_supported_cpu(unsigned int cpu) { cpumask_t oldmask; + cpumask_of_cpu_ptr(cpu_mask, cpu); u32 eax, ebx, ecx, edx; unsigned int rc = 0; oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, cpu_mask); if (smp_processor_id() != cpu) { printk(KERN_ERR PFX "limiting to cpu %u failed\n", cpu); @@ -1016,6 +1017,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation) { cpumask_t oldmask; + cpumask_of_cpu_ptr(cpu_mask, pol->cpu); struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); u32 checkfid; u32 checkvid; @@ -1030,7 +1032,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi /* only run on specific CPU from here on */ oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pol->cpu)); + set_cpus_allowed_ptr(current, cpu_mask); if (smp_processor_id() != pol->cpu) { printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu); @@ -1105,6 +1107,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) { struct powernow_k8_data *data; cpumask_t oldmask; + cpumask_of_cpu_ptr_declare(newmask); int rc; if (!cpu_online(pol->cpu)) @@ -1156,7 +1159,8 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) /* only run on specific CPU from here on */ oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pol->cpu)); + cpumask_of_cpu_ptr_next(newmask, pol->cpu); + set_cpus_allowed_ptr(current, newmask); if (smp_processor_id() != pol->cpu) { printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu); @@ -1178,7 +1182,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) set_cpus_allowed_ptr(current, &oldmask); if (cpu_family == CPU_HW_PSTATE) - pol->cpus = cpumask_of_cpu(pol->cpu); + pol->cpus = *newmask; else pol->cpus = per_cpu(cpu_core_map, pol->cpu); data->available_cores = &(pol->cpus); @@ -1244,6 +1248,7 @@ static unsigned int powernowk8_get (unsigned int cpu) { struct powernow_k8_data *data; cpumask_t oldmask = current->cpus_allowed; + cpumask_of_cpu_ptr(newmask, cpu); unsigned int khz = 0; unsigned int first; @@ -1253,7 +1258,7 @@ static unsigned int powernowk8_get (unsigned int cpu) if (!data) return -EINVAL; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); if (smp_processor_id() != cpu) { printk(KERN_ERR PFX "limiting to CPU %d failed in powernowk8_get\n", cpu); diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c index 8b0dd6f2a1ac..fd561bb26c60 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c @@ -313,9 +313,10 @@ static unsigned int get_cur_freq(unsigned int cpu) unsigned l, h; unsigned clock_freq; cpumask_t saved_mask; + cpumask_of_cpu_ptr(new_mask, cpu); saved_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, new_mask); if (smp_processor_id() != cpu) return 0; @@ -554,9 +555,11 @@ static int centrino_target (struct cpufreq_policy *policy, */ if (!cpus_empty(covered_cpus)) { + cpumask_of_cpu_ptr_declare(new_mask); + for_each_cpu_mask_nr(j, covered_cpus) { - set_cpus_allowed_ptr(current, - &cpumask_of_cpu(j)); + cpumask_of_cpu_ptr_next(new_mask, j); + set_cpus_allowed_ptr(current, new_mask); wrmsr(MSR_IA32_PERF_CTL, oldmsr, h); } } diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c index 191f7263c61d..2f3728dc24f6 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c @@ -244,7 +244,8 @@ static unsigned int _speedstep_get(const cpumask_t *cpus) static unsigned int speedstep_get(unsigned int cpu) { - return _speedstep_get(&cpumask_of_cpu(cpu)); + cpumask_of_cpu_ptr(newmask, cpu); + return _speedstep_get(newmask); } /** diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index a7b0f8f1736b..e4b8d189d7ed 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -516,6 +516,7 @@ static int __cpuinit detect_cache_attributes(unsigned int cpu) unsigned long j; int retval; cpumask_t oldmask; + cpumask_of_cpu_ptr(newmask, cpu); if (num_cache_leaves == 0) return -ENOENT; @@ -526,7 +527,7 @@ static int __cpuinit detect_cache_attributes(unsigned int cpu) return -ENOMEM; oldmask = current->cpus_allowed; - retval = set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + retval = set_cpus_allowed_ptr(current, newmask); if (retval) goto out; diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c index 56b933119a04..58520169e35d 100644 --- a/arch/x86/kernel/microcode.c +++ b/arch/x86/kernel/microcode.c @@ -388,6 +388,7 @@ static int do_microcode_update (void) void *new_mc = NULL; int cpu; cpumask_t old; + cpumask_of_cpu_ptr_declare(newmask); old = current->cpus_allowed; @@ -404,7 +405,8 @@ static int do_microcode_update (void) if (!uci->valid) continue; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + cpumask_of_cpu_ptr_next(newmask, cpu); + set_cpus_allowed_ptr(current, newmask); error = get_maching_microcode(new_mc, cpu); if (error < 0) goto out; @@ -574,6 +576,7 @@ static int apply_microcode_check_cpu(int cpu) struct cpuinfo_x86 *c = &cpu_data(cpu); struct ucode_cpu_info *uci = ucode_cpu_info + cpu; cpumask_t old; + cpumask_of_cpu_ptr(newmask, cpu); unsigned int val[2]; int err = 0; @@ -582,7 +585,7 @@ static int apply_microcode_check_cpu(int cpu) return 0; old = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); /* Check if the microcode we have in memory matches the CPU */ if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || @@ -620,11 +623,12 @@ static int apply_microcode_check_cpu(int cpu) static void microcode_init_cpu(int cpu, int resume) { cpumask_t old; + cpumask_of_cpu_ptr(newmask, cpu); struct ucode_cpu_info *uci = ucode_cpu_info + cpu; old = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); mutex_lock(µcode_mutex); collect_cpu_info(cpu); if (uci->valid && system_state == SYSTEM_RUNNING && !resume) @@ -656,11 +660,12 @@ static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) return -EINVAL; if (val == 1) { cpumask_t old; + cpumask_of_cpu_ptr(newmask, cpu); old = current->cpus_allowed; get_online_cpus(); - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); mutex_lock(µcode_mutex); if (uci->valid) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index f8a62160e151..214bbdfc851e 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -403,24 +403,28 @@ void native_machine_shutdown(void) { /* Stop the cpus and apics */ #ifdef CONFIG_SMP - int reboot_cpu_id; /* The boot cpu is always logical cpu 0 */ - reboot_cpu_id = 0; + int reboot_cpu_id = 0; + cpumask_of_cpu_ptr(newmask, reboot_cpu_id); #ifdef CONFIG_X86_32 /* See if there has been given a command line override */ if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) && - cpu_online(reboot_cpu)) + cpu_online(reboot_cpu)) { reboot_cpu_id = reboot_cpu; + cpumask_of_cpu_ptr_next(newmask, reboot_cpu_id); + } #endif /* Make certain the cpu I'm about to reboot on is online */ - if (!cpu_online(reboot_cpu_id)) + if (!cpu_online(reboot_cpu_id)) { reboot_cpu_id = smp_processor_id(); + cpumask_of_cpu_ptr_next(newmask, reboot_cpu_id); + } /* Make certain I only run on the appropriate processor */ - set_cpus_allowed_ptr(current, &cpumask_of_cpu(reboot_cpu_id)); + set_cpus_allowed_ptr(current, newmask); /* O.K Now that I'm on the appropriate processor, * stop all of the others. diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index a56fc6c4394b..a2c3f9cfa549 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -827,6 +827,7 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) static int acpi_processor_get_throttling(struct acpi_processor *pr) { cpumask_t saved_mask; + cpumask_of_cpu_ptr_declare(new_mask); int ret; if (!pr) @@ -838,7 +839,8 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) * Migrate task to the cpu pointed by pr. */ saved_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pr->id)); + cpumask_of_cpu_ptr_next(new_mask, pr->id); + set_cpus_allowed_ptr(current, new_mask); ret = pr->throttling.acpi_processor_get_throttling(pr); /* restore the previous state */ set_cpus_allowed_ptr(current, &saved_mask); @@ -987,6 +989,7 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, int acpi_processor_set_throttling(struct acpi_processor *pr, int state) { cpumask_t saved_mask; + cpumask_of_cpu_ptr_declare(new_mask); int ret = 0; unsigned int i; struct acpi_processor *match_pr; @@ -1025,7 +1028,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) * it can be called only for the cpu pointed by pr. */ if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pr->id)); + cpumask_of_cpu_ptr_next(new_mask, pr->id); + set_cpus_allowed_ptr(current, new_mask); ret = p_throttling->acpi_processor_set_throttling(pr, t_state.target_state); } else { @@ -1056,7 +1060,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) continue; } t_state.cpu = i; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(i)); + cpumask_of_cpu_ptr_next(new_mask, i); + set_cpus_allowed_ptr(current, new_mask); ret = match_pr->throttling. acpi_processor_set_throttling( match_pr, t_state.target_state); diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 25918f7dfd0f..0b624e927a6f 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -254,6 +254,7 @@ static ssize_t host_control_on_shutdown_store(struct device *dev, static int smi_request(struct smi_cmd *smi_cmd) { cpumask_t old_mask; + cpumask_of_cpu_ptr(new_mask, 0); int ret = 0; if (smi_cmd->magic != SMI_CMD_MAGIC) { @@ -264,7 +265,7 @@ static int smi_request(struct smi_cmd *smi_cmd) /* SMI requires CPU 0 */ old_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(0)); + set_cpus_allowed_ptr(current, new_mask); if (smp_processor_id() != 0) { dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", __func__); diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 80226e776143..2dbd9a287e77 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -62,6 +62,15 @@ * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids * * cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set + *ifdef CONFIG_HAS_CPUMASK_OF_CPU + * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t *v + * cpumask_of_cpu_ptr_next(v, cpu) Sets v = &cpumask_of_cpu_map[cpu] + * cpumask_of_cpu_ptr(v, cpu) Combines above two operations + *else + * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t _v and *v = &_v + * cpumask_of_cpu_ptr_next(v, cpu) Sets _v = cpumask_of_cpu(cpu) + * cpumask_of_cpu_ptr(v, cpu) Combines above two operations + *endif * CPU_MASK_ALL Initializer - all bits set * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask @@ -236,11 +245,16 @@ static inline void __cpus_shift_left(cpumask_t *dstp, #ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP extern cpumask_t *cpumask_of_cpu_map; -#define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) - +#define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) +#define cpumask_of_cpu_ptr(v, cpu) \ + const cpumask_t *v = &cpumask_of_cpu(cpu) +#define cpumask_of_cpu_ptr_declare(v) \ + const cpumask_t *v +#define cpumask_of_cpu_ptr_next(v, cpu) \ + v = &cpumask_of_cpu(cpu) #else #define cpumask_of_cpu(cpu) \ -(*({ \ +({ \ typeof(_unused_cpumask_arg_) m; \ if (sizeof(m) == sizeof(unsigned long)) { \ m.bits[0] = 1UL<<(cpu); \ @@ -248,8 +262,16 @@ extern cpumask_t *cpumask_of_cpu_map; cpus_clear(m); \ cpu_set((cpu), m); \ } \ - &m; \ -})) + m; \ +}) +#define cpumask_of_cpu_ptr(v, cpu) \ + cpumask_t _##v = cpumask_of_cpu(cpu); \ + const cpumask_t *v = &_##v +#define cpumask_of_cpu_ptr_declare(v) \ + cpumask_t _##v; \ + const cpumask_t *v = &_##v +#define cpumask_of_cpu_ptr_next(v, cpu) \ + _##v = cpumask_of_cpu(cpu) #endif #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index ba9b2054ecbd..738b411ff2d3 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -33,8 +33,9 @@ static int stopmachine(void *cpu) { int irqs_disabled = 0; int prepared = 0; + cpumask_of_cpu_ptr(cpumask, (int)(long)cpu); - set_cpus_allowed_ptr(current, &cpumask_of_cpu((int)(long)cpu)); + set_cpus_allowed_ptr(current, cpumask); /* Ack: we are alive */ smp_mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ diff --git a/kernel/trace/trace_sysprof.c b/kernel/trace/trace_sysprof.c index 2301e1e7c606..63528086337c 100644 --- a/kernel/trace/trace_sysprof.c +++ b/kernel/trace/trace_sysprof.c @@ -213,7 +213,9 @@ static void start_stack_timers(void) int cpu; for_each_online_cpu(cpu) { - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + cpumask_of_cpu_ptr(new_mask, cpu); + + set_cpus_allowed_ptr(current, new_mask); start_stack_timer(cpu); } set_cpus_allowed_ptr(current, &saved_mask); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index d43cf8ddff67..083d12688134 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -314,9 +314,10 @@ svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) case SVC_POOL_PERCPU: { unsigned int cpu = m->pool_to[pidx]; + cpumask_of_cpu_ptr(cpumask, cpu); *oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, cpumask); return 1; } case SVC_POOL_PERNODE: -- cgit v1.2.3 From 77586c2bdad0798cb24e35de5a878e7c6b200574 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Tue, 15 Jul 2008 14:14:36 -0700 Subject: cpumask: Provide a generic set of CPUMASK_ALLOC macros * Provide a generic set of CPUMASK_ALLOC macros patterned after the SCHED_CPUMASK_ALLOC macros. This is used where multiple cpumask_t variables are declared on the stack to reduce the amount of stack space required. Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 2dbd9a287e77..72f9c32c12b0 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -75,6 +75,17 @@ * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask * + *if NR_CPUS > BITS_PER_LONG + * CPUMASK_ALLOC(m) Declares and allocates struct m *m = + * (struct m *)kmalloc(sizeof(*m), ...) + * CPUMASK_FREE(m) Macro for kfree(v) + *else + * CPUMASK_ALLOC(m) Declares struct m _m, *m = &_m + * CPUMASK_FREE(m) Nop + *endif + * CPUMASK_VAR(v, m) Declares cpumask_t *v = + * m + offset(struct m, v) + * * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing * int cpumask_parse_user(ubuf, ulen, mask) Parse ascii string as cpumask * int cpulist_scnprintf(buf, len, mask) Format cpumask as list for printing @@ -311,6 +322,16 @@ extern cpumask_t cpu_mask_all; #define cpus_addr(src) ((src).bits) +#if NR_CPUS > BITS_PER_LONG +#define CPUMASK_ALLOC(m) struct m *m = kmalloc(sizeof(*m), GFP_KERNEL) +#define CPUMASK_FREE(m) kfree(m) +#else +#define CPUMASK_ALLOC(m) struct allmasks _m, *m = &_m +#define CPUMASK_FREE(m) +#endif +#define CPUMASK_VAR(v, m) cpumask_t *v = (cpumask_t *) \ + ((unsigned long)(m) + offsetof(struct m, v)) + #define cpumask_scnprintf(buf, len, src) \ __cpumask_scnprintf((buf), (len), &(src), NR_CPUS) static inline int __cpumask_scnprintf(char *buf, int len, -- cgit v1.2.3 From ae79cdaacb5599781f8bb49f4bdd5723029669cf Mon Sep 17 00:00:00 2001 From: "venkatesh.pallipadi@intel.com" Date: Fri, 18 Jul 2008 16:08:13 -0700 Subject: x86: Add a arch directory for x86 under debugfs Add a directory for x86 arch under debugfs. Can be used to accumulate all x86 specific debugfs files. Signed-off-by: Venkatesh Pallipadi Signed-off-by: H. Peter Anvin --- arch/x86/kernel/kdebugfs.c | 8 ++++++++ include/linux/debugfs.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c index c03205991718..f2d43bc75514 100644 --- a/arch/x86/kernel/kdebugfs.c +++ b/arch/x86/kernel/kdebugfs.c @@ -12,9 +12,13 @@ #include #include #include +#include #include +struct dentry *arch_debugfs_dir; +EXPORT_SYMBOL(arch_debugfs_dir); + #ifdef CONFIG_DEBUG_BOOT_PARAMS struct setup_data_node { u64 paddr; @@ -209,6 +213,10 @@ static int __init arch_kdebugfs_init(void) { int error = 0; + arch_debugfs_dir = debugfs_create_dir("x86", NULL); + if (!arch_debugfs_dir) + return -ENOMEM; + #ifdef CONFIG_DEBUG_BOOT_PARAMS error = boot_params_kdebugfs_init(); #endif diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 7266124361b4..32755cdf68db 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -26,6 +26,8 @@ struct debugfs_blob_wrapper { unsigned long size; }; +extern struct dentry *arch_debugfs_dir; + #if defined(CONFIG_DEBUG_FS) /* declared over in file.c */ -- cgit v1.2.3 From 8913336a7e8d56e984109a3137d6c0e3362596a4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 18 Jul 2008 18:05:19 -0700 Subject: packet: add PACKET_RESERVE sockopt Add new sockopt to reserve some headroom in the mmaped ring frames in front of the packet payload. This can be used f.i. when the VLAN header needs to be (re)constructed to avoid moving the entire payload. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 1 + net/packet/af_packet.c | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index a630295b255f..18db0668065a 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -45,6 +45,7 @@ struct sockaddr_ll #define PACKET_ORIGDEV 9 #define PACKET_VERSION 10 #define PACKET_HDRLEN 11 +#define PACKET_RESERVE 12 struct tpacket_stats { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index db792e02a37f..de73bcb5235a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -188,6 +188,7 @@ struct packet_sock { unsigned int pg_vec_len; enum tpacket_versions tp_version; unsigned int tp_hdrlen; + unsigned int tp_reserve; #endif }; @@ -635,11 +636,13 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe snaplen = res; if (sk->sk_type == SOCK_DGRAM) { - macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16; + macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16 + + po->tp_reserve; } else { unsigned maclen = skb_network_offset(skb); netoff = TPACKET_ALIGN(po->tp_hdrlen + - (maclen < 16 ? 16 : maclen)); + (maclen < 16 ? 16 : maclen)) + + po->tp_reserve; macoff = netoff - maclen; } @@ -1448,6 +1451,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return -EINVAL; } } + case PACKET_RESERVE: + { + unsigned int val; + + if (optlen != sizeof(val)) + return -EINVAL; + if (po->pg_vec) + return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + po->tp_reserve = val; + return 0; + } #endif case PACKET_AUXDATA: { @@ -1547,6 +1563,12 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, } data = &val; break; + case PACKET_RESERVE: + if (len > sizeof(unsigned int)) + len = sizeof(unsigned int); + val = po->tp_reserve; + data = &val; + break; #endif default: return -ENOPROTOOPT; @@ -1790,7 +1812,8 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing return -EINVAL; if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) return -EINVAL; - if (unlikely(req->tp_frame_size < po->tp_hdrlen)) + if (unlikely(req->tp_frame_size < po->tp_hdrlen + + po->tp_reserve)) return -EINVAL; if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1))) return -EINVAL; -- cgit v1.2.3 From 5ec461d083066441c1a280aa9e13d3bb5c3e008a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Jul 2008 00:40:25 -0400 Subject: Input: add microphone insert switch definition Add a new switch type to the input API for reporting microphone insertion. This will be used by the ALSA jack reporting API. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index 3e86b61d2e4c..1be28cbbc57b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -642,6 +642,7 @@ struct input_absinfo { #define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */ #define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ #define SW_MAX 0x0f #define SW_CNT (SW_MAX+1) -- cgit v1.2.3 From 92c49890922d54cba4b1eadeb0b185773c2c9570 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 19 Jul 2008 00:43:54 -0400 Subject: Input: add switch for dock events Add a SW_DOCK switch to input.h. ACPI docks currently send their docking status as a uevent, but not all docks are ACPI or correspond to a device. In that case, it makes more sense to simply generate an input event on docking or undocking. Signed-off-by: Matthew Garrett Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index 1be28cbbc57b..feaea76f0ef3 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -643,6 +643,7 @@ struct input_absinfo { set = radio enabled */ #define SW_RADIO SW_RFKILL_ALL /* deprecated */ #define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ +#define SW_DOCK 0x05 /* set = plugged into dock */ #define SW_MAX 0x0f #define SW_CNT (SW_MAX+1) -- cgit v1.2.3 From 3072367300aa8c779e3a14ee8e89de079e90f3ad Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 18 Jul 2008 22:50:15 -0700 Subject: pkt_sched: Manage qdisc list inside of root qdisc. Idea is from Patrick McHardy. Instead of managing the list of qdiscs on the device level, manage it in the root qdisc of a netdev_queue. This solves all kinds of visibility issues during qdisc destruction. The way to iterate over all qdiscs of a netdev_queue is to visit the netdev_queue->qdisc, and then traverse it's list. The only special case is to ignore builting qdiscs at the root when dumping or doing a qdisc_lookup(). That was not needed previously because builtin qdiscs were not added to the device's qdisc_list. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 - net/core/dev.c | 2 - net/sched/sch_api.c | 175 ++++++++++++++++++++++++++++++++++------------ net/sched/sch_generic.c | 10 +-- 4 files changed, 133 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9c5a68850114..812bcd8b4363 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -636,8 +636,6 @@ struct net_device unsigned int real_num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ - spinlock_t qdisc_list_lock; - struct list_head qdisc_list; /* * One part is mostly used on xmit path (device) diff --git a/net/core/dev.c b/net/core/dev.c index e54acde839da..065b9817e209 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3888,8 +3888,6 @@ int register_netdevice(struct net_device *dev) net = dev_net(dev); spin_lock_init(&dev->addr_list_lock); - spin_lock_init(&dev->qdisc_list_lock); - INIT_LIST_HEAD(&dev->qdisc_list); netdev_init_queue_locks(dev); dev->iflink = -1; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index b3ef8307204e..fb43731c9860 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -185,11 +185,20 @@ EXPORT_SYMBOL(unregister_qdisc); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { - struct Qdisc *q; + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + struct Qdisc *q, *txq_root = txq->qdisc; - list_for_each_entry(q, &dev->qdisc_list, list) { - if (q->handle == handle) - return q; + if (!(txq_root->flags & TCQ_F_BUILTIN) && + txq_root->handle == handle) + return txq_root; + + list_for_each_entry(q, &txq_root->list, list) { + if (q->handle == handle) + return q; + } } return NULL; } @@ -676,9 +685,8 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, goto err_out3; } } - spin_lock_bh(&dev->qdisc_list_lock); - list_add_tail(&sch->list, &dev->qdisc_list); - spin_unlock_bh(&dev->qdisc_list_lock); + if (parent) + list_add_tail(&sch->list, &dev_queue->qdisc->list); return sch; } @@ -1037,13 +1045,57 @@ err_out: return -EINVAL; } +static bool tc_qdisc_dump_ignore(struct Qdisc *q) +{ + return (q->flags & TCQ_F_BUILTIN) ? true : false; +} + +static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, + struct netlink_callback *cb, + int *q_idx_p, int s_q_idx) +{ + int ret = 0, q_idx = *q_idx_p; + struct Qdisc *q; + + if (!root) + return 0; + + q = root; + if (q_idx < s_q_idx) { + q_idx++; + } else { + if (!tc_qdisc_dump_ignore(q) && + tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) + goto done; + q_idx++; + } + list_for_each_entry(q, &root->list, list) { + if (q_idx < s_q_idx) { + q_idx++; + continue; + } + if (!tc_qdisc_dump_ignore(q) && + tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) + goto done; + q_idx++; + } + +out: + *q_idx_p = q_idx; + return ret; +done: + ret = -1; + goto out; +} + static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); int idx, q_idx; int s_idx, s_q_idx; struct net_device *dev; - struct Qdisc *q; if (net != &init_net) return 0; @@ -1053,21 +1105,22 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { + struct netdev_queue *dev_queue; + if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { - if (q_idx < s_q_idx) { - q_idx++; - continue; - } - if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) - goto done; - q_idx++; - } + + dev_queue = netdev_get_tx_queue(dev, 0); + if (tc_dump_qdisc_root(dev_queue->qdisc, skb, cb, &q_idx, s_q_idx) < 0) + goto done; + + dev_queue = &dev->rx_queue; + if (tc_dump_qdisc_root(dev_queue->qdisc, skb, cb, &q_idx, s_q_idx) < 0) + goto done; + cont: idx++; } @@ -1285,15 +1338,62 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS); } +static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, + struct tcmsg *tcm, struct netlink_callback *cb, + int *t_p, int s_t) +{ + struct qdisc_dump_args arg; + + if (tc_qdisc_dump_ignore(q) || + *t_p < s_t || !q->ops->cl_ops || + (tcm->tcm_parent && + TC_H_MAJ(tcm->tcm_parent) != q->handle)) { + (*t_p)++; + return 0; + } + if (*t_p > s_t) + memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); + arg.w.fn = qdisc_class_dump; + arg.skb = skb; + arg.cb = cb; + arg.w.stop = 0; + arg.w.skip = cb->args[1]; + arg.w.count = 0; + q->ops->cl_ops->walk(q, &arg.w); + cb->args[1] = arg.w.count; + if (arg.w.stop) + return -1; + (*t_p)++; + return 0; +} + +static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb, + struct tcmsg *tcm, struct netlink_callback *cb, + int *t_p, int s_t) +{ + struct Qdisc *q; + + if (!root) + return 0; + + if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0) + return -1; + + list_for_each_entry(q, &root->list, list) { + if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0) + return -1; + } + + return 0; +} + static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) { + struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); struct net *net = sock_net(skb->sk); - int t; - int s_t; + struct netdev_queue *dev_queue; struct net_device *dev; - struct Qdisc *q; - struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); - struct qdisc_dump_args arg; + int t, s_t; if (net != &init_net) return 0; @@ -1306,28 +1406,15 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { - if (t < s_t || !q->ops->cl_ops || - (tcm->tcm_parent && - TC_H_MAJ(tcm->tcm_parent) != q->handle)) { - t++; - continue; - } - if (t > s_t) - memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); - arg.w.fn = qdisc_class_dump; - arg.skb = skb; - arg.cb = cb; - arg.w.stop = 0; - arg.w.skip = cb->args[1]; - arg.w.count = 0; - q->ops->cl_ops->walk(q, &arg.w); - cb->args[1] = arg.w.count; - if (arg.w.stop) - break; - t++; - } + dev_queue = netdev_get_tx_queue(dev, 0); + if (tc_dump_tclass_root(dev_queue->qdisc, skb, tcm, cb, &t, s_t) < 0) + goto done; + + dev_queue = &dev->rx_queue; + if (tc_dump_tclass_root(dev_queue->qdisc, skb, tcm, cb, &t, s_t) < 0) + goto done; +done: cb->args[0] = t; dev_put(dev); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index e244c462e6bd..14cc443d0490 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -480,15 +480,12 @@ static void __qdisc_destroy(struct rcu_head *head) void qdisc_destroy(struct Qdisc *qdisc) { - struct net_device *dev = qdisc_dev(qdisc); - if (qdisc->flags & TCQ_F_BUILTIN || !atomic_dec_and_test(&qdisc->refcnt)) return; - spin_lock_bh(&dev->qdisc_list_lock); - list_del(&qdisc->list); - spin_unlock_bh(&dev->qdisc_list_lock); + if (qdisc->parent) + list_del(&qdisc->list); call_rcu(&qdisc->q_rcu, __qdisc_destroy); } @@ -520,9 +517,6 @@ static void attach_one_default_qdisc(struct net_device *dev, printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - spin_lock_bh(&dev->qdisc_list_lock); - list_add_tail(&qdisc->list, &dev->qdisc_list); - spin_unlock_bh(&dev->qdisc_list_lock); } else { qdisc = &noqueue_qdisc; } -- cgit v1.2.3 From 4389dded7767d24290463f2a8302ba3253ebdd56 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Sat, 19 Jul 2008 00:07:02 -0700 Subject: tcp: Remove redundant checks when setting eff_sacks Remove redundant checks when setting eff_sacks and make the number of SACKs a compile time constant. Now that the options code knows how many SACK blocks can fit in the header, we don't need to have the SACK code guessing at it. Signed-off-by: Adam Langley Signed-off-by: David S. Miller --- include/linux/tcp.h | 6 ++++++ net/ipv4/tcp_input.c | 25 ++++++++++--------------- 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 07e79bdb9cdf..2e2557388e36 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -224,6 +224,12 @@ struct tcp_options_received { u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ }; +/* This is the max number of SACKS that we'll generate and process. It's safe + * to increse this, although since: + * size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8) + * only four options will fit in a standard TCP header */ +#define TCP_NUM_SACKS 4 + struct tcp_request_sock { struct inet_request_sock req; #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 88810bc01370..1f5e6049883e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1423,10 +1423,10 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, unsigned char *ptr = (skb_transport_header(ack_skb) + TCP_SKB_CB(ack_skb)->sacked); struct tcp_sack_block_wire *sp_wire = (struct tcp_sack_block_wire *)(ptr+2); - struct tcp_sack_block sp[4]; + struct tcp_sack_block sp[TCP_NUM_SACKS]; struct tcp_sack_block *cache; struct sk_buff *skb; - int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE) >> 3; + int num_sacks = min(TCP_NUM_SACKS, (ptr[1] - TCPOLEN_SACK_BASE) >> 3); int used_sacks; int reord = tp->packets_out; int flag = 0; @@ -3735,8 +3735,7 @@ static void tcp_dsack_set(struct sock *sk, u32 seq, u32 end_seq) tp->rx_opt.dsack = 1; tp->duplicate_sack[0].start_seq = seq; tp->duplicate_sack[0].end_seq = end_seq; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + 1, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + 1; } } @@ -3791,9 +3790,8 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp) * Decrease num_sacks. */ tp->rx_opt.num_sacks--; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + - tp->rx_opt.dsack, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + + tp->rx_opt.dsack; for (i = this_sack; i < tp->rx_opt.num_sacks; i++) sp[i] = sp[i + 1]; continue; @@ -3843,7 +3841,7 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq) * * If the sack array is full, forget about the last one. */ - if (this_sack >= 4) { + if (this_sack >= TCP_NUM_SACKS) { this_sack--; tp->rx_opt.num_sacks--; sp--; @@ -3856,8 +3854,7 @@ new_sack: sp->start_seq = seq; sp->end_seq = end_seq; tp->rx_opt.num_sacks++; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + tp->rx_opt.dsack, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; } /* RCV.NXT advances, some SACKs should be eaten. */ @@ -3894,9 +3891,8 @@ static void tcp_sack_remove(struct tcp_sock *tp) } if (num_sacks != tp->rx_opt.num_sacks) { tp->rx_opt.num_sacks = num_sacks; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + - tp->rx_opt.dsack, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + + tp->rx_opt.dsack; } } @@ -3975,8 +3971,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (tp->rx_opt.dsack) { tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = min_t(unsigned int, tp->rx_opt.num_sacks, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks; } /* Queue data for delivery to the user. -- cgit v1.2.3 From 230b183921ecbaa5fedc0d35ad6ba7bb64b6e06a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Sat, 19 Jul 2008 22:35:47 -0700 Subject: net: Use standard structures for generic socket address structures. Use sockaddr_storage{} for generic socket address storage and ensures proper alignment. Use sockaddr{} for pointers to omit several casts. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/linux/socket.h | 6 ++-- include/net/compat.h | 2 +- net/compat.c | 2 +- net/core/iovec.c | 2 +- net/socket.c | 82 ++++++++++++++++++++++++++++---------------------- 5 files changed, 52 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/include/linux/socket.h b/include/linux/socket.h index bd2b30a74e76..950af631e7fb 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -306,10 +306,10 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata, int offset, unsigned int len, __wsum *csump); -extern int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode); +extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode); extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len); -extern int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, int __user *ulen); -extern int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr); +extern int move_addr_to_user(struct sockaddr *kaddr, int klen, void __user *uaddr, int __user *ulen); +extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr); extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); #endif diff --git a/include/net/compat.h b/include/net/compat.h index 164cb682e220..5bbf8bf9efea 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -32,7 +32,7 @@ extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); #endif /* defined(CONFIG_COMPAT) */ extern int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *); -extern int verify_compat_iovec(struct msghdr *, struct iovec *, char *, int); +extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr *, int); extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); diff --git a/net/compat.c b/net/compat.c index c823f6f290cb..6e1b03b51933 100644 --- a/net/compat.c +++ b/net/compat.c @@ -75,7 +75,7 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) /* I've named the args so it is easy to tell whose space the pointers are in. */ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, - char *kern_address, int mode) + struct sockaddr *kern_address, int mode) { int tot_len; diff --git a/net/core/iovec.c b/net/core/iovec.c index 755c37fdaee7..4c9c0121c9da 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -36,7 +36,7 @@ * in any case. */ -int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode) +int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode) { int size, err, ct; diff --git a/net/socket.c b/net/socket.c index 81fe82513046..1ba57d888981 100644 --- a/net/socket.c +++ b/net/socket.c @@ -180,9 +180,9 @@ static DEFINE_PER_CPU(int, sockets_in_use) = 0; * invalid addresses -EFAULT is returned. On a success 0 is returned. */ -int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr) +int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr) { - if (ulen < 0 || ulen > MAX_SOCK_ADDR) + if (ulen < 0 || ulen > sizeof(struct sockaddr_storage)) return -EINVAL; if (ulen == 0) return 0; @@ -208,7 +208,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr) * specified. Zero is returned for a success. */ -int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, +int move_addr_to_user(struct sockaddr *kaddr, int klen, void __user *uaddr, int __user *ulen) { int err; @@ -219,7 +219,7 @@ int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, return err; if (len > klen) len = klen; - if (len < 0 || len > MAX_SOCK_ADDR) + if (len < 0 || len > sizeof(struct sockaddr_storage)) return -EINVAL; if (len) { if (audit_sockaddr(klen, kaddr)) @@ -1342,20 +1342,20 @@ out_fd: asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { - err = move_addr_to_kernel(umyaddr, addrlen, address); + err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address); if (err >= 0) { err = security_socket_bind(sock, - (struct sockaddr *)address, + (struct sockaddr *)&address, addrlen); if (!err) err = sock->ops->bind(sock, (struct sockaddr *) - address, addrlen); + &address, addrlen); } fput_light(sock->file, fput_needed); } @@ -1407,7 +1407,7 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, struct socket *sock, *newsock; struct file *newfile; int err, len, newfd, fput_needed; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) @@ -1446,13 +1446,13 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, goto out_fd; if (upeer_sockaddr) { - if (newsock->ops->getname(newsock, (struct sockaddr *)address, + if (newsock->ops->getname(newsock, (struct sockaddr *)&address, &len, 2) < 0) { err = -ECONNABORTED; goto out_fd; } - err = move_addr_to_user(address, len, upeer_sockaddr, - upeer_addrlen); + err = move_addr_to_user((struct sockaddr *)&address, + len, upeer_sockaddr, upeer_addrlen); if (err < 0) goto out_fd; } @@ -1495,22 +1495,22 @@ asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; - err = move_addr_to_kernel(uservaddr, addrlen, address); + err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address); if (err < 0) goto out_put; err = - security_socket_connect(sock, (struct sockaddr *)address, addrlen); + security_socket_connect(sock, (struct sockaddr *)&address, addrlen); if (err) goto out_put; - err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, + err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen, sock->file->f_flags); out_put: fput_light(sock->file, fput_needed); @@ -1527,7 +1527,7 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int len, err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); @@ -1538,10 +1538,10 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, if (err) goto out_put; - err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 0); + err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0); if (err) goto out_put; - err = move_addr_to_user(address, len, usockaddr, usockaddr_len); + err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len); out_put: fput_light(sock->file, fput_needed); @@ -1558,7 +1558,7 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int len, err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); @@ -1570,10 +1570,10 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, } err = - sock->ops->getname(sock, (struct sockaddr *)address, &len, + sock->ops->getname(sock, (struct sockaddr *)&address, &len, 1); if (!err) - err = move_addr_to_user(address, len, usockaddr, + err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len); fput_light(sock->file, fput_needed); } @@ -1591,7 +1591,7 @@ asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, int addr_len) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err; struct msghdr msg; struct iovec iov; @@ -1610,10 +1610,10 @@ asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, msg.msg_controllen = 0; msg.msg_namelen = 0; if (addr) { - err = move_addr_to_kernel(addr, addr_len, address); + err = move_addr_to_kernel(addr, addr_len, (struct sockaddr *)&address); if (err < 0) goto out_put; - msg.msg_name = address; + msg.msg_name = (struct sockaddr *)&address; msg.msg_namelen = addr_len; } if (sock->file->f_flags & O_NONBLOCK) @@ -1649,7 +1649,7 @@ asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size, struct socket *sock; struct iovec iov; struct msghdr msg; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err, err2; int fput_needed; @@ -1663,14 +1663,15 @@ asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size, msg.msg_iov = &iov; iov.iov_len = size; iov.iov_base = ubuf; - msg.msg_name = address; - msg.msg_namelen = MAX_SOCK_ADDR; + msg.msg_name = (struct sockaddr *)&address; + msg.msg_namelen = sizeof(address); if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; err = sock_recvmsg(sock, &msg, size, flags); if (err >= 0 && addr != NULL) { - err2 = move_addr_to_user(address, msg.msg_namelen, addr, addr_len); + err2 = move_addr_to_user((struct sockaddr *)&address, + msg.msg_namelen, addr, addr_len); if (err2 < 0) err = err2; } @@ -1790,7 +1791,7 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg; struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; unsigned char ctl[sizeof(struct cmsghdr) + 20] __attribute__ ((aligned(sizeof(__kernel_size_t)))); @@ -1828,9 +1829,13 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) /* This will also move the address data into kernel space */ if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(&msg_sys, iov, address, VERIFY_READ); + err = verify_compat_iovec(&msg_sys, iov, + (struct sockaddr *)&address, + VERIFY_READ); } else - err = verify_iovec(&msg_sys, iov, address, VERIFY_READ); + err = verify_iovec(&msg_sys, iov, + (struct sockaddr *)&address, + VERIFY_READ); if (err < 0) goto out_freeiov; total_len = err; @@ -1901,7 +1906,7 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, int fput_needed; /* kernel mode address */ - char addr[MAX_SOCK_ADDR]; + struct sockaddr_storage addr; /* user mode address pointers */ struct sockaddr __user *uaddr; @@ -1939,9 +1944,13 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, uaddr = (__force void __user *)msg_sys.msg_name; uaddr_len = COMPAT_NAMELEN(msg); if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(&msg_sys, iov, addr, VERIFY_WRITE); + err = verify_compat_iovec(&msg_sys, iov, + (struct sockaddr *)&addr, + VERIFY_WRITE); } else - err = verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE); + err = verify_iovec(&msg_sys, iov, + (struct sockaddr *)&addr, + VERIFY_WRITE); if (err < 0) goto out_freeiov; total_len = err; @@ -1957,7 +1966,8 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, len = err; if (uaddr != NULL) { - err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, + err = move_addr_to_user((struct sockaddr *)&addr, + msg_sys.msg_namelen, uaddr, uaddr_len); if (err < 0) goto out_freeiov; -- cgit v1.2.3 From 175f9c1bba9b825d22b142d183c9e175488b260c Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sun, 20 Jul 2008 00:08:47 -0700 Subject: net_sched: Add size table for qdiscs Add size table functions for qdiscs and calculate packet size in qdisc_enqueue(). Based on patch by Patrick McHardy http://marc.info/?l=linux-netdev&m=115201979221729&w=2 Signed-off-by: Jussi Kivilinna Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 20 ++++++ include/linux/rtnetlink.h | 1 + include/net/pkt_sched.h | 1 + include/net/sch_generic.h | 25 +++++++- net/sched/sch_api.c | 151 +++++++++++++++++++++++++++++++++++++++++++++- net/sched/sch_generic.c | 1 + net/sched/sch_netem.c | 5 +- 7 files changed, 199 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 87f4e0fa8f27..e5de421ac7b4 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -85,6 +85,26 @@ struct tc_ratespec #define TC_RTAB_SIZE 1024 +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + /* FIFO section */ struct tc_fifo_qopt diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index b358c704d102..f4d386c191f5 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -482,6 +482,7 @@ enum TCA_RATE, TCA_FCNT, TCA_STATS2, + TCA_STAB, __TCA_MAX }; diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index e4e30052e4e2..6affcfaa123e 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -83,6 +83,7 @@ extern struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab); extern void qdisc_put_rtab(struct qdisc_rate_table *tab); +extern void qdisc_put_stab(struct qdisc_size_table *tab); extern void __qdisc_run(struct Qdisc *q); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 8229520e088a..db9ad655eb8a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -29,6 +29,13 @@ enum qdisc_state_t __QDISC_STATE_SCHED, }; +struct qdisc_size_table { + struct list_head list; + struct tc_sizespec szopts; + int refcnt; + u16 data[]; +}; + struct Qdisc { int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev); @@ -39,6 +46,7 @@ struct Qdisc #define TCQ_F_INGRESS 4 int padded; struct Qdisc_ops *ops; + struct qdisc_size_table *stab; u32 handle; u32 parent; atomic_t refcnt; @@ -165,6 +173,16 @@ struct tcf_proto struct tcf_proto_ops *ops; }; +struct qdisc_skb_cb { + unsigned int pkt_len; + char data[]; +}; + +static inline struct qdisc_skb_cb *qdisc_skb_cb(struct sk_buff *skb) +{ + return (struct qdisc_skb_cb *)skb->cb; +} + static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc) { return &qdisc->q.lock; @@ -257,6 +275,8 @@ extern struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, extern struct Qdisc *qdisc_create_dflt(struct net_device *dev, struct netdev_queue *dev_queue, struct Qdisc_ops *ops, u32 parentid); +extern void qdisc_calculate_pkt_len(struct sk_buff *skb, + struct qdisc_size_table *stab); extern void tcf_destroy(struct tcf_proto *tp); extern void tcf_destroy_chain(struct tcf_proto **fl); @@ -308,16 +328,19 @@ static inline bool qdisc_tx_is_noop(const struct net_device *dev) static inline unsigned int qdisc_pkt_len(struct sk_buff *skb) { - return skb->len; + return qdisc_skb_cb(skb)->pkt_len; } static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) { + if (sch->stab) + qdisc_calculate_pkt_len(skb, sch->stab); return sch->enqueue(skb, sch); } static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch) { + qdisc_skb_cb(skb)->pkt_len = skb->len; return qdisc_enqueue(skb, sch); } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index fb43731c9860..5219d5f9d754 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -286,6 +286,129 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab) } EXPORT_SYMBOL(qdisc_put_rtab); +static LIST_HEAD(qdisc_stab_list); +static DEFINE_SPINLOCK(qdisc_stab_lock); + +static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = { + [TCA_STAB_BASE] = { .len = sizeof(struct tc_sizespec) }, + [TCA_STAB_DATA] = { .type = NLA_BINARY }, +}; + +static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt) +{ + struct nlattr *tb[TCA_STAB_MAX + 1]; + struct qdisc_size_table *stab; + struct tc_sizespec *s; + unsigned int tsize = 0; + u16 *tab = NULL; + int err; + + err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy); + if (err < 0) + return ERR_PTR(err); + if (!tb[TCA_STAB_BASE]) + return ERR_PTR(-EINVAL); + + s = nla_data(tb[TCA_STAB_BASE]); + + if (s->tsize > 0) { + if (!tb[TCA_STAB_DATA]) + return ERR_PTR(-EINVAL); + tab = nla_data(tb[TCA_STAB_DATA]); + tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16); + } + + if (!s || tsize != s->tsize || (!tab && tsize > 0)) + return ERR_PTR(-EINVAL); + + spin_lock(&qdisc_stab_lock); + + list_for_each_entry(stab, &qdisc_stab_list, list) { + if (memcmp(&stab->szopts, s, sizeof(*s))) + continue; + if (tsize > 0 && memcmp(stab->data, tab, tsize * sizeof(u16))) + continue; + stab->refcnt++; + spin_unlock(&qdisc_stab_lock); + return stab; + } + + spin_unlock(&qdisc_stab_lock); + + stab = kmalloc(sizeof(*stab) + tsize * sizeof(u16), GFP_KERNEL); + if (!stab) + return ERR_PTR(-ENOMEM); + + stab->refcnt = 1; + stab->szopts = *s; + if (tsize > 0) + memcpy(stab->data, tab, tsize * sizeof(u16)); + + spin_lock(&qdisc_stab_lock); + list_add_tail(&stab->list, &qdisc_stab_list); + spin_unlock(&qdisc_stab_lock); + + return stab; +} + +void qdisc_put_stab(struct qdisc_size_table *tab) +{ + if (!tab) + return; + + spin_lock(&qdisc_stab_lock); + + if (--tab->refcnt == 0) { + list_del(&tab->list); + kfree(tab); + } + + spin_unlock(&qdisc_stab_lock); +} +EXPORT_SYMBOL(qdisc_put_stab); + +static int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, TCA_STAB); + NLA_PUT(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts); + nla_nest_end(skb, nest); + + return skb->len; + +nla_put_failure: + return -1; +} + +void qdisc_calculate_pkt_len(struct sk_buff *skb, struct qdisc_size_table *stab) +{ + int pkt_len, slot; + + pkt_len = skb->len + stab->szopts.overhead; + if (unlikely(!stab->szopts.tsize)) + goto out; + + slot = pkt_len + stab->szopts.cell_align; + if (unlikely(slot < 0)) + slot = 0; + + slot >>= stab->szopts.cell_log; + if (likely(slot < stab->szopts.tsize)) + pkt_len = stab->data[slot]; + else + pkt_len = stab->data[stab->szopts.tsize - 1] * + (slot / stab->szopts.tsize) + + stab->data[slot % stab->szopts.tsize]; + + pkt_len <<= stab->szopts.size_log; +out: + if (unlikely(pkt_len < 1)) + pkt_len = 1; + qdisc_skb_cb(skb)->pkt_len = pkt_len; +} +EXPORT_SYMBOL(qdisc_calculate_pkt_len); + static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, @@ -613,6 +736,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, struct nlattr *kind = tca[TCA_KIND]; struct Qdisc *sch; struct Qdisc_ops *ops; + struct qdisc_size_table *stab; ops = qdisc_lookup_ops(kind); #ifdef CONFIG_KMOD @@ -670,6 +794,14 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, sch->handle = handle; if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) { + if (tca[TCA_STAB]) { + stab = qdisc_get_stab(tca[TCA_STAB]); + if (IS_ERR(stab)) { + err = PTR_ERR(stab); + goto err_out3; + } + sch->stab = stab; + } if (tca[TCA_RATE]) { err = gen_new_estimator(&sch->bstats, &sch->rate_est, qdisc_root_lock(sch), @@ -691,6 +823,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, return sch; } err_out3: + qdisc_put_stab(sch->stab); dev_put(dev); kfree((char *) sch - sch->padded); err_out2: @@ -702,15 +835,26 @@ err_out: static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) { - if (tca[TCA_OPTIONS]) { - int err; + struct qdisc_size_table *stab = NULL; + int err = 0; + if (tca[TCA_OPTIONS]) { if (sch->ops->change == NULL) return -EINVAL; err = sch->ops->change(sch, tca[TCA_OPTIONS]); if (err) return err; } + + if (tca[TCA_STAB]) { + stab = qdisc_get_stab(tca[TCA_STAB]); + if (IS_ERR(stab)) + return PTR_ERR(stab); + } + + qdisc_put_stab(sch->stab); + sch->stab = stab; + if (tca[TCA_RATE]) gen_replace_estimator(&sch->bstats, &sch->rate_est, qdisc_root_lock(sch), tca[TCA_RATE]); @@ -994,6 +1138,9 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, goto nla_put_failure; q->qstats.qlen = q->q.qlen; + if (q->stab && qdisc_dump_stab(skb, q->stab) < 0) + goto nla_put_failure; + if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, qdisc_root_lock(q), &d) < 0) goto nla_put_failure; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 522a41a9f904..27a51f04db49 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -469,6 +469,7 @@ static void __qdisc_destroy(struct rcu_head *head) struct Qdisc *qdisc = container_of(head, struct Qdisc, q_rcu); const struct Qdisc_ops *ops = qdisc->ops; + qdisc_put_stab(qdisc->stab); gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est); if (ops->reset) ops->reset(qdisc); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index ae49be00022f..a59085700678 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -84,8 +84,9 @@ struct netem_skb_cb { static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct netem_skb_cb)); - return (struct netem_skb_cb *)skb->cb; + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb)); + return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } /* init_crandom - initialize correlated random number generator -- cgit v1.2.3 From 80422d3431cc990b967da129f9eb8e3e9989f841 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Fri, 18 Jul 2008 18:11:33 -0700 Subject: cpumask: Provide a generic set of CPUMASK_ALLOC macros, FIXUP * Rename CPUMASK_VAR --> CPUMASK_PTR (and simplify) * Fix a semantic error in CPUMASK_ALLOC * Add a bit of commentry to cpumask.h Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 72f9c32c12b0..30d59d1d0626 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -75,16 +75,36 @@ * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask * + * CPUMASK_ALLOC kmalloc's a structure that is a composite of many cpumask_t + * variables, and CPUMASK_PTR provides pointers to each field. + * + * The structure should be defined something like this: + * struct my_cpumasks { + * cpumask_t mask1; + * cpumask_t mask2; + * }; + * + * Usage is then: + * CPUMASK_ALLOC(my_cpumasks); + * CPUMASK_PTR(mask1, my_cpumasks); + * CPUMASK_PTR(mask2, my_cpumasks); + * + * --- DO NOT reference cpumask_t pointers until this check --- + * if (my_cpumasks == NULL) + * "kmalloc failed"... + * + * References are now pointers to the cpumask_t variables (*mask1, ...) + * *if NR_CPUS > BITS_PER_LONG * CPUMASK_ALLOC(m) Declares and allocates struct m *m = - * (struct m *)kmalloc(sizeof(*m), ...) - * CPUMASK_FREE(m) Macro for kfree(v) + * kmalloc(sizeof(*m), GFP_KERNEL) + * CPUMASK_FREE(m) Macro for kfree(m) *else * CPUMASK_ALLOC(m) Declares struct m _m, *m = &_m * CPUMASK_FREE(m) Nop *endif - * CPUMASK_VAR(v, m) Declares cpumask_t *v = - * m + offset(struct m, v) + * CPUMASK_PTR(v, m) Declares cpumask_t *v = &(m->v) + * ------------------------------------------------------------------------ * * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing * int cpumask_parse_user(ubuf, ulen, mask) Parse ascii string as cpumask @@ -326,11 +346,10 @@ extern cpumask_t cpu_mask_all; #define CPUMASK_ALLOC(m) struct m *m = kmalloc(sizeof(*m), GFP_KERNEL) #define CPUMASK_FREE(m) kfree(m) #else -#define CPUMASK_ALLOC(m) struct allmasks _m, *m = &_m +#define CPUMASK_ALLOC(m) struct m _m, *m = &_m #define CPUMASK_FREE(m) #endif -#define CPUMASK_VAR(v, m) cpumask_t *v = (cpumask_t *) \ - ((unsigned long)(m) + offsetof(struct m, v)) +#define CPUMASK_PTR(v, m) cpumask_t *v = &(m->v) #define cpumask_scnprintf(buf, len, src) \ __cpumask_scnprintf((buf), (len), &(src), NR_CPUS) -- cgit v1.2.3 From 7cc8883074b040aa8c1ebd3a17463b0ea3a9ef16 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 13 May 2008 16:29:20 +0300 Subject: KVM: Remove decache_vcpus_on_cpu() and related callbacks Obsoleted by the vmx-specific per-cpu list. Signed-off-by: Avi Kivity --- arch/ia64/kvm/kvm-ia64.c | 8 -------- arch/powerpc/kvm/powerpc.c | 4 ---- arch/s390/kvm/kvm-s390.c | 4 ---- arch/x86/kvm/svm.c | 5 ----- arch/x86/kvm/vmx.c | 6 ------ arch/x86/kvm/x86.c | 8 -------- include/asm-x86/kvm_host.h | 1 - include/linux/kvm_host.h | 3 --- virt/kvm/kvm_main.c | 1 - 9 files changed, 40 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 68c978be9a51..7c504be57972 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1035,14 +1035,6 @@ static void kvm_free_vmm_area(void) } } -/* - * Make sure that a cpu that is being hot-unplugged does not have any vcpus - * cached on it. Leave it as blank for IA64. - */ -void decache_vcpus_on_cpu(int cpu) -{ -} - static void vti_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 777e0f34e0ea..0513b359851b 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -240,10 +240,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) { } -void decache_vcpus_on_cpu(int cpu) -{ -} - int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg) { diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 6558b09ff579..4585c8ac2b0c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -79,10 +79,6 @@ void kvm_arch_hardware_disable(void *garbage) { } -void decache_vcpus_on_cpu(int cpu) -{ -} - int kvm_arch_hardware_setup(void) { return 0; diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 9390a31c06f4..238e8f3afaf4 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -709,10 +709,6 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu) rdtscll(vcpu->arch.host_tsc); } -static void svm_vcpu_decache(struct kvm_vcpu *vcpu) -{ -} - static void svm_cache_regs(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -1933,7 +1929,6 @@ static struct kvm_x86_ops svm_x86_ops = { .prepare_guest_switch = svm_prepare_guest_switch, .vcpu_load = svm_vcpu_load, .vcpu_put = svm_vcpu_put, - .vcpu_decache = svm_vcpu_decache, .set_guest_debug = svm_guest_debug, .get_msr = svm_get_msr, diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 4d179d106376..b99bb37e5dec 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -692,11 +692,6 @@ static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu) update_exception_bitmap(vcpu); } -static void vmx_vcpu_decache(struct kvm_vcpu *vcpu) -{ - vcpu_clear(to_vmx(vcpu)); -} - static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu) { return vmcs_readl(GUEST_RFLAGS); @@ -3114,7 +3109,6 @@ static struct kvm_x86_ops vmx_x86_ops = { .prepare_guest_switch = vmx_save_host_state, .vcpu_load = vmx_vcpu_load, .vcpu_put = vmx_vcpu_put, - .vcpu_decache = vmx_vcpu_decache, .set_guest_debug = set_guest_debug, .guest_debug_pre = kvm_guest_debug_pre, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8c14ddcaba70..fd03b4465bcc 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -817,14 +817,6 @@ out: return r; } -/* - * Make sure that a cpu that is being hot-unplugged does not have any vcpus - * cached on it. - */ -void decache_vcpus_on_cpu(int cpu) -{ -} - int kvm_dev_ioctl_check_extension(long ext) { int r; diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h index 0df9d5fa281a..4bcdc7de07b5 100644 --- a/include/asm-x86/kvm_host.h +++ b/include/asm-x86/kvm_host.h @@ -380,7 +380,6 @@ struct kvm_x86_ops { void (*prepare_guest_switch)(struct kvm_vcpu *vcpu); void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu); void (*vcpu_put)(struct kvm_vcpu *vcpu); - void (*vcpu_decache)(struct kvm_vcpu *vcpu); int (*set_guest_debug)(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index de9d1df4bba2..865dcbcb891f 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -135,9 +135,6 @@ void kvm_vcpu_uninit(struct kvm_vcpu *vcpu); void vcpu_load(struct kvm_vcpu *vcpu); void vcpu_put(struct kvm_vcpu *vcpu); -void decache_vcpus_on_cpu(int cpu); - - int kvm_init(void *opaque, unsigned int vcpu_size, struct module *module); void kvm_exit(void); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e4bf88a9ee4e..83a0e5ce6037 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1273,7 +1273,6 @@ static void hardware_disable(void *junk) if (!cpu_isset(cpu, cpus_hardware_enabled)) return; cpu_clear(cpu, cpus_hardware_enabled); - decache_vcpus_on_cpu(cpu); kvm_arch_hardware_disable(NULL); } -- cgit v1.2.3 From 92760499d01ef91518119908eb9b8798b6c9bd3f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 30 May 2008 16:05:53 +0200 Subject: KVM: kvm_io_device: extend in_range() to manage len and write attribute Modify member in_range() of structure kvm_io_device to pass length and the type of the I/O (write or read). This modification allows to use kvm_io_device with coalesced MMIO. Signed-off-by: Laurent Vivier Signed-off-by: Avi Kivity --- arch/ia64/kvm/kvm-ia64.c | 6 +++--- arch/x86/kvm/i8254.c | 6 ++++-- arch/x86/kvm/i8259.c | 3 ++- arch/x86/kvm/lapic.c | 3 ++- arch/x86/kvm/x86.c | 28 +++++++++++++++++----------- include/linux/kvm_host.h | 3 ++- virt/kvm/ioapic.c | 3 ++- virt/kvm/iodev.h | 8 +++++--- virt/kvm/kvm_main.c | 5 +++-- 9 files changed, 40 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 7c504be57972..bb58df7cc418 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -195,11 +195,11 @@ int kvm_dev_ioctl_check_extension(long ext) } static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, int is_write) { struct kvm_io_device *dev; - dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr); + dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, is_write); return dev; } @@ -231,7 +231,7 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) kvm_run->exit_reason = KVM_EXIT_MMIO; return 0; mmio: - mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr); + mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr, p->size, !p->dir); if (mmio_dev) { if (!p->dir) kvm_iodevice_write(mmio_dev, p->addr, p->size, diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 60074dc66bd7..9e3391e9a1b7 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -460,7 +460,8 @@ static void pit_ioport_read(struct kvm_io_device *this, mutex_unlock(&pit_state->lock); } -static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +static int pit_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { return ((addr >= KVM_PIT_BASE_ADDRESS) && (addr < KVM_PIT_BASE_ADDRESS + KVM_PIT_MEM_LENGTH)); @@ -501,7 +502,8 @@ static void speaker_ioport_read(struct kvm_io_device *this, mutex_unlock(&pit_state->lock); } -static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +static int speaker_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { return (addr == KVM_SPEAKER_BASE_ADDRESS); } diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index ab29cf2def47..5857f59ad4aa 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c @@ -346,7 +346,8 @@ static u32 elcr_ioport_read(void *opaque, u32 addr1) return s->elcr; } -static int picdev_in_range(struct kvm_io_device *this, gpa_t addr) +static int picdev_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { switch (addr) { case 0x20: diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index e48d19394031..180ba7316da5 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -785,7 +785,8 @@ static void apic_mmio_write(struct kvm_io_device *this, } -static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr) +static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr, + int len, int size) { struct kvm_lapic *apic = (struct kvm_lapic *)this->private; int ret = 0; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4c94fad7f01e..ab3f5552d694 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1797,13 +1797,14 @@ static void kvm_init_msr_list(void) * Only apic need an MMIO device hook, so shortcut now.. */ static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, + int is_write) { struct kvm_io_device *dev; if (vcpu->arch.apic) { dev = &vcpu->arch.apic->dev; - if (dev->in_range(dev, addr)) + if (dev->in_range(dev, addr, len, is_write)) return dev; } return NULL; @@ -1811,13 +1812,15 @@ static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu, static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, + int is_write) { struct kvm_io_device *dev; - dev = vcpu_find_pervcpu_dev(vcpu, addr); + dev = vcpu_find_pervcpu_dev(vcpu, addr, len, is_write); if (dev == NULL) - dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr); + dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, + is_write); return dev; } @@ -1885,7 +1888,7 @@ mmio: * Is this MMIO handled locally? */ mutex_lock(&vcpu->kvm->lock); - mmio_dev = vcpu_find_mmio_dev(vcpu, gpa); + mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 0); if (mmio_dev) { kvm_iodevice_read(mmio_dev, gpa, bytes, val); mutex_unlock(&vcpu->kvm->lock); @@ -1940,7 +1943,7 @@ mmio: * Is this MMIO handled locally? */ mutex_lock(&vcpu->kvm->lock); - mmio_dev = vcpu_find_mmio_dev(vcpu, gpa); + mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 1); if (mmio_dev) { kvm_iodevice_write(mmio_dev, gpa, bytes, val); mutex_unlock(&vcpu->kvm->lock); @@ -2317,9 +2320,10 @@ static void pio_string_write(struct kvm_io_device *pio_dev, } static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, + int is_write) { - return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr); + return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr, len, is_write); } int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, @@ -2351,7 +2355,7 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, kvm_x86_ops->skip_emulated_instruction(vcpu); - pio_dev = vcpu_find_pio_dev(vcpu, port); + pio_dev = vcpu_find_pio_dev(vcpu, port, size, !in); if (pio_dev) { kernel_pio(pio_dev, vcpu, vcpu->arch.pio_data); complete_pio(vcpu); @@ -2433,7 +2437,9 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, } } - pio_dev = vcpu_find_pio_dev(vcpu, port); + pio_dev = vcpu_find_pio_dev(vcpu, port, + vcpu->arch.pio.cur_count, + !vcpu->arch.pio.in); if (!vcpu->arch.pio.in) { /* string PIO write */ ret = pio_copy_data(vcpu); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 865dcbcb891f..499ff0604234 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -52,7 +52,8 @@ struct kvm_io_bus { void kvm_io_bus_init(struct kvm_io_bus *bus); void kvm_io_bus_destroy(struct kvm_io_bus *bus); -struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, gpa_t addr); +struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, + gpa_t addr, int len, int is_write); void kvm_io_bus_register_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev); diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c index d0c668c6959e..c0d22870ee9c 100644 --- a/virt/kvm/ioapic.c +++ b/virt/kvm/ioapic.c @@ -307,7 +307,8 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector) __kvm_ioapic_update_eoi(ioapic, i); } -static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr) +static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { struct kvm_ioapic *ioapic = (struct kvm_ioapic *)this->private; diff --git a/virt/kvm/iodev.h b/virt/kvm/iodev.h index c14e642027b2..55e8846ac3a6 100644 --- a/virt/kvm/iodev.h +++ b/virt/kvm/iodev.h @@ -27,7 +27,8 @@ struct kvm_io_device { gpa_t addr, int len, const void *val); - int (*in_range)(struct kvm_io_device *this, gpa_t addr); + int (*in_range)(struct kvm_io_device *this, gpa_t addr, int len, + int is_write); void (*destructor)(struct kvm_io_device *this); void *private; @@ -49,9 +50,10 @@ static inline void kvm_iodevice_write(struct kvm_io_device *dev, dev->write(dev, addr, len, val); } -static inline int kvm_iodevice_inrange(struct kvm_io_device *dev, gpa_t addr) +static inline int kvm_iodevice_inrange(struct kvm_io_device *dev, + gpa_t addr, int len, int is_write) { - return dev->in_range(dev, addr); + return dev->in_range(dev, addr, len, is_write); } static inline void kvm_iodevice_destructor(struct kvm_io_device *dev) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 83a0e5ce6037..9330fad2b918 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1350,14 +1350,15 @@ void kvm_io_bus_destroy(struct kvm_io_bus *bus) } } -struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, gpa_t addr) +struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, + gpa_t addr, int len, int is_write) { int i; for (i = 0; i < bus->dev_count; i++) { struct kvm_io_device *pos = bus->devs[i]; - if (pos->in_range(pos, addr)) + if (pos->in_range(pos, addr, len, is_write)) return pos; } -- cgit v1.2.3 From 5f94c1741bdc7a336553122036e8a779e616ccbf Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 30 May 2008 16:05:54 +0200 Subject: KVM: Add coalesced MMIO support (common part) This patch adds all needed structures to coalesce MMIOs. Until an architecture uses it, it is not compiled. Coalesced MMIO introduces two ioctl() to define where are the MMIO zones that can be coalesced: - KVM_REGISTER_COALESCED_MMIO registers a coalesced MMIO zone. It requests one parameter (struct kvm_coalesced_mmio_zone) which defines a memory area where MMIOs can be coalesced until the next switch to user space. The maximum number of MMIO zones is KVM_COALESCED_MMIO_ZONE_MAX. - KVM_UNREGISTER_COALESCED_MMIO cancels all registered zones inside the given bounds (bounds are also given by struct kvm_coalesced_mmio_zone). The userspace client can check kernel coalesced MMIO availability by asking ioctl(KVM_CHECK_EXTENSION) for the KVM_CAP_COALESCED_MMIO capability. The ioctl() call to KVM_CAP_COALESCED_MMIO will return 0 if not supported, or the page offset where will be stored the ring buffer. The page offset depends on the architecture. After an ioctl(KVM_RUN), the first page of the KVM memory mapped points to a kvm_run structure. The offset given by KVM_CAP_COALESCED_MMIO is an offset to the coalesced MMIO ring expressed in PAGE_SIZE relatively to the address of the start of th kvm_run structure. The MMIO ring buffer is defined by the structure kvm_coalesced_mmio_ring. [akio: fix oops during guest shutdown] Signed-off-by: Laurent Vivier Signed-off-by: Akio Takebe Signed-off-by: Avi Kivity --- include/linux/kvm.h | 29 +++++++++ include/linux/kvm_host.h | 4 ++ virt/kvm/coalesced_mmio.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++ virt/kvm/coalesced_mmio.h | 23 +++++++ virt/kvm/kvm_main.c | 57 +++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 virt/kvm/coalesced_mmio.c create mode 100644 virt/kvm/coalesced_mmio.h (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index a281afeddfbb..1c908ac29c6c 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -173,6 +173,30 @@ struct kvm_run { }; }; +/* for KVM_REGISTER_COALESCED_MMIO / KVM_UNREGISTER_COALESCED_MMIO */ + +struct kvm_coalesced_mmio_zone { + __u64 addr; + __u32 size; + __u32 pad; +}; + +struct kvm_coalesced_mmio { + __u64 phys_addr; + __u32 len; + __u32 pad; + __u8 data[8]; +}; + +struct kvm_coalesced_mmio_ring { + __u32 first, last; + struct kvm_coalesced_mmio coalesced_mmio[0]; +}; + +#define KVM_COALESCED_MMIO_MAX \ + ((PAGE_SIZE - sizeof(struct kvm_coalesced_mmio_ring)) / \ + sizeof(struct kvm_coalesced_mmio)) + /* for KVM_TRANSLATE */ struct kvm_translation { /* in */ @@ -346,6 +370,7 @@ struct kvm_trace_rec { #define KVM_CAP_NOP_IO_DELAY 12 #define KVM_CAP_PV_MMU 13 #define KVM_CAP_MP_STATE 14 +#define KVM_CAP_COALESCED_MMIO 15 /* * ioctls for VM fds @@ -371,6 +396,10 @@ struct kvm_trace_rec { #define KVM_CREATE_PIT _IO(KVMIO, 0x64) #define KVM_GET_PIT _IOWR(KVMIO, 0x65, struct kvm_pit_state) #define KVM_SET_PIT _IOR(KVMIO, 0x66, struct kvm_pit_state) +#define KVM_REGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone) +#define KVM_UNREGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone) /* * ioctls for vcpu fds diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 499ff0604234..d220b4926c4a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -117,6 +117,10 @@ struct kvm { struct kvm_vm_stat stat; struct kvm_arch arch; atomic_t users_count; +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + struct kvm_coalesced_mmio_dev *coalesced_mmio_dev; + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; +#endif }; /* The guest did something we don't support. */ diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c new file mode 100644 index 000000000000..5ae620d32fac --- /dev/null +++ b/virt/kvm/coalesced_mmio.c @@ -0,0 +1,156 @@ +/* + * KVM coalesced MMIO + * + * Copyright (c) 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + */ + +#include "iodev.h" + +#include +#include + +#include "coalesced_mmio.h" + +static int coalesced_mmio_in_range(struct kvm_io_device *this, + gpa_t addr, int len, int is_write) +{ + struct kvm_coalesced_mmio_dev *dev = + (struct kvm_coalesced_mmio_dev*)this->private; + struct kvm_coalesced_mmio_zone *zone; + int next; + int i; + + if (!is_write) + return 0; + + /* kvm->lock is taken by the caller and must be not released before + * dev.read/write + */ + + /* Are we able to batch it ? */ + + /* last is the first free entry + * check if we don't meet the first used entry + * there is always one unused entry in the buffer + */ + + next = (dev->kvm->coalesced_mmio_ring->last + 1) % + KVM_COALESCED_MMIO_MAX; + if (next == dev->kvm->coalesced_mmio_ring->first) { + /* full */ + return 0; + } + + /* is it in a batchable area ? */ + + for (i = 0; i < dev->nb_zones; i++) { + zone = &dev->zone[i]; + + /* (addr,len) is fully included in + * (zone->addr, zone->size) + */ + + if (zone->addr <= addr && + addr + len <= zone->addr + zone->size) + return 1; + } + return 0; +} + +static void coalesced_mmio_write(struct kvm_io_device *this, + gpa_t addr, int len, const void *val) +{ + struct kvm_coalesced_mmio_dev *dev = + (struct kvm_coalesced_mmio_dev*)this->private; + struct kvm_coalesced_mmio_ring *ring = dev->kvm->coalesced_mmio_ring; + + /* kvm->lock must be taken by caller before call to in_range()*/ + + /* copy data in first free entry of the ring */ + + ring->coalesced_mmio[ring->last].phys_addr = addr; + ring->coalesced_mmio[ring->last].len = len; + memcpy(ring->coalesced_mmio[ring->last].data, val, len); + smp_wmb(); + ring->last = (ring->last + 1) % KVM_COALESCED_MMIO_MAX; +} + +static void coalesced_mmio_destructor(struct kvm_io_device *this) +{ + kfree(this); +} + +int kvm_coalesced_mmio_init(struct kvm *kvm) +{ + struct kvm_coalesced_mmio_dev *dev; + + dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev.write = coalesced_mmio_write; + dev->dev.in_range = coalesced_mmio_in_range; + dev->dev.destructor = coalesced_mmio_destructor; + dev->dev.private = dev; + dev->kvm = kvm; + kvm->coalesced_mmio_dev = dev; + kvm_io_bus_register_dev(&kvm->mmio_bus, &dev->dev); + + return 0; +} + +int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone) +{ + struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev; + + if (dev == NULL) + return -EINVAL; + + mutex_lock(&kvm->lock); + if (dev->nb_zones >= KVM_COALESCED_MMIO_ZONE_MAX) { + mutex_unlock(&kvm->lock); + return -ENOBUFS; + } + + dev->zone[dev->nb_zones] = *zone; + dev->nb_zones++; + + mutex_unlock(&kvm->lock); + return 0; +} + +int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone) +{ + int i; + struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev; + struct kvm_coalesced_mmio_zone *z; + + if (dev == NULL) + return -EINVAL; + + mutex_lock(&kvm->lock); + + i = dev->nb_zones; + while(i) { + z = &dev->zone[i - 1]; + + /* unregister all zones + * included in (zone->addr, zone->size) + */ + + if (zone->addr <= z->addr && + z->addr + z->size <= zone->addr + zone->size) { + dev->nb_zones--; + *z = dev->zone[dev->nb_zones]; + } + i--; + } + + mutex_unlock(&kvm->lock); + + return 0; +} diff --git a/virt/kvm/coalesced_mmio.h b/virt/kvm/coalesced_mmio.h new file mode 100644 index 000000000000..5ac0ec628461 --- /dev/null +++ b/virt/kvm/coalesced_mmio.h @@ -0,0 +1,23 @@ +/* + * KVM coalesced MMIO + * + * Copyright (c) 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + */ + +#define KVM_COALESCED_MMIO_ZONE_MAX 100 + +struct kvm_coalesced_mmio_dev { + struct kvm_io_device dev; + struct kvm *kvm; + int nb_zones; + struct kvm_coalesced_mmio_zone zone[KVM_COALESCED_MMIO_ZONE_MAX]; +}; + +int kvm_coalesced_mmio_init(struct kvm *kvm); +int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone); +int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9330fad2b918..7d10dfa0d388 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -47,6 +47,10 @@ #include #include +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET +#include "coalesced_mmio.h" +#endif + MODULE_AUTHOR("Qumranet"); MODULE_LICENSE("GPL"); @@ -185,10 +189,23 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_uninit); static struct kvm *kvm_create_vm(void) { struct kvm *kvm = kvm_arch_create_vm(); +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + struct page *page; +#endif if (IS_ERR(kvm)) goto out; +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!page) { + kfree(kvm); + return ERR_PTR(-ENOMEM); + } + kvm->coalesced_mmio_ring = + (struct kvm_coalesced_mmio_ring *)page_address(page); +#endif + kvm->mm = current->mm; atomic_inc(&kvm->mm->mm_count); spin_lock_init(&kvm->mmu_lock); @@ -200,6 +217,9 @@ static struct kvm *kvm_create_vm(void) spin_lock(&kvm_lock); list_add(&kvm->vm_list, &vm_list); spin_unlock(&kvm_lock); +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + kvm_coalesced_mmio_init(kvm); +#endif out: return kvm; } @@ -242,6 +262,10 @@ static void kvm_destroy_vm(struct kvm *kvm) spin_unlock(&kvm_lock); kvm_io_bus_destroy(&kvm->pio_bus); kvm_io_bus_destroy(&kvm->mmio_bus); +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + if (kvm->coalesced_mmio_ring != NULL) + free_page((unsigned long)kvm->coalesced_mmio_ring); +#endif kvm_arch_destroy_vm(kvm); mmdrop(mm); } @@ -825,6 +849,10 @@ static int kvm_vcpu_fault(struct vm_area_struct *vma, struct vm_fault *vmf) #ifdef CONFIG_X86 else if (vmf->pgoff == KVM_PIO_PAGE_OFFSET) page = virt_to_page(vcpu->arch.pio_data); +#endif +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + else if (vmf->pgoff == KVM_COALESCED_MMIO_PAGE_OFFSET) + page = virt_to_page(vcpu->kvm->coalesced_mmio_ring); #endif else return VM_FAULT_SIGBUS; @@ -1148,6 +1176,32 @@ static long kvm_vm_ioctl(struct file *filp, goto out; break; } +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + case KVM_REGISTER_COALESCED_MMIO: { + struct kvm_coalesced_mmio_zone zone; + r = -EFAULT; + if (copy_from_user(&zone, argp, sizeof zone)) + goto out; + r = -ENXIO; + r = kvm_vm_ioctl_register_coalesced_mmio(kvm, &zone); + if (r) + goto out; + r = 0; + break; + } + case KVM_UNREGISTER_COALESCED_MMIO: { + struct kvm_coalesced_mmio_zone zone; + r = -EFAULT; + if (copy_from_user(&zone, argp, sizeof zone)) + goto out; + r = -ENXIO; + r = kvm_vm_ioctl_unregister_coalesced_mmio(kvm, &zone); + if (r) + goto out; + r = 0; + break; + } +#endif default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); } @@ -1231,6 +1285,9 @@ static long kvm_dev_ioctl(struct file *filp, r = PAGE_SIZE; /* struct kvm_run */ #ifdef CONFIG_X86 r += PAGE_SIZE; /* pio data page */ +#endif +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + r += PAGE_SIZE; /* coalesced mmio ring page */ #endif break; case KVM_TRACE_ENABLE: -- cgit v1.2.3 From 9ef621d3be56e1188300476a8102ff54f7b6793f Mon Sep 17 00:00:00 2001 From: "Tan, Li" Date: Fri, 23 May 2008 14:54:09 +0800 Subject: KVM: Support mixed endian machines Currently kvmtrace is not portable. This will prevent from copying a trace file from big-endian target to little-endian workstation for analysis. In the patch, kernel outputs metadata containing a magic number to trace log, and changes 64-bit words to be u64 instead of a pair of u32s. Signed-off-by: Tan Li Acked-by: Jerone Young Acked-by: Hollis Blanchard Signed-off-by: Avi Kivity --- include/linux/kvm.h | 4 ++-- virt/kvm/kvm_trace.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 1c908ac29c6c..0ea064cbfbc8 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -318,14 +318,14 @@ struct kvm_trace_rec { __u32 vcpu_id; union { struct { - __u32 cycle_lo, cycle_hi; + __u64 cycle_u64; __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } cycle; struct { __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } nocycle; } u; -}; +} __attribute__((packed)); #define KVMIO 0xAE diff --git a/virt/kvm/kvm_trace.c b/virt/kvm/kvm_trace.c index 0e495470788d..58141f31ea8f 100644 --- a/virt/kvm/kvm_trace.c +++ b/virt/kvm/kvm_trace.c @@ -72,11 +72,7 @@ static void kvm_add_trace(void *probe_private, void *call_data, rec.cycle_in = p->cycle_in; if (rec.cycle_in) { - u64 cycle = 0; - - cycle = get_cycles(); - rec.u.cycle.cycle_lo = (u32)cycle; - rec.u.cycle.cycle_hi = (u32)(cycle >> 32); + rec.u.cycle.cycle_u64 = get_cycles(); for (i = 0; i < rec.extra_u32; i++) rec.u.cycle.extra_u32[i] = va_arg(*args, u32); @@ -114,8 +110,18 @@ static int kvm_subbuf_start_callback(struct rchan_buf *buf, void *subbuf, { struct kvm_trace *kt; - if (!relay_buf_full(buf)) + if (!relay_buf_full(buf)) { + if (!prev_subbuf) { + /* + * executed only once when the channel is opened + * save metadata as first record + */ + subbuf_start_reserve(buf, sizeof(u32)); + *(u32 *)subbuf = 0x12345678; + } + return 1; + } kt = buf->chan->private_data; atomic_inc(&kt->lost_records); -- cgit v1.2.3 From 34d4cb8fca1f2a31be152b74797e6cd160ec9de6 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Thu, 10 Jul 2008 20:49:31 -0300 Subject: KVM: MMU: nuke shadowed pgtable pages and ptes on memslot destruction Flush the shadow mmu before removing regions to avoid stale entries. Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/ia64/kvm/kvm-ia64.c | 3 +++ arch/powerpc/kvm/powerpc.c | 4 ++++ arch/s390/kvm/kvm-s390.c | 4 ++++ arch/x86/kvm/x86.c | 5 +++++ include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 3 +++ 6 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 9408b30576d6..2672f4d278ac 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1455,6 +1455,9 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index b850d2497027..53826a5f6c06 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -170,6 +170,10 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} + struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) { struct kvm_vcpu *vcpu; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 399acf3f64dd..1782cbcd2829 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -675,6 +675,10 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} + gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn) { return gfn; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b131f3c0cf64..9f1cdb011cff 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4032,6 +4032,11 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ + kvm_mmu_zap_all(kvm); +} + int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) { return vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index d220b4926c4a..07d68a8ae8e9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -168,6 +168,7 @@ int kvm_arch_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, int user_alloc); +void kvm_arch_flush_shadow(struct kvm *kvm); gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn); struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9ccaf8f5402e..30b36368fcdf 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -405,6 +405,9 @@ int __kvm_set_memory_region(struct kvm *kvm, if (mem->slot >= kvm->nmemslots) kvm->nmemslots = mem->slot + 1; + if (!npages) + kvm_arch_flush_shadow(kvm); + *memslot = new; r = kvm_arch_set_memory_region(kvm, mem, old, user_alloc); -- cgit v1.2.3 From 1d0ba5f3784612fe6e91a12e0dec37c797d4f07c Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Mon, 26 May 2008 18:40:46 -0300 Subject: V4L/DVB (7942): Hardware frequency seek ioctl interface Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/compat_ioctl32.c | 1 + drivers/media/video/videodev.c | 12 ++++++++++++ include/linux/videodev2.h | 10 ++++++++++ include/media/v4l2-dev.h | 2 ++ 4 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index cefd1381e8de..54de0cd482e9 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -884,6 +884,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: case VIDIOC_TRY_FMT32: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 7649860a388d..52c56678ee69 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -278,6 +278,7 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", + [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -1763,6 +1764,17 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret = vfd->vidioc_default(file, fh, cmd, arg); break; } + case VIDIOC_S_HW_FREQ_SEEK: + { + struct v4l2_hw_freq_seek *p = arg; + if (!vfd->vidioc_s_hw_freq_seek) + break; + dbgarg(cmd, + "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n", + p->tuner, p->type, p->seek_upward, p->wrap_around); + ret = vfd->vidioc_s_hw_freq_seek(file, fh, p); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 4a535ea1e123..9385c823a978 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -246,6 +246,7 @@ struct v4l2_capability #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */ #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */ +#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */ #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ @@ -1156,6 +1157,14 @@ struct v4l2_frequency __u32 reserved[8]; }; +struct v4l2_hw_freq_seek { + __u32 tuner; + enum v4l2_tuner_type type; + __u32 seek_upward; + __u32 wrap_around; + __u32 reserved[8]; +}; + /* * A U D I O */ @@ -1441,6 +1450,7 @@ struct v4l2_chip_ident { #define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) #endif +#define VIDIOC_S_HW_FREQ_SEEK _IOW ('V', 82, struct v4l2_hw_freq_seek) #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 859f7a6f6f67..53b651ac685a 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -306,6 +306,8 @@ struct video_device /* Log status ioctl */ int (*vidioc_log_status) (struct file *file, void *fh); + int (*vidioc_s_hw_freq_seek) (struct file *file, void *fh, + struct v4l2_hw_freq_seek *a); /* Debugging ioctls */ #ifdef CONFIG_VIDEO_ADV_DEBUG -- cgit v1.2.3 From 6a7eba24e4f0ff725d33159f6265e3a79d53a833 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 30 Jun 2008 15:50:11 -0300 Subject: V4L/DVB (8157): gspca: all subdrivers - remaning subdrivers added - remove the decoding helper and some specific frame decodings Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 306 ++--- drivers/media/video/Makefile | 1 + drivers/media/video/gspca/Kconfig | 2 +- drivers/media/video/gspca/Makefile | 26 +- drivers/media/video/gspca/conex.c | 1059 +++++++++++++++++ drivers/media/video/gspca/etoms.c | 1062 +++++++++++++++++ drivers/media/video/gspca/gspca.c | 579 ++------- drivers/media/video/gspca/gspca.h | 39 +- drivers/media/video/gspca/mars.c | 455 +++++++ drivers/media/video/gspca/ov519.c | 2174 ++++++++++++++++++++++++++++++++++ drivers/media/video/gspca/pac207.c | 10 +- drivers/media/video/gspca/pac7311.c | 754 ++++++++++++ drivers/media/video/gspca/sonixb.c | 879 ++++++++++++++ drivers/media/video/gspca/sonixj.c | 1629 +++++++++++++++++++++++++ drivers/media/video/gspca/spca500.c | 1195 +++++++++++++++++++ drivers/media/video/gspca/spca501.c | 2219 +++++++++++++++++++++++++++++++++++ drivers/media/video/gspca/spca505.c | 933 +++++++++++++++ drivers/media/video/gspca/spca506.c | 830 +++++++++++++ drivers/media/video/gspca/spca508.c | 1774 ++++++++++++++++++++++++++++ drivers/media/video/gspca/spca561.c | 1025 ++++++++++++++++ drivers/media/video/gspca/stk014.c | 170 ++- drivers/media/video/gspca/sunplus.c | 1638 ++++++++++++++++++++++++++ drivers/media/video/gspca/t613.c | 1013 ++++++++++++++++ drivers/media/video/gspca/tv8532.c | 709 +++++++++++ drivers/media/video/gspca/vc032x.c | 1816 ++++++++++++++++++++++++++++ drivers/media/video/gspca/zc3xx.c | 649 +++++----- include/linux/videodev2.h | 2 + 27 files changed, 21964 insertions(+), 984 deletions(-) create mode 100644 drivers/media/video/gspca/conex.c create mode 100644 drivers/media/video/gspca/etoms.c create mode 100644 drivers/media/video/gspca/mars.c create mode 100644 drivers/media/video/gspca/ov519.c create mode 100644 drivers/media/video/gspca/pac7311.c create mode 100644 drivers/media/video/gspca/sonixb.c create mode 100644 drivers/media/video/gspca/sonixj.c create mode 100644 drivers/media/video/gspca/spca500.c create mode 100644 drivers/media/video/gspca/spca501.c create mode 100644 drivers/media/video/gspca/spca505.c create mode 100644 drivers/media/video/gspca/spca506.c create mode 100644 drivers/media/video/gspca/spca508.c create mode 100644 drivers/media/video/gspca/spca561.c create mode 100644 drivers/media/video/gspca/sunplus.c create mode 100644 drivers/media/video/gspca/t613.c create mode 100644 drivers/media/video/gspca/tv8532.c create mode 100644 drivers/media/video/gspca/vc032x.c (limited to 'include/linux') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 9c404b56dbb3..37996e59d516 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -1,4 +1,4 @@ -Here the list of the known working cameras with gspca. +List of the webcams know by gspca. The modules are: gspca_main main driver @@ -6,106 +6,73 @@ The modules are: xxxx vend:prod ---- -conex 0572:0041 Creative Notebook cx11646 -etoms 102c:6151 Qcam Sangha CIF -etoms 102c:6251 Qcam xxxxxx VGA -mars 093a:050f Mars-Semi Pc-Camera +spca501 0000:0000 MystFromOri Unknow Camera +spca501 040a:0002 Kodak DVC-325 +spca500 040a:0300 Kodak EZ200 +zc3xx 041e:041e Creative WebCam Live! +spca500 041e:400a Creative PC-CAM 300 +sunplus 041e:400b Creative PC-CAM 600 +sunplus 041e:4012 PC-Cam350 +sunplus 041e:4013 Creative Pccam750 +zc3xx 041e:4017 Creative Webcam Mobile PD1090 +spca508 041e:4018 Creative Webcam Vista (PD1100) +spca561 041e:401a Creative Webcam Vista (PD1100) +zc3xx 041e:401c Creative NX +spca505 041e:401d Creative Webcam NX ULTRA +zc3xx 041e:401e Creative Nx Pro +zc3xx 041e:401f Creative Webcam Notebook PD1171 +pac207 041e:4028 Creative Webcam Vista Plus +zc3xx 041e:4029 Creative WebCam Vista Pro +zc3xx 041e:4034 Creative Instant P0620 +zc3xx 041e:4035 Creative Instant P0620D +zc3xx 041e:4036 Creative Live ! +zc3xx 041e:403a Creative Nx Pro 2 +spca561 041e:403b Creative Webcam Vista (VF0010) +zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250) ov519 041e:4052 Creative Live! VISTA IM +zc3xx 041e:4053 Creative Live!Cam Video IM ov519 041e:405f Creative Live! VISTA VF0330 ov519 041e:4060 Creative Live! VISTA VF0350 ov519 041e:4061 Creative Live! VISTA VF0400 ov519 041e:4064 Creative Live! VISTA VF0420 ov519 041e:4068 Creative Live! VISTA VF0470 -ov519 045e:028c Micro$oft xbox cam -ov519 054c:0154 Sonny toy4 -ov519 054c:0155 Sonny toy5 -ov519 05a9:0519 OmniVision -ov519 05a9:4519 OmniVision -ov519 05a9:8519 OmniVision -ov519 05a9:0530 OmniVision -pac207 041e:4028 Creative Webcam Vista Plus -pac207 093a:2460 PAC207 Qtec Webcam 100 -pac207 093a:2463 Philips spc200nc pac207 -pac207 093a:2464 Labtec Webcam 1200 -pac207 093a:2468 PAC207 -pac207 093a:2470 Genius GF112 -pac207 093a:2471 PAC207 Genius VideoCam ge111 -pac207 093a:2472 PAC207 Genius VideoCam ge110 -pac7311 093a:2600 PAC7311 Typhoon -pac7311 093a:2601 PAC7311 Phillips SPC610NC -pac7311 093a:2603 PAC7312 -pac7311 093a:2608 PAC7311 Trust WB-3300p -pac7311 093a:260e PAC7311 Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 -pac7311 093a:260f PAC7311 SnakeCam -sonixb 0c45:6001 Genius VideoCAM NB -sonixb 0c45:6005 Sweex Tas5110 -sonixb 0c45:6007 Sonix sn9c101 + Tas5110D -sonixb 0c45:6009 spcaCam@120 -sonixb 0c45:600d spcaCam@120 -sonixb 0c45:6011 MAX Webcam (Microdia - OV6650 - SN9C101G) -sonixb 0c45:6019 Generic Sonix OV7630 -sonixb 0c45:6024 Generic Sonix Tas5130c -sonixb 0c45:6025 Xcam Shanga -sonixb 0c45:6028 Sonix Btc Pc380 -sonixb 0c45:6029 spcaCam@150 -sonixb 0c45:602c Generic Sonix OV7630 -sonixb 0c45:602d LIC-200 LG -sonixb 0c45:602e Genius VideoCam Messenger +spca561 0458:7004 Genius VideoCAM Express V2 +sunplus 0458:7006 Genius Dsc 1.3 Smart +zc3xx 0458:7007 Genius VideoCam V2 +zc3xx 0458:700c Genius VideoCam V3 +zc3xx 0458:700f Genius VideoCam Web V2 sonixj 0458:7025 Genius Eye 311Q sonixj 045e:00f5 MicroSoft VX3000 sonixj 045e:00f7 MicroSoft VX1000 -sonixj 0471:0327 Philips SPC 600 NC -sonixj 0471:0328 Philips SPC 700 NC -sonixj 0471:0330 Philips SPC 710NC -sonixj 0c45:6040 Speed NVC 350K -sonixj 0c45:607c Sonix sn9c102p Hv7131R -sonixj 0c45:60c0 Sangha Sn535 -sonixj 0c45:60ec SN9C105+MO4000 -sonixj 0c45:60fb Surfer NoName -sonixj 0c45:60fc LG-LIC300 -sonixj 0c45:612a Avant Camera -sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix -sonixj 0c45:6130 Sonix Pccam -sonixj 0c45:6138 Sn9c120 Mo4000 -sonixj 0c45:613b Surfer SN-206 -sonixj 0c45:613c Sonix Pccam168 -spca500 040a:0300 Kodak EZ200 -spca500 041e:400a Creative PC-CAM 300 +ov519 045e:028c Micro$oft xbox cam +spca508 0461:0815 Micro Innovation IC200 +zc3xx 0461:0a00 MicroInnovation WebCam320 spca500 046d:0890 Logitech QuickCam traveler +vc032x 046d:0892 Logitech Orbicam +vc032x 046d:0896 Logitech Orbicam +zc3xx 046d:08a0 Logitech QC IM +zc3xx 046d:08a1 Logitech QC IM 0x08A1 +sound +zc3xx 046d:08a2 Labtec Webcam Pro +zc3xx 046d:08a3 Logitech QC Chat +zc3xx 046d:08a6 Logitech QCim +zc3xx 046d:08a7 Logitech QuickCam Image +zc3xx 046d:08a9 Logitech Notebook Deluxe +zc3xx 046d:08aa Labtec Webcam Notebook +zc3xx 046d:08ac Logitech QuickCam Cool +zc3xx 046d:08ad Logitech QCCommunicate STX +zc3xx 046d:08ae Logitech QuickCam for Notebooks +zc3xx 046d:08af Logitech QuickCam Cool +zc3xx 046d:08b9 Logitech QC IM ??? +zc3xx 046d:08d7 Logitech QCam STX +zc3xx 046d:08d9 Logitech QuickCam IM/Connect +zc3xx 046d:08d8 Logitech Notebook Deluxe +zc3xx 046d:08da Logitech QuickCam Messenger +zc3xx 046d:08dd Logitech QuickCam for Notebooks spca500 046d:0900 Logitech Inc. ClickSmart 310 spca500 046d:0901 Logitech Inc. ClickSmart 510 -spca500 04a5:300c Benq DC1016 -spca500 04fc:7333 PalmPixDC85 -spca500 055f:c200 Mustek Gsmart 300 -spca500 055f:c220 Gsmart Mini -spca500 06bd:0404 Agfa CL20 -spca500 06be:0800 Optimedia -spca500 084d:0003 D-Link DSC-350 -spca500 08ca:0103 Aiptek PocketDV -spca500 2899:012c Toptro Industrial -spca500 8086:0630 Intel Pocket PC Camera -spca501 040a:0002 Kodak DVC-325 -spca501 0497:c001 Smile International -spca501 0506:00df 3Com HomeConnect Lite -spca501 0733:0401 Intel Create and Share -spca501 0733:0402 ViewQuest M318B -spca501 1776:501c Arowana 300K CMOS Camera -spca501 0000:0000 MystFromOri Unknow Camera -spca505 041e:401d Creative Webcam NX ULTRA -spca505 0733:0430 Intel PC Camera Pro -spca506 06e1:a190 ADS Instant VCD -spca506 0734:043b 3DeMon USB Capture aka -spca506 99fa:8988 Grandtec V.cap -spca506 99fa:8988 Grandtec V.cap -spca508 041e:4018 Creative Webcam Vista (PD1100) -spca508 0461:0815 Micro Innovation IC200 -spca508 0733:0110 ViewQuest VQ110 -spca508 0af9:0010 Hama USB Sightcam 100 -spca508 0af9:0011 Hama USB Sightcam 100 -spca508 8086:0110 Intel Easy PC Camera -spca561 041e:401a Creative Webcam Vista (PD1100) -spca561 041e:403b Creative Webcam Vista (VF0010) -spca561 0458:7004 Genius VideoCAM Express V2 +sunplus 046d:0905 Logitech ClickSmart 820 +tv8532 046d:0920 QC Express +tv8532 046d:0921 Labtec Webcam spca561 046d:0928 Logitech QC Express Etch2 spca561 046d:0929 Labtec Webcam Elch2 spca561 046d:092a Logitech QC for Notebook @@ -114,33 +81,42 @@ spca561 046d:092c Logitech QC chat Elch2 spca561 046d:092d Logitech QC Elch2 spca561 046d:092e Logitech QC Elch2 spca561 046d:092f Logitech QC Elch2 -spca561 04fc:0561 Flexcam 100 -spca561 060b:a001 Maxell Compact Pc PM3 -spca561 10fd:7e50 FlyCam Usb 100 -spca561 abcd:cdee Petcam -stk014 05e1:0893 Syntek DV4000 -sunplus 041e:400b Creative PC-CAM 600 -sunplus 041e:4012 PC-Cam350 -sunplus 041e:4013 Creative Pccam750 -sunplus 0458:7006 Genius Dsc 1.3 Smart -sunplus 046d:0905 Logitech ClickSmart 820 sunplus 046d:0960 Logitech ClickSmart 420 sunplus 0471:0322 Philips DMVC1300K +zc3xx 0471:0325 Philips SPC 200 NC +zc3xx 0471:0326 Philips SPC 300 NC +sonixj 0471:0327 Philips SPC 600 NC +sonixj 0471:0328 Philips SPC 700 NC +zc3xx 0471:032d Philips spc210nc +zc3xx 0471:032e Philips spc315nc +sonixj 0471:0330 Philips SPC 710NC +spca501 0497:c001 Smile International sunplus 04a5:3003 Benq DC 1300 sunplus 04a5:3008 Benq DC 1500 sunplus 04a5:300a Benq DC3410 +spca500 04a5:300c Benq DC1016 sunplus 04f1:1001 JVC GC A50 +spca561 04fc:0561 Flexcam 100 sunplus 04fc:500c Sunplus CA500C sunplus 04fc:504a Aiptek Mini PenCam 1.3 sunplus 04fc:504b Maxell MaxPocket LE 1.3 sunplus 04fc:5330 Digitrex 2110 sunplus 04fc:5360 Sunplus Generic +spca500 04fc:7333 PalmPixDC85 sunplus 04fc:ffff Pure DigitalDakota +spca501 0506:00df 3Com HomeConnect Lite sunplus 052b:1513 Megapix V4 +tv8532 0545:808b Veo Stingray +tv8532 0545:8333 Veo Stingray sunplus 0546:3155 Polaroid PDC3070 sunplus 0546:3191 Polaroid Ion 80 sunplus 0546:3273 Polaroid PDC2030 +ov519 054c:0154 Sonny toy4 +ov519 054c:0155 Sonny toy5 +zc3xx 055f:c005 Mustek Wcam300A +spca500 055f:c200 Mustek Gsmart 300 sunplus 055f:c211 Kowa Bs888e Microcamera +spca500 055f:c220 Gsmart Mini sunplus 055f:c230 Mustek Digicam 330K sunplus 055f:c232 Mustek MDC3500 sunplus 055f:c360 Mustek DV4000 Mpeg4 @@ -152,14 +128,34 @@ sunplus 055f:c530 Mustek Gsmart LCD 3 sunplus 055f:c540 Gsmart D30 sunplus 055f:c630 Mustek MDC4000 sunplus 055f:c650 Mustek MDC5500Z +zc3xx 055f:d003 Mustek WCam300A +zc3xx 055f:d004 Mustek WCam300 AN +conex 0572:0041 Creative Notebook cx11646 +ov519 05a9:0519 OmniVision +ov519 05a9:0530 OmniVision +ov519 05a9:4519 OmniVision +ov519 05a9:8519 OmniVision sunplus 05da:1018 Digital Dream Enigma 1.3 +stk014 05e1:0893 Syntek DV4000 +spca561 060b:a001 Maxell Compact Pc PM3 +zc3xx 0698:2003 CTX M730V built in +spca500 06bd:0404 Agfa CL20 +spca500 06be:0800 Optimedia sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom +spca506 06e1:a190 ADS Instant VCD +spca508 0733:0110 ViewQuest VQ110 +spca501 0733:0401 Intel Create and Share +spca501 0733:0402 ViewQuest M318B +spca505 0733:0430 Intel PC Camera Pro sunplus 0733:1311 Digital Dream Epsilon 1.3 sunplus 0733:1314 Mercury 2.1MEG Deluxe Classic Cam sunplus 0733:2211 Jenoptik jdc 21 LCD sunplus 0733:2221 Mercury Digital Pro 3.1p sunplus 0733:3261 Concord 3045 spca536a sunplus 0733:3281 Cyberpix S550V +spca506 0734:043b 3DeMon USB Capture aka +spca500 084d:0003 D-Link DSC-350 +spca500 08ca:0103 Aiptek PocketDV sunplus 08ca:0104 Aiptek PocketDVII 1.3 sunplus 08ca:0106 Aiptek Pocket DV3100+ sunplus 08ca:2008 Aiptek Mini PenCam 2 M @@ -173,66 +169,72 @@ sunplus 08ca:2028 Aiptek PocketCam4M sunplus 08ca:2040 Aiptek PocketDV4100M sunplus 08ca:2042 Aiptek PocketDV5100 sunplus 08ca:2060 Aiptek PocketDV5300 -sunplus 0d64:0303 Sunplus FashionCam DXG -tv8532 046d:0920 QC Express -tv8532 046d:0921 Labtec Webcam -tv8532 0545:808b Veo Stingray -tv8532 0545:8333 Veo Stingray tv8532 0923:010f ICM532 cams -vc032x 046d:0892 Logitech Orbicam -vc032x 046d:0896 Logitech Orbicam +mars 093a:050f Mars-Semi Pc-Camera +pac207 093a:2460 PAC207 Qtec Webcam 100 +pac207 093a:2463 Philips spc200nc pac207 +pac207 093a:2464 Labtec Webcam 1200 +pac207 093a:2468 PAC207 +pac207 093a:2470 Genius GF112 +pac207 093a:2471 PAC207 Genius VideoCam ge111 +pac207 093a:2472 PAC207 Genius VideoCam ge110 +pac7311 093a:2600 PAC7311 Typhoon +pac7311 093a:2601 PAC7311 Phillips SPC610NC +pac7311 093a:2603 PAC7312 +pac7311 093a:2608 PAC7311 Trust WB-3300p +pac7311 093a:260e PAC7311 Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 +pac7311 093a:260f PAC7311 SnakeCam +pac7311 093a:2621 PAC731x +zc3xx 0ac8:0302 Z-star Vimicro zc0302 vc032x 0ac8:0321 Vimicro generic vc0321 vc032x 0ac8:0323 Vimicro Vc0323 vc032x 0ac8:0328 A4Tech PK-130MG -vc032x 0ac8:c001 Sony embedded vimicro -vc032x 0ac8:c002 Sony embedded vimicro -vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC -zc3xx 041e:041e Creative WebCam Live! -zc3xx 041e:4017 Creative Webcam Mobile PD1090 -zc3xx 041e:401c Creative NX -zc3xx 041e:401e Creative Nx Pro -zc3xx 041e:401f Creative Webcam Notebook PD1171 -zc3xx 041e:4029 Creative WebCam Vista Pro -zc3xx 041e:4034 Creative Instant P0620 -zc3xx 041e:4035 Creative Instant P0620D -zc3xx 041e:4036 Creative Live ! -zc3xx 041e:403a Creative Nx Pro 2 -zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250) -zc3xx 041e:4053 Creative Live!Cam Video IM -zc3xx 0458:7007 Genius VideoCam V2 -zc3xx 0458:700c Genius VideoCam V3 -zc3xx 0458:700f Genius VideoCam Web V2 -zc3xx 0461:0a00 MicroInnovation WebCam320 -zc3xx 046d:08a0 Logitech QC IM -zc3xx 046d:08a1 Logitech QC IM 0x08A1 +sound -zc3xx 046d:08a2 Labtec Webcam Pro -zc3xx 046d:08a3 Logitech QC Chat -zc3xx 046d:08a6 Logitech QCim -zc3xx 046d:08a7 Logitech QuickCam Image -zc3xx 046d:08a9 Logitech Notebook Deluxe -zc3xx 046d:08aa Labtec Webcam Notebook -zc3xx 046d:08ac Logitech QuickCam Cool -zc3xx 046d:08ad Logitech QCCommunicate STX -zc3xx 046d:08ae Logitech QuickCam for Notebooks -zc3xx 046d:08af Logitech QuickCam Cool -zc3xx 046d:08b9 Logitech QC IM ??? -zc3xx 046d:08d7 Logitech QCam STX -zc3xx 046d:08d9 Logitech QuickCam IM/Connect -zc3xx 046d:08d8 Logitech Notebook Deluxe -zc3xx 046d:08da Logitech QuickCam Messenger -zc3xx 046d:08dd Logitech QuickCam for Notebooks -zc3xx 0471:0325 Philips SPC 200 NC -zc3xx 0471:0326 Philips SPC 300 NC -zc3xx 0471:032d Philips spc210nc -zc3xx 0471:032e Philips spc315nc -zc3xx 055f:c005 Mustek Wcam300A -zc3xx 055f:d003 Mustek WCam300A -zc3xx 055f:d004 Mustek WCam300 AN -zc3xx 0698:2003 CTX M730V built in -zc3xx 0ac8:0302 Z-star Vimicro zc0302 zc3xx 0ac8:301b Z-Star zc301b zc3xx 0ac8:303b Vimicro 0x303b zc3xx 0ac8:305b Z-star Vimicro zc0305b zc3xx 0ac8:307b Ldlc VC302+Ov7620 +vc032x 0ac8:c001 Sony embedded vimicro +vc032x 0ac8:c002 Sony embedded vimicro +spca508 0af9:0010 Hama USB Sightcam 100 +spca508 0af9:0011 Hama USB Sightcam 100 +sonixb 0c45:6001 Genius VideoCAM NB +sonixb 0c45:6005 Sweex Tas5110 +sonixb 0c45:6007 Sonix sn9c101 + Tas5110D +sonixb 0c45:6009 spcaCam@120 +sonixb 0c45:600d spcaCam@120 +sonixb 0c45:6011 MAX Webcam (Microdia - OV6650 - SN9C101G) +sonixb 0c45:6019 Generic Sonix OV7630 +sonixb 0c45:6024 Generic Sonix Tas5130c +sonixb 0c45:6025 Xcam Shanga +sonixb 0c45:6028 Sonix Btc Pc380 +sonixb 0c45:6029 spcaCam@150 +sonixb 0c45:602c Generic Sonix OV7630 +sonixb 0c45:602d LIC-200 LG +sonixb 0c45:602e Genius VideoCam Messenger +sonixj 0c45:6040 Speed NVC 350K +sonixj 0c45:607c Sonix sn9c102p Hv7131R +sonixj 0c45:60c0 Sangha Sn535 +sonixj 0c45:60ec SN9C105+MO4000 +sonixj 0c45:60fb Surfer NoName +sonixj 0c45:60fc LG-LIC300 +sonixj 0c45:612a Avant Camera +sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix +sonixj 0c45:6130 Sonix Pccam +sonixj 0c45:6138 Sn9c120 Mo4000 +sonixj 0c45:613b Surfer SN-206 +sonixj 0c45:613c Sonix Pccam168 +sunplus 0d64:0303 Sunplus FashionCam DXG +etoms 102c:6151 Qcam Sangha CIF +etoms 102c:6251 Qcam xxxxxx VGA zc3xx 10fd:0128 Typhoon Webshot II USB 300k 0x0128 +spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k +spca501 1776:501c Arowana 300K CMOS Camera +t613 17a1:0128 T613/TAS5130A +vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC +pac207 2001:f115 D-Link DSB-C120 +spca500 2899:012c Toptro Industrial +spca508 8086:0110 Intel Easy PC Camera +spca500 8086:0630 Intel Pocket PC Camera +spca506 99fa:8988 Grandtec V.cap +spca561 abcd:cdee Petcam diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 2ec920dc32e0..d5f6eea7d2fd 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_ZC0301) += zc0301/ +obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index a04e413e1258..42b90742b40b 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -2,7 +2,7 @@ config USB_GSPCA tristate "USB GSPCA driver" depends on VIDEO_V4L2 ---help--- - Say Y here if you want support for various USB cameras. + Say Y here if you want support for various USB webcams. See for more info. diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index d959f7771526..e68a8965297a 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,7 +1,29 @@ -obj-$(CONFIG_GSPCA) += gspca_main.o \ - gspca_pac207.o gspca_stk014.o gspca_zc3xx.o +obj-$(CONFIG_USB_GSPCA) += gspca_main.o \ + gspca_conex.o gspca_etoms.o gspca_mars.o \ + gspca_ov519.o gspca_pac207.o gspca_pac7311.o \ + gspca_sonixb.o gspca_sonixj.o gspca_spca500.o gspca_spca501.o \ + gspca_spca505.o gspca_spca506.o gspca_spca508.o gspca_spca561.o \ + gspca_sunplus.o gspca_stk014.o gspca_t613.o gspca_tv8532.o \ + gspca_vc032x.o gspca_zc3xx.o gspca_main-objs := gspca.o +gspca_conex-objs := conex.o +gspca_etoms-objs := etoms.o +gspca_mars-objs := mars.o +gspca_ov519-objs := ov519.o gspca_pac207-objs := pac207.o +gspca_pac7311-objs := pac7311.o +gspca_sonixb-objs := sonixb.o +gspca_sonixj-objs := sonixj.o +gspca_spca500-objs := spca500.o +gspca_spca501-objs := spca501.o +gspca_spca505-objs := spca505.o +gspca_spca506-objs := spca506.o +gspca_spca508-objs := spca508.o +gspca_spca561-objs := spca561.o gspca_stk014-objs := stk014.o +gspca_sunplus-objs := sunplus.o +gspca_t613-objs := t613.o +gspca_tv8532-objs := tv8532.o +gspca_vc032x-objs := vc032x.o gspca_zc3xx-objs := zc3xx.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c new file mode 100644 index 000000000000..b0294c9274e3 --- /dev/null +++ b/drivers/media/video/gspca/conex.c @@ -0,0 +1,1059 @@ +/* + * Connexant Cx11646 library + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "conex" + +#include "gspca.h" +#define CONEX_CAM 1 /* special JPEG header */ +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + unsigned char qindex; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0xd4, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0x0a, + .maximum = 0x1f, + .step = 1, + .default_value = 0x0c, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 7, + .step = 1, + .default_value = 3, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 176, 144, 3}, + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 352, 288, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +static void reg_r(struct usb_device *dev, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, buffer, length, + 500); + PDEBUG(D_USBI, "reg read i:%02x -> %02x", index, *buffer); +} + +static void reg_w(struct usb_device *dev, + __u16 index, + const __u8 *buffer, __u16 length) +{ + PDEBUG(D_USBO, "reg write i:%02x = %02x", index, *buffer); + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, (__u8 *) buffer, length, + 500); +} + +static const __u8 cx_sensor_init[][4] = { + {0x88, 0x11, 0x01, 0x01}, + {0x88, 0x12, 0x70, 0x01}, + {0x88, 0x0f, 0x00, 0x01}, + {0x88, 0x05, 0x01, 0x01}, + {} +}; + +static const __u8 cx11646_fw1[][3] = { + {0x00, 0x02, 0x00}, + {0x01, 0x43, 0x00}, + {0x02, 0xA7, 0x00}, + {0x03, 0x8B, 0x01}, + {0x04, 0xE9, 0x02}, + {0x05, 0x08, 0x04}, + {0x06, 0x08, 0x05}, + {0x07, 0x07, 0x06}, + {0x08, 0xE7, 0x06}, + {0x09, 0xC6, 0x07}, + {0x0A, 0x86, 0x08}, + {0x0B, 0x46, 0x09}, + {0x0C, 0x05, 0x0A}, + {0x0D, 0xA5, 0x0A}, + {0x0E, 0x45, 0x0B}, + {0x0F, 0xE5, 0x0B}, + {0x10, 0x85, 0x0C}, + {0x11, 0x25, 0x0D}, + {0x12, 0xC4, 0x0D}, + {0x13, 0x45, 0x0E}, + {0x14, 0xE4, 0x0E}, + {0x15, 0x64, 0x0F}, + {0x16, 0xE4, 0x0F}, + {0x17, 0x64, 0x10}, + {0x18, 0xE4, 0x10}, + {0x19, 0x64, 0x11}, + {0x1A, 0xE4, 0x11}, + {0x1B, 0x64, 0x12}, + {0x1C, 0xE3, 0x12}, + {0x1D, 0x44, 0x13}, + {0x1E, 0xC3, 0x13}, + {0x1F, 0x24, 0x14}, + {0x20, 0xA3, 0x14}, + {0x21, 0x04, 0x15}, + {0x22, 0x83, 0x15}, + {0x23, 0xE3, 0x15}, + {0x24, 0x43, 0x16}, + {0x25, 0xA4, 0x16}, + {0x26, 0x23, 0x17}, + {0x27, 0x83, 0x17}, + {0x28, 0xE3, 0x17}, + {0x29, 0x43, 0x18}, + {0x2A, 0xA3, 0x18}, + {0x2B, 0x03, 0x19}, + {0x2C, 0x63, 0x19}, + {0x2D, 0xC3, 0x19}, + {0x2E, 0x22, 0x1A}, + {0x2F, 0x63, 0x1A}, + {0x30, 0xC3, 0x1A}, + {0x31, 0x23, 0x1B}, + {0x32, 0x83, 0x1B}, + {0x33, 0xE2, 0x1B}, + {0x34, 0x23, 0x1C}, + {0x35, 0x83, 0x1C}, + {0x36, 0xE2, 0x1C}, + {0x37, 0x23, 0x1D}, + {0x38, 0x83, 0x1D}, + {0x39, 0xE2, 0x1D}, + {0x3A, 0x23, 0x1E}, + {0x3B, 0x82, 0x1E}, + {0x3C, 0xC3, 0x1E}, + {0x3D, 0x22, 0x1F}, + {0x3E, 0x63, 0x1F}, + {0x3F, 0xC1, 0x1F}, + {} +}; +static void cx11646_fw(struct gspca_dev*gspca_dev) +{ + __u8 val; + int i = 0; + + val = 0x02; + reg_w(gspca_dev->dev, 0x006a, &val, 1); + while (cx11646_fw1[i][1]) { + reg_w(gspca_dev->dev, 0x006b, cx11646_fw1[i], 3); + i++; + } + val = 0x00; + reg_w(gspca_dev->dev, 0x006a, &val, 1); +} + +static __u8 cxsensor[] = { + 0x88, 0x12, 0x70, 0x01, + 0x88, 0x0d, 0x02, 0x01, + 0x88, 0x0f, 0x00, 0x01, + 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */ + 0x88, 0x02, 0x10, 0x01, + 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */ + 0x88, 0x0B, 0x00, 0x01, + 0x88, 0x0A, 0x0A, 0x01, + 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */ + 0x88, 0x05, 0x01, 0x01, + 0xA1, 0x18, 0x00, 0x01, + 0x00 +}; + +static __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff }; +static __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff }; +static __u8 reg10[] = { 0xb1, 0xb1 }; +static __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; /* 640 */ +static __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f }; + /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */ +static __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 }; + /* 320{0x04,0x0c,0x05,0x0f}; //320 */ +static __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; /* 176 */ +static __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; + +static void cx_sensor(struct gspca_dev*gspca_dev) +{ + __u8 val = 0; + int i = 0; + __u8 bufread[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int length = 0; + __u8 *ptsensor = cxsensor; + + reg_w(gspca_dev->dev, 0x0020, reg20, 8); + reg_w(gspca_dev->dev, 0x0028, reg28, 8); + reg_w(gspca_dev->dev, 0x0010, reg10, 8); + val = 0x03; + reg_w(gspca_dev->dev, 0x0092, &val, 1); + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + reg_w(gspca_dev->dev, 0x0071, reg71a, 4); + break; + case 1: + reg_w(gspca_dev->dev, 0x0071, reg71b, 4); + break; + default: +/* case 2: */ + reg_w(gspca_dev->dev, 0x0071, reg71c, 4); + break; + case 3: + reg_w(gspca_dev->dev, 0x0071, reg71d, 4); + break; + } + reg_w(gspca_dev->dev, 0x007b, reg7b, 6); + val = 0x00; + reg_w(gspca_dev->dev, 0x00f8, &val, 1); + reg_w(gspca_dev->dev, 0x0010, reg10, 8); + val = 0x41; + reg_w(gspca_dev->dev, 0x0098, &val, 1); + for (i = 0; i < 11; i++) { + if (i == 3 || i == 5 || i == 8) + length = 8; + else + length = 4; + reg_w(gspca_dev->dev, 0x00e5, ptsensor, length); + if (length == 4) + reg_r(gspca_dev->dev, 0x00e8, &val, 1); + else + reg_r(gspca_dev->dev, 0x00e8, bufread, length); + ptsensor += length; + } + reg_r(gspca_dev->dev, 0x00e7, bufread, 8); +} + +static __u8 cx_inits_176[] = { + 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */ + 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03, + 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30, + 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF, + 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02, + 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static __u8 cx_inits_320[] = { + 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01, + 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02, + 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static __u8 cx_inits_352[] = { + 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03, + 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b, + 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25, + 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00, + 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02, + 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static __u8 cx_inits_640[] = { + 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01, + 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02, + 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int cx11646_initsize(struct gspca_dev *gspca_dev) +{ + __u8 *cxinit; + __u8 val; + static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 }; + static const __u8 reg17[] = + { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 }; + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + cxinit = cx_inits_640; + break; + case 1: + cxinit = cx_inits_352; + break; + default: +/* case 2: */ + cxinit = cx_inits_320; + break; + case 3: + cxinit = cx_inits_176; + break; + } + val = 0x01; + reg_w(gspca_dev->dev, 0x009a, &val, 1); + val = 0x10; + reg_w(gspca_dev->dev, 0x0010, &val, 1); + reg_w(gspca_dev->dev, 0x0012, reg12, 5); + reg_w(gspca_dev->dev, 0x0017, reg17, 8); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + val = 0x04; + reg_w(gspca_dev->dev, 0x00c1, &val, 1); + val = 0x04; + reg_w(gspca_dev->dev, 0x00c2, &val, 1); + + reg_w(gspca_dev->dev, 0x0061, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x00ca, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x00d2, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x00da, cxinit, 6); + cxinit += 8; + reg_w(gspca_dev->dev, 0x0041, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x0049, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x0051, cxinit, 2); + + reg_r(gspca_dev->dev, 0x0010, &val, 1); + return val; +} + +static __u8 cx_jpeg_init[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15}, /* 1 */ + {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11}, + {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22}, + {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26}, + {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a}, + {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73}, + {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D}, + {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0}, + {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01}, + {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12}, + {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35}, + {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31}, + {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43}, + {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A}, + {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73}, + {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95}, + {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83}, + {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, + {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, + {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, + {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00}, + {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05}, + {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01}, + {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21}, + {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22}, + {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23}, + {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24}, + {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17}, + {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29}, + {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A}, + {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A}, + {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A}, + {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A}, + {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A}, + {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A}, + {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}, + {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8}, + {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, + {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6}, + {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5}, + {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3}, + {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1}, + {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9}, + {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04}, + {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01}, + {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04}, + {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07}, + {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14}, + {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33}, + {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16}, + {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19}, + {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36}, + {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46}, + {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56}, + {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66}, + {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76}, + {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85}, + {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94}, + {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3}, + {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2}, + {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA}, + {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9}, + {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8}, + {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7}, + {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6}, + {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F}, + {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22}, + {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11}, + {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00}, + {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08}, + {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00}, + {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA}, + {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02}, + {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} /* 79 */ +}; + + +static __u8 cxjpeg_640[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10}, /* 1 */ + {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d}, + {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a}, + {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d}, + {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38}, + {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57}, + {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F}, + {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79}, + {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01}, + {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E}, + {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28}, + {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25}, + {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33}, + {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44}, + {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57}, + {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71}, + {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63}, + {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static __u8 cxjpeg_352[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a}, + {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +static __u8 cxjpeg_320[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05}, + {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04}, + {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08}, + {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09}, + {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11}, + {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A}, + {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D}, + {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24}, + {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01}, + {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04}, + {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C}, + {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B}, + {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F}, + {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14}, + {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A}, + {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22}, + {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E}, + {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static __u8 cxjpeg_176[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, + {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +static __u8 cxjpeg_qtable[][8] = { /* 640 take with the zcx30x part */ + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08}, + {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07}, + {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a}, + {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f}, + {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c}, + {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c}, + {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30}, + {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d}, + {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01}, + {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a}, + {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 18 */ +}; + + +static void cx11646_jpegInit(struct gspca_dev*gspca_dev) +{ + __u8 val; + int i; + int length; + + val = 0x01; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c3, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + reg_r(gspca_dev->dev, 0x0001, &val, 1); + length = 8; + for (i = 0; i < 79; i++) { + if (i == 78) + length = 6; + reg_w(gspca_dev->dev, 0x0008, cx_jpeg_init[i], length); + } + reg_r(gspca_dev->dev, 0x0002, &val, 1); + val = 0x14; + reg_w(gspca_dev->dev, 0x0055, &val, 1); +} + +static __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 }; +static __u8 regE5_8[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; +static __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 }; +static __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 }; +static __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 }; +static __u8 reg51[] = { 0x77, 0x03 }; +static __u8 reg70 = 0x03; + +static void cx11646_jpeg(struct gspca_dev*gspca_dev) +{ + __u8 val; + int i; + int length = 8; + __u8 Reg55 = 0x14; + __u8 bufread[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int retry = 50; + + val = 0x01; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c3, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + reg_r(gspca_dev->dev, 0x0001, &val, 1); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_640[i], length); + } + Reg55 = 0x28; + break; + case 1: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_352[i], length); + } + Reg55 = 0x16; + break; + default: +/* case 2: */ + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_320[i], length); + } + Reg55 = 0x14; + break; + case 3: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_176[i], length); + } + Reg55 = 0x0B; + break; + } + + reg_r(gspca_dev->dev, 0x0002, &val, 1); + val = Reg55; + reg_w(gspca_dev->dev, 0x0055, &val, 1); + reg_r(gspca_dev->dev, 0x0002, &val, 1); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + val = 0x02; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x01; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x94; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + val = 0xc0; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + val = 0xe1; + reg_w(gspca_dev->dev, 0x00fc, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + /* wait for completion */ + while (retry--) { + reg_r(gspca_dev->dev, 0x0002, &val, 1); + /* 0x07 until 0x00 */ + if (val == 0x00) + break; + val = 0x00; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + } + if (retry == 0) + PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); + /* send the qtable now */ + reg_r(gspca_dev->dev, 0x0001, &val, 1); /* -> 0x18 */ + length = 8; + for (i = 0; i < 18; i++) { + if (i == 17) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_qtable[i], length); + + } + reg_r(gspca_dev->dev, 0x0002, &val, 1); /* 0x00 */ + reg_r(gspca_dev->dev, 0x0053, &val, 1); /* 0x00 */ + val = 0x02; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x01; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x94; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + val = 0xc0; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + + reg_r(gspca_dev->dev, 0x0038, &val, 1); /* 0x40 */ + reg_r(gspca_dev->dev, 0x0038, &val, 1); /* 0x40 */ + reg_r(gspca_dev->dev, 0x001f, &val, 1); /* 0x38 */ + reg_w(gspca_dev->dev, 0x0012, reg12, 5); + reg_w(gspca_dev->dev, 0x00e5, regE5_8, 8); + reg_r(gspca_dev->dev, 0x00e8, bufread, 8); + reg_w(gspca_dev->dev, 0x00e5, regE5a, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + val = 0x01; + reg_w(gspca_dev->dev, 0x009a, &val, 1); + reg_w(gspca_dev->dev, 0x00e5, regE5b, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + reg_w(gspca_dev->dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + + reg_w(gspca_dev->dev, 0x0051, reg51, 2); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + reg_w(gspca_dev->dev, 0x0070, ®70, 1); +} + +static void cx11646_init1(struct gspca_dev *gspca_dev) +{ + __u8 val; + int i = 0; + + val = 0; + reg_w(gspca_dev->dev, 0x0010, &val, 1); + reg_w(gspca_dev->dev, 0x0053, &val, 1); + reg_w(gspca_dev->dev, 0x0052, &val, 1); + val = 0x2f; + reg_w(gspca_dev->dev, 0x009b, &val, 1); + val = 0x10; + reg_w(gspca_dev->dev, 0x009c, &val, 1); + reg_r(gspca_dev->dev, 0x0098, &val, 1); + val = 0x40; + reg_w(gspca_dev->dev, 0x0098, &val, 1); + reg_r(gspca_dev->dev, 0x0099, &val, 1); + val = 0x07; + reg_w(gspca_dev->dev, 0x0099, &val, 1); + val = 0x40; + reg_w(gspca_dev->dev, 0x0039, &val, 1); + val = 0xff; + reg_w(gspca_dev->dev, 0x003c, &val, 1); + val = 0x1f; + reg_w(gspca_dev->dev, 0x003f, &val, 1); + val = 0x40; + reg_w(gspca_dev->dev, 0x003d, &val, 1); +/* val= 0x60; */ +/* reg_w(gspca_dev->dev,0x00,0x00,0x003d,&val,1); */ + reg_r(gspca_dev->dev, 0x0099, &val, 1); /* ->0x07 */ + + while (cx_sensor_init[i][0]) { + reg_w(gspca_dev->dev, 0x00e5, cx_sensor_init[i], 1); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* -> 0x00 */ + if (i == 1) { + val = 1; + reg_w(gspca_dev->dev, 0x00ed, &val, 1); + reg_r(gspca_dev->dev, 0x00ed, &val, 1); /* -> 0x01 */ + } + i++; + } + val = 0x00; + reg_w(gspca_dev->dev, 0x00c3, &val, 1); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + + sd->qindex = 0; /* set the quantization table */ + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + cx11646_init1(gspca_dev); + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpegInit(gspca_dev); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpeg(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int retry = 50; + __u8 val; + + val = 0; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + reg_r(gspca_dev->dev, 0x0002, &val, 1); + val = 0; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + + while (retry--) { +/* reg_r (gspca_dev->dev,0x00,0x00,0x0002,&val,1);*/ + reg_r(gspca_dev->dev, 0x0053, &val, 1); + if (val == 0) + break; + } + val = 0; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + reg_r(gspca_dev->dev, 0x0002, &val, 1); + + val = 0; + reg_w(gspca_dev->dev, 0x0010, &val, 1); + reg_r(gspca_dev->dev, 0x0033, &val, 1); + val = 0xe0; + reg_w(gspca_dev->dev, 0x00fc, &val, 1); +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + if (data[0] == 0xff && data[1] == 0xd8) { + + /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + data += 2; + len -= 2; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void setbrightness(struct gspca_dev*gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; + __u8 reg51c[] = { 0x77, 0x03 }; + __u8 bright; + __u8 colors; + __u8 val; + __u8 bufread[8]; + + bright = sd->brightness; + colors = sd->colors; + regE5cbx[2] = bright; + reg51c[1] = colors; + reg_w(gspca_dev->dev, 0x00e5, regE5cbx, 8); + reg_r(gspca_dev->dev, 0x00e8, bufread, 8); + reg_w(gspca_dev->dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + + reg_w(gspca_dev->dev, 0x0051, reg51c, 2); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + reg_w(gspca_dev->dev, 0x0070, ®70, 1); +} + +static void setcontrast(struct gspca_dev*gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */ + /* __u8 regE5bcx[]={0x88,0x0b,0x12,0x01}; // LSB */ + __u8 reg51c[] = { 0x77, 0x03 }; + __u8 val; + + reg51c[1] = sd->colors; + regE5acx[2] = sd->contrast; + reg_w(gspca_dev->dev, 0x00e5, regE5acx, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + reg_w(gspca_dev->dev, 0x0051, reg51c, 2); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + reg_w(gspca_dev->dev, 0x0070, ®70, 1); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) { + setbrightness(gspca_dev); + setcontrast(gspca_dev); + } + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0572, 0x0041), DVNM("Creative Notebook cx11646")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c new file mode 100644 index 000000000000..c479f638413e --- /dev/null +++ b/drivers/media/video/gspca/etoms.c @@ -0,0 +1,1062 @@ +/* + * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "etoms" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("Etoms USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char sensor; +#define SENSOR_PAS106 0 +#define SENSOR_TAS5130CXX 1 + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 1, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 7, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_SBGGR8, 320, 240, 1}, +/* {V4L2_PIX_FMT_SBGGR8, 640, 480, 0}, */ +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, + {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, +}; + +#define ETOMS_ALT_SIZE_1000 12 + +#define ET_GPIO_DIR_CTRL 0x04 /* Control IO bit[0..5] (0 in 1 out) */ +#define ET_GPIO_OUT 0x05 /* Only IO data */ +#define ET_GPIO_IN 0x06 /* Read Only IO data */ +#define ET_RESET_ALL 0x03 +#define ET_ClCK 0x01 +#define ET_CTRL 0x02 /* enable i2c OutClck Powerdown mode */ + +#define ET_COMP 0x12 /* Compression register */ +#define ET_MAXQt 0x13 +#define ET_MINQt 0x14 +#define ET_COMP_VAL0 0x02 +#define ET_COMP_VAL1 0x03 + +#define ET_REG1d 0x1d +#define ET_REG1e 0x1e +#define ET_REG1f 0x1f +#define ET_REG20 0x20 +#define ET_REG21 0x21 +#define ET_REG22 0x22 +#define ET_REG23 0x23 +#define ET_REG24 0x24 +#define ET_REG25 0x25 +/* base registers for luma calculation */ +#define ET_LUMA_CENTER 0x39 + +#define ET_G_RED 0x4d +#define ET_G_GREEN1 0x4e +#define ET_G_BLUE 0x4f +#define ET_G_GREEN2 0x50 +#define ET_G_GR_H 0x51 +#define ET_G_GB_H 0x52 + +#define ET_O_RED 0x34 +#define ET_O_GREEN1 0x35 +#define ET_O_BLUE 0x36 +#define ET_O_GREEN2 0x37 + +#define ET_SYNCHRO 0x68 +#define ET_STARTX 0x69 +#define ET_STARTY 0x6a +#define ET_WIDTH_LOW 0x6b +#define ET_HEIGTH_LOW 0x6c +#define ET_W_H_HEIGTH 0x6d + +#define ET_REG6e 0x6e /* OBW */ +#define ET_REG6f 0x6f /* OBW */ +#define ET_REG70 0x70 /* OBW_AWB */ +#define ET_REG71 0x71 /* OBW_AWB */ +#define ET_REG72 0x72 /* OBW_AWB */ +#define ET_REG73 0x73 /* Clkdelay ns */ +#define ET_REG74 0x74 /* test pattern */ +#define ET_REG75 0x75 /* test pattern */ + +#define ET_I2C_CLK 0x8c +#define ET_PXL_CLK 0x60 + +#define ET_I2C_BASE 0x89 +#define ET_I2C_COUNT 0x8a +#define ET_I2C_PREFETCH 0x8b +#define ET_I2C_REG 0x88 +#define ET_I2C_DATA7 0x87 +#define ET_I2C_DATA6 0x86 +#define ET_I2C_DATA5 0x85 +#define ET_I2C_DATA4 0x84 +#define ET_I2C_DATA3 0x83 +#define ET_I2C_DATA2 0x82 +#define ET_I2C_DATA1 0x81 +#define ET_I2C_DATA0 0x80 + +#define PAS106_REG2 0x02 /* pxlClk = systemClk/(reg2) */ +#define PAS106_REG3 0x03 /* line/frame H [11..4] */ +#define PAS106_REG4 0x04 /* line/frame L [3..0] */ +#define PAS106_REG5 0x05 /* exposure time line offset(default 5) */ +#define PAS106_REG6 0x06 /* exposure time pixel offset(default 6) */ +#define PAS106_REG7 0x07 /* signbit Dac (default 0) */ +#define PAS106_REG9 0x09 +#define PAS106_REG0e 0x0e /* global gain [4..0](default 0x0e) */ +#define PAS106_REG13 0x13 /* end i2c write */ + +static __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + +static __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d }; + +static __u8 I2c3[] = { 0x12, 0x05 }; + +static __u8 I2c4[] = { 0x41, 0x08 }; + +static void Et_RegRead(struct usb_device *dev, + __u16 index, __u8 *buffer, int len) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, index, buffer, len, 500); +} + +static void Et_RegWrite(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 len) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, index, buffer, len, 500); +} + +static int Et_i2cwrite(struct usb_device *dev, __u8 reg, __u8 * buffer, + __u16 length, __u8 mode) +{ +/* buffer should be [D0..D7] */ + int i, j; + __u8 base = 0x40; /* sensor base for the pas106 */ + __u8 ptchcount = 0; + + ptchcount = (((length & 0x07) << 4) | (mode & 0x03)); +/* set the base address */ + Et_RegWrite(dev, ET_I2C_BASE, &base, 1); +/* set count and prefetch */ + Et_RegWrite(dev, ET_I2C_COUNT, &ptchcount, 1); +/* set the register base */ + Et_RegWrite(dev, ET_I2C_REG, ®, 1); + j = length - 1; + for (i = 0; i < length; i++) { + Et_RegWrite(dev, (ET_I2C_DATA0 + j), &buffer[j], 1); + j--; + } + return 0; +} + +static int Et_i2cread(struct usb_device *dev, __u8 reg, __u8 * buffer, + __u16 length, __u8 mode) +{ +/* buffer should be [D0..D7] */ + int i, j; + __u8 base = 0x40; /* sensor base for the pas106 */ + __u8 ptchcount; + __u8 prefetch = 0x02; + + ptchcount = (((length & 0x07) << 4) | (mode & 0x03)); +/* set the base address */ + Et_RegWrite(dev, ET_I2C_BASE, &base, 1); +/* set count and prefetch */ + Et_RegWrite(dev, ET_I2C_COUNT, &ptchcount, 1); +/* set the register base */ + Et_RegWrite(dev, ET_I2C_REG, ®, 1); + Et_RegWrite(dev, ET_I2C_PREFETCH, &prefetch, 1); + prefetch = 0x00; + Et_RegWrite(dev, ET_I2C_PREFETCH, &prefetch, 1); + j = length - 1; + for (i = 0; i < length; i++) { + Et_RegRead(dev, (ET_I2C_DATA0 + j), &buffer[j], 1); + j--; + } + return 0; +} + +static int Et_WaitStatus(struct usb_device *dev) +{ + __u8 bytereceived; + int retry = 10; + + while (retry--) { + Et_RegRead(dev, ET_ClCK, &bytereceived, 1); + if (bytereceived != 0) + return 1; + } + return 0; +} + +static int Et_videoOff(struct usb_device *dev) +{ + int err; + __u8 stopvideo = 0; + + Et_RegWrite(dev, ET_GPIO_OUT, &stopvideo, 1); + err = Et_WaitStatus(dev); + if (!err) + PDEBUG(D_ERR, "timeout Et_waitStatus VideoON"); + return err; +} + +static int Et_videoOn(struct usb_device *dev) +{ + int err; + __u8 startvideo = 0x10; /* set Bit5 */ + + Et_RegWrite(dev, ET_GPIO_OUT, &startvideo, 1); + err = Et_WaitStatus(dev); + if (!err) + PDEBUG(D_ERR, "timeout Et_waitStatus VideoOFF"); + return err; +} + +static void Et_init2(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 value = 0x00; + __u8 received = 0x00; + __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; + + PDEBUG(D_STREAM, "Open Init2 ET"); + value = 0x2f; + Et_RegWrite(dev, ET_GPIO_DIR_CTRL, &value, 1); + value = 0x10; + Et_RegWrite(dev, ET_GPIO_OUT, &value, 1); + Et_RegRead(dev, ET_GPIO_IN, &received, 1); + value = 0x14; /* 0x14 // 0x16 enabled pattern */ + Et_RegWrite(dev, ET_ClCK, &value, 1); + value = 0x1b; + Et_RegWrite(dev, ET_CTRL, &value, 1); + + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) + value = ET_COMP_VAL1; /* 320 */ + else + value = ET_COMP_VAL0; /* 640 */ + Et_RegWrite(dev, ET_COMP, &value, 1); + value = 0x1f; + Et_RegWrite(dev, ET_MAXQt, &value, 1); + value = 0x04; + Et_RegWrite(dev, ET_MINQt, &value, 1); + /* undocumented registers */ + value = 0xff; + Et_RegWrite(dev, ET_REG1d, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1e, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1f, &value, 1); + value = 0x35; + Et_RegWrite(dev, ET_REG20, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG21, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG22, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG23, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG24, &value, 1); + value = 0x0f; + Et_RegWrite(dev, ET_REG25, &value, 1); + /* colors setting */ + value = 0x11; + Et_RegWrite(dev, 0x30, &value, 1); /* 0x30 */ + value = 0x40; + Et_RegWrite(dev, 0x31, &value, 1); + value = 0x00; + Et_RegWrite(dev, 0x32, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_O_RED, &value, 1); /* 0x34 */ + value = 0x00; + Et_RegWrite(dev, ET_O_GREEN1, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_O_BLUE, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_O_GREEN2, &value, 1); + /*************/ + value = 0x80; + Et_RegWrite(dev, ET_G_RED, &value, 1); /* 0x4d */ + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN1, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_BLUE, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN2, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GR_H, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GB_H, &value, 1); /* 0x52 */ + /* Window control registers */ + + value = 0x80; /* use cmc_out */ + Et_RegWrite(dev, 0x61, &value, 1); + + value = 0x02; + Et_RegWrite(dev, 0x62, &value, 1); + value = 0x03; + Et_RegWrite(dev, 0x63, &value, 1); + value = 0x14; + Et_RegWrite(dev, 0x64, &value, 1); + value = 0x0e; + Et_RegWrite(dev, 0x65, &value, 1); + value = 0x02; + Et_RegWrite(dev, 0x66, &value, 1); + value = 0x02; + Et_RegWrite(dev, 0x67, &value, 1); + + /**************************************/ + value = 0x8f; + Et_RegWrite(dev, ET_SYNCHRO, &value, 1); /* 0x68 */ + value = 0x69; /* 0x6a //0x69 */ + Et_RegWrite(dev, ET_STARTX, &value, 1); + value = 0x0d; /* 0x0d //0x0c */ + Et_RegWrite(dev, ET_STARTY, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_WIDTH_LOW, &value, 1); + value = 0xe0; + Et_RegWrite(dev, ET_HEIGTH_LOW, &value, 1); + value = 0x60; + Et_RegWrite(dev, ET_W_H_HEIGTH, &value, 1); /* 6d */ + value = 0x86; + Et_RegWrite(dev, ET_REG6e, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG6f, &value, 1); + value = 0x26; + Et_RegWrite(dev, ET_REG70, &value, 1); + value = 0x7a; + Et_RegWrite(dev, ET_REG71, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG72, &value, 1); + /* Clock Pattern registers ***************** */ + value = 0x00; + Et_RegWrite(dev, ET_REG73, &value, 1); + value = 0x18; /* 0x28 */ + Et_RegWrite(dev, ET_REG74, &value, 1); + value = 0x0f; /* 0x01 */ + Et_RegWrite(dev, ET_REG75, &value, 1); + /**********************************************/ + value = 0x20; + Et_RegWrite(dev, 0x8a, &value, 1); + value = 0x0f; + Et_RegWrite(dev, 0x8d, &value, 1); + value = 0x08; + Et_RegWrite(dev, 0x8e, &value, 1); + /**************************************/ + value = 0x08; + Et_RegWrite(dev, 0x03, &value, 1); + value = 0x03; + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + value = 0xff; + Et_RegWrite(dev, 0x81, &value, 1); + value = 0x00; + Et_RegWrite(dev, 0x80, &value, 1); + value = 0xff; + Et_RegWrite(dev, 0x81, &value, 1); + value = 0x20; + Et_RegWrite(dev, 0x80, &value, 1); + value = 0x01; + Et_RegWrite(dev, 0x03, &value, 1); + value = 0x00; + Et_RegWrite(dev, 0x03, &value, 1); + value = 0x08; + Et_RegWrite(dev, 0x03, &value, 1); + /********************************************/ + + /* Et_RegRead(dev,0x0,ET_I2C_BASE,&received,1); + always 0x40 as the pas106 ??? */ + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + value = 0x04; /* 320 */ + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + /* now set by fifo the FormatLine setting */ + Et_RegWrite(dev, 0x62, FormLine, 6); + } else { /* 640 */ + /* setting PixelClock + 0x03 mean 24/(3+1) = 6 Mhz + 0x05 -> 24/(5+1) = 4 Mhz + 0x0b -> 24/(11+1) = 2 Mhz + 0x17 -> 24/(23+1) = 1 Mhz + */ + value = 0x1e; /* 0x17 */ + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + /* now set by fifo the FormatLine setting */ + Et_RegWrite(dev, 0x62, FormLine, 6); + } + + /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */ + value = 0x47; /* 0x47; */ + Et_RegWrite(dev, 0x81, &value, 1); + value = 0x40; /* 0x40; */ + Et_RegWrite(dev, 0x80, &value, 1); + /* Pedro change */ + /* Brightness change Brith+ decrease value */ + /* Brigth- increase value */ + /* original value = 0x70; */ + value = 0x30; /* 0x20; */ + Et_RegWrite(dev, 0x81, &value, 1); /* set brightness */ + value = 0x20; /* 0x20; */ + Et_RegWrite(dev, 0x80, &value, 1); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + static __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d }; + __u8 i2cflags = 0x01; + /* __u8 green = 0; */ + __u8 colors = sd->colors; + + I2cc[3] = colors; /* red */ + I2cc[0] = 15 - colors; /* blue */ + /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */ + /* I2cc[1] = I2cc[2] = green; */ + if (sd->sensor == SENSOR_PAS106) { + Et_i2cwrite(dev, PAS106_REG13, &i2cflags, 1, 3); + Et_i2cwrite(dev, PAS106_REG9, I2cc, sizeof(I2cc), 1); + } +/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d", + I2cc[3], I2cc[0], green); */ +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + /* __u8 valblue = 0; */ + __u8 valred; + + if (sd->sensor == SENSOR_PAS106) { + /* Et_i2cread(gspca_dev->dev,PAS106_REG9,&valblue,1,1); */ + Et_i2cread(gspca_dev->dev, PAS106_REG9 + 3, &valred, 1, 1); + sd->colors = valred & 0x0f; + } +} + +static void Et_init1(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 value = 0x00; + __u8 received = 0x00; +/* __u8 I2c0 [] ={0x0a,0x12,0x05,0x22,0xac,0x00,0x01,0x00}; */ + __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 }; + /* try 1/120 0x6d 0xcd 0x40 */ +/* __u8 I2c0 [] ={0x0a,0x12,0x05,0xfe,0xfe,0xc0,0x01,0x00}; + * 1/60000 hmm ?? */ + + PDEBUG(D_STREAM, "Open Init1 ET"); + value = 7; + Et_RegWrite(dev, ET_GPIO_DIR_CTRL, &value, 1); + Et_RegRead(dev, ET_GPIO_IN, &received, 1); + value = 1; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + value = 0; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + value = 0x10; + Et_RegWrite(dev, ET_ClCK, &value, 1); + value = 0x19; + Et_RegWrite(dev, ET_CTRL, &value, 1); + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) + value = ET_COMP_VAL1; + else + value = ET_COMP_VAL0; + + PDEBUG(D_STREAM, "Open mode %d Compression %d", + gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode, + value); + Et_RegWrite(dev, ET_COMP, &value, 1); + value = 0x1d; + Et_RegWrite(dev, ET_MAXQt, &value, 1); + value = 0x02; + Et_RegWrite(dev, ET_MINQt, &value, 1); + /* undocumented registers */ + value = 0xff; + Et_RegWrite(dev, ET_REG1d, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1e, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1f, &value, 1); + value = 0x35; + Et_RegWrite(dev, ET_REG20, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG21, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG22, &value, 1); + value = 0xf7; + Et_RegWrite(dev, ET_REG23, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG24, &value, 1); + value = 0x07; + Et_RegWrite(dev, ET_REG25, &value, 1); + /* colors setting */ + value = 0x80; + Et_RegWrite(dev, ET_G_RED, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN1, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_BLUE, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN2, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GR_H, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GB_H, &value, 1); + /* Window control registers */ + value = 0xf0; + Et_RegWrite(dev, ET_SYNCHRO, &value, 1); + value = 0x56; /* 0x56 */ + Et_RegWrite(dev, ET_STARTX, &value, 1); + value = 0x05; /* 0x04 */ + Et_RegWrite(dev, ET_STARTY, &value, 1); + value = 0x60; + Et_RegWrite(dev, ET_WIDTH_LOW, &value, 1); + value = 0x20; + Et_RegWrite(dev, ET_HEIGTH_LOW, &value, 1); + value = 0x50; + Et_RegWrite(dev, ET_W_H_HEIGTH, &value, 1); + value = 0x86; + Et_RegWrite(dev, ET_REG6e, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG6f, &value, 1); + value = 0x86; + Et_RegWrite(dev, ET_REG70, &value, 1); + value = 0x14; + Et_RegWrite(dev, ET_REG71, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG72, &value, 1); + /* Clock Pattern registers */ + value = 0x00; + Et_RegWrite(dev, ET_REG73, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG74, &value, 1); + value = 0x0a; + Et_RegWrite(dev, ET_REG75, &value, 1); + value = 0x04; + Et_RegWrite(dev, ET_I2C_CLK, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + I2c0[0] = 0x06; + Et_i2cwrite(dev, PAS106_REG2, I2c0, sizeof(I2c0), 1); + Et_i2cwrite(dev, PAS106_REG9, I2c2, sizeof(I2c2), 1); + value = 0x06; + Et_i2cwrite(dev, PAS106_REG2, &value, 1, 1); + Et_i2cwrite(dev, PAS106_REG3, I2c3, sizeof(I2c3), 1); + /* value = 0x1f; */ + value = 0x04; + Et_i2cwrite(dev, PAS106_REG0e, &value, 1, 1); + } else { + I2c0[0] = 0x0a; + + Et_i2cwrite(dev, PAS106_REG2, I2c0, sizeof(I2c0), 1); + Et_i2cwrite(dev, PAS106_REG9, I2c2, sizeof(I2c2), 1); + value = 0x0a; + + Et_i2cwrite(dev, PAS106_REG2, &value, 1, 1); + Et_i2cwrite(dev, PAS106_REG3, I2c3, sizeof(I2c3), 1); + value = 0x04; + /* value = 0x10; */ + Et_i2cwrite(dev, PAS106_REG0e, &value, 1, 1); + /* bit 2 enable bit 1:2 select 0 1 2 3 + value = 0x07; * curve 0 * + Et_i2cwrite(dev,PAS106_REG0f,&value,1,1); + */ + } + +/* value = 0x01; */ +/* value = 0x22; */ +/* Et_i2cwrite(dev, PAS106_REG5, &value, 1, 1); */ + /* magnetude and sign bit for DAC */ + Et_i2cwrite(dev, PAS106_REG7, I2c4, sizeof I2c4, 1); + /* now set by fifo the whole colors setting */ + Et_RegWrite(dev, ET_G_RED, GainRGBG, 6); + getcolors(gspca_dev); + setcolors(gspca_dev); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; +/* switch (vendor) { */ +/* case 0x102c: * Etoms */ + switch (product) { + case 0x6151: + sd->sensor = SENSOR_PAS106; /* Etoms61x151 */ + break; + case 0x6251: + sd->sensor = SENSOR_TAS5130CXX; /* Etoms61x251 */ + break; +/* } */ +/* break; */ + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 1; + if (sd->sensor == SENSOR_PAS106) { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } else { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int err; + __u8 value; + + PDEBUG(D_STREAM, "Initialize ET1"); + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + value = 0x08; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + err = Et_videoOff(dev); + PDEBUG(D_STREAM, "Et_Init_VideoOff %d", err); + return 0; +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int err; + __u8 value; + + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + + value = 0x08; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + err = Et_videoOn(dev); + PDEBUG(D_STREAM, "Et_VideoOn %d", err); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int err; + + err = Et_videoOff(gspca_dev->dev); + PDEBUG(D_STREAM, "Et_VideoOff %d", err); + +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + __u8 brightness = sd->brightness; + + for (i = 0; i < 4; i++) + Et_RegWrite(gspca_dev->dev, (ET_O_RED + i), &brightness, 1); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + int brightness = 0; + __u8 value = 0; + + for (i = 0; i < 4; i++) { + Et_RegRead(gspca_dev->dev, (ET_O_RED + i), &value, 1); + brightness += value; + } + sd->brightness = brightness >> 3; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + __u8 contrast = sd->contrast; + + memset(RGBG, contrast, sizeof RGBG - 2); + Et_RegWrite(gspca_dev->dev, ET_G_RED, RGBG, 6); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + int contrast = 0; + __u8 value = 0; + + for (i = 0; i < 4; i++) { + Et_RegRead(gspca_dev->dev, (ET_G_RED + i), &value, 1); + contrast += value; + } + sd->contrast = contrast >> 2; +} + +static __u8 Et_getgainG(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value = 0; + + if (sd->sensor == SENSOR_PAS106) { + Et_i2cread(gspca_dev->dev, PAS106_REG0e, &value, 1, 1); + PDEBUG(D_CONF, "Etoms gain G %d", value); + return value; + } + return 0x1f; +} + +static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 i2cflags = 0x01; + + if (sd->sensor == SENSOR_PAS106) { + Et_i2cwrite(dev, PAS106_REG13, &i2cflags, 1, 3); + Et_i2cwrite(dev, PAS106_REG0e, &gain, 1, 1); + } +} + +#define BLIMIT(bright) \ + (__u8)((bright > 0x1f)?0x1f:((bright < 4)?3:bright)) +#define LIMIT(color) \ + (unsigned char)((color > 0xff)?0xff:((color < 0)?0:color)) + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 GRBG[] = { 0, 0, 0, 0 }; + __u8 luma = 0; + __u8 luma_mean = 128; + __u8 luma_delta = 20; + __u8 spring = 4; + int Gbright = 0; + __u8 r, g, b; + + Gbright = Et_getgainG(gspca_dev); + Et_RegRead(dev, ET_LUMA_CENTER, GRBG, 4); + g = (GRBG[0] + GRBG[3]) >> 1; + r = GRBG[1]; + b = GRBG[2]; + r = ((r << 8) - (r << 4) - (r << 3)) >> 10; + b = ((b << 7) >> 10); + g = ((g << 9) + (g << 7) + (g << 5)) >> 10; + luma = LIMIT(r + g + b); + PDEBUG(D_FRAM, "Etoms luma G %d", luma); + if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + Gbright = BLIMIT(Gbright); + PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright); + Et_setgainG(gspca_dev, (__u8) Gbright); + } +} + +#undef BLIMIT +#undef LIMIT + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd; + int seqframe; + + seqframe = data[0] & 0x3f; + len = (int) (((data[0] & 0xc0) << 2) | data[1]); + if (seqframe == 0x3f) { + PDEBUG(D_FRAM, + "header packet found datalength %d !!", len); + PDEBUG(D_FRAM, "G %d R %d G %d B %d", + data[2], data[3], data[4], data[5]); + data += 30; + /* don't change datalength as the chips provided it */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + sd = (struct sd *) gspca_dev; + if (sd->ag_cnt >= 0) { + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev); + } + } + return; + } + if (len) { + data += 8; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + } else { /* Drop Packet */ + gspca_dev->last_packet_type = DISCARD_PACKET; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x102c, 0x6151), DVNM("Qcam Sangha CIF")}, + {USB_DEVICE(0x102c, 0x6251), DVNM("Qcam xxxxxx VGA")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 423ebbdc4b4f..5583c53e4863 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -35,24 +35,24 @@ #include "gspca.h" -/* option */ -#define GSPCA_HLP 0 +#undef CONFIG_VIDEO_V4L1_COMPAT /* global values */ #define DEF_NURBS 2 /* default number of URBs (mmap) */ +#define USR_NURBS 5 /* default number of URBs (userptr) */ MODULE_AUTHOR("Jean-Francois Moine "); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 15) -static const char version[] = "0.2.15"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; static int video_nr = -1; static int comp_fac = 30; /* Buffer size ratio when compressed in % */ -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG int gspca_debug = D_ERR | D_PROBE; EXPORT_SYMBOL(gspca_debug); @@ -81,224 +81,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 -#ifndef GSPCA_HLP #define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) -#else -#define GSPCA_BUF_FLAG_DECODE 0x1000 /* internal buffer flag */ -#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE \ - | GSPCA_BUF_FLAG_DECODE) - -static int autostart = 4; -module_param(autostart, int, 0644); -MODULE_PARM_DESC(autostart, - "Automatically start the helper process"); - -/* try to start the helper process */ -static void start_hlp(void) -{ - int ret; - static char *argv[] = {"gspca_hlp", NULL}; - static char *env[] = {NULL}; - - if (autostart <= 0) { - if (autostart < 0) - PDEBUG(D_ERR|D_PROBE, "Too many helper restart"); - return; - } - autostart--; - if (autostart == 0) - autostart = -1; - ret = call_usermodehelper("/sbin/gspca_hlp", argv, env, - UMH_WAIT_EXEC); - if (ret != 0) - PDEBUG(D_ERR|D_PROBE, - "/sbin/gspca_hlp start failed %d", ret); -} - -/* /dev/gspca_hlp stuff */ -#include -#include "gspca_hlp.h" - -/* !! possible decodings defined in decoder.c */ -static __u32 bayer_to_tb[] = { - V4L2_PIX_FMT_SBGGR8, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_BGR24, - V4L2_PIX_FMT_RGB565, -}; -static __u32 jpeg_to_tb[] = { - V4L2_PIX_FMT_JPEG, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_BGR24, - V4L2_PIX_FMT_RGB565, -}; - -/* /dev/gspca_hlp device */ -struct hlp_dev { - struct gspca_dev *gspca_dev; /* associated device */ - struct gspca_frame *frame; /* frame being decoded */ - __u32 pixfmt; /* webcam pixel format */ - atomic_t nevent; /* nb of frames ready to decode */ - wait_queue_head_t wq; /* wait queue */ - char fr_d; /* next frame to decode */ -} *hlp; - -static int hlp_open(struct inode *inode, struct file *file) -{ - struct hlp_dev *hlp_dev; - - PDEBUG(D_CONF, "hlp open"); - if (hlp != 0) - return -EBUSY; - hlp_dev = kzalloc(sizeof *hlp_dev, GFP_KERNEL); - if (hlp_dev == NULL) { - err("couldn't kzalloc hlp struct"); - return -EIO; - } - init_waitqueue_head(&hlp_dev->wq); - file->private_data = hlp_dev; - hlp = hlp_dev; - return 0; -} - -static int hlp_close(struct inode *inode, struct file *file) -{ - struct gspca_dev *gspca_dev; - int mode; - - PDEBUG(D_CONF, "hlp close"); - file->private_data = NULL; - - /* stop decoding */ - gspca_dev = hlp->gspca_dev; - if (gspca_dev != 0) { - mode = gspca_dev->curr_mode; - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[mode].pixfmt; - } - - /* destroy the helper structure */ - kfree(hlp); - hlp = 0; - - /* try to restart the helper process */ - start_hlp(); - return 0; -} - -static ssize_t hlp_read(struct file *file, char __user *buf, - size_t cnt, loff_t *ppos) -{ - struct hlp_dev *hlp_dev = file->private_data; - struct gspca_dev *gspca_dev; - struct gspca_frame *frame; - struct gspca_hlp_read_hd head; - int i, j, len, ret; - - PDEBUG(D_FRAM, "hlp read (%d)", cnt); - - /* check / wait till a frame is ready */ - for (;;) { - gspca_dev = hlp_dev->gspca_dev; - if (gspca_dev != 0 && gspca_dev->streaming) { - i = hlp_dev->fr_d; /* frame to decode */ - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - if (frame->v4l2_buf.flags & GSPCA_BUF_FLAG_DECODE) - break; - } - ret = wait_event_interruptible(hlp_dev->wq, - atomic_read(&hlp_dev->nevent) > 0); - if (ret < 0) { /* helper process is killed */ - autostart = 0; /* don't restart it */ - return ret; - } - } - atomic_dec(&hlp_dev->nevent); - hlp_dev->fr_d = (i + 1) % gspca_dev->nframes; - PDEBUG(D_FRAM, "hlp read q:%d i:%d d:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - hlp_dev->fr_d, - gspca_dev->fr_o); - - hlp_dev->frame = frame; /* memorize the current frame */ - len = frame->v4l2_buf.bytesused; - if (cnt < sizeof head - sizeof head.data + len) -/*fixme: special errno?*/ - return -EINVAL; - head.pixfmt_out = gspca_dev->pixfmt; - head.pixfmt_in = hlp_dev->pixfmt; - head.width = gspca_dev->width; - head.height = gspca_dev->height; - copy_to_user(buf, &head, sizeof head); - copy_to_user(buf + sizeof head - sizeof head.data, - frame->data, len); - return sizeof head - sizeof head.data + len; -} - -static ssize_t hlp_write(struct file *file, - const char __user *buf, - size_t cnt, loff_t *ppos) -{ - struct hlp_dev *hlp_dev = file->private_data; - struct gspca_dev *gspca_dev; - struct gspca_frame *frame; - - PDEBUG(D_FRAM, "hlp write (%d)", cnt); - gspca_dev = hlp_dev->gspca_dev; - if (gspca_dev == 0) - return cnt; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - if (!gspca_dev->streaming) - goto out; - frame = hlp_dev->frame; - hlp_dev->frame = 0; - if (frame == 0) - goto out; - if (cnt > frame->v4l2_buf.length) { - PDEBUG(D_ERR|D_FRAM, "bad frame size %d - %d", - cnt, frame->v4l2_buf.length); - cnt = -EINVAL; - goto out; - } - copy_from_user(frame->data, buf, cnt); - frame->v4l2_buf.bytesused = cnt; - frame->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_QUEUED - | GSPCA_BUF_FLAG_DECODE); - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - mutex_unlock(&gspca_dev->queue_lock); - atomic_inc(&gspca_dev->nevent); - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ - PDEBUG(D_FRAM, "hlp write q:%d i:%d d:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - hlp_dev->fr_d, - gspca_dev->fr_o); - return cnt; -out: - mutex_unlock(&gspca_dev->queue_lock); - return cnt; -} - -static struct file_operations hlp_fops = { - .owner = THIS_MODULE, - .open = hlp_open, - .release = hlp_close, - .read = hlp_read, - .write = hlp_write, - .llseek = no_llseek -}; -static struct miscdevice hlp_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "gspca_hlp", - .fops = &hlp_fops, -}; -#endif /* * VMA operations. @@ -331,10 +114,14 @@ static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { struct gspca_frame *frame; - unsigned char *data; /* address of data in the iso message */ + __u8 *data; /* address of data in the iso message */ int i, j, len, st; cam_pkt_op pkt_scan; + if (urb->status != 0) { + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + return; /* disconnection ? */ + } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { @@ -350,20 +137,21 @@ static void fill_frame(struct gspca_dev *gspca_dev, /* check the packet status and length */ len = urb->iso_frame_desc[i].actual_length; + if (len == 0) + continue; st = urb->iso_frame_desc[i].status; if (st) { - PDEBUG(D_ERR, "ISOC data error: [%d] len=%d, status=%d", + PDEBUG(D_ERR, + "ISOC data error: [%d] len=%d, status=%d", i, len, st); gspca_dev->last_packet_type = DISCARD_PACKET; continue; } - if (len == 0) - continue; /* let the packet be analyzed by the subdriver */ PDEBUG(D_PACK, "packet [%d] o:%d l:%d", i, urb->iso_frame_desc[i].offset, len); - data = (unsigned char *) urb->transfer_buffer + data = (__u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; pkt_scan(gspca_dev, frame, data, len); } @@ -390,7 +178,8 @@ static void fill_frame(struct gspca_dev *gspca_dev, * buffers are in user space (userptr). The frame detection * and copy is done by the application. */ -static void isoc_irq_mmap(struct urb *urb) +static void isoc_irq_mmap(struct urb *urb +) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; @@ -400,7 +189,8 @@ static void isoc_irq_mmap(struct urb *urb) fill_frame(gspca_dev, urb); } -static void isoc_irq_user(struct urb *urb) +static void isoc_irq_user(struct urb *urb +) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; int i; @@ -459,7 +249,7 @@ static void isoc_transfer(struct gspca_dev *gspca_dev) struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, int packet_type, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len) { int i, j; @@ -503,23 +293,10 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, /* if last packet, wake the application and advance in the queue */ if (packet_type == LAST_PACKET) { frame->v4l2_buf.bytesused = frame->data_end - frame->data; -#ifndef GSPCA_HLP frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; atomic_inc(&gspca_dev->nevent); wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ -#else /*GSPCA_HLP*/ - if (hlp != 0 && hlp->gspca_dev == gspca_dev) { - frame->v4l2_buf.flags |= GSPCA_BUF_FLAG_DECODE; - atomic_inc(&hlp->nevent); - wake_up_interruptible(&hlp->wq); - } else { - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - atomic_inc(&gspca_dev->nevent); - wake_up_interruptible(&gspca_dev->wq); /* new frame */ - } -#endif /*GSPCA_HLP*/ i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; gspca_dev->fr_i = i; PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d", @@ -581,13 +358,13 @@ static void rvfree(void *mem, unsigned long size) static __u32 get_v4l2_depth(__u32 pixfmt) { switch (pixfmt) { - case V4L2_PIX_FMT_BGR32: +/* case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_RGB32: - return 32; + return 32; */ case V4L2_PIX_FMT_RGB24: /* 'RGB3' */ case V4L2_PIX_FMT_BGR24: return 24; - case V4L2_PIX_FMT_RGB565: /* 'RGBP' */ +/* case V4L2_PIX_FMT_RGB565: * 'RGBP' */ case V4L2_PIX_FMT_YUYV: /* 'YUYV' packed 4.2.2 */ case V4L2_PIX_FMT_YYUV: /* 'YYUV' */ return 16; @@ -596,6 +373,9 @@ static __u32 get_v4l2_depth(__u32 pixfmt) case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SBGGR8: /* 'BA81' Bayer */ + case V4L2_PIX_FMT_SN9C10X: /* 'S910' SN9C10x compression */ + case V4L2_PIX_FMT_SPCA501: /* 'S501' YUYV per line */ + case V4L2_PIX_FMT_SPCA561: /* 'S561' compressed BGGR bayer */ return 8; } PDEBUG(D_ERR|D_CONF, "Unknown pixel format %c%c%c%c", @@ -603,7 +383,7 @@ static __u32 get_v4l2_depth(__u32 pixfmt) (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, pixfmt >> 24); - return -EINVAL; + return 24; } static int gspca_get_buff_size(struct gspca_dev *gspca_dev) @@ -632,7 +412,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, count = GSPCA_MAX_FRAMES; /* if compressed (JPEG), reduce the buffer size */ if (gspca_is_compressed(gspca_dev->pixfmt)) - frsz = (frsz * comp_fac) / 100 + 600; /* plus JPEG header */ + frsz = (frsz * comp_fac) / 100 + 600; /* (+ JPEG header sz) */ frsz = PAGE_ALIGN(frsz); PDEBUG(D_STREAM, "new fr_sz: %d", frsz); gspca_dev->frsz = frsz; @@ -660,17 +440,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev, } } gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; -#ifdef GSPCA_HLP - { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp != 0 && hlp_dev->gspca_dev == gspca_dev) { - hlp_dev->fr_d = 0; - atomic_set(&hlp_dev->nevent, 0); - } - } -#endif /*GSPCA_HLP*/ gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; atomic_set(&gspca_dev->nevent, 0); @@ -752,13 +521,14 @@ struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) int i, ret; intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + ep = NULL; i = gspca_dev->alt; /* previous alt setting */ while (--i > 0) { /* alt 0 is unusable */ ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr); if (ep) break; } - if (i <= 0) { + if (ep == NULL) { err("no ISOC endpoint found"); return NULL; } @@ -796,11 +566,14 @@ static int create_urbs(struct gspca_dev *gspca_dev, "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize); /*fixme:change for userptr*/ /*fixme:don't submit all URBs when userptr*/ - gspca_dev->nurbs = nurbs = DEF_NURBS; - if (gspca_dev->memory == V4L2_MEMORY_MMAP) + if (gspca_dev->memory == V4L2_MEMORY_MMAP) { usb_complete = isoc_irq_mmap; - else + nurbs = DEF_NURBS; + } else { usb_complete = isoc_irq_user; + nurbs = USR_NURBS; + } + gspca_dev->nurbs = nurbs; for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { @@ -904,16 +677,6 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; atomic_set(&gspca_dev->nevent, 0); -#ifdef GSPCA_HLP - { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp_dev != 0 - && hlp_dev->gspca_dev == gspca_dev) - atomic_set(&hlp_dev->nevent, 0); - } -#endif if (gspca_dev->present) { gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); @@ -979,15 +742,11 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { struct gspca_dev *gspca_dev = priv; - int i; -#ifndef GSPCA_HLP - int j, index; + int i, j, index; __u32 fmt_tb[8]; -#endif PDEBUG(D_CONF, "enum fmt cap"); -#ifndef GSPCA_HLP /* give an index to each format */ index = 0; j = 0; @@ -1013,36 +772,6 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv, fmtdesc->pixelformat = fmt_tb[index]; if (gspca_is_compressed(fmt_tb[index])) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; -#else /*GSPCA_HLP*/ - /* !! code tied to the decoding functions in decoder.c */ - i = gspca_dev->cam.nmodes - 1; - if (fmtdesc->index == 0) { /* (assume one format per subdriver) */ - fmtdesc->pixelformat = gspca_dev->cam.cam_mode[i].pixfmt; - if (gspca_is_compressed(fmtdesc->pixelformat)) - fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; - } else { - if (hlp == 0 - || (hlp->gspca_dev != 0 - && hlp->gspca_dev != gspca_dev)) - return -EINVAL; - switch (gspca_dev->cam.cam_mode[i].pixfmt) { - case V4L2_PIX_FMT_JPEG: - if (fmtdesc->index >= sizeof jpeg_to_tb - / sizeof jpeg_to_tb[0]) - return -EINVAL; - fmtdesc->pixelformat = jpeg_to_tb[fmtdesc->index]; - break; - case V4L2_PIX_FMT_SBGGR8: - if (fmtdesc->index >= sizeof bayer_to_tb - / sizeof bayer_to_tb[0]) - return -EINVAL; - fmtdesc->pixelformat = bayer_to_tb[fmtdesc->index]; - break; - default: - return -EINVAL; - } - } -#endif /*GSPCA_HLP*/ fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; @@ -1057,25 +786,12 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, { struct gspca_dev *gspca_dev = priv; -#ifdef GSPCA_HLP - int i; - - /* if the pixel format is not the one of the device and - * if the helper is inactive or busy, restore */ - i = gspca_dev->curr_mode; - if (gspca_dev->pixfmt != gspca_dev->cam.cam_mode[i].pixfmt) { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp_dev == 0 || hlp_dev->gspca_dev != gspca_dev) - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixfmt; - } -#endif /*GSPCA_HLP*/ - + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; fmt->fmt.pix.width = gspca_dev->width; fmt->fmt.pix.height = gspca_dev->height; fmt->fmt.pix.pixelformat = gspca_dev->pixfmt; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) { PDEBUG_MODE("get fmt cap", fmt->fmt.pix.pixelformat, @@ -1099,13 +815,15 @@ static int try_fmt_cap(struct gspca_dev *gspca_dev, { int w, h, mode, mode2, frsz; + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; /* (luvcview problem) */ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h); #endif @@ -1121,44 +839,11 @@ static int try_fmt_cap(struct gspca_dev *gspca_dev, if (mode2 >= 0) { mode = mode2; } else { - __u32 pixfmt; - - pixfmt = gspca_dev->cam.cam_mode[mode].pixfmt; -#ifndef GSPCA_HLP /* no chance, return this mode */ - fmt->fmt.pix.pixelformat = pixfmt; -#else /*GSPCA_HLP*/ - if (hlp != 0 - && (hlp->gspca_dev == 0 - || hlp->gspca_dev == gspca_dev) -/* decoding works for JPEG and Bayer only */ - && (pixfmt == V4L2_PIX_FMT_JPEG - || pixfmt == V4L2_PIX_FMT_SBGGR8)) { - switch (fmt->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: /* 'YUYV' */ - case V4L2_PIX_FMT_BGR24: /* 'BGR3' */ - case V4L2_PIX_FMT_RGB24: /* 'RGB3' */ - case V4L2_PIX_FMT_YUV420: /* 'YU12' */ - case V4L2_PIX_FMT_RGB565: /* 'RGBP' */ - break; - default: { - /* return any of the supported fmt's */ - __u8 u; - - u = get_jiffies_64(); - u %= sizeof bayer_to_tb - / sizeof bayer_to_tb[0] - 1; - fmt->fmt.pix.pixelformat = - bayer_to_tb[u + 1]; - break; - } - } - } else { - fmt->fmt.pix.pixelformat = pixfmt; - } -#endif /*GSPCA_HLP*/ -#ifdef GSPCA_DEBUG + fmt->fmt.pix.pixelformat = + gspca_dev->cam.cam_mode[mode].pixfmt; +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) { PDEBUG_MODE("new format", fmt->fmt.pix.pixelformat, @@ -1198,7 +883,17 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int ret; -#ifdef GSPCA_DEBUG +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /* if v4l1 got JPEG */ + if (fmt->fmt.pix.pixelformat == 0 + && gspca_dev->streaming) { + fmt->fmt.pix.width = gspca_dev->width; + fmt->fmt.pix.height = gspca_dev->height; + fmt->fmt.pix.pixelformat = gspca_dev->pixfmt; + return 0; + } +#endif +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) { PDEBUG_MODE("set fmt cap", fmt->fmt.pix.pixelformat, @@ -1218,14 +913,8 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, goto out; } -#ifndef GSPCA_HLP if (ret == gspca_dev->curr_mode) goto out; /* same mode */ -#else /*GSPCA_HLP*/ - if (ret == gspca_dev->curr_mode - && gspca_dev->pixfmt == fmt->fmt.pix.pixelformat) - goto out; /* same mode */ -#endif /*GSPCA_HLP*/ if (gspca_dev->streaming) { ret = -EBUSY; @@ -1236,26 +925,6 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; gspca_dev->curr_mode = ret; -#ifdef GSPCA_HLP - /* if frame decoding is required */ - if (gspca_dev->pixfmt != gspca_dev->cam.cam_mode[ret].pixfmt) { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp_dev == 0 - || (hlp_dev->gspca_dev != 0 - && hlp_dev->gspca_dev != gspca_dev)) { /* helper busy */ - fmt->fmt.pix.pixelformat = - gspca_dev->pixfmt = - gspca_dev->cam.cam_mode[ret].pixfmt; - } else { /* helper active */ - hlp_dev->gspca_dev = gspca_dev; - hlp_dev->pixfmt = gspca_dev->cam.cam_mode[ret].pixfmt; - hlp_dev->fr_d = gspca_dev->fr_i; - } - } else if (hlp != 0 && hlp->gspca_dev == gspca_dev) - hlp->gspca_dev = 0; -#endif /*GSPCA_HLP*/ ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1294,7 +963,7 @@ static int dev_open(struct inode *inode, struct file *file) } gspca_dev->users++; file->private_data = gspca_dev; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) gspca_dev->vdev.debug |= 3; @@ -1329,22 +998,6 @@ static int dev_close(struct inode *inode, struct file *file) frame_free(gspca_dev); gspca_dev->capt_file = 0; gspca_dev->memory = GSPCA_MEMORY_NO; -#ifdef GSPCA_HLP - { - struct hlp_dev *hlp_dev; - int mode; - - hlp_dev = hlp; - if (hlp_dev != 0 - && hlp_dev->gspca_dev == gspca_dev) { - hlp_dev->gspca_dev = 0; - hlp_dev->frame = 0; - mode = gspca_dev->curr_mode; - gspca_dev->pixfmt = - gspca_dev->cam.cam_mode[mode].pixfmt; - } - } -#endif /*GSPCA_HLP*/ } file->private_data = NULL; mutex_unlock(&gspca_dev->queue_lock); @@ -1370,23 +1023,38 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } +/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { struct gspca_dev *gspca_dev = priv; int i; - - PDEBUG(D_CONF, "queryctrl"); + u32 id; + + id = q_ctrl->id; + if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { + id &= V4L2_CTRL_ID_MASK; + id++; + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) { + memcpy(q_ctrl, + &gspca_dev->sd_desc->ctrls[i].qctrl, + sizeof *q_ctrl); + return 0; + } + } + return -EINVAL; + } for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (q_ctrl->id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { + if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[i].qctrl, sizeof *q_ctrl); return 0; } } - if (q_ctrl->id >= V4L2_CID_BASE - && q_ctrl->id <= V4L2_CID_LASTP1) { + if (id >= V4L2_CID_BASE + && id <= V4L2_CID_LASTP1) { q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; } @@ -1489,13 +1157,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; switch (rb->memory) { case V4L2_MEMORY_MMAP: - break; case V4L2_MEMORY_USERPTR: -#ifdef GSPCA_HLP - if (hlp == 0 || hlp->gspca_dev != gspca_dev) - break; -#endif - return -EINVAL; + break; default: return -EINVAL; } @@ -1578,7 +1241,7 @@ static int vidioc_streamon(struct file *file, void *priv, if (ret < 0) goto out; } -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_STREAM) { PDEBUG_MODE("stream on OK", gspca_dev->pixfmt, @@ -1657,7 +1320,7 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = priv; - memset(parm, 0, sizeof parm); + memset(parm, 0, sizeof *parm); parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; return 0; @@ -1677,6 +1340,12 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } +static int vidioc_s_std(struct file *filp, void *priv, + v4l2_std_id *parm) +{ + return 0; +} + #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) @@ -1686,29 +1355,32 @@ static int vidiocgmbuf(struct file *file, void *priv, PDEBUG(D_STREAM, "cgmbuf"); if (gspca_dev->nframes == 0) { - struct v4l2_requestbuffers rb; int ret; - __u32 pixfmt; - short width, height; - - /* as the final format is not yet defined, allocate - buffers with the max size */ - pixfmt = gspca_dev->pixfmt; - width = gspca_dev->width; - height = gspca_dev->height; - gspca_dev->pixfmt = V4L2_PIX_FMT_BGR32; - gspca_dev->width = 640; - gspca_dev->height = 480; - memset(&rb, 0, sizeof rb); - rb.count = 4; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = V4L2_MEMORY_MMAP; - ret = vidioc_reqbufs(file, priv, &rb); - gspca_dev->pixfmt = pixfmt; - gspca_dev->width = width; - gspca_dev->height = height; - if (ret != 0) - return ret; + + { + struct v4l2_format fmt; + + memset(&fmt, 0, sizeof fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + i = gspca_dev->cam.nmodes - 1; /* highest mode */ + fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; + fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + ret = vidioc_s_fmt_cap(file, priv, &fmt); + if (ret != 0) + return ret; + } + { + struct v4l2_requestbuffers rb; + + memset(&rb, 0, sizeof rb); + rb.count = 4; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rb.memory = V4L2_MEMORY_MMAP; + ret = vidioc_reqbufs(file, priv, &rb); + if (ret != 0) + return ret; + } } mbuf->frames = gspca_dev->nframes; mbuf->size = gspca_dev->frsz * gspca_dev->nframes; @@ -1951,7 +1623,7 @@ static int vidioc_qbuf(struct file *file, void *priv, if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { frame->data = frame->data_end = - (unsigned char *) v4l2_buf->m.userptr; + (__u8 *) v4l2_buf->m.userptr; frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; frame->v4l2_buf.length = v4l2_buf->length; } @@ -2154,6 +1826,9 @@ static struct file_operations dev_fops = { .read = dev_read, .mmap = dev_mmap, .ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, .poll = dev_poll, }; @@ -2186,6 +1861,7 @@ static struct video_device gspca_template = { .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, + .vidioc_s_std = vidioc_s_std, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif @@ -2207,12 +1883,8 @@ int gspca_dev_probe(struct usb_interface *intf, struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); int ret; - __u16 vendor; - __u16 product; - vendor = id->idVendor; - product = id->idProduct; - PDEBUG(D_PROBE, "probing %04x:%04x", vendor, product); + PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); /* we don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -2309,35 +1981,24 @@ EXPORT_SYMBOL(gspca_disconnect); /* -- module insert / remove -- */ static int __init gspca_init(void) { -#ifdef GSPCA_HLP - int ret; - - /* create /dev/gspca_hlp */ - ret = misc_register(&hlp_device); - if (ret < 0) - err("misc_register err %d", ret); - start_hlp(); /* try to start the helper process */ -#endif info("main v%s registered", version); return 0; } static void __exit gspca_exit(void) { -#ifdef GSPCA_HLP - misc_deregister(&hlp_device); -#endif info("main deregistered"); } module_init(gspca_init); module_exit(gspca_exit); +#ifdef VIDEO_ADV_DEBUG module_param_named(debug, gspca_debug, int, 0644); MODULE_PARM_DESC(debug, "Debug (bit) 0x01:error 0x02:probe 0x04:config" " 0x08:stream 0x10:frame 0x20:packet 0x40:USBin 0x80:USBout" " 0x0100: v4l2"); - +#endif module_param(comp_fac, int, 0644); MODULE_PARM_DESC(comp_fac, "Buffer size ratio when compressed in percent"); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index e69d8472a284..1581fa808b6f 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -9,7 +9,26 @@ #include #include -#ifdef GSPCA_DEBUG +/* values in 2.6.27 */ +#ifndef V4L2_PIX_FMT_SPCA501 +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') +#endif +#ifndef V4L2_PIX_FMT_SPCA561 +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') +#endif + +/* values in 2.6.26 */ +#ifndef V4L2_CID_POWER_LINE_FREQUENCY +#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24) +#endif +#ifndef V4L2_CID_WHITE_BALANCE_TEMPERATURE +#define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_BASE + 26) +#endif +#ifndef V4L2_CID_SHARPNESS +#define V4L2_CID_SHARPNESS (V4L2_CID_BASE+27) +#endif + +#ifdef VIDEO_ADV_DEBUG /* GSPCA our debug messages */ extern int gspca_debug; #define PDEBUG(level, fmt, args...) \ @@ -47,7 +66,7 @@ extern int gspca_debug; #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ /* ISOC transfers */ -#define MAX_NURBS 32 /* max number of URBs (read & userptr) */ +#define MAX_NURBS 16 /* max number of URBs */ #define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */ #define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */ @@ -79,7 +98,7 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len); struct ctrl { @@ -116,8 +135,8 @@ struct sd_desc { #define LAST_PACKET 3 struct gspca_frame { - unsigned char *data; /* frame buffer */ - unsigned char *data_end; /* end of frame while filling */ + __u8 *data; /* frame buffer */ + __u8 *data_end; /* end of frame while filling */ int vma_use_count; struct v4l2_buffer v4l2_buf; }; @@ -135,7 +154,7 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; - unsigned int frsz; /* frame size */ + __u32 frsz; /* frame size */ char nframes; /* number of frames */ char fr_i; /* frame being filled */ char fr_q; /* next frame to queue */ @@ -145,10 +164,10 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ - unsigned char curr_mode; /* current camera mode */ + __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ - short width; - short height; + __u16 width; + __u16 height; atomic_t nevent; /* number of frames done */ wait_queue_head_t wq; /* wait queue */ @@ -176,6 +195,6 @@ void gspca_disconnect(struct usb_interface *intf); struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, int packet_type, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len); #endif /* GSPCAV2_H */ diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c new file mode 100644 index 000000000000..48b861d68299 --- /dev/null +++ b/drivers/media/video/gspca/mars.c @@ -0,0 +1,455 @@ +/* + * Mars-Semi MR97311A library + * Copyright (C) 2005 + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "mars" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + char qindex; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 640, 480, 1}, +}; + +/* MI Register table //elvis */ +enum { + REG_HW_MI_0, + REG_HW_MI_1, + REG_HW_MI_2, + REG_HW_MI_3, + REG_HW_MI_4, + REG_HW_MI_5, + REG_HW_MI_6, + REG_HW_MI_7, + REG_HW_MI_9 = 0x09, + REG_HW_MI_B = 0x0B, + REG_HW_MI_C, + REG_HW_MI_D, + REG_HW_MI_1E = 0x1E, + REG_HW_MI_20 = 0x20, + REG_HW_MI_2B = 0x2B, + REG_HW_MI_2C, + REG_HW_MI_2D, + REG_HW_MI_2E, + REG_HW_MI_35 = 0x35, + REG_HW_MI_5F = 0x5f, + REG_HW_MI_60, + REG_HW_MI_61, + REG_HW_MI_62, + REG_HW_MI_63, + REG_HW_MI_64, + REG_HW_MI_F1 = 0xf1, + ATTR_TOTAL_MI_REG = 242 +}; + +static int pcam_reg_write(struct usb_device *dev, + __u16 index, unsigned char *value, int length) +{ + int rc; + + rc = usb_control_msg(dev, + usb_sndbulkpipe(dev, 4), + 0x12, +/* ?? 0xc8 = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_??? !? */ + 0xc8, + 0, /* value */ + index, value, length, 500); + PDEBUG(D_USBO, "reg write: 0x%02X , result = 0x%x", index, rc); + + if (rc < 0) + PDEBUG(D_ERR, "reg write: error %d", rc); + return rc; +} + +static void MISensor_BulkWrite(struct usb_device *dev, unsigned short *pch, + char Address) +{ + int result; + unsigned char data[6]; + + data[0] = 0x1f; + data[1] = 0; + data[2] = Address; + data[3] = *pch >> 8; /* high byte */ + data[4] = *pch; /* low byte */ + data[5] = 0; + + result = usb_control_msg(dev, + usb_sndbulkpipe(dev, 4), + 0x12, +/* ?? 0xc8 = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_??? !? */ + 0xc8, + 0, /* value */ + Address, /* index */ + data, 5, 500); + PDEBUG(D_USBO, "bulk write 0x%02x = 0x%04x", Address, *pch); + + if (result < 0) + PDEBUG(D_ERR, "reg write: error %d", result); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->qindex = 1; /* set the quantization table */ + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int err_code; + __u8 data[12]; + __u16 MI_buf[242]; + int h_size, v_size; + int intpipe; + /* struct usb_device *dev = pcam->dev; */ + memset(data, 0, sizeof data); + memset(MI_buf, 0, sizeof MI_buf); + + PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface); + if (usb_set_interface(dev, gspca_dev->iface, 8) < 0) { + PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error"); + return; + } + + data[0] = 0x01; /* address */ + data[1] = 0x01; + + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) + return; + + /* + Initialize the MR97113 chip register + */ + data[0] = 0x00; /* address */ + data[1] = 0x0c | 0x01; /* reg 0 */ + data[2] = 0x01; /* reg 1 */ + h_size = gspca_dev->width; + v_size = gspca_dev->height; + data[3] = h_size / 8; /* h_size , reg 2 */ + data[4] = v_size / 8; /* v_size , reg 3 */ + data[5] = 0x30; /* reg 4, MI, PAS5101 : + * 0x30 for 24mhz , 0x28 for 12mhz */ + data[6] = 4; /* reg 5, H start */ + data[7] = 0xc0; /* reg 6, gamma 1.5 */ + data[8] = 3; /* reg 7, V start */ +/* if(h_size == 320 ) */ +/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ +/* else */ + data[9] = 0x52; /* reg 8, 24MHz, no scale down */ + data[10] = 0x5d; /* reg 9, I2C device address + * [for PAS5101 (0x40)] [for MI (0x5d)] */ + + err_code = pcam_reg_write(dev, data[0], data, 0x0b); + if (err_code < 0) + return; + + data[0] = 0x23; /* address */ + data[1] = 0x09; /* reg 35, append frame header */ + + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) { + PDEBUG(D_ERR, "Register write failed"); + return; + } + + data[0] = 0x3C; /* address */ +/* if (pcam->width == 1280) */ +/* data[1] = 200; * reg 60, pc-cam frame size + * (unit: 4KB) 800KB */ +/* else */ + data[1] = 50; /* 50 reg 60, pc-cam frame size + * (unit: 4KB) 200KB */ + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) + return; + + if (0) { /* fixed dark-gain */ + data[1] = 0; /* reg 94, Y Gain (1.75) */ + data[2] = 0; /* reg 95, UV Gain (1.75) */ + data[3] = 0x3f; /* reg 96, Y Gain/UV Gain/disable auto dark-gain */ + data[4] = 0; /* reg 97, set fixed dark level */ + data[5] = 0; /* reg 98, don't care */ + } else { /* auto dark-gain */ + data[1] = 0; /* reg 94, Y Gain (auto) */ + data[2] = 0; /* reg 95, UV Gain (1.75) */ + data[3] = 0x78; /* reg 96, Y Gain/UV Gain/disable auto dark-gain */ + switch (gspca_dev->width) { +/* case 1280: */ +/* data[4] = 154; + * reg 97, %3 shadow point (unit: 256 pixel) */ +/* data[5] = 51; + * reg 98, %1 highlight point + * (uint: 256 pixel) */ +/* break; */ + default: +/* case 640: */ + data[4] = 36; /* reg 97, %3 shadow point + * (unit: 256 pixel) */ + data[5] = 12; /* reg 98, %1 highlight point + * (uint: 256 pixel) */ + break; + case 320: + data[4] = 9; /* reg 97, %3 shadow point + * (unit: 256 pixel) */ + data[5] = 3; /* reg 98, %1 highlight point + * (uint: 256 pixel) */ + break; + } + } + /* auto dark-gain */ + data[0] = 0x5e; /* address */ + + err_code = pcam_reg_write(dev, data[0], data, 0x06); + if (err_code < 0) + return; + + data[0] = 0x67; + data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */ + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) + return; + + /* + * initialize the value of MI sensor... + */ + MI_buf[REG_HW_MI_1] = 0x000a; + MI_buf[REG_HW_MI_2] = 0x000c; + MI_buf[REG_HW_MI_3] = 0x0405; + MI_buf[REG_HW_MI_4] = 0x0507; + /* mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 */ + MI_buf[REG_HW_MI_5] = 0x0013; /* 13 */ + MI_buf[REG_HW_MI_6] = 0x001f; /* vertical blanking */ + /* mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking */ + MI_buf[REG_HW_MI_7] = 0x0002; + /* mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; */ + /* mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; */ + MI_buf[REG_HW_MI_9] = 0x0374; + MI_buf[REG_HW_MI_B] = 0x0000; + MI_buf[REG_HW_MI_C] = 0x0000; + MI_buf[REG_HW_MI_D] = 0x0000; + MI_buf[REG_HW_MI_1E] = 0x8000; +/* mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; */ + MI_buf[REG_HW_MI_20] = 0x1104; /* 0x111c; */ + MI_buf[REG_HW_MI_2B] = 0x0008; +/* mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; */ + MI_buf[REG_HW_MI_2C] = 0x001f; /* lita suggest */ + MI_buf[REG_HW_MI_2D] = 0x0008; + MI_buf[REG_HW_MI_2E] = 0x0008; + MI_buf[REG_HW_MI_35] = 0x0051; + MI_buf[REG_HW_MI_5F] = 0x0904; /* fail to write */ + MI_buf[REG_HW_MI_60] = 0x0000; + MI_buf[REG_HW_MI_61] = 0x0000; + MI_buf[REG_HW_MI_62] = 0x0498; + MI_buf[REG_HW_MI_63] = 0x0000; + MI_buf[REG_HW_MI_64] = 0x0000; + MI_buf[REG_HW_MI_F1] = 0x0001; + /* changing while setting up the different value of dx/dy */ + + if (gspca_dev->width != 1280) { + MI_buf[0x01] = 0x010a; + MI_buf[0x02] = 0x014c; + MI_buf[0x03] = 0x01e5; + MI_buf[0x04] = 0x0287; + } + MI_buf[0x20] = 0x1104; + + MISensor_BulkWrite(dev, MI_buf + 1, 1); + MISensor_BulkWrite(dev, MI_buf + 2, 2); + MISensor_BulkWrite(dev, MI_buf + 3, 3); + MISensor_BulkWrite(dev, MI_buf + 4, 4); + MISensor_BulkWrite(dev, MI_buf + 5, 5); + MISensor_BulkWrite(dev, MI_buf + 6, 6); + MISensor_BulkWrite(dev, MI_buf + 7, 7); + MISensor_BulkWrite(dev, MI_buf + 9, 9); + MISensor_BulkWrite(dev, MI_buf + 0x0b, 0x0b); + MISensor_BulkWrite(dev, MI_buf + 0x0c, 0x0c); + MISensor_BulkWrite(dev, MI_buf + 0x0d, 0x0d); + MISensor_BulkWrite(dev, MI_buf + 0x1e, 0x1e); + MISensor_BulkWrite(dev, MI_buf + 0x20, 0x20); + MISensor_BulkWrite(dev, MI_buf + 0x2b, 0x2b); + MISensor_BulkWrite(dev, MI_buf + 0x2c, 0x2c); + MISensor_BulkWrite(dev, MI_buf + 0x2d, 0x2d); + MISensor_BulkWrite(dev, MI_buf + 0x2e, 0x2e); + MISensor_BulkWrite(dev, MI_buf + 0x35, 0x35); + MISensor_BulkWrite(dev, MI_buf + 0x5f, 0x5f); + MISensor_BulkWrite(dev, MI_buf + 0x60, 0x60); + MISensor_BulkWrite(dev, MI_buf + 0x61, 0x61); + MISensor_BulkWrite(dev, MI_buf + 0x62, 0x62); + MISensor_BulkWrite(dev, MI_buf + 0x63, 0x63); + MISensor_BulkWrite(dev, MI_buf + 0x64, 0x64); + MISensor_BulkWrite(dev, MI_buf + 0xf1, 0xf1); + + intpipe = usb_sndintpipe(dev, 0); + err_code = usb_clear_halt(dev, intpipe); + + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + pcam_reg_write(dev, data[0], data, 0x02); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + __u8 data[2]; + + data[0] = 1; + data[1] = 0; + result = pcam_reg_write(gspca_dev->dev, data[0], data, 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int p; + + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + for (p = 0; p < len - 6; p++) { + if (data[0 + p] == 0xff + && data[1 + p] == 0xff + && data[2 + p] == 0x00 + && data[3 + p] == 0xff + && data[4 + p] == 0x96) { + if (data[5 + p] == 0x64 + || data[5 + p] == 0x65 + || data[5 + p] == 0x66 + || data[5 + p] == 0x67) { + PDEBUG(D_PACK, "sof offset: %d leng: %d", + p, len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, 0); + + /* put the JPEG header */ + jpeg_put_header(gspca_dev, frame, + sd->qindex, 0x21); + data += 16; + len -= 16; + break; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x050f), DVNM("Mars-Semi Pc-Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c new file mode 100644 index 000000000000..7d6237f18ba0 --- /dev/null +++ b/drivers/media/video/gspca/ov519.c @@ -0,0 +1,2174 @@ +/** + * OV519 driver + * + * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * + * (This module is adapted from the ov51x-jpeg package) + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "ov519" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("OV519 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* global parameters */ +static int frame_rate; + +/* Number of times to retry a failed I2C transaction. Increase this if you + * are getting "Failed to read sensor ID..." */ +static int i2c_detect_tries = 10; + +/* ov519 device descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + /* Determined by sensor type */ + short maxwidth; + short maxheight; + + unsigned char primary_i2c_slave; /* I2C write id of sensor */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + char compress; /* Should the next frame be compressed? */ + char compress_inited; /* Are compression params uploaded? */ + char stopped; /* Streaming is temporarily paused */ + + char frame_rate; /* current Framerate (OV519 only) */ + char clockdiv; /* clockdiv override for OV519 only */ + + char sensor; /* Type of image sensor chip (SEN_*) */ +#define SEN_UNKNOWN 0 +#define SEN_OV6620 1 +#define SEN_OV6630 2 +#define SEN_OV7610 3 +#define SEN_OV7620 4 +#define SEN_OV7630 5 +#define SEN_OV7640 6 +#define SEN_OV7670 7 +#define SEN_OV76BE 8 +#define SEN_OV8610 9 + +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240}, + {V4L2_PIX_FMT_JPEG, 640, 480}, +}; +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_JPEG, 176, 144}, + {V4L2_PIX_FMT_JPEG, 352, 288}, +}; + +/* OV519 Camera interface register numbers */ +#define OV519_CAM_H_SIZE 0x10 +#define OV519_CAM_V_SIZE 0x11 +#define OV519_CAM_X_OFFSETL 0x12 +#define OV519_CAM_X_OFFSETH 0x13 +#define OV519_CAM_Y_OFFSETL 0x14 +#define OV519_CAM_Y_OFFSETH 0x15 +#define OV519_CAM_DIVIDER 0x16 +#define OV519_CAM_DFR 0x20 +#define OV519_CAM_FORMAT 0x25 + +/* OV519 System Controller register numbers */ +#define OV519_SYS_RESET1 0x51 +#define OV519_SYS_EN_CLK1 0x54 + +#define OV519_GPIO_DATA_OUT0 0x71 +#define OV519_GPIO_IO_CTRL0 0x72 + +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ + +/* I2C registers */ +#define R51x_I2C_W_SID 0x41 +#define R51x_I2C_SADDR_3 0x42 +#define R51x_I2C_SADDR_2 0x43 +#define R51x_I2C_R_SID 0x44 +#define R51x_I2C_DATA 0x45 +#define R518_I2C_CTL 0x47 /* OV518(+) only */ + +/* I2C ADDRESSES */ +#define OV7xx0_SID 0x42 +#define OV8xx0_SID 0xa0 +#define OV6xx0_SID 0xc0 + +/* OV7610 registers */ +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_SAT 0x03 /* saturation */ +#define OV8610_REG_HUE 0x04 /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ + +/* OV7670 registers */ +#define OV7670_REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define OV7670_REG_BLUE 0x01 /* blue gain */ +#define OV7670_REG_RED 0x02 /* red gain */ +#define OV7670_REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define OV7670_REG_COM1 0x04 /* Control 1 */ +#define OV7670_REG_AECHH 0x07 /* AEC MS 5 bits */ +#define OV7670_REG_COM3 0x0c /* Control 3 */ +#define OV7670_REG_COM4 0x0d /* Control 4 */ +#define OV7670_REG_COM5 0x0e /* All "reserved" */ +#define OV7670_REG_COM6 0x0f /* Control 6 */ +#define OV7670_REG_AECH 0x10 /* More bits of AEC value */ +#define OV7670_REG_CLKRC 0x11 /* Clock control */ +#define OV7670_REG_COM7 0x12 /* Control 7 */ +#define OV7670_COM7_FMT_VGA 0x00 +#define OV7670_COM7_YUV 0x00 /* YUV */ +#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */ +#define OV7670_COM7_FMT_MASK 0x38 +#define OV7670_COM7_RESET 0x80 /* Register reset */ +#define OV7670_REG_COM8 0x13 /* Control 8 */ +#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */ +#define OV7670_COM8_AWB 0x02 /* White balance enable */ +#define OV7670_COM8_AGC 0x04 /* Auto gain enable */ +#define OV7670_COM8_BFILT 0x20 /* Band filter enable */ +#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define OV7670_REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define OV7670_REG_COM10 0x15 /* Control 10 */ +#define OV7670_REG_HSTART 0x17 /* Horiz start high bits */ +#define OV7670_REG_HSTOP 0x18 /* Horiz stop high bits */ +#define OV7670_REG_VSTART 0x19 /* Vert start high bits */ +#define OV7670_REG_VSTOP 0x1a /* Vert stop high bits */ +#define OV7670_REG_MVFP 0x1e /* Mirror / vflip */ +#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */ +#define OV7670_REG_AEW 0x24 /* AGC upper limit */ +#define OV7670_REG_AEB 0x25 /* AGC lower limit */ +#define OV7670_REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define OV7670_REG_HREF 0x32 /* HREF pieces */ +#define OV7670_REG_TSLB 0x3a /* lots of stuff */ +#define OV7670_REG_COM11 0x3b /* Control 11 */ +#define OV7670_COM11_EXP 0x02 +#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define OV7670_REG_COM12 0x3c /* Control 12 */ +#define OV7670_REG_COM13 0x3d /* Control 13 */ +#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */ +#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define OV7670_REG_COM14 0x3e /* Control 14 */ +#define OV7670_REG_EDGE 0x3f /* Edge enhancement factor */ +#define OV7670_REG_COM15 0x40 /* Control 15 */ +#define OV7670_COM15_R00FF 0xc0 /* 00 to FF */ +#define OV7670_REG_COM16 0x41 /* Control 16 */ +#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define OV7670_REG_BRIGHT 0x55 /* Brightness */ +#define OV7670_REG_CONTRAS 0x56 /* Contrast control */ +#define OV7670_REG_GFIX 0x69 /* Fix gain control */ +#define OV7670_REG_RGB444 0x8c /* RGB 444 control */ +#define OV7670_REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define OV7670_REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ +#define OV7670_REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define OV7670_REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define OV7670_REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define OV7670_REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define OV7670_REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define OV7670_REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define OV7670_REG_BD60MAX 0xab /* 60hz banding step limit */ + +struct ovsensor_window { + short x; + short y; + short width; + short height; +/* int format; */ + short quarter; /* Scale width and height down 2x */ + short clockdiv; /* Clock divisor setting */ +}; + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + return (128 - v) | 0x80; +} + +/* Write a OV519 register */ +static int reg_w(struct sd *sd, __u16 index, __u8 value) +{ + int ret; + __u8 buf[4]; + + buf[0] = value; + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO (ov518/519) */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + &buf[0], 1, 500); + if (ret < 0) + PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); + return ret; +} + +/* Read from a OV519 register */ +/* returns: negative is error, pos or zero is data */ +static int reg_r(struct sd *sd, __u16 index) +{ + int ret; + __u8 buf[4]; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, &buf[0], 1, 500); + + if (ret >= 0) + ret = buf[0]; + else + PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); + return ret; +} + +/* Read 8 values from a OV519 register */ +static int reg_r8(struct sd *sd, + __u16 index) +{ + int ret; + __u8 buf[8]; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, &buf[0], 8, 500); + + if (ret >= 0) + ret = buf[0]; + else + PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); + return ret; +} + +/* + * Writes bits at positions specified by mask to an OV51x reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int reg_w_mask(struct sd *sd, + __u16 index, + __u8 value, + __u8 mask) +{ + int ret; + __u8 oldval; + + if (mask != 0xff) { + value &= mask; /* Enforce mask on value */ + ret = reg_r(sd, index); + if (ret < 0) + return ret; + + oldval = ret & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + } + return reg_w(sd, index, value); +} + +/* + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_w(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int i2c_w(struct sd *sd, + __u8 reg, + __u8 value) +{ + int rc; + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(sd, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x01); + + /* wait for write complete */ + msleep(4); + if (rc < 0) + return rc; + return reg_r8(sd, R518_I2C_CTL); +} + +/* + * returns: negative is error, pos or zero is data + * + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_r(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int i2c_r(struct sd *sd, __u8 reg) +{ + int rc, value; + + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + /* Initiate 2-byte read cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x05); + if (rc < 0) + return rc; + value = reg_r(sd, R51x_I2C_DATA); + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + return value; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int i2c_w_mask(struct sd *sd, + __u8 reg, + __u8 value, + __u8 mask) +{ + int rc; + __u8 oldval; + + value &= mask; /* Enforce mask on value */ + rc = i2c_r(sd, reg); + if (rc < 0) + return rc; + oldval = rc & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + return i2c_w(sd, reg, value); +} + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline int ov51x_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "stopping"); + sd->stopped = 1; + return reg_w(sd, OV519_SYS_RESET1, 0x0f); +} + +/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not + * actually stopped (for performance). */ +static inline int ov51x_restart(struct sd *sd) +{ + PDEBUG(D_STREAM, "restarting"); + if (!sd->stopped) + return 0; + sd->stopped = 0; + + /* Reinitialize the stream */ + return reg_w(sd, OV519_SYS_RESET1, 0x00); +} + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 on failure. + */ +static int init_ov_sensor(struct sd *sd) +{ + int i, success; + + /* Reset the sensor */ + if (i2c_w(sd, 0x12, 0x80) < 0) + return -EIO; + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f && + i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) { + success = 1; + continue; + } + + /* Reset the sensor */ + if (i2c_w(sd, 0x12, 0x80) < 0) + return -EIO; + /* Wait for it to initialize */ + msleep(150); + /* Dummy read to sync I2C */ + if (i2c_r(sd, 0x00) < 0) + return -EIO; + } + if (!success) + return -EIO; + PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); + return 0; +} + +/* Switch on standard JPEG compression. Returns 0 for success. */ +static int ov519_init_compression(struct sd *sd) +{ + if (!sd->compress_inited) { + if (reg_w_mask(sd, OV519_SYS_EN_CLK1, 1 << 2, 1 << 2) < 0) { + PDEBUG(D_ERR, "Error switching to compressed mode"); + return -EIO; + } + sd->compress_inited = 1; + } + return 0; +} + +/* Set the read and write slave IDs. The "slave" argument is the write slave, + * and the read slave will be set to (slave + 1). + * This should not be called from outside the i2c I/O functions. + * Sets I2C read and write slave IDs. Returns <0 for error + */ +static int ov51x_set_slave_ids(struct sd *sd, + __u8 slave) +{ + int rc; + + rc = reg_w(sd, R51x_I2C_W_SID, slave); + if (rc < 0) + return rc; + return reg_w(sd, R51x_I2C_R_SID, slave + 1); +} + +struct ov_regvals { + __u8 reg; + __u8 val; +}; +struct ov_i2c_regvals { + __u8 reg; + __u8 val; +}; + +static int write_regvals(struct sd *sd, + struct ov_regvals *regvals, + int n) +{ + int rc; + + while (--n >= 0) { + rc = reg_w(sd, regvals->reg, regvals->val); + if (rc < 0) + return rc; + regvals++; + } + return 0; +} + +static int write_i2c_regvals(struct sd *sd, + struct ov_i2c_regvals *regvals, + int n) +{ + int rc; + + while (--n >= 0) { + rc = i2c_w(sd, regvals->reg, regvals->val); + if (rc < 0) + return rc; + regvals++; + } + return 0; +} + +/**************************************************************************** + * + * OV511 and sensor configuration + * + ***************************************************************************/ + +/* This initializes the OV8110, OV8610 sensor. The OV8110 uses + * the same register settings as the OV8610, since they are very similar. + */ +static int ov8xx0_configure(struct sd *sd) +{ + int rc; + static struct ov_i2c_regvals norm_8610[] = { + { 0x12, 0x80 }, + { 0x00, 0x00 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, 0xc0 }, + { 0x04, 0x30 }, + { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { 0x0a, 0x86 }, + { 0x0b, 0xb0 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x12, 0x25 }, + { 0x13, 0x01 }, + { 0x14, 0x04 }, + { 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { 0x16, 0x03 }, + { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x80 }, + { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { 0x2c, 0xac }, + { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { 0x2e, 0x80 }, + { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { 0x4c, 0x00 }, + { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { 0x63, 0xff }, + { 0x64, 0x53 }, /* new windrv 090403 says 0x57, + * maybe thats wrong */ + { 0x65, 0x00 }, + { 0x66, 0x55 }, + { 0x67, 0xb0 }, + { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { 0x69, 0x02 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but + deleting bit7 colors the first images red */ + { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6f, 0x01 }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ + { 0x75, 0x0e }, + { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { 0x7c, 0x00 }, + { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { 0x7f, 0xfb }, + { 0x80, 0x28 }, + { 0x81, 0x00 }, + { 0x82, 0x23 }, + { 0x83, 0x0b }, + { 0x84, 0x00 }, + { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { 0x86, 0xc9 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, + { 0x89, 0x01 }, + { 0x12, 0x20 }, + { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ + }; + + PDEBUG(D_PROBE, "starting ov8xx0 configuration"); + + if (init_ov_sensor(sd) < 0) + PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID"); + else + PDEBUG(D_PROBE, "OV86x0 initialized"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + if ((rc & 3) == 1) { + PDEBUG(D_PROBE, "Sensor is an OV8610"); + sd->sensor = SEN_OV8610; + } else { + PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); + return -1; + } + PDEBUG(D_PROBE, "Writing 8610 registers"); + if (write_i2c_regvals(sd, + norm_8610, + sizeof norm_8610 / sizeof norm_8610[0])) + return -1; + + /* Set sensor-specific vars */ + sd->maxwidth = 640; + sd->maxheight = 480; + return 0; +} + +/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses + * the same register settings as the OV7610, since they are very similar. + */ +static int ov7xx0_configure(struct sd *sd) +{ + int rc, high, low; + + /* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ + static struct ov_i2c_regvals norm_7610[] = { + { 0x10, 0xff }, + { 0x16, 0x06 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + }; + + static struct ov_i2c_regvals norm_7620[] = { + { 0x00, 0x00 }, /* gain */ + { 0x01, 0x80 }, /* blue gain */ + { 0x02, 0x80 }, /* red gain */ + { 0x03, 0xc0 }, /* OV7670_REG_VREF */ + { 0x06, 0x60 }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, 0x00 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, + }; + + /* 7640 and 7648. The defaults should be OK for most registers. */ + static struct ov_i2c_regvals norm_7640[] = { + { 0x12, 0x80 }, + { 0x12, 0x14 }, + }; + + /* 7670. Defaults taken from OmniVision provided data, + * as provided by Jonathan Corbet of OLPC */ + static struct ov_i2c_regvals norm_7670[] = { + { OV7670_REG_COM7, OV7670_COM7_RESET }, + { OV7670_REG_TSLB, 0x04 }, /* OV */ + { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ + { OV7670_REG_CLKRC, 0x1 }, + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { OV7670_REG_HSTART, 0x13 }, { OV7670_REG_HSTOP, 0x01 }, + { OV7670_REG_HREF, 0xb6 }, { OV7670_REG_VSTART, 0x02 }, + { OV7670_REG_VSTOP, 0x7a }, { OV7670_REG_VREF, 0x0a }, + + { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, +/* jfm */ +/* { OV7670_REG_COM10, 0x0 }, */ + + /* Gamma curve values */ + { 0x7a, 0x20 }, +/* jfm:win 7b=1c */ + { 0x7b, 0x10 }, +/* jfm:win 7c=28 */ + { 0x7c, 0x1e }, +/* jfm:win 7d=3c */ + { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT }, + { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 }, + { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ +/* jfm:win 14=38 */ + { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { OV7670_REG_BD50MAX, 0x05 }, { OV7670_REG_BD60MAX, 0x07 }, + { OV7670_REG_AEW, 0x95 }, { OV7670_REG_AEB, 0x33 }, + { OV7670_REG_VPT, 0xe3 }, { OV7670_REG_HAECC1, 0x78 }, + { OV7670_REG_HAECC2, 0x68 }, +/* jfm:win a1=0b */ + { 0xa1, 0x03 }, /* magic */ + { OV7670_REG_HAECC3, 0xd8 }, { OV7670_REG_HAECC4, 0xd8 }, + { OV7670_REG_HAECC5, 0xf0 }, { OV7670_REG_HAECC6, 0x90 }, + { OV7670_REG_HAECC7, 0x94 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { OV7670_REG_COM5, 0x61 }, { OV7670_REG_COM6, 0x4b }, + { 0x16, 0x02 }, +/* jfm */ +/* { OV7670_REG_MVFP, 0x07|OV7670_MVFP_MIRROR }, */ + { OV7670_REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { OV7670_REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { OV7670_REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, + /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { OV7670_REG_BLUE, 0x40 }, + { OV7670_REG_RED, 0x60 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC + | OV7670_COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, + { OV7670_REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { OV7670_REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { OV7670_REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + /* Format YUV422 */ + { OV7670_REG_COM7, OV7670_COM7_YUV }, /* Selects YUV mode */ + { OV7670_REG_RGB444, 0 }, /* No RGB444 please */ + { OV7670_REG_COM1, 0 }, + { OV7670_REG_COM15, OV7670_COM15_R00FF }, + { OV7670_REG_COM9, 0x18 }, + /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { OV7670_REG_COM13, OV7670_COM13_GAMMA|OV7670_COM13_UVSAT }, +}; + + PDEBUG(D_PROBE, "starting OV7xx0 configuration"); + +/* jfm:already done? */ + if (init_ov_sensor(sd) < 0) + PDEBUG(D_ERR, "Failed to read sensor ID"); + else + PDEBUG(D_PROBE, "OV7xx0 initialized"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + + /* add OV7670 here + * it appears to be wrongly detected as a 7610 by default */ + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + if ((rc & 3) == 3) { + /* quick hack to make OV7670s work */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x76 && low == 0x73) { + PDEBUG(D_PROBE, "Sensor is an OV7670"); + sd->sensor = SEN_OV7670; + } else { + PDEBUG(D_PROBE, "Sensor is an OV7610"); + sd->sensor = SEN_OV7610; + } + } else if ((rc & 3) == 1) { + /* I don't know what's different about the 76BE yet. */ + if (i2c_r(sd, 0x15) & 1) + PDEBUG(D_PROBE, "Sensor is an OV7620AE"); + else + PDEBUG(D_PROBE, "Sensor is an OV76BE"); + + /* OV511+ will return all zero isoc data unless we + * configure the sensor as a 7620. Someone needs to + * find the exact reg. setting that causes this. */ + sd->sensor = SEN_OV76BE; + } else if ((rc & 3) == 0) { + /* try to read product id registers */ + high = i2c_r(sd, 0x0a); + if (high < 0) { + PDEBUG(D_ERR, "Error detecting camera chip PID"); + return high; + } + low = i2c_r(sd, 0x0b); + if (low < 0) { + PDEBUG(D_ERR, "Error detecting camera chip VER"); + return low; + } + if (high == 0x76) { + if (low == 0x30) { + PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635"); + sd->sensor = SEN_OV7630; + } else if (low == 0x40) { + PDEBUG(D_PROBE, "Sensor is an OV7645"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else if (low == 0x45) { + PDEBUG(D_PROBE, "Sensor is an OV7645B"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else if (low == 0x48) { + PDEBUG(D_PROBE, "Sensor is an OV7648"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else { + PDEBUG(D_PROBE, "Unknown sensor: 0x76%X", low); + return -1; + } + } else { + PDEBUG(D_PROBE, "Sensor is an OV7620"); + sd->sensor = SEN_OV7620; + } + } else { + PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); + return -1; + } + + if (sd->sensor == SEN_OV7620) { + PDEBUG(D_PROBE, "Writing 7620 registers"); + if (write_i2c_regvals(sd, norm_7620, + sizeof norm_7620 / sizeof norm_7620[0])) + return -1; + } else if (sd->sensor == SEN_OV7630) { + PDEBUG(D_ERR, "7630 is not supported by this driver version"); + return -1; + } else if (sd->sensor == SEN_OV7640) { + PDEBUG(D_PROBE, "Writing 7640 registers"); + if (write_i2c_regvals(sd, norm_7640, + sizeof norm_7640 / sizeof norm_7640[0])) + return -1; + } else if (sd->sensor == SEN_OV7670) { + PDEBUG(D_PROBE, "Writing 7670 registers"); + if (write_i2c_regvals(sd, norm_7670, + sizeof norm_7670 / sizeof norm_7670[0])) + return -1; + } else { + PDEBUG(D_PROBE, "Writing 7610 registers"); + if (write_i2c_regvals(sd, norm_7610, + sizeof norm_7610 / sizeof norm_7610[0])) + return -1; + } + + /* Set sensor-specific vars */ + sd->maxwidth = 640; + sd->maxheight = 480; + return 0; +} + +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ +static int ov6xx0_configure(struct sd *sd) +{ + int rc; + static struct ov_i2c_regvals norm_6x20[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC */ + { 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { 0x16, 0x06 }, +/* { 0x20, 0x30 }, * Aperture correction enable */ + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ +/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. + * They control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, +/* Do 50-53 have any effect? */ +/* Toggle 0x12[2] off and on here? */ + }; + + static struct ov_i2c_regvals norm_6x30[] = { + { 0x12, 0x80 }, /* Reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* 60 Hz power */ + { 0x2b, 0xa8 }, /* 60 Hz power */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + { 0x12, 0x20 }, /* Toggle AWB */ + { 0x12, 0x24 }, + }; + + PDEBUG(D_PROBE, "starting sensor configuration"); + + if (init_ov_sensor(sd) < 0) { + PDEBUG(D_ERR, "Failed to read sensor ID."); + return -1; + } + PDEBUG(D_PROBE, "OV6xx0 sensor detected"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + + /* Ugh. The first two bits are the version bits, but + * the entire register value must be used. I guess OVT + * underestimated how many variants they would make. */ + if (rc == 0x00) { + sd->sensor = SEN_OV6630; + PDEBUG(D_ERR, + "WARNING: Sensor is an OV66308. Your camera may have"); + PDEBUG(D_ERR, "been misdetected in previous driver versions."); + } else if (rc == 0x01) { + sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); + } else if (rc == 0x02) { + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AE"); + } else if (rc == 0x03) { + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AF"); + } else if (rc == 0x90) { + sd->sensor = SEN_OV6630; + PDEBUG(D_ERR, + "WARNING: Sensor is an OV66307. Your camera may have"); + PDEBUG(D_ERR, "been misdetected in previous driver versions."); + } else { + PDEBUG(D_ERR, "FATAL: Unknown sensor version: 0x%02x", rc); + return -1; + } + + /* Set sensor-specific vars */ + sd->maxwidth = 352; + sd->maxheight = 288; + + if (sd->sensor == SEN_OV6620) { + PDEBUG(D_PROBE, "Writing 6x20 registers"); + if (write_i2c_regvals(sd, norm_6x20, + sizeof norm_6x20 / sizeof norm_6x20[0])) + return -1; + } else { + PDEBUG(D_PROBE, "Writing 6x30 registers"); + if (write_i2c_regvals(sd, norm_6x30, + sizeof norm_6x30 / sizeof norm_6x30[0])) + return -1; + } + return 0; +} + +/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ +static void ov51x_led_control(struct sd *sd, int on) +{ + PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); + +/* if (sd->bridge == BRG_OV511PLUS) */ +/* reg_w(sd, R511_SYS_LED_CTL, on ? 1 : 0); */ +/* else if (sd->bridge == BRG_OV519) */ + reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */ +/* else if (sd->bclass == BCL_OV518) */ +/* reg_w_mask(sd, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); */ +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + +/* (from ov519_configure) */ + static struct ov_regvals init_519[] = { + { 0x5a, 0x6d }, /* EnableSystem */ +/* jfm trace usbsnoop3-1.txt */ +/* jfm 53 = fb */ + { 0x53, 0x9b }, + { 0x54, 0xff }, /* set bit2 to enable jpeg */ + { 0x5d, 0x03 }, + { 0x49, 0x01 }, + { 0x48, 0x00 }, + /* Set LED pin to output mode. Bit 4 must be cleared or sensor + * detection will fail. This deserves further investigation. */ + { OV519_GPIO_IO_CTRL0, 0xee }, + { 0x51, 0x0f }, /* SetUsbInit */ + { 0x51, 0x00 }, + { 0x22, 0x00 }, + /* windows reads 0x55 at this point*/ + }; + + if (write_regvals(sd, init_519, + sizeof init_519 / sizeof init_519[0])) + goto error; +/* jfm: not seen in windows trace */ + if (ov519_init_compression(sd)) + goto error; + ov51x_led_control(sd, 0); /* turn LED off */ + + /* Test for 76xx */ + sd->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) + goto error; + + /* The OV519 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + if (init_ov_sensor(sd) < 0) { + /* Test for 6xx0 */ + sd->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) + goto error; + + if (init_ov_sensor(sd) < 0) { + /* Test for 8xx0 */ + sd->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) + goto error; + + if (init_ov_sensor(sd) < 0) { + PDEBUG(D_ERR, + "Can't determine sensor slave IDs"); + goto error; + } else { + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, + "Failed to configure OV8xx0 sensor"); + goto error; + } + } + } else { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV7xx0"); + goto error; + } + } + + cam = &gspca_dev->cam; + cam->epaddr = OV511_ENDPOINT_ADDRESS; + if (sd->maxwidth == 640) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + cam->dev_name = (char *) id->driver_info; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +error: + PDEBUG(D_ERR, "OV519 Config failed"); + return -EBUSY; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* Sets up the OV519 with the given image parameters + * + * OV519 needs a completely different approach, until we can figure out what + * the individual registers do. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov519_mode_init_regs(struct sd *sd, + int width, int height) +{ + static struct ov_regvals mode_init_519_ov7670[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x20, 0x0c }, + { 0x21, 0x38 }, + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + + static struct ov_regvals mode_init_519[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + +/* int hi_res; */ + + PDEBUG(D_CONF, "mode init %dx%d", width, height); + +/* if (width >= 800 && height >= 600) + hi_res = 1; + else + hi_res = 0; */ + +/* if (ov51x_stop(sd) < 0) + return -EIO; */ + + /******** Set the mode ********/ + if (sd->sensor != SEN_OV7670) { + if (write_regvals(sd, mode_init_519, + sizeof mode_init_519 / sizeof mode_init_519[0])) + return -EIO; + } else { + if (write_regvals(sd, mode_init_519_ov7670, + sizeof mode_init_519_ov7670 + / sizeof mode_init_519_ov7670[0])) + return -EIO; + } + + if (sd->sensor == SEN_OV7640) { + /* Select 8-bit input mode */ + reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10); + } + + reg_w(sd, OV519_CAM_H_SIZE, width >> 4); + reg_w(sd, OV519_CAM_V_SIZE, height >> 3); + reg_w(sd, OV519_CAM_X_OFFSETL, 0x00); + reg_w(sd, OV519_CAM_X_OFFSETH, 0x00); + reg_w(sd, OV519_CAM_Y_OFFSETL, 0x00); + reg_w(sd, OV519_CAM_Y_OFFSETH, 0x00); + reg_w(sd, OV519_CAM_DIVIDER, 0x00); + reg_w(sd, OV519_CAM_FORMAT, 0x03); /* YUV422 */ + reg_w(sd, 0x26, 0x00); /* Undocumented */ + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + +/* FIXME: These are only valid at the max resolution. */ + sd->clockdiv = 0; + if (sd->sensor == SEN_OV7640) { + switch (sd->frame_rate) { +/*jfm: default was 30 fps */ + case 30: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0xff); + break; + case 25: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1f); + break; + case 20: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1b); + break; + default: +/* case 15: */ + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + case 10: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1f); + sd->clockdiv = 1; + break; + case 5: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1b); + sd->clockdiv = 1; + break; + } + } else if (sd->sensor == SEN_OV8610) { + switch (sd->frame_rate) { + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0xff); + break; + case 10: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1f); + break; + case 5: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1b); + break; + } + sd->clockdiv = 0; + } else if (sd->sensor == SEN_OV7670) { /* guesses, based on 7640 */ + PDEBUG(D_STREAM, "Setting framerate to %d fps", + (sd->frame_rate == 0) ? 15 : sd->frame_rate); + switch (sd->frame_rate) { + case 30: + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0xff); + break; + case 20: + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0x1b); + break; + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + } + } + +/* if (ov51x_restart(sd) < 0) + return -EIO; */ + + /* Reset it just for good measure */ +/* if (ov51x_reset(sd, OV511_RESET_NOREGS) < 0) + return -EIO; */ + return 0; +} + +static int mode_init_ov_sensor_regs(struct sd *sd, + struct ovsensor_window *win) +{ + int qvga = win->quarter; + + /******** Mode (VGA/QVGA) and sensor specific regs ********/ + switch (sd->sensor) { + case SEN_OV8610: + /* For OV8610 qvga means qsvga */ + i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + break; + case SEN_OV7610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV7620: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV76BE: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV7640: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); +/* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */ +/* i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); */ +/* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */ +/* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */ +/* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */ + break; + case SEN_OV7670: + /* set COM7_FMT_VGA or COM7_FMT_QVGA + * do we need to set anything else? + * HSTART etc are set in set_ov_sensor_window itself */ + i2c_w_mask(sd, OV7670_REG_COM7, + qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, + OV7670_COM7_FMT_MASK); + break; + case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV6630: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + default: + return -EINVAL; + } + + /******** Palette-specific regs ********/ +/* Need to do work here for the OV7670 */ + + if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { + /* not valid on the OV6620/OV7620/6630? */ + i2c_w_mask(sd, 0x0e, 0x00, 0x40); + } + + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + /* OV7640 is 8-bit only */ + + if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640) + i2c_w_mask(sd, 0x13, 0x00, 0x20); +/* } */ + + /******** Clock programming ********/ + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (sd->sensor == SEN_OV6620) { + + /* Clock down */ + i2c_w(sd, 0x2a, 0x04); + i2c_w(sd, 0x11, win->clockdiv); + i2c_w(sd, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + i2c_w(sd, 0x2d, 0x85); + } else if (win->clockdiv >= 0) { + i2c_w(sd, 0x11, win->clockdiv); + } + + /******** Special Features ********/ +/* no evidence this is possible with OV7670, either */ + /* Test Pattern */ + if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670) + i2c_w_mask(sd, 0x12, 0x00, 0x02); + + /* Enable auto white balance */ + if (sd->sensor == SEN_OV7670) + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + else + i2c_w_mask(sd, 0x12, 0x04, 0x04); + + /* This will go away as soon as ov51x_mode_init_sensor_regs() */ + /* is fully tested. */ + /* 7620/6620/6630? don't have register 0x35, so play it safe */ + if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { + if (win->width == 640 /*&& win->height == 480*/) + i2c_w(sd, 0x35, 0x9e); + else + i2c_w(sd, 0x35, 0x1e); + } + return 0; +} + +static int set_ov_sensor_window(struct sd *sd, + struct ovsensor_window *win) +{ + int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; + int ret, hstart, hstop, vstop, vstart; + __u8 v; + + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ + switch (sd->sensor) { + case SEN_OV8610: + hwsbase = 0x1e; + hwebase = 0x1e; + vwsbase = 0x02; + vwebase = 0x02; + break; + case SEN_OV7610: + case SEN_OV76BE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + break; + case SEN_OV7620: + hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ + hwebase = 0x2f; + vwsbase = vwebase = 0x05; + break; + case SEN_OV7640: + hwsbase = 0x1a; + hwebase = 0x1a; + vwsbase = vwebase = 0x03; + break; + case SEN_OV7670: + /*handling of OV7670 hardware sensor start and stop values + * is very odd, compared to the other OV sensors */ + vwsbase = vwebase = hwebase = hwsbase = 0x00; + break; + default: + return -EINVAL; + } + + switch (sd->sensor) { + case SEN_OV6620: + case SEN_OV6630: + if (win->quarter) { /* QCIF */ + hwscale = 0; + vwscale = 0; + } else { /* CIF */ + hwscale = 1; + vwscale = 1; /* The datasheet says 0; + * it's wrong */ + } + break; + case SEN_OV8610: + if (win->quarter) { /* QSVGA */ + hwscale = 1; + vwscale = 1; + } else { /* SVGA */ + hwscale = 2; + vwscale = 2; + } + break; + default: /* SEN_OV7xx0 */ + if (win->quarter) { /* QVGA */ + hwscale = 1; + vwscale = 0; + } else { /* VGA */ + hwscale = 2; + vwscale = 1; + } + } + + ret = mode_init_ov_sensor_regs(sd, win); + if (ret < 0) + return ret; + + if (sd->sensor == SEN_OV8610) { + i2c_w_mask(sd, 0x2d, 0x05, 0x40); + /* old 0x95, new 0x05 from windrv 090403 */ + /* bits 5-7: reserved */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); + /* bit 5: progressive mode on */ + } + + /* The below is wrong for OV7670s because their window registers + * only store the high bits in 0x17 to 0x1a */ + + /* SRH Use sd->max values instead of requested win values */ + /* SCS Since we're sticking with only the max hardware widths + * for a given mode */ + /* I can hard code this for OV7670s */ + /* Yes, these numbers do look odd, but they're tested and work! */ + if (sd->sensor == SEN_OV7670) { + if (win->quarter) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + hstart = 164; + hstop = 20; + vstart = 14; + vstop = 494; + } else { /* VGA */ + hstart = 158; + hstop = 14; + vstart = 10; + vstop = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_REG_HSTART, (hstart >> 3) & 0xff); + i2c_w(sd, OV7670_REG_HSTOP, (hstop >> 3) & 0xff); + v = i2c_r(sd, OV7670_REG_HREF); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_HREF, v); + + i2c_w(sd, OV7670_REG_VSTART, (vstart >> 2) & 0xff); + i2c_w(sd, OV7670_REG_VSTOP, (vstop >> 2) & 0xff); + v = i2c_r(sd, OV7670_REG_VREF); + v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_VREF, v); + + } else { + i2c_w(sd, 0x17, hwsbase + (win->x >> hwscale)); + i2c_w(sd, 0x18, hwebase + ((win->x + win->width) >> hwscale)); + i2c_w(sd, 0x19, vwsbase + (win->y >> vwscale)); + i2c_w(sd, 0x1a, vwebase + ((win->y + win->height) >> vwscale)); + } + return 0; +} + +static int ov_sensor_mode_setup(struct sd *sd, + int width, int height) +{ + struct ovsensor_window win; + +/* win.format = mode; */ + + /* Unless subcapture is enabled, + * center the image window and downsample + * if possible to increase the field of view */ + /* NOTE: OV518(+) and OV519 does downsampling on its own */ + win.width = width; + win.height = height; + if (width == sd->maxwidth) + win.quarter = 0; + else + win.quarter = 1; + + /* Center it */ + win.x = (win.width - width) / 2; + win.y = (win.height - height) / 2; + + /* Clock is determined by OV519 frame rate code */ + win.clockdiv = sd->clockdiv; + + PDEBUG(D_CONF, "Setting clock divider to %d", win.clockdiv); + return set_ov_sensor_window(sd, &win); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + + ret = ov519_mode_init_regs(sd, gspca_dev->width, gspca_dev->height); + if (ret < 0) + goto out; + ret = ov_sensor_mode_setup(sd, gspca_dev->width, gspca_dev->height); + if (ret < 0) + goto out; + + ret = ov51x_restart((struct sd *) gspca_dev); + if (ret < 0) + goto out; + PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); + ov51x_led_control(sd, 1); + return; +out: + PDEBUG(D_ERR, "camera start error:%d", ret); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + ov51x_stop((struct sd *) gspca_dev); + ov51x_led_control((struct sd *) gspca_dev, 0); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* Header of ov519 is 16 bytes: + * Byte Value Description + * 0 0xff magic + * 1 0xff magic + * 2 0xff magic + * 3 0xXX 0x50 = SOF, 0x51 = EOF + * 9 0xXX 0x01 initial frame without data, + * 0x00 standard frame with image + * 14 Lo in EOF: length of image data / 8 + * 15 Hi + */ + + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { + switch (data[3]) { + case 0x50: /* start of frame */ +#define HDRSZ 16 + data += HDRSZ; + len -= HDRSZ; +#undef HDRSZ + if (data[0] == 0xff || data[1] == 0xd8) + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + else + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + case 0x51: /* end of frame */ + if (data[9] != 0) + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + return; + } + } + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); +} + +/* -- management routines -- */ + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->brightness; + PDEBUG(D_CONF, "brightness:%d", val); +/* was_streaming = gspca_dev->streaming; + * if (was_streaming) + * ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7640: + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7620: + /* 7620 doesn't like manual changes when in auto mode */ +/*fixme + * if (!sd->auto_brt) */ + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7670: +/*jfm - from windblows + * i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_AEC); */ + i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val)); + break; + } +/* if (was_streaming) + * ov51x_restart(sd); */ +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->contrast; + PDEBUG(D_CONF, "contrast:%d", val); +/* was_streaming = gspca_dev->streaming; + if (was_streaming) + ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV7610: + case SEN_OV6620: + i2c_w(sd, OV7610_REG_CNT, val); + break; + case SEN_OV6630: + i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); + case SEN_OV8610: { + static __u8 ctab[] = { + 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 5]); + break; + } + case SEN_OV7620: { + static __u8 ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 4]); + break; + } + case SEN_OV7640: + /* Use gain control instead. */ + i2c_w(sd, OV7610_REG_GAIN, val >> 2); + break; + case SEN_OV7670: + /* check that this isn't just the same as ov7610 */ + i2c_w(sd, OV7670_REG_CONTRAS, val >> 1); + break; + } +/* if (was_streaming) + ov51x_restart(sd); */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->colors; + PDEBUG(D_CONF, "saturation:%d", val); +/* was_streaming = gspca_dev->streaming; + if (was_streaming) + ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7620: + /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e); + if (rc < 0) + goto out; */ + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7640: + i2c_w(sd, OV7610_REG_SAT, val & 0xf0); + break; + case SEN_OV7670: + /* supported later once I work out how to do it + * transparently fail now! */ + /* set REG_COM13 values for UV sat auto mode */ + break; + } +/* if (was_streaming) + ov51x_restart(sd); */ +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4052), DVNM("Creative Live! VISTA IM")}, + {USB_DEVICE(0x041e, 0x405f), DVNM("Creative Live! VISTA VF0330")}, + {USB_DEVICE(0x041e, 0x4060), DVNM("Creative Live! VISTA VF0350")}, + {USB_DEVICE(0x041e, 0x4061), DVNM("Creative Live! VISTA VF0400")}, + {USB_DEVICE(0x041e, 0x4064), DVNM("Creative Live! VISTA VF0420")}, + {USB_DEVICE(0x041e, 0x4068), DVNM("Creative Live! VISTA VF0470")}, + {USB_DEVICE(0x045e, 0x028c), DVNM("Microsoft xbox cam")}, + {USB_DEVICE(0x054c, 0x0154), DVNM("Sonny toy4")}, + {USB_DEVICE(0x054c, 0x0155), DVNM("Sonny toy5")}, + {USB_DEVICE(0x05a9, 0x0519), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x0530), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x4519), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x8519), DVNM("OmniVision")}, + {} +}; +#undef DVNAME +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(frame_rate, int, 0644); +MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)"); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 482ef4a6afc0..72a5b89cd59d 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -27,8 +27,8 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 15) -static const char version[] = "0.2.15"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Pixart PAC207"); @@ -297,7 +297,6 @@ static int sd_open(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; sd->autogain = 1; - return 0; } @@ -338,7 +337,7 @@ static void sd_start(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ - udelay(1000); /* taken from gspca */ + msleep(10); pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ sd->sof_read = 0; @@ -743,8 +742,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, PDEBUG(D_STREAM, "Incomplete frame"); } pac207_decode_frame_init(gspca_dev); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, - 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); len -= sof - data; data = sof; } diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c new file mode 100644 index 000000000000..8f51976db995 --- /dev/null +++ b/drivers/media/video/gspca/pac7311.c @@ -0,0 +1,754 @@ +/* + * Pixart PAC7311 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "pac7311" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7311"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int avg_lum; + + unsigned char brightness; +#define BRIGHTNESS_MAX 0x20 + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char ffseq; + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = BRIGHTNESS_MAX, + .step = 1, + .default_value = 0x10, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 160, 120, 2}, + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header) /* (594) */ + +const unsigned char pac7311_jpeg_header[] = { + 0xff, 0xd8, + 0xff, 0xe0, 0x00, 0x03, 0x20, + 0xff, 0xc0, 0x00, 0x11, 0x08, + 0x01, 0xe0, /* 12: height */ + 0x02, 0x80, /* 14: width */ + 0x03, /* 16 */ + 0x01, 0x21, 0x00, + 0x02, 0x11, 0x01, + 0x03, 0x11, 0x01, + 0xff, 0xdb, 0x00, 0x84, + 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, + 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, + 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, + 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, + 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, + 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, + 0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, + 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, + 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, + 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, + 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, + 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, + 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, + 0x11, 0x00, 0x3f, 0x00 +}; + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, + 500); +} + +static void pac7311_reg_read(struct usb_device *dev, __u16 index, + __u8 *buffer) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, 1, + 500); +} + +static void pac7311_reg_write(struct usb_device *dev, + __u16 index, + __u8 value) +{ + __u8 buf; + + buf = value; + reg_w(dev, 0x00, value, index, &buf, 1); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + + PDEBUG(D_CONF, "Find Sensor PAC7311"); + pac7311_reg_write(dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x27, 0x80); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x53); + pac7311_reg_write(dev, 0x2a, 0x0e); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x3e, 0x20); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x05; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int brightness; + +/*jfm: inverted?*/ + brightness = BRIGHTNESS_MAX - sd->brightness; + pac7311_reg_write(gspca_dev->dev, 0xff, 0x04); + /* pac7311_reg_write(gspca_dev->dev, 0x0e, 0x00); */ + pac7311_reg_write(gspca_dev->dev, 0x0f, brightness); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "brightness: %i", brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + pac7311_reg_write(gspca_dev->dev, 0xff, 0x01); + pac7311_reg_write(gspca_dev->dev, 0x80, sd->contrast); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "contrast: %i", sd->contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + pac7311_reg_write(gspca_dev->dev, 0xff, 0x01); + pac7311_reg_write(gspca_dev->dev, 0x10, sd->colors); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + pac7311_reg_write(gspca_dev->dev, 0x78, 0x00); /* Turn on LED */ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; + + pac7311_reg_write(dev, 0xff, 0x01); + reg_w(dev, 0x01, 0, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8); + reg_w(dev, 0x01, 0, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8); + reg_w(dev, 0x01, 0, 0x0012, "\x00\x07\x00\x0a\x10\x00\xa0\x10", 8); + reg_w(dev, 0x01, 0, 0x001a, "\x02\x00\x00\x00\x00\x0b\x01\x00", 8); + reg_w(dev, 0x01, 0, 0x0022, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); + reg_w(dev, 0x01, 0, 0x002a, "\x00\x00\x00", 3); + reg_w(dev, 0x01, 0, 0x003e, "\x00\x00\x78\x52\x4a\x52\x78\x6e", 8); + reg_w(dev, 0x01, 0, 0x0046, "\x48\x46\x48\x6e\x5f\x49\x42\x49", 8); + reg_w(dev, 0x01, 0, 0x004e, "\x5f\x5f\x49\x42\x49\x5f\x6e\x48", 8); + reg_w(dev, 0x01, 0, 0x0056, "\x46\x48\x6e\x78\x52\x4a\x52\x78", 8); + reg_w(dev, 0x01, 0, 0x005e, "\x00\x00\x09\x1b\x34\x49\x5c\x9b", 8); + reg_w(dev, 0x01, 0, 0x0066, "\xd0\xff", 2); + reg_w(dev, 0x01, 0, 0x0078, "\x44\x00\xf2\x01\x01\x80", 6); + reg_w(dev, 0x01, 0, 0x007f, "\x2a\x1c\x00\xc8\x02\x58\x03\x84", 8); + reg_w(dev, 0x01, 0, 0x0087, "\x12\x00\x1a\x04\x08\x0c\x10\x14", 8); + reg_w(dev, 0x01, 0, 0x008f, "\x18\x20", 2); + reg_w(dev, 0x01, 0, 0x0096, "\x01\x08\x04", 3); + reg_w(dev, 0x01, 0, 0x00a0, "\x44\x44\x44\x04", 4); + reg_w(dev, 0x01, 0, 0x00f0, "\x01\x00\x00\x00\x22\x00\x20\x00", 8); + reg_w(dev, 0x01, 0, 0x00f8, "\x3f\x00\x0a\x01\x00", 5); + + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x04); + pac7311_reg_write(dev, 0x03, 0x54); + pac7311_reg_write(dev, 0x04, 0x07); + pac7311_reg_write(dev, 0x05, 0x2b); + pac7311_reg_write(dev, 0x06, 0x09); + pac7311_reg_write(dev, 0x07, 0x0f); + pac7311_reg_write(dev, 0x08, 0x09); + pac7311_reg_write(dev, 0x09, 0x00); + pac7311_reg_write(dev, 0x0c, 0x07); + pac7311_reg_write(dev, 0x0d, 0x00); + pac7311_reg_write(dev, 0x0e, 0x00); + pac7311_reg_write(dev, 0x0f, 0x62); + pac7311_reg_write(dev, 0x10, 0x08); + pac7311_reg_write(dev, 0x12, 0x07); + pac7311_reg_write(dev, 0x13, 0x00); + pac7311_reg_write(dev, 0x14, 0x00); + pac7311_reg_write(dev, 0x15, 0x00); + pac7311_reg_write(dev, 0x16, 0x00); + pac7311_reg_write(dev, 0x17, 0x00); + pac7311_reg_write(dev, 0x18, 0x00); + pac7311_reg_write(dev, 0x19, 0x00); + pac7311_reg_write(dev, 0x1a, 0x00); + pac7311_reg_write(dev, 0x1b, 0x03); + pac7311_reg_write(dev, 0x1c, 0xa0); + pac7311_reg_write(dev, 0x1d, 0x01); + pac7311_reg_write(dev, 0x1e, 0xf4); + pac7311_reg_write(dev, 0x21, 0x00); + pac7311_reg_write(dev, 0x22, 0x08); + pac7311_reg_write(dev, 0x24, 0x03); + pac7311_reg_write(dev, 0x26, 0x00); + pac7311_reg_write(dev, 0x27, 0x01); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x10); + pac7311_reg_write(dev, 0x2a, 0x06); + pac7311_reg_write(dev, 0x2b, 0x78); + pac7311_reg_write(dev, 0x2c, 0x00); + pac7311_reg_write(dev, 0x2d, 0x00); + pac7311_reg_write(dev, 0x2e, 0x00); + pac7311_reg_write(dev, 0x2f, 0x00); + pac7311_reg_write(dev, 0x30, 0x23); + pac7311_reg_write(dev, 0x31, 0x28); + pac7311_reg_write(dev, 0x32, 0x04); + pac7311_reg_write(dev, 0x33, 0x11); + pac7311_reg_write(dev, 0x34, 0x00); + pac7311_reg_write(dev, 0x35, 0x00); + pac7311_reg_write(dev, 0x11, 0x01); + setcontrast(gspca_dev); + setbrightness(gspca_dev); + setcolors(gspca_dev); + + /* set correct resolution */ + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 2: /* 160x120 */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x03); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x08, 0x09); + pac7311_reg_write(dev, 0x17, 0x20); + pac7311_reg_write(dev, 0x1b, 0x00); +/* pac7311_reg_write(dev, 0x80, 0x69); */ + pac7311_reg_write(dev, 0x87, 0x10); + break; + case 1: /* 320x240 */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x03); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x08, 0x09); + pac7311_reg_write(dev, 0x17, 0x30); +/* pac7311_reg_write(dev, 0x80, 0x3f); */ + pac7311_reg_write(dev, 0x87, 0x11); + break; + case 0: /* 640x480 */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x03); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x08, 0x08); + pac7311_reg_write(dev, 0x17, 0x00); +/* pac7311_reg_write(dev, 0x80, 0x1c); */ + pac7311_reg_write(dev, 0x87, 0x12); + break; + } + + /* start stream */ + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x78, 0x04); + pac7311_reg_write(dev, 0x78, 0x05); + + if (sd->autogain) { + sd->ag_cnt = AG_CNT_START; + sd->avg_lum = 0; + } else { + sd->ag_cnt = -1; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x27, 0x80); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x53); + pac7311_reg_write(dev, 0x2a, 0x0e); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x3e, 0x20); + pac7311_reg_write(dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x27, 0x80); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x53); + pac7311_reg_write(dev, 0x2a, 0x0e); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x3e, 0x20); + pac7311_reg_write(dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ +} + +static void setautogain(struct gspca_dev *gspca_dev, int luma) +{ + int luma_mean = 128; + int luma_delta = 20; + __u8 spring = 5; + __u8 Pxclk; + int Gbright; + + pac7311_reg_read(gspca_dev->dev, 0x02, &Pxclk); + Gbright = Pxclk; + PDEBUG(D_FRAM, "luma mean %d", luma); + if (luma < luma_mean - luma_delta || + luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + if (Gbright > 0x1a) + Gbright = 0x1a; + else if (Gbright < 4) + Gbright = 4; + PDEBUG(D_FRAM, "gbright %d", Gbright); + pac7311_reg_write(gspca_dev->dev, 0xff, 0x04); + pac7311_reg_write(gspca_dev->dev, 0x0f, Gbright); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char tmpbuf[4]; + int i, p, ffseq; + +/* if (len < 5) { */ + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + + ffseq = sd->ffseq; + + for (p = 0; p < len - 6; p++) { + if ((data[0 + p] == 0xff) + && (data[1 + p] == 0xff) + && (data[2 + p] == 0x00) + && (data[3 + p] == 0xff) + && (data[4 + p] == 0x96)) { + + /* start of frame */ + if (sd->ag_cnt >= 0 && p > 28) { + sd->avg_lum += data[p - 23]; + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev, + sd->avg_lum / AG_CNT_START); + sd->avg_lum = 0; + } + } + + /* copy the end of data to the current frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, p); + + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + (unsigned char *) pac7311_jpeg_header, + 12); + tmpbuf[0] = gspca_dev->height >> 8; + tmpbuf[1] = gspca_dev->height & 0xff; + tmpbuf[2] = gspca_dev->width >> 8; + tmpbuf[3] = gspca_dev->width & 0xff; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + tmpbuf, 4); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + (unsigned char *) &pac7311_jpeg_header[16], + PAC7311_JPEG_HEADER_SIZE - 16); + + data += p + 7; + len -= p + 7; + ffseq = 0; + break; + } + } + + /* remove the 'ff ff ff xx' sequences */ + switch (ffseq) { + case 3: + data += 1; + len -= 1; + break; + case 2: + if (data[0] == 0xff) { + data += 2; + len -= 2; + frame->data_end -= 2; + } + break; + case 1: + if (data[0] == 0xff + && data[1] == 0xff) { + data += 3; + len -= 3; + frame->data_end -= 1; + } + break; + } + for (i = 0; i < len - 4; i++) { + if (data[i] == 0xff + && data[i + 1] == 0xff + && data[i + 2] == 0xff) { + memmove(&data[i], &data[i + 4], len - i - 4); + len -= 4; + } + } + ffseq = 0; + if (data[len - 4] == 0xff) { + if (data[len - 3] == 0xff + && data[len - 2] == 0xff) { + len -= 4; + } + } else if (data[len - 3] == 0xff) { + if (data[len - 2] == 0xff + && data[len - 1] == 0xff) + ffseq = 3; + } else if (data[len - 2] == 0xff) { + if (data[len - 1] == 0xff) + ffseq = 2; + } else if (data[len - 1] == 0xff) + ffseq = 1; + sd->ffseq = ffseq; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ +/* __u8 brightness = 0; + + pac7311_reg_read(gspca_dev->dev, 0x0008, &brightness); + spca50x->brightness = brightness; + return spca50x->brightness; */ +/* PDEBUG(D_CONF, "Called pac7311_getbrightness: Not implemented yet"); */ +} + + + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + +/* getcontrast(gspca_dev); */ + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + +/* getcolors(gspca_dev); */ + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) { + sd->ag_cnt = AG_CNT_START; + sd->avg_lum = 0; + } else { + sd->ag_cnt = -1; + } + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x2600), DVNM("Typhoon")}, + {USB_DEVICE(0x093a, 0x2601), DVNM("Philips SPC610NC")}, + {USB_DEVICE(0x093a, 0x2603), DVNM("PAC7312")}, + {USB_DEVICE(0x093a, 0x2608), DVNM("Trust WB-3300p")}, + {USB_DEVICE(0x093a, 0x260e), DVNM("Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350")}, + {USB_DEVICE(0x093a, 0x260f), DVNM("SnakeCam")}, + {USB_DEVICE(0x093a, 0x2621), DVNM("PAC731x")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c new file mode 100644 index 000000000000..d26255ddfd5b --- /dev/null +++ b/drivers/media/video/gspca/sonixb.c @@ -0,0 +1,879 @@ +/* + * sonix sn9c102 (bayer) library + * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr + * Add Pas106 Stefano Mozzi (C) 2004 + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sonixb" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + + char sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131R 0 +#define SENSOR_OV6650 1 +#define SENSOR_OV7630 2 +#define SENSOR_OV7630_3 3 +#define SENSOR_PAS106 4 +#define SENSOR_PAS202 5 +#define SENSOR_TAS5110 6 +#define SENSOR_TAS5130CXX 7 +}; + +#define COMP2 0x8f +#define COMP 0xc7 /* 0x87 //0x07 */ +#define COMP1 0xc9 /* 0x89 //0x09 */ + +#define MCK_INIT 0x63 +#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/ + +#define SYS_CLK 0x04 + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +}; + +/* fixme: should have V4L2_PIX_FMT_SN9C10X */ +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_SN9C10X, 160, 120, 2}, + {V4L2_PIX_FMT_SN9C10X, 320, 240, 1}, + {V4L2_PIX_FMT_SN9C10X, 640, 480, 0}, +}; +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SN9C10X, 176, 144, 1}, + {V4L2_PIX_FMT_SN9C10X, 352, 288, 0}, +}; + +static const __u8 probe_ov7630[] = {0x08, 0x44}; + +static const __u8 initHv7131[] = { + 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* shift from 0x02 0x01 0x00 */ + 0x28, 0x1e, 0x60, 0x8a, 0x20, + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c +}; +static const __u8 hv7131_sensor_init[][8] = { + {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, + {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, + {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, + {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, + {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, +}; +static const __u8 initOv6650[] = { + 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x0b, + 0x10, 0x1d, 0x10, 0x00, 0x06, 0x1f, 0x00 +}; +static const __u8 ov6650_sensor_init[][8] = +{ + /* Bright, contrast, etc are set througth SCBB interface. + * AVCAP on win2 do not send any data on this controls. */ + /* Anyway, some registers appears to alter bright and constrat */ + {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, + {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, +/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, + * THIS SET GREEN SCREEN + * (pixels could be innverted in decode kind of "brg", + * but blue wont be there. Avoid this data ... */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, + {0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10}, + {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, + {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, + {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ + {0xa0, 0x60, 0x10, 0x5d, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x2d, 0x0a, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x33, 0x40, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x11, 0xc0, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x00, 0x16, 0x99, 0x04, 0x94, 0x15}, /* bright / Lumino */ + {0xa0, 0x60, 0x2b, 0xab, 0x99, 0x04, 0x94, 0x15}, + /* ?flicker o brillo */ + {0xa0, 0x60, 0x2d, 0x2a, 0x99, 0x04, 0x94, 0x15}, + {0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x33, 0x00, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x10, 0x57, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, + /* Low Light (Enabled: 0x32 0x1 | Disabled: 0x32 0x00) */ + {0xa0, 0x60, 0x33, 0x29, 0x99, 0x04, 0x94, 0x16}, + /* Low Ligth (Enabled: 0x33 0x13 | Disabled: 0x33 0x29) */ +/* {0xa0, 0x60, 0x11, 0xc1, 0x99, 0x04, 0x94, 0x16}, */ + {0xa0, 0x60, 0x00, 0x17, 0x99, 0x04, 0x94, 0x15}, /* clip? r */ + {0xa0, 0x60, 0x00, 0x18, 0x99, 0x04, 0x94, 0x15}, /* clip? r */ +}; +static const __u8 initOv7630[] = { + 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ +}; +static const __u8 initOv7630_3[] = { + 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ +}; +static const __u8 ov7630_sensor_init_com[][8] = { + {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, +/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ + {0xd0, 0x21, 0x12, 0x78, 0x00, 0x80, 0x34, 0x10}, /* jfm */ + {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, + {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, +/* {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10}, jfm */ + {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, /* jfm */ + {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, + {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, + {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, + {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, + {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, +}; +static const __u8 ov7630_sensor_init[][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 200ms */ + {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x10}, /* jfm */ + {0xa0, 0x21, 0x10, 0x57, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x76, 0x02, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ +}; +static const __u8 ov7630_sensor_init_3[][8] = { + {0xa0, 0x21, 0x10, 0x36, 0xbd, 0x06, 0xf6, 0x16}, /* exposure */ + {0xa0, 0x21, 0x76, 0x03, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ +/* {0xb0, 0x21, 0x2a, 0xc0, 0x3c, 0x06, 0xf6, 0x1d}, + * a0 1c,a0 1f,c0 3c frame rate ?line interval from ov6630 */ +/* {0xb0, 0x21, 0x2a, 0xa0, 0x1f, 0x06, 0xf6, 0x1d}, * from win */ + {0xb0, 0x21, 0x2a, 0xa0, 0x1c, 0x06, 0xf6, 0x1d}, +}; + +static const __u8 initPas106[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, + 0x16, 0x12, 0x28, COMP1, MCK_INIT1, + 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c +}; +/* compression 0x86 mckinit1 0x2b */ +static const __u8 pas106_data[][2] = { + {0x02, 0x04}, /* Pixel Clock Divider 6 */ + {0x03, 0x13}, /* Frame Time MSB */ +/* {0x03, 0x12}, * Frame Time MSB */ + {0x04, 0x06}, /* Frame Time LSB */ +/* {0x04, 0x05}, * Frame Time LSB */ + {0x05, 0x65}, /* Shutter Time Line Offset */ +/* {0x05, 0x6d}, * Shutter Time Line Offset */ +/* {0x06, 0xb1}, * Shutter Time Pixel Offset */ + {0x06, 0xcd}, /* Shutter Time Pixel Offset */ + {0x07, 0xc1}, /* Black Level Subtract Sign */ +/* {0x07, 0x00}, * Black Level Subtract Sign */ + {0x08, 0x06}, /* Black Level Subtract Level */ + {0x08, 0x06}, /* Black Level Subtract Level */ +/* {0x08, 0x01}, * Black Level Subtract Level */ + {0x09, 0x05}, /* Color Gain B Pixel 5 a */ + {0x0a, 0x04}, /* Color Gain G1 Pixel 1 5 */ + {0x0b, 0x04}, /* Color Gain G2 Pixel 1 0 5 */ + {0x0c, 0x05}, /* Color Gain R Pixel 3 1 */ + {0x0d, 0x00}, /* Color GainH Pixel */ + {0x0e, 0x0e}, /* Global Gain */ + {0x0f, 0x00}, /* Contrast */ + {0x10, 0x06}, /* H&V synchro polarity */ + {0x11, 0x06}, /* ?default */ + {0x12, 0x06}, /* DAC scale */ + {0x14, 0x02}, /* ?default */ + {0x13, 0x01}, /* Validate Settings */ +}; +static const __u8 initPas202[] = { + 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x03, 0x0a, /* 6 */ + 0x28, 0x1e, 0x28, 0x89, 0x30, + 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c +}; +static const __u8 pas202_sensor_init[][8] = { + {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, + {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, + {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, + {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10}, + {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10}, + + {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15}, + {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, + {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}, + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, +}; + +static const __u8 initTas5110[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, 0x00, 0x46, 0x09, 0x0a, /* shift from 0x45 0x09 0x0a */ + 0x16, 0x12, 0x60, 0x86, 0x2b, + 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 +}; +static const __u8 tas5110_sensor_init[][8] = { + {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, + {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, + {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, +}; + +static const __u8 initTas5130[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a, + 0x28, 0x1e, 0x60, COMP, MCK_INIT, + 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c +}; +static const __u8 tas5130_sensor_init[][8] = { +/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, + * shutter 0x47 short exposure? */ + {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10}, + /* shutter 0x01 long exposure */ + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, +}; + +static void reg_r(struct usb_device *dev, + __u16 value, __u8 *buffer) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + buffer, 1, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 value, + const __u8 *buffer, + __u16 len) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0x08, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + (__u8 *) buffer, len, + 500); +} + +static int i2c_w(struct usb_device *dev, const __u8 *buffer) +{ + int retry = 60; + __u8 ByteReceive; + + /* is i2c ready */ + reg_w(dev, 0x08, buffer, 8); + while (retry--) { + msleep(10); + reg_r(dev, 0x08, &ByteReceive); + if (ByteReceive == 4) + return 0; + } + return -1; +} + +static void i2c_w_vector(struct usb_device *dev, + const __u8 buffer[][8], int len) +{ + for (;;) { + reg_w(dev, 0x08, *buffer, 8); + len -= 8; + if (len <= 0) + break; + buffer++; + } +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + + switch (sd->sensor) { + case SENSOR_OV6650: { + __u8 i2cOV6650[] = + {0xa0, 0x60, 0x06, 0x11, 0x99, 0x04, 0x94, 0x15}; + + i2cOV6650[3] = sd->brightness; + if (i2c_w(gspca_dev->dev, i2cOV6650) < 0) + goto err; + break; + } + case SENSOR_OV7630: { + __u8 i2cOV[] = + {0xa0, 0x21, 0x06, 0x36, 0xbd, 0x06, 0xf6, 0x16}; + + /* change reg 0x06 */ + i2cOV[3] = sd->brightness; + if (i2c_w(gspca_dev->dev, i2cOV) < 0) + goto err; + break; + } + case SENSOR_PAS106: { + __u8 i2c1[] = + {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}; + + i2c1[3] = sd->brightness >> 3; + i2c1[2] = 0x0e; + if (i2c_w(gspca_dev->dev, i2c1) < 0) + goto err; + i2c1[3] = 0x01; + i2c1[2] = 0x13; + if (i2c_w(gspca_dev->dev, i2c1) < 0) + goto err; + break; + } + case SENSOR_PAS202: { + /* __u8 i2cpexpo1[] = + {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */ + __u8 i2cpexpo[] = + {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16}; + __u8 i2cp202[] = + {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}; + static __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}; + + /* change reg 0x10 */ + i2cpexpo[4] = 0xff - sd->brightness; +/* if(i2c_w(gspca_dev->dev,i2cpexpo1) < 0) + goto err; */ +/* if(i2c_w(gspca_dev->dev,i2cpdoit) < 0) + goto err; */ + if (i2c_w(gspca_dev->dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev->dev, i2cpdoit) < 0) + goto err; + i2cp202[3] = sd->brightness >> 3; + if (i2c_w(gspca_dev->dev, i2cp202) < 0) + goto err; + if (i2c_w(gspca_dev->dev, i2cpdoit) < 0) + goto err; + break; + } + case SENSOR_TAS5130CXX: + case SENSOR_TAS5110: { + __u8 i2c[] = + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; + + value = 0xff - sd->brightness; + i2c[4] = value; + PDEBUG(D_CONF, "brightness %d : %d", value, i2c[4]); + if (i2c_w(gspca_dev->dev, i2c) < 0) + goto err; + break; + } + } + return; +err: + PDEBUG(D_ERR, "i2c error brightness"); +} +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 gain; + __u8 rgb_value; + + gain = sd->contrast >> 4; + /* red and blue gain */ + rgb_value = gain << 4 | gain; + reg_w(gspca_dev->dev, 0x10, &rgb_value, 1); + /* green gain */ + rgb_value = gain; + reg_w(gspca_dev->dev, 0x11, &rgb_value, 1); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; +/* __u16 vendor; */ + __u16 product; + int sif = 0; + +/* vendor = id->idVendor; */ + product = id->idProduct; +/* switch (vendor) { */ +/* case 0x0c45: * Sonix */ + switch (product) { + case 0x6001: /* SN9C102 */ + case 0x6005: /* SN9C101 */ + case 0x6007: /* SN9C101 */ + sd->sensor = SENSOR_TAS5110; + sif = 1; + break; + case 0x6009: /* SN9C101 */ + case 0x600d: /* SN9C101 */ + case 0x6029: /* SN9C101 */ + sd->sensor = SENSOR_PAS106; + sif = 1; + break; + case 0x6011: /* SN9C101 - SN9C101G */ + sd->sensor = SENSOR_OV6650; + sif = 1; + break; + case 0x6019: /* SN9C101 */ + case 0x602c: /* SN9C102 */ + case 0x602e: /* SN9C102 */ + sd->sensor = SENSOR_OV7630; + break; + case 0x60b0: /* SN9C103 */ + sd->sensor = SENSOR_OV7630_3; + break; + case 0x6024: /* SN9C102 */ + case 0x6025: /* SN9C102 */ + sd->sensor = SENSOR_TAS5130CXX; + break; + case 0x6028: /* SN9C102 */ + sd->sensor = SENSOR_PAS202; + break; + case 0x602d: /* SN9C102 */ + sd->sensor = SENSOR_HV7131R; + break; + case 0x60af: /* SN9C103 */ + sd->sensor = SENSOR_PAS202; + break; + } +/* break; */ +/* } */ + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + if (!sif) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */ + reg_w(gspca_dev->dev, 0x01, probe_ov7630, sizeof probe_ov7630); + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + __u8 ByteReceive; + + reg_r(gspca_dev->dev, 0x00, &ByteReceive); + if (ByteReceive != 0x10) + return -ENODEV; + return 0; +} + +static void pas106_i2cinit(struct usb_device *dev) +{ + int i; + const __u8 *data; + __u8 i2c1[] = { 0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; + + i = ARRAY_SIZE(pas106_data); + data = pas106_data[0]; + while (--i >= 0) { + memcpy(&i2c1[2], data, 2); + /* copy 2 bytes from the template */ + if (i2c_w(dev, i2c1) < 0) + PDEBUG(D_ERR, "i2c error pas106"); + data += 2; + } +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int mode, l; + const __u8 *sn9c10x; + __u8 reg01, reg17; + __u8 reg17_19[3]; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + switch (sd->sensor) { + case SENSOR_HV7131R: + sn9c10x = initHv7131; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | 0x8a; + reg17_19[2] = 0x20; + break; + case SENSOR_OV6650: + sn9c10x = initOv6650; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | 0x8b; + reg17_19[2] = 0x20; + break; + case SENSOR_OV7630: + sn9c10x = initOv7630; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | COMP2; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_OV7630_3: + sn9c10x = initOv7630_3; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | COMP2; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_PAS106: + sn9c10x = initPas106; + reg17_19[0] = 0x24; /* 0x28 */ + reg17_19[1] = (mode << 4) | COMP1; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_PAS202: + sn9c10x = initPas202; + reg17_19[0] = mode ? 0x24 : 0x20; + reg17_19[1] = (mode << 4) | 0x89; + reg17_19[2] = 0x20; + break; + case SENSOR_TAS5110: + sn9c10x = initTas5110; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | 0x86; + reg17_19[2] = 0x2b; /* 0xf3; */ + break; + default: +/* case SENSOR_TAS5130CXX: */ + sn9c10x = initTas5130; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | COMP; + reg17_19[2] = mode ? 0x23 : 0x43; + break; + } + switch (sd->sensor) { + case SENSOR_OV7630: + reg01 = 0x06; + reg17 = 0x29; + l = 0x10; + break; + case SENSOR_OV7630_3: + reg01 = 0x44; + reg17 = 0x68; + l = 0x10; + break; + default: + reg01 = sn9c10x[0]; + reg17 = sn9c10x[0x17 - 1]; + l = 0x1f; + break; + } + + /* reg 0x01 bit 2 video transfert on */ + reg_w(dev, 0x01, ®01, 1); + /* reg 0x17 SensorClk enable inv Clk 0x60 */ + reg_w(dev, 0x17, ®17, 1); +/*fixme: for ov7630 102 + reg_w(dev, 0x01, {0x06, sn9c10x[1]}, 2); */ + /* Set the registers from the template */ + reg_w(dev, 0x01, sn9c10x, l); + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_w_vector(dev, hv7131_sensor_init, + sizeof hv7131_sensor_init); + break; + case SENSOR_OV6650: + i2c_w_vector(dev, ov6650_sensor_init, + sizeof ov6650_sensor_init); + break; + case SENSOR_OV7630: + i2c_w_vector(dev, ov7630_sensor_init_com, + sizeof ov7630_sensor_init_com); + msleep(200); + i2c_w_vector(dev, ov7630_sensor_init, + sizeof ov7630_sensor_init); + break; + case SENSOR_OV7630_3: + i2c_w_vector(dev, ov7630_sensor_init_com, + sizeof ov7630_sensor_init_com); + msleep(200); + i2c_w_vector(dev, ov7630_sensor_init_3, + sizeof ov7630_sensor_init_3); + break; + case SENSOR_PAS106: + pas106_i2cinit(dev); + break; + case SENSOR_PAS202: + i2c_w_vector(dev, pas202_sensor_init, + sizeof pas202_sensor_init); + break; + case SENSOR_TAS5110: + i2c_w_vector(dev, tas5110_sensor_init, + sizeof tas5110_sensor_init); + break; + default: +/* case SENSOR_TAS5130CXX: */ + i2c_w_vector(dev, tas5130_sensor_init, + sizeof tas5130_sensor_init); + break; + } + /* H_size V_size 0x28, 0x1e maybe 640x480 */ + reg_w(dev, 0x15, &sn9c10x[0x15 - 1], 2); + /* compression register */ + reg_w(dev, 0x18, ®17_19[1], 1); + /* H_start */ /*fixme: not ov7630*/ + reg_w(dev, 0x12, &sn9c10x[0x12 - 1], 1); + /* V_START */ /*fixme: not ov7630*/ + reg_w(dev, 0x13, &sn9c10x[0x13 - 1], 1); + /* reset 0x17 SensorClk enable inv Clk 0x60 */ + /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ + reg_w(dev, 0x17, ®17_19[0], 1); + /*MCKSIZE ->3 */ /*fixme: not ov7630*/ + reg_w(dev, 0x19, ®17_19[2], 1); + /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ + reg_w(dev, 0x1c, &sn9c10x[0x1c - 1], 4); + /* Enable video transfert */ + reg_w(dev, 0x01, &sn9c10x[0], 1); + /* Compression */ + reg_w(dev, 0x18, ®17_19[1], 2); + msleep(20); + + setcontrast(gspca_dev); + setbrightness(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + __u8 ByteSend = 0; + + ByteSend = 0x09; /* 0X00 */ + reg_w(gspca_dev->dev, 0x01, &ByteSend, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int p; + + if (len > 6 && len < 24) { + for (p = 0; p < len - 6; p++) { + if (data[0 + p] == 0xff + && data[1 + p] == 0xff + && data[2 + p] == 0x00 + && data[3 + p] == 0xc4 + && data[4 + p] == 0xc4 + && data[5 + p] == 0x96) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, 0); + data += 12; + len -= 12; + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + return; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, + frame, data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6001), DVNM("Genius VideoCAM NB")}, + {USB_DEVICE(0x0c45, 0x6005), DVNM("Sweex Tas5110")}, + {USB_DEVICE(0x0c45, 0x6007), DVNM("Sonix sn9c101 + Tas5110D")}, + {USB_DEVICE(0x0c45, 0x6009), DVNM("spcaCam@120")}, + {USB_DEVICE(0x0c45, 0x600d), DVNM("spcaCam@120")}, + {USB_DEVICE(0x0c45, 0x6011), DVNM("MAX Webcam Microdia-OV6650-SN9C101G")}, + {USB_DEVICE(0x0c45, 0x6019), DVNM("Generic Sonix OV7630")}, + {USB_DEVICE(0x0c45, 0x6024), DVNM("Generic Sonix Tas5130c")}, + {USB_DEVICE(0x0c45, 0x6025), DVNM("Xcam Shanga")}, + {USB_DEVICE(0x0c45, 0x6028), DVNM("Sonix Btc Pc380")}, + {USB_DEVICE(0x0c45, 0x6029), DVNM("spcaCam@150")}, + {USB_DEVICE(0x0c45, 0x602c), DVNM("Generic Sonix OV7630")}, + {USB_DEVICE(0x0c45, 0x602d), DVNM("LIC-200 LG")}, + {USB_DEVICE(0x0c45, 0x602e), DVNM("Genius VideoCam Messenger")}, + {USB_DEVICE(0x0c45, 0x60af), DVNM("Trust WB3100P")}, + {USB_DEVICE(0x0c45, 0x60b0), DVNM("Genius VideoCam Look")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c new file mode 100644 index 000000000000..6180bc565ca1 --- /dev/null +++ b/drivers/media/video/gspca/sonixj.c @@ -0,0 +1,1629 @@ +/* + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sonixj" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int avg_lum; + unsigned int exposure; + + unsigned short brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + signed char ag_cnt; +#define AG_CNT_START 13 + + char qindex; + char sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131R 0 +#define SENSOR_MI0360 1 +#define SENSOR_MO4000 2 +#define SENSOR_OV7648 3 +#define SENSOR_OV7660 4 + unsigned char customid; +#define SN9C102P 0 +#define SN9C105 1 +#define SN9C110 2 +#define SN9C120 3 +#define SN9C325 4 + unsigned char i2c_base; + unsigned char i2c_ctrl_reg; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0x7fff, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 160, 120, 2}, + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +/*Data from sn9c102p+hv71331r */ +static __u8 sn_hv7131[] = { + 0x00, 0x03, 0x64, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xA1, 0x11, +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */ + 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, /* 00 */ +/* rega regb regc regd rege regf reg10 reg11 */ + 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, 0x0a, 0x00, 0x00, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */ +}; + +static __u8 sn_mi0360[] = { + 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xb1, 0x5d, +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, +/* rega regb regc regd rege regf reg10 reg11 */ + 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, 0x06, 0x00, 0x00, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */ +}; + +static __u8 sn_mo4000[] = { + 0x12, 0x23, 0x60, 0x00, 0x1A, 0x00, 0x20, 0x18, 0x81, +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, +/* reg9 rega regb regc regd rege regf reg10 reg11*/ + 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, 0x08, 0x00, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x25, 0x39, 0x4b, +/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/ + 0x5c, 0x6b, 0x79, 0x87, 0x95, 0xa2, 0xaf, 0xbb, 0xc7, + 0xd3, 0xdf, 0xea, 0xf5 +}; + +static __u8 sn_ov7648[] = { + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xA1, 0x6E, 0x18, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1E, 0x82, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static __u8 sn_ov7660[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x81, +/* reg9 rega regb regc regd rege regf reg10 reg11*/ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/ + 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, 0x07, 0x00, 0x00, +/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* sequence specific to the sensors - !! index = SENSOR_xxx */ +static __u8 *sn_tb[] = { + sn_hv7131, + sn_mi0360, + sn_mo4000, + sn_ov7648, + sn_ov7660 +}; + +static __u8 regsn20[] = { + 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, + 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff +}; +static __u8 regsn20_sn9c325[] = { + 0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4, + 0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5 +}; + +static __u8 reg84[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f, + 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f, +/* 0x00, 0x00, 0x00, 0x00, 0x00 */ + 0xf7, 0x0f, 0x0a, 0x00, 0x00 +}; +static __u8 reg84_sn9c325[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f, + 0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f, + 0xf8, 0x0f, 0x00, 0x00, 0x00 +}; + +static __u8 hv7131r_sensor_init[][8] = { + {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10}, + {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10}, + {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10}, + {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10}, + {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10}, + {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ + {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; +static __u8 mi0360_sensor_init[][8] = { + {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, + {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10}, + {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, + {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10}, + {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, + {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10}, + {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, + {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10}, + {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, + + {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10}, + {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10}, + + {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ + {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ + + {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10}, + {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */ +/* {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ +/* {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ + {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ + {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ + {0, 0, 0, 0, 0, 0, 0, 0} +}; +static __u8 mo4000_sensor_init[][8] = { + {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; +static __u8 ov7660_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ + {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, + /* Outformat ?? rawRGB */ + {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ +/* {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, + * GAIN BLUE RED VREF */ + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, + /* GAIN BLUE RED VREF */ + {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, + /* COM 1 BAVE GEAVE AECHH */ + {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ + {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ +/* {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10}, + * AECH CLKRC COM7 COM8 */ + {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, + /* AECH CLKRC COM7 COM8 */ + {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ + {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, + /* HSTART HSTOP VSTRT VSTOP */ + {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */ + {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */ + {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10}, + /* BOS GBOS GROS ROS (BGGR offset) */ +/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, + * AEW AEB VPT BBIAS */ + {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, + /* AEW AEB VPT BBIAS */ + {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10}, + /* GbBIAS RSVD EXHCH EXHCL */ + {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10}, + /* RBIAS ADVFL ASDVFH YAVE */ + {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10}, + /* HSYST HSYEN HREF */ + {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10}, + /* ADC ACOM OFON TSLB */ + {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10}, + /* COM11 COM12 COM13 COM14 */ + {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10}, + /* EDGE COM15 COM16 COM17 */ + {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */ + {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */ + {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */ + {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */ + {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */ + {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */ + {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10}, + /* LCC1 LCC2 LCC3 LCC4 */ + {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */ + {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, + {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10}, + /* band gap reference [0..3] DBLV */ + {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ + {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, +/****** (some exchanges in the win trace) ******/ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + /* bits[3..0]reserved */ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + /* VREF vertical frame ctrl */ + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* 0x20 */ + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, */ +/****** (some exchanges in the win trace) ******/ + {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ + {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10},/* dummy line low */ + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, */ +/****** (some exchanges in the win trace) ******/ +/**********startsensor KO if changed !!****/ + {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, +/* here may start the isoc exchanges */ + {0, 0, 0, 0, 0, 0, 0, 0} +}; +/* reg0x04 reg0x07 reg 0x10 */ +/* expo = (COM1 & 0x02) | (AECHH & 0x2f <<10) [ (AECh << 2) */ + +static __u8 ov7648_sensor_init[][8] = { + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10}, + {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10}, + {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10}, + {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10}, + {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10}, + {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10}, + {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10}, + {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10}, + {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10}, + {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10}, + {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10}, + {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10}, + {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10}, + /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + * This is currently setting a + * blue tint, and some things more , i leave it here for future test if + * somene is having problems with color on this sensor + {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10}, + {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, + {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */ + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */ + {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */ + {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/ +/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */ + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +static __u8 qtable4[] = { + 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, + 0x06, 0x08, 0x0A, 0x11, + 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15, + 0x19, 0x19, 0x17, 0x15, + 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17, + 0x21, 0x2E, 0x21, 0x23, + 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32, + 0x25, 0x29, 0x2C, 0x29, + 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B, + 0x17, 0x1B, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29 +}; + +static void reg_r(struct usb_device *dev, + __u16 value, + __u8 *buffer, int len) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + buffer, len, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 value, + __u8 *buffer, + int len) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + buffer, len, + 500); +} + +/* write 2 bytes */ +static void i2c_w2(struct gspca_dev *gspca_dev, + __u8 *buffer) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 mode[8]; + + /* is i2c ready */ + mode[0] = sd->i2c_ctrl_reg | (2 << 4); + mode[1] = sd->i2c_base; + mode[2] = buffer[0]; + mode[3] = buffer[1]; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + reg_w(dev, 0x08, mode, 8); +} + +/* write 8 bytes */ +static void i2c_w8(struct usb_device *dev, __u8 *buffer) +{ + reg_w(dev, 0x08, buffer, 8); + msleep(1); +} + +/* read 5 bytes */ +static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg, + __u8 *buffer) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 mode[8]; + + mode[0] = sd->i2c_ctrl_reg | 0x10; + mode[1] = sd->i2c_base; + mode[2] = reg; + mode[3] = 0; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + i2c_w8(dev, mode); + mode[0] = sd->i2c_ctrl_reg | (5 << 4) | 0x02; + mode[2] = 0; + i2c_w8(dev, mode); + reg_r(dev, 0x0a, buffer, 5); +} + +static int probesensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 reg02; + static __u8 datasend[] = { 2, 0 }; + /* reg val1 val2 val3 val4 */ + __u8 datarecd[6]; + + i2c_w2(gspca_dev, datasend); +/* should write 0xa1 0x11 0x02 0x00 0x00 0x00 0x00 the 0x10 is add by i2cw */ + msleep(10); + reg02 = 0x66; + reg_w(dev, 0x02, ®02, 1); /* Gpio on */ + msleep(10); + i2c_r5(gspca_dev, 0, datarecd); /* read sensor id */ + if (datarecd[0] == 0x02 + && datarecd[1] == 0x09 + && datarecd[2] == 0x01 + && datarecd[3] == 0x00 + && datarecd[4] == 0x00) { + PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); + sd->sensor = SENSOR_HV7131R; + return SENSOR_HV7131R; + } + PDEBUG(D_PROBE, "Find Sensor %d %d %d", + datarecd[0], datarecd[1], datarecd[2]); + PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); + return -ENODEV; +} + +static int configure_gpio(struct gspca_dev *gspca_dev, + __u8 *sn9c1xx) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 data; + __u8 regF1; + __u8 *reg9a; + static __u8 reg9a_def[] = + {0x08, 0x40, 0x20, 0x10, 0x00, 0x04}; + static __u8 reg9a_sn9c120[] = /* from win trace */ + {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; + static __u8 reg9a_sn9c325[] = + {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20}; + + + regF1 = 0x00; + reg_w(dev, 0xf1, ®F1, 1); + + reg_w(dev, 0x01, &sn9c1xx[0], 1); /*fixme:jfm was [1] en v1*/ + + /* configure gpio */ + reg_w(dev, 0x01, &sn9c1xx[1], 2); + reg_w(dev, 0x08, &sn9c1xx[8], 2); + reg_w(dev, 0x17, &sn9c1xx[0x17], 3); + switch (sd->customid) { + case SN9C325: + reg9a = reg9a_sn9c325; + break; + case SN9C120: + reg9a = reg9a_sn9c120; + break; + default: + reg9a = reg9a_def; + break; + } + reg_w(dev, 0x9a, reg9a, 6); + + data = 0x60; /*fixme:jfm 60 00 00 (3) */ + reg_w(dev, 0xd4, &data, 1); + + reg_w(dev, 0x03, &sn9c1xx[3], 0x0f); + + switch (sd->customid) { + case SN9C120: /* from win trace */ + data = 0x61; + reg_w(dev, 0x01, &data, 1); + data = 0x20; + reg_w(dev, 0x17, &data, 1); + data = 0x60; + reg_w(dev, 0x01, &data, 1); + break; + case SN9C325: + data = 0x43; + reg_w(dev, 0x01, &data, 1); + data = 0xae; + reg_w(dev, 0x17, &data, 1); + data = 0x42; + reg_w(dev, 0x01, &data, 1); + break; + default: + data = 0x43; + reg_w(dev, 0x01, &data, 1); + data = 0x61; + reg_w(dev, 0x17, &data, 1); + data = 0x42; + reg_w(dev, 0x01, &data, 1); + } + + if (sd->sensor == SENSOR_HV7131R) { + if (probesensor(gspca_dev) < 0) + return -ENODEV; + } + return 0; +} + +static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + static __u8 SetSensorClk[] = /* 0x08 Mclk */ + { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; + + while (hv7131r_sensor_init[i][0]) { + i2c_w8(dev, hv7131r_sensor_init[i]); + i++; + } + i2c_w8(dev, SetSensorClk); +} + +static void mi0360_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + + while (mi0360_sensor_init[i][0]) { + i2c_w8(dev, mi0360_sensor_init[i]); + i++; + } +} + +static void mo4000_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + + while (mo4000_sensor_init[i][0]) { + i2c_w8(dev, mo4000_sensor_init[i]); + i++; + } +} + +static void ov7648_InitSensor(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int i = 0; + + while (ov7648_sensor_init[i][0]) { + i2c_w8(dev, ov7648_sensor_init[i]); + i++; + } +} + +static void ov7660_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + + while (ov7660_sensor_init[i][0]) { + i2c_w8(dev, ov7660_sensor_init[i]); + i++; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + sd->sensor = -1; + switch (vendor) { + case 0x0458: /* Genius */ +/* switch (product) { + case 0x7025: */ + sd->customid = SN9C120; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; +/* break; + } */ + break; + case 0x045e: +/* switch (product) { + case 0x00f5: + case 0x00f7: */ + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7660; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; +/* break; + } */ + break; + case 0x0471: /* Philips */ +/* switch (product) { + case 0x0327: + case 0x0328: + case 0x0330: */ + sd->customid = SN9C105; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; +/* break; + } */ + break; + case 0x0c45: /* Sonix */ + switch (product) { + case 0x6040: + sd->customid = SN9C102P; + sd->sensor = SENSOR_MI0360; /* from BW600.inf */ +/* sd->sensor = SENSOR_HV7131R; * gspcav1 value */ + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x607a: * from BW600.inf + sd->customid = SN9C102P; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x607c: + sd->customid = SN9C102P; + sd->sensor = SENSOR_HV7131R; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x607e: * from BW600.inf + sd->customid = SN9C102P; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x60c0: + sd->customid = SN9C105; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; + break; +/* case 0x60c8: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_OM6801; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x60cc: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_HV7131GP; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x60ec: + sd->customid = SN9C105; + sd->sensor = SENSOR_MO4000; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x60ef: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_ICM105C; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x60fa: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x60fb: + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7660; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; + case 0x60fc: + sd->customid = SN9C105; + sd->sensor = SENSOR_HV7131R; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x60fe: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x6108: * from BW600.inf + sd->customid = SN9C120; + sd->sensor = SENSOR_OM6801; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x6122: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_ICM105C; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x612a: +/* sd->customid = SN9C110; * in BW600.inf */ + sd->customid = SN9C325; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x6123: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_SanyoCCD; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x612c: + sd->customid = SN9C110; + sd->sensor = SENSOR_MO4000; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x612e: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x612f: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_ICM105C; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x6130: + sd->customid = SN9C120; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; + break; + case 0x6138: + sd->customid = SN9C120; + sd->sensor = SENSOR_MO4000; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x613a: * from BW600.inf + sd->customid = SN9C120; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x613b: + sd->customid = SN9C120; + sd->sensor = SENSOR_OV7660; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; + case 0x613c: + sd->customid = SN9C120; + sd->sensor = SENSOR_HV7131R; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x613e: * from BW600.inf + sd->customid = SN9C120; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + } + break; + } + if (sd->sensor < 0) { + PDEBUG(D_ERR, "Invalid vendor/product %04x:%04x", + vendor, product); + return -EINVAL; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + sd->qindex = 4; /* set the quantization table */ + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; +/* __u8 *sn9c1xx; */ + __u8 regF1; + __u8 regGpio[] = { 0x29, 0x74 }; + + /* setup a selector by customid */ + regF1 = 0x01; + reg_w(dev, 0xf1, ®F1, 1); + reg_r(dev, 0x00, ®F1, 1); /* -> regF1 = 0x00 */ + reg_w(dev, 0xf1, ®F1, 1); + reg_r(dev, 0x00, ®F1, 1); + switch (sd->customid) { + case SN9C102P: + if (regF1 != 0x11) + return -ENODEV; + reg_w(dev, 0x02, ®Gpio[1], 1); + break; + case SN9C105: + if (regF1 != 0x11) + return -ENODEV; + reg_w(dev, 0x02, regGpio, 2); + break; + case SN9C110: + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x62; + reg_w(dev, 0x02, ®Gpio[1], 1); + break; + case SN9C120: + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x70; + reg_w(dev, 0x02, regGpio, 2); + break; + default: +/* case SN9C325: */ + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x62; + reg_w(dev, 0x02, ®Gpio[1], 1); + break; + } + + regF1 = 0x01; + reg_w(dev, 0xf1, ®F1, 1); + + return 0; +} + +static unsigned int setexposure(struct gspca_dev *gspca_dev, + unsigned int expo) +{ + struct sd *sd = (struct sd *) gspca_dev; + static __u8 doit[] = /* update sensor */ + { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; + static __u8 sensorgo[] = /* sensor on */ + { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; + static __u8 gainMo[] = + { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; + + switch (sd->sensor) { + case SENSOR_HV7131R: { + __u8 Expodoit[] = + { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; + + Expodoit[3] = expo >> 16; + Expodoit[4] = expo >> 8; + Expodoit[5] = expo; + i2c_w8(gspca_dev->dev, Expodoit); + break; + } + case SENSOR_MI0360: { + __u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ + { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + + if (expo > 0x0635) + expo = 0x0635; + else if (expo < 0x0001) + expo = 0x0001; + expoMi[3] = expo >> 8; + expoMi[4] = expo; + i2c_w8(gspca_dev->dev, expoMi); + i2c_w8(gspca_dev->dev, doit); + i2c_w8(gspca_dev->dev, sensorgo); + break; + } + case SENSOR_MO4000: { + __u8 expoMof[] = + { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; + __u8 expoMo10[] = + { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x1fff) + expo = 0x1fff; + else if (expo < 0x0001) + expo = 0x0001; + expoMof[3] = (expo & 0x03fc) >> 2; + i2c_w8(gspca_dev->dev, expoMof); + expoMo10[3] = ((expo & 0x1c00) >> 10) + | ((expo & 0x0003) << 4); + i2c_w8(gspca_dev->dev, expoMo10); + i2c_w8(gspca_dev->dev, gainMo); + PDEBUG(D_CONF," set exposure %d", + ((expoMo10[3] & 0x07) << 10) + | (expoMof[3] << 2) + | ((expoMo10[3] & 0x30) >> 4)); + break; + } + } + return expo; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned int expo; + __u8 k2; + + switch (sd->sensor) { + case SENSOR_HV7131R: + expo = sd->brightness << 4; + if (expo > 0x002dc6c0) + expo = 0x002dc6c0; + else if (expo < 0x02a0) + expo = 0x02a0; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_MI0360: + expo = sd->brightness >> 4; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_MO4000: + expo = sd->brightness >> 4; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_OV7660: + return; /*jfm??*/ + } + + k2 = sd->brightness >> 10; + reg_w(gspca_dev->dev, 0x96, &k2, 1); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 k2; + __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 }; + + if (sd->sensor == SENSOR_OV7660) + return; /*jfm??*/ + k2 = sd->contrast; + contrast[2] = k2; + contrast[0] = (k2 + 1) >> 1; + contrast[4] = (k2 + 1) / 5; + reg_w(gspca_dev->dev, 0x84, contrast, 6); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + int colour; + + colour = sd->colors - 128; + if (colour > 0) + data = (colour + 32) & 0x7f; /* blue */ + else + data = (-colour + 32) & 0x7f; /* red */ + reg_w(gspca_dev->dev, 0x05, &data, 1); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int i; + __u8 data; + __u8 reg1; + __u8 reg17; + __u8 *sn9c1xx; + int mode; + static __u8 DC29[] = { 0x6a, 0x50, 0x00, 0x00, 0x50, 0x3c }; + static __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; + static __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; + static __u8 CA_sn9c120[] = { 0x14, 0xec, 0x0a, 0xf6 }; /* SN9C120 */ + static __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static __u8 CE_sn9c325[] = + { 0x32, 0xdd, 0x32, 0xdd }; /* OV7648 - SN9C325 */ + + sn9c1xx = sn_tb[(int) sd->sensor]; + configure_gpio(gspca_dev, sn9c1xx); + +/*fixme:jfm this sequence should appear at end of sd_start */ +/* with + data = 0x44; + reg_w(dev, 0x01, &data, 1); */ + reg_w(dev, 0x15, &sn9c1xx[0x15], 1); + reg_w(dev, 0x16, &sn9c1xx[0x16], 1); + reg_w(dev, 0x12, &sn9c1xx[0x12], 1); + reg_w(dev, 0x13, &sn9c1xx[0x13], 1); + reg_w(dev, 0x18, &sn9c1xx[0x18], 1); + reg_w(dev, 0xd2, &DC29[0], 1); + reg_w(dev, 0xd3, &DC29[1], 1); + reg_w(dev, 0xc6, &DC29[2], 1); + reg_w(dev, 0xc7, &DC29[3], 1); + reg_w(dev, 0xc8, &DC29[4], 1); + reg_w(dev, 0xc9, &DC29[5], 1); +/*fixme:jfm end of ending sequence */ + reg_w(dev, 0x18, &sn9c1xx[0x18], 1); + if (sd->customid == SN9C325) + data = 0xae; + else + data = 0x60; + reg_w(dev, 0x17, &data, 1); + reg_w(dev, 0x05, &sn9c1xx[5], 1); + reg_w(dev, 0x07, &sn9c1xx[7], 1); + reg_w(dev, 0x06, &sn9c1xx[6], 1); + reg_w(dev, 0x14, &sn9c1xx[0x14], 1); + if (sd->customid == SN9C325) { + reg_w(dev, 0x20, regsn20_sn9c325, 0x11); + for (i = 0; i < 8; i++) + reg_w(dev, 0x84, reg84_sn9c325, 0x15); + data = 0x0a; + reg_w(dev, 0x9a, &data, 1); + data = 0x60; + reg_w(dev, 0x99, &data, 1); + } else { + reg_w(dev, 0x20, regsn20, 0x11); + for (i = 0; i < 8; i++) + reg_w(dev, 0x84, reg84, 0x15); + data = 0x08; + reg_w(dev, 0x9a, &data, 1); + data = 0x59; + reg_w(dev, 0x99, &data, 1); + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + reg1 = 0x02; + reg17 = 0x61; + switch (sd->sensor) { + case SENSOR_HV7131R: + hv7131R_InitSensor(gspca_dev); + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ + break; + case SENSOR_MI0360: + mi0360_InitSensor(gspca_dev); + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ + break; + case SENSOR_MO4000: + mo4000_InitSensor(gspca_dev); + if (mode) { +/* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ + reg1 = 0x06; /* clk 24Mz */ + } else { + reg17 = 0x22; /* 640 MCKSIZE */ + reg1 = 0x06; /* 640 clk 24Mz */ + } + break; + case SENSOR_OV7648: + reg17 = 0xa2; + reg1 = 0x44; + ov7648_InitSensor(gspca_dev); +/* if (mode) + ; * 320x2... + else + ; * 640x... */ + break; + default: +/* case SENSOR_OV7660: */ + ov7660_InitSensor(gspca_dev); + if (mode) { +/* reg17 = 0x21; * 320 */ +/* reg1 = 0x44; */ + reg1 = 0x46; + } else { + reg17 = 0xa2; /* 640 */ + reg1 = 0x40; + } + break; + } + reg_w(dev, 0xc0, C0, 6); + switch (sd->customid) { + case SN9C120: /*jfm ?? */ + reg_w(dev, 0xca, CA_sn9c120, 4); + break; + default: + reg_w(dev, 0xca, CA, 4); + break; + } + switch (sd->customid) { + case SN9C120: /*jfm ?? */ + case SN9C325: + reg_w(dev, 0xce, CE_sn9c325, 4); + break; + default: + reg_w(dev, 0xce, CE, 4); + /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ + break; + } + + /* here change size mode 0 -> VGA; 1 -> CIF */ + data = 0x40 | sn9c1xx[0x18] | (mode << 4); + reg_w(dev, 0x18, &data, 1); + + reg_w(dev, 0x100, qtable4, 0x40); + reg_w(dev, 0x140, qtable4 + 0x40, 0x40); + + data = sn9c1xx[0x18] | (mode << 4); + reg_w(dev, 0x18, &data, 1); + + reg_w(dev, 0x17, ®17, 1); + reg_w(dev, 0x01, ®1, 1); + setbrightness(gspca_dev); + setcontrast(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + static __u8 stophv7131[] = + { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; + static __u8 stopmi0360[] = + { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; + __u8 regF1; + __u8 data; + __u8 *sn9c1xx; + + data = 0x0b; + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_w8(dev, stophv7131); + data = 0x2b; + break; + case SENSOR_MI0360: + i2c_w8(dev, stopmi0360); + data = 0x29; + break; + case SENSOR_MO4000: + break; + case SENSOR_OV7648: + data = 0x29; + break; + default: +/* case SENSOR_OV7660: */ + break; + } + sn9c1xx = sn_tb[(int) sd->sensor]; + reg_w(dev, 0x01, &sn9c1xx[1], 1); + reg_w(dev, 0x17, &sn9c1xx[0x17], 1); + reg_w(dev, 0x01, &sn9c1xx[1], 1); + reg_w(dev, 0x01, &data, 1); + regF1 = 0x01; + reg_w(dev, 0xf1, ®F1, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + /* Thanks S., without your advice, autobright should not work :) */ + int delta; + int expotimes = 0; + __u8 luma_mean = 130; + __u8 luma_delta = 20; + + delta = sd->avg_lum; + if (delta < luma_mean - luma_delta || + delta > luma_mean + luma_delta) { + switch (sd->sensor) { + case SENSOR_HV7131R: + expotimes = sd->exposure >> 8; + expotimes += (luma_mean - delta) >> 4; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) (expotimes << 8)); + break; + case SENSOR_MO4000: + case SENSOR_MI0360: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + setcolors(gspca_dev); + break; + } + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int sof, avg_lum; + + sof = len - 64; + if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) { + + /* end of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, sof + 2); + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; +/* w1 w2 w3 */ +/* w4 w5 w6 */ +/* w7 w8 */ +/* w4 */ + avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6; +/* w6 */ + avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6; +/* w2 */ + avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6; +/* w8 */ + avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6; +/* w5 */ + avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4; + avg_lum >>= 4; + sd->avg_lum = avg_lum; + PDEBUG(D_PACK, "mean lum %d", avg_lum); + setautogain(gspca_dev); + return; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + + /* put the JPEG 422 header */ + jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21); + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static unsigned int getexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 hexpo, mexpo, lexpo; + __u8 expo[6]; + + switch (sd->sensor) { + case SENSOR_HV7131R: + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x25, expo); + return (expo[0] << 16) | (expo[1] << 8) | expo[2]; + case SENSOR_MI0360: + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x09, expo); + return (expo[0] << 8) | expo[1]; + case SENSOR_MO4000: + i2c_r5(gspca_dev, 0x0e, expo); + hexpo = 0; /* expo[1] & 0x07; */ + mexpo = 0x40; /* expo[2] &0xff; */ + lexpo = (expo[1] & 0x30) >> 4; + PDEBUG(D_CONF, "exposure %d", + (hexpo << 10) | (mexpo << 2) | lexpo); + return (hexpo << 10) | (mexpo << 2) | lexpo; + default: +/* case SENSOR_OV7660: */ + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x04, expo); + hexpo = expo[3] & 0x2f; + lexpo = expo[0] & 0x02; + i2c_r5(gspca_dev, 0x08, expo); + mexpo = expo[2]; + return (hexpo << 10) | (mexpo << 2) | lexpo; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* hardcoded registers seem not readable */ + switch (sd->sensor) { + case SENSOR_HV7131R: +/* sd->brightness = 0x7fff; */ + sd->brightness = getexposure(gspca_dev) >> 4; + break; + case SENSOR_MI0360: + sd->brightness = getexposure(gspca_dev) << 4; + break; + case SENSOR_MO4000: +/* sd->brightness = 0x1fff; */ + sd->brightness = getexposure(gspca_dev) << 4; + break; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0458, 0x7025), DVNM("Genius Eye 311Q")}, + {USB_DEVICE(0x045e, 0x00f5), DVNM("MicroSoft VX3000")}, + {USB_DEVICE(0x045e, 0x00f7), DVNM("MicroSoft VX1000")}, + {USB_DEVICE(0x0471, 0x0327), DVNM("Philips SPC 600 NC")}, + {USB_DEVICE(0x0471, 0x0328), DVNM("Philips SPC 700 NC")}, + {USB_DEVICE(0x0471, 0x0330), DVNM("Philips SPC 710NC")}, + {USB_DEVICE(0x0c45, 0x6040), DVNM("Speed NVC 350K")}, + {USB_DEVICE(0x0c45, 0x607c), DVNM("Sonix sn9c102p Hv7131R")}, + {USB_DEVICE(0x0c45, 0x60c0), DVNM("Sangha Sn535")}, + {USB_DEVICE(0x0c45, 0x60ec), DVNM("SN9C105+MO4000")}, + {USB_DEVICE(0x0c45, 0x60fb), DVNM("Surfer NoName")}, + {USB_DEVICE(0x0c45, 0x60fc), DVNM("LG-LIC300")}, + {USB_DEVICE(0x0c45, 0x612a), DVNM("Avant Camera")}, + {USB_DEVICE(0x0c45, 0x612c), DVNM("Typhoon Rasy Cam 1.3MPix")}, + {USB_DEVICE(0x0c45, 0x6130), DVNM("Sonix Pccam")}, + {USB_DEVICE(0x0c45, 0x6138), DVNM("Sn9c120 Mo4000")}, + {USB_DEVICE(0x0c45, 0x613b), DVNM("Surfer SN-206")}, + {USB_DEVICE(0x0c45, 0x613c), DVNM("Sonix Pccam168")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + info("v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c new file mode 100644 index 000000000000..c0dd969a3106 --- /dev/null +++ b/drivers/media/video/gspca/spca500.c @@ -0,0 +1,1195 @@ +/* + * SPCA500 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca500" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char packet[ISO_MAX_SIZE + 128]; + /* !! no more than 128 ff in an ISO packet */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + char qindex; + char subtype; +#define AgfaCl20 0 +#define AiptekPocketDV 1 +#define BenqDC1016 2 +#define CreativePCCam300 3 +#define DLinkDSC350 4 +#define Gsmartmini 5 +#define IntelPocketPCCamera 6 +#define KodakEZ200 7 +#define LogitechClickSmart310 8 +#define LogitechClickSmart510 9 +#define LogitechTraveler 10 +#define MustekGsmart300 11 +#define Optimedia 12 +#define PalmPixDC85 13 +#define ToptroIndus 14 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_JPEG, 176, 144, 1}, + {V4L2_PIX_FMT_JPEG, 352, 288, 0}, +}; + +/* Frame packet header offsets for the spca500 */ +#define SPCA500_OFFSET_PADDINGLB 2 +#define SPCA500_OFFSET_PADDINGHB 3 +#define SPCA500_OFFSET_MODE 4 +#define SPCA500_OFFSET_IMGWIDTH 5 +#define SPCA500_OFFSET_IMGHEIGHT 6 +#define SPCA500_OFFSET_IMGMODE 7 +#define SPCA500_OFFSET_QTBLINDEX 8 +#define SPCA500_OFFSET_FRAMSEQ 9 +#define SPCA500_OFFSET_CDSPINFO 10 +#define SPCA500_OFFSET_GPIO 11 +#define SPCA500_OFFSET_AUGPIO 12 +#define SPCA500_OFFSET_DATA 16 + + +static __u16 spca500_visual_defaults[][3] = { + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, + * saturation/hue enable, + * brightness/contrast enable. + */ + {0x00, 0x0000, 0x8167}, /* brightness = 0 */ + {0x00, 0x0020, 0x8168}, /* contrast = 0 */ + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, saturation/hue enable, + * brightness/contrast enable. + * was 0x0003, now 0x0000. + */ + {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ + {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ + {0x00, 0x0050, 0x8157}, /* edge gain high threshold */ + {0x00, 0x0030, 0x8158}, /* edge gain low threshold */ + {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ + {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ + {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ + {0x0c, 0x0004, 0x0000}, + /* set interface */ + + {0, 0, 0} +}; +static __u16 Clicksmart510_defaults[][3] = { + {0x00, 0x00, 0x8211}, + {0x00, 0x01, 0x82c0}, + {0x00, 0x10, 0x82cb}, + {0x00, 0x0f, 0x800d}, + {0x00, 0x82, 0x8225}, + {0x00, 0x21, 0x8228}, + {0x00, 0x00, 0x8203}, + {0x00, 0x00, 0x8204}, + {0x00, 0x08, 0x8205}, + {0x00, 0xf8, 0x8206}, + {0x00, 0x28, 0x8207}, + {0x00, 0xa0, 0x8208}, + {0x00, 0x08, 0x824a}, + {0x00, 0x08, 0x8214}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x00, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x04, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0xfc, 0x8100}, + {0x00, 0xfc, 0x8105}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8102}, + {0x00, 0x00, 0x8103}, + {0x00, 0x66, 0x8107}, + {0x00, 0x00, 0x816b}, + {0x00, 0x00, 0x8155}, + {0x00, 0x01, 0x8156}, + {0x00, 0x60, 0x8157}, + {0x00, 0x40, 0x8158}, + {0x00, 0x0a, 0x8159}, + {0x00, 0x06, 0x815a}, + {0x00, 0x00, 0x813f}, + {0x00, 0x00, 0x8200}, + {0x00, 0x19, 0x8201}, + {0x00, 0x00, 0x82c1}, + {0x00, 0xa0, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x00, 0x8117}, + {0x00, 0x00, 0x8118}, + {0x00, 0x65, 0x8119}, + {0x00, 0x00, 0x811a}, + {0x00, 0x00, 0x811b}, + {0x00, 0x55, 0x811c}, + {0x00, 0x65, 0x811d}, + {0x00, 0x55, 0x811e}, + {0x00, 0x16, 0x811f}, + {0x00, 0x19, 0x8120}, + {0x00, 0x80, 0x8103}, + {0x00, 0x83, 0x816b}, + {0x00, 0x25, 0x8168}, + {0x00, 0x01, 0x820f}, + {0x00, 0xff, 0x8115}, + {0x00, 0x48, 0x8116}, + {0x00, 0x50, 0x8151}, + {0x00, 0x40, 0x8152}, + {0x00, 0x78, 0x8153}, + {0x00, 0x40, 0x8154}, + {0x00, 0x00, 0x8167}, + {0x00, 0x20, 0x8168}, + {0x00, 0x00, 0x816a}, + {0x00, 0x03, 0x816b}, + {0x00, 0x20, 0x8169}, + {0x00, 0x60, 0x8157}, + {0x00, 0x00, 0x8190}, + {0x00, 0x00, 0x81a1}, + {0x00, 0x00, 0x81b2}, + {0x00, 0x27, 0x8191}, + {0x00, 0x27, 0x81a2}, + {0x00, 0x27, 0x81b3}, + {0x00, 0x4b, 0x8192}, + {0x00, 0x4b, 0x81a3}, + {0x00, 0x4b, 0x81b4}, + {0x00, 0x66, 0x8193}, + {0x00, 0x66, 0x81a4}, + {0x00, 0x66, 0x81b5}, + {0x00, 0x79, 0x8194}, + {0x00, 0x79, 0x81a5}, + {0x00, 0x79, 0x81b6}, + {0x00, 0x8a, 0x8195}, + {0x00, 0x8a, 0x81a6}, + {0x00, 0x8a, 0x81b7}, + {0x00, 0x9b, 0x8196}, + {0x00, 0x9b, 0x81a7}, + {0x00, 0x9b, 0x81b8}, + {0x00, 0xa6, 0x8197}, + {0x00, 0xa6, 0x81a8}, + {0x00, 0xa6, 0x81b9}, + {0x00, 0xb2, 0x8198}, + {0x00, 0xb2, 0x81a9}, + {0x00, 0xb2, 0x81ba}, + {0x00, 0xbe, 0x8199}, + {0x00, 0xbe, 0x81aa}, + {0x00, 0xbe, 0x81bb}, + {0x00, 0xc8, 0x819a}, + {0x00, 0xc8, 0x81ab}, + {0x00, 0xc8, 0x81bc}, + {0x00, 0xd2, 0x819b}, + {0x00, 0xd2, 0x81ac}, + {0x00, 0xd2, 0x81bd}, + {0x00, 0xdb, 0x819c}, + {0x00, 0xdb, 0x81ad}, + {0x00, 0xdb, 0x81be}, + {0x00, 0xe4, 0x819d}, + {0x00, 0xe4, 0x81ae}, + {0x00, 0xe4, 0x81bf}, + {0x00, 0xed, 0x819e}, + {0x00, 0xed, 0x81af}, + {0x00, 0xed, 0x81c0}, + {0x00, 0xf7, 0x819f}, + {0x00, 0xf7, 0x81b0}, + {0x00, 0xf7, 0x81c1}, + {0x00, 0xff, 0x81a0}, + {0x00, 0xff, 0x81b1}, + {0x00, 0xff, 0x81c2}, + {0x00, 0x03, 0x8156}, + {0x00, 0x00, 0x8211}, + {0x00, 0x20, 0x8168}, + {0x00, 0x01, 0x8202}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8111}, + {0x00, 0x00, 0x8112}, + {0x00, 0x00, 0x8113}, + {0x00, 0x00, 0x8114}, + {} +}; + +static unsigned char qtable_creative_pccam[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static unsigned char qtable_kodak_ez200[2][64] = { + { /* Q-table Y-components */ + 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, + 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, + 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, + 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, + 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, + 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, + 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, + 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, + { /* Q-table C-components */ + 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} +}; + +static unsigned char qtable_pocketdv[2][64] = { + { /* Q-table Y-components start registers 0x8800 */ + 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, + 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, + 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, + 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, + 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, + 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, + 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, + 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, + }, + { /* Q-table C-components start registers 0x8840 */ + 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, + 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} +}; + +static void spca5xxRegRead(struct usb_device *dev, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x, 0x%x", + index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + __u8 buf[2]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +/* + * Simple function to wait for a given 8-bit value to be returned from + * a reg_read call. + * Returns: negative is error or timeout, zero is success. + */ +static int reg_readwait(struct usb_device *dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret, cnt = 20; + + while (--cnt > 0) { + ret = reg_read(dev, reg, index, 1); + if (ret == value) + return 0; + msleep(50); + } + return -EIO; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, + unsigned int request, + unsigned int ybase, + unsigned int cbase, + unsigned char qtable[2][64]) +{ + struct usb_device *dev = gspca_dev->dev; + int i, err; + + /* loop over y components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, ybase + i, qtable[0][i]); + if (err < 0) + return err; + } + + /* loop over c components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, cbase + i, qtable[1][i]); + if (err < 0) + return err; + } + return 0; +} + +static void spca500_ping310(struct gspca_dev *gspca_dev) +{ + __u8 Data[2]; + + spca5xxRegRead(gspca_dev->dev, 0x0d04, Data, 2); + PDEBUG(D_PACK, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x", + Data[0], Data[1]); +} + +static void spca500_clksmart310_init(struct gspca_dev *gspca_dev) +{ + __u8 Data[2]; + + spca5xxRegRead(gspca_dev->dev, 0x0d05, Data, 2); + PDEBUG(D_PACK, "ClickSmart310 init 0x0d05 0x%02x 0x%02x", Data[0], + Data[1]); + reg_write(gspca_dev->dev, 0x00, 0x8167, 0x5a); + spca500_ping310(gspca_dev); + + reg_write(gspca_dev->dev, 0x00, 0x8168, 0x22); + reg_write(gspca_dev->dev, 0x00, 0x816a, 0xc0); + reg_write(gspca_dev->dev, 0x00, 0x816b, 0x0b); + reg_write(gspca_dev->dev, 0x00, 0x8169, 0x25); + reg_write(gspca_dev->dev, 0x00, 0x8157, 0x5b); + reg_write(gspca_dev->dev, 0x00, 0x8158, 0x5b); + reg_write(gspca_dev->dev, 0x00, 0x813f, 0x03); + reg_write(gspca_dev->dev, 0x00, 0x8151, 0x4a); + reg_write(gspca_dev->dev, 0x00, 0x8153, 0x78); + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x04); + /* 00 for adjust shutter */ + reg_write(gspca_dev->dev, 0x00, 0x0d02, 0x01); + reg_write(gspca_dev->dev, 0x00, 0x8169, 0x25); + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x02); +} + +static void spca500_setmode(struct gspca_dev *gspca_dev, + __u8 xmult, __u8 ymult) +{ + int mode; + + /* set x multiplier */ + reg_write(gspca_dev->dev, 0, 0x8001, xmult); + + /* set y multiplier */ + reg_write(gspca_dev->dev, 0, 0x8002, ymult); + + /* use compressed mode, VGA, with mode specific subsample */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + reg_write(gspca_dev->dev, 0, 0x8003, mode << 4); +} + +static int spca500_full_reset(struct gspca_dev *gspca_dev) +{ + int err; + + /* send the reset command */ + err = reg_write(gspca_dev->dev, 0xe0, 0x0001, 0x0000); + if (err < 0) + return err; + + /* wait for the reset to complete */ + err = reg_readwait(gspca_dev->dev, 0x06, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_write(gspca_dev->dev, 0xe0, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_readwait(gspca_dev->dev, 0x06, 0, 0); + if (err < 0) { + PDEBUG(D_ERR, "reg_readwait() failed"); + return err; + } + /* all ok */ + return 0; +} + +/* Synchro the Bridge with sensor */ +/* Maybe that will work on all spca500 chip */ +/* because i only own a clicksmart310 try for that chip */ +/* using spca50x_set_packet_size() cause an Ooops here */ +/* usb_set_interface from kernel 2.6.x clear all the urb stuff */ +/* up-port the same feature as in 2.4.x kernel */ +static int spca500_synch310(struct gspca_dev *gspca_dev) +{ + __u8 Data; + + if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + spca500_ping310(gspca_dev); + + spca5xxRegRead(gspca_dev->dev, 0x0d00, &Data, 1); + + /* need alt setting here */ + PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt); + + /* Windoze use pipe with altsetting 6 why 7 here */ + if (usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + gspca_dev->alt) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + return 0; +error: + return -EBUSY; +} + +static void spca500_reinit(struct gspca_dev *gspca_dev) +{ + int err; + __u8 Data; + + /* some unknow command from Aiptek pocket dv and family300 */ + + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x01); + reg_write(gspca_dev->dev, 0x00, 0x0d03, 0x00); + reg_write(gspca_dev->dev, 0x00, 0x0d02, 0x01); + + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, + qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init"); + + /* set qtable index */ + reg_write(gspca_dev->dev, 0x00, 0x8880, 2); + /* family cam Quicksmart stuff */ + reg_write(gspca_dev->dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced inbetween frames */ + reg_write(gspca_dev->dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + /*Start init sequence or stream */ + + reg_write(gspca_dev->dev, 0, 0x8003, 0x00); + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + msleep(2000); + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x040a: /* Kodak cameras */ +/* switch (product) { */ +/* case 0x0300: */ + sd->subtype = KodakEZ200; +/* break; */ +/* } */ + break; + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x400a: */ + sd->subtype = CreativePCCam300; +/* break; */ +/* } */ + break; + case 0x046d: /* Logitech Labtec */ + switch (product) { + case 0x0890: + sd->subtype = LogitechTraveler; + break; + case 0x0900: + sd->subtype = LogitechClickSmart310; + break; + case 0x0901: + sd->subtype = LogitechClickSmart510; + break; + } + break; + case 0x04a5: /* Benq */ +/* switch (product) { */ +/* case 0x300c: */ + sd->subtype = BenqDC1016; +/* break; */ +/* } */ + break; + case 0x04fc: /* SunPlus */ +/* switch (product) { */ +/* case 0x7333: */ + sd->subtype = PalmPixDC85; +/* break; */ +/* } */ + break; + case 0x055f: /* Mustek cameras */ + switch (product) { + case 0xc200: + sd->subtype = MustekGsmart300; + break; + case 0xc220: + sd->subtype = Gsmartmini; + break; + } + break; + case 0x06bd: /* Agfa Cl20 */ +/* switch (product) { */ +/* case 0x0404: */ + sd->subtype = AgfaCl20; +/* break; */ +/* } */ + break; + case 0x06be: /* Optimedia */ +/* switch (product) { */ +/* case 0x0800: */ + sd->subtype = Optimedia; +/* break; */ +/* } */ + break; + case 0x084d: /* D-Link / Minton */ +/* switch (product) { */ +/* case 0x0003: * DSC-350 / S-Cam F5 */ + sd->subtype = DLinkDSC350; +/* break; */ +/* } */ + break; + case 0x08ca: /* Aiptek */ +/* switch (product) { */ +/* case 0x0103: */ + sd->subtype = AiptekPocketDV; +/* break; */ +/* } */ + break; + case 0x2899: /* ToptroIndustrial */ +/* switch (product) { */ +/* case 0x012c: */ + sd->subtype = ToptroIndus; +/* break; */ +/* } */ + break; + case 0x8086: /* Intel */ +/* switch (product) { */ +/* case 0x0630: * Pocket PC Camera */ + sd->subtype = IntelPocketPCCamera; +/* break; */ +/* } */ + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + if (sd->subtype != LogitechClickSmart310) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + sd->qindex = 5; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialisation of spca500 based cameras is deferred */ + PDEBUG(D_STREAM, "SPCA500 init"); + if (sd->subtype == LogitechClickSmart310) + spca500_clksmart310_init(gspca_dev); +/* else + spca500_initialise(gspca_dev); */ + PDEBUG(D_STREAM, "SPCA500 init done"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + __u8 Data; + __u8 xmult, ymult; + + if (sd->subtype == LogitechClickSmart310) { + xmult = 0x16; + ymult = 0x12; + } else { + xmult = 0x28; + ymult = 0x1e; + } + + /* is there a sensor here ? */ + spca5xxRegRead(gspca_dev->dev, 0x8a04, &Data, 1); + PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02X", Data); + PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02X, Ymult: 0x%02X", + gspca_dev->curr_mode, xmult, ymult); + + /* setup qtable */ + switch (sd->subtype) { + case LogitechClickSmart310: + spca500_setmode(gspca_dev, xmult, ymult); + + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + msleep(500); + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + + spca500_synch310(gspca_dev); + + write_vector(gspca_dev, spca500_visual_defaults); + spca500_setmode(gspca_dev, xmult, ymult); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + break; + case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */ + case IntelPocketPCCamera: /* FIXME: Temporary fix for + * Intel Pocket PC Camera + * - NWG (Sat 29th March 2003) */ + + /* do a full reset */ + if ((err = spca500_full_reset(gspca_dev)) < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + + /* enable drop packet */ + err = reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + if (err < 0) + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + spca500_setmode(gspca_dev, xmult, ymult); + reg_write(gspca_dev->dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + + /* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + case KodakEZ200: /* Kodak EZ200 */ + + /* do a full reset */ + err = spca500_full_reset(gspca_dev); + if (err < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + reg_write(gspca_dev->dev, 0x00, 0x8880, 0); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_kodak_ez200); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + spca500_setmode(gspca_dev, xmult, ymult); + + reg_write(gspca_dev->dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + + /* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + + case BenqDC1016: + case DLinkDSC350: /* FamilyCam 300 */ + case AiptekPocketDV: /* Aiptek PocketDV */ + case Gsmartmini: /*Mustek Gsmart Mini */ + case MustekGsmart300: /* Mustek Gsmart 300 */ + case PalmPixDC85: + case Optimedia: + case ToptroIndus: + case AgfaCl20: + spca500_reinit(gspca_dev); + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x01); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 2); + + /* familycam Quicksmart pocketDV stuff */ + reg_write(gspca_dev->dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced inbetween frames */ + reg_write(gspca_dev->dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev,xmult,ymult); + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + break; + case LogitechTraveler: + case LogitechClickSmart510: + reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, + 0x8840, qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + reg_write(gspca_dev->dev, 0x00, 0x800a, 0x00); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev, xmult, ymult); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + write_vector(gspca_dev, Clicksmart510_defaults); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + __u8 data = 0; + + reg_write(gspca_dev->dev, 0, 0x8003, 0x00); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + spca5xxRegRead(gspca_dev->dev, 0x8000, &data, 1); + PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x", data); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + unsigned char *s, *d; + static unsigned char ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + + data += SPCA500_OFFSET_DATA; + len -= SPCA500_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + + /* add 0x00 after 0xff */ + for (i = len; --i >= 0; ) + if (data[i] == 0xff) + break; + if (i < 0) { /* no 0xff */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + return; + } + s = data; + d = sd->packet; + for (i = 0; i < len; i++) { + *d++ = *s++; + if (s[-1] == 0xff) + *d++ = 0x00; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + sd->packet, d - sd->packet); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x8167, + (__u8) (sd->brightness - 128)); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = reg_read(gspca_dev->dev, 0x00, 0x8167, 1) + 128; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x8168, sd->contrast >> 2); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = reg_read(gspca_dev->dev, 0x0, 0x8168, 1) << 2; +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x8169, sd->colors >> 2); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = reg_read(gspca_dev->dev, 0x0, 0x8169, 1) << 2; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0300), DVNM("Kodak EZ200")}, + {USB_DEVICE(0x041e, 0x400a), DVNM("Creative PC-CAM 300")}, + {USB_DEVICE(0x046d, 0x0890), DVNM("Logitech QuickCam traveler")}, + {USB_DEVICE(0x046d, 0x0900), DVNM("Logitech Inc. ClickSmart 310")}, + {USB_DEVICE(0x046d, 0x0901), DVNM("Logitech Inc. ClickSmart 510")}, + {USB_DEVICE(0x04a5, 0x300c), DVNM("Benq DC1016")}, + {USB_DEVICE(0x04fc, 0x7333), DVNM("PalmPixDC85")}, + {USB_DEVICE(0x055f, 0xc200), DVNM("Mustek Gsmart 300")}, + {USB_DEVICE(0x055f, 0xc220), DVNM("Gsmart Mini")}, + {USB_DEVICE(0x06bd, 0x0404), DVNM("Agfa CL20")}, + {USB_DEVICE(0x06be, 0x0800), DVNM("Optimedia")}, + {USB_DEVICE(0x084d, 0x0003), DVNM("D-Link DSC-350")}, + {USB_DEVICE(0x08ca, 0x0103), DVNM("Aiptek PocketDV")}, + {USB_DEVICE(0x2899, 0x012c), DVNM("Toptro Industrial")}, + {USB_DEVICE(0x8086, 0x0630), DVNM("Intel Pocket PC Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c new file mode 100644 index 000000000000..c6468cf3506a --- /dev/null +++ b/drivers/media/video/gspca/spca501.c @@ -0,0 +1,2219 @@ +/* + * SPCA501 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca501" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 colors; + + char subtype; +#define Arowana300KCMOSCamera 0 +#define IntelCreateAndShare 1 +#define KodakDVC325 2 +#define MystFromOriUnknownCamera 3 +#define SmileIntlCamera 4 +#define ThreeComHomeConnectLite 5 +#define ViewQuestM318B 6 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define MY_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define MY_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0xaa00, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define MY_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 31, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_SPCA501, 160, 120, 2}, + {V4L2_PIX_FMT_SPCA501, 320, 240, 1}, + {V4L2_PIX_FMT_SPCA501, 640, 480, 0}, +}; + +#define SPCA50X_REG_USB 0x2 /* spca505 501 */ +/* + * Data to initialize a SPCA501. From a capture file provided by Bill Roehl + * With SPCA501 chip description + */ +#define CCDSP_SET /* set CCDSP parameters */ +#define TG_SET /* set time generator set */ +#undef DSPWIN_SET /* set DSP windows parameters */ +#undef ALTER_GAMA /* Set alternate set to YUV transform coeffs. */ +#define SPCA501_SNAPBIT 0x80 +#define SPCA501_SNAPCTRL 0x10 +/* Frame packet header offsets for the spca501 */ +#define SPCA501_OFFSET_GPIO 1 +#define SPCA501_OFFSET_TYPE 2 +#define SPCA501_OFFSET_TURN3A 3 +#define SPCA501_OFFSET_FRAMSEQ 4 +#define SPCA501_OFFSET_COMPRESS 5 +#define SPCA501_OFFSET_QUANT 6 +#define SPCA501_OFFSET_QUANT2 7 +#define SPCA501_OFFSET_DATA 8 + +#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1) +#define SPCA501_PROP_SNAP(d) ((d) & 0x40) +#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10) +#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1) +#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4) + +/* SPCA501 CCDSP control */ +#define SPCA501_REG_CCDSP 0x01 +/* SPCA501 control/status registers */ +#define SPCA501_REG_CTLRL 0x02 + +/* registers for color correction and YUV transformation */ +#define SPCA501_A11 0x08 +#define SPCA501_A12 0x09 +#define SPCA501_A13 0x0A +#define SPCA501_A21 0x0B +#define SPCA501_A22 0x0C +#define SPCA501_A23 0x0D +#define SPCA501_A31 0x0E +#define SPCA501_A32 0x0F +#define SPCA501_A33 0x10 + +/* Data for video camera initialization before capturing */ +static __u16 spca501_open_data[][3] = { + /* bmRequest,value,index */ + + {0x2, 0x50, 0x00}, /* C/S enable soft reset */ + {0x2, 0x40, 0x00}, /* C/S disable soft reset */ + {0x2, 0x02, 0x05}, /* C/S general purpose I/O data */ + {0x2, 0x03, 0x05}, /* C/S general purpose I/O data */ + +#ifdef CCDSP_SET + {0x1, 0x38, 0x01}, /* CCDSP options */ + {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */ + {0x1, 0xC0, 0x03}, /* CCDSP Optical black settings */ + + {0x1, 0x67, 0x07}, + {0x1, 0x63, 0x3f}, /* CCDSP CCD gamma enable */ + {0x1, 0x03, 0x56}, /* Add gamma correction */ + + {0x1, 0xFF, 0x15}, /* CCDSP High luminance for white balance */ + {0x1, 0x01, 0x16}, /* CCDSP Low luminance for white balance */ + +/* Color correction and RGB-to-YUV transformation coefficients changing */ +#ifdef ALTER_GAMA + {0x0, 0x00, 0x08}, /* A11 */ + {0x0, 0x00, 0x09}, /* A12 */ + {0x0, 0x90, 0x0A}, /* A13 */ + {0x0, 0x12, 0x0B}, /* A21 */ + {0x0, 0x00, 0x0C}, /* A22 */ + {0x0, 0x00, 0x0D}, /* A23 */ + {0x0, 0x00, 0x0E}, /* A31 */ + {0x0, 0x02, 0x0F}, /* A32 */ + {0x0, 0x00, 0x10}, /* A33 */ +#else + {0x1, 0x2a, 0x08}, /* A11 0x31 */ + {0x1, 0xf8, 0x09}, /* A12 f8 */ + {0x1, 0xf8, 0x0A}, /* A13 f8 */ + {0x1, 0xf8, 0x0B}, /* A21 f8 */ + {0x1, 0x14, 0x0C}, /* A22 0x14 */ + {0x1, 0xf8, 0x0D}, /* A23 f8 */ + {0x1, 0xf8, 0x0E}, /* A31 f8 */ + {0x1, 0xf8, 0x0F}, /* A32 f8 */ + {0x1, 0x20, 0x10}, /* A33 0x20 */ +#endif + {0x1, 0x00, 0x11}, /* R offset */ + {0x1, 0x00, 0x12}, /* G offset */ + {0x1, 0x00, 0x13}, /* B offset */ + {0x1, 0x00, 0x14}, /* GB offset */ + +#endif + +#ifdef TG_SET + /* Time generator manipulations */ + {0x0, 0xfc, 0x0}, /* Set up high bits of shutter speed */ + {0x0, 0x01, 0x1}, /* Set up low bits of shutter speed */ + + {0x0, 0xe4, 0x04}, /* DCLK*2 clock phase adjustment */ + {0x0, 0x08, 0x05}, /* ADCK phase adjustment, inv. ext. VB */ + {0x0, 0x03, 0x06}, /* FR phase adjustment */ + {0x0, 0x01, 0x07}, /* FCDS phase adjustment */ + {0x0, 0x39, 0x08}, /* FS phase adjustment */ + {0x0, 0x88, 0x0a}, /* FH1 phase and delay adjustment */ + {0x0, 0x03, 0x0f}, /* pixel identification */ + {0x0, 0x00, 0x11}, /* clock source selection (default) */ + + /*VERY strange manipulations with + * select DMCLP or OBPX to be ADCLP output (0x0C) + * OPB always toggle or not (0x0D) but they allow + * us to set up brightness + */ + {0x0, 0x01, 0x0c}, + {0x0, 0xe0, 0x0d}, + /* Done */ +#endif + +#ifdef DSPWIN_SET + {0x1, 0xa0, 0x01}, /* Setting image processing parameters */ + {0x1, 0x1c, 0x17}, /* Changing Windows positions X1 */ + {0x1, 0xe2, 0x19}, /* X2 */ + {0x1, 0x1c, 0x1b}, /* X3 */ + {0x1, 0xe2, 0x1d}, /* X4 */ + {0x1, 0x5f, 0x1f}, /* X5 */ + {0x1, 0x32, 0x20}, /* Y5 */ + {0x1, 0x01, 0x10}, /* Changing A33 */ +#endif + + {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */ + {0x2, 0x94, 0x06}, /* Setting video no compression */ + {} +}; + +/* + The SPCAxxx docs from Sunplus document these values + in tables, one table per register number. In the data + below, dmRequest is the register number, index is the Addr, + and value is a combination of Bit values. + Bit Value (hex) + 0 01 + 1 02 + 2 04 + 3 08 + 4 10 + 5 20 + 6 40 + 7 80 + */ + +/* Data for chip initialization (set default values) */ +static __u16 spca501_init_data[][3] = { + /* Set all the values to powerup defaults */ + /* bmRequest,value,index */ + {0x0, 0xAA, 0x00}, + {0x0, 0x02, 0x01}, + {0x0, 0x01, 0x02}, + {0x0, 0x02, 0x03}, + {0x0, 0xCE, 0x04}, + {0x0, 0x00, 0x05}, + {0x0, 0x00, 0x06}, + {0x0, 0x00, 0x07}, + {0x0, 0x00, 0x08}, + {0x0, 0x00, 0x09}, + {0x0, 0x90, 0x0A}, + {0x0, 0x12, 0x0B}, + {0x0, 0x00, 0x0C}, + {0x0, 0x00, 0x0D}, + {0x0, 0x00, 0x0E}, + {0x0, 0x02, 0x0F}, + {0x0, 0x00, 0x10}, + {0x0, 0x00, 0x11}, + {0x0, 0x00, 0x12}, + {0x0, 0x00, 0x13}, + {0x0, 0x00, 0x14}, + {0x0, 0x00, 0x15}, + {0x0, 0x00, 0x16}, + {0x0, 0x00, 0x17}, + {0x0, 0x00, 0x18}, + {0x0, 0x00, 0x19}, + {0x0, 0x00, 0x1A}, + {0x0, 0x00, 0x1B}, + {0x0, 0x00, 0x1C}, + {0x0, 0x00, 0x1D}, + {0x0, 0x00, 0x1E}, + {0x0, 0x00, 0x1F}, + {0x0, 0x00, 0x20}, + {0x0, 0x00, 0x21}, + {0x0, 0x00, 0x22}, + {0x0, 0x00, 0x23}, + {0x0, 0x00, 0x24}, + {0x0, 0x00, 0x25}, + {0x0, 0x00, 0x26}, + {0x0, 0x00, 0x27}, + {0x0, 0x00, 0x28}, + {0x0, 0x00, 0x29}, + {0x0, 0x00, 0x2A}, + {0x0, 0x00, 0x2B}, + {0x0, 0x00, 0x2C}, + {0x0, 0x00, 0x2D}, + {0x0, 0x00, 0x2E}, + {0x0, 0x00, 0x2F}, + {0x0, 0x00, 0x30}, + {0x0, 0x00, 0x31}, + {0x0, 0x00, 0x32}, + {0x0, 0x00, 0x33}, + {0x0, 0x00, 0x34}, + {0x0, 0x00, 0x35}, + {0x0, 0x00, 0x36}, + {0x0, 0x00, 0x37}, + {0x0, 0x00, 0x38}, + {0x0, 0x00, 0x39}, + {0x0, 0x00, 0x3A}, + {0x0, 0x00, 0x3B}, + {0x0, 0x00, 0x3C}, + {0x0, 0x00, 0x3D}, + {0x0, 0x00, 0x3E}, + {0x0, 0x00, 0x3F}, + {0x0, 0x00, 0x40}, + {0x0, 0x00, 0x41}, + {0x0, 0x00, 0x42}, + {0x0, 0x00, 0x43}, + {0x0, 0x00, 0x44}, + {0x0, 0x00, 0x45}, + {0x0, 0x00, 0x46}, + {0x0, 0x00, 0x47}, + {0x0, 0x00, 0x48}, + {0x0, 0x00, 0x49}, + {0x0, 0x00, 0x4A}, + {0x0, 0x00, 0x4B}, + {0x0, 0x00, 0x4C}, + {0x0, 0x00, 0x4D}, + {0x0, 0x00, 0x4E}, + {0x0, 0x00, 0x4F}, + {0x0, 0x00, 0x50}, + {0x0, 0x00, 0x51}, + {0x0, 0x00, 0x52}, + {0x0, 0x00, 0x53}, + {0x0, 0x00, 0x54}, + {0x0, 0x00, 0x55}, + {0x0, 0x00, 0x56}, + {0x0, 0x00, 0x57}, + {0x0, 0x00, 0x58}, + {0x0, 0x00, 0x59}, + {0x0, 0x00, 0x5A}, + {0x0, 0x00, 0x5B}, + {0x0, 0x00, 0x5C}, + {0x0, 0x00, 0x5D}, + {0x0, 0x00, 0x5E}, + {0x0, 0x00, 0x5F}, + {0x0, 0x00, 0x60}, + {0x0, 0x00, 0x61}, + {0x0, 0x00, 0x62}, + {0x0, 0x00, 0x63}, + {0x0, 0x00, 0x64}, + {0x0, 0x00, 0x65}, + {0x0, 0x00, 0x66}, + {0x0, 0x00, 0x67}, + {0x0, 0x00, 0x68}, + {0x0, 0x00, 0x69}, + {0x0, 0x00, 0x6A}, + {0x0, 0x00, 0x6B}, + {0x0, 0x00, 0x6C}, + {0x0, 0x00, 0x6D}, + {0x0, 0x00, 0x6E}, + {0x0, 0x00, 0x6F}, + {0x0, 0x00, 0x70}, + {0x0, 0x00, 0x71}, + {0x0, 0x00, 0x72}, + {0x0, 0x00, 0x73}, + {0x0, 0x00, 0x74}, + {0x0, 0x00, 0x75}, + {0x0, 0x00, 0x76}, + {0x0, 0x00, 0x77}, + {0x0, 0x00, 0x78}, + {0x0, 0x00, 0x79}, + {0x0, 0x00, 0x7A}, + {0x0, 0x00, 0x7B}, + {0x0, 0x00, 0x7C}, + {0x0, 0x00, 0x7D}, + {0x0, 0x00, 0x7E}, + {0x0, 0x00, 0x7F}, + {0x0, 0x00, 0x80}, + {0x0, 0x00, 0x81}, + {0x0, 0x00, 0x82}, + {0x0, 0x00, 0x83}, + {0x0, 0x00, 0x84}, + {0x0, 0x00, 0x85}, + {0x0, 0x00, 0x86}, + {0x0, 0x00, 0x87}, + {0x0, 0x00, 0x88}, + {0x0, 0x00, 0x89}, + {0x0, 0x00, 0x8A}, + {0x0, 0x00, 0x8B}, + {0x0, 0x00, 0x8C}, + {0x0, 0x00, 0x8D}, + {0x0, 0x00, 0x8E}, + {0x0, 0x00, 0x8F}, + {0x0, 0x00, 0x90}, + {0x0, 0x00, 0x91}, + {0x0, 0x00, 0x92}, + {0x0, 0x00, 0x93}, + {0x0, 0x00, 0x94}, + {0x0, 0x00, 0x95}, + {0x0, 0x00, 0x96}, + {0x0, 0x00, 0x97}, + {0x0, 0x00, 0x98}, + {0x0, 0x00, 0x99}, + {0x0, 0x00, 0x9A}, + {0x0, 0x00, 0x9B}, + {0x0, 0x00, 0x9C}, + {0x0, 0x00, 0x9D}, + {0x0, 0x00, 0x9E}, + {0x0, 0x00, 0x9F}, + {0x0, 0x00, 0xA0}, + {0x0, 0x00, 0xA1}, + {0x0, 0x00, 0xA2}, + {0x0, 0x00, 0xA3}, + {0x0, 0x00, 0xA4}, + {0x0, 0x00, 0xA5}, + {0x0, 0x00, 0xA6}, + {0x0, 0x00, 0xA7}, + {0x0, 0x00, 0xA8}, + {0x0, 0x00, 0xA9}, + {0x0, 0x00, 0xAA}, + {0x0, 0x00, 0xAB}, + {0x0, 0x00, 0xAC}, + {0x0, 0x00, 0xAD}, + {0x0, 0x00, 0xAE}, + {0x0, 0x00, 0xAF}, + {0x0, 0x00, 0xB0}, + {0x0, 0x00, 0xB1}, + {0x0, 0x00, 0xB2}, + {0x0, 0x00, 0xB3}, + {0x0, 0x00, 0xB4}, + {0x0, 0x00, 0xB5}, + {0x0, 0x00, 0xB6}, + {0x0, 0x00, 0xB7}, + {0x0, 0x00, 0xB8}, + {0x0, 0x00, 0xB9}, + {0x0, 0x00, 0xBA}, + {0x0, 0x00, 0xBB}, + {0x0, 0x00, 0xBC}, + {0x0, 0x00, 0xBD}, + {0x0, 0x00, 0xBE}, + {0x0, 0x00, 0xBF}, + {0x0, 0x00, 0xC0}, + {0x0, 0x00, 0xC1}, + {0x0, 0x00, 0xC2}, + {0x0, 0x00, 0xC3}, + {0x0, 0x00, 0xC4}, + {0x0, 0x00, 0xC5}, + {0x0, 0x00, 0xC6}, + {0x0, 0x00, 0xC7}, + {0x0, 0x00, 0xC8}, + {0x0, 0x00, 0xC9}, + {0x0, 0x00, 0xCA}, + {0x0, 0x00, 0xCB}, + {0x0, 0x00, 0xCC}, + {0x1, 0xF4, 0x00}, + {0x1, 0x38, 0x01}, + {0x1, 0x40, 0x02}, + {0x1, 0x0A, 0x03}, + {0x1, 0x40, 0x04}, + {0x1, 0x40, 0x05}, + {0x1, 0x40, 0x06}, + {0x1, 0x67, 0x07}, + {0x1, 0x31, 0x08}, + {0x1, 0x00, 0x09}, + {0x1, 0x00, 0x0A}, + {0x1, 0x00, 0x0B}, + {0x1, 0x14, 0x0C}, + {0x1, 0x00, 0x0D}, + {0x1, 0x00, 0x0E}, + {0x1, 0x00, 0x0F}, + {0x1, 0x1E, 0x10}, + {0x1, 0x00, 0x11}, + {0x1, 0x00, 0x12}, + {0x1, 0x00, 0x13}, + {0x1, 0x00, 0x14}, + {0x1, 0xFF, 0x15}, + {0x1, 0x01, 0x16}, + {0x1, 0x32, 0x17}, + {0x1, 0x23, 0x18}, + {0x1, 0xCE, 0x19}, + {0x1, 0x23, 0x1A}, + {0x1, 0x32, 0x1B}, + {0x1, 0x8D, 0x1C}, + {0x1, 0xCE, 0x1D}, + {0x1, 0x8D, 0x1E}, + {0x1, 0x00, 0x1F}, + {0x1, 0x00, 0x20}, + {0x1, 0xFF, 0x3E}, + {0x1, 0x02, 0x3F}, + {0x1, 0x00, 0x40}, + {0x1, 0x00, 0x41}, + {0x1, 0x00, 0x42}, + {0x1, 0x00, 0x43}, + {0x1, 0x00, 0x44}, + {0x1, 0x00, 0x45}, + {0x1, 0x00, 0x46}, + {0x1, 0x00, 0x47}, + {0x1, 0x00, 0x48}, + {0x1, 0x00, 0x49}, + {0x1, 0x00, 0x4A}, + {0x1, 0x00, 0x4B}, + {0x1, 0x00, 0x4C}, + {0x1, 0x00, 0x4D}, + {0x1, 0x00, 0x4E}, + {0x1, 0x00, 0x4F}, + {0x1, 0x00, 0x50}, + {0x1, 0x00, 0x51}, + {0x1, 0x00, 0x52}, + {0x1, 0x00, 0x53}, + {0x1, 0x00, 0x54}, + {0x1, 0x00, 0x55}, + {0x1, 0x00, 0x56}, + {0x1, 0x00, 0x57}, + {0x1, 0x00, 0x58}, + {0x1, 0x00, 0x59}, + {0x1, 0x00, 0x5A}, + {0x2, 0x03, 0x00}, + {0x2, 0x00, 0x01}, + {0x2, 0x00, 0x05}, + {0x2, 0x00, 0x06}, + {0x2, 0x00, 0x07}, + {0x2, 0x00, 0x10}, + {0x2, 0x00, 0x11}, + /* Strange - looks like the 501 driver doesn't do anything + * at insert time except read the EEPROM + */ + {} +}; + +/* Data for video camera init before capture. + * Capture and decoding by Colin Peart. + * This is is for the 3com HomeConnect Lite which is spca501a based. + */ +static __u16 spca501_3com_open_data[][3] = { + /* bmRequest,value,index */ + {0x2, 0x0050, 0x0000}, /* C/S Enable TG soft reset, timing mode=010 */ + {0x2, 0x0043, 0x0000}, /* C/S Disable TG soft reset, timing mode=010 */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + +#ifdef CCDSP_SET + {0x1, 0x0020, 0x0001}, /* CCDSP Options */ + + {0x1, 0x0020, 0x0002}, /* CCDSP Black Level */ + {0x1, 0x006e, 0x0007}, /* CCDSP Gamma options */ + {0x1, 0x0090, 0x0015}, /* CCDSP Luminance Low */ + {0x1, 0x00ff, 0x0016}, /* CCDSP Luminance High */ + {0x1, 0x0003, 0x003F}, /* CCDSP Gamma correction toggle */ + +#ifdef ALTER_GAMMA + {0x1, 0x0010, 0x0008}, /* CCDSP YUV A11 */ + {0x1, 0x0000, 0x0009}, /* CCDSP YUV A12 */ + {0x1, 0x0000, 0x000a}, /* CCDSP YUV A13 */ + {0x1, 0x0000, 0x000b}, /* CCDSP YUV A21 */ + {0x1, 0x0010, 0x000c}, /* CCDSP YUV A22 */ + {0x1, 0x0000, 0x000d}, /* CCDSP YUV A23 */ + {0x1, 0x0000, 0x000e}, /* CCDSP YUV A31 */ + {0x1, 0x0000, 0x000f}, /* CCDSP YUV A32 */ + {0x1, 0x0010, 0x0010}, /* CCDSP YUV A33 */ + {0x1, 0x0000, 0x0011}, /* CCDSP R Offset */ + {0x1, 0x0000, 0x0012}, /* CCDSP G Offset */ + {0x1, 0x0001, 0x0013}, /* CCDSP B Offset */ + {0x1, 0x0001, 0x0014}, /* CCDSP BG Offset */ + {0x1, 0x003f, 0x00C1}, /* CCDSP Gamma Correction Enable */ +#endif +#endif + +#ifdef TG_SET + {0x0, 0x00fc, 0x0000}, /* TG Shutter Speed High Bits */ + {0x0, 0x0000, 0x0001}, /* TG Shutter Speed Low Bits */ + {0x0, 0x00e4, 0x0004}, /* TG DCLK*2 Adjust */ + {0x0, 0x0008, 0x0005}, /* TG ADCK Adjust */ + {0x0, 0x0003, 0x0006}, /* TG FR Phase Adjust */ + {0x0, 0x0001, 0x0007}, /* TG FCDS Phase Adjust */ + {0x0, 0x0039, 0x0008}, /* TG FS Phase Adjust */ + {0x0, 0x0088, 0x000a}, /* TG MH1 */ + {0x0, 0x0003, 0x000f}, /* TG Pixel ID */ + + /* Like below, unexplained toglleing */ + {0x0, 0x0080, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0000, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0040, 0x000c}, + {0x0, 0x0017, 0x000d}, + {0x0, 0x00c0, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0006, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0002, 0x0003}, +#endif + +#ifdef DSPWIN_SET + {0x1, 0x001c, 0x0017}, /* CCDSP W1 Start X */ + {0x1, 0x00e2, 0x0019}, /* CCDSP W2 Start X */ + {0x1, 0x001c, 0x001b}, /* CCDSP W3 Start X */ + {0x1, 0x00e2, 0x001d}, /* CCDSP W4 Start X */ + {0x1, 0x00aa, 0x001f}, /* CCDSP W5 Start X */ + {0x1, 0x0070, 0x0020}, /* CCDSP W5 Start Y */ +#endif + {0x0, 0x0001, 0x0010}, /* TG Start Clock */ + +/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */ + {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */ + {0x2, 0x0000, 0x0005}, + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + + {0x2, 0x006a, 0x0001}, /* C/S Enable ISOSYNCH Packet Engine */ + {} +}; + +/* + * Data used to initialize a SPCA501C with HV7131B sensor. + * From a capture file taken with USBSnoop v 1.5 + * I have a "SPCA501C pc camera chipset" manual by sunplus, but some + * of the value meanings are obscure or simply "reserved". + * to do list: + * 1) Understand what every value means + * 2) Understand why some values seem to appear more than once + * 3) Write a small comment for each line of the following arrays. + */ +static __u16 spca501c_arowana_open_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {} +}; + +static __u16 spca501c_arowana_init_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, + {0x01, 0x0032, 0x0016}, + {0x01, 0x0000, 0x0011}, + {0x01, 0x0000, 0x0012}, + {0x01, 0x0000, 0x0013}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x000f, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x001e, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x002d, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {} +}; + +/* Unknow camera from Ori Usbid 0x0000:0x0000 */ +/* Based on snoops from Ori Cohen */ +static __u16 spca501c_mysterious_open_data[][3] = { + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, +/* DSP Registers */ + {0x01, 0x0016, 0x0011}, /* RGB offset */ + {0x01, 0x0000, 0x0012}, + {0x01, 0x0006, 0x0013}, + {0x01, 0x0078, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0046, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x00, 0x0025, 0x0000}, +/* {0x00, 0x0000, 0x0000 }, */ +/* Part 2 */ +/* TG Registers */ + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {} +}; + +/* Based on snoops from Ori Cohen */ +static __u16 spca501c_mysterious_init_data[][3] = { +/* Part 3 */ +/* TG registers */ +/* {0x00, 0x0000, 0x0000}, */ + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x0006, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0001, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, /* 640 */ + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, /* 480 */ + {0x00, 0x0000, 0x0024}, /* Offset H hight */ + {0x00, 0x00d3, 0x0025}, /* low */ + {0x00, 0x0000, 0x0026}, /* Offset V */ + {0x00, 0x000d, 0x0027}, /* low */ + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, +/* DSP Registers */ + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */ + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */ + {0x01, 0x002d, 0x0009}, /* A12 */ + {0x01, 0x0005, 0x000a}, /* A13 */ + {0x01, 0x0023, 0x000b}, /* A21 */ + {0x01, 0x00e0, 0x000c}, /* A22 */ + {0x01, 0x00fd, 0x000d}, /* A23 */ + {0x01, 0x00f4, 0x000e}, /* A31 */ + {0x01, 0x00e4, 0x000f}, /* A32 */ + {0x01, 0x0028, 0x0010}, /* A33 */ + {0x01, 0x00ff, 0x0015}, /* Reserved */ + {0x01, 0x0001, 0x0016}, /* Reserved */ + {0x01, 0x0032, 0x0017}, /* Win1 Start begin */ + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, /* Win1 Start end */ + {0x01, 0x00ff, 0x003e}, /* Reserved begin */ + {0x01, 0x0002, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0003, 0x0056}, /* Reserved end */ + {0x01, 0x0060, 0x0057}, /* Edge Gain */ + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */ + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x200a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, +/* Part 4 */ + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x004e, 0x0000}, +/* Part 5 */ + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x000f, 0x0008}, + {0x01, 0x002d, 0x0009}, + {0x01, 0x0005, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffe0, 0x000c}, + {0x01, 0xfffd, 0x000d}, + {0x01, 0xfff4, 0x000e}, + {0x01, 0xffe4, 0x000f}, + {0x01, 0x0028, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */ + {0x01, 0x0032, 0x0016}, /* 32 */ + {0x01, 0x0016, 0x0011}, /* R 00 */ + {0x01, 0x0016, 0x0012}, /* G 00 */ + {0x01, 0x0016, 0x0013}, /* B 00 */ + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x", + req, index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + __u8 buf[2]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Reg write failed for 0x%02x,0x%02x,0x%02x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness); + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 brightness; + + brightness = reg_read(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, 2); + sd->brightness = brightness << 1; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x00, + (sd->contrast >> 8) & 0xff); + reg_write(gspca_dev->dev, 0x00, 0x01, + sd->contrast & 0xff); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ +/* spca50x->contrast = 0xaa01; */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = reg_read(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, 2); +/* sd->hue = (reg_read(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, */ +/* 2) & 0xFF) << 8; */ +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x0000: /* Unknow Camera */ +/* switch (product) { */ +/* case 0x0000: */ + sd->subtype = MystFromOriUnknownCamera; +/* break; */ +/* } */ + break; + case 0x040a: /* Kodak cameras */ +/* switch (product) { */ +/* case 0x0002: */ + sd->subtype = KodakDVC325; +/* break; */ +/* } */ + break; + case 0x0497: /* Smile International */ +/* switch (product) { */ +/* case 0xc001: */ + sd->subtype = SmileIntlCamera; +/* break; */ +/* } */ + break; + case 0x0506: /* 3COM cameras */ +/* switch (product) { */ +/* case 0x00df: */ + sd->subtype = ThreeComHomeConnectLite; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ + switch (product) { + case 0x0401: + sd->subtype = IntelCreateAndShare; + break; + case 0x0402: + sd->subtype = ViewQuestM318B; + break; + } + break; + case 0x1776: /* Arowana */ +/* switch (product) { */ +/* case 0x501c: */ + sd->subtype = Arowana300KCMOSCamera; +/* break; */ +/* } */ + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value; + + switch (sd->subtype) { + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_arowana_init_data)) + goto error; + break; + case MystFromOriUnknownCamera: + /* UnKnow Ori CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_mysterious_open_data)) + goto error; + break; + default: + /* generic spca501 init data */ + if (write_vector(gspca_dev, spca501_init_data)) + goto error; + break; + } + return 0; +error: + return -EINVAL; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_STREAM, "SPCA501 init"); + switch (sd->subtype) { + case ThreeComHomeConnectLite: + /* Special handling for 3com data */ + write_vector(gspca_dev, spca501_3com_open_data); + break; + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + write_vector(gspca_dev, spca501c_arowana_open_data); + break; + case MystFromOriUnknownCamera: + /* UnKnow CMOS Camera data */ + write_vector(gspca_dev, spca501c_mysterious_init_data); + break; + default: + /* Generic 501 open data */ + write_vector(gspca_dev, spca501_open_data); + } + PDEBUG(D_STREAM, "Initializing SPCA501 finished"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int mode; + + /* memorize the wanted pixel format */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + + /* Enable ISO packet machine CTRL reg=2, + * index=1 bitmask=0x2 (bit ordinal 1) */ + reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94); + switch (mode) { + case 0: /* 640x480 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a); + break; + case 1: /* 320x240 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a); + break; + default: +/* case 2: * 160x120 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a); + break; + } + reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02); + + /* HDG atleast the Intel CreateAndShare needs to have one of its + * brightness / contrast / color set otherwise it assumes wath seems + * max contrast. Note that strange enough setting any of these is + * enough to fix the max contrast problem, to be sure we set all 3 */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet + * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, 0); + data += SPCA501_OFFSET_DATA; + len -= SPCA501_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0002), DVNM("Kodak DVC-325")}, + {USB_DEVICE(0x0497, 0xc001), DVNM("Smile International")}, + {USB_DEVICE(0x0506, 0x00df), DVNM("3Com HomeConnect Lite")}, + {USB_DEVICE(0x0733, 0x0401), DVNM("Intel Create and Share")}, + {USB_DEVICE(0x0733, 0x0402), DVNM("ViewQuest M318B")}, + {USB_DEVICE(0x1776, 0x501c), DVNM("Arowana 300K CMOS Camera")}, + {USB_DEVICE(0x0000, 0x0000), DVNM("MystFromOri Unknow Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c new file mode 100644 index 000000000000..5b23518d970d --- /dev/null +++ b/drivers/media/video/gspca/spca505.c @@ -0,0 +1,933 @@ +/* + * SPCA505 chip based cameras initialization data + * + * V4L2 by Jean-Francis Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca505" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */ + unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ + + unsigned char brightness; + + char subtype; +#define IntelPCCameraPro 0 +#define Nxultra 1 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_YUYV, 160, 120, 5}, + {V4L2_PIX_FMT_YUYV, 176, 144, 4}, + {V4L2_PIX_FMT_YUYV, 320, 240, 2}, + {V4L2_PIX_FMT_YUYV, 352, 288, 1}, + {V4L2_PIX_FMT_YUYV, 640, 480, 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SPCA50X_REG_USB 0x02 /* spca505 501 */ + +#define SPCA50X_USB_CTRL 0x00 /* spca505 */ +#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */ +#define SPCA50X_REG_GLOBAL 0x03 /* spca505 */ +#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */ +#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */ + +#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */ +#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */ +#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */ + +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static __u16 spca505_init_data[][3] = { + /* line bmRequest,value,index */ + /* 1819 */ + {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, + /* Sensor reset */ + /* 1822 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, + /* 1825 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, + /* Block USB reset */ + /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, + SPCA50X_GLOBAL_MISC0}, + + /* 1831 */ {0x5, 0x01, 0x10}, + /* Maybe power down some stuff */ + /* 1834 */ {0x5, 0x0f, 0x11}, + + /* Setup internal CCD ? */ + /* 1837 */ {0x6, 0x10, 0x08}, + /* 1840 */ {0x6, 0x00, 0x09}, + /* 1843 */ {0x6, 0x00, 0x0a}, + /* 1846 */ {0x6, 0x00, 0x0b}, + /* 1849 */ {0x6, 0x10, 0x0c}, + /* 1852 */ {0x6, 0x00, 0x0d}, + /* 1855 */ {0x6, 0x00, 0x0e}, + /* 1858 */ {0x6, 0x00, 0x0f}, + /* 1861 */ {0x6, 0x10, 0x10}, + /* 1864 */ {0x6, 0x02, 0x11}, + /* 1867 */ {0x6, 0x00, 0x12}, + /* 1870 */ {0x6, 0x04, 0x13}, + /* 1873 */ {0x6, 0x02, 0x14}, + /* 1876 */ {0x6, 0x8a, 0x51}, + /* 1879 */ {0x6, 0x40, 0x52}, + /* 1882 */ {0x6, 0xb6, 0x53}, + /* 1885 */ {0x6, 0x3d, 0x54}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static __u16 spca505_open_data_ccd[][3] = { + /* line bmRequest,value,index */ + /* Internal CCD data set */ + /* 1891 */ {0x3, 0x04, 0x01}, + /* This could be a reset */ + /* 1894 */ {0x3, 0x00, 0x01}, + + /* Setup compression and image registers. 0x6 and 0x7 seem to be + related to H&V hold, and are resolution mode specific */ + /* 1897 */ {0x4, 0x10, 0x01}, + /* DIFF(0x50), was (0x10) */ + /* 1900 */ {0x4, 0x00, 0x04}, + /* 1903 */ {0x4, 0x00, 0x05}, + /* 1906 */ {0x4, 0x20, 0x06}, + /* 1909 */ {0x4, 0x20, 0x07}, + + /* 1912 */ {0x8, 0x0a, 0x00}, + /* DIFF (0x4a), was (0xa) */ + + /* 1915 */ {0x5, 0x00, 0x10}, + /* 1918 */ {0x5, 0x00, 0x11}, + /* 1921 */ {0x5, 0x00, 0x00}, + /* DIFF not written */ + /* 1924 */ {0x5, 0x00, 0x01}, + /* DIFF not written */ + /* 1927 */ {0x5, 0x00, 0x02}, + /* DIFF not written */ + /* 1930 */ {0x5, 0x00, 0x03}, + /* DIFF not written */ + /* 1933 */ {0x5, 0x00, 0x04}, + /* DIFF not written */ + /* 1936 */ {0x5, 0x80, 0x05}, + /* DIFF not written */ + /* 1939 */ {0x5, 0xe0, 0x06}, + /* DIFF not written */ + /* 1942 */ {0x5, 0x20, 0x07}, + /* DIFF not written */ + /* 1945 */ {0x5, 0xa0, 0x08}, + /* DIFF not written */ + /* 1948 */ {0x5, 0x0, 0x12}, + /* DIFF not written */ + /* 1951 */ {0x5, 0x02, 0x0f}, + /* DIFF not written */ + /* 1954 */ {0x5, 0x10, 0x46}, + /* DIFF not written */ + /* 1957 */ {0x5, 0x8, 0x4a}, + /* DIFF not written */ + + /* 1960 */ {0x3, 0x08, 0x03}, + /* DIFF (0x3,0x28,0x3) */ + /* 1963 */ {0x3, 0x08, 0x01}, + /* 1966 */ {0x3, 0x0c, 0x03}, + /* DIFF not written */ + /* 1969 */ {0x3, 0x21, 0x00}, + /* DIFF (0x39) */ + +/* Extra block copied from init to hopefully ensure CCD is in a sane state */ + /* 1837 */ {0x6, 0x10, 0x08}, + /* 1840 */ {0x6, 0x00, 0x09}, + /* 1843 */ {0x6, 0x00, 0x0a}, + /* 1846 */ {0x6, 0x00, 0x0b}, + /* 1849 */ {0x6, 0x10, 0x0c}, + /* 1852 */ {0x6, 0x00, 0x0d}, + /* 1855 */ {0x6, 0x00, 0x0e}, + /* 1858 */ {0x6, 0x00, 0x0f}, + /* 1861 */ {0x6, 0x10, 0x10}, + /* 1864 */ {0x6, 0x02, 0x11}, + /* 1867 */ {0x6, 0x00, 0x12}, + /* 1870 */ {0x6, 0x04, 0x13}, + /* 1873 */ {0x6, 0x02, 0x14}, + /* 1876 */ {0x6, 0x8a, 0x51}, + /* 1879 */ {0x6, 0x40, 0x52}, + /* 1882 */ {0x6, 0xb6, 0x53}, + /* 1885 */ {0x6, 0x3d, 0x54}, + /* End of extra block */ + + /* 1972 */ {0x6, 0x3f, 0x1}, + /* Block skipped */ + /* 1975 */ {0x6, 0x10, 0x02}, + /* 1978 */ {0x6, 0x64, 0x07}, + /* 1981 */ {0x6, 0x10, 0x08}, + /* 1984 */ {0x6, 0x00, 0x09}, + /* 1987 */ {0x6, 0x00, 0x0a}, + /* 1990 */ {0x6, 0x00, 0x0b}, + /* 1993 */ {0x6, 0x10, 0x0c}, + /* 1996 */ {0x6, 0x00, 0x0d}, + /* 1999 */ {0x6, 0x00, 0x0e}, + /* 2002 */ {0x6, 0x00, 0x0f}, + /* 2005 */ {0x6, 0x10, 0x10}, + /* 2008 */ {0x6, 0x02, 0x11}, + /* 2011 */ {0x6, 0x00, 0x12}, + /* 2014 */ {0x6, 0x04, 0x13}, + /* 2017 */ {0x6, 0x02, 0x14}, + /* 2020 */ {0x6, 0x8a, 0x51}, + /* 2023 */ {0x6, 0x40, 0x52}, + /* 2026 */ {0x6, 0xb6, 0x53}, + /* 2029 */ {0x6, 0x3d, 0x54}, + /* 2032 */ {0x6, 0x60, 0x57}, + /* 2035 */ {0x6, 0x20, 0x58}, + /* 2038 */ {0x6, 0x15, 0x59}, + /* 2041 */ {0x6, 0x05, 0x5a}, + + /* 2044 */ {0x5, 0x01, 0xc0}, + /* 2047 */ {0x5, 0x10, 0xcb}, + /* 2050 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2053 */ {0x5, 0x0, 0xc2}, + /* 4 was 0 */ + /* 2056 */ {0x5, 0x00, 0xca}, + /* 2059 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2062 */ {0x5, 0x04, 0xc2}, + /* 2065 */ {0x5, 0x00, 0xca}, + /* 2068 */ {0x5, 0x0, 0xc1}, + /* */ + /* 2071 */ {0x5, 0x00, 0xc2}, + /* 2074 */ {0x5, 0x00, 0xca}, + /* 2077 */ {0x5, 0x40, 0xc1}, + /* */ + /* 2080 */ {0x5, 0x17, 0xc2}, + /* 2083 */ {0x5, 0x00, 0xca}, + /* 2086 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2089 */ {0x5, 0x06, 0xc2}, + /* 2092 */ {0x5, 0x00, 0xca}, + /* 2095 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2098 */ {0x5, 0x04, 0xc2}, + /* 2101 */ {0x5, 0x00, 0xca}, + + /* 2104 */ {0x3, 0x4c, 0x3}, + /* 2107 */ {0x3, 0x18, 0x1}, + + /* 2110 */ {0x6, 0x70, 0x51}, + /* 2113 */ {0x6, 0xbe, 0x53}, + /* 2116 */ {0x6, 0x71, 0x57}, + /* 2119 */ {0x6, 0x20, 0x58}, + /* 2122 */ {0x6, 0x05, 0x59}, + /* 2125 */ {0x6, 0x15, 0x5a}, + + /* 2128 */ {0x4, 0x00, 0x08}, + /* Compress = OFF (0x1 to turn on) */ + /* 2131 */ {0x4, 0x12, 0x09}, + /* 2134 */ {0x4, 0x21, 0x0a}, + /* 2137 */ {0x4, 0x10, 0x0b}, + /* 2140 */ {0x4, 0x21, 0x0c}, + /* 2143 */ {0x4, 0x05, 0x00}, + /* was 5 (Image Type ? ) */ + /* 2146 */ {0x4, 0x00, 0x01}, + + /* 2149 */ {0x6, 0x3f, 0x01}, + + /* 2152 */ {0x4, 0x00, 0x04}, + /* 2155 */ {0x4, 0x00, 0x05}, + /* 2158 */ {0x4, 0x40, 0x06}, + /* 2161 */ {0x4, 0x40, 0x07}, + + /* 2164 */ {0x6, 0x1c, 0x17}, + /* 2167 */ {0x6, 0xe2, 0x19}, + /* 2170 */ {0x6, 0x1c, 0x1b}, + /* 2173 */ {0x6, 0xe2, 0x1d}, + /* 2176 */ {0x6, 0xaa, 0x1f}, + /* 2179 */ {0x6, 0x70, 0x20}, + + /* 2182 */ {0x5, 0x01, 0x10}, + /* 2185 */ {0x5, 0x00, 0x11}, + /* 2188 */ {0x5, 0x01, 0x00}, + /* 2191 */ {0x5, 0x05, 0x01}, + /* 2194 */ {0x5, 0x00, 0xc1}, + /* */ + /* 2197 */ {0x5, 0x00, 0xc2}, + /* 2200 */ {0x5, 0x00, 0xca}, + + /* 2203 */ {0x6, 0x70, 0x51}, + /* 2206 */ {0x6, 0xbe, 0x53}, + {} +}; + +/* + Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) + * SPCA505b chip based cameras initialization data + * + */ +/* jfm */ +#define initial_brightness 0x7f /* 0x0(white)-0xff(black) */ +/* #define initial_brightness 0x0 //0x0(white)-0xff(black) */ +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static __u16 spca505b_init_data[][3] = { +/* start */ + {0x02, 0x00, 0x00}, /* init */ + {0x02, 0x00, 0x01}, + {0x02, 0x00, 0x02}, + {0x02, 0x00, 0x03}, + {0x02, 0x00, 0x04}, + {0x02, 0x00, 0x05}, + {0x02, 0x00, 0x06}, + {0x02, 0x00, 0x07}, + {0x02, 0x00, 0x08}, + {0x02, 0x00, 0x09}, + {0x03, 0x00, 0x00}, + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x02}, + {0x03, 0x00, 0x03}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x05}, + {0x03, 0x00, 0x06}, + {0x04, 0x00, 0x00}, + {0x04, 0x00, 0x02}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x00, 0x06}, + {0x04, 0x00, 0x07}, + {0x04, 0x00, 0x08}, + {0x04, 0x00, 0x09}, + {0x04, 0x00, 0x0a}, + {0x04, 0x00, 0x0b}, + {0x04, 0x00, 0x0c}, + {0x07, 0x00, 0x00}, + {0x07, 0x00, 0x03}, + {0x08, 0x00, 0x00}, + {0x08, 0x00, 0x01}, + {0x08, 0x00, 0x02}, + {0x00, 0x01, 0x00}, + {0x00, 0x01, 0x01}, + {0x00, 0x01, 0x34}, + {0x00, 0x01, 0x35}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x11}, + {0x06, 0x00, 0x14}, + {0x06, 0x00, 0x13}, + {0x06, 0x28, 0x51}, + {0x06, 0xff, 0x53}, + {0x02, 0x00, 0x08}, + + {0x03, 0x00, 0x03}, + {0x03, 0x10, 0x03}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static __u16 spca505b_open_data_ccd[][3] = { + +/* {0x02,0x00,0x00}, */ + {0x03, 0x04, 0x01}, /* rst */ + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x00}, + {0x03, 0x21, 0x00}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x03}, + {0x03, 0x18, 0x03}, + {0x03, 0x08, 0x01}, + {0x03, 0x1c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x18, 0x01}, + +/* same as 505 */ + {0x04, 0x10, 0x01}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x20, 0x06}, + {0x04, 0x20, 0x07}, + + {0x08, 0x0a, 0x00}, + + {0x05, 0x00, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x00, 0x12}, + {0x05, 0x6f, 0x00}, + {0x05, initial_brightness >> 6, 0x00}, + {0x05, initial_brightness << 2, 0x01}, + {0x05, 0x00, 0x02}, + {0x05, 0x01, 0x03}, + {0x05, 0x00, 0x04}, + {0x05, 0x03, 0x05}, + {0x05, 0xe0, 0x06}, + {0x05, 0x20, 0x07}, + {0x05, 0xa0, 0x08}, + {0x05, 0x00, 0x12}, + {0x05, 0x02, 0x0f}, + {0x05, 128, 0x14}, /* max exposure off (0=on) */ + {0x05, 0x01, 0xb0}, + {0x05, 0x01, 0xbf}, + {0x03, 0x02, 0x06}, + {0x05, 0x10, 0x46}, + {0x05, 0x08, 0x4a}, + + {0x06, 0x00, 0x01}, + {0x06, 0x10, 0x02}, + {0x06, 0x64, 0x07}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x04, 0x00, 0x01}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x11, 0x10}, /* contrast */ + {0x06, 0x00, 0x11}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x13}, + {0x06, 0x00, 0x14}, + {0x06, 0x9d, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0x7c, 0x53}, + {0x06, 0x40, 0x54}, + {0x06, 0x02, 0x57}, + {0x06, 0x03, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x05, 0x5a}, + {0x06, 0x03, 0x56}, + {0x06, 0x02, 0x3f}, + {0x06, 0x00, 0x40}, + {0x06, 0x39, 0x41}, + {0x06, 0x69, 0x42}, + {0x06, 0x87, 0x43}, + {0x06, 0x9e, 0x44}, + {0x06, 0xb1, 0x45}, + {0x06, 0xbf, 0x46}, + {0x06, 0xcc, 0x47}, + {0x06, 0xd5, 0x48}, + {0x06, 0xdd, 0x49}, + {0x06, 0xe3, 0x4a}, + {0x06, 0xe8, 0x4b}, + {0x06, 0xed, 0x4c}, + {0x06, 0xf2, 0x4d}, + {0x06, 0xf7, 0x4e}, + {0x06, 0xfc, 0x4f}, + {0x06, 0xff, 0x50}, + + {0x05, 0x01, 0xc0}, + {0x05, 0x10, 0xcb}, + {0x05, 0x40, 0xc1}, + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0xc0, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x59, 0xc2}, + {0x05, 0x00, 0xca}, + {0x04, 0x00, 0x01}, + {0x05, 0x80, 0xc1}, + {0x05, 0xec, 0xc2}, + {0x05, 0x0, 0xca}, + + {0x06, 0x02, 0x57}, + {0x06, 0x01, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x0a, 0x5a}, + {0x06, 0x01, 0x57}, + {0x06, 0x8a, 0x03}, + {0x06, 0x0a, 0x6c}, + {0x06, 0x30, 0x01}, + {0x06, 0x20, 0x02}, + {0x06, 0x00, 0x03}, + + {0x05, 0x8c, 0x25}, + + {0x06, 0x4d, 0x51}, /* maybe saturation (4d) */ + {0x06, 0x84, 0x53}, /* making green (84) */ + {0x06, 0x00, 0x57}, /* sharpness (1) */ + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, /* maybe hue (18) */ + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, /* maybe contrast (18) */ + + {0x05, 0x01, 0x02}, + + {0x04, 0x00, 0x08}, /* compression */ + {0x04, 0x12, 0x09}, + {0x04, 0x21, 0x0a}, + {0x04, 0x10, 0x0b}, + {0x04, 0x21, 0x0c}, + {0x04, 0x1d, 0x00}, /* imagetype (1d) */ + {0x04, 0x41, 0x01}, /* hardware snapcontrol */ + + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x10, 0x06}, + {0x04, 0x10, 0x07}, + {0x04, 0x40, 0x06}, + {0x04, 0x40, 0x07}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + + {0x06, 0x1c, 0x17}, + {0x06, 0xe2, 0x19}, + {0x06, 0x1c, 0x1b}, + {0x06, 0xe2, 0x1d}, + {0x06, 0x5f, 0x1f}, + {0x06, 0x32, 0x20}, + + {0x05, initial_brightness >> 6, 0x00}, + {0x05, initial_brightness << 2, 0x01}, + {0x05, 0x06, 0xc1}, + {0x05, 0x58, 0xc2}, + {0x05, 0x0, 0xca}, + {0x05, 0x0, 0x11}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + reg, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x", + reg, index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 reg, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + unsigned char buf[4]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + reg, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16) 0, /* value */ + (__u16) index, + buf, + length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Register write failed for 0x%x,0x%x,0x%x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x401d: * here505b */ + sd->subtype = Nxultra; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ +/* switch (product) { */ +/* case 0x0430: */ +/* fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ + sd->subtype = IntelPCCameraPro; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + if (sd->subtype != IntelPCCameraPro) + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + else /* no 640x480 for IntelPCCameraPro */ + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0] - 1; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + + if (sd->subtype == Nxultra) { + if (write_vector(gspca_dev, spca505b_init_data)) + return -EIO; + } else { + if (write_vector(gspca_dev, spca505_init_data)) + return -EIO; + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + PDEBUG(D_STREAM, "Initializing SPCA505"); + if (sd->subtype == Nxultra) + write_vector(gspca_dev, spca505b_open_data_ccd); + else + write_vector(gspca_dev, spca505_open_data_ccd); + ret = reg_read(gspca_dev->dev, 6, 0x16, 2); + + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, + "register read failed for after vector read err = %d", + ret); + return -EIO; + } + PDEBUG(D_STREAM, + "After vector read returns : 0x%x should be 0x0101", + ret & 0xffff); + + ret = reg_write(gspca_dev->dev, 6, 0x16, 0x0a); + if (ret < 0) { + PDEBUG(D_ERR, "register write failed for (6,0xa,0x16) err=%d", + ret); + return -EIO; + } + reg_write(gspca_dev->dev, 5, 0xc2, 18); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + /* necessary because without it we can see stream + * only once after loading module */ + /* stopping usb registers Tomasz change */ + reg_write(dev, 0x02, 0x0, 0x0); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + reg_write(dev, 0x04, 0x00, 0x00); + reg_write(dev, 0x04, 0x06, 0x10); + reg_write(dev, 0x04, 0x07, 0x10); + break; + case 1: + reg_write(dev, 0x04, 0x00, 0x01); + reg_write(dev, 0x04, 0x06, 0x1a); + reg_write(dev, 0x04, 0x07, 0x1a); + break; + case 2: + reg_write(dev, 0x04, 0x00, 0x02); + reg_write(dev, 0x04, 0x06, 0x1c); + reg_write(dev, 0x04, 0x07, 0x1d); + break; + case 4: + reg_write(dev, 0x04, 0x00, 0x04); + reg_write(dev, 0x04, 0x06, 0x34); + reg_write(dev, 0x04, 0x07, 0x34); + break; + default: +/* case 5: */ + reg_write(dev, 0x04, 0x00, 0x05); + reg_write(dev, 0x04, 0x06, 0x40); + reg_write(dev, 0x04, 0x07, 0x40); + break; + } +/* Enable ISO packet machine - should we do this here or in ISOC init ? */ + ret = reg_write(dev, SPCA50X_REG_USB, + SPCA50X_USB_CTRL, + SPCA50X_CUSB_ENABLE); + +/* reg_write(dev, 0x5, 0x0, 0x0); */ +/* reg_write(dev, 0x5, 0x0, 0x1); */ +/* reg_write(dev, 0x5, 0x11, 0x2); */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet machine */ + reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + /* This maybe reset or power control */ + reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); + reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); + reg_write(gspca_dev->dev, 0x03, 0x00, 0x1); + reg_write(gspca_dev->dev, 0x05, 0x10, 0x1); + reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); +} + +/* convert YYUV per line to YUYV (YUV 4:2:2) */ +static void yyuv_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + yi1 = yi + width; + Ui = yi1 + width; + Vi = Ui + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yyuv_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + __u8 brightness = sd->brightness; + reg_write(gspca_dev->dev, 5, 0x00, (255 - brightness) >> 6); + reg_write(gspca_dev->dev, 5, 0x01, (255 - brightness) << 2); + +} +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = 255 + - ((reg_read(gspca_dev->dev, 5, 0x01, 1) >> 2) + + (reg_read(gspca_dev->dev, 5, 0x0, 1) << 6)); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401d), DVNM("Creative Webcam NX ULTRA")}, + {USB_DEVICE(0x0733, 0x0430), DVNM("Intel PC Camera Pro")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof (struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c new file mode 100644 index 000000000000..614fb3ad7711 --- /dev/null +++ b/drivers/media/video/gspca/spca506.c @@ -0,0 +1,830 @@ +/* + * SPCA506 chip based cameras function + * M Xhaard 15/04/2004 based on different work Mark Taylor and others + * and my own snoopy file on a pv-321c donate by a german compagny + * "Firma Frank Gmbh" from Saarbruecken + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca506" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[640 * 480 * 3]; /* YYUV per line */ + unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char hue; + char norme; + char channel; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x80, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x47, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x40, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_HUE 3 + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0, + }, + .set = sd_sethue, + .get = sd_gethue, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_YUYV, 160, 120, 5}, + {V4L2_PIX_FMT_YUYV, 176, 144, 4}, + {V4L2_PIX_FMT_YUYV, 320, 240, 2}, + {V4L2_PIX_FMT_YUYV, 352, 288, 1}, + {V4L2_PIX_FMT_YUYV, 640, 480, 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SAA7113_bright 0x0a /* defaults 0x80 */ +#define SAA7113_contrast 0x0b /* defaults 0x47 */ +#define SAA7113_saturation 0x0c /* defaults 0x40 */ +#define SAA7113_hue 0x0d /* defaults 0x00 */ +#define SAA7113_I2C_BASE_WRITE 0x4a + +static void reg_r(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); +} + +static void spca506_Initi2c(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); +} + +static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur, + __u16 reg) +{ + int retry = 60; + unsigned char Data[2]; + + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, valeur, 0x0000); + while (retry--) { + reg_r(gspca_dev->dev, 0x07, 0x0003, Data, 2); + if ((Data[0] | Data[1]) == 0x00) + break; + } +} + +static int spca506_ReadI2c(struct gspca_dev *gspca_dev, __u16 reg) +{ + int retry = 60; + unsigned char Data[2]; + unsigned char value; + + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, 0x01, 0x0002); + while (--retry) { + reg_r(gspca_dev->dev, 0x07, 0x0003, Data, 2); + if ((Data[0] | Data[1]) == 0x00) + break; + } + if (retry == 0) + return -1; + reg_r(gspca_dev->dev, 0x07, 0x0000, &value, 1); + return value; +} + +static void spca506_SetNormeInput(struct gspca_dev *gspca_dev, + __u16 norme, + __u16 channel) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* fixme: check if channel == 0..3 and 6..9 (8 values) */ + __u8 setbit0 = 0x00; + __u8 setbit1 = 0x00; + __u8 videomask = 0x00; + + PDEBUG(D_STREAM, "** Open Set Norme **"); + spca506_Initi2c(gspca_dev); + /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */ + /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */ + /* and exclude SAA7113 reserved channel set default 0 otherwise */ + if (norme & V4L2_STD_NTSC) + setbit0 = 0x01; + if (channel == 4 || channel == 5 || channel > 9) + channel = 0; + if (channel < 4) + setbit1 = 0x02; + videomask = (0x48 | setbit0 | setbit1); + reg_w(gspca_dev->dev, 0x08, videomask, 0x0000); + spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02); + + if (norme & V4L2_STD_NTSC) + spca506_WriteI2c(gspca_dev, 0x33, 0x0e); + /* Chrominance Control NTSC N */ + else if (norme & V4L2_STD_SECAM) + spca506_WriteI2c(gspca_dev, 0x53, 0x0e); + /* Chrominance Control SECAM */ + else + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); + /* Chrominance Control PAL BGHIV */ + + sd->norme = norme; + sd->channel = channel; + PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask); + PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel); +} + +static void spca506_GetNormeInput(struct gspca_dev *gspca_dev, + __u16 *norme, __u16 *channel) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Read the register is not so good value change so + we use your own copy in spca50x struct */ + *norme = sd->norme; + *channel = sd->channel; + PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel); +} + +static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code, + __u16 xmult, __u16 ymult) +{ + struct usb_device *dev = gspca_dev->dev; + + PDEBUG(D_STREAM, "** SetSize **"); + reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000); + /* Soft snap 0x40 Hard 0x41 */ + reg_w(dev, 0x04, 0x41, 0x0001); + reg_w(dev, 0x04, 0x00, 0x0002); + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0003); + + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0004); + /* reserved */ + reg_w(dev, 0x04, 0x01, 0x0005); + /* reserced */ + reg_w(dev, 0x04, xmult, 0x0006); + /* reserved */ + reg_w(dev, 0x04, ymult, 0x0007); + /* compression 1 */ + reg_w(dev, 0x04, 0x00, 0x0008); + /* T=64 -> 2 */ + reg_w(dev, 0x04, 0x00, 0x0009); + /* threshold2D */ + reg_w(dev, 0x04, 0x21, 0x000a); + /* quantization */ + reg_w(dev, 0x04, 0x00, 0x000b); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->hue = sd_ctrls[SD_HUE].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0000); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + /* Init on PAL and composite input0 */ + spca506_SetNormeInput(gspca_dev, 0, 0); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + reg_w(dev, 0x05, 0x00, 0x0000); + reg_w(dev, 0x05, 0xef, 0x0001); + reg_w(dev, 0x05, 0x00, 0x00c1); + reg_w(dev, 0x05, 0x00, 0x00c2); + reg_w(dev, 0x06, 0x18, 0x0002); + reg_w(dev, 0x06, 0xf5, 0x0011); + reg_w(dev, 0x06, 0x02, 0x0012); + reg_w(dev, 0x06, 0xfb, 0x0013); + reg_w(dev, 0x06, 0x00, 0x0014); + reg_w(dev, 0x06, 0xa4, 0x0051); + reg_w(dev, 0x06, 0x40, 0x0052); + reg_w(dev, 0x06, 0x71, 0x0053); + reg_w(dev, 0x06, 0x40, 0x0054); + /************************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + /* for a better reading mx :) */ + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); + spca506_WriteI2c(gspca_dev, 0xc0, 0x02); + /* input composite video */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + spca506_WriteI2c(gspca_dev, 0x98, 0x08); + spca506_WriteI2c(gspca_dev, 0x03, 0x09); + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); + spca506_WriteI2c(gspca_dev, 0x01, 0x13); + spca506_WriteI2c(gspca_dev, 0x00, 0x14); + spca506_WriteI2c(gspca_dev, 0x00, 0x15); + spca506_WriteI2c(gspca_dev, 0x00, 0x16); + spca506_WriteI2c(gspca_dev, 0x00, 0x17); + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + PDEBUG(D_STREAM, "** Close Init *"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u16 norme; + __u16 channel; + __u8 Data[2]; + + /**************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */ +/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + /* Analog Input Control 2 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + /* Analog Input Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + /* Analog Input Control 4 */ + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + /* Horizontal Sync Start 0xe9-0x0d */ + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + /* Horizontal Sync Stop 0x0d-0xf0 */ + + spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */ +/* Defaults value */ + spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */ + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + /* Luminance Brightness */ + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */ + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + /* Chrominance Saturation */ + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + /* Chrominance Hue Control */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + /* Chrominance Gain Control */ + /**************************************/ + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + /* Format/Delay Control */ + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */ + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */ + spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */ + spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */ + spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */ + spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */ + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + /**************************************/ + reg_w(dev, 0x05, 0x00, 0x0003); + reg_w(dev, 0x05, 0x00, 0x0004); + reg_w(dev, 0x03, 0x10, 0x0001); + reg_w(dev, 0x03, 0x78, 0x0000); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + spca506_Setsize(gspca_dev, 0, 0x10, 0x10); + break; + case 1: + spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a); + break; + case 2: + spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c); + break; + case 4: + spca506_Setsize(gspca_dev, 4, 0x34, 0x34); + break; + default: +/* case 5: */ + spca506_Setsize(gspca_dev, 5, 0x40, 0x40); + break; + } + + /* compress setting and size */ + /* set i2c luma */ + reg_w(dev, 0x02, 0x01, 0x0000); + reg_w(dev, 0x03, 0x12, 0x000); + reg_r(dev, 0x04, 0x0001, Data, 2); + PDEBUG(D_STREAM, "webcam started"); + spca506_GetNormeInput(gspca_dev, &norme, &channel); + spca506_SetNormeInput(gspca_dev, norme, channel); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +/* convert YYUV per line to YUYV (YUV 4:2:2) */ +static void yyuv_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + yi1 = yi + width; + Ui = yi1 + width; + Vi = Ui + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yyuv_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = spca506_ReadI2c(gspca_dev, SAA7113_bright); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = spca506_ReadI2c(gspca_dev, SAA7113_contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = spca506_ReadI2c(gspca_dev, SAA7113_saturation); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void gethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = spca506_ReadI2c(gspca_dev, SAA7113_hue); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gethue(gspca_dev); + *val = sd->hue; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06e1, 0xa190), DVNM("ADS Instant VCD")}, +/* {USB_DEVICE(0x0733, 0x0430), DVNM("UsbGrabber PV321c")}, */ + {USB_DEVICE(0x0734, 0x043b), DVNM("3DeMon USB Capture aka")}, + {USB_DEVICE(0x99fa, 0x8988), DVNM("Grandtec V.cap")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c new file mode 100644 index 000000000000..566adf41f59f --- /dev/null +++ b/drivers/media/video/gspca/spca508.c @@ -0,0 +1,1774 @@ +/* + * SPCA508 chip based cameras subdriver + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca508" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */ + unsigned char tmpbuf2[352 * 288 * 2]; /* YUYV */ + + unsigned char brightness; + + char subtype; +#define CreativeVista 0 +#define HamaUSBSightcam 1 +#define HamaUSBSightcam2 2 +#define IntelEasyPCCamera 3 +#define MicroInnovationIC200 4 +#define ViewQuestVQ110 5 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x80, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_YUYV, 160, 120, 3}, + {V4L2_PIX_FMT_YUYV, 176, 144, 2}, + {V4L2_PIX_FMT_YUYV, 320, 240, 1}, + {V4L2_PIX_FMT_YUYV, 352, 288, 0}, +}; + +/* Frame packet header offsets for the spca508 */ +#define SPCA508_OFFSET_TYPE 1 +#define SPCA508_OFFSET_COMPRESS 2 +#define SPCA508_OFFSET_FRAMSEQ 8 +#define SPCA508_OFFSET_WIN1LUM 11 +#define SPCA508_OFFSET_DATA 37 + +#define SPCA508_SNAPBIT 0x20 +#define SPCA508_SNAPCTRL 0x40 +/*************** I2c ****************/ +#define SPCA508_INDEX_I2C_BASE 0x8800 + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +static __u16 spca508_init_data[][3] = +#define IGN(x) /* nothing */ +{ + /* line URB value, index */ + /* 44274 1804 */ {0x0000, 0x870b}, + + /* 44299 1805 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ + /* 44324 1806 */ {0x0003, 0x8111}, + /* Reset compression & memory */ + /* 44349 1807 */ {0x0000, 0x8110}, + /* Disable all outputs */ + /* 44372 1808 */ /* READ {0x0000, 0x8114} -> 0000: 00 */ + /* 44398 1809 */ {0x0000, 0x8114}, + /* SW GPIO data */ + /* 44423 1810 */ {0x0008, 0x8110}, + /* Enable charge pump output */ + /* 44527 1811 */ {0x0002, 0x8116}, + /* 200 kHz pump clock */ + /* 44555 1812 */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ + /* 44590 1813 */ {0x0003, 0x8111}, + /* Reset compression & memory */ + /* 44615 1814 */ {0x0000, 0x8111}, + /* Normal mode (not reset) */ + /* 44640 1815 */ {0x0098, 0x8110}, + /* Enable charge pump output, sync.serial,external 2x clock */ + /* 44665 1816 */ {0x000d, 0x8114}, + /* SW GPIO data */ + /* 44690 1817 */ {0x0002, 0x8116}, + /* 200 kHz pump clock */ + /* 44715 1818 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ +/* --------------------------------------- */ + /* 44740 1819 */ {0x000f, 0x8402}, + /* memory bank */ + /* 44765 1820 */ {0x0000, 0x8403}, + /* ... address */ +/* --------------------------------------- */ +/* 0x88__ is Synchronous Serial Interface. */ +/* TBD: This table could be expressed more compactly */ +/* using spca508_write_i2c_vector(). */ +/* TBD: Should see if the values in spca50x_i2c_data */ +/* would work with the VQ110 instead of the values */ +/* below. */ + /* 44790 1821 */ {0x00c0, 0x8804}, + /* SSI slave addr */ + /* 44815 1822 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 44838 1823 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 44862 1824 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 44888 1825 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 44913 1826 */ {0x0012, 0x8801}, + /* SSI reg addr */ + /* 44938 1827 */ {0x0080, 0x8800}, + /* SSI data to write */ + /* 44961 1828 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 44985 1829 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45009 1830 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45035 1831 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 45060 1832 */ {0x0012, 0x8801}, + /* SSI reg addr */ + /* 45085 1833 */ {0x0000, 0x8800}, + /* SSI data to write */ + /* 45108 1834 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45132 1835 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45156 1836 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45182 1837 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 45207 1838 */ {0x0011, 0x8801}, + /* SSI reg addr */ + /* 45232 1839 */ {0x0040, 0x8800}, + /* SSI data to write */ + /* 45255 1840 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45279 1841 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45303 1842 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45329 1843 */ {0x0008, 0x8802}, + /* 45354 1844 */ {0x0013, 0x8801}, + /* 45379 1845 */ {0x0000, 0x8800}, + /* 45402 1846 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45426 1847 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45450 1848 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45476 1849 */ {0x0008, 0x8802}, + /* 45501 1850 */ {0x0014, 0x8801}, + /* 45526 1851 */ {0x0000, 0x8800}, + /* 45549 1852 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45573 1853 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45597 1854 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45623 1855 */ {0x0008, 0x8802}, + /* 45648 1856 */ {0x0015, 0x8801}, + /* 45673 1857 */ {0x0001, 0x8800}, + /* 45696 1858 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45720 1859 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45744 1860 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45770 1861 */ {0x0008, 0x8802}, + /* 45795 1862 */ {0x0016, 0x8801}, + /* 45820 1863 */ {0x0003, 0x8800}, + /* 45843 1864 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45867 1865 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45891 1866 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45917 1867 */ {0x0008, 0x8802}, + /* 45942 1868 */ {0x0017, 0x8801}, + /* 45967 1869 */ {0x0036, 0x8800}, + /* 45990 1870 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46014 1871 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46038 1872 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46064 1873 */ {0x0008, 0x8802}, + /* 46089 1874 */ {0x0018, 0x8801}, + /* 46114 1875 */ {0x00ec, 0x8800}, + /* 46137 1876 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46161 1877 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46185 1878 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46211 1879 */ {0x0008, 0x8802}, + /* 46236 1880 */ {0x001a, 0x8801}, + /* 46261 1881 */ {0x0094, 0x8800}, + /* 46284 1882 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46308 1883 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46332 1884 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46358 1885 */ {0x0008, 0x8802}, + /* 46383 1886 */ {0x001b, 0x8801}, + /* 46408 1887 */ {0x0000, 0x8800}, + /* 46431 1888 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46455 1889 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46479 1890 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46505 1891 */ {0x0008, 0x8802}, + /* 46530 1892 */ {0x0027, 0x8801}, + /* 46555 1893 */ {0x00a2, 0x8800}, + /* 46578 1894 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46602 1895 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46626 1896 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46652 1897 */ {0x0008, 0x8802}, + /* 46677 1898 */ {0x0028, 0x8801}, + /* 46702 1899 */ {0x0040, 0x8800}, + /* 46725 1900 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46749 1901 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46773 1902 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46799 1903 */ {0x0008, 0x8802}, + /* 46824 1904 */ {0x002a, 0x8801}, + /* 46849 1905 */ {0x0084, 0x8800}, + /* 46872 1906 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46896 1907 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46920 1908 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46946 1909 */ {0x0008, 0x8802}, + /* 46971 1910 */ {0x002b, 0x8801}, + /* 46996 1911 */ {0x00a8, 0x8800}, + /* 47019 1912 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47043 1913 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47067 1914 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47093 1915 */ {0x0008, 0x8802}, + /* 47118 1916 */ {0x002c, 0x8801}, + /* 47143 1917 */ {0x00fe, 0x8800}, + /* 47166 1918 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47190 1919 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47214 1920 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47240 1921 */ {0x0008, 0x8802}, + /* 47265 1922 */ {0x002d, 0x8801}, + /* 47290 1923 */ {0x0003, 0x8800}, + /* 47313 1924 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47337 1925 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47361 1926 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47387 1927 */ {0x0008, 0x8802}, + /* 47412 1928 */ {0x0038, 0x8801}, + /* 47437 1929 */ {0x0083, 0x8800}, + /* 47460 1930 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47484 1931 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47508 1932 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47534 1933 */ {0x0008, 0x8802}, + /* 47559 1934 */ {0x0033, 0x8801}, + /* 47584 1935 */ {0x0081, 0x8800}, + /* 47607 1936 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47631 1937 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47655 1938 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47681 1939 */ {0x0008, 0x8802}, + /* 47706 1940 */ {0x0034, 0x8801}, + /* 47731 1941 */ {0x004a, 0x8800}, + /* 47754 1942 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47778 1943 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47802 1944 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47828 1945 */ {0x0008, 0x8802}, + /* 47853 1946 */ {0x0039, 0x8801}, + /* 47878 1947 */ {0x0000, 0x8800}, + /* 47901 1948 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47925 1949 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47949 1950 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47975 1951 */ {0x0008, 0x8802}, + /* 48000 1952 */ {0x0010, 0x8801}, + /* 48025 1953 */ {0x00a8, 0x8800}, + /* 48048 1954 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48072 1955 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48096 1956 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48122 1957 */ {0x0008, 0x8802}, + /* 48147 1958 */ {0x0006, 0x8801}, + /* 48172 1959 */ {0x0058, 0x8800}, + /* 48195 1960 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48219 1961 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48243 1962 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48269 1963 */ {0x0008, 0x8802}, + /* 48294 1964 */ {0x0000, 0x8801}, + /* 48319 1965 */ {0x0004, 0x8800}, + /* 48342 1966 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48366 1967 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48390 1968 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48416 1969 */ {0x0008, 0x8802}, + /* 48441 1970 */ {0x0040, 0x8801}, + /* 48466 1971 */ {0x0080, 0x8800}, + /* 48489 1972 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48513 1973 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48537 1974 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48563 1975 */ {0x0008, 0x8802}, + /* 48588 1976 */ {0x0041, 0x8801}, + /* 48613 1977 */ {0x000c, 0x8800}, + /* 48636 1978 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48660 1979 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48684 1980 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48710 1981 */ {0x0008, 0x8802}, + /* 48735 1982 */ {0x0042, 0x8801}, + /* 48760 1983 */ {0x000c, 0x8800}, + /* 48783 1984 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48807 1985 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48831 1986 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48857 1987 */ {0x0008, 0x8802}, + /* 48882 1988 */ {0x0043, 0x8801}, + /* 48907 1989 */ {0x0028, 0x8800}, + /* 48930 1990 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48954 1991 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48978 1992 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49004 1993 */ {0x0008, 0x8802}, + /* 49029 1994 */ {0x0044, 0x8801}, + /* 49054 1995 */ {0x0080, 0x8800}, + /* 49077 1996 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49101 1997 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49125 1998 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49151 1999 */ {0x0008, 0x8802}, + /* 49176 2000 */ {0x0045, 0x8801}, + /* 49201 2001 */ {0x0020, 0x8800}, + /* 49224 2002 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49248 2003 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49272 2004 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49298 2005 */ {0x0008, 0x8802}, + /* 49323 2006 */ {0x0046, 0x8801}, + /* 49348 2007 */ {0x0020, 0x8800}, + /* 49371 2008 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49395 2009 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49419 2010 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49445 2011 */ {0x0008, 0x8802}, + /* 49470 2012 */ {0x0047, 0x8801}, + /* 49495 2013 */ {0x0080, 0x8800}, + /* 49518 2014 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49542 2015 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49566 2016 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49592 2017 */ {0x0008, 0x8802}, + /* 49617 2018 */ {0x0048, 0x8801}, + /* 49642 2019 */ {0x004c, 0x8800}, + /* 49665 2020 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49689 2021 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49713 2022 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49739 2023 */ {0x0008, 0x8802}, + /* 49764 2024 */ {0x0049, 0x8801}, + /* 49789 2025 */ {0x0084, 0x8800}, + /* 49812 2026 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49836 2027 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49860 2028 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49886 2029 */ {0x0008, 0x8802}, + /* 49911 2030 */ {0x004a, 0x8801}, + /* 49936 2031 */ {0x0084, 0x8800}, + /* 49959 2032 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49983 2033 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 50007 2034 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 50033 2035 */ {0x0008, 0x8802}, + /* 50058 2036 */ {0x004b, 0x8801}, + /* 50083 2037 */ {0x0084, 0x8800}, + /* 50106 2038 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* --------------------------------------- */ + /* 50132 2039 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 50157 2040 */ {0x0000, 0x8701}, + /* CKx1 clock delay adj */ + /* 50182 2041 */ {0x0000, 0x8701}, + /* CKx1 clock delay adj */ + /* 50207 2042 */ {0x0001, 0x870c}, + /* CKOx2 output */ + /* --------------------------------------- */ + /* 50232 2043 */ {0x0080, 0x8600}, + /* Line memory read counter (L) */ + /* 50257 2044 */ {0x0001, 0x8606}, + /* reserved */ + /* 50282 2045 */ {0x0064, 0x8607}, + /* Line memory read counter (H) 0x6480=25,728 */ + /* 50307 2046 */ {0x002a, 0x8601}, + /* CDSP sharp interpolation mode, + * line sel for color sep, edge enhance enab */ + /* 50332 2047 */ {0x0000, 0x8602}, + /* optical black level for user settng = 0 */ + /* 50357 2048 */ {0x0080, 0x8600}, + /* Line memory read counter (L) */ + /* 50382 2049 */ {0x000a, 0x8603}, + /* optical black level calc mode: auto; optical black offset = 10 */ + /* 50407 2050 */ {0x00df, 0x865b}, + /* Horiz offset for valid pixels (L)=0xdf */ + /* 50432 2051 */ {0x0012, 0x865c}, + /* Vert offset for valid lines (L)=0x12 */ + +/* The following two lines seem to be the "wrong" resolution. */ +/* But perhaps these indicate the actual size of the sensor */ +/* rather than the size of the current video mode. */ + /* 50457 2052 */ {0x0058, 0x865d}, + /* Horiz valid pixels (*4) (L) = 352 */ + /* 50482 2053 */ {0x0048, 0x865e}, + /* Vert valid lines (*4) (L) = 288 */ + + /* 50507 2054 */ {0x0015, 0x8608}, + /* A11 Coef ... */ + /* 50532 2055 */ {0x0030, 0x8609}, + /* 50557 2056 */ {0x00fb, 0x860a}, + /* 50582 2057 */ {0x003e, 0x860b}, + /* 50607 2058 */ {0x00ce, 0x860c}, + /* 50632 2059 */ {0x00f4, 0x860d}, + /* 50657 2060 */ {0x00eb, 0x860e}, + /* 50682 2061 */ {0x00dc, 0x860f}, + /* 50707 2062 */ {0x0039, 0x8610}, + /* 50732 2063 */ {0x0001, 0x8611}, + /* R offset for white balance ... */ + /* 50757 2064 */ {0x0000, 0x8612}, + /* 50782 2065 */ {0x0001, 0x8613}, + /* 50807 2066 */ {0x0000, 0x8614}, + /* 50832 2067 */ {0x005b, 0x8651}, + /* R gain for white balance ... */ + /* 50857 2068 */ {0x0040, 0x8652}, + /* 50882 2069 */ {0x0060, 0x8653}, + /* 50907 2070 */ {0x0040, 0x8654}, + /* 50932 2071 */ {0x0000, 0x8655}, + /* 50957 2072 */ {0x0001, 0x863f}, + /* Fixed gamma correction enable, USB control, + * lum filter disable, lum noise clip disable */ + /* 50982 2073 */ {0x00a1, 0x8656}, + /* Window1 size 256x256, Windows2 size 64x64, + * gamma look-up disable, new edge enhancement enable */ + /* 51007 2074 */ {0x0018, 0x8657}, + /* Edge gain high thresh */ + /* 51032 2075 */ {0x0020, 0x8658}, + /* Edge gain low thresh */ + /* 51057 2076 */ {0x000a, 0x8659}, + /* Edge bandwidth high threshold */ + /* 51082 2077 */ {0x0005, 0x865a}, + /* Edge bandwidth low threshold */ + /* -------------------------------- */ + /* 51107 2078 */ {0x0030, 0x8112}, + /* Video drop enable, ISO streaming enable */ + /* 51130 2079 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51154 2080 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51180 2081 */ {0xa908, 0x8802}, + /* 51205 2082 */ {0x0034, 0x8801}, + /* SSI reg addr */ + /* 51230 2083 */ {0x00ca, 0x8800}, + /* SSI data to write */ + /* 51253 2084 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51277 2085 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51301 2086 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51327 2087 */ {0x1f08, 0x8802}, + /* 51352 2088 */ {0x0006, 0x8801}, + /* 51377 2089 */ {0x0080, 0x8800}, + /* 51400 2090 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + +/* ----- Read back coefs we wrote earlier. */ + /* 51424 2091 */ /* READ { 0, 0x0000, 0x8608 } -> 0000: 15 */ + /* 51448 2092 */ /* READ { 0, 0x0000, 0x8609 } -> 0000: 30 */ + /* 51472 2093 */ /* READ { 0, 0x0000, 0x860a } -> 0000: fb */ + /* 51496 2094 */ /* READ { 0, 0x0000, 0x860b } -> 0000: 3e */ + /* 51520 2095 */ /* READ { 0, 0x0000, 0x860c } -> 0000: ce */ + /* 51544 2096 */ /* READ { 0, 0x0000, 0x860d } -> 0000: f4 */ + /* 51568 2097 */ /* READ { 0, 0x0000, 0x860e } -> 0000: eb */ + /* 51592 2098 */ /* READ { 0, 0x0000, 0x860f } -> 0000: dc */ + /* 51616 2099 */ /* READ { 0, 0x0000, 0x8610 } -> 0000: 39 */ + /* 51640 2100 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51664 2101 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51690 2102 */ {0xb008, 0x8802}, + /* 51715 2103 */ {0x0006, 0x8801}, + /* 51740 2104 */ {0x007d, 0x8800}, + /* 51763 2105 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + + + /* This chunk is seemingly redundant with */ + /* earlier commands (A11 Coef...), but if I disable it, */ + /* the image appears too dark. Maybe there was some kind of */ + /* reset since the earlier commands, so this is necessary again. */ + /* 51789 2106 */ {0x0015, 0x8608}, + /* 51814 2107 */ {0x0030, 0x8609}, + /* 51839 2108 */ {0xfffb, 0x860a}, + /* 51864 2109 */ {0x003e, 0x860b}, + /* 51889 2110 */ {0xffce, 0x860c}, + /* 51914 2111 */ {0xfff4, 0x860d}, + /* 51939 2112 */ {0xffeb, 0x860e}, + /* 51964 2113 */ {0xffdc, 0x860f}, + /* 51989 2114 */ {0x0039, 0x8610}, + /* 52014 2115 */ {0x0018, 0x8657}, + + /* 52039 2116 */ {0x0000, 0x8508}, + /* Disable compression. */ + /* Previous line was: + * 52039 2116 * { 0, 0x0021, 0x8508 }, * Enable compression. */ + /* 52064 2117 */ {0x0032, 0x850b}, + /* compression stuff */ + /* 52089 2118 */ {0x0003, 0x8509}, + /* compression stuff */ + /* 52114 2119 */ {0x0011, 0x850a}, + /* compression stuff */ + /* 52139 2120 */ {0x0021, 0x850d}, + /* compression stuff */ + /* 52164 2121 */ {0x0010, 0x850c}, + /* compression stuff */ + /* 52189 2122 */ {0x0003, 0x8500}, + /* *** Video mode: 160x120 */ + /* 52214 2123 */ {0x0001, 0x8501}, + /* Hardware-dominated snap control */ + /* 52239 2124 */ {0x0061, 0x8656}, + /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, new edge enhancement enable */ + /* 52264 2125 */ {0x0018, 0x8617}, + /* Window1 start X (*2) */ + /* 52289 2126 */ {0x0008, 0x8618}, + /* Window1 start Y (*2) */ + /* 52314 2127 */ {0x0061, 0x8656}, + /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, new edge enhancement enable */ + /* 52339 2128 */ {0x0058, 0x8619}, + /* Window2 start X (*2) */ + /* 52364 2129 */ {0x0008, 0x861a}, + /* Window2 start Y (*2) */ + /* 52389 2130 */ {0x00ff, 0x8615}, + /* High lum thresh for white balance */ + /* 52414 2131 */ {0x0000, 0x8616}, + /* Low lum thresh for white balance */ + /* 52439 2132 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 52464 2133 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 52487 2134 */ /* READ { 0, 0x0000, 0x8656 } -> 0000: 61 */ + /* 52513 2135 */ {0x0028, 0x8802}, + /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* 52536 2136 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52560 2137 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 52586 2138 */ {0x1f28, 0x8802}, + /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* 52611 2139 */ {0x0010, 0x8801}, + /* SSI reg addr */ + /* 52636 2140 */ {0x003e, 0x8800}, + /* SSI data to write */ + /* 52659 2141 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52685 2142 */ {0x0028, 0x8802}, + /* 52708 2143 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52732 2144 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 52758 2145 */ {0x1f28, 0x8802}, + /* 52783 2146 */ {0x0000, 0x8801}, + /* 52808 2147 */ {0x001f, 0x8800}, + /* 52831 2148 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52857 2149 */ {0x0001, 0x8602}, + /* optical black level for user settning = 1 */ + + /* Original: */ + /* 52882 2150 */ {0x0023, 0x8700}, + /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ + /* 52907 2151 */ {0x000f, 0x8602}, + /* optical black level for user settning = 15 */ + + /* 52932 2152 */ {0x0028, 0x8802}, + /* 52955 2153 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52979 2154 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 53005 2155 */ {0x1f28, 0x8802}, + /* 53030 2156 */ {0x0010, 0x8801}, + /* 53055 2157 */ {0x007b, 0x8800}, + /* 53078 2158 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 53104 2159 */ {0x002f, 0x8651}, + /* R gain for white balance ... */ + /* 53129 2160 */ {0x0080, 0x8653}, + /* 53152 2161 */ /* READ { 0, 0x0000, 0x8655 } -> 0000: 00 */ + /* 53178 2162 */ {0x0000, 0x8655}, + + /* 53203 2163 */ {0x0030, 0x8112}, + /* Video drop enable, ISO streaming enable */ + /* 53228 2164 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ + /* 53252 2165 */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ + {0, 0} +}; + + +/* + * Initialization data for Intel EasyPC Camera CS110 + */ +static __u16 spca508cs110_init_data[][3] = { + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8111}, /* Normal operation on reset */ + {0x0090, 0x8110}, + /* External Clock 2x & Synchronous Serial Interface Output */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + /* Initial sequence Synchronous Serial Interface */ + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ + + {0x0001, 0x8801}, + {0x000a, 0x8805},/* a - NWG: Dunno what this is about */ + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ + {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ + + {0x0006, 0x8660}, /* Nibble data + input order */ + + {0x000a, 0x8602}, /* Optical black level set to 0x0a */ +/* 1945 */ {0x0000, 0x8603}, /* Optical black level Offset */ + +/* 1962 * {0, 0x0000, 0x8611}, * 0 R Offset for white Balance */ +/* 1963 * {0, 0x0000, 0x8612}, * 1 Gr Offset for white Balance */ +/* 1964 * {0, 0x0000, 0x8613}, * 1f B Offset for white Balance */ +/* 1965 * {0, 0x0000, 0x8614}, * f0 Gb Offset for white Balance */ + + {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ + {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ + {0x0035, 0x8653}, /* 26 RED gain for white balance */ + {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ + {0x0041, 0x863f}, + /* Fixed Gamma correction enabled (makes colours look better) */ + +/* 2422 */ {0x0000, 0x8655}, + /* High bits for white balance*****brightness control*** */ + {} +}; + +static __u16 spca508_sightcam_init_data[][3] = { +/* This line seems to setup the frame/canvas */ + /*368 */ {0x000f, 0x8402}, + +/* Theese 6 lines are needed to startup the webcam */ + /*398 */ {0x0090, 0x8110}, + /*399 */ {0x0001, 0x8114}, + /*400 */ {0x0001, 0x8114}, + /*401 */ {0x0001, 0x8114}, + /*402 */ {0x0003, 0x8114}, + /*403 */ {0x0080, 0x8804}, + +/* This part seems to make the pictures darker? (autobrightness?) */ + /*436 */ {0x0001, 0x8801}, + /*437 */ {0x0004, 0x8800}, + /*439 */ {0x0003, 0x8801}, + /*440 */ {0x00e0, 0x8800}, + /*442 */ {0x0004, 0x8801}, + /*443 */ {0x00b4, 0x8800}, + /*445 */ {0x0005, 0x8801}, + /*446 */ {0x0000, 0x8800}, + + /*448 */ {0x0006, 0x8801}, + /*449 */ {0x00e0, 0x8800}, + /*451 */ {0x0007, 0x8801}, + /*452 */ {0x000c, 0x8800}, + +/* This section is just needed, it probably + * does something like the previous section, + * but the cam won't start if it's not included. + */ + /*484 */ {0x0014, 0x8801}, + /*485 */ {0x0008, 0x8800}, + /*487 */ {0x0015, 0x8801}, + /*488 */ {0x0067, 0x8800}, + /*490 */ {0x0016, 0x8801}, + /*491 */ {0x0000, 0x8800}, + /*493 */ {0x0017, 0x8801}, + /*494 */ {0x0020, 0x8800}, + /*496 */ {0x0018, 0x8801}, + /*497 */ {0x0044, 0x8800}, + +/* Makes the picture darker - and the + * cam won't start if not included + */ + /*505 */ {0x001e, 0x8801}, + /*506 */ {0x00ea, 0x8800}, + /*508 */ {0x001f, 0x8801}, + /*509 */ {0x0001, 0x8800}, + /*511 */ {0x0003, 0x8801}, + /*512 */ {0x00e0, 0x8800}, + +/* seems to place the colors ontop of each other #1 */ + /*517 */ {0x0006, 0x8704}, + /*518 */ {0x0001, 0x870c}, + /*519 */ {0x0016, 0x8600}, + /*520 */ {0x0002, 0x8606}, + +/* if not included the pictures becomes _very_ dark */ + /*521 */ {0x0064, 0x8607}, + /*522 */ {0x003a, 0x8601}, + /*523 */ {0x0000, 0x8602}, + +/* seems to place the colors ontop of each other #2 */ + /*524 */ {0x0016, 0x8600}, + /*525 */ {0x0018, 0x8617}, + /*526 */ {0x0008, 0x8618}, + /*527 */ {0x00a1, 0x8656}, + +/* webcam won't start if not included */ + /*528 */ {0x0007, 0x865b}, + /*529 */ {0x0001, 0x865c}, + /*530 */ {0x0058, 0x865d}, + /*531 */ {0x0048, 0x865e}, + +/* adjusts the colors */ + /*541 */ {0x0049, 0x8651}, + /*542 */ {0x0040, 0x8652}, + /*543 */ {0x004c, 0x8653}, + /*544 */ {0x0040, 0x8654}, + + {0, 0} +}; + +static __u16 spca508_sightcam2_init_data[][3] = { +/* 35 */ {0x0020, 0x8112}, + +/* 36 */ {0x000f, 0x8402}, +/* 37 */ {0x0000, 0x8403}, + +/* 38 */ {0x0008, 0x8201}, +/* 39 */ {0x0008, 0x8200}, +/* 40 */ {0x0001, 0x8200}, +/* 43 */ {0x0009, 0x8201}, +/* 44 */ {0x0008, 0x8200}, +/* 45 */ {0x0001, 0x8200}, +/* 48 */ {0x000a, 0x8201}, +/* 49 */ {0x0008, 0x8200}, +/* 50 */ {0x0001, 0x8200}, +/* 53 */ {0x000b, 0x8201}, +/* 54 */ {0x0008, 0x8200}, +/* 55 */ {0x0001, 0x8200}, +/* 58 */ {0x000c, 0x8201}, +/* 59 */ {0x0008, 0x8200}, +/* 60 */ {0x0001, 0x8200}, +/* 63 */ {0x000d, 0x8201}, +/* 64 */ {0x0008, 0x8200}, +/* 65 */ {0x0001, 0x8200}, +/* 68 */ {0x000e, 0x8201}, +/* 69 */ {0x0008, 0x8200}, +/* 70 */ {0x0001, 0x8200}, +/* 73 */ {0x0007, 0x8201}, +/* 74 */ {0x0008, 0x8200}, +/* 75 */ {0x0001, 0x8200}, +/* 78 */ {0x000f, 0x8201}, +/* 79 */ {0x0008, 0x8200}, +/* 80 */ {0x0001, 0x8200}, + +/* 84 */ {0x0018, 0x8660}, +/* 85 */ {0x0010, 0x8201}, + +/* 86 */ {0x0008, 0x8200}, +/* 87 */ {0x0001, 0x8200}, +/* 90 */ {0x0011, 0x8201}, +/* 91 */ {0x0008, 0x8200}, +/* 92 */ {0x0001, 0x8200}, + +/* 95 */ {0x0000, 0x86b0}, +/* 96 */ {0x0034, 0x86b1}, +/* 97 */ {0x0000, 0x86b2}, +/* 98 */ {0x0049, 0x86b3}, +/* 99 */ {0x0000, 0x86b4}, +/* 100 */ {0x0000, 0x86b4}, + +/* 101 */ {0x0012, 0x8201}, +/* 102 */ {0x0008, 0x8200}, +/* 103 */ {0x0001, 0x8200}, +/* 106 */ {0x0013, 0x8201}, +/* 107 */ {0x0008, 0x8200}, +/* 108 */ {0x0001, 0x8200}, + +/* 111 */ {0x0001, 0x86b0}, +/* 112 */ {0x00aa, 0x86b1}, +/* 113 */ {0x0000, 0x86b2}, +/* 114 */ {0x00e4, 0x86b3}, +/* 115 */ {0x0000, 0x86b4}, +/* 116 */ {0x0000, 0x86b4}, + +/* 118 */ {0x0018, 0x8660}, + +/* 119 */ {0x0090, 0x8110}, +/* 120 */ {0x0001, 0x8114}, +/* 121 */ {0x0001, 0x8114}, +/* 122 */ {0x0001, 0x8114}, +/* 123 */ {0x0003, 0x8114}, + +/* 124 */ {0x0080, 0x8804}, +/* 157 */ {0x0003, 0x8801}, +/* 158 */ {0x0012, 0x8800}, +/* 160 */ {0x0004, 0x8801}, +/* 161 */ {0x0005, 0x8800}, +/* 163 */ {0x0005, 0x8801}, +/* 164 */ {0x0000, 0x8800}, +/* 166 */ {0x0006, 0x8801}, +/* 167 */ {0x0000, 0x8800}, +/* 169 */ {0x0007, 0x8801}, +/* 170 */ {0x0000, 0x8800}, +/* 172 */ {0x0008, 0x8801}, +/* 173 */ {0x0005, 0x8800}, +/* 175 */ {0x000a, 0x8700}, +/* 176 */ {0x000e, 0x8801}, +/* 177 */ {0x0004, 0x8800}, +/* 179 */ {0x0005, 0x8801}, +/* 180 */ {0x0047, 0x8800}, +/* 182 */ {0x0006, 0x8801}, +/* 183 */ {0x0000, 0x8800}, +/* 185 */ {0x0007, 0x8801}, +/* 186 */ {0x00c0, 0x8800}, +/* 188 */ {0x0008, 0x8801}, +/* 189 */ {0x0003, 0x8800}, +/* 191 */ {0x0013, 0x8801}, +/* 192 */ {0x0001, 0x8800}, +/* 194 */ {0x0009, 0x8801}, +/* 195 */ {0x0000, 0x8800}, +/* 197 */ {0x000a, 0x8801}, +/* 198 */ {0x0000, 0x8800}, +/* 200 */ {0x000b, 0x8801}, +/* 201 */ {0x0000, 0x8800}, +/* 203 */ {0x000c, 0x8801}, +/* 204 */ {0x0000, 0x8800}, +/* 206 */ {0x000e, 0x8801}, +/* 207 */ {0x0004, 0x8800}, +/* 209 */ {0x000f, 0x8801}, +/* 210 */ {0x0000, 0x8800}, +/* 212 */ {0x0010, 0x8801}, +/* 213 */ {0x0006, 0x8800}, +/* 215 */ {0x0011, 0x8801}, +/* 216 */ {0x0006, 0x8800}, +/* 218 */ {0x0012, 0x8801}, +/* 219 */ {0x0000, 0x8800}, +/* 221 */ {0x0013, 0x8801}, +/* 222 */ {0x0001, 0x8800}, + +/* 224 */ {0x000a, 0x8700}, +/* 225 */ {0x0000, 0x8702}, +/* 226 */ {0x0000, 0x8703}, +/* 227 */ {0x00c2, 0x8704}, +/* 228 */ {0x0001, 0x870c}, + +/* 229 */ {0x0044, 0x8600}, +/* 230 */ {0x0002, 0x8606}, +/* 231 */ {0x0064, 0x8607}, +/* 232 */ {0x003a, 0x8601}, +/* 233 */ {0x0008, 0x8602}, +/* 234 */ {0x0044, 0x8600}, +/* 235 */ {0x0018, 0x8617}, +/* 236 */ {0x0008, 0x8618}, +/* 237 */ {0x00a1, 0x8656}, +/* 238 */ {0x0004, 0x865b}, +/* 239 */ {0x0002, 0x865c}, +/* 240 */ {0x0058, 0x865d}, +/* 241 */ {0x0048, 0x865e}, +/* 242 */ {0x0012, 0x8608}, +/* 243 */ {0x002c, 0x8609}, +/* 244 */ {0x0002, 0x860a}, +/* 245 */ {0x002c, 0x860b}, +/* 246 */ {0x00db, 0x860c}, +/* 247 */ {0x00f9, 0x860d}, +/* 248 */ {0x00f1, 0x860e}, +/* 249 */ {0x00e3, 0x860f}, +/* 250 */ {0x002c, 0x8610}, +/* 251 */ {0x006c, 0x8651}, +/* 252 */ {0x0041, 0x8652}, +/* 253 */ {0x0059, 0x8653}, +/* 254 */ {0x0040, 0x8654}, +/* 255 */ {0x00fa, 0x8611}, +/* 256 */ {0x00ff, 0x8612}, +/* 257 */ {0x00f8, 0x8613}, +/* 258 */ {0x0000, 0x8614}, +/* 259 */ {0x0001, 0x863f}, +/* 260 */ {0x0000, 0x8640}, +/* 261 */ {0x0026, 0x8641}, +/* 262 */ {0x0045, 0x8642}, +/* 263 */ {0x0060, 0x8643}, +/* 264 */ {0x0075, 0x8644}, +/* 265 */ {0x0088, 0x8645}, +/* 266 */ {0x009b, 0x8646}, +/* 267 */ {0x00b0, 0x8647}, +/* 268 */ {0x00c5, 0x8648}, +/* 269 */ {0x00d2, 0x8649}, +/* 270 */ {0x00dc, 0x864a}, +/* 271 */ {0x00e5, 0x864b}, +/* 272 */ {0x00eb, 0x864c}, +/* 273 */ {0x00f0, 0x864d}, +/* 274 */ {0x00f6, 0x864e}, +/* 275 */ {0x00fa, 0x864f}, +/* 276 */ {0x00ff, 0x8650}, +/* 277 */ {0x0060, 0x8657}, +/* 278 */ {0x0010, 0x8658}, +/* 279 */ {0x0018, 0x8659}, +/* 280 */ {0x0005, 0x865a}, +/* 281 */ {0x0018, 0x8660}, +/* 282 */ {0x0003, 0x8509}, +/* 283 */ {0x0011, 0x850a}, +/* 284 */ {0x0032, 0x850b}, +/* 285 */ {0x0010, 0x850c}, +/* 286 */ {0x0021, 0x850d}, +/* 287 */ {0x0001, 0x8500}, +/* 288 */ {0x0000, 0x8508}, +/* 289 */ {0x0012, 0x8608}, +/* 290 */ {0x002c, 0x8609}, +/* 291 */ {0x0002, 0x860a}, +/* 292 */ {0x0039, 0x860b}, +/* 293 */ {0x00d0, 0x860c}, +/* 294 */ {0x00f7, 0x860d}, +/* 295 */ {0x00ed, 0x860e}, +/* 296 */ {0x00db, 0x860f}, +/* 297 */ {0x0039, 0x8610}, +/* 298 */ {0x0012, 0x8657}, +/* 299 */ {0x000c, 0x8619}, +/* 300 */ {0x0004, 0x861a}, +/* 301 */ {0x00a1, 0x8656}, +/* 302 */ {0x00c8, 0x8615}, +/* 303 */ {0x0032, 0x8616}, + +/* 306 */ {0x0030, 0x8112}, +/* 313 */ {0x0020, 0x8112}, +/* 314 */ {0x0020, 0x8112}, +/* 315 */ {0x000f, 0x8402}, +/* 316 */ {0x0000, 0x8403}, + +/* 317 */ {0x0090, 0x8110}, +/* 318 */ {0x0001, 0x8114}, +/* 319 */ {0x0001, 0x8114}, +/* 320 */ {0x0001, 0x8114}, +/* 321 */ {0x0003, 0x8114}, +/* 322 */ {0x0080, 0x8804}, + +/* 355 */ {0x0003, 0x8801}, +/* 356 */ {0x0012, 0x8800}, +/* 358 */ {0x0004, 0x8801}, +/* 359 */ {0x0005, 0x8800}, +/* 361 */ {0x0005, 0x8801}, +/* 362 */ {0x0047, 0x8800}, +/* 364 */ {0x0006, 0x8801}, +/* 365 */ {0x0000, 0x8800}, +/* 367 */ {0x0007, 0x8801}, +/* 368 */ {0x00c0, 0x8800}, +/* 370 */ {0x0008, 0x8801}, +/* 371 */ {0x0003, 0x8800}, +/* 373 */ {0x000a, 0x8700}, +/* 374 */ {0x000e, 0x8801}, +/* 375 */ {0x0004, 0x8800}, +/* 377 */ {0x0005, 0x8801}, +/* 378 */ {0x0047, 0x8800}, +/* 380 */ {0x0006, 0x8801}, +/* 381 */ {0x0000, 0x8800}, +/* 383 */ {0x0007, 0x8801}, +/* 384 */ {0x00c0, 0x8800}, +/* 386 */ {0x0008, 0x8801}, +/* 387 */ {0x0003, 0x8800}, +/* 389 */ {0x0013, 0x8801}, +/* 390 */ {0x0001, 0x8800}, +/* 392 */ {0x0009, 0x8801}, +/* 393 */ {0x0000, 0x8800}, +/* 395 */ {0x000a, 0x8801}, +/* 396 */ {0x0000, 0x8800}, +/* 398 */ {0x000b, 0x8801}, +/* 399 */ {0x0000, 0x8800}, +/* 401 */ {0x000c, 0x8801}, +/* 402 */ {0x0000, 0x8800}, +/* 404 */ {0x000e, 0x8801}, +/* 405 */ {0x0004, 0x8800}, +/* 407 */ {0x000f, 0x8801}, +/* 408 */ {0x0000, 0x8800}, +/* 410 */ {0x0010, 0x8801}, +/* 411 */ {0x0006, 0x8800}, +/* 413 */ {0x0011, 0x8801}, +/* 414 */ {0x0006, 0x8800}, +/* 416 */ {0x0012, 0x8801}, +/* 417 */ {0x0000, 0x8800}, +/* 419 */ {0x0013, 0x8801}, +/* 420 */ {0x0001, 0x8800}, +/* 422 */ {0x000a, 0x8700}, +/* 423 */ {0x0000, 0x8702}, +/* 424 */ {0x0000, 0x8703}, +/* 425 */ {0x00c2, 0x8704}, +/* 426 */ {0x0001, 0x870c}, +/* 427 */ {0x0044, 0x8600}, +/* 428 */ {0x0002, 0x8606}, +/* 429 */ {0x0064, 0x8607}, +/* 430 */ {0x003a, 0x8601}, +/* 431 */ {0x0008, 0x8602}, +/* 432 */ {0x0044, 0x8600}, +/* 433 */ {0x0018, 0x8617}, +/* 434 */ {0x0008, 0x8618}, +/* 435 */ {0x00a1, 0x8656}, +/* 436 */ {0x0004, 0x865b}, +/* 437 */ {0x0002, 0x865c}, +/* 438 */ {0x0058, 0x865d}, +/* 439 */ {0x0048, 0x865e}, +/* 440 */ {0x0012, 0x8608}, +/* 441 */ {0x002c, 0x8609}, +/* 442 */ {0x0002, 0x860a}, +/* 443 */ {0x002c, 0x860b}, +/* 444 */ {0x00db, 0x860c}, +/* 445 */ {0x00f9, 0x860d}, +/* 446 */ {0x00f1, 0x860e}, +/* 447 */ {0x00e3, 0x860f}, +/* 448 */ {0x002c, 0x8610}, +/* 449 */ {0x006c, 0x8651}, +/* 450 */ {0x0041, 0x8652}, +/* 451 */ {0x0059, 0x8653}, +/* 452 */ {0x0040, 0x8654}, +/* 453 */ {0x00fa, 0x8611}, +/* 454 */ {0x00ff, 0x8612}, +/* 455 */ {0x00f8, 0x8613}, +/* 456 */ {0x0000, 0x8614}, +/* 457 */ {0x0001, 0x863f}, +/* 458 */ {0x0000, 0x8640}, +/* 459 */ {0x0026, 0x8641}, +/* 460 */ {0x0045, 0x8642}, +/* 461 */ {0x0060, 0x8643}, +/* 462 */ {0x0075, 0x8644}, +/* 463 */ {0x0088, 0x8645}, +/* 464 */ {0x009b, 0x8646}, +/* 465 */ {0x00b0, 0x8647}, +/* 466 */ {0x00c5, 0x8648}, +/* 467 */ {0x00d2, 0x8649}, +/* 468 */ {0x00dc, 0x864a}, +/* 469 */ {0x00e5, 0x864b}, +/* 470 */ {0x00eb, 0x864c}, +/* 471 */ {0x00f0, 0x864d}, +/* 472 */ {0x00f6, 0x864e}, +/* 473 */ {0x00fa, 0x864f}, +/* 474 */ {0x00ff, 0x8650}, +/* 475 */ {0x0060, 0x8657}, +/* 476 */ {0x0010, 0x8658}, +/* 477 */ {0x0018, 0x8659}, +/* 478 */ {0x0005, 0x865a}, +/* 479 */ {0x0018, 0x8660}, +/* 480 */ {0x0003, 0x8509}, +/* 481 */ {0x0011, 0x850a}, +/* 482 */ {0x0032, 0x850b}, +/* 483 */ {0x0010, 0x850c}, +/* 484 */ {0x0021, 0x850d}, +/* 485 */ {0x0001, 0x8500}, +/* 486 */ {0x0000, 0x8508}, + +/* 487 */ {0x0012, 0x8608}, +/* 488 */ {0x002c, 0x8609}, +/* 489 */ {0x0002, 0x860a}, +/* 490 */ {0x0039, 0x860b}, +/* 491 */ {0x00d0, 0x860c}, +/* 492 */ {0x00f7, 0x860d}, +/* 493 */ {0x00ed, 0x860e}, +/* 494 */ {0x00db, 0x860f}, +/* 495 */ {0x0039, 0x8610}, +/* 496 */ {0x0012, 0x8657}, +/* 497 */ {0x0064, 0x8619}, + +/* This line starts it all, it is not needed here */ +/* since it has been build into the driver */ +/* jfm: don't start now */ +/* 590 * {0x0030, 0x8112}, */ + {} +}; + +/* + * Initialization data for Creative Webcam Vista + */ +static __u16 spca508_vista_init_data[][3] = { + {0x0008, 0x8200}, /* Clear register */ + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8110}, /* Disable everything */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, + + {0x0003, 0x8111}, + {0x0000, 0x8111}, + {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ + {0x0020, 0x8112}, + {0x0000, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ + {0x0020, 0x8801}, /* Register address for SSI read/write */ + {0x0044, 0x8805}, /* DATA2 */ + {0x0004, 0x8800}, /* DATA1 -> write triggered */ + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x003c, 0x8801}, + {0x0001, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0001, 0x8801}, + {0x000a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x000e, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0028, 0x8801}, + {0x002e, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0039, 0x8801}, + {0x0013, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x003b, 0x8801}, + {0x000c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0035, 0x8801}, + {0x0028, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + {0x0050, 0x8703}, + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x870C}, /* Select CKOx2 output */ + {0x009A, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x0023, 0x8601}, + {0x0010, 0x8602}, + {0x000A, 0x8603}, + {0x009A, 0x8600}, + {0x0001, 0x865B}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865C}, /* Vertical offset for valid lines (L) */ + {0x0058, 0x865D}, /* Horizontal valid pixels window (L) */ + {0x0048, 0x865E}, /* Vertical valid lines window (L) */ + {0x0000, 0x865F}, + + {0x0006, 0x8660}, + /* Enable nibble data input, select nibble input order */ + + {0x0013, 0x8608}, /* A11 Coeficients for color correction */ + {0x0028, 0x8609}, + /* Note: these values are confirmed at the end of array */ + {0x0005, 0x860A}, /* ... */ + {0x0025, 0x860B}, + {0x00E1, 0x860C}, + {0x00FA, 0x860D}, + {0x00F4, 0x860E}, + {0x00E8, 0x860F}, + {0x0025, 0x8610}, /* A33 Coef. */ + {0x00FC, 0x8611}, /* White balance offset: R */ + {0x0001, 0x8612}, /* White balance offset: Gr */ + {0x00FE, 0x8613}, /* White balance offset: B */ + {0x0000, 0x8614}, /* White balance offset: Gb */ + + {0x0064, 0x8651}, /* R gain for white balance (L) */ + {0x0040, 0x8652}, /* Gr gain for white balance (L) */ + {0x0066, 0x8653}, /* B gain for white balance (L) */ + {0x0040, 0x8654}, /* Gb gain for white balance (L) */ + {0x0001, 0x863F}, /* Enable fixed gamma correction */ + + {0x00A1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ + /* UV division: UV no change, Enable New edge enhancement */ + {0x0018, 0x8657}, /* Edge gain high threshold */ + {0x0020, 0x8658}, /* Edge gain low threshold */ + {0x000A, 0x8659}, /* Edge bandwidth high threshold */ + {0x0005, 0x865A}, /* Edge bandwidth low threshold */ + {0x0064, 0x8607}, /* UV filter enable */ + + {0x0016, 0x8660}, + {0x0000, 0x86B0}, /* Bad pixels compensation address */ + {0x00DC, 0x86B1}, /* X coord for bad pixels compensation (L) */ + {0x0000, 0x86B2}, + {0x0009, 0x86B3}, /* Y coord for bad pixels compensation (L) */ + {0x0000, 0x86B4}, + + {0x0001, 0x86B0}, + {0x00F5, 0x86B1}, + {0x0000, 0x86B2}, + {0x00C6, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0002, 0x86B0}, + {0x001C, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D7, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0003, 0x86B0}, + {0x001C, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D8, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0004, 0x86B0}, + {0x001D, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D8, 0x86B3}, + {0x0000, 0x86B4}, + {0x001E, 0x8660}, + + /* READ { 0, 0x0000, 0x8608 } -> + 0000: 13 */ + /* READ { 0, 0x0000, 0x8609 } -> + 0000: 28 */ + /* READ { 0, 0x0000, 0x8610 } -> + 0000: 05 */ + /* READ { 0, 0x0000, 0x8611 } -> + 0000: 25 */ + /* READ { 0, 0x0000, 0x8612 } -> + 0000: e1 */ + /* READ { 0, 0x0000, 0x8613 } -> + 0000: fa */ + /* READ { 0, 0x0000, 0x8614 } -> + 0000: f4 */ + /* READ { 0, 0x0000, 0x8615 } -> + 0000: e8 */ + /* READ { 0, 0x0000, 0x8616 } -> + 0000: 25 */ + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x", + index, value); + if (ret < 0) + PDEBUG(D_ERR|D_USBO, "reg write: error %d", ret); + return ret; +} + +/* read 1 byte */ +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 index) /* wIndex */ +{ + int ret; + __u8 data; + + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* register */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16) 0, /* value */ + index, + &data, 1, + 500); /* timeout */ + PDEBUG(D_USBI, "reg read i:%04x --> %02x", index, data); + if (ret < 0) { + PDEBUG(D_ERR|D_USBI, "reg_read err %d", ret); + return ret; + } + return data; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][1] != 0) { + ret = reg_write(dev, data[i][1], data[i][0]); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor; + __u16 product; + int data1, data2; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x4018: */ + sd->subtype = CreativeVista; +/* break; */ +/* } */ + break; + case 0x0461: /* MicroInnovation */ +/* switch (product) { */ +/* case 0x0815: */ + sd->subtype = MicroInnovationIC200; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ +/* switch (product) { */ +/* case 0x110: */ + sd->subtype = ViewQuestVQ110; +/* break; */ +/* } */ + break; + case 0x0af9: /* Hama cameras */ + switch (product) { + case 0x0010: + sd->subtype = HamaUSBSightcam; + break; + case 0x0011: + sd->subtype = HamaUSBSightcam2; + break; + } + break; + case 0x8086: /* Intel */ +/* switch (product) { */ +/* case 0x0110: */ + sd->subtype = IntelEasyPCCamera; +/* break; */ +/* } */ + break; + } + + /* Read from global register the USB product and vendor IDs, just to */ + /* prove that we can communicate with the device. This works, which */ + /* confirms at we are communicating properly and that the device */ + /* is a 508. */ + data1 = reg_read(dev, 0x8104); + data2 = reg_read(dev, 0x8105); + PDEBUG(D_PROBE, + "Read from GLOBAL: USB Vendor ID 0x%02x%02x", data2, data1); + + data1 = reg_read(dev, 0x8106); + data2 = reg_read(dev, 0x8107); + PDEBUG(D_PROBE, + "Read from GLOBAL: USB Product ID 0x%02x%02x", data2, data1); + + data1 = reg_read(dev, 0x8621); + PDEBUG(D_PROBE, + "Read from GLOBAL: Window 1 average luminance %d", data1); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + + switch (sd->subtype) { + case ViewQuestVQ110: + if (write_vector(gspca_dev, spca508_init_data)) + return -1; + break; + default: +/* case MicroInnovationIC200: */ +/* case IntelEasyPCCamera: */ + if (write_vector(gspca_dev, spca508cs110_init_data)) + return -1; + break; + case HamaUSBSightcam: + if (write_vector(gspca_dev, spca508_sightcam_init_data)) + return -1; + break; + case HamaUSBSightcam2: + if (write_vector(gspca_dev, spca508_sightcam2_init_data)) + return -1; + break; + case CreativeVista: + if (write_vector(gspca_dev, spca508_vista_init_data)) + return -1; + break; + } + return 0; /* success */ +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ +/* write_vector(gspca_dev, spca508_open_data); */ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + reg_write(gspca_dev->dev, 0x8500, mode); + switch (mode) { + case 0: + case 1: + reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */ + break; + default: +/* case 2: */ +/* case 3: */ + reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */ + break; + } + reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Video ISO disable, Video Drop Packet enable: */ + reg_write(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +/* convert YUVY per line to YUYV (YUV 4:2:2) */ +static void yuvy_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + Ui = yi + width; + Vi = Ui + width / 2; + yi1 = Vi + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yuvy_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA508_OFFSET_DATA; + len -= SPCA508_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 brightness = sd->brightness; + +/* MX seem contrast */ + reg_write(gspca_dev->dev, 0x8651, brightness); + reg_write(gspca_dev->dev, 0x8652, brightness); + reg_write(gspca_dev->dev, 0x8653, brightness); + reg_write(gspca_dev->dev, 0x8654, brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = reg_read(gspca_dev->dev, 0x8651); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4018), DVNM("Creative Webcam Vista (PD1100)")}, + {USB_DEVICE(0x0461, 0x0815), DVNM("Micro Innovation IC200")}, + {USB_DEVICE(0x0733, 0x0110), DVNM("ViewQuest VQ110")}, + {USB_DEVICE(0x0af9, 0x0010), DVNM("Hama USB Sightcam 100")}, + {USB_DEVICE(0x0af9, 0x0011), DVNM("Hama USB Sightcam 100")}, + {USB_DEVICE(0x8086, 0x0110), DVNM("Intel Easy PC Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c new file mode 100644 index 000000000000..a94e6270115e --- /dev/null +++ b/drivers/media/video/gspca/spca561.c @@ -0,0 +1,1025 @@ +/* + * Sunplus spca561 subdriver + * + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca561" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 autogain; + + __u8 chip_revision; + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 32, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x3fff, + .step = 1, + .default_value = 0x2000, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_AUTOGAIN 2 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SPCA561, 160, 120, 3}, + {V4L2_PIX_FMT_SPCA561, 176, 144, 2}, + {V4L2_PIX_FMT_SPCA561, 320, 240, 1}, + {V4L2_PIX_FMT_SPCA561, 352, 288, 0}, +}; + +/* + * Initialization data + * I'm not very sure how to split initialization from open data + * chunks. For now, we'll consider everything as initialization + */ +/* Frame packet header offsets for the spca561 */ +#define SPCA561_OFFSET_SNAP 1 +#define SPCA561_OFFSET_TYPE 2 +#define SPCA561_OFFSET_COMPRESS 3 +#define SPCA561_OFFSET_FRAMSEQ 4 +#define SPCA561_OFFSET_GPIO 5 +#define SPCA561_OFFSET_USBBUFF 6 +#define SPCA561_OFFSET_WIN2GRAVE 7 +#define SPCA561_OFFSET_WIN2RAVE 8 +#define SPCA561_OFFSET_WIN2BAVE 9 +#define SPCA561_OFFSET_WIN2GBAVE 10 +#define SPCA561_OFFSET_WIN1GRAVE 11 +#define SPCA561_OFFSET_WIN1RAVE 12 +#define SPCA561_OFFSET_WIN1BAVE 13 +#define SPCA561_OFFSET_WIN1GBAVE 14 +#define SPCA561_OFFSET_FREQ 15 +#define SPCA561_OFFSET_VSYNC 16 +#define SPCA561_OFFSET_DATA 1 +#define SPCA561_INDEX_I2C_BASE 0x8800 +#define SPCA561_SNAPBIT 0x20 +#define SPCA561_SNAPCTRL 0x40 +enum { + Rev072A = 0, + Rev012A, +}; + +static void reg_w_val(struct usb_device *dev, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); +} + +static void write_vector(struct gspca_dev *gspca_dev, __u16 data[][2]) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + + i = 0; + while (data[i][1] != 0) { + reg_w_val(dev, data[i][1], data[i][0]); + i++; + } +} + +static void reg_r(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void reg_w_buf(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void i2c_init(struct gspca_dev *gspca_dev, __u8 mode) +{ + reg_w_val(gspca_dev->dev, 0x92, 0x8804); + reg_w_val(gspca_dev->dev, mode, 0x8802); +} + +static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) +{ + int retry = 60; + __u8 DataLow; + __u8 DataHight; + __u8 Data; + + DataLow = valeur; + DataHight = valeur >> 8; + reg_w_val(gspca_dev->dev, reg, 0x8801); + reg_w_val(gspca_dev->dev, DataLow, 0x8805); + reg_w_val(gspca_dev->dev, DataHight, 0x8800); + while (retry--) { + reg_r(gspca_dev->dev, 0x8803, &Data, 1); + if (!Data) + break; + } +} + +static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) +{ + int retry = 60; + __u8 value; + __u8 vallsb; + __u8 Data; + + reg_w_val(gspca_dev->dev, 0x92, 0x8804); + reg_w_val(gspca_dev->dev, reg, 0x8801); + reg_w_val(gspca_dev->dev, (mode | 0x01), 0x8802); + while (retry--) { + reg_r(gspca_dev->dev, 0x8803, &Data, 1); + if (!Data) + break; + } + if (retry == 0) + return -1; + reg_r(gspca_dev->dev, 0x8800, &value, 1); + reg_r(gspca_dev->dev, 0x8805, &vallsb, 1); + return ((int) value << 8) | vallsb; +} + +static __u16 spca561_init_data[][2] = { + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset */ + {0x0003, 0x8701}, /* PCLK clock delay adjustment */ + {0x0001, 0x8703}, /* HSYNC from cmos inverted */ + {0x0011, 0x8118}, /* Enable and conf sensor */ + {0x0001, 0x8118}, /* Conf sensor */ + {0x0092, 0x8804}, /* I know nothing about these */ + {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ + /***************/ + {0x000d, 0x8805}, /* sensor default setting */ + {0x0001, 0x8801}, /* 1 <- 0x0d */ + {0x0000, 0x8800}, + {0x0018, 0x8805}, + {0x0002, 0x8801}, /* 2 <- 0x18 */ + {0x0000, 0x8800}, + {0x0065, 0x8805}, + {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */ + {0x0001, 0x8800}, + {0x0021, 0x8805}, + {0x0005, 0x8801}, /* 5 <- 0x21 */ + {0x0000, 0x8800}, + {0x00aa, 0x8805}, + {0x0007, 0x8801}, /* 7 <- 0xaa */ + {0x0000, 0x8800}, + {0x0004, 0x8805}, + {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */ + {0x0015, 0x8800}, + {0x0002, 0x8805}, + {0x0039, 0x8801}, /* 0x39 <- 0x02 */ + {0x0000, 0x8800}, + {0x0010, 0x8805}, + {0x0035, 0x8801}, /* 0x35 <- 0x10 */ + {0x0000, 0x8800}, + {0x0049, 0x8805}, + {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */ + {0x0010, 0x8800}, + {0x000b, 0x8805}, + {0x0028, 0x8801}, /* 0x28 <- 0x0b */ + {0x0000, 0x8800}, + {0x000f, 0x8805}, + {0x003b, 0x8801}, /* 0x3b <- 0x0f */ + {0x0000, 0x8800}, + {0x0000, 0x8805}, + {0x003c, 0x8801}, /* 0x3c <- 0x00 */ + {0x0000, 0x8800}, + /***************/ + {0x0018, 0x8601}, /* Pixel/line selection for color separation */ + {0x0000, 0x8602}, /* Optical black level for user setting */ + {0x0060, 0x8604}, /* Optical black horizontal offset */ + {0x0002, 0x8605}, /* Optical black vertical offset */ + {0x0000, 0x8603}, /* Non-automatic optical black level */ + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ + {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ + {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ + {0x00e0, 0x8406}, /* Memory buffer threshold */ + {0x0000, 0x8660}, /* Compensation memory stuff */ + {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ + {0x0007, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ + {0x0010, 0x8660}, /* Compensation memory stuff */ + {0x0018, 0x8660}, /* Compensation memory stuff */ + + {0x0004, 0x8611}, /* R offset for white balance */ + {0x0004, 0x8612}, /* Gr offset for white balance */ + {0x0007, 0x8613}, /* B offset for white balance */ + {0x0000, 0x8614}, /* Gb offset for white balance */ + {0x008c, 0x8651}, /* R gain for white balance */ + {0x008c, 0x8652}, /* Gr gain for white balance */ + {0x00b5, 0x8653}, /* B gain for white balance */ + {0x008c, 0x8654}, /* Gb gain for white balance */ + {0x0002, 0x8502}, /* Maximum average bit rate stuff */ + + {0x0011, 0x8802}, + {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ + {0x0081, 0x8702}, /* Master clock output enable */ + + {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ + /* Originally was 0x0010 (352x288 compression) */ + + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0003, 0x865c}, /* Vertical offset for valid lines */ + /***************//* sensor active */ + {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */ + {0x0021, 0x8805}, + {0x0001, 0x8800}, + {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */ + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0005, 0x8801}, /* 0x05 <- 0x2f */ + {0x002f, 0x8805}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, /* 0x06 <- 0 */ + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, /* 0x0a <- 2 */ + {0x0002, 0x8805}, + {0x0000, 0x8800}, + {0x0009, 0x8801}, /* 0x09 <- 0x1061 */ + {0x0061, 0x8805}, + {0x0010, 0x8800}, + {0x0035, 0x8801}, /* 0x35 <-0x14 */ + {0x0014, 0x8805}, + {0x0000, 0x8800}, + {0x0030, 0x8112}, /* ISO and drop packet enable */ + {0x0000, 0x8112}, /* Some kind of reset ???? */ + {0x0009, 0x8118}, /* Enable sensor and set standby */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset ??? */ + {0x0003, 0x8701}, + {0x0001, 0x8703}, + {0x0011, 0x8118}, + {0x0001, 0x8118}, + /***************/ + {0x0092, 0x8804}, + {0x0010, 0x8802}, + {0x000d, 0x8805}, + {0x0001, 0x8801}, + {0x0000, 0x8800}, + {0x0018, 0x8805}, + {0x0002, 0x8801}, + {0x0000, 0x8800}, + {0x0065, 0x8805}, + {0x0004, 0x8801}, + {0x0001, 0x8800}, + {0x0021, 0x8805}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + {0x00aa, 0x8805}, + {0x0007, 0x8801}, /* mode 0xaa */ + {0x0000, 0x8800}, + {0x0004, 0x8805}, + {0x0020, 0x8801}, + {0x0015, 0x8800}, /* mode 0x0415 */ + {0x0002, 0x8805}, + {0x0039, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8805}, + {0x0035, 0x8801}, + {0x0000, 0x8800}, + {0x0049, 0x8805}, + {0x0009, 0x8801}, + {0x0010, 0x8800}, + {0x000b, 0x8805}, + {0x0028, 0x8801}, + {0x0000, 0x8800}, + {0x000f, 0x8805}, + {0x003b, 0x8801}, + {0x0000, 0x8800}, + {0x0000, 0x8805}, + {0x003c, 0x8801}, + {0x0000, 0x8800}, + {0x0002, 0x8502}, + {0x0039, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + + {0x0087, 0x8700}, /* overwrite by start */ + {0x0081, 0x8702}, + {0x0000, 0x8500}, +/* {0x0010, 0x8500}, -- Previous line was this */ + {0x0002, 0x865b}, + {0x0003, 0x865c}, + /***************/ + {0x0003, 0x8801}, /* 0x121-> 289 */ + {0x0021, 0x8805}, + {0x0001, 0x8800}, + {0x0004, 0x8801}, /* 0x165 -> 357 */ + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0005, 0x8801}, /* 0x2f //blanking control colonne */ + {0x002f, 0x8805}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, /* 0x00 //blanking mode row */ + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, /* 0x01 //0x02 */ + {0x0001, 0x8805}, + {0x0000, 0x8800}, + {0x0009, 0x8801}, /* 0x1061 - setexposure times && pixel clock + * 0001 0 | 000 0110 0001 */ + {0x0061, 0x8805}, /* 61 31 */ + {0x0008, 0x8800}, /* 08 */ + {0x0035, 0x8801}, /* 0x14 - set gain general */ + {0x001f, 0x8805}, /* 0x14 */ + {0x0000, 0x8800}, + {0x0030, 0x8112}, + {} +}; + +static void sensor_reset(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8631, 0xc8); + reg_w_val(gspca_dev->dev, 0x8634, 0xc8); + reg_w_val(gspca_dev->dev, 0x8112, 0x00); + reg_w_val(gspca_dev->dev, 0x8114, 0x00); + reg_w_val(gspca_dev->dev, 0x8118, 0x21); + i2c_init(gspca_dev, 0x14); + i2c_write(gspca_dev, 1, 0x0d); + i2c_write(gspca_dev, 0, 0x0d); +} + +/******************** QC Express etch2 stuff ********************/ +static __u16 Pb100_1map8300[][2] = { + /* reg, value */ + {0x8320, 0x3304}, + + {0x8303, 0x0125}, /* image area */ + {0x8304, 0x0169}, + {0x8328, 0x000b}, + {0x833c, 0x0001}, + + {0x832f, 0x0419}, + {0x8307, 0x00aa}, + {0x8301, 0x0003}, + {0x8302, 0x000e}, + {} +}; +static __u16 Pb100_2map8300[][2] = { + /* reg, value */ + {0x8339, 0x0000}, + {0x8307, 0x00aa}, + {} +}; + +static __u16 spca561_161rev12A_data1[][2] = { + {0x21, 0x8118}, + {0x01, 0x8114}, + {0x00, 0x8112}, + {0x92, 0x8804}, + {0x04, 0x8802}, /* windows uses 08 */ + {} +}; +static __u16 spca561_161rev12A_data2[][2] = { + {0x21, 0x8118}, + {0x10, 0x8500}, + {0x07, 0x8601}, + {0x07, 0x8602}, + {0x04, 0x8501}, + {0x21, 0x8118}, + + {0x07, 0x8201}, /* windows uses 02 */ + {0x08, 0x8200}, + {0x01, 0x8200}, + + {0x00, 0x8114}, + {0x01, 0x8114}, /* windows uses 00 */ + + {0x90, 0x8604}, + {0x00, 0x8605}, + {0xb0, 0x8603}, + + /* sensor gains */ + {0x00, 0x8610}, /* *red */ + {0x00, 0x8611}, /* 3f *green */ + {0x00, 0x8612}, /* green *blue */ + {0x00, 0x8613}, /* blue *green */ + {0x35, 0x8614}, /* green *red */ + {0x35, 0x8615}, /* 40 *green */ + {0x35, 0x8616}, /* 7a *blue */ + {0x35, 0x8617}, /* 40 *green */ + + {0x0c, 0x8620}, /* 0c */ + {0xc8, 0x8631}, /* c8 */ + {0xc8, 0x8634}, /* c8 */ + {0x23, 0x8635}, /* 23 */ + {0x1f, 0x8636}, /* 1f */ + {0xdd, 0x8637}, /* dd */ + {0xe1, 0x8638}, /* e1 */ + {0x1d, 0x8639}, /* 1d */ + {0x21, 0x863a}, /* 21 */ + {0xe3, 0x863b}, /* e3 */ + {0xdf, 0x863c}, /* df */ + {0xf0, 0x8505}, + {0x32, 0x850a}, + {} +}; + +static void sensor_mapwrite(struct gspca_dev *gspca_dev, + __u16 sensormap[][2]) +{ + int i = 0; + __u8 usbval[2]; + + while (sensormap[i][0]) { + usbval[0] = sensormap[i][1]; + usbval[1] = sensormap[i][1] >> 8; + reg_w_buf(gspca_dev->dev, sensormap[i][0], usbval, 2); + i++; + } +} +static void init_161rev12A(struct gspca_dev *gspca_dev) +{ + sensor_reset(gspca_dev); + write_vector(gspca_dev, spca561_161rev12A_data1); + sensor_mapwrite(gspca_dev, Pb100_1map8300); + write_vector(gspca_dev, spca561_161rev12A_data2); + sensor_mapwrite(gspca_dev, Pb100_2map8300); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor, product; + __u8 data1, data2; + + /* Read frm global register the USB product and vendor IDs, just to + * prove that we can communicate with the device. This works, which + * confirms at we are communicating properly and that the device + * is a 561. */ + reg_r(dev, 0x8104, &data1, 1); + reg_r(dev, 0x8105, &data2, 1); + vendor = (data2 << 8) | data1; + reg_r(dev, 0x8106, &data1, 1); + reg_r(dev, 0x8107, &data2, 1); + product = (data2 << 8) | data1; + if (vendor != id->idVendor || product != id->idProduct) { + PDEBUG(D_PROBE, "Bad vendor / product from device"); + return -EINVAL; + } + switch (product) { + case 0x0928: + case 0x0929: + case 0x092a: + case 0x092b: + case 0x092c: + case 0x092d: + case 0x092e: + case 0x092f: + case 0x403b: + sd->chip_revision = Rev012A; + break; + default: +/* case 0x0561: + case 0x0815: * ?? in spca508.c + case 0x401a: + case 0x7004: + case 0x7e50: + case 0xa001: + case 0xcdee: */ + sd->chip_revision = Rev072A; + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->chip_revision) { + case Rev072A: + PDEBUG(D_STREAM, "Chip revision id: 072a"); + write_vector(gspca_dev, spca561_init_data); + break; + default: +/* case Rev012A: */ + PDEBUG(D_STREAM, "Chip revision id: 012a"); + init_161rev12A(gspca_dev); + break; + } + return 0; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 lowb; + int expotimes; + + switch (sd->chip_revision) { + case Rev072A: + lowb = sd->contrast >> 8; + reg_w_val(dev, lowb, 0x8651); + reg_w_val(dev, lowb, 0x8652); + reg_w_val(dev, lowb, 0x8653); + reg_w_val(dev, lowb, 0x8654); + break; + case Rev012A: { + __u8 Reg8391[] = + { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00 }; + + /* Write camera sensor settings */ + expotimes = (sd->contrast >> 5) & 0x07ff; + Reg8391[0] = expotimes & 0xff; /* exposure */ + Reg8391[1] = 0x18 | (expotimes >> 8); + Reg8391[2] = sd->brightness; /* gain */ + reg_w_buf(dev, 0x8391, Reg8391, 8); + reg_w_buf(dev, 0x8390, Reg8391, 8); + break; + } + } +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int Clck; + __u8 Reg8307[] = { 0xaa, 0x00 }; + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + switch (sd->chip_revision) { + case Rev072A: + switch (mode) { + default: +/* case 0: + case 1: */ + Clck = 0x25; + break; + case 2: + Clck = 0x22; + break; + case 3: + Clck = 0x21; + break; + } + reg_w_val(dev, 0x8500, mode); /* mode */ + reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(dev, 0x8112, 0x10 | 0x20); + break; + default: +/* case Rev012A: */ + switch (mode) { + case 0: + case 1: + Clck = 0x8a; + break; + case 2: + Clck = 0x85; + break; + default: + Clck = 0x83; + break; + } + if (mode <= 1) { + /* Use compression on 320x240 and above */ + reg_w_val(dev, 0x8500, 0x10 | mode); + } else { + /* I couldn't get the compression to work below 320x240 + * Fortunately at these resolutions the bandwidth + * is sufficient to push raw frames at ~20fps */ + reg_w_val(dev, 0x8500, mode); + } /* -- qq@kuku.eu.org */ + reg_w_buf(dev, 0x8307, Reg8307, 2); + reg_w_val(dev, 0x8700, Clck); /* 0x8f 0x85 0x27 clock */ + reg_w_val(dev, 0x8112, 0x1e | 0x20); + reg_w_val(dev, 0x850b, 0x03); + setcontrast(gspca_dev); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8114, 0); +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int expotimes = 0; + int pixelclk = 0; + int gainG = 0; + __u8 R, Gr, Gb, B; + int y; + __u8 luma_mean = 110; + __u8 luma_delta = 20; + __u8 spring = 4; + + switch (sd->chip_revision) { + case Rev072A: + reg_r(gspca_dev->dev, 0x8621, &Gr, 1); + reg_r(gspca_dev->dev, 0x8622, &R, 1); + reg_r(gspca_dev->dev, 0x8623, &B, 1); + reg_r(gspca_dev->dev, 0x8624, &Gb, 1); + y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; + /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ + /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ + /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */ + + if (y < luma_mean - luma_delta || + y > luma_mean + luma_delta) { + expotimes = i2c_read(gspca_dev, 0x09, 0x10); + pixelclk = 0x0800; + expotimes = expotimes & 0x07ff; + /* PDEBUG(D_PACK, + "Exposition Times 0x%03X Clock 0x%04X ", + expotimes,pixelclk); */ + gainG = i2c_read(gspca_dev, 0x35, 0x10); + /* PDEBUG(D_PACK, + "reading Gain register %d", gainG); */ + + expotimes += (luma_mean - y) >> spring; + gainG += (luma_mean - y) / 50; + /* PDEBUG(D_PACK, + "compute expotimes %d gain %d", + expotimes,gainG); */ + + if (gainG > 0x3f) + gainG = 0x3f; + else if (gainG < 4) + gainG = 3; + i2c_write(gspca_dev, gainG, 0x35); + + if (expotimes >= 0x0256) + expotimes = 0x0256; + else if (expotimes < 4) + expotimes = 3; + i2c_write(gspca_dev, expotimes | pixelclk, 0x09); + } + break; + case Rev012A: + /* sensor registers is access and memory mapped to 0x8300 */ + /* readind all 0x83xx block the sensor */ + /* + * The data from the header seem wrong where is the luma + * and chroma mean value + * at the moment set exposure in contrast set + */ + break; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + if (sd->ag_cnt >= 0) { + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev); + } + } + data += SPCA561_OFFSET_DATA; + len -= SPCA561_OFFSET_DATA; + if (data[1] & 0x10) { + /* compressed bayer */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + } else { + /*fixme: which format?*/ + data += 20; + len -= 20; + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + } + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + + switch (sd->chip_revision) { + case Rev072A: + value = sd->brightness; + reg_w_val(gspca_dev->dev, value, 0x8611); + reg_w_val(gspca_dev->dev, value, 0x8612); + reg_w_val(gspca_dev->dev, value, 0x8613); + reg_w_val(gspca_dev->dev, value, 0x8614); + break; + default: +/* case Rev012A: */ + setcontrast(gspca_dev); + break; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + __u16 tot; + + switch (sd->chip_revision) { + case Rev072A: + tot = 0; + reg_r(gspca_dev->dev, 0x8611, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8612, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8613, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8614, &value, 1); + tot += value; + sd->brightness = tot >> 2; + break; + default: +/* case Rev012A: */ + /* no way to read sensor settings */ + break; + } +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + __u16 tot; + + switch (sd->chip_revision) { + case Rev072A: + tot = 0; + reg_r(gspca_dev->dev, 0x8651, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8652, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8653, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8654, &value, 1); + tot += value; + sd->contrast = tot << 6; + break; + default: +/* case Rev012A: */ + /* no way to read sensor settings */ + break; + } + PDEBUG(D_CONF, "get contrast %d", sd->contrast); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401a), DVNM("Creative Webcam Vista (PD1100)")}, + {USB_DEVICE(0x041e, 0x403b), DVNM("Creative Webcam Vista (VF0010)")}, + {USB_DEVICE(0x0458, 0x7004), DVNM("Genius VideoCAM Express V2")}, + {USB_DEVICE(0x046d, 0x0928), DVNM("Logitech QC Express Etch2")}, + {USB_DEVICE(0x046d, 0x0929), DVNM("Labtec Webcam Elch2")}, + {USB_DEVICE(0x046d, 0x092a), DVNM("Logitech QC for Notebook")}, + {USB_DEVICE(0x046d, 0x092b), DVNM("Labtec Webcam Plus")}, + {USB_DEVICE(0x046d, 0x092c), DVNM("Logitech QC chat Elch2")}, + {USB_DEVICE(0x046d, 0x092d), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x046d, 0x092e), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x046d, 0x092f), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x04fc, 0x0561), DVNM("Flexcam 100")}, + {USB_DEVICE(0x060b, 0xa001), DVNM("Maxell Compact Pc PM3")}, + {USB_DEVICE(0x10fd, 0x7e50), DVNM("FlyCam Usb 100")}, + {USB_DEVICE(0xabcd, 0xcdee), DVNM("Petcam")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index d8c203e99cd3..6832fe0f3403 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -23,8 +23,8 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 7) -static const char version[] = "0.2.7"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; MODULE_AUTHOR("Jean-Francois Moine "); MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); @@ -37,10 +37,10 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + unsigned char lightfreq; }; /* global parameters */ -static int lightfreq = 50; static int sd_quant = 7; /* <= 4 KO - 7: good (enough!) */ /* V4L2 controls supported by the driver */ @@ -50,6 +50,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { #define SD_BRIGHTNESS 0 @@ -94,6 +96,20 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, +#define SD_FREQ 3 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 1, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; static struct cam_mode vga_mode[] = { @@ -102,11 +118,11 @@ static struct cam_mode vga_mode[] = { }; /* -- read a register -- */ -static int reg_read(struct gspca_dev *gspca_dev, +static int reg_r(struct gspca_dev *gspca_dev, __u16 index, __u8 *buf) { - int ret; struct usb_device *dev = gspca_dev->dev; + int ret; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x00, @@ -116,12 +132,12 @@ static int reg_read(struct gspca_dev *gspca_dev, buf, 1, 500); if (ret < 0) - PDEBUG(D_ERR, "reg_read err %d", ret); + PDEBUG(D_ERR, "reg_r err %d", ret); return ret; } /* -- write a register -- */ -static int reg_write(struct gspca_dev *gspca_dev, +static int reg_w(struct gspca_dev *gspca_dev, __u16 index, __u16 value) { struct usb_device *dev = gspca_dev->dev; @@ -136,7 +152,7 @@ static int reg_write(struct gspca_dev *gspca_dev, 0, 500); if (ret < 0) - PDEBUG(D_ERR, "reg_write err %d", ret); + PDEBUG(D_ERR, "reg_w err %d", ret); return ret; } @@ -149,15 +165,15 @@ static int rcv_val(struct gspca_dev *gspca_dev, int alen, ret; unsigned char bulk_buf[4]; - reg_write(gspca_dev, 0x634, (ads >> 16) & 0xff); - reg_write(gspca_dev, 0x635, (ads >> 8) & 0xff); - reg_write(gspca_dev, 0x636, ads & 0xff); - reg_write(gspca_dev, 0x637, 0); - reg_write(gspca_dev, 0x638, len & 0xff); - reg_write(gspca_dev, 0x639, len >> 8); - reg_write(gspca_dev, 0x63a, 0); - reg_write(gspca_dev, 0x63b, 0); - reg_write(gspca_dev, 0x630, 5); + reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff); + reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x636, ads & 0xff); + reg_w(gspca_dev, 0x637, 0); + reg_w(gspca_dev, 0x638, len & 0xff); + reg_w(gspca_dev, 0x639, len >> 8); + reg_w(gspca_dev, 0x63a, 0); + reg_w(gspca_dev, 0x63b, 0); + reg_w(gspca_dev, 0x630, 5); if (len > sizeof bulk_buf) return -1; ret = usb_bulk_msg(dev, @@ -180,26 +196,26 @@ static int snd_val(struct gspca_dev *gspca_dev, unsigned char bulk_buf[4]; if (ads == 0x003f08) { - ret = reg_read(gspca_dev, 0x0704, &value); + ret = reg_r(gspca_dev, 0x0704, &value); if (ret < 0) goto ko; - ret = reg_read(gspca_dev, 0x0705, &seq); + ret = reg_r(gspca_dev, 0x0705, &seq); if (ret < 0) goto ko; - ret = reg_read(gspca_dev, 0x0650, &value); + ret = reg_r(gspca_dev, 0x0650, &value); if (ret < 0) goto ko; - reg_write(gspca_dev, 0x654, seq); + reg_w(gspca_dev, 0x654, seq); } else - reg_write(gspca_dev, 0x654, (ads >> 16) & 0xff); - reg_write(gspca_dev, 0x655, (ads >> 8) & 0xff); - reg_write(gspca_dev, 0x656, ads & 0xff); - reg_write(gspca_dev, 0x657, 0); - reg_write(gspca_dev, 0x658, 0x04); /* size */ - reg_write(gspca_dev, 0x659, 0); - reg_write(gspca_dev, 0x65a, 0); - reg_write(gspca_dev, 0x65b, 0); - reg_write(gspca_dev, 0x650, 5); + reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff); + reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x656, ads & 0xff); + reg_w(gspca_dev, 0x657, 0); + reg_w(gspca_dev, 0x658, 0x04); /* size */ + reg_w(gspca_dev, 0x659, 0); + reg_w(gspca_dev, 0x65a, 0); + reg_w(gspca_dev, 0x65b, 0); + reg_w(gspca_dev, 0x650, 5); bulk_buf[0] = (val >> 24) & 0xff; bulk_buf[1] = (val >> 16) & 0xff; bulk_buf[2] = (val >> 8) & 0xff; @@ -215,7 +231,7 @@ static int snd_val(struct gspca_dev *gspca_dev, if (ads == 0x003f08) { seq += 4; seq &= 0x3f; - reg_write(gspca_dev, 0x705, seq); + reg_w(gspca_dev, 0x705, seq); } return ret; ko: @@ -235,7 +251,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int parval; - PDEBUG(D_CONF, "brightness: %d", sd->brightness); parval = 0x06000000 /* whiteness */ + (sd->brightness << 16); set_par(gspca_dev, parval); @@ -246,7 +261,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int parval; - PDEBUG(D_CONF, "contrast: %d", sd->contrast); parval = 0x07000000 /* contrast */ + (sd->contrast << 16); set_par(gspca_dev, parval); @@ -257,13 +271,20 @@ static void setcolors(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int parval; - PDEBUG(D_CONF, "saturation: %d", - sd->colors); parval = 0x08000000 /* saturation */ + (sd->colors << 16); set_par(gspca_dev, parval); } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + set_par(gspca_dev, sd->lightfreq == 1 + ? 0x33640000 /* 50 Hz */ + : 0x33780000); /* 60 Hz */ +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -278,6 +299,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; return 0; } @@ -289,7 +311,7 @@ static int sd_open(struct gspca_dev *gspca_dev) /* check if the device responds */ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); - ret = reg_read(gspca_dev, 0x0740, &value); + ret = reg_r(gspca_dev, 0x0740, &value); if (ret < 0) return ret; if (value != 0xff) { @@ -320,21 +342,24 @@ static void sd_start(struct gspca_dev *gspca_dev) ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->alt); - if (ret < 0) + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed", + gspca_dev->iface, gspca_dev->alt); goto out; - ret = reg_read(gspca_dev, 0x0630, &dum); + } + ret = reg_r(gspca_dev, 0x0630, &dum); if (ret < 0) goto out; rcv_val(gspca_dev, 0x000020, 4); /* << (value ff ff ff ff) */ - ret = reg_read(gspca_dev, 0x0650, &dum); + ret = reg_r(gspca_dev, 0x0650, &dum); if (ret < 0) goto out; snd_val(gspca_dev, 0x000020, 0xffffffff); - reg_write(gspca_dev, 0x0620, 0); - reg_write(gspca_dev, 0x0630, 0); - reg_write(gspca_dev, 0x0640, 0); - reg_write(gspca_dev, 0x0650, 0); - reg_write(gspca_dev, 0x0660, 0); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); setbrightness(gspca_dev); /* whiteness */ setcontrast(gspca_dev); /* contrast */ setcolors(gspca_dev); /* saturation */ @@ -342,9 +367,7 @@ static void sd_start(struct gspca_dev *gspca_dev) set_par(gspca_dev, 0x0a800000); /* Green ? */ set_par(gspca_dev, 0x0b800000); /* Blue ? */ set_par(gspca_dev, 0x0d030000); /* Gamma ? */ - set_par(gspca_dev, lightfreq == 60 - ? 0x33780000 /* 60 Hz */ - : 0x33640000); /* 50 Hz */ + setfreq(gspca_dev); /* light frequency */ /* start the video flow */ set_par(gspca_dev, 0x01000000); @@ -363,15 +386,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev) set_par(gspca_dev, 0x02000000); set_par(gspca_dev, 0x02000000); usb_set_interface(dev, gspca_dev->iface, 1); - reg_read(gspca_dev, 0x0630, &value); + reg_r(gspca_dev, 0x0630, &value); rcv_val(gspca_dev, 0x000020, 4); /* << (value ff ff ff ff) */ - reg_read(gspca_dev, 0x0650, &value); + reg_r(gspca_dev, 0x0650, &value); snd_val(gspca_dev, 0x000020, 0xffffffff); - reg_write(gspca_dev, 0x0620, 0); - reg_write(gspca_dev, 0x0630, 0); - reg_write(gspca_dev, 0x0640, 0); - reg_write(gspca_dev, 0x0650, 0); - reg_write(gspca_dev, 0x0660, 0); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); PDEBUG(D_STREAM, "camera stopped"); } @@ -470,6 +493,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -482,6 +541,7 @@ static struct sd_desc sd_desc = { .stop0 = sd_stop0, .close = sd_close, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -524,7 +584,5 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); -module_param(lightfreq, int, 0644); -MODULE_PARM_DESC(lightfreq, "Light frequency 50 or 60 Hz"); module_param_named(quant, sd_quant, int, 0644); MODULE_PARM_DESC(quant, "Quantization index (0..8)"); diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c new file mode 100644 index 000000000000..52d1b32523b1 --- /dev/null +++ b/drivers/media/video/gspca/sunplus.c @@ -0,0 +1,1638 @@ +/* + * Sunplus spca504(abc) spca533 spca536 library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sunplus" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char packet[ISO_MAX_SIZE + 128]; + /* !! no more than 128 ff in an ISO packet */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char qindex; + char bridge; +#define BRIDGE_SPCA504 0 +#define BRIDGE_SPCA504B 1 +#define BRIDGE_SPCA504C 2 +#define BRIDGE_SPCA533 3 +#define BRIDGE_SPCA536 4 + char subtype; +#define AiptekMiniPenCam13 1 +#define LogitechClickSmart420 2 +#define LogitechClickSmart820 3 +#define MegapixV4 4 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x20, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x1a, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 640, 480, 1}, +}; + +static struct cam_mode custom_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 464, 480, 1}, +}; + +static struct cam_mode vga_mode2[] = { + {V4L2_PIX_FMT_JPEG, 176, 144, 4}, + {V4L2_PIX_FMT_JPEG, 320, 240, 3}, + {V4L2_PIX_FMT_JPEG, 352, 288, 2}, + {V4L2_PIX_FMT_JPEG, 640, 480, 1}, +}; + +#define SPCA50X_OFFSET_DATA 10 +#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 +#define SPCA504_PCCAM600_OFFSET_COMPRESS 4 +#define SPCA504_PCCAM600_OFFSET_MODE 5 +#define SPCA504_PCCAM600_OFFSET_DATA 14 + /* Frame packet header offsets for the spca533 */ +#define SPCA533_OFFSET_DATA 16 +#define SPCA533_OFFSET_FRAMSEQ 15 +/* Frame packet header offsets for the spca536 */ +#define SPCA536_OFFSET_DATA 4 +#define SPCA536_OFFSET_FRAMSEQ 1 + +/* Initialisation data for the Creative PC-CAM 600 */ +static __u16 spca504_pccam600_init_data[][3] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0154, 0x0008}, + {0x30, 0x0004, 0x0006}, + {0x30, 0x0258, 0x0009}, + {0x30, 0x0004, 0x0000}, + {0x30, 0x0093, 0x0004}, + {0x30, 0x0066, 0x0005}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {} +}; + +/* Creative PC-CAM 600 specific open data, sent before using the + * generic initialisation data from spca504_open_data. + */ +static __u16 spca504_pccam600_open_data[][3] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0500, 0x0001}, /* snapshot mode */ + {0x00, 0x0003, 0x2880}, + {0x00, 0x0001, 0x2881}, + {} +}; + +/* Initialisation data for the logitech clicksmart 420 */ +static __u16 spca504A_clicksmart420_init_data[][3] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0004, 0x000a}, + {0xb0, 0x0001, 0x0000}, + + + {0x0a1, 0x0080, 0x0001}, + {0x30, 0x0049, 0x0000}, + {0x30, 0x0060, 0x0005}, + {0x0c, 0x0004, 0x0000}, + {0x00, 0x0000, 0x0000}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0000, 0x2000}, + + {} +}; + +/* clicksmart 420 open data ? */ +static __u16 spca504A_clicksmart420_open_data[][3] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0502, 0x0000}, + {0x06, 0x0000, 0x0000}, + {0x00, 0x0004, 0x2880}, + {0x00, 0x0001, 0x2881}, +/* look like setting a qTable */ + {0x00, 0x0006, 0x2800}, + {0x00, 0x0004, 0x2801}, + {0x00, 0x0004, 0x2802}, + {0x00, 0x0006, 0x2803}, + {0x00, 0x000a, 0x2804}, + {0x00, 0x0010, 0x2805}, + {0x00, 0x0014, 0x2806}, + {0x00, 0x0018, 0x2807}, + {0x00, 0x0005, 0x2808}, + {0x00, 0x0005, 0x2809}, + {0x00, 0x0006, 0x280a}, + {0x00, 0x0008, 0x280b}, + {0x00, 0x000a, 0x280c}, + {0x00, 0x0017, 0x280d}, + {0x00, 0x0018, 0x280e}, + {0x00, 0x0016, 0x280f}, + + {0x00, 0x0006, 0x2810}, + {0x00, 0x0005, 0x2811}, + {0x00, 0x0006, 0x2812}, + {0x00, 0x000a, 0x2813}, + {0x00, 0x0010, 0x2814}, + {0x00, 0x0017, 0x2815}, + {0x00, 0x001c, 0x2816}, + {0x00, 0x0016, 0x2817}, + {0x00, 0x0006, 0x2818}, + {0x00, 0x0007, 0x2819}, + {0x00, 0x0009, 0x281a}, + {0x00, 0x000c, 0x281b}, + {0x00, 0x0014, 0x281c}, + {0x00, 0x0023, 0x281d}, + {0x00, 0x0020, 0x281e}, + {0x00, 0x0019, 0x281f}, + + {0x00, 0x0007, 0x2820}, + {0x00, 0x0009, 0x2821}, + {0x00, 0x000f, 0x2822}, + {0x00, 0x0016, 0x2823}, + {0x00, 0x001b, 0x2824}, + {0x00, 0x002c, 0x2825}, + {0x00, 0x0029, 0x2826}, + {0x00, 0x001f, 0x2827}, + {0x00, 0x000a, 0x2828}, + {0x00, 0x000e, 0x2829}, + {0x00, 0x0016, 0x282a}, + {0x00, 0x001a, 0x282b}, + {0x00, 0x0020, 0x282c}, + {0x00, 0x002a, 0x282d}, + {0x00, 0x002d, 0x282e}, + {0x00, 0x0025, 0x282f}, + + {0x00, 0x0014, 0x2830}, + {0x00, 0x001a, 0x2831}, + {0x00, 0x001f, 0x2832}, + {0x00, 0x0023, 0x2833}, + {0x00, 0x0029, 0x2834}, + {0x00, 0x0030, 0x2835}, + {0x00, 0x0030, 0x2836}, + {0x00, 0x0028, 0x2837}, + {0x00, 0x001d, 0x2838}, + {0x00, 0x0025, 0x2839}, + {0x00, 0x0026, 0x283a}, + {0x00, 0x0027, 0x283b}, + {0x00, 0x002d, 0x283c}, + {0x00, 0x0028, 0x283d}, + {0x00, 0x0029, 0x283e}, + {0x00, 0x0028, 0x283f}, + + {0x00, 0x0007, 0x2840}, + {0x00, 0x0007, 0x2841}, + {0x00, 0x000a, 0x2842}, + {0x00, 0x0013, 0x2843}, + {0x00, 0x0028, 0x2844}, + {0x00, 0x0028, 0x2845}, + {0x00, 0x0028, 0x2846}, + {0x00, 0x0028, 0x2847}, + {0x00, 0x0007, 0x2848}, + {0x00, 0x0008, 0x2849}, + {0x00, 0x000a, 0x284a}, + {0x00, 0x001a, 0x284b}, + {0x00, 0x0028, 0x284c}, + {0x00, 0x0028, 0x284d}, + {0x00, 0x0028, 0x284e}, + {0x00, 0x0028, 0x284f}, + + {0x00, 0x000a, 0x2850}, + {0x00, 0x000a, 0x2851}, + {0x00, 0x0016, 0x2852}, + {0x00, 0x0028, 0x2853}, + {0x00, 0x0028, 0x2854}, + {0x00, 0x0028, 0x2855}, + {0x00, 0x0028, 0x2856}, + {0x00, 0x0028, 0x2857}, + {0x00, 0x0013, 0x2858}, + {0x00, 0x001a, 0x2859}, + {0x00, 0x0028, 0x285a}, + {0x00, 0x0028, 0x285b}, + {0x00, 0x0028, 0x285c}, + {0x00, 0x0028, 0x285d}, + {0x00, 0x0028, 0x285e}, + {0x00, 0x0028, 0x285f}, + + {0x00, 0x0028, 0x2860}, + {0x00, 0x0028, 0x2861}, + {0x00, 0x0028, 0x2862}, + {0x00, 0x0028, 0x2863}, + {0x00, 0x0028, 0x2864}, + {0x00, 0x0028, 0x2865}, + {0x00, 0x0028, 0x2866}, + {0x00, 0x0028, 0x2867}, + {0x00, 0x0028, 0x2868}, + {0x00, 0x0028, 0x2869}, + {0x00, 0x0028, 0x286a}, + {0x00, 0x0028, 0x286b}, + {0x00, 0x0028, 0x286c}, + {0x00, 0x0028, 0x286d}, + {0x00, 0x0028, 0x286e}, + {0x00, 0x0028, 0x286f}, + + {0x00, 0x0028, 0x2870}, + {0x00, 0x0028, 0x2871}, + {0x00, 0x0028, 0x2872}, + {0x00, 0x0028, 0x2873}, + {0x00, 0x0028, 0x2874}, + {0x00, 0x0028, 0x2875}, + {0x00, 0x0028, 0x2876}, + {0x00, 0x0028, 0x2877}, + {0x00, 0x0028, 0x2878}, + {0x00, 0x0028, 0x2879}, + {0x00, 0x0028, 0x287a}, + {0x00, 0x0028, 0x287b}, + {0x00, 0x0028, 0x287c}, + {0x00, 0x0028, 0x287d}, + {0x00, 0x0028, 0x287e}, + {0x00, 0x0028, 0x287f}, + + {0xa0, 0x0000, 0x0503}, + {} +}; + +static unsigned char qtable_creative_pccam[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +/* FIXME: This Q-table is identical to the Creative PC-CAM one, + * except for one byte. Possibly a typo? + * NWG: 18/05/2003. + */ +static unsigned char qtable_spca504_default[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, + }, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static void spca5xxRegRead(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, + 500); +} + +static void spca5xxRegWrite(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, + 500); +} + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x", + reg, index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +static int reg_read_info(struct usb_device *dev, + __u16 value) /* wValue */ +{ + int ret; + __u8 data; + + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0x20, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, /* index */ + &data, 1, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read_info err %d", ret); + return 0; + } + return data; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + __u8 buf[2]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + buf, length, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Register write failed for 0x%x,0x%x,0x%x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, + unsigned int request, + unsigned int ybase, + unsigned int cbase, + unsigned char qtable[2][64]) +{ + struct usb_device *dev = gspca_dev->dev; + int i, err; + + /* loop over y components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, ybase + i, qtable[0][i]); + if (err < 0) + return err; + } + + /* loop over c components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, cbase + i, qtable[1][i]); + if (err < 0) + return err; + } + return 0; +} + +static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, + __u16 req, __u16 idx, __u16 val) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 notdone; + + reg_write(dev, req, idx, val); + notdone = reg_read(dev, 0x01, 0x0001, 1); + reg_write(dev, req, idx, val); + + PDEBUG(D_FRAM, "before wait 0x%x", notdone); + + msleep(200); + notdone = reg_read(dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "after wait 0x%x", notdone); +} + +static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, + __u16 req, + __u16 idx, __u16 val, __u8 stat, __u8 count) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 status; + __u8 endcode; + + reg_write(dev, req, idx, val); + status = reg_read(dev, 0x01, 0x0001, 1); + endcode = stat; + PDEBUG(D_FRAM, "Status 0x%x Need 0x%x", status, stat); + if (!count) + return; + count = 200; + while (--count > 0) { + msleep(10); + /* gsmart mini2 write a each wait setting 1 ms is enought */ +/* reg_write(dev, req, idx, val); */ + status = reg_read(dev, 0x01, 0x0001, 1); + if (status == endcode) { + PDEBUG(D_FRAM, "status 0x%x after wait 0x%x", + status, 200 - count); + break; + } + } +} + +static int spca504B_PollingDataReady(struct usb_device *dev) +{ + __u8 DataReady; + int count = 10; + + while (--count > 0) { + spca5xxRegRead(dev, 0x21, 0, &DataReady, 1); + if ((DataReady & 0x01) == 0) + break; + msleep(10); + } + return DataReady; +} + +static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 DataReady; + int count = 50; + + while (--count > 0) { + spca5xxRegRead(dev, 0x21, 1, &DataReady, 1); + + if (DataReady) { + DataReady = 0; + spca5xxRegWrite(dev, 0x21, 0, 1, &DataReady, 1); + spca5xxRegRead(dev, 0x21, 1, &DataReady, 1); + spca504B_PollingDataReady(dev); + break; + } + msleep(10); + } +} + +static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 FW[5]; + __u8 ProductInfo[64]; + + spca5xxRegRead(dev, 0x20, 0, FW, 5); + PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ", + FW[0], FW[1], FW[2], FW[3], FW[4]); + spca5xxRegRead(dev, 0x23, 0, ProductInfo, 64); + spca5xxRegRead(dev, 0x23, 1, ProductInfo, 64); +} + +static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 Size; + __u8 Type; + int rc; + + Size = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + Type = 0; + switch (sd->bridge) { + case BRIDGE_SPCA533: + spca5xxRegWrite(dev, 0x31, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(dev); + spca50x_GetFirmware(gspca_dev); + Type = 2; + spca5xxRegWrite(dev, 0x24, 0, 8, &Type, 1); + spca5xxRegRead(dev, 0x24, 8, &Type, 1); + + spca5xxRegWrite(dev, 0x25, 0, 4, &Size, 1); + spca5xxRegRead(dev, 0x25, 4, &Size, 1); + rc = spca504B_PollingDataReady(dev); + + /* Init the cam width height with some values get on init ? */ + spca5xxRegWrite(dev, 0x31, 0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(dev); + break; + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA536: */ + Type = 6; + spca5xxRegWrite(dev, 0x25, 0, 4, &Size, 1); + spca5xxRegRead(dev, 0x25, 4, &Size, 1); + spca5xxRegWrite(dev, 0x27, 0, 0, &Type, 1); + spca5xxRegRead(dev, 0x27, 0, &Type, 1); + rc = spca504B_PollingDataReady(dev); + break; + case BRIDGE_SPCA504: + Size += 3; + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, + 0x08, Size, 0, + 0x80 | (Size & 0x0f), 1); + spca504A_acknowledged_command(gspca_dev, + 1, 3, 0, 0x9f, 0); + } else { + spca504_acknowledged_command(gspca_dev, 0x08, Size, 0); + } + break; + case BRIDGE_SPCA504C: + /* capture mode */ + reg_write(dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x0); + reg_write(dev, 0x20, 0x01, 0x0500 | (Size & 0x0f)); + break; + } +} + +static void spca504_wait_status(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int cnt; + + cnt = 256; + while (--cnt > 0) { + /* With this we get the status, when return 0 it's all ok */ + if (reg_read(dev, 0x06, 0x00, 1) == 0) + return; + msleep(10); + } +} + +static void spca504B_setQtable(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 Data = 3; + + spca5xxRegWrite(dev, 0x26, 0, 0, &Data, 1); + spca5xxRegRead(dev, 0x26, 0, &Data, 1); + spca504B_PollingDataReady(dev); +} + +static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int pollreg = 1; + + switch (sd->bridge) { + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + pollreg = 0; + /* fall thru */ + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ + spca5xxRegWrite(dev, 0, 0, 0x21a7, NULL, 0); /* brightness */ + spca5xxRegWrite(dev, 0, 0x20, 0x21a8, NULL, 0); /* contrast */ + spca5xxRegWrite(dev, 0, 0, 0x21ad, NULL, 0); /* hue */ + spca5xxRegWrite(dev, 0, 1, 0x21ac, NULL, 0); /* sat/hue */ + spca5xxRegWrite(dev, 0, 0x20, 0x21ae, NULL, 0); /* saturation */ + spca5xxRegWrite(dev, 0, 0, 0x21a3, NULL, 0); /* gamma */ + break; + case BRIDGE_SPCA536: + spca5xxRegWrite(dev, 0, 0, 0x20f0, NULL, 0); + spca5xxRegWrite(dev, 0, 0x21, 0x20f1, NULL, 0); + spca5xxRegWrite(dev, 0, 0x40, 0x20f5, NULL, 0); + spca5xxRegWrite(dev, 0, 1, 0x20f4, NULL, 0); + spca5xxRegWrite(dev, 0, 0x40, 0x20f6, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x2089, NULL, 0); + break; + } + if (pollreg) + spca504B_PollingDataReady(dev); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor; + __u16 product; + __u8 fw; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x400b: */ +/* case 0x4012: */ +/* case 0x4013: */ +/* sd->bridge = BRIDGE_SPCA504C; */ +/* break; */ +/* } */ + break; + case 0x0458: /* Genius KYE cameras */ +/* switch (product) { */ +/* case 0x7006: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x046d: /* Logitech Labtec */ + switch (product) { + case 0x0905: + sd->subtype = LogitechClickSmart820; + sd->bridge = BRIDGE_SPCA533; + break; + case 0x0960: + sd->subtype = LogitechClickSmart420; + sd->bridge = BRIDGE_SPCA504C; + break; + } + break; + case 0x0471: /* Philips */ +/* switch (product) { */ +/* case 0x0322: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x04a5: /* Benq */ + switch (product) { + case 0x3003: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x3008: + case 0x300a: + sd->bridge = BRIDGE_SPCA533; + break; + } + break; + case 0x04f1: /* JVC */ +/* switch (product) { */ +/* case 0x1001: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x04fc: /* SunPlus */ + switch (product) { + case 0x500c: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x504a: +/* try to get the firmware as some cam answer 2.0.1.2.2 + * and should be a spca504b then overwrite that setting */ + spca5xxRegRead(dev, 0x20, 0, &fw, 1); + if (fw == 1) { + sd->subtype = AiptekMiniPenCam13; + sd->bridge = BRIDGE_SPCA504; + } else if (fw == 2) { + sd->bridge = BRIDGE_SPCA504B; + } else + return -ENODEV; + break; + case 0x504b: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x5330: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x5360: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xffff: + sd->bridge = BRIDGE_SPCA504B; + break; + } + break; + case 0x052b: /* ?? Megapix */ +/* switch (product) { */ +/* case 0x1513: */ + sd->subtype = MegapixV4; + sd->bridge = BRIDGE_SPCA533; +/* break; */ +/* } */ + break; + case 0x0546: /* Polaroid */ + switch (product) { + case 0x3155: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x3191: + case 0x3273: + sd->bridge = BRIDGE_SPCA504B; + break; + } + break; + case 0x055f: /* Mustek cameras */ + switch (product) { + case 0xc211: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xc230: + case 0xc232: + sd->bridge = BRIDGE_SPCA533; + break; + case 0xc360: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xc420: + sd->bridge = BRIDGE_SPCA504; + break; + case 0xc430: + case 0xc440: + sd->bridge = BRIDGE_SPCA533; + break; + case 0xc520: + sd->bridge = BRIDGE_SPCA504; + break; + case 0xc530: + case 0xc540: + case 0xc630: + case 0xc650: + sd->bridge = BRIDGE_SPCA533; + break; + } + break; + case 0x05da: /* Digital Dream cameras */ +/* switch (product) { */ +/* case 0x1018: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x06d6: /* Trust */ +/* switch (product) { */ +/* case 0x0031: */ + sd->bridge = BRIDGE_SPCA533; /* SPCA533A */ +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ + switch (product) { + case 0x1311: + case 0x1314: + case 0x2211: + case 0x2221: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x3261: + case 0x3281: + sd->bridge = BRIDGE_SPCA536; + break; + } + break; + case 0x08ca: /* Aiptek */ + switch (product) { + case 0x0104: + case 0x0106: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2008: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x2010: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2016: + case 0x2018: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x2020: + case 0x2022: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2024: + sd->bridge = BRIDGE_SPCA536; + break; + case 0x2028: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2040: + case 0x2042: + case 0x2060: + sd->bridge = BRIDGE_SPCA536; + break; + } + break; + case 0x0d64: /* SunPlus */ +/* switch (product) { */ +/* case 0x0303: */ + sd->bridge = BRIDGE_SPCA536; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA536: */ + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + break; + case BRIDGE_SPCA533: + cam->cam_mode = custom_mode; + cam->nmodes = sizeof custom_mode / sizeof custom_mode[0]; + break; + case BRIDGE_SPCA504C: + cam->cam_mode = vga_mode2; + cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0]; + break; + } + sd->qindex = 5; /* set the quantization table */ + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int rc; + __u8 Data; + __u8 i; + __u8 info[6]; + int err_code; + + switch (sd->bridge) { + case BRIDGE_SPCA504B: + spca5xxRegWrite(dev, 0x1d, 0, 0, NULL, 0); + spca5xxRegWrite(dev, 0, 1, 0x2306, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x0d04, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x2000, NULL, 0); + spca5xxRegWrite(dev, 0, 0x13, 0x2301, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x2306, NULL, 0); + /* fall thru */ + case BRIDGE_SPCA533: + rc = spca504B_PollingDataReady(dev); + spca50x_GetFirmware(gspca_dev); + break; + case BRIDGE_SPCA536: + spca50x_GetFirmware(gspca_dev); + spca5xxRegRead(dev, 0x00, 0x5002, &Data, 1); + Data = 0; + spca5xxRegWrite(dev, 0x24, 0, 0, &Data, 1); + spca5xxRegRead(dev, 0x24, 0, &Data, 1); + rc = spca504B_PollingDataReady(dev); + spca5xxRegWrite(dev, 0x34, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + break; + case BRIDGE_SPCA504C: /* pccam600 */ + PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)"); + reg_write(dev, 0xe0, 0x0000, 0x0000); + reg_write(dev, 0xe0, 0x0000, 0x0001); /* reset */ + spca504_wait_status(gspca_dev); + if (sd->subtype == LogitechClickSmart420) + write_vector(gspca_dev, + spca504A_clicksmart420_open_data); + else + write_vector(gspca_dev, spca504_pccam600_open_data); + err_code = spca50x_setup_qtable(gspca_dev, + 0x00, 0x2800, + 0x2840, qtable_creative_pccam); + if (err_code < 0) { + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed"); + return err_code; + } + break; + default: +/* case BRIDGE_SPCA504: */ + PDEBUG(D_STREAM, "Opening SPCA504"); + if (sd->subtype == AiptekMiniPenCam13) { + /*****************************/ + for (i = 0; i < 6; i++) + info[i] = reg_read_info(dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + /* spca504a aiptek */ + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequencial need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + /******************************/ + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); +/* reg_write (dev, 0, 0x2000, 0); */ +/* reg_write (dev, 0, 0x2883, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x08, + 6, 0, 0x86, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x24, + 0, 0, 0x9D, 1); */ + reg_write(dev, 0x0, 0x270c, 0x5); /* L92 sno1t.txt */ + reg_write(dev, 0x0, 0x2310, 0x5); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0, 0xff, 0); + } + /* setup qtable */ + reg_write(dev, 0, 0x2000, 0); + reg_write(dev, 0, 0x2883, 1); + err_code = spca50x_setup_qtable(gspca_dev, + 0x00, 0x2800, + 0x2840, + qtable_spca504_default); + if (err_code < 0) { + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + return err_code; + } + break; + } + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int rc; + int enable; + __u8 i; + __u8 info[6]; + + if (sd->bridge == BRIDGE_SPCA504B) + spca504B_setQtable(gspca_dev); + spca504B_SetSizeType(gspca_dev); + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ + if (sd->subtype == MegapixV4 || + sd->subtype == LogitechClickSmart820) { + spca5xxRegWrite(dev, 0xf0, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca5xxRegRead(dev, 0xf0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + } else { + spca5xxRegWrite(dev, 0x31, 0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(dev); + } + break; + case BRIDGE_SPCA504: + if (sd->subtype == AiptekMiniPenCam13) { + for (i = 0; i < 6; i++) + info[i] = reg_read_info(dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + /* spca504a aiptek */ + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequencial need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + for (i = 0; i < 6; i++) + info[i] = reg_read_info(dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + } + spca504B_SetSizeType(gspca_dev); + reg_write(dev, 0x0, 0x270c, 0x5); /* L92 sno1t.txt */ + reg_write(dev, 0x0, 0x2310, 0x5); + break; + case BRIDGE_SPCA504C: + if (sd->subtype == LogitechClickSmart420) { + write_vector(gspca_dev, + spca504A_clicksmart420_init_data); + } else { + write_vector(gspca_dev, spca504_pccam600_init_data); + } + enable = (sd->autogain ? 0x4 : 0x1); + reg_write(dev, 0x0c, 0x0000, enable); /* auto exposure */ + reg_write(dev, 0xb0, 0x0000, enable); /* auto whiteness */ + + /* set default exposure compensation and whiteness balance */ + reg_write(dev, 0x30, 0x0001, 800); /* ~ 20 fps */ + reg_write(dev, 0x30, 0x0002, 1600); + spca504B_SetSizeType(gspca_dev); + break; + } + sp5xx_initContBrigHueRegisters(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ +/* case BRIDGE_SPCA504B: */ + spca5xxRegWrite(dev, 0x31, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(dev); + break; + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + reg_write(dev, 0x00, 0x2000, 0x0000); + + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ +/* spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 0x00, 0x00, 0x9d, 1); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0x00, 0xff, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + reg_write(dev, 0x01, 0x000f, 0x0); + } + break; + } +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sof = 0; + unsigned char *s, *d; + static unsigned char ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + switch (sd->bridge) { + case BRIDGE_SPCA533: + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + sof = 1; + data += SPCA533_OFFSET_DATA; + len -= SPCA533_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + break; + case BRIDGE_SPCA536: + if (data[0] == 0xff) { + sof = 1; + data += SPCA536_OFFSET_DATA; + len -= SPCA536_OFFSET_DATA; + } else { + data += 2; + len -= 2; + } + break; + default: +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504B: */ + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + case BRIDGE_SPCA504C: + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA504_PCCAM600_OFFSET_DATA; + len -= SPCA504_PCCAM600_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + } + if (sof) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + } + + /* add 0x00 after 0xff */ + for (i = len; --i >= 0; ) + if (data[i] == 0xff) + break; + if (i < 0) { /* no 0xff */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + return; + } + s = data; + d = sd->packet; + for (i = 0; i < len; i++) { + *d++ = *s++; + if (s[-1] == 0xff) + *d++ = 0x00; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + sd->packet, d - sd->packet); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + reg_write(dev, 0x0, 0x21a7, sd->brightness); + break; + case BRIDGE_SPCA536: + reg_write(dev, 0x0, 0x20f0, sd->brightness); + break; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 brightness = 0; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + brightness = reg_read(dev, 0x0, 0x21a7, 2); + break; + case BRIDGE_SPCA536: + brightness = reg_read(dev, 0x0, 0x20f0, 2); + break; + } + sd->brightness = ((brightness & 0xff) - 128) % 255; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + reg_write(dev, 0x0, 0x21a8, sd->contrast); + break; + case BRIDGE_SPCA536: + reg_write(dev, 0x0, 0x20f1, sd->contrast); + break; + } +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + sd->contrast = reg_read(dev, 0x0, 0x21a8, 2); + break; + case BRIDGE_SPCA536: + sd->contrast = reg_read(dev, 0x0, 0x20f1, 2); + break; + } +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + reg_write(dev, 0x0, 0x21ae, sd->colors); + break; + case BRIDGE_SPCA536: + reg_write(dev, 0x0, 0x20f6, sd->colors); + break; + } +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + sd->colors = reg_read(dev, 0x0, 0x21ae, 2) >> 1; + break; + case BRIDGE_SPCA536: + sd->colors = reg_read(dev, 0x0, 0x20f6, 2) >> 1; + break; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x400b), DVNM("Creative PC-CAM 600")}, + {USB_DEVICE(0x041e, 0x4012), DVNM("PC-Cam350")}, + {USB_DEVICE(0x041e, 0x4013), DVNM("Creative Pccam750")}, + {USB_DEVICE(0x0458, 0x7006), DVNM("Genius Dsc 1.3 Smart")}, + {USB_DEVICE(0x046d, 0x0905), DVNM("Logitech ClickSmart 820")}, + {USB_DEVICE(0x046d, 0x0960), DVNM("Logitech ClickSmart 420")}, + {USB_DEVICE(0x0471, 0x0322), DVNM("Philips DMVC1300K")}, + {USB_DEVICE(0x04a5, 0x3003), DVNM("Benq DC 1300")}, + {USB_DEVICE(0x04a5, 0x3008), DVNM("Benq DC 1500")}, + {USB_DEVICE(0x04a5, 0x300a), DVNM("Benq DC3410")}, + {USB_DEVICE(0x04f1, 0x1001), DVNM("JVC GC A50")}, + {USB_DEVICE(0x04fc, 0x500c), DVNM("Sunplus CA500C")}, + {USB_DEVICE(0x04fc, 0x504a), DVNM("Aiptek Mini PenCam 1.3")}, + {USB_DEVICE(0x04fc, 0x504b), DVNM("Maxell MaxPocket LE 1.3")}, + {USB_DEVICE(0x04fc, 0x5330), DVNM("Digitrex 2110")}, + {USB_DEVICE(0x04fc, 0x5360), DVNM("Sunplus Generic")}, + {USB_DEVICE(0x04fc, 0xffff), DVNM("Pure DigitalDakota")}, + {USB_DEVICE(0x052b, 0x1513), DVNM("Megapix V4")}, + {USB_DEVICE(0x0546, 0x3155), DVNM("Polaroid PDC3070")}, + {USB_DEVICE(0x0546, 0x3191), DVNM("Polaroid Ion 80")}, + {USB_DEVICE(0x0546, 0x3273), DVNM("Polaroid PDC2030")}, + {USB_DEVICE(0x055f, 0xc211), DVNM("Kowa Bs888e Microcamera")}, + {USB_DEVICE(0x055f, 0xc230), DVNM("Mustek Digicam 330K")}, + {USB_DEVICE(0x055f, 0xc232), DVNM("Mustek MDC3500")}, + {USB_DEVICE(0x055f, 0xc360), DVNM("Mustek DV4000 Mpeg4 ")}, + {USB_DEVICE(0x055f, 0xc420), DVNM("Mustek gSmart Mini 2")}, + {USB_DEVICE(0x055f, 0xc430), DVNM("Mustek Gsmart LCD 2")}, + {USB_DEVICE(0x055f, 0xc440), DVNM("Mustek DV 3000")}, + {USB_DEVICE(0x055f, 0xc520), DVNM("Mustek gSmart Mini 3")}, + {USB_DEVICE(0x055f, 0xc530), DVNM("Mustek Gsmart LCD 3")}, + {USB_DEVICE(0x055f, 0xc540), DVNM("Gsmart D30")}, + {USB_DEVICE(0x055f, 0xc630), DVNM("Mustek MDC4000")}, + {USB_DEVICE(0x055f, 0xc650), DVNM("Mustek MDC5500Z")}, + {USB_DEVICE(0x05da, 0x1018), DVNM("Digital Dream Enigma 1.3")}, + {USB_DEVICE(0x06d6, 0x0031), DVNM("Trust 610 LCD PowerC@m Zoom")}, + {USB_DEVICE(0x0733, 0x1311), DVNM("Digital Dream Epsilon 1.3")}, + {USB_DEVICE(0x0733, 0x1314), DVNM("Mercury 2.1MEG Deluxe Classic Cam")}, + {USB_DEVICE(0x0733, 0x2211), DVNM("Jenoptik jdc 21 LCD")}, + {USB_DEVICE(0x0733, 0x2221), DVNM("Mercury Digital Pro 3.1p")}, + {USB_DEVICE(0x0733, 0x3261), DVNM("Concord 3045 spca536a")}, + {USB_DEVICE(0x0733, 0x3281), DVNM("Cyberpix S550V")}, + {USB_DEVICE(0x08ca, 0x0104), DVNM("Aiptek PocketDVII 1.3")}, + {USB_DEVICE(0x08ca, 0x0106), DVNM("Aiptek Pocket DV3100+")}, + {USB_DEVICE(0x08ca, 0x2008), DVNM("Aiptek Mini PenCam 2 M")}, + {USB_DEVICE(0x08ca, 0x2010), DVNM("Aiptek PocketCam 3M")}, + {USB_DEVICE(0x08ca, 0x2016), DVNM("Aiptek PocketCam 2 Mega")}, + {USB_DEVICE(0x08ca, 0x2018), DVNM("Aiptek Pencam SD 2M")}, + {USB_DEVICE(0x08ca, 0x2020), DVNM("Aiptek Slim 3000F")}, + {USB_DEVICE(0x08ca, 0x2022), DVNM("Aiptek Slim 3200")}, + {USB_DEVICE(0x08ca, 0x2024), DVNM("Aiptek DV3500 Mpeg4 ")}, + {USB_DEVICE(0x08ca, 0x2028), DVNM("Aiptek PocketCam4M")}, + {USB_DEVICE(0x08ca, 0x2040), DVNM("Aiptek PocketDV4100M")}, + {USB_DEVICE(0x08ca, 0x2042), DVNM("Aiptek PocketDV5100")}, + {USB_DEVICE(0x08ca, 0x2060), DVNM("Aiptek PocketDV5300")}, + {USB_DEVICE(0x0d64, 0x0303), DVNM("Sunplus FashionCam DXG")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c new file mode 100644 index 000000000000..c22b301ebae0 --- /dev/null +++ b/drivers/media/video/gspca/t613.c @@ -0,0 +1,1013 @@ +/* + *Notes: * t613 + tas5130A + * * Focus to light do not balance well as in win. + * Quality in win is not good, but its kinda better. + * * Fix some "extraneous bytes", most of apps will show the image anyway + * * Gamma table, is there, but its really doing something? + * * 7~8 Fps, its ok, max on win its 10. + * Costantino Leandro + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "t613" +#include "gspca.h" +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +struct control_menu_info { + int value; + char name[32]; +}; + +#define MAX_GAMMA 0x10 /* 0 to 15 */ + +/* From LUVCVIEW */ +#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3) + +MODULE_AUTHOR("Leandro Costantino "); +MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); +MODULE_LICENSE("GPL"); + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + unsigned char gamma; + unsigned char sharpness; + unsigned char freq; + unsigned char whitebalance; + unsigned char mirror; + unsigned char effect; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0x0f, + .step = 1, + .default_value = 0x09, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x0d, + .step = 1, + .default_value = 0x07, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0x0f, + .step = 1, + .default_value = 0x05, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_GAMMA 3 + { + { + .id = V4L2_CID_GAMMA, /* (gamma on win) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma (Untested)", + .minimum = 0, + .maximum = MAX_GAMMA, + .step = 1, + .default_value = 0x09, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, +#define SD_AUTOGAIN 4 + { + { + .id = V4L2_CID_GAIN, /* here, i activate only the lowlight, + * some apps dont bring up the + * backligth_compensation control) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Low Light", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0x01, + }, + .set = sd_setlowlight, + .get = sd_getlowlight, + }, +#define SD_MIRROR 5 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = sd_setflip, + .get = sd_getflip + }, +#define SD_LIGHTFREQ 6 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light Frequency Filter", + .minimum = 1, /* 1 -> 0x50, 2->0x60 */ + .maximum = 2, + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq}, + +#define SD_WHITE_BALANCE 7 + { + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setwhitebalance, + .get = sd_getwhitebalance + }, +#define SD_SHARPNESS 8 /* (aka definition on win) */ + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = MAX_GAMMA, /* 0 to 16 */ + .step = 1, + .default_value = 0x06, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +#define SD_EFFECTS 9 + { + { + .id = V4L2_CID_EFFECTS, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Webcam Effects", + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0, + }, + .set = sd_seteffect, + .get = sd_geteffect + }, +}; + +static struct control_menu_info effects_control[] = { + {0, "Normal"}, + {1, "Emboss"}, /* disabled */ + {2, "Monochrome"}, + {3, "Sepia"}, + {4, "Sketch"}, + {5, "Sun Effect"}, /* disabled */ + {6, "Negative"}, +}; + +#define NUM_EFFECTS_CONTROL \ + (sizeof(effects_control)/sizeof(effects_control[0])) + +static struct cam_mode vga_mode_t16[] = { + {V4L2_PIX_FMT_JPEG, 160, 120, 4}, + {V4L2_PIX_FMT_JPEG, 176, 144, 3}, + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 352, 288, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +#define T16_OFFSET_DATA 631 +#define MAX_EFFECTS 7 +/* easily done by soft, this table could be removed, + * i keep it here just in case */ +unsigned char effects_table[MAX_EFFECTS][6] = { + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80}, /* Sepia */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02}, /* Croquis */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10}, /* Sun Effect */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ +}; + +unsigned char gamma_table[MAX_GAMMA][34] = { + {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85, + 0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9, + 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb, + 0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x33, 0x92, 0x5A, 0x93, 0x75, + 0x94, 0x85, 0x95, 0x93, 0x96, 0xA1, 0x97, 0xAD, + 0x98, 0xB7, 0x99, 0xC2, 0x9A, 0xCB, 0x9B, 0xD4, + 0x9C, 0xDE, 0x9D, 0xE7, 0x9E, 0xF0, 0x9F, 0xF7, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x2F, 0x92, 0x51, 0x93, 0x6B, + 0x94, 0x7C, 0x95, 0x8A, 0x96, 0x99, 0x97, 0xA6, + 0x98, 0xB1, 0x99, 0xBC, 0x9A, 0xC6, 0x9B, 0xD0, + 0x9C, 0xDB, 0x9D, 0xE4, 0x9E, 0xED, 0x9F, 0xF6, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60, + 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9E, + 0x98, 0xAA, 0x99, 0xB5, 0x9A, 0xBF, 0x9B, 0xCB, + 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x23, 0x92, 0x3F, 0x93, 0x55, + 0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95, + 0x98, 0xA2, 0x99, 0xAD, 0x9A, 0xB9, 0x9B, 0xC6, + 0x9C, 0xD2, 0x9D, 0xDE, 0x9E, 0xE9, 0x9F, 0xF4, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x1B, 0x92, 0x33, 0x93, 0x48, + 0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87, + 0x98, 0x96, 0x99, 0xA3, 0x9A, 0xB1, 0x9B, 0xBE, + 0x9C, 0xCC, 0x9D, 0xDA, 0x9E, 0xE7, 0x9F, 0xF3, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20, + 0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67, + 0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa, + 0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26, + 0x94, 0x38, 0x95, 0x4A, 0x96, 0x60, 0x97, 0x70, + 0x98, 0x80, 0x99, 0x90, 0x9A, 0xA0, 0x9B, 0xB0, + 0x9C, 0xC0, 0x9D, 0xD0, 0x9E, 0xE0, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35, + 0x94, 0x47, 0x95, 0x5A, 0x96, 0x69, 0x97, 0x79, + 0x98, 0x88, 0x99, 0x97, 0x9A, 0xA7, 0x9B, 0xB6, + 0x9C, 0xC4, 0x9D, 0xD3, 0x9E, 0xE0, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40, + 0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, + 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, + 0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x18, 0x92, 0x2B, 0x93, 0x44, + 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8E, + 0x98, 0x9C, 0x99, 0xAA, 0x9A, 0xB7, 0x9B, 0xC4, + 0x9C, 0xD0, 0x9D, 0xD8, 0x9E, 0xE2, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x1A, 0x92, 0x34, 0x93, 0x52, + 0x94, 0x66, 0x95, 0x7E, 0x96, 0x8D, 0x97, 0x9B, + 0x98, 0xA8, 0x99, 0xB4, 0x9A, 0xC0, 0x9B, 0xCB, + 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x3F, 0x92, 0x5A, 0x93, 0x6E, + 0x94, 0x7F, 0x95, 0x8E, 0x96, 0x9C, 0x97, 0xA8, + 0x98, 0xB4, 0x99, 0xBF, 0x9A, 0xC9, 0x9B, 0xD3, + 0x9C, 0xDC, 0x9D, 0xE5, 0x9E, 0xEE, 0x9F, 0xF6, + 0xA0, 0xFF}, + {0x90, 0x00, 0x91, 0x54, 0x92, 0x6F, 0x93, 0x83, + 0x94, 0x93, 0x95, 0xA0, 0x96, 0xAD, 0x97, 0xB7, + 0x98, 0xC2, 0x99, 0xCB, 0x9A, 0xD4, 0x9B, 0xDC, + 0x9C, 0xE4, 0x9D, 0xEB, 0x9E, 0xF2, 0x9F, 0xF9, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x6E, 0x92, 0x88, 0x93, 0x9A, + 0x94, 0xA8, 0x95, 0xB3, 0x96, 0xBD, 0x97, 0xC6, + 0x98, 0xCF, 0x99, 0xD6, 0x9A, 0xDD, 0x9B, 0xE3, + 0x9C, 0xE9, 0x9D, 0xEF, 0x9E, 0xF4, 0x9F, 0xFA, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x93, 0x92, 0xA8, 0x93, 0xB7, + 0x94, 0xC1, 0x95, 0xCA, 0x96, 0xD2, 0x97, 0xD8, + 0x98, 0xDE, 0x99, 0xE3, 0x9A, 0xE8, 0x9B, 0xED, + 0x9C, 0xF1, 0x9D, 0xF5, 0x9E, 0xF8, 0x9F, 0xFC, + 0xA0, 0xFF} +}; + +static __u8 tas5130a_sensor_init[][8] = { + {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, + {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, + {}, +}; + +static void t16RegRead(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void t16RegWrite(struct usb_device *dev, + __u16 value, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, 500); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + + cam->cam_mode = vga_mode_t16; + cam->nmodes = ARRAY_SIZE(vga_mode_t16); + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->gamma = sd_ctrls[SD_GAMMA].qctrl.default_value; + sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value; + sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value; + sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value; + sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; + sd->effect = sd_ctrls[SD_EFFECTS].qctrl.default_value; + return 0; +} + +static int init_default_parameters(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + /* some of this registers are not really neded, because + * they are overriden by setbrigthness, setcontrast, etc, + * but wont hurt anyway, and can help someone with similar webcam + * to see the initial parameters.*/ + int i = 0; + __u8 test_byte; + + static unsigned char read_indexs[] = + { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, + 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 }; + static unsigned char n1[6] = + {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; + static unsigned char n2[2] = + {0x08, 0x00}; + static unsigned char nset[6] = + { 0x61, 0x68, 0x62, 0xff, 0x60, 0x07 }; + static unsigned char n3[6] = + {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}; + static unsigned char n4[0x46] = + {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, + 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, + 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, + 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, + 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, + 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, + 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, + 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, + 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46}; + static unsigned char nset4[18] = { + 0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60, 0xe4, 0xa8, + 0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8, + 0xe8, 0xe0 + }; + /* ojo puede ser 0xe6 en vez de 0xe9 */ + static unsigned char nset2[20] = { + 0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10, 0xd4, 0xbb, + 0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27, + 0xd8, 0xc8, 0xd9, 0xfc + }; + static unsigned char missing[8] = + { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 }; + static unsigned char nset3[18] = { + 0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60, 0xcb, 0xa8, + 0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8, + 0xcf, 0xe0 + }; + static unsigned char nset5[4] = + { 0x8f, 0x24, 0xc3, 0x00 }; /* bright */ + static unsigned char nset6[34] = { + 0x90, 0x00, 0x91, 0x1c, 0x92, 0x30, 0x93, 0x43, 0x94, 0x54, + 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, + 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, 0x9c, 0xca, + 0x9d, 0xd8, 0x9e, 0xe5, 0x9f, 0xf2, + 0xa0, 0xff + }; /* Gamma */ + static unsigned char nset7[4] = + { 0x66, 0xca, 0xa8, 0xf8 }; /* 50/60 Hz */ + static unsigned char nset9[4] = + { 0x0b, 0x04, 0x0a, 0x78 }; + static unsigned char nset8[6] = + { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 }; + static unsigned char nset10[6] = + { 0x0c, 0x03, 0xab, 0x10, 0x81, 0x20 }; + + t16RegWrite(dev, 0x01, 0x0000, n1, 0x06); + t16RegWrite(dev, 0x01, 0x0000, nset, 0x06); + t16RegRead(dev, 0x0063, &test_byte, 1); + t16RegWrite(dev, 0x01, 0x0000, n2, 0x02); + + while (read_indexs[i] != 0x00) { + t16RegRead(dev, read_indexs[i], &test_byte, 1); + PDEBUG(D_CONF, "Reg 0x%x => 0x%x", read_indexs[i], + test_byte); + i++; + } + + t16RegWrite(dev, 0x01, 0x0000, n3, 0x06); + t16RegWrite(dev, 0x01, 0x0000, n4, 0x46); + t16RegRead(dev, 0x0080, &test_byte, 1); + t16RegWrite(dev, 0x00, 0x2c80, 0x00, 0x0); + t16RegWrite(dev, 0x01, 0x0000, nset2, 0x14); + t16RegWrite(dev, 0x01, 0x0000, nset3, 0x12); + t16RegWrite(dev, 0x01, 0x0000, nset4, 0x12); + t16RegWrite(dev, 0x00, 0x3880, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x3880, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x338e, 0x00, 0x0); + t16RegWrite(dev, 0x01, 00, nset5, 0x04); + t16RegWrite(dev, 0x00, 0x00a9, 0x00, 0x0); + t16RegWrite(dev, 0x01, 00, nset6, 0x22); + t16RegWrite(dev, 0x00, 0x86bb, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x4aa6, 0x00, 0x0); + + t16RegWrite(dev, 0x01, 00, missing, 0x08); + + t16RegWrite(dev, 0x00, 0x2087, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x2088, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x2089, 0x00, 0x0); + + t16RegWrite(dev, 0x01, 00, nset7, 0x4); + t16RegWrite(dev, 0x01, 00, nset10, 0x06); + t16RegWrite(dev, 0x01, 00, nset8, 0x06); + t16RegWrite(dev, 0x01, 00, nset9, 0x04); + + t16RegWrite(dev, 0x00, 0x2880, 0x00, 0x0); + t16RegWrite(dev, 0x01, 0x0000, nset2, 0x14); + t16RegWrite(dev, 0x01, 0x0000, nset3, 0x12); + t16RegWrite(dev, 0x01, 0x0000, nset4, 0x12); + + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + unsigned int brightness; + unsigned char set6[4] = { 0x8f, 0x26, 0xc3, 0x80 }; + brightness = sd->brightness; + + if (brightness < 7) { + set6[3] = 0x70 - (brightness * 0xa); + } else { + set6[1] = 0x24; + set6[3] = 0x00 + ((brightness - 7) * 0xa); + } + + t16RegWrite(dev, 0x01, 0x0000, set6, 4); +} + +static void setflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + unsigned char flipcmd[8] = + { 0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09 }; + + if (sd->mirror == 1) + flipcmd[3] = 0x01; + + t16RegWrite(dev, 0x01, 0x0000, flipcmd, 8); +} + +static void seteffect(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + t16RegWrite(dev, 0x01, 0x0000, effects_table[sd->effect], 0x06); + if (sd->effect == 1 || sd->effect == 5) { + PDEBUG(D_CONF, + "This effect have been disabled for webcam \"safety\""); + return; + } + + if (sd->effect == 1 || sd->effect == 4) + t16RegWrite(dev, 0x00, 0x4aa6, 0x00, 0x00); + else + t16RegWrite(dev, 0x00, 0xfaa6, 0x00, 0x00); +} + +static void setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + unsigned char white_balance[8] = + { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 }; + + if (sd->whitebalance == 1) + white_balance[7] = 0x3c; + + t16RegWrite(dev, 0x01, 0x0000, white_balance, 8); +} + +static void setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; + + if (sd->freq == 2) /* 60hz */ + freq[1] = 0x00; + + t16RegWrite(dev, 0x1, 0x0000, freq, 0x4); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + unsigned int contrast = sd->contrast; + __u16 reg_to_write = 0x00; + + if (contrast < 7) + reg_to_write = 0x8ea9 - (0x200 * contrast); + else + reg_to_write = (0x00a9 + ((contrast - 7) * 0x200)); + + t16RegWrite(dev, 0x00, reg_to_write, 0x00, 0); + +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 reg_to_write = 0x00; + + reg_to_write = 0xc0bb + sd->colors * 0x100; + t16RegWrite(dev, 0x00, reg_to_write, 0x00, 0); +} + +static void setgamma(struct gspca_dev *gspca_dev) +{ +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 reg_to_write = 0x00; + + reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness; + + t16RegWrite(dev, 0x00, reg_to_write, 0x00, 0x00); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->brightness; + return *val; +} + +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->whitebalance = val; + if (gspca_dev->streaming) + setwhitebalance(gspca_dev); + return 0; +} + +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->whitebalance; + return *val; +} + + +static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirror = val; + if (gspca_dev->streaming) + setflip(gspca_dev); + return 0; +} + +static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->mirror; + return *val; +} + +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->effect = val; + if (gspca_dev->streaming) + seteffect(gspca_dev); + return 0; +} + +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->effect; + return *val; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return *val; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setgamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gamma; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +/* Low Light set here......*/ +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + sd->autogain = val; + if (val != 0) + t16RegWrite(dev, 0x00, 0xf48e, 0x00, 0); + else + t16RegWrite(dev, 0x00, 0xb48e, 0x00, 0); + return 0; +} + +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int mode; + __u8 test_byte; + + static __u8 t1[] = { 0x66, 0x00, 0xa8, 0xe8 }; + __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; + static __u8 t3[] = + { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06, + 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 }; + static __u8 t4[] = { 0x0b, 0x04, 0x0a, 0x40 }; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. mode; + switch (mode) { + case 1: /* 352x288 */ + t2[1] = 0x40; + break; + case 2: /* 320x240 */ + t2[1] = 0x10; + break; + case 3: /* 176x144 */ + t2[1] = 0x50; + break; + case 4: /* 160x120 */ + t2[1] = 0x20; + break; + default: /* 640x480 (0x00) */ + break; + } + + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[0], 0x8); + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[1], 0x8); + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[2], 0x8); + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8); + t16RegWrite(dev, 0x00, 0x3c80, 0x00, 0x00); + /* just in case and to keep sync with logs (for mine) */ + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8); + t16RegWrite(dev, 0x00, 0x3c80, 0x00, 0x00); + /* just in case and to keep sync with logs (for mine) */ + t16RegWrite(dev, 0x01, 0x0000, t1, 4); + t16RegWrite(dev, 0x01, 0x0000, t2, 6); + t16RegRead(dev, 0x0012, &test_byte, 0x1); + t16RegWrite(dev, 0x01, 0x0000, t3, 0x10); + t16RegWrite(dev, 0x00, 0x0013, 0x00, 0x00); + t16RegWrite(dev, 0x01, 0x0000, t4, 0x4); + /* restart on each start, just in case, sometimes regs goes wrong + * when using controls from app */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int sof = 0; + static unsigned char ffd9[] = { 0xff, 0xd9 }; + + if (data[0] == 0x5a) { + /* Control Packet, after this came the header again, + * but extra bytes came in the packet before this, + * sometimes an EOF arrives, sometimes not... */ + return; + } + + if (data[len - 1] == 0xff && data[len] == 0xd9) { + /* Just in case, i have seen packets with the marker, + * other's do not include it... */ + data += 2; + len -= 4; + } else if (data[2] == 0xff && data[3] == 0xd8) { + sof = 1; + data += 2; + len -= 2; + } else { + data += 2; + len -= 2; + } + + if (sof) { + /* extra bytes....., could be processed too but would be + * a waste of time, right now leave the application and + * libjpeg do it for ourserlves.. */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + return; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + memset(menu->name, 0, sizeof menu->name); + + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + case V4L2_CID_EFFECTS: + if (menu->index < 0 || menu->index >= NUM_EFFECTS_CONTROL) + return -EINVAL; + strncpy((char *) menu->name, + effects_control[menu->index].name, 32); + break; + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + init_default_parameters(gspca_dev); + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x17a1, 0x0128), DVNM("XPX Webcam")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c new file mode 100644 index 000000000000..6218441ba1f0 --- /dev/null +++ b/drivers/media/video/gspca/tv8532.c @@ -0,0 +1,709 @@ +/* + * Quickcam cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "tv8532" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("TV8532 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; /* current length of tmpbuf */ + __u8 tmpbuf[352 * 288 + 10 * 288]; /* no protection... */ + __u8 tmpbuf2[352 * 288]; /* no protection... */ + + unsigned short brightness; + unsigned short contrast; + + char packet; + char synchro; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 1, + .maximum = 0x2ff, + .step = 1, + .default_value = 0x18f, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0x7fff, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, + {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, +}; + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +#define TESTCLK 0x10 /* reg 0x2c -> 0x12 //10 */ +#define TESTCOMP 0x90 /* reg 0x28 -> 0x80 */ +#define TESTLINE 0x81 /* reg 0x29 -> 0x81 */ +#define QCIFLINE 0x41 /* reg 0x29 -> 0x81 */ +#define TESTPTL 0x14 /* reg 0x2D -> 0x14 */ +#define TESTPTH 0x01 /* reg 0x2E -> 0x01 */ +#define TESTPTBL 0x12 /* reg 0x2F -> 0x0a */ +#define TESTPTBH 0x01 /* reg 0x30 -> 0x01 */ +#define ADWIDTHL 0xe8 /* reg 0x0c -> 0xe8 */ +#define ADWIDTHH 0x03 /* reg 0x0d -> 0x03 */ +#define ADHEIGHL 0x90 /* reg 0x0e -> 0x91 //93 */ +#define ADHEIGHH 0x01 /* reg 0x0f -> 0x01 */ +#define EXPOL 0x8f /* reg 0x1c -> 0x8f */ +#define EXPOH 0x01 /* reg 0x1d -> 0x01 */ +#define ADCBEGINL 0x44 /* reg 0x10 -> 0x46 //47 */ +#define ADCBEGINH 0x00 /* reg 0x11 -> 0x00 */ +#define ADRBEGINL 0x0a /* reg 0x14 -> 0x0b //0x0c */ +#define ADRBEGINH 0x00 /* reg 0x15 -> 0x00 */ +#define TV8532_CMD_UPDATE 0x84 + +#define TV8532_EEprom_Add 0x03 +#define TV8532_EEprom_DataL 0x04 +#define TV8532_EEprom_DataM 0x05 +#define TV8532_EEprom_DataH 0x06 +#define TV8532_EEprom_TableLength 0x07 +#define TV8532_EEprom_Write 0x08 +#define TV8532_PART_CTRL 0x00 +#define TV8532_CTRL 0x01 +#define TV8532_CMD_EEprom_Open 0x30 +#define TV8532_CMD_EEprom_Close 0x29 +#define TV8532_UDP_UPDATE 0x31 +#define TV8532_GPIO 0x39 +#define TV8532_GPIO_OE 0x3B +#define TV8532_REQ_RegWrite 0x02 +#define TV8532_REQ_RegRead 0x03 + +#define TV8532_ADWIDTH_L 0x0C +#define TV8532_ADWIDTH_H 0x0D +#define TV8532_ADHEIGHT_L 0x0E +#define TV8532_ADHEIGHT_H 0x0F +#define TV8532_EXPOSURE 0x1C +#define TV8532_QUANT_COMP 0x28 +#define TV8532_MODE_PACKET 0x29 +#define TV8532_SETCLK 0x2C +#define TV8532_POINT_L 0x2D +#define TV8532_POINT_H 0x2E +#define TV8532_POINTB_L 0x2F +#define TV8532_POINTB_H 0x30 +#define TV8532_BUDGET_L 0x2A +#define TV8532_BUDGET_H 0x2B +#define TV8532_VID_L 0x34 +#define TV8532_VID_H 0x35 +#define TV8532_PID_L 0x36 +#define TV8532_PID_H 0x37 +#define TV8532_DeviceID 0x83 +#define TV8532_AD_SLOPE 0x91 +#define TV8532_AD_BITCTRL 0x94 +#define TV8532_AD_COLBEGIN_L 0x10 +#define TV8532_AD_COLBEGIN_H 0x11 +#define TV8532_AD_ROWBEGIN_L 0x14 +#define TV8532_AD_ROWBEGIN_H 0x15 + +static __u32 tv_8532_eeprom_data[] = { +/* add dataL dataM dataH */ + 0x00010001, 0x01018011, 0x02050014, 0x0305001c, + 0x040d001e, 0x0505001f, 0x06050519, 0x0705011b, + 0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9, + 0x0c0509f1, 0 +}; + +static void reg_r(struct usb_device *dev, + __u16 index, __u8 *buffer) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + TV8532_REQ_RegRead, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, sizeof(__u8), + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + TV8532_REQ_RegWrite, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) +{ + int i = 0; + __u8 reg, data0, data1, data2, datacmd; + struct usb_device *dev = gspca_dev->dev; + + datacmd = 0xb0;; + reg_w(dev, TV8532_GPIO, &datacmd, 1); + datacmd = TV8532_CMD_EEprom_Open; + reg_w(dev, TV8532_CTRL, &datacmd, + 1); +/* msleep(1); */ + while (tv_8532_eeprom_data[i]) { + reg = (tv_8532_eeprom_data[i] & 0xff000000) >> 24; + reg_w(dev, TV8532_EEprom_Add, ®, 1); + /* msleep(1); */ + data0 = (tv_8532_eeprom_data[i] & 0x000000ff); + reg_w(dev, TV8532_EEprom_DataL, &data0, 1); + /* msleep(1); */ + data1 = (tv_8532_eeprom_data[i] & 0x0000FF00) >> 8; + reg_w(dev, TV8532_EEprom_DataM, &data1, 1); + /* msleep(1); */ + data2 = (tv_8532_eeprom_data[i] & 0x00FF0000) >> 16; + reg_w(dev, TV8532_EEprom_DataH, &data2, 1); + /* msleep(1); */ + datacmd = 0; + reg_w(dev, TV8532_EEprom_Write, &datacmd, 1); + /* msleep(10); */ + i++; + } + datacmd = i; + reg_w(dev, TV8532_EEprom_TableLength, &datacmd, 1); +/* msleep(1); */ + datacmd = TV8532_CMD_EEprom_Close; + reg_w(dev, TV8532_CTRL, &datacmd, 1); + msleep(10); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + tv_8532WriteEEprom(gspca_dev); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 1; + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + return 0; +} + +static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; +/* __u16 vid, pid; */ + + reg_r(dev, 0x0001, &data); + PDEBUG(D_USBI, "register 0x01-> %x", data); + reg_r(dev, 0x0002, &data); + PDEBUG(D_USBI, "register 0x02-> %x", data); + reg_r(dev, TV8532_ADWIDTH_L, &data); + reg_r(dev, TV8532_ADWIDTH_H, &data); + reg_r(dev, TV8532_QUANT_COMP, &data); + reg_r(dev, TV8532_MODE_PACKET, &data); + reg_r(dev, TV8532_SETCLK, &data); + reg_r(dev, TV8532_POINT_L, &data); + reg_r(dev, TV8532_POINT_H, &data); + reg_r(dev, TV8532_POINTB_L, &data); + reg_r(dev, TV8532_POINTB_H, &data); + reg_r(dev, TV8532_BUDGET_L, &data); + reg_r(dev, TV8532_BUDGET_H, &data); + reg_r(dev, TV8532_VID_L, &data); + reg_r(dev, TV8532_VID_H, &data); + reg_r(dev, TV8532_PID_L, &data); + reg_r(dev, TV8532_PID_H, &data); + reg_r(dev, TV8532_DeviceID, &data); + reg_r(dev, TV8532_AD_COLBEGIN_L, &data); + reg_r(dev, TV8532_AD_COLBEGIN_H, &data); + reg_r(dev, TV8532_AD_ROWBEGIN_L, &data); + reg_r(dev, TV8532_AD_ROWBEGIN_H, &data); +} + +static void tv_8532_setReg(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + __u8 value[2] = { 0, 0 }; + + data = ADCBEGINL; + reg_w(dev, TV8532_AD_COLBEGIN_L, &data, 1); /* 0x10 */ + data = ADCBEGINH; /* also digital gain */ + reg_w(dev, TV8532_AD_COLBEGIN_H, &data, 1); + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ + + data = 0x0a; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + /******************************************************/ + data = ADHEIGHL; + reg_w(dev, TV8532_ADHEIGHT_L, &data, 1); /* 0e */ + data = ADHEIGHH; + reg_w(dev, TV8532_ADHEIGHT_H, &data, 1); /* 0f */ + value[0] = EXPOL; + value[1] = EXPOH; /* 350d 0x014c; */ + reg_w(dev, TV8532_EXPOSURE, value, 2); /* 1c */ + data = ADCBEGINL; + reg_w(dev, TV8532_AD_COLBEGIN_L, &data, 1); /* 0x10 */ + data = ADCBEGINH; /* also digital gain */ + reg_w(dev, TV8532_AD_COLBEGIN_H, &data, 1); + data = ADRBEGINL; + reg_w(dev, TV8532_AD_ROWBEGIN_L, &data, 1); /* 0x14 */ + + data = 0x00; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); /* 0x91 */ + data = 0x02; + reg_w(dev, TV8532_AD_BITCTRL, &data, 1); /* 0x94 */ + + + data = TV8532_CMD_EEprom_Close; + reg_w(dev, TV8532_CTRL, &data, 1); /* 0x01 */ + + data = 0x00; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); /* 0x91 */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ +} + +static void tv_8532_PollReg(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + int i; + + /* strange polling from tgc */ + for (i = 0; i < 10; i++) { + data = TESTCLK; /* 0x48; //0x08; */ + reg_w(dev, TV8532_SETCLK, &data, 1); /* 0x2c */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); + data = 0x01; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + } +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + __u8 dataStart = 0; + __u8 value[2] = { 0, 0 }; + + data = 0x32; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); + data = 0; + reg_w(dev, TV8532_AD_BITCTRL, &data, 1); + tv_8532ReadRegisters(gspca_dev); + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + value[0] = ADHEIGHL; + value[1] = ADHEIGHH; /* 401d 0x0169; */ + reg_w(dev, TV8532_ADHEIGHT_L, value, 2); /* 0e */ + value[0] = EXPOL; + value[1] = EXPOH; /* 350d 0x014c; */ + reg_w(dev, TV8532_EXPOSURE, value, 2); /* 1c */ + data = ADWIDTHL; /* 0x20; */ + reg_w(dev, TV8532_ADWIDTH_L, &data, 1); /* 0x0c */ + data = ADWIDTHH; + reg_w(dev, TV8532_ADWIDTH_H, &data, 1); /* 0x0d */ + + /*******************************************************************/ + data = TESTCOMP; /* 0x72 compressed mode */ + reg_w(dev, TV8532_QUANT_COMP, &data, 1); /* 0x28 */ + data = TESTLINE; /* 0x84; // CIF | 4 packet */ + reg_w(dev, TV8532_MODE_PACKET, &data, 1); /* 0x29 */ + + /************************************************/ + data = TESTCLK; /* 0x48; //0x08; */ + reg_w(dev, TV8532_SETCLK, &data, 1); /* 0x2c */ + data = TESTPTL; /* 0x38; */ + reg_w(dev, TV8532_POINT_L, &data, 1); /* 0x2d */ + data = TESTPTH; /* 0x04; */ + reg_w(dev, TV8532_POINT_H, &data, 1); /* 0x2e */ + dataStart = TESTPTBL; /* 0x04; */ + reg_w(dev, TV8532_POINTB_L, &dataStart, 1); /* 0x2f */ + data = TESTPTBH; /* 0x04; */ + reg_w(dev, TV8532_POINTB_H, &data, 1); /* 0x30 */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ + /*************************************************/ + data = 0x01; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + msleep(200); + data = 0x00; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + /*************************************************/ + tv_8532_setReg(gspca_dev); + /*************************************************/ + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, + 1); + /*************************************************/ + tv_8532_setReg(gspca_dev); + /*************************************************/ + tv_8532_PollReg(gspca_dev); + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value[2]; + __u8 data; + int brightness = sd->brightness; + + value[1] = (brightness >> 8) & 0xff; + value[0] = (brightness) & 0xff; + reg_w(gspca_dev->dev, TV8532_EXPOSURE, value, 2); /* 1c */ + data = TV8532_CMD_UPDATE; + reg_w(gspca_dev->dev, TV8532_PART_CTRL, &data, 1); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + __u8 dataStart = 0; + __u8 value[2]; + + data = 0x32; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); + data = 0; + reg_w(dev, TV8532_AD_BITCTRL, &data, 1); + tv_8532ReadRegisters(gspca_dev); + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + value[0] = ADHEIGHL; + value[1] = ADHEIGHH; /* 401d 0x0169; */ + reg_w(dev, TV8532_ADHEIGHT_L, value, 2); /* 0e */ +/* value[0] = EXPOL; value[1] =EXPOH; * 350d 0x014c; */ +/* reg_w(dev,TV8532_REQ_RegWrite,0,TV8532_EXPOSURE,value,2); * 1c */ + setbrightness(gspca_dev); + + data = ADWIDTHL; /* 0x20; */ + reg_w(dev, TV8532_ADWIDTH_L, &data, 1); /* 0x0c */ + data = ADWIDTHH; + reg_w(dev, TV8532_ADWIDTH_H, &data, 1); /* 0x0d */ + + /************************************************/ + data = TESTCOMP; /* 0x72 compressed mode */ + reg_w(dev, TV8532_QUANT_COMP, &data, 1); /* 0x28 */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + /* 176x144 */ + data = QCIFLINE; /* 0x84; // CIF | 4 packet */ + reg_w(dev, TV8532_MODE_PACKET, &data, 1); /* 0x29 */ + } else { + /* 352x288 */ + data = TESTLINE; /* 0x84; // CIF | 4 packet */ + reg_w(dev, TV8532_MODE_PACKET, &data, 1); /* 0x29 */ + } + /************************************************/ + data = TESTCLK; /* 0x48; //0x08; */ + reg_w(dev, TV8532_SETCLK, &data, 1); /* 0x2c */ + data = TESTPTL; /* 0x38; */ + reg_w(dev, TV8532_POINT_L, &data, 1); /* 0x2d */ + data = TESTPTH; /* 0x04; */ + reg_w(dev, TV8532_POINT_H, &data, 1); /* 0x2e */ + dataStart = TESTPTBL; /* 0x04; */ + reg_w(dev, TV8532_POINTB_L, &dataStart, 1); /* 0x2f */ + data = TESTPTBH; /* 0x04; */ + reg_w(dev, TV8532_POINTB_H, &data, 1); /* 0x30 */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ + /************************************************/ + data = 0x01; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + msleep(200); + data = 0x00; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + /************************************************/ + tv_8532_setReg(gspca_dev); + /************************************************/ + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + /************************************************/ + tv_8532_setReg(gspca_dev); + /************************************************/ + tv_8532_PollReg(gspca_dev); + data = 0x00; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data; + + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void tv8532_preprocess(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* we should received a whole frame with header and EOL marker + * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2 + * sequence 2bytes header the Alternate pixels bayer GB 4 bytes + * Alternate pixels bayer RG 4 bytes EOL */ + int width = gspca_dev->width; + int height = gspca_dev->height; + unsigned char *dst = sd->tmpbuf2; + unsigned char *data = sd->tmpbuf; + int i; + + /* precompute where is the good bayer line */ + if (((data[3] + data[width + 7]) >> 1) + + (data[4] >> 2) + + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1) + + (data[3] >> 2) + + (data[width + 5] >> 1)) + data += 3; + else + data += 2; + for (i = 0; i < height / 2; i++) { + memcpy(dst, data, width); + data += width + 3; + dst += width; + memcpy(dst, data, width); + data += width + 7; + dst += width; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] != 0x80) { + sd->packet++; + if (sd->buflen + len > sizeof sd->tmpbuf) { + if (gspca_dev->last_packet_type != DISCARD_PACKET) { + PDEBUG(D_PACK, "buffer overflow"); + gspca_dev->last_packet_type = DISCARD_PACKET; + } + return; + } + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; + return; + } + + /* here we detect 0x80 */ + /* counter is limited so we need few header for a frame :) */ + + /* header 0x80 0x80 0x80 0x80 0x80 */ + /* packet 00 63 127 145 00 */ + /* sof 0 1 1 0 0 */ + + /* update sequence */ + if (sd->packet == 63 || sd->packet == 127) + sd->synchro = 1; + + /* is there a frame start ? */ + if (sd->packet >= (gspca_dev->height >> 1) - 1) { + PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro, + sd->packet); + if (!sd->synchro) { /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + tv8532_preprocess(gspca_dev); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, sd->tmpbuf2, + gspca_dev->width * + gspca_dev->width); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, 0); + memcpy(sd->tmpbuf, data, len); + sd->buflen = len; + sd->packet = 0; + return; + } + if (gspca_dev->last_packet_type != DISCARD_PACKET) { + PDEBUG(D_PACK, + "Warning wrong TV8532 frame detection %d", + sd->packet); + gspca_dev->last_packet_type = DISCARD_PACKET; + } + return; + } + + if (!sd->synchro) { + /* Drop packet frame corrupt */ + PDEBUG(D_PACK, "DROP SOF %d packet %d", + sd->synchro, sd->packet); + sd->packet = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + sd->synchro = 1; + sd->packet++; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0920), DVNM("QC Express")}, + {USB_DEVICE(0x046d, 0x0921), DVNM("Labtec Webcam")}, + {USB_DEVICE(0x0545, 0x808b), DVNM("Veo Stingray")}, + {USB_DEVICE(0x0545, 0x8333), DVNM("Veo Stingray")}, + {USB_DEVICE(0x0923, 0x010f), DVNM("ICM532 cams")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c new file mode 100644 index 000000000000..8b5f6d17d2a5 --- /dev/null +++ b/drivers/media/video/gspca/vc032x.c @@ -0,0 +1,1816 @@ +/* + * Z-star vc0321 library + * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl + * Copyright (C) 2006 Michel Xhaard + * + * V4L2 by Jean-Francois Moine + * + * 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 + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "vc032x" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char autogain; + unsigned char lightfreq; + + char qindex; + char bridge; +#define BRIDGE_VC0321 0 +#define BRIDGE_VC0323 1 + char sensor; +#define SENSOR_HV7131R 0 +#define SENSOR_MI1320 1 +#define SENSOR_MI1310_SOC 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_PO3130NC 5 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_AUTOGAIN 0 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define SD_FREQ 1 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +}; + +static struct cam_mode vc0321_mode[] = { + {V4L2_PIX_FMT_YUYV, 320, 240, 1}, + {V4L2_PIX_FMT_YUYV, 640, 480, 0}, +}; +static struct cam_mode vc0323_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +static __u8 mi1310_socinitVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static __u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; + +static __u8 mi1320_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static __u8 mi1320_matrix[9] = { + 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 +}; +static __u8 mi1320_initVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd}, + {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb}, + {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb}, + {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb}, + {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb}, + {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb}, + {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb}, + {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb}, + {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb}, + {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb}, + {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb}, + {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb}, + {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb}, + {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb}, + {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb}, + {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb}, + {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb}, + {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb}, + {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb}, + {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb}, + {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, + {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static __u8 mi1320_initQVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb}, + {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb}, + {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb}, + {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb}, + {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb}, + {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb}, + {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb}, + {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb}, + {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb}, + {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb}, + {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb}, + {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb}, + {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb}, + {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb}, + {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb}, + {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb}, + {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb}, + {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb}, + {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb}, + {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb}, + {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb}, + {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static __u8 po3130_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static __u8 po3130_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; + +static __u8 po3130_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, +/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */ + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static __u8 po3130_rundata[][4] = { + {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, + {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, + {0x00, 0x44, 0x40, 0xaa}, +/* {0x00, 0xd5, 0x7c, 0xaa}, */ + {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa}, + {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa}, + {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa}, + {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa}, + {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa}, + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {} +}; + +static __u8 po3130_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, + {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static __u8 hv7131r_gamma[17] = { +/* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ + 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, + 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff +}; +static __u8 hv7131r_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; +static __u8 hv7131r_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, {0xb3, 0x00, 0x27, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x09, 0xaa}, {0x00, 0x26, 0x27, 0xaa}, + {0x00, 0x27, 0xc0, 0xaa}, + {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, {0xb8, 0x34, 0x65, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x30, 0x18, 0xaa}, + {} +}; + +static __u8 hv7131r_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, {0x00, 0x25, 0x01, 0xaa}, + {0x00, 0x26, 0xd4, 0xaa}, {0x00, 0x27, 0xc0, 0xaa}, + {0xbc, 0x02, 0x08, 0xcc}, + {0xbc, 0x03, 0x70, 0xcc}, {0xbc, 0x04, 0x08, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x3c, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x04, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb8, 0xfe, 0x02, 0xcc}, + {0xb8, 0xff, 0x07, 0xcc}, {0xb9, 0x00, 0x14, 0xcc}, + {0xb9, 0x01, 0x14, 0xcc}, {0xb9, 0x02, 0x14, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x02, 0xcc}, {0xb9, 0x05, 0x05, 0xcc}, + {0xb9, 0x06, 0x0f, 0xcc}, {0xb9, 0x07, 0x0f, 0xcc}, + {0xb9, 0x08, 0x0f, 0xcc}, + {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x65, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, {0x00, 0x30, 0x18, 0xaa}, + {} +}; + +static __u8 ov7660_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static __u8 ov7660_matrix[9] = { + 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 +}; +static __u8 ov7660_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, + {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, + {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, + {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + + {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, + {} +}; +static __u8 ov7660_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, +/* sizer */ + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, +/* sizer filters */ + {0xbc, 0x02, 0x08, 0xcc}, + {0xbc, 0x03, 0x70, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xbc, 0x04, 0x08, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x3c, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x04, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, +/* */ + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, +/* */ + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, +/* */ + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, /* ff */ + {0x00, 0x29, 0x3c, 0xaa}, + {0xb3, 0x01, 0x45, 0xcc}, /* 45 */ + {0x00, 0x00, 0x00, 0x00} +}; + +static __u8 ov7660_50HZ[][4] = { + {0x00, 0x3b, 0x08, 0xaa}, + {0x00, 0x9d, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x00, 0x00, 0x00} +}; + +static __u8 ov7660_60HZ[][4] = { + {0x00, 0x3b, 0x00, 0xaa}, + {0x00, 0x9e, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {} +}; + +static __u8 ov7660_NoFliker[][4] = { + {0x00, 0x13, 0x87, 0xaa}, + {} +}; + +static __u8 ov7670_initVGA_JPG[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0x41, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x13, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, + {}, +}; + +static __u8 ov7670_initQVGA_JPG[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x21, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa }, + {}, +}; + +struct sensor_info { + int sensorId; + __u8 I2cAdd; + __u8 IdAdd; + __u16 VpId; + __u8 m1; + __u8 m2; + __u8 op; + }; + +static struct sensor_info sensor_info_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, + {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, + {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, + {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +}; + +static void reg_r(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, /* value */ + index, buffer, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); +} + +static void vc032x_read_sensor_register(struct usb_device *dev, + __u16 address, __u16 *value) +{ + __u8 ldata, mdata, hdata; + __u8 tmpvalue = 0; + int retry = 50; + ldata = 0; + mdata = 0; + hdata = 0; + *value = 0; + + reg_r(dev, 0xa1, 0xb33f, &tmpvalue, 1); + /*PDEBUG(D_PROBE, " I2c Bus Busy Wait 0x%02X ", tmpvalue); */ + if (!(tmpvalue & 0x02)) { + PDEBUG(D_ERR, "I2c Bus Busy Wait %d", tmpvalue & 0x02); + return; + } + reg_w(dev, 0xa0, address, 0xb33a); + reg_w(dev, 0xa0, 0x02, 0xb339); + + tmpvalue = 0; + reg_r(dev, 0xa1, 0xb33b, &tmpvalue, 1); + while (retry-- && tmpvalue) { + reg_r(dev, 0xa1, 0xb33b, &tmpvalue, 1); +/* PDEBUG(D_PROBE, "Read again 0xb33b %d", tmpvalue); */ + msleep(1); + } + reg_r(dev, 0xa1, 0xb33e, &hdata, 1); + reg_r(dev, 0xa1, 0xb33d, &mdata, 1); + reg_r(dev, 0xa1, 0xb33c, &ldata, 1); + PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)", + hdata, mdata, ldata); + tmpvalue = 0; + reg_r(dev, 0xa1, 0xb334, &tmpvalue, 1); + if (tmpvalue == 0x02) + *value = (ldata << 8) + mdata; + else + *value = ldata; +} +static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + __u8 data; + __u16 value; + struct sensor_info *ptsensor_info; + + reg_r(dev, 0xa1, 0xbfcf, &data, 1); + PDEBUG(D_PROBE, "check sensor header %d", data); + for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { + ptsensor_info = &sensor_info_data[i]; + reg_w(dev, 0xa0, 0x02, 0xb334); + reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300); + reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300); + reg_w(dev, 0xa0, 0x01, 0xb308); + reg_w(dev, 0xa0, 0x0c, 0xb309); + reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); +/* PDEBUG(D_PROBE, + "check sensor VC032X -> %d Add -> ox%02X!", + i, ptsensor_info->I2cAdd); */ + reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); + vc032x_read_sensor_register(dev, ptsensor_info->IdAdd, &value); + if (value == ptsensor_info->VpId) { +/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!", + ptsensor_info->VpId); */ + return ptsensor_info->sensorId; + } + } + return -1; +} + +static __u8 i2c_write(struct usb_device *dev, + __u8 reg, __u8 *val, __u8 size) +{ + __u8 retbyte; + + if (size > 3 || size < 1) + return -EINVAL; + reg_r(dev, 0xa1, 0xb33f, &retbyte, 1); + reg_w(dev, 0xa0, size , 0xb334); + reg_w(dev, 0xa0, reg , 0xb33a); + switch (size) { + case 1: + reg_w(dev, 0xa0, val[0] , 0xb336); + break; + case 2: + reg_w(dev, 0xa0, val[0] , 0xb336); + reg_w(dev, 0xa0, val[1] , 0xb337); + break; + case 3: + reg_w(dev, 0xa0, val[0] , 0xb336); + reg_w(dev, 0xa0, val[1] , 0xb337); + reg_w(dev, 0xa0, val[2] , 0xb338); + break; + default: + reg_w(dev, 0xa0, 0x01, 0xb334); + return -EINVAL; + } + reg_w(dev, 0xa0, 0x01, 0xb339); + reg_r(dev, 0xa1, 0xb33b, &retbyte, 1); + return retbyte == 0; +} + +static void put_tab_to_reg(struct gspca_dev *gspca_dev, + __u8 *tab, __u8 tabsize, __u16 addr) +{ + int j; + __u16 ad = addr; + + for (j = 0; j < tabsize; j++) + reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); +} + +static void usb_exchange(struct gspca_dev *gspca_dev, + __u8 data[][4]) +{ + struct usb_device *dev = gspca_dev->dev; + int i = 0; + + for (;;) { + switch (data[i][3]) { + default: + return; + case 0xcc: /* normal write */ + reg_w(dev, 0xa0, data[i][2], + ((data[i][0])<<8) | data[i][1]); + break; + case 0xaa: /* i2c op */ + i2c_write(dev, data[i][1], &data[i][2], 1); + break; + case 0xbb: /* i2c op */ + i2c_write(dev, data[i][0], &data[i][1], 2); + break; + case 0xdd: + msleep(data[i][2] + 10); + break; + } + i++; + } + /*not reached*/ +} + +/* + "GammaT"=hex:04,17,31,4f,6a,83,99,ad,bf,ce,da,e5,ee,f5,fb,ff,ff + "MatrixT"=hex:60,f9,e5,e7,50,05,f3,e6,66 + */ + +static void vc0321_reset(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb04d); + reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb301); + msleep(100); + reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb003); + msleep(100); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u8 tmp2[4]; + int sensor; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x046d: /* Logitech Labtec */ +/* switch (product) { */ +/* case 0x0892: */ +/* case 0x0896: */ + sd->bridge = BRIDGE_VC0321; +/* break; */ +/* } */ + break; + case 0x0ac8: /* Vimicro z-star */ + switch (product) { + case 0x0321: + case 0x0328: + case 0xc001: + case 0xc002: + sd->bridge = BRIDGE_VC0321; + break; + case 0x0323: + sd->bridge = BRIDGE_VC0323; + break; + } + break; + case 0x17ef: /* Lenovo */ +/* switch (product) { */ +/* case 0x4802: * Lenovo MI1310_SOC */ + sd->bridge = BRIDGE_VC0323; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x02; + if (sd->bridge == BRIDGE_VC0321) { + cam->cam_mode = vc0321_mode; + cam->nmodes = sizeof vc0321_mode / sizeof vc0321_mode[0]; + } else { + cam->cam_mode = vc0323_mode; + cam->nmodes = sizeof vc0323_mode / sizeof vc0323_mode[0]; + } + + vc0321_reset(gspca_dev); + sensor = vc032x_probe_sensor(gspca_dev); + switch (sensor) { + case -1: + PDEBUG(D_PROBE, "Unknown sensor..."); + return -EINVAL; + case SENSOR_HV7131R: + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; + break; + case SENSOR_MI1310_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); + sd->sensor = SENSOR_MI1310_SOC; + break; + case SENSOR_MI1320: + PDEBUG(D_PROBE, "Find Sensor MI1320"); + sd->sensor = SENSOR_MI1320; + break; + case SENSOR_OV7660: + PDEBUG(D_PROBE, "Find Sensor OV7660"); + sd->sensor = SENSOR_OV7660; + break; + case SENSOR_OV7670: + PDEBUG(D_PROBE, "Find Sensor OV7670"); + sd->sensor = SENSOR_OV7670; + break; + case SENSOR_PO3130NC: + PDEBUG(D_PROBE, "Find Sensor PO3130NC"); + sd->sensor = SENSOR_PO3130NC; + break; + } + + sd->qindex = 7; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + + if (sd->bridge == BRIDGE_VC0321) { + reg_r(dev, 0x8a, 0, tmp2, 3); + reg_w(dev, 0x87, 0x00, 0x0f0f); + + reg_r(dev, 0x8b, 0, tmp2, 3); + reg_w(dev, 0x88, 0x00, 0x0202); + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static void setquality(struct gspca_dev *gspca_dev) +{ +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ +} + +static void setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static __u8 (*ov7660_freq_tb[3])[4] = + {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; + + if (sd->sensor != SENSOR_OV7660) + return; + usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* __u8 tmp2; */ + __u8 *GammaT = NULL; + __u8 *MatrixT = NULL; + int mode; + + /* Assume start use the good resolution from gspca_dev->mode */ + if (sd->bridge == BRIDGE_VC0321) { + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + switch (sd->sensor) { + case SENSOR_HV7131R: + GammaT = hv7131r_gamma; + MatrixT = hv7131r_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, hv7131r_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, hv7131r_initVGA_data); + } + break; + case SENSOR_OV7660: + GammaT = ov7660_gamma; + MatrixT = ov7660_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, ov7660_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, ov7660_initVGA_data); + } + break; + case SENSOR_OV7670: + /*GammaT = ov7660_gamma; */ + /*MatrixT = ov7660_matrix; */ + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, ov7670_initQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, ov7670_initVGA_JPG); + } + break; + case SENSOR_MI1310_SOC: + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi1310_socinitQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi1310_socinitVGA_JPG); + } + break; + case SENSOR_MI1320: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi1320_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi1320_initVGA_data); + } + break; + case SENSOR_PO3130NC: + GammaT = po3130_gamma; + MatrixT = po3130_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, po3130_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, po3130_initVGA_data); + } + usb_exchange(gspca_dev, po3130_rundata); + break; + default: + PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); + return; + } + if (GammaT && MatrixT) { + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); + put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); + + /* Seem SHARPNESS */ + /* + reg_w(gspca_dev->dev, 0xa0, 0x80, 0xb80a); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80b); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80e); + */ + /* all 0x40 ??? do nothing + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb822); + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb823); + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb824); + */ + /* Only works for HV7131R ?? + reg_r (gspca_dev->dev, 0xa1, 0xb881, &tmp2, 1); + reg_w(gspca_dev->dev, 0xa0, 0xfe01, 0xb881); + reg_w(gspca_dev->dev, 0xa0, 0x79, 0xb801); + */ + /* only hv7131r et ov7660 + reg_w(gspca_dev->dev, 0xa0, 0x20, 0xb827); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb826); * ISP_GAIN 80 + reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS + */ + /* set the led on 0x0892 0x0896 */ + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); + setquality(gspca_dev); + setautogain(gspca_dev); + setlightfreq(gspca_dev); + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0xa0, 0x09, 0xb003); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x89, 0xffff, 0xffff); +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +/* struct usb_device *dev = gspca_dev->dev; + __u8 buffread; + + reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0xa0, 0x09, 0xb303); + reg_w(dev, 0x89, 0xffff, 0xffff); +*/ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso pkt length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] == 0xff && data[1] == 0xd8) { + PDEBUG(D_PACK, + "vc032x header packet found len %d", len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + if (sd->bridge == BRIDGE_VC0321) { +#define VCHDRSZ 46 + data += VCHDRSZ; + len -= VCHDRSZ; +#undef VCHDRSZ + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (gspca_dev->streaming) + setautogain(gspca_dev); + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy(menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0892), DVNM("Logitech Orbicam")}, + {USB_DEVICE(0x046d, 0x0896), DVNM("Logitech Orbicam")}, + {USB_DEVICE(0x0ac8, 0x0321), DVNM("Vimicro generic vc0321")}, + {USB_DEVICE(0x0ac8, 0x0323), DVNM("Vimicro Vc0323")}, + {USB_DEVICE(0x0ac8, 0x0328), DVNM("A4Tech PK-130MG")}, + {USB_DEVICE(0x0ac8, 0xc001), DVNM("Sony embedded vimicro")}, + {USB_DEVICE(0x0ac8, 0xc002), DVNM("Sony embedded vimicro")}, + {USB_DEVICE(0x17ef, 0x4802), DVNM("Lenovo Vc0323+MI1310_SOC")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 03cc7fc58dbc..b767f32511bb 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -3,7 +3,7 @@ * Copyright (C) 2004 2005 2006 Michel Xhaard * mxhaard@magic.fr * - * V4L2 by Jean-François Moine + * V4L2 by Jean-Francois Moine * * 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 @@ -24,15 +24,14 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 13) -static const char version[] = "0.2.13"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; MODULE_AUTHOR("Michel Xhaard , " "Serge A. Suchkov "); MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); MODULE_LICENSE("GPL"); -static int lightfreq = 50; static int force_sensor = -1; #include "jpeg.h" @@ -41,10 +40,12 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char autogain; - unsigned char gamma; + __u8 brightness; + __u8 contrast; + __u8 gamma; + __u8 autogain; + __u8 lightfreq; + __u8 sharpness; char qindex; char sensor; /* Type of image sensor chip */ @@ -61,14 +62,13 @@ struct sd { #define SENSOR_OV7620 9 /*#define SENSOR_OV7648 9 - same values */ #define SENSOR_OV7630C 10 -/*#define SENSOR_free 11 */ -#define SENSOR_PAS106 12 -#define SENSOR_PB0330 13 -#define SENSOR_PO2030 14 -#define SENSOR_TAS5130CK 15 -#define SENSOR_TAS5130CXX 16 -#define SENSOR_TAS5130C_VF0250 17 -#define SENSOR_MAX 18 +#define SENSOR_PAS106 11 +#define SENSOR_PB0330 12 +#define SENSOR_PO2030 13 +#define SENSOR_TAS5130CK 14 +#define SENSOR_TAS5130CXX 15 +#define SENSOR_TAS5130C_VF0250 16 +#define SENSOR_MAX 17 unsigned short chip_revision; }; @@ -79,7 +79,12 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { #define SD_BRIGHTNESS 0 @@ -110,7 +115,21 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -#define SD_AUTOGAIN 2 +#define SD_GAMMA 2 + { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 1, + .maximum = 6, + .step = 1, + .default_value = 4, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, +#define SD_AUTOGAIN 3 { { .id = V4L2_CID_AUTOGAIN, @@ -124,19 +143,33 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define SD_GAMMA 3 +#define SD_FREQ 4 { { - .id = V4L2_CID_GAMMA, + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define SD_SHARPNESS 5 + { + { + .id = V4L2_CID_SHARPNESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 1, - .maximum = 6, + .name = "Sharpness", + .minimum = 0, + .maximum = 3, .step = 1, - .default_value = 4, + .default_value = 2, }, - .set = sd_setcontrast, - .get = sd_getgamma, + .set = sd_setsharpness, + .get = sd_getsharpness, }, }; @@ -211,11 +244,11 @@ static struct usb_action cs2102_Initial[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* 00 */ - {0xa0, 0x08, 0x01c6}, /* clock ? */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x24, 0x0120}, /* gamma 5 */ {0xa0, 0x44, 0x0121}, {0xa0, 0x64, 0x0122}, @@ -284,7 +317,7 @@ static struct usb_action cs2102_Initial[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action cs2102_InitialScale[] = { @@ -341,11 +374,11 @@ static struct usb_action cs2102_InitialScale[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* 00 */ - {0xa0, 0x08, 0x01c6}, /* clock ? */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x24, 0x0120}, /* gamma 5 */ {0xa0, 0x44, 0x0121}, {0xa0, 0x64, 0x0122}, @@ -414,7 +447,7 @@ static struct usb_action cs2102_InitialScale[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action cs2102_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -439,7 +472,7 @@ static struct usb_action cs2102_50HZ[] = { {0xa0, 0x8c, 0x001d}, /* 00,1d,8c,cc */ {0xa0, 0xb0, 0x001e}, /* 00,1e,b0,cc */ {0xa0, 0xd0, 0x001f}, /* 00,1f,d0,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -464,7 +497,7 @@ static struct usb_action cs2102_50HZScale[] = { {0xa0, 0x93, 0x001d}, /* 00,1d,93,cc */ {0xa0, 0xb0, 0x001e}, /* 00,1e,b0,cc */ {0xa0, 0xd0, 0x001f}, /* 00,1f,d0,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -489,7 +522,7 @@ static struct usb_action cs2102_60HZ[] = { {0xa0, 0x5d, 0x001d}, /* 00,1d,5d,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -514,7 +547,7 @@ static struct usb_action cs2102_60HZScale[] = { {0xa0, 0xb7, 0x001d}, /* 00,1d,b7,cc */ {0xa0, 0xd0, 0x001e}, /* 00,1e,d0,cc */ {0xa0, 0xe8, 0x001f}, /* 00,1f,e8,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -539,7 +572,7 @@ static struct usb_action cs2102_NoFliker[] = { {0xa0, 0x59, 0x001d}, /* 00,1d,59,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xc8, 0x001f}, /* 00,1f,c8,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_NoFlikerScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -564,7 +597,7 @@ static struct usb_action cs2102_NoFlikerScale[] = { {0xa0, 0x59, 0x001d}, /* 00,1d,59,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xc8, 0x001f}, /* 00,1f,c8,cc */ - {0, 0, 0} + {} }; /* CS2102_KOCOM */ @@ -676,8 +709,8 @@ static struct usb_action cs2102K_Initial[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x4c, 0x0118}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -824,7 +857,7 @@ static struct usb_action cs2102K_Initial[] = { {0xa0, 0x00, 0x01a7}, {0xa0, 0x04, 0x01a7}, {0xa0, 0x00, 0x01a7}, - {0, 0, 0} + {} }; static struct usb_action cs2102K_InitialScale[] = { @@ -936,8 +969,8 @@ static struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x4c, 0x0118}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -1137,8 +1170,8 @@ static struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x4c, 0x0118}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -1401,7 +1434,7 @@ static struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x04, 0x01a7}, {0xa0, 0x00, 0x01a7}, {0xa0, 0x04, 0x01a7}, - {0, 0, 0} + {} }; static struct usb_action gc0305_Initial[] = { /* 640x480 */ @@ -1466,7 +1499,7 @@ static struct usb_action gc0305_Initial[] = { /* 640x480 */ {0xa0, 0x40, 0x0117}, /* 01,17,40,cc */ {0xa0, 0x52, 0x0118}, /* 01,18,52,cc */ {0xa0, 0x03, 0x0113}, /* 01,13,03,cc */ - {0,0,0} + {} }; static struct usb_action gc0305_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, 0x0000}, /* 00,00,01,cc */ @@ -1529,7 +1562,7 @@ static struct usb_action gc0305_InitialScale[] = { /* 320x240 */ {0xa0, 0x40, 0x0117}, /* 01,17,40,cc */ {0xa0, 0x52, 0x0118}, /* 01,18,52,cc */ {0xa0, 0x03, 0x0113}, /* 01,13,03,cc */ - {0,0,0} + {} }; static struct usb_action gc0305_50HZ[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ @@ -1552,7 +1585,7 @@ static struct usb_action gc0305_50HZ[] = { {0xa0, 0x60, 0x011d}, /* 01,1d,60,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ /* {0xa0, 0x85, 0x018d}, * 01,8d,85,cc * * if 640x480 */ - {0,0,0} + {} }; static struct usb_action gc0305_60HZ[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ @@ -1575,7 +1608,7 @@ static struct usb_action gc0305_60HZ[] = { {0xa0, 0x60, 0x011d}, /* 01,1d,60,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ {0xa0, 0x80, 0x018d}, /* 01,8d,80,cc */ - {0,0,0} + {} }; static struct usb_action gc0305_NoFliker[] = { @@ -1598,7 +1631,7 @@ static struct usb_action gc0305_NoFliker[] = { {0xa0, 0x60, 0x011d}, /* 01,1d,60,cc */ {0xa0, 0x03, 0x0180}, /* 01,80,03,cc */ {0xa0, 0x80, 0x018d}, /* 01,8d,80,cc */ - {0,0,0} + {} }; /* play poker with registers at your own risk !! */ @@ -1647,11 +1680,11 @@ static struct usb_action hdcs2020xx_Initial[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x04, 0x01c6}, + {0xa0, 0x04, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, 0x01cb}, + {0xa0, 0x07, 0x01cb}, /* sharpness- */ {0xa0, 0x11, 0x0120}, /* gamma ~4 */ {0xa0, 0x37, 0x0121}, {0xa0, 0x58, 0x0122}, @@ -1744,7 +1777,7 @@ static struct usb_action hdcs2020xx_Initial[] = { {0xa1, 0x01, 0x0118}, /* {0xa0, 0x02, 0x0008}, */ {0xa0, 0x00, 0x0007}, - {0, 0, 0} + {} }; static struct usb_action hdcs2020xx_InitialScale[] = { @@ -1792,11 +1825,11 @@ static struct usb_action hdcs2020xx_InitialScale[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x04, 0x01c6}, + {0xa0, 0x04, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, 0x01cb}, + {0xa0, 0x07, 0x01cb}, /* sharpness- */ {0xa0, 0x11, 0x0120}, /* gamma ~4*/ {0xa0, 0x37, 0x0121}, {0xa0, 0x58, 0x0122}, @@ -1887,7 +1920,7 @@ static struct usb_action hdcs2020xx_InitialScale[] = { /* {0xa0, 0x02, 0x0008}, */ {0xa0, 0x00, 0x0007}, /* {0xa0, 0x18, 0x00fe}, */ - {0, 0, 0} + {} }; static struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x01, 0x0000}, @@ -1942,11 +1975,11 @@ static struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -2019,7 +2052,7 @@ static struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action hdcs2020xb_InitialScale[] = { {0xa0, 0x01, 0x0000}, @@ -2072,11 +2105,11 @@ static struct usb_action hdcs2020xb_InitialScale[] = { {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -2147,7 +2180,7 @@ static struct usb_action hdcs2020xb_InitialScale[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action hdcs2020b_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -2168,7 +2201,7 @@ static struct usb_action hdcs2020b_50HZ[] = { {0xa0, 0x05, 0x001d}, /* 00,1d,05,cc */ {0xa0, 0x1a, 0x001e}, /* 00,1e,1a,cc */ {0xa0, 0x2f, 0x001f}, /* 00,1f,2f,cc */ - {0, 0, 0} + {} }; static struct usb_action hdcs2020b_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -2189,7 +2222,7 @@ static struct usb_action hdcs2020b_60HZ[] = { {0xa0, 0x04, 0x001d}, /* 00,1d,04,cc */ {0xa0, 0x18, 0x001e}, /* 00,1e,18,cc */ {0xa0, 0x2c, 0x001f}, /* 00,1f,2c,cc */ - {0, 0, 0} + {} }; static struct usb_action hdcs2020b_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -2210,7 +2243,7 @@ static struct usb_action hdcs2020b_NoFliker[] = { {0xa0, 0x04, 0x001d}, /* 00,1d,04,cc */ {0xa0, 0x17, 0x001e}, /* 00,1e,17,cc */ {0xa0, 0x2a, 0x001f}, /* 00,1f,2a,cc */ - {0, 0, 0} + {} }; static struct usb_action hv7131bxx_Initial[] = { @@ -2266,11 +2299,11 @@ static struct usb_action hv7131bxx_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -2318,7 +2351,7 @@ static struct usb_action hv7131bxx_Initial[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, /* {0xa0, 0x02, 0x0008}, */ - {0, 0, 0} + {} }; static struct usb_action hv7131bxx_InitialScale[] = { @@ -2373,11 +2406,11 @@ static struct usb_action hv7131bxx_InitialScale[] = { {0xa1, 0x01, 0x0096}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -2424,7 +2457,7 @@ static struct usb_action hv7131bxx_InitialScale[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, /* {0xa0, 0x02, 0x0008}, */ - {0, 0, 0} + {} }; static struct usb_action hv7131cxx_Initial[] = { @@ -2478,11 +2511,11 @@ static struct usb_action hv7131cxx_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x60, 0x010a}, /* matrix */ {0xa0, 0xf0, 0x010b}, @@ -2518,7 +2551,7 @@ static struct usb_action hv7131cxx_Initial[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action hv7131cxx_InitialScale[] = { @@ -2577,11 +2610,11 @@ static struct usb_action hv7131cxx_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x60, 0x010a}, /* matrix */ {0xa0, 0xf0, 0x010b}, @@ -2619,7 +2652,7 @@ static struct usb_action hv7131cxx_InitialScale[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action icm105axx_Initial[] = { @@ -2743,11 +2776,11 @@ static struct usb_action icm105axx_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x52, 0x010a}, /* matrix */ {0xa0, 0xf7, 0x010b}, {0xa0, 0xf7, 0x010c}, @@ -2796,7 +2829,7 @@ static struct usb_action icm105axx_Initial[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action icm105axx_InitialScale[] = { @@ -2924,11 +2957,11 @@ static struct usb_action icm105axx_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x52, 0x010a}, /* matrix */ {0xa0, 0xf7, 0x010b}, @@ -2976,7 +3009,7 @@ static struct usb_action icm105axx_InitialScale[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action icm105a_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3007,7 +3040,7 @@ static struct usb_action icm105a_50HZ[] = { {0xa0, 0xd8, 0x001e}, /* 00,1e,d8,cc */ {0xa0, 0xea, 0x001f}, /* 00,1f,ea,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3040,7 +3073,7 @@ static struct usb_action icm105a_50HZScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x00, 0x01a7}, /* 01,a7,00,cc */ {0xa0, 0xc0, 0x01a8}, /* 01,a8,c0,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3071,7 +3104,7 @@ static struct usb_action icm105a_60HZ[] = { {0xa0, 0xd4, 0x001e}, /* 00,1e,d4,cc */ {0xa0, 0xe8, 0x001f}, /* 00,1f,e8,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3104,7 +3137,7 @@ static struct usb_action icm105a_60HZScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x00, 0x01a7}, /* 01,a7,00,cc */ {0xa0, 0xc0, 0x01a8}, /* 01,a8,c0,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3135,7 +3168,7 @@ static struct usb_action icm105a_NoFliker[] = { {0xa0, 0xd4, 0x001e}, /* 00,1e,d4,cc */ {0xa0, 0xe8, 0x001f}, /* 00,1f,e8,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_NoFlikerScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3168,7 +3201,7 @@ static struct usb_action icm105a_NoFlikerScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x00, 0x01a7}, /* 01,a7,00,cc */ {0xa0, 0xc0, 0x01a8}, /* 01,a8,c0,cc */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_InitialScale[] = { @@ -3288,7 +3321,7 @@ static struct usb_action MC501CB_InitialScale[] = { {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_Initial[] = { /* 320x240 */ @@ -3407,7 +3440,7 @@ static struct usb_action MC501CB_Initial[] = { /* 320x240 */ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_50HZ[] = { @@ -3424,7 +3457,7 @@ static struct usb_action MC501CB_50HZ[] = { {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_50HZScale[] = { @@ -3441,7 +3474,7 @@ static struct usb_action MC501CB_50HZScale[] = { {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_60HZ[] = { @@ -3458,7 +3491,7 @@ static struct usb_action MC501CB_60HZ[] = { {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_60HZScale[] = { @@ -3475,7 +3508,7 @@ static struct usb_action MC501CB_60HZScale[] = { {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_NoFliker[] = { @@ -3492,7 +3525,7 @@ static struct usb_action MC501CB_NoFliker[] = { {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_NoFlikerScale[] = { @@ -3504,7 +3537,7 @@ static struct usb_action MC501CB_NoFlikerScale[] = { {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */ @@ -3575,7 +3608,7 @@ static struct usb_action OV7620_mode0[] = { {0xa0, 0x40, 0x011d}, /* 01,1d,40,cc */ {0xa0, 0x02, 0x0180}, /* 01,80,02,cc */ {0xa0, 0x50, 0x01a8}, /* 01,a8,50,cc */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */ @@ -3646,7 +3679,7 @@ static struct usb_action OV7620_mode1[] = { {0xa0, 0x50, 0x011d}, /* 01,1d,50,cc */ {0xa0, 0x02, 0x0180}, /* 01,80,02,cc */ {0xa0, 0x50, 0x01a8}, /* 01,a8,50,cc */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%\AE,50HZ */ @@ -3665,7 +3698,7 @@ static struct usb_action OV7620_50HZ[] = { {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ /* {0xa0, 0x40, 0x0002}, * 00,02,40,cc - if mode0 (640x480) */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%\AE,60HZ */ @@ -3687,7 +3720,7 @@ static struct usb_action OV7620_60HZ[] = { /* ?? in gspca v1, it was {0xa0, 0x00, 0x0039}, * 00,00,00,dd * {0xa1, 0x01, 0x0037}, */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%\AE,NoFliker */ @@ -3707,7 +3740,7 @@ static struct usb_action OV7620_NoFliker[] = { /* ?? was {0xa0, 0x00, 0x0039}, * 00,00,00,dd * {0xa1, 0x01, 0x0037}, */ - {0, 0, 0} + {} }; static struct usb_action ov7630c_Initial[] = { @@ -3795,14 +3828,11 @@ static struct usb_action ov7630c_Initial[] = { /* 0x03, */ {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, -/* 0x05, */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, -/* 0x07, */ {0xa1, 0x01, 0x01c9}, -/* 0x0f, */ {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x01, 0x0120}, /* gamma 2 ?*/ {0xa0, 0x0c, 0x0121}, {0xa0, 0x1f, 0x0122}, @@ -3867,7 +3897,7 @@ static struct usb_action ov7630c_Initial[] = { {0xaa, 0x13, 0x0083}, /* 40 */ {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action ov7630c_InitialScale[] = { @@ -3954,14 +3984,11 @@ static struct usb_action ov7630c_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x16, 0x0120}, /* gamma ~4 */ {0xa0, 0x3a, 0x0121}, {0xa0, 0x5b, 0x0122}, @@ -4027,7 +4054,7 @@ static struct usb_action ov7630c_InitialScale[] = { {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action pas106b_Initial_com[] = { @@ -4041,7 +4068,7 @@ static struct usb_action pas106b_Initial_com[] = { {0xa0, 0x03, 0x003a}, {0xa0, 0x0c, 0x003b}, {0xa0, 0x04, 0x0038}, - {0, 0, 0} + {} }; static struct usb_action pas106b_Initial[] = { /* 176x144 */ @@ -4099,10 +4126,8 @@ static struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Unknown */ - {0xa0, 0x08, 0x01c6}, -/* Sharpness */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ /* Other registers */ {0xa0, 0x0d, 0x0100}, /* OperationMode */ /* Auto exposure and white balance */ @@ -4113,9 +4138,8 @@ static struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Sharpness */ - {0xa0, 0x08, 0x01c6}, /* Sharpness00 */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x58, 0x010a}, /* matrix */ {0xa0, 0xf4, 0x010b}, @@ -4162,7 +4186,7 @@ static struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0x40, 0x0116}, /* RGain */ {0xa0, 0x40, 0x0117}, /* GGain */ {0xa0, 0x40, 0x0118}, /* BGain */ - {0, 0, 0} + {} }; static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ @@ -4221,10 +4245,8 @@ static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Unknown */ - {0xa0, 0x08, 0x01c6}, -/* Sharpness */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ /* Other registers */ {0xa0, 0x0d, 0x0100}, /* OperationMode */ /* Auto exposure and white balance */ @@ -4235,9 +4257,8 @@ static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Sharpness */ - {0xa0, 0x08, 0x01c6}, /* Sharpness00 */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x58, 0x010a}, /* matrix */ {0xa0, 0xf4, 0x010b}, @@ -4289,7 +4310,7 @@ static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */ {0xa0, 0xff, 0x0018}, /* Frame adjust */ - {0, 0, 0} + {} }; static struct usb_action pas106b_50HZ[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4305,7 +4326,7 @@ static struct usb_action pas106b_50HZ[] = { {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */ {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */ {0xa0, 0x04, 0x01a9}, /* 01,a9,04,cc */ - {0, 0, 0} + {} }; static struct usb_action pas106b_60HZ[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4321,7 +4342,7 @@ static struct usb_action pas106b_60HZ[] = { {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */ {0xa0, 0x04, 0x01a9}, /* 01,a9,04,cc */ - {0, 0, 0} + {} }; static struct usb_action pas106b_NoFliker[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4337,10 +4358,9 @@ static struct usb_action pas106b_NoFliker[] = { {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */ {0xa0, 0x00, 0x01a9}, /* 01,a9,00,cc */ - {0, 0, 0} + {} }; -/* Aurelien setting from snoop */ static struct usb_action pb03303x_Initial[] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x03, 0x0008}, @@ -4411,11 +4431,11 @@ static struct usb_action pb03303x_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -4484,7 +4504,7 @@ static struct usb_action pb03303x_Initial[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action pb03303x_InitialScale[] = { @@ -4559,11 +4579,11 @@ static struct usb_action pb03303x_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, @@ -4633,7 +4653,7 @@ static struct usb_action pb03303x_InitialScale[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action pb0330xx_Initial[] = { {0xa1, 0x01, 0x0008}, @@ -4701,11 +4721,11 @@ static struct usb_action pb0330xx_Initial[] = { {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -4747,7 +4767,7 @@ static struct usb_action pb0330xx_Initial[] = { {0xa1, 0x01, 0x0007}, /* {0xa0, 0x30, 0x0007}, */ /* {0xa0, 0x00, 0x0007}, */ - {0, 0, 0} + {} }; static struct usb_action pb0330xx_InitialScale[] = { @@ -4816,11 +4836,11 @@ static struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -4861,7 +4881,7 @@ static struct usb_action pb0330xx_InitialScale[] = { {0xa1, 0x01, 0x0007}, /* {0xa0, 0x30, 0x0007}, */ /* {0xa0, 0x00, 0x0007}, */ - {0, 0, 0} + {} }; static struct usb_action pb0330_50HZ[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4877,7 +4897,7 @@ static struct usb_action pb0330_50HZ[] = { {0xa0, 0x68, 0x001d}, /* 00,1d,68,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xc8, 0x001f}, /* 00,1f,c8,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4894,7 +4914,7 @@ static struct usb_action pb0330_50HZScale[] = { {0xa0, 0xe5, 0x001d}, /* 00,1d,e5,cc */ {0xa0, 0xf0, 0x001e}, /* 00,1e,f0,cc */ {0xa0, 0xf8, 0x001f}, /* 00,1f,f8,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4911,7 +4931,7 @@ static struct usb_action pb0330_60HZ[] = { {0xa0, 0x43, 0x001d}, /* 00,1d,43,cc */ {0xa0, 0x50, 0x001e}, /* 00,1e,50,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4928,7 +4948,7 @@ static struct usb_action pb0330_60HZScale[] = { {0xa0, 0x41, 0x001d}, /* 00,1d,41,cc */ {0xa0, 0x50, 0x001e}, /* 00,1e,50,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4945,7 +4965,7 @@ static struct usb_action pb0330_NoFliker[] = { {0xa0, 0x09, 0x001d}, /* 00,1d,09,cc */ {0xa0, 0x40, 0x001e}, /* 00,1e,40,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_NoFlikerScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4962,7 +4982,7 @@ static struct usb_action pb0330_NoFlikerScale[] = { {0xa0, 0x09, 0x001d}, /* 00,1d,09,cc */ {0xa0, 0x40, 0x001e}, /* 00,1e,40,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; /* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */ @@ -5039,7 +5059,7 @@ static struct usb_action PO2030_mode0[] = { {0xa0, 0x08, 0x0301}, /* 03,01,08,cc */ {0xa0, 0x7a, 0x0116}, /* 01,16,7a,cc */ {0xa0, 0x4a, 0x0118}, /* 01,18,4a,cc */ - {0, 0, 0} + {} }; /* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */ @@ -5116,7 +5136,7 @@ static struct usb_action PO2030_mode1[] = { {0xa0, 0x08, 0x0301}, /* 03,01,08,cc */ {0xa0, 0x7a, 0x0116}, /* 01,16,7a,cc */ {0xa0, 0x4a, 0x0118}, /* 01,18,4a,cc */ - {0, 0, 0} + {} }; static struct usb_action PO2030_50HZ[] = { @@ -5138,7 +5158,7 @@ static struct usb_action PO2030_50HZ[] = { {0xa0, 0x88, 0x018d}, /* 01,8d,88,cc */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ - {0, 0, 0} + {} }; static struct usb_action PO2030_60HZ[] = { @@ -5160,7 +5180,7 @@ static struct usb_action PO2030_60HZ[] = { {0xa0, 0x88, 0x018d}, /* 01,8d,88,cc */ /* win: 01,8d,80 */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ - {0, 0, 0} + {} }; static struct usb_action PO2030_NoFliker[] = { @@ -5171,7 +5191,7 @@ static struct usb_action PO2030_NoFliker[] = { {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */ {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ - {0, 0, 0} + {} }; /* TEST */ @@ -5302,8 +5322,8 @@ static struct usb_action tas5130CK_Initial[] = { {0xa0, 0x03, 0x0111}, {0xa0, 0x51, 0x0112}, {0xa0, 0x03, 0x0008}, - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x38, 0x0120}, /* gamma > 5 */ {0xa0, 0x51, 0x0121}, {0xa0, 0x6e, 0x0122}, @@ -5375,7 +5395,7 @@ static struct usb_action tas5130CK_Initial[] = { {0xa0, 0x15, 0x01ae}, {0xa0, 0x40, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action tas5130CK_InitialScale[] = { @@ -5505,8 +5525,8 @@ static struct usb_action tas5130CK_InitialScale[] = { {0xa0, 0x03, 0x0111}, {0xa0, 0x51, 0x0112}, {0xa0, 0x03, 0x0008}, - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x38, 0x0120}, /* gamma > 5 */ {0xa0, 0x51, 0x0121}, {0xa0, 0x6e, 0x0122}, @@ -5583,7 +5603,7 @@ static struct usb_action tas5130CK_InitialScale[] = { {0xa0, 0x02, 0x0008}, {0xa0, 0x00, 0x0007}, {0xa0, 0x03, 0x0008}, - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_Initial[] = { @@ -5625,11 +5645,11 @@ static struct usb_action tas5130cxx_Initial[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x68, 0x010a}, /* matrix */ {0xa0, 0xec, 0x010b}, @@ -5673,7 +5693,7 @@ static struct usb_action tas5130cxx_Initial[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x01, 0x0000}, @@ -5718,11 +5738,11 @@ static struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x03, 0x0008}, {0xa1, 0x01, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x68, 0x010a}, /* matrix */ {0xa0, 0xec, 0x010b}, @@ -5763,7 +5783,7 @@ static struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5786,7 +5806,7 @@ static struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0xea, 0x001f}, /* 00,1f,ea,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5809,7 +5829,7 @@ static struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0xf8, 0x001f}, /* 00,1f,f8,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5832,7 +5852,7 @@ static struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5855,7 +5875,7 @@ static struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5878,7 +5898,7 @@ static struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x02, 0x009f}, /* 00,9f,02,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_NoFlikerScale[] = { @@ -5902,7 +5922,7 @@ static struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x02, 0x009f}, /* 00,9f,02,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130c_vf0250_Initial[] = { @@ -5966,7 +5986,7 @@ static struct usb_action tas5130c_vf0250_Initial[] = { {0xa0, 0x60, 0x01a8}, /* 01,a8,60,cc, */ {0xa0, 0x61, 0x0116}, /* 01,16,61,cc, */ {0xa0, 0x65, 0x0118}, /* 01,18,65,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130c_vf0250_InitialScale[] = { @@ -6030,7 +6050,7 @@ static struct usb_action tas5130c_vf0250_InitialScale[] = { {0xa0, 0x60, 0x01a8}, /* 01,a8,60,cc, */ {0xa0, 0x61, 0x0116}, /* 01,16,61,cc, */ {0xa0, 0x65, 0x0118}, /* 01,18,65,cc */ - {0, 0, 0} + {} }; /* "50HZ" light frequency banding filter */ static struct usb_action tas5130c_vf0250_50HZ[] = { @@ -6054,7 +6074,7 @@ static struct usb_action tas5130c_vf0250_50HZ[] = { {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,8d,78,cc */ - {0, 0, 0} + {} }; /* "50HZScale" light frequency banding filter */ @@ -6079,7 +6099,7 @@ static struct usb_action tas5130c_vf0250_50HZScale[] = { {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,8d,78,cc */ - {0, 0, 0} + {} }; /* "60HZ" light frequency banding filter */ @@ -6104,7 +6124,7 @@ static struct usb_action tas5130c_vf0250_60HZ[] = { {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,8d,78,cc */ - {0, 0, 0} + {} }; /* "60HZScale" light frequency banding ilter */ @@ -6129,7 +6149,7 @@ static struct usb_action tas5130c_vf0250_60HZScale[] = { {0xa0, 0x58, 0x011d}, /* 01,d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,d,78,cc */ - {0, 0, 0} + {} }; /* "NoFliker" light frequency banding flter */ @@ -6152,7 +6172,7 @@ static struct usb_action tas5130c_vf0250_NoFliker[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc, */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x03, 0x0180}, /* 01,80,03,cc */ - {0, 0, 0} + {} }; /* "NoFlikerScale" light frequency banding filter */ @@ -6175,7 +6195,7 @@ static struct usb_action tas5130c_vf0250_NoFlikerScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc, */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x03, 0x0180}, /* 01,80,03,cc */ - {0, 0, 0} + {} }; static void reg_r_i(struct usb_device *dev, @@ -6325,7 +6345,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_PO2030: return; } -/*fixme: is it really 011d 018d for all other sensors? */ +/*fixme: is it really write to 011d and 018d for all other sensors? */ brightness = sd->brightness; reg_w(gspca_dev->dev, brightness, 0x011d); if (brightness < 0x70) @@ -6348,20 +6368,7 @@ static void setsharpness(struct gspca_dev *gspca_dev) {0x10, 0x1e} }; - switch (sd->sensor) { - case SENSOR_GC0305: - sharpness = 3; - break; - case SENSOR_OV7620: - sharpness = 2; - break; - case SENSOR_PO2030: - sharpness = 0; - break; - default: - return; - } -/*fixme: sharpness set by V4L2_CID_SATURATION?*/ + sharpness = sd->sharpness; reg_w(dev, sharpness_tb[sharpness][0], 0x01c6); reg_r(dev, 0x01c8, &retbyte); reg_r(dev, 0x01c9, &retbyte); @@ -6411,7 +6418,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) static __u8 Tgradient_5[16] = {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02}; - static __u8 Tgamma_6[16] = /* ?? was gama 5 */ + static __u8 Tgamma_6[16] = /* ?? was gamma 5 */ {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3, 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}; static __u8 Tgradient_6[16] = @@ -6425,7 +6432,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) 0, Tgradient_1, Tgradient_2, Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6 }; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG __u8 v[16]; #endif @@ -6443,7 +6450,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) else if (g <= 0) g = 1; reg_w(dev, g, 0x0120 + i); /* gamma */ -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) v[i] = g; #endif @@ -6463,7 +6470,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) g = 1; } reg_w(dev, g, 0x0130 + i); /* gradient */ -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) v[i] = g; #endif @@ -6488,7 +6495,7 @@ static void setquality(struct gspca_dev *gspca_dev) return; } /*fixme: is it really 0008 0007 0018 for all other sensors? */ - quality = sd->qindex & 0x0f; + quality = sd->qindex; reg_w(dev, quality, 0x0008); frxt = 0x30; reg_w(dev, frxt, 0x0007); @@ -6525,25 +6532,25 @@ static int setlightfreq(struct gspca_dev *gspca_dev) struct usb_action *zc3_freq; static struct usb_action *freq_tb[SENSOR_MAX][6] = { /* SENSOR_CS2102 0 */ - {cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale, - cs2102_NoFliker, cs2102_NoFlikerScale}, + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, /* SENSOR_CS2102K 1 */ - {cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale, - cs2102_NoFliker, cs2102_NoFlikerScale}, + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, /* SENSOR_GC0305 2 */ - {gc0305_50HZ, gc0305_50HZ, - gc0305_60HZ, gc0305_60HZ, - gc0305_NoFliker, gc0305_NoFliker}, + {gc0305_NoFliker, gc0305_NoFliker, + gc0305_50HZ, gc0305_50HZ, + gc0305_60HZ, gc0305_60HZ}, /* SENSOR_HDCS2020 3 */ {0, 0, 0, 0, 0, 0}, /* SENSOR_HDCS2020b 4 */ - {hdcs2020b_50HZ, hdcs2020b_50HZ, - hdcs2020b_60HZ, hdcs2020b_60HZ, - hdcs2020b_NoFliker, hdcs2020b_NoFliker}, + {hdcs2020b_NoFliker, hdcs2020b_NoFliker, + hdcs2020b_50HZ, hdcs2020b_50HZ, + hdcs2020b_60HZ, hdcs2020b_60HZ}, /* SENSOR_HV7131B 5 */ {0, 0, 0, 0, @@ -6553,66 +6560,48 @@ static int setlightfreq(struct gspca_dev *gspca_dev) 0, 0, 0, 0}, /* SENSOR_ICM105A 7 */ - {icm105a_50HZ, icm105a_50HZScale, - icm105a_60HZ, icm105a_60HZScale, - icm105a_NoFliker, icm105a_NoFlikerScale}, + {icm105a_NoFliker, icm105a_NoFlikerScale, + icm105a_50HZ, icm105a_50HZScale, + icm105a_60HZ, icm105a_60HZScale}, /* SENSOR_MC501CB 8 */ - {MC501CB_50HZ, MC501CB_50HZScale, - MC501CB_60HZ, MC501CB_60HZScale, - MC501CB_NoFliker, MC501CB_NoFlikerScale}, + {MC501CB_NoFliker, MC501CB_NoFlikerScale, + MC501CB_50HZ, MC501CB_50HZScale, + MC501CB_60HZ, MC501CB_60HZScale}, /* SENSOR_OV7620 9 */ - {OV7620_50HZ, OV7620_50HZ, - OV7620_60HZ, OV7620_60HZ, - OV7620_NoFliker, OV7620_NoFliker}, + {OV7620_NoFliker, OV7620_NoFliker, + OV7620_50HZ, OV7620_50HZ, + OV7620_60HZ, OV7620_60HZ}, /* SENSOR_OV7630C 10 */ {0, 0, 0, 0, 0, 0}, -/* SENSOR_free 11 */ - {0, 0, - 0, 0, - 0, 0}, -/* SENSOR_PAS106 12 */ - {pas106b_50HZ, pas106b_50HZ, - pas106b_60HZ, pas106b_60HZ, - pas106b_NoFliker, pas106b_NoFliker}, -/* SENSOR_PB0330 13 */ - {pb0330_50HZ, pb0330_50HZScale, - pb0330_60HZ, pb0330_60HZScale, - pb0330_NoFliker, pb0330_NoFlikerScale}, -/* SENSOR_PO2030 14 */ - {PO2030_50HZ, PO2030_50HZ, - PO2030_60HZ, PO2030_60HZ, - PO2030_NoFliker, PO2030_NoFliker}, -/* SENSOR_TAS5130CK 15 */ - {tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale, - tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale}, -/* SENSOR_TAS5130CXX 16 */ - {tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale, - tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale}, -/* SENSOR_TAS5130C_VF0250 17 */ - {tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, - tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale, - tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale}, +/* SENSOR_PAS106 11 */ + {pas106b_NoFliker, pas106b_NoFliker, + pas106b_50HZ, pas106b_50HZ, + pas106b_60HZ, pas106b_60HZ}, +/* SENSOR_PB0330 12 */ + {pb0330_NoFliker, pb0330_NoFlikerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, +/* SENSOR_PO2030 13 */ + {PO2030_NoFliker, PO2030_NoFliker, + PO2030_50HZ, PO2030_50HZ, + PO2030_60HZ, PO2030_60HZ}, +/* SENSOR_TAS5130CK 14 */ + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, +/* SENSOR_TAS5130CXX 15 */ + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, +/* SENSOR_TAS5130C_VF0250 16 */ + {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, + tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, + tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, }; - switch (lightfreq) { - case 50: - i = 0; - break; - case 60: - i = 2; - break; - default: - PDEBUG(D_ERR, "Invalid light freq value %d", lightfreq); - lightfreq = 0; /* set to default filter value */ - /* fall thru */ - case 0: - i = 4; - break; - } + i = sd->lightfreq * 2; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; if (!mode) i++; /* 640x480 */ @@ -6622,13 +6611,13 @@ static int setlightfreq(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_GC0305: if (mode /* if 320x240 */ - && lightfreq == 50) + && sd->lightfreq == 1) /* and 50Hz */ reg_w(gspca_dev->dev, 0x85, 0x018d); /* win: 0x80, 0x018d */ break; case SENSOR_OV7620: if (!mode) { /* if 640x480 */ - if (lightfreq != 0) /* 50 or 60 Hz */ + if (sd->lightfreq != 0) /* and 50 or 60 Hz */ reg_w(gspca_dev->dev, 0x40, 0x0002); else reg_w(gspca_dev->dev, 0x44, 0x0002); @@ -6653,9 +6642,9 @@ static void setautogain(struct gspca_dev *gspca_dev) static void send_unknown(struct usb_device *dev, int sensor) { + reg_w(dev, 0x01, 0x0000); /* led off */ switch (sensor) { case SENSOR_PAS106: - reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x03, 0x003a); reg_w(dev, 0x0c, 0x003b); reg_w(dev, 0x08, 0x0038); @@ -6664,7 +6653,6 @@ static void send_unknown(struct usb_device *dev, int sensor) case SENSOR_OV7620: case SENSOR_PB0330: case SENSOR_PO2030: - reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x0d, 0x003a); reg_w(dev, 0x02, 0x003b); reg_w(dev, 0x00, 0x0038); @@ -6817,7 +6805,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ reg_w(dev, 0x02, 0x0010); - reg_r(dev, 0x0010, &retbyte); + reg_r(dev, 0x10, &retbyte); reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x00, 0x0010); reg_w(dev, 0x01, 0x0001); @@ -6964,7 +6952,7 @@ static int sd_config(struct gspca_dev *gspca_dev, int sensor; __u8 bsensor; int vga = 1; /* 1: vga, 0: sif */ - static unsigned char gamma[SENSOR_MAX] = { + static __u8 gamma[SENSOR_MAX] = { 5, /* SENSOR_CS2102 0 */ 5, /* SENSOR_CS2102K 1 */ 4, /* SENSOR_GC0305 2 */ @@ -6976,16 +6964,16 @@ static int sd_config(struct gspca_dev *gspca_dev, 4, /* SENSOR_MC501CB 8 */ 3, /* SENSOR_OV7620 9 */ 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_free 11 */ - 4, /* SENSOR_PAS106 12 */ - 4, /* SENSOR_PB0330 13 */ - 4, /* SENSOR_PO2030 14 */ - 4, /* SENSOR_TAS5130CK 15 */ - 4, /* SENSOR_TAS5130CXX 16 */ - 3, /* SENSOR_TAS5130C_VF0250 17 */ + 4, /* SENSOR_PAS106 11 */ + 4, /* SENSOR_PB0330 12 */ + 4, /* SENSOR_PO2030 13 */ + 4, /* SENSOR_TAS5130CK 14 */ + 4, /* SENSOR_TAS5130CXX 15 */ + 3, /* SENSOR_TAS5130C_VF0250 16 */ }; /* define some sensors from the vendor/product */ + sd->sharpness = 2; switch (id->idVendor) { case 0x041e: /* Creative */ switch (id->idProduct) { @@ -7055,8 +7043,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = SENSOR_ICM105A; break; case 0x0e: - PDEBUG(D_PROBE, "Find Sensor PAS202BCB"); + PDEBUG(D_PROBE, "Find Sensor HDCS2020"); sd->sensor = SENSOR_HDCS2020; + sd->sharpness = 1; break; case 0x0f: PDEBUG(D_PROBE, "Find Sensor PAS106"); @@ -7097,6 +7086,7 @@ static int sd_config(struct gspca_dev *gspca_dev, case 0x2030: PDEBUG(D_PROBE, "Find Sensor PO2030"); sd->sensor = SENSOR_PO2030; + sd->sharpness = 0; /* from win traces */ break; case 0x7620: PDEBUG(D_PROBE, "Find Sensor OV7620"); @@ -7134,13 +7124,13 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->qindex = 1; sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; sd->gamma = gamma[(int) sd->sensor]; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; /* switch the led off */ -/*fixme: other sensors? */ - if (sensor == 0x06 || sensor == 0x11) - reg_w(gspca_dev->dev, 0x01, 0x0000); + reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; } @@ -7170,15 +7160,14 @@ static void sd_start(struct gspca_dev *gspca_dev) {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */ {OV7620_mode0, OV7620_mode1}, /* 9 */ {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {0, 0}, /* 11 */ - {pas106b_InitialScale, pas106b_Initial}, /* 12 */ - {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */ + {pas106b_InitialScale, pas106b_Initial}, /* 11 */ + {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */ /* or {pb03303x_InitialScale, pb03303x_Initial}, */ - {PO2030_mode0, PO2030_mode1}, /* 14 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */ - {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */ + {PO2030_mode0, PO2030_mode1}, /* 13 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 14 */ + {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 15 */ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, - /* 17 */ + /* 16 */ }; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; @@ -7324,7 +7313,7 @@ static void sd_close(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len) { @@ -7401,6 +7390,16 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; @@ -7409,6 +7408,63 @@ static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy(menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -7420,6 +7476,7 @@ static struct sd_desc sd_desc = { .stop0 = sd_stop0, .close = sd_close, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; #define DVNM(name) .driver_info = (kernel_ulong_t) name @@ -7514,10 +7571,6 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); -module_param(lightfreq, int, 0644); -MODULE_PARM_DESC(lightfreq, - "Light frequency banding filter: 50, 60 Hz or" - " 0 to NoFliker (default=50)"); module_param(force_sensor, int, 0644); MODULE_PARM_DESC(force_sensor, "Force sensor. Only for experts!!!"); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 9385c823a978..43a6c81a53ef 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -324,6 +324,8 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */ #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed BGGR bayer */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From 54ab92ca05550550bcec2462de2605f35d079b66 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 3 Jul 2008 11:20:58 -0300 Subject: V4L/DVB (8194): gspca: Fix the format of the low resolution mode of spca561. The low (half) res modes of the spca561 are not spca561 compressed, but are raw bayer, this patches fixes this and adds a PIX_FMT define for the GBRG bayer format used by the spca561 in low res mode. Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/spca561.c | 6 +++--- include/linux/videodev2.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index a94e6270115e..dbd1648a655f 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -98,8 +98,8 @@ static struct ctrl sd_ctrls[] = { }; static struct cam_mode sif_mode[] = { - {V4L2_PIX_FMT_SPCA561, 160, 120, 3}, - {V4L2_PIX_FMT_SPCA561, 176, 144, 2}, + {V4L2_PIX_FMT_SGBRG8, 160, 120, 3}, + {V4L2_PIX_FMT_SGBRG8, 176, 144, 2}, {V4L2_PIX_FMT_SPCA561, 320, 240, 1}, {V4L2_PIX_FMT_SPCA561, 352, 288, 0}, }; @@ -808,7 +808,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); } else { - /*fixme: which format?*/ + /* raw bayer (with a header, which we skip) */ data += 20; len -= 20; gspca_frame_add(gspca_dev, FIRST_PACKET, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 43a6c81a53ef..577f15634791 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -310,6 +310,7 @@ struct v4l2_pix_format /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') /* 8 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B','Y','R','2') /* 16 BGBG.. GRGR.. */ /* compressed formats */ -- cgit v1.2.3 From ab8f12cf8e3d33adec382585f9f60f8686a372c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2008 18:29:32 -0300 Subject: V4L/DVB (8197): gspca: pac207 frames no more decoded in the subdriver. videodev2: New pixfmt pac207: Remove the specific decoding. main: get_buff_size operation added for the subdriver. Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 7 +- drivers/media/video/gspca/gspca.h | 2 + drivers/media/video/gspca/pac207.c | 353 +++++-------------------------------- include/linux/videodev2.h | 3 +- 4 files changed, 53 insertions(+), 312 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index f6d216316b11..708270a46e1f 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -321,6 +321,7 @@ static int gspca_is_compressed(__u32 format) case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SPCA561: + case V4L2_PIX_FMT_PAC207: return 1; } return 0; @@ -380,7 +381,8 @@ static __u32 get_v4l2_depth(__u32 pixfmt) case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SBGGR8: /* 'BA81' Bayer */ case V4L2_PIX_FMT_SN9C10X: /* 'S910' SN9C10x compression */ - case V4L2_PIX_FMT_SPCA561: /* 'S561' compressed BGGR bayer */ + case V4L2_PIX_FMT_SPCA561: /* 'S561' compressed GBRG bayer */ + case V4L2_PIX_FMT_PAC207: /* 'P207' compressed BGGR bayer */ return 8; } PDEBUG(D_ERR|D_CONF, "Unknown pixel format %c%c%c%c", @@ -395,6 +397,9 @@ static int gspca_get_buff_size(struct gspca_dev *gspca_dev, int mode) { unsigned int size; + if (gspca_dev->sd_desc->get_buff_size) + return gspca_dev->sd_desc->get_buff_size(gspca_dev, mode); + size = gspca_dev->cam.cam_mode[mode].width * gspca_dev->cam.cam_mode[mode].height * get_v4l2_depth(gspca_dev->cam.cam_mode[mode].pixfmt) / 8; diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 0d23e0a90c77..05e8ee06071b 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -80,6 +80,7 @@ typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len); +typedef int (*cam_get_buff_size_op) (struct gspca_dev *gspca_dev, int mode); struct ctrl { struct v4l2_queryctrl qctrl; @@ -106,6 +107,7 @@ struct sd_desc { cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; cam_qmnu_op querymenu; + cam_get_buff_size_op get_buff_size; /* optional */ }; /* packet types when moving from iso buf to frame buf */ diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 7e067456e62b..0135ba599e81 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -58,34 +58,10 @@ MODULE_LICENSE("GPL"); before doing any other adjustments */ #define PAC207_AUTOGAIN_IGNORE_FRAMES 3 -enum pac207_line_state { - LINE_HEADER1, - LINE_HEADER2, - LINE_UNCOMPRESSED, - LINE_COMPRESSED, -}; - -struct pac207_decoder_state { - /* generic state */ - u16 line_read; - u16 line_marker; - u8 line_state; - u8 header_read; - /* compression state */ - u16 processed_bytes; - u8 remaining_bits; - s8 no_remaining_bits; - u8 get_abs; - u8 discard_byte; - u8 line_decode_buf[352]; -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct pac207_decoder_state decoder_state; - u8 mode; u8 brightness; @@ -94,6 +70,7 @@ struct sd { u8 gain; u8 sof_read; + u8 header_read; u8 autogain_ignore_frames; atomic_t avg_lum; @@ -173,8 +150,8 @@ static struct ctrl sd_ctrls[] = { }; static struct cam_mode sif_mode[] = { - {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, - {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, + {V4L2_PIX_FMT_PAC207, 176, 144, 1}, + {V4L2_PIX_FMT_PAC207, 352, 288, 0}, }; static const __u8 pac207_sensor_init[][8] = { @@ -361,68 +338,17 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* -- convert pixart frames to Bayer -- */ -/* Sonix decompressor struct B.S.(2004) */ -static struct { - u8 is_abs; - u8 len; - s8 val; -} table[256]; - -void init_pixart_decoder(void) +static int sd_get_buff_size_op(struct gspca_dev *gspca_dev, int mode) { - int i, is_abs, val, len; - - for (i = 0; i < 256; i++) { - is_abs = 0; - val = 0; - len = 0; - if ((i & 0xC0) == 0) { - /* code 00 */ - val = 0; - len = 2; - } else if ((i & 0xC0) == 0x40) { - /* code 01 */ - val = -5; - len = 2; - } else if ((i & 0xC0) == 0x80) { - /* code 10 */ - val = 5; - len = 2; - } else if ((i & 0xF0) == 0xC0) { - /* code 1100 */ - val = -10; - len = 4; - } else if ((i & 0xF0) == 0xD0) { - /* code 1101 */ - val = 10; - len = 4; - } else if ((i & 0xF8) == 0xE0) { - /* code 11100 */ - val = -15; - len = 5; - } else if ((i & 0xF8) == 0xE8) { - /* code 11101 */ - val = 15; - len = 5; - } else if ((i & 0xFC) == 0xF0) { - /* code 111100 */ - val = -20; - len = 6; - } else if ((i & 0xFC) == 0xF4) { - /* code 111101 */ - val = 20; - len = 6; - } else if ((i & 0xF8) == 0xF8) { - /* code 11111xxxxxx */ - is_abs = 1; - val = 0; - len = 5; - } - table[i].is_abs = is_abs; - table[i].val = val; - table[i].len = len; + switch (gspca_dev->cam.cam_mode[mode].width) { + case 176: /* 176x144 */ + /* uncompressed, add 2 bytes / line for line header */ + return (176 + 2) * 144; + case 352: /* 352x288 */ + /* compressed */ + return 352 * 288 / 2; } + return -EIO; /* should never happen */ } /* auto gain and exposure algorithm based on the knee algorithm described here: @@ -517,245 +443,52 @@ static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, return NULL; } -static int pac207_decompress_row(struct gspca_dev *gspca_dev, - struct gspca_frame *f, - __u8 *cdata, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - unsigned char *outp = decoder_state->line_decode_buf + - decoder_state->line_read; - int val, bitlen, bitpos = -decoder_state->no_remaining_bits; - u8 code; - - /* first two pixels are stored as raw 8-bit */ - while (decoder_state->line_read < 2) { - *outp++ = *cdata++; - decoder_state->line_read++; - len--; - if (len == 0) - return 0; - } - - while (decoder_state->line_read < gspca_dev->width) { - if (bitpos < 0) { - code = decoder_state->remaining_bits << (8 + bitpos) | - cdata[0] >> -bitpos; - } else { - u8 *addr = cdata + bitpos / 8; - code = addr[0] << (bitpos & 7) | - addr[1] >> (8 - (bitpos & 7)); - } - - bitlen = decoder_state->get_abs ? - 6 : table[code].len; - - /* Stop decompressing if we're out of input data */ - if ((bitpos + bitlen) > (len * 8)) - break; - - if (decoder_state->get_abs) { - *outp++ = code & 0xFC; - decoder_state->line_read++; - decoder_state->get_abs = 0; - } else { - if (table[code].is_abs) { - decoder_state->get_abs = 1; - } else { - /* relative to left pixel */ - val = outp[-2] + - table[code].val; - if (val > 0xff) - val = 0xff; - else if (val < 0) - val = 0; - *outp++ = val; - decoder_state->line_read++; - } - } - bitpos += bitlen; - } - - if (decoder_state->line_read == gspca_dev->width) { - int compressed_line_len; - - gspca_frame_add(gspca_dev, INTER_PACKET, f, - decoder_state->line_decode_buf, - gspca_dev->width); - - /* completely decompressed line, round pos to nearest word */ - compressed_line_len = ((decoder_state->processed_bytes * 8 + - bitpos + 15) / 16) * 2; - - len -= compressed_line_len - decoder_state->processed_bytes; - if (len < 0) { - decoder_state->discard_byte = 1; - len = 0; - } - } else { - decoder_state->processed_bytes += len; - decoder_state->remaining_bits = cdata[bitpos/8]; - decoder_state->no_remaining_bits = (8 - bitpos) & 7; - len = 0; - } - - return len; -} - -static void pac207_decode_line_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - - decoder_state->line_read = 0; - decoder_state->line_state = LINE_HEADER1; - decoder_state->processed_bytes = 0; - decoder_state->no_remaining_bits = 0; - decoder_state->get_abs = 0; -} - -static void pac207_decode_frame_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - - decoder_state->header_read = 0; - decoder_state->discard_byte = 0; - - pac207_decode_line_init(gspca_dev); -} - -static int pac207_decode_frame_data(struct gspca_dev *gspca_dev, - struct gspca_frame *f, unsigned char *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - int needed = 0; - - /* first 11 bytes after sof marker: frame header */ - if (decoder_state->header_read < 11) { - /* get average lumination from frame header (byte 5) */ - if (decoder_state->header_read < 5) { - needed = 5 - decoder_state->header_read; - if (len >= needed) - atomic_set(&sd->avg_lum, data[needed-1]); - } - /* skip the rest of the header */ - needed = 11 - decoder_state->header_read; - if (len <= needed) { - decoder_state->header_read += len; - return 0; - } - data += needed; - len -= needed; - decoder_state->header_read = 11; - } - - while (len) { - if (decoder_state->discard_byte) { - data++; - len--; - decoder_state->discard_byte = 0; - continue; - } - - switch (decoder_state->line_state) { - case LINE_HEADER1: - decoder_state->line_marker = data[0] << 8; - decoder_state->line_state = LINE_HEADER2; - needed = 1; - break; - case LINE_HEADER2: - decoder_state->line_marker |= data[0]; - switch (decoder_state->line_marker) { - case 0x0ff0: - decoder_state->line_state = LINE_UNCOMPRESSED; - break; - case 0x1ee1: - decoder_state->line_state = LINE_COMPRESSED; - break; - default: - PDEBUG(D_STREAM, - "Error unknown line-header %04X", - (int) decoder_state->line_marker); - gspca_dev->last_packet_type = DISCARD_PACKET; - return 0; - } - needed = 1; - break; - case LINE_UNCOMPRESSED: - needed = gspca_dev->width - decoder_state->line_read; - if (needed > len) - needed = len; - gspca_frame_add(gspca_dev, INTER_PACKET, f, data, - needed); - decoder_state->line_read += needed; - break; - case LINE_COMPRESSED: - needed = len - - pac207_decompress_row(gspca_dev, f, data, len); - break; - } - - data += needed; - len -= needed; - - if (decoder_state->line_read == gspca_dev->width) { - if ((f->data_end - f->data) == - (gspca_dev->width * gspca_dev->height)) { - /* eureka we've got a frame */ - return 1; - } - pac207_decode_line_init(gspca_dev); - } - } - - return 0; -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len) { + struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - int n; sof = pac207_find_sof(gspca_dev, data, len); - if (sof) { + int n; + /* finish decoding current frame */ - if (gspca_dev->last_packet_type == INTER_PACKET) { - n = sof - data; - if (n > sizeof(pac207_sof_marker)) - n -= sizeof(pac207_sof_marker); - else - n = 0; - n = pac207_decode_frame_data(gspca_dev, frame, - data, n); - if (n) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - NULL, - 0); - else - PDEBUG(D_STREAM, "Incomplete frame"); - } - pac207_decode_frame_init(gspca_dev); + n = sof - data; + if (n > sizeof pac207_sof_marker) + n -= sizeof pac207_sof_marker; + else + n = 0; + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, n); + sd->header_read = 0; gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); len -= sof - data; data = sof; } + if (sd->header_read < 11) { + int needed; - if (gspca_dev->last_packet_type == DISCARD_PACKET) - return; + /* get average lumination from frame header (byte 5) */ + if (sd->header_read < 5) { + needed = 5 - sd->header_read; + if (len >= needed) + atomic_set(&sd->avg_lum, data[needed - 1]); + } + /* skip the rest of the header */ + needed = 11 - sd->header_read; + if (len <= needed) { + sd->header_read += len; + return; + } + data += needed; + len -= needed; + sd->header_read = 11; + } - n = pac207_decode_frame_data(gspca_dev, frame, data, len); - if (n) - gspca_frame_add(gspca_dev, LAST_PACKET, - frame, NULL, 0); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) @@ -891,6 +624,7 @@ static const struct sd_desc sd_desc = { .close = sd_close, .dq_callback = pac207_do_auto_gain, .pkt_scan = sd_pkt_scan, + .get_buff_size = sd_get_buff_size_op, }; /* -- module initialisation -- */ @@ -927,7 +661,6 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - init_pixart_decoder(); if (usb_register(&sd_driver) < 0) return -1; PDEBUG(D_PROBE, "v%s registered", version); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 577f15634791..2e66a95e8d32 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -326,7 +326,8 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ -#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed GBRG bayer */ +#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') /* compressed BGGR bayer */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From be99af6679174e5d0e9f36fc8c18318a8ce34bca Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 10 Jul 2008 11:04:10 -0300 Subject: V4L/DVB (8245): ovcamchip: Delete stray I2C bus ID I2C_HW_SMBUS_OVFX2 is referenced in ovcamchip_core.c, but no bus uses this driver ID, so we can remove the reference. As far as I can see, the Cypress FX2 webcam is handled by a different driver (dvb-usb). Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ovcamchip/ovcamchip_core.c | 1 - include/linux/i2c-id.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index 8063e33f1c85..065c2454113e 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -297,7 +297,6 @@ static int ovcamchip_attach(struct i2c_adapter *adap) switch (adap->id) { case I2C_HW_SMBUS_OV511: case I2C_HW_SMBUS_OV518: - case I2C_HW_SMBUS_OVFX2: case I2C_HW_SMBUS_W9968CF: PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); break; diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index ef13b7c66df3..9083847157a3 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -158,7 +158,6 @@ #define I2C_HW_SMBUS_W9968CF 0x04000d #define I2C_HW_SMBUS_OV511 0x04000e /* OV511(+) USB 1.1 webcam ICs */ #define I2C_HW_SMBUS_OV518 0x04000f /* OV518(+) USB 1.1 webcam ICs */ -#define I2C_HW_SMBUS_OVFX2 0x040011 /* Cypress/OmniVision FX2 webcam */ #define I2C_HW_SMBUS_CAFE 0x040012 /* Marvell 88ALP01 "CAFE" cam */ #define I2C_HW_SMBUS_ALI1563 0x040013 -- cgit v1.2.3 From 5a367dfb739831d54caf226ce0bc4c75ef264d8d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 10 Jul 2008 11:23:37 -0300 Subject: V4L/DVB (8246): tvaudio: Stop I2C driver ID abuse The tvaudio driver is using "official" I2C device IDs for internal purpose. There must be some historical reason behind this but anyway, it shouldn't do that. As the stored values are never used, the easiest way to fix the problem is simply to remove them altogether. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvaudio.c | 13 ------------- include/linux/i2c-id.h | 7 ------- 2 files changed, 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index c77914d99d15..463680b13289 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -69,7 +69,6 @@ typedef struct AUDIOCMD { /* chip description */ struct CHIPDESC { char *name; /* chip name */ - int id; /* ID */ int addr_lo, addr_hi; /* i2c address range */ int registers; /* # of registers */ @@ -1256,7 +1255,6 @@ module_param(ta8874z, int, 0444); static struct CHIPDESC chiplist[] = { { .name = "tda9840", - .id = I2C_DRIVERID_TDA9840, .insmodopt = &tda9840, .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, @@ -1272,7 +1270,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9873h", - .id = I2C_DRIVERID_TDA9873, .checkit = tda9873_checkit, .insmodopt = &tda9873, .addr_lo = I2C_ADDR_TDA985x_L >> 1, @@ -1293,7 +1290,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9874h/a", - .id = I2C_DRIVERID_TDA9874, .checkit = tda9874a_checkit, .initialize = tda9874a_initialize, .insmodopt = &tda9874a, @@ -1306,7 +1302,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9850", - .id = I2C_DRIVERID_TDA9850, .insmodopt = &tda9850, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, @@ -1319,7 +1314,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9855", - .id = I2C_DRIVERID_TDA9855, .insmodopt = &tda9855, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, @@ -1344,7 +1338,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6300", - .id = I2C_DRIVERID_TEA6300, .insmodopt = &tea6300, .addr_lo = I2C_ADDR_TEA6300 >> 1, .addr_hi = I2C_ADDR_TEA6300 >> 1, @@ -1365,7 +1358,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6320", - .id = I2C_DRIVERID_TEA6300, .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_ADDR_TEA6300 >> 1, @@ -1387,7 +1379,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6420", - .id = I2C_DRIVERID_TEA6420, .insmodopt = &tea6420, .addr_lo = I2C_ADDR_TEA6420 >> 1, .addr_hi = I2C_ADDR_TEA6420 >> 1, @@ -1400,7 +1391,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda8425", - .id = I2C_DRIVERID_TDA8425, .insmodopt = &tda8425, .addr_lo = I2C_ADDR_TDA8425 >> 1, .addr_hi = I2C_ADDR_TDA8425 >> 1, @@ -1424,7 +1414,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "pic16c54 (PV951)", - .id = I2C_DRIVERID_PIC16C54_PV9, .insmodopt = &pic16c54, .addr_lo = I2C_ADDR_PIC16C54 >> 1, .addr_hi = I2C_ADDR_PIC16C54>> 1, @@ -1440,8 +1429,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "ta8874z", - .id = -1, - /*.id = I2C_DRIVERID_TA8874Z, */ .checkit = ta8874z_checkit, .insmodopt = &ta8874z, .addr_lo = I2C_ADDR_TDA9840 >> 1, diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 9083847157a3..4862398e05bf 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -33,15 +33,11 @@ #define I2C_DRIVERID_MSP3400 1 #define I2C_DRIVERID_TUNER 2 -#define I2C_DRIVERID_TDA8425 4 /* stereo sound processor */ #define I2C_DRIVERID_TEA6420 5 /* audio matrix switch */ #define I2C_DRIVERID_TEA6415C 6 /* video matrix switch */ #define I2C_DRIVERID_TDA9840 7 /* stereo sound processor */ #define I2C_DRIVERID_SAA7111A 8 /* video input processor */ #define I2C_DRIVERID_SAA7185B 13 /* video encoder */ -#define I2C_DRIVERID_TEA6300 18 /* audio mixer */ -#define I2C_DRIVERID_TDA9850 20 /* audio mixer */ -#define I2C_DRIVERID_TDA9855 21 /* audio mixer */ #define I2C_DRIVERID_SAA7110 22 /* video decoder */ #define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */ #define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */ @@ -50,9 +46,7 @@ #define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */ #define I2C_DRIVERID_TVMIXER 28 /* Mixer driver for tv cards */ #define I2C_DRIVERID_TVAUDIO 29 /* Generic TV sound driver */ -#define I2C_DRIVERID_TDA9873 31 /* TV sound decoder chip */ #define I2C_DRIVERID_TDA9875 32 /* TV sound decoder chip */ -#define I2C_DRIVERID_PIC16C54_PV9 33 /* Audio mux/ir receiver */ #define I2C_DRIVERID_BT819 40 /* video decoder */ #define I2C_DRIVERID_BT856 41 /* video encoder */ #define I2C_DRIVERID_VPX3220 42 /* video decoder+vbi/vtxt */ @@ -63,7 +57,6 @@ #define I2C_DRIVERID_INDYCAM 58 /* SGI IndyCam */ #define I2C_DRIVERID_OVCAMCHIP 61 /* OmniVision CMOS image sens. */ #define I2C_DRIVERID_MAX6900 63 /* MAX6900 real-time clock */ -#define I2C_DRIVERID_TDA9874 66 /* TV sound decoder */ #define I2C_DRIVERID_SAA6752HS 67 /* MPEG2 encoder */ #define I2C_DRIVERID_TVEEPROM 68 /* TV EEPROM */ #define I2C_DRIVERID_WM8775 69 /* wm8775 audio processor */ -- cgit v1.2.3 From 6f2384c4bdd4be3dc1e5d22ed5e6f0c3076fda60 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:02:19 +0200 Subject: mfd: asic3 gpiolib support ASIC3 is, among other things, a GPIO extender. We should thus have it supporting the current gpiolib API. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 224 +++++++++++++++++++++++++++++++--------------- include/linux/mfd/asic3.h | 24 +++-- 2 files changed, 164 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index ef8a492766a7..c70e7a5a5a90 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -9,7 +9,7 @@ * * Copyright 2001 Compaq Computer Corporation. * Copyright 2004-2005 Phil Blundell - * Copyright 2007 OpenedHand Ltd. + * Copyright 2007-2008 OpenedHand Ltd. * * Authors: Phil Blundell , * Samuel Ortiz @@ -19,12 +19,26 @@ #include #include #include +#include #include #include #include #include +struct asic3 { + void __iomem *mapping; + unsigned int bus_shift; + unsigned int irq_nr; + unsigned int irq_base; + spinlock_t lock; + u16 irq_bothedge[4]; + struct gpio_chip gpio; + struct device *dev; +}; + +static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); + static inline void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value) { @@ -251,7 +265,7 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) edge &= ~bit; } else if (type == IRQT_BOTHEDGE) { trigger |= bit; - if (asic3_gpio_get_value(asic, irq - asic->irq_base)) + if (asic3_gpio_get(&asic->gpio, irq - asic->irq_base)) edge &= ~bit; else edge |= bit; @@ -350,6 +364,107 @@ static void asic3_irq_remove(struct platform_device *pdev) } /* GPIOs */ +static int asic3_gpio_direction(struct gpio_chip *chip, + unsigned offset, int out) +{ + u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg; + unsigned int gpio_base; + unsigned long flags; + struct asic3 *asic; + + asic = container_of(chip, struct asic3, gpio); + gpio_base = ASIC3_GPIO_TO_BASE(offset); + + if (gpio_base > ASIC3_GPIO_D_Base) { + printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", + gpio_base, offset); + return -EINVAL; + } + + spin_lock_irqsave(&asic->lock, flags); + + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Direction); + + /* Input is 0, Output is 1 */ + if (out) + out_reg |= mask; + else + out_reg &= ~mask; + + asic3_write_register(asic, gpio_base + ASIC3_GPIO_Direction, out_reg); + + spin_unlock_irqrestore(&asic->lock, flags); + + return 0; + +} + +static int asic3_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return asic3_gpio_direction(chip, offset, 0); +} + +static int asic3_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + return asic3_gpio_direction(chip, offset, 1); +} + +static int asic3_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + unsigned int gpio_base; + u32 mask = ASIC3_GPIO_TO_MASK(offset); + struct asic3 *asic; + + asic = container_of(chip, struct asic3, gpio); + gpio_base = ASIC3_GPIO_TO_BASE(offset); + + if (gpio_base > ASIC3_GPIO_D_Base) { + printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", + gpio_base, offset); + return -EINVAL; + } + + return asic3_read_register(asic, gpio_base + ASIC3_GPIO_Status) & mask; +} + +static void asic3_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + u32 mask, out_reg; + unsigned int gpio_base; + unsigned long flags; + struct asic3 *asic; + + asic = container_of(chip, struct asic3, gpio); + gpio_base = ASIC3_GPIO_TO_BASE(offset); + + if (gpio_base > ASIC3_GPIO_D_Base) { + printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", + gpio_base, offset); + return; + } + + mask = ASIC3_GPIO_TO_MASK(offset); + + spin_lock_irqsave(&asic->lock, flags); + + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Out); + + if (value) + out_reg |= mask; + else + out_reg &= ~mask; + + asic3_write_register(asic, gpio_base + ASIC3_GPIO_Out, out_reg); + + spin_unlock_irqrestore(&asic->lock, flags); + + return; +} + static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, unsigned int function) { @@ -368,15 +483,6 @@ static void asic3_set_gpio(struct asic3 *asic, unsigned int base, spin_unlock_irqrestore(&asic->lock, flags); } -#define asic3_get_gpio_a(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn) -#define asic3_get_gpio_b(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn) -#define asic3_get_gpio_c(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn) -#define asic3_get_gpio_d(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn) - #define asic3_set_gpio_a(asic, fn, bits, val) \ asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) #define asic3_set_gpio_b(asic, fn, bits, val) \ @@ -394,54 +500,6 @@ static void asic3_set_gpio(struct asic3 *asic, unsigned int base, asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ } while (0) -int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio) -{ - u32 mask = ASIC3_GPIO_bit(gpio); - - switch (gpio >> 4) { - case ASIC3_GPIO_BANK_A: - return asic3_get_gpio_a(asic, Status) & mask; - case ASIC3_GPIO_BANK_B: - return asic3_get_gpio_b(asic, Status) & mask; - case ASIC3_GPIO_BANK_C: - return asic3_get_gpio_c(asic, Status) & mask; - case ASIC3_GPIO_BANK_D: - return asic3_get_gpio_d(asic, Status) & mask; - default: - printk(KERN_ERR "%s: invalid GPIO value 0x%x", - __func__, gpio); - return -EINVAL; - } -} -EXPORT_SYMBOL(asic3_gpio_get_value); - -void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val) -{ - u32 mask = ASIC3_GPIO_bit(gpio); - u32 bitval = 0; - if (val) - bitval = mask; - - switch (gpio >> 4) { - case ASIC3_GPIO_BANK_A: - asic3_set_gpio_a(asic, Out, mask, bitval); - return; - case ASIC3_GPIO_BANK_B: - asic3_set_gpio_b(asic, Out, mask, bitval); - return; - case ASIC3_GPIO_BANK_C: - asic3_set_gpio_c(asic, Out, mask, bitval); - return; - case ASIC3_GPIO_BANK_D: - asic3_set_gpio_d(asic, Out, mask, bitval); - return; - default: - printk(KERN_ERR "%s: invalid GPIO value 0x%x", - __func__, gpio); - return; - } -} -EXPORT_SYMBOL(asic3_gpio_set_value); static int asic3_gpio_probe(struct platform_device *pdev) { @@ -472,12 +530,14 @@ static int asic3_gpio_probe(struct platform_device *pdev) alt_function); } - return 0; + return gpiochip_add(&asic->gpio); } -static void asic3_gpio_remove(struct platform_device *pdev) +static int asic3_gpio_remove(struct platform_device *pdev) { - return; + struct asic3 *asic = platform_get_drvdata(pdev); + + return gpiochip_remove(&asic->gpio); } @@ -488,11 +548,13 @@ static int asic3_probe(struct platform_device *pdev) struct asic3 *asic; struct resource *mem; unsigned long clksel; - int ret; + int ret = 0; asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); - if (!asic) + if (asic == NULL) { + printk(KERN_ERR "kzalloc failed\n"); return -ENOMEM; + } spin_lock_init(&asic->lock); platform_set_drvdata(pdev, asic); @@ -502,14 +564,15 @@ static int asic3_probe(struct platform_device *pdev) if (!mem) { ret = -ENOMEM; printk(KERN_ERR "asic3: no MEM resource\n"); - goto err_out_1; + goto out_free; } + asic->mapping = ioremap(mem->start, PAGE_SIZE); if (!asic->mapping) { ret = -ENOMEM; printk(KERN_ERR "asic3: couldn't ioremap\n"); - goto err_out_1; + goto out_free; } asic->irq_base = pdata->irq_base; @@ -525,9 +588,21 @@ static int asic3_probe(struct platform_device *pdev) ret = asic3_irq_probe(pdev); if (ret < 0) { printk(KERN_ERR "asic3: couldn't probe IRQs\n"); - goto err_out_2; + goto out_unmap; + } + + asic->gpio.base = pdata->gpio_base; + asic->gpio.ngpio = ASIC3_NUM_GPIOS; + asic->gpio.get = asic3_gpio_get; + asic->gpio.set = asic3_gpio_set; + asic->gpio.direction_input = asic3_gpio_direction_input; + asic->gpio.direction_output = asic3_gpio_direction_output; + + ret = asic3_gpio_probe(pdev); + if (ret < 0) { + printk(KERN_ERR "GPIO probe failed\n"); + goto out_irq; } - asic3_gpio_probe(pdev); if (pdata->children) { int i; @@ -541,9 +616,13 @@ static int asic3_probe(struct platform_device *pdev) return 0; - err_out_2: + out_irq: + asic3_irq_remove(pdev); + + out_unmap: iounmap(asic->mapping); - err_out_1: + + out_free: kfree(asic); return ret; @@ -551,9 +630,12 @@ static int asic3_probe(struct platform_device *pdev) static int asic3_remove(struct platform_device *pdev) { + int ret; struct asic3 *asic = platform_get_drvdata(pdev); - asic3_gpio_remove(pdev); + ret = asic3_gpio_remove(pdev); + if (ret < 0) + return ret; asic3_irq_remove(pdev); asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 4ab2162db13b..06ef8165f406 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -16,16 +16,6 @@ #include -struct asic3 { - void __iomem *mapping; - unsigned int bus_shift; - unsigned int irq_nr; - unsigned int irq_base; - spinlock_t lock; - u16 irq_bothedge[4]; - struct device *dev; -}; - struct asic3_platform_data { struct { u32 dir; @@ -41,18 +31,19 @@ struct asic3_platform_data { unsigned int irq_base; + unsigned int gpio_base; + struct platform_device **children; unsigned int n_children; }; -int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio); -void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val); - #define ASIC3_NUM_GPIO_BANKS 4 #define ASIC3_GPIOS_PER_BANK 16 #define ASIC3_NUM_GPIOS 64 #define ASIC3_NR_IRQS ASIC3_NUM_GPIOS + 6 +#define ASIC3_TO_GPIO(gpio) (NR_BUILTIN_GPIO + (gpio)) + #define ASIC3_GPIO_BANK_A 0 #define ASIC3_GPIO_BANK_B 1 #define ASIC3_GPIO_BANK_C 2 @@ -73,6 +64,13 @@ void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val); #define ASIC3_GPIO_C_Base 0x0200 #define ASIC3_GPIO_D_Base 0x0300 +#define ASIC3_GPIO_TO_BANK(gpio) ((gpio) >> 4) +#define ASIC3_GPIO_TO_BIT(gpio) ((gpio) - \ + (ASIC3_GPIOS_PER_BANK * ((gpio) >> 4))) +#define ASIC3_GPIO_TO_MASK(gpio) (1 << ASIC3_GPIO_TO_BIT(gpio)) +#define ASIC3_GPIO_TO_BASE(gpio) (ASIC3_GPIO_A_Base + (((gpio) >> 4) * 0x0100)) +#define ASIC3_BANK_TO_BASE(bank) (ASIC3_GPIO_A_Base + ((bank) * 0x100)) + #define ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask */ #define ASIC3_GPIO_Direction 0x04 /* R/W 0:input */ #define ASIC3_GPIO_Out 0x08 /* R/W 0:output low */ -- cgit v1.2.3 From 1effe5bc6cfbac4506d7944d68dadbd29ad62645 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:07:39 +0200 Subject: mfd: asic3 children platform data removal Platform devices should be dynamically allocated, and each supported device should have its own platform data. For now we just remove this buggy code. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 8 -------- include/linux/mfd/asic3.h | 3 --- 2 files changed, 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index c70e7a5a5a90..dfee0a2ba167 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -604,14 +604,6 @@ static int asic3_probe(struct platform_device *pdev) goto out_irq; } - if (pdata->children) { - int i; - for (i = 0; i < pdata->n_children; i++) { - pdata->children[i]->dev.parent = &pdev->dev; - platform_device_register(pdata->children[i]); - } - } - printk(KERN_INFO "ASIC3 Core driver\n"); return 0; diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 06ef8165f406..b1c365800ab5 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -32,9 +32,6 @@ struct asic3_platform_data { unsigned int irq_base; unsigned int gpio_base; - - struct platform_device **children; - unsigned int n_children; }; #define ASIC3_NUM_GPIO_BANKS 4 -- cgit v1.2.3 From 3b26bf17226f66bfd6cd4e36ac81f83fe994043a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:09:51 +0200 Subject: mfd: New asic3 gpio configuration code The ASIC3 GPIO configuration code is a bit obscure and hardly readable. This patch changes it so that it is now more readable and understandable, by being more explicit. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 99 ++++++++++++++++++++--------------------------- include/linux/mfd/asic3.h | 34 +++++++++++----- 2 files changed, 67 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index dfee0a2ba167..36b46ded1bff 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -465,69 +465,54 @@ static void asic3_gpio_set(struct gpio_chip *chip, return; } -static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, - unsigned int function) +static int asic3_gpio_probe(struct platform_device *pdev, + u16 *gpio_config, int num) { - return asic3_read_register(asic, base + function); -} - -static void asic3_set_gpio(struct asic3 *asic, unsigned int base, - unsigned int function, u32 bits, u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&asic->lock, flags); - val |= (asic3_read_register(asic, base + function) & ~bits); - - asic3_write_register(asic, base + function, val); - spin_unlock_irqrestore(&asic->lock, flags); -} - -#define asic3_set_gpio_a(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) -#define asic3_set_gpio_b(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn, bits, val) -#define asic3_set_gpio_c(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn, bits, val) -#define asic3_set_gpio_d(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn, bits, val) - -#define asic3_set_gpio_banks(asic, fn, bits, pdata, field) \ - do { \ - asic3_set_gpio_a((asic), fn, (bits), (pdata)->gpio_a.field); \ - asic3_set_gpio_b((asic), fn, (bits), (pdata)->gpio_b.field); \ - asic3_set_gpio_c((asic), fn, (bits), (pdata)->gpio_c.field); \ - asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ - } while (0) - - -static int asic3_gpio_probe(struct platform_device *pdev) -{ - struct asic3_platform_data *pdata = pdev->dev.platform_data; struct asic3 *asic = platform_get_drvdata(pdev); + u16 alt_reg[ASIC3_NUM_GPIO_BANKS]; + u16 out_reg[ASIC3_NUM_GPIO_BANKS]; + u16 dir_reg[ASIC3_NUM_GPIO_BANKS]; + int i; + memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS); + memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS); + memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS); + + /* Enable all GPIOs */ asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, Mask), 0xffff); asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, Mask), 0xffff); asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, Mask), 0xffff); asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, Mask), 0xffff); - asic3_set_gpio_a(asic, SleepMask, 0xffff, 0xffff); - asic3_set_gpio_b(asic, SleepMask, 0xffff, 0xffff); - asic3_set_gpio_c(asic, SleepMask, 0xffff, 0xffff); - asic3_set_gpio_d(asic, SleepMask, 0xffff, 0xffff); - - if (pdata) { - asic3_set_gpio_banks(asic, Out, 0xffff, pdata, init); - asic3_set_gpio_banks(asic, Direction, 0xffff, pdata, dir); - asic3_set_gpio_banks(asic, SleepMask, 0xffff, pdata, - sleep_mask); - asic3_set_gpio_banks(asic, SleepOut, 0xffff, pdata, sleep_out); - asic3_set_gpio_banks(asic, BattFaultOut, 0xffff, pdata, - batt_fault_out); - asic3_set_gpio_banks(asic, SleepConf, 0xffff, pdata, - sleep_conf); - asic3_set_gpio_banks(asic, AltFunction, 0xffff, pdata, - alt_function); + for (i = 0; i < num; i++) { + u8 alt, pin, dir, init, bank_num, bit_num; + u16 config = gpio_config[i]; + + pin = ASIC3_CONFIG_GPIO_PIN(config); + alt = ASIC3_CONFIG_GPIO_ALT(config); + dir = ASIC3_CONFIG_GPIO_DIR(config); + init = ASIC3_CONFIG_GPIO_INIT(config); + + bank_num = ASIC3_GPIO_TO_BANK(pin); + bit_num = ASIC3_GPIO_TO_BIT(pin); + + alt_reg[bank_num] |= (alt << bit_num); + out_reg[bank_num] |= (init << bit_num); + dir_reg[bank_num] |= (dir << bit_num); + } + + for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) { + asic3_write_register(asic, + ASIC3_BANK_TO_BASE(i) + + ASIC3_GPIO_Direction, + dir_reg[i]); + asic3_write_register(asic, + ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_Out, + out_reg[i]); + asic3_write_register(asic, + ASIC3_BANK_TO_BASE(i) + + ASIC3_GPIO_AltFunction, + alt_reg[i]); } return gpiochip_add(&asic->gpio); @@ -598,7 +583,9 @@ static int asic3_probe(struct platform_device *pdev) asic->gpio.direction_input = asic3_gpio_direction_input; asic->gpio.direction_output = asic3_gpio_direction_output; - ret = asic3_gpio_probe(pdev); + ret = asic3_gpio_probe(pdev, + pdata->gpio_config, + pdata->gpio_config_num); if (ret < 0) { printk(KERN_ERR "GPIO probe failed\n"); goto out_irq; diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index b1c365800ab5..7e47cfb0c440 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -8,7 +8,7 @@ * published by the Free Software Foundation. * * Copyright 2001 Compaq Computer Corporation. - * Copyright 2007 OpendHand. + * Copyright 2007-2008 OpenedHand Ltd. */ #ifndef __ASIC3_H__ @@ -17,15 +17,8 @@ #include struct asic3_platform_data { - struct { - u32 dir; - u32 init; - u32 sleep_mask; - u32 sleep_out; - u32 batt_fault_out; - u32 sleep_conf; - u32 alt_function; - } gpio_a, gpio_b, gpio_c, gpio_d; + u16 *gpio_config; + unsigned int gpio_config_num; unsigned int bus_shift; @@ -86,6 +79,27 @@ struct asic3_platform_data { */ #define ASIC3_GPIO_Status 0x30 /* R Pin status */ +/* + * ASIC3 GPIO config + * + * Bits 0..6 gpio number + * Bits 7..13 Alternate function + * Bit 14 Direction + * Bit 15 Initial value + * + */ +#define ASIC3_CONFIG_GPIO_PIN(config) ((config) & 0x7f) +#define ASIC3_CONFIG_GPIO_ALT(config) (((config) & (0x7f << 7)) >> 7) +#define ASIC3_CONFIG_GPIO_DIR(config) ((config & (1 << 14)) >> 14) +#define ASIC3_CONFIG_GPIO_INIT(config) ((config & (1 << 15)) >> 15) +#define ASIC3_CONFIG_GPIO(gpio, alt, dir, init) (((gpio) & 0x7f) \ + | (((alt) & 0x7f) << 7) | (((dir) & 0x1) << 14) \ + | (((init) & 0x1) << 15)) +#define ASIC3_CONFIG_GPIO_DEFAULT(gpio, dir, init) \ + ASIC3_CONFIG_GPIO((gpio), 0, (dir), (init)) +#define ASIC3_CONFIG_GPIO_DEFAULT_OUT(gpio, init) \ + ASIC3_CONFIG_GPIO((gpio), 0, 1, (init)) + #define ASIC3_SPI_Base 0x0400 #define ASIC3_SPI_Control 0x0000 #define ASIC3_SPI_TxData 0x0004 -- cgit v1.2.3 From 3b8139f8b1457af7b5295d97050b3f9a2545a17a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:12:21 +0200 Subject: mfd: Use uppercase only for asic3 macros and defines Let's be consistent and use uppercase only, for both macro and defines. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 88 +++++++++++++++++++++++------------------------ include/linux/mfd/asic3.h | 56 +++++++++++++++--------------- 2 files changed, 72 insertions(+), 72 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 1924eb01cc8c..9931581b08a1 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -55,8 +55,8 @@ static inline u32 asic3_read_register(struct asic3 *asic, /* IRQs */ #define MAX_ASIC_ISR_LOOPS 20 -#define ASIC3_GPIO_Base_INCR \ - (ASIC3_GPIO_B_Base - ASIC3_GPIO_A_Base) +#define ASIC3_GPIO_BASE_INCR \ + (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE) static void asic3_irq_flip_edge(struct asic3 *asic, u32 base, int bit) @@ -66,10 +66,10 @@ static void asic3_irq_flip_edge(struct asic3 *asic, spin_lock_irqsave(&asic->lock, flags); edge = asic3_read_register(asic, - base + ASIC3_GPIO_EdgeTrigger); + base + ASIC3_GPIO_EDGE_TRIGGER); edge ^= bit; asic3_write_register(asic, - base + ASIC3_GPIO_EdgeTrigger, edge); + base + ASIC3_GPIO_EDGE_TRIGGER, edge); spin_unlock_irqrestore(&asic->lock, flags); } @@ -89,7 +89,7 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) spin_lock_irqsave(&asic->lock, flags); status = asic3_read_register(asic, - ASIC3_OFFSET(INTR, PIntStat)); + ASIC3_OFFSET(INTR, P_INT_STAT)); spin_unlock_irqrestore(&asic->lock, flags); /* Check all ten register bits */ @@ -101,17 +101,17 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) if (status & (1 << bank)) { unsigned long base, istat; - base = ASIC3_GPIO_A_Base - + bank * ASIC3_GPIO_Base_INCR; + base = ASIC3_GPIO_A_BASE + + bank * ASIC3_GPIO_BASE_INCR; spin_lock_irqsave(&asic->lock, flags); istat = asic3_read_register(asic, base + - ASIC3_GPIO_IntStatus); + ASIC3_GPIO_INT_STATUS); /* Clearing IntStatus */ asic3_write_register(asic, base + - ASIC3_GPIO_IntStatus, 0); + ASIC3_GPIO_INT_STATUS, 0); spin_unlock_irqrestore(&asic->lock, flags); for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) { @@ -154,7 +154,7 @@ static inline int asic3_irq_to_bank(struct asic3 *asic, int irq) n = (irq - asic->irq_base) >> 4; - return (n * (ASIC3_GPIO_B_Base - ASIC3_GPIO_A_Base)); + return (n * (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE)); } static inline int asic3_irq_to_index(struct asic3 *asic, int irq) @@ -172,9 +172,9 @@ static void asic3_mask_gpio_irq(unsigned int irq) index = asic3_irq_to_index(asic, irq); spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, bank + ASIC3_GPIO_Mask); + val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); val |= 1 << index; - asic3_write_register(asic, bank + ASIC3_GPIO_Mask, val); + asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); spin_unlock_irqrestore(&asic->lock, flags); } @@ -186,15 +186,15 @@ static void asic3_mask_irq(unsigned int irq) spin_lock_irqsave(&asic->lock, flags); regval = asic3_read_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask); + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK); regval &= ~(ASIC3_INTMASK_MASK0 << (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); asic3_write_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask, + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK, regval); spin_unlock_irqrestore(&asic->lock, flags); } @@ -209,9 +209,9 @@ static void asic3_unmask_gpio_irq(unsigned int irq) index = asic3_irq_to_index(asic, irq); spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, bank + ASIC3_GPIO_Mask); + val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); val &= ~(1 << index); - asic3_write_register(asic, bank + ASIC3_GPIO_Mask, val); + asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); spin_unlock_irqrestore(&asic->lock, flags); } @@ -223,15 +223,15 @@ static void asic3_unmask_irq(unsigned int irq) spin_lock_irqsave(&asic->lock, flags); regval = asic3_read_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask); + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK); regval |= (ASIC3_INTMASK_MASK0 << (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); asic3_write_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask, + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK, regval); spin_unlock_irqrestore(&asic->lock, flags); } @@ -249,11 +249,11 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) spin_lock_irqsave(&asic->lock, flags); level = asic3_read_register(asic, - bank + ASIC3_GPIO_LevelTrigger); + bank + ASIC3_GPIO_LEVEL_TRIGGER); edge = asic3_read_register(asic, - bank + ASIC3_GPIO_EdgeTrigger); + bank + ASIC3_GPIO_EDGE_TRIGGER); trigger = asic3_read_register(asic, - bank + ASIC3_GPIO_TriggerType); + bank + ASIC3_GPIO_TRIGGER_TYPE); asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit; if (type == IRQT_RISING) { @@ -283,11 +283,11 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) */ dev_notice(asic->dev, "irq type not changed\n"); } - asic3_write_register(asic, bank + ASIC3_GPIO_LevelTrigger, + asic3_write_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER, level); - asic3_write_register(asic, bank + ASIC3_GPIO_EdgeTrigger, + asic3_write_register(asic, bank + ASIC3_GPIO_EDGE_TRIGGER, edge); - asic3_write_register(asic, bank + ASIC3_GPIO_TriggerType, + asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE, trigger); spin_unlock_irqrestore(&asic->lock, flags); return 0; @@ -336,7 +336,7 @@ static int asic3_irq_probe(struct platform_device *pdev) set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - asic3_write_register(asic, ASIC3_OFFSET(INTR, IntMask), + asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK), ASIC3_INTMASK_GINTMASK); set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); @@ -374,7 +374,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, asic = container_of(chip, struct asic3, gpio); gpio_base = ASIC3_GPIO_TO_BASE(offset); - if (gpio_base > ASIC3_GPIO_D_Base) { + if (gpio_base > ASIC3_GPIO_D_BASE) { dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", gpio_base, offset); return -EINVAL; @@ -382,7 +382,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, spin_lock_irqsave(&asic->lock, flags); - out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Direction); + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION); /* Input is 0, Output is 1 */ if (out) @@ -390,7 +390,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, else out_reg &= ~mask; - asic3_write_register(asic, gpio_base + ASIC3_GPIO_Direction, out_reg); + asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg); spin_unlock_irqrestore(&asic->lock, flags); @@ -420,13 +420,13 @@ static int asic3_gpio_get(struct gpio_chip *chip, asic = container_of(chip, struct asic3, gpio); gpio_base = ASIC3_GPIO_TO_BASE(offset); - if (gpio_base > ASIC3_GPIO_D_Base) { + if (gpio_base > ASIC3_GPIO_D_BASE) { dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", gpio_base, offset); return -EINVAL; } - return asic3_read_register(asic, gpio_base + ASIC3_GPIO_Status) & mask; + return asic3_read_register(asic, gpio_base + ASIC3_GPIO_STATUS) & mask; } static void asic3_gpio_set(struct gpio_chip *chip, @@ -440,7 +440,7 @@ static void asic3_gpio_set(struct gpio_chip *chip, asic = container_of(chip, struct asic3, gpio); gpio_base = ASIC3_GPIO_TO_BASE(offset); - if (gpio_base > ASIC3_GPIO_D_Base) { + if (gpio_base > ASIC3_GPIO_D_BASE) { dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", gpio_base, offset); return; @@ -450,14 +450,14 @@ static void asic3_gpio_set(struct gpio_chip *chip, spin_lock_irqsave(&asic->lock, flags); - out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Out); + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT); if (value) out_reg |= mask; else out_reg &= ~mask; - asic3_write_register(asic, gpio_base + ASIC3_GPIO_Out, out_reg); + asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg); spin_unlock_irqrestore(&asic->lock, flags); @@ -478,10 +478,10 @@ static int asic3_gpio_probe(struct platform_device *pdev, memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS); /* Enable all GPIOs */ - asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, Mask), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, Mask), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, Mask), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, Mask), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, MASK), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, MASK), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, MASK), 0xffff); for (i = 0; i < num; i++) { u8 alt, pin, dir, init, bank_num, bit_num; @@ -503,14 +503,14 @@ static int asic3_gpio_probe(struct platform_device *pdev, for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) { asic3_write_register(asic, ASIC3_BANK_TO_BASE(i) + - ASIC3_GPIO_Direction, + ASIC3_GPIO_DIRECTION, dir_reg[i]); asic3_write_register(asic, - ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_Out, + ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_OUT, out_reg[i]); asic3_write_register(asic, ASIC3_BANK_TO_BASE(i) + - ASIC3_GPIO_AltFunction, + ASIC3_GPIO_ALT_FUNCTION, alt_reg[i]); } diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 7e47cfb0c440..6461a2ae69a2 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -45,39 +45,39 @@ struct asic3_platform_data { /* All offsets below are specified with this address bus shift */ #define ASIC3_DEFAULT_ADDR_SHIFT 2 -#define ASIC3_OFFSET(base, reg) (ASIC3_##base##_Base + ASIC3_##base##_##reg) +#define ASIC3_OFFSET(base, reg) (ASIC3_##base##_BASE + ASIC3_##base##_##reg) #define ASIC3_GPIO_OFFSET(base, reg) \ - (ASIC3_GPIO_##base##_Base + ASIC3_GPIO_##reg) + (ASIC3_GPIO_##base##_BASE + ASIC3_GPIO_##reg) -#define ASIC3_GPIO_A_Base 0x0000 -#define ASIC3_GPIO_B_Base 0x0100 -#define ASIC3_GPIO_C_Base 0x0200 -#define ASIC3_GPIO_D_Base 0x0300 +#define ASIC3_GPIO_A_BASE 0x0000 +#define ASIC3_GPIO_B_BASE 0x0100 +#define ASIC3_GPIO_C_BASE 0x0200 +#define ASIC3_GPIO_D_BASE 0x0300 #define ASIC3_GPIO_TO_BANK(gpio) ((gpio) >> 4) #define ASIC3_GPIO_TO_BIT(gpio) ((gpio) - \ (ASIC3_GPIOS_PER_BANK * ((gpio) >> 4))) #define ASIC3_GPIO_TO_MASK(gpio) (1 << ASIC3_GPIO_TO_BIT(gpio)) -#define ASIC3_GPIO_TO_BASE(gpio) (ASIC3_GPIO_A_Base + (((gpio) >> 4) * 0x0100)) -#define ASIC3_BANK_TO_BASE(bank) (ASIC3_GPIO_A_Base + ((bank) * 0x100)) - -#define ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask */ -#define ASIC3_GPIO_Direction 0x04 /* R/W 0:input */ -#define ASIC3_GPIO_Out 0x08 /* R/W 0:output low */ -#define ASIC3_GPIO_TriggerType 0x0c /* R/W 0:level */ -#define ASIC3_GPIO_EdgeTrigger 0x10 /* R/W 0:falling */ -#define ASIC3_GPIO_LevelTrigger 0x14 /* R/W 0:low level detect */ -#define ASIC3_GPIO_SleepMask 0x18 /* R/W 0:don't mask in sleep mode */ -#define ASIC3_GPIO_SleepOut 0x1c /* R/W level 0:low in sleep mode */ -#define ASIC3_GPIO_BattFaultOut 0x20 /* R/W level 0:low in batt_fault */ -#define ASIC3_GPIO_IntStatus 0x24 /* R/W 0:none, 1:detect */ -#define ASIC3_GPIO_AltFunction 0x28 /* R/W 1:LED register control */ -#define ASIC3_GPIO_SleepConf 0x2c /* +#define ASIC3_GPIO_TO_BASE(gpio) (ASIC3_GPIO_A_BASE + (((gpio) >> 4) * 0x0100)) +#define ASIC3_BANK_TO_BASE(bank) (ASIC3_GPIO_A_BASE + ((bank) * 0x100)) + +#define ASIC3_GPIO_MASK 0x00 /* R/W 0:don't mask */ +#define ASIC3_GPIO_DIRECTION 0x04 /* R/W 0:input */ +#define ASIC3_GPIO_OUT 0x08 /* R/W 0:output low */ +#define ASIC3_GPIO_TRIGGER_TYPE 0x0c /* R/W 0:level */ +#define ASIC3_GPIO_EDGE_TRIGGER 0x10 /* R/W 0:falling */ +#define ASIC3_GPIO_LEVEL_TRIGGER 0x14 /* R/W 0:low level detect */ +#define ASIC3_GPIO_SLEEP_MASK 0x18 /* R/W 0:don't mask in sleep mode */ +#define ASIC3_GPIO_SLEEP_OUT 0x1c /* R/W level 0:low in sleep mode */ +#define ASIC3_GPIO_BAT_FAULT_OUT 0x20 /* R/W level 0:low in batt_fault */ +#define ASIC3_GPIO_INT_STATUS 0x24 /* R/W 0:none, 1:detect */ +#define ASIC3_GPIO_ALT_FUNCTION 0x28 /* R/W 1:LED register control */ +#define ASIC3_GPIO_SLEEP_CONF 0x2c /* * R/W bit 1: autosleep * 0: disable gposlpout in normal mode, * enable gposlpout in sleep mode. */ -#define ASIC3_GPIO_Status 0x30 /* R Pin status */ +#define ASIC3_GPIO_STATUS 0x30 /* R Pin status */ /* * ASIC3 GPIO config @@ -137,7 +137,7 @@ struct asic3_platform_data { #define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop 0:disable, 1:enable */ #define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ -#define ASIC3_CLOCK_Base 0x0A00 +#define ASIC3_CLOCK_BASE 0x0A00 #define ASIC3_CLOCK_CDEX 0x00 #define ASIC3_CLOCK_SEL 0x04 @@ -168,12 +168,12 @@ struct asic3_platform_data { #define CLOCK_SEL_CX (1 << 2) -#define ASIC3_INTR_Base 0x0B00 +#define ASIC3_INTR_BASE 0x0B00 -#define ASIC3_INTR_IntMask 0x00 /* Interrupt mask control */ -#define ASIC3_INTR_PIntStat 0x04 /* Peripheral interrupt status */ -#define ASIC3_INTR_IntCPS 0x08 /* Interrupt timer clock pre-scale */ -#define ASIC3_INTR_IntTBS 0x0c /* Interrupt timer set */ +#define ASIC3_INTR_INT_MASK 0x00 /* Interrupt mask control */ +#define ASIC3_INTR_P_INT_STAT 0x04 /* Peripheral interrupt status */ +#define ASIC3_INTR_INT_CPS 0x08 /* Interrupt timer clock pre-scale */ +#define ASIC3_INTR_INT_TBS 0x0c /* Interrupt timer set */ #define ASIC3_INTMASK_GINTMASK (1 << 0) /* Global INTs mask 1:enable */ #define ASIC3_INTMASK_GINTEL (1 << 1) /* 1: rising edge, 0: hi level */ -- cgit v1.2.3 From 4a67b528e0be5d855b1a7bb71ec769d954765f6c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 3 Jul 2008 12:27:32 +0200 Subject: mfd: add ASIC3_CONFIG_GPIO templates As ASIC3 GPIO alternate function configuration is expected to be similar for several devices, it is convenient to define descriptive macros. This patch is inspired by the PXA MFP configuration, the alternate functions were observed on hx4700 and blueangel. Signed-off-by: Philipp Zabel Signed-off-by: Samuel Ortiz --- include/linux/mfd/asic3.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 6461a2ae69a2..0eae3083da43 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -100,6 +100,35 @@ struct asic3_platform_data { #define ASIC3_CONFIG_GPIO_DEFAULT_OUT(gpio, init) \ ASIC3_CONFIG_GPIO((gpio), 0, 1, (init)) +/* + * Alternate functions + */ +#define ASIC3_GPIOA11_PWM0 ASIC3_CONFIG_GPIO(11, 1, 1, 0) +#define ASIC3_GPIOA12_PWM1 ASIC3_CONFIG_GPIO(12, 1, 1, 0) +#define ASIC3_GPIOA15_CONTROL_CX ASIC3_CONFIG_GPIO(15, 1, 1, 0) +#define ASIC3_GPIOC0_LED0 ASIC3_CONFIG_GPIO(32, 1, 1, 0) +#define ASIC3_GPIOC1_LED1 ASIC3_CONFIG_GPIO(33, 1, 1, 0) +#define ASIC3_GPIOC2_LED2 ASIC3_CONFIG_GPIO(34, 1, 1, 0) +#define ASIC3_GPIOC3_SPI_RXD ASIC3_CONFIG_GPIO(35, 1, 0, 0) +#define ASIC3_GPIOC4_CF_nCD ASIC3_CONFIG_GPIO(36, 1, 0, 0) +#define ASIC3_GPIOC4_SPI_TXD ASIC3_CONFIG_GPIO(36, 1, 1, 0) +#define ASIC3_GPIOC5_SPI_CLK ASIC3_CONFIG_GPIO(37, 1, 1, 0) +#define ASIC3_GPIOC5_nCIOW ASIC3_CONFIG_GPIO(37, 1, 1, 0) +#define ASIC3_GPIOC6_nCIOR ASIC3_CONFIG_GPIO(38, 1, 1, 0) +#define ASIC3_GPIOC7_nPCE_1 ASIC3_CONFIG_GPIO(39, 1, 0, 0) +#define ASIC3_GPIOC8_nPCE_2 ASIC3_CONFIG_GPIO(40, 1, 0, 0) +#define ASIC3_GPIOC9_nPOE ASIC3_CONFIG_GPIO(41, 1, 0, 0) +#define ASIC3_GPIOC10_nPWE ASIC3_CONFIG_GPIO(42, 1, 0, 0) +#define ASIC3_GPIOC11_PSKTSEL ASIC3_CONFIG_GPIO(43, 1, 0, 0) +#define ASIC3_GPIOC12_nPREG ASIC3_CONFIG_GPIO(44, 1, 0, 0) +#define ASIC3_GPIOC13_nPWAIT ASIC3_CONFIG_GPIO(45, 1, 1, 0) +#define ASIC3_GPIOC14_nPIOIS16 ASIC3_CONFIG_GPIO(46, 1, 1, 0) +#define ASIC3_GPIOC15_nPIOR ASIC3_CONFIG_GPIO(47, 1, 0, 0) +#define ASIC3_GPIOD11_nCIOIS16 ASIC3_CONFIG_GPIO(59, 1, 0, 0) +#define ASIC3_GPIOD12_nCWAIT ASIC3_CONFIG_GPIO(60, 1, 0, 0) +#define ASIC3_GPIOD15_nPIOW ASIC3_CONFIG_GPIO(63, 1, 0, 0) + + #define ASIC3_SPI_Base 0x0400 #define ASIC3_SPI_Control 0x0000 #define ASIC3_SPI_TxData 0x0004 -- cgit v1.2.3 From 279cac484e55317456900fe3567c7cb5bd46fd5f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jul 2008 02:16:27 +0200 Subject: mfd: remove DS1WM register definitions from asic3.h There is a dedicated ds1wm driver, no need to duplicate this information here. Signed-off-by: Philipp Zabel Signed-off-by: Samuel Ortiz --- include/linux/mfd/asic3.h | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 0eae3083da43..8f8c46c41f44 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -265,44 +265,12 @@ struct asic3_platform_data { #define ASIC3_EXTCF_CF_SLEEP (1 << 15) /* CF sleep mode control */ /********************************************* - * The Onewire interface registers - * - * OWM_CMD - * OWM_DAT - * OWM_INTR - * OWM_INTEN - * OWM_CLKDIV + * The Onewire interface (DS1WM) is handled + * by the ds1wm driver. * *********************************************/ -#define ASIC3_OWM_Base 0xC00 - -#define ASIC3_OWM_CMD 0x00 -#define ASIC3_OWM_DAT 0x04 -#define ASIC3_OWM_INTR 0x08 -#define ASIC3_OWM_INTEN 0x0C -#define ASIC3_OWM_CLKDIV 0x10 - -#define ASIC3_OWM_CMD_ONEWR (1 << 0) -#define ASIC3_OWM_CMD_SRA (1 << 1) -#define ASIC3_OWM_CMD_DQO (1 << 2) -#define ASIC3_OWM_CMD_DQI (1 << 3) - -#define ASIC3_OWM_INTR_PD (1 << 0) -#define ASIC3_OWM_INTR_PDR (1 << 1) -#define ASIC3_OWM_INTR_TBE (1 << 2) -#define ASIC3_OWM_INTR_TEMP (1 << 3) -#define ASIC3_OWM_INTR_RBF (1 << 4) - -#define ASIC3_OWM_INTEN_EPD (1 << 0) -#define ASIC3_OWM_INTEN_IAS (1 << 1) -#define ASIC3_OWM_INTEN_ETBE (1 << 2) -#define ASIC3_OWM_INTEN_ETMT (1 << 3) -#define ASIC3_OWM_INTEN_ERBF (1 << 4) - -#define ASIC3_OWM_CLKDIV_PRE (3 << 0) /* two bits wide at bit 0 */ -#define ASIC3_OWM_CLKDIV_DIV (7 << 2) /* 3 bits wide at bit 2 */ - +#define ASIC3_OWM_BASE 0xC00 /***************************************************************************** * The SD configuration registers are at a completely different location -- cgit v1.2.3 From 99cdb0c8c5e0e43652d25951a85bac82a1231591 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jul 2008 02:17:02 +0200 Subject: mfd: let asic3 use mem resource instead of bus_shift The bus_shift parameter in platform_data is not needed as we can tell the driver with the IOMEM_RESOURCE whether the ASIC is located on a 16bit or 32bit memory bus. The htc-egpio driver uses a more descriptive bus_width parameter, but for drivers where the register map size fixed, we don't even need this. Signed-off-by: Philipp Zabel Signed-off-by: Samuel Ortiz --- drivers/mfd/asic3.c | 11 +++++------ include/linux/mfd/asic3.h | 5 ++--- 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index f2fb233b0ff3..3b870e7fb3e1 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -313,6 +313,7 @@ static int __init asic3_irq_probe(struct platform_device *pdev) struct asic3 *asic = platform_get_drvdata(pdev); unsigned long clksel = 0; unsigned int irq, irq_base; + int map_size; asic->irq_nr = platform_get_irq(pdev, 0); if (asic->irq_nr < 0) @@ -551,8 +552,8 @@ static int __init asic3_probe(struct platform_device *pdev) goto out_free; } - - asic->mapping = ioremap(mem->start, PAGE_SIZE); + map_size = mem->end - mem->start + 1; + asic->mapping = ioremap(mem->start, map_size); if (!asic->mapping) { ret = -ENOMEM; dev_err(asic->dev, "Couldn't ioremap\n"); @@ -561,10 +562,8 @@ static int __init asic3_probe(struct platform_device *pdev) asic->irq_base = pdata->irq_base; - if (pdata && pdata->bus_shift) - asic->bus_shift = 2 - pdata->bus_shift; - else - asic->bus_shift = 0; + /* calculate bus shift from mem resource */ + asic->bus_shift = 2 - (map_size >> 12); clksel = 0; asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel); diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 8f8c46c41f44..322cd6deb9f0 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -20,8 +20,6 @@ struct asic3_platform_data { u16 *gpio_config; unsigned int gpio_config_num; - unsigned int bus_shift; - unsigned int irq_base; unsigned int gpio_base; @@ -498,6 +496,7 @@ struct asic3_platform_data { #define ASIC3_SDIO_CTRL_LEDCtrl 0x7C #define ASIC3_SDIO_CTRL_SoftwareReset 0x1C0 -#define ASIC3_MAP_SIZE 0x2000 +#define ASIC3_MAP_SIZE_32BIT 0x2000 +#define ASIC3_MAP_SIZE_16BIT 0x1000 #endif /* __ASIC3_H__ */ -- cgit v1.2.3 From 6bb0e3a59a089e23eecc0af3b6f6012b2a9affba Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 16 Jul 2008 21:52:36 +0100 Subject: Subject: [PATCH 1/2] serial: Add flush_buffer() operation to uart_ops Serial drivers using DMA (like the atmel_serial driver) tend to get very confused when the xmit buffer is flushed and nobody told them. They also tend to spew a lot of garbage since the DMA engine keeps running after the buffer is flushed and possibly refilled with unrelated data. This patch adds a new flush_buffer operation to the uart_ops struct, along with a call to it from uart_flush_buffer() right after the xmit buffer has been cleared. The driver can implement this in order to syncronize its internal DMA state with the xmit buffer when the buffer is flushed. Signed-off-by: Haavard Skinnemoen Acked-by: Alan Cox Signed-off-by: Linus Torvalds --- Documentation/serial/driver | 11 +++++++++++ drivers/serial/serial_core.c | 2 ++ include/linux/serial_core.h | 1 + 3 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/Documentation/serial/driver b/Documentation/serial/driver index 88ad615dd338..77ba0afbe4db 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver @@ -186,6 +186,17 @@ hardware. Locking: port_sem taken. Interrupts: caller dependent. + flush_buffer(port) + Flush any write buffers, reset any DMA state and stop any + ongoing DMA transfers. + + This will be called whenever the port->info->xmit circular + buffer is cleared. + + Locking: port->lock taken. + Interrupts: locally disabled. + This call must not sleep + set_termios(port,termios,oldtermios) Change the port parameters, including word length, parity, stop bits. Update read_status_mask and ignore_status_mask to indicate diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 42d2e108b679..9884bc9eecb1 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -573,6 +573,8 @@ static void uart_flush_buffer(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); uart_circ_clear(&state->info->xmit); + if (port->ops->flush_buffer) + port->ops->flush_buffer(port); spin_unlock_irqrestore(&port->lock, flags); tty_wakeup(tty); } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index d8f31de632c5..1d2faa6592ae 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -190,6 +190,7 @@ struct uart_ops { void (*break_ctl)(struct uart_port *, int ctl); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); + void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); void (*set_ldisc)(struct uart_port *); -- cgit v1.2.3 From a352def21a642133758b868c71bee12ab34ad5c5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:53:12 +0100 Subject: tty: Ldisc revamp Move the line disciplines towards a conventional ->ops arrangement. For the moment the actual 'tty_ldisc' struct in the tty is kept as part of the tty struct but this can then be changed if it turns out that when it all settles down we want to refcount ldiscs separately to the tty. Pull the ldisc code out of /proc and put it with our ldisc code. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/bluetooth/hci_ldisc.c | 6 +- drivers/char/cyclades.c | 3 +- drivers/char/epca.c | 4 +- drivers/char/ip2/i2lib.c | 4 +- drivers/char/ip2/ip2main.c | 7 +- drivers/char/n_hdlc.c | 6 +- drivers/char/n_r3964.c | 2 +- drivers/char/n_tty.c | 2 +- drivers/char/pcmcia/synclink_cs.c | 4 +- drivers/char/pty.c | 10 +- drivers/char/selection.c | 3 +- drivers/char/synclink.c | 4 +- drivers/char/synclink_gt.c | 4 +- drivers/char/synclinkmp.c | 4 +- drivers/char/tty_io.c | 336 +++++++++++++++++++++++-------------- drivers/char/tty_ioctl.c | 16 +- drivers/input/serio/serport.c | 2 +- drivers/isdn/capi/capi.c | 4 +- drivers/isdn/gigaset/ser-gigaset.c | 2 +- drivers/net/hamradio/6pack.c | 2 +- drivers/net/hamradio/mkiss.c | 2 +- drivers/net/irda/irtty-sir.c | 2 +- drivers/net/ppp_async.c | 2 +- drivers/net/ppp_synctty.c | 2 +- drivers/net/slip.c | 2 +- drivers/net/wan/pc300_tty.c | 4 +- drivers/net/wan/x25_asy.c | 2 +- fs/proc/proc_tty.c | 48 ------ include/linux/tty.h | 9 +- include/linux/tty_ldisc.h | 7 +- net/bluetooth/rfcomm/tty.c | 13 +- net/irda/ircomm/ircomm_tty.c | 14 +- 32 files changed, 286 insertions(+), 246 deletions(-) (limited to 'include/linux') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index e5cd856a2fea..69df187d74ce 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -282,8 +282,8 @@ static int hci_uart_tty_open(struct tty_struct *tty) /* FIXME: why is this needed. Note don't use ldisc_ref here as the open path is before the ldisc is referencable */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); tty_driver_flush_buffer(tty); return 0; @@ -514,7 +514,7 @@ static unsigned int hci_uart_tty_poll(struct tty_struct *tty, static int __init hci_uart_init(void) { - static struct tty_ldisc hci_uart_ldisc; + static struct tty_ldisc_ops hci_uart_ldisc; int err; BT_INFO("HCI UART driver ver %s", VERSION); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 6bff9d87dc57..a957dbcc5a46 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -5246,7 +5246,8 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, HZ, info->idle_stats.recv_bytes, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, - (long)info->tty->ldisc.num); + /* FIXME: double check locking */ + (long)info->tty->ldisc.ops->num); else size = sprintf(buf + len, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 60a4df7dac12..aa8e19f44b45 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -2262,8 +2262,8 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file, tty_wait_until_sent(tty, 0); } else { /* ldisc lock already held in ioctl */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); } unlock_kernel(); /* Fall Thru */ diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c index 938879cc7bcc..0061e18aff60 100644 --- a/drivers/char/ip2/i2lib.c +++ b/drivers/char/ip2/i2lib.c @@ -868,11 +868,11 @@ i2Input(i2ChanStrPtr pCh) amountToMove = count; } // Move the first block - pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY, &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); // If we needed to wrap, do the second data move if (count > amountToMove) { - pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY, pCh->Ibuf, NULL, count - amountToMove ); } // Bump and wrap the stripIndex all at once by the amount of data read. This diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 9a2394cda943..5dc74404058f 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -1289,11 +1289,12 @@ static void do_input(struct work_struct *work) // code duplicated from n_tty (ldisc) static inline void isig(int sig, struct tty_struct *tty, int flush) { + /* FIXME: This is completely bogus */ if (tty->pgrp) kill_pgrp(tty->pgrp, sig, 1); if (flush || !L_NOFLSH(tty)) { - if ( tty->ldisc.flush_buffer ) - tty->ldisc.flush_buffer(tty); + if ( tty->ldisc.ops->flush_buffer ) + tty->ldisc.ops->flush_buffer(tty); i2InputFlush( tty->driver_data ); } } @@ -1342,7 +1343,7 @@ static void do_status(struct work_struct *work) } tmp = pCh->pTTY->real_raw; pCh->pTTY->real_raw = 0; - pCh->pTTY->ldisc.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); + pCh->pTTY->ldisc->ops.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); pCh->pTTY->real_raw = tmp; } #endif /* NEVER_HAPPENS_AS_SETUP_XXX */ diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index a35bfd7ee80e..ed4e03333ab4 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -199,7 +199,7 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) #define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) -static struct tty_ldisc n_hdlc_ldisc = { +static struct tty_ldisc_ops n_hdlc_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "hdlc", @@ -342,8 +342,8 @@ static int n_hdlc_tty_open (struct tty_struct *tty) #endif /* Flush any pending characters in the driver and discipline. */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); tty_driver_flush_buffer(tty); diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 902169062332..ae377aa473ba 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -143,7 +143,7 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count); -static struct tty_ldisc tty_ldisc_N_R3964 = { +static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "R3964", diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 8096389b0dc2..708c2b1dbe51 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1573,7 +1573,7 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, return mask; } -struct tty_ldisc tty_ldisc_N_TTY = { +struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", .open = n_tty_open, diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 1dd0e992c83d..95743e2682fe 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -514,8 +514,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 0a05c038ae6f..76b27932d229 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -111,7 +111,7 @@ static int pty_write(struct tty_struct * tty, const unsigned char *buf, int coun c = to->receive_room; if (c > count) c = count; - to->ldisc.receive_buf(to, buf, NULL, c); + to->ldisc.ops->receive_buf(to, buf, NULL, c); return c; } @@ -149,11 +149,11 @@ static int pty_chars_in_buffer(struct tty_struct *tty) int count; /* We should get the line discipline lock for "tty->link" */ - if (!to || !to->ldisc.chars_in_buffer) + if (!to || !to->ldisc.ops->chars_in_buffer) return 0; /* The ldisc must report 0 if no characters available to be read */ - count = to->ldisc.chars_in_buffer(to); + count = to->ldisc.ops->chars_in_buffer(to); if (tty->driver->subtype == PTY_TYPE_SLAVE) return count; @@ -186,8 +186,8 @@ static void pty_flush_buffer(struct tty_struct *tty) if (!to) return; - if (to->ldisc.flush_buffer) - to->ldisc.flush_buffer(to); + if (to->ldisc.ops->flush_buffer) + to->ldisc.ops->flush_buffer(to); if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); diff --git a/drivers/char/selection.c b/drivers/char/selection.c index d63f5ccc29e6..2978a49a172b 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -327,7 +327,8 @@ int paste_selection(struct tty_struct *tty) } count = sel_buffer_lth - pasted; count = min(count, tty->receive_room); - tty->ldisc.receive_buf(tty, sel_buffer + pasted, NULL, count); + tty->ldisc.ops->receive_buf(tty, sel_buffer + pasted, + NULL, count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index ac5080df2565..5e4b2e638d0c 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -975,8 +975,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 55c1653be00c..e473778cd6fa 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -641,8 +641,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index bec54866e0bb..5341b5aaf8bc 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -712,8 +712,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 047a17339f83..54c4ada460ee 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -95,8 +95,9 @@ #include #include #include +#include -#include +#include #include #include @@ -682,7 +683,7 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) static DEFINE_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ -static struct tty_ldisc tty_ldiscs[NR_LDISCS]; +static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; /** * tty_register_ldisc - install a line discipline @@ -697,7 +698,7 @@ static struct tty_ldisc tty_ldiscs[NR_LDISCS]; * takes tty_ldisc_lock to guard against ldisc races */ -int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) { unsigned long flags; int ret = 0; @@ -706,10 +707,9 @@ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) return -EINVAL; spin_lock_irqsave(&tty_ldisc_lock, flags); - tty_ldiscs[disc] = *new_ldisc; - tty_ldiscs[disc].num = disc; - tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED; - tty_ldiscs[disc].refcount = 0; + tty_ldiscs[disc] = new_ldisc; + new_ldisc->num = disc; + new_ldisc->refcount = 0; spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; @@ -737,19 +737,56 @@ int tty_unregister_ldisc(int disc) return -EINVAL; spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty_ldiscs[disc].refcount) + if (tty_ldiscs[disc]->refcount) ret = -EBUSY; else - tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED; + tty_ldiscs[disc] = NULL; spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } EXPORT_SYMBOL(tty_unregister_ldisc); + +/** + * tty_ldisc_try_get - try and reference an ldisc + * @disc: ldisc number + * @ld: tty ldisc structure to complete + * + * Attempt to open and lock a line discipline into place. Return + * the line discipline refcounted and assigned in ld. On an error + * report the error code back + */ + +static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) +{ + unsigned long flags; + struct tty_ldisc_ops *ldops; + int err = -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld->ops = NULL; + ldops = tty_ldiscs[disc]; + /* Check the entry is defined */ + if (ldops) { + /* If the module is being unloaded we can't use it */ + if (!try_module_get(ldops->owner)) + err = -EAGAIN; + else { + /* lock it */ + ldops->refcount++; + ld->ops = ldops; + err = 0; + } + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return err; +} + /** * tty_ldisc_get - take a reference to an ldisc * @disc: ldisc number + * @ld: tty line discipline structure to use * * Takes a reference to a line discipline. Deals with refcounts and * module locking counts. Returns NULL if the discipline is not available. @@ -760,32 +797,20 @@ EXPORT_SYMBOL(tty_unregister_ldisc); * takes tty_ldisc_lock to guard against ldisc races */ -struct tty_ldisc *tty_ldisc_get(int disc) +static int tty_ldisc_get(int disc, struct tty_ldisc *ld) { - unsigned long flags; - struct tty_ldisc *ld; + int err; if (disc < N_TTY || disc >= NR_LDISCS) - return NULL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - - ld = &tty_ldiscs[disc]; - /* Check the entry is defined */ - if (ld->flags & LDISC_FLAG_DEFINED) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ld->owner)) - ld = NULL; - else /* lock it */ - ld->refcount++; - } else - ld = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ld; + return -EINVAL; + err = tty_ldisc_try_get(disc, ld); + if (err == -EAGAIN) { + request_module("tty-ldisc-%d", disc); + err = tty_ldisc_try_get(disc, ld); + } + return err; } -EXPORT_SYMBOL_GPL(tty_ldisc_get); - /** * tty_ldisc_put - drop ldisc reference * @disc: ldisc number @@ -797,22 +822,67 @@ EXPORT_SYMBOL_GPL(tty_ldisc_get); * takes tty_ldisc_lock to guard against ldisc races */ -void tty_ldisc_put(int disc) +static void tty_ldisc_put(struct tty_ldisc_ops *ld) { - struct tty_ldisc *ld; unsigned long flags; + int disc = ld->num; BUG_ON(disc < N_TTY || disc >= NR_LDISCS); spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = &tty_ldiscs[disc]; + ld = tty_ldiscs[disc]; BUG_ON(ld->refcount == 0); ld->refcount--; module_put(ld->owner); spin_unlock_irqrestore(&tty_ldisc_lock, flags); } -EXPORT_SYMBOL_GPL(tty_ldisc_put); +static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) +{ + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) +{ +} + +static int tty_ldiscs_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct tty_ldisc ld; + + if (tty_ldisc_get(i, &ld) < 0) + return 0; + seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); + tty_ldisc_put(ld.ops); + return 0; +} + +static const struct seq_operations tty_ldiscs_seq_ops = { + .start = tty_ldiscs_seq_start, + .next = tty_ldiscs_seq_next, + .stop = tty_ldiscs_seq_stop, + .show = tty_ldiscs_seq_show, +}; + +static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &tty_ldiscs_seq_ops); +} + +const struct file_operations tty_ldiscs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_tty_ldiscs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; /** * tty_ldisc_assign - set ldisc on a tty @@ -829,8 +899,8 @@ EXPORT_SYMBOL_GPL(tty_ldisc_put); static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) { + ld->refcount = 0; tty->ldisc = *ld; - tty->ldisc.refcount = 0; } /** @@ -953,6 +1023,41 @@ static void tty_ldisc_enable(struct tty_struct *tty) wake_up(&tty_ldisc_wait); } +/** + * tty_ldisc_restore - helper for tty ldisc change + * @tty: tty to recover + * @old: previous ldisc + * + * Restore the previous line discipline or N_TTY when a line discipline + * change fails due to an open error + */ + +static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) +{ + char buf[64]; + struct tty_ldisc new_ldisc; + + /* There is an outstanding reference here so this is safe */ + tty_ldisc_get(old->ops->num, old); + tty_ldisc_assign(tty, old); + tty_set_termios_ldisc(tty, old->ops->num); + if (old->ops->open && (old->ops->open(tty) < 0)) { + tty_ldisc_put(old->ops); + /* This driver is always present */ + if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) + panic("n_tty: get"); + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, N_TTY); + if (new_ldisc.ops->open) { + int r = new_ldisc.ops->open(tty); + if (r < 0) + panic("Couldn't open N_TTY ldisc for " + "%s --- error %d.", + tty_name(tty, buf), r); + } + } +} + /** * tty_set_ldisc - set line discipline * @tty: the terminal to set @@ -967,28 +1072,18 @@ static void tty_ldisc_enable(struct tty_struct *tty) static int tty_set_ldisc(struct tty_struct *tty, int ldisc) { - int retval = 0; - struct tty_ldisc o_ldisc; - char buf[64]; + int retval; + struct tty_ldisc o_ldisc, new_ldisc; int work; unsigned long flags; - struct tty_ldisc *ld; struct tty_struct *o_tty; - if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) - return -EINVAL; - restart: - - ld = tty_ldisc_get(ldisc); - /* Eduardo Blanco */ - /* Cyrus Durgin */ - if (ld == NULL) { - request_module("tty-ldisc-%d", ldisc); - ld = tty_ldisc_get(ldisc); - } - if (ld == NULL) - return -EINVAL; + /* This is a bit ugly for now but means we can break the 'ldisc + is part of the tty struct' assumption later */ + retval = tty_ldisc_get(ldisc, &new_ldisc); + if (retval) + return retval; /* * Problem: What do we do if this blocks ? @@ -996,8 +1091,8 @@ restart: tty_wait_until_sent(tty, 0); - if (tty->ldisc.num == ldisc) { - tty_ldisc_put(ldisc); + if (tty->ldisc.ops->num == ldisc) { + tty_ldisc_put(new_ldisc.ops); return 0; } @@ -1024,7 +1119,7 @@ restart: /* Free the new ldisc we grabbed. Must drop the lock first. */ spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(ldisc); + tty_ldisc_put(o_ldisc.ops); /* * There are several reasons we may be busy, including * random momentary I/O traffic. We must therefore @@ -1038,7 +1133,7 @@ restart: } if (o_tty && o_tty->ldisc.refcount) { spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(ldisc); + tty_ldisc_put(o_tty->ldisc.ops); if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) return -ERESTARTSYS; goto restart; @@ -1049,8 +1144,9 @@ restart: * another ldisc change */ if (!test_bit(TTY_LDISC, &tty->flags)) { + struct tty_ldisc *ld; spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(ldisc); + tty_ldisc_put(new_ldisc.ops); ld = tty_ldisc_ref_wait(tty); tty_ldisc_deref(ld); goto restart; @@ -1060,7 +1156,7 @@ restart: if (o_tty) clear_bit(TTY_LDISC, &o_tty->flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags); - + /* * From this point on we know nobody has an ldisc * usage reference, nor can they obtain one until @@ -1070,45 +1166,30 @@ restart: work = cancel_delayed_work(&tty->buf.work); /* * Wait for ->hangup_work and ->buf.work handlers to terminate + * MUST NOT hold locks here. */ flush_scheduled_work(); /* Shutdown the current discipline. */ - if (tty->ldisc.close) - (tty->ldisc.close)(tty); + if (o_ldisc.ops->close) + (o_ldisc.ops->close)(tty); /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, ld); + tty_ldisc_assign(tty, &new_ldisc); tty_set_termios_ldisc(tty, ldisc); - if (tty->ldisc.open) - retval = (tty->ldisc.open)(tty); + if (new_ldisc.ops->open) + retval = (new_ldisc.ops->open)(tty); if (retval < 0) { - tty_ldisc_put(ldisc); - /* There is an outstanding reference here so this is safe */ - tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num)); - tty_set_termios_ldisc(tty, tty->ldisc.num); - if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { - tty_ldisc_put(o_ldisc.num); - /* This driver is always present */ - tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); - tty_set_termios_ldisc(tty, N_TTY); - if (tty->ldisc.open) { - int r = tty->ldisc.open(tty); - - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty, buf), r); - } - } + tty_ldisc_put(new_ldisc.ops); + tty_ldisc_restore(tty, &o_ldisc); } /* At this point we hold a reference to the new ldisc and a a reference to the old ldisc. If we ended up flipping back to the existing ldisc we have two references to it */ - if (tty->ldisc.num != o_ldisc.num && tty->ops->set_ldisc) + if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) tty->ops->set_ldisc(tty); - tty_ldisc_put(o_ldisc.num); + tty_ldisc_put(o_ldisc.ops); /* * Allow ldisc referencing to occur as soon as the driver @@ -1335,8 +1416,8 @@ void tty_wakeup(struct tty_struct *tty) if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { ld = tty_ldisc_ref(tty); if (ld) { - if (ld->write_wakeup) - ld->write_wakeup(tty); + if (ld->ops->write_wakeup) + ld->ops->write_wakeup(tty); tty_ldisc_deref(ld); } } @@ -1357,8 +1438,8 @@ void tty_ldisc_flush(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_ref(tty); if (ld) { - if (ld->flush_buffer) - ld->flush_buffer(tty); + if (ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); tty_ldisc_deref(ld); } tty_buffer_flush(tty); @@ -1386,7 +1467,7 @@ static void tty_reset_termios(struct tty_struct *tty) * do_tty_hangup - actual handler for hangup events * @work: tty device * - * This can be called by the "eventd" kernel thread. That is process +k * This can be called by the "eventd" kernel thread. That is process * synchronous but doesn't hold any locks, so we need to make sure we * have the appropriate locks for what we're doing. * @@ -1449,14 +1530,14 @@ static void do_tty_hangup(struct work_struct *work) ld = tty_ldisc_ref(tty); if (ld != NULL) { /* We may have no line discipline at this point */ - if (ld->flush_buffer) - ld->flush_buffer(tty); + if (ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); tty_driver_flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && - ld->write_wakeup) - ld->write_wakeup(tty); - if (ld->hangup) - ld->hangup(tty); + ld->ops->write_wakeup) + ld->ops->write_wakeup(tty); + if (ld->ops->hangup) + ld->ops->hangup(tty); } /* * FIXME: Once we trust the LDISC code better we can wait here for @@ -1825,8 +1906,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); - if (ld->read) - i = (ld->read)(tty, file, buf, count); + if (ld->ops->read) + i = (ld->ops->read)(tty, file, buf, count); else i = -EIO; tty_ldisc_deref(ld); @@ -1978,10 +2059,10 @@ static ssize_t tty_write(struct file *file, const char __user *buf, printk(KERN_ERR "tty driver %s lacks a write_room method.\n", tty->driver->name); ld = tty_ldisc_ref_wait(tty); - if (!ld->write) + if (!ld->ops->write) ret = -EIO; else - ret = do_tty_write(ld->write, tty, file, buf, count); + ret = do_tty_write(ld->ops->write, tty, file, buf, count); tty_ldisc_deref(ld); return ret; } @@ -2076,6 +2157,7 @@ static int init_dev(struct tty_driver *driver, int idx, struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval = 0; + struct tty_ldisc *ld; /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { @@ -2224,17 +2306,19 @@ static int init_dev(struct tty_driver *driver, int idx, * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ + + ld = &tty->ldisc; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); + if (ld->ops->open) { + retval = (ld->ops->open)(tty); if (retval) goto release_mem_out; } - if (o_tty && o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); + if (o_tty && o_tty->ldisc.ops->open) { + retval = (o_tty->ldisc.ops->open)(o_tty); if (retval) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); + if (ld->ops->close) + (ld->ops->close)(tty); goto release_mem_out; } tty_ldisc_enable(o_tty); @@ -2378,6 +2462,7 @@ static void release_tty(struct tty_struct *tty, int idx) static void release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; + struct tty_ldisc ld; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; @@ -2611,26 +2696,27 @@ static void release_dev(struct file *filp) spin_unlock_irqrestore(&tty_ldisc_lock, flags); /* * Shutdown the current line discipline, and reset it to N_TTY. - * N.B. why reset ldisc when we're releasing the memory?? * * FIXME: this MUST get fixed for the new reflocking */ - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty_ldisc_put(tty->ldisc.num); + if (tty->ldisc.ops->close) + (tty->ldisc.ops->close)(tty); + tty_ldisc_put(tty->ldisc.ops); /* * Switch the line discipline back */ - tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(tty, &ld); tty_set_termios_ldisc(tty, N_TTY); if (o_tty) { /* FIXME: could o_tty be in setldisc here ? */ clear_bit(TTY_LDISC, &o_tty->flags); - if (o_tty->ldisc.close) - (o_tty->ldisc.close)(o_tty); - tty_ldisc_put(o_tty->ldisc.num); - tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY)); + if (o_tty->ldisc.ops->close) + (o_tty->ldisc.ops->close)(o_tty); + tty_ldisc_put(o_tty->ldisc.ops); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(o_tty, &ld); tty_set_termios_ldisc(o_tty, N_TTY); } /* @@ -2899,8 +2985,8 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) return 0; ld = tty_ldisc_ref_wait(tty); - if (ld->poll) - ret = (ld->poll)(tty, filp, wait); + if (ld->ops->poll) + ret = (ld->ops->poll)(tty, filp, wait); tty_ldisc_deref(ld); return ret; } @@ -2974,7 +3060,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) if (get_user(ch, p)) return -EFAULT; ld = tty_ldisc_ref_wait(tty); - ld->receive_buf(tty, &ch, &mbz, 1); + ld->ops->receive_buf(tty, &ch, &mbz, 1); tty_ldisc_deref(ld); return 0; } @@ -3528,7 +3614,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: - return put_user(tty->ldisc.num, (int __user *)p); + return put_user(tty->ldisc.ops->num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); #ifdef CONFIG_VT @@ -3581,8 +3667,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } ld = tty_ldisc_ref_wait(tty); retval = -EINVAL; - if (ld->ioctl) { - retval = ld->ioctl(tty, file, cmd, arg); + if (ld->ops->ioctl) { + retval = ld->ops->ioctl(tty, file, cmd, arg); if (retval == -ENOIOCTLCMD) retval = -EINVAL; } @@ -3609,8 +3695,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, } ld = tty_ldisc_ref_wait(tty); - if (ld->compat_ioctl) - retval = ld->compat_ioctl(tty, file, cmd, arg); + if (ld->ops->compat_ioctl) + retval = ld->ops->compat_ioctl(tty, file, cmd, arg); tty_ldisc_deref(ld); return retval; @@ -3782,7 +3868,8 @@ static void flush_to_ldisc(struct work_struct *work) flag_buf = head->flag_buf_ptr + head->read; head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->receive_buf(tty, char_buf, flag_buf, count); + disc->ops->receive_buf(tty, char_buf, + flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } /* Restore the queue head */ @@ -3843,9 +3930,12 @@ EXPORT_SYMBOL(tty_flip_buffer_push); static void initialize_tty_struct(struct tty_struct *tty) { + struct tty_ldisc ld; memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; - tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); + if (tty_ldisc_get(N_TTY, &ld) < 0) + panic("n_tty: init_tty"); + tty_ldisc_assign(tty, &ld); tty->session = NULL; tty->pgrp = NULL; tty->overrun_time = jiffies; diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 8f81139d6194..ea9fc5d03b99 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -491,8 +491,8 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) ld = tty_ldisc_ref(tty); if (ld != NULL) { - if (ld->set_termios) - (ld->set_termios)(tty, &old_termios); + if (ld->ops->set_termios) + (ld->ops->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } mutex_unlock(&tty->termios_mutex); @@ -552,8 +552,8 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) ld = tty_ldisc_ref(tty); if (ld != NULL) { - if ((opt & TERMIOS_FLUSH) && ld->flush_buffer) - ld->flush_buffer(tty); + if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); tty_ldisc_deref(ld); } @@ -959,12 +959,12 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) ld = tty_ldisc_ref(tty); switch (arg) { case TCIFLUSH: - if (ld && ld->flush_buffer) - ld->flush_buffer(tty); + if (ld && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); break; case TCIOFLUSH: - if (ld && ld->flush_buffer) - ld->flush_buffer(tty); + if (ld && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); /* fall through */ case TCOFLUSH: tty_driver_flush_buffer(tty); diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 7ff71ba7b7c9..b9694b6445d0 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -216,7 +216,7 @@ static void serport_ldisc_write_wakeup(struct tty_struct * tty) * The line discipline structure. */ -static struct tty_ldisc serport_ldisc = { +static struct tty_ldisc_ops serport_ldisc = { .owner = THIS_MODULE, .name = "input", .open = serport_ldisc_open, diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 2095153582f1..8a35029caca0 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -466,7 +466,7 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) ld = tty_ldisc_ref(mp->tty); if (ld == NULL) return -1; - if (ld->receive_buf == NULL) { + if (ld->ops->receive_buf == NULL) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); #endif @@ -501,7 +501,7 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", datahandle, skb->len); #endif - ld->receive_buf(mp->tty, skb->data, NULL, skb->len); + ld->ops->receive_buf(mp->tty, skb->data, NULL, skb->len); kfree_skb(skb); tty_ldisc_deref(ld); return 0; diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 45d1ee93cd39..5e89fa177816 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -766,7 +766,7 @@ gigaset_tty_wakeup(struct tty_struct *tty) cs_put(cs); } -static struct tty_ldisc gigaset_ldisc = { +static struct tty_ldisc_ops gigaset_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "ser_gigaset", diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 9d5721287d6f..19dd0a61749c 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -783,7 +783,7 @@ static int sixpack_ioctl(struct tty_struct *tty, struct file *file, return err; } -static struct tty_ldisc sp_ldisc = { +static struct tty_ldisc_ops sp_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "6pack", diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 65166035aca0..c6ca47599fd4 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -969,7 +969,7 @@ out: mkiss_put(ax); } -static struct tty_ldisc ax_ldisc = { +static struct tty_ldisc_ops ax_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "mkiss", diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index e6f40b7f9041..9e33196f9459 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -533,7 +533,7 @@ static void irtty_close(struct tty_struct *tty) /* ------------------------------------------------------- */ -static struct tty_ldisc irda_ldisc = { +static struct tty_ldisc_ops irda_ldisc = { .magic = TTY_LDISC_MAGIC, .name = "irda", .flags = 0, diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index f1a52def1241..451bdb57d6fc 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -378,7 +378,7 @@ ppp_asynctty_wakeup(struct tty_struct *tty) } -static struct tty_ldisc ppp_ldisc = { +static struct tty_ldisc_ops ppp_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "ppp", diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index b8f0369a71e7..801d8f99d471 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -418,7 +418,7 @@ ppp_sync_wakeup(struct tty_struct *tty) } -static struct tty_ldisc ppp_sync_ldisc = { +static struct tty_ldisc_ops ppp_sync_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "pppsync", diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 84af68fdb6c2..1d58991d395b 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -1301,7 +1301,7 @@ static int sl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) #endif /* VSV changes end */ -static struct tty_ldisc sl_ldisc = { +static struct tty_ldisc_ops sl_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "slip", diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index e03eef2f2282..c2c10c632260 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -688,9 +688,9 @@ static void cpc_tty_rx_work(struct work_struct *work) if (cpc_tty->tty) { ld = tty_ldisc_ref(cpc_tty->tty); if (ld) { - if (ld->receive_buf) { + if (ld->ops->receive_buf) { CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name); - ld->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size); + ld->ops->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size); } tty_ldisc_deref(ld); } diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 069f8bb0a99f..2a6c7a60756f 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -754,7 +754,7 @@ static void x25_asy_setup(struct net_device *dev) dev->flags = IFF_NOARP; } -static struct tty_ldisc x25_ldisc = { +static struct tty_ldisc_ops x25_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "X.25", diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 21f490f5d65c..d153946d6d15 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -136,54 +136,6 @@ static const struct file_operations proc_tty_drivers_operations = { .release = seq_release, }; -static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) -{ - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) -{ -} - -static int tty_ldiscs_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct tty_ldisc *ld; - - ld = tty_ldisc_get(i); - if (ld == NULL) - return 0; - seq_printf(m, "%-10s %2d\n", ld->name ? ld->name : "???", i); - tty_ldisc_put(i); - return 0; -} - -static const struct seq_operations tty_ldiscs_seq_ops = { - .start = tty_ldiscs_seq_start, - .next = tty_ldiscs_seq_next, - .stop = tty_ldiscs_seq_stop, - .show = tty_ldiscs_seq_show, -}; - -static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &tty_ldiscs_seq_ops); -} - -static const struct file_operations tty_ldiscs_proc_fops = { - .owner = THIS_MODULE, - .open = proc_tty_ldiscs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/ diff --git a/include/linux/tty.h b/include/linux/tty.h index 324a3b231d40..013711ea7385 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -185,6 +185,7 @@ struct tty_struct { struct tty_driver *driver; const struct tty_operations *ops; int index; + /* The ldisc objects are protected by tty_ldisc_lock at the moment */ struct tty_ldisc ldisc; struct mutex termios_mutex; spinlock_t ctrl_lock; @@ -289,7 +290,7 @@ extern void tty_wait_until_sent(struct tty_struct * tty, long timeout); extern int tty_check_change(struct tty_struct * tty); extern void stop_tty(struct tty_struct * tty); extern void start_tty(struct tty_struct * tty); -extern int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc); +extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); @@ -330,9 +331,7 @@ extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); extern void tty_ldisc_deref(struct tty_ldisc *); extern struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *); - -extern struct tty_ldisc *tty_ldisc_get(int); -extern void tty_ldisc_put(int); +extern const struct file_operations tty_ldiscs_proc_fops; extern void tty_wakeup(struct tty_struct *tty); extern void tty_ldisc_flush(struct tty_struct *tty); @@ -354,7 +353,7 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); /* n_tty.c */ -extern struct tty_ldisc tty_ldisc_N_TTY; +extern struct tty_ldisc_ops tty_ldisc_N_TTY; /* tty_audit.c */ #ifdef CONFIG_AUDIT diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 6226504d9108..40f38d896777 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -104,7 +104,7 @@ #include #include -struct tty_ldisc { +struct tty_ldisc_ops { int magic; char *name; int num; @@ -142,6 +142,11 @@ struct tty_ldisc { int refcount; }; +struct tty_ldisc { + struct tty_ldisc_ops *ops; + int refcount; +}; + #define TTY_LDISC_MAGIC 0x5403 #define LDISC_FLAG_DEFINED 0x00000001 diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c9191871c1e0..0a387f2eb7a9 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -617,14 +617,7 @@ static void rfcomm_tty_wakeup(unsigned long arg) return; BT_DBG("dev %p tty %p", dev, tty); - - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - - wake_up_interruptible(&tty->write_wait); -#ifdef SERIAL_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif + tty_wakeup(tty); } static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) @@ -1005,9 +998,7 @@ static void rfcomm_tty_flush_buffer(struct tty_struct *tty) return; skb_queue_purge(&dev->dlc->tx_queue); - - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); + tty_wakeup(tty); } static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 76c3057d0179..e4e2caeb9d82 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -650,12 +650,7 @@ static void ircomm_tty_do_softint(struct work_struct *work) } /* Check if user (still) wants to be waken up */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - { - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } /* @@ -1141,6 +1136,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_ldisc *ld; IRDA_DEBUG(2, "%s()\n", __func__ ); @@ -1173,7 +1169,11 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * involve the flip buffers, since we are not running in an interrupt * handler */ - self->tty->ldisc.receive_buf(self->tty, skb->data, NULL, skb->len); + + ld = tty_ldisc_ref(self->tty); + if (ld) + ld->ops->receive_buf(self->tty, skb->data, NULL, skb->len); + tty_ldisc_deref(ld); /* No need to kfree_skb - see ircomm_ttp_data_indication() */ -- cgit v1.2.3 From 6f67048cd010afe19d79d821f16055d9c704c6f0 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:53:41 +0100 Subject: tty: Introduce a tty_port common structure Every tty driver has its own concept of a port structure and because they all differ we cannot extract commonality. Begin fixing this by creating a structure drivers can elect to use so that over time we can push fields into this and create commonality and then introduce common methods. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/tty.h | 30 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 54c4ada460ee..739c9c59fc62 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2088,6 +2088,40 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, return tty_write(file, buf, count, ppos); } +void tty_port_init(struct tty_port *port) +{ + memset(port, 0, sizeof(*port)); + init_waitqueue_head(&port->open_wait); + init_waitqueue_head(&port->close_wait); + mutex_init(&port->mutex); +} +EXPORT_SYMBOL(tty_port_init); + +int tty_port_alloc_xmit_buf(struct tty_port *port) +{ + /* We may sleep in get_zeroed_page() */ + mutex_lock(&port->mutex); + if (port->xmit_buf == NULL) + port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + mutex_unlock(&port->mutex); + if (port->xmit_buf == NULL) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(tty_port_alloc_xmit_buf); + +void tty_port_free_xmit_buf(struct tty_port *port) +{ + mutex_lock(&port->mutex); + if (port->xmit_buf != NULL) { + free_page((unsigned long)port->xmit_buf); + port->xmit_buf = NULL; + } + mutex_unlock(&port->mutex); +} +EXPORT_SYMBOL(tty_port_free_xmit_buf); + + static char ptychar[] = "pqrstuvwxyzabcde"; /** diff --git a/include/linux/tty.h b/include/linux/tty.h index 013711ea7385..d7c695b65c0e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -166,6 +166,29 @@ struct tty_bufhead { struct device; struct signal_struct; + +/* + * Port level information. Each device keeps its own port level information + * so provide a common structure for those ports wanting to use common support + * routines. + * + * The tty port has a different lifetime to the tty so must be kept apart. + * In addition be careful as tty -> port mappings are valid for the life + * of the tty object but in many cases port -> tty mappings are valid only + * until a hangup so don't use the wrong path. + */ + +struct tty_port { + struct tty_struct *tty; /* Back pointer */ + int blocked_open; /* Waiting to open */ + int count; /* Usage count */ + wait_queue_head_t open_wait; /* Open waiters */ + wait_queue_head_t close_wait; /* Close waiters */ + unsigned long flags; /* TTY flags ASY_*/ + struct mutex mutex; /* Locking */ + unsigned char *xmit_buf; /* Optional buffer */ +}; + /* * Where all of the state associated with a tty is kept while the tty * is open. Since the termios state should be kept even if the tty @@ -214,7 +237,7 @@ struct tty_struct { struct list_head tty_files; #define N_TTY_BUF_SIZE 4096 - + /* * The following is data for the N_TTY line discipline. For * historical reasons, this is included in the tty structure. @@ -242,6 +265,7 @@ struct tty_struct { spinlock_t read_lock; /* If the tty has a pending do_SAK, queue it here - akpm */ struct work_struct SAK_work; + struct tty_port *port; }; /* tty magic number */ @@ -350,6 +374,10 @@ extern void tty_write_unlock(struct tty_struct *tty); extern int tty_write_lock(struct tty_struct *tty, int ndelay); #define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) +extern void tty_port_init(struct tty_port *port); +extern int tty_port_alloc_xmit_buf(struct tty_port *port); +extern void tty_port_free_xmit_buf(struct tty_port *port); + /* n_tty.c */ -- cgit v1.2.3 From df4f4dd429870f435f8d5d9d561db029a29f063b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:53:50 +0100 Subject: serial: use tty_port Switch the serial_core based drivers to use the new tty_port structure. We can't quite use all of it yet because of the dynamically allocated extras in the serial_core layer. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250.c | 2 +- drivers/serial/jsm/jsm_neo.c | 2 +- drivers/serial/jsm/jsm_tty.c | 8 ++--- drivers/serial/serial_core.c | 80 +++++++++++++++++++++++--------------------- include/linux/serial_core.h | 26 +++++++------- 5 files changed, 60 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index ac4f20cbfe41..ce948b66bbd4 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1287,7 +1287,7 @@ static void serial8250_enable_ms(struct uart_port *port) static void receive_chars(struct uart_8250_port *up, unsigned int *status) { - struct tty_struct *tty = up->port.info->tty; + struct tty_struct *tty = up->port.info->port.tty; unsigned char ch, lsr = *status; int max_count = 256; char flag; diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c index b2d6f5b1a7c2..b7584ca55ade 100644 --- a/drivers/serial/jsm/jsm_neo.c +++ b/drivers/serial/jsm/jsm_neo.c @@ -998,7 +998,7 @@ static void neo_param(struct jsm_channel *ch) { 50, B50 }, }; - cflag = C_BAUD(ch->uart_port.info->tty); + cflag = C_BAUD(ch->uart_port.info->port.tty); baud = 9600; for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { if (baud_rates[i].cflag == cflag) { diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 94ec66372508..a697914ae3d0 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -145,7 +145,7 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) struct ktermios *termios; spin_lock_irqsave(&port->lock, lock_flags); - termios = port->info->tty->termios; + termios = port->info->port.tty->termios; if (ch == termios->c_cc[VSTART]) channel->ch_bd->bd_ops->send_start_character(channel); @@ -239,7 +239,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_cached_lsr = 0; channel->ch_stops_sent = 0; - termios = port->info->tty->termios; + termios = port->info->port.tty->termios; channel->ch_c_cflag = termios->c_cflag; channel->ch_c_iflag = termios->c_iflag; channel->ch_c_oflag = termios->c_oflag; @@ -272,7 +272,7 @@ static void jsm_tty_close(struct uart_port *port) jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; - ts = channel->uart_port.info->tty->termios; + ts = channel->uart_port.info->port.tty->termios; channel->ch_flags &= ~(CH_STOPI); @@ -515,7 +515,7 @@ void jsm_input(struct jsm_channel *ch) if (!ch) return; - tp = ch->uart_port.info->tty; + tp = ch->uart_port.info->port.tty; bd = ch->ch_bd; if(!bd) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 9884bc9eecb1..0bce1fe2c62a 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -50,7 +50,7 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->port.blocked_open : 0)) #ifdef CONFIG_SERIAL_CORE_CONSOLE #define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) @@ -113,7 +113,7 @@ static void uart_start(struct tty_struct *tty) static void uart_tasklet_action(unsigned long data) { struct uart_state *state = (struct uart_state *)data; - tty_wakeup(state->info->tty); + tty_wakeup(state->info->port.tty); } static inline void @@ -135,7 +135,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) /* * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port semaphore. + * will be serialised by the per-port mutex. */ static int uart_startup(struct uart_state *state, int init_hw) { @@ -152,7 +152,7 @@ static int uart_startup(struct uart_state *state, int init_hw) * once we have successfully opened the port. Also set * up the tty->alt_speed kludge */ - set_bit(TTY_IO_ERROR, &info->tty->flags); + set_bit(TTY_IO_ERROR, &info->port.tty->flags); if (port->type == PORT_UNKNOWN) return 0; @@ -162,6 +162,7 @@ static int uart_startup(struct uart_state *state, int init_hw) * buffer. */ if (!info->xmit.buf) { + /* This is protected by the per port mutex */ page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; @@ -182,20 +183,20 @@ static int uart_startup(struct uart_state *state, int init_hw) * Setup the RTS and DTR signals once the * port is open and ready to respond. */ - if (info->tty->termios->c_cflag & CBAUD) + if (info->port.tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); } if (info->flags & UIF_CTS_FLOW) { spin_lock_irq(&port->lock); if (!(port->ops->get_mctrl(port) & TIOCM_CTS)) - info->tty->hw_stopped = 1; + info->port.tty->hw_stopped = 1; spin_unlock_irq(&port->lock); } info->flags |= UIF_INITIALIZED; - clear_bit(TTY_IO_ERROR, &info->tty->flags); + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -217,8 +218,8 @@ static void uart_shutdown(struct uart_state *state) /* * Set the TTY IO error marker */ - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); if (info->flags & UIF_INITIALIZED) { info->flags &= ~UIF_INITIALIZED; @@ -226,7 +227,7 @@ static void uart_shutdown(struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* @@ -426,7 +427,7 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct uart_state *state, struct ktermios *old_termios) { - struct tty_struct *tty = state->info->tty; + struct tty_struct *tty = state->info->port.tty; struct uart_port *port = state->port; struct ktermios *termios; @@ -836,8 +837,8 @@ static int uart_set_info(struct uart_state *state, state->closing_wait = closing_wait; if (new_serial.xmit_fifo_size) port->fifosize = new_serial.xmit_fifo_size; - if (state->info->tty) - state->info->tty->low_latency = + if (state->info->port.tty) + state->info->port.tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: @@ -857,7 +858,7 @@ static int uart_set_info(struct uart_state *state, printk(KERN_NOTICE "%s sets custom speed on %s. This " "is deprecated.\n", current->comm, - tty_name(state->info->tty, buf)); + tty_name(state->info->port.tty, buf)); } uart_change_speed(state, NULL); } @@ -889,7 +890,7 @@ static int uart_get_lsr_info(struct uart_state *state, */ if (port->x_char || ((uart_circ_chars_pending(&state->info->xmit) > 0) && - !state->info->tty->stopped && !state->info->tty->hw_stopped)) + !state->info->port.tty->stopped && !state->info->port.tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -1239,7 +1240,7 @@ static void uart_set_termios(struct tty_struct *tty, */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->port.open_wait); #endif } @@ -1320,9 +1321,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - state->info->tty = NULL; + state->info->port.tty = NULL; - if (state->info->blocked_open) { + if (state->info->port.blocked_open) { if (state->close_delay) msleep_interruptible(state->close_delay); } else if (!uart_console(port)) { @@ -1333,7 +1334,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * Wake up anyone trying to open this port. */ state->info->flags &= ~UIF_NORMAL_ACTIVE; - wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->port.open_wait); done: mutex_unlock(&state->mutex); @@ -1417,8 +1418,8 @@ static void uart_hangup(struct tty_struct *tty) uart_shutdown(state); state->count = 0; state->info->flags &= ~UIF_NORMAL_ACTIVE; - state->info->tty = NULL; - wake_up_interruptible(&state->info->open_wait); + state->info->port.tty = NULL; + wake_up_interruptible(&state->info->port.open_wait); wake_up_interruptible(&state->info->delta_msr_wait); } mutex_unlock(&state->mutex); @@ -1432,7 +1433,7 @@ static void uart_hangup(struct tty_struct *tty) */ static void uart_update_termios(struct uart_state *state) { - struct tty_struct *tty = state->info->tty; + struct tty_struct *tty = state->info->port.tty; struct uart_port *port = state->port; if (uart_console(port) && port->cons->cflag) { @@ -1471,17 +1472,17 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) struct uart_port *port = state->port; unsigned int mctrl; - info->blocked_open++; + info->port.blocked_open++; state->count--; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); /* * If we have been hung up, tell userspace/restart open. */ - if (tty_hung_up_p(filp) || info->tty == NULL) + if (tty_hung_up_p(filp) || info->port.tty == NULL) break; /* @@ -1500,8 +1501,8 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) * have set TTY_IO_ERROR for a non-existant port. */ if ((filp->f_flags & O_NONBLOCK) || - (info->tty->termios->c_cflag & CLOCAL) || - (info->tty->flags & (1 << TTY_IO_ERROR))) + (info->port.tty->termios->c_cflag & CLOCAL) || + (info->port.tty->flags & (1 << TTY_IO_ERROR))) break; /* @@ -1509,7 +1510,7 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) * not set RTS here - we want to make sure we catch * the data from the modem. */ - if (info->tty->termios->c_cflag & CBAUD) + if (info->port.tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_DTR); /* @@ -1531,15 +1532,15 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) break; } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); state->count++; - info->blocked_open--; + info->port.blocked_open--; if (signal_pending(current)) return -ERESTARTSYS; - if (!info->tty || tty_hung_up_p(filp)) + if (!info->port.tty || tty_hung_up_p(filp)) return -EAGAIN; return 0; @@ -1562,10 +1563,13 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) goto err_unlock; } + /* BKL: RACE HERE - LEAK */ + /* We should move this into the uart_state structure and kill off + this whole complexity */ if (!state->info) { state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL); if (state->info) { - init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->port.open_wait); init_waitqueue_head(&state->info->delta_msr_wait); /* @@ -1622,7 +1626,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * be re-entered while allocating the info structure, or while we * request any IRQs that the driver may need. This also has the nice * side-effect that it delays the action of uart_hangup, so we can - * guarantee that info->tty will always contain something reasonable. + * guarantee that info->port.tty will always contain something reasonable. */ state = uart_get(drv, line); if (IS_ERR(state)) { @@ -1638,7 +1642,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) tty->driver_data = state; tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - state->info->tty = tty; + state->info->port.tty = tty; /* * If the port is in the middle of closing, bail out now. @@ -2101,8 +2105,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) /* * If that's unset, use the tty termios setting. */ - if (state->info && state->info->tty && termios.c_cflag == 0) - termios = *state->info->tty->termios; + if (state->info && state->info->port.tty && termios.c_cflag == 0) + termios = *state->info->port.tty->termios; uart_change_pm(state, 0); port->ops->set_termios(port, &termios, NULL); @@ -2521,8 +2525,8 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) tty_unregister_device(drv->tty_driver, port->line); info = state->info; - if (info && info->tty) - tty_vhangup(info->tty); + if (info && info->port.tty) + tty_vhangup(info->port.tty); /* * All users of this port should now be disconnected from diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 1d2faa6592ae..f3a1c0e45021 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -344,13 +344,15 @@ typedef unsigned int __bitwise__ uif_t; * stuff here. */ struct uart_info { - struct tty_struct *tty; + struct tty_port port; struct circ_buf xmit; uif_t flags; /* * Definitions for info->flags. These are _private_ to serial_core, and * are specific to this structure. They may be queried by low level drivers. + * + * FIXME: use the ASY_ definitions */ #define UIF_CHECK_CD ((__force uif_t) (1 << 25)) #define UIF_CTS_FLOW ((__force uif_t) (1 << 26)) @@ -358,11 +360,7 @@ struct uart_info { #define UIF_INITIALIZED ((__force uif_t) (1 << 31)) #define UIF_SUSPENDED ((__force uif_t) (1 << 30)) - int blocked_open; - struct tasklet_struct tlet; - - wait_queue_head_t open_wait; wait_queue_head_t delta_msr_wait; }; @@ -439,8 +437,8 @@ int uart_resume_port(struct uart_driver *reg, struct uart_port *port); #define uart_circ_chars_free(circ) \ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) -#define uart_tx_stopped(port) \ - ((port)->info->tty->stopped || (port)->info->tty->hw_stopped) +#define uart_tx_stopped(portp) \ + ((portp)->info->port.tty->stopped || (portp)->info->port.tty->hw_stopped) /* * The following are helper functions for the low level drivers. @@ -451,7 +449,7 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) #ifdef SUPPORT_SYSRQ if (port->sysrq) { if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch, port->info ? port->info->tty : NULL); + handle_sysrq(ch, port->info ? port->info->port.tty : NULL); port->sysrq = 0; return 1; } @@ -480,7 +478,7 @@ static inline int uart_handle_break(struct uart_port *port) } #endif if (port->flags & UPF_SAK) - do_SAK(info->tty); + do_SAK(info->port.tty); return 0; } @@ -503,9 +501,9 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status) if (info->flags & UIF_CHECK_CD) { if (status) - wake_up_interruptible(&info->open_wait); - else if (info->tty) - tty_hangup(info->tty); + wake_up_interruptible(&info->port.open_wait); + else if (info->port.tty) + tty_hangup(info->port.tty); } } @@ -518,7 +516,7 @@ static inline void uart_handle_cts_change(struct uart_port *port, unsigned int status) { struct uart_info *info = port->info; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; port->icount.cts++; @@ -544,7 +542,7 @@ static inline void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag) { - struct tty_struct *tty = port->info->tty; + struct tty_struct *tty = port->info->port.tty; if ((status & port->ignore_status_mask & ~overrun) == 0) tty_insert_flip_char(tty, ch, flag); -- cgit v1.2.3 From 7a4d29f426f17479395980ded8fa5e3bdd6d94e4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:54:54 +0100 Subject: tty.h: clean up Coding style clean up and white space tidy Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- include/linux/tty.h | 165 +++++++++++++++++++++++++++------------------------- 1 file changed, 85 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tty.h b/include/linux/tty.h index d7c695b65c0e..46008e86b191 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -71,7 +71,8 @@ struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ struct tty_buffer *free; /* Free queue head */ - int memory_used; /* Buffer space used excluding free queue */ + int memory_used; /* Buffer space used excluding + free queue */ }; /* * When a break, frame error, or parity error happens, these codes are @@ -101,68 +102,68 @@ struct tty_bufhead { #define LNEXT_CHAR(tty) ((tty)->termios->c_cc[VLNEXT]) #define EOL2_CHAR(tty) ((tty)->termios->c_cc[VEOL2]) -#define _I_FLAG(tty,f) ((tty)->termios->c_iflag & (f)) -#define _O_FLAG(tty,f) ((tty)->termios->c_oflag & (f)) -#define _C_FLAG(tty,f) ((tty)->termios->c_cflag & (f)) -#define _L_FLAG(tty,f) ((tty)->termios->c_lflag & (f)) - -#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK) -#define I_BRKINT(tty) _I_FLAG((tty),BRKINT) -#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR) -#define I_PARMRK(tty) _I_FLAG((tty),PARMRK) -#define I_INPCK(tty) _I_FLAG((tty),INPCK) -#define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP) -#define I_INLCR(tty) _I_FLAG((tty),INLCR) -#define I_IGNCR(tty) _I_FLAG((tty),IGNCR) -#define I_ICRNL(tty) _I_FLAG((tty),ICRNL) -#define I_IUCLC(tty) _I_FLAG((tty),IUCLC) -#define I_IXON(tty) _I_FLAG((tty),IXON) -#define I_IXANY(tty) _I_FLAG((tty),IXANY) -#define I_IXOFF(tty) _I_FLAG((tty),IXOFF) -#define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL) -#define I_IUTF8(tty) _I_FLAG((tty),IUTF8) - -#define O_OPOST(tty) _O_FLAG((tty),OPOST) -#define O_OLCUC(tty) _O_FLAG((tty),OLCUC) -#define O_ONLCR(tty) _O_FLAG((tty),ONLCR) -#define O_OCRNL(tty) _O_FLAG((tty),OCRNL) -#define O_ONOCR(tty) _O_FLAG((tty),ONOCR) -#define O_ONLRET(tty) _O_FLAG((tty),ONLRET) -#define O_OFILL(tty) _O_FLAG((tty),OFILL) -#define O_OFDEL(tty) _O_FLAG((tty),OFDEL) -#define O_NLDLY(tty) _O_FLAG((tty),NLDLY) -#define O_CRDLY(tty) _O_FLAG((tty),CRDLY) -#define O_TABDLY(tty) _O_FLAG((tty),TABDLY) -#define O_BSDLY(tty) _O_FLAG((tty),BSDLY) -#define O_VTDLY(tty) _O_FLAG((tty),VTDLY) -#define O_FFDLY(tty) _O_FLAG((tty),FFDLY) - -#define C_BAUD(tty) _C_FLAG((tty),CBAUD) -#define C_CSIZE(tty) _C_FLAG((tty),CSIZE) -#define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB) -#define C_CREAD(tty) _C_FLAG((tty),CREAD) -#define C_PARENB(tty) _C_FLAG((tty),PARENB) -#define C_PARODD(tty) _C_FLAG((tty),PARODD) -#define C_HUPCL(tty) _C_FLAG((tty),HUPCL) -#define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL) -#define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD) -#define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS) - -#define L_ISIG(tty) _L_FLAG((tty),ISIG) -#define L_ICANON(tty) _L_FLAG((tty),ICANON) -#define L_XCASE(tty) _L_FLAG((tty),XCASE) -#define L_ECHO(tty) _L_FLAG((tty),ECHO) -#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) -#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) -#define L_ECHONL(tty) _L_FLAG((tty),ECHONL) -#define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH) -#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) -#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) -#define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT) -#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) -#define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO) -#define L_PENDIN(tty) _L_FLAG((tty),PENDIN) -#define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN) +#define _I_FLAG(tty, f) ((tty)->termios->c_iflag & (f)) +#define _O_FLAG(tty, f) ((tty)->termios->c_oflag & (f)) +#define _C_FLAG(tty, f) ((tty)->termios->c_cflag & (f)) +#define _L_FLAG(tty, f) ((tty)->termios->c_lflag & (f)) + +#define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK) +#define I_BRKINT(tty) _I_FLAG((tty), BRKINT) +#define I_IGNPAR(tty) _I_FLAG((tty), IGNPAR) +#define I_PARMRK(tty) _I_FLAG((tty), PARMRK) +#define I_INPCK(tty) _I_FLAG((tty), INPCK) +#define I_ISTRIP(tty) _I_FLAG((tty), ISTRIP) +#define I_INLCR(tty) _I_FLAG((tty), INLCR) +#define I_IGNCR(tty) _I_FLAG((tty), IGNCR) +#define I_ICRNL(tty) _I_FLAG((tty), ICRNL) +#define I_IUCLC(tty) _I_FLAG((tty), IUCLC) +#define I_IXON(tty) _I_FLAG((tty), IXON) +#define I_IXANY(tty) _I_FLAG((tty), IXANY) +#define I_IXOFF(tty) _I_FLAG((tty), IXOFF) +#define I_IMAXBEL(tty) _I_FLAG((tty), IMAXBEL) +#define I_IUTF8(tty) _I_FLAG((tty), IUTF8) + +#define O_OPOST(tty) _O_FLAG((tty), OPOST) +#define O_OLCUC(tty) _O_FLAG((tty), OLCUC) +#define O_ONLCR(tty) _O_FLAG((tty), ONLCR) +#define O_OCRNL(tty) _O_FLAG((tty), OCRNL) +#define O_ONOCR(tty) _O_FLAG((tty), ONOCR) +#define O_ONLRET(tty) _O_FLAG((tty), ONLRET) +#define O_OFILL(tty) _O_FLAG((tty), OFILL) +#define O_OFDEL(tty) _O_FLAG((tty), OFDEL) +#define O_NLDLY(tty) _O_FLAG((tty), NLDLY) +#define O_CRDLY(tty) _O_FLAG((tty), CRDLY) +#define O_TABDLY(tty) _O_FLAG((tty), TABDLY) +#define O_BSDLY(tty) _O_FLAG((tty), BSDLY) +#define O_VTDLY(tty) _O_FLAG((tty), VTDLY) +#define O_FFDLY(tty) _O_FLAG((tty), FFDLY) + +#define C_BAUD(tty) _C_FLAG((tty), CBAUD) +#define C_CSIZE(tty) _C_FLAG((tty), CSIZE) +#define C_CSTOPB(tty) _C_FLAG((tty), CSTOPB) +#define C_CREAD(tty) _C_FLAG((tty), CREAD) +#define C_PARENB(tty) _C_FLAG((tty), PARENB) +#define C_PARODD(tty) _C_FLAG((tty), PARODD) +#define C_HUPCL(tty) _C_FLAG((tty), HUPCL) +#define C_CLOCAL(tty) _C_FLAG((tty), CLOCAL) +#define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD) +#define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS) + +#define L_ISIG(tty) _L_FLAG((tty), ISIG) +#define L_ICANON(tty) _L_FLAG((tty), ICANON) +#define L_XCASE(tty) _L_FLAG((tty), XCASE) +#define L_ECHO(tty) _L_FLAG((tty), ECHO) +#define L_ECHOE(tty) _L_FLAG((tty), ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty), ECHOK) +#define L_ECHONL(tty) _L_FLAG((tty), ECHONL) +#define L_NOFLSH(tty) _L_FLAG((tty), NOFLSH) +#define L_TOSTOP(tty) _L_FLAG((tty), TOSTOP) +#define L_ECHOCTL(tty) _L_FLAG((tty), ECHOCTL) +#define L_ECHOPRT(tty) _L_FLAG((tty), ECHOPRT) +#define L_ECHOKE(tty) _L_FLAG((tty), ECHOKE) +#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO) +#define L_PENDIN(tty) _L_FLAG((tty), PENDIN) +#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN) struct device; struct signal_struct; @@ -177,7 +178,7 @@ struct signal_struct; * of the tty object but in many cases port -> tty mappings are valid only * until a hangup so don't use the wrong path. */ - + struct tty_port { struct tty_struct *tty; /* Back pointer */ int blocked_open; /* Waiting to open */ @@ -273,14 +274,14 @@ struct tty_struct { /* * These bits are used in the flags field of the tty structure. - * + * * So that interrupts won't be able to mess up the queues, * copy_to_cooked must be atomic with respect to itself, as must * tty->write. Thus, you must use the inline functions set_bit() and * clear_bit() to make things atomic. */ #define TTY_THROTTLED 0 /* Call unthrottle() at threshold min */ -#define TTY_IO_ERROR 1 /* Canse an I/O error (may be no ldisc too) */ +#define TTY_IO_ERROR 1 /* Cause an I/O error (may be no ldisc too) */ #define TTY_OTHER_CLOSED 2 /* Other side (if any) has closed */ #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ #define TTY_DEBUG 4 /* Debugging */ @@ -310,10 +311,10 @@ extern int vcs_init(void); extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); extern char *tty_name(struct tty_struct *tty, char *buf); -extern void tty_wait_until_sent(struct tty_struct * tty, long timeout); -extern int tty_check_change(struct tty_struct * tty); -extern void stop_tty(struct tty_struct * tty); -extern void start_tty(struct tty_struct * tty); +extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); +extern int tty_check_change(struct tty_struct *tty); +extern void stop_tty(struct tty_struct *tty); +extern void start_tty(struct tty_struct *tty); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); @@ -335,10 +336,10 @@ extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); -extern void tty_hangup(struct tty_struct * tty); -extern void tty_vhangup(struct tty_struct * tty); +extern void tty_hangup(struct tty_struct *tty); +extern void tty_vhangup(struct tty_struct *tty); extern void tty_unhangup(struct file *filp); -extern int tty_hung_up_p(struct file * filp); +extern int tty_hung_up_p(struct file *filp); extern void do_SAK(struct tty_struct *tty); extern void __do_SAK(struct tty_struct *tty); extern void disassociate_ctty(int priv); @@ -347,8 +348,10 @@ extern void tty_flip_buffer_push(struct tty_struct *tty); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); -extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud); -extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); +extern void tty_termios_encode_baud_rate(struct ktermios *termios, + speed_t ibaud, speed_t obaud); +extern void tty_encode_baud_rate(struct tty_struct *tty, + speed_t ibaud, speed_t obaud); extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); @@ -390,7 +393,8 @@ extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, extern void tty_audit_exit(void); extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_push(struct tty_struct *tty); -extern void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid); +extern void tty_audit_push_task(struct task_struct *tsk, + uid_t loginuid, u32 sessionid); #else static inline void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, size_t size) @@ -405,19 +409,20 @@ static inline void tty_audit_fork(struct signal_struct *sig) static inline void tty_audit_push(struct tty_struct *tty) { } -static inline void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) +static inline void tty_audit_push_task(struct task_struct *tsk, + uid_t loginuid, u32 sessionid) { } #endif /* tty_ioctl.c */ -extern int n_tty_ioctl(struct tty_struct * tty, struct file * file, +extern int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); /* serial.c */ extern void serial_console_init(void); - + /* pcxx.c */ extern int pcxe_open(struct tty_struct *tty, struct file *filp); @@ -428,7 +433,7 @@ extern void console_print(const char *); /* vt.c */ -extern int vt_ioctl(struct tty_struct *tty, struct file * file, +extern int vt_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); #endif /* __KERNEL__ */ -- cgit v1.2.3 From 4982d6b37a5ccebe6c2af79970c7a15c1939243a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:55:11 +0100 Subject: esp: use tty_port Switch esp to use the new tty_port structures Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/esp.c | 272 +++++++++++++++++++++++------------------------ include/linux/hayesesp.h | 9 +- 2 files changed, 138 insertions(+), 143 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 84840ba13ff0..2eaf09f93e3d 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -128,9 +128,9 @@ static struct tty_driver *esp_driver; #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - tty->name, info->flags, \ + tty->name, info->port.flags, \ serial_driver.refcount, \ - info->count, tty->count, s) + info->port.count, tty->count, s) #else #define DBG_CNT(s) #endif @@ -172,13 +172,13 @@ static inline int serial_paranoia_check(struct esp_struct *info, static inline unsigned int serial_in(struct esp_struct *info, int offset) { - return inb(info->port + offset); + return inb(info->io_port + offset); } static inline void serial_out(struct esp_struct *info, int offset, unsigned char value) { - outb(value, info->port+offset); + outb(value, info->io_port+offset); } /* @@ -273,7 +273,7 @@ static inline void release_pio_buffer(struct esp_pio_buffer *buf) static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; int i; struct esp_pio_buffer *pio_buf; struct esp_pio_buffer *err_buf; @@ -295,7 +295,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) for (i = 0; i < num_bytes - 1; i += 2) { *((unsigned short *)(pio_buf->data + i)) = - inw(info->port + UART_ESI_RX); + inw(info->io_port + UART_ESI_RX); err_buf->data[i] = serial_in(info, UART_ESI_RWS); err_buf->data[i + 1] = (err_buf->data[i] >> 3) & status_mask; err_buf->data[i] &= status_mask; @@ -308,7 +308,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) } /* make sure everything is still ok since interrupts were enabled */ - tty = info->tty; + tty = info->port.tty; if (!tty) { release_pio_buffer(pio_buf); @@ -325,7 +325,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) if (err_buf->data[i] & 0x04) { flag = TTY_BREAK; - if (info->flags & ASYNC_SAK) + if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (err_buf->data[i] & 0x02) flag = TTY_FRAME; @@ -370,7 +370,7 @@ static void receive_chars_dma(struct esp_struct *info, int num_bytes) static inline void receive_chars_dma_done(struct esp_struct *info, int status) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; int num_bytes; unsigned long flags; @@ -396,7 +396,7 @@ static inline void receive_chars_dma_done(struct esp_struct *info, if (status & 0x10) { statflag = TTY_BREAK; (info->icount.brk)++; - if (info->flags & ASYNC_SAK) + if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (status & 0x08) { statflag = TTY_FRAME; @@ -451,7 +451,7 @@ static inline void transmit_chars_pio(struct esp_struct *info, for (i = 0; i < space_avail - 1; i += 2) { outw(*((unsigned short *)(pio_buf->data + i)), - info->port + UART_ESI_TX); + info->io_port + UART_ESI_TX); } if (space_avail & 0x0001) @@ -470,8 +470,8 @@ static inline void transmit_chars_pio(struct esp_struct *info, } if (info->xmit_cnt < WAKEUP_CHARS) { - if (info->tty) - tty_wakeup(info->tty); + if (info->port.tty) + tty_wakeup(info->port.tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -507,8 +507,8 @@ static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes) info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1); if (info->xmit_cnt < WAKEUP_CHARS) { - if (info->tty) - tty_wakeup(info->tty); + if (info->port.tty) + tty_wakeup(info->port.tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -575,18 +575,18 @@ static void check_modem_status(struct esp_struct *info) wake_up_interruptible(&info->delta_msr_wait); } - if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if ((info->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off"); #endif if (status & UART_MSR_DCD) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); else { #ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup..."); #endif - tty_hangup(info->tty); + tty_hangup(info->port.tty); } } } @@ -609,7 +609,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) spin_lock(&info->lock); - if (!info->tty) { + if (!info->port.tty) { spin_unlock(&info->lock); return IRQ_NONE; } @@ -647,7 +647,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) num_bytes = serial_in(info, UART_ESI_STAT1) << 8; num_bytes |= serial_in(info, UART_ESI_STAT2); - num_bytes = tty_buffer_request_room(info->tty, num_bytes); + num_bytes = tty_buffer_request_room(info->port.tty, num_bytes); if (num_bytes) { if (dma_bytes || @@ -661,7 +661,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) && (scratch & 0x02) && (info->IER & UART_IER_THRI)) { - if ((info->xmit_cnt <= 0) || info->tty->stopped) { + if ((info->xmit_cnt <= 0) || info->port.tty->stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); @@ -782,7 +782,7 @@ static int startup(struct esp_struct *info) spin_lock_irqsave(&info->lock, flags); - if (info->flags & ASYNC_INITIALIZED) + if (info->port.flags & ASYNC_INITIALIZED) goto out; if (!info->xmit_buf) { @@ -806,7 +806,7 @@ static int startup(struct esp_struct *info) num_chars |= serial_in(info, UART_ESI_STAT2); while (num_chars > 1) { - inw(info->port + UART_ESI_RX); + inw(info->io_port + UART_ESI_RX); num_chars -= 2; } @@ -834,9 +834,9 @@ static int startup(struct esp_struct *info) if (retval) { if (capable(CAP_SYS_ADMIN)) { - if (info->tty) + if (info->port.tty) set_bit(TTY_IO_ERROR, - &info->tty->flags); + &info->port.tty->flags); retval = 0; } goto out_unlocked; @@ -874,30 +874,30 @@ static int startup(struct esp_struct *info) serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&info->lock, flags); /* * Set up the tty->alt_speed kludge */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + if (info->port.tty) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->port.tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->port.tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->port.tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->port.tty->alt_speed = 460800; } /* * set the speed of the serial port */ change_speed(info); - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; return 0; out: @@ -914,7 +914,7 @@ static void shutdown(struct esp_struct *info) { unsigned long flags, f; - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef SERIAL_DEBUG_OPEN @@ -951,7 +951,7 @@ static void shutdown(struct esp_struct *info) while (current_port) { if ((current_port != info) && - (current_port->flags & ASYNC_INITIALIZED)) + (current_port->port.flags & ASYNC_INITIALIZED)) break; current_port = current_port->next_port; @@ -974,7 +974,7 @@ static void shutdown(struct esp_struct *info) serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, 0x00); - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); info->MCR &= ~UART_MCR_OUT2; @@ -982,10 +982,10 @@ static void shutdown(struct esp_struct *info) serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&info->lock, flags); } @@ -1002,10 +1002,10 @@ static void change_speed(struct esp_struct *info) unsigned char flow1 = 0, flow2 = 0; unsigned long flags; - if (!info->tty || !info->tty->termios) + if (!info->port.tty || !info->port.tty->termios) return; - cflag = info->tty->termios->c_cflag; - port = info->port; + cflag = info->port.tty->termios->c_cflag; + port = info->io_port; /* byte size and parity */ switch (cflag & CSIZE) { @@ -1029,9 +1029,9 @@ static void change_speed(struct esp_struct *info) if (cflag & CMSPAR) cval |= UART_LCR_SPAR; #endif - baud = tty_get_baud_rate(info->tty); + baud = tty_get_baud_rate(info->port.tty); if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ @@ -1046,49 +1046,49 @@ static void change_speed(struct esp_struct *info) if (baud) { /* Actual rate */ baud = BASE_BAUD/quot; - tty_encode_baud_rate(info->tty, baud, baud); + tty_encode_baud_rate(info->port.tty, baud, baud); } info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50); /* CTS flow control flag and modem status interrupts */ /* info->IER &= ~UART_IER_MSI; */ if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; + info->port.flags |= ASYNC_CTS_FLOW; /* info->IER |= UART_IER_MSI; */ flow1 = 0x04; flow2 = 0x10; } else - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; /* * Set up parity check flag */ info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->tty)) + if (I_INPCK(info->port.tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0; #if 0 /* This should be safe, but for some broken bits of hardware... */ - if (I_IGNPAR(info->tty)) { + if (I_IGNPAR(info->port.tty)) { info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; } #endif - if (I_IGNBRK(info->tty)) { + if (I_IGNBRK(info->port.tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ - if (I_IGNPAR(info->tty)) { + if (I_IGNPAR(info->port.tty)) { info->ignore_status_mask |= UART_LSR_OE | \ UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_OE | \ @@ -1096,7 +1096,7 @@ static void change_speed(struct esp_struct *info) } } - if (I_IXOFF(info->tty)) + if (I_IXOFF(info->port.tty)) flow1 |= 0x81; spin_lock_irqsave(&info->lock, flags); @@ -1116,10 +1116,10 @@ static void change_speed(struct esp_struct *info) serial_out(info, UART_ESI_CMD2, flow2); /* set flow control characters (XON/XOFF only) */ - if (I_IXOFF(info->tty)) { + if (I_IXOFF(info->port.tty)) { serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CHARS); - serial_out(info, UART_ESI_CMD2, START_CHAR(info->tty)); - serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->tty)); + serial_out(info, UART_ESI_CMD2, START_CHAR(info->port.tty)); + serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->port.tty)); serial_out(info, UART_ESI_CMD2, 0x10); serial_out(info, UART_ESI_CMD2, 0x21); switch (cflag & CSIZE) { @@ -1355,9 +1355,9 @@ static int get_serial_info(struct esp_struct *info, memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_16550A; tmp.line = info->line; - tmp.port = info->port; + tmp.port = info->io_port; tmp.irq = info->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.xmit_fifo_size = 1024; tmp.baud_base = BASE_BAUD; tmp.close_delay = info->close_delay; @@ -1407,7 +1407,7 @@ static int set_serial_info(struct esp_struct *info, if ((new_serial.type != PORT_16550A) || (new_serial.hub6) || - (info->port != new_serial.port) || + (info->io_port != new_serial.port) || (new_serial.baud_base != BASE_BAUD) || (new_serial.irq > 15) || (new_serial.irq < 2) || @@ -1425,9 +1425,9 @@ static int set_serial_info(struct esp_struct *info, if (change_irq || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) + (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | + info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; } else { @@ -1441,9 +1441,9 @@ static int set_serial_info(struct esp_struct *info, if ((current_async->line >= info->line) && (current_async->line < (info->line + 8))) { if (current_async == info) { - if (current_async->count > 1) + if (current_async->port.count > 1) return -EBUSY; - } else if (current_async->count) + } else if (current_async->port.count) return -EBUSY; } @@ -1456,7 +1456,7 @@ static int set_serial_info(struct esp_struct *info, * At this point, we start making changes..... */ - info->flags = ((info->flags & ~ASYNC_FLAGS) | + info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->close_delay = new_serial.close_delay * HZ/100; @@ -1487,18 +1487,18 @@ static int set_serial_info(struct esp_struct *info, } } - if (info->flags & ASYNC_INITIALIZED) { - if (((old_info.flags & ASYNC_SPD_MASK) != - (info->flags & ASYNC_SPD_MASK)) || + if (info->port.flags & ASYNC_INITIALIZED) { + if (((old_info.port.flags & ASYNC_SPD_MASK) != + (info->port.flags & ASYNC_SPD_MASK)) || (old_info.custom_divisor != info->custom_divisor)) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->port.tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->port.tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->port.tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->port.tty->alt_speed = 460800; change_speed(info); } } else @@ -1554,9 +1554,9 @@ static int set_esp_config(struct esp_struct *info, while (current_async) { if (current_async == info) { - if (current_async->count > 1) + if (current_async->port.count > 1) return -EBUSY; - } else if (current_async->count) + } else if (current_async->port.count) return -EBUSY; current_async = current_async->next_port; @@ -1578,7 +1578,7 @@ static int set_esp_config(struct esp_struct *info, spin_unlock_irqrestore(&info->lock, flags); } else { /* DMA mode to PIO mode only */ - if (info->count > 1) + if (info->port.count > 1) return -EBUSY; shutdown(info); @@ -1634,7 +1634,7 @@ static int set_esp_config(struct esp_struct *info, spin_unlock_irqrestore(&info->lock, flags); } - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) retval = startup(info); return retval; @@ -1917,9 +1917,9 @@ static void rs_close(struct tty_struct *tty, struct file *filp) #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "rs_close ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif - if (tty->count == 1 && info->count != 1) { + if (tty->count == 1 && info->port.count != 1) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -1927,19 +1927,19 @@ static void rs_close(struct tty_struct *tty, struct file *filp) * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->count is %d\n", info->count); - info->count = 1; + printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->port.count is %d\n", info->port.count); + info->port.count = 1; } - if (--info->count < 0) { + if (--info->port.count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->port.count); + info->port.count = 0; } - if (info->count) { + if (info->port.count) { DBG_CNT("before DEC-2"); goto out; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; spin_unlock_irqrestore(&info->lock, flags); /* @@ -1958,7 +1958,7 @@ static void rs_close(struct tty_struct *tty, struct file *filp) /* info->IER &= ~UART_IER_RLSI; */ info->IER &= ~UART_IER_RDI; info->read_status_mask &= ~UART_LSR_DR; - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); @@ -1981,15 +1981,15 @@ static void rs_close(struct tty_struct *tty, struct file *filp) rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->tty = NULL; + info->port.tty = NULL; - if (info->blocked_open) { + if (info->port.blocked_open) { if (info->close_delay) msleep_interruptible(jiffies_to_msecs(info->close_delay)); - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->port.close_wait); return; out: @@ -2047,10 +2047,10 @@ static void esp_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + wake_up_interruptible(&info->port.open_wait); } /* @@ -2071,11 +2071,11 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + (info->port.flags & ASYNC_CLOSING)) { + if (info->port.flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->port.close_wait); #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -2090,7 +2090,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2100,20 +2100,20 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif spin_lock_irqsave(&info->lock, flags); if (!tty_hung_up_p(filp)) - info->count--; - info->blocked_open++; + info->port.count--; + info->port.blocked_open++; while (1) { if ((tty->termios->c_cflag & CBAUD)) { unsigned int scratch; @@ -2128,9 +2128,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(info->port.flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -2144,7 +2144,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD) do_clocal = 1; - if (!(info->flags & ASYNC_CLOSING) && + if (!(info->port.flags & ASYNC_CLOSING) && (do_clocal)) break; if (signal_pending(current)) { @@ -2153,25 +2153,25 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif spin_unlock_irqrestore(&info->lock, flags); schedule(); spin_lock_irqsave(&info->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; + info->port.count++; + info->port.blocked_open--; spin_unlock_irqrestore(&info->lock, flags); #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2204,12 +2204,12 @@ static int esp_open(struct tty_struct *tty, struct file *filp) } #ifdef SERIAL_DEBUG_OPEN - printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->count); + printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->port.count); #endif spin_lock_irqsave(&info->lock, flags); - info->count++; + info->port.count++; tty->driver_data = info; - info->tty = tty; + info->port.tty = tty; spin_unlock_irqrestore(&info->lock, flags); @@ -2263,7 +2263,7 @@ static int autoconfig(struct esp_struct *info) int port_detected = 0; unsigned long flags; - if (!request_region(info->port, REGION_SIZE, "esp serial")) + if (!request_region(info->io_port, REGION_SIZE, "esp serial")) return -EIO; spin_lock_irqsave(&info->lock, flags); @@ -2300,7 +2300,7 @@ static int autoconfig(struct esp_struct *info) } } if (!port_detected) - release_region(info->port, REGION_SIZE); + release_region(info->io_port, REGION_SIZE); spin_unlock_irqrestore(&info->lock, flags); return (port_detected); @@ -2414,7 +2414,7 @@ static int __init espserial_init(void) offset = 0; do { - info->port = esp[i] + offset; + info->io_port = esp[i] + offset; info->irq = irq[i]; info->line = (i * 8) + (offset / 8); @@ -2425,9 +2425,9 @@ static int __init espserial_init(void) } info->custom_divisor = (divisor[i] >> (offset / 2)) & 0xf; - info->flags = STD_COM_FLAGS; + info->port.flags = STD_COM_FLAGS; if (info->custom_divisor) - info->flags |= ASYNC_SPD_CUST; + info->port.flags |= ASYNC_SPD_CUST; info->magic = ESP_MAGIC; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; @@ -2436,13 +2436,13 @@ static int __init espserial_init(void) info->config.flow_off = flow_off; info->config.pio_threshold = pio_threshold; info->next_port = ports; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->port.open_wait); + init_waitqueue_head(&info->port.close_wait); init_waitqueue_head(&info->delta_msr_wait); init_waitqueue_head(&info->break_wait); ports = info; printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ", - info->line, info->port, info->irq); + info->line, info->io_port, info->irq); if (info->line % 8) { printk("secondary port\n"); @@ -2498,8 +2498,8 @@ static void __exit espserial_exit(void) put_tty_driver(esp_driver); while (ports) { - if (ports->port) - release_region(ports->port, REGION_SIZE); + if (ports->io_port) + release_region(ports->io_port, REGION_SIZE); temp_async = ports->next_port; kfree(ports); ports = temp_async; diff --git a/include/linux/hayesesp.h b/include/linux/hayesesp.h index 2177ee5b2fe2..940aeb51d53f 100644 --- a/include/linux/hayesesp.h +++ b/include/linux/hayesesp.h @@ -76,11 +76,10 @@ struct hayes_esp_config { struct esp_struct { int magic; + struct tty_port port; spinlock_t lock; - int port; + int io_port; int irq; - int flags; /* defined in tty.h */ - struct tty_struct *tty; int read_status_mask; int ignore_status_mask; int timeout; @@ -93,14 +92,10 @@ struct esp_struct { int MCR; /* Modem control register */ unsigned long last_active; int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; wait_queue_head_t break_wait; struct async_icount icount; /* kernel counters for the 4 input interrupts */ -- cgit v1.2.3 From b5391e29f428d11755ca2c91074c6db6f5c69d7c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:55:20 +0100 Subject: gs: use tty_port Switch drivers using the old "generic serial" driver to use the tty_port structures Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/generic_serial.c | 158 ++++++++++++++++++++--------------------- drivers/char/rio/rio_linux.c | 20 +++--- drivers/char/rio/riocmd.c | 10 +-- drivers/char/rio/riointr.c | 10 +-- drivers/char/rio/rioparam.c | 2 +- drivers/char/rio/riotty.c | 16 ++--- drivers/char/sx.c | 115 +++++++++++++++--------------- include/linux/generic_serial.h | 8 +-- 8 files changed, 166 insertions(+), 173 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 252f73e48596..19d3afb0e50c 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -60,7 +60,7 @@ int gs_put_char(struct tty_struct * tty, unsigned char ch) if (!port) return 0; - if (! (port->flags & ASYNC_INITIALIZED)) return 0; + if (! (port->port.flags & ASYNC_INITIALIZED)) return 0; /* Take a lock on the serial tranmit buffer! */ mutex_lock(& port->port_write_mutex); @@ -103,7 +103,7 @@ int gs_write(struct tty_struct * tty, if (!port) return 0; - if (! (port->flags & ASYNC_INITIALIZED)) + if (! (port->port.flags & ASYNC_INITIALIZED)) return 0; /* get exclusive "write" access to this port (problem 3) */ @@ -141,13 +141,13 @@ int gs_write(struct tty_struct * tty, mutex_unlock(& port->port_write_mutex); gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n", - (port->flags & GS_TX_INTEN)?"enabled": "disabled"); + (port->port.flags & GS_TX_INTEN)?"enabled": "disabled"); if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(port->flags & GS_TX_INTEN)) { - port->flags |= GS_TX_INTEN; + !(port->port.flags & GS_TX_INTEN)) { + port->port.flags |= GS_TX_INTEN; port->rd->enable_tx_interrupts (port); } func_exit (); @@ -208,7 +208,7 @@ static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port); if (port) { gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n", - port->xmit_cnt, port->xmit_buf, port->tty); + port->xmit_cnt, port->xmit_buf, port->port.tty); } if (!port || port->xmit_cnt < 0 || !port->xmit_buf) { @@ -217,7 +217,7 @@ static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) return -EINVAL; /* This is an error which we don't know how to handle. */ } - rcib = gs_real_chars_in_buffer(port->tty); + rcib = gs_real_chars_in_buffer(port->port.tty); if(rcib <= 0) { gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n"); @@ -236,7 +236,7 @@ static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) /* the expression is actually jiffies < end_jiffies, but that won't work around the wraparound. Tricky eh? */ - while ((charsleft = gs_real_chars_in_buffer (port->tty)) && + while ((charsleft = gs_real_chars_in_buffer (port->port.tty)) && time_after (end_jiffies, jiffies)) { /* Units check: chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies! @@ -309,7 +309,7 @@ void gs_flush_chars(struct tty_struct * tty) } /* Beats me -- REW */ - port->flags |= GS_TX_INTEN; + port->port.flags |= GS_TX_INTEN; port->rd->enable_tx_interrupts (port); func_exit (); } @@ -329,8 +329,8 @@ void gs_stop(struct tty_struct * tty) if (port->xmit_cnt && port->xmit_buf && - (port->flags & GS_TX_INTEN) ) { - port->flags &= ~GS_TX_INTEN; + (port->port.flags & GS_TX_INTEN) ) { + port->port.flags &= ~GS_TX_INTEN; port->rd->disable_tx_interrupts (port); } func_exit (); @@ -349,8 +349,8 @@ void gs_start(struct tty_struct * tty) if (port->xmit_cnt && port->xmit_buf && - !(port->flags & GS_TX_INTEN) ) { - port->flags |= GS_TX_INTEN; + !(port->port.flags & GS_TX_INTEN) ) { + port->port.flags |= GS_TX_INTEN; port->rd->enable_tx_interrupts (port); } func_exit (); @@ -365,7 +365,7 @@ static void gs_shutdown_port (struct gs_port *port) if (!port) return; - if (!(port->flags & ASYNC_INITIALIZED)) + if (!(port->port.flags & ASYNC_INITIALIZED)) return; spin_lock_irqsave(&port->driver_lock, flags); @@ -375,12 +375,12 @@ static void gs_shutdown_port (struct gs_port *port) port->xmit_buf = NULL; } - if (port->tty) - set_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->port.tty) + set_bit(TTY_IO_ERROR, &port->port.tty->flags); port->rd->shutdown_port (port); - port->flags &= ~ASYNC_INITIALIZED; + port->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&port->driver_lock, flags); func_exit(); @@ -396,16 +396,16 @@ void gs_hangup(struct tty_struct *tty) if (!tty) return; port = tty->driver_data; - tty = port->tty; + tty = port->port.tty; if (!tty) return; gs_shutdown_port (port); - port->flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE); - port->tty = NULL; - port->count = 0; + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE); + port->port.tty = NULL; + port->port.count = 0; - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); func_exit (); } @@ -424,7 +424,7 @@ int gs_block_til_ready(void *port_, struct file * filp) if (!port) return 0; - tty = port->tty; + tty = port->port.tty; if (!tty) return 0; @@ -433,9 +433,9 @@ int gs_block_til_ready(void *port_, struct file * filp) * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) + if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->port.close_wait); + if (port->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -449,7 +449,7 @@ int gs_block_til_ready(void *port_, struct file * filp) */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -461,34 +461,34 @@ int gs_block_til_ready(void *port_, struct file * filp) /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that + * this loop, port->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->open_wait, &wait); + add_wait_queue(&port->port.open_wait, &wait); gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); spin_lock_irqsave(&port->driver_lock, flags); if (!tty_hung_up_p(filp)) { - port->count--; + port->port.count--; } spin_unlock_irqrestore(&port->driver_lock, flags); - port->blocked_open++; + port->port.blocked_open++; while (1) { CD = port->rd->get_CD (port); gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD); set_current_state (TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) + !(port->port.flags & ASYNC_INITIALIZED)) { + if (port->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->flags & ASYNC_CLOSING) && + if (!(port->port.flags & ASYNC_CLOSING) && (do_clocal || CD)) break; gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n", @@ -500,17 +500,17 @@ int gs_block_til_ready(void *port_, struct file * filp) schedule(); } gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n", - port->blocked_open); + port->port.blocked_open); set_current_state (TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); + remove_wait_queue(&port->port.open_wait, &wait); if (!tty_hung_up_p(filp)) { - port->count++; + port->port.count++; } - port->blocked_open--; + port->port.blocked_open--; if (retval) return retval; - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; func_exit (); return 0; } @@ -529,10 +529,10 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (!port) return; - if (!port->tty) { + if (!port->port.tty) { /* This seems to happen when this is called from vhangup. */ - gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n"); - port->tty = tty; + gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->port.tty is NULL\n"); + port->port.tty = tty; } spin_lock_irqsave(&port->driver_lock, flags); @@ -545,23 +545,23 @@ void gs_close(struct tty_struct * tty, struct file * filp) return; } - if ((tty->count == 1) && (port->count != 1)) { + if ((tty->count == 1) && (port->port.count != 1)) { printk(KERN_ERR "gs: gs_close port %p: bad port count;" - " tty->count is 1, port count is %d\n", port, port->count); - port->count = 1; + " tty->count is 1, port count is %d\n", port, port->port.count); + port->port.count = 1; } - if (--port->count < 0) { - printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->count); - port->count = 0; + if (--port->port.count < 0) { + printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->port.count); + port->port.count = 0; } - if (port->count) { - gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->count); + if (port->port.count) { + gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->port.count); spin_unlock_irqrestore(&port->driver_lock, flags); func_exit (); return; } - port->flags |= ASYNC_CLOSING; + port->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify @@ -585,7 +585,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) gs_wait_tx_flushed (port, port->closing_wait); - port->flags &= ~GS_ACTIVE; + port->port.flags &= ~GS_ACTIVE; gs_flush_buffer(tty); @@ -595,18 +595,18 @@ void gs_close(struct tty_struct * tty, struct file * filp) port->event = 0; port->rd->close (port); port->rd->shutdown_port (port); - port->tty = NULL; + port->port.tty = NULL; - if (port->blocked_open) { + if (port->port.blocked_open) { if (port->close_delay) { spin_unlock_irqrestore(&port->driver_lock, flags); msleep_interruptible(jiffies_to_msecs(port->close_delay)); spin_lock_irqsave(&port->driver_lock, flags); } - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED); - wake_up_interruptible(&port->close_wait); + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED); + wake_up_interruptible(&port->port.close_wait); func_exit (); } @@ -626,10 +626,10 @@ void gs_set_termios (struct tty_struct * tty, port = tty->driver_data; if (!port) return; - if (!port->tty) { + if (!port->port.tty) { /* This seems to happen when this is called after gs_close. */ - gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->tty is NULL\n"); - port->tty = tty; + gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->port.tty is NULL\n"); + port->port.tty = tty; } @@ -651,15 +651,15 @@ void gs_set_termios (struct tty_struct * tty, baudrate = tty_get_baud_rate(tty); if ((tiosp->c_cflag & CBAUD) == B38400) { - if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ( (port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baudrate = 230400; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baudrate = 460800; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (port->baud_base / port->custom_divisor); } @@ -715,7 +715,7 @@ int gs_init_port(struct gs_port *port) func_enter (); - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { func_exit (); return 0; } @@ -737,15 +737,15 @@ int gs_init_port(struct gs_port *port) } spin_lock_irqsave (&port->driver_lock, flags); - if (port->tty) - clear_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->port.tty) + clear_bit(TTY_IO_ERROR, &port->port.tty->flags); mutex_init(&port->port_write_mutex); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; spin_unlock_irqrestore(&port->driver_lock, flags); - gs_set_termios(port->tty, NULL); + gs_set_termios(port->port.tty, NULL); spin_lock_irqsave (&port->driver_lock, flags); - port->flags |= ASYNC_INITIALIZED; - port->flags &= ~GS_TX_INTEN; + port->port.flags |= ASYNC_INITIALIZED; + port->port.flags &= ~GS_TX_INTEN; spin_unlock_irqrestore(&port->driver_lock, flags); func_exit (); @@ -764,11 +764,11 @@ int gs_setserial(struct gs_port *port, struct serial_struct __user *sp) if ((sio.baud_base != port->baud_base) || (sio.close_delay != port->close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) + (port->port.flags & ~ASYNC_USR_MASK))) return(-EPERM); } - port->flags = (port->flags & ~ASYNC_USR_MASK) | + port->port.flags = (port->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); port->baud_base = sio.baud_base; @@ -776,7 +776,7 @@ int gs_setserial(struct gs_port *port, struct serial_struct __user *sp) port->closing_wait = sio.closing_wait; port->custom_divisor = sio.custom_divisor; - gs_set_termios (port->tty, NULL); + gs_set_termios (port->port.tty, NULL); return 0; } @@ -793,7 +793,7 @@ int gs_getserial(struct gs_port *port, struct serial_struct __user *sp) struct serial_struct sio; memset(&sio, 0, sizeof(struct serial_struct)); - sio.flags = port->flags; + sio.flags = port->port.flags; sio.baud_base = port->baud_base; sio.close_delay = port->close_delay; sio.closing_wait = port->closing_wait; @@ -821,10 +821,10 @@ void gs_got_break(struct gs_port *port) { func_enter (); - tty_insert_flip_char(port->tty, 0, TTY_BREAK); - tty_schedule_flip(port->tty); - if (port->flags & ASYNC_SAK) { - do_SAK (port->tty); + tty_insert_flip_char(port->port.tty, 0, TTY_BREAK); + tty_schedule_flip(port->port.tty); + if (port->port.flags & ASYNC_SAK) { + do_SAK (port->port.tty); } func_exit (); diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index e49e6e6372f3..0cdfee152916 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -431,7 +431,7 @@ static void rio_disable_tx_interrupts(void *ptr) { func_enter(); - /* port->gs.flags &= ~GS_TX_INTEN; */ + /* port->gs.port.flags &= ~GS_TX_INTEN; */ func_exit(); } @@ -455,7 +455,7 @@ static void rio_enable_tx_interrupts(void *ptr) * In general we cannot count on "tx empty" interrupts, although * the interrupt routine seems to be able to tell the difference. */ - PortP->gs.flags &= ~GS_TX_INTEN; + PortP->gs.port.flags &= ~GS_TX_INTEN; func_exit(); } @@ -510,7 +510,7 @@ static void rio_shutdown_port(void *ptr) func_enter(); PortP = (struct Port *) ptr; - PortP->gs.tty = NULL; + PortP->gs.port.tty = NULL; func_exit(); } @@ -529,7 +529,7 @@ static void rio_hungup(void *ptr) func_enter(); PortP = (struct Port *) ptr; - PortP->gs.tty = NULL; + PortP->gs.port.tty = NULL; func_exit(); } @@ -549,12 +549,12 @@ static void rio_close(void *ptr) riotclose(ptr); - if (PortP->gs.count) { - printk(KERN_ERR "WARNING port count:%d\n", PortP->gs.count); - PortP->gs.count = 0; + if (PortP->gs.port.count) { + printk(KERN_ERR "WARNING port count:%d\n", PortP->gs.port.count); + PortP->gs.port.count = 0; } - PortP->gs.tty = NULL; + PortP->gs.port.tty = NULL; func_exit(); } @@ -849,8 +849,8 @@ static int rio_init_datastructures(void) /* * Initializing wait queue */ - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); + init_waitqueue_head(&port->gs.port.open_wait); + init_waitqueue_head(&port->gs.port.close_wait); } #else /* We could postpone initializing them to when they are configured. */ diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 391f0b4da7ea..01f2654d5a2e 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -484,12 +484,12 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc ** If the device is a modem, then check the modem ** carrier. */ - if (PortP->gs.tty == NULL) + if (PortP->gs.port.tty == NULL) break; - if (PortP->gs.tty->termios == NULL) + if (PortP->gs.port.tty->termios == NULL) break; - if (!(PortP->gs.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN | RIO_WOPEN)))) { + if (!(PortP->gs.port.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN | RIO_WOPEN)))) { rio_dprintk(RIO_DEBUG_CMD, "Is there a Carrier?\n"); /* @@ -506,7 +506,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc ** wakeup anyone in WOPEN */ if (PortP->State & (PORT_ISOPEN | RIO_WOPEN)) - wake_up_interruptible(&PortP->gs.open_wait); + wake_up_interruptible(&PortP->gs.port.open_wait); } } else { /* @@ -514,7 +514,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc */ if (PortP->State & RIO_CARR_ON) { if (PortP->State & (PORT_ISOPEN | RIO_WOPEN | RIO_MOPEN)) - tty_hangup(PortP->gs.tty); + tty_hangup(PortP->gs.port.tty); PortP->State &= ~RIO_CARR_ON; rio_dprintk(RIO_DEBUG_CMD, "Carrirer just went down\n"); } diff --git a/drivers/char/rio/riointr.c b/drivers/char/rio/riointr.c index 11c7987821c4..71f87600907c 100644 --- a/drivers/char/rio/riointr.c +++ b/drivers/char/rio/riointr.c @@ -102,7 +102,7 @@ void RIOTxEnable(char *en) PortP = (struct Port *) en; p = (struct rio_info *) PortP->p; - tty = PortP->gs.tty; + tty = PortP->gs.port.tty; rio_dprintk(RIO_DEBUG_INTR, "tx port %d: %d chars queued.\n", PortP->PortNum, PortP->gs.xmit_cnt); @@ -158,7 +158,7 @@ void RIOTxEnable(char *en) rio_spin_unlock_irqrestore(&PortP->portSem, flags); if (PortP->gs.xmit_cnt <= (PortP->gs.wakeup_chars + 2 * PKT_MAX_DATA_LEN)) - tty_wakeup(PortP->gs.tty); + tty_wakeup(PortP->gs.port.tty); } @@ -241,7 +241,7 @@ void RIOServiceHost(struct rio_info *p, struct Host *HostP) ** find corresponding tty structure. The process of mapping ** the ports puts these here. */ - ttyP = PortP->gs.tty; + ttyP = PortP->gs.port.tty; /* ** Lock the port before we begin working on it. @@ -335,7 +335,7 @@ void RIOServiceHost(struct rio_info *p, struct Host *HostP) ** find corresponding tty structure. The process of mapping ** the ports puts these here. */ - ttyP = PortP->gs.tty; + ttyP = PortP->gs.port.tty; /* If ttyP is NULL, the port is getting closed. Forget about it. */ if (!ttyP) { rio_dprintk(RIO_DEBUG_INTR, "no tty, so skipping.\n"); @@ -542,7 +542,7 @@ static void RIOReceive(struct rio_info *p, struct Port *PortP) intCount++; - TtyP = PortP->gs.tty; + TtyP = PortP->gs.port.tty; if (!TtyP) { rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: tty is null. \n"); return; diff --git a/drivers/char/rio/rioparam.c b/drivers/char/rio/rioparam.c index 447ca34a6a72..d687c17be152 100644 --- a/drivers/char/rio/rioparam.c +++ b/drivers/char/rio/rioparam.c @@ -160,7 +160,7 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) func_enter(); - TtyP = PortP->gs.tty; + TtyP = PortP->gs.port.tty; rio_dprintk(RIO_DEBUG_PARAM, "RIOParam: Port:%d cmd:%d Modem:%d SleepFlag:%d Mapped: %d, tty=%p\n", PortP->PortNum, cmd, Modem, SleepFlag, PortP->Mapped, TtyP); diff --git a/drivers/char/rio/riotty.c b/drivers/char/rio/riotty.c index 95a88a4138e8..2fb49e89b324 100644 --- a/drivers/char/rio/riotty.c +++ b/drivers/char/rio/riotty.c @@ -140,14 +140,14 @@ int riotopen(struct tty_struct *tty, struct file *filp) tty->driver_data = PortP; - PortP->gs.tty = tty; - PortP->gs.count++; + PortP->gs.port.tty = tty; + PortP->gs.port.count++; rio_dprintk(RIO_DEBUG_TTY, "%d bytes in tx buffer\n", PortP->gs.xmit_cnt); retval = gs_init_port(&PortP->gs); if (retval) { - PortP->gs.count--; + PortP->gs.port.count--; return -ENXIO; } /* @@ -293,7 +293,7 @@ int riotopen(struct tty_struct *tty, struct file *filp) ** insert test for carrier here. -- ??? ** I already see that test here. What's the deal? -- REW */ - if ((PortP->gs.tty->termios->c_cflag & CLOCAL) || + if ((PortP->gs.port.tty->termios->c_cflag & CLOCAL) || (PortP->ModemState & RIOC_MSVR1_CD)) { rio_dprintk(RIO_DEBUG_TTY, "open(%d) Modem carr on\n", SysPort); /* @@ -301,16 +301,16 @@ int riotopen(struct tty_struct *tty, struct file *filp) wakeup((caddr_t) &tp->tm.c_canq); */ PortP->State |= RIO_CARR_ON; - wake_up_interruptible(&PortP->gs.open_wait); + wake_up_interruptible(&PortP->gs.port.open_wait); } else { /* no carrier - wait for DCD */ /* - while (!(PortP->gs.tty->termios->c_state & CARR_ON) && + while (!(PortP->gs.port.tty->termios->c_state & CARR_ON) && !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted ) */ while (!(PortP->State & RIO_CARR_ON) && !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted) { rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr on\n", SysPort); /* - PortP->gs.tty->termios->c_state |= WOPEN; + PortP->gs.port.tty->termios->c_state |= WOPEN; */ PortP->State |= RIO_WOPEN; rio_spin_unlock_irqrestore(&PortP->portSem, flags); @@ -380,7 +380,7 @@ int riotclose(void *ptr) /* PortP = p->RIOPortp[SysPort]; */ rio_dprintk(RIO_DEBUG_TTY, "Port is at address %p\n", PortP); /* tp = PortP->TtyP; *//* Get tty */ - tty = PortP->gs.tty; + tty = PortP->gs.port.tty; rio_dprintk(RIO_DEBUG_TTY, "TTY is at address %p\n", tty); if (PortP->gs.closing_wait) diff --git a/drivers/char/sx.c b/drivers/char/sx.c index b1a7a8cb65ea..b1239ee48b78 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1,4 +1,3 @@ - /* sx.c -- driver for the Specialix SX series cards. * * This driver will also support the older SI, and XIO cards. @@ -930,7 +929,7 @@ static int sx_set_real_termios(void *ptr) func_enter2(); - if (!port->gs.tty) + if (!port->gs.port.tty) return 0; /* What is this doing here? -- REW @@ -941,19 +940,19 @@ static int sx_set_real_termios(void *ptr) sx_set_baud(port); -#define CFLAG port->gs.tty->termios->c_cflag +#define CFLAG port->gs.port.tty->termios->c_cflag sx_write_channel_byte(port, hi_mr1, - (C_PARENB(port->gs.tty) ? MR1_WITH : MR1_NONE) | - (C_PARODD(port->gs.tty) ? MR1_ODD : MR1_EVEN) | - (C_CRTSCTS(port->gs.tty) ? MR1_RTS_RXFLOW : 0) | + (C_PARENB(port->gs.port.tty) ? MR1_WITH : MR1_NONE) | + (C_PARODD(port->gs.port.tty) ? MR1_ODD : MR1_EVEN) | + (C_CRTSCTS(port->gs.port.tty) ? MR1_RTS_RXFLOW : 0) | (((CFLAG & CSIZE) == CS8) ? MR1_8_BITS : 0) | (((CFLAG & CSIZE) == CS7) ? MR1_7_BITS : 0) | (((CFLAG & CSIZE) == CS6) ? MR1_6_BITS : 0) | (((CFLAG & CSIZE) == CS5) ? MR1_5_BITS : 0)); sx_write_channel_byte(port, hi_mr2, - (C_CRTSCTS(port->gs.tty) ? MR2_CTS_TXFLOW : 0) | - (C_CSTOPB(port->gs.tty) ? MR2_2_STOP : + (C_CRTSCTS(port->gs.port.tty) ? MR2_CTS_TXFLOW : 0) | + (C_CSTOPB(port->gs.port.tty) ? MR2_2_STOP : MR2_1_STOP)); switch (CFLAG & CSIZE) { @@ -976,44 +975,44 @@ static int sx_set_real_termios(void *ptr) } sx_write_channel_byte(port, hi_prtcl, - (I_IXON(port->gs.tty) ? SP_TXEN : 0) | - (I_IXOFF(port->gs.tty) ? SP_RXEN : 0) | - (I_IXANY(port->gs.tty) ? SP_TANY : 0) | SP_DCEN); + (I_IXON(port->gs.port.tty) ? SP_TXEN : 0) | + (I_IXOFF(port->gs.port.tty) ? SP_RXEN : 0) | + (I_IXANY(port->gs.port.tty) ? SP_TANY : 0) | SP_DCEN); sx_write_channel_byte(port, hi_break, - (I_IGNBRK(port->gs.tty) ? BR_IGN : 0 | - I_BRKINT(port->gs.tty) ? BR_INT : 0)); + (I_IGNBRK(port->gs.port.tty) ? BR_IGN : 0 | + I_BRKINT(port->gs.port.tty) ? BR_INT : 0)); - sx_write_channel_byte(port, hi_txon, START_CHAR(port->gs.tty)); - sx_write_channel_byte(port, hi_rxon, START_CHAR(port->gs.tty)); - sx_write_channel_byte(port, hi_txoff, STOP_CHAR(port->gs.tty)); - sx_write_channel_byte(port, hi_rxoff, STOP_CHAR(port->gs.tty)); + sx_write_channel_byte(port, hi_txon, START_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_rxon, START_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_txoff, STOP_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_rxoff, STOP_CHAR(port->gs.port.tty)); sx_reconfigure_port(port); /* Tell line discipline whether we will do input cooking */ - if (I_OTHER(port->gs.tty)) { - clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + if (I_OTHER(port->gs.port.tty)) { + clear_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags); } else { - set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + set_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags); } sx_dprintk(SX_DEBUG_TERMIOS, "iflags: %x(%d) ", - (unsigned int)port->gs.tty->termios->c_iflag, - I_OTHER(port->gs.tty)); + (unsigned int)port->gs.port.tty->termios->c_iflag, + I_OTHER(port->gs.port.tty)); /* Tell line discipline whether we will do output cooking. * If OPOST is set and no other output flags are set then we can do output * processing. Even if only *one* other flag in the O_OTHER group is set * we do cooking in software. */ - if (O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty)) { - set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + if (O_OPOST(port->gs.port.tty) && !O_OTHER(port->gs.port.tty)) { + set_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags); } else { - clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + clear_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags); } sx_dprintk(SX_DEBUG_TERMIOS, "oflags: %x(%d)\n", - (unsigned int)port->gs.tty->termios->c_oflag, - O_OTHER(port->gs.tty)); + (unsigned int)port->gs.port.tty->termios->c_oflag, + O_OTHER(port->gs.port.tty)); /* port->c_dcd = sx_get_CD (port); */ func_exit(); return 0; @@ -1102,8 +1101,8 @@ static void sx_transmit_chars(struct sx_port *port) sx_disable_tx_interrupts(port); } - if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - tty_wakeup(port->gs.tty); + if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) { + tty_wakeup(port->gs.port.tty); sx_dprintk(SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); } @@ -1126,7 +1125,7 @@ static inline void sx_receive_chars(struct sx_port *port) unsigned char *rp; func_enter2(); - tty = port->gs.tty; + tty = port->gs.port.tty; while (1) { rx_op = sx_read_channel_byte(port, hi_rxopos); c = (sx_read_channel_byte(port, hi_rxipos) - rx_op) & 0xff; @@ -1211,12 +1210,12 @@ static inline void sx_check_modem_signals(struct sx_port *port) /* DCD went UP */ if ((sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) && - !(port->gs.tty->termios-> + !(port->gs.port.tty->termios-> c_cflag & CLOCAL)) { /* Are we blocking in open? */ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " "active, unblocking open\n"); - wake_up_interruptible(&port->gs. + wake_up_interruptible(&port->gs.port. open_wait); } else { sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " @@ -1224,10 +1223,10 @@ static inline void sx_check_modem_signals(struct sx_port *port) } } else { /* DCD went down! */ - if (!(port->gs.tty->termios->c_cflag & CLOCAL)){ + if (!(port->gs.port.tty->termios->c_cflag & CLOCAL)){ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " "dropped. hanging up....\n"); - tty_hangup(port->gs.tty); + tty_hangup(port->gs.port.tty); } else { sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " "dropped. ignoring.\n"); @@ -1325,7 +1324,7 @@ static irqreturn_t sx_interrupt(int irq, void *ptr) for (i = 0; i < board->nports; i++) { port = &board->ports[i]; - if (port->gs.flags & GS_ACTIVE) { + if (port->gs.port.flags & GS_ACTIVE) { if (sx_read_channel_byte(port, hi_state)) { sx_dprintk(SX_DEBUG_INTERRUPTS, "Port %d: " "modem signal change?... \n",i); @@ -1334,7 +1333,7 @@ static irqreturn_t sx_interrupt(int irq, void *ptr) if (port->gs.xmit_cnt) { sx_transmit_chars(port); } - if (!(port->gs.flags & SX_RX_THROTTLE)) { + if (!(port->gs.port.flags & SX_RX_THROTTLE)) { sx_receive_chars(port); } } @@ -1373,7 +1372,7 @@ static void sx_disable_tx_interrupts(void *ptr) struct sx_port *port = ptr; func_enter2(); - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; func_exit(); } @@ -1394,7 +1393,7 @@ static void sx_enable_tx_interrupts(void *ptr) /* XXX Must be "HIGH_WATER" for SI card according to doc. */ if (data_in_buffer < LOW_WATER) - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; func_exit(); } @@ -1442,8 +1441,8 @@ static void sx_shutdown_port(void *ptr) func_enter(); - port->gs.flags &= ~GS_ACTIVE; - if (port->gs.tty && (port->gs.tty->termios->c_cflag & HUPCL)) { + port->gs.port.flags &= ~GS_ACTIVE; + if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) { sx_setsignals(port, 0, 0); sx_reconfigure_port(port); } @@ -1485,8 +1484,8 @@ static int sx_open(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&port->gs.driver_lock, flags); tty->driver_data = port; - port->gs.tty = tty; - port->gs.count++; + port->gs.port.tty = tty; + port->gs.port.count++; spin_unlock_irqrestore(&port->gs.driver_lock, flags); sx_dprintk(SX_DEBUG_OPEN, "starting port\n"); @@ -1497,12 +1496,12 @@ static int sx_open(struct tty_struct *tty, struct file *filp) retval = gs_init_port(&port->gs); sx_dprintk(SX_DEBUG_OPEN, "done gs_init\n"); if (retval) { - port->gs.count--; + port->gs.port.count--; return retval; } - port->gs.flags |= GS_ACTIVE; - if (port->gs.count <= 1) + port->gs.port.flags |= GS_ACTIVE; + if (port->gs.port.count <= 1) sx_setsignals(port, 1, 1); #if 0 @@ -1513,12 +1512,12 @@ static int sx_open(struct tty_struct *tty, struct file *filp) my_hd_io(port->board->base + port->ch_base, sizeof(*port)); #endif - if (port->gs.count <= 1) { + if (port->gs.port.count <= 1) { if (sx_send_command(port, HS_LOPEN, -1, HS_IDLE_OPEN) != 1) { printk(KERN_ERR "sx: Card didn't respond to LOPEN " "command.\n"); spin_lock_irqsave(&port->gs.driver_lock, flags); - port->gs.count--; + port->gs.port.count--; spin_unlock_irqrestore(&port->gs.driver_lock, flags); return -EIO; } @@ -1526,11 +1525,11 @@ static int sx_open(struct tty_struct *tty, struct file *filp) retval = gs_block_til_ready(port, filp); sx_dprintk(SX_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n", - retval, port->gs.count); + retval, port->gs.port.count); if (retval) { /* - * Don't lower gs.count here because sx_close() will be called later + * Don't lower gs.port.count here because sx_close() will be called later */ return retval; @@ -1571,14 +1570,14 @@ static void sx_close(void *ptr) } sx_dprintk(SX_DEBUG_CLOSE, "waited %d jiffies for close. count=%d\n", - 5 * HZ - to - 1, port->gs.count); + 5 * HZ - to - 1, port->gs.port.count); - if (port->gs.count) { + if (port->gs.port.count) { sx_dprintk(SX_DEBUG_CLOSE, "WARNING port count:%d\n", - port->gs.count); + port->gs.port.count); /*printk("%s SETTING port count to zero: %p count: %d\n", - __func__, port, port->gs.count); - port->gs.count = 0;*/ + __func__, port, port->gs.port.count); + port->gs.port.count = 0;*/ } func_exit(); @@ -1939,7 +1938,7 @@ static void sx_throttle(struct tty_struct *tty) * control then throttle the port. */ if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty))) { - port->gs.flags |= SX_RX_THROTTLE; + port->gs.port.flags |= SX_RX_THROTTLE; } func_exit(); } @@ -1953,7 +1952,7 @@ static void sx_unthrottle(struct tty_struct *tty) * this port in case we disabled flow control while the port * was throttled */ - port->gs.flags &= ~SX_RX_THROTTLE; + port->gs.port.flags &= ~SX_RX_THROTTLE; func_exit(); return; } @@ -2408,9 +2407,7 @@ static int sx_init_portstructs(int nboards, int nports) /* * Initializing wait queue */ - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); - + tty_port_init(&port->gs.port); port++; } } diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h index 110833666e37..4cc913939817 100644 --- a/include/linux/generic_serial.h +++ b/include/linux/generic_serial.h @@ -14,6 +14,7 @@ #ifdef __KERNEL__ #include +#include struct real_driver { void (*disable_tx_interrupts) (void *); @@ -33,17 +34,12 @@ struct real_driver { struct gs_port { int magic; + struct tty_port port; unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; struct mutex port_write_mutex; - int flags; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - int count; - int blocked_open; - struct tty_struct *tty; unsigned long event; unsigned short closing_wait; int close_delay; -- cgit v1.2.3 From b02f5ad6a3ff5a1ee2a7b8ec6eee338de553b060 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:55:53 +0100 Subject: istallion: use tty_port Switch istallion to use the new tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/istallion.c | 116 +++++++++++++++++++++++----------------------- include/linux/istallion.h | 6 +-- 2 files changed, 59 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 7c8b62f162bf..6ef1c565705c 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -735,8 +735,8 @@ static void stli_cleanup_ports(struct stlibrd *brdp) for (j = 0; j < STL_MAXPORTS; j++) { portp = brdp->ports[j]; if (portp != NULL) { - if (portp->tty != NULL) - tty_hangup(portp->tty); + if (portp->port.tty != NULL) + tty_hangup(portp->port.tty); kfree(portp); } } @@ -811,9 +811,9 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * The sleep here does not need interrupt protection since the wakeup * for it is done with the same context. */ - if (portp->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->close_wait); - if (portp->flags & ASYNC_HUP_NOTIFY) + if (portp->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&portp->port.close_wait); + if (portp->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; return -ERESTARTSYS; } @@ -824,7 +824,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * requires several commands to the board we will need to wait for any * other open that is already initializing the port. */ - portp->tty = tty; + portp->port.tty = tty; tty->driver_data = portp; portp->refcount++; @@ -833,10 +833,10 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if (signal_pending(current)) return -ERESTARTSYS; - if ((portp->flags & ASYNC_INITIALIZED) == 0) { + if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { set_bit(ST_INITIALIZING, &portp->state); if ((rc = stli_initopen(brdp, portp)) >= 0) { - portp->flags |= ASYNC_INITIALIZED; + portp->port.flags |= ASYNC_INITIALIZED; clear_bit(TTY_IO_ERROR, &tty->flags); } clear_bit(ST_INITIALIZING, &portp->state); @@ -851,9 +851,9 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * The sleep here does not need interrupt protection since the wakeup * for it is done with the same context. */ - if (portp->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->close_wait); - if (portp->flags & ASYNC_HUP_NOTIFY) + if (portp->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&portp->port.close_wait); + if (portp->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; return -ERESTARTSYS; } @@ -867,7 +867,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0) return rc; } - portp->flags |= ASYNC_NORMAL_ACTIVE; + portp->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -895,7 +895,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) return; } - portp->flags |= ASYNC_CLOSING; + portp->port.flags |= ASYNC_CLOSING; /* * May want to wait for data to drain before closing. The BUSY flag @@ -911,7 +911,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, portp->closing_wait); - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; brdp = stli_brds[portp->brdnr]; stli_rawclose(brdp, portp, 0, 0); if (tty->termios->c_cflag & HUPCL) { @@ -931,16 +931,16 @@ static void stli_close(struct tty_struct *tty, struct file *filp) stli_flushbuffer(tty); tty->closing = 0; - portp->tty = NULL; + portp->port.tty = NULL; if (portp->openwaitcnt) { if (portp->close_delay) msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } - portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&portp->close_wait); + portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&portp->port.close_wait); } /*****************************************************************************/ @@ -970,7 +970,7 @@ static int stli_initopen(struct stlibrd *brdp, struct stliport *portp) sizeof(asynotify_t), 0)) < 0) return rc; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return -ENODEV; stli_mkasyport(portp, &aport, tty->termios); @@ -1169,7 +1169,7 @@ static int stli_setport(struct stliport *portp) if (portp == NULL) return -ENODEV; - if (portp->tty == NULL) + if (portp->port.tty == NULL) return -ENODEV; if (portp->brdnr >= stli_nrbrds) return -ENODEV; @@ -1177,7 +1177,7 @@ static int stli_setport(struct stliport *portp) if (brdp == NULL) return -ENODEV; - stli_mkasyport(portp, &aport, portp->tty->termios); + stli_mkasyport(portp, &aport, portp->port.tty->termios); return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)); } @@ -1196,7 +1196,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct rc = 0; doclocal = 0; - if (portp->tty->termios->c_cflag & CLOCAL) + if (portp->port.tty->termios->c_cflag & CLOCAL) doclocal++; spin_lock_irqsave(&stli_lock, flags); @@ -1211,14 +1211,14 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct &portp->asig, sizeof(asysigs_t), 0)) < 0) break; if (tty_hung_up_p(filp) || - ((portp->flags & ASYNC_INITIALIZED) == 0)) { - if (portp->flags & ASYNC_HUP_NOTIFY) + ((portp->port.flags & ASYNC_INITIALIZED) == 0)) { + if (portp->port.flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else rc = -ERESTARTSYS; break; } - if (((portp->flags & ASYNC_CLOSING) == 0) && + if (((portp->port.flags & ASYNC_CLOSING) == 0) && (doclocal || (portp->sigs & TIOCM_CD))) { break; } @@ -1226,7 +1226,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct rc = -ERESTARTSYS; break; } - interruptible_sleep_on(&portp->open_wait); + interruptible_sleep_on(&portp->port.open_wait); } spin_lock_irqsave(&stli_lock, flags); @@ -1548,7 +1548,7 @@ static int stli_getserial(struct stliport *portp, struct serial_struct __user *s sio.type = PORT_UNKNOWN; sio.line = portp->portnr; sio.irq = 0; - sio.flags = portp->flags; + sio.flags = portp->port.flags; sio.baud_base = portp->baud_base; sio.close_delay = portp->close_delay; sio.closing_wait = portp->closing_wait; @@ -1583,11 +1583,11 @@ static int stli_setserial(struct stliport *portp, struct serial_struct __user *s if ((sio.baud_base != portp->baud_base) || (sio.close_delay != portp->close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != - (portp->flags & ~ASYNC_USR_MASK))) + (portp->port.flags & ~ASYNC_USR_MASK))) return -EPERM; } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; @@ -1751,7 +1751,7 @@ static void stli_settermios(struct tty_struct *tty, struct ktermios *old) if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -1834,7 +1834,7 @@ static void stli_hangup(struct tty_struct *tty) if (brdp == NULL) return; - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; if (!test_bit(ST_CLOSING, &portp->state)) stli_rawclose(brdp, portp, 0, 0); @@ -1855,12 +1855,12 @@ static void stli_hangup(struct tty_struct *tty) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - portp->tty = NULL; - portp->flags &= ~ASYNC_NORMAL_ACTIVE; + portp->port.tty = NULL; + portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; portp->refcount = 0; spin_unlock_irqrestore(&stli_lock, flags); - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -2188,7 +2188,7 @@ static void stli_read(struct stlibrd *brdp, struct stliport *portp) if (test_bit(ST_RXSTOP, &portp->state)) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -2362,7 +2362,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) if (ap->notify) { nt = ap->changed; ap->notify = 0; - tty = portp->tty; + tty = portp->port.tty; if (nt.signal & SG_DCD) { oldsigs = portp->sigs; @@ -2370,10 +2370,10 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) clear_bit(ST_GETSIGS, &portp->state); if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { - if (portp->flags & ASYNC_CHECK_CD) { + if (portp->port.flags & ASYNC_CHECK_CD) { if (tty) tty_hangup(tty); } @@ -2392,7 +2392,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) { if (tty != NULL) { tty_insert_flip_char(tty, 0, TTY_BREAK); - if (portp->flags & ASYNC_SAK) { + if (portp->port.flags & ASYNC_SAK) { do_SAK(tty); EBRDENABLE(brdp); } @@ -2542,17 +2542,17 @@ static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermio /* * Start of by setting the baud, char size, parity and stop bit info. */ - pp->baudout = tty_get_baud_rate(portp->tty); + pp->baudout = tty_get_baud_rate(portp->port.tty); if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) pp->baudout = 57600; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) pp->baudout = 115200; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) pp->baudout = 230400; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) pp->baudout = 460800; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) pp->baudout = (portp->baud_base / portp->custom_divisor); } if (pp->baudout > STL_MAXBAUD) @@ -2625,9 +2625,9 @@ static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermio * Set up clocal processing as required. */ if (tiosp->c_cflag & CLOCAL) - portp->flags &= ~ASYNC_CHECK_CD; + portp->port.flags &= ~ASYNC_CHECK_CD; else - portp->flags |= ASYNC_CHECK_CD; + portp->port.flags |= ASYNC_CHECK_CD; /* * Transfer any persistent flags into the asyport structure. @@ -2703,8 +2703,8 @@ static int stli_initports(struct stlibrd *brdp) portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; - init_waitqueue_head(&portp->open_wait); - init_waitqueue_head(&portp->close_wait); + init_waitqueue_head(&portp->port.open_wait); + init_waitqueue_head(&portp->port.close_wait); init_waitqueue_head(&portp->raw_wait); panelport++; if (panelport >= brdp->panels[panelnr]) { @@ -4246,18 +4246,18 @@ static int stli_portcmdstats(struct stliport *portp) stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; stli_comstats.state = portp->state; - stli_comstats.flags = portp->flags; + stli_comstats.flags = portp->port.flag; spin_lock_irqsave(&brd_lock, flags); - if (portp->tty != NULL) { - if (portp->tty->driver_data == portp) { - stli_comstats.ttystate = portp->tty->flags; + if (portp->port.tty != NULL) { + if (portp->port.tty->driver_data == portp) { + stli_comstats.ttystate = portp->port.tty->flags; stli_comstats.rxbuffered = -1; - if (portp->tty->termios != NULL) { - stli_comstats.cflags = portp->tty->termios->c_cflag; - stli_comstats.iflags = portp->tty->termios->c_iflag; - stli_comstats.oflags = portp->tty->termios->c_oflag; - stli_comstats.lflags = portp->tty->termios->c_lflag; + if (portp->port.tty->termios != NULL) { + stli_comstats.cflags = portp->port.tty->termios->c_cflag; + stli_comstats.iflags = portp->port.tty->termios->c_iflag; + stli_comstats.oflags = portp->port.tty->termios->c_oflag; + stli_comstats.lflags = portp->port.tty->termios->c_lflag; } } } diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 5a84fe944b74..0d1840723249 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -51,25 +51,21 @@ */ struct stliport { unsigned long magic; + struct tty_port port; unsigned int portnr; unsigned int panelnr; unsigned int brdnr; unsigned long state; unsigned int devnr; - int flags; int baud_base; int custom_divisor; int close_delay; int closing_wait; - int refcount; int openwaitcnt; int rc; int argsize; void *argp; unsigned int rxmarkmsk; - struct tty_struct *tty; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; wait_queue_head_t raw_wait; struct asysigs asig; unsigned long addr; -- cgit v1.2.3 From f8ae47641611fcdf175ab8bbe89054731b16971d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:56:37 +0100 Subject: stallion: use tty_port Switch the stallion driver to use the tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/stallion.c | 160 +++++++++++++++++++++++------------------------ include/linux/stallion.h | 6 +- 2 files changed, 81 insertions(+), 85 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index d17be10c5d21..0243efb0be95 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -613,17 +613,17 @@ static void stl_cd_change(struct stlport *portp) { unsigned int oldsigs = portp->sigs; - if (!portp->tty) + if (!portp->port.tty) return; portp->sigs = stl_getsignals(portp); if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) - if (portp->flags & ASYNC_CHECK_CD) - tty_hangup(portp->tty); + if (portp->port.flags & ASYNC_CHECK_CD) + tty_hangup(portp->port.tty); } /* @@ -734,11 +734,11 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * On the first open of the device setup the port hardware, and * initialize the per port data structure. */ - portp->tty = tty; + portp->port.tty = tty; tty->driver_data = portp; - portp->refcount++; + portp->port.count++; - if ((portp->flags & ASYNC_INITIALIZED) == 0) { + if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { if (!portp->tx.buf) { portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL); if (!portp->tx.buf) @@ -752,7 +752,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) stl_enablerxtx(portp, 1, 1); stl_startrxtx(portp, 1, 0); clear_bit(TTY_IO_ERROR, &tty->flags); - portp->flags |= ASYNC_INITIALIZED; + portp->port.flags |= ASYNC_INITIALIZED; } /* @@ -761,9 +761,9 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * The sleep here does not need interrupt protection since the wakeup * for it is done with the same context. */ - if (portp->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->close_wait); - if (portp->flags & ASYNC_HUP_NOTIFY) + if (portp->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&portp->port.close_wait); + if (portp->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; return -ERESTARTSYS; } @@ -777,7 +777,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) if ((rc = stl_waitcarrier(portp, filp)) != 0) return rc; - portp->flags |= ASYNC_NORMAL_ACTIVE; + portp->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -801,25 +801,25 @@ static int stl_waitcarrier(struct stlport *portp, struct file *filp) spin_lock_irqsave(&stallion_lock, flags); - if (portp->tty->termios->c_cflag & CLOCAL) + if (portp->port.tty->termios->c_cflag & CLOCAL) doclocal++; portp->openwaitcnt++; if (! tty_hung_up_p(filp)) - portp->refcount--; + portp->port.count--; for (;;) { /* Takes brd_lock internally */ stl_setsignals(portp, 1, 1); if (tty_hung_up_p(filp) || - ((portp->flags & ASYNC_INITIALIZED) == 0)) { - if (portp->flags & ASYNC_HUP_NOTIFY) + ((portp->port.flags & ASYNC_INITIALIZED) == 0)) { + if (portp->port.flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else rc = -ERESTARTSYS; break; } - if (((portp->flags & ASYNC_CLOSING) == 0) && + if (((portp->port.flags & ASYNC_CLOSING) == 0) && (doclocal || (portp->sigs & TIOCM_CD))) break; if (signal_pending(current)) { @@ -827,11 +827,11 @@ static int stl_waitcarrier(struct stlport *portp, struct file *filp) break; } /* FIXME */ - interruptible_sleep_on(&portp->open_wait); + interruptible_sleep_on(&portp->port.open_wait); } if (! tty_hung_up_p(filp)) - portp->refcount++; + portp->port.count++; portp->openwaitcnt--; spin_unlock_irqrestore(&stallion_lock, flags); @@ -904,15 +904,15 @@ static void stl_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&stallion_lock, flags); return; } - if ((tty->count == 1) && (portp->refcount != 1)) - portp->refcount = 1; - if (portp->refcount-- > 1) { + if ((tty->count == 1) && (portp->port.count != 1)) + portp->port.count = 1; + if (portp->port.count-- > 1) { spin_unlock_irqrestore(&stallion_lock, flags); return; } - portp->refcount = 0; - portp->flags |= ASYNC_CLOSING; + portp->port.count = 0; + portp->port.flags |= ASYNC_CLOSING; /* * May want to wait for any data to drain before closing. The BUSY @@ -930,7 +930,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&stallion_lock, flags); - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&stallion_lock, flags); stl_disableintrs(portp); @@ -949,16 +949,16 @@ static void stl_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - portp->tty = NULL; + portp->port.tty = NULL; if (portp->openwaitcnt) { if (portp->close_delay) msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } - portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&portp->close_wait); + portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&portp->port.close_wait); } /*****************************************************************************/ @@ -1153,7 +1153,7 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp) memset(&sio, 0, sizeof(struct serial_struct)); sio.line = portp->portnr; sio.port = portp->ioaddr; - sio.flags = portp->flags; + sio.flags = portp->port.flags; sio.baud_base = portp->baud_base; sio.close_delay = portp->close_delay; sio.closing_wait = portp->closing_wait; @@ -1194,17 +1194,17 @@ static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp) if ((sio.baud_base != portp->baud_base) || (sio.close_delay != portp->close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != - (portp->flags & ~ASYNC_USR_MASK))) + (portp->port.flags & ~ASYNC_USR_MASK))) return -EPERM; } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; - stl_setport(portp, portp->tty->termios); + stl_setport(portp, portp->port.tty->termios); return 0; } @@ -1353,7 +1353,7 @@ static void stl_settermios(struct tty_struct *tty, struct ktermios *old) stl_start(tty); } if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -1438,7 +1438,7 @@ static void stl_hangup(struct tty_struct *tty) if (portp == NULL) return; - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; stl_disableintrs(portp); if (tty->termios->c_cflag & HUPCL) stl_setsignals(portp, 0, 0); @@ -1452,10 +1452,10 @@ static void stl_hangup(struct tty_struct *tty) portp->tx.head = NULL; portp->tx.tail = NULL; } - portp->tty = NULL; - portp->flags &= ~ASYNC_NORMAL_ACTIVE; - portp->refcount = 0; - wake_up_interruptible(&portp->open_wait); + portp->port.tty = NULL; + portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; + portp->port.count = 0; + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -1814,8 +1814,8 @@ static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; - init_waitqueue_head(&portp->open_wait); - init_waitqueue_head(&portp->close_wait); + init_waitqueue_head(&portp->port.open_wait); + init_waitqueue_head(&portp->port.close_wait); portp->stats.brd = portp->brdnr; portp->stats.panel = portp->panelnr; portp->stats.port = portp->portnr; @@ -1840,8 +1840,8 @@ static void stl_cleanup_panels(struct stlbrd *brdp) portp = panelp->ports[k]; if (portp == NULL) continue; - if (portp->tty != NULL) - stl_hangup(portp->tty); + if (portp->port.tty != NULL) + stl_hangup(portp->port.tty); kfree(portp->tx.buf); kfree(portp); } @@ -2513,7 +2513,7 @@ static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) } portp->stats.state = portp->istate; - portp->stats.flags = portp->flags; + portp->stats.flags = portp->port.flags; portp->stats.hwid = portp->hwid; portp->stats.ttystate = 0; @@ -2524,16 +2524,16 @@ static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) portp->stats.rxbuffered = 0; spin_lock_irqsave(&stallion_lock, flags); - if (portp->tty != NULL) - if (portp->tty->driver_data == portp) { - portp->stats.ttystate = portp->tty->flags; + if (portp->port.tty != NULL) + if (portp->port.tty->driver_data == portp) { + portp->stats.ttystate = portp->port.tty->flags; /* No longer available as a statistic */ - portp->stats.rxbuffered = 1; /*portp->tty->flip.count; */ - if (portp->tty->termios != NULL) { - portp->stats.cflags = portp->tty->termios->c_cflag; - portp->stats.iflags = portp->tty->termios->c_iflag; - portp->stats.oflags = portp->tty->termios->c_oflag; - portp->stats.lflags = portp->tty->termios->c_lflag; + portp->stats.rxbuffered = 1; /*portp->port.tty->flip.count; */ + if (portp->port.tty->termios != NULL) { + portp->stats.cflags = portp->port.tty->termios->c_cflag; + portp->stats.iflags = portp->port.tty->termios->c_iflag; + portp->stats.oflags = portp->port.tty->termios->c_oflag; + portp->stats.lflags = portp->port.tty->termios->c_lflag; } } spin_unlock_irqrestore(&stallion_lock, flags); @@ -2939,15 +2939,15 @@ static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp) } baudrate = stl_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baudrate = 230400; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baudrate = 460800; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } if (baudrate > STL_CD1400MAXBAUD) @@ -2969,9 +2969,9 @@ static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp) mcor1 |= MCOR1_DCD; mcor2 |= MCOR2_DCD; sreron |= SRER_MODEM; - portp->flags |= ASYNC_CHECK_CD; + portp->port.flags |= ASYNC_CHECK_CD; } else - portp->flags &= ~ASYNC_CHECK_CD; + portp->port.flags &= ~ASYNC_CHECK_CD; /* * Setup cd1400 enhanced modes if we can. In particular we want to @@ -3242,7 +3242,7 @@ static void stl_cd1400flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -3304,7 +3304,7 @@ static void stl_cd1400sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -3503,8 +3503,8 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->tty) - tty_wakeup(portp->tty); + if (portp->port.tty) + tty_wakeup(portp->port.tty); } if (len == 0) { @@ -3568,7 +3568,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) return; } portp = panelp->ports[(ioack >> 3)]; - tty = portp->tty; + tty = portp->port.tty; if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { outb((RDCR + portp->uartaddr), ioaddr); @@ -3613,7 +3613,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) if (portp->rxmarkmsk & status) { if (status & ST_BREAK) { status = TTY_BREAK; - if (portp->flags & ASYNC_SAK) { + if (portp->port.flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } @@ -3899,15 +3899,15 @@ static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp) } baudrate = stl_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baudrate = 230400; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baudrate = 460800; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } if (baudrate > STL_SC26198MAXBAUD) @@ -3922,11 +3922,11 @@ static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp) * Check what form of modem signaling is required and set it up. */ if (tiosp->c_cflag & CLOCAL) { - portp->flags &= ~ASYNC_CHECK_CD; + portp->port.flags &= ~ASYNC_CHECK_CD; } else { iopr |= IOPR_DCDCOS; imron |= IR_IOPORT; - portp->flags |= ASYNC_CHECK_CD; + portp->port.flags |= ASYNC_CHECK_CD; } /* @@ -4174,7 +4174,7 @@ static void stl_sc26198flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -4243,7 +4243,7 @@ static void stl_sc26198sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -4421,8 +4421,8 @@ static void stl_sc26198txisr(struct stlport *portp) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->tty) - tty_wakeup(portp->tty); + if (portp->port.tty) + tty_wakeup(portp->port.tty); } if (len == 0) { @@ -4475,7 +4475,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack); - tty = portp->tty; + tty = portp->port.tty; ioaddr = portp->ioaddr; outb(GIBCR, (ioaddr + XP_ADDR)); len = inb(ioaddr + XP_DATA) + 1; @@ -4527,7 +4527,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char struct tty_struct *tty; unsigned int ioaddr; - tty = portp->tty; + tty = portp->port.tty; ioaddr = portp->ioaddr; if (status & SR_RXPARITY) @@ -4544,7 +4544,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char if (portp->rxmarkmsk & status) { if (status & SR_RXBREAK) { status = TTY_BREAK; - if (portp->flags & ASYNC_SAK) { + if (portp->port.flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } diff --git a/include/linux/stallion.h b/include/linux/stallion.h index 0424d75a5aaa..336af33c6ea4 100644 --- a/include/linux/stallion.h +++ b/include/linux/stallion.h @@ -69,6 +69,7 @@ struct stlrq { */ struct stlport { unsigned long magic; + struct tty_port port; unsigned int portnr; unsigned int panelnr; unsigned int brdnr; @@ -76,12 +77,10 @@ struct stlport { int uartaddr; unsigned int pagenr; unsigned long istate; - int flags; int baud_base; int custom_divisor; int close_delay; int closing_wait; - int refcount; int openwaitcnt; int brklen; unsigned int sigs; @@ -92,9 +91,6 @@ struct stlport { unsigned long clk; unsigned long hwid; void *uartp; - struct tty_struct *tty; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; comstats_t stats; struct stlrq tx; }; -- cgit v1.2.3 From 77451e53e0a509a98eda272567869cfe96431ba9 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:57:02 +0100 Subject: cyclades: use tty_port Switch cyclades to use the new tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/cyclades.c | 315 +++++++++++++++++++++++------------------------ include/linux/cyclades.h | 7 +- 2 files changed, 158 insertions(+), 164 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index a957dbcc5a46..0144b19d1036 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -762,7 +762,7 @@ static int cy_next_channel; /* next minor available */ /* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra - * are accessed via settings in info->flags. + * are accessed via settings in info->port.flags. * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, * HI VHI @@ -1003,7 +1003,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, cy_writeb(base_addr + (CyCAR << index), save_xir); /* if there is nowhere to put the data, discard it */ - if (info->tty == NULL) { + if (info->port.tty == NULL) { if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == CyIVRRxEx) { /* exception */ data = readb(base_addr + (CyRDSR << index)); @@ -1015,7 +1015,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, goto end; } /* there is an open port for this data */ - tty = info->tty; + tty = info->port.tty; if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == CyIVRRxEx) { /* exception */ data = readb(base_addr + (CyRDSR << index)); @@ -1041,7 +1041,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, readb(base_addr + (CyRDSR << index)), TTY_BREAK); info->icount.rx++; - if (info->flags & ASYNC_SAK) + if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (data & CyFRAME) { tty_insert_flip_char(tty, @@ -1145,7 +1145,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, goto end; } info = &cinfo->ports[channel + chip * 4]; - if (info->tty == NULL) { + if (info->port.tty == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); goto end; @@ -1190,13 +1190,13 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, } goto done; } - if (info->xmit_buf == NULL) { + if (info->port.xmit_buf == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); goto done; } - if (info->tty->stopped || info->tty->hw_stopped) { + if (info->port.tty->stopped || info->port.tty->hw_stopped) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1211,7 +1211,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, * character. This is necessary because there may not be room * for the two chars needed to send a NULL.) */ - outch = info->xmit_buf[info->xmit_tail]; + outch = info->port.xmit_buf[info->xmit_tail]; if (outch) { info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & @@ -1232,7 +1232,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, } done: - tty_wakeup(info->tty); + tty_wakeup(info->port.tty); end: /* end of service */ cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f); @@ -1256,7 +1256,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, mdm_change = readb(base_addr + (CyMISR << index)); mdm_status = readb(base_addr + (CyMSVR1 << index)); - if (!info->tty) + if (!info->port.tty) goto end; if (mdm_change & CyANY_DELTA) { @@ -1273,29 +1273,29 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, wake_up_interruptible(&info->delta_msr_wait); } - if ((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)) { + if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { if (!(mdm_status & CyDCD)) { - tty_hangup(info->tty); - info->flags &= ~ASYNC_NORMAL_ACTIVE; + tty_hangup(info->port.tty); + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; } - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } - if ((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)) { - if (info->tty->hw_stopped) { + if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { + if (info->port.tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used because... !!! */ - info->tty->hw_stopped = 0; + info->port.tty->hw_stopped = 0; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); - tty_wakeup(info->tty); + tty_wakeup(info->port.tty); } } else { if (!(mdm_status & CyCTS)) { /* cy_stop isn't used because ... !!! */ - info->tty->hw_stopped = 1; + info->port.tty->hw_stopped = 1; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1449,7 +1449,7 @@ static void cyz_handle_rx(struct cyclades_port *info, struct BUF_CTRL __iomem *buf_ctrl) { struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; unsigned int char_count; int len; #ifdef BLOCKMOVE @@ -1542,7 +1542,7 @@ static void cyz_handle_tx(struct cyclades_port *info, struct BUF_CTRL __iomem *buf_ctrl) { struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; u8 data; unsigned int char_count; #ifdef BLOCKMOVE @@ -1585,7 +1585,7 @@ static void cyz_handle_tx(struct cyclades_port *info, memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), - &info->xmit_buf[info->xmit_tail], + &info->port.xmit_buf[info->xmit_tail], small_count); tx_put = (tx_put + small_count) & (tx_bufsize - 1); @@ -1597,7 +1597,7 @@ static void cyz_handle_tx(struct cyclades_port *info, } #else while (info->xmit_cnt && char_count) { - data = info->xmit_buf[info->xmit_tail]; + data = info->port.xmit_buf[info->xmit_tail]; info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); @@ -1642,7 +1642,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; - tty = info->tty; + tty = info->port.tty; if (tty == NULL) continue; @@ -1668,15 +1668,15 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) case C_CM_MDCD: info->icount.dcd++; delta_count++; - if (info->flags & ASYNC_CHECK_CD) { + if (info->port.flags & ASYNC_CHECK_CD) { if ((fw_ver > 241 ? ((u_long) param) : readl(&ch_ctrl->rs_status)) & C_RS_DCD) { - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } else { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~ASYNC_NORMAL_ACTIVE; + tty_hangup(info->port.tty); + wake_up_interruptible(&info->port.open_wait); + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; } } break; @@ -1814,7 +1814,7 @@ static void cyz_poll(unsigned long arg) for (port = 0; port < cinfo->nports; port++) { info = &cinfo->ports[port]; - tty = info->tty; + tty = info->port.tty; buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); if (!info->throttle) @@ -1853,22 +1853,22 @@ static int startup(struct cyclades_port *info) spin_lock_irqsave(&card->card_lock, flags); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!info->type) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); free_page(page); goto errout; } - if (info->xmit_buf) + if (info->port.xmit_buf) free_page(page); else - info->xmit_buf = (unsigned char *)page; + info->port.xmit_buf = (unsigned char *)page; spin_unlock_irqrestore(&card->card_lock, flags); @@ -1909,10 +1909,10 @@ static int startup(struct cyclades_port *info) cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyRxData); - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -1994,9 +1994,9 @@ static int startup(struct cyclades_port *info) /* enable send, recv, modem !!! */ - info->flags |= ASYNC_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->port.flags |= ASYNC_INITIALIZED; + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -2065,7 +2065,7 @@ static void shutdown(struct cyclades_port *info) void __iomem *base_addr; int chip, channel, index; - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; card = info->card; @@ -2087,14 +2087,14 @@ static void shutdown(struct cyclades_port *info) /* Clear delta_msr_wait queue to avoid mem leaks. */ wake_up_interruptible(&info->delta_msr_wait); - if (info->xmit_buf) { + if (info->port.xmit_buf) { unsigned char *temp; - temp = info->xmit_buf; - info->xmit_buf = NULL; + temp = info->port.xmit_buf; + info->port.xmit_buf = NULL; free_page((unsigned long)temp); } cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); #ifdef CY_DEBUG_DTR @@ -2108,9 +2108,9 @@ static void shutdown(struct cyclades_port *info) /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { struct FIRM_ID __iomem *firm_id; @@ -2136,14 +2136,14 @@ static void shutdown(struct cyclades_port *info) spin_lock_irqsave(&card->card_lock, flags); - if (info->xmit_buf) { + if (info->port.xmit_buf) { unsigned char *temp; - temp = info->xmit_buf; - info->xmit_buf = NULL; + temp = info->port.xmit_buf; + info->port.xmit_buf = NULL; free_page((unsigned long)temp); } - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { cy_writel(&ch_ctrl[channel].rs_control, (__u32)(readl(&ch_ctrl[channel].rs_control) & ~(C_RS_RTS | C_RS_DTR))); @@ -2158,9 +2158,9 @@ static void shutdown(struct cyclades_port *info) #endif } - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } @@ -2194,10 +2194,10 @@ block_til_ready(struct tty_struct *tty, struct file *filp, * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, - !(info->flags & ASYNC_CLOSING)); - return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; + if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible(info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); + return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } /* @@ -2206,32 +2206,32 @@ block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * cy_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc block_til_ready before block: ttyC%d, " - "count = %d\n", info->line, info->count); + "count = %d\n", info->line, info->port.count); #endif spin_lock_irqsave(&cinfo->card_lock, flags); if (!tty_hung_up_p(filp)) - info->count--; + info->port.count--; spin_unlock_irqrestore(&cinfo->card_lock, flags); #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc block_til_ready: (%d): decrementing count to " - "%d\n", current->pid, info->count); + "%d\n", current->pid, info->port.count); #endif - info->blocked_open++; + info->port.blocked_open++; if (!IS_CYC_Z(*cinfo)) { chip = channel >> 2; @@ -2260,8 +2260,8 @@ block_til_ready(struct tty_struct *tty, struct file *filp, set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { - retval = ((info->flags & ASYNC_HUP_NOTIFY) ? + !(info->port.flags & ASYNC_INITIALIZED)) { + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); break; } @@ -2269,7 +2269,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, spin_lock_irqsave(&cinfo->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (!(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || + if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (readb(base_addr + (CyMSVR1 << index)) & CyDCD))) { spin_unlock_irqrestore(&cinfo->card_lock, flags); @@ -2284,7 +2284,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc block_til_ready blocking: " "ttyC%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif schedule(); } @@ -2298,7 +2298,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, firm_id = base_addr + ID_ADDRESS; if (!ISZLOADED(*cinfo)) { __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); return -EINVAL; } @@ -2327,12 +2327,12 @@ block_til_ready(struct tty_struct *tty, struct file *filp, set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { - retval = ((info->flags & ASYNC_HUP_NOTIFY) ? + !(info->port.flags & ASYNC_INITIALIZED)) { + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); break; } - if (!(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || + if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) { break; @@ -2344,28 +2344,28 @@ block_til_ready(struct tty_struct *tty, struct file *filp, #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc block_til_ready blocking: " "ttyC%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif schedule(); } } __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (!tty_hung_up_p(filp)) { - info->count++; + info->port.count++; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:block_til_ready (%d): incrementing " - "count to %d\n", current->pid, info->count); + "count to %d\n", current->pid, info->port.count); #endif } - info->blocked_open--; + info->port.blocked_open--; #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:block_til_ready after blocking: ttyC%d, " - "count = %d\n", info->line, info->count); + "count = %d\n", info->line, info->port.count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } /* block_til_ready */ @@ -2456,27 +2456,27 @@ static int cy_open(struct tty_struct *tty, struct file *filp) printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); #endif tty->driver_data = info; - info->tty = tty; + info->port.tty = tty; if (serial_paranoia_check(info, tty->name, "cy_open")) return -ENODEV; #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, - info->count); + info->port.count); #endif - info->count++; + info->port.count++; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n", - current->pid, info->count); + current->pid, info->port.count); #endif /* * If the port is the middle of closing, bail out now */ - if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, - !(info->flags & ASYNC_CLOSING)); - return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; + if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible(info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); + return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } /* @@ -2641,9 +2641,9 @@ static void cy_close(struct tty_struct *tty, struct file *filp) } #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_close ttyC%d, count = %d\n", info->line, - info->count); + info->port.count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -2652,24 +2652,24 @@ static void cy_close(struct tty_struct *tty, struct file *filp) * serial port won't be shutdown. */ printk(KERN_ERR "cyc:cy_close: bad serial port count; " - "tty->count is 1, info->count is %d\n", info->count); - info->count = 1; + "tty->count is 1, info->port.count is %d\n", info->port.count); + info->port.count = 1; } #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_close at (%d): decrementing count to %d\n", - current->pid, info->count - 1); + current->pid, info->port.count - 1); #endif - if (--info->count < 0) { + if (--info->port.count < 0) { #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cyc_close setting count to 0\n"); #endif - info->count = 0; + info->port.count = 0; } - if (info->count) { + if (info->port.count) { spin_unlock_irqrestore(&card->card_lock, flags); return; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify @@ -2692,7 +2692,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) cy_writeb(base_addr + (CyCAR << index), (u_char) channel); cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyRxData); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { /* Waiting for on-board buffers to be empty before closing the port */ spin_unlock_irqrestore(&card->card_lock, flags); @@ -2731,18 +2731,18 @@ static void cy_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&card->card_lock, flags); tty->closing = 0; - info->tty = NULL; - if (info->blocked_open) { + info->port.tty = NULL; + if (info->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); if (info->close_delay) { msleep_interruptible(jiffies_to_msecs (info->close_delay)); } - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); spin_lock_irqsave(&card->card_lock, flags); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&info->port.close_wait); #ifdef CY_DEBUG_OTHER printk(KERN_DEBUG "cyc:cy_close done\n"); @@ -2777,7 +2777,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) if (serial_paranoia_check(info, tty->name, "cy_write")) return 0; - if (!info->xmit_buf) + if (!info->port.xmit_buf) return 0; spin_lock_irqsave(&info->card->card_lock, flags); @@ -2788,7 +2788,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) if (c <= 0) break; - memcpy(info->xmit_buf + info->xmit_head, buf, c); + memcpy(info->port.xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; @@ -2826,7 +2826,7 @@ static int cy_put_char(struct tty_struct *tty, unsigned char ch) if (serial_paranoia_check(info, tty->name, "cy_put_char")) return 0; - if (!info->xmit_buf) + if (!info->port.xmit_buf) return 0; spin_lock_irqsave(&info->card->card_lock, flags); @@ -2835,7 +2835,7 @@ static int cy_put_char(struct tty_struct *tty, unsigned char ch) return 0; } - info->xmit_buf[info->xmit_head++] = ch; + info->port.xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; info->idle_stats.xmit_bytes++; @@ -2860,7 +2860,7 @@ static void cy_flush_chars(struct tty_struct *tty) return; if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) + !info->port.xmit_buf) return; start_xmit(info); @@ -2988,27 +2988,27 @@ static void set_line_char(struct cyclades_port *info) int baud, baud_rate = 0; int i; - if (!info->tty || !info->tty->termios) + if (!info->port.tty || !info->port.tty->termios) return; if (info->line == -1) return; - cflag = info->tty->termios->c_cflag; - iflag = info->tty->termios->c_iflag; + cflag = info->port.tty->termios->c_cflag; + iflag = info->port.tty->termios->c_iflag; /* * Set up the tty->alt_speed kludge */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + if (info->port.tty) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->port.tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->port.tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->port.tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->port.tty->alt_speed = 460800; } card = info->card; @@ -3020,8 +3020,8 @@ static void set_line_char(struct cyclades_port *info) index = card->bus_index; /* baud rate */ - baud = tty_get_baud_rate(info->tty); - if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + baud = tty_get_baud_rate(info->port.tty); + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) baud_rate = info->baud / info->custom_divisor; @@ -3038,7 +3038,7 @@ static void set_line_char(struct cyclades_port *info) if (i == 20) i = 19; /* CD1400_MAX_SPEED */ - if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { cyy_baud_calc(info, baud_rate); } else { @@ -3059,7 +3059,7 @@ static void set_line_char(struct cyclades_port *info) /* get it right for 134.5 baud */ info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; - } else if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_rate) + 2; @@ -3108,16 +3108,16 @@ static void set_line_char(struct cyclades_port *info) /* CTS flow control flag */ if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; + info->port.flags |= ASYNC_CTS_FLOW; info->cor2 |= CyCtsAE; } else { - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; info->cor2 &= ~CyCtsAE; } if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; /*********************************************** The hardware option, CyRtsAO, presents RTS when @@ -3146,8 +3146,8 @@ static void set_line_char(struct cyclades_port *info) /* set line characteristics according configuration */ cy_writeb(base_addr + (CySCHR1 << index), - START_CHAR(info->tty)); - cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(info->tty)); + START_CHAR(info->port.tty)); + cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(info->port.tty)); cy_writeb(base_addr + (CyCOR1 << index), info->cor1); cy_writeb(base_addr + (CyCOR2 << index), info->cor2); cy_writeb(base_addr + (CyCOR3 << index), info->cor3); @@ -3163,7 +3163,7 @@ static void set_line_char(struct cyclades_port *info) (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - if (C_CLOCAL(info->tty)) { + if (C_CLOCAL(info->port.tty)) { /* without modem intr */ cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyMdmCh); @@ -3226,8 +3226,8 @@ static void set_line_char(struct cyclades_port *info) #endif } - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); spin_unlock_irqrestore(&card->card_lock, flags); } else { @@ -3250,8 +3250,8 @@ static void set_line_char(struct cyclades_port *info) buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; /* baud rate */ - baud = tty_get_baud_rate(info->tty); - if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + baud = tty_get_baud_rate(info->port.tty); + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) baud_rate = info->baud / info->custom_divisor; @@ -3266,7 +3266,7 @@ static void set_line_char(struct cyclades_port *info) /* get it right for 134.5 baud */ info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; - } else if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_rate) + 2; @@ -3318,7 +3318,7 @@ static void set_line_char(struct cyclades_port *info) } /* As the HW flow control is done in firmware, the driver doesn't need to care about it */ - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; /* XON/XOFF/XANY flow control flags */ sw_flow = 0; @@ -3337,9 +3337,9 @@ static void set_line_char(struct cyclades_port *info) /* CD sensitivity */ if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; if (baud == 0) { /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, @@ -3361,8 +3361,8 @@ static void set_line_char(struct cyclades_port *info) "was %x\n", info->line, retval); } - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); } } /* set_line_char */ @@ -3381,7 +3381,7 @@ get_serial_info(struct cyclades_port *info, tmp.port = (info->card - cy_card) * 0x100 + info->line - cinfo->first_line; tmp.irq = cinfo->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.baud_base = info->baud; @@ -3406,9 +3406,9 @@ set_serial_info(struct cyclades_port *info, new_serial.baud_base != info->baud || (new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != - (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) + (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) return -EPERM; - info->flags = (info->flags & ~ASYNC_USR_MASK) | + info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK); info->baud = new_serial.baud_base; info->custom_divisor = new_serial.custom_divisor; @@ -3422,13 +3422,13 @@ set_serial_info(struct cyclades_port *info, info->baud = new_serial.baud_base; info->custom_divisor = new_serial.custom_divisor; - info->flags = (info->flags & ~ASYNC_FLAGS) | + info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS); info->close_delay = new_serial.close_delay * HZ / 100; info->closing_wait = new_serial.closing_wait * HZ / 100; check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { set_line_char(info); return 0; } else { @@ -4097,7 +4097,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); #endif } /* cy_set_termios */ @@ -4326,14 +4326,14 @@ static void cy_hangup(struct tty_struct *tty) cy_flush_buffer(tty); shutdown(info); - info->count = 0; + info->port.count = 0; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif - info->tty = NULL; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - wake_up_interruptible(&info->open_wait); + info->port.tty = NULL; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + wake_up_interruptible(&info->port.open_wait); } /* cy_hangup */ /* @@ -4379,12 +4379,11 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->magic = CYCLADES_MAGIC; info->card = cinfo; info->line = port; - info->flags = STD_COM_FLAGS; info->closing_wait = CLOSING_WAIT_DELAY; info->close_delay = 5 * HZ / 10; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); + tty_port_init(&info->port); + info->port.flags = STD_COM_FLAGS; init_completion(&info->shutdown_wait); init_waitqueue_head(&info->delta_msr_wait); @@ -5237,7 +5236,7 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, for (j = 0; j < cy_card[i].nports; j++) { info = &cy_card[i].ports[j]; - if (info->count) + if (info->port.count) size = sprintf(buf + len, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", info->line, (cur_jifs - info->idle_stats.in_use) / @@ -5247,7 +5246,7 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, /* FIXME: double check locking */ - (long)info->tty->ldisc.ops->num); + (long)info->port.tty->ldisc.ops->num); else size = sprintf(buf + len, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 504cb2c3fa9a..a982b74a6ee6 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -550,11 +550,11 @@ struct cyclades_icount { struct cyclades_port { int magic; + struct tty_port port; struct cyclades_card *card; int line; int flags; /* defined in tty.h */ int type; /* UART type */ - struct tty_struct *tty; int read_status_mask; int ignore_status_mask; int timeout; @@ -569,11 +569,8 @@ struct cyclades_port { u8 x_char; /* to be pushed out ASAP */ int close_delay; unsigned short closing_wait; - int count; /* # of fd on device */ int breakon; int breakoff; - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; @@ -583,8 +580,6 @@ struct cyclades_port { struct cyclades_monitor mon; struct cyclades_idle_stats idle_stats; struct cyclades_icount icount; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; struct completion shutdown_wait; wait_queue_head_t delta_msr_wait; int throttle; -- cgit v1.2.3 From 44b7d1b37f786c61d0e382b6f72f605f73de284b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:57:18 +0100 Subject: tty: add more tty_port fields Move more bits into the tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/cyclades.c | 28 ++++---- drivers/char/isicom.c | 28 ++++---- drivers/char/moxa.c | 8 +-- drivers/char/mxser.c | 29 ++++---- drivers/char/riscom8.c | 24 ++++--- drivers/char/riscom8.h | 2 - drivers/char/rocket.c | 23 +++---- drivers/char/rocket.h | 4 +- drivers/char/rocket_int.h | 2 - drivers/char/specialix.c | 153 +++++++++++++++++++++---------------------- drivers/char/specialix_io8.h | 8 +-- drivers/char/sx.c | 2 +- drivers/char/synclink.c | 16 ++--- drivers/char/synclink_gt.c | 16 ++--- drivers/char/synclinkmp.c | 14 ++-- drivers/char/tty_io.c | 2 + include/linux/cyclades.h | 6 +- include/linux/tty.h | 2 + 18 files changed, 169 insertions(+), 198 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 0144b19d1036..e991dc85f2fb 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2677,8 +2677,8 @@ static void cy_close(struct tty_struct *tty, struct file *filp) */ tty->closing = 1; spin_unlock_irqrestore(&card->card_lock, flags); - if (info->closing_wait != CY_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != CY_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); spin_lock_irqsave(&card->card_lock, flags); @@ -2734,9 +2734,9 @@ static void cy_close(struct tty_struct *tty, struct file *filp) info->port.tty = NULL; if (info->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); - if (info->close_delay) { + if (info->port.close_delay) { msleep_interruptible(jiffies_to_msecs - (info->close_delay)); + (info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); spin_lock_irqsave(&card->card_lock, flags); @@ -3382,8 +3382,8 @@ get_serial_info(struct cyclades_port *info, cinfo->first_line; tmp.irq = cinfo->irq; tmp.flags = info->port.flags; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.baud_base = info->baud; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; /*!!! */ @@ -3402,7 +3402,7 @@ set_serial_info(struct cyclades_port *info, old_info = *info; if (!capable(CAP_SYS_ADMIN)) { - if (new_serial.close_delay != info->close_delay || + if (new_serial.close_delay != info->port.close_delay || new_serial.baud_base != info->baud || (new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != @@ -3424,8 +3424,8 @@ set_serial_info(struct cyclades_port *info, info->custom_divisor = new_serial.custom_divisor; info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS); - info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; + info->port.close_delay = new_serial.close_delay * HZ / 100; + info->port.closing_wait = new_serial.closing_wait * HZ / 100; check_and_exit: if (info->port.flags & ASYNC_INITIALIZED) { @@ -3971,11 +3971,11 @@ cy_ioctl(struct tty_struct *tty, struct file *file, break; #endif /* CONFIG_CYZ_INTR */ case CYSETWAIT: - info->closing_wait = (unsigned short)arg * HZ / 100; + info->port.closing_wait = (unsigned short)arg * HZ / 100; ret_val = 0; break; case CYGETWAIT: - ret_val = info->closing_wait / (HZ / 100); + ret_val = info->port.closing_wait / (HZ / 100); break; case TIOCGSERIAL: ret_val = get_serial_info(info, argp); @@ -4376,13 +4376,13 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) for (port = cinfo->first_line; port < cinfo->first_line + nports; port++) { info = &cinfo->ports[port - cinfo->first_line]; + tty_port_init(&info->port); info->magic = CYCLADES_MAGIC; info->card = cinfo; info->line = port; - info->closing_wait = CLOSING_WAIT_DELAY; - info->close_delay = 5 * HZ / 10; - tty_port_init(&info->port); + info->port.closing_wait = CLOSING_WAIT_DELAY; + info->port.close_delay = 5 * HZ / 10; info->port.flags = STD_COM_FLAGS; init_completion(&info->shutdown_wait); init_waitqueue_head(&info->delta_msr_wait); diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 5a53c15b0dc2..d4281df10c22 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -199,10 +199,8 @@ struct isi_board { struct isi_port { unsigned short magic; struct tty_port port; - int close_delay; u16 channel; u16 status; - u16 closing_wait; struct isi_board *card; unsigned char *xmit_buf; int xmit_head; @@ -1051,8 +1049,8 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) tty->closing = 1; spin_unlock_irqrestore(&card->card_lock, flags); - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->port.closing_wait); /* indicate to the card that no more data can be received on this port */ spin_lock_irqsave(&card->card_lock, flags); @@ -1071,10 +1069,10 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) if (port->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); - if (port->close_delay) { + if (port->port.close_delay) { pr_dbg("scheduling until time out.\n"); msleep_interruptible( - jiffies_to_msecs(port->close_delay)); + jiffies_to_msecs(port->port.close_delay)); } spin_lock_irqsave(&card->card_lock, flags); wake_up_interruptible(&port->port.open_wait); @@ -1256,8 +1254,8 @@ static int isicom_set_serial_info(struct isi_port *port, (newinfo.flags & ASYNC_SPD_MASK)); if (!capable(CAP_SYS_ADMIN)) { - if ((newinfo.close_delay != port->close_delay) || - (newinfo.closing_wait != port->closing_wait) || + if ((newinfo.close_delay != port->port.close_delay) || + (newinfo.closing_wait != port->port.closing_wait) || ((newinfo.flags & ~ASYNC_USR_MASK) != (port->port.flags & ~ASYNC_USR_MASK))) { unlock_kernel(); @@ -1266,8 +1264,8 @@ static int isicom_set_serial_info(struct isi_port *port, port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | (newinfo.flags & ASYNC_USR_MASK)); } else { - port->close_delay = newinfo.close_delay; - port->closing_wait = newinfo.closing_wait; + port->port.close_delay = newinfo.close_delay; + port->port.closing_wait = newinfo.closing_wait; port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | (newinfo.flags & ASYNC_FLAGS)); } @@ -1294,8 +1292,8 @@ static int isicom_get_serial_info(struct isi_port *port, out_info.irq = port->card->irq; out_info.flags = port->port.flags; /* out_info.baud_base = ? */ - out_info.close_delay = port->close_delay; - out_info.closing_wait = port->closing_wait; + out_info.close_delay = port->port.close_delay; + out_info.closing_wait = port->port.closing_wait; unlock_kernel(); if (copy_to_user(info, &out_info, sizeof(out_info))) return -EFAULT; @@ -1804,13 +1802,13 @@ static int __init isicom_init(void) isi_card[idx].ports = port; spin_lock_init(&isi_card[idx].card_lock); for (channel = 0; channel < 16; channel++, port++) { + tty_port_init(&port->port); port->magic = ISICOM_MAGIC; port->card = &isi_card[idx]; port->channel = channel; - port->close_delay = 50 * HZ/100; - port->closing_wait = 3000 * HZ/100; + port->port.close_delay = 50 * HZ/100; + port->port.closing_wait = 3000 * HZ/100; port->status = 0; - tty_port_init(&port->port); /* . . . */ } isi_card[idx].base = 0; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index dd10f143d96f..2bba250ffc8e 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -135,7 +135,6 @@ struct moxa_port { void __iomem *tableAddr; int type; - int close_delay; int cflag; unsigned long statusflags; @@ -822,10 +821,9 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) } for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) { + tty_port_init(&p->port); p->type = PORT_16550A; - p->close_delay = 5 * HZ / 10; p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; - tty_port_init(&p->port); } switch (brd->boardType) { @@ -2124,7 +2122,7 @@ static int moxa_get_serial_info(struct moxa_port *info, .line = info->port.tty->index, .flags = info->port.flags, .baud_base = 921600, - .close_delay = info->close_delay + .close_delay = info->port.close_delay }; return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; } @@ -2148,7 +2146,7 @@ static int moxa_set_serial_info(struct moxa_port *info, (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; } else - info->close_delay = new_serial.close_delay * HZ / 100; + info->port.close_delay = new_serial.close_delay * HZ / 100; new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); new_serial.flags |= (info->port.flags & ASYNC_FLAGS); diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index e83ccee03161..6307e301bd26 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -243,10 +243,7 @@ struct mxser_port { unsigned char ldisc_stop_rx; int custom_divisor; - int close_delay; - unsigned short closing_wait; unsigned char err_shadow; - unsigned long event; struct async_icount icount; /* kernel counters for 4 input interrupts */ int timeout; @@ -1199,8 +1196,8 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1231,11 +1228,10 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - info->event = 0; info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); + if (info->port.close_delay) + schedule_timeout_interruptible(info->port.close_delay); wake_up_interruptible(&info->port.open_wait); } @@ -1370,8 +1366,8 @@ static int mxser_get_serial_info(struct mxser_port *info, .irq = info->board->irq, .flags = info->port.flags, .baud_base = info->baud_base, - .close_delay = info->close_delay, - .closing_wait = info->closing_wait, + .close_delay = info->port.close_delay, + .closing_wait = info->port.closing_wait, .custom_divisor = info->custom_divisor, .hub6 = 0 }; @@ -1402,7 +1398,7 @@ static int mxser_set_serial_info(struct mxser_port *info, if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.baud_base != info->baud_base) || - (new_serial.close_delay != info->close_delay) || + (new_serial.close_delay != info->port.close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | @@ -1414,8 +1410,8 @@ static int mxser_set_serial_info(struct mxser_port *info, */ info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; + info->port.close_delay = new_serial.close_delay * HZ / 100; + info->port.closing_wait = new_serial.closing_wait * HZ / 100; info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; info->port.tty->low_latency = 0; @@ -2214,7 +2210,6 @@ static void mxser_hangup(struct tty_struct *tty) mxser_flush_buffer(tty); mxser_shutdown(info); - info->event = 0; info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; @@ -2545,6 +2540,7 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, for (i = 0; i < brd->info->nports; i++) { info = &brd->ports[i]; + tty_port_init(&info->port); info->board = brd; info->stop_rx = 0; info->ldisc_stop_rx = 0; @@ -2559,10 +2555,9 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, process_txrx_fifo(info); info->custom_divisor = info->baud_base * 16; - info->close_delay = 5 * HZ / 10; - info->closing_wait = 30 * HZ; + info->port.close_delay = 5 * HZ / 10; + info->port.closing_wait = 30 * HZ; info->normal_termios = mxvar_sdriver->init_termios; - tty_port_init(&info->port); init_waitqueue_head(&info->delta_msr_wait); memset(&info->mon_data, 0, sizeof(struct mxser_mon)); info->err_shadow = 0; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 3ca8957ba327..724b2b20f4b2 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1032,8 +1032,8 @@ static void rc_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1065,8 +1065,8 @@ static void rc_close(struct tty_struct *tty, struct file *filp) tty->closing = 0; port->port.tty = NULL; if (port->port.blocked_open) { - if (port->close_delay) - msleep_interruptible(jiffies_to_msecs(port->close_delay)); + if (port->port.close_delay) + msleep_interruptible(jiffies_to_msecs(port->port.close_delay)); wake_up_interruptible(&port->port.open_wait); } port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -1295,8 +1295,8 @@ static int rc_set_serial_info(struct riscom_port *port, (tmp.flags & ASYNC_SPD_MASK)); if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->close_delay) || - (tmp.closing_wait != port->closing_wait) || + if ((tmp.close_delay != port->port.close_delay) || + (tmp.closing_wait != port->port.closing_wait) || ((tmp.flags & ~ASYNC_USR_MASK) != (port->port.flags & ~ASYNC_USR_MASK))) return -EPERM; @@ -1305,8 +1305,8 @@ static int rc_set_serial_info(struct riscom_port *port, } else { port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | (tmp.flags & ASYNC_FLAGS)); - port->close_delay = tmp.close_delay; - port->closing_wait = tmp.closing_wait; + port->port.close_delay = tmp.close_delay; + port->port.closing_wait = tmp.closing_wait; } if (change_speed) { unsigned long flags; @@ -1331,8 +1331,8 @@ static int rc_get_serial_info(struct riscom_port *port, tmp.irq = bp->irq; tmp.flags = port->port.flags; tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC; - tmp.close_delay = port->close_delay * HZ/100; - tmp.closing_wait = port->closing_wait * HZ/100; + tmp.close_delay = port->port.close_delay * HZ/100; + tmp.closing_wait = port->port.closing_wait * HZ/100; tmp.xmit_fifo_size = CD180_NFIFO; return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -1549,10 +1549,8 @@ static int __init rc_init_drivers(void) } memset(rc_port, 0, sizeof(rc_port)); for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { - rc_port[i].magic = RISCOM8_MAGIC; - rc_port[i].close_delay = 50 * HZ / 100; - rc_port[i].closing_wait = 3000 * HZ / 100; tty_port_init(&rc_port[i].port); + rc_port[i].magic = RISCOM8_MAGIC; } return 0; } diff --git a/drivers/char/riscom8.h b/drivers/char/riscom8.h index 29ddbab78801..c9876b3f9714 100644 --- a/drivers/char/riscom8.h +++ b/drivers/char/riscom8.h @@ -69,14 +69,12 @@ struct riscom_port { struct tty_port port; int baud_base; int timeout; - int close_delay; int custom_divisor; int xmit_head; int xmit_tail; int xmit_cnt; short wakeup_chars; short break_length; - unsigned short closing_wait; unsigned char mark_mask; unsigned char IER; unsigned char MSVR; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index bc35b900f9c6..e670eae2f510 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,7 @@ #include #include #include -#include +#include #include #include #include @@ -648,8 +649,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) info->board = board; info->aiop = aiop; info->chan = chan; - info->closing_wait = 3000; - info->close_delay = 50; + info->port.closing_wait = 3000; + info->port.close_delay = 50; init_waitqueue_head(&info->port.open_wait); init_completion(&info->close_wait); info->flags &= ~ROCKET_MODE_MASK; @@ -1137,8 +1138,8 @@ static void rp_close(struct tty_struct *tty, struct file *filp) /* * Wait for the transmit buffer to clear */ - if (info->closing_wait != ROCKET_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ROCKET_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -1168,8 +1169,8 @@ static void rp_close(struct tty_struct *tty, struct file *filp) clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } else { @@ -1327,8 +1328,8 @@ static int get_config(struct r_port *info, struct rocket_config __user *retinfo) memset(&tmp, 0, sizeof (tmp)); tmp.line = info->line; tmp.flags = info->flags; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) @@ -1353,8 +1354,8 @@ static int set_config(struct r_port *info, struct rocket_config __user *new_info } info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) info->port.tty->alt_speed = 57600; diff --git a/drivers/char/rocket.h b/drivers/char/rocket.h index ae6b04f90c03..a8b09195ebba 100644 --- a/drivers/char/rocket.h +++ b/drivers/char/rocket.h @@ -64,8 +64,8 @@ struct rocket_version { /* * For closing_wait and closing_wait2 */ -#define ROCKET_CLOSING_WAIT_NONE 65535 -#define ROCKET_CLOSING_WAIT_INF 0 +#define ROCKET_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE +#define ROCKET_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF /* * Rocketport ioctls -- "RP" diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 3affc48f6a57..21f3ff53ba32 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -1133,8 +1133,6 @@ struct r_port { unsigned int chan:3; CONTROLLER_t *ctlp; CHANNEL_t channel; - int closing_wait; - int close_delay; int intmask; int xmit_fifo_room; /* room in xmit fifo */ unsigned char *xmit_buf; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 2ee4d9893757..037dc47e4cb1 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -608,9 +608,9 @@ static inline struct specialix_port * sx_get_port(struct specialix_board * bp, dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel); if (channel < CD186x_NCH) { port = &sx_port[board_No(bp) * SX_NPORT + channel]; - dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED); + dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",board_No(bp) * SX_NPORT + channel, port, port->port.flags & ASYNC_INITIALIZED); - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port); func_exit(); return port; @@ -637,7 +637,7 @@ static inline void sx_receive_exc(struct specialix_board * bp) func_exit(); return; } - tty = port->tty; + tty = port->port.tty; status = sx_in(bp, CD186x_RCSR); @@ -673,7 +673,7 @@ static inline void sx_receive_exc(struct specialix_board * bp) dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n", board_No(bp), port_No(port)); flag = TTY_BREAK; - if (port->flags & ASYNC_SAK) + if (port->port.flags & ASYNC_SAK) do_SAK(tty); } else if (status & RCSR_PE) @@ -707,7 +707,7 @@ static inline void sx_receive(struct specialix_board * bp) func_exit(); return; } - tty = port->tty; + tty = port->port.tty; count = sx_in(bp, CD186x_RDCR); dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count); @@ -734,7 +734,7 @@ static inline void sx_transmit(struct specialix_board * bp) return; } dprintk (SX_DEBUG_TX, "port: %p\n", port); - tty = port->tty; + tty = port->port.tty; if (port->IER & IER_TXEMPTY) { /* FIFO drained */ @@ -811,7 +811,7 @@ static inline void sx_check_modem(struct specialix_board * bp) if (!(port = sx_get_port(bp, "Modem"))) return; - tty = port->tty; + tty = port->port.tty; mcr = sx_in(bp, CD186x_MCR); printk ("mcr = %02x.\n", mcr); @@ -821,7 +821,7 @@ static inline void sx_check_modem(struct specialix_board * bp) msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD; if (msvr_cd) { dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); } else { dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n"); tty_hangup(tty); @@ -1030,7 +1030,7 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p func_enter(); - if (!(tty = port->tty) || !tty->termios) { + if (!(tty = port->port.tty) || !tty->termios) { func_exit(); return; } @@ -1052,9 +1052,9 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p baud = tty_get_baud_rate(tty); if (baud == 38400) { - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud = 57600; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud = 115200; } @@ -1244,7 +1244,7 @@ static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port func_enter(); - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { func_exit(); return 0; } @@ -1268,12 +1268,12 @@ static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port spin_lock_irqsave(&port->lock, flags); - if (port->tty) - clear_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->port.tty) + clear_bit(TTY_IO_ERROR, &port->port.tty->flags); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; sx_change_speed(bp, port); - port->flags |= ASYNC_INITIALIZED; + port->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&port->lock, flags); @@ -1292,7 +1292,7 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * func_enter(); - if (!(port->flags & ASYNC_INITIALIZED)) { + if (!(port->port.flags & ASYNC_INITIALIZED)) { func_exit(); return; } @@ -1315,7 +1315,7 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); - if (!(tty = port->tty) || C_HUPCL(tty)) { + if (!(tty = port->port.tty) || C_HUPCL(tty)) { /* Drop DTR */ sx_out(bp, CD186x_MSVDTR, 0); } @@ -1330,7 +1330,7 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * spin_unlock_irqrestore(&bp->lock, flags); if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - port->flags &= ~ASYNC_INITIALIZED; + port->port.flags &= ~ASYNC_INITIALIZED; if (!bp->count) sx_shutdown_board(bp); @@ -1354,9 +1354,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) { + if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->port.close_wait); + if (port->port.flags & ASYNC_HUP_NOTIFY) { func_exit(); return -EAGAIN; } else { @@ -1371,7 +1371,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; func_exit(); return 0; } @@ -1387,13 +1387,13 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->open_wait, &wait); + add_wait_queue(&port->port.open_wait, &wait); spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { - port->count--; + port->port.count--; } spin_unlock_irqrestore(&port->lock, flags); - port->blocked_open++; + port->port.blocked_open++; while (1) { spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); @@ -1410,14 +1410,14 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, spin_unlock_irqrestore(&bp->lock, flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) + !(port->port.flags & ASYNC_INITIALIZED)) { + if (port->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->flags & ASYNC_CLOSING) && + if (!(port->port.flags & ASYNC_CLOSING) && (do_clocal || CD)) break; if (signal_pending(current)) { @@ -1428,19 +1428,19 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); + remove_wait_queue(&port->port.open_wait, &wait); spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { - port->count++; + port->port.count++; } - port->blocked_open--; + port->port.blocked_open--; spin_unlock_irqrestore(&port->lock, flags); if (retval) { func_exit(); return retval; } - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; func_exit(); return 0; } @@ -1484,10 +1484,10 @@ static int sx_open(struct tty_struct * tty, struct file * filp) } spin_lock_irqsave(&bp->lock, flags); - port->count++; + port->port.count++; bp->count++; tty->driver_data = port; - port->tty = tty; + port->port.tty = tty; spin_unlock_irqrestore(&bp->lock, flags); if ((error = sx_setup_port(bp, port))) { @@ -1547,15 +1547,15 @@ static void sx_close(struct tty_struct * tty, struct file * filp) } bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { + if ((tty->count == 1) && (port->port.count != 1)) { printk(KERN_ERR "sx%d: sx_close: bad port count;" " tty->count is 1, port count is %d\n", - board_No(bp), port->count); - port->count = 1; + board_No(bp), port->port.count); + port->port.count = 1; } - if (port->count > 1) { - port->count--; + if (port->port.count > 1) { + port->port.count--; bp->count--; spin_unlock_irqrestore(&port->lock, flags); @@ -1563,7 +1563,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) func_exit(); return; } - port->flags |= ASYNC_CLOSING; + port->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. @@ -1571,8 +1571,8 @@ static void sx_close(struct tty_struct * tty, struct file * filp) tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); dprintk (SX_DEBUG_OPEN, "Closing\n"); - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { - tty_wait_until_sent(tty, port->closing_wait); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { + tty_wait_until_sent(tty, port->port.closing_wait); } /* * At this point we stop accepting input. To do this, we @@ -1582,7 +1582,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) */ dprintk (SX_DEBUG_OPEN, "Closed\n"); port->IER &= ~IER_RXD; - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { port->IER &= ~IER_TXRDY; port->IER |= IER_TXEMPTY; spin_lock_irqsave(&bp->lock, flags); @@ -1611,10 +1611,10 @@ static void sx_close(struct tty_struct * tty, struct file * filp) board_No(bp), bp->count, tty->index); bp->count = 0; } - if (--port->count < 0) { + if (--port->port.count < 0) { printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", - board_No(bp), port_No(port), port->count); - port->count = 0; + board_No(bp), port_No(port), port->port.count); + port->port.count = 0; } sx_shutdown_port(bp, port); @@ -1622,16 +1622,16 @@ static void sx_close(struct tty_struct * tty, struct file * filp) tty_ldisc_flush(tty); spin_lock_irqsave(&port->lock, flags); tty->closing = 0; - port->tty = NULL; + port->port.tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - if (port->blocked_open) { - if (port->close_delay) { - msleep_interruptible(jiffies_to_msecs(port->close_delay)); + if (port->port.blocked_open) { + if (port->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(port->port.close_delay)); } - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&port->port.close_wait); func_exit(); } @@ -1815,7 +1815,7 @@ static int sx_tiocmget(struct tty_struct *tty, struct file *file) dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n", port_No(port), status, sx_in (bp, CD186x_CAR)); dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port); - if (SX_CRTSCTS(port->tty)) { + if (SX_CRTSCTS(port->port.tty)) { result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ | ((status & MSVR_DTR) ? TIOCM_RTS : 0) | ((status & MSVR_CD) ? TIOCM_CAR : 0) @@ -1857,7 +1857,7 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, /* if (set & TIOCM_DTR) port->MSVR |= MSVR_DTR; */ - if (SX_CRTSCTS(port->tty)) { + if (SX_CRTSCTS(port->port.tty)) { if (set & TIOCM_RTS) port->MSVR |= MSVR_DTR; } else { @@ -1869,7 +1869,7 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, port->MSVR &= ~MSVR_RTS; */ /* if (clear & TIOCM_DTR) port->MSVR &= ~MSVR_DTR; */ - if (SX_CRTSCTS(port->tty)) { + if (SX_CRTSCTS(port->port.tty)) { if (clear & TIOCM_RTS) port->MSVR &= ~MSVR_DTR; } else { @@ -1929,27 +1929,27 @@ static inline int sx_set_serial_info(struct specialix_port * port, lock_kernel(); - change_speed = ((port->flags & ASYNC_SPD_MASK) != + change_speed = ((port->port.flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); change_speed |= (tmp.custom_divisor != port->custom_divisor); if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->close_delay) || - (tmp.closing_wait != port->closing_wait) || + if ((tmp.close_delay != port->port.close_delay) || + (tmp.closing_wait != port->port.closing_wait) || ((tmp.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) { + (port->port.flags & ~ASYNC_USR_MASK))) { func_exit(); unlock_kernel(); return -EPERM; } - port->flags = ((port->flags & ~ASYNC_USR_MASK) | + port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | (tmp.flags & ASYNC_USR_MASK)); port->custom_divisor = tmp.custom_divisor; } else { - port->flags = ((port->flags & ~ASYNC_FLAGS) | + port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | (tmp.flags & ASYNC_FLAGS)); - port->close_delay = tmp.close_delay; - port->closing_wait = tmp.closing_wait; + port->port.close_delay = tmp.close_delay; + port->port.closing_wait = tmp.closing_wait; port->custom_divisor = tmp.custom_divisor; } if (change_speed) { @@ -1975,10 +1975,10 @@ static inline int sx_get_serial_info(struct specialix_port * port, tmp.line = port - sx_port; tmp.port = bp->base; tmp.irq = bp->irq; - tmp.flags = port->flags; + tmp.flags = port->port.flags; tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; - tmp.close_delay = port->close_delay * HZ/100; - tmp.closing_wait = port->closing_wait * HZ/100; + tmp.close_delay = port->port.close_delay * HZ/100; + tmp.closing_wait = port->port.closing_wait * HZ/100; tmp.custom_divisor = port->custom_divisor; tmp.xmit_fifo_size = CD186x_NFIFO; unlock_kernel(); @@ -2199,17 +2199,17 @@ static void sx_hangup(struct tty_struct * tty) sx_shutdown_port(bp, port); spin_lock_irqsave(&port->lock, flags); - bp->count -= port->count; + bp->count -= port->port.count; if (bp->count < 0) { printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n", board_No(bp), bp->count, tty->index); bp->count = 0; } - port->count = 0; - port->flags &= ~ASYNC_NORMAL_ACTIVE; - port->tty = NULL; + port->port.count = 0; + port->port.flags &= ~ASYNC_NORMAL_ACTIVE; + port->port.tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); func_exit(); } @@ -2224,10 +2224,6 @@ static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termio if (sx_paranoia_check(port, tty->name, "sx_set_termios")) return; - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; - bp = port_Board(port); spin_lock_irqsave(&port->lock, flags); sx_change_speed(port_Board(port), port); @@ -2297,10 +2293,7 @@ static int sx_init_drivers(void) memset(sx_port, 0, sizeof(sx_port)); for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { sx_port[i].magic = SPECIALIX_MAGIC; - sx_port[i].close_delay = 50 * HZ/100; - sx_port[i].closing_wait = 3000 * HZ/100; - init_waitqueue_head(&sx_port[i].open_wait); - init_waitqueue_head(&sx_port[i].close_wait); + tty_port_init(&sx_port[i].port); spin_lock_init(&sx_port[i].lock); } diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h index 3f2f85bdf516..c63005274d9b 100644 --- a/drivers/char/specialix_io8.h +++ b/drivers/char/specialix_io8.h @@ -107,23 +107,17 @@ struct specialix_board { struct specialix_port { int magic; + struct tty_port port; int baud_base; int flags; - struct tty_struct * tty; - int count; - int blocked_open; int timeout; - int close_delay; unsigned char * xmit_buf; int custom_divisor; int xmit_head; int xmit_tail; int xmit_cnt; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; short wakeup_chars; short break_length; - unsigned short closing_wait; unsigned char mark_mask; unsigned char IER; unsigned char MSVR; diff --git a/drivers/char/sx.c b/drivers/char/sx.c index b1239ee48b78..d5cffcd6a572 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -2395,6 +2395,7 @@ static int sx_init_portstructs(int nboards, int nports) board->ports = port; for (j = 0; j < boards[i].nports; j++) { sx_dprintk(SX_DEBUG_INIT, "initing port %d\n", j); + tty_port_init(&port->gs.port); port->gs.magic = SX_MAGIC; port->gs.close_delay = HZ / 2; port->gs.closing_wait = 30 * HZ; @@ -2407,7 +2408,6 @@ static int sx_init_portstructs(int nboards, int nports) /* * Initializing wait queue */ - tty_port_init(&port->gs.port); port++; } } diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 734098f7dfa2..9f14753fada1 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -183,8 +183,6 @@ struct mgsl_struct { struct tty_port port; int line; int hw_version; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ struct mgsl_icount icount; @@ -3142,11 +3140,11 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) /* wait for transmit data to clear all layers */ - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n", __FILE__,__LINE__, info->device_name ); - tty_wait_until_sent(tty, info->closing_wait); + tty_wait_until_sent(tty, info->port.closing_wait); } if (info->port.flags & ASYNC_INITIALIZED) @@ -3162,8 +3160,8 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } @@ -4326,12 +4324,12 @@ static struct mgsl_struct* mgsl_allocate_device(void) if (!info) { printk("Error can't allocate device instance data\n"); } else { + tty_port_init(&info->port); info->magic = MGSL_MAGIC; INIT_WORK(&info->task, mgsl_bh_handler); info->max_frame_size = 4096; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - tty_port_init(&info->port); + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->irq_spinlock); diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index fc71d9819165..07aa42a7f397 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -261,8 +261,6 @@ struct slgt_info { struct slgt_info *port_array[SLGT_MAX_PORTS]; int line; /* tty line instance number */ - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ struct mgsl_icount icount; @@ -758,9 +756,9 @@ static void close(struct tty_struct *tty, struct file *filp) /* wait for transmit data to clear all layers */ - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { DBGINFO(("%s call tty_wait_until_sent\n", info->device_name)); - tty_wait_until_sent(tty, info->closing_wait); + tty_wait_until_sent(tty, info->port.closing_wait); } if (info->port.flags & ASYNC_INITIALIZED) @@ -774,8 +772,8 @@ static void close(struct tty_struct *tty, struct file *filp) info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } @@ -3448,13 +3446,13 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev DBGERR(("%s device alloc failed adapter=%d port=%d\n", driver_name, adapter_num, port_num)); } else { + tty_port_init(&info->port); info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; info->raw_rx_size = DMABUFSIZE; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - tty_port_init(&info->port); + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->netlock); diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5b5b292d046b..c4bc09018368 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -846,11 +846,11 @@ static void close(struct tty_struct *tty, struct file *filp) /* wait for transmit data to clear all layers */ - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s close() calling tty_wait_until_sent\n", __FILE__,__LINE__, info->device_name ); - tty_wait_until_sent(tty, info->closing_wait); + tty_wait_until_sent(tty, info->port.closing_wait); } if (info->port.flags & ASYNC_INITIALIZED) @@ -866,8 +866,8 @@ static void close(struct tty_struct *tty, struct file *filp) info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } @@ -3802,12 +3802,12 @@ static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n", __FILE__,__LINE__, adapter_num, port_num); } else { + tty_port_init(&info->port); info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - tty_port_init(&info->port); + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->netlock); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a8cc416a23c0..82f6a8c86332 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2094,6 +2094,8 @@ void tty_port_init(struct tty_port *port) init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); mutex_init(&port->mutex); + port->close_delay = (50 * HZ) / 100; + port->closing_wait = (3000 * HZ) / 100; } EXPORT_SYMBOL(tty_port_init); diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index a982b74a6ee6..2d3d1e04ba92 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -567,8 +567,6 @@ struct cyclades_port { int chip_rev; int custom_divisor; u8 x_char; /* to be pushed out ASAP */ - int close_delay; - unsigned short closing_wait; int breakon; int breakoff; int xmit_head; @@ -586,8 +584,8 @@ struct cyclades_port { }; #define CLOSING_WAIT_DELAY 30*HZ -#define CY_CLOSING_WAIT_NONE 65535 -#define CY_CLOSING_WAIT_INF 0 +#define CY_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE +#define CY_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF #define CyMAX_CHIPS_PER_CARD 8 diff --git a/include/linux/tty.h b/include/linux/tty.h index 46008e86b191..4e5833073aa6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -188,6 +188,8 @@ struct tty_port { unsigned long flags; /* TTY flags ASY_*/ struct mutex mutex; /* Locking */ unsigned char *xmit_buf; /* Optional buffer */ + int close_delay; /* Close port delay */ + int closing_wait; /* Delay for output */ }; /* -- cgit v1.2.3 From f233ea5c9e0d8b95e4283bf6a3436b88f6fd3586 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 21 Jul 2008 17:05:22 +1000 Subject: md: Make mddev->array_size sector-based. This patch renames the array_size field of struct mddev_s to array_sectors and converts all instances to use units of 512 byte sectors instead of 1k blocks. Signed-off-by: Andre Noll Signed-off-by: NeilBrown --- drivers/md/faulty.c | 2 +- drivers/md/linear.c | 6 +++--- drivers/md/md.c | 12 +++++++----- drivers/md/multipath.c | 2 +- drivers/md/raid0.c | 8 ++++---- drivers/md/raid1.c | 11 ++++++----- drivers/md/raid10.c | 2 +- drivers/md/raid5.c | 16 +++++++++------- include/linux/raid/md_k.h | 2 +- 9 files changed, 33 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index d107ddceefcd..268547dbfbd3 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -297,7 +297,7 @@ static int run(mddev_t *mddev) rdev_for_each(rdev, tmp, mddev) conf->rdev = rdev; - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->private = conf; reconfig(mddev, mddev->layout, -1); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index ec921f58fbb8..57644a780f16 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -256,7 +256,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_size = conf->array_size; + mddev->array_sectors = conf->array_size * 2; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -290,8 +290,8 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_size = newconf->array_size; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = newconf->array_size * 2; + set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index df13a17a9627..4bfbc1982cda 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3704,7 +3704,7 @@ static int do_md_run(mddev_t * mddev) if (mddev->flags) md_update_sb(mddev, 0); - set_capacity(disk, mddev->array_size<<1); + set_capacity(disk, mddev->array_sectors); /* If we call blk_queue_make_request here, it will * re-initialise max_sectors etc which may have been @@ -3905,7 +3905,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) export_array(mddev); - mddev->array_size = 0; + mddev->array_sectors = 0; mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; @@ -4644,7 +4644,8 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) bdev = bdget_disk(mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } @@ -5391,10 +5392,11 @@ static int md_seq_show(struct seq_file *seq, void *v) if (!list_empty(&mddev->disks)) { if (mddev->pers) seq_printf(seq, "\n %llu blocks", - (unsigned long long)mddev->array_size); + (unsigned long long) + mddev->array_sectors / 2); else seq_printf(seq, "\n %llu blocks", - (unsigned long long)size); + (unsigned long long)size); } if (mddev->persistent) { if (mddev->major_version != 0 || diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 541cbe3414bd..c4779ccba1c3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -504,7 +504,7 @@ static int multipath_run (mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = multipath_unplug; mddev->queue->backing_dev_info.congested_fn = multipath_congested; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 914c04ddec7c..2f30ebd8b7ab 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -293,16 +293,16 @@ static int raid0_run (mddev_t *mddev) goto out_free_conf; /* calculate array device size */ - mddev->array_size = 0; + mddev->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) - mddev->array_size += rdev->size; + mddev->array_sectors += rdev->size * 2; printk("raid0 : md_size is %llu blocks.\n", - (unsigned long long)mddev->array_size); + (unsigned long long)mddev->array_sectors / 2); printk("raid0 : conf->hash_spacing is %llu blocks.\n", (unsigned long long)conf->hash_spacing); { - sector_t s = mddev->array_size; + sector_t s = mddev->array_sectors / 2; sector_t space = conf->hash_spacing; int round; conf->preshift = 0; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 491dc2d4ad5f..03a5ab705c20 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2043,7 +2043,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = raid1_unplug; mddev->queue->backing_dev_info.congested_fn = raid1_congested; @@ -2105,14 +2105,15 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) * any io in the removed space completes, but it hardly seems * worth it. */ - mddev->array_size = sectors>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors; + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; - if (mddev->array_size > mddev->size && mddev->recovery_cp == MaxSector) { + if (mddev->array_sectors / 2 > mddev->size && + mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); } - mddev->size = mddev->array_size; + mddev->size = mddev->array_sectors / 2; mddev->resync_max_sectors = sectors; return 0; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index df08a9fa3a1f..2acea4025243 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2164,7 +2164,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = size << (conf->chunk_shift-1); + mddev->array_sectors = size << conf->chunk_shift; mddev->resync_max_sectors = size << conf->chunk_shift; mddev->queue->unplug_fn = raid10_unplug; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8f4c70a53210..42a480ba767b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3540,7 +3540,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped j == raid6_next_disk(sh->pd_idx, sh->disks)) continue; s = compute_blocknr(sh, j); - if (s < (mddev->array_size<<1)) { + if (s < mddev->array_sectors) { skipped = 1; continue; } @@ -4189,7 +4189,7 @@ static int run(mddev_t *mddev) mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = raid5_congested; - mddev->array_size = mddev->size * (conf->previous_raid_disks - + mddev->array_sectors = 2 * mddev->size * (conf->previous_raid_disks - conf->max_degraded); blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec); @@ -4413,8 +4413,9 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) raid5_conf_t *conf = mddev_to_conf(mddev); sectors &= ~((sector_t)mddev->chunk_size/512 - 1); - mddev->array_size = (sectors * (mddev->raid_disks-conf->max_degraded))>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors * (mddev->raid_disks + - conf->max_degraded); + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; @@ -4547,15 +4548,16 @@ static void end_reshape(raid5_conf_t *conf) struct block_device *bdev; if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { - conf->mddev->array_size = conf->mddev->size * + conf->mddev->array_sectors = 2 * conf->mddev->size * (conf->raid_disks - conf->max_degraded); - set_capacity(conf->mddev->gendisk, conf->mddev->array_size << 1); + set_capacity(conf->mddev->gendisk, conf->mddev->array_sectors); conf->mddev->changed = 1; bdev = bdget_disk(conf->mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)conf->mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)conf->mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index e37aaa41efc6..6f72b47ae41c 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -150,7 +150,7 @@ struct mddev_s int raid_disks; int max_disks; sector_t size; /* used size of component devices */ - sector_t array_size; /* exported array size */ + sector_t array_sectors; /* exported array size */ __u64 events; char uuid[16]; -- cgit v1.2.3 From d6e2215052810678bc9782fd980b52706fc71f50 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: linear: Make array_size sector-based and rename it to array_sectors. Signed-off-by: Andre Noll Signed-off-by: NeilBrown --- drivers/md/linear.c | 16 ++++++++-------- include/linux/raid/linear.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 57644a780f16..1cafaa959443 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -120,7 +120,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) return NULL; cnt = 0; - conf->array_size = 0; + conf->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) { int j = rdev->raid_disk; @@ -144,7 +144,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); disk->size = rdev->size; - conf->array_size += rdev->size; + conf->array_sectors += rdev->size * 2; cnt++; } @@ -153,7 +153,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) goto out; } - min_spacing = conf->array_size; + min_spacing = conf->array_sectors / 2; sector_div(min_spacing, PAGE_SIZE/sizeof(struct dev_info *)); /* min_spacing is the minimum spacing that will fit the hash @@ -162,7 +162,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) * that is larger than min_spacing as use the size of that as * the actual spacing */ - conf->hash_spacing = conf->array_size; + conf->hash_spacing = conf->array_sectors / 2; for (i=0; i < cnt-1 ; i++) { sector_t sz = 0; int j; @@ -192,7 +192,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) unsigned round; unsigned long base; - sz = conf->array_size >> conf->preshift; + sz = conf->array_sectors >> (conf->preshift + 1); sz += 1; /* force round-up */ base = conf->hash_spacing >> conf->preshift; round = sector_div(sz, base); @@ -219,7 +219,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) curr_offset = 0; i = 0; for (curr_offset = 0; - curr_offset < conf->array_size; + curr_offset < conf->array_sectors / 2; curr_offset += conf->hash_spacing) { while (i < raid_disks-1 && @@ -256,7 +256,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_sectors = conf->array_size * 2; + mddev->array_sectors = conf->array_sectors; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -290,7 +290,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_sectors = newconf->array_size * 2; + mddev->array_sectors = newconf->array_sectors; set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/include/linux/raid/linear.h b/include/linux/raid/linear.h index ba15469daf11..7e375111d007 100644 --- a/include/linux/raid/linear.h +++ b/include/linux/raid/linear.h @@ -16,7 +16,7 @@ struct linear_private_data struct linear_private_data *prev; /* earlier version */ dev_info_t **hash_table; sector_t hash_spacing; - sector_t array_size; + sector_t array_sectors; int preshift; /* shift before dividing by hash_spacing */ dev_info_t disks[0]; }; -- cgit v1.2.3 From f2ea68cf42aafdd93393b6b8b20fc3c2b5f4390c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: only count actual openers as access which prevent a 'stop' Open isn't the only thing that increments ->active. e.g. reading /proc/mdstat will increment it briefly. So to avoid false positives in testing for concurrent access, introduce a new counter that counts just the number of times the md device it open. Signed-off-by: NeilBrown --- drivers/md/md.c | 9 ++++++--- include/linux/raid/md_k.h | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 4bfbc1982cda..450f30b6edf9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -273,6 +273,7 @@ static mddev_t * mddev_find(dev_t unit) INIT_LIST_HEAD(&new->all_mddevs); init_timer(&new->safemode_timer); atomic_set(&new->active, 1); + atomic_set(&new->openers, 0); spin_lock_init(&new->write_lock); init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); @@ -2695,14 +2696,14 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) break; case clear: /* stopping an active array */ - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; err = do_md_stop(mddev, 0, 0); break; case inactive: /* stopping an active array */ if (mddev->pers) { - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; err = do_md_stop(mddev, 2, 0); } else @@ -3816,7 +3817,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) int err = 0; struct gendisk *disk = mddev->gendisk; - if (atomic_read(&mddev->active) > 1 + is_open) { + if (atomic_read(&mddev->openers) > is_open) { printk("md: %s still in use.\n",mdname(mddev)); return -EBUSY; } @@ -5014,6 +5015,7 @@ static int md_open(struct inode *inode, struct file *file) err = 0; mddev_get(mddev); + atomic_inc(&mddev->openers); mddev_unlock(mddev); check_disk_change(inode->i_bdev); @@ -5026,6 +5028,7 @@ static int md_release(struct inode *inode, struct file * file) mddev_t *mddev = inode->i_bdev->bd_disk->private_data; BUG_ON(!mddev); + atomic_dec(&mddev->openers); mddev_put(mddev); return 0; diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 6f72b47ae41c..4bef4791d80d 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -215,7 +215,8 @@ struct mddev_s int in_sync; /* know to not need resync */ struct mutex reconfig_mutex; - atomic_t active; + atomic_t active; /* general refcount */ + atomic_t openers; /* number of active opens */ int changed; /* true if we might need to reread partition info */ int degraded; /* whether md should consider -- cgit v1.2.3 From 4b80991c6cb9efa607bc4fd6f3ecdf5511c31bb0 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: Protect access to mddev->disks list using RCU All modifications and most access to the mddev->disks list are made under the reconfig_mutex lock. However there are three places where the list is walked without any locking. If a reconfig happens at this time, havoc (and oops) can ensue. So use RCU to protect these accesses: - wrap them in rcu_read_{,un}lock() - use list_for_each_entry_rcu - add to the list with list_add_rcu - delete from the list with list_del_rcu - delay the 'free' with call_rcu rather than schedule_work Note that export_rdev did a list_del_init on this list. In almost all cases the entry was not in the list anymore so it was a no-op and so safe. It is no longer safe as after list_del_rcu we may not touch the list_head. An audit shows that export_rdev is called: - after unbind_rdev_from_array, in which case the delete has already been done, - after bind_rdev_to_array fails, in which case the delete isn't needed. - before the device has been put on a list at all (e.g. in add_new_disk where reading the superblock fails). - and in autorun devices after a failure when the device is on a different list. So remove the list_del_init call from export_rdev, and add it back immediately before the called to export_rdev for that last case. Note also that ->same_set is sometimes used for lists other than mddev->list (e.g. candidates). In these cases rcu is not needed. Signed-off-by: NeilBrown --- drivers/md/bitmap.c | 15 ++++++++++----- drivers/md/md.c | 30 ++++++++++++++++++------------ include/linux/raid/md_k.h | 3 +++ 3 files changed, 31 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index eba83e25b678..621a272a2c74 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -241,10 +241,10 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) { mdk_rdev_t *rdev; - struct list_head *tmp; mddev_t *mddev = bitmap->mddev; - rdev_for_each(rdev, tmp, mddev) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) if (test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) { int size = PAGE_SIZE; @@ -260,11 +260,11 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + (long)(page->index * (PAGE_SIZE/512)) + size/512 > 0) /* bitmap runs in to metadata */ - return -EINVAL; + goto bad_alignment; if (rdev->data_offset + mddev->size*2 > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ - return -EINVAL; + goto bad_alignment; } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ if (rdev->sb_start @@ -272,7 +272,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) /* bitmap runs in to data */ - return -EINVAL; + goto bad_alignment; } else { /* DATA METADATA BITMAP - no problems */ } @@ -282,10 +282,15 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) size, page); } + rcu_read_unlock(); if (wait) md_super_wait(mddev); return 0; + + bad_alignment: + rcu_read_unlock(); + return -EINVAL; } static void bitmap_file_kick(struct bitmap *bitmap); diff --git a/drivers/md/md.c b/drivers/md/md.c index 450f30b6edf9..c2ff77ccec50 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1395,15 +1395,17 @@ static struct super_type super_types[] = { static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) { - struct list_head *tmp, *tmp2; mdk_rdev_t *rdev, *rdev2; - rdev_for_each(rdev, tmp, mddev1) - rdev_for_each(rdev2, tmp2, mddev2) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev1) + rdev_for_each_rcu(rdev2, mddev2) if (rdev->bdev->bd_contains == - rdev2->bdev->bd_contains) + rdev2->bdev->bd_contains) { + rcu_read_unlock(); return 1; - + } + rcu_read_unlock(); return 0; } @@ -1470,7 +1472,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) kobject_del(&rdev->kobj); goto fail; } - list_add(&rdev->same_set, &mddev->disks); + list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); return 0; @@ -1495,14 +1497,16 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) return; } bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk); - list_del_init(&rdev->same_set); + list_del_rcu(&rdev->same_set); printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); rdev->mddev = NULL; sysfs_remove_link(&rdev->kobj, "block"); /* We need to delay this, otherwise we can deadlock when - * writing to 'remove' to "dev/state" + * writing to 'remove' to "dev/state". We also need + * to delay it due to rcu usage. */ + synchronize_rcu(); INIT_WORK(&rdev->del_work, md_delayed_delete); kobject_get(&rdev->kobj); schedule_work(&rdev->del_work); @@ -1558,7 +1562,6 @@ static void export_rdev(mdk_rdev_t * rdev) if (rdev->mddev) MD_BUG(); free_disk_sb(rdev); - list_del_init(&rdev->same_set); #ifndef MODULE if (test_bit(AutoDetected, &rdev->flags)) md_autodetect_dev(rdev->bdev->bd_dev); @@ -4062,8 +4065,10 @@ static void autorun_devices(int part) /* on success, candidates will be empty, on error * it won't... */ - rdev_for_each_list(rdev, tmp, candidates) + rdev_for_each_list(rdev, tmp, candidates) { + list_del_init(&rdev->same_set); export_rdev(rdev); + } mddev_put(mddev); } printk(KERN_INFO "md: ... autorun DONE.\n"); @@ -5529,12 +5534,12 @@ int unregister_md_personality(struct mdk_personality *p) static int is_mddev_idle(mddev_t *mddev) { mdk_rdev_t * rdev; - struct list_head *tmp; int idle; long curr_events; idle = 1; - rdev_for_each(rdev, tmp, mddev) { + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) { struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; curr_events = disk_stat_read(disk, sectors[0]) + disk_stat_read(disk, sectors[1]) - @@ -5566,6 +5571,7 @@ static int is_mddev_idle(mddev_t *mddev) idle = 0; } } + rcu_read_unlock(); return idle; } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 4bef4791d80d..9f2549ac0e2d 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -339,6 +339,9 @@ static inline char * mdname (mddev_t * mddev) #define rdev_for_each(rdev, tmp, mddev) \ rdev_for_each_list(rdev, tmp, (mddev)->disks) +#define rdev_for_each_rcu(rdev, mddev) \ + list_for_each_entry_rcu(rdev, &((mddev)->disks), same_set) + typedef struct mdk_thread_s { void (*run) (mddev_t *mddev); mddev_t *mddev; -- cgit v1.2.3 From f6fccb1213ba3d661baeb2a5eee0a9701dc03e1b Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Mon, 21 Jul 2008 12:00:37 +0100 Subject: dm: introduce merge_bvec_fn Introduce a bvec merge function for device mapper devices for dynamic size restrictions. This code ensures the requested biovec lies within a single target and then calls a target-specific function to check against any constraints imposed by underlying devices. Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon --- drivers/md/dm.c | 44 +++++++++++++++++++++++++++++++++++++++++++ include/linux/device-mapper.h | 6 ++++++ include/linux/dm-ioctl.h | 4 ++-- 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/dm.c b/drivers/md/dm.c index efe969074928..bca448e11878 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -829,6 +829,49 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) * CRUD END *---------------------------------------------------------------*/ +static int dm_merge_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) +{ + struct mapped_device *md = q->queuedata; + struct dm_table *map = dm_get_table(md); + struct dm_target *ti; + sector_t max_sectors; + int max_size; + + if (unlikely(!map)) + return 0; + + ti = dm_table_find_target(map, bvm->bi_sector); + + /* + * Find maximum amount of I/O that won't need splitting + */ + max_sectors = min(max_io_len(md, bvm->bi_sector, ti), + (sector_t) BIO_MAX_SECTORS); + max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size; + if (max_size < 0) + max_size = 0; + + /* + * merge_bvec_fn() returns number of bytes + * it can accept at this offset + * max is precomputed maximal io size + */ + if (max_size && ti->type->merge) + max_size = ti->type->merge(ti, bvm, biovec, max_size); + + /* + * Always allow an entire first page + */ + if (max_size <= biovec->bv_len && !(bvm->bi_size >> SECTOR_SHIFT)) + max_size = biovec->bv_len; + + dm_table_put(map); + + return max_size; +} + /* * The request function that just remaps the bio built up by * dm_merge_bvec. @@ -1032,6 +1075,7 @@ static struct mapped_device *alloc_dev(int minor) blk_queue_make_request(md->queue, dm_request); blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); md->queue->unplug_fn = dm_unplug_all; + blk_queue_merge_bvec(md->queue, dm_merge_bvec); md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); if (!md->io_pool) diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 0d8d419d191a..a90222e3297d 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -9,11 +9,13 @@ #define _LINUX_DEVICE_MAPPER_H #include +#include struct dm_target; struct dm_table; struct dm_dev; struct mapped_device; +struct bio_vec; typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; @@ -72,6 +74,9 @@ typedef int (*dm_ioctl_fn) (struct dm_target *ti, struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); + void dm_error(const char *message); /* @@ -107,6 +112,7 @@ struct target_type { dm_status_fn status; dm_message_fn message; dm_ioctl_fn ioctl; + dm_merge_fn merge; }; struct io_restrictions { diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index b03c41bbfa14..28c2940eb30d 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h @@ -256,9 +256,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 13 +#define DM_VERSION_MINOR 14 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2007-10-18)" +#define DM_VERSION_EXTRA "-ioctl (2008-04-23)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ -- cgit v1.2.3 From 584015727a3b88b46602b20077b46cd04f8b4ab3 Mon Sep 17 00:00:00 2001 From: Krzysztof Piotr Oledzki Date: Mon, 21 Jul 2008 10:01:34 -0700 Subject: netfilter: accounting rework: ct_extend + 64bit counters (v4) Initially netfilter has had 64bit counters for conntrack-based accounting, but it was changed in 2.6.14 to save memory. Unfortunately in-kernel 64bit counters are still required, for example for "connbytes" extension. However, 64bit counters waste a lot of memory and it was not possible to enable/disable it runtime. This patch: - reimplements accounting with respect to the extension infrastructure, - makes one global version of seq_print_acct() instead of two seq_print_counters(), - makes it possible to enable it at boot time (for CONFIG_SYSCTL/CONFIG_SYSFS=n), - makes it possible to enable/disable it at runtime by sysctl or sysfs, - extends counters from 32bit to 64bit, - renames ip_conntrack_counter -> nf_conn_counter, - enables accounting code unconditionally (no longer depends on CONFIG_NF_CT_ACCT), - set initial accounting enable state based on CONFIG_NF_CT_ACCT - removes buggy IPCT_COUNTER_FILLING event handling. If accounting is enabled newly created connections get additional acct extend. Old connections are not changed as it is not possible to add a ct_extend area to confirmed conntrack. Accounting is performed for all connections with acct extend regardless of a current state of "net.netfilter.nf_conntrack_acct". Signed-off-by: Krzysztof Piotr Oledzki Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- Documentation/feature-removal-schedule.txt | 10 ++ Documentation/kernel-parameters.txt | 7 ++ include/linux/netfilter/nf_conntrack_common.h | 8 +- include/linux/netfilter/nfnetlink_conntrack.h | 8 +- include/net/netfilter/nf_conntrack.h | 6 -- include/net/netfilter/nf_conntrack_acct.h | 51 ++++++++++ include/net/netfilter/nf_conntrack_extend.h | 2 + .../netfilter/nf_conntrack_l3proto_ipv4_compat.c | 18 +--- net/netfilter/Kconfig | 9 ++ net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_acct.c | 104 +++++++++++++++++++++ net/netfilter/nf_conntrack_core.c | 39 +++++--- net/netfilter/nf_conntrack_netlink.c | 44 +++++---- net/netfilter/nf_conntrack_standalone.c | 18 +--- net/netfilter/xt_connbytes.c | 8 +- 15 files changed, 248 insertions(+), 86 deletions(-) create mode 100644 include/net/netfilter/nf_conntrack_acct.h create mode 100644 net/netfilter/nf_conntrack_acct.c (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 86334b6f8238..9f73587219e8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -336,3 +336,13 @@ When: After the only user (hal) has seen a release with the patches Why: Over 1K .text/.data size reduction, data is available in other ways (ioctls) Who: Johannes Berg + +--------------------------- + +What: CONFIG_NF_CT_ACCT +When: 2.6.29 +Why: Accounting can now be enabled/disabled without kernel recompilation. + Currently used only to set a default value for a feature that is also + controlled by a kernel/module/sysfs/sysctl parameter. +Who: Krzysztof Piotr Oledzki + diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 09ad7450647b..e4ef27584408 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1279,6 +1279,13 @@ and is between 256 and 4096 characters. It is defined in the file This usage is only documented in each driver source file if at all. + nf_conntrack.acct= + [NETFILTER] Enable connection tracking flow accounting + 0 to disable accounting + 1 to enable accounting + Default value depends on CONFIG_NF_CT_ACCT that is + going to be removed in 2.6.29. + nfsaddrs= [NFS] See Documentation/filesystems/nfsroot.txt. diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index bad1eb760f61..885cbe282260 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -122,7 +122,7 @@ enum ip_conntrack_events IPCT_NATINFO_BIT = 10, IPCT_NATINFO = (1 << IPCT_NATINFO_BIT), - /* Counter highest bit has been set */ + /* Counter highest bit has been set, unused */ IPCT_COUNTER_FILLING_BIT = 11, IPCT_COUNTER_FILLING = (1 << IPCT_COUNTER_FILLING_BIT), @@ -145,12 +145,6 @@ enum ip_conntrack_expect_events { }; #ifdef __KERNEL__ -struct ip_conntrack_counter -{ - u_int32_t packets; - u_int32_t bytes; -}; - struct ip_conntrack_stat { unsigned int searched; diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 759bc043dc65..c19595c89304 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -115,10 +115,10 @@ enum ctattr_protoinfo_sctp { enum ctattr_counters { CTA_COUNTERS_UNSPEC, - CTA_COUNTERS_PACKETS, /* old 64bit counters */ - CTA_COUNTERS_BYTES, /* old 64bit counters */ - CTA_COUNTERS32_PACKETS, - CTA_COUNTERS32_BYTES, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ __CTA_COUNTERS_MAX }; #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 8f5b75734dd0..0741ad592da0 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -88,7 +88,6 @@ struct nf_conn_help { u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; }; - #include #include @@ -111,11 +110,6 @@ struct nf_conn /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; -#ifdef CONFIG_NF_CT_ACCT - /* Accounting Information (same cache line as other written members) */ - struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; -#endif - #if defined(CONFIG_NF_CONNTRACK_MARK) u_int32_t mark; #endif diff --git a/include/net/netfilter/nf_conntrack_acct.h b/include/net/netfilter/nf_conntrack_acct.h new file mode 100644 index 000000000000..5d5ae55d54c4 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_acct.h @@ -0,0 +1,51 @@ +/* + * (C) 2008 Krzysztof Piotr Oledzki + * + * 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 _NF_CONNTRACK_ACCT_H +#define _NF_CONNTRACK_ACCT_H +#include +#include +#include +#include + +struct nf_conn_counter { + u_int64_t packets; + u_int64_t bytes; +}; + +extern int nf_ct_acct; + +static inline +struct nf_conn_counter *nf_conn_acct_find(const struct nf_conn *ct) +{ + return nf_ct_ext_find(ct, NF_CT_EXT_ACCT); +} + +static inline +struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) +{ + struct nf_conn_counter *acct; + + if (!nf_ct_acct) + return NULL; + + acct = nf_ct_ext_add(ct, NF_CT_EXT_ACCT, gfp); + if (!acct) + pr_debug("failed to add accounting extension area"); + + + return acct; +}; + +extern unsigned int +seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir); + +extern int nf_conntrack_acct_init(void); +extern void nf_conntrack_acct_fini(void); + +#endif /* _NF_CONNTRACK_ACCT_H */ diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index f80c0ed6d870..da8ee52613a5 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -7,11 +7,13 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, NF_CT_EXT_NAT, + NF_CT_EXT_ACCT, NF_CT_EXT_NUM, }; #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat +#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 40a46d482490..3a020720e40b 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -18,19 +18,7 @@ #include #include #include - -#ifdef CONFIG_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif +#include struct ct_iter_state { unsigned int bucket; @@ -127,7 +115,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) + if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) return -ENOSPC; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) @@ -138,7 +126,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) + if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) return -ENOSPC; if (test_bit(IPS_ASSURED_BIT, &ct->status)) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 316c7af1d2b1..ee898e74808d 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -49,6 +49,15 @@ config NF_CT_ACCT Those counters can be used for flow-based accounting or the `connbytes' match. + Please note that currently this option only sets a default state. + You may change it at boot time with nf_conntrack.acct=0/1 kernel + paramater or by loading the nf_conntrack module with acct=0/1. + + You may also disable/enable it on a running system with: + sysctl net.netfilter.nf_conntrack_acct=0/1 + + This option will be removed in 2.6.29. + If unsure, say `N'. config NF_CONNTRACK_MARK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 5c4b183f6422..3bd2cc556aea 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,6 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o -nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o +nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o obj-$(CONFIG_NETFILTER) = netfilter.o diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c new file mode 100644 index 000000000000..59bd8b903a19 --- /dev/null +++ b/net/netfilter/nf_conntrack_acct.c @@ -0,0 +1,104 @@ +/* Accouting handling for netfilter. */ + +/* + * (C) 2008 Krzysztof Piotr Oledzki + * + * 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 +#include +#include + +#include +#include +#include + +#ifdef CONFIG_NF_CT_ACCT +#define NF_CT_ACCT_DEFAULT 1 +#else +#define NF_CT_ACCT_DEFAULT 0 +#endif + +int nf_ct_acct __read_mostly = NF_CT_ACCT_DEFAULT; +EXPORT_SYMBOL_GPL(nf_ct_acct); + +module_param_named(acct, nf_ct_acct, bool, 0644); +MODULE_PARM_DESC(acct, "Enable connection tracking flow accounting."); + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *acct_sysctl_header; +static struct ctl_table acct_sysctl_table[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nf_conntrack_acct", + .data = &nf_ct_acct, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + {} +}; +#endif /* CONFIG_SYSCTL */ + +unsigned int +seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir) +{ + struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; + + return seq_printf(s, "packets=%llu bytes=%llu ", + (unsigned long long)acct[dir].packets, + (unsigned long long)acct[dir].bytes); +}; +EXPORT_SYMBOL_GPL(seq_print_acct); + +static struct nf_ct_ext_type acct_extend __read_mostly = { + .len = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]), + .align = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]), + .id = NF_CT_EXT_ACCT, +}; + +int nf_conntrack_acct_init(void) +{ + int ret; + +#ifdef CONFIG_NF_CT_ACCT + printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Plase use\n"); + printk(KERN_WARNING "nf_conntrack.acct=1 kernel paramater, acct=1 nf_conntrack module option or\n"); + printk(KERN_WARNING "sysctl net.netfilter.nf_conntrack_acct=1 to enable it.\n"); +#endif + + ret = nf_ct_extend_register(&acct_extend); + if (ret < 0) { + printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n"); + return ret; + } + +#ifdef CONFIG_SYSCTL + acct_sysctl_header = register_sysctl_paths(nf_net_netfilter_sysctl_path, + acct_sysctl_table); + + if (!acct_sysctl_header) { + nf_ct_extend_unregister(&acct_extend); + + printk(KERN_ERR "nf_conntrack_acct: can't register to sysctl.\n"); + return -ENOMEM; + } +#endif + + return 0; +} + +void nf_conntrack_acct_fini(void) +{ +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(acct_sysctl_header); +#endif + nf_ct_extend_unregister(&acct_extend); +} diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 28d03e64200b..c519d090bdb9 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -37,6 +37,7 @@ #include #include #include +#include #define NF_CONNTRACK_VERSION "0.5.0" @@ -555,6 +556,8 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, return NULL; } + nf_ct_acct_ext_add(ct, GFP_ATOMIC); + spin_lock_bh(&nf_conntrack_lock); exp = nf_ct_find_expectation(tuple); if (exp) { @@ -828,17 +831,16 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, } acct: -#ifdef CONFIG_NF_CT_ACCT if (do_acct) { - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - skb_network_offset(skb); + struct nf_conn_counter *acct; - if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000) - || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000)) - event |= IPCT_COUNTER_FILLING; + acct = nf_conn_acct_find(ct); + if (acct) { + acct[CTINFO2DIR(ctinfo)].packets++; + acct[CTINFO2DIR(ctinfo)].bytes += + skb->len - skb_network_offset(skb); + } } -#endif spin_unlock_bh(&nf_conntrack_lock); @@ -853,15 +855,19 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, const struct sk_buff *skb, int do_acct) { -#ifdef CONFIG_NF_CT_ACCT if (do_acct) { + struct nf_conn_counter *acct; + spin_lock_bh(&nf_conntrack_lock); - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - skb_network_offset(skb); + acct = nf_conn_acct_find(ct); + if (acct) { + acct[CTINFO2DIR(ctinfo)].packets++; + acct[CTINFO2DIR(ctinfo)].bytes += + skb->len - skb_network_offset(skb); + } spin_unlock_bh(&nf_conntrack_lock); } -#endif + if (del_timer(&ct->timeout)) { ct->timeout.function((unsigned long)ct); return true; @@ -1029,6 +1035,7 @@ void nf_conntrack_cleanup(void) nf_conntrack_proto_fini(); nf_conntrack_helper_fini(); nf_conntrack_expect_fini(); + nf_conntrack_acct_fini(); } struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced) @@ -1168,6 +1175,10 @@ int __init nf_conntrack_init(void) if (ret < 0) goto out_fini_expect; + ret = nf_conntrack_acct_init(); + if (ret < 0) + goto out_fini_helper; + /* For use by REJECT target */ rcu_assign_pointer(ip_ct_attach, nf_conntrack_attach); rcu_assign_pointer(nf_ct_destroy, destroy_conntrack); @@ -1180,6 +1191,8 @@ int __init nf_conntrack_init(void) return ret; +out_fini_helper: + nf_conntrack_helper_fini(); out_fini_expect: nf_conntrack_expect_fini(); out_fini_proto: diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 95a7967731f9..105a616c5c78 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_NF_NAT_NEEDED #include #include @@ -206,22 +207,26 @@ nla_put_failure: return -1; } -#ifdef CONFIG_NF_CT_ACCT static int ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, enum ip_conntrack_dir dir) { enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; struct nlattr *nest_count; + const struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; nest_count = nla_nest_start(skb, type | NLA_F_NESTED); if (!nest_count) goto nla_put_failure; - NLA_PUT_BE32(skb, CTA_COUNTERS32_PACKETS, - htonl(ct->counters[dir].packets)); - NLA_PUT_BE32(skb, CTA_COUNTERS32_BYTES, - htonl(ct->counters[dir].bytes)); + NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS, + cpu_to_be64(acct[dir].packets)); + NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES, + cpu_to_be64(acct[dir].bytes)); nla_nest_end(skb, nest_count); @@ -230,9 +235,6 @@ ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, nla_put_failure: return -1; } -#else -#define ctnetlink_dump_counters(a, b, c) (0) -#endif #ifdef CONFIG_NF_CONNTRACK_MARK static inline int @@ -501,11 +503,6 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, goto nla_put_failure; #endif - if (events & IPCT_COUNTER_FILLING && - (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) - goto nla_put_failure; - if (events & IPCT_RELATED && ctnetlink_dump_master(skb, ct) < 0) goto nla_put_failure; @@ -576,11 +573,15 @@ restart: cb->args[1] = (unsigned long)ct; goto out; } -#ifdef CONFIG_NF_CT_ACCT + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == - IPCTNL_MSG_CT_GET_CTRZERO) - memset(&ct->counters, 0, sizeof(ct->counters)); -#endif + IPCTNL_MSG_CT_GET_CTRZERO) { + struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (acct) + memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX])); + } } if (cb->args[1]) { cb->args[1] = 0; @@ -832,14 +833,9 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, u_int8_t u3 = nfmsg->nfgen_family; int err = 0; - if (nlh->nlmsg_flags & NLM_F_DUMP) { -#ifndef CONFIG_NF_CT_ACCT - if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) - return -ENOTSUPP; -#endif + if (nlh->nlmsg_flags & NLM_F_DUMP) return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, ctnetlink_done); - } if (cda[CTA_TUPLE_ORIG]) err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3); @@ -1152,6 +1148,8 @@ ctnetlink_create_conntrack(struct nlattr *cda[], goto err; } + nf_ct_acct_ext_add(ct, GFP_KERNEL); + #if defined(CONFIG_NF_CONNTRACK_MARK) if (cda[CTA_MARK]) ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 46ea542d0df9..869ef9349d0f 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -25,6 +25,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); @@ -38,19 +39,6 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, } EXPORT_SYMBOL_GPL(print_tuple); -#ifdef CONFIG_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif - struct ct_iter_state { unsigned int bucket; }; @@ -146,7 +134,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) + if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) return -ENOSPC; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) @@ -157,7 +145,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) + if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) return -ENOSPC; if (test_bit(IPS_ASSURED_BIT, &ct->status)) diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index d7e8983cd37f..3e39c4fe1931 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -8,6 +8,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); @@ -27,12 +28,15 @@ connbytes_mt(const struct sk_buff *skb, const struct net_device *in, u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; - const struct ip_conntrack_counter *counters; + const struct nf_conn_counter *counters; ct = nf_ct_get(skb, &ctinfo); if (!ct) return false; - counters = ct->counters; + + counters = nf_conn_acct_find(ct); + if (!counters) + return false; switch (sinfo->what) { case XT_CONNBYTES_PKTS: -- cgit v1.2.3 From 72961ecf84d67d6359a1b30f9b2a8427f13e1e71 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Mon, 21 Jul 2008 10:02:35 -0700 Subject: netfilter: nfnetlink_log: send complete hardware header This patch adds some fields to NFLOG to be able to send the complete hardware header with all necessary informations. It sends to userspace: * the type of hardware link * the lenght of hardware header * the hardware header Signed-off-by: Eric Leblond Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/nfnetlink_log.h | 3 +++ net/netfilter/nfnetlink_log.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h index a85721332924..f661731f3cb1 100644 --- a/include/linux/netfilter/nfnetlink_log.h +++ b/include/linux/netfilter/nfnetlink_log.h @@ -48,6 +48,9 @@ enum nfulnl_attr_type { NFULA_SEQ, /* instance-local sequence number */ NFULA_SEQ_GLOBAL, /* global sequence number */ NFULA_GID, /* group id of socket */ + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ __NFULA_MAX }; diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index b8173af8c24a..9a35b57ab76d 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -453,6 +453,14 @@ __build_packet_message(struct nfulnl_instance *inst, } } + if (indev && skb_mac_header_was_set(skb)) { + NLA_PUT_BE16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)); + NLA_PUT_BE16(inst->skb, NFULA_HWLEN, + htons(skb->dev->hard_header_len)); + NLA_PUT(inst->skb, NFULA_HWHEADER, skb->dev->hard_header_len, + skb_mac_header(skb)); + } + if (skb->tstamp.tv64) { struct nfulnl_msg_packet_timestamp ts; struct timeval tv = ktime_to_timeval(skb->tstamp); -- cgit v1.2.3 From 6579e57b31d79d31d9b806e41ba48774e73257dc Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 21 Jul 2008 13:31:48 -0700 Subject: net: Print the module name as part of the watchdog message As suggested by Dave: This patch adds a function to get the driver name from a struct net_device, and consequently uses this in the watchdog timeout handler to print as part of the message. Signed-off-by: Arjan van de Ven Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 20 ++++++++++++++++++++ net/sched/sch_generic.c | 6 +++--- 3 files changed, 25 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 812bcd8b4363..f5ea445f89f0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1645,6 +1645,8 @@ extern void dev_seq_stop(struct seq_file *seq, void *v); extern int netdev_class_create_file(struct class_attribute *class_attr); extern void netdev_class_remove_file(struct class_attribute *class_attr); +extern char *netdev_drivername(struct net_device *dev, char *buffer, int len); + extern void linkwatch_run_queue(void); extern int netdev_compute_features(unsigned long all, unsigned long one); diff --git a/net/core/dev.c b/net/core/dev.c index 1698b3998981..ad5598d2bb37 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4686,6 +4686,26 @@ err_name: return -ENOMEM; } +char *netdev_drivername(struct net_device *dev, char *buffer, int len) +{ + struct device_driver *driver; + struct device *parent; + + if (len <= 0 || !buffer) + return buffer; + buffer[0] = 0; + + parent = dev->dev.parent; + + if (!parent) + return buffer; + + driver = parent->driver; + if (driver && driver->name) + strlcpy(buffer, driver->name, len); + return buffer; +} + static void __net_exit netdev_exit(struct net *net) { kfree(net->dev_name_head); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index cb625b4d6da5..4ac7e3a8c253 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -212,9 +212,9 @@ static void dev_watchdog(unsigned long arg) if (some_queue_stopped && time_after(jiffies, (dev->trans_start + dev->watchdog_timeo))) { - printk(KERN_INFO "NETDEV WATCHDOG: %s: " - "transmit timed out\n", - dev->name); + char drivername[64]; + printk(KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit timed out\n", + dev->name, netdev_drivername(dev, drivername, 64)); dev->tx_timeout(dev); WARN_ON_ONCE(1); } -- cgit v1.2.3 From ebb36a978131810c98e7198b1187090c697cf99f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 21 Jul 2008 13:41:16 -0700 Subject: ipv6: __KERNEL__ ifdef struct ipv6_devconf Based upon a report by Olaf Hering. Signed-off-by: David S. Miller --- include/linux/ipv6.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 391ad0843a46..641e026eee8f 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -123,6 +123,7 @@ struct ipv6hdr { struct in6_addr daddr; }; +#ifdef __KERNEL__ /* * This structure contains configuration options per IPv6 link. */ @@ -167,6 +168,7 @@ struct ipv6_devconf { __s32 accept_dad; void *sysctl; }; +#endif /* index values for the variables in ipv6_devconf */ enum { -- cgit v1.2.3 From 1b26da1510c02a2dac33c0ea48904256dcec4617 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Jul 2008 12:46:22 -0700 Subject: USB: handle pci_name() being const This changes usb_create_hcd() to be able to handle the fact that pci_name() has changed to a constant string. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- drivers/usb/core/hcd.h | 2 +- include/linux/usb.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 42a436478b78..07f7bedf4dca 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1764,7 +1764,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * If memory is unavailable, returns NULL. */ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, - struct device *dev, char *bus_name) + struct device *dev, const char *bus_name) { struct usb_hcd *hcd; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index b9de1569b39e..50b8bb2d1212 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -235,7 +235,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev, extern int usb_hcd_get_frame_number(struct usb_device *udev); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, - struct device *dev, char *bus_name); + struct device *dev, const char *bus_name); extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd); extern void usb_put_hcd(struct usb_hcd *hcd); extern int usb_add_hcd(struct usb_hcd *hcd, diff --git a/include/linux/usb.h b/include/linux/usb.h index c08689ea9b4b..cee7fbb2b605 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -293,7 +293,7 @@ struct usb_devmap { struct usb_bus { struct device *controller; /* host/master side hardware */ int busnum; /* Bus number (in order of reg) */ - char *bus_name; /* stable id (PCI slot_name etc) */ + const char *bus_name; /* stable id (PCI slot_name etc) */ u8 uses_dma; /* Does the host controller use DMA? */ u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ -- cgit v1.2.3 From 9da82bd4649334817ef0e752a69eb99051645dad Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 8 May 2008 11:54:37 -0400 Subject: USB: implement "soft" unbinding This patch (as1091) changes the way usbcore handles interface unbinding. If the interface's driver supports "soft" unbinding (a new flag in the driver structure) then in-flight URBs are not cancelled and endpoints are not disabled. Instead the driver is allowed to continue communicating with the device (although of course it should stop before its disconnect routine returns). The purpose of this change is to allow drivers to do a clean shutdown when they get unbound from a device that is still plugged in. Killing all the URBs and disabling the endpoints before calling the driver's disconnect method doesn't give the driver any control over what happens, and it can leave devices in indeterminate states. For example, when usb-storage unbinds it doesn't want to stop while in the middle of transmitting a SCSI command. The soft_unbind flag is added because in the past, a number of drivers have experienced problems related to ongoing I/O after their disconnect routine returned. Hence "soft" unbinding is made available only to drivers that claim to support it. The patch also replaces "interface_to_usbdev(intf)" with "udev" in a couple of places, a minor simplification. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 11 ++++++----- include/linux/usb.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0a0e8cea0afc..8da1a56659be 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -257,15 +257,16 @@ static int usb_unbind_interface(struct device *dev) udev = interface_to_usbdev(intf); error = usb_autoresume_device(udev); - /* release all urbs for this interface */ - usb_disable_interface(interface_to_usbdev(intf), intf); + /* Terminate all URBs for this interface unless the driver + * supports "soft" unbinding. + */ + if (!driver->soft_unbind) + usb_disable_interface(udev, intf); driver->disconnect(intf); /* reset other interface state */ - usb_set_interface(interface_to_usbdev(intf), - intf->altsetting[0].desc.bInterfaceNumber, - 0); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; diff --git a/include/linux/usb.h b/include/linux/usb.h index cee7fbb2b605..8429d08bd2fd 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -972,6 +972,8 @@ struct usbdrv_wrap { * added to this driver by preventing the sysfs file from being created. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for interfaces bound to this driver. + * @soft_unbind: if set to 1, the USB core will not kill URBs and disable + * endpoints before calling the driver's disconnect method. * * USB interface drivers must provide a name, probe() and disconnect() * methods, and an id_table. Other driver fields are optional. @@ -1012,6 +1014,7 @@ struct usb_driver { struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; + unsigned int soft_unbind:1; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) -- cgit v1.2.3 From ea05af61a874ffbc158d9cf06df8a9396f299f38 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:00:46 +0300 Subject: USB: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 2 -- drivers/usb/core/devio.c | 2 -- drivers/usb/gadget/rndis.c | 2 -- drivers/usb/gadget/rndis.h | 2 -- drivers/usb/misc/emi62.c | 2 -- drivers/usb/serial/digi_acceleport.c | 2 -- drivers/usb/storage/datafab.c | 2 -- drivers/usb/storage/debug.c | 2 -- drivers/usb/storage/debug.h | 2 -- drivers/usb/storage/dpcm.c | 2 -- drivers/usb/storage/dpcm.h | 2 -- drivers/usb/storage/freecom.c | 2 -- drivers/usb/storage/freecom.h | 2 -- drivers/usb/storage/initializers.c | 2 -- drivers/usb/storage/initializers.h | 2 -- drivers/usb/storage/isd200.c | 2 -- drivers/usb/storage/jumpshot.c | 2 -- drivers/usb/storage/protocol.c | 2 -- drivers/usb/storage/protocol.h | 2 -- drivers/usb/storage/scsiglue.c | 2 -- drivers/usb/storage/scsiglue.h | 2 -- drivers/usb/storage/sddr09.c | 1 - drivers/usb/storage/sddr09.h | 2 -- drivers/usb/storage/sddr55.c | 2 -- drivers/usb/storage/sddr55.h | 2 -- drivers/usb/storage/shuttle_usbat.c | 2 -- drivers/usb/storage/shuttle_usbat.h | 2 -- drivers/usb/storage/transport.c | 2 -- drivers/usb/storage/transport.h | 2 -- drivers/usb/storage/unusual_devs.h | 2 -- drivers/usb/storage/usb.c | 2 -- drivers/usb/storage/usb.h | 2 -- include/linux/usbdevice_fs.h | 2 -- 33 files changed, 65 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 83d9dc379d96..a681d9b92bda 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -46,8 +46,6 @@ * 2000-07-05: Ashley Montanaro * Converted file reading routine to dump to buffer once * per device, not per bus - * - * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ */ #include diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 9218cca21043..7bee9c18b3bc 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -19,8 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ - * * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index d0677f5d3cd5..20b15fcc9d49 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1,8 +1,6 @@ /* * RNDIS MSG parser * - * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 397b149f3ca7..b917b4f34ea8 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -1,8 +1,6 @@ /* * RNDIS Definitions for Remote NDIS * - * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 20886c21e739..5d859ded5bbf 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -6,8 +6,6 @@ * 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, version 2. - * - * $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $ */ #include #include diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 04a56f300ea6..d96302d0d96e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -229,8 +229,6 @@ * in case a wake up is lost. * - Following Documentation/DocBook/kernel-locking.pdf no spin locks * are held when calling copy_to/from_user or printk. -* -* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $ */ #include diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 579e9f52053a..17f1ae232919 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -1,6 +1,4 @@ /* Driver for Datafab USB Compact Flash reader - * - * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ * * datafab driver v0.1: * diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 01e430654a13..a2b5526c9fa0 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Source Code File * - * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 77e244a8c376..dbb985d52423 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Header File * - * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c index 9a410b5a6e5b..939923471af4 100644 --- a/drivers/usb/storage/dpcm.c +++ b/drivers/usb/storage/dpcm.c @@ -1,6 +1,4 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $ * * DPCM driver v0.1: * diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h index 81b464cfcc1e..e7b7b0f120d7 100644 --- a/drivers/usb/storage/dpcm.h +++ b/drivers/usb/storage/dpcm.h @@ -1,6 +1,4 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $ * * DPCM driver v0.1: * diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index f5a4e8d6a3b1..7a4d45677227 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -1,6 +1,4 @@ /* Driver for Freecom USB/IDE adaptor - * - * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $ * * Freecom v0.1: * diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h index 1b012d62d0a8..20d0fe6ba0c8 100644 --- a/drivers/usb/storage/freecom.h +++ b/drivers/usb/storage/freecom.h @@ -1,6 +1,4 @@ /* Driver for Freecom USB/IDE adaptor - * - * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $ * * Freecom v0.1: * diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 187dd1e01093..4995bb595aef 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -1,6 +1,4 @@ /* Special Initializers for certain USB Mass Storage devices - * - * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index ad3ffd4236c2..529327fbb06b 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -1,6 +1,4 @@ /* Header file for Special Initializers for certain USB Mass Storage devices - * - * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index a153335f3648..383abf2516a5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1,6 +1,4 @@ /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC - * - * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance: * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se) diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 61097cbb1585..df67f13c9e73 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -1,6 +1,4 @@ /* Driver for Lexar "Jumpshot" Compact Flash reader - * - * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ * * jumpshot driver v0.1: * diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index b9b8ede61fb3..3b3357e20ea7 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 8737a36891ca..487056ffb516 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Protocol Functions Header File * - * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 043b60b2ad17..b4c9e0f18a82 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index 737e4fa6045f..ffa1cca93d2c 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI Connecting Glue Header File * - * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 8972b17da843..c5a54b872c24 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1,6 +1,5 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * - * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $ * (c) 2000, 2001 Robert Baruch (autophile@starband.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) * Developed with the assistance of: diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index c03089a9ec38..e50033ad7b19 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * Header File * - * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 6d14327c921d..0d8df7577899 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -1,6 +1,4 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader - * - * $Id:$ * * SDDR55 driver v0.1: * diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h index d6bd32f6c9f3..a815a0470c84 100644 --- a/drivers/usb/storage/sddr55.h +++ b/drivers/usb/storage/sddr55.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader * Header File * - * $Id:$ - * * Current development and maintenance by: * (c) 2002 Simon Munton * diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 570c1250f6f3..ae6d64810d2a 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,6 +1,4 @@ /* Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable - * - * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 2000, 2001 Robert Baruch (autophile@starband.net) diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h index 3ddf143a1dec..d8bfc43e9044 100644 --- a/drivers/usb/storage/shuttle_usbat.h +++ b/drivers/usb/storage/shuttle_usbat.h @@ -1,8 +1,6 @@ /* Driver for SCM Microsystems USB-ATAPI cable * Header File * - * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2004, 2005 Daniel Drake diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 94138df557b9..08d3a13fec2c 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index ada7c2f43f84..e70b88182f0e 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Transport Functions Header File * - * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 3ba972c20f3c..7ae69f55aa96 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Unusual Devices File * - * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 6bfd99dd57aa..bfea851be985 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 47906dc620db..a4ad73bd832d 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Main Header File * - * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 3118ede2c67b..0044d9b4cb85 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -22,8 +22,6 @@ * * History: * 0.1 04.01.2000 Created - * - * $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $ */ /*****************************************************************************/ -- cgit v1.2.3 From a4c39c41bf3592684e36fa0dbbd4ab1a31f969b9 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 19 Jun 2008 17:52:25 -0700 Subject: usb gadget: descriptor copying support Define three new descriptor manipulation utilities, for use when setting up functions that may have multiple instances: usb_copy_descriptors() to copy a vector of descriptors usb_free_descriptors() to free the copy usb_find_endpoint() to find a copied version These will be used as follows. Functions will continue to have static tables of descriptors they update, now used as __initdata templates. When a function creates a new instance, it patches those tables with relevant interface and string IDs, plus endpoint assignments. Then it copies those morphed descriptors, associates the copies with the new function instance, and records the endpoint descriptors to use when activating the endpoints. When initialization is done, only the copies remain in memory. The copies are freed on driver removal. This ensures that each instance has descriptors which hold the right instance-specific data. Two instances in the same configuration will obviously never share the same interface IDs or use the same endpoints. Instances in different configurations won't do so either, which means this is slightly less memory-efficient in some cases. This also includes a bugfix to the epautoconf code that shows up with this usage model. It must replace the previous endpoint number when updating the template descriptors, not just mask in a few more bits. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/config.c | 76 ++++++++++++++++++++++++++++++++++++++++- drivers/usb/gadget/epautoconf.c | 1 + include/linux/usb/gadget.h | 19 +++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index a4e54b2743f0..1ca1c326392a 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -96,7 +96,7 @@ int usb_gadget_config_buf( /* config descriptor first */ if (length < USB_DT_CONFIG_SIZE || !desc) return -EINVAL; - *cp = *config; + *cp = *config; /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, @@ -115,3 +115,77 @@ int usb_gadget_config_buf( return len; } +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors. Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors. Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header **__init +usb_copy_descriptors(struct usb_descriptor_header **src) +{ + struct usb_descriptor_header **tmp; + unsigned bytes; + unsigned n_desc; + void *mem; + struct usb_descriptor_header **ret; + + /* count descriptors and their sizes; then add vector size */ + for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) + bytes += (*tmp)->bLength; + bytes += (n_desc + 1) * sizeof(*tmp); + + mem = kmalloc(bytes, GFP_KERNEL); + if (!mem) + return NULL; + + /* fill in pointers starting at "tmp", + * to descriptors copied starting at "mem"; + * and return "ret" + */ + tmp = mem; + ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); + *tmp = mem; + tmp++; + mem += (*src)->bLength; + src++; + } + *tmp = NULL; + + return ret; +} + +/** + * usb_find_endpoint - find a copy of an endpoint descriptor + * @src: original vector of descriptors + * @copy: copy of @src + * @ep: endpoint descriptor found in @src + * + * This returns the copy of the @match descriptor made for @copy. Its + * intended use is to help remembering the endpoint descriptor to use + * when enabling a given endpoint. + */ +struct usb_endpoint_descriptor *__init +usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match +) +{ + while (*src) { + if (*src == (void *) match) + return (void *)*copy; + src++; + copy++; + } + return NULL; +} diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 8bdad221fa91..9462e30192d8 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -159,6 +159,7 @@ ep_matches ( /* MATCH!! */ /* report address */ + desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { u8 num = simple_strtol (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index cf468fbdbf8e..0ebedaec075d 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -858,6 +858,25 @@ int usb_descriptor_fillbuf(void *, unsigned, int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned buflen, const struct usb_descriptor_header **desc); +/* copy a NULL-terminated vector of descriptors */ +struct usb_descriptor_header **usb_copy_descriptors( + struct usb_descriptor_header **); + +/* return copy of endpoint descriptor given original descriptor set */ +struct usb_endpoint_descriptor *usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match); + +/** + * usb_free_descriptors - free descriptors returned by usb_copy_descriptors() + * @v: vector of descriptors + */ +static inline void usb_free_descriptors(struct usb_descriptor_header **v) +{ + kfree(v); +} + /*-------------------------------------------------------------------------*/ /* utility wrapping a simple endpoint selection policy */ -- cgit v1.2.3 From 40982be52d8f64c3e10adce17e66ab755a4fa26b Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 19 Jun 2008 17:52:58 -0700 Subject: usb gadget: composite gadget core Add interfaces for composite gadget drivers, and basic implementation support behind it: - struct usb_function ... groups one or more interfaces into a function managed as one unit within a configuration, to which it's added by usb_add_function(). - struct usb_configuration ... groups one or more such functions into a configuration managed as one unit by a driver, to which it's added by usb_add_config(). These operate at either high or full/low speeds and at a given bMaxPower. - struct usb_composite_driver ... groups one or more such configurations into a gadget driver, which may be registered or unregistered. - struct usb_composite_dev ... a usb_composite_driver manages this; it wraps the usb_gadget exposed by the controller driver. This also includes some basic kerneldoc. How to use it (the short version): provide a usb_composite_driver with a bind() that calls usb_add_config() for each of the needed configurations. The configurations in turn have bind() calls, which will usb_add_function() for each function required. Each function's bind() allocates resources needed to perform its tasks, like endpoints; sometimes configurations will allocate resources too. Separate patches will convert most gadget drivers to this infrastructure. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/gadget.tmpl | 35 ++ drivers/usb/gadget/composite.c | 1040 +++++++++++++++++++++++++++++++++++++ include/linux/usb/composite.h | 338 ++++++++++++ 3 files changed, 1413 insertions(+) create mode 100644 drivers/usb/gadget/composite.c create mode 100644 include/linux/usb/composite.h (limited to 'include/linux') diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index 5a8ffa761e09..478bfe16a19d 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -524,6 +524,41 @@ These utilities include endpoint autoconfiguration. +Composite Device Framework + +The core API is sufficient for writing drivers for composite +USB devices (with more than one function in a given configuration), +and also multi-configuration devices (also more than one function, +but not necessarily sharing a given configuration). +There is however an optional framework which makes it easier to +reuse and combine functions. + + +Devices using this framework provide a struct +usb_composite_driver, which in turn provides one or +more struct usb_configuration instances. +Each such configuration includes at least one +struct usb_function, which packages a user +visible role such as "network link" or "mass storage device". +Management functions may also exist, such as "Device Firmware +Upgrade". + + +!Iinclude/linux/usb/composite.h +!Edrivers/usb/gadget/composite.c + + + +Composite Device Functions + +At this writing, a few of the current gadget drivers have +been converted to this framework. +Near-term plans include converting all of them, except for "gadgetfs". + + + + + Peripheral Controller Drivers diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c new file mode 100644 index 000000000000..b3d302a6a081 --- /dev/null +++ b/drivers/usb/gadget/composite.c @@ -0,0 +1,1040 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include + +#include + + +/* + * The code in this file is utility code, used to build a gadget driver + * from one or more "function" drivers, one or more "configuration" + * objects, and a "usb_composite_driver" by gluing them together along + * with the relevant device-wide data. + */ + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 512 + +static struct usb_composite_driver *composite; + +/* Some systems will need runtime overrides for the product identifers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ + +static ushort idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *iManufacturer; +module_param(iManufacturer, charp, 0); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *iProduct; +module_param(iProduct, charp, 0); +MODULE_PARM_DESC(iProduct, "USB Product string"); + +static char *iSerialNumber; +module_param(iSerialNumber, charp, 0); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int __init usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + /* REVISIT *require* function->bind? */ + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + /* We allow configurations that don't work at both speeds. + * If we run into a lowspeed Linux system, treat it the same + * as full speed ... it's the function drivers that will need + * to avoid bulk and ISO transfers. + */ + if (!config->fullspeed && function->descriptors) + config->fullspeed = true; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = true; + +done: + if (value) + DBG(config->cdev, "adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int __init usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + struct usb_config_descriptor *c = buf; + void *next = buf + USB_DT_CONFIG_SIZE; + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + struct usb_function *f; + int status; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + /* wTotalLength is written later */ + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower; + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + struct usb_descriptor_header **descriptors; + + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + u8 type = w_value >> 8; + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + + if (gadget_is_dualspeed(gadget)) { + int hs = 0; + + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + + } + + /* This is a lookup by config *INDEX* */ + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + unsigned count = 0; + int hs = 0; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); +} + +/*-------------------------------------------------------------------------*/ + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + DBG(cdev, "reset config\n"); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c = NULL; + int result = -EINVAL; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + int tmp; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + INFO(cdev, "%s speed config #%d: %s\n", + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } ; speed; }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + struct usb_function *f = c->interface[tmp]; + + if (!f) + break; + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = 2 * c->bMaxPower; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int __init usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + unsigned i; + + DBG(cdev, "cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + struct usb_function *f = config->interface[i]; + + if (!f) + continue; + DBG(cdev, " interface %d = %s/%p\n", + i, f->name, f); + } + } + + /* set_alt(), or next config->bind(), sets up + * ep->driver_data as needed. + */ + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + DBG(cdev, "added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + struct usb_gadget_strings *s; + int value; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_configuration *c; + struct usb_function *f; + int len; + + /* Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; s->wData[len] && len <= 126; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. + */ +int __init usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* string id 0 is reserved */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DBG((struct usb_composite_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + struct usb_function *f = NULL; + + /* partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + DBG(cdev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(cdev, "HNP on another port\n"); + else + VDBG(cdev, "HNP inactive\n"); + } + spin_lock(&cdev->lock); + value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + if (w_value && !f->get_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + VDBG(cdev, + "non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* functions always handle their interfaces ... punt other + * recipients (endpoint, other, WUSB, ...) to the current + * configuration code. + * + * REVISIT it could make sense to let the composite device + * take such requests too, if that's ever needed: to work + * in config 0, etc. + */ + if ((ctrl->bRequestType & USB_RECIP_MASK) + == USB_RECIP_INTERFACE) { + f = cdev->config->interface[w_index]; + if (f && f->setup) + value = f->setup(f, ctrl); + else + f = NULL; + } + if (value < 0 && !f) { + struct usb_configuration *c; + + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +static void /* __init_or_exit */ +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + struct usb_function *f; + + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + /* may free memory for "f" */ + } + } + list_del(&c->list); + if (c->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + /* may free memory for "c" */ + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + composite = NULL; +} + +static void __init +string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +{ + struct usb_string *str = tab->strings; + + for (str = tab->strings; str->s; str++) { + if (str->id == id) { + str->s = s; + return; + } + } +} + +static void __init +string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +{ + while (*tab) { + string_override_one(*tab, id, s); + tab++; + } +} + +static int __init composite_bind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + + /* interface and string IDs start at zero via kzalloc. + * we force endpoints to start unassigned; few controller + * drivers will zero ep->driver_data. + */ + usb_ep_autoconfig_reset(cdev->gadget); + + /* composite gadget needs to assign strings for whole device (like + * serial number), register function drivers, potentially update + * power state and consumption, etc + */ + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + + /* strings can't be assigned before bind() allocates the + * releavnt identifiers + */ + if (cdev->desc.iManufacturer && iManufacturer) + string_override(composite->strings, + cdev->desc.iManufacturer, iManufacturer); + if (cdev->desc.iProduct && iProduct) + string_override(composite->strings, + cdev->desc.iProduct, iProduct); + if (cdev->desc.iSerialNumber && iSerialNumber) + string_override(composite->strings, + cdev->desc.iSerialNumber, iSerialNumber); + + INFO(cdev, "%s ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "suspend\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "resume\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = __exit_p(composite_unbind), + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, + + .driver = { + .owner = THIS_MODULE, + }, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int __init usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite_driver.function = (char *) driver->name; + composite_driver.driver.name = driver->name; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void __exit usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h new file mode 100644 index 000000000000..747c3a49cdc9 --- /dev/null +++ b/include/linux/usb/composite.h @@ -0,0 +1,338 @@ +/* + * composite.h -- framework for usb gadgets which are composite devices + * + * Copyright (C) 2006-2008 David Brownell + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LINUX_USB_COMPOSITE_H +#define __LINUX_USB_COMPOSITE_H + +/* + * This framework is an optional layer on top of the USB Gadget interface, + * making it easier to build (a) Composite devices, supporting multiple + * functions within any single configuration, and (b) Multi-configuration + * devices, also supporting multiple functions but without necessarily + * having more than one function per configuration. + * + * Example: a device with a single configuration supporting both network + * link and mass storage functions is a composite device. Those functions + * might alternatively be packaged in individual configurations, but in + * the composite model the host can use both functions at the same time. + */ + +#include +#include + + +struct usb_configuration; + +/** + * struct usb_function - describes one function of a configuration + * @name: For diagnostics, identifies the function. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and by language IDs provided in control requests + * @descriptors: Table of full (or low) speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at full speed (or at low speed). + * @hs_descriptors: Table of high speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at high speed. + * @config: assigned when @usb_add_function() is called; this is the + * configuration with which this function is associated. + * @bind: Before the gadget can register, all of its functions bind() to the + * available resources including string and interface identifiers used + * in interface or class descriptors; endpoints; I/O buffers; and so on. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this function. + * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may + * initialize usb_ep.driver data at this time (when it is used). + * Note that setting an interface to its current altsetting resets + * interface state, and that all interfaces have a disabled state. + * @get_alt: Returns the active altsetting. If this is not provided, + * then only altsetting zero is supported. + * @disable: (REQUIRED) Indicates the function should be disabled. Reasons + * include host resetting or reconfiguring the gadget, and disconnection. + * @setup: Used for interface-specific control requests. + * @suspend: Notifies functions when the host stops sending USB traffic. + * @resume: Notifies functions when the host restarts USB traffic. + * + * A single USB function uses one or more interfaces, and should in most + * cases support operation at both full and high speeds. Each function is + * associated by @usb_add_function() with a one configuration; that function + * causes @bind() to be called so resources can be allocated as part of + * setting up a gadget driver. Those resources include endpoints, which + * should be allocated using @usb_ep_autoconfig(). + * + * To support dual speed operation, a function driver provides descriptors + * for both high and full speed operation. Except in rare cases that don't + * involve bulk endpoints, each speed needs different endpoint descriptors. + * + * Function drivers choose their own strategies for managing instance data. + * The simplest strategy just declares it "static', which means the function + * can only be activated once. If the function needs to be exposed in more + * than one configuration at a given speed, it needs to support multiple + * usb_function structures (one for each configuration). + * + * A more complex strategy might encapsulate a @usb_function structure inside + * a driver-specific instance structure to allows multiple activations. An + * example of multiple activations might be a CDC ACM function that supports + * two or more distinct instances within the same configuration, providing + * several independent logical data links to a USB host. + */ +struct usb_function { + const char *name; + struct usb_gadget_strings **strings; + struct usb_descriptor_header **descriptors; + struct usb_descriptor_header **hs_descriptors; + + struct usb_configuration *config; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching. + * Related: unbind() may kfree() but bind() won't... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *, + struct usb_function *); + void (*unbind)(struct usb_configuration *, + struct usb_function *); + + /* runtime state management */ + int (*set_alt)(struct usb_function *, + unsigned interface, unsigned alt); + int (*get_alt)(struct usb_function *, + unsigned interface); + void (*disable)(struct usb_function *); + int (*setup)(struct usb_function *, + const struct usb_ctrlrequest *); + void (*suspend)(struct usb_function *); + void (*resume)(struct usb_function *); + + /* internals */ + struct list_head list; +}; + +int usb_add_function(struct usb_configuration *, struct usb_function *); + +int usb_interface_id(struct usb_configuration *, struct usb_function *); + +/** + * ep_choose - select descriptor endpoint at current device speed + * @g: gadget, connected and running at some speed + * @hs: descriptor to use for high speed operation + * @fs: descriptor to use for full or low speed operation + */ +static inline struct usb_endpoint_descriptor * +ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ + +/** + * struct usb_configuration - represents one gadget configuration + * @label: For diagnostics, describes the configuration. + * @strings: Tables of strings, keyed by identifiers assigned during @bind() + * and by language IDs provided in control requests. + * @descriptors: Table of descriptors preceding all function descriptors. + * Examples include OTG and vendor-specific descriptors. + * @bind: Called from @usb_add_config() to allocate resources unique to this + * configuration and to call @usb_add_function() for each function used. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this configuration. + * @setup: Used to delegate control requests that aren't handled by standard + * device infrastructure or directed at a specific interface. + * @bConfigurationValue: Copied into configuration descriptor. + * @iConfiguration: Copied into configuration descriptor. + * @bmAttributes: Copied into configuration descriptor. + * @bMaxPower: Copied into configuration descriptor. + * @cdev: assigned by @usb_add_config() before calling @bind(); this is + * the device associated with this configuration. + * + * Configurations are building blocks for gadget drivers structured around + * function drivers. Simple USB gadgets require only one function and one + * configuration, and handle dual-speed hardware by always providing the same + * functionality. Slightly more complex gadgets may have more than one + * single-function configuration at a given speed; or have configurations + * that only work at one speed. + * + * Composite devices are, by definition, ones with configurations which + * include more than one function. + * + * The lifecycle of a usb_configuration includes allocation, initialization + * of the fields described above, and calling @usb_add_config() to set up + * internal data and bind it to a specific device. The configuration's + * @bind() method is then used to initialize all the functions and then + * call @usb_add_function() for them. + * + * Those functions would normally be independant of each other, but that's + * not mandatory. CDC WMC devices are an example where functions often + * depend on other functions, with some functions subsidiary to others. + * Such interdependency may be managed in any way, so long as all of the + * descriptors complete by the time the composite driver returns from + * its bind() routine. + */ +struct usb_configuration { + const char *label; + struct usb_gadget_strings **strings; + const struct usb_descriptor_header **descriptors; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *); + void (*unbind)(struct usb_configuration *); + int (*setup)(struct usb_configuration *, + const struct usb_ctrlrequest *); + + /* fields in the config descriptor */ + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; + + struct usb_composite_dev *cdev; + + /* internals */ + struct list_head list; + struct list_head functions; + u8 next_interface_id; + unsigned highspeed:1; + unsigned fullspeed:1; + struct usb_function *interface[MAX_CONFIG_INTERFACES]; +}; + +int usb_add_config(struct usb_composite_dev *, + struct usb_configuration *); + +/** + * struct usb_composite_driver - groups configurations into a gadget + * @name: For diagnostics, identifies the driver. + * @dev: Template descriptor for the device, including default device + * identifiers. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and language IDs provided in control requests + * @bind: (REQUIRED) Used to allocate resources that are shared across the + * whole device, such as string IDs, and add its configurations using + * @usb_add_config(). This may fail by returning a negative errno + * value; it should return zero on successful initialization. + * @unbind: Reverses @bind(); called as a side effect of unregistering + * this driver. + * + * Devices default to reporting self powered operation. Devices which rely + * on bus powered operation should report this in their @bind() method. + * + * Before returning from @bind, various fields in the template descriptor + * may be overridden. These include the idVendor/idProduct/bcdDevice values + * normally to bind the appropriate host side driver, and the three strings + * (iManufacturer, iProduct, iSerialNumber) normally used to provide user + * meaningful device identifiers. (The strings will not be defined unless + * they are defined in @dev and @strings.) The correct ep0 maxpacket size + * is also reported, as defined by the underlying controller driver. + */ +struct usb_composite_driver { + const char *name; + const struct usb_device_descriptor *dev; + struct usb_gadget_strings **strings; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + int (*bind)(struct usb_composite_dev *); + int (*unbind)(struct usb_composite_dev *); +}; + +extern int usb_composite_register(struct usb_composite_driver *); +extern void usb_composite_unregister(struct usb_composite_driver *); + + +/** + * struct usb_composite_device - represents one composite usb gadget + * @gadget: read-only, abstracts the gadget's usb peripheral controller + * @req: used for control responses; buffer is pre-allocated + * @bufsiz: size of buffer pre-allocated in @req + * @config: the currently active configuration + * + * One of these devices is allocated and initialized before the + * associated device driver's bind() is called. + * + * OPEN ISSUE: it appears that some WUSB devices will need to be + * built by combining a normal (wired) gadget with a wireless one. + * This revision of the gadget framework should probably try to make + * sure doing that won't hurt too much. + * + * One notion for how to handle Wireless USB devices involves: + * (a) a second gadget here, discovery mechanism TBD, but likely + * needing separate "register/unregister WUSB gadget" calls; + * (b) updates to usb_gadget to include flags "is it wireless", + * "is it wired", plus (presumably in a wrapper structure) + * bandgroup and PHY info; + * (c) presumably a wireless_ep wrapping a usb_ep, and reporting + * wireless-specific parameters like maxburst and maxsequence; + * (d) configurations that are specific to wireless links; + * (e) function drivers that understand wireless configs and will + * support wireless for (additional) function instances; + * (f) a function to support association setup (like CBAF), not + * necessarily requiring a wireless adapter; + * (g) composite device setup that can create one or more wireless + * configs, including appropriate association setup support; + * (h) more, TBD. + */ +struct usb_composite_dev { + struct usb_gadget *gadget; + struct usb_request *req; + unsigned bufsiz; + + struct usb_configuration *config; + + /* internals */ + struct usb_device_descriptor desc; + struct list_head configs; + struct usb_composite_driver *driver; + u8 next_string_id; + + spinlock_t lock; + + /* REVISIT use and existence of lock ... */ +}; + +extern int usb_string_id(struct usb_composite_dev *c); + +/* messaging utils */ +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARN(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#endif /* __LINUX_USB_COMPOSITE_H */ -- cgit v1.2.3 From e0d795e4f36c2d8949c1355b497fc5425dbb9437 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 3 Jun 2008 14:47:52 +0300 Subject: usb: irda: cleanup on ir-usb module General cleanup on ir-usb module. Introduced a common header that could be used also on usb gadget framework. Lot's of cleanups and now using macros from the header file. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ir-usb.c | 490 ++++++++++++++++++++++++-------------------- include/linux/usb/irda.h | 151 ++++++++++++++ 2 files changed, 421 insertions(+), 220 deletions(-) create mode 100644 include/linux/usb/irda.h (limited to 'include/linux') diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 004d57385a75..0063c11c8081 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -19,7 +19,12 @@ * was written by Roman Weissgaerber , Dag Brattli * , and Jean Tourrilhes * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver + * + * 2008_Jun_02 Felipe Balbi + * Introduced common header to be used also in USB Gadget Framework. + * Still needs some other style fixes. * * 2007_Jun_21 Alan Cox * Minimal cleanups for some of the driver problens and tty layer abuse. @@ -59,9 +64,10 @@ #include #include #include -#include +#include #include #include +#include /* * Version Information @@ -70,100 +76,75 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB IR Dongle driver" -/* USB IrDA class spec information */ -#define USB_CLASS_IRDA 0x02 -#define USB_DT_IRDA 0x21 -#define IU_REQ_GET_CLASS_DESC 0x06 -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 - -struct irda_class_desc { - u8 bLength; - u8 bDescriptorType; - u16 bcdSpecRevision; - u8 bmDataSize; - u8 bmWindowSize; - u8 bmMinTurnaroundTime; - u16 wBaudRate; - u8 bmAdditionalBOFs; - u8 bIrdaRateSniff; - u8 bMaxUnicastList; -} __attribute__ ((packed)); - static int debug; /* if overridden by the user, then use their value for the size of the read and * write urbs */ static int buffer_size; + /* if overridden by the user, then use the specified number of XBOFs */ static int xbof = -1; -static int ir_startup (struct usb_serial *serial); -static int ir_open (struct usb_serial_port *port, struct file *filep); -static void ir_close (struct usb_serial_port *port, struct file *filep); -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static void ir_write_bulk_callback (struct urb *urb); -static void ir_read_bulk_callback (struct urb *urb); -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +static int ir_startup(struct usb_serial *serial); +static int ir_open(struct usb_serial_port *port, struct file *filep); +static void ir_close(struct usb_serial_port *port, struct file *filep); +static int ir_write(struct usb_serial_port *port, + const unsigned char *buf, int count); +static void ir_write_bulk_callback(struct urb *urb); +static void ir_read_bulk_callback(struct urb *urb); +static void ir_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios); /* Not that this lot means you can only have one per system */ -static u8 ir_baud = 0; -static u8 ir_xbof = 0; -static u8 ir_add_bof = 0; +static u8 ir_baud; +static u8 ir_xbof; +static u8 ir_add_bof; -static struct usb_device_id id_table [] = { +static struct usb_device_id ir_id_table[] = { { USB_DEVICE(0x050f, 0x0180) }, /* KC Technology, KC-180 */ { USB_DEVICE(0x08e9, 0x0100) }, /* XTNDAccess */ { USB_DEVICE(0x09c4, 0x0011) }, /* ACTiSys ACT-IR2000U */ - { USB_INTERFACE_INFO (USB_CLASS_APP_SPEC, USB_CLASS_IRDA, 0) }, + { USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, ir_id_table); static struct usb_driver ir_driver = { - .name = "ir-usb", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, + .name = "ir-usb", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = ir_id_table, + .no_dynamic_id = 1, }; - static struct usb_serial_driver ir_device = { - .driver = { - .owner = THIS_MODULE, - .name = "ir-usb", + .driver = { + .owner = THIS_MODULE, + .name = "ir-usb", }, - .description = "IR Dongle", - .usb_driver = &ir_driver, - .id_table = id_table, - .num_ports = 1, - .set_termios = ir_set_termios, - .attach = ir_startup, - .open = ir_open, - .close = ir_close, - .write = ir_write, - .write_bulk_callback = ir_write_bulk_callback, - .read_bulk_callback = ir_read_bulk_callback, + .description = "IR Dongle", + .usb_driver = &ir_driver, + .id_table = ir_id_table, + .num_ports = 1, + .set_termios = ir_set_termios, + .attach = ir_startup, + .open = ir_open, + .close = ir_close, + .write = ir_write, + .write_bulk_callback = ir_write_bulk_callback, + .read_bulk_callback = ir_read_bulk_callback, }; -static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) +static inline void irda_usb_dump_class_desc(struct usb_irda_cs_descriptor *desc) { dbg("bLength=%x", desc->bLength); dbg("bDescriptorType=%x", desc->bDescriptorType); - dbg("bcdSpecRevision=%x", desc->bcdSpecRevision); + dbg("bcdSpecRevision=%x", __le16_to_cpu(desc->bcdSpecRevision)); dbg("bmDataSize=%x", desc->bmDataSize); dbg("bmWindowSize=%x", desc->bmWindowSize); dbg("bmMinTurnaroundTime=%d", desc->bmMinTurnaroundTime); - dbg("wBaudRate=%x", desc->wBaudRate); + dbg("wBaudRate=%x", __le16_to_cpu(desc->wBaudRate)); dbg("bmAdditionalBOFs=%x", desc->bmAdditionalBOFs); dbg("bIrdaRateSniff=%x", desc->bIrdaRateSniff); dbg("bMaxUnicastList=%x", desc->bMaxUnicastList); @@ -181,35 +162,37 @@ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) * * Based on the same function in drivers/net/irda/irda-usb.c */ -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) +static struct usb_irda_cs_descriptor * +irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) { - struct irda_class_desc *desc; + struct usb_irda_cs_descriptor *desc; int ret; - - desc = kzalloc(sizeof (struct irda_class_desc), GFP_KERNEL); - if (desc == NULL) + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) return NULL; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), - IU_REQ_GET_CLASS_DESC, + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CS_IRDA_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, ifnum, desc, sizeof(*desc), 1000); - + dbg("%s - ret=%d", __func__, ret); if (ret < sizeof(*desc)) { dbg("%s - class descriptor read %s (%d)", __func__, - (ret<0) ? "failed" : "too short", + (ret < 0) ? "failed" : "too short", ret); goto error; } - if (desc->bDescriptorType != USB_DT_IRDA) { + if (desc->bDescriptorType != USB_DT_CS_IRDA) { dbg("%s - bad class descriptor type", __func__); goto error; } - + irda_usb_dump_class_desc(desc); return desc; + error: kfree(desc); return NULL; @@ -219,64 +202,100 @@ error: static u8 ir_xbof_change(u8 xbof) { u8 result; + /* reference irda-usb.c */ - switch(xbof) { - case 48: result = 0x10; break; - case 28: - case 24: result = 0x20; break; - default: - case 12: result = 0x30; break; - case 5: - case 6: result = 0x40; break; - case 3: result = 0x50; break; - case 2: result = 0x60; break; - case 1: result = 0x70; break; - case 0: result = 0x80; break; + switch (xbof) { + case 48: + result = 0x10; + break; + case 28: + case 24: + result = 0x20; + break; + default: + case 12: + result = 0x30; + break; + case 5: + case 6: + result = 0x40; + break; + case 3: + result = 0x50; + break; + case 2: + result = 0x60; + break; + case 1: + result = 0x70; + break; + case 0: + result = 0x80; + break; } + return(result); } -static int ir_startup (struct usb_serial *serial) +static int ir_startup(struct usb_serial *serial) { - struct irda_class_desc *irda_desc; + struct usb_irda_cs_descriptor *irda_desc; - irda_desc = irda_usb_find_class_desc (serial->dev, 0); - if (irda_desc == NULL) { - dev_err (&serial->dev->dev, "IRDA class descriptor not found, device not bound\n"); + irda_desc = irda_usb_find_class_desc(serial->dev, 0); + if (!irda_desc) { + dev_err(&serial->dev->dev, + "IRDA class descriptor not found, device not bound\n"); return -ENODEV; } - dbg ("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", + dbg("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", __func__, - (irda_desc->wBaudRate & 0x0001) ? " 2400" : "", - (irda_desc->wBaudRate & 0x0002) ? " 9600" : "", - (irda_desc->wBaudRate & 0x0004) ? " 19200" : "", - (irda_desc->wBaudRate & 0x0008) ? " 38400" : "", - (irda_desc->wBaudRate & 0x0010) ? " 57600" : "", - (irda_desc->wBaudRate & 0x0020) ? " 115200" : "", - (irda_desc->wBaudRate & 0x0040) ? " 576000" : "", - (irda_desc->wBaudRate & 0x0080) ? " 1152000" : "", - (irda_desc->wBaudRate & 0x0100) ? " 4000000" : ""); - - switch( irda_desc->bmAdditionalBOFs ) { - case 0x01: ir_add_bof = 48; break; - case 0x02: ir_add_bof = 24; break; - case 0x04: ir_add_bof = 12; break; - case 0x08: ir_add_bof = 6; break; - case 0x10: ir_add_bof = 3; break; - case 0x20: ir_add_bof = 2; break; - case 0x40: ir_add_bof = 1; break; - case 0x80: ir_add_bof = 0; break; - default:; + (irda_desc->wBaudRate & USB_IRDA_BR_2400) ? " 2400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_9600) ? " 9600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_19200) ? " 19200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_38400) ? " 38400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_57600) ? " 57600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_115200) ? " 115200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_576000) ? " 576000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_1152000) ? " 1152000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_4000000) ? " 4000000" : ""); + + switch (irda_desc->bmAdditionalBOFs) { + case USB_IRDA_AB_48: + ir_add_bof = 48; + break; + case USB_IRDA_AB_24: + ir_add_bof = 24; + break; + case USB_IRDA_AB_12: + ir_add_bof = 12; + break; + case USB_IRDA_AB_6: + ir_add_bof = 6; + break; + case USB_IRDA_AB_3: + ir_add_bof = 3; + break; + case USB_IRDA_AB_2: + ir_add_bof = 2; + break; + case USB_IRDA_AB_1: + ir_add_bof = 1; + break; + case USB_IRDA_AB_0: + ir_add_bof = 0; + break; + default: + break; } - kfree (irda_desc); + kfree(irda_desc); - return 0; + return 0; } -static int ir_open (struct usb_serial_port *port, struct file *filp) +static int ir_open(struct usb_serial_port *port, struct file *filp) { char *buffer; int result = 0; @@ -285,51 +304,55 @@ static int ir_open (struct usb_serial_port *port, struct file *filp) if (buffer_size) { /* override the default buffer sizes */ - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->read_urb->transfer_buffer); + kfree(port->read_urb->transfer_buffer); port->read_urb->transfer_buffer = buffer; port->read_urb->transfer_buffer_length = buffer_size; - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->write_urb->transfer_buffer); + kfree(port->write_urb->transfer_buffer); port->write_urb->transfer_buffer = buffer; port->write_urb->transfer_buffer_length = buffer_size; port->bulk_out_size = buffer_size; } /* Start reading from the device */ - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ir_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); return result; } -static void ir_close (struct usb_serial_port *port, struct file * filp) +static void ir_close(struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); - + /* shutdown our bulk read */ usb_kill_urb(port->read_urb); } -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int ir_write(struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *transfer_buffer; int result; @@ -338,7 +361,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int dbg("%s - port = %d, count = %d", __func__, port->number, count); if (!port->tty) { - dev_err (&port->dev, "%s - no tty???\n", __func__); + dev_err(&port->dev, "%s - no tty???\n", __func__); return 0; } @@ -359,7 +382,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int /* * The first byte of the packet we send to the device contains an - * inband header which indicates an additional number of BOFs and + * inbound header which indicates an additional number of BOFs and * a baud rate change. * * See section 5.4.2.2 of the USB IrDA spec. @@ -367,9 +390,9 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int *transfer_buffer = ir_xbof | ir_baud; ++transfer_buffer; - memcpy (transfer_buffer, buf, transfer_size); + memcpy(transfer_buffer, buf, transfer_size); - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, @@ -381,17 +404,19 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { port->write_urb_busy = 0; - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); } else result = transfer_size; return result; } -static void ir_write_bulk_callback (struct urb *urb) +static void ir_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -405,7 +430,7 @@ static void ir_write_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data ( + usb_serial_debug_data( debug, &port->dev, __func__, @@ -415,7 +440,7 @@ static void ir_write_bulk_callback (struct urb *urb) usb_serial_port_softint(port); } -static void ir_read_bulk_callback (struct urb *urb) +static void ir_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct tty_struct *tty; @@ -431,68 +456,69 @@ static void ir_read_bulk_callback (struct urb *urb) } switch (status) { - case 0: /* Successful */ - - /* - * The first byte of the packet we get from the device - * contains a busy indicator and baud rate change. - * See section 5.4.1.2 of the USB IrDA spec. - */ - if ((*data & 0x0f) > 0) - ir_baud = *data & 0x0f; - - usb_serial_debug_data ( - debug, - &port->dev, - __func__, - urb->actual_length, - data); - - tty = port->tty; - - if (tty_buffer_request_room(tty, urb->actual_length - 1)) { - tty_insert_flip_string(tty, data+1, urb->actual_length - 1); - tty_flip_buffer_push(tty); - } - - /* - * No break here. - * We want to resubmit the urb so we can read - * again. - */ - - case -EPROTO: /* taking inspiration from pl2303.c */ - - /* Continue trying to always read */ - usb_fill_bulk_urb ( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", - __func__, result); - - break ; - - default: - dbg("%s - nonzero read bulk status received: %d", - __func__, - status); - break ; + case 0: /* Successful */ + + /* + * The first byte of the packet we get from the device + * contains a busy indicator and baud rate change. + * See section 5.4.1.2 of the USB IrDA spec. + */ + if ((*data & 0x0f) > 0) + ir_baud = *data & 0x0f; + + usb_serial_debug_data( + debug, + &port->dev, + __func__, + urb->actual_length, + data); + + tty = port->tty; + + if (tty_buffer_request_room(tty, urb->actual_length - 1)) { + tty_insert_flip_string(tty, data + 1, + urb->actual_length - 1); + tty_flip_buffer_push(tty); + } + /* + * No break here. + * We want to resubmit the urb so we can read + * again. + */ + + case -EPROTO: /* taking inspiration from pl2303.c */ + + /* Continue trying to always read */ + usb_fill_bulk_urb( + port->read_urb, + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ir_read_bulk_callback, + port); + + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + break; + + default: + dbg("%s - nonzero read bulk status received: %d", + __func__, + status); + break; } return; } -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ir_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios) { unsigned char *transfer_buffer; int result; @@ -510,19 +536,36 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t */ switch (baud) { - case 2400: ir_baud = SPEED_2400; break; - case 9600: ir_baud = SPEED_9600; break; - case 19200: ir_baud = SPEED_19200; break; - case 38400: ir_baud = SPEED_38400; break; - case 57600: ir_baud = SPEED_57600; break; - case 115200: ir_baud = SPEED_115200; break; - case 576000: ir_baud = SPEED_576000; break; - case 1152000: ir_baud = SPEED_1152000; break; - case 4000000: ir_baud = SPEED_4000000; break; - break; - default: - ir_baud = SPEED_9600; - baud = 9600; + case 2400: + ir_baud = USB_IRDA_BR_2400; + break; + case 9600: + ir_baud = USB_IRDA_BR_9600; + break; + case 19200: + ir_baud = USB_IRDA_BR_19200; + break; + case 38400: + ir_baud = USB_IRDA_BR_38400; + break; + case 57600: + ir_baud = USB_IRDA_BR_57600; + break; + case 115200: + ir_baud = USB_IRDA_BR_115200; + break; + case 576000: + ir_baud = USB_IRDA_BR_576000; + break; + case 1152000: + ir_baud = USB_IRDA_BR_1152000; + break; + case 4000000: + ir_baud = USB_IRDA_BR_4000000; + break; + default: + ir_baud = USB_IRDA_BR_9600; + baud = 9600; } if (xbof == -1) @@ -538,10 +581,11 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t transfer_buffer = port->write_urb->transfer_buffer; *transfer_buffer = ir_xbof | ir_baud; - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, 1, ir_write_bulk_callback, @@ -549,38 +593,44 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_KERNEL); + result = usb_submit_urb(port->write_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); /* Only speed changes are supported */ tty_termios_copy_hw(port->tty->termios, old_termios); tty_encode_baud_rate(port->tty, baud, baud); } - -static int __init ir_init (void) +static int __init ir_init(void) { int retval; + retval = usb_serial_register(&ir_device); if (retval) goto failed_usb_serial_register; + retval = usb_register(&ir_driver); - if (retval) + if (retval) goto failed_usb_register; + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; + failed_usb_register: usb_serial_deregister(&ir_device); + failed_usb_serial_register: return retval; } - -static void __exit ir_exit (void) +static void __exit ir_exit(void) { - usb_deregister (&ir_driver); - usb_serial_deregister (&ir_device); + usb_deregister(&ir_driver); + usb_serial_deregister(&ir_device); } diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h new file mode 100644 index 000000000000..e345ceaf72d6 --- /dev/null +++ b/include/linux/usb/irda.h @@ -0,0 +1,151 @@ +/* + * USB IrDA Bridge Device Definition + */ + +#ifndef __LINUX_USB_IRDA_H +#define __LINUX_USB_IRDA_H + +/* This device should use Application-specific class */ + +#define USB_SUBCLASS_IRDA 0x02 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific requests (bRequest field) */ + +#define USB_REQ_CS_IRDA_RECEIVING 1 +#define USB_REQ_CS_IRDA_CHECK_MEDIA_BUSY 3 +#define USB_REQ_CS_IRDA_RATE_SNIFF 4 +#define USB_REQ_CS_IRDA_UNICAST_LIST 5 +#define USB_REQ_CS_IRDA_GET_CLASS_DESC 6 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific descriptor */ + +#define USB_DT_CS_IRDA 0x21 + +/*-------------------------------------------------------------------------*/ + +/* Data sizes */ + +#define USB_IRDA_DS_2048 (1 << 5) +#define USB_IRDA_DS_1024 (1 << 4) +#define USB_IRDA_DS_512 (1 << 3) +#define USB_IRDA_DS_256 (1 << 2) +#define USB_IRDA_DS_128 (1 << 1) +#define USB_IRDA_DS_64 (1 << 0) + +/* Window sizes */ + +#define USB_IRDA_WS_7 (1 << 6) +#define USB_IRDA_WS_6 (1 << 5) +#define USB_IRDA_WS_5 (1 << 4) +#define USB_IRDA_WS_4 (1 << 3) +#define USB_IRDA_WS_3 (1 << 2) +#define USB_IRDA_WS_2 (1 << 1) +#define USB_IRDA_WS_1 (1 << 0) + +/* Min turnaround times in usecs */ + +#define USB_IRDA_MTT_0 (1 << 7) +#define USB_IRDA_MTT_10 (1 << 6) +#define USB_IRDA_MTT_50 (1 << 5) +#define USB_IRDA_MTT_100 (1 << 4) +#define USB_IRDA_MTT_500 (1 << 3) +#define USB_IRDA_MTT_1000 (1 << 2) +#define USB_IRDA_MTT_5000 (1 << 1) +#define USB_IRDA_MTT_10000 (1 << 0) + +/* Baud rates */ + +#define USB_IRDA_BR_4000000 (1 << 8) +#define USB_IRDA_BR_1152000 (1 << 7) +#define USB_IRDA_BR_576000 (1 << 6) +#define USB_IRDA_BR_115200 (1 << 5) +#define USB_IRDA_BR_57600 (1 << 4) +#define USB_IRDA_BR_38400 (1 << 3) +#define USB_IRDA_BR_19200 (1 << 2) +#define USB_IRDA_BR_9600 (1 << 1) +#define USB_IRDA_BR_2400 (1 << 0) + +/* Additional BOFs */ + +#define USB_IRDA_AB_0 (1 << 7) +#define USB_IRDA_AB_1 (1 << 6) +#define USB_IRDA_AB_2 (1 << 5) +#define USB_IRDA_AB_3 (1 << 4) +#define USB_IRDA_AB_6 (1 << 3) +#define USB_IRDA_AB_12 (1 << 2) +#define USB_IRDA_AB_24 (1 << 1) +#define USB_IRDA_AB_48 (1 << 0) + +/* IRDA Rate Sniff */ + +#define USB_IRDA_RATE_SNIFF 1 + +/*-------------------------------------------------------------------------*/ + +struct usb_irda_cs_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdSpecRevision; + __u8 bmDataSize; + __u8 bmWindowSize; + __u8 bmMinTurnaroundTime; + __le16 wBaudRate; + __u8 bmAdditionalBOFs; + __u8 bIrdaRateSniff; + __u8 bMaxUnicastList; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* Data Format */ + +#define USB_IRDA_STATUS_MEDIA_BUSY (1 << 7) + +/* The following is a 4-bit value used for both + * inbound and outbound headers: + * + * 0 - speed ignored + * 1 - 2400 bps + * 2 - 9600 bps + * 3 - 19200 bps + * 4 - 38400 bps + * 5 - 57600 bps + * 6 - 115200 bps + * 7 - 576000 bps + * 8 - 1.152 Mbps + * 9 - 5 mbps + * 10..15 - Reserved + */ +#define USB_IRDA_STATUS_LINK_SPEED 0x0f + +/* The following is a 4-bit value used only for + * outbound header: + * + * 0 - No change (BOF ignored) + * 1 - 48 BOFs + * 2 - 24 BOFs + * 3 - 12 BOFs + * 4 - 6 BOFs + * 5 - 3 BOFs + * 6 - 2 BOFs + * 7 - 1 BOFs + * 8 - 0 BOFs + * 9..15 - Reserved + */ +#define USB_IRDA_EXTRA_BOFS 0xf0 + +struct usb_irda_inbound_header { + __u8 bmStatus; +}; + +struct usb_irda_outbound_header { + __u8 bmChange; +}; + +#endif /* __LINUX_USB_IRDA_H */ + -- cgit v1.2.3 From f579c2b46f74038e8f5a762c7f10c2385b33e3dc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 Jun 2008 16:26:48 -0400 Subject: USB Gadget: documentation update This patch (as1102) clarifies two points in the USB Gadget kerneldoc: Request completion callbacks are always made with interrupts disabled; Device controllers may not support STALLing the status stage of a control transfer after the data stage is over. Signed-off-by: Alan Stern Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/gadget.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0ebedaec075d..0460a746480c 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -33,7 +33,8 @@ struct usb_ep; * @short_not_ok: When reading data, makes short packets be * treated as errors (queue stops advancing till cleanup). * @complete: Function called when request completes, so this request and - * its buffer may be re-used. + * its buffer may be re-used. The function will always be called with + * interrupts disabled, and it must not sleep. * Reads terminate with a short packet, or when the buffer fills, * whichever comes first. When writes terminate, some data bytes * will usually still be in flight (often in a hardware fifo). @@ -271,7 +272,10 @@ static inline void usb_ep_free_request(struct usb_ep *ep, * (Note that some USB device controllers disallow protocol stall responses * in some cases.) When control responses are deferred (the response is * written after the setup callback returns), then usb_ep_set_halt() may be - * used on ep0 to trigger protocol stalls. + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. * * For periodic endpoints, like interrupt or isochronous ones, the usb host * arranges to poll once per interval, and the gadget driver usually will -- cgit v1.2.3 From 625f694936cbbdee98e6cc65f72724a7660e7946 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 15 Jun 2008 09:42:02 +0800 Subject: USB: remove interface parameter of usb_reset_composite_device From the current implementation of usb_reset_composite_device function, the iface parameter is no longer useful. This function doesn't do something special for the iface usb_interface,compared with other interfaces in the usb_device. So remove the parameter and fix the related caller. Signed-off-by: Ming Lei Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hid-core.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 11 +++-------- drivers/usb/storage/transport.c | 3 +-- include/linux/usb.h | 3 +-- 5 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 01427c51c7cc..69fa79b6b51c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); + rc = usb_reset_composite_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 7bee9c18b3bc..bc1cce5cf758 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -872,7 +872,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev, NULL); + return usb_reset_composite_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3251120b414e..207c33d369be 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2712,7 +2712,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif } else { - status = usb_reset_composite_device(udev, NULL); + status = usb_reset_composite_device(udev); } usb_unlock_device(udev); @@ -2940,7 +2940,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev, intf); + ret = usb_reset_composite_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -3355,7 +3355,6 @@ EXPORT_SYMBOL_GPL(usb_reset_device); /** * usb_reset_composite_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) - * @iface: interface bound to the driver making the request (optional) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that @@ -3368,8 +3367,7 @@ EXPORT_SYMBOL_GPL(usb_reset_device); * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). */ -int usb_reset_composite_device(struct usb_device *udev, - struct usb_interface *iface) +int usb_reset_composite_device(struct usb_device *udev) { int ret; int i; @@ -3385,9 +3383,6 @@ int usb_reset_composite_device(struct usb_device *udev, /* Prevent autosuspend during the reset */ usb_autoresume_device(udev); - if (iface && iface->condition != USB_INTERFACE_BINDING) - iface = NULL; - if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 08d3a13fec2c..670e4cbd1f06 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1172,8 +1172,7 @@ int usb_stor_port_reset(struct us_data *us) result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device( - us->pusb_dev, us->pusb_intf); + result = usb_reset_composite_device(us->pusb_dev); US_DEBUGP("usb_reset_composite_device returns %d\n", result); } diff --git a/include/linux/usb.h b/include/linux/usb.h index 8429d08bd2fd..c74cc64bddc8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -497,8 +497,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int usb_reset_composite_device(struct usb_device *dev, - struct usb_interface *iface); +extern int usb_reset_composite_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); -- cgit v1.2.3 From 742120c63138651c898614001cb58cd607401eac Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 18 Jun 2008 22:00:29 +0800 Subject: USB: fix usb_reset_device and usb_reset_composite_device(take 3) This patch renames the existing usb_reset_device in hub.c to usb_reset_and_verify_device and renames the existing usb_reset_composite_device to usb_reset_device. Also the new usb_reset_and_verify_device does't need to be EXPORTED . The idea of the patch is that external interface driver should warn the other interfaces' driver of the same device before and after reseting the usb device. One interface driver shoud call _old_ usb_reset_composite_device instead of _old_ usb_reset_device since it can't assume the device contains only one interface. The _old_ usb_reset_composite_device is safe for single interface device also. we rename the two functions to make the change easily. This patch is under guideline from Alan Stern. Signed-off-by: Ming Lei --- drivers/hid/usbhid/hid-core.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 33 +++++++++++++++++---------------- drivers/usb/storage/transport.c | 4 ++-- include/linux/usb.h | 5 ++--- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 69fa79b6b51c..27fe4d8912cb 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid)); + rc = usb_reset_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index bc1cce5cf758..e09935acae80 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -872,7 +872,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev); + return usb_reset_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 207c33d369be..bb3ecc4c08f2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -135,6 +135,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); #define HUB_DEBOUNCE_STABLE 100 +static int usb_reset_and_verify_device(struct usb_device *udev); + static inline char *portspeed(int portstatus) { if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) @@ -1971,7 +1973,7 @@ static int finish_port_resume(struct usb_device *udev) * resumed. */ if (udev->reset_resume) - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, @@ -2030,7 +2032,7 @@ static int finish_port_resume(struct usb_device *udev) * to it will be lost. Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * - * This facility can be dangerous. Although usb_reset_device() makes + * This facility can be dangerous. Although usb_reset_and_verify_device() makes * every effort to insure that the same device is present after the * reset as before, it cannot provide a 100% guarantee. Furthermore it's * quite possible for a device to remain unaltered but its media to be @@ -2140,7 +2142,7 @@ int usb_port_resume(struct usb_device *udev) hub_port_logical_disconnect(hub, port1); } else if (udev->reset_resume) { dev_dbg(&udev->dev, "reset-resume\n"); - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); } return status; } @@ -2321,7 +2323,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of - * usb_reset_device), the caller must own the device lock. For a + * usb_reset_and_verify_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */ @@ -2638,7 +2640,7 @@ hub_power_remaining (struct usb_hub *hub) * This routine is called when: * a port connection-change occurs; * a port enable-change occurs (often caused by EMI); - * usb_reset_device() encounters changed descriptors (as from + * usb_reset_and_verify_device() encounters changed descriptors (as from * a firmware download) * caller already locked the hub */ @@ -2712,7 +2714,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif } else { - status = usb_reset_composite_device(udev); + status = usb_reset_device(udev); } usb_unlock_device(udev); @@ -2940,7 +2942,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev); + ret = usb_reset_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -3233,12 +3235,12 @@ static int descriptors_changed(struct usb_device *udev, } /** - * usb_reset_device - perform a USB port reset to reinitialize a device + * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * WARNING - don't use this routine to reset a composite device * (one with multiple interfaces owned by separate drivers)! - * Use usb_reset_composite_device() instead. + * Use usb_reset_device() instead. * * Do a port reset, reassign the device's address, and establish its * former operating configuration. If the reset fails, or the device's @@ -3262,7 +3264,7 @@ static int descriptors_changed(struct usb_device *udev, * holding the device lock because these tasks should always call * usb_autopm_resume_device(), thereby preventing any unwanted autoresume. */ -int usb_reset_device(struct usb_device *udev) +static int usb_reset_and_verify_device(struct usb_device *udev) { struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; @@ -3350,24 +3352,23 @@ re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } -EXPORT_SYMBOL_GPL(usb_reset_device); /** - * usb_reset_composite_device - warn interface drivers and perform a USB port reset + * usb_reset_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that * the reset is over (using their post_reset method). * - * Return value is the same as for usb_reset_device(). + * Return value is the same as for usb_reset_and_verify_device(). * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). */ -int usb_reset_composite_device(struct usb_device *udev) +int usb_reset_device(struct usb_device *udev) { int ret; int i; @@ -3397,7 +3398,7 @@ int usb_reset_composite_device(struct usb_device *udev) } } - ret = usb_reset_device(udev); + ret = usb_reset_and_verify_device(udev); if (config) { for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { @@ -3416,4 +3417,4 @@ int usb_reset_composite_device(struct usb_device *udev) usb_autosuspend_device(udev); return ret; } -EXPORT_SYMBOL_GPL(usb_reset_composite_device); +EXPORT_SYMBOL_GPL(usb_reset_device); diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 670e4cbd1f06..fcbbfdb7b2b0 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1172,8 +1172,8 @@ int usb_stor_port_reset(struct us_data *us) result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device(us->pusb_dev); - US_DEBUGP("usb_reset_composite_device returns %d\n", + result = usb_reset_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); } if (rc_lock) diff --git a/include/linux/usb.h b/include/linux/usb.h index c74cc64bddc8..3cc8db5254d1 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -497,7 +497,6 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int usb_reset_composite_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); @@ -957,9 +956,9 @@ struct usbdrv_wrap { * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. - * @pre_reset: Called by usb_reset_composite_device() when the device + * @pre_reset: Called by usb_reset_device() when the device * is about to be reset. - * @post_reset: Called by usb_reset_composite_device() after the device + * @post_reset: Called by usb_reset_device() after the device * has been reset * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set -- cgit v1.2.3 From 78d9a487ee961c356e1a934d9a92eca38ffb3a70 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 23 Jun 2008 16:00:40 -0400 Subject: USB: Force unbinding of drivers lacking reset_resume or other methods This patch (as1024) takes care of a FIXME issue: Drivers that don't have the necessary suspend, resume, reset_resume, pre_reset, or post_reset methods will be unbound and their interface reprobed when one of the unsupported events occurs. This is made slightly more difficult by the fact that bind operations won't work during a system sleep transition. So instead the code has to defer the operation until the transition ends. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 131 ++++++++++++++++++++++++++++++++++++++++------ drivers/usb/core/hub.c | 27 +++++++--- drivers/usb/core/usb.h | 2 + include/linux/usb.h | 1 + 4 files changed, 140 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8da1a56659be..ddb54e14a5c5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev) intf = to_usb_interface(dev); udev = interface_to_usbdev(intf); + intf->needs_binding = 0; if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); @@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); + iface->needs_binding = 0; usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; @@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *driver) } EXPORT_SYMBOL_GPL(usb_deregister); + +/* Forced unbinding of a USB interface driver, either because + * it doesn't support pre_reset/post_reset/reset_resume or + * because it doesn't support suspend/resume. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + */ +void usb_forced_unbind_intf(struct usb_interface *intf) +{ + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + + /* Mark the interface for later rebinding */ + intf->needs_binding = 1; +} + +/* Delayed forced unbinding of a USB interface driver and scan + * for rebinding. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + * + * FIXME: The caller must block system sleep transitions. + */ +void usb_rebind_intf(struct usb_interface *intf) +{ + int rc; + + /* Delayed unbind of an existing driver */ + if (intf->dev.driver) { + struct usb_driver *driver = + to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + } + + /* Try to rebind the interface */ + intf->needs_binding = 0; + rc = device_attach(&intf->dev); + if (rc < 0) + dev_warn(&intf->dev, "rebind failed: %d\n", rc); +} + +#define DO_UNBIND 0 +#define DO_REBIND 1 + +/* Unbind drivers for @udev's interfaces that don't support suspend/resume, + * or rebind interfaces that have been unbound, according to @action. + * + * The caller must hold @udev's device lock. + * FIXME: For rebinds, the caller must block system sleep transitions. + */ +static void do_unbind_rebind(struct usb_device *udev, int action) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + struct usb_driver *drv; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + switch (action) { + case DO_UNBIND: + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); + } + break; + case DO_REBIND: + if (intf->needs_binding) { + + /* FIXME: The next line is needed because we are going to probe + * the interface, but as far as the PM core is concerned the + * interface is still suspended. The problem wouldn't exist + * if we could rebind the interface during the interface's own + * resume() call, but at the time the usb_device isn't locked! + * + * The real solution will be to carry this out during the device's + * complete() callback. Until that is implemented, we have to + * use this hack. + */ +// intf->dev.power.sleeping = 0; + + usb_rebind_intf(intf); + } + break; + } + } + } +} + #ifdef CONFIG_PM /* Caller has locked udev's pm_mutex */ @@ -841,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) goto done; driver = to_usb_driver(intf->dev.driver); - if (driver->suspend && driver->resume) { + if (driver->suspend) { status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); @@ -849,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { - /* - * FIXME else if there's no suspend method, disconnect... - * Not possible if auto_pm is set... - */ - dev_warn(&intf->dev, "no suspend for driver %s?\n", - driver->name); + /* Later we will unbind the driver and reprobe */ + intf->needs_binding = 1; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "suspend", driver->name); mark_quiesced(intf); } @@ -878,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) goto done; /* Can't resume it if it doesn't have a driver. */ - if (intf->condition == USB_INTERFACE_UNBOUND) { - status = -ENOTCONN; + if (intf->condition == USB_INTERFACE_UNBOUND) + goto done; + + /* Don't resume if the interface is marked for rebinding */ + if (intf->needs_binding) goto done; - } driver = to_usb_driver(intf->dev.driver); if (reset_resume) { @@ -891,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "reset_resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "reset_resume", driver->name); } @@ -902,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "resume", driver->name); } @@ -910,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) done: dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); - if (status == 0) + if (status == 0 && intf->condition == USB_INTERFACE_BOUND) mark_active(intf); - /* FIXME: Unbind the driver and reprobe if the resume failed - * (not possible if auto_pm is set) */ + /* Later we will unbind the driver and/or reprobe, if necessary */ return status; } @@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; + do_unbind_rebind(udev, DO_UNBIND); usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, msg); @@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev) status = usb_resume_both(udev); udev->last_busy = jiffies; usb_pm_unlock(udev); + do_unbind_rebind(udev, DO_REBIND); /* Now that the device is awake, we can start trying to autosuspend * it again. */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bb3ecc4c08f2..f1efabbc1ca2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3367,6 +3367,11 @@ re_enumerate: * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). + * + * If an interface is currently being probed or disconnected, we assume + * its driver knows how to handle resets. For all other interfaces, + * if the driver doesn't have pre_reset and post_reset methods then + * we attempt to unbind it and rebind afterward. */ int usb_reset_device(struct usb_device *udev) { @@ -3388,12 +3393,17 @@ int usb_reset_device(struct usb_device *udev) for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int unbind = 0; if (cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); - if (drv->pre_reset) - (drv->pre_reset)(cintf); - /* FIXME: Unbind if pre_reset returns an error or isn't defined */ + if (drv->pre_reset && drv->post_reset) + unbind = (drv->pre_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + unbind = 1; + if (unbind) + usb_forced_unbind_intf(cintf); } } } @@ -3404,13 +3414,18 @@ int usb_reset_device(struct usb_device *udev) for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int rebind = cintf->needs_binding; - if (cintf->dev.driver) { + if (!rebind && cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); if (drv->post_reset) - (drv->post_reset)(cintf); - /* FIXME: Unbind if post_reset returns an error or isn't defined */ + rebind = (drv->post_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + rebind = 1; } + if (rebind) + usb_rebind_intf(cintf); } } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1a8bc21c335e..d3eb0a29bca1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev); extern void usb_kick_khubd(struct usb_device *dev); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); +extern void usb_forced_unbind_intf(struct usb_interface *intf); +extern void usb_rebind_intf(struct usb_interface *intf); extern int usb_hub_init(void); extern void usb_hub_cleanup(void); diff --git a/include/linux/usb.h b/include/linux/usb.h index 3cc8db5254d1..5811c5da69f9 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -160,6 +160,7 @@ struct usb_interface { unsigned is_active:1; /* the interface is not suspended */ unsigned sysfs_files_created:1; /* the sysfs attributes exist */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ + unsigned needs_binding:1; /* needs delayed unbind/rebind */ struct device dev; /* interface specific device info */ struct device *usb_dev; -- cgit v1.2.3 From 18ad7a61e1b700dfe177fabf1c350b4f5d4da8ac Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 30 May 2008 22:51:33 +1000 Subject: of_gpio: Should use new header Since commit 7560fa60fcdcdb0da662f6a9fad9064b554ef46c (gpio: and "no GPIO support here" stubs) drivers can use GPIOs if they're available, but don't require them. This patch actually enables this feature. Signed-off-by: Wolfgang Grandegger Signed-off-by: Anton Vorontsov Signed-off-by: Benjamin Herrenschmidt --- include/linux/of_gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 2ee97e9877a7..67db101d0eb8 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -15,7 +15,7 @@ #define __LINUX_OF_GPIO_H #include -#include +#include #ifdef CONFIG_OF_GPIO -- cgit v1.2.3 From 1ed6af73440c5ec920884bb800685a8cab4ce847 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Fri, 18 Jul 2008 23:03:34 +1000 Subject: powerpc/cell: Add DMA_ATTR_WEAK_ORDERING dma attribute and use in Cell IOMMU code Introduce a new dma attriblue DMA_ATTR_WEAK_ORDERING to use weak ordering on DMA mappings in the Cell processor. Add the code to the Cell's IOMMU implementation to use this code. Dynamic mappings can be weakly or strongly ordered on an individual basis but the fixed mapping has to be either completely strong or completely weak. This is currently decided by a kernel boot option (pass iommu_fixed=weak for a weakly ordered fixed linear mapping, strongly ordered is the default). Signed-off-by: Mark Nelson Signed-off-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- Documentation/DMA-attributes.txt | 9 +++ arch/powerpc/platforms/cell/iommu.c | 113 ++++++++++++++++++++++++++++++++++-- include/linux/dma-attrs.h | 1 + 3 files changed, 118 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index 6d772f84b477..b768cc0e402b 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt @@ -22,3 +22,12 @@ ready and available in memory. The DMA of the "completion indication" could race with data DMA. Mapping the memory used for completion indications with DMA_ATTR_WRITE_BARRIER would prevent the race. +DMA_ATTR_WEAK_ORDERING +---------------------- + +DMA_ATTR_WEAK_ORDERING specifies that reads and writes to the mapping +may be weakly ordered, that is that reads and writes may pass each other. + +Since it is optional for platforms to implement DMA_ATTR_WEAK_ORDERING, +those that do not will simply ignore the attribute and exhibit default +behavior. diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 3b7078453e7f..208005ca262c 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -199,6 +199,8 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages, base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | (window->ioid & IOPTE_IOID_Mask); #endif + if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))) + base_pte &= ~IOPTE_SO_RW; io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); @@ -539,7 +541,9 @@ static struct cbe_iommu *cell_iommu_for_node(int nid) static unsigned long cell_dma_direct_offset; static unsigned long dma_iommu_fixed_base; -struct dma_mapping_ops dma_iommu_fixed_ops; + +/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */ +static int iommu_fixed_is_weak; static struct iommu_table *cell_get_iommu_table(struct device *dev) { @@ -563,6 +567,98 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) return &window->table; } +/* A coherent allocation implies strong ordering */ + +static void *dma_fixed_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + if (iommu_fixed_is_weak) + return iommu_alloc_coherent(dev, cell_get_iommu_table(dev), + size, dma_handle, + device_to_mask(dev), flag, + dev->archdata.numa_node); + else + return dma_direct_ops.alloc_coherent(dev, size, dma_handle, + flag); +} + +static void dma_fixed_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + if (iommu_fixed_is_weak) + iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr, + dma_handle); + else + dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle); +} + +static dma_addr_t dma_fixed_map_single(struct device *dev, void *ptr, + size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + return dma_direct_ops.map_single(dev, ptr, size, direction, + attrs); + else + return iommu_map_single(dev, cell_get_iommu_table(dev), ptr, + size, device_to_mask(dev), direction, + attrs); +} + +static void dma_fixed_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + dma_direct_ops.unmap_single(dev, dma_addr, size, direction, + attrs); + else + iommu_unmap_single(cell_get_iommu_table(dev), dma_addr, size, + direction, attrs); +} + +static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); + else + return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents, + device_to_mask(dev), direction, attrs); +} + +static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); + else + iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction, + attrs); +} + +static int dma_fixed_dma_supported(struct device *dev, u64 mask) +{ + return mask == DMA_64BIT_MASK; +} + +static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask); + +struct dma_mapping_ops dma_iommu_fixed_ops = { + .alloc_coherent = dma_fixed_alloc_coherent, + .free_coherent = dma_fixed_free_coherent, + .map_single = dma_fixed_map_single, + .unmap_single = dma_fixed_unmap_single, + .map_sg = dma_fixed_map_sg, + .unmap_sg = dma_fixed_unmap_sg, + .dma_supported = dma_fixed_dma_supported, + .set_dma_mask = dma_set_mask_and_switch, +}; + static void cell_dma_dev_setup_fixed(struct device *dev); static void cell_dma_dev_setup(struct device *dev) @@ -919,9 +1015,16 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu, pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase); - base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW + base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask); + if (iommu_fixed_is_weak) + pr_info("IOMMU: Using weak ordering for fixed mapping\n"); + else { + pr_info("IOMMU: Using strong ordering for fixed mapping\n"); + base_pte |= IOPTE_SO_RW; + } + for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) { /* Don't touch the dynamic region */ ioaddr = uaddr + fbase; @@ -1037,9 +1140,6 @@ static int __init cell_iommu_fixed_mapping_init(void) cell_iommu_setup_window(iommu, np, dbase, dsize, 0); } - dma_iommu_fixed_ops = dma_direct_ops; - dma_iommu_fixed_ops.set_dma_mask = dma_set_mask_and_switch; - dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; set_pci_dma_ops(&dma_iommu_ops); @@ -1053,6 +1153,9 @@ static int __init setup_iommu_fixed(char *str) if (strcmp(str, "off") == 0) iommu_fixed_disabled = 1; + else if (strcmp(str, "weak") == 0) + iommu_fixed_is_weak = 1; + return 1; } __setup("iommu_fixed=", setup_iommu_fixed); diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index 1677e2bfa00c..71ad34eca6e3 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h @@ -12,6 +12,7 @@ */ enum dma_attr { DMA_ATTR_WRITE_BARRIER, + DMA_ATTR_WEAK_ORDERING, DMA_ATTR_MAX, }; -- cgit v1.2.3 From e105b8bfc769b0545b6f0f395179d1e43cbee822 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 21 Apr 2008 10:51:07 -0700 Subject: sysfs: add /sys/dev/{char,block} to lookup sysfs path by major:minor Why?: There are occasions where userspace would like to access sysfs attributes for a device but it may not know how sysfs has named the device or the path. For example what is the sysfs path for /dev/disk/by-id/ata-ST3160827AS_5MT004CK? With this change a call to stat(2) returns the major:minor then userspace can see that /sys/dev/block/8:32 links to /sys/block/sdc. What are the alternatives?: 1/ Add an ioctl to return the path: Doable, but sysfs is meant to reduce the need to proliferate ioctl interfaces into the kernel, so this seems counter productive. 2/ Use udev to create these symlinks: Also doable, but it adds a udev dependency to utilities that might be running in a limited environment like an initramfs. 3/ Do a full-tree search of sysfs. [kay.sievers@vrfy.org: fix duplicate registrations] [kay.sievers@vrfy.org: cleanup suggestions] Cc: Neil Brown Cc: Tejun Heo Acked-by: Kay Sievers Reviewed-by: SL Baur Acked-by: Kay Sievers Acked-by: Mark Lord Acked-by: H. Peter Anvin Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-dev | 20 +++++++++ Documentation/filesystems/sysfs.txt | 6 +++ block/genhd.c | 5 ++- drivers/base/class.c | 4 ++ drivers/base/core.c | 83 ++++++++++++++++++++++++++++++++++++- drivers/usb/core/devio.c | 5 +++ include/linux/device.h | 3 ++ 7 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-dev (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-dev b/Documentation/ABI/testing/sysfs-dev new file mode 100644 index 000000000000..a9f2b8b0530f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-dev @@ -0,0 +1,20 @@ +What: /sys/dev +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Dan Williams +Description: The /sys/dev tree provides a method to look up the sysfs + path for a device using the information returned from + stat(2). There are two directories, 'block' and 'char', + beneath /sys/dev containing symbolic links with names of + the form ":". These links point to the + corresponding sysfs path for the given device. + + Example: + $ readlink /sys/dev/block/8:32 + ../../block/sdc + + Entries in /sys/dev/char and /sys/dev/block will be + dynamically created and destroyed as devices enter and + leave the system. + +Users: mdadm diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt index 7f27b8f840d0..9e9c348275a9 100644 --- a/Documentation/filesystems/sysfs.txt +++ b/Documentation/filesystems/sysfs.txt @@ -248,6 +248,7 @@ The top level sysfs directory looks like: block/ bus/ class/ +dev/ devices/ firmware/ net/ @@ -274,6 +275,11 @@ fs/ contains a directory for some filesystems. Currently each filesystem wanting to export attributes must create its own hierarchy below fs/ (see ./fuse.txt for an example). +dev/ contains two directories char/ and block/. Inside these two +directories there are symlinks named :. These symlinks +point to the sysfs directory for the given device. /sys/dev provides a +quick way to lookup the sysfs interface for a device from the result of +a stat(2) operation. More information can driver-model specific features can be found in Documentation/driver-model/. diff --git a/block/genhd.c b/block/genhd.c index 9074f384b097..24e3fc9095fe 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -370,7 +370,10 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data) static int __init genhd_device_init(void) { - int error = class_register(&block_class); + int error; + + block_class.dev_kobj = sysfs_dev_block_kobj; + error = class_register(&block_class); if (unlikely(error)) return error; bdev_map = kobj_map_init(base_probe, &block_class_lock); diff --git a/drivers/base/class.c b/drivers/base/class.c index e085af0ff94f..71ce3ff6bdf5 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -148,6 +148,10 @@ int class_register(struct class *cls) if (error) return error; + /* set the default /sys/dev directory for devices of this class */ + if (!cls->dev_kobj) + cls->dev_kobj = sysfs_dev_char_kobj; + #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (cls != &block_class) diff --git a/drivers/base/core.c b/drivers/base/core.c index ee0a51a3a41d..be9aba4dc2fb 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -27,6 +27,9 @@ int (*platform_notify)(struct device *dev) = NULL; int (*platform_notify_remove)(struct device *dev) = NULL; +static struct kobject *dev_kobj; +struct kobject *sysfs_dev_char_kobj; +struct kobject *sysfs_dev_block_kobj; #ifdef CONFIG_BLOCK static inline int device_is_not_partition(struct device *dev) @@ -775,6 +778,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...) } EXPORT_SYMBOL_GPL(dev_set_name); +/** + * device_to_dev_kobj - select a /sys/dev/ directory for the device + * @dev: device + * + * By default we select char/ for new entries. Setting class->dev_obj + * to NULL prevents an entry from being created. class->dev_kobj must + * be set (or cleared) before any devices are registered to the class + * otherwise device_create_sys_dev_entry() and + * device_remove_sys_dev_entry() will disagree about the the presence + * of the link. + */ +static struct kobject *device_to_dev_kobj(struct device *dev) +{ + struct kobject *kobj; + + if (dev->class) + kobj = dev->class->dev_kobj; + else + kobj = sysfs_dev_char_kobj; + + return kobj; +} + +static int device_create_sys_dev_entry(struct device *dev) +{ + struct kobject *kobj = device_to_dev_kobj(dev); + int error = 0; + char devt_str[15]; + + if (kobj) { + format_dev_t(devt_str, dev->devt); + error = sysfs_create_link(kobj, &dev->kobj, devt_str); + } + + return error; +} + +static void device_remove_sys_dev_entry(struct device *dev) +{ + struct kobject *kobj = device_to_dev_kobj(dev); + char devt_str[15]; + + if (kobj) { + format_dev_t(devt_str, dev->devt); + sysfs_remove_link(kobj, devt_str); + } +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -829,6 +880,10 @@ int device_add(struct device *dev) error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; + + error = device_create_sys_dev_entry(dev); + if (error) + goto devtattrError; } error = device_add_class_symlinks(dev); @@ -872,6 +927,9 @@ int device_add(struct device *dev) AttrsError: device_remove_class_symlinks(dev); SymlinkError: + if (MAJOR(dev->devt)) + device_remove_sys_dev_entry(dev); + devtattrError: if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr); ueventattrError: @@ -948,8 +1006,10 @@ void device_del(struct device *dev) device_pm_remove(dev); if (parent) klist_del(&dev->knode_parent); - if (MAJOR(dev->devt)) + if (MAJOR(dev->devt)) { + device_remove_sys_dev_entry(dev); device_remove_file(dev, &devt_attr); + } if (dev->class) { device_remove_class_symlinks(dev); @@ -1074,7 +1134,25 @@ int __init devices_init(void) devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; + dev_kobj = kobject_create_and_add("dev", NULL); + if (!dev_kobj) + goto dev_kobj_err; + sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); + if (!sysfs_dev_block_kobj) + goto block_kobj_err; + sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); + if (!sysfs_dev_char_kobj) + goto char_kobj_err; + return 0; + + char_kobj_err: + kobject_put(sysfs_dev_block_kobj); + block_kobj_err: + kobject_put(dev_kobj); + dev_kobj_err: + kset_unregister(devices_kset); + return -ENOMEM; } EXPORT_SYMBOL_GPL(device_for_each_child); @@ -1447,4 +1525,7 @@ void device_shutdown(void) dev->driver->shutdown(dev); } } + kobject_put(sysfs_dev_char_kobj); + kobject_put(sysfs_dev_block_kobj); + kobject_put(dev_kobj); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 54a350ccd033..6fbc8f5ab80c 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1792,6 +1792,11 @@ int __init usb_devio_init(void) usb_classdev_class = NULL; goto out; } + /* devices of this class shadow the major:minor of their parent + * device, so clear ->dev_kobj to prevent adding duplicate entries + * to /sys/dev + */ + usb_classdev_class->dev_kobj = NULL; usb_register_notify(&usbdev_nb); #endif diff --git a/include/linux/device.h b/include/linux/device.h index f71a78d123ae..e49aa74f248c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -193,6 +193,7 @@ struct class { struct semaphore sem; /* locks children, devices, interfaces */ struct class_attribute *class_attrs; struct device_attribute *dev_attrs; + struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); @@ -205,6 +206,8 @@ struct class { struct pm_ops *pm; }; +extern struct kobject *sysfs_dev_block_kobj; +extern struct kobject *sysfs_dev_char_kobj; extern int __must_check class_register(struct class *class); extern void class_unregister(struct class *class); extern int class_for_each_device(struct class *class, void *data, -- cgit v1.2.3 From ccea44fadca396b0f89aed5bdeb60e4abb212566 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 May 2008 12:52:33 -0700 Subject: driver core: remove device_create() There are no more users of this, and it is racy. Use device_create_drvdata() or device_create_vargs() instead. Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 34 ---------------------------------- include/linux/device.h | 3 --- 2 files changed, 37 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index be9aba4dc2fb..4dc0d272f6d9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1273,40 +1273,6 @@ struct device *device_create_drvdata(struct class *class, } EXPORT_SYMBOL_GPL(device_create_drvdata); -/** - * device_create - creates a device and registers it with sysfs - * @class: pointer to the struct class that this device should be registered to - * @parent: pointer to the parent struct device of this new device, if any - * @devt: the dev_t for the char device to be added - * @fmt: string for the device's name - * - * This function can be used by char device classes. A struct device - * will be created in sysfs, registered to the specified class. - * - * A "dev" file will be created, showing the dev_t for the device, if - * the dev_t is not 0,0. - * If a pointer to a parent struct device is passed in, the newly created - * struct device will be a child of that device in sysfs. - * The pointer to the struct device will be returned from the call. - * Any further sysfs files that might be required can be created using this - * pointer. - * - * Note: the struct class passed to this function must have previously - * been created with a call to class_create(). - */ -struct device *device_create(struct class *class, struct device *parent, - dev_t devt, const char *fmt, ...) -{ - va_list vargs; - struct device *dev; - - va_start(vargs, fmt); - dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs); - va_end(vargs); - return dev; -} -EXPORT_SYMBOL_GPL(device_create); - static int __match_devt(struct device *dev, void *data) { dev_t *devt = data; diff --git a/include/linux/device.h b/include/linux/device.h index e49aa74f248c..a3ef5a2d4fbb 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -470,9 +470,6 @@ extern struct device *device_create_vargs(struct class *cls, void *drvdata, const char *fmt, va_list vargs); -extern struct device *device_create(struct class *cls, struct device *parent, - dev_t devt, const char *fmt, ...) - __attribute__((format(printf, 4, 5))); extern struct device *device_create_drvdata(struct class *cls, struct device *parent, dev_t devt, -- cgit v1.2.3 From 4e10673944a5c386378ff9d692ae37e19993f9d5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 21 Jul 2008 20:03:34 -0700 Subject: device create: convert device_create_drvdata to device_create Now that device_create() has been audited, rename things back to the original call to be sane. Keep the device_create_drvdata macro around to make merges easier. Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 11 ++++------- include/linux/device.h | 9 ++++----- 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4dc0d272f6d9..9ae28aa709d5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1236,7 +1236,7 @@ error: EXPORT_SYMBOL_GPL(device_create_vargs); /** - * device_create_drvdata - creates a device and registers it with sysfs + * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added @@ -1257,11 +1257,8 @@ EXPORT_SYMBOL_GPL(device_create_vargs); * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */ -struct device *device_create_drvdata(struct class *class, - struct device *parent, - dev_t devt, - void *drvdata, - const char *fmt, ...) +struct device *device_create(struct class *class, struct device *parent, + dev_t devt, void *drvdata, const char *fmt, ...) { va_list vargs; struct device *dev; @@ -1271,7 +1268,7 @@ struct device *device_create_drvdata(struct class *class, va_end(vargs); return dev; } -EXPORT_SYMBOL_GPL(device_create_drvdata); +EXPORT_SYMBOL_GPL(device_create); static int __match_devt(struct device *dev, void *data) { diff --git a/include/linux/device.h b/include/linux/device.h index a3ef5a2d4fbb..de178712e02c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -470,12 +470,11 @@ extern struct device *device_create_vargs(struct class *cls, void *drvdata, const char *fmt, va_list vargs); -extern struct device *device_create_drvdata(struct class *cls, - struct device *parent, - dev_t devt, - void *drvdata, - const char *fmt, ...) +extern struct device *device_create(struct class *cls, struct device *parent, + dev_t devt, void *drvdata, + const char *fmt, ...) __attribute__((format(printf, 5, 6))); +#define device_create_drvdata device_create extern void device_destroy(struct class *cls, dev_t devt); /* -- cgit v1.2.3 From 93562b537659fc0f63920fd4d9d24f54e434f4c4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 May 2008 17:21:08 -0400 Subject: Driver Core: add ability for class_for_each_device to start in middle of list This mirrors the functionality that driver_for_each_device has as well. We add a start variable, and all callers of the function are fixed up at the same time. The block layer will be using this new functionality in a follow-on patch. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 21 +++++++++++++-------- drivers/i2c/i2c-core.c | 6 ++++-- drivers/ieee1394/nodemgr.c | 14 +++++++++----- drivers/power/apm_power.c | 2 +- drivers/power/power_supply_core.c | 4 ++-- include/linux/device.h | 3 ++- 6 files changed, 31 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 71ce3ff6bdf5..2eb7048003a8 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -256,11 +256,14 @@ char *make_class_name(const char *name, struct kobject *kobj) /** * class_for_each_device - device iterator * @class: the class we're iterating + * @start: the device to start with in the list, if any. * @data: data for the callback * @fn: function to be called for each device * * Iterate over @class's list of devices, and call @fn for each, - * passing it @data. + * passing it @data. If @start is set, the list iteration will start + * there, otherwise if it is NULL, the iteration starts at the + * beginning of the list. * * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. @@ -269,8 +272,8 @@ char *make_class_name(const char *name, struct kobject *kobj) * re-acquired in @fn, otherwise it will self-deadlocking. For * example, calls to add or remove class members would be verboten. */ -int class_for_each_device(struct class *class, void *data, - int (*fn)(struct device *, void *)) +int class_for_each_device(struct class *class, struct device *start, + void *data, int (*fn)(struct device *, void *)) { struct device *dev; int error = 0; @@ -279,12 +282,14 @@ int class_for_each_device(struct class *class, void *data, return -EINVAL; down(&class->sem); list_for_each_entry(dev, &class->devices, node) { + if (start) { + if (start == dev) + start = NULL; + continue; + } dev = get_device(dev); - if (dev) { - error = fn(dev, data); - put_device(dev); - } else - error = -ENODEV; + error = fn(dev, data); + put_device(dev); if (error) break; } diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 7608df83d6d1..7bf38c418086 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -722,7 +722,8 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ - class_for_each_device(&i2c_adapter_class, driver, __attach_adapter); + class_for_each_device(&i2c_adapter_class, NULL, driver, + __attach_adapter); mutex_unlock(&core_lock); return 0; @@ -782,7 +783,8 @@ void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - class_for_each_device(&i2c_adapter_class, driver, __detach_adapter); + class_for_each_device(&i2c_adapter_class, NULL, driver, + __detach_adapter); driver_unregister(&driver->driver); pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 05710c7c1220..47c0d85e0f32 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -1453,7 +1453,8 @@ static void nodemgr_suspend_ne(struct node_entry *ne) ne->in_limbo = 1; WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo)); - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_suspend); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_driver_suspend); } @@ -1462,7 +1463,8 @@ static void nodemgr_resume_ne(struct node_entry *ne) ne->in_limbo = 0; device_remove_file(&ne->device, &dev_attr_ne_in_limbo); - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_resume); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_driver_resume); HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); } @@ -1498,7 +1500,8 @@ static int __nodemgr_update_pdrv(struct device *dev, void *data) static void nodemgr_update_pdrv(struct node_entry *ne) { - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_update_pdrv); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_update_pdrv); } @@ -1591,7 +1594,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation) * while probes are time-consuming. (Well, those probes need some * improvement...) */ - class_for_each_device(&nodemgr_ne_class, ¶m, __nodemgr_node_probe); + class_for_each_device(&nodemgr_ne_class, NULL, ¶m, + __nodemgr_node_probe); /* If we had a bus reset while we were scanning the bus, it is * possible that we did not probe all nodes. In that case, we @@ -1826,7 +1830,7 @@ int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) hip.cb = cb; hip.data = data; - error = class_for_each_device(&hpsb_host_class, &hip, + error = class_for_each_device(&hpsb_host_class, NULL, &hip, __nodemgr_for_each_host); return error; diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index a4892275659d..936bae560fa1 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -78,7 +78,7 @@ static void find_main_battery(void) main_battery = NULL; bp.main = main_battery; - error = class_for_each_device(power_supply_class, &bp, + error = class_for_each_device(power_supply_class, NULL, &bp, __find_main_battery); if (error) { main_battery = bp.main; diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index af1633eb3b70..cb1ccb472921 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -41,7 +41,7 @@ static void power_supply_changed_work(struct work_struct *work) dev_dbg(psy->dev, "%s\n", __func__); - class_for_each_device(power_supply_class, psy, + class_for_each_device(power_supply_class, NULL, psy, __power_supply_changed_work); power_supply_update_leds(psy); @@ -79,7 +79,7 @@ int power_supply_am_i_supplied(struct power_supply *psy) { int error; - error = class_for_each_device(power_supply_class, psy, + error = class_for_each_device(power_supply_class, NULL, psy, __power_supply_am_i_supplied); dev_dbg(psy->dev, "%s %d\n", __func__, error); diff --git a/include/linux/device.h b/include/linux/device.h index de178712e02c..6d5b351b29c9 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -210,7 +210,8 @@ extern struct kobject *sysfs_dev_block_kobj; extern struct kobject *sysfs_dev_char_kobj; extern int __must_check class_register(struct class *class); extern void class_unregister(struct class *class); -extern int class_for_each_device(struct class *class, void *data, +extern int class_for_each_device(struct class *class, struct device *start, + void *data, int (*fn)(struct device *dev, void *data)); extern struct device *class_find_device(struct class *class, void *data, int (*match)(struct device *, void *)); -- cgit v1.2.3 From 695794ae0c5bdd9bd06e35b118801e2e9be04f9e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 May 2008 17:21:08 -0400 Subject: Driver Core: add ability for class_find_device to start in middle of list This mirrors the functionality that driver_find_device has as well. We add a start variable, and all callers of the function are fixed up at the same time. The block layer will be using this new functionality in a follow-on patch. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 22 +++++++++++++--------- drivers/base/core.c | 2 +- drivers/ieee1394/nodemgr.c | 9 ++++++--- drivers/rtc/interface.c | 2 +- drivers/scsi/hosts.c | 3 ++- drivers/scsi/scsi_transport_iscsi.c | 4 ++-- drivers/spi/spi.c | 2 +- include/linux/device.h | 3 ++- 8 files changed, 28 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 2eb7048003a8..3918d0e432d4 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -302,6 +302,7 @@ EXPORT_SYMBOL_GPL(class_for_each_device); /** * class_find_device - device iterator for locating a particular device * @class: the class we're iterating + * @start: Device to begin with * @data: data for the match function * @match: function to check device * @@ -319,8 +320,9 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * re-acquired in @match, otherwise it will self-deadlocking. For * example, calls to add or remove class members would be verboten. */ -struct device *class_find_device(struct class *class, void *data, - int (*match)(struct device *, void *)) +struct device *class_find_device(struct class *class, struct device *start, + void *data, + int (*match)(struct device *, void *)) { struct device *dev; int found = 0; @@ -330,15 +332,17 @@ struct device *class_find_device(struct class *class, void *data, down(&class->sem); list_for_each_entry(dev, &class->devices, node) { + if (start) { + if (start == dev) + start = NULL; + continue; + } dev = get_device(dev); - if (dev) { - if (match(dev, data)) { - found = 1; - break; - } else - put_device(dev); - } else + if (match(dev, data)) { + found = 1; break; + } else + put_device(dev); } up(&class->sem); diff --git a/drivers/base/core.c b/drivers/base/core.c index 9ae28aa709d5..9f05de6f80b5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1289,7 +1289,7 @@ void device_destroy(struct class *class, dev_t devt) { struct device *dev; - dev = class_find_device(class, &devt, __match_devt); + dev = class_find_device(class, NULL, &devt, __match_devt); if (dev) { put_device(dev); device_unregister(dev); diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 47c0d85e0f32..994a21e5a0aa 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -754,7 +754,8 @@ static void nodemgr_remove_uds(struct node_entry *ne) */ mutex_lock(&nodemgr_serialize_remove_uds); for (;;) { - dev = class_find_device(&nodemgr_ud_class, ne, __match_ne); + dev = class_find_device(&nodemgr_ud_class, NULL, ne, + __match_ne); if (!dev) break; ud = container_of(dev, struct unit_directory, unit_dev); @@ -901,7 +902,8 @@ static struct node_entry *find_entry_by_guid(u64 guid) struct device *dev; struct node_entry *ne; - dev = class_find_device(&nodemgr_ne_class, &guid, __match_ne_guid); + dev = class_find_device(&nodemgr_ne_class, NULL, &guid, + __match_ne_guid); if (!dev) return NULL; ne = container_of(dev, struct node_entry, node_dev); @@ -940,7 +942,8 @@ static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, param.host = host; param.nodeid = nodeid; - dev = class_find_device(&nodemgr_ne_class, ¶m, __match_ne_nodeid); + dev = class_find_device(&nodemgr_ne_class, NULL, ¶m, + __match_ne_nodeid); if (!dev) return NULL; ne = container_of(dev, struct node_entry, node_dev); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 58b7336640ff..d397fa5f3a91 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -345,7 +345,7 @@ struct rtc_device *rtc_class_open(char *name) struct device *dev; struct rtc_device *rtc = NULL; - dev = class_find_device(rtc_class, name, __rtc_match); + dev = class_find_device(rtc_class, NULL, name, __rtc_match); if (dev) rtc = to_rtc_device(dev); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 35cd892dce04..78dad28b70d5 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -466,7 +466,8 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct device *cdev; struct Scsi_Host *shost = ERR_PTR(-ENXIO); - cdev = class_find_device(&shost_class, &hostnum, __scsi_host_match); + cdev = class_find_device(&shost_class, NULL, &hostnum, + __scsi_host_match); if (cdev) { shost = scsi_host_get(class_to_shost(cdev)); put_device(cdev); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 3af7cbcc5c5d..06748f318cd5 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -170,7 +170,7 @@ iscsi_create_endpoint(int dd_size) int err; for (id = 1; id < ISCSI_MAX_EPID; id++) { - dev = class_find_device(&iscsi_endpoint_class, &id, + dev = class_find_device(&iscsi_endpoint_class, NULL, &id, iscsi_match_epid); if (!dev) break; @@ -222,7 +222,7 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) struct iscsi_endpoint *ep; struct device *dev; - dev = class_find_device(&iscsi_endpoint_class, &handle, + dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, iscsi_match_epid); if (!dev) return NULL; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1ad12afc6ba0..1771b2456bfa 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -502,7 +502,7 @@ struct spi_master *spi_busnum_to_master(u16 bus_num) struct device *dev; struct spi_master *master = NULL; - dev = class_find_device(&spi_master_class, &bus_num, + dev = class_find_device(&spi_master_class, NULL, &bus_num, __spi_master_match); if (dev) master = container_of(dev, struct spi_master, dev); diff --git a/include/linux/device.h b/include/linux/device.h index 6d5b351b29c9..c1f72984875f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -213,7 +213,8 @@ extern void class_unregister(struct class *class); extern int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); -extern struct device *class_find_device(struct class *class, void *data, +extern struct device *class_find_device(struct class *class, + struct device *start, void *data, int (*match)(struct device *, void *)); struct class_attribute { -- cgit v1.2.3 From 7c71448b8aa80123fc521563d5f7c63a099d97ab Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2008 18:17:41 -0500 Subject: class: move driver core specific parts to a private structure This moves the portions of struct class that are dynamic (kobject and lock and lists) out of the main structure and into a dynamic, private, structure. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 27 ++++++++++++++++ drivers/base/class.c | 87 ++++++++++++++++++++++++++++---------------------- drivers/base/core.c | 57 +++++++++++++++++---------------- include/linux/device.h | 7 ++-- 4 files changed, 107 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c9ae43e2219..0ec372a67762 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -36,6 +36,33 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) + +/** + * struct class_private - structure to hold the private to the driver core portions of the class structure. + * + * @subsys - the struct kset that defines this class. This is the main kobject + * @children - list of class_devices associated with this class + * @devices - list of devices associated with this class + * @interfaces - list of class_interfaces associated with this class + * @class_dirs - + * @sem - semaphore to protect the children, devices, and interfaces lists. + * @class - pointer back to the struct class that this structure is associated + * with. + * + * This structure is the one that is the actual kobject allowing struct + * class to be statically allocated safely. Nothing outside of the driver + * core should ever touch these fields. + */ +struct class_private { + struct kset subsys; + struct list_head devices; + struct list_head interfaces; + struct kset class_dirs; + struct semaphore sem; + struct class *class; +}; +#define to_class(obj) container_of(obj, struct class_private, subsys.kobj) + /* initialisation functions */ extern int devices_init(void); extern int buses_init(void); diff --git a/drivers/base/class.c b/drivers/base/class.c index 3918d0e432d4..06f09c929a91 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -21,17 +21,16 @@ #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) -#define to_class(obj) container_of(obj, struct class, subsys.kobj) static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct class_attribute *class_attr = to_class_attr(attr); - struct class *dc = to_class(kobj); + struct class_private *cp = to_class(kobj); ssize_t ret = -EIO; if (class_attr->show) - ret = class_attr->show(dc, buf); + ret = class_attr->show(cp->class, buf); return ret; } @@ -39,17 +38,18 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct class_attribute *class_attr = to_class_attr(attr); - struct class *dc = to_class(kobj); + struct class_private *cp = to_class(kobj); ssize_t ret = -EIO; if (class_attr->store) - ret = class_attr->store(dc, buf, count); + ret = class_attr->store(cp->class, buf, count); return ret; } static void class_release(struct kobject *kobj) { - struct class *class = to_class(kobj); + struct class_private *cp = to_class(kobj); + struct class *class = cp->class; pr_debug("class '%s': release.\n", class->name); @@ -78,7 +78,7 @@ int class_create_file(struct class *cls, const struct class_attribute *attr) { int error; if (cls) - error = sysfs_create_file(&cls->subsys.kobj, &attr->attr); + error = sysfs_create_file(&cls->p->subsys.kobj, &attr->attr); else error = -EINVAL; return error; @@ -87,21 +87,20 @@ int class_create_file(struct class *cls, const struct class_attribute *attr) void class_remove_file(struct class *cls, const struct class_attribute *attr) { if (cls) - sysfs_remove_file(&cls->subsys.kobj, &attr->attr); + sysfs_remove_file(&cls->p->subsys.kobj, &attr->attr); } static struct class *class_get(struct class *cls) { if (cls) - return container_of(kset_get(&cls->subsys), - struct class, subsys); - return NULL; + kset_get(&cls->p->subsys); + return cls; } static void class_put(struct class *cls) { if (cls) - kset_put(&cls->subsys); + kset_put(&cls->p->subsys); } static int add_class_attrs(struct class *cls) @@ -136,17 +135,23 @@ static void remove_class_attrs(struct class *cls) int class_register(struct class *cls) { + struct class_private *cp; int error; pr_debug("device class '%s': registering\n", cls->name); - INIT_LIST_HEAD(&cls->devices); - INIT_LIST_HEAD(&cls->interfaces); - kset_init(&cls->class_dirs); - init_MUTEX(&cls->sem); - error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name); - if (error) + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + INIT_LIST_HEAD(&cp->devices); + INIT_LIST_HEAD(&cp->interfaces); + kset_init(&cp->class_dirs); + init_MUTEX(&cp->sem); + error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name); + if (error) { + kfree(cp); return error; + } /* set the default /sys/dev directory for devices of this class */ if (!cls->dev_kobj) @@ -155,17 +160,21 @@ int class_register(struct class *cls) #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (cls != &block_class) - cls->subsys.kobj.kset = class_kset; + cp->subsys.kobj.kset = class_kset; #else - cls->subsys.kobj.kset = class_kset; + cp->subsys.kobj.kset = class_kset; #endif - cls->subsys.kobj.ktype = &class_ktype; + cp->subsys.kobj.ktype = &class_ktype; + cp->class = cls; + cls->p = cp; - error = kset_register(&cls->subsys); - if (!error) { - error = add_class_attrs(class_get(cls)); - class_put(cls); + error = kset_register(&cp->subsys); + if (error) { + kfree(cp); + return error; } + error = add_class_attrs(class_get(cls)); + class_put(cls); return error; } @@ -173,7 +182,7 @@ void class_unregister(struct class *cls) { pr_debug("device class '%s': unregistering\n", cls->name); remove_class_attrs(cls); - kset_unregister(&cls->subsys); + kset_unregister(&cls->p->subsys); } static void class_create_release(struct class *cls) @@ -280,8 +289,8 @@ int class_for_each_device(struct class *class, struct device *start, if (!class) return -EINVAL; - down(&class->sem); - list_for_each_entry(dev, &class->devices, node) { + down(&class->p->sem); + list_for_each_entry(dev, &class->p->devices, node) { if (start) { if (start == dev) start = NULL; @@ -293,7 +302,7 @@ int class_for_each_device(struct class *class, struct device *start, if (error) break; } - up(&class->sem); + up(&class->p->sem); return error; } @@ -330,8 +339,8 @@ struct device *class_find_device(struct class *class, struct device *start, if (!class) return NULL; - down(&class->sem); - list_for_each_entry(dev, &class->devices, node) { + down(&class->p->sem); + list_for_each_entry(dev, &class->p->devices, node) { if (start) { if (start == dev) start = NULL; @@ -344,7 +353,7 @@ struct device *class_find_device(struct class *class, struct device *start, } else put_device(dev); } - up(&class->sem); + up(&class->p->sem); return found ? dev : NULL; } @@ -362,13 +371,13 @@ int class_interface_register(struct class_interface *class_intf) if (!parent) return -EINVAL; - down(&parent->sem); - list_add_tail(&class_intf->node, &parent->interfaces); + down(&parent->p->sem); + list_add_tail(&class_intf->node, &parent->p->interfaces); if (class_intf->add_dev) { - list_for_each_entry(dev, &parent->devices, node) + list_for_each_entry(dev, &parent->p->devices, node) class_intf->add_dev(dev, class_intf); } - up(&parent->sem); + up(&parent->p->sem); return 0; } @@ -381,13 +390,13 @@ void class_interface_unregister(struct class_interface *class_intf) if (!parent) return; - down(&parent->sem); + down(&parent->p->sem); list_del_init(&class_intf->node); if (class_intf->remove_dev) { - list_for_each_entry(dev, &parent->devices, node) + list_for_each_entry(dev, &parent->p->devices, node) class_intf->remove_dev(dev, class_intf); } - up(&parent->sem); + up(&parent->p->sem); class_put(parent); } diff --git a/drivers/base/core.c b/drivers/base/core.c index 9f05de6f80b5..64c150b5a883 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -551,7 +551,7 @@ static struct kobject *get_device_parent(struct device *dev, { /* class devices without a parent live in /sys/class// */ if (dev->class && (!parent || parent->class != dev->class)) - return &dev->class->subsys.kobj; + return &dev->class->p->subsys.kobj; /* all other devices keep their parent */ else if (parent) return &parent->kobj; @@ -597,13 +597,13 @@ static struct kobject *get_device_parent(struct device *dev, parent_kobj = &parent->kobj; /* find our class-directory at the parent and reference it */ - spin_lock(&dev->class->class_dirs.list_lock); - list_for_each_entry(k, &dev->class->class_dirs.list, entry) + spin_lock(&dev->class->p->class_dirs.list_lock); + list_for_each_entry(k, &dev->class->p->class_dirs.list, entry) if (k->parent == parent_kobj) { kobj = kobject_get(k); break; } - spin_unlock(&dev->class->class_dirs.list_lock); + spin_unlock(&dev->class->p->class_dirs.list_lock); if (kobj) return kobj; @@ -611,7 +611,7 @@ static struct kobject *get_device_parent(struct device *dev, k = kobject_create(); if (!k) return NULL; - k->kset = &dev->class->class_dirs; + k->kset = &dev->class->p->class_dirs; retval = kobject_add(k, parent_kobj, "%s", dev->class->name); if (retval < 0) { kobject_put(k); @@ -630,7 +630,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) { /* see if we live in a "glue" directory */ if (!glue_dir || !dev->class || - glue_dir->kset != &dev->class->class_dirs) + glue_dir->kset != &dev->class->p->class_dirs) return; kobject_put(glue_dir); @@ -657,17 +657,17 @@ static int device_add_class_symlinks(struct device *dev) if (!dev->class) return 0; - error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, + error = sysfs_create_link(&dev->kobj, &dev->class->p->subsys.kobj, "subsystem"); if (error) goto out; #ifdef CONFIG_SYSFS_DEPRECATED /* stacked class devices need a symlink in the class directory */ - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->subsys.kobj && device_is_not_partition(dev)) { - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link(&dev->class->p->subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out_subsys; } @@ -704,12 +704,12 @@ out_device: if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); out_busid: - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #else /* link in the class directory pointing to the device */ - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, + error = sysfs_create_link(&dev->class->p->subsys.kobj, &dev->kobj, dev->bus_id); if (error) goto out_subsys; @@ -723,7 +723,7 @@ out_busid: return 0; out_busid: - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #endif out_subsys: @@ -749,14 +749,14 @@ static void device_remove_class_symlinks(struct device *dev) sysfs_remove_link(&dev->kobj, "device"); } - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #else if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #endif sysfs_remove_link(&dev->kobj, "subsystem"); @@ -904,15 +904,16 @@ int device_add(struct device *dev) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { - down(&dev->class->sem); + down(&dev->class->p->sem); /* tie the class to the device */ - list_add_tail(&dev->node, &dev->class->devices); + list_add_tail(&dev->node, &dev->class->p->devices); /* notify any interfaces that the device is here */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) + list_for_each_entry(class_intf, &dev->class->p->interfaces, + node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); - up(&dev->class->sem); + up(&dev->class->p->sem); } Done: put_device(dev); @@ -1013,14 +1014,15 @@ void device_del(struct device *dev) if (dev->class) { device_remove_class_symlinks(dev); - down(&dev->class->sem); + down(&dev->class->p->sem); /* notify any interfaces that the device is now gone */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) + list_for_each_entry(class_intf, &dev->class->p->interfaces, + node) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ list_del_init(&dev->node); - up(&dev->class->sem); + up(&dev->class->p->sem); } device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); @@ -1348,11 +1350,12 @@ int device_rename(struct device *dev, char *new_name) } #else if (dev->class) { - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link(&dev->class->p->subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out; - sysfs_remove_link(&dev->class->subsys.kobj, old_device_name); + sysfs_remove_link(&dev->class->p->subsys.kobj, + old_device_name); } #endif diff --git a/include/linux/device.h b/include/linux/device.h index c1f72984875f..b0556082179b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -35,6 +35,7 @@ struct device; struct device_driver; struct driver_private; struct class; +struct class_private; struct bus_type; struct bus_type_private; @@ -186,11 +187,6 @@ struct class { const char *name; struct module *owner; - struct kset subsys; - struct list_head devices; - struct list_head interfaces; - struct kset class_dirs; - struct semaphore sem; /* locks children, devices, interfaces */ struct class_attribute *class_attrs; struct device_attribute *dev_attrs; struct kobject *dev_kobj; @@ -204,6 +200,7 @@ struct class { int (*resume)(struct device *dev); struct pm_ops *pm; + struct class_private *p; }; extern struct kobject *sysfs_dev_block_kobj; -- cgit v1.2.3 From d2a3b9146e4f40c2e872d7567c996ef95083d802 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 28 May 2008 09:28:39 -0700 Subject: class: add lockdep infrastructure This adds the infrastructure to properly handle lockdep issues when the internal class semaphore is changed to a mutex. Matthew wrote the original patch, and Greg fixed it up to work properly with the class_create() function. From: Matthew Wilcox Cc: Kay Sievers Cc: Dave Young Cc: Andrew Morton Cc: James Bottomley Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 11 ++++++----- include/linux/device.h | 25 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index d24d21114ccb..89000566690c 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -134,7 +134,7 @@ static void remove_class_attrs(struct class *cls) } } -int class_register(struct class *cls) +int __class_register(struct class *cls, struct lock_class_key *key) { struct class_private *cp; int error; @@ -178,6 +178,7 @@ int class_register(struct class *cls) class_put(cls); return error; } +EXPORT_SYMBOL_GPL(__class_register); void class_unregister(struct class *cls) { @@ -203,7 +204,8 @@ static void class_create_release(struct class *cls) * Note, the pointer created here is to be destroyed when finished by * making a call to class_destroy(). */ -struct class *class_create(struct module *owner, const char *name) +struct class *__class_create(struct module *owner, const char *name, + struct lock_class_key *key) { struct class *cls; int retval; @@ -218,7 +220,7 @@ struct class *class_create(struct module *owner, const char *name) cls->owner = owner; cls->class_release = class_create_release; - retval = class_register(cls); + retval = __class_register(cls, key); if (retval) goto error; @@ -228,6 +230,7 @@ error: kfree(cls); return ERR_PTR(retval); } +EXPORT_SYMBOL_GPL(__class_create); /** * class_destroy - destroys a struct class structure @@ -412,9 +415,7 @@ int __init classes_init(void) EXPORT_SYMBOL_GPL(class_create_file); EXPORT_SYMBOL_GPL(class_remove_file); -EXPORT_SYMBOL_GPL(class_register); EXPORT_SYMBOL_GPL(class_unregister); -EXPORT_SYMBOL_GPL(class_create); EXPORT_SYMBOL_GPL(class_destroy); EXPORT_SYMBOL_GPL(class_interface_register); diff --git a/include/linux/device.h b/include/linux/device.h index b0556082179b..0e1d24c2ed41 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -205,8 +206,18 @@ struct class { extern struct kobject *sysfs_dev_block_kobj; extern struct kobject *sysfs_dev_char_kobj; -extern int __must_check class_register(struct class *class); +extern int __must_check __class_register(struct class *class, + struct lock_class_key *key); extern void class_unregister(struct class *class); + +/* This is a #define to keep the compiler from merging different + * instances of the __key variable */ +#define class_register(class) \ +({ \ + static struct lock_class_key __key; \ + __class_register(class, &__key); \ +}) + extern int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); @@ -239,9 +250,19 @@ struct class_interface { extern int __must_check class_interface_register(struct class_interface *); extern void class_interface_unregister(struct class_interface *); -extern struct class *class_create(struct module *owner, const char *name); +extern struct class * __must_check __class_create(struct module *owner, + const char *name, + struct lock_class_key *key); extern void class_destroy(struct class *cls); +/* This is a #define to keep the compiler from merging different + * instances of the __key variable */ +#define class_create(owner, name) \ +({ \ + static struct lock_class_key __key; \ + __class_create(owner, name, &__key); \ +}) + /* * The type of device, "struct device" is embedded in. A class * or bus can contain devices of different types -- cgit v1.2.3 From aab0de245150c09e61c30962feb16aacde508dc3 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 2 May 2008 06:02:41 +0200 Subject: driver core: remove KOBJ_NAME_LEN define Kobjects do not have a limit in name size since a while, so stop pretending that they do. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- arch/arm/plat-omap/mailbox.c | 2 +- arch/sparc64/kernel/vio.c | 2 +- drivers/message/fusion/mptbase.c | 3 ++- drivers/message/fusion/mptbase.h | 4 ++-- drivers/message/fusion/mptfc.c | 4 ++-- drivers/pci/hotplug/acpiphp.h | 4 ++-- drivers/scsi/hosts.c | 4 ++-- drivers/scsi/scsi_transport_fc.c | 9 +++++---- drivers/scsi/scsi_transport_iscsi.c | 4 ++-- fs/partitions/check.c | 2 +- include/linux/device.h | 3 +-- include/linux/kobject.h | 1 - include/linux/spi/spi.h | 2 +- include/scsi/scsi_host.h | 2 +- include/scsi/scsi_transport_fc.h | 4 ++-- include/scsi/scsi_transport_iscsi.h | 2 +- 16 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index 6f33f58bca45..848fdcafaa28 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c @@ -334,7 +334,7 @@ static int omap_mbox_init(struct omap_mbox *mbox) } mbox->dev.class = &omap_mbox_class; - strlcpy(mbox->dev.bus_id, mbox->name, KOBJ_NAME_LEN); + strlcpy(mbox->dev.bus_id, mbox->name, BUS_ID_SIZE); dev_set_drvdata(&mbox->dev, mbox); ret = device_register(&mbox->dev); diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index e78b3517940b..ecbb8b618b8c 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c @@ -224,7 +224,7 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, if (!strcmp(type, "domain-services-port")) bus_id_name = "ds"; - if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) { + if (strlen(bus_id_name) >= BUS_ID_SIZE - 4) { printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n", bus_id_name); return NULL; diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 75e599b85b64..34402c47027e 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -1670,7 +1670,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work); spin_lock_init(&ioc->fault_reset_work_lock); - snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id); + snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name), + "mpt_poll_%d", ioc->id); ioc->reset_work_q = create_singlethread_workqueue(ioc->reset_work_q_name); if (!ioc->reset_work_q) { diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 6adab648dbb9..dff048cfa101 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -707,12 +707,12 @@ typedef struct _MPT_ADAPTER u8 fc_link_speed[2]; spinlock_t fc_rescan_work_lock; struct work_struct fc_rescan_work; - char fc_rescan_work_q_name[KOBJ_NAME_LEN]; + char fc_rescan_work_q_name[20]; struct workqueue_struct *fc_rescan_work_q; struct scsi_cmnd **ScsiLookup; spinlock_t scsi_lookup_lock; - char reset_work_q_name[KOBJ_NAME_LEN]; + char reset_work_q_name[20]; struct workqueue_struct *reset_work_q; struct delayed_work fault_reset_work; spinlock_t fault_reset_work_lock; diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index fc31ca6829d8..b36cae9ec6db 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -1326,8 +1326,8 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* initialize workqueue */ - snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d", - sh->host_no); + snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name), + "mptfc_wq_%d", sh->host_no); ioc->fc_rescan_work_q = create_singlethread_workqueue(ioc->fc_rescan_work_q_name); if (!ioc->fc_rescan_work_q) diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index eecf7cbf4139..5a58b075dd8d 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -36,7 +36,7 @@ #define _ACPIPHP_H #include -#include /* for KOBJ_NAME_LEN */ +#include #include #include @@ -51,7 +51,7 @@ #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) /* name size which is used for entries in pcihpfs */ -#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */ +#define SLOT_NAME_SIZE 20 /* {_SUN} */ struct acpiphp_bridge; struct acpiphp_slot; diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 78dad28b70d5..fed0b02ebc1d 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -232,8 +232,8 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) } if (shost->transportt->create_work_queue) { - snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d", - shost->host_no); + snprintf(shost->work_q_name, sizeof(shost->work_q_name), + "scsi_wq_%d", shost->host_no); shost->work_q = create_singlethread_workqueue( shost->work_q_name); if (!shost->work_q) { diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 5fd64e70029d..a272b9a2c869 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -417,15 +417,16 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, fc_host->next_vport_number = 0; fc_host->npiv_vports_inuse = 0; - snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d", - shost->host_no); + snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name), + "fc_wq_%d", shost->host_no); fc_host->work_q = create_singlethread_workqueue( fc_host->work_q_name); if (!fc_host->work_q) return -ENOMEM; - snprintf(fc_host->devloss_work_q_name, KOBJ_NAME_LEN, "fc_dl_%d", - shost->host_no); + snprintf(fc_host->devloss_work_q_name, + sizeof(fc_host->devloss_work_q_name), + "fc_dl_%d", shost->host_no); fc_host->devloss_work_q = create_singlethread_workqueue( fc_host->devloss_work_q_name); if (!fc_host->devloss_work_q) { diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 06748f318cd5..043c3921164f 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -247,8 +247,8 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev, atomic_set(&ihost->nr_scans, 0); mutex_init(&ihost->mutex); - snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d", - shost->host_no); + snprintf(ihost->scan_workq_name, sizeof(ihost->scan_workq_name), + "iscsi_scan_%d", shost->host_no); ihost->scan_workq = create_singlethread_workqueue( ihost->scan_workq_name); if (!ihost->scan_workq) diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 6149e4b58c88..efef715135d3 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -401,7 +401,7 @@ void register_disk(struct gendisk *disk) disk->dev.parent = disk->driverfs_dev; disk->dev.devt = MKDEV(disk->major, disk->first_minor); - strlcpy(disk->dev.bus_id, disk->disk_name, KOBJ_NAME_LEN); + strlcpy(disk->dev.bus_id, disk->disk_name, BUS_ID_SIZE); /* ewww... some of these buggers have / in the name... */ s = strchr(disk->dev.bus_id, '/'); if (s) diff --git a/include/linux/device.h b/include/linux/device.h index 0e1d24c2ed41..fba1bb0d1758 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -29,8 +29,7 @@ /* DEVICE_NAME_HALF is really less than half to accommodate slop */ #define DEVICE_NAME_HALF __stringify(20) #define DEVICE_ID_SIZE 32 -#define BUS_ID_SIZE KOBJ_NAME_LEN - +#define BUS_ID_SIZE 20 struct device; struct device_driver; diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 39e709f88aa0..d542faa6cb47 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -26,7 +26,6 @@ #include #include -#define KOBJ_NAME_LEN 20 #define UEVENT_HELPER_PATH_LEN 256 #define UEVENT_NUM_ENVP 32 /* number of env pointers */ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 387e428f1cdf..b9a76c972084 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -733,7 +733,7 @@ struct spi_board_info { * controller_data goes to spi_device.controller_data, * irq is copied too */ - char modalias[KOBJ_NAME_LEN]; + char modalias[32]; const void *platform_data; void *controller_data; int irq; diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 1834fdfe82a7..a594bac4a77d 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -623,7 +623,7 @@ struct Scsi_Host { /* * Optional work queue to be utilized by the transport */ - char work_q_name[KOBJ_NAME_LEN]; + char work_q_name[20]; struct workqueue_struct *work_q; /* diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 06f72bab9df0..878373c32ef7 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -489,9 +489,9 @@ struct fc_host_attrs { u16 npiv_vports_inuse; /* work queues for rport state manipulation */ - char work_q_name[KOBJ_NAME_LEN]; + char work_q_name[20]; struct workqueue_struct *work_q; - char devloss_work_q_name[KOBJ_NAME_LEN]; + char devloss_work_q_name[20]; struct workqueue_struct *devloss_work_q; }; diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index f5444e033cc9..8b6c91df4c7a 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -198,7 +198,7 @@ struct iscsi_cls_host { atomic_t nr_scans; struct mutex mutex; struct workqueue_struct *scan_workq; - char scan_workq_name[KOBJ_NAME_LEN]; + char scan_workq_name[20]; }; extern void iscsi_host_for_each_session(struct Scsi_Host *shost, -- cgit v1.2.3 From ca52a49846f1451163c0dc14c40cd06ff808df3e Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 2 May 2008 06:02:41 +0200 Subject: driver core: remove DEVICE_NAME_SIZE define There is no such thing as a "device name size" in the driver core, so remove the define and fix up any users of this odd define in the rest of the kernel. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/eisa/Makefile | 2 +- drivers/eisa/eisa-bus.c | 4 ++-- drivers/mca/mca-bus.c | 2 +- drivers/video/aty/aty128fb.c | 2 +- drivers/video/aty/radeonfb.h | 2 +- include/linux/device.h | 3 --- include/linux/eisa.h | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/eisa/Makefile b/drivers/eisa/Makefile index 70abf93fe6b0..5369ce957c6d 100644 --- a/drivers/eisa/Makefile +++ b/drivers/eisa/Makefile @@ -9,7 +9,7 @@ obj-${CONFIG_EISA_VIRTUAL_ROOT} += virtual_root.o # Ugly hack to get DEVICE_NAME_SIZE value... -DEVICE_NAME_SIZE =$(shell awk '$$1=="\#define" && $$2=="DEVICE_NAME_SIZE" {print $$3-1}' $(srctree)/include/linux/device.h) +DEVICE_NAME_SIZE = 50 $(obj)/eisa-bus.o: $(obj)/devlist.h diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 65dcf0432653..c950bf8606d9 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -22,7 +22,7 @@ struct eisa_device_info { struct eisa_device_id id; - char name[DEVICE_NAME_SIZE]; + char name[50]; }; #ifdef CONFIG_EISA_NAMES @@ -63,7 +63,7 @@ static void __init eisa_name_device (struct eisa_device *edev) if (!strcmp (edev->id.sig, eisa_table[i].id.sig)) { strlcpy (edev->pretty_name, eisa_table[i].name, - DEVICE_NAME_SIZE); + sizeof(edev->pretty_name)); return; } } diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c index 67b8e9453b19..ef2dbfe74714 100644 --- a/drivers/mca/mca-bus.c +++ b/drivers/mca/mca-bus.c @@ -40,7 +40,7 @@ static struct mca_bus *mca_root_busses[MAX_MCA_BUSSES]; struct mca_device_info { short pos_id; /* the 2 byte pos id for this card */ - char name[DEVICE_NAME_SIZE]; + char name[50]; }; static int mca_bus_match (struct device *dev, struct device_driver *drv) diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 24ee96c4e9e9..07b6addbb3c1 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1872,7 +1872,7 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i struct fb_info *info = pci_get_drvdata(pdev); struct aty128fb_par *par = info->par; struct fb_var_screeninfo var; - char video_card[DEVICE_NAME_SIZE]; + char video_card[50]; u8 chip_rev; u32 dac; diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index c347e38cd0b0..ccbfffd12805 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -289,7 +289,7 @@ struct radeonfb_info { struct radeon_regs state; struct radeon_regs init_state; - char name[DEVICE_NAME_SIZE]; + char name[50]; unsigned long mmio_base_phys; unsigned long fb_base_phys; diff --git a/include/linux/device.h b/include/linux/device.h index fba1bb0d1758..894fdb2e483a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -25,9 +25,6 @@ #include #include -#define DEVICE_NAME_SIZE 50 -/* DEVICE_NAME_HALF is really less than half to accommodate slop */ -#define DEVICE_NAME_HALF __stringify(20) #define DEVICE_ID_SIZE 32 #define BUS_ID_SIZE 20 diff --git a/include/linux/eisa.h b/include/linux/eisa.h index fe806b6f030d..e61c0be2a459 100644 --- a/include/linux/eisa.h +++ b/include/linux/eisa.h @@ -40,7 +40,7 @@ struct eisa_device { u64 dma_mask; struct device dev; /* generic device */ #ifdef CONFIG_EISA_NAMES - char pretty_name[DEVICE_NAME_SIZE]; + char pretty_name[50]; #endif }; -- cgit v1.2.3 From b98cb4b7fe0e83238501b48489e46b3e0dce9aaf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 2 May 2008 06:02:41 +0200 Subject: driver core: remove DEVICE_ID_SIZE define There is no such thing as a "device id size" in the driver core, so remove the define and fix up any users of this odd define in the rest of the kernel. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/memstick/host/jmb38x_ms.c | 4 ++-- include/linux/device.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index a054668eda16..4e3bfbcdf155 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -51,7 +51,7 @@ struct jmb38x_ms_host { void __iomem *addr; spinlock_t lock; int id; - char host_id[DEVICE_ID_SIZE]; + char host_id[32]; int irq; unsigned int block_pos; unsigned long timeout_jiffies; @@ -781,7 +781,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) spin_lock_init(&host->lock); host->id = cnt; - snprintf(host->host_id, DEVICE_ID_SIZE, DRIVER_NAME ":slot%d", + snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d", host->id); host->irq = jm->pdev->irq; host->timeout_jiffies = msecs_to_jiffies(1000); diff --git a/include/linux/device.h b/include/linux/device.h index 894fdb2e483a..d24a47f80f9c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -25,7 +25,6 @@ #include #include -#define DEVICE_ID_SIZE 32 #define BUS_ID_SIZE 20 struct device; -- cgit v1.2.3 From 328a14e70e7f46997cb50d4258dd93d5377f98c6 Mon Sep 17 00:00:00 2001 From: "Hans J. Koch" Date: Fri, 23 May 2008 13:50:14 +0200 Subject: UIO: Add write function to allow irq masking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it is necessary to enable/disable the interrupt of a UIO device from the userspace part of the driver. With this patch, the UIO kernel driver can implement an "irqcontrol()" function that does this. Userspace can write an s32 value to /dev/uioX (usually 0 or 1 to turn the irq off or on). The UIO core will then call the driver's irqcontrol function. Signed-off-by: Hans J. Koch Acked-by: Uwe Kleine-König Acked-by: Magnus Damm Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 40 +++++++++++++++++++++++++++++++++++- drivers/uio/uio.c | 26 +++++++++++++++++++++++ include/linux/uio_driver.h | 2 ++ 3 files changed, 67 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index fdd7f4f887b7..c4d187313963 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -29,6 +29,12 @@ + + 0.5 + 2008-05-22 + hjk + Added description of write() function. + 0.4 2007-11-26 @@ -64,7 +70,7 @@ Copyright and License - Copyright (c) 2006 by Hans-Jürgen Koch. + Copyright (c) 2006-2008 by Hans-Jürgen Koch. This documentation is Free Software licensed under the terms of the GPL version 2. @@ -189,6 +195,30 @@ interested in translating it, please email me represents the total interrupt count. You can use this number to figure out if you missed some interrupts. + + For some hardware that has more than one interrupt source internally, + but not separate IRQ mask and status registers, there might be + situations where userspace cannot determine what the interrupt source + was if the kernel handler disables them by writing to the chip's IRQ + register. In such a case, the kernel has to disable the IRQ completely + to leave the chip's register untouched. Now the userspace part can + determine the cause of the interrupt, but it cannot re-enable + interrupts. Another cornercase is chips where re-enabling interrupts + is a read-modify-write operation to a combined IRQ status/acknowledge + register. This would be racy if a new interrupt occurred + simultaneously. + + + To address these problems, UIO also implements a write() function. It + is normally not used and can be ignored for hardware that has only a + single interrupt source or has separate IRQ mask and status registers. + If you need it, however, a write to /dev/uioX + will call the irqcontrol() function implemented + by the driver. You have to write a 32-bit value that is usually either + 0 or 1 to disable or enable interrupts. If a driver does not implement + irqcontrol(), write() will + return with -ENOSYS. + To handle interrupts properly, your custom kernel module can @@ -362,6 +392,14 @@ device is actually used. open(), you will probably also want a custom release() function. + + +int (*irqcontrol)(struct uio_info *info, s32 irq_on) +: Optional. If you need to be able to enable or disable +interrupts from userspace by writing to /dev/uioX, +you can implement this function. The parameter irq_on +will be 0 to disable interrupts and 1 to enable them. + diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 5a7ca2e6094d..3a6934bf7131 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -427,6 +427,31 @@ static ssize_t uio_read(struct file *filep, char __user *buf, return retval; } +static ssize_t uio_write(struct file *filep, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + ssize_t retval; + s32 irq_on; + + if (idev->info->irq == UIO_IRQ_NONE) + return -EIO; + + if (count != sizeof(s32)) + return -EINVAL; + + if (!idev->info->irqcontrol) + return -ENOSYS; + + if (copy_from_user(&irq_on, buf, count)) + return -EFAULT; + + retval = idev->info->irqcontrol(idev->info, irq_on); + + return retval ? retval : sizeof(s32); +} + static int uio_find_mem_index(struct vm_area_struct *vma) { int mi; @@ -546,6 +571,7 @@ static const struct file_operations uio_fops = { .open = uio_open, .release = uio_release, .read = uio_read, + .write = uio_write, .mmap = uio_mmap, .poll = uio_poll, .fasync = uio_fasync, diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 973386d439da..cf65e964102b 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -53,6 +53,7 @@ struct uio_device; * @mmap: mmap operation for this uio device * @open: open operation for this uio device * @release: release operation for this uio device + * @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX */ struct uio_info { struct uio_device *uio_dev; @@ -66,6 +67,7 @@ struct uio_info { int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); int (*open)(struct uio_info *info, struct inode *inode); int (*release)(struct uio_info *info, struct inode *inode); + int (*irqcontrol)(struct uio_info *info, s32 irq_on); }; extern int __must_check -- cgit v1.2.3 From 6d8333c24d41637f0f847f6e17032189dae08c02 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 10 Jun 2008 09:14:48 +0200 Subject: UIO: minor style and comment fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Hans J. Koch --- include/linux/uio_driver.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index cf65e964102b..cdf338d94b7f 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -36,7 +36,7 @@ struct uio_mem { struct uio_map *map; }; -#define MAX_UIO_MAPS 5 +#define MAX_UIO_MAPS 5 struct uio_device; @@ -82,11 +82,11 @@ static inline int __must_check extern void uio_unregister_device(struct uio_info *info); extern void uio_event_notify(struct uio_info *info); -/* defines for uio_device->irq */ +/* defines for uio_info->irq */ #define UIO_IRQ_CUSTOM -1 #define UIO_IRQ_NONE -2 -/* defines for uio_device->memtype */ +/* defines for uio_mem->memtype */ #define UIO_MEM_NONE 0 #define UIO_MEM_PHYS 1 #define UIO_MEM_LOGICAL 2 -- cgit v1.2.3 From a231934bdf086a4fefc0df06e669499125a9db6f Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Mon, 2 Jun 2008 11:07:25 +0100 Subject: kobject: reorder kobject to save space on 64 bit builds reorder kobject to save space on 64 bit builds. shrinks from 72 to 64 bytes & moves allocated kobject to a smaller slab. Signed-off-by: Richard Kennedy Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index d542faa6cb47..60f0d418ae32 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -58,12 +58,12 @@ enum kobject_action { struct kobject { const char *name; - struct kref kref; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct sysfs_dirent *sd; + struct kref kref; unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; -- cgit v1.2.3 From 9505e6375640fc61d92d36c8e9f25a6a218f3f57 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 1 Jul 2008 15:14:51 +0200 Subject: debugfs: Implement debugfs_remove_recursive() debugfs_remove_recursive() will remove a dentry and all its children. Drivers can use this to zap their whole debugfs tree so that they don't need to keep track of every single debugfs dentry they created. It may fail to remove the whole tree in certain cases: sh-3.2# rmmod atmel-mci < /sys/kernel/debug/mmc0/ios/clock mmc0: card b368 removed atmel_mci atmel_mci.0: Lost dma0chan1, falling back to PIO sh-3.2# ls /sys/kernel/debug/mmc0/ ios But I'm not sure if that case can be handled in any sane manner. Signed-off-by: Haavard Skinnemoen Cc: Pierre Ossman Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/inode.c | 114 ++++++++++++++++++++++++++++++++++++++++-------- include/linux/debugfs.h | 4 ++ 2 files changed, 100 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index e9602d85c11d..08e28c9bb416 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -309,6 +309,31 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, } EXPORT_SYMBOL_GPL(debugfs_create_symlink); +static void __debugfs_remove(struct dentry *dentry, struct dentry *parent) +{ + int ret = 0; + + if (debugfs_positive(dentry)) { + if (dentry->d_inode) { + dget(dentry); + switch (dentry->d_inode->i_mode & S_IFMT) { + case S_IFDIR: + ret = simple_rmdir(parent->d_inode, dentry); + break; + case S_IFLNK: + kfree(dentry->d_inode->i_private); + /* fall through */ + default: + simple_unlink(parent->d_inode, dentry); + break; + } + if (!ret) + d_delete(dentry); + dput(dentry); + } + } +} + /** * debugfs_remove - removes a file or directory from the debugfs filesystem * @dentry: a pointer to a the dentry of the file or directory to be @@ -325,7 +350,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_symlink); void debugfs_remove(struct dentry *dentry) { struct dentry *parent; - int ret = 0; if (!dentry) return; @@ -335,29 +359,83 @@ void debugfs_remove(struct dentry *dentry) return; mutex_lock(&parent->d_inode->i_mutex); - if (debugfs_positive(dentry)) { - if (dentry->d_inode) { - dget(dentry); - switch (dentry->d_inode->i_mode & S_IFMT) { - case S_IFDIR: - ret = simple_rmdir(parent->d_inode, dentry); - break; - case S_IFLNK: - kfree(dentry->d_inode->i_private); - /* fall through */ - default: - simple_unlink(parent->d_inode, dentry); + __debugfs_remove(dentry, parent); + mutex_unlock(&parent->d_inode->i_mutex); + simple_release_fs(&debugfs_mount, &debugfs_mount_count); +} +EXPORT_SYMBOL_GPL(debugfs_remove); + +/** + * debugfs_remove_recursive - recursively removes a directory + * @dentry: a pointer to a the dentry of the directory to be removed. + * + * This function recursively removes a directory tree in debugfs that + * was previously created with a call to another debugfs function + * (like debugfs_create_file() or variants thereof.) + * + * This function is required to be called in order for the file to be + * removed, no automatic cleanup of files will happen when a module is + * removed, you are responsible here. + */ +void debugfs_remove_recursive(struct dentry *dentry) +{ + struct dentry *child; + struct dentry *parent; + + if (!dentry) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + parent = dentry; + mutex_lock(&parent->d_inode->i_mutex); + + while (1) { + /* + * When all dentries under "parent" has been removed, + * walk up the tree until we reach our starting point. + */ + if (list_empty(&parent->d_subdirs)) { + mutex_unlock(&parent->d_inode->i_mutex); + if (parent == dentry) break; - } - if (!ret) - d_delete(dentry); - dput(dentry); + parent = parent->d_parent; + mutex_lock(&parent->d_inode->i_mutex); + } + child = list_entry(parent->d_subdirs.next, struct dentry, + d_u.d_child); + + /* + * If "child" isn't empty, walk down the tree and + * remove all its descendants first. + */ + if (!list_empty(&child->d_subdirs)) { + mutex_unlock(&parent->d_inode->i_mutex); + parent = child; + mutex_lock(&parent->d_inode->i_mutex); + continue; } + __debugfs_remove(child, parent); + if (parent->d_subdirs.next == &child->d_u.d_child) { + /* + * Avoid infinite loop if we fail to remove + * one dentry. + */ + mutex_unlock(&parent->d_inode->i_mutex); + break; + } + simple_release_fs(&debugfs_mount, &debugfs_mount_count); } + + parent = dentry->d_parent; + mutex_lock(&parent->d_inode->i_mutex); + __debugfs_remove(dentry, parent); mutex_unlock(&parent->d_inode->i_mutex); simple_release_fs(&debugfs_mount, &debugfs_mount_count); } -EXPORT_SYMBOL_GPL(debugfs_remove); +EXPORT_SYMBOL_GPL(debugfs_remove_recursive); /** * debugfs_rename - rename a file/directory in the debugfs filesystem diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 32755cdf68db..e1a6c046cea3 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -44,6 +44,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *dest); void debugfs_remove(struct dentry *dentry); +void debugfs_remove_recursive(struct dentry *dentry); struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, const char *new_name); @@ -101,6 +102,9 @@ static inline struct dentry *debugfs_create_symlink(const char *name, static inline void debugfs_remove(struct dentry *dentry) { } +static inline void debugfs_remove_recursive(struct dentry *dentry) +{ } + static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, char *new_name) { -- cgit v1.2.3 From 36ce6dad6e3cb3f050ed41e0beac0070d2062b25 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 10 Jun 2008 11:09:08 +0200 Subject: driver core: Suppress sysfs warnings for device_rename(). driver core: Suppress sysfs warnings for device_rename(). Renaming network devices to an already existing name is not something we want sysfs to print a scary warning for, since the callers can deal with this correctly. So let's introduce sysfs_create_link_nowarn() which gets rid of the common warning. Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 9 +++++---- fs/sysfs/dir.c | 37 +++++++++++++++++++++++++++++++++++-- fs/sysfs/symlink.c | 41 +++++++++++++++++++++++++++++++++-------- fs/sysfs/sysfs.h | 1 + include/linux/sysfs.h | 10 ++++++++++ 5 files changed, 84 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index c05b1159023e..7d5c63c81a59 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1345,8 +1345,9 @@ int device_rename(struct device *dev, char *new_name) if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { - error = sysfs_create_link(&dev->parent->kobj, - &dev->kobj, new_class_name); + error = sysfs_create_link_nowarn(&dev->parent->kobj, + &dev->kobj, + new_class_name); if (error) goto out; sysfs_remove_link(&dev->parent->kobj, old_class_name); @@ -1354,8 +1355,8 @@ int device_rename(struct device *dev, char *new_name) } #else if (dev->class) { - error = sysfs_create_link(&dev->class->p->class_subsys.kobj, - &dev->kobj, dev->bus_id); + error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out; sysfs_remove_link(&dev->class->p->class_subsys.kobj, diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 8c0e4b92574f..c1a7efb310bf 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -398,7 +398,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, } /** - * sysfs_add_one - add sysfs_dirent to parent + * __sysfs_add_one - add sysfs_dirent to parent without warning * @acxt: addrm context to use * @sd: sysfs_dirent to be added * @@ -417,7 +417,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, * 0 on success, -EEXIST if entry with the given name already * exists. */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) return -EEXIST; @@ -434,6 +434,39 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) return 0; } +/** + * sysfs_add_one - add sysfs_dirent to parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * + * Get @acxt->parent_sd and set sd->s_parent to it and increment + * nlink of parent inode if @sd is a directory and link into the + * children list of the parent. + * + * This function should be called between calls to + * sysfs_addrm_start() and sysfs_addrm_finish() and should be + * passed the same @acxt as passed to sysfs_addrm_start(). + * + * LOCKING: + * Determined by sysfs_addrm_start(). + * + * RETURNS: + * 0 on success, -EEXIST if entry with the given name already + * exists. + */ +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +{ + int ret; + + ret = __sysfs_add_one(acxt, sd); + if (ret == -EEXIST) { + printk(KERN_WARNING "sysfs: duplicate filename '%s' " + "can not be created\n", sd->s_name); + WARN_ON(1); + } + return ret; +} + /** * sysfs_remove_one - remove sysfs_dirent from parent * @acxt: addrm context to use diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 817f5966edca..a3ba217fbe74 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -19,13 +19,8 @@ #include "sysfs.h" -/** - * sysfs_create_link - create symlink between two objects. - * @kobj: object whose directory we're creating the link in. - * @target: object we're pointing to. - * @name: name of the symlink. - */ -int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) +static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, + const char *name, int warn) { struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; @@ -65,7 +60,10 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt, parent_sd); - error = sysfs_add_one(&acxt, sd); + if (warn) + error = sysfs_add_one(&acxt, sd); + else + error = __sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (error) @@ -79,6 +77,33 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char return error; } +/** + * sysfs_create_link - create symlink between two objects. + * @kobj: object whose directory we're creating the link in. + * @target: object we're pointing to. + * @name: name of the symlink. + */ +int sysfs_create_link(struct kobject *kobj, struct kobject *target, + const char *name) +{ + return sysfs_do_create_link(kobj, target, name, 1); +} + +/** + * sysfs_create_link_nowarn - create symlink between two objects. + * @kobj: object whose directory we're creating the link in. + * @target: object we're pointing to. + * @name: name of the symlink. + * + * This function does the same as sysf_create_link(), but it + * doesn't warn if the link already exists. + */ +int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, + const char *name) +{ + return sysfs_do_create_link(kobj, target, name, 0); +} + /** * sysfs_remove_link - remove symlink in object's directory. * @kobj: object we're acting for. diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ce4e15f8aaeb..a5db496f71c7 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -107,6 +107,7 @@ struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); void sysfs_put_active_two(struct sysfs_dirent *sd); void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *parent_sd); +int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 7858eac40aa7..37fa24152bd8 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -101,6 +101,9 @@ void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr); int __must_check sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name); +int __must_check sysfs_create_link_nowarn(struct kobject *kobj, + struct kobject *target, + const char *name); void sysfs_remove_link(struct kobject *kobj, const char *name); int __must_check sysfs_create_group(struct kobject *kobj, @@ -180,6 +183,13 @@ static inline int sysfs_create_link(struct kobject *kobj, return 0; } +static inline int sysfs_create_link_nowarn(struct kobject *kobj, + struct kobject *target, + const char *name) +{ + return 0; +} + static inline void sysfs_remove_link(struct kobject *kobj, const char *name) { } -- cgit v1.2.3 From 4a0b2b4dbe1335b8b9886ba3dc85a145d5d938ed Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jul 2008 18:48:41 +0200 Subject: sysdev: Pass the attribute to the low level sysdev show/store function This allow to dynamically generate attributes and share show/store functions between attributes. Right now most attributes are generated by special macros and lots of duplicated code. With the attribute passed it's instead possible to attach some data to the attribute and then use that in shared low level functions to do different things. I need this for the dynamically generated bank attributes in the x86 machine check code, but it'll allow some further cleanups. I converted all users in tree to the new show/store prototype. It's a single huge patch to avoid unbisectable sections. Runtime tested: x86-32, x86-64 Compiled only: ia64, powerpc Not compile tested/only grep converted: sh, arm, avr32 Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/time.c | 4 ++- arch/avr32/kernel/cpu.c | 38 +++++++++++++++++--------- arch/ia64/kernel/err_inject.c | 22 ++++++++++----- arch/powerpc/kernel/sysfs.c | 15 ++++++++--- arch/powerpc/platforms/cell/cbe_thermal.c | 45 ++++++++++++++++++++----------- arch/powerpc/platforms/cell/spu_base.c | 3 ++- arch/s390/kernel/smp.c | 36 ++++++++++++++++--------- arch/s390/kernel/time.c | 35 ++++++++++++++++-------- arch/sh/drivers/dma/dma-sysfs.c | 15 ++++++++--- arch/sparc64/kernel/sysfs.c | 16 +++++++---- arch/x86/kernel/cpu/mcheck/mce_64.c | 14 +++++++--- arch/x86/kernel/cpu/mcheck/therm_throt.c | 1 + arch/x86/kernel/microcode.c | 10 ++++--- drivers/base/cpu.c | 10 ++++--- drivers/base/memory.c | 12 ++++++--- drivers/base/node.c | 15 +++++++---- drivers/base/sys.c | 4 +-- drivers/base/topology.c | 17 ++++++++---- drivers/cpuidle/sysfs.c | 10 ++++--- drivers/xen/balloon.c | 1 + include/linux/sysdev.h | 5 ++-- kernel/rtmutex-tester.c | 7 ++--- kernel/sched.c | 8 ++++-- kernel/time/clocksource.c | 8 ++++-- 24 files changed, 239 insertions(+), 112 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index cc5145b28e7f..368d171754cf 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -130,7 +130,9 @@ static const struct leds_evt_name evt_names[] = { { "red", led_red_on, led_red_off }, }; -static ssize_t leds_store(struct sys_device *dev, const char *buf, size_t size) +static ssize_t leds_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t size) { int ret = -EINVAL, len = strcspn(buf, " "); diff --git a/arch/avr32/kernel/cpu.c b/arch/avr32/kernel/cpu.c index b8409caeb23d..e84faffbbeca 100644 --- a/arch/avr32/kernel/cpu.c +++ b/arch/avr32/kernel/cpu.c @@ -26,14 +26,16 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices); * XXX: If/when a SMP-capable implementation of AVR32 will ever be * made, we must make sure that the code executes on the correct CPU. */ -static ssize_t show_pc0event(struct sys_device *dev, char *buf) +static ssize_t show_pc0event(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccr; pccr = sysreg_read(PCCR); return sprintf(buf, "0x%lx\n", (pccr >> 12) & 0x3f); } -static ssize_t store_pc0event(struct sys_device *dev, const char *buf, +static ssize_t store_pc0event(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -46,15 +48,17 @@ static ssize_t store_pc0event(struct sys_device *dev, const char *buf, sysreg_write(PCCR, val); return count; } -static ssize_t show_pc0count(struct sys_device *dev, char *buf) +static ssize_t show_pc0count(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pcnt0; pcnt0 = sysreg_read(PCNT0); return sprintf(buf, "%lu\n", pcnt0); } -static ssize_t store_pc0count(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t store_pc0count(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { unsigned long val; char *endp; @@ -67,14 +71,16 @@ static ssize_t store_pc0count(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_pc1event(struct sys_device *dev, char *buf) +static ssize_t show_pc1event(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccr; pccr = sysreg_read(PCCR); return sprintf(buf, "0x%lx\n", (pccr >> 18) & 0x3f); } -static ssize_t store_pc1event(struct sys_device *dev, const char *buf, +static ssize_t store_pc1event(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -87,14 +93,16 @@ static ssize_t store_pc1event(struct sys_device *dev, const char *buf, sysreg_write(PCCR, val); return count; } -static ssize_t show_pc1count(struct sys_device *dev, char *buf) +static ssize_t show_pc1count(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pcnt1; pcnt1 = sysreg_read(PCNT1); return sprintf(buf, "%lu\n", pcnt1); } -static ssize_t store_pc1count(struct sys_device *dev, const char *buf, +static ssize_t store_pc1count(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -108,14 +116,16 @@ static ssize_t store_pc1count(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_pccycles(struct sys_device *dev, char *buf) +static ssize_t show_pccycles(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccnt; pccnt = sysreg_read(PCCNT); return sprintf(buf, "%lu\n", pccnt); } -static ssize_t store_pccycles(struct sys_device *dev, const char *buf, +static ssize_t store_pccycles(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -129,14 +139,16 @@ static ssize_t store_pccycles(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_pcenable(struct sys_device *dev, char *buf) +static ssize_t show_pcenable(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccr; pccr = sysreg_read(PCCR); return sprintf(buf, "%c\n", (pccr & 1)?'1':'0'); } -static ssize_t store_pcenable(struct sys_device *dev, const char *buf, +static ssize_t store_pcenable(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long pccr, val; diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c index b642648cc2ac..c539c689493b 100644 --- a/arch/ia64/kernel/err_inject.c +++ b/arch/ia64/kernel/err_inject.c @@ -55,7 +55,8 @@ static u64 resources[NR_CPUS]; #define show(name) \ static ssize_t \ -show_##name(struct sys_device *dev, char *buf) \ +show_##name(struct sys_device *dev, struct sysdev_attribute *attr, \ + char *buf) \ { \ u32 cpu=dev->id; \ return sprintf(buf, "%lx\n", name[cpu]); \ @@ -63,7 +64,8 @@ show_##name(struct sys_device *dev, char *buf) \ #define store(name) \ static ssize_t \ -store_##name(struct sys_device *dev, const char *buf, size_t size) \ +store_##name(struct sys_device *dev, struct sysdev_attribute *attr, \ + const char *buf, size_t size) \ { \ unsigned int cpu=dev->id; \ name[cpu] = simple_strtoull(buf, NULL, 16); \ @@ -76,7 +78,8 @@ show(call_start) * processor. The cpu number in driver is only used for storing data. */ static ssize_t -store_call_start(struct sys_device *dev, const char *buf, size_t size) +store_call_start(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t size) { unsigned int cpu=dev->id; unsigned long call_start = simple_strtoull(buf, NULL, 16); @@ -124,14 +127,16 @@ show(err_type_info) store(err_type_info) static ssize_t -show_virtual_to_phys(struct sys_device *dev, char *buf) +show_virtual_to_phys(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { unsigned int cpu=dev->id; return sprintf(buf, "%lx\n", phys_addr[cpu]); } static ssize_t -store_virtual_to_phys(struct sys_device *dev, const char *buf, size_t size) +store_virtual_to_phys(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t size) { unsigned int cpu=dev->id; u64 virt_addr=simple_strtoull(buf, NULL, 16); @@ -154,7 +159,8 @@ show(err_struct_info) store(err_struct_info) static ssize_t -show_err_data_buffer(struct sys_device *dev, char *buf) +show_err_data_buffer(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned int cpu=dev->id; @@ -165,7 +171,9 @@ show_err_data_buffer(struct sys_device *dev, char *buf) } static ssize_t -store_err_data_buffer(struct sys_device *dev, const char *buf, size_t size) +store_err_data_buffer(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t size) { unsigned int cpu=dev->id; int ret; diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index c8127f832df0..aba0ba95f062 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -28,7 +28,9 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices); /* Time in microseconds we delay before sleeping in the idle loop */ DEFINE_PER_CPU(unsigned long, smt_snooze_delay) = { 100 }; -static ssize_t store_smt_snooze_delay(struct sys_device *dev, const char *buf, +static ssize_t store_smt_snooze_delay(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); @@ -44,7 +46,9 @@ static ssize_t store_smt_snooze_delay(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_smt_snooze_delay(struct sys_device *dev, char *buf) +static ssize_t show_smt_snooze_delay(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); @@ -152,14 +156,17 @@ static unsigned long write_##NAME(unsigned long val) \ mtspr(ADDRESS, val); \ return 0; \ } \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ struct cpu *cpu = container_of(dev, struct cpu, sysdev); \ unsigned long val = run_on_cpu(cpu->sysdev.id, read_##NAME, 0); \ return sprintf(buf, "%lx\n", val); \ } \ static ssize_t __used \ - store_##NAME(struct sys_device *dev, const char *buf, size_t count) \ + store_##NAME(struct sys_device *dev, struct sysdev_attribute *attr, \ + const char *buf, size_t count) \ { \ struct cpu *cpu = container_of(dev, struct cpu, sysdev); \ unsigned long val; \ diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c index 4852bf312d83..4d4c8c169124 100644 --- a/arch/powerpc/platforms/cell/cbe_thermal.c +++ b/arch/powerpc/platforms/cell/cbe_thermal.c @@ -97,7 +97,8 @@ static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iom return value.spe[spu->spe_id]; } -static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_temp(struct sys_device *sysdev, struct sysdev_attribute *attr, + char *buf) { u8 value; struct cbe_pmd_regs __iomem *pmd_regs; @@ -146,32 +147,38 @@ static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char return size; } -static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 0); } -static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 8); } -static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 16); } -static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 0); } -static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 8); } -static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 16); } @@ -192,43 +199,51 @@ static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) /* shows the temperature of the DTS on the PPE, * located near the linear thermal sensor */ -static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_temp0(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return ppe_show_temp(sysdev, buf, 32); } /* shows the temperature of the second DTS on the PPE */ -static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_temp1(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return ppe_show_temp(sysdev, buf, 0); } -static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32); } -static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40); } -static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48); } -static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32); } -static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40); } -static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48); } diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 78f905bc6a42..a5bdb89a17c3 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -703,7 +703,8 @@ static unsigned long long spu_acct_time(struct spu *spu, } -static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf) +static ssize_t spu_stat_show(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { struct spu *spu = container_of(sysdev, struct spu, sysdev); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index b6781030cfbd..b795b3e24afd 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -864,7 +864,8 @@ int setup_profiling_timer(unsigned int multiplier) } #ifdef CONFIG_HOTPLUG_CPU -static ssize_t cpu_configure_show(struct sys_device *dev, char *buf) +static ssize_t cpu_configure_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t count; @@ -874,8 +875,9 @@ static ssize_t cpu_configure_show(struct sys_device *dev, char *buf) return count; } -static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t cpu_configure_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { int cpu = dev->id; int val, rc; @@ -922,7 +924,8 @@ out: static SYSDEV_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store); #endif /* CONFIG_HOTPLUG_CPU */ -static ssize_t cpu_polarization_show(struct sys_device *dev, char *buf) +static ssize_t cpu_polarization_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { int cpu = dev->id; ssize_t count; @@ -950,7 +953,8 @@ static ssize_t cpu_polarization_show(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(polarization, 0444, cpu_polarization_show, NULL); -static ssize_t show_cpu_address(struct sys_device *dev, char *buf) +static ssize_t show_cpu_address(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return sprintf(buf, "%d\n", __cpu_logical_map[dev->id]); } @@ -970,7 +974,8 @@ static struct attribute_group cpu_common_attr_group = { .attrs = cpu_common_attrs, }; -static ssize_t show_capability(struct sys_device *dev, char *buf) +static ssize_t show_capability(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned int capability; int rc; @@ -982,7 +987,8 @@ static ssize_t show_capability(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(capability, 0444, show_capability, NULL); -static ssize_t show_idle_count(struct sys_device *dev, char *buf) +static ssize_t show_idle_count(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct s390_idle_data *idle; unsigned long long idle_count; @@ -995,7 +1001,8 @@ static ssize_t show_idle_count(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(idle_count, 0444, show_idle_count, NULL); -static ssize_t show_idle_time(struct sys_device *dev, char *buf) +static ssize_t show_idle_time(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct s390_idle_data *idle; unsigned long long new_time; @@ -1112,7 +1119,9 @@ out: return rc; } -static ssize_t __ref rescan_store(struct sys_device *dev, const char *buf, +static ssize_t __ref rescan_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { int rc; @@ -1123,7 +1132,9 @@ static ssize_t __ref rescan_store(struct sys_device *dev, const char *buf, static SYSDEV_ATTR(rescan, 0200, NULL, rescan_store); #endif /* CONFIG_HOTPLUG_CPU */ -static ssize_t dispatching_show(struct sys_device *dev, char *buf) +static ssize_t dispatching_show(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { ssize_t count; @@ -1133,8 +1144,9 @@ static ssize_t dispatching_show(struct sys_device *dev, char *buf) return count; } -static ssize_t dispatching_store(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t dispatching_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { int val, rc; char delim; diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index f2cede3947b2..ab70d9bd9261 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -1100,7 +1100,9 @@ static inline struct etr_aib *etr_aib_from_dev(struct sys_device *dev) return etr_port1_online ? &etr_port1 : NULL; } -static ssize_t etr_online_show(struct sys_device *dev, char *buf) +static ssize_t etr_online_show(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { unsigned int online; @@ -1109,7 +1111,8 @@ static ssize_t etr_online_show(struct sys_device *dev, char *buf) } static ssize_t etr_online_store(struct sys_device *dev, - const char *buf, size_t count) + struct sysdev_attribute *attr, + const char *buf, size_t count) { unsigned int value; @@ -1136,7 +1139,9 @@ static ssize_t etr_online_store(struct sys_device *dev, static SYSDEV_ATTR(online, 0600, etr_online_show, etr_online_store); -static ssize_t etr_stepping_control_show(struct sys_device *dev, char *buf) +static ssize_t etr_stepping_control_show(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ? etr_eacr.e0 : etr_eacr.e1); @@ -1144,7 +1149,8 @@ static ssize_t etr_stepping_control_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(stepping_control, 0400, etr_stepping_control_show, NULL); -static ssize_t etr_mode_code_show(struct sys_device *dev, char *buf) +static ssize_t etr_mode_code_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { if (!etr_port0_online && !etr_port1_online) /* Status word is not uptodate if both ports are offline. */ @@ -1155,7 +1161,8 @@ static ssize_t etr_mode_code_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(state_code, 0400, etr_mode_code_show, NULL); -static ssize_t etr_untuned_show(struct sys_device *dev, char *buf) +static ssize_t etr_untuned_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1166,7 +1173,8 @@ static ssize_t etr_untuned_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(untuned, 0400, etr_untuned_show, NULL); -static ssize_t etr_network_id_show(struct sys_device *dev, char *buf) +static ssize_t etr_network_id_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1177,7 +1185,8 @@ static ssize_t etr_network_id_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(network, 0400, etr_network_id_show, NULL); -static ssize_t etr_id_show(struct sys_device *dev, char *buf) +static ssize_t etr_id_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1188,7 +1197,8 @@ static ssize_t etr_id_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(id, 0400, etr_id_show, NULL); -static ssize_t etr_port_number_show(struct sys_device *dev, char *buf) +static ssize_t etr_port_number_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1199,7 +1209,8 @@ static ssize_t etr_port_number_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(port, 0400, etr_port_number_show, NULL); -static ssize_t etr_coupled_show(struct sys_device *dev, char *buf) +static ssize_t etr_coupled_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1210,7 +1221,8 @@ static ssize_t etr_coupled_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(coupled, 0400, etr_coupled_show, NULL); -static ssize_t etr_local_time_show(struct sys_device *dev, char *buf) +static ssize_t etr_local_time_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1221,7 +1233,8 @@ static ssize_t etr_local_time_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(local_time, 0400, etr_local_time_show, NULL); -static ssize_t etr_utc_offset_show(struct sys_device *dev, char *buf) +static ssize_t etr_utc_offset_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c index 51b57c0d1a3c..347ee11351ec 100644 --- a/arch/sh/drivers/dma/dma-sysfs.c +++ b/arch/sh/drivers/dma/dma-sysfs.c @@ -23,7 +23,8 @@ static struct sysdev_class dma_sysclass = { }; EXPORT_SYMBOL(dma_sysclass); -static ssize_t dma_show_devices(struct sys_device *dev, char *buf) +static ssize_t dma_show_devices(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t len = 0; int i; @@ -57,13 +58,15 @@ static int __init dma_sysclass_init(void) } postcore_initcall(dma_sysclass_init); -static ssize_t dma_show_dev_id(struct sys_device *dev, char *buf) +static ssize_t dma_show_dev_id(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct dma_channel *channel = to_dma_channel(dev); return sprintf(buf, "%s\n", channel->dev_id); } static ssize_t dma_store_dev_id(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -74,6 +77,7 @@ static ssize_t dma_store_dev_id(struct sys_device *dev, static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id); static ssize_t dma_store_config(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -87,13 +91,15 @@ static ssize_t dma_store_config(struct sys_device *dev, static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config); -static ssize_t dma_show_mode(struct sys_device *dev, char *buf) +static ssize_t dma_show_mode(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct dma_channel *channel = to_dma_channel(dev); return sprintf(buf, "0x%08x\n", channel->mode); } static ssize_t dma_store_mode(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -104,7 +110,8 @@ static ssize_t dma_store_mode(struct sys_device *dev, static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode); #define dma_ro_attr(field, fmt) \ -static ssize_t dma_show_##field(struct sys_device *dev, char *buf) \ +static ssize_t dma_show_##field(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf)\ { \ struct dma_channel *channel = to_dma_channel(dev); \ return sprintf(buf, fmt, channel->field); \ diff --git a/arch/sparc64/kernel/sysfs.c b/arch/sparc64/kernel/sysfs.c index e885034a6b73..84e5ce146713 100644 --- a/arch/sparc64/kernel/sysfs.c +++ b/arch/sparc64/kernel/sysfs.c @@ -14,7 +14,8 @@ static DEFINE_PER_CPU(struct hv_mmu_statistics, mmu_stats) __attribute__((aligned(64))); #define SHOW_MMUSTAT_ULONG(NAME) \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ struct hv_mmu_statistics *p = &per_cpu(mmu_stats, dev->id); \ return sprintf(buf, "%lu\n", p->NAME); \ @@ -135,13 +136,16 @@ static unsigned long write_mmustat_enable(unsigned long val) return sun4v_mmustat_conf(ra, &orig_ra); } -static ssize_t show_mmustat_enable(struct sys_device *s, char *buf) +static ssize_t show_mmustat_enable(struct sys_device *s, + struct sysdev_attribute *attr, char *buf) { unsigned long val = run_on_cpu(s->id, read_mmustat_enable, 0); return sprintf(buf, "%lx\n", val); } -static ssize_t store_mmustat_enable(struct sys_device *s, const char *buf, size_t count) +static ssize_t store_mmustat_enable(struct sys_device *s, + struct sysdev_attribute *attr, const char *buf, + size_t count) { unsigned long val, err; int ret = sscanf(buf, "%ld", &val); @@ -179,14 +183,16 @@ static void unregister_mmu_stats(struct sys_device *s) #endif #define SHOW_CPUDATA_ULONG_NAME(NAME, MEMBER) \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ cpuinfo_sparc *c = &cpu_data(dev->id); \ return sprintf(buf, "%lu\n", c->MEMBER); \ } #define SHOW_CPUDATA_UINT_NAME(NAME, MEMBER) \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ cpuinfo_sparc *c = &cpu_data(dev->id); \ return sprintf(buf, "%u\n", c->MEMBER); \ diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index c4a7ec31394c..e6a4d5f67643 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c @@ -762,10 +762,14 @@ DEFINE_PER_CPU(struct sys_device, device_mce); /* Why are there no generic functions for this? */ #define ACCESSOR(name, var, start) \ - static ssize_t show_ ## name(struct sys_device *s, char *buf) { \ + static ssize_t show_ ## name(struct sys_device *s, \ + struct sysdev_attribute *attr, \ + char *buf) { \ return sprintf(buf, "%lx\n", (unsigned long)var); \ } \ - static ssize_t set_ ## name(struct sys_device *s,const char *buf,size_t siz) { \ + static ssize_t set_ ## name(struct sys_device *s, \ + struct sysdev_attribute *attr, \ + const char *buf, size_t siz) { \ char *end; \ unsigned long new = simple_strtoul(buf, &end, 0); \ if (end == buf) return -EINVAL; \ @@ -786,14 +790,16 @@ ACCESSOR(bank3ctl,bank[3],mce_restart()) ACCESSOR(bank4ctl,bank[4],mce_restart()) ACCESSOR(bank5ctl,bank[5],mce_restart()) -static ssize_t show_trigger(struct sys_device *s, char *buf) +static ssize_t show_trigger(struct sys_device *s, struct sysdev_attribute *attr, + char *buf) { strcpy(buf, trigger); strcat(buf, "\n"); return strlen(trigger) + 1; } -static ssize_t set_trigger(struct sys_device *s,const char *buf,size_t siz) +static ssize_t set_trigger(struct sys_device *s, struct sysdev_attribute *attr, + const char *buf,size_t siz) { char *p; int len; diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index 1f4cc48c14c6..d5ae2243f0b9 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -35,6 +35,7 @@ atomic_t therm_throt_en = ATOMIC_INIT(0); #define define_therm_throt_sysdev_show_func(name) \ static ssize_t therm_throt_sysdev_show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ char *buf) \ { \ unsigned int cpu = dev->id; \ diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c index 56b933119a04..fc4790638b69 100644 --- a/arch/x86/kernel/microcode.c +++ b/arch/x86/kernel/microcode.c @@ -644,7 +644,9 @@ static void microcode_fini_cpu(int cpu) mutex_unlock(µcode_mutex); } -static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) +static ssize_t reload_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t sz) { struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; char *end; @@ -674,14 +676,16 @@ static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) return sz; } -static ssize_t version_show(struct sys_device *dev, char *buf) +static ssize_t version_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; return sprintf(buf, "0x%x\n", uci->rev); } -static ssize_t pf_show(struct sys_device *dev, char *buf) +static ssize_t pf_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index e38dfed41d80..20537d507909 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -21,15 +21,16 @@ EXPORT_SYMBOL(cpu_sysdev_class); static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices); #ifdef CONFIG_HOTPLUG_CPU -static ssize_t show_online(struct sys_device *dev, char *buf) +static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id)); } -static ssize_t __ref store_online(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t count) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t ret; @@ -80,7 +81,8 @@ static inline void register_cpu_control(struct cpu *cpu) #ifdef CONFIG_KEXEC #include -static ssize_t show_crash_notes(struct sys_device *dev, char *buf) +static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t rc; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 937e8258981d..4d4e0e7b6e92 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -92,7 +92,8 @@ unregister_memory(struct memory_block *memory, struct mem_section *section) * uses. */ -static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf) +static ssize_t show_mem_phys_index(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -102,7 +103,8 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf) /* * online, offline, going offline, etc. */ -static ssize_t show_mem_state(struct sys_device *dev, char *buf) +static ssize_t show_mem_state(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -217,7 +219,8 @@ out: } static ssize_t -store_mem_state(struct sys_device *dev, const char *buf, size_t count) +store_mem_state(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; unsigned int phys_section_nr; @@ -248,7 +251,8 @@ out: * s.t. if I offline all of these sections I can then * remove the physical device? */ -static ssize_t show_phys_device(struct sys_device *dev, char *buf) +static ssize_t show_phys_device(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); diff --git a/drivers/base/node.c b/drivers/base/node.c index 0f867a083338..5116b78c6325 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -36,11 +36,13 @@ static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf) return len; } -static inline ssize_t node_read_cpumask(struct sys_device *dev, char *buf) +static inline ssize_t node_read_cpumask(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return node_read_cpumap(dev, 0, buf); } -static inline ssize_t node_read_cpulist(struct sys_device *dev, char *buf) +static inline ssize_t node_read_cpulist(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return node_read_cpumap(dev, 1, buf); } @@ -49,7 +51,8 @@ static SYSDEV_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL); static SYSDEV_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL); #define K(x) ((x) << (PAGE_SHIFT - 10)) -static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) +static ssize_t node_read_meminfo(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { int n; int nid = dev->id; @@ -112,7 +115,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) #undef K static SYSDEV_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL); -static ssize_t node_read_numastat(struct sys_device * dev, char * buf) +static ssize_t node_read_numastat(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { return sprintf(buf, "numa_hit %lu\n" @@ -130,7 +134,8 @@ static ssize_t node_read_numastat(struct sys_device * dev, char * buf) } static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL); -static ssize_t node_read_distance(struct sys_device * dev, char * buf) +static ssize_t node_read_distance(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { int nid = dev->id; int len = 0; diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 50690d9df248..dc7dace14e1c 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -36,7 +36,7 @@ sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer) struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr); if (sysdev_attr->show) - return sysdev_attr->show(sysdev, buffer); + return sysdev_attr->show(sysdev, sysdev_attr, buffer); return -EIO; } @@ -49,7 +49,7 @@ sysdev_store(struct kobject * kobj, struct attribute * attr, struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr); if (sysdev_attr->store) - return sysdev_attr->store(sysdev, buffer, count); + return sysdev_attr->store(sysdev, sysdev_attr, buffer, count); return -EIO; } diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 3f6d9b0a6abe..199cd97e32e6 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -34,7 +34,8 @@ static SYSDEV_ATTR(_name, 0444, show_##_name, NULL) #define define_id_show_func(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ return sprintf(buf, "%d\n", topology_##name(cpu)); \ @@ -59,14 +60,17 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) #ifdef arch_provides_topology_pointers #define define_siblings_show_map(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ return show_cpumap(0, &(topology_##name(cpu)), buf); \ } #define define_siblings_show_list(name) \ -static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ +static ssize_t show_##name##_list(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ unsigned int cpu = dev->id; \ return show_cpumap(1, &(topology_##name(cpu)), buf); \ @@ -74,7 +78,8 @@ static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ #else #define define_siblings_show_map(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ cpumask_t mask = topology_##name(cpu); \ @@ -82,7 +87,9 @@ static ssize_t show_##name(struct sys_device *dev, char *buf) \ } #define define_siblings_show_list(name) \ -static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ +static ssize_t show_##name##_list(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ unsigned int cpu = dev->id; \ cpumask_t mask = topology_##name(cpu); \ diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index e949618b9be0..31a0e0b455b6 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -21,7 +21,8 @@ static int __init cpuidle_sysfs_setup(char *unused) } __setup("cpuidle_sysfs_switch", cpuidle_sysfs_setup); -static ssize_t show_available_governors(struct sys_device *dev, char *buf) +static ssize_t show_available_governors(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t i = 0; struct cpuidle_governor *tmp; @@ -39,7 +40,8 @@ out: return i; } -static ssize_t show_current_driver(struct sys_device *dev, char *buf) +static ssize_t show_current_driver(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t ret; @@ -53,7 +55,8 @@ static ssize_t show_current_driver(struct sys_device *dev, char *buf) return ret; } -static ssize_t show_current_governor(struct sys_device *dev, char *buf) +static ssize_t show_current_governor(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t ret; @@ -68,6 +71,7 @@ static ssize_t show_current_governor(struct sys_device *dev, char *buf) } static ssize_t store_current_governor(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { char gov_name[CPUIDLE_NAME_LEN]; diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 591bc29b55f5..d4427cb86979 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -610,6 +610,7 @@ static ssize_t show_target_kb(struct sys_device *dev, char *buf) } static ssize_t store_target_kb(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index f2767bc6b735..8dcf3162b21b 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -99,8 +99,9 @@ extern void sysdev_unregister(struct sys_device *); struct sysdev_attribute { struct attribute attr; - ssize_t (*show)(struct sys_device *, char *); - ssize_t (*store)(struct sys_device *, const char *, size_t); + ssize_t (*show)(struct sys_device *, struct sysdev_attribute *, char *); + ssize_t (*store)(struct sys_device *, struct sysdev_attribute *, + const char *, size_t); }; diff --git a/kernel/rtmutex-tester.c b/kernel/rtmutex-tester.c index 092e4c620af9..a56f629b057a 100644 --- a/kernel/rtmutex-tester.c +++ b/kernel/rtmutex-tester.c @@ -297,8 +297,8 @@ static int test_func(void *data) * * opcode:data */ -static ssize_t sysfs_test_command(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t sysfs_test_command(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t count) { struct sched_param schedpar; struct test_thread_data *td; @@ -360,7 +360,8 @@ static ssize_t sysfs_test_command(struct sys_device *dev, const char *buf, * @dev: thread to query * @buf: char buffer to be filled with thread status info */ -static ssize_t sysfs_test_status(struct sys_device *dev, char *buf) +static ssize_t sysfs_test_status(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct test_thread_data *td; struct task_struct *tsk; diff --git a/kernel/sched.c b/kernel/sched.c index 99e6d850ecab..b1104ea5d255 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7737,11 +7737,13 @@ static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) } #ifdef CONFIG_SCHED_MC -static ssize_t sched_mc_power_savings_show(struct sys_device *dev, char *page) +static ssize_t sched_mc_power_savings_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *page) { return sprintf(page, "%u\n", sched_mc_power_savings); } static ssize_t sched_mc_power_savings_store(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { return sched_power_savings_store(buf, count, 0); @@ -7751,11 +7753,13 @@ static SYSDEV_ATTR(sched_mc_power_savings, 0644, sched_mc_power_savings_show, #endif #ifdef CONFIG_SCHED_SMT -static ssize_t sched_smt_power_savings_show(struct sys_device *dev, char *page) +static ssize_t sched_smt_power_savings_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *page) { return sprintf(page, "%u\n", sched_smt_power_savings); } static ssize_t sched_smt_power_savings_store(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { return sched_power_savings_store(buf, count, 1); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index dadde5361f32..b1c2da81b050 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -376,7 +376,8 @@ void clocksource_unregister(struct clocksource *cs) * Provides sysfs interface for listing current clocksource. */ static ssize_t -sysfs_show_current_clocksources(struct sys_device *dev, char *buf) +sysfs_show_current_clocksources(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t count = 0; @@ -397,6 +398,7 @@ sysfs_show_current_clocksources(struct sys_device *dev, char *buf) * clocksource selction. */ static ssize_t sysfs_override_clocksource(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct clocksource *ovr = NULL; @@ -449,7 +451,9 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, * Provides sysfs interface for listing registered clocksources */ static ssize_t -sysfs_show_available_clocksources(struct sys_device *dev, char *buf) +sysfs_show_available_clocksources(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { struct clocksource *src; ssize_t count = 0; -- cgit v1.2.3 From 9800794ac11d4646384b3a310dfd1fe2eed577bf Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jul 2008 18:48:42 +0200 Subject: sysdev: Add utility functions for simple int/ulong variable sysdev attributes This adds a new sysdev_ext_attribute that stores a pointer to the variable it manages and some utility functions/macro to easily use them. Previously all users wrote custom macros to generate show/store functions for each variable, with this it is possible to avoid that in many cases. Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- drivers/base/sys.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysdev.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/sys.c b/drivers/base/sys.c index dc7dace14e1c..40fc14f03540 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -479,3 +479,52 @@ int __init system_bus_init(void) EXPORT_SYMBOL_GPL(sysdev_register); EXPORT_SYMBOL_GPL(sysdev_unregister); + +#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr) + +ssize_t sysdev_store_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + unsigned long new = simple_strtoul(buf, &end, 0); + if (end == buf) + return -EINVAL; + *(unsigned long *)(ea->var) = new; + return end - buf; +} +EXPORT_SYMBOL_GPL(sysdev_store_ulong); + +ssize_t sysdev_show_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_ulong); + +ssize_t sysdev_store_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + long new = simple_strtol(buf, &end, 0); + if (end == buf || new > INT_MAX || new < INT_MIN) + return -EINVAL; + *(int *)(ea->var) = new; + return end - buf; +} +EXPORT_SYMBOL_GPL(sysdev_store_int); + +ssize_t sysdev_show_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_int); + diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index 8dcf3162b21b..f395bb3fa2f2 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -119,4 +119,38 @@ struct sysdev_attribute { extern int sysdev_create_file(struct sys_device *, struct sysdev_attribute *); extern void sysdev_remove_file(struct sys_device *, struct sysdev_attribute *); +struct sysdev_ext_attribute { + struct sysdev_attribute attr; + void *var; +}; + +/* + * Support for simple variable sysdev attributes. + * The pointer to the variable is stored in a sysdev_ext_attribute + */ + +/* Add more types as needed */ + +extern ssize_t sysdev_show_ulong(struct sys_device *, struct sysdev_attribute *, + char *); +extern ssize_t sysdev_store_ulong(struct sys_device *, + struct sysdev_attribute *, const char *, size_t); +extern ssize_t sysdev_show_int(struct sys_device *, struct sysdev_attribute *, + char *); +extern ssize_t sysdev_store_int(struct sys_device *, + struct sysdev_attribute *, const char *, size_t); + +#define _SYSDEV_ULONG_ATTR(_name, _mode, _var) \ + { _SYSDEV_ATTR(_name, _mode, sysdev_show_ulong, sysdev_store_ulong), \ + &(_var) } +#define SYSDEV_ULONG_ATTR(_name, _mode, _var) \ + struct sysdev_ext_attribute attr_##_name = \ + _SYSDEV_ULONG_ATTR(_name, _mode, _var); +#define _SYSDEV_INT_ATTR(_name, _mode, _var) \ + { _SYSDEV_ATTR(_name, _mode, sysdev_show_int, sysdev_store_int), \ + &(_var) } +#define SYSDEV_INT_ATTR(_name, _mode, _var) \ + struct sysdev_ext_attribute attr_##_name = \ + _SYSDEV_INT_ATTR(_name, _mode, _var); + #endif /* _SYSDEV_H_ */ -- cgit v1.2.3 From eadcf0d704a46979c29984fa05f1fc413c775bcb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Jul 2008 12:46:22 -0700 Subject: MTD: handle pci_name() being const This changes the MTD core to handle pci_name() now returning a constant string. Cc: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/block2mtd.c | 8 +++++--- include/linux/mtd/map.h | 2 +- include/linux/mtd/mtd.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 519d942e7940..7b72a1b36115 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -241,6 +241,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) { struct block_device *bdev; struct block2mtd_dev *dev; + char *name; if (!devname) return NULL; @@ -279,12 +280,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* Setup the MTD structure */ /* make the name contain the block device in */ - dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), + name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, GFP_KERNEL); - if (!dev->mtd.name) + if (!name) goto devinit_err; - sprintf(dev->mtd.name, "block2mtd: %s", devname); + sprintf(name, "block2mtd: %s", devname); + dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; dev->mtd.erasesize = erase_size; diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index a9fae032ba81..9c1d95491f8b 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -189,7 +189,7 @@ typedef union { */ struct map_info { - char *name; + const char *name; unsigned long size; resource_size_t phys; #define NO_XIP (-1UL) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 245f9098e171..8b5d49133ec6 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -121,7 +121,7 @@ struct mtd_info { u_int32_t oobavail; // Available OOB bytes per block // Kernel-only stuff starts here. - char *name; + const char *name; int index; /* ecc layout structure pointer - read only ! */ -- cgit v1.2.3 From af5406895a05720a879dc33e2f4878fa503e81b3 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Tue, 22 Jul 2008 19:24:26 -0500 Subject: module: reorder struct module to save space on 64 bit builds reorder struct module to save space on 64 bit builds. saves 1 cacheline_size (128 on default x86_64 & 64 on AMD Opteron/athlon) when CONFIG_MODULE_UNLOAD=y. Signed-off-by: Richard Kennedy Signed-off-by: Rusty Russell --- include/linux/module.h | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 3e03b1acbc94..63f0eb69e214 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -249,27 +249,28 @@ struct module /* Exported symbols */ const struct kernel_symbol *syms; - unsigned int num_syms; const unsigned long *crcs; + unsigned int num_syms; /* GPL-only exported symbols. */ - const struct kernel_symbol *gpl_syms; unsigned int num_gpl_syms; + const struct kernel_symbol *gpl_syms; const unsigned long *gpl_crcs; /* unused exported symbols. */ const struct kernel_symbol *unused_syms; - unsigned int num_unused_syms; const unsigned long *unused_crcs; + unsigned int num_unused_syms; + /* GPL-only, unused exported symbols. */ - const struct kernel_symbol *unused_gpl_syms; unsigned int num_unused_gpl_syms; + const struct kernel_symbol *unused_gpl_syms; const unsigned long *unused_gpl_crcs; /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; - unsigned int num_gpl_future_syms; const unsigned long *gpl_future_crcs; + unsigned int num_gpl_future_syms; /* Exception table */ unsigned int num_exentries; @@ -300,23 +301,9 @@ struct module #ifdef CONFIG_GENERIC_BUG /* Support for BUG */ + unsigned num_bugs; struct list_head bug_list; struct bug_entry *bug_table; - unsigned num_bugs; -#endif - -#ifdef CONFIG_MODULE_UNLOAD - /* Reference counts */ - struct module_ref ref[NR_CPUS]; - - /* What modules depend on me? */ - struct list_head modules_which_use_me; - - /* Who is waiting for us to be unloaded */ - struct task_struct *waiter; - - /* Destruction function. */ - void (*exit)(void); #endif #ifdef CONFIG_KALLSYMS @@ -342,6 +329,21 @@ struct module struct marker *markers; unsigned int num_markers; #endif + +#ifdef CONFIG_MODULE_UNLOAD + /* What modules depend on me? */ + struct list_head modules_which_use_me; + + /* Who is waiting for us to be unloaded */ + struct task_struct *waiter; + + /* Destruction function. */ + void (*exit)(void); + + /* Reference counts */ + struct module_ref ref[NR_CPUS]; +#endif + }; #ifndef MODULE_ARCH_INIT #define MODULE_ARCH_INIT {} -- cgit v1.2.3 From f7f5b67557eac1131ba6532522e3c50eced34238 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 22 Jul 2008 19:24:26 -0500 Subject: Shrink struct module: CONFIG_UNUSED_SYMBOLS ifdefs module.c and module.h conatains code for finding exported symbols which are declared with EXPORT_UNUSED_SYMBOL, and this code is compiled in even if CONFIG_UNUSED_SYMBOLS is not set and thus there can be no EXPORT_UNUSED_SYMBOLs in modules anyway (because EXPORT_UNUSED_SYMBOL(x) are compiled out to nothing then). This patch adds required #ifdefs. Signed-off-by: Denys Vlasenko Signed-off-by: Rusty Russell --- include/linux/module.h | 2 ++ init/Kconfig | 4 ++-- kernel/module.c | 49 ++++++++++++++++++++++++++++++++++--------------- 3 files changed, 38 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 63f0eb69e214..a860a2c1f379 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -257,6 +257,7 @@ struct module const struct kernel_symbol *gpl_syms; const unsigned long *gpl_crcs; +#ifdef CONFIG_UNUSED_SYMBOLS /* unused exported symbols. */ const struct kernel_symbol *unused_syms; const unsigned long *unused_crcs; @@ -266,6 +267,7 @@ struct module unsigned int num_unused_gpl_syms; const struct kernel_symbol *unused_gpl_syms; const unsigned long *unused_gpl_crcs; +#endif /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; diff --git a/init/Kconfig b/init/Kconfig index 6199d1120900..c8578f9ee31d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -856,8 +856,8 @@ config MODULE_UNLOAD help Without this option you will not be able to unload any modules (note that some modules may not be unloadable - anyway), which makes your kernel slightly smaller and - simpler. If unsure, say Y. + anyway), which makes your kernel smaller, faster + and simpler. If unsure, say Y. config MODULE_FORCE_UNLOAD bool "Forced module unloading" diff --git a/kernel/module.c b/kernel/module.c index c51c089c666e..ea9580521eb1 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -134,17 +134,19 @@ extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; -extern const struct kernel_symbol __start___ksymtab_unused[]; -extern const struct kernel_symbol __stop___ksymtab_unused[]; -extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; -extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; extern const unsigned long __start___kcrctab[]; extern const unsigned long __start___kcrctab_gpl[]; extern const unsigned long __start___kcrctab_gpl_future[]; +#ifdef CONFIG_UNUSED_SYMBOLS +extern const struct kernel_symbol __start___ksymtab_unused[]; +extern const struct kernel_symbol __stop___ksymtab_unused[]; +extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; +extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const unsigned long __start___kcrctab_unused[]; extern const unsigned long __start___kcrctab_unused_gpl[]; +#endif #ifndef CONFIG_MODVERSIONS #define symversion(base, idx) NULL @@ -198,12 +200,14 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr, { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, __start___kcrctab_gpl_future, WILL_BE_GPL_ONLY, false }, +#ifdef CONFIG_UNUSED_SYMBOLS { __start___ksymtab_unused, __stop___ksymtab_unused, __start___kcrctab_unused, NOT_GPL_ONLY, true }, { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, __start___kcrctab_unused_gpl, GPL_ONLY, true }, +#endif }; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) @@ -220,6 +224,7 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr, mod->gpl_future_syms + mod->num_gpl_future_syms, mod->gpl_future_crcs, WILL_BE_GPL_ONLY, false }, +#ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->unused_syms + mod->num_unused_syms, mod->unused_crcs, @@ -228,6 +233,7 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr, mod->unused_gpl_syms + mod->num_unused_gpl_syms, mod->unused_gpl_crcs, GPL_ONLY, true }, +#endif }; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) @@ -270,6 +276,7 @@ static bool find_symbol_in_section(const struct symsearch *syms, } } +#ifdef CONFIG_UNUSED_SYMBOLS if (syms->unused && fsa->warn) { printk(KERN_WARNING "Symbol %s is marked as UNUSED, " "however this module is using it.\n", fsa->name); @@ -281,6 +288,7 @@ static bool find_symbol_in_section(const struct symsearch *syms, "mailinglist together with submitting your code for " "inclusion.\n"); } +#endif fsa->owner = owner; fsa->crc = symversion(syms->crcs, symnum); @@ -1476,8 +1484,10 @@ static int verify_export_symbols(struct module *mod) { mod->syms, mod->num_syms }, { mod->gpl_syms, mod->num_gpl_syms }, { mod->gpl_future_syms, mod->num_gpl_future_syms }, +#ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->num_unused_syms }, { mod->unused_gpl_syms, mod->num_unused_gpl_syms }, +#endif }; for (i = 0; i < ARRAY_SIZE(arr); i++) { @@ -1795,10 +1805,12 @@ static struct module *load_module(void __user *umod, unsigned int gplfutureindex; unsigned int gplfuturecrcindex; unsigned int unwindex = 0; +#ifdef CONFIG_UNUSED_SYMBOLS unsigned int unusedindex; unsigned int unusedcrcindex; unsigned int unusedgplindex; unsigned int unusedgplcrcindex; +#endif unsigned int markersindex; unsigned int markersstringsindex; struct module *mod; @@ -1881,13 +1893,15 @@ static struct module *load_module(void __user *umod, exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab"); gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl"); gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future"); - unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused"); - unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl"); crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab"); gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl"); gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future"); +#ifdef CONFIG_UNUSED_SYMBOLS + unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused"); + unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl"); unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused"); unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl"); +#endif setupindex = find_sec(hdr, sechdrs, secstrings, "__param"); exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table"); obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm"); @@ -2049,14 +2063,15 @@ static struct module *load_module(void __user *umod, mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr; mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size / sizeof(*mod->gpl_future_syms); - mod->num_unused_syms = sechdrs[unusedindex].sh_size / - sizeof(*mod->unused_syms); - mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size / - sizeof(*mod->unused_gpl_syms); mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr; if (gplfuturecrcindex) mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr; +#ifdef CONFIG_UNUSED_SYMBOLS + mod->num_unused_syms = sechdrs[unusedindex].sh_size / + sizeof(*mod->unused_syms); + mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size / + sizeof(*mod->unused_gpl_syms); mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr; if (unusedcrcindex) mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr; @@ -2064,13 +2079,17 @@ static struct module *load_module(void __user *umod, if (unusedgplcrcindex) mod->unused_gpl_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr; +#endif #ifdef CONFIG_MODVERSIONS - if ((mod->num_syms && !crcindex) || - (mod->num_gpl_syms && !gplcrcindex) || - (mod->num_gpl_future_syms && !gplfuturecrcindex) || - (mod->num_unused_syms && !unusedcrcindex) || - (mod->num_unused_gpl_syms && !unusedgplcrcindex)) { + if ((mod->num_syms && !crcindex) + || (mod->num_gpl_syms && !gplcrcindex) + || (mod->num_gpl_future_syms && !gplfuturecrcindex) +#ifdef CONFIG_UNUSED_SYMBOLS + || (mod->num_unused_syms && !unusedcrcindex) + || (mod->num_unused_gpl_syms && !unusedgplcrcindex) +#endif + ) { printk(KERN_WARNING "%s: No versions for exported symbols.\n", mod->name); err = try_to_force_load(mod, "nocrc"); if (err) -- cgit v1.2.3 From 2f0f2a334bc38b61a9afca951185cd3844ee709d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 22 Jul 2008 19:24:27 -0500 Subject: module: turn longs into ints for module sizes This shrinks module.o and each *.ko file. And finally, structure members which hold length of module code (four such members there) and count of symbols are converted from longs to ints. We cannot possibly have a module where 32 bits won't be enough to hold such counts. For one, module loading checks module size for sanity before loading, so such insanely big module will fail that test first. Signed-off-by: Denys Vlasenko Signed-off-by: Rusty Russell --- include/linux/module.h | 6 +++--- kernel/module.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index a860a2c1f379..fce15ebd0e1c 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -288,10 +288,10 @@ struct module void *module_core; /* Here are the sizes of the init and core sections */ - unsigned long init_size, core_size; + unsigned int init_size, core_size; /* The size of the executable code in each section. */ - unsigned long init_text_size, core_text_size; + unsigned int init_text_size, core_text_size; /* The handle returned from unwind_add_table. */ void *unwind_info; @@ -311,7 +311,7 @@ struct module #ifdef CONFIG_KALLSYMS /* We keep the symbol and string tables for kallsyms. */ Elf_Sym *symtab; - unsigned long num_symtab; + unsigned int num_symtab; char *strtab; /* Section attributes */ diff --git a/kernel/module.c b/kernel/module.c index ea9580521eb1..5c7eb0695b3c 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1567,7 +1567,7 @@ static int simplify_symbols(Elf_Shdr *sechdrs, } /* Update size with this section: return offset. */ -static long get_offset(unsigned long *size, Elf_Shdr *sechdr) +static long get_offset(unsigned int *size, Elf_Shdr *sechdr) { long ret; @@ -2562,7 +2562,7 @@ static int m_show(struct seq_file *m, void *p) struct module *mod = list_entry(p, struct module, list); char buf[8]; - seq_printf(m, "%s %lu", + seq_printf(m, "%s %u", mod->name, mod->init_size + mod->core_size); print_unload_info(m, mod); -- cgit v1.2.3 From df648c9fbebb4de50e7a6e76cc253c7cb0421f9b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Jul 2008 19:00:18 +0200 Subject: rework try_then_request_module to do less in non-modular kernels This reworks try_then_request_module to only invoke the "lookup" function "x" once when the kernel is not modular. Signed-off-by: Johannes Berg Signed-off-by: Rusty Russell --- include/linux/kmod.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 5dc13848891b..9bdb28d6660f 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -29,11 +29,12 @@ /* modprobe exit status on success, -ve on error. Return value * usually useless though. */ extern int request_module(const char * name, ...) __attribute__ ((format (printf, 1, 2))); +#define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) #else static inline int request_module(const char * name, ...) { return -ENOSYS; } +#define try_then_request_module(x, mod...) (x) #endif -#define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) struct key; struct file; -- cgit v1.2.3 From a1ef5adb4cad43460ebba23c5a78cf4a55bb6a5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Jul 2008 19:00:17 +0200 Subject: remove CONFIG_KMOD from core kernel code Always compile request_module when the kernel allows modules. Signed-off-by: Johannes Berg Signed-off-by: Rusty Russell --- include/linux/kmod.h | 2 +- kernel/exec_domain.c | 2 +- kernel/kmod.c | 2 +- kernel/sysctl.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 9bdb28d6660f..0509c4ce4857 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -25,7 +25,7 @@ #define KMOD_PATH_LEN 256 -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /* modprobe exit status on success, -ve on error. Return value * usually useless though. */ extern int request_module(const char * name, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index a9e6bad9f706..c1ef192aa655 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -65,7 +65,7 @@ lookup_exec_domain(u_long personality) goto out; } -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES read_unlock(&exec_domains_lock); request_module("personality-%ld", pers); read_lock(&exec_domains_lock); diff --git a/kernel/kmod.c b/kernel/kmod.c index 8df97d3dfda8..90d7af1c1655 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -42,7 +42,7 @@ extern int max_threads; static struct workqueue_struct *khelper_wq; -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /* modprobe_path is set via /proc/sys. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6b16e16428d8..b859e6b5a767 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -110,7 +110,7 @@ static int min_percpu_pagelist_fract = 8; static int ngroups_max = NGROUPS_MAX; -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES extern char modprobe_path[]; #endif #ifdef CONFIG_CHR_DEV_SG @@ -475,7 +475,7 @@ static struct ctl_table kern_table[] = { .proc_handler = &ftrace_enable_sysctl, }, #endif -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES { .ctl_name = KERN_MODPROBE, .procname = "modprobe", -- cgit v1.2.3 From 651910874633a75f4a726d44e449be0a56b4b2e0 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Mon, 21 Jul 2008 14:21:32 -0700 Subject: execve filename: document and export via auxiliary vector The Linux kernel puts the filename argument of execve() into the new address space. Many developers are surprised to learn this. Those who know and could use it, object "But it's not documented." Those who want to use it dislike the expression (char *)(1+ strlen(env[-1+ n_env]) + env[-1+ n_env]) because it requires locating the last original environment variable, and assumes that the filename follows the characters. This patch documents the insertion of the filename, and makes it easier to find by adding a new tag AT_EXECFN in the ElfXX_auxv_t; see . In many cases readlink("/proc/self/exe",) gives the same answer. But if all the original pages get unmapped, then the kernel erases the symlink for /proc/self/exe. This can happen when a program decompressor does a good job of cleaning up after uncompressing directly to memory, so that the address space of the target program looks the same as if compression had never happened. One example is http://upx.sourceforge.net . One notable use of the underlying concept (what path containED the executable) is glibc expanding $ORIGIN in DT_RUNPATH. In practice for the near term, it may be a good idea for user-mode code to use both /proc/self/exe and AT_EXECFN as fall-back methods for each other. /proc/self/exe can fail due to unmapping, AT_EXECFN can fail because it won't be present on non-new systems. The auxvec or {AT_EXECFN}.d_val also can get overwritten, although in nearly all cases this would be the result of a bug. The runtime cost is one NEW_AUX_ENT using two words of stack space. The underlying value is maintained already as bprm->exec; setup_arg_pages() in fs/exec.c slides it for stack_shift, etc. Signed-off-by: John Reiser Cc: Roland McGrath Cc: Jakub Jelinek Cc: Ulrich Drepper Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 1 + include/linux/auxvec.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index d48ff5f370f4..639d2d8b5710 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -204,6 +204,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, NEW_AUX_ENT(AT_GID, tsk->gid); NEW_AUX_ENT(AT_EGID, tsk->egid); NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm)); + NEW_AUX_ENT(AT_EXECFN, bprm->exec); if (k_platform) { NEW_AUX_ENT(AT_PLATFORM, (elf_addr_t)(unsigned long)u_platform); diff --git a/include/linux/auxvec.h b/include/linux/auxvec.h index ad895455ab72..0da17d14fd13 100644 --- a/include/linux/auxvec.h +++ b/include/linux/auxvec.h @@ -26,8 +26,10 @@ #define AT_SECURE 23 /* secure mode boolean */ +#define AT_EXECFN 31 /* filename of program */ #ifdef __KERNEL__ -#define AT_VECTOR_SIZE_BASE (14 + 2) /* NEW_AUX_ENT entries in auxiliary table */ +#define AT_VECTOR_SIZE_BASE 17 /* NEW_AUX_ENT entries in auxiliary table */ + /* number of "#define AT_.*" above, minus {AT_NULL, AT_IGNORE, AT_NOTELF} */ #endif #endif /* _LINUX_AUXVEC_H */ -- cgit v1.2.3 From 0c36ec31473593aa937ff04f3b3b630e81512734 Mon Sep 17 00:00:00 2001 From: Juergen Beisert Date: Mon, 21 Jul 2008 14:21:34 -0700 Subject: gpio: gpio driver for max7301 SPI GPIO expander Maxim's MAX7301 is an SPI GPIO expander with 28 GPIOs. Note: MAX7301's interrupt feature is not supported yet. [akpm@linux-foundation.org: coding-style fixes] [g.liakhovetski@pengutronix.de: Fix inaccuracies in comments, check spi_setup() return code, mask off high byte in max7301_read()] Signed-off-by: Juergen Beisert Signed-off-by: Guennadi Liakhovetski Cc: Russell King Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/max7301.c | 339 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/max7301.h | 9 ++ 4 files changed, 355 insertions(+) create mode 100644 drivers/gpio/max7301.c create mode 100644 include/linux/spi/max7301.h (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 008c38ba774f..9e0c4fbfc51a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -69,6 +69,12 @@ config GPIO_PCF857X comment "SPI GPIO expanders:" +config GPIO_MAX7301 + tristate "Maxim MAX7301 GPIO expander" + depends on SPI_MASTER + help + gpio driver for Maxim MAX7301 SPI GPIO expander. + config GPIO_MCP23S08 tristate "Microchip MCP23S08 I/O expander" depends on SPI_MASTER diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fdde9923cf33..16e796dc5410 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o +obj-$(CONFIG_GPIO_MAX7301) += max7301.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o diff --git a/drivers/gpio/max7301.c b/drivers/gpio/max7301.c new file mode 100644 index 000000000000..39c795ad8312 --- /dev/null +++ b/drivers/gpio/max7301.c @@ -0,0 +1,339 @@ +/** + * drivers/gpio/max7301.c + * + * Copyright (C) 2006 Juergen Beisert, Pengutronix + * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix + * + * 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. + * + * The Maxim's MAX7301 device is an SPI driven GPIO expander. There are + * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more + * details + * Note: + * - DIN must be stable at the rising edge of clock. + * - when writing: + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = databyte, D8..D14 = commandbyte + * - D15 = low -> write command + * - when reading + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = dummy, D8..D14 = register address + * - D15 = high -> read command + * - raise CS and assert it again + * - always clock in 16 clocks at once + * - at DOUT: D15 first, D0 last + * - D0..D7 contains the data from the first cycle + * + * The driver exports a standard gpiochip interface + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "max7301" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 28 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct max7301 { + struct mutex lock; + u8 port_config[8]; /* field 0 is unused */ + u32 out_level; /* cached output levels */ + struct gpio_chip chip; + struct spi_device *spi; +}; + +/** + * max7301_write - Write a new register content + * @spi: The SPI device + * @reg: Register offset + * @val: Value to write + * + * A write to the MAX7301 means one message with one transfer + * + * Returns 0 if successful or a negative value on error + */ +static int max7301_write(struct spi_device *spi, unsigned int reg, unsigned int val) +{ + u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); + return spi_write(spi, (const u8 *)&word, sizeof(word)); +} + +/** + * max7301_read - Read back register content + * @spi: The SPI device + * @reg: Register offset + * + * A read from the MAX7301 means two transfers; here, one message each + * + * Returns positive 8 bit value from device if successful or a + * negative value on error + */ +static int max7301_read(struct spi_device *spi, unsigned int reg) +{ + int ret; + u16 word; + + word = 0x8000 | (reg << 8); + ret = spi_write(spi, (const u8 *)&word, sizeof(word)); + if (ret) + return ret; + /* + * This relies on the fact, that a transfer with NULL tx_buf shifts out + * zero bytes (=NOOP for MAX7301) + */ + ret = spi_read(spi, (u8 *)&word, sizeof(word)); + if (ret) + return ret; + return word & 0xff; +} + +static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + /* Standard GPIO API doesn't support pull-ups, has to be extended. + * Hard-coding no pollup for now. */ + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int __max7301_set(struct max7301 *ts, unsigned offset, int value) +{ + if (value) { + ts->out_level |= 1 << offset; + return max7301_write(ts->spi, 0x20 + offset, 0x01); + } else { + ts->out_level &= ~(1 << offset); + return max7301_write(ts->spi, 0x20 + offset, 0x00); + } +} + +static int max7301_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = __max7301_set(ts, offset, value); + + if (!ret) + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int max7301_get(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + int config, level = -EINVAL; + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + config = (ts->port_config[offset >> 2] >> ((offset & 3) * 2)) & 3; + + switch (config) { + case 1: + /* Output: return cached level */ + level = !!(ts->out_level & (1 << offset)); + break; + case 2: + case 3: + /* Input: read out */ + level = max7301_read(ts->spi, 0x20 + offset) & 0x01; + } + mutex_unlock(&ts->lock); + + return level; +} + +static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + __max7301_set(ts, offset, value); + + mutex_unlock(&ts->lock); +} + +static int __devinit max7301_probe(struct spi_device *spi) +{ + struct max7301 *ts; + struct max7301_platform_data *pdata; + int i, ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) + return -ENODEV; + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 16; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->lock); + + dev_set_drvdata(&spi->dev, ts); + + /* Power up the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x01); + + ts->spi = spi; + + ts->chip.label = DRIVER_NAME, + + ts->chip.direction_input = max7301_direction_input; + ts->chip.get = max7301_get; + ts->chip.direction_output = max7301_direction_output; + ts->chip.set = max7301_set; + + ts->chip.base = pdata->base; + ts->chip.ngpio = PIN_NUMBER; + ts->chip.can_sleep = 1; + ts->chip.dev = &spi->dev; + ts->chip.owner = THIS_MODULE; + + ret = gpiochip_add(&ts->chip); + if (ret) + goto exit_destroy; + + /* + * tristate all pins in hardware and cache the + * register values for later use. + */ + for (i = 1; i < 8; i++) { + int j; + /* 0xAA means input with internal pullup disabled */ + max7301_write(spi, 0x08 + i, 0xAA); + ts->port_config[i] = 0xAA; + for (j = 0; j < 4; j++) { + int idx = ts->chip.base + (i - 1) * 4 + j; + ret = gpio_direction_input(idx); + if (ret) + goto exit_remove; + gpio_free(idx); + } + } + return ret; + +exit_remove: + gpiochip_remove(&ts->chip); +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&ts->lock); + kfree(ts); + return ret; +} + +static int max7301_remove(struct spi_device *spi) +{ + struct max7301 *ts; + int ret; + + ts = dev_get_drvdata(&spi->dev); + if (ts == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + /* Power down the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x00); + + ret = gpiochip_remove(&ts->chip); + if (!ret) { + mutex_destroy(&ts->lock); + kfree(ts); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver max7301_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = max7301_probe, + .remove = __devexit_p(max7301_remove), +}; + +static int __init max7301_init(void) +{ + return spi_register_driver(&max7301_driver); +} + +static void __exit max7301_exit(void) +{ + spi_unregister_driver(&max7301_driver); +} + +module_init(max7301_init); +module_exit(max7301_exit); + +MODULE_AUTHOR("Juergen Beisert"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX7301 SPI based GPIO-Expander"); diff --git a/include/linux/spi/max7301.h b/include/linux/spi/max7301.h new file mode 100644 index 000000000000..6dfd83f19b4b --- /dev/null +++ b/include/linux/spi/max7301.h @@ -0,0 +1,9 @@ +#ifndef LINUX_SPI_MAX7301_H +#define LINUX_SPI_MAX7301_H + +struct max7301_platform_data { + /* number assigned to the first GPIO */ + unsigned base; +}; + +#endif -- cgit v1.2.3 From 95da310e66ee8090119596c70ca8432e57f9a97f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 22 Jul 2008 11:09:07 +0100 Subject: usb_serial: API all change USB serial likes to use port->tty back pointers for the real work it does and to do so without any actual locking. Unfortunately when you consider hangup events, hangup/parallel reopen or even worse hangup followed by parallel close events the tty->port and port->tty pointers are not guaranteed to be the same as port->tty is the active tty while tty->port is the port the tty may or may not still be attached to. So rework the entire API to pass the tty struct. For console cases we need to pass both for now. This shows up multiple drivers that immediately crash with USB console some of which have been fixed in the process. Longer term we need a proper tty as console abstraction Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/usb/serial/aircable.c | 19 +- drivers/usb/serial/airprime.c | 355 ++++++++++++++++++++++++++++++++++ drivers/usb/serial/ark3116.c | 32 +-- drivers/usb/serial/belkin_sa.c | 69 +++---- drivers/usb/serial/ch341.c | 10 +- drivers/usb/serial/console.c | 34 ++-- drivers/usb/serial/cp2101.c | 74 +++---- drivers/usb/serial/cyberjack.c | 47 +++-- drivers/usb/serial/cypress_m8.c | 117 ++++++----- drivers/usb/serial/digi_acceleport.c | 127 ++++++------ drivers/usb/serial/empeg.c | 78 +++----- drivers/usb/serial/ftdi_sio.c | 211 ++++++++++---------- drivers/usb/serial/garmin_gps.c | 58 ++---- drivers/usb/serial/generic.c | 39 ++-- drivers/usb/serial/io_edgeport.c | 175 ++++++----------- drivers/usb/serial/io_ti.c | 109 ++++++----- drivers/usb/serial/ipaq.c | 48 +++-- drivers/usb/serial/ipw.c | 15 +- drivers/usb/serial/ir-usb.c | 81 ++++---- drivers/usb/serial/iuu_phoenix.c | 43 ++-- drivers/usb/serial/keyspan.c | 138 ++++++------- drivers/usb/serial/keyspan.h | 39 ++-- drivers/usb/serial/keyspan_pda.c | 69 +++---- drivers/usb/serial/kl5kusb105.c | 74 ++++--- drivers/usb/serial/kobil_sct.c | 80 ++++---- drivers/usb/serial/mct_u232.c | 121 +++++------- drivers/usb/serial/mos7720.c | 87 ++++----- drivers/usb/serial/mos7840.c | 165 +++++++--------- drivers/usb/serial/navman.c | 10 +- drivers/usb/serial/omninet.c | 26 +-- drivers/usb/serial/option.c | 105 ++++------ drivers/usb/serial/oti6858.c | 119 +++++------- drivers/usb/serial/pl2303.c | 76 ++++---- drivers/usb/serial/safe_serial.c | 14 +- drivers/usb/serial/sierra.c | 93 +++------ drivers/usb/serial/spcp8x5.c | 69 ++++--- drivers/usb/serial/ti_usb_3410_5052.c | 203 ++++++++++--------- drivers/usb/serial/usb-serial.c | 82 ++++---- drivers/usb/serial/usb_debug.c | 5 +- drivers/usb/serial/visor.c | 66 ++----- drivers/usb/serial/whiteheat.c | 102 +++++----- include/linux/usb/serial.h | 56 +++--- 42 files changed, 1820 insertions(+), 1720 deletions(-) create mode 100644 drivers/usb/serial/airprime.c (limited to 'include/linux') diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index db6f97a93c02..79ea98c66fa8 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work) * 64 bytes, to ensure I do not get throttled. * Ask USB mailing list for better aproach. */ - tty = port->tty; + tty = port->port.tty; if (!tty) { schedule_work(&priv->rx_work); @@ -378,13 +378,14 @@ static void aircable_shutdown(struct usb_serial *serial) } } -static int aircable_write_room(struct usb_serial_port *port) +static int aircable_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); return serial_buf_data_avail(priv->tx_buf); } -static int aircable_write(struct usb_serial_port *port, +static int aircable_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *source, int count) { struct aircable_private *priv = usb_get_serial_port_data(port); @@ -466,7 +467,7 @@ static void aircable_read_bulk_callback(struct urb *urb) if (status) { dbg("%s - urb status = %d", __func__, status); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port is closed, exiting.", __func__); return; } @@ -494,7 +495,7 @@ static void aircable_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, urb->transfer_buffer); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { if (urb->actual_length <= 2) { /* This is an incomplete package */ @@ -528,7 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -547,8 +548,9 @@ static void aircable_read_bulk_callback(struct urb *urb) } /* Based on ftdi_sio.c throttle */ -static void aircable_throttle(struct usb_serial_port *port) +static void aircable_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -560,8 +562,9 @@ static void aircable_throttle(struct usb_serial_port *port) } /* Based on ftdi_sio.c unthrottle */ -static void aircable_unthrottle(struct usb_serial_port *port) +static void aircable_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c new file mode 100644 index 000000000000..b3f1d1e82468 --- /dev/null +++ b/drivers/usb/serial/airprime.c @@ -0,0 +1,355 @@ +/* + * AirPrime CDMA Wireless Serial USB driver + * + * Copyright (C) 2005-2006 Greg Kroah-Hartman + * + * 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 +#include +#include +#include +#include +#include +#include + +static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */ + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +#define URB_TRANSFER_BUFFER_SIZE 4096 +#define NUM_READ_URBS 4 +#define NUM_WRITE_URBS 4 +#define NUM_BULK_EPS 3 +#define MAX_BULK_EPS 6 + +/* if overridden by the user, then use their value for the size of the + * read and write urbs, and the number of endpoints */ +static int buffer_size = URB_TRANSFER_BUFFER_SIZE; +static int endpoints = NUM_BULK_EPS; +static int debug; +struct airprime_private { + spinlock_t lock; + int outstanding_urbs; + int throttled; + struct urb *read_urbp[NUM_READ_URBS]; + + /* Settings for the port */ + int rts_state; /* Handshaking pins (outputs) */ + int dtr_state; + int cts_state; /* Handshaking pins (inputs) */ + int dsr_state; + int dcd_state; + int ri_state; +}; + +static int airprime_send_setup(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct airprime_private *priv; + + dbg("%s", __func__); + + if (port->number != 0) + return 0; + + priv = usb_get_serial_port_data(port); + + if (port->port.tty) { + int val = 0; + if (priv->dtr_state) + val |= 0x01; + if (priv->rts_state) + val |= 0x02; + + return usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + } + + return 0; +} + +static void airprime_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + int result; + int status = urb->status; + + dbg("%s - port %d", __func__, port->number); + + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __func__, status); + return; + } + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + + tty = port->port.tty; + if (tty && urb->actual_length) { + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); + } + + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + return; +} + +static void airprime_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct airprime_private *priv = usb_get_serial_port_data(port); + int status = urb->status; + unsigned long flags; + + dbg("%s - port %d", __func__, port->number); + + /* free up the transfer buffer, as usb_free_urb() does not do this */ + kfree(urb->transfer_buffer); + + if (status) + dbg("%s - nonzero write bulk status received: %d", + __func__, status); + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + + usb_serial_port_softint(port); +} + +static int airprime_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct urb *urb; + char *buffer = NULL; + int i; + int result = 0; + + dbg("%s - port %d", __func__, port->number); + + /* initialize our private data structure if it isn't already created */ + if (!priv) { + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + result = -ENOMEM; + goto out; + } + spin_lock_init(&priv->lock); + usb_set_serial_port_data(port, priv); + } + + /* Set some sane defaults */ + priv->rts_state = 1; + priv->dtr_state = 1; + + for (i = 0; i < NUM_READ_URBS; ++i) { + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) { + dev_err(&port->dev, "%s - out of memory.\n", + __func__); + result = -ENOMEM; + goto errout; + } + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(buffer); + dev_err(&port->dev, "%s - no more urbs?\n", + __func__); + result = -ENOMEM; + goto errout; + } + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, buffer_size, + airprime_read_bulk_callback, port); + result = usb_submit_urb(urb, GFP_KERNEL); + if (result) { + usb_free_urb(urb); + kfree(buffer); + dev_err(&port->dev, + "%s - failed submitting read urb %d for port %d, error %d\n", + __func__, i, port->number, result); + goto errout; + } + /* remember this urb so we can kill it when the + port is closed */ + priv->read_urbp[i] = urb; + } + + airprime_send_setup(port); + + goto out; + + errout: + /* some error happened, cancel any submitted urbs and clean up + anything that got allocated successfully */ + + while (i-- != 0) { + urb = priv->read_urbp[i]; + buffer = urb->transfer_buffer; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(buffer); + } + + out: + return result; +} + +static void airprime_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + int i; + + dbg("%s - port %d", __func__, port->number); + + priv->rts_state = 0; + priv->dtr_state = 0; + + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) + airprime_send_setup(port); + mutex_unlock(&port->serial->disc_mutex); + + for (i = 0; i < NUM_READ_URBS; ++i) { + usb_kill_urb(priv->read_urbp[i]); + kfree(priv->read_urbp[i]->transfer_buffer); + usb_free_urb(priv->read_urbp[i]); + } + + /* free up private structure */ + kfree(priv); + usb_set_serial_port_data(port, NULL); +} + +static int airprime_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct urb *urb; + unsigned char *buffer; + unsigned long flags; + int status; + dbg("%s - port %d", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + if (priv->outstanding_urbs > NUM_WRITE_URBS) { + spin_unlock_irqrestore(&priv->lock, flags); + dbg("%s - write limit hit\n", __func__); + return 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + buffer = kmalloc(count, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, "out of memory\n"); + return -ENOMEM; + } + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "no more free urbs\n"); + kfree(buffer); + return -ENOMEM; + } + memcpy(buffer, buf, count); + + usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); + + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, count, + airprime_write_bulk_callback, port); + + /* send it down the pipe */ + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, + "%s - usb_submit_urb(write bulk) failed with status = %d\n", + __func__, status); + count = status; + kfree(buffer); + } else { + spin_lock_irqsave(&priv->lock, flags); + ++priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + } + /* we are done with this urb, so let the host driver + * really free it when it is finished with it */ + usb_free_urb(urb); + return count; +} + +static struct usb_driver airprime_driver = { + .name = "airprime", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static struct usb_serial_driver airprime_device = { + .driver = { + .owner = THIS_MODULE, + .name = "airprime", + }, + .usb_driver = &airprime_driver, + .id_table = id_table, + .open = airprime_open, + .close = airprime_close, + .write = airprime_write, +}; + +static int __init airprime_init(void) +{ + int retval; + + airprime_device.num_ports = endpoints; + if (endpoints < 0 || endpoints >= MAX_BULK_EPS) + airprime_device.num_ports = NUM_BULK_EPS; + + retval = usb_serial_register(&airprime_device); + if (retval) + return retval; + retval = usb_register(&airprime_driver); + if (retval) + usb_serial_deregister(&airprime_device); + return retval; +} + +static void __exit airprime_exit(void) +{ + dbg("%s", __func__); + + usb_deregister(&airprime_driver); + usb_serial_deregister(&airprime_device); +} + +module_init(airprime_init); +module_exit(airprime_exit); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled"); +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, + "Size of the transfer buffers in bytes (default 4096)"); +module_param(endpoints, int, 0); +MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)"); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 77895c8f8f31..aec61880f36c 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -158,12 +158,13 @@ cleanup: return -ENOMEM; } -static void ark3116_set_termios(struct usb_serial_port *port, +static void ark3116_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct ark3116_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; unsigned long flags; int baud; @@ -177,8 +178,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 + *termios = tty_std_termios; + termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; termios->c_ispeed = 9600; termios->c_ospeed = 9600; @@ -192,7 +193,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, buf = kmalloc(1, GFP_KERNEL); if (!buf) { dbg("error kmalloc"); - *port->tty->termios = *old_termios; + *termios = *old_termios; return; } @@ -243,7 +244,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, } /* set baudrate */ - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); switch (baud) { case 75: @@ -262,11 +263,11 @@ static void ark3116_set_termios(struct usb_serial_port *port, case 230400: case 460800: /* Report the resulting rate back to the caller */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); break; /* set 9600 as default (if given baudrate is invalid for example) */ default: - tty_encode_baud_rate(port->tty, 9600, 9600); + tty_encode_baud_rate(tty, 9600, 9600); case 0: baud = 9600; } @@ -317,7 +318,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, return; } -static int ark3116_open(struct usb_serial_port *port, struct file *filp) +static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -332,7 +334,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) return -ENOMEM; } - result = usb_serial_generic_open(port, filp); + result = usb_serial_generic_open(tty, port, filp); if (result) goto err_out; @@ -362,8 +364,8 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); /* initialise termios */ - if (port->tty) - ark3116_set_termios(port, &tmp_termios); + if (tty) + ark3116_set_termios(tty, port, &tmp_termios); err_out: kfree(buf); @@ -371,9 +373,10 @@ err_out: return result; } -static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, +static int ark3116_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -403,8 +406,9 @@ static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file) +static int ark3116_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; char *buf; char temp; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 0a322fc53d6e..1a762692c739 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -89,14 +89,13 @@ static int debug; /* function prototypes for a Belkin USB Serial Adapter F5U103 */ static int belkin_sa_startup (struct usb_serial *serial); static void belkin_sa_shutdown (struct usb_serial *serial); -static int belkin_sa_open (struct usb_serial_port *port, struct file *filp); -static void belkin_sa_close (struct usb_serial_port *port, struct file *filp); +static int belkin_sa_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void belkin_sa_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void belkin_sa_read_int_callback (struct urb *urb); -static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void belkin_sa_break_ctl (struct usb_serial_port *port, int break_state ); -static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file); -static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); +static void belkin_sa_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static void belkin_sa_break_ctl (struct tty_struct *tty, int break_state ); +static int belkin_sa_tiocmget (struct tty_struct *tty, struct file *file); +static int belkin_sa_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static struct usb_device_id id_table_combined [] = { @@ -132,7 +131,6 @@ static struct usb_serial_driver belkin_device = { .open = belkin_sa_open, .close = belkin_sa_close, .read_int_callback = belkin_sa_read_int_callback, /* How we get the status info */ - .ioctl = belkin_sa_ioctl, .set_termios = belkin_sa_set_termios, .break_ctl = belkin_sa_break_ctl, .tiocmget = belkin_sa_tiocmget, @@ -190,7 +188,7 @@ static int belkin_sa_startup (struct usb_serial *serial) } -static void belkin_sa_shutdown (struct usb_serial *serial) +static void belkin_sa_shutdown(struct usb_serial *serial) { struct belkin_sa_private *priv; int i; @@ -206,7 +204,7 @@ static void belkin_sa_shutdown (struct usb_serial *serial) } -static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) +static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { int retval = 0; @@ -235,7 +233,8 @@ exit: } /* belkin_sa_open */ -static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) +static void belkin_sa_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s port %d", __func__, port->number); @@ -246,7 +245,7 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) } /* belkin_sa_close */ -static void belkin_sa_read_int_callback (struct urb *urb) +static void belkin_sa_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct belkin_sa_private *priv; @@ -311,7 +310,7 @@ static void belkin_sa_read_int_callback (struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = port->tty; + tty = port->port.tty; /* Overrun Error */ if (priv->last_lsr & BELKIN_SA_LSR_OE) { } @@ -334,7 +333,8 @@ exit: __func__, retval); } -static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void belkin_sa_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = usb_get_serial_port_data(port); @@ -347,7 +347,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios unsigned long control_state; int bad_flow_control; speed_t baud; - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; iflag = termios->c_iflag; cflag = termios->c_cflag; @@ -377,7 +377,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } } - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); if (baud) { urb_value = BELKIN_SA_BAUD(baud); /* Clip to maximum speed */ @@ -387,7 +387,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios baud = BELKIN_SA_BAUD(urb_value); /* Report the actual baud rate back to the caller */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) err("Set baudrate error"); } else { @@ -463,8 +463,9 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } /* belkin_sa_set_termios */ -static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) +static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) @@ -472,8 +473,9 @@ static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) } -static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file) +static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; unsigned long flags; @@ -488,9 +490,10 @@ static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file) } -static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, +static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; @@ -540,29 +543,7 @@ exit: } -static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return( 0 ); - - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - - default: - dbg("belkin_sa_ioctl arg not supported - 0x%04x",cmd); - return(-ENOIOCTLCMD); - break; - } - return 0; -} /* belkin_sa_ioctl */ - - -static int __init belkin_sa_init (void) +static int __init belkin_sa_init(void) { int retval; retval = usb_serial_register(&belkin_device); @@ -583,7 +564,7 @@ failed_usb_serial_register: static void __exit belkin_sa_exit (void) { usb_deregister (&belkin_driver); - usb_serial_deregister (&belkin_device); + usb_serial_deregister(&belkin_device); } diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 1f7c86bd8297..f61e3ca64305 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -232,7 +232,8 @@ error: kfree(priv); } /* open this device, set default parameters */ -static int ch341_open(struct usb_serial_port *port, struct file *filp) +static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]); @@ -256,7 +257,7 @@ static int ch341_open(struct usb_serial_port *port, struct file *filp) if (r) goto out; - r = usb_serial_generic_open(port, filp); + r = usb_serial_generic_open(tty, port, filp); out: return r; } @@ -264,11 +265,10 @@ out: return r; /* Old_termios contains the original termios settings and * tty->termios contains the new setting to be used. */ -static void ch341_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ch341_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ch341_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned baud_rate; dbg("ch341_set_termios()"); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 201184c3fb87..940f5de68980 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -145,12 +145,12 @@ static int usb_console_setup(struct console *co, char *options) } port = serial->port[0]; - port->tty = NULL; + port->port.tty = NULL; info->port = port; - ++port->open_count; - if (port->open_count == 1) { + ++port->port.count; + if (port->port.count == 1) { if (serial->type->set_termios) { /* * allocate a fake tty so the driver can initialize @@ -171,15 +171,15 @@ static int usb_console_setup(struct console *co, char *options) } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; - port->tty = tty; + port->port.tty = tty; } /* only call the device specific open if this * is the first time the port is opened */ if (serial->type->open) - retval = serial->type->open(port, NULL); + retval = serial->type->open(NULL, port, NULL); else - retval = usb_serial_generic_open(port, NULL); + retval = usb_serial_generic_open(NULL, port, NULL); if (retval) { err("could not open USB console port"); @@ -188,9 +188,9 @@ static int usb_console_setup(struct console *co, char *options) if (serial->type->set_termios) { termios->c_cflag = cflag; - serial->type->set_termios(port, &dummy); + serial->type->set_termios(NULL, port, &dummy); - port->tty = NULL; + port->port.tty = NULL; kfree(termios); kfree(tty); } @@ -203,11 +203,11 @@ out: return retval; free_termios: kfree(termios); - port->tty = NULL; + port->port.tty = NULL; free_tty: kfree(tty); reset_open_count: - port->open_count = 0; + port->port.count = 0; goto out; } @@ -227,7 +227,7 @@ static void usb_console_write(struct console *co, const char *buf, unsigned coun dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->open_count) { + if (!port->port.count) { dbg ("%s - port not opened", __func__); return; } @@ -245,17 +245,17 @@ static void usb_console_write(struct console *co, const char *buf, unsigned coun } /* pass on to the driver specific version of this function if it is available */ if (serial->type->write) - retval = serial->type->write(port, buf, i); + retval = serial->type->write(NULL, port, buf, i); else - retval = usb_serial_generic_write(port, buf, i); + retval = usb_serial_generic_write(NULL, port, buf, i); dbg("%s - return value : %d", __func__, retval); if (lf) { /* append CR after LF */ unsigned char cr = 13; if (serial->type->write) - retval = serial->type->write(port, &cr, 1); + retval = serial->type->write(NULL, port, &cr, 1); else - retval = usb_serial_generic_write(port, &cr, 1); + retval = usb_serial_generic_write(NULL, port, &cr, 1); dbg("%s - return value : %d", __func__, retval); } buf += i; @@ -306,8 +306,8 @@ void usb_serial_console_exit (void) { if (usbcons_info.port) { unregister_console(&usbcons); - if (usbcons_info.port->open_count) - usbcons_info.port->open_count--; + if (usbcons_info.port->port.count) + usbcons_info.port->port.count--; usbcons_info.port = NULL; } } diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 2bc5576c443a..46c33fc9f6ce 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -37,15 +37,18 @@ /* * Function Prototypes */ -static int cp2101_open(struct usb_serial_port*, struct file*); -static void cp2101_cleanup(struct usb_serial_port*); -static void cp2101_close(struct usb_serial_port*, struct file*); -static void cp2101_get_termios(struct usb_serial_port*); -static void cp2101_set_termios(struct usb_serial_port*, struct ktermios*); -static int cp2101_tiocmget (struct usb_serial_port *, struct file *); -static int cp2101_tiocmset (struct usb_serial_port *, struct file *, +static int cp2101_open(struct tty_struct *, struct usb_serial_port *, + struct file *); +static void cp2101_cleanup(struct usb_serial_port *); +static void cp2101_close(struct tty_struct *, struct usb_serial_port *, + struct file*); +static void cp2101_get_termios(struct tty_struct *); +static void cp2101_set_termios(struct tty_struct *, struct usb_serial_port *, + struct ktermios*); +static int cp2101_tiocmget (struct tty_struct *, struct file *); +static int cp2101_tiocmset (struct tty_struct *, struct file *, unsigned int, unsigned int); -static void cp2101_break_ctl(struct usb_serial_port*, int); +static void cp2101_break_ctl(struct tty_struct *, int); static int cp2101_startup (struct usb_serial *); static void cp2101_shutdown(struct usb_serial*); @@ -182,7 +185,7 @@ static struct usb_serial_driver cp2101_device = { * 'data' is a pointer to a pre-allocated array of integers large * enough to hold 'size' bytes (with 4 bytes to each integer) */ -static int cp2101_get_config(struct usb_serial_port* port, u8 request, +static int cp2101_get_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; @@ -228,7 +231,7 @@ static int cp2101_get_config(struct usb_serial_port* port, u8 request, * Values less than 16 bits wide are sent directly * 'size' is specified in bytes. */ -static int cp2101_set_config(struct usb_serial_port* port, u8 request, +static int cp2101_set_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; @@ -283,13 +286,14 @@ static int cp2101_set_config(struct usb_serial_port* port, u8 request, * Convenience function for calling cp2101_set_config on single data values * without requiring an integer pointer */ -static inline int cp2101_set_config_single(struct usb_serial_port* port, +static inline int cp2101_set_config_single(struct usb_serial_port *port, u8 request, unsigned int data) { return cp2101_set_config(port, request, &data, 2); } -static int cp2101_open (struct usb_serial_port *port, struct file *filp) +static int cp2101_open (struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; int result; @@ -318,10 +322,10 @@ static int cp2101_open (struct usb_serial_port *port, struct file *filp) } /* Configure the termios structure */ - cp2101_get_termios(port); + cp2101_get_termios(tty); /* Set the DTR and RTS pins low */ - cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0); + cp2101_tiocmset(tty, NULL, TIOCM_DTR | TIOCM_RTS, 0); return 0; } @@ -341,7 +345,8 @@ static void cp2101_cleanup (struct usb_serial_port *port) } } -static void cp2101_close (struct usb_serial_port *port, struct file * filp) +static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file * filp) { dbg("%s - port %d", __func__, port->number); @@ -362,19 +367,15 @@ static void cp2101_close (struct usb_serial_port *port, struct file * filp) * from the device, corrects any unsupported values, and configures the * termios structure to reflect the state of the device */ -static void cp2101_get_termios (struct usb_serial_port *port) +static void cp2101_get_termios (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned int cflag, modem_ctl[4]; unsigned int baud; unsigned int bits; dbg("%s - port %d", __func__, port->number); - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __func__); - return; - } - cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2); /* Convert to baudrate */ if (baud) @@ -382,8 +383,8 @@ static void cp2101_get_termios (struct usb_serial_port *port) dbg("%s - baud rate = %d", __func__, baud); - tty_encode_baud_rate(port->tty, baud, baud); - cflag = port->tty->termios->c_cflag; + tty_encode_baud_rate(tty, baud, baud); + cflag = tty->termios->c_cflag; cp2101_get_config(port, CP2101_BITS, &bits, 2); cflag &= ~CSIZE; @@ -491,11 +492,11 @@ static void cp2101_get_termios (struct usb_serial_port *port) cflag &= ~CRTSCTS; } - port->tty->termios->c_cflag = cflag; + tty->termios->c_cflag = cflag; } -static void cp2101_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cp2101_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; unsigned int baud = 0, bits; @@ -503,15 +504,13 @@ static void cp2101_set_termios (struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __func__); + if (!tty) return; - } - port->tty->termios->c_cflag &= ~CMSPAR; - cflag = port->tty->termios->c_cflag; + tty->termios->c_cflag &= ~CMSPAR; + cflag = tty->termios->c_cflag; old_cflag = old_termios->c_cflag; - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); /* If the baud rate is to be updated*/ if (baud != tty_termios_baud_rate(old_termios)) { @@ -554,7 +553,7 @@ static void cp2101_set_termios (struct usb_serial_port *port, } } /* Report back the resulting baud rate */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { @@ -651,9 +650,10 @@ static void cp2101_set_termios (struct usb_serial_port *port, } -static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, +static int cp2101_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; unsigned int control = 0; dbg("%s - port %d", __func__, port->number); @@ -681,8 +681,9 @@ static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, } -static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) +static int cp2101_tiocmget (struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int control; int result; @@ -702,8 +703,9 @@ static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) return result; } -static void cp2101_break_ctl (struct usb_serial_port *port, int break_state) +static void cp2101_break_ctl (struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned int state; dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index c164e2cf2752..546178ea6f2d 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -57,15 +57,18 @@ static int debug; #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ -static int cyberjack_startup (struct usb_serial *serial); -static void cyberjack_shutdown (struct usb_serial *serial); -static int cyberjack_open (struct usb_serial_port *port, struct file *filp); -static void cyberjack_close (struct usb_serial_port *port, struct file *filp); -static int cyberjack_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int cyberjack_write_room( struct usb_serial_port *port ); -static void cyberjack_read_int_callback (struct urb *urb); -static void cyberjack_read_bulk_callback (struct urb *urb); -static void cyberjack_write_bulk_callback (struct urb *urb); +static int cyberjack_startup(struct usb_serial *serial); +static void cyberjack_shutdown(struct usb_serial *serial); +static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void cyberjack_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int cyberjack_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +static int cyberjack_write_room( struct tty_struct *tty); +static void cyberjack_read_int_callback(struct urb *urb); +static void cyberjack_read_bulk_callback(struct urb *urb); +static void cyberjack_write_bulk_callback(struct urb *urb); static struct usb_device_id id_table [] = { { USB_DEVICE(CYBERJACK_VENDOR_ID, CYBERJACK_PRODUCT_ID) }, @@ -111,7 +114,7 @@ struct cyberjack_private { }; /* do some startup allocations not currently performed by usb_serial_probe() */ -static int cyberjack_startup (struct usb_serial *serial) +static int cyberjack_startup(struct usb_serial *serial) { struct cyberjack_private *priv; int i; @@ -145,7 +148,7 @@ static int cyberjack_startup (struct usb_serial *serial) return( 0 ); } -static void cyberjack_shutdown (struct usb_serial *serial) +static void cyberjack_shutdown(struct usb_serial *serial) { int i; @@ -159,7 +162,8 @@ static void cyberjack_shutdown (struct usb_serial *serial) } } -static int cyberjack_open (struct usb_serial_port *port, struct file *filp) +static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cyberjack_private *priv; unsigned long flags; @@ -174,7 +178,8 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp) * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); @@ -186,7 +191,8 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp) return result; } -static void cyberjack_close (struct usb_serial_port *port, struct file *filp) +static void cyberjack_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -197,7 +203,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp) } } -static int cyberjack_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int cyberjack_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -292,13 +299,13 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b return (count); } -static int cyberjack_write_room( struct usb_serial_port *port ) +static int cyberjack_write_room(struct tty_struct *tty) { /* FIXME: .... */ return CYBERJACK_LOCAL_BUF_SIZE; } -static void cyberjack_read_int_callback( struct urb *urb ) +static void cyberjack_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -355,7 +362,7 @@ resubmit: dbg("%s - usb_submit_urb(int urb)", __func__); } -static void cyberjack_read_bulk_callback (struct urb *urb) +static void cyberjack_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -374,7 +381,7 @@ static void cyberjack_read_bulk_callback (struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - ignoring since device not open\n", __func__); return; @@ -407,7 +414,7 @@ static void cyberjack_read_bulk_callback (struct urb *urb) } } -static void cyberjack_write_bulk_callback (struct urb *urb) +static void cyberjack_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 0230d3c0888a..6999d3372d85 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -167,18 +167,18 @@ static int cypress_earthmate_startup (struct usb_serial *serial); static int cypress_hidcom_startup (struct usb_serial *serial); static int cypress_ca42v2_startup (struct usb_serial *serial); static void cypress_shutdown (struct usb_serial *serial); -static int cypress_open (struct usb_serial_port *port, struct file *filp); -static void cypress_close (struct usb_serial_port *port, struct file *filp); -static int cypress_write (struct usb_serial_port *port, const unsigned char *buf, int count); +static int cypress_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void cypress_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int cypress_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void cypress_send (struct usb_serial_port *port); -static int cypress_write_room (struct usb_serial_port *port); -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void cypress_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file); -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static int cypress_chars_in_buffer (struct usb_serial_port *port); -static void cypress_throttle (struct usb_serial_port *port); -static void cypress_unthrottle (struct usb_serial_port *port); +static int cypress_write_room (struct tty_struct *tty); +static int cypress_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void cypress_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static int cypress_tiocmget (struct tty_struct *tty, struct file *file); +static int cypress_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +static int cypress_chars_in_buffer (struct tty_struct *tty); +static void cypress_throttle (struct tty_struct *tty); +static void cypress_unthrottle (struct tty_struct *tty); static void cypress_set_dead (struct usb_serial_port *port); static void cypress_read_int_callback (struct urb *urb); static void cypress_write_int_callback (struct urb *urb); @@ -322,8 +322,10 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) /* This function can either set or retrieve the current serial line settings */ -static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_rate, int data_bits, int stop_bits, - int parity_enable, int parity_type, int reset, int cypress_request_type) +static int cypress_serial_control (struct tty_struct *tty, + struct usb_serial_port *port, speed_t baud_rate, int data_bits, + int stop_bits, int parity_enable, int parity_type, int reset, + int cypress_request_type) { int new_baudrate = 0, retval = 0, tries = 0; struct cypress_private *priv; @@ -395,7 +397,7 @@ static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_ra spin_unlock_irqrestore(&priv->lock, flags); /* If we asked for a speed change encode it */ if (baud_rate) - tty_encode_baud_rate(port->tty, + tty_encode_baud_rate(tty, new_baudrate, new_baudrate); } break; @@ -611,7 +613,8 @@ static void cypress_shutdown (struct usb_serial *serial) } -static int cypress_open (struct usb_serial_port *port, struct file *filp) +static int cypress_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cypress_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -636,14 +639,15 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) spin_unlock_irqrestore(&priv->lock, flags); /* setting to zero could cause data loss */ - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* raise both lines and set termios */ spin_lock_irqsave(&priv->lock, flags); priv->line_control = CONTROL_DTR | CONTROL_RTS; priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - result = cypress_write(port, NULL, 0); + result = cypress_write(tty, port, NULL, 0); if (result) { dev_err(&port->dev, "%s - failed setting the control lines - error %d\n", __func__, result); @@ -651,7 +655,8 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) } else dbg("%s - success setting the control lines", __func__); - cypress_set_termios(port, &priv->tmp_termios); + if (tty) + cypress_set_termios(tty, port, &priv->tmp_termios); /* setup the port and start reading from the device */ if(!port->interrupt_in_urb){ @@ -674,7 +679,8 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) } /* cypress_open */ -static void cypress_close(struct usb_serial_port *port, struct file * filp) +static void cypress_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned int c_cflag; @@ -688,7 +694,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) spin_lock_irq(&priv->lock); timeout = CYPRESS_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (cypress_buf_data_avail(priv->buf) == 0 @@ -701,7 +707,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) spin_lock_irq(&priv->lock); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ cypress_buf_clear(priv->buf); spin_unlock_irq(&priv->lock); @@ -713,19 +719,21 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) return; } /* wait for characters to drain from device */ - bps = tty_get_baud_rate(port->tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); - else - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); + if (tty) { + bps = tty_get_baud_rate(tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + schedule_timeout_interruptible(timeout); + } dbg("%s - stopping urbs", __func__); usb_kill_urb (port->interrupt_in_urb); usb_kill_urb (port->interrupt_out_urb); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop dtr and rts */ priv = usb_get_serial_port_data(port); @@ -733,7 +741,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) priv->line_control = 0; priv->cmd_ctrl = 1; spin_unlock_irq(&priv->lock); - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } @@ -744,7 +752,8 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) } /* cypress_close */ -static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -878,8 +887,9 @@ send: /* returns how much space is available in the soft buffer */ -static int cypress_write_room(struct usb_serial_port *port) +static int cypress_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -895,8 +905,9 @@ static int cypress_write_room(struct usb_serial_port *port) } -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) +static int cypress_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); __u8 status, control; unsigned int result = 0; @@ -922,9 +933,10 @@ static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) } -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, +static int cypress_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -942,12 +954,14 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - return cypress_write(port, NULL, 0); + return cypress_write(tty, port, NULL, 0); } -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int cypress_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); @@ -983,22 +997,18 @@ static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsi } } return 0; - break; default: break; } - dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __func__, cmd); - return -ENOIOCTLCMD; } /* cypress_ioctl */ -static void cypress_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cypress_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct cypress_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty; int data_bits, stop_bits, parity_type, parity_enable; unsigned cflag, iflag; unsigned long flags; @@ -1007,8 +1017,6 @@ static void cypress_set_termios (struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - tty = port->tty; - spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { @@ -1096,13 +1104,13 @@ static void cypress_set_termios (struct usb_serial_port *port, "%d data_bits (+5)", __func__, stop_bits, parity_enable, parity_type, data_bits); - cypress_serial_control(port, tty_get_baud_rate(tty), data_bits, stop_bits, + cypress_serial_control(tty, port, tty_get_baud_rate(tty), data_bits, stop_bits, parity_enable, parity_type, 0, CYPRESS_SET_CONFIG); /* we perform a CYPRESS_GET_CONFIG so that the current settings are * filled into the private structure this should confirm that all is * working if it returns what we just set */ - cypress_serial_control(port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); + cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); /* Here we can define custom tty settings for devices; the main tty * termios flag base comes from empeg.c */ @@ -1142,14 +1150,15 @@ static void cypress_set_termios (struct usb_serial_port *port, /* if necessary, set lines */ if (linechange) { priv->cmd_ctrl = 1; - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } /* cypress_set_termios */ /* returns amount of data still left in soft buffer */ -static int cypress_chars_in_buffer(struct usb_serial_port *port) +static int cypress_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -1165,8 +1174,9 @@ static int cypress_chars_in_buffer(struct usb_serial_port *port) } -static void cypress_throttle (struct usb_serial_port *port) +static void cypress_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -1178,8 +1188,9 @@ static void cypress_throttle (struct usb_serial_port *port) } -static void cypress_unthrottle (struct usb_serial_port *port) +static void cypress_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int actually_throttled, result; unsigned long flags; @@ -1251,7 +1262,7 @@ static void cypress_read_int_callback(struct urb *urb) } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1327,7 +1338,7 @@ static void cypress_read_int_callback(struct urb *urb) data[i]); tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push(port->tty); + tty_flip_buffer_push(port->port.tty); } spin_lock_irqsave(&priv->lock, flags); @@ -1339,7 +1350,7 @@ continue_read: /* Continue trying to always read... unless the port has closed. */ - if (port->open_count > 0 && priv->comm_is_ok) { + if (port->port.count > 0 && priv->comm_is_ok) { usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress), diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 28bc6fcf44f0..dc92dbfc962e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -15,7 +15,7 @@ * Al Borchers (borchers@steinerpoint.com) * * (12/03/2001) gkh -* switched to using port->open_count instead of private version. +* switched to using port->port.count instead of private version. * Removed port->active * * (04/08/2001) gb @@ -441,22 +441,23 @@ static int digi_set_modem_signals(struct usb_serial_port *port, unsigned int modem_signals, int interruptible); static int digi_transmit_idle(struct usb_serial_port *port, unsigned long timeout); -static void digi_rx_throttle (struct usb_serial_port *port); -static void digi_rx_unthrottle (struct usb_serial_port *port); -static void digi_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); -static void digi_break_ctl(struct usb_serial_port *port, int break_state); -static int digi_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static int digi_tiocmget(struct usb_serial_port *port, struct file *file); -static int digi_tiocmset(struct usb_serial_port *port, struct file *file, +static void digi_rx_throttle (struct tty_struct *tty); +static void digi_rx_unthrottle (struct tty_struct *tty); +static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void digi_break_ctl(struct tty_struct *tty, int break_state); +static int digi_tiocmget(struct tty_struct *tty, struct file *file); +static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int digi_write(struct usb_serial_port *port, const unsigned char *buf, int count); +static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); static void digi_write_bulk_callback(struct urb *urb); -static int digi_write_room(struct usb_serial_port *port); -static int digi_chars_in_buffer(struct usb_serial_port *port); -static int digi_open(struct usb_serial_port *port, struct file *filp); -static void digi_close(struct usb_serial_port *port, struct file *filp); +static int digi_write_room(struct tty_struct *tty); +static int digi_chars_in_buffer(struct tty_struct *tty); +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); static void digi_shutdown(struct usb_serial *serial); @@ -516,7 +517,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, - .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, @@ -543,7 +543,6 @@ static struct usb_serial_driver digi_acceleport_4_device = { .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, - .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, @@ -604,7 +603,7 @@ static void digi_wakeup_write_lock(struct work_struct *work) static void digi_wakeup_write(struct usb_serial_port *port) { - tty_wakeup(port->tty); + tty_wakeup(port->port.tty); } @@ -856,9 +855,10 @@ static int digi_transmit_idle(struct usb_serial_port *port, } -static void digi_rx_throttle(struct usb_serial_port *port) +static void digi_rx_throttle(struct tty_struct *tty) { unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); @@ -872,10 +872,11 @@ static void digi_rx_throttle(struct usb_serial_port *port) } -static void digi_rx_unthrottle(struct usb_serial_port *port) +static void digi_rx_unthrottle(struct tty_struct *tty) { int ret = 0; unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); dbg("digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num); @@ -900,12 +901,10 @@ static void digi_rx_unthrottle(struct usb_serial_port *port) } -static void digi_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - struct digi_port *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int iflag = tty->termios->c_iflag; unsigned int cflag = tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; @@ -1088,8 +1087,9 @@ static void digi_set_termios(struct usb_serial_port *port, } -static void digi_break_ctl(struct usb_serial_port *port, int break_state) +static void digi_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char buf[4]; buf[0] = DIGI_CMD_BREAK_CONTROL; @@ -1100,8 +1100,9 @@ static void digi_break_ctl(struct usb_serial_port *port, int break_state) } -static int digi_tiocmget(struct usb_serial_port *port, struct file *file) +static int digi_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); unsigned int val; unsigned long flags; @@ -1115,9 +1116,10 @@ static int digi_tiocmget(struct usb_serial_port *port, struct file *file) } -static int digi_tiocmset(struct usb_serial_port *port, struct file *file, +static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); unsigned int val; unsigned long flags; @@ -1131,27 +1133,8 @@ static int digi_tiocmset(struct usb_serial_port *port, struct file *file, } -static int digi_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct digi_port *priv = usb_get_serial_port_data(port); - dbg("digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd); - - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return 0; - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - } - return -ENOIOCTLCMD; - -} - -static int digi_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { int ret,data_len,new_len; @@ -1261,7 +1244,7 @@ static void digi_write_bulk_callback(struct urb *urb) /* try to send any buffered data on this port, if it is open */ spin_lock(&priv->dp_port_lock); priv->dp_write_urb_in_use = 0; - if (port->open_count && port->write_urb->status != -EINPROGRESS + if (port->port.count && port->write_urb->status != -EINPROGRESS && priv->dp_out_buf_len > 0) { *((unsigned char *)(port->write_urb->transfer_buffer)) = (unsigned char)DIGI_CMD_SEND_DATA; @@ -1288,11 +1271,11 @@ static void digi_write_bulk_callback(struct urb *urb) __func__, ret, priv->dp_port_num); } -static int digi_write_room(struct usb_serial_port *port) +static int digi_write_room(struct tty_struct *tty) { - - int room; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); + int room; unsigned long flags = 0; spin_lock_irqsave(&priv->dp_port_lock, flags); @@ -1308,12 +1291,11 @@ static int digi_write_room(struct usb_serial_port *port) } -static int digi_chars_in_buffer(struct usb_serial_port *port) +static int digi_chars_in_buffer(struct tty_struct *tty) { - + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); - if (port->write_urb->status == -EINPROGRESS || priv->dp_write_urb_in_use) { dbg("digi_chars_in_buffer: port=%d, chars=%d", @@ -1329,7 +1311,8 @@ static int digi_chars_in_buffer(struct usb_serial_port *port) } -static int digi_open(struct usb_serial_port *port, struct file *filp) +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { int ret; unsigned char buf[32]; @@ -1338,7 +1321,7 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) unsigned long flags = 0; dbg("digi_open: TOP: port=%d, open_count=%d", - priv->dp_port_num, port->open_count); + priv->dp_port_num, port->port.count); /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1380,9 +1363,11 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) dbg("digi_open: write oob failed, ret=%d", ret); /* set termios settings */ - not_termios.c_cflag = ~port->tty->termios->c_cflag; - not_termios.c_iflag = ~port->tty->termios->c_iflag; - digi_set_termios(port, ¬_termios); + if (tty) { + not_termios.c_cflag = ~tty->termios->c_cflag; + not_termios.c_iflag = ~tty->termios->c_iflag; + digi_set_termios(tty, port, ¬_termios); + } /* set DTR and RTS */ digi_set_modem_signals(port, TIOCM_DTR|TIOCM_RTS, 1); @@ -1391,16 +1376,16 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) } -static void digi_close(struct usb_serial_port *port, struct file *filp) +static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { DEFINE_WAIT(wait); int ret; unsigned char buf[32]; - struct tty_struct *tty = port->tty; struct digi_port *priv = usb_get_serial_port_data(port); dbg("digi_close: TOP: port=%d, open_count=%d", - priv->dp_port_num, port->open_count); + priv->dp_port_num, port->port.count); mutex_lock(&port->serial->disc_mutex); /* if disconnected, just clear flags */ @@ -1663,7 +1648,7 @@ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; @@ -1675,7 +1660,7 @@ static int digi_read_inb_callback(struct urb *urb) /* do not process callbacks on closed ports */ /* but do continue the read chain */ - if (port->open_count == 0) + if (port->port.count == 0) return 0; /* short/multiple packet check */ @@ -1785,17 +1770,17 @@ static int digi_read_oob_callback(struct urb *urb) if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ - if (port->open_count - && port->tty->termios->c_cflag & CRTSCTS) { - port->tty->hw_stopped = 0; + if (port->port.count + && port->port.tty->termios->c_cflag & CRTSCTS) { + port->port.tty->hw_stopped = 0; digi_wakeup_write(port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ - if (port->open_count - && port->tty->termios->c_cflag & CRTSCTS) { - port->tty->hw_stopped = 1; + if (port->port.count + && port->port.tty->termios->c_cflag & CRTSCTS) { + port->port.tty->hw_stopped = 1; } } if (val & DIGI_READ_INPUT_SIGNALS_DSR) diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index c5ec309a3cb1..47ebdf5fad8e 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -31,7 +31,7 @@ * Moved MOD_DEC_USE_COUNT to end of empeg_close(). * * (12/03/2000) gb - * Added port->tty->ldisc.set_termios(port->tty, NULL) to empeg_open() + * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to empeg_open() * This notifies the tty driver that the termios have changed. * * (11/13/2000) gb @@ -77,22 +77,18 @@ static int debug; #define EMPEG_PRODUCT_ID 0x0001 /* function prototypes for an empeg-car player */ -static int empeg_open (struct usb_serial_port *port, struct file *filp); -static void empeg_close (struct usb_serial_port *port, struct file *filp); -static int empeg_write (struct usb_serial_port *port, +static int empeg_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void empeg_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int empeg_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int empeg_write_room (struct usb_serial_port *port); -static int empeg_chars_in_buffer (struct usb_serial_port *port); -static void empeg_throttle (struct usb_serial_port *port); -static void empeg_unthrottle (struct usb_serial_port *port); +static int empeg_write_room (struct tty_struct *tty); +static int empeg_chars_in_buffer (struct tty_struct *tty); +static void empeg_throttle (struct tty_struct *tty); +static void empeg_unthrottle (struct tty_struct *tty); static int empeg_startup (struct usb_serial *serial); static void empeg_shutdown (struct usb_serial *serial); -static int empeg_ioctl (struct usb_serial_port *port, - struct file * file, - unsigned int cmd, - unsigned long arg); -static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +static void empeg_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static void empeg_write_bulk_callback (struct urb *urb); static void empeg_read_bulk_callback (struct urb *urb); @@ -125,7 +121,6 @@ static struct usb_serial_driver empeg_device = { .unthrottle = empeg_unthrottle, .attach = empeg_startup, .shutdown = empeg_shutdown, - .ioctl = empeg_ioctl, .set_termios = empeg_set_termios, .write = empeg_write, .write_room = empeg_write_room, @@ -145,7 +140,8 @@ static int bytes_out; /****************************************************************************** * Empeg specific driver functions ******************************************************************************/ -static int empeg_open (struct usb_serial_port *port, struct file *filp) +static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; @@ -153,7 +149,7 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); /* Force default termio settings */ - empeg_set_termios (port, NULL) ; + empeg_set_termios (tty, port, NULL) ; bytes_in = 0; bytes_out = 0; @@ -178,7 +174,8 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) } -static void empeg_close (struct usb_serial_port *port, struct file * filp) +static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file * filp) { dbg("%s - port %d", __func__, port->number); @@ -189,7 +186,7 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp) } -static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct urb *urb; @@ -203,7 +200,6 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, dbg("%s - port %d", __func__, port->number); while (count > 0) { - /* try to find a free urb in our list of them */ urb = NULL; @@ -262,15 +258,14 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, bytes_out += transfer_size; } - exit: return bytes_sent; - } -static int empeg_write_room (struct usb_serial_port *port) +static int empeg_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int room = 0; @@ -278,25 +273,22 @@ static int empeg_write_room (struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); spin_lock_irqsave (&write_urb_pool_lock, flags); - /* tally up the number of bytes available */ for (i = 0; i < NUM_URBS; ++i) { if (write_urb_pool[i]->status != -EINPROGRESS) { room += URB_TRANSFER_BUFFER_SIZE; } } - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - dbg("%s - returns %d", __func__, room); - - return (room); + return room; } -static int empeg_chars_in_buffer (struct usb_serial_port *port) +static int empeg_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int chars = 0; @@ -356,7 +348,7 @@ static void empeg_read_bulk_callback (struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); @@ -386,27 +378,24 @@ static void empeg_read_bulk_callback (struct urb *urb) } -static void empeg_throttle (struct usb_serial_port *port) +static void empeg_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); } -static void empeg_unthrottle (struct usb_serial_port *port) +static void empeg_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int result; - dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); - - return; } @@ -436,17 +425,10 @@ static void empeg_shutdown (struct usb_serial *serial) } -static int empeg_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - - return -ENOIOCTLCMD; -} - - -static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void empeg_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; dbg("%s - port %d", __func__, port->number); /* @@ -491,8 +473,8 @@ static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *ol * this is bad as it opens up the possibility of dropping bytes * on the floor. We don't want to drop bytes on the floor. :) */ - port->tty->low_latency = 1; - tty_encode_baud_rate(port->tty, 115200, 115200); + tty->low_latency = 1; + tty_encode_baud_rate(tty, 115200, 115200); } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0ff4a3971e45..abbb447e5375 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -682,21 +682,21 @@ static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_i static void ftdi_shutdown (struct usb_serial *serial); static int ftdi_sio_port_probe (struct usb_serial_port *port); static int ftdi_sio_port_remove (struct usb_serial_port *port); -static int ftdi_open (struct usb_serial_port *port, struct file *filp); -static void ftdi_close (struct usb_serial_port *port, struct file *filp); -static int ftdi_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int ftdi_write_room (struct usb_serial_port *port); -static int ftdi_chars_in_buffer (struct usb_serial_port *port); +static int ftdi_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void ftdi_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int ftdi_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int ftdi_write_room (struct tty_struct *tty); +static int ftdi_chars_in_buffer (struct tty_struct *tty); static void ftdi_write_bulk_callback (struct urb *urb); static void ftdi_read_bulk_callback (struct urb *urb); static void ftdi_process_read (struct work_struct *work); -static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file); -static int ftdi_tiocmset (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear); -static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void ftdi_break_ctl (struct usb_serial_port *port, int break_state ); -static void ftdi_throttle (struct usb_serial_port *port); -static void ftdi_unthrottle (struct usb_serial_port *port); +static void ftdi_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static int ftdi_tiocmget (struct tty_struct *tty, struct file *file); +static int ftdi_tiocmset (struct tty_struct *tty, struct file * file, unsigned int set, unsigned int clear); +static int ftdi_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void ftdi_break_ctl (struct tty_struct *tty, int break_state ); +static void ftdi_throttle (struct tty_struct *tty); +static void ftdi_unthrottle (struct tty_struct *tty); static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base); static unsigned short int ftdi_232am_baud_to_divisor (int baud); @@ -843,42 +843,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, unsigned } -static __u32 get_ftdi_divisor(struct usb_serial_port * port); - - -static int change_speed(struct usb_serial_port *port) -{ - struct ftdi_private *priv = usb_get_serial_port_data(port); - char *buf; - __u16 urb_value; - __u16 urb_index; - __u32 urb_index_value; - int rv; - - buf = kmalloc(1, GFP_NOIO); - if (!buf) - return -ENOMEM; - - urb_index_value = get_ftdi_divisor(port); - urb_value = (__u16)urb_index_value; - urb_index = (__u16)(urb_index_value >> 16); - if (priv->interface) { /* FT2232C */ - urb_index = (__u16)((urb_index << 8) | priv->interface); - } - - rv = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - FTDI_SIO_SET_BAUDRATE_REQUEST, - FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, - urb_value, urb_index, - buf, 0, WDR_SHORT_TIMEOUT); - - kfree(buf); - return rv; -} - - -static __u32 get_ftdi_divisor(struct usb_serial_port * port) +static __u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) { /* get_ftdi_divisor */ struct ftdi_private *priv = usb_get_serial_port_data(port); __u32 div_value = 0; @@ -910,7 +875,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) /* 1. Get the baud rate from the tty settings, this observes alt_speed hack */ - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); dbg("%s - tty_get_baud_rate reports speed %d", __func__, baud); /* 2. Observe async-compatible custom_divisor hack, update baudrate if needed */ @@ -976,10 +941,42 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) ftdi_chip_name[priv->chip_type]); } - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); return(div_value); } +static int change_speed(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + char *buf; + __u16 urb_value; + __u16 urb_index; + __u32 urb_index_value; + int rv; + + buf = kmalloc(1, GFP_NOIO); + if (!buf) + return -ENOMEM; + + urb_index_value = get_ftdi_divisor(tty, port); + urb_value = (__u16)urb_index_value; + urb_index = (__u16)(urb_index_value >> 16); + if (priv->interface) { /* FT2232C */ + urb_index = (__u16)((urb_index << 8) | priv->interface); + } + + rv = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_BAUDRATE_REQUEST, + FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + urb_value, urb_index, + buf, 0, WDR_SHORT_TIMEOUT); + + kfree(buf); + return rv; +} + + static int get_serial_info(struct usb_serial_port * port, struct serial_struct __user * retinfo) { @@ -998,7 +995,8 @@ static int get_serial_info(struct usb_serial_port * port, struct serial_struct _ } /* get_serial_info */ -static int set_serial_info(struct usb_serial_port * port, struct serial_struct __user * newinfo) +static int set_serial_info(struct tty_struct *tty, + struct usb_serial_port * port, struct serial_struct __user * newinfo) { /* set_serial_info */ struct ftdi_private *priv = usb_get_serial_port_data(port); struct serial_struct new_serial; @@ -1030,30 +1028,29 @@ static int set_serial_info(struct usb_serial_port * port, struct serial_struct _ (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; - port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: if ((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) { if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - port->tty->alt_speed = 57600; + tty->alt_speed = 57600; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - port->tty->alt_speed = 115200; + tty->alt_speed = 115200; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - port->tty->alt_speed = 230400; + tty->alt_speed = 230400; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - port->tty->alt_speed = 460800; + tty->alt_speed = 460800; else - port->tty->alt_speed = 0; + tty->alt_speed = 0; } if (((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) || (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) && (old_priv.custom_divisor != priv->custom_divisor))) { - change_speed(port); + change_speed(tty, port); } - - return (0); + return 0; } /* set_serial_info */ @@ -1415,7 +1412,8 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } -static int ftdi_open (struct usb_serial_port *port, struct file *filp) +static int ftdi_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* ftdi_open */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1433,8 +1431,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) priv->rx_bytes = 0; spin_unlock_irqrestore(&priv->rx_lock, flags); - if (port->tty) - port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (tty) + tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* No error checking for this (will get errors later anyway) */ /* See ftdi_sio.h for description of what is reset */ @@ -1448,8 +1446,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (port->tty) - ftdi_set_termios(port, port->tty->termios); + if (tty) + ftdi_set_termios(tty, port, tty->termios); /* FIXME: Flow control might be enabled, so it should be checked - we have no control of defaults! */ @@ -1485,9 +1483,10 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) * */ -static void ftdi_close (struct usb_serial_port *port, struct file *filp) +static void ftdi_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* ftdi_close */ - unsigned int c_cflag = port->tty->termios->c_cflag; + unsigned int c_cflag = tty->termios->c_cflag; struct ftdi_private *priv = usb_get_serial_port_data(port); char buf[1]; @@ -1527,7 +1526,7 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) * * The new devices do not require this byte */ -static int ftdi_write (struct usb_serial_port *port, +static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { /* ftdi_write */ struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1686,8 +1685,9 @@ static void ftdi_write_bulk_callback (struct urb *urb) } /* ftdi_write_bulk_callback */ -static int ftdi_write_room( struct usb_serial_port *port ) +static int ftdi_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int room; unsigned long flags; @@ -1707,11 +1707,11 @@ static int ftdi_write_room( struct usb_serial_port *port ) } spin_unlock_irqrestore(&priv->tx_lock, flags); return room; -} /* ftdi_write_room */ - +} -static int ftdi_chars_in_buffer (struct usb_serial_port *port) -{ /* ftdi_chars_in_buffer */ +static int ftdi_chars_in_buffer(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int buffered; unsigned long flags; @@ -1726,12 +1726,10 @@ static int ftdi_chars_in_buffer (struct usb_serial_port *port) buffered = 0; } return buffered; -} /* ftdi_chars_in_buffer */ - - +} -static void ftdi_read_bulk_callback (struct urb *urb) -{ /* ftdi_read_bulk_callback */ +static void ftdi_read_bulk_callback(struct urb *urb) +{ struct usb_serial_port *port = urb->context; struct tty_struct *tty; struct ftdi_private *priv; @@ -1747,10 +1745,10 @@ static void ftdi_read_bulk_callback (struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (port->open_count <= 0) + if (port->port.count <= 0) return; - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting",__func__); return; @@ -1803,10 +1801,10 @@ static void ftdi_process_read (struct work_struct *work) dbg("%s - port %d", __func__, port->number); - if (port->open_count <= 0) + if (port->port.count <= 0) return; - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting",__func__); return; @@ -1954,7 +1952,7 @@ static void ftdi_process_read (struct work_struct *work) } spin_unlock_irqrestore(&priv->rx_lock, flags); /* if the port is closed stop trying to read */ - if (port->open_count > 0){ + if (port->port.count > 0){ /* delay processing of remainder */ schedule_delayed_work(&priv->rx_work, 1); } else { @@ -1967,7 +1965,7 @@ static void ftdi_process_read (struct work_struct *work) priv->rx_processed = 0; /* if the port is closed stop trying to read */ - if (port->open_count > 0){ + if (port->port.count > 0){ /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -1983,8 +1981,9 @@ static void ftdi_process_read (struct work_struct *work) } /* ftdi_process_read */ -static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) +static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); __u16 urb_value = 0; char buf[1]; @@ -2018,11 +2017,12 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) * WARNING: set_termios calls this with old_termios in kernel space */ -static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ftdi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { /* ftdi_termios */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ @@ -2037,7 +2037,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old /* Force baud rate if this device requires it, unless it is set to B0. */ if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) { dbg("%s: forcing baud rate for this device", __func__); - tty_encode_baud_rate(port->tty, priv->force_baud, + tty_encode_baud_rate(tty, priv->force_baud, priv->force_baud); } @@ -2104,7 +2104,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); } else { /* set the baudrate determined before */ - if (change_speed(port)) { + if (change_speed(tty, port)) { err("%s urb failed to set baudrate", __func__); } /* Ensure RTS and DTR are raised when baudrate changed from 0 */ @@ -2168,11 +2168,11 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old } return; -} /* ftdi_termios */ - +} -static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) +static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned char buf[2]; int ret; @@ -2221,15 +2221,18 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) priv->last_dtr_rts; } -static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear) +static int ftdi_tiocmset(struct tty_struct *tty, struct file * file, + unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; dbg("%s TIOCMSET", __func__); return update_mctrl(port, set, clear); } -static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int ftdi_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s cmd 0x%04x", __func__, cmd); @@ -2241,7 +2244,7 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne return get_serial_info(port, (struct serial_struct __user *) arg); case TIOCSSERIAL: /* sets serial port data */ - return set_serial_info(port, (struct serial_struct __user *) arg); + return set_serial_info(tty, port, (struct serial_struct __user *) arg); /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change @@ -2280,25 +2283,20 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne */ } } - return(0); - break; + return 0; default: break; - } - - /* This is not necessarily an error - turns out the higher layers will do * some ioctls itself (see comment above) */ dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __func__, cmd); + return -ENOIOCTLCMD; +} - return(-ENOIOCTLCMD); -} /* ftdi_ioctl */ - - -static void ftdi_throttle (struct usb_serial_port *port) +static void ftdi_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -2310,8 +2308,9 @@ static void ftdi_throttle (struct usb_serial_port *port) } -static void ftdi_unthrottle (struct usb_serial_port *port) +static void ftdi_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 8ce5a56a48e3..06cfa43c6f02 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -275,7 +275,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, char *data, unsigned int actual_length) { - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; if (tty && actual_length) { @@ -970,7 +970,8 @@ static int garmin_init_session(struct usb_serial_port *port) -static int garmin_open (struct usb_serial_port *port, struct file *filp) +static int garmin_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { unsigned long flags; int status = 0; @@ -983,8 +984,8 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) * through, otherwise it is scheduled, and with high data rates (like * with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; @@ -998,17 +999,16 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) usb_kill_urb (port->write_urb); usb_kill_urb (port->read_urb); - if (garmin_data_p->state == STATE_RESET) { + if (garmin_data_p->state == STATE_RESET) status = garmin_init_session(port); - } garmin_data_p->state = STATE_ACTIVE; - return status; } -static void garmin_close (struct usb_serial_port *port, struct file * filp) +static void garmin_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1042,7 +1042,6 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp) mutex_unlock(&port->serial->disc_mutex); } - static void garmin_write_bulk_callback (struct urb *urb) { unsigned long flags; @@ -1145,10 +1144,8 @@ static int garmin_write_bulk (struct usb_serial_port *port, return count; } - - -static int garmin_write (struct usb_serial_port *port, - const unsigned char *buf, int count) +static int garmin_write (struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { int pktid, pktsiz, len; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1158,7 +1155,6 @@ static int garmin_write (struct usb_serial_port *port, /* check for our private packets */ if (count >= GARMIN_PKTHDR_LENGTH) { - len = PRIVPKTSIZ; if (count < len) len = count; @@ -1226,8 +1222,9 @@ static int garmin_write (struct usb_serial_port *port, } -static int garmin_write_room (struct usb_serial_port *port) +static int garmin_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; /* * Report back the bytes currently available in the output buffer. */ @@ -1236,20 +1233,6 @@ static int garmin_write_room (struct usb_serial_port *port) } -static int garmin_chars_in_buffer (struct usb_serial_port *port) -{ - /* - * Report back the number of bytes currently in our input buffer. - * Will this lock up the driver - the buffer contains an incomplete - * package which will not be written to the device until it - * has been completed ? - */ - //struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); - //return garmin_data_p->insize; - return 0; -} - - static void garmin_read_process(struct garmin_data * garmin_data_p, unsigned char *data, unsigned data_length) { @@ -1468,10 +1451,11 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p) } -static void garmin_throttle (struct usb_serial_port *port) +static void garmin_throttle(struct tty_struct *tty) { - unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + unsigned long flags; dbg("%s - port %d", __func__, port->number); /* set flag, data received will be put into a queue @@ -1482,10 +1466,11 @@ static void garmin_throttle (struct usb_serial_port *port) } -static void garmin_unthrottle (struct usb_serial_port *port) +static void garmin_unthrottle (struct tty_struct *tty) { - unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + unsigned long flags; int status; dbg("%s - port %d", __func__, port->number); @@ -1507,8 +1492,6 @@ static void garmin_unthrottle (struct usb_serial_port *port) } } - - /* * The timer is currently only used to send queued packets to * the tty in cases where the protocol provides no own handshaking @@ -1526,7 +1509,7 @@ static void timeout_handler(unsigned long data) -static int garmin_attach (struct usb_serial *serial) +static int garmin_attach(struct usb_serial *serial) { int status = 0; struct usb_serial_port *port = serial->port[0]; @@ -1556,7 +1539,7 @@ static int garmin_attach (struct usb_serial *serial) } -static void garmin_shutdown (struct usb_serial *serial) +static void garmin_shutdown(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1588,7 +1571,6 @@ static struct usb_serial_driver garmin_device = { .shutdown = garmin_shutdown, .write = garmin_write, .write_room = garmin_write_room, - .chars_in_buffer = garmin_chars_in_buffer, .write_bulk_callback = garmin_write_bulk_callback, .read_bulk_callback = garmin_read_bulk_callback, .read_int_callback = garmin_read_int_callback, diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 537f12a027c2..5128018c2766 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -112,7 +112,8 @@ void usb_serial_generic_deregister (void) #endif } -int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) +int usb_serial_generic_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; @@ -123,8 +124,8 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) /* force low_latency on so that our tty_push actually forces the data through, otherwise it is scheduled, and with high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* clear the throttle flags */ spin_lock_irqsave(&port->lock, flags); @@ -152,7 +153,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) } EXPORT_SYMBOL_GPL(usb_serial_generic_open); -static void generic_cleanup (struct usb_serial_port *port) +static void generic_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -182,7 +183,7 @@ int usb_serial_generic_resume(struct usb_serial *serial) #endif for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; - if (port->open_count && port->read_urb) { + if (port->port.count && port->read_urb) { r = usb_submit_urb(port->read_urb, GFP_NOIO); if (r < 0) c++; @@ -192,13 +193,15 @@ int usb_serial_generic_resume(struct usb_serial *serial) return c ? -EIO : 0; } -void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp) +void usb_serial_generic_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); generic_cleanup (port); } -int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *buf, int count) +int usb_serial_generic_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int result; @@ -255,8 +258,9 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * return 0; } -int usb_serial_generic_write_room (struct usb_serial_port *port) +int usb_serial_generic_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int room = 0; @@ -272,8 +276,9 @@ int usb_serial_generic_write_room (struct usb_serial_port *port) return room; } -int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) +int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int chars = 0; @@ -286,7 +291,7 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) } dbg("%s - returns %d", __func__, chars); - return (chars); + return chars; } @@ -311,10 +316,10 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) } /* Push data to tty layer and resubmit the bulk read URB */ -static void flush_and_resubmit_read_urb (struct usb_serial_port *port) +static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; int room; /* Push data to tty */ @@ -329,7 +334,7 @@ static void flush_and_resubmit_read_urb (struct usb_serial_port *port) resubmit_read_urb(port, GFP_ATOMIC); } -void usb_serial_generic_read_bulk_callback (struct urb *urb) +void usb_serial_generic_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; @@ -357,7 +362,7 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); -void usb_serial_generic_write_bulk_callback (struct urb *urb) +void usb_serial_generic_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -374,8 +379,9 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb) } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); -void usb_serial_generic_throttle (struct usb_serial_port *port) +void usb_serial_generic_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -387,8 +393,9 @@ void usb_serial_generic_throttle (struct usb_serial_port *port) spin_unlock_irqrestore(&port->lock, flags); } -void usb_serial_generic_unthrottle (struct usb_serial_port *port) +void usb_serial_generic_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int was_throttled; unsigned long flags; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 2fd449bcfa35..29346d79df7b 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -206,18 +206,18 @@ static void edge_bulk_out_data_callback (struct urb *urb); static void edge_bulk_out_cmd_callback (struct urb *urb); /* function prototypes for the usbserial callbacks */ -static int edge_open (struct usb_serial_port *port, struct file *filp); -static void edge_close (struct usb_serial_port *port, struct file *filp); -static int edge_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int edge_write_room (struct usb_serial_port *port); -static int edge_chars_in_buffer (struct usb_serial_port *port); -static void edge_throttle (struct usb_serial_port *port); -static void edge_unthrottle (struct usb_serial_port *port); -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); -static void edge_break (struct usb_serial_port *port, int break_state); -static int edge_tiocmget (struct usb_serial_port *port, struct file *file); -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); +static int edge_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void edge_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int edge_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int edge_write_room (struct tty_struct *tty); +static int edge_chars_in_buffer (struct tty_struct *tty); +static void edge_throttle (struct tty_struct *tty); +static void edge_unthrottle (struct tty_struct *tty); +static void edge_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); +static int edge_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static void edge_break (struct tty_struct *tty, int break_state); +static int edge_tiocmget (struct tty_struct *tty, struct file *file); +static int edge_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int edge_startup (struct usb_serial *serial); static void edge_shutdown (struct usb_serial *serial); @@ -233,7 +233,8 @@ static void handle_new_lsr (struct edgeport_port *edge_port, __u8 lsrData, __u8 static int send_iosp_ext_cmd (struct edgeport_port *edge_port, __u8 command, __u8 param); static int calc_baud_rate_divisor (int baud_rate, int *divisor); static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate); -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios); +static void change_port_settings (struct tty_struct *tty, struct edgeport_port *edge_port, + struct ktermios *old_termios); static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue); static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer, int writeLength); static void send_more_port_data (struct edgeport_serial *edge_serial, struct edgeport_port *edge_port); @@ -639,8 +640,8 @@ static void edge_interrupt_callback (struct urb *urb) dbg("%s - txcredits for port%d = %d", __func__, portNumber, edge_port->txCredits); /* tell the tty driver that something has changed */ - if (edge_port->port->tty) - tty_wakeup(edge_port->port->tty); + if (edge_port->port->port.tty) + tty_wakeup(edge_port->port->port.tty); // Since we have more credit, check if more data can be sent send_more_port_data(edge_serial, edge_port); @@ -737,7 +738,7 @@ static void edge_bulk_out_data_callback (struct urb *urb) __func__, status); } - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ @@ -781,7 +782,7 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) } /* Get pointer to tty */ - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; /* tell the tty driver that something has changed */ if (tty && edge_port->open) @@ -803,7 +804,8 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) * If successful, we return 0 * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_open (struct usb_serial_port *port, struct file * filp) +static int edge_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct usb_serial *serial; @@ -815,16 +817,15 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (edge_port == NULL) return -ENODEV; - if (port->tty) - port->tty->low_latency = low_latency; + if (tty) + tty->low_latency = low_latency; /* see if we've set up our endpoint info yet (can't set it up in edge_startup as the structures were not set up at that time.) */ serial = port->serial; edge_serial = usb_get_serial_data(serial); - if (edge_serial == NULL) { + if (edge_serial == NULL) return -ENODEV; - } if (edge_serial->interrupt_in_buffer == NULL) { struct usb_serial_port *port0 = serial->port[0]; @@ -908,7 +909,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (!edge_port->txfifo.fifo) { dbg("%s - no memory", __func__); - edge_close (port, filp); + edge_close (tty, port, filp); return -ENOMEM; } @@ -918,7 +919,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (!edge_port->write_urb) { dbg("%s - no memory", __func__); - edge_close (port, filp); + edge_close (tty, port, filp); return -ENOMEM; } @@ -1038,7 +1039,8 @@ static void block_until_tx_empty (struct edgeport_port *edge_port) * edge_close * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void edge_close (struct usb_serial_port *port, struct file * filp) +static void edge_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -1106,7 +1108,8 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) * If successful, we return the number of bytes written, otherwise we return * a negative error number. *****************************************************************************/ -static int edge_write (struct usb_serial_port *port, const unsigned char *data, int count) +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct TxFifo *fifo; @@ -1308,8 +1311,9 @@ exit_send: * (the txCredits), * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_write_room (struct usb_serial_port *port) +static int edge_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int room; unsigned long flags; @@ -1347,8 +1351,9 @@ static int edge_write_room (struct usb_serial_port *port) * system, * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_chars_in_buffer (struct usb_serial_port *port) +static int edge_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int num_chars; unsigned long flags; @@ -1381,10 +1386,10 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) * this function is called by the tty driver when it wants to stop the data * being read from the port. *****************************************************************************/ -static void edge_throttle (struct usb_serial_port *port) +static void edge_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -1397,16 +1402,10 @@ static void edge_throttle (struct usb_serial_port *port) return; } - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = edge_write (port, &stop_char, 1); + status = edge_write (tty, port, &stop_char, 1); if (status <= 0) { return; } @@ -1430,10 +1429,10 @@ static void edge_throttle (struct usb_serial_port *port) * this function is called by the tty driver when it wants to resume the data * being read from the port (called after SerialThrottle is called) *****************************************************************************/ -static void edge_unthrottle (struct usb_serial_port *port) +static void edge_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -1446,31 +1445,18 @@ static void edge_unthrottle (struct usb_serial_port *port) return; } - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = edge_write (port, &start_char, 1); - if (status <= 0) { + status = edge_write(tty, port, &start_char, 1); + if (status <= 0) return; - } } - /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { edge_port->shadowMCR |= MCR_RTS; - status = send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); - if (status != 0) { - return; - } + send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); } - - return; } @@ -1478,11 +1464,10 @@ static void edge_unthrottle (struct usb_serial_port *port) * SerialSetTermios * this function is called by the tty driver when it wants to change the termios structure *****************************************************************************/ -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - /* FIXME: This function appears unused ?? */ struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int cflag; cflag = tty->termios->c_cflag; @@ -1502,9 +1487,7 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old } /* change the port settings to the new ones specified */ - change_port_settings (edge_port, old_termios); - - return; + change_port_settings(tty, edge_port, old_termios); } @@ -1536,25 +1519,9 @@ static int get_lsr_info(struct edgeport_port *edge_port, unsigned int __user *va return 0; } -static int get_number_bytes_avail(struct edgeport_port *edge_port, unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = edge_port->port->tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, edge_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - //return 0; - return -ENOIOCTLCMD; -} - -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear) +static int edge_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int mcr; @@ -1582,8 +1549,9 @@ static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsig return 0; } -static int edge_tiocmget(struct usb_serial_port *port, struct file *file) +static int edge_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int result = 0; unsigned int msr; @@ -1624,9 +1592,6 @@ static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct tmp.baud_base = 9600; tmp.close_delay = 5*HZ; tmp.closing_wait = 30*HZ; -// tmp.custom_divisor = state->custom_divisor; -// tmp.hub6 = state->hub6; -// tmp.io_type = state->io_type; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1639,8 +1604,10 @@ static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct * SerialIoctl * this function handles any ioctl calls to the driver *****************************************************************************/ -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +static int edge_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; DEFINE_WAIT(wait); struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; @@ -1650,25 +1617,14 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - // return number of bytes available - case TIOCINQ: - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(edge_port, (unsigned int __user *) arg); - break; - case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return get_lsr_info(edge_port, (unsigned int __user *) arg); - return 0; case TIOCGSERIAL: dbg("%s (%d) TIOCGSERIAL", __func__, port->number); return get_serial_info(edge_port, (struct serial_struct __user *) arg); - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - case TIOCMIWAIT: dbg("%s (%d) TIOCMIWAIT", __func__, port->number); cprev = edge_port->icount; @@ -1723,8 +1679,9 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned * SerialBreak * this function sends a break to the port *****************************************************************************/ -static void edge_break (struct usb_serial_port *port, int break_state) +static void edge_break (struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); int status; @@ -1866,7 +1823,7 @@ static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned cha port = edge_serial->serial->port[edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port->open) { - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty) { dbg("%s - Sending %d bytes to TTY for port %d", __func__, rxLen, edge_serial->rxPort); edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); @@ -1941,8 +1898,9 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 handle_new_msr (edge_port, byte2); /* send the current line settings to the port so we are in sync with any further termios calls */ - if (edge_port->port->tty) - change_port_settings (edge_port, edge_port->port->tty->termios); + /* FIXME: locking on tty */ + if (edge_port->port->port.tty) + change_port_settings(edge_port->port->port.tty, edge_port, edge_port->port->port.tty->termios); /* we have completed the open */ edge_port->openPending = false; @@ -2078,8 +2036,8 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, __u8 l } /* Place LSR data byte into Rx buffer */ - if (lsrData && edge_port->port->tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, &data, 1); + if (lsrData && edge_port->port->port.tty) + edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); /* update input line counters */ icount = &edge_port->icount; @@ -2473,13 +2431,11 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r * This routine is called to set the UART on the device to match the specified * new settings. *****************************************************************************/ -#ifndef CMSPAR -#define CMSPAR 0 -#endif -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) + +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, struct ktermios *old_termios) { struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); - struct tty_struct *tty; int baud; unsigned cflag; __u8 mask = 0xff; @@ -2498,13 +2454,6 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi return; } - tty = edge_port->port->tty; - if ((!tty) || - (!tty->termios)) { - dbg("%s - no tty structures", __func__); - return; - } - cflag = tty->termios->c_cflag; switch (cflag & CSIZE) { diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index a58822a14a87..7cf383a2a556 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -243,9 +243,9 @@ static void edge_tty_recv(struct device *dev, struct tty_struct *tty, static void stop_read(struct edgeport_port *edge_port); static int restart_read(struct edgeport_port *edge_port); -static void edge_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); -static void edge_send(struct usb_serial_port *port); +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void edge_send(struct tty_struct *tty); /* sysfs attributes */ static int edge_create_sysfs_attrs(struct usb_serial_port *port); @@ -572,7 +572,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, int flush) { int baud_rate; - struct tty_struct *tty = port->port->tty; + struct tty_struct *tty = port->port->port.tty; wait_queue_t wait; unsigned long flags; @@ -1554,7 +1554,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) /* Save the new modem status */ edge_port->shadow_msr = msr & 0xf0; - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { if (msr & EDGEPORT_MSR_CTS) { @@ -1587,9 +1587,8 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); /* Place LSR data byte into Rx buffer */ - if (lsr_data && edge_port->port->tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, - &data, 1); + if (lsr_data && edge_port->port->port.tty) + edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); /* update input line counters */ icount = &edge_port->icount; @@ -1750,7 +1749,7 @@ static void edge_bulk_in_callback(struct urb *urb) ++data; } - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty && urb->actual_length) { usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data); @@ -1819,10 +1818,11 @@ static void edge_bulk_out_callback(struct urb *urb) } /* send any buffered data */ - edge_send(port); + edge_send(port->port.tty); } -static int edge_open(struct usb_serial_port *port, struct file *filp) +static int edge_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial; @@ -1838,7 +1838,8 @@ static int edge_open(struct usb_serial_port *port, struct file *filp) if (edge_port == NULL) return -ENODEV; - port->tty->low_latency = low_latency; + if (tty) + tty->low_latency = low_latency; port_number = port->number - port->serial->minor; switch (port_number) { @@ -1874,7 +1875,8 @@ static int edge_open(struct usb_serial_port *port, struct file *filp) } /* set up the port settings */ - edge_set_termios(port, port->tty->termios); + if (tty) + edge_set_termios(tty, port, port->port.tty->termios); /* open up the port */ @@ -2000,7 +2002,8 @@ release_es_lock: return status; } -static void edge_close(struct usb_serial_port *port, struct file *filp) +static void edge_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -2048,8 +2051,8 @@ static void edge_close(struct usb_serial_port *port, struct file *filp) dbg("%s - exited", __func__); } -static int edge_write(struct usb_serial_port *port, const unsigned char *data, - int count) +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned long flags; @@ -2070,16 +2073,16 @@ static int edge_write(struct usb_serial_port *port, const unsigned char *data, count = edge_buf_put(edge_port->ep_out_buf, data, count); spin_unlock_irqrestore(&edge_port->ep_lock, flags); - edge_send(port); + edge_send(tty); return count; } -static void edge_send(struct usb_serial_port *port) +static void edge_send(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int count, result; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned long flags; @@ -2133,8 +2136,9 @@ static void edge_send(struct usb_serial_port *port) tty_wakeup(tty); } -static int edge_write_room(struct usb_serial_port *port) +static int edge_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -2154,8 +2158,9 @@ static int edge_write_room(struct usb_serial_port *port) return room; } -static int edge_chars_in_buffer(struct usb_serial_port *port) +static int edge_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -2175,10 +2180,10 @@ static int edge_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void edge_throttle(struct usb_serial_port *port) +static void edge_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; int status; dbg("%s - port %d", __func__, port->number); @@ -2189,11 +2194,10 @@ static void edge_throttle(struct usb_serial_port *port) /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = edge_write(port, &stop_char, 1); - if (status <= 0) - dev_err(&port->dev, - "%s - failed to write stop character, %d\n", - __func__, status); + status = edge_write(tty, port, &stop_char, 1); + if (status <= 0) { + dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status); + } } /* if we are implementing RTS/CTS, stop reads */ @@ -2203,10 +2207,10 @@ static void edge_throttle(struct usb_serial_port *port) } -static void edge_unthrottle(struct usb_serial_port *port) +static void edge_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; int status; dbg("%s - port %d", __func__, port->number); @@ -2217,11 +2221,10 @@ static void edge_unthrottle(struct usb_serial_port *port) /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = edge_write(port, &start_char, 1); - if (status <= 0) - dev_err(&port->dev, - "%s - failed to write start character, %d\n", - __func__, status); + status = edge_write(tty, port, &start_char, 1); + if (status <= 0) { + dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status); + } } /* if we are implementing RTS/CTS, restart reads */ /* are the Edgeport will assert the RTS line */ @@ -2271,11 +2274,10 @@ static int restart_read(struct edgeport_port *edge_port) return status; } -static void change_port_settings(struct edgeport_port *edge_port, - struct ktermios *old_termios) +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, struct ktermios *old_termios) { struct ump_uart_config *config; - struct tty_struct *tty; int baud; unsigned cflag; int status; @@ -2284,9 +2286,7 @@ static void change_port_settings(struct edgeport_port *edge_port, dbg("%s - port %d", __func__, edge_port->port->number); - tty = edge_port->port->tty; - - config = kmalloc(sizeof(*config), GFP_KERNEL); + config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { *tty->termios = *old_termios; dev_err(&edge_port->port->dev, "%s - out of memory\n", @@ -2419,11 +2419,13 @@ static void change_port_settings(struct edgeport_port *edge_port, return; } -static void edge_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; + unsigned int cflag; + + cflag = tty->termios->c_cflag; dbg("%s - clfag %08x iflag %08x", __func__, tty->termios->c_cflag, tty->termios->c_iflag); @@ -2434,12 +2436,14 @@ static void edge_set_termios(struct usb_serial_port *port, if (edge_port == NULL) return; /* change the port settings to the new ones specified */ - change_port_settings(edge_port, old_termios); + change_port_settings(tty, edge_port, old_termios); + return; } -static int edge_tiocmset(struct usb_serial_port *port, struct file *file, +static int edge_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int mcr; unsigned long flags; @@ -2469,8 +2473,9 @@ static int edge_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int edge_tiocmget(struct usb_serial_port *port, struct file *file) +static int edge_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int result = 0; unsigned int msr; @@ -2522,9 +2527,10 @@ static int get_serial_info(struct edgeport_port *edge_port, return 0; } -static int edge_ioctl(struct usb_serial_port *port, struct file *file, +static int edge_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -2569,18 +2575,19 @@ static int edge_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static void edge_break(struct usb_serial_port *port, int on) +static void edge_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status; int bv = 0; /* Off */ - dbg("%s - state = %d", __func__, on); + dbg("%s - state = %d", __func__, break_state); /* chase the port close */ chase_port(edge_port, 0, 0); - if (on == -1) + if (break_state == -1) bv = 1; /* On */ status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv); if (status) diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 80d9ec5570d6..a7784642d6a1 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -74,19 +74,21 @@ static int connect_retries = KP_RETRIES; static int initial_wait; /* Function prototypes for an ipaq */ -static int ipaq_open (struct usb_serial_port *port, struct file *filp); -static void ipaq_close (struct usb_serial_port *port, struct file *filp); -static int ipaq_startup (struct usb_serial *serial); -static void ipaq_shutdown (struct usb_serial *serial); -static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, - int count); +static int ipaq_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void ipaq_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int ipaq_startup(struct usb_serial *serial); +static void ipaq_shutdown(struct usb_serial *serial); +static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); static int ipaq_write_bulk(struct usb_serial_port *port, const unsigned char *buf, - int count); + int count); static void ipaq_write_gather(struct usb_serial_port *port); static void ipaq_read_bulk_callback (struct urb *urb); static void ipaq_write_bulk_callback(struct urb *urb); -static int ipaq_write_room(struct usb_serial_port *port); -static int ipaq_chars_in_buffer(struct usb_serial_port *port); +static int ipaq_write_room(struct tty_struct *tty); +static int ipaq_chars_in_buffer(struct tty_struct *tty); static void ipaq_destroy_lists(struct usb_serial_port *port); @@ -591,7 +593,8 @@ static spinlock_t write_list_lock; static int bytes_in; static int bytes_out; -static int ipaq_open(struct usb_serial_port *port, struct file *filp) +static int ipaq_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct ipaq_private *priv; @@ -637,10 +640,12 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) * discipline instead of queueing. */ - port->tty->low_latency = 1; - port->tty->raw = 1; - port->tty->real_raw = 1; - + if (tty) { + tty->low_latency = 1; + /* FIXME: These two are bogus */ + tty->raw = 1; + tty->real_raw = 1; + } /* * Lose the small buffers usbserial provides. Make larger ones. */ @@ -714,7 +719,8 @@ error: } -static void ipaq_close(struct usb_serial_port *port, struct file *filp) +static void ipaq_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ipaq_private *priv = usb_get_serial_port_data(port); @@ -751,7 +757,7 @@ static void ipaq_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -770,8 +776,8 @@ static void ipaq_read_bulk_callback(struct urb *urb) return; } -static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { const unsigned char *current_position = buf; int bytes_sent = 0; @@ -905,16 +911,18 @@ static void ipaq_write_bulk_callback(struct urb *urb) usb_serial_port_softint(port); } -static int ipaq_write_room(struct usb_serial_port *port) +static int ipaq_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - freelen %d", __func__, priv->free_len); return priv->free_len; } -static int ipaq_chars_in_buffer(struct usb_serial_port *port) +static int ipaq_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - queuelen %d", __func__, priv->queue_len); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index bc85ca5c1c37..a89ebfe9e915 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -179,7 +179,7 @@ static void ipw_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -199,7 +199,8 @@ static void ipw_read_bulk_callback(struct urb *urb) return; } -static int ipw_open(struct usb_serial_port *port, struct file *filp) +static int ipw_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_device *dev = port->serial->dev; u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT; @@ -212,8 +213,8 @@ static int ipw_open(struct usb_serial_port *port, struct file *filp) if (!buf_flow_init) return -ENOMEM; - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* --1: Tell the modem to initialize (we think) From sniffs this is always the * first thing that gets sent to the modem during opening of the device */ @@ -301,7 +302,8 @@ static int ipw_open(struct usb_serial_port *port, struct file *filp) return 0; } -static void ipw_close(struct usb_serial_port *port, struct file * filp) +static void ipw_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct usb_device *dev = port->serial->dev; int result; @@ -384,7 +386,8 @@ static void ipw_write_bulk_callback(struct urb *urb) usb_serial_port_softint(port); } -static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int ipw_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct usb_device *dev = port->serial->dev; int ret; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 0063c11c8081..e59155c6607d 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -85,15 +85,17 @@ static int buffer_size; /* if overridden by the user, then use the specified number of XBOFs */ static int xbof = -1; -static int ir_startup(struct usb_serial *serial); -static int ir_open(struct usb_serial_port *port, struct file *filep); -static void ir_close(struct usb_serial_port *port, struct file *filep); -static int ir_write(struct usb_serial_port *port, - const unsigned char *buf, int count); -static void ir_write_bulk_callback(struct urb *urb); -static void ir_read_bulk_callback(struct urb *urb); -static void ir_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); +static int ir_startup (struct usb_serial *serial); +static int ir_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filep); +static void ir_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filep); +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static void ir_write_bulk_callback (struct urb *urb); +static void ir_read_bulk_callback (struct urb *urb); +static void ir_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); /* Not that this lot means you can only have one per system */ static u8 ir_baud; @@ -295,7 +297,8 @@ static int ir_startup(struct usb_serial *serial) return 0; } -static int ir_open(struct usb_serial_port *port, struct file *filp) +static int ir_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { char *buffer; int result = 0; @@ -343,7 +346,8 @@ static int ir_open(struct usb_serial_port *port, struct file *filp) return result; } -static void ir_close(struct usb_serial_port *port, struct file *filp) +static void ir_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); @@ -351,8 +355,8 @@ static void ir_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->read_urb); } -static int ir_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *transfer_buffer; int result; @@ -360,11 +364,6 @@ static int ir_write(struct usb_serial_port *port, dbg("%s - port = %d, count = %d", __func__, port->number, count); - if (!port->tty) { - dev_err(&port->dev, "%s - no tty???\n", __func__); - return 0; - } - if (count == 0) return 0; @@ -450,14 +449,13 @@ static void ir_read_bulk_callback(struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port closed.", __func__); return; } switch (status) { case 0: /* Successful */ - /* * The first byte of the packet we get from the device * contains a busy indicator and baud rate change. @@ -465,19 +463,11 @@ static void ir_read_bulk_callback(struct urb *urb) */ if ((*data & 0x0f) > 0) ir_baud = *data & 0x0f; - - usb_serial_debug_data( - debug, - &port->dev, - __func__, - urb->actual_length, - data); - - tty = port->tty; - + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + tty = port->port.tty; if (tty_buffer_request_room(tty, urb->actual_length - 1)) { - tty_insert_flip_string(tty, data + 1, - urb->actual_length - 1); + tty_insert_flip_string(tty, data+1, urb->actual_length - 1); tty_flip_buffer_push(tty); } @@ -488,11 +478,10 @@ static void ir_read_bulk_callback(struct urb *urb) */ case -EPROTO: /* taking inspiration from pl2303.c */ - - /* Continue trying to always read */ + /* Continue trying to always read */ usb_fill_bulk_urb( port->read_urb, - port->serial->dev, + port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -502,23 +491,19 @@ static void ir_read_bulk_callback(struct urb *urb) result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); - break; - + break ; default: dbg("%s - nonzero read bulk status received: %d", - __func__, - status); - break; + __func__, status); + break ; } - return; } -static void ir_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ir_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { unsigned char *transfer_buffer; int result; @@ -527,7 +512,7 @@ static void ir_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); /* * FIXME, we should compare the baud request against the @@ -600,8 +585,8 @@ static void ir_set_termios(struct usb_serial_port *port, __func__, result); /* Only speed changes are supported */ - tty_termios_copy_hw(port->tty->termios, old_termios); - tty_encode_baud_rate(port->tty, baud, baud); + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, baud, baud); } static int __init ir_init(void) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index a01e987c7d32..d65414888349 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -144,9 +144,10 @@ static void iuu_shutdown(struct usb_serial *serial) } } -static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, +static int iuu_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -171,8 +172,9 @@ static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, * When no card , the reader respond with TIOCM_CD * This is known as CD autodetect mechanism */ -static int iuu_tiocmget(struct usb_serial_port *port, struct file *file) +static int iuu_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; int rc; @@ -630,7 +632,7 @@ static void read_buf_callback(struct urb *urb) } dbg("%s - %i chars to write", __func__, urb->actual_length); - tty = port->tty; + tty = port->port.tty; if (data == NULL) dbg("%s - data is NULL !!!", __func__); if (tty && urb->actual_length && data) { @@ -752,11 +754,10 @@ static void iuu_uart_read_callback(struct urb *urb) /* if nothing to write call again rxcmd */ dbg("%s - rxcmd recall", __func__); iuu_led_activity_off(urb); - return; } -static int iuu_uart_write(struct usb_serial_port *port, const u8 *buf, - int count) +static int iuu_uart_write(struct tty_struct *tty, struct usb_serial_port *port, + const u8 *buf, int count) { struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -948,7 +949,8 @@ static int set_control_lines(struct usb_device *dev, u8 value) return 0; } -static void iuu_close(struct usb_serial_port *port, struct file *filp) +static void iuu_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* iuu_led (port,255,0,0,0); */ struct usb_serial *serial; @@ -964,8 +966,8 @@ static void iuu_close(struct usb_serial_port *port, struct file *filp) iuu_uart_off(port); if (serial->dev) { - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ priv = usb_get_serial_port_data(port); @@ -989,7 +991,8 @@ static void iuu_close(struct usb_serial_port *port, struct file *filp) } } -static int iuu_open(struct usb_serial_port *port, struct file *filp) +static int iuu_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; u8 *buf; @@ -1036,15 +1039,17 @@ static int iuu_open(struct usb_serial_port *port, struct file *filp) /* set the termios structure */ spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 - | TIOCM_CTS | CSTOPB | PARENB; - port->tty->termios->c_lflag = 0; - port->tty->termios->c_oflag = 0; - port->tty->termios->c_iflag = 0; + if (tty && !priv->termios_initialized) { + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + | TIOCM_CTS | CSTOPB | PARENB; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; + tty->termios->c_lflag = 0; + tty->termios->c_oflag = 0; + tty->termios->c_iflag = 0; priv->termios_initialized = 1; - port->tty->low_latency = 1; + tty->low_latency = 1; priv->poll = 0; } spin_unlock_irqrestore(&priv->lock, flags); @@ -1148,7 +1153,7 @@ static int iuu_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - iuu_close(port, NULL); + iuu_close(tty, port, NULL); return -EPROTO; } else { dbg("%s - rxcmd OK", __func__); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 11e439b90eac..a371c41bb3ab 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -244,20 +244,9 @@ static void __exit keyspan_exit (void) module_init(keyspan_init); module_exit(keyspan_exit); -static void keyspan_rx_throttle (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); -} - - -static void keyspan_rx_unthrottle (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); -} - - -static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) +static void keyspan_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; dbg("%s", __func__); @@ -273,14 +262,13 @@ static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) } -static void keyspan_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void keyspan_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int baud_rate, device_port; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; unsigned int cflag; - struct tty_struct *tty = port->tty; dbg("%s", __func__); @@ -312,12 +300,11 @@ static void keyspan_set_termios (struct usb_serial_port *port, keyspan_send_setup(port, 0); } -static int keyspan_tiocmget(struct usb_serial_port *port, struct file *file) +static int keyspan_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); unsigned int value; - struct keyspan_port_private *p_priv; - - p_priv = usb_get_serial_port_data(port); value = ((p_priv->rts_state) ? TIOCM_RTS : 0) | ((p_priv->dtr_state) ? TIOCM_DTR : 0) | @@ -329,18 +316,16 @@ static int keyspan_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int keyspan_tiocmset(struct usb_serial_port *port, struct file *file, +static int keyspan_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct keyspan_port_private *p_priv; - - p_priv = usb_get_serial_port_data(port); + struct usb_serial_port *port = tty->driver_data; + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); if (set & TIOCM_RTS) p_priv->rts_state = 1; if (set & TIOCM_DTR) p_priv->dtr_state = 1; - if (clear & TIOCM_RTS) p_priv->rts_state = 0; if (clear & TIOCM_DTR) @@ -349,16 +334,10 @@ static int keyspan_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int keyspan_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* Write function is similar for the four protocols used - with only a minor change for usa90 (usa19hs) required */ -static int keyspan_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +/* Write function is similar for the four protocols used + with only a minor change for usa90 (usa19hs) required */ +static int keyspan_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; @@ -448,7 +427,7 @@ static void usa26_indat_callback(struct urb *urb) } port = urb->context; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -479,7 +458,7 @@ static void usa26_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -496,7 +475,7 @@ static void usa2x_outdat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); dbg ("%s - urb %d", __func__, urb == p_priv->out_urbs[1]); - if (port->open_count) + if (port->port.count) usb_serial_port_softint(port); } @@ -567,10 +546,10 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -619,7 +598,7 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); @@ -629,7 +608,7 @@ static void usa28_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -704,10 +683,10 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -797,10 +776,10 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -839,7 +818,7 @@ static void usa49_indat_callback(struct urb *urb) } port = urb->context; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -866,7 +845,7 @@ static void usa49_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -904,7 +883,7 @@ static void usa49wg_indat_callback(struct urb *urb) return; } port = serial->port[data[i++]]; - tty = port->tty; + tty = port->port.tty; len = data[i++]; /* 0x80 bit is error flag */ @@ -912,7 +891,7 @@ static void usa49wg_indat_callback(struct urb *urb) /* no error on any byte */ i++; for (x = 1; x < len ; ++x) - if (port->open_count) + if (port->port.count) tty_insert_flip_char(tty, data[i++], 0); else @@ -930,13 +909,13 @@ static void usa49wg_indat_callback(struct urb *urb) if (stat & RXERROR_PARITY) flag |= TTY_PARITY; /* XXX should handle break (0x10) */ - if (port->open_count) + if (port->port.count) tty_insert_flip_char(tty, data[i+1], flag); i += 2; } } - if (port->open_count) + if (port->port.count) tty_flip_buffer_push(tty); } } @@ -978,7 +957,7 @@ static void usa90_indat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { /* if current mode is DMA, looks like usa28 format @@ -1021,7 +1000,7 @@ static void usa90_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -1064,10 +1043,10 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -1139,10 +1118,10 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -1177,8 +1156,9 @@ static void usa67_glocont_callback(struct urb *urb) } } -static int keyspan_write_room (struct usb_serial_port *port) +static int keyspan_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; int flip; @@ -1210,13 +1190,8 @@ static int keyspan_write_room (struct usb_serial_port *port) } -static int keyspan_chars_in_buffer (struct usb_serial_port *port) -{ - return 0; -} - - -static int keyspan_open (struct usb_serial_port *port, struct file *filp) +static int keyspan_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct keyspan_port_private *p_priv; struct keyspan_serial_private *s_priv; @@ -1225,7 +1200,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) int i, err; int baud_rate, device_port; struct urb *urb; - unsigned int cflag; + unsigned int cflag = 0; s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -1271,19 +1246,19 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) /* get the terminal config for the setup message now so we don't * need to send 2 of them */ - cflag = port->tty->termios->c_cflag; device_port = port->number - port->serial->minor; - - /* Baud rate calculation takes baud rate as an integer - so other rates can be generated if desired. */ - baud_rate = tty_get_baud_rate(port->tty); - /* If no match or invalid, leave as default */ - if (baud_rate >= 0 - && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, - NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { - p_priv->baud = baud_rate; + if (tty) { + cflag = tty->termios->c_cflag; + /* Baud rate calculation takes baud rate as an integer + so other rates can be generated if desired. */ + baud_rate = tty_get_baud_rate(tty); + /* If no match or invalid, leave as default */ + if (baud_rate >= 0 + && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, + NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { + p_priv->baud = baud_rate; + } } - /* set CTS/RTS handshake etc. */ p_priv->cflag = cflag; p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; @@ -1301,7 +1276,8 @@ static inline void stop_urb(struct urb *urb) usb_kill_urb(urb); } -static void keyspan_close(struct usb_serial_port *port, struct file *filp) +static void keyspan_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -1338,7 +1314,7 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp) stop_urb(p_priv->out_urbs[i]); } } - port->tty = NULL; + port->port.tty = NULL; } /* download the firmware to a pre-renumeration device */ @@ -2427,7 +2403,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, } /* Sending intermediate configs */ else { - if (port->open_count) + if (port->port.count) msg.portEnabled = 1; msg.txBreak = (p_priv->break_on); } diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 8bf72639b148..38b4582e0734 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -35,17 +35,18 @@ /* Function prototypes for Keyspan serial converter */ -static int keyspan_open (struct usb_serial_port *port, +static int keyspan_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void keyspan_close (struct usb_serial_port *port, +static void keyspan_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); static int keyspan_startup (struct usb_serial *serial); static void keyspan_shutdown (struct usb_serial *serial); -static void keyspan_rx_throttle (struct usb_serial_port *port); -static void keyspan_rx_unthrottle (struct usb_serial_port *port); -static int keyspan_write_room (struct usb_serial_port *port); +static int keyspan_write_room (struct tty_struct *tty); -static int keyspan_write (struct usb_serial_port *port, +static int keyspan_write (struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); @@ -53,18 +54,14 @@ static void keyspan_send_setup (struct usb_serial_port *port, int reset_port); -static int keyspan_chars_in_buffer (struct usb_serial_port *port); -static int keyspan_ioctl (struct usb_serial_port *port, - struct file *file, - unsigned int cmd, - unsigned long arg); -static void keyspan_set_termios (struct usb_serial_port *port, +static void keyspan_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); -static void keyspan_break_ctl (struct usb_serial_port *port, +static void keyspan_break_ctl (struct tty_struct *tty, int break_state); -static int keyspan_tiocmget (struct usb_serial_port *port, +static int keyspan_tiocmget (struct tty_struct *tty, struct file *file); -static int keyspan_tiocmset (struct usb_serial_port *port, +static int keyspan_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int keyspan_fake_startup (struct usb_serial *serial); @@ -567,10 +564,6 @@ static struct usb_serial_driver keyspan_1port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, @@ -591,10 +584,6 @@ static struct usb_serial_driver keyspan_2port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, @@ -615,10 +604,6 @@ static struct usb_serial_driver keyspan_4port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 60b3e22bd633..24a08ac2e4ee 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -171,7 +171,7 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - tty_wakeup(port->tty); + tty_wakeup(port->port.tty); } static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -203,7 +203,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) static void keyspan_pda_rx_interrupt (struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; unsigned char *data = urb->transfer_buffer; int i; int retval; @@ -266,7 +266,7 @@ exit: } -static void keyspan_pda_rx_throttle (struct usb_serial_port *port) +static void keyspan_pda_rx_throttle(struct tty_struct *tty) { /* stop receiving characters. We just turn off the URB request, and let chars pile up in the device. If we're doing hardware @@ -274,14 +274,15 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port) fills up. If we're doing XON/XOFF, this would be a good time to send an XOFF, although it might make sense to foist that off upon the device too. */ - + struct usb_serial_port *port = tty->driver_data; dbg("keyspan_pda_rx_throttle port %d", port->number); usb_kill_urb(port->interrupt_in_urb); } -static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port) +static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; /* just restart the receive interrupt URB */ dbg("keyspan_pda_rx_unthrottle port %d", port->number); port->interrupt_in_urb->dev = port->serial->dev; @@ -330,8 +331,9 @@ static speed_t keyspan_pda_setbaud (struct usb_serial *serial, speed_t baud) } -static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state) +static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int value; int result; @@ -354,8 +356,8 @@ static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state } -static void keyspan_pda_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void keyspan_pda_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; speed_t speed; @@ -380,7 +382,7 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, For now, just do baud. */ - speed = tty_get_baud_rate(port->tty); + speed = tty_get_baud_rate(tty); speed = keyspan_pda_setbaud(serial, speed); if (speed == 0) { @@ -390,8 +392,8 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, } /* Only speed can change so copy the old h/w parameters then encode the new speed */ - tty_termios_copy_hw(port->tty->termios, old_termios); - tty_encode_baud_rate(port->tty, speed, speed); + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, speed, speed); } @@ -425,8 +427,9 @@ static int keyspan_pda_set_modem_info(struct usb_serial *serial, return rc; } -static int keyspan_pda_tiocmget(struct usb_serial_port *port, struct file *file) +static int keyspan_pda_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int rc; unsigned char status; @@ -445,9 +448,10 @@ static int keyspan_pda_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int keyspan_pda_tiocmset(struct usb_serial_port *port, struct file *file, +static int keyspan_pda_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int rc; unsigned char status; @@ -469,23 +473,8 @@ static int keyspan_pda_tiocmset(struct usb_serial_port *port, struct file *file, return rc; } -static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - case TIOCGICOUNT: - /* return count of modemline transitions */ - return 0; /* TODO */ - } - - return -ENOIOCTLCMD; -} - -static int keyspan_pda_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int keyspan_pda_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int request_unthrottle = 0; @@ -607,22 +596,21 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb) } -static int keyspan_pda_write_room (struct usb_serial_port *port) +static int keyspan_pda_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_pda_private *priv; - priv = usb_get_serial_port_data(port); - /* used by n_tty.c for processing of tabs and such. Giving it our conservative guess is probably good enough, but needs testing by running a console through the device. */ - return (priv->tx_room); } -static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) +static int keyspan_pda_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_pda_private *priv; unsigned long flags; int ret = 0; @@ -640,7 +628,8 @@ static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) } -static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) +static int keyspan_pda_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; unsigned char room; @@ -672,7 +661,7 @@ static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) /* the normal serial device seems to always turn on DTR and RTS here, so do the same */ - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) ); else keyspan_pda_set_modem_info(serial, 0); @@ -690,13 +679,14 @@ error: } -static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) +static void keyspan_pda_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; if (serial->dev) { /* the normal serial device seems to always shut off DTR and RTS now */ - if (port->tty->termios->c_cflag & HUPCL) + if (tty->termios->c_cflag & HUPCL) keyspan_pda_set_modem_info(serial, 0); /* shutdown our bulk reads and writes */ @@ -832,7 +822,6 @@ static struct usb_serial_driver keyspan_pda_device = { .chars_in_buffer = keyspan_pda_chars_in_buffer, .throttle = keyspan_pda_rx_throttle, .unthrottle = keyspan_pda_rx_unthrottle, - .ioctl = keyspan_pda_ioctl, .set_termios = keyspan_pda_set_termios, .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 79787eda9524..4a38ec8f5fe3 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -74,29 +74,33 @@ static int debug; */ static int klsi_105_startup (struct usb_serial *serial); static void klsi_105_shutdown (struct usb_serial *serial); -static int klsi_105_open (struct usb_serial_port *port, +static int klsi_105_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void klsi_105_close (struct usb_serial_port *port, +static void klsi_105_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static int klsi_105_write (struct usb_serial_port *port, +static int klsi_105_write (struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); static void klsi_105_write_bulk_callback (struct urb *urb); -static int klsi_105_chars_in_buffer (struct usb_serial_port *port); -static int klsi_105_write_room (struct usb_serial_port *port); +static int klsi_105_chars_in_buffer (struct tty_struct *tty); +static int klsi_105_write_room (struct tty_struct *tty); static void klsi_105_read_bulk_callback (struct urb *urb); -static void klsi_105_set_termios (struct usb_serial_port *port, +static void klsi_105_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); -static void klsi_105_throttle (struct usb_serial_port *port); -static void klsi_105_unthrottle (struct usb_serial_port *port); +static void klsi_105_throttle (struct tty_struct *tty); +static void klsi_105_unthrottle (struct tty_struct *tty); /* -static void klsi_105_break_ctl (struct usb_serial_port *port, +static void klsi_105_break_ctl (struct tty_struct *tty, int break_state ); */ -static int klsi_105_tiocmget (struct usb_serial_port *port, +static int klsi_105_tiocmget (struct tty_struct *tty, struct file *file); -static int klsi_105_tiocmset (struct usb_serial_port *port, +static int klsi_105_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); @@ -361,7 +365,8 @@ static void klsi_105_shutdown (struct usb_serial *serial) } } /* klsi_105_shutdown */ -static int klsi_105_open (struct usb_serial_port *port, struct file *filp) +static int klsi_105_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int retval = 0; @@ -375,7 +380,7 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) /* force low_latency on so that our tty_push actually forces * the data through - * port->tty->low_latency = 1; */ + * tty->low_latency = 1; */ /* Do a defined restart: * Set up sane default baud rate and send the 'READ_ON' @@ -393,12 +398,12 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) /* set up termios structure */ spin_lock_irqsave (&priv->lock, flags); - priv->termios.c_iflag = port->tty->termios->c_iflag; - priv->termios.c_oflag = port->tty->termios->c_oflag; - priv->termios.c_cflag = port->tty->termios->c_cflag; - priv->termios.c_lflag = port->tty->termios->c_lflag; + priv->termios.c_iflag = tty->termios->c_iflag; + priv->termios.c_oflag = tty->termios->c_oflag; + priv->termios.c_cflag = tty->termios->c_cflag; + priv->termios.c_lflag = tty->termios->c_lflag; for (i=0; itermios.c_cc[i] = port->tty->termios->c_cc[i]; + priv->termios.c_cc[i] = tty->termios->c_cc[i]; priv->cfg.pktlen = cfg.pktlen; priv->cfg.baudrate = cfg.baudrate; priv->cfg.databits = cfg.databits; @@ -452,7 +457,8 @@ exit: } /* klsi_105_open */ -static void klsi_105_close (struct usb_serial_port *port, struct file *filp) +static void klsi_105_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int rc; @@ -493,8 +499,8 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) #define KLSI_105_DATA_OFFSET 2 /* in the bulk urb data block */ -static int klsi_105_write (struct usb_serial_port *port, - const unsigned char *buf, int count) +static int klsi_105_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int result, size; @@ -584,8 +590,9 @@ static void klsi_105_write_bulk_callback ( struct urb *urb) /* return number of characters currently in the writing process */ -static int klsi_105_chars_in_buffer (struct usb_serial_port *port) +static int klsi_105_chars_in_buffer (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int chars = 0; int i; unsigned long flags; @@ -605,8 +612,9 @@ static int klsi_105_chars_in_buffer (struct usb_serial_port *port) return (chars); } -static int klsi_105_write_room (struct usb_serial_port *port) +static int klsi_105_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int room = 0; @@ -660,7 +668,7 @@ static void klsi_105_read_bulk_callback (struct urb *urb) } else { int bytes_sent = ((__u8 *) data)[0] + ((unsigned int) ((__u8 *) data)[1] << 8); - tty = port->tty; + tty = port->port.tty; /* we should immediately resubmit the URB, before attempting * to pass the data on to the tty layer. But that needs locking * against re-entry an then mixed-up data because of @@ -699,11 +707,11 @@ static void klsi_105_read_bulk_callback (struct urb *urb) } /* klsi_105_read_bulk_callback */ -static void klsi_105_set_termios (struct usb_serial_port *port, +static void klsi_105_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct klsi_105_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int iflag = tty->termios->c_iflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = tty->termios->c_cflag; @@ -863,8 +871,9 @@ static void klsi_105_set_termios (struct usb_serial_port *port, #if 0 -static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +static void mct_u232_break_ctl( struct tty_struct *tty, int break_state ) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = (struct mct_u232_private *)port->private; unsigned char lcr = priv->last_lcr; @@ -878,8 +887,9 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) } /* mct_u232_break_ctl */ #endif -static int klsi_105_tiocmget (struct usb_serial_port *port, struct file *file) +static int klsi_105_tiocmget (struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct klsi_105_private *priv = usb_get_serial_port_data(port); unsigned long flags; int rc; @@ -900,7 +910,7 @@ static int klsi_105_tiocmget (struct usb_serial_port *port, struct file *file) return (int)line_state; } -static int klsi_105_tiocmset (struct usb_serial_port *port, struct file *file, +static int klsi_105_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { int retval = -EINVAL; @@ -929,14 +939,16 @@ static int klsi_105_tiocmset (struct usb_serial_port *port, struct file *file, return retval; } -static void klsi_105_throttle (struct usb_serial_port *port) +static void klsi_105_throttle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); } -static void klsi_105_unthrottle (struct usb_serial_port *port) +static void klsi_105_unthrottle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int result; dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 693f00da7c03..40c67f0096b1 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -70,19 +70,22 @@ static int debug; /* Function prototypes */ static int kobil_startup (struct usb_serial *serial); static void kobil_shutdown (struct usb_serial *serial); -static int kobil_open (struct usb_serial_port *port, struct file *filp); -static void kobil_close (struct usb_serial_port *port, struct file *filp); -static int kobil_write (struct usb_serial_port *port, +static int kobil_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void kobil_close (struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static int kobil_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int kobil_write_room(struct usb_serial_port *port); -static int kobil_ioctl(struct usb_serial_port *port, struct file *file, +static int kobil_write_room(struct tty_struct *tty); +static int kobil_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -static int kobil_tiocmget(struct usb_serial_port *port, struct file *file); -static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, +static int kobil_tiocmget(struct tty_struct *tty, struct file *file); +static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void kobil_read_int_callback( struct urb *urb ); static void kobil_write_callback( struct urb *purb ); -static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old); +static void kobil_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); static struct usb_device_id id_table [] = { @@ -201,8 +204,8 @@ static void kobil_shutdown (struct usb_serial *serial) dbg("%s - port %d", __func__, serial->port[0]->number); for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i]->open_count > 0) { - kobil_close (serial->port[i], NULL); + while (serial->port[i]->port.count > 0) { + kobil_close (NULL, serial->port[i], NULL); } kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); @@ -210,7 +213,8 @@ static void kobil_shutdown (struct usb_serial *serial) } -static int kobil_open (struct usb_serial_port *port, struct file *filp) +static int kobil_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int result = 0; struct kobil_private *priv; @@ -229,14 +233,15 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ - port->tty->low_latency = 1; - - // without this, every push_tty_char is echoed :-( - port->tty->termios->c_lflag = 0; - port->tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); - port->tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; - port->tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) - + if (tty) { + tty->low_latency = 1; + + /* Default to echo off and other sane device settings */ + tty->termios->c_lflag = 0; + tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); + tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; + tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) + } // allocate memory for transfer buffer transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (! transfer_buffer) { @@ -330,7 +335,8 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) } -static void kobil_close (struct usb_serial_port *port, struct file *filp) +static void kobil_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -360,7 +366,7 @@ static void kobil_read_int_callback(struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { // BEGIN DEBUG @@ -395,7 +401,7 @@ static void kobil_write_callback( struct urb *purb ) } -static int kobil_write (struct usb_serial_port *port, +static int kobil_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { int length = 0; @@ -417,12 +423,9 @@ static int kobil_write (struct usb_serial_port *port, // Copy data to buffer memcpy (priv->buf + priv->filled, buf, count); - usb_serial_debug_data(debug, &port->dev, __func__, count, priv->buf + priv->filled); - priv->filled = priv->filled + count; - // only send complete block. TWIN, KAAN SIM and adapter K use the same protocol. if ( ((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) { @@ -478,15 +481,17 @@ static int kobil_write (struct usb_serial_port *port, } -static int kobil_write_room (struct usb_serial_port *port) +static int kobil_write_room (struct tty_struct *tty) { //dbg("%s - port %d", __func__, port->number); + /* FIXME */ return 8; } -static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) +static int kobil_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct kobil_private * priv; int result; unsigned char *transfer_buffer; @@ -524,9 +529,10 @@ static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, +static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct kobil_private * priv; int result; int dtr = 0; @@ -590,12 +596,13 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, return (result < 0) ? result : 0; } -static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old) +static void kobil_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old) { struct kobil_private * priv; int result; unsigned short urb_val = 0; - int c_cflag = port->tty->termios->c_cflag; + int c_cflag = tty->termios->c_cflag; speed_t speed; void * settings; @@ -604,7 +611,7 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old // This device doesn't support ioctl calls return; - switch (speed = tty_get_baud_rate(port->tty)) { + switch (speed = tty_get_baud_rate(tty)) { case 1200: urb_val = SUSBCR_SBR_1200; break; @@ -634,8 +641,8 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old urb_val |= SUSBCR_SPASB_NoParity; strcat(settings, "No Parity"); } - port->tty->termios->c_cflag &= ~CMSPAR; - tty_encode_baud_rate(port->tty, speed, speed); + tty->termios->c_cflag &= ~CMSPAR; + tty_encode_baud_rate(tty, speed, speed); result = usb_control_msg( port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0 ), @@ -650,8 +657,9 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old kfree(settings); } -static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int kobil_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct kobil_private * priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; int transfer_buffer_length = 8; @@ -662,7 +670,7 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigne return 0; switch (cmd) { - case TCFLSH: // 0x540B + case TCFLSH: transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL); if (! transfer_buffer) return -ENOBUFS; @@ -680,7 +688,7 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigne dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __func__, port->number, result); kfree(transfer_buffer); - return (result < 0) ? -EFAULT : 0; + return (result < 0) ? -EIO: 0; default: return -ENOIOCTLCMD; } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 5fc2cef30e39..7bce4302a5f9 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -92,26 +92,25 @@ static int debug; */ static int mct_u232_startup (struct usb_serial *serial); static void mct_u232_shutdown (struct usb_serial *serial); -static int mct_u232_open (struct usb_serial_port *port, +static int mct_u232_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void mct_u232_close (struct usb_serial_port *port, +static void mct_u232_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); static void mct_u232_read_int_callback (struct urb *urb); -static void mct_u232_set_termios (struct usb_serial_port *port, +static void mct_u232_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios * old); -static int mct_u232_ioctl (struct usb_serial_port *port, - struct file * file, - unsigned int cmd, - unsigned long arg); -static void mct_u232_break_ctl (struct usb_serial_port *port, +static void mct_u232_break_ctl (struct tty_struct *tty, int break_state ); -static int mct_u232_tiocmget (struct usb_serial_port *port, +static int mct_u232_tiocmget (struct tty_struct *tty, struct file *file); -static int mct_u232_tiocmset (struct usb_serial_port *port, +static int mct_u232_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void mct_u232_throttle (struct usb_serial_port *port); -static void mct_u232_unthrottle (struct usb_serial_port *port); +static void mct_u232_throttle (struct tty_struct *tty); +static void mct_u232_unthrottle (struct tty_struct *tty); /* @@ -149,7 +148,6 @@ static struct usb_serial_driver mct_u232_device = { .throttle = mct_u232_throttle, .unthrottle = mct_u232_unthrottle, .read_int_callback = mct_u232_read_int_callback, - .ioctl = mct_u232_ioctl, .set_termios = mct_u232_set_termios, .break_ctl = mct_u232_break_ctl, .tiocmget = mct_u232_tiocmget, @@ -224,8 +222,8 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value } } -static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port, - speed_t value) +static int mct_u232_set_baud_rate(struct tty_struct *tty, + struct usb_serial *serial, struct usb_serial_port *port, speed_t value) { __le32 divisor; int rc; @@ -243,7 +241,7 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_p if (rc < 0) /*FIXME: What value speed results */ err("Set BAUD RATE %d failed (error = %d)", value, rc); else - tty_encode_baud_rate(port->tty, speed, speed); + tty_encode_baud_rate(tty, speed, speed); dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor); /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which @@ -272,7 +270,7 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_p err("Sending USB device request code %d failed (error = %d)", MCT_U232_SET_UNKNOWN1_REQUEST, rc); - if (port && C_CRTSCTS(port->tty)) { + if (port && C_CRTSCTS(tty)) { cts_enable_byte = 1; } @@ -411,7 +409,8 @@ static void mct_u232_shutdown (struct usb_serial *serial) } } /* mct_u232_shutdown */ -static int mct_u232_open (struct usb_serial_port *port, struct file *filp) +static int mct_u232_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); @@ -437,7 +436,7 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp) * either. */ spin_lock_irqsave(&priv->lock, flags); - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) priv->control_state = TIOCM_DTR | TIOCM_RTS; else priv->control_state = 0; @@ -481,15 +480,16 @@ error: } /* mct_u232_open */ -static void mct_u232_close (struct usb_serial_port *port, struct file *filp) +static void mct_u232_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { unsigned int c_cflag; unsigned int control_state; struct mct_u232_private *priv = usb_get_serial_port_data(port); dbg("%s port %d", __func__, port->number); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; mutex_lock(&port->serial->disc_mutex); if (c_cflag & HUPCL && !port->serial->disconnected) { /* drop DTR and RTS */ @@ -553,7 +553,7 @@ static void mct_u232_read_int_callback (struct urb *urb) */ if (urb->transfer_buffer_length > 2) { int i; - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); @@ -583,7 +583,7 @@ static void mct_u232_read_int_callback (struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & MCT_U232_LSR_ERR) { - tty = port->tty; + tty = port->port.tty; /* Overrun Error */ if (priv->last_lsr & MCT_U232_LSR_OE) { } @@ -606,12 +606,13 @@ exit: __func__, retval); } /* mct_u232_read_int_callback */ -static void mct_u232_set_termios (struct usb_serial_port *port, +static void mct_u232_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long flags; @@ -638,7 +639,7 @@ static void mct_u232_set_termios (struct usb_serial_port *port, mct_u232_set_modem_ctrl(serial, control_state); } - mct_u232_set_baud_rate(serial, port, tty_get_baud_rate(port->tty)); + mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty)); if ((cflag & CBAUD) == B0 ) { dbg("%s: baud is B0", __func__); @@ -689,8 +690,9 @@ static void mct_u232_set_termios (struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); } /* mct_u232_set_termios */ -static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned char lcr; @@ -709,8 +711,9 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) } /* mct_u232_break_ctl */ -static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file) +static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned int control_state; unsigned long flags; @@ -724,9 +727,10 @@ static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file) return control_state; } -static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, +static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned int control_state; @@ -751,73 +755,46 @@ static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, return mct_u232_set_modem_ctrl(serial, control_state); } -static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, - unsigned int cmd, unsigned long arg) -{ - dbg("%scmd=0x%x", __func__, cmd); - - /* Based on code from acm.c and others */ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return( 0 ); - - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - - default: - dbg("%s: arg not supported - 0x%04x", __func__,cmd); - return(-ENOIOCTLCMD); - break; - } - return 0; -} /* mct_u232_ioctl */ - -static void mct_u232_throttle (struct usb_serial_port *port) +static void mct_u232_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int control_state; - struct tty_struct *tty; - tty = port->tty; dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->rx_flags |= THROTTLED; if (C_CRTSCTS(tty)) { - priv->control_state &= ~TIOCM_RTS; - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - (void) mct_u232_set_modem_ctrl(port->serial, control_state); + priv->control_state &= ~TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } -static void mct_u232_unthrottle (struct usb_serial_port *port) +static void mct_u232_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int control_state; - struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); - tty = port->tty; spin_lock_irqsave(&priv->lock, flags); if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { - priv->rx_flags &= ~THROTTLED; - priv->control_state |= TIOCM_RTS; - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - (void) mct_u232_set_modem_ctrl(port->serial, control_state); + priv->rx_flags &= ~THROTTLED; + priv->control_state |= TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 50f1fe263338..d47f0814ce2d 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -218,7 +218,7 @@ static void mos7720_bulk_in_callback(struct urb *urb) data = urb->transfer_buffer; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -264,7 +264,7 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) dbg("Entering ........."); - tty = mos7720_port->port->tty; + tty = mos7720_port->port->port.tty; if (tty && mos7720_port->open) tty_wakeup(tty); @@ -320,7 +320,8 @@ static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, return status; } -static int mos7720_open(struct usb_serial_port *port, struct file * filp) +static int mos7720_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial; struct usb_serial_port *port0; @@ -443,14 +444,12 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); -//Matrix - /* force low_latency on so that our tty_push actually forces * * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* see if we've set up our endpoint info yet * * (can't set it up in mos7720_startup as the * @@ -515,8 +514,9 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) * system, * Otherwise we return a negative error number. */ -static int mos7720_chars_in_buffer(struct usb_serial_port *port) +static int mos7720_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int chars = 0; struct moschip_port *mos7720_port; @@ -537,7 +537,8 @@ static int mos7720_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void mos7720_close(struct usb_serial_port *port, struct file *filp) +static void mos7720_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7720_port; @@ -588,8 +589,9 @@ static void mos7720_close(struct usb_serial_port *port, struct file *filp) dbg("Leaving %s", __func__); } -static void mos7720_break(struct usb_serial_port *port, int break_state) +static void mos7720_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char data; struct usb_serial *serial; struct moschip_port *mos7720_port; @@ -621,8 +623,9 @@ static void mos7720_break(struct usb_serial_port *port, int break_state) * If successful, we return the amount of room that we have for this port * Otherwise we return a negative error number. */ -static int mos7720_write_room(struct usb_serial_port *port) +static int mos7720_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; int room = 0; int i; @@ -645,8 +648,8 @@ static int mos7720_write_room(struct usb_serial_port *port) return room; } -static int mos7720_write(struct usb_serial_port *port, - const unsigned char *data, int count) +static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { int status; int i; @@ -719,10 +722,10 @@ exit: return bytes_sent; } -static void mos7720_throttle(struct usb_serial_port *port) +static void mos7720_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; - struct tty_struct *tty; int status; dbg("%s- port %d\n", __func__, port->number); @@ -739,16 +742,10 @@ static void mos7720_throttle(struct usb_serial_port *port) dbg("%s: Entering ..........", __func__); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = mos7720_write(port, &stop_char, 1); + status = mos7720_write(tty, port, &stop_char, 1); if (status <= 0) return; } @@ -764,11 +761,11 @@ static void mos7720_throttle(struct usb_serial_port *port) } } -static void mos7720_unthrottle(struct usb_serial_port *port) +static void mos7720_unthrottle(struct tty_struct *tty) { - struct tty_struct *tty; - int status; + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); + int status; if (mos7720_port == NULL) return; @@ -780,16 +777,10 @@ static void mos7720_unthrottle(struct usb_serial_port *port) dbg("%s: Entering ..........", __func__); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = mos7720_write(port, &start_char, 1); + status = mos7720_write(tty, port, &start_char, 1); if (status <= 0) return; } @@ -1011,12 +1002,12 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, * This routine is called to set the UART on the device to match * the specified new settings. */ -static void change_port_settings(struct moschip_port *mos7720_port, +static void change_port_settings(struct tty_struct *tty, + struct moschip_port *mos7720_port, struct ktermios *old_termios) { struct usb_serial_port *port; struct usb_serial *serial; - struct tty_struct *tty; int baud; unsigned cflag; unsigned iflag; @@ -1042,8 +1033,6 @@ static void change_port_settings(struct moschip_port *mos7720_port, return; } - tty = mos7720_port->port->tty; - dbg("%s: Entering ..........", __func__); lData = UART_LCR_WLEN8; @@ -1198,14 +1187,13 @@ static void change_port_settings(struct moschip_port *mos7720_port, * this function is called by the tty driver when it wants to change the * termios structure. */ -static void mos7720_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void mos7720_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int status; unsigned int cflag; struct usb_serial *serial; struct moschip_port *mos7720_port; - struct tty_struct *tty; serial = port->serial; @@ -1214,9 +1202,6 @@ static void mos7720_set_termios(struct usb_serial_port *port, if (mos7720_port == NULL) return; - tty = port->tty; - - if (!mos7720_port->open) { dbg("%s - port not opened", __func__); return; @@ -1237,7 +1222,7 @@ static void mos7720_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); /* change the port settings to the new ones specified */ - change_port_settings(mos7720_port, old_termios); + change_port_settings(tty, mos7720_port, old_termios); if(!port->read_urb) { dbg("%s","URB KILLED !!!!!\n"); @@ -1264,13 +1249,13 @@ static void mos7720_set_termios(struct usb_serial_port *port, * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ -static int get_lsr_info(struct moschip_port *mos7720_port, +static int get_lsr_info(struct tty_struct *tty, struct moschip_port *mos7720_port, unsigned int __user *value) { int count; unsigned int result = 0; - count = mos7720_chars_in_buffer(mos7720_port->port); + count = mos7720_chars_in_buffer(tty); if (count == 0) { dbg("%s -- Empty", __func__); result = TIOCSER_TEMT; @@ -1290,7 +1275,7 @@ static int get_number_bytes_avail(struct moschip_port *mos7720_port, unsigned int __user *value) { unsigned int result = 0; - struct tty_struct *tty = mos7720_port->port->tty; + struct tty_struct *tty = mos7720_port->port->port.tty; if (!tty) return -ENOIOCTLCMD; @@ -1407,9 +1392,10 @@ static int get_serial_info(struct moschip_port *mos7720_port, return 0; } -static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, +static int mos7720_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; struct async_icount cnow; struct async_icount cprev; @@ -1431,9 +1417,10 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return get_lsr_info(mos7720_port, (unsigned int __user *)arg); + return get_lsr_info(tty, mos7720_port, (unsigned int __user *)arg); return 0; + /* FIXME: These should be using the mode methods */ case TIOCMBIS: case TIOCMBIC: case TIOCMSET: @@ -1452,10 +1439,6 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, return get_serial_info(mos7720_port, (struct serial_struct __user *)arg); - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - case TIOCMIWAIT: dbg("%s (%d) TIOCMIWAIT", __func__, port->number); cprev = mos7720_port->icount; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 78f2f6db494d..2b1fded6619d 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -710,7 +710,7 @@ static void mos7840_bulk_in_callback(struct urb *urb) dbg("%s", "Entering ........... \n"); if (urb->actual_length) { - tty = mos7840_port->port->tty; + tty = mos7840_port->port->port.tty; if (tty) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -774,7 +774,7 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) dbg("%s \n", "Entering ........."); - tty = mos7840_port->port->tty; + tty = mos7840_port->port->port.tty; if (tty && mos7840_port->open) tty_wakeup(tty); @@ -804,7 +804,8 @@ static int mos7840_serial_probe(struct usb_serial *serial, * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_open(struct usb_serial_port *port, struct file *filp) +static int mos7840_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int response; int j; @@ -1008,8 +1009,8 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* Check to see if we've set up our endpoint info yet * * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ @@ -1104,11 +1105,12 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) * been written, but hasn't made it out the port yet) * If successful, we return the number of bytes left to be written in the * system, - * Otherwise we return a negative error number. + * Otherwise we return zero. *****************************************************************************/ -static int mos7840_chars_in_buffer(struct usb_serial_port *port) +static int mos7840_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int chars = 0; unsigned long flags; @@ -1118,21 +1120,19 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Invalid port \n"); - return -1; + return 0; } mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) { dbg("%s \n", "mos7840_break:leaving ..........."); - return -1; + return 0; } spin_lock_irqsave(&mos7840_port->pool_lock,flags); - for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->busy[i]) { + for (i = 0; i < NUM_URBS; ++i) + if (mos7840_port->busy[i]) chars += URB_TRANSFER_BUFFER_SIZE; - } - } spin_unlock_irqrestore(&mos7840_port->pool_lock,flags); dbg("%s - returns %d", __func__, chars); return chars; @@ -1149,7 +1149,8 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) * 3. A timeout of 3 seconds without activity has expired * ************************************************************************/ -static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) +static void mos7840_block_until_tx_empty(struct tty_struct *tty, + struct moschip_port *mos7840_port) { int timeout = HZ / 10; int wait = 30; @@ -1157,7 +1158,7 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) while (1) { - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); /* Check for Buffer status */ if (count <= 0) { @@ -1185,7 +1186,8 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void mos7840_close(struct usb_serial_port *port, struct file *filp) +static void mos7840_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1226,20 +1228,17 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) } } - if (serial->dev) { + if (serial->dev) /* flush and block until tx is empty */ - mos7840_block_until_tx_empty(mos7840_port); - } + mos7840_block_until_tx_empty(tty, mos7840_port); /* While closing port, shutdown all bulk read, write * * and interrupt read if they exists */ if (serial->dev) { - if (mos7840_port->write_urb) { dbg("%s", "Shutdown bulk write\n"); usb_kill_urb(mos7840_port->write_urb); } - if (mos7840_port->read_urb) { dbg("%s", "Shutdown bulk read\n"); usb_kill_urb(mos7840_port->read_urb); @@ -1247,11 +1246,10 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) if ((&mos7840_port->control_urb)) { dbg("%s", "Shutdown control read\n"); // usb_kill_urb (mos7840_port->control_urb); - } } -// if(mos7840_port->ctrl_buf != NULL) -// kfree(mos7840_port->ctrl_buf); +// if(mos7840_port->ctrl_buf != NULL) +// kfree(mos7840_port->ctrl_buf); port0->open_ports--; dbg("mos7840_num_open_ports in close%d:in port%d\n", port0->open_ports, port->number); @@ -1293,15 +1291,15 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) * ************************************************************************/ -static void mos7840_block_until_chase_response(struct moschip_port - *mos7840_port) +static void mos7840_block_until_chase_response(struct tty_struct *tty, + struct moschip_port *mos7840_port) { int timeout = 1 * HZ; int wait = 10; int count; while (1) { - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); /* Check for Buffer status */ if (count <= 0) { @@ -1328,8 +1326,9 @@ static void mos7840_block_until_chase_response(struct moschip_port * mos7840_break * this function sends a break to the port *****************************************************************************/ -static void mos7840_break(struct usb_serial_port *port, int break_state) +static void mos7840_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char data; struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1354,17 +1353,14 @@ static void mos7840_break(struct usb_serial_port *port, int break_state) return; } - if (serial->dev) { - + if (serial->dev) /* flush and block until tx is empty */ - mos7840_block_until_chase_response(mos7840_port); - } + mos7840_block_until_chase_response(tty, mos7840_port); - if (break_state == -1) { + if (break_state == -1) data = mos7840_port->shadowLCR | LCR_SET_BREAK; - } else { + else data = mos7840_port->shadowLCR & ~LCR_SET_BREAK; - } mos7840_port->shadowLCR = data; dbg("mcs7840_break mos7840_port->shadowLCR is %x\n", @@ -1383,8 +1379,9 @@ static void mos7840_break(struct usb_serial_port *port, int break_state) * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_write_room(struct usb_serial_port *port) +static int mos7840_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int room = 0; unsigned long flags; @@ -1426,7 +1423,7 @@ static int mos7840_write_room(struct usb_serial_port *port) * return a negative error number. *****************************************************************************/ -static int mos7840_write(struct usb_serial_port *port, +static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { int status; @@ -1555,8 +1552,7 @@ static int mos7840_write(struct usb_serial_port *port, mos7840_port->icount.tx += transfer_size; smp_wmb(); dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); - exit: - +exit: return bytes_sent; } @@ -1567,10 +1563,10 @@ static int mos7840_write(struct usb_serial_port *port, * being read from the port. *****************************************************************************/ -static void mos7840_throttle(struct usb_serial_port *port) +static void mos7840_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; - struct tty_struct *tty; int status; if (mos7840_port_paranoia_check(port, __func__)) { @@ -1592,32 +1588,21 @@ static void mos7840_throttle(struct usb_serial_port *port) dbg("%s", "Entering .......... \n"); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = mos7840_write(port, &stop_char, 1); - if (status <= 0) { + status = mos7840_write(tty, port, &stop_char, 1); + if (status <= 0) return; - } } - /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7840_port->shadowMCR &= ~MCR_RTS; status = 0; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); - - if (status < 0) { + if (status < 0) return; - } } return; @@ -1628,9 +1613,9 @@ static void mos7840_throttle(struct usb_serial_port *port) * this function is called by the tty driver when it wants to resume the data * being read from the port (called after SerialThrottle is called) *****************************************************************************/ -static void mos7840_unthrottle(struct usb_serial_port *port) +static void mos7840_unthrottle(struct tty_struct *tty) { - struct tty_struct *tty; + struct usb_serial_port *port = tty->driver_data; int status; struct moschip_port *mos7840_port = mos7840_get_port_private(port); @@ -1649,38 +1634,28 @@ static void mos7840_unthrottle(struct usb_serial_port *port) dbg("%s", "Entering .......... \n"); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = mos7840_write(port, &start_char, 1); - if (status <= 0) { + status = mos7840_write(tty, port, &start_char, 1); + if (status <= 0) return; - } } /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7840_port->shadowMCR |= MCR_RTS; status = 0; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); - if (status < 0) { + if (status < 0) return; - } } - - return; } -static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) +static int mos7840_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; unsigned int result; __u16 msr; @@ -1708,9 +1683,10 @@ static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file, +static int mos7840_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; unsigned int mcr; int status; @@ -1949,10 +1925,9 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, * the specified new settings. *****************************************************************************/ -static void mos7840_change_port_settings(struct moschip_port *mos7840_port, - struct ktermios *old_termios) +static void mos7840_change_port_settings(struct tty_struct *tty, + struct moschip_port *mos7840_port, struct ktermios *old_termios) { - struct tty_struct *tty; int baud; unsigned cflag; unsigned iflag; @@ -1988,8 +1963,6 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, return; } - tty = mos7840_port->port->tty; - dbg("%s", "Entering .......... \n"); lData = LCR_BITS_8; @@ -2131,14 +2104,14 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, * the termios structure *****************************************************************************/ -static void mos7840_set_termios(struct usb_serial_port *port, +static void mos7840_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int status; unsigned int cflag; struct usb_serial *serial; struct moschip_port *mos7840_port; - struct tty_struct *tty; dbg("mos7840_set_termios: START\n"); if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Invalid port \n"); @@ -2157,8 +2130,6 @@ static void mos7840_set_termios(struct usb_serial_port *port, if (mos7840_port == NULL) return; - tty = port->tty; - if (!mos7840_port->open) { dbg("%s - port not opened", __func__); return; @@ -2176,7 +2147,7 @@ static void mos7840_set_termios(struct usb_serial_port *port, /* change the port settings to the new ones specified */ - mos7840_change_port_settings(mos7840_port, old_termios); + mos7840_change_port_settings(tty, mos7840_port, old_termios); if (!mos7840_port->read_urb) { dbg("%s", "URB KILLED !!!!!\n"); @@ -2205,13 +2176,13 @@ static void mos7840_set_termios(struct usb_serial_port *port, * allows an RS485 driver to be written in user space. *****************************************************************************/ -static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, +static int mos7840_get_lsr_info(struct tty_struct *tty, unsigned int __user *value) { int count; unsigned int result = 0; - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); if (count == 0) { dbg("%s -- Empty", __func__); result = TIOCSER_TEMT; @@ -2227,6 +2198,8 @@ static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, * function to set modem info *****************************************************************************/ +/* FIXME: Should be using the model control hooks */ + static int mos7840_set_modem_info(struct moschip_port *mos7840_port, unsigned int cmd, unsigned int __user *value) { @@ -2304,9 +2277,8 @@ static int mos7840_get_modem_info(struct moschip_port *mos7840_port, __u16 msr; unsigned int mcr = mos7840_port->shadowMCR; int status = 0; - status = - mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER, - &msr); + status = mos7840_get_uart_reg(mos7840_port->port, + MODEM_STATUS_REGISTER, &msr); result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ @@ -2359,12 +2331,12 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port, * this function handles any ioctl calls to the driver *****************************************************************************/ -static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, +static int mos7840_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; void __user *argp = (void __user *)arg; struct moschip_port *mos7840_port; - struct tty_struct *tty; struct async_icount cnow; struct async_icount cprev; @@ -2381,8 +2353,6 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (mos7840_port == NULL) return -1; - tty = mos7840_port->port->tty; - dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { @@ -2390,9 +2360,10 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return mos7840_get_lsr_info(mos7840_port, argp); + return mos7840_get_lsr_info(tty, argp); return 0; + /* FIXME: use the modem hooks and remove this */ case TIOCMBIS: case TIOCMBIC: case TIOCMSET: @@ -2463,13 +2434,9 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (copy_to_user(argp, &icount, sizeof(icount))) return -EFAULT; return 0; - - case TIOCEXBAUD: - return 0; default: break; } - return -ENOIOCTLCMD; } diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 43c8894353bf..d6736531a0fa 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,7 +64,7 @@ static void navman_read_int_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -79,7 +79,8 @@ exit: __func__, result); } -static int navman_open(struct usb_serial_port *port, struct file *filp) +static int navman_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int result = 0; @@ -96,14 +97,15 @@ static int navman_open(struct usb_serial_port *port, struct file *filp) return result; } -static void navman_close(struct usb_serial_port *port, struct file *filp) +static void navman_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->interrupt_in_urb); } -static int navman_write(struct usb_serial_port *port, +static int navman_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 7b7422f49478..5a2d045562f0 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -61,12 +61,12 @@ static int debug; #define BT_IGNITIONPRO_ID 0x2000 /* This one seems to be a re-branded ZyXEL device */ /* function prototypes */ -static int omninet_open (struct usb_serial_port *port, struct file *filp); -static void omninet_close (struct usb_serial_port *port, struct file *filp); +static int omninet_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void omninet_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void omninet_read_bulk_callback (struct urb *urb); static void omninet_write_bulk_callback (struct urb *urb); -static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int omninet_write_room (struct usb_serial_port *port); +static int omninet_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int omninet_write_room (struct tty_struct *tty); static void omninet_shutdown (struct usb_serial *serial); static int omninet_attach (struct usb_serial *serial); @@ -157,7 +157,8 @@ static int omninet_attach (struct usb_serial *serial) return 0; } -static int omninet_open (struct usb_serial_port *port, struct file *filp) +static int omninet_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; @@ -166,7 +167,7 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); wport = serial->port[1]; - wport->tty = port->tty; + wport->port.tty = tty; /* FIXME */ /* Start reading from the device */ usb_fill_bulk_urb(port->read_urb, serial->dev, @@ -181,7 +182,8 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp) return result; } -static void omninet_close (struct usb_serial_port *port, struct file * filp) +static void omninet_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); @@ -221,9 +223,9 @@ static void omninet_read_bulk_callback (struct urb *urb) if (urb->actual_length && header->oh_len) { for (i = 0; i < header->oh_len; i++) { - tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0); + tty_insert_flip_char(port->port.tty, data[OMNINET_DATAOFFSET + i], 0); } - tty_flip_buffer_push(port->tty); + tty_flip_buffer_push(port->port.tty); } /* Continue trying to always read */ @@ -238,7 +240,8 @@ static void omninet_read_bulk_callback (struct urb *urb) return; } -static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport = serial->port[1]; @@ -290,8 +293,9 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf } -static int omninet_write_room (struct usb_serial_port *port) +static int omninet_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct usb_serial_port *wport = serial->port[1]; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 1e936a1cbe0b..4350990abf14 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -43,29 +43,23 @@ #include /* Function prototypes */ -static int option_open(struct usb_serial_port *port, struct file *filp); -static void option_close(struct usb_serial_port *port, struct file *filp); +static int option_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void option_close(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static int option_startup(struct usb_serial *serial); static void option_shutdown(struct usb_serial *serial); -static void option_rx_throttle(struct usb_serial_port *port); -static void option_rx_unthrottle(struct usb_serial_port *port); -static int option_write_room(struct usb_serial_port *port); +static int option_write_room(struct tty_struct *tty); static void option_instat_callback(struct urb *urb); -static int option_write(struct usb_serial_port *port, +static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); - -static int option_chars_in_buffer(struct usb_serial_port *port); -static int option_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static void option_set_termios(struct usb_serial_port *port, - struct ktermios *old); -static void option_break_ctl(struct usb_serial_port *port, int break_state); -static int option_tiocmget(struct usb_serial_port *port, struct file *file); -static int option_tiocmset(struct usb_serial_port *port, struct file *file, +static int option_chars_in_buffer(struct tty_struct *tty); +static void option_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int option_tiocmget(struct tty_struct *tty, struct file *file); +static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int option_send_setup(struct usb_serial_port *port); +static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port); /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 @@ -342,11 +336,7 @@ static struct usb_serial_driver option_1port_device = { .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, - .throttle = option_rx_throttle, - .unthrottle = option_rx_unthrottle, - .ioctl = option_ioctl, .set_termios = option_set_termios, - .break_ctl = option_break_ctl, .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, @@ -417,33 +407,18 @@ static void __exit option_exit(void) module_init(option_init); module_exit(option_exit); -static void option_rx_throttle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void option_rx_unthrottle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void option_break_ctl(struct usb_serial_port *port, int break_state) -{ - /* Unfortunately, I don't know how to send a break */ - dbg("%s", __func__); -} - -static void option_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void option_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); /* Doesn't support option setting */ - tty_termios_copy_hw(port->tty->termios, old_termios); - option_send_setup(port); + tty_termios_copy_hw(tty->termios, old_termios); + option_send_setup(tty, port); } -static int option_tiocmget(struct usb_serial_port *port, struct file *file) +static int option_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int value; struct option_port_private *portdata; @@ -459,9 +434,10 @@ static int option_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int option_tiocmset(struct usb_serial_port *port, struct file *file, +static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); @@ -476,17 +452,11 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return option_send_setup(port); -} - -static int option_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; + return option_send_setup(tty, port); } /* Write */ -static int option_write(struct usb_serial_port *port, +static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct option_port_private *portdata; @@ -562,7 +532,7 @@ static void option_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -572,7 +542,7 @@ static void option_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) printk(KERN_ERR "%s: resubmit read urb failed. " @@ -638,9 +608,9 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) && + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType,req_pkt->bRequest); @@ -658,8 +628,9 @@ static void option_instat_callback(struct urb *urb) } } -static int option_write_room(struct usb_serial_port *port) +static int option_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; int i; int data_len = 0; @@ -678,8 +649,9 @@ static int option_write_room(struct usb_serial_port *port) return data_len; } -static int option_chars_in_buffer(struct usb_serial_port *port) +static int option_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; int i; int data_len = 0; @@ -698,7 +670,8 @@ static int option_chars_in_buffer(struct usb_serial_port *port) return data_len; } -static int option_open(struct usb_serial_port *port, struct file *filp) +static int option_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct option_port_private *portdata; struct usb_serial *serial = port->serial; @@ -748,14 +721,16 @@ static int option_open(struct usb_serial_port *port, struct file *filp) usb_pipeout(urb->pipe), 0); */ } - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; - option_send_setup(port); + option_send_setup(tty, port); return (0); } -static void option_close(struct usb_serial_port *port, struct file *filp) +static void option_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -770,7 +745,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp) if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - option_send_setup(port); + option_send_setup(tty, port); mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ @@ -779,7 +754,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp) for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - port->tty = NULL; + port->port.tty = NULL; /* FIXME */ } /* Helper functions used by option_setup_urbs */ @@ -841,7 +816,8 @@ static void option_setup_urbs(struct usb_serial *serial) * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN * CDC. */ -static int option_send_setup(struct usb_serial_port *port) +static int option_send_setup(struct tty_struct *tty, + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct option_port_private *portdata; @@ -850,7 +826,7 @@ static int option_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (port->tty) { + if (tty) { int val = 0; if (portdata->dtr_state) val |= 0x01; @@ -861,7 +837,6 @@ static int option_send_setup(struct usb_serial_port *port) usb_rcvctrlpipe(serial->dev, 0), 0x22,0x21,val,ifNum,NULL,0,USB_CTRL_SET_TIMEOUT); } - return 0; } diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a9625c180dc3..069d276a5276 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -140,22 +140,23 @@ struct oti6858_control_pkt { && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) ) /* function prototypes */ -static int oti6858_open(struct usb_serial_port *port, struct file *filp); -static void oti6858_close(struct usb_serial_port *port, struct file *filp); -static void oti6858_set_termios(struct usb_serial_port *port, - struct ktermios *old); -static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, +static int oti6858_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void oti6858_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void oti6858_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); static void oti6858_read_bulk_callback(struct urb *urb); static void oti6858_write_bulk_callback(struct urb *urb); -static int oti6858_write(struct usb_serial_port *port, +static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int oti6858_write_room(struct usb_serial_port *port); -static void oti6858_break_ctl(struct usb_serial_port *port, int break_state); -static int oti6858_chars_in_buffer(struct usb_serial_port *port); -static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file); -static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, +static int oti6858_write_room(struct tty_struct *tty); +static int oti6858_chars_in_buffer(struct tty_struct *tty); +static int oti6858_tiocmget(struct tty_struct *tty, struct file *file); +static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int oti6858_startup(struct usb_serial *serial); static void oti6858_shutdown(struct usb_serial *serial); @@ -184,7 +185,6 @@ static struct usb_serial_driver oti6858_device = { .close = oti6858_close, .write = oti6858_write, .ioctl = oti6858_ioctl, - .break_ctl = oti6858_break_ctl, .set_termios = oti6858_set_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, @@ -395,7 +395,7 @@ static int oti6858_startup(struct usb_serial *serial) return -ENOMEM; } -static int oti6858_write(struct usb_serial_port *port, +static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct oti6858_private *priv = usb_get_serial_port_data(port); @@ -413,8 +413,9 @@ static int oti6858_write(struct usb_serial_port *port, return count; } -static int oti6858_write_room(struct usb_serial_port *port) +static int oti6858_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -428,8 +429,9 @@ static int oti6858_write_room(struct usb_serial_port *port) return room; } -static int oti6858_chars_in_buffer(struct usb_serial_port *port) +static int oti6858_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -443,8 +445,8 @@ static int oti6858_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void oti6858_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void oti6858_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -455,22 +457,22 @@ static void oti6858_set_termios(struct usb_serial_port *port, dbg("%s(port = %d)", __func__, port->number); - if (!port->tty || !port->tty->termios) { + if (!tty) { dbg("%s(): no tty structures", __func__); return; } spin_lock_irqsave(&priv->lock, flags); if (!priv->flags.termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 38400; + tty->termios->c_ospeed = 38400; priv->flags.termios_initialized = 1; - port->tty->termios->c_ispeed = 38400; - port->tty->termios->c_ospeed = 38400; } spin_unlock_irqrestore(&priv->lock, flags); - cflag = port->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; spin_lock_irqsave(&priv->lock, flags); divisor = priv->pending_setup.divisor; @@ -500,7 +502,7 @@ static void oti6858_set_termios(struct usb_serial_port *port, * guarantee that any other baud rate will work (especially * the higher ones) */ - br = tty_get_baud_rate(port->tty); + br = tty_get_baud_rate(tty); if (br == 0) { divisor = 0; } else { @@ -511,7 +513,7 @@ static void oti6858_set_termios(struct usb_serial_port *port, new_divisor = (96000000 + 8 * br) / (16 * br); real_br = 96000000 / (16 * new_divisor); divisor = cpu_to_le16(new_divisor); - tty_encode_baud_rate(port->tty, real_br, real_br); + tty_encode_baud_rate(tty, real_br, real_br); } frame_fmt &= ~FMT_STOP_BITS_MASK; @@ -564,7 +566,8 @@ static void oti6858_set_termios(struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); } -static int oti6858_open(struct usb_serial_port *port, struct file *filp) +static int oti6858_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct oti6858_private *priv = usb_get_serial_port_data(port); struct ktermios tmp_termios; @@ -578,7 +581,7 @@ static int oti6858_open(struct usb_serial_port *port, struct file *filp) usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); - if (port->open_count != 1) + if (port->port.count != 1) return 0; if ((buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { @@ -617,18 +620,19 @@ static int oti6858_open(struct usb_serial_port *port, struct file *filp) if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); - oti6858_close(port, NULL); + oti6858_close(tty, port, NULL); return -EPROTO; } /* setup termios */ - if (port->tty) - oti6858_set_termios(port, &tmp_termios); + if (tty) + oti6858_set_termios(tty, port, &tmp_termios); return 0; } -static void oti6858_close(struct usb_serial_port *port, struct file *filp) +static void oti6858_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -641,7 +645,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */ init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); dbg("%s(): entering wait loop", __func__); for (;;) { set_current_state(TASK_INTERRUPTIBLE); @@ -654,7 +658,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); dbg("%s(): after wait loop", __func__); /* clear out any remaining data in the buffer */ @@ -669,7 +673,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) /* data is in the buffer to compute a delay */ /* that is not unnecessarily long) */ /* FIXME - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560)/bps,HZ/10); else @@ -690,7 +694,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->interrupt_in_urb); /* - if (port->tty && (port->tty->termios->c_cflag) & HUPCL) { + if (tty && (tty->termios->c_cflag) & HUPCL) { // drop DTR and RTS spin_lock_irqsave(&priv->lock, flags); priv->pending_setup.control &= ~CONTROL_MASK; @@ -699,9 +703,10 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) */ } -static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, +static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -732,8 +737,9 @@ static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file) +static int oti6858_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned pin_state; @@ -802,26 +808,15 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, +static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - void __user *user_arg = (void __user *) arg; - unsigned int x; + struct usb_serial_port *port = tty->driver_data; dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)", __func__, port->number, cmd, arg); switch (cmd) { - case TIOCMBIS: - if (copy_from_user(&x, user_arg, sizeof(x))) - return -EFAULT; - return oti6858_tiocmset(port, NULL, x, 0); - - case TIOCMBIC: - if (copy_from_user(&x, user_arg, sizeof(x))) - return -EFAULT; - return oti6858_tiocmset(port, NULL, 0, x); - case TIOCMIWAIT: dbg("%s(): TIOCMIWAIT", __func__); return wait_modem_info(port, arg); @@ -834,24 +829,6 @@ static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static void oti6858_break_ctl(struct usb_serial_port *port, int break_state) -{ - int state; - - dbg("%s(port = %d)", __func__, port->number); - - state = (break_state == 0) ? 0 : 1; - dbg("%s(): turning break %s", __func__, state ? "on" : "off"); - - /* FIXME */ -/* - result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - BREAK_REQUEST, BREAK_REQUEST_TYPE, state, - 0, NULL, 0, 100); - if (result != 0) - dbg("%s(): error sending break", __func__); - */ -} static void oti6858_shutdown(struct usb_serial *serial) { @@ -1002,7 +979,7 @@ static void oti6858_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); if (status != 0) { - if (!port->open_count) { + if (!port->port.count) { dbg("%s(): port is closed, exiting", __func__); return; } @@ -1020,14 +997,14 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (tty != NULL && urb->actual_length > 0) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } // schedule the interrupt urb if we are still open */ - if (port->open_count != 0) { + if (port->port.count != 0) { port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 2a0dd1b50dc4..a0016725d314 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -458,8 +458,8 @@ static void pl2303_send(struct usb_serial_port *port) usb_serial_port_softint(port); } -static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -478,8 +478,9 @@ static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, return count; } -static int pl2303_write_room(struct usb_serial_port *port) +static int pl2303_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -494,8 +495,9 @@ static int pl2303_write_room(struct usb_serial_port *port) return room; } -static int pl2303_chars_in_buffer(struct usb_serial_port *port) +static int pl2303_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -510,8 +512,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void pl2303_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void pl2303_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -526,11 +528,10 @@ static void pl2303_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 | CREAD | - HUPCL | CLOCAL; - port->tty->termios->c_ispeed = 9600; - port->tty->termios->c_ospeed = 9600; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); @@ -539,16 +540,16 @@ static void pl2303_set_termios(struct usb_serial_port *port, serial settings even to the same values as before. Thus we actually need to filter in this specific case */ - if (!tty_termios_hw_change(port->tty->termios, old_termios)) + if (!tty_termios_hw_change(tty->termios, old_termios)) return; - cflag = port->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; buf = kzalloc(7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __func__); /* Report back no change occurred */ - *port->tty->termios = *old_termios; + *tty->termios = *old_termios; return; } @@ -569,7 +570,7 @@ static void pl2303_set_termios(struct usb_serial_port *port, dbg("%s - data bits = %d", __func__, buf[6]); } - baud = tty_get_baud_rate(port->tty);; + baud = tty_get_baud_rate(tty); dbg("%s - baud = %d", __func__, baud); if (baud) { buf[0] = baud & 0xff; @@ -646,12 +647,13 @@ static void pl2303_set_termios(struct usb_serial_port *port, /* FIXME: Need to read back resulting baud rate */ if (baud) - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); kfree(buf); } -static void pl2303_close(struct usb_serial_port *port, struct file *filp) +static void pl2303_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -666,7 +668,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = PL2303_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (pl2303_buf_data_avail(priv->buf) == 0 || @@ -678,7 +680,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ pl2303_buf_clear(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); @@ -690,7 +692,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) /* for lower rates we should really know how much */ /* data is in the buffer to compute a delay */ /* that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560)/bps,HZ/10); else @@ -703,8 +705,8 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ spin_lock_irqsave(&priv->lock, flags); @@ -715,7 +717,8 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) } } -static int pl2303_open(struct usb_serial_port *port, struct file *filp) +static int pl2303_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -734,9 +737,8 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) } /* Setup termios */ - if (port->tty) { - pl2303_set_termios(port, &tmp_termios); - } + if (tty) + pl2303_set_termios(tty, port, &tmp_termios); //FIXME: need to assert RTS and DTR if CRTSCTS off @@ -746,7 +748,7 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - pl2303_close(port, NULL); + pl2303_close(tty, port, NULL); return -EPROTO; } @@ -756,15 +758,16 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting interrupt urb," " error %d\n", __func__, result); - pl2303_close(port, NULL); + pl2303_close(tty, port, NULL); return -EPROTO; } return 0; } -static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, +static int pl2303_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -787,8 +790,9 @@ static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, return set_control_lines(port->serial->dev, control); } -static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file) +static int pl2303_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; @@ -853,9 +857,10 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, +static int pl2303_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); switch (cmd) { @@ -871,8 +876,9 @@ static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static void pl2303_break_ctl(struct usb_serial_port *port, int break_state) +static void pl2303_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; u16 state; int result; @@ -1001,7 +1007,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) if (status) { dbg("%s - urb status = %d", __func__, status); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port is closed, exiting.", __func__); return; } @@ -1044,7 +1050,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -1056,7 +1062,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 94bddf06ea4f..f823e4dcea1e 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -229,8 +229,8 @@ static void safe_read_bulk_callback (struct urb *urb) int actual_length = data[length - 2] >> 2; if (actual_length <= (length - 2)) { info ("%s - actual: %d", __func__, actual_length); - tty_insert_flip_string(port->tty, data, actual_length); - tty_flip_buffer_push (port->tty); + tty_insert_flip_string(port->port.tty, data, actual_length); + tty_flip_buffer_push (port->port.tty); } else { err ("%s - inconsistent lengths %d:%d", __func__, actual_length, length); @@ -239,8 +239,8 @@ static void safe_read_bulk_callback (struct urb *urb) err ("%s - bad CRC %x", __func__, fcs); } } else { - tty_insert_flip_string(port->tty, data, length); - tty_flip_buffer_push (port->tty); + tty_insert_flip_string(port->port.tty, data, length); + tty_flip_buffer_push (port->port.tty); } /* Continue trying to always read */ @@ -255,7 +255,8 @@ static void safe_read_bulk_callback (struct urb *urb) } } -static int safe_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int safe_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *data; int result; @@ -349,8 +350,9 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i return (count); } -static int safe_write_room (struct usb_serial_port *port) +static int safe_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int room = 0; /* Default: no room */ unsigned long flags; diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 29074c1ba22b..892020d48555 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -250,7 +250,8 @@ struct sierra_port_private { int ri_state; }; -static int sierra_send_setup(struct usb_serial_port *port) +static int sierra_send_setup(struct tty_struct *tty, + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; @@ -260,7 +261,7 @@ static int sierra_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (port->tty) { + if (tty) { int val = 0; if (portdata->dtr_state) val |= 0x01; @@ -284,32 +285,17 @@ static int sierra_send_setup(struct usb_serial_port *port) return 0; } -static void sierra_rx_throttle(struct usb_serial_port *port) +static void sierra_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); + tty_termios_copy_hw(tty->termios, old_termios); + sierra_send_setup(tty, port); } -static void sierra_rx_unthrottle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void sierra_break_ctl(struct usb_serial_port *port, int break_state) -{ - /* Unfortunately, I don't know how to send a break */ - dbg("%s", __func__); -} - -static void sierra_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) -{ - dbg("%s", __func__); - tty_termios_copy_hw(port->tty->termios, old_termios); - sierra_send_setup(port); -} - -static int sierra_tiocmget(struct usb_serial_port *port, struct file *file) +static int sierra_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int value; struct sierra_port_private *portdata; @@ -325,9 +311,10 @@ static int sierra_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int sierra_tiocmset(struct usb_serial_port *port, struct file *file, +static int sierra_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct sierra_port_private *portdata; portdata = usb_get_serial_port_data(port); @@ -341,13 +328,7 @@ static int sierra_tiocmset(struct usb_serial_port *port, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return sierra_send_setup(port); -} - -static int sierra_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; + return sierra_send_setup(tty, port); } static void sierra_outdat_callback(struct urb *urb) @@ -374,8 +355,8 @@ static void sierra_outdat_callback(struct urb *urb) } /* Write */ -static int sierra_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -463,7 +444,7 @@ static void sierra_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -473,7 +454,7 @@ static void sierra_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) dev_err(&port->dev, "resubmit read urb failed." @@ -517,9 +498,9 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) && + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -537,8 +518,9 @@ static void sierra_instat_callback(struct urb *urb) } } -static int sierra_write_room(struct usb_serial_port *port) +static int sierra_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct sierra_port_private *portdata = usb_get_serial_port_data(port); unsigned long flags; @@ -557,22 +539,8 @@ static int sierra_write_room(struct usb_serial_port *port) return 2048; } -static int sierra_chars_in_buffer(struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device as we can't see the backend here, so just - * tell the tty layer that everything is flushed. - * - * FIXME: should walk the outstanding urbs info - */ - return 0; -} - -static int sierra_open(struct usb_serial_port *port, struct file *filp) +static int sierra_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; @@ -612,9 +580,10 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) } } - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; - sierra_send_setup(port); + sierra_send_setup(tty, port); /* start up the interrupt endpoint if we have one */ if (port->interrupt_in_urb) { @@ -626,7 +595,8 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) return 0; } -static void sierra_close(struct usb_serial_port *port, struct file *filp) +static void sierra_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -641,7 +611,7 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - sierra_send_setup(port); + sierra_send_setup(tty, port); mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ @@ -651,7 +621,7 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->interrupt_in_urb); - port->tty = NULL; + port->port.tty = NULL; /* FIXME */ } static int sierra_startup(struct usb_serial *serial) @@ -754,12 +724,7 @@ static struct usb_serial_driver sierra_device = { .close = sierra_close, .write = sierra_write, .write_room = sierra_write_room, - .chars_in_buffer = sierra_chars_in_buffer, - .throttle = sierra_rx_throttle, - .unthrottle = sierra_rx_unthrottle, - .ioctl = sierra_ioctl, .set_termios = sierra_set_termios, - .break_ctl = sierra_break_ctl, .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 55b2570b8b8b..58495f5cca1f 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -448,7 +448,8 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, /* close the serial port. We should wait for data sending to device 1st and * then kill all urb. */ -static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) +static void spcp8x5_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -464,7 +465,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = SPCP8x5_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (ringbuf_avail_data(priv->buf) == 0 || @@ -475,7 +476,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ clear_ringbuf(priv->buf); @@ -486,7 +487,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) * flow control for data rates of 1200 bps or more, for lower rates we * should really know how much data is in the buffer to compute a delay * that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560) / bps, HZ/10); else @@ -495,8 +496,8 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) schedule_timeout(timeout); /* clear control lines */ - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; @@ -518,14 +519,14 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) } /* set the serial param for transfer. we should check if we really need to - * transfer. then if be set flow contorl we should do this too. */ -static void spcp8x5_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) + * transfer. if we set flow control we should do this too. */ +static void spcp8x5_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag = tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned short uartdata; unsigned char buf[2] = {0, 0}; @@ -533,21 +534,19 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, int i; u8 control; - if ((!port->tty) || (!port->tty->termios)) - return; - /* for the 1st time call this function */ spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B115200 | CS8 | CREAD | - HUPCL | CLOCAL; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 115200; + tty->termios->c_ospeed = 115200; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); /* check that they really want us to change something */ - if (!tty_termios_hw_change(port->tty->termios, old_termios)) + if (!tty_termios_hw_change(tty->termios, old_termios)) return; /* set DTR/RTS active */ @@ -567,7 +566,7 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, } /* Set Baud Rate */ - baud = tty_get_baud_rate(port->tty);; + baud = tty_get_baud_rate(tty);; switch (baud) { case 300: buf[0] = 0x00; break; case 600: buf[0] = 0x01; break; @@ -643,7 +642,8 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, /* open the serial port. do some usb system call. set termios and get the line * status of the device. then submit the read urb */ -static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) +static int spcp8x5_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -665,7 +665,7 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) return ret; spin_lock_irqsave(&priv->lock, flags); - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) priv->line_control = MCR_DTR | MCR_RTS; else priv->line_control = 0; @@ -674,8 +674,8 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type); /* Setup termios */ - if (port->tty) - spcp8x5_set_termios(port, &tmp_termios); + if (tty) + spcp8x5_set_termios(tty, port, &tmp_termios); spcp8x5_get_msr(serial->dev, &status, priv->type); @@ -690,7 +690,7 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) port->read_urb->dev = serial->dev; ret = usb_submit_urb(port->read_urb, GFP_KERNEL); if (ret) { - spcp8x5_close(port, NULL); + spcp8x5_close(tty, port, NULL); return -EPROTO; } return 0; @@ -717,7 +717,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) /* check the urb status */ if (urb->status) { - if (!port->open_count) + if (!port->port.count) return; if (urb->status == -EPROTO) { /* spcp8x5 mysteriously fails with -EPROTO */ @@ -755,7 +755,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -767,7 +767,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { urb->dev = port->serial->dev; result = usb_submit_urb(urb , GFP_ATOMIC); if (result) @@ -866,7 +866,7 @@ static void spcp8x5_write_bulk_callback(struct urb *urb) } /* write data to ring buffer. and then start the write transfer */ -static int spcp8x5_write(struct usb_serial_port *port, +static int spcp8x5_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); @@ -925,9 +925,10 @@ static int spcp8x5_wait_modem_info(struct usb_serial_port *port, return 0; } -static int spcp8x5_ioctl(struct usb_serial_port *port, struct file *file, +static int spcp8x5_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); switch (cmd) { @@ -943,9 +944,10 @@ static int spcp8x5_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static int spcp8x5_tiocmset(struct usb_serial_port *port, struct file *file, +static int spcp8x5_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -965,8 +967,9 @@ static int spcp8x5_tiocmset(struct usb_serial_port *port, struct file *file, return spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); } -static int spcp8x5_tiocmget(struct usb_serial_port *port, struct file *file) +static int spcp8x5_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; @@ -989,8 +992,9 @@ static int spcp8x5_tiocmget(struct usb_serial_port *port, struct file *file) } /* get the avail space room in ring buffer */ -static int spcp8x5_write_room(struct usb_serial_port *port) +static int spcp8x5_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -1003,8 +1007,9 @@ static int spcp8x5_write_room(struct usb_serial_port *port) } /* get the number of avail data in write ring buffer */ -static int spcp8x5_chars_in_buffer(struct usb_serial_port *port) +static int spcp8x5_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a26a629dfc4f..48831a755fc1 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -70,6 +70,7 @@ #include #include +#include #include #include #include @@ -149,21 +150,23 @@ struct ti_device { static int ti_startup(struct usb_serial *serial); static void ti_shutdown(struct usb_serial *serial); -static int ti_open(struct usb_serial_port *port, struct file *file); -static void ti_close(struct usb_serial_port *port, struct file *file); -static int ti_write(struct usb_serial_port *port, const unsigned char *data, - int count); -static int ti_write_room(struct usb_serial_port *port); -static int ti_chars_in_buffer(struct usb_serial_port *port); -static void ti_throttle(struct usb_serial_port *port); -static void ti_unthrottle(struct usb_serial_port *port); -static int ti_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); -static void ti_set_termios(struct usb_serial_port *port, +static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); +static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); +static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count); +static int ti_write_room(struct tty_struct *tty); +static int ti_chars_in_buffer(struct tty_struct *tty); +static void ti_throttle(struct tty_struct *tty); +static void ti_unthrottle(struct tty_struct *tty); +static int ti_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static void ti_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); -static int ti_tiocmget(struct usb_serial_port *port, struct file *file); -static int ti_tiocmset(struct usb_serial_port *port, struct file *file, +static int ti_tiocmget(struct tty_struct *tty, struct file *file); +static int ti_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void ti_break(struct usb_serial_port *port, int break_state); +static void ti_break(struct tty_struct *tty, int break_state); static void ti_interrupt_callback(struct urb *urb); static void ti_bulk_in_callback(struct urb *urb); static void ti_bulk_out_callback(struct urb *urb); @@ -192,8 +195,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command, static int ti_write_byte(struct ti_device *tdev, unsigned long addr, __u8 mask, __u8 byte); -static int ti_download_firmware(struct ti_device *tdev, char *fw_name); - +static int ti_download_firmware(struct ti_device *tdev, int type); /* circular buffer */ static struct circ_buf *ti_buf_alloc(void); @@ -430,11 +432,10 @@ static int ti_startup(struct usb_serial *serial) /* if we have only 1 configuration, download firmware */ if (dev->descriptor.bNumConfigurations == 1) { - if (tdev->td_is_3410) - status = ti_download_firmware(tdev, "ti_3410.fw"); + status = ti_download_firmware(tdev, 3410); else - status = ti_download_firmware(tdev, "ti_5052.fw"); + status = ti_download_firmware(tdev, 5052); if (status) goto free_tdev; @@ -519,7 +520,8 @@ static void ti_shutdown(struct usb_serial *serial) } -static int ti_open(struct usb_serial_port *port, struct file *file) +static int ti_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *file) { struct ti_port *tport = usb_get_serial_port_data(port); struct ti_device *tdev; @@ -543,9 +545,9 @@ static int ti_open(struct usb_serial_port *port, struct file *file) if (mutex_lock_interruptible(&tdev->td_open_close_lock)) return -ERESTARTSYS; - if (port->tty) - port->tty->low_latency = - (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (tty) + tty->low_latency = + (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; port_number = port->number - port->serial->minor; @@ -573,7 +575,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) } } - ti_set_termios(port, port->tty->termios); + if (tty) + ti_set_termios(tty, port, tty->termios); dbg("%s - sending TI_OPEN_PORT", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -610,7 +613,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) usb_clear_halt(dev, port->write_urb->pipe); usb_clear_halt(dev, port->read_urb->pipe); - ti_set_termios(port, port->tty->termios); + if (tty) + ti_set_termios(tty, port, tty->termios); dbg("%s - sending TI_OPEN_PORT (2)", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -661,7 +665,8 @@ release_lock: } -static void ti_close(struct usb_serial_port *port, struct file *file) +static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file) { struct ti_device *tdev; struct ti_port *tport; @@ -707,8 +712,8 @@ static void ti_close(struct usb_serial_port *port, struct file *file) } -static int ti_write(struct usb_serial_port *port, const unsigned char *data, - int count) +static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct ti_port *tport = usb_get_serial_port_data(port); unsigned long flags; @@ -733,8 +738,9 @@ static int ti_write(struct usb_serial_port *port, const unsigned char *data, } -static int ti_write_room(struct usb_serial_port *port) +static int ti_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -753,8 +759,9 @@ static int ti_write_room(struct usb_serial_port *port) } -static int ti_chars_in_buffer(struct usb_serial_port *port) +static int ti_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -773,32 +780,26 @@ static int ti_chars_in_buffer(struct usb_serial_port *port) } -static void ti_throttle(struct usb_serial_port *port) +static void ti_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); if (tport == NULL) return; - tty = port->tty; - if (!tty) { - dbg("%s - no tty", __func__); - return; - } - if (I_IXOFF(tty) || C_CRTSCTS(tty)) ti_stop_read(tport, tty); } -static void ti_unthrottle(struct usb_serial_port *port) +static void ti_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -806,12 +807,6 @@ static void ti_unthrottle(struct usb_serial_port *port) if (tport == NULL) return; - tty = port->tty; - if (!tty) { - dbg("%s - no tty", __func__); - return; - } - if (I_IXOFF(tty) || C_CRTSCTS(tty)) { status = ti_restart_read(tport, tty); if (status) @@ -820,9 +815,10 @@ static void ti_unthrottle(struct usb_serial_port *port) } -static int ti_ioctl(struct usb_serial_port *port, struct file *file, +static int ti_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -875,11 +871,10 @@ static int ti_ioctl(struct usb_serial_port *port, struct file *file, } -static void ti_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ti_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; struct ti_uart_config *config; tcflag_t cflag,iflag; int baud; @@ -1008,8 +1003,9 @@ static void ti_set_termios(struct usb_serial_port *port, } -static int ti_tiocmget(struct usb_serial_port *port, struct file *file) +static int ti_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); unsigned int result; unsigned int msr; @@ -1040,9 +1036,10 @@ static int ti_tiocmget(struct usb_serial_port *port, struct file *file) } -static int ti_tiocmset(struct usb_serial_port *port, struct file *file, +static int ti_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); unsigned int mcr; unsigned long flags; @@ -1074,8 +1071,9 @@ static int ti_tiocmset(struct usb_serial_port *port, struct file *file, } -static void ti_break(struct usb_serial_port *port, int break_state) +static void ti_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int status; @@ -1213,14 +1211,14 @@ static void ti_bulk_in_callback(struct urb *urb) return; } - if (port->tty && urb->actual_length) { + if (port->port.tty && urb->actual_length) { usb_serial_debug_data(debug, dev, __func__, urb->actual_length, urb->transfer_buffer); if (!tport->tp_is_open) dbg("%s - port closed, dropping data", __func__); else - ti_recv(&urb->dev->dev, port->tty, urb->transfer_buffer, + ti_recv(&urb->dev->dev, port->port.tty, urb->transfer_buffer, urb->actual_length); spin_lock(&tport->tp_lock); @@ -1302,7 +1300,7 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; /* FIXME */ unsigned long flags; @@ -1442,8 +1440,9 @@ static int ti_set_serial_info(struct ti_port *tport, return -EFAULT; tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - if (port->tty) - port->tty->low_latency = + /* FIXME */ + if (port->port.tty) + port->port.tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; tport->tp_closing_wait = new_serial.closing_wait; @@ -1477,7 +1476,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tport->tp_msr = msr & TI_MSR_MASK; /* handle CTS flow control */ - tty = tport->tp_port->tty; + tty = tport->tp_port->port.tty; if (tty && C_CRTSCTS(tty)) { if (msr & TI_MSR_CTS) { tty->hw_stopped = 0; @@ -1655,65 +1654,65 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr, return status; } - -static int ti_download_firmware(struct ti_device *tdev, - char *fw_name) +static int ti_do_download(struct usb_device *dev, int pipe, + u8 *buffer, int size) { - const struct firmware *fw; - int status = 0; - int buffer_size; int pos; - int len; + u8 cs = 0; int done; - __u8 cs = 0; - __u8 *buffer; - struct usb_device *dev = tdev->td_serial->dev; struct ti_firmware_header *header; - unsigned int pipe = usb_sndbulkpipe(dev, - tdev->td_serial->port[0]->bulk_out_endpointAddress); - - buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); - - if (request_firmware(&fw, fw_name, &dev->dev)) { - dev_err(&dev->dev, "%s - failed to load firmware \"%s\"\n", - __func__, fw_name); - return -ENOENT; - } - if (fw->size > buffer_size) { - dev_err(&dev->dev, "%s - firmware \"%s\" is too large\n", - __func__, fw_name); - release_firmware(fw); - return -EINVAL; - } - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); - release_firmware(fw); - return -ENOMEM; - } - - memcpy(buffer, fw->data, fw->size); - memset(buffer+fw->size, 0xff, buffer_size-fw->size); - - for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++) + int status; + int len; + + for(pos = sizeof(struct ti_firmware_header); pos < size; pos++) cs = (__u8)(cs + buffer[pos]); header = (struct ti_firmware_header *)buffer; - header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header))); + header->wLength = cpu_to_le16((__u16)(size + - sizeof(struct ti_firmware_header))); header->bCheckSum = cs; dbg("%s - downloading firmware", __func__); - for (pos = 0; pos < buffer_size; pos += done) { - len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); - status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, 1000); + for (pos = 0; pos < size; pos += done) { + len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); + status = usb_bulk_msg(dev, pipe, buffer + pos, len, + &done, 1000); if (status) break; } + return status; +} - kfree(buffer); - release_firmware(fw); +static int ti_download_firmware(struct ti_device *tdev, int type) +{ + int status = -ENOMEM; + int buffer_size; + __u8 *buffer; + struct usb_device *dev = tdev->td_serial->dev; + unsigned int pipe = usb_sndbulkpipe(dev, + tdev->td_serial->port[0]->bulk_out_endpointAddress); + const struct firmware *fw_p; + char buf[32]; + sprintf(buf, "ti_usb-%d.bin", type); + if (request_firmware(&fw_p, buf, &dev->dev)) { + dev_err(&dev->dev, "%s - firmware not found\n", __func__); + return -ENOENT; + } + if (fw_p->size > TI_FIRMWARE_BUF_SIZE) { + dev_err(&dev->dev, "%s - firmware too large\n", __func__); + return -ENOENT; + } + + buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (buffer) { + memcpy(buffer, fw_p->data, fw_p->size); + memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size); + ti_do_download(dev, pipe, buffer, fw_p->size); + kfree(buffer); + } + release_firmware(fw_p); if (status) { dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __func__, status); return status; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 353798631903..ffaed8ace066 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -143,7 +143,7 @@ static void destroy_serial(struct kref *kref) return_serial(serial); for (i = 0; i < serial->num_ports; ++i) - serial->port[i]->open_count = 0; + serial->port[i]->port.count = 0; /* the ports are cleaned up and released in port_release() */ for (i = 0; i < serial->num_ports; ++i) @@ -208,14 +208,14 @@ static int serial_open (struct tty_struct *tty, struct file * filp) goto bailout_kref_put; } - ++port->open_count; + ++port->port.count; /* set up our port structure making the tty driver * remember our port object, and us it */ tty->driver_data = port; - port->tty = tty; + port->port.tty = tty; - if (port->open_count == 1) { + if (port->port.count == 1) { /* lock this module before we call it * this may fail, which means we must bail out, @@ -230,7 +230,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) goto bailout_module_put; /* only call the device specific open if this * is the first time the port is opened */ - retval = serial->type->open(port, filp); + retval = serial->type->open(tty, port, filp); if (retval) goto bailout_interface_put; } @@ -243,9 +243,9 @@ bailout_interface_put: bailout_module_put: module_put(serial->type->driver.owner); bailout_mutex_unlock: - port->open_count = 0; + port->port.count = 0; tty->driver_data = NULL; - port->tty = NULL; + port->port.tty = NULL; mutex_unlock(&port->mutex); bailout_kref_put: usb_serial_put(serial); @@ -263,26 +263,26 @@ static void serial_close(struct tty_struct *tty, struct file * filp) mutex_lock(&port->mutex); - if (port->open_count == 0) { + if (port->port.count == 0) { mutex_unlock(&port->mutex); return; } - --port->open_count; - if (port->open_count == 0) + --port->port.count; + if (port->port.count == 0) /* only call the device specific close if this * port is being closed by the last owner */ - port->serial->type->close(port, filp); + port->serial->type->close(tty, port, filp); - if (port->open_count == (port->console? 1 : 0)) { - if (port->tty) { - if (port->tty->driver_data) - port->tty->driver_data = NULL; - port->tty = NULL; + if (port->port.count == (port->console? 1 : 0)) { + if (port->port.tty) { + if (port->port.tty->driver_data) + port->port.tty->driver_data = NULL; + port->port.tty = NULL; } } - if (port->open_count == 0) { + if (port->port.count == 0) { mutex_lock(&port->serial->disc_mutex); if (!port->serial->disconnected) usb_autopm_put_interface(port->serial->interface); @@ -304,12 +304,12 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - /* open_count is managed under the mutex lock for the tty so cannot + /* count is managed under the mutex lock for the tty so cannot drop to zero until after the last close completes */ - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - retval = port->serial->type->write(port, buf, count); + retval = port->serial->type->write(tty, port, buf, count); exit: return retval; @@ -319,9 +319,9 @@ static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - return port->serial->type->write_room(port); + return port->serial->type->write_room(tty); } static int serial_chars_in_buffer (struct tty_struct *tty) @@ -329,9 +329,9 @@ static int serial_chars_in_buffer (struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; dbg("%s = port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - return port->serial->type->chars_in_buffer(port); + return port->serial->type->chars_in_buffer(tty); } static void serial_throttle (struct tty_struct * tty) @@ -339,10 +339,10 @@ static void serial_throttle (struct tty_struct * tty) struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) - port->serial->type->throttle(port); + port->serial->type->throttle(tty); } static void serial_unthrottle (struct tty_struct * tty) @@ -350,10 +350,10 @@ static void serial_unthrottle (struct tty_struct * tty) struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) - port->serial->type->unthrottle(port); + port->serial->type->unthrottle(tty); } static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -363,12 +363,12 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->ioctl) { lock_kernel(); - retval = port->serial->type->ioctl(port, file, cmd, arg); + retval = port->serial->type->ioctl(tty, file, cmd, arg); unlock_kernel(); } else @@ -381,10 +381,10 @@ static void serial_set_termios (struct tty_struct *tty, struct ktermios * old) struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->set_termios) - port->serial->type->set_termios(port, old); + port->serial->type->set_termios(tty, port, old); else tty_termios_copy_hw(tty->termios, old); } @@ -395,11 +395,11 @@ static void serial_break (struct tty_struct *tty, int break_state) dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->break_ctl) { lock_kernel(); - port->serial->type->break_ctl(port, break_state); + port->serial->type->break_ctl(tty, break_state); unlock_kernel(); } } @@ -457,9 +457,9 @@ static int serial_tiocmget (struct tty_struct *tty, struct file *file) dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); if (port->serial->type->tiocmget) - return port->serial->type->tiocmget(port, file); + return port->serial->type->tiocmget(tty, file); return -EINVAL; } @@ -470,9 +470,9 @@ static int serial_tiocmset (struct tty_struct *tty, struct file *file, dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); if (port->serial->type->tiocmset) - return port->serial->type->tiocmset(port, file, set, clear); + return port->serial->type->tiocmset(tty, file, set, clear); return -EINVAL; } @@ -497,7 +497,7 @@ static void usb_serial_port_work(struct work_struct *work) if (!port) return; - tty = port->tty; + tty = port->port.tty; if (!tty) return; @@ -1010,8 +1010,8 @@ void usb_serial_disconnect(struct usb_interface *interface) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { - if (port->tty) - tty_hangup(port->tty); + if (port->port.tty) + tty_hangup(port->port.tty); kill_traffic(port); } } diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 9ca4d4db1ddd..fc5d9952b03b 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -31,10 +31,11 @@ static struct usb_driver debug_driver = { .no_dynamic_id = 1, }; -int usb_debug_open(struct usb_serial_port *port, struct file *filp) +int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; - return usb_serial_generic_open(port, filp); + return usb_serial_generic_open(tty, port, filp); } static struct usb_serial_driver debug_device = { diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 5fc20122145f..373a3c7ea77b 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -35,17 +35,15 @@ #define DRIVER_DESC "USB HandSpring Visor / Palm OS driver" /* function prototypes for a handspring visor */ -static int visor_open (struct usb_serial_port *port, struct file *filp); -static void visor_close (struct usb_serial_port *port, struct file *filp); -static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int visor_write_room (struct usb_serial_port *port); -static int visor_chars_in_buffer (struct usb_serial_port *port); -static void visor_throttle (struct usb_serial_port *port); -static void visor_unthrottle (struct usb_serial_port *port); +static int visor_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void visor_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int visor_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int visor_write_room (struct tty_struct *tty); +static void visor_throttle (struct tty_struct *tty); +static void visor_unthrottle (struct tty_struct *tty); static int visor_probe (struct usb_serial *serial, const struct usb_device_id *id); static int visor_calc_num_ports(struct usb_serial *serial); static void visor_shutdown (struct usb_serial *serial); -static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static void visor_write_bulk_callback (struct urb *urb); static void visor_read_bulk_callback (struct urb *urb); static void visor_read_int_callback (struct urb *urb); @@ -198,10 +196,8 @@ static struct usb_serial_driver handspring_device = { .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, .shutdown = visor_shutdown, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, @@ -225,10 +221,8 @@ static struct usb_serial_driver clie_5_device = { .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, .shutdown = visor_shutdown, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, @@ -249,10 +243,8 @@ static struct usb_serial_driver clie_3_5_device = { .throttle = visor_throttle, .unthrottle = visor_unthrottle, .attach = clie_3_5_startup, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, }; @@ -274,7 +266,7 @@ static int stats; /****************************************************************************** * Handspring Visor specific driver functions ******************************************************************************/ -static int visor_open (struct usb_serial_port *port, struct file *filp) +static int visor_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct visor_private *priv = usb_get_serial_port_data(port); @@ -300,8 +292,8 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) * through, otherwise it is scheduled, and with high data rates (like * with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* Start reading from the device */ usb_fill_bulk_urb (port->read_urb, serial->dev, @@ -329,7 +321,8 @@ exit: } -static void visor_close (struct usb_serial_port *port, struct file * filp) +static void visor_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; @@ -361,7 +354,8 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) } -static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct visor_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -435,8 +429,9 @@ error_no_buffer: } -static int visor_write_room (struct usb_serial_port *port) +static int visor_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -460,22 +455,6 @@ static int visor_write_room (struct usb_serial_port *port) } -static int visor_chars_in_buffer (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device, so just tell the tty layer that everything - * is flushed. - * - * FIXME: Should walk outstanding_urbs - */ - return 0; -} - - static void visor_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -520,7 +499,7 @@ static void visor_read_bulk_callback (struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { available_room = tty_buffer_request_room(tty, urb->actual_length); if (available_room) { @@ -591,8 +570,9 @@ exit: __func__, result); } -static void visor_throttle (struct usb_serial_port *port) +static void visor_throttle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -603,8 +583,9 @@ static void visor_throttle (struct usb_serial_port *port) } -static void visor_unthrottle (struct usb_serial_port *port) +static void visor_unthrottle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; int result; @@ -922,13 +903,6 @@ static void visor_shutdown (struct usb_serial *serial) } } -static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - - return -ENOIOCTLCMD; -} - static int __init visor_init (void) { int i, retval; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 665aa77a917b..b07d6a5cac31 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -142,18 +142,18 @@ static int whiteheat_firmware_attach (struct usb_serial *serial); /* function prototypes for the Connect Tech WhiteHEAT serial converter */ static int whiteheat_attach (struct usb_serial *serial); static void whiteheat_shutdown (struct usb_serial *serial); -static int whiteheat_open (struct usb_serial_port *port, struct file *filp); -static void whiteheat_close (struct usb_serial_port *port, struct file *filp); -static int whiteheat_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int whiteheat_write_room (struct usb_serial_port *port); -static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file); -static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static void whiteheat_break_ctl (struct usb_serial_port *port, int break_state); -static int whiteheat_chars_in_buffer (struct usb_serial_port *port); -static void whiteheat_throttle (struct usb_serial_port *port); -static void whiteheat_unthrottle (struct usb_serial_port *port); +static int whiteheat_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void whiteheat_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int whiteheat_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int whiteheat_write_room (struct tty_struct *tty); +static int whiteheat_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void whiteheat_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static int whiteheat_tiocmget (struct tty_struct *tty, struct file *file); +static int whiteheat_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +static void whiteheat_break_ctl (struct tty_struct *tty, int break_state); +static int whiteheat_chars_in_buffer (struct tty_struct *tty); +static void whiteheat_throttle (struct tty_struct *tty); +static void whiteheat_unthrottle (struct tty_struct *tty); static void whiteheat_read_callback (struct urb *urb); static void whiteheat_write_callback (struct urb *urb); @@ -246,7 +246,7 @@ static void rx_data_softint(struct work_struct *work); static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize); static int firm_open(struct usb_serial_port *port); static int firm_close(struct usb_serial_port *port); -static int firm_setup_port(struct usb_serial_port *port); +static int firm_setup_port(struct tty_struct *tty); static int firm_set_rts(struct usb_serial_port *port, __u8 onoff); static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); static int firm_set_break(struct usb_serial_port *port, __u8 onoff); @@ -613,7 +613,8 @@ static void whiteheat_shutdown (struct usb_serial *serial) } -static int whiteheat_open (struct usb_serial_port *port, struct file *filp) +static int whiteheat_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int retval = 0; struct ktermios old_term; @@ -624,7 +625,8 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) if (retval) goto exit; - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* send an open port command */ retval = firm_open(port); @@ -640,9 +642,11 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) goto exit; } - old_term.c_cflag = ~port->tty->termios->c_cflag; - old_term.c_iflag = ~port->tty->termios->c_iflag; - whiteheat_set_termios(port, &old_term); + if (tty) { + old_term.c_cflag = ~tty->termios->c_cflag; + old_term.c_iflag = ~tty->termios->c_iflag; + whiteheat_set_termios(tty, port, &old_term); + } /* Work around HCD bugs */ usb_clear_halt(port->serial->dev, port->read_urb->pipe); @@ -663,7 +667,8 @@ exit: } -static void whiteheat_close(struct usb_serial_port *port, struct file * filp) +static void whiteheat_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct whiteheat_private *info = usb_get_serial_port_data(port); struct whiteheat_urb_wrap *wrap; @@ -681,7 +686,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) } mutex_unlock(&port->serial->disc_mutex); - port->tty->closing = 1; + tty->closing = 1; /* * Not currently in use; tty_wait_until_sent() calls @@ -689,12 +694,12 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) * acquisition. This should be fixed at some point. Greg's been * notified. if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) { - tty_wait_until_sent(port->tty, CLOSING_DELAY); + tty_wait_until_sent(tty, CLOSING_DELAY); } */ - tty_driver_flush_buffer(port->tty); - tty_ldisc_flush(port->tty); + tty_driver_flush_buffer(tty); + tty_ldisc_flush(tty); firm_report_tx_done(port); @@ -728,11 +733,12 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) stop_command_port(port->serial); - port->tty->closing = 0; + tty->closing = 0; } -static int whiteheat_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int whiteheat_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct whiteheat_private *info = usb_get_serial_port_data(port); @@ -791,8 +797,9 @@ static int whiteheat_write(struct usb_serial_port *port, const unsigned char *bu } -static int whiteheat_write_room(struct usb_serial_port *port) +static int whiteheat_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); struct list_head *tmp; int room = 0; @@ -811,8 +818,9 @@ static int whiteheat_write_room(struct usb_serial_port *port) } -static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) +static int whiteheat_tiocmget (struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); unsigned int modem_signals = 0; @@ -828,9 +836,10 @@ static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) } -static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, +static int whiteheat_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); @@ -851,8 +860,9 @@ static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, } -static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int whiteheat_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -896,20 +906,21 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un } -static void whiteheat_set_termios(struct usb_serial_port *port, struct ktermios *old_termios) +static void whiteheat_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - dbg("%s -port %d", __func__, port->number); - firm_setup_port(port); + firm_setup_port(tty); } -static void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) { +static void whiteheat_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; firm_set_break(port, break_state); } -static int whiteheat_chars_in_buffer(struct usb_serial_port *port) +static int whiteheat_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); struct list_head *tmp; struct whiteheat_urb_wrap *wrap; @@ -930,8 +941,9 @@ static int whiteheat_chars_in_buffer(struct usb_serial_port *port) } -static void whiteheat_throttle (struct usb_serial_port *port) +static void whiteheat_throttle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); unsigned long flags; @@ -945,8 +957,9 @@ static void whiteheat_throttle (struct usb_serial_port *port) } -static void whiteheat_unthrottle (struct usb_serial_port *port) +static void whiteheat_unthrottle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; @@ -1184,9 +1197,10 @@ static int firm_close(struct usb_serial_port *port) { } -static int firm_setup_port(struct usb_serial_port *port) { +static int firm_setup_port(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_port_settings port_settings; - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag = tty->termios->c_cflag; port_settings.port = port->number + 1; @@ -1235,22 +1249,22 @@ static int firm_setup_port(struct usb_serial_port *port) { (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : ""); /* determine software flow control */ - if (I_IXOFF(port->tty)) + if (I_IXOFF(tty)) port_settings.sflow = WHITEHEAT_SFLOW_RXTX; else port_settings.sflow = WHITEHEAT_SFLOW_NONE; dbg("%s - software flow control = %c", __func__, port_settings.sflow); - port_settings.xon = START_CHAR(port->tty); - port_settings.xoff = STOP_CHAR(port->tty); + port_settings.xon = START_CHAR(tty); + port_settings.xoff = STOP_CHAR(tty); dbg("%s - XON = %2x, XOFF = %2x", __func__, port_settings.xon, port_settings.xoff); /* get the baud rate wanted */ - port_settings.baud = tty_get_baud_rate(port->tty); + port_settings.baud = tty_get_baud_rate(tty); dbg("%s - baud rate = %d", __func__, port_settings.baud); /* fixme: should set validated settings */ - tty_encode_baud_rate(port->tty, port_settings.baud, port_settings.baud); + tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud); /* handle any settings that aren't specified in the tty structure */ port_settings.lloop = 0; @@ -1426,7 +1440,7 @@ static void rx_data_softint(struct work_struct *work) struct whiteheat_private *info = container_of(work, struct whiteheat_private, rx_work); struct usb_serial_port *port = info->port; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; struct whiteheat_urb_wrap *wrap; struct urb *urb; unsigned long flags; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8f891cbaf9ab..09a3e6a7518f 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -62,7 +62,7 @@ */ struct usb_serial_port { struct usb_serial *serial; - struct tty_struct *tty; + struct tty_port port; spinlock_t lock; struct mutex mutex; unsigned char number; @@ -89,7 +89,6 @@ struct usb_serial_port { wait_queue_head_t write_wait; struct work_struct work; - int open_count; char throttled; char throttle_req; char console; @@ -217,22 +216,27 @@ struct usb_serial_driver { int (*resume)(struct usb_serial *serial); /* serial function calls */ - int (*open)(struct usb_serial_port *port, struct file *filp); - void (*close)(struct usb_serial_port *port, struct file *filp); - int (*write)(struct usb_serial_port *port, const unsigned char *buf, - int count); - int (*write_room)(struct usb_serial_port *port); - int (*ioctl)(struct usb_serial_port *port, struct file *file, + /* Called by console with tty = NULL and by tty */ + int (*open)(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + void (*close)(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + int (*write)(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); + /* Called only by the tty layer */ + int (*write_room)(struct tty_struct *tty); + int (*ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - void (*set_termios)(struct usb_serial_port *port, struct ktermios *old); - void (*break_ctl)(struct usb_serial_port *port, int break_state); - int (*chars_in_buffer)(struct usb_serial_port *port); - void (*throttle)(struct usb_serial_port *port); - void (*unthrottle)(struct usb_serial_port *port); - int (*tiocmget)(struct usb_serial_port *port, struct file *file); - int (*tiocmset)(struct usb_serial_port *port, struct file *file, + void (*set_termios)(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); + void (*break_ctl)(struct tty_struct *tty, int break_state); + int (*chars_in_buffer)(struct tty_struct *tty); + void (*throttle)(struct tty_struct *tty); + void (*unthrottle)(struct tty_struct *tty); + int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); - + /* USB events */ void (*read_int_callback)(struct urb *urb); void (*write_int_callback)(struct urb *urb); void (*read_bulk_callback)(struct urb *urb); @@ -270,19 +274,19 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} /* Functions needed by other parts of the usbserial core */ extern struct usb_serial *usb_serial_get_by_index(unsigned int minor); extern void usb_serial_put(struct usb_serial *serial); -extern int usb_serial_generic_open(struct usb_serial_port *port, - struct file *filp); -extern int usb_serial_generic_write(struct usb_serial_port *port, - const unsigned char *buf, int count); -extern void usb_serial_generic_close(struct usb_serial_port *port, - struct file *filp); +extern int usb_serial_generic_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +extern int usb_serial_generic_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +extern void usb_serial_generic_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); extern int usb_serial_generic_resume(struct usb_serial *serial); -extern int usb_serial_generic_write_room(struct usb_serial_port *port); -extern int usb_serial_generic_chars_in_buffer(struct usb_serial_port *port); +extern int usb_serial_generic_write_room(struct tty_struct *tty); +extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty); extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); -extern void usb_serial_generic_throttle(struct usb_serial_port *port); -extern void usb_serial_generic_unthrottle(struct usb_serial_port *port); +extern void usb_serial_generic_throttle(struct tty_struct *tty); +extern void usb_serial_generic_unthrottle(struct tty_struct *tty); extern void usb_serial_generic_shutdown(struct usb_serial *serial); extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void); -- cgit v1.2.3 From 01e1abb2c27e43339b8829a2e3b1c6f53806b77a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 22 Jul 2008 11:16:55 +0100 Subject: tty: Split ldisc code into its own file Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/char/Makefile | 2 +- drivers/char/tty_io.c | 636 +---------------------------------------- drivers/char/tty_ldisc.c | 714 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/tty.h | 11 +- 4 files changed, 733 insertions(+), 630 deletions(-) create mode 100644 drivers/char/tty_ldisc.c (limited to 'include/linux') diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 0e0d12a06462..dc5a327d72d5 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 82f6a8c86332..d27a08b374d0 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -655,558 +655,6 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); -/** - * tty_set_termios_ldisc - set ldisc field - * @tty: tty structure - * @num: line discipline number - * - * This is probably overkill for real world processors but - * they are not on hot paths so a little discipline won't do - * any harm. - * - * Locking: takes termios_mutex - */ - -static void tty_set_termios_ldisc(struct tty_struct *tty, int num) -{ - mutex_lock(&tty->termios_mutex); - tty->termios->c_line = num; - mutex_unlock(&tty->termios_mutex); -} - -/* - * This guards the refcounted line discipline lists. The lock - * must be taken with irqs off because there are hangup path - * callers who will do ldisc lookups and cannot sleep. - */ - -static DEFINE_SPINLOCK(tty_ldisc_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); -/* Line disc dispatch table */ -static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; - -/** - * tty_register_ldisc - install a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Installs a new line discipline into the kernel. The discipline - * is set up as unreferenced and then made available to the kernel - * from this point onwards. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - tty_ldiscs[disc] = new_ldisc; - new_ldisc->num = disc; - new_ldisc->refcount = 0; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_register_ldisc); - -/** - * tty_unregister_ldisc - unload a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Remove a line discipline from the kernel providing it is not - * currently in use. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_unregister_ldisc(int disc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty_ldiscs[disc]->refcount) - ret = -EBUSY; - else - tty_ldiscs[disc] = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_unregister_ldisc); - - -/** - * tty_ldisc_try_get - try and reference an ldisc - * @disc: ldisc number - * @ld: tty ldisc structure to complete - * - * Attempt to open and lock a line discipline into place. Return - * the line discipline refcounted and assigned in ld. On an error - * report the error code back - */ - -static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) -{ - unsigned long flags; - struct tty_ldisc_ops *ldops; - int err = -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld->ops = NULL; - ldops = tty_ldiscs[disc]; - /* Check the entry is defined */ - if (ldops) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ldops->owner)) - err = -EAGAIN; - else { - /* lock it */ - ldops->refcount++; - ld->ops = ldops; - err = 0; - } - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return err; -} - -/** - * tty_ldisc_get - take a reference to an ldisc - * @disc: ldisc number - * @ld: tty line discipline structure to use - * - * Takes a reference to a line discipline. Deals with refcounts and - * module locking counts. Returns NULL if the discipline is not available. - * Returns a pointer to the discipline and bumps the ref count if it is - * available - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static int tty_ldisc_get(int disc, struct tty_ldisc *ld) -{ - int err; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - err = tty_ldisc_try_get(disc, ld); - if (err == -EAGAIN) { - request_module("tty-ldisc-%d", disc); - err = tty_ldisc_try_get(disc, ld); - } - return err; -} - -/** - * tty_ldisc_put - drop ldisc reference - * @disc: ldisc number - * - * Drop a reference to a line discipline. Manage refcounts and - * module usage counts - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static void tty_ldisc_put(struct tty_ldisc_ops *ld) -{ - unsigned long flags; - int disc = ld->num; - - BUG_ON(disc < N_TTY || disc >= NR_LDISCS); - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = tty_ldiscs[disc]; - BUG_ON(ld->refcount == 0); - ld->refcount--; - module_put(ld->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) -{ - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) -{ -} - -static int tty_ldiscs_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct tty_ldisc ld; - - if (tty_ldisc_get(i, &ld) < 0) - return 0; - seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); - tty_ldisc_put(ld.ops); - return 0; -} - -static const struct seq_operations tty_ldiscs_seq_ops = { - .start = tty_ldiscs_seq_start, - .next = tty_ldiscs_seq_next, - .stop = tty_ldiscs_seq_stop, - .show = tty_ldiscs_seq_show, -}; - -static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &tty_ldiscs_seq_ops); -} - -const struct file_operations tty_ldiscs_proc_fops = { - .owner = THIS_MODULE, - .open = proc_tty_ldiscs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -/** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains/ - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - ld->refcount = 0; - tty->ldisc = *ld; -} - -/** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldisc_lock - */ - -static int tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - int ret = 0; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = &tty->ldisc; - if (test_bit(TTY_LDISC, &tty->flags)) { - ld->refcount++; - ret = 1; - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ret; -} - -/** - * tty_ldisc_ref_wait - wait for the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * wait patiently until it changes. - * - * Note: Must not be called from an IRQ/timer context. The caller - * must also be careful not to hold other locks that will deadlock - * against a discipline change, such as an existing ldisc reference - * (which we check for) - * - * Locking: call functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) -{ - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); - if (tty->ldisc.refcount == 0) - printk(KERN_ERR "tty_ldisc_ref_wait\n"); - return &tty->ldisc; -} - -EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); - -/** - * tty_ldisc_ref - get the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) -{ - if (tty_ldisc_try(tty)) - return &tty->ldisc; - return NULL; -} - -EXPORT_SYMBOL_GPL(tty_ldisc_ref); - -/** - * tty_ldisc_deref - free a tty ldisc reference - * @ld: reference to free up - * - * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May - * be called in IRQ context. - * - * Locking: takes tty_ldisc_lock - */ - -void tty_ldisc_deref(struct tty_ldisc *ld) -{ - unsigned long flags; - - BUG_ON(ld == NULL); - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (ld->refcount == 0) - printk(KERN_ERR "tty_ldisc_deref: no references.\n"); - else - ld->refcount--; - if (ld->refcount == 0) - wake_up(&tty_ldisc_wait); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -EXPORT_SYMBOL_GPL(tty_ldisc_deref); - -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. - * - * Note: nobody should set this bit except via this function. Clearing - * directly is allowed. - */ - -static void tty_ldisc_enable(struct tty_struct *tty) -{ - set_bit(TTY_LDISC, &tty->flags); - wake_up(&tty_ldisc_wait); -} - -/** - * tty_ldisc_restore - helper for tty ldisc change - * @tty: tty to recover - * @old: previous ldisc - * - * Restore the previous line discipline or N_TTY when a line discipline - * change fails due to an open error - */ - -static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) -{ - char buf[64]; - struct tty_ldisc new_ldisc; - - /* There is an outstanding reference here so this is safe */ - tty_ldisc_get(old->ops->num, old); - tty_ldisc_assign(tty, old); - tty_set_termios_ldisc(tty, old->ops->num); - if (old->ops->open && (old->ops->open(tty) < 0)) { - tty_ldisc_put(old->ops); - /* This driver is always present */ - if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) - panic("n_tty: get"); - tty_ldisc_assign(tty, &new_ldisc); - tty_set_termios_ldisc(tty, N_TTY); - if (new_ldisc.ops->open) { - int r = new_ldisc.ops->open(tty); - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty, buf), r); - } - } -} - -/** - * tty_set_ldisc - set line discipline - * @tty: the terminal to set - * @ldisc: the line discipline - * - * Set the discipline of a tty line. Must be called from a process - * context. - * - * Locking: takes tty_ldisc_lock. - * called functions take termios_mutex - */ - -static int tty_set_ldisc(struct tty_struct *tty, int ldisc) -{ - int retval; - struct tty_ldisc o_ldisc, new_ldisc; - int work; - unsigned long flags; - struct tty_struct *o_tty; - -restart: - /* This is a bit ugly for now but means we can break the 'ldisc - is part of the tty struct' assumption later */ - retval = tty_ldisc_get(ldisc, &new_ldisc); - if (retval) - return retval; - - /* - * Problem: What do we do if this blocks ? - */ - - tty_wait_until_sent(tty, 0); - - if (tty->ldisc.ops->num == ldisc) { - tty_ldisc_put(new_ldisc.ops); - return 0; - } - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - - o_ldisc = tty->ldisc; - o_tty = tty->link; - - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - */ - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { - if (tty->ldisc.refcount) { - /* Free the new ldisc we grabbed. Must drop the lock - first. */ - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(o_ldisc.ops); - /* - * There are several reasons we may be busy, including - * random momentary I/O traffic. We must therefore - * retry. We could distinguish between blocking ops - * and retries if we made tty_ldisc_wait() smarter. - * That is up for discussion. - */ - if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) - return -ERESTARTSYS; - goto restart; - } - if (o_tty && o_tty->ldisc.refcount) { - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(o_tty->ldisc.ops); - if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) - return -ERESTARTSYS; - goto restart; - } - } - /* - * If the TTY_LDISC bit is set, then we are racing against - * another ldisc change - */ - if (!test_bit(TTY_LDISC, &tty->flags)) { - struct tty_ldisc *ld; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(new_ldisc.ops); - ld = tty_ldisc_ref_wait(tty); - tty_ldisc_deref(ld); - goto restart; - } - - clear_bit(TTY_LDISC, &tty->flags); - if (o_tty) - clear_bit(TTY_LDISC, &o_tty->flags); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - /* - * From this point on we know nobody has an ldisc - * usage reference, nor can they obtain one until - * we say so later on. - */ - - work = cancel_delayed_work(&tty->buf.work); - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate - * MUST NOT hold locks here. - */ - flush_scheduled_work(); - /* Shutdown the current discipline. */ - if (o_ldisc.ops->close) - (o_ldisc.ops->close)(tty); - - /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, &new_ldisc); - tty_set_termios_ldisc(tty, ldisc); - if (new_ldisc.ops->open) - retval = (new_ldisc.ops->open)(tty); - if (retval < 0) { - tty_ldisc_put(new_ldisc.ops); - tty_ldisc_restore(tty, &o_ldisc); - } - /* At this point we hold a reference to the new ldisc and a - a reference to the old ldisc. If we ended up flipping back - to the existing ldisc we have two references to it */ - - if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) - tty->ops->set_ldisc(tty); - - tty_ldisc_put(o_ldisc.ops); - - /* - * Allow ldisc referencing to occur as soon as the driver - * ldisc callback completes. - */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); - - /* Restart it in case no characters kick it off. Safe if - already running */ - if (work) - schedule_delayed_work(&tty->buf.work, 1); - return retval; -} - /** * get_tty_driver - find device of a tty * @dev_t: device identifier @@ -2193,7 +1641,6 @@ static int init_dev(struct tty_driver *driver, int idx, struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval = 0; - struct tty_ldisc *ld; /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { @@ -2342,25 +1789,12 @@ static int init_dev(struct tty_driver *driver, int idx, * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ - - ld = &tty->ldisc; - if (ld->ops->open) { - retval = (ld->ops->open)(tty); - if (retval) - goto release_mem_out; - } - if (o_tty && o_tty->ldisc.ops->open) { - retval = (o_tty->ldisc.ops->open)(o_tty); - if (retval) { - if (ld->ops->close) - (ld->ops->close)(tty); - goto release_mem_out; - } - tty_ldisc_enable(o_tty); - } - tty_ldisc_enable(tty); - goto success; + retval = tty_ldisc_setup(tty, o_tty); + + if (retval) + goto release_mem_out; + goto success; /* * This fast open can be used if the tty is already open. @@ -2498,12 +1932,10 @@ static void release_tty(struct tty_struct *tty, int idx) static void release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; - struct tty_ldisc ld; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; char buf[64]; - unsigned long flags; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, @@ -2705,56 +2137,9 @@ static void release_dev(struct file *filp) printk(KERN_DEBUG "freeing tty structure..."); #endif /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. - */ - clear_bit(TTY_LDISC, &tty->flags); - cancel_delayed_work(&tty->buf.work); - - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate - */ - - flush_scheduled_work(); - - /* - * Wait for any short term users (we know they are just driver - * side waiters as the file is closing so user count on the file - * side is zero. + * Ask the line discipline code to release its structures */ - spin_lock_irqsave(&tty_ldisc_lock, flags); - while (tty->ldisc.refcount) { - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); - spin_lock_irqsave(&tty_ldisc_lock, flags); - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - /* - * Shutdown the current line discipline, and reset it to N_TTY. - * - * FIXME: this MUST get fixed for the new reflocking - */ - if (tty->ldisc.ops->close) - (tty->ldisc.ops->close)(tty); - tty_ldisc_put(tty->ldisc.ops); - - /* - * Switch the line discipline back - */ - WARN_ON(tty_ldisc_get(N_TTY, &ld)); - tty_ldisc_assign(tty, &ld); - tty_set_termios_ldisc(tty, N_TTY); - if (o_tty) { - /* FIXME: could o_tty be in setldisc here ? */ - clear_bit(TTY_LDISC, &o_tty->flags); - if (o_tty->ldisc.ops->close) - (o_tty->ldisc.ops->close)(o_tty); - tty_ldisc_put(o_tty->ldisc.ops); - WARN_ON(tty_ldisc_get(N_TTY, &ld)); - tty_ldisc_assign(o_tty, &ld); - tty_set_termios_ldisc(o_tty, N_TTY); - } + tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. @@ -3962,12 +3347,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push); static void initialize_tty_struct(struct tty_struct *tty) { - struct tty_ldisc ld; memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; - if (tty_ldisc_get(N_TTY, &ld) < 0) - panic("n_tty: init_tty"); - tty_ldisc_assign(tty, &ld); + tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; tty->overrun_time = jiffies; @@ -4280,7 +3662,7 @@ void __init console_init(void) initcall_t *call; /* Setup the default TTY line discipline. */ - (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); + tty_ldisc_begin(); /* * set up the console device so that later boot sequences can diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c new file mode 100644 index 000000000000..241cbdea65ab --- /dev/null +++ b/drivers/char/tty_ldisc.c @@ -0,0 +1,714 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/* + * This guards the refcounted line discipline lists. The lock + * must be taken with irqs off because there are hangup path + * callers who will do ldisc lookups and cannot sleep. + */ + +static DEFINE_SPINLOCK(tty_ldisc_lock); +static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +/* Line disc dispatch table */ +static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; + +/** + * tty_register_ldisc - install a line discipline + * @disc: ldisc number + * @new_ldisc: pointer to the ldisc object + * + * Installs a new line discipline into the kernel. The discipline + * is set up as unreferenced and then made available to the kernel + * from this point onwards. + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + tty_ldiscs[disc] = new_ldisc; + new_ldisc->num = disc; + new_ldisc->refcount = 0; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_register_ldisc); + +/** + * tty_unregister_ldisc - unload a line discipline + * @disc: ldisc number + * @new_ldisc: pointer to the ldisc object + * + * Remove a line discipline from the kernel providing it is not + * currently in use. + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +int tty_unregister_ldisc(int disc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty_ldiscs[disc]->refcount) + ret = -EBUSY; + else + tty_ldiscs[disc] = NULL; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_unregister_ldisc); + + +/** + * tty_ldisc_try_get - try and reference an ldisc + * @disc: ldisc number + * @ld: tty ldisc structure to complete + * + * Attempt to open and lock a line discipline into place. Return + * the line discipline refcounted and assigned in ld. On an error + * report the error code back + */ + +static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) +{ + unsigned long flags; + struct tty_ldisc_ops *ldops; + int err = -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld->ops = NULL; + ldops = tty_ldiscs[disc]; + /* Check the entry is defined */ + if (ldops) { + /* If the module is being unloaded we can't use it */ + if (!try_module_get(ldops->owner)) + err = -EAGAIN; + else { + /* lock it */ + ldops->refcount++; + ld->ops = ldops; + err = 0; + } + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return err; +} + +/** + * tty_ldisc_get - take a reference to an ldisc + * @disc: ldisc number + * @ld: tty line discipline structure to use + * + * Takes a reference to a line discipline. Deals with refcounts and + * module locking counts. Returns NULL if the discipline is not available. + * Returns a pointer to the discipline and bumps the ref count if it is + * available + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +static int tty_ldisc_get(int disc, struct tty_ldisc *ld) +{ + int err; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + err = tty_ldisc_try_get(disc, ld); + if (err == -EAGAIN) { + request_module("tty-ldisc-%d", disc); + err = tty_ldisc_try_get(disc, ld); + } + return err; +} + +/** + * tty_ldisc_put - drop ldisc reference + * @disc: ldisc number + * + * Drop a reference to a line discipline. Manage refcounts and + * module usage counts + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +static void tty_ldisc_put(struct tty_ldisc_ops *ld) +{ + unsigned long flags; + int disc = ld->num; + + BUG_ON(disc < N_TTY || disc >= NR_LDISCS); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = tty_ldiscs[disc]; + BUG_ON(ld->refcount == 0); + ld->refcount--; + module_put(ld->owner); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) +{ + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) +{ +} + +static int tty_ldiscs_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct tty_ldisc ld; + + if (tty_ldisc_get(i, &ld) < 0) + return 0; + seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); + tty_ldisc_put(ld.ops); + return 0; +} + +static const struct seq_operations tty_ldiscs_seq_ops = { + .start = tty_ldiscs_seq_start, + .next = tty_ldiscs_seq_next, + .stop = tty_ldiscs_seq_stop, + .show = tty_ldiscs_seq_show, +}; + +static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &tty_ldiscs_seq_ops); +} + +const struct file_operations tty_ldiscs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_tty_ldiscs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/** + * tty_ldisc_assign - set ldisc on a tty + * @tty: tty to assign + * @ld: line discipline + * + * Install an instance of a line discipline into a tty structure. The + * ldisc must have a reference count above zero to ensure it remains/ + * The tty instance refcount starts at zero. + * + * Locking: + * Caller must hold references + */ + +static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) +{ + ld->refcount = 0; + tty->ldisc = *ld; +} + +/** + * tty_ldisc_try - internal helper + * @tty: the tty + * + * Make a single attempt to grab and bump the refcount on + * the tty ldisc. Return 0 on failure or 1 on success. This is + * used to implement both the waiting and non waiting versions + * of tty_ldisc_ref + * + * Locking: takes tty_ldisc_lock + */ + +static int tty_ldisc_try(struct tty_struct *tty) +{ + unsigned long flags; + struct tty_ldisc *ld; + int ret = 0; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = &tty->ldisc; + if (test_bit(TTY_LDISC, &tty->flags)) { + ld->refcount++; + ret = 1; + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return ret; +} + +/** + * tty_ldisc_ref_wait - wait for the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * wait patiently until it changes. + * + * Note: Must not be called from an IRQ/timer context. The caller + * must also be careful not to hold other locks that will deadlock + * against a discipline change, such as an existing ldisc reference + * (which we check for) + * + * Locking: call functions take tty_ldisc_lock + */ + +struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) +{ + /* wait_event is a macro */ + wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); + if (tty->ldisc.refcount == 0) + printk(KERN_ERR "tty_ldisc_ref_wait\n"); + return &tty->ldisc; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); + +/** + * tty_ldisc_ref - get the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * return NULL. Can be called from IRQ and timer functions. + * + * Locking: called functions take tty_ldisc_lock + */ + +struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) +{ + if (tty_ldisc_try(tty)) + return &tty->ldisc; + return NULL; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref); + +/** + * tty_ldisc_deref - free a tty ldisc reference + * @ld: reference to free up + * + * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May + * be called in IRQ context. + * + * Locking: takes tty_ldisc_lock + */ + +void tty_ldisc_deref(struct tty_ldisc *ld) +{ + unsigned long flags; + + BUG_ON(ld == NULL); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (ld->refcount == 0) + printk(KERN_ERR "tty_ldisc_deref: no references.\n"); + else + ld->refcount--; + if (ld->refcount == 0) + wake_up(&tty_ldisc_wait); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +EXPORT_SYMBOL_GPL(tty_ldisc_deref); + +/** + * tty_ldisc_enable - allow ldisc use + * @tty: terminal to activate ldisc on + * + * Set the TTY_LDISC flag when the line discipline can be called + * again. Do necessary wakeups for existing sleepers. + * + * Note: nobody should set this bit except via this function. Clearing + * directly is allowed. + */ + +void tty_ldisc_enable(struct tty_struct *tty) +{ + set_bit(TTY_LDISC, &tty->flags); + wake_up(&tty_ldisc_wait); +} + +/** + * tty_set_termios_ldisc - set ldisc field + * @tty: tty structure + * @num: line discipline number + * + * This is probably overkill for real world processors but + * they are not on hot paths so a little discipline won't do + * any harm. + * + * Locking: takes termios_mutex + */ + +static void tty_set_termios_ldisc(struct tty_struct *tty, int num) +{ + mutex_lock(&tty->termios_mutex); + tty->termios->c_line = num; + mutex_unlock(&tty->termios_mutex); +} + + +/** + * tty_ldisc_restore - helper for tty ldisc change + * @tty: tty to recover + * @old: previous ldisc + * + * Restore the previous line discipline or N_TTY when a line discipline + * change fails due to an open error + */ + +static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) +{ + char buf[64]; + struct tty_ldisc new_ldisc; + + /* There is an outstanding reference here so this is safe */ + tty_ldisc_get(old->ops->num, old); + tty_ldisc_assign(tty, old); + tty_set_termios_ldisc(tty, old->ops->num); + if (old->ops->open && (old->ops->open(tty) < 0)) { + tty_ldisc_put(old->ops); + /* This driver is always present */ + if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) + panic("n_tty: get"); + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, N_TTY); + if (new_ldisc.ops->open) { + int r = new_ldisc.ops->open(tty); + if (r < 0) + panic("Couldn't open N_TTY ldisc for " + "%s --- error %d.", + tty_name(tty, buf), r); + } + } +} + +/** + * tty_set_ldisc - set line discipline + * @tty: the terminal to set + * @ldisc: the line discipline + * + * Set the discipline of a tty line. Must be called from a process + * context. + * + * Locking: takes tty_ldisc_lock. + * called functions take termios_mutex + */ + +int tty_set_ldisc(struct tty_struct *tty, int ldisc) +{ + int retval; + struct tty_ldisc o_ldisc, new_ldisc; + int work; + unsigned long flags; + struct tty_struct *o_tty; + +restart: + /* This is a bit ugly for now but means we can break the 'ldisc + is part of the tty struct' assumption later */ + retval = tty_ldisc_get(ldisc, &new_ldisc); + if (retval) + return retval; + + /* + * Problem: What do we do if this blocks ? + */ + + tty_wait_until_sent(tty, 0); + + if (tty->ldisc.ops->num == ldisc) { + tty_ldisc_put(new_ldisc.ops); + return 0; + } + + /* + * No more input please, we are switching. The new ldisc + * will update this value in the ldisc open function + */ + + tty->receive_room = 0; + + o_ldisc = tty->ldisc; + o_tty = tty->link; + + /* + * Make sure we don't change while someone holds a + * reference to the line discipline. The TTY_LDISC bit + * prevents anyone taking a reference once it is clear. + * We need the lock to avoid racing reference takers. + */ + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { + if (tty->ldisc.refcount) { + /* Free the new ldisc we grabbed. Must drop the lock + first. */ + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(o_ldisc.ops); + /* + * There are several reasons we may be busy, including + * random momentary I/O traffic. We must therefore + * retry. We could distinguish between blocking ops + * and retries if we made tty_ldisc_wait() smarter. + * That is up for discussion. + */ + if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + if (o_tty && o_tty->ldisc.refcount) { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(o_tty->ldisc.ops); + if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + } + /* + * If the TTY_LDISC bit is set, then we are racing against + * another ldisc change + */ + if (!test_bit(TTY_LDISC, &tty->flags)) { + struct tty_ldisc *ld; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(new_ldisc.ops); + ld = tty_ldisc_ref_wait(tty); + tty_ldisc_deref(ld); + goto restart; + } + + clear_bit(TTY_LDISC, &tty->flags); + if (o_tty) + clear_bit(TTY_LDISC, &o_tty->flags); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + /* + * From this point on we know nobody has an ldisc + * usage reference, nor can they obtain one until + * we say so later on. + */ + + work = cancel_delayed_work(&tty->buf.work); + /* + * Wait for ->hangup_work and ->buf.work handlers to terminate + * MUST NOT hold locks here. + */ + flush_scheduled_work(); + /* Shutdown the current discipline. */ + if (o_ldisc.ops->close) + (o_ldisc.ops->close)(tty); + + /* Now set up the new line discipline. */ + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, ldisc); + if (new_ldisc.ops->open) + retval = (new_ldisc.ops->open)(tty); + if (retval < 0) { + tty_ldisc_put(new_ldisc.ops); + tty_ldisc_restore(tty, &o_ldisc); + } + /* At this point we hold a reference to the new ldisc and a + a reference to the old ldisc. If we ended up flipping back + to the existing ldisc we have two references to it */ + + if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) + tty->ops->set_ldisc(tty); + + tty_ldisc_put(o_ldisc.ops); + + /* + * Allow ldisc referencing to occur as soon as the driver + * ldisc callback completes. + */ + + tty_ldisc_enable(tty); + if (o_tty) + tty_ldisc_enable(o_tty); + + /* Restart it in case no characters kick it off. Safe if + already running */ + if (work) + schedule_delayed_work(&tty->buf.work, 1); + return retval; +} + + +/** + * tty_ldisc_setup - open line discipline + * @tty: tty being shut down + * @o_tty: pair tty for pty/tty pairs + * + * Called during the initial open of a tty/pty pair in order to set up the + * line discplines and bind them to the tty. + */ + +int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) +{ + struct tty_ldisc *ld = &tty->ldisc; + int retval; + + if (ld->ops->open) { + retval = (ld->ops->open)(tty); + if (retval) + return retval; + } + if (o_tty && o_tty->ldisc.ops->open) { + retval = (o_tty->ldisc.ops->open)(o_tty); + if (retval) { + if (ld->ops->close) + (ld->ops->close)(tty); + return retval; + } + tty_ldisc_enable(o_tty); + } + tty_ldisc_enable(tty); + return 0; +} + +/** + * tty_ldisc_release - release line discipline + * @tty: tty being shut down + * @o_tty: pair tty for pty/tty pairs + * + * Called during the final close of a tty/pty pair in order to shut down the + * line discpline layer. + */ + +void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) +{ + unsigned long flags; + struct tty_ldisc ld; + /* + * Prevent flush_to_ldisc() from rescheduling the work for later. Then + * kill any delayed work. As this is the final close it does not + * race with the set_ldisc code path. + */ + clear_bit(TTY_LDISC, &tty->flags); + cancel_delayed_work(&tty->buf.work); + + /* + * Wait for ->hangup_work and ->buf.work handlers to terminate + */ + + flush_scheduled_work(); + + /* + * Wait for any short term users (we know they are just driver + * side waiters as the file is closing so user count on the file + * side is zero. + */ + spin_lock_irqsave(&tty_ldisc_lock, flags); + while (tty->ldisc.refcount) { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); + spin_lock_irqsave(&tty_ldisc_lock, flags); + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + /* + * Shutdown the current line discipline, and reset it to N_TTY. + * + * FIXME: this MUST get fixed for the new reflocking + */ + if (tty->ldisc.ops->close) + (tty->ldisc.ops->close)(tty); + tty_ldisc_put(tty->ldisc.ops); + + /* + * Switch the line discipline back + */ + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(tty, &ld); + tty_set_termios_ldisc(tty, N_TTY); + if (o_tty) { + /* FIXME: could o_tty be in setldisc here ? */ + clear_bit(TTY_LDISC, &o_tty->flags); + if (o_tty->ldisc.ops->close) + (o_tty->ldisc.ops->close)(o_tty); + tty_ldisc_put(o_tty->ldisc.ops); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(o_tty, &ld); + tty_set_termios_ldisc(o_tty, N_TTY); + } +} + +/** + * tty_ldisc_init - ldisc setup for new tty + * @tty: tty being allocated + * + * Set up the line discipline objects for a newly allocated tty. Note that + * the tty structure is not completely set up when this call is made. + */ + +void tty_ldisc_init(struct tty_struct *tty) +{ + struct tty_ldisc ld; + if (tty_ldisc_get(N_TTY, &ld) < 0) + panic("n_tty: init_tty"); + tty_ldisc_assign(tty, &ld); +} + +void tty_ldisc_begin(void) +{ + /* Setup the default TTY line discipline. */ + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); +} diff --git a/include/linux/tty.h b/include/linux/tty.h index 4e5833073aa6..e3579cb086e0 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -317,8 +317,6 @@ extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int tty_check_change(struct tty_struct *tty); extern void stop_tty(struct tty_struct *tty); extern void start_tty(struct tty_struct *tty); -extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); -extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); extern struct device *tty_register_device(struct tty_driver *driver, @@ -383,6 +381,15 @@ extern void tty_port_init(struct tty_port *port); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); +extern int tty_unregister_ldisc(int disc); +extern int tty_set_ldisc(struct tty_struct *tty, int ldisc); +extern int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty); +extern void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty); +extern void tty_ldisc_init(struct tty_struct *tty); +extern void tty_ldisc_begin(void); +/* This last one is just for the tty layer internals and shouldn't be used elsewhere */ +extern void tty_ldisc_enable(struct tty_struct *tty); /* n_tty.c */ -- cgit v1.2.3 From 9e98966c7bb94355689478bc84cc3e0c190f977e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 22 Jul 2008 11:18:03 +0100 Subject: tty: rework break handling Some hardware needs to do break handling itself and may have partial support only. Make break_ctl return an error code. Add a tty driver flag so you can indicate driver hardware side break support. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/Kconfig | 2 +- drivers/char/amiserial.c | 3 +- drivers/char/cyclades.c | 8 ++--- drivers/char/esp.c | 5 +-- drivers/char/istallion.c | 11 +++--- drivers/char/moxa.c | 3 +- drivers/char/mxser.c | 3 +- drivers/char/pcmcia/synclink_cs.c | 5 +-- drivers/char/rocket.c | 5 +-- drivers/char/sx.c | 3 +- drivers/char/synclink.c | 7 ++-- drivers/char/synclink_gt.c | 9 ++--- drivers/char/synclinkmp.c | 9 ++--- drivers/char/tty_io.c | 71 +++++++++++++++------------------------ drivers/char/vme_scc.c | 5 +-- drivers/isdn/capi/capi.c | 3 +- drivers/serial/serial_core.c | 3 +- drivers/usb/class/cdc-acm.c | 9 +++-- drivers/usb/serial/usb-serial.c | 3 +- include/linux/tty_driver.h | 14 ++++++-- 20 files changed, 97 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ba8782b9c217..a185263b5862 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -218,7 +218,7 @@ config MOXA_SMARTIO config ISI tristate "Multi-Tech multiport card support (EXPERIMENTAL)" - depends on SERIAL_NONSTANDARD && PCI + depends on SERIAL_NONSTANDARD && PCI && BROKEN select FW_LOADER help This is a driver for the Multi-Tech cards which provide several diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 37457e5a4f2b..3530ff417a51 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1248,7 +1248,7 @@ static int rs_tiocmset(struct tty_struct *tty, struct file *file, /* * rs_break() --- routine which turns the break handling on or off */ -static void rs_break(struct tty_struct *tty, int break_state) +static int rs_break(struct tty_struct *tty, int break_state) { struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned long flags; @@ -1263,6 +1263,7 @@ static void rs_break(struct tty_struct *tty, int break_state) custom.adkcon = AC_UARTBRK; mb(); local_irq_restore(flags); + return 0; } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e991dc85f2fb..fe6d774fe2e4 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -3700,14 +3700,15 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, /* * cy_break() --- routine which turns the break handling on or off */ -static void cy_break(struct tty_struct *tty, int break_state) +static int cy_break(struct tty_struct *tty, int break_state) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; + int retval = 0; if (serial_paranoia_check(info, tty->name, "cy_break")) - return; + return -EINVAL; card = info->card; @@ -3736,8 +3737,6 @@ static void cy_break(struct tty_struct *tty, int break_state) } } } else { - int retval; - if (break_state == -1) { retval = cyz_issue_cmd(card, info->line - card->first_line, @@ -3758,6 +3757,7 @@ static void cy_break(struct tty_struct *tty, int break_state) } } spin_unlock_irqrestore(&card->card_lock, flags); + return retval; } /* cy_break */ static int get_mon_info(struct cyclades_port *info, diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 2eaf09f93e3d..7f077c0097f6 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -1725,13 +1725,13 @@ static int esp_tiocmset(struct tty_struct *tty, struct file *file, /* * rs_break() --- routine which turns the break handling on or off */ -static void esp_break(struct tty_struct *tty, int break_state) +static int esp_break(struct tty_struct *tty, int break_state) { struct esp_struct *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "esp_break")) - return; + return -EINVAL; if (break_state == -1) { spin_lock_irqsave(&info->lock, flags); @@ -1747,6 +1747,7 @@ static void esp_break(struct tty_struct *tty, int break_state) serial_out(info, UART_ESI_CMD2, 0x00); spin_unlock_irqrestore(&info->lock, flags); } + return 0; } static int rs_ioctl(struct tty_struct *tty, struct file *file, diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 7930fba4bafc..63d22b5ebc0d 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -609,7 +609,7 @@ static void stli_unthrottle(struct tty_struct *tty); static void stli_stop(struct tty_struct *tty); static void stli_start(struct tty_struct *tty); static void stli_flushbuffer(struct tty_struct *tty); -static void stli_breakctl(struct tty_struct *tty, int state); +static int stli_breakctl(struct tty_struct *tty, int state); static void stli_waituntilsent(struct tty_struct *tty, int timeout); static void stli_sendxchar(struct tty_struct *tty, char ch); static void stli_hangup(struct tty_struct *tty); @@ -1909,7 +1909,7 @@ static void stli_flushbuffer(struct tty_struct *tty) /*****************************************************************************/ -static void stli_breakctl(struct tty_struct *tty, int state) +static int stli_breakctl(struct tty_struct *tty, int state) { struct stlibrd *brdp; struct stliport *portp; @@ -1917,15 +1917,16 @@ static void stli_breakctl(struct tty_struct *tty, int state) portp = tty->driver_data; if (portp == NULL) - return; + return -EINVAL; if (portp->brdnr >= stli_nrbrds) - return; + return -EINVAL; brdp = stli_brds[portp->brdnr]; if (brdp == NULL) - return; + return -EINVAL; arg = (state == -1) ? BREAKON : BREAKOFF; stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); + return 0; } /*****************************************************************************/ diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 2bba250ffc8e..d3d7864e0c1e 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -374,12 +374,13 @@ copy: return ret; } -static void moxa_break_ctl(struct tty_struct *tty, int state) +static int moxa_break_ctl(struct tty_struct *tty, int state) { struct moxa_port *port = tty->driver_data; moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak, Magic_code); + return 0; } static const struct tty_operations moxa_ops = { diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 1fb25571bf85..f04c3c58a05a 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -2183,7 +2183,7 @@ static void mxser_hangup(struct tty_struct *tty) /* * mxser_rs_break() --- routine which turns the break handling on or off */ -static void mxser_rs_break(struct tty_struct *tty, int break_state) +static int mxser_rs_break(struct tty_struct *tty, int break_state) { struct mxser_port *info = tty->driver_data; unsigned long flags; @@ -2196,6 +2196,7 @@ static void mxser_rs_break(struct tty_struct *tty, int break_state) outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, info->ioaddr + UART_LCR); spin_unlock_irqrestore(&info->slock, flags); + return 0; } static void mxser_receive_chars(struct mxser_port *port, int *status) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index b694d430f10e..d1fceabe3aef 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2230,7 +2230,7 @@ static int tiocmset(struct tty_struct *tty, struct file *file, * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear */ -static void mgslpc_break(struct tty_struct *tty, int break_state) +static int mgslpc_break(struct tty_struct *tty, int break_state) { MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; @@ -2240,7 +2240,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->lock,flags); if (break_state == -1) @@ -2248,6 +2248,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state) else clear_reg_bits(info, CHA+DAFO, BIT6); spin_unlock_irqrestore(&info->lock,flags); + return 0; } /* Service an IOCTL request diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index e670eae2f510..584d791e84a6 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1236,13 +1236,13 @@ static void rp_set_termios(struct tty_struct *tty, } } -static void rp_break(struct tty_struct *tty, int break_state) +static int rp_break(struct tty_struct *tty, int break_state) { struct r_port *info = (struct r_port *) tty->driver_data; unsigned long flags; if (rocket_paranoia_check(info, "rp_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->slock, flags); if (break_state == -1) @@ -1250,6 +1250,7 @@ static void rp_break(struct tty_struct *tty, int break_state) else sClrBreak(&info->channel); spin_unlock_irqrestore(&info->slock, flags); + return 0; } /* diff --git a/drivers/char/sx.c b/drivers/char/sx.c index d5cffcd6a572..2162439bbe48 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1840,7 +1840,7 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, return rc; } -static void sx_break(struct tty_struct *tty, int flag) +static int sx_break(struct tty_struct *tty, int flag) { struct sx_port *port = tty->driver_data; int rv; @@ -1857,6 +1857,7 @@ static void sx_break(struct tty_struct *tty, int flag) read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat))); unlock_kernel(); func_exit(); + return 0; } static int sx_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 527d220aa4aa..ef6706f09061 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -2897,9 +2897,9 @@ static int tiocmset(struct tty_struct *tty, struct file *file, * * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear - * Return Value: None + * Return Value: error code */ -static void mgsl_break(struct tty_struct *tty, int break_state) +static int mgsl_break(struct tty_struct *tty, int break_state) { struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; unsigned long flags; @@ -2909,7 +2909,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (mgsl_paranoia_check(info, tty->name, "mgsl_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->irq_spinlock,flags); if (break_state == -1) @@ -2917,6 +2917,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state) else usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; } /* end of mgsl_break() */ diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 2c3e43bb2cc9..cf87bb89a77d 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -165,7 +165,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); /* * generic HDLC support and callbacks @@ -513,7 +513,7 @@ static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty, struct file *file); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); static int get_interface(struct slgt_info *info, int __user *if_mode); static int set_interface(struct slgt_info *info, int if_mode); static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); @@ -1452,14 +1452,14 @@ static void unthrottle(struct tty_struct * tty) * set or clear transmit break condition * break_state -1=set break condition, 0=clear */ -static void set_break(struct tty_struct *tty, int break_state) +static int set_break(struct tty_struct *tty, int break_state) { struct slgt_info *info = tty->driver_data; unsigned short value; unsigned long flags; if (sanity_check(info, tty->name, "set_break")) - return; + return -EINVAL; DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); spin_lock_irqsave(&info->lock,flags); @@ -1470,6 +1470,7 @@ static void set_break(struct tty_struct *tty, int break_state) value &= ~BIT6; wr_reg16(info, TCR, value); spin_unlock_irqrestore(&info->lock,flags); + return 0; } #if SYNCLINK_GENERIC_HDLC diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5768c4136342..c0490cbd0db2 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -527,7 +527,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); #if SYNCLINK_GENERIC_HDLC #define dev_to_port(D) (dev_to_hdlc(D)->priv) @@ -552,7 +552,7 @@ static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty, struct file *file); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); static void add_device(SLMP_INFO *info); static void device_init(int adapter_num, struct pci_dev *pdev); @@ -1587,7 +1587,7 @@ static void unthrottle(struct tty_struct * tty) /* set or clear transmit break condition * break_state -1=set break condition, 0=clear */ -static void set_break(struct tty_struct *tty, int break_state) +static int set_break(struct tty_struct *tty, int break_state) { unsigned char RegValue; SLMP_INFO * info = (SLMP_INFO *)tty->driver_data; @@ -1598,7 +1598,7 @@ static void set_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (sanity_check(info, tty->name, "set_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->lock,flags); RegValue = read_reg(info, CTL); @@ -1608,6 +1608,7 @@ static void set_break(struct tty_struct *tty, int break_state) RegValue &= ~BIT3; write_reg(info, CTL, RegValue); spin_unlock_irqrestore(&info->lock,flags); + return 0; } #if SYNCLINK_GENERIC_HDLC diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index d27a08b374d0..d94cd8410c53 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2849,16 +2849,29 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) static int send_break(struct tty_struct *tty, unsigned int duration) { - if (tty_write_lock(tty, 0) < 0) - return -EINTR; - tty->ops->break_ctl(tty, -1); - if (!signal_pending(current)) - msleep_interruptible(duration); - tty->ops->break_ctl(tty, 0); - tty_write_unlock(tty); - if (signal_pending(current)) - return -EINTR; - return 0; + int retval; + + if (tty->ops->break_ctl == NULL) + return 0; + + if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) + retval = tty->ops->break_ctl(tty, duration); + else { + /* Do the work ourselves */ + if (tty_write_lock(tty, 0) < 0) + return -EINTR; + retval = tty->ops->break_ctl(tty, -1); + if (retval) + goto out; + if (!signal_pending(current)) + msleep_interruptible(duration); + retval = tty->ops->break_ctl(tty, 0); +out: + tty_write_unlock(tty); + if (signal_pending(current)) + retval = -EINTR; + } + return retval; } /** @@ -2949,36 +2962,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) tty->driver->subtype == PTY_TYPE_MASTER) real_tty = tty->link; - /* - * Break handling by driver - */ - - retval = -EINVAL; - - if (!tty->ops->break_ctl) { - switch (cmd) { - case TIOCSBRK: - case TIOCCBRK: - if (tty->ops->ioctl) - retval = tty->ops->ioctl(tty, file, cmd, arg); - if (retval != -EINVAL && retval != -ENOIOCTLCMD) - printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); - return retval; - - /* These two ioctl's always return success; even if */ - /* the driver doesn't support them. */ - case TCSBRK: - case TCSBRKP: - if (!tty->ops->ioctl) - return 0; - retval = tty->ops->ioctl(tty, file, cmd, arg); - if (retval != -EINVAL && retval != -ENOIOCTLCMD) - printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); - if (retval == -ENOIOCTLCMD) - retval = 0; - return retval; - } - } /* * Factor out some common prep work @@ -3000,6 +2983,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } + /* + * Now do the stuff. + */ switch (cmd) { case TIOCSTI: return tiocsti(tty, p); @@ -3043,12 +3029,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ case TIOCSBRK: /* Turn break on, unconditionally */ if (tty->ops->break_ctl) - tty->ops->break_ctl(tty, -1); + return tty->ops->break_ctl(tty, -1); return 0; - case TIOCCBRK: /* Turn break off, unconditionally */ if (tty->ops->break_ctl) - tty->ops->break_ctl(tty, 0); + return tty->ops->break_ctl(tty, 0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ /* non-zero arg means wait for all output data diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index f17ac043b551..69c5afe97f19 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -85,7 +85,7 @@ static irqreturn_t scc_rx_int(int irq, void *data); static irqreturn_t scc_stat_int(int irq, void *data); static irqreturn_t scc_spcond_int(int irq, void *data); static void scc_setsignals(struct scc_port *port, int dtr, int rts); -static void scc_break_ctl(struct tty_struct *tty, int break_state); +static int scc_break_ctl(struct tty_struct *tty, int break_state); static struct tty_driver *scc_driver; @@ -942,7 +942,7 @@ static int scc_ioctl(struct tty_struct *tty, struct file *file, } -static void scc_break_ctl(struct tty_struct *tty, int break_state) +static int scc_break_ctl(struct tty_struct *tty, int break_state) { struct scc_port *port = (struct scc_port *)tty->driver_data; unsigned long flags; @@ -952,6 +952,7 @@ static void scc_break_ctl(struct tty_struct *tty, int break_state) SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, break_state ? TCR_SEND_BREAK : 0); local_irq_restore(flags); + return 0; } diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 8a35029caca0..19e005e81fef 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1302,11 +1302,12 @@ static void capinc_tty_hangup(struct tty_struct *tty) #endif } -static void capinc_tty_break_ctl(struct tty_struct *tty, int state) +static int capinc_tty_break_ctl(struct tty_struct *tty, int state) { #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state); #endif + return 0; } static void capinc_tty_flush_buffer(struct tty_struct *tty) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 0bce1fe2c62a..f977c98cfa95 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -934,7 +934,7 @@ uart_tiocmset(struct tty_struct *tty, struct file *file, return ret; } -static void uart_break_ctl(struct tty_struct *tty, int break_state) +static int uart_break_ctl(struct tty_struct *tty, int break_state) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; @@ -945,6 +945,7 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) port->ops->break_ctl(port, break_state); mutex_unlock(&state->mutex); + return 0; } static int uart_do_autoconfig(struct uart_state *state) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 95ae6377d7e5..0725b1871f23 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -732,13 +732,16 @@ static void acm_tty_unthrottle(struct tty_struct *tty) tasklet_schedule(&acm->urb_task); } -static void acm_tty_break_ctl(struct tty_struct *tty, int state) +static int acm_tty_break_ctl(struct tty_struct *tty, int state) { struct acm *acm = tty->driver_data; + int retval; if (!ACM_READY(acm)) - return; - if (acm_send_break(acm, state ? 0xffff : 0)) + return -EINVAL; + retval = acm_send_break(acm, state ? 0xffff : 0); + if (retval < 0) dbg("send break failed"); + return retval; } static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 51917b0f079a..8c2d531eedea 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -395,7 +395,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) tty_termios_copy_hw(tty->termios, old); } -static void serial_break(struct tty_struct *tty, int break_state) +static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -409,6 +409,7 @@ static void serial_break(struct tty_struct *tty, int break_state) port->serial->type->break_ctl(tty, break_state); unlock_kernel(); } + return 0; } static int serial_read_proc(char *page, char **start, off_t off, int count, diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index d2a003586761..e1065ac0d922 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -135,7 +135,7 @@ * * Optional: * - * void (*break_ctl)(struct tty_stuct *tty, int state); + * int (*break_ctl)(struct tty_stuct *tty, int state); * * This optional routine requests the tty driver to turn on or * off BREAK status on the RS-232 port. If state is -1, @@ -146,6 +146,10 @@ * handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK, * TIOCCBRK. * + * If the driver sets TTY_DRIVER_HARDWARE_BREAK then the interface + * will also be called with actual times and the hardware is expected + * to do the delay work itself. 0 and -1 are still used for on/off. + * * Optional: Required for TCSBRK/BRKP/etc handling. * * void (*wait_until_sent)(struct tty_struct *tty, int timeout); @@ -192,7 +196,7 @@ struct tty_operations { void (*stop)(struct tty_struct *tty); void (*start)(struct tty_struct *tty); void (*hangup)(struct tty_struct *tty); - void (*break_ctl)(struct tty_struct *tty, int state); + int (*break_ctl)(struct tty_struct *tty, int state); void (*flush_buffer)(struct tty_struct *tty); void (*set_ldisc)(struct tty_struct *tty); void (*wait_until_sent)(struct tty_struct *tty, int timeout); @@ -285,12 +289,18 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line); * TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead * use dynamic memory keyed through the devpts filesystem. This * is only applicable to the pty driver. + * + * TTY_DRIVER_HARDWARE_BREAK -- hardware handles break signals. Pass + * the requested timeout to the caller instead of using a simple + * on/off interface. + * */ #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 #define TTY_DRIVER_REAL_RAW 0x0004 #define TTY_DRIVER_DYNAMIC_DEV 0x0008 #define TTY_DRIVER_DEVPTS_MEM 0x0010 +#define TTY_DRIVER_HARDWARE_BREAK 0x0020 /* tty driver types */ #define TTY_DRIVER_TYPE_SYSTEM 0x0001 -- cgit v1.2.3 From e5590717afd5fb6f494323206a1a35ea25610c2d Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Tue, 22 Jul 2008 11:21:39 +0100 Subject: synclink_gt: add serial bit order control Add control of hardware serial bit order between LSB first (default/standard) and MSB first. Signed-off-by: Paul Fulghum Signed-off-by: Andrew Morton Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/synclink_gt.c | 2 ++ include/linux/synclink.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 0e59cf54adaf..3cfc9e1f8882 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -4403,6 +4403,8 @@ static void msc_set_vcr(struct slgt_info *info) break; } + if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) + val |= BIT4; if (info->signals & SerialSignal_DTR) val |= BIT3; if (info->signals & SerialSignal_RTS) diff --git a/include/linux/synclink.h b/include/linux/synclink.h index 45f6bc82d317..c844a229acc9 100644 --- a/include/linux/synclink.h +++ b/include/linux/synclink.h @@ -136,6 +136,7 @@ #define MGSL_INTERFACE_RTS_EN 0x10 #define MGSL_INTERFACE_LL 0x20 #define MGSL_INTERFACE_RL 0x40 +#define MGSL_INTERFACE_MSB_FIRST 0x80 typedef struct _MGSL_PARAMS { -- cgit v1.2.3 From 217d5a51953143046cf2972eebdefe2b2db05718 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Jul 2008 17:43:42 +0200 Subject: fs_enet: Remove unused fields in the fs_mii_bb_platform_info structure. The mdio_port, mdio_bit, mdc_port and mdc_bit fields in the fs_mii_bb_platform_info structure are left-overs from the move to the Phy Abstraction Layer subsystem. They are not used anymore and can be safely removed. Signed-off-by: Laurent Pinchart Signed-off-by: Jeff Garzik --- include/linux/fs_enet_pd.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 9bc045b8c478..decc4b56ccf9 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -103,10 +103,6 @@ struct fs_mii_bb_platform_info { struct fs_mii_bit mdio_dir; struct fs_mii_bit mdio_dat; struct fs_mii_bit mdc_dat; - int mdio_port; /* port & bit for MDIO */ - int mdio_bit; - int mdc_port; /* port & bit for MDC */ - int mdc_bit; int delay; /* delay in us */ int irq[32]; /* irqs per phy's */ }; -- cgit v1.2.3 From d29f749e252bcdbfe7a75a58f0ee92da16f127c0 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 22 Jul 2008 14:09:06 -0700 Subject: net: Fix build failure with 'make mandocs'. The function header comments have to go with the functions they are documenting, or things go horribly wrong when we try to process them with the docbook tools. Warning(include/linux/netdevice.h:1006): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1033): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1067): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1093): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1474): No description found for parameter 'txq' Error(net/core/dev.c:1674): cannot understand prototype: 'u32 simple_tx_hashrnd; ' Signed-off-by: Dave Jones Acked-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/netdevice.h | 58 +++++++++++++++++++++++------------------------ net/core/dev.c | 51 ++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f5ea445f89f0..b4d056ceab96 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -996,17 +996,17 @@ static inline void netif_tx_schedule_all(struct net_device *dev) netif_schedule_queue(netdev_get_tx_queue(dev, i)); } +static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) +{ + clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + /** * netif_start_queue - allow transmit * @dev: network device * * Allow upper layers to call the device hard_start_xmit routine. */ -static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) -{ - clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -} - static inline void netif_start_queue(struct net_device *dev) { netif_tx_start_queue(netdev_get_tx_queue(dev, 0)); @@ -1022,13 +1022,6 @@ static inline void netif_tx_start_all_queues(struct net_device *dev) } } -/** - * netif_wake_queue - restart transmit - * @dev: network device - * - * Allow upper layers to call the device hard_start_xmit routine. - * Used for flow control when transmit resources are available. - */ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) { #ifdef CONFIG_NETPOLL_TRAP @@ -1041,6 +1034,13 @@ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) __netif_schedule(dev_queue->qdisc); } +/** + * netif_wake_queue - restart transmit + * @dev: network device + * + * Allow upper layers to call the device hard_start_xmit routine. + * Used for flow control when transmit resources are available. + */ static inline void netif_wake_queue(struct net_device *dev) { netif_tx_wake_queue(netdev_get_tx_queue(dev, 0)); @@ -1056,6 +1056,11 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev) } } +static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) +{ + set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + /** * netif_stop_queue - stop transmitted packets * @dev: network device @@ -1063,11 +1068,6 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev) * Stop upper layers calling the device hard_start_xmit routine. * Used for flow control when transmit resources are unavailable. */ -static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) -{ - set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -} - static inline void netif_stop_queue(struct net_device *dev) { netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); @@ -1083,17 +1083,17 @@ static inline void netif_tx_stop_all_queues(struct net_device *dev) } } +static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) +{ + return test_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + /** * netif_queue_stopped - test if transmit queue is flowblocked * @dev: network device * * Test if transmit queue on device is currently unable to send. */ -static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) -{ - return test_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -} - static inline int netif_queue_stopped(const struct net_device *dev) { return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); @@ -1463,13 +1463,6 @@ static inline void netif_rx_complete(struct net_device *dev, local_irq_restore(flags); } -/** - * netif_tx_lock - grab network device transmit lock - * @dev: network device - * @cpu: cpu number of lock owner - * - * Get network device transmit lock - */ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) { spin_lock(&txq->_xmit_lock); @@ -1482,6 +1475,13 @@ static inline void __netif_tx_lock_bh(struct netdev_queue *txq) txq->xmit_lock_owner = smp_processor_id(); } +/** + * netif_tx_lock - grab network device transmit lock + * @dev: network device + * @cpu: cpu number of lock owner + * + * Get network device transmit lock + */ static inline void netif_tx_lock(struct net_device *dev) { int cpu = smp_processor_id(); diff --git a/net/core/dev.c b/net/core/dev.c index ad5598d2bb37..65eea83613ef 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1645,32 +1645,6 @@ out_kfree_skb: return 0; } -/** - * dev_queue_xmit - transmit a buffer - * @skb: buffer to transmit - * - * Queue a buffer for transmission to a network device. The caller must - * have set the device and priority and built the buffer before calling - * this function. The function can be called from an interrupt. - * - * A negative errno code is returned on a failure. A success does not - * guarantee the frame will be transmitted as it may be dropped due - * to congestion or traffic shaping. - * - * ----------------------------------------------------------------------------------- - * I notice this method can also return errors from the queue disciplines, - * including NET_XMIT_DROP, which is a positive value. So, errors can also - * be positive. - * - * Regardless of the return value, the skb is consumed, so it is currently - * difficult to retry a send to this method. (You can bump the ref count - * before sending to hold a reference for retry if you are careful.) - * - * When calling this method, interrupts MUST be enabled. This is because - * the BH enable code must have IRQs enabled so that it will not deadlock. - * --BLG - */ - static u32 simple_tx_hashrnd; static int simple_tx_hashrnd_initialized = 0; @@ -1738,6 +1712,31 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev, return netdev_get_tx_queue(dev, queue_index); } +/** + * dev_queue_xmit - transmit a buffer + * @skb: buffer to transmit + * + * Queue a buffer for transmission to a network device. The caller must + * have set the device and priority and built the buffer before calling + * this function. The function can be called from an interrupt. + * + * A negative errno code is returned on a failure. A success does not + * guarantee the frame will be transmitted as it may be dropped due + * to congestion or traffic shaping. + * + * ----------------------------------------------------------------------------------- + * I notice this method can also return errors from the queue disciplines, + * including NET_XMIT_DROP, which is a positive value. So, errors can also + * be positive. + * + * Regardless of the return value, the skb is consumed, so it is currently + * difficult to retry a send to this method. (You can bump the ref count + * before sending to hold a reference for retry if you are careful.) + * + * When calling this method, interrupts MUST be enabled. This is because + * the BH enable code must have IRQs enabled so that it will not deadlock. + * --BLG + */ int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; -- cgit v1.2.3 From 8086cd451f08f4c0f9693fc66d87754bbd18cfba Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 22 Jul 2008 14:19:19 -0700 Subject: netns: make get_proc_net() static get_proc_net() can now become static. Signed-off-by: Adrian Bunk Acked-by: Pavel Emelyanov Signed-off-by: David S. Miller --- fs/proc/proc_net.c | 11 +++++------ include/linux/proc_fs.h | 2 -- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index b224a28e0c15..7bc296f424ae 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -27,6 +27,11 @@ #include "internal.h" +static struct net *get_proc_net(const struct inode *inode) +{ + return maybe_get_net(PDE_NET(PDE(inode))); +} + int seq_open_net(struct inode *ino, struct file *f, const struct seq_operations *ops, int size) { @@ -185,12 +190,6 @@ void proc_net_remove(struct net *net, const char *name) } EXPORT_SYMBOL_GPL(proc_net_remove); -struct net *get_proc_net(const struct inode *inode) -{ - return maybe_get_net(PDE_NET(PDE(inode))); -} -EXPORT_SYMBOL_GPL(get_proc_net); - static __net_init int proc_net_ns_init(struct net *net) { struct proc_dir_entry *netd, *net_statd; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index fff1d27ddb4c..15a9eaf4a802 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -305,8 +305,6 @@ static inline struct net *PDE_NET(struct proc_dir_entry *pde) return pde->parent->data; } -struct net *get_proc_net(const struct inode *inode); - struct proc_maps_private { struct pid *pid; struct task_struct *task; -- cgit v1.2.3 From 47b374752aed1c029f995473c7c463ee3ae5fbaa Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 22 Jul 2008 14:19:39 -0700 Subject: IB/mlx4: Rename struct mlx4_lso_seg to mlx4_wqe_lso_seg Make the struct name consistent with other WQE segment struct types defined in . Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/qp.c | 2 +- include/linux/mlx4/qp.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 89eb6cbe592e..bda0859a5ac5 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1395,7 +1395,7 @@ static void __set_data_seg(struct mlx4_wqe_data_seg *dseg, struct ib_sge *sg) dseg->addr = cpu_to_be64(sg->addr); } -static int build_lso_seg(struct mlx4_lso_seg *wqe, struct ib_send_wr *wr, +static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr, struct mlx4_ib_qp *qp, unsigned *lso_seg_len) { unsigned halign = ALIGN(sizeof *wqe + wr->wr.ud.hlen, 16); diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 7f128b266faa..f02e9ed36cfa 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -219,7 +219,7 @@ struct mlx4_wqe_datagram_seg { __be32 reservd[2]; }; -struct mlx4_lso_seg { +struct mlx4_wqe_lso_seg { __be32 mss_hdr_size; __be32 header[0]; }; -- cgit v1.2.3 From e5899e1b7d73e67de758a32174a859cc2586c0b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 19 Jul 2008 14:39:24 +0200 Subject: PCI PM: make more PCI PM core functionality available to drivers Make more PCI PM core functionality available to drivers * Export pci_pme_capable() so that it can be called directly by drivers (for example, tg3 needs that). * Move the state choosing part of pci_prepare_to_sleep() to a separate function, pci_target_state(), that can be called directly by drivers (for example, tg3 needs that). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 34 ++++++++++++++++++++++++---------- include/linux/pci.h | 2 ++ 2 files changed, 26 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d00f0e0d8453..e9c356236d27 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1040,7 +1040,7 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) * @dev: PCI device to handle. * @state: PCI state from which device will issue PME#. */ -static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) +bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) { if (!dev->pm_cap) return false; @@ -1123,17 +1123,10 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) } /** - * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state - * @dev: Device to handle. - * - * Choose the power state appropriate for the device depending on whether - * it can wake up the system and/or is power manageable by the platform - * (PCI_D3hot is the default) and put the device into that state. */ -int pci_prepare_to_sleep(struct pci_dev *dev) +pci_power_t pci_target_state(struct pci_dev *dev) { pci_power_t target_state = PCI_D3hot; - int error; if (platform_pci_power_manageable(dev)) { /* @@ -1160,7 +1153,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev) * to generate PME#. */ if (!dev->pm_cap) - return -EIO; + return PCI_POWER_ERROR; if (dev->pme_support) { while (target_state @@ -1169,6 +1162,25 @@ int pci_prepare_to_sleep(struct pci_dev *dev) } } + return target_state; +} + +/** + * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state + * @dev: Device to handle. + * + * Choose the power state appropriate for the device depending on whether + * it can wake up the system and/or is power manageable by the platform + * (PCI_D3hot is the default) and put the device into that state. + */ +int pci_prepare_to_sleep(struct pci_dev *dev) +{ + pci_power_t target_state = pci_target_state(dev); + int error; + + if (target_state == PCI_POWER_ERROR) + return -EIO; + pci_enable_wake(dev, target_state, true); error = pci_set_power_state(dev, target_state); @@ -1918,7 +1930,9 @@ EXPORT_SYMBOL(pci_select_bars); EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); +EXPORT_SYMBOL(pci_pme_capable); EXPORT_SYMBOL(pci_enable_wake); +EXPORT_SYMBOL(pci_target_state); EXPORT_SYMBOL(pci_prepare_to_sleep); EXPORT_SYMBOL(pci_back_from_sleep); EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); diff --git a/include/linux/pci.h b/include/linux/pci.h index a6a088e1a804..1d296d31abe0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -638,7 +638,9 @@ int pci_save_state(struct pci_dev *dev); int pci_restore_state(struct pci_dev *dev); int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); +bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); +pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); -- cgit v1.2.3 From 7f1b358a236ee9c19657a619ac6f2dcabcaa0924 Mon Sep 17 00:00:00 2001 From: Maciej Sosnowski Date: Tue, 22 Jul 2008 17:30:57 -0700 Subject: I/OAT: I/OAT version 3.0 support This patch adds to ioatdma and dca modules support for Intel I/OAT DMA engine ver.3 (aka CB3 device). The main features of I/OAT ver.3 are: * 8 single channel DMA devices (8 channels total) * 8 DCA providers, each can accept 2 requesters * 8-bit TAG values and 32-bit extended APIC IDs Signed-off-by: Maciej Sosnowski Signed-off-by: Dan Williams --- drivers/dca/dca-core.c | 131 ++++++++++++++++----- drivers/dca/dca-sysfs.c | 3 +- drivers/dma/ioat.c | 15 +++ drivers/dma/ioat_dca.c | 244 ++++++++++++++++++++++++++++++++++++++-- drivers/dma/ioat_dma.c | 96 ++++++++++++++-- drivers/dma/ioatdma.h | 5 +- drivers/dma/ioatdma_hw.h | 1 + drivers/dma/ioatdma_registers.h | 20 ++++ include/linux/dca.h | 7 +- include/linux/pci_ids.h | 8 ++ 10 files changed, 481 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bf5b92f86df7..ec249d2db633 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -28,13 +28,29 @@ #include #include -MODULE_LICENSE("GPL"); +#define DCA_VERSION "1.4" -/* For now we're assuming a single, global, DCA provider for the system. */ +MODULE_VERSION(DCA_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Intel Corporation"); static DEFINE_SPINLOCK(dca_lock); -static struct dca_provider *global_dca = NULL; +static LIST_HEAD(dca_providers); + +static struct dca_provider *dca_find_provider_by_dev(struct device *dev) +{ + struct dca_provider *dca, *ret = NULL; + + list_for_each_entry(dca, &dca_providers, node) { + if ((!dev) || (dca->ops->dev_managed(dca, dev))) { + ret = dca; + break; + } + } + + return ret; +} /** * dca_add_requester - add a dca client to the list @@ -42,25 +58,39 @@ static struct dca_provider *global_dca = NULL; */ int dca_add_requester(struct device *dev) { - int err, slot; + struct dca_provider *dca; + int err, slot = -ENODEV; - if (!global_dca) - return -ENODEV; + if (!dev) + return -EFAULT; spin_lock(&dca_lock); - slot = global_dca->ops->add_requester(global_dca, dev); - spin_unlock(&dca_lock); - if (slot < 0) + + /* check if the requester has not been added already */ + dca = dca_find_provider_by_dev(dev); + if (dca) { + spin_unlock(&dca_lock); + return -EEXIST; + } + + list_for_each_entry(dca, &dca_providers, node) { + slot = dca->ops->add_requester(dca, dev); + if (slot >= 0) + break; + } + if (slot < 0) { + spin_unlock(&dca_lock); return slot; + } - err = dca_sysfs_add_req(global_dca, dev, slot); + err = dca_sysfs_add_req(dca, dev, slot); if (err) { - spin_lock(&dca_lock); - global_dca->ops->remove_requester(global_dca, dev); + dca->ops->remove_requester(dca, dev); spin_unlock(&dca_lock); return err; } + spin_unlock(&dca_lock); return 0; } EXPORT_SYMBOL_GPL(dca_add_requester); @@ -71,30 +101,78 @@ EXPORT_SYMBOL_GPL(dca_add_requester); */ int dca_remove_requester(struct device *dev) { + struct dca_provider *dca; int slot; - if (!global_dca) - return -ENODEV; + + if (!dev) + return -EFAULT; spin_lock(&dca_lock); - slot = global_dca->ops->remove_requester(global_dca, dev); - spin_unlock(&dca_lock); - if (slot < 0) + dca = dca_find_provider_by_dev(dev); + if (!dca) { + spin_unlock(&dca_lock); + return -ENODEV; + } + slot = dca->ops->remove_requester(dca, dev); + if (slot < 0) { + spin_unlock(&dca_lock); return slot; + } - dca_sysfs_remove_req(global_dca, slot); + dca_sysfs_remove_req(dca, slot); + + spin_unlock(&dca_lock); return 0; } EXPORT_SYMBOL_GPL(dca_remove_requester); /** - * dca_get_tag - return the dca tag for the given cpu + * dca_common_get_tag - return the dca tag (serves both new and old api) + * @dev - the device that wants dca service * @cpu - the cpuid as returned by get_cpu() */ -u8 dca_get_tag(int cpu) +u8 dca_common_get_tag(struct device *dev, int cpu) { - if (!global_dca) + struct dca_provider *dca; + u8 tag; + + spin_lock(&dca_lock); + + dca = dca_find_provider_by_dev(dev); + if (!dca) { + spin_unlock(&dca_lock); return -ENODEV; - return global_dca->ops->get_tag(global_dca, cpu); + } + tag = dca->ops->get_tag(dca, dev, cpu); + + spin_unlock(&dca_lock); + return tag; +} + +/** + * dca3_get_tag - return the dca tag to the requester device + * for the given cpu (new api) + * @dev - the device that wants dca service + * @cpu - the cpuid as returned by get_cpu() + */ +u8 dca3_get_tag(struct device *dev, int cpu) +{ + if (!dev) + return -EFAULT; + + return dca_common_get_tag(dev, cpu); +} +EXPORT_SYMBOL_GPL(dca3_get_tag); + +/** + * dca_get_tag - return the dca tag for the given cpu (old api) + * @cpu - the cpuid as returned by get_cpu() + */ +u8 dca_get_tag(int cpu) +{ + struct device *dev = NULL; + + return dca_common_get_tag(dev, cpu); } EXPORT_SYMBOL_GPL(dca_get_tag); @@ -140,12 +218,10 @@ int register_dca_provider(struct dca_provider *dca, struct device *dev) { int err; - if (global_dca) - return -EEXIST; err = dca_sysfs_add_provider(dca, dev); if (err) return err; - global_dca = dca; + list_add(&dca->node, &dca_providers); blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_ADD, NULL); return 0; @@ -158,11 +234,9 @@ EXPORT_SYMBOL_GPL(register_dca_provider); */ void unregister_dca_provider(struct dca_provider *dca) { - if (!global_dca) - return; blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_REMOVE, NULL); - global_dca = NULL; + list_del(&dca->node); dca_sysfs_remove_provider(dca); } EXPORT_SYMBOL_GPL(unregister_dca_provider); @@ -187,6 +261,7 @@ EXPORT_SYMBOL_GPL(dca_unregister_notify); static int __init dca_init(void) { + printk(KERN_ERR "dca service started, version %s\n", DCA_VERSION); return dca_sysfs_init(); } diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c index 011328faa5f2..3d47e9d8e34f 100644 --- a/drivers/dca/dca-sysfs.c +++ b/drivers/dca/dca-sysfs.c @@ -13,9 +13,10 @@ static spinlock_t dca_idr_lock; int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot) { struct device *cd; + static int req_count; cd = device_create(dca_class, dca->cd, MKDEV(0, slot + 1), - "requester%d", slot); + "requester%d", req_count++); if (IS_ERR(cd)) return PTR_ERR(cd); return 0; diff --git a/drivers/dma/ioat.c b/drivers/dma/ioat.c index 16e0fd8facfb..9b16a3af9a0a 100644 --- a/drivers/dma/ioat.c +++ b/drivers/dma/ioat.c @@ -47,6 +47,16 @@ static struct pci_device_id ioat_pci_tbl[] = { /* I/OAT v2 platforms */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB) }, + + /* I/OAT v3 platforms */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG4) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG5) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG6) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG7) }, { 0, } }; @@ -83,6 +93,11 @@ static int ioat_setup_functionality(struct pci_dev *pdev, void __iomem *iobase) if (device->dma && ioat_dca_enabled) device->dca = ioat2_dca_init(pdev, iobase); break; + case IOAT_VER_3_0: + device->dma = ioat_dma_probe(pdev, iobase); + if (device->dma && ioat_dca_enabled) + device->dca = ioat3_dca_init(pdev, iobase); + break; default: err = -ENODEV; break; diff --git a/drivers/dma/ioat_dca.c b/drivers/dma/ioat_dca.c index 9e922760b7ff..6cf622da0286 100644 --- a/drivers/dma/ioat_dca.c +++ b/drivers/dma/ioat_dca.c @@ -37,12 +37,18 @@ #include "ioatdma_registers.h" /* - * Bit 16 of a tag map entry is the "valid" bit, if it is set then bits 0:15 + * Bit 7 of a tag map entry is the "valid" bit, if it is set then bits 0:6 * contain the bit number of the APIC ID to map into the DCA tag. If the valid * bit is not set, then the value must be 0 or 1 and defines the bit in the tag. */ #define DCA_TAG_MAP_VALID 0x80 +#define DCA3_TAG_MAP_BIT_TO_INV 0x80 +#define DCA3_TAG_MAP_BIT_TO_SEL 0x40 +#define DCA3_TAG_MAP_LITERAL_VAL 0x1 + +#define DCA_TAG_MAP_MASK 0xDF + /* * "Legacy" DCA systems do not implement the DCA register set in the * I/OAT device. Software needs direct support for their tag mappings. @@ -95,6 +101,7 @@ struct ioat_dca_slot { }; #define IOAT_DCA_MAX_REQ 6 +#define IOAT3_DCA_MAX_REQ 2 struct ioat_dca_priv { void __iomem *iobase; @@ -171,7 +178,9 @@ static int ioat_dca_remove_requester(struct dca_provider *dca, return -ENODEV; } -static u8 ioat_dca_get_tag(struct dca_provider *dca, int cpu) +static u8 ioat_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) { struct ioat_dca_priv *ioatdca = dca_priv(dca); int i, apic_id, bit, value; @@ -193,10 +202,26 @@ static u8 ioat_dca_get_tag(struct dca_provider *dca, int cpu) return tag; } +static int ioat_dca_dev_managed(struct dca_provider *dca, + struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + + pdev = to_pci_dev(dev); + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == pdev) + return 1; + } + return 0; +} + static struct dca_ops ioat_dca_ops = { .add_requester = ioat_dca_add_requester, .remove_requester = ioat_dca_remove_requester, .get_tag = ioat_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, }; @@ -207,6 +232,8 @@ struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) u8 *tag_map = NULL; int i; int err; + u8 version; + u8 max_requesters; if (!system_has_dca_enabled(pdev)) return NULL; @@ -237,15 +264,20 @@ struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) if (tag_map == NULL) return NULL; + version = readb(iobase + IOAT_VER_OFFSET); + if (version == IOAT_VER_3_0) + max_requesters = IOAT3_DCA_MAX_REQ; + else + max_requesters = IOAT_DCA_MAX_REQ; + dca = alloc_dca_provider(&ioat_dca_ops, sizeof(*ioatdca) + - (sizeof(struct ioat_dca_slot) * IOAT_DCA_MAX_REQ)); + (sizeof(struct ioat_dca_slot) * max_requesters)); if (!dca) return NULL; ioatdca = dca_priv(dca); - ioatdca->max_requesters = IOAT_DCA_MAX_REQ; - + ioatdca->max_requesters = max_requesters; ioatdca->dca_base = iobase + 0x54; /* copy over the APIC ID to DCA tag mapping */ @@ -323,11 +355,13 @@ static int ioat2_dca_remove_requester(struct dca_provider *dca, return -ENODEV; } -static u8 ioat2_dca_get_tag(struct dca_provider *dca, int cpu) +static u8 ioat2_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) { u8 tag; - tag = ioat_dca_get_tag(dca, cpu); + tag = ioat_dca_get_tag(dca, dev, cpu); tag = (~tag) & 0x1F; return tag; } @@ -336,6 +370,7 @@ static struct dca_ops ioat2_dca_ops = { .add_requester = ioat2_dca_add_requester, .remove_requester = ioat2_dca_remove_requester, .get_tag = ioat2_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, }; static int ioat2_dca_count_dca_slots(void __iomem *iobase, u16 dca_offset) @@ -425,3 +460,198 @@ struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase) return dca; } + +static int ioat3_dca_add_requester(struct dca_provider *dca, struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + u16 id; + u16 global_req_table; + + /* This implementation only supports PCI-Express */ + if (dev->bus != &pci_bus_type) + return -ENODEV; + pdev = to_pci_dev(dev); + id = dcaid_from_pcidev(pdev); + + if (ioatdca->requester_count == ioatdca->max_requesters) + return -ENODEV; + + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == NULL) { + /* found an empty slot */ + ioatdca->requester_count++; + ioatdca->req_slots[i].pdev = pdev; + ioatdca->req_slots[i].rid = id; + global_req_table = + readw(ioatdca->dca_base + IOAT3_DCA_GREQID_OFFSET); + writel(id | IOAT_DCA_GREQID_VALID, + ioatdca->iobase + global_req_table + (i * 4)); + return i; + } + } + /* Error, ioatdma->requester_count is out of whack */ + return -EFAULT; +} + +static int ioat3_dca_remove_requester(struct dca_provider *dca, + struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + u16 global_req_table; + + /* This implementation only supports PCI-Express */ + if (dev->bus != &pci_bus_type) + return -ENODEV; + pdev = to_pci_dev(dev); + + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == pdev) { + global_req_table = + readw(ioatdca->dca_base + IOAT3_DCA_GREQID_OFFSET); + writel(0, ioatdca->iobase + global_req_table + (i * 4)); + ioatdca->req_slots[i].pdev = NULL; + ioatdca->req_slots[i].rid = 0; + ioatdca->requester_count--; + return i; + } + } + return -ENODEV; +} + +static u8 ioat3_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) +{ + u8 tag; + + struct ioat_dca_priv *ioatdca = dca_priv(dca); + int i, apic_id, bit, value; + u8 entry; + + tag = 0; + apic_id = cpu_physical_id(cpu); + + for (i = 0; i < IOAT_TAG_MAP_LEN; i++) { + entry = ioatdca->tag_map[i]; + if (entry & DCA3_TAG_MAP_BIT_TO_SEL) { + bit = entry & + ~(DCA3_TAG_MAP_BIT_TO_SEL | DCA3_TAG_MAP_BIT_TO_INV); + value = (apic_id & (1 << bit)) ? 1 : 0; + } else if (entry & DCA3_TAG_MAP_BIT_TO_INV) { + bit = entry & ~DCA3_TAG_MAP_BIT_TO_INV; + value = (apic_id & (1 << bit)) ? 0 : 1; + } else { + value = (entry & DCA3_TAG_MAP_LITERAL_VAL) ? 1 : 0; + } + tag |= (value << i); + } + + return tag; +} + +static struct dca_ops ioat3_dca_ops = { + .add_requester = ioat3_dca_add_requester, + .remove_requester = ioat3_dca_remove_requester, + .get_tag = ioat3_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, +}; + +static int ioat3_dca_count_dca_slots(void *iobase, u16 dca_offset) +{ + int slots = 0; + u32 req; + u16 global_req_table; + + global_req_table = readw(iobase + dca_offset + IOAT3_DCA_GREQID_OFFSET); + if (global_req_table == 0) + return 0; + + do { + req = readl(iobase + global_req_table + (slots * sizeof(u32))); + slots++; + } while ((req & IOAT_DCA_GREQID_LASTID) == 0); + + return slots; +} + +struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase) +{ + struct dca_provider *dca; + struct ioat_dca_priv *ioatdca; + int slots; + int i; + int err; + u16 dca_offset; + u16 csi_fsb_control; + u16 pcie_control; + u8 bit; + + union { + u64 full; + struct { + u32 low; + u32 high; + }; + } tag_map; + + if (!system_has_dca_enabled(pdev)) + return NULL; + + dca_offset = readw(iobase + IOAT_DCAOFFSET_OFFSET); + if (dca_offset == 0) + return NULL; + + slots = ioat3_dca_count_dca_slots(iobase, dca_offset); + if (slots == 0) + return NULL; + + dca = alloc_dca_provider(&ioat3_dca_ops, + sizeof(*ioatdca) + + (sizeof(struct ioat_dca_slot) * slots)); + if (!dca) + return NULL; + + ioatdca = dca_priv(dca); + ioatdca->iobase = iobase; + ioatdca->dca_base = iobase + dca_offset; + ioatdca->max_requesters = slots; + + /* some bios might not know to turn these on */ + csi_fsb_control = readw(ioatdca->dca_base + IOAT3_CSI_CONTROL_OFFSET); + if ((csi_fsb_control & IOAT3_CSI_CONTROL_PREFETCH) == 0) { + csi_fsb_control |= IOAT3_CSI_CONTROL_PREFETCH; + writew(csi_fsb_control, + ioatdca->dca_base + IOAT3_CSI_CONTROL_OFFSET); + } + pcie_control = readw(ioatdca->dca_base + IOAT3_PCI_CONTROL_OFFSET); + if ((pcie_control & IOAT3_PCI_CONTROL_MEMWR) == 0) { + pcie_control |= IOAT3_PCI_CONTROL_MEMWR; + writew(pcie_control, + ioatdca->dca_base + IOAT3_PCI_CONTROL_OFFSET); + } + + + /* TODO version, compatibility and configuration checks */ + + /* copy out the APIC to DCA tag map */ + tag_map.low = + readl(ioatdca->dca_base + IOAT3_APICID_TAG_MAP_OFFSET_LOW); + tag_map.high = + readl(ioatdca->dca_base + IOAT3_APICID_TAG_MAP_OFFSET_HIGH); + for (i = 0; i < 8; i++) { + bit = tag_map.full >> (8 * i); + ioatdca->tag_map[i] = bit & DCA_TAG_MAP_MASK; + } + + err = register_dca_provider(dca, &pdev->dev); + if (err) { + free_dca_provider(dca); + return NULL; + } + + return dca; +} diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index ece5a0e3a335..a52156e56886 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -53,6 +53,12 @@ MODULE_PARM_DESC(ioat_pending_level, static void ioat_dma_chan_reset_part2(struct work_struct *work); static void ioat_dma_chan_watchdog(struct work_struct *work); +/* + * workaround for IOAT ver.3.0 null descriptor issue + * (channel returns error when size is 0) + */ +#define NULL_DESC_BUFFER_SIZE 1 + /* internal functions */ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan); static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); @@ -129,6 +135,38 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) int i; struct ioat_dma_chan *ioat_chan; + /* + * IOAT ver.3 workarounds + */ + if (device->version == IOAT_VER_3_0) { + u32 chan_err_mask; + u16 dev_id; + u32 dmauncerrsts; + + /* + * Write CHANERRMSK_INT with 3E07h to mask out the errors + * that can cause stability issues for IOAT ver.3 + */ + chan_err_mask = 0x3E07; + pci_write_config_dword(device->pdev, + IOAT_PCI_CHANERRMASK_INT_OFFSET, + chan_err_mask); + + /* + * Clear DMAUNCERRSTS Cfg-Reg Parity Error status bit + * (workaround for spurious config parity error after restart) + */ + pci_read_config_word(device->pdev, + IOAT_PCI_DEVICE_ID_OFFSET, + &dev_id); + if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0) { + dmauncerrsts = 0x10; + pci_write_config_dword(device->pdev, + IOAT_PCI_DMAUNCERRSTS_OFFSET, + dmauncerrsts); + } + } + device->common.chancnt = readb(device->reg_base + IOAT_CHANCNT_OFFSET); xfercap_scale = readb(device->reg_base + IOAT_XFERCAP_OFFSET); xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); @@ -473,6 +511,13 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) prev = new; } while (len && (new = ioat1_dma_get_next_descriptor(ioat_chan))); + if (!new) { + dev_err(&ioat_chan->device->pdev->dev, + "tx submit failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return -ENOMEM; + } + hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; if (new->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; @@ -558,7 +603,14 @@ static dma_cookie_t ioat2_tx_submit(struct dma_async_tx_descriptor *tx) desc_count++; } while (len && (new = ioat2_dma_get_next_descriptor(ioat_chan))); - hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; + if (!new) { + dev_err(&ioat_chan->device->pdev->dev, + "tx submit failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return -ENOMEM; + } + + hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_CP_STS; if (new->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; if (first != new) { @@ -629,6 +681,7 @@ static struct ioat_desc_sw *ioat_dma_alloc_descriptor( desc_sw->async_tx.tx_submit = ioat1_tx_submit; break; case IOAT_VER_2_0: + case IOAT_VER_3_0: desc_sw->async_tx.tx_submit = ioat2_tx_submit; break; } @@ -779,6 +832,7 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) } break; case IOAT_VER_2_0: + case IOAT_VER_3_0: list_for_each_entry_safe(desc, _desc, ioat_chan->free_desc.next, node) { list_del(&desc->node); @@ -868,7 +922,8 @@ ioat2_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan) /* set up the noop descriptor */ noop_desc = to_ioat_desc(ioat_chan->used_desc.next); - noop_desc->hw->size = 0; + /* set size to non-zero value (channel returns error when size is 0) */ + noop_desc->hw->size = NULL_DESC_BUFFER_SIZE; noop_desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; noop_desc->hw->src_addr = 0; noop_desc->hw->dst_addr = 0; @@ -918,6 +973,7 @@ static struct ioat_desc_sw *ioat_dma_get_next_descriptor( return ioat1_dma_get_next_descriptor(ioat_chan); break; case IOAT_VER_2_0: + case IOAT_VER_3_0: return ioat2_dma_get_next_descriptor(ioat_chan); break; } @@ -1061,10 +1117,12 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) * perhaps we're stuck so hard that the watchdog can't go off? * try to catch it after 2 seconds */ - if (time_after(jiffies, - ioat_chan->last_completion_time + HZ*WATCHDOG_DELAY)) { - ioat_dma_chan_watchdog(&(ioat_chan->device->work.work)); - ioat_chan->last_completion_time = jiffies; + if (ioat_chan->device->version != IOAT_VER_3_0) { + if (time_after(jiffies, + ioat_chan->last_completion_time + HZ*WATCHDOG_DELAY)) { + ioat_dma_chan_watchdog(&(ioat_chan->device->work.work)); + ioat_chan->last_completion_time = jiffies; + } } return; } @@ -1120,6 +1178,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) } break; case IOAT_VER_2_0: + case IOAT_VER_3_0: /* has some other thread has already cleaned up? */ if (ioat_chan->used_desc.prev == NULL) break; @@ -1223,10 +1282,19 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) spin_lock_bh(&ioat_chan->desc_lock); desc = ioat_dma_get_next_descriptor(ioat_chan); + + if (!desc) { + dev_err(&ioat_chan->device->pdev->dev, + "Unable to start null desc - get next desc failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return; + } + desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL | IOAT_DMA_DESCRIPTOR_CTL_INT_GN | IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - desc->hw->size = 0; + /* set size to non-zero value (channel returns error when size is 0) */ + desc->hw->size = NULL_DESC_BUFFER_SIZE; desc->hw->src_addr = 0; desc->hw->dst_addr = 0; async_tx_ack(&desc->async_tx); @@ -1244,6 +1312,7 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); break; case IOAT_VER_2_0: + case IOAT_VER_3_0: writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); writel(((u64) desc->async_tx.phys) >> 32, @@ -1562,6 +1631,7 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, ioat1_dma_memcpy_issue_pending; break; case IOAT_VER_2_0: + case IOAT_VER_3_0: device->common.device_prep_dma_memcpy = ioat2_dma_prep_memcpy; device->common.device_issue_pending = ioat2_dma_memcpy_issue_pending; @@ -1585,9 +1655,11 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, dma_async_device_register(&device->common); - INIT_DELAYED_WORK(&device->work, ioat_dma_chan_watchdog); - schedule_delayed_work(&device->work, - WATCHDOG_DELAY); + if (device->version != IOAT_VER_3_0) { + INIT_DELAYED_WORK(&device->work, ioat_dma_chan_watchdog); + schedule_delayed_work(&device->work, + WATCHDOG_DELAY); + } return device; @@ -1621,7 +1693,9 @@ void ioat_dma_remove(struct ioatdma_device *device) pci_release_regions(device->pdev); pci_disable_device(device->pdev); - cancel_delayed_work(&device->work); + if (device->version != IOAT_VER_3_0) { + cancel_delayed_work(&device->work); + } list_for_each_entry_safe(chan, _chan, &device->common.channels, device_node) { diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h index 685adb62aa5a..a3306d0e1372 100644 --- a/drivers/dma/ioatdma.h +++ b/drivers/dma/ioatdma.h @@ -29,7 +29,7 @@ #include #include -#define IOAT_DMA_VERSION "2.18" +#define IOAT_DMA_VERSION "3.30" enum ioat_interrupt { none = 0, @@ -135,6 +135,7 @@ static inline void ioat_set_tcp_copy_break(struct ioatdma_device *dev) #ifdef CONFIG_NET_DMA switch (dev->version) { case IOAT_VER_1_2: + case IOAT_VER_3_0: sysctl_tcp_dma_copybreak = 4096; break; case IOAT_VER_2_0: @@ -150,11 +151,13 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, void ioat_dma_remove(struct ioatdma_device *device); struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase); +struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase); #else #define ioat_dma_probe(pdev, iobase) NULL #define ioat_dma_remove(device) do { } while (0) #define ioat_dca_init(pdev, iobase) NULL #define ioat2_dca_init(pdev, iobase) NULL +#define ioat3_dca_init(pdev, iobase) NULL #endif #endif /* IOATDMA_H */ diff --git a/drivers/dma/ioatdma_hw.h b/drivers/dma/ioatdma_hw.h index dd470fa91d86..f1ae2c776f74 100644 --- a/drivers/dma/ioatdma_hw.h +++ b/drivers/dma/ioatdma_hw.h @@ -35,6 +35,7 @@ #define IOAT_PCI_SID 0x8086 #define IOAT_VER_1_2 0x12 /* Version 1.2 */ #define IOAT_VER_2_0 0x20 /* Version 2.0 */ +#define IOAT_VER_3_0 0x30 /* Version 3.0 */ struct ioat_dma_descriptor { uint32_t size; diff --git a/drivers/dma/ioatdma_registers.h b/drivers/dma/ioatdma_registers.h index 9832d7ebd931..827cb503cac6 100644 --- a/drivers/dma/ioatdma_registers.h +++ b/drivers/dma/ioatdma_registers.h @@ -25,6 +25,10 @@ #define IOAT_PCI_DMACTRL_DMA_EN 0x00000001 #define IOAT_PCI_DMACTRL_MSI_EN 0x00000002 +#define IOAT_PCI_DEVICE_ID_OFFSET 0x02 +#define IOAT_PCI_DMAUNCERRSTS_OFFSET 0x148 +#define IOAT_PCI_CHANERRMASK_INT_OFFSET 0x184 + /* MMIO Device Registers */ #define IOAT_CHANCNT_OFFSET 0x00 /* 8-bit */ @@ -149,7 +153,23 @@ #define IOAT_DCA_GREQID_VALID 0x20000000 #define IOAT_DCA_GREQID_LASTID 0x80000000 +#define IOAT3_CSI_CAPABILITY_OFFSET 0x08 +#define IOAT3_CSI_CAPABILITY_PREFETCH 0x1 + +#define IOAT3_PCI_CAPABILITY_OFFSET 0x0A +#define IOAT3_PCI_CAPABILITY_MEMWR 0x1 + +#define IOAT3_CSI_CONTROL_OFFSET 0x0C +#define IOAT3_CSI_CONTROL_PREFETCH 0x1 + +#define IOAT3_PCI_CONTROL_OFFSET 0x0E +#define IOAT3_PCI_CONTROL_MEMWR 0x1 + +#define IOAT3_APICID_TAG_MAP_OFFSET 0x10 +#define IOAT3_APICID_TAG_MAP_OFFSET_LOW 0x10 +#define IOAT3_APICID_TAG_MAP_OFFSET_HIGH 0x14 +#define IOAT3_DCA_GREQID_OFFSET 0x02 #define IOAT1_CHAINADDR_OFFSET 0x0C /* 64-bit Descriptor Chain Address Register */ #define IOAT2_CHAINADDR_OFFSET 0x10 /* 64-bit Descriptor Chain Address Register */ diff --git a/include/linux/dca.h b/include/linux/dca.h index af61cd1f37e9..b00a753eda53 100644 --- a/include/linux/dca.h +++ b/include/linux/dca.h @@ -10,6 +10,7 @@ void dca_unregister_notify(struct notifier_block *nb); #define DCA_PROVIDER_REMOVE 0x0002 struct dca_provider { + struct list_head node; struct dca_ops *ops; struct device *cd; int id; @@ -18,7 +19,9 @@ struct dca_provider { struct dca_ops { int (*add_requester) (struct dca_provider *, struct device *); int (*remove_requester) (struct dca_provider *, struct device *); - u8 (*get_tag) (struct dca_provider *, int cpu); + u8 (*get_tag) (struct dca_provider *, struct device *, + int cpu); + int (*dev_managed) (struct dca_provider *, struct device *); }; struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size); @@ -32,9 +35,11 @@ static inline void *dca_priv(struct dca_provider *dca) } /* Requester API */ +#define DCA_GET_TAG_TWO_ARGS int dca_add_requester(struct device *dev); int dca_remove_requester(struct device *dev); u8 dca_get_tag(int cpu); +u8 dca3_get_tag(struct device *dev, int cpu); /* internal stuff */ int __init dca_sysfs_init(void); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9b940e644179..06a5b7ae79be 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2363,6 +2363,14 @@ #define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 #define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a +#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b +#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 #define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 #define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 #define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 -- cgit v1.2.3 From e14fa82439d33cef67eaafc1a48960bbfa610c8e Mon Sep 17 00:00:00 2001 From: Riku Voipio Date: Sat, 31 May 2008 14:43:41 +0100 Subject: leds: Add pca9532 led driver NXP pca9532 is a LED dimmer/controller attached to i2c bus. It allows attaching upto 16 leds which can either be on, off or dimmed and/or blinked with the two PWM modulators available. This driver is a "new-style" i2c driver that adheres to the driver model and implements the led framework api. Since the leds connected to the driver are platform specific, it is only useful when platform data is passed to the driver to define what leds are connected to which pins. Signed-off-by: Riku Voipio Signed-off-by: Andrew Morton Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-pca9532.c | 337 +++++++++++++++++++++++++++++++++++++++++++ include/linux/leds-pca9532.h | 45 ++++++ 4 files changed, 391 insertions(+) create mode 100644 drivers/leds/leds-pca9532.c create mode 100644 include/linux/leds-pca9532.h (limited to 'include/linux') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 86a369bc57d6..1c35dfaef721 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -103,6 +103,14 @@ config LEDS_HP6XX This option enables led support for the handheld HP Jornada 620/660/680/690. +config LEDS_PCA9532 + tristate "LED driver for PCA9532 dimmer" + depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL + help + This option enables support for NXP pca9532 + led controller. It is generally only usefull + as a platform driver + config LEDS_GPIO tristate "LED Support for GPIO connected LEDs" depends on LEDS_CLASS && GENERIC_GPIO diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 973d626f5f4a..7156f9970fa9 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o +obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c new file mode 100644 index 000000000000..4064d4f6b33b --- /dev/null +++ b/drivers/leds/leds-pca9532.c @@ -0,0 +1,337 @@ +/* + * pca9532.c - 16-bit Led dimmer + * + * Copyright (C) 2008 Riku Voipio + * + * 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; version 2 of the License. + * + * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf + * + */ + +#include +#include +#include +#include +#include +#include + +static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; +I2C_CLIENT_INSMOD_1(pca9532); + +#define PCA9532_REG_PSC(i) (0x2+(i)*2) +#define PCA9532_REG_PWM(i) (0x3+(i)*2) +#define PCA9532_REG_LS0 0x6 +#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0) +#define LED_NUM(led) (led & 0x3) + +#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) + +struct pca9532_data { + struct i2c_client *client; + struct pca9532_led leds[16]; + struct mutex update_lock; + struct input_dev *idev; + u8 pwm[2]; + u8 psc[2]; +}; + +static int pca9532_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int pca9532_remove(struct i2c_client *client); + +static const struct i2c_device_id pca9532_id[] = { + { "pca9532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, pca9532_id); + +static struct i2c_driver pca9532_driver = { + .driver = { + .name = "pca9532", + }, + .probe = pca9532_probe, + .remove = pca9532_remove, + .id_table = pca9532_id, +}; + +/* We have two pwm/blinkers, but 16 possible leds to drive. Additionaly, + * the clever Thecus people are using one pwm to drive the beeper. So, + * as a compromise we average one pwm to the values requested by all + * leds that are not ON/OFF. + * */ +static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, + enum led_brightness value) +{ + int a = 0, b = 0, i = 0; + struct pca9532_data *data = i2c_get_clientdata(client); + for (i = 0; i < 16; i++) { + if (data->leds[i].type == PCA9532_TYPE_LED && + data->leds[i].state == PCA9532_PWM0+pwm) { + a++; + b += data->leds[i].ldev.brightness; + } + } + if (a == 0) { + dev_err(&client->dev, + "fear of division by zero %d/%d, wanted %d\n", + b, a, value); + return -EINVAL; + } + b = b/a; + if (b > 0xFF) + return -EINVAL; + mutex_lock(&data->update_lock); + data->pwm[pwm] = b; + i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), + data->pwm[pwm]); + data->psc[pwm] = blink; + i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), + data->psc[pwm]); + mutex_unlock(&data->update_lock); + return 0; +} + +/* Set LED routing */ +static void pca9532_setled(struct pca9532_led *led) +{ + struct i2c_client *client = led->client; + struct pca9532_data *data = i2c_get_clientdata(client); + char reg; + + mutex_lock(&data->update_lock); + reg = i2c_smbus_read_byte_data(client, LED_REG(led->id)); + /* zero led bits */ + reg = reg & ~(0x3<id)*2); + /* set the new value */ + reg = reg | (led->state << LED_NUM(led->id)*2); + i2c_smbus_write_byte_data(client, LED_REG(led->id), reg); + mutex_unlock(&data->update_lock); +} + +static void pca9532_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + int err = 0; + struct pca9532_led *led = ldev_to_led(led_cdev); + + if (value == LED_OFF) + led->state = PCA9532_OFF; + else if (value == LED_FULL) + led->state = PCA9532_ON; + else { + led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ + err = pca9532_setpwm(led->client, 0, 0, value); + if (err) + return; /* XXX: led api doesn't allow error code? */ + } + pca9532_setled(led); +} + +static int pca9532_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct pca9532_led *led = ldev_to_led(led_cdev); + struct i2c_client *client = led->client; + int psc; + + if (*delay_on == 0 && *delay_off == 0) { + /* led subsystem ask us for a blink rate */ + *delay_on = 1000; + *delay_off = 1000; + } + if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) + return -EINVAL; + + /* Thecus specific: only use PSC/PWM 0 */ + psc = (*delay_on * 152-1)/1000; + return pca9532_setpwm(client, 0, psc, led_cdev->brightness); +} + +int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, + int value) +{ + struct pca9532_data *data = input_get_drvdata(dev); + + if (type != EV_SND && (code != SND_BELL || code != SND_TONE)) + return -1; + + /* XXX: allow different kind of beeps with psc/pwm modifications */ + if (value > 1 && value < 32767) + data->pwm[1] = 127; + else + data->pwm[1] = 0; + + dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), + data->pwm[1]); + mutex_unlock(&data->update_lock); + + return 0; +} + +static int pca9532_configure(struct i2c_client *client, + struct pca9532_data *data, struct pca9532_platform_data *pdata) +{ + int i, err = 0; + + for (i = 0; i < 2; i++) { + data->pwm[i] = pdata->pwm[i]; + data->psc[i] = pdata->psc[i]; + i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i), + data->pwm[i]); + i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i), + data->psc[i]); + } + + for (i = 0; i < 16; i++) { + struct pca9532_led *led = &data->leds[i]; + struct pca9532_led *pled = &pdata->leds[i]; + led->client = client; + led->id = i; + led->type = pled->type; + switch (led->type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led->state = pled->state; + led->name = pled->name; + led->ldev.name = led->name; + led->ldev.brightness = LED_OFF; + led->ldev.brightness_set = pca9532_set_brightness; + led->ldev.blink_set = pca9532_set_blink; + if (led_classdev_register(&client->dev, + &led->ldev) < 0) { + dev_err(&client->dev, + "couldn't register LED %s\n", + led->name); + goto exit; + } + pca9532_setled(led); + break; + case PCA9532_TYPE_N2100_BEEP: + BUG_ON(data->idev); + led->state = PCA9532_PWM1; + pca9532_setled(led); + data->idev = input_allocate_device(); + if (data->idev == NULL) { + err = -ENOMEM; + goto exit; + } + data->idev->name = pled->name; + data->idev->phys = "i2c/pca9532"; + data->idev->id.bustype = BUS_HOST; + data->idev->id.vendor = 0x001f; + data->idev->id.product = 0x0001; + data->idev->id.version = 0x0100; + data->idev->evbit[0] = BIT_MASK(EV_SND); + data->idev->sndbit[0] = BIT_MASK(SND_BELL) | + BIT_MASK(SND_TONE); + data->idev->event = pca9532_event; + input_set_drvdata(data->idev, data); + err = input_register_device(data->idev); + if (err) { + input_free_device(data->idev); + data->idev = NULL; + goto exit; + } + break; + } + } + return 0; + +exit: + if (i > 0) + for (i = i - 1; i >= 0; i--) + switch (data->leds[i].type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led_classdev_unregister(&data->leds[i].ldev); + break; + case PCA9532_TYPE_N2100_BEEP: + if (data->idev != NULL) { + input_unregister_device(data->idev); + input_free_device(data->idev); + data->idev = NULL; + } + break; + } + + return err; + +} + +static int pca9532_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_info(&client->dev, "setting platform data\n"); + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->update_lock); + + if (pca9532_pdata == NULL) + return -EIO; + + pca9532_configure(client, data, pca9532_pdata); + return 0; + +} + +static int pca9532_remove(struct i2c_client *client) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + int i; + for (i = 0; i < 16; i++) + switch (data->leds[i].type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led_classdev_unregister(&data->leds[i].ldev); + break; + case PCA9532_TYPE_N2100_BEEP: + if (data->idev != NULL) { + input_unregister_device(data->idev); + input_free_device(data->idev); + data->idev = NULL; + } + break; + } + + kfree(data); + i2c_set_clientdata(client, NULL); + return 0; +} + +static int __init pca9532_init(void) +{ + return i2c_add_driver(&pca9532_driver); +} + +static void __exit pca9532_exit(void) +{ + i2c_del_driver(&pca9532_driver); +} + +MODULE_AUTHOR("Riku Voipio "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCA 9532 LED dimmer"); + +module_init(pca9532_init); +module_exit(pca9532_exit); + diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h new file mode 100644 index 000000000000..81b4207deb95 --- /dev/null +++ b/include/linux/leds-pca9532.h @@ -0,0 +1,45 @@ +/* + * pca9532.h - platform data structure for pca9532 led controller + * + * Copyright (C) 2008 Riku Voipio + * + * 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; version 2 of the License. + * + * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf + * + */ + +#ifndef __LINUX_PCA9532_H +#define __LINUX_PCA9532_H + +#include + +enum pca9532_state { + PCA9532_OFF = 0x0, + PCA9532_ON = 0x1, + PCA9532_PWM0 = 0x2, + PCA9532_PWM1 = 0x3 +}; + +enum pca9532_type { PCA9532_TYPE_NONE, PCA9532_TYPE_LED, + PCA9532_TYPE_N2100_BEEP }; + +struct pca9532_led { + u8 id; + struct i2c_client *client; + char *name; + struct led_classdev ldev; + enum pca9532_type type; + enum pca9532_state state; +}; + +struct pca9532_platform_data { + struct pca9532_led leds[16]; + u8 pwm[2]; + u8 psc[2]; +}; + +#endif /* __LINUX_PCA9532_H */ + -- cgit v1.2.3 From 781a54e7664cc0089287a90d27086e9656ac68a1 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 31 May 2008 15:23:19 +0100 Subject: leds: mark led_classdev.default_trigger as const LED classdev core doesn't modify memory pointed by the default_trigger, so mark it as const and we'll able to pass const char *s without getting compiler warnings. Signed-off-by: Anton Vorontsov Signed-off-by: Richard Purdie --- include/linux/leds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/leds.h b/include/linux/leds.h index 519df72e939d..e7a5e89932fe 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -48,7 +48,7 @@ struct led_classdev { struct device *dev; struct list_head node; /* LED Device list */ - char *default_trigger; /* Trigger to use */ + const char *default_trigger; /* Trigger to use */ #ifdef CONFIG_LEDS_TRIGGERS /* Protects the trigger data below */ -- cgit v1.2.3 From f46e9203d9a100bae216cc06e17f2e77351aa8d8 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Wed, 16 Jul 2008 22:49:55 +0100 Subject: leds: Add support for Philips PCA955x I2C LED drivers This driver supports the PCA9550, PCA9551, PCA9552, and PCA9553 LED driver chips. Signed-off-by: Nate Case Signed-off-by: Andrew Morton Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-pca955x.c | 384 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/leds.h | 14 ++ 4 files changed, 407 insertions(+) create mode 100644 drivers/leds/leds-pca955x.c (limited to 'include/linux') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1c35dfaef721..9556262dda5a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -155,6 +155,14 @@ config LEDS_CLEVO_MAIL To compile this driver as a module, choose M here: the module will be called leds-clevo-mail. +config LEDS_PCA955X + tristate "LED Support for PCA955x I2C chips" + depends on LEDS_CLASS && I2C + help + This option enables support for LEDs connected to PCA955x + LED driver chips accessed via the I2C bus. Supported + devices include PCA9550, PCA9551, PCA9552, and PCA9553. + comment "LED Triggers" config LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 7156f9970fa9..ff7982b44565 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o +obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c new file mode 100644 index 000000000000..146c06972863 --- /dev/null +++ b/drivers/leds/leds-pca955x.c @@ -0,0 +1,384 @@ +/* + * Copyright 2007-2008 Extreme Engineering Solutions, Inc. + * + * Author: Nate Case + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * LED driver for various PCA955x I2C LED drivers + * + * Supported devices: + * + * Device Description 7-bit slave address + * ------ ----------- ------------------- + * PCA9550 2-bit driver 0x60 .. 0x61 + * PCA9551 8-bit driver 0x60 .. 0x67 + * PCA9552 16-bit driver 0x60 .. 0x67 + * PCA9553/01 4-bit driver 0x62 + * PCA9553/02 4-bit driver 0x63 + * + * Philips PCA955x LED driver chips follow a register map as shown below: + * + * Control Register Description + * ---------------- ----------- + * 0x0 Input register 0 + * .. + * NUM_INPUT_REGS - 1 Last Input register X + * + * NUM_INPUT_REGS Frequency prescaler 0 + * NUM_INPUT_REGS + 1 PWM register 0 + * NUM_INPUT_REGS + 2 Frequency prescaler 1 + * NUM_INPUT_REGS + 3 PWM register 1 + * + * NUM_INPUT_REGS + 4 LED selector 0 + * NUM_INPUT_REGS + 4 + * + NUM_LED_REGS - 1 Last LED selector + * + * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many + * bits the chip supports. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* LED select registers determine the source that drives LED outputs */ +#define PCA955X_LS_LED_ON 0x0 /* Output LOW */ +#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ +#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */ +#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */ + +enum pca955x_type { + pca9550, + pca9551, + pca9552, + pca9553, +}; + +struct pca955x_chipdef { + int bits; + u8 slv_addr; /* 7-bit slave address mask */ + int slv_addr_shift; /* Number of bits to ignore */ +}; + +static struct pca955x_chipdef pca955x_chipdefs[] = { + [pca9550] = { + .bits = 2, + .slv_addr = /* 110000x */ 0x60, + .slv_addr_shift = 1, + }, + [pca9551] = { + .bits = 8, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9552] = { + .bits = 16, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9553] = { + .bits = 4, + .slv_addr = /* 110001x */ 0x62, + .slv_addr_shift = 1, + }, +}; + +static const struct i2c_device_id pca955x_id[] = { + { "pca9550", pca9550 }, + { "pca9551", pca9551 }, + { "pca9552", pca9552 }, + { "pca9553", pca9553 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca955x_id); + +struct pca955x_led { + struct pca955x_chipdef *chipdef; + struct i2c_client *client; + struct work_struct work; + spinlock_t lock; + enum led_brightness brightness; + struct led_classdev led_cdev; + int led_num; /* 0 .. 15 potentially */ + char name[32]; +}; + +/* 8 bits per input register */ +static inline int pca95xx_num_input_regs(int bits) +{ + return (bits + 7) / 8; +} + +/* 4 bits per LED selector register */ +static inline int pca95xx_num_led_regs(int bits) +{ + return (bits + 3) / 4; +} + +/* + * Return an LED selector register value based on an existing one, with + * the appropriate 2-bit state value set for the given LED number (0-3). + */ +static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) +{ + return (oldval & (~(0x3 << (led_num << 1)))) | + ((state & 0x3) << (led_num << 1)); +} + +/* + * Write to frequency prescaler register, used to program the + * period of the PWM output. period = (PSCx + 1) / 38 + */ +static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, + val); +} + +/* + * Write to PWM register, which determines the duty cycle of the + * output. LED is OFF when the count is less than the value of this + * register, and ON when it is greater. If PWMx == 0, LED is always OFF. + * + * Duty cycle is (256 - PWMx) / 256 + */ +static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, + val); +} + +/* + * Write to LED selector register, which determines the source that + * drives the LED output. + */ +static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, + val); +} + +/* + * Read the LED selector register, which determines the source that + * drives the LED output. + */ +static u8 pca955x_read_ls(struct i2c_client *client, int n) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + return (u8) i2c_smbus_read_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); +} + +static void pca955x_led_work(struct work_struct *work) +{ + struct pca955x_led *pca955x; + u8 ls; + int chip_ls; /* which LSx to use (0-3 potentially) */ + int ls_led; /* which set of bits within LSx to use (0-3) */ + + pca955x = container_of(work, struct pca955x_led, work); + chip_ls = pca955x->led_num / 4; + ls_led = pca955x->led_num % 4; + + ls = pca955x_read_ls(pca955x->client, chip_ls); + + switch (pca955x->brightness) { + case LED_FULL: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); + break; + case LED_OFF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF); + break; + case LED_HALF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0); + break; + default: + /* + * Use PWM1 for all other values. This has the unwanted + * side effect of making all LEDs on the chip share the + * same brightness level if set to a value other than + * OFF, HALF, or FULL. But, this is probably better than + * just turning off for all other values. + */ + pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness); + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); + break; + } + + pca955x_write_ls(pca955x->client, chip_ls, ls); +} + +void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + struct pca955x_led *pca955x; + + pca955x = container_of(led_cdev, struct pca955x_led, led_cdev); + + spin_lock(&pca955x->lock); + pca955x->brightness = value; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca955x->work); + + spin_unlock(&pca955x->lock); +} + +static int __devinit pca955x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca955x_led *pca955x; + int i; + int err = -ENODEV; + struct pca955x_chipdef *chip; + struct i2c_adapter *adapter; + struct led_platform_data *pdata; + + chip = &pca955x_chipdefs[id->driver_data]; + adapter = to_i2c_adapter(client->dev.parent); + pdata = client->dev.platform_data; + + /* Make sure the slave address / chip type combo given is possible */ + if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != + chip->slv_addr) { + dev_err(&client->dev, "invalid slave address %02x\n", + client->addr); + return -ENODEV; + } + + printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at " + "slave address 0x%02x\n", + id->name, chip->bits, client->addr); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + if (pdata) { + if (pdata->num_leds != chip->bits) { + dev_err(&client->dev, "board info claims %d LEDs" + " on a %d-bit chip\n", + pdata->num_leds, chip->bits); + return -ENODEV; + } + } + + for (i = 0; i < chip->bits; i++) { + pca955x = kzalloc(sizeof(struct pca955x_led), GFP_KERNEL); + if (!pca955x) { + err = -ENOMEM; + goto exit; + } + + pca955x->chipdef = chip; + pca955x->client = client; + pca955x->led_num = i; + /* Platform data can specify LED names and default triggers */ + if (pdata) { + if (pdata->leds[i].name) + snprintf(pca955x->name, 32, "pca955x:%s", + pdata->leds[i].name); + if (pdata->leds[i].default_trigger) + pca955x->led_cdev.default_trigger = + pdata->leds[i].default_trigger; + } else { + snprintf(pca955x->name, 32, "pca955x:%d", i); + } + spin_lock_init(&pca955x->lock); + + pca955x->led_cdev.name = pca955x->name; + pca955x->led_cdev.brightness_set = + pca955x_led_set; + + /* + * Client data is a pointer to the _first_ pca955x_led + * struct + */ + if (i == 0) + i2c_set_clientdata(client, pca955x); + + INIT_WORK(&(pca955x->work), pca955x_led_work); + + led_classdev_register(&client->dev, &(pca955x->led_cdev)); + } + + /* Turn off LEDs */ + for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) + pca955x_write_ls(client, i, 0x55); + + /* PWM0 is used for half brightness or 50% duty cycle */ + pca955x_write_pwm(client, 0, 255-LED_HALF); + + /* PWM1 is used for variable brightness, default to OFF */ + pca955x_write_pwm(client, 1, 0); + + /* Set to fast frequency so we do not see flashing */ + pca955x_write_psc(client, 0, 0); + pca955x_write_psc(client, 1, 0); + + return 0; +exit: + return err; +} + +static int __devexit pca955x_remove(struct i2c_client *client) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + int leds = pca955x->chipdef->bits; + int i; + + for (i = 0; i < leds; i++) { + led_classdev_unregister(&(pca955x->led_cdev)); + cancel_work_sync(&(pca955x->work)); + kfree(pca955x); + pca955x = pca955x + 1; + } + + return 0; +} + +static struct i2c_driver pca955x_driver = { + .driver = { + .name = "leds-pca955x", + .owner = THIS_MODULE, + }, + .probe = pca955x_probe, + .remove = __devexit_p(pca955x_remove), + .id_table = pca955x_id, +}; + +static int __init pca955x_leds_init(void) +{ + return i2c_add_driver(&pca955x_driver); +} + +static void __exit pca955x_leds_exit(void) +{ + i2c_del_driver(&pca955x_driver); +} + +module_init(pca955x_leds_init); +module_exit(pca955x_leds_exit); + +MODULE_AUTHOR("Nate Case "); +MODULE_DESCRIPTION("PCA955x LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/leds.h b/include/linux/leds.h index e7a5e89932fe..d41ccb56146a 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -118,6 +118,20 @@ extern void ledtrig_ide_activity(void); #define ledtrig_ide_activity() do {} while(0) #endif +/* + * Generic LED platform data for describing LED names and default triggers. + */ +struct led_info { + const char *name; + char *default_trigger; + int flags; +}; + +struct led_platform_data { + int num_leds; + struct led_info *leds; +}; + /* For the leds-gpio driver */ struct gpio_led { const char *name; -- cgit v1.2.3 From 137d3edb48425f82a6a4226b664f90ed5e42eea5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 19 Jul 2008 23:03:35 +0900 Subject: sg: reimplement sg mapping iterator This is alternative implementation of sg content iterator introduced by commit 83e7d317... from Pierre Ossman in next-20080716. As there's already an sg iterator which iterates over sg entries themselves, name this sg_mapping_iterator. Slightly edited description from the original implementation follows. Iteration over a sg list is not that trivial when you take into account that memory pages might have to be mapped before being used. Unfortunately, that means that some parts of the kernel restrict themselves to directly accesible memory just to not have to deal with the mess. This patch adds a simple iterator system that allows any code to easily traverse an sg list and not have to deal with all the details. The user can decide to consume part of the iteration. Also, iteration can be stopped and resumed later if releasing the kmap between iteration steps is necessary. These features are useful to implement piecemeal sg copying for interrupt drive PIO for example. Signed-off-by: Tejun Heo Signed-off-by: Pierre Ossman --- include/linux/scatterlist.h | 38 ++++++++++ lib/scatterlist.c | 176 ++++++++++++++++++++++++++++++++------------ 2 files changed, 168 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 71fc81360048..e5996984ddd0 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -224,4 +224,42 @@ size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents, */ #define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) + +/* + * Mapping sg iterator + * + * Iterates over sg entries mapping page-by-page. On each successful + * iteration, @miter->page points to the mapped page and + * @miter->length bytes of data can be accessed at @miter->addr. As + * long as an interation is enclosed between start and stop, the user + * is free to choose control structure and when to stop. + * + * @miter->consumed is set to @miter->length on each iteration. It + * can be adjusted if the user can't consume all the bytes in one go. + * Also, a stopped iteration can be resumed by calling next on it. + * This is useful when iteration needs to release all resources and + * continue later (e.g. at the next interrupt). + */ + +#define SG_MITER_ATOMIC (1 << 0) /* use kmap_atomic */ + +struct sg_mapping_iter { + /* the following three fields can be accessed directly */ + struct page *page; /* currently mapped page */ + void *addr; /* pointer to the mapped area */ + size_t length; /* length of the mapped area */ + size_t consumed; /* number of consumed bytes */ + + /* these are internal states, keep away */ + struct scatterlist *__sg; /* current entry */ + unsigned int __nents; /* nr of remaining entries */ + unsigned int __offset; /* offset within sg */ + unsigned int __flags; +}; + +void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, + unsigned int nents, unsigned int flags); +bool sg_miter_next(struct sg_mapping_iter *miter); +void sg_miter_stop(struct sg_mapping_iter *miter); + #endif /* _LINUX_SCATTERLIST_H */ diff --git a/lib/scatterlist.c b/lib/scatterlist.c index b80c21100d78..876ba6d5b670 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -294,6 +294,117 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) } EXPORT_SYMBOL(sg_alloc_table); +/** + * sg_miter_start - start mapping iteration over a sg list + * @miter: sg mapping iter to be started + * @sgl: sg list to iterate over + * @nents: number of sg entries + * + * Description: + * Starts mapping iterator @miter. + * + * Context: + * Don't care. + */ +void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, + unsigned int nents, unsigned int flags) +{ + memset(miter, 0, sizeof(struct sg_mapping_iter)); + + miter->__sg = sgl; + miter->__nents = nents; + miter->__offset = 0; + miter->__flags = flags; +} +EXPORT_SYMBOL(sg_miter_start); + +/** + * sg_miter_next - proceed mapping iterator to the next mapping + * @miter: sg mapping iter to proceed + * + * Description: + * Proceeds @miter@ to the next mapping. @miter@ should have been + * started using sg_miter_start(). On successful return, + * @miter@->page, @miter@->addr and @miter@->length point to the + * current mapping. + * + * Context: + * IRQ disabled if SG_MITER_ATOMIC. IRQ must stay disabled till + * @miter@ is stopped. May sleep if !SG_MITER_ATOMIC. + * + * Returns: + * true if @miter contains the next mapping. false if end of sg + * list is reached. + */ +bool sg_miter_next(struct sg_mapping_iter *miter) +{ + unsigned int off, len; + + /* check for end and drop resources from the last iteration */ + if (!miter->__nents) + return false; + + sg_miter_stop(miter); + + /* get to the next sg if necessary. __offset is adjusted by stop */ + if (miter->__offset == miter->__sg->length && --miter->__nents) { + miter->__sg = sg_next(miter->__sg); + miter->__offset = 0; + } + + /* map the next page */ + off = miter->__sg->offset + miter->__offset; + len = miter->__sg->length - miter->__offset; + + miter->page = nth_page(sg_page(miter->__sg), off >> PAGE_SHIFT); + off &= ~PAGE_MASK; + miter->length = min_t(unsigned int, len, PAGE_SIZE - off); + miter->consumed = miter->length; + + if (miter->__flags & SG_MITER_ATOMIC) + miter->addr = kmap_atomic(miter->page, KM_BIO_SRC_IRQ) + off; + else + miter->addr = kmap(miter->page) + off; + + return true; +} +EXPORT_SYMBOL(sg_miter_next); + +/** + * sg_miter_stop - stop mapping iteration + * @miter: sg mapping iter to be stopped + * + * Description: + * Stops mapping iterator @miter. @miter should have been started + * started using sg_miter_start(). A stopped iteration can be + * resumed by calling sg_miter_next() on it. This is useful when + * resources (kmap) need to be released during iteration. + * + * Context: + * IRQ disabled if the SG_MITER_ATOMIC is set. Don't care otherwise. + */ +void sg_miter_stop(struct sg_mapping_iter *miter) +{ + WARN_ON(miter->consumed > miter->length); + + /* drop resources from the last iteration */ + if (miter->addr) { + miter->__offset += miter->consumed; + + if (miter->__flags & SG_MITER_ATOMIC) { + WARN_ON(!irqs_disabled()); + kunmap_atomic(miter->addr, KM_BIO_SRC_IRQ); + } else + kunmap(miter->addr); + + miter->page = NULL; + miter->addr = NULL; + miter->length = 0; + miter->consumed = 0; + } +} +EXPORT_SYMBOL(sg_miter_stop); + /** * sg_copy_buffer - Copy data between a linear buffer and an SG list * @sgl: The SG list @@ -309,56 +420,29 @@ EXPORT_SYMBOL(sg_alloc_table); static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, size_t buflen, int to_buffer) { - struct scatterlist *sg; - size_t buf_off = 0; - int i; - - WARN_ON(!irqs_disabled()); - - for_each_sg(sgl, sg, nents, i) { - struct page *page; - int n = 0; - unsigned int sg_off = sg->offset; - unsigned int sg_copy = sg->length; - - if (sg_copy > buflen) - sg_copy = buflen; - buflen -= sg_copy; - - while (sg_copy > 0) { - unsigned int page_copy; - void *p; - - page_copy = PAGE_SIZE - sg_off; - if (page_copy > sg_copy) - page_copy = sg_copy; - - page = nth_page(sg_page(sg), n); - p = kmap_atomic(page, KM_BIO_SRC_IRQ); - - if (to_buffer) - memcpy(buf + buf_off, p + sg_off, page_copy); - else { - memcpy(p + sg_off, buf + buf_off, page_copy); - flush_kernel_dcache_page(page); - } - - kunmap_atomic(p, KM_BIO_SRC_IRQ); - - buf_off += page_copy; - sg_off += page_copy; - if (sg_off == PAGE_SIZE) { - sg_off = 0; - n++; - } - sg_copy -= page_copy; + unsigned int offset = 0; + struct sg_mapping_iter miter; + + sg_miter_start(&miter, sgl, nents, SG_MITER_ATOMIC); + + while (sg_miter_next(&miter) && offset < buflen) { + unsigned int len; + + len = min(miter.length, buflen - offset); + + if (to_buffer) + memcpy(buf + offset, miter.addr, len); + else { + memcpy(miter.addr, buf + offset, len); + flush_kernel_dcache_page(miter.page); } - if (!buflen) - break; + offset += len; } - return buf_off; + sg_miter_stop(&miter); + + return offset; } /** -- cgit v1.2.3 From f472f80034ae5d0be7d7196c7e65d0ca8890afb4 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Thu, 19 Jun 2008 11:51:04 +0200 Subject: HID: add n-trig digitizer usage This adds a hid usage that is reported by the N-Trig digitizer in the Dell Latitude XT screen. Signed-off-by: Rafi Rubin Signed-off-by: Vojtech Pavlik Signed-off-by: Jiri Kosina --- include/linux/hid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hid.h b/include/linux/hid.h index fe56b86f2c67..ac4e678a04ed 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -512,7 +512,7 @@ struct hid_descriptor { /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ -#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001)) +#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) /* HID core API */ -- cgit v1.2.3 From 95d04f0735b4fc837bff9aedcc3f3efb20ddc3d1 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 23 Jul 2008 08:12:26 -0700 Subject: IB/mlx4: Add support for memory management extensions and local DMA L_Key Add support for the following operations to mlx4 when device firmware supports them: - Send with invalidate and local invalidate send queue work requests; - Allocate/free fast register MRs; - Allocate/free fast register MR page lists; - Fast register MR send queue work requests; - Local DMA L_Key. Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/cq.c | 12 ++++++ drivers/infiniband/hw/mlx4/main.c | 11 ++++++ drivers/infiniband/hw/mlx4/mlx4_ib.h | 15 ++++++++ drivers/infiniband/hw/mlx4/mr.c | 70 +++++++++++++++++++++++++++++++++++ drivers/infiniband/hw/mlx4/qp.c | 72 +++++++++++++++++++++++++++++++++--- drivers/net/mlx4/fw.c | 10 ++--- drivers/net/mlx4/fw.h | 2 +- drivers/net/mlx4/main.c | 2 + drivers/net/mlx4/mr.c | 23 +++++++++--- include/linux/mlx4/device.h | 10 +++++ include/linux/mlx4/qp.h | 16 ++++++-- 11 files changed, 221 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index 299f20832ab6..0b191a4842ce 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -637,6 +637,7 @@ repoll: case MLX4_OPCODE_SEND_IMM: wc->wc_flags |= IB_WC_WITH_IMM; case MLX4_OPCODE_SEND: + case MLX4_OPCODE_SEND_INVAL: wc->opcode = IB_WC_SEND; break; case MLX4_OPCODE_RDMA_READ: @@ -657,6 +658,12 @@ repoll: case MLX4_OPCODE_LSO: wc->opcode = IB_WC_LSO; break; + case MLX4_OPCODE_FMR: + wc->opcode = IB_WC_FAST_REG_MR; + break; + case MLX4_OPCODE_LOCAL_INVAL: + wc->opcode = IB_WC_LOCAL_INV; + break; } } else { wc->byte_len = be32_to_cpu(cqe->byte_cnt); @@ -667,6 +674,11 @@ repoll: wc->wc_flags = IB_WC_WITH_IMM; wc->ex.imm_data = cqe->immed_rss_invalid; break; + case MLX4_RECV_OPCODE_SEND_INVAL: + wc->opcode = IB_WC_RECV; + wc->wc_flags = IB_WC_WITH_INVALIDATE; + wc->ex.invalidate_rkey = be32_to_cpu(cqe->immed_rss_invalid); + break; case MLX4_RECV_OPCODE_SEND: wc->opcode = IB_WC_RECV; wc->wc_flags = 0; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index bcf50648fa18..38d6907ab521 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -104,6 +104,12 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_UD_IP_CSUM; if (dev->dev->caps.max_gso_sz) props->device_cap_flags |= IB_DEVICE_UD_TSO; + if (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_RESERVED_LKEY) + props->device_cap_flags |= IB_DEVICE_LOCAL_DMA_LKEY; + if ((dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_LOCAL_INV) && + (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_REMOTE_INV) && + (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_FAST_REG_WR)) + props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS; props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) & 0xffffff; @@ -127,6 +133,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs; props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1; props->max_srq_sge = dev->dev->caps.max_srq_sge; + props->max_fast_reg_page_list_len = PAGE_SIZE / sizeof (u64); props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay; props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ? IB_ATOMIC_HCA : IB_ATOMIC_NONE; @@ -565,6 +572,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX); ibdev->ib_dev.owner = THIS_MODULE; ibdev->ib_dev.node_type = RDMA_NODE_IB_CA; + ibdev->ib_dev.local_dma_lkey = dev->caps.reserved_lkey; ibdev->ib_dev.phys_port_cnt = dev->caps.num_ports; ibdev->ib_dev.num_comp_vectors = 1; ibdev->ib_dev.dma_device = &dev->pdev->dev; @@ -627,6 +635,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->ib_dev.get_dma_mr = mlx4_ib_get_dma_mr; ibdev->ib_dev.reg_user_mr = mlx4_ib_reg_user_mr; ibdev->ib_dev.dereg_mr = mlx4_ib_dereg_mr; + ibdev->ib_dev.alloc_fast_reg_mr = mlx4_ib_alloc_fast_reg_mr; + ibdev->ib_dev.alloc_fast_reg_page_list = mlx4_ib_alloc_fast_reg_page_list; + ibdev->ib_dev.free_fast_reg_page_list = mlx4_ib_free_fast_reg_page_list; ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach; ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach; ibdev->ib_dev.process_mad = mlx4_ib_process_mad; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index c4cf5b69eefa..d26a91317d4d 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -83,6 +83,11 @@ struct mlx4_ib_mr { struct ib_umem *umem; }; +struct mlx4_ib_fast_reg_page_list { + struct ib_fast_reg_page_list ibfrpl; + dma_addr_t map; +}; + struct mlx4_ib_fmr { struct ib_fmr ibfmr; struct mlx4_fmr mfmr; @@ -199,6 +204,11 @@ static inline struct mlx4_ib_mr *to_mmr(struct ib_mr *ibmr) return container_of(ibmr, struct mlx4_ib_mr, ibmr); } +static inline struct mlx4_ib_fast_reg_page_list *to_mfrpl(struct ib_fast_reg_page_list *ibfrpl) +{ + return container_of(ibfrpl, struct mlx4_ib_fast_reg_page_list, ibfrpl); +} + static inline struct mlx4_ib_fmr *to_mfmr(struct ib_fmr *ibfmr) { return container_of(ibfmr, struct mlx4_ib_fmr, ibfmr); @@ -239,6 +249,11 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int access_flags, struct ib_udata *udata); int mlx4_ib_dereg_mr(struct ib_mr *mr); +struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, + int max_page_list_len); +struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev, + int page_list_len); +void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list); int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period); int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 68e92485fc76..db2086faa4ed 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -183,6 +183,76 @@ int mlx4_ib_dereg_mr(struct ib_mr *ibmr) return 0; } +struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, + int max_page_list_len) +{ + struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_mr *mr; + int err; + + mr = kmalloc(sizeof *mr, GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + err = mlx4_mr_alloc(dev->dev, to_mpd(pd)->pdn, 0, 0, 0, + max_page_list_len, 0, &mr->mmr); + if (err) + goto err_free; + + err = mlx4_mr_enable(dev->dev, &mr->mmr); + if (err) + goto err_mr; + + return &mr->ibmr; + +err_mr: + mlx4_mr_free(dev->dev, &mr->mmr); + +err_free: + kfree(mr); + return ERR_PTR(err); +} + +struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev, + int page_list_len) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct mlx4_ib_fast_reg_page_list *mfrpl; + int size = page_list_len * sizeof (u64); + + if (size > PAGE_SIZE) + return ERR_PTR(-EINVAL); + + mfrpl = kmalloc(sizeof *mfrpl, GFP_KERNEL); + if (!mfrpl) + return ERR_PTR(-ENOMEM); + + mfrpl->ibfrpl.page_list = dma_alloc_coherent(&dev->dev->pdev->dev, + size, &mfrpl->map, + GFP_KERNEL); + if (!mfrpl->ibfrpl.page_list) + goto err_free; + + WARN_ON(mfrpl->map & 0x3f); + + return &mfrpl->ibfrpl; + +err_free: + kfree(mfrpl); + return ERR_PTR(-ENOMEM); +} + +void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list) +{ + struct mlx4_ib_dev *dev = to_mdev(page_list->device); + struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list); + int size = page_list->max_page_list_len * sizeof (u64); + + dma_free_coherent(&dev->dev->pdev->dev, size, page_list->page_list, + mfrpl->map); + kfree(mfrpl); +} + struct ib_fmr *mlx4_ib_fmr_alloc(struct ib_pd *pd, int acc, struct ib_fmr_attr *fmr_attr) { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index bda0859a5ac5..02a99bc4442e 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -78,6 +78,9 @@ static const __be32 mlx4_ib_opcode[] = { [IB_WR_RDMA_READ] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_READ), [IB_WR_ATOMIC_CMP_AND_SWP] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_CS), [IB_WR_ATOMIC_FETCH_AND_ADD] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_FA), + [IB_WR_SEND_WITH_INV] = __constant_cpu_to_be32(MLX4_OPCODE_SEND_INVAL), + [IB_WR_LOCAL_INV] = __constant_cpu_to_be32(MLX4_OPCODE_LOCAL_INVAL), + [IB_WR_FAST_REG_MR] = __constant_cpu_to_be32(MLX4_OPCODE_FMR), }; static struct mlx4_ib_sqp *to_msqp(struct mlx4_ib_qp *mqp) @@ -976,6 +979,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn); context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28); + /* Set "fast registration enabled" for all kernel QPs */ + if (!qp->ibqp.uobject) + context->params1 |= cpu_to_be32(1 << 11); + if (attr_mask & IB_QP_RNR_RETRY) { context->params1 |= cpu_to_be32(attr->rnr_retry << 13); optpar |= MLX4_QP_OPTPAR_RNR_RETRY; @@ -1322,6 +1329,38 @@ static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq return cur + nreq >= wq->max_post; } +static __be32 convert_access(int acc) +{ + return (acc & IB_ACCESS_REMOTE_ATOMIC ? cpu_to_be32(MLX4_WQE_FMR_PERM_ATOMIC) : 0) | + (acc & IB_ACCESS_REMOTE_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_WRITE) : 0) | + (acc & IB_ACCESS_REMOTE_READ ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_READ) : 0) | + (acc & IB_ACCESS_LOCAL_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_WRITE) : 0) | + cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_READ); +} + +static void set_fmr_seg(struct mlx4_wqe_fmr_seg *fseg, struct ib_send_wr *wr) +{ + struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(wr->wr.fast_reg.page_list); + + fseg->flags = convert_access(wr->wr.fast_reg.access_flags); + fseg->mem_key = cpu_to_be32(wr->wr.fast_reg.rkey); + fseg->buf_list = cpu_to_be64(mfrpl->map); + fseg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start); + fseg->reg_len = cpu_to_be64(wr->wr.fast_reg.length); + fseg->offset = 0; /* XXX -- is this just for ZBVA? */ + fseg->page_size = cpu_to_be32(wr->wr.fast_reg.page_shift); + fseg->reserved[0] = 0; + fseg->reserved[1] = 0; +} + +static void set_local_inv_seg(struct mlx4_wqe_local_inval_seg *iseg, u32 rkey) +{ + iseg->flags = 0; + iseg->mem_key = cpu_to_be32(rkey); + iseg->guest_id = 0; + iseg->pa = 0; +} + static __always_inline void set_raddr_seg(struct mlx4_wqe_raddr_seg *rseg, u64 remote_addr, u32 rkey) { @@ -1423,6 +1462,21 @@ static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr, return 0; } +static __be32 send_ieth(struct ib_send_wr *wr) +{ + switch (wr->opcode) { + case IB_WR_SEND_WITH_IMM: + case IB_WR_RDMA_WRITE_WITH_IMM: + return wr->ex.imm_data; + + case IB_WR_SEND_WITH_INV: + return cpu_to_be32(wr->ex.invalidate_rkey); + + default: + return 0; + } +} + int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, struct ib_send_wr **bad_wr) { @@ -1469,11 +1523,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, MLX4_WQE_CTRL_TCP_UDP_CSUM) : 0) | qp->sq_signal_bits; - if (wr->opcode == IB_WR_SEND_WITH_IMM || - wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM) - ctrl->imm = wr->ex.imm_data; - else - ctrl->imm = 0; + ctrl->imm = send_ieth(wr); wqe += sizeof *ctrl; size = sizeof *ctrl / 16; @@ -1505,6 +1555,18 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, size += sizeof (struct mlx4_wqe_raddr_seg) / 16; break; + case IB_WR_LOCAL_INV: + set_local_inv_seg(wqe, wr->ex.invalidate_rkey); + wqe += sizeof (struct mlx4_wqe_local_inval_seg); + size += sizeof (struct mlx4_wqe_local_inval_seg) / 16; + break; + + case IB_WR_FAST_REG_MR: + set_fmr_seg(wqe, wr); + wqe += sizeof (struct mlx4_wqe_fmr_seg); + size += sizeof (struct mlx4_wqe_fmr_seg) / 16; + break; + default: /* No extra segments required for sends */ break; diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index 0851ebdddfd4..57278224ba1e 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c @@ -202,7 +202,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_C_MPT_ENTRY_SZ_OFFSET 0x8e #define QUERY_DEV_CAP_MTT_ENTRY_SZ_OFFSET 0x90 #define QUERY_DEV_CAP_D_MPT_ENTRY_SZ_OFFSET 0x92 -#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x97 +#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x94 #define QUERY_DEV_CAP_RSVD_LKEY_OFFSET 0x98 #define QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET 0xa0 @@ -377,12 +377,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) } } - if (dev_cap->bmme_flags & 1) - mlx4_dbg(dev, "Base MM extensions: yes " - "(flags %d, rsvd L_Key %08x)\n", - dev_cap->bmme_flags, dev_cap->reserved_lkey); - else - mlx4_dbg(dev, "Base MM extensions: no\n"); + mlx4_dbg(dev, "Base MM extensions: flags %08x, rsvd L_Key %08x\n", + dev_cap->bmme_flags, dev_cap->reserved_lkey); /* * Each UAR has 4 EQ doorbells; so if a UAR is reserved, then diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h index a0e046c149b7..fbf0e22be122 100644 --- a/drivers/net/mlx4/fw.h +++ b/drivers/net/mlx4/fw.h @@ -98,7 +98,7 @@ struct mlx4_dev_cap { int cmpt_entry_sz; int mtt_entry_sz; int resize_srq; - u8 bmme_flags; + u32 bmme_flags; u32 reserved_lkey; u64 max_icm_sz; int max_gso_sz; diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index d3736013fe9b..8e1d24cda1b0 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -158,6 +158,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_msg_sz = dev_cap->max_msg_sz; dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1); dev->caps.flags = dev_cap->flags; + dev->caps.bmme_flags = dev_cap->bmme_flags; + dev->caps.reserved_lkey = dev_cap->reserved_lkey; dev->caps.stat_rate_support = dev_cap->stat_rate_support; dev->caps.max_gso_sz = dev_cap->max_gso_sz; diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index b3ea93b98689..a3c04c5f12c2 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -47,7 +47,7 @@ struct mlx4_mpt_entry { __be32 flags; __be32 qpn; __be32 key; - __be32 pd; + __be32 pd_flags; __be64 start; __be64 length; __be32 lkey; @@ -61,11 +61,15 @@ struct mlx4_mpt_entry { } __attribute__((packed)); #define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28) +#define MLX4_MPT_FLAG_FREE (0x3UL << 28) #define MLX4_MPT_FLAG_MIO (1 << 17) #define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15) #define MLX4_MPT_FLAG_PHYSICAL (1 << 9) #define MLX4_MPT_FLAG_REGION (1 << 8) +#define MLX4_MPT_PD_FLAG_FAST_REG (1 << 26) +#define MLX4_MPT_PD_FLAG_EN_INV (3 << 24) + #define MLX4_MTT_FLAG_PRESENT 1 #define MLX4_MPT_STATUS_SW 0xF0 @@ -324,21 +328,30 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) memset(mpt_entry, 0, sizeof *mpt_entry); - mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS | - MLX4_MPT_FLAG_MIO | + mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO | MLX4_MPT_FLAG_REGION | mr->access); mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key)); - mpt_entry->pd = cpu_to_be32(mr->pd); + mpt_entry->pd_flags = cpu_to_be32(mr->pd | MLX4_MPT_PD_FLAG_EN_INV); mpt_entry->start = cpu_to_be64(mr->iova); mpt_entry->length = cpu_to_be64(mr->size); mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); + if (mr->mtt.order < 0) { mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); mpt_entry->mtt_seg = 0; - } else + } else { mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt)); + } + + if (mr->mtt.order >= 0 && mr->mtt.page_shift == 0) { + /* fast register MR in free state */ + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_FREE); + mpt_entry->pd_flags |= cpu_to_be32(MLX4_MPT_PD_FLAG_FAST_REG); + } else { + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS); + } err = mlx4_SW2HW_MPT(dev, mailbox, key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1)); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 81b3dd5206e0..655ea0d1ee14 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -68,6 +68,14 @@ enum { MLX4_DEV_CAP_FLAG_UD_MCAST = 1 << 21 }; +enum { + MLX4_BMME_FLAG_LOCAL_INV = 1 << 6, + MLX4_BMME_FLAG_REMOTE_INV = 1 << 7, + MLX4_BMME_FLAG_TYPE_2_WIN = 1 << 9, + MLX4_BMME_FLAG_RESERVED_LKEY = 1 << 10, + MLX4_BMME_FLAG_FAST_REG_WR = 1 << 11, +}; + enum mlx4_event { MLX4_EVENT_TYPE_COMP = 0x00, MLX4_EVENT_TYPE_PATH_MIG = 0x01, @@ -184,6 +192,8 @@ struct mlx4_caps { u32 max_msg_sz; u32 page_size_cap; u32 flags; + u32 bmme_flags; + u32 reserved_lkey; u16 stat_rate_support; u8 port_width_cap[MLX4_MAX_PORTS + 1]; int max_gso_sz; diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index f02e9ed36cfa..e27082cd650e 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -233,6 +233,14 @@ struct mlx4_wqe_bind_seg { __be64 length; }; +enum { + MLX4_WQE_FMR_PERM_LOCAL_READ = 1 << 27, + MLX4_WQE_FMR_PERM_LOCAL_WRITE = 1 << 28, + MLX4_WQE_FMR_PERM_REMOTE_READ = 1 << 29, + MLX4_WQE_FMR_PERM_REMOTE_WRITE = 1 << 30, + MLX4_WQE_FMR_PERM_ATOMIC = 1 << 31 +}; + struct mlx4_wqe_fmr_seg { __be32 flags; __be32 mem_key; @@ -255,11 +263,11 @@ struct mlx4_wqe_fmr_ext_seg { }; struct mlx4_wqe_local_inval_seg { - u8 flags; - u8 reserved1[3]; + __be32 flags; + u32 reserved1; __be32 mem_key; - u8 reserved2[3]; - u8 guest_id; + u32 reserved2[2]; + __be32 guest_id; __be64 pa; }; -- cgit v1.2.3 From c97c6aca75fd5f718056fde7cff798b8cbdb07c0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:50 +0200 Subject: ide: pass hw_regs_t-s to ide_device_add[_all]() (take 3) * Add 'hw_regs_t **hws' argument to ide_device_add[_all]() and convert host drivers + ide_legacy_init_one() + ide_setup_pci_device[s]() to use it instead of calling ide_init_port_hw() directly. [ However if host has > 1 port we must still set hwif->chipset to hint consecutive ide_find_port() call that the previous slot is occupied. ] * Unexport ide_init_port_hw(). v2: * Use defines instead of hard-coded values in buddha.c, gayle.c and q40ide.c. (Suggested by Geert Uytterhoeven) * Better patch description. v3: * Fix build problem in ide-cs.c. (Noticed by Stephen Rothwell) There should be no functional changes caused by this patch. Cc: Geert Uytterhoeven Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 13 ++++++------- drivers/ide/arm/ide_arm.c | 5 ++--- drivers/ide/arm/palm_bk3710.c | 6 ++---- drivers/ide/arm/rapide.c | 5 ++--- drivers/ide/h8300/ide-h8300.c | 5 ++--- drivers/ide/ide-generic.c | 23 ++++++++++++----------- drivers/ide/ide-pnp.c | 6 ++---- drivers/ide/ide-probe.c | 29 ++++++++++++++++++----------- drivers/ide/ide.c | 1 - drivers/ide/legacy/buddha.c | 16 +++++++++------- drivers/ide/legacy/falconide.c | 6 ++---- drivers/ide/legacy/gayle.c | 13 ++++++------- drivers/ide/legacy/ide-4drives.c | 10 ++++++---- drivers/ide/legacy/ide-cs.c | 10 +++------- drivers/ide/legacy/ide_platform.c | 9 +++------ drivers/ide/legacy/macide.c | 6 ++---- drivers/ide/legacy/q40ide.c | 14 ++++++-------- drivers/ide/mips/au1xxx-ide.c | 6 ++---- drivers/ide/mips/swarm.c | 6 ++---- drivers/ide/pci/cmd640.c | 10 ++++++---- drivers/ide/pci/cs5520.c | 5 +++-- drivers/ide/pci/delkin_cb.c | 6 ++---- drivers/ide/pci/scc_pata.c | 5 ++--- drivers/ide/pci/sgiioc4.c | 6 ++---- drivers/ide/ppc/pmac.c | 5 ++--- drivers/ide/setup-pci.c | 38 +++++++++++++++++++++++--------------- include/linux/ide.h | 7 ++++--- 27 files changed, 131 insertions(+), 140 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 52f58c885783..850fe9342a1f 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -442,8 +442,8 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) { ide_hwif_t *hwif; void __iomem *base; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -467,7 +467,6 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) if (!hwif) return -ENODEV; - ide_init_port_hw(hwif, &hw); default_hwif_mmiops(hwif); state->hwif[0] = hwif; @@ -476,7 +475,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) idx[0] = hwif->index; - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } @@ -497,9 +496,9 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) void __iomem *ioc_base, *easi_base; unsigned int sel = 0; int ret; + hw_regs_t hw[2], *hws[] = { &hw[0], NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = icside_v6_port_info; - hw_regs_t hw[2]; ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); if (!ioc_base) { @@ -545,16 +544,16 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) if (hwif == NULL) return -ENODEV; - ide_init_port_hw(hwif, &hw[0]); + hwif->chipset = ide_acorn; default_hwif_mmiops(hwif); idx[0] = hwif->index; mate = ide_find_port(); if (mate) { - ide_init_port_hw(mate, &hw[1]); default_hwif_mmiops(mate); + hws[1] = &hw[1]; idx[1] = mate->index; } @@ -569,7 +568,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_device_add(idx, &d); + ide_device_add(idx, &d, hws); return 0; diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index 2f311da4c963..e9831bbd988a 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -29,8 +29,8 @@ static int __init ide_arm_init(void) { ide_hwif_t *hwif; - hw_regs_t hw; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(base, 8, DRV_NAME)) { @@ -53,10 +53,9 @@ static int __init ide_arm_init(void) hwif = ide_find_port(); if (hwif) { - ide_init_port_hw(hwif, &hw); idx[0] = hwif->index; - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); } return 0; diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index c79b85b6e4a3..023c10753f15 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -351,7 +351,7 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) ide_hwif_t *hwif; unsigned long base, rate; int i; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; clk = clk_get(NULL, "IDECLK"); @@ -400,13 +400,11 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) i = hwif->index; - ide_init_port_hw(hwif, &hw); - default_hwif_mmiops(hwif); idx[0] = i; - ide_device_add(idx, &palm_bk3710_port_info); + ide_device_add(idx, &palm_bk3710_port_info, hws); return 0; out: diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 43057e0303c8..01896f6e8acf 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -35,8 +35,8 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) ide_hwif_t *hwif; void __iomem *base; int ret; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; ret = ecard_request_resources(ec); if (ret) @@ -59,12 +59,11 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) goto release; } - ide_init_port_hw(hwif, &hw); default_hwif_mmiops(hwif); idx[0] = hwif->index; - ide_device_add(idx, &rapide_port_info); + ide_device_add(idx, &rapide_port_info, hws); ecard_set_drvdata(ec, hwif); goto out; diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 20fad6d542cc..a71433b3d0f3 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -184,9 +184,9 @@ static const struct ide_port_info h8300_port_info = { static int __init h8300_ide_init(void) { - hw_regs_t hw; ide_hwif_t *hwif; int index; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -205,12 +205,11 @@ static int __init h8300_ide_init(void) return -ENOENT; index = hwif->index; - ide_init_port_hw(hwif, &hw); hwif_setup(hwif); idx[0] = index; - ide_device_add(idx, &h8300_port_info); + ide_device_add(idx, &h8300_port_info, hws); return 0; diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index 2d92214096ab..bb9fc905d60c 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -31,7 +31,7 @@ static ssize_t store_add(struct class *cls, const char *buf, size_t n) ide_hwif_t *hwif; unsigned int base, ctl; int irq; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) @@ -46,11 +46,9 @@ static ssize_t store_add(struct class *cls, const char *buf, size_t n) hw.irq = irq; hw.chipset = ide_generic; - ide_init_port_hw(hwif, &hw); - idx[0] = hwif->index; - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return n; }; @@ -90,6 +88,7 @@ static int __init ide_generic_sysfs_init(void) static int __init ide_generic_init(void) { + hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; u8 idx[MAX_HWIFS]; int i; @@ -99,8 +98,8 @@ static int __init ide_generic_init(void) for (i = 0; i < MAX_HWIFS; i++) { ide_hwif_t *hwif; unsigned long io_addr = ide_default_io_base(i); - hw_regs_t hw; + hws[i] = NULL; idx[i] = 0xff; if ((probe_mask & (1 << i)) && io_addr) { @@ -129,17 +128,19 @@ static int __init ide_generic_init(void) continue; } - memset(&hw, 0, sizeof(hw)); - ide_std_init_ports(&hw, io_addr, io_addr + 0x206); - hw.irq = ide_default_irq(io_addr); - hw.chipset = ide_generic; - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_generic; + + memset(&hw[i], 0, sizeof(hw[i])); + ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206); + hw[i].irq = ide_default_irq(io_addr); + hw[i].chipset = ide_generic; + hws[i] = &hw[i]; idx[i] = i; } } - ide_device_add_all(idx, NULL); + ide_device_add_all(idx, NULL, hws); if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 03f2ef5470a3..89cd5cbe8573 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -29,9 +29,9 @@ static struct pnp_device_id idepnp_devices[] = { static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - hw_regs_t hw; ide_hwif_t *hwif; unsigned long base, ctl; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n"); @@ -64,11 +64,9 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - ide_init_port_hw(hwif, &hw); - pnp_set_drvdata(dev, hwif); - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 235ebdb29b28..9d8686a49fcf 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1501,7 +1501,7 @@ out_found: } EXPORT_SYMBOL_GPL(ide_find_port_slot); -int ide_device_add_all(u8 *idx, const struct ide_port_info *d) +int ide_device_add_all(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) { ide_hwif_t *hwif, *mate = NULL; int i, rc = 0; @@ -1514,6 +1514,7 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) hwif = &ide_hwifs[idx[i]]; + ide_init_port_hw(hwif, hws[i]); ide_port_apply_params(hwif); if (d == NULL) { @@ -1603,15 +1604,18 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) } EXPORT_SYMBOL_GPL(ide_device_add_all); -int ide_device_add(u8 idx[4], const struct ide_port_info *d) +int ide_device_add(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) { + hw_regs_t *hws_all[MAX_HWIFS]; u8 idx_all[MAX_HWIFS]; int i; - for (i = 0; i < MAX_HWIFS; i++) + for (i = 0; i < MAX_HWIFS; i++) { + hws_all[i] = (i < 4) ? hws[i] : NULL; idx_all[i] = (i < 4) ? idx[i] : 0xff; + } - return ide_device_add_all(idx_all, d); + return ide_device_add_all(idx_all, d, hws_all); } EXPORT_SYMBOL_GPL(ide_device_add); @@ -1634,8 +1638,8 @@ void ide_port_scan(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_port_scan); -static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, - const struct ide_port_info *d, +static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, + u8 port_no, const struct ide_port_info *d, unsigned long config) { ide_hwif_t *hwif; @@ -1671,9 +1675,12 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, hwif = ide_find_port_slot(d); if (hwif) { - ide_init_port_hw(hwif, hw); + hwif->chipset = hw->chipset; + if (config) hwif->config_data = config; + + hws[port_no] = hw; idx[port_no] = hwif->index; } } @@ -1681,19 +1688,19 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw[2]; + hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0) - ide_legacy_init_one(idx, &hw[0], 0, d, config); - ide_legacy_init_one(idx, &hw[1], 1, d, config); + ide_legacy_init_one(idx, hws, &hw[0], 0, d, config); + ide_legacy_init_one(idx, hws, &hw[1], 1, d, config); if (idx[0] == 0xff && idx[1] == 0xff && (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - ide_device_add(idx, d); + ide_device_add(idx, d, hws); return 0; } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index d4a6b102a772..b6018f7b0907 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -288,7 +288,6 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; hwif->ack_intr = hw->ack_intr; } -EXPORT_SYMBOL_GPL(ide_init_port_hw); /* * Locks for IDE setting functionality diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 0497e7f85b09..c61bc6a1db36 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -37,6 +37,8 @@ #define CATWEASEL_NUM_HWIFS 3 #define XSURF_NUM_HWIFS 2 +#define MAX_NUM_HWIFS 3 + /* * Bases of the IDE interfaces (relative to the board address) */ @@ -148,7 +150,6 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { - hw_regs_t hw; ide_hwif_t *hwif; int i; @@ -159,6 +160,7 @@ static int __init buddha_init(void) while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { unsigned long board; + hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { @@ -221,19 +223,19 @@ fail_base2: ack_intr = xsurf_ack_intr; } - buddha_setup_ports(&hw, base, ctl, irq_port, ack_intr); + buddha_setup_ports(&hw[i], base, ctl, irq_port, + ack_intr); hwif = ide_find_port(); if (hwif) { - u8 index = hwif->index; - - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_generic; - idx[i] = index; + hws[i] = &hw[i]; + idx[i] = hwif->index; } } - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); } return 0; diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 129a812bb57f..1bb2aa72cc7f 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -91,8 +91,8 @@ static void __init falconide_setup_ports(hw_regs_t *hw) static int __init falconide_init(void) { - hw_regs_t hw; ide_hwif_t *hwif; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) return 0; @@ -111,14 +111,12 @@ static int __init falconide_init(void) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - ide_init_port_hw(hwif, &hw); - /* Atari has a byte-swapped IDE interface */ hwif->input_data = falconide_input_data; hwif->output_data = falconide_output_data; ide_get_lock(NULL, NULL); - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); ide_release_lock(); } diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 7e74b20202df..e45c7341186c 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -125,6 +125,7 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base, static int __init gayle_init(void) { int a4000, i; + hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_AMIGA) @@ -151,7 +152,6 @@ found: for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; - hw_regs_t hw; ide_hwif_t *hwif; unsigned long phys_base, res_start, res_n; @@ -179,20 +179,19 @@ found: base = (unsigned long)ZTWO_VADDR(phys_base); ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; - gayle_setup_ports(&hw, base, ctrlport, irqport, ack_intr); + gayle_setup_ports(&hw[i], base, ctrlport, irqport, ack_intr); hwif = ide_find_port(); if (hwif) { - u8 index = hwif->index; + hwif->chipset = ide_generic; - ide_init_port_hw(hwif, &hw); - - idx[i] = index; + hws[i] = &hw[i]; + idx[i] = hwif->index; } else release_mem_region(res_start, res_n); } - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 89c8ff0a4d08..6310dc50e3c5 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -30,8 +30,8 @@ static int __init ide_4drives_init(void) { ide_hwif_t *hwif, *mate; unsigned long base = 0x1f0, ctl = 0x3f6; + hw_regs_t hw, *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; if (probe_4drives == 0) return -ENODEV; @@ -57,17 +57,19 @@ static int __init ide_4drives_init(void) hwif = ide_find_port(); if (hwif) { - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_4drives; + + hws[0] = &hw; idx[0] = hwif->index; } mate = ide_find_port(); if (mate) { - ide_init_port_hw(mate, &hw); + hws[1] = &hw; idx[1] = mate->index; } - ide_device_add(idx, &ide_4drives_port_info); + ide_device_add(idx, &ide_4drives_port_info, hws); return 0; } diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 27b1e0b7ecb4..f93d5454ebf8 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -161,8 +161,8 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle) { ide_hwif_t *hwif; - hw_regs_t hw; int i; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(io, 8, DRV_NAME)) { @@ -188,13 +188,9 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, if (hwif == NULL) goto out_release; - i = hwif->index; + idx[0] = hwif->index; - ide_init_port_hw(hwif, &hw); - - idx[0] = i; - - ide_device_add(idx, &idecs_port_info); + ide_device_add(idx, &idecs_port_info, hws); if (hwif->present) return hwif; diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index a249562b34b5..609da0d43196 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -54,10 +54,9 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) void __iomem *base, *alt_base; ide_hwif_t *hwif; struct pata_platform_info *pdata; + int ret = 0, mmio = 0; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - int ret = 0; - int mmio = 0; - hw_regs_t hw; struct ide_port_info d = platform_ide_port_info; pdata = pdev->dev.platform_data; @@ -104,8 +103,6 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; - ide_init_port_hw(hwif, &hw); - if (mmio) { d.host_flags |= IDE_HFLAG_MMIO; default_hwif_mmiops(hwif); @@ -113,7 +110,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) idx[0] = hwif->index; - ide_device_add(idx, &d); + ide_device_add(idx, &d, hws); platform_set_drvdata(pdev, hwif); diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index 0a6195bcfeda..d839df2239fc 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -95,7 +95,7 @@ static int __init macide_init(void) ide_ack_intr_t *ack_intr; unsigned long base; int irq; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_MAC) return -ENODEV; @@ -130,9 +130,7 @@ static int __init macide_init(void) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - ide_init_port_hw(hwif, &hw); - - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); } return 0; diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 9c2b9d078f69..fcb04b8b0238 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -112,7 +112,7 @@ static int __init q40ide_init(void) { int i; ide_hwif_t *hwif; - const char *name; + hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_Q40) @@ -121,9 +121,8 @@ static int __init q40ide_init(void) printk(KERN_INFO "ide: Q40 IDE controller\n"); for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { - hw_regs_t hw; + const char *name = q40_ide_names[i]; - name = q40_ide_names[i]; if (!request_region(pcide_bases[i], 8, name)) { printk("could not reserve ports %lx-%lx for %s\n", pcide_bases[i],pcide_bases[i]+8,name); @@ -135,24 +134,23 @@ static int __init q40ide_init(void) release_region(pcide_bases[i], 8); continue; } - q40_ide_setup_ports(&hw, pcide_bases[i], - NULL, -// m68kide_iops, + q40_ide_setup_ports(&hw[i], pcide_bases[i], NULL, q40ide_default_irq(pcide_bases[i])); hwif = ide_find_port(); if (hwif) { - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_generic; /* Q40 has a byte-swapped IDE interface */ hwif->input_data = q40ide_input_data; hwif->output_data = q40ide_output_data; + hws[i] = &hw[i]; idx[i] = hwif->index; } } - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 48d57cae63c6..475da582fd89 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -546,8 +546,8 @@ static int au_ide_probe(struct device *dev) ide_hwif_t *hwif; struct resource *res; int ret = 0; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) char *mode = "MWDMA2"; @@ -596,8 +596,6 @@ static int au_ide_probe(struct device *dev) hw.dev = dev; hw.chipset = ide_au1xxx; - ide_init_port_hw(hwif, &hw); - /* If the user has selected DDMA assisted copies, then set up a few local I/O function entry points */ @@ -611,7 +609,7 @@ static int au_ide_probe(struct device *dev) idx[0] = hwif->index; - ide_device_add(idx, &au1xxx_port_info); + ide_device_add(idx, &au1xxx_port_info, hws); dev_set_drvdata(dev, hwif); diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 9f1212cc4aed..6da6844d2d8c 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -75,8 +75,8 @@ static int __devinit swarm_ide_probe(struct device *dev) ide_hwif_t *hwif; u8 __iomem *base; phys_t offset, size; - hw_regs_t hw; int i; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (!SIBYTE_HAVE_IDE) @@ -120,14 +120,12 @@ static int __devinit swarm_ide_probe(struct device *dev) if (hwif == NULL) goto err; - ide_init_port_hw(hwif, &hw); - /* Setup MMIO ops. */ default_hwif_mmiops(hwif); idx[0] = hwif->index; - ide_device_add(idx, &swarm_port_info); + ide_device_add(idx, &swarm_port_info, hws); dev_set_drvdata(dev, hwif); diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 1ad1e23e3105..ccde1e444e13 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -717,8 +717,8 @@ static int __init cmd640x_init(void) int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; + hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw[2]; if (cmd640_vlb && probe_for_cmd640_vlb()) { bus_type = "VLB"; @@ -787,7 +787,9 @@ static int __init cmd640x_init(void) * Initialize data for primary port */ if (cmd_hwif0) { - ide_init_port_hw(cmd_hwif0, &hw[0]); + cmd_hwif0->chipset = ide_cmd640; + + hws[0] = &hw[0]; idx[0] = cmd_hwif0->index; } @@ -832,7 +834,7 @@ static int __init cmd640x_init(void) if (second_port_cmd640) { cmd_hwif1 = ide_find_port(); if (cmd_hwif1) { - ide_init_port_hw(cmd_hwif1, &hw[1]); + hws[1] = &hw[1]; idx[1] = cmd_hwif1->index; } } @@ -843,7 +845,7 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - ide_device_add(idx, &cmd640_port_info); + ide_device_add(idx, &cmd640_port_info, hws); return 1; } diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index 992b1cf8db69..a13f2bc0665c 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -146,6 +146,7 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; ide_setup_pci_noise(dev, d); @@ -168,9 +169,9 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic * do all the device setup for us */ - ide_pci_setup_ports(dev, d, 14, &idx[0]); + ide_pci_setup_ports(dev, d, 14, &idx[0], &hw[0], &hws[0]); - ide_device_add(idx, d); + ide_device_add(idx, d, hws); return 0; } diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 0106e2a2df77..33fe15db408a 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -57,9 +57,9 @@ static int __devinit delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) { unsigned long base; - hw_regs_t hw; ide_hwif_t *hwif = NULL; int i, rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; rc = pci_enable_device(dev); @@ -93,11 +93,9 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) i = hwif->index; - ide_init_port_hw(hwif, &hw); - idx[0] = i; - ide_device_add(idx, &delkin_cb_port_info); + ide_device_add(idx, &delkin_cb_port_info, hws); pci_set_drvdata(dev, hwif); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 789c66dfbde5..328e2df66550 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -554,7 +554,7 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, { struct scc_ports *ports = pci_get_drvdata(dev); ide_hwif_t *hwif = NULL; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; int i; @@ -568,11 +568,10 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.irq = dev->irq; hw.dev = &dev->dev; hw.chipset = ide_pci; - ide_init_port_hw(hwif, &hw); idx[0] = hwif->index; - ide_device_add(idx, d); + ide_device_add(idx, d, hws); return 0; } diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index c79ff5b41088..76afa1f9c599 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -584,8 +584,8 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) unsigned long bar0, cmd_phys_base, ctl; void __iomem *virt_base; ide_hwif_t *hwif; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; struct ide_port_info d = sgiioc4_port_info; /* Get the CmdBlk and CtrlBlk Base Registers */ @@ -622,8 +622,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) if (hwif == NULL) goto err; - ide_init_port_hw(hwif, &hw); - /* The IOC4 uses MMIO rather than Port IO. */ default_hwif_mmiops(hwif); @@ -634,7 +632,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) idx[0] = hwif->index; - if (ide_device_add(idx, &d)) + if (ide_device_add(idx, &d, hws)) return -EIO; return 0; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 4e2944ee76d8..e68e33bb2c35 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1010,6 +1010,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) struct device_node *np = pmif->node; const int *bidp; ide_hwif_t *hwif; + hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = pmac_port_info; @@ -1095,11 +1096,9 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) default_hwif_mmiops(hwif); hwif->OUTBSYNC = pmac_outbsync; - ide_init_port_hw(hwif, hw); - idx[0] = hwif->index; - ide_device_add(idx, &d); + ide_device_add(idx, &d, hws); return 0; } diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 7ae6ae45331f..acb467c6f345 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -289,6 +289,7 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * * @d: IDE port info * @port: port number * @irq: PCI IRQ + * @hw: hw_regs_t instance corresponding to this port * * Perform the initial set up for the hardware interface structure. This * is done per interface port rather than per PCI device. There may be @@ -299,11 +300,11 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, const struct ide_port_info *d, - unsigned int port, int irq) + unsigned int port, int irq, + hw_regs_t *hw) { unsigned long ctl = 0, base = 0; ide_hwif_t *hwif; - struct hw_regs_s hw; if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { if (ide_pci_check_iomem(dev, d, 2 * port) || @@ -327,17 +328,17 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, return NULL; } + memset(hw, 0, sizeof(*hw)); + hw->irq = irq; + hw->dev = &dev->dev; + hw->chipset = d->chipset ? d->chipset : ide_pci; + ide_std_init_ports(hw, base, ctl | 2); + hwif = ide_find_port_slot(d); if (hwif == NULL) return NULL; - memset(&hw, 0, sizeof(hw)); - hw.irq = irq; - hw.dev = &dev->dev; - hw.chipset = d->chipset ? d->chipset : ide_pci; - ide_std_init_ports(&hw, base, ctl | 2); - - ide_init_port_hw(hwif, &hw); + hwif->chipset = hw->chipset; return hwif; } @@ -430,6 +431,8 @@ out: * @d: IDE port info * @pciirq: IRQ line * @idx: ATA index table to update + * @hw: hw_regs_t instances corresponding to this PCI IDE device + * @hws: hw_regs_t pointers table to update * * Scan the interfaces attached to this device and do any * necessary per port setup. Attach the devices and ask the @@ -440,7 +443,8 @@ out: * where the chipset setup is not the default PCI IDE one. */ -void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int pciirq, u8 *idx) +void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, + int pciirq, u8 *idx, hw_regs_t *hw, hw_regs_t **hws) { int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port; ide_hwif_t *hwif; @@ -459,10 +463,11 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int continue; /* port not enabled */ } - hwif = ide_hwif_configure(dev, d, port, pciirq); + hwif = ide_hwif_configure(dev, d, port, pciirq, hw + port); if (hwif == NULL) continue; + *(hws + port) = hw + port; *(idx + port) = hwif->index; } } @@ -537,15 +542,16 @@ out: int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; ret = do_ide_setup_pci_device(dev, d, 1); if (ret >= 0) { /* FIXME: silent failure can happen */ - ide_pci_setup_ports(dev, d, ret, &idx[0]); + ide_pci_setup_ports(dev, d, ret, &idx[0], &hw[0], &hws[0]); - ide_device_add(idx, d); + ide_device_add(idx, d, hws); } return ret; @@ -557,6 +563,7 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, { struct pci_dev *pdev[] = { dev1, dev2 }; int ret, i; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; for (i = 0; i < 2; i++) { @@ -570,10 +577,11 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, goto out; /* FIXME: silent failure can happen */ - ide_pci_setup_ports(pdev[i], d, ret, &idx[i*2]); + ide_pci_setup_ports(pdev[i], d, ret, &idx[i*2], &hw[i*2], + &hws[i*2]); } - ide_device_add(idx, d); + ide_device_add(idx, d, hws); out: return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 4726126f5a59..f58548becac0 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1000,7 +1000,8 @@ extern int __ide_pci_register_driver(struct pci_driver *driver, struct module *o #define ide_pci_register_driver(d) pci_register_driver(d) #endif -void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int, u8 *); +void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int, + u8 *, hw_regs_t *, hw_regs_t **); void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -1217,8 +1218,8 @@ void ide_undecoded_slave(ide_drive_t *); void ide_port_apply_params(ide_hwif_t *); -int ide_device_add_all(u8 *idx, const struct ide_port_info *); -int ide_device_add(u8 idx[4], const struct ide_port_info *); +int ide_device_add_all(u8 *, const struct ide_port_info *, hw_regs_t **); +int ide_device_add(u8 *, const struct ide_port_info *, hw_regs_t **); int ide_legacy_device_add(const struct ide_port_info *, unsigned long); void ide_port_unregister_devices(ide_hwif_t *); void ide_port_scan(ide_hwif_t *); -- cgit v1.2.3 From b2f951aabc9cc7d5fb987aeec9aef96ccce618a5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:50 +0200 Subject: ide: add ->read_sff_dma_status method Add ->read_sff_dma_status method for reading DMA Status register and use it instead of ->INB. While at it: * Use inb() directly in ns87415.c::ns87415_dma_end(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 12 ++++++------ drivers/ide/ide-iops.c | 10 ++++++++++ drivers/ide/pci/ns87415.c | 13 ++++++++++--- drivers/ide/pci/scc_pata.c | 7 +++++++ include/linux/ide.h | 2 ++ 5 files changed, 35 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 7ee44f86bc54..ebddedde24af 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -334,7 +334,7 @@ static int config_drive_for_dma (ide_drive_t *drive) static int dma_timer_expiry (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->read_sff_dma_status(hwif); printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); @@ -369,7 +369,7 @@ void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->read_sff_dma_status(hwif); if (on) dma_stat |= (1 << (5 + unit)); @@ -472,8 +472,8 @@ int ide_dma_setup(ide_drive_t *drive) /* specify r/w */ hwif->OUTB(reading, hwif->dma_command); - /* read dma_status for INTR & ERROR flags */ - dma_stat = hwif->INB(hwif->dma_status); + /* read DMA status for INTR & ERROR flags */ + dma_stat = hwif->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ hwif->OUTB(dma_stat|6, hwif->dma_status); @@ -520,7 +520,7 @@ int __ide_dma_end (ide_drive_t *drive) /* stop DMA */ hwif->OUTB(dma_cmd&~1, hwif->dma_command); /* get DMA status */ - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = hwif->read_sff_dma_status(hwif); /* clear the INTR & ERROR bits */ hwif->OUTB(dma_stat|6, hwif->dma_status); /* purge DMA mappings */ @@ -537,7 +537,7 @@ EXPORT_SYMBOL(__ide_dma_end); int ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->read_sff_dma_status(hwif); /* return 1 if INTR asserted */ if ((dma_stat & 4) == 4) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 44aaec256a30..a09bf4369ed8 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -103,6 +103,14 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } +static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->dma_status); + else + return inb(hwif->dma_status); +} + static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; @@ -323,6 +331,8 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, void default_hwif_transport(ide_hwif_t *hwif) { + hwif->read_sff_dma_status = ide_read_sff_dma_status; + hwif->tf_load = ide_tf_load; hwif->tf_read = ide_tf_read; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 45ba71a7182f..9ffdbb89df5c 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -63,6 +63,11 @@ static u8 superio_ide_inb (unsigned long port) return inb(port); } +static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->dma_status); +} + static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) { struct ide_io_ports *io_ports = &drive->hwif->io_ports; @@ -122,6 +127,8 @@ static void __devinit superio_ide_init_iops (struct hwif_s *hwif) tmp = superio_ide_inb(superio_ide_dma_status[port]); outb(tmp | 0x66, superio_ide_dma_status[port]); + hwif->read_sff_dma_status = superio_read_sff_dma_status; + hwif->tf_read = superio_tf_read; /* We need to override inb to workaround a SuperIO errata */ @@ -200,13 +207,13 @@ static int ns87415_dma_end(ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = hwif->read_sff_dma_status(hwif); /* get dma command mode */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_cmd = inb(hwif->dma_command); /* stop DMA */ outb(dma_cmd & ~1, hwif->dma_command); /* from ERRATA: clear the INTR & ERROR bits */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_cmd = inb(hwif->dma_command); outb(dma_cmd | 6, hwif->dma_command); /* and free any DMA resources */ ide_destroy_dmatable(drive); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 328e2df66550..7a2a7b2a319a 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -126,6 +126,11 @@ static u8 scc_ide_inb(unsigned long port) return (u8)data; } +static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->dma_status); +} + static void scc_ide_insw(unsigned long port, void *addr, u32 count) { u16 *ptr = (u16 *)addr; @@ -773,6 +778,8 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); + hwif->read_sff_dma_status = scc_read_sff_dma_status; + hwif->tf_load = scc_tf_load; hwif->tf_read = scc_tf_read; diff --git a/include/linux/ide.h b/include/linux/ide.h index f58548becac0..ca0efbb0a8b4 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -489,6 +489,8 @@ typedef struct hwif_s { const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; + u8 (*read_sff_dma_status)(struct hwif_s *); + void (*tf_load)(ide_drive_t *, struct ide_task_s *); void (*tf_read)(ide_drive_t *, struct ide_task_s *); -- cgit v1.2.3 From cab7f8eda40d3e3e16b137c67cdddc2cf893c5d7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: remove ->dma_{status,command} fields from ide_hwif_t * Use ->dma_base + offset instead of ->dma_{status,command} and remove no longer needed ->dma_{status,command}. While at it: * Use ATA_DMA_* defines. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 42 +++++++++++++++++++++--------------------- drivers/ide/ide-iops.c | 4 ++-- drivers/ide/pci/cmd64x.c | 12 ++++++------ drivers/ide/pci/hpt366.c | 10 +++++----- drivers/ide/pci/ns87415.c | 14 +++++++------- drivers/ide/pci/pdc202xx_old.c | 2 +- drivers/ide/pci/piix.c | 4 ++-- drivers/ide/pci/scc_pata.c | 38 ++++++++++++++++++-------------------- drivers/ide/pci/siimage.c | 4 ++-- drivers/ide/pci/sl82c105.c | 4 ++-- drivers/ide/pci/tc86c001.c | 13 ++++++++----- include/linux/ide.h | 2 -- 12 files changed, 74 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index f3229642c052..d98a9da2699c 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -377,9 +377,10 @@ void ide_dma_host_set(ide_drive_t *drive, int on) dma_stat &= ~(1 << (5 + unit)); if (hwif->host_flags & IDE_HFLAG_MMIO) - writeb(dma_stat, (void __iomem *)hwif->dma_status); + writeb(dma_stat, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - outb(dma_stat, hwif->dma_status); + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); } EXPORT_SYMBOL_GPL(ide_dma_host_set); @@ -475,18 +476,19 @@ int ide_dma_setup(ide_drive_t *drive) /* specify r/w */ if (mmio) - writeb(reading, (void __iomem *)hwif->dma_command); + writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); else - outb(reading, hwif->dma_command); + outb(reading, hwif->dma_base + ATA_DMA_CMD); /* read DMA status for INTR & ERROR flags */ dma_stat = hwif->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ if (mmio) - writeb(dma_stat | 6, (void __iomem *)hwif->dma_status); + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); drive->waiting_for_dma = 1; return 0; @@ -512,12 +514,13 @@ void ide_dma_start(ide_drive_t *drive) * we do this part before issuing the drive cmd. */ if (hwif->host_flags & IDE_HFLAG_MMIO) { - dma_cmd = readb((void __iomem *)hwif->dma_command); + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); /* start DMA */ - writeb(dma_cmd | 1, (void __iomem *)hwif->dma_command); + writeb(dma_cmd | 1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); } else { - dma_cmd = inb(hwif->dma_command); - outb(dma_cmd | 1, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD); } hwif->dma = 1; @@ -537,12 +540,13 @@ int __ide_dma_end (ide_drive_t *drive) if (mmio) { /* get DMA command mode */ - dma_cmd = readb((void __iomem *)hwif->dma_command); + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); /* stop DMA */ - writeb(dma_cmd & ~1, (void __iomem *)hwif->dma_command); + writeb(dma_cmd & ~1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); } else { - dma_cmd = inb(hwif->dma_command); - outb(dma_cmd & ~1, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); } /* get DMA status */ @@ -550,9 +554,10 @@ int __ide_dma_end (ide_drive_t *drive) if (mmio) /* clear the INTR & ERROR bits */ - writeb(dma_stat | 6, (void __iomem *)hwif->dma_status); + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); /* purge DMA mappings */ ide_destroy_dmatable(drive); @@ -888,11 +893,6 @@ void ide_setup_dma(ide_hwif_t *hwif, unsigned long base) { hwif->dma_base = base; - if (!hwif->dma_command) - hwif->dma_command = hwif->dma_base + 0; - if (!hwif->dma_status) - hwif->dma_status = hwif->dma_base + 2; - hwif->dma_ops = &sff_dma_ops; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index a09bf4369ed8..17cad6c39ee3 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -106,9 +106,9 @@ void SELECT_MASK(ide_drive_t *drive, int mask) static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) - return readb((void __iomem *)hwif->dma_status); + return readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - return inb(hwif->dma_status); + return inb(hwif->dma_base + ATA_DMA_STATUS); } static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index cfa784bacf48..ce58bfcdb3c6 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -262,7 +262,7 @@ static int cmd648_dma_test_irq(ide_drive_t *drive) unsigned long base = hwif->dma_base - (hwif->channel * 8); u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 mrdmode = inb(base + 1); #ifdef DEBUG @@ -286,7 +286,7 @@ static int cmd64x_dma_test_irq(ide_drive_t *drive) int irq_reg = hwif->channel ? ARTTIM23 : CFR; u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 irq_stat = 0; (void) pci_read_config_byte(dev, irq_reg, &irq_stat); @@ -317,13 +317,13 @@ static int cmd646_1_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA status */ - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* read DMA command state */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* clear the INTR & ERROR bits */ - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 397c6cbe953c..d2f470ec8055 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -801,9 +801,9 @@ static void hpt370_irq_timeout(ide_drive_t *drive) printk(KERN_DEBUG "%s: %d bytes in FIFO\n", drive->name, bfifo & 0x1ff); /* get DMA command mode */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~0x1, hwif->dma_command); + outb(dma_cmd & ~0x1, hwif->dma_base + ATA_DMA_CMD); hpt370_clear_engine(drive); } @@ -818,12 +818,12 @@ static void hpt370_dma_start(ide_drive_t *drive) static int hpt370_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) { /* wait a little */ udelay(20); - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) hpt370_irq_timeout(drive); } @@ -850,7 +850,7 @@ static int hpt374_dma_test_irq(ide_drive_t *drive) return 0; } - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* return 1 if INTR asserted */ if (dma_stat & 4) return 1; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 9ffdbb89df5c..76ce112fd857 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -65,7 +65,7 @@ static u8 superio_ide_inb (unsigned long port) static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) { - return superio_ide_inb(hwif->dma_status); + return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); } static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) @@ -208,13 +208,13 @@ static int ns87415_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; dma_stat = hwif->read_sff_dma_status(hwif); - /* get dma command mode */ - dma_cmd = inb(hwif->dma_command); + /* get DMA command mode */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* from ERRATA: clear the INTR & ERROR bits */ - dma_cmd = inb(hwif->dma_command); - outb(dma_cmd | 6, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -298,7 +298,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) if (!hwif->dma_base) return; - outb(0x60, hwif->dma_status); + outb(0x60, hwif->dma_base + ATA_DMA_STATUS); } static const struct ide_port_ops ns87415_port_ops = { diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index fca89eda5c02..1c2f9df31129 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -206,7 +206,7 @@ static int pdc202xx_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); unsigned long high_16 = hwif->extra_base - 16; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 sc1d = inb(high_16 + 0x001d); if (hwif->channel) { diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index f04738d14a6f..0ce41b4dddaf 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -227,9 +227,9 @@ static void piix_dma_clear_irq(ide_drive_t *drive) u8 dma_stat; /* clear the INTR & ERROR bits */ - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Should we force the bit as well ? */ - outb(dma_stat, hwif->dma_status); + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); } struct ich_laptop { diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 7a2a7b2a319a..fc163a7772a7 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -128,7 +128,7 @@ static u8 scc_ide_inb(unsigned long port) static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { - return (u8)in_be32((void *)hwif->dma_status); + return (u8)in_be32((void *)(hwif->dma_base + 4)); } static void scc_ide_insw(unsigned long port, void *addr, u32 count) @@ -266,14 +266,14 @@ static void scc_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = drive->hwif; u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = scc_ide_inb(hwif->dma_status); + u8 dma_stat = scc_ide_inb(hwif->dma_base + 4); if (on) dma_stat |= (1 << (5 + unit)); else dma_stat &= ~(1 << (5 + unit)); - scc_ide_outb(dma_stat, hwif->dma_status); + scc_ide_outb(dma_stat, hwif->dma_base + 4); } /** @@ -309,13 +309,13 @@ static int scc_dma_setup(ide_drive_t *drive) out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma); /* specify r/w */ - out_be32((void __iomem *)hwif->dma_command, reading); + out_be32((void __iomem *)hwif->dma_base, reading); - /* read dma_status for INTR & ERROR flags */ - dma_stat = in_be32((void __iomem *)hwif->dma_status); + /* read DMA status for INTR & ERROR flags */ + dma_stat = in_be32((void __iomem *)(hwif->dma_base + 4)); /* clear INTR & ERROR flags */ - out_be32((void __iomem *)hwif->dma_status, dma_stat|6); + out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6); drive->waiting_for_dma = 1; return 0; } @@ -323,10 +323,10 @@ static int scc_dma_setup(ide_drive_t *drive) static void scc_dma_start(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 dma_cmd = scc_ide_inb(hwif->dma_command); + u8 dma_cmd = scc_ide_inb(hwif->dma_base); /* start DMA */ - scc_ide_outb(dma_cmd | 1, hwif->dma_command); + scc_ide_outb(dma_cmd | 1, hwif->dma_base); hwif->dma = 1; wmb(); } @@ -338,13 +338,13 @@ static int __scc_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA command mode */ - dma_cmd = scc_ide_inb(hwif->dma_command); + dma_cmd = scc_ide_inb(hwif->dma_base); /* stop DMA */ - scc_ide_outb(dma_cmd & ~1, hwif->dma_command); + scc_ide_outb(dma_cmd & ~1, hwif->dma_base); /* get DMA status */ - dma_stat = scc_ide_inb(hwif->dma_status); + dma_stat = scc_ide_inb(hwif->dma_base + 4); /* clear the INTR & ERROR bits */ - scc_ide_outb(dma_stat | 6, hwif->dma_status); + scc_ide_outb(dma_stat | 6, hwif->dma_base + 4); /* purge DMA mappings */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -364,6 +364,7 @@ static int __scc_dma_end(ide_drive_t *drive) static int scc_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + void __iomem *dma_base = (void __iomem *)hwif->dma_base; unsigned long intsts_port = hwif->dma_base + 0x014; u32 reg; int dma_stat, data_loss = 0; @@ -402,7 +403,7 @@ static int scc_dma_end(ide_drive_t *drive) printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT); - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } @@ -417,7 +418,7 @@ static int scc_dma_end(ide_drive_t *drive) out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT); - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } @@ -425,12 +426,12 @@ static int scc_dma_end(ide_drive_t *drive) printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT); - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } if (reg & INTSTS_ICERR) { - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT); @@ -832,9 +833,6 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) ports->hwif = hwif; - hwif->dma_command = hwif->dma_base; - hwif->dma_status = hwif->dma_base + 0x04; - /* PTERADD */ out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma); diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 6e9d7655d89c..21d7137f7d6c 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -334,7 +334,7 @@ static int siimage_io_dma_test_irq(ide_drive_t *drive) unsigned long addr = siimage_selreg(hwif, 1); /* return 1 if INTR asserted */ - if (hwif->INB(hwif->dma_status) & 4) + if (inb(hwif->dma_base + ATA_DMA_STATUS) & 4) return 1; /* return 1 if Device INTR asserted */ @@ -382,7 +382,7 @@ static int siimage_mmio_dma_test_irq(ide_drive_t *drive) } /* return 1 if INTR asserted */ - if (readb((void __iomem *)hwif->dma_status) & 0x04) + if (readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)) & 4) return 1; /* return 1 if Device INTR asserted */ diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 6efbde297174..f82a6502c1b7 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -157,9 +157,9 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive) * Was DMA enabled? If so, disable it - we're resetting the * host. The IDE layer will be handling the drive for us. */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); if (dma_cmd & 1) { - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); printk("sl82c105: DMA was enabled\n"); } diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index 9b4b27a4c711..a81d47c55ce1 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -63,7 +63,7 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); ide_expiry_t *expiry = ide_get_hwifdata(hwif); ide_hwgroup_t *hwgroup = HWGROUP(drive); - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Restore a higher level driver's expiry handler first. */ hwgroup->expiry = expiry; @@ -71,21 +71,24 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */ unsigned long sc_base = hwif->config_data; unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); - u8 dma_cmd = inb(hwif->dma_command); + u8 dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); printk(KERN_WARNING "%s: DMA interrupt possibly stuck, " "attempting recovery...\n", drive->name); /* Stop DMA */ - outb(dma_cmd & ~0x01, hwif->dma_command); + outb(dma_cmd & ~0x01, hwif->dma_base + ATA_DMA_CMD); /* Setup the dummy DMA transfer */ outw(0, sc_base + 0x0a); /* Sector Count */ outw(0, twcr_port); /* Transfer Word Count 1 or 2 */ /* Start the dummy DMA transfer */ - outb(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */ - outb(0x01, hwif->dma_command); /* set START_STOPBM */ + + /* clear R_OR_WCTR for write */ + outb(0x00, hwif->dma_base + ATA_DMA_CMD); + /* set START_STOPBM */ + outb(0x01, hwif->dma_base + ATA_DMA_CMD); /* * If an interrupt was pending, it should come thru shortly. diff --git a/include/linux/ide.h b/include/linux/ide.h index ca0efbb0a8b4..85a32f472ef0 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -526,8 +526,6 @@ typedef struct hwif_s { int irq; /* our irq number */ unsigned long dma_base; /* base addr for dma ports */ - unsigned long dma_command; /* dma command register */ - unsigned long dma_status; /* dma status register */ unsigned long config_data; /* for use by chipset-specific code */ unsigned long select_data; /* for use by chipset-specific code */ -- cgit v1.2.3 From 81e8d5a34f7d2a2acbe309cfa5810a9699a63239 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: remove ide_setup_dma() Export sff_dma_ops and then remove ide_setup_dma(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/palm_bk3710.c | 7 +++---- drivers/ide/ide-dma.c | 12 ++---------- drivers/ide/pci/alim15x3.c | 4 +++- drivers/ide/pci/hpt366.c | 4 +++- drivers/ide/setup-pci.c | 4 +++- include/linux/ide.h | 2 +- 6 files changed, 15 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 023c10753f15..0229d794d909 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -316,15 +316,14 @@ static u8 __devinit palm_bk3710_cable_detect(ide_hwif_t *hwif) static int __devinit palm_bk3710_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) { - unsigned long base = - hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; - printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; + + hwif->dma_ops = &sff_dma_ops; return 0; } diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index d98a9da2699c..ac342ebf6c54 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -878,7 +878,7 @@ int ide_allocate_dma_engine(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); -static const struct ide_dma_ops sff_dma_ops = { +const struct ide_dma_ops sff_dma_ops = { .dma_host_set = ide_dma_host_set, .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, @@ -888,13 +888,5 @@ static const struct ide_dma_ops sff_dma_ops = { .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, }; - -void ide_setup_dma(ide_hwif_t *hwif, unsigned long base) -{ - hwif->dma_base = base; - - hwif->dma_ops = &sff_dma_ops; -} - -EXPORT_SYMBOL_GPL(ide_setup_dma); +EXPORT_SYMBOL_GPL(sff_dma_ops); #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 80d19c0eb780..8015f6f65488 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -483,7 +483,9 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = base; + + hwif->dma_ops = &sff_dma_ops; return 0; } diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index d2f470ec8055..201e5ddae921 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1346,7 +1346,9 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = base; + + hwif->dma_ops = &sff_dma_ops; return 0; } diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index acb467c6f345..b047013f3652 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -377,7 +377,9 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = base; + + hwif->dma_ops = &sff_dma_ops; } return 0; diff --git a/include/linux/ide.h b/include/linux/ide.h index 85a32f472ef0..6d774607e32a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1007,6 +1007,7 @@ void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI int ide_pci_set_master(struct pci_dev *, const char *); unsigned long ide_pci_dma_base(ide_hwif_t *, const struct ide_port_info *); +extern const struct ide_dma_ops sff_dma_ops; int ide_hwif_setup_dma(ide_hwif_t *, const struct ide_port_info *); #else static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, @@ -1164,7 +1165,6 @@ void ide_destroy_dmatable(ide_drive_t *); extern int ide_build_dmatable(ide_drive_t *, struct request *); int ide_allocate_dma_engine(ide_hwif_t *); void ide_release_dma_engine(ide_hwif_t *); -void ide_setup_dma(ide_hwif_t *, unsigned long); void ide_dma_host_set(ide_drive_t *, int); extern int ide_dma_setup(ide_drive_t *); -- cgit v1.2.3 From ebb00fb55d0566bb3e81518122a57b4b3bedf1e4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: factor out simplex handling from ide_pci_dma_base() * Factor out simplex handling from ide_pci_dma_base() to ide_pci_check_simplex(). * Set hwif->dma_base early in ->init_dma method / ide_hwif_setup_dma() and reset it in ide_init_port() if DMA initialization fails. * Use ->read_sff_dma_status instead of ->INB in ide_pci_dma_base(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 1 + drivers/ide/pci/alim15x3.c | 12 +++++++++--- drivers/ide/pci/hpt366.c | 12 +++++++++--- drivers/ide/setup-pci.c | 35 +++++++++++++++++++++++------------ include/linux/ide.h | 1 + 5 files changed, 43 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9d8686a49fcf..ec5f58c6f19a 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1363,6 +1363,7 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, if (rc < 0) { printk(KERN_INFO "%s: DMA disabled\n", hwif->name); + hwif->dma_base = 0; hwif->swdma_mask = 0; hwif->mwdma_mask = 0; hwif->ultra_mask = 0; diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 8015f6f65488..5ef7817ac64f 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -471,7 +471,15 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long base = ide_pci_dma_base(hwif, d); - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; if (!hwif->channel) @@ -483,8 +491,6 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - hwif->dma_base = base; - hwif->dma_ops = &sff_dma_ops; return 0; diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 201e5ddae921..e5651cefa395 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1320,7 +1320,15 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long flags, base = ide_pci_dma_base(hwif, d); u8 dma_old, dma_new, masterdma = 0, slavedma = 0; - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; dma_old = inb(base + 2); @@ -1346,8 +1354,6 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - hwif->dma_base = base; - hwif->dma_ops = &sff_dma_ops; return 0; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index b047013f3652..c1b609d9cb28 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -73,15 +73,12 @@ static void ide_pci_clear_simplex(unsigned long dma_base, const char *name) * @d: IDE port info * * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space. - * Where a device has a partner that is already in DMA mode we check - * and enforce IDE simplex rules. */ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) { struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long dma_base = 0; - u8 dma_stat = 0; if (hwif->host_flags & IDE_HFLAG_MMIO) return hwif->dma_base; @@ -102,11 +99,19 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) if (hwif->channel) dma_base += 8; - if (d->host_flags & IDE_HFLAG_CS5520) + return dma_base; +} +EXPORT_SYMBOL_GPL(ide_pci_dma_base); + +int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + u8 dma_stat; + + if (d->host_flags & (IDE_HFLAG_MMIO | IDE_HFLAG_CS5520)) goto out; if (d->host_flags & IDE_HFLAG_CLEAR_SIMPLEX) { - ide_pci_clear_simplex(dma_base, d->name); + ide_pci_clear_simplex(hwif->dma_base, d->name); goto out; } @@ -120,15 +125,15 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) * we tune the drive then try to grab DMA ownership if we want to be * the DMA end. This has to be become dynamic to handle hot-plug. */ - dma_stat = hwif->INB(dma_base + 2); + dma_stat = hwif->read_sff_dma_status(hwif); if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { printk(KERN_INFO "%s: simplex device: DMA disabled\n", d->name); - dma_base = 0; + return -1; } out: - return dma_base; + return 0; } -EXPORT_SYMBOL_GPL(ide_pci_dma_base); +EXPORT_SYMBOL_GPL(ide_pci_check_simplex); /* * Set up BM-DMA capability (PnP BIOS should have done this) @@ -363,7 +368,15 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) (dev->class & 0x80))) { unsigned long base = ide_pci_dma_base(hwif, d); - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -377,8 +390,6 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (ide_allocate_dma_engine(hwif)) return -1; - hwif->dma_base = base; - hwif->dma_ops = &sff_dma_ops; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 6d774607e32a..a179a7f6e444 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1008,6 +1008,7 @@ void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); int ide_pci_set_master(struct pci_dev *, const char *); unsigned long ide_pci_dma_base(ide_hwif_t *, const struct ide_port_info *); extern const struct ide_dma_ops sff_dma_ops; +int ide_pci_check_simplex(ide_hwif_t *, const struct ide_port_info *); int ide_hwif_setup_dma(ide_hwif_t *, const struct ide_port_info *); #else static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, -- cgit v1.2.3 From c6dfa867bb45f4bff2e48f3bc89ab1d6a7ab4c21 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: add ->exec_command method Add ->exec_command method for writing ATA Command register and use it instead of ->OUTBSYNC. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 3 +-- drivers/ide/ide-iops.c | 19 ++++++++++++++----- drivers/ide/ide-probe.c | 6 +++--- drivers/ide/ide-taskfile.c | 2 +- drivers/ide/pci/scc_pata.c | 9 +++++++++ drivers/ide/ppc/pmac.c | 9 +++++++++ drivers/scsi/ide-scsi.c | 3 +-- include/linux/ide.h | 3 ++- 8 files changed, 40 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 661b75a89d4d..ca0c04a919cb 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -437,8 +437,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, - hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); if (rq->errors >= ERROR_MAX) { ide_kill_rq(drive, rq); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 17cad6c39ee3..d8db25581909 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -103,6 +103,14 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } +static void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + else + outb(cmd, hwif->io_ports.command_addr); +} + static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -331,6 +339,7 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, void default_hwif_transport(ide_hwif_t *hwif) { + hwif->exec_command = ide_exec_command; hwif->read_sff_dma_status = ide_read_sff_dma_status; hwif->tf_load = ide_tf_load; @@ -696,7 +705,7 @@ int ide_driveid_update(ide_drive_t *drive) SELECT_MASK(drive, 1); ide_set_irq(drive, 0); msleep(50); - hwif->OUTBSYNC(hwif, WIN_IDENTIFY, hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; do { if (time_after(jiffies, timeout)) { @@ -783,7 +792,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) ide_set_irq(drive, 0); hwif->OUTB(speed, io_ports->nsect_addr); hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr); - hwif->OUTBSYNC(hwif, WIN_SETFEATURES, io_ports->command_addr); + hwif->exec_command(hwif, WIN_SETFEATURES); if (drive->quirk_list == 2) ide_set_irq(drive, 1); @@ -891,7 +900,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, spin_lock_irqsave(&ide_lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); + hwif->exec_command(hwif, cmd); /* * Drive takes 400nS to respond, we must avoid the IRQ being * serviced before that. @@ -909,7 +918,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - hwif->OUTBSYNC(hwif, WIN_PACKETCMD, hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_PACKETCMD); ndelay(400); spin_unlock_irqrestore(&ide_lock, flags); } @@ -1116,7 +1125,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); + hwif->exec_command(hwif, WIN_SRST); ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index ec5f58c6f19a..d1e834a1c5cd 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -295,7 +295,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) hwif->OUTB(0, io_ports->feature_addr); /* ask drive for ID */ - hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); + hwif->exec_command(hwif, cmd); timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; @@ -482,7 +482,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); + hwif->exec_command(hwif, WIN_SRST); (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } @@ -518,7 +518,7 @@ static void enable_nest (ide_drive_t *drive) printk("%s: enabling %s -- ", hwif->name, drive->id->model); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(hwif, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr); + hwif->exec_command(hwif, EXABYTE_ENABLE_NEST); if (ide_busy_sleep(hwif)) { printk(KERN_CONT "failed (timeout)\n"); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 1fbdb746dc88..c56be289417e 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -88,7 +88,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: - hwif->OUTBSYNC(hwif, tf->command, hwif->io_ports.command_addr); + hwif->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index fc163a7772a7..d140dfd565a4 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -126,6 +126,14 @@ static u8 scc_ide_inb(unsigned long port) return (u8)data; } +static void scc_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + out_be32((void *)hwif->io_ports.command_addr, cmd); + eieio(); + in_be32((void *)(hwif->dma_base + 0x01c)); + eieio(); +} + static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); @@ -779,6 +787,7 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); + hwif->exec_command = scc_exec_command; hwif->read_sff_dma_status = scc_read_sff_dma_status; hwif->tf_load = scc_tf_load; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index e68e33bb2c35..7c3a84f8fbed 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -495,6 +495,13 @@ static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) + IDE_TIMING_CONFIG)); } +static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + /* * Old tuning functions (called on hdparm -p), sets up drive PIO timings */ @@ -1092,6 +1099,8 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) if (hwif == NULL) return -ENOENT; + hwif->exec_command = pmac_exec_command; + /* Setup MMIO ops */ default_hwif_mmiops(hwif); hwif->OUTBSYNC = pmac_outbsync; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index f843c1383a4b..80123890ced0 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -248,8 +248,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, - hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); rq->errors++; diff --git a/include/linux/ide.h b/include/linux/ide.h index a179a7f6e444..d77c1994d0e3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -489,7 +489,8 @@ typedef struct hwif_s { const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; - u8 (*read_sff_dma_status)(struct hwif_s *); + void (*exec_command)(struct hwif_s *, u8); + u8 (*read_sff_dma_status)(struct hwif_s *); void (*tf_load)(ide_drive_t *, struct ide_task_s *); void (*tf_read)(ide_drive_t *, struct ide_task_s *); -- cgit v1.2.3 From b73c7ee25da6133f97f47ffd3557288417da7c76 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:52 +0200 Subject: ide: add ->read_status method * Remove ide_read_status() inline helper. * Add ->read_status method for reading ATA Status register and use it instead of ->INB. While at it: * Don't use HWGROUP() macro. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 4 +++- drivers/ide/ide-atapi.c | 2 +- drivers/ide/ide-cd.c | 12 ++++++++---- drivers/ide/ide-dma.c | 5 +++-- drivers/ide/ide-floppy.c | 3 ++- drivers/ide/ide-io.c | 19 ++++++++++--------- drivers/ide/ide-iops.c | 33 ++++++++++++++++++++++----------- drivers/ide/ide-probe.c | 22 +++++++++++----------- drivers/ide/ide-tape.c | 6 ++++-- drivers/ide/ide-taskfile.c | 23 ++++++++++++++--------- drivers/ide/pci/ns87415.c | 8 +++++++- drivers/ide/pci/scc_pata.c | 6 ++++++ drivers/ide/pci/sgiioc4.c | 19 ++++++++++--------- drivers/scsi/ide-scsi.c | 2 +- include/linux/ide.h | 8 +------- 15 files changed, 103 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 850fe9342a1f..f73422dd8474 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -375,12 +375,14 @@ static int icside_dma_test_irq(ide_drive_t *drive) static void icside_dma_timeout(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; + printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); if (icside_dma_test_irq(drive)) return; - ide_dump_status(drive, "DMA timeout", ide_read_status(drive)); + ide_dump_status(drive, "DMA timeout", hwif->read_status(hwif)); icside_dma_end(drive); } diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2802031de670..6554c4225a05 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -35,7 +35,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Clear the interrupt */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 6e29dd532090..82879a1a89e5 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -280,11 +280,12 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st) */ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) { - struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->hwgroup->rq; int stat, err, sense_key; /* check for errors */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat_ret) *stat_ret = stat; @@ -606,6 +607,8 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, int len, int ireason, int rw) { + ide_hwif_t *hwif = drive->hwif; + /* * ireason == 0: the drive wants to receive data from us * ireason == 2: the drive is expecting to transfer data to us @@ -624,7 +627,7 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, * Some drives (ASUS) seem to tell us that status info is * available. Just get it and ignore. */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); return 0; } else { /* drive wants a command packet, or invalid ireason... */ @@ -1199,8 +1202,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (blk_fs_request(rq)) { if (info->cd_flags & IDE_CD_FLAG_SEEKING) { + ide_hwif_t *hwif = drive->hwif; unsigned long elapsed = jiffies - info->start_seek; - int stat = ide_read_status(drive); + int stat = hwif->read_status(hwif); if ((stat & SEEK_STAT) != SEEK_STAT) { if (elapsed < IDECD_SEEK_TIMEOUT) { diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index ac342ebf6c54..ecf60dbbe3e9 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -100,10 +100,11 @@ static const struct drive_list_entry drive_blacklist [] = { ide_startstop_t ide_dma_intr (ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; u8 stat = 0, dma_stat = 0; - dma_stat = drive->hwif->dma_ops->dma_end(drive); - stat = ide_read_status(drive); + dma_stat = hwif->dma_ops->dma_end(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { if (!dma_stat) { diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 011d72011cc4..6f5294cfff23 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -949,11 +949,12 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) /* Else assume format_unit has finished, and we're at 0x10000 */ } else { + ide_hwif_t *hwif = drive->hwif; unsigned long flags; u8 stat; local_irq_save(flags); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); local_irq_restore(flags); progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ca0c04a919cb..fdc221ce9920 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -381,8 +381,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 if (err == ABRT_ERR) { if (drive->select.b.lba && /* some newer drives don't support WIN_SPECIFY */ - hwif->INB(hwif->io_ports.command_addr) == - WIN_SPECIFY) + hwif->read_status(hwif) == WIN_SPECIFY) return ide_stopped; } else if ((err & BAD_CRC) == BAD_CRC) { /* UDMA crc error, just retry the operation */ @@ -408,7 +407,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 return ide_stopped; } - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) rq->errors |= ERROR_RESET; if ((rq->errors & ERROR_RESET) == ERROR_RESET) { @@ -435,7 +434,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u /* add decoding error stuff */ } - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); @@ -711,7 +710,8 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, #ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", drive->name); #endif - ide_end_drive_cmd(drive, ide_read_status(drive), ide_read_error(drive)); + ide_end_drive_cmd(drive, hwif->read_status(hwif), + ide_read_error(drive)); return ide_stopped; } @@ -1141,7 +1141,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); (void)hwif->dma_ops->dma_end(drive); ret = ide_error(drive, "dma timeout error", - ide_read_status(drive)); + hwif->read_status(hwif)); } else { printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); hwif->dma_ops->dma_timeout(drive); @@ -1266,7 +1266,7 @@ void ide_timer_expiry (unsigned long data) } else startstop = ide_error(drive, "irq timeout", - ide_read_status(drive)); + hwif->read_status(hwif)); } drive->service_time = jiffies - drive->service_start; spin_lock_irq(&ide_lock); @@ -1322,7 +1322,8 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) */ do { if (hwif->irq == irq) { - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { /* Try to not flood the console with msgs */ static unsigned long last_msgtime, count; @@ -1412,7 +1413,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * Whack the status register, just in case * we have a leftover pending IRQ. */ - (void) hwif->INB(hwif->io_ports.status_addr); + (void)hwif->read_status(hwif); #endif /* CONFIG_BLK_DEV_IDEPCI */ } spin_unlock_irqrestore(&ide_lock, flags); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index d8db25581909..086eceaeeafd 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -111,6 +111,14 @@ static void ide_exec_command(ide_hwif_t *hwif, u8 cmd) outb(cmd, hwif->io_ports.command_addr); } +static u8 ide_read_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.status_addr); + else + return inb(hwif->io_ports.status_addr); +} + static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -340,6 +348,7 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, void default_hwif_transport(ide_hwif_t *hwif) { hwif->exec_command = ide_exec_command; + hwif->read_status = ide_read_status; hwif->read_sff_dma_status = ide_read_sff_dma_status; hwif->tf_load = ide_tf_load; @@ -505,7 +514,7 @@ int drive_is_ready (ide_drive_t *drive) stat = ide_read_altstatus(drive); else /* Note: this may clear a pending IRQ!! */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & BUSY_STAT) /* drive busy: definitely not interrupting */ @@ -530,24 +539,25 @@ EXPORT_SYMBOL(drive_is_ready); */ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout, u8 *rstat) { + ide_hwif_t *hwif = drive->hwif; unsigned long flags; int i; u8 stat; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & BUSY_STAT) { local_irq_set(flags); timeout += jiffies; - while ((stat = ide_read_status(drive)) & BUSY_STAT) { + while ((stat = hwif->read_status(hwif)) & BUSY_STAT) { if (time_after(jiffies, timeout)) { /* * One last read after the timeout in case * heavy interrupt load made us not make any * progress during the timeout.. */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!(stat & BUSY_STAT)) break; @@ -567,7 +577,7 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti */ for (i = 0; i < 10; i++) { udelay(1); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat, good, bad)) { *rstat = stat; @@ -718,7 +728,7 @@ int ide_driveid_update(ide_drive_t *drive) } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { SELECT_MASK(drive, 0); @@ -733,7 +743,7 @@ int ide_driveid_update(ide_drive_t *drive) return 0; } hwif->input_data(drive, NULL, id, SECTOR_SIZE); - (void)ide_read_status(drive); /* clear drive IRQ */ + (void)hwif->read_status(hwif); /* clear drive IRQ */ local_irq_enable(); local_irq_restore(flags); ide_fix_driveid(id); @@ -943,12 +953,13 @@ static ide_startstop_t do_reset1 (ide_drive_t *, int); */ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) { - ide_hwgroup_t *hwgroup = HWGROUP(drive); + ide_hwif_t *hwif = drive->hwif; + ide_hwgroup_t *hwgroup = hwif->hwgroup; u8 stat; SELECT_DRIVE(drive); udelay (10); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat, 0, BUSY_STAT)) printk("%s: ATAPI reset complete\n", drive->name); @@ -994,7 +1005,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } - tmp = ide_read_status(drive); + tmp = hwif->read_status(hwif); if (!OK_STAT(tmp, 0, BUSY_STAT)) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -1208,7 +1219,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) * about locking issues (2.5 work ?). */ mdelay(1); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; /* diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index d1e834a1c5cd..c42fcfedcbf6 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -276,7 +276,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) if (io_ports->ctl_addr) { a = ide_read_altstatus(drive); - s = ide_read_status(drive); + s = hwif->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ printk(KERN_INFO "%s: probing with STATUS(0x%02x) " @@ -307,12 +307,12 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* give drive a breather */ msleep(50); s = use_altstatus ? ide_read_altstatus(drive) - : ide_read_status(drive); + : hwif->read_status(hwif); } while (s & BUSY_STAT); /* wait for IRQ and DRQ_STAT */ msleep(50); - s = ide_read_status(drive); + s = hwif->read_status(hwif); if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) { unsigned long flags; @@ -324,7 +324,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* drive responded with ID */ rc = 0; /* clear drive IRQ */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); local_irq_restore(flags); } else { /* drive refused ID */ @@ -371,7 +371,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) ide_set_irq(drive, 0); /* clear drive IRQ */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); udelay(5); irq = probe_irq_off(cookie); if (!hwif->irq) { @@ -396,7 +396,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) do { msleep(50); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; } while (time_before(jiffies, timeout)); @@ -461,7 +461,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) return 3; } - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat, READY_STAT, BUSY_STAT) || drive->present || cmd == WIN_PIDENTIFY) { @@ -471,7 +471,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) rc = try_to_identify(drive,cmd); } - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat == (BUSY_STAT | READY_STAT)) return 4; @@ -488,7 +488,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) } /* ensure drive IRQ is clear */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (rc == 1) printk(KERN_ERR "%s: no response (status = 0x%02x)\n", @@ -502,7 +502,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(&hwif->drives[0]); msleep(50); /* ensure drive irq is clear */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); } return rc; } @@ -527,7 +527,7 @@ static void enable_nest (ide_drive_t *drive) msleep(50); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!OK_STAT(stat, 0, BAD_STAT)) printk(KERN_CONT "failed (status = 0x%02x)\n", stat); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 353dd11b9283..ef54728a74b0 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -927,11 +927,12 @@ static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = tape->pc; u8 stat; - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & SEEK_STAT) { if (stat & ERR_STAT) { @@ -980,6 +981,7 @@ static void ide_tape_create_rw_cmd(idetape_tape_t *tape, static ide_startstop_t idetape_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { + ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = NULL; struct request *postponed_rq = tape->postponed_rq; @@ -1017,7 +1019,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, * If the tape is still busy, postpone our request and service * the other device meanwhile. */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2)) set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index c56be289417e..fc2b3957afac 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -124,7 +124,8 @@ EXPORT_SYMBOL_GPL(do_rw_taskfile); */ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) { - u8 stat = ide_read_status(drive); + ide_hwif_t *hwif = drive->hwif; + u8 stat = hwif->read_status(hwif); if (OK_STAT(stat, READY_STAT, BAD_STAT)) drive->mult_count = drive->mult_req; @@ -141,10 +142,11 @@ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) */ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; int retries = 5; u8 stat; - while (((stat = ide_read_status(drive)) & BUSY_STAT) && retries--) + while (((stat = hwif->read_status(hwif)) & BUSY_STAT) && retries--) udelay(10); if (OK_STAT(stat, READY_STAT, BAD_STAT)) @@ -162,7 +164,8 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) */ static ide_startstop_t recal_intr(ide_drive_t *drive) { - u8 stat = ide_read_status(drive); + ide_hwif_t *hwif = drive->hwif; + u8 stat = hwif->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "recal_intr", stat); @@ -174,11 +177,12 @@ static ide_startstop_t recal_intr(ide_drive_t *drive) */ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) { - ide_task_t *args = HWGROUP(drive)->rq->special; + ide_hwif_t *hwif = drive->hwif; + ide_task_t *args = hwif->hwgroup->rq->special; u8 stat; local_irq_enable_in_hardirq(); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); @@ -192,6 +196,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) static u8 wait_drive_not_busy(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; int retries; u8 stat; @@ -200,7 +205,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. */ for (retries = 0; retries < 1000; retries++) { - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & BUSY_STAT) udelay(10); @@ -383,8 +388,8 @@ static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq static ide_startstop_t task_in_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = HWGROUP(drive)->rq; - u8 stat = ide_read_status(drive); + struct request *rq = hwif->hwgroup->rq; + u8 stat = hwif->read_status(hwif); /* Error? */ if (stat & ERR_STAT) @@ -418,7 +423,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = HWGROUP(drive)->rq; - u8 stat = ide_read_status(drive); + u8 stat = hwif->read_status(hwif); if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) return task_error(drive, rq, __func__, stat); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 76ce112fd857..b20e5f01ac89 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -63,6 +63,11 @@ static u8 superio_ide_inb (unsigned long port) return inb(port); } +static u8 superio_read_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->io_ports.status_addr); +} + static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) { return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); @@ -127,6 +132,7 @@ static void __devinit superio_ide_init_iops (struct hwif_s *hwif) tmp = superio_ide_inb(superio_ide_dma_status[port]); outb(tmp | 0x66, superio_ide_dma_status[port]); + hwif->read_status = superio_read_status; hwif->read_sff_dma_status = superio_read_sff_dma_status; hwif->tf_read = superio_tf_read; @@ -283,7 +289,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) outb(8, hwif->io_ports.ctl_addr); do { udelay(50); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); if (stat == 0xff) break; } while ((stat & BUSY_STAT) && --timeout); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index d140dfd565a4..3d72a5e03f3d 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -134,6 +134,11 @@ static void scc_exec_command(ide_hwif_t *hwif, u8 cmd) eieio(); } +static u8 scc_read_status(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->io_ports.status_addr); +} + static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); @@ -788,6 +793,7 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); hwif->exec_command = scc_exec_command; + hwif->read_status = scc_read_status; hwif->read_sff_dma_status = scc_read_sff_dma_status; hwif->tf_load = scc_tf_load; diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 76afa1f9c599..3005a6334a58 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -127,7 +127,7 @@ sgiioc4_checkirq(ide_hwif_t * hwif) return 0; } -static u8 sgiioc4_INB(unsigned long); +static u8 sgiioc4_read_status(ide_hwif_t *); static int sgiioc4_clearirq(ide_drive_t * drive) @@ -141,18 +141,19 @@ sgiioc4_clearirq(ide_drive_t * drive) intr_reg = readl((void __iomem *)other_ir); if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */ /* - * Using sgiioc4_INB to read the Status register has a side - * effect of clearing the interrupt. The first read should + * Using sgiioc4_read_status to read the Status register has a + * side effect of clearing the interrupt. The first read should * clear it if it is set. The second read should return * a "clear" status if it got cleared. If not, then spin * for a bit trying to clear it. */ - u8 stat = sgiioc4_INB(io_ports->status_addr); + u8 stat = sgiioc4_read_status(hwif); int count = 0; - stat = sgiioc4_INB(io_ports->status_addr); + + stat = sgiioc4_read_status(hwif); while ((stat & 0x80) && (count++ < 100)) { udelay(1); - stat = sgiioc4_INB(io_ports->status_addr); + stat = sgiioc4_read_status(hwif); } if (intr_reg & 0x02) { @@ -304,9 +305,9 @@ sgiioc4_dma_lost_irq(ide_drive_t * drive) ide_dma_lost_irq(drive); } -static u8 -sgiioc4_INB(unsigned long port) +static u8 sgiioc4_read_status(ide_hwif_t *hwif) { + unsigned long port = hwif->io_ports.status_addr; u8 reg = (u8) readb((void __iomem *) port); if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ @@ -628,7 +629,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - hwif->INB = &sgiioc4_INB; + hwif->read_status = sgiioc4_read_status; idx[0] = hwif->index; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 80123890ced0..2a86af91f64a 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -246,7 +246,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { ide_hwif_t *hwif = drive->hwif; - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); diff --git a/include/linux/ide.h b/include/linux/ide.h index d77c1994d0e3..a23ae25d7da8 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -490,6 +490,7 @@ typedef struct hwif_s { const struct ide_dma_ops *dma_ops; void (*exec_command)(struct hwif_s *, u8); + u8 (*read_status)(struct hwif_s *); u8 (*read_sff_dma_status)(struct hwif_s *); void (*tf_load)(ide_drive_t *, struct ide_task_s *); @@ -1362,13 +1363,6 @@ static inline void ide_set_irq(ide_drive_t *drive, int on) hwif->io_ports.ctl_addr); } -static inline u8 ide_read_status(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - return hwif->INB(hwif->io_ports.status_addr); -} - static inline u8 ide_read_altstatus(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; -- cgit v1.2.3 From 1f6d8a0fd8f6cc5ee2219a8cf9b2da16dfd67397 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:52 +0200 Subject: ide: add ->read_altstatus method * Remove ide_read_altstatus() inline helper. * Add ->read_altstatus method for reading ATA Alternate Status register and use it instead of ->INB. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-iops.c | 13 +++++++++++-- drivers/ide/ide-probe.c | 4 ++-- drivers/ide/pci/scc_pata.c | 6 ++++++ include/linux/ide.h | 8 +------- 4 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 086eceaeeafd..e106954e13f9 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -119,6 +119,14 @@ static u8 ide_read_status(ide_hwif_t *hwif) return inb(hwif->io_ports.status_addr); } +static u8 ide_read_altstatus(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.ctl_addr); + else + return inb(hwif->io_ports.ctl_addr); +} + static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -349,6 +357,7 @@ void default_hwif_transport(ide_hwif_t *hwif) { hwif->exec_command = ide_exec_command; hwif->read_status = ide_read_status; + hwif->read_altstatus = ide_read_altstatus; hwif->read_sff_dma_status = ide_read_sff_dma_status; hwif->tf_load = ide_tf_load; @@ -511,7 +520,7 @@ int drive_is_ready (ide_drive_t *drive) * about possible isa-pnp and pci-pnp issues yet. */ if (hwif->io_ports.ctl_addr) - stat = ide_read_altstatus(drive); + stat = hwif->read_altstatus(hwif); else /* Note: this may clear a pending IRQ!! */ stat = hwif->read_status(hwif); @@ -724,7 +733,7 @@ int ide_driveid_update(ide_drive_t *drive) } msleep(50); /* give drive a breather */ - stat = ide_read_altstatus(drive); + stat = hwif->read_altstatus(hwif); } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index c42fcfedcbf6..fe14d576ef01 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -275,7 +275,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) msleep(50); if (io_ports->ctl_addr) { - a = ide_read_altstatus(drive); + a = hwif->read_altstatus(hwif); s = hwif->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ @@ -306,7 +306,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) } /* give drive a breather */ msleep(50); - s = use_altstatus ? ide_read_altstatus(drive) + s = use_altstatus ? hwif->read_altstatus(hwif) : hwif->read_status(hwif); } while (s & BUSY_STAT); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 3d72a5e03f3d..a89dc4780786 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -139,6 +139,11 @@ static u8 scc_read_status(ide_hwif_t *hwif) return (u8)in_be32((void *)hwif->io_ports.status_addr); } +static u8 scc_read_altstatus(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->io_ports.ctl_addr); +} + static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); @@ -794,6 +799,7 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->exec_command = scc_exec_command; hwif->read_status = scc_read_status; + hwif->read_altstatus = scc_read_altstatus; hwif->read_sff_dma_status = scc_read_sff_dma_status; hwif->tf_load = scc_tf_load; diff --git a/include/linux/ide.h b/include/linux/ide.h index a23ae25d7da8..e5fa5e868d67 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -491,6 +491,7 @@ typedef struct hwif_s { void (*exec_command)(struct hwif_s *, u8); u8 (*read_status)(struct hwif_s *); + u8 (*read_altstatus)(struct hwif_s *); u8 (*read_sff_dma_status)(struct hwif_s *); void (*tf_load)(ide_drive_t *, struct ide_task_s *); @@ -1363,13 +1364,6 @@ static inline void ide_set_irq(ide_drive_t *drive, int on) hwif->io_ports.ctl_addr); } -static inline u8 ide_read_altstatus(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - return hwif->INB(hwif->io_ports.ctl_addr); -} - static inline u8 ide_read_error(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; -- cgit v1.2.3 From 6e6afb3b7401f0181da74a1add57f126946b43e6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:52 +0200 Subject: ide: add ->set_irq method Add ->set_irq method for setting nIEN bit of ATA Device Control register and use it instead of ide_set_irq(). While at it: * Use ->set_irq in init_irq() and do_reset1(). * Don't use HWIF() macro in ide_check_pm_state(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 14 ++++++++------ drivers/ide/ide-iops.c | 37 +++++++++++++++++++++++++++---------- drivers/ide/ide-probe.c | 9 ++++----- drivers/ide/ide-taskfile.c | 2 +- drivers/ide/pci/scc_pata.c | 19 +++++++++++++++++++ drivers/ide/ppc/pmac.c | 17 +++++++++++++++++ include/linux/ide.h | 10 ++-------- 7 files changed, 78 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index fdc221ce9920..bbd7bd4c48ee 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -746,16 +746,17 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) * the bus may be broken enough to walk on our toes at this * point. */ + ide_hwif_t *hwif = drive->hwif; int rc; #ifdef DEBUG_PM printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name); #endif - rc = ide_wait_not_busy(HWIF(drive), 35000); + rc = ide_wait_not_busy(hwif, 35000); if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - ide_set_irq(drive, 1); - rc = ide_wait_not_busy(HWIF(drive), 100000); + hwif->set_irq(hwif, 1); + rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); } @@ -1041,7 +1042,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * quirk_list may not like intr setups/cleanups */ if (drive->quirk_list != 1) - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1519,6 +1520,7 @@ EXPORT_SYMBOL(ide_do_drive_cmd); void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) { + ide_hwif_t *hwif = drive->hwif; ide_task_t task; memset(&task, 0, sizeof(task)); @@ -1529,9 +1531,9 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); SELECT_MASK(drive, 0); - drive->hwif->tf_load(drive, &task); + hwif->tf_load(drive, &task); } EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index e106954e13f9..41ec53f329fa 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -135,6 +135,23 @@ static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) return inb(hwif->dma_base + ATA_DMA_STATUS); } +static void ide_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + else + outb(ctl, hwif->io_ports.ctl_addr); +} + static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; @@ -360,6 +377,8 @@ void default_hwif_transport(ide_hwif_t *hwif) hwif->read_altstatus = ide_read_altstatus; hwif->read_sff_dma_status = ide_read_sff_dma_status; + hwif->set_irq = ide_set_irq; + hwif->tf_load = ide_tf_load; hwif->tf_read = ide_tf_read; @@ -722,7 +741,7 @@ int ide_driveid_update(ide_drive_t *drive) */ SELECT_MASK(drive, 1); - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); msleep(50); hwif->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; @@ -808,12 +827,12 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 0); udelay(1); - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); hwif->OUTB(speed, io_ports->nsect_addr); hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr); hwif->exec_command(hwif, WIN_SETFEATURES); if (drive->quirk_list == 2) - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); error = __ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT|ERR_STAT, @@ -1129,7 +1148,6 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) ide_hwgroup_t *hwgroup; struct ide_io_ports *io_ports; const struct ide_port_ops *port_ops; - u8 ctl; spin_lock_irqsave(&ide_lock, flags); hwif = HWIF(drive); @@ -1174,16 +1192,15 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * immediate interrupt due to the edge transition it produces. * This single interrupt gives us a "fast poll" for drives that * recover from reset very quickly, saving us the first 50ms wait time. + * + * TODO: add ->softreset method and stop abusing ->set_irq */ /* set SRST and nIEN */ - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | 6, io_ports->ctl_addr); + hwif->set_irq(hwif, 4); /* more than enough time */ udelay(10); - if (drive->quirk_list == 2) - ctl = ATA_DEVCTL_OBS; /* clear SRST and nIEN */ - else - ctl = ATA_DEVCTL_OBS | 2; /* clear SRST, leave nIEN */ - hwif->OUTBSYNC(hwif, ctl, io_ports->ctl_addr); + /* clear SRST, leave nIEN (unless device is on the quirk list) */ + hwif->set_irq(hwif, drive->quirk_list == 2); /* more than enough time */ udelay(10); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index fe14d576ef01..475bd7263184 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -361,7 +361,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) autoprobe = 1; cookie = probe_irq_on(); } - ide_set_irq(drive, autoprobe); + hwif->set_irq(hwif, autoprobe); } retval = actual_try_to_identify(drive, cmd); @@ -369,7 +369,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) if (autoprobe) { int irq; - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); /* clear drive IRQ */ (void)hwif->read_status(hwif); udelay(5); @@ -709,7 +709,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if (!drive->noprobe || drive->present) { SELECT_DRIVE(drive); - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -1066,8 +1066,7 @@ static int init_irq (ide_hwif_t *hwif) sa = IRQF_SHARED; if (io_ports->ctl_addr) - /* clear nIEN */ - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS, io_ports->ctl_addr); + hwif->set_irq(hwif, 1); if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) goto out_unlink; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index fc2b3957afac..ea345369553e 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -80,7 +80,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); SELECT_MASK(drive, 0); hwif->tf_load(drive, task); } diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index a89dc4780786..727eda6db76c 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -149,6 +149,23 @@ static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) return (u8)in_be32((void *)(hwif->dma_base + 4)); } +static void scc_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + out_be32((void *)hwif->io_ports.ctl_addr, ctl); + eieio(); + in_be32((void *)(hwif->dma_base + 0x01c)); + eieio(); +} + static void scc_ide_insw(unsigned long port, void *addr, u32 count) { u16 *ptr = (u16 *)addr; @@ -802,6 +819,8 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->read_altstatus = scc_read_altstatus; hwif->read_sff_dma_status = scc_read_sff_dma_status; + hwif->set_irq = scc_set_irq; + hwif->tf_load = scc_tf_load; hwif->tf_read = scc_tf_read; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 7c3a84f8fbed..a0d66480a797 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -502,6 +502,22 @@ static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) + IDE_TIMING_CONFIG)); } +static void pmac_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + /* * Old tuning functions (called on hdparm -p), sets up drive PIO timings */ @@ -1100,6 +1116,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) return -ENOENT; hwif->exec_command = pmac_exec_command; + hwif->set_irq = pmac_set_irq; /* Setup MMIO ops */ default_hwif_mmiops(hwif); diff --git a/include/linux/ide.h b/include/linux/ide.h index e5fa5e868d67..ae93f89e4448 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -494,6 +494,8 @@ typedef struct hwif_s { u8 (*read_altstatus)(struct hwif_s *); u8 (*read_sff_dma_status)(struct hwif_s *); + void (*set_irq)(struct hwif_s *, int); + void (*tf_load)(ide_drive_t *, struct ide_task_s *); void (*tf_read)(ide_drive_t *, struct ide_task_s *); @@ -1356,14 +1358,6 @@ static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive) return &hwif->drives[(drive->dn ^ 1) & 1]; } -static inline void ide_set_irq(ide_drive_t *drive, int on) -{ - ide_hwif_t *hwif = drive->hwif; - - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | (on ? 0 : 2), - hwif->io_ports.ctl_addr); -} - static inline u8 ide_read_error(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; -- cgit v1.2.3 From 92eb43800a3c1300bd5cb8a2a27e6f2a84f7042e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:53 +0200 Subject: ide: use ->tf_read in ide_read_error() * Add IDE_TFLAG_IN_FEATURE taskfile flag for reading Feature register and handle it in ->tf_read. * Convert ide_read_error() to use ->tf_read instead of ->INB, then uninline and export it. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/h8300/ide-h8300.c | 2 ++ drivers/ide/ide-iops.c | 15 +++++++++++++++ drivers/ide/pci/ns87415.c | 2 ++ drivers/ide/pci/scc_pata.c | 2 ++ include/linux/ide.h | 10 +++------- 5 files changed, 24 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index a71433b3d0f3..548a2bf232c9 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -100,6 +100,8 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 1983b353eb16..113db8744736 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -241,6 +241,8 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = tf_inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tf_inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) @@ -390,6 +392,19 @@ void default_hwif_transport(ide_hwif_t *hwif) hwif->output_data = ata_output_data; } +u8 ide_read_error(ide_drive_t *drive) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_FEATURE; + + drive->hwif->tf_read(drive, &task); + + return task.tf.error; +} +EXPORT_SYMBOL_GPL(ide_read_error); + void ide_fix_driveid (struct hd_driveid *id) { #ifndef __LITTLE_ENDIAN diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index b20e5f01ac89..b9bb8428b35e 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -88,6 +88,8 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 727eda6db76c..c110f359b03e 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -741,6 +741,8 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = scc_ide_inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = scc_ide_inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) diff --git a/include/linux/ide.h b/include/linux/ide.h index ae93f89e4448..7890768d03ed 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -889,6 +889,7 @@ enum { IDE_TFLAG_IN_HOB = IDE_TFLAG_IN_HOB_FEATURE | IDE_TFLAG_IN_HOB_NSECT | IDE_TFLAG_IN_HOB_LBA, + IDE_TFLAG_IN_FEATURE = (1 << 1), IDE_TFLAG_IN_NSECT = (1 << 25), IDE_TFLAG_IN_LBAL = (1 << 26), IDE_TFLAG_IN_LBAM = (1 << 27), @@ -956,6 +957,8 @@ void ide_tf_dump(const char *, struct ide_taskfile *); extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); +u8 ide_read_error(ide_drive_t *); + extern int drive_is_ready(ide_drive_t *); void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); @@ -1357,11 +1360,4 @@ static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive) return &hwif->drives[(drive->dn ^ 1) & 1]; } - -static inline u8 ide_read_error(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - return hwif->INB(hwif->io_ports.error_addr); -} #endif /* _IDE_H */ -- cgit v1.2.3 From 1823649b5abb77ffe638178bc5253249d3ecd17d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:54 +0200 Subject: ide: add ide_read_bcount_and_ireason() helper Add ide_read_bcount_and_ireason() helper and use it instead of ->INB in {cdrom_newpc,ide_pc}_intr(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 6 ++---- drivers/ide/ide-cd.c | 12 ++++-------- drivers/ide/ide-iops.c | 15 +++++++++++++++ include/linux/ide.h | 1 + 4 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index bfab5c4afc6a..f17a00ccbe96 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -107,11 +107,9 @@ cmd_finished: ide_dma_off(drive); return ide_do_reset(drive); } - /* Get the number of bytes to transfer on this interrupt. */ - bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | - hwif->INB(hwif->io_ports.lbam_addr); - ireason = hwif->INB(hwif->io_ports.nsect_addr); + /* Get the number of bytes to transfer on this interrupt. */ + ide_read_bcount_and_ireason(drive, &bcount, &ireason); if (ireason & CD) { printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 82879a1a89e5..563a380d0e7a 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -895,10 +895,11 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) struct request *rq = HWGROUP(drive)->rq; xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; - int dma_error = 0, dma, stat, ireason, len, thislen, uptodate = 0; + int dma_error = 0, dma, stat, thislen, uptodate = 0; int write = (rq_data_dir(rq) == WRITE) ? 1 : 0; unsigned int timeout; - u8 lowcyl, highcyl; + u16 len; + u8 ireason; /* check for errors */ dma = info->dma; @@ -926,12 +927,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) goto end_request; } - /* ok we fall to pio :/ */ - ireason = hwif->INB(hwif->io_ports.nsect_addr) & 0x3; - lowcyl = hwif->INB(hwif->io_ports.lbam_addr); - highcyl = hwif->INB(hwif->io_ports.lbah_addr); - - len = lowcyl + (256 * highcyl); + ide_read_bcount_and_ireason(drive, &len, &ireason); thislen = blk_fs_request(rq) ? len : rq->data_len; if (thislen > len) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 113db8744736..cb11c7861a50 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -405,6 +405,21 @@ u8 ide_read_error(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_read_error); +void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM | + IDE_TFLAG_IN_NSECT; + + drive->hwif->tf_read(drive, &task); + + *bcount = (task.tf.lbah << 8) | task.tf.lbam; + *ireason = task.tf.nsect & 3; +} +EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); + void ide_fix_driveid (struct hd_driveid *id) { #ifndef __LITTLE_ENDIAN diff --git a/include/linux/ide.h b/include/linux/ide.h index 7890768d03ed..fd05758e6995 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -958,6 +958,7 @@ extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); u8 ide_read_error(ide_drive_t *); +void ide_read_bcount_and_ireason(ide_drive_t *, u16 *, u8 *); extern int drive_is_ready(ide_drive_t *); -- cgit v1.2.3 From 761052e676372465fdeb97c148d5a4b0790fa8a0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:54 +0200 Subject: ide: remove ->INB, ->OUTB and ->OUTBSYNC methods * Remove no longer needed ->INB, ->OUTB and ->OUTBSYNC methods. Then: * Remove no longer used default_hwif_[mm]iops() and ide_[mm_]outbsync(). * Cleanup SuperIO handling in ns87415.c. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 5 ----- drivers/ide/arm/palm_bk3710.c | 2 -- drivers/ide/arm/rapide.c | 2 -- drivers/ide/h8300/ide-h8300.c | 2 -- drivers/ide/ide-iops.c | 28 ------------------------ drivers/ide/ide.c | 1 - drivers/ide/legacy/ide_platform.c | 4 +--- drivers/ide/mips/swarm.c | 3 --- drivers/ide/pci/ns87415.c | 46 +++++++++++---------------------------- drivers/ide/pci/scc_pata.c | 12 ---------- drivers/ide/pci/sgiioc4.c | 3 --- drivers/ide/pci/siimage.c | 2 +- drivers/ide/ppc/pmac.c | 13 ----------- include/linux/ide.h | 7 ------ 14 files changed, 15 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index f73422dd8474..0fd01d630f12 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -469,8 +469,6 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) if (!hwif) return -ENODEV; - default_hwif_mmiops(hwif); - state->hwif[0] = hwif; ecard_set_drvdata(ec, state); @@ -547,14 +545,11 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) return -ENODEV; hwif->chipset = ide_acorn; - default_hwif_mmiops(hwif); idx[0] = hwif->index; mate = ide_find_port(); if (mate) { - default_hwif_mmiops(mate); - hws[1] = &hw[1]; idx[1] = mate->index; } diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 0229d794d909..545563bc7e23 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -399,8 +399,6 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) i = hwif->index; - default_hwif_mmiops(hwif); - idx[0] = i; ide_device_add(idx, &palm_bk3710_port_info, hws); diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 01896f6e8acf..a45c2f694949 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -59,8 +59,6 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) goto release; } - default_hwif_mmiops(hwif); - idx[0] = hwif->index; ide_device_add(idx, &rapide_port_info, hws); diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 548a2bf232c9..0795d6554913 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -171,8 +171,6 @@ static inline void hw_setup(hw_regs_t *hw) static inline void hwif_setup(ide_hwif_t *hwif) { - default_hwif_iops(hwif); - hwif->tf_load = h8300_tf_load; hwif->tf_read = h8300_tf_read; diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index cb11c7861a50..6c54fe1f9841 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -42,18 +42,6 @@ static void ide_outb (u8 val, unsigned long port) outb(val, port); } -static void ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) -{ - outb(addr, port); -} - -void default_hwif_iops (ide_hwif_t *hwif) -{ - hwif->OUTB = ide_outb; - hwif->OUTBSYNC = ide_outbsync; - hwif->INB = ide_inb; -} - /* * MMIO operations, typically used for SATA controllers */ @@ -68,22 +56,6 @@ static void ide_mm_outb (u8 value, unsigned long port) writeb(value, (void __iomem *) port); } -static void ide_mm_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) -{ - writeb(value, (void __iomem *) port); -} - -void default_hwif_mmiops (ide_hwif_t *hwif) -{ - hwif->OUTB = ide_mm_outb; - /* Most systems will need to override OUTBSYNC, alas however - this one is controller specific! */ - hwif->OUTBSYNC = ide_mm_outbsync; - hwif->INB = ide_mm_inb; -} - -EXPORT_SYMBOL(default_hwif_mmiops); - void SELECT_DRIVE (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index b6018f7b0907..434dd02a4bdc 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -101,7 +101,6 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) init_completion(&hwif->gendev_rel_comp); - default_hwif_iops(hwif); default_hwif_transport(hwif); ide_port_init_devices_data(hwif); diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 609da0d43196..3d71e336a221 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -103,10 +103,8 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; - if (mmio) { + if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - default_hwif_mmiops(hwif); - } idx[0] = hwif->index; diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 6da6844d2d8c..c1ffb83a2de7 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -120,9 +120,6 @@ static int __devinit swarm_ide_probe(struct device *dev) if (hwif == NULL) goto err; - /* Setup MMIO ops. */ - default_hwif_mmiops(hwif); - idx[0] = hwif->index; ide_device_add(idx, &swarm_port_info, hws); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index b9bb8428b35e..02fd3a877afa 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -28,10 +28,6 @@ */ #include -static unsigned long superio_ide_status[2]; -static unsigned long superio_ide_select[2]; -static unsigned long superio_ide_dma_status[2]; - #define SUPERIO_IDE_MAX_RETRIES 25 /* Because of a defect in Super I/O, all reads of the PCI DMA status @@ -40,27 +36,18 @@ static unsigned long superio_ide_dma_status[2]; */ static u8 superio_ide_inb (unsigned long port) { - if (port == superio_ide_status[0] || - port == superio_ide_status[1] || - port == superio_ide_select[0] || - port == superio_ide_select[1] || - port == superio_ide_dma_status[0] || - port == superio_ide_dma_status[1]) { - u8 tmp; - int retries = SUPERIO_IDE_MAX_RETRIES; - - /* printk(" [ reading port 0x%x with retry ] ", port); */ + u8 tmp; + int retries = SUPERIO_IDE_MAX_RETRIES; - do { - tmp = inb(port); - if (tmp == 0) - udelay(50); - } while (tmp == 0 && retries-- > 0); + /* printk(" [ reading port 0x%x with retry ] ", port); */ - return tmp; - } + do { + tmp = inb(port); + if (tmp == 0) + udelay(50); + } while (tmp == 0 && retries-- > 0); - return inb(port); + return tmp; } static u8 superio_read_status(ide_hwif_t *hwif) @@ -120,27 +107,20 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) static void __devinit superio_ide_init_iops (struct hwif_s *hwif) { struct pci_dev *pdev = to_pci_dev(hwif->dev); - u32 base, dmabase; + u32 dma_stat; u8 port = hwif->channel, tmp; - base = pci_resource_start(pdev, port * 2) & ~3; - dmabase = pci_resource_start(pdev, 4) & ~3; - - superio_ide_status[port] = base + 7; - superio_ide_select[port] = base + 6; - superio_ide_dma_status[port] = dmabase + (!port ? 2 : 0xa); + dma_stat = (pci_resource_start(pdev, 4) & ~3) + (!port ? 2 : 0xa); /* Clear error/interrupt, enable dma */ - tmp = superio_ide_inb(superio_ide_dma_status[port]); - outb(tmp | 0x66, superio_ide_dma_status[port]); + tmp = superio_ide_inb(dma_stat); + outb(tmp | 0x66, dma_stat); hwif->read_status = superio_read_status; hwif->read_sff_dma_status = superio_read_sff_dma_status; hwif->tf_read = superio_tf_read; - /* We need to override inb to workaround a SuperIO errata */ - hwif->INB = superio_ide_inb; } static void __devinit init_iops_ns87415(ide_hwif_t *hwif) diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index c110f359b03e..38765d9b0314 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -188,14 +188,6 @@ static void scc_ide_outb(u8 addr, unsigned long port) out_be32((void*)port, addr); } -static void scc_ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) -{ - out_be32((void*)port, addr); - eieio(); - in_be32((void*)(hwif->dma_base + 0x01c)); - eieio(); -} - static void scc_ide_outsw(unsigned long port, void *addr, u32 count) { @@ -829,10 +821,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->input_data = scc_input_data; hwif->output_data = scc_output_data; - hwif->INB = scc_ide_inb; - hwif->OUTB = scc_ide_outb; - hwif->OUTBSYNC = scc_ide_outbsync; - hwif->dma_base = dma_base; hwif->config_data = ports->ctl; } diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 3005a6334a58..86f7c4901837 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -623,9 +623,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) if (hwif == NULL) goto err; - /* The IOC4 uses MMIO rather than Port IO. */ - default_hwif_mmiops(hwif); - /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 21d7137f7d6c..5965a35d94ae 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -601,7 +601,7 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) * Fill in the basic hwif bits */ hwif->host_flags |= IDE_HFLAG_MMIO; - default_hwif_mmiops(hwif); + hwif->hwif_data = addr; /* diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index a0d66480a797..ed073c6635a8 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -486,15 +486,6 @@ pmac_ide_do_update_timings(ide_drive_t *drive) pmac_ide_selectproc(drive); } -static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) -{ - u32 tmp; - - writeb(value, (void __iomem *) port); - tmp = readl((void __iomem *)(hwif->io_ports.data_addr - + IDE_TIMING_CONFIG)); -} - static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) { writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); @@ -1118,10 +1109,6 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) hwif->exec_command = pmac_exec_command; hwif->set_irq = pmac_set_irq; - /* Setup MMIO ops */ - default_hwif_mmiops(hwif); - hwif->OUTBSYNC = pmac_outbsync; - idx[0] = hwif->index; ide_device_add(idx, &d, hws); diff --git a/include/linux/ide.h b/include/linux/ide.h index fd05758e6995..0544ff7d9afc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -504,11 +504,6 @@ typedef struct hwif_s { void (*ide_dma_clear_irq)(ide_drive_t *drive); - void (*OUTB)(u8 addr, unsigned long port); - void (*OUTBSYNC)(struct hwif_s *hwif, u8 addr, unsigned long port); - - u8 (*INB)(unsigned long port); - /* dma physical region descriptor table (cpu view) */ unsigned int *dmatable_cpu; /* dma physical region descriptor table (dma view) */ @@ -1027,8 +1022,6 @@ static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, } #endif -extern void default_hwif_iops(ide_hwif_t *); -extern void default_hwif_mmiops(ide_hwif_t *); extern void default_hwif_transport(ide_hwif_t *); typedef struct ide_pci_enablebit_s { -- cgit v1.2.3 From ba4b2e607e4e9eaa929935325dafd5c86d3b5262 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:55 +0200 Subject: ide: remove dead Virtual DMA support Lets remove dead Virtual DMA support for now so it doesn't clutter core IDE code (it can be bring back when there is a need for it): * Remove IDE_HFLAG_VDMA host flag. * Remove ide_drive_t.vdma flag. * cs5520.c: remove stale FIXMEs, cs5520_dma_host_set() and cs5520_dma_ops (also there is no longer a need to set IDE_HFLAG_NO_ATAPI_DMA). There should be no functional changes caused by this patch. Cc: TAKADA Yoshihito Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 2 +- drivers/ide/ide-dma.c | 5 ++--- drivers/ide/ide-iops.c | 3 +-- drivers/ide/pci/cs5520.c | 30 ------------------------------ include/linux/ide.h | 3 --- 5 files changed, 4 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 3a2e80237c10..df5fe5756871 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -158,7 +158,7 @@ static void ide_tf_set_cmd(ide_drive_t *drive, ide_task_t *task, u8 dma) write = (task->tf_flags & IDE_TFLAG_WRITE) ? 1 : 0; if (dma) - index = drive->vdma ? 4 : 8; + index = 8; else index = drive->mult_count ? 0 : 4; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index ecf60dbbe3e9..e72112efab9a 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -756,9 +756,8 @@ static int ide_tune_dma(ide_drive_t *drive) static int ide_dma_check(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - int vdma = (hwif->host_flags & IDE_HFLAG_VDMA)? 1 : 0; - if (!vdma && ide_tune_dma(drive)) + if (ide_tune_dma(drive)) return 0; /* TODO: always do PIO fallback */ @@ -767,7 +766,7 @@ static int ide_dma_check(ide_drive_t *drive) ide_set_max_pio(drive); - return vdma ? 0 : -1; + return -1; } int ide_id_dma_bug(ide_drive_t *drive) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 6c54fe1f9841..c9d15be4c48d 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -866,8 +866,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) skip: #ifdef CONFIG_BLK_DEV_IDEDMA - if ((speed >= XFER_SW_DMA_0 || (hwif->host_flags & IDE_HFLAG_VDMA)) && - drive->using_dma) + if (speed >= XFER_SW_DMA_0 && drive->using_dma) hwif->dma_ops->dma_host_set(drive, 1); else if (hwif->dma_ops) /* check if host supports DMA */ ide_dma_off_quietly(drive); diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index a13f2bc0665c..d30cb4d99c45 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -62,8 +62,6 @@ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio) struct pci_dev *pdev = to_pci_dev(hwif->dev); int controller = drive->dn > 1 ? 1 : 0; - /* FIXME: if DMA = 1 do we need to set the DMA bit here ? */ - /* 8bit CAT/CRT - 8bit command timing for channel */ pci_write_config_byte(pdev, 0x62 + controller, (cs5520_pio_clocks[pio].recovery << 4) | @@ -89,45 +87,17 @@ static void cs5520_set_dma_mode(ide_drive_t *drive, const u8 speed) cs5520_set_pio_mode(drive, 0); } -/* - * We wrap the DMA activate to set the vdma flag. This is needed - * so that the IDE DMA layer issues PIO not DMA commands over the - * DMA channel - * - * ATAPI is harder so disable it for now using IDE_HFLAG_NO_ATAPI_DMA - */ - -static void cs5520_dma_host_set(ide_drive_t *drive, int on) -{ - drive->vdma = on; - ide_dma_host_set(drive, on); -} - static const struct ide_port_ops cs5520_port_ops = { .set_pio_mode = cs5520_set_pio_mode, .set_dma_mode = cs5520_set_dma_mode, }; -static const struct ide_dma_ops cs5520_dma_ops = { - .dma_host_set = cs5520_dma_host_set, - .dma_setup = ide_dma_setup, - .dma_exec_cmd = ide_dma_exec_cmd, - .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, - .dma_test_irq = ide_dma_test_irq, - .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = ide_dma_timeout, -}; - -/* FIXME: VDMA is disabled because it caused system hangs */ #define DECLARE_CS_DEV(name_str) \ { \ .name = name_str, \ .port_ops = &cs5520_port_ops, \ - .dma_ops = &cs5520_dma_ops, \ .host_flags = IDE_HFLAG_ISA_PORTS | \ IDE_HFLAG_CS5520 | \ - IDE_HFLAG_NO_ATAPI_DMA | \ IDE_HFLAG_ABUSE_SET_DMA_MODE, \ .pio_mask = ATA_PIO4, \ } diff --git a/include/linux/ide.h b/include/linux/ide.h index 0544ff7d9afc..3f2889400250 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -355,7 +355,6 @@ typedef struct ide_drive_s { unsigned nodma : 1; /* disallow DMA */ unsigned remap_0_to_1 : 1; /* 0=noremap, 1=remap 0->1 (for EZDrive) */ unsigned blocked : 1; /* 1=powermanagment told us not to do anything, so sleep nicely */ - unsigned vdma : 1; /* 1=doing PIO over DMA 0=doing normal DMA */ unsigned scsi : 1; /* 0=default, 1=ide-scsi emulation */ unsigned sleeping : 1; /* 1=sleeping & sleep field valid */ unsigned post_reset : 1; @@ -1097,8 +1096,6 @@ enum { IDE_HFLAG_NO_IO_32BIT = (1 << 30), /* never unmask IRQs */ IDE_HFLAG_NO_UNMASK_IRQS = (1 << 31), - /* host uses VDMA (disabled for now) */ - IDE_HFLAG_VDMA = 0, }; #ifdef CONFIG_BLK_DEV_OFFBOARD -- cgit v1.2.3 From 3b2a5c7149ee4af4aff8fee953f66fc846d92cea Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:56 +0200 Subject: ide: filter out "default" transfer mode values in set_xfer_rate() * Filter out "default" transfer mode values (0x00 - default PIO mode, 0x01 - default PIO mode w/ IORDY disabled) in write handler for obsoleted /proc/ide/hd?/settings:current_speed setting. Allowing "default" transfer mode values is a dangerous thing to do as we don't support programming controller to the "default" transfer mode and devices often use different values for the default and maximum PIO mode (i.e. PIO2 default and PIO4 maximum) so the controller will stay programmed for higher PIO mode while device will use the lower PIO mode. There is no functionality loss as by using special IOCTLs device can still be programmed to "default" transfer modes (it is only useful for debugging/testing purposes anyway). * Remove no longer needed IDE_HFLAG_ABUSE_SET_DMA_MODE host flag, it was previously used by few host drivers to program the controller to PIO0 timings for "default" transfer mode == 0x01 (although some host drivers would program invalid PIO timings instead). * Cleanup ide_set_xfer_rate() and add BUG_ON(). Suggested-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-lib.c | 15 ++------------- drivers/ide/ide-proc.c | 4 ++-- drivers/ide/pci/aec62xx.c | 5 ----- drivers/ide/pci/amd74xx.c | 1 - drivers/ide/pci/cs5520.c | 3 +-- drivers/ide/pci/cs5535.c | 3 +-- drivers/ide/pci/hpt34x.c | 1 - drivers/ide/pci/hpt366.c | 1 - drivers/ide/pci/pdc202xx_old.c | 1 - drivers/ide/pci/serverworks.c | 4 +--- drivers/ide/pci/tc86c001.c | 3 +-- drivers/ide/pci/via82cxxx.c | 1 - include/linux/ide.h | 1 - 13 files changed, 8 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 13af72f09ec4..7ac44d515470 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -266,22 +266,11 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) rate = ide_rate_filter(drive, rate); + BUG_ON(rate < XFER_PIO_0); + if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5) return ide_set_pio_mode(drive, rate); - /* - * TODO: transfer modes 0x00-0x07 passed from the user-space are - * currently handled here which needs fixing (please note that such - * case could happen iff the transfer mode has already been set on - * the device by ide-proc.c::set_xfer_rate()). - */ - if (rate < XFER_PIO_0) { - if (hwif->host_flags & IDE_HFLAG_ABUSE_SET_DMA_MODE) - return ide_set_dma_mode(drive, rate); - else - return ide_config_drive_speed(drive, rate); - } - return ide_set_dma_mode(drive, rate); } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 54d57a15d59e..151c91e933da 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -345,7 +345,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) ide_task_t task; int err; - if (arg < 0 || (arg > 1 && arg < XFER_PIO_0) || arg > XFER_UDMA_6) + if (arg < XFER_PIO_0 || arg > XFER_UDMA_6) return -EINVAL; memset(&task, 0, sizeof(task)); @@ -357,7 +357,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) err = ide_no_data_taskfile(drive, &task); - if (!err && arg) { + if (!err) { ide_set_xfer_rate(drive, (u8) arg); ide_driveid_update(drive); } diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index ae7a4329a581..fbc43e121e6b 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -195,7 +195,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_DSC | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -205,7 +204,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .init_chipset = init_chipset_aec62xx, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -216,7 +214,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_NON_BOOTABLE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -226,7 +223,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .init_chipset = init_chipset_aec62xx, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -237,7 +233,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index 0bfcdd0e77b3..ef7d971031ee 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -218,7 +218,6 @@ static const struct ide_port_ops amd_port_ops = { #define IDE_HFLAGS_AMD \ (IDE_HFLAG_PIO_NO_BLACKLIST | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_POST_SET_MODE | \ IDE_HFLAG_IO_32BIT | \ IDE_HFLAG_UNMASK_IRQS) diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index d30cb4d99c45..e8e7df1915bf 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -97,8 +97,7 @@ static const struct ide_port_ops cs5520_port_ops = { .name = name_str, \ .port_ops = &cs5520_port_ops, \ .host_flags = IDE_HFLAG_ISA_PORTS | \ - IDE_HFLAG_CS5520 | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE, \ + IDE_HFLAG_CS5520, \ .pio_mask = ATA_PIO4, \ } diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index dc97c48623f3..5404fe4f701d 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -171,8 +171,7 @@ static const struct ide_port_ops cs5535_port_ops = { static const struct ide_port_info cs5535_chipset __devinitdata = { .name = "CS5535", .port_ops = &cs5535_port_ops, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE | - IDE_HFLAG_ABUSE_SET_DMA_MODE, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 84c36c117194..9e1d1c4741da 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -123,7 +123,6 @@ static const struct ide_port_ops hpt34x_port_ops = { #define IDE_HFLAGS_HPT34X \ (IDE_HFLAG_NO_ATAPI_DMA | \ IDE_HFLAG_NO_DSC | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_NO_AUTODMA) static const struct ide_port_info hpt34x_chipsets[] __devinitdata = { diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index e5651cefa395..1f1135ce7cd6 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1409,7 +1409,6 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2) #define IDE_HFLAGS_HPT3XX \ (IDE_HFLAG_NO_ATAPI_DMA | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_OFF_BOARD) static const struct ide_port_ops hpt3xx_port_ops = { diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 1c2f9df31129..e54dc653b8c4 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -312,7 +312,6 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev, #define IDE_HFLAGS_PDC202XX \ (IDE_HFLAG_ERROR_STOPS_FIFO | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_OFF_BOARD) static const struct ide_port_ops pdc20246_port_ops = { diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index a1fb20826a5b..127ccb45e261 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -349,9 +349,7 @@ static const struct ide_port_ops svwks_port_ops = { .cable_detect = svwks_cable_detect, }; -#define IDE_HFLAGS_SVWKS \ - (IDE_HFLAG_LEGACY_IRQS | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE) +#define IDE_HFLAGS_SVWKS IDE_HFLAG_LEGACY_IRQS static const struct ide_port_info serverworks_chipsets[] __devinitdata = { { /* 0 */ diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index a81d47c55ce1..477e19790102 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -206,8 +206,7 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = { .init_hwif = init_hwif_tc86c001, .port_ops = &tc86c001_port_ops, .dma_ops = &tc86c001_dma_ops, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD | - IDE_HFLAG_ABUSE_SET_DMA_MODE, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index e47384c70c40..09dc4803ef9d 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -425,7 +425,6 @@ static const struct ide_port_info via82cxxx_chipset __devinitdata = { .enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } }, .port_ops = &via_port_ops, .host_flags = IDE_HFLAG_PIO_NO_BLACKLIST | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_POST_SET_MODE | IDE_HFLAG_IO_32BIT, .pio_mask = ATA_PIO5, diff --git a/include/linux/ide.h b/include/linux/ide.h index 3f2889400250..260e871ae880 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1085,7 +1085,6 @@ enum { IDE_HFLAG_IO_32BIT = (1 << 24), /* unmask IRQs */ IDE_HFLAG_UNMASK_IRQS = (1 << 25), - IDE_HFLAG_ABUSE_SET_DMA_MODE = (1 << 26), /* serialize ports if DMA is possible (for sl82c105) */ IDE_HFLAG_SERIALIZE_DMA = (1 << 27), /* force host out of "simplex" mode */ -- cgit v1.2.3 From d6276b5f5cc7508124de291f3ed59c6945c17ae7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:56 +0200 Subject: ide: add 'config' field to hw_regs_t Add 'config' field to hw_regs_t and use it to set hwif->config_data in ide_init_port_hw(), then convert ide_legacy_init_one() to use hw->config. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 4 +--- drivers/ide/ide.c | 1 + include/linux/ide.h | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 21cea45e9f21..3cc8ade2cc4f 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1689,14 +1689,12 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, ide_std_init_ports(hw, base, ctl); hw->irq = irq; hw->chipset = d->chipset; + hw->config = config; hwif = ide_find_port_slot(d); if (hwif) { hwif->chipset = hw->chipset; - if (config) - hwif->config_data = config; - hws[port_no] = hw; idx[port_no] = hwif->index; } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 434dd02a4bdc..961f31c648c9 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -286,6 +286,7 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) hwif->dev = hw->dev; hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; hwif->ack_intr = hw->ack_intr; + hwif->config_data = hw->config; } /* diff --git a/include/linux/ide.h b/include/linux/ide.h index 260e871ae880..e340218b2a5f 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -178,6 +178,7 @@ typedef struct hw_regs_s { ide_ack_intr_t *ack_intr; /* acknowledge interrupt */ hwif_chipset_t chipset; struct device *dev, *parent; + unsigned long config; } hw_regs_t; void ide_init_port_data(struct hwif_s *, unsigned int); -- cgit v1.2.3 From 374e042c3e767ac2e5a40b78529220e0b3de793c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:56 +0200 Subject: ide: add struct ide_tp_ops (take 2) * Add struct ide_tp_ops for transport methods. * Add 'const struct ide_tp_ops *tp_ops' to struct ide_port_info and ide_hwif_t. * Set the default hwif->tp_ops in ide_init_port_data(). * Set host driver specific hwif->tp_ops in ide_init_port(). * Export ide_exec_command(), ide_read_status(), ide_read_altstatus(), ide_read_sff_dma_status(), ide_set_irq(), ide_tf_{load,read}() and ata_{in,out}put_data(). * Convert host drivers and core code to use struct ide_tp_ops. * Remove no longer needed default_hwif_transport(). * Cleanup ide_hwif_t from methods that are now in struct ide_tp_ops. While at it: * Use struct ide_port_info in falconide.c and q40ide.c. * Rename ata_{in,out}put_data() to ide_{in,out}put_data(). v2: * Fix missing convertion in ns87415.c. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 2 +- drivers/ide/h8300/ide-h8300.c | 26 +++++---- drivers/ide/ide-atapi.c | 13 +++-- drivers/ide/ide-cd.c | 12 ++-- drivers/ide/ide-dma.c | 12 ++-- drivers/ide/ide-floppy.c | 8 +-- drivers/ide/ide-io.c | 32 +++++------ drivers/ide/ide-iops.c | 122 +++++++++++++++++++++++------------------ drivers/ide/ide-lib.c | 2 +- drivers/ide/ide-probe.c | 53 ++++++++++-------- drivers/ide/ide-tape.c | 8 +-- drivers/ide/ide-taskfile.c | 29 ++++++---- drivers/ide/ide.c | 2 +- drivers/ide/legacy/falconide.c | 27 +++++++-- drivers/ide/legacy/q40ide.c | 27 +++++++-- drivers/ide/mips/au1xxx-ide.c | 29 +++++++--- drivers/ide/pci/ns87415.c | 50 +++++++++-------- drivers/ide/pci/scc_pata.c | 29 +++++----- drivers/ide/pci/sgiioc4.c | 18 +++++- drivers/ide/ppc/pmac.c | 21 +++++-- drivers/ide/setup-pci.c | 2 +- drivers/scsi/ide-scsi.c | 7 ++- include/linux/ide.h | 52 ++++++++++++------ 23 files changed, 358 insertions(+), 225 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 0fd01d630f12..0283d162f7f7 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -382,7 +382,7 @@ static void icside_dma_timeout(ide_drive_t *drive) if (icside_dma_test_irq(drive)) return; - ide_dump_status(drive, "DMA timeout", hwif->read_status(hwif)); + ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); icside_dma_end(drive); } diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 0795d6554913..84644e150531 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -155,6 +155,21 @@ static void h8300_output_data(ide_drive_t *drive, struct request *rq, mm_outsw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); } +static const struct ide_tp_ops h8300_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = h8300_tf_load, + .tf_read = h8300_tf_read, + + .input_data = h8300_input_data, + .output_data = h8300_output_data, +}; + #define H8300_IDE_GAP (2) static inline void hw_setup(hw_regs_t *hw) @@ -169,16 +184,8 @@ static inline void hw_setup(hw_regs_t *hw) hw->chipset = ide_generic; } -static inline void hwif_setup(ide_hwif_t *hwif) -{ - hwif->tf_load = h8300_tf_load; - hwif->tf_read = h8300_tf_read; - - hwif->input_data = h8300_input_data; - hwif->output_data = h8300_output_data; -} - static const struct ide_port_info h8300_port_info = { + .tp_ops = &h8300_tp_ops, .host_flags = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA, }; @@ -205,7 +212,6 @@ static int __init h8300_ide_init(void) return -ENOENT; index = hwif->index; - hwif_setup(hwif); idx[0] = index; diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f17a00ccbe96..6789b81ea78d 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -22,6 +22,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; unsigned int temp; u16 bcount; @@ -35,7 +36,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Clear the interrupt */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || @@ -140,7 +141,7 @@ cmd_finished: if (pc->sg) io_buffers(drive, pc, temp, 0); else - hwif->input_data(drive, NULL, + tp_ops->input_data(drive, NULL, pc->cur_pos, temp); printk(KERN_ERR "%s: transferred %d of " "%d bytes\n", @@ -157,9 +158,9 @@ cmd_finished: debug_log("The device wants to send us more data than " "expected - allowing transfer\n"); } - xferfunc = hwif->input_data; + xferfunc = tp_ops->input_data; } else - xferfunc = hwif->output_data; + xferfunc = tp_ops->output_data; if ((drive->media == ide_floppy && !scsi && !pc->buf) || (drive->media == ide_tape && !scsi && pc->bh) || @@ -188,7 +189,7 @@ static u8 ide_read_ireason(ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_IN_NSECT; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); return task.tf.nsect & 3; } @@ -249,7 +250,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, /* Send the actual packet */ if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) - hwif->output_data(drive, NULL, pc->c, 12); + hwif->tp_ops->output_data(drive, NULL, pc->c, 12); return ide_started; } diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 563a380d0e7a..d9798ca433ba 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -285,7 +285,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) int stat, err, sense_key; /* check for errors */ - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat_ret) *stat_ret = stat; @@ -590,7 +590,7 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, cmd_len = ATAPI_MIN_CDB_BYTES; /* send the command to the device */ - hwif->output_data(drive, NULL, rq->cmd, cmd_len); + hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); /* start the DMA if need be */ if (info->dma) @@ -627,7 +627,7 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, * Some drives (ASUS) seem to tell us that status info is * available. Just get it and ignore. */ - (void)hwif->read_status(hwif); + (void)hwif->tp_ops->read_status(hwif); return 0; } else { /* drive wants a command packet, or invalid ireason... */ @@ -990,10 +990,10 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (ireason == 0) { write = 1; - xferfunc = hwif->output_data; + xferfunc = hwif->tp_ops->output_data; } else { write = 0; - xferfunc = hwif->input_data; + xferfunc = hwif->tp_ops->input_data; } /* transfer data */ @@ -1200,7 +1200,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (info->cd_flags & IDE_CD_FLAG_SEEKING) { ide_hwif_t *hwif = drive->hwif; unsigned long elapsed = jiffies - info->start_seek; - int stat = hwif->read_status(hwif); + int stat = hwif->tp_ops->read_status(hwif); if ((stat & SEEK_STAT) != SEEK_STAT) { if (elapsed < IDECD_SEEK_TIMEOUT) { diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index e72112efab9a..be99d463dcc7 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -104,7 +104,7 @@ ide_startstop_t ide_dma_intr (ide_drive_t *drive) u8 stat = 0, dma_stat = 0; dma_stat = hwif->dma_ops->dma_end(drive); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { if (!dma_stat) { @@ -335,7 +335,7 @@ static int config_drive_for_dma (ide_drive_t *drive) static int dma_timer_expiry (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->read_sff_dma_status(hwif); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); @@ -370,7 +370,7 @@ void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = hwif->read_sff_dma_status(hwif); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if (on) dma_stat |= (1 << (5 + unit)); @@ -482,7 +482,7 @@ int ide_dma_setup(ide_drive_t *drive) outb(reading, hwif->dma_base + ATA_DMA_CMD); /* read DMA status for INTR & ERROR flags */ - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ if (mmio) @@ -551,7 +551,7 @@ int __ide_dma_end (ide_drive_t *drive) } /* get DMA status */ - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if (mmio) /* clear the INTR & ERROR bits */ @@ -574,7 +574,7 @@ EXPORT_SYMBOL(__ide_dma_end); int ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->read_sff_dma_status(hwif); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* return 1 if INTR asserted */ if ((dma_stat & 4) == 4) diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 6f5294cfff23..62be2b27f236 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -247,9 +247,9 @@ static void ide_floppy_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, data = bvec_kmap_irq(bvec, &flags); if (direction) - hwif->output_data(drive, NULL, data, count); + hwif->tp_ops->output_data(drive, NULL, data, count); else - hwif->input_data(drive, NULL, data, count); + hwif->tp_ops->input_data(drive, NULL, data, count); bvec_kunmap_irq(data, &flags); bcount -= count; @@ -402,7 +402,7 @@ static int idefloppy_transfer_pc(ide_drive_t *drive) idefloppy_floppy_t *floppy = drive->driver_data; /* Send the actual packet */ - drive->hwif->output_data(drive, NULL, floppy->pc->c, 12); + drive->hwif->tp_ops->output_data(drive, NULL, floppy->pc->c, 12); /* Timeout for the packet command */ return IDEFLOPPY_WAIT_CMD; @@ -954,7 +954,7 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) u8 stat; local_irq_save(flags); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); local_irq_restore(flags); progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index bbd7bd4c48ee..a896a283f27f 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -330,7 +330,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) tf->error = err; tf->status = stat; - drive->hwif->tf_read(drive, task); + drive->hwif->tp_ops->tf_read(drive, task); if (task->tf_flags & IDE_TFLAG_DYN) kfree(task); @@ -381,7 +381,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 if (err == ABRT_ERR) { if (drive->select.b.lba && /* some newer drives don't support WIN_SPECIFY */ - hwif->read_status(hwif) == WIN_SPECIFY) + hwif->tp_ops->read_status(hwif) == WIN_SPECIFY) return ide_stopped; } else if ((err & BAD_CRC) == BAD_CRC) { /* UDMA crc error, just retry the operation */ @@ -407,7 +407,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 return ide_stopped; } - if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) rq->errors |= ERROR_RESET; if ((rq->errors & ERROR_RESET) == ERROR_RESET) { @@ -434,9 +434,9 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u /* add decoding error stuff */ } - if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); + hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE); if (rq->errors >= ERROR_MAX) { ide_kill_rq(drive, rq); @@ -710,7 +710,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, #ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", drive->name); #endif - ide_end_drive_cmd(drive, hwif->read_status(hwif), + ide_end_drive_cmd(drive, hwif->tp_ops->read_status(hwif), ide_read_error(drive)); return ide_stopped; @@ -755,7 +755,7 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); @@ -1042,7 +1042,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * quirk_list may not like intr setups/cleanups */ if (drive->quirk_list != 1) - hwif->set_irq(hwif, 0); + hwif->tp_ops->set_irq(hwif, 0); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1142,7 +1142,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); (void)hwif->dma_ops->dma_end(drive); ret = ide_error(drive, "dma timeout error", - hwif->read_status(hwif)); + hwif->tp_ops->read_status(hwif)); } else { printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); hwif->dma_ops->dma_timeout(drive); @@ -1267,7 +1267,7 @@ void ide_timer_expiry (unsigned long data) } else startstop = ide_error(drive, "irq timeout", - hwif->read_status(hwif)); + hwif->tp_ops->read_status(hwif)); } drive->service_time = jiffies - drive->service_start; spin_lock_irq(&ide_lock); @@ -1323,7 +1323,7 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) */ do { if (hwif->irq == irq) { - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { /* Try to not flood the console with msgs */ @@ -1414,7 +1414,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * Whack the status register, just in case * we have a leftover pending IRQ. */ - (void)hwif->read_status(hwif); + (void)hwif->tp_ops->read_status(hwif); #endif /* CONFIG_BLK_DEV_IDEPCI */ } spin_unlock_irqrestore(&ide_lock, flags); @@ -1531,9 +1531,9 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); - hwif->tf_load(drive, &task); + hwif->tp_ops->tf_load(drive, &task); } EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load); @@ -1545,9 +1545,9 @@ void ide_pad_transfer(ide_drive_t *drive, int write, int len) while (len > 0) { if (write) - hwif->output_data(drive, NULL, buf, min(4, len)); + hwif->tp_ops->output_data(drive, NULL, buf, min(4, len)); else - hwif->input_data(drive, NULL, buf, min(4, len)); + hwif->tp_ops->input_data(drive, NULL, buf, min(4, len)); len -= 4; } } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index c9d15be4c48d..07da5fb9eaff 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -68,7 +68,7 @@ void SELECT_DRIVE (ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_OUT_DEVICE; - drive->hwif->tf_load(drive, &task); + drive->hwif->tp_ops->tf_load(drive, &task); } void SELECT_MASK(ide_drive_t *drive, int mask) @@ -79,39 +79,43 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } -static void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +void ide_exec_command(ide_hwif_t *hwif, u8 cmd) { if (hwif->host_flags & IDE_HFLAG_MMIO) writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); else outb(cmd, hwif->io_ports.command_addr); } +EXPORT_SYMBOL_GPL(ide_exec_command); -static u8 ide_read_status(ide_hwif_t *hwif) +u8 ide_read_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) return readb((void __iomem *)hwif->io_ports.status_addr); else return inb(hwif->io_ports.status_addr); } +EXPORT_SYMBOL_GPL(ide_read_status); -static u8 ide_read_altstatus(ide_hwif_t *hwif) +u8 ide_read_altstatus(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) return readb((void __iomem *)hwif->io_ports.ctl_addr); else return inb(hwif->io_ports.ctl_addr); } +EXPORT_SYMBOL_GPL(ide_read_altstatus); -static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) +u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) return readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else return inb(hwif->dma_base + ATA_DMA_STATUS); } +EXPORT_SYMBOL_GPL(ide_read_sff_dma_status); -static void ide_set_irq(ide_hwif_t *hwif, int on) +void ide_set_irq(ide_hwif_t *hwif, int on) { u8 ctl = ATA_DEVCTL_OBS; @@ -127,8 +131,9 @@ static void ide_set_irq(ide_hwif_t *hwif, int on) else outb(ctl, hwif->io_ports.ctl_addr); } +EXPORT_SYMBOL_GPL(ide_set_irq); -static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) +void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -180,8 +185,9 @@ static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) tf_outb((tf->device & HIHI) | drive->select.all, io_ports->device_addr); } +EXPORT_SYMBOL_GPL(ide_tf_load); -static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) +void ide_tf_read(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -241,6 +247,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) tf->hob_lbah = tf_inb(io_ports->lbah_addr); } } +EXPORT_SYMBOL_GPL(ide_tf_read); /* * Some localbus EIDE interfaces require a special access sequence @@ -263,8 +270,8 @@ static void ata_vlb_sync(unsigned long port) * so if an odd len is specified, be sure that there's at least one * extra byte allocated for the buffer. */ -static void ata_input_data(ide_drive_t *drive, struct request *rq, - void *buf, unsigned int len) +void ide_input_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -304,12 +311,13 @@ static void ata_input_data(ide_drive_t *drive, struct request *rq, insw(data_addr, buf, len / 2); } } +EXPORT_SYMBOL_GPL(ide_input_data); /* * This is used for most PIO data transfers *to* the IDE interface */ -static void ata_output_data(ide_drive_t *drive, struct request *rq, - void *buf, unsigned int len) +void ide_output_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -347,22 +355,7 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, outsw(data_addr, buf, len / 2); } } - -void default_hwif_transport(ide_hwif_t *hwif) -{ - hwif->exec_command = ide_exec_command; - hwif->read_status = ide_read_status; - hwif->read_altstatus = ide_read_altstatus; - hwif->read_sff_dma_status = ide_read_sff_dma_status; - - hwif->set_irq = ide_set_irq; - - hwif->tf_load = ide_tf_load; - hwif->tf_read = ide_tf_read; - - hwif->input_data = ata_input_data; - hwif->output_data = ata_output_data; -} +EXPORT_SYMBOL_GPL(ide_output_data); u8 ide_read_error(ide_drive_t *drive) { @@ -371,7 +364,7 @@ u8 ide_read_error(ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_IN_FEATURE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); return task.tf.error; } @@ -385,13 +378,28 @@ void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM | IDE_TFLAG_IN_NSECT; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); *bcount = (task.tf.lbah << 8) | task.tf.lbam; *ireason = task.tf.nsect & 3; } EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); +const struct ide_tp_ops default_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + void ide_fix_driveid (struct hd_driveid *id) { #ifndef __LITTLE_ENDIAN @@ -545,10 +553,10 @@ int drive_is_ready (ide_drive_t *drive) * about possible isa-pnp and pci-pnp issues yet. */ if (hwif->io_ports.ctl_addr) - stat = hwif->read_altstatus(hwif); + stat = hwif->tp_ops->read_altstatus(hwif); else /* Note: this may clear a pending IRQ!! */ - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat & BUSY_STAT) /* drive busy: definitely not interrupting */ @@ -574,24 +582,25 @@ EXPORT_SYMBOL(drive_is_ready); static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout, u8 *rstat) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; unsigned long flags; int i; u8 stat; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (stat & BUSY_STAT) { local_irq_set(flags); timeout += jiffies; - while ((stat = hwif->read_status(hwif)) & BUSY_STAT) { + while ((stat = tp_ops->read_status(hwif)) & BUSY_STAT) { if (time_after(jiffies, timeout)) { /* * One last read after the timeout in case * heavy interrupt load made us not make any * progress during the timeout.. */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (!(stat & BUSY_STAT)) break; @@ -611,7 +620,7 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti */ for (i = 0; i < 10; i++) { udelay(1); - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, good, bad)) { *rstat = stat; @@ -737,6 +746,7 @@ no_80w: int ide_driveid_update(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; struct hd_driveid *id; unsigned long timeout, flags; u8 stat; @@ -747,9 +757,9 @@ int ide_driveid_update(ide_drive_t *drive) */ SELECT_MASK(drive, 1); - hwif->set_irq(hwif, 0); + tp_ops->set_irq(hwif, 0); msleep(50); - hwif->exec_command(hwif, WIN_IDENTIFY); + tp_ops->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; do { if (time_after(jiffies, timeout)) { @@ -758,11 +768,11 @@ int ide_driveid_update(ide_drive_t *drive) } msleep(50); /* give drive a breather */ - stat = hwif->read_altstatus(hwif); + stat = tp_ops->read_altstatus(hwif); } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { SELECT_MASK(drive, 0); @@ -776,8 +786,8 @@ int ide_driveid_update(ide_drive_t *drive) local_irq_restore(flags); return 0; } - hwif->input_data(drive, NULL, id, SECTOR_SIZE); - (void)hwif->read_status(hwif); /* clear drive IRQ */ + tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); + (void)tp_ops->read_status(hwif); /* clear drive IRQ */ local_irq_enable(); local_irq_restore(flags); ide_fix_driveid(id); @@ -798,6 +808,7 @@ int ide_driveid_update(ide_drive_t *drive) int ide_config_drive_speed(ide_drive_t *drive, u8 speed) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int error = 0; u8 stat; ide_task_t task; @@ -833,19 +844,19 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 0); udelay(1); - hwif->set_irq(hwif, 0); + tp_ops->set_irq(hwif, 0); memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT; task.tf.feature = SETFEATURES_XFER; task.tf.nsect = speed; - hwif->tf_load(drive, &task); + tp_ops->tf_load(drive, &task); - hwif->exec_command(hwif, WIN_SETFEATURES); + tp_ops->exec_command(hwif, WIN_SETFEATURES); if (drive->quirk_list == 2) - hwif->set_irq(hwif, 1); + tp_ops->set_irq(hwif, 1); error = __ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT|ERR_STAT, @@ -950,7 +961,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, spin_lock_irqsave(&ide_lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - hwif->exec_command(hwif, cmd); + hwif->tp_ops->exec_command(hwif, cmd); /* * Drive takes 400nS to respond, we must avoid the IRQ being * serviced before that. @@ -968,7 +979,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - hwif->exec_command(hwif, WIN_PACKETCMD); + hwif->tp_ops->exec_command(hwif, WIN_PACKETCMD); ndelay(400); spin_unlock_irqrestore(&ide_lock, flags); } @@ -999,7 +1010,7 @@ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) SELECT_DRIVE(drive); udelay (10); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, 0, BUSY_STAT)) printk("%s: ATAPI reset complete\n", drive->name); @@ -1045,7 +1056,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } - tmp = hwif->read_status(hwif); + tmp = hwif->tp_ops->read_status(hwif); if (!OK_STAT(tmp, 0, BUSY_STAT)) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -1159,6 +1170,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) ide_hwif_t *hwif; ide_hwgroup_t *hwgroup; struct ide_io_ports *io_ports; + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; spin_lock_irqsave(&ide_lock, flags); @@ -1167,6 +1179,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) io_ports = &hwif->io_ports; + tp_ops = hwif->tp_ops; + /* We must not reset with running handlers */ BUG_ON(hwgroup->handler != NULL); @@ -1175,7 +1189,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->exec_command(hwif, WIN_SRST); + tp_ops->exec_command(hwif, WIN_SRST); ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; @@ -1208,11 +1222,11 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * TODO: add ->softreset method and stop abusing ->set_irq */ /* set SRST and nIEN */ - hwif->set_irq(hwif, 4); + tp_ops->set_irq(hwif, 4); /* more than enough time */ udelay(10); /* clear SRST, leave nIEN (unless device is on the quirk list) */ - hwif->set_irq(hwif, drive->quirk_list == 2); + tp_ops->set_irq(hwif, drive->quirk_list == 2); /* more than enough time */ udelay(10); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; @@ -1257,7 +1271,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) * about locking issues (2.5 work ?). */ mdelay(1); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; /* diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 7ac44d515470..97fefabea8b8 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -325,7 +325,7 @@ static void ide_dump_sector(ide_drive_t *drive) else task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_DEVICE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); if (lba48 || (tf->device & ATA_LBA)) printk(", LBAsect=%llu", diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 3cc8ade2cc4f..c588066295db 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -126,7 +126,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) id = drive->id; /* read 512 bytes of id info */ - hwif->input_data(drive, NULL, id, SECTOR_SIZE); + hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); drive->id_read = 1; local_irq_enable(); @@ -267,6 +267,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int use_altstatus = 0, rc; unsigned long timeout; u8 s = 0, a = 0; @@ -275,8 +276,8 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) msleep(50); if (io_ports->ctl_addr) { - a = hwif->read_altstatus(hwif); - s = hwif->read_status(hwif); + a = tp_ops->read_altstatus(hwif); + s = tp_ops->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ printk(KERN_INFO "%s: probing with STATUS(0x%02x) " @@ -297,11 +298,11 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* disable DMA & overlap */ task.tf_flags = IDE_TFLAG_OUT_FEATURE; - drive->hwif->tf_load(drive, &task); + tp_ops->tf_load(drive, &task); } /* ask drive for ID */ - hwif->exec_command(hwif, cmd); + tp_ops->exec_command(hwif, cmd); timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; @@ -312,13 +313,13 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) } /* give drive a breather */ msleep(50); - s = use_altstatus ? hwif->read_altstatus(hwif) - : hwif->read_status(hwif); + s = use_altstatus ? tp_ops->read_altstatus(hwif) + : tp_ops->read_status(hwif); } while (s & BUSY_STAT); /* wait for IRQ and DRQ_STAT */ msleep(50); - s = hwif->read_status(hwif); + s = tp_ops->read_status(hwif); if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) { unsigned long flags; @@ -330,7 +331,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* drive responded with ID */ rc = 0; /* clear drive IRQ */ - (void)hwif->read_status(hwif); + (void)tp_ops->read_status(hwif); local_irq_restore(flags); } else { /* drive refused ID */ @@ -352,6 +353,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) static int try_to_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int retval; int autoprobe = 0; unsigned long cookie = 0; @@ -367,7 +369,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) autoprobe = 1; cookie = probe_irq_on(); } - hwif->set_irq(hwif, autoprobe); + tp_ops->set_irq(hwif, autoprobe); } retval = actual_try_to_identify(drive, cmd); @@ -375,9 +377,9 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) if (autoprobe) { int irq; - hwif->set_irq(hwif, 0); + tp_ops->set_irq(hwif, 0); /* clear drive IRQ */ - (void)hwif->read_status(hwif); + (void)tp_ops->read_status(hwif); udelay(5); irq = probe_irq_off(cookie); if (!hwif->irq) { @@ -402,7 +404,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) do { msleep(50); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; } while (time_before(jiffies, timeout)); @@ -417,7 +419,7 @@ static u8 ide_read_device(ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_IN_DEVICE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); return task.tf.device; } @@ -446,6 +448,7 @@ static u8 ide_read_device(ide_drive_t *drive) static int do_probe (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int rc; u8 stat; @@ -478,7 +481,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) return 3; } - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, READY_STAT, BUSY_STAT) || drive->present || cmd == WIN_PIDENTIFY) { @@ -488,7 +491,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) rc = try_to_identify(drive,cmd); } - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (stat == (BUSY_STAT | READY_STAT)) return 4; @@ -499,13 +502,13 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - hwif->exec_command(hwif, WIN_SRST); + tp_ops->exec_command(hwif, WIN_SRST); (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } /* ensure drive IRQ is clear */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (rc == 1) printk(KERN_ERR "%s: no response (status = 0x%02x)\n", @@ -519,7 +522,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(&hwif->drives[0]); msleep(50); /* ensure drive irq is clear */ - (void)hwif->read_status(hwif); + (void)tp_ops->read_status(hwif); } return rc; } @@ -530,12 +533,13 @@ static int do_probe (ide_drive_t *drive, u8 cmd) static void enable_nest (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; u8 stat; printk("%s: enabling %s -- ", hwif->name, drive->id->model); SELECT_DRIVE(drive); msleep(50); - hwif->exec_command(hwif, EXABYTE_ENABLE_NEST); + tp_ops->exec_command(hwif, EXABYTE_ENABLE_NEST); if (ide_busy_sleep(hwif)) { printk(KERN_CONT "failed (timeout)\n"); @@ -544,7 +548,7 @@ static void enable_nest (ide_drive_t *drive) msleep(50); - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (!OK_STAT(stat, 0, BAD_STAT)) printk(KERN_CONT "failed (status = 0x%02x)\n", stat); @@ -726,7 +730,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if (!drive->noprobe || drive->present) { SELECT_DRIVE(drive); - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -1083,7 +1087,7 @@ static int init_irq (ide_hwif_t *hwif) sa = IRQF_SHARED; if (io_ports->ctl_addr) - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) goto out_unlink; @@ -1361,6 +1365,9 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, hwif->host_flags |= d->host_flags; hwif->pio_mask = d->pio_mask; + if (d->tp_ops) + hwif->tp_ops = d->tp_ops; + /* ->set_pio_mode for DTC2278 is currently limited to port 0 */ if (hwif->chipset != ide_dtc2278 || hwif->channel == 0) hwif->port_ops = d->port_ops; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index ef54728a74b0..0af128826f1e 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -398,7 +398,7 @@ static void idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, count = min( (unsigned int)(bh->b_size - atomic_read(&bh->b_count)), bcount); - drive->hwif->input_data(drive, NULL, bh->b_data + + drive->hwif->tp_ops->input_data(drive, NULL, bh->b_data + atomic_read(&bh->b_count), count); bcount -= count; atomic_add(count, &bh->b_count); @@ -424,7 +424,7 @@ static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, return; } count = min((unsigned int)pc->b_count, (unsigned int)bcount); - drive->hwif->output_data(drive, NULL, pc->b_data, count); + drive->hwif->tp_ops->output_data(drive, NULL, pc->b_data, count); bcount -= count; pc->b_data += count; pc->b_count -= count; @@ -932,7 +932,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) struct ide_atapi_pc *pc = tape->pc; u8 stat; - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat & SEEK_STAT) { if (stat & ERR_STAT) { @@ -1019,7 +1019,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, * If the tape is still busy, postpone our request and service * the other device meanwhile. */ - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2)) set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index ea345369553e..aeddbbd69e86 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -64,6 +64,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) ide_hwif_t *hwif = HWIF(drive); struct ide_taskfile *tf = &task->tf; ide_handler_t *handler = NULL; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_dma_ops *dma_ops = hwif->dma_ops; if (task->data_phase == TASKFILE_MULTI_IN || @@ -80,15 +81,15 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); - hwif->set_irq(hwif, 1); + tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); - hwif->tf_load(drive, task); + tp_ops->tf_load(drive, task); } switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: - hwif->exec_command(hwif, tf->command); + tp_ops->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: @@ -125,7 +126,7 @@ EXPORT_SYMBOL_GPL(do_rw_taskfile); static ide_startstop_t set_multmode_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, READY_STAT, BAD_STAT)) drive->mult_count = drive->mult_req; @@ -146,8 +147,12 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) int retries = 5; u8 stat; - while (((stat = hwif->read_status(hwif)) & BUSY_STAT) && retries--) + while (1) { + stat = hwif->tp_ops->read_status(hwif); + if ((stat & BUSY_STAT) == 0 || retries-- == 0) + break; udelay(10); + }; if (OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_stopped; @@ -165,7 +170,7 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) static ide_startstop_t recal_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "recal_intr", stat); @@ -182,7 +187,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) u8 stat; local_irq_enable_in_hardirq(); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); @@ -205,7 +210,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. */ for (retries = 0; retries < 1000; retries++) { - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat & BUSY_STAT) udelay(10); @@ -260,9 +265,9 @@ static void ide_pio_sector(ide_drive_t *drive, struct request *rq, /* do the actual data transfer */ if (write) - hwif->output_data(drive, rq, buf, SECTOR_SIZE); + hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE); else - hwif->input_data(drive, rq, buf, SECTOR_SIZE); + hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE); kunmap_atomic(buf, KM_BIO_SRC_IRQ); #ifdef CONFIG_HIGHMEM @@ -389,7 +394,7 @@ static ide_startstop_t task_in_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->hwgroup->rq; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); /* Error? */ if (stat & ERR_STAT) @@ -423,7 +428,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = HWGROUP(drive)->rq; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) return task_error(drive, rq, __func__, stat); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 961f31c648c9..132b504168e9 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -101,7 +101,7 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) init_completion(&hwif->gendev_rel_comp); - default_hwif_transport(hwif); + hwif->tp_ops = &default_tp_ops; ide_port_init_devices_data(hwif); } diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 1bb2aa72cc7f..3e2c6125f031 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -66,6 +66,27 @@ static void falconide_output_data(ide_drive_t *drive, struct request *rq, outsw_swapw(data_addr, buf, (len + 1) / 2); } +/* Atari has a byte-swapped IDE interface */ +static const struct ide_tp_ops falconide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = falconide_input_data, + .output_data = falconide_output_data, +}; + +static const struct ide_port_info falconide_port_info = { + .tp_ops = &falconide_tp_ops, + .host_flags = IDE_HFLAG_NO_DMA, +}; + static void __init falconide_setup_ports(hw_regs_t *hw) { int i; @@ -111,12 +132,8 @@ static int __init falconide_init(void) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - /* Atari has a byte-swapped IDE interface */ - hwif->input_data = falconide_input_data; - hwif->output_data = falconide_output_data; - ide_get_lock(NULL, NULL); - ide_device_add(idx, NULL, hws); + ide_device_add(idx, &falconide_port_info, hws); ide_release_lock(); } diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index fcb04b8b0238..2dc306f852a6 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -96,6 +96,27 @@ static void q40ide_output_data(ide_drive_t *drive, struct request *rq, outsw_swapw(data_addr, buf, (len + 1) / 2); } +/* Q40 has a byte-swapped IDE interface */ +static const struct ide_tp_ops q40ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = q40ide_input_data, + .output_data = q40ide_output_data, +}; + +static const struct ide_port_info q40ide_port_info = { + .tp_ops = &q40ide_tp_ops, + .host_flags = IDE_HFLAG_NO_DMA, +}; + /* * the static array is needed to have the name reported in /proc/ioports, * hwif->name unfortunately isn't available yet @@ -141,16 +162,12 @@ static int __init q40ide_init(void) if (hwif) { hwif->chipset = ide_generic; - /* Q40 has a byte-swapped IDE interface */ - hwif->input_data = q40ide_input_data; - hwif->output_data = q40ide_output_data; - hws[i] = &hw[i]; idx[i] = hwif->index; } } - ide_device_add(idx, NULL, hws); + ide_device_add(idx, &q40ide_port_info, hws); return 0; } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 475da582fd89..ed1c9a134079 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -519,6 +519,23 @@ static void auide_setup_ports(hw_regs_t *hw, _auide_hwif *ahwif) *ata_regs = ahwif->regbase + (14 << IDE_REG_SHIFT); } +#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA +static const struct ide_tp_ops au1xxx_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = au1xxx_input_data, + .output_data = au1xxx_output_data, +}; +#endif + static const struct ide_port_ops au1xxx_port_ops = { .set_pio_mode = au1xxx_set_pio_mode, .set_dma_mode = auide_set_dma_mode, @@ -526,6 +543,9 @@ static const struct ide_port_ops au1xxx_port_ops = { static const struct ide_port_info au1xxx_port_info = { .init_dma = auide_ddma_init, +#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA + .tp_ops = &au1xxx_tp_ops, +#endif .port_ops = &au1xxx_port_ops, #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA .dma_ops = &au1xxx_dma_ops, @@ -596,15 +616,6 @@ static int au_ide_probe(struct device *dev) hw.dev = dev; hw.chipset = ide_au1xxx; - /* If the user has selected DDMA assisted copies, - then set up a few local I/O function entry points - */ - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA - hwif->input_data = au1xxx_input_data; - hwif->output_data = au1xxx_output_data; -#endif - auide_hwif.hwif = hwif; idx[0] = hwif->index; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 02fd3a877afa..5cd2b32ff0ef 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -104,7 +104,22 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) } } -static void __devinit superio_ide_init_iops (struct hwif_s *hwif) +static const struct ide_tp_ops superio_tp_ops = { + .exec_command = ide_exec_command, + .read_status = superio_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = superio_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = superio_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static void __devinit superio_init_iops(struct hwif_s *hwif) { struct pci_dev *pdev = to_pci_dev(hwif->dev); u32 dma_stat; @@ -115,21 +130,6 @@ static void __devinit superio_ide_init_iops (struct hwif_s *hwif) /* Clear error/interrupt, enable dma */ tmp = superio_ide_inb(dma_stat); outb(tmp | 0x66, dma_stat); - - hwif->read_status = superio_read_status; - hwif->read_sff_dma_status = superio_read_sff_dma_status; - - hwif->tf_read = superio_tf_read; - -} - -static void __devinit init_iops_ns87415(ide_hwif_t *hwif) -{ - struct pci_dev *dev = to_pci_dev(hwif->dev); - - if (PCI_SLOT(dev->devfn) == 0xE) - /* Built-in - assume it's under superio. */ - superio_ide_init_iops(hwif); } #endif @@ -195,7 +195,7 @@ static int ns87415_dma_end(ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* get DMA command mode */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ @@ -271,7 +271,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) outb(8, hwif->io_ports.ctl_addr); do { udelay(50); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat == 0xff) break; } while ((stat & BUSY_STAT) && --timeout); @@ -306,9 +306,6 @@ static const struct ide_dma_ops ns87415_dma_ops = { static const struct ide_port_info ns87415_chipset __devinitdata = { .name = "NS87415", -#ifdef CONFIG_SUPERIO - .init_iops = init_iops_ns87415, -#endif .init_hwif = init_hwif_ns87415, .port_ops = &ns87415_port_ops, .dma_ops = &ns87415_dma_ops, @@ -318,7 +315,16 @@ static const struct ide_port_info ns87415_chipset __devinitdata = { static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &ns87415_chipset); + struct ide_port_info d = ns87415_chipset; + +#ifdef CONFIG_SUPERIO + if (PCI_SLOT(dev->devfn) == 0xE) { + /* Built-in - assume it's under superio. */ + d.init_iops = superio_init_iops; + d.tp_ops = &superio_tp_ops; + } +#endif + return ide_setup_pci_device(dev, &d); } static const struct pci_device_id ns87415_pci_tbl[] = { diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 38765d9b0314..5b1a0e950dfd 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -808,19 +808,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); - hwif->exec_command = scc_exec_command; - hwif->read_status = scc_read_status; - hwif->read_altstatus = scc_read_altstatus; - hwif->read_sff_dma_status = scc_read_sff_dma_status; - - hwif->set_irq = scc_set_irq; - - hwif->tf_load = scc_tf_load; - hwif->tf_read = scc_tf_read; - - hwif->input_data = scc_input_data; - hwif->output_data = scc_output_data; - hwif->dma_base = dma_base; hwif->config_data = ports->ctl; } @@ -872,6 +859,21 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) hwif->ultra_mask = ATA_UDMA5; /* 100MHz */ } +static const struct ide_tp_ops scc_tp_ops = { + .exec_command = scc_exec_command, + .read_status = scc_read_status, + .read_altstatus = scc_read_altstatus, + .read_sff_dma_status = scc_read_sff_dma_status, + + .set_irq = scc_set_irq, + + .tf_load = scc_tf_load, + .tf_read = scc_tf_read, + + .input_data = scc_input_data, + .output_data = scc_output_data, +}; + static const struct ide_port_ops scc_port_ops = { .set_pio_mode = scc_set_pio_mode, .set_dma_mode = scc_set_dma_mode, @@ -895,6 +897,7 @@ static const struct ide_dma_ops scc_dma_ops = { .name = name_str, \ .init_iops = init_iops_scc, \ .init_hwif = init_hwif_scc, \ + .tp_ops = &scc_tp_ops, \ .port_ops = &scc_port_ops, \ .dma_ops = &scc_dma_ops, \ .host_flags = IDE_HFLAG_SINGLE, \ diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 86f7c4901837..5598bd5936d9 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -550,6 +550,21 @@ static int sgiioc4_dma_setup(ide_drive_t *drive) return 0; } +static const struct ide_tp_ops sgiioc4_tp_ops = { + .exec_command = ide_exec_command, + .read_status = sgiioc4_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops sgiioc4_port_ops = { .set_dma_mode = sgiioc4_set_dma_mode, /* reset DMA engine, clear IRQs */ @@ -572,6 +587,7 @@ static const struct ide_port_info sgiioc4_port_info __devinitdata = { .name = DRV_NAME, .chipset = ide_pci, .init_dma = ide_dma_sgiioc4, + .tp_ops = &sgiioc4_tp_ops, .port_ops = &sgiioc4_port_ops, .dma_ops = &sgiioc4_dma_ops, .host_flags = IDE_HFLAG_MMIO, @@ -626,8 +642,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - hwif->read_status = sgiioc4_read_status; - idx[0] = hwif->index; if (ide_device_add(idx, &d, hws)) diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ed073c6635a8..ee557d10a764 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -974,6 +974,21 @@ static void pmac_ide_init_dev(ide_drive_t *drive) } } +static const struct ide_tp_ops pmac_tp_ops = { + .exec_command = pmac_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = pmac_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops pmac_ide_ata6_port_ops = { .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, @@ -1003,10 +1018,11 @@ static const struct ide_port_info pmac_port_info = { .name = DRV_NAME, .init_dma = pmac_ide_init_dma, .chipset = ide_pmac, + .tp_ops = &pmac_tp_ops, + .port_ops = &pmac_ide_port_ops, #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC .dma_ops = &pmac_dma_ops, #endif - .port_ops = &pmac_ide_port_ops, .host_flags = IDE_HFLAG_SET_PIO_MODE_KEEP_DMA | IDE_HFLAG_POST_SET_MODE | IDE_HFLAG_MMIO | @@ -1106,9 +1122,6 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) if (hwif == NULL) return -ENOENT; - hwif->exec_command = pmac_exec_command; - hwif->set_irq = pmac_set_irq; - idx[0] = hwif->index; ide_device_add(idx, &d, hws); diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index c1b609d9cb28..804c3ef245f9 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -125,7 +125,7 @@ int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) * we tune the drive then try to grab DMA ownership if we want to be * the DMA end. This has to be become dynamic to handle hot-plug. */ - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { printk(KERN_INFO "%s: simplex device: DMA disabled\n", d->name); return -1; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 2a86af91f64a..659db3f7ae08 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -142,7 +142,8 @@ static void ide_scsi_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount, int write) { ide_hwif_t *hwif = drive->hwif; - xfer_func_t *xf = write ? hwif->output_data : hwif->input_data; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; char *buf; int count; @@ -246,9 +247,9 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { ide_hwif_t *hwif = drive->hwif; - if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); + hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE); rq->errors++; diff --git a/include/linux/ide.h b/include/linux/ide.h index e340218b2a5f..1286a2275efb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -408,8 +408,28 @@ typedef struct ide_drive_s { ((1<> (c)) & 1) +struct ide_task_s; struct ide_port_info; +struct ide_tp_ops { + void (*exec_command)(struct hwif_s *, u8); + u8 (*read_status)(struct hwif_s *); + u8 (*read_altstatus)(struct hwif_s *); + u8 (*read_sff_dma_status)(struct hwif_s *); + + void (*set_irq)(struct hwif_s *, int); + + void (*tf_load)(ide_drive_t *, struct ide_task_s *); + void (*tf_read)(ide_drive_t *, struct ide_task_s *); + + void (*input_data)(ide_drive_t *, struct request *, void *, + unsigned int); + void (*output_data)(ide_drive_t *, struct request *, void *, + unsigned int); +}; + +extern const struct ide_tp_ops default_tp_ops; + struct ide_port_ops { /* host specific initialization of a device */ void (*init_dev)(ide_drive_t *); @@ -447,8 +467,6 @@ struct ide_dma_ops { void (*dma_timeout)(struct ide_drive_s *); }; -struct ide_task_s; - typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ struct hwif_s *mate; /* other hwif from same PCI chip */ @@ -486,22 +504,10 @@ typedef struct hwif_s { void (*rw_disk)(ide_drive_t *, struct request *); + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; - void (*exec_command)(struct hwif_s *, u8); - u8 (*read_status)(struct hwif_s *); - u8 (*read_altstatus)(struct hwif_s *); - u8 (*read_sff_dma_status)(struct hwif_s *); - - void (*set_irq)(struct hwif_s *, int); - - void (*tf_load)(ide_drive_t *, struct ide_task_s *); - void (*tf_read)(ide_drive_t *, struct ide_task_s *); - - void (*input_data)(ide_drive_t *, struct request *, void *, unsigned); - void (*output_data)(ide_drive_t *, struct request *, void *, unsigned); - void (*ide_dma_clear_irq)(ide_drive_t *drive); /* dma physical region descriptor table (cpu view) */ @@ -949,6 +955,19 @@ typedef struct ide_task_s { void ide_tf_dump(const char *, struct ide_taskfile *); +void ide_exec_command(ide_hwif_t *, u8); +u8 ide_read_status(ide_hwif_t *); +u8 ide_read_altstatus(ide_hwif_t *); +u8 ide_read_sff_dma_status(ide_hwif_t *); + +void ide_set_irq(ide_hwif_t *, int); + +void ide_tf_load(ide_drive_t *, ide_task_t *); +void ide_tf_read(ide_drive_t *, ide_task_t *); + +void ide_input_data(ide_drive_t *, struct request *, void *, unsigned int); +void ide_output_data(ide_drive_t *, struct request *, void *, unsigned int); + extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); @@ -1022,8 +1041,6 @@ static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, } #endif -extern void default_hwif_transport(ide_hwif_t *); - typedef struct ide_pci_enablebit_s { u8 reg; /* byte pci reg holding the enable-bit */ u8 mask; /* mask to isolate the enable-bit */ @@ -1112,6 +1129,7 @@ struct ide_port_info { int (*init_dma)(ide_hwif_t *, const struct ide_port_info *); + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; -- cgit v1.2.3 From 48c3c1072651922ed153bcf0a33ea82cf20df390 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:57 +0200 Subject: ide: add struct ide_host (take 3) * Add struct ide_host which keeps pointers to host's ports. * Add ide_host_alloc[_all]() and ide_host_remove() helpers. * Pass 'struct ide_host *host' instead of 'u8 *idx' to ide_device_add[_all]() and rename it to ide_host_register[_all](). * Convert host drivers and core code to use struct ide_host. * Remove no longer needed ide_find_port(). * Make ide_find_port_slot() static. * Unexport ide_unregister(). v2: * Add missing 'struct ide_host *host' to macide.c. v3: * Fix build problem in pmac.c (s/ide_alloc_host/ide_host_alloc/) (Noticed by Stephen Rothwell). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 40 +++++------------ drivers/ide/arm/ide_arm.c | 12 ++--- drivers/ide/arm/palm_bk3710.c | 13 ++---- drivers/ide/arm/rapide.c | 17 +++---- drivers/ide/h8300/ide-h8300.c | 14 ++---- drivers/ide/ide-generic.c | 28 ++++-------- drivers/ide/ide-pnp.c | 17 +++---- drivers/ide/ide-probe.c | 94 ++++++++++++++++++++++++++++----------- drivers/ide/ide.c | 2 - drivers/ide/legacy/buddha.c | 19 +++----- drivers/ide/legacy/falconide.c | 11 ++--- drivers/ide/legacy/gayle.c | 16 +++---- drivers/ide/legacy/ide-4drives.c | 23 +++------- drivers/ide/legacy/ide-cs.c | 46 +++++++++---------- drivers/ide/legacy/ide_platform.c | 23 +++++----- drivers/ide/legacy/macide.c | 12 ++--- drivers/ide/legacy/q40ide.c | 15 +++---- drivers/ide/mips/au1xxx-ide.c | 25 +++++------ drivers/ide/mips/swarm.c | 13 +++--- drivers/ide/pci/cmd640.c | 30 ++++--------- drivers/ide/pci/cs5520.c | 8 ++-- drivers/ide/pci/delkin_cb.c | 19 +++----- drivers/ide/pci/scc_pata.c | 24 +++++----- drivers/ide/pci/sgiioc4.c | 13 +++--- drivers/ide/ppc/pmac.c | 10 ++--- drivers/ide/setup-pci.c | 48 ++++++++------------ include/linux/ide.h | 21 ++++----- 27 files changed, 264 insertions(+), 349 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 0283d162f7f7..6fa58425466a 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -72,7 +72,7 @@ struct icside_state { void __iomem *ioc_base; unsigned int sel; unsigned int type; - ide_hwif_t *hwif[2]; + struct ide_host *host; }; #define ICS_TYPE_A3IN 0 @@ -442,10 +442,9 @@ static void icside_setup_ports(hw_regs_t *hw, void __iomem *base, static int __init icside_register_v5(struct icside_state *state, struct expansion_card *ec) { - ide_hwif_t *hwif; void __iomem *base; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -465,17 +464,15 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec); - hwif = ide_find_port(); - if (!hwif) + host = ide_host_alloc(NULL, hws); + if (host == NULL) return -ENODEV; - state->hwif[0] = hwif; + state->host = host; ecard_set_drvdata(ec, state); - idx[0] = hwif->index; - - ide_device_add(idx, NULL, hws); + ide_host_register(host, NULL, hws); return 0; } @@ -492,12 +489,11 @@ static const struct ide_port_info icside_v6_port_info __initdata = { static int __init icside_register_v6(struct icside_state *state, struct expansion_card *ec) { - ide_hwif_t *hwif, *mate; void __iomem *ioc_base, *easi_base; + struct ide_host *host; unsigned int sel = 0; int ret; hw_regs_t hw[2], *hws[] = { &hw[0], NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = icside_v6_port_info; ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); @@ -537,25 +533,11 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec); icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec); - /* - * Find and register the interfaces. - */ - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&d, hws); + if (host == NULL) return -ENODEV; - hwif->chipset = ide_acorn; - - idx[0] = hwif->index; - - mate = ide_find_port(); - if (mate) { - hws[1] = &hw[1]; - idx[1] = mate->index; - } - - state->hwif[0] = hwif; - state->hwif[1] = mate; + state->host = host; ecard_set_drvdata(ec, state); @@ -565,7 +547,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_device_add(idx, &d, hws); + ide_host_register(host, &d, hws); return 0; diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index e9831bbd988a..9efd7a86db45 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -28,10 +28,9 @@ static int __init ide_arm_init(void) { - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(base, 8, DRV_NAME)) { printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", @@ -51,12 +50,9 @@ static int __init ide_arm_init(void) hw.irq = IDE_ARM_IRQ; hw.chipset = ide_generic; - hwif = ide_find_port(); - if (hwif) { - idx[0] = hwif->index; - - ide_device_add(idx, NULL, hws); - } + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); return 0; } diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 545563bc7e23..24389a571c37 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -347,11 +347,10 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) { struct clk *clk; struct resource *mem, *irq; - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base, rate; int i; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; clk = clk_get(NULL, "IDECLK"); if (IS_ERR(clk)) @@ -393,15 +392,11 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) hw.irq = irq->start; hw.chipset = ide_palm3710; - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&palm_bk3710_port_info, hws); + if (host == NULL) goto out; - i = hwif->index; - - idx[0] = i; - - ide_device_add(idx, &palm_bk3710_port_info, hws); + ide_host_register(host, &palm_bk3710_port_info, hws); return 0; out: diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index a45c2f694949..11f3307385de 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -32,11 +32,10 @@ static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base, static int __devinit rapide_probe(struct expansion_card *ec, const struct ecard_id *id) { - ide_hwif_t *hwif; void __iomem *base; + struct ide_host *host; int ret; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; ret = ecard_request_resources(ec); if (ret) @@ -53,17 +52,15 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) hw.chipset = ide_generic; hw.dev = &ec->dev; - hwif = ide_find_port(); - if (hwif == NULL) { + host = ide_host_alloc(&rapide_port_info, hws); + if (host == NULL) { ret = -ENOENT; goto release; } - idx[0] = hwif->index; + ide_host_register(host, &rapide_port_info, hws); - ide_device_add(idx, &rapide_port_info, hws); - - ecard_set_drvdata(ec, hwif); + ecard_set_drvdata(ec, host); goto out; release: @@ -74,11 +71,11 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) static void __devexit rapide_remove(struct expansion_card *ec) { - ide_hwif_t *hwif = ecard_get_drvdata(ec); + struct ide_host *host = ecard_get_drvdata(ec); ecard_set_drvdata(ec, NULL); - ide_unregister(hwif); + ide_host_remove(host); ecard_release_resources(ec); } diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 84644e150531..15f76690a48c 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -191,10 +191,8 @@ static const struct ide_port_info h8300_port_info = { static int __init h8300_ide_init(void) { - ide_hwif_t *hwif; - int index; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -207,15 +205,11 @@ static int __init h8300_ide_init(void) hw_setup(&hw); - hwif = ide_find_port_slot(&h8300_port_info); - if (hwif == NULL) + host = ide_host_alloc(&h8300_port_info, hws); + if (host == NULL) return -ENOENT; - index = hwif->index; - - idx[0] = index; - - ide_device_add(idx, &h8300_port_info, hws); + ide_host_register(host, &h8300_port_info, hws); return 0; diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index a5c352abff59..e8818362eb46 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -28,27 +28,24 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports"); static ssize_t store_add(struct class *cls, const char *buf, size_t n) { - ide_hwif_t *hwif; + struct ide_host *host; unsigned int base, ctl; int irq; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) return -EINVAL; - hwif = ide_find_port(); - if (hwif == NULL) - return -ENOENT; - memset(&hw, 0, sizeof(hw)); ide_std_init_ports(&hw, base, ctl); hw.irq = irq; hw.chipset = ide_generic; - idx[0] = hwif->index; + host = ide_host_alloc(NULL, hws); + if (host == NULL) + return -ENOENT; - ide_device_add(idx, NULL, hws); + ide_host_register(host, NULL, hws); return n; }; @@ -89,18 +86,16 @@ static int __init ide_generic_sysfs_init(void) static int __init ide_generic_init(void) { hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; - u8 idx[MAX_HWIFS]; + struct ide_host *host; int i; printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module " "parameter for probing all legacy ISA IDE ports\n"); for (i = 0; i < MAX_HWIFS; i++) { - ide_hwif_t *hwif; unsigned long io_addr = ide_default_io_base(i); hws[i] = NULL; - idx[i] = 0xff; if ((probe_mask & (1 << i)) && io_addr) { if (!request_region(io_addr, 8, DRV_NAME)) { @@ -118,23 +113,18 @@ static int __init ide_generic_init(void) continue; } - hwif = ide_find_port(); - if (hwif == NULL) - continue; - - hwif->chipset = ide_generic; - memset(&hw[i], 0, sizeof(hw[i])); ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206); hw[i].irq = ide_default_irq(io_addr); hw[i].chipset = ide_generic; hws[i] = &hw[i]; - idx[i] = i; } } - ide_device_add_all(idx, NULL, hws); + host = ide_host_alloc_all(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 89cd5cbe8573..4458ca61897a 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -29,7 +29,7 @@ static struct pnp_device_id idepnp_devices[] = { static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base, ctl; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -59,14 +59,11 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) hw.irq = pnp_irq(dev, 0); hw.chipset = ide_generic; - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; + host = ide_host_alloc(NULL, hws); + if (host) { + pnp_set_drvdata(dev, host); - pnp_set_drvdata(dev, hwif); - - ide_device_add(idx, NULL, hws); + ide_host_register(host, NULL, hws); return 0; } @@ -79,9 +76,9 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) static void idepnp_remove(struct pnp_dev *dev) { - ide_hwif_t *hwif = pnp_get_drvdata(dev); + struct ide_host *host = pnp_get_drvdata(dev); - ide_unregister(hwif); + ide_host_remove(host); release_region(pnp_port_start(dev, 1), 1); release_region(pnp_port_start(dev, 0), 8); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index c588066295db..84a89561ec0f 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1477,7 +1477,7 @@ static int ide_sysfs_register_port(ide_hwif_t *hwif) * Return the new hwif. If we are out of free slots return NULL. */ -ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d) +static ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d) { ide_hwif_t *hwif; int i; @@ -1523,14 +1523,63 @@ out_found: ide_init_port_data(hwif, i); return hwif; } -EXPORT_SYMBOL_GPL(ide_find_port_slot); -int ide_device_add_all(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) +struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, + hw_regs_t **hws) +{ + struct ide_host *host; + int i; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (host == NULL) + return NULL; + + for (i = 0; i < MAX_HWIFS; i++) { + ide_hwif_t *hwif; + + if (hws[i] == NULL) + continue; + + hwif = ide_find_port_slot(d); + if (hwif) { + hwif->chipset = hws[i]->chipset; + + host->ports[i] = hwif; + host->n_ports++; + } + } + + if (host->n_ports == 0) { + kfree(host); + return NULL; + } + + return host; +} +EXPORT_SYMBOL_GPL(ide_host_alloc_all); + +struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) +{ + hw_regs_t *hws_all[MAX_HWIFS]; + int i; + + for (i = 0; i < MAX_HWIFS; i++) + hws_all[i] = (i < 4) ? hws[i] : NULL; + + return ide_host_alloc_all(d, hws_all); +} +EXPORT_SYMBOL_GPL(ide_host_alloc); + +int ide_host_register(struct ide_host *host, const struct ide_port_info *d, + hw_regs_t **hws) { ide_hwif_t *hwif, *mate = NULL; + u8 idx[MAX_HWIFS]; int i, rc = 0; for (i = 0; i < MAX_HWIFS; i++) { + idx[i] = host->ports[i] ? host->ports[i]->index : 0xff; + if (idx[i] == 0xff) { mate = NULL; continue; @@ -1626,22 +1675,20 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) return rc; } -EXPORT_SYMBOL_GPL(ide_device_add_all); +EXPORT_SYMBOL_GPL(ide_host_register); -int ide_device_add(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) +void ide_host_remove(struct ide_host *host) { - hw_regs_t *hws_all[MAX_HWIFS]; - u8 idx_all[MAX_HWIFS]; int i; for (i = 0; i < MAX_HWIFS; i++) { - hws_all[i] = (i < 4) ? hws[i] : NULL; - idx_all[i] = (i < 4) ? idx[i] : 0xff; + if (host->ports[i]) + ide_unregister(host->ports[i]); } - return ide_device_add_all(idx_all, d, hws_all); + kfree(host); } -EXPORT_SYMBOL_GPL(ide_device_add); +EXPORT_SYMBOL_GPL(ide_host_remove); void ide_port_scan(ide_hwif_t *hwif) { @@ -1662,11 +1709,10 @@ void ide_port_scan(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_port_scan); -static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, +static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw, u8 port_no, const struct ide_port_info *d, unsigned long config) { - ide_hwif_t *hwif; unsigned long base, ctl; int irq; @@ -1698,31 +1744,29 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, hw->chipset = d->chipset; hw->config = config; - hwif = ide_find_port_slot(d); - if (hwif) { - hwif->chipset = hw->chipset; - - hws[port_no] = hw; - idx[port_no] = hwif->index; - } + hws[port_no] = hw; } int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + struct ide_host *host; hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0) - ide_legacy_init_one(idx, hws, &hw[0], 0, d, config); - ide_legacy_init_one(idx, hws, &hw[1], 1, d, config); + ide_legacy_init_one(hws, &hw[0], 0, d, config); + ide_legacy_init_one(hws, &hw[1], 1, d, config); - if (idx[0] == 0xff && idx[1] == 0xff && + if (hws[0] == NULL && hws[1] == NULL && (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; + + ide_host_register(host, d, hws); return 0; } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 132b504168e9..7e9575d1aee3 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -276,8 +276,6 @@ void ide_unregister(ide_hwif_t *hwif) mutex_unlock(&ide_cfg_mtx); } -EXPORT_SYMBOL(ide_unregister); - void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) { memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index c61bc6a1db36..2625667fab4c 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -150,18 +150,15 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { - ide_hwif_t *hwif; - int i; - struct zorro_dev *z = NULL; + struct ide_host *host; u_long buddha_board = 0; BuddhaType type; - int buddha_num_hwifs; + int buddha_num_hwifs, i; while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { unsigned long board; hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { buddha_num_hwifs = BUDDHA_NUM_HWIFS; @@ -226,16 +223,12 @@ fail_base2: buddha_setup_ports(&hw[i], base, ctl, irq_port, ack_intr); - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_generic; - - hws[i] = &hw[i]; - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, NULL, hws); + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); } return 0; diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 3e2c6125f031..4eb5c3f9fecc 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -112,7 +112,7 @@ static void __init falconide_setup_ports(hw_regs_t *hw) static int __init falconide_init(void) { - ide_hwif_t *hwif; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) @@ -127,13 +127,10 @@ static int __init falconide_init(void) falconide_setup_ports(&hw); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; - + host = ide_host_alloc(&falconide_port_info, hws); + if (host) { ide_get_lock(NULL, NULL); - ide_device_add(idx, &falconide_port_info, hws); + ide_host_register(host, &falconide_port_info, hws); ide_release_lock(); } diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 7baeefa870fa..13d22bded6b4 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -127,9 +127,9 @@ static int __init gayle_init(void) unsigned long phys_base, res_start, res_n; unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; + struct ide_host *host; int a4000, i; hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_AMIGA) return -ENODEV; @@ -172,23 +172,17 @@ found: return -EBUSY; for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { - ide_hwif_t *hwif; - base = (unsigned long)ZTWO_VADDR(phys_base + i * GAYLE_NEXT_PORT); ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; gayle_setup_ports(&hw[i], base, ctrlport, irqport, ack_intr); - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_generic; - - hws[i] = &hw[i]; - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, NULL, hws); + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); return 0; } diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 6310dc50e3c5..5935153ef2ad 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -28,10 +28,9 @@ static const struct ide_port_info ide_4drives_port_info = { static int __init ide_4drives_init(void) { - ide_hwif_t *hwif, *mate; + struct ide_host *host; unsigned long base = 0x1f0, ctl = 0x3f6; - hw_regs_t hw, *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL }; if (probe_4drives == 0) return -ENODEV; @@ -55,21 +54,9 @@ static int __init ide_4drives_init(void) hw.irq = 14; hw.chipset = ide_4drives; - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_4drives; - - hws[0] = &hw; - idx[0] = hwif->index; - } - - mate = ide_find_port(); - if (mate) { - hws[1] = &hw; - idx[1] = mate->index; - } - - ide_device_add(idx, &ide_4drives_port_info, hws); + host = ide_host_alloc(&ide_4drives_port_info, hws); + if (host) + ide_host_register(host, &ide_4drives_port_info, hws); return 0; } diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index f93d5454ebf8..1a4b9e6887fa 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -74,7 +74,7 @@ INT_MODULE_PARM(pc_debug, 0); typedef struct ide_info_t { struct pcmcia_device *p_dev; - ide_hwif_t *hwif; + struct ide_host *host; int ndev; dev_node_t node; } ide_info_t; @@ -132,7 +132,7 @@ static int ide_probe(struct pcmcia_device *link) static void ide_detach(struct pcmcia_device *link) { ide_info_t *info = link->priv; - ide_hwif_t *hwif = info->hwif; + ide_hwif_t *hwif = info->host->ports[0]; unsigned long data_addr, ctl_addr; DEBUG(0, "ide_detach(0x%p)\n", link); @@ -157,13 +157,13 @@ static const struct ide_port_info idecs_port_info = { .host_flags = IDE_HFLAG_NO_DMA, }; -static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, +static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle) { + struct ide_host *host; ide_hwif_t *hwif; int i; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(io, 8, DRV_NAME)) { printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", @@ -184,26 +184,26 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, hw.chipset = ide_pci; hw.dev = &handle->dev; - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&idecs_port_info, hws); + if (host == NULL) goto out_release; - idx[0] = hwif->index; + ide_host_register(host, &idecs_port_info, hws); - ide_device_add(idx, &idecs_port_info, hws); + hwif = host->ports[0]; if (hwif->present) - return hwif; + return host; /* retry registration in case device is still spinning up */ for (i = 0; i < 10; i++) { msleep(100); ide_port_scan(hwif); if (hwif->present) - return hwif; + return host; } - return hwif; + return host; out_release: release_region(ctl, 1); @@ -235,7 +235,7 @@ static int ide_config(struct pcmcia_device *link) cistpl_cftable_entry_t *cfg; int pass, last_ret = 0, last_fn = 0, is_kme = 0; unsigned long io_base, ctl_base; - ide_hwif_t *hwif; + struct ide_host *host; DEBUG(0, "ide_config(0x%p)\n", link); @@ -330,21 +330,21 @@ static int ide_config(struct pcmcia_device *link) if (is_kme) outb(0x81, ctl_base+1); - hwif = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); - if (hwif == NULL && link->io.NumPorts1 == 0x20) { + host = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); + if (host == NULL && link->io.NumPorts1 == 0x20) { outb(0x02, ctl_base + 0x10); - hwif = idecs_register(io_base + 0x10, ctl_base + 0x10, + host = idecs_register(io_base + 0x10, ctl_base + 0x10, link->irq.AssignedIRQ, link); } - if (hwif == NULL) + if (host == NULL) goto failed; info->ndev = 1; - sprintf(info->node.dev_name, "hd%c", 'a' + hwif->index * 2); - info->node.major = hwif->major; + sprintf(info->node.dev_name, "hd%c", 'a' + host->ports[0]->index * 2); + info->node.major = host->ports[0]->major; info->node.minor = 0; - info->hwif = hwif; + info->host = host; link->dev_node = &info->node; printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n", info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10); @@ -375,15 +375,15 @@ failed: static void ide_release(struct pcmcia_device *link) { ide_info_t *info = link->priv; - ide_hwif_t *hwif = info->hwif; + struct ide_host *host = info->host; DEBUG(0, "ide_release(0x%p)\n", link); - if (info->ndev) { + if (info->ndev) /* FIXME: if this fails we need to queue the cleanup somehow -- need to investigate the required PCMCIA magic */ - ide_unregister(hwif); - } + ide_host_remove(host); + info->ndev = 0; pcmcia_disable_device(link); diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 3d71e336a221..58a942c6a131 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -52,11 +52,10 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) { struct resource *res_base, *res_alt, *res_irq; void __iomem *base, *alt_base; - ide_hwif_t *hwif; struct pata_platform_info *pdata; + struct ide_host *host; int ret = 0, mmio = 0; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = platform_ide_port_info; pdata = pdev->dev.platform_data; @@ -93,12 +92,6 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) res_alt->start, res_alt->end - res_alt->start + 1); } - hwif = ide_find_port(); - if (!hwif) { - ret = -ENODEV; - goto out; - } - memset(&hw, 0, sizeof(hw)); plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; @@ -106,11 +99,15 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - idx[0] = hwif->index; + host = ide_host_alloc(&d, hws); + if (host == NULL) { + ret = -ENODEV; + goto out; + } - ide_device_add(idx, &d, hws); + ide_host_register(host, &d, hws); - platform_set_drvdata(pdev, hwif); + platform_set_drvdata(pdev, host); return 0; @@ -120,9 +117,9 @@ out: static int __devexit plat_ide_remove(struct platform_device *pdev) { - ide_hwif_t *hwif = pdev->dev.driver_data; + struct ide_host *host = pdev->dev.driver_data; - ide_unregister(hwif); + ide_host_remove(host); return 0; } diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index d839df2239fc..b49cf8c2b91a 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -91,8 +91,8 @@ static const char *mac_ide_name[] = static int __init macide_init(void) { - ide_hwif_t *hwif; ide_ack_intr_t *ack_intr; + struct ide_host *host; unsigned long base; int irq; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -125,13 +125,9 @@ static int __init macide_init(void) macide_setup_ports(&hw, base, irq, ack_intr); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; - - ide_device_add(idx, NULL, hws); - } + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); return 0; } diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 2dc306f852a6..8fb4438a6afd 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -131,10 +131,9 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ static int __init q40ide_init(void) { + struct ide_host *host; int i; - ide_hwif_t *hwif; hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_Q40) return -ENODEV; @@ -158,16 +157,12 @@ static int __init q40ide_init(void) q40_ide_setup_ports(&hw[i], pcide_bases[i], NULL, q40ide_default_irq(pcide_bases[i])); - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_generic; - - hws[i] = &hw[i]; - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, &q40ide_port_info, hws); + host = ide_host_alloc(&q40ide_port_info, hws); + if (host) + ide_host_register(host, &q40ide_port_info, hws); return 0; } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index ed1c9a134079..903c628bddd0 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -563,11 +563,10 @@ static int au_ide_probe(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); _auide_hwif *ahwif = &auide_hwif; - ide_hwif_t *hwif; struct resource *res; + struct ide_host *host; int ret = 0; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) char *mode = "MWDMA2"; @@ -604,25 +603,23 @@ static int au_ide_probe(struct device *dev) goto out; } - hwif = ide_find_port(); - if (hwif == NULL) { - ret = -ENOENT; - goto out; - } - memset(&hw, 0, sizeof(hw)); auide_setup_ports(&hw, ahwif); hw.irq = ahwif->irq; hw.dev = dev; hw.chipset = ide_au1xxx; - auide_hwif.hwif = hwif; + host = ide_host_alloc(&au1xxx_port_info, hws); + if (host == NULL) { + ret = -ENOENT; + goto out; + } - idx[0] = hwif->index; + ide_host_register(host, &au1xxx_port_info, hws); - ide_device_add(idx, &au1xxx_port_info, hws); + auide_hwif.hwif = host->ports[0]; - dev_set_drvdata(dev, hwif); + dev_set_drvdata(dev, host); printk(KERN_INFO "Au1xxx IDE(builtin) configured for %s\n", mode ); @@ -634,10 +631,10 @@ static int au_ide_remove(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct resource *res; - ide_hwif_t *hwif = dev_get_drvdata(dev); + struct ide_host *host = dev_get_drvdata(dev); _auide_hwif *ahwif = &auide_hwif; - ide_unregister(hwif); + ide_host_remove(host); iounmap((void *)ahwif->regbase); diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index c1ffb83a2de7..b12d9d224831 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -72,12 +72,11 @@ static const struct ide_port_info swarm_port_info = { */ static int __devinit swarm_ide_probe(struct device *dev) { - ide_hwif_t *hwif; u8 __iomem *base; + struct ide_host *host; phys_t offset, size; int i; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (!SIBYTE_HAVE_IDE) return -ENODEV; @@ -116,15 +115,13 @@ static int __devinit swarm_ide_probe(struct device *dev) hw.irq = K_INT_GB_IDE; hw.chipset = ide_generic; - hwif = ide_find_port_slot(&swarm_port_info); - if (hwif == NULL) + host = ide_host_alloc(&swarm_port_info, hws); + if (host == NULL) goto err; - idx[0] = hwif->index; + ide_host_register(host, &swarm_port_info, hws); - ide_device_add(idx, &swarm_port_info, hws); - - dev_set_drvdata(dev, hwif); + dev_set_drvdata(dev, host); return 0; err: diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index ccde1e444e13..013697b8cef4 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -180,11 +180,6 @@ static u8 recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */ static DEFINE_SPINLOCK(cmd640_lock); -/* - * These are initialized to point at the devices we control - */ -static ide_hwif_t *cmd_hwif0, *cmd_hwif1; - /* * Interface to access cmd640x registers */ @@ -714,11 +709,11 @@ static int cmd640x_init_one(unsigned long base, unsigned long ctl) */ static int __init cmd640x_init(void) { + struct ide_host *host; int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (cmd640_vlb && probe_for_cmd640_vlb()) { bus_type = "VLB"; @@ -781,17 +776,10 @@ static int __init cmd640x_init(void) printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x" "\n", 'a' + cmd640_chip_version - 1, bus_type, cfr); - cmd_hwif0 = ide_find_port(); - /* * Initialize data for primary port */ - if (cmd_hwif0) { - cmd_hwif0->chipset = ide_cmd640; - - hws[0] = &hw[0]; - idx[0] = cmd_hwif0->index; - } + hws[0] = &hw[0]; /* * Ensure compatibility by always using the slowest timings @@ -831,13 +819,9 @@ static int __init cmd640x_init(void) /* * Initialize data for secondary cmd640 port, if enabled */ - if (second_port_cmd640) { - cmd_hwif1 = ide_find_port(); - if (cmd_hwif1) { - hws[1] = &hw[1]; - idx[1] = cmd_hwif1->index; - } - } + if (second_port_cmd640) + hws[1] = &hw[1]; + printk(KERN_INFO "cmd640: %sserialized, secondary interface %s\n", second_port_cmd640 ? "" : "not ", port2); @@ -845,7 +829,9 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - ide_device_add(idx, &cmd640_port_info, hws); + host = ide_host_alloc(&cmd640_port_info, hws); + if (host) + ide_host_register(host, &cmd640_port_info, hws); return 1; } diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index e8e7df1915bf..b8ec06d22c61 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -114,9 +114,9 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { + struct ide_host *host; const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; ide_setup_pci_noise(dev, d); @@ -138,9 +138,11 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic * do all the device setup for us */ - ide_pci_setup_ports(dev, d, 14, &idx[0], &hw[0], &hws[0]); + ide_pci_setup_ports(dev, d, 14, &hw[0], &hws[0]); - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host) + ide_host_register(host, d, hws); return 0; } diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 33fe15db408a..5eb9d9325184 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -56,11 +56,10 @@ static const struct ide_port_info delkin_cb_port_info = { static int __devinit delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) { + struct ide_host *host; unsigned long base; - ide_hwif_t *hwif = NULL; int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; rc = pci_enable_device(dev); if (rc) { @@ -87,17 +86,13 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) hw.dev = &dev->dev; hw.chipset = ide_pci; /* this enables IRQ sharing */ - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&delkin_cb_port_info, hws); + if (host == NULL) goto out_disable; - i = hwif->index; + ide_host_register(host, &delkin_cb_port_info, hws); - idx[0] = i; - - ide_device_add(idx, &delkin_cb_port_info, hws); - - pci_set_drvdata(dev, hwif); + pci_set_drvdata(dev, host); return 0; @@ -110,9 +105,9 @@ out_disable: static void delkin_cb_remove (struct pci_dev *dev) { - ide_hwif_t *hwif = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); - ide_unregister(hwif); + ide_host_remove(host); pci_release_regions(dev); pci_disable_device(dev); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 5b1a0e950dfd..d5e2ba6bacd6 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -65,7 +65,7 @@ static struct scc_ports { unsigned long ctl, dma; - ide_hwif_t *hwif; /* for removing port from system */ + struct ide_host *host; /* for removing port from system */ } scc_ports[MAX_HWIFS]; /* PIO transfer mode table */ @@ -586,15 +586,10 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { struct scc_ports *ports = pci_get_drvdata(dev); - ide_hwif_t *hwif = NULL; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; int i; - hwif = ide_find_port_slot(d); - if (hwif == NULL) - return -ENOMEM; - memset(&hw, 0, sizeof(hw)); for (i = 0; i <= 8; i++) hw.io_ports_array[i] = ports->dma + 0x20 + i * 4; @@ -602,9 +597,13 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.dev = &dev->dev; hw.chipset = ide_pci; - idx[0] = hwif->index; + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; - ide_device_add(idx, d, hws); + ide_host_register(host, d, hws); + + ports->host = host; return 0; } @@ -848,8 +847,6 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) { struct scc_ports *ports = ide_get_hwifdata(hwif); - ports->hwif = hwif; - /* PTERADD */ out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma); @@ -932,7 +929,8 @@ static int __devinit scc_init_one(struct pci_dev *dev, const struct pci_device_i static void __devexit scc_remove(struct pci_dev *dev) { struct scc_ports *ports = pci_get_drvdata(dev); - ide_hwif_t *hwif = ports->hwif; + struct ide_host *host = ports->host; + ide_hwif_t *hwif = host->ports[0]; if (hwif->dmatable_cpu) { pci_free_consistent(dev, PRD_ENTRIES * PRD_BYTES, @@ -940,7 +938,7 @@ static void __devexit scc_remove(struct pci_dev *dev) hwif->dmatable_cpu = NULL; } - ide_unregister(hwif); + ide_host_remove(host); iounmap((void*)ports->dma); iounmap((void*)ports->ctl); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 5598bd5936d9..440f43a86ad3 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -600,9 +600,8 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) unsigned long cmd_base, irqport; unsigned long bar0, cmd_phys_base, ctl; void __iomem *virt_base; - ide_hwif_t *hwif; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = sgiioc4_port_info; /* Get the CmdBlk and CtrlBlk Base Registers */ @@ -635,16 +634,14 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) hw.chipset = ide_pci; hw.dev = &dev->dev; - hwif = ide_find_port_slot(&d); - if (hwif == NULL) - goto err; - /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - idx[0] = hwif->index; + host = ide_host_alloc(&d, hws); + if (host == NULL) + goto err; - if (ide_device_add(idx, &d, hws)) + if (ide_host_register(host, &d, hws)) return -EIO; return 0; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ee557d10a764..ecd2f28da1ba 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1039,9 +1039,9 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) { struct device_node *np = pmif->node; const int *bidp; + struct ide_host *host; ide_hwif_t *hwif; hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = pmac_port_info; pmif->broken_dma = pmif->broken_dma_warn = 0; @@ -1118,13 +1118,13 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id, pmif->mediabay ? " (mediabay)" : "", hw->irq); - hwif = ide_find_port_slot(&d); - if (hwif == NULL) + host = ide_host_alloc(&d, hws); + if (host == NULL) return -ENOENT; - idx[0] = hwif->index; + ide_host_register(host, &d, hws); - ide_device_add(idx, &d, hws); + hwif = host->ports[0]; return 0; } diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 804c3ef245f9..1c0c5570dec8 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -289,7 +289,7 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * } /** - * ide_hwif_configure - configure an IDE interface + * ide_hw_configure - configure a hw_regs_t instance * @dev: PCI device holding interface * @d: IDE port info * @port: port number @@ -300,23 +300,20 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * * is done per interface port rather than per PCI device. There may be * more than one port per device. * - * Returns the new hardware interface structure, or NULL on a failure + * Returns zero on success or an error code. */ -static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, - const struct ide_port_info *d, - unsigned int port, int irq, - hw_regs_t *hw) +static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d, + unsigned int port, int irq, hw_regs_t *hw) { unsigned long ctl = 0, base = 0; - ide_hwif_t *hwif; if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { if (ide_pci_check_iomem(dev, d, 2 * port) || ide_pci_check_iomem(dev, d, 2 * port + 1)) { printk(KERN_ERR "%s: I/O baseregs (BIOS) are reported " "as MEM for port %d!\n", d->name, port); - return NULL; + return -EINVAL; } ctl = pci_resource_start(dev, 2*port+1); @@ -330,7 +327,7 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, if (!base || !ctl) { printk(KERN_ERR "%s: bad PCI BARs for port %d, skipping\n", d->name, port); - return NULL; + return -EINVAL; } memset(hw, 0, sizeof(*hw)); @@ -339,13 +336,7 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, hw->chipset = d->chipset ? d->chipset : ide_pci; ide_std_init_ports(hw, base, ctl | 2); - hwif = ide_find_port_slot(d); - if (hwif == NULL) - return NULL; - - hwif->chipset = hw->chipset; - - return hwif; + return 0; } #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -443,7 +434,6 @@ out: * @dev: PCI device * @d: IDE port info * @pciirq: IRQ line - * @idx: ATA index table to update * @hw: hw_regs_t instances corresponding to this PCI IDE device * @hws: hw_regs_t pointers table to update * @@ -457,10 +447,9 @@ out: */ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, - int pciirq, u8 *idx, hw_regs_t *hw, hw_regs_t **hws) + int pciirq, hw_regs_t *hw, hw_regs_t **hws) { int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port; - ide_hwif_t *hwif; u8 tmp; /* @@ -476,12 +465,10 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, continue; /* port not enabled */ } - hwif = ide_hwif_configure(dev, d, port, pciirq, hw + port); - if (hwif == NULL) + if (ide_hw_configure(dev, d, port, pciirq, hw + port)) continue; *(hws + port) = hw + port; - *(idx + port) = hwif->index; } } EXPORT_SYMBOL_GPL(ide_pci_setup_ports); @@ -554,7 +541,7 @@ out: int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + struct ide_host *host; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; @@ -562,9 +549,11 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) if (ret >= 0) { /* FIXME: silent failure can happen */ - ide_pci_setup_ports(dev, d, ret, &idx[0], &hw[0], &hws[0]); + ide_pci_setup_ports(dev, d, ret, &hw[0], &hws[0]); - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host) + ide_host_register(host, d, hws); } return ret; @@ -575,9 +564,9 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, const struct ide_port_info *d) { struct pci_dev *pdev[] = { dev1, dev2 }; + struct ide_host *host; int ret, i; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; for (i = 0; i < 2; i++) { ret = do_ide_setup_pci_device(pdev[i], d, !i); @@ -590,11 +579,12 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, goto out; /* FIXME: silent failure can happen */ - ide_pci_setup_ports(pdev[i], d, ret, &idx[i*2], &hw[i*2], - &hws[i*2]); + ide_pci_setup_ports(pdev[i], d, ret, &hw[i*2], &hws[i*2]); } - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host) + ide_host_register(host, d, hws); out: return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 1286a2275efb..a41ae57fafc5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -558,6 +558,11 @@ typedef struct hwif_s { #endif } ____cacheline_internodealigned_in_smp ide_hwif_t; +struct ide_host { + ide_hwif_t *ports[MAX_HWIFS]; + unsigned int n_ports; +}; + /* * internal ide interrupt handler type */ @@ -813,13 +818,6 @@ int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsig extern int ide_vlb_clk; extern int ide_pci_clk; -ide_hwif_t *ide_find_port_slot(const struct ide_port_info *); - -static inline ide_hwif_t *ide_find_port(void) -{ - return ide_find_port_slot(NULL); -} - extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs); int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq, int uptodate, int nr_sectors); @@ -1024,7 +1022,7 @@ extern int __ide_pci_register_driver(struct pci_driver *driver, struct module *o #endif void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int, - u8 *, hw_regs_t *, hw_regs_t **); + hw_regs_t *, hw_regs_t **); void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -1236,8 +1234,11 @@ void ide_undecoded_slave(ide_drive_t *); void ide_port_apply_params(ide_hwif_t *); -int ide_device_add_all(u8 *, const struct ide_port_info *, hw_regs_t **); -int ide_device_add(u8 *, const struct ide_port_info *, hw_regs_t **); +struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); +struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); +int ide_host_register(struct ide_host *, const struct ide_port_info *, + hw_regs_t **); +void ide_host_remove(struct ide_host *); int ide_legacy_device_add(const struct ide_port_info *, unsigned long); void ide_port_unregister_devices(ide_hwif_t *); void ide_port_scan(ide_hwif_t *); -- cgit v1.2.3 From 6f904d015262dfa43eb1cecc00b0998b4c3543f2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:57 +0200 Subject: ide: add ide_host_add() helper Add ide_host_add() helper which does ide_host_alloc()+ide_host_register(), then convert ide_setup_pci_device[s](), ide_legacy_device_add() and some host drivers to use it. While at it: * Fix ide_setup_pci_device[s](), ide_arm.c, gayle.c, ide-4drives.c, macide.c, q40ide.c, cmd640.c and cs5520.c to return correct error value. * -ENOENT -> -ENOMEM in rapide.c, ide-h8300.c, ide-generic.c, au1xxx-ide.c and pmac.c * -ENODEV -> -ENOMEM in palm_bk3710.c, ide_platform.c and delkin_cb.c * -1 -> -ENOMEM in ide-pnp.c Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/ide_arm.c | 7 +------ drivers/ide/arm/palm_bk3710.c | 10 ++++------ drivers/ide/arm/rapide.c | 8 ++------ drivers/ide/h8300/ide-h8300.c | 9 +-------- drivers/ide/ide-generic.c | 11 ++++------- drivers/ide/ide-pnp.c | 16 ++++++++-------- drivers/ide/ide-probe.c | 27 +++++++++++++++++++-------- drivers/ide/legacy/buddha.c | 5 +---- drivers/ide/legacy/gayle.c | 7 +------ drivers/ide/legacy/ide-4drives.c | 7 +------ drivers/ide/legacy/ide-cs.c | 8 +++----- drivers/ide/legacy/ide_platform.c | 8 ++------ drivers/ide/legacy/macide.c | 7 +------ drivers/ide/legacy/q40ide.c | 7 +------ drivers/ide/mips/au1xxx-ide.c | 8 ++------ drivers/ide/mips/swarm.c | 10 ++++------ drivers/ide/pci/cmd640.c | 7 +------ drivers/ide/pci/cs5520.c | 7 +------ drivers/ide/pci/delkin_cb.c | 8 +++----- drivers/ide/pci/scc_pata.c | 10 ++++------ drivers/ide/ppc/pmac.c | 9 ++++----- drivers/ide/setup-pci.c | 10 ++-------- include/linux/ide.h | 2 ++ 23 files changed, 72 insertions(+), 136 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index 9efd7a86db45..176532ffae0e 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -28,7 +28,6 @@ static int __init ide_arm_init(void) { - struct ide_host *host; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -50,11 +49,7 @@ static int __init ide_arm_init(void) hw.irq = IDE_ARM_IRQ; hw.chipset = ide_generic; - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(ide_arm_init); diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 24389a571c37..65bb4b8fd570 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -349,7 +349,7 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) struct resource *mem, *irq; struct ide_host *host; unsigned long base, rate; - int i; + int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; clk = clk_get(NULL, "IDECLK"); @@ -392,16 +392,14 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) hw.irq = irq->start; hw.chipset = ide_palm3710; - host = ide_host_alloc(&palm_bk3710_port_info, hws); - if (host == NULL) + rc = ide_host_add(&palm_bk3710_port_info, hws, NULL); + if (rc) goto out; - ide_host_register(host, &palm_bk3710_port_info, hws); - return 0; out: printk(KERN_WARNING "Palm Chip BK3710 IDE Register Fail\n"); - return -ENODEV; + return rc; } /* work with hotplug and coldplug */ diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 11f3307385de..2bdd8b734afb 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -52,13 +52,9 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) hw.chipset = ide_generic; hw.dev = &ec->dev; - host = ide_host_alloc(&rapide_port_info, hws); - if (host == NULL) { - ret = -ENOENT; + ret = ide_host_add(&rapide_port_info, hws, &host); + if (ret) goto release; - } - - ide_host_register(host, &rapide_port_info, hws); ecard_set_drvdata(ec, host); goto out; diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 15f76690a48c..bde7a585f198 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -191,7 +191,6 @@ static const struct ide_port_info h8300_port_info = { static int __init h8300_ide_init(void) { - struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -205,13 +204,7 @@ static int __init h8300_ide_init(void) hw_setup(&hw); - host = ide_host_alloc(&h8300_port_info, hws); - if (host == NULL) - return -ENOENT; - - ide_host_register(host, &h8300_port_info, hws); - - return 0; + return ide_host_add(&h8300_port_info, hws, NULL); out_busy: printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index e8818362eb46..a7082c28d06f 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -28,9 +28,8 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports"); static ssize_t store_add(struct class *cls, const char *buf, size_t n) { - struct ide_host *host; unsigned int base, ctl; - int irq; + int irq, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) @@ -41,11 +40,9 @@ static ssize_t store_add(struct class *cls, const char *buf, size_t n) hw.irq = irq; hw.chipset = ide_generic; - host = ide_host_alloc(NULL, hws); - if (host == NULL) - return -ENOENT; - - ide_host_register(host, NULL, hws); + rc = ide_host_add(NULL, hws, NULL); + if (rc) + return rc; return n; }; diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 4458ca61897a..bac9b392b689 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -31,6 +31,7 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct ide_host *host; unsigned long base, ctl; + int rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n"); @@ -59,19 +60,18 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) hw.irq = pnp_irq(dev, 0); hw.chipset = ide_generic; - host = ide_host_alloc(NULL, hws); - if (host) { - pnp_set_drvdata(dev, host); + rc = ide_host_add(NULL, hws, &host); + if (rc) + goto out; - ide_host_register(host, NULL, hws); - - return 0; - } + pnp_set_drvdata(dev, host); + return 0; +out: release_region(ctl, 1); release_region(base, 8); - return -1; + return rc; } static void idepnp_remove(struct pnp_dev *dev) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 84a89561ec0f..17a104b95d54 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1677,6 +1677,24 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, } EXPORT_SYMBOL_GPL(ide_host_register); +int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, + struct ide_host **hostp) +{ + struct ide_host *host; + + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; + + ide_host_register(host, d, hws); + + if (hostp) + *hostp = host; + + return 0; +} +EXPORT_SYMBOL_GPL(ide_host_add); + void ide_host_remove(struct ide_host *host) { int i; @@ -1749,7 +1767,6 @@ static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw, int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { - struct ide_host *host; hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); @@ -1762,12 +1779,6 @@ int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - host = ide_host_alloc(d, hws); - if (host == NULL) - return -ENOMEM; - - ide_host_register(host, d, hws); - - return 0; + return ide_host_add(d, hws, NULL); } EXPORT_SYMBOL_GPL(ide_legacy_device_add); diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 2625667fab4c..7c2afa97f417 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -151,7 +151,6 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { struct zorro_dev *z = NULL; - struct ide_host *host; u_long buddha_board = 0; BuddhaType type; int buddha_num_hwifs, i; @@ -226,9 +225,7 @@ fail_base2: hws[i] = &hw[i]; } - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); + ide_host_add(NULL, hws, NULL); } return 0; diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 13d22bded6b4..dd5c467d8dd0 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -127,7 +127,6 @@ static int __init gayle_init(void) unsigned long phys_base, res_start, res_n; unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; - struct ide_host *host; int a4000, i; hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; @@ -180,11 +179,7 @@ found: hws[i] = &hw[i]; } - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(gayle_init); diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 5935153ef2ad..c76d55de6996 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -28,7 +28,6 @@ static const struct ide_port_info ide_4drives_port_info = { static int __init ide_4drives_init(void) { - struct ide_host *host; unsigned long base = 0x1f0, ctl = 0x3f6; hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL }; @@ -54,11 +53,7 @@ static int __init ide_4drives_init(void) hw.irq = 14; hw.chipset = ide_4drives; - host = ide_host_alloc(&ide_4drives_port_info, hws); - if (host) - ide_host_register(host, &ide_4drives_port_info, hws); - - return 0; + return ide_host_add(&ide_4drives_port_info, hws, NULL); } module_init(ide_4drives_init); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 1a4b9e6887fa..21bfac137844 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -162,7 +162,7 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, { struct ide_host *host; ide_hwif_t *hwif; - int i; + int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!request_region(io, 8, DRV_NAME)) { @@ -184,12 +184,10 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, hw.chipset = ide_pci; hw.dev = &handle->dev; - host = ide_host_alloc(&idecs_port_info, hws); - if (host == NULL) + rc = ide_host_add(&idecs_port_info, hws, &host); + if (rc) goto out_release; - ide_host_register(host, &idecs_port_info, hws); - hwif = host->ports[0]; if (hwif->present) diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 58a942c6a131..051b4ab0f359 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -99,13 +99,9 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - host = ide_host_alloc(&d, hws); - if (host == NULL) { - ret = -ENODEV; + ret = ide_host_add(&d, hws, &host); + if (ret) goto out; - } - - ide_host_register(host, &d, hws); platform_set_drvdata(pdev, host); diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index b49cf8c2b91a..a0bb167980e7 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -92,7 +92,6 @@ static const char *mac_ide_name[] = static int __init macide_init(void) { ide_ack_intr_t *ack_intr; - struct ide_host *host; unsigned long base; int irq; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -125,11 +124,7 @@ static int __init macide_init(void) macide_setup_ports(&hw, base, irq, ack_intr); - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(macide_init); diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 8fb4438a6afd..4abd8fc78197 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -131,7 +131,6 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ static int __init q40ide_init(void) { - struct ide_host *host; int i; hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; @@ -160,11 +159,7 @@ static int __init q40ide_init(void) hws[i] = &hw[i]; } - host = ide_host_alloc(&q40ide_port_info, hws); - if (host) - ide_host_register(host, &q40ide_port_info, hws); - - return 0; + return ide_host_add(&q40ide_port_info, hws, NULL); } module_init(q40ide_init); diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 903c628bddd0..11b7f61aae40 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -609,13 +609,9 @@ static int au_ide_probe(struct device *dev) hw.dev = dev; hw.chipset = ide_au1xxx; - host = ide_host_alloc(&au1xxx_port_info, hws); - if (host == NULL) { - ret = -ENOENT; + ret = ide_host_add(&au1xxx_port_info, hws, &host); + if (ret) goto out; - } - - ide_host_register(host, &au1xxx_port_info, hws); auide_hwif.hwif = host->ports[0]; diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index b12d9d224831..badf79fc9e3a 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -75,7 +75,7 @@ static int __devinit swarm_ide_probe(struct device *dev) u8 __iomem *base; struct ide_host *host; phys_t offset, size; - int i; + int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!SIBYTE_HAVE_IDE) @@ -115,19 +115,17 @@ static int __devinit swarm_ide_probe(struct device *dev) hw.irq = K_INT_GB_IDE; hw.chipset = ide_generic; - host = ide_host_alloc(&swarm_port_info, hws); - if (host == NULL) + rc = ide_host_add(&swarm_port_info, hws, &host); + if (rc) goto err; - ide_host_register(host, &swarm_port_info, hws); - dev_set_drvdata(dev, host); return 0; err: release_resource(&swarm_ide_resource); iounmap(base); - return -ENOMEM; + return rc; } static struct device_driver swarm_ide_driver = { diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 013697b8cef4..e6c62006ca1a 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -709,7 +709,6 @@ static int cmd640x_init_one(unsigned long base, unsigned long ctl) */ static int __init cmd640x_init(void) { - struct ide_host *host; int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; @@ -829,11 +828,7 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - host = ide_host_alloc(&cmd640_port_info, hws); - if (host) - ide_host_register(host, &cmd640_port_info, hws); - - return 1; + return ide_host_add(&cmd640_port_info, hws, NULL); } module_param_named(probe_vlb, cmd640_vlb, bool, 0); diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index b8ec06d22c61..b03d8ae947e6 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -114,7 +114,6 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - struct ide_host *host; const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; @@ -140,11 +139,7 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic ide_pci_setup_ports(dev, d, 14, &hw[0], &hws[0]); - host = ide_host_alloc(d, hws); - if (host) - ide_host_register(host, d, hws); - - return 0; + return ide_host_add(d, hws, NULL); } static const struct pci_device_id cs5520_pci_tbl[] = { diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 5eb9d9325184..f84bfb4f600f 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -86,12 +86,10 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) hw.dev = &dev->dev; hw.chipset = ide_pci; /* this enables IRQ sharing */ - host = ide_host_alloc(&delkin_cb_port_info, hws); - if (host == NULL) + rc = ide_host_add(&delkin_cb_port_info, hws, &host); + if (rc) goto out_disable; - ide_host_register(host, &delkin_cb_port_info, hws); - pci_set_drvdata(dev, host); return 0; @@ -99,7 +97,7 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) out_disable: pci_release_regions(dev); pci_disable_device(dev); - return -ENODEV; + return rc; } static void diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index d5e2ba6bacd6..94a7ab864236 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -588,7 +588,7 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, struct scc_ports *ports = pci_get_drvdata(dev); struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - int i; + int i, rc; memset(&hw, 0, sizeof(hw)); for (i = 0; i <= 8; i++) @@ -597,11 +597,9 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.dev = &dev->dev; hw.chipset = ide_pci; - host = ide_host_alloc(d, hws); - if (host == NULL) - return -ENOMEM; - - ide_host_register(host, d, hws); + rc = ide_host_add(d, hws, &host); + if (rc) + return rc; ports->host = host; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ecd2f28da1ba..c521bf6e1bf2 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1043,6 +1043,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) ide_hwif_t *hwif; hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; struct ide_port_info d = pmac_port_info; + int rc; pmif->broken_dma = pmif->broken_dma_warn = 0; if (of_device_is_compatible(np, "shasta-ata")) { @@ -1118,11 +1119,9 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id, pmif->mediabay ? " (mediabay)" : "", hw->irq); - host = ide_host_alloc(&d, hws); - if (host == NULL) - return -ENOENT; - - ide_host_register(host, &d, hws); + rc = ide_host_add(&d, hws, &host); + if (rc) + return rc; hwif = host->ports[0]; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 1c0c5570dec8..b15cad58dc81 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -541,7 +541,6 @@ out: int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { - struct ide_host *host; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; @@ -551,9 +550,7 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) /* FIXME: silent failure can happen */ ide_pci_setup_ports(dev, d, ret, &hw[0], &hws[0]); - host = ide_host_alloc(d, hws); - if (host) - ide_host_register(host, d, hws); + ret = ide_host_add(d, hws, NULL); } return ret; @@ -564,7 +561,6 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, const struct ide_port_info *d) { struct pci_dev *pdev[] = { dev1, dev2 }; - struct ide_host *host; int ret, i; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; @@ -582,9 +578,7 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, ide_pci_setup_ports(pdev[i], d, ret, &hw[i*2], &hws[i*2]); } - host = ide_host_alloc(d, hws); - if (host) - ide_host_register(host, d, hws); + ret = ide_host_add(d, hws, NULL); out: return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index a41ae57fafc5..764afd94b917 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1238,6 +1238,8 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); int ide_host_register(struct ide_host *, const struct ide_port_info *, hw_regs_t **); +int ide_host_add(const struct ide_port_info *, hw_regs_t **, + struct ide_host **); void ide_host_remove(struct ide_host *); int ide_legacy_device_add(const struct ide_port_info *, unsigned long); void ide_port_unregister_devices(ide_hwif_t *); -- cgit v1.2.3 From 8a69580e1ea9516caada5eed202afd39546e9809 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:59 +0200 Subject: ide: add ide_host_free() helper (take 2) * Add ide_host_free() helper and convert ide_host_remove() to use it. * Fix handling of ide_host_register() failure in ide_host_add(), icside.c, ide-generic.c, falconide.c and sgiioc4.c. While at it: * Fix handling of ide_host_alloc_all() failure in ide-generic.c. * Fix handling of ide_host_alloc() failure in falconide.c (also return the correct error value if no device is found). v2: * falconide build fix. (From Stephen Rothwell) Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 21 +++++++++++++++++---- drivers/ide/ide-generic.c | 27 +++++++++++++++++++++++---- drivers/ide/ide-probe.c | 23 ++++++++++++++++++++--- drivers/ide/legacy/falconide.c | 22 +++++++++++++++++----- drivers/ide/pci/sgiioc4.c | 14 ++++++++++---- include/linux/ide.h | 1 + 6 files changed, 88 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 6fa58425466a..f575e8341aec 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -445,6 +445,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) void __iomem *base; struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int ret; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -472,9 +473,15 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) ecard_set_drvdata(ec, state); - ide_host_register(host, NULL, hws); + ret = ide_host_register(host, NULL, hws); + if (ret) + goto err_free; return 0; +err_free: + ide_host_free(host); + ecard_set_drvdata(ec, NULL); + return ret; } static const struct ide_port_info icside_v6_port_info __initdata = { @@ -547,11 +554,17 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_host_register(host, &d, hws); + ret = ide_host_register(host, NULL, hws); + if (ret) + goto err_free; return 0; - - out: +err_free: + ide_host_free(host); + if (d.dma_ops) + free_dma(ec->dma); + ecard_set_drvdata(ec, NULL); +out: return ret; } diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index a7082c28d06f..31d98fec775f 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -84,13 +84,14 @@ static int __init ide_generic_init(void) { hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; struct ide_host *host; - int i; + unsigned long io_addr; + int i, rc; printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module " "parameter for probing all legacy ISA IDE ports\n"); for (i = 0; i < MAX_HWIFS; i++) { - unsigned long io_addr = ide_default_io_base(i); + io_addr = ide_default_io_base(i); hws[i] = NULL; @@ -120,14 +121,32 @@ static int __init ide_generic_init(void) } host = ide_host_alloc_all(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); + if (host == NULL) { + rc = -ENOMEM; + goto err; + } + + rc = ide_host_register(host, NULL, hws); + if (rc) + goto err_free; if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " "class\n"); return 0; +err_free: + ide_host_free(host); +err: + for (i = 0; i < MAX_HWIFS; i++) { + if (hws[i] == NULL) + continue; + + io_addr = hws[i]->io_ports.data_addr; + release_region(io_addr + 0x206, 1); + release_region(io_addr, 8); + } + return rc; } module_init(ide_generic_init); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 6d57b7cd5424..0ead4537fc63 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1729,12 +1729,17 @@ int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, struct ide_host **hostp) { struct ide_host *host; + int rc; host = ide_host_alloc(d, hws); if (host == NULL) return -ENOMEM; - ide_host_register(host, d, hws); + rc = ide_host_register(host, d, hws); + if (rc) { + ide_host_free(host); + return rc; + } if (hostp) *hostp = host; @@ -1743,7 +1748,7 @@ int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, } EXPORT_SYMBOL_GPL(ide_host_add); -void ide_host_remove(struct ide_host *host) +void ide_host_free(struct ide_host *host) { ide_hwif_t *hwif; int i; @@ -1754,13 +1759,25 @@ void ide_host_remove(struct ide_host *host) if (hwif == NULL) continue; - ide_unregister(hwif); ide_free_port_slot(hwif->index); kfree(hwif); } kfree(host); } +EXPORT_SYMBOL_GPL(ide_host_free); + +void ide_host_remove(struct ide_host *host) +{ + int i; + + for (i = 0; i < MAX_HWIFS; i++) { + if (host->ports[i]) + ide_unregister(host->ports[i]); + } + + ide_host_free(host); +} EXPORT_SYMBOL_GPL(ide_host_remove); void ide_port_scan(ide_hwif_t *hwif) diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 4eb5c3f9fecc..724f95073d80 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -114,9 +114,10 @@ static int __init falconide_init(void) { struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int rc; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) - return 0; + return -ENODEV; printk(KERN_INFO "ide: Falcon IDE controller\n"); @@ -128,13 +129,24 @@ static int __init falconide_init(void) falconide_setup_ports(&hw); host = ide_host_alloc(&falconide_port_info, hws); - if (host) { - ide_get_lock(NULL, NULL); - ide_host_register(host, &falconide_port_info, hws); - ide_release_lock(); + if (host == NULL) { + rc = -ENOMEM; + goto err; } + ide_get_lock(NULL, NULL); + rc = ide_host_register(host, &falconide_port_info, hws); + ide_release_lock(); + + if (rc) + goto err_free; + return 0; +err_free: + ide_host_free(host); +err: + release_mem_region(ATA_HD_BASE, 0x40); + return rc; } module_init(falconide_init); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 440f43a86ad3..42eef19a18f1 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -603,6 +603,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; struct ide_port_info d = sgiioc4_port_info; + int rc; /* Get the CmdBlk and CtrlBlk Base Registers */ bar0 = pci_resource_start(dev, 0); @@ -638,17 +639,22 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); host = ide_host_alloc(&d, hws); - if (host == NULL) + if (host == NULL) { + rc = -ENOMEM; goto err; + } - if (ide_host_register(host, &d, hws)) - return -EIO; + rc = ide_host_register(host, &d, hws); + if (rc) + goto err_free; return 0; +err_free: + ide_host_free(host); err: release_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE); iounmap(virt_base); - return -ENOMEM; + return rc; } static unsigned int __devinit diff --git a/include/linux/ide.h b/include/linux/ide.h index 764afd94b917..46d5bfe2fefb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1236,6 +1236,7 @@ void ide_port_apply_params(ide_hwif_t *); struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); +void ide_host_free(struct ide_host *); int ide_host_register(struct ide_host *, const struct ide_port_info *, hw_regs_t **); int ide_host_add(const struct ide_port_info *, hw_regs_t **, -- cgit v1.2.3 From d7c26ebb5bca284ece9db7311a9e180aabae1922 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:55:59 +0200 Subject: ide: push pc callback pointer into the ide_drive_t structure Refrain from carrying the callback ptr with every packet command since the callback function is only one anyways. ide_drive_t is probably not the most suitable place for it right now but is the more sane solution. Besides, these structs are going to be reorganized anyways during the generic ide rewrite. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 46d5bfe2fefb..a28823297edc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -308,7 +308,7 @@ struct ide_acpi_drive_link; struct ide_acpi_hwif_link; #endif -typedef struct ide_drive_s { +struct ide_drive_s { char name[4]; /* drive name, such as "hda" */ char driver_req[10]; /* requests specific driver */ @@ -400,7 +400,12 @@ typedef struct ide_drive_s { struct list_head list; struct device gendev; struct completion gendev_rel_comp; /* to deal with device release() */ -} ide_drive_t; + + /* callback for packet commands */ + void (*pc_callback)(struct ide_drive_s *); +}; + +typedef struct ide_drive_s ide_drive_t; #define to_ide_device(dev)container_of(dev, ide_drive_t, gendev) -- cgit v1.2.3 From 8bcda3bc492c56abed33a8cbf824c410d7ce69f0 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:00 +0200 Subject: ide: remove pc->callback member from ide_atapi_pc There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index a28823297edc..d8ec8afb4ef3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -662,8 +662,6 @@ struct ide_atapi_pc { */ u8 pc_buf[256]; - void (*callback)(ide_drive_t *); - /* idetape only */ struct idetape_bh *bh; char *b_data; -- cgit v1.2.3 From 3b8ac5398ca8137b41aa5da4d9fad89153450aaa Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:01 +0200 Subject: ide: add per-device flags Push device flags up into ide_drive_t. There should be no functionality change resulting from this patch. [bart: IDE_FLAG_* -> IDE_AFLAG_*, dev_flags -> atapi_flags] Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index d8ec8afb4ef3..fcf2d1bede08 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -308,6 +308,64 @@ struct ide_acpi_drive_link; struct ide_acpi_hwif_link; #endif +/* ATAPI device flags */ +enum { + IDE_AFLAG_DRQ_INTERRUPT = (1 << 0), + IDE_AFLAG_MEDIA_CHANGED = (1 << 1), + + /* ide-cd */ + /* Drive cannot lock the door. */ + IDE_AFLAG_NO_DOORLOCK = (1 << 2), + /* Drive cannot eject the disc. */ + IDE_AFLAG_NO_EJECT = (1 << 3), + /* Drive is a pre ATAPI 1.2 drive. */ + IDE_AFLAG_PRE_ATAPI12 = (1 << 4), + /* TOC addresses are in BCD. */ + IDE_AFLAG_TOCADDR_AS_BCD = (1 << 5), + /* TOC track numbers are in BCD. */ + IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 6), + /* + * Drive does not provide data in multiples of SECTOR_SIZE + * when more than one interrupt is needed. + */ + IDE_AFLAG_LIMIT_NFRAMES = (1 << 7), + /* Seeking in progress. */ + IDE_AFLAG_SEEKING = (1 << 8), + /* Saved TOC information is current. */ + IDE_AFLAG_TOC_VALID = (1 << 9), + /* We think that the drive door is locked. */ + IDE_AFLAG_DOOR_LOCKED = (1 << 10), + /* SET_CD_SPEED command is unsupported. */ + IDE_AFLAG_NO_SPEED_SELECT = (1 << 11), + IDE_AFLAG_VERTOS_300_SSD = (1 << 12), + IDE_AFLAG_VERTOS_600_ESD = (1 << 13), + IDE_AFLAG_SANYO_3CD = (1 << 14), + IDE_AFLAG_FULL_CAPS_PAGE = (1 << 15), + IDE_AFLAG_PLAY_AUDIO_OK = (1 << 16), + IDE_AFLAG_LE_SPEED_FIELDS = (1 << 17), + + /* ide-floppy */ + /* Format in progress */ + IDE_AFLAG_FORMAT_IN_PROGRESS = (1 << 18), + /* Avoid commands not supported in Clik drive */ + IDE_AFLAG_CLIK_DRIVE = (1 << 19), + /* Requires BH algorithm for packets */ + IDE_AFLAG_ZIP_DRIVE = (1 << 20), + + /* ide-tape */ + IDE_AFLAG_IGNORE_DSC = (1 << 21), + /* 0 When the tape position is unknown */ + IDE_AFLAG_ADDRESS_VALID = (1 << 22), + /* Device already opened */ + IDE_AFLAG_BUSY = (1 << 23), + /* Attempt to auto-detect the current user block size */ + IDE_AFLAG_DETECT_BS = (1 << 24), + /* Currently on a filemark */ + IDE_AFLAG_FILEMARK = (1 << 25), + /* 0 = no tape is loaded, so we don't rewind after ejecting */ + IDE_AFLAG_MEDIUM_PRESENT = (1 << 26) +}; + struct ide_drive_s { char name[4]; /* drive name, such as "hda" */ char driver_req[10]; /* requests specific driver */ @@ -403,6 +461,8 @@ struct ide_drive_s { /* callback for packet commands */ void (*pc_callback)(struct ide_drive_s *); + + unsigned long atapi_flags; }; typedef struct ide_drive_s ide_drive_t; -- cgit v1.2.3 From ea68d270ff55bcdfa5d07697eb68103b5b02c7bb Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:01 +0200 Subject: ide-floppy: convert to using the new atapi_flags (take 2) while at it, remove PC_FLAG_ZIP_DRIVE from the packed command flags altogether and query the drive type through drive->atapi_flags. v2: ide-floppy fix. There should be no functionality change resulting from this patch. [bart: IDE_FLAG_* -> IDE_AFLAG_*, dev_flags -> atapi_flags] Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 5 ++-- drivers/ide/ide-floppy.c | 73 ++++++++++++++++++------------------------------ include/linux/ide.h | 3 +- 3 files changed, 31 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f848010c15a5..711a5f6d35e4 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -257,7 +257,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Send the actual packet */ - if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) + if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) hwif->tp_ops->output_data(drive, NULL, rq->cmd, 12); return ide_started; @@ -302,7 +302,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, bcount, dma); /* Issue the packet command */ - if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { + if ((pc->flags & PC_FLAG_DRQ_INTERRUPT) || + (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT)) { ide_execute_command(drive, WIN_PACKETCMD, handler, timeout, NULL); return ide_started; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index cbf84f2e9cce..3d8e6dd0f41e 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -125,26 +125,10 @@ typedef struct ide_floppy_obj { int wp; /* Supports format progress report */ int srfp; - /* Status/Action flags */ - unsigned long flags; } idefloppy_floppy_t; #define IDEFLOPPY_TICKS_DELAY HZ/20 /* default delay for ZIP 100 (50ms) */ -/* Floppy flag bits values. */ -enum { - /* DRQ interrupt device */ - IDEFLOPPY_FLAG_DRQ_INTERRUPT = (1 << 0), - /* Media may have changed */ - IDEFLOPPY_FLAG_MEDIA_CHANGED = (1 << 1), - /* Format in progress */ - IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS = (1 << 2), - /* Avoid commands not supported in Clik drive */ - IDEFLOPPY_FLAG_CLIK_DRIVE = (1 << 3), - /* Requires BH algorithm for packets */ - IDEFLOPPY_FLAG_ZIP_DRIVE = (1 << 4), -}; - /* Defines for the MODE SENSE command */ #define MODE_SENSE_CURRENT 0x00 #define MODE_SENSE_CHANGEABLE 0x01 @@ -429,7 +413,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) * 40 and 50msec work well. idefloppy_pc_intr will not be actually * used until after the packet is moved in about 50 msec. */ - if (pc->flags & PC_FLAG_ZIP_DRIVE) { + if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { timeout = floppy->ticks; expiry = &idefloppy_transfer_pc; } else { @@ -649,12 +633,6 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive, return ide_stopped; } - if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT) - pc->flags |= PC_FLAG_DRQ_INTERRUPT; - - if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) - pc->flags |= PC_FLAG_ZIP_DRIVE; - pc->rq = rq; return idefloppy_issue_pc(drive, pc); @@ -798,7 +776,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) switch (pc.buf[desc_start + 4] & 0x03) { /* Clik! drive returns this instead of CAPACITY_CURRENT */ case CAPACITY_UNFORMATTED: - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) /* * If it is not a clik drive, break out * (maintains previous driver behaviour) @@ -844,7 +822,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) } /* Clik! disk does not support get_flexible_disk_page */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) (void) ide_floppy_get_flexible_disk_page(drive); set_capacity(floppy->disk, floppy->blocks * floppy->bs_factor); @@ -1046,7 +1024,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) drive->pc_callback = ide_floppy_callback; if (((gcw[0] & 0x60) >> 5) == 1) - floppy->flags |= IDEFLOPPY_FLAG_DRQ_INTERRUPT; + drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; /* * We used to check revisions here. At this point however I'm giving up. * Just assume they are all broken, its easier. @@ -1057,7 +1035,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) * we'll leave the limitation below for the 2.2.x tree. */ if (!strncmp(drive->id->model, "IOMEGA ZIP 100 ATAPI", 20)) { - floppy->flags |= IDEFLOPPY_FLAG_ZIP_DRIVE; + drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE; /* This value will be visible in the /proc/ide/hdx/settings */ floppy->ticks = IDEFLOPPY_TICKS_DELAY; blk_queue_max_sectors(drive->queue, 64); @@ -1069,7 +1047,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) */ if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) { blk_queue_max_sectors(drive->queue, 64); - floppy->flags |= IDEFLOPPY_FLAG_CLIK_DRIVE; + drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE; } (void) ide_floppy_get_capacity(drive); @@ -1158,7 +1136,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) floppy->openers++; if (floppy->openers == 1) { - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; /* Just in case */ idefloppy_init_pc(&pc); @@ -1185,14 +1163,14 @@ static int idefloppy_open(struct inode *inode, struct file *filp) ret = -EROFS; goto out_put_floppy; } - floppy->flags |= IDEFLOPPY_FLAG_MEDIA_CHANGED; + drive->atapi_flags |= IDE_AFLAG_MEDIA_CHANGED; /* IOMEGA Clik! drives do not support lock/unlock commands */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { idefloppy_create_prevent_cmd(&pc, 1); (void) idefloppy_queue_pc_tail(drive, &pc); } check_disk_change(inode->i_bdev); - } else if (floppy->flags & IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS) { + } else if (drive->atapi_flags & IDE_AFLAG_FORMAT_IN_PROGRESS) { ret = -EBUSY; goto out_put_floppy; } @@ -1215,12 +1193,12 @@ static int idefloppy_release(struct inode *inode, struct file *filp) if (floppy->openers == 1) { /* IOMEGA Clik! drives do not support lock/unlock commands */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { idefloppy_create_prevent_cmd(&pc, 0); (void) idefloppy_queue_pc_tail(drive, &pc); } - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; } floppy->openers--; @@ -1241,15 +1219,17 @@ static int idefloppy_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } -static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, - struct ide_atapi_pc *pc, unsigned long arg, unsigned int cmd) +static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, + unsigned long arg, unsigned int cmd) { + idefloppy_floppy_t *floppy = drive->driver_data; + if (floppy->openers > 1) return -EBUSY; /* The IOMEGA Clik! Drive doesn't support this command - * no room for an eject mechanism */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { int prevent = arg ? 1 : 0; if (cmd == CDROMEJECT) @@ -1270,16 +1250,17 @@ static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, static int ide_floppy_format_unit(idefloppy_floppy_t *floppy, int __user *arg) { - int blocks, length, flags, err = 0; struct ide_atapi_pc pc; + ide_drive_t *drive = floppy->drive; + int blocks, length, flags, err = 0; if (floppy->openers > 1) { /* Don't format if someone is using the disk */ - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; return -EBUSY; } - floppy->flags |= IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags |= IDE_AFLAG_FORMAT_IN_PROGRESS; /* * Send ATAPI_FORMAT_UNIT to the drive. @@ -1303,15 +1284,15 @@ static int ide_floppy_format_unit(idefloppy_floppy_t *floppy, goto out; } - (void) idefloppy_get_sfrp_bit(floppy->drive); + (void) idefloppy_get_sfrp_bit(drive); idefloppy_create_format_unit_cmd(&pc, blocks, length, flags); - if (idefloppy_queue_pc_tail(floppy->drive, &pc)) + if (idefloppy_queue_pc_tail(drive, &pc)) err = -EIO; out: if (err) - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; return err; } @@ -1330,7 +1311,7 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, case CDROMEJECT: /* fall through */ case CDROM_LOCKDOOR: - return ide_floppy_lockdoor(floppy, &pc, arg, cmd); + return ide_floppy_lockdoor(drive, &pc, arg, cmd); case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: return 0; case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: @@ -1371,8 +1352,8 @@ static int idefloppy_media_changed(struct gendisk *disk) drive->attach = 0; return 0; } - ret = !!(floppy->flags & IDEFLOPPY_FLAG_MEDIA_CHANGED); - floppy->flags &= ~IDEFLOPPY_FLAG_MEDIA_CHANGED; + ret = !!(drive->atapi_flags & IDE_AFLAG_MEDIA_CHANGED); + drive->atapi_flags &= ~IDE_AFLAG_MEDIA_CHANGED; return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index fcf2d1bede08..147bfee1fe70 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -687,8 +687,7 @@ enum { PC_FLAG_WRITING = (1 << 6), /* command timed out */ PC_FLAG_TIMEDOUT = (1 << 7), - PC_FLAG_ZIP_DRIVE = (1 << 8), - PC_FLAG_DRQ_INTERRUPT = (1 << 9), + PC_FLAG_DRQ_INTERRUPT = (1 << 8), }; struct ide_atapi_pc { -- cgit v1.2.3 From ac77ef8b03677c8ae8afe77bccc5f6a969193a79 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:01 +0200 Subject: ide: remove unused PC_FLAG_DRQ_INTERRUPT There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 3 +-- include/linux/ide.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 711a5f6d35e4..adf04f99cdeb 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -302,8 +302,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, bcount, dma); /* Issue the packet command */ - if ((pc->flags & PC_FLAG_DRQ_INTERRUPT) || - (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT)) { + if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { ide_execute_command(drive, WIN_PACKETCMD, handler, timeout, NULL); return ide_started; diff --git a/include/linux/ide.h b/include/linux/ide.h index 147bfee1fe70..d67ccca2b964 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -687,7 +687,6 @@ enum { PC_FLAG_WRITING = (1 << 6), /* command timed out */ PC_FLAG_TIMEDOUT = (1 << 7), - PC_FLAG_DRQ_INTERRUPT = (1 << 8), }; struct ide_atapi_pc { -- cgit v1.2.3 From a822bea7962b500b0bcab41bf3500f7c40ae56b5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 6 Jun 2008 01:34:00 -0400 Subject: Input: serio - mark serio_register_driver() __must_check Also remove extra declaration of serio_register_driver(). Signed-off-by: Dmitry Torokhov --- include/linux/serio.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/serio.h b/include/linux/serio.h index e72716cca577..25641d9e0ea8 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -87,11 +87,10 @@ void serio_unregister_port(struct serio *serio); void serio_unregister_child_port(struct serio *serio); int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name); -static inline int serio_register_driver(struct serio_driver *drv) +static inline int __must_check serio_register_driver(struct serio_driver *drv) { return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME); } -int serio_register_driver(struct serio_driver *drv); void serio_unregister_driver(struct serio_driver *drv); static inline int serio_write(struct serio *serio, unsigned char data) -- cgit v1.2.3 From d8e64406a037a64444175730294e449c9e21f5ec Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 23 Jul 2008 13:09:48 -0700 Subject: md: delay notification of 'active_idle' to the recovery thread sysfs_notify might sleep, so do not call it from md_safemode_timeout. Signed-off-by: Dan Williams --- drivers/md/md.c | 5 ++++- include/linux/raid/md_k.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index c2ff77ccec50..0f1b83096425 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3483,7 +3483,7 @@ static void md_safemode_timeout(unsigned long data) if (!atomic_read(&mddev->writes_pending)) { mddev->safemode = 1; if (mddev->external) - sysfs_notify(&mddev->kobj, NULL, "array_state"); + set_bit(MD_NOTIFY_ARRAY_STATE, &mddev->flags); } md_wakeup_thread(mddev->thread); } @@ -6051,6 +6051,9 @@ void md_check_recovery(mddev_t *mddev) if (mddev->bitmap) bitmap_daemon_work(mddev->bitmap); + if (test_and_clear_bit(MD_NOTIFY_ARRAY_STATE, &mddev->flags)) + sysfs_notify(&mddev->kobj, NULL, "array_state"); + if (mddev->ro) return; diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 9f2549ac0e2d..c200b9a34aff 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -128,6 +128,7 @@ struct mddev_s #define MD_CHANGE_DEVS 0 /* Some device status has changed */ #define MD_CHANGE_CLEAN 1 /* transition to or from 'clean' */ #define MD_CHANGE_PENDING 2 /* superblock update in progress */ +#define MD_NOTIFY_ARRAY_STATE 3 /* atomic context wants to notify userspace */ int ro; -- cgit v1.2.3 From a5bf6190417cbbf80443a9f71c65b653e13e9982 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 10 Jul 2008 18:38:33 +0300 Subject: UBI: add ubi_sync() interface To flush MTD device caches. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/kapi.c | 24 ++++++++++++++++++++++++ include/linux/mtd/ubi.h | 1 + 2 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 51508832566d..e65c8e0bcd5d 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -632,3 +632,27 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) return vol->eba_tbl[lnum] >= 0; } EXPORT_SYMBOL_GPL(ubi_is_mapped); + +/** + * ubi_sync - synchronize UBI device buffers. + * @ubi_num: UBI device to synchronize + * + * The underlying MTD device may cache data in hardware or in software. This + * function ensures the caches are flushed. Returns zero in case of success and + * a negative error code in case of failure. + */ +int ubi_sync(int ubi_num) +{ + struct ubi_device *ubi; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + + if (ubi->mtd->sync) + ubi->mtd->sync(ubi->mtd); + + ubi_put_device(ubi); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_sync); diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index f71201d0f3e7..83302bbbddb4 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -152,6 +152,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum); int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum); int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype); int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); +int ubi_sync(int ubi_num); /* * This function is the same as the 'ubi_leb_read()' function, but it does not -- cgit v1.2.3 From 85c6e6e28259e9b58b8984db536c45bc3161f40c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 16 Jul 2008 10:25:56 +0300 Subject: UBI: amend commentaries Hch asked not to use "unit" for sub-systems, let it be so. Also some other commentaries modifications. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/debug.h | 6 +-- drivers/mtd/ubi/eba.c | 22 +++++------ drivers/mtd/ubi/io.c | 22 +++++------ drivers/mtd/ubi/scan.c | 28 +++++++------- drivers/mtd/ubi/scan.h | 19 +++++---- drivers/mtd/ubi/ubi-media.h | 23 +++++------ drivers/mtd/ubi/ubi.h | 37 +++++++++--------- drivers/mtd/ubi/wl.c | 94 ++++++++++++++++++++++----------------------- include/linux/mtd/ubi.h | 4 +- 10 files changed, 129 insertions(+), 128 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a5b19944eca8..27271fe32e02 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -524,7 +524,7 @@ out_si: } /** - * io_init - initialize I/O unit for a given UBI device. + * io_init - initialize I/O sub-system for a given UBI device. * @ubi: UBI device description object * * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 8ea99d8c9e1f..7d8d77c31dfe 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -76,21 +76,21 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #endif /* CONFIG_MTD_UBI_DEBUG_MSG */ #ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA -/* Messages from the eraseblock association unit */ +/* Messages from the eraseblock association sub-system */ #define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_eba(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL -/* Messages from the wear-leveling unit */ +/* Messages from the wear-leveling sub-system */ #define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_wl(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO -/* Messages from the input/output unit */ +/* Messages from the input/output sub-system */ #define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_io(fmt, ...) ({}) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 8dc488fc0cdf..613cd1e51648 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -19,20 +19,20 @@ */ /* - * The UBI Eraseblock Association (EBA) unit. + * The UBI Eraseblock Association (EBA) sub-system. * - * This unit is responsible for I/O to/from logical eraseblock. + * This sub-system is responsible for I/O to/from logical eraseblock. * * Although in this implementation the EBA table is fully kept and managed in * RAM, which assumes poor scalability, it might be (partially) maintained on * flash in future implementations. * - * The EBA unit implements per-logical eraseblock locking. Before accessing a - * logical eraseblock it is locked for reading or writing. The per-logical - * eraseblock locking is implemented by means of the lock tree. The lock tree - * is an RB-tree which refers all the currently locked logical eraseblocks. The - * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by - * (@vol_id, @lnum) pairs. + * The EBA sub-system implements per-logical eraseblock locking. Before + * accessing a logical eraseblock it is locked for reading or writing. The + * per-logical eraseblock locking is implemented by means of the lock tree. The + * lock tree is an RB-tree which refers all the currently locked logical + * eraseblocks. The lock tree elements are &struct ubi_ltree_entry objects. + * They are indexed by (@vol_id, @lnum) pairs. * * EBA also maintains the global sequence counter which is incremented each * time a logical eraseblock is mapped to a physical eraseblock and it is @@ -1128,7 +1128,7 @@ out_unlock_leb: } /** - * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * ubi_eba_init_scan - initialize the EBA sub-system using scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1143,7 +1143,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) struct ubi_scan_leb *seb; struct rb_node *rb; - dbg_eba("initialize EBA unit"); + dbg_eba("initialize EBA sub-system"); spin_lock_init(&ubi->ltree_lock); mutex_init(&ubi->alc_mutex); @@ -1209,7 +1209,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->rsvd_pebs += ubi->beb_rsvd_pebs; } - dbg_eba("EBA unit is initialized"); + dbg_eba("EBA sub-system is initialized"); return 0; out_free: diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 4ac11df7b048..561e7b2f96cb 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -20,15 +20,15 @@ */ /* - * UBI input/output unit. + * UBI input/output sub-system. * - * This unit provides a uniform way to work with all kinds of the underlying - * MTD devices. It also implements handy functions for reading and writing UBI - * headers. + * This sub-system provides a uniform way to work with all kinds of the + * underlying MTD devices. It also implements handy functions for reading and + * writing UBI headers. * * We are trying to have a paranoid mindset and not to trust to what we read - * from the flash media in order to be more secure and robust. So this unit - * validates every single header it reads from the flash media. + * from the flash media in order to be more secure and robust. So this + * sub-system validates every single header it reads from the flash media. * * Some words about how the eraseblock headers are stored. * @@ -79,11 +79,11 @@ * 512-byte chunks, we have to allocate one more buffer and copy our VID header * to offset 448 of this buffer. * - * The I/O unit does the following trick in order to avoid this extra copy. - * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header - * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the - * VID header is being written out, it shifts the VID header pointer back and - * writes the whole sub-page. + * The I/O sub-system does the following trick in order to avoid this extra + * copy. It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID + * header and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. + * When the VID header is being written out, it shifts the VID header pointer + * back and writes the whole sub-page. */ #include diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 96d410e106ab..892c2ba49777 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -19,9 +19,9 @@ */ /* - * UBI scanning unit. + * UBI scanning sub-system. * - * This unit is responsible for scanning the flash media, checking UBI + * This sub-system is responsible for scanning the flash media, checking UBI * headers and providing complete information about the UBI flash image. * * The scanning information is represented by a &struct ubi_scan_info' object. @@ -103,7 +103,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, * non-zero if an inconsistency was found and zero if not. * * Note, UBI does sanity check of everything it reads from the flash media. - * Most of the checks are done in the I/O unit. Here we check that the + * Most of the checks are done in the I/O sub-system. Here we check that the * information in the VID header is consistent to the information in other VID * headers of the same volume. */ @@ -256,8 +256,8 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, * that versions that are close to %0xFFFFFFFF are less then * versions that are close to %0. * - * The UBI WL unit guarantees that the number of pending tasks - * is not greater then %0x7FFFFFFF. So, if the difference + * The UBI WL sub-system guarantees that the number of pending + * tasks is not greater then %0x7FFFFFFF. So, if the difference * between any two versions is greater or equivalent to * %0x7FFFFFFF, there was an overflow and the logical * eraseblock with lower version is actually newer then the one @@ -645,9 +645,9 @@ void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) * * This function erases physical eraseblock 'pnum', and writes the erase * counter header to it. This function should only be used on UBI device - * initialization stages, when the EBA unit had not been yet initialized. This - * function returns zero in case of success and a negative error code in case - * of failure. + * initialization stages, when the EBA sub-system had not been yet initialized. + * This function returns zero in case of success and a negative error code in + * case of failure. */ int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si, int pnum, int ec) @@ -687,9 +687,10 @@ out_free: * @si: scanning information * * This function returns a free physical eraseblock. It is supposed to be - * called on the UBI initialization stages when the wear-leveling unit is not - * initialized yet. This function picks a physical eraseblocks from one of the - * lists, writes the EC header if it is needed, and removes it from the list. + * called on the UBI initialization stages when the wear-leveling sub-system is + * not initialized yet. This function picks a physical eraseblocks from one of + * the lists, writes the EC header if it is needed, and removes it from the + * list. * * This function returns scanning physical eraseblock information in case of * success and an error code in case of failure. @@ -764,8 +765,9 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum return err; else if (err) { /* - * FIXME: this is actually duty of the I/O unit to initialize - * this, but MTD does not provide enough information. + * FIXME: this is actually duty of the I/O sub-system to + * initialize this, but MTD does not provide enough + * information. */ si->bad_peb_count += 1; return 0; diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 966b9b682a42..4e2e3cc0becd 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -59,16 +59,16 @@ struct ubi_scan_leb { * @leb_count: number of logical eraseblocks in this volume * @vol_type: volume type * @used_ebs: number of used logical eraseblocks in this volume (only for - * static volumes) + * static volumes) * @last_data_size: amount of data in the last logical eraseblock of this - * volume (always equivalent to the usable logical eraseblock size in case of - * dynamic volumes) + * volume (always equivalent to the usable logical eraseblock + * size in case of dynamic volumes) * @data_pad: how many bytes at the end of logical eraseblocks of this volume - * are not used (due to volume alignment) + * are not used (due to volume alignment) * @compat: compatibility flags of this volume * @rb: link in the volume RB-tree * @root: root of the RB-tree containing all the eraseblock belonging to this - * volume (&struct ubi_scan_leb objects) + * volume (&struct ubi_scan_leb objects) * * One object of this type is allocated for each volume during scanning. */ @@ -92,8 +92,8 @@ struct ubi_scan_volume { * @free: list of free physical eraseblocks * @erase: list of physical eraseblocks which have to be erased * @alien: list of physical eraseblocks which should not be used by UBI (e.g., + * those belonging to "preserve"-compatible internal volumes) * @bad_peb_count: count of bad physical eraseblocks - * those belonging to "preserve"-compatible internal volumes) * @vols_found: number of volumes found during scanning * @highest_vol_id: highest volume ID * @alien_peb_count: count of physical eraseblocks in the @alien list @@ -106,8 +106,8 @@ struct ubi_scan_volume { * @ec_count: a temporary variable used when calculating @mean_ec * * This data structure contains the result of scanning and may be used by other - * UBI units to build final UBI data structures, further error-recovery and so - * on. + * UBI sub-systems to build final UBI data structures, further error-recovery + * and so on. */ struct ubi_scan_info { struct rb_root volumes; @@ -132,8 +132,7 @@ struct ubi_device; struct ubi_vid_hdr; /* - * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a - * list. + * ubi_scan_move_to_list - move a PEB from the volume tree to a list. * * @sv: volume scanning information * @seb: scanning eraseblock infprmation diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h index c3185d9fd048..26bb7af9787a 100644 --- a/drivers/mtd/ubi/ubi-media.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -98,10 +98,11 @@ enum { * Compatibility constants used by internal volumes. * * @UBI_COMPAT_DELETE: delete this internal volume before anything is written - * to the flash + * to the flash * @UBI_COMPAT_RO: attach this device in read-only mode * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its - * physical eraseblocks, don't allow the wear-leveling unit to move them + * physical eraseblocks, don't allow the wear-leveling + * sub-system to move them * @UBI_COMPAT_REJECT: reject this UBI image */ enum { @@ -123,7 +124,7 @@ enum { * struct ubi_ec_hdr - UBI erase counter header. * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) * @version: version of UBI implementation which is supposed to accept this - * UBI image + * UBI image * @padding1: reserved for future, zeroes * @ec: the erase counter * @vid_hdr_offset: where the VID header starts @@ -159,20 +160,20 @@ struct ubi_ec_hdr { * struct ubi_vid_hdr - on-flash UBI volume identifier header. * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) * @version: UBI implementation version which is supposed to accept this UBI - * image (%UBI_VERSION) + * image (%UBI_VERSION) * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) * @copy_flag: if this logical eraseblock was copied from another physical - * eraseblock (for wear-leveling reasons) + * eraseblock (for wear-leveling reasons) * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, - * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) * @vol_id: ID of this volume * @lnum: logical eraseblock number * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be - * removed, kept only for not breaking older UBI users) + * removed, kept only for not breaking older UBI users) * @data_size: how many bytes of data this logical eraseblock contains * @used_ebs: total number of used logical eraseblocks in this volume * @data_pad: how many bytes at the end of this physical eraseblock are not - * used + * used * @data_crc: CRC checksum of the data stored in this logical eraseblock * @padding1: reserved for future, zeroes * @sqnum: sequence number @@ -248,9 +249,9 @@ struct ubi_ec_hdr { * The @data_crc field contains the CRC checksum of the contents of the logical * eraseblock if this is a static volume. In case of dynamic volumes, it does * not contain the CRC checksum as a rule. The only exception is when the - * data of the physical eraseblock was moved by the wear-leveling unit, then - * the wear-leveling unit calculates the data CRC and stores it in the - * @data_crc field. And of course, the @copy_flag is %in this case. + * data of the physical eraseblock was moved by the wear-leveling sub-system, + * then the wear-leveling sub-system calculates the data CRC and stores it in + * the @data_crc field. And of course, the @copy_flag is %in this case. * * The @data_size field is used only for static volumes because UBI has to know * how many bytes of data are stored in this eraseblock. For dynamic volumes, diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 940f6b7deec3..1fc32c863b78 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -74,15 +74,15 @@ #define UBI_IO_RETRIES 3 /* - * Error codes returned by the I/O unit. + * Error codes returned by the I/O sub-system. * * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only - * 0xFF bytes + * %0xFF bytes * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a - * valid erase counter header, and the rest are %0xFF bytes + * valid erase counter header, and the rest are %0xFF bytes * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or - * CRC) + * CRC) * UBI_IO_BITFLIPS: bit-flips were detected and corrected */ enum { @@ -99,9 +99,9 @@ enum { * @ec: erase counter * @pnum: physical eraseblock number * - * This data structure is used in the WL unit. Each physical eraseblock has a - * corresponding &struct wl_entry object which may be kept in different - * RB-trees. See WL unit for details. + * This data structure is used in the WL sub-system. Each physical eraseblock + * has a corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL sub-system for details. */ struct ubi_wl_entry { struct rb_node rb; @@ -118,10 +118,10 @@ struct ubi_wl_entry { * @mutex: read/write mutex to implement read/write access serialization to * the (@vol_id, @lnum) logical eraseblock * - * This data structure is used in the EBA unit to implement per-LEB locking. - * When a logical eraseblock is being locked - corresponding + * This data structure is used in the EBA sub-system to implement per-LEB + * locking. When a logical eraseblock is being locked - corresponding * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). - * See EBA unit for details. + * See EBA sub-system for details. */ struct ubi_ltree_entry { struct rb_node rb; @@ -225,7 +225,7 @@ struct ubi_volume { #ifdef CONFIG_MTD_UBI_GLUEBI /* * Gluebi-related stuff may be compiled out. - * TODO: this should not be built into UBI but should be a separate + * Note: this should not be built into UBI but should be a separate * ubimtd driver which works on top of UBI and emulates MTD devices. */ struct ubi_volume_desc *gluebi_desc; @@ -235,8 +235,7 @@ struct ubi_volume { }; /** - * struct ubi_volume_desc - descriptor of the UBI volume returned when it is - * opened. + * struct ubi_volume_desc - UBI volume descriptor returned when it is opened. * @vol: reference to the corresponding volume description object * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) */ @@ -316,11 +315,11 @@ struct ubi_wl_entry; * @ro_mode: if the UBI device is in read-only mode * @leb_size: logical eraseblock size * @leb_start: starting offset of logical eraseblocks within physical - * eraseblocks + * eraseblocks * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size * @vid_hdr_offset: starting offset of the volume identifier header (might be - * unaligned) + * unaligned) * @vid_hdr_aloffset: starting offset of the VID header aligned to * @hdrs_min_io_size * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset @@ -356,16 +355,16 @@ struct ubi_device { struct mutex volumes_mutex; int max_ec; - /* TODO: mean_ec is not updated run-time, fix */ + /* Note, mean_ec is not updated run-time - should be fixed */ int mean_ec; - /* EBA unit's stuff */ + /* EBA sub-system's stuff */ unsigned long long global_sqnum; spinlock_t ltree_lock; struct rb_root ltree; struct mutex alc_mutex; - /* Wear-leveling unit's stuff */ + /* Wear-leveling sub-system's stuff */ struct rb_root used; struct rb_root free; struct rb_root scrub; @@ -388,7 +387,7 @@ struct ubi_device { int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; - /* I/O unit's stuff */ + /* I/O sub-system's stuff */ long long flash_size; int peb_count; int peb_size; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index cc8fe2934d2b..761952ba125b 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -19,22 +19,22 @@ */ /* - * UBI wear-leveling unit. + * UBI wear-leveling sub-system. * - * This unit is responsible for wear-leveling. It works in terms of physical - * eraseblocks and erase counters and knows nothing about logical eraseblocks, - * volumes, etc. From this unit's perspective all physical eraseblocks are of - * two types - used and free. Used physical eraseblocks are those that were - * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are - * those that were put by the 'ubi_wl_put_peb()' function. + * This sub-system is responsible for wear-leveling. It works in terms of + * physical* eraseblocks and erase counters and knows nothing about logical + * eraseblocks, volumes, etc. From this sub-system's perspective all physical + * eraseblocks are of two types - used and free. Used physical eraseblocks are + * those that were "get" by the 'ubi_wl_get_peb()' function, and free physical + * eraseblocks are those that were put by the 'ubi_wl_put_peb()' function. * * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter - * header. The rest of the physical eraseblock contains only 0xFF bytes. + * header. The rest of the physical eraseblock contains only %0xFF bytes. * - * When physical eraseblocks are returned to the WL unit by means of the + * When physical eraseblocks are returned to the WL sub-system by means of the * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is * done asynchronously in context of the per-UBI device background thread, - * which is also managed by the WL unit. + * which is also managed by the WL sub-system. * * The wear-leveling is ensured by means of moving the contents of used * physical eraseblocks with low erase counter to free physical eraseblocks @@ -43,34 +43,36 @@ * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick * an "optimal" physical eraseblock. For example, when it is known that the * physical eraseblock will be "put" soon because it contains short-term data, - * the WL unit may pick a free physical eraseblock with low erase counter, and - * so forth. + * the WL sub-system may pick a free physical eraseblock with low erase + * counter, and so forth. * - * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * If the WL sub-system fails to erase a physical eraseblock, it marks it as + * bad. * - * This unit is also responsible for scrubbing. If a bit-flip is detected in a - * physical eraseblock, it has to be moved. Technically this is the same as - * moving it for wear-leveling reasons. + * This sub-system is also responsible for scrubbing. If a bit-flip is detected + * in a physical eraseblock, it has to be moved. Technically this is the same + * as moving it for wear-leveling reasons. * - * As it was said, for the UBI unit all physical eraseblocks are either "free" - * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used - * eraseblocks are kept in a set of different RB-trees: @wl->used, + * As it was said, for the UBI sub-system all physical eraseblocks are either + * "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while + * used eraseblocks are kept in a set of different RB-trees: @wl->used, * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. * * Note, in this implementation, we keep a small in-RAM object for each physical * eraseblock. This is surely not a scalable solution. But it appears to be good * enough for moderately large flashes and it is simple. In future, one may - * re-work this unit and make it more scalable. + * re-work this sub-system and make it more scalable. * - * At the moment this unit does not utilize the sequence number, which was - * introduced relatively recently. But it would be wise to do this because the - * sequence number of a logical eraseblock characterizes how old is it. For + * At the moment this sub-system does not utilize the sequence number, which + * was introduced relatively recently. But it would be wise to do this because + * the sequence number of a logical eraseblock characterizes how old is it. For * example, when we move a PEB with low erase counter, and we need to pick the * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we * pick target PEB with an average EC if our PEB is not very "old". This is a - * room for future re-works of the WL unit. + * room for future re-works of the WL sub-system. * - * FIXME: looks too complex, should be simplified (later). + * Note: the stuff with protection trees looks too complex and is difficult to + * understand. Should be fixed. */ #include @@ -92,20 +94,21 @@ /* * Maximum difference between two erase counters. If this threshold is - * exceeded, the WL unit starts moving data from used physical eraseblocks with - * low erase counter to free physical eraseblocks with high erase counter. + * exceeded, the WL sub-system starts moving data from used physical + * eraseblocks with low erase counter to free physical eraseblocks with high + * erase counter. */ #define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD /* - * When a physical eraseblock is moved, the WL unit has to pick the target + * When a physical eraseblock is moved, the WL sub-system has to pick the target * physical eraseblock to move to. The simplest way would be just to pick the * one with the highest erase counter. But in certain workloads this could lead * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a * situation when the picked physical eraseblock is constantly erased after the * data is written to it. So, we have a constant which limits the highest erase - * counter of the free physical eraseblock to pick. Namely, the WL unit does - * not pick eraseblocks with erase counter greater then the lowest erase + * counter of the free physical eraseblock to pick. Namely, the WL sub-system + * does not pick eraseblocks with erase counter greater then the lowest erase * counter plus %WL_FREE_MAX_DIFF. */ #define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) @@ -123,11 +126,11 @@ * @abs_ec: the absolute erase counter value when the protection ends * @e: the wear-leveling entry of the physical eraseblock under protection * - * When the WL unit returns a physical eraseblock, the physical eraseblock is - * protected from being moved for some "time". For this reason, the physical - * eraseblock is not directly moved from the @wl->free tree to the @wl->used - * tree. There is one more tree in between where this physical eraseblock is - * temporarily stored (@wl->prot). + * When the WL sub-system returns a physical eraseblock, the physical + * eraseblock is protected from being moved for some "time". For this reason, + * the physical eraseblock is not directly moved from the @wl->free tree to the + * @wl->used tree. There is one more tree in between where this physical + * eraseblock is temporarily stored (@wl->prot). * * All this protection stuff is needed because: * o we don't want to move physical eraseblocks just after we have given them @@ -175,7 +178,6 @@ struct ubi_wl_prot_entry { * @list: a link in the list of pending works * @func: worker function * @priv: private data of the worker function - * * @e: physical eraseblock to erase * @torture: if the physical eraseblock has to be tortured * @@ -1136,7 +1138,7 @@ out_ro: } /** - * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * ubi_wl_put_peb - return a PEB to the wear-leveling sub-system. * @ubi: UBI device description object * @pnum: physical eraseblock to return * @torture: if this physical eraseblock has to be tortured @@ -1175,11 +1177,11 @@ retry: /* * User is putting the physical eraseblock which was selected * as the target the data is moved to. It may happen if the EBA - * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but - * the WL unit has not put the PEB to the "used" tree yet, but - * it is about to do this. So we just set a flag which will - * tell the WL worker that the PEB is not needed anymore and - * should be scheduled for erasure. + * sub-system already re-mapped the LEB in 'ubi_eba_copy_leb()' + * but the WL sub-system has not put the PEB to the "used" tree + * yet, but it is about to do this. So we just set a flag which + * will tell the WL worker that the PEB is not needed anymore + * and should be scheduled for erasure. */ dbg_wl("PEB %d is the target of data moving", pnum); ubi_assert(!ubi->move_to_put); @@ -1425,8 +1427,7 @@ static void cancel_pending(struct ubi_device *ubi) } /** - * ubi_wl_init_scan - initialize the wear-leveling unit using scanning - * information. + * ubi_wl_init_scan - initialize the WL sub-system using scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1583,13 +1584,12 @@ static void protection_trees_destroy(struct ubi_device *ubi) } /** - * ubi_wl_close - close the wear-leveling unit. + * ubi_wl_close - close the wear-leveling sub-system. * @ubi: UBI device description object */ void ubi_wl_close(struct ubi_device *ubi) { - dbg_wl("close the UBI wear-leveling unit"); - + dbg_wl("close the WL sub-system"); cancel_pending(ubi); protection_trees_destroy(ubi); tree_destroy(&ubi->used); diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index 83302bbbddb4..6316fafe5c2a 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -45,13 +45,13 @@ enum { * @size: how many physical eraseblocks are reserved for this volume * @used_bytes: how many bytes of data this volume contains * @used_ebs: how many physical eraseblocks of this volume actually contain any - * data + * data * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @corrupted: non-zero if the volume is corrupted (static volumes only) * @upd_marker: non-zero if the volume has update marker set * @alignment: volume alignment * @usable_leb_size: how many bytes are available in logical eraseblocks of - * this volume + * this volume * @name_len: volume name length * @name: volume name * @cdev: UBI volume character device major and minor numbers -- cgit v1.2.3 From b552068999b0b05087c454e525b30b785c79dc9b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 23 Apr 2008 10:07:27 -0400 Subject: Remove __DECLARE_SEMAPHORE_GENERIC There are no users of __DECLARE_SEMAPHORE_GENERIC in the kernel Signed-off-by: Matthew Wilcox --- include/linux/semaphore.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/semaphore.h b/include/linux/semaphore.h index 9cae64b00d6b..7415839ac890 100644 --- a/include/linux/semaphore.h +++ b/include/linux/semaphore.h @@ -26,10 +26,8 @@ struct semaphore { .wait_list = LIST_HEAD_INIT((name).wait_list), \ } -#define __DECLARE_SEMAPHORE_GENERIC(name, count) \ - struct semaphore name = __SEMAPHORE_INITIALIZER(name, count) - -#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name, 1) +#define DECLARE_MUTEX(name) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) static inline void sema_init(struct semaphore *sem, int val) { -- cgit v1.2.3 From e108526e77aa41c89b3be96f75d97615db2b751c Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 23 Jul 2008 21:26:44 -0700 Subject: move memory_read_from_buffer() from fs.h to string.h James Bottomley warns that inclusion of linux/fs.h in a low level driver was always a danger signal. This patch moves memory_read_from_buffer() from fs.h to string.h and fixes includes in existing memory_read_from_buffer() users. Signed-off-by: Akinobu Mita Cc: James Bottomley Cc: Geert Uytterhoeven Cc: Zhang Rui Cc: Bob Moore Cc: Thomas Renninger Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/acpi/system.c | 1 + drivers/zorro/zorro-sysfs.c | 1 - include/linux/fs.h | 2 -- include/linux/string.h | 3 +++ 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index d8e3f153b295..91dec448b3ed 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 3da712cc7708..5290552d2ef7 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "zorro.h" diff --git a/include/linux/fs.h b/include/linux/fs.h index 9c2ac5c0ef5c..ff54ae4933f3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2006,8 +2006,6 @@ extern void simple_release_fs(struct vfsmount **mount, int *count); extern ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos, const void *from, size_t available); -extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, - const void *from, size_t available); #ifdef CONFIG_MIGRATION extern int buffer_migrate_page(struct address_space *, diff --git a/include/linux/string.h b/include/linux/string.h index efdc44593b52..810d80df0a1d 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -111,5 +111,8 @@ extern void argv_free(char **argv); extern bool sysfs_streq(const char *s1, const char *s2); +extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, + const void *from, size_t available); + #endif #endif /* _LINUX_STRING_H_ */ -- cgit v1.2.3 From 8b05c7e6e159d2f33c9275281b8b909a89eb7c5d Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Wed, 23 Jul 2008 21:26:53 -0700 Subject: add a helper function to test if an object is on the stack lib/debugobjects.c has a function to test if an object is on the stack. The block layer and ide needs it (they need to avoid DMA from/to stack buffers). This patch moves the function to include/linux/sched.h so that everyone can use it. lib/debugobjects.c uses current->stack but this patch uses a task_stack_page() accessor, which is a preferable way to access the stack. Signed-off-by: FUJITA Tomonori Cc: Christoph Lameter Cc: Andy Whitcroft Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 7 +++++++ lib/debugobjects.c | 4 +--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index dc7e592c473a..6aca4a16e377 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1983,6 +1983,13 @@ static inline unsigned long *end_of_stack(struct task_struct *p) #endif +static inline int object_is_on_stack(void *obj) +{ + void *stack = task_stack_page(current); + + return (obj >= stack) && (obj < (stack + THREAD_SIZE)); +} + extern void thread_info_cache_init(void); /* set thread flags in other task's structures diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 85b18d79be89..f86196390cfd 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -226,15 +226,13 @@ debug_object_fixup(int (*fixup)(void *addr, enum debug_obj_state state), static void debug_object_is_on_stack(void *addr, int onstack) { - void *stack = current->stack; int is_on_stack; static int limit; if (limit > 4) return; - is_on_stack = (addr >= stack && addr < (stack + THREAD_SIZE)); - + is_on_stack = object_is_on_stack(addr); if (is_on_stack == onstack) return; -- cgit v1.2.3 From b61bfa3c462671c48a51fb5c31af337c5a996a04 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:26:55 -0700 Subject: mm: move bootmem descriptors definition to a single place There are a lot of places that define either a single bootmem descriptor or an array of them. Use only one central array with MAX_NUMNODES items instead. Signed-off-by: Johannes Weiner Acked-by: Ralf Baechle Cc: Ingo Molnar Cc: Richard Henderson Cc: Russell King Cc: Tony Luck Cc: Hirokazu Takata Cc: Geert Uytterhoeven Cc: Kyle McMartin Cc: Paul Mackerras Cc: Paul Mundt Cc: David S. Miller Cc: Yinghai Lu Cc: Christoph Lameter Cc: Mel Gorman Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/numa.c | 8 ++++---- arch/arm/mm/discontig.c | 34 ++++++++++++++++------------------ arch/ia64/mm/discontig.c | 11 +++++------ arch/m32r/mm/discontig.c | 4 +--- arch/m68k/mm/init.c | 4 +--- arch/mips/sgi-ip27/ip27-memory.c | 4 +--- arch/parisc/mm/init.c | 3 +-- arch/powerpc/mm/numa.c | 3 +-- arch/sh/mm/numa.c | 5 ++--- arch/sparc64/mm/init.c | 3 +-- arch/x86/mm/discontig_32.c | 3 +-- arch/x86/mm/numa_64.c | 4 +--- include/linux/bootmem.h | 2 ++ mm/bootmem.c | 2 ++ mm/page_alloc.c | 4 +--- 15 files changed, 40 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 10ab7833e83c..a53fda0481ca 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -19,7 +19,6 @@ #include pg_data_t node_data[MAX_NUMNODES]; -bootmem_data_t node_bdata[MAX_NUMNODES]; EXPORT_SYMBOL(node_data); #undef DEBUG_DISCONTIG @@ -141,7 +140,7 @@ setup_memory_node(int nid, void *kernel_end) printk(" not enough mem to reserve NODE_DATA"); return; } - NODE_DATA(nid)->bdata = &node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; printk(" Detected node memory: start %8lu, end %8lu\n", node_min_pfn, node_max_pfn); @@ -304,8 +303,9 @@ void __init paging_init(void) dma_local_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; for_each_online_node(nid) { - unsigned long start_pfn = node_bdata[nid].node_boot_start >> PAGE_SHIFT; - unsigned long end_pfn = node_bdata[nid].node_low_pfn; + bootmem_data_t *bdata = &bootmem_node_data[nid]; + unsigned long start_pfn = bdata->node_boot_start >> PAGE_SHIFT; + unsigned long end_pfn = bdata->node_low_pfn; if (dma_local_pfn >= end_pfn - start_pfn) zones_size[ZONE_DMA] = end_pfn - start_pfn; diff --git a/arch/arm/mm/discontig.c b/arch/arm/mm/discontig.c index 1e5602189507..c8c0c4b0f0a3 100644 --- a/arch/arm/mm/discontig.c +++ b/arch/arm/mm/discontig.c @@ -21,26 +21,24 @@ * Our node_data structure for discontiguous memory. */ -static bootmem_data_t node_bootmem_data[MAX_NUMNODES]; - pg_data_t discontig_node_data[MAX_NUMNODES] = { - { .bdata = &node_bootmem_data[0] }, - { .bdata = &node_bootmem_data[1] }, - { .bdata = &node_bootmem_data[2] }, - { .bdata = &node_bootmem_data[3] }, + { .bdata = &bootmem_node_data[0] }, + { .bdata = &bootmem_node_data[1] }, + { .bdata = &bootmem_node_data[2] }, + { .bdata = &bootmem_node_data[3] }, #if MAX_NUMNODES == 16 - { .bdata = &node_bootmem_data[4] }, - { .bdata = &node_bootmem_data[5] }, - { .bdata = &node_bootmem_data[6] }, - { .bdata = &node_bootmem_data[7] }, - { .bdata = &node_bootmem_data[8] }, - { .bdata = &node_bootmem_data[9] }, - { .bdata = &node_bootmem_data[10] }, - { .bdata = &node_bootmem_data[11] }, - { .bdata = &node_bootmem_data[12] }, - { .bdata = &node_bootmem_data[13] }, - { .bdata = &node_bootmem_data[14] }, - { .bdata = &node_bootmem_data[15] }, + { .bdata = &bootmem_node_data[4] }, + { .bdata = &bootmem_node_data[5] }, + { .bdata = &bootmem_node_data[6] }, + { .bdata = &bootmem_node_data[7] }, + { .bdata = &bootmem_node_data[8] }, + { .bdata = &bootmem_node_data[9] }, + { .bdata = &bootmem_node_data[10] }, + { .bdata = &bootmem_node_data[11] }, + { .bdata = &bootmem_node_data[12] }, + { .bdata = &bootmem_node_data[13] }, + { .bdata = &bootmem_node_data[14] }, + { .bdata = &bootmem_node_data[15] }, #endif }; diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 544dc420c65e..2fcf8464331e 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -36,7 +36,6 @@ struct early_node_data { struct ia64_node_data *node_data; unsigned long pernode_addr; unsigned long pernode_size; - struct bootmem_data bootmem_data; unsigned long num_physpages; #ifdef CONFIG_ZONE_DMA unsigned long num_dma_physpages; @@ -76,7 +75,7 @@ static int __init build_node_maps(unsigned long start, unsigned long len, int node) { unsigned long cstart, epfn, end = start + len; - struct bootmem_data *bdp = &mem_data[node].bootmem_data; + struct bootmem_data *bdp = &bootmem_node_data[node]; epfn = GRANULEROUNDUP(end) >> PAGE_SHIFT; cstart = GRANULEROUNDDOWN(start); @@ -167,7 +166,7 @@ static void __init fill_pernode(int node, unsigned long pernode, { void *cpu_data; int cpus = early_nr_cpus_node(node); - struct bootmem_data *bdp = &mem_data[node].bootmem_data; + struct bootmem_data *bdp = &bootmem_node_data[node]; mem_data[node].pernode_addr = pernode; mem_data[node].pernode_size = pernodesize; @@ -224,7 +223,7 @@ static int __init find_pernode_space(unsigned long start, unsigned long len, { unsigned long epfn; unsigned long pernodesize = 0, pernode, pages, mapsize; - struct bootmem_data *bdp = &mem_data[node].bootmem_data; + struct bootmem_data *bdp = &bootmem_node_data[node]; epfn = (start + len) >> PAGE_SHIFT; @@ -440,7 +439,7 @@ void __init find_memory(void) efi_memmap_walk(find_max_min_low_pfn, NULL); for_each_online_node(node) - if (mem_data[node].bootmem_data.node_low_pfn) { + if (bootmem_node_data[node].node_low_pfn) { node_clear(node, memory_less_mask); mem_data[node].min_pfn = ~0UL; } @@ -460,7 +459,7 @@ void __init find_memory(void) else if (node_isset(node, memory_less_mask)) continue; - bdp = &mem_data[node].bootmem_data; + bdp = &bootmem_node_data[node]; pernode = mem_data[node].pernode_addr; pernodesize = mem_data[node].pernode_size; map = pernode + pernodesize; diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index 07c1af7dc0e2..aa9145ef6cca 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -20,7 +20,6 @@ extern char _end[]; struct pglist_data *node_data[MAX_NUMNODES]; EXPORT_SYMBOL(node_data); -static bootmem_data_t node_bdata[MAX_NUMNODES] __initdata; pg_data_t m32r_node_data[MAX_NUMNODES]; @@ -81,7 +80,7 @@ unsigned long __init setup_memory(void) for_each_online_node(nid) { mp = &mem_prof[nid]; NODE_DATA(nid)=(pg_data_t *)&m32r_node_data[nid]; - NODE_DATA(nid)->bdata = &node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; min_pfn = mp->start_pfn; max_pfn = mp->start_pfn + mp->pages; bootmap_size = init_bootmem_node(NODE_DATA(nid), mp->free_pfn, @@ -163,4 +162,3 @@ unsigned long __init zone_sizes_init(void) return holes; } - diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index d8fb9c5303cc..79f5f94d4800 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -32,8 +32,6 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -static bootmem_data_t __initdata bootmem_data[MAX_NUMNODES]; - pg_data_t pg_data_map[MAX_NUMNODES]; EXPORT_SYMBOL(pg_data_map); @@ -58,7 +56,7 @@ void __init m68k_setup_node(int node) pg_data_table[i] = pg_data_map + node; } #endif - pg_data_map[node].bdata = bootmem_data + node; + pg_data_map[node].bdata = bootmem_node_data + node; node_set_online(node); } diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index 42cd10956306..060d853d7b35 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -33,8 +33,6 @@ #define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) #define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) -static struct bootmem_data __initdata plat_node_bdata[MAX_COMPACT_NODES]; - struct node_data *__node_data[MAX_COMPACT_NODES]; EXPORT_SYMBOL(__node_data); @@ -403,7 +401,7 @@ static void __init node_mem_init(cnodeid_t node) */ __node_data[node] = __va(slot_freepfn << PAGE_SHIFT); - NODE_DATA(node)->bdata = &plat_node_bdata[node]; + NODE_DATA(node)->bdata = &bootmem_node_data[node]; NODE_DATA(node)->node_start_pfn = start_pfn; NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index b4d6c8777ed0..0ddf4904640a 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -36,7 +36,6 @@ extern int data_start; #ifdef CONFIG_DISCONTIGMEM struct node_map_data node_data[MAX_NUMNODES] __read_mostly; -bootmem_data_t bmem_data[MAX_NUMNODES] __read_mostly; unsigned char pfnnid_map[PFNNID_MAP_MAX] __read_mostly; #endif @@ -262,7 +261,7 @@ static void __init setup_bootmem(void) #ifdef CONFIG_DISCONTIGMEM for (i = 0; i < MAX_PHYSMEM_RANGES; i++) { memset(NODE_DATA(i), 0, sizeof(pg_data_t)); - NODE_DATA(i)->bdata = &bmem_data[i]; + NODE_DATA(i)->bdata = &bootmem_node_data[i]; } memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index cf4bffba6f7c..d9a181351332 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -39,7 +39,6 @@ EXPORT_SYMBOL(numa_cpu_lookup_table); EXPORT_SYMBOL(numa_cpumask_lookup_table); EXPORT_SYMBOL(node_data); -static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; static int min_common_depth; static int n_mem_addr_cells, n_mem_size_cells; @@ -816,7 +815,7 @@ void __init do_init_bootmem(void) dbg("node %d\n", nid); dbg("NODE_DATA() = %p\n", NODE_DATA(nid)); - NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; NODE_DATA(nid)->node_start_pfn = start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; diff --git a/arch/sh/mm/numa.c b/arch/sh/mm/numa.c index 1663199ce888..095d93bec7cd 100644 --- a/arch/sh/mm/numa.c +++ b/arch/sh/mm/numa.c @@ -14,7 +14,6 @@ #include #include -static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL_GPL(node_data); @@ -35,7 +34,7 @@ void __init setup_memory(void) NODE_DATA(0) = pfn_to_kaddr(free_pfn); memset(NODE_DATA(0), 0, sizeof(struct pglist_data)); free_pfn += PFN_UP(sizeof(struct pglist_data)); - NODE_DATA(0)->bdata = &plat_node_bdata[0]; + NODE_DATA(0)->bdata = &bootmem_node_data[0]; /* Set up node 0 */ setup_bootmem_allocator(free_pfn); @@ -66,7 +65,7 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) free_pfn += PFN_UP(sizeof(struct pglist_data)); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; NODE_DATA(nid)->node_start_pfn = start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 84898c44dd4d..713297473951 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -788,7 +788,6 @@ int numa_cpu_lookup_table[NR_CPUS]; cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES]; #ifdef CONFIG_NEED_MULTIPLE_NODES -static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; struct mdesc_mblock { u64 base; @@ -871,7 +870,7 @@ static void __init allocate_node_data(int nid) NODE_DATA(nid) = __va(paddr); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; #endif p = NODE_DATA(nid); diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index 5dfef9fa061a..62fa440678d8 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -42,7 +42,6 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data); -static bootmem_data_t node0_bdata; /* * numa interface - we expect the numa architecture specific code to have @@ -385,7 +384,7 @@ void __init initmem_init(unsigned long start_pfn, for_each_online_node(nid) memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(0)->bdata = &node0_bdata; + NODE_DATA(0)->bdata = &bootmem_node_data[0]; setup_bootmem_allocator(); } diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 9782f42dd319..a4dd793d6003 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -23,8 +23,6 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data); -static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; - struct memnode memnode; s16 apicid_to_node[MAX_LOCAL_APIC] __cpuinitdata = { @@ -198,7 +196,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, nodedata_phys + pgdat_size - 1); memset(NODE_DATA(nodeid), 0, sizeof(pg_data_t)); - NODE_DATA(nodeid)->bdata = &plat_node_bdata[nodeid]; + NODE_DATA(nodeid)->bdata = &bootmem_node_data[nodeid]; NODE_DATA(nodeid)->node_start_pfn = start_pfn; NODE_DATA(nodeid)->node_spanned_pages = last_pfn - start_pfn; diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index a1d9b79078ea..2599c741405e 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -38,6 +38,8 @@ typedef struct bootmem_data { struct list_head list; } bootmem_data_t; +extern bootmem_data_t bootmem_node_data[]; + extern unsigned long bootmem_bootmap_pages(unsigned long); extern unsigned long init_bootmem(unsigned long addr, unsigned long memend); extern void free_bootmem(unsigned long addr, unsigned long size); diff --git a/mm/bootmem.c b/mm/bootmem.c index 9f4bbc5da73f..35b3cb667036 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -36,6 +36,8 @@ static LIST_HEAD(bdata_list); unsigned long saved_max_pfn; #endif +bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata; + /* return the number of _pages_ that will be allocated for the boot bitmap */ unsigned long __init bootmem_bootmap_pages(unsigned long pages) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9ece07ce65b0..e089b92cdfff 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4040,9 +4040,7 @@ void __init set_dma_reserve(unsigned long new_dma_reserve) } #ifndef CONFIG_NEED_MULTIPLE_NODES -static bootmem_data_t contig_bootmem_data; -struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data }; - +struct pglist_data contig_page_data = { .bdata = &bootmem_node_data[0] }; EXPORT_SYMBOL(contig_page_data); #endif -- cgit v1.2.3 From ffc6421f0720f433b5b35b89ff56e998eabff93b Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:26:59 -0700 Subject: mm: unexport __alloc_bootmem_core() This function has no external callers, so unexport it. Also fix its naming inconsistency. Signed-off-by: Johannes Weiner Cc: Ingo Molnar Cc: Yinghai Lu Cc: Christoph Lameter Cc: Mel Gorman Cc: Andy Whitcroft Cc: Mel Gorman Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 5 ----- mm/bootmem.c | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 2599c741405e..dd8fee6c46d9 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -56,11 +56,6 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); -extern void *__alloc_bootmem_core(struct bootmem_data *bdata, - unsigned long size, - unsigned long align, - unsigned long goal, - unsigned long limit); /* * flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, diff --git a/mm/bootmem.c b/mm/bootmem.c index 251c66c5d96a..4bc6ae2fbaa3 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -234,9 +234,9 @@ static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, * * NOTE: This function is _not_ reentrant. */ -void * __init -__alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, - unsigned long align, unsigned long goal, unsigned long limit) +static void * __init +alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, + unsigned long align, unsigned long goal, unsigned long limit) { unsigned long areasize, preferred; unsigned long i, start = 0, incr, eidx, end_pfn; @@ -245,7 +245,7 @@ __alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, void *node_bootmem_map; if (!size) { - printk("__alloc_bootmem_core(): zero-sized request\n"); + printk("alloc_bootmem_core(): zero-sized request\n"); BUG(); } BUG_ON(align & (align-1)); @@ -512,7 +512,7 @@ void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align, void *ptr; list_for_each_entry(bdata, &bdata_list, list) { - ptr = __alloc_bootmem_core(bdata, size, align, goal, 0); + ptr = alloc_bootmem_core(bdata, size, align, goal, 0); if (ptr) return ptr; } @@ -540,7 +540,7 @@ void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, { void *ptr; - ptr = __alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); + ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); if (ptr) return ptr; @@ -559,8 +559,8 @@ void * __init alloc_bootmem_section(unsigned long size, goal = PFN_PHYS(pfn); limit = PFN_PHYS(section_nr_to_pfn(section_nr + 1)) - 1; pgdat = NODE_DATA(early_pfn_to_nid(pfn)); - ptr = __alloc_bootmem_core(pgdat->bdata, size, SMP_CACHE_BYTES, goal, - limit); + ptr = alloc_bootmem_core(pgdat->bdata, size, SMP_CACHE_BYTES, goal, + limit); if (!ptr) return NULL; @@ -589,8 +589,8 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, void *ptr; list_for_each_entry(bdata, &bdata_list, list) { - ptr = __alloc_bootmem_core(bdata, size, align, goal, - ARCH_LOW_ADDRESS_LIMIT); + ptr = alloc_bootmem_core(bdata, size, align, goal, + ARCH_LOW_ADDRESS_LIMIT); if (ptr) return ptr; } @@ -606,6 +606,6 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal) { - return __alloc_bootmem_core(pgdat->bdata, size, align, goal, - ARCH_LOW_ADDRESS_LIMIT); + return alloc_bootmem_core(pgdat->bdata, size, align, goal, + ARCH_LOW_ADDRESS_LIMIT); } -- cgit v1.2.3 From e4048e5dc4aecec670f48ed007a28779f09cebd6 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 23 Jul 2008 21:27:01 -0700 Subject: page allocator: inline some __alloc_pages() wrappers Two zonelist patch series rewrote __page_alloc() largely. Now, it is just a wrapper function. Inlining them will save a function call. [akpm@linux-foundation.org: export __alloc_pages_internal] Cc: Lee Schermerhorn Cc: Mel Gorman Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 21 +++++++++++++++++---- mm/page_alloc.c | 19 ++----------------- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index b414be387180..f640ed241422 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -173,11 +173,24 @@ static inline void arch_free_page(struct page *page, int order) { } static inline void arch_alloc_page(struct page *page, int order) { } #endif -extern struct page *__alloc_pages(gfp_t, unsigned int, struct zonelist *); +struct page * +__alloc_pages_internal(gfp_t gfp_mask, unsigned int order, + struct zonelist *zonelist, nodemask_t *nodemask); + +static inline struct page * +__alloc_pages(gfp_t gfp_mask, unsigned int order, + struct zonelist *zonelist) +{ + return __alloc_pages_internal(gfp_mask, order, zonelist, NULL); +} + +static inline struct page * +__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, + struct zonelist *zonelist, nodemask_t *nodemask) +{ + return __alloc_pages_internal(gfp_mask, order, zonelist, nodemask); +} -extern struct page * -__alloc_pages_nodemask(gfp_t, unsigned int, - struct zonelist *, nodemask_t *nodemask); static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e089b92cdfff..35b1347d81bb 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1429,7 +1429,7 @@ try_next_zone: /* * This is the 'heart' of the zoned buddy allocator. */ -static struct page * +struct page * __alloc_pages_internal(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, nodemask_t *nodemask) { @@ -1632,22 +1632,7 @@ nopage: got_pg: return page; } - -struct page * -__alloc_pages(gfp_t gfp_mask, unsigned int order, - struct zonelist *zonelist) -{ - return __alloc_pages_internal(gfp_mask, order, zonelist, NULL); -} - -struct page * -__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, - struct zonelist *zonelist, nodemask_t *nodemask) -{ - return __alloc_pages_internal(gfp_mask, order, zonelist, nodemask); -} - -EXPORT_SYMBOL(__alloc_pages); +EXPORT_SYMBOL(__alloc_pages_internal); /* * Common helper functions. -- cgit v1.2.3 From c748e1340e0de3fa7fed86f8bdf499be9242afff Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 23 Jul 2008 21:27:03 -0700 Subject: mm/vmstat.c: proper externs This patch adds proper extern declarations for five variables in include/linux/vmstat.h Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/proc_misc.c | 4 ---- include/linux/vmstat.h | 6 ++++++ kernel/sysctl.c | 2 +- mm/vmstat.c | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index c652d469dc08..b14f43d25e9e 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -232,7 +232,6 @@ static int meminfo_read_proc(char *page, char **start, off_t off, #undef K } -extern const struct seq_operations fragmentation_op; static int fragmentation_open(struct inode *inode, struct file *file) { (void)inode; @@ -246,7 +245,6 @@ static const struct file_operations fragmentation_file_operations = { .release = seq_release, }; -extern const struct seq_operations pagetypeinfo_op; static int pagetypeinfo_open(struct inode *inode, struct file *file) { return seq_open(file, &pagetypeinfo_op); @@ -259,7 +257,6 @@ static const struct file_operations pagetypeinfo_file_ops = { .release = seq_release, }; -extern const struct seq_operations zoneinfo_op; static int zoneinfo_open(struct inode *inode, struct file *file) { return seq_open(file, &zoneinfo_op); @@ -356,7 +353,6 @@ static const struct file_operations proc_devinfo_operations = { .release = seq_release, }; -extern const struct seq_operations vmstat_op; static int vmstat_open(struct inode *inode, struct file *file) { return seq_open(file, &vmstat_op); diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index e83b69346d23..58334d439516 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -44,6 +44,12 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, NR_VM_EVENT_ITEMS }; +extern const struct seq_operations fragmentation_op; +extern const struct seq_operations pagetypeinfo_op; +extern const struct seq_operations zoneinfo_op; +extern const struct seq_operations vmstat_op; +extern int sysctl_stat_interval; + #ifdef CONFIG_VM_EVENT_COUNTERS /* * Light weight per cpu counter implementation. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2a7b9d88706b..1f7b3b76a166 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,6 @@ extern int sysctl_drop_caches; extern int percpu_pagelist_fraction; extern int compat_log; extern int maps_protect; -extern int sysctl_stat_interval; extern int latencytop_enabled; extern int sysctl_nr_open_min, sysctl_nr_open_max; #ifdef CONFIG_RCU_TORTURE_TEST diff --git a/mm/vmstat.c b/mm/vmstat.c index c3d4a781802f..b0d08e667ece 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifdef CONFIG_VM_EVENT_COUNTERS -- cgit v1.2.3 From 0d71d10a4252a3938e6b70189bc776171c02e076 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Wed, 23 Jul 2008 21:27:05 -0700 Subject: mm: remove nopfn There are no users of nopfn in the tree. Remove it. [hugh@veritas.com: fix build error] Signed-off-by: Nick Piggin Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 9 -------- mm/memory.c | 67 ++++++------------------------------------------------ 2 files changed, 7 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 2128ef7780c6..eb815cfc1b35 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -166,8 +166,6 @@ struct vm_operations_struct { void (*open)(struct vm_area_struct * area); void (*close)(struct vm_area_struct * area); int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf); - unsigned long (*nopfn)(struct vm_area_struct *area, - unsigned long address); /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ @@ -674,13 +672,6 @@ static inline int page_mapped(struct page *page) return atomic_read(&(page)->_mapcount) >= 0; } -/* - * Error return values for the *_nopfn functions - */ -#define NOPFN_SIGBUS ((unsigned long) -1) -#define NOPFN_OOM ((unsigned long) -2) -#define NOPFN_REFAULT ((unsigned long) -3) - /* * Different kinds of faults, as returned by handle_mm_fault(). * Used to decide whether a process gets delivered SIGBUS or diff --git a/mm/memory.c b/mm/memory.c index 2302d228fe04..46dbed4b7446 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1058,11 +1058,9 @@ static inline int use_zero_page(struct vm_area_struct *vma) if (vma->vm_flags & (VM_LOCKED | VM_SHARED)) return 0; /* - * And if we have a fault or a nopfn routine, it's not an - * anonymous region. + * And if we have a fault routine, it's not an anonymous region. */ - return !vma->vm_ops || - (!vma->vm_ops->fault && !vma->vm_ops->nopfn); + return !vma->vm_ops || !vma->vm_ops->fault; } int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, @@ -1338,6 +1336,11 @@ out: * * This function should only be called from a vm_ops->fault handler, and * in that case the handler should return NULL. + * + * vma cannot be a COW mapping. + * + * As this is called only for pages that do not currently exist, we + * do not need to flush old virtual caches or the TLB. */ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) @@ -2501,59 +2504,6 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma, return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); } - -/* - * do_no_pfn() tries to create a new page mapping for a page without - * a struct_page backing it - * - * As this is called only for pages that do not currently exist, we - * do not need to flush old virtual caches or the TLB. - * - * We enter with non-exclusive mmap_sem (to exclude vma changes, - * but allow concurrent faults), and pte mapped but not yet locked. - * We return with mmap_sem still held, but pte unmapped and unlocked. - * - * It is expected that the ->nopfn handler always returns the same pfn - * for a given virtual mapping. - * - * Mark this `noinline' to prevent it from bloating the main pagefault code. - */ -static noinline int do_no_pfn(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long address, pte_t *page_table, pmd_t *pmd, - int write_access) -{ - spinlock_t *ptl; - pte_t entry; - unsigned long pfn; - - pte_unmap(page_table); - BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP))); - BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags)); - - pfn = vma->vm_ops->nopfn(vma, address & PAGE_MASK); - - BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn)); - - if (unlikely(pfn == NOPFN_OOM)) - return VM_FAULT_OOM; - else if (unlikely(pfn == NOPFN_SIGBUS)) - return VM_FAULT_SIGBUS; - else if (unlikely(pfn == NOPFN_REFAULT)) - return 0; - - page_table = pte_offset_map_lock(mm, pmd, address, &ptl); - - /* Only go through if we didn't race with anybody else... */ - if (pte_none(*page_table)) { - entry = pfn_pte(pfn, vma->vm_page_prot); - if (write_access) - entry = maybe_mkwrite(pte_mkdirty(entry), vma); - set_pte_at(mm, address, page_table, entry); - } - pte_unmap_unlock(page_table, ptl); - return 0; -} - /* * Fault of a previously existing named mapping. Repopulate the pte * from the encoded file_pte if possible. This enables swappable @@ -2614,9 +2564,6 @@ static inline int handle_pte_fault(struct mm_struct *mm, if (likely(vma->vm_ops->fault)) return do_linear_fault(mm, vma, address, pte, pmd, write_access, entry); - if (unlikely(vma->vm_ops->nopfn)) - return do_no_pfn(mm, vma, address, pte, - pmd, write_access); } return do_anonymous_page(mm, vma, address, pte, pmd, write_access); -- cgit v1.2.3 From 28b2ee20c7cba812b6f2ccf6d722cf86d00a84dc Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Wed, 23 Jul 2008 21:27:05 -0700 Subject: access_process_vm device memory infrastructure In order to be able to debug things like the X server and programs using the PPC Cell SPUs, the debugger needs to be able to access device memory through ptrace and /proc/pid/mem. This patch: Add the generic_access_phys access function and put the hooks in place to allow access_process_vm to access device or PPC Cell SPU memory. [riel@redhat.com: Add documentation for the vm_ops->access function] Signed-off-by: Rik van Riel Signed-off-by: Benjamin Herrensmidt Cc: Dave Airlie Cc: Hugh Dickins Cc: Paul Mackerras Cc: Arnd Bergmann Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 7 ++ arch/Kconfig | 3 + arch/x86/Kconfig | 1 + arch/x86/mm/ioremap.c | 8 +++ include/asm-x86/io_32.h | 2 + include/asm-x86/io_64.h | 2 + include/linux/mm.h | 8 +++ mm/memory.c | 131 ++++++++++++++++++++++++++++++++------ 8 files changed, 144 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 8b22d7d8b991..680fb566b928 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -510,6 +510,7 @@ prototypes: void (*close)(struct vm_area_struct*); int (*fault)(struct vm_area_struct*, struct vm_fault *); int (*page_mkwrite)(struct vm_area_struct *, struct page *); + int (*access)(struct vm_area_struct *, unsigned long, void*, int, int); locking rules: BKL mmap_sem PageLocked(page) @@ -517,6 +518,7 @@ open: no yes close: no yes fault: no yes page_mkwrite: no yes no +access: no yes ->page_mkwrite() is called when a previously read-only page is about to become writeable. The file system is responsible for @@ -525,6 +527,11 @@ taking to lock out truncate, the page range should be verified to be within i_size. The page mapping should also be checked that it is not NULL. + ->access() is called when get_user_pages() fails in +acces_process_vm(), typically used to debug a process through +/proc/pid/mem or ptrace. This function is needed only for +VM_IO | VM_PFNMAP VMAs. + ================================================================================ Dubious stuff diff --git a/arch/Kconfig b/arch/Kconfig index 4d5ebbc1e72b..6093c0be58b0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -31,6 +31,9 @@ config KRETPROBES def_bool y depends on KPROBES && HAVE_KRETPROBES +config HAVE_IOREMAP_PROT + def_bool n + config HAVE_KPROBES def_bool n diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 03980cb04291..b2ddfcf01728 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -21,6 +21,7 @@ config X86 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_IDE select HAVE_OPROFILE + select HAVE_IOREMAP_PROT select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_DYNAMIC_FTRACE diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 24c1d3c30186..016f335bbeea 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -330,6 +330,14 @@ static void __iomem *ioremap_default(resource_size_t phys_addr, return (void __iomem *)ret; } +void __iomem *ioremap_prot(resource_size_t phys_addr, unsigned long size, + unsigned long prot_val) +{ + return __ioremap_caller(phys_addr, size, (prot_val & _PAGE_CACHE_MASK), + __builtin_return_address(0)); +} +EXPORT_SYMBOL(ioremap_prot); + /** * iounmap - Free a IO remapping * @addr: virtual address from ioremap_* diff --git a/include/asm-x86/io_32.h b/include/asm-x86/io_32.h index 4df44ed54077..e876d89ac156 100644 --- a/include/asm-x86/io_32.h +++ b/include/asm-x86/io_32.h @@ -110,6 +110,8 @@ static inline void *phys_to_virt(unsigned long address) */ extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size); extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size); +extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size, + unsigned long prot_val); /* * The default ioremap() behavior is non-cached: diff --git a/include/asm-x86/io_64.h b/include/asm-x86/io_64.h index ddd8058a5026..22995c5c5adc 100644 --- a/include/asm-x86/io_64.h +++ b/include/asm-x86/io_64.h @@ -175,6 +175,8 @@ extern void early_iounmap(void *addr, unsigned long size); */ extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size); extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size); +extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size, + unsigned long prot_val); /* * The default ioremap() behavior is non-cached: diff --git a/include/linux/mm.h b/include/linux/mm.h index eb815cfc1b35..5c7f8f64f70e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -170,6 +170,12 @@ struct vm_operations_struct { /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ int (*page_mkwrite)(struct vm_area_struct *vma, struct page *page); + + /* called by access_process_vm when get_user_pages() fails, typically + * for use by special VMAs that can switch between memory and hardware + */ + int (*access)(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write); #ifdef CONFIG_NUMA /* * set_policy() op must add a reference to any non-NULL @new mempolicy @@ -771,6 +777,8 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma); void unmap_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen, int even_cows); +int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write); static inline void unmap_shared_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen) diff --git a/mm/memory.c b/mm/memory.c index 46dbed4b7446..87350321e66f 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2751,6 +2751,86 @@ int in_gate_area_no_task(unsigned long addr) #endif /* __HAVE_ARCH_GATE_AREA */ +#ifdef CONFIG_HAVE_IOREMAP_PROT +static resource_size_t follow_phys(struct vm_area_struct *vma, + unsigned long address, unsigned int flags, + unsigned long *prot) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep, pte; + spinlock_t *ptl; + resource_size_t phys_addr = 0; + struct mm_struct *mm = vma->vm_mm; + + VM_BUG_ON(!(vma->vm_flags & (VM_IO | VM_PFNMAP))); + + pgd = pgd_offset(mm, address); + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) + goto no_page_table; + + pud = pud_offset(pgd, address); + if (pud_none(*pud) || unlikely(pud_bad(*pud))) + goto no_page_table; + + pmd = pmd_offset(pud, address); + if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) + goto no_page_table; + + /* We cannot handle huge page PFN maps. Luckily they don't exist. */ + if (pmd_huge(*pmd)) + goto no_page_table; + + ptep = pte_offset_map_lock(mm, pmd, address, &ptl); + if (!ptep) + goto out; + + pte = *ptep; + if (!pte_present(pte)) + goto unlock; + if ((flags & FOLL_WRITE) && !pte_write(pte)) + goto unlock; + phys_addr = pte_pfn(pte); + phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */ + + *prot = pgprot_val(pte_pgprot(pte)); + +unlock: + pte_unmap_unlock(ptep, ptl); +out: + return phys_addr; +no_page_table: + return 0; +} + +int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + resource_size_t phys_addr; + unsigned long prot = 0; + void *maddr; + int offset = addr & (PAGE_SIZE-1); + + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) + return -EINVAL; + + phys_addr = follow_phys(vma, addr, write, &prot); + + if (!phys_addr) + return -EINVAL; + + maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot); + if (write) + memcpy_toio(maddr + offset, buf, len); + else + memcpy_fromio(buf, maddr + offset, len); + iounmap(maddr); + + return len; +} +#endif + /* * Access another process' address space. * Source/target buffer must be kernel space, @@ -2760,7 +2840,6 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in { struct mm_struct *mm; struct vm_area_struct *vma; - struct page *page; void *old_buf = buf; mm = get_task_mm(tsk); @@ -2772,28 +2851,44 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in while (len) { int bytes, ret, offset; void *maddr; + struct page *page = NULL; ret = get_user_pages(tsk, mm, addr, 1, write, 1, &page, &vma); - if (ret <= 0) - break; - - bytes = len; - offset = addr & (PAGE_SIZE-1); - if (bytes > PAGE_SIZE-offset) - bytes = PAGE_SIZE-offset; - - maddr = kmap(page); - if (write) { - copy_to_user_page(vma, page, addr, - maddr + offset, buf, bytes); - set_page_dirty_lock(page); + if (ret <= 0) { + /* + * Check if this is a VM_IO | VM_PFNMAP VMA, which + * we can access using slightly different code. + */ +#ifdef CONFIG_HAVE_IOREMAP_PROT + vma = find_vma(mm, addr); + if (!vma) + break; + if (vma->vm_ops && vma->vm_ops->access) + ret = vma->vm_ops->access(vma, addr, buf, + len, write); + if (ret <= 0) +#endif + break; + bytes = ret; } else { - copy_from_user_page(vma, page, addr, - buf, maddr + offset, bytes); + bytes = len; + offset = addr & (PAGE_SIZE-1); + if (bytes > PAGE_SIZE-offset) + bytes = PAGE_SIZE-offset; + + maddr = kmap(page); + if (write) { + copy_to_user_page(vma, page, addr, + maddr + offset, buf, bytes); + set_page_dirty_lock(page); + } else { + copy_from_user_page(vma, page, addr, + buf, maddr + offset, bytes); + } + kunmap(page); + page_cache_release(page); } - kunmap(page); - page_cache_release(page); len -= bytes; buf += bytes; addr += bytes; -- cgit v1.2.3 From 42b7772812d15b86543a23b82bd6070eef9a08b1 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Wed, 23 Jul 2008 21:27:10 -0700 Subject: mm: remove double indirection on tlb parameter to free_pgd_range() & Co The double indirection here is not needed anywhere and hence (at least) confusing. Signed-off-by: Jan Beulich Cc: Hugh Dickins Cc: Nick Piggin Cc: Christoph Lameter Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: "Luck, Tony" Cc: Paul Mundt Cc: "David S. Miller" Acked-by: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/hugetlbpage.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 8 ++++---- fs/exec.c | 4 ++-- include/asm-ia64/hugetlb.h | 2 +- include/asm-powerpc/hugetlb.h | 2 +- include/asm-sh/hugetlb.h | 2 +- include/asm-sparc/hugetlb.h | 2 +- include/asm-x86/hugetlb.h | 2 +- include/linux/mm.h | 4 +--- mm/internal.h | 3 +++ mm/memory.c | 10 ++++++---- mm/mmap.c | 6 ++++-- 12 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index d3ce8f3bcaa6..cd49e2860eef 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -112,7 +112,7 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int wri return NULL; } -void hugetlb_free_pgd_range(struct mmu_gather **tlb, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 0d12fba31bc5..1a96cc891cf5 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -255,7 +255,7 @@ static void hugetlb_free_pud_range(struct mmu_gather *tlb, pgd_t *pgd, * * Must be called with pagetable lock held. */ -void hugetlb_free_pgd_range(struct mmu_gather **tlb, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { @@ -315,13 +315,13 @@ void hugetlb_free_pgd_range(struct mmu_gather **tlb, return; start = addr; - pgd = pgd_offset((*tlb)->mm, addr); + pgd = pgd_offset(tlb->mm, addr); do { - BUG_ON(get_slice_psize((*tlb)->mm, addr) != mmu_huge_psize); + BUG_ON(get_slice_psize(tlb->mm, addr) != mmu_huge_psize); next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(pgd)) continue; - hugetlb_free_pud_range(*tlb, pgd, addr, next, floor, ceiling); + hugetlb_free_pud_range(tlb, pgd, addr, next, floor, ceiling); } while (pgd++, addr = next, addr != end); } diff --git a/fs/exec.c b/fs/exec.c index fd9234379e8d..190ed1f92774 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -541,7 +541,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) /* * when the old and new regions overlap clear from new_end. */ - free_pgd_range(&tlb, new_end, old_end, new_end, + free_pgd_range(tlb, new_end, old_end, new_end, vma->vm_next ? vma->vm_next->vm_start : 0); } else { /* @@ -550,7 +550,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) * have constraints on va-space that make this illegal (IA64) - * for the others its just a little faster. */ - free_pgd_range(&tlb, old_start, old_end, new_end, + free_pgd_range(tlb, old_start, old_end, new_end, vma->vm_next ? vma->vm_next->vm_start : 0); } tlb_finish_mmu(tlb, new_end, old_end); diff --git a/include/asm-ia64/hugetlb.h b/include/asm-ia64/hugetlb.h index f28a9701f1cf..e9d1e5e2382d 100644 --- a/include/asm-ia64/hugetlb.h +++ b/include/asm-ia64/hugetlb.h @@ -4,7 +4,7 @@ #include -void hugetlb_free_pgd_range(struct mmu_gather **tlb, unsigned long addr, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); diff --git a/include/asm-powerpc/hugetlb.h b/include/asm-powerpc/hugetlb.h index be32ff02f4a0..0a37aa5ecaa5 100644 --- a/include/asm-powerpc/hugetlb.h +++ b/include/asm-powerpc/hugetlb.h @@ -7,7 +7,7 @@ int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, unsigned long len); -void hugetlb_free_pgd_range(struct mmu_gather **tlb, unsigned long addr, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); diff --git a/include/asm-sh/hugetlb.h b/include/asm-sh/hugetlb.h index 02402303d89b..fb30018938c7 100644 --- a/include/asm-sh/hugetlb.h +++ b/include/asm-sh/hugetlb.h @@ -26,7 +26,7 @@ static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) { } -static inline void hugetlb_free_pgd_range(struct mmu_gather **tlb, +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) diff --git a/include/asm-sparc/hugetlb.h b/include/asm-sparc/hugetlb.h index 412af58926a0..aeb92374ca3d 100644 --- a/include/asm-sparc/hugetlb.h +++ b/include/asm-sparc/hugetlb.h @@ -31,7 +31,7 @@ static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) return 0; } -static inline void hugetlb_free_pgd_range(struct mmu_gather **tlb, +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) diff --git a/include/asm-x86/hugetlb.h b/include/asm-x86/hugetlb.h index 14171a4924f6..7eed6e0883bf 100644 --- a/include/asm-x86/hugetlb.h +++ b/include/asm-x86/hugetlb.h @@ -26,7 +26,7 @@ static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) { } -static inline void hugetlb_free_pgd_range(struct mmu_gather **tlb, +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) diff --git a/include/linux/mm.h b/include/linux/mm.h index 5c7f8f64f70e..f8071097302a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -769,10 +769,8 @@ struct mm_walk { int walk_page_range(unsigned long addr, unsigned long end, struct mm_walk *walk); -void free_pgd_range(struct mmu_gather **tlb, unsigned long addr, +void free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); -void free_pgtables(struct mmu_gather **tlb, struct vm_area_struct *start_vma, - unsigned long floor, unsigned long ceiling); int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma); void unmap_mapping_range(struct address_space *mapping, diff --git a/mm/internal.h b/mm/internal.h index 50807e12490e..858ad01864dc 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -13,6 +13,9 @@ #include +void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma, + unsigned long floor, unsigned long ceiling); + static inline void set_page_count(struct page *page, int v) { atomic_set(&page->_count, v); diff --git a/mm/memory.c b/mm/memory.c index 87350321e66f..82f3f1c5cf17 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -61,6 +61,8 @@ #include #include +#include "internal.h" + #ifndef CONFIG_NEED_MULTIPLE_NODES /* use the per-pgdat data instead for discontigmem - mbligh */ unsigned long max_mapnr; @@ -211,7 +213,7 @@ static inline void free_pud_range(struct mmu_gather *tlb, pgd_t *pgd, * * Must be called with pagetable lock held. */ -void free_pgd_range(struct mmu_gather **tlb, +void free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { @@ -262,16 +264,16 @@ void free_pgd_range(struct mmu_gather **tlb, return; start = addr; - pgd = pgd_offset((*tlb)->mm, addr); + pgd = pgd_offset(tlb->mm, addr); do { next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(pgd)) continue; - free_pud_range(*tlb, pgd, addr, next, floor, ceiling); + free_pud_range(tlb, pgd, addr, next, floor, ceiling); } while (pgd++, addr = next, addr != end); } -void free_pgtables(struct mmu_gather **tlb, struct vm_area_struct *vma, +void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long floor, unsigned long ceiling) { while (vma) { diff --git a/mm/mmap.c b/mm/mmap.c index 1d102b956fd8..75e0d0673d78 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -32,6 +32,8 @@ #include #include +#include "internal.h" + #ifndef arch_mmap_check #define arch_mmap_check(addr, len, flags) (0) #endif @@ -1763,7 +1765,7 @@ static void unmap_region(struct mm_struct *mm, update_hiwater_rss(mm); unmap_vmas(&tlb, vma, start, end, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); - free_pgtables(&tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, + free_pgtables(tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, next? next->vm_start: 0); tlb_finish_mmu(tlb, start, end); } @@ -2063,7 +2065,7 @@ void exit_mmap(struct mm_struct *mm) /* Use -1 here to ensure all VMAs in the mm are unmapped */ end = unmap_vmas(&tlb, vma, 0, -1, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); - free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0); + free_pgtables(tlb, vma, FIRST_USER_ADDRESS, 0); tlb_finish_mmu(tlb, 0, end); /* -- cgit v1.2.3 From da3bbdd4632c0171406b2677e31494afa5bde2f8 Mon Sep 17 00:00:00 2001 From: Kentaro Makita Date: Wed, 23 Jul 2008 21:27:13 -0700 Subject: fix soft lock up at NFS mount via per-SB LRU-list of unused dentries [Summary] Split LRU-list of unused dentries to one per superblock to avoid soft lock up during NFS mounts and remounting of any filesystem. Previously I posted here: http://lkml.org/lkml/2008/3/5/590 [Descriptions] - background dentry_unused is a list of dentries which are not referenced. dentry_unused grows up when references on directories or files are released. This list can be very long if there is huge free memory. - the problem When shrink_dcache_sb() is called, it scans all dentry_unused linearly under spin_lock(), and if dentry->d_sb is differnt from given superblock, scan next dentry. This scan costs very much if there are many entries, and very ineffective if there are many superblocks. IOW, When we need to shrink unused dentries on one dentry, but scans unused dentries on all superblocks in the system. For example, we scan 500 dentries to unmount a filesystem, but scans 1,000,000 or more unused dentries on other superblocks. In our case , At mounting NFS*, shrink_dcache_sb() is called to shrink unused dentries on NFS, but scans 100,000,000 unused dentries on superblocks in the system such as local ext3 filesystems. I hear NFS mounting took 1 min on some system in use. * : NFS uses virtual filesystem in rpc layer, so NFS is affected by this problem. 100,000,000 is possible number on large systems. Per-superblock LRU of unused dentried can reduce the cost in reasonable manner. - How to fix I found this problem is solved by David Chinner's "Per-superblock unused dentry LRU lists V3"(1), so I rebase it and add some fix to reclaim with fairness, which is in Andrew Morton's comments(2). 1) http://lkml.org/lkml/2006/5/25/318 2) http://lkml.org/lkml/2006/5/25/320 Split LRU-list of unused dentries to each superblocks. Then, NFS mounting will check dentries under a superblock instead of all. But this spliting will break LRU of dentry-unused. So, I've attempted to make reclaim unused dentrins with fairness by calculate number of dentries to scan on this sb based on following way number of dentries to scan on this sb = count * (number of dentries on this sb / number of dentries in the machine) - ToDo - I have to measuring performance number and do stress tests. - When unmount occurs during prune_dcache(), scanning on same superblock, It is unable to reach next superblock because it is gone away. We restart scannig superblock from first one, it causes unfairness of reclaim unused dentries on first superblock. But I think this happens very rarely. - Test Results Result on 6GB boxes with excessive unused dentries. Without patch: $ cat /proc/sys/fs/dentry-state 10181835 10180203 45 0 0 0 # mount -t nfs 10.124.60.70:/work/kernel-src nfs real 0m1.830s user 0m0.001s sys 0m1.653s With this patch: $ cat /proc/sys/fs/dentry-state 10236610 10234751 45 0 0 0 # mount -t nfs 10.124.60.70:/work/kernel-src nfs real 0m0.106s user 0m0.002s sys 0m0.032s [akpm@linux-foundation.org: fix comments] Signed-off-by: Kentaro Makita Cc: Neil Brown Cc: Trond Myklebust Cc: David Chinner Cc: "J. Bruce Fields" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dcache.c | 335 ++++++++++++++++++++++++++++------------------------- fs/super.c | 1 + include/linux/fs.h | 4 + 3 files changed, 185 insertions(+), 155 deletions(-) (limited to 'include/linux') diff --git a/fs/dcache.c b/fs/dcache.c index 6068c25b393c..3818d6ab76ca 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -61,7 +61,6 @@ static struct kmem_cache *dentry_cache __read_mostly; static unsigned int d_hash_mask __read_mostly; static unsigned int d_hash_shift __read_mostly; static struct hlist_head *dentry_hashtable __read_mostly; -static LIST_HEAD(dentry_unused); /* Statistics gathering. */ struct dentry_stat_t dentry_stat = { @@ -96,14 +95,6 @@ static void d_free(struct dentry *dentry) call_rcu(&dentry->d_u.d_rcu, d_callback); } -static void dentry_lru_remove(struct dentry *dentry) -{ - if (!list_empty(&dentry->d_lru)) { - list_del_init(&dentry->d_lru); - dentry_stat.nr_unused--; - } -} - /* * Release the dentry's inode, using the filesystem * d_iput() operation if defined. @@ -130,6 +121,41 @@ static void dentry_iput(struct dentry * dentry) } } +/* + * dentry_lru_(add|add_tail|del|del_init) must be called with dcache_lock held. + */ +static void dentry_lru_add(struct dentry *dentry) +{ + list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); + dentry->d_sb->s_nr_dentry_unused++; + dentry_stat.nr_unused++; +} + +static void dentry_lru_add_tail(struct dentry *dentry) +{ + list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); + dentry->d_sb->s_nr_dentry_unused++; + dentry_stat.nr_unused++; +} + +static void dentry_lru_del(struct dentry *dentry) +{ + if (!list_empty(&dentry->d_lru)) { + list_del(&dentry->d_lru); + dentry->d_sb->s_nr_dentry_unused--; + dentry_stat.nr_unused--; + } +} + +static void dentry_lru_del_init(struct dentry *dentry) +{ + if (likely(!list_empty(&dentry->d_lru))) { + list_del_init(&dentry->d_lru); + dentry->d_sb->s_nr_dentry_unused--; + dentry_stat.nr_unused--; + } +} + /** * d_kill - kill dentry and return parent * @dentry: dentry to kill @@ -212,8 +238,7 @@ repeat: goto kill_it; if (list_empty(&dentry->d_lru)) { dentry->d_flags |= DCACHE_REFERENCED; - list_add(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; + dentry_lru_add(dentry); } spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); @@ -222,7 +247,8 @@ repeat: unhash_it: __d_drop(dentry); kill_it: - dentry_lru_remove(dentry); + /* if dentry was on the d_lru list delete it from there */ + dentry_lru_del(dentry); dentry = d_kill(dentry); if (dentry) goto repeat; @@ -290,7 +316,7 @@ int d_invalidate(struct dentry * dentry) static inline struct dentry * __dget_locked(struct dentry *dentry) { atomic_inc(&dentry->d_count); - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); return dentry; } @@ -406,133 +432,167 @@ static void prune_one_dentry(struct dentry * dentry) if (dentry->d_op && dentry->d_op->d_delete) dentry->d_op->d_delete(dentry); - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); __d_drop(dentry); dentry = d_kill(dentry); spin_lock(&dcache_lock); } } -/** - * prune_dcache - shrink the dcache - * @count: number of entries to try and free - * @sb: if given, ignore dentries for other superblocks - * which are being unmounted. - * - * Shrink the dcache. This is done when we need - * more memory, or simply when we need to unmount - * something (at which point we need to unuse - * all dentries). - * - * This function may fail to free any resources if - * all the dentries are in use. +/* + * Shrink the dentry LRU on a given superblock. + * @sb : superblock to shrink dentry LRU. + * @count: If count is NULL, we prune all dentries on superblock. + * @flags: If flags is non-zero, we need to do special processing based on + * which flags are set. This means we don't need to maintain multiple + * similar copies of this loop. */ - -static void prune_dcache(int count, struct super_block *sb) +static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags) { - spin_lock(&dcache_lock); - for (; count ; count--) { - struct dentry *dentry; - struct list_head *tmp; - struct rw_semaphore *s_umount; - - cond_resched_lock(&dcache_lock); + LIST_HEAD(referenced); + LIST_HEAD(tmp); + struct dentry *dentry; + int cnt = 0; - tmp = dentry_unused.prev; - if (sb) { - /* Try to find a dentry for this sb, but don't try - * too hard, if they aren't near the tail they will - * be moved down again soon + BUG_ON(!sb); + BUG_ON((flags & DCACHE_REFERENCED) && count == NULL); + spin_lock(&dcache_lock); + if (count != NULL) + /* called from prune_dcache() and shrink_dcache_parent() */ + cnt = *count; +restart: + if (count == NULL) + list_splice_init(&sb->s_dentry_lru, &tmp); + else { + while (!list_empty(&sb->s_dentry_lru)) { + dentry = list_entry(sb->s_dentry_lru.prev, + struct dentry, d_lru); + BUG_ON(dentry->d_sb != sb); + + spin_lock(&dentry->d_lock); + /* + * If we are honouring the DCACHE_REFERENCED flag and + * the dentry has this flag set, don't free it. Clear + * the flag and put it back on the LRU. */ - int skip = count; - while (skip && tmp != &dentry_unused && - list_entry(tmp, struct dentry, d_lru)->d_sb != sb) { - skip--; - tmp = tmp->prev; + if ((flags & DCACHE_REFERENCED) + && (dentry->d_flags & DCACHE_REFERENCED)) { + dentry->d_flags &= ~DCACHE_REFERENCED; + list_move_tail(&dentry->d_lru, &referenced); + spin_unlock(&dentry->d_lock); + } else { + list_move_tail(&dentry->d_lru, &tmp); + spin_unlock(&dentry->d_lock); + cnt--; + if (!cnt) + break; } } - if (tmp == &dentry_unused) - break; - list_del_init(tmp); - prefetch(dentry_unused.prev); - dentry_stat.nr_unused--; - dentry = list_entry(tmp, struct dentry, d_lru); - - spin_lock(&dentry->d_lock); + } + while (!list_empty(&tmp)) { + dentry = list_entry(tmp.prev, struct dentry, d_lru); + dentry_lru_del_init(dentry); + spin_lock(&dentry->d_lock); /* * We found an inuse dentry which was not removed from - * dentry_unused because of laziness during lookup. Do not free - * it - just keep it off the dentry_unused list. + * the LRU because of laziness during lookup. Do not free + * it - just keep it off the LRU list. */ - if (atomic_read(&dentry->d_count)) { - spin_unlock(&dentry->d_lock); + if (atomic_read(&dentry->d_count)) { + spin_unlock(&dentry->d_lock); continue; } - /* If the dentry was recently referenced, don't free it. */ - if (dentry->d_flags & DCACHE_REFERENCED) { - dentry->d_flags &= ~DCACHE_REFERENCED; - list_add(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; - spin_unlock(&dentry->d_lock); + prune_one_dentry(dentry); + /* dentry->d_lock was dropped in prune_one_dentry() */ + cond_resched_lock(&dcache_lock); + } + if (count == NULL && !list_empty(&sb->s_dentry_lru)) + goto restart; + if (count != NULL) + *count = cnt; + if (!list_empty(&referenced)) + list_splice(&referenced, &sb->s_dentry_lru); + spin_unlock(&dcache_lock); +} + +/** + * prune_dcache - shrink the dcache + * @count: number of entries to try to free + * + * Shrink the dcache. This is done when we need more memory, or simply when we + * need to unmount something (at which point we need to unuse all dentries). + * + * This function may fail to free any resources if all the dentries are in use. + */ +static void prune_dcache(int count) +{ + struct super_block *sb; + int w_count; + int unused = dentry_stat.nr_unused; + int prune_ratio; + int pruned; + + if (unused == 0 || count == 0) + return; + spin_lock(&dcache_lock); +restart: + if (count >= unused) + prune_ratio = 1; + else + prune_ratio = unused / count; + spin_lock(&sb_lock); + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_nr_dentry_unused == 0) continue; - } - /* - * If the dentry is not DCACHED_REFERENCED, it is time - * to remove it from the dcache, provided the super block is - * NULL (which means we are trying to reclaim memory) - * or this dentry belongs to the same super block that - * we want to shrink. - */ - /* - * If this dentry is for "my" filesystem, then I can prune it - * without taking the s_umount lock (I already hold it). + sb->s_count++; + /* Now, we reclaim unused dentrins with fairness. + * We reclaim them same percentage from each superblock. + * We calculate number of dentries to scan on this sb + * as follows, but the implementation is arranged to avoid + * overflows: + * number of dentries to scan on this sb = + * count * (number of dentries on this sb / + * number of dentries in the machine) */ - if (sb && dentry->d_sb == sb) { - prune_one_dentry(dentry); - continue; - } + spin_unlock(&sb_lock); + if (prune_ratio != 1) + w_count = (sb->s_nr_dentry_unused / prune_ratio) + 1; + else + w_count = sb->s_nr_dentry_unused; + pruned = w_count; /* - * ...otherwise we need to be sure this filesystem isn't being - * unmounted, otherwise we could race with - * generic_shutdown_super(), and end up holding a reference to - * an inode while the filesystem is unmounted. - * So we try to get s_umount, and make sure s_root isn't NULL. - * (Take a local copy of s_umount to avoid a use-after-free of - * `dentry'). + * We need to be sure this filesystem isn't being unmounted, + * otherwise we could race with generic_shutdown_super(), and + * end up holding a reference to an inode while the filesystem + * is unmounted. So we try to get s_umount, and make sure + * s_root isn't NULL. */ - s_umount = &dentry->d_sb->s_umount; - if (down_read_trylock(s_umount)) { - if (dentry->d_sb->s_root != NULL) { - prune_one_dentry(dentry); - up_read(s_umount); - continue; + if (down_read_trylock(&sb->s_umount)) { + if ((sb->s_root != NULL) && + (!list_empty(&sb->s_dentry_lru))) { + spin_unlock(&dcache_lock); + __shrink_dcache_sb(sb, &w_count, + DCACHE_REFERENCED); + pruned -= w_count; + spin_lock(&dcache_lock); } - up_read(s_umount); + up_read(&sb->s_umount); } - spin_unlock(&dentry->d_lock); + spin_lock(&sb_lock); + count -= pruned; /* - * Insert dentry at the head of the list as inserting at the - * tail leads to a cycle. + * restart only when sb is no longer on the list and + * we have more work to do. */ - list_add(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; + if (__put_super_and_need_restart(sb) && count > 0) { + spin_unlock(&sb_lock); + goto restart; + } } + spin_unlock(&sb_lock); spin_unlock(&dcache_lock); } -/* - * Shrink the dcache for the specified super block. - * This allows us to unmount a device without disturbing - * the dcache for the other devices. - * - * This implementation makes just two traversals of the - * unused list. On the first pass we move the selected - * dentries to the most recent end, and on the second - * pass we free them. The second pass must restart after - * each dput(), but since the target dentries are all at - * the end, it's really just a single traversal. - */ - /** * shrink_dcache_sb - shrink dcache for a superblock * @sb: superblock @@ -541,44 +601,9 @@ static void prune_dcache(int count, struct super_block *sb) * is used to free the dcache before unmounting a file * system */ - void shrink_dcache_sb(struct super_block * sb) { - struct list_head *tmp, *next; - struct dentry *dentry; - - /* - * Pass one ... move the dentries for the specified - * superblock to the most recent end of the unused list. - */ - spin_lock(&dcache_lock); - list_for_each_prev_safe(tmp, next, &dentry_unused) { - dentry = list_entry(tmp, struct dentry, d_lru); - if (dentry->d_sb != sb) - continue; - list_move_tail(tmp, &dentry_unused); - } - - /* - * Pass two ... free the dentries for this superblock. - */ -repeat: - list_for_each_prev_safe(tmp, next, &dentry_unused) { - dentry = list_entry(tmp, struct dentry, d_lru); - if (dentry->d_sb != sb) - continue; - dentry_stat.nr_unused--; - list_del_init(tmp); - spin_lock(&dentry->d_lock); - if (atomic_read(&dentry->d_count)) { - spin_unlock(&dentry->d_lock); - continue; - } - prune_one_dentry(dentry); - cond_resched_lock(&dcache_lock); - goto repeat; - } - spin_unlock(&dcache_lock); + __shrink_dcache_sb(sb, NULL, 0); } /* @@ -595,7 +620,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) /* detach this root from the system */ spin_lock(&dcache_lock); - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); __d_drop(dentry); spin_unlock(&dcache_lock); @@ -609,7 +634,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) spin_lock(&dcache_lock); list_for_each_entry(loop, &dentry->d_subdirs, d_u.d_child) { - dentry_lru_remove(loop); + dentry_lru_del_init(loop); __d_drop(loop); cond_resched_lock(&dcache_lock); } @@ -791,14 +816,13 @@ resume: struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); /* * move only zero ref count dentries to the end * of the unused list for prune_dcache */ if (!atomic_read(&dentry->d_count)) { - list_add_tail(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; + dentry_lru_add_tail(dentry); found++; } @@ -840,10 +864,11 @@ out: void shrink_dcache_parent(struct dentry * parent) { + struct super_block *sb = parent->d_sb; int found; while ((found = select_parent(parent)) != 0) - prune_dcache(found, parent->d_sb); + __shrink_dcache_sb(sb, &found, 0); } /* @@ -863,7 +888,7 @@ static int shrink_dcache_memory(int nr, gfp_t gfp_mask) if (nr) { if (!(gfp_mask & __GFP_FS)) return -1; - prune_dcache(nr, NULL); + prune_dcache(nr); } return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; } @@ -1215,7 +1240,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) * rcu_read_lock() and rcu_read_unlock() are used to disable preemption while * lookup is going on. * - * dentry_unused list is not updated even if lookup finds the required dentry + * The dentry unused LRU is not updated even if lookup finds the required dentry * in there. It is updated in places such as prune_dcache, shrink_dcache_sb, * select_parent and __dget_locked. This laziness saves lookup from dcache_lock * acquisition. diff --git a/fs/super.c b/fs/super.c index 453877c5697b..e931ae9511fe 100644 --- a/fs/super.c +++ b/fs/super.c @@ -70,6 +70,7 @@ static struct super_block *alloc_super(struct file_system_type *type) INIT_LIST_HEAD(&s->s_instances); INIT_HLIST_HEAD(&s->s_anon); INIT_LIST_HEAD(&s->s_inodes); + INIT_LIST_HEAD(&s->s_dentry_lru); init_rwsem(&s->s_umount); mutex_init(&s->s_lock); lockdep_set_class(&s->s_umount, &type->s_umount_key); diff --git a/include/linux/fs.h b/include/linux/fs.h index ff54ae4933f3..e5e6a244096c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1025,6 +1025,7 @@ extern int send_sigurg(struct fown_struct *fown); extern struct list_head super_blocks; extern spinlock_t sb_lock; +#define sb_entry(list) list_entry((list), struct super_block, s_list) #define S_BIAS (1<<30) struct super_block { struct list_head s_list; /* Keep this first */ @@ -1058,6 +1059,9 @@ struct super_block { struct list_head s_more_io; /* parked for more writeback */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ struct list_head s_files; + /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ + struct list_head s_dentry_lru; /* unused dentry lru */ + int s_nr_dentry_unused; /* # of dentry on lru */ struct block_device *s_bdev; struct mtd_info *s_mtd; -- cgit v1.2.3 From 0cad47cf13bc2e9142d3a11d9f50523797d0d4ea Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:16 -0700 Subject: page-flags: record page flag overlays explicitly With the recent page flag reorganisation we have a single enum which defines the valid page flags and their values, nice and clear. However there are a number of bits which are overloaded by different subsystems. Firstly there is PG_owner_priv_1 which is used by filesystems and by XEN. Secondly both SLOB and SLUB use a couple of extra page bits to manage internal state for pages they own; both overlay other bits. All of these "aliases" are scattered about the source making it very hard for a reader to know if the bits are safe to rely on in all contexts; confusion here is bad. As we now have a single place where the bits are clearly assigned it makes sense to clarify the reuse of bits by making the aliases explicit and visible with the original bit assignments. This patch creates explicit aliases within the enum itself for the overloaded bits, creates standard bit accessors PageFoo etc. and uses those throughout. This version pulls the bit manipulation out to standard named page bit accessors as suggested by Christoph, it retains the explicit mapping to the overlayed bits. A fusion of both ideas. This has been SLUB and SLOB have been compile tested on x86_64 only, and SLUB boot tested. If people feel this is worth doing then I can run a fuller set of testing. This patch: Some page flags are used for more than one purpose, for example PG_owner_priv_1. Currently there are individual accessors for each user, each built using the common flag name far away from the bit definitions. This makes it hard to see all possible uses of these bits. Now that we have a single enum to generate the bit orders it makes sense to express overlays in the same place. So create per use aliases for this bit in the main page-flags enum and use those in the accessors. [akpm@linux-foundation.org: fix xen] Signed-off-by: Andy Whitcroft Cc: Pekka Enberg Cc: Christoph Lameter Cc: Matt Mackall Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Reviewed-by: KOSAKI Motohiro Cc: Rik van Riel Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 0d2a4e7012aa..7d8db1233e44 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -96,7 +96,14 @@ enum pageflags { #ifdef CONFIG_IA64_UNCACHED_ALLOCATOR PG_uncached, /* Page has been mapped as uncached */ #endif - __NR_PAGEFLAGS + __NR_PAGEFLAGS, + + /* Filesystems */ + PG_checked = PG_owner_priv_1, + + /* XEN */ + PG_pinned = PG_owner_priv_1, + PG_savepinned = PG_dirty, }; #ifndef __GENERATING_BOUNDS_H @@ -155,9 +162,9 @@ PAGEFLAG(Dirty, dirty) TESTSCFLAG(Dirty, dirty) __CLEARPAGEFLAG(Dirty, dirty) PAGEFLAG(LRU, lru) __CLEARPAGEFLAG(LRU, lru) PAGEFLAG(Active, active) __CLEARPAGEFLAG(Active, active) __PAGEFLAG(Slab, slab) -PAGEFLAG(Checked, owner_priv_1) /* Used by some filesystems */ -PAGEFLAG(Pinned, owner_priv_1) TESTSCFLAG(Pinned, owner_priv_1) /* Xen */ -PAGEFLAG(SavePinned, dirty); /* Xen */ +PAGEFLAG(Checked, checked) /* Used by some filesystems */ +PAGEFLAG(Pinned, pinned) TESTSCFLAG(Pinned, pinned) /* Xen */ +PAGEFLAG(SavePinned, savepinned); /* Xen */ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) -- cgit v1.2.3 From 8a38082d21cbc5ec961da7dda195e98a9a064dcf Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:18 -0700 Subject: slub: record page flag overlays explicitly SLUB reuses two page bits for internal purposes, it overlays PG_active and PG_error. This is hidden away in slub.c. Document these overlays explicitly in the main page-flags enum along with all the others. Signed-off-by: Andy Whitcroft Cc: Pekka Enberg Cc: Christoph Lameter Cc: Matt Mackall Cc: Nick Piggin Tested-by: KOSAKI Motohiro Cc: KOSAKI Motohiro Cc: Rik van Riel Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 7 +++++ mm/slub.c | 65 ++++++++++++---------------------------------- 2 files changed, 24 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 7d8db1233e44..3fc586b7b90b 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -104,6 +104,10 @@ enum pageflags { /* XEN */ PG_pinned = PG_owner_priv_1, PG_savepinned = PG_dirty, + + /* SLUB */ + PG_slub_frozen = PG_active, + PG_slub_debug = PG_error, }; #ifndef __GENERATING_BOUNDS_H @@ -169,6 +173,9 @@ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) +__PAGEFLAG(SlubFrozen, slub_frozen) +__PAGEFLAG(SlubDebug, slub_debug) + /* * Only test-and-set exist for PG_writeback. The unconditional operators are * risky: they bypass page accounting. diff --git a/mm/slub.c b/mm/slub.c index 6d4a49c1ff2f..77c21cf53ff9 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -102,44 +102,12 @@ * the fast path and disables lockless freelists. */ -#define FROZEN (1 << PG_active) - #ifdef CONFIG_SLUB_DEBUG -#define SLABDEBUG (1 << PG_error) +#define SLABDEBUG 1 #else #define SLABDEBUG 0 #endif -static inline int SlabFrozen(struct page *page) -{ - return page->flags & FROZEN; -} - -static inline void SetSlabFrozen(struct page *page) -{ - page->flags |= FROZEN; -} - -static inline void ClearSlabFrozen(struct page *page) -{ - page->flags &= ~FROZEN; -} - -static inline int SlabDebug(struct page *page) -{ - return page->flags & SLABDEBUG; -} - -static inline void SetSlabDebug(struct page *page) -{ - page->flags |= SLABDEBUG; -} - -static inline void ClearSlabDebug(struct page *page) -{ - page->flags &= ~SLABDEBUG; -} - /* * Issues still to be resolved: * @@ -971,7 +939,7 @@ static int free_debug_processing(struct kmem_cache *s, struct page *page, } /* Special debug activities for freeing objects */ - if (!SlabFrozen(page) && !page->freelist) + if (!PageSlubFrozen(page) && !page->freelist) remove_full(s, page); if (s->flags & SLAB_STORE_USER) set_track(s, object, TRACK_FREE, addr); @@ -1157,7 +1125,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) page->flags |= 1 << PG_slab; if (s->flags & (SLAB_DEBUG_FREE | SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | SLAB_TRACE)) - SetSlabDebug(page); + __SetPageSlubDebug(page); start = page_address(page); @@ -1184,14 +1152,14 @@ static void __free_slab(struct kmem_cache *s, struct page *page) int order = compound_order(page); int pages = 1 << order; - if (unlikely(SlabDebug(page))) { + if (unlikely(SLABDEBUG && PageSlubDebug(page))) { void *p; slab_pad_check(s, page); for_each_object(p, s, page_address(page), page->objects) check_object(s, page, p, 0); - ClearSlabDebug(page); + __ClearPageSlubDebug(page); } mod_zone_page_state(page_zone(page), @@ -1288,7 +1256,7 @@ static inline int lock_and_freeze_slab(struct kmem_cache_node *n, if (slab_trylock(page)) { list_del(&page->lru); n->nr_partial--; - SetSlabFrozen(page); + __SetPageSlubFrozen(page); return 1; } return 0; @@ -1398,7 +1366,7 @@ static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail) struct kmem_cache_node *n = get_node(s, page_to_nid(page)); struct kmem_cache_cpu *c = get_cpu_slab(s, smp_processor_id()); - ClearSlabFrozen(page); + __ClearPageSlubFrozen(page); if (page->inuse) { if (page->freelist) { @@ -1406,7 +1374,8 @@ static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail) stat(c, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD); } else { stat(c, DEACTIVATE_FULL); - if (SlabDebug(page) && (s->flags & SLAB_STORE_USER)) + if (SLABDEBUG && PageSlubDebug(page) && + (s->flags & SLAB_STORE_USER)) add_full(n, page); } slab_unlock(page); @@ -1551,7 +1520,7 @@ load_freelist: object = c->page->freelist; if (unlikely(!object)) goto another_slab; - if (unlikely(SlabDebug(c->page))) + if (unlikely(SLABDEBUG && PageSlubDebug(c->page))) goto debug; c->freelist = object[c->offset]; @@ -1588,7 +1557,7 @@ new_slab: if (c->page) flush_slab(s, c); slab_lock(new); - SetSlabFrozen(new); + __SetPageSlubFrozen(new); c->page = new; goto load_freelist; } @@ -1674,7 +1643,7 @@ static void __slab_free(struct kmem_cache *s, struct page *page, stat(c, FREE_SLOWPATH); slab_lock(page); - if (unlikely(SlabDebug(page))) + if (unlikely(SLABDEBUG && PageSlubDebug(page))) goto debug; checks_ok: @@ -1682,7 +1651,7 @@ checks_ok: page->freelist = object; page->inuse--; - if (unlikely(SlabFrozen(page))) { + if (unlikely(PageSlubFrozen(page))) { stat(c, FREE_FROZEN); goto out_unlock; } @@ -3317,12 +3286,12 @@ static void validate_slab_slab(struct kmem_cache *s, struct page *page, s->name, page); if (s->flags & DEBUG_DEFAULT_FLAGS) { - if (!SlabDebug(page)) - printk(KERN_ERR "SLUB %s: SlabDebug not set " + if (!PageSlubDebug(page)) + printk(KERN_ERR "SLUB %s: SlubDebug not set " "on slab 0x%p\n", s->name, page); } else { - if (SlabDebug(page)) - printk(KERN_ERR "SLUB %s: SlabDebug set on " + if (PageSlubDebug(page)) + printk(KERN_ERR "SLUB %s: SlubDebug set on " "slab 0x%p\n", s->name, page); } } -- cgit v1.2.3 From 9023cb7e8564d95a1893f8cb6895a293be9a71fe Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:19 -0700 Subject: slob: record page flag overlays explicitly SLOB reuses two page bits for internal purposes, it overlays PG_active and PG_private. This is hidden away in slob.c. Document these overlays explicitly in the main page-flags enum along with all the others. Signed-off-by: Andy Whitcroft Cc: Pekka Enberg Cc: Christoph Lameter Cc: Matt Mackall Cc: Nick Piggin Reviewed-by: KOSAKI Motohiro Cc: KOSAKI Motohiro Cc: Rik van Riel Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 7 +++++++ mm/slob.c | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 3fc586b7b90b..54590a9a103e 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -105,6 +105,10 @@ enum pageflags { PG_pinned = PG_owner_priv_1, PG_savepinned = PG_dirty, + /* SLOB */ + PG_slob_page = PG_active, + PG_slob_free = PG_private, + /* SLUB */ PG_slub_frozen = PG_active, PG_slub_debug = PG_error, @@ -173,6 +177,9 @@ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) +__PAGEFLAG(SlobPage, slob_page) +__PAGEFLAG(SlobFree, slob_free) + __PAGEFLAG(SlubFrozen, slub_frozen) __PAGEFLAG(SlubDebug, slub_debug) diff --git a/mm/slob.c b/mm/slob.c index a3ad6671adf1..de268eb7ac70 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -130,17 +130,17 @@ static LIST_HEAD(free_slob_large); */ static inline int slob_page(struct slob_page *sp) { - return test_bit(PG_active, &sp->flags); + return PageSlobPage((struct page *)sp); } static inline void set_slob_page(struct slob_page *sp) { - __set_bit(PG_active, &sp->flags); + __SetPageSlobPage((struct page *)sp); } static inline void clear_slob_page(struct slob_page *sp) { - __clear_bit(PG_active, &sp->flags); + __ClearPageSlobPage((struct page *)sp); } /* @@ -148,19 +148,19 @@ static inline void clear_slob_page(struct slob_page *sp) */ static inline int slob_page_free(struct slob_page *sp) { - return test_bit(PG_private, &sp->flags); + return PageSlobFree((struct page *)sp); } static void set_slob_page_free(struct slob_page *sp, struct list_head *list) { list_add(&sp->list, list); - __set_bit(PG_private, &sp->flags); + __SetPageSlobFree((struct page *)sp); } static inline void clear_slob_page_free(struct slob_page *sp) { list_del(&sp->list); - __clear_bit(PG_private, &sp->flags); + __ClearPageSlobFree((struct page *)sp); } #define SLOB_UNIT sizeof(slob_t) -- cgit v1.2.3 From 2185e69f680ae8c8496b6fc15e20c889d5b39b67 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 23 Jul 2008 21:27:19 -0700 Subject: mapping_set_error: add unlikely() This is called on a per-page basis and in the vast majority of cases `error' is zero. Cc: Guillaume Chazarain Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pagemap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index d2fca802f809..ee1ec2c7723c 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -22,7 +22,7 @@ static inline void mapping_set_error(struct address_space *mapping, int error) { - if (error) { + if (unlikely(error)) { if (error == -ENOSPC) set_bit(AS_ENOSPC, &mapping->flags); else -- cgit v1.2.3 From 9109fb7b3520de187ebc3646c209d66a233f7169 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:27:20 -0700 Subject: mm: drop unneeded pgdat argument from free_area_init_node() free_area_init_node() gets passed in the node id as well as the node descriptor. This is redundant as the function can trivially get the node descriptor itself by means of NODE_DATA() and the node's id. I checked all the users and NODE_DATA() seems to be usable everywhere from where this function is called. Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/numa.c | 2 +- arch/arm/mm/init.c | 2 +- arch/avr32/mm/init.c | 2 +- arch/cris/arch-v10/mm/init.c | 2 +- arch/cris/arch-v32/mm/init.c | 2 +- arch/m32r/mm/discontig.c | 3 +-- arch/m32r/mm/init.c | 2 +- arch/m68k/mm/motorola.c | 2 +- arch/m68k/mm/sun3mmu.c | 2 +- arch/parisc/mm/init.c | 2 +- arch/sparc/mm/srmmu.c | 3 +-- arch/sparc/mm/sun4c.c | 3 +-- arch/v850/kernel/setup.c | 3 +-- include/linux/mm.h | 5 ++--- mm/memory_hotplug.c | 2 +- mm/page_alloc.c | 11 ++++++----- 16 files changed, 22 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index a53fda0481ca..def0c74a78a8 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -313,7 +313,7 @@ void __init paging_init(void) zones_size[ZONE_DMA] = dma_local_pfn; zones_size[ZONE_NORMAL] = (end_pfn - start_pfn) - dma_local_pfn; } - free_area_init_node(nid, NODE_DATA(nid), zones_size, start_pfn, NULL); + free_area_init_node(nid, zones_size, start_pfn, NULL); } /* Initialize the kernel's ZERO_PGE. */ diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index b657f1719af0..e6352946dde0 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -284,7 +284,7 @@ bootmem_init_node(int node, int initrd_node, struct meminfo *mi) */ arch_adjust_zones(node, zone_size, zhole_size); - free_area_init_node(node, pgdat, zone_size, start_pfn, zhole_size); + free_area_init_node(node, zone_size, start_pfn, zhole_size); return end_pfn; } diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 3f90a87527bb..786de88a82a7 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -129,7 +129,7 @@ void __init paging_init(void) printk("Node %u: start_pfn = 0x%lx, low = 0x%lx\n", nid, start_pfn, low); - free_area_init_node(nid, pgdat, zones_size, start_pfn, NULL); + free_area_init_node(nid, zones_size, start_pfn, NULL); printk("Node %u: mem_map starts at %p\n", pgdat->node_id, pgdat->node_mem_map); diff --git a/arch/cris/arch-v10/mm/init.c b/arch/cris/arch-v10/mm/init.c index e0fcd1a9bfd5..742fd1974c2e 100644 --- a/arch/cris/arch-v10/mm/init.c +++ b/arch/cris/arch-v10/mm/init.c @@ -182,7 +182,7 @@ paging_init(void) * mem_map page array. */ - free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + free_area_init_node(0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); } /* Initialize remaps of some I/O-ports. It is important that this diff --git a/arch/cris/arch-v32/mm/init.c b/arch/cris/arch-v32/mm/init.c index 5a9ac5834647..8a34b8b74293 100644 --- a/arch/cris/arch-v32/mm/init.c +++ b/arch/cris/arch-v32/mm/init.c @@ -162,7 +162,7 @@ paging_init(void) * substantially higher than 0, like us (we start at PAGE_OFFSET). This * saves space in the mem_map page array. */ - free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + free_area_init_node(0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); mem_map = contig_page_data.node_mem_map; } diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index aa9145ef6cca..cc23934bc41e 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -147,8 +147,7 @@ unsigned long __init zone_sizes_init(void) zholes_size[ZONE_DMA] = mp->holes; holes += zholes_size[ZONE_DMA]; - free_area_init_node(nid, NODE_DATA(nid), zones_size, - start_pfn, zholes_size); + free_area_init_node(nid, zones_size, start_pfn, zholes_size); } /* diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index bbd97c85bc5d..28799af15e95 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -123,7 +123,7 @@ unsigned long __init zone_sizes_init(void) start_pfn = __MEMORY_START >> PAGE_SHIFT; #endif /* CONFIG_MMU */ - free_area_init_node(0, NODE_DATA(0), zones_size, start_pfn, 0); + free_area_init_node(0, zones_size, start_pfn, 0); return 0; } diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c index 226795bdf355..c5dbb9bdb322 100644 --- a/arch/m68k/mm/motorola.c +++ b/arch/m68k/mm/motorola.c @@ -296,7 +296,7 @@ void __init paging_init(void) #endif for (i = 0; i < m68k_num_memory; i++) { zones_size[ZONE_DMA] = m68k_memory[i].size >> PAGE_SHIFT; - free_area_init_node(i, pg_data_map + i, zones_size, + free_area_init_node(i, zones_size, m68k_memory[i].addr >> PAGE_SHIFT, NULL); } } diff --git a/arch/m68k/mm/sun3mmu.c b/arch/m68k/mm/sun3mmu.c index edceefc18870..1b902dbd4376 100644 --- a/arch/m68k/mm/sun3mmu.c +++ b/arch/m68k/mm/sun3mmu.c @@ -94,7 +94,7 @@ void __init paging_init(void) /* I really wish I knew why the following change made things better... -- Sam */ /* free_area_init(zones_size); */ - free_area_init_node(0, NODE_DATA(0), zones_size, + free_area_init_node(0, zones_size, (__pa(PAGE_OFFSET) >> PAGE_SHIFT) + 1, NULL); diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 0ddf4904640a..7c155c254e72 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -887,7 +887,7 @@ void __init paging_init(void) } #endif - free_area_init_node(i, NODE_DATA(i), zones_size, + free_area_init_node(i, zones_size, pmem_ranges[i].start_pfn, NULL); } } diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index c624e04ff03e..ee30462598fc 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1352,8 +1352,7 @@ void __init srmmu_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, zones_size, - pfn_base, zholes_size); + free_area_init_node(0, zones_size, pfn_base, zholes_size); } } diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 2375fe9dc312..d1782f6368be 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -2123,8 +2123,7 @@ void __init sun4c_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, zones_size, - pfn_base, zholes_size); + free_area_init_node(0, zones_size, pfn_base, zholes_size); } cnt = 0; diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c index a0a8456a8430..10335cecf7bd 100644 --- a/arch/v850/kernel/setup.c +++ b/arch/v850/kernel/setup.c @@ -295,8 +295,7 @@ init_mem_alloc (unsigned long ram_start, unsigned long ram_len) #error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it) #endif NODE_DATA(0)->node_mem_map = NULL; - free_area_init_node (0, NODE_DATA(0), zones_size, - ADDR_TO_PAGE (PAGE_OFFSET), 0); + free_area_init_node(0, zones_size, ADDR_TO_PAGE (PAGE_OFFSET), 0); } diff --git a/include/linux/mm.h b/include/linux/mm.h index f8071097302a..196924b657bc 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -962,9 +962,8 @@ static inline void pgtable_page_dtor(struct page *page) NULL: pte_offset_kernel(pmd, address)) extern void free_area_init(unsigned long * zones_size); -extern void free_area_init_node(int nid, pg_data_t *pgdat, - unsigned long * zones_size, unsigned long zone_start_pfn, - unsigned long *zholes_size); +extern void free_area_init_node(int nid, unsigned long * zones_size, + unsigned long zone_start_pfn, unsigned long *zholes_size); #ifdef CONFIG_ARCH_POPULATES_NODE_MAP /* * With CONFIG_ARCH_POPULATES_NODE_MAP set, an architecture may initialise its diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 833f854eabe5..6e26adc08f14 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -455,7 +455,7 @@ static pg_data_t *hotadd_new_pgdat(int nid, u64 start) /* we can use NODE_DATA(nid) from here */ /* init node's zones as empty zones, we don't have any present pages.*/ - free_area_init_node(nid, pgdat, zones_size, start_pfn, zholes_size); + free_area_init_node(nid, zones_size, start_pfn, zholes_size); return pgdat; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 24aa3d1b9d96..e43aae135b38 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3461,10 +3461,11 @@ static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat) #endif /* CONFIG_FLAT_NODE_MEM_MAP */ } -void __paginginit free_area_init_node(int nid, struct pglist_data *pgdat, - unsigned long *zones_size, unsigned long node_start_pfn, - unsigned long *zholes_size) +void __paginginit free_area_init_node(int nid, unsigned long *zones_size, + unsigned long node_start_pfn, unsigned long *zholes_size) { + pg_data_t *pgdat = NODE_DATA(nid); + pgdat->node_id = nid; pgdat->node_start_pfn = node_start_pfn; calculate_node_totalpages(pgdat, zones_size, zholes_size); @@ -3961,7 +3962,7 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn) setup_nr_node_ids(); for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); - free_area_init_node(nid, pgdat, NULL, + free_area_init_node(nid, NULL, find_min_pfn_for_node(nid), NULL); /* Any memory on that node */ @@ -4032,7 +4033,7 @@ EXPORT_SYMBOL(contig_page_data); void __init free_area_init(unsigned long *zones_size) { - free_area_init_node(0, NODE_DATA(0), zones_size, + free_area_init_node(0, zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL); } -- cgit v1.2.3 From a1e78772d72b2616ed20e54896e68e0e7044854e Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 23 Jul 2008 21:27:23 -0700 Subject: hugetlb: reserve huge pages for reliable MAP_PRIVATE hugetlbfs mappings until fork() This patch reserves huge pages at mmap() time for MAP_PRIVATE mappings in a similar manner to the reservations taken for MAP_SHARED mappings. The reserve count is accounted both globally and on a per-VMA basis for private mappings. This guarantees that a process that successfully calls mmap() will successfully fault all pages in the future unless fork() is called. The characteristics of private mappings of hugetlbfs files behaviour after this patch are; 1. The process calling mmap() is guaranteed to succeed all future faults until it forks(). 2. On fork(), the parent may die due to SIGKILL on writes to the private mapping if enough pages are not available for the COW. For reasonably reliable behaviour in the face of a small huge page pool, children of hugepage-aware processes should not reference the mappings; such as might occur when fork()ing to exec(). 3. On fork(), the child VMAs inherit no reserves. Reads on pages already faulted by the parent will succeed. Successful writes will depend on enough huge pages being free in the pool. 4. Quotas of the hugetlbfs mount are checked at reserve time for the mapper and at fault time otherwise. Before this patch, all reads or writes in the child potentially needs page allocations that can later lead to the death of the parent. This applies to reads and writes of uninstantiated pages as well as COW. After the patch it is only a write to an instantiated page that causes problems. Signed-off-by: Mel Gorman Acked-by: Adam Litke Cc: Andy Whitcroft Cc: William Lee Irwin III Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 8 +-- include/linux/hugetlb.h | 9 ++- kernel/fork.c | 9 +++ mm/hugetlb.c | 158 ++++++++++++++++++++++++++++++++++++------------ 4 files changed, 140 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index aeabf80f81a5..1576bbecd084 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -103,9 +103,9 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) ret = -ENOMEM; len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); - if (vma->vm_flags & VM_MAYSHARE && - hugetlb_reserve_pages(inode, vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), - len >> HPAGE_SHIFT)) + if (hugetlb_reserve_pages(inode, + vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), + len >> HPAGE_SHIFT, vma)) goto out; ret = 0; @@ -942,7 +942,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size) goto out_dentry; error = -ENOMEM; - if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT)) + if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT, NULL)) goto out_inode; d_instantiate(dentry, inode); diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index a79e80b689d8..185b14c9f021 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -17,6 +17,7 @@ static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) return vma->vm_flags & VM_HUGETLB; } +void reset_vma_resv_huge_pages(struct vm_area_struct *vma); int hugetlb_sysctl_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); int hugetlb_overcommit_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); int hugetlb_treat_movable_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); @@ -30,7 +31,8 @@ int hugetlb_report_node_meminfo(int, char *); unsigned long hugetlb_total_pages(void); int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, int write_access); -int hugetlb_reserve_pages(struct inode *inode, long from, long to); +int hugetlb_reserve_pages(struct inode *inode, long from, long to, + struct vm_area_struct *vma); void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); extern unsigned long max_huge_pages; @@ -58,6 +60,11 @@ static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) { return 0; } + +static inline void reset_vma_resv_huge_pages(struct vm_area_struct *vma) +{ +} + static inline unsigned long hugetlb_total_pages(void) { return 0; diff --git a/kernel/fork.c b/kernel/fork.c index adefc1131f27..552c8d8e77ad 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -306,6 +307,14 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) spin_unlock(&file->f_mapping->i_mmap_lock); } + /* + * Clear hugetlb-related page reserves for children. This only + * affects MAP_PRIVATE mappings. Faults generated by the child + * are not guaranteed to succeed, even if read-only + */ + if (is_vm_hugetlb_page(tmp)) + reset_vma_resv_huge_pages(tmp); + /* * Link in the new vma and copy the page table entries. */ diff --git a/mm/hugetlb.c b/mm/hugetlb.c index a4dbba8965f3..0af500db3632 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -40,6 +40,69 @@ static int hugetlb_next_nid; */ static DEFINE_SPINLOCK(hugetlb_lock); +/* + * These helpers are used to track how many pages are reserved for + * faults in a MAP_PRIVATE mapping. Only the process that called mmap() + * is guaranteed to have their future faults succeed. + * + * With the exception of reset_vma_resv_huge_pages() which is called at fork(), + * the reserve counters are updated with the hugetlb_lock held. It is safe + * to reset the VMA at fork() time as it is not in use yet and there is no + * chance of the global counters getting corrupted as a result of the values. + */ +static unsigned long vma_resv_huge_pages(struct vm_area_struct *vma) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + if (!(vma->vm_flags & VM_SHARED)) + return (unsigned long)vma->vm_private_data; + return 0; +} + +static void set_vma_resv_huge_pages(struct vm_area_struct *vma, + unsigned long reserve) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + VM_BUG_ON(vma->vm_flags & VM_SHARED); + + vma->vm_private_data = (void *)reserve; +} + +/* Decrement the reserved pages in the hugepage pool by one */ +static void decrement_hugepage_resv_vma(struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_SHARED) { + /* Shared mappings always use reserves */ + resv_huge_pages--; + } else { + /* + * Only the process that called mmap() has reserves for + * private mappings. + */ + if (vma_resv_huge_pages(vma)) { + resv_huge_pages--; + reserve = (unsigned long)vma->vm_private_data - 1; + vma->vm_private_data = (void *)reserve; + } + } +} + +void reset_vma_resv_huge_pages(struct vm_area_struct *vma) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + if (!(vma->vm_flags & VM_SHARED)) + vma->vm_private_data = (void *)0; +} + +/* Returns true if the VMA has associated reserve pages */ +static int vma_has_private_reserves(struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_SHARED) + return 0; + if (!vma_resv_huge_pages(vma)) + return 0; + return 1; +} + static void clear_huge_page(struct page *page, unsigned long addr) { int i; @@ -101,6 +164,15 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, struct zone *zone; struct zoneref *z; + /* + * A child process with MAP_PRIVATE mappings created by their parent + * have no page reserves. This check ensures that reservations are + * not "stolen". The child may still get SIGKILLed + */ + if (!vma_has_private_reserves(vma) && + free_huge_pages - resv_huge_pages == 0) + return NULL; + for_each_zone_zonelist_nodemask(zone, z, zonelist, MAX_NR_ZONES - 1, nodemask) { nid = zone_to_nid(zone); @@ -111,8 +183,8 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, list_del(&page->lru); free_huge_pages--; free_huge_pages_node[nid]--; - if (vma && vma->vm_flags & VM_MAYSHARE) - resv_huge_pages--; + decrement_hugepage_resv_vma(vma); + break; } } @@ -461,55 +533,40 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) } } - -static struct page *alloc_huge_page_shared(struct vm_area_struct *vma, - unsigned long addr) +static struct page *alloc_huge_page(struct vm_area_struct *vma, + unsigned long addr) { struct page *page; + struct address_space *mapping = vma->vm_file->f_mapping; + struct inode *inode = mapping->host; + unsigned int chg = 0; + + /* + * Processes that did not create the mapping will have no reserves and + * will not have accounted against quota. Check that the quota can be + * made before satisfying the allocation + */ + if (!vma_has_private_reserves(vma)) { + chg = 1; + if (hugetlb_get_quota(inode->i_mapping, chg)) + return ERR_PTR(-ENOSPC); + } spin_lock(&hugetlb_lock); page = dequeue_huge_page_vma(vma, addr); spin_unlock(&hugetlb_lock); - return page ? page : ERR_PTR(-VM_FAULT_OOM); -} -static struct page *alloc_huge_page_private(struct vm_area_struct *vma, - unsigned long addr) -{ - struct page *page = NULL; - - if (hugetlb_get_quota(vma->vm_file->f_mapping, 1)) - return ERR_PTR(-VM_FAULT_SIGBUS); - - spin_lock(&hugetlb_lock); - if (free_huge_pages > resv_huge_pages) - page = dequeue_huge_page_vma(vma, addr); - spin_unlock(&hugetlb_lock); if (!page) { page = alloc_buddy_huge_page(vma, addr); if (!page) { - hugetlb_put_quota(vma->vm_file->f_mapping, 1); + hugetlb_put_quota(inode->i_mapping, chg); return ERR_PTR(-VM_FAULT_OOM); } } - return page; -} -static struct page *alloc_huge_page(struct vm_area_struct *vma, - unsigned long addr) -{ - struct page *page; - struct address_space *mapping = vma->vm_file->f_mapping; - - if (vma->vm_flags & VM_MAYSHARE) - page = alloc_huge_page_shared(vma, addr); - else - page = alloc_huge_page_private(vma, addr); + set_page_refcounted(page); + set_page_private(page, (unsigned long) mapping); - if (!IS_ERR(page)) { - set_page_refcounted(page); - set_page_private(page, (unsigned long) mapping); - } return page; } @@ -757,6 +814,13 @@ out: return ret; } +static void hugetlb_vm_op_close(struct vm_area_struct *vma) +{ + unsigned long reserve = vma_resv_huge_pages(vma); + if (reserve) + hugetlb_acct_memory(-reserve); +} + /* * We cannot handle pagefaults against hugetlb pages at all. They cause * handle_mm_fault() to try to instantiate regular-sized pages in the @@ -771,6 +835,7 @@ static int hugetlb_vm_op_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct vm_operations_struct hugetlb_vm_ops = { .fault = hugetlb_vm_op_fault, + .close = hugetlb_vm_op_close, }; static pte_t make_huge_pte(struct vm_area_struct *vma, struct page *page, @@ -1289,11 +1354,25 @@ static long region_truncate(struct list_head *head, long end) return chg; } -int hugetlb_reserve_pages(struct inode *inode, long from, long to) +int hugetlb_reserve_pages(struct inode *inode, + long from, long to, + struct vm_area_struct *vma) { long ret, chg; - chg = region_chg(&inode->i_mapping->private_list, from, to); + /* + * Shared mappings base their reservation on the number of pages that + * are already allocated on behalf of the file. Private mappings need + * to reserve the full area even if read-only as mprotect() may be + * called to make the mapping read-write. Assume !vma is a shm mapping + */ + if (!vma || vma->vm_flags & VM_SHARED) + chg = region_chg(&inode->i_mapping->private_list, from, to); + else { + chg = to - from; + set_vma_resv_huge_pages(vma, chg); + } + if (chg < 0) return chg; @@ -1304,7 +1383,8 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to) hugetlb_put_quota(inode->i_mapping, chg); return ret; } - region_add(&inode->i_mapping->private_list, from, to); + if (!vma || vma->vm_flags & VM_SHARED) + region_add(&inode->i_mapping->private_list, from, to); return 0; } -- cgit v1.2.3 From 04f2cbe35699d22dbf428373682ead85ca1240f5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 23 Jul 2008 21:27:25 -0700 Subject: hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed After patch 2 in this series, a process that successfully calls mmap() for a MAP_PRIVATE mapping will be guaranteed to successfully fault until a process calls fork(). At that point, the next write fault from the parent could fail due to COW if the child still has a reference. We only reserve pages for the parent but a copy must be made to avoid leaking data from the parent to the child after fork(). Reserves could be taken for both parent and child at fork time to guarantee faults but if the mapping is large it is highly likely we will not have sufficient pages for the reservation, and it is common to fork only to exec() immediatly after. A failure here would be very undesirable. Note that the current behaviour of mainline with MAP_PRIVATE pages is pretty bad. The following situation is allowed to occur today. 1. Process calls mmap(MAP_PRIVATE) 2. Process calls mlock() to fault all pages and makes sure it succeeds 3. Process forks() 4. Process writes to MAP_PRIVATE mapping while child still exists 5. If the COW fails at this point, the process gets SIGKILLed even though it had taken care to ensure the pages existed This patch improves the situation by guaranteeing the reliability of the process that successfully calls mmap(). When the parent performs COW, it will try to satisfy the allocation without using reserves. If that fails the parent will steal the page leaving any children without a page. Faults from the child after that point will result in failure. If the child COW happens first, an attempt will be made to allocate the page without reserves and the child will get SIGKILLed on failure. To summarise the new behaviour: 1. If the original mapper performs COW on a private mapping with multiple references, it will attempt to allocate a hugepage from the pool or the buddy allocator without using the existing reserves. On fail, VMAs mapping the same area are traversed and the page being COW'd is unmapped where found. It will then steal the original page as the last mapper in the normal way. 2. The VMAs the pages were unmapped from are flagged to note that pages with data no longer exist. Future no-page faults on those VMAs will terminate the process as otherwise it would appear that data was corrupted. A warning is printed to the console that this situation occured. 2. If the child performs COW first, it will attempt to satisfy the COW from the pool if there are enough pages or via the buddy allocator if overcommit is allowed and the buddy allocator can satisfy the request. If it fails, the child will be killed. If the pool is large enough, existing applications will not notice that the reserves were a factor. Existing applications depending on the no-reserves been set are unlikely to exist as for much of the history of hugetlbfs, pages were prefaulted at mmap(), allocating the pages at that point or failing the mmap(). [npiggin@suse.de: fix CONFIG_HUGETLB=n build] Signed-off-by: Mel Gorman Acked-by: Adam Litke Cc: Andy Whitcroft Cc: William Lee Irwin III Cc: Hugh Dickins Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 2 +- include/linux/hugetlb.h | 8 +- mm/hugetlb.c | 201 +++++++++++++++++++++++++++++++++++++++++++----- mm/memory.c | 2 +- 4 files changed, 190 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 1576bbecd084..428eff5b73f3 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -441,7 +441,7 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff) v_offset = 0; __unmap_hugepage_range(vma, - vma->vm_start + v_offset, vma->vm_end); + vma->vm_start + v_offset, vma->vm_end, NULL); } } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 185b14c9f021..abbc187193a1 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -23,8 +23,10 @@ int hugetlb_overcommit_handler(struct ctl_table *, int, struct file *, void __us int hugetlb_treat_movable_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *); int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int, int); -void unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long); -void __unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long); +void unmap_hugepage_range(struct vm_area_struct *, + unsigned long, unsigned long, struct page *); +void __unmap_hugepage_range(struct vm_area_struct *, + unsigned long, unsigned long, struct page *); int hugetlb_prefault(struct address_space *, struct vm_area_struct *); int hugetlb_report_meminfo(char *); int hugetlb_report_node_meminfo(int, char *); @@ -74,7 +76,7 @@ static inline unsigned long hugetlb_total_pages(void) #define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL) #define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; }) #define hugetlb_prefault(mapping, vma) ({ BUG(); 0; }) -#define unmap_hugepage_range(vma, start, end) BUG() +#define unmap_hugepage_range(vma, start, end, page) BUG() #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0af500db3632..a2d29b84501f 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -40,6 +40,9 @@ static int hugetlb_next_nid; */ static DEFINE_SPINLOCK(hugetlb_lock); +#define HPAGE_RESV_OWNER (1UL << (BITS_PER_LONG - 1)) +#define HPAGE_RESV_UNMAPPED (1UL << (BITS_PER_LONG - 2)) +#define HPAGE_RESV_MASK (HPAGE_RESV_OWNER | HPAGE_RESV_UNMAPPED) /* * These helpers are used to track how many pages are reserved for * faults in a MAP_PRIVATE mapping. Only the process that called mmap() @@ -54,17 +57,32 @@ static unsigned long vma_resv_huge_pages(struct vm_area_struct *vma) { VM_BUG_ON(!is_vm_hugetlb_page(vma)); if (!(vma->vm_flags & VM_SHARED)) - return (unsigned long)vma->vm_private_data; + return (unsigned long)vma->vm_private_data & ~HPAGE_RESV_MASK; return 0; } static void set_vma_resv_huge_pages(struct vm_area_struct *vma, unsigned long reserve) { + unsigned long flags; VM_BUG_ON(!is_vm_hugetlb_page(vma)); VM_BUG_ON(vma->vm_flags & VM_SHARED); - vma->vm_private_data = (void *)reserve; + flags = (unsigned long)vma->vm_private_data & HPAGE_RESV_MASK; + vma->vm_private_data = (void *)(reserve | flags); +} + +static void set_vma_resv_flags(struct vm_area_struct *vma, unsigned long flags) +{ + unsigned long reserveflags = (unsigned long)vma->vm_private_data; + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + vma->vm_private_data = (void *)(reserveflags | flags); +} + +static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + return ((unsigned long)vma->vm_private_data & flag) != 0; } /* Decrement the reserved pages in the hugepage pool by one */ @@ -78,14 +96,18 @@ static void decrement_hugepage_resv_vma(struct vm_area_struct *vma) * Only the process that called mmap() has reserves for * private mappings. */ - if (vma_resv_huge_pages(vma)) { + if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { + unsigned long flags, reserve; resv_huge_pages--; + flags = (unsigned long)vma->vm_private_data & + HPAGE_RESV_MASK; reserve = (unsigned long)vma->vm_private_data - 1; - vma->vm_private_data = (void *)reserve; + vma->vm_private_data = (void *)(reserve | flags); } } } +/* Reset counters to 0 and clear all HPAGE_RESV_* flags */ void reset_vma_resv_huge_pages(struct vm_area_struct *vma) { VM_BUG_ON(!is_vm_hugetlb_page(vma)); @@ -153,7 +175,7 @@ static struct page *dequeue_huge_page(void) } static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, - unsigned long address) + unsigned long address, int avoid_reserve) { int nid; struct page *page = NULL; @@ -173,6 +195,10 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, free_huge_pages - resv_huge_pages == 0) return NULL; + /* If reserves cannot be used, ensure enough pages are in the pool */ + if (avoid_reserve && free_huge_pages - resv_huge_pages == 0) + return NULL; + for_each_zone_zonelist_nodemask(zone, z, zonelist, MAX_NR_ZONES - 1, nodemask) { nid = zone_to_nid(zone); @@ -183,7 +209,9 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, list_del(&page->lru); free_huge_pages--; free_huge_pages_node[nid]--; - decrement_hugepage_resv_vma(vma); + + if (!avoid_reserve) + decrement_hugepage_resv_vma(vma); break; } @@ -534,7 +562,7 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) } static struct page *alloc_huge_page(struct vm_area_struct *vma, - unsigned long addr) + unsigned long addr, int avoid_reserve) { struct page *page; struct address_space *mapping = vma->vm_file->f_mapping; @@ -546,14 +574,15 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, * will not have accounted against quota. Check that the quota can be * made before satisfying the allocation */ - if (!vma_has_private_reserves(vma)) { + if (!(vma->vm_flags & VM_SHARED) && + !is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { chg = 1; if (hugetlb_get_quota(inode->i_mapping, chg)) return ERR_PTR(-ENOSPC); } spin_lock(&hugetlb_lock); - page = dequeue_huge_page_vma(vma, addr); + page = dequeue_huge_page_vma(vma, addr, avoid_reserve); spin_unlock(&hugetlb_lock); if (!page) { @@ -909,7 +938,7 @@ nomem: } void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) + unsigned long end, struct page *ref_page) { struct mm_struct *mm = vma->vm_mm; unsigned long address; @@ -937,6 +966,27 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, if (huge_pmd_unshare(mm, &address, ptep)) continue; + /* + * If a reference page is supplied, it is because a specific + * page is being unmapped, not a range. Ensure the page we + * are about to unmap is the actual page of interest. + */ + if (ref_page) { + pte = huge_ptep_get(ptep); + if (huge_pte_none(pte)) + continue; + page = pte_page(pte); + if (page != ref_page) + continue; + + /* + * Mark the VMA as having unmapped its page so that + * future faults in this VMA will fail rather than + * looking like data was lost + */ + set_vma_resv_flags(vma, HPAGE_RESV_UNMAPPED); + } + pte = huge_ptep_get_and_clear(mm, address, ptep); if (huge_pte_none(pte)) continue; @@ -955,7 +1005,7 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, } void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) + unsigned long end, struct page *ref_page) { /* * It is undesirable to test vma->vm_file as it should be non-null @@ -967,19 +1017,68 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, */ if (vma->vm_file) { spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); - __unmap_hugepage_range(vma, start, end); + __unmap_hugepage_range(vma, start, end, ref_page); spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock); } } +/* + * This is called when the original mapper is failing to COW a MAP_PRIVATE + * mappping it owns the reserve page for. The intention is to unmap the page + * from other VMAs and let the children be SIGKILLed if they are faulting the + * same region. + */ +int unmap_ref_private(struct mm_struct *mm, + struct vm_area_struct *vma, + struct page *page, + unsigned long address) +{ + struct vm_area_struct *iter_vma; + struct address_space *mapping; + struct prio_tree_iter iter; + pgoff_t pgoff; + + /* + * vm_pgoff is in PAGE_SIZE units, hence the different calculation + * from page cache lookup which is in HPAGE_SIZE units. + */ + address = address & huge_page_mask(hstate_vma(vma)); + pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + + (vma->vm_pgoff >> PAGE_SHIFT); + mapping = (struct address_space *)page_private(page); + + vma_prio_tree_foreach(iter_vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + /* Do not unmap the current VMA */ + if (iter_vma == vma) + continue; + + /* + * Unmap the page from other VMAs without their own reserves. + * They get marked to be SIGKILLed if they fault in these + * areas. This is because a future no-page fault on this VMA + * could insert a zeroed page instead of the data existing + * from the time of fork. This would look like data corruption + */ + if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) + unmap_hugepage_range(iter_vma, + address, address + HPAGE_SIZE, + page); + } + + return 1; +} + static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long address, pte_t *ptep, pte_t pte) + unsigned long address, pte_t *ptep, pte_t pte, + struct page *pagecache_page) { struct page *old_page, *new_page; int avoidcopy; + int outside_reserve = 0; old_page = pte_page(pte); +retry_avoidcopy: /* If no-one else is actually using this page, avoid the copy * and just make the page writable */ avoidcopy = (page_count(old_page) == 1); @@ -988,11 +1087,43 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } + /* + * If the process that created a MAP_PRIVATE mapping is about to + * perform a COW due to a shared page count, attempt to satisfy + * the allocation without using the existing reserves. The pagecache + * page is used to determine if the reserve at this address was + * consumed or not. If reserves were used, a partial faulted mapping + * at the time of fork() could consume its reserves on COW instead + * of the full address range. + */ + if (!(vma->vm_flags & VM_SHARED) && + is_vma_resv_set(vma, HPAGE_RESV_OWNER) && + old_page != pagecache_page) + outside_reserve = 1; + page_cache_get(old_page); - new_page = alloc_huge_page(vma, address); + new_page = alloc_huge_page(vma, address, outside_reserve); if (IS_ERR(new_page)) { page_cache_release(old_page); + + /* + * If a process owning a MAP_PRIVATE mapping fails to COW, + * it is due to references held by a child and an insufficient + * huge page pool. To guarantee the original mappers + * reliability, unmap the page from child processes. The child + * may get SIGKILLed if it later faults. + */ + if (outside_reserve) { + BUG_ON(huge_pte_none(pte)); + if (unmap_ref_private(mm, vma, old_page, address)) { + BUG_ON(page_count(old_page) != 1); + BUG_ON(huge_pte_none(pte)); + goto retry_avoidcopy; + } + WARN_ON_ONCE(1); + } + return -PTR_ERR(new_page); } @@ -1015,6 +1146,20 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } +/* Return the pagecache page at a given address within a VMA */ +static struct page *hugetlbfs_pagecache_page(struct vm_area_struct *vma, + unsigned long address) +{ + struct address_space *mapping; + unsigned long idx; + + mapping = vma->vm_file->f_mapping; + idx = ((address - vma->vm_start) >> HPAGE_SHIFT) + + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); + + return find_lock_page(mapping, idx); +} + static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *ptep, int write_access) { @@ -1025,6 +1170,18 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, struct address_space *mapping; pte_t new_pte; + /* + * Currently, we are forced to kill the process in the event the + * original mapper has unmapped pages from the child due to a failed + * COW. Warn that such a situation has occured as it may not be obvious + */ + if (is_vma_resv_set(vma, HPAGE_RESV_UNMAPPED)) { + printk(KERN_WARNING + "PID %d killed due to inadequate hugepage pool\n", + current->pid); + return ret; + } + mapping = vma->vm_file->f_mapping; idx = ((address - vma->vm_start) >> HPAGE_SHIFT) + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); @@ -1039,7 +1196,7 @@ retry: size = i_size_read(mapping->host) >> HPAGE_SHIFT; if (idx >= size) goto out; - page = alloc_huge_page(vma, address); + page = alloc_huge_page(vma, address, 0); if (IS_ERR(page)) { ret = -PTR_ERR(page); goto out; @@ -1081,7 +1238,7 @@ retry: if (write_access && !(vma->vm_flags & VM_SHARED)) { /* Optimization, do the COW without a second fault */ - ret = hugetlb_cow(mm, vma, address, ptep, new_pte); + ret = hugetlb_cow(mm, vma, address, ptep, new_pte, page); } spin_unlock(&mm->page_table_lock); @@ -1126,8 +1283,15 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, spin_lock(&mm->page_table_lock); /* Check for a racing update before calling hugetlb_cow */ if (likely(pte_same(entry, huge_ptep_get(ptep)))) - if (write_access && !pte_write(entry)) - ret = hugetlb_cow(mm, vma, address, ptep, entry); + if (write_access && !pte_write(entry)) { + struct page *page; + page = hugetlbfs_pagecache_page(vma, address); + ret = hugetlb_cow(mm, vma, address, ptep, entry, page); + if (page) { + unlock_page(page); + put_page(page); + } + } spin_unlock(&mm->page_table_lock); mutex_unlock(&hugetlb_instantiation_mutex); @@ -1371,6 +1535,7 @@ int hugetlb_reserve_pages(struct inode *inode, else { chg = to - from; set_vma_resv_huge_pages(vma, chg); + set_vma_resv_flags(vma, HPAGE_RESV_OWNER); } if (chg < 0) diff --git a/mm/memory.c b/mm/memory.c index 82f3f1c5cf17..72932489a082 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -901,7 +901,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, } if (unlikely(is_vm_hugetlb_page(vma))) { - unmap_hugepage_range(vma, start, end); + unmap_hugepage_range(vma, start, end, NULL); zap_work -= (end - start) / (HPAGE_SIZE / PAGE_SIZE); start = end; -- cgit v1.2.3 From cdfd4325c0d878679bd6a3ba8285b71d9980e3c0 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:28 -0700 Subject: mm: record MAP_NORESERVE status on vmas and fix small page mprotect reservations With Mel's hugetlb private reservation support patches applied, strict overcommit semantics are applied to both shared and private huge page mappings. This can be a problem if an application relied on unlimited overcommit semantics for private mappings. An example of this would be an application which maps a huge area with the intention of using it very sparsely. These application would benefit from being able to opt-out of the strict overcommit. It should be noted that prior to hugetlb supporting demand faulting all mappings were fully populated and so applications of this type should be rare. This patch stack implements the MAP_NORESERVE mmap() flag for huge page mappings. This flag has the same meaning as for small page mappings, suppressing reservations for that mapping. Thanks to Mel Gorman for reviewing a number of early versions of these patches. This patch: When a small page mapping is created with mmap() reservations are created by default for any memory pages required. When the region is read/write the reservation is increased for every page, no reservation is needed for read-only regions (as they implicitly share the zero page). Reservations are tracked via the VM_ACCOUNT vma flag which is present when the region has reservation backing it. When we convert a region from read-only to read-write new reservations are aquired and VM_ACCOUNT is set. However, when a read-only map is created with MAP_NORESERVE it is indistinguishable from a normal mapping. When we then convert that to read/write we are forced to incorrectly create reservations for it as we have no record of the original MAP_NORESERVE. This patch introduces a new vma flag VM_NORESERVE which records the presence of the original MAP_NORESERVE flag. This allows us to distinguish these two circumstances and correctly account the reserve. As well as fixing this FIXME in the code, this makes it much easier to introduce MAP_NORESERVE support for huge pages as this flag is available consistantly for the life of the mapping. VM_ACCOUNT on the other hand is heavily used at the generic level in association with small pages. Signed-off-by: Andy Whitcroft Cc: Mel Gorman Cc: Adam Litke Cc: Johannes Weiner Cc: Andy Whitcroft Cc: William Lee Irwin III Cc: Hugh Dickins Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 + mm/mmap.c | 3 +++ mm/mprotect.c | 6 ++---- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 196924b657bc..df322fb4df31 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -100,6 +100,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */ #define VM_RESERVED 0x00080000 /* Count as reserved_vm like IO */ #define VM_ACCOUNT 0x00100000 /* Is a VM accounted object */ +#define VM_NORESERVE 0x00200000 /* should the VM suppress accounting */ #define VM_HUGETLB 0x00400000 /* Huge TLB Page VM */ #define VM_NONLINEAR 0x00800000 /* Is non-linear (remap_file_pages) */ #define VM_MAPPED_COPY 0x01000000 /* T if mapped copy of data (nommu mmap) */ diff --git a/mm/mmap.c b/mm/mmap.c index 75e0d0673d78..57d3b6097deb 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1110,6 +1110,9 @@ munmap_back: if (!may_expand_vm(mm, len >> PAGE_SHIFT)) return -ENOMEM; + if (flags & MAP_NORESERVE) + vm_flags |= VM_NORESERVE; + if (accountable && (!(flags & MAP_NORESERVE) || sysctl_overcommit_memory == OVERCOMMIT_NEVER)) { if (vm_flags & VM_SHARED) { diff --git a/mm/mprotect.c b/mm/mprotect.c index 360d9cc8b38c..abd645a3b0a0 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -153,12 +153,10 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, * If we make a private mapping writable we increase our commit; * but (without finer accounting) cannot reduce our commit if we * make it unwritable again. - * - * FIXME? We haven't defined a VM_NORESERVE flag, so mprotecting - * a MAP_NORESERVE private mapping to writable will now reserve. */ if (newflags & VM_WRITE) { - if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_SHARED))) { + if (!(oldflags & (VM_ACCOUNT|VM_WRITE| + VM_SHARED|VM_NORESERVE))) { charged = nrpages; if (security_vm_enough_memory(charged)) return -ENOMEM; -- cgit v1.2.3 From ff7ea79cf7c3a481851bd4b2185fdeb6ce4afa29 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 23 Jul 2008 21:27:39 -0700 Subject: mm: create /sys/kernel/mm Add a kobject to create /sys/kernel/mm when sysfs is mounted. The kobject will exist regardless. This will allow for the hugepage related sysfs directories to exist under the mm "subsystem" directory. Add an ABI file appropriately. [kosaki.motohiro@jp.fujitsu.com: fix build] Signed-off-by: Nishanth Aravamudan Cc: Nick Piggin Cc: Mel Gorman Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-kernel-mm | 6 ++++++ include/linux/kobject.h | 2 ++ mm/mm_init.c | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-kernel-mm (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-kernel-mm b/Documentation/ABI/testing/sysfs-kernel-mm new file mode 100644 index 000000000000..190d523ac159 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-mm @@ -0,0 +1,6 @@ +What: /sys/kernel/mm +Date: July 2008 +Contact: Nishanth Aravamudan , VM maintainers +Description: + /sys/kernel/mm/ should contain any and all VM + related information in /sys/kernel/. diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 60f0d418ae32..5437ac0276e2 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -186,6 +186,8 @@ extern struct kobject *kset_find_obj(struct kset *, const char *); /* The global /sys/kernel/ kobject for people to chain off of */ extern struct kobject *kernel_kobj; +/* The global /sys/kernel/mm/ kobject for people to chain off of */ +extern struct kobject *mm_kobj; /* The global /sys/hypervisor/ kobject for people to chain off of */ extern struct kobject *hypervisor_kobj; /* The global /sys/power/ kobject for people to chain off of */ diff --git a/mm/mm_init.c b/mm/mm_init.c index eaf0d3b47099..c6af41ea9994 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -7,6 +7,8 @@ */ #include #include +#include +#include #include "internal.h" #ifdef CONFIG_DEBUG_MEMORY_INIT @@ -134,3 +136,17 @@ static __init int set_mminit_loglevel(char *str) } early_param("mminit_loglevel", set_mminit_loglevel); #endif /* CONFIG_DEBUG_MEMORY_INIT */ + +struct kobject *mm_kobj; +EXPORT_SYMBOL_GPL(mm_kobj); + +static int __init mm_sysfs_init(void) +{ + mm_kobj = kobject_create_and_add("mm", kernel_kobj); + if (!mm_kobj) + return -ENOMEM; + + return 0; +} + +__initcall(mm_sysfs_init); -- cgit v1.2.3 From a5516438959d90b071ff0a484ce4f3f523dc3152 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:41 -0700 Subject: hugetlb: modular state for hugetlb page size The goal of this patchset is to support multiple hugetlb page sizes. This is achieved by introducing a new struct hstate structure, which encapsulates the important hugetlb state and constants (eg. huge page size, number of huge pages currently allocated, etc). The hstate structure is then passed around the code which requires these fields, they will do the right thing regardless of the exact hstate they are operating on. This patch adds the hstate structure, with a single global instance of it (default_hstate), and does the basic work of converting hugetlb to use the hstate. Future patches will add more hstate structures to allow for different hugetlbfs mounts to have different page sizes. [akpm@linux-foundation.org: coding-style fixes] Acked-by: Adam Litke Acked-by: Nishanth Aravamudan Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/hugetlbpage.c | 7 +- arch/powerpc/mm/hugetlbpage.c | 3 +- arch/s390/mm/hugetlbpage.c | 3 +- arch/sh/mm/hugetlbpage.c | 3 +- arch/sparc64/mm/hugetlbpage.c | 5 +- arch/x86/mm/hugetlbpage.c | 5 +- fs/hugetlbfs/inode.c | 52 +++--- include/asm-ia64/hugetlb.h | 3 +- include/asm-powerpc/hugetlb.h | 3 +- include/asm-s390/hugetlb.h | 3 +- include/asm-sh/hugetlb.h | 3 +- include/asm-sparc/hugetlb.h | 3 +- include/asm-x86/hugetlb.h | 8 +- include/linux/hugetlb.h | 88 +++++++++- ipc/shm.c | 3 +- mm/hugetlb.c | 368 +++++++++++++++++++++++------------------- mm/memory.c | 2 +- mm/mempolicy.c | 9 +- mm/mmap.c | 3 +- 19 files changed, 356 insertions(+), 218 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index cd49e2860eef..6170f097d255 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -24,7 +24,7 @@ unsigned int hpage_shift=HPAGE_SHIFT_DEFAULT; pte_t * -huge_pte_alloc (struct mm_struct *mm, unsigned long addr) +huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz) { unsigned long taddr = htlbpage_to_page(addr); pgd_t *pgd; @@ -75,7 +75,8 @@ int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) * Don't actually need to do any preparation, but need to make sure * the address is in the right region. */ -int prepare_hugepage_range(unsigned long addr, unsigned long len) +int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; @@ -149,7 +150,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, u /* Handle MAP_FIXED */ if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 1a96cc891cf5..c94dc71af989 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -128,7 +128,8 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) return NULL; } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pg; pud_t *pu; diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index f4b6124fdb75..9162dc84f77f 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -72,7 +72,8 @@ void arch_release_hugepage(struct page *page) page[1].index = 0; } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgdp; pud_t *pudp; diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index ae8c321d6e2a..2f9dbe0ef4ac 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c @@ -22,7 +22,8 @@ #include #include -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; pud_t *pud; diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index ebefd2a14375..1307b23f6a76 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -175,7 +175,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, return -ENOMEM; if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } @@ -195,7 +195,8 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, pgoff, flags); } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; pud_t *pud; diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 0b3d567e686d..52476fde8996 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -124,7 +124,8 @@ int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) return 1; } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; pud_t *pud; @@ -368,7 +369,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, return -ENOMEM; if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 428eff5b73f3..516c581b5371 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -80,6 +80,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) struct inode *inode = file->f_path.dentry->d_inode; loff_t len, vma_len; int ret; + struct hstate *h = hstate_file(file); /* * vma address alignment (but not the pgoff alignment) has @@ -92,7 +93,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_flags |= VM_HUGETLB | VM_RESERVED; vma->vm_ops = &hugetlb_vm_ops; - if (vma->vm_pgoff & ~(HPAGE_MASK >> PAGE_SHIFT)) + if (vma->vm_pgoff & ~(huge_page_mask(h) >> PAGE_SHIFT)) return -EINVAL; vma_len = (loff_t)(vma->vm_end - vma->vm_start); @@ -104,8 +105,8 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); if (hugetlb_reserve_pages(inode, - vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), - len >> HPAGE_SHIFT, vma)) + vma->vm_pgoff >> huge_page_order(h), + len >> huge_page_shift(h), vma)) goto out; ret = 0; @@ -130,20 +131,21 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, struct mm_struct *mm = current->mm; struct vm_area_struct *vma; unsigned long start_addr; + struct hstate *h = hstate_file(file); - if (len & ~HPAGE_MASK) + if (len & ~huge_page_mask(h)) return -EINVAL; if (len > TASK_SIZE) return -ENOMEM; if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } if (addr) { - addr = ALIGN(addr, HPAGE_SIZE); + addr = ALIGN(addr, huge_page_size(h)); vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && (!vma || addr + len <= vma->vm_start)) @@ -156,7 +158,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, start_addr = TASK_UNMAPPED_BASE; full_search: - addr = ALIGN(start_addr, HPAGE_SIZE); + addr = ALIGN(start_addr, huge_page_size(h)); for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { /* At this point: (!vma || addr < vma->vm_end). */ @@ -174,7 +176,7 @@ full_search: if (!vma || addr + len <= vma->vm_start) return addr; - addr = ALIGN(vma->vm_end, HPAGE_SIZE); + addr = ALIGN(vma->vm_end, huge_page_size(h)); } } #endif @@ -225,10 +227,11 @@ hugetlbfs_read_actor(struct page *page, unsigned long offset, static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { + struct hstate *h = hstate_file(filp); struct address_space *mapping = filp->f_mapping; struct inode *inode = mapping->host; - unsigned long index = *ppos >> HPAGE_SHIFT; - unsigned long offset = *ppos & ~HPAGE_MASK; + unsigned long index = *ppos >> huge_page_shift(h); + unsigned long offset = *ppos & ~huge_page_mask(h); unsigned long end_index; loff_t isize; ssize_t retval = 0; @@ -243,17 +246,17 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, if (!isize) goto out; - end_index = (isize - 1) >> HPAGE_SHIFT; + end_index = (isize - 1) >> huge_page_shift(h); for (;;) { struct page *page; - int nr, ret; + unsigned long nr, ret; /* nr is the maximum number of bytes to copy from this page */ - nr = HPAGE_SIZE; + nr = huge_page_size(h); if (index >= end_index) { if (index > end_index) goto out; - nr = ((isize - 1) & ~HPAGE_MASK) + 1; + nr = ((isize - 1) & ~huge_page_mask(h)) + 1; if (nr <= offset) { goto out; } @@ -287,8 +290,8 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, offset += ret; retval += ret; len -= ret; - index += offset >> HPAGE_SHIFT; - offset &= ~HPAGE_MASK; + index += offset >> huge_page_shift(h); + offset &= ~huge_page_mask(h); if (page) page_cache_release(page); @@ -298,7 +301,7 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, break; } out: - *ppos = ((loff_t)index << HPAGE_SHIFT) + offset; + *ppos = ((loff_t)index << huge_page_shift(h)) + offset; mutex_unlock(&inode->i_mutex); return retval; } @@ -339,8 +342,9 @@ static void truncate_huge_page(struct page *page) static void truncate_hugepages(struct inode *inode, loff_t lstart) { + struct hstate *h = hstate_inode(inode); struct address_space *mapping = &inode->i_data; - const pgoff_t start = lstart >> HPAGE_SHIFT; + const pgoff_t start = lstart >> huge_page_shift(h); struct pagevec pvec; pgoff_t next; int i, freed = 0; @@ -449,8 +453,9 @@ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) { pgoff_t pgoff; struct address_space *mapping = inode->i_mapping; + struct hstate *h = hstate_inode(inode); - BUG_ON(offset & ~HPAGE_MASK); + BUG_ON(offset & ~huge_page_mask(h)); pgoff = offset >> PAGE_SHIFT; i_size_write(inode, offset); @@ -465,6 +470,7 @@ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; + struct hstate *h = hstate_inode(inode); int error; unsigned int ia_valid = attr->ia_valid; @@ -476,7 +482,7 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) if (ia_valid & ATTR_SIZE) { error = -EINVAL; - if (!(attr->ia_size & ~HPAGE_MASK)) + if (!(attr->ia_size & ~huge_page_mask(h))) error = hugetlb_vmtruncate(inode, attr->ia_size); if (error) goto out; @@ -610,9 +616,10 @@ static int hugetlbfs_set_page_dirty(struct page *page) static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb); + struct hstate *h = hstate_inode(dentry->d_inode); buf->f_type = HUGETLBFS_MAGIC; - buf->f_bsize = HPAGE_SIZE; + buf->f_bsize = huge_page_size(h); if (sbinfo) { spin_lock(&sbinfo->stat_lock); /* If no limits set, just report 0 for max/free/used @@ -942,7 +949,8 @@ struct file *hugetlb_file_setup(const char *name, size_t size) goto out_dentry; error = -ENOMEM; - if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT, NULL)) + if (hugetlb_reserve_pages(inode, 0, + size >> huge_page_shift(hstate_inode(inode)), NULL)) goto out_inode; d_instantiate(dentry, inode); diff --git a/include/asm-ia64/hugetlb.h b/include/asm-ia64/hugetlb.h index e9d1e5e2382d..da55c63728e0 100644 --- a/include/asm-ia64/hugetlb.h +++ b/include/asm-ia64/hugetlb.h @@ -8,7 +8,8 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); -int prepare_hugepage_range(unsigned long addr, unsigned long len); +int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len); static inline int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, diff --git a/include/asm-powerpc/hugetlb.h b/include/asm-powerpc/hugetlb.h index 0a37aa5ecaa5..ca37c4af27b1 100644 --- a/include/asm-powerpc/hugetlb.h +++ b/include/asm-powerpc/hugetlb.h @@ -21,7 +21,8 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-s390/hugetlb.h b/include/asm-s390/hugetlb.h index 600a776f8f75..670a1d1745d2 100644 --- a/include/asm-s390/hugetlb.h +++ b/include/asm-s390/hugetlb.h @@ -22,7 +22,8 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-sh/hugetlb.h b/include/asm-sh/hugetlb.h index fb30018938c7..967068fb79ac 100644 --- a/include/asm-sh/hugetlb.h +++ b/include/asm-sh/hugetlb.h @@ -14,7 +14,8 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-sparc/hugetlb.h b/include/asm-sparc/hugetlb.h index aeb92374ca3d..177061064ee6 100644 --- a/include/asm-sparc/hugetlb.h +++ b/include/asm-sparc/hugetlb.h @@ -22,7 +22,8 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-x86/hugetlb.h b/include/asm-x86/hugetlb.h index 7eed6e0883bf..439a9acc132d 100644 --- a/include/asm-x86/hugetlb.h +++ b/include/asm-x86/hugetlb.h @@ -14,11 +14,13 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { - if (len & ~HPAGE_MASK) + struct hstate *h = hstate_file(file); + if (len & ~huge_page_mask(h)) return -EINVAL; - if (addr & ~HPAGE_MASK) + if (addr & ~huge_page_mask(h)) return -EINVAL; return 0; } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index abbc187193a1..ad2271e11f9b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -8,7 +8,6 @@ #include #include #include -#include struct ctl_table; @@ -45,7 +44,8 @@ extern int sysctl_hugetlb_shm_group; /* arch callbacks */ -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr); +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz); pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr); int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep); struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, @@ -80,7 +80,7 @@ static inline unsigned long hugetlb_total_pages(void) #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL -#define prepare_hugepage_range(addr,len) (-EINVAL) +#define prepare_hugepage_range(file, addr, len) (-EINVAL) #define pmd_huge(x) 0 #define is_hugepage_only_range(mm, addr, len) 0 #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; }) @@ -134,8 +134,6 @@ struct file *hugetlb_file_setup(const char *name, size_t); int hugetlb_get_quota(struct address_space *mapping, long delta); void hugetlb_put_quota(struct address_space *mapping, long delta); -#define BLOCKS_PER_HUGEPAGE (HPAGE_SIZE / 512) - static inline int is_file_hugepages(struct file *file) { if (file->f_op == &hugetlbfs_file_operations) @@ -164,4 +162,84 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long flags); #endif /* HAVE_ARCH_HUGETLB_UNMAPPED_AREA */ +#ifdef CONFIG_HUGETLB_PAGE + +/* Defines one hugetlb page size */ +struct hstate { + int hugetlb_next_nid; + unsigned int order; + unsigned long mask; + unsigned long max_huge_pages; + unsigned long nr_huge_pages; + unsigned long free_huge_pages; + unsigned long resv_huge_pages; + unsigned long surplus_huge_pages; + unsigned long nr_overcommit_huge_pages; + struct list_head hugepage_freelists[MAX_NUMNODES]; + unsigned int nr_huge_pages_node[MAX_NUMNODES]; + unsigned int free_huge_pages_node[MAX_NUMNODES]; + unsigned int surplus_huge_pages_node[MAX_NUMNODES]; +}; + +extern struct hstate default_hstate; + +static inline struct hstate *hstate_vma(struct vm_area_struct *vma) +{ + return &default_hstate; +} + +static inline struct hstate *hstate_file(struct file *f) +{ + return &default_hstate; +} + +static inline struct hstate *hstate_inode(struct inode *i) +{ + return &default_hstate; +} + +static inline unsigned long huge_page_size(struct hstate *h) +{ + return (unsigned long)PAGE_SIZE << h->order; +} + +static inline unsigned long huge_page_mask(struct hstate *h) +{ + return h->mask; +} + +static inline unsigned int huge_page_order(struct hstate *h) +{ + return h->order; +} + +static inline unsigned huge_page_shift(struct hstate *h) +{ + return h->order + PAGE_SHIFT; +} + +static inline unsigned int pages_per_huge_page(struct hstate *h) +{ + return 1 << h->order; +} + +static inline unsigned int blocks_per_huge_page(struct hstate *h) +{ + return huge_page_size(h) / 512; +} + +#include + +#else +struct hstate {}; +#define hstate_file(f) NULL +#define hstate_vma(v) NULL +#define hstate_inode(i) NULL +#define huge_page_size(h) PAGE_SIZE +#define huge_page_mask(h) PAGE_MASK +#define huge_page_order(h) 0 +#define huge_page_shift(h) PAGE_SHIFT +#define pages_per_huge_page(h) 1 +#endif + #endif /* _LINUX_HUGETLB_H */ diff --git a/ipc/shm.c b/ipc/shm.c index 790240cd067f..a726aebce7d7 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -577,7 +577,8 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, if (is_file_hugepages(shp->shm_file)) { struct address_space *mapping = inode->i_mapping; - *rss += (HPAGE_SIZE/PAGE_SIZE)*mapping->nrpages; + struct hstate *h = hstate_file(shp->shm_file); + *rss += pages_per_huge_page(h) * mapping->nrpages; } else { struct shmem_inode_info *info = SHMEM_I(inode); spin_lock(&info->lock); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 32dff4290c66..0d8153e25f09 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -22,18 +22,12 @@ #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; -static unsigned long nr_huge_pages, free_huge_pages, resv_huge_pages; -static unsigned long surplus_huge_pages; -static unsigned long nr_overcommit_huge_pages; unsigned long max_huge_pages; unsigned long sysctl_overcommit_huge_pages; -static struct list_head hugepage_freelists[MAX_NUMNODES]; -static unsigned int nr_huge_pages_node[MAX_NUMNODES]; -static unsigned int free_huge_pages_node[MAX_NUMNODES]; -static unsigned int surplus_huge_pages_node[MAX_NUMNODES]; static gfp_t htlb_alloc_mask = GFP_HIGHUSER; unsigned long hugepages_treat_as_movable; -static int hugetlb_next_nid; + +struct hstate default_hstate; /* * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages @@ -203,11 +197,11 @@ static long region_count(struct list_head *head, long f, long t) * Convert the address within this vma to the page offset within * the mapping, in pagecache page units; huge pages here. */ -static pgoff_t vma_hugecache_offset(struct vm_area_struct *vma, - unsigned long address) +static pgoff_t vma_hugecache_offset(struct hstate *h, + struct vm_area_struct *vma, unsigned long address) { - return ((address - vma->vm_start) >> HPAGE_SHIFT) + - (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); + return ((address - vma->vm_start) >> huge_page_shift(h)) + + (vma->vm_pgoff >> huge_page_order(h)); } /* @@ -309,20 +303,21 @@ static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag) } /* Decrement the reserved pages in the hugepage pool by one */ -static void decrement_hugepage_resv_vma(struct vm_area_struct *vma) +static void decrement_hugepage_resv_vma(struct hstate *h, + struct vm_area_struct *vma) { if (vma->vm_flags & VM_NORESERVE) return; if (vma->vm_flags & VM_SHARED) { /* Shared mappings always use reserves */ - resv_huge_pages--; + h->resv_huge_pages--; } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { /* * Only the process that called mmap() has reserves for * private mappings. */ - resv_huge_pages--; + h->resv_huge_pages--; } } @@ -344,12 +339,13 @@ static int vma_has_private_reserves(struct vm_area_struct *vma) return 1; } -static void clear_huge_page(struct page *page, unsigned long addr) +static void clear_huge_page(struct page *page, + unsigned long addr, unsigned long sz) { int i; might_sleep(); - for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); i++) { + for (i = 0; i < sz/PAGE_SIZE; i++) { cond_resched(); clear_user_highpage(page + i, addr + i * PAGE_SIZE); } @@ -359,41 +355,43 @@ static void copy_huge_page(struct page *dst, struct page *src, unsigned long addr, struct vm_area_struct *vma) { int i; + struct hstate *h = hstate_vma(vma); might_sleep(); - for (i = 0; i < HPAGE_SIZE/PAGE_SIZE; i++) { + for (i = 0; i < pages_per_huge_page(h); i++) { cond_resched(); copy_user_highpage(dst + i, src + i, addr + i*PAGE_SIZE, vma); } } -static void enqueue_huge_page(struct page *page) +static void enqueue_huge_page(struct hstate *h, struct page *page) { int nid = page_to_nid(page); - list_add(&page->lru, &hugepage_freelists[nid]); - free_huge_pages++; - free_huge_pages_node[nid]++; + list_add(&page->lru, &h->hugepage_freelists[nid]); + h->free_huge_pages++; + h->free_huge_pages_node[nid]++; } -static struct page *dequeue_huge_page(void) +static struct page *dequeue_huge_page(struct hstate *h) { int nid; struct page *page = NULL; for (nid = 0; nid < MAX_NUMNODES; ++nid) { - if (!list_empty(&hugepage_freelists[nid])) { - page = list_entry(hugepage_freelists[nid].next, + if (!list_empty(&h->hugepage_freelists[nid])) { + page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); list_del(&page->lru); - free_huge_pages--; - free_huge_pages_node[nid]--; + h->free_huge_pages--; + h->free_huge_pages_node[nid]--; break; } } return page; } -static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, +static struct page *dequeue_huge_page_vma(struct hstate *h, + struct vm_area_struct *vma, unsigned long address, int avoid_reserve) { int nid; @@ -411,26 +409,26 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, * not "stolen". The child may still get SIGKILLed */ if (!vma_has_private_reserves(vma) && - free_huge_pages - resv_huge_pages == 0) + h->free_huge_pages - h->resv_huge_pages == 0) return NULL; /* If reserves cannot be used, ensure enough pages are in the pool */ - if (avoid_reserve && free_huge_pages - resv_huge_pages == 0) + if (avoid_reserve && h->free_huge_pages - h->resv_huge_pages == 0) return NULL; for_each_zone_zonelist_nodemask(zone, z, zonelist, MAX_NR_ZONES - 1, nodemask) { nid = zone_to_nid(zone); if (cpuset_zone_allowed_softwall(zone, htlb_alloc_mask) && - !list_empty(&hugepage_freelists[nid])) { - page = list_entry(hugepage_freelists[nid].next, + !list_empty(&h->hugepage_freelists[nid])) { + page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); list_del(&page->lru); - free_huge_pages--; - free_huge_pages_node[nid]--; + h->free_huge_pages--; + h->free_huge_pages_node[nid]--; if (!avoid_reserve) - decrement_hugepage_resv_vma(vma); + decrement_hugepage_resv_vma(h, vma); break; } @@ -439,12 +437,13 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, return page; } -static void update_and_free_page(struct page *page) +static void update_and_free_page(struct hstate *h, struct page *page) { int i; - nr_huge_pages--; - nr_huge_pages_node[page_to_nid(page)]--; - for (i = 0; i < (HPAGE_SIZE / PAGE_SIZE); i++) { + + h->nr_huge_pages--; + h->nr_huge_pages_node[page_to_nid(page)]--; + for (i = 0; i < pages_per_huge_page(h); i++) { page[i].flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced | 1 << PG_dirty | 1 << PG_active | 1 << PG_reserved | 1 << PG_private | 1<< PG_writeback); @@ -452,11 +451,16 @@ static void update_and_free_page(struct page *page) set_compound_page_dtor(page, NULL); set_page_refcounted(page); arch_release_hugepage(page); - __free_pages(page, HUGETLB_PAGE_ORDER); + __free_pages(page, huge_page_order(h)); } static void free_huge_page(struct page *page) { + /* + * Can't pass hstate in here because it is called from the + * compound page destructor. + */ + struct hstate *h = &default_hstate; int nid = page_to_nid(page); struct address_space *mapping; @@ -466,12 +470,12 @@ static void free_huge_page(struct page *page) INIT_LIST_HEAD(&page->lru); spin_lock(&hugetlb_lock); - if (surplus_huge_pages_node[nid]) { - update_and_free_page(page); - surplus_huge_pages--; - surplus_huge_pages_node[nid]--; + if (h->surplus_huge_pages_node[nid]) { + update_and_free_page(h, page); + h->surplus_huge_pages--; + h->surplus_huge_pages_node[nid]--; } else { - enqueue_huge_page(page); + enqueue_huge_page(h, page); } spin_unlock(&hugetlb_lock); if (mapping) @@ -483,7 +487,7 @@ static void free_huge_page(struct page *page) * balanced by operating on them in a round-robin fashion. * Returns 1 if an adjustment was made. */ -static int adjust_pool_surplus(int delta) +static int adjust_pool_surplus(struct hstate *h, int delta) { static int prev_nid; int nid = prev_nid; @@ -496,15 +500,15 @@ static int adjust_pool_surplus(int delta) nid = first_node(node_online_map); /* To shrink on this node, there must be a surplus page */ - if (delta < 0 && !surplus_huge_pages_node[nid]) + if (delta < 0 && !h->surplus_huge_pages_node[nid]) continue; /* Surplus cannot exceed the total number of pages */ - if (delta > 0 && surplus_huge_pages_node[nid] >= - nr_huge_pages_node[nid]) + if (delta > 0 && h->surplus_huge_pages_node[nid] >= + h->nr_huge_pages_node[nid]) continue; - surplus_huge_pages += delta; - surplus_huge_pages_node[nid] += delta; + h->surplus_huge_pages += delta; + h->surplus_huge_pages_node[nid] += delta; ret = 1; break; } while (nid != prev_nid); @@ -513,46 +517,46 @@ static int adjust_pool_surplus(int delta) return ret; } -static void prep_new_huge_page(struct page *page, int nid) +static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) { set_compound_page_dtor(page, free_huge_page); spin_lock(&hugetlb_lock); - nr_huge_pages++; - nr_huge_pages_node[nid]++; + h->nr_huge_pages++; + h->nr_huge_pages_node[nid]++; spin_unlock(&hugetlb_lock); put_page(page); /* free it into the hugepage allocator */ } -static struct page *alloc_fresh_huge_page_node(int nid) +static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid) { struct page *page; page = alloc_pages_node(nid, htlb_alloc_mask|__GFP_COMP|__GFP_THISNODE| __GFP_REPEAT|__GFP_NOWARN, - HUGETLB_PAGE_ORDER); + huge_page_order(h)); if (page) { if (arch_prepare_hugepage(page)) { __free_pages(page, HUGETLB_PAGE_ORDER); return NULL; } - prep_new_huge_page(page, nid); + prep_new_huge_page(h, page, nid); } return page; } -static int alloc_fresh_huge_page(void) +static int alloc_fresh_huge_page(struct hstate *h) { struct page *page; int start_nid; int next_nid; int ret = 0; - start_nid = hugetlb_next_nid; + start_nid = h->hugetlb_next_nid; do { - page = alloc_fresh_huge_page_node(hugetlb_next_nid); + page = alloc_fresh_huge_page_node(h, h->hugetlb_next_nid); if (page) ret = 1; /* @@ -566,11 +570,11 @@ static int alloc_fresh_huge_page(void) * if we just successfully allocated a hugepage so that * the next caller gets hugepages on the next node. */ - next_nid = next_node(hugetlb_next_nid, node_online_map); + next_nid = next_node(h->hugetlb_next_nid, node_online_map); if (next_nid == MAX_NUMNODES) next_nid = first_node(node_online_map); - hugetlb_next_nid = next_nid; - } while (!page && hugetlb_next_nid != start_nid); + h->hugetlb_next_nid = next_nid; + } while (!page && h->hugetlb_next_nid != start_nid); if (ret) count_vm_event(HTLB_BUDDY_PGALLOC); @@ -580,8 +584,8 @@ static int alloc_fresh_huge_page(void) return ret; } -static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, - unsigned long address) +static struct page *alloc_buddy_huge_page(struct hstate *h, + struct vm_area_struct *vma, unsigned long address) { struct page *page; unsigned int nid; @@ -610,18 +614,18 @@ static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, * per-node value is checked there. */ spin_lock(&hugetlb_lock); - if (surplus_huge_pages >= nr_overcommit_huge_pages) { + if (h->surplus_huge_pages >= h->nr_overcommit_huge_pages) { spin_unlock(&hugetlb_lock); return NULL; } else { - nr_huge_pages++; - surplus_huge_pages++; + h->nr_huge_pages++; + h->surplus_huge_pages++; } spin_unlock(&hugetlb_lock); page = alloc_pages(htlb_alloc_mask|__GFP_COMP| __GFP_REPEAT|__GFP_NOWARN, - HUGETLB_PAGE_ORDER); + huge_page_order(h)); spin_lock(&hugetlb_lock); if (page) { @@ -636,12 +640,12 @@ static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, /* * We incremented the global counters already */ - nr_huge_pages_node[nid]++; - surplus_huge_pages_node[nid]++; + h->nr_huge_pages_node[nid]++; + h->surplus_huge_pages_node[nid]++; __count_vm_event(HTLB_BUDDY_PGALLOC); } else { - nr_huge_pages--; - surplus_huge_pages--; + h->nr_huge_pages--; + h->surplus_huge_pages--; __count_vm_event(HTLB_BUDDY_PGALLOC_FAIL); } spin_unlock(&hugetlb_lock); @@ -653,16 +657,16 @@ static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, * Increase the hugetlb pool such that it can accomodate a reservation * of size 'delta'. */ -static int gather_surplus_pages(int delta) +static int gather_surplus_pages(struct hstate *h, int delta) { struct list_head surplus_list; struct page *page, *tmp; int ret, i; int needed, allocated; - needed = (resv_huge_pages + delta) - free_huge_pages; + needed = (h->resv_huge_pages + delta) - h->free_huge_pages; if (needed <= 0) { - resv_huge_pages += delta; + h->resv_huge_pages += delta; return 0; } @@ -673,7 +677,7 @@ static int gather_surplus_pages(int delta) retry: spin_unlock(&hugetlb_lock); for (i = 0; i < needed; i++) { - page = alloc_buddy_huge_page(NULL, 0); + page = alloc_buddy_huge_page(h, NULL, 0); if (!page) { /* * We were not able to allocate enough pages to @@ -694,7 +698,8 @@ retry: * because either resv_huge_pages or free_huge_pages may have changed. */ spin_lock(&hugetlb_lock); - needed = (resv_huge_pages + delta) - (free_huge_pages + allocated); + needed = (h->resv_huge_pages + delta) - + (h->free_huge_pages + allocated); if (needed > 0) goto retry; @@ -707,7 +712,7 @@ retry: * before they are reserved. */ needed += allocated; - resv_huge_pages += delta; + h->resv_huge_pages += delta; ret = 0; free: /* Free the needed pages to the hugetlb pool */ @@ -715,7 +720,7 @@ free: if ((--needed) < 0) break; list_del(&page->lru); - enqueue_huge_page(page); + enqueue_huge_page(h, page); } /* Free unnecessary surplus pages to the buddy allocator */ @@ -743,7 +748,8 @@ free: * allocated to satisfy the reservation must be explicitly freed if they were * never used. */ -static void return_unused_surplus_pages(unsigned long unused_resv_pages) +static void return_unused_surplus_pages(struct hstate *h, + unsigned long unused_resv_pages) { static int nid = -1; struct page *page; @@ -758,27 +764,27 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) unsigned long remaining_iterations = num_online_nodes(); /* Uncommit the reservation */ - resv_huge_pages -= unused_resv_pages; + h->resv_huge_pages -= unused_resv_pages; - nr_pages = min(unused_resv_pages, surplus_huge_pages); + nr_pages = min(unused_resv_pages, h->surplus_huge_pages); while (remaining_iterations-- && nr_pages) { nid = next_node(nid, node_online_map); if (nid == MAX_NUMNODES) nid = first_node(node_online_map); - if (!surplus_huge_pages_node[nid]) + if (!h->surplus_huge_pages_node[nid]) continue; - if (!list_empty(&hugepage_freelists[nid])) { - page = list_entry(hugepage_freelists[nid].next, + if (!list_empty(&h->hugepage_freelists[nid])) { + page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); list_del(&page->lru); - update_and_free_page(page); - free_huge_pages--; - free_huge_pages_node[nid]--; - surplus_huge_pages--; - surplus_huge_pages_node[nid]--; + update_and_free_page(h, page); + h->free_huge_pages--; + h->free_huge_pages_node[nid]--; + h->surplus_huge_pages--; + h->surplus_huge_pages_node[nid]--; nr_pages--; remaining_iterations = num_online_nodes(); } @@ -794,13 +800,14 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) * an instantiated the change should be committed via vma_commit_reservation. * No action is required on failure. */ -static int vma_needs_reservation(struct vm_area_struct *vma, unsigned long addr) +static int vma_needs_reservation(struct hstate *h, + struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping = vma->vm_file->f_mapping; struct inode *inode = mapping->host; if (vma->vm_flags & VM_SHARED) { - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); return region_chg(&inode->i_mapping->private_list, idx, idx + 1); @@ -809,7 +816,7 @@ static int vma_needs_reservation(struct vm_area_struct *vma, unsigned long addr) } else { int err; - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); struct resv_map *reservations = vma_resv_map(vma); err = region_chg(&reservations->regions, idx, idx + 1); @@ -818,18 +825,18 @@ static int vma_needs_reservation(struct vm_area_struct *vma, unsigned long addr) return 0; } } -static void vma_commit_reservation(struct vm_area_struct *vma, - unsigned long addr) +static void vma_commit_reservation(struct hstate *h, + struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping = vma->vm_file->f_mapping; struct inode *inode = mapping->host; if (vma->vm_flags & VM_SHARED) { - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); region_add(&inode->i_mapping->private_list, idx, idx + 1); } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); struct resv_map *reservations = vma_resv_map(vma); /* Mark this page used in the map. */ @@ -840,6 +847,7 @@ static void vma_commit_reservation(struct vm_area_struct *vma, static struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr, int avoid_reserve) { + struct hstate *h = hstate_vma(vma); struct page *page; struct address_space *mapping = vma->vm_file->f_mapping; struct inode *inode = mapping->host; @@ -852,7 +860,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, * MAP_NORESERVE mappings may also need pages and quota allocated * if no reserve mapping overlaps. */ - chg = vma_needs_reservation(vma, addr); + chg = vma_needs_reservation(h, vma, addr); if (chg < 0) return ERR_PTR(chg); if (chg) @@ -860,11 +868,11 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return ERR_PTR(-ENOSPC); spin_lock(&hugetlb_lock); - page = dequeue_huge_page_vma(vma, addr, avoid_reserve); + page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve); spin_unlock(&hugetlb_lock); if (!page) { - page = alloc_buddy_huge_page(vma, addr); + page = alloc_buddy_huge_page(h, vma, addr); if (!page) { hugetlb_put_quota(inode->i_mapping, chg); return ERR_PTR(-VM_FAULT_OOM); @@ -874,7 +882,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, set_page_refcounted(page); set_page_private(page, (unsigned long) mapping); - vma_commit_reservation(vma, addr); + vma_commit_reservation(h, vma, addr); return page; } @@ -882,21 +890,28 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, static int __init hugetlb_init(void) { unsigned long i; + struct hstate *h = &default_hstate; if (HPAGE_SHIFT == 0) return 0; + if (!h->order) { + h->order = HPAGE_SHIFT - PAGE_SHIFT; + h->mask = HPAGE_MASK; + } + for (i = 0; i < MAX_NUMNODES; ++i) - INIT_LIST_HEAD(&hugepage_freelists[i]); + INIT_LIST_HEAD(&h->hugepage_freelists[i]); - hugetlb_next_nid = first_node(node_online_map); + h->hugetlb_next_nid = first_node(node_online_map); for (i = 0; i < max_huge_pages; ++i) { - if (!alloc_fresh_huge_page()) + if (!alloc_fresh_huge_page(h)) break; } - max_huge_pages = free_huge_pages = nr_huge_pages = i; - printk("Total HugeTLB memory allocated, %ld\n", free_huge_pages); + max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i; + printk(KERN_INFO "Total HugeTLB memory allocated, %ld\n", + h->free_huge_pages); return 0; } module_init(hugetlb_init); @@ -922,34 +937,36 @@ static unsigned int cpuset_mems_nr(unsigned int *array) #ifdef CONFIG_SYSCTL #ifdef CONFIG_HIGHMEM -static void try_to_free_low(unsigned long count) +static void try_to_free_low(struct hstate *h, unsigned long count) { int i; for (i = 0; i < MAX_NUMNODES; ++i) { struct page *page, *next; - list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) { - if (count >= nr_huge_pages) + struct list_head *freel = &h->hugepage_freelists[i]; + list_for_each_entry_safe(page, next, freel, lru) { + if (count >= h->nr_huge_pages) return; if (PageHighMem(page)) continue; list_del(&page->lru); update_and_free_page(page); - free_huge_pages--; - free_huge_pages_node[page_to_nid(page)]--; + h->free_huge_pages--; + h->free_huge_pages_node[page_to_nid(page)]--; } } } #else -static inline void try_to_free_low(unsigned long count) +static inline void try_to_free_low(struct hstate *h, unsigned long count) { } #endif -#define persistent_huge_pages (nr_huge_pages - surplus_huge_pages) +#define persistent_huge_pages(h) (h->nr_huge_pages - h->surplus_huge_pages) static unsigned long set_max_huge_pages(unsigned long count) { unsigned long min_count, ret; + struct hstate *h = &default_hstate; /* * Increase the pool size @@ -963,19 +980,19 @@ static unsigned long set_max_huge_pages(unsigned long count) * within all the constraints specified by the sysctls. */ spin_lock(&hugetlb_lock); - while (surplus_huge_pages && count > persistent_huge_pages) { - if (!adjust_pool_surplus(-1)) + while (h->surplus_huge_pages && count > persistent_huge_pages(h)) { + if (!adjust_pool_surplus(h, -1)) break; } - while (count > persistent_huge_pages) { + while (count > persistent_huge_pages(h)) { /* * If this allocation races such that we no longer need the * page, free_huge_page will handle it by freeing the page * and reducing the surplus. */ spin_unlock(&hugetlb_lock); - ret = alloc_fresh_huge_page(); + ret = alloc_fresh_huge_page(h); spin_lock(&hugetlb_lock); if (!ret) goto out; @@ -997,21 +1014,21 @@ static unsigned long set_max_huge_pages(unsigned long count) * and won't grow the pool anywhere else. Not until one of the * sysctls are changed, or the surplus pages go out of use. */ - min_count = resv_huge_pages + nr_huge_pages - free_huge_pages; + min_count = h->resv_huge_pages + h->nr_huge_pages - h->free_huge_pages; min_count = max(count, min_count); - try_to_free_low(min_count); - while (min_count < persistent_huge_pages) { - struct page *page = dequeue_huge_page(); + try_to_free_low(h, min_count); + while (min_count < persistent_huge_pages(h)) { + struct page *page = dequeue_huge_page(h); if (!page) break; - update_and_free_page(page); + update_and_free_page(h, page); } - while (count < persistent_huge_pages) { - if (!adjust_pool_surplus(1)) + while (count < persistent_huge_pages(h)) { + if (!adjust_pool_surplus(h, 1)) break; } out: - ret = persistent_huge_pages; + ret = persistent_huge_pages(h); spin_unlock(&hugetlb_lock); return ret; } @@ -1041,9 +1058,10 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) { + struct hstate *h = &default_hstate; proc_doulongvec_minmax(table, write, file, buffer, length, ppos); spin_lock(&hugetlb_lock); - nr_overcommit_huge_pages = sysctl_overcommit_huge_pages; + h->nr_overcommit_huge_pages = sysctl_overcommit_huge_pages; spin_unlock(&hugetlb_lock); return 0; } @@ -1052,37 +1070,40 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, int hugetlb_report_meminfo(char *buf) { + struct hstate *h = &default_hstate; return sprintf(buf, "HugePages_Total: %5lu\n" "HugePages_Free: %5lu\n" "HugePages_Rsvd: %5lu\n" "HugePages_Surp: %5lu\n" "Hugepagesize: %5lu kB\n", - nr_huge_pages, - free_huge_pages, - resv_huge_pages, - surplus_huge_pages, - HPAGE_SIZE/1024); + h->nr_huge_pages, + h->free_huge_pages, + h->resv_huge_pages, + h->surplus_huge_pages, + 1UL << (huge_page_order(h) + PAGE_SHIFT - 10)); } int hugetlb_report_node_meminfo(int nid, char *buf) { + struct hstate *h = &default_hstate; return sprintf(buf, "Node %d HugePages_Total: %5u\n" "Node %d HugePages_Free: %5u\n" "Node %d HugePages_Surp: %5u\n", - nid, nr_huge_pages_node[nid], - nid, free_huge_pages_node[nid], - nid, surplus_huge_pages_node[nid]); + nid, h->nr_huge_pages_node[nid], + nid, h->free_huge_pages_node[nid], + nid, h->surplus_huge_pages_node[nid]); } /* Return the number pages of memory we physically have, in PAGE_SIZE units. */ unsigned long hugetlb_total_pages(void) { - return nr_huge_pages * (HPAGE_SIZE / PAGE_SIZE); + struct hstate *h = &default_hstate; + return h->nr_huge_pages * pages_per_huge_page(h); } -static int hugetlb_acct_memory(long delta) +static int hugetlb_acct_memory(struct hstate *h, long delta) { int ret = -ENOMEM; @@ -1105,18 +1126,18 @@ static int hugetlb_acct_memory(long delta) * semantics that cpuset has. */ if (delta > 0) { - if (gather_surplus_pages(delta) < 0) + if (gather_surplus_pages(h, delta) < 0) goto out; - if (delta > cpuset_mems_nr(free_huge_pages_node)) { - return_unused_surplus_pages(delta); + if (delta > cpuset_mems_nr(h->free_huge_pages_node)) { + return_unused_surplus_pages(h, delta); goto out; } } ret = 0; if (delta < 0) - return_unused_surplus_pages((unsigned long) -delta); + return_unused_surplus_pages(h, (unsigned long) -delta); out: spin_unlock(&hugetlb_lock); @@ -1141,14 +1162,15 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma) static void hugetlb_vm_op_close(struct vm_area_struct *vma) { + struct hstate *h = hstate_vma(vma); struct resv_map *reservations = vma_resv_map(vma); unsigned long reserve; unsigned long start; unsigned long end; if (reservations) { - start = vma_hugecache_offset(vma, vma->vm_start); - end = vma_hugecache_offset(vma, vma->vm_end); + start = vma_hugecache_offset(h, vma, vma->vm_start); + end = vma_hugecache_offset(h, vma, vma->vm_end); reserve = (end - start) - region_count(&reservations->regions, start, end); @@ -1156,7 +1178,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma) kref_put(&reservations->refs, resv_map_release); if (reserve) - hugetlb_acct_memory(-reserve); + hugetlb_acct_memory(h, -reserve); } } @@ -1214,14 +1236,16 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, struct page *ptepage; unsigned long addr; int cow; + struct hstate *h = hstate_vma(vma); + unsigned long sz = huge_page_size(h); cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; - for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) { + for (addr = vma->vm_start; addr < vma->vm_end; addr += sz) { src_pte = huge_pte_offset(src, addr); if (!src_pte) continue; - dst_pte = huge_pte_alloc(dst, addr); + dst_pte = huge_pte_alloc(dst, addr, sz); if (!dst_pte) goto nomem; @@ -1257,6 +1281,9 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, pte_t pte; struct page *page; struct page *tmp; + struct hstate *h = hstate_vma(vma); + unsigned long sz = huge_page_size(h); + /* * A page gathering list, protected by per file i_mmap_lock. The * lock is used to avoid list corruption from multiple unmapping @@ -1265,11 +1292,11 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, LIST_HEAD(page_list); WARN_ON(!is_vm_hugetlb_page(vma)); - BUG_ON(start & ~HPAGE_MASK); - BUG_ON(end & ~HPAGE_MASK); + BUG_ON(start & ~huge_page_mask(h)); + BUG_ON(end & ~huge_page_mask(h)); spin_lock(&mm->page_table_lock); - for (address = start; address < end; address += HPAGE_SIZE) { + for (address = start; address < end; address += sz) { ptep = huge_pte_offset(mm, address); if (!ptep) continue; @@ -1383,6 +1410,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t pte, struct page *pagecache_page) { + struct hstate *h = hstate_vma(vma); struct page *old_page, *new_page; int avoidcopy; int outside_reserve = 0; @@ -1443,7 +1471,7 @@ retry_avoidcopy: __SetPageUptodate(new_page); spin_lock(&mm->page_table_lock); - ptep = huge_pte_offset(mm, address & HPAGE_MASK); + ptep = huge_pte_offset(mm, address & huge_page_mask(h)); if (likely(pte_same(huge_ptep_get(ptep), pte))) { /* Break COW */ huge_ptep_clear_flush(vma, address, ptep); @@ -1458,14 +1486,14 @@ retry_avoidcopy: } /* Return the pagecache page at a given address within a VMA */ -static struct page *hugetlbfs_pagecache_page(struct vm_area_struct *vma, - unsigned long address) +static struct page *hugetlbfs_pagecache_page(struct hstate *h, + struct vm_area_struct *vma, unsigned long address) { struct address_space *mapping; pgoff_t idx; mapping = vma->vm_file->f_mapping; - idx = vma_hugecache_offset(vma, address); + idx = vma_hugecache_offset(h, vma, address); return find_lock_page(mapping, idx); } @@ -1473,6 +1501,7 @@ static struct page *hugetlbfs_pagecache_page(struct vm_area_struct *vma, static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *ptep, int write_access) { + struct hstate *h = hstate_vma(vma); int ret = VM_FAULT_SIGBUS; pgoff_t idx; unsigned long size; @@ -1493,7 +1522,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, } mapping = vma->vm_file->f_mapping; - idx = vma_hugecache_offset(vma, address); + idx = vma_hugecache_offset(h, vma, address); /* * Use page lock to guard against racing truncation @@ -1502,7 +1531,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, retry: page = find_lock_page(mapping, idx); if (!page) { - size = i_size_read(mapping->host) >> HPAGE_SHIFT; + size = i_size_read(mapping->host) >> huge_page_shift(h); if (idx >= size) goto out; page = alloc_huge_page(vma, address, 0); @@ -1510,7 +1539,7 @@ retry: ret = -PTR_ERR(page); goto out; } - clear_huge_page(page, address); + clear_huge_page(page, address, huge_page_size(h)); __SetPageUptodate(page); if (vma->vm_flags & VM_SHARED) { @@ -1526,14 +1555,14 @@ retry: } spin_lock(&inode->i_lock); - inode->i_blocks += BLOCKS_PER_HUGEPAGE; + inode->i_blocks += blocks_per_huge_page(h); spin_unlock(&inode->i_lock); } else lock_page(page); } spin_lock(&mm->page_table_lock); - size = i_size_read(mapping->host) >> HPAGE_SHIFT; + size = i_size_read(mapping->host) >> huge_page_shift(h); if (idx >= size) goto backout; @@ -1569,8 +1598,9 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, pte_t entry; int ret; static DEFINE_MUTEX(hugetlb_instantiation_mutex); + struct hstate *h = hstate_vma(vma); - ptep = huge_pte_alloc(mm, address); + ptep = huge_pte_alloc(mm, address, huge_page_size(h)); if (!ptep) return VM_FAULT_OOM; @@ -1594,7 +1624,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (likely(pte_same(entry, huge_ptep_get(ptep)))) if (write_access && !pte_write(entry)) { struct page *page; - page = hugetlbfs_pagecache_page(vma, address); + page = hugetlbfs_pagecache_page(h, vma, address); ret = hugetlb_cow(mm, vma, address, ptep, entry, page); if (page) { unlock_page(page); @@ -1615,6 +1645,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long pfn_offset; unsigned long vaddr = *position; int remainder = *length; + struct hstate *h = hstate_vma(vma); spin_lock(&mm->page_table_lock); while (vaddr < vma->vm_end && remainder) { @@ -1626,7 +1657,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, * each hugepage. We have to make * sure we get the * first, for the page indexing below to work. */ - pte = huge_pte_offset(mm, vaddr & HPAGE_MASK); + pte = huge_pte_offset(mm, vaddr & huge_page_mask(h)); if (!pte || huge_pte_none(huge_ptep_get(pte)) || (write && !pte_write(huge_ptep_get(pte)))) { @@ -1644,7 +1675,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, break; } - pfn_offset = (vaddr & ~HPAGE_MASK) >> PAGE_SHIFT; + pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT; page = pte_page(huge_ptep_get(pte)); same_page: if (pages) { @@ -1660,7 +1691,7 @@ same_page: --remainder; ++i; if (vaddr < vma->vm_end && remainder && - pfn_offset < HPAGE_SIZE/PAGE_SIZE) { + pfn_offset < pages_per_huge_page(h)) { /* * We use pfn_offset to avoid touching the pageframes * of this compound page. @@ -1682,13 +1713,14 @@ void hugetlb_change_protection(struct vm_area_struct *vma, unsigned long start = address; pte_t *ptep; pte_t pte; + struct hstate *h = hstate_vma(vma); BUG_ON(address >= end); flush_cache_range(vma, address, end); spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); spin_lock(&mm->page_table_lock); - for (; address < end; address += HPAGE_SIZE) { + for (; address < end; address += huge_page_size(h)) { ptep = huge_pte_offset(mm, address); if (!ptep) continue; @@ -1711,6 +1743,7 @@ int hugetlb_reserve_pages(struct inode *inode, struct vm_area_struct *vma) { long ret, chg; + struct hstate *h = hstate_inode(inode); if (vma && vma->vm_flags & VM_NORESERVE) return 0; @@ -1739,7 +1772,7 @@ int hugetlb_reserve_pages(struct inode *inode, if (hugetlb_get_quota(inode->i_mapping, chg)) return -ENOSPC; - ret = hugetlb_acct_memory(chg); + ret = hugetlb_acct_memory(h, chg); if (ret < 0) { hugetlb_put_quota(inode->i_mapping, chg); return ret; @@ -1751,12 +1784,13 @@ int hugetlb_reserve_pages(struct inode *inode, void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed) { + struct hstate *h = hstate_inode(inode); long chg = region_truncate(&inode->i_mapping->private_list, offset); spin_lock(&inode->i_lock); - inode->i_blocks -= BLOCKS_PER_HUGEPAGE * freed; + inode->i_blocks -= blocks_per_huge_page(h); spin_unlock(&inode->i_lock); hugetlb_put_quota(inode->i_mapping, (chg - freed)); - hugetlb_acct_memory(-(chg - freed)); + hugetlb_acct_memory(h, -(chg - freed)); } diff --git a/mm/memory.c b/mm/memory.c index 72932489a082..c1c1d6d8c22b 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -903,7 +903,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, if (unlikely(is_vm_hugetlb_page(vma))) { unmap_hugepage_range(vma, start, end, NULL); zap_work -= (end - start) / - (HPAGE_SIZE / PAGE_SIZE); + pages_per_huge_page(hstate_vma(vma)); start = end; } else start = unmap_page_range(*tlbp, vma, diff --git a/mm/mempolicy.c b/mm/mempolicy.c index c94e58b192c3..e550bec20582 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1481,7 +1481,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr, if (unlikely((*mpol)->mode == MPOL_INTERLEAVE)) { zl = node_zonelist(interleave_nid(*mpol, vma, addr, - HPAGE_SHIFT), gfp_flags); + huge_page_shift(hstate_vma(vma))), gfp_flags); } else { zl = policy_zonelist(gfp_flags, *mpol); if ((*mpol)->mode == MPOL_BIND) @@ -2220,9 +2220,12 @@ static void check_huge_range(struct vm_area_struct *vma, { unsigned long addr; struct page *page; + struct hstate *h = hstate_vma(vma); + unsigned long sz = huge_page_size(h); - for (addr = start; addr < end; addr += HPAGE_SIZE) { - pte_t *ptep = huge_pte_offset(vma->vm_mm, addr & HPAGE_MASK); + for (addr = start; addr < end; addr += sz) { + pte_t *ptep = huge_pte_offset(vma->vm_mm, + addr & huge_page_mask(h)); pte_t pte; if (!ptep) diff --git a/mm/mmap.c b/mm/mmap.c index 57d3b6097deb..5e0cc99e9cd5 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1812,7 +1812,8 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma, struct mempolicy *pol; struct vm_area_struct *new; - if (is_vm_hugetlb_page(vma) && (addr & ~HPAGE_MASK)) + if (is_vm_hugetlb_page(vma) && (addr & + ~(huge_page_mask(hstate_vma(vma))))) return -EINVAL; if (mm->map_count >= sysctl_max_map_count) -- cgit v1.2.3 From e5ff215941d59f8ae6bf58f6428dc5c26745a612 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:42 -0700 Subject: hugetlb: multiple hstates for multiple page sizes Add basic support for more than one hstate in hugetlbfs. This is the key to supporting multiple hugetlbfs page sizes at once. - Rather than a single hstate, we now have an array, with an iterator - default_hstate continues to be the struct hstate which we use by default - Add functions for architectures to register new hstates [akpm@linux-foundation.org: coding-style fixes] Acked-by: Adam Litke Acked-by: Nishanth Aravamudan Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 19 ++++++- kernel/sysctl.c | 8 ++- mm/hugetlb.c | 148 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 142 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ad2271e11f9b..b75bdb4deba3 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -36,8 +36,6 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to, struct vm_area_struct *vma); void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); -extern unsigned long max_huge_pages; -extern unsigned long sysctl_overcommit_huge_pages; extern unsigned long hugepages_treat_as_movable; extern const unsigned long hugetlb_zero, hugetlb_infinity; extern int sysctl_hugetlb_shm_group; @@ -181,7 +179,17 @@ struct hstate { unsigned int surplus_huge_pages_node[MAX_NUMNODES]; }; -extern struct hstate default_hstate; +void __init hugetlb_add_hstate(unsigned order); +struct hstate *size_to_hstate(unsigned long size); + +#ifndef HUGE_MAX_HSTATE +#define HUGE_MAX_HSTATE 1 +#endif + +extern struct hstate hstates[HUGE_MAX_HSTATE]; +extern unsigned int default_hstate_idx; + +#define default_hstate (hstates[default_hstate_idx]) static inline struct hstate *hstate_vma(struct vm_area_struct *vma) { @@ -230,6 +238,11 @@ static inline unsigned int blocks_per_huge_page(struct hstate *h) #include +static inline struct hstate *page_hstate(struct page *page) +{ + return size_to_hstate(PAGE_SIZE << compound_order(page)); +} + #else struct hstate {}; #define hstate_file(f) NULL diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1f7b3b76a166..1a8299d1fe59 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -959,7 +959,7 @@ static struct ctl_table vm_table[] = { #ifdef CONFIG_HUGETLB_PAGE { .procname = "nr_hugepages", - .data = &max_huge_pages, + .data = NULL, .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = &hugetlb_sysctl_handler, @@ -985,10 +985,12 @@ static struct ctl_table vm_table[] = { { .ctl_name = CTL_UNNUMBERED, .procname = "nr_overcommit_hugepages", - .data = &sysctl_overcommit_huge_pages, - .maxlen = sizeof(sysctl_overcommit_huge_pages), + .data = NULL, + .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = &hugetlb_overcommit_handler, + .extra1 = (void *)&hugetlb_zero, + .extra2 = (void *)&hugetlb_infinity, }, #endif { diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0d8153e25f09..82378d44a0c5 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -22,12 +22,19 @@ #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; -unsigned long max_huge_pages; -unsigned long sysctl_overcommit_huge_pages; static gfp_t htlb_alloc_mask = GFP_HIGHUSER; unsigned long hugepages_treat_as_movable; -struct hstate default_hstate; +static int max_hstate; +unsigned int default_hstate_idx; +struct hstate hstates[HUGE_MAX_HSTATE]; + +/* for command line parsing */ +static struct hstate * __initdata parsed_hstate; +static unsigned long __initdata default_hstate_max_huge_pages; + +#define for_each_hstate(h) \ + for ((h) = hstates; (h) < &hstates[max_hstate]; (h)++) /* * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages @@ -454,13 +461,24 @@ static void update_and_free_page(struct hstate *h, struct page *page) __free_pages(page, huge_page_order(h)); } +struct hstate *size_to_hstate(unsigned long size) +{ + struct hstate *h; + + for_each_hstate(h) { + if (huge_page_size(h) == size) + return h; + } + return NULL; +} + static void free_huge_page(struct page *page) { /* * Can't pass hstate in here because it is called from the * compound page destructor. */ - struct hstate *h = &default_hstate; + struct hstate *h = page_hstate(page); int nid = page_to_nid(page); struct address_space *mapping; @@ -887,39 +905,94 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return page; } -static int __init hugetlb_init(void) +static void __init hugetlb_init_one_hstate(struct hstate *h) { unsigned long i; - struct hstate *h = &default_hstate; - - if (HPAGE_SHIFT == 0) - return 0; - - if (!h->order) { - h->order = HPAGE_SHIFT - PAGE_SHIFT; - h->mask = HPAGE_MASK; - } for (i = 0; i < MAX_NUMNODES; ++i) INIT_LIST_HEAD(&h->hugepage_freelists[i]); h->hugetlb_next_nid = first_node(node_online_map); - for (i = 0; i < max_huge_pages; ++i) { + for (i = 0; i < h->max_huge_pages; ++i) { if (!alloc_fresh_huge_page(h)) break; } - max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i; - printk(KERN_INFO "Total HugeTLB memory allocated, %ld\n", - h->free_huge_pages); + h->max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i; +} + +static void __init hugetlb_init_hstates(void) +{ + struct hstate *h; + + for_each_hstate(h) { + hugetlb_init_one_hstate(h); + } +} + +static void __init report_hugepages(void) +{ + struct hstate *h; + + for_each_hstate(h) { + printk(KERN_INFO "Total HugeTLB memory allocated, " + "%ld %dMB pages\n", + h->free_huge_pages, + 1 << (h->order + PAGE_SHIFT - 20)); + } +} + +static int __init hugetlb_init(void) +{ + BUILD_BUG_ON(HPAGE_SHIFT == 0); + + if (!size_to_hstate(HPAGE_SIZE)) { + hugetlb_add_hstate(HUGETLB_PAGE_ORDER); + parsed_hstate->max_huge_pages = default_hstate_max_huge_pages; + } + default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates; + + hugetlb_init_hstates(); + + report_hugepages(); + return 0; } module_init(hugetlb_init); +/* Should be called on processing a hugepagesz=... option */ +void __init hugetlb_add_hstate(unsigned order) +{ + struct hstate *h; + if (size_to_hstate(PAGE_SIZE << order)) { + printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n"); + return; + } + BUG_ON(max_hstate >= HUGE_MAX_HSTATE); + BUG_ON(order == 0); + h = &hstates[max_hstate++]; + h->order = order; + h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1); + hugetlb_init_one_hstate(h); + parsed_hstate = h; +} + static int __init hugetlb_setup(char *s) { - if (sscanf(s, "%lu", &max_huge_pages) <= 0) - max_huge_pages = 0; + unsigned long *mhp; + + /* + * !max_hstate means we haven't parsed a hugepagesz= parameter yet, + * so this hugepages= parameter goes to the "default hstate". + */ + if (!max_hstate) + mhp = &default_hstate_max_huge_pages; + else + mhp = &parsed_hstate->max_huge_pages; + + if (sscanf(s, "%lu", mhp) <= 0) + *mhp = 0; + return 1; } __setup("hugepages=", hugetlb_setup); @@ -950,7 +1023,7 @@ static void try_to_free_low(struct hstate *h, unsigned long count) if (PageHighMem(page)) continue; list_del(&page->lru); - update_and_free_page(page); + update_and_free_page(h, page); h->free_huge_pages--; h->free_huge_pages_node[page_to_nid(page)]--; } @@ -963,10 +1036,9 @@ static inline void try_to_free_low(struct hstate *h, unsigned long count) #endif #define persistent_huge_pages(h) (h->nr_huge_pages - h->surplus_huge_pages) -static unsigned long set_max_huge_pages(unsigned long count) +static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count) { unsigned long min_count, ret; - struct hstate *h = &default_hstate; /* * Increase the pool size @@ -1037,8 +1109,19 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) { + struct hstate *h = &default_hstate; + unsigned long tmp; + + if (!write) + tmp = h->max_huge_pages; + + table->data = &tmp; + table->maxlen = sizeof(unsigned long); proc_doulongvec_minmax(table, write, file, buffer, length, ppos); - max_huge_pages = set_max_huge_pages(max_huge_pages); + + if (write) + h->max_huge_pages = set_max_huge_pages(h, tmp); + return 0; } @@ -1059,10 +1142,21 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, size_t *length, loff_t *ppos) { struct hstate *h = &default_hstate; + unsigned long tmp; + + if (!write) + tmp = h->nr_overcommit_huge_pages; + + table->data = &tmp; + table->maxlen = sizeof(unsigned long); proc_doulongvec_minmax(table, write, file, buffer, length, ppos); - spin_lock(&hugetlb_lock); - h->nr_overcommit_huge_pages = sysctl_overcommit_huge_pages; - spin_unlock(&hugetlb_lock); + + if (write) { + spin_lock(&hugetlb_lock); + h->nr_overcommit_huge_pages = tmp; + spin_unlock(&hugetlb_lock); + } + return 0; } -- cgit v1.2.3 From a137e1cc6d6e7d315fef03962a2a5a113348b13b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:43 -0700 Subject: hugetlbfs: per mount huge page sizes Add the ability to configure the hugetlb hstate used on a per mount basis. - Add a new pagesize= option to the hugetlbfs mount that allows setting the page size - This option causes the mount code to find the hstate corresponding to the specified size, and sets up a pointer to the hstate in the mount's superblock. - Change the hstate accessors to use this information rather than the global_hstate they were using (requires a slight change in mm/memory.c so we don't NULL deref in the error-unmap path -- see comments). [np: take hstate out of hugetlbfs inode and vma->vm_private_data] Acked-by: Adam Litke Acked-by: Nishanth Aravamudan Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 45 ++++++++++++++++++++++++++++++++++++--------- include/linux/hugetlb.h | 14 +++++++++----- mm/hugetlb.c | 16 +++------------- mm/memory.c | 18 ++++++++++++++++-- 4 files changed, 64 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 516c581b5371..dbd01d262ca4 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -53,6 +53,7 @@ int sysctl_hugetlb_shm_group; enum { Opt_size, Opt_nr_inodes, Opt_mode, Opt_uid, Opt_gid, + Opt_pagesize, Opt_err, }; @@ -62,6 +63,7 @@ static match_table_t tokens = { {Opt_mode, "mode=%o"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, + {Opt_pagesize, "pagesize=%s"}, {Opt_err, NULL}, }; @@ -750,6 +752,8 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) char *p, *rest; substring_t args[MAX_OPT_ARGS]; int option; + unsigned long long size = 0; + enum { NO_SIZE, SIZE_STD, SIZE_PERCENT } setsize = NO_SIZE; if (!options) return 0; @@ -780,17 +784,13 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) break; case Opt_size: { - unsigned long long size; /* memparse() will accept a K/M/G without a digit */ if (!isdigit(*args[0].from)) goto bad_val; size = memparse(args[0].from, &rest); - if (*rest == '%') { - size <<= HPAGE_SHIFT; - size *= max_huge_pages; - do_div(size, 100); - } - pconfig->nr_blocks = (size >> HPAGE_SHIFT); + setsize = SIZE_STD; + if (*rest == '%') + setsize = SIZE_PERCENT; break; } @@ -801,6 +801,19 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) pconfig->nr_inodes = memparse(args[0].from, &rest); break; + case Opt_pagesize: { + unsigned long ps; + ps = memparse(args[0].from, &rest); + pconfig->hstate = size_to_hstate(ps); + if (!pconfig->hstate) { + printk(KERN_ERR + "hugetlbfs: Unsupported page size %lu MB\n", + ps >> 20); + return -EINVAL; + } + break; + } + default: printk(KERN_ERR "hugetlbfs: Bad mount option: \"%s\"\n", p); @@ -808,6 +821,18 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) break; } } + + /* Do size after hstate is set up */ + if (setsize > NO_SIZE) { + struct hstate *h = pconfig->hstate; + if (setsize == SIZE_PERCENT) { + size <<= huge_page_shift(h); + size *= h->max_huge_pages; + do_div(size, 100); + } + pconfig->nr_blocks = (size >> huge_page_shift(h)); + } + return 0; bad_val: @@ -832,6 +857,7 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) config.uid = current->fsuid; config.gid = current->fsgid; config.mode = 0755; + config.hstate = &default_hstate; ret = hugetlbfs_parse_options(data, &config); if (ret) return ret; @@ -840,14 +866,15 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) if (!sbinfo) return -ENOMEM; sb->s_fs_info = sbinfo; + sbinfo->hstate = config.hstate; spin_lock_init(&sbinfo->stat_lock); sbinfo->max_blocks = config.nr_blocks; sbinfo->free_blocks = config.nr_blocks; sbinfo->max_inodes = config.nr_inodes; sbinfo->free_inodes = config.nr_inodes; sb->s_maxbytes = MAX_LFS_FILESIZE; - sb->s_blocksize = HPAGE_SIZE; - sb->s_blocksize_bits = HPAGE_SHIFT; + sb->s_blocksize = huge_page_size(config.hstate); + sb->s_blocksize_bits = huge_page_shift(config.hstate); sb->s_magic = HUGETLBFS_MAGIC; sb->s_op = &hugetlbfs_ops; sb->s_time_gran = 1; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index b75bdb4deba3..ba9263e631b9 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -100,6 +100,7 @@ struct hugetlbfs_config { umode_t mode; long nr_blocks; long nr_inodes; + struct hstate *hstate; }; struct hugetlbfs_sb_info { @@ -108,6 +109,7 @@ struct hugetlbfs_sb_info { long max_inodes; /* inodes allowed */ long free_inodes; /* inodes free */ spinlock_t stat_lock; + struct hstate *hstate; }; @@ -191,19 +193,21 @@ extern unsigned int default_hstate_idx; #define default_hstate (hstates[default_hstate_idx]) -static inline struct hstate *hstate_vma(struct vm_area_struct *vma) +static inline struct hstate *hstate_inode(struct inode *i) { - return &default_hstate; + struct hugetlbfs_sb_info *hsb; + hsb = HUGETLBFS_SB(i->i_sb); + return hsb->hstate; } static inline struct hstate *hstate_file(struct file *f) { - return &default_hstate; + return hstate_inode(f->f_dentry->d_inode); } -static inline struct hstate *hstate_inode(struct inode *i) +static inline struct hstate *hstate_vma(struct vm_area_struct *vma) { - return &default_hstate; + return hstate_file(vma->vm_file); } static inline unsigned long huge_page_size(struct hstate *h) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 82378d44a0c5..4cf7a90e9140 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1439,19 +1439,9 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) { - /* - * It is undesirable to test vma->vm_file as it should be non-null - * for valid hugetlb area. However, vm_file will be NULL in the error - * cleanup path of do_mmap_pgoff. When hugetlbfs ->mmap method fails, - * do_mmap_pgoff() nullifies vma->vm_file before calling this function - * to clean up. Since no pte has actually been setup, it is safe to - * do nothing in this case. - */ - if (vma->vm_file) { - spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); - __unmap_hugepage_range(vma, start, end, ref_page); - spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock); - } + spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); + __unmap_hugepage_range(vma, start, end, ref_page); + spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock); } /* diff --git a/mm/memory.c b/mm/memory.c index c1c1d6d8c22b..02fc6b1047b0 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -901,9 +901,23 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, } if (unlikely(is_vm_hugetlb_page(vma))) { - unmap_hugepage_range(vma, start, end, NULL); - zap_work -= (end - start) / + /* + * It is undesirable to test vma->vm_file as it + * should be non-null for valid hugetlb area. + * However, vm_file will be NULL in the error + * cleanup path of do_mmap_pgoff. When + * hugetlbfs ->mmap method fails, + * do_mmap_pgoff() nullifies vma->vm_file + * before calling this function to clean up. + * Since no pte has actually been setup, it is + * safe to do nothing in this case. + */ + if (vma->vm_file) { + unmap_hugepage_range(vma, start, end, NULL); + zap_work -= (end - start) / pages_per_huge_page(hstate_vma(vma)); + } + start = end; } else start = unmap_page_range(*tlbp, vma, -- cgit v1.2.3 From a3437870160cf2caaac6bdd76c7377a5a4145a8c Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 23 Jul 2008 21:27:44 -0700 Subject: hugetlb: new sysfs interface Provide new hugepages user APIs that are more suited to multiple hstates in sysfs. There is a new directory, /sys/kernel/hugepages. Underneath that directory there will be a directory per-supported hugepage size, e.g.: /sys/kernel/hugepages/hugepages-64kB /sys/kernel/hugepages/hugepages-16384kB /sys/kernel/hugepages/hugepages-16777216kB corresponding to 64k, 16m and 16g respectively. Within each hugepages-size directory there are a number of files, corresponding to the tracked counters in the hstate, e.g.: /sys/kernel/hugepages/hugepages-64/nr_hugepages /sys/kernel/hugepages/hugepages-64/nr_overcommit_hugepages /sys/kernel/hugepages/hugepages-64/free_hugepages /sys/kernel/hugepages/hugepages-64/resv_hugepages /sys/kernel/hugepages/hugepages-64/surplus_hugepages Of these files, the first two are read-write and the latter three are read-only. The size of the hugepage being manipulated is trivially deducible from the enclosing directory and is always expressed in kB (to match meminfo). [dave@linux.vnet.ibm.com: fix build] [nacc@us.ibm.com: hugetlb: hang off of /sys/kernel/mm rather than /sys/kernel] [nacc@us.ibm.com: hugetlb: remove CONFIG_SYSFS dependency] Acked-by: Greg Kroah-Hartman Signed-off-by: Nishanth Aravamudan Signed-off-by: Nick Piggin Cc: Dave Hansen Signed-off-by: Nishanth Aravamudan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../ABI/testing/sysfs-kernel-mm-hugepages | 15 ++ Documentation/vm/hugetlbpage.txt | 23 ++ include/linux/hugetlb.h | 2 + mm/hugetlb.c | 288 ++++++++++++++++----- 4 files changed, 262 insertions(+), 66 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-kernel-mm-hugepages (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-hugepages b/Documentation/ABI/testing/sysfs-kernel-mm-hugepages new file mode 100644 index 000000000000..e21c00571cf4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-mm-hugepages @@ -0,0 +1,15 @@ +What: /sys/kernel/mm/hugepages/ +Date: June 2008 +Contact: Nishanth Aravamudan , hugetlb maintainers +Description: + /sys/kernel/mm/hugepages/ contains a number of subdirectories + of the form hugepages-kB, where is the page size + of the hugepages supported by the kernel/CPU combination. + + Under these directories are a number of files: + nr_hugepages + nr_overcommit_hugepages + free_hugepages + surplus_hugepages + resv_hugepages + See Documentation/vm/hugetlbpage.txt for details. diff --git a/Documentation/vm/hugetlbpage.txt b/Documentation/vm/hugetlbpage.txt index 3102b81bef88..8a5b5763f0fe 100644 --- a/Documentation/vm/hugetlbpage.txt +++ b/Documentation/vm/hugetlbpage.txt @@ -95,6 +95,29 @@ this condition holds, however, no more surplus huge pages will be allowed on the system until one of the two sysctls are increased sufficiently, or the surplus huge pages go out of use and are freed. +With support for multiple hugepage pools at run-time available, much of +the hugepage userspace interface has been duplicated in sysfs. The above +information applies to the default hugepage size (which will be +controlled by the proc interfaces for backwards compatibility). The root +hugepage control directory is + + /sys/kernel/mm/hugepages + +For each hugepage size supported by the running kernel, a subdirectory +will exist, of the form + + hugepages-${size}kB + +Inside each of these directories, the same set of files will exist: + + nr_hugepages + nr_overcommit_hugepages + free_hugepages + resv_hugepages + surplus_hugepages + +which function as described above for the default hugepage-sized case. + If the user applications are going to request hugepages using mmap system call, then it is required that system administrator mount a file system of type hugetlbfs: diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ba9263e631b9..58c0de32e7f0 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -164,6 +164,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, #ifdef CONFIG_HUGETLB_PAGE +#define HSTATE_NAME_LEN 32 /* Defines one hugetlb page size */ struct hstate { int hugetlb_next_nid; @@ -179,6 +180,7 @@ struct hstate { unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; unsigned int surplus_huge_pages_node[MAX_NUMNODES]; + char name[HSTATE_NAME_LEN]; }; void __init hugetlb_add_hstate(unsigned order); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4cf7a90e9140..bb49ce5d0067 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -942,72 +943,6 @@ static void __init report_hugepages(void) } } -static int __init hugetlb_init(void) -{ - BUILD_BUG_ON(HPAGE_SHIFT == 0); - - if (!size_to_hstate(HPAGE_SIZE)) { - hugetlb_add_hstate(HUGETLB_PAGE_ORDER); - parsed_hstate->max_huge_pages = default_hstate_max_huge_pages; - } - default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates; - - hugetlb_init_hstates(); - - report_hugepages(); - - return 0; -} -module_init(hugetlb_init); - -/* Should be called on processing a hugepagesz=... option */ -void __init hugetlb_add_hstate(unsigned order) -{ - struct hstate *h; - if (size_to_hstate(PAGE_SIZE << order)) { - printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n"); - return; - } - BUG_ON(max_hstate >= HUGE_MAX_HSTATE); - BUG_ON(order == 0); - h = &hstates[max_hstate++]; - h->order = order; - h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1); - hugetlb_init_one_hstate(h); - parsed_hstate = h; -} - -static int __init hugetlb_setup(char *s) -{ - unsigned long *mhp; - - /* - * !max_hstate means we haven't parsed a hugepagesz= parameter yet, - * so this hugepages= parameter goes to the "default hstate". - */ - if (!max_hstate) - mhp = &default_hstate_max_huge_pages; - else - mhp = &parsed_hstate->max_huge_pages; - - if (sscanf(s, "%lu", mhp) <= 0) - *mhp = 0; - - return 1; -} -__setup("hugepages=", hugetlb_setup); - -static unsigned int cpuset_mems_nr(unsigned int *array) -{ - int node; - unsigned int nr = 0; - - for_each_node_mask(node, cpuset_current_mems_allowed) - nr += array[node]; - - return nr; -} - #ifdef CONFIG_SYSCTL #ifdef CONFIG_HIGHMEM static void try_to_free_low(struct hstate *h, unsigned long count) @@ -1105,6 +1040,227 @@ out: return ret; } +#define HSTATE_ATTR_RO(_name) \ + static struct kobj_attribute _name##_attr = __ATTR_RO(_name) + +#define HSTATE_ATTR(_name) \ + static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +static struct kobject *hugepages_kobj; +static struct kobject *hstate_kobjs[HUGE_MAX_HSTATE]; + +static struct hstate *kobj_to_hstate(struct kobject *kobj) +{ + int i; + for (i = 0; i < HUGE_MAX_HSTATE; i++) + if (hstate_kobjs[i] == kobj) + return &hstates[i]; + BUG(); + return NULL; +} + +static ssize_t nr_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->nr_huge_pages); +} +static ssize_t nr_hugepages_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long input; + struct hstate *h = kobj_to_hstate(kobj); + + err = strict_strtoul(buf, 10, &input); + if (err) + return 0; + + h->max_huge_pages = set_max_huge_pages(h, input); + + return count; +} +HSTATE_ATTR(nr_hugepages); + +static ssize_t nr_overcommit_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->nr_overcommit_huge_pages); +} +static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long input; + struct hstate *h = kobj_to_hstate(kobj); + + err = strict_strtoul(buf, 10, &input); + if (err) + return 0; + + spin_lock(&hugetlb_lock); + h->nr_overcommit_huge_pages = input; + spin_unlock(&hugetlb_lock); + + return count; +} +HSTATE_ATTR(nr_overcommit_hugepages); + +static ssize_t free_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->free_huge_pages); +} +HSTATE_ATTR_RO(free_hugepages); + +static ssize_t resv_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->resv_huge_pages); +} +HSTATE_ATTR_RO(resv_hugepages); + +static ssize_t surplus_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->surplus_huge_pages); +} +HSTATE_ATTR_RO(surplus_hugepages); + +static struct attribute *hstate_attrs[] = { + &nr_hugepages_attr.attr, + &nr_overcommit_hugepages_attr.attr, + &free_hugepages_attr.attr, + &resv_hugepages_attr.attr, + &surplus_hugepages_attr.attr, + NULL, +}; + +static struct attribute_group hstate_attr_group = { + .attrs = hstate_attrs, +}; + +static int __init hugetlb_sysfs_add_hstate(struct hstate *h) +{ + int retval; + + hstate_kobjs[h - hstates] = kobject_create_and_add(h->name, + hugepages_kobj); + if (!hstate_kobjs[h - hstates]) + return -ENOMEM; + + retval = sysfs_create_group(hstate_kobjs[h - hstates], + &hstate_attr_group); + if (retval) + kobject_put(hstate_kobjs[h - hstates]); + + return retval; +} + +static void __init hugetlb_sysfs_init(void) +{ + struct hstate *h; + int err; + + hugepages_kobj = kobject_create_and_add("hugepages", mm_kobj); + if (!hugepages_kobj) + return; + + for_each_hstate(h) { + err = hugetlb_sysfs_add_hstate(h); + if (err) + printk(KERN_ERR "Hugetlb: Unable to add hstate %s", + h->name); + } +} + +static void __exit hugetlb_exit(void) +{ + struct hstate *h; + + for_each_hstate(h) { + kobject_put(hstate_kobjs[h - hstates]); + } + + kobject_put(hugepages_kobj); +} +module_exit(hugetlb_exit); + +static int __init hugetlb_init(void) +{ + BUILD_BUG_ON(HPAGE_SHIFT == 0); + + if (!size_to_hstate(HPAGE_SIZE)) { + hugetlb_add_hstate(HUGETLB_PAGE_ORDER); + parsed_hstate->max_huge_pages = default_hstate_max_huge_pages; + } + default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates; + + hugetlb_init_hstates(); + + report_hugepages(); + + hugetlb_sysfs_init(); + + return 0; +} +module_init(hugetlb_init); + +/* Should be called on processing a hugepagesz=... option */ +void __init hugetlb_add_hstate(unsigned order) +{ + struct hstate *h; + if (size_to_hstate(PAGE_SIZE << order)) { + printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n"); + return; + } + BUG_ON(max_hstate >= HUGE_MAX_HSTATE); + BUG_ON(order == 0); + h = &hstates[max_hstate++]; + h->order = order; + h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1); + snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", + huge_page_size(h)/1024); + hugetlb_init_one_hstate(h); + parsed_hstate = h; +} + +static int __init hugetlb_setup(char *s) +{ + unsigned long *mhp; + + /* + * !max_hstate means we haven't parsed a hugepagesz= parameter yet, + * so this hugepages= parameter goes to the "default hstate". + */ + if (!max_hstate) + mhp = &default_hstate_max_huge_pages; + else + mhp = &parsed_hstate->max_huge_pages; + + if (sscanf(s, "%lu", mhp) <= 0) + *mhp = 0; + + return 1; +} +__setup("hugepages=", hugetlb_setup); + +static unsigned int cpuset_mems_nr(unsigned int *array) +{ + int node; + unsigned int nr = 0; + + for_each_node_mask(node, cpuset_current_mems_allowed) + nr += array[node]; + + return nr; +} + int hugetlb_sysctl_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) -- cgit v1.2.3 From b54bbf7b81170f03597c17dd0b559e3006bc9868 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:45 -0700 Subject: mm: introduce non panic alloc_bootmem Straight forward variant of the existing __alloc_bootmem_node, only subsequent patch when allocating giant hugepages at boot -- don't want to panic if we can't allocate as many as the user asked for. Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 4 ++++ mm/bootmem.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index dd8fee6c46d9..f352c5f125b4 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -89,6 +89,10 @@ extern void *__alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); +extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal); extern unsigned long init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, diff --git a/mm/bootmem.c b/mm/bootmem.c index 4bc6ae2fbaa3..9ac972535fff 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -578,6 +578,18 @@ void * __init alloc_bootmem_section(unsigned long size, } #endif +void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, + unsigned long align, unsigned long goal) +{ + void *ptr; + + ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); + if (ptr) + return ptr; + + return __alloc_bootmem_nopanic(size, align, goal); +} + #ifndef ARCH_LOW_ADDRESS_LIMIT #define ARCH_LOW_ADDRESS_LIMIT 0xffffffffUL #endif -- cgit v1.2.3 From ceb868796181dc95ea01a110e123afd391639873 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:50 -0700 Subject: hugetlb: introduce pud_huge Straight forward extensions for huge pages located in the PUD instead of PMDs. Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Cc: Martin Schwidefsky Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/hugetlbpage.c | 6 ++++++ arch/powerpc/mm/hugetlbpage.c | 5 +++++ arch/s390/mm/hugetlbpage.c | 5 +++++ arch/sh/mm/hugetlbpage.c | 5 +++++ arch/sparc64/mm/hugetlbpage.c | 5 +++++ arch/x86/mm/hugetlbpage.c | 25 ++++++++++++++++++++++++- include/linux/hugetlb.h | 5 +++++ mm/hugetlb.c | 9 +++++++++ mm/memory.c | 15 +++++++++++---- 9 files changed, 75 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index 6170f097d255..c45fc7f5a979 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -107,6 +107,12 @@ int pmd_huge(pmd_t pmd) { return 0; } + +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) { diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index c94dc71af989..63db7adce717 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -369,6 +369,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 9162dc84f77f..f28c43d2f61d 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -120,6 +120,11 @@ int pmd_huge(pmd_t pmd) return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmdp, int write) { diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index 2f9dbe0ef4ac..9304117039c4 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c @@ -79,6 +79,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) { diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index 1307b23f6a76..f27d10369e0c 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -295,6 +295,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) { diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 52476fde8996..a4789e87a315 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -189,6 +189,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) @@ -209,6 +214,11 @@ int pmd_huge(pmd_t pmd) return !!(pmd_val(pmd) & _PAGE_PSE); } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) @@ -217,9 +227,22 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, page = pte_page(*(pte_t *)pmd); if (page) - page += ((address & ~HPAGE_MASK) >> PAGE_SHIFT); + page += ((address & ~PMD_MASK) >> PAGE_SHIFT); return page; } + +struct page * +follow_huge_pud(struct mm_struct *mm, unsigned long address, + pud_t *pud, int write) +{ + struct page *page; + + page = pte_page(*(pte_t *)pud); + if (page) + page += ((address & ~PUD_MASK) >> PAGE_SHIFT); + return page; +} + #endif /* x86_64 also uses this file */ diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 58c0de32e7f0..b2c17f62cacb 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -50,7 +50,10 @@ struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, int write); struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write); +struct page *follow_huge_pud(struct mm_struct *mm, unsigned long address, + pud_t *pud, int write); int pmd_huge(pmd_t pmd); +int pud_huge(pud_t pmd); void hugetlb_change_protection(struct vm_area_struct *vma, unsigned long address, unsigned long end, pgprot_t newprot); @@ -78,8 +81,10 @@ static inline unsigned long hugetlb_total_pages(void) #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL +#define follow_huge_pud(mm, addr, pud, write) NULL #define prepare_hugepage_range(file, addr, len) (-EINVAL) #define pmd_huge(x) 0 +#define pud_huge(x) 0 #define is_hugepage_only_range(mm, addr, len) 0 #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; }) #define hugetlb_fault(mm, vma, addr, write) ({ BUG(); 0; }) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0c74c14dd2f7..107c1ce223cb 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1996,6 +1996,15 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, return ret; } +/* Can be overriden by architectures */ +__attribute__((weak)) struct page * +follow_huge_pud(struct mm_struct *mm, unsigned long address, + pud_t *pud, int write) +{ + BUG(); + return NULL; +} + int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page **pages, struct vm_area_struct **vmas, unsigned long *position, int *length, int i, diff --git a/mm/memory.c b/mm/memory.c index 02fc6b1047b0..262e3eb6601a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -998,19 +998,24 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, goto no_page_table; pud = pud_offset(pgd, address); - if (pud_none(*pud) || unlikely(pud_bad(*pud))) + if (pud_none(*pud)) + goto no_page_table; + if (pud_huge(*pud)) { + BUG_ON(flags & FOLL_GET); + page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE); + goto out; + } + if (unlikely(pud_bad(*pud))) goto no_page_table; - + pmd = pmd_offset(pud, address); if (pmd_none(*pmd)) goto no_page_table; - if (pmd_huge(*pmd)) { BUG_ON(flags & FOLL_GET); page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE); goto out; } - if (unlikely(pmd_bad(*pmd))) goto no_page_table; @@ -1567,6 +1572,8 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud, unsigned long next; int err; + BUG_ON(pud_huge(*pud)); + pmd = pmd_alloc(mm, pud, addr); if (!pmd) return -ENOMEM; -- cgit v1.2.3 From 53ba51d21d6e048424ab8aadfebdb1f25ae07b60 Mon Sep 17 00:00:00 2001 From: Jon Tollefson Date: Wed, 23 Jul 2008 21:27:52 -0700 Subject: hugetlb: allow arch overridden hugepage allocation Allow alloc_bootmem_huge_page() to be overridden by architectures that can't always use bootmem. This requires huge_boot_pages to be available for use by this function. This is required for powerpc 16G pages, which have to be reserved prior to boot-time. The location of these pages are indicated in the device tree. Acked-by: Adam Litke Signed-off-by: Jon Tollefson Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 10 ++++++++++ mm/hugetlb.c | 11 +++-------- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index b2c17f62cacb..9a71d4cc88c8 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -39,6 +39,7 @@ void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); extern unsigned long hugepages_treat_as_movable; extern const unsigned long hugetlb_zero, hugetlb_infinity; extern int sysctl_hugetlb_shm_group; +extern struct list_head huge_boot_pages; /* arch callbacks */ @@ -188,6 +189,14 @@ struct hstate { char name[HSTATE_NAME_LEN]; }; +struct huge_bootmem_page { + struct list_head list; + struct hstate *hstate; +}; + +/* arch callback */ +int __init alloc_bootmem_huge_page(struct hstate *h); + void __init hugetlb_add_hstate(unsigned order); struct hstate *size_to_hstate(unsigned long size); @@ -256,6 +265,7 @@ static inline struct hstate *page_hstate(struct page *page) #else struct hstate {}; +#define alloc_bootmem_huge_page(h) NULL #define hstate_file(f) NULL #define hstate_vma(v) NULL #define hstate_inode(i) NULL diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 2a2f6e869401..3e1506b808a3 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -31,6 +31,8 @@ static int max_hstate; unsigned int default_hstate_idx; struct hstate hstates[HUGE_MAX_HSTATE]; +__initdata LIST_HEAD(huge_boot_pages); + /* for command line parsing */ static struct hstate * __initdata parsed_hstate; static unsigned long __initdata default_hstate_max_huge_pages; @@ -925,14 +927,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return page; } -static __initdata LIST_HEAD(huge_boot_pages); - -struct huge_bootmem_page { - struct list_head list; - struct hstate *hstate; -}; - -static int __init alloc_bootmem_huge_page(struct hstate *h) +__attribute__((weak)) int alloc_bootmem_huge_page(struct hstate *h) { struct huge_bootmem_page *m; int nr_nodes = nodes_weight(node_online_map); -- cgit v1.2.3 From 223e8dc9249c9e15f6c8b638d73fcad78ccb0a88 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:28:00 -0700 Subject: bootmem: reorder code to match new bootmem structure This only reorders functions so that further patches will be easier to read. No code changed. Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 86 ++++++------ mm/bootmem.c | 356 ++++++++++++++++++++++++------------------------ 2 files changed, 222 insertions(+), 220 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index f352c5f125b4..5000fd70b04f 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -41,36 +41,62 @@ typedef struct bootmem_data { extern bootmem_data_t bootmem_node_data[]; extern unsigned long bootmem_bootmap_pages(unsigned long); + +extern unsigned long init_bootmem_node(pg_data_t *pgdat, + unsigned long freepfn, + unsigned long startpfn, + unsigned long endpfn); extern unsigned long init_bootmem(unsigned long addr, unsigned long memend); + +extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); +extern unsigned long free_all_bootmem(void); + +extern void free_bootmem_node(pg_data_t *pgdat, + unsigned long addr, + unsigned long size); extern void free_bootmem(unsigned long addr, unsigned long size); -extern void *__alloc_bootmem(unsigned long size, + +/* + * Flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, + * the architecture-specific code should honor this). + * + * If flags is 0, then the return value is always 0 (success). If + * flags contains BOOTMEM_EXCLUSIVE, then -EBUSY is returned if the + * memory already was reserved. + */ +#define BOOTMEM_DEFAULT 0 +#define BOOTMEM_EXCLUSIVE (1<<0) + +extern int reserve_bootmem_node(pg_data_t *pgdat, + unsigned long physaddr, + unsigned long size, + int flags); +#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE +extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); +#endif + +extern void *__alloc_bootmem_nopanic(unsigned long size, unsigned long align, unsigned long goal); -extern void *__alloc_bootmem_nopanic(unsigned long size, +extern void *__alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal); extern void *__alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal); +extern void *__alloc_bootmem_node(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal); +extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal); extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); - -/* - * flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, - * the architecture-specific code should honor this) - */ -#define BOOTMEM_DEFAULT 0 -#define BOOTMEM_EXCLUSIVE (1<<0) - #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE -/* - * If flags is 0, then the return value is always 0 (success). If - * flags contains BOOTMEM_EXCLUSIVE, then -EBUSY is returned if the - * memory already was reserved. - */ -extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); #define alloc_bootmem(x) \ __alloc_bootmem(x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low(x) \ @@ -83,38 +109,16 @@ extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, int flags); -extern unsigned long free_all_bootmem(void); -extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); -extern void *__alloc_bootmem_node(pg_data_t *pgdat, - unsigned long size, - unsigned long align, - unsigned long goal); -extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, - unsigned long size, - unsigned long align, - unsigned long goal); -extern unsigned long init_bootmem_node(pg_data_t *pgdat, - unsigned long freepfn, - unsigned long startpfn, - unsigned long endpfn); -extern int reserve_bootmem_node(pg_data_t *pgdat, - unsigned long physaddr, - unsigned long size, - int flags); -extern void free_bootmem_node(pg_data_t *pgdat, - unsigned long addr, - unsigned long size); -extern void *alloc_bootmem_section(unsigned long size, - unsigned long section_nr); -#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE #define alloc_bootmem_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_pages_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages_node(pgdat, x) \ __alloc_bootmem_low_node(pgdat, x, PAGE_SIZE, 0) -#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ + +extern void *alloc_bootmem_section(unsigned long size, + unsigned long section_nr); #ifdef CONFIG_HAVE_ARCH_ALLOC_REMAP extern void *alloc_remap(int nid, unsigned long size); diff --git a/mm/bootmem.c b/mm/bootmem.c index 9ac972535fff..24eacf52c50e 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -38,6 +38,19 @@ unsigned long saved_max_pfn; bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata; +/* + * Given an initialised bdata, it returns the size of the boot bitmap + */ +static unsigned long __init get_mapsize(bootmem_data_t *bdata) +{ + unsigned long mapsize; + unsigned long start = PFN_DOWN(bdata->node_boot_start); + unsigned long end = bdata->node_low_pfn; + + mapsize = ((end - start) + 7) / 8; + return ALIGN(mapsize, sizeof(long)); +} + /* return the number of _pages_ that will be allocated for the boot bitmap */ unsigned long __init bootmem_bootmap_pages(unsigned long pages) { @@ -71,19 +84,6 @@ static void __init link_bootmem(bootmem_data_t *bdata) list_add_tail(&bdata->list, &bdata_list); } -/* - * Given an initialised bdata, it returns the size of the boot bitmap - */ -static unsigned long __init get_mapsize(bootmem_data_t *bdata) -{ - unsigned long mapsize; - unsigned long start = PFN_DOWN(bdata->node_boot_start); - unsigned long end = bdata->node_low_pfn; - - mapsize = ((end - start) + 7) / 8; - return ALIGN(mapsize, sizeof(long)); -} - /* * Called once to set up the allocator itself. */ @@ -108,6 +108,146 @@ static unsigned long __init init_bootmem_core(bootmem_data_t *bdata, return mapsize; } +unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, + unsigned long startpfn, unsigned long endpfn) +{ + return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn); +} + +unsigned long __init init_bootmem(unsigned long start, unsigned long pages) +{ + max_low_pfn = pages; + min_low_pfn = start; + return init_bootmem_core(NODE_DATA(0)->bdata, start, 0, pages); +} + +static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) +{ + struct page *page; + unsigned long pfn; + unsigned long i, count; + unsigned long idx; + unsigned long *map; + int gofast = 0; + + BUG_ON(!bdata->node_bootmem_map); + + count = 0; + /* first extant page of the node */ + pfn = PFN_DOWN(bdata->node_boot_start); + idx = bdata->node_low_pfn - pfn; + map = bdata->node_bootmem_map; + /* + * Check if we are aligned to BITS_PER_LONG pages. If so, we might + * be able to free page orders of that size at once. + */ + if (!(pfn & (BITS_PER_LONG-1))) + gofast = 1; + + for (i = 0; i < idx; ) { + unsigned long v = ~map[i / BITS_PER_LONG]; + + if (gofast && v == ~0UL) { + int order; + + page = pfn_to_page(pfn); + count += BITS_PER_LONG; + order = ffs(BITS_PER_LONG) - 1; + __free_pages_bootmem(page, order); + i += BITS_PER_LONG; + page += BITS_PER_LONG; + } else if (v) { + unsigned long m; + + page = pfn_to_page(pfn); + for (m = 1; m && i < idx; m<<=1, page++, i++) { + if (v & m) { + count++; + __free_pages_bootmem(page, 0); + } + } + } else { + i += BITS_PER_LONG; + } + pfn += BITS_PER_LONG; + } + + /* + * Now free the allocator bitmap itself, it's not + * needed anymore: + */ + page = virt_to_page(bdata->node_bootmem_map); + idx = (get_mapsize(bdata) + PAGE_SIZE-1) >> PAGE_SHIFT; + for (i = 0; i < idx; i++, page++) + __free_pages_bootmem(page, 0); + count += i; + bdata->node_bootmem_map = NULL; + + return count; +} + +unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) +{ + register_page_bootmem_info_node(pgdat); + return free_all_bootmem_core(pgdat->bdata); +} + +unsigned long __init free_all_bootmem(void) +{ + return free_all_bootmem_core(NODE_DATA(0)->bdata); +} + +static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, + unsigned long size) +{ + unsigned long sidx, eidx; + unsigned long i; + + BUG_ON(!size); + + /* out range */ + if (addr + size < bdata->node_boot_start || + PFN_DOWN(addr) > bdata->node_low_pfn) + return; + /* + * round down end of usable mem, partially free pages are + * considered reserved. + */ + + if (addr >= bdata->node_boot_start && addr < bdata->last_success) + bdata->last_success = addr; + + /* + * Round up to index to the range. + */ + if (PFN_UP(addr) > PFN_DOWN(bdata->node_boot_start)) + sidx = PFN_UP(addr) - PFN_DOWN(bdata->node_boot_start); + else + sidx = 0; + + eidx = PFN_DOWN(addr + size - bdata->node_boot_start); + if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) + eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); + + for (i = sidx; i < eidx; i++) { + if (unlikely(!test_and_clear_bit(i, bdata->node_bootmem_map))) + BUG(); + } +} + +void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, + unsigned long size) +{ + free_bootmem_core(pgdat->bdata, physaddr, size); +} + +void __init free_bootmem(unsigned long addr, unsigned long size) +{ + bootmem_data_t *bdata; + list_for_each_entry(bdata, &bdata_list, list) + free_bootmem_core(bdata, addr, size); +} + /* * Marks a particular physical memory range as unallocatable. Usable RAM * might be used for boot-time allocations - or it might get added @@ -183,43 +323,36 @@ static void __init reserve_bootmem_core(bootmem_data_t *bdata, } } -static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, - unsigned long size) +int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, + unsigned long size, int flags) { - unsigned long sidx, eidx; - unsigned long i; - - BUG_ON(!size); - - /* out range */ - if (addr + size < bdata->node_boot_start || - PFN_DOWN(addr) > bdata->node_low_pfn) - return; - /* - * round down end of usable mem, partially free pages are - * considered reserved. - */ - - if (addr >= bdata->node_boot_start && addr < bdata->last_success) - bdata->last_success = addr; + int ret; - /* - * Round up to index to the range. - */ - if (PFN_UP(addr) > PFN_DOWN(bdata->node_boot_start)) - sidx = PFN_UP(addr) - PFN_DOWN(bdata->node_boot_start); - else - sidx = 0; + ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); + if (ret < 0) + return -ENOMEM; + reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); + return 0; +} - eidx = PFN_DOWN(addr + size - bdata->node_boot_start); - if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) - eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); +#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE +int __init reserve_bootmem(unsigned long addr, unsigned long size, + int flags) +{ + bootmem_data_t *bdata; + int ret; - for (i = sidx; i < eidx; i++) { - if (unlikely(!test_and_clear_bit(i, bdata->node_bootmem_map))) - BUG(); + list_for_each_entry(bdata, &bdata_list, list) { + ret = can_reserve_bootmem_core(bdata, addr, size, flags); + if (ret < 0) + return ret; } + list_for_each_entry(bdata, &bdata_list, list) + reserve_bootmem_core(bdata, addr, size, flags); + + return 0; } +#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ /* * We 'merge' subsequent allocations to save space. We might 'lose' @@ -371,140 +504,6 @@ found: return ret; } -static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) -{ - struct page *page; - unsigned long pfn; - unsigned long i, count; - unsigned long idx; - unsigned long *map; - int gofast = 0; - - BUG_ON(!bdata->node_bootmem_map); - - count = 0; - /* first extant page of the node */ - pfn = PFN_DOWN(bdata->node_boot_start); - idx = bdata->node_low_pfn - pfn; - map = bdata->node_bootmem_map; - /* - * Check if we are aligned to BITS_PER_LONG pages. If so, we might - * be able to free page orders of that size at once. - */ - if (!(pfn & (BITS_PER_LONG-1))) - gofast = 1; - - for (i = 0; i < idx; ) { - unsigned long v = ~map[i / BITS_PER_LONG]; - - if (gofast && v == ~0UL) { - int order; - - page = pfn_to_page(pfn); - count += BITS_PER_LONG; - order = ffs(BITS_PER_LONG) - 1; - __free_pages_bootmem(page, order); - i += BITS_PER_LONG; - page += BITS_PER_LONG; - } else if (v) { - unsigned long m; - - page = pfn_to_page(pfn); - for (m = 1; m && i < idx; m<<=1, page++, i++) { - if (v & m) { - count++; - __free_pages_bootmem(page, 0); - } - } - } else { - i += BITS_PER_LONG; - } - pfn += BITS_PER_LONG; - } - - /* - * Now free the allocator bitmap itself, it's not - * needed anymore: - */ - page = virt_to_page(bdata->node_bootmem_map); - idx = (get_mapsize(bdata) + PAGE_SIZE-1) >> PAGE_SHIFT; - for (i = 0; i < idx; i++, page++) - __free_pages_bootmem(page, 0); - count += i; - bdata->node_bootmem_map = NULL; - - return count; -} - -unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, - unsigned long startpfn, unsigned long endpfn) -{ - return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn); -} - -int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, - unsigned long size, int flags) -{ - int ret; - - ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); - if (ret < 0) - return -ENOMEM; - reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); - - return 0; -} - -void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, - unsigned long size) -{ - free_bootmem_core(pgdat->bdata, physaddr, size); -} - -unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) -{ - register_page_bootmem_info_node(pgdat); - return free_all_bootmem_core(pgdat->bdata); -} - -unsigned long __init init_bootmem(unsigned long start, unsigned long pages) -{ - max_low_pfn = pages; - min_low_pfn = start; - return init_bootmem_core(NODE_DATA(0)->bdata, start, 0, pages); -} - -#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE -int __init reserve_bootmem(unsigned long addr, unsigned long size, - int flags) -{ - bootmem_data_t *bdata; - int ret; - - list_for_each_entry(bdata, &bdata_list, list) { - ret = can_reserve_bootmem_core(bdata, addr, size, flags); - if (ret < 0) - return ret; - } - list_for_each_entry(bdata, &bdata_list, list) - reserve_bootmem_core(bdata, addr, size, flags); - - return 0; -} -#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ - -void __init free_bootmem(unsigned long addr, unsigned long size) -{ - bootmem_data_t *bdata; - list_for_each_entry(bdata, &bdata_list, list) - free_bootmem_core(bdata, addr, size); -} - -unsigned long __init free_all_bootmem(void) -{ - return free_all_bootmem_core(NODE_DATA(0)->bdata); -} - void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align, unsigned long goal) { @@ -534,7 +533,6 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align, return NULL; } - void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal) { -- cgit v1.2.3 From 5f2809e69c7128f86316048221cf45146f69a4a0 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:28:05 -0700 Subject: bootmem: clean up alloc_bootmem_core alloc_bootmem_core has become quite nasty to read over time. This is a clean rewrite that keeps the semantics. bdata->last_pos has been dropped. bdata->last_success has been renamed to hint_idx and it is now an index relative to the node's range. Since further block searching might start at this index, it is now set to the end of a succeeded allocation rather than its beginning. bdata->last_offset has been renamed to last_end_off to be more clear that it represents the ending address of the last allocation relative to the node. [y-goto@jp.fujitsu.com: fix new alloc_bootmem_core()] Signed-off-by: Johannes Weiner Signed-off-by: Yasunori Goto Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 6 +- mm/bootmem.c | 212 +++++++++++++++++------------------------------- 2 files changed, 78 insertions(+), 140 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 5000fd70b04f..90921d10ffa2 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -31,10 +31,8 @@ typedef struct bootmem_data { unsigned long node_boot_start; unsigned long node_low_pfn; void *node_bootmem_map; - unsigned long last_offset; - unsigned long last_pos; - unsigned long last_success; /* Previous allocation point. To speed - * up searching */ + unsigned long last_end_off; + unsigned long hint_idx; struct list_head list; } bootmem_data_t; diff --git a/mm/bootmem.c b/mm/bootmem.c index 300d126ec533..94ea612deccf 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -242,8 +242,9 @@ static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, * considered reserved. */ - if (addr >= bdata->node_boot_start && addr < bdata->last_success) - bdata->last_success = addr; + if (addr >= bdata->node_boot_start && + PFN_DOWN(addr - bdata->node_boot_start) < bdata->hint_idx) + bdata->hint_idx = PFN_DOWN(addr - bdata->node_boot_start); /* * Round up to index to the range. @@ -431,36 +432,16 @@ int __init reserve_bootmem(unsigned long addr, unsigned long size, } #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ -/* - * We 'merge' subsequent allocations to save space. We might 'lose' - * some fraction of a page if allocations cannot be satisfied due to - * size constraints on boxes where there is physical RAM space - * fragmentation - in these cases (mostly large memory boxes) this - * is not a problem. - * - * On low memory boxes we get it right in 100% of the cases. - * - * alignment has to be a power of 2 value. - * - * NOTE: This function is _not_ reentrant. - */ -static void * __init -alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, - unsigned long align, unsigned long goal, unsigned long limit) +static void * __init alloc_bootmem_core(struct bootmem_data *bdata, + unsigned long size, unsigned long align, + unsigned long goal, unsigned long limit) { - unsigned long areasize, preferred; - unsigned long i, start = 0, incr, eidx, end_pfn; - void *ret; - unsigned long node_boot_start; - void *node_bootmem_map; - - if (!size) { - printk("alloc_bootmem_core(): zero-sized request\n"); - BUG(); - } - BUG_ON(align & (align-1)); + unsigned long min, max, start, sidx, midx, step; + + BUG_ON(!size); + BUG_ON(align & (align - 1)); + BUG_ON(limit && goal + size > limit); - /* on nodes without memory - bootmem_map is NULL */ if (!bdata->node_bootmem_map) return NULL; @@ -468,126 +449,85 @@ alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT, align, goal, limit); - /* bdata->node_boot_start is supposed to be (12+6)bits alignment on x86_64 ? */ - node_boot_start = bdata->node_boot_start; - node_bootmem_map = bdata->node_bootmem_map; - if (align) { - node_boot_start = ALIGN(bdata->node_boot_start, align); - if (node_boot_start > bdata->node_boot_start) - node_bootmem_map = (unsigned long *)bdata->node_bootmem_map + - PFN_DOWN(node_boot_start - bdata->node_boot_start)/BITS_PER_LONG; - } + min = PFN_DOWN(bdata->node_boot_start); + max = bdata->node_low_pfn; - if (limit && node_boot_start >= limit) + goal >>= PAGE_SHIFT; + limit >>= PAGE_SHIFT; + + if (limit && max > limit) + max = limit; + if (max <= min) return NULL; - end_pfn = bdata->node_low_pfn; - limit = PFN_DOWN(limit); - if (limit && end_pfn > limit) - end_pfn = limit; + step = max(align >> PAGE_SHIFT, 1UL); - eidx = end_pfn - PFN_DOWN(node_boot_start); + if (goal && min < goal && goal < max) + start = ALIGN(goal, step); + else + start = ALIGN(min, step); - /* - * We try to allocate bootmem pages above 'goal' - * first, then we try to allocate lower pages. - */ - preferred = 0; - if (goal && PFN_DOWN(goal) < end_pfn) { - if (goal > node_boot_start) - preferred = goal - node_boot_start; - - if (bdata->last_success > node_boot_start && - bdata->last_success - node_boot_start >= preferred) - if (!limit || (limit && limit > bdata->last_success)) - preferred = bdata->last_success - node_boot_start; - } + sidx = start - PFN_DOWN(bdata->node_boot_start); + midx = max - PFN_DOWN(bdata->node_boot_start); - preferred = PFN_DOWN(ALIGN(preferred, align)); - areasize = (size + PAGE_SIZE-1) / PAGE_SIZE; - incr = align >> PAGE_SHIFT ? : 1; + if (bdata->hint_idx > sidx) { + /* Make sure we retry on failure */ + goal = 1; + sidx = ALIGN(bdata->hint_idx, step); + } -restart_scan: - for (i = preferred; i < eidx;) { - unsigned long j; + while (1) { + int merge; + void *region; + unsigned long eidx, i, start_off, end_off; +find_block: + sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx); + sidx = ALIGN(sidx, step); + eidx = sidx + PFN_UP(size); - i = find_next_zero_bit(node_bootmem_map, eidx, i); - i = ALIGN(i, incr); - if (i >= eidx) + if (sidx >= midx || eidx > midx) break; - if (test_bit(i, node_bootmem_map)) { - i += incr; - continue; - } - for (j = i + 1; j < i + areasize; ++j) { - if (j >= eidx) - goto fail_block; - if (test_bit(j, node_bootmem_map)) - goto fail_block; - } - start = i; - goto found; - fail_block: - i = ALIGN(j, incr); - if (i == j) - i += incr; - } - - if (preferred > 0) { - preferred = 0; - goto restart_scan; - } - return NULL; -found: - bdata->last_success = PFN_PHYS(start) + node_boot_start; - BUG_ON(start >= eidx); + for (i = sidx; i < eidx; i++) + if (test_bit(i, bdata->node_bootmem_map)) { + sidx = ALIGN(i, step); + if (sidx == i) + sidx += step; + goto find_block; + } - /* - * Is the next page of the previous allocation-end the start - * of this allocation's buffer? If yes then we can 'merge' - * the previous partial page with this allocation. - */ - if (align < PAGE_SIZE && - bdata->last_offset && bdata->last_pos+1 == start) { - unsigned long offset, remaining_size; - offset = ALIGN(bdata->last_offset, align); - BUG_ON(offset > PAGE_SIZE); - remaining_size = PAGE_SIZE - offset; - if (size < remaining_size) { - areasize = 0; - /* last_pos unchanged */ - bdata->last_offset = offset + size; - ret = phys_to_virt(bdata->last_pos * PAGE_SIZE + - offset + node_boot_start); - } else { - remaining_size = size - remaining_size; - areasize = (remaining_size + PAGE_SIZE-1) / PAGE_SIZE; - ret = phys_to_virt(bdata->last_pos * PAGE_SIZE + - offset + node_boot_start); - bdata->last_pos = start + areasize - 1; - bdata->last_offset = remaining_size; - } - bdata->last_offset &= ~PAGE_MASK; - } else { - bdata->last_pos = start + areasize - 1; - bdata->last_offset = size & ~PAGE_MASK; - ret = phys_to_virt(start * PAGE_SIZE + node_boot_start); + if (bdata->last_end_off && + PFN_DOWN(bdata->last_end_off) + 1 == sidx) + start_off = ALIGN(bdata->last_end_off, align); + else + start_off = PFN_PHYS(sidx); + + merge = PFN_DOWN(start_off) < sidx; + end_off = start_off + size; + + bdata->last_end_off = end_off; + bdata->hint_idx = PFN_UP(end_off); + + /* + * Reserve the area now: + */ + for (i = PFN_DOWN(start_off) + merge; + i < PFN_UP(end_off); i++) + if (test_and_set_bit(i, bdata->node_bootmem_map)) + BUG(); + + region = phys_to_virt(bdata->node_boot_start + start_off); + memset(region, 0, size); + return region; } - bdebug("nid=%td start=%lx end=%lx\n", - bdata - bootmem_node_data, - start + PFN_DOWN(bdata->node_boot_start), - start + areasize + PFN_DOWN(bdata->node_boot_start)); + if (goal) { + goal = 0; + sidx = 0; + goto find_block; + } - /* - * Reserve the area now: - */ - for (i = start; i < start + areasize; i++) - if (unlikely(test_and_set_bit(i, node_bootmem_map))) - BUG(); - memset(ret, 0, size); - return ret; + return NULL; } /** -- cgit v1.2.3 From 3560e249abda6bee41a07a7bf0383a6e193e2839 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:28:09 -0700 Subject: bootmem: replace node_boot_start in struct bootmem_data Almost all users of this field need a PFN instead of a physical address, so replace node_boot_start with node_min_pfn. [Lee.Schermerhorn@hp.com: fix spurious BUG_ON() in mark_bootmem()] Signed-off-by: Johannes Weiner Cc: Signed-off-by: Lee Schermerhorn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/numa.c | 2 +- arch/arm/plat-omap/fb.c | 4 +--- arch/avr32/mm/init.c | 3 +-- arch/ia64/mm/discontig.c | 19 ++++++++++--------- arch/m32r/mm/discontig.c | 3 +-- arch/m32r/mm/init.c | 4 +--- arch/mn10300/mm/init.c | 6 +++--- arch/sh/mm/init.c | 2 +- include/linux/bootmem.h | 2 +- mm/bootmem.c | 40 +++++++++++++++++++++------------------- 10 files changed, 41 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index def0c74a78a8..d8c4ceaf00b9 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -304,7 +304,7 @@ void __init paging_init(void) for_each_online_node(nid) { bootmem_data_t *bdata = &bootmem_node_data[nid]; - unsigned long start_pfn = bdata->node_boot_start >> PAGE_SHIFT; + unsigned long start_pfn = bdata->node_min_pfn; unsigned long end_pfn = bdata->node_low_pfn; if (dma_local_pfn >= end_pfn - start_pfn) diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 7854f19b77cf..96d6f0619733 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -182,7 +182,7 @@ void __init omapfb_reserve_sdram(void) return; bdata = NODE_DATA(0)->bdata; - sdram_start = bdata->node_boot_start; + sdram_start = bdata->node_min_pfn << PAGE_SHIFT; sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; reserved = 0; for (i = 0; ; i++) { @@ -340,5 +340,3 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart, #endif - - diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 786de88a82a7..3c85fdaa9487 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -119,8 +119,7 @@ void __init paging_init(void) unsigned long zones_size[MAX_NR_ZONES]; unsigned long low, start_pfn; - start_pfn = pgdat->bdata->node_boot_start; - start_pfn >>= PAGE_SHIFT; + start_pfn = pgdat->bdata->node_min_pfn; low = pgdat->bdata->node_low_pfn; memset(zones_size, 0, sizeof(zones_size)); diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 2fcf8464331e..d83125e1ed27 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -74,17 +74,17 @@ pg_data_t *pgdat_list[MAX_NUMNODES]; static int __init build_node_maps(unsigned long start, unsigned long len, int node) { - unsigned long cstart, epfn, end = start + len; + unsigned long spfn, epfn, end = start + len; struct bootmem_data *bdp = &bootmem_node_data[node]; epfn = GRANULEROUNDUP(end) >> PAGE_SHIFT; - cstart = GRANULEROUNDDOWN(start); + spfn = GRANULEROUNDDOWN(start) >> PAGE_SHIFT; if (!bdp->node_low_pfn) { - bdp->node_boot_start = cstart; + bdp->node_min_pfn = spfn; bdp->node_low_pfn = epfn; } else { - bdp->node_boot_start = min(cstart, bdp->node_boot_start); + bdp->node_min_pfn = min(spfn, bdp->node_min_pfn); bdp->node_low_pfn = max(epfn, bdp->node_low_pfn); } @@ -221,20 +221,21 @@ static void __init fill_pernode(int node, unsigned long pernode, static int __init find_pernode_space(unsigned long start, unsigned long len, int node) { - unsigned long epfn; + unsigned long spfn, epfn; unsigned long pernodesize = 0, pernode, pages, mapsize; struct bootmem_data *bdp = &bootmem_node_data[node]; + spfn = start >> PAGE_SHIFT; epfn = (start + len) >> PAGE_SHIFT; - pages = bdp->node_low_pfn - (bdp->node_boot_start >> PAGE_SHIFT); + pages = bdp->node_low_pfn - bdp->node_min_pfn; mapsize = bootmem_bootmap_pages(pages) << PAGE_SHIFT; /* * Make sure this memory falls within this node's usable memory * since we may have thrown some away in build_maps(). */ - if (start < bdp->node_boot_start || epfn > bdp->node_low_pfn) + if (spfn < bdp->node_min_pfn || epfn > bdp->node_low_pfn) return 0; /* Don't setup this node's local space twice... */ @@ -296,7 +297,7 @@ static void __init reserve_pernode_space(void) bdp = pdp->bdata; /* First the bootmem_map itself */ - pages = bdp->node_low_pfn - (bdp->node_boot_start>>PAGE_SHIFT); + pages = bdp->node_low_pfn - bdp->node_min_pfn; size = bootmem_bootmap_pages(pages) << PAGE_SHIFT; base = __pa(bdp->node_bootmem_map); reserve_bootmem_node(pdp, base, size, BOOTMEM_DEFAULT); @@ -466,7 +467,7 @@ void __init find_memory(void) init_bootmem_node(pgdat_list[node], map>>PAGE_SHIFT, - bdp->node_boot_start>>PAGE_SHIFT, + bdp->node_min_pfn, bdp->node_low_pfn); } diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index cc23934bc41e..cbc3c4c54566 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -123,8 +123,7 @@ unsigned long __init setup_memory(void) return max_low_pfn; } -#define START_PFN(nid) \ - (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define START_PFN(nid) (NODE_DATA(nid)->bdata->node_min_pfn) #define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) unsigned long __init zone_sizes_init(void) diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index 28799af15e95..2554eb59cfef 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -93,8 +93,7 @@ void free_initrd_mem(unsigned long, unsigned long); #endif /* It'd be good if these lines were in the standard header file. */ -#define START_PFN(nid) \ - (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define START_PFN(nid) (NODE_DATA(nid)->bdata->node_min_pfn) #define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) #ifndef CONFIG_DISCONTIGMEM @@ -252,4 +251,3 @@ void free_initrd_mem(unsigned long start, unsigned long end) printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); } #endif - diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c index 8c5d88c7b90a..8cee387a24fd 100644 --- a/arch/mn10300/mm/init.c +++ b/arch/mn10300/mm/init.c @@ -67,8 +67,8 @@ void __init paging_init(void) /* declare the sizes of the RAM zones (only use the normal zone) */ zones_size[ZONE_NORMAL] = - (contig_page_data.bdata->node_low_pfn) - - (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT); + contig_page_data.bdata->node_low_pfn - + contig_page_data.bdata->node_min_pfn; /* pass the memory from the bootmem allocator to the main allocator */ free_area_init(zones_size); @@ -87,7 +87,7 @@ void __init mem_init(void) if (!mem_map) BUG(); -#define START_PFN (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT) +#define START_PFN (contig_page_data.bdata->node_min_pfn) #define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index d7df26bd1e54..d652d375eb1e 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -191,7 +191,7 @@ void __init paging_init(void) pg_data_t *pgdat = NODE_DATA(nid); unsigned long low, start_pfn; - start_pfn = pgdat->bdata->node_boot_start >> PAGE_SHIFT; + start_pfn = pgdat->bdata->node_min_pfn; low = pgdat->bdata->node_low_pfn; if (max_zone_pfns[ZONE_NORMAL] < low) diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 90921d10ffa2..4ddf2922fc8d 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -28,7 +28,7 @@ extern unsigned long saved_max_pfn; * memory pages (including holes) on the node. */ typedef struct bootmem_data { - unsigned long node_boot_start; + unsigned long node_min_pfn; unsigned long node_low_pfn; void *node_bootmem_map; unsigned long last_end_off; diff --git a/mm/bootmem.c b/mm/bootmem.c index 282b786c2b15..4af15d0340ad 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -80,7 +80,7 @@ static void __init link_bootmem(bootmem_data_t *bdata) bootmem_data_t *ent; ent = list_entry(iter, bootmem_data_t, list); - if (bdata->node_boot_start < ent->node_boot_start) + if (bdata->node_min_pfn < ent->node_min_pfn) break; } list_add_tail(&bdata->list, iter); @@ -96,7 +96,7 @@ static unsigned long __init init_bootmem_core(bootmem_data_t *bdata, mminit_validate_memmodel_limits(&start, &end); bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart)); - bdata->node_boot_start = PFN_PHYS(start); + bdata->node_min_pfn = start; bdata->node_low_pfn = end; link_bootmem(bdata); @@ -151,7 +151,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) if (!bdata->node_bootmem_map) return 0; - start = PFN_DOWN(bdata->node_boot_start); + start = bdata->node_min_pfn; end = bdata->node_low_pfn; /* @@ -167,7 +167,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) unsigned long *map, idx, vec; map = bdata->node_bootmem_map; - idx = start - PFN_DOWN(bdata->node_boot_start); + idx = start - bdata->node_min_pfn; vec = ~map[idx / BITS_PER_LONG]; if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) { @@ -192,7 +192,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) } page = virt_to_page(bdata->node_bootmem_map); - pages = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); + pages = bdata->node_low_pfn - bdata->node_min_pfn; pages = bootmem_bootmap_pages(pages); count += pages; while (pages--) @@ -231,8 +231,8 @@ static void __init __free(bootmem_data_t *bdata, unsigned long idx; bdebug("nid=%td start=%lx end=%lx\n", bdata - bootmem_node_data, - sidx + PFN_DOWN(bdata->node_boot_start), - eidx + PFN_DOWN(bdata->node_boot_start)); + sidx + bdata->node_min_pfn, + eidx + bdata->node_min_pfn); if (bdata->hint_idx > sidx) bdata->hint_idx = sidx; @@ -250,8 +250,8 @@ static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx, bdebug("nid=%td start=%lx end=%lx flags=%x\n", bdata - bootmem_node_data, - sidx + PFN_DOWN(bdata->node_boot_start), - eidx + PFN_DOWN(bdata->node_boot_start), + sidx + bdata->node_min_pfn, + eidx + bdata->node_min_pfn, flags); for (idx = sidx; idx < eidx; idx++) @@ -261,7 +261,7 @@ static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx, return -EBUSY; } bdebug("silent double reserve of PFN %lx\n", - idx + PFN_DOWN(bdata->node_boot_start)); + idx + bdata->node_min_pfn); } return 0; } @@ -275,11 +275,11 @@ static int __init mark_bootmem_node(bootmem_data_t *bdata, bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n", bdata - bootmem_node_data, start, end, reserve, flags); - BUG_ON(start < PFN_DOWN(bdata->node_boot_start)); + BUG_ON(start < bdata->node_min_pfn); BUG_ON(end > bdata->node_low_pfn); - sidx = start - PFN_DOWN(bdata->node_boot_start); - eidx = end - PFN_DOWN(bdata->node_boot_start); + sidx = start - bdata->node_min_pfn; + eidx = end - bdata->node_min_pfn; if (reserve) return __reserve(bdata, sidx, eidx, flags); @@ -299,7 +299,8 @@ static int __init mark_bootmem(unsigned long start, unsigned long end, int err; unsigned long max; - if (pos < PFN_DOWN(bdata->node_boot_start)) { + if (pos < bdata->node_min_pfn || + pos >= bdata->node_low_pfn) { BUG_ON(pos != start); continue; } @@ -422,7 +423,7 @@ static void * __init alloc_bootmem_core(struct bootmem_data *bdata, bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT, align, goal, limit); - min = PFN_DOWN(bdata->node_boot_start); + min = bdata->node_min_pfn; max = bdata->node_low_pfn; goal >>= PAGE_SHIFT; @@ -440,8 +441,8 @@ static void * __init alloc_bootmem_core(struct bootmem_data *bdata, else start = ALIGN(min, step); - sidx = start - PFN_DOWN(bdata->node_boot_start); - midx = max - PFN_DOWN(bdata->node_boot_start); + sidx = start - bdata->node_min_pfn;; + midx = max - bdata->node_min_pfn; if (bdata->hint_idx > sidx) { /* @@ -491,7 +492,8 @@ find_block: PFN_UP(end_off), BOOTMEM_EXCLUSIVE)) BUG(); - region = phys_to_virt(bdata->node_boot_start + start_off); + region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) + + start_off); memset(region, 0, size); return region; } @@ -518,7 +520,7 @@ restart: if (goal && bdata->node_low_pfn <= PFN_DOWN(goal)) continue; - if (limit && bdata->node_boot_start >= limit) + if (limit && bdata->node_min_pfn >= PFN_DOWN(limit)) break; region = alloc_bootmem_core(bdata, size, align, goal, limit); -- cgit v1.2.3 From 2be0ffe2b29bd31d3debd0877797892ff2d91f4c Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 23 Jul 2008 21:28:11 -0700 Subject: mm: add alloc_pages_exact() and free_pages_exact() alloc_pages_exact() is similar to alloc_pages(), except that it allocates the minimum number of pages to fulfill the request. This is useful if you want to allocate a very large buffer that is slightly larger than an even power-of-two number of pages. In that case, alloc_pages() will waste a lot of memory. I have a video driver that wants to allocate a 5MB buffer. alloc_pages() wiill waste 3MB of physically-contiguous memory. Signed-off-by: Timur Tabi Cc: Andi Kleen Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 3 +++ mm/page_alloc.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'include/linux') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index f640ed241422..e8003afeffba 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -228,6 +228,9 @@ extern struct page *alloc_page_vma(gfp_t gfp_mask, extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); extern unsigned long get_zeroed_page(gfp_t gfp_mask); +void *alloc_pages_exact(size_t size, gfp_t gfp_mask); +void free_pages_exact(void *virt, size_t size); + #define __get_free_page(gfp_mask) \ __get_free_pages((gfp_mask),0) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index eaa86671ebbd..8d528d57b403 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1697,6 +1697,59 @@ void free_pages(unsigned long addr, unsigned int order) EXPORT_SYMBOL(free_pages); +/** + * alloc_pages_exact - allocate an exact number physically-contiguous pages. + * @size: the number of bytes to allocate + * @gfp_mask: GFP flags for the allocation + * + * This function is similar to alloc_pages(), except that it allocates the + * minimum number of pages to satisfy the request. alloc_pages() can only + * allocate memory in power-of-two pages. + * + * This function is also limited by MAX_ORDER. + * + * Memory allocated by this function must be released by free_pages_exact(). + */ +void *alloc_pages_exact(size_t size, gfp_t gfp_mask) +{ + unsigned int order = get_order(size); + unsigned long addr; + + addr = __get_free_pages(gfp_mask, order); + if (addr) { + unsigned long alloc_end = addr + (PAGE_SIZE << order); + unsigned long used = addr + PAGE_ALIGN(size); + + split_page(virt_to_page(addr), order); + while (used < alloc_end) { + free_page(used); + used += PAGE_SIZE; + } + } + + return (void *)addr; +} +EXPORT_SYMBOL(alloc_pages_exact); + +/** + * free_pages_exact - release memory allocated via alloc_pages_exact() + * @virt: the value returned by alloc_pages_exact. + * @size: size of allocation, same value as passed to alloc_pages_exact(). + * + * Release the memory allocated by a previous call to alloc_pages_exact. + */ +void free_pages_exact(void *virt, size_t size) +{ + unsigned long addr = (unsigned long)virt; + unsigned long end = addr + PAGE_ALIGN(size); + + while (addr < end) { + free_page(addr); + addr += PAGE_SIZE; + } +} +EXPORT_SYMBOL(free_pages_exact); + static unsigned int nr_free_zone_pages(int offset) { struct zoneref *z; -- cgit v1.2.3 From 27ac792ca0b0a1e7e65f20342260650516c95864 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 23 Jul 2008 21:28:13 -0700 Subject: PAGE_ALIGN(): correctly handle 64-bit values on 32-bit architectures On 32-bit architectures PAGE_ALIGN() truncates 64-bit values to the 32-bit boundary. For example: u64 val = PAGE_ALIGN(size); always returns a value < 4GB even if size is greater than 4GB. The problem resides in PAGE_MASK definition (from include/asm-x86/page.h for example): #define PAGE_SHIFT 12 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) ... #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) The "~" is performed on a 32-bit value, so everything in "and" with PAGE_MASK greater than 4GB will be truncated to the 32-bit boundary. Using the ALIGN() macro seems to be the right way, because it uses typeof(addr) for the mask. Also move the PAGE_ALIGN() definitions out of include/asm-*/page.h in include/linux/mm.h. See also lkml discussion: http://lkml.org/lkml/2008/6/11/237 [akpm@linux-foundation.org: fix drivers/media/video/uvc/uvc_queue.c] [akpm@linux-foundation.org: fix v850] [akpm@linux-foundation.org: fix powerpc] [akpm@linux-foundation.org: fix arm] [akpm@linux-foundation.org: fix mips] [akpm@linux-foundation.org: fix drivers/media/video/pvrusb2/pvrusb2-dvb.c] [akpm@linux-foundation.org: fix drivers/mtd/maps/uclinux.c] [akpm@linux-foundation.org: fix powerpc] Signed-off-by: Andrea Righi Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/kernel/module.c | 1 + arch/arm/plat-omap/fb.c | 1 + arch/avr32/mm/ioremap.c | 1 + arch/h8300/kernel/setup.c | 1 + arch/m68k/amiga/chipram.c | 1 + arch/m68knommu/kernel/setup.c | 1 + arch/mips/kernel/module.c | 1 + arch/mips/sgi-ip27/ip27-klnuma.c | 1 + arch/powerpc/kernel/suspend.c | 1 + arch/powerpc/lib/code-patching.c | 1 + arch/sparc64/kernel/iommu_common.h | 2 +- arch/x86/kernel/module_64.c | 1 + arch/xtensa/kernel/setup.c | 1 + drivers/char/random.c | 1 + drivers/ieee1394/iso.c | 1 + drivers/media/video/pvrusb2/pvrusb2-dvb.c | 1 + drivers/media/video/pvrusb2/pvrusb2-ioread.c | 1 + drivers/media/video/uvc/uvc_queue.c | 1 + drivers/media/video/videobuf-core.c | 1 + drivers/mtd/maps/uclinux.c | 1 + drivers/net/mlx4/eq.c | 1 + drivers/pcmcia/electra_cf.c | 1 + drivers/scsi/sun_esp.c | 1 + drivers/video/acornfb.c | 1 + drivers/video/imxfb.c | 1 + drivers/video/omap/dispc.c | 1 + drivers/video/omap/omapfb_main.c | 1 + drivers/video/pxafb.c | 1 + drivers/video/sa1100fb.c | 1 + include/asm-alpha/page.h | 3 --- include/asm-arm/page-nommu.h | 4 +--- include/asm-arm/page.h | 3 --- include/asm-avr32/page.h | 3 --- include/asm-blackfin/page.h | 3 --- include/asm-cris/page.h | 3 --- include/asm-frv/page.h | 3 --- include/asm-h8300/page.h | 3 --- include/asm-ia64/page.h | 1 - include/asm-m32r/page.h | 3 --- include/asm-m68k/dvma.h | 2 +- include/asm-m68k/page.h | 3 --- include/asm-m68knommu/page.h | 3 --- include/asm-mips/page.h | 3 --- include/asm-mips/processor.h | 2 +- include/asm-mn10300/page.h | 3 --- include/asm-parisc/page.h | 4 ---- include/asm-powerpc/page.h | 3 --- include/asm-s390/page.h | 3 --- include/asm-sh/page.h | 3 --- include/asm-sparc/page_32.h | 3 --- include/asm-sparc/page_64.h | 3 --- include/asm-um/page.h | 3 --- include/asm-v850/page.h | 4 ---- include/asm-x86/page.h | 3 --- include/asm-xtensa/page.h | 2 -- include/linux/mm.h | 3 +++ sound/core/info.c | 1 + 57 files changed, 36 insertions(+), 74 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 79b7e5cf5416..a68259a0cccd 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 96d6f0619733..5d107520e6b9 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/arch/avr32/mm/ioremap.c b/arch/avr32/mm/ioremap.c index 3437c82434ac..f03b79f0e0ab 100644 --- a/arch/avr32/mm/ioremap.c +++ b/arch/avr32/mm/ioremap.c @@ -6,6 +6,7 @@ * published by the Free Software Foundation. */ #include +#include #include #include diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c index b1f25c20a5db..7fda657110eb 100644 --- a/arch/h8300/kernel/setup.c +++ b/arch/h8300/kernel/setup.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/m68k/amiga/chipram.c b/arch/m68k/amiga/chipram.c index cbe36538af47..61df1d33c050 100644 --- a/arch/m68k/amiga/chipram.c +++ b/arch/m68k/amiga/chipram.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/arch/m68knommu/kernel/setup.c b/arch/m68knommu/kernel/setup.c index 03f4fe6a2fc0..5985f1989021 100644 --- a/arch/m68knommu/kernel/setup.c +++ b/arch/m68knommu/kernel/setup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c index e7ed0ac48537..1f60e27523d9 100644 --- a/arch/mips/kernel/module.c +++ b/arch/mips/kernel/module.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/arch/mips/sgi-ip27/ip27-klnuma.c b/arch/mips/sgi-ip27/ip27-klnuma.c index 48932ce1d730..d9c79d8be81d 100644 --- a/arch/mips/sgi-ip27/ip27-klnuma.c +++ b/arch/mips/sgi-ip27/ip27-klnuma.c @@ -4,6 +4,7 @@ * Copyright 2000 - 2001 Kanoj Sarcar (kanoj@sgi.com) */ #include +#include #include #include #include diff --git a/arch/powerpc/kernel/suspend.c b/arch/powerpc/kernel/suspend.c index 8cee57107541..6fc6328dc626 100644 --- a/arch/powerpc/kernel/suspend.c +++ b/arch/powerpc/kernel/suspend.c @@ -7,6 +7,7 @@ * Copyright (c) 2001 Patrick Mochel */ +#include #include /* References to section boundaries */ diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index 0559fe086eb4..7c975d43e3f3 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/arch/sparc64/kernel/iommu_common.h b/arch/sparc64/kernel/iommu_common.h index f3575a614fa2..53b19c8231a9 100644 --- a/arch/sparc64/kernel/iommu_common.h +++ b/arch/sparc64/kernel/iommu_common.h @@ -23,7 +23,7 @@ #define IO_PAGE_SHIFT 13 #define IO_PAGE_SIZE (1UL << IO_PAGE_SHIFT) #define IO_PAGE_MASK (~(IO_PAGE_SIZE-1)) -#define IO_PAGE_ALIGN(addr) (((addr)+IO_PAGE_SIZE-1)&IO_PAGE_MASK) +#define IO_PAGE_ALIGN(addr) ALIGN(addr, IO_PAGE_SIZE) #define IO_TSB_ENTRIES (128*1024) #define IO_TSB_SIZE (IO_TSB_ENTRIES * 8) diff --git a/arch/x86/kernel/module_64.c b/arch/x86/kernel/module_64.c index 0e867676b5a5..6ba87830d4b1 100644 --- a/arch/x86/kernel/module_64.c +++ b/arch/x86/kernel/module_64.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 5e6d75c9f92b..a00359e8f7a8 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/drivers/char/random.c b/drivers/char/random.c index 0cf98bd4f2d2..e0d0e371909c 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -236,6 +236,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c index 07ca35c98f96..1cf6487b65ba 100644 --- a/drivers/ieee1394/iso.c +++ b/drivers/ieee1394/iso.c @@ -11,6 +11,7 @@ #include #include +#include #include #include "hosts.h" diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c index 6ec4bf81fc7f..77b3c3385066 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -20,6 +20,7 @@ #include #include +#include #include "dvbdev.h" #include "pvrusb2-debug.h" #include "pvrusb2-hdw-internal.h" diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index 05a1376405e7..b4824782d858 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -22,6 +22,7 @@ #include "pvrusb2-debug.h" #include #include +#include #include #include #include diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 7388d0cee3d4..5646a6a32939 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 0a88c44ace00..b7b05842cf28 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index c42f4b83f686..3fcf92130aa4 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index e141a1513f07..ea3a09aaa844 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -33,6 +33,7 @@ #include #include +#include #include #include diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index c21f9a9c3e3f..a34284b1482a 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index 2c87db98cdfb..f9cf70151366 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index eedb8285e32f..017233d0c481 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 94e4d3ac1a05..0c5a475c1cae 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index ab32ceb06178..ab77c51fe9d6 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 14d0f7a11145..f85af5c4fa68 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -25,6 +25,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include +#include #include #include diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index bb2514369507..5e8a140399fc 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index ab2b2110478b..4a9f7e121807 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -167,6 +167,7 @@ #include #include #include +#include #include #include #include diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h index 22ff9762d17b..0995f9d13417 100644 --- a/include/asm-alpha/page.h +++ b/include/asm-alpha/page.h @@ -80,9 +80,6 @@ typedef struct page *pgtable_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) #ifndef CONFIG_DISCONTIGMEM diff --git a/include/asm-arm/page-nommu.h b/include/asm-arm/page-nommu.h index a1bcad060480..ea1cde84f500 100644 --- a/include/asm-arm/page-nommu.h +++ b/include/asm-arm/page-nommu.h @@ -7,6 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #ifndef _ASMARM_PAGE_NOMMU_H #define _ASMARM_PAGE_NOMMU_H @@ -42,9 +43,6 @@ typedef unsigned long pgprot_t; #define __pmd(x) (x) #define __pgprot(x) (x) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index 8e05bdb5f12f..7c5fc5582e5d 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -15,9 +15,6 @@ #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #ifndef __ASSEMBLY__ #ifndef CONFIG_MMU diff --git a/include/asm-avr32/page.h b/include/asm-avr32/page.h index cbbc5ca9728b..f805d1cb11bc 100644 --- a/include/asm-avr32/page.h +++ b/include/asm-avr32/page.h @@ -57,9 +57,6 @@ static inline int get_order(unsigned long size) #endif /* !__ASSEMBLY__ */ -/* Align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * The hardware maps the virtual addresses 0x80000000 -> 0x9fffffff * permanently to the physical addresses 0x00000000 -> 0x1fffffff when diff --git a/include/asm-blackfin/page.h b/include/asm-blackfin/page.h index c7db0220fbd6..344f6a8c1f22 100644 --- a/include/asm-blackfin/page.h +++ b/include/asm-blackfin/page.h @@ -51,9 +51,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-cris/page.h b/include/asm-cris/page.h index c45bb1ef397c..d19272ba6b69 100644 --- a/include/asm-cris/page.h +++ b/include/asm-cris/page.h @@ -60,9 +60,6 @@ typedef struct page *pgtable_t; #define page_to_phys(page) __pa((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #ifndef __ASSEMBLY__ #endif /* __ASSEMBLY__ */ diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h index c2c1e89e747d..bd9c220094c7 100644 --- a/include/asm-frv/page.h +++ b/include/asm-frv/page.h @@ -40,9 +40,6 @@ typedef struct page *pgtable_t; #define __pgprot(x) ((pgprot_t) { (x) } ) #define PTE_MASK PAGE_MASK -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - #define devmem_is_allowed(pfn) 1 #define __pa(vaddr) virt_to_phys((void *) (unsigned long) (vaddr)) diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h index d6a3eaf3b27e..0b6acf0b03aa 100644 --- a/include/asm-h8300/page.h +++ b/include/asm-h8300/page.h @@ -43,9 +43,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 36f39321b768..5f271bc712ee 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -40,7 +40,6 @@ #define PAGE_SIZE (__IA64_UL_CONST(1) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE - 1)) -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) #define PERCPU_PAGE_SHIFT 16 /* log2() of max. size of per-CPU area */ #define PERCPU_PAGE_SIZE (__IA64_UL_CONST(1) << PERCPU_PAGE_SHIFT) diff --git a/include/asm-m32r/page.h b/include/asm-m32r/page.h index 8a677f3fca68..c9333089fe11 100644 --- a/include/asm-m32r/page.h +++ b/include/asm-m32r/page.h @@ -41,9 +41,6 @@ typedef struct page *pgtable_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * This handles the memory map.. We could make this a config * option, but too many people screw it up, and too few need diff --git a/include/asm-m68k/dvma.h b/include/asm-m68k/dvma.h index 4fff408d0150..890bbf7e7758 100644 --- a/include/asm-m68k/dvma.h +++ b/include/asm-m68k/dvma.h @@ -13,7 +13,7 @@ #define DVMA_PAGE_SHIFT 13 #define DVMA_PAGE_SIZE (1UL << DVMA_PAGE_SHIFT) #define DVMA_PAGE_MASK (~(DVMA_PAGE_SIZE-1)) -#define DVMA_PAGE_ALIGN(addr) (((addr)+DVMA_PAGE_SIZE-1)&DVMA_PAGE_MASK) +#define DVMA_PAGE_ALIGN(addr) ALIGN(addr, DVMA_PAGE_SIZE) extern void dvma_init(void); extern int dvma_map_iommu(unsigned long kaddr, unsigned long baddr, diff --git a/include/asm-m68k/page.h b/include/asm-m68k/page.h index 880c2cbff8a6..a34b8bad7847 100644 --- a/include/asm-m68k/page.h +++ b/include/asm-m68k/page.h @@ -103,9 +103,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #endif /* !__ASSEMBLY__ */ #include diff --git a/include/asm-m68knommu/page.h b/include/asm-m68knommu/page.h index 1e82ebb7d644..3a1ede4544cb 100644 --- a/include/asm-m68knommu/page.h +++ b/include/asm-m68knommu/page.h @@ -43,9 +43,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index 494f00ba9541..fe7a88ea066e 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -137,9 +137,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * __pa()/__va() should be used only during mem init. */ diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 58cbac5a64e4..a1e4453469f9 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -45,7 +45,7 @@ extern unsigned int vced_count, vcei_count; * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) +#define TASK_UNMAPPED_BASE ((TASK_SIZE / 3) & ~(PAGE_SIZE)) #endif #ifdef CONFIG_64BIT diff --git a/include/asm-mn10300/page.h b/include/asm-mn10300/page.h index 124971b9fb9b..8288e124165b 100644 --- a/include/asm-mn10300/page.h +++ b/include/asm-mn10300/page.h @@ -61,9 +61,6 @@ typedef struct page *pgtable_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * This handles the memory map.. We could make this a config * option, but too many people screw it up, and too few need diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index 27d50b859541..c3941f09a878 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -119,10 +119,6 @@ extern int npmem_ranges; #define PMD_ENTRY_SIZE (1UL << BITS_PER_PMD_ENTRY) #define PTE_ENTRY_SIZE (1UL << BITS_PER_PTE_ENTRY) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - - #define LINUX_GATEWAY_SPACE 0 /* This governs the relationship between virtual and physical addresses. diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index cffdf0eb0df6..e088545cb3f5 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -119,9 +119,6 @@ extern phys_addr_t kernstart_addr; /* align addr on a size boundary - adjust address up if needed */ #define _ALIGN(addr,size) _ALIGN_UP(addr,size) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE) - /* * Don't compare things with KERNELBASE or PAGE_OFFSET to test for * "kernelness", use is_kernel_addr() - it should do what you want. diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 12fd9c4f0f15..991ba939408c 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -138,9 +138,6 @@ void arch_alloc_page(struct page *page, int order); #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #define __PAGE_OFFSET 0x0UL #define PAGE_OFFSET 0x0UL #define __pa(x) (unsigned long)(x) diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index 304c30b5d947..5dc01d2fcc4c 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -22,9 +22,6 @@ #define PAGE_MASK (~(PAGE_SIZE-1)) #define PTE_MASK PAGE_MASK -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #if defined(CONFIG_HUGETLB_PAGE_SIZE_64K) #define HPAGE_SHIFT 16 #elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K) diff --git a/include/asm-sparc/page_32.h b/include/asm-sparc/page_32.h index 14de518cc38f..cf5fb70ca1c1 100644 --- a/include/asm-sparc/page_32.h +++ b/include/asm-sparc/page_32.h @@ -134,9 +134,6 @@ BTFIXUPDEF_SETHI(sparc_unmapped_base) #endif /* !(__ASSEMBLY__) */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #define PAGE_OFFSET 0xf0000000 #ifndef __ASSEMBLY__ extern unsigned long phys_base; diff --git a/include/asm-sparc/page_64.h b/include/asm-sparc/page_64.h index a8a2bba032c1..b579b910ef51 100644 --- a/include/asm-sparc/page_64.h +++ b/include/asm-sparc/page_64.h @@ -106,9 +106,6 @@ typedef struct page *pgtable_t; #endif /* !(__ASSEMBLY__) */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - /* We used to stick this into a hard-coded global register (%g4) * but that does not make sense anymore. */ diff --git a/include/asm-um/page.h b/include/asm-um/page.h index 916e1a61999f..335c57383c02 100644 --- a/include/asm-um/page.h +++ b/include/asm-um/page.h @@ -92,9 +92,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long uml_physmem; #define PAGE_OFFSET (uml_physmem) diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h index 74a539a9bd59..f9de35d873fa 100644 --- a/include/asm-v850/page.h +++ b/include/asm-v850/page.h @@ -94,10 +94,6 @@ typedef unsigned long pgprot_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - - /* No current v850 processor has virtual memory. */ #define __virt_to_phys(addr) (addr) #define __phys_to_virt(addr) (addr) diff --git a/include/asm-x86/page.h b/include/asm-x86/page.h index 6e02098b1605..49982110e4d9 100644 --- a/include/asm-x86/page.h +++ b/include/asm-x86/page.h @@ -34,9 +34,6 @@ #define HUGE_MAX_HSTATE 2 -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #ifndef __ASSEMBLY__ #include #endif diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h index 80a6ae0dd259..11f7dc2dbec7 100644 --- a/include/asm-xtensa/page.h +++ b/include/asm-xtensa/page.h @@ -26,13 +26,11 @@ /* * PAGE_SHIFT determines the page size - * PAGE_ALIGN(x) aligns the pointer to the (next) page boundary */ #define PAGE_SHIFT 12 #define PAGE_SIZE (__XTENSA_UL_CONST(1) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE - 1) & PAGE_MASK) #define PAGE_OFFSET XCHAL_KSEG_CACHED_VADDR #define MAX_MEM_PFN XCHAL_KSEG_SIZE diff --git a/include/linux/mm.h b/include/linux/mm.h index df322fb4df31..d87a5a5fe87d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -41,6 +41,9 @@ extern unsigned long mmap_min_addr; #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n)) +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) + /* * Linux kernel virtual memory manager primitives. * The idea being to have a "virtual" mm in the same way diff --git a/sound/core/info.c b/sound/core/info.c index cb5ead3e202d..c67773ad9298 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From af370fb8cb3031f20438f246798d5f0d98089f29 Mon Sep 17 00:00:00 2001 From: Yasunori Goto Date: Wed, 23 Jul 2008 21:28:17 -0700 Subject: memory hotplug: small fixes to bootmem freeing for memory hotremove - Change some naming * Magic -> types * MIX_INFO -> MIX_SECTION_INFO * Change definition of bootmem type from direct hex value - __free_pages_bootmem() becomes __meminit. Signed-off-by: Yasunori Goto Cc: Andy Whitcroft Cc: Badari Pulavarty Cc: Yinghai Lu Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memory_hotplug.h | 8 ++++---- mm/memory_hotplug.c | 12 ++++++------ mm/page_alloc.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index ea9f5ad9ec8e..3628e5088f64 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -13,12 +13,12 @@ struct mem_section; #ifdef CONFIG_MEMORY_HOTPLUG /* - * Magic number for free bootmem. + * Types for free bootmem. * The normal smallest mapcount is -1. Here is smaller value than it. */ -#define SECTION_INFO 0xfffffffe -#define MIX_INFO 0xfffffffd -#define NODE_INFO 0xfffffffc +#define SECTION_INFO (-1 - 1) +#define MIX_SECTION_INFO (-1 - 2) +#define NODE_INFO (-1 - 3) /* * pgdat resizing functions diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index ec85c37dcfb9..0fb05b258f0c 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -62,9 +62,9 @@ static void release_memory_resource(struct resource *res) #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE #ifndef CONFIG_SPARSEMEM_VMEMMAP -static void get_page_bootmem(unsigned long info, struct page *page, int magic) +static void get_page_bootmem(unsigned long info, struct page *page, int type) { - atomic_set(&page->_mapcount, magic); + atomic_set(&page->_mapcount, type); SetPagePrivate(page); set_page_private(page, info); atomic_inc(&page->_count); @@ -72,10 +72,10 @@ static void get_page_bootmem(unsigned long info, struct page *page, int magic) void put_page_bootmem(struct page *page) { - int magic; + int type; - magic = atomic_read(&page->_mapcount); - BUG_ON(magic >= -1); + type = atomic_read(&page->_mapcount); + BUG_ON(type >= -1); if (atomic_dec_return(&page->_count) == 1) { ClearPagePrivate(page); @@ -119,7 +119,7 @@ static void register_page_bootmem_info_section(unsigned long start_pfn) mapsize = PAGE_ALIGN(usemap_size()) >> PAGE_SHIFT; for (i = 0; i < mapsize; i++, page++) - get_page_bootmem(section_nr, page, MIX_INFO); + get_page_bootmem(section_nr, page, MIX_SECTION_INFO); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index cd4c41432ef6..6da667274df5 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -533,7 +533,7 @@ static void __free_pages_ok(struct page *page, unsigned int order) /* * permit the bootmem allocator to evade page validation on high-order frees */ -void __free_pages_bootmem(struct page *page, unsigned int order) +void __meminit __free_pages_bootmem(struct page *page, unsigned int order) { if (order == 0) { __ClearPageReserved(page); -- cgit v1.2.3 From 5c755e9fd813810680abd56ec09a5f90143e815b Mon Sep 17 00:00:00 2001 From: Badari Pulavarty Date: Wed, 23 Jul 2008 21:28:19 -0700 Subject: memory-hotplug: add sysfs removable attribute for hotplug memory remove Memory may be hot-removed on a per-memory-block basis, particularly on POWER where the SPARSEMEM section size often matches the memory-block size. A user-level agent must be able to identify which sections of memory are likely to be removable before attempting the potentially expensive operation. This patch adds a file called "removable" to the memory directory in sysfs to help such an agent. In this patch, a memory block is considered removable if; o It contains only MOVABLE pageblocks o It contains only pageblocks with free pages regardless of pageblock type On the other hand, a memory block starting with a PageReserved() page will never be considered removable. Without this patch, the user-agent is forced to choose a memory block to remove randomly. Sample output of the sysfs files: ./memory/memory0/removable: 0 ./memory/memory1/removable: 0 ./memory/memory2/removable: 0 ./memory/memory3/removable: 0 ./memory/memory4/removable: 0 ./memory/memory5/removable: 0 ./memory/memory6/removable: 0 ./memory/memory7/removable: 1 ./memory/memory8/removable: 0 ./memory/memory9/removable: 0 ./memory/memory10/removable: 0 ./memory/memory11/removable: 0 ./memory/memory12/removable: 0 ./memory/memory13/removable: 0 ./memory/memory14/removable: 0 ./memory/memory15/removable: 0 ./memory/memory16/removable: 0 ./memory/memory17/removable: 1 ./memory/memory18/removable: 1 ./memory/memory19/removable: 1 ./memory/memory20/removable: 1 ./memory/memory21/removable: 1 ./memory/memory22/removable: 1 Signed-off-by: Badari Pulavarty Signed-off-by: Mel Gorman Acked-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-devices-memory | 24 +++++++++++ drivers/base/memory.c | 19 ++++++++ include/linux/memory_hotplug.h | 12 ++++++ mm/memory_hotplug.c | 60 ++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-devices-memory (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-devices-memory b/Documentation/ABI/testing/sysfs-devices-memory new file mode 100644 index 000000000000..7a16fe1e2270 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-memory @@ -0,0 +1,24 @@ +What: /sys/devices/system/memory +Date: June 2008 +Contact: Badari Pulavarty +Description: + The /sys/devices/system/memory contains a snapshot of the + internal state of the kernel memory blocks. Files could be + added or removed dynamically to represent hot-add/remove + operations. + +Users: hotplug memory add/remove tools + https://w3.opensource.ibm.com/projects/powerpc-utils/ + +What: /sys/devices/system/memory/memoryX/removable +Date: June 2008 +Contact: Badari Pulavarty +Description: + The file /sys/devices/system/memory/memoryX/removable + indicates whether this memory block is removable or not. + This is useful for a user-level agent to determine + identify removable sections of the memory before attempting + potentially expensive hot-remove memory operation + +Users: hotplug memory remove tools + https://w3.opensource.ibm.com/projects/powerpc-utils/ diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 4d4e0e7b6e92..855ed1a9f97b 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -100,6 +100,21 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, return sprintf(buf, "%08lx\n", mem->phys_index); } +/* + * Show whether the section of memory is likely to be hot-removable + */ +static ssize_t show_mem_removable(struct sys_device *dev, char *buf) +{ + unsigned long start_pfn; + int ret; + struct memory_block *mem = + container_of(dev, struct memory_block, sysdev); + + start_pfn = section_nr_to_pfn(mem->phys_index); + ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION); + return sprintf(buf, "%d\n", ret); +} + /* * online, offline, going offline, etc. */ @@ -262,6 +277,7 @@ static ssize_t show_phys_device(struct sys_device *dev, static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL); static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state); static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL); +static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL); #define mem_create_simple_file(mem, attr_name) \ sysdev_create_file(&mem->sysdev, &attr_##attr_name) @@ -350,6 +366,8 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, ret = mem_create_simple_file(mem, state); if (!ret) ret = mem_create_simple_file(mem, phys_device); + if (!ret) + ret = mem_create_simple_file(mem, removable); return ret; } @@ -394,6 +412,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, mem_remove_simple_file(mem, phys_index); mem_remove_simple_file(mem, state); mem_remove_simple_file(mem, phys_device); + mem_remove_simple_file(mem, removable); unregister_memory(mem, section); return 0; diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 3628e5088f64..763ba81fc0f0 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -199,6 +199,18 @@ extern int walk_memory_resource(unsigned long start_pfn, unsigned long nr_pages, void *arg, int (*func)(unsigned long, unsigned long, void *)); +#ifdef CONFIG_MEMORY_HOTREMOVE + +extern int is_mem_section_removable(unsigned long pfn, unsigned long nr_pages); + +#else +static inline int is_mem_section_removable(unsigned long pfn, + unsigned long nr_pages) +{ + return 0; +} +#endif /* CONFIG_MEMORY_HOTREMOVE */ + extern int add_memory(int nid, u64 start, u64 size); extern int arch_add_memory(int nid, u64 start, u64 size); extern int remove_memory(u64 start, u64 size); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 93aba78dc8b6..89fee2dcb039 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -522,6 +522,66 @@ error: EXPORT_SYMBOL_GPL(add_memory); #ifdef CONFIG_MEMORY_HOTREMOVE +/* + * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy + * set and the size of the free page is given by page_order(). Using this, + * the function determines if the pageblock contains only free pages. + * Due to buddy contraints, a free page at least the size of a pageblock will + * be located at the start of the pageblock + */ +static inline int pageblock_free(struct page *page) +{ + return PageBuddy(page) && page_order(page) >= pageblock_order; +} + +/* Return the start of the next active pageblock after a given page */ +static struct page *next_active_pageblock(struct page *page) +{ + int pageblocks_stride; + + /* Ensure the starting page is pageblock-aligned */ + BUG_ON(page_to_pfn(page) & (pageblock_nr_pages - 1)); + + /* Move forward by at least 1 * pageblock_nr_pages */ + pageblocks_stride = 1; + + /* If the entire pageblock is free, move to the end of free page */ + if (pageblock_free(page)) + pageblocks_stride += page_order(page) - pageblock_order; + + return page + (pageblocks_stride * pageblock_nr_pages); +} + +/* Checks if this range of memory is likely to be hot-removable. */ +int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages) +{ + int type; + struct page *page = pfn_to_page(start_pfn); + struct page *end_page = page + nr_pages; + + /* Check the starting page of each pageblock within the range */ + for (; page < end_page; page = next_active_pageblock(page)) { + type = get_pageblock_migratetype(page); + + /* + * A pageblock containing MOVABLE or free pages is considered + * removable + */ + if (type != MIGRATE_MOVABLE && !pageblock_free(page)) + return 0; + + /* + * A pageblock starting with a PageReserved page is not + * considered removable. + */ + if (PageReserved(page)) + return 0; + } + + /* All pageblocks in the memory block are likely to be hot-removable */ + return 1; +} + /* * Confirm all pages in a range [start, end) is belongs to the same zone. */ -- cgit v1.2.3 From 9ca908f47bc784c90e17a553ce33e756c73feac4 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Wed, 23 Jul 2008 21:28:20 -0700 Subject: kcalloc: remove runtime division While in all cases in the kernel we know the size of the elements to be created, we don't always know the count of elements. By commuting the size and count in the overflow check, the compiler can reduce the runtime division of size_t with a compare to a (unique) constant in these cases. Signed-off-by: Milton Miller Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/slab.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/slab.h b/include/linux/slab.h index 9aa90a6f20e0..41103910f8a2 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -180,7 +180,7 @@ size_t ksize(const void *); */ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) { - if (n != 0 && size > ULONG_MAX / n) + if (size != 0 && n > ULONG_MAX / size) return NULL; return __kmalloc(n * size, flags | __GFP_ZERO); } -- cgit v1.2.3 From 83d1674a946141c3c59d430e96c224f7937e6158 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Wed, 23 Jul 2008 21:28:22 -0700 Subject: mm: make CONFIG_MIGRATION available w/o CONFIG_NUMA We'd like to support CONFIG_MEMORY_HOTREMOVE on s390, which depends on CONFIG_MIGRATION. So far, CONFIG_MIGRATION is only available with NUMA support. This patch makes CONFIG_MIGRATION selectable for architectures that define ARCH_ENABLE_MEMORY_HOTREMOVE. When MIGRATION is enabled w/o NUMA, the kernel won't compile because migrate_vmas() does not know about vm_ops->migrate() and vma_migratable() does not know about policy_zone. To fix this, those two functions can be restricted to '#ifdef CONFIG_NUMA' because they are not being used w/o NUMA. vma_migratable() is moved over from migrate.h to mempolicy.h. [kosaki.motohiro@jp.fujitsu.com: build fix] Acked-by: Christoph Lameter Signed-off-by: Gerald Schaefer Cc: Martin Schwidefsky Cc: Heiko Carstens Signed-off-by: KOSAKI Motorhiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempolicy.h | 19 +++++++++++++++++++ include/linux/migrate.h | 21 --------------------- mm/Kconfig | 2 +- mm/migrate.c | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 3a39570b81b8..085c903fe0f1 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -59,6 +59,7 @@ enum { #include #include #include +#include struct mm_struct; @@ -220,6 +221,24 @@ extern int mpol_parse_str(char *str, struct mempolicy **mpol, int no_context); extern int mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol, int no_context); #endif + +/* Check if a vma is migratable */ +static inline int vma_migratable(struct vm_area_struct *vma) +{ + if (vma->vm_flags & (VM_IO|VM_HUGETLB|VM_PFNMAP|VM_RESERVED)) + return 0; + /* + * Migration allocates pages in the highest zone. If we cannot + * do so then migration (at least from node to node) is not + * possible. + */ + if (vma->vm_file && + gfp_zone(mapping_gfp_mask(vma->vm_file->f_mapping)) + < policy_zone) + return 0; + return 1; +} + #else struct mempolicy {}; diff --git a/include/linux/migrate.h b/include/linux/migrate.h index e10a90a93b5d..03aea612d284 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -3,28 +3,10 @@ #include #include -#include typedef struct page *new_page_t(struct page *, unsigned long private, int **); #ifdef CONFIG_MIGRATION -/* Check if a vma is migratable */ -static inline int vma_migratable(struct vm_area_struct *vma) -{ - if (vma->vm_flags & (VM_IO|VM_HUGETLB|VM_PFNMAP|VM_RESERVED)) - return 0; - /* - * Migration allocates pages in the highest zone. If we cannot - * do so then migration (at least from node to node) is not - * possible. - */ - if (vma->vm_file && - gfp_zone(mapping_gfp_mask(vma->vm_file->f_mapping)) - < policy_zone) - return 0; - return 1; -} - extern int isolate_lru_page(struct page *p, struct list_head *pagelist); extern int putback_lru_pages(struct list_head *l); extern int migrate_page(struct address_space *, @@ -39,9 +21,6 @@ extern int migrate_vmas(struct mm_struct *mm, const nodemask_t *from, const nodemask_t *to, unsigned long flags); #else -static inline int vma_migratable(struct vm_area_struct *vma) - { return 0; } - static inline int isolate_lru_page(struct page *p, struct list_head *list) { return -ENOSYS; } static inline int putback_lru_pages(struct list_head *l) { return 0; } diff --git a/mm/Kconfig b/mm/Kconfig index c4de85285bb4..aa799007a11b 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -174,7 +174,7 @@ config SPLIT_PTLOCK_CPUS config MIGRATION bool "Page migration" def_bool y - depends on NUMA + depends on NUMA || ARCH_ENABLE_MEMORY_HOTREMOVE help Allows the migration of the physical location of pages of processes while the virtual addresses are not changed. This is useful for diff --git a/mm/migrate.c b/mm/migrate.c index e7d13a708da0..376cceba82f9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1071,7 +1071,6 @@ out2: mmput(mm); return err; } -#endif /* * Call migration functions in the vma_ops that may prepare @@ -1093,3 +1092,4 @@ int migrate_vmas(struct mm_struct *mm, const nodemask_t *to, } return err; } +#endif -- cgit v1.2.3 From 5459c164f0591ee75ed0203bb8f3817f25948e2f Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Wed, 23 Jul 2008 21:28:24 -0700 Subject: security: protect legacy applications from executing with insufficient privilege When cap_bset suppresses some of the forced (fP) capabilities of a file, it is generally only safe to execute the program if it understands how to recognize it doesn't have enough privilege to work correctly. For legacy applications (fE!=0), which have no non-destructive way to determine that they are missing privilege, we fail to execute (EPERM) any executable that requires fP capabilities, but would otherwise get pP' < fP. This is a fail-safe permission check. For some discussion of why it is problematic for (legacy) privileged applications to run with less than the set of capabilities requested for them, see: http://userweb.kernel.org/~morgan/sendmail-capabilities-war-story.html With this iteration of this support, we do not include setuid-0 based privilege protection from the bounding set. That is, the admin can still (ab)use the bounding set to suppress the privileges of a setuid-0 program. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: cleanup] Signed-off-by: Andrew G. Morgan Acked-by: Serge Hallyn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/binfmts.h | 2 +- security/commoncap.c | 108 ++++++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index ee0ed48e8348..826f62350805 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -38,7 +38,7 @@ struct linux_binprm{ misc_bang:1; struct file * file; int e_uid, e_gid; - kernel_cap_t cap_inheritable, cap_permitted; + kernel_cap_t cap_post_exec_permitted; bool cap_effective; void *security; int argc, envc; diff --git a/security/commoncap.c b/security/commoncap.c index 0b6537a3672d..4afbece37a08 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -162,8 +162,7 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, static inline void bprm_clear_caps(struct linux_binprm *bprm) { - cap_clear(bprm->cap_inheritable); - cap_clear(bprm->cap_permitted); + cap_clear(bprm->cap_post_exec_permitted); bprm->cap_effective = false; } @@ -198,6 +197,7 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, { __u32 magic_etc; unsigned tocopy, i; + int ret; if (size < sizeof(magic_etc)) return -EINVAL; @@ -225,19 +225,40 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, bprm->cap_effective = false; } - for (i = 0; i < tocopy; ++i) { - bprm->cap_permitted.cap[i] = - le32_to_cpu(caps->data[i].permitted); - bprm->cap_inheritable.cap[i] = - le32_to_cpu(caps->data[i].inheritable); - } - while (i < VFS_CAP_U32) { - bprm->cap_permitted.cap[i] = 0; - bprm->cap_inheritable.cap[i] = 0; - i++; + ret = 0; + + CAP_FOR_EACH_U32(i) { + __u32 value_cpu; + + if (i >= tocopy) { + /* + * Legacy capability sets have no upper bits + */ + bprm->cap_post_exec_permitted.cap[i] = 0; + continue; + } + /* + * pP' = (X & fP) | (pI & fI) + */ + value_cpu = le32_to_cpu(caps->data[i].permitted); + bprm->cap_post_exec_permitted.cap[i] = + (current->cap_bset.cap[i] & value_cpu) | + (current->cap_inheritable.cap[i] & + le32_to_cpu(caps->data[i].inheritable)); + if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) { + /* + * insufficient to execute correctly + */ + ret = -EPERM; + } } - return 0; + /* + * For legacy apps, with no internal support for recognizing they + * do not have enough capabilities, we return an error if they are + * missing some "forced" (aka file-permitted) capabilities. + */ + return bprm->cap_effective ? ret : 0; } /* Locate any VFS capabilities: */ @@ -269,9 +290,9 @@ static int get_file_caps(struct linux_binprm *bprm) goto out; rc = cap_from_disk(&vcaps, bprm, rc); - if (rc) + if (rc == -EINVAL) printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", - __func__, rc, bprm->filename); + __func__, rc, bprm->filename); out: dput(dentry); @@ -304,25 +325,24 @@ int cap_bprm_set_security (struct linux_binprm *bprm) int ret; ret = get_file_caps(bprm); - if (ret) - printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n", - __func__, ret, bprm->filename); - - /* To support inheritance of root-permissions and suid-root - * executables under compatibility mode, we raise all three - * capability sets for the file. - * - * If only the real uid is 0, we only raise the inheritable - * and permitted sets of the executable file. - */ - if (!issecure (SECURE_NOROOT)) { + if (!issecure(SECURE_NOROOT)) { + /* + * To support inheritance of root-permissions and suid-root + * executables under compatibility mode, we override the + * capability sets for the file. + * + * If only the real uid is 0, we do not set the effective + * bit. + */ if (bprm->e_uid == 0 || current->uid == 0) { - cap_set_full (bprm->cap_inheritable); - cap_set_full (bprm->cap_permitted); + /* pP' = (cap_bset & ~0) | (pI & ~0) */ + bprm->cap_post_exec_permitted = cap_combine( + current->cap_bset, current->cap_inheritable + ); + bprm->cap_effective = (bprm->e_uid == 0); + ret = 0; } - if (bprm->e_uid == 0) - bprm->cap_effective = true; } return ret; @@ -330,17 +350,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm) void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { - /* Derived from fs/exec.c:compute_creds. */ - kernel_cap_t new_permitted, working; - - new_permitted = cap_intersect(bprm->cap_permitted, - current->cap_bset); - working = cap_intersect(bprm->cap_inheritable, - current->cap_inheritable); - new_permitted = cap_combine(new_permitted, working); - if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || - !cap_issubset (new_permitted, current->cap_permitted)) { + !cap_issubset(bprm->cap_post_exec_permitted, + current->cap_permitted)) { set_dumpable(current->mm, suid_dumpable); current->pdeath_signal = 0; @@ -350,9 +362,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) bprm->e_gid = current->gid; } if (cap_limit_ptraced_target()) { - new_permitted = - cap_intersect(new_permitted, - current->cap_permitted); + bprm->cap_post_exec_permitted = cap_intersect( + bprm->cap_post_exec_permitted, + current->cap_permitted); } } } @@ -364,9 +376,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) * in the init_task struct. Thus we skip the usual * capability rules */ if (!is_global_init(current)) { - current->cap_permitted = new_permitted; + current->cap_permitted = bprm->cap_post_exec_permitted; if (bprm->cap_effective) - current->cap_effective = new_permitted; + current->cap_effective = bprm->cap_post_exec_permitted; else cap_clear(current->cap_effective); } @@ -381,9 +393,7 @@ int cap_bprm_secureexec (struct linux_binprm *bprm) if (current->uid != 0) { if (bprm->cap_effective) return 1; - if (!cap_isclear(bprm->cap_permitted)) - return 1; - if (!cap_isclear(bprm->cap_inheritable)) + if (!cap_isclear(bprm->cap_post_exec_permitted)) return 1; } -- cgit v1.2.3 From 9b3e43a747c74029b0acf6acf4666601f132f471 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 23 Jul 2008 21:28:26 -0700 Subject: security: remove unused forwards Why would linux/security.h need forward declarations for nfsctl_arg and swap_info_struct? It's hard to imagine: remove them. Signed-off-by: Hugh Dickins Acked-by: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/security.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 31c8851ec5d0..f0e9adb22ac2 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -102,9 +102,7 @@ extern unsigned long mmap_min_addr; #define LSM_SETID_FS 8 /* forward declares to avoid warnings */ -struct nfsctl_arg; struct sched_param; -struct swap_info_struct; struct request_sock; /* bprm_apply_creds unsafe reasons */ -- cgit v1.2.3 From d75f65fd247fe85d90a3880d143b1bb22fe13a48 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 23 Jul 2008 21:28:34 -0700 Subject: remove include/linux/pm_legacy.h Remove the obsolete and no longer used include/linux/pm_legacy.h Reviewed-by: Robert P. J. Day Signed-off-by: Adrian Bunk Cc: Pavel Machek Acked-by: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/kernel/pm.c | 1 - arch/mips/au1000/common/power.c | 1 - arch/x86/kernel/apm_32.c | 1 - include/linux/pm_legacy.h | 35 ----------------------------------- 4 files changed, 38 deletions(-) delete mode 100644 include/linux/pm_legacy.h (limited to 'include/linux') diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c index 73f3aeefd203..d1113c5031f5 100644 --- a/arch/frv/kernel/pm.c +++ b/arch/frv/kernel/pm.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c index 2166b9e1e80c..bd854a6d1d89 100644 --- a/arch/mips/au1000/common/power.c +++ b/arch/mips/au1000/common/power.c @@ -31,7 +31,6 @@ #include #include -#include #include #include diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index bf9b441331e9..9ee24e6bc4b0 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -219,7 +219,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/pm_legacy.h b/include/linux/pm_legacy.h deleted file mode 100644 index 446f4f42b952..000000000000 --- a/include/linux/pm_legacy.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __LINUX_PM_LEGACY_H__ -#define __LINUX_PM_LEGACY_H__ - - -#ifdef CONFIG_PM_LEGACY - -/* - * Register a device with power management - */ -struct pm_dev __deprecated * -pm_register(pm_dev_t type, unsigned long id, pm_callback callback); - -/* - * Send a request to all devices - */ -int __deprecated pm_send_all(pm_request_t rqst, void *data); - -#else /* CONFIG_PM_LEGACY */ - -static inline struct pm_dev *pm_register(pm_dev_t type, - unsigned long id, - pm_callback callback) -{ - return NULL; -} - -static inline int pm_send_all(pm_request_t rqst, void *data) -{ - return 0; -} - -#endif /* CONFIG_PM_LEGACY */ - -#endif /* __LINUX_PM_LEGACY_H__ */ - -- cgit v1.2.3 From 558481f038e587b22d02167af58914c814ce9de5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2008 21:28:35 -0700 Subject: pm: remove definition of struct pm_dev Remove the definition of 'struct pm_dev', which is not used any more, along with some related stuff from include/linux/pm.h . Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pm.h | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index 4ad9de94449a..5bf1ce89cfbb 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -68,30 +68,6 @@ enum */ #define PM_PCI_ID(dev) ((dev)->bus->number << 16 | (dev)->devfn) -/* - * Request handler callback - */ -struct pm_dev; - -typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data); - -/* - * Dynamic device information - */ -struct pm_dev -{ - pm_dev_t type; - unsigned long id; - pm_callback callback; - void *data; - - unsigned long flags; - unsigned long state; - unsigned long prev_state; - - struct list_head entry; -}; - /* Functions above this comment are list-based old-style power * management. Please avoid using them. */ -- cgit v1.2.3 From e7ecb331e11d1f7aa66aeef9170fc20781c9bb55 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2008 21:28:35 -0700 Subject: pm: remove remaining obsolete definitions from pm.h Remove the remaining obsolete definitions from include/linux/pm.h and move the definitions of PM_SUSPEND and PM_RESUME to the header of h3600 which is the only user of them. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-arm/arch-sa1100/h3600.h | 5 ++++ include/linux/pm.h | 46 ------------------------------------- 2 files changed, 5 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/asm-arm/arch-sa1100/h3600.h b/include/asm-arm/arch-sa1100/h3600.h index 1b6355971574..3ca0ecf095e6 100644 --- a/include/asm-arm/arch-sa1100/h3600.h +++ b/include/asm-arm/arch-sa1100/h3600.h @@ -23,6 +23,11 @@ #ifndef _INCLUDE_H3600_H_ #define _INCLUDE_H3600_H_ +typedef int __bitwise pm_request_t; + +#define PM_SUSPEND ((__force pm_request_t) 1) /* enter D1-D3 */ +#define PM_RESUME ((__force pm_request_t) 2) /* enter D0 */ + /* generalized support for H3xxx series Compaq Pocket PC's */ #define machine_is_h3xxx() (machine_is_h3100() || machine_is_h3600() || machine_is_h3800()) diff --git a/include/linux/pm.h b/include/linux/pm.h index 5bf1ce89cfbb..390dd95a375e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -25,52 +25,6 @@ #include #include -/* - * Power management requests... these are passed to pm_send_all() and friends. - * - * these functions are old and deprecated, see below. - */ -typedef int __bitwise pm_request_t; - -#define PM_SUSPEND ((__force pm_request_t) 1) /* enter D1-D3 */ -#define PM_RESUME ((__force pm_request_t) 2) /* enter D0 */ - - -/* - * Device types... these are passed to pm_register - */ -typedef int __bitwise pm_dev_t; - -#define PM_UNKNOWN_DEV ((__force pm_dev_t) 0) /* generic */ -#define PM_SYS_DEV ((__force pm_dev_t) 1) /* system device (fan, KB controller, ...) */ -#define PM_PCI_DEV ((__force pm_dev_t) 2) /* PCI device */ -#define PM_USB_DEV ((__force pm_dev_t) 3) /* USB device */ -#define PM_SCSI_DEV ((__force pm_dev_t) 4) /* SCSI device */ -#define PM_ISA_DEV ((__force pm_dev_t) 5) /* ISA device */ -#define PM_MTD_DEV ((__force pm_dev_t) 6) /* Memory Technology Device */ - -/* - * System device hardware ID (PnP) values - */ -enum -{ - PM_SYS_UNKNOWN = 0x00000000, /* generic */ - PM_SYS_KBC = 0x41d00303, /* keyboard controller */ - PM_SYS_COM = 0x41d00500, /* serial port */ - PM_SYS_IRDA = 0x41d00510, /* IRDA controller */ - PM_SYS_FDC = 0x41d00700, /* floppy controller */ - PM_SYS_VGA = 0x41d00900, /* VGA controller */ - PM_SYS_PCMCIA = 0x41d00e00, /* PCMCIA controller */ -}; - -/* - * Device identifier - */ -#define PM_PCI_ID(dev) ((dev)->bus->number << 16 | (dev)->devfn) - -/* Functions above this comment are list-based old-style power - * management. Please avoid using them. */ - /* * Callbacks for platform drivers to implement. */ -- cgit v1.2.3 From 8c363265d57d755e62053e9f69a1f2164e83f7ea Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2008 21:28:37 -0700 Subject: pm: drop unnecessary includes from pm.h Drop unnecessary includes from include/linux/pm.h . Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pm.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index 390dd95a375e..ed98d967f9fb 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -22,8 +22,6 @@ #define _LINUX_PM_H #include -#include -#include /* * Callbacks for platform drivers to implement. -- cgit v1.2.3 From 8111d1b552349921aae1acf73e4e8cea98e80970 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Jul 2008 21:28:37 -0700 Subject: pm: add new PM_EVENT codes for runtime power transitions This patch (as1112) adds some new PM_EVENT_* codes for use by kernel subsystems. They describe runtime power-state transitions of the sort already implemented by the USB subsystem. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pm.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index ed98d967f9fb..4dcce54b6d76 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -245,6 +245,21 @@ struct pm_ext_ops { * RECOVER Creation of a hibernation image or restoration of the main * memory contents from a hibernation image has failed, call * ->thaw() and ->complete() for all devices. + * + * The following PM_EVENT_ messages are defined for internal use by + * kernel subsystems. They are never issued by the PM core. + * + * USER_SUSPEND Manual selective suspend was issued by userspace. + * + * USER_RESUME Manual selective resume was issued by userspace. + * + * REMOTE_WAKEUP Remote-wakeup request was received from the device. + * + * AUTO_SUSPEND Automatic (device idle) runtime suspend was + * initiated by the subsystem. + * + * AUTO_RESUME Automatic (device needed) runtime resume was + * requested by a driver. */ #define PM_EVENT_ON 0x0000 @@ -256,9 +271,18 @@ struct pm_ext_ops { #define PM_EVENT_THAW 0x0020 #define PM_EVENT_RESTORE 0x0040 #define PM_EVENT_RECOVER 0x0080 +#define PM_EVENT_USER 0x0100 +#define PM_EVENT_REMOTE 0x0200 +#define PM_EVENT_AUTO 0x0400 -#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) +#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) +#define PM_EVENT_USER_SUSPEND (PM_EVENT_USER | PM_EVENT_SUSPEND) +#define PM_EVENT_USER_RESUME (PM_EVENT_USER | PM_EVENT_RESUME) +#define PM_EVENT_REMOTE_WAKEUP (PM_EVENT_REMOTE | PM_EVENT_RESUME) +#define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND) +#define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) +#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) #define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) #define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) @@ -267,7 +291,16 @@ struct pm_ext_ops { #define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) #define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) #define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) -#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) +#define PMSG_USER_SUSPEND ((struct pm_messge) \ + { .event = PM_EVENT_USER_SUSPEND, }) +#define PMSG_USER_RESUME ((struct pm_messge) \ + { .event = PM_EVENT_USER_RESUME, }) +#define PMSG_REMOTE_RESUME ((struct pm_messge) \ + { .event = PM_EVENT_REMOTE_RESUME, }) +#define PMSG_AUTO_SUSPEND ((struct pm_messge) \ + { .event = PM_EVENT_AUTO_SUSPEND, }) +#define PMSG_AUTO_RESUME ((struct pm_messge) \ + { .event = PM_EVENT_AUTO_RESUME, }) /** * Device power management states -- cgit v1.2.3 From c1a220e7acf8ad2c03504891f4a70cd9c32c904b Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 23 Jul 2008 21:28:39 -0700 Subject: pm: introduce new interfaces schedule_work_on() and queue_work_on() This interface allows adding a job on a specific cpu. Although a work struct on a cpu will be scheduled to other cpu if the cpu dies, there is a recursion if a work task tries to offline the cpu it's running on. we need to schedule the task to a specific cpu in this case. http://bugzilla.kernel.org/show_bug.cgi?id=10897 [oleg@tv-sign.ru: cleanups] Signed-off-by: Zhang Rui Tested-by: Rus Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 3 +++ kernel/workqueue.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 542526c6e8ef..14d47120682b 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -179,6 +179,8 @@ __create_workqueue_key(const char *name, int singlethread, extern void destroy_workqueue(struct workqueue_struct *wq); extern int queue_work(struct workqueue_struct *wq, struct work_struct *work); +extern int queue_work_on(int cpu, struct workqueue_struct *wq, + struct work_struct *work); extern int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, @@ -188,6 +190,7 @@ extern void flush_workqueue(struct workqueue_struct *wq); extern void flush_scheduled_work(void); extern int schedule_work(struct work_struct *work); +extern int schedule_work_on(int cpu, struct work_struct *work); extern int schedule_delayed_work(struct delayed_work *work, unsigned long delay); extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, unsigned long delay); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index a6d36346d10a..6fd158b21026 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -140,7 +140,6 @@ static void insert_work(struct cpu_workqueue_struct *cwq, wake_up(&cwq->more_work); } -/* Preempt must be disabled. */ static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) { @@ -175,6 +174,31 @@ int queue_work(struct workqueue_struct *wq, struct work_struct *work) } EXPORT_SYMBOL_GPL(queue_work); +/** + * queue_work_on - queue work on specific cpu + * @cpu: CPU number to execute work on + * @wq: workqueue to use + * @work: work to queue + * + * Returns 0 if @work was already on a queue, non-zero otherwise. + * + * We queue the work to a specific CPU, the caller must ensure it + * can't go away. + */ +int +queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work) +{ + int ret = 0; + + if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) { + BUG_ON(!list_empty(&work->entry)); + __queue_work(wq_per_cpu(wq, cpu), work); + ret = 1; + } + return ret; +} +EXPORT_SYMBOL_GPL(queue_work_on); + static void delayed_work_timer_fn(unsigned long __data) { struct delayed_work *dwork = (struct delayed_work *)__data; @@ -553,6 +577,19 @@ int schedule_work(struct work_struct *work) } EXPORT_SYMBOL(schedule_work); +/* + * schedule_work_on - put work task on a specific cpu + * @cpu: cpu to put the work task on + * @work: job to be done + * + * This puts a job on a specific cpu + */ +int schedule_work_on(int cpu, struct work_struct *work) +{ + return queue_work_on(cpu, keventd_wq, work); +} +EXPORT_SYMBOL(schedule_work_on); + /** * schedule_delayed_work - put work task in global workqueue after delay * @dwork: job to be done -- cgit v1.2.3 From bdfe6b7c681669148dae4db27eb24ee5408ba371 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 23 Jul 2008 21:28:41 -0700 Subject: pm: acpi hibernation: utilize hardware signature ACPI defines a hardware signature. BIOS calculates the signature according to hardware configure and if hardware changes while hibernated, the signature will change. In that case, S4 resume should fail. Still, there may be systems on which this mechanism does not work correctly, so it is better to provide a workaround for them. For this reason, add a new switch to the acpi_sleep= command line argument allowing one to disable hardware signature checking. [shaohua.li@intel.com: build fix] Signed-off-by: Shaohua Li Signed-off-by: Rafael J. Wysocki Cc: Andi Kleen Cc: Len Brown Acked-by: Pavel Machek Cc: Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 4 +++- arch/x86/kernel/acpi/sleep.c | 4 ++++ drivers/acpi/sleep/main.c | 22 ++++++++++++++++++++++ include/linux/acpi.h | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4d705713cabc..497a98dafdaa 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -148,10 +148,12 @@ and is between 256 and 4096 characters. It is defined in the file default: 0 acpi_sleep= [HW,ACPI] Sleep options - Format: { s3_bios, s3_mode, s3_beep, old_ordering } + Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering } See Documentation/power/video.txt for s3_bios and s3_mode. s3_beep is for debugging; it makes the PC's speaker beep as soon as the kernel's real-mode entry point is called. + s4_nohwsig prevents ACPI hardware signature from being + used during resume from hibernation. old_ordering causes the ACPI 1.0 ordering of the _PTS control method, wrt putting devices into low power states, to be enforced (the ACPI 2.0 ordering of _PTS is diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index a3ddad18aaa3..fa2161d5003b 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -150,6 +150,10 @@ static int __init acpi_sleep_setup(char *str) acpi_realmode_flags |= 2; if (strncmp(str, "s3_beep", 7) == 0) acpi_realmode_flags |= 4; +#ifdef CONFIG_HIBERNATION + if (strncmp(str, "s4_nohwsig", 10) == 0) + acpi_no_s4_hw_signature(); +#endif if (strncmp(str, "old_ordering", 12) == 0) acpi_old_suspend_ordering(); str = strchr(str, ','); diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 0489a7d1d42c..313507accf18 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -283,6 +283,15 @@ static struct platform_suspend_ops acpi_suspend_ops_old = { #endif /* CONFIG_SUSPEND */ #ifdef CONFIG_HIBERNATION +static unsigned long s4_hardware_signature; +static struct acpi_table_facs *facs; +static bool nosigcheck; + +void __init acpi_no_s4_hw_signature(void) +{ + nosigcheck = true; +} + static int acpi_hibernation_begin(void) { acpi_target_sleep_state = ACPI_STATE_S4; @@ -316,6 +325,12 @@ static void acpi_hibernation_leave(void) acpi_enable(); /* Reprogram control registers and execute _BFS */ acpi_leave_sleep_state_prep(ACPI_STATE_S4); + /* Check the hardware signature */ + if (facs && s4_hardware_signature != facs->hardware_signature) { + printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " + "cannot resume!\n"); + panic("ACPI S4 hardware signature mismatch"); + } } static void acpi_pm_enable_gpes(void) @@ -544,6 +559,13 @@ int __init acpi_sleep_init(void) &acpi_hibernation_ops_old : &acpi_hibernation_ops); sleep_states[ACPI_STATE_S4] = 1; printk(" S4"); + if (!nosigcheck) { + acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, + (struct acpi_table_header **)&facs); + if (facs) + s4_hardware_signature = + facs->hardware_signature; + } } #endif status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index a17177639376..702f79dad16a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -236,6 +236,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n, const char *name); #ifdef CONFIG_PM_SLEEP +void __init acpi_no_s4_hw_signature(void); void __init acpi_old_suspend_ordering(void); #endif /* CONFIG_PM_SLEEP */ #else /* CONFIG_ACPI */ -- cgit v1.2.3 From f606ddf42fd4edc558eeb48bfee66d2c591571d2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 23 Jul 2008 21:28:50 -0700 Subject: remove the v850 port Trying to compile the v850 port brings many compile errors, one of them exists since at least kernel 2.6.19. There also seems to be noone willing to bring this port back into a usable state. This patch therefore removes the v850 port. If anyone ever decides to revive the v850 port the code will still be available from older kernels, and it wouldn't be impossible for the port to reenter the kernel if it would become actively maintained again. Signed-off-by: Adrian Bunk Acked-by: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 3 - arch/v850/Kconfig | 353 ---------- arch/v850/Kconfig.debug | 10 - arch/v850/Makefile | 54 -- arch/v850/README | 44 -- arch/v850/configs/rte-ma1-cb_defconfig | 617 ------------------ arch/v850/configs/rte-me2-cb_defconfig | 462 ------------- arch/v850/configs/sim_defconfig | 451 ------------- arch/v850/kernel/Makefile | 40 -- arch/v850/kernel/anna-rom.ld | 16 - arch/v850/kernel/anna.c | 202 ------ arch/v850/kernel/anna.ld | 20 - arch/v850/kernel/as85ep1-rom.ld | 21 - arch/v850/kernel/as85ep1.c | 234 ------- arch/v850/kernel/as85ep1.ld | 49 -- arch/v850/kernel/asm-offsets.c | 58 -- arch/v850/kernel/bug.c | 142 ---- arch/v850/kernel/entry.S | 1121 -------------------------------- arch/v850/kernel/fpga85e2c.c | 167 ----- arch/v850/kernel/fpga85e2c.ld | 62 -- arch/v850/kernel/gbus_int.c | 271 -------- arch/v850/kernel/head.S | 128 ---- arch/v850/kernel/highres_timer.c | 132 ---- arch/v850/kernel/init_task.c | 48 -- arch/v850/kernel/intv.S | 87 --- arch/v850/kernel/irq.c | 123 ---- arch/v850/kernel/ma.c | 69 -- arch/v850/kernel/mach.c | 17 - arch/v850/kernel/mach.h | 56 -- arch/v850/kernel/me2.c | 73 --- arch/v850/kernel/memcons.c | 135 ---- arch/v850/kernel/module.c | 237 ------- arch/v850/kernel/process.c | 217 ------- arch/v850/kernel/procfs.c | 67 -- arch/v850/kernel/ptrace.c | 235 ------- arch/v850/kernel/rte_cb.c | 193 ------ arch/v850/kernel/rte_cb_leds.c | 137 ---- arch/v850/kernel/rte_cb_multi.c | 121 ---- arch/v850/kernel/rte_ma1_cb-rom.ld | 14 - arch/v850/kernel/rte_ma1_cb.c | 107 --- arch/v850/kernel/rte_ma1_cb.ld | 57 -- arch/v850/kernel/rte_mb_a_pci.c | 819 ----------------------- arch/v850/kernel/rte_me2_cb.c | 298 --------- arch/v850/kernel/rte_me2_cb.ld | 30 - arch/v850/kernel/rte_nb85e_cb-multi.ld | 57 -- arch/v850/kernel/rte_nb85e_cb.c | 81 --- arch/v850/kernel/rte_nb85e_cb.ld | 22 - arch/v850/kernel/setup.c | 329 ---------- arch/v850/kernel/signal.c | 523 --------------- arch/v850/kernel/sim.c | 172 ----- arch/v850/kernel/sim.ld | 13 - arch/v850/kernel/sim85e2.c | 195 ------ arch/v850/kernel/sim85e2.ld | 36 - arch/v850/kernel/simcons.c | 161 ----- arch/v850/kernel/syscalls.c | 196 ------ arch/v850/kernel/teg.c | 62 -- arch/v850/kernel/time.c | 106 --- arch/v850/kernel/v850_ksyms.c | 51 -- arch/v850/kernel/v850e2_cache.c | 127 ---- arch/v850/kernel/v850e_cache.c | 174 ----- arch/v850/kernel/v850e_intc.c | 104 --- arch/v850/kernel/v850e_timer_d.c | 54 -- arch/v850/kernel/v850e_utils.c | 62 -- arch/v850/kernel/vmlinux.lds.S | 306 --------- arch/v850/lib/Makefile | 6 - arch/v850/lib/ashldi3.c | 62 -- arch/v850/lib/ashrdi3.c | 63 -- arch/v850/lib/checksum.c | 155 ----- arch/v850/lib/lshrdi3.c | 62 -- arch/v850/lib/memcpy.c | 92 --- arch/v850/lib/memset.c | 68 -- arch/v850/lib/muldi3.c | 61 -- arch/v850/lib/negdi2.c | 25 - drivers/serial/Kconfig | 16 - drivers/watchdog/Kconfig | 2 - drivers/watchdog/Makefile | 2 - include/asm-v850/Kbuild | 1 - include/asm-v850/a.out.h | 21 - include/asm-v850/anna.h | 137 ---- include/asm-v850/as85ep1.h | 152 ----- include/asm-v850/asm.h | 32 - include/asm-v850/atomic.h | 131 ---- include/asm-v850/auxvec.h | 4 - include/asm-v850/bitops.h | 161 ----- include/asm-v850/bug.h | 25 - include/asm-v850/bugs.h | 16 - include/asm-v850/byteorder.h | 48 -- include/asm-v850/cache.h | 26 - include/asm-v850/cacheflush.h | 70 -- include/asm-v850/checksum.h | 112 ---- include/asm-v850/clinkage.h | 26 - include/asm-v850/cputime.h | 6 - include/asm-v850/current.h | 47 -- include/asm-v850/delay.h | 47 -- include/asm-v850/device.h | 7 - include/asm-v850/div64.h | 1 - include/asm-v850/dma-mapping.h | 11 - include/asm-v850/dma.h | 18 - include/asm-v850/elf.h | 99 --- include/asm-v850/emergency-restart.h | 6 - include/asm-v850/entry.h | 113 ---- include/asm-v850/errno.h | 6 - include/asm-v850/fb.h | 12 - include/asm-v850/fcntl.h | 11 - include/asm-v850/flat.h | 133 ---- include/asm-v850/fpga85e2c.h | 82 --- include/asm-v850/futex.h | 6 - include/asm-v850/gbus_int.h | 97 --- include/asm-v850/hardirq.h | 28 - include/asm-v850/highres_timer.h | 44 -- include/asm-v850/hw_irq.h | 4 - include/asm-v850/io.h | 142 ---- include/asm-v850/ioctl.h | 1 - include/asm-v850/ioctls.h | 84 --- include/asm-v850/ipcbuf.h | 29 - include/asm-v850/irq.h | 55 -- include/asm-v850/irq_regs.h | 1 - include/asm-v850/kdebug.h | 1 - include/asm-v850/kmap_types.h | 19 - include/asm-v850/kvm.h | 6 - include/asm-v850/linkage.h | 8 - include/asm-v850/local.h | 6 - include/asm-v850/ma.h | 101 --- include/asm-v850/ma1.h | 50 -- include/asm-v850/machdep.h | 60 -- include/asm-v850/macrology.h | 17 - include/asm-v850/me2.h | 182 ------ include/asm-v850/mman.h | 15 - include/asm-v850/mmu.h | 11 - include/asm-v850/mmu_context.h | 13 - include/asm-v850/module.h | 62 -- include/asm-v850/msgbuf.h | 31 - include/asm-v850/mutex.h | 9 - include/asm-v850/page.h | 124 ---- include/asm-v850/param.h | 33 - include/asm-v850/pci.h | 119 ---- include/asm-v850/percpu.h | 14 - include/asm-v850/pgalloc.h | 22 - include/asm-v850/pgtable.h | 59 -- include/asm-v850/poll.h | 9 - include/asm-v850/posix_types.h | 72 -- include/asm-v850/processor.h | 120 ---- include/asm-v850/ptrace.h | 121 ---- include/asm-v850/resource.h | 6 - include/asm-v850/rte_cb.h | 78 --- include/asm-v850/rte_ma1_cb.h | 128 ---- include/asm-v850/rte_mb_a_pci.h | 56 -- include/asm-v850/rte_me2_cb.h | 202 ------ include/asm-v850/rte_nb85e_cb.h | 111 ---- include/asm-v850/scatterlist.h | 31 - include/asm-v850/sections.h | 6 - include/asm-v850/segment.h | 36 - include/asm-v850/semaphore.h | 1 - include/asm-v850/sembuf.h | 25 - include/asm-v850/serial.h | 56 -- include/asm-v850/setup.h | 6 - include/asm-v850/shmbuf.h | 42 -- include/asm-v850/shmparam.h | 6 - include/asm-v850/sigcontext.h | 25 - include/asm-v850/siginfo.h | 6 - include/asm-v850/signal.h | 168 ----- include/asm-v850/sim.h | 47 -- include/asm-v850/sim85e2.h | 69 -- include/asm-v850/sim85e2c.h | 26 - include/asm-v850/sim85e2s.h | 28 - include/asm-v850/simsyscall.h | 99 --- include/asm-v850/socket.h | 57 -- include/asm-v850/sockios.h | 13 - include/asm-v850/stat.h | 73 --- include/asm-v850/statfs.h | 6 - include/asm-v850/string.h | 25 - include/asm-v850/system.h | 123 ---- include/asm-v850/teg.h | 101 --- include/asm-v850/termbits.h | 200 ------ include/asm-v850/termios.h | 90 --- include/asm-v850/thread_info.h | 129 ---- include/asm-v850/timex.h | 18 - include/asm-v850/tlb.h | 21 - include/asm-v850/tlbflush.h | 64 -- include/asm-v850/topology.h | 6 - include/asm-v850/types.h | 36 - include/asm-v850/uaccess.h | 159 ----- include/asm-v850/ucontext.h | 14 - include/asm-v850/unaligned.h | 22 - include/asm-v850/unistd.h | 244 ------- include/asm-v850/user.h | 52 -- include/asm-v850/v850e.h | 21 - include/asm-v850/v850e2.h | 69 -- include/asm-v850/v850e2_cache.h | 75 --- include/asm-v850/v850e_cache.h | 48 -- include/asm-v850/v850e_intc.h | 133 ---- include/asm-v850/v850e_timer_c.h | 48 -- include/asm-v850/v850e_timer_d.h | 62 -- include/asm-v850/v850e_uart.h | 76 --- include/asm-v850/v850e_uarta.h | 278 -------- include/asm-v850/v850e_uartb.h | 262 -------- include/asm-v850/v850e_utils.h | 35 - include/linux/audit.h | 1 - include/linux/module.h | 2 +- include/linux/serial_core.h | 3 - include/linux/syscalls.h | 2 +- scripts/genksyms/genksyms.c | 3 +- scripts/mod/file2alias.c | 2 +- scripts/mod/mk_elfconfig.c | 2 +- 204 files changed, 5 insertions(+), 18406 deletions(-) delete mode 100644 arch/v850/Kconfig delete mode 100644 arch/v850/Kconfig.debug delete mode 100644 arch/v850/Makefile delete mode 100644 arch/v850/README delete mode 100644 arch/v850/configs/rte-ma1-cb_defconfig delete mode 100644 arch/v850/configs/rte-me2-cb_defconfig delete mode 100644 arch/v850/configs/sim_defconfig delete mode 100644 arch/v850/kernel/Makefile delete mode 100644 arch/v850/kernel/anna-rom.ld delete mode 100644 arch/v850/kernel/anna.c delete mode 100644 arch/v850/kernel/anna.ld delete mode 100644 arch/v850/kernel/as85ep1-rom.ld delete mode 100644 arch/v850/kernel/as85ep1.c delete mode 100644 arch/v850/kernel/as85ep1.ld delete mode 100644 arch/v850/kernel/asm-offsets.c delete mode 100644 arch/v850/kernel/bug.c delete mode 100644 arch/v850/kernel/entry.S delete mode 100644 arch/v850/kernel/fpga85e2c.c delete mode 100644 arch/v850/kernel/fpga85e2c.ld delete mode 100644 arch/v850/kernel/gbus_int.c delete mode 100644 arch/v850/kernel/head.S delete mode 100644 arch/v850/kernel/highres_timer.c delete mode 100644 arch/v850/kernel/init_task.c delete mode 100644 arch/v850/kernel/intv.S delete mode 100644 arch/v850/kernel/irq.c delete mode 100644 arch/v850/kernel/ma.c delete mode 100644 arch/v850/kernel/mach.c delete mode 100644 arch/v850/kernel/mach.h delete mode 100644 arch/v850/kernel/me2.c delete mode 100644 arch/v850/kernel/memcons.c delete mode 100644 arch/v850/kernel/module.c delete mode 100644 arch/v850/kernel/process.c delete mode 100644 arch/v850/kernel/procfs.c delete mode 100644 arch/v850/kernel/ptrace.c delete mode 100644 arch/v850/kernel/rte_cb.c delete mode 100644 arch/v850/kernel/rte_cb_leds.c delete mode 100644 arch/v850/kernel/rte_cb_multi.c delete mode 100644 arch/v850/kernel/rte_ma1_cb-rom.ld delete mode 100644 arch/v850/kernel/rte_ma1_cb.c delete mode 100644 arch/v850/kernel/rte_ma1_cb.ld delete mode 100644 arch/v850/kernel/rte_mb_a_pci.c delete mode 100644 arch/v850/kernel/rte_me2_cb.c delete mode 100644 arch/v850/kernel/rte_me2_cb.ld delete mode 100644 arch/v850/kernel/rte_nb85e_cb-multi.ld delete mode 100644 arch/v850/kernel/rte_nb85e_cb.c delete mode 100644 arch/v850/kernel/rte_nb85e_cb.ld delete mode 100644 arch/v850/kernel/setup.c delete mode 100644 arch/v850/kernel/signal.c delete mode 100644 arch/v850/kernel/sim.c delete mode 100644 arch/v850/kernel/sim.ld delete mode 100644 arch/v850/kernel/sim85e2.c delete mode 100644 arch/v850/kernel/sim85e2.ld delete mode 100644 arch/v850/kernel/simcons.c delete mode 100644 arch/v850/kernel/syscalls.c delete mode 100644 arch/v850/kernel/teg.c delete mode 100644 arch/v850/kernel/time.c delete mode 100644 arch/v850/kernel/v850_ksyms.c delete mode 100644 arch/v850/kernel/v850e2_cache.c delete mode 100644 arch/v850/kernel/v850e_cache.c delete mode 100644 arch/v850/kernel/v850e_intc.c delete mode 100644 arch/v850/kernel/v850e_timer_d.c delete mode 100644 arch/v850/kernel/v850e_utils.c delete mode 100644 arch/v850/kernel/vmlinux.lds.S delete mode 100644 arch/v850/lib/Makefile delete mode 100644 arch/v850/lib/ashldi3.c delete mode 100644 arch/v850/lib/ashrdi3.c delete mode 100644 arch/v850/lib/checksum.c delete mode 100644 arch/v850/lib/lshrdi3.c delete mode 100644 arch/v850/lib/memcpy.c delete mode 100644 arch/v850/lib/memset.c delete mode 100644 arch/v850/lib/muldi3.c delete mode 100644 arch/v850/lib/negdi2.c delete mode 100644 include/asm-v850/Kbuild delete mode 100644 include/asm-v850/a.out.h delete mode 100644 include/asm-v850/anna.h delete mode 100644 include/asm-v850/as85ep1.h delete mode 100644 include/asm-v850/asm.h delete mode 100644 include/asm-v850/atomic.h delete mode 100644 include/asm-v850/auxvec.h delete mode 100644 include/asm-v850/bitops.h delete mode 100644 include/asm-v850/bug.h delete mode 100644 include/asm-v850/bugs.h delete mode 100644 include/asm-v850/byteorder.h delete mode 100644 include/asm-v850/cache.h delete mode 100644 include/asm-v850/cacheflush.h delete mode 100644 include/asm-v850/checksum.h delete mode 100644 include/asm-v850/clinkage.h delete mode 100644 include/asm-v850/cputime.h delete mode 100644 include/asm-v850/current.h delete mode 100644 include/asm-v850/delay.h delete mode 100644 include/asm-v850/device.h delete mode 100644 include/asm-v850/div64.h delete mode 100644 include/asm-v850/dma-mapping.h delete mode 100644 include/asm-v850/dma.h delete mode 100644 include/asm-v850/elf.h delete mode 100644 include/asm-v850/emergency-restart.h delete mode 100644 include/asm-v850/entry.h delete mode 100644 include/asm-v850/errno.h delete mode 100644 include/asm-v850/fb.h delete mode 100644 include/asm-v850/fcntl.h delete mode 100644 include/asm-v850/flat.h delete mode 100644 include/asm-v850/fpga85e2c.h delete mode 100644 include/asm-v850/futex.h delete mode 100644 include/asm-v850/gbus_int.h delete mode 100644 include/asm-v850/hardirq.h delete mode 100644 include/asm-v850/highres_timer.h delete mode 100644 include/asm-v850/hw_irq.h delete mode 100644 include/asm-v850/io.h delete mode 100644 include/asm-v850/ioctl.h delete mode 100644 include/asm-v850/ioctls.h delete mode 100644 include/asm-v850/ipcbuf.h delete mode 100644 include/asm-v850/irq.h delete mode 100644 include/asm-v850/irq_regs.h delete mode 100644 include/asm-v850/kdebug.h delete mode 100644 include/asm-v850/kmap_types.h delete mode 100644 include/asm-v850/kvm.h delete mode 100644 include/asm-v850/linkage.h delete mode 100644 include/asm-v850/local.h delete mode 100644 include/asm-v850/ma.h delete mode 100644 include/asm-v850/ma1.h delete mode 100644 include/asm-v850/machdep.h delete mode 100644 include/asm-v850/macrology.h delete mode 100644 include/asm-v850/me2.h delete mode 100644 include/asm-v850/mman.h delete mode 100644 include/asm-v850/mmu.h delete mode 100644 include/asm-v850/mmu_context.h delete mode 100644 include/asm-v850/module.h delete mode 100644 include/asm-v850/msgbuf.h delete mode 100644 include/asm-v850/mutex.h delete mode 100644 include/asm-v850/page.h delete mode 100644 include/asm-v850/param.h delete mode 100644 include/asm-v850/pci.h delete mode 100644 include/asm-v850/percpu.h delete mode 100644 include/asm-v850/pgalloc.h delete mode 100644 include/asm-v850/pgtable.h delete mode 100644 include/asm-v850/poll.h delete mode 100644 include/asm-v850/posix_types.h delete mode 100644 include/asm-v850/processor.h delete mode 100644 include/asm-v850/ptrace.h delete mode 100644 include/asm-v850/resource.h delete mode 100644 include/asm-v850/rte_cb.h delete mode 100644 include/asm-v850/rte_ma1_cb.h delete mode 100644 include/asm-v850/rte_mb_a_pci.h delete mode 100644 include/asm-v850/rte_me2_cb.h delete mode 100644 include/asm-v850/rte_nb85e_cb.h delete mode 100644 include/asm-v850/scatterlist.h delete mode 100644 include/asm-v850/sections.h delete mode 100644 include/asm-v850/segment.h delete mode 100644 include/asm-v850/semaphore.h delete mode 100644 include/asm-v850/sembuf.h delete mode 100644 include/asm-v850/serial.h delete mode 100644 include/asm-v850/setup.h delete mode 100644 include/asm-v850/shmbuf.h delete mode 100644 include/asm-v850/shmparam.h delete mode 100644 include/asm-v850/sigcontext.h delete mode 100644 include/asm-v850/siginfo.h delete mode 100644 include/asm-v850/signal.h delete mode 100644 include/asm-v850/sim.h delete mode 100644 include/asm-v850/sim85e2.h delete mode 100644 include/asm-v850/sim85e2c.h delete mode 100644 include/asm-v850/sim85e2s.h delete mode 100644 include/asm-v850/simsyscall.h delete mode 100644 include/asm-v850/socket.h delete mode 100644 include/asm-v850/sockios.h delete mode 100644 include/asm-v850/stat.h delete mode 100644 include/asm-v850/statfs.h delete mode 100644 include/asm-v850/string.h delete mode 100644 include/asm-v850/system.h delete mode 100644 include/asm-v850/teg.h delete mode 100644 include/asm-v850/termbits.h delete mode 100644 include/asm-v850/termios.h delete mode 100644 include/asm-v850/thread_info.h delete mode 100644 include/asm-v850/timex.h delete mode 100644 include/asm-v850/tlb.h delete mode 100644 include/asm-v850/tlbflush.h delete mode 100644 include/asm-v850/topology.h delete mode 100644 include/asm-v850/types.h delete mode 100644 include/asm-v850/uaccess.h delete mode 100644 include/asm-v850/ucontext.h delete mode 100644 include/asm-v850/unaligned.h delete mode 100644 include/asm-v850/unistd.h delete mode 100644 include/asm-v850/user.h delete mode 100644 include/asm-v850/v850e.h delete mode 100644 include/asm-v850/v850e2.h delete mode 100644 include/asm-v850/v850e2_cache.h delete mode 100644 include/asm-v850/v850e_cache.h delete mode 100644 include/asm-v850/v850e_intc.h delete mode 100644 include/asm-v850/v850e_timer_c.h delete mode 100644 include/asm-v850/v850e_timer_d.h delete mode 100644 include/asm-v850/v850e_uart.h delete mode 100644 include/asm-v850/v850e_uarta.h delete mode 100644 include/asm-v850/v850e_uartb.h delete mode 100644 include/asm-v850/v850e_utils.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 7ffd78c4e277..7e5c7b0290bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4131,9 +4131,6 @@ W: http://www.uclinux.org/ L: uclinux-dev@uclinux.org (subscribers-only) S: Maintained -UCLINUX FOR NEC V850 -P: Miles Bader - UCLINUX FOR RENESAS H8/300 P: Yoshinori Sato M: ysato@users.sourceforge.jp diff --git a/arch/v850/Kconfig b/arch/v850/Kconfig deleted file mode 100644 index 4379f43505ef..000000000000 --- a/arch/v850/Kconfig +++ /dev/null @@ -1,353 +0,0 @@ -############################################################################# -# -# For a description of the syntax of this configuration file, -# see Documentation/kbuild/kconfig-language.txt. -# -############################################################################# - -mainmenu "uClinux/v850 (w/o MMU) Kernel Configuration" - -config MMU - bool - default n -config ZONE_DMA - bool - default y -config RWSEM_GENERIC_SPINLOCK - bool - default y -config RWSEM_XCHGADD_ALGORITHM - bool - default n -config GENERIC_FIND_NEXT_BIT - bool - default y -config GENERIC_HWEIGHT - bool - default y -config GENERIC_CALIBRATE_DELAY - bool - default y - -config GENERIC_HARDIRQS - bool - default y - -config GENERIC_IRQ_PROBE - bool - default y - -config GENERIC_TIME - bool - default y - -config TIME_LOW_RES - bool - default y - -config ARCH_HAS_ILOG2_U32 - bool - default n - -config ARCH_HAS_ILOG2_U64 - bool - default n - -config ARCH_SUPPORTS_AOUT - def_bool y - -# Turn off some random 386 crap that can affect device config -config ISA - bool - default n -config ISAPNP - bool - default n -config EISA - bool - default n -config MCA - bool - default n - - -############################################################################# -#### v850-specific config - -# Define the architecture -config V850 - bool - default y - select HAVE_IDE - -menu "Processor type and features" - - choice - prompt "Platform" - default GDB - config V850E_SIM - bool "GDB" - config RTE_CB_MA1 - bool "RTE-V850E/MA1-CB" - config RTE_CB_NB85E - bool "RTE-V850E/NB85E-CB" - config RTE_CB_ME2 - bool "RTE-V850E/ME2-CB" - config V850E_AS85EP1 - bool "AS85EP1" - config V850E2_SIM85E2C - bool "sim85e2c" - config V850E2_SIM85E2S - bool "sim85e2s" - config V850E2_FPGA85E2C - bool "NA85E2C-FPGA" - config V850E2_ANNA - bool "Anna" - endchoice - - #### V850E processor-specific config - - # All CPUs currently supported use the v850e architecture - config V850E - bool - default y - - # The RTE-V850E/MA1-CB is the only type of V850E/MA1 platform we - # currently support - config V850E_MA1 - bool - depends on RTE_CB_MA1 - default y - # Similarly for the RTE-V850E/NB85E-CB - V850E/TEG - config V850E_TEG - bool - depends on RTE_CB_NB85E - default y - # ... and the RTE-V850E/ME2-CB - V850E/ME2 - config V850E_ME2 - bool - depends on RTE_CB_ME2 - default y - - - #### sim85e2-specific config - - config V850E2_SIM85E2 - bool - depends on V850E2_SIM85E2C || V850E2_SIM85E2S - default y - - - #### V850E2 processor-specific config - - # V850E2 processors - config V850E2 - bool - depends on V850E2_SIM85E2 || V850E2_FPGA85E2C || V850E2_ANNA - default y - - - #### RTE-CB platform-specific config - - # Boards in the RTE-x-CB series - config RTE_CB - bool - depends on RTE_CB_MA1 || RTE_CB_NB85E || RTE_CB_ME2 - default y - - config RTE_CB_MULTI - bool - # RTE_CB_NB85E can either have multi ROM support or not, but - # other platforms (currently only RTE_CB_MA1) require it. - prompt "Multi monitor ROM support" if RTE_CB_NB85E - depends on RTE_CB_MA1 || RTE_CB_NB85E - default y - - config RTE_CB_MULTI_DBTRAP - bool "Pass illegal insn trap / dbtrap to kernel" - depends on RTE_CB_MULTI - default n - - config RTE_CB_MA1_KSRAM - bool "Kernel in SRAM (limits size of kernel)" - depends on RTE_CB_MA1 && RTE_CB_MULTI - default n - - config RTE_MB_A_PCI - bool "Mother-A PCI support" - depends on RTE_CB - default y - - # The GBUS is used to talk to the RTE-MOTHER-A board - config RTE_GBUS_INT - bool - depends on RTE_MB_A_PCI - default y - - # The only PCI bus we support is on the RTE-MOTHER-A board - config PCI - bool - default RTE_MB_A_PCI - - #### Some feature-specific configs - - # Everything except for the GDB simulator uses the same interrupt controller - config V850E_INTC - bool - default !V850E_SIM - - # Everything except for the various simulators uses the "Timer D" unit - config V850E_TIMER_D - bool - default !V850E_SIM && !V850E2_SIM85E2 - - # Cache control used on some v850e1 processors - config V850E_CACHE - bool - default V850E_TEG || V850E_ME2 - - # Cache control used on v850e2 processors; I think this should - # actually apply to more, but currently only the SIM85E2S uses it - config V850E2_CACHE - bool - default V850E2_SIM85E2S - - config NO_CACHE - bool - default !V850E_CACHE && !V850E2_CACHE - - # HZ depends on the platform - config HZ - int - default 24 if V850E_SIM || V850E2_SIM85E2 - default 122 if V850E2_FPGA85E2C - default 100 - - #### Misc config - - config ROM_KERNEL - bool "Kernel in ROM" - depends on V850E2_ANNA || V850E_AS85EP1 || RTE_CB_ME2 - - # Some platforms pre-zero memory, in which case the kernel doesn't need to - config ZERO_BSS - bool - depends on !V850E2_SIM85E2C - default y - - # The crappy-ass zone allocator requires that the start of allocatable - # memory be aligned to the largest possible allocation. - config FORCE_MAX_ZONEORDER - int - default 8 if V850E2_SIM85E2C || V850E2_FPGA85E2C - - config V850E_HIGHRES_TIMER - bool "High resolution timer support" - depends on V850E_TIMER_D - config TIME_BOOTUP - bool "Time bootup" - depends on V850E_HIGHRES_TIMER - - config RESET_GUARD - bool "Reset Guard" - -source "mm/Kconfig" - -endmenu - - -############################################################################# - -source init/Kconfig - -############################################################################# - -menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)" - -# config PCI -# bool "PCI support" -# help -# Support for PCI bus. - -source "drivers/pci/Kconfig" - -source "drivers/pcmcia/Kconfig" - -source "drivers/pci/hotplug/Kconfig" - -endmenu - -menu "Executable file formats" - -source "fs/Kconfig.binfmt" - -endmenu - -source "net/Kconfig" - -############################################################################# - -source "drivers/base/Kconfig" - -source drivers/mtd/Kconfig - -source drivers/parport/Kconfig - -#source drivers/pnp/Kconfig - -source drivers/block/Kconfig - -############################################################################# - -menu "Disk device support" - -source "drivers/ide/Kconfig" - -source "drivers/scsi/Kconfig" - -endmenu - -############################################################################# - - -source "drivers/md/Kconfig" - -source "drivers/message/fusion/Kconfig" - -source "drivers/ieee1394/Kconfig" - -source "drivers/message/i2o/Kconfig" - -source "drivers/net/Kconfig" - -source "drivers/isdn/Kconfig" - -#source "drivers/telephony/Kconfig" - -# -# input before char - char/joystick depends on it. As does USB. -# -source "drivers/input/Kconfig" - -source "drivers/char/Kconfig" - -#source drivers/misc/Config.in -source "drivers/media/Kconfig" - -source "fs/Kconfig" - -source "drivers/video/Kconfig" - -source "sound/Kconfig" - -source "drivers/usb/Kconfig" - -source "arch/v850/Kconfig.debug" - -source "security/Kconfig" - -source "crypto/Kconfig" - -source "lib/Kconfig" - -############################################################################# diff --git a/arch/v850/Kconfig.debug b/arch/v850/Kconfig.debug deleted file mode 100644 index 4acfb9cca1ca..000000000000 --- a/arch/v850/Kconfig.debug +++ /dev/null @@ -1,10 +0,0 @@ -menu "Kernel hacking" - -source "lib/Kconfig.debug" - -config NO_KERNEL_MSG - bool "Suppress Kernel BUG Messages" - help - Do not output any debug BUG messages within the kernel. - -endmenu diff --git a/arch/v850/Makefile b/arch/v850/Makefile deleted file mode 100644 index 8b629df0029a..000000000000 --- a/arch/v850/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# -# arch/v850/Makefile -# -# Copyright (C) 2001,02,03,05 NEC Corporation -# Copyright (C) 2001,02,03,05 Miles Bader -# -# This file is included by the global makefile so that you can add your own -# architecture-specific flags and dependencies. Remember to do have actions -# for "archclean" and "archdep" for cleaning up and making dependencies for -# this architecture -# -# This file is subject to the terms and conditions of the GNU General Public -# License. See the file "COPYING" in the main directory of this archive -# for more details. -# - -arch_dir = arch/v850 - -KBUILD_CFLAGS += -mv850e -# r16 is a fixed pointer to the current task -KBUILD_CFLAGS += -ffixed-r16 -mno-prolog-function -KBUILD_CFLAGS += -fno-builtin -KBUILD_CFLAGS += -D__linux__ -DUTS_SYSNAME=\"uClinux\" - -# By default, build a kernel that runs on the gdb v850 simulator. -KBUILD_DEFCONFIG := sim_defconfig - -# This prevents the linker from consolidating the .gnu.linkonce.this_module -# section into .text (which the v850 default linker script for -r does for -# some reason) -LDFLAGS_MODULE += --unique=.gnu.linkonce.this_module - -OBJCOPY_FLAGS_BLOB := -I binary -O elf32-little -B v850e - - -head-y := $(arch_dir)/kernel/head.o $(arch_dir)/kernel/init_task.o -core-y += $(arch_dir)/kernel/ -libs-y += $(arch_dir)/lib/ - - -# Deal with the initial contents of the root device -ifdef ROOT_FS_IMAGE -core-y += root_fs_image.o - -# Because the kernel build-system erases all explicit .o build rules, we -# have to use an intermediate target to fool it into building for us. -# This results in it being built anew each time, but that's alright. -root_fs_image.o: root_fs_image_force - -root_fs_image_force: $(ROOT_FS_IMAGE) - $(OBJCOPY) $(OBJCOPY_FLAGS_BLOB) --rename-section .data=.root,alloc,load,readonly,data,contents $< root_fs_image.o -endif - -CLEAN_FILES += root_fs_image.o diff --git a/arch/v850/README b/arch/v850/README deleted file mode 100644 index 12f7f7a665e0..000000000000 --- a/arch/v850/README +++ /dev/null @@ -1,44 +0,0 @@ -This port to the NEC V850E processor supports the following platforms: - - "sim" - The gdb v850e simulator (CONFIG_V850E_SIM). - - "rte-ma1-cb" - The Midas labs RTE-V850E/MA1-CB and RTE-V850E/NB85E-CB evaluation - boards (CONFIG_RTE_CB_MA1 and CONFIG_RTE_CB_NB85E). This support - has only been tested when running with the Multi-debugger monitor - ROM (for the Green Hills Multi debugger). The optional NEC - Solution Gear RTE-MOTHER-A motherboard is also supported, which - allows PCI boards to be used (CONFIG_RTE_MB_A_PCI). - - "rte-me2-cb" - The Midas labs RTE-V850E/ME2-CB evaluation board (CONFIG_RTE_CB_ME2). - This has only been tested using a kernel downloaded via an ICE - connection using the Multi debugger. Support for the RTE-MOTHER-A is - present, but hasn't been tested (unlike the other Midas labs cpu - boards, the RTE-V850E/ME2-CB includes an ethernet adaptor). - - "as85ep1" - The NEC AS85EP1 V850E evaluation chip/board (CONFIG_V850E_AS85EP1). - - "anna" - The NEC `Anna' (board/chip) implementation of the V850E2 processor - (CONFIG_V850E2_ANNA). - - "sim85e2c", "sim85e2s" - The sim85e2c and sim85e2s simulators, which are verilog simulations - of the V850E2 NA85E2C/NA85E2S cpu cores (CONFIG_V850E2_SIM85E2C and - CONFIG_V850E2_SIM85E2S). - - "fpga85e2c" - A FPGA implementation of the V850E2 NA85E2C cpu core - (CONFIG_V850E2_FPGA85E2C). - -To get a default kernel configuration for a particular platform, you can -use a _defconfig make target (e.g., "make rte-me2-cb_defconfig"); -to see which default configurations are possible, look in the directory -"arch/v850/configs". - -Porting to anything with a V850E/MA1 or MA2 processor should be simple. -See the file and the files it includes for an example of -how to add platform/chip-specific support. diff --git a/arch/v850/configs/rte-ma1-cb_defconfig b/arch/v850/configs/rte-ma1-cb_defconfig deleted file mode 100644 index 1a5beda36e29..000000000000 --- a/arch/v850/configs/rte-ma1-cb_defconfig +++ /dev/null @@ -1,617 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.13-uc0 -# Fri Sep 2 13:54:27 2005 -# -# CONFIG_MMU is not set -# CONFIG_UID16 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_GENERIC_CALIBRATE_DELAY=y -# CONFIG_ISA is not set -# CONFIG_ISAPNP is not set -# CONFIG_EISA is not set -# CONFIG_MCA is not set -CONFIG_V850=y - -# -# Processor type and features -# -# CONFIG_V850E_SIM is not set -CONFIG_RTE_CB_MA1=y -# CONFIG_RTE_CB_NB85E is not set -# CONFIG_RTE_CB_ME2 is not set -# CONFIG_V850E_AS85EP1 is not set -# CONFIG_V850E2_SIM85E2C is not set -# CONFIG_V850E2_SIM85E2S is not set -# CONFIG_V850E2_FPGA85E2C is not set -# CONFIG_V850E2_ANNA is not set -CONFIG_V850E=y -CONFIG_V850E_MA1=y -CONFIG_RTE_CB=y -CONFIG_RTE_CB_MULTI=y -CONFIG_RTE_CB_MULTI_DBTRAP=y -# CONFIG_RTE_CB_MA1_KSRAM is not set -CONFIG_RTE_MB_A_PCI=y -CONFIG_RTE_GBUS_INT=y -CONFIG_PCI=y -CONFIG_V850E_INTC=y -CONFIG_V850E_TIMER_D=y -# CONFIG_V850E_CACHE is not set -# CONFIG_V850E2_CACHE is not set -CONFIG_NO_CACHE=y -CONFIG_ZERO_BSS=y -# CONFIG_V850E_HIGHRES_TIMER is not set -# CONFIG_RESET_GUARD is not set -CONFIG_LARGE_ALLOCS=y -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y - -# -# Code maturity level options -# -# CONFIG_EXPERIMENTAL is not set -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_AUDIT is not set -# CONFIG_HOTPLUG is not set -CONFIG_KOBJECT_UEVENT=y -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_PRINTK=y -CONFIG_BUG=y -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -CONFIG_BASE_SMALL=1 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Bus options (PCI, PCMCIA, EISA, MCA, ISA) -# -# CONFIG_PCI_LEGACY_PROC is not set -# CONFIG_PCI_NAMES is not set -# CONFIG_PCI_DEBUG is not set - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# - -# -# Executable file formats -# -CONFIG_BINFMT_FLAT=y -# CONFIG_BINFMT_ZFLAT is not set -# CONFIG_BINFMT_SHARED_FLAT is not set -# CONFIG_BINFMT_MISC is not set - -# -# Networking -# -CONFIG_NET=y - -# -# Networking options -# -# CONFIG_PACKET is not set -# CONFIG_UNIX is not set -# CONFIG_NET_KEY is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_PNP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_TUNNEL is not set -# CONFIG_IP_TCPDIAG is not set -# CONFIG_IP_TCPDIAG_IPV6 is not set -# CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_BIC=y -# CONFIG_IPV6 is not set -# CONFIG_NETFILTER is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_PARTITIONS is not set - -# -# User Modules And Translation Layers -# -# CONFIG_MTD_CHAR is not set -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PLATRAM is not set - -# -# Self-contained MTD device drivers -# -# CONFIG_MTD_PMC551 is not set -CONFIG_MTD_SLRAM=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_CPQ_DA is not set -# CONFIG_BLK_CPQ_CISS_DA is not set -# CONFIG_BLK_DEV_DAC960 is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_SX8 is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set -# CONFIG_ATA_OVER_ETH is not set - -# -# Disk device support -# - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# -# CONFIG_IEEE1394 is not set - -# -# I2O device support -# -# CONFIG_I2O is not set - -# -# Network device support -# -CONFIG_NETDEVICES=y -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set - -# -# ARCnet devices -# -# CONFIG_ARCNET is not set - -# -# Ethernet (10 or 100Mbit) -# -CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNGEM is not set -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_NET_VENDOR_SMC is not set - -# -# Tulip family network device support -# -# CONFIG_NET_TULIP is not set -# CONFIG_HP100 is not set -# CONFIG_NE2000 is not set -CONFIG_NET_PCI=y -# CONFIG_PCNET32 is not set -# CONFIG_AMD8111_ETH is not set -# CONFIG_ADAPTEC_STARFIRE is not set -# CONFIG_DGRS is not set -CONFIG_EEPRO100=y -# CONFIG_E100 is not set -# CONFIG_FEALNX is not set -# CONFIG_NATSEMI is not set -# CONFIG_NE2K_PCI is not set -# CONFIG_8139TOO is not set -# CONFIG_SIS900 is not set -# CONFIG_EPIC100 is not set -# CONFIG_SUNDANCE is not set -# CONFIG_TLAN is not set -# CONFIG_VIA_RHINE is not set - -# -# Ethernet (1000 Mbit) -# -# CONFIG_ACENIC is not set -# CONFIG_DL2K is not set -# CONFIG_E1000 is not set -# CONFIG_NS83820 is not set -# CONFIG_HAMACHI is not set -# CONFIG_R8169 is not set -# CONFIG_SK98LIN is not set -# CONFIG_VIA_VELOCITY is not set -# CONFIG_TIGON3 is not set -# CONFIG_BNX2 is not set - -# -# Ethernet (10000 Mbit) -# -# CONFIG_IXGB is not set -# CONFIG_S2IO is not set - -# -# Token Ring devices -# -# CONFIG_TR is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set -# CONFIG_FDDI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -# CONFIG_SERIO is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_V850E_UART=y -CONFIG_V850E_UART_CONSOLE=y -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_SERIAL_JSM is not set -# CONFIG_UNIX98_PTYS is not set -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_DRM is not set -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -CONFIG_ROMFS_FS=y -# CONFIG_MAGIC_ROM_PTR is not set -CONFIG_INOTIFY=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_HFSPLUS_FS is not set -# CONFIG_JFFS_FS is not set -# CONFIG_JFFS2_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Network File Systems -# -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -# CONFIG_NFSD is not set -CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=y -# CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB_ARCH_HAS_OHCI=y -# CONFIG_USB is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -# CONFIG_NO_KERNEL_MSG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -# CONFIG_CRYPTO is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC32 is not set -# CONFIG_LIBCRC32C is not set diff --git a/arch/v850/configs/rte-me2-cb_defconfig b/arch/v850/configs/rte-me2-cb_defconfig deleted file mode 100644 index 15e666478061..000000000000 --- a/arch/v850/configs/rte-me2-cb_defconfig +++ /dev/null @@ -1,462 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.13-uc0 -# Fri Sep 2 13:47:50 2005 -# -# CONFIG_MMU is not set -# CONFIG_UID16 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_GENERIC_CALIBRATE_DELAY=y -# CONFIG_ISA is not set -# CONFIG_ISAPNP is not set -# CONFIG_EISA is not set -# CONFIG_MCA is not set -CONFIG_V850=y - -# -# Processor type and features -# -# CONFIG_V850E_SIM is not set -# CONFIG_RTE_CB_MA1 is not set -# CONFIG_RTE_CB_NB85E is not set -CONFIG_RTE_CB_ME2=y -# CONFIG_V850E_AS85EP1 is not set -# CONFIG_V850E2_SIM85E2C is not set -# CONFIG_V850E2_SIM85E2S is not set -# CONFIG_V850E2_FPGA85E2C is not set -# CONFIG_V850E2_ANNA is not set -CONFIG_V850E=y -CONFIG_V850E_ME2=y -CONFIG_RTE_CB=y -# CONFIG_RTE_MB_A_PCI is not set -# CONFIG_PCI is not set -CONFIG_V850E_INTC=y -CONFIG_V850E_TIMER_D=y -CONFIG_V850E_CACHE=y -# CONFIG_V850E2_CACHE is not set -# CONFIG_NO_CACHE is not set -# CONFIG_ROM_KERNEL is not set -CONFIG_ZERO_BSS=y -# CONFIG_V850E_HIGHRES_TIMER is not set -# CONFIG_RESET_GUARD is not set -CONFIG_LARGE_ALLOCS=y -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y - -# -# Code maturity level options -# -# CONFIG_EXPERIMENTAL is not set -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_HOTPLUG is not set -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_PRINTK=y -CONFIG_BUG=y -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -CONFIG_BASE_SMALL=1 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Bus options (PCI, PCMCIA, EISA, MCA, ISA) -# - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# - -# -# Executable file formats -# -CONFIG_BINFMT_FLAT=y -# CONFIG_BINFMT_ZFLAT is not set -# CONFIG_BINFMT_SHARED_FLAT is not set -# CONFIG_BINFMT_MISC is not set - -# -# Networking -# -# CONFIG_NET is not set - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_PARTITIONS is not set - -# -# User Modules And Translation Layers -# -# CONFIG_MTD_CHAR is not set -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PLATRAM is not set - -# -# Self-contained MTD device drivers -# -CONFIG_MTD_SLRAM=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set - -# -# Disk device support -# - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Network device support -# -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -CONFIG_SERIO=y -# CONFIG_SERIO_I8042 is not set -# CONFIG_SERIO_SERPORT is not set -# CONFIG_SERIO_LIBPS2 is not set -# CONFIG_SERIO_RAW is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_NR_UARTS=1 -# CONFIG_SERIAL_8250_EXTENDED is not set - -# -# Non-8250 serial port support -# -# CONFIG_V850E_UART is not set -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_UNIX98_PTYS is not set -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -CONFIG_ROMFS_FS=y -# CONFIG_MAGIC_ROM_PTR is not set -CONFIG_INOTIFY=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_HFSPLUS_FS is not set -# CONFIG_JFFS_FS is not set -# CONFIG_JFFS2_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -# CONFIG_USB_ARCH_HAS_HCD is not set -# CONFIG_USB_ARCH_HAS_OHCI is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -# CONFIG_NO_KERNEL_MSG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -# CONFIG_CRYPTO is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC32 is not set -# CONFIG_LIBCRC32C is not set diff --git a/arch/v850/configs/sim_defconfig b/arch/v850/configs/sim_defconfig deleted file mode 100644 index f31ba7398ad0..000000000000 --- a/arch/v850/configs/sim_defconfig +++ /dev/null @@ -1,451 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.13-uc0 -# Fri Sep 2 13:36:43 2005 -# -# CONFIG_MMU is not set -# CONFIG_UID16 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_GENERIC_CALIBRATE_DELAY=y -# CONFIG_ISA is not set -# CONFIG_ISAPNP is not set -# CONFIG_EISA is not set -# CONFIG_MCA is not set -CONFIG_V850=y - -# -# Processor type and features -# -CONFIG_V850E_SIM=y -# CONFIG_RTE_CB_MA1 is not set -# CONFIG_RTE_CB_NB85E is not set -# CONFIG_RTE_CB_ME2 is not set -# CONFIG_V850E_AS85EP1 is not set -# CONFIG_V850E2_SIM85E2C is not set -# CONFIG_V850E2_SIM85E2S is not set -# CONFIG_V850E2_FPGA85E2C is not set -# CONFIG_V850E2_ANNA is not set -CONFIG_V850E=y -# CONFIG_PCI is not set -# CONFIG_V850E_INTC is not set -# CONFIG_V850E_TIMER_D is not set -# CONFIG_V850E_CACHE is not set -# CONFIG_V850E2_CACHE is not set -CONFIG_NO_CACHE=y -CONFIG_ZERO_BSS=y -# CONFIG_RESET_GUARD is not set -CONFIG_LARGE_ALLOCS=y -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y - -# -# Code maturity level options -# -# CONFIG_EXPERIMENTAL is not set -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_HOTPLUG is not set -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_PRINTK=y -CONFIG_BUG=y -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -CONFIG_BASE_SMALL=1 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Bus options (PCI, PCMCIA, EISA, MCA, ISA) -# - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# - -# -# Executable file formats -# -CONFIG_BINFMT_FLAT=y -# CONFIG_BINFMT_ZFLAT is not set -# CONFIG_BINFMT_SHARED_FLAT is not set -# CONFIG_BINFMT_MISC is not set - -# -# Networking -# -# CONFIG_NET is not set - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_PARTITIONS is not set - -# -# User Modules And Translation Layers -# -# CONFIG_MTD_CHAR is not set -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PLATRAM is not set - -# -# Self-contained MTD device drivers -# -CONFIG_MTD_SLRAM=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set - -# -# Disk device support -# - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Network device support -# -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -CONFIG_SERIO=y -# CONFIG_SERIO_I8042 is not set -# CONFIG_SERIO_SERPORT is not set -# CONFIG_SERIO_LIBPS2 is not set -# CONFIG_SERIO_RAW is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -# CONFIG_UNIX98_PTYS is not set -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -CONFIG_ROMFS_FS=y -# CONFIG_MAGIC_ROM_PTR is not set -CONFIG_INOTIFY=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_HFSPLUS_FS is not set -# CONFIG_JFFS_FS is not set -# CONFIG_JFFS2_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -# CONFIG_USB_ARCH_HAS_HCD is not set -# CONFIG_USB_ARCH_HAS_OHCI is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -# CONFIG_NO_KERNEL_MSG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -# CONFIG_CRYPTO is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC32 is not set -# CONFIG_LIBCRC32C is not set diff --git a/arch/v850/kernel/Makefile b/arch/v850/kernel/Makefile deleted file mode 100644 index da5889c53576..000000000000 --- a/arch/v850/kernel/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# -# arch/v850/kernel/Makefile -# -# Copyright (C) 2001,02,03 NEC Electronics Corporation -# Copyright (C) 2001,02,03 Miles Bader -# -# This file is subject to the terms and conditions of the GNU General Public -# License. See the file "COPYING" in the main directory of this archive -# for more details. -# - -extra-y := head.o init_task.o vmlinux.lds - -obj-y += intv.o entry.o process.o syscalls.o time.o setup.o \ - signal.o irq.o mach.o ptrace.o bug.o -obj-$(CONFIG_MODULES) += module.o v850_ksyms.o -# chip-specific code -obj-$(CONFIG_V850E_MA1) += ma.o -obj-$(CONFIG_V850E_ME2) += me2.o -obj-$(CONFIG_V850E_TEG) += teg.o -obj-$(CONFIG_V850E_AS85EP1) += as85ep1.o -obj-$(CONFIG_V850E2_ANNA) += anna.o -# platform-specific code -obj-$(CONFIG_V850E_SIM) += sim.o simcons.o -obj-$(CONFIG_V850E2_SIM85E2) += sim85e2.o memcons.o -obj-$(CONFIG_V850E2_FPGA85E2C) += fpga85e2c.o memcons.o -obj-$(CONFIG_RTE_CB) += rte_cb.o rte_cb_leds.o -obj-$(CONFIG_RTE_CB_MA1) += rte_ma1_cb.o -obj-$(CONFIG_RTE_CB_ME2) += rte_me2_cb.o -obj-$(CONFIG_RTE_CB_NB85E) += rte_nb85e_cb.o -obj-$(CONFIG_RTE_CB_MULTI) += rte_cb_multi.o -obj-$(CONFIG_RTE_MB_A_PCI) += rte_mb_a_pci.o -obj-$(CONFIG_RTE_GBUS_INT) += gbus_int.o -# feature-specific code -obj-$(CONFIG_V850E_INTC) += v850e_intc.o -obj-$(CONFIG_V850E_TIMER_D) += v850e_timer_d.o v850e_utils.o -obj-$(CONFIG_V850E_CACHE) += v850e_cache.o -obj-$(CONFIG_V850E2_CACHE) += v850e2_cache.o -obj-$(CONFIG_V850E_HIGHRES_TIMER) += highres_timer.o -obj-$(CONFIG_PROC_FS) += procfs.o diff --git a/arch/v850/kernel/anna-rom.ld b/arch/v850/kernel/anna-rom.ld deleted file mode 100644 index 7c54e7e3f1b1..000000000000 --- a/arch/v850/kernel/anna-rom.ld +++ /dev/null @@ -1,16 +0,0 @@ -/* Linker script for the Midas labs Anna V850E2 evaluation board - (CONFIG_V850E2_ANNA), with kernel in ROM (CONFIG_ROM_KERNEL). */ - -MEMORY { - /* 8MB of flash ROM. */ - ROM : ORIGIN = 0, LENGTH = 0x00800000 - - /* 1MB of static RAM. This memory is mirrored 64 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 64MB of DRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - ROMK_SECTIONS(ROM, SRAM) -} diff --git a/arch/v850/kernel/anna.c b/arch/v850/kernel/anna.c deleted file mode 100644 index 5978a25170fb..000000000000 --- a/arch/v850/kernel/anna.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * arch/v850/kernel/anna.c -- Anna V850E2 evaluation chip/board - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mach.h" - - -/* SRAM and SDRAM are vaguely contiguous (with a big hole in between; see - mach_reserve_bootmem for details); use both as one big area. */ -#define RAM_START SRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - -/* The bits of this port are connected to an 8-LED bar-graph. */ -#define LEDS_PORT 0 - - -static void anna_led_tick (void); - - -void __init mach_early_init (void) -{ - ANNA_ILBEN = 0; - - V850E2_CSC(0) = 0x402F; - V850E2_CSC(1) = 0x4000; - V850E2_BPC = 0; - V850E2_BSC = 0xAAAA; - V850E2_BEC = 0; - -#if 0 - V850E2_BHC = 0xFFFF; /* icache all memory, dcache all */ -#else - V850E2_BHC = 0; /* cache no memory */ -#endif - V850E2_BCT(0) = 0xB088; - V850E2_BCT(1) = 0x0008; - V850E2_DWC(0) = 0x0027; - V850E2_DWC(1) = 0; - V850E2_BCC = 0x0006; - V850E2_ASC = 0; - V850E2_LBS = 0x0089; - V850E2_SCR(3) = 0x21A9; - V850E2_RFS(3) = 0x8121; - - v850e_intc_disable_irqs (); -} - -void __init mach_setup (char **cmdline) -{ - ANNA_PORT_PM (LEDS_PORT) = 0; /* Make all LED pins output pins. */ - mach_tick = anna_led_tick; -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void __init mach_reserve_bootmem () -{ - /* The space between SRAM and SDRAM is filled with duplicate - images of SRAM. Prevent the kernel from using them. */ - reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), - BOOTMEM_DEFAULT); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "PIN", IRQ_INTP(0), IRQ_INTP_NUM, 1, 4 }, - { "CCC", IRQ_INTCCC(0), IRQ_INTCCC_NUM, 1, 5 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "DMA", IRQ_INTDMA(0), IRQ_INTDMA_NUM, 1, 2 }, - { "DMXER", IRQ_INTDMXER,1, 1, 2 }, - { "SRE", IRQ_INTSRE(0), IRQ_INTSRE_NUM, 3, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 3, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 3, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -void machine_restart (char *__unused) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - asm ("jmp r0"); /* Jump to the reset vector. */ -} - -void machine_halt (void) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - local_irq_disable (); /* Ignore all interrupts. */ - ANNA_PORT_IO(LEDS_PORT) = 0xAA; /* Note that we halted. */ - for (;;) - asm ("halt; nop; nop; nop; nop; nop"); -} - -void machine_power_off (void) -{ - machine_halt (); -} - -/* Called before configuring an on-chip UART. */ -void anna_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* The Anna connects some general-purpose I/O pins on the CPU to - the RTS/CTS lines of UART 1's serial connection. I/O pins P07 - and P37 are RTS and CTS respectively. */ - if (chan == 1) { - ANNA_PORT_PM(0) &= ~0x80; /* P07 in output mode */ - ANNA_PORT_PM(3) |= 0x80; /* P37 in input mode */ - } -} - -/* Minimum and maximum bounds for the moving upper LED boundary in the - clock tick display. We can't use the last bit because it's used for - UART0's CTS output. */ -#define MIN_MAX_POS 0 -#define MAX_MAX_POS 6 - -/* There are MAX_MAX_POS^2 - MIN_MAX_POS^2 cycles in the animation, so if - we pick 6 and 0 as above, we get 49 cycles, which is when divided into - the standard 100 value for HZ, gives us an almost 1s total time. */ -#define TICKS_PER_FRAME \ - (HZ / (MAX_MAX_POS * MAX_MAX_POS - MIN_MAX_POS * MIN_MAX_POS)) - -static void anna_led_tick () -{ - static unsigned counter = 0; - - if (++counter == TICKS_PER_FRAME) { - static int pos = 0, max_pos = MAX_MAX_POS, dir = 1; - - if (dir > 0 && pos == max_pos) { - dir = -1; - if (max_pos == MIN_MAX_POS) - max_pos = MAX_MAX_POS; - else - max_pos--; - } else { - if (dir < 0 && pos == 0) - dir = 1; - - if (pos + dir <= max_pos) { - /* Each bit of port 0 has a LED. */ - clear_bit (pos, &ANNA_PORT_IO(LEDS_PORT)); - pos += dir; - set_bit (pos, &ANNA_PORT_IO(LEDS_PORT)); - } - } - - counter = 0; - } -} diff --git a/arch/v850/kernel/anna.ld b/arch/v850/kernel/anna.ld deleted file mode 100644 index df7f80f2833d..000000000000 --- a/arch/v850/kernel/anna.ld +++ /dev/null @@ -1,20 +0,0 @@ -/* Linker script for the Midas labs Anna V850E2 evaluation board - (CONFIG_V850E2_ANNA). */ - -MEMORY { - /* 256KB of internal memory (followed by one mirror). */ - iMEM0 : ORIGIN = 0, LENGTH = 0x00040000 - /* 256KB of internal memory (followed by one mirror). */ - iMEM1 : ORIGIN = 0x00040000, LENGTH = 0x00040000 - - /* 1MB of static RAM. This memory is mirrored 64 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 64MB of DRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - .intv : { INTV_CONTENTS } > iMEM0 - .sram : { RAMK_KRAM_CONTENTS } > SRAM - .root : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/as85ep1-rom.ld b/arch/v850/kernel/as85ep1-rom.ld deleted file mode 100644 index fe2a9a3ab525..000000000000 --- a/arch/v850/kernel/as85ep1-rom.ld +++ /dev/null @@ -1,21 +0,0 @@ -/* Linker script for the NEC AS85EP1 V850E evaluation board - (CONFIG_V850E_AS85EP1), with kernel in ROM (CONFIG_ROM_KERNEL). */ - -MEMORY { - /* 4MB of flash ROM. */ - ROM : ORIGIN = 0, LENGTH = 0x00400000 - - /* 1MB of static RAM. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - - /* About 58MB of DRAM. This can actually be at one of two - positions, determined by jumper JP3; we have to use the first - position because the second is partially out of processor - instruction addressing range (though in the second position - there's actually 64MB available). */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - ROMK_SECTIONS(ROM, SRAM) -} diff --git a/arch/v850/kernel/as85ep1.c b/arch/v850/kernel/as85ep1.c deleted file mode 100644 index b525ecf3aea4..000000000000 --- a/arch/v850/kernel/as85ep1.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * arch/v850/kernel/as85ep1.c -- AS85EP1 V850E evaluation chip/board - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mach.h" - - -/* SRAM and SDRAM are vaguely contiguous (with a big hole in between; see - mach_reserve_bootmem for details); use both as one big area. */ -#define RAM_START SRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - -/* The bits of this port are connected to an 8-LED bar-graph. */ -#define LEDS_PORT 4 - - -static void as85ep1_led_tick (void); - -extern char _intv_copy_src_start, _intv_copy_src_end; -extern char _intv_copy_dst_start; - - -void __init mach_early_init (void) -{ -#ifndef CONFIG_ROM_KERNEL - const u32 *src; - register u32 *dst asm ("ep"); -#endif - - AS85EP1_CSC(0) = 0x0403; - AS85EP1_BCT(0) = 0xB8B8; - AS85EP1_DWC(0) = 0x0104; - AS85EP1_BCC = 0x0012; - AS85EP1_ASC = 0; - AS85EP1_LBS = 0x00A9; - - AS85EP1_PORT_PMC(6) = 0xFF; /* valid A0,A1,A20-A25 */ - AS85EP1_PORT_PMC(7) = 0x0E; /* valid CS1-CS3 */ - AS85EP1_PORT_PMC(9) = 0xFF; /* valid D16-D23 */ - AS85EP1_PORT_PMC(10) = 0xFF; /* valid D24-D31 */ - - AS85EP1_RFS(1) = 0x800c; - AS85EP1_RFS(3) = 0x800c; - AS85EP1_SCR(1) = 0x20A9; - AS85EP1_SCR(3) = 0x20A9; - -#ifndef CONFIG_ROM_KERNEL - /* The early chip we have is buggy, and writing the interrupt - vectors into low RAM may screw up, so for non-ROM kernels, we - only rely on the reset vector being downloaded, and copy the - rest of the interrupt vectors into place here. The specific bug - is that writing address N, where (N & 0x10) == 0x10, will _also_ - write to address (N - 0x10). We avoid this (effectively) by - writing in 16-byte chunks backwards from the end. */ - - AS85EP1_IRAMM = 0x3; /* "write-mode" for the internal instruction memory */ - - src = (u32 *)(((u32)&_intv_copy_src_end - 1) & ~0xF); - dst = (u32 *)&_intv_copy_dst_start - + (src - (u32 *)&_intv_copy_src_start); - do { - u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3]; - dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3; - dst -= 4; - src -= 4; - } while (src > (u32 *)&_intv_copy_src_start); - - AS85EP1_IRAMM = 0x0; /* "read-mode" for the internal instruction memory */ -#endif /* !CONFIG_ROM_KERNEL */ - - v850e_intc_disable_irqs (); -} - -void __init mach_setup (char **cmdline) -{ - AS85EP1_PORT_PMC (LEDS_PORT) = 0; /* Make the LEDs port an I/O port. */ - AS85EP1_PORT_PM (LEDS_PORT) = 0; /* Make all the bits output pins. */ - mach_tick = as85ep1_led_tick; -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -/* Convenience macros. */ -#define SRAM_END (SRAM_ADDR + SRAM_SIZE) -#define SDRAM_END (SDRAM_ADDR + SDRAM_SIZE) - -void __init mach_reserve_bootmem () -{ - if (SDRAM_ADDR < RAM_END && SDRAM_ADDR > RAM_START) - /* We can't use the space between SRAM and SDRAM, so - prevent the kernel from trying. */ - reserve_bootmem(SRAM_END, SDRAM_ADDR - SRAM_END, - BOOTMEM_DEFAULT); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "CCC", IRQ_INTCCC(0), IRQ_INTCCC_NUM, 1, 5 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "SRE", IRQ_INTSRE(0), IRQ_INTSRE_NUM, 3, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 3, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 3, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -void machine_restart (char *__unused) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - asm ("jmp r0"); /* Jump to the reset vector. */ -} - -void machine_halt (void) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - local_irq_disable (); /* Ignore all interrupts. */ - AS85EP1_PORT_IO (LEDS_PORT) = 0xAA; /* Note that we halted. */ - for (;;) - asm ("halt; nop; nop; nop; nop; nop"); -} - -void machine_power_off (void) -{ - machine_halt (); -} - -/* Called before configuring an on-chip UART. */ -void as85ep1_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* Make the shared uart/port pins be uart pins. */ - AS85EP1_PORT_PMC(3) |= (0x5 << chan); - - /* The AS85EP1 connects some general-purpose I/O pins on the CPU to - the RTS/CTS lines of UART 1's serial connection. I/O pins P53 - and P54 are RTS and CTS respectively. */ - if (chan == 1) { - /* Put P53 & P54 in I/O port mode. */ - AS85EP1_PORT_PMC(5) &= ~0x18; - /* Make P53 an output, and P54 an input. */ - AS85EP1_PORT_PM(5) |= 0x10; - } -} - -/* Minimum and maximum bounds for the moving upper LED boundary in the - clock tick display. */ -#define MIN_MAX_POS 0 -#define MAX_MAX_POS 7 - -/* There are MAX_MAX_POS^2 - MIN_MAX_POS^2 cycles in the animation, so if - we pick 6 and 0 as above, we get 49 cycles, which is when divided into - the standard 100 value for HZ, gives us an almost 1s total time. */ -#define TICKS_PER_FRAME \ - (HZ / (MAX_MAX_POS * MAX_MAX_POS - MIN_MAX_POS * MIN_MAX_POS)) - -static void as85ep1_led_tick () -{ - static unsigned counter = 0; - - if (++counter == TICKS_PER_FRAME) { - static int pos = 0, max_pos = MAX_MAX_POS, dir = 1; - - if (dir > 0 && pos == max_pos) { - dir = -1; - if (max_pos == MIN_MAX_POS) - max_pos = MAX_MAX_POS; - else - max_pos--; - } else { - if (dir < 0 && pos == 0) - dir = 1; - - if (pos + dir <= max_pos) { - /* Each bit of port 0 has a LED. */ - set_bit (pos, &AS85EP1_PORT_IO(LEDS_PORT)); - pos += dir; - clear_bit (pos, &AS85EP1_PORT_IO(LEDS_PORT)); - } - } - - counter = 0; - } -} diff --git a/arch/v850/kernel/as85ep1.ld b/arch/v850/kernel/as85ep1.ld deleted file mode 100644 index ef2c4399063e..000000000000 --- a/arch/v850/kernel/as85ep1.ld +++ /dev/null @@ -1,49 +0,0 @@ -/* Linker script for the NEC AS85EP1 V850E evaluation board - (CONFIG_V850E_AS85EP1). */ - -MEMORY { - /* 1MB of internal instruction memory. */ - iMEM0 : ORIGIN = 0, LENGTH = 0x00100000 - - /* 1MB of static RAM. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - - /* About 58MB of DRAM. This can actually be at one of two - positions, determined by jump JP3; we have to use the first - position because the second is partially out of processor - instruction addressing range (though in the second position - there's actually 64MB available). */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - .resetv : { - __intv_start = . ; - *(.intv.reset) /* Reset vector */ - } > iMEM0 - - .sram : { - RAMK_KRAM_CONTENTS - - /* We stick most of the interrupt vectors here; they'll be - copied into the proper location by the early init code (we - can't put them directly in the right place because of - hardware bugs). The vectors shouldn't need to be - relocated, so we don't have to use `> ... AT> ...' to - split the load/vm addresses (and we can't because of - problems with the loader). */ - . = ALIGN (0x10) ; - __intv_copy_src_start = . ; - *(.intv.common) /* Vectors common to all v850e proc. */ - *(.intv.mach) /* Machine-specific int. vectors. */ - . = ALIGN (0x10) ; - __intv_copy_src_end = . ; - } > SRAM - - /* Where we end up putting the vectors. */ - __intv_copy_dst_start = 0x10 ; - __intv_copy_dst_end = __intv_copy_dst_start + (__intv_copy_src_end - __intv_copy_src_start) ; - __intv_end = __intv_copy_dst_end ; - - .root : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/asm-offsets.c b/arch/v850/kernel/asm-offsets.c deleted file mode 100644 index 581e6986a776..000000000000 --- a/arch/v850/kernel/asm-offsets.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This program is used to generate definitions needed by - * assembly language modules. - * - * We use the technique used in the OSF Mach kernel code: - * generate asm statements containing #defines, - * compile this file to assembler, and then extract the - * #defines from the assembly-language output. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -int main (void) -{ - /* offsets into the task struct */ - DEFINE (TASK_STATE, offsetof (struct task_struct, state)); - DEFINE (TASK_FLAGS, offsetof (struct task_struct, flags)); - DEFINE (TASK_PTRACE, offsetof (struct task_struct, ptrace)); - DEFINE (TASK_BLOCKED, offsetof (struct task_struct, blocked)); - DEFINE (TASK_THREAD, offsetof (struct task_struct, thread)); - DEFINE (TASK_THREAD_INFO, offsetof (struct task_struct, stack)); - DEFINE (TASK_MM, offsetof (struct task_struct, mm)); - DEFINE (TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm)); - DEFINE (TASK_PID, offsetof (struct task_struct, pid)); - - /* offsets into the kernel_stat struct */ - DEFINE (STAT_IRQ, offsetof (struct kernel_stat, irqs)); - - - /* signal defines */ - DEFINE (SIGSEGV, SIGSEGV); - DEFINE (SEGV_MAPERR, SEGV_MAPERR); - DEFINE (SIGTRAP, SIGTRAP); - DEFINE (SIGCHLD, SIGCHLD); - DEFINE (SIGILL, SIGILL); - DEFINE (TRAP_TRACE, TRAP_TRACE); - - /* ptrace flag bits */ - DEFINE (PT_PTRACED, PT_PTRACED); - DEFINE (PT_DTRACE, PT_DTRACE); - - /* error values */ - DEFINE (ENOSYS, ENOSYS); - - /* clone flag bits */ - DEFINE (CLONE_VFORK, CLONE_VFORK); - DEFINE (CLONE_VM, CLONE_VM); - - return 0; -} diff --git a/arch/v850/kernel/bug.c b/arch/v850/kernel/bug.c deleted file mode 100644 index c78cf750915a..000000000000 --- a/arch/v850/kernel/bug.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * arch/v850/kernel/bug.c -- Bug reporting functions - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -/* We should use __builtin_return_address, but it doesn't work in gcc-2.90 - (which is currently our standard compiler on the v850). */ -#define ret_addr() ({ register u32 lp asm ("lp"); lp; }) -#define stack_addr() ({ register u32 sp asm ("sp"); sp; }) - -void __bug () -{ - printk (KERN_CRIT "kernel BUG at PC 0x%x (SP ~0x%x)!\n", - ret_addr() - 4, /* - 4 for `jarl' */ - stack_addr()); - machine_halt (); -} - -int bad_trap (int trap_num, struct pt_regs *regs) -{ - printk (KERN_CRIT - "unimplemented trap %d called at 0x%08lx, pid %d!\n", - trap_num, regs->pc, current->pid); - return -ENOSYS; -} - -#ifdef CONFIG_RESET_GUARD -void unexpected_reset (unsigned long ret_addr, unsigned long kmode, - struct task_struct *task, unsigned long sp) -{ - printk (KERN_CRIT - "unexpected reset in %s mode, pid %d" - " (ret_addr = 0x%lx, sp = 0x%lx)\n", - kmode ? "kernel" : "user", - task ? task->pid : -1, - ret_addr, sp); - - machine_halt (); -} -#endif /* CONFIG_RESET_GUARD */ - - - -struct spec_reg_name { - const char *name; - int gpr; -}; - -struct spec_reg_name spec_reg_names[] = { - { "sp", GPR_SP }, - { "gp", GPR_GP }, - { "tp", GPR_TP }, - { "ep", GPR_EP }, - { "lp", GPR_LP }, - { 0, 0 } -}; - -void show_regs (struct pt_regs *regs) -{ - int gpr_base, gpr_offs; - - printk (" pc 0x%08lx psw 0x%08lx kernel_mode %d\n", - regs->pc, regs->psw, regs->kernel_mode); - printk (" ctpc 0x%08lx ctpsw 0x%08lx ctbp 0x%08lx\n", - regs->ctpc, regs->ctpsw, regs->ctbp); - - for (gpr_base = 0; gpr_base < NUM_GPRS; gpr_base += 4) { - for (gpr_offs = 0; gpr_offs < 4; gpr_offs++) { - int gpr = gpr_base + gpr_offs; - long val = regs->gpr[gpr]; - struct spec_reg_name *srn; - - for (srn = spec_reg_names; srn->name; srn++) - if (srn->gpr == gpr) - break; - - if (srn->name) - printk ("%7s 0x%08lx", srn->name, val); - else - printk (" r%02d 0x%08lx", gpr, val); - } - - printk ("\n"); - } -} - -/* - * TASK is a pointer to the task whose backtrace we want to see (or NULL - * for current task), SP is the stack pointer of the first frame that - * should be shown in the back trace (or NULL if the entire call-chain of - * the task should be shown). - */ -void show_stack (struct task_struct *task, unsigned long *sp) -{ - unsigned long addr, end; - - if (sp) - addr = (unsigned long)sp; - else if (task) - addr = task_sp (task); - else - addr = stack_addr (); - - addr = addr & ~3; - end = (addr + THREAD_SIZE - 1) & THREAD_MASK; - - while (addr < end) { - printk ("%8lX: ", addr); - while (addr < end) { - printk (" %8lX", *(unsigned long *)addr); - addr += sizeof (unsigned long); - if (! (addr & 0xF)) - break; - } - printk ("\n"); - } -} - -void dump_stack () -{ - show_stack (0, 0); -} - -EXPORT_SYMBOL(dump_stack); diff --git a/arch/v850/kernel/entry.S b/arch/v850/kernel/entry.S deleted file mode 100644 index e4327a8d6bcd..000000000000 --- a/arch/v850/kernel/entry.S +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * arch/v850/kernel/entry.S -- Low-level system-call handling, trap handlers, - * and context-switching - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - - -/* Make a slightly more convenient alias for C_SYMBOL_NAME. */ -#define CSYM C_SYMBOL_NAME - - -/* The offset of the struct pt_regs in a state-save-frame on the stack. */ -#define PTO STATE_SAVE_PT_OFFSET - - -/* Save argument registers to the state-save-frame pointed to by EP. */ -#define SAVE_ARG_REGS \ - sst.w r6, PTO+PT_GPR(6)[ep]; \ - sst.w r7, PTO+PT_GPR(7)[ep]; \ - sst.w r8, PTO+PT_GPR(8)[ep]; \ - sst.w r9, PTO+PT_GPR(9)[ep] -/* Restore argument registers from the state-save-frame pointed to by EP. */ -#define RESTORE_ARG_REGS \ - sld.w PTO+PT_GPR(6)[ep], r6; \ - sld.w PTO+PT_GPR(7)[ep], r7; \ - sld.w PTO+PT_GPR(8)[ep], r8; \ - sld.w PTO+PT_GPR(9)[ep], r9 - -/* Save value return registers to the state-save-frame pointed to by EP. */ -#define SAVE_RVAL_REGS \ - sst.w r10, PTO+PT_GPR(10)[ep]; \ - sst.w r11, PTO+PT_GPR(11)[ep] -/* Restore value return registers from the state-save-frame pointed to by EP. */ -#define RESTORE_RVAL_REGS \ - sld.w PTO+PT_GPR(10)[ep], r10; \ - sld.w PTO+PT_GPR(11)[ep], r11 - - -#define SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS \ - sst.w r1, PTO+PT_GPR(1)[ep]; \ - sst.w r5, PTO+PT_GPR(5)[ep] -#define SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL \ - sst.w r12, PTO+PT_GPR(12)[ep]; \ - sst.w r13, PTO+PT_GPR(13)[ep]; \ - sst.w r14, PTO+PT_GPR(14)[ep]; \ - sst.w r15, PTO+PT_GPR(15)[ep]; \ - sst.w r16, PTO+PT_GPR(16)[ep]; \ - sst.w r17, PTO+PT_GPR(17)[ep]; \ - sst.w r18, PTO+PT_GPR(18)[ep]; \ - sst.w r19, PTO+PT_GPR(19)[ep] -#define RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS \ - sld.w PTO+PT_GPR(1)[ep], r1; \ - sld.w PTO+PT_GPR(5)[ep], r5 -#define RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL \ - sld.w PTO+PT_GPR(12)[ep], r12; \ - sld.w PTO+PT_GPR(13)[ep], r13; \ - sld.w PTO+PT_GPR(14)[ep], r14; \ - sld.w PTO+PT_GPR(15)[ep], r15; \ - sld.w PTO+PT_GPR(16)[ep], r16; \ - sld.w PTO+PT_GPR(17)[ep], r17; \ - sld.w PTO+PT_GPR(18)[ep], r18; \ - sld.w PTO+PT_GPR(19)[ep], r19 - -/* Save `call clobbered' registers to the state-save-frame pointed to by EP. */ -#define SAVE_CALL_CLOBBERED_REGS \ - SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - SAVE_ARG_REGS; \ - SAVE_RVAL_REGS; \ - SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL -/* Restore `call clobbered' registers from the state-save-frame pointed to - by EP. */ -#define RESTORE_CALL_CLOBBERED_REGS \ - RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - RESTORE_ARG_REGS; \ - RESTORE_RVAL_REGS; \ - RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL - -/* Save `call clobbered' registers except for the return-value registers - to the state-save-frame pointed to by EP. */ -#define SAVE_CALL_CLOBBERED_REGS_NO_RVAL \ - SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - SAVE_ARG_REGS; \ - SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL -/* Restore `call clobbered' registers except for the return-value registers - from the state-save-frame pointed to by EP. */ -#define RESTORE_CALL_CLOBBERED_REGS_NO_RVAL \ - RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - RESTORE_ARG_REGS; \ - RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL - -/* Save `call saved' registers to the state-save-frame pointed to by EP. */ -#define SAVE_CALL_SAVED_REGS \ - sst.w r2, PTO+PT_GPR(2)[ep]; \ - sst.w r20, PTO+PT_GPR(20)[ep]; \ - sst.w r21, PTO+PT_GPR(21)[ep]; \ - sst.w r22, PTO+PT_GPR(22)[ep]; \ - sst.w r23, PTO+PT_GPR(23)[ep]; \ - sst.w r24, PTO+PT_GPR(24)[ep]; \ - sst.w r25, PTO+PT_GPR(25)[ep]; \ - sst.w r26, PTO+PT_GPR(26)[ep]; \ - sst.w r27, PTO+PT_GPR(27)[ep]; \ - sst.w r28, PTO+PT_GPR(28)[ep]; \ - sst.w r29, PTO+PT_GPR(29)[ep] -/* Restore `call saved' registers from the state-save-frame pointed to by EP. */ -#define RESTORE_CALL_SAVED_REGS \ - sld.w PTO+PT_GPR(2)[ep], r2; \ - sld.w PTO+PT_GPR(20)[ep], r20; \ - sld.w PTO+PT_GPR(21)[ep], r21; \ - sld.w PTO+PT_GPR(22)[ep], r22; \ - sld.w PTO+PT_GPR(23)[ep], r23; \ - sld.w PTO+PT_GPR(24)[ep], r24; \ - sld.w PTO+PT_GPR(25)[ep], r25; \ - sld.w PTO+PT_GPR(26)[ep], r26; \ - sld.w PTO+PT_GPR(27)[ep], r27; \ - sld.w PTO+PT_GPR(28)[ep], r28; \ - sld.w PTO+PT_GPR(29)[ep], r29 - - -/* Save the PC stored in the special register SAVEREG to the state-save-frame - pointed to by EP. r19 is clobbered. */ -#define SAVE_PC(savereg) \ - stsr SR_ ## savereg, r19; \ - sst.w r19, PTO+PT_PC[ep] -/* Restore the PC from the state-save-frame pointed to by EP, to the special - register SAVEREG. LP is clobbered (it is used as a scratch register - because the POP_STATE macro restores it, and this macro is usually used - inside POP_STATE). */ -#define RESTORE_PC(savereg) \ - sld.w PTO+PT_PC[ep], lp; \ - ldsr lp, SR_ ## savereg -/* Save the PSW register stored in the special register SAVREG to the - state-save-frame pointed to by EP. r19 is clobbered. */ -#define SAVE_PSW(savereg) \ - stsr SR_ ## savereg, r19; \ - sst.w r19, PTO+PT_PSW[ep] -/* Restore the PSW register from the state-save-frame pointed to by EP, to - the special register SAVEREG. LP is clobbered (it is used as a scratch - register because the POP_STATE macro restores it, and this macro is - usually used inside POP_STATE). */ -#define RESTORE_PSW(savereg) \ - sld.w PTO+PT_PSW[ep], lp; \ - ldsr lp, SR_ ## savereg - -/* Save CTPC/CTPSW/CTBP registers to the state-save-frame pointed to by REG. - r19 is clobbered. */ -#define SAVE_CT_REGS \ - stsr SR_CTPC, r19; \ - sst.w r19, PTO+PT_CTPC[ep]; \ - stsr SR_CTPSW, r19; \ - sst.w r19, PTO+PT_CTPSW[ep]; \ - stsr SR_CTBP, r19; \ - sst.w r19, PTO+PT_CTBP[ep] -/* Restore CTPC/CTPSW/CTBP registers from the state-save-frame pointed to by EP. - LP is clobbered (it is used as a scratch register because the POP_STATE - macro restores it, and this macro is usually used inside POP_STATE). */ -#define RESTORE_CT_REGS \ - sld.w PTO+PT_CTPC[ep], lp; \ - ldsr lp, SR_CTPC; \ - sld.w PTO+PT_CTPSW[ep], lp; \ - ldsr lp, SR_CTPSW; \ - sld.w PTO+PT_CTBP[ep], lp; \ - ldsr lp, SR_CTBP - - -/* Push register state, except for the stack pointer, on the stack in the - form of a state-save-frame (plus some extra padding), in preparation for - a system call. This macro makes sure that the EP, GP, and LP - registers are saved, and TYPE identifies the set of extra registers to - be saved as well. Also copies (the new value of) SP to EP. */ -#define PUSH_STATE(type) \ - addi -STATE_SAVE_SIZE, sp, sp; /* Make room on the stack. */ \ - st.w ep, PTO+PT_GPR(GPR_EP)[sp]; \ - mov sp, ep; \ - sst.w gp, PTO+PT_GPR(GPR_GP)[ep]; \ - sst.w lp, PTO+PT_GPR(GPR_LP)[ep]; \ - type ## _STATE_SAVER -/* Pop a register state pushed by PUSH_STATE, except for the stack pointer, - from the stack. */ -#define POP_STATE(type) \ - mov sp, ep; \ - type ## _STATE_RESTORER; \ - sld.w PTO+PT_GPR(GPR_GP)[ep], gp; \ - sld.w PTO+PT_GPR(GPR_LP)[ep], lp; \ - sld.w PTO+PT_GPR(GPR_EP)[ep], ep; \ - addi STATE_SAVE_SIZE, sp, sp /* Clean up our stack space. */ - - -/* Switch to the kernel stack if necessary, and push register state on the - stack in the form of a state-save-frame. Also load the current task - pointer if switching from user mode. The stack-pointer (r3) should have - already been saved to the memory location SP_SAVE_LOC (the reason for - this is that the interrupt vectors may be beyond a 22-bit signed offset - jump from the actual interrupt handler, and this allows them to save the - stack-pointer and use that register to do an indirect jump). This macro - makes sure that `special' registers, system registers, and the stack - pointer are saved; TYPE identifies the set of extra registers to be - saved as well. SYSCALL_NUM is the register in which the system-call - number this state is for is stored (r0 if this isn't a system call). - Interrupts should already be disabled when calling this. */ -#define SAVE_STATE(type, syscall_num, sp_save_loc) \ - tst1 0, KM; /* See if already in kernel mode. */ \ - bz 1f; \ - ld.w sp_save_loc, sp; /* ... yes, use saved SP. */ \ - br 2f; \ -1: ld.w KSP, sp; /* ... no, switch to kernel stack. */ \ -2: PUSH_STATE(type); \ - ld.b KM, r19; /* Remember old kernel-mode. */ \ - sst.w r19, PTO+PT_KERNEL_MODE[ep]; \ - ld.w sp_save_loc, r19; /* Remember old SP. */ \ - sst.w r19, PTO+PT_GPR(GPR_SP)[ep]; \ - mov 1, r19; /* Now definitely in kernel-mode. */ \ - st.b r19, KM; \ - GET_CURRENT_TASK(CURRENT_TASK); /* Fetch the current task pointer. */ \ - /* Save away the syscall number. */ \ - sst.w syscall_num, PTO+PT_CUR_SYSCALL[ep] - - -/* Save register state not normally saved by PUSH_STATE for TYPE, to the - state-save-frame on the stack; also copies SP to EP. r19 may be trashed. */ -#define SAVE_EXTRA_STATE(type) \ - mov sp, ep; \ - type ## _EXTRA_STATE_SAVER -/* Restore register state not normally restored by POP_STATE for TYPE, - from the state-save-frame on the stack; also copies SP to EP. - r19 may be trashed. */ -#define RESTORE_EXTRA_STATE(type) \ - mov sp, ep; \ - type ## _EXTRA_STATE_RESTORER - -/* Save any call-clobbered registers not normally saved by PUSH_STATE for - TYPE, to the state-save-frame on the stack. - EP may be trashed, but is not guaranteed to contain a copy of SP - (unlike after most SAVE_... macros). r19 may be trashed. */ -#define SAVE_EXTRA_STATE_FOR_SCHEDULE(type) \ - type ## _SCHEDULE_EXTRA_STATE_SAVER -/* Restore any call-clobbered registers not normally restored by - POP_STATE for TYPE, to the state-save-frame on the stack. - EP may be trashed, but is not guaranteed to contain a copy of SP - (unlike after most RESTORE_... macros). r19 may be trashed. */ -#define RESTORE_EXTRA_STATE_FOR_SCHEDULE(type) \ - type ## _SCHEDULE_EXTRA_STATE_RESTORER - - -/* These are extra_state_saver/restorer values for a user trap. Note - that we save the argument registers so that restarted syscalls will - function properly (otherwise it wouldn't be necessary), and we must - _not_ restore the return-value registers (so that traps can return a - value!), but call-clobbered registers are not saved at all, as the - caller of the syscall function should have saved them. */ - -#define TRAP_RET reti -/* Traps don't save call-clobbered registers (but do still save arg regs). - We preserve PSw to keep long-term state, namely interrupt status (for traps - from kernel-mode), and the single-step flag (for user traps). */ -#define TRAP_STATE_SAVER \ - SAVE_ARG_REGS; \ - SAVE_PC(EIPC); \ - SAVE_PSW(EIPSW) -/* When traps return, they just leave call-clobbered registers (except for arg - regs) with whatever value they have from the kernel. Traps don't preserve - the PSW, but we zero EIPSW to ensure it doesn't contain anything dangerous - (in particular, the single-step flag). */ -#define TRAP_STATE_RESTORER \ - RESTORE_ARG_REGS; \ - RESTORE_PC(EIPC); \ - RESTORE_PSW(EIPSW) -/* Save registers not normally saved by traps. We need to save r12, even - though it's nominally call-clobbered, because it's used when restarting - a system call (the signal-handling path uses SAVE_EXTRA_STATE, and - expects r12 to be restored when the trap returns). */ -#define TRAP_EXTRA_STATE_SAVER \ - SAVE_RVAL_REGS; \ - sst.w r12, PTO+PT_GPR(12)[ep]; \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define TRAP_EXTRA_STATE_RESTORER \ - RESTORE_RVAL_REGS; \ - sld.w PTO+PT_GPR(12)[ep], r12; \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -/* Save registers prior to calling scheduler (just before trap returns). - We have to save the return-value registers to preserve the trap's return - value. Note that ..._SCHEDULE_EXTRA_STATE_SAVER, unlike most ..._SAVER - macros, is required to setup EP itself if EP is needed (this is because - in many cases, the macro is empty). */ -#define TRAP_SCHEDULE_EXTRA_STATE_SAVER \ - mov sp, ep; \ - SAVE_RVAL_REGS -/* Note that ..._SCHEDULE_EXTRA_STATE_RESTORER, unlike most ..._RESTORER - macros, is required to setup EP itself if EP is needed (this is because - in many cases, the macro is empty). */ -#define TRAP_SCHEDULE_EXTRA_STATE_RESTORER \ - mov sp, ep; \ - RESTORE_RVAL_REGS - -/* Register saving/restoring for maskable interrupts. */ -#define IRQ_RET reti -#define IRQ_STATE_SAVER \ - SAVE_CALL_CLOBBERED_REGS; \ - SAVE_PC(EIPC); \ - SAVE_PSW(EIPSW) -#define IRQ_STATE_RESTORER \ - RESTORE_CALL_CLOBBERED_REGS; \ - RESTORE_PC(EIPC); \ - RESTORE_PSW(EIPSW) -#define IRQ_EXTRA_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define IRQ_EXTRA_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -#define IRQ_SCHEDULE_EXTRA_STATE_SAVER /* nothing */ -#define IRQ_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */ - -/* Register saving/restoring for non-maskable interrupts. */ -#define NMI_RET reti -#define NMI_STATE_SAVER \ - SAVE_CALL_CLOBBERED_REGS; \ - SAVE_PC(FEPC); \ - SAVE_PSW(FEPSW); -#define NMI_STATE_RESTORER \ - RESTORE_CALL_CLOBBERED_REGS; \ - RESTORE_PC(FEPC); \ - RESTORE_PSW(FEPSW); -#define NMI_EXTRA_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define NMI_EXTRA_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -#define NMI_SCHEDULE_EXTRA_STATE_SAVER /* nothing */ -#define NMI_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */ - -/* Register saving/restoring for debug traps. */ -#define DBTRAP_RET .long 0x014607E0 /* `dbret', but gas doesn't support it. */ -#define DBTRAP_STATE_SAVER \ - SAVE_CALL_CLOBBERED_REGS; \ - SAVE_PC(DBPC); \ - SAVE_PSW(DBPSW) -#define DBTRAP_STATE_RESTORER \ - RESTORE_CALL_CLOBBERED_REGS; \ - RESTORE_PC(DBPC); \ - RESTORE_PSW(DBPSW) -#define DBTRAP_EXTRA_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define DBTRAP_EXTRA_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -#define DBTRAP_SCHEDULE_EXTRA_STATE_SAVER /* nothing */ -#define DBTRAP_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */ - -/* Register saving/restoring for a context switch. We don't need to save - too many registers, because context-switching looks like a function call - (via the function `switch_thread'), so callers will save any - call-clobbered registers themselves. We do need to save the CT regs, as - they're normally not saved during kernel entry (the kernel doesn't use - them). We save PSW so that interrupt-status state will correctly follow - each thread (mostly NMI vs. normal-IRQ/trap), though for the most part - it doesn't matter since threads are always in almost exactly the same - processor state during a context switch. The stack pointer and return - value are handled by switch_thread itself. */ -#define SWITCH_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_PSW(PSW); \ - SAVE_CT_REGS -#define SWITCH_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_PSW(PSW); \ - RESTORE_CT_REGS - - -/* Restore register state from the state-save-frame on the stack, switch back - to the user stack if necessary, and return from the trap/interrupt. - EXTRA_STATE_RESTORER is a sequence of assembly language statements to - restore anything not restored by this macro. Only registers not saved by - the C compiler are restored (that is, R3(sp), R4(gp), R31(lp), and - anything restored by EXTRA_STATE_RESTORER). */ -#define RETURN(type) \ - ld.b PTO+PT_KERNEL_MODE[sp], r19; \ - di; /* Disable interrupts */ \ - cmp r19, r0; /* See if returning to kernel mode, */\ - bne 2f; /* ... if so, skip resched &c. */ \ - \ - /* We're returning to user mode, so check for various conditions that \ - trigger rescheduling. */ \ - GET_CURRENT_THREAD(r18); \ - ld.w TI_FLAGS[r18], r19; \ - andi _TIF_NEED_RESCHED, r19, r0; \ - bnz 3f; /* Call the scheduler. */ \ -5: andi _TIF_SIGPENDING, r19, r18; \ - ld.w TASK_PTRACE[CURRENT_TASK], r19; /* ptrace flags */ \ - or r18, r19; /* see if either is non-zero */ \ - bnz 4f; /* if so, handle them */ \ - \ -/* Return to user state. */ \ -1: st.b r0, KM; /* Now officially in user state. */ \ - \ -/* Final return. The stack-pointer fiddling is not needed when returning \ - to kernel-mode, but they don't hurt, and this way we can share the \ - (sometimes rather lengthy) POP_STATE macro. */ \ -2: POP_STATE(type); \ - st.w sp, KSP; /* Save the kernel stack pointer. */ \ - ld.w PT_GPR(GPR_SP)-PT_SIZE[sp], sp; /* Restore stack pointer. */ \ - type ## _RET; /* Return from the trap/interrupt. */ \ - \ -/* Call the scheduler before returning from a syscall/trap. */ \ -3: SAVE_EXTRA_STATE_FOR_SCHEDULE(type); /* Prepare to call scheduler. */ \ - jarl call_scheduler, lp; /* Call scheduler */ \ - di; /* The scheduler enables interrupts */\ - RESTORE_EXTRA_STATE_FOR_SCHEDULE(type); \ - GET_CURRENT_THREAD(r18); \ - ld.w TI_FLAGS[r18], r19; \ - br 5b; /* Continue with return path. */ \ - \ -/* Handle a signal or ptraced process return. \ - r18 should be non-zero if there are pending signals. */ \ -4: /* Not all registers are saved by the normal trap/interrupt entry \ - points (for instance, call-saved registers (because the normal \ - C-compiler calling sequence in the kernel makes sure they're \ - preserved), and call-clobbered registers in the case of \ - traps), but signal handlers may want to examine or change the \ - complete register state. Here we save anything not saved by \ - the normal entry sequence, so that it may be safely restored \ - (in a possibly modified form) after do_signal returns. */ \ - SAVE_EXTRA_STATE(type); /* Save state not saved by entry. */ \ - jarl handle_signal_or_ptrace_return, lp; \ - RESTORE_EXTRA_STATE(type); /* Restore extra regs. */ \ - br 1b - - -/* Jump to the appropriate function for the system call number in r12 - (r12 is not preserved), or return an error if r12 is not valid. The - LP register should point to the location where the called function - should return. [note that MAKE_SYS_CALL uses label 1] */ -#define MAKE_SYS_CALL \ - /* Figure out which function to use for this system call. */ \ - shl 2, r12; \ - /* See if the system call number is valid. */ \ - addi lo(CSYM(sys_call_table) - sys_call_table_end), r12, r0; \ - bnh 1f; \ - mov hilo(CSYM(sys_call_table)), r19; \ - add r19, r12; \ - ld.w 0[r12], r12; \ - /* Make the system call. */ \ - jmp [r12]; \ - /* The syscall number is invalid, return an error. */ \ -1: addi -ENOSYS, r0, r10; \ - jmp [lp] - - - .text - -/* - * User trap. - * - * Trap 0 system calls are also handled here. - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - * - * Syscall protocol: - * Syscall number in r12, args in r6-r9 - * Return value in r10 - */ -G_ENTRY(trap): - SAVE_STATE (TRAP, r12, ENTRY_SP) // Save registers. - stsr SR_ECR, r19 // Find out which trap it was. - ei // Enable interrupts. - mov hilo(ret_from_trap), lp // where the trap should return - - // The following two shifts (1) clear out extraneous NMI data in the - // upper 16-bits, (2) convert the 0x40 - 0x5f range of trap ECR - // numbers into the (0-31) << 2 range we want, (3) set the flags. - shl 27, r19 // chop off all high bits - shr 25, r19 // scale back down and then << 2 - bnz 2f // See if not trap 0. - - // Trap 0 is a `short' system call, skip general trap table. - MAKE_SYS_CALL // Jump to the syscall function. - -2: // For other traps, use a table lookup. - mov hilo(CSYM(trap_table)), r18 - add r19, r18 - ld.w 0[r18], r18 - jmp [r18] // Jump to the trap handler. -END(trap) - -/* This is just like ret_from_trap, but first restores extra registers - saved by some wrappers. */ -L_ENTRY(restore_extra_regs_and_ret_from_trap): - RESTORE_EXTRA_STATE(TRAP) - // fall through -END(restore_extra_regs_and_ret_from_trap) - -/* Entry point used to return from a syscall/trap. */ -L_ENTRY(ret_from_trap): - RETURN(TRAP) -END(ret_from_trap) - - -/* This the initial entry point for a new child thread, with an appropriate - stack in place that makes it look that the child is in the middle of an - syscall. This function is actually `returned to' from switch_thread - (copy_thread makes ret_from_fork the return address in each new thread's - saved context). */ -C_ENTRY(ret_from_fork): - mov r10, r6 // switch_thread returns the prev task. - jarl CSYM(schedule_tail), lp // ...which is schedule_tail's arg - mov r0, r10 // Child's fork call should return 0. - br ret_from_trap // Do normal trap return. -C_END(ret_from_fork) - - -/* - * Trap 1: `long' system calls - * `Long' syscall protocol: - * Syscall number in r12, args in r6-r9, r13-r14 - * Return value in r10 - */ -L_ENTRY(syscall_long): - // Push extra arguments on the stack. Note that by default, the trap - // handler reserves enough stack space for 6 arguments, so we don't - // have to make any additional room. - st.w r13, 16[sp] // arg 5 - st.w r14, 20[sp] // arg 6 - - // Make sure r13 and r14 are preserved, in case we have to restart a - // system call because of a signal (ep has already been set by caller). - st.w r13, PTO+PT_GPR(13)[sp] - st.w r14, PTO+PT_GPR(13)[sp] - mov hilo(ret_from_long_syscall), lp - - MAKE_SYS_CALL // Jump to the syscall function. -END(syscall_long) - -/* Entry point used to return from a long syscall. Only needed to restore - r13/r14 if the general trap mechanism doesnt' do so. */ -L_ENTRY(ret_from_long_syscall): - ld.w PTO+PT_GPR(13)[sp], r13 // Restore the extra registers - ld.w PTO+PT_GPR(13)[sp], r14 - br ret_from_trap // The rest is the same as other traps -END(ret_from_long_syscall) - - -/* These syscalls need access to the struct pt_regs on the stack, so we - implement them in assembly (they're basically all wrappers anyway). */ - -L_ENTRY(sys_fork_wrapper): -#ifdef CONFIG_MMU - addi SIGCHLD, r0, r6 // Arg 0: flags - ld.w PTO+PT_GPR(GPR_SP)[sp], r7 // Arg 1: child SP (use parent's) - movea PTO, sp, r8 // Arg 2: parent context - mov r0, r9 // Arg 3/4/5: 0 - st.w r0, 16[sp] - st.w r0, 20[sp] - mov hilo(CSYM(do_fork)), r18 // Where the real work gets done - br save_extra_state_tramp // Save state and go there -#else - // fork almost works, enough to trick you into looking elsewhere :-( - addi -EINVAL, r0, r10 - jmp [lp] -#endif -END(sys_fork_wrapper) - -L_ENTRY(sys_vfork_wrapper): - addi CLONE_VFORK | CLONE_VM | SIGCHLD, r0, r6 // Arg 0: flags - ld.w PTO+PT_GPR(GPR_SP)[sp], r7 // Arg 1: child SP (use parent's) - movea PTO, sp, r8 // Arg 2: parent context - mov r0, r9 // Arg 3/4/5: 0 - st.w r0, 16[sp] - st.w r0, 20[sp] - mov hilo(CSYM(do_fork)), r18 // Where the real work gets done - br save_extra_state_tramp // Save state and go there -END(sys_vfork_wrapper) - -L_ENTRY(sys_clone_wrapper): - ld.w PTO+PT_GPR(GPR_SP)[sp], r19// parent's stack pointer - cmp r7, r0 // See if child SP arg (arg 1) is 0. - cmov z, r19, r7, r7 // ... and use the parent's if so. - movea PTO, sp, r8 // Arg 2: parent context - mov r0, r9 // Arg 3/4/5: 0 - st.w r0, 16[sp] - st.w r0, 20[sp] - mov hilo(CSYM(do_fork)), r18 // Where the real work gets done - br save_extra_state_tramp // Save state and go there -END(sys_clone_wrapper) - - -L_ENTRY(sys_execve_wrapper): - movea PTO, sp, r9 // add user context as 4th arg - jr CSYM(sys_execve) // Do real work (tail-call). -END(sys_execve_wrapper) - - -L_ENTRY(sys_sigsuspend_wrapper): - movea PTO, sp, r7 // add user context as 2nd arg - mov hilo(CSYM(sys_sigsuspend)), r18 // syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_sigsuspend_wrapper) -L_ENTRY(sys_rt_sigsuspend_wrapper): - movea PTO, sp, r8 // add user context as 3rd arg - mov hilo(CSYM(sys_rt_sigsuspend)), r18 // syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_rt_sigsuspend_wrapper) - -L_ENTRY(sys_sigreturn_wrapper): - movea PTO, sp, r6 // add user context as 1st arg - mov hilo(CSYM(sys_sigreturn)), r18 // syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_sigreturn_wrapper) -L_ENTRY(sys_rt_sigreturn_wrapper): - movea PTO, sp, r6 // add user context as 1st arg - mov hilo(CSYM(sys_rt_sigreturn)), r18// syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_rt_sigreturn_wrapper) - - -/* Save any state not saved by SAVE_STATE(TRAP), and jump to r18. - It's main purpose is to share the rather lengthy code sequence that - SAVE_STATE expands into among the above wrapper functions. */ -L_ENTRY(save_extra_state_tramp): - SAVE_EXTRA_STATE(TRAP) // Save state not saved by entry. - jmp [r18] // Do the work the caller wants -END(save_extra_state_tramp) - - -/* - * Hardware maskable interrupts. - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - */ -G_ENTRY(irq): - SAVE_STATE (IRQ, r0, ENTRY_SP) // Save registers. - - stsr SR_ECR, r6 // Find out which interrupt it was. - movea PTO, sp, r7 // User regs are arg2 - - // All v850 implementations I know about encode their interrupts as - // multiples of 0x10, starting at 0x80 (after NMIs and software - // interrupts). Convert this number into a simple IRQ index for the - // rest of the kernel. We also clear the upper 16 bits, which hold - // NMI info, and don't appear to be cleared when a NMI returns. - shl 16, r6 // clear upper 16 bits - shr 20, r6 // shift back, and remove lower nibble - add -8, r6 // remove bias for irqs - - // Call the high-level interrupt handling code. - jarl CSYM(handle_irq), lp - - RETURN(IRQ) -END(irq) - - -/* - * Debug trap / illegal-instruction exception - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - */ -G_ENTRY(dbtrap): - SAVE_STATE (DBTRAP, r0, ENTRY_SP)// Save registers. - - /* First see if we came from kernel mode; if so, the dbtrap - instruction has a special meaning, to set the DIR (`debug - information register') register. This is because the DIR register - can _only_ be manipulated/read while in `debug mode,' and debug - mode is only active while we're inside the dbtrap handler. The - exact functionality is: { DIR = (DIR | r6) & ~r7; return DIR; }. */ - ld.b PTO+PT_KERNEL_MODE[sp], r19 - cmp r19, r0 - bz 1f - - stsr SR_DIR, r10 - or r6, r10 - not r7, r7 - and r7, r10 - ldsr r10, SR_DIR - stsr SR_DIR, r10 // Confirm the value we set - st.w r10, PTO+PT_GPR(10)[sp] // return it - br 3f - -1: ei // Enable interrupts. - - /* The default signal type we raise. */ - mov SIGTRAP, r6 - - /* See if it's a single-step trap. */ - stsr SR_DBPSW, r19 - andi 0x0800, r19, r19 - bnz 2f - - /* Look to see if the preceding instruction was is a dbtrap or not, - to decide which signal we should use. */ - stsr SR_DBPC, r19 // PC following trapping insn - ld.hu -2[r19], r19 - ori 0xf840, r0, r20 // DBTRAP insn - cmp r19, r20 // Was this trap caused by DBTRAP? - cmov ne, SIGILL, r6, r6 // Choose signal appropriately - - /* Raise the desired signal. */ -2: mov CURRENT_TASK, r7 // Arg 1: task - jarl CSYM(send_sig), lp // tail call - -3: RETURN(DBTRAP) -END(dbtrap) - - -/* - * Hardware non-maskable interrupts. - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - */ -G_ENTRY(nmi): - SAVE_STATE (NMI, r0, NMI_ENTRY_SP); /* Save registers. */ - - stsr SR_ECR, r6; /* Find out which nmi it was. */ - shr 20, r6; /* Extract NMI code in bits 20-24. */ - movea PTO, sp, r7; /* User regs are arg2. */ - - /* Non-maskable interrupts always lie right after maskable interrupts. - Call the generic IRQ handler, with two arguments, the IRQ number, - and a pointer to the user registers, to handle the specifics. - (we subtract one because the first NMI has code 1). */ - addi FIRST_NMI - 1, r6, r6 - jarl CSYM(handle_irq), lp - - RETURN(NMI) -END(nmi) - - -/* - * Trap with no handler - */ -L_ENTRY(bad_trap_wrapper): - mov r19, r6 // Arg 0: trap number - movea PTO, sp, r7 // Arg 1: user regs - jr CSYM(bad_trap) // tail call handler -END(bad_trap_wrapper) - - -/* - * Invoke the scheduler, called from the trap/irq kernel exit path. - * - * This basically just calls `schedule', but also arranges for extra - * registers to be saved for ptrace'd processes, so ptrace can modify them. - */ -L_ENTRY(call_scheduler): - ld.w TASK_PTRACE[CURRENT_TASK], r19 // See if task is ptrace'd - cmp r19, r0 - bnz 1f // ... yes, do special stuff - jr CSYM(schedule) // ... no, just tail-call scheduler - - // Save extra regs for ptrace'd task. We want to save anything - // that would otherwise only be `implicitly' saved by the normal - // compiler calling-convention. -1: mov sp, ep // Setup EP for SAVE_CALL_SAVED_REGS - SAVE_CALL_SAVED_REGS // Save call-saved registers to stack - mov lp, r20 // Save LP in a callee-saved register - - jarl CSYM(schedule), lp // Call scheduler - - mov r20, lp - mov sp, ep // We can't rely on EP after return - RESTORE_CALL_SAVED_REGS // Restore (possibly modified) regs - jmp [lp] // Return to the return path -END(call_scheduler) - - -/* - * This is an out-of-line handler for two special cases during the kernel - * trap/irq exit sequence: - * - * (1) If r18 is non-zero then a signal needs to be handled, which is - * done, and then the caller returned to. - * - * (2) If r18 is non-zero then we're returning to a ptraced process, which - * has several special cases -- single-stepping and trap tracing, both - * of which require using the `dbret' instruction to exit the kernel - * instead of the normal `reti' (this is because the CPU not correctly - * single-step after a reti). In this case, of course, this handler - * never returns to the caller. - * - * In either case, all registers should have been saved to the current - * state-save-frame on the stack, except for callee-saved registers. - * - * [These two different cases are combined merely to avoid bloating the - * macro-inlined code, not because they really make much sense together!] - */ -L_ENTRY(handle_signal_or_ptrace_return): - cmp r18, r0 // See if handling a signal - bz 1f // ... nope, go do ptrace return - - // Handle a signal - mov lp, r20 // Save link-pointer - mov r10, r21 // Save return-values (for trap) - mov r11, r22 - - movea PTO, sp, r6 // Arg 1: struct pt_regs *regs - mov r0, r7 // Arg 2: sigset_t *oldset - jarl CSYM(do_signal), lp // Handle the signal - di // sig handling enables interrupts - - mov r20, lp // Restore link-pointer - mov r21, r10 // Restore return-values (for trap) - mov r22, r11 - ld.w TASK_PTRACE[CURRENT_TASK], r19 // check ptrace flags too - cmp r19, r0 - bnz 1f // ... some set, so look more -2: jmp [lp] // ... none set, so return normally - - // ptrace return -1: ld.w PTO+PT_PSW[sp], r19 // Look at user-processes's flags - andi 0x0800, r19, r19 // See if single-step flag is set - bz 2b // ... nope, return normally - - // Return as if from a dbtrap insn - st.b r0, KM // Now officially in user state. - POP_STATE(DBTRAP) // Restore regs - st.w sp, KSP // Save the kernel stack pointer. - ld.w PT_GPR(GPR_SP)-PT_SIZE[sp], sp // Restore user stack pointer. - DBTRAP_RET // Return from the trap/interrupt. -END(handle_signal_or_ptrace_return) - - -/* - * This is where we switch between two threads. The arguments are: - * r6 -- pointer to the struct thread for the `current' process - * r7 -- pointer to the struct thread for the `new' process. - * when this function returns, it will return to the new thread. - */ -C_ENTRY(switch_thread): - // Return the previous task (r10 is not clobbered by restore below) - mov CURRENT_TASK, r10 - // First, push the current processor state on the stack - PUSH_STATE(SWITCH) - // Now save the location of the kernel stack pointer for this thread; - // since we've pushed all other state on the stack, this is enough to - // restore it all later. - st.w sp, THREAD_KSP[r6] - // Now restore the stack pointer from the new process - ld.w THREAD_KSP[r7], sp - // ... and restore all state from that - POP_STATE(SWITCH) - // Update the current task pointer - GET_CURRENT_TASK(CURRENT_TASK) - // Now return into the new thread - jmp [lp] -C_END(switch_thread) - - - .data - - .align 4 -C_DATA(trap_table): - .long bad_trap_wrapper // trap 0, doesn't use trap table. - .long syscall_long // trap 1, `long' syscall. - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper -C_END(trap_table) - - - .section .rodata - - .align 4 -C_DATA(sys_call_table): - .long CSYM(sys_restart_syscall) // 0 - .long CSYM(sys_exit) - .long sys_fork_wrapper - .long CSYM(sys_read) - .long CSYM(sys_write) - .long CSYM(sys_open) // 5 - .long CSYM(sys_close) - .long CSYM(sys_waitpid) - .long CSYM(sys_creat) - .long CSYM(sys_link) - .long CSYM(sys_unlink) // 10 - .long sys_execve_wrapper - .long CSYM(sys_chdir) - .long CSYM(sys_time) - .long CSYM(sys_mknod) - .long CSYM(sys_chmod) // 15 - .long CSYM(sys_chown) - .long CSYM(sys_ni_syscall) // was: break - .long CSYM(sys_ni_syscall) // was: oldstat (aka stat) - .long CSYM(sys_lseek) - .long CSYM(sys_getpid) // 20 - .long CSYM(sys_mount) - .long CSYM(sys_oldumount) - .long CSYM(sys_setuid) - .long CSYM(sys_getuid) - .long CSYM(sys_stime) // 25 - .long CSYM(sys_ptrace) - .long CSYM(sys_alarm) - .long CSYM(sys_ni_syscall) // was: oldfstat (aka fstat) - .long CSYM(sys_pause) - .long CSYM(sys_utime) // 30 - .long CSYM(sys_ni_syscall) // was: stty - .long CSYM(sys_ni_syscall) // was: gtty - .long CSYM(sys_access) - .long CSYM(sys_nice) - .long CSYM(sys_ni_syscall) // 35, was: ftime - .long CSYM(sys_sync) - .long CSYM(sys_kill) - .long CSYM(sys_rename) - .long CSYM(sys_mkdir) - .long CSYM(sys_rmdir) // 40 - .long CSYM(sys_dup) - .long CSYM(sys_pipe) - .long CSYM(sys_times) - .long CSYM(sys_ni_syscall) // was: prof - .long CSYM(sys_brk) // 45 - .long CSYM(sys_setgid) - .long CSYM(sys_getgid) - .long CSYM(sys_signal) - .long CSYM(sys_geteuid) - .long CSYM(sys_getegid) // 50 - .long CSYM(sys_acct) - .long CSYM(sys_umount) // recycled never used phys() - .long CSYM(sys_ni_syscall) // was: lock - .long CSYM(sys_ioctl) - .long CSYM(sys_fcntl) // 55 - .long CSYM(sys_ni_syscall) // was: mpx - .long CSYM(sys_setpgid) - .long CSYM(sys_ni_syscall) // was: ulimit - .long CSYM(sys_ni_syscall) - .long CSYM(sys_umask) // 60 - .long CSYM(sys_chroot) - .long CSYM(sys_ustat) - .long CSYM(sys_dup2) - .long CSYM(sys_getppid) - .long CSYM(sys_getpgrp) // 65 - .long CSYM(sys_setsid) - .long CSYM(sys_sigaction) - .long CSYM(sys_sgetmask) - .long CSYM(sys_ssetmask) - .long CSYM(sys_setreuid) // 70 - .long CSYM(sys_setregid) - .long sys_sigsuspend_wrapper - .long CSYM(sys_sigpending) - .long CSYM(sys_sethostname) - .long CSYM(sys_setrlimit) // 75 - .long CSYM(sys_getrlimit) - .long CSYM(sys_getrusage) - .long CSYM(sys_gettimeofday) - .long CSYM(sys_settimeofday) - .long CSYM(sys_getgroups) // 80 - .long CSYM(sys_setgroups) - .long CSYM(sys_select) - .long CSYM(sys_symlink) - .long CSYM(sys_ni_syscall) // was: oldlstat (aka lstat) - .long CSYM(sys_readlink) // 85 - .long CSYM(sys_uselib) - .long CSYM(sys_swapon) - .long CSYM(sys_reboot) - .long CSYM(old_readdir) - .long CSYM(sys_mmap) // 90 - .long CSYM(sys_munmap) - .long CSYM(sys_truncate) - .long CSYM(sys_ftruncate) - .long CSYM(sys_fchmod) - .long CSYM(sys_fchown) // 95 - .long CSYM(sys_getpriority) - .long CSYM(sys_setpriority) - .long CSYM(sys_ni_syscall) // was: profil - .long CSYM(sys_statfs) - .long CSYM(sys_fstatfs) // 100 - .long CSYM(sys_ni_syscall) // i386: ioperm - .long CSYM(sys_socketcall) - .long CSYM(sys_syslog) - .long CSYM(sys_setitimer) - .long CSYM(sys_getitimer) // 105 - .long CSYM(sys_newstat) - .long CSYM(sys_newlstat) - .long CSYM(sys_newfstat) - .long CSYM(sys_ni_syscall) // was: olduname (aka uname) - .long CSYM(sys_ni_syscall) // 110, i386: iopl - .long CSYM(sys_vhangup) - .long CSYM(sys_ni_syscall) // was: idle - .long CSYM(sys_ni_syscall) // i386: vm86old - .long CSYM(sys_wait4) - .long CSYM(sys_swapoff) // 115 - .long CSYM(sys_sysinfo) - .long CSYM(sys_ipc) - .long CSYM(sys_fsync) - .long sys_sigreturn_wrapper - .long sys_clone_wrapper // 120 - .long CSYM(sys_setdomainname) - .long CSYM(sys_newuname) - .long CSYM(sys_ni_syscall) // i386: modify_ldt, m68k: cacheflush - .long CSYM(sys_adjtimex) - .long CSYM(sys_ni_syscall) // 125 - sys_mprotect - .long CSYM(sys_sigprocmask) - .long CSYM(sys_ni_syscall) // sys_create_module - .long CSYM(sys_init_module) - .long CSYM(sys_delete_module) - .long CSYM(sys_ni_syscall) // 130 - sys_get_kernel_syms - .long CSYM(sys_quotactl) - .long CSYM(sys_getpgid) - .long CSYM(sys_fchdir) - .long CSYM(sys_bdflush) - .long CSYM(sys_sysfs) // 135 - .long CSYM(sys_personality) - .long CSYM(sys_ni_syscall) // for afs_syscall - .long CSYM(sys_setfsuid) - .long CSYM(sys_setfsgid) - .long CSYM(sys_llseek) // 140 - .long CSYM(sys_getdents) - .long CSYM(sys_select) // for backward compat; remove someday - .long CSYM(sys_flock) - .long CSYM(sys_ni_syscall) // sys_msync - .long CSYM(sys_readv) // 145 - .long CSYM(sys_writev) - .long CSYM(sys_getsid) - .long CSYM(sys_fdatasync) - .long CSYM(sys_sysctl) - .long CSYM(sys_ni_syscall) // 150 - sys_mlock - .long CSYM(sys_ni_syscall) // sys_munlock - .long CSYM(sys_ni_syscall) // sys_mlockall - .long CSYM(sys_ni_syscall) // sys_munlockall - .long CSYM(sys_sched_setparam) - .long CSYM(sys_sched_getparam) // 155 - .long CSYM(sys_sched_setscheduler) - .long CSYM(sys_sched_getscheduler) - .long CSYM(sys_sched_yield) - .long CSYM(sys_sched_get_priority_max) - .long CSYM(sys_sched_get_priority_min) // 160 - .long CSYM(sys_sched_rr_get_interval) - .long CSYM(sys_nanosleep) - .long CSYM(sys_ni_syscall) // sys_mremap - .long CSYM(sys_setresuid) - .long CSYM(sys_getresuid) // 165 - .long CSYM(sys_ni_syscall) // for vm86 - .long CSYM(sys_ni_syscall) // sys_query_module - .long CSYM(sys_poll) - .long CSYM(sys_nfsservctl) - .long CSYM(sys_setresgid) // 170 - .long CSYM(sys_getresgid) - .long CSYM(sys_prctl) - .long sys_rt_sigreturn_wrapper - .long CSYM(sys_rt_sigaction) - .long CSYM(sys_rt_sigprocmask) // 175 - .long CSYM(sys_rt_sigpending) - .long CSYM(sys_rt_sigtimedwait) - .long CSYM(sys_rt_sigqueueinfo) - .long sys_rt_sigsuspend_wrapper - .long CSYM(sys_pread64) // 180 - .long CSYM(sys_pwrite64) - .long CSYM(sys_lchown) - .long CSYM(sys_getcwd) - .long CSYM(sys_capget) - .long CSYM(sys_capset) // 185 - .long CSYM(sys_sigaltstack) - .long CSYM(sys_sendfile) - .long CSYM(sys_ni_syscall) // streams1 - .long CSYM(sys_ni_syscall) // streams2 - .long sys_vfork_wrapper // 190 - .long CSYM(sys_ni_syscall) - .long CSYM(sys_mmap2) - .long CSYM(sys_truncate64) - .long CSYM(sys_ftruncate64) - .long CSYM(sys_stat64) // 195 - .long CSYM(sys_lstat64) - .long CSYM(sys_fstat64) - .long CSYM(sys_fcntl64) - .long CSYM(sys_getdents64) - .long CSYM(sys_pivot_root) // 200 - .long CSYM(sys_gettid) - .long CSYM(sys_tkill) -sys_call_table_end: -C_END(sys_call_table) diff --git a/arch/v850/kernel/fpga85e2c.c b/arch/v850/kernel/fpga85e2c.c deleted file mode 100644 index ab9cf16a85c8..000000000000 --- a/arch/v850/kernel/fpga85e2c.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * arch/v850/kernel/fpga85e2c.h -- Machine-dependent defs for - * FPGA implementation of V850E2/NA85E2C - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mach.h" - -extern void memcons_setup (void); - - -#define REG_DUMP_ADDR 0x220000 - - -extern struct irqaction reg_snap_action; /* fwd decl */ - - -void __init mach_early_init (void) -{ - int i; - const u32 *src; - register u32 *dst asm ("ep"); - extern u32 _intv_end, _intv_load_start; - - /* Set bus sizes: CS0 32-bit, CS1 16-bit, CS7 8-bit, - everything else 32-bit. */ - V850E2_BSC = 0x2AA6; - for (i = 2; i <= 6; i++) - CSDEV(i) = 0; /* 32 bit */ - - /* Ensure that the simulator halts on a panic, instead of going - into an infinite loop inside the panic function. */ - panic_timeout = -1; - - /* Move the interrupt vectors into their real location. Note that - any relocations there are relative to the real location, so we - don't have to fix anything up. We use a loop instead of calling - memcpy to keep this a leaf function (to avoid a function - prologue being generated). */ - dst = 0x10; /* &_intv_start + 0x10. */ - src = &_intv_load_start; - do { - u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3]; - u32 t4 = src[4], t5 = src[5], t6 = src[6], t7 = src[7]; - dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3; - dst[4] = t4; dst[5] = t5; dst[6] = t6; dst[7] = t7; - dst += 8; - src += 8; - } while (dst < &_intv_end); -} - -void __init mach_setup (char **cmdline) -{ - memcons_setup (); - - /* Setup up NMI0 to copy the registers to a known memory location. - The FGPA board has a button that produces NMI0 when pressed, so - this allows us to push the button, and then look at memory to see - what's in the registers (there's no other way to easily do so). - We have to use `setup_irq' instead of `request_irq' because it's - still too early to do memory allocation. */ - setup_irq (IRQ_NMI (0), ®_snap_action); -} - -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len) -{ - *ram_start = ERAM_ADDR; - *ram_len = ERAM_SIZE; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Setup up the timer interrupt. The FPGA peripheral control - registers _only_ work with single-bit writes (set1/clr1)! */ - __clear_bit (RPU_GTMC_CE_BIT, &RPU_GTMC); - __clear_bit (RPU_GTMC_CLK_BIT, &RPU_GTMC); - __set_bit (RPU_GTMC_CE_BIT, &RPU_GTMC); - - /* We use the first RPU interrupt, which occurs every 8.192ms. */ - setup_irq (IRQ_RPU (0), timer_action); -} - - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -void machine_halt (void) __attribute__ ((noreturn)); -void machine_halt (void) -{ - for (;;) { - DWC(0) = 0x7777; - DWC(1) = 0x7777; - ASC = 0xffff; - FLGREG(0) = 1; /* Halt immediately. */ - asm ("di; halt; nop; nop; nop; nop; nop"); - } -} - -void machine_restart (char *__unused) -{ - machine_halt (); -} - -void machine_power_off (void) -{ - machine_halt (); -} - - -/* Interrupts */ - -struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "RPU", IRQ_RPU(0), IRQ_RPU_NUM, 1, 6 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize interrupts. */ -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - - -/* An interrupt handler that copies the registers to a known memory location, - for debugging purposes. */ - -static void make_reg_snap (int irq, void *dummy, struct pt_regs *regs) -{ - (*(unsigned *)REG_DUMP_ADDR)++; - (*(struct pt_regs *)(REG_DUMP_ADDR + sizeof (unsigned))) = *regs; -} - -static int reg_snap_dev_id; -static struct irqaction reg_snap_action = { - .handler = make_reg_snap, - .mask = CPU_MASK_NONE, - .name = "reg_snap", - .dev_id = ®_snap_dev_id, -}; diff --git a/arch/v850/kernel/fpga85e2c.ld b/arch/v850/kernel/fpga85e2c.ld deleted file mode 100644 index b5d4578ae411..000000000000 --- a/arch/v850/kernel/fpga85e2c.ld +++ /dev/null @@ -1,62 +0,0 @@ -/* Linker script for the FPGA implementation of the V850E2 NA85E2C cpu core - (CONFIG_V850E2_FPGA85E2C). */ - -MEMORY { - /* Reset vector. */ - RESET : ORIGIN = 0, LENGTH = 0x10 - /* Interrupt vectors. */ - INTV : ORIGIN = 0x10, LENGTH = 0x470 - /* The `window' in RAM were we're allowed to load stuff. */ - RAM_LOW : ORIGIN = 0x480, LENGTH = 0x0005FB80 - /* Some more ram above the window were we can put bss &c. */ - RAM_HIGH : ORIGIN = 0x00060000, LENGTH = 0x000A0000 - /* This is the area visible from the outside world (we can use - this only for uninitialized data). */ - VISIBLE : ORIGIN = 0x00200000, LENGTH = 0x00060000 -} - -SECTIONS { - .reset : { - __kram_start = . ; - __intv_start = . ; - *(.intv.reset) /* Reset vector */ - } > RESET - - .ram_low : { - __r0_ram = . ; /* Must be near address 0. */ - . = . + 32 ; - - TEXT_CONTENTS - DATA_CONTENTS - ROOT_FS_CONTENTS - RAMK_INIT_CONTENTS_NO_END - INITRAMFS_CONTENTS - } > RAM_LOW - - /* Where the interrupt vectors are initially loaded. */ - __intv_load_start = . ; - - .intv : { - *(.intv.common) /* Vectors common to all v850e proc. */ - *(.intv.mach) /* Machine-specific int. vectors. */ - __intv_end = . ; - } > INTV AT> RAM_LOW - - .ram_high : { - /* This is here so that when we free init memory the - load-time copy of the interrupt vectors and any empty - space at the end of the `RAM_LOW' area is freed too. */ - . = ALIGN (4096); - __init_end = . ; - - BSS_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - } > RAM_HIGH - - .visible : { - _memcons_output = . ; - . = . + 0x8000 ; - _memcons_output_end = . ; - } > VISIBLE -} diff --git a/arch/v850/kernel/gbus_int.c b/arch/v850/kernel/gbus_int.c deleted file mode 100644 index b2bcc251f65b..000000000000 --- a/arch/v850/kernel/gbus_int.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * arch/v850/kernel/gbus_int.c -- Midas labs GBUS interrupt support - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include - - -/* The number of shared GINT interrupts. */ -#define NUM_GINTS 4 - -/* For each GINT interrupt, how many GBUS interrupts are using it. */ -static unsigned gint_num_active_irqs[NUM_GINTS] = { 0 }; - -/* A table of GINTn interrupts we actually use. - Note that we don't use GINT0 because all the boards we support treat it - specially. */ -struct used_gint { - unsigned gint; - unsigned priority; -} used_gint[] = { - { 1, GBUS_INT_PRIORITY_HIGH }, - { 3, GBUS_INT_PRIORITY_LOW } -}; -#define NUM_USED_GINTS ARRAY_SIZE(used_gint) - -/* A table of which GINT is used by each GBUS interrupts (they are - assigned based on priority). */ -static unsigned char gbus_int_gint[IRQ_GBUS_INT_NUM]; - - -/* Interrupt enabling/disabling. */ - -/* Enable interrupt handling for interrupt IRQ. */ -void gbus_int_enable_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint) - |= GBUS_INT_IRQ_MASK (irq); -} - -/* Disable interrupt handling for interrupt IRQ. Note that any - interrupts received while disabled will be delivered once the - interrupt is enabled again, unless they are explicitly cleared using - `gbus_int_clear_pending_irq'. */ -void gbus_int_disable_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint) - &= ~GBUS_INT_IRQ_MASK (irq); -} - -/* Return true if interrupt handling for interrupt IRQ is enabled. */ -int gbus_int_irq_enabled (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - return (GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint) - & GBUS_INT_IRQ_MASK(irq)); -} - -/* Disable all GBUS irqs. */ -void gbus_int_disable_irqs () -{ - unsigned w, n; - for (w = 0; w < GBUS_INT_NUM_WORDS; w++) - for (n = 0; n < IRQ_GINT_NUM; n++) - GBUS_INT_ENABLE (w, n) = 0; -} - -/* Clear any pending interrupts for IRQ. */ -void gbus_int_clear_pending_irq (unsigned irq) -{ - GBUS_INT_CLEAR (GBUS_INT_IRQ_WORD(irq)) = GBUS_INT_IRQ_MASK (irq); -} - -/* Return true if interrupt IRQ is pending (but disabled). */ -int gbus_int_irq_pending (unsigned irq) -{ - return (GBUS_INT_STATUS (GBUS_INT_IRQ_WORD(irq)) - & GBUS_INT_IRQ_MASK(irq)); -} - - -/* Delegating interrupts. */ - -/* Handle a shared GINT interrupt by passing to the appropriate GBUS - interrupt handler. */ -static irqreturn_t gbus_int_handle_irq (int irq, void *dev_id, - struct pt_regs *regs) -{ - unsigned w; - irqreturn_t rval = IRQ_NONE; - unsigned gint = irq - IRQ_GINT (0); - - for (w = 0; w < GBUS_INT_NUM_WORDS; w++) { - unsigned status = GBUS_INT_STATUS (w); - unsigned enable = GBUS_INT_ENABLE (w, gint); - - /* Only pay attention to enabled interrupts. */ - status &= enable; - if (status) { - irq = IRQ_GBUS_INT (w * GBUS_INT_BITS_PER_WORD); - do { - /* There's an active interrupt in word - W, find out which one, and call its - handler. */ - - while (! (status & 0x1)) { - irq++; - status >>= 1; - } - status &= ~0x1; - - /* Recursively call handle_irq to handle it. */ - handle_irq (irq, regs); - rval = IRQ_HANDLED; - } while (status); - } - } - - /* Toggle the `all enable' bit back and forth, which should cause - another edge transition if there are any other interrupts - still pending, and so result in another CPU interrupt. */ - GBUS_INT_ENABLE (0, gint) &= ~0x1; - GBUS_INT_ENABLE (0, gint) |= 0x1; - - return rval; -} - - -/* Initialize GBUS interrupt sources. */ - -static void irq_nop (unsigned irq) { } - -static unsigned gbus_int_startup_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - - if (gint_num_active_irqs[gint] == 0) { - /* First enable the CPU interrupt. */ - int rval = - request_irq (IRQ_GINT(gint), gbus_int_handle_irq, - IRQF_DISABLED, - "gbus_int_handler", - &gint_num_active_irqs[gint]); - if (rval != 0) - return rval; - } - - gint_num_active_irqs[gint]++; - - gbus_int_clear_pending_irq (irq); - gbus_int_enable_irq (irq); - - return 0; -} - -static void gbus_int_shutdown_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - - gbus_int_disable_irq (irq); - - if (--gint_num_active_irqs[gint] == 0) - /* Disable the CPU interrupt. */ - free_irq (IRQ_GINT(gint), &gint_num_active_irqs[gint]); -} - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -void __init gbus_int_init_irq_types (struct gbus_int_irq_init *inits, - struct hw_interrupt_type *hw_irq_types) -{ - struct gbus_int_irq_init *init; - for (init = inits; init->name; init++) { - unsigned i; - struct hw_interrupt_type *hwit = hw_irq_types++; - - hwit->typename = init->name; - - hwit->startup = gbus_int_startup_irq; - hwit->shutdown = gbus_int_shutdown_irq; - hwit->enable = gbus_int_enable_irq; - hwit->disable = gbus_int_disable_irq; - hwit->ack = irq_nop; - hwit->end = irq_nop; - - /* Initialize kernel IRQ infrastructure for this interrupt. */ - init_irq_handlers(init->base, init->num, init->interval, hwit); - - /* Set the interrupt priorities. */ - for (i = 0; i < init->num; i++) { - unsigned j; - for (j = 0; j < NUM_USED_GINTS; j++) - if (used_gint[j].priority > init->priority) - break; - /* Wherever we stopped looking is one past the - GINT we want. */ - gbus_int_gint[init->base + i * init->interval - - GBUS_INT_BASE_IRQ] - = used_gint[j > 0 ? j - 1 : 0].gint; - } - } -} - - -/* Initialize IRQS. */ - -/* Chip interrupts (GINTn) shared among GBUS interrupts. */ -static struct hw_interrupt_type gint_hw_itypes[NUM_USED_GINTS]; - - -/* GBUS interrupts themselves. */ - -struct gbus_int_irq_init gbus_irq_inits[] __initdata = { - /* First set defaults. */ - { "GBUS_INT", IRQ_GBUS_INT(0), IRQ_GBUS_INT_NUM, 1, 6}, - { 0 } -}; -#define NUM_GBUS_IRQ_INITS (ARRAY_SIZE(gbus_irq_inits) - 1) - -static struct hw_interrupt_type gbus_hw_itypes[NUM_GBUS_IRQ_INITS]; - - -/* Initialize GBUS interrupts. */ -void __init gbus_int_init_irqs (void) -{ - unsigned i; - - /* First initialize the shared gint interrupts. */ - for (i = 0; i < NUM_USED_GINTS; i++) { - unsigned gint = used_gint[i].gint; - struct v850e_intc_irq_init gint_irq_init[2]; - - /* We initialize one GINT interrupt at a time. */ - gint_irq_init[0].name = "GINT"; - gint_irq_init[0].base = IRQ_GINT (gint); - gint_irq_init[0].num = 1; - gint_irq_init[0].interval = 1; - gint_irq_init[0].priority = used_gint[i].priority; - - gint_irq_init[1].name = 0; /* Terminate the vector. */ - - v850e_intc_init_irq_types (gint_irq_init, gint_hw_itypes); - } - - /* Then the GBUS interrupts. */ - gbus_int_disable_irqs (); - gbus_int_init_irq_types (gbus_irq_inits, gbus_hw_itypes); - /* Turn on the `all enable' bits, which are ANDed with - individual interrupt enable bits; we only want to bother with - the latter. They are the first bit in the first word of each - interrupt-enable area. */ - for (i = 0; i < NUM_USED_GINTS; i++) - GBUS_INT_ENABLE (0, used_gint[i].gint) = 0x1; -} diff --git a/arch/v850/kernel/head.S b/arch/v850/kernel/head.S deleted file mode 100644 index c490b937ef14..000000000000 --- a/arch/v850/kernel/head.S +++ /dev/null @@ -1,128 +0,0 @@ -/* - * arch/v850/kernel/head.S -- Lowest-level startup code - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include - - -/* Make a slightly more convenient alias for C_SYMBOL_NAME. */ -#define CSYM C_SYMBOL_NAME - - - .text - - // Define `mach_early_init' as a weak symbol - .global CSYM(mach_early_init) - .weak CSYM(mach_early_init) - -C_ENTRY(start): - // Make sure interrupts are turned off, just in case - di - -#ifdef CONFIG_RESET_GUARD - // See if we got here via an unexpected reset - ld.w RESET_GUARD, r19 // Check current value of reset guard - mov RESET_GUARD_ACTIVE, r20 - cmp r19, r20 - bne 1f // Guard was not active - - // If we get here, the reset guard was active. Load up some - // interesting values as arguments, and jump to the handler. - st.w r0, RESET_GUARD // Allow further resets to succeed - mov lp, r6 // Arg 0: return address - ld.b KM, r7 // Arg 1: kernel mode - mov sp, r9 // Arg 3: stack pointer - ld.w KSP, r19 // maybe switch to kernel stack - cmp r7, r0 // see if already in kernel mode - cmov z, r19, sp, sp // and switch to kernel stack if not - GET_CURRENT_TASK(r8) // Arg 2: task pointer - jr CSYM(unexpected_reset) - -1: st.w r20, RESET_GUARD // Turn on reset guard -#endif /* CONFIG_RESET_GUARD */ - - // Setup a temporary stack for doing pre-initialization function calls. - // - // We can't use the initial kernel stack, because (1) it may be - // located in memory we're not allowed to touch, and (2) since - // it's in the data segment, calling memcpy to initialize that - // area from ROM will overwrite memcpy's return address. - mov hilo(CSYM(_init_stack_end) - 4), sp - - // See if there's a platform-specific early-initialization routine - // defined; it's a weak symbol, so it will have an address of zero if - // there's not. - mov hilo(CSYM(mach_early_init)), r6 - cmp r6, r0 - bz 3f - - // There is one, so call it. If this function is written in C, it - // should be very careful -- the stack pointer is valid, but very - // little else is (e.g., bss is not zeroed yet, and initialized data - // hasn't been). - jarl 2f, lp // first figure out return address -2: add 3f - ., lp - jmp [r6] // do call -3: - -#ifdef CONFIG_ROM_KERNEL - // Copy the data area from ROM to RAM - mov hilo(CSYM(_rom_copy_dst_start)), r6 - mov hilo(CSYM(_rom_copy_src_start)), r7 - mov hilo(CSYM(_rom_copy_dst_end)), r8 - sub r6, r8 - jarl CSYM(memcpy), lp -#endif - - // Load the initial thread's stack, and current task pointer (in r16) - mov hilo(CSYM(init_thread_union)), r19 - movea THREAD_SIZE, r19, sp - ld.w TI_TASK[r19], CURRENT_TASK - -#ifdef CONFIG_TIME_BOOTUP - /* This stuff must come after mach_early_init, because interrupts may - not work until after its been called. */ - jarl CSYM(highres_timer_reset), lp - jarl CSYM(highres_timer_start), lp -#endif - - // Kernel stack pointer save location - st.w sp, KSP - - // Assert that we're in `kernel mode' - mov 1, r19 - st.w r19, KM - -#ifdef CONFIG_ZERO_BSS - // Zero bss area, since we can't rely upon any loader to do so - mov hilo(CSYM(_sbss)), r6 - mov r0, r7 - mov hilo(CSYM(_ebss)), r8 - sub r6, r8 - jarl CSYM(memset), lp -#endif - - // What happens if the main kernel function returns (it shouldn't) - mov hilo(CSYM(machine_halt)), lp - - // Start the linux kernel. We use an indirect jump to get extra - // range, because on some platforms this initial startup code - // (and the associated platform-specific code in mach_early_init) - // are located far away from the main kernel, e.g. so that they - // can initialize RAM first and copy the kernel or something. - mov hilo(CSYM(start_kernel)), r12 - jmp [r12] -C_END(start) diff --git a/arch/v850/kernel/highres_timer.c b/arch/v850/kernel/highres_timer.c deleted file mode 100644 index b16ad1eaf966..000000000000 --- a/arch/v850/kernel/highres_timer.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * arch/v850/kernel/highres_timer.c -- High resolution timing routines - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include - -#define HIGHRES_TIMER_USEC_SHIFT 12 - -/* Pre-calculated constant used for converting ticks to real time - units. We initialize it to prevent it being put into BSS. */ -static u32 highres_timer_usec_prescale = 1; - -void highres_timer_slow_tick_irq (void) __attribute__ ((noreturn)); -void highres_timer_slow_tick_irq (void) -{ - /* This is an interrupt handler, so it must be very careful to - not to trash any registers. At this point, the stack-pointer - (r3) has been saved in the chip ram location ENTRY_SP by the - interrupt vector, so we can use it as a scratch register; we - must also restore it before returning. */ - asm ("ld.w %0[r0], sp;" - "add 1, sp;" - "st.w sp, %0[r0];" - "ld.w %1[r0], sp;" /* restore pre-irq stack-pointer */ - "reti" - :: - "i" (HIGHRES_TIMER_SLOW_TICKS_ADDR), - "i" (ENTRY_SP_ADDR) - : "memory"); -} - -void highres_timer_reset (void) -{ - V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT) = 0; - HIGHRES_TIMER_SLOW_TICKS = 0; -} - -void highres_timer_start (void) -{ - u32 fast_tick_rate; - - /* Start hardware timer. */ - v850e_timer_d_configure (HIGHRES_TIMER_TIMER_D_UNIT, - HIGHRES_TIMER_SLOW_TICK_RATE); - - fast_tick_rate = - (V850E_TIMER_D_BASE_FREQ - >> V850E_TIMER_D_DIVLOG2 (HIGHRES_TIMER_TIMER_D_UNIT)); - - /* The obvious way of calculating microseconds from fast ticks - is to do: - - usec = fast_ticks * 10^6 / fast_tick_rate - - However, divisions are much slower than multiplications, and - the above calculation can overflow, so we do this instead: - - usec = fast_ticks * (10^6 * 2^12 / fast_tick_rate) / 2^12 - - since we can pre-calculate (10^6 * (2^12 / fast_tick_rate)) - and use a shift for dividing by 2^12, this avoids division, - and is almost as accurate (it differs by about 2 microseconds - at the extreme value of the fast-tick counter's ranger). */ - highres_timer_usec_prescale = ((1000000 << HIGHRES_TIMER_USEC_SHIFT) - / fast_tick_rate); - - /* Enable the interrupt (which is hardwired to this use), and - give it the highest priority. */ - V850E_INTC_IC (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)) = 0; -} - -void highres_timer_stop (void) -{ - /* Stop the timer. */ - V850E_TIMER_D_TMCD (HIGHRES_TIMER_TIMER_D_UNIT) = - V850E_TIMER_D_TMCD_CAE; - /* Disable its interrupt, just in case. */ - v850e_intc_disable_irq (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)); -} - -inline void highres_timer_read_ticks (u32 *slow_ticks, u32 *fast_ticks) -{ - int flags; - u32 fast_ticks_1, fast_ticks_2, _slow_ticks; - - local_irq_save (flags); - fast_ticks_1 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT); - _slow_ticks = HIGHRES_TIMER_SLOW_TICKS; - fast_ticks_2 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT); - local_irq_restore (flags); - - if (fast_ticks_2 < fast_ticks_1) - _slow_ticks++; - - *slow_ticks = _slow_ticks; - *fast_ticks = fast_ticks_2; -} - -inline void highres_timer_ticks_to_timeval (u32 slow_ticks, u32 fast_ticks, - struct timeval *tv) -{ - unsigned long sec, sec_rem, usec; - - usec = ((fast_ticks * highres_timer_usec_prescale) - >> HIGHRES_TIMER_USEC_SHIFT); - - sec = slow_ticks / HIGHRES_TIMER_SLOW_TICK_RATE; - sec_rem = slow_ticks % HIGHRES_TIMER_SLOW_TICK_RATE; - - usec += sec_rem * (1000000 / HIGHRES_TIMER_SLOW_TICK_RATE); - - tv->tv_sec = sec; - tv->tv_usec = usec; -} - -void highres_timer_read (struct timeval *tv) -{ - u32 fast_ticks, slow_ticks; - highres_timer_read_ticks (&slow_ticks, &fast_ticks); - highres_timer_ticks_to_timeval (slow_ticks, fast_ticks, tv); -} diff --git a/arch/v850/kernel/init_task.c b/arch/v850/kernel/init_task.c deleted file mode 100644 index 44b274dff33f..000000000000 --- a/arch/v850/kernel/init_task.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * arch/v850/kernel/init_task.c -- Initial task/thread structures - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static struct fs_struct init_fs = INIT_FS; -static struct signal_struct init_signals = INIT_SIGNALS (init_signals); -static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); -struct mm_struct init_mm = INIT_MM (init_mm); - -EXPORT_SYMBOL(init_mm); - -/* - * Initial task structure. - * - * All other task structs will be allocated on slabs in fork.c - */ -struct task_struct init_task = INIT_TASK (init_task); - -EXPORT_SYMBOL(init_task); - -/* - * Initial thread structure. - * - * We need to make sure that this is 8192-byte aligned due to the - * way process stacks are handled. This is done by having a special - * "init_task" linker map entry. - */ -union thread_union init_thread_union - __attribute__((__section__(".data.init_task"))) = - { INIT_THREAD_INFO(init_task) }; diff --git a/arch/v850/kernel/intv.S b/arch/v850/kernel/intv.S deleted file mode 100644 index 671e4c6150dd..000000000000 --- a/arch/v850/kernel/intv.S +++ /dev/null @@ -1,87 +0,0 @@ -/* - * arch/v850/kernel/intv.S -- Interrupt vectors - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include - -#ifdef CONFIG_V850E_HIGHRES_TIMER -#include -#endif - -/* Jump to an interrupt/trap handler. These handlers (defined in entry.S) - expect the stack-pointer to be saved in ENTRY_SP, so we use sp to do an - indirect jump (which avoids problems when the handler is more than a signed - 22-bit offset away). */ -#define JUMP_TO_HANDLER(name, sp_save_loc) \ - st.w sp, sp_save_loc; \ - mov hilo(name), sp; \ - jmp [sp] - - - /* Reset vector. */ - .section .intv.reset, "ax" - .org 0x0 - mov hilo(C_SYMBOL_NAME(start)), r1; - jmp [r1] - - - /* Generic interrupt vectors. */ - .section .intv.common, "ax" - .balign 0x10 - JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x10 - NMI0 - .balign 0x10 - JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x20 - NMI1 - .balign 0x10 - JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x30 - NMI2 - - .balign 0x10 - JUMP_TO_HANDLER (trap, ENTRY_SP) // 0x40 - TRAP0n - .balign 0x10 - JUMP_TO_HANDLER (trap, ENTRY_SP) // 0x50 - TRAP1n - - .balign 0x10 - JUMP_TO_HANDLER (dbtrap, ENTRY_SP) // 0x60 - Illegal op / DBTRAP insn - - - /* Hardware interrupt vectors. */ - .section .intv.mach, "ax" - .org 0x0 - -#if defined (CONFIG_V850E_HIGHRES_TIMER) && defined (IRQ_INTCMD) - - /* Interrupts before the highres timer interrupt. */ - .rept IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT) - .balign 0x10 - JUMP_TO_HANDLER (irq, ENTRY_SP) - .endr - - /* The highres timer interrupt. */ - .balign 0x10 - JUMP_TO_HANDLER (C_SYMBOL_NAME (highres_timer_slow_tick_irq), ENTRY_SP) - - /* Interrupts after the highres timer interrupt. */ - .rept NUM_CPU_IRQS - IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT) - 1 - .balign 0x10 - JUMP_TO_HANDLER (irq, ENTRY_SP) - .endr - -#else /* No highres timer */ - - .rept NUM_CPU_IRQS - .balign 0x10 - JUMP_TO_HANDLER (irq, ENTRY_SP) - .endr - -#endif /* Highres timer */ diff --git a/arch/v850/kernel/irq.c b/arch/v850/kernel/irq.c deleted file mode 100644 index 858c45819aab..000000000000 --- a/arch/v850/kernel/irq.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * arch/v850/kernel/irq.c -- High-level interrupt handling - * - * Copyright (C) 2001,02,03,04,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04,05 Miles Bader - * Copyright (C) 1994-2000 Ralf Baechle - * Copyright (C) 1992 Linus Torvalds - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * This file was was derived from the mips version, arch/mips/kernel/irq.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * 'what should we do if we get a hw irq event on an illegal vector'. - * each architecture has to answer this themselves, it doesn't deserve - * a generic callback i think. - */ -void ack_bad_irq(unsigned int irq) -{ - printk("received IRQ %d with unknown interrupt type\n", irq); -} - -volatile unsigned long irq_err_count, spurious_count; - -/* - * Generic, controller-independent functions: - */ - -int show_interrupts(struct seq_file *p, void *v) -{ - int irq = *(loff_t *) v; - - if (irq == 0) { - int cpu; - seq_puts(p, " "); - for (cpu=0; cpu < 1 /*smp_num_cpus*/; cpu++) - seq_printf(p, "CPU%d ", cpu); - seq_putc(p, '\n'); - } - - if (irq < NR_IRQS) { - unsigned long flags; - struct irqaction *action; - - spin_lock_irqsave(&irq_desc[irq].lock, flags); - - action = irq_desc[irq].action; - if (action) { - int j; - int count = 0; - int num = -1; - const char *type_name = irq_desc[irq].chip->typename; - - for (j = 0; j < NR_IRQS; j++) - if (irq_desc[j].chip->typename == type_name){ - if (irq == j) - num = count; - count++; - } - - seq_printf(p, "%3d: ",irq); - seq_printf(p, "%10u ", kstat_irqs(irq)); - if (count > 1) { - int prec = (num >= 100 ? 3 : num >= 10 ? 2 : 1); - seq_printf(p, " %*s%d", 14 - prec, - type_name, num); - } else - seq_printf(p, " %14s", type_name); - - seq_printf(p, " %s", action->name); - for (action=action->next; action; action = action->next) - seq_printf(p, ", %s", action->name); - seq_putc(p, '\n'); - } - - spin_unlock_irqrestore(&irq_desc[irq].lock, flags); - } else if (irq == NR_IRQS) - seq_printf(p, "ERR: %10lu\n", irq_err_count); - - return 0; -} - -/* Handle interrupt IRQ. REGS are the registers at the time of ther - interrupt. */ -unsigned int handle_irq (int irq, struct pt_regs *regs) -{ - irq_enter(); - __do_IRQ(irq, regs); - irq_exit(); - return 1; -} - -/* Initialize irq handling for IRQs. - BASE_IRQ, BASE_IRQ+INTERVAL, ..., BASE_IRQ+NUM*INTERVAL - to IRQ_TYPE. An IRQ_TYPE of 0 means to use a generic interrupt type. */ -void __init -init_irq_handlers (int base_irq, int num, int interval, - struct hw_interrupt_type *irq_type) -{ - while (num-- > 0) { - irq_desc[base_irq].status = IRQ_DISABLED; - irq_desc[base_irq].action = NULL; - irq_desc[base_irq].depth = 1; - irq_desc[base_irq].chip = irq_type; - base_irq += interval; - } -} diff --git a/arch/v850/kernel/ma.c b/arch/v850/kernel/ma.c deleted file mode 100644 index 143774de75e1..000000000000 --- a/arch/v850/kernel/ma.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * arch/v850/kernel/ma.c -- V850E/MA series of cpu chips - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "DMA", IRQ_INTDMA(0), IRQ_INTDMA_NUM, 1, 2 }, - { "CSI", IRQ_INTCSI(0), IRQ_INTCSI_NUM, 4, 4 }, - { "SER", IRQ_INTSER(0), IRQ_INTSER_NUM, 4, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 4, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 4, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize MA chip interrupts. */ -void __init ma_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -/* Called before configuring an on-chip UART. */ -void ma_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* We only know about the first two UART channels (though - specific chips may have more). */ - if (chan < 2) { - unsigned bits = 0x3 << (chan * 3); - /* Specify that the relevant pins on the chip should do - serial I/O, not direct I/O. */ - MA_PORT4_PMC |= bits; - /* Specify that we're using the UART, not the CSI device. */ - MA_PORT4_PFC |= bits; - } -} diff --git a/arch/v850/kernel/mach.c b/arch/v850/kernel/mach.c deleted file mode 100644 index b9db278d2b71..000000000000 --- a/arch/v850/kernel/mach.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * arch/v850/kernel/mach.c -- Defaults for some things defined by "mach.h" - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include "mach.h" - -/* Called with each timer tick, if non-zero. */ -void (*mach_tick)(void) = 0; diff --git a/arch/v850/kernel/mach.h b/arch/v850/kernel/mach.h deleted file mode 100644 index 9e0e4816ec56..000000000000 --- a/arch/v850/kernel/mach.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * arch/v850/kernel/mach.h -- Machine-dependent functions used by v850 port - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MACH_H__ -#define __V850_MACH_H__ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -void mach_setup (char **cmdline); -void mach_gettimeofday (struct timespec *tv); -void mach_sched_init (struct irqaction *timer_action); -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len); -void mach_init_irqs (void); - -/* If defined, is called very early in the kernel initialization. The - stack pointer is valid, but very little has been initialized (e.g., - bss is not zeroed yet) when this is called, so care must taken. */ -void mach_early_init (void); - -/* If defined, called after the bootmem allocator has been initialized, - to allow the platform-dependent code to reserve any areas of RAM that - the kernel shouldn't touch. */ -void mach_reserve_bootmem (void) __attribute__ ((__weak__)); - -/* Called with each timer tick, if non-zero. */ -extern void (*mach_tick) (void); - -/* The following establishes aliases for various mach_ functions to the - name by which the rest of the kernel calls them. These statements - should only have an effect in the file that defines the actual functions. */ -#define MACH_ALIAS(to, from) \ - asm (".global " macrology_stringify (C_SYMBOL_NAME (to)) ";" \ - macrology_stringify (C_SYMBOL_NAME (to)) \ - " = " macrology_stringify (C_SYMBOL_NAME (from))) -/* e.g.: MACH_ALIAS (kernel_name, arch_spec_name); */ - -#endif /* __V850_MACH_H__ */ diff --git a/arch/v850/kernel/me2.c b/arch/v850/kernel/me2.c deleted file mode 100644 index 007115dc9ce0..000000000000 --- a/arch/v850/kernel/me2.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * arch/v850/kernel/me2.c -- V850E/ME2 chip-specific support - * - * Copyright (C) 2003 NEC Corporation - * Copyright (C) 2003 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_CPU_IRQS, 1, 7 }, - { "INTP", IRQ_INTP(0), IRQ_INTP_NUM, 1, 5 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 3 }, - { "UBTIRE", IRQ_INTUBTIRE(0), IRQ_INTUBTIRE_NUM, 5, 4 }, - { "UBTIR", IRQ_INTUBTIR(0), IRQ_INTUBTIR_NUM, 5, 4 }, - { "UBTIT", IRQ_INTUBTIT(0), IRQ_INTUBTIT_NUM, 5, 4 }, - { "UBTIF", IRQ_INTUBTIF(0), IRQ_INTUBTIF_NUM, 5, 4 }, - { "UBTITO", IRQ_INTUBTITO(0), IRQ_INTUBTITO_NUM, 5, 4 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize V850E/ME2 chip interrupts. */ -void __init me2_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -/* Called before configuring an on-chip UART. */ -void me2_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - if (chan == 0) { - /* Specify that the relevant pins on the chip should do - serial I/O, not direct I/O. */ - ME2_PORT1_PMC |= 0xC; - /* Specify that we're using the UART, not the CSI device. */ - ME2_PORT1_PFC |= 0xC; - } else if (chan == 1) { - /* Specify that the relevant pins on the chip should do - serial I/O, not direct I/O. */ - ME2_PORT2_PMC |= 0x6; - /* Specify that we're using the UART, not the CSI device. */ - ME2_PORT2_PFC |= 0x6; - } -} diff --git a/arch/v850/kernel/memcons.c b/arch/v850/kernel/memcons.c deleted file mode 100644 index 92f514fdcc79..000000000000 --- a/arch/v850/kernel/memcons.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * arch/v850/kernel/memcons.c -- Console I/O to a memory buffer - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include - -/* If this device is enabled, the linker map should define start and - end points for its buffer. */ -extern char memcons_output[], memcons_output_end; - -/* Current offset into the buffer. */ -static unsigned long memcons_offs = 0; - -/* Spinlock protecting memcons_offs. */ -static DEFINE_SPINLOCK(memcons_lock); - - -static size_t write (const char *buf, size_t len) -{ - unsigned long flags; - char *point; - - spin_lock_irqsave (memcons_lock, flags); - - point = memcons_output + memcons_offs; - if (point + len >= &memcons_output_end) { - len = &memcons_output_end - point; - memcons_offs = 0; - } else - memcons_offs += len; - - spin_unlock_irqrestore (memcons_lock, flags); - - memcpy (point, buf, len); - - return len; -} - - -/* Low-level console. */ - -static void memcons_write (struct console *co, const char *buf, unsigned len) -{ - while (len > 0) - len -= write (buf, len); -} - -static struct tty_driver *tty_driver; - -static struct tty_driver *memcons_device (struct console *co, int *index) -{ - *index = co->index; - return tty_driver; -} - -static struct console memcons = -{ - .name = "memcons", - .write = memcons_write, - .device = memcons_device, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -void memcons_setup (void) -{ - register_console (&memcons); - printk (KERN_INFO "Console: static memory buffer (memcons)\n"); -} - -/* Higher level TTY interface. */ - -int memcons_tty_open (struct tty_struct *tty, struct file *filp) -{ - return 0; -} - -int memcons_tty_write (struct tty_struct *tty, const unsigned char *buf, int len) -{ - return write (buf, len); -} - -int memcons_tty_write_room (struct tty_struct *tty) -{ - return &memcons_output_end - (memcons_output + memcons_offs); -} - -int memcons_tty_chars_in_buffer (struct tty_struct *tty) -{ - /* We have no buffer. */ - return 0; -} - -static const struct tty_operations ops = { - .open = memcons_tty_open, - .write = memcons_tty_write, - .write_room = memcons_tty_write_room, - .chars_in_buffer = memcons_tty_chars_in_buffer, -}; - -int __init memcons_tty_init (void) -{ - int err; - struct tty_driver *driver = alloc_tty_driver(1); - if (!driver) - return -ENOMEM; - - driver->name = "memcons"; - driver->major = TTY_MAJOR; - driver->minor_start = 64; - driver->type = TTY_DRIVER_TYPE_SYSCONS; - driver->init_termios = tty_std_termios; - tty_set_operations(driver, &ops); - err = tty_register_driver(driver); - if (err) { - put_tty_driver(driver); - return err; - } - tty_driver = driver; - return 0; -} -__initcall (memcons_tty_init); diff --git a/arch/v850/kernel/module.c b/arch/v850/kernel/module.c deleted file mode 100644 index 64aeb3e37c52..000000000000 --- a/arch/v850/kernel/module.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * arch/v850/kernel/module.c -- Architecture-specific module functions - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * Copyright (C) 2001,03 Rusty Russell - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - * - * Derived in part from arch/ppc/kernel/module.c - */ - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(fmt , ...) -#endif - -void *module_alloc (unsigned long size) -{ - return size == 0 ? 0 : vmalloc (size); -} - -void module_free (struct module *mod, void *module_region) -{ - vfree (module_region); - /* FIXME: If module_region == mod->init_region, trim exception - table entries. */ -} - -int module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, - struct module *mod) -{ - return 0; -} - -/* Count how many different relocations (different symbol, different - addend) */ -static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) -{ - unsigned int i, j, ret = 0; - - /* Sure, this is order(n^2), but it's usually short, and not - time critical */ - for (i = 0; i < num; i++) { - for (j = 0; j < i; j++) { - /* If this addend appeared before, it's - already been counted */ - if (ELF32_R_SYM(rela[i].r_info) - == ELF32_R_SYM(rela[j].r_info) - && rela[i].r_addend == rela[j].r_addend) - break; - } - if (j == i) ret++; - } - return ret; -} - -/* Get the potential trampolines size required of the init and - non-init sections */ -static unsigned long get_plt_size(const Elf32_Ehdr *hdr, - const Elf32_Shdr *sechdrs, - const char *secstrings, - int is_init) -{ - unsigned long ret = 0; - unsigned i; - - /* Everything marked ALLOC (this includes the exported - symbols) */ - for (i = 1; i < hdr->e_shnum; i++) { - /* If it's called *.init*, and we're not init, we're - not interested */ - if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) - != is_init) - continue; - - if (sechdrs[i].sh_type == SHT_RELA) { - DEBUGP("Found relocations in section %u\n", i); - DEBUGP("Ptr: %p. Number: %u\n", - (void *)hdr + sechdrs[i].sh_offset, - sechdrs[i].sh_size / sizeof(Elf32_Rela)); - ret += count_relocs((void *)hdr - + sechdrs[i].sh_offset, - sechdrs[i].sh_size - / sizeof(Elf32_Rela)) - * sizeof(struct v850_plt_entry); - } - } - - return ret; -} - -int module_frob_arch_sections(Elf32_Ehdr *hdr, - Elf32_Shdr *sechdrs, - char *secstrings, - struct module *me) -{ - unsigned int i; - - /* Find .plt and .pltinit sections */ - for (i = 0; i < hdr->e_shnum; i++) { - if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) - me->arch.init_plt_section = i; - else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) - me->arch.core_plt_section = i; - } - if (!me->arch.core_plt_section || !me->arch.init_plt_section) { - printk("Module doesn't contain .plt or .plt.init sections.\n"); - return -ENOEXEC; - } - - /* Override their sizes */ - sechdrs[me->arch.core_plt_section].sh_size - = get_plt_size(hdr, sechdrs, secstrings, 0); - sechdrs[me->arch.init_plt_section].sh_size - = get_plt_size(hdr, sechdrs, secstrings, 1); - return 0; -} - -int apply_relocate (Elf32_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *mod) -{ - printk ("Barf\n"); - return -ENOEXEC; -} - -/* Set up a trampoline in the PLT to bounce us to the distant function */ -static uint32_t do_plt_call (void *location, Elf32_Addr val, - Elf32_Shdr *sechdrs, struct module *mod) -{ - struct v850_plt_entry *entry; - /* Instructions used to do the indirect jump. */ - uint32_t tramp[2]; - - /* We have to trash a register, so we assume that any control - transfer more than 21-bits away must be a function call - (so we can use a call-clobbered register). */ - tramp[0] = 0x0621 + ((val & 0xffff) << 16); /* mov sym, r1 ... */ - tramp[1] = ((val >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ - - /* Init, or core PLT? */ - if (location >= mod->module_core - && location < mod->module_core + mod->core_size) - entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; - else - entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; - - /* Find this entry, or if that fails, the next avail. entry */ - while (entry->tramp[0]) - if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1]) - return (uint32_t)entry; - else - entry++; - - entry->tramp[0] = tramp[0]; - entry->tramp[1] = tramp[1]; - - return (uint32_t)entry; -} - -int apply_relocate_add (Elf32_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *mod) -{ - unsigned int i; - Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; - - DEBUGP ("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); - - for (i = 0; i < sechdrs[relsec].sh_size / sizeof (*rela); i++) { - /* This is where to make the change */ - uint32_t *loc - = ((void *)sechdrs[sechdrs[relsec].sh_info].sh_addr - + rela[i].r_offset); - /* This is the symbol it is referring to. Note that all - undefined symbols have been resolved. */ - Elf32_Sym *sym - = ((Elf32_Sym *)sechdrs[symindex].sh_addr - + ELF32_R_SYM (rela[i].r_info)); - uint32_t val = sym->st_value + rela[i].r_addend; - - switch (ELF32_R_TYPE (rela[i].r_info)) { - case R_V850_32: - /* We write two shorts instead of a long because even - 32-bit insns only need half-word alignment, but - 32-bit data writes need to be long-word aligned. */ - val += ((uint16_t *)loc)[0]; - val += ((uint16_t *)loc)[1] << 16; - ((uint16_t *)loc)[0] = val & 0xffff; - ((uint16_t *)loc)[1] = (val >> 16) & 0xffff; - break; - - case R_V850_22_PCREL: - /* Maybe jump indirectly via a PLT table entry. */ - if ((int32_t)(val - (uint32_t)loc) > 0x1fffff - || (int32_t)(val - (uint32_t)loc) < -0x200000) - val = do_plt_call (loc, val, sechdrs, mod); - - val -= (uint32_t)loc; - - /* We write two shorts instead of a long because - even 32-bit insns only need half-word alignment, - but 32-bit data writes need to be long-word - aligned. */ - ((uint16_t *)loc)[0] = - (*(uint16_t *)loc & 0xffc0) /* opcode + reg */ - | ((val >> 16) & 0xffc03f); /* offs high */ - ((uint16_t *)loc)[1] = - (val & 0xffff); /* offs low */ - break; - - default: - printk (KERN_ERR "module %s: Unknown reloc: %u\n", - mod->name, ELF32_R_TYPE (rela[i].r_info)); - return -ENOEXEC; - } - } - - return 0; -} - -void -module_arch_cleanup(struct module *mod) -{ -} diff --git a/arch/v850/kernel/process.c b/arch/v850/kernel/process.c deleted file mode 100644 index e4a4b8e7d5a3..000000000000 --- a/arch/v850/kernel/process.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * arch/v850/kernel/process.c -- Arch-dependent process handling - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -void (*pm_power_off)(void) = NULL; -EXPORT_SYMBOL(pm_power_off); - -extern void ret_from_fork (void); - - -/* The idle loop. */ -static void default_idle (void) -{ - while (! need_resched ()) - asm ("halt; nop; nop; nop; nop; nop" ::: "cc"); -} - -void (*idle)(void) = default_idle; - -/* - * The idle thread. There's no useful work to be - * done, so just try to conserve power and have a - * low exit latency (ie sit in a loop waiting for - * somebody to say that they'd like to reschedule) - */ -void cpu_idle (void) -{ - /* endless idle loop with no priority at all */ - while (1) { - while (!need_resched()) - (*idle) (); - - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } -} - -/* - * This is the mechanism for creating a new kernel thread. - * - * NOTE! Only a kernel-only process (ie the swapper or direct descendants who - * haven't done an "execve()") should use this: it will work within a system - * call from a "real" process, but the process memory space will not be free'd - * until both the parent and the child have exited. - */ -int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags) -{ - register mm_segment_t fs = get_fs (); - register unsigned long syscall asm (SYSCALL_NUM); - register unsigned long arg0 asm (SYSCALL_ARG0); - register unsigned long ret asm (SYSCALL_RET); - - set_fs (KERNEL_DS); - - /* Clone this thread. Note that we don't pass the clone syscall's - second argument -- it's ignored for calls from kernel mode (the - child's SP is always set to the top of the kernel stack). */ - arg0 = flags | CLONE_VM; - syscall = __NR_clone; - asm volatile ("trap " SYSCALL_SHORT_TRAP - : "=r" (ret), "=r" (syscall) - : "1" (syscall), "r" (arg0) - : SYSCALL_SHORT_CLOBBERS); - - if (ret == 0) { - /* In child thread, call FN and exit. */ - arg0 = (*fn) (arg); - syscall = __NR_exit; - asm volatile ("trap " SYSCALL_SHORT_TRAP - : "=r" (ret), "=r" (syscall) - : "1" (syscall), "r" (arg0) - : SYSCALL_SHORT_CLOBBERS); - } - - /* In parent. */ - set_fs (fs); - - return ret; -} - -void flush_thread (void) -{ - set_fs (USER_DS); -} - -int copy_thread (int nr, unsigned long clone_flags, - unsigned long stack_start, unsigned long stack_size, - struct task_struct *p, struct pt_regs *regs) -{ - /* Start pushing stuff from the top of the child's kernel stack. */ - unsigned long orig_ksp = task_tos(p); - unsigned long ksp = orig_ksp; - /* We push two `state save' stack fames (see entry.S) on the new - kernel stack: - 1) The innermost one is what switch_thread would have - pushed, and is used when we context switch to the child - thread for the first time. It's set up to return to - ret_from_fork in entry.S. - 2) The outermost one (nearest the top) is what a syscall - trap would have pushed, and is set up to return to the - same location as the parent thread, but with a return - value of 0. */ - struct pt_regs *child_switch_regs, *child_trap_regs; - - /* Trap frame. */ - ksp -= STATE_SAVE_SIZE; - child_trap_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET); - /* Switch frame. */ - ksp -= STATE_SAVE_SIZE; - child_switch_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET); - - /* First copy parent's register state to child. */ - *child_switch_regs = *regs; - *child_trap_regs = *regs; - - /* switch_thread returns to the restored value of the lp - register (r31), so we make that the place where we want to - jump when the child thread begins running. */ - child_switch_regs->gpr[GPR_LP] = (v850_reg_t)ret_from_fork; - - if (regs->kernel_mode) - /* Since we're returning to kernel-mode, make sure the child's - stored kernel stack pointer agrees with what the actual - stack pointer will be at that point (the trap return code - always restores the SP, even when returning to - kernel-mode). */ - child_trap_regs->gpr[GPR_SP] = orig_ksp; - else - /* Set the child's user-mode stack-pointer (the name - `stack_start' is a misnomer, it's just the initial SP - value). */ - child_trap_regs->gpr[GPR_SP] = stack_start; - - /* Thread state for the child (everything else is on the stack). */ - p->thread.ksp = ksp; - - return 0; -} - -/* - * sys_execve() executes a new program. - */ -int sys_execve (char *name, char **argv, char **envp, struct pt_regs *regs) -{ - char *filename = getname (name); - int error = PTR_ERR (filename); - - if (! IS_ERR (filename)) { - error = do_execve (filename, argv, envp, regs); - putname (filename); - } - - return error; -} - - -/* - * These bracket the sleeping functions.. - */ -#define first_sched ((unsigned long)__sched_text_start) -#define last_sched ((unsigned long)__sched_text_end) - -unsigned long get_wchan (struct task_struct *p) -{ -#if 0 /* Barf. Figure out the stack-layout later. XXX */ - unsigned long fp, pc; - int count = 0; - - if (!p || p == current || p->state == TASK_RUNNING) - return 0; - - pc = thread_saved_pc (p); - - /* This quite disgusting function walks up the stack, following - saved return address, until it something that's out of bounds - (as defined by `first_sched' and `last_sched'). It then - returns the last PC that was in-bounds. */ - do { - if (fp < stack_page + sizeof (struct task_struct) || - fp >= 8184+stack_page) - return 0; - pc = ((unsigned long *)fp)[1]; - if (pc < first_sched || pc >= last_sched) - return pc; - fp = *(unsigned long *) fp; - } while (count++ < 16); -#endif - - return 0; -} diff --git a/arch/v850/kernel/procfs.c b/arch/v850/kernel/procfs.c deleted file mode 100644 index e433cde789b4..000000000000 --- a/arch/v850/kernel/procfs.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * arch/v850/kernel/procfs.c -- Introspection functions for /proc filesystem - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include "mach.h" - -static int cpuinfo_print (struct seq_file *m, void *v) -{ - extern unsigned long loops_per_jiffy; - - seq_printf (m, "CPU-Family: v850\nCPU-Arch: %s\n", CPU_ARCH); - -#ifdef CPU_MODEL_LONG - seq_printf (m, "CPU-Model: %s (%s)\n", CPU_MODEL, CPU_MODEL_LONG); -#else - seq_printf (m, "CPU-Model: %s\n", CPU_MODEL); -#endif - -#ifdef CPU_CLOCK_FREQ - seq_printf (m, "CPU-Clock: %ld (%ld MHz)\n", - (long)CPU_CLOCK_FREQ, - (long)CPU_CLOCK_FREQ / 1000000); -#endif - - seq_printf (m, "BogoMips: %lu.%02lu\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100); - -#ifdef PLATFORM_LONG - seq_printf (m, "Platform: %s (%s)\n", PLATFORM, PLATFORM_LONG); -#elif defined (PLATFORM) - seq_printf (m, "Platform: %s\n", PLATFORM); -#endif - - return 0; -} - -static void *cpuinfo_start (struct seq_file *m, loff_t *pos) -{ - return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL; -} - -static void *cpuinfo_next (struct seq_file *m, void *v, loff_t *pos) -{ - ++*pos; - return cpuinfo_start (m, pos); -} - -static void cpuinfo_stop (struct seq_file *m, void *v) -{ -} - -const struct seq_operations cpuinfo_op = { - .start = cpuinfo_start, - .next = cpuinfo_next, - .stop = cpuinfo_stop, - .show = cpuinfo_print -}; diff --git a/arch/v850/kernel/ptrace.c b/arch/v850/kernel/ptrace.c deleted file mode 100644 index a458ac941b25..000000000000 --- a/arch/v850/kernel/ptrace.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * arch/v850/kernel/ptrace.c -- `ptrace' system call - * - * Copyright (C) 2002,03,04 NEC Electronics Corporation - * Copyright (C) 2002,03,04 Miles Bader - * - * Derived from arch/mips/kernel/ptrace.c: - * - * Copyright (C) 1992 Ross Biro - * Copyright (C) Linus Torvalds - * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle - * Copyright (C) 1996 David S. Miller - * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com - * Copyright (C) 1999 MIPS Technologies, Inc. - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Returns the address where the register at REG_OFFS in P is stashed away. */ -static v850_reg_t *reg_save_addr (unsigned reg_offs, struct task_struct *t) -{ - struct pt_regs *regs; - - /* Three basic cases: - - (1) A register normally saved before calling the scheduler, is - available in the kernel entry pt_regs structure at the top - of the kernel stack. The kernel trap/irq exit path takes - care to save/restore almost all registers for ptrace'd - processes. - - (2) A call-clobbered register, where the process P entered the - kernel via [syscall] trap, is not stored anywhere; that's - OK, because such registers are not expected to be preserved - when the trap returns anyway (so we don't actually bother to - test for this case). - - (3) A few registers not used at all by the kernel, and so - normally never saved except by context-switches, are in the - context switch state. */ - - if (reg_offs == PT_CTPC || reg_offs == PT_CTPSW || reg_offs == PT_CTBP) - /* Register saved during context switch. */ - regs = thread_saved_regs (t); - else - /* Register saved during kernel entry (or not available). */ - regs = task_pt_regs (t); - - return (v850_reg_t *)((char *)regs + reg_offs); -} - -/* Set the bits SET and clear the bits CLEAR in the v850e DIR - (`debug information register'). Returns the new value of DIR. */ -static inline v850_reg_t set_dir (v850_reg_t set, v850_reg_t clear) -{ - register v850_reg_t rval asm ("r10"); - register v850_reg_t arg0 asm ("r6") = set; - register v850_reg_t arg1 asm ("r7") = clear; - - /* The dbtrap handler has exactly this functionality when called - from kernel mode. 0xf840 is a `dbtrap' insn. */ - asm (".short 0xf840" : "=r" (rval) : "r" (arg0), "r" (arg1)); - - return rval; -} - -/* Makes sure hardware single-stepping is (globally) enabled. - Returns true if successful. */ -static inline int enable_single_stepping (void) -{ - static int enabled = 0; /* Remember whether we already did it. */ - if (! enabled) { - /* Turn on the SE (`single-step enable') bit, 0x100, in the - DIR (`debug information register'). This may fail if a - processor doesn't support it or something. We also try - to clear bit 0x40 (`INI'), which is necessary to use the - debug stuff on the v850e2; on the v850e, clearing 0x40 - shouldn't cause any problem. */ - v850_reg_t dir = set_dir (0x100, 0x40); - /* Make sure it really got set. */ - if (dir & 0x100) - enabled = 1; - } - return enabled; -} - -/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */ -static int set_single_step (struct task_struct *t, int val) -{ - v850_reg_t *psw_addr = reg_save_addr(PT_PSW, t); - if (val) { - /* Make sure single-stepping is enabled. */ - if (! enable_single_stepping ()) - return 0; - /* Set T's single-step flag. */ - *psw_addr |= 0x800; - } else - *psw_addr &= ~0x800; - return 1; -} - -long arch_ptrace(struct task_struct *child, long request, long addr, long data) -{ - int rval; - - switch (request) { - unsigned long val; - - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - rval = generic_ptrace_peekdata(child, addr, data); - goto out; - - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - rval = generic_ptrace_pokedata(child, addr, data); - goto out; - - /* Read/write the word at location ADDR in the registers. */ - case PTRACE_PEEKUSR: - case PTRACE_POKEUSR: - rval = 0; - if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) { - /* Special requests that don't actually correspond - to offsets in struct pt_regs. */ - if (addr == PT_TEXT_ADDR) - val = child->mm->start_code; - else if (addr == PT_DATA_ADDR) - val = child->mm->start_data; - else if (addr == PT_TEXT_LEN) - val = child->mm->end_code - - child->mm->start_code; - else - rval = -EIO; - } else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) { - v850_reg_t *reg_addr = reg_save_addr(addr, child); - if (request == PTRACE_PEEKUSR) - val = *reg_addr; - else - *reg_addr = data; - } else - rval = -EIO; - - if (rval == 0 && request == PTRACE_PEEKUSR) - rval = put_user (val, (unsigned long *)data); - goto out; - - /* Continue and stop at next (return from) syscall */ - case PTRACE_SYSCALL: - /* Restart after a signal. */ - case PTRACE_CONT: - /* Execute a single instruction. */ - case PTRACE_SINGLESTEP: - rval = -EIO; - if (!valid_signal(data)) - break; - - /* Turn CHILD's single-step flag on or off. */ - if (! set_single_step (child, request == PTRACE_SINGLESTEP)) - break; - - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - child->exit_code = data; - wake_up_process(child); - rval = 0; - break; - - /* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: - rval = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - wake_up_process(child); - break; - - case PTRACE_DETACH: /* detach a process that was attached. */ - set_single_step (child, 0); /* Clear single-step flag */ - rval = ptrace_detach(child, data); - break; - - default: - rval = -EIO; - goto out; - } - out: - return rval; -} - -asmlinkage void syscall_trace(void) -{ - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; - /* The 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } -} - -void ptrace_disable (struct task_struct *child) -{ - /* nothing to do */ -} diff --git a/arch/v850/kernel/rte_cb.c b/arch/v850/kernel/rte_cb.c deleted file mode 100644 index 43018e1edebd..000000000000 --- a/arch/v850/kernel/rte_cb.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * include/asm-v850/rte_cb.c -- Midas lab RTE-CB series of evaluation boards - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "mach.h" - -static void led_tick (void); - -/* LED access routines. */ -extern unsigned read_leds (int pos, char *buf, int len); -extern unsigned write_leds (int pos, const char *buf, int len); - -#ifdef CONFIG_RTE_CB_MULTI -extern void multi_init (void); -#endif - - -void __init rte_cb_early_init (void) -{ - v850e_intc_disable_irqs (); - -#ifdef CONFIG_RTE_CB_MULTI - multi_init (); -#endif -} - -void __init mach_setup (char **cmdline) -{ -#ifdef CONFIG_RTE_MB_A_PCI - /* Probe for Mother-A, and print a message if we find it. */ - *(volatile unsigned long *)MB_A_SRAM_ADDR = 0xDEADBEEF; - if (*(volatile unsigned long *)MB_A_SRAM_ADDR == 0xDEADBEEF) { - *(volatile unsigned long *)MB_A_SRAM_ADDR = 0x12345678; - if (*(volatile unsigned long *)MB_A_SRAM_ADDR == 0x12345678) - printk (KERN_INFO - " NEC SolutionGear/Midas lab" - " RTE-MOTHER-A motherboard\n"); - } -#endif /* CONFIG_RTE_MB_A_PCI */ - - mach_tick = led_tick; -} - -void machine_restart (char *__unused) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - asm ("jmp r0"); /* Jump to the reset vector. */ -} - -/* This says `HALt.' in LEDese. */ -static unsigned char halt_leds_msg[] = { 0x76, 0x77, 0x38, 0xF8 }; - -void machine_halt (void) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - - /* Ignore all interrupts. */ - local_irq_disable (); - - /* Write a little message. */ - write_leds (0, halt_leds_msg, sizeof halt_leds_msg); - - /* Really halt. */ - for (;;) - asm ("halt; nop; nop; nop; nop; nop"); -} - -void machine_power_off (void) -{ - machine_halt (); -} - - -/* Animated LED display for timer tick. */ - -#define TICK_UPD_FREQ 6 -static int tick_frames[][10] = { - { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, -1 }, - { 0x63, 0x5c, -1 }, - { 0x5c, 0x00, -1 }, - { 0x63, 0x00, -1 }, - { -1 } -}; - -static void led_tick () -{ - static unsigned counter = 0; - - if (++counter == (HZ / TICK_UPD_FREQ)) { - /* Which frame we're currently displaying for each digit. */ - static unsigned frame_nums[LED_NUM_DIGITS] = { 0 }; - /* Display image. */ - static unsigned char image[LED_NUM_DIGITS] = { 0 }; - unsigned char prev_image[LED_NUM_DIGITS]; - int write_to_leds = 1; /* true if we should actually display */ - int digit; - - /* We check to see if the physical LEDs contains what we last - wrote to them; if not, we suppress display (this is so that - users can write to the LEDs, and not have their output - overwritten). As a special case, we start writing again if - all the LEDs are blank, or our display image is all zeros - (indicating that this is the initial update, when the actual - LEDs might contain random data). */ - read_leds (0, prev_image, LED_NUM_DIGITS); - for (digit = 0; digit < LED_NUM_DIGITS; digit++) - if (image[digit] != prev_image[digit] - && image[digit] && prev_image[digit]) - { - write_to_leds = 0; - break; - } - - /* Update display image. */ - for (digit = 0; - digit < LED_NUM_DIGITS && tick_frames[digit][0] >= 0; - digit++) - { - int frame = tick_frames[digit][frame_nums[digit]]; - if (frame < 0) { - image[digit] = tick_frames[digit][0]; - frame_nums[digit] = 1; - } else { - image[digit] = frame; - frame_nums[digit]++; - break; - } - } - - if (write_to_leds) - /* Write the display image to the physical LEDs. */ - write_leds (0, image, LED_NUM_DIGITS); - - counter = 0; - } -} - - -/* Mother-A interrupts. */ - -#ifdef CONFIG_RTE_GBUS_INT - -#define L GBUS_INT_PRIORITY_LOW -#define M GBUS_INT_PRIORITY_MEDIUM -#define H GBUS_INT_PRIORITY_HIGH - -static struct gbus_int_irq_init gbus_irq_inits[] = { -#ifdef CONFIG_RTE_MB_A_PCI - { "MB_A_LAN", IRQ_MB_A_LAN, 1, 1, L }, - { "MB_A_PCI1", IRQ_MB_A_PCI1(0), IRQ_MB_A_PCI1_NUM, 1, L }, - { "MB_A_PCI2", IRQ_MB_A_PCI2(0), IRQ_MB_A_PCI2_NUM, 1, L }, - { "MB_A_EXT", IRQ_MB_A_EXT(0), IRQ_MB_A_EXT_NUM, 1, L }, - { "MB_A_USB_OC",IRQ_MB_A_USB_OC(0), IRQ_MB_A_USB_OC_NUM, 1, L }, - { "MB_A_PCMCIA_OC",IRQ_MB_A_PCMCIA_OC, 1, 1, L }, -#endif - { 0 } -}; -#define NUM_GBUS_IRQ_INITS (ARRAY_SIZE(gbus_irq_inits) - 1) - -static struct hw_interrupt_type gbus_hw_itypes[NUM_GBUS_IRQ_INITS]; - -#endif /* CONFIG_RTE_GBUS_INT */ - - -void __init rte_cb_init_irqs (void) -{ -#ifdef CONFIG_RTE_GBUS_INT - gbus_int_init_irqs (); - gbus_int_init_irq_types (gbus_irq_inits, gbus_hw_itypes); -#endif /* CONFIG_RTE_GBUS_INT */ -} diff --git a/arch/v850/kernel/rte_cb_leds.c b/arch/v850/kernel/rte_cb_leds.c deleted file mode 100644 index aa47ab1dcd87..000000000000 --- a/arch/v850/kernel/rte_cb_leds.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * include/asm-v850/rte_cb_leds.c -- Midas lab RTE-CB board LED device support - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include - -#include - -#define LEDS_MINOR 169 /* Minor device number, using misc major. */ - -/* The actual LED hardware is write-only, so we hold the contents here too. */ -static unsigned char leds_image[LED_NUM_DIGITS] = { 0 }; - -/* Spinlock protecting the above leds. */ -static DEFINE_SPINLOCK(leds_lock); - -/* Common body of LED read/write functions, checks POS and LEN for - correctness, declares a variable using IMG_DECL, initialized pointing at - the POS position in the LED image buffer, and and iterates COPY_EXPR - until BUF is equal to the last buffer position; finally, sets LEN to be - the amount actually copied. IMG should be a variable declaration - (without an initializer or a terminating semicolon); POS, BUF, and LEN - should all be simple variables. */ -#define DO_LED_COPY(img_decl, pos, buf, len, copy_expr) \ -do { \ - if (pos > LED_NUM_DIGITS) \ - len = 0; \ - else { \ - if (pos + len > LED_NUM_DIGITS) \ - len = LED_NUM_DIGITS - pos; \ - \ - if (len > 0) { \ - unsigned long _flags; \ - const char *_end = buf + len; \ - img_decl = &leds_image[pos]; \ - \ - spin_lock_irqsave (leds_lock, _flags); \ - do \ - (copy_expr); \ - while (buf != _end); \ - spin_unlock_irqrestore (leds_lock, _flags); \ - } \ - } \ -} while (0) - -/* Read LEN bytes from LEDs at position POS, into BUF. - Returns actual amount read. */ -unsigned read_leds (unsigned pos, char *buf, unsigned len) -{ - DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++); - return len; -} - -/* Write LEN bytes to LEDs at position POS, from BUF. - Returns actual amount written. */ -unsigned write_leds (unsigned pos, const char *buf, unsigned len) -{ - /* We write the actual LED values backwards, because - increasing memory addresses reflect LEDs right-to-left. */ - volatile char *led = &LED (LED_NUM_DIGITS - pos - 1); - /* We invert the value written to the hardware, because 1 = off, - and 0 = on. */ - DO_LED_COPY (char *img, pos, buf, len, - *led-- = 0xFF ^ (*img++ = *buf++)); - return len; -} - - -/* Device functions. */ - -static ssize_t leds_dev_read (struct file *file, char *buf, size_t len, - loff_t *pos) -{ - char temp_buf[LED_NUM_DIGITS]; - len = read_leds (*pos, temp_buf, len); - if (copy_to_user (buf, temp_buf, len)) - return -EFAULT; - *pos += len; - return len; -} - -static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len, - loff_t *pos) -{ - char temp_buf[LED_NUM_DIGITS]; - if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS))) - return -EFAULT; - len = write_leds (*pos, temp_buf, len); - *pos += len; - return len; -} - -static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence) -{ - if (whence == 1) - offs += file->f_pos; /* relative */ - else if (whence == 2) - offs += LED_NUM_DIGITS; /* end-relative */ - - if (offs < 0 || offs > LED_NUM_DIGITS) - return -EINVAL; - - file->f_pos = offs; - - return 0; -} - -static const struct file_operations leds_fops = { - .read = leds_dev_read, - .write = leds_dev_write, - .llseek = leds_dev_lseek -}; - -static struct miscdevice leds_miscdev = { - .name = "leds", - .minor = LEDS_MINOR, - .fops = &leds_fops -}; - -int __init leds_dev_init (void) -{ - return misc_register (&leds_miscdev); -} - -__initcall (leds_dev_init); diff --git a/arch/v850/kernel/rte_cb_multi.c b/arch/v850/kernel/rte_cb_multi.c deleted file mode 100644 index 963d55ab34cc..000000000000 --- a/arch/v850/kernel/rte_cb_multi.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * include/asm-v850/rte_multi.c -- Support for Multi debugger monitor ROM - * on Midas lab RTE-CB series of evaluation boards - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include - -#include - -#define IRQ_ADDR(irq) (0x80 + (irq) * 0x10) - -/* A table of which interrupt vectors to install, since blindly - installing all of them makes the debugger stop working. This is a - list of offsets in the interrupt vector area; each entry means to - copy that particular 16-byte vector. An entry less than zero ends - the table. */ -static long multi_intv_install_table[] = { - /* Trap vectors */ - 0x40, 0x50, - -#ifdef CONFIG_RTE_CB_MULTI_DBTRAP - /* Illegal insn / dbtrap. These are used by multi, so only handle - them if configured to do so. */ - 0x60, -#endif - - /* GINT1 - GINT3 (note, not GINT0!) */ - IRQ_ADDR (IRQ_GINT(1)), - IRQ_ADDR (IRQ_GINT(2)), - IRQ_ADDR (IRQ_GINT(3)), - - /* Timer D interrupts (up to 4 timers) */ - IRQ_ADDR (IRQ_INTCMD(0)), -#if IRQ_INTCMD_NUM > 1 - IRQ_ADDR (IRQ_INTCMD(1)), -#if IRQ_INTCMD_NUM > 2 - IRQ_ADDR (IRQ_INTCMD(2)), -#if IRQ_INTCMD_NUM > 3 - IRQ_ADDR (IRQ_INTCMD(3)), -#endif -#endif -#endif - - /* UART interrupts (up to 3 channels) */ - IRQ_ADDR (IRQ_INTSER (0)), /* err */ - IRQ_ADDR (IRQ_INTSR (0)), /* rx */ - IRQ_ADDR (IRQ_INTST (0)), /* tx */ -#if IRQ_INTSR_NUM > 1 - IRQ_ADDR (IRQ_INTSER (1)), /* err */ - IRQ_ADDR (IRQ_INTSR (1)), /* rx */ - IRQ_ADDR (IRQ_INTST (1)), /* tx */ -#if IRQ_INTSR_NUM > 2 - IRQ_ADDR (IRQ_INTSER (2)), /* err */ - IRQ_ADDR (IRQ_INTSR (2)), /* rx */ - IRQ_ADDR (IRQ_INTST (2)), /* tx */ -#endif -#endif - - -1 -}; - -/* Early initialization for kernel using Multi debugger ROM monitor. */ -void __init multi_init (void) -{ - /* We're using the Multi debugger monitor, so we have to install - the interrupt vectors. The monitor doesn't allow them to be - initially downloaded into their final destination because - it's in the monitor's scratch-RAM area. Unfortunately, Multi - also doesn't deal correctly with ELF sections where the LMA - and VMA differ -- it just ignores the LMA -- so we can't use - that feature to work around the problem. What we do instead - is just put the interrupt vectors into a normal section, and - do the necessary copying and relocation here. Since the - interrupt vector basically only contains `jr' instructions - and no-ops, it's not that hard. */ - extern unsigned long _intv_load_start, _intv_start; - register unsigned long *src = &_intv_load_start; - register unsigned long *dst = (unsigned long *)INTV_BASE; - register unsigned long jr_fixup = (char *)&_intv_start - (char *)dst; - register long *ii; - - /* Copy interrupt vectors as instructed by multi_intv_install_table. */ - for (ii = multi_intv_install_table; *ii >= 0; ii++) { - /* Copy 16-byte interrupt vector at offset *ii. */ - int boffs; - for (boffs = 0; boffs < 0x10; boffs += sizeof *src) { - /* Copy a single word, fixing up the jump offs - if it's a `jr' instruction. */ - int woffs = (*ii + boffs) / sizeof *src; - unsigned long word = src[woffs]; - - if ((word & 0xFC0) == 0x780) { - /* A `jr' insn, fix up its offset (and yes, the - weird half-word swapping is intentional). */ - unsigned short hi = word & 0xFFFF; - unsigned short lo = word >> 16; - unsigned long udisp22 - = lo + ((hi & 0x3F) << 16); - long disp22 = (long)(udisp22 << 10) >> 10; - - disp22 += jr_fixup; - - hi = ((disp22 >> 16) & 0x3F) | 0x780; - lo = disp22 & 0xFFFF; - - word = hi + (lo << 16); - } - - dst[woffs] = word; - } - } -} diff --git a/arch/v850/kernel/rte_ma1_cb-rom.ld b/arch/v850/kernel/rte_ma1_cb-rom.ld deleted file mode 100644 index 87b618f8253b..000000000000 --- a/arch/v850/kernel/rte_ma1_cb-rom.ld +++ /dev/null @@ -1,14 +0,0 @@ -/* Linker script for the Midas labs RTE-V850E/MA1-CB evaluation board - (CONFIG_RTE_CB_MA1), with kernel in ROM. */ - -MEMORY { - ROM : ORIGIN = 0x00000000, LENGTH = 0x00100000 - /* 1MB of SRAM. This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 32MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - ROMK_SECTIONS(ROM, SRAM) -} diff --git a/arch/v850/kernel/rte_ma1_cb.c b/arch/v850/kernel/rte_ma1_cb.c deleted file mode 100644 index 08abf3d5f8df..000000000000 --- a/arch/v850/kernel/rte_ma1_cb.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * arch/v850/kernel/rte_ma1_cb.c -- Midas labs RTE-V850E/MA1-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mach.h" - - -/* SRAM and SDRAM are almost contiguous (with a small hole in between; - see mach_reserve_bootmem for details), so just use both as one big area. */ -#define RAM_START SRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - - -void __init mach_early_init (void) -{ - rte_cb_early_init (); -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void __init mach_reserve_bootmem () -{ -#ifdef CONFIG_RTE_CB_MULTI - /* Prevent the kernel from touching the monitor's scratch RAM. */ - reserve_bootmem(MON_SCRATCH_ADDR, MON_SCRATCH_SIZE, - BOOTMEM_DEFAULT); -#endif - - /* The space between SRAM and SDRAM is filled with duplicate - images of SRAM. Prevent the kernel from using them. */ - reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), - BOOTMEM_DEFAULT); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Called before configuring an on-chip UART. */ -void rte_ma1_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud) -{ - /* The RTE-MA1-CB connects some general-purpose I/O pins on the - CPU to the RTS/CTS lines of UART 0's serial connection. - I/O pins P42 and P43 are RTS and CTS respectively. */ - if (chan == 0) { - /* Put P42 & P43 in I/O port mode. */ - MA_PORT4_PMC &= ~0xC; - /* Make P42 an output, and P43 an input. */ - MA_PORT4_PM = (MA_PORT4_PM & ~0xC) | 0x8; - } - - /* Do pre-configuration for the actual UART. */ - ma_uart_pre_configure (chan, cflags, baud); -} - -void __init mach_init_irqs (void) -{ - unsigned tc; - - /* Initialize interrupts. */ - ma_init_irqs (); - rte_cb_init_irqs (); - - /* Use falling-edge-sensitivity for interrupts . */ - V850E_TIMER_C_SESC (0) &= ~0xC; - V850E_TIMER_C_SESC (1) &= ~0xF; - - /* INTP000-INTP011 are shared with `Timer C', so we have to set - up Timer C to pass them through as raw interrupts. */ - for (tc = 0; tc < 2; tc++) - /* Turn on the timer. */ - V850E_TIMER_C_TMCC0 (tc) |= V850E_TIMER_C_TMCC0_CAE; - - /* Make sure the relevant port0/port1 pins are assigned - interrupt duty. We used INTP001-INTP011 (don't screw with - INTP000 because the monitor uses it). */ - MA_PORT0_PMC |= 0x4; /* P02 (INTP001) in IRQ mode. */ - MA_PORT1_PMC |= 0x6; /* P11 (INTP010) & P12 (INTP011) in IRQ mode.*/ -} diff --git a/arch/v850/kernel/rte_ma1_cb.ld b/arch/v850/kernel/rte_ma1_cb.ld deleted file mode 100644 index c8e16d16be41..000000000000 --- a/arch/v850/kernel/rte_ma1_cb.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* Linker script for the Midas labs RTE-V850E/MA1-CB evaluation board - (CONFIG_RTE_CB_MA1), with kernel in SDRAM, under Multi debugger. */ - -MEMORY { - /* 1MB of SRAM; we can't use the last 32KB, because it's used by - the monitor scratch-RAM. This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = (SRAM_SIZE - MON_SCRATCH_SIZE) - /* Monitor scratch RAM; only the interrupt vectors should go here. */ - MRAM : ORIGIN = MON_SCRATCH_ADDR, LENGTH = MON_SCRATCH_SIZE - /* 32MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#ifdef CONFIG_RTE_CB_MA1_KSRAM -# define KRAM SRAM -#else -# define KRAM SDRAM -#endif - -SECTIONS { - /* We can't use RAMK_KRAM_CONTENTS because that puts the whole - kernel in a single ELF segment, and the Multi debugger (which - we use to load the kernel) appears to have bizarre problems - dealing with it. */ - - .text : { - __kram_start = . ; - TEXT_CONTENTS - } > KRAM - - .data : { - DATA_CONTENTS - BSS_CONTENTS - RAMK_INIT_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - - /* The address at which the interrupt vectors are initially - loaded by the loader. We can't load the interrupt vectors - directly into their target location, because the monitor - ROM for the GHS Multi debugger barfs if we try. - Unfortunately, Multi also doesn't deal correctly with ELF - sections where the LMA and VMA differ (it just ignores the - LMA), so we can't use that feature to work around the - problem! What we do instead is just put the interrupt - vectors into a normal section, and have the - `mach_early_init' function for Midas boards do the - necessary copying and relocation at runtime (this section - basically only contains `jr' instructions, so it's not - that hard). */ - . = ALIGN (0x10) ; - __intv_load_start = . ; - INTV_CONTENTS - } > KRAM - - .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c deleted file mode 100644 index 687e367d8b64..000000000000 --- a/arch/v850/kernel/rte_mb_a_pci.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * arch/v850/kernel/mb_a_pci.c -- PCI support for Midas lab RTE-MOTHER-A board - * - * Copyright (C) 2001,02,03,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include - -/* __nomods_init is like __devinit, but is a no-op when modules are enabled. - This is used by some routines that can be called either during boot - or by a module. */ -#ifdef CONFIG_MODULES -#define __nomods_init /*nothing*/ -#else -#define __nomods_init __devinit -#endif - -/* PCI devices on the Mother-A board can only do DMA to/from the MB SRAM - (the RTE-V850E/MA1-CB cpu board doesn't support PCI access to - CPU-board memory), and since linux DMA buffers are allocated in - normal kernel memory, we basically have to copy DMA blocks around - (this is like a `bounce buffer'). When a DMA block is `mapped', we - allocate an identically sized block in MB SRAM, and if we're doing - output to the device, copy the CPU-memory block to the MB-SRAM block. - When an active block is `unmapped', we will copy the block back to - CPU memory if necessary, and then deallocate the MB SRAM block. - Ack. */ - -/* Where the motherboard SRAM is in the PCI-bus address space (the - first 512K of it is also mapped at PCI address 0). */ -#define PCI_MB_SRAM_ADDR 0x800000 - -/* Convert CPU-view MB SRAM address to/from PCI-view addresses of the - same memory. */ -#define MB_SRAM_TO_PCI(mb_sram_addr) \ - ((dma_addr_t)mb_sram_addr - MB_A_SRAM_ADDR + PCI_MB_SRAM_ADDR) -#define PCI_TO_MB_SRAM(pci_addr) \ - (void *)(pci_addr - PCI_MB_SRAM_ADDR + MB_A_SRAM_ADDR) - -static void pcibios_assign_resources (void); - -struct mb_pci_dev_irq { - unsigned dev; /* PCI device number */ - unsigned irq_base; /* First IRQ */ - unsigned query_pin; /* True if we should read the device's - Interrupt Pin info, and allocate - interrupt IRQ_BASE + PIN. */ -}; - -/* PCI interrupts are mapped statically to GBUS interrupts. */ -static struct mb_pci_dev_irq mb_pci_dev_irqs[] = { - /* Motherboard SB82558 ethernet controller */ - { 10, IRQ_MB_A_LAN, 0 }, - /* PCI slot 1 */ - { 8, IRQ_MB_A_PCI1(0), 1 }, - /* PCI slot 2 */ - { 9, IRQ_MB_A_PCI2(0), 1 } -}; -#define NUM_MB_PCI_DEV_IRQS ARRAY_SIZE(mb_pci_dev_irqs) - - -/* PCI configuration primitives. */ - -#define CONFIG_DMCFGA(bus, devfn, offs) \ - (0x80000000 \ - | ((offs) & ~0x3) \ - | ((devfn) << 8) \ - | ((bus)->number << 16)) - -static int -mb_pci_read (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 *rval) -{ - u32 addr; - int flags; - - local_irq_save (flags); - - MB_A_PCI_PCICR = 0x7; - MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs); - - addr = MB_A_PCI_IO_ADDR + (offs & 0x3); - - switch (size) { - case 1: *rval = *(volatile u8 *)addr; break; - case 2: *rval = *(volatile u16 *)addr; break; - case 4: *rval = *(volatile u32 *)addr; break; - } - - if (MB_A_PCI_PCISR & 0x2000) { - MB_A_PCI_PCISR = 0x2000; - *rval = ~0; - } - - MB_A_PCI_DMCFGA = 0; - - local_irq_restore (flags); - - return PCIBIOS_SUCCESSFUL; -} - -static int -mb_pci_write (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 val) -{ - u32 addr; - int flags; - - local_irq_save (flags); - - MB_A_PCI_PCICR = 0x7; - MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs); - - addr = MB_A_PCI_IO_ADDR + (offs & 0x3); - - switch (size) { - case 1: *(volatile u8 *)addr = val; break; - case 2: *(volatile u16 *)addr = val; break; - case 4: *(volatile u32 *)addr = val; break; - } - - if (MB_A_PCI_PCISR & 0x2000) - MB_A_PCI_PCISR = 0x2000; - - MB_A_PCI_DMCFGA = 0; - - local_irq_restore (flags); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops mb_pci_config_ops = { - .read = mb_pci_read, - .write = mb_pci_write, -}; - - -/* PCI Initialization. */ - -static struct pci_bus *mb_pci_bus = 0; - -/* Do initial PCI setup. */ -static int __devinit pcibios_init (void) -{ - u32 id = MB_A_PCI_PCIHIDR; - u16 vendor = id & 0xFFFF; - u16 device = (id >> 16) & 0xFFFF; - - if (vendor == PCI_VENDOR_ID_PLX && device == PCI_DEVICE_ID_PLX_9080) { - printk (KERN_INFO - "PCI: PLX Technology PCI9080 HOST/PCI bridge\n"); - - MB_A_PCI_PCICR = 0x147; - - MB_A_PCI_PCIBAR0 = 0x007FFF00; - MB_A_PCI_PCIBAR1 = 0x0000FF00; - MB_A_PCI_PCIBAR2 = 0x00800000; - - MB_A_PCI_PCILTR = 0x20; - - MB_A_PCI_PCIPBAM |= 0x3; - - MB_A_PCI_PCISR = ~0; /* Clear errors. */ - - /* Reprogram the motherboard's IO/config address space, - as we don't support the GCS7 address space that the - default uses. */ - - /* Significant address bits used for decoding PCI GCS5 space - accesses. */ - MB_A_PCI_DMRR = ~(MB_A_PCI_MEM_SIZE - 1); - - /* I don't understand this, but the SolutionGear example code - uses such an offset, and it doesn't work without it. XXX */ -#if GCS5_SIZE == 0x00800000 -#define GCS5_CFG_OFFS 0x00800000 -#else -#define GCS5_CFG_OFFS 0 -#endif - - /* Address bit values for matching. Note that we have to give - the address from the motherboard's point of view, which is - different than the CPU's. */ - /* PCI memory space. */ - MB_A_PCI_DMLBAM = GCS5_CFG_OFFS + 0x0; - /* PCI I/O space. */ - MB_A_PCI_DMLBAI = - GCS5_CFG_OFFS + (MB_A_PCI_IO_ADDR - GCS5_ADDR); - - mb_pci_bus = pci_scan_bus (0, &mb_pci_config_ops, 0); - - pcibios_assign_resources (); - } else - printk (KERN_ERR "PCI: HOST/PCI bridge not found\n"); - - return 0; -} - -subsys_initcall (pcibios_init); - -char __devinit *pcibios_setup (char *option) -{ - /* Don't handle any options. */ - return option; -} - - -int __nomods_init pcibios_enable_device (struct pci_dev *dev, int mask) -{ - u16 cmd, old_cmd; - int idx; - struct resource *r; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - old_cmd = cmd; - for (idx = 0; idx < 6; idx++) { - r = &dev->resource[idx]; - if (!r->start && r->end) { - printk(KERN_ERR "PCI: Device %s not available because " - "of resource collisions\n", pci_name(dev)); - return -EINVAL; - } - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - if (cmd != old_cmd) { - printk("PCI: Enabling device %s (%04x -> %04x)\n", - pci_name(dev), old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - return 0; -} - - -/* Resource allocation. */ -static void __devinit pcibios_assign_resources (void) -{ - struct pci_dev *dev = NULL; - struct resource *r; - - for_each_pci_dev(dev) { - unsigned di_num; - unsigned class = dev->class >> 8; - - if (class && class != PCI_CLASS_BRIDGE_HOST) { - unsigned r_num; - for(r_num = 0; r_num < 6; r_num++) { - r = &dev->resource[r_num]; - if (!r->start && r->end) - pci_assign_resource (dev, r_num); - } - } - - /* Assign interrupts. */ - for (di_num = 0; di_num < NUM_MB_PCI_DEV_IRQS; di_num++) { - struct mb_pci_dev_irq *di = &mb_pci_dev_irqs[di_num]; - - if (di->dev == PCI_SLOT (dev->devfn)) { - unsigned irq = di->irq_base; - - if (di->query_pin) { - /* Find out which interrupt pin - this device uses (each PCI - slot has 4). */ - u8 irq_pin; - - pci_read_config_byte (dev, - PCI_INTERRUPT_PIN, - &irq_pin); - - if (irq_pin == 0) - /* Doesn't use interrupts. */ - continue; - else - irq += irq_pin - 1; - } - - pcibios_update_irq (dev, irq); - } - } - } -} - -void __devinit pcibios_update_irq (struct pci_dev *dev, int irq) -{ - dev->irq = irq; - pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq); -} - -void __devinit -pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - unsigned long offset = 0; - - if (res->flags & IORESOURCE_IO) { - offset = MB_A_PCI_IO_ADDR; - } else if (res->flags & IORESOURCE_MEM) { - offset = MB_A_PCI_MEM_ADDR; - } - - region->start = res->start - offset; - region->end = res->end - offset; -} - - -/* Stubs for things we don't use. */ - -/* Called after each bus is probed, but before its children are examined. */ -void pcibios_fixup_bus(struct pci_bus *b) -{ -} - -void -pcibios_align_resource (void *data, struct resource *res, - resource_size_t size, resource_size_t align) -{ -} - -void pcibios_set_master (struct pci_dev *dev) -{ -} - - -/* Mother-A SRAM memory allocation. This is a simple first-fit allocator. */ - -/* A memory free-list node. */ -struct mb_sram_free_area { - void *mem; - unsigned long size; - struct mb_sram_free_area *next; -}; - -/* The tail of the free-list, which starts out containing all the SRAM. */ -static struct mb_sram_free_area mb_sram_free_tail = { - (void *)MB_A_SRAM_ADDR, MB_A_SRAM_SIZE, 0 -}; - -/* The free-list. */ -static struct mb_sram_free_area *mb_sram_free_areas = &mb_sram_free_tail; - -/* The free-list of free free-list nodes. (:-) */ -static struct mb_sram_free_area *mb_sram_free_free_areas = 0; - -/* Spinlock protecting the above globals. */ -static DEFINE_SPINLOCK(mb_sram_lock); - -/* Allocate a memory block at least SIZE bytes long in the Mother-A SRAM - space. */ -static void *alloc_mb_sram (size_t size) -{ - struct mb_sram_free_area *prev, *fa; - unsigned long flags; - void *mem = 0; - - spin_lock_irqsave (mb_sram_lock, flags); - - /* Look for a free area that can contain SIZE bytes. */ - for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next) - if (fa->size >= size) { - /* Found one! */ - mem = fa->mem; - - if (fa->size == size) { - /* In fact, it fits exactly, so remove - this node from the free-list. */ - if (prev) - prev->next = fa->next; - else - mb_sram_free_areas = fa->next; - /* Put it on the free-list-entry-free-list. */ - fa->next = mb_sram_free_free_areas; - mb_sram_free_free_areas = fa; - } else { - /* FA is bigger than SIZE, so just - reduce its size to account for this - allocation. */ - fa->mem += size; - fa->size -= size; - } - - break; - } - - spin_unlock_irqrestore (mb_sram_lock, flags); - - return mem; -} - -/* Return the memory area MEM of size SIZE to the MB SRAM free pool. */ -static void free_mb_sram (void *mem, size_t size) -{ - struct mb_sram_free_area *prev, *fa, *new_fa; - unsigned long flags; - void *end = mem + size; - - spin_lock_irqsave (mb_sram_lock, flags); - - retry: - /* Find an adjacent free-list entry. */ - for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next) - if (fa->mem == end) { - /* FA is just after MEM, grow down to encompass it. */ - fa->mem = mem; - fa->size += size; - goto done; - } else if (fa->mem + fa->size == mem) { - struct mb_sram_free_area *next_fa = fa->next; - - /* FA is just before MEM, expand to encompass it. */ - fa->size += size; - - /* See if FA can now be merged with its successor. */ - if (next_fa && fa->mem + fa->size == next_fa->mem) { - /* Yup; merge NEXT_FA's info into FA. */ - fa->size += next_fa->size; - fa->next = next_fa->next; - /* Free NEXT_FA. */ - next_fa->next = mb_sram_free_free_areas; - mb_sram_free_free_areas = next_fa; - } - goto done; - } else if (fa->mem > mem) - /* We've reached the right spot in the free-list - without finding an adjacent free-area, so add - a new free area to hold mem. */ - break; - - /* Make a new free-list entry. */ - - /* First, get a free-list entry. */ - if (! mb_sram_free_free_areas) { - /* There are none, so make some. */ - void *block; - size_t block_size = sizeof (struct mb_sram_free_area) * 8; - - /* Don't hold the lock while calling kmalloc (I'm not - sure whether it would be a problem, since we use - GFP_ATOMIC, but it makes me nervous). */ - spin_unlock_irqrestore (mb_sram_lock, flags); - - block = kmalloc (block_size, GFP_ATOMIC); - if (! block) - panic ("free_mb_sram: can't allocate free-list entry"); - - /* Now get the lock back. */ - spin_lock_irqsave (mb_sram_lock, flags); - - /* Add the new free free-list entries. */ - while (block_size > 0) { - struct mb_sram_free_area *nfa = block; - nfa->next = mb_sram_free_free_areas; - mb_sram_free_free_areas = nfa; - block += sizeof *nfa; - block_size -= sizeof *nfa; - } - - /* Since we dropped the lock to call kmalloc, the - free-list could have changed, so retry from the - beginning. */ - goto retry; - } - - /* Remove NEW_FA from the free-list of free-list entries. */ - new_fa = mb_sram_free_free_areas; - mb_sram_free_free_areas = new_fa->next; - - /* NEW_FA initially holds only MEM. */ - new_fa->mem = mem; - new_fa->size = size; - - /* Insert NEW_FA in the free-list between PREV and FA. */ - new_fa->next = fa; - if (prev) - prev->next = new_fa; - else - mb_sram_free_areas = new_fa; - - done: - spin_unlock_irqrestore (mb_sram_lock, flags); -} - - -/* Maintainence of CPU -> Mother-A DMA mappings. */ - -struct dma_mapping { - void *cpu_addr; - void *mb_sram_addr; - size_t size; - struct dma_mapping *next; -}; - -/* A list of mappings from CPU addresses to MB SRAM addresses for active - DMA blocks (that have been `granted' to the PCI device). */ -static struct dma_mapping *active_dma_mappings = 0; - -/* A list of free mapping objects. */ -static struct dma_mapping *free_dma_mappings = 0; - -/* Spinlock protecting the above globals. */ -static DEFINE_SPINLOCK(dma_mappings_lock); - -static struct dma_mapping *new_dma_mapping (size_t size) -{ - unsigned long flags; - struct dma_mapping *mapping; - void *mb_sram_block = alloc_mb_sram (size); - - if (! mb_sram_block) - return 0; - - spin_lock_irqsave (dma_mappings_lock, flags); - - if (! free_dma_mappings) { - /* We're out of mapping structures, make more. */ - void *mblock; - size_t mblock_size = sizeof (struct dma_mapping) * 8; - - /* Don't hold the lock while calling kmalloc (I'm not - sure whether it would be a problem, since we use - GFP_ATOMIC, but it makes me nervous). */ - spin_unlock_irqrestore (dma_mappings_lock, flags); - - mblock = kmalloc (mblock_size, GFP_ATOMIC); - if (! mblock) { - free_mb_sram (mb_sram_block, size); - return 0; - } - - /* Get the lock back. */ - spin_lock_irqsave (dma_mappings_lock, flags); - - /* Add the new mapping structures to the free-list. */ - while (mblock_size > 0) { - struct dma_mapping *fm = mblock; - fm->next = free_dma_mappings; - free_dma_mappings = fm; - mblock += sizeof *fm; - mblock_size -= sizeof *fm; - } - } - - /* Get a mapping struct from the freelist. */ - mapping = free_dma_mappings; - free_dma_mappings = mapping->next; - - /* Initialize the mapping. Other fields should be filled in by - caller. */ - mapping->mb_sram_addr = mb_sram_block; - mapping->size = size; - - /* Add it to the list of active mappings. */ - mapping->next = active_dma_mappings; - active_dma_mappings = mapping; - - spin_unlock_irqrestore (dma_mappings_lock, flags); - - return mapping; -} - -static struct dma_mapping *find_dma_mapping (void *mb_sram_addr) -{ - unsigned long flags; - struct dma_mapping *mapping; - - spin_lock_irqsave (dma_mappings_lock, flags); - - for (mapping = active_dma_mappings; mapping; mapping = mapping->next) - if (mapping->mb_sram_addr == mb_sram_addr) { - spin_unlock_irqrestore (dma_mappings_lock, flags); - return mapping; - } - - panic ("find_dma_mapping: unmapped PCI DMA addr 0x%x", - MB_SRAM_TO_PCI (mb_sram_addr)); -} - -static struct dma_mapping *deactivate_dma_mapping (void *mb_sram_addr) -{ - unsigned long flags; - struct dma_mapping *mapping, *prev; - - spin_lock_irqsave (dma_mappings_lock, flags); - - for (prev = 0, mapping = active_dma_mappings; - mapping; - prev = mapping, mapping = mapping->next) - { - if (mapping->mb_sram_addr == mb_sram_addr) { - /* This is the MAPPING; deactivate it. */ - if (prev) - prev->next = mapping->next; - else - active_dma_mappings = mapping->next; - - spin_unlock_irqrestore (dma_mappings_lock, flags); - - return mapping; - } - } - - panic ("deactivate_dma_mapping: unmapped PCI DMA addr 0x%x", - MB_SRAM_TO_PCI (mb_sram_addr)); -} - -/* Return MAPPING to the freelist. */ -static inline void -free_dma_mapping (struct dma_mapping *mapping) -{ - unsigned long flags; - - free_mb_sram (mapping->mb_sram_addr, mapping->size); - - spin_lock_irqsave (dma_mappings_lock, flags); - - mapping->next = free_dma_mappings; - free_dma_mappings = mapping; - - spin_unlock_irqrestore (dma_mappings_lock, flags); -} - - -/* Single PCI DMA mappings. */ - -/* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The - 32-bit PCI bus mastering address to use is returned. the device owns - this memory until either pci_unmap_single or pci_dma_sync_single is - performed. */ -dma_addr_t -pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir) -{ - struct dma_mapping *mapping = new_dma_mapping (size); - - if (! mapping) - return 0; - - mapping->cpu_addr = cpu_addr; - - if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_TODEVICE) - memcpy (mapping->mb_sram_addr, cpu_addr, size); - - return MB_SRAM_TO_PCI (mapping->mb_sram_addr); -} - -/* Return to the CPU the PCI DMA memory block previously `granted' to - PDEV, at DMA_ADDR. */ -void pci_unmap_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir) -{ - void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); - struct dma_mapping *mapping = deactivate_dma_mapping (mb_sram_addr); - - if (size != mapping->size) - panic ("pci_unmap_single: size (%d) doesn't match" - " size of mapping at PCI DMA addr 0x%x (%d)\n", - size, dma_addr, mapping->size); - - /* Copy back the DMA'd contents if necessary. */ - if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_FROMDEVICE) - memcpy (mapping->cpu_addr, mb_sram_addr, size); - - /* Return mapping to the freelist. */ - free_dma_mapping (mapping); -} - -/* Make physical memory consistent for a single streaming mode DMA - translation after a transfer. - - If you perform a pci_map_single() but wish to interrogate the - buffer using the cpu, yet do not wish to teardown the PCI dma - mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, you must first - perform a pci_dma_sync_for_device, and then the device again owns - the buffer. */ -void -pci_dma_sync_single_for_cpu (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir) -{ - void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); - struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr); - - /* Synchronize the DMA buffer with the CPU buffer if necessary. */ - if (dir == PCI_DMA_FROMDEVICE) - memcpy (mapping->cpu_addr, mb_sram_addr, size); - else if (dir == PCI_DMA_TODEVICE) - ; /* nothing to do */ - else - panic("pci_dma_sync_single: unsupported sync dir: %d", dir); -} - -void -pci_dma_sync_single_for_device (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir) -{ - void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); - struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr); - - /* Synchronize the DMA buffer with the CPU buffer if necessary. */ - if (dir == PCI_DMA_FROMDEVICE) - ; /* nothing to do */ - else if (dir == PCI_DMA_TODEVICE) - memcpy (mb_sram_addr, mapping->cpu_addr, size); - else - panic("pci_dma_sync_single: unsupported sync dir: %d", dir); -} - - -/* Scatter-gather PCI DMA mappings. */ - -/* Do multiple DMA mappings at once. */ -int -pci_map_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, int dir) -{ - BUG (); - return 0; -} - -/* Unmap multiple DMA mappings at once. */ -void -pci_unmap_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len,int dir) -{ - BUG (); -} - -/* Make physical memory consistent for a set of streaming mode DMA - translations after a transfer. The same as pci_dma_sync_single_* but - for a scatter-gather list, same rules and usage. */ - -void -pci_dma_sync_sg_for_cpu (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir) -{ - BUG (); -} - -void -pci_dma_sync_sg_for_device (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir) -{ - BUG (); -} - - -/* PCI mem mapping. */ - -/* Allocate and map kernel buffer using consistent mode DMA for PCI - device. Returns non-NULL cpu-view pointer to the buffer if - successful and sets *DMA_ADDR to the pci side dma address as well, - else DMA_ADDR is undefined. */ -void * -pci_alloc_consistent (struct pci_dev *pdev, size_t size, dma_addr_t *dma_addr) -{ - void *mb_sram_mem = alloc_mb_sram (size); - if (mb_sram_mem) - *dma_addr = MB_SRAM_TO_PCI (mb_sram_mem); - return mb_sram_mem; -} - -/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must - be values that were returned from pci_alloc_consistent. SIZE must be - the same as what as passed into pci_alloc_consistent. References to - the memory and mappings associated with CPU_ADDR or DMA_ADDR past - this call are illegal. */ -void -pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr, - dma_addr_t dma_addr) -{ - void *mb_sram_mem = PCI_TO_MB_SRAM (dma_addr); - free_mb_sram (mb_sram_mem, size); -} - - -/* iomap/iomap */ - -void __iomem *pci_iomap (struct pci_dev *dev, int bar, unsigned long max) -{ - resource_size_t start = pci_resource_start (dev, bar); - resource_size_t len = pci_resource_len (dev, bar); - - if (!start || len == 0) - return 0; - - /* None of the ioremap functions actually do anything, other than - re-casting their argument, so don't bother differentiating them. */ - return ioremap (start, len); -} - -void pci_iounmap (struct pci_dev *dev, void __iomem *addr) -{ - /* nothing */ -} - - -/* symbol exports (for modules) */ - -EXPORT_SYMBOL (pci_map_single); -EXPORT_SYMBOL (pci_unmap_single); -EXPORT_SYMBOL (pci_alloc_consistent); -EXPORT_SYMBOL (pci_free_consistent); -EXPORT_SYMBOL (pci_dma_sync_single_for_cpu); -EXPORT_SYMBOL (pci_dma_sync_single_for_device); -EXPORT_SYMBOL (pci_iomap); -EXPORT_SYMBOL (pci_iounmap); diff --git a/arch/v850/kernel/rte_me2_cb.c b/arch/v850/kernel/rte_me2_cb.c deleted file mode 100644 index 46803d48dffe..000000000000 --- a/arch/v850/kernel/rte_me2_cb.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * arch/v850/kernel/rte_me2_cb.c -- Midas labs RTE-V850E/ME2-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mach.h" - -extern unsigned long *_intv_start; -extern unsigned long *_intv_end; - -/* LED access routines. */ -extern unsigned read_leds (int pos, char *buf, int len); -extern unsigned write_leds (int pos, const char *buf, int len); - - -/* SDRAM are almost contiguous (with a small hole in between; - see mach_reserve_bootmem for details), so just use both as one big area. */ -#define RAM_START SDRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Called before configuring an on-chip UART. */ -void rte_me2_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud) -{ - /* The RTE-V850E/ME2-CB connects some general-purpose I/O - pins on the CPU to the RTS/CTS lines of UARTB channel 0's - serial connection. - I/O pins P21 and P22 are RTS and CTS respectively. */ - if (chan == 0) { - /* Put P21 & P22 in I/O port mode. */ - ME2_PORT2_PMC &= ~0x6; - /* Make P21 and output, and P22 an input. */ - ME2_PORT2_PM = (ME2_PORT2_PM & ~0xC) | 0x4; - } - - me2_uart_pre_configure (chan, cflags, baud); -} - -void __init mach_init_irqs (void) -{ - /* Initialize interrupts. */ - me2_init_irqs (); - rte_me2_cb_init_irqs (); -} - -#ifdef CONFIG_ROM_KERNEL -/* Initialization for kernel in ROM. */ -static inline rom_kernel_init (void) -{ - /* If the kernel is in ROM, we have to copy any initialized data - from ROM into RAM. */ - extern unsigned long _data_load_start, _sdata, _edata; - register unsigned long *src = &_data_load_start; - register unsigned long *dst = &_sdata, *end = &_edata; - - while (dst != end) - *dst++ = *src++; -} -#endif /* CONFIG_ROM_KERNEL */ - -static void install_interrupt_vectors (void) -{ - unsigned long *p1, *p2; - - ME2_IRAMM = 0x03; /* V850E/ME2 iRAM write mode */ - - /* vector copy to iRAM */ - p1 = (unsigned long *)0; /* v85x vector start */ - p2 = (unsigned long *)&_intv_start; - while (p2 < (unsigned long *)&_intv_end) - *p1++ = *p2++; - - ME2_IRAMM = 0x00; /* V850E/ME2 iRAM read mode */ -} - -/* CompactFlash */ - -static void cf_power_on (void) -{ - /* CF card detected? */ - if (CB_CF_STS0 & 0x0030) - return; - - CB_CF_REG0 = 0x0002; /* reest on */ - mdelay (10); - CB_CF_REG0 = 0x0003; /* power on */ - mdelay (10); - CB_CF_REG0 = 0x0001; /* reset off */ - mdelay (10); -} - -static void cf_power_off (void) -{ - CB_CF_REG0 = 0x0003; /* power on */ - mdelay (10); - CB_CF_REG0 = 0x0002; /* reest on */ - mdelay (10); -} - -void __init mach_early_init (void) -{ - install_interrupt_vectors (); - - /* CS1 SDRAM instruction cache enable */ - v850e_cache_enable (0x04, 0x03, 0); - - rte_cb_early_init (); - - /* CompactFlash power on */ - cf_power_on (); - -#if defined (CONFIG_ROM_KERNEL) - rom_kernel_init (); -#endif -} - - -/* RTE-V850E/ME2-CB Programmable Interrupt Controller. */ - -static struct cb_pic_irq_init cb_pic_irq_inits[] = { - { "CB_EXTTM0", IRQ_CB_EXTTM0, 1, 1, 6 }, - { "CB_EXTSIO", IRQ_CB_EXTSIO, 1, 1, 6 }, - { "CB_TOVER", IRQ_CB_TOVER, 1, 1, 6 }, - { "CB_GINT0", IRQ_CB_GINT0, 1, 1, 6 }, - { "CB_USB", IRQ_CB_USB, 1, 1, 6 }, - { "CB_LANC", IRQ_CB_LANC, 1, 1, 6 }, - { "CB_USB_VBUS_ON", IRQ_CB_USB_VBUS_ON, 1, 1, 6 }, - { "CB_USB_VBUS_OFF", IRQ_CB_USB_VBUS_OFF, 1, 1, 6 }, - { "CB_EXTTM1", IRQ_CB_EXTTM1, 1, 1, 6 }, - { "CB_EXTTM2", IRQ_CB_EXTTM2, 1, 1, 6 }, - { 0 } -}; -#define NUM_CB_PIC_IRQ_INITS (ARRAY_SIZE(cb_pic_irq_inits) - 1) - -static struct hw_interrupt_type cb_pic_hw_itypes[NUM_CB_PIC_IRQ_INITS]; -static unsigned char cb_pic_active_irqs = 0; - -void __init rte_me2_cb_init_irqs (void) -{ - cb_pic_init_irq_types (cb_pic_irq_inits, cb_pic_hw_itypes); - - /* Initalize on board PIC1 (not PIC0) enable */ - CB_PIC_INT0M = 0x0000; - CB_PIC_INT1M = 0x0000; - CB_PIC_INTR = 0x0000; - CB_PIC_INTEN |= CB_PIC_INT1EN; - - ME2_PORT2_PMC |= 0x08; /* INTP23/SCK1 mode */ - ME2_PORT2_PFC &= ~0x08; /* INTP23 mode */ - ME2_INTR(2) &= ~0x08; /* INTP23 falling-edge detect */ - ME2_INTF(2) &= ~0x08; /* " */ - - rte_cb_init_irqs (); /* gbus &c */ -} - - -/* Enable interrupt handling for interrupt IRQ. */ -void cb_pic_enable_irq (unsigned irq) -{ - CB_PIC_INT1M |= 1 << (irq - CB_PIC_BASE_IRQ); -} - -void cb_pic_disable_irq (unsigned irq) -{ - CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ)); -} - -void cb_pic_shutdown_irq (unsigned irq) -{ - cb_pic_disable_irq (irq); - - if (--cb_pic_active_irqs == 0) - free_irq (IRQ_CB_PIC, 0); - - CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ)); -} - -static irqreturn_t cb_pic_handle_irq (int irq, void *dev_id, - struct pt_regs *regs) -{ - irqreturn_t rval = IRQ_NONE; - unsigned status = CB_PIC_INTR; - unsigned enable = CB_PIC_INT1M; - - /* Only pay attention to enabled interrupts. */ - status &= enable; - - CB_PIC_INTEN &= ~CB_PIC_INT1EN; - - if (status) { - unsigned mask = 1; - - irq = CB_PIC_BASE_IRQ; - do { - /* There's an active interrupt, find out which one, - and call its handler. */ - while (! (status & mask)) { - irq++; - mask <<= 1; - } - status &= ~mask; - - CB_PIC_INTR = mask; - - /* Recursively call handle_irq to handle it. */ - handle_irq (irq, regs); - rval = IRQ_HANDLED; - } while (status); - } - - CB_PIC_INTEN |= CB_PIC_INT1EN; - - return rval; -} - - -static void irq_nop (unsigned irq) { } - -static unsigned cb_pic_startup_irq (unsigned irq) -{ - int rval; - - if (cb_pic_active_irqs == 0) { - rval = request_irq (IRQ_CB_PIC, cb_pic_handle_irq, - IRQF_DISABLED, "cb_pic_handler", 0); - if (rval != 0) - return rval; - } - - cb_pic_active_irqs++; - - cb_pic_enable_irq (irq); - - return 0; -} - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -void __init cb_pic_init_irq_types (struct cb_pic_irq_init *inits, - struct hw_interrupt_type *hw_irq_types) -{ - struct cb_pic_irq_init *init; - for (init = inits; init->name; init++) { - struct hw_interrupt_type *hwit = hw_irq_types++; - - hwit->typename = init->name; - - hwit->startup = cb_pic_startup_irq; - hwit->shutdown = cb_pic_shutdown_irq; - hwit->enable = cb_pic_enable_irq; - hwit->disable = cb_pic_disable_irq; - hwit->ack = irq_nop; - hwit->end = irq_nop; - - /* Initialize kernel IRQ infrastructure for this interrupt. */ - init_irq_handlers(init->base, init->num, init->interval, hwit); - } -} diff --git a/arch/v850/kernel/rte_me2_cb.ld b/arch/v850/kernel/rte_me2_cb.ld deleted file mode 100644 index cf0766065ec6..000000000000 --- a/arch/v850/kernel/rte_me2_cb.ld +++ /dev/null @@ -1,30 +0,0 @@ -/* Linker script for the Midas labs RTE-V850E/ME2-CB evaluation board - (CONFIG_RTE_CB_ME2), with kernel in SDRAM. */ - -MEMORY { - /* 128Kbyte of IRAM */ - IRAM : ORIGIN = 0x00000000, LENGTH = 0x00020000 - - /* 32MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#define KRAM SDRAM - -SECTIONS { - .text : { - __kram_start = . ; - TEXT_CONTENTS - INTV_CONTENTS /* copy to iRAM (0x0-0x620) */ - } > KRAM - - .data : { - DATA_CONTENTS - BSS_CONTENTS - RAMK_INIT_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - } > KRAM - - .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/rte_nb85e_cb-multi.ld b/arch/v850/kernel/rte_nb85e_cb-multi.ld deleted file mode 100644 index de347b4fffac..000000000000 --- a/arch/v850/kernel/rte_nb85e_cb-multi.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* Linker script for the Midas labs RTE-NB85E-CB evaluation board - (CONFIG_RTE_CB_NB85E), with the Multi debugger ROM monitor . */ - -MEMORY { - /* 1MB of SRAM; we can't use the last 96KB, because it's used by - the monitor scratch-RAM. This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = (SRAM_SIZE - MON_SCRATCH_SIZE) - /* Monitor scratch RAM; only the interrupt vectors should go here. */ - MRAM : ORIGIN = MON_SCRATCH_ADDR, LENGTH = MON_SCRATCH_SIZE - /* 16MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#ifdef CONFIG_RTE_CB_NB85E_KSRAM -# define KRAM SRAM -#else -# define KRAM SDRAM -#endif - -SECTIONS { - /* We can't use RAMK_KRAM_CONTENTS because that puts the whole - kernel in a single ELF segment, and the Multi debugger (which - we use to load the kernel) appears to have bizarre problems - dealing with it. */ - - .text : { - __kram_start = . ; - TEXT_CONTENTS - } > KRAM - - .data : { - DATA_CONTENTS - BSS_CONTENTS - RAMK_INIT_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - - /* The address at which the interrupt vectors are initially - loaded by the loader. We can't load the interrupt vectors - directly into their target location, because the monitor - ROM for the GHS Multi debugger barfs if we try. - Unfortunately, Multi also doesn't deal correctly with ELF - sections where the LMA and VMA differ (it just ignores the - LMA), so we can't use that feature to work around the - problem! What we do instead is just put the interrupt - vectors into a normal section, and have the - `mach_early_init' function for Midas boards do the - necessary copying and relocation at runtime (this section - basically only contains `jr' instructions, so it's not - that hard). */ - . = ALIGN (0x10) ; - __intv_load_start = . ; - INTV_CONTENTS - } > KRAM - - .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/rte_nb85e_cb.c b/arch/v850/kernel/rte_nb85e_cb.c deleted file mode 100644 index b4a045da5d70..000000000000 --- a/arch/v850/kernel/rte_nb85e_cb.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * arch/v850/kernel/rte_nb85e_cb.c -- Midas labs RTE-V850E/NB85E-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_early_init (void) -{ - /* Configure caching; some possible settings: - - BHC = 0x0000, DCC = 0x0000 -- all caching disabled - BHC = 0x0040, DCC = 0x0000 -- SDRAM: icache only - BHC = 0x0080, DCC = 0x0C00 -- SDRAM: write-back dcache only - BHC = 0x00C0, DCC = 0x0C00 -- SDRAM: icache + write-back dcache - BHC = 0x00C0, DCC = 0x0800 -- SDRAM: icache + write-thru dcache - - We can only cache SDRAM (we can't use cache SRAM because it's in - the same memory region as the on-chip RAM and I/O space). - - Unfortunately, the dcache seems to be buggy, so we only use the - icache for now. */ - v850e_cache_enable (0x0040 /*BHC*/, 0x0003 /*ICC*/, 0x0000 /*DCC*/); - - rte_cb_early_init (); -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - /* We just use SDRAM here. */ - *ram_start = SDRAM_ADDR; - *ram_len = SDRAM_SIZE; -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Called before configuring an on-chip UART. */ -void rte_nb85e_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud) -{ - /* The RTE-NB85E-CB connects some general-purpose I/O pins on the - CPU to the RTS/CTS lines the UART's serial connection, as follows: - P00 = CTS (in), P01 = DSR (in), P02 = RTS (out), P03 = DTR (out). */ - - TEG_PORT0_PM = 0x03; /* P00 and P01 inputs, P02 and P03 outputs */ - TEG_PORT0_IO = 0x03; /* Accept input */ - - /* Do pre-configuration for the actual UART. */ - teg_uart_pre_configure (chan, cflags, baud); -} - -void __init mach_init_irqs (void) -{ - teg_init_irqs (); - rte_cb_init_irqs (); -} diff --git a/arch/v850/kernel/rte_nb85e_cb.ld b/arch/v850/kernel/rte_nb85e_cb.ld deleted file mode 100644 index b672f484f085..000000000000 --- a/arch/v850/kernel/rte_nb85e_cb.ld +++ /dev/null @@ -1,22 +0,0 @@ -/* Linker script for the Midas labs RTE-NB85E-CB evaluation board - (CONFIG_RTE_CB_NB85E). */ - -MEMORY { - LOW : ORIGIN = 0x0, LENGTH = 0x00100000 - /* 1MB of SRAM This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 16MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#ifdef CONFIG_RTE_CB_NB85E_KSRAM -# define KRAM SRAM -#else -# define KRAM SDRAM -#endif - -SECTIONS { - .intv : { INTV_CONTENTS } > LOW - .sram : { RAMK_KRAM_CONTENTS } > KRAM - .root : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c deleted file mode 100644 index 10335cecf7bd..000000000000 --- a/arch/v850/kernel/setup.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * arch/v850/kernel/setup.c -- Arch-dependent initialization functions - * - * Copyright (C) 2001,02,03,05,06 NEC Electronics Corporation - * Copyright (C) 2001,02,03,05,06 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include /* we don't have swap, but for nr_free_pages */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "mach.h" - -/* These symbols are all defined in the linker map to delineate various - statically allocated regions of memory. */ - -extern char _intv_start, _intv_end; -/* `kram' is only used if the kernel uses part of normal user RAM. */ -extern char _kram_start __attribute__ ((__weak__)); -extern char _kram_end __attribute__ ((__weak__)); -extern char _init_start, _init_end; -extern char _bootmap; -extern char _stext, _etext, _sdata, _edata, _sbss, _ebss; -/* Many platforms use an embedded root image. */ -extern char _root_fs_image_start __attribute__ ((__weak__)); -extern char _root_fs_image_end __attribute__ ((__weak__)); - - -char __initdata command_line[COMMAND_LINE_SIZE]; - -/* Memory not used by the kernel. */ -static unsigned long total_ram_pages; - -/* System RAM. */ -static unsigned long ram_start = 0, ram_len = 0; - - -#define ADDR_TO_PAGE_UP(x) ((((unsigned long)x) + PAGE_SIZE-1) >> PAGE_SHIFT) -#define ADDR_TO_PAGE(x) (((unsigned long)x) >> PAGE_SHIFT) -#define PAGE_TO_ADDR(x) (((unsigned long)x) << PAGE_SHIFT) - -static void init_mem_alloc (unsigned long ram_start, unsigned long ram_len); - -void set_mem_root (void *addr, size_t len, char *cmd_line); - - -void __init setup_arch (char **cmdline) -{ - /* Keep a copy of command line */ - *cmdline = command_line; - memcpy (boot_command_line, command_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE - 1] = '\0'; - - console_verbose (); - - init_mm.start_code = (unsigned long) &_stext; - init_mm.end_code = (unsigned long) &_etext; - init_mm.end_data = (unsigned long) &_edata; - init_mm.brk = (unsigned long) &_kram_end; - - /* Find out what mem this machine has. */ - mach_get_physical_ram (&ram_start, &ram_len); - /* ... and tell the kernel about it. */ - init_mem_alloc (ram_start, ram_len); - - printk (KERN_INFO "CPU: %s\nPlatform: %s\n", - CPU_MODEL_LONG, PLATFORM_LONG); - - /* do machine-specific setups. */ - mach_setup (cmdline); - -#ifdef CONFIG_MTD - if (!ROOT_DEV && &_root_fs_image_end > &_root_fs_image_start) - set_mem_root (&_root_fs_image_start, - &_root_fs_image_end - &_root_fs_image_start, - *cmdline); -#endif -} - -void __init trap_init (void) -{ -} - -#ifdef CONFIG_MTD - -/* From drivers/mtd/devices/slram.c */ -#define SLRAM_BLK_SZ 0x4000 - -/* Set the root filesystem to be the given memory region. - Some parameter may be appended to CMD_LINE. */ -void set_mem_root (void *addr, size_t len, char *cmd_line) -{ - /* Some sort of idiocy in MTD means we must supply a length that's - a multiple of SLRAM_BLK_SZ. We just round up the real length, - as the file system shouldn't attempt to access anything beyond - the end of the image anyway. */ - len = (((len - 1) + SLRAM_BLK_SZ) / SLRAM_BLK_SZ) * SLRAM_BLK_SZ; - - /* The only way to pass info to the MTD slram driver is via - the command line. */ - if (*cmd_line) { - cmd_line += strlen (cmd_line); - *cmd_line++ = ' '; - } - sprintf (cmd_line, "slram=root,0x%x,+0x%x", (u32)addr, (u32)len); - - ROOT_DEV = MKDEV (MTD_BLOCK_MAJOR, 0); -} -#endif - - -static void irq_nop (unsigned irq) { } -static unsigned irq_zero (unsigned irq) { return 0; } - -static void nmi_end (unsigned irq) -{ - if (irq != IRQ_NMI (0)) { - printk (KERN_CRIT "NMI %d is unrecoverable; restarting...", - irq - IRQ_NMI (0)); - machine_restart (0); - } -} - -static struct hw_interrupt_type nmi_irq_type = { - .typename = "NMI", - .startup = irq_zero, /* startup */ - .shutdown = irq_nop, /* shutdown */ - .enable = irq_nop, /* enable */ - .disable = irq_nop, /* disable */ - .ack = irq_nop, /* ack */ - .end = nmi_end, /* end */ -}; - -void __init init_IRQ (void) -{ - init_irq_handlers (0, NUM_MACH_IRQS, 1, 0); - init_irq_handlers (IRQ_NMI (0), NUM_NMIS, 1, &nmi_irq_type); - mach_init_irqs (); -} - - -void __init mem_init (void) -{ - max_mapnr = MAP_NR (ram_start + ram_len); - - num_physpages = ADDR_TO_PAGE (ram_len); - - total_ram_pages = free_all_bootmem (); - - printk (KERN_INFO - "Memory: %luK/%luK available" - " (%luK kernel code, %luK data)\n", - PAGE_TO_ADDR (nr_free_pages()) / 1024, - ram_len / 1024, - ((unsigned long)&_etext - (unsigned long)&_stext) / 1024, - ((unsigned long)&_ebss - (unsigned long)&_sdata) / 1024); -} - -void free_initmem (void) -{ - unsigned long ram_end = ram_start + ram_len; - unsigned long start = PAGE_ALIGN ((unsigned long)(&_init_start)); - - if (start >= ram_start && start < ram_end) { - unsigned long addr; - unsigned long end = PAGE_ALIGN ((unsigned long)(&_init_end)); - - if (end > ram_end) - end = ram_end; - - printk("Freeing unused kernel memory: %ldK freed\n", - (end - start) / 1024); - - for (addr = start; addr < end; addr += PAGE_SIZE) { - struct page *page = virt_to_page (addr); - ClearPageReserved (page); - init_page_count (page); - __free_page (page); - total_ram_pages++; - } - } -} - - -/* Initialize the `bootmem allocator'. RAM_START and RAM_LEN identify - what RAM may be used. */ -static void __init -init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) -{ - /* The part of the kernel that's in the same managed RAM space - used for general allocation. */ - unsigned long kram_start = (unsigned long)&_kram_start; - unsigned long kram_end = (unsigned long)&_kram_end; - /* End of the managed RAM space. */ - unsigned long ram_end = ram_start + ram_len; - /* Address range of the interrupt vector table. */ - unsigned long intv_start = (unsigned long)&_intv_start; - unsigned long intv_end = (unsigned long)&_intv_end; - /* True if the interrupt vectors are in the managed RAM area. */ - int intv_in_ram = (intv_end > ram_start && intv_start < ram_end); - /* True if the interrupt vectors are inside the kernel's RAM. */ - int intv_in_kram = (intv_end > kram_start && intv_start < kram_end); - /* A pointer to an optional function that reserves platform-specific - memory regions. We declare the pointer `volatile' to avoid gcc - turning the call into a static call (the problem is that since - it's a weak symbol, a static call may end up trying to reference - the location 0x0, which is not always reachable). */ - void (*volatile mrb) (void) = mach_reserve_bootmem; - /* The bootmem allocator's allocation bitmap. */ - unsigned long bootmap = (unsigned long)&_bootmap; - unsigned long bootmap_len; - - /* Round bootmap location up to next page. */ - bootmap = PAGE_TO_ADDR (ADDR_TO_PAGE_UP (bootmap)); - - /* Initialize bootmem allocator. */ - bootmap_len = init_bootmem_node (NODE_DATA (0), - ADDR_TO_PAGE (bootmap), - ADDR_TO_PAGE (PAGE_OFFSET), - ADDR_TO_PAGE (ram_end)); - - /* Now make the RAM actually allocatable (it starts out `reserved'). */ - free_bootmem (ram_start, ram_len); - - if (kram_end > kram_start) - /* Reserve the RAM part of the kernel's address space, so it - doesn't get allocated. */ - reserve_bootmem(kram_start, kram_end - kram_start, - BOOTMEM_DEFAULT); - - if (intv_in_ram && !intv_in_kram) - /* Reserve the interrupt vector space. */ - reserve_bootmem(intv_start, intv_end - intv_start, - BOOTMEM_DEFAULT); - - if (bootmap >= ram_start && bootmap < ram_end) - /* Reserve the bootmap space. */ - reserve_bootmem(bootmap, bootmap_len, - BOOTMEM_DEFAULT); - - /* Reserve the memory used by the root filesystem image if it's - in RAM. */ - if (&_root_fs_image_end > &_root_fs_image_start - && (unsigned long)&_root_fs_image_start >= ram_start - && (unsigned long)&_root_fs_image_start < ram_end) - reserve_bootmem ((unsigned long)&_root_fs_image_start, - &_root_fs_image_end - &_root_fs_image_start, - BOOTMEM_DEFAULT); - - /* Let the platform-dependent code reserve some too. */ - if (mrb) - (*mrb) (); -} - -/* Tell the kernel about what RAM it may use for memory allocation. */ -static void __init -init_mem_alloc (unsigned long ram_start, unsigned long ram_len) -{ - unsigned i; - unsigned long zones_size[MAX_NR_ZONES]; - - init_bootmem_alloc (ram_start, ram_len); - - for (i = 0; i < MAX_NR_ZONES; i++) - zones_size[i] = 0; - - /* We stuff all the memory into one area, which includes the - initial gap from PAGE_OFFSET to ram_start. */ - zones_size[ZONE_DMA] - = ADDR_TO_PAGE (ram_len + (ram_start - PAGE_OFFSET)); - - /* The allocator is very picky about the address of the first - allocatable page -- it must be at least as aligned as the - maximum allocation -- so try to detect cases where it will get - confused and signal them at compile time (this is a common - problem when porting to a new platform with ). There is a - similar runtime check in free_area_init_core. */ -#if ((PAGE_OFFSET >> PAGE_SHIFT) & ((1UL << (MAX_ORDER - 1)) - 1)) -#error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it) -#endif - NODE_DATA(0)->node_mem_map = NULL; - free_area_init_node(0, zones_size, ADDR_TO_PAGE (PAGE_OFFSET), 0); -} - - - -/* Taken from m68knommu */ -void show_mem(void) -{ - unsigned long i; - int free = 0, total = 0, reserved = 0, shared = 0; - int cached = 0; - - printk(KERN_INFO "\nMem-info:\n"); - show_free_areas(); - i = max_mapnr; - while (i-- > 0) { - total++; - if (PageReserved(mem_map+i)) - reserved++; - else if (PageSwapCache(mem_map+i)) - cached++; - else if (!page_count(mem_map+i)) - free++; - else - shared += page_count(mem_map+i) - 1; - } - printk(KERN_INFO "%d pages of RAM\n",total); - printk(KERN_INFO "%d free pages\n",free); - printk(KERN_INFO "%d reserved pages\n",reserved); - printk(KERN_INFO "%d pages shared\n",shared); - printk(KERN_INFO "%d pages swap cached\n",cached); -} diff --git a/arch/v850/kernel/signal.c b/arch/v850/kernel/signal.c deleted file mode 100644 index bf166e7e762c..000000000000 --- a/arch/v850/kernel/signal.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * arch/v850/kernel/signal.c -- Signal handling - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * Copyright (C) 1999,2000,2002 Niibe Yutaka & Kaz Kojima - * Copyright (C) 1991,1992 Linus Torvalds - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson - * - * This file was derived from the sh version, arch/sh/kernel/signal.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define DEBUG_SIG 0 - -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); - -/* - * Atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage int -sys_sigsuspend(old_sigset_t mask, struct pt_regs *regs) -{ - sigset_t saveset; - - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->gpr[GPR_RVAL] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(regs, &saveset)) - return -EINTR; - } -} - -asmlinkage int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, - struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->gpr[GPR_RVAL] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(regs, &saveset)) - return -EINTR; - } -} - -asmlinkage int -sys_sigaction(int sig, const struct old_sigaction *act, - struct old_sigaction *oact) -{ - struct k_sigaction new_ka, old_ka; - int ret; - - if (act) { - old_sigset_t mask; - if (!access_ok(VERIFY_READ, act, sizeof(*act)) || - __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) - return -EFAULT; - __get_user(new_ka.sa.sa_flags, &act->sa_flags); - __get_user(mask, &act->sa_mask); - siginitset(&new_ka.sa.sa_mask, mask); - } - - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - - if (!ret && oact) { - if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || - __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) - return -EFAULT; - __put_user(old_ka.sa.sa_flags, &oact->sa_flags); - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); - } - - return ret; -} - -asmlinkage int -sys_sigaltstack(const stack_t *uss, stack_t *uoss, - struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->gpr[GPR_SP]); -} - - -/* - * Do a signal return; undo the signal stack. - */ - -struct sigframe -{ - struct sigcontext sc; - unsigned long extramask[_NSIG_WORDS-1]; - unsigned long tramp[2]; /* signal trampoline */ -}; - -struct rt_sigframe -{ - struct siginfo info; - struct ucontext uc; - unsigned long tramp[2]; /* signal trampoline */ -}; - -static int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *rval_p) -{ - unsigned int err = 0; - -#define COPY(x) err |= __get_user(regs->x, &sc->regs.x) - COPY(gpr[0]); COPY(gpr[1]); COPY(gpr[2]); COPY(gpr[3]); - COPY(gpr[4]); COPY(gpr[5]); COPY(gpr[6]); COPY(gpr[7]); - COPY(gpr[8]); COPY(gpr[9]); COPY(gpr[10]); COPY(gpr[11]); - COPY(gpr[12]); COPY(gpr[13]); COPY(gpr[14]); COPY(gpr[15]); - COPY(gpr[16]); COPY(gpr[17]); COPY(gpr[18]); COPY(gpr[19]); - COPY(gpr[20]); COPY(gpr[21]); COPY(gpr[22]); COPY(gpr[23]); - COPY(gpr[24]); COPY(gpr[25]); COPY(gpr[26]); COPY(gpr[27]); - COPY(gpr[28]); COPY(gpr[29]); COPY(gpr[30]); COPY(gpr[31]); - COPY(pc); COPY(psw); - COPY(ctpc); COPY(ctpsw); COPY(ctbp); -#undef COPY - - return err; -} - -asmlinkage int sys_sigreturn(struct pt_regs *regs) -{ - struct sigframe *frame = (struct sigframe *)regs->gpr[GPR_SP]; - sigset_t set; - int rval; - - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; - - if (__get_user(set.sig[0], &frame->sc.oldmask) - || (_NSIG_WORDS > 1 - && __copy_from_user(&set.sig[1], &frame->extramask, - sizeof(frame->extramask)))) - goto badframe; - - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - if (restore_sigcontext(regs, &frame->sc, &rval)) - goto badframe; - return rval; - -badframe: - force_sig(SIGSEGV, current); - return 0; -} - -asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) -{ - struct rt_sigframe *frame = (struct rt_sigframe *)regs->gpr[GPR_SP]; - sigset_t set; - stack_t st; - int rval; - - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; - - if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) - goto badframe; - - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval)) - goto badframe; - - if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) - goto badframe; - /* It is more difficult to avoid calling this function than to - call it and ignore errors. */ - do_sigaltstack(&st, NULL, regs->gpr[GPR_SP]); - - return rval; - -badframe: - force_sig(SIGSEGV, current); - return 0; -} - -/* - * Set up a signal frame. - */ - -static int -setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, - unsigned long mask) -{ - int err = 0; - -#define COPY(x) err |= __put_user(regs->x, &sc->regs.x) - COPY(gpr[0]); COPY(gpr[1]); COPY(gpr[2]); COPY(gpr[3]); - COPY(gpr[4]); COPY(gpr[5]); COPY(gpr[6]); COPY(gpr[7]); - COPY(gpr[8]); COPY(gpr[9]); COPY(gpr[10]); COPY(gpr[11]); - COPY(gpr[12]); COPY(gpr[13]); COPY(gpr[14]); COPY(gpr[15]); - COPY(gpr[16]); COPY(gpr[17]); COPY(gpr[18]); COPY(gpr[19]); - COPY(gpr[20]); COPY(gpr[21]); COPY(gpr[22]); COPY(gpr[23]); - COPY(gpr[24]); COPY(gpr[25]); COPY(gpr[26]); COPY(gpr[27]); - COPY(gpr[28]); COPY(gpr[29]); COPY(gpr[30]); COPY(gpr[31]); - COPY(pc); COPY(psw); - COPY(ctpc); COPY(ctpsw); COPY(ctbp); -#undef COPY - - err |= __put_user(mask, &sc->oldmask); - - return err; -} - -/* - * Determine which stack to use.. - */ -static inline void * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) -{ - /* Default to using normal stack */ - unsigned long sp = regs->gpr[GPR_SP]; - - if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp)) - sp = current->sas_ss_sp + current->sas_ss_size; - - return (void *)((sp - frame_size) & -8UL); -} - -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs *regs) -{ - struct sigframe *frame; - int err = 0; - int signal; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); - - if (_NSIG_WORDS > 1) { - err |= __copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) { - regs->gpr[GPR_LP] = (unsigned long) ka->sa.sa_restorer; - } else { - /* Note, these encodings are _little endian_! */ - - /* addi __NR_sigreturn, r0, r12 */ - err |= __put_user(0x6600 | (__NR_sigreturn << 16), - frame->tramp + 0); - /* trap 0 */ - err |= __put_user(0x010007e0, - frame->tramp + 1); - - regs->gpr[GPR_LP] = (unsigned long)frame->tramp; - - flush_cache_sigtramp (regs->gpr[GPR_LP]); - } - - if (err) - goto give_sigsegv; - - /* Set up registers for signal handler. */ - regs->pc = (v850_reg_t) ka->sa.sa_handler; - regs->gpr[GPR_SP] = (v850_reg_t)frame; - /* Signal handler args: */ - regs->gpr[GPR_ARG0] = signal; /* arg 0: signum */ - regs->gpr[GPR_ARG1] = (v850_reg_t)&frame->sc;/* arg 1: sigcontext */ - - set_fs(USER_DS); - -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx ra=%08lx\n", - current->comm, current->pid, frame, regs->pc, ); -#endif - - return; - -give_sigsegv: - force_sigsegv(sig, current); -} - -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) -{ - struct rt_sigframe *frame; - int err = 0; - int signal; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= copy_siginfo_to_user(&frame->info, info); - - /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user((void *)current->sas_ss_sp, - &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(regs->gpr[GPR_SP]), - &frame->uc.uc_stack.ss_flags); - err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= setup_sigcontext(&frame->uc.uc_mcontext, - regs, set->sig[0]); - err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) { - regs->gpr[GPR_LP] = (unsigned long) ka->sa.sa_restorer; - } else { - /* Note, these encodings are _little endian_! */ - - /* addi __NR_sigreturn, r0, r12 */ - err |= __put_user(0x6600 | (__NR_sigreturn << 16), - frame->tramp + 0); - /* trap 0 */ - err |= __put_user(0x010007e0, - frame->tramp + 1); - - regs->gpr[GPR_LP] = (unsigned long)frame->tramp; - - flush_cache_sigtramp (regs->gpr[GPR_LP]); - } - - if (err) - goto give_sigsegv; - - /* Set up registers for signal handler. */ - regs->pc = (v850_reg_t) ka->sa.sa_handler; - regs->gpr[GPR_SP] = (v850_reg_t)frame; - /* Signal handler args: */ - regs->gpr[GPR_ARG0] = signal; /* arg 0: signum */ - regs->gpr[GPR_ARG1] = (v850_reg_t)&frame->info; /* arg 1: siginfo */ - regs->gpr[GPR_ARG2] = (v850_reg_t)&frame->uc; /* arg 2: ucontext */ - - set_fs(USER_DS); - -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", - current->comm, current->pid, frame, regs->pc, regs->pr); -#endif - - return; - -give_sigsegv: - force_sigsegv(sig, current); -} - -/* - * OK, we're invoking a handler - */ - -static void -handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, - sigset_t *oldset, struct pt_regs * regs) -{ - /* Are we from a system call? */ - if (PT_REGS_SYSCALL (regs)) { - /* If so, check system call restarting.. */ - switch (regs->gpr[GPR_RVAL]) { - case -ERESTART_RESTARTBLOCK: - current_thread_info()->restart_block.fn = - do_no_restart_syscall; - /* fall through */ - case -ERESTARTNOHAND: - regs->gpr[GPR_RVAL] = -EINTR; - break; - - case -ERESTARTSYS: - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->gpr[GPR_RVAL] = -EINTR; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - regs->gpr[12] = PT_REGS_SYSCALL (regs); - regs->pc -= 4; /* Size of `trap 0' insn. */ - } - - PT_REGS_SET_SYSCALL (regs, 0); - } - - /* Set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); - else - setup_frame(sig, ka, oldset, regs); - - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - * - * Note that we go through the signals twice: once to check the signals that - * the kernel can handle, and then we build all the user-level signal handling - * stack-frames in one go after that. - */ -int do_signal(struct pt_regs *regs, sigset_t *oldset) -{ - siginfo_t info; - int signr; - struct k_sigaction ka; - - /* - * We want the common case to go fast, which - * is why we may in certain cases get here from - * kernel mode. Just return without doing anything - * if so. - */ - if (!user_mode(regs)) - return 1; - - if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { - /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, &ka, oldset, regs); - return 1; - } - - /* Did we come from a system call? */ - if (PT_REGS_SYSCALL (regs)) { - int rval = (int)regs->gpr[GPR_RVAL]; - /* Restart the system call - no handlers present */ - if (rval == -ERESTARTNOHAND - || rval == -ERESTARTSYS - || rval == -ERESTARTNOINTR) - { - regs->gpr[12] = PT_REGS_SYSCALL (regs); - regs->pc -= 4; /* Size of `trap 0' insn. */ - } - else if (rval == -ERESTART_RESTARTBLOCK) { - regs->gpr[12] = __NR_restart_syscall; - regs->pc -= 4; /* Size of `trap 0' insn. */ - } - } - return 0; -} diff --git a/arch/v850/kernel/sim.c b/arch/v850/kernel/sim.c deleted file mode 100644 index 467b4aa0acdd..000000000000 --- a/arch/v850/kernel/sim.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * arch/v850/kernel/sim.c -- Machine-specific stuff for GDB v850e simulator - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -/* The name of a file containing the root filesystem. */ -#define ROOT_FS "rootfs.image" - -extern void simcons_setup (void); -extern void simcons_poll_ttys (void); -extern void set_mem_root (void *addr, size_t len, char *cmd_line); - -static int read_file (const char *name, - unsigned long *addr, unsigned long *len, - const char **err); - -void __init mach_setup (char **cmdline) -{ - const char *err; - unsigned long root_dev_addr, root_dev_len; - - simcons_setup (); - - printk (KERN_INFO "Reading root filesystem: %s", ROOT_FS); - - if (read_file (ROOT_FS, &root_dev_addr, &root_dev_len, &err)) { - printk (" (size %luK)\n", root_dev_len / 1024); - set_mem_root ((void *)root_dev_addr, (size_t)root_dev_len, - *cmdline); - } else - printk ("...%s failed!\n", err); -} - -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len) -{ - *ram_start = RAM_ADDR; - *ram_len = RAM_SIZE; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* ...do magic timer initialization?... */ - mach_tick = simcons_poll_ttys; - setup_irq (0, timer_action); -} - - -static void irq_nop (unsigned irq) { } -static unsigned irq_zero (unsigned irq) { return 0; } - -static struct hw_interrupt_type sim_irq_type = { - .typename = "IRQ", - .startup = irq_zero, /* startup */ - .shutdown = irq_nop, /* shutdown */ - .enable = irq_nop, /* enable */ - .disable = irq_nop, /* disable */ - .ack = irq_nop, /* ack */ - .end = irq_nop, /* end */ -}; - -void __init mach_init_irqs (void) -{ - init_irq_handlers (0, NUM_MACH_IRQS, 1, &sim_irq_type); -} - - -void mach_gettimeofday (struct timespec *tv) -{ - long timeval[2], timezone[2]; - int rval = V850_SIM_SYSCALL (gettimeofday, timeval, timezone); - if (rval == 0) { - tv->tv_sec = timeval[0]; - tv->tv_nsec = timeval[1] * 1000; - } -} - -void machine_restart (char *__unused) -{ - V850_SIM_SYSCALL (write, 1, "RESTART\n", 8); - V850_SIM_SYSCALL (exit, 0); -} - -void machine_halt (void) -{ - V850_SIM_SYSCALL (write, 1, "HALT\n", 5); - V850_SIM_SYSCALL (exit, 0); -} - -void machine_power_off (void) -{ - V850_SIM_SYSCALL (write, 1, "POWER OFF\n", 10); - V850_SIM_SYSCALL (exit, 0); -} - - -/* Load data from a file called NAME into ram. The address and length - of the data image are returned in ADDR and LEN. */ -static int __init -read_file (const char *name, - unsigned long *addr, unsigned long *len, - const char **err) -{ - int rval, fd; - unsigned long cur, left; - /* Note this is not a normal stat buffer, it's an ad-hoc - structure defined by the simulator. */ - unsigned long stat_buf[10]; - - /* Stat the file to find out the length. */ - rval = V850_SIM_SYSCALL (stat, name, stat_buf); - if (rval < 0) { - if (err) *err = "stat"; - return 0; - } - *len = stat_buf[4]; - - /* Open the file; `0' is O_RDONLY. */ - fd = V850_SIM_SYSCALL (open, name, 0); - if (fd < 0) { - if (err) *err = "open"; - return 0; - } - - *addr = (unsigned long)alloc_bootmem(*len); - if (! *addr) { - V850_SIM_SYSCALL (close, fd); - if (err) *err = "alloc_bootmem"; - return 0; - } - - cur = *addr; - left = *len; - while (left > 0) { - int chunk = V850_SIM_SYSCALL (read, fd, cur, left); - if (chunk <= 0) - break; - cur += chunk; - left -= chunk; - } - V850_SIM_SYSCALL (close, fd); - if (left > 0) { - /* Some read failed. */ - free_bootmem (*addr, *len); - if (err) *err = "read"; - return 0; - } - - return 1; -} diff --git a/arch/v850/kernel/sim.ld b/arch/v850/kernel/sim.ld deleted file mode 100644 index 101885f3c9f0..000000000000 --- a/arch/v850/kernel/sim.ld +++ /dev/null @@ -1,13 +0,0 @@ -/* Linker script for the gdb v850e simulator (CONFIG_V850E_SIM). */ - -MEMORY { - /* Interrupt vectors. */ - INTV : ORIGIN = 0x0, LENGTH = 0xe0 - /* Main RAM. */ - RAM : ORIGIN = RAM_ADDR, LENGTH = RAM_SIZE -} - -SECTIONS { - .intv : { INTV_CONTENTS } > INTV - .ram : { RAMK_KRAM_CONTENTS } > RAM -} diff --git a/arch/v850/kernel/sim85e2.c b/arch/v850/kernel/sim85e2.c deleted file mode 100644 index 566dde5e6070..000000000000 --- a/arch/v850/kernel/sim85e2.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * arch/v850/kernel/sim85e2.c -- Machine-specific stuff for - * V850E2 RTL simulator - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mach.h" - - -/* There are 4 possible areas we can use: - - IRAM (1MB) is fast for instruction fetches, but slow for data - DRAM (1020KB) is fast for data, but slow for instructions - ERAM is cached, so should be fast for both insns and data - SDRAM is external DRAM, similar to ERAM -*/ - -#define INIT_MEMC_FOR_SDRAM -#define USE_SDRAM_AREA -#define KERNEL_IN_SDRAM_AREA - -#define DCACHE_MODE V850E2_CACHE_BTSC_DCM_WT -/*#define DCACHE_MODE V850E2_CACHE_BTSC_DCM_WB_ALLOC*/ - -#ifdef USE_SDRAM_AREA -#define RAM_START SDRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) -#else -/* When we use DRAM, we need to account for the fact that the end of it is - used for R0_RAM. */ -#define RAM_START DRAM_ADDR -#define RAM_END R0_RAM_ADDR -#endif - - -extern void memcons_setup (void); - - -#ifdef KERNEL_IN_SDRAM_AREA -#define EARLY_INIT_SECTION_ATTR __attribute__ ((section (".early.text"))) -#else -#define EARLY_INIT_SECTION_ATTR __init -#endif - -void EARLY_INIT_SECTION_ATTR mach_early_init (void) -{ - /* The sim85e2 simulator tracks `undefined' values, so to make - debugging easier, we begin by zeroing out all otherwise - undefined registers. This is not strictly necessary. - - The registers we zero are: - Every GPR except: - stack-pointer (r3) - task-pointer (r16) - our return addr (r31) - Every system register (SPR) that we know about except for - the PSW (SPR 5), which we zero except for the - disable-interrupts bit. - */ - - /* GPRs */ - asm volatile (" mov r0, r1 ; mov r0, r2 "); - asm volatile ("mov r0, r4 ; mov r0, r5 ; mov r0, r6 ; mov r0, r7 "); - asm volatile ("mov r0, r8 ; mov r0, r9 ; mov r0, r10; mov r0, r11"); - asm volatile ("mov r0, r12; mov r0, r13; mov r0, r14; mov r0, r15"); - asm volatile (" mov r0, r17; mov r0, r18; mov r0, r19"); - asm volatile ("mov r0, r20; mov r0, r21; mov r0, r22; mov r0, r23"); - asm volatile ("mov r0, r24; mov r0, r25; mov r0, r26; mov r0, r27"); - asm volatile ("mov r0, r28; mov r0, r29; mov r0, r30"); - - /* SPRs */ - asm volatile ("ldsr r0, 0; ldsr r0, 1; ldsr r0, 2; ldsr r0, 3"); - asm volatile ("ldsr r0, 4"); - asm volatile ("addi 0x20, r0, r1; ldsr r1, 5"); /* PSW */ - asm volatile ("ldsr r0, 16; ldsr r0, 17; ldsr r0, 18; ldsr r0, 19"); - asm volatile ("ldsr r0, 20"); - - -#ifdef INIT_MEMC_FOR_SDRAM - /* Settings for SDRAM controller. */ - V850E2_VSWC = 0x0042; - V850E2_BSC = 0x9286; - V850E2_BCT(0) = 0xb000; /* was: 0 */ - V850E2_BCT(1) = 0x000b; - V850E2_ASC = 0; - V850E2_LBS = 0xa9aa; /* was: 0xaaaa */ - V850E2_LBC(0) = 0; - V850E2_LBC(1) = 0; /* was: 0x3 */ - V850E2_BCC = 0; - V850E2_RFS(4) = 0x800a; /* was: 0xf109 */ - V850E2_SCR(4) = 0x2091; /* was: 0x20a1 */ - V850E2_RFS(3) = 0x800c; - V850E2_SCR(3) = 0x20a1; - V850E2_DWC(0) = 0; - V850E2_DWC(1) = 0; -#endif - -#if 0 -#ifdef CONFIG_V850E2_SIM85E2S - /* Turn on the caches. */ - V850E2_CACHE_BTSC = V850E2_CACHE_BTSC_ICM | DCACHE_MODE; - V850E2_BHC = 0x1010; -#elif CONFIG_V850E2_SIM85E2C - V850E2_CACHE_BTSC |= (V850E2_CACHE_BTSC_ICM | V850E2_CACHE_BTSC_DCM0); - V850E2_BUSM_BHC = 0xFFFF; -#endif -#else - V850E2_BHC = 0; -#endif - - /* Don't stop the simulator at `halt' instructions. */ - SIM85E2_NOTHAL = 1; - - /* Ensure that the simulator halts on a panic, instead of going - into an infinite loop inside the panic function. */ - panic_timeout = -1; -} - -void __init mach_setup (char **cmdline) -{ - memcons_setup (); -} - -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* The simulator actually cycles through all interrupts - periodically. We just pay attention to IRQ0, which gives us - 1/64 the rate of the periodic interrupts. */ - setup_irq (0, timer_action); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Interrupts */ - -struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { 0 } -}; -struct hw_interrupt_type hw_itypes[1]; - -/* Initialize interrupts. */ -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - - -void machine_halt (void) __attribute__ ((noreturn)); -void machine_halt (void) -{ - SIM85E2_SIMFIN = 0; /* Halt immediately. */ - for (;;) {} -} - -void machine_restart (char *__unused) -{ - machine_halt (); -} - -void machine_power_off (void) -{ - machine_halt (); -} - diff --git a/arch/v850/kernel/sim85e2.ld b/arch/v850/kernel/sim85e2.ld deleted file mode 100644 index 7470fd2ffb5b..000000000000 --- a/arch/v850/kernel/sim85e2.ld +++ /dev/null @@ -1,36 +0,0 @@ -/* Linker script for the sim85e2c simulator, which is a verilog simulation of - the V850E2 NA85E2C cpu core (CONFIG_V850E2_SIM85E2C). */ - -MEMORY { - /* 1MB of `instruction RAM', starting at 0. - Instruction fetches are much faster from IRAM than from DRAM. */ - IRAM : ORIGIN = IRAM_ADDR, LENGTH = IRAM_SIZE - - /* 1MB of `data RAM', below and contiguous with the I/O space. - Data fetches are much faster from DRAM than from IRAM. */ - DRAM : ORIGIN = DRAM_ADDR, LENGTH = DRAM_SIZE - - /* `external ram' (CS1 area), comes after IRAM. */ - ERAM : ORIGIN = ERAM_ADDR, LENGTH = ERAM_SIZE - - /* Dynamic RAM; uses memory controller. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - .iram : { - INTV_CONTENTS - *arch/v850/kernel/head.o - *(.early.text) - } > IRAM - .dram : { - _memcons_output = . ; - . = . + 0x8000 ; - _memcons_output_end = . ; - } > DRAM - .sdram : { - /* We stick console output into a buffer here. */ - RAMK_KRAM_CONTENTS - ROOT_FS_CONTENTS - } > SDRAM -} diff --git a/arch/v850/kernel/simcons.c b/arch/v850/kernel/simcons.c deleted file mode 100644 index 9973596ae304..000000000000 --- a/arch/v850/kernel/simcons.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * arch/v850/kernel/simcons.c -- Console I/O for GDB v850e simulator - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -/* Low-level console. */ - -static void simcons_write (struct console *co, const char *buf, unsigned len) -{ - V850_SIM_SYSCALL (write, 1, buf, len); -} - -static int simcons_read (struct console *co, char *buf, unsigned len) -{ - return V850_SIM_SYSCALL (read, 0, buf, len); -} - -static struct tty_driver *tty_driver; -static struct tty_driver *simcons_device (struct console *c, int *index) -{ - *index = c->index; - return tty_driver; -} - -static struct console simcons = -{ - .name = "simcons", - .write = simcons_write, - .read = simcons_read, - .device = simcons_device, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* Higher level TTY interface. */ - -int simcons_tty_open (struct tty_struct *tty, struct file *filp) -{ - return 0; -} - -int simcons_tty_write (struct tty_struct *tty, - const unsigned char *buf, int count) -{ - return V850_SIM_SYSCALL (write, 1, buf, count); -} - -int simcons_tty_write_room (struct tty_struct *tty) -{ - /* Completely arbitrary. */ - return 0x100000; -} - -int simcons_tty_chars_in_buffer (struct tty_struct *tty) -{ - /* We have no buffer. */ - return 0; -} - -static const struct tty_operations ops = { - .open = simcons_tty_open, - .write = simcons_tty_write, - .write_room = simcons_tty_write_room, - .chars_in_buffer = simcons_tty_chars_in_buffer, -}; - -int __init simcons_tty_init (void) -{ - struct tty_driver *driver = alloc_tty_driver(1); - int err; - if (!driver) - return -ENOMEM; - driver->name = "simcons"; - driver->major = TTY_MAJOR; - driver->minor_start = 64; - driver->type = TTY_DRIVER_TYPE_SYSCONS; - driver->init_termios = tty_std_termios; - tty_set_operations(driver, &ops); - err = tty_register_driver(driver); - if (err) { - put_tty_driver(driver); - return err; - } - tty_driver = driver; - return 0; -} -/* We use `late_initcall' instead of just `__initcall' as a workaround for - the fact that (1) simcons_tty_init can't be called before tty_init, - (2) tty_init is called via `module_init', (3) if statically linked, - module_init == device_init, and (4) there's no ordering of init lists. - We can do this easily because simcons is always statically linked, but - other tty drivers that depend on tty_init and which must use - `module_init' to declare their init routines are likely to be broken. */ -late_initcall(simcons_tty_init); - -/* Poll for input on the console, and if there's any, deliver it to the - tty driver. */ -void simcons_poll_tty (struct tty_struct *tty) -{ - char buf[32]; /* Not the nicest way to do it but I need it correct first */ - int flip = 0, send_break = 0; - struct pollfd pfd; - pfd.fd = 0; - pfd.events = POLLIN; - - if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) { - if (pfd.revents & POLLIN) { - /* Real block hardware knows the transfer size before - transfer so the new tty buffering doesn't try to handle - this rather weird simulator specific case well */ - int rd = V850_SIM_SYSCALL (read, 0, buf, 32); - if (rd > 0) { - tty_insert_flip_string(tty, buf, rd); - flip = 1; - } else - send_break = 1; - } else if (pfd.revents & POLLERR) - send_break = 1; - } - - if (send_break) { - tty_insert_flip_char (tty, 0, TTY_BREAK); - flip = 1; - } - - if (flip) - tty_schedule_flip (tty); -} - -void simcons_poll_ttys (void) -{ - if (tty_driver && tty_driver->ttys[0]) - simcons_poll_tty (tty_driver->ttys[0]); -} - -void simcons_setup (void) -{ - V850_SIM_SYSCALL (make_raw, 0); - register_console (&simcons); - printk (KERN_INFO "Console: GDB V850E simulator stdio\n"); -} diff --git a/arch/v850/kernel/syscalls.c b/arch/v850/kernel/syscalls.c deleted file mode 100644 index 1a83daf8e24f..000000000000 --- a/arch/v850/kernel/syscalls.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * arch/v850/kernel/syscalls.c -- Various system-call definitions not - * defined in machine-independent code - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * This file was derived the ppc version, arch/ppc/kernel/syscalls.c - * ... which was derived from "arch/i386/kernel/sys_i386.c" by Gary Thomas; - * modified by Cort Dougan (cort@cs.nmt.edu) - * and Paul Mackerras (paulus@cs.anu.edu.au). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * sys_ipc() is the de-multiplexer for the SysV IPC calls.. - * - * This is really horribly ugly. - */ -int -sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) -{ - int version, ret; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - ret = -EINVAL; - switch (call) { - case SEMOP: - ret = sys_semop (first, (struct sembuf *)ptr, second); - break; - case SEMGET: - ret = sys_semget (first, second, third); - break; - case SEMCTL: - { - union semun fourth; - - if (!ptr) - break; - if ((ret = access_ok(VERIFY_READ, ptr, sizeof(long)) ? 0 : -EFAULT) - || (ret = get_user(fourth.__pad, (void **)ptr))) - break; - ret = sys_semctl (first, second, third, fourth); - break; - } - case MSGSND: - ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third); - break; - case MSGRCV: - switch (version) { - case 0: { - struct ipc_kludge tmp; - - if (!ptr) - break; - if ((ret = access_ok(VERIFY_READ, ptr, sizeof(tmp)) ? 0 : -EFAULT) - || (ret = copy_from_user(&tmp, - (struct ipc_kludge *) ptr, - sizeof (tmp)))) - break; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, - third); - break; - } - default: - ret = sys_msgrcv (first, (struct msgbuf *) ptr, - second, fifth, third); - break; - } - break; - case MSGGET: - ret = sys_msgget ((key_t) first, second); - break; - case MSGCTL: - ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); - break; - case SHMAT: - switch (version) { - default: { - ulong raddr; - - if ((ret = access_ok(VERIFY_WRITE, (ulong*) third, - sizeof(ulong)) ? 0 : -EFAULT)) - break; - ret = do_shmat (first, (char *) ptr, second, &raddr); - if (ret) - break; - ret = put_user (raddr, (ulong *) third); - break; - } - case 1: /* iBCS2 emulator entry point */ - if (!segment_eq(get_fs(), get_ds())) - break; - ret = do_shmat (first, (char *) ptr, second, - (ulong *) third); - break; - } - break; - case SHMDT: - ret = sys_shmdt ((char *)ptr); - break; - case SHMGET: - ret = sys_shmget (first, second, third); - break; - case SHMCTL: - ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); - break; - } - - return ret; -} - -static inline unsigned long -do_mmap2 (unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long pgoff) -{ - struct file * file = NULL; - int ret = -EBADF; - - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - if (! (flags & MAP_ANONYMOUS)) { - if (!(file = fget (fd))) - goto out; - } - - down_write (¤t->mm->mmap_sem); - ret = do_mmap_pgoff (file, addr, len, prot, flags, pgoff); - up_write (¤t->mm->mmap_sem); - if (file) - fput (file); -out: - return ret; -} - -unsigned long sys_mmap2 (unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long pgoff) -{ - return do_mmap2 (addr, len, prot, flags, fd, pgoff); -} - -unsigned long sys_mmap (unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, off_t offset) -{ - int err = -EINVAL; - - if (offset & ~PAGE_MASK) - goto out; - - err = do_mmap2 (addr, len, prot, flags, fd, offset >> PAGE_SHIFT); -out: - return err; -} - -/* - * Do a system call from kernel instead of calling sys_execve so we - * end up with proper pt_regs. - */ -int kernel_execve(const char *filename, char *const argv[], char *const envp[]) -{ - register char *__a __asm__ ("r6") = filename; - register void *__b __asm__ ("r7") = argv; - register void *__c __asm__ ("r8") = envp; - register unsigned long __syscall __asm__ ("r12") = __NR_execve; - register unsigned long __ret __asm__ ("r10"); - __asm__ __volatile__ ("trap 0" - : "=r" (__ret), "=r" (__syscall) - : "1" (__syscall), "r" (__a), "r" (__b), "r" (__c) - : "r1", "r5", "r11", "r13", "r14", - "r15", "r16", "r17", "r18", "r19"); - return __ret; -} diff --git a/arch/v850/kernel/teg.c b/arch/v850/kernel/teg.c deleted file mode 100644 index 699248f92aae..000000000000 --- a/arch/v850/kernel/teg.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * arch/v850/kernel/teg.c -- NB85E-TEG cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Select timer interrupt instead of external pin. */ - TEG_ISS |= 0x1; - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_CPU_IRQS, 1, 7 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "SER", IRQ_INTSER(0), IRQ_INTSER_NUM, 1, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 1, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 1, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize MA chip interrupts. */ -void __init teg_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -/* Called before configuring an on-chip UART. */ -void teg_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* Enable UART I/O pins instead of external interrupt pins, and - UART interrupts instead of external pin interrupts. */ - TEG_ISS |= 0x4E; -} diff --git a/arch/v850/kernel/time.c b/arch/v850/kernel/time.c deleted file mode 100644 index d810c93fe665..000000000000 --- a/arch/v850/kernel/time.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * linux/arch/v850/kernel/time.c -- Arch-dependent timer functions - * - * Copyright (C) 1991, 1992, 1995, 2001, 2002 Linus Torvalds - * - * This file contains the v850-specific time handling details. - * Most of the stuff is located in the machine specific files. - * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "mach.h" - -#define TICK_SIZE (tick_nsec / 1000) - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ -static irqreturn_t timer_interrupt (int irq, void *dummy, struct pt_regs *regs) -{ -#if 0 - /* last time the cmos clock got updated */ - static long last_rtc_update=0; -#endif - - /* may need to kick the hardware timer */ - if (mach_tick) - mach_tick (); - - do_timer (1); -#ifndef CONFIG_SMP - update_process_times(user_mode(regs)); -#endif - profile_tick(CPU_PROFILING, regs); -#if 0 - /* - * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be - * called as close as possible to 500 ms before the new second starts. - */ - if (ntp_synced() && - xtime.tv_sec > last_rtc_update + 660 && - (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && - (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { - if (set_rtc_mmss (xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ - } -#ifdef CONFIG_HEARTBEAT - /* use power LED as a heartbeat instead -- much more useful - for debugging -- based on the version for PReP by Cort */ - /* acts like an actual heart beat -- ie thump-thump-pause... */ - if (mach_heartbeat) { - static unsigned cnt = 0, period = 0, dist = 0; - - if (cnt == 0 || cnt == dist) - mach_heartbeat ( 1 ); - else if (cnt == 7 || cnt == dist+7) - mach_heartbeat ( 0 ); - - if (++cnt > period) { - cnt = 0; - /* The hyperbolic function below modifies the heartbeat period - * length in dependency of the current (5min) load. It goes - * through the points f(0)=126, f(1)=86, f(5)=51, - * f(inf)->30. */ - period = ((672< -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -extern void *trap_table; -EXPORT_SYMBOL (trap_table); - -/* platform dependent support */ -EXPORT_SYMBOL (kernel_thread); -EXPORT_SYMBOL (__bug); - -/* Networking helper routines. */ -EXPORT_SYMBOL (csum_partial_copy_nocheck); -EXPORT_SYMBOL (csum_partial_copy_from_user); -EXPORT_SYMBOL (ip_compute_csum); -EXPORT_SYMBOL (ip_fast_csum); - -/* string / mem functions */ -EXPORT_SYMBOL (memset); -EXPORT_SYMBOL (memcpy); -EXPORT_SYMBOL (memmove); - -/* - * libgcc functions - functions that are used internally by the - * compiler... (prototypes are not correct though, but that - * doesn't really matter since they're not versioned). - */ -extern void __ashldi3 (void); -extern void __ashrdi3 (void); -extern void __lshrdi3 (void); -extern void __muldi3 (void); -extern void __negdi2 (void); - -EXPORT_SYMBOL (__ashldi3); -EXPORT_SYMBOL (__ashrdi3); -EXPORT_SYMBOL (__lshrdi3); -EXPORT_SYMBOL (__muldi3); -EXPORT_SYMBOL (__negdi2); diff --git a/arch/v850/kernel/v850e2_cache.c b/arch/v850/kernel/v850e2_cache.c deleted file mode 100644 index 4570312c689c..000000000000 --- a/arch/v850/kernel/v850e2_cache.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * arch/v850/kernel/v850e2_cache.c -- Cache control for V850E2 cache - * memories - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include - -#include - -/* Cache operations we can do. The encoding corresponds directly to the - value we need to write into the COPR register. */ -enum cache_op { - OP_SYNC_IF_DIRTY = V850E2_CACHE_COPR_CFC(0), /* 000 */ - OP_SYNC_IF_VALID = V850E2_CACHE_COPR_CFC(1), /* 001 */ - OP_SYNC_IF_VALID_AND_CLEAR = V850E2_CACHE_COPR_CFC(3), /* 011 */ - OP_WAY_CLEAR = V850E2_CACHE_COPR_CFC(4), /* 100 */ - OP_FILL = V850E2_CACHE_COPR_CFC(5), /* 101 */ - OP_CLEAR = V850E2_CACHE_COPR_CFC(6), /* 110 */ - OP_CREATE_DIRTY = V850E2_CACHE_COPR_CFC(7) /* 111 */ -}; - -/* Which cache to use. This encoding also corresponds directly to the - value we need to write into the COPR register. */ -enum cache { - ICACHE = 0, - DCACHE = V850E2_CACHE_COPR_LBSL -}; - -/* Returns ADDR rounded down to the beginning of its cache-line. */ -#define CACHE_LINE_ADDR(addr) \ - ((addr) & ~(V850E2_CACHE_LINE_SIZE - 1)) -/* Returns END_ADDR rounded up to the `limit' of its cache-line. */ -#define CACHE_LINE_END_ADDR(end_addr) \ - CACHE_LINE_ADDR(end_addr + (V850E2_CACHE_LINE_SIZE - 1)) - - -/* Low-level cache ops. */ - -/* Apply cache-op OP to all entries in CACHE. */ -static inline void cache_op_all (enum cache_op op, enum cache cache) -{ - int cmd = op | cache | V850E2_CACHE_COPR_WSLE | V850E2_CACHE_COPR_STRT; - - if (op != OP_WAY_CLEAR) { - /* The WAY_CLEAR operation does the whole way, but other - ops take begin-index and count params; we just indicate - the entire cache. */ - V850E2_CACHE_CADL = 0; - V850E2_CACHE_CADH = 0; - V850E2_CACHE_CCNT = V850E2_CACHE_WAY_SIZE - 1; - } - - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(0); /* way 0 */ - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(1); /* way 1 */ - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(2); /* way 2 */ - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(3); /* way 3 */ -} - -/* Apply cache-op OP to all entries in CACHE covering addresses ADDR - through ADDR+LEN. */ -static inline void cache_op_range (enum cache_op op, u32 addr, u32 len, - enum cache cache) -{ - u32 start = CACHE_LINE_ADDR (addr); - u32 end = CACHE_LINE_END_ADDR (addr + len); - u32 num_lines = (end - start) >> V850E2_CACHE_LINE_SIZE_BITS; - - V850E2_CACHE_CADL = start & 0xFFFF; - V850E2_CACHE_CADH = start >> 16; - V850E2_CACHE_CCNT = num_lines - 1; - - V850E2_CACHE_COPR = op | cache | V850E2_CACHE_COPR_STRT; -} - - -/* High-level ops. */ - -static void cache_exec_after_store_all (void) -{ - cache_op_all (OP_SYNC_IF_DIRTY, DCACHE); - cache_op_all (OP_WAY_CLEAR, ICACHE); -} - -static void cache_exec_after_store_range (u32 start, u32 len) -{ - cache_op_range (OP_SYNC_IF_DIRTY, start, len, DCACHE); - cache_op_range (OP_CLEAR, start, len, ICACHE); -} - - -/* Exported functions. */ - -void flush_icache (void) -{ - cache_exec_after_store_all (); -} - -void flush_icache_range (unsigned long start, unsigned long end) -{ - cache_exec_after_store_range (start, end - start); -} - -void flush_icache_page (struct vm_area_struct *vma, struct page *page) -{ - cache_exec_after_store_range (page_to_virt (page), PAGE_SIZE); -} - -void flush_icache_user_range (struct vm_area_struct *vma, struct page *page, - unsigned long addr, int len) -{ - cache_exec_after_store_range (addr, len); -} - -void flush_cache_sigtramp (unsigned long addr) -{ - /* For the exact size, see signal.c, but 16 bytes should be enough. */ - cache_exec_after_store_range (addr, 16); -} diff --git a/arch/v850/kernel/v850e_cache.c b/arch/v850/kernel/v850e_cache.c deleted file mode 100644 index ea3e51cfb259..000000000000 --- a/arch/v850/kernel/v850e_cache.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * arch/v850/kernel/v850e_cache.c -- Cache control for V850E cache memories - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -/* This file implements cache control for the rather simple cache used on - some V850E CPUs, specifically the NB85E/TEG CPU-core and the V850E/ME2 - CPU. V850E2 processors have their own (better) cache - implementation. */ - -#include -#include -#include - -#define WAIT_UNTIL_CLEAR(value) while (value) {} - -/* Set caching params via the BHC and DCC registers. */ -void v850e_cache_enable (u16 bhc, u16 icc, u16 dcc) -{ - unsigned long *r0_ram = (unsigned long *)R0_RAM_ADDR; - register u16 bhc_val asm ("r6") = bhc; - - /* Read the instruction cache control register (ICC) and confirm - that bits 0 and 1 (TCLR0, TCLR1) are all cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); - V850E_CACHE_ICC = icc; - -#ifdef V850E_CACHE_DCC - /* Configure data-cache. */ - V850E_CACHE_DCC = dcc; -#endif /* V850E_CACHE_DCC */ - - /* Configure caching for various memory regions by writing the BHC - register. The documentation says that an instruction _cannot_ - enable/disable caching for the memory region in which the - instruction itself exists; to work around this, we store - appropriate instructions into the on-chip RAM area (which is never - cached), and briefly jump there to do the work. */ -#ifdef V850E_CACHE_WRITE_IBS - *r0_ram++ = 0xf0720760; /* st.h r0, 0xfffff072[r0] */ -#endif - *r0_ram++ = 0xf06a3760; /* st.h r6, 0xfffff06a[r0] */ - *r0_ram = 0x5640006b; /* jmp [r11] */ - - asm ("mov hilo(1f), r11; jmp [%1]; 1:;" - :: "r" (bhc_val), "r" (R0_RAM_ADDR) : "r11"); -} - -static void clear_icache (void) -{ - /* 1. Read the instruction cache control register (ICC) and confirm - that bits 0 and 1 (TCLR0, TCLR1) are all cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); - - /* 2. Read the ICC register and confirm that bit 12 (LOCK0) is - cleared. Bit 13 of the ICC register is always cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x1000); - - /* 3. Set the TCLR0 and TCLR1 bits of the ICC register as follows, - when clearing way 0 and way 1 at the same time: - (a) Set the TCLR0 and TCLR1 bits. - (b) Read the TCLR0 and TCLR1 bits to confirm that these bits - are cleared. - (c) Perform (a) and (b) above again. */ - V850E_CACHE_ICC |= 0x3; - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); - -#ifdef V850E_CACHE_REPEAT_ICC_WRITE - /* Do it again. */ - V850E_CACHE_ICC |= 0x3; - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); -#endif -} - -#ifdef V850E_CACHE_DCC -/* Flush or clear (or both) the data cache, depending on the value of FLAGS; - the procedure is the same for both, just the control bits used differ (and - both may be performed simultaneously). */ -static void dcache_op (unsigned short flags) -{ - /* 1. Read the data cache control register (DCC) and confirm that bits - 0, 1, 4, and 5 (DC00, DC01, DC04, DC05) are all cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_DCC & 0x33); - - /* 2. Clear DCC register bit 12 (DC12), bit 13 (DC13), or both - depending on the way for which tags are to be cleared. */ - V850E_CACHE_DCC &= ~0xC000; - - /* 3. Set DCC register bit 0 (DC00), bit 1 (DC01) or both depending on - the way for which tags are to be cleared. - ... - Set DCC register bit 4 (DC04), bit 5 (DC05), or both depending - on the way to be data flushed. */ - V850E_CACHE_DCC |= flags; - - /* 4. Read DCC register bit DC00, DC01 [DC04, DC05], or both depending - on the way for which tags were cleared [flushed] and confirm - that that bit is cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_DCC & flags); -} -#endif /* V850E_CACHE_DCC */ - -/* Flushes the contents of the dcache to memory. */ -static inline void flush_dcache (void) -{ -#ifdef V850E_CACHE_DCC - /* We only need to do something if in write-back mode. */ - if (V850E_CACHE_DCC & 0x0400) - dcache_op (0x30); -#endif /* V850E_CACHE_DCC */ -} - -/* Flushes the contents of the dcache to memory, and then clears it. */ -static inline void clear_dcache (void) -{ -#ifdef V850E_CACHE_DCC - /* We only need to do something if the dcache is enabled. */ - if (V850E_CACHE_DCC & 0x0C00) - dcache_op (0x33); -#endif /* V850E_CACHE_DCC */ -} - -/* Clears the dcache without flushing to memory first. */ -static inline void clear_dcache_no_flush (void) -{ -#ifdef V850E_CACHE_DCC - /* We only need to do something if the dcache is enabled. */ - if (V850E_CACHE_DCC & 0x0C00) - dcache_op (0x3); -#endif /* V850E_CACHE_DCC */ -} - -static inline void cache_exec_after_store (void) -{ - flush_dcache (); - clear_icache (); -} - - -/* Exported functions. */ - -void flush_icache (void) -{ - cache_exec_after_store (); -} - -void flush_icache_range (unsigned long start, unsigned long end) -{ - cache_exec_after_store (); -} - -void flush_icache_page (struct vm_area_struct *vma, struct page *page) -{ - cache_exec_after_store (); -} - -void flush_icache_user_range (struct vm_area_struct *vma, struct page *page, - unsigned long adr, int len) -{ - cache_exec_after_store (); -} - -void flush_cache_sigtramp (unsigned long addr) -{ - cache_exec_after_store (); -} diff --git a/arch/v850/kernel/v850e_intc.c b/arch/v850/kernel/v850e_intc.c deleted file mode 100644 index 8d39a52ee6d1..000000000000 --- a/arch/v850/kernel/v850e_intc.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * arch/v850/kernel/v850e_intc.c -- V850E interrupt controller (INTC) - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include - -#include - -static void irq_nop (unsigned irq) { } - -static unsigned v850e_intc_irq_startup (unsigned irq) -{ - v850e_intc_clear_pending_irq (irq); - v850e_intc_enable_irq (irq); - return 0; -} - -static void v850e_intc_end_irq (unsigned irq) -{ - unsigned long psw, temp; - - /* Clear the highest-level bit in the In-service priority register - (ISPR), to allow this interrupt (or another of the same or - lesser priority) to happen again. - - The `reti' instruction normally does this automatically when the - PSW bits EP and NP are zero, but we can't always rely on reti - being used consistently to return after an interrupt (another - process can be scheduled, for instance, which can delay the - associated reti for a long time, or this process may be being - single-stepped, which uses the `dbret' instruction to return - from the kernel). - - We also set the PSW EP bit, which prevents reti from also - trying to modify the ISPR itself. */ - - /* Get PSW and disable interrupts. */ - asm volatile ("stsr psw, %0; di" : "=r" (psw)); - /* We don't want to do anything for NMIs (they don't use the ISPR). */ - if (! (psw & 0xC0)) { - /* Transition to `trap' state, so that an eventual real - reti instruction won't modify the ISPR. */ - psw |= 0x40; - /* Fake an interrupt return, which automatically clears the - appropriate bit in the ISPR. */ - asm volatile ("mov hilo(1f), %0;" - "ldsr %0, eipc; ldsr %1, eipsw;" - "reti;" - "1:" - : "=&r" (temp) : "r" (psw)); - } -} - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -void __init v850e_intc_init_irq_types (struct v850e_intc_irq_init *inits, - struct hw_interrupt_type *hw_irq_types) -{ - struct v850e_intc_irq_init *init; - for (init = inits; init->name; init++) { - unsigned i; - struct hw_interrupt_type *hwit = hw_irq_types++; - - hwit->typename = init->name; - - hwit->startup = v850e_intc_irq_startup; - hwit->shutdown = v850e_intc_disable_irq; - hwit->enable = v850e_intc_enable_irq; - hwit->disable = v850e_intc_disable_irq; - hwit->ack = irq_nop; - hwit->end = v850e_intc_end_irq; - - /* Initialize kernel IRQ infrastructure for this interrupt. */ - init_irq_handlers(init->base, init->num, init->interval, hwit); - - /* Set the interrupt priorities. */ - for (i = 0; i < init->num; i++) { - unsigned irq = init->base + i * init->interval; - - /* If the interrupt is currently enabled (all - interrupts are initially disabled), then - assume whoever enabled it has set things up - properly, and avoid messing with it. */ - if (! v850e_intc_irq_enabled (irq)) - /* This write also (1) disables the - interrupt, and (2) clears any pending - interrupts. */ - V850E_INTC_IC (irq) - = (V850E_INTC_IC_PR (init->priority) - | V850E_INTC_IC_MK); - } - } -} diff --git a/arch/v850/kernel/v850e_timer_d.c b/arch/v850/kernel/v850e_timer_d.c deleted file mode 100644 index d2a4ece2574c..000000000000 --- a/arch/v850/kernel/v850e_timer_d.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * include/asm-v850/v850e_timer_d.c -- `Timer D' component often used - * with V850E CPUs - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include - -#include -#include - -/* Start interval timer TIMER (0-3). The timer will issue the - corresponding INTCMD interrupt RATE times per second. - This function does not enable the interrupt. */ -void v850e_timer_d_configure (unsigned timer, unsigned rate) -{ - unsigned divlog2, count; - - /* Calculate params for timer. */ - if (! calc_counter_params ( - V850E_TIMER_D_BASE_FREQ, rate, - V850E_TIMER_D_TMCD_CS_MIN, V850E_TIMER_D_TMCD_CS_MAX, 16, - &divlog2, &count)) - printk (KERN_WARNING - "Cannot find interval timer %d setting suitable" - " for rate of %dHz.\n" - "Using rate of %dHz instead.\n", - timer, rate, - (V850E_TIMER_D_BASE_FREQ >> divlog2) >> 16); - - /* Do the actual hardware timer initialization: */ - - /* Enable timer. */ - V850E_TIMER_D_TMCD(timer) = V850E_TIMER_D_TMCD_CAE; - /* Set clock divider. */ - V850E_TIMER_D_TMCD(timer) - = V850E_TIMER_D_TMCD_CAE - | V850E_TIMER_D_TMCD_CS(divlog2); - /* Set timer compare register. */ - V850E_TIMER_D_CMD(timer) = count; - /* Start counting. */ - V850E_TIMER_D_TMCD(timer) - = V850E_TIMER_D_TMCD_CAE - | V850E_TIMER_D_TMCD_CS(divlog2) - | V850E_TIMER_D_TMCD_CE; -} diff --git a/arch/v850/kernel/v850e_utils.c b/arch/v850/kernel/v850e_utils.c deleted file mode 100644 index e6807ef8dee6..000000000000 --- a/arch/v850/kernel/v850e_utils.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * include/asm-v850/v850e_utils.h -- Utility functions associated with - * V850E CPUs - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include - -/* Calculate counter clock-divider and count values to attain the - desired frequency RATE from the base frequency BASE_FREQ. The - counter is expected to have a clock-divider, which can divide the - system cpu clock by a power of two value from MIN_DIVLOG2 to - MAX_DIV_LOG2, and a word-size of COUNTER_SIZE bits (the counter - counts up and resets whenever it's equal to the compare register, - generating an interrupt or whatever when it does so). The returned - values are: *DIVLOG2 -- log2 of the desired clock divider and *COUNT - -- the counter compare value to use. Returns true if it was possible - to find a reasonable value, otherwise false (and the other return - values will be set to be as good as possible). */ -int calc_counter_params (unsigned long base_freq, - unsigned long rate, - unsigned min_divlog2, unsigned max_divlog2, - unsigned counter_size, - unsigned *divlog2, unsigned *count) -{ - unsigned _divlog2; - int ok = 0; - - /* Find the lowest clock divider setting that can represent RATE. */ - for (_divlog2 = min_divlog2; _divlog2 <= max_divlog2; _divlog2++) { - /* Minimum interrupt rate possible using this divider. */ - unsigned min_int_rate - = (base_freq >> _divlog2) >> counter_size; - - if (min_int_rate <= rate) { - /* This setting is the highest resolution - setting that's slow enough enough to attain - RATE interrupts per second, so use it. */ - ok = 1; - break; - } - } - - if (_divlog2 > max_divlog2) - /* Can't find correct setting. */ - _divlog2 = max_divlog2; - - if (divlog2) - *divlog2 = _divlog2; - if (count) - *count = ((base_freq >> _divlog2) + rate/2) / rate; - - return ok; -} diff --git a/arch/v850/kernel/vmlinux.lds.S b/arch/v850/kernel/vmlinux.lds.S deleted file mode 100644 index d08cd1d27f27..000000000000 --- a/arch/v850/kernel/vmlinux.lds.S +++ /dev/null @@ -1,306 +0,0 @@ -/* - * arch/v850/vmlinux.lds.S -- kernel linker script for v850 platforms - * - * Copyright (C) 2002,03,04,05 NEC Electronics Corporation - * Copyright (C) 2002,03,04,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - - -#define VMLINUX_SYMBOL(_sym_) _##_sym_ -#include - -/* For most platforms, this will define useful things like RAM addr/size. */ -#include - - -/* The following macros contain the usual definitions for various data areas. - The prefix `RAMK_' is used to indicate macros suitable for kernels loaded - into RAM, and similarly `ROMK_' for ROM-resident kernels. Note that all - symbols are prefixed with an extra `_' for compatibility with the v850 - toolchain. */ - - -/* Interrupt vectors. */ -#define INTV_CONTENTS \ - . = ALIGN (0x10) ; \ - __intv_start = . ; \ - *(.intv.reset) /* Reset vector */ \ - . = __intv_start + 0x10 ; \ - *(.intv.common) /* Vectors common to all v850e proc */\ - . = __intv_start + 0x80 ; \ - *(.intv.mach) /* Machine-specific int. vectors. */ \ - __intv_end = . ; - -#define RODATA_CONTENTS \ - . = ALIGN (16) ; \ - *(.rodata) *(.rodata.*) \ - *(__vermagic) /* Kernel version magic */ \ - *(.rodata1) \ - /* PCI quirks */ \ - ___start_pci_fixups_early = . ; \ - *(.pci_fixup_early) \ - ___end_pci_fixups_early = . ; \ - ___start_pci_fixups_header = . ; \ - *(.pci_fixup_header) \ - ___end_pci_fixups_header = . ; \ - ___start_pci_fixups_final = . ; \ - *(.pci_fixup_final) \ - ___end_pci_fixups_final = . ; \ - ___start_pci_fixups_enable = . ; \ - *(.pci_fixup_enable) \ - ___end_pci_fixups_enable = . ; \ - /* Kernel symbol table: Normal symbols */ \ - ___start___ksymtab = .; \ - *(__ksymtab) \ - ___stop___ksymtab = .; \ - /* Kernel symbol table: GPL-only symbols */ \ - ___start___ksymtab_gpl = .; \ - *(__ksymtab_gpl) \ - ___stop___ksymtab_gpl = .; \ - /* Kernel symbol table: GPL-future symbols */ \ - ___start___ksymtab_gpl_future = .; \ - *(__ksymtab_gpl_future) \ - ___stop___ksymtab_gpl_future = .; \ - /* Kernel symbol table: strings */ \ - *(__ksymtab_strings) \ - /* Kernel symbol table: Normal symbols */ \ - ___start___kcrctab = .; \ - *(__kcrctab) \ - ___stop___kcrctab = .; \ - /* Kernel symbol table: GPL-only symbols */ \ - ___start___kcrctab_gpl = .; \ - *(__kcrctab_gpl) \ - ___stop___kcrctab_gpl = .; \ - /* Kernel symbol table: GPL-future symbols */ \ - ___start___kcrctab_gpl_future = .; \ - *(__kcrctab_gpl_future) \ - ___stop___kcrctab_gpl_future = .; \ - /* Built-in module parameters */ \ - . = ALIGN (4) ; \ - ___start___param = .; \ - *(__param) \ - ___stop___param = .; - - -/* Kernel text segment, and some constant data areas. */ -#define TEXT_CONTENTS \ - _text = .; \ - __stext = . ; \ - TEXT_TEXT \ - SCHED_TEXT \ - *(.exit.text) /* 2.5 convention */ \ - *(.text.exit) /* 2.4 convention */ \ - *(.text.lock) \ - *(.exitcall.exit) \ - __real_etext = . ; /* There may be data after here. */ \ - RODATA_CONTENTS \ - . = ALIGN (4) ; \ - *(.call_table_data) \ - *(.call_table_text) \ - . = ALIGN (16) ; /* Exception table. */ \ - ___start___ex_table = . ; \ - *(__ex_table) \ - ___stop___ex_table = . ; \ - . = ALIGN (4) ; \ - __etext = . ; - -/* Kernel data segment. */ -#define DATA_CONTENTS \ - __sdata = . ; \ - DATA_DATA \ - EXIT_DATA /* 2.5 convention */ \ - *(.data.exit) /* 2.4 convention */ \ - . = ALIGN (16) ; \ - *(.data.cacheline_aligned) \ - . = ALIGN (0x2000) ; \ - *(.data.init_task) \ - . = ALIGN (0x2000) ; \ - __edata = . ; - -/* Kernel BSS segment. */ -#define BSS_CONTENTS \ - __sbss = . ; \ - *(.bss) \ - *(COMMON) \ - . = ALIGN (4) ; \ - __init_stack_end = . ; \ - __ebss = . ; - -/* `initcall' tables. */ -#define INITCALL_CONTENTS \ - . = ALIGN (16) ; \ - ___setup_start = . ; \ - *(.init.setup) /* 2.5 convention */ \ - *(.setup.init) /* 2.4 convention */ \ - ___setup_end = . ; \ - ___initcall_start = . ; \ - *(.initcall.init) \ - INITCALLS \ - . = ALIGN (4) ; \ - ___initcall_end = . ; \ - ___con_initcall_start = .; \ - *(.con_initcall.init) \ - ___con_initcall_end = .; - -/* Contents of `init' section for a kernel that's loaded into RAM. */ -#define RAMK_INIT_CONTENTS \ - RAMK_INIT_CONTENTS_NO_END \ - __init_end = . ; -/* Same as RAMK_INIT_CONTENTS, but doesn't define the `__init_end' symbol. */ -#define RAMK_INIT_CONTENTS_NO_END \ - . = ALIGN (4096) ; \ - __init_start = . ; \ - __sinittext = .; \ - INIT_TEXT /* 2.5 convention */ \ - __einittext = .; \ - INIT_DATA \ - *(.text.init) /* 2.4 convention */ \ - *(.data.init) \ - INITCALL_CONTENTS \ - INITRAMFS_CONTENTS - -/* The contents of `init' section for a ROM-resident kernel which - should go into RAM. */ -#define ROMK_INIT_RAM_CONTENTS \ - . = ALIGN (4096) ; \ - __init_start = . ; \ - INIT_DATA /* 2.5 convention */ \ - *(.data.init) /* 2.4 convention */ \ - __init_end = . ; \ - . = ALIGN (4096) ; - -/* The contents of `init' section for a ROM-resident kernel which - should go into ROM. */ -#define ROMK_INIT_ROM_CONTENTS \ - _sinittext = .; \ - INIT_TEXT /* 2.5 convention */ \ - _einittext = .; \ - *(.text.init) /* 2.4 convention */ \ - INITCALL_CONTENTS \ - INITRAMFS_CONTENTS - -/* A root filesystem image, for kernels with an embedded root filesystem. */ -#define ROOT_FS_CONTENTS \ - __root_fs_image_start = . ; \ - *(.root) \ - __root_fs_image_end = . ; - -#ifdef CONFIG_BLK_DEV_INITRD -/* The initramfs archive. */ -#define INITRAMFS_CONTENTS \ - . = ALIGN (4) ; \ - ___initramfs_start = . ; \ - *(.init.ramfs) \ - ___initramfs_end = . ; -#endif - -/* Where the initial bootmap (bitmap for the boot-time memory allocator) - should be place. */ -#define BOOTMAP_CONTENTS \ - . = ALIGN (4096) ; \ - __bootmap = . ; \ - . = . + 4096 ; /* enough for 128MB. */ - -/* The contents of a `typical' kram area for a kernel in RAM. */ -#define RAMK_KRAM_CONTENTS \ - __kram_start = . ; \ - TEXT_CONTENTS \ - DATA_CONTENTS \ - BSS_CONTENTS \ - RAMK_INIT_CONTENTS \ - __kram_end = . ; \ - BOOTMAP_CONTENTS - - -/* Define output sections normally used for a ROM-resident kernel. - ROM and RAM should be appropriate memory areas to use for kernel - ROM and RAM data. This assumes that ROM starts at 0 (and thus can - hold the interrupt vectors). */ -#define ROMK_SECTIONS(ROM, RAM) \ - .rom : { \ - INTV_CONTENTS \ - TEXT_CONTENTS \ - ROMK_INIT_ROM_CONTENTS \ - ROOT_FS_CONTENTS \ - } > ROM \ - \ - __rom_copy_src_start = . ; \ - \ - .data : { \ - __kram_start = . ; \ - __rom_copy_dst_start = . ; \ - DATA_CONTENTS \ - ROMK_INIT_RAM_CONTENTS \ - __rom_copy_dst_end = . ; \ - } > RAM AT> ROM \ - \ - .bss ALIGN (4) : { \ - BSS_CONTENTS \ - __kram_end = . ; \ - BOOTMAP_CONTENTS \ - } > RAM - - -/* The 32-bit variable `jiffies' is just the lower 32-bits of `jiffies_64'. */ -_jiffies = _jiffies_64 ; - - -/* Include an appropriate platform-dependent linker-script (which - usually should use the above macros to do most of the work). */ - -#ifdef CONFIG_V850E_SIM -# include "sim.ld" -#endif - -#ifdef CONFIG_V850E2_SIM85E2 -# include "sim85e2.ld" -#endif - -#ifdef CONFIG_V850E2_FPGA85E2C -# include "fpga85e2c.ld" -#endif - -#ifdef CONFIG_V850E2_ANNA -# ifdef CONFIG_ROM_KERNEL -# include "anna-rom.ld" -# else -# include "anna.ld" -# endif -#endif - -#ifdef CONFIG_V850E_AS85EP1 -# ifdef CONFIG_ROM_KERNEL -# include "as85ep1-rom.ld" -# else -# include "as85ep1.ld" -# endif -#endif - -#ifdef CONFIG_RTE_CB_MA1 -# ifdef CONFIG_ROM_KERNEL -# include "rte_ma1_cb-rom.ld" -# else -# include "rte_ma1_cb.ld" -# endif -#endif - -#ifdef CONFIG_RTE_CB_NB85E -# ifdef CONFIG_ROM_KERNEL -# include "rte_nb85e_cb-rom.ld" -# elif defined(CONFIG_RTE_CB_MULTI) -# include "rte_nb85e_cb-multi.ld" -# else -# include "rte_nb85e_cb.ld" -# endif -#endif - -#ifdef CONFIG_RTE_CB_ME2 -# include "rte_me2_cb.ld" -#endif - diff --git a/arch/v850/lib/Makefile b/arch/v850/lib/Makefile deleted file mode 100644 index 1c78b728a117..000000000000 --- a/arch/v850/lib/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# -# arch/v850/lib/Makefile -# - -lib-y = ashrdi3.o ashldi3.o lshrdi3.o muldi3.o negdi2.o \ - checksum.o memcpy.o memset.o diff --git a/arch/v850/lib/ashldi3.c b/arch/v850/lib/ashldi3.c deleted file mode 100644 index 9e792d53f0e4..000000000000 --- a/arch/v850/lib/ashldi3.c +++ /dev/null @@ -1,62 +0,0 @@ -/* ashldi3.c extracted from gcc-2.95.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__ashldi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.low = 0; - w.s.high = (USItype)uu.s.low << -bm; - } - else - { - USItype carries = (USItype)uu.s.low >> bm; - w.s.low = (USItype)uu.s.low << b; - w.s.high = ((USItype)uu.s.high << b) | carries; - } - - return w.ll; -} diff --git a/arch/v850/lib/ashrdi3.c b/arch/v850/lib/ashrdi3.c deleted file mode 100644 index 78efb65e315a..000000000000 --- a/arch/v850/lib/ashrdi3.c +++ /dev/null @@ -1,63 +0,0 @@ -/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__ashrdi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - /* w.s.high = 1..1 or 0..0 */ - w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1); - w.s.low = uu.s.high >> -bm; - } - else - { - USItype carries = (USItype)uu.s.high << bm; - w.s.high = uu.s.high >> b; - w.s.low = ((USItype)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/arch/v850/lib/checksum.c b/arch/v850/lib/checksum.c deleted file mode 100644 index 042158dfe17a..000000000000 --- a/arch/v850/lib/checksum.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * MIPS specific IP/TCP/UDP checksumming routines - * - * Authors: Ralf Baechle, - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - * - * 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. - * - * $Id: checksum.c,v 1.1 2002/09/28 14:58:40 gerg Exp $ - */ -#include -#include -#include -#include -#include -#include - -static inline unsigned short from32to16 (unsigned long sum) -{ - unsigned int result; - /* - %0 %1 - hsw %1, %0 H L L H - add %1, %0 H L H+L+C H+L - */ - asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum)); - return result >> 16; -} - -static inline unsigned int do_csum(const unsigned char * buff, int len) -{ - int odd, count; - unsigned int result = 0; - - if (len <= 0) - goto out; - odd = 1 & (unsigned long) buff; - if (odd) { - result = be16_to_cpu(*buff); - len--; - buff++; - } - count = len >> 1; /* nr of 16-bit words.. */ - if (count) { - if (2 & (unsigned long) buff) { - result += *(unsigned short *) buff; - count--; - len -= 2; - buff += 2; - } - count >>= 1; /* nr of 32-bit words.. */ - if (count) { - unsigned int carry = 0; - do { - unsigned int w = *(unsigned int *) buff; - count--; - buff += 4; - result += carry; - result += w; - carry = (w > result); - } while (count); - result += carry; - result = (result & 0xffff) + (result >> 16); - } - if (len & 2) { - result += *(unsigned short *) buff; - buff += 2; - } - } - if (len & 1) - result += le16_to_cpu(*buff); - result = from32to16(result); - if (odd) - result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); -out: - return result; -} - -/* - * This is a version of ip_compute_csum() optimized for IP headers, - * which always checksum on 4 octet boundaries. - */ -__sum16 ip_fast_csum(const void *iph, unsigned int ihl) -{ - return (__force __sum16)~do_csum(iph,ihl*4); -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -__sum16 ip_compute_csum(const void *buff, int len) -{ - return (__force __sum16)~do_csum(buff,len); -} - -/* - * computes a partial checksum, e.g. for TCP/UDP fragments - */ -__wsum csum_partial(const void *buff, int len, __wsum sum) -{ - unsigned int result = do_csum(buff, len); - - /* add in old sum, and carry.. */ - result += (__force u32)sum; - if ((__force u32)sum > result) - result += 1; - return (__force __wsum)result; -} - -EXPORT_SYMBOL(csum_partial); - -/* - * copy while checksumming, otherwise like csum_partial - */ -__wsum csum_partial_copy_nocheck(const void *src, void *dst, - int len, __wsum sum) -{ - /* - * It's 2:30 am and I don't feel like doing it real ... - * This is lots slower than the real thing (tm) - */ - sum = csum_partial(src, len, sum); - memcpy(dst, src, len); - - return sum; -} - -/* - * Copy from userspace and compute checksum. If we catch an exception - * then zero the rest of the buffer. - */ -__wsum csum_partial_copy_from_user (const void *src, - void *dst, - int len, __wsum sum, - int *err_ptr) -{ - int missing; - - missing = copy_from_user(dst, src, len); - if (missing) { - memset(dst + len - missing, 0, missing); - *err_ptr = -EFAULT; - } - - return csum_partial(dst, len, sum); -} diff --git a/arch/v850/lib/lshrdi3.c b/arch/v850/lib/lshrdi3.c deleted file mode 100644 index 93b1cb6fdee8..000000000000 --- a/arch/v850/lib/lshrdi3.c +++ /dev/null @@ -1,62 +0,0 @@ -/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__lshrdi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.high = 0; - w.s.low = (USItype)uu.s.high >> -bm; - } - else - { - USItype carries = (USItype)uu.s.high << bm; - w.s.high = (USItype)uu.s.high >> b; - w.s.low = ((USItype)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/arch/v850/lib/memcpy.c b/arch/v850/lib/memcpy.c deleted file mode 100644 index 492847b3e612..000000000000 --- a/arch/v850/lib/memcpy.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * arch/v850/lib/memcpy.c -- Memory copying - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include -#include - -#define CHUNK_SIZE 32 /* bytes */ -#define CHUNK_ALIGNED(addr) (((unsigned long)addr & 0x3) == 0) - -/* Note that this macro uses 8 call-clobbered registers (not including - R1), which are few enough so that the following functions don't need - to spill anything to memory. It also uses R1, which is nominally - reserved for the assembler, but here it should be OK. */ -#define COPY_CHUNK(src, dst) \ - asm ("mov %0, ep;" \ - "sld.w 0[ep], r1; sld.w 4[ep], r12;" \ - "sld.w 8[ep], r13; sld.w 12[ep], r14;" \ - "sld.w 16[ep], r15; sld.w 20[ep], r17;" \ - "sld.w 24[ep], r18; sld.w 28[ep], r19;" \ - "mov %1, ep;" \ - "sst.w r1, 0[ep]; sst.w r12, 4[ep];" \ - "sst.w r13, 8[ep]; sst.w r14, 12[ep];" \ - "sst.w r15, 16[ep]; sst.w r17, 20[ep];" \ - "sst.w r18, 24[ep]; sst.w r19, 28[ep]" \ - :: "r" (src), "r" (dst) \ - : "r1", "r12", "r13", "r14", "r15", \ - "r17", "r18", "r19", "ep", "memory"); - -void *memcpy (void *dst, const void *src, __kernel_size_t size) -{ - char *_dst = dst; - const char *_src = src; - - if (size >= CHUNK_SIZE && CHUNK_ALIGNED(_src) && CHUNK_ALIGNED(_dst)) { - /* Copy large blocks efficiently. */ - unsigned count; - for (count = size / CHUNK_SIZE; count; count--) { - COPY_CHUNK (_src, _dst); - _src += CHUNK_SIZE; - _dst += CHUNK_SIZE; - } - size %= CHUNK_SIZE; - } - - if (size > 0) - do - *_dst++ = *_src++; - while (--size); - - return dst; -} - -void *memmove (void *dst, const void *src, __kernel_size_t size) -{ - if ((unsigned long)dst < (unsigned long)src - || (unsigned long)src + size < (unsigned long)dst) - return memcpy (dst, src, size); - else { - char *_dst = dst + size; - const char *_src = src + size; - - if (size >= CHUNK_SIZE - && CHUNK_ALIGNED (_src) && CHUNK_ALIGNED (_dst)) - { - /* Copy large blocks efficiently. */ - unsigned count; - for (count = size / CHUNK_SIZE; count; count--) { - _src -= CHUNK_SIZE; - _dst -= CHUNK_SIZE; - COPY_CHUNK (_src, _dst); - } - size %= CHUNK_SIZE; - } - - if (size > 0) - do - *--_dst = *--_src; - while (--size); - - return _dst; - } -} diff --git a/arch/v850/lib/memset.c b/arch/v850/lib/memset.c deleted file mode 100644 index d1b2ad821b15..000000000000 --- a/arch/v850/lib/memset.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * arch/v850/lib/memset.c -- Memory initialization - * - * Copyright (C) 2001,02,04 NEC Corporation - * Copyright (C) 2001,02,04 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#include - -void *memset (void *dst, int val, __kernel_size_t count) -{ - if (count) { - register unsigned loop; - register void *ptr asm ("ep") = dst; - - /* replicate VAL into a long. */ - val &= 0xff; - val |= val << 8; - val |= val << 16; - - /* copy initial unaligned bytes. */ - if ((long)ptr & 1) { - *(char *)ptr = val; - ptr = (void *)((char *)ptr + 1); - count--; - } - if (count > 2 && ((long)ptr & 2)) { - *(short *)ptr = val; - ptr = (void *)((short *)ptr + 1); - count -= 2; - } - - /* 32-byte copying loop. */ - for (loop = count / 32; loop; loop--) { - asm ("sst.w %0, 0[ep]; sst.w %0, 4[ep];" - "sst.w %0, 8[ep]; sst.w %0, 12[ep];" - "sst.w %0, 16[ep]; sst.w %0, 20[ep];" - "sst.w %0, 24[ep]; sst.w %0, 28[ep]" - :: "r" (val) : "memory"); - ptr += 32; - } - count %= 32; - - /* long copying loop. */ - for (loop = count / 4; loop; loop--) { - *(long *)ptr = val; - ptr = (void *)((long *)ptr + 1); - } - count %= 4; - - /* finish up with any trailing bytes. */ - if (count & 2) { - *(short *)ptr = val; - ptr = (void *)((short *)ptr + 1); - } - if (count & 1) { - *(char *)ptr = val; - } - } - - return dst; -} diff --git a/arch/v850/lib/muldi3.c b/arch/v850/lib/muldi3.c deleted file mode 100644 index 277ca25c82c8..000000000000 --- a/arch/v850/lib/muldi3.c +++ /dev/null @@ -1,61 +0,0 @@ -/* muldi3.c extracted from gcc-2.7.2.3/libgcc2.c and - gcc-2.7.2.3/longlong.h which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 2001 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define umul_ppmm(w1, w0, u, v) \ - __asm__ ("mulu %3, %0, %1" \ - : "=r" ((USItype)(w0)), \ - "=r" ((USItype)(w1)) \ - : "%0" ((USItype)(u)), \ - "r" ((USItype)(v))) - -#define __umulsidi3(u, v) \ - ({DIunion __w; \ - umul_ppmm (__w.s.high, __w.s.low, u, v); \ - __w.ll; }) - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__muldi3 (DItype u, DItype v) -{ - DIunion w; - DIunion uu, vv; - - uu.ll = u, - vv.ll = v; - - w.ll = __umulsidi3 (uu.s.low, vv.s.low); - w.s.high += ((USItype) uu.s.low * (USItype) vv.s.high - + (USItype) uu.s.high * (USItype) vv.s.low); - - return w.ll; -} diff --git a/arch/v850/lib/negdi2.c b/arch/v850/lib/negdi2.c deleted file mode 100644 index 571e04fc619a..000000000000 --- a/arch/v850/lib/negdi2.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * arch/v850/lib/negdi2.c -- 64-bit negation - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -typedef int DItype __attribute__ ((mode (DI))); - -DItype __negdi2 (DItype x) -{ - __asm__ __volatile__ - ("not r6, r10;" - "add 1, r10;" - "setf c, r6;" - "not r7, r11;" - "add r6, r11" - ::: "r6", "r7", "r10", "r11"); -} diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8fc7451c0049..3b4a14e355c1 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -942,22 +942,6 @@ config SERIAL_IP22_ZILOG_CONSOLE depends on SERIAL_IP22_ZILOG=y select SERIAL_CORE_CONSOLE -config V850E_UART - bool "NEC V850E on-chip UART support" - depends on V850E_MA1 || V850E_ME2 || V850E_TEG || V850E2_ANNA || V850E_AS85EP1 - select SERIAL_CORE - default y - -config V850E_UARTB - bool - depends on V850E_UART && V850E_ME2 - default y - -config V850E_UART_CONSOLE - bool "Use NEC V850E on-chip UART for console" - depends on V850E_UART - select SERIAL_CORE_CONSOLE - config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" depends on SUPERH || H8300 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ccb78f66c2b6..48399e134c0d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -788,8 +788,6 @@ config WATCHDOG_RIO machines. The watchdog timeout period is normally one minute but can be changed with a boot-time parameter. -# V850 Architecture - # XTENSA Architecture # diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 25b352b664d9..edd305a64e63 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -119,8 +119,6 @@ obj-$(CONFIG_SH_WDT) += shwdt.o # SPARC64 Architecture -# V850 Architecture - # XTENSA Architecture # Architecture Independant diff --git a/include/asm-v850/Kbuild b/include/asm-v850/Kbuild deleted file mode 100644 index c68e1680da01..000000000000 --- a/include/asm-v850/Kbuild +++ /dev/null @@ -1 +0,0 @@ -include include/asm-generic/Kbuild.asm diff --git a/include/asm-v850/a.out.h b/include/asm-v850/a.out.h deleted file mode 100644 index e9439a0708f6..000000000000 --- a/include/asm-v850/a.out.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __V850_A_OUT_H__ -#define __V850_A_OUT_H__ - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - - -#endif /* __V850_A_OUT_H__ */ diff --git a/include/asm-v850/anna.h b/include/asm-v850/anna.h deleted file mode 100644 index cd5eaee103b0..000000000000 --- a/include/asm-v850/anna.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * include/asm-v850/anna.h -- Anna V850E2 evaluation cpu chip/board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ANNA_H__ -#define __V850_ANNA_H__ - -#include /* Based on V850E2 core. */ - - -#define CPU_MODEL "v850e2/anna" -#define CPU_MODEL_LONG "NEC V850E2/Anna" -#define PLATFORM "anna" -#define PLATFORM_LONG "NEC/Midas lab V850E2/Anna evaluation board" - -#define CPU_CLOCK_FREQ 200000000 /* 200MHz */ -#define SYS_CLOCK_FREQ 33300000 /* 33.3MHz */ - - -/* 1MB of static RAM. This memory is mirrored 64 times. */ -#define SRAM_ADDR 0x04000000 -#define SRAM_SIZE 0x00100000 /* 1MB */ -/* 64MB of DRAM. */ -#define SDRAM_ADDR 0x08000000 -#define SDRAM_SIZE 0x04000000 /* 64MB */ - - -/* For */ -#define PAGE_OFFSET SRAM_ADDR - -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. The Anna chip has - 128K of `dLB' ram nominally located at 0xFFF00000, but it's mirrored - every 128K, so we can use the `last mirror' (except for the portion at - the top which is overridden by I/O space). In addition, the early - sample chip we're using has lots of memory errors in the dLB ram, so we - use a specially chosen location that has at least 20 bytes of contiguous - valid memory (xxxF0020 - xxxF003F). */ -#define R0_RAM_ADDR 0xFFFF8020 - - -/* Anna specific control registers. */ -#define ANNA_ILBEN_ADDR 0xFFFFF7F2 -#define ANNA_ILBEN (*(volatile u16 *)ANNA_ILBEN_ADDR) - - -/* I/O port P0-P3. */ -/* Direct I/O. Bits 0-7 are pins Pn0-Pn7. */ -#define ANNA_PORT_IO_ADDR(n) (0xFFFFF400 + (n) * 2) -#define ANNA_PORT_IO(n) (*(volatile u8 *)ANNA_PORT_IO_ADDR(n)) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ANNA_PORT_PM_ADDR(n) (0xFFFFF410 + (n) * 2) -#define ANNA_PORT_PM(n) (*(volatile u8 *)ANNA_PORT_PM_ADDR(n)) - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTP(n) (n) /* Pnnn (pin) interrupts 0-15 */ -#define IRQ_INTP_NUM 16 -#define IRQ_INTOV(n) (0x10 + (n)) /* 0-2 */ -#define IRQ_INTOV_NUM 2 -#define IRQ_INTCCC(n) (0x12 + (n)) -#define IRQ_INTCCC_NUM 4 -#define IRQ_INTCMD(n) (0x16 + (n)) /* interval timer interrupts 0-5 */ -#define IRQ_INTCMD_NUM 6 -#define IRQ_INTDMA(n) (0x1C + (n)) /* DMA interrupts 0-3 */ -#define IRQ_INTDMA_NUM 4 -#define IRQ_INTDMXER 0x20 -#define IRQ_INTSRE(n) (0x21 + (n)*3) /* UART 0-1 reception error */ -#define IRQ_INTSRE_NUM 2 -#define IRQ_INTSR(n) (0x22 + (n)*3) /* UART 0-1 reception completion */ -#define IRQ_INTSR_NUM 2 -#define IRQ_INTST(n) (0x23 + (n)*3) /* UART 0-1 transmission completion */ -#define IRQ_INTST_NUM 2 - -#define NUM_CPU_IRQS 64 - -#ifndef __ASSEMBLY__ -/* Initialize chip interrupts. */ -extern void anna_init_irqs (void); -#endif - - -/* Anna UART details (basically the same as the V850E/MA1, but 2 channels). */ -#define V850E_UART_NUM_CHANNELS 2 -#define V850E_UART_BASE_FREQ (SYS_CLOCK_FREQ / 2) -#define V850E_UART_CHIP_NAME "V850E2/NA85E2A" - -/* This is the UART channel that's actually connected on the board. */ -#define V850E_UART_CONSOLE_CHANNEL 1 - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE anna_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void anna_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 1. */ - -/* CTS for UART channel 1 is pin P37 (bit 7 of port 3). */ -#define V850E_UART_CTS(chan) ((chan) == 1 ? !(ANNA_PORT_IO(3) & 0x80) : 1) -/* RTS for UART channel 1 is pin P07 (bit 7 of port 0). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 1) { \ - unsigned old = ANNA_PORT_IO(0); \ - if (val) \ - ANNA_PORT_IO(0) = old & ~0x80; \ - else \ - ANNA_PORT_IO(0) = old | 0x80; \ - } \ - } while (0) - - -/* Timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - -/* Timer D details (the Anna actually has 5 of these; should change later). */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ SYS_CLOCK_FREQ -#define V850E_TIMER_D_TMCD_CS_MIN 1 /* min 2^1 divider */ - - -#endif /* __V850_ANNA_H__ */ diff --git a/include/asm-v850/as85ep1.h b/include/asm-v850/as85ep1.h deleted file mode 100644 index 5a5ca9073d09..000000000000 --- a/include/asm-v850/as85ep1.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * include/asm-v850/as85ep1.h -- AS85EP1 evaluation CPU chip/board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_AS85EP1_H__ -#define __V850_AS85EP1_H__ - -#include - - -#define CPU_MODEL "as85ep1" -#define CPU_MODEL_LONG "NEC V850E/AS85EP1" -#define PLATFORM "AS85EP1" -#define PLATFORM_LONG "NEC V850E/AS85EP1 evaluation board" - -#define CPU_CLOCK_FREQ 96000000 /* 96MHz */ -#define SYS_CLOCK_FREQ CPU_CLOCK_FREQ - - -/* 1MB of static RAM. */ -#define SRAM_ADDR 0x00400000 -#define SRAM_SIZE 0x00100000 /* 1MB */ -/* About 58MB of DRAM. This can actually be at one of two positions, - determined by jump JP3; we have to use the first position because the - second is partially out of processor instruction addressing range - (though in the second position there's actually 64MB available). */ -#define SDRAM_ADDR 0x00600000 -#define SDRAM_SIZE 0x039F8000 /* approx 58MB */ - -/* For */ -#define PAGE_OFFSET SRAM_ADDR - -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. The AS85EP1 chip - 16K of internal RAM located slightly before I/O space. */ -#define R0_RAM_ADDR 0xFFFF8000 - - -/* AS85EP1 specific control registers. */ -#define AS85EP1_CSC_ADDR(n) (0xFFFFF060 + (n) * 2) -#define AS85EP1_CSC(n) (*(volatile u16 *)AS85EP1_CSC_ADDR(n)) -#define AS85EP1_BSC_ADDR 0xFFFFF066 -#define AS85EP1_BSC (*(volatile u16 *)AS85EP1_BSC_ADDR) -#define AS85EP1_BCT_ADDR(n) (0xFFFFF480 + (n) * 2) -#define AS85EP1_BCT(n) (*(volatile u16 *)AS85EP1_BCT_ADDR(n)) -#define AS85EP1_DWC_ADDR(n) (0xFFFFF484 + (n) * 2) -#define AS85EP1_DWC(n) (*(volatile u16 *)AS85EP1_DWC_ADDR(n)) -#define AS85EP1_BCC_ADDR 0xFFFFF488 -#define AS85EP1_BCC (*(volatile u16 *)AS85EP1_BCC_ADDR) -#define AS85EP1_ASC_ADDR 0xFFFFF48A -#define AS85EP1_ASC (*(volatile u16 *)AS85EP1_ASC_ADDR) -#define AS85EP1_BCP_ADDR 0xFFFFF48C -#define AS85EP1_BCP (*(volatile u16 *)AS85EP1_BCP_ADDR) -#define AS85EP1_LBS_ADDR 0xFFFFF48E -#define AS85EP1_LBS (*(volatile u16 *)AS85EP1_LBS_ADDR) -#define AS85EP1_BMC_ADDR 0xFFFFF498 -#define AS85EP1_BMC (*(volatile u16 *)AS85EP1_BMC_ADDR) -#define AS85EP1_PRC_ADDR 0xFFFFF49A -#define AS85EP1_PRC (*(volatile u16 *)AS85EP1_PRC_ADDR) -#define AS85EP1_SCR_ADDR(n) (0xFFFFF4A0 + (n) * 4) -#define AS85EP1_SCR(n) (*(volatile u16 *)AS85EP1_SCR_ADDR(n)) -#define AS85EP1_RFS_ADDR(n) (0xFFFFF4A2 + (n) * 4) -#define AS85EP1_RFS(n) (*(volatile u16 *)AS85EP1_RFS_ADDR(n)) -#define AS85EP1_IRAMM_ADDR 0xFFFFF80A -#define AS85EP1_IRAMM (*(volatile u8 *)AS85EP1_IRAMM_ADDR) - - - -/* I/O port P0-P13. */ -/* Direct I/O. Bits 0-7 are pins Pn0-Pn7. */ -#define AS85EP1_PORT_IO_ADDR(n) (0xFFFFF400 + (n) * 2) -#define AS85EP1_PORT_IO(n) (*(volatile u8 *)AS85EP1_PORT_IO_ADDR(n)) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define AS85EP1_PORT_PM_ADDR(n) (0xFFFFF420 + (n) * 2) -#define AS85EP1_PORT_PM(n) (*(volatile u8 *)AS85EP1_PORT_PM_ADDR(n)) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define AS85EP1_PORT_PMC_ADDR(n) (0xFFFFF440 + (n) * 2) -#define AS85EP1_PORT_PMC(n) (*(volatile u8 *)AS85EP1_PORT_PMC_ADDR(n)) - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTCCC(n) (0x0C + (n)) -#define IRQ_INTCCC_NUM 8 -#define IRQ_INTCMD(n) (0x14 + (n)) /* interval timer interrupts 0-5 */ -#define IRQ_INTCMD_NUM 6 -#define IRQ_INTSRE(n) (0x1E + (n)*3) /* UART 0-1 reception error */ -#define IRQ_INTSRE_NUM 2 -#define IRQ_INTSR(n) (0x1F + (n)*3) /* UART 0-1 reception completion */ -#define IRQ_INTSR_NUM 2 -#define IRQ_INTST(n) (0x20 + (n)*3) /* UART 0-1 transmission completion */ -#define IRQ_INTST_NUM 2 - -#define NUM_CPU_IRQS 64 - -#ifndef __ASSEMBLY__ -/* Initialize chip interrupts. */ -extern void as85ep1_init_irqs (void); -#endif - - -/* AS85EP1 UART details (basically the same as the V850E/MA1, but 2 channels). */ -#define V850E_UART_NUM_CHANNELS 2 -#define V850E_UART_BASE_FREQ (SYS_CLOCK_FREQ / 4) -#define V850E_UART_CHIP_NAME "V850E/NA85E" - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE as85ep1_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void as85ep1_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 1. */ - -/* CTS for UART channel 1 is pin P54 (bit 4 of port 5). */ -#define V850E_UART_CTS(chan) ((chan) == 1 ? !(AS85EP1_PORT_IO(5) & 0x10) : 1) -/* RTS for UART channel 1 is pin P53 (bit 3 of port 5). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 1) { \ - unsigned old = AS85EP1_PORT_IO(5); \ - if (val) \ - AS85EP1_PORT_IO(5) = old & ~0x8; \ - else \ - AS85EP1_PORT_IO(5) = old | 0x8; \ - } \ - } while (0) - - -/* Timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - -/* Timer D details (the AS85EP1 actually has 5 of these; should change later). */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ SYS_CLOCK_FREQ -#define V850E_TIMER_D_TMCD_CS_MIN 2 /* min 2^2 divider */ - - -#endif /* __V850_AS85EP1_H__ */ diff --git a/include/asm-v850/asm.h b/include/asm-v850/asm.h deleted file mode 100644 index bf1e785a5dde..000000000000 --- a/include/asm-v850/asm.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * include/asm-v850/asm.h -- Macros for writing assembly code - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#define G_ENTRY(name) \ - .balign 4; \ - .globl name; \ - .type name,@function; \ - name -#define G_DATA(name) \ - .globl name; \ - .type name,@object; \ - name -#define END(name) \ - .size name,.-name - -#define L_ENTRY(name) \ - .balign 4; \ - .type name,@function; \ - name -#define L_DATA(name) \ - .type name,@object; \ - name diff --git a/include/asm-v850/atomic.h b/include/asm-v850/atomic.h deleted file mode 100644 index e4e57de08f73..000000000000 --- a/include/asm-v850/atomic.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * include/asm-v850/atomic.h -- Atomic operations - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ATOMIC_H__ -#define __V850_ATOMIC_H__ - - -#include - -#ifdef CONFIG_SMP -#error SMP not supported -#endif - -typedef struct { int counter; } atomic_t; - -#define ATOMIC_INIT(i) { (i) } - -#ifdef __KERNEL__ - -#define atomic_read(v) ((v)->counter) -#define atomic_set(v,i) (((v)->counter) = (i)) - -static inline int atomic_add_return (int i, volatile atomic_t *v) -{ - unsigned long flags; - int res; - - local_irq_save (flags); - res = v->counter + i; - v->counter = res; - local_irq_restore (flags); - - return res; -} - -static __inline__ int atomic_sub_return (int i, volatile atomic_t *v) -{ - unsigned long flags; - int res; - - local_irq_save (flags); - res = v->counter - i; - v->counter = res; - local_irq_restore (flags); - - return res; -} - -static __inline__ void atomic_clear_mask (unsigned long mask, unsigned long *addr) -{ - unsigned long flags; - - local_irq_save (flags); - *addr &= ~mask; - local_irq_restore (flags); -} - -#endif - -#define atomic_add(i, v) atomic_add_return ((i), (v)) -#define atomic_sub(i, v) atomic_sub_return ((i), (v)) - -#define atomic_dec_return(v) atomic_sub_return (1, (v)) -#define atomic_inc_return(v) atomic_add_return (1, (v)) -#define atomic_inc(v) atomic_inc_return (v) -#define atomic_dec(v) atomic_dec_return (v) - -/* - * atomic_inc_and_test - increment and test - * @v: pointer of type atomic_t - * - * Atomically increments @v by 1 - * and returns true if the result is zero, or false for all - * other cases. - */ -#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0) - -#define atomic_sub_and_test(i,v) (atomic_sub_return ((i), (v)) == 0) -#define atomic_dec_and_test(v) (atomic_sub_return (1, (v)) == 0) -#define atomic_add_negative(i,v) (atomic_add_return ((i), (v)) < 0) - -static inline int atomic_cmpxchg(atomic_t *v, int old, int new) -{ - int ret; - unsigned long flags; - - local_irq_save(flags); - ret = v->counter; - if (likely(ret == old)) - v->counter = new; - local_irq_restore(flags); - - return ret; -} - -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) - -static inline int atomic_add_unless(atomic_t *v, int a, int u) -{ - int ret; - unsigned long flags; - - local_irq_save(flags); - ret = v->counter; - if (ret != u) - v->counter += a; - local_irq_restore(flags); - - return ret != u; -} - -#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) - -/* Atomic operations are already serializing on ARM */ -#define smp_mb__before_atomic_dec() barrier() -#define smp_mb__after_atomic_dec() barrier() -#define smp_mb__before_atomic_inc() barrier() -#define smp_mb__after_atomic_inc() barrier() - -#include -#endif /* __V850_ATOMIC_H__ */ diff --git a/include/asm-v850/auxvec.h b/include/asm-v850/auxvec.h deleted file mode 100644 index f493232d0224..000000000000 --- a/include/asm-v850/auxvec.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __V850_AUXVEC_H__ -#define __V850_AUXVEC_H__ - -#endif /* __V850_AUXVEC_H__ */ diff --git a/include/asm-v850/bitops.h b/include/asm-v850/bitops.h deleted file mode 100644 index f82f5b4a56e0..000000000000 --- a/include/asm-v850/bitops.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * include/asm-v850/bitops.h -- Bit operations - * - * Copyright (C) 2001,02,03,04,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04,05 Miles Bader - * Copyright (C) 1992 Linus Torvalds. - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - */ - -#ifndef __V850_BITOPS_H__ -#define __V850_BITOPS_H__ - -#ifndef _LINUX_BITOPS_H -#error only can be included directly -#endif - -#include /* unlikely */ -#include /* swab32 */ -#include /* interrupt enable/disable */ - - -#ifdef __KERNEL__ - -#include - -/* - * The __ functions are not atomic - */ - -/* In the following constant-bit-op macros, a "g" constraint is used when - we really need an integer ("i" constraint). This is to avoid - warnings/errors from the compiler in the case where the associated - operand _isn't_ an integer, and shouldn't produce bogus assembly because - use of that form is protected by a guard statement that checks for - constants, and should otherwise be removed by the optimizer. This - _usually_ works -- however, __builtin_constant_p returns true for a - variable with a known constant value too, and unfortunately gcc will - happily put the variable in a register and use the register for the "g" - constraint'd asm operand. To avoid the latter problem, we add a - constant offset to the operand and subtract it back in the asm code; - forcing gcc to do arithmetic on the value is usually enough to get it - to use a real constant value. This is horrible, and ultimately - unreliable too, but it seems to work for now (hopefully gcc will offer - us more control in the future, so we can do a better job). */ - -#define __const_bit_op(op, nr, addr) \ - ({ __asm__ (op " (%0 - 0x123), %1" \ - :: "g" (((nr) & 0x7) + 0x123), \ - "m" (*((char *)(addr) + ((nr) >> 3))) \ - : "memory"); }) -#define __var_bit_op(op, nr, addr) \ - ({ int __nr = (nr); \ - __asm__ (op " %0, [%1]" \ - :: "r" (__nr & 0x7), \ - "r" ((char *)(addr) + (__nr >> 3)) \ - : "memory"); }) -#define __bit_op(op, nr, addr) \ - ((__builtin_constant_p (nr) && (unsigned)(nr) <= 0x7FFFF) \ - ? __const_bit_op (op, nr, addr) \ - : __var_bit_op (op, nr, addr)) - -#define __set_bit(nr, addr) __bit_op ("set1", nr, addr) -#define __clear_bit(nr, addr) __bit_op ("clr1", nr, addr) -#define __change_bit(nr, addr) __bit_op ("not1", nr, addr) - -/* The bit instructions used by `non-atomic' variants are actually atomic. */ -#define set_bit __set_bit -#define clear_bit __clear_bit -#define change_bit __change_bit - - -#define __const_tns_bit_op(op, nr, addr) \ - ({ int __tns_res; \ - __asm__ __volatile__ ( \ - "tst1 (%1 - 0x123), %2; setf nz, %0; " op " (%1 - 0x123), %2" \ - : "=&r" (__tns_res) \ - : "g" (((nr) & 0x7) + 0x123), \ - "m" (*((char *)(addr) + ((nr) >> 3))) \ - : "memory"); \ - __tns_res; \ - }) -#define __var_tns_bit_op(op, nr, addr) \ - ({ int __nr = (nr); \ - int __tns_res; \ - __asm__ __volatile__ ( \ - "tst1 %1, [%2]; setf nz, %0; " op " %1, [%2]" \ - : "=&r" (__tns_res) \ - : "r" (__nr & 0x7), \ - "r" ((char *)(addr) + (__nr >> 3)) \ - : "memory"); \ - __tns_res; \ - }) -#define __tns_bit_op(op, nr, addr) \ - ((__builtin_constant_p (nr) && (unsigned)(nr) <= 0x7FFFF) \ - ? __const_tns_bit_op (op, nr, addr) \ - : __var_tns_bit_op (op, nr, addr)) -#define __tns_atomic_bit_op(op, nr, addr) \ - ({ int __tns_atomic_res, __tns_atomic_flags; \ - local_irq_save (__tns_atomic_flags); \ - __tns_atomic_res = __tns_bit_op (op, nr, addr); \ - local_irq_restore (__tns_atomic_flags); \ - __tns_atomic_res; \ - }) - -#define __test_and_set_bit(nr, addr) __tns_bit_op ("set1", nr, addr) -#define test_and_set_bit(nr, addr) __tns_atomic_bit_op ("set1", nr, addr) - -#define __test_and_clear_bit(nr, addr) __tns_bit_op ("clr1", nr, addr) -#define test_and_clear_bit(nr, addr) __tns_atomic_bit_op ("clr1", nr, addr) - -#define __test_and_change_bit(nr, addr) __tns_bit_op ("not1", nr, addr) -#define test_and_change_bit(nr, addr) __tns_atomic_bit_op ("not1", nr, addr) - - -#define __const_test_bit(nr, addr) \ - ({ int __test_bit_res; \ - __asm__ __volatile__ ("tst1 (%1 - 0x123), %2; setf nz, %0" \ - : "=r" (__test_bit_res) \ - : "g" (((nr) & 0x7) + 0x123), \ - "m" (*((const char *)(addr) + ((nr) >> 3)))); \ - __test_bit_res; \ - }) -static inline int __test_bit (int nr, const void *addr) -{ - int res; - __asm__ __volatile__ ("tst1 %1, [%2]; setf nz, %0" - : "=r" (res) - : "r" (nr & 0x7), "r" (addr + (nr >> 3))); - return res; -} -#define test_bit(nr,addr) \ - ((__builtin_constant_p (nr) && (unsigned)(nr) <= 0x7FFFF) \ - ? __const_test_bit ((nr), (addr)) \ - : __test_bit ((nr), (addr))) - - -/* clear_bit doesn't provide any barrier for the compiler. */ -#define smp_mb__before_clear_bit() barrier () -#define smp_mb__after_clear_bit() barrier () - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) - -#include - -#endif /* __KERNEL__ */ - -#endif /* __V850_BITOPS_H__ */ diff --git a/include/asm-v850/bug.h b/include/asm-v850/bug.h deleted file mode 100644 index b0ed2d35f3e8..000000000000 --- a/include/asm-v850/bug.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * include/asm-v850/bug.h -- Bug reporting - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_BUG_H__ -#define __V850_BUG_H__ - -#ifdef CONFIG_BUG -extern void __bug (void) __attribute__ ((noreturn)); -#define BUG() __bug() -#define HAVE_ARCH_BUG -#endif - -#include - -#endif /* __V850_BUG_H__ */ diff --git a/include/asm-v850/bugs.h b/include/asm-v850/bugs.h deleted file mode 100644 index 71110a65c1d7..000000000000 --- a/include/asm-v850/bugs.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * include/asm-v850e/bugs.h - * - * Copyright (C) 1994 Linus Torvalds - */ - -/* - * This is included by init/main.c to check for architecture-dependent bugs. - * - * Needs: - * void check_bugs(void); - */ - -static void check_bugs(void) -{ -} diff --git a/include/asm-v850/byteorder.h b/include/asm-v850/byteorder.h deleted file mode 100644 index a6f07530050e..000000000000 --- a/include/asm-v850/byteorder.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * include/asm-v850/byteorder.h -- Endian id and conversion ops - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_BYTEORDER_H__ -#define __V850_BYTEORDER_H__ - -#include -#include - -#ifdef __GNUC__ - -static __inline__ __attribute_const__ __u32 ___arch__swab32 (__u32 word) -{ - __u32 res; - __asm__ ("bsw %1, %0" : "=r" (res) : "r" (word)); - return res; -} - -static __inline__ __attribute_const__ __u16 ___arch__swab16 (__u16 half_word) -{ - __u16 res; - __asm__ ("bsh %1, %0" : "=r" (res) : "r" (half_word)); - return res; -} - -#define __arch__swab32(x) ___arch__swab32(x) -#define __arch__swab16(x) ___arch__swab16(x) - -#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) -# define __BYTEORDER_HAS_U64__ -# define __SWAB_64_THRU_32__ -#endif - -#endif /* __GNUC__ */ - -#include - -#endif /* __V850_BYTEORDER_H__ */ diff --git a/include/asm-v850/cache.h b/include/asm-v850/cache.h deleted file mode 100644 index 8832c7ea3242..000000000000 --- a/include/asm-v850/cache.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-v850/cache.h -- Cache operations - * - * Copyright (C) 2001,05 NEC Corporation - * Copyright (C) 2001,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CACHE_H__ -#define __V850_CACHE_H__ - -/* All cache operations are machine-dependent. */ -#include - -#ifndef L1_CACHE_BYTES -/* This processor has no cache, so just choose an arbitrary value. */ -#define L1_CACHE_BYTES 16 -#define L1_CACHE_SHIFT 4 -#endif - -#endif /* __V850_CACHE_H__ */ diff --git a/include/asm-v850/cacheflush.h b/include/asm-v850/cacheflush.h deleted file mode 100644 index 9ece05a202ef..000000000000 --- a/include/asm-v850/cacheflush.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * include/asm-v850/cacheflush.h - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CACHEFLUSH_H__ -#define __V850_CACHEFLUSH_H__ - -/* Somebody depends on this; sigh... */ -#include - -#include - - -/* The following are all used by the kernel in ways that only affect - systems with MMUs, so we don't need them. */ -#define flush_cache_all() ((void)0) -#define flush_cache_mm(mm) ((void)0) -#define flush_cache_dup_mm(mm) ((void)0) -#define flush_cache_range(vma, start, end) ((void)0) -#define flush_cache_page(vma, vmaddr, pfn) ((void)0) -#define flush_dcache_page(page) ((void)0) -#define flush_dcache_mmap_lock(mapping) ((void)0) -#define flush_dcache_mmap_unlock(mapping) ((void)0) -#define flush_cache_vmap(start, end) ((void)0) -#define flush_cache_vunmap(start, end) ((void)0) - -#ifdef CONFIG_NO_CACHE - -/* Some systems have no cache at all, in which case we don't need these - either. */ -#define flush_icache() ((void)0) -#define flush_icache_range(start, end) ((void)0) -#define flush_icache_page(vma,pg) ((void)0) -#define flush_icache_user_range(vma,pg,adr,len) ((void)0) -#define flush_cache_sigtramp(vaddr) ((void)0) - -#else /* !CONFIG_NO_CACHE */ - -struct page; -struct mm_struct; -struct vm_area_struct; - -/* Otherwise, somebody had better define them. */ -extern void flush_icache (void); -extern void flush_icache_range (unsigned long start, unsigned long end); -extern void flush_icache_page (struct vm_area_struct *vma, struct page *page); -extern void flush_icache_user_range (struct vm_area_struct *vma, - struct page *page, - unsigned long adr, int len); -extern void flush_cache_sigtramp (unsigned long addr); - -#endif /* CONFIG_NO_CACHE */ - -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ -do { memcpy(dst, src, len); \ - flush_icache_user_range(vma, page, vaddr, len); \ -} while (0) -#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) - -#endif /* __V850_CACHEFLUSH_H__ */ diff --git a/include/asm-v850/checksum.h b/include/asm-v850/checksum.h deleted file mode 100644 index d1dddd938262..000000000000 --- a/include/asm-v850/checksum.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * include/asm-v850/checksum.h -- Checksum ops - * - * Copyright (C) 2001,2005 NEC Corporation - * Copyright (C) 2001,2005 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CHECKSUM_H__ -#define __V850_CHECKSUM_H__ - -/* - * computes the checksum of a memory block at buff, length len, - * and adds in "sum" (32-bit) - * - * returns a 32-bit number suitable for feeding into itself - * or csum_tcpudp_magic - * - * this function must be called with even lengths, except - * for the last fragment, which may be odd - * - * it's best to have buff aligned on a 32-bit boundary - */ -extern __wsum csum_partial(const void *buff, int len, __wsum sum); - -/* - * the same as csum_partial, but copies from src while it - * checksums - * - * here even more important to align src and dst on a 32-bit (or even - * better 64-bit) boundary - */ -extern __wsum csum_partial_copy_nocheck(const void *src, - void *dst, int len, __wsum sum); - - -/* - * the same as csum_partial_copy, but copies from user space. - * - * here even more important to align src and dst on a 32-bit (or even - * better 64-bit) boundary - */ -extern __wsum csum_partial_copy_from_user (const void *src, - void *dst, - int len, __wsum sum, - int *csum_err); - -__sum16 ip_fast_csum(const void *iph, unsigned int ihl); - -/* - * Fold a partial checksum - */ -static inline __sum16 csum_fold (__wsum sum) -{ - unsigned int result; - /* - %0 %1 - hsw %1, %0 H L L H - add %1, %0 H L H+L+C H+L - */ - asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum)); - return (__force __sum16)(~result >> 16); -} - - -/* - * computes the checksum of the TCP/UDP pseudo-header - * returns a 16-bit checksum, already complemented - */ -static inline __wsum -csum_tcpudp_nofold (__be32 saddr, __be32 daddr, - unsigned short len, - unsigned short proto, __wsum sum) -{ - int __carry; - __asm__ ("add %2, %0;" - "setf c, %1;" - "add %1, %0;" - "add %3, %0;" - "setf c, %1;" - "add %1, %0;" - "add %4, %0;" - "setf c, %1;" - "add %1, %0" - : "=&r" (sum), "=&r" (__carry) - : "r" (daddr), "r" (saddr), - "r" ((len + proto) << 8), - "0" (sum)); - return sum; -} - -static inline __sum16 -csum_tcpudp_magic (__be32 saddr, __be32 daddr, - unsigned short len, - unsigned short proto, __wsum sum) -{ - return csum_fold (csum_tcpudp_nofold (saddr, daddr, len, proto, sum)); -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -extern __sum16 ip_compute_csum(const void *buff, int len); - - -#endif /* __V850_CHECKSUM_H__ */ diff --git a/include/asm-v850/clinkage.h b/include/asm-v850/clinkage.h deleted file mode 100644 index c389691d6f86..000000000000 --- a/include/asm-v850/clinkage.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-v850/clinkage.h -- Macros to reflect C symbol-naming conventions - * - * Copyright (C) 2001,02 NEC Corporatione - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CLINKAGE_H__ -#define __V850_CLINKAGE_H__ - -#include -#include - -#define C_SYMBOL_NAME(name) macrology_paste(_, name) -#define C_SYMBOL_STRING(name) macrology_stringify(C_SYMBOL_NAME(name)) -#define C_ENTRY(name) G_ENTRY(C_SYMBOL_NAME(name)) -#define C_DATA(name) G_DATA(C_SYMBOL_NAME(name)) -#define C_END(name) END(C_SYMBOL_NAME(name)) - -#endif /* __V850_CLINKAGE_H__ */ diff --git a/include/asm-v850/cputime.h b/include/asm-v850/cputime.h deleted file mode 100644 index 7c799c33b8a9..000000000000 --- a/include/asm-v850/cputime.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_CPUTIME_H -#define __V850_CPUTIME_H - -#include - -#endif /* __V850_CPUTIME_H */ diff --git a/include/asm-v850/current.h b/include/asm-v850/current.h deleted file mode 100644 index 30aae5673770..000000000000 --- a/include/asm-v850/current.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * include/asm-v850/current.h -- Current task - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CURRENT_H__ -#define __V850_CURRENT_H__ - -#ifndef __ASSEMBLY__ /* is not asm-safe. */ -#include -#endif - -#include - - -/* Register used to hold the current task pointer while in the kernel. - Any `call clobbered' register without a special meaning should be OK, - but check asm/v850/kernel/entry.S to be sure. */ -#define CURRENT_TASK_REGNUM 16 -#define CURRENT_TASK macrology_paste (r, CURRENT_TASK_REGNUM) - - -#ifdef __ASSEMBLY__ - -/* Put a pointer to the current task structure into REG. */ -#define GET_CURRENT_TASK(reg) \ - GET_CURRENT_THREAD(reg); \ - ld.w TI_TASK[reg], reg - -#else /* !__ASSEMBLY__ */ - -/* A pointer to the current task. */ -register struct task_struct *current \ - __asm__ (macrology_stringify (CURRENT_TASK)); - -#endif /* __ASSEMBLY__ */ - - -#endif /* _V850_CURRENT_H */ diff --git a/include/asm-v850/delay.h b/include/asm-v850/delay.h deleted file mode 100644 index 6d028e6b2354..000000000000 --- a/include/asm-v850/delay.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * include/asm-v850/delay.h -- Delay routines, using a pre-computed - * "loops_per_second" value - * - * Copyright (C) 2001,03 NEC Corporation - * Copyright (C) 2001,03 Miles Bader - * Copyright (C) 1994 Hamish Macdonald - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - */ - -#ifndef __V850_DELAY_H__ -#define __V850_DELAY_H__ - -#include - -static inline void __delay(unsigned long loops) -{ - if (loops) - __asm__ __volatile__ ("1: add -1, %0; bnz 1b" - : "=r" (loops) : "0" (loops)); -} - -/* - * Use only for very small delays ( < 1 msec). Should probably use a - * lookup table, really, as the multiplications take much too long with - * short delays. This is a "reasonable" implementation, though (and the - * first constant multiplications gets optimized away if the delay is - * a constant) - */ - -extern unsigned long loops_per_jiffy; - -static inline void udelay(unsigned long usecs) -{ - register unsigned long full_loops, part_loops; - - full_loops = ((usecs * HZ) / 1000000) * loops_per_jiffy; - usecs %= (1000000 / HZ); - part_loops = (usecs * HZ * loops_per_jiffy) / 1000000; - - __delay(full_loops + part_loops); -} - -#endif /* __V850_DELAY_H__ */ diff --git a/include/asm-v850/device.h b/include/asm-v850/device.h deleted file mode 100644 index d8f9872b0e2d..000000000000 --- a/include/asm-v850/device.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Arch specific extensions to struct device - * - * This file is released under the GPLv2 - */ -#include - diff --git a/include/asm-v850/div64.h b/include/asm-v850/div64.h deleted file mode 100644 index 6cd978cefb28..000000000000 --- a/include/asm-v850/div64.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/dma-mapping.h b/include/asm-v850/dma-mapping.h deleted file mode 100644 index 1cc42c603a1b..000000000000 --- a/include/asm-v850/dma-mapping.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __V850_DMA_MAPPING_H__ -#define __V850_DMA_MAPPING_H__ - - -#ifdef CONFIG_PCI -#include -#else -#include -#endif - -#endif /* __V850_DMA_MAPPING_H__ */ diff --git a/include/asm-v850/dma.h b/include/asm-v850/dma.h deleted file mode 100644 index 2369849e2d0a..000000000000 --- a/include/asm-v850/dma.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __V850_DMA_H__ -#define __V850_DMA_H__ - -/* What should this be? */ -#define MAX_DMA_ADDRESS 0xFFFFFFFF - -/* reserve a DMA channel */ -extern int request_dma (unsigned int dmanr, const char * device_id); -/* release it again */ -extern void free_dma (unsigned int dmanr); - -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) -#endif - -#endif /* __V850_DMA_H__ */ diff --git a/include/asm-v850/elf.h b/include/asm-v850/elf.h deleted file mode 100644 index 28f5b176ff1a..000000000000 --- a/include/asm-v850/elf.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef __V850_ELF_H__ -#define __V850_ELF_H__ - -/* - * ELF register definitions.. - */ - -#include -#include -#include - -typedef unsigned long elf_greg_t; - -#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; - -typedef struct user_fpu_struct elf_fpregset_t; - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) \ - ((x)->e_machine == EM_V850 || (x)->e_machine == EM_CYGNUS_V850) - - -/* v850 relocation types. */ -#define R_V850_NONE 0 -#define R_V850_9_PCREL 1 -#define R_V850_22_PCREL 2 -#define R_V850_HI16_S 3 -#define R_V850_HI16 4 -#define R_V850_LO16 5 -#define R_V850_32 6 -#define R_V850_16 7 -#define R_V850_8 8 -#define R_V850_SDA_16_16_OFFSET 9 /* For ld.b, st.b, set1, clr1, - not1, tst1, movea, movhi */ -#define R_V850_SDA_15_16_OFFSET 10 /* For ld.w, ld.h, ld.hu, st.w, st.h */ -#define R_V850_ZDA_16_16_OFFSET 11 /* For ld.b, st.b, set1, clr1, - not1, tst1, movea, movhi */ -#define R_V850_ZDA_15_16_OFFSET 12 /* For ld.w, ld.h, ld.hu, st.w, st.h */ -#define R_V850_TDA_6_8_OFFSET 13 /* For sst.w, sld.w */ -#define R_V850_TDA_7_8_OFFSET 14 /* For sst.h, sld.h */ -#define R_V850_TDA_7_7_OFFSET 15 /* For sst.b, sld.b */ -#define R_V850_TDA_16_16_OFFSET 16 /* For set1, clr1, not1, tst1, - movea, movhi */ -#define R_V850_NUM 17 - - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#ifdef __LITTLE_ENDIAN__ -#define ELF_DATA ELFDATA2LSB -#else -#define ELF_DATA ELFDATA2MSB -#endif -#define ELF_ARCH EM_V850 - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - - -#define ELF_CORE_COPY_REGS(_dest,_regs) \ - memcpy((char *) &_dest, (char *) _regs, \ - sizeof(struct pt_regs)); - -/* This yields a mask that user programs can use to figure out what - instruction set this CPU supports. This could be done in user space, - but it's not easy, and we've already done it here. */ - -#define ELF_HWCAP (0) - -/* This yields a string that ld.so will use to load implementation - specific libraries for optimization. This is more specific in - intent than poking at uname or /proc/cpuinfo. - - For the moment, we have only optimizations for the Intel generations, - but that could change... */ - -#define ELF_PLATFORM (NULL) - -#define ELF_PLAT_INIT(_r, load_addr) \ - do { \ - _r->gpr[0] = _r->gpr[1] = _r->gpr[2] = _r->gpr[3] = \ - _r->gpr[4] = _r->gpr[5] = _r->gpr[6] = _r->gpr[7] = \ - _r->gpr[8] = _r->gpr[9] = _r->gpr[10] = _r->gpr[11] = \ - _r->gpr[12] = _r->gpr[13] = _r->gpr[14] = _r->gpr[15] = \ - _r->gpr[16] = _r->gpr[17] = _r->gpr[18] = _r->gpr[19] = \ - _r->gpr[20] = _r->gpr[21] = _r->gpr[22] = _r->gpr[23] = \ - _r->gpr[24] = _r->gpr[25] = _r->gpr[26] = _r->gpr[27] = \ - _r->gpr[28] = _r->gpr[29] = _r->gpr[30] = _r->gpr[31] = \ - 0; \ - } while (0) - -#define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) - -#endif /* __V850_ELF_H__ */ diff --git a/include/asm-v850/emergency-restart.h b/include/asm-v850/emergency-restart.h deleted file mode 100644 index 108d8c48e42e..000000000000 --- a/include/asm-v850/emergency-restart.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_EMERGENCY_RESTART_H -#define _ASM_EMERGENCY_RESTART_H - -#include - -#endif /* _ASM_EMERGENCY_RESTART_H */ diff --git a/include/asm-v850/entry.h b/include/asm-v850/entry.h deleted file mode 100644 index d9df8ac48584..000000000000 --- a/include/asm-v850/entry.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * include/asm-v850/entry.h -- Definitions used by low-level trap handlers - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ENTRY_H__ -#define __V850_ENTRY_H__ - - -#include -#include - - -/* These are special variables using by the kernel trap/interrupt code - to save registers in, at a time when there are no spare registers we - can use to do so, and we can't depend on the value of the stack - pointer. This means that they must be within a signed 16-bit - displacement of 0x00000000. */ - -#define KERNEL_VAR_SPACE_ADDR R0_RAM_ADDR - -#ifdef __ASSEMBLY__ -#define KERNEL_VAR(addr) addr[r0] -#else -#define KERNEL_VAR(addr) (*(volatile unsigned long *)(addr)) -#endif - -/* Kernel stack pointer, 4 bytes. */ -#define KSP_ADDR (KERNEL_VAR_SPACE_ADDR + 0) -#define KSP KERNEL_VAR (KSP_ADDR) -/* 1 if in kernel-mode, 0 if in user mode, 1 byte. */ -#define KM_ADDR (KERNEL_VAR_SPACE_ADDR + 4) -#define KM KERNEL_VAR (KM_ADDR) -/* Temporary storage for interrupt handlers, 4 bytes. */ -#define INT_SCRATCH_ADDR (KERNEL_VAR_SPACE_ADDR + 8) -#define INT_SCRATCH KERNEL_VAR (INT_SCRATCH_ADDR) -/* Where the stack-pointer is saved when jumping to various sorts of - interrupt handlers. ENTRY_SP is used by everything except NMIs, - which have their own location. Higher-priority NMIs can clobber the - value written by a lower priority NMI, since they can't be disabled, - but that's OK, because only NMI0 (the lowest-priority one) is allowed - to return. */ -#define ENTRY_SP_ADDR (KERNEL_VAR_SPACE_ADDR + 12) -#define ENTRY_SP KERNEL_VAR (ENTRY_SP_ADDR) -#define NMI_ENTRY_SP_ADDR (KERNEL_VAR_SPACE_ADDR + 16) -#define NMI_ENTRY_SP KERNEL_VAR (NMI_ENTRY_SP_ADDR) - -#ifdef CONFIG_RESET_GUARD -/* Used to detect unexpected resets (since the v850 has no MMU, any call - through a null pointer will jump to the reset vector). We detect - such resets by checking for a magic value, RESET_GUARD_ACTIVE, in - this location. Properly resetting the machine stores zero there, so - it shouldn't trigger the guard; the power-on value is uncertain, but - it's unlikely to be RESET_GUARD_ACTIVE. */ -#define RESET_GUARD_ADDR (KERNEL_VAR_SPACE_ADDR + 28) -#define RESET_GUARD KERNEL_VAR (RESET_GUARD_ADDR) -#define RESET_GUARD_ACTIVE 0xFAB4BEEF -#endif /* CONFIG_RESET_GUARD */ - -#ifdef CONFIG_V850E_HIGHRES_TIMER -#define HIGHRES_TIMER_SLOW_TICKS_ADDR (KERNEL_VAR_SPACE_ADDR + 32) -#define HIGHRES_TIMER_SLOW_TICKS KERNEL_VAR (HIGHRES_TIMER_SLOW_TICKS_ADDR) -#endif /* CONFIG_V850E_HIGHRES_TIMER */ - -#ifndef __ASSEMBLY__ - -#ifdef CONFIG_RESET_GUARD -/* Turn off reset guard, so that resetting the machine works normally. - This should be called in the various machine_halt, etc., functions. */ -static inline void disable_reset_guard (void) -{ - RESET_GUARD = 0; -} -#endif /* CONFIG_RESET_GUARD */ - -#endif /* !__ASSEMBLY__ */ - - -/* A `state save frame' is a struct pt_regs preceded by some extra space - suitable for a function call stack frame. */ - -/* Amount of room on the stack reserved for arguments and to satisfy the - C calling conventions, in addition to the space used by the struct - pt_regs that actually holds saved values. */ -#define STATE_SAVE_ARG_SPACE (6*4) /* Up to six arguments. */ - - -#ifdef __ASSEMBLY__ - -/* The size of a state save frame. */ -#define STATE_SAVE_SIZE (PT_SIZE + STATE_SAVE_ARG_SPACE) - -#else /* !__ASSEMBLY__ */ - -/* The size of a state save frame. */ -#define STATE_SAVE_SIZE (sizeof (struct pt_regs) + STATE_SAVE_ARG_SPACE) - -#endif /* __ASSEMBLY__ */ - - -/* Offset of the struct pt_regs in a state save frame. */ -#define STATE_SAVE_PT_OFFSET STATE_SAVE_ARG_SPACE - - -#endif /* __V850_ENTRY_H__ */ diff --git a/include/asm-v850/errno.h b/include/asm-v850/errno.h deleted file mode 100644 index 31c91df01205..000000000000 --- a/include/asm-v850/errno.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_ERRNO_H__ -#define __V850_ERRNO_H__ - -#include - -#endif /* __V850_ERRNO_H__ */ diff --git a/include/asm-v850/fb.h b/include/asm-v850/fb.h deleted file mode 100644 index c7df38030992..000000000000 --- a/include/asm-v850/fb.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _ASM_FB_H_ -#define _ASM_FB_H_ -#include - -#define fb_pgprotect(...) do {} while (0) - -static inline int fb_is_primary_device(struct fb_info *info) -{ - return 0; -} - -#endif /* _ASM_FB_H_ */ diff --git a/include/asm-v850/fcntl.h b/include/asm-v850/fcntl.h deleted file mode 100644 index 3af4d56776dd..000000000000 --- a/include/asm-v850/fcntl.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __V850_FCNTL_H__ -#define __V850_FCNTL_H__ - -#define O_DIRECTORY 040000 /* must be a directory */ -#define O_NOFOLLOW 0100000 /* don't follow links */ -#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ -#define O_LARGEFILE 0400000 - -#include - -#endif /* __V850_FCNTL_H__ */ diff --git a/include/asm-v850/flat.h b/include/asm-v850/flat.h deleted file mode 100644 index 17f0ea566611..000000000000 --- a/include/asm-v850/flat.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * include/asm-v850/flat.h -- uClinux flat-format executables - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_FLAT_H__ -#define __V850_FLAT_H__ - -/* The amount by which a relocation can exceed the program image limits - without being regarded as an error. On the v850, the relocations of - some base-pointers can be offset by 0x8000 (to allow better usage of the - space offered by 16-bit signed offsets -- in most cases the offsets used - with such a base-pointer will be negative). */ - -#define flat_reloc_valid(reloc, size) ((reloc) <= (size + 0x8000)) - -#define flat_stack_align(sp) /* nothing needed */ -#define flat_argvp_envp_on_stack() 0 -#define flat_old_ram_flag(flags) (flags) -#define flat_set_persistent(relval, p) 0 - -/* We store the type of relocation in the top 4 bits of the `relval.' */ - -/* Convert a relocation entry into an address. */ -static inline unsigned long -flat_get_relocate_addr (unsigned long relval) -{ - return relval & 0x0fffffff; /* Mask out top 4-bits */ -} - -#define flat_v850_get_reloc_type(relval) ((relval) >> 28) - -#define FLAT_V850_R_32 0 /* Normal 32-bit reloc */ -#define FLAT_V850_R_HI16S_LO15 1 /* High 16-bits + signed 15-bit low field */ -#define FLAT_V850_R_HI16S_LO16 2 /* High 16-bits + signed 16-bit low field */ - -/* Extract the address to be relocated from the symbol reference at RP; - RELVAL is the raw relocation-table entry from which RP is derived. - For the v850, RP should always be half-word aligned. */ -static inline unsigned long flat_get_addr_from_rp (unsigned long *rp, - unsigned long relval, - unsigned long flags, - unsigned long *persistent) -{ - short *srp = (short *)rp; - - switch (flat_v850_get_reloc_type (relval)) - { - case FLAT_V850_R_32: - /* Simple 32-bit address. */ - return srp[0] | (srp[1] << 16); - - case FLAT_V850_R_HI16S_LO16: - /* The high and low halves of the address are in the 16 - bits at RP, and the 2nd word of the 32-bit instruction - following that, respectively. The low half is _signed_ - so we have to sign-extend it and add it to the upper - half instead of simply or-ing them together. - - Unlike most relocated address, this one is stored in - native (little-endian) byte-order to avoid problems with - trashing the low-order bit, so we have to convert to - network-byte-order before returning, as that's what the - caller expects. */ - return htonl ((srp[0] << 16) + srp[2]); - - case FLAT_V850_R_HI16S_LO15: - /* The high and low halves of the address are in the 16 - bits at RP, and the upper 15 bits of the 2nd word of the - 32-bit instruction following that, respectively. The - low half is _signed_ so we have to sign-extend it and - add it to the upper half instead of simply or-ing them - together. The lowest bit is always zero. - - Unlike most relocated address, this one is stored in - native (little-endian) byte-order to avoid problems with - trashing the low-order bit, so we have to convert to - network-byte-order before returning, as that's what the - caller expects. */ - return htonl ((srp[0] << 16) + (srp[2] & ~0x1)); - - default: - return ~0; /* bogus value */ - } -} - -/* Insert the address ADDR into the symbol reference at RP; - RELVAL is the raw relocation-table entry from which RP is derived. - For the v850, RP should always be half-word aligned. */ -static inline void flat_put_addr_at_rp (unsigned long *rp, unsigned long addr, - unsigned long relval) -{ - short *srp = (short *)rp; - - switch (flat_v850_get_reloc_type (relval)) { - case FLAT_V850_R_32: - /* Simple 32-bit address. */ - srp[0] = addr & 0xFFFF; - srp[1] = (addr >> 16); - break; - - case FLAT_V850_R_HI16S_LO16: - /* The high and low halves of the address are in the 16 - bits at RP, and the 2nd word of the 32-bit instruction - following that, respectively. The low half is _signed_ - so we must carry its sign bit to the upper half before - writing the upper half. */ - srp[0] = (addr >> 16) + ((addr >> 15) & 0x1); - srp[2] = addr & 0xFFFF; - break; - - case FLAT_V850_R_HI16S_LO15: - /* The high and low halves of the address are in the 16 - bits at RP, and the upper 15 bits of the 2nd word of the - 32-bit instruction following that, respectively. The - low half is _signed_ so we must carry its sign bit to - the upper half before writing the upper half. The - lowest bit we preserve from the existing instruction. */ - srp[0] = (addr >> 16) + ((addr >> 15) & 0x1); - srp[2] = (addr & 0xFFFE) | (srp[2] & 0x1); - break; - } -} - -#endif /* __V850_FLAT_H__ */ diff --git a/include/asm-v850/fpga85e2c.h b/include/asm-v850/fpga85e2c.h deleted file mode 100644 index 23aae666c718..000000000000 --- a/include/asm-v850/fpga85e2c.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * include/asm-v850/fpga85e2c.h -- Machine-dependent defs for - * FPGA implementation of V850E2/NA85E2C - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_FPGA85E2C_H__ -#define __V850_FPGA85E2C_H__ - -#include -#include - - -#define CPU_MODEL "v850e2/fpga85e2c" -#define CPU_MODEL_LONG "NEC V850E2/NA85E2C" -#define PLATFORM "fpga85e2c" -#define PLATFORM_LONG "NA85E2C FPGA implementation" - - -/* `external ram'. */ -#define ERAM_ADDR 0 -#define ERAM_SIZE 0x00100000 /* 1MB */ - - -/* FPGA specific control registers. */ - -/* Writing a non-zero value to FLGREG(0) will signal the controlling CPU - to stop execution. */ -#define FLGREG_ADDR(n) (0xFFE80100 + 2*(n)) -#define FLGREG(n) (*(volatile unsigned char *)FLGREG_ADDR (n)) -#define FLGREG_NUM 2 - -#define CSDEV_ADDR(n) (0xFFE80110 + 2*(n)) -#define CSDEV(n) (*(volatile unsigned char *)CSDEV_ADDR (n)) - - -/* Timer interrupts 0-3, interrupt at intervals from CLK/4096 to CLK/16384. */ -#define IRQ_RPU(n) (60 + (n)) -#define IRQ_RPU_NUM 4 - -/* For */ -#define NUM_CPU_IRQS 64 - - -/* General-purpose timer. */ -/* control/status register (can only be read/written via bit insns) */ -#define RPU_GTMC_ADDR 0xFFFFFB00 -#define RPU_GTMC (*(volatile unsigned char *)RPU_GTMC_ADDR) -#define RPU_GTMC_CE_BIT 7 /* clock enable (control) */ -#define RPU_GTMC_OV_BIT 6 /* overflow (status) */ -#define RPU_GTMC_CLK_BIT 1 /* 0 = .5 MHz CLK, 1 = 1 Mhz (control) */ -/* 32-bit count (8 least-significant bits are always zero). */ -#define RPU_GTM_ADDR 0xFFFFFB28 -#define RPU_GTM (*(volatile unsigned long *)RPU_GTMC_ADDR) - - -/* For */ -#define PAGE_OFFSET ERAM_ADDR /* minimum allocatable address */ - - -/* For */ -/* `R0 RAM', used for a few miscellaneous variables that must be accessible - using a load instruction relative to R0. The FPGA implementation - actually has no on-chip RAM, so we use part of main ram just after the - interrupt vectors. */ -#ifdef __ASSEMBLY__ -#define R0_RAM_ADDR lo(C_SYMBOL_NAME(_r0_ram)) -#else -extern char _r0_ram; -#define R0_RAM_ADDR ((unsigned long)&_r0_ram); -#endif - - -#endif /* __V850_FPGA85E2C_H__ */ diff --git a/include/asm-v850/futex.h b/include/asm-v850/futex.h deleted file mode 100644 index 6a332a9f099c..000000000000 --- a/include/asm-v850/futex.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_FUTEX_H -#define _ASM_FUTEX_H - -#include - -#endif diff --git a/include/asm-v850/gbus_int.h b/include/asm-v850/gbus_int.h deleted file mode 100644 index 0c4bce753c7e..000000000000 --- a/include/asm-v850/gbus_int.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * include/asm-v850/gbus_int.h -- Midas labs GBUS interrupt support - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_GBUS_INT_H__ -#define __V850_GBUS_INT_H__ - - -/* The GBUS interrupt interface has 32 interrupts shared among 4 - processor interrupts. The 32 GBUS interrupts are divided into two - sets of 16 each, for allocating among control registers, etc (there - are two of each control register, with bits 0-15 controlling an - interrupt each). */ - -/* The GBUS interrupts themselves. */ -#define IRQ_GBUS_INT(n) (GBUS_INT_BASE_IRQ + (n)) -#define IRQ_GBUS_INT_NUM 32 - -/* Control registers. */ -#define GBUS_INT_STATUS_ADDR(w) (GBUS_INT_BASE_ADDR + (w)*0x40) -#define GBUS_INT_STATUS(w) (*(volatile u16 *)GBUS_INT_STATUS_ADDR(w)) -#define GBUS_INT_CLEAR_ADDR(w) (GBUS_INT_BASE_ADDR + 0x10 + (w)*0x40) -#define GBUS_INT_CLEAR(w) (*(volatile u16 *)GBUS_INT_CLEAR_ADDR(w)) -#define GBUS_INT_EDGE_ADDR(w) (GBUS_INT_BASE_ADDR + 0x20 + (w)*0x40) -#define GBUS_INT_EDGE(w) (*(volatile u16 *)GBUS_INT_EDGE_ADDR(w)) -#define GBUS_INT_POLARITY_ADDR(w) (GBUS_INT_BASE_ADDR + 0x30 + (w)*0x40) -#define GBUS_INT_POLARITY(w) (*(volatile u16 *)GBUS_INT_POLARITY_ADDR(w)) -/* This allows enabling interrupt bits in word W for interrupt GINTn. */ -#define GBUS_INT_ENABLE_ADDR(w, n) \ - (GBUS_INT_BASE_ADDR + 0x100 + (w)*0x10 + (n)*0x20) -#define GBUS_INT_ENABLE(w, n) (*(volatile u16 *)GBUS_INT_ENABLE_ADDR(w, n)) - -/* Mapping between kernel interrupt numbers and hardware control regs/bits. */ -#define GBUS_INT_BITS_PER_WORD 16 -#define GBUS_INT_NUM_WORDS (IRQ_GBUS_INT_NUM / GBUS_INT_BITS_PER_WORD) -#define GBUS_INT_IRQ_WORD(irq) (((irq) - GBUS_INT_BASE_IRQ) >> 4) -#define GBUS_INT_IRQ_BIT(irq) (((irq) - GBUS_INT_BASE_IRQ) & 0xF) -#define GBUS_INT_IRQ_MASK(irq) (1 << GBUS_INT_IRQ_BIT(irq)) - - -/* Possible priorities for GBUS interrupts. */ -#define GBUS_INT_PRIORITY_HIGH 2 -#define GBUS_INT_PRIORITY_MEDIUM 4 -#define GBUS_INT_PRIORITY_LOW 6 - - -#ifndef __ASSEMBLY__ - -/* Enable interrupt handling for interrupt IRQ. */ -extern void gbus_int_enable_irq (unsigned irq); -/* Disable interrupt handling for interrupt IRQ. Note that any - interrupts received while disabled will be delivered once the - interrupt is enabled again, unless they are explicitly cleared using - `gbus_int_clear_pending_irq'. */ -extern void gbus_int_disable_irq (unsigned irq); -/* Return true if interrupt handling for interrupt IRQ is enabled. */ -extern int gbus_int_irq_enabled (unsigned irq); -/* Disable all GBUS irqs. */ -extern void gbus_int_disable_irqs (void); -/* Clear any pending interrupts for IRQ. */ -extern void gbus_int_clear_pending_irq (unsigned irq); -/* Return true if interrupt IRQ is pending (but disabled). */ -extern int gbus_int_irq_pending (unsigned irq); - - -struct gbus_int_irq_init { - const char *name; /* name of interrupt type */ - - /* Range of kernel irq numbers for this type: - BASE, BASE+INTERVAL, ..., BASE+INTERVAL*NUM */ - unsigned base, num, interval; - - unsigned priority; /* interrupt priority to assign */ -}; -struct hw_interrupt_type; /* fwd decl */ - -/* Initialize HW_IRQ_TYPES for GBUS irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -extern void gbus_int_init_irq_types (struct gbus_int_irq_init *inits, - struct hw_interrupt_type *hw_irq_types); - -/* Initialize GBUS interrupts. */ -extern void gbus_int_init_irqs (void); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_GBUS_INT_H__ */ diff --git a/include/asm-v850/hardirq.h b/include/asm-v850/hardirq.h deleted file mode 100644 index 04e20127c5af..000000000000 --- a/include/asm-v850/hardirq.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __V850_HARDIRQ_H__ -#define __V850_HARDIRQ_H__ - -#include -#include - -#include - -typedef struct { - unsigned int __softirq_pending; -} ____cacheline_aligned irq_cpustat_t; - -#include /* Standard mappings for irq_cpustat_t above */ - -#define HARDIRQ_BITS 8 - -/* - * The hardirq mask has to be large enough to have - * space for potentially all IRQ sources in the system - * nesting on a single CPU: - */ -#if (1 << HARDIRQ_BITS) < NR_IRQS -# error HARDIRQ_BITS is too low! -#endif - -void ack_bad_irq(unsigned int irq); - -#endif /* __V850_HARDIRQ_H__ */ diff --git a/include/asm-v850/highres_timer.h b/include/asm-v850/highres_timer.h deleted file mode 100644 index 486fb49ceab6..000000000000 --- a/include/asm-v850/highres_timer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * include/asm-v850/highres_timer.h -- High resolution timing routines - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_HIGHRES_TIMER_H__ -#define __V850_HIGHRES_TIMER_H__ - -#ifndef __ASSEMBLY__ -#include -#endif - -#include - - -/* Frequency of the `slow ticks' (one tick each time the fast-tick - counter overflows). */ -#define HIGHRES_TIMER_SLOW_TICK_RATE 25 - -/* Which timer in the V850E `Timer D' we use. */ -#define HIGHRES_TIMER_TIMER_D_UNIT 3 - - -#ifndef __ASSEMBLY__ - -extern void highres_timer_start (void), highres_timer_stop (void); -extern void highres_timer_reset (void); -extern void highres_timer_read_ticks (u32 *slow_ticks, u32 *fast_ticks); -extern void highres_timer_ticks_to_timeval (u32 slow_ticks, u32 fast_ticks, - struct timeval *tv); -extern void highres_timer_read (struct timeval *tv); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_HIGHRES_TIMER_H__ */ diff --git a/include/asm-v850/hw_irq.h b/include/asm-v850/hw_irq.h deleted file mode 100644 index 043e94bb6bd8..000000000000 --- a/include/asm-v850/hw_irq.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __V850_HW_IRQ_H__ -#define __V850_HW_IRQ_H__ - -#endif /* __V850_HW_IRQ_H__ */ diff --git a/include/asm-v850/io.h b/include/asm-v850/io.h deleted file mode 100644 index cdad251fba9f..000000000000 --- a/include/asm-v850/io.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * include/asm-v850/io.h -- Misc I/O operations - * - * Copyright (C) 2001,02,03,04,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_IO_H__ -#define __V850_IO_H__ - -#define IO_SPACE_LIMIT 0xFFFFFFFF - -#define readb(addr) \ - ({ unsigned char __v = (*(volatile unsigned char *) (addr)); __v; }) -#define readw(addr) \ - ({ unsigned short __v = (*(volatile unsigned short *) (addr)); __v; }) -#define readl(addr) \ - ({ unsigned long __v = (*(volatile unsigned long *) (addr)); __v; }) - -#define readb_relaxed(a) readb(a) -#define readw_relaxed(a) readw(a) -#define readl_relaxed(a) readl(a) - -#define writeb(val, addr) \ - (void)((*(volatile unsigned char *) (addr)) = (val)) -#define writew(val, addr) \ - (void)((*(volatile unsigned short *) (addr)) = (val)) -#define writel(val, addr) \ - (void)((*(volatile unsigned int *) (addr)) = (val)) - -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel - -#define inb(addr) readb (addr) -#define inw(addr) readw (addr) -#define inl(addr) readl (addr) -#define outb(x, addr) ((void) writeb (x, addr)) -#define outw(x, addr) ((void) writew (x, addr)) -#define outl(x, addr) ((void) writel (x, addr)) - -#define inb_p(port) inb((port)) -#define outb_p(val, port) outb((val), (port)) -#define inw_p(port) inw((port)) -#define outw_p(val, port) outw((val), (port)) -#define inl_p(port) inl((port)) -#define outl_p(val, port) outl((val), (port)) - -static inline void insb (unsigned long port, void *dst, unsigned long count) -{ - unsigned char *p = dst; - while (count--) - *p++ = inb (port); -} -static inline void insw (unsigned long port, void *dst, unsigned long count) -{ - unsigned short *p = dst; - while (count--) - *p++ = inw (port); -} -static inline void insl (unsigned long port, void *dst, unsigned long count) -{ - unsigned long *p = dst; - while (count--) - *p++ = inl (port); -} - -static inline void -outsb (unsigned long port, const void *src, unsigned long count) -{ - const unsigned char *p = src; - while (count--) - outb (*p++, port); -} -static inline void -outsw (unsigned long port, const void *src, unsigned long count) -{ - const unsigned short *p = src; - while (count--) - outw (*p++, port); -} -static inline void -outsl (unsigned long port, const void *src, unsigned long count) -{ - const unsigned long *p = src; - while (count--) - outl (*p++, port); -} - - -/* Some places try to pass in an loff_t for PHYSADDR (?!), so we cast it to - long before casting it to a pointer to avoid compiler warnings. */ -#define ioremap(physaddr, size) ((void __iomem *)(unsigned long)(physaddr)) -#define iounmap(addr) ((void)0) - -#define ioremap_nocache(physaddr, size) ioremap (physaddr, size) -#define ioremap_writethrough(physaddr, size) ioremap (physaddr, size) -#define ioremap_fullcache(physaddr, size) ioremap (physaddr, size) - -#define ioread8(addr) readb (addr) -#define ioread16(addr) readw (addr) -#define ioread32(addr) readl (addr) -#define iowrite8(val, addr) writeb (val, addr) -#define iowrite16(val, addr) writew (val, addr) -#define iowrite32(val, addr) writel (val, addr) - -#define mmiowb() - -#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) -#if 0 -/* This is really stupid; don't define it. */ -#define page_to_bus(page) page_to_phys (page) -#endif - -/* Conversion between virtual and physical mappings. */ -#define phys_to_virt(addr) ((void *)__phys_to_virt (addr)) -#define virt_to_phys(addr) ((unsigned long)__virt_to_phys (addr)) - -#define memcpy_fromio(dst, src, len) memcpy (dst, (void *)src, len) -#define memcpy_toio(dst, src, len) memcpy ((void *)dst, src, len) - -/* - * Convert a physical pointer to a virtual kernel pointer for /dev/mem - * access - */ -#define xlate_dev_mem_ptr(p) __va(p) - -/* - * Convert a virtual cached pointer to an uncached pointer - */ -#define xlate_dev_kmem_ptr(p) p - -#endif /* __V850_IO_H__ */ diff --git a/include/asm-v850/ioctl.h b/include/asm-v850/ioctl.h deleted file mode 100644 index b279fe06dfe5..000000000000 --- a/include/asm-v850/ioctl.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/ioctls.h b/include/asm-v850/ioctls.h deleted file mode 100644 index 5313abd5f388..000000000000 --- a/include/asm-v850/ioctls.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef __V850_IOCTLS_H__ -#define __V850_IOCTLS_H__ - -#include - -/* 0x54 is just a magic number to make these relatively unique ('T') */ - -#define TCGETS 0x5401 -#define TCSETS 0x5402 -#define TCSETSW 0x5403 -#define TCSETSF 0x5404 -#define TCGETA 0x5405 -#define TCSETA 0x5406 -#define TCSETAW 0x5407 -#define TCSETAF 0x5408 -#define TCSBRK 0x5409 -#define TCXONC 0x540A -#define TCFLSH 0x540B -#define TIOCEXCL 0x540C -#define TIOCNXCL 0x540D -#define TIOCSCTTY 0x540E -#define TIOCGPGRP 0x540F -#define TIOCSPGRP 0x5410 -#define TIOCOUTQ 0x5411 -#define TIOCSTI 0x5412 -#define TIOCGWINSZ 0x5413 -#define TIOCSWINSZ 0x5414 -#define TIOCMGET 0x5415 -#define TIOCMBIS 0x5416 -#define TIOCMBIC 0x5417 -#define TIOCMSET 0x5418 -#define TIOCGSOFTCAR 0x5419 -#define TIOCSSOFTCAR 0x541A -#define FIONREAD 0x541B -#define TIOCINQ FIONREAD -#define TIOCLINUX 0x541C -#define TIOCCONS 0x541D -#define TIOCGSERIAL 0x541E -#define TIOCSSERIAL 0x541F -#define TIOCPKT 0x5420 -#define FIONBIO 0x5421 -#define TIOCNOTTY 0x5422 -#define TIOCSETD 0x5423 -#define TIOCGETD 0x5424 -#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ -#define TIOCSBRK 0x5427 /* BSD compatibility */ -#define TIOCCBRK 0x5428 /* BSD compatibility */ -#define TIOCGSID 0x5429 /* Return the session ID of FD */ -#define TCGETS2 _IOR('T',0x2A, struct termios2) -#define TCSETS2 _IOW('T',0x2B, struct termios2) -#define TCSETSW2 _IOW('T',0x2C, struct termios2) -#define TCSETSF2 _IOW('T',0x2D, struct termios2) -#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ -#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ - -#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ -#define FIOCLEX 0x5451 -#define FIOASYNC 0x5452 -#define TIOCSERCONFIG 0x5453 -#define TIOCSERGWILD 0x5454 -#define TIOCSERSWILD 0x5455 -#define TIOCGLCKTRMIOS 0x5456 -#define TIOCSLCKTRMIOS 0x5457 -#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ -#define TIOCSERGETLSR 0x5459 /* Get line status register */ -#define TIOCSERGETMULTI 0x545A /* Get multiport config */ -#define TIOCSERSETMULTI 0x545B /* Set multiport config */ - -#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ -#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ -#define FIOQSIZE 0x545E - -/* Used for packet mode */ -#define TIOCPKT_DATA 0 -#define TIOCPKT_FLUSHREAD 1 -#define TIOCPKT_FLUSHWRITE 2 -#define TIOCPKT_STOP 4 -#define TIOCPKT_START 8 -#define TIOCPKT_NOSTOP 16 -#define TIOCPKT_DOSTOP 32 - -#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ - -#endif /* __V850_IOCTLS_H__ */ diff --git a/include/asm-v850/ipcbuf.h b/include/asm-v850/ipcbuf.h deleted file mode 100644 index d8cbe9886d95..000000000000 --- a/include/asm-v850/ipcbuf.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __V850E_IPCBUF_H__ -#define __V850E_IPCBUF_H__ - -/* - * The user_ipc_perm structure for v850e architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 32-bit mode_t and seq - * - 2 miscellaneous 32-bit values - */ - -struct ipc64_perm -{ - __kernel_key_t key; - __kernel_uid32_t uid; - __kernel_gid32_t gid; - __kernel_uid32_t cuid; - __kernel_gid32_t cgid; - __kernel_mode_t mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - unsigned long __unused1; - unsigned long __unused2; -}; - -#endif /* __V850E_IPCBUF_H__ */ diff --git a/include/asm-v850/irq.h b/include/asm-v850/irq.h deleted file mode 100644 index 7d0d4cd1ce54..000000000000 --- a/include/asm-v850/irq.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * include/asm-v850/irq.h -- Machine interrupt handling - * - * Copyright (C) 2001,02,04 NEC Electronics Corporation - * Copyright (C) 2001,02,04 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_IRQ_H__ -#define __V850_IRQ_H__ - -#include - -/* Default NUM_MACH_IRQS. */ -#ifndef NUM_MACH_IRQS -#define NUM_MACH_IRQS NUM_CPU_IRQS -#endif - -/* NMIs have IRQ numbers from FIRST_NMI to FIRST_NMI+NUM_NMIS-1. */ -#define FIRST_NMI NUM_MACH_IRQS -#define IRQ_NMI(n) (FIRST_NMI + (n)) -/* v850 processors have 3 non-maskable interrupts. */ -#define NUM_NMIS 3 - -/* Includes both maskable and non-maskable irqs. */ -#define NR_IRQS (NUM_MACH_IRQS + NUM_NMIS) - - -#ifndef __ASSEMBLY__ - -struct pt_regs; -struct hw_interrupt_type; -struct irqaction; - -#define irq_canonicalize(irq) (irq) - -/* Initialize irq handling for IRQs. - BASE_IRQ, BASE_IRQ+INTERVAL, ..., BASE_IRQ+NUM*INTERVAL - to IRQ_TYPE. An IRQ_TYPE of 0 means to use a generic interrupt type. */ -extern void -init_irq_handlers (int base_irq, int num, int interval, - struct hw_interrupt_type *irq_type); - -/* Handle interrupt IRQ. REGS are the registers at the time of ther - interrupt. */ -extern unsigned int handle_irq (int irq, struct pt_regs *regs); - -#endif /* !__ASSEMBLY__ */ - -#endif /* __V850_IRQ_H__ */ diff --git a/include/asm-v850/irq_regs.h b/include/asm-v850/irq_regs.h deleted file mode 100644 index 3dd9c0b70270..000000000000 --- a/include/asm-v850/irq_regs.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/kdebug.h b/include/asm-v850/kdebug.h deleted file mode 100644 index 6ece1b037665..000000000000 --- a/include/asm-v850/kdebug.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/kmap_types.h b/include/asm-v850/kmap_types.h deleted file mode 100644 index 3288976b161f..000000000000 --- a/include/asm-v850/kmap_types.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __V850_KMAP_TYPES_H__ -#define __V850_KMAP_TYPES_H__ - -enum km_type { - KM_BOUNCE_READ, - KM_SKB_SUNRPC_DATA, - KM_SKB_DATA_SOFTIRQ, - KM_USER0, - KM_USER1, - KM_BIO_SRC_IRQ, - KM_BIO_DST_IRQ, - KM_PTE0, - KM_PTE1, - KM_IRQ0, - KM_IRQ1, - KM_TYPE_NR -}; - -#endif /* __V850_KMAP_TYPES_H__ */ diff --git a/include/asm-v850/kvm.h b/include/asm-v850/kvm.h deleted file mode 100644 index 3f729b79febc..000000000000 --- a/include/asm-v850/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_V850_H -#define __LINUX_KVM_V850_H - -/* v850 does not support KVM */ - -#endif diff --git a/include/asm-v850/linkage.h b/include/asm-v850/linkage.h deleted file mode 100644 index b6185d3cfe68..000000000000 --- a/include/asm-v850/linkage.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __ASM_LINKAGE_H -#define __ASM_LINKAGE_H - -#ifdef __ASSEMBLY__ -#include -#endif - -#endif diff --git a/include/asm-v850/local.h b/include/asm-v850/local.h deleted file mode 100644 index 705148abe276..000000000000 --- a/include/asm-v850/local.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_LOCAL_H__ -#define __V850_LOCAL_H__ - -#include - -#endif /* __V850_LOCAL_H__ */ diff --git a/include/asm-v850/ma.h b/include/asm-v850/ma.h deleted file mode 100644 index 89e66473a176..000000000000 --- a/include/asm-v850/ma.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * include/asm-v850/ma.h -- V850E/MA series of cpu chips - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MA_H__ -#define __V850_MA_H__ - -/* The MA series uses the V850E cpu core. */ -#include - - -/* For */ -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. The amount - varies between chip models, but there's always at least 4K, and it - should always start at FFFFC000. */ -#define R0_RAM_ADDR 0xFFFFC000 - - -/* MA series UART details. */ -#define V850E_UART_BASE_FREQ CPU_CLOCK_FREQ - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE ma_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void ma_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - - -/* MA series timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - - -/* MA series timer D details. */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ CPU_CLOCK_FREQ - - -/* Port 0 */ -/* Direct I/O. Bits 0-7 are pins P00-P07. */ -#define MA_PORT0_IO_ADDR 0xFFFFF400 -#define MA_PORT0_IO (*(volatile u8 *)MA_PORT0_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define MA_PORT0_PM_ADDR 0xFFFFF420 -#define MA_PORT0_PM (*(volatile u8 *)MA_PORT0_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define MA_PORT0_PMC_ADDR 0xFFFFF440 -#define MA_PORT0_PMC (*(volatile u8 *)MA_PORT0_PMC_ADDR) -/* Port function control (for P04-P07, 0 = IRQ, 1 = DMARQ). */ -#define MA_PORT0_PFC_ADDR 0xFFFFF460 -#define MA_PORT0_PFC (*(volatile u8 *)MA_PORT0_PFC_ADDR) - -/* Port 1 */ -/* Direct I/O. Bits 0-3 are pins P10-P13. */ -#define MA_PORT1_IO_ADDR 0xFFFFF402 -#define MA_PORT1_IO (*(volatile u8 *)MA_PORT1_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define MA_PORT1_PM_ADDR 0xFFFFF420 -#define MA_PORT1_PM (*(volatile u8 *)MA_PORT1_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define MA_PORT1_PMC_ADDR 0xFFFFF442 -#define MA_PORT1_PMC (*(volatile u8 *)MA_PORT1_PMC_ADDR) - -/* Port 4 */ -/* Direct I/O. Bits 0-5 are pins P40-P45. */ -#define MA_PORT4_IO_ADDR 0xFFFFF408 -#define MA_PORT4_IO (*(volatile u8 *)MA_PORT4_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define MA_PORT4_PM_ADDR 0xFFFFF428 -#define MA_PORT4_PM (*(volatile u8 *)MA_PORT4_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define MA_PORT4_PMC_ADDR 0xFFFFF448 -#define MA_PORT4_PMC (*(volatile u8 *)MA_PORT4_PMC_ADDR) -/* Port function control (for serial interfaces, 0 = CSI, 1 = UART). */ -#define MA_PORT4_PFC_ADDR 0xFFFFF468 -#define MA_PORT4_PFC (*(volatile u8 *)MA_PORT4_PFC_ADDR) - - -#ifndef __ASSEMBLY__ - -/* Initialize MA chip interrupts. */ -extern void ma_init_irqs (void); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_MA_H__ */ diff --git a/include/asm-v850/ma1.h b/include/asm-v850/ma1.h deleted file mode 100644 index ede1f1de2b7a..000000000000 --- a/include/asm-v850/ma1.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * include/asm-v850/ma1.h -- V850E/MA1 cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MA1_H__ -#define __V850_MA1_H__ - -/* Inherit more generic details from MA series. */ -#include - - -#define CPU_MODEL "v850e/ma1" -#define CPU_MODEL_LONG "NEC V850E/MA1" - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTOV(n) (n) /* 0-3 */ -#define IRQ_INTOV_NUM 4 -#define IRQ_INTP(n) (0x4 + (n)) /* Pnnn (pin) interrupts */ -#define IRQ_INTP_NUM 24 -#define IRQ_INTCMD(n) (0x1c + (n)) /* interval timer interrupts 0-3 */ -#define IRQ_INTCMD_NUM 4 -#define IRQ_INTDMA(n) (0x20 + (n)) /* DMA interrupts 0-3 */ -#define IRQ_INTDMA_NUM 4 -#define IRQ_INTCSI(n) (0x24 + (n)*4)/* CSI 0-2 transmit/receive completion */ -#define IRQ_INTCSI_NUM 3 -#define IRQ_INTSER(n) (0x25 + (n)*4) /* UART 0-2 reception error */ -#define IRQ_INTSER_NUM 3 -#define IRQ_INTSR(n) (0x26 + (n)*4) /* UART 0-2 reception completion */ -#define IRQ_INTSR_NUM 3 -#define IRQ_INTST(n) (0x27 + (n)*4) /* UART 0-2 transmission completion */ -#define IRQ_INTST_NUM 3 - -#define NUM_CPU_IRQS 0x30 - - -/* The MA1 has a UART with 3 channels. */ -#define V850E_UART_NUM_CHANNELS 3 - - -#endif /* __V850_MA1_H__ */ diff --git a/include/asm-v850/machdep.h b/include/asm-v850/machdep.h deleted file mode 100644 index f1e3b8b91508..000000000000 --- a/include/asm-v850/machdep.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * include/asm-v850/machdep.h -- Machine-dependent definitions - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MACHDEP_H__ -#define __V850_MACHDEP_H__ - - -/* chips */ -#ifdef CONFIG_V850E_MA1 -#include -#endif -#ifdef CONFIG_V850E_ME2 -#include -#endif -#ifdef CONFIG_V850E_TEG -#include -#endif - -/* These are both chips _and_ platforms, so put them in the middle... */ -#ifdef CONFIG_V850E2_ANNA -#include -#endif -#ifdef CONFIG_V850E_AS85EP1 -#include -#endif - -/* platforms */ -#ifdef CONFIG_RTE_CB_MA1 -#include -#endif -#ifdef CONFIG_RTE_CB_ME2 -#include -#endif -#ifdef CONFIG_RTE_CB_NB85E -#include -#endif -#ifdef CONFIG_V850E_SIM -#include -#endif -#ifdef CONFIG_V850E2_SIM85E2C -#include -#endif -#ifdef CONFIG_V850E2_SIM85E2S -#include -#endif -#ifdef CONFIG_V850E2_FPGA85E2C -#include -#endif - -#endif /* __V850_MACHDEP_H__ */ diff --git a/include/asm-v850/macrology.h b/include/asm-v850/macrology.h deleted file mode 100644 index 37abf874832c..000000000000 --- a/include/asm-v850/macrology.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * include/asm-v850/macrology.h -- Various useful CPP macros - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#define macrology_paste(arg1, arg2) macrology_paste_1(arg1, arg2) -#define macrology_paste_1(arg1, arg2) arg1 ## arg2 -#define macrology_stringify(sym) macrology_stringify_1(sym) -#define macrology_stringify_1(sym) #sym diff --git a/include/asm-v850/me2.h b/include/asm-v850/me2.h deleted file mode 100644 index ac7c9ce0bdc1..000000000000 --- a/include/asm-v850/me2.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * include/asm-v850/me2.h -- V850E/ME2 cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ME2_H__ -#define __V850_ME2_H__ - -#include -#include - - -#define CPU_MODEL "v850e/me2" -#define CPU_MODEL_LONG "NEC V850E/ME2" - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTP(n) (n) /* Pnnn (pin) interrupts */ -#define IRQ_INTP_NUM 31 -#define IRQ_INTCMD(n) (0x31 + (n)) /* interval timer interrupts 0-3 */ -#define IRQ_INTCMD_NUM 4 -#define IRQ_INTDMA(n) (0x41 + (n)) /* DMA interrupts 0-3 */ -#define IRQ_INTDMA_NUM 4 -#define IRQ_INTUBTIRE(n) (0x49 + (n)*5)/* UARTB 0-1 reception error */ -#define IRQ_INTUBTIRE_NUM 2 -#define IRQ_INTUBTIR(n) (0x4a + (n)*5) /* UARTB 0-1 reception complete */ -#define IRQ_INTUBTIR_NUM 2 -#define IRQ_INTUBTIT(n) (0x4b + (n)*5) /* UARTB 0-1 transmission complete */ -#define IRQ_INTUBTIT_NUM 2 -#define IRQ_INTUBTIF(n) (0x4c + (n)*5) /* UARTB 0-1 FIFO trans. complete */ -#define IRQ_INTUBTIF_NUM 2 -#define IRQ_INTUBTITO(n) (0x4d + (n)*5) /* UARTB 0-1 reception timeout */ -#define IRQ_INTUBTITO_NUM 2 - -/* For */ -#define NUM_CPU_IRQS 0x59 /* V850E/ME2 */ - - -/* For */ -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. */ -#define R0_RAM_ADDR 0xFFFFB000 /* V850E/ME2 */ - - -/* V850E/ME2 UARTB details.*/ -#define V850E_UART_NUM_CHANNELS 2 -#define V850E_UARTB_BASE_FREQ (CPU_CLOCK_FREQ / 4) - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE me2_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void me2_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif /* __ASSEMBLY__ */ - - -/* V850E/ME2 timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - - -/* V850E/ME2 timer D details. */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ (CPU_CLOCK_FREQ / 2) - - -/* Select iRAM mode. */ -#define ME2_IRAMM_ADDR 0xFFFFF80A -#define ME2_IRAMM (*(volatile u8*)ME2_IRAMM_ADDR) - - -/* Interrupt edge-detection configuration. INTF(n) and INTR(n) are only - valid for n == 1, 2, or 5. */ -#define ME2_INTF_ADDR(n) (0xFFFFFC00 + (n) * 0x2) -#define ME2_INTF(n) (*(volatile u8*)ME2_INTF_ADDR(n)) -#define ME2_INTR_ADDR(n) (0xFFFFFC20 + (n) * 0x2) -#define ME2_INTR(n) (*(volatile u8*)ME2_INTR_ADDR(n)) -#define ME2_INTFAL_ADDR 0xFFFFFC10 -#define ME2_INTFAL (*(volatile u8*)ME2_INTFAL_ADDR) -#define ME2_INTRAL_ADDR 0xFFFFFC30 -#define ME2_INTRAL (*(volatile u8*)ME2_INTRAL_ADDR) -#define ME2_INTFDH_ADDR 0xFFFFFC16 -#define ME2_INTFDH (*(volatile u16*)ME2_INTFDH_ADDR) -#define ME2_INTRDH_ADDR 0xFFFFFC36 -#define ME2_INTRDH (*(volatile u16*)ME2_INTRDH_ADDR) -#define ME2_SESC_ADDR(n) (0xFFFFF609 + (n) * 0x10) -#define ME2_SESC(n) (*(volatile u8*)ME2_SESC_ADDR(n)) -#define ME2_SESA10_ADDR 0xFFFFF5AD -#define ME2_SESA10 (*(volatile u8*)ME2_SESA10_ADDR) -#define ME2_SESA11_ADDR 0xFFFFF5DD -#define ME2_SESA11 (*(volatile u8*)ME2_SESA11_ADDR) - - -/* Port 1 */ -/* Direct I/O. Bits 0-3 are pins P10-P13. */ -#define ME2_PORT1_IO_ADDR 0xFFFFF402 -#define ME2_PORT1_IO (*(volatile u8 *)ME2_PORT1_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT1_PM_ADDR 0xFFFFF422 -#define ME2_PORT1_PM (*(volatile u8 *)ME2_PORT1_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT1_PMC_ADDR 0xFFFFF442 -#define ME2_PORT1_PMC (*(volatile u8 *)ME2_PORT1_PMC_ADDR) -/* Port function control (for serial interfaces, 0 = CSI30, 1 = UARTB0 ). */ -#define ME2_PORT1_PFC_ADDR 0xFFFFF462 -#define ME2_PORT1_PFC (*(volatile u8 *)ME2_PORT1_PFC_ADDR) - -/* Port 2 */ -/* Direct I/O. Bits 0-3 are pins P20-P25. */ -#define ME2_PORT2_IO_ADDR 0xFFFFF404 -#define ME2_PORT2_IO (*(volatile u8 *)ME2_PORT2_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT2_PM_ADDR 0xFFFFF424 -#define ME2_PORT2_PM (*(volatile u8 *)ME2_PORT2_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT2_PMC_ADDR 0xFFFFF444 -#define ME2_PORT2_PMC (*(volatile u8 *)ME2_PORT2_PMC_ADDR) -/* Port function control (for serial interfaces, 0 = INTP2x, 1 = UARTB1 ). */ -#define ME2_PORT2_PFC_ADDR 0xFFFFF464 -#define ME2_PORT2_PFC (*(volatile u8 *)ME2_PORT2_PFC_ADDR) - -/* Port 5 */ -/* Direct I/O. Bits 0-5 are pins P50-P55. */ -#define ME2_PORT5_IO_ADDR 0xFFFFF40A -#define ME2_PORT5_IO (*(volatile u8 *)ME2_PORT5_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT5_PM_ADDR 0xFFFFF42A -#define ME2_PORT5_PM (*(volatile u8 *)ME2_PORT5_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT5_PMC_ADDR 0xFFFFF44A -#define ME2_PORT5_PMC (*(volatile u8 *)ME2_PORT5_PMC_ADDR) -/* Port function control (). */ -#define ME2_PORT5_PFC_ADDR 0xFFFFF46A -#define ME2_PORT5_PFC (*(volatile u8 *)ME2_PORT5_PFC_ADDR) - -/* Port 6 */ -/* Direct I/O. Bits 5-7 are pins P65-P67. */ -#define ME2_PORT6_IO_ADDR 0xFFFFF40C -#define ME2_PORT6_IO (*(volatile u8 *)ME2_PORT6_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT6_PM_ADDR 0xFFFFF42C -#define ME2_PORT6_PM (*(volatile u8 *)ME2_PORT6_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT6_PMC_ADDR 0xFFFFF44C -#define ME2_PORT6_PMC (*(volatile u8 *)ME2_PORT6_PMC_ADDR) -/* Port function control (). */ -#define ME2_PORT6_PFC_ADDR 0xFFFFF46C -#define ME2_PORT6_PFC (*(volatile u8 *)ME2_PORT6_PFC_ADDR) - -/* Port 7 */ -/* Direct I/O. Bits 2-7 are pins P72-P77. */ -#define ME2_PORT7_IO_ADDR 0xFFFFF40E -#define ME2_PORT7_IO (*(volatile u8 *)ME2_PORT7_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT7_PM_ADDR 0xFFFFF42E -#define ME2_PORT7_PM (*(volatile u8 *)ME2_PORT7_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT7_PMC_ADDR 0xFFFFF44E -#define ME2_PORT7_PMC (*(volatile u8 *)ME2_PORT7_PMC_ADDR) -/* Port function control (). */ -#define ME2_PORT7_PFC_ADDR 0xFFFFF46E -#define ME2_PORT7_PFC (*(volatile u8 *)ME2_PORT7_PFC_ADDR) - - -#ifndef __ASSEMBLY__ -/* Initialize V850E/ME2 chip interrupts. */ -extern void me2_init_irqs (void); -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_ME2_H__ */ diff --git a/include/asm-v850/mman.h b/include/asm-v850/mman.h deleted file mode 100644 index edbf6edbfb37..000000000000 --- a/include/asm-v850/mman.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __V850_MMAN_H__ -#define __V850_MMAN_H__ - -#include - -#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ -#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ -#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ -#define MAP_LOCKED 0x2000 /* pages are locked */ -#define MAP_NORESERVE 0x4000 /* don't check for reservations */ - -#define MCL_CURRENT 1 /* lock all current mappings */ -#define MCL_FUTURE 2 /* lock all future mappings */ - -#endif /* __V850_MMAN_H__ */ diff --git a/include/asm-v850/mmu.h b/include/asm-v850/mmu.h deleted file mode 100644 index 267768c66ef6..000000000000 --- a/include/asm-v850/mmu.h +++ /dev/null @@ -1,11 +0,0 @@ -/* Copyright (C) 2002, 2005, David McCullough */ - -#ifndef __V850_MMU_H__ -#define __V850_MMU_H__ - -typedef struct { - struct vm_list_struct *vmlist; - unsigned long end_brk; -} mm_context_t; - -#endif /* __V850_MMU_H__ */ diff --git a/include/asm-v850/mmu_context.h b/include/asm-v850/mmu_context.h deleted file mode 100644 index 01daacd5474e..000000000000 --- a/include/asm-v850/mmu_context.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __V850_MMU_CONTEXT_H__ -#define __V850_MMU_CONTEXT_H__ - -#include - -#define destroy_context(mm) ((void)0) -#define init_new_context(tsk,mm) 0 -#define switch_mm(prev,next,tsk) ((void)0) -#define deactivate_mm(tsk,mm) do { } while (0) -#define activate_mm(prev,next) ((void)0) -#define enter_lazy_tlb(mm,tsk) ((void)0) - -#endif /* __V850_MMU_CONTEXT_H__ */ diff --git a/include/asm-v850/module.h b/include/asm-v850/module.h deleted file mode 100644 index 2c2f4944f09f..000000000000 --- a/include/asm-v850/module.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * include/asm-v850/module.h -- Architecture-specific module hooks - * - * Copyright (C) 2001,02,03,04 NEC Corporation - * Copyright (C) 2001,02,03,04 Miles Bader - * Copyright (C) 2001,03 Rusty Russell - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - * - * Derived in part from include/asm-ppc/module.h - */ - -#ifndef __V850_MODULE_H__ -#define __V850_MODULE_H__ - -#define MODULE_SYMBOL_PREFIX "_" - -struct v850_plt_entry -{ - /* Indirect jump instruction sequence (6-byte mov + 2-byte jr). */ - unsigned long tramp[2]; -}; - -struct mod_arch_specific -{ - /* Indices of PLT sections within module. */ - unsigned int core_plt_section, init_plt_section; -}; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - -/* Make empty sections for module_frob_arch_sections to expand. */ -#ifdef MODULE -asm(".section .plt,\"ax\",@nobits; .align 3; .previous"); -asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous"); -#endif - -/* We don't do exception tables. */ -struct exception_table_entry; -static inline const struct exception_table_entry * -search_extable(const struct exception_table_entry *first, - const struct exception_table_entry *last, - unsigned long value) -{ - return 0; -} -#define ARCH_HAS_SEARCH_EXTABLE -static inline void -sort_extable(struct exception_table_entry *start, - struct exception_table_entry *finish) -{ - /* nada */ -} -#define ARCH_HAS_SORT_EXTABLE - -#endif /* __V850_MODULE_H__ */ diff --git a/include/asm-v850/msgbuf.h b/include/asm-v850/msgbuf.h deleted file mode 100644 index ed07dbd01637..000000000000 --- a/include/asm-v850/msgbuf.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef __V850_MSGBUF_H__ -#define __V850_MSGBUF_H__ - -/* - * The msqid64_ds structure for v850 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct msqid64_ds { - struct ipc64_perm msg_perm; - __kernel_time_t msg_stime; /* last msgsnd time */ - unsigned long __unused1; - __kernel_time_t msg_rtime; /* last msgrcv time */ - unsigned long __unused2; - __kernel_time_t msg_ctime; /* last change time */ - unsigned long __unused3; - unsigned long msg_cbytes; /* current number of bytes on queue */ - unsigned long msg_qnum; /* number of messages in queue */ - unsigned long msg_qbytes; /* max number of bytes on queue */ - __kernel_pid_t msg_lspid; /* pid of last msgsnd */ - __kernel_pid_t msg_lrpid; /* last receive pid */ - unsigned long __unused4; - unsigned long __unused5; -}; - -#endif /* __V850_MSGBUF_H__ */ diff --git a/include/asm-v850/mutex.h b/include/asm-v850/mutex.h deleted file mode 100644 index 458c1f7fbc18..000000000000 --- a/include/asm-v850/mutex.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Pull in the generic implementation for the mutex fastpath. - * - * TODO: implement optimized primitives instead, or leave the generic - * implementation in place, or pick the atomic_xchg() based generic - * implementation. (see asm-generic/mutex-xchg.h for details) - */ - -#include diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h deleted file mode 100644 index f9de35d873fa..000000000000 --- a/include/asm-v850/page.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * include/asm-v850/page.h -- VM ops - * - * Copyright (C) 2001,02,03,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PAGE_H__ -#define __V850_PAGE_H__ - -#include - - -#define PAGE_SHIFT 12 -#define PAGE_SIZE (1UL << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) - - -/* - * PAGE_OFFSET -- the first address of the first page of memory. For archs with - * no MMU this corresponds to the first free page in physical memory (aligned - * on a page boundary). - */ -#ifndef PAGE_OFFSET -#define PAGE_OFFSET 0x0000000 -#endif - - -#ifndef __ASSEMBLY__ - -#define STRICT_MM_TYPECHECKS - -#define clear_page(page) memset ((void *)(page), 0, PAGE_SIZE) -#define copy_page(to, from) memcpy ((void *)(to), (void *)from, PAGE_SIZE) - -#define clear_user_page(addr, vaddr, page) \ - do { clear_page(addr); \ - flush_dcache_page(page); \ - } while (0) -#define copy_user_page(to, from, vaddr, page) \ - do { copy_page(to, from); \ - flush_dcache_page(page); \ - } while (0) - -#ifdef STRICT_MM_TYPECHECKS -/* - * These are used to make use of C type-checking.. - */ - -typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pmd; } pmd_t; -typedef struct { unsigned long pgd; } pgd_t; -typedef struct { unsigned long pgprot; } pgprot_t; -typedef struct page *pgtable_t; - -#define pte_val(x) ((x).pte) -#define pmd_val(x) ((x).pmd) -#define pgd_val(x) ((x).pgd) -#define pgprot_val(x) ((x).pgprot) - -#define __pte(x) ((pte_t) { (x) } ) -#define __pmd(x) ((pmd_t) { (x) } ) -#define __pgd(x) ((pgd_t) { (x) } ) -#define __pgprot(x) ((pgprot_t) { (x) } ) - -#else /* !STRICT_MM_TYPECHECKS */ -/* - * .. while these make it easier on the compiler - */ - -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif /* STRICT_MM_TYPECHECKS */ - -#endif /* !__ASSEMBLY__ */ - - -/* No current v850 processor has virtual memory. */ -#define __virt_to_phys(addr) (addr) -#define __phys_to_virt(addr) (addr) - -#define virt_to_pfn(kaddr) (__virt_to_phys (kaddr) >> PAGE_SHIFT) -#define pfn_to_virt(pfn) __phys_to_virt ((pfn) << PAGE_SHIFT) - -#define MAP_NR(kaddr) \ - (((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) -#define virt_to_page(kaddr) (mem_map + MAP_NR (kaddr)) -#define page_to_virt(page) \ - ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) - -#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) -#define pfn_valid(pfn) ((pfn) < max_mapnr) - -#define virt_addr_valid(kaddr) \ - (((void *)(kaddr) >= (void *)PAGE_OFFSET) && MAP_NR (kaddr) < max_mapnr) - - -#define __pa(x) __virt_to_phys ((unsigned long)(x)) -#define __va(x) ((void *)__phys_to_virt ((unsigned long)(x))) - - -#include -#include - -#endif /* __V850_PAGE_H__ */ diff --git a/include/asm-v850/param.h b/include/asm-v850/param.h deleted file mode 100644 index 4391f5fe0204..000000000000 --- a/include/asm-v850/param.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * include/asm-v850/param.h -- Varions kernel parameters - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PARAM_H__ -#define __V850_PARAM_H__ - -#define EXEC_PAGESIZE 4096 - -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ - -#ifdef __KERNEL__ -# define HZ CONFIG_HZ -# define USER_HZ 100 -# define CLOCKS_PER_SEC USER_HZ -#else -# define HZ 100 -#endif - -#endif /* __V850_PARAM_H__ */ diff --git a/include/asm-v850/pci.h b/include/asm-v850/pci.h deleted file mode 100644 index de2a7d0a81cc..000000000000 --- a/include/asm-v850/pci.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * include/asm-v850/pci.h -- PCI support - * - * Copyright (C) 2001,02,05 NEC Corporation - * Copyright (C) 2001,02,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PCI_H__ -#define __V850_PCI_H__ - -/* Get any platform-dependent definitions. */ -#include - -#define pcibios_scan_all_fns(a, b) 0 - -/* Generic declarations. */ - -struct scatterlist; - -extern void pcibios_set_master (struct pci_dev *dev); - -/* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The - 32-bit PCI bus mastering address to use is returned. the device owns - this memory until either pci_unmap_single or pci_dma_sync_single_for_cpu is - performed. */ -extern dma_addr_t -pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir); - -/* Return to the CPU the PCI DMA memory block previously `granted' to - PDEV, at DMA_ADDR. */ -extern void -pci_unmap_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir); - -/* Make physical memory consistent for a single streaming mode DMA - translation after a transfer. - - If you perform a pci_map_single() but wish to interrogate the - buffer using the cpu, yet do not wish to teardown the PCI dma - mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, you must first - perform a pci_dma_sync_for_device, and then the device again owns - the buffer. */ -extern void -pci_dma_sync_single_for_cpu (struct pci_dev *dev, dma_addr_t dma_addr, - size_t size, int dir); - -extern void -pci_dma_sync_single_for_device (struct pci_dev *dev, dma_addr_t dma_addr, - size_t size, int dir); - - -/* Do multiple DMA mappings at once. */ -extern int -pci_map_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, int dir); - -/* Unmap multiple DMA mappings at once. */ -extern void -pci_unmap_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, - int dir); - -/* SG-list versions of pci_dma_sync functions. */ -extern void -pci_dma_sync_sg_for_cpu (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir); -extern void -pci_dma_sync_sg_for_device (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir); - -#define pci_map_page(dev, page, offs, size, dir) \ - pci_map_single(dev, (page_address(page) + (offs)), size, dir) -#define pci_unmap_page(dev,addr,sz,dir) \ - pci_unmap_single(dev, addr, sz, dir) - -/* Test for pci_map_single or pci_map_page having generated an error. */ -static inline int -pci_dma_mapping_error (dma_addr_t dma_addr) -{ - return dma_addr == 0; -} - -/* Allocate and map kernel buffer using consistent mode DMA for PCI - device. Returns non-NULL cpu-view pointer to the buffer if - successful and sets *DMA_ADDR to the pci side dma address as well, - else DMA_ADDR is undefined. */ -extern void * -pci_alloc_consistent (struct pci_dev *pdev, size_t size, dma_addr_t *dma_addr); - -/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must - be values that were returned from pci_alloc_consistent. SIZE must be - the same as what as passed into pci_alloc_consistent. References to - the memory and mappings assosciated with CPU_ADDR or DMA_ADDR past - this call are illegal. */ -extern void -pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr, - dma_addr_t dma_addr); - -#ifdef CONFIG_PCI -static inline void pci_dma_burst_advice(struct pci_dev *pdev, - enum pci_dma_burst_strategy *strat, - unsigned long *strategy_parameter) -{ - *strat = PCI_DMA_BURST_INFINITY; - *strategy_parameter = ~0UL; -} -#endif - -extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); -extern void pci_iounmap (struct pci_dev *dev, void __iomem *addr); - -#endif /* __V850_PCI_H__ */ diff --git a/include/asm-v850/percpu.h b/include/asm-v850/percpu.h deleted file mode 100644 index 755ac6522b63..000000000000 --- a/include/asm-v850/percpu.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __V850_PERCPU_H__ -#define __V850_PERCPU_H__ - -#include - -/* This is a stupid hack to satisfy some grotty implicit include-file - dependency; basically, uses BUG_ON, which calls BUG, but - doesn't include the necessary headers to define it. In the twisted - festering mess of includes this must all be resolved somehow on other - platforms, but I haven't the faintest idea how, and don't care; here will - do, even though doesn't actually make any sense. */ -#include - -#endif /* __V850_PERCPU_H__ */ diff --git a/include/asm-v850/pgalloc.h b/include/asm-v850/pgalloc.h deleted file mode 100644 index b91eb2d02bfd..000000000000 --- a/include/asm-v850/pgalloc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * include/asm-v850/pgalloc.h - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PGALLOC_H__ -#define __V850_PGALLOC_H__ - -#include /* some crap code expects this */ - -/* ... and then, there was one. */ -#define check_pgt_cache() ((void)0) - -#endif /* __V850_PGALLOC_H__ */ diff --git a/include/asm-v850/pgtable.h b/include/asm-v850/pgtable.h deleted file mode 100644 index 1ea2a900f0f8..000000000000 --- a/include/asm-v850/pgtable.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef __V850_PGTABLE_H__ -#define __V850_PGTABLE_H__ - -#include - -#include - - -#define pgd_present(pgd) (1) /* pages are always present on NO_MM */ -#define pgd_none(pgd) (0) -#define pgd_bad(pgd) (0) -#define pgd_clear(pgdp) ((void)0) - -#define pmd_offset(a, b) ((void *)0) - -#define kern_addr_valid(addr) (1) - - -#define __swp_type(x) (0) -#define __swp_offset(x) (0) -#define __swp_entry(typ,off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - -static inline int pte_file (pte_t pte) { return 0; } - - -/* These mean nothing to !CONFIG_MMU. */ -#define PAGE_NONE __pgprot(0) -#define PAGE_SHARED __pgprot(0) -#define PAGE_COPY __pgprot(0) -#define PAGE_READONLY __pgprot(0) -#define PAGE_KERNEL __pgprot(0) - - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc. When CONFIG_MMU is not defined, this - * should never actually be used, so just define it to something that's - * will hopefully cause a bus error if it is. - */ -#define ZERO_PAGE(vaddr) ((void *)0x87654321) - - -/* Some bogus code in procfs uses these; whatever. */ -#define VMALLOC_START 0 -#define VMALLOC_END (~0) - - -extern void paging_init (void); -#define swapper_pg_dir ((pgd_t *) 0) - -#define pgtable_cache_init() ((void)0) - - -extern unsigned int kobjsize(const void *objp); - - -#endif /* __V850_PGTABLE_H__ */ diff --git a/include/asm-v850/poll.h b/include/asm-v850/poll.h deleted file mode 100644 index 803cad0b9b59..000000000000 --- a/include/asm-v850/poll.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __V850_POLL_H__ -#define __V850_POLL_H__ - -#define POLLWRNORM POLLOUT -#define POLLWRBAND 0x0100 - -#include - -#endif /* __V850_POLL_H__ */ diff --git a/include/asm-v850/posix_types.h b/include/asm-v850/posix_types.h deleted file mode 100644 index 7f403b765390..000000000000 --- a/include/asm-v850/posix_types.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * include/asm-v850/posix_types.h -- Kernel versions of standard types - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_POSIX_TYPES_H__ -#define __V850_POSIX_TYPES_H__ - -typedef unsigned long __kernel_ino_t; -typedef unsigned long long __kernel_ino64_t; -typedef unsigned int __kernel_mode_t; -typedef unsigned int __kernel_nlink_t; -typedef long __kernel_off_t; -typedef long long __kernel_loff_t; -typedef int __kernel_pid_t; -typedef unsigned short __kernel_ipc_pid_t; -typedef unsigned int __kernel_uid_t; -typedef unsigned int __kernel_gid_t; -typedef unsigned int __kernel_size_t; -typedef int __kernel_ssize_t; -typedef int __kernel_ptrdiff_t; -typedef long __kernel_time_t; -typedef long __kernel_suseconds_t; -typedef long __kernel_clock_t; -typedef int __kernel_timer_t; -typedef int __kernel_clockid_t; -typedef int __kernel_daddr_t; -typedef char * __kernel_caddr_t; -typedef unsigned short __kernel_uid16_t; -typedef unsigned short __kernel_gid16_t; -typedef unsigned int __kernel_uid32_t; -typedef unsigned int __kernel_gid32_t; - -/* Some bogus code depends on this; we don't care. */ -typedef __kernel_uid_t __kernel_old_uid_t; -typedef unsigned int __kernel_old_dev_t; - -typedef struct { - int val[2]; -} __kernel_fsid_t; - - -#if defined(__KERNEL__) - -/* We used to include here, which seems the right thing, but - it caused nasty include-file definition order problems. Removing the - include seems to work, so fingers crossed... */ - -#undef __FD_SET -#define __FD_SET(fd, fd_set) \ - __set_bit (fd, (void *)&((__kernel_fd_set *)fd_set)->fds_bits) -#undef __FD_CLR -#define __FD_CLR(fd, fd_set) \ - __clear_bit (fd, (void *)&((__kernel_fd_set *)fd_set)->fds_bits) -#undef __FD_ISSET -#define __FD_ISSET(fd, fd_set) \ - __test_bit (fd, (void *)&((__kernel_fd_set *)fd_set)->fds_bits) -#undef __FD_ZERO -#define __FD_ZERO(fd_set) \ - memset (fd_set, 0, sizeof (*(fd_set *)fd_set)) - -#endif /* defined(__KERNEL__) */ - -#endif /* __V850_POSIX_TYPES_H__ */ diff --git a/include/asm-v850/processor.h b/include/asm-v850/processor.h deleted file mode 100644 index 979e3467f9af..000000000000 --- a/include/asm-v850/processor.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * include/asm-v850/processor.h - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PROCESSOR_H__ -#define __V850_PROCESSOR_H__ - -#ifndef __ASSEMBLY__ /* is not asm-safe. */ -#include -#endif - -#include -#include -#include - -/* Some code expects `segment' stuff to be defined here. */ -#include - - -/* - * The only places this is used seem to be horrible bletcherous kludges, - * so we just define it to be as large as possible. - */ -#define TASK_SIZE (0xFFFFFFFF) - -/* - * This decides where the kernel will search for a free chunk of vm - * space during mmap's. We won't be using it. - */ -#define TASK_UNMAPPED_BASE 0 - - -#ifndef __ASSEMBLY__ - - -/* - * Default implementation of macro that returns current - * instruction pointer ("program counter"). - */ -#define current_text_addr() ({ __label__ _l; _l: &&_l;}) - -/* If you change this, you must change the associated assembly-languages - constants defined below, THREAD_*. */ -struct thread_struct { - /* kernel stack pointer (must be first field in structure) */ - unsigned long ksp; -}; - -#define INIT_THREAD { sizeof init_stack + (unsigned long)init_stack } - - -/* Do necessary setup to start up a newly executed thread. */ -static inline void start_thread (struct pt_regs *regs, - unsigned long pc, unsigned long usp) -{ - regs->pc = pc; - regs->gpr[GPR_SP] = usp; - regs->kernel_mode = 0; -} - -/* Free all resources held by a thread. */ -static inline void release_thread (struct task_struct *dead_task) -{ -} - -/* Prepare to copy thread state - unlazy all lazy status */ -#define prepare_to_copy(tsk) do { } while (0) - -extern int kernel_thread (int (*fn)(void *), void * arg, unsigned long flags); - -/* Free current thread data structures etc. */ -static inline void exit_thread (void) -{ -} - - -/* Return the registers saved during context-switch by the currently - not-running thread T. Note that this only includes some registers! - See entry.S for details. */ -#define thread_saved_regs(t) \ - ((struct pt_regs*)((t)->thread.ksp + STATE_SAVE_PT_OFFSET)) -/* Return saved (kernel) PC of a blocked thread. Actually, we return the - LP register, because the thread is actually blocked in switch_thread, - and we're interested in the PC it will _return_ to. */ -#define thread_saved_pc(t) (thread_saved_regs(t)->gpr[GPR_LP]) - - -unsigned long get_wchan (struct task_struct *p); - - -/* Return some info about the user process TASK. */ -#define task_tos(task) ((unsigned long)task_stack_page(task) + THREAD_SIZE) -#define task_pt_regs(task) ((struct pt_regs *)task_tos (task) - 1) -#define task_sp(task) (task_pt_regs (task)->gpr[GPR_SP]) -#define task_pc(task) (task_pt_regs (task)->pc) -/* Grotty old names for some. */ -#define KSTK_EIP(task) task_pc (task) -#define KSTK_ESP(task) task_sp (task) - - -#define cpu_relax() barrier() - - -#else /* __ASSEMBLY__ */ - -#define THREAD_KSP 0 - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_PROCESSOR_H__ */ diff --git a/include/asm-v850/ptrace.h b/include/asm-v850/ptrace.h deleted file mode 100644 index 4f35cf2cd641..000000000000 --- a/include/asm-v850/ptrace.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * include/asm-v850/ptrace.h -- Access to CPU registers - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PTRACE_H__ -#define __V850_PTRACE_H__ - - -/* v850 general purpose registers with special meanings. */ -#define GPR_ZERO 0 /* constant zero */ -#define GPR_ASM 1 /* reserved for assembler */ -#define GPR_SP 3 /* stack pointer */ -#define GPR_GP 4 /* global data pointer */ -#define GPR_TP 5 /* `text pointer' */ -#define GPR_EP 30 /* `element pointer' */ -#define GPR_LP 31 /* link pointer (current return address) */ - -/* These aren't official names, but they make some code more descriptive. */ -#define GPR_ARG0 6 -#define GPR_ARG1 7 -#define GPR_ARG2 8 -#define GPR_ARG3 9 -#define GPR_RVAL0 10 -#define GPR_RVAL1 11 -#define GPR_RVAL GPR_RVAL0 - -#define NUM_GPRS 32 - -/* v850 `system' registers. */ -#define SR_EIPC 0 -#define SR_EIPSW 1 -#define SR_FEPC 2 -#define SR_FEPSW 3 -#define SR_ECR 4 -#define SR_PSW 5 -#define SR_CTPC 16 -#define SR_CTPSW 17 -#define SR_DBPC 18 -#define SR_DBPSW 19 -#define SR_CTBP 20 -#define SR_DIR 21 -#define SR_ASID 23 - - -#ifndef __ASSEMBLY__ - -typedef unsigned long v850_reg_t; - -/* How processor state is stored on the stack during a syscall/signal. - If you change this structure, change the associated assembly-language - macros below too (PT_*)! */ -struct pt_regs -{ - /* General purpose registers. */ - v850_reg_t gpr[NUM_GPRS]; - - v850_reg_t pc; /* program counter */ - v850_reg_t psw; /* program status word */ - - /* Registers used by `callt' instruction: */ - v850_reg_t ctpc; /* saved program counter */ - v850_reg_t ctpsw; /* saved psw */ - v850_reg_t ctbp; /* base pointer for callt table */ - - char kernel_mode; /* 1 if in `kernel mode', 0 if user mode */ -}; - - -#define instruction_pointer(regs) ((regs)->pc) -#define profile_pc(regs) instruction_pointer(regs) -#define user_mode(regs) (!(regs)->kernel_mode) - -/* When a struct pt_regs is used to save user state for a system call in - the kernel, the system call is stored in the space for R0 (since it's - never used otherwise, R0 being a constant 0). Non-system-calls - simply store 0 there. */ -#define PT_REGS_SYSCALL(regs) (regs)->gpr[0] -#define PT_REGS_SET_SYSCALL(regs, val) ((regs)->gpr[0] = (val)) - -#endif /* !__ASSEMBLY__ */ - - -/* The number of bytes used to store each register. */ -#define _PT_REG_SIZE 4 - -/* Offset of a general purpose register in a struct pt_regs. */ -#define PT_GPR(num) ((num) * _PT_REG_SIZE) - -/* Offsets of various special registers & fields in a struct pt_regs. */ -#define PT_PC ((NUM_GPRS + 0) * _PT_REG_SIZE) -#define PT_PSW ((NUM_GPRS + 1) * _PT_REG_SIZE) -#define PT_CTPC ((NUM_GPRS + 2) * _PT_REG_SIZE) -#define PT_CTPSW ((NUM_GPRS + 3) * _PT_REG_SIZE) -#define PT_CTBP ((NUM_GPRS + 4) * _PT_REG_SIZE) -#define PT_KERNEL_MODE ((NUM_GPRS + 5) * _PT_REG_SIZE) - -/* Where the current syscall number is stashed; obviously only valid in - the kernel! */ -#define PT_CUR_SYSCALL PT_GPR(0) - -/* Size of struct pt_regs, including alignment. */ -#define PT_SIZE ((NUM_GPRS + 6) * _PT_REG_SIZE) - - -/* These are `magic' values for PTRACE_PEEKUSR that return info about where - a process is located in memory. */ -#define PT_TEXT_ADDR (PT_SIZE + 1) -#define PT_TEXT_LEN (PT_SIZE + 2) -#define PT_DATA_ADDR (PT_SIZE + 3) - - -#endif /* __V850_PTRACE_H__ */ diff --git a/include/asm-v850/resource.h b/include/asm-v850/resource.h deleted file mode 100644 index 4b9dcd44f8d1..000000000000 --- a/include/asm-v850/resource.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_RESOURCE_H__ -#define __V850_RESOURCE_H__ - -#include - -#endif /* __V850_RESOURCE_H__ */ diff --git a/include/asm-v850/rte_cb.h b/include/asm-v850/rte_cb.h deleted file mode 100644 index db9879f00aa7..000000000000 --- a/include/asm-v850/rte_cb.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * include/asm-v850/rte_cb.h -- Midas labs RTE-CB series of evaluation boards - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_CB_H__ -#define __V850_RTE_CB_H__ - - -/* The SRAM on the Mother-A motherboard. */ -#define MB_A_SRAM_ADDR GCS0_ADDR -#define MB_A_SRAM_SIZE 0x00200000 /* 2MB */ - - -#ifdef CONFIG_RTE_GBUS_INT -/* GBUS interrupt support. */ - -# include - -# define GBUS_INT_BASE_IRQ NUM_RTE_CB_IRQS -# define GBUS_INT_BASE_ADDR (GCS2_ADDR + 0x00006000) - -/* Some specific interrupts. */ -# define IRQ_MB_A_LAN IRQ_GBUS_INT(10) -# define IRQ_MB_A_PCI1(n) (IRQ_GBUS_INT(16) + (n)) -# define IRQ_MB_A_PCI1_NUM 4 -# define IRQ_MB_A_PCI2(n) (IRQ_GBUS_INT(20) + (n)) -# define IRQ_MB_A_PCI2_NUM 4 -# define IRQ_MB_A_EXT(n) (IRQ_GBUS_INT(24) + (n)) -# define IRQ_MB_A_EXT_NUM 4 -# define IRQ_MB_A_USB_OC(n) (IRQ_GBUS_INT(28) + (n)) -# define IRQ_MB_A_USB_OC_NUM 2 -# define IRQ_MB_A_PCMCIA_OC IRQ_GBUS_INT(30) - -/* We define NUM_MACH_IRQS to include extra interrupts from the GBUS. */ -# define NUM_MACH_IRQS (NUM_RTE_CB_IRQS + IRQ_GBUS_INT_NUM) - -#else /* !CONFIG_RTE_GBUS_INT */ - -# define NUM_MACH_IRQS NUM_RTE_CB_IRQS - -#endif /* CONFIG_RTE_GBUS_INT */ - - -#ifdef CONFIG_RTE_MB_A_PCI -/* Mother-A PCI bus support. */ - -# include - -/* These are the base addresses used for allocating device address - space. 512K of the motherboard SRAM is in the same space, so we have - to be careful not to let it be allocated. */ -# define PCIBIOS_MIN_MEM (MB_A_PCI_MEM_ADDR + 0x80000) -# define PCIBIOS_MIN_IO MB_A_PCI_IO_ADDR - -/* As we don't really support PCI DMA to cpu memory, and use bounce-buffers - instead, perversely enough, this becomes always true! */ -# define pci_dma_supported(dev, mask) 1 -# define pcibios_assign_all_busses() 1 - -#endif /* CONFIG_RTE_MB_A_PCI */ - - -#ifndef __ASSEMBLY__ -extern void rte_cb_early_init (void); -extern void rte_cb_init_irqs (void); -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_RTE_CB_H__ */ diff --git a/include/asm-v850/rte_ma1_cb.h b/include/asm-v850/rte_ma1_cb.h deleted file mode 100644 index bd3162ab9844..000000000000 --- a/include/asm-v850/rte_ma1_cb.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * include/asm-v850/rte_ma1_cb.h -- Midas labs RTE-V850/MA1-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_MA1_CB_H__ -#define __V850_RTE_MA1_CB_H__ - -#include /* Common defs for Midas RTE-CB boards. */ - - -#define PLATFORM "rte-v850e/ma1-cb" -#define PLATFORM_LONG "Midas lab RTE-V850E/MA1-CB" - -#define CPU_CLOCK_FREQ 50000000 /* 50MHz */ - -/* 1MB of onboard SRAM. Note that the monitor ROM uses parts of this - for its own purposes, so care must be taken. Some address lines are - not decoded, so the SRAM area is mirrored every 1MB from 0x400000 to - 0x800000 (exclusive). */ -#define SRAM_ADDR 0x00400000 -#define SRAM_SIZE 0x00100000 /* 1MB */ - -/* 32MB of onbard SDRAM. */ -#define SDRAM_ADDR 0x00800000 -#define SDRAM_SIZE 0x02000000 /* 32MB */ - - -/* CPU addresses of GBUS memory spaces. */ -#define GCS0_ADDR 0x05000000 /* GCS0 - Common SRAM (2MB) */ -#define GCS0_SIZE 0x00200000 /* 2MB */ -#define GCS1_ADDR 0x06000000 /* GCS1 - Flash ROM (8MB) */ -#define GCS1_SIZE 0x00800000 /* 8MB */ -#define GCS2_ADDR 0x07900000 /* GCS2 - I/O registers */ -#define GCS2_SIZE 0x00400000 /* 4MB */ -#define GCS5_ADDR 0x04000000 /* GCS5 - PCI bus space */ -#define GCS5_SIZE 0x01000000 /* 16MB */ -#define GCS6_ADDR 0x07980000 /* GCS6 - PCI control registers */ -#define GCS6_SIZE 0x00000200 /* 512B */ - - -/* For */ -#define PAGE_OFFSET SRAM_ADDR - - -/* The GBUS GINT0 - GINT3 interrupts are connected to the INTP000 - INTP011 - pins on the CPU. These are shared among the GBUS interrupts. */ -#define IRQ_GINT(n) IRQ_INTP(n) -#define IRQ_GINT_NUM 4 - -/* Used by to derive NUM_MACH_IRQS. */ -#define NUM_RTE_CB_IRQS NUM_CPU_IRQS - - -#ifdef CONFIG_ROM_KERNEL -/* Kernel is in ROM, starting at address 0. */ - -#define INTV_BASE 0 - -#else /* !CONFIG_ROM_KERNEL */ - -#ifdef CONFIG_RTE_CB_MULTI -/* Using RAM kernel with ROM monitor for Multi debugger. */ - -/* The chip's real interrupt vectors are in ROM, but they jump to a - secondary interrupt vector table in RAM. */ -#define INTV_BASE 0x004F8000 - -/* Scratch memory used by the ROM monitor, which shouldn't be used by - linux (except for the alternate interrupt vector area, defined - above). */ -#define MON_SCRATCH_ADDR 0x004F8000 -#define MON_SCRATCH_SIZE 0x00008000 /* 32KB */ - -#else /* !CONFIG_RTE_CB_MULTI */ -/* Using RAM-kernel. Assume some sort of boot-loader got us loaded at - address 0. */ - -#define INTV_BASE 0 - -#endif /* CONFIG_RTE_CB_MULTI */ - -#endif /* CONFIG_ROM_KERNEL */ - - -/* Some misc. on-board devices. */ - -/* Seven-segment LED display (two digits). Write-only. */ -#define LED_ADDR(n) (0x07802000 + (n)) -#define LED(n) (*(volatile unsigned char *)LED_ADDR(n)) -#define LED_NUM_DIGITS 2 - - -/* Override the basic MA uart pre-initialization so that we can - initialize extra stuff. */ -#undef V850E_UART_PRE_CONFIGURE /* should be defined by */ -#define V850E_UART_PRE_CONFIGURE rte_ma1_cb_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void rte_ma1_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 0. */ - -/* CTS for UART channel 0 is pin P43 (bit 3 of port 4). */ -#define V850E_UART_CTS(chan) ((chan) == 0 ? !(MA_PORT4_IO & 0x8) : 1) -/* RTS for UART channel 0 is pin P42 (bit 2 of port 4). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 0) { \ - unsigned old = MA_PORT4_IO; \ - if (val) \ - MA_PORT4_IO = old & ~0x4; \ - else \ - MA_PORT4_IO = old | 0x4; \ - } \ - } while (0) - - -#endif /* __V850_RTE_MA1_CB_H__ */ diff --git a/include/asm-v850/rte_mb_a_pci.h b/include/asm-v850/rte_mb_a_pci.h deleted file mode 100644 index 41ac185ca9cd..000000000000 --- a/include/asm-v850/rte_mb_a_pci.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * include/asm-v850/mb_a_pci.h -- PCI support for Midas lab RTE-MOTHER-A board - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MB_A_PCI_H__ -#define __V850_MB_A_PCI_H__ - - -#define MB_A_PCI_MEM_ADDR GCS5_ADDR -#define MB_A_PCI_MEM_SIZE (GCS5_SIZE / 2) -#define MB_A_PCI_IO_ADDR (GCS5_ADDR + MB_A_PCI_MEM_SIZE) -#define MB_A_PCI_IO_SIZE (GCS5_SIZE / 2) -#define MB_A_PCI_REG_BASE_ADDR GCS6_ADDR - -#define MB_A_PCI_PCICR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x4) -#define MB_A_PCI_PCICR (*(volatile u16 *)MB_A_PCI_PCICR_ADDR) -#define MB_A_PCI_PCISR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x6) -#define MB_A_PCI_PCISR (*(volatile u16 *)MB_A_PCI_PCISR_ADDR) -#define MB_A_PCI_PCILTR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xD) -#define MB_A_PCI_PCILTR (*(volatile u8 *)MB_A_PCI_PCILTR_ADDR) -#define MB_A_PCI_PCIBAR0_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x10) -#define MB_A_PCI_PCIBAR0 (*(volatile u32 *)MB_A_PCI_PCIBAR0_ADDR) -#define MB_A_PCI_PCIBAR1_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x14) -#define MB_A_PCI_PCIBAR1 (*(volatile u32 *)MB_A_PCI_PCIBAR1_ADDR) -#define MB_A_PCI_PCIBAR2_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x18) -#define MB_A_PCI_PCIBAR2 (*(volatile u32 *)MB_A_PCI_PCIBAR2_ADDR) -#define MB_A_PCI_VENDOR_ID_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x2C) -#define MB_A_PCI_VENDOR_ID (*(volatile u16 *)MB_A_PCI_VENDOR_ID_ADDR) -#define MB_A_PCI_DEVICE_ID_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x2E) -#define MB_A_PCI_DEVICE_ID (*(volatile u16 *)MB_A_PCI_DEVICE_ID_ADDR) -#define MB_A_PCI_DMRR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x9C) -#define MB_A_PCI_DMRR (*(volatile u32 *)MB_A_PCI_DMRR_ADDR) -#define MB_A_PCI_DMLBAM_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xA0) -#define MB_A_PCI_DMLBAM (*(volatile u32 *)MB_A_PCI_DMLBAM_ADDR) -#define MB_A_PCI_DMLBAI_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xA4) -#define MB_A_PCI_DMLBAI (*(volatile u32 *)MB_A_PCI_DMLBAI_ADDR) -#define MB_A_PCI_PCIPBAM_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xA8) -#define MB_A_PCI_PCIPBAM (*(volatile u32 *)MB_A_PCI_PCIPBAM_ADDR) -/* `PCI Configuration Address Register for Direct Master to PCI IO/CFG' */ -#define MB_A_PCI_DMCFGA_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xAC) -#define MB_A_PCI_DMCFGA (*(volatile u32 *)MB_A_PCI_DMCFGA_ADDR) -/* `PCI Permanent Configuration ID Register' */ -#define MB_A_PCI_PCIHIDR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xF0) -#define MB_A_PCI_PCIHIDR (*(volatile u32 *)MB_A_PCI_PCIHIDR_ADDR) - - -#endif /* __V850_MB_A_PCI_H__ */ diff --git a/include/asm-v850/rte_me2_cb.h b/include/asm-v850/rte_me2_cb.h deleted file mode 100644 index 9922c85c85a8..000000000000 --- a/include/asm-v850/rte_me2_cb.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * include/asm-v850/rte_me2_cb.h -- Midas labs RTE-V850E/ME2-CB board - * - * Copyright (C) 2001,02,03 NEC Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_ME2_CB_H__ -#define __V850_RTE_ME2_CB_H__ - -#include /* Common defs for Midas RTE-CB boards. */ - - -#define PLATFORM "rte-v850e/me2-cb" -#define PLATFORM_LONG "Midas lab RTE-V850E/ME2-CB" - -#define CPU_CLOCK_FREQ 150000000 /* 150MHz */ -#define FIXED_BOGOMIPS 50 - -/* 32MB of onbard SDRAM. */ -#define SDRAM_ADDR 0x00800000 -#define SDRAM_SIZE 0x02000000 /* 32MB */ - - -/* CPU addresses of GBUS memory spaces. */ -#define GCS0_ADDR 0x04000000 /* GCS0 - Common SRAM (2MB) */ -#define GCS0_SIZE 0x00800000 /* 8MB */ -#define GCS1_ADDR 0x04800000 /* GCS1 - Flash ROM (8MB) */ -#define GCS1_SIZE 0x00800000 /* 8MB */ -#define GCS2_ADDR 0x07000000 /* GCS2 - I/O registers */ -#define GCS2_SIZE 0x00800000 /* 8MB */ -#define GCS5_ADDR 0x08000000 /* GCS5 - PCI bus space */ -#define GCS5_SIZE 0x02000000 /* 32MB */ -#define GCS6_ADDR 0x07800000 /* GCS6 - PCI control registers */ -#define GCS6_SIZE 0x00800000 /* 8MB */ - - -/* For */ -#define PAGE_OFFSET SDRAM_ADDR - - -#ifdef CONFIG_ROM_KERNEL -/* Kernel is in ROM, starting at address 0. */ - -#define INTV_BASE 0 -#define ROOT_FS_IMAGE_RW 0 - -#else /* !CONFIG_ROM_KERNEL */ -/* Using RAM-kernel. Assume some sort of boot-loader got us loaded at - address 0. */ - -#define INTV_BASE 0 -#define ROOT_FS_IMAGE_RW 1 - -#endif /* CONFIG_ROM_KERNEL */ - - -/* Some misc. on-board devices. */ - -/* Seven-segment LED display (four digits). */ -#define LED_ADDR(n) (0x0FE02000 + (n)) -#define LED(n) (*(volatile unsigned char *)LED_ADDR(n)) -#define LED_NUM_DIGITS 4 - - -/* On-board PIC. */ - -#define CB_PIC_BASE_ADDR 0x0FE04000 - -#define CB_PIC_INT0M_ADDR (CB_PIC_BASE_ADDR + 0x00) -#define CB_PIC_INT0M (*(volatile u16 *)CB_PIC_INT0M_ADDR) -#define CB_PIC_INT1M_ADDR (CB_PIC_BASE_ADDR + 0x10) -#define CB_PIC_INT1M (*(volatile u16 *)CB_PIC_INT1M_ADDR) -#define CB_PIC_INTR_ADDR (CB_PIC_BASE_ADDR + 0x20) -#define CB_PIC_INTR (*(volatile u16 *)CB_PIC_INTR_ADDR) -#define CB_PIC_INTEN_ADDR (CB_PIC_BASE_ADDR + 0x30) -#define CB_PIC_INTEN (*(volatile u16 *)CB_PIC_INTEN_ADDR) - -#define CB_PIC_INT0EN 0x0001 -#define CB_PIC_INT1EN 0x0002 -#define CB_PIC_INT0SEL 0x0080 - -/* The PIC interrupts themselves. */ -#define CB_PIC_BASE_IRQ NUM_CPU_IRQS -#define IRQ_CB_PIC_NUM 10 - -/* Some specific CB_PIC interrupts. */ -#define IRQ_CB_EXTTM0 (CB_PIC_BASE_IRQ + 0) -#define IRQ_CB_EXTSIO (CB_PIC_BASE_IRQ + 1) -#define IRQ_CB_TOVER (CB_PIC_BASE_IRQ + 2) -#define IRQ_CB_GINT0 (CB_PIC_BASE_IRQ + 3) -#define IRQ_CB_USB (CB_PIC_BASE_IRQ + 4) -#define IRQ_CB_LANC (CB_PIC_BASE_IRQ + 5) -#define IRQ_CB_USB_VBUS_ON (CB_PIC_BASE_IRQ + 6) -#define IRQ_CB_USB_VBUS_OFF (CB_PIC_BASE_IRQ + 7) -#define IRQ_CB_EXTTM1 (CB_PIC_BASE_IRQ + 8) -#define IRQ_CB_EXTTM2 (CB_PIC_BASE_IRQ + 9) - -/* The GBUS GINT1 - GINT3 (note, not GINT0!) interrupts are connected to - the INTP65 - INTP67 pins on the CPU. These are shared among the GBUS - interrupts. */ -#define IRQ_GINT(n) IRQ_INTP((n) + 9) /* 0 is unused! */ -#define IRQ_GINT_NUM 4 /* 0 is unused! */ - -/* The shared interrupt line from the PIC is connected to CPU pin INTP23. */ -#define IRQ_CB_PIC IRQ_INTP(4) /* P23 */ - -/* Used by to derive NUM_MACH_IRQS. */ -#define NUM_RTE_CB_IRQS (NUM_CPU_IRQS + IRQ_CB_PIC_NUM) - - -#ifndef __ASSEMBLY__ -struct cb_pic_irq_init { - const char *name; /* name of interrupt type */ - - /* Range of kernel irq numbers for this type: - BASE, BASE+INTERVAL, ..., BASE+INTERVAL*NUM */ - unsigned base, num, interval; - - unsigned priority; /* interrupt priority to assign */ -}; -struct hw_interrupt_type; /* fwd decl */ - -/* Enable interrupt handling for interrupt IRQ. */ -extern void cb_pic_enable_irq (unsigned irq); -/* Disable interrupt handling for interrupt IRQ. Note that any interrupts - received while disabled will be delivered once the interrupt is enabled - again, unless they are explicitly cleared using `cb_pic_clear_pending_irq'. */ -extern void cb_pic_disable_irq (unsigned irq); -/* Initialize HW_IRQ_TYPES for PIC irqs described in array INITS (which is - terminated by an entry with the name field == 0). */ -extern void cb_pic_init_irq_types (struct cb_pic_irq_init *inits, - struct hw_interrupt_type *hw_irq_types); -/* Initialize PIC interrupts. */ -extern void cb_pic_init_irqs (void); -#endif /* __ASSEMBLY__ */ - - -/* TL16C550C on board UART see also asm/serial.h */ -#define CB_UART_BASE 0x0FE08000 -#define CB_UART_REG_GAP 0x10 -#define CB_UART_CLOCK 0x16000000 - -/* CompactFlash setting */ -#define CB_CF_BASE 0x0FE0C000 -#define CB_CF_CCR_ADDR (CB_CF_BASE+0x200) -#define CB_CF_CCR (*(volatile u8 *)CB_CF_CCR_ADDR) -#define CB_CF_REG0_ADDR (CB_CF_BASE+0x1000) -#define CB_CF_REG0 (*(volatile u16 *)CB_CF_REG0_ADDR) -#define CB_CF_STS0_ADDR (CB_CF_BASE+0x1004) -#define CB_CF_STS0 (*(volatile u16 *)CB_CF_STS0_ADDR) -#define CB_PCATA_BASE (CB_CF_BASE+0x800) -#define CB_IDE_BASE (CB_CF_BASE+0x9F0) -#define CB_IDE_CTRL (CB_CF_BASE+0xBF6) -#define CB_IDE_REG_OFFS 0x1 - - -/* SMSC LAN91C111 setting */ -#if defined(CONFIG_SMC91111) -#define CB_LANC_BASE 0x0FE10300 -#define CONFIG_SMC16BITONLY -#define ETH0_ADDR CB_LANC_BASE -#define ETH0_IRQ IRQ_CB_LANC -#endif /* CONFIG_SMC16BITONLY */ - - -#undef V850E_UART_PRE_CONFIGURE -#define V850E_UART_PRE_CONFIGURE rte_me2_cb_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void rte_me2_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif /* __ASSEMBLY__ */ - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 0. */ - -/* CTS for UART channel 0 is pin P22 (bit 2 of port 2). */ -#define V850E_UART_CTS(chan) ((chan) == 0 ? !(ME2_PORT2_IO & 0x4) : 1) -/* RTS for UART channel 0 is pin P21 (bit 1 of port 2). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 0) { \ - unsigned old = ME2_PORT2_IO; \ - if (val) \ - ME2_PORT2_IO = old & ~0x2; \ - else \ - ME2_PORT2_IO = old | 0x2; \ - } \ - } while (0) - - -#ifndef __ASSEMBLY__ -extern void rte_me2_cb_init_irqs (void); -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_RTE_ME2_CB_H__ */ diff --git a/include/asm-v850/rte_nb85e_cb.h b/include/asm-v850/rte_nb85e_cb.h deleted file mode 100644 index f56591cad90a..000000000000 --- a/include/asm-v850/rte_nb85e_cb.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * include/asm-v850/rte_nb85e_cb.h -- Midas labs RTE-V850/NB85E-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_NB85E_CB_H__ -#define __V850_RTE_NB85E_CB_H__ - -#include /* Common defs for Midas RTE-CB boards. */ - - -#define PLATFORM "rte-v850e/nb85e-cb" -#define PLATFORM_LONG "Midas lab RTE-V850E/NB85E-CB" - -#define CPU_CLOCK_FREQ 50000000 /* 50MHz */ - -/* 1MB of onboard SRAM. Note that the monitor ROM uses parts of this - for its own purposes, so care must be taken. */ -#define SRAM_ADDR 0x03C00000 -#define SRAM_SIZE 0x00100000 /* 1MB */ - -/* 16MB of onbard SDRAM. */ -#define SDRAM_ADDR 0x01000000 -#define SDRAM_SIZE 0x01000000 /* 16MB */ - - -/* CPU addresses of GBUS memory spaces. */ -#define GCS0_ADDR 0x00400000 /* GCS0 - Common SRAM (2MB) */ -#define GCS0_SIZE 0x00400000 /* 4MB */ -#define GCS1_ADDR 0x02000000 /* GCS1 - Flash ROM (8MB) */ -#define GCS1_SIZE 0x00800000 /* 8MB */ -#define GCS2_ADDR 0x03900000 /* GCS2 - I/O registers */ -#define GCS2_SIZE 0x00080000 /* 512KB */ -#define GCS3_ADDR 0x02800000 /* GCS3 - EXT-bus: memory space */ -#define GCS3_SIZE 0x00800000 /* 8MB */ -#define GCS4_ADDR 0x03A00000 /* GCS4 - EXT-bus: I/O space */ -#define GCS4_SIZE 0x00200000 /* 2MB */ -#define GCS5_ADDR 0x00800000 /* GCS5 - PCI bus space */ -#define GCS5_SIZE 0x00800000 /* 8MB */ -#define GCS6_ADDR 0x03980000 /* GCS6 - PCI control registers */ -#define GCS6_SIZE 0x00010000 /* 64KB */ - - -/* The GBUS GINT0 - GINT3 interrupts are connected to CPU interrupts 10-12. - These are shared among the GBUS interrupts. */ -#define IRQ_GINT(n) (10 + (n)) -#define IRQ_GINT_NUM 3 - -/* Used by to derive NUM_MACH_IRQS. */ -#define NUM_RTE_CB_IRQS NUM_CPU_IRQS - - -#ifdef CONFIG_ROM_KERNEL -/* Kernel is in ROM, starting at address 0. */ - -#define INTV_BASE 0 - -#else /* !CONFIG_ROM_KERNEL */ -/* We're using the ROM monitor. */ - -/* The chip's real interrupt vectors are in ROM, but they jump to a - secondary interrupt vector table in RAM. */ -#define INTV_BASE 0x03CF8000 - -/* Scratch memory used by the ROM monitor, which shouldn't be used by - linux (except for the alternate interrupt vector area, defined - above). */ -#define MON_SCRATCH_ADDR 0x03CE8000 -#define MON_SCRATCH_SIZE 0x00018000 /* 96KB */ - -#endif /* CONFIG_ROM_KERNEL */ - - -/* Some misc. on-board devices. */ - -/* Seven-segment LED display (two digits). Write-only. */ -#define LED_ADDR(n) (0x03802000 + (n)) -#define LED(n) (*(volatile unsigned char *)LED_ADDR(n)) -#define LED_NUM_DIGITS 4 - - -/* Override the basic TEG UART pre-initialization so that we can - initialize extra stuff. */ -#undef V850E_UART_PRE_CONFIGURE /* should be defined by */ -#define V850E_UART_PRE_CONFIGURE rte_nb85e_cb_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void rte_nb85e_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART. */ - -/* CTS is pin P00. */ -#define V850E_UART_CTS(chan) (! (TEG_PORT0_IO & 0x1)) -/* RTS is pin P02. */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - unsigned old = TEG_PORT0_IO; \ - TEG_PORT0_IO = val ? (old & ~0x4) : (old | 0x4); \ - } while (0) - - -#endif /* __V850_RTE_NB85E_CB_H__ */ diff --git a/include/asm-v850/scatterlist.h b/include/asm-v850/scatterlist.h deleted file mode 100644 index 02d27b3fb061..000000000000 --- a/include/asm-v850/scatterlist.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * include/asm-v850/scatterlist.h - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SCATTERLIST_H__ -#define __V850_SCATTERLIST_H__ - -#include - -struct scatterlist { -#ifdef CONFIG_DEBUG_SG - unsigned long sg_magic; -#endif - unsigned long page_link; - unsigned offset; - dma_addr_t dma_address; - unsigned length; -}; - -#define ISA_DMA_THRESHOLD (~0UL) - -#endif /* __V850_SCATTERLIST_H__ */ diff --git a/include/asm-v850/sections.h b/include/asm-v850/sections.h deleted file mode 100644 index e0238253a0d0..000000000000 --- a/include/asm-v850/sections.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_SECTIONS_H__ -#define __V850_SECTIONS_H__ - -#include - -#endif /* __V850_SECTIONS_H__ */ diff --git a/include/asm-v850/segment.h b/include/asm-v850/segment.h deleted file mode 100644 index 5e2b15dcf3d9..000000000000 --- a/include/asm-v850/segment.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __V850_SEGMENT_H__ -#define __V850_SEGMENT_H__ - - -#ifndef __ASSEMBLY__ - -typedef unsigned long mm_segment_t; /* domain register */ - -#endif /* !__ASSEMBLY__ */ - - -#define __KERNEL_CS 0x0 -#define __KERNEL_DS 0x0 - -#define __USER_CS 0x1 -#define __USER_DS 0x1 - -#define KERNEL_DS __KERNEL_DS -#define KERNEL_CS __KERNEL_CS -#define USER_DS __USER_DS -#define USER_CS __USER_CS - -#define segment_eq(a,b) ((a) == (b)) - -#define get_ds() (KERNEL_DS) -#define get_fs() (USER_DS) - -#define set_fs(seg) ((void)(seg)) - - -#define copy_segments(task, mm) ((void)((void)(task), (mm))) -#define release_segments(mm) ((void)(mm)) -#define forget_segments() ((void)0) - - -#endif /* __V850_SEGMENT_H__ */ diff --git a/include/asm-v850/semaphore.h b/include/asm-v850/semaphore.h deleted file mode 100644 index d9b2034ed1d2..000000000000 --- a/include/asm-v850/semaphore.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/sembuf.h b/include/asm-v850/sembuf.h deleted file mode 100644 index 1622231a8b85..000000000000 --- a/include/asm-v850/sembuf.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __V850_SEMBUF_H__ -#define __V850_SEMBUF_H__ - -/* - * The semid64_ds structure for v850 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct semid64_ds { - struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ - __kernel_time_t sem_otime; /* last semop time */ - unsigned long __unused1; - __kernel_time_t sem_ctime; /* last change time */ - unsigned long __unused2; - unsigned long sem_nsems; /* no. of semaphores in array */ - unsigned long __unused3; - unsigned long __unused4; -}; - -#endif /* __V850_SEMBUF_H__ */ diff --git a/include/asm-v850/serial.h b/include/asm-v850/serial.h deleted file mode 100644 index 36d8f4cbbf39..000000000000 --- a/include/asm-v850/serial.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1999 by Ralf Baechle - * Copyright (C) 1999, 2000 Silicon Graphics, Inc. - */ - -#ifdef CONFIG_RTE_CB_ME2 - -#include - -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) - -#define irq_cannonicalize(x) (x) -#define BASE_BAUD 250000 /* (16MHz / (16 * 38400)) * 9600 */ -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, CB_UART_BASE, IRQ_CB_EXTSIO, STD_COM_FLAGS }, - -/* Redefine UART register offsets. */ -#undef UART_RX -#undef UART_TX -#undef UART_DLL -#undef UART_TRG -#undef UART_DLM -#undef UART_IER -#undef UART_FCTR -#undef UART_IIR -#undef UART_FCR -#undef UART_EFR -#undef UART_LCR -#undef UART_MCR -#undef UART_LSR -#undef UART_MSR -#undef UART_SCR -#undef UART_EMSR - -#define UART_RX (0 * CB_UART_REG_GAP) -#define UART_TX (0 * CB_UART_REG_GAP) -#define UART_DLL (0 * CB_UART_REG_GAP) -#define UART_TRG (0 * CB_UART_REG_GAP) -#define UART_DLM (1 * CB_UART_REG_GAP) -#define UART_IER (1 * CB_UART_REG_GAP) -#define UART_FCTR (1 * CB_UART_REG_GAP) -#define UART_IIR (2 * CB_UART_REG_GAP) -#define UART_FCR (2 * CB_UART_REG_GAP) -#define UART_EFR (2 * CB_UART_REG_GAP) -#define UART_LCR (3 * CB_UART_REG_GAP) -#define UART_MCR (4 * CB_UART_REG_GAP) -#define UART_LSR (5 * CB_UART_REG_GAP) -#define UART_MSR (6 * CB_UART_REG_GAP) -#define UART_SCR (7 * CB_UART_REG_GAP) -#define UART_EMSR (7 * CB_UART_REG_GAP) - -#endif /* CONFIG_RTE_CB_ME2 */ diff --git a/include/asm-v850/setup.h b/include/asm-v850/setup.h deleted file mode 100644 index c48a9b97d05b..000000000000 --- a/include/asm-v850/setup.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _V850_SETUP_H -#define _V850_SETUP_H - -#define COMMAND_LINE_SIZE 512 - -#endif /* __SETUP_H */ diff --git a/include/asm-v850/shmbuf.h b/include/asm-v850/shmbuf.h deleted file mode 100644 index 3d085c9c418e..000000000000 --- a/include/asm-v850/shmbuf.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __V850_SHMBUF_H__ -#define __V850_SHMBUF_H__ - -/* - * The shmid64_ds structure for v850 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct shmid64_ds { - struct ipc64_perm shm_perm; /* operation perms */ - size_t shm_segsz; /* size of segment (bytes) */ - __kernel_time_t shm_atime; /* last attach time */ - unsigned long __unused1; - __kernel_time_t shm_dtime; /* last detach time */ - unsigned long __unused2; - __kernel_time_t shm_ctime; /* last change time */ - unsigned long __unused3; - __kernel_pid_t shm_cpid; /* pid of creator */ - __kernel_pid_t shm_lpid; /* pid of last operator */ - unsigned long shm_nattch; /* no. of current attaches */ - unsigned long __unused4; - unsigned long __unused5; -}; - -struct shminfo64 { - unsigned long shmmax; - unsigned long shmmin; - unsigned long shmmni; - unsigned long shmseg; - unsigned long shmall; - unsigned long __unused1; - unsigned long __unused2; - unsigned long __unused3; - unsigned long __unused4; -}; - -#endif /* __V850_SHMBUF_H__ */ diff --git a/include/asm-v850/shmparam.h b/include/asm-v850/shmparam.h deleted file mode 100644 index 7dcb6739073e..000000000000 --- a/include/asm-v850/shmparam.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_SHMPARAM_H__ -#define __V850_SHMPARAM_H__ - -#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ - -#endif /* __V850_SHMPARAM_H__ */ diff --git a/include/asm-v850/sigcontext.h b/include/asm-v850/sigcontext.h deleted file mode 100644 index e0890f6f4bc9..000000000000 --- a/include/asm-v850/sigcontext.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * include/asm-v850/sigcontext.h -- Signal contexts - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIGCONTEXT_H__ -#define __V850_SIGCONTEXT_H__ - -#include - -struct sigcontext -{ - struct pt_regs regs; - unsigned long oldmask; -}; - -#endif /* __V850_SIGCONTEXT_H__ */ diff --git a/include/asm-v850/siginfo.h b/include/asm-v850/siginfo.h deleted file mode 100644 index 7eb94703dce0..000000000000 --- a/include/asm-v850/siginfo.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_SIGINFO_H__ -#define __V850_SIGINFO_H__ - -#include - -#endif /* __V850_SIGINFO_H__ */ diff --git a/include/asm-v850/signal.h b/include/asm-v850/signal.h deleted file mode 100644 index a38df0834bbf..000000000000 --- a/include/asm-v850/signal.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef __V850_SIGNAL_H__ -#define __V850_SIGNAL_H__ - -#include - -/* Avoid too many header ordering problems. */ -struct siginfo; - - -#ifdef __KERNEL__ - -/* Most things should be clean enough to redefine this at will, if care - is taken to make libc match. */ -#define _NSIG 64 -#define _NSIG_BPW 32 -#define _NSIG_WORDS (_NSIG / _NSIG_BPW) - -typedef unsigned long old_sigset_t; /* at least 32 bits */ - -typedef struct { - unsigned long sig[_NSIG_WORDS]; -} sigset_t; - -#else /* !__KERNEL__ */ - -/* Here we must cater to libcs that poke about in kernel headers. */ - -#define NSIG 32 -typedef unsigned long sigset_t; - -#endif /* __KERNEL__ */ - - -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGIOT 6 -#define SIGBUS 7 -#define SIGFPE 8 -#define SIGKILL 9 -#define SIGUSR1 10 -#define SIGSEGV 11 -#define SIGUSR2 12 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 -#define SIGSTKFLT 16 -#define SIGCHLD 17 -#define SIGCONT 18 -#define SIGSTOP 19 -#define SIGTSTP 20 -#define SIGTTIN 21 -#define SIGTTOU 22 -#define SIGURG 23 -#define SIGXCPU 24 -#define SIGXFSZ 25 -#define SIGVTALRM 26 -#define SIGPROF 27 -#define SIGWINCH 28 -#define SIGIO 29 -#define SIGPOLL SIGIO -/* -#define SIGLOST 29 -*/ -#define SIGPWR 30 -#define SIGSYS 31 -#define SIGUNUSED 31 - -/* These should not be considered constants from userland. */ -#define SIGRTMIN 32 -#define SIGRTMAX _NSIG - -/* - * SA_FLAGS values: - * - * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. - * SA_RESETHAND clears the handler when the signal is delivered. - * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. - * SA_NODEFER prevents the current signal from being masked in the handler. - * - * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single - * Unix names RESETHAND and NODEFER respectively. - */ -#define SA_NOCLDSTOP 0x00000001 -#define SA_NOCLDWAIT 0x00000002 -#define SA_SIGINFO 0x00000004 -#define SA_ONSTACK 0x08000000 -#define SA_RESTART 0x10000000 -#define SA_NODEFER 0x40000000 -#define SA_RESETHAND 0x80000000 - -#define SA_NOMASK SA_NODEFER -#define SA_ONESHOT SA_RESETHAND - -#define SA_RESTORER 0x04000000 - -/* - * sigaltstack controls - */ -#define SS_ONSTACK 1 -#define SS_DISABLE 2 - -#define MINSIGSTKSZ 2048 -#define SIGSTKSZ 8192 - -#include - -#ifdef __KERNEL__ - -struct old_sigaction { - __sighandler_t sa_handler; - old_sigset_t sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -}; - -struct sigaction { - __sighandler_t sa_handler; - unsigned long sa_flags; - void (*sa_restorer)(void); - sigset_t sa_mask; /* mask last for extensibility */ -}; - -struct k_sigaction { - struct sigaction sa; -}; - -#else /* !__KERNEL__ */ - -/* Here we must cater to libcs that poke about in kernel headers. */ - -struct sigaction { - union { - __sighandler_t _sa_handler; - void (*_sa_sigaction)(int, struct siginfo *, void *); - } _u; - sigset_t sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -}; - -#define sa_handler _u._sa_handler -#define sa_sigaction _u._sa_sigaction - -#endif /* __KERNEL__ */ - - -typedef struct sigaltstack { - void *ss_sp; - int ss_flags; - size_t ss_size; -} stack_t; - -#ifdef __KERNEL__ - -#include -#undef __HAVE_ARCH_SIG_BITOPS - -#define ptrace_signal_deliver(regs, cookie) do { } while (0) - -#endif /* __KERNEL__ */ - -#endif /* __V850_SIGNAL_H__ */ diff --git a/include/asm-v850/sim.h b/include/asm-v850/sim.h deleted file mode 100644 index 026932d476cd..000000000000 --- a/include/asm-v850/sim.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * include/asm-v850/sim.h -- Machine-dependent defs for GDB v850e simulator - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM_H__ -#define __V850_SIM_H__ - - -#define CPU_ARCH "v850e" -#define CPU_MODEL "v850e" -#define CPU_MODEL_LONG "NEC V850E" -#define PLATFORM "gdb/v850e" -#define PLATFORM_LONG "GDB V850E simulator" - - -/* We use a weird value for RAM, not just 0, for testing purposes. - These must match the values used in the linker script. */ -#define RAM_ADDR 0x8F000000 -#define RAM_SIZE 0x03000000 - - -/* For */ -#define PAGE_OFFSET RAM_ADDR - - -/* For */ -/* `R0 RAM', used for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. On real - processors, this usually is on-chip RAM, but here we just - choose an arbitrary address that meets the above constraint. */ -#define R0_RAM_ADDR 0xFFFFF000 - - -/* For */ -#define NUM_CPU_IRQS 6 - - -#endif /* __V850_SIM_H__ */ diff --git a/include/asm-v850/sim85e2.h b/include/asm-v850/sim85e2.h deleted file mode 100644 index 8b4d6974066c..000000000000 --- a/include/asm-v850/sim85e2.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * include/asm-v850/sim85e2.h -- Machine-dependent defs for - * V850E2 RTL simulator - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM85E2_H__ -#define __V850_SIM85E2_H__ - - -#include /* Based on V850E2 core. */ - - -/* Various memory areas supported by the simulator. - These should match the corresponding definitions in the linker script. */ - -/* `instruction RAM'; instruction fetches are much faster from IRAM than - from DRAM. */ -#define IRAM_ADDR 0 -#define IRAM_SIZE 0x00100000 /* 1MB */ -/* `data RAM', below and contiguous with the I/O space. - Data fetches are much faster from DRAM than from IRAM. */ -#define DRAM_ADDR 0xfff00000 -#define DRAM_SIZE 0x000ff000 /* 1020KB */ -/* `external ram'. Unlike the above RAM areas, this memory is cached, - so both instruction and data fetches should be (mostly) fast -- - however, currently only write-through caching is supported, so writes - to ERAM will be slow. */ -#define ERAM_ADDR 0x00100000 -#define ERAM_SIZE 0x07f00000 /* 127MB (max) */ -/* Dynamic RAM; uses memory controller. */ -#define SDRAM_ADDR 0x10000000 -#define SDRAM_SIZE 0x01000000 /* 16MB */ - - -/* Simulator specific control registers. */ -/* NOTHAL controls whether the simulator will stop at a `halt' insn. */ -#define SIM85E2_NOTHAL_ADDR 0xffffff22 -#define SIM85E2_NOTHAL (*(volatile u8 *)SIM85E2_NOTHAL_ADDR) -/* The simulator will stop N cycles after N is written to SIMFIN. */ -#define SIM85E2_SIMFIN_ADDR 0xffffff24 -#define SIM85E2_SIMFIN (*(volatile u16 *)SIM85E2_SIMFIN_ADDR) - - -/* For */ -#define NUM_CPU_IRQS 64 - - -/* For */ -#define PAGE_OFFSET SDRAM_ADDR - - -/* For */ -/* `R0 RAM', used for a few miscellaneous variables that must be accessible - using a load instruction relative to R0. The sim85e2 simulator - actually puts 1020K of RAM from FFF00000 to FFFFF000, so we arbitarily - choose a small portion at the end of that. */ -#define R0_RAM_ADDR 0xFFFFE000 - - -#endif /* __V850_SIM85E2_H__ */ diff --git a/include/asm-v850/sim85e2c.h b/include/asm-v850/sim85e2c.h deleted file mode 100644 index eee543ff3af8..000000000000 --- a/include/asm-v850/sim85e2c.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-v850/sim85e2c.h -- Machine-dependent defs for - * V850E2 RTL simulator - * - * Copyright (C) 2002 NEC Corporation - * Copyright (C) 2002 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM85E2C_H__ -#define __V850_SIM85E2C_H__ - -/* Use generic sim85e2 settings, other than the various names. */ -#include - -#define CPU_MODEL "v850e2" -#define CPU_MODEL_LONG "NEC V850E2" -#define PLATFORM "sim85e2c" -#define PLATFORM_LONG "SIM85E2C V850E2 simulator" - -#endif /* __V850_SIM85E2C_H__ */ diff --git a/include/asm-v850/sim85e2s.h b/include/asm-v850/sim85e2s.h deleted file mode 100644 index ee066d5d3c51..000000000000 --- a/include/asm-v850/sim85e2s.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * include/asm-v850/sim85e2s.h -- Machine-dependent defs for - * V850E2 RTL simulator - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM85E2S_H__ -#define __V850_SIM85E2S_H__ - -#include /* Use generic sim85e2 settings. */ -#if 0 -#include /* + cache */ -#endif - -#define CPU_MODEL "v850e2" -#define CPU_MODEL_LONG "NEC V850E2" -#define PLATFORM "sim85e2s" -#define PLATFORM_LONG "SIM85E2S V850E2 simulator" - -#endif /* __V850_SIM85E2S_H__ */ diff --git a/include/asm-v850/simsyscall.h b/include/asm-v850/simsyscall.h deleted file mode 100644 index 4a19d5ae9d17..000000000000 --- a/include/asm-v850/simsyscall.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * include/asm-v850/simsyscall.h -- `System calls' under the v850e emulator - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIMSYSCALL_H__ -#define __V850_SIMSYSCALL_H__ - -#define V850_SIM_SYS_exit(a...) V850_SIM_SYSCALL_1 (1 , ##a) -#define V850_SIM_SYS_fork(a...) V850_SIM_SYSCALL_0 (2 , ##a) -#define V850_SIM_SYS_read(a...) V850_SIM_SYSCALL_3 (3 , ##a) -#define V850_SIM_SYS_write(a...) V850_SIM_SYSCALL_3 (4 , ##a) -#define V850_SIM_SYS_open(a...) V850_SIM_SYSCALL_2 (5 , ##a) -#define V850_SIM_SYS_close(a...) V850_SIM_SYSCALL_1 (6 , ##a) -#define V850_SIM_SYS_wait4(a...) V850_SIM_SYSCALL_4 (7 , ##a) -/* #define V850_SIM_SYS_creat(a...) V850_SIM_SYSCALL_1 (8 , ##a) */ -/* #define V850_SIM_SYS_link(a...) V850_SIM_SYSCALL_1 (9 , ##a) */ -/* #define V850_SIM_SYS_unlink(a...) V850_SIM_SYSCALL_1 (10 , ##a) */ -#define V850_SIM_SYS_execv(a...) V850_SIM_SYSCALL_2 (11 , ##a) -/* #define V850_SIM_SYS_chdir(a...) V850_SIM_SYSCALL_1 (12 , ##a) */ -/* #define V850_SIM_SYS_mknod(a...) V850_SIM_SYSCALL_1 (14 , ##a) */ -#define V850_SIM_SYS_chmod(a...) V850_SIM_SYSCALL_2 (15 , ##a) -#define V850_SIM_SYS_chown(a...) V850_SIM_SYSCALL_2 (16 , ##a) -#define V850_SIM_SYS_lseek(a...) V850_SIM_SYSCALL_3 (19 , ##a) -/* #define V850_SIM_SYS_getpid(a...) V850_SIM_SYSCALL_1 (20 , ##a) */ -/* #define V850_SIM_SYS_isatty(a...) V850_SIM_SYSCALL_1 (21 , ##a) */ -/* #define V850_SIM_SYS_fstat(a...) V850_SIM_SYSCALL_1 (22 , ##a) */ -#define V850_SIM_SYS_time(a...) V850_SIM_SYSCALL_1 (23 , ##a) -#define V850_SIM_SYS_poll(a...) V850_SIM_SYSCALL_3 (24 , ##a) -#define V850_SIM_SYS_stat(a...) V850_SIM_SYSCALL_2 (38 , ##a) -#define V850_SIM_SYS_pipe(a...) V850_SIM_SYSCALL_1 (42 , ##a) -#define V850_SIM_SYS_times(a...) V850_SIM_SYSCALL_1 (43 , ##a) -#define V850_SIM_SYS_execve(a...) V850_SIM_SYSCALL_3 (59 , ##a) -#define V850_SIM_SYS_gettimeofday(a...) V850_SIM_SYSCALL_2 (116 , ##a) -/* #define V850_SIM_SYS_utime(a...) V850_SIM_SYSCALL_2 (201 , ##a) */ -/* #define V850_SIM_SYS_wait(a...) V850_SIM_SYSCALL_1 (202 , ##a) */ - -#define V850_SIM_SYS_make_raw(a...) V850_SIM_SYSCALL_1 (1024 , ##a) - - -#define V850_SIM_SYSCALL_0(_call) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call) \ - : "r11", "memory"); \ - rval; \ -}) -#define V850_SIM_SYSCALL_1(_call, _arg0) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register long arg0 __asm__ ("r7") = (long)_arg0; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call), "r" (arg0) \ - : "r11", "memory"); \ - rval; \ -}) -#define V850_SIM_SYSCALL_2(_call, _arg0, _arg1) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register long arg0 __asm__ ("r7") = (long)_arg0; \ - register long arg1 __asm__ ("r8") = (long)_arg1; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call), "r" (arg0), "r" (arg1) \ - : "r11", "memory"); \ - rval; \ -}) -#define V850_SIM_SYSCALL_3(_call, _arg0, _arg1, _arg2) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register long arg0 __asm__ ("r7") = (long)_arg0; \ - register long arg1 __asm__ ("r8") = (long)_arg1; \ - register long arg2 __asm__ ("r9") = (long)_arg2; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call), "r" (arg0), "r" (arg1), "r" (arg2)\ - : "r11", "memory"); \ - rval; \ -}) - -#define V850_SIM_SYSCALL(call, args...) \ - V850_SIM_SYS_##call (args) - -#endif /* __V850_SIMSYSCALL_H__ */ diff --git a/include/asm-v850/socket.h b/include/asm-v850/socket.h deleted file mode 100644 index e199a2bf12aa..000000000000 --- a/include/asm-v850/socket.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __V850_SOCKET_H__ -#define __V850_SOCKET_H__ - -#include - -/* For setsockoptions(2) */ -#define SOL_SOCKET 1 - -#define SO_DEBUG 1 -#define SO_REUSEADDR 2 -#define SO_TYPE 3 -#define SO_ERROR 4 -#define SO_DONTROUTE 5 -#define SO_BROADCAST 6 -#define SO_SNDBUF 7 -#define SO_RCVBUF 8 -#define SO_SNDBUFFORCE 32 -#define SO_RCVBUFFORCE 33 -#define SO_KEEPALIVE 9 -#define SO_OOBINLINE 10 -#define SO_NO_CHECK 11 -#define SO_PRIORITY 12 -#define SO_LINGER 13 -#define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ -#define SO_PASSCRED 16 -#define SO_PEERCRED 17 -#define SO_RCVLOWAT 18 -#define SO_SNDLOWAT 19 -#define SO_RCVTIMEO 20 -#define SO_SNDTIMEO 21 - -/* Security levels - as per NRL IPv6 - don't actually do anything */ -#define SO_SECURITY_AUTHENTICATION 22 -#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 -#define SO_SECURITY_ENCRYPTION_NETWORK 24 - -#define SO_BINDTODEVICE 25 - -/* Socket filtering */ -#define SO_ATTACH_FILTER 26 -#define SO_DETACH_FILTER 27 - -#define SO_PEERNAME 28 -#define SO_TIMESTAMP 29 -#define SCM_TIMESTAMP SO_TIMESTAMP - -#define SO_ACCEPTCONN 30 - -#define SO_PEERSEC 31 -#define SO_PASSSEC 34 -#define SO_TIMESTAMPNS 35 -#define SCM_TIMESTAMPNS SO_TIMESTAMPNS - -#define SO_MARK 36 - -#endif /* __V850_SOCKET_H__ */ diff --git a/include/asm-v850/sockios.h b/include/asm-v850/sockios.h deleted file mode 100644 index 823e106e6cd0..000000000000 --- a/include/asm-v850/sockios.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __V850_SOCKIOS_H__ -#define __V850_SOCKIOS_H__ - -/* Socket-level I/O control calls. */ -#define FIOSETOWN 0x8901 -#define SIOCSPGRP 0x8902 -#define FIOGETOWN 0x8903 -#define SIOCGPGRP 0x8904 -#define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ -#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ - -#endif /* __V850_SOCKIOS_H__ */ diff --git a/include/asm-v850/stat.h b/include/asm-v850/stat.h deleted file mode 100644 index c68c60d06e2f..000000000000 --- a/include/asm-v850/stat.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * include/asm-v850/stat.h -- v850 stat structure - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_STAT_H__ -#define __V850_STAT_H__ - -#include - -struct stat { - unsigned int st_dev; - unsigned long st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned int st_rdev; - long st_size; - unsigned long st_blksize; - unsigned long st_blocks; - unsigned long st_atime; - unsigned long __unused1; - unsigned long st_mtime; - unsigned long __unused2; - unsigned long st_ctime; - unsigned long __unused3; - unsigned long __unused4; - unsigned long __unused5; -}; - -struct stat64 { - unsigned long long st_dev; - unsigned long __unused1; - - unsigned long long st_ino; - - unsigned int st_mode; - unsigned int st_nlink; - - unsigned int st_uid; - unsigned int st_gid; - - unsigned long long st_rdev; - unsigned long __unused3; - - long long st_size; - unsigned long st_blksize; - - unsigned long st_blocks; /* No. of 512-byte blocks allocated */ - unsigned long __unused4; /* future possible st_blocks high bits */ - - unsigned long st_atime; - unsigned long st_atime_nsec; - - unsigned long st_mtime; - unsigned long st_mtime_nsec; - - unsigned long st_ctime; - unsigned long st_ctime_nsec; - - unsigned long __unused8; -}; - -#endif /* __V850_STAT_H__ */ diff --git a/include/asm-v850/statfs.h b/include/asm-v850/statfs.h deleted file mode 100644 index ea1596607f26..000000000000 --- a/include/asm-v850/statfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_STATFS_H__ -#define __V850_STATFS_H__ - -#include - -#endif /* __V850_STATFS_H__ */ diff --git a/include/asm-v850/string.h b/include/asm-v850/string.h deleted file mode 100644 index 478e234789d6..000000000000 --- a/include/asm-v850/string.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * include/asm-v850/string.h -- Architecture specific string routines - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_STRING_H__ -#define __V850_STRING_H__ - -#define __HAVE_ARCH_MEMCPY -#define __HAVE_ARCH_MEMSET -#define __HAVE_ARCH_MEMMOVE - -extern void *memcpy (void *, const void *, __kernel_size_t); -extern void *memset (void *, int, __kernel_size_t); -extern void *memmove (void *, const void *, __kernel_size_t); - -#endif /* __V850_STRING_H__ */ diff --git a/include/asm-v850/system.h b/include/asm-v850/system.h deleted file mode 100644 index 7daf1fdee119..000000000000 --- a/include/asm-v850/system.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * include/asm-v850/system.h -- Low-level interrupt/thread ops - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SYSTEM_H__ -#define __V850_SYSTEM_H__ - -#include -#include - - -/* - * switch_to(n) should switch tasks to task ptr, first checking that - * ptr isn't the current task, in which case it does nothing. - */ -struct thread_struct; -extern void *switch_thread (struct thread_struct *last, - struct thread_struct *next); -#define switch_to(prev,next,last) \ - do { \ - if (prev != next) { \ - (last) = switch_thread (&prev->thread, &next->thread); \ - } \ - } while (0) - - -/* Enable/disable interrupts. */ -#define local_irq_enable() __asm__ __volatile__ ("ei") -#define local_irq_disable() __asm__ __volatile__ ("di") - -#define local_save_flags(flags) \ - __asm__ __volatile__ ("stsr %1, %0" : "=r" (flags) : "i" (SR_PSW)) -#define local_restore_flags(flags) \ - __asm__ __volatile__ ("ldsr %0, %1" :: "r" (flags), "i" (SR_PSW)) - -/* For spinlocks etc */ -#define local_irq_save(flags) \ - do { local_save_flags (flags); local_irq_disable (); } while (0) -#define local_irq_restore(flags) \ - local_restore_flags (flags); - - -static inline int irqs_disabled (void) -{ - unsigned flags; - local_save_flags (flags); - return !!(flags & 0x20); -} - - -/* - * Force strict CPU ordering. - * Not really required on v850... - */ -#define nop() __asm__ __volatile__ ("nop") -#define mb() __asm__ __volatile__ ("" ::: "memory") -#define rmb() mb () -#define wmb() mb () -#define read_barrier_depends() ((void)0) -#define set_mb(var, value) do { xchg (&var, value); } while (0) - -#define smp_mb() mb () -#define smp_rmb() rmb () -#define smp_wmb() wmb () -#define smp_read_barrier_depends() read_barrier_depends() - -#define xchg(ptr, with) \ - ((__typeof__ (*(ptr)))__xchg ((unsigned long)(with), (ptr), sizeof (*(ptr)))) - -static inline unsigned long __xchg (unsigned long with, - __volatile__ void *ptr, int size) -{ - unsigned long tmp, flags; - - local_irq_save (flags); - - switch (size) { - case 1: - tmp = *(unsigned char *)ptr; - *(unsigned char *)ptr = with; - break; - case 2: - tmp = *(unsigned short *)ptr; - *(unsigned short *)ptr = with; - break; - case 4: - tmp = *(unsigned long *)ptr; - *(unsigned long *)ptr = with; - break; - } - - local_irq_restore (flags); - - return tmp; -} - -#include - -/* - * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make - * them available. - */ -#define cmpxchg_local(ptr, o, n) \ - ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ - (unsigned long)(n), sizeof(*(ptr)))) -#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) - -#ifndef CONFIG_SMP -#include -#endif - -#define arch_align_stack(x) (x) - -#endif /* __V850_SYSTEM_H__ */ diff --git a/include/asm-v850/teg.h b/include/asm-v850/teg.h deleted file mode 100644 index acc8c7d95329..000000000000 --- a/include/asm-v850/teg.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * include/asm-v850/teg.h -- NB85E-TEG cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_TEG_H__ -#define __V850_TEG_H__ - - -/* The TEG uses the V850E cpu core. */ -#include -#include - - -#define CPU_MODEL "v850e/nb85e-teg" -#define CPU_MODEL_LONG "NEC V850E/NB85E TEG" - - -/* For */ -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. On the NB85E/TEG, - There's 60KB of iRAM starting at 0xFFFF0000, however we need the base - address to be addressable by a 16-bit signed offset, so we only use the - second half of it starting from 0xFFFF8000. */ -#define R0_RAM_ADDR 0xFFFF8000 - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). - Some of these are parameterized even though there's only a single - interrupt, for compatibility with some generic code that works on other - processor models. */ -#define IRQ_INTCMD(n) 6 /* interval timer interrupt */ -#define IRQ_INTCMD_NUM 1 -#define IRQ_INTSER(n) 16 /* UART reception error */ -#define IRQ_INTSER_NUM 1 -#define IRQ_INTSR(n) 17 /* UART reception completion */ -#define IRQ_INTSR_NUM 1 -#define IRQ_INTST(n) 18 /* UART transmission completion */ -#define IRQ_INTST_NUM 1 - -/* For */ -#define NUM_CPU_IRQS 64 - - -/* TEG UART details. */ -#define V850E_UART_BASE_ADDR(n) (0xFFFFF600 + 0x10 * (n)) -#define V850E_UART_ASIM_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x0) -#define V850E_UART_ASIS_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x2) -#define V850E_UART_ASIF_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x4) -#define V850E_UART_CKSR_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x6) -#define V850E_UART_BRGC_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x8) -#define V850E_UART_TXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0xA) -#define V850E_UART_RXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0xC) -#define V850E_UART_NUM_CHANNELS 1 -#define V850E_UART_BASE_FREQ CPU_CLOCK_FREQ -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE teg_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void teg_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - - -/* The TEG RTPU. */ -#define V850E_RTPU_BASE_ADDR 0xFFFFF210 - - -/* TEG series timer D details. */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF210 -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x8) -#define V850E_TIMER_D_BASE_FREQ CPU_CLOCK_FREQ - - -/* `Interrupt Source Select' control register. */ -#define TEG_ISS_ADDR 0xFFFFF7FA -#define TEG_ISS (*(volatile u8 *)TEG_ISS_ADDR) - -/* Port 0 I/O register (bits 0-3 used). */ -#define TEG_PORT0_IO_ADDR 0xFFFFF7F2 -#define TEG_PORT0_IO (*(volatile u8 *)TEG_PORT0_IO_ADDR) -/* Port 0 control register (bits 0-3 control mode, 0 = output, 1 = input). */ -#define TEG_PORT0_PM_ADDR 0xFFFFF7F4 -#define TEG_PORT0_PM (*(volatile u8 *)TEG_PORT0_PM_ADDR) - - -#ifndef __ASSEMBLY__ -extern void teg_init_irqs (void); -#endif - - -#endif /* __V850_TEG_H__ */ diff --git a/include/asm-v850/termbits.h b/include/asm-v850/termbits.h deleted file mode 100644 index 295d7bf69451..000000000000 --- a/include/asm-v850/termbits.h +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef __V850_TERMBITS_H__ -#define __V850_TERMBITS_H__ - -#include - -typedef unsigned char cc_t; -typedef unsigned int speed_t; -typedef unsigned int tcflag_t; - -#define NCCS 19 -struct termios { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ -}; - -struct termios2 { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ - speed_t c_ispeed; /* input speed */ - speed_t c_ospeed; /* output speed */ -}; - -struct ktermios { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ - speed_t c_ispeed; /* input speed */ - speed_t c_ospeed; /* output speed */ -}; - -/* c_cc characters */ -#define VINTR 0 -#define VQUIT 1 -#define VERASE 2 -#define VKILL 3 -#define VEOF 4 -#define VTIME 5 -#define VMIN 6 -#define VSWTC 7 -#define VSTART 8 -#define VSTOP 9 -#define VSUSP 10 -#define VEOL 11 -#define VREPRINT 12 -#define VDISCARD 13 -#define VWERASE 14 -#define VLNEXT 15 -#define VEOL2 16 - - -/* c_iflag bits */ -#define IGNBRK 0000001 -#define BRKINT 0000002 -#define IGNPAR 0000004 -#define PARMRK 0000010 -#define INPCK 0000020 -#define ISTRIP 0000040 -#define INLCR 0000100 -#define IGNCR 0000200 -#define ICRNL 0000400 -#define IUCLC 0001000 -#define IXON 0002000 -#define IXANY 0004000 -#define IXOFF 0010000 -#define IMAXBEL 0020000 -#define IUTF8 0040000 - -/* c_oflag bits */ -#define OPOST 0000001 -#define OLCUC 0000002 -#define ONLCR 0000004 -#define OCRNL 0000010 -#define ONOCR 0000020 -#define ONLRET 0000040 -#define OFILL 0000100 -#define OFDEL 0000200 -#define NLDLY 0000400 -#define NL0 0000000 -#define NL1 0000400 -#define CRDLY 0003000 -#define CR0 0000000 -#define CR1 0001000 -#define CR2 0002000 -#define CR3 0003000 -#define TABDLY 0014000 -#define TAB0 0000000 -#define TAB1 0004000 -#define TAB2 0010000 -#define TAB3 0014000 -#define XTABS 0014000 -#define BSDLY 0020000 -#define BS0 0000000 -#define BS1 0020000 -#define VTDLY 0040000 -#define VT0 0000000 -#define VT1 0040000 -#define FFDLY 0100000 -#define FF0 0000000 -#define FF1 0100000 - -/* c_cflag bit meaning */ -#define CBAUD 0010017 -#define B0 0000000 /* hang up */ -#define B50 0000001 -#define B75 0000002 -#define B110 0000003 -#define B134 0000004 -#define B150 0000005 -#define B200 0000006 -#define B300 0000007 -#define B600 0000010 -#define B1200 0000011 -#define B1800 0000012 -#define B2400 0000013 -#define B4800 0000014 -#define B9600 0000015 -#define B19200 0000016 -#define B38400 0000017 -#define EXTA B19200 -#define EXTB B38400 -#define CSIZE 0000060 -#define CS5 0000000 -#define CS6 0000020 -#define CS7 0000040 -#define CS8 0000060 -#define CSTOPB 0000100 -#define CREAD 0000200 -#define PARENB 0000400 -#define PARODD 0001000 -#define HUPCL 0002000 -#define CLOCAL 0004000 -#define CBAUDEX 0010000 -#define BOTHER 0010000 -#define B57600 0010001 -#define B115200 0010002 -#define B230400 0010003 -#define B460800 0010004 -#define B500000 0010005 -#define B576000 0010006 -#define B921600 0010007 -#define B1000000 0010010 -#define B1152000 0010011 -#define B1500000 0010012 -#define B2000000 0010013 -#define B2500000 0010014 -#define B3000000 0010015 -#define B3500000 0010016 -#define B4000000 0010017 -#define CIBAUD 002003600000 /* input baud rate */ -#define CMSPAR 010000000000 /* mark or space (stick) parity */ -#define CRTSCTS 020000000000 /* flow control */ - -#define IBSHIFT 16 /* Shifr from CBAUD to CIBAUD */ - -/* c_lflag bits */ -#define ISIG 0000001 -#define ICANON 0000002 -#define XCASE 0000004 -#define ECHO 0000010 -#define ECHOE 0000020 -#define ECHOK 0000040 -#define ECHONL 0000100 -#define NOFLSH 0000200 -#define TOSTOP 0000400 -#define ECHOCTL 0001000 -#define ECHOPRT 0002000 -#define ECHOKE 0004000 -#define FLUSHO 0010000 -#define PENDIN 0040000 -#define IEXTEN 0100000 - - -/* tcflow() and TCXONC use these */ -#define TCOOFF 0 -#define TCOON 1 -#define TCIOFF 2 -#define TCION 3 - -/* tcflush() and TCFLSH use these */ -#define TCIFLUSH 0 -#define TCOFLUSH 1 -#define TCIOFLUSH 2 - -/* tcsetattr uses these */ -#define TCSANOW 0 -#define TCSADRAIN 1 -#define TCSAFLUSH 2 - -#endif /* __V850_TERMBITS_H__ */ diff --git a/include/asm-v850/termios.h b/include/asm-v850/termios.h deleted file mode 100644 index fcd171838d9c..000000000000 --- a/include/asm-v850/termios.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef __V850_TERMIOS_H__ -#define __V850_TERMIOS_H__ - -#include -#include - -struct winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; - unsigned short ws_ypixel; -}; - -#define NCC 8 -struct termio { - unsigned short c_iflag; /* input mode flags */ - unsigned short c_oflag; /* output mode flags */ - unsigned short c_cflag; /* control mode flags */ - unsigned short c_lflag; /* local mode flags */ - unsigned char c_line; /* line discipline */ - unsigned char c_cc[NCC]; /* control characters */ -}; - -/* modem lines */ -#define TIOCM_LE 0x001 -#define TIOCM_DTR 0x002 -#define TIOCM_RTS 0x004 -#define TIOCM_ST 0x008 -#define TIOCM_SR 0x010 -#define TIOCM_CTS 0x020 -#define TIOCM_CAR 0x040 -#define TIOCM_RNG 0x080 -#define TIOCM_DSR 0x100 -#define TIOCM_CD TIOCM_CAR -#define TIOCM_RI TIOCM_RNG -#define TIOCM_OUT1 0x2000 -#define TIOCM_OUT2 0x4000 -#define TIOCM_LOOP 0x8000 - -/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ - -#ifdef __KERNEL__ - -/* intr=^C quit=^\ erase=del kill=^U - eof=^D vtime=\0 vmin=\1 sxtc=\0 - start=^Q stop=^S susp=^Z eol=\0 - reprint=^R discard=^U werase=^W lnext=^V - eol2=\0 -*/ -#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" - -/* - * Translate a "termio" structure into a "termios". Ugh. - */ -#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ - unsigned short __tmp; \ - get_user(__tmp,&(termio)->x); \ - *(unsigned short *) &(termios)->x = __tmp; \ -} - -#define user_termio_to_kernel_termios(termios, termio) \ -({ \ - SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ - copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ -}) - -/* - * Translate a "termios" structure into a "termio". Ugh. - */ -#define kernel_termios_to_user_termio(termio, termios) \ -({ \ - put_user((termios)->c_iflag, &(termio)->c_iflag); \ - put_user((termios)->c_oflag, &(termio)->c_oflag); \ - put_user((termios)->c_cflag, &(termio)->c_cflag); \ - put_user((termios)->c_lflag, &(termio)->c_lflag); \ - put_user((termios)->c_line, &(termio)->c_line); \ - copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ -}) - -#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios2)) -#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios2)) -#define user_termios_to_kernel_termios_1(k, u) copy_from_user(k, u, sizeof(struct termios)) -#define kernel_termios_to_user_termios_1(u, k) copy_to_user(u, k, sizeof(struct termios)) - -#endif /* __KERNEL__ */ - -#endif /* __V850_TERMIOS_H__ */ diff --git a/include/asm-v850/thread_info.h b/include/asm-v850/thread_info.h deleted file mode 100644 index 1a9e6ae0c5fd..000000000000 --- a/include/asm-v850/thread_info.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * include/asm-v850/thread_info.h -- v850 low-level thread information - * - * Copyright (C) 2002 NEC Corporation - * Copyright (C) 2002 Miles Bader - * Copyright (C) 2002 David Howells (dhowells@redhat.com) - * - Incorporating suggestions made by Linus Torvalds and Dave Miller - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * This file was derived from the PPC version, include/asm-ppc/thread_info.h - * which was adapted from the i386 version by Paul Mackerras - */ - -#ifndef __V850_THREAD_INFO_H__ -#define __V850_THREAD_INFO_H__ - -#ifdef __KERNEL__ - -#ifndef __ASSEMBLY__ - -/* - * low level task data. - * If you change this, change the TI_* offsets below to match. - */ -struct thread_info { - struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ - unsigned long flags; /* low level flags */ - int cpu; /* cpu we're on */ - int preempt_count; /* 0 => preemptable, - <0 => BUG */ - struct restart_block restart_block; -}; - -#define INIT_THREAD_INFO(tsk) \ -{ \ - .task = &tsk, \ - .exec_domain = &default_exec_domain, \ - .flags = 0, \ - .cpu = 0, \ - .preempt_count = 1, \ - .restart_block = { \ - .fn = do_no_restart_syscall, \ - }, \ -} - -#define init_thread_info (init_thread_union.thread_info) -#define init_stack (init_thread_union.stack) - -/* - * macros/functions for gaining access to the thread information structure - */ - -/* thread information allocation */ -#define alloc_thread_info(tsk) ((struct thread_info *) \ - __get_free_pages(GFP_KERNEL, 1)) -#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) - -#endif /* __ASSEMBLY__ */ - - -/* - * Offsets in thread_info structure, used in assembly code - */ -#define TI_TASK 0 -#define TI_EXECDOMAIN 4 -#define TI_FLAGS 8 -#define TI_CPU 12 -#define TI_PREEMPT 16 - -#define PREEMPT_ACTIVE 0x4000000 - -/* - * thread information flag bit numbers - */ -#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ -#define TIF_SIGPENDING 1 /* signal pending */ -#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ -#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling - TIF_NEED_RESCHED */ -#define TIF_MEMDIE 4 - -/* as above, but as bit values */ -#define _TIF_SYSCALL_TRACE (1< - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_TLB_H__ -#define __V850_TLB_H__ - -#define tlb_flush(tlb) ((void)0) - -#include - -#endif /* __V850_TLB_H__ */ diff --git a/include/asm-v850/tlbflush.h b/include/asm-v850/tlbflush.h deleted file mode 100644 index c44aa64449c8..000000000000 --- a/include/asm-v850/tlbflush.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * include/asm-v850/tlbflush.h - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_TLBFLUSH_H__ -#define __V850_TLBFLUSH_H__ - -#include - - -/* - * flush all user-space atc entries. - */ -static inline void __flush_tlb(void) -{ - BUG (); -} - -static inline void __flush_tlb_one(unsigned long addr) -{ - BUG (); -} - -#define flush_tlb() __flush_tlb() - -/* - * flush all atc entries (both kernel and user-space entries). - */ -static inline void flush_tlb_all(void) -{ - BUG (); -} - -static inline void flush_tlb_mm(struct mm_struct *mm) -{ - BUG (); -} - -static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) -{ - BUG (); -} - -static inline void flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, unsigned long end) -{ - BUG (); -} - -static inline void flush_tlb_kernel_page(unsigned long addr) -{ - BUG (); -} - -#endif /* __V850_TLBFLUSH_H__ */ diff --git a/include/asm-v850/topology.h b/include/asm-v850/topology.h deleted file mode 100644 index 6040e41d7945..000000000000 --- a/include/asm-v850/topology.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_TOPOLOGY_H__ -#define __V850_TOPOLOGY_H__ - -#include - -#endif /* __V850_TOPOLOGY_H__ */ diff --git a/include/asm-v850/types.h b/include/asm-v850/types.h deleted file mode 100644 index 89f735ee41dd..000000000000 --- a/include/asm-v850/types.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __V850_TYPES_H__ -#define __V850_TYPES_H__ - -#ifndef __ASSEMBLY__ - -/* - * This file is never included by application software unless - * explicitly requested (e.g., via linux/types.h) in which case the - * application is Linux specific so (user-) name space pollution is - * not a major issue. However, for interoperability, libraries still - * need to be careful to avoid a name clashes. - */ -#include - -typedef unsigned short umode_t; - -#endif /* !__ASSEMBLY__ */ - -/* - * These aren't exported outside the kernel to avoid name space clashes - */ -#ifdef __KERNEL__ - -#define BITS_PER_LONG 32 - -#ifndef __ASSEMBLY__ - -/* Dma addresses are 32-bits wide. */ - -typedef u32 dma_addr_t; - -#endif /* !__ASSEMBLY__ */ - -#endif /* __KERNEL__ */ - -#endif /* __V850_TYPES_H__ */ diff --git a/include/asm-v850/uaccess.h b/include/asm-v850/uaccess.h deleted file mode 100644 index 64563c409bb2..000000000000 --- a/include/asm-v850/uaccess.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef __V850_UACCESS_H__ -#define __V850_UACCESS_H__ - -/* - * User space memory access functions - */ - -#include -#include - -#include -#include - -#define VERIFY_READ 0 -#define VERIFY_WRITE 1 - -static inline int access_ok (int type, const void *addr, unsigned long size) -{ - /* XXX I guess we should check against real ram bounds at least, and - possibly make sure ADDR is not within the kernel. - For now we just check to make sure it's not a small positive - or negative value, as that will at least catch some kinds of - error. In particular, we make sure that ADDR's not within the - interrupt vector area, which we know starts at zero, or within the - peripheral-I/O area, which is located just _before_ zero. */ - unsigned long val = (unsigned long)addr; - return val >= (0x80 + NUM_CPU_IRQS*16) && val < 0xFFFFF000; -} - -/* - * The exception table consists of pairs of addresses: the first is the - * address of an instruction that is allowed to fault, and the second is - * the address at which the program should continue. No registers are - * modified, so it is entirely up to the continuation code to figure out - * what to do. - * - * All the routines below use bits of fixup code that are out of line - * with the main instruction path. This means when everything is well, - * we don't even have to jump over them. Further, they do not intrude - * on our cache or tlb entries. - */ - -struct exception_table_entry -{ - unsigned long insn, fixup; -}; - -/* Returns 0 if exception not found and fixup otherwise. */ -extern unsigned long search_exception_table (unsigned long); - - -/* - * These are the main single-value transfer routines. They automatically - * use the right size if we just have the right pointer type. - */ - -extern int bad_user_access_length (void); - -#define __get_user(var, ptr) \ - ({ \ - int __gu_err = 0; \ - typeof(*(ptr)) __gu_val = 0; \ - switch (sizeof (*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - __gu_val = *(ptr); \ - break; \ - case 8: \ - memcpy(&__gu_val, ptr, sizeof(__gu_val)); \ - break; \ - default: \ - __gu_val = 0; \ - __gu_err = __get_user_bad (); \ - break; \ - } \ - (var) = __gu_val; \ - __gu_err; \ - }) -#define __get_user_bad() (bad_user_access_length (), (-EFAULT)) - -#define __put_user(var, ptr) \ - ({ \ - int __pu_err = 0; \ - switch (sizeof (*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - *(ptr) = (var); \ - break; \ - case 8: { \ - typeof(*(ptr)) __pu_val = 0; \ - memcpy(ptr, &__pu_val, sizeof(__pu_val)); \ - } \ - break; \ - default: \ - __pu_err = __put_user_bad (); \ - break; \ - } \ - __pu_err; \ - }) -#define __put_user_bad() (bad_user_access_length (), (-EFAULT)) - -#define put_user(x, ptr) __put_user(x, ptr) -#define get_user(x, ptr) __get_user(x, ptr) - -#define __copy_from_user(to, from, n) (memcpy (to, from, n), 0) -#define __copy_to_user(to, from, n) (memcpy(to, from, n), 0) - -#define __copy_to_user_inatomic __copy_to_user -#define __copy_from_user_inatomic __copy_from_user - -#define copy_from_user(to, from, n) __copy_from_user (to, from, n) -#define copy_to_user(to, from, n) __copy_to_user(to, from, n) - -#define copy_to_user_ret(to,from,n,retval) \ - ({ if (copy_to_user (to,from,n)) return retval; }) - -#define copy_from_user_ret(to,from,n,retval) \ - ({ if (copy_from_user (to,from,n)) return retval; }) - -/* - * Copy a null terminated string from userspace. - */ - -static inline long -strncpy_from_user (char *dst, const char *src, long count) -{ - char *tmp; - strncpy (dst, src, count); - for (tmp = dst; *tmp && count > 0; tmp++, count--) - ; - return tmp - dst; -} - -/* - * Return the size of a string (including the ending 0) - * - * Return 0 on exception, a value greater than N if too long - */ -static inline long strnlen_user (const char *src, long n) -{ - return strlen (src) + 1; -} - -#define strlen_user(str) strnlen_user (str, 32767) - -/* - * Zero Userspace - */ - -static inline unsigned long -clear_user (void *to, unsigned long n) -{ - memset (to, 0, n); - return 0; -} - -#endif /* __V850_UACCESS_H__ */ diff --git a/include/asm-v850/ucontext.h b/include/asm-v850/ucontext.h deleted file mode 100644 index 303c21590cff..000000000000 --- a/include/asm-v850/ucontext.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __V850_UCONTEXT_H__ -#define __V850_UCONTEXT_H__ - -#include - -struct ucontext { - unsigned long uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - sigset_t uc_sigmask; /* mask last for extensibility */ -}; - -#endif /* __V850_UCONTEXT_H__ */ diff --git a/include/asm-v850/unaligned.h b/include/asm-v850/unaligned.h deleted file mode 100644 index 53122b28491e..000000000000 --- a/include/asm-v850/unaligned.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Note that some v850 chips support unaligned access, but it seems too - * annoying to use. - */ -#ifndef _ASM_V850_UNALIGNED_H -#define _ASM_V850_UNALIGNED_H - -#include -#include -#include - -#define get_unaligned __get_unaligned_le -#define put_unaligned __put_unaligned_le - -#endif /* _ASM_V850_UNALIGNED_H */ diff --git a/include/asm-v850/unistd.h b/include/asm-v850/unistd.h deleted file mode 100644 index 2241ed45ecfe..000000000000 --- a/include/asm-v850/unistd.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * include/asm-v850/unistd.h -- System call numbers and invocation mechanism - * - * Copyright (C) 2001,02,03,04 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_UNISTD_H__ -#define __V850_UNISTD_H__ - -#define __NR_restart_syscall 0 -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_waitpid 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_time 13 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_chown 16 -#define __NR_break 17 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_umount 22 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_stty 31 -#define __NR_gtty 32 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_ftime 35 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_prof 44 -#define __NR_brk 45 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_signal 48 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_acct 51 -#define __NR_umount2 52 -#define __NR_lock 53 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_setpgid 57 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sgetmask 68 -#define __NR_ssetmask 69 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_ugetrlimit 76 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_select 82 -#define __NR_symlink 83 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_fchown 95 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_profil 98 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_socketcall 102 -#define __NR_syslog 103 -#define __NR_setitimer 104 -#define __NR_getitimer 105 -#define __NR_stat 106 -#define __NR_lstat 107 -#define __NR_fstat 108 -#define __NR_vhangup 111 -#define __NR_wait4 114 -#define __NR_swapoff 115 -#define __NR_sysinfo 116 -#define __NR_ipc 117 -#define __NR_fsync 118 -#define __NR_sigreturn 119 -#define __NR_clone 120 -#define __NR_setdomainname 121 -#define __NR_uname 122 -#define __NR_cacheflush 123 -#define __NR_adjtimex 124 -#define __NR_mprotect 125 -#define __NR_sigprocmask 126 -#define __NR_create_module 127 -#define __NR_init_module 128 -#define __NR_delete_module 129 -#define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 -#define __NR_getpgid 132 -#define __NR_fchdir 133 -#define __NR_bdflush 134 -#define __NR_sysfs 135 -#define __NR_personality 136 -#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#define __NR__llseek 140 -#define __NR_getdents 141 -#define __NR_flock 143 -#define __NR_msync 144 -#define __NR_readv 145 -#define __NR_writev 146 -#define __NR_getsid 147 -#define __NR_fdatasync 148 -#define __NR__sysctl 149 -#define __NR_mlock 150 -#define __NR_munlock 151 -#define __NR_mlockall 152 -#define __NR_munlockall 153 -#define __NR_sched_setparam 154 -#define __NR_sched_getparam 155 -#define __NR_sched_setscheduler 156 -#define __NR_sched_getscheduler 157 -#define __NR_sched_yield 158 -#define __NR_sched_get_priority_max 159 -#define __NR_sched_get_priority_min 160 -#define __NR_sched_rr_get_interval 161 -#define __NR_nanosleep 162 -#define __NR_mremap 163 -#define __NR_setresuid 164 -#define __NR_getresuid 165 -#define __NR_query_module 167 -#define __NR_poll 168 -#define __NR_nfsservctl 169 -#define __NR_setresgid 170 -#define __NR_getresgid 171 -#define __NR_prctl 172 -#define __NR_rt_sigreturn 173 -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigtimedwait 177 -#define __NR_rt_sigqueueinfo 178 -#define __NR_rt_sigsuspend 179 -#define __NR_pread 180 -#define __NR_pwrite 181 -#define __NR_lchown 182 -#define __NR_getcwd 183 -#define __NR_capget 184 -#define __NR_capset 185 -#define __NR_sigaltstack 186 -#define __NR_sendfile 187 -#define __NR_getpmsg 188 /* some people actually want streams */ -#define __NR_putpmsg 189 /* some people actually want streams */ -#define __NR_vfork 190 -#define __NR_mmap2 192 -#define __NR_truncate64 193 -#define __NR_ftruncate64 194 -#define __NR_stat64 195 -#define __NR_lstat64 196 -#define __NR_fstat64 197 -#define __NR_fcntl64 198 -#define __NR_getdents64 199 -#define __NR_pivot_root 200 -#define __NR_gettid 201 -#define __NR_tkill 202 - -#ifdef __KERNEL__ - -#define __ARCH_WANT_IPC_PARSE_VERSION -#define __ARCH_WANT_OLD_READDIR -#define __ARCH_WANT_STAT64 -#define __ARCH_WANT_SYS_ALARM -#define __ARCH_WANT_SYS_GETHOSTNAME -#define __ARCH_WANT_SYS_PAUSE -#define __ARCH_WANT_SYS_SGETMASK -#define __ARCH_WANT_SYS_SIGNAL -#define __ARCH_WANT_SYS_TIME -#define __ARCH_WANT_SYS_UTIME -#define __ARCH_WANT_SYS_WAITPID -#define __ARCH_WANT_SYS_SOCKETCALL -#define __ARCH_WANT_SYS_FADVISE64 -#define __ARCH_WANT_SYS_GETPGRP -#define __ARCH_WANT_SYS_LLSEEK -#define __ARCH_WANT_SYS_NICE -#define __ARCH_WANT_SYS_OLDUMOUNT -#define __ARCH_WANT_SYS_SIGPENDING -#define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION - -/* - * "Conditional" syscalls - */ -#define cond_syscall(name) \ - asm (".weak\t" C_SYMBOL_STRING(name) ";" \ - ".set\t" C_SYMBOL_STRING(name) "," C_SYMBOL_STRING(sys_ni_syscall)) -#if 0 -/* This doesn't work if there's a function prototype for NAME visible, - because the argument types probably won't match. */ -#define cond_syscall(name) \ - void name (void) __attribute__ ((weak, alias ("sys_ni_syscall"))); -#endif - -#endif /* __KERNEL__ */ -#endif /* __V850_UNISTD_H__ */ diff --git a/include/asm-v850/user.h b/include/asm-v850/user.h deleted file mode 100644 index 63cdc567d272..000000000000 --- a/include/asm-v850/user.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef __V850_USER_H__ -#define __V850_USER_H__ - -/* Adapted from . */ - -#include -#include - -/* - * Core file format: The core file is written in such a way that gdb - * can understand it and provide useful information to the user (under - * linux we use the `trad-core' bfd, NOT the osf-core). The file contents - * are as follows: - * - * upage: 1 page consisting of a user struct that tells gdb - * what is present in the file. Directly after this is a - * copy of the task_struct, which is currently not used by gdb, - * but it may come in handy at some point. All of the registers - * are stored as part of the upage. The upage should always be - * only one page long. - * data: The data segment follows next. We use current->end_text to - * current->brk to pick up all of the user variables, plus any memory - * that may have been sbrk'ed. No attempt is made to determine if a - * page is demand-zero or if a page is totally unused, we just cover - * the entire range. All of the addresses are rounded in such a way - * that an integral number of pages is written. - * stack: We need the stack information in order to get a meaningful - * backtrace. We need to write the data from usp to - * current->start_stack, so we round each of these in order to be able - * to write an integer number of pages. - */ -struct user { - struct pt_regs regs; /* entire machine state */ - size_t u_tsize; /* text size (pages) */ - size_t u_dsize; /* data size (pages) */ - size_t u_ssize; /* stack size (pages) */ - unsigned long start_code; /* text starting address */ - unsigned long start_data; /* data starting address */ - unsigned long start_stack; /* stack starting address */ - long int signal; /* signal causing core dump */ - unsigned long u_ar0; /* help gdb find registers */ - unsigned long magic; /* identifies a core file */ - char u_comm[32]; /* user command name */ -}; - -#define NBPG PAGE_SIZE -#define UPAGES 1 -#define HOST_TEXT_START_ADDR (u.start_code) -#define HOST_DATA_START_ADDR (u.start_data) -#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) - -#endif /* __V850_USER_H__ */ diff --git a/include/asm-v850/v850e.h b/include/asm-v850/v850e.h deleted file mode 100644 index 5a222eb5117f..000000000000 --- a/include/asm-v850/v850e.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * include/asm-v850/v850e.h -- V850E CPU - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_H__ -#define __V850_V850E_H__ - -#include - -#define CPU_ARCH "v850e" - -#endif /* __V850_V850E_H__ */ diff --git a/include/asm-v850/v850e2.h b/include/asm-v850/v850e2.h deleted file mode 100644 index 48680408ab7e..000000000000 --- a/include/asm-v850/v850e2.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * include/asm-v850/v850e2.h -- Machine-dependent defs for V850E2 CPUs - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E2_H__ -#define __V850_V850E2_H__ - -#include /* v850e-style interrupt system. */ - - -#define CPU_ARCH "v850e2" - - -/* Control registers. */ - -/* Chip area select control */ -#define V850E2_CSC_ADDR(n) (0xFFFFF060 + (n) * 2) -#define V850E2_CSC(n) (*(volatile u16 *)V850E2_CSC_ADDR(n)) -/* I/O area select control */ -#define V850E2_BPC_ADDR 0xFFFFF064 -#define V850E2_BPC (*(volatile u16 *)V850E2_BPC_ADDR) -/* Bus size configuration */ -#define V850E2_BSC_ADDR 0xFFFFF066 -#define V850E2_BSC (*(volatile u16 *)V850E2_BSC_ADDR) -/* Endian configuration */ -#define V850E2_BEC_ADDR 0xFFFFF068 -#define V850E2_BEC (*(volatile u16 *)V850E2_BEC_ADDR) -/* Cache configuration */ -#define V850E2_BHC_ADDR 0xFFFFF06A -#define V850E2_BHC (*(volatile u16 *)V850E2_BHC_ADDR) -/* NPB strobe-wait configuration */ -#define V850E2_VSWC_ADDR 0xFFFFF06E -#define V850E2_VSWC (*(volatile u16 *)V850E2_VSWC_ADDR) -/* Bus cycle type */ -#define V850E2_BCT_ADDR(n) (0xFFFFF480 + (n) * 2) -#define V850E2_BCT(n) (*(volatile u16 *)V850E2_BCT_ADDR(n)) -/* Data wait control */ -#define V850E2_DWC_ADDR(n) (0xFFFFF484 + (n) * 2) -#define V850E2_DWC(n) (*(volatile u16 *)V850E2_DWC_ADDR(n)) -/* Bus cycle control */ -#define V850E2_BCC_ADDR 0xFFFFF488 -#define V850E2_BCC (*(volatile u16 *)V850E2_BCC_ADDR) -/* Address wait control */ -#define V850E2_ASC_ADDR 0xFFFFF48A -#define V850E2_ASC (*(volatile u16 *)V850E2_ASC_ADDR) -/* Local bus sizing control */ -#define V850E2_LBS_ADDR 0xFFFFF48E -#define V850E2_LBS (*(volatile u16 *)V850E2_LBS_ADDR) -/* Line buffer control */ -#define V850E2_LBC_ADDR(n) (0xFFFFF490 + (n) * 2) -#define V850E2_LBC(n) (*(volatile u16 *)V850E2_LBC_ADDR(n)) -/* SDRAM configuration */ -#define V850E2_SCR_ADDR(n) (0xFFFFF4A0 + (n) * 4) -#define V850E2_SCR(n) (*(volatile u16 *)V850E2_SCR_ADDR(n)) -/* SDRAM refresh cycle control */ -#define V850E2_RFS_ADDR(n) (0xFFFFF4A2 + (n) * 4) -#define V850E2_RFS(n) (*(volatile u16 *)V850E2_RFS_ADDR(n)) - - -#endif /* __V850_V850E2_H__ */ diff --git a/include/asm-v850/v850e2_cache.h b/include/asm-v850/v850e2_cache.h deleted file mode 100644 index 87edf0d311d5..000000000000 --- a/include/asm-v850/v850e2_cache.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * include/asm-v850/v850e2_cache_cache.h -- Cache control for V850E2 - * cache memories - * - * Copyright (C) 2003,05 NEC Electronics Corporation - * Copyright (C) 2003,05 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E2_CACHE_H__ -#define __V850_V850E2_CACHE_H__ - -#include - - -/* Cache control registers. */ - -/* Bus Transaction Control */ -#define V850E2_CACHE_BTSC_ADDR 0xFFFFF070 -#define V850E2_CACHE_BTSC (*(volatile u16 *)V850E2_CACHE_BTSC_ADDR) -#define V850E2_CACHE_BTSC_ICM 0x0001 /* icache enable */ -#define V850E2_CACHE_BTSC_DCM0 0x0004 /* dcache enable, bit 0 */ -#define V850E2_CACHE_BTSC_DCM1 0x0008 /* dcache enable, bit 1 */ -#define V850E2_CACHE_BTSC_DCM_WT /* write-through */ \ - V850E2_CACHE_BTSC_DCM0 -#ifdef CONFIG_V850E2_V850E2S -# define V850E2_CACHE_BTSC_DCM_WB_NO_ALLOC /* write-back, non-alloc */ \ - V850E2_CACHE_BTSC_DCM1 -# define V850E2_CACHE_BTSC_DCM_WB_ALLOC /* write-back, non-alloc */ \ - (V850E2_CACHE_BTSC_DCM1 | V850E2_CACHE_BTSC_DCM0) -# define V850E2_CACHE_BTSC_ISEQ 0x0010 /* icache `address sequence mode' */ -# define V850E2_CACHE_BTSC_DSEQ 0x0020 /* dcache `address sequence mode' */ -# define V850E2_CACHE_BTSC_IRFC 0x0030 -# define V850E2_CACHE_BTSC_ILCD 0x4000 -# define V850E2_CACHE_BTSC_VABE 0x8000 -#endif /* CONFIG_V850E2_V850E2S */ - -/* Cache operation start address register (low-bits). */ -#define V850E2_CACHE_CADL_ADDR 0xFFFFF074 -#define V850E2_CACHE_CADL (*(volatile u16 *)V850E2_CACHE_CADL_ADDR) -/* Cache operation start address register (high-bits). */ -#define V850E2_CACHE_CADH_ADDR 0xFFFFF076 -#define V850E2_CACHE_CADH (*(volatile u16 *)V850E2_CACHE_CADH_ADDR) -/* Cache operation count register. */ -#define V850E2_CACHE_CCNT_ADDR 0xFFFFF078 -#define V850E2_CACHE_CCNT (*(volatile u16 *)V850E2_CACHE_CCNT_ADDR) -/* Cache operation specification register. */ -#define V850E2_CACHE_COPR_ADDR 0xFFFFF07A -#define V850E2_CACHE_COPR (*(volatile u16 *)V850E2_CACHE_COPR_ADDR) -#define V850E2_CACHE_COPR_STRT 0x0001 /* start cache operation */ -#define V850E2_CACHE_COPR_LBSL 0x0100 /* 0 = icache, 1 = dcache */ -#define V850E2_CACHE_COPR_WSLE 0x0200 /* operate on cache way */ -#define V850E2_CACHE_COPR_WSL(way) ((way) * 0x0400) /* way select */ -#define V850E2_CACHE_COPR_CFC(op) ((op) * 0x1000) /* cache function code */ - - -/* Size of a cache line in bytes. */ -#define V850E2_CACHE_LINE_SIZE_BITS 4 -#define V850E2_CACHE_LINE_SIZE (1 << V850E2_CACHE_LINE_SIZE_BITS) - -/* The size of each cache `way' in lines. */ -#define V850E2_CACHE_WAY_SIZE 256 - - -/* For */ -#define L1_CACHE_BYTES V850E2_CACHE_LINE_SIZE -#define L1_CACHE_SHIFT V850E2_CACHE_LINE_SIZE_BITS - - -#endif /* __V850_V850E2_CACHE_H__ */ diff --git a/include/asm-v850/v850e_cache.h b/include/asm-v850/v850e_cache.h deleted file mode 100644 index aa7d7eb9da50..000000000000 --- a/include/asm-v850/v850e_cache.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * include/asm-v850/v850e_cache.h -- Cache control for V850E cache memories - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -/* This file implements cache control for the rather simple cache used on - some V850E CPUs, specifically the NB85E/TEG CPU-core and the V850E/ME2 - CPU. V850E2 processors have their own (better) cache - implementation. */ - -#ifndef __V850_V850E_CACHE_H__ -#define __V850_V850E_CACHE_H__ - -#include - - -/* Cache control registers. */ -#define V850E_CACHE_BHC_ADDR 0xFFFFF06A -#define V850E_CACHE_BHC (*(volatile u16 *)V850E_CACHE_BHC_ADDR) -#define V850E_CACHE_ICC_ADDR 0xFFFFF070 -#define V850E_CACHE_ICC (*(volatile u16 *)V850E_CACHE_ICC_ADDR) -#define V850E_CACHE_ISI_ADDR 0xFFFFF072 -#define V850E_CACHE_ISI (*(volatile u16 *)V850E_CACHE_ISI_ADDR) -#define V850E_CACHE_DCC_ADDR 0xFFFFF078 -#define V850E_CACHE_DCC (*(volatile u16 *)V850E_CACHE_DCC_ADDR) - -/* Size of a cache line in bytes. */ -#define V850E_CACHE_LINE_SIZE 16 - -/* For */ -#define L1_CACHE_BYTES V850E_CACHE_LINE_SIZE - - -#if defined(__KERNEL__) && !defined(__ASSEMBLY__) -/* Set caching params via the BHC, ICC, and DCC registers. */ -void v850e_cache_enable (u16 bhc, u16 icc, u16 dcc); -#endif /* __KERNEL__ && !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_CACHE_H__ */ diff --git a/include/asm-v850/v850e_intc.h b/include/asm-v850/v850e_intc.h deleted file mode 100644 index 6fdf95708317..000000000000 --- a/include/asm-v850/v850e_intc.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * include/asm-v850/v850e_intc.h -- V850E CPU interrupt controller (INTC) - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_INTC_H__ -#define __V850_V850E_INTC_H__ - - -/* There are 4 16-bit `Interrupt Mask Registers' located contiguously - starting from this base. Each interrupt uses a single bit to - indicated enabled/disabled status. */ -#define V850E_INTC_IMR_BASE_ADDR 0xFFFFF100 -#define V850E_INTC_IMR_ADDR(irq) (V850E_INTC_IMR_BASE_ADDR + ((irq) >> 3)) -#define V850E_INTC_IMR_BIT(irq) ((irq) & 0x7) - -/* Each maskable interrupt has a single-byte control register at this - address. */ -#define V850E_INTC_IC_BASE_ADDR 0xFFFFF110 -#define V850E_INTC_IC_ADDR(irq) (V850E_INTC_IC_BASE_ADDR + ((irq) << 1)) -#define V850E_INTC_IC(irq) (*(volatile u8 *)V850E_INTC_IC_ADDR(irq)) -/* Encode priority PR for storing in an interrupt control register. */ -#define V850E_INTC_IC_PR(pr) (pr) -/* Interrupt disable bit in an interrupt control register. */ -#define V850E_INTC_IC_MK_BIT 6 -#define V850E_INTC_IC_MK (1 << V850E_INTC_IC_MK_BIT) -/* Interrupt pending flag in an interrupt control register. */ -#define V850E_INTC_IC_IF_BIT 7 -#define V850E_INTC_IC_IF (1 << V850E_INTC_IC_IF_BIT) - -/* The ISPR (In-service priority register) contains one bit for each interrupt - priority level, which is set to one when that level is currently being - serviced (and thus blocking any interrupts of equal or lesser level). */ -#define V850E_INTC_ISPR_ADDR 0xFFFFF1FA -#define V850E_INTC_ISPR (*(volatile u8 *)V850E_INTC_ISPR_ADDR) - - -#ifndef __ASSEMBLY__ - -/* Enable interrupt handling for interrupt IRQ. */ -static inline void v850e_intc_enable_irq (unsigned irq) -{ - __asm__ __volatile__ ("clr1 %0, [%1]" - :: "r" (V850E_INTC_IMR_BIT (irq)), - "r" (V850E_INTC_IMR_ADDR (irq)) - : "memory"); -} - -/* Disable interrupt handling for interrupt IRQ. Note that any - interrupts received while disabled will be delivered once the - interrupt is enabled again, unless they are explicitly cleared using - `v850e_intc_clear_pending_irq'. */ -static inline void v850e_intc_disable_irq (unsigned irq) -{ - __asm__ __volatile__ ("set1 %0, [%1]" - :: "r" (V850E_INTC_IMR_BIT (irq)), - "r" (V850E_INTC_IMR_ADDR (irq)) - : "memory"); -} - -/* Return true if interrupt handling for interrupt IRQ is enabled. */ -static inline int v850e_intc_irq_enabled (unsigned irq) -{ - int rval; - __asm__ __volatile__ ("tst1 %1, [%2]; setf z, %0" - : "=r" (rval) - : "r" (V850E_INTC_IMR_BIT (irq)), - "r" (V850E_INTC_IMR_ADDR (irq))); - return rval; -} - -/* Disable irqs from 0 until LIMIT. LIMIT must be a multiple of 8. */ -static inline void _v850e_intc_disable_irqs (unsigned limit) -{ - unsigned long addr; - for (addr = V850E_INTC_IMR_BASE_ADDR; limit >= 8; addr++, limit -= 8) - *(char *)addr = 0xFF; -} - -/* Disable all irqs. This is purposely a macro, because NUM_MACH_IRQS - will be only be defined later. */ -#define v850e_intc_disable_irqs() _v850e_intc_disable_irqs (NUM_MACH_IRQS) - -/* Clear any pending interrupts for IRQ. */ -static inline void v850e_intc_clear_pending_irq (unsigned irq) -{ - __asm__ __volatile__ ("clr1 %0, 0[%1]" - :: "i" (V850E_INTC_IC_IF_BIT), - "r" (V850E_INTC_IC_ADDR (irq)) - : "memory"); -} - -/* Return true if interrupt IRQ is pending (but disabled). */ -static inline int v850e_intc_irq_pending (unsigned irq) -{ - int rval; - __asm__ __volatile__ ("tst1 %1, 0[%2]; setf nz, %0" - : "=r" (rval) - : "i" (V850E_INTC_IC_IF_BIT), - "r" (V850E_INTC_IC_ADDR (irq))); - return rval; -} - - -struct v850e_intc_irq_init { - const char *name; /* name of interrupt type */ - - /* Range of kernel irq numbers for this type: - BASE, BASE+INTERVAL, ..., BASE+INTERVAL*NUM */ - unsigned base, num, interval; - - unsigned priority; /* interrupt priority to assign */ -}; -struct hw_interrupt_type; /* fwd decl */ - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -extern void v850e_intc_init_irq_types (struct v850e_intc_irq_init *inits, - struct hw_interrupt_type *hw_irq_types); - - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_INTC_H__ */ diff --git a/include/asm-v850/v850e_timer_c.h b/include/asm-v850/v850e_timer_c.h deleted file mode 100644 index f70575df6ea9..000000000000 --- a/include/asm-v850/v850e_timer_c.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * include/asm-v850/v850e_timer_c.h -- `Timer C' component often used - * with the V850E cpu core - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -/* NOTE: this include file currently contains only enough to allow us to - use timer C as an interrupt pass-through. */ - -#ifndef __V850_V850E_TIMER_C_H__ -#define __V850_V850E_TIMER_C_H__ - -#include -#include /* Pick up chip-specific defs. */ - - -/* Timer C (16-bit interval timers). */ - -/* Control register 0 for timer C. */ -#define V850E_TIMER_C_TMCC0_ADDR(n) (V850E_TIMER_C_BASE_ADDR + 0x6 + 0x10 *(n)) -#define V850E_TIMER_C_TMCC0(n) (*(volatile u8 *)V850E_TIMER_C_TMCC0_ADDR(n)) -#define V850E_TIMER_C_TMCC0_CAE 0x01 /* clock action enable */ -#define V850E_TIMER_C_TMCC0_CE 0x02 /* count enable */ -/* ... */ - -/* Control register 1 for timer C. */ -#define V850E_TIMER_C_TMCC1_ADDR(n) (V850E_TIMER_C_BASE_ADDR + 0x8 + 0x10 *(n)) -#define V850E_TIMER_C_TMCC1(n) (*(volatile u8 *)V850E_TIMER_C_TMCC1_ADDR(n)) -#define V850E_TIMER_C_TMCC1_CMS0 0x01 /* capture/compare mode select (ccc0) */ -#define V850E_TIMER_C_TMCC1_CMS1 0x02 /* capture/compare mode select (ccc1) */ -/* ... */ - -/* Interrupt edge-sensitivity control for timer C. */ -#define V850E_TIMER_C_SESC_ADDR(n) (V850E_TIMER_C_BASE_ADDR + 0x9 + 0x10 *(n)) -#define V850E_TIMER_C_SESC(n) (*(volatile u8 *)V850E_TIMER_C_SESC_ADDR(n)) - -/* ...etc... */ - - -#endif /* __V850_V850E_TIMER_C_H__ */ diff --git a/include/asm-v850/v850e_timer_d.h b/include/asm-v850/v850e_timer_d.h deleted file mode 100644 index 417612c5b22f..000000000000 --- a/include/asm-v850/v850e_timer_d.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * include/asm-v850/v850e_timer_d.h -- `Timer D' component often used - * with the V850E cpu core - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_TIMER_D_H__ -#define __V850_V850E_TIMER_D_H__ - -#include -#include /* Pick up chip-specific defs. */ - - -/* Timer D (16-bit interval timers). */ - -/* Count registers for timer D. */ -#define V850E_TIMER_D_TMD_ADDR(n) (V850E_TIMER_D_TMD_BASE_ADDR + 0x10 * (n)) -#define V850E_TIMER_D_TMD(n) (*(volatile u16 *)V850E_TIMER_D_TMD_ADDR(n)) - -/* Count compare registers for timer D. */ -#define V850E_TIMER_D_CMD_ADDR(n) (V850E_TIMER_D_CMD_BASE_ADDR + 0x10 * (n)) -#define V850E_TIMER_D_CMD(n) (*(volatile u16 *)V850E_TIMER_D_CMD_ADDR(n)) - -/* Control registers for timer D. */ -#define V850E_TIMER_D_TMCD_ADDR(n) (V850E_TIMER_D_TMCD_BASE_ADDR + 0x10 * (n)) -#define V850E_TIMER_D_TMCD(n) (*(volatile u8 *)V850E_TIMER_D_TMCD_ADDR(n)) -/* Control bits for timer D. */ -#define V850E_TIMER_D_TMCD_CE 0x2 /* count enable */ -#define V850E_TIMER_D_TMCD_CAE 0x1 /* clock action enable */ -/* Clock divider setting (log2). */ -#define V850E_TIMER_D_TMCD_CS(divlog2) (((divlog2) - V850E_TIMER_D_TMCD_CS_MIN) << 4) -/* Minimum clock divider setting (log2). */ -#ifndef V850E_TIMER_D_TMCD_CS_MIN /* Can be overridden by mach-specific hdrs */ -#define V850E_TIMER_D_TMCD_CS_MIN 2 /* Default is correct for the v850e/ma1 */ -#endif -/* Maximum clock divider setting (log2). */ -#define V850E_TIMER_D_TMCD_CS_MAX (V850E_TIMER_D_TMCD_CS_MIN + 7) - -/* Return the clock-divider (log2) of timer D unit N. */ -#define V850E_TIMER_D_DIVLOG2(n) \ - (((V850E_TIMER_D_TMCD(n) >> 4) & 0x7) + V850E_TIMER_D_TMCD_CS_MIN) - - -#ifndef __ASSEMBLY__ - -/* Start interval timer TIMER (0-3). The timer will issue the - corresponding INTCMD interrupt RATE times per second. This function - does not enable the interrupt. */ -extern void v850e_timer_d_configure (unsigned timer, unsigned rate); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_TIMER_D_H__ */ diff --git a/include/asm-v850/v850e_uart.h b/include/asm-v850/v850e_uart.h deleted file mode 100644 index 5182fb4cc989..000000000000 --- a/include/asm-v850/v850e_uart.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * include/asm-v850/v850e_uart.h -- common V850E on-chip UART driver - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -/* There's not actually a single UART implementation used by V850E CPUs, - but rather a series of implementations that are all `close' to one - another. This file corresponds to the single driver which handles all - of them. */ - -#ifndef __V850_V850E_UART_H__ -#define __V850_V850E_UART_H__ - -#include - -#include -#include -#include /* Pick up chip-specific defs. */ - - -/* Include model-specific definitions. */ -#ifdef CONFIG_V850E_UART -# ifdef CONFIG_V850E_UARTB -# include -# else -# include /* original V850E UART */ -# endif -#endif - - -/* Optional capabilities some hardware provides. */ - -/* This UART doesn't implement RTS/CTS by default, but some platforms - implement them externally, so check to see if defined - anything. */ -#ifdef V850E_UART_CTS -#define v850e_uart_cts(n) V850E_UART_CTS(n) -#else -#define v850e_uart_cts(n) (1) -#endif - -/* Do the same for RTS. */ -#ifdef V850E_UART_SET_RTS -#define v850e_uart_set_rts(n,v) V850E_UART_SET_RTS(n,v) -#else -#define v850e_uart_set_rts(n,v) ((void)0) -#endif - - -/* This is the serial channel to use for the boot console (if desired). */ -#ifndef V850E_UART_CONSOLE_CHANNEL -# define V850E_UART_CONSOLE_CHANNEL 0 -#endif - - -#ifndef __ASSEMBLY__ - -/* Setup a console using channel 0 of the builtin uart. */ -extern void v850e_uart_cons_init (unsigned chan); - -/* Configure and turn on uart channel CHAN, using the termios `control - modes' bits in CFLAGS, and a baud-rate of BAUD. */ -void v850e_uart_configure (unsigned chan, unsigned cflags, unsigned baud); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_UART_H__ */ diff --git a/include/asm-v850/v850e_uarta.h b/include/asm-v850/v850e_uarta.h deleted file mode 100644 index e483e0950725..000000000000 --- a/include/asm-v850/v850e_uarta.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * include/asm-v850/v850e_uarta.h -- original V850E on-chip UART - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -/* This is the original V850E UART implementation is called just `UART' in - the docs, but we name this header file because the - name is used for the common driver that handles both - `UART' and `UARTB' implementations. */ - -#ifndef __V850_V850E_UARTA_H__ -#define __V850_V850E_UARTA_H__ - - -/* Raw hardware interface. */ - -/* The base address of the UART control registers for channel N. - The default is the address used on the V850E/MA1. */ -#ifndef V850E_UART_BASE_ADDR -#define V850E_UART_BASE_ADDR(n) (0xFFFFFA00 + 0x10 * (n)) -#endif - -/* Addresses of specific UART control registers for channel N. - The defaults are the addresses used on the V850E/MA1; if a platform - wants to redefine any of these, it must redefine them all. */ -#ifndef V850E_UART_ASIM_ADDR -#define V850E_UART_ASIM_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x0) -#define V850E_UART_RXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x2) -#define V850E_UART_ASIS_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x3) -#define V850E_UART_TXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x4) -#define V850E_UART_ASIF_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x5) -#define V850E_UART_CKSR_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x6) -#define V850E_UART_BRGC_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x7) -#endif - -/* UART config registers. */ -#define V850E_UART_ASIM(n) (*(volatile u8 *)V850E_UART_ASIM_ADDR(n)) -/* Control bits for config registers. */ -#define V850E_UART_ASIM_CAE 0x80 /* clock enable */ -#define V850E_UART_ASIM_TXE 0x40 /* transmit enable */ -#define V850E_UART_ASIM_RXE 0x20 /* receive enable */ -#define V850E_UART_ASIM_PS_MASK 0x18 /* mask covering parity-select bits */ -#define V850E_UART_ASIM_PS_NONE 0x00 /* no parity */ -#define V850E_UART_ASIM_PS_ZERO 0x08 /* zero parity */ -#define V850E_UART_ASIM_PS_ODD 0x10 /* odd parity */ -#define V850E_UART_ASIM_PS_EVEN 0x18 /* even parity */ -#define V850E_UART_ASIM_CL_8 0x04 /* char len is 8 bits (otherwise, 7) */ -#define V850E_UART_ASIM_SL_2 0x02 /* 2 stop bits (otherwise, 1) */ -#define V850E_UART_ASIM_ISRM 0x01 /* generate INTSR interrupt on errors - (otherwise, generate INTSER) */ - -/* UART serial interface status registers. */ -#define V850E_UART_ASIS(n) (*(volatile u8 *)V850E_UART_ASIS_ADDR(n)) -/* Control bits for status registers. */ -#define V850E_UART_ASIS_PE 0x04 /* parity error */ -#define V850E_UART_ASIS_FE 0x02 /* framing error */ -#define V850E_UART_ASIS_OVE 0x01 /* overrun error */ - -/* UART serial interface transmission status registers. */ -#define V850E_UART_ASIF(n) (*(volatile u8 *)V850E_UART_ASIF_ADDR(n)) -#define V850E_UART_ASIF_TXBF 0x02 /* transmit buffer flag (data in TXB) */ -#define V850E_UART_ASIF_TXSF 0x01 /* transmit shift flag (sending data) */ - -/* UART receive buffer register. */ -#define V850E_UART_RXB(n) (*(volatile u8 *)V850E_UART_RXB_ADDR(n)) - -/* UART transmit buffer register. */ -#define V850E_UART_TXB(n) (*(volatile u8 *)V850E_UART_TXB_ADDR(n)) - -/* UART baud-rate generator control registers. */ -#define V850E_UART_CKSR(n) (*(volatile u8 *)V850E_UART_CKSR_ADDR(n)) -#define V850E_UART_CKSR_MAX 11 -#define V850E_UART_BRGC(n) (*(volatile u8 *)V850E_UART_BRGC_ADDR(n)) -#define V850E_UART_BRGC_MIN 8 - - -#ifndef V850E_UART_CKSR_MAX_FREQ -#define V850E_UART_CKSR_MAX_FREQ (25*1000*1000) -#endif - -/* Calculate the minimum value for CKSR on this processor. */ -static inline unsigned v850e_uart_cksr_min (void) -{ - int min = 0; - unsigned freq = V850E_UART_BASE_FREQ; - while (freq > V850E_UART_CKSR_MAX_FREQ) { - freq >>= 1; - min++; - } - return min; -} - - -/* Slightly abstract interface used by driver. */ - - -/* Interrupts used by the UART. */ - -/* Received when the most recently transmitted character has been sent. */ -#define V850E_UART_TX_IRQ(chan) IRQ_INTST (chan) -/* Received when a new character has been received. */ -#define V850E_UART_RX_IRQ(chan) IRQ_INTSR (chan) - - -/* UART clock generator interface. */ - -/* This type encapsulates a particular uart frequency. */ -typedef struct { - unsigned clk_divlog2; - unsigned brgen_count; -} v850e_uart_speed_t; - -/* Calculate a uart speed from BAUD for this uart. */ -static inline v850e_uart_speed_t v850e_uart_calc_speed (unsigned baud) -{ - v850e_uart_speed_t speed; - - /* Calculate the log2 clock divider and baud-rate counter values - (note that the UART divides the resulting clock by 2, so - multiply BAUD by 2 here to compensate). */ - calc_counter_params (V850E_UART_BASE_FREQ, baud * 2, - v850e_uart_cksr_min(), - V850E_UART_CKSR_MAX, 8/*bits*/, - &speed.clk_divlog2, &speed.brgen_count); - - return speed; -} - -/* Return the current speed of uart channel CHAN. */ -static inline v850e_uart_speed_t v850e_uart_speed (unsigned chan) -{ - v850e_uart_speed_t speed; - speed.clk_divlog2 = V850E_UART_CKSR (chan); - speed.brgen_count = V850E_UART_BRGC (chan); - return speed; -} - -/* Set the current speed of uart channel CHAN. */ -static inline void v850e_uart_set_speed(unsigned chan,v850e_uart_speed_t speed) -{ - V850E_UART_CKSR (chan) = speed.clk_divlog2; - V850E_UART_BRGC (chan) = speed.brgen_count; -} - -static inline int -v850e_uart_speed_eq (v850e_uart_speed_t speed1, v850e_uart_speed_t speed2) -{ - return speed1.clk_divlog2 == speed2.clk_divlog2 - && speed1.brgen_count == speed2.brgen_count; -} - -/* Minimum baud rate possible. */ -#define v850e_uart_min_baud() \ - ((V850E_UART_BASE_FREQ >> V850E_UART_CKSR_MAX) / (2 * 255) + 1) - -/* Maximum baud rate possible. The error is quite high at max, though. */ -#define v850e_uart_max_baud() \ - ((V850E_UART_BASE_FREQ >> v850e_uart_cksr_min()) / (2 *V850E_UART_BRGC_MIN)) - -/* The `maximum' clock rate the uart can used, which is wanted (though not - really used in any useful way) by the serial framework. */ -#define v850e_uart_max_clock() \ - ((V850E_UART_BASE_FREQ >> v850e_uart_cksr_min()) / 2) - - -/* UART configuration interface. */ - -/* Type of the uart config register; must be a scalar. */ -typedef u16 v850e_uart_config_t; - -/* The uart hardware config register for channel CHAN. */ -#define V850E_UART_CONFIG(chan) V850E_UART_ASIM (chan) - -/* This config bit set if the uart is enabled. */ -#define V850E_UART_CONFIG_ENABLED V850E_UART_ASIM_CAE -/* If the uart _isn't_ enabled, store this value to it to do so. */ -#define V850E_UART_CONFIG_INIT V850E_UART_ASIM_CAE -/* Store this config value to disable the uart channel completely. */ -#define V850E_UART_CONFIG_FINI 0 - -/* Setting/clearing these bits enable/disable TX/RX, respectively (but - otherwise generally leave things running). */ -#define V850E_UART_CONFIG_RX_ENABLE V850E_UART_ASIM_RXE -#define V850E_UART_CONFIG_TX_ENABLE V850E_UART_ASIM_TXE - -/* These masks define which config bits affect TX/RX modes, respectively. */ -#define V850E_UART_CONFIG_RX_BITS \ - (V850E_UART_ASIM_PS_MASK | V850E_UART_ASIM_CL_8 | V850E_UART_ASIM_ISRM) -#define V850E_UART_CONFIG_TX_BITS \ - (V850E_UART_ASIM_PS_MASK | V850E_UART_ASIM_CL_8 | V850E_UART_ASIM_SL_2) - -static inline v850e_uart_config_t v850e_uart_calc_config (unsigned cflags) -{ - v850e_uart_config_t config = 0; - - /* Figure out new configuration of control register. */ - if (cflags & CSTOPB) - /* Number of stop bits, 1 or 2. */ - config |= V850E_UART_ASIM_SL_2; - if ((cflags & CSIZE) == CS8) - /* Number of data bits, 7 or 8. */ - config |= V850E_UART_ASIM_CL_8; - if (! (cflags & PARENB)) - /* No parity check/generation. */ - config |= V850E_UART_ASIM_PS_NONE; - else if (cflags & PARODD) - /* Odd parity check/generation. */ - config |= V850E_UART_ASIM_PS_ODD; - else - /* Even parity check/generation. */ - config |= V850E_UART_ASIM_PS_EVEN; - if (cflags & CREAD) - /* Reading enabled. */ - config |= V850E_UART_ASIM_RXE; - - config |= V850E_UART_ASIM_CAE; - config |= V850E_UART_ASIM_TXE; /* Writing is always enabled. */ - config |= V850E_UART_ASIM_ISRM; /* Errors generate a read-irq. */ - - return config; -} - -/* This should delay as long as necessary for a recently written config - setting to settle, before we turn the uart back on. */ -static inline void -v850e_uart_config_delay (v850e_uart_config_t config, v850e_uart_speed_t speed) -{ - /* The UART may not be reset properly unless we wait at least 2 - `basic-clocks' until turning on the TXE/RXE bits again. - A `basic clock' is the clock used by the baud-rate generator, - i.e., the cpu clock divided by the 2^new_clk_divlog2. - The loop takes 2 insns, so loop CYCLES / 2 times. */ - register unsigned count = 1 << speed.clk_divlog2; - while (--count != 0) - /* nothing */; -} - - -/* RX/TX interface. */ - -/* Return true if all characters awaiting transmission on uart channel N - have been transmitted. */ -#define v850e_uart_xmit_done(n) \ - (! (V850E_UART_ASIF(n) & V850E_UART_ASIF_TXBF)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_done(n) \ - do { } while (! v850e_uart_xmit_done (n)) - -/* Return true if uart channel N is ready to transmit a character. */ -#define v850e_uart_xmit_ok(n) \ - (v850e_uart_xmit_done(n) && v850e_uart_cts(n)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_ok(n) \ - do { } while (! v850e_uart_xmit_ok (n)) - -/* Write character CH to uart channel CHAN. */ -#define v850e_uart_putc(chan, ch) (V850E_UART_TXB(chan) = (ch)) - -/* Return latest character read on channel CHAN. */ -#define v850e_uart_getc(chan) V850E_UART_RXB (chan) - -/* Return bit-mask of uart error status. */ -#define v850e_uart_err(chan) V850E_UART_ASIS (chan) -/* Various error bits set in the error result. */ -#define V850E_UART_ERR_OVERRUN V850E_UART_ASIS_OVE -#define V850E_UART_ERR_FRAME V850E_UART_ASIS_FE -#define V850E_UART_ERR_PARITY V850E_UART_ASIS_PE - - -#endif /* __V850_V850E_UARTA_H__ */ diff --git a/include/asm-v850/v850e_uartb.h b/include/asm-v850/v850e_uartb.h deleted file mode 100644 index 6d4767d5a835..000000000000 --- a/include/asm-v850/v850e_uartb.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * include/asm-v850/v850e_uartb.h -- V850E on-chip `UARTB' UART - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -/* The V850E UARTB is basically a superset of the original V850E UART, but - even where it's the same, the names and details have changed a bit. - It's similar enough to use the same driver (v850e_uart.c), but the - details have been abstracted slightly to do so. */ - -#ifndef __V850_V850E_UARTB_H__ -#define __V850_V850E_UARTB_H__ - - -/* Raw hardware interface. */ - -#define V850E_UARTB_BASE_ADDR(n) (0xFFFFFA00 + 0x10 * (n)) - -/* Addresses of specific UART control registers for channel N. */ -#define V850E_UARTB_CTL0_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x0) -#define V850E_UARTB_CTL2_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x2) -#define V850E_UARTB_STR_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x4) -#define V850E_UARTB_RX_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x6) -#define V850E_UARTB_RXAP_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x6) -#define V850E_UARTB_TX_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x8) -#define V850E_UARTB_FIC0_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xA) -#define V850E_UARTB_FIC1_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xB) -#define V850E_UARTB_FIC2_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xC) -#define V850E_UARTB_FIS0_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xE) -#define V850E_UARTB_FIS1_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xF) - -/* UARTB control register 0 (general config). */ -#define V850E_UARTB_CTL0(n) (*(volatile u8 *)V850E_UARTB_CTL0_ADDR(n)) -/* Control bits for config registers. */ -#define V850E_UARTB_CTL0_PWR 0x80 /* clock enable */ -#define V850E_UARTB_CTL0_TXE 0x40 /* transmit enable */ -#define V850E_UARTB_CTL0_RXE 0x20 /* receive enable */ -#define V850E_UARTB_CTL0_DIR 0x10 /* */ -#define V850E_UARTB_CTL0_PS1 0x08 /* parity */ -#define V850E_UARTB_CTL0_PS0 0x04 /* parity */ -#define V850E_UARTB_CTL0_CL 0x02 /* char len 1:8bit, 0:7bit */ -#define V850E_UARTB_CTL0_SL 0x01 /* stop bit 1:2bit, 0:1bit */ -#define V850E_UARTB_CTL0_PS_MASK 0x0C /* mask covering parity bits */ -#define V850E_UARTB_CTL0_PS_NONE 0x00 /* no parity */ -#define V850E_UARTB_CTL0_PS_ZERO 0x04 /* zero parity */ -#define V850E_UARTB_CTL0_PS_ODD 0x08 /* odd parity */ -#define V850E_UARTB_CTL0_PS_EVEN 0x0C /* even parity */ -#define V850E_UARTB_CTL0_CL_8 0x02 /* char len 1:8bit, 0:7bit */ -#define V850E_UARTB_CTL0_SL_2 0x01 /* stop bit 1:2bit, 0:1bit */ - -/* UARTB control register 2 (clock divider). */ -#define V850E_UARTB_CTL2(n) (*(volatile u16 *)V850E_UARTB_CTL2_ADDR(n)) -#define V850E_UARTB_CTL2_MIN 4 -#define V850E_UARTB_CTL2_MAX 0xFFFF - -/* UARTB serial interface status register. */ -#define V850E_UARTB_STR(n) (*(volatile u8 *)V850E_UARTB_STR_ADDR(n)) -/* Control bits for status registers. */ -#define V850E_UARTB_STR_TSF 0x80 /* UBTX or FIFO exist data */ -#define V850E_UARTB_STR_OVF 0x08 /* overflow error */ -#define V850E_UARTB_STR_PE 0x04 /* parity error */ -#define V850E_UARTB_STR_FE 0x02 /* framing error */ -#define V850E_UARTB_STR_OVE 0x01 /* overrun error */ - -/* UARTB receive data register. */ -#define V850E_UARTB_RX(n) (*(volatile u8 *)V850E_UARTB_RX_ADDR(n)) -#define V850E_UARTB_RXAP(n) (*(volatile u16 *)V850E_UARTB_RXAP_ADDR(n)) -/* Control bits for status registers. */ -#define V850E_UARTB_RXAP_PEF 0x0200 /* parity error */ -#define V850E_UARTB_RXAP_FEF 0x0100 /* framing error */ - -/* UARTB transmit data register. */ -#define V850E_UARTB_TX(n) (*(volatile u8 *)V850E_UARTB_TX_ADDR(n)) - -/* UARTB FIFO control register 0. */ -#define V850E_UARTB_FIC0(n) (*(volatile u8 *)V850E_UARTB_FIC0_ADDR(n)) - -/* UARTB FIFO control register 1. */ -#define V850E_UARTB_FIC1(n) (*(volatile u8 *)V850E_UARTB_FIC1_ADDR(n)) - -/* UARTB FIFO control register 2. */ -#define V850E_UARTB_FIC2(n) (*(volatile u16 *)V850E_UARTB_FIC2_ADDR(n)) - -/* UARTB FIFO status register 0. */ -#define V850E_UARTB_FIS0(n) (*(volatile u8 *)V850E_UARTB_FIS0_ADDR(n)) - -/* UARTB FIFO status register 1. */ -#define V850E_UARTB_FIS1(n) (*(volatile u8 *)V850E_UARTB_FIS1_ADDR(n)) - - -/* Slightly abstract interface used by driver. */ - - -/* Interrupts used by the UART. */ - -/* Received when the most recently transmitted character has been sent. */ -#define V850E_UART_TX_IRQ(chan) IRQ_INTUBTIT (chan) -/* Received when a new character has been received. */ -#define V850E_UART_RX_IRQ(chan) IRQ_INTUBTIR (chan) - -/* Use by serial driver for information purposes. */ -#define V850E_UART_BASE_ADDR(chan) V850E_UARTB_BASE_ADDR(chan) - - -/* UART clock generator interface. */ - -/* This type encapsulates a particular uart frequency. */ -typedef u16 v850e_uart_speed_t; - -/* Calculate a uart speed from BAUD for this uart. */ -static inline v850e_uart_speed_t v850e_uart_calc_speed (unsigned baud) -{ - v850e_uart_speed_t speed; - - /* - * V850E/ME2 UARTB baud rate is determined by the value of UBCTL2 - * fx = V850E_UARTB_BASE_FREQ = CPU_CLOCK_FREQ/4 - * baud = fx / 2*speed [ speed >= 4 ] - */ - speed = V850E_UARTB_CTL2_MIN; - while (((V850E_UARTB_BASE_FREQ / 2) / speed ) > baud) - speed++; - - return speed; -} - -/* Return the current speed of uart channel CHAN. */ -#define v850e_uart_speed(chan) V850E_UARTB_CTL2 (chan) - -/* Set the current speed of uart channel CHAN. */ -#define v850e_uart_set_speed(chan, speed) (V850E_UARTB_CTL2 (chan) = (speed)) - -/* Return true if SPEED1 and SPEED2 are the same. */ -#define v850e_uart_speed_eq(speed1, speed2) ((speed1) == (speed2)) - -/* Minimum baud rate possible. */ -#define v850e_uart_min_baud() \ - ((V850E_UARTB_BASE_FREQ / 2) / V850E_UARTB_CTL2_MAX) - -/* Maximum baud rate possible. The error is quite high at max, though. */ -#define v850e_uart_max_baud() \ - ((V850E_UARTB_BASE_FREQ / 2) / V850E_UARTB_CTL2_MIN) - -/* The `maximum' clock rate the uart can used, which is wanted (though not - really used in any useful way) by the serial framework. */ -#define v850e_uart_max_clock() \ - (V850E_UARTB_BASE_FREQ / 2) - - -/* UART configuration interface. */ - -/* Type of the uart config register; must be a scalar. */ -typedef u16 v850e_uart_config_t; - -/* The uart hardware config register for channel CHAN. */ -#define V850E_UART_CONFIG(chan) V850E_UARTB_CTL0 (chan) - -/* This config bit set if the uart is enabled. */ -#define V850E_UART_CONFIG_ENABLED V850E_UARTB_CTL0_PWR -/* If the uart _isn't_ enabled, store this value to it to do so. */ -#define V850E_UART_CONFIG_INIT V850E_UARTB_CTL0_PWR -/* Store this config value to disable the uart channel completely. */ -#define V850E_UART_CONFIG_FINI 0 - -/* Setting/clearing these bits enable/disable TX/RX, respectively (but - otherwise generally leave things running). */ -#define V850E_UART_CONFIG_RX_ENABLE V850E_UARTB_CTL0_RXE -#define V850E_UART_CONFIG_TX_ENABLE V850E_UARTB_CTL0_TXE - -/* These masks define which config bits affect TX/RX modes, respectively. */ -#define V850E_UART_CONFIG_RX_BITS \ - (V850E_UARTB_CTL0_PS_MASK | V850E_UARTB_CTL0_CL_8) -#define V850E_UART_CONFIG_TX_BITS \ - (V850E_UARTB_CTL0_PS_MASK | V850E_UARTB_CTL0_CL_8 | V850E_UARTB_CTL0_SL_2) - -static inline v850e_uart_config_t v850e_uart_calc_config (unsigned cflags) -{ - v850e_uart_config_t config = 0; - - /* Figure out new configuration of control register. */ - if (cflags & CSTOPB) - /* Number of stop bits, 1 or 2. */ - config |= V850E_UARTB_CTL0_SL_2; - if ((cflags & CSIZE) == CS8) - /* Number of data bits, 7 or 8. */ - config |= V850E_UARTB_CTL0_CL_8; - if (! (cflags & PARENB)) - /* No parity check/generation. */ - config |= V850E_UARTB_CTL0_PS_NONE; - else if (cflags & PARODD) - /* Odd parity check/generation. */ - config |= V850E_UARTB_CTL0_PS_ODD; - else - /* Even parity check/generation. */ - config |= V850E_UARTB_CTL0_PS_EVEN; - if (cflags & CREAD) - /* Reading enabled. */ - config |= V850E_UARTB_CTL0_RXE; - - config |= V850E_UARTB_CTL0_PWR; - config |= V850E_UARTB_CTL0_TXE; /* Writing is always enabled. */ - config |= V850E_UARTB_CTL0_DIR; /* LSB first. */ - - return config; -} - -/* This should delay as long as necessary for a recently written config - setting to settle, before we turn the uart back on. */ -static inline void -v850e_uart_config_delay (v850e_uart_config_t config, v850e_uart_speed_t speed) -{ - /* The UART may not be reset properly unless we wait at least 2 - `basic-clocks' until turning on the TXE/RXE bits again. - A `basic clock' is the clock used by the baud-rate generator, - i.e., the cpu clock divided by the 2^new_clk_divlog2. - The loop takes 2 insns, so loop CYCLES / 2 times. */ - register unsigned count = 1 << speed; - while (--count != 0) - /* nothing */; -} - - -/* RX/TX interface. */ - -/* Return true if all characters awaiting transmission on uart channel N - have been transmitted. */ -#define v850e_uart_xmit_done(n) \ - (! (V850E_UARTB_STR(n) & V850E_UARTB_STR_TSF)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_done(n) \ - do { } while (! v850e_uart_xmit_done (n)) - -/* Return true if uart channel N is ready to transmit a character. */ -#define v850e_uart_xmit_ok(n) \ - (v850e_uart_xmit_done(n) && v850e_uart_cts(n)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_ok(n) \ - do { } while (! v850e_uart_xmit_ok (n)) - -/* Write character CH to uart channel CHAN. */ -#define v850e_uart_putc(chan, ch) (V850E_UARTB_TX(chan) = (ch)) - -/* Return latest character read on channel CHAN. */ -#define v850e_uart_getc(chan) V850E_UARTB_RX (chan) - -/* Return bit-mask of uart error status. */ -#define v850e_uart_err(chan) V850E_UARTB_STR (chan) -/* Various error bits set in the error result. */ -#define V850E_UART_ERR_OVERRUN V850E_UARTB_STR_OVE -#define V850E_UART_ERR_FRAME V850E_UARTB_STR_FE -#define V850E_UART_ERR_PARITY V850E_UARTB_STR_PE - - -#endif /* __V850_V850E_UARTB_H__ */ diff --git a/include/asm-v850/v850e_utils.h b/include/asm-v850/v850e_utils.h deleted file mode 100644 index 52eb72822d3d..000000000000 --- a/include/asm-v850/v850e_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * include/asm-v850/v850e_utils.h -- Utility functions associated with - * V850E CPUs - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file COPYING in the main directory of this - * archive for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_UTILS_H__ -#define __V850_V850E_UTILS_H__ - -/* Calculate counter clock-divider and count values to attain the - desired frequency RATE from the base frequency BASE_FREQ. The - counter is expected to have a clock-divider, which can divide the - system cpu clock by a power of two value from MIN_DIVLOG2 to - MAX_DIV_LOG2, and a word-size of COUNTER_SIZE bits (the counter - counts up and resets whenever it's equal to the compare register, - generating an interrupt or whatever when it does so). The returned - values are: *DIVLOG2 -- log2 of the desired clock divider and *COUNT - -- the counter compare value to use. Returns true if it was possible - to find a reasonable value, otherwise false (and the other return - values will be set to be as good as possible). */ -extern int calc_counter_params (unsigned long base_freq, - unsigned long rate, - unsigned min_divlog2, unsigned max_divlog2, - unsigned counter_size, - unsigned *divlog2, unsigned *count); - -#endif /* __V850_V850E_UTILS_H__ */ diff --git a/include/linux/audit.h b/include/linux/audit.h index 8b82974bdc12..6272a395d43c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -286,7 +286,6 @@ #define AUDIT_ARCH_SHEL64 (EM_SH|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_SPARC (EM_SPARC) #define AUDIT_ARCH_SPARC64 (EM_SPARCV9|__AUDIT_ARCH_64BIT) -#define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE) #define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_PERM_EXEC 1 diff --git a/include/linux/module.h b/include/linux/module.h index fce15ebd0e1c..68e09557c951 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -23,7 +23,7 @@ /* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE(name) -/* v850 toolchain uses a `_' prefix for all user symbols */ +/* some toolchains uses a `_' prefix for all user symbols */ #ifndef MODULE_SYMBOL_PREFIX #define MODULE_SYMBOL_PREFIX "" #endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f3a1c0e45021..3b2f6c04855e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -59,9 +59,6 @@ #define PORT_SUNZILOG 38 #define PORT_SUNSAB 39 -/* NEC v850. */ -#define PORT_V850E_UART 40 - /* DEC */ #define PORT_DZ 46 #define PORT_ZS 47 diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 0522f368f9d7..4394dadff813 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -443,7 +443,7 @@ asmlinkage long sys_newuname(struct new_utsname __user *name); asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit __user *rlim); -#if defined(COMPAT_RLIM_OLD_INFINITY) || !(defined(CONFIG_IA64) || defined(CONFIG_V850)) +#if defined(COMPAT_RLIM_OLD_INFINITY) || !(defined(CONFIG_IA64)) asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *rlim); #endif asmlinkage long sys_setrlimit(unsigned int resource, diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index dca5e0dd09bf..4f8a3007e457 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c @@ -520,8 +520,7 @@ int main(int argc, char **argv) genksyms_usage(); return 1; } - if ((strcmp(arch, "v850") == 0) || (strcmp(arch, "h8300") == 0) - || (strcmp(arch, "blackfin") == 0)) + if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0)) mod_prefix = "_"; { extern int yydebug; diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 1fcaf3284a6a..4fa1f3ad2513 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -623,7 +623,7 @@ static int do_i2c_entry(const char *filename, struct i2c_device_id *id, return 1; } -/* Ignore any prefix, eg. v850 prepends _ */ +/* Ignore any prefix, eg. some architectures prepend _ */ static inline int sym_is(const char *symbol, const char *name) { const char *match; diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c index db3881f14c2d..6a96d47bd1e6 100644 --- a/scripts/mod/mk_elfconfig.c +++ b/scripts/mod/mk_elfconfig.c @@ -55,7 +55,7 @@ main(int argc, char **argv) else exit(1); - if ((strcmp(argv[1], "v850") == 0) || (strcmp(argv[1], "h8300") == 0) + if ((strcmp(argv[1], "h8300") == 0) || (strcmp(argv[1], "blackfin") == 0)) printf("#define MODULE_SYMBOL_PREFIX \"_\"\n"); else -- cgit v1.2.3 From a677a039be7243357d93502bff2b40850c942e2d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:17 -0700 Subject: flag parameters: socket and socketpair This patch adds support for flag values which are ORed to the type passwd to socket and socketpair. The additional code is minimal. The flag values in this implementation can and must match the O_* flags. This avoids overhead in the conversion. The internal functions sock_alloc_fd and sock_map_fd get a new parameters and all callers are changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #define PORT 57392 /* For Linux these must be the same. */ #define SOCK_CLOEXEC O_CLOEXEC int main (void) { int fd; fd = socket (PF_INET, SOCK_STREAM, 0); if (fd == -1) { puts ("socket(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("socket(0) set close-on-exec flag"); return 1; } close (fd); fd = socket (PF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0); if (fd == -1) { puts ("socket(SOCK_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("socket(SOCK_CLOEXEC) does not set close-on-exec flag"); return 1; } close (fd); int fds[2]; if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1) { puts ("socketpair(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { coe = fcntl (fds[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { printf ("socketpair(0) set close-on-exec flag for fds[%d]\n", i); return 1; } close (fds[i]); } if (socketpair (PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds) == -1) { puts ("socketpair(SOCK_CLOEXEC) failed"); return 1; } for (int i = 0; i < 2; ++i) { coe = fcntl (fds[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { printf ("socketpair(SOCK_CLOEXEC) does not set close-on-exec flag for fds[%d]\n", i); return 1; } close (fds[i]); } puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: "David S. Miller" Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-mips/socket.h | 7 +++++++ include/linux/net.h | 9 ++++++++- net/9p/trans_fd.c | 2 +- net/sctp/socket.c | 2 +- net/socket.c | 28 ++++++++++++++++++++-------- 5 files changed, 37 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/asm-mips/socket.h b/include/asm-mips/socket.h index 63f60254d308..facc2d7a87ca 100644 --- a/include/asm-mips/socket.h +++ b/include/asm-mips/socket.h @@ -102,6 +102,13 @@ enum sock_type { }; #define SOCK_MAX (SOCK_PACKET + 1) +/* Mask which covers at least up to SOCK_MASK-1. The + * * remaining bits are used as flags. */ +#define SOCK_TYPE_MASK 0xf + +/* Flags for socket, socketpair, paccept */ +#define SOCK_CLOEXEC O_CLOEXEC +#define SOCK_NONBLOCK O_NONBLOCK #define ARCH_HAS_SOCKET_TYPES 1 diff --git a/include/linux/net.h b/include/linux/net.h index 150a48c68d52..8b5383c45b45 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -20,6 +20,7 @@ #include #include +#include /* For O_CLOEXEC */ #include struct poll_table_struct; @@ -94,6 +95,12 @@ enum sock_type { }; #define SOCK_MAX (SOCK_PACKET + 1) +/* Mask which covers at least up to SOCK_MASK-1. The + * remaining bits are used as flags. */ +#define SOCK_TYPE_MASK 0xf + +/* Flags for socket, socketpair, paccept */ +#define SOCK_CLOEXEC O_CLOEXEC #endif /* ARCH_HAS_SOCKET_TYPES */ @@ -208,7 +215,7 @@ extern int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len); extern int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags); -extern int sock_map_fd(struct socket *sock); +extern int sock_map_fd(struct socket *sock, int flags); extern struct socket *sockfd_lookup(int fd, int *err); #define sockfd_put(sock) fput(sock->file) extern int net_ratelimit(void); diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index 4507f744f44e..cdf137af7adc 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -1285,7 +1285,7 @@ static int p9_socket_open(struct p9_trans *trans, struct socket *csocket) int fd, ret; csocket->sk->sk_allocation = GFP_NOIO; - fd = sock_map_fd(csocket); + fd = sock_map_fd(csocket, 0); if (fd < 0) { P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to map fd\n"); return fd; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 79bece16aede..dbb79adf8f3c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3910,7 +3910,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval goto out; /* Map the socket to an unused fd that can be returned to the user. */ - retval = sock_map_fd(newsock); + retval = sock_map_fd(newsock, 0); if (retval < 0) { sock_release(newsock); goto out; diff --git a/net/socket.c b/net/socket.c index 1ba57d888981..64601f900352 100644 --- a/net/socket.c +++ b/net/socket.c @@ -349,11 +349,11 @@ static struct dentry_operations sockfs_dentry_operations = { * but we take care of internal coherence yet. */ -static int sock_alloc_fd(struct file **filep) +static int sock_alloc_fd(struct file **filep, int flags) { int fd; - fd = get_unused_fd(); + fd = get_unused_fd_flags(flags); if (likely(fd >= 0)) { struct file *file = get_empty_filp(); @@ -396,10 +396,10 @@ static int sock_attach_fd(struct socket *sock, struct file *file) return 0; } -int sock_map_fd(struct socket *sock) +int sock_map_fd(struct socket *sock, int flags) { struct file *newfile; - int fd = sock_alloc_fd(&newfile); + int fd = sock_alloc_fd(&newfile, flags); if (likely(fd >= 0)) { int err = sock_attach_fd(sock, newfile); @@ -1218,12 +1218,18 @@ asmlinkage long sys_socket(int family, int type, int protocol) { int retval; struct socket *sock; + int flags; + + flags = type & ~SOCK_TYPE_MASK; + if (flags & ~SOCK_CLOEXEC) + return -EINVAL; + type &= SOCK_TYPE_MASK; retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; - retval = sock_map_fd(sock); + retval = sock_map_fd(sock, flags & O_CLOEXEC); if (retval < 0) goto out_release; @@ -1246,6 +1252,12 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, struct socket *sock1, *sock2; int fd1, fd2, err; struct file *newfile1, *newfile2; + int flags; + + flags = type & ~SOCK_TYPE_MASK; + if (flags & ~SOCK_CLOEXEC) + return -EINVAL; + type &= SOCK_TYPE_MASK; /* * Obtain the first socket and check if the underlying protocol @@ -1264,13 +1276,13 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, if (err < 0) goto out_release_both; - fd1 = sock_alloc_fd(&newfile1); + fd1 = sock_alloc_fd(&newfile1, flags & O_CLOEXEC); if (unlikely(fd1 < 0)) { err = fd1; goto out_release_both; } - fd2 = sock_alloc_fd(&newfile2); + fd2 = sock_alloc_fd(&newfile2, flags & O_CLOEXEC); if (unlikely(fd2 < 0)) { err = fd2; put_filp(newfile1); @@ -1426,7 +1438,7 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, */ __module_get(newsock->ops->owner); - newfd = sock_alloc_fd(&newfile); + newfd = sock_alloc_fd(&newfile, 0); if (unlikely(newfd < 0)) { err = newfd; sock_release(newsock); -- cgit v1.2.3 From aaca0bdca573f3f51ea03139f9c7289541e7bca3 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:20 -0700 Subject: flag parameters: paccept This patch is by far the most complex in the series. It adds a new syscall paccept. This syscall differs from accept in that it adds (at the userlevel) two additional parameters: - a signal mask - a flags value The flags parameter can be used to set flag like SOCK_CLOEXEC. This is imlpemented here as well. Some people argued that this is a property which should be inherited from the file desriptor for the server but this is against POSIX. Additionally, we really want the signal mask parameter as well (similar to pselect, ppoll, etc). So an interface change in inevitable. The flag value is the same as for socket and socketpair. I think diverging here will only create confusion. Similar to the filesystem interfaces where the use of the O_* constants differs, it is acceptable here. The signal mask is handled as for pselect etc. The mask is temporarily installed for the thread and removed before the call returns. I modeled the code after pselect. If there is a problem it's likely also in pselect. For architectures which use socketcall I maintained this interface instead of adding a system call. The symmetry shouldn't be broken. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #include #include #include #include #ifndef __NR_paccept # ifdef __x86_64__ # define __NR_paccept 288 # elif defined __i386__ # define SYS_PACCEPT 18 # define USE_SOCKETCALL 1 # else # error "need __NR_paccept" # endif #endif #ifdef USE_SOCKETCALL # define paccept(fd, addr, addrlen, mask, flags) \ ({ long args[6] = { \ (long) fd, (long) addr, (long) addrlen, (long) mask, 8, (long) flags }; \ syscall (__NR_socketcall, SYS_PACCEPT, args); }) #else # define paccept(fd, addr, addrlen, mask, flags) \ syscall (__NR_paccept, fd, addr, addrlen, mask, 8, flags) #endif #define PORT 57392 #define SOCK_CLOEXEC O_CLOEXEC static pthread_barrier_t b; static void * tf (void *arg) { pthread_barrier_wait (&b); int s = socket (AF_INET, SOCK_STREAM, 0); struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); s = socket (AF_INET, SOCK_STREAM, 0); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); pthread_barrier_wait (&b); sleep (2); pthread_kill ((pthread_t) arg, SIGUSR1); return NULL; } static void handler (int s) { } int main (void) { pthread_barrier_init (&b, NULL, 2); struct sockaddr_in sin; pthread_t th; if (pthread_create (&th, NULL, tf, (void *) pthread_self ()) != 0) { puts ("pthread_create failed"); return 1; } int s = socket (AF_INET, SOCK_STREAM, 0); int reuse = 1; setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); bind (s, (struct sockaddr *) &sin, sizeof (sin)); listen (s, SOMAXCONN); pthread_barrier_wait (&b); int s2 = paccept (s, NULL, 0, NULL, 0); if (s2 < 0) { puts ("paccept(0) failed"); return 1; } int coe = fcntl (s2, F_GETFD); if (coe & FD_CLOEXEC) { puts ("paccept(0) set close-on-exec-flag"); return 1; } close (s2); pthread_barrier_wait (&b); s2 = paccept (s, NULL, 0, NULL, SOCK_CLOEXEC); if (s2 < 0) { puts ("paccept(SOCK_CLOEXEC) failed"); return 1; } coe = fcntl (s2, F_GETFD); if ((coe & FD_CLOEXEC) == 0) { puts ("paccept(SOCK_CLOEXEC) does not set close-on-exec flag"); return 1; } close (s2); pthread_barrier_wait (&b); struct sigaction sa; sa.sa_handler = handler; sa.sa_flags = 0; sigemptyset (&sa.sa_mask); sigaction (SIGUSR1, &sa, NULL); sigset_t ss; pthread_sigmask (SIG_SETMASK, NULL, &ss); sigaddset (&ss, SIGUSR1); pthread_sigmask (SIG_SETMASK, &ss, NULL); sigdelset (&ss, SIGUSR1); alarm (4); pthread_barrier_wait (&b); errno = 0 ; s2 = paccept (s, NULL, 0, &ss, 0); if (s2 != -1 || errno != EINTR) { puts ("paccept did not fail with EINTR"); return 1; } close (s); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: make it compile] [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Cc: "David S. Miller" Cc: Roland McGrath Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-alpha/socket.h | 5 +++ include/asm-parisc/socket.h | 5 +++ include/asm-x86/unistd_64.h | 2 ++ include/linux/net.h | 3 ++ include/linux/syscalls.h | 2 ++ kernel/sys_ni.c | 1 + net/compat.c | 52 ++++++++++++++++++++++++++--- net/socket.c | 81 ++++++++++++++++++++++++++++++++++++++++----- 8 files changed, 139 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/asm-alpha/socket.h b/include/asm-alpha/socket.h index 08c979319929..a1057c2d95e7 100644 --- a/include/asm-alpha/socket.h +++ b/include/asm-alpha/socket.h @@ -62,4 +62,9 @@ #define SO_MARK 36 +/* O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. + */ +#define SOCK_NONBLOCK 0x40000000 + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-parisc/socket.h b/include/asm-parisc/socket.h index 69a7a0d30b02..fba402c95ac2 100644 --- a/include/asm-parisc/socket.h +++ b/include/asm-parisc/socket.h @@ -54,4 +54,9 @@ #define SO_MARK 0x401f +/* O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. + */ +#define SOCK_NONBLOCK 0x40000000 + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index 9c1a4a3470d9..e323994a370f 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -639,6 +639,8 @@ __SYSCALL(__NR_fallocate, sys_fallocate) __SYSCALL(__NR_timerfd_settime, sys_timerfd_settime) #define __NR_timerfd_gettime 287 __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) +#define __NR_paccept 288 +__SYSCALL(__NR_paccept, sys_paccept) #ifndef __NO_STUBS diff --git a/include/linux/net.h b/include/linux/net.h index 8b5383c45b45..3a9b06d4d0fe 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -47,6 +47,7 @@ struct net; #define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */ #define SYS_SENDMSG 16 /* sys_sendmsg(2) */ #define SYS_RECVMSG 17 /* sys_recvmsg(2) */ +#define SYS_PACCEPT 18 /* sys_paccept(2) */ typedef enum { SS_FREE = 0, /* not allocated */ @@ -219,6 +220,8 @@ extern int sock_map_fd(struct socket *sock, int flags); extern struct socket *sockfd_lookup(int fd, int *err); #define sockfd_put(sock) fput(sock->file) extern int net_ratelimit(void); +extern long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, int flags); #define net_random() random32() #define net_srandom(seed) srandom32((__force u32)seed) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 4394dadff813..2a2a40af6b2c 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -409,6 +409,8 @@ asmlinkage long sys_getsockopt(int fd, int level, int optname, asmlinkage long sys_bind(int, struct sockaddr __user *, int); asmlinkage long sys_connect(int, struct sockaddr __user *, int); asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *); +asmlinkage long sys_paccept(int, struct sockaddr __user *, int __user *, + const sigset_t *, size_t, int); asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *); asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *); asmlinkage long sys_send(int, void __user *, size_t, unsigned); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 0fea0ee12da9..2f0b8a2e600f 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -31,6 +31,7 @@ cond_syscall(sys_socketpair); cond_syscall(sys_bind); cond_syscall(sys_listen); cond_syscall(sys_accept); +cond_syscall(sys_paccept); cond_syscall(sys_connect); cond_syscall(sys_getsockname); cond_syscall(sys_getpeername); diff --git a/net/compat.c b/net/compat.c index 6e1b03b51933..67fb6a3834a3 100644 --- a/net/compat.c +++ b/net/compat.c @@ -722,9 +722,10 @@ EXPORT_SYMBOL(compat_mc_getsockopt); /* Argument list sizes for compat_sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) -static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), +static unsigned char nas[19]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), - AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3), + AL(6)}; #undef AL asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) @@ -737,13 +738,52 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } +asmlinkage long compat_sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize, int flags) +{ + compat_sigset_t ss32; + sigset_t ksigmask, sigsaved; + int ret; + + if (sigmask) { + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &ss32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_accept(fd, upeer_sockaddr, upeer_addrlen, flags); + + if (ret == -ERESTARTNOHAND) { + /* + * Don't restore the signal mask yet. Let do_signal() deliver + * the signal on the way back to userspace, before the signal + * mask is restored. + */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_restore_sigmask(); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + asmlinkage long compat_sys_socketcall(int call, u32 __user *args) { int ret; u32 a[6]; u32 a0, a1; - if (call < SYS_SOCKET || call > SYS_RECVMSG) + if (call < SYS_SOCKET || call > SYS_PACCEPT) return -EINVAL; if (copy_from_user(a, args, nas[call])) return -EFAULT; @@ -764,7 +804,7 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) ret = sys_listen(a0, a1); break; case SYS_ACCEPT: - ret = sys_accept(a0, compat_ptr(a1), compat_ptr(a[2])); + ret = do_accept(a0, compat_ptr(a1), compat_ptr(a[2]), 0); break; case SYS_GETSOCKNAME: ret = sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2])); @@ -804,6 +844,10 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) case SYS_RECVMSG: ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); break; + case SYS_PACCEPT: + ret = compat_sys_paccept(a0, compat_ptr(a1), compat_ptr(a[2]), + compat_ptr(a[3]), a[4], a[5]); + break; default: ret = -EINVAL; break; diff --git a/net/socket.c b/net/socket.c index 64601f900352..a0ce8ad72252 100644 --- a/net/socket.c +++ b/net/socket.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -1225,6 +1226,9 @@ asmlinkage long sys_socket(int family, int type, int protocol) return -EINVAL; type &= SOCK_TYPE_MASK; + if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) + flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; + retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; @@ -1259,6 +1263,9 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, return -EINVAL; type &= SOCK_TYPE_MASK; + if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) + flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; + /* * Obtain the first socket and check if the underlying protocol * supports the socketpair call. @@ -1413,14 +1420,20 @@ asmlinkage long sys_listen(int fd, int backlog) * clean when we restucture accept also. */ -asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, - int __user *upeer_addrlen) +long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, int flags) { struct socket *sock, *newsock; struct file *newfile; int err, len, newfd, fput_needed; struct sockaddr_storage address; + if (flags & ~SOCK_CLOEXEC) + return -EINVAL; + + if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) + flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1438,7 +1451,7 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, */ __module_get(newsock->ops->owner); - newfd = sock_alloc_fd(&newfile, 0); + newfd = sock_alloc_fd(&newfile, flags & O_CLOEXEC); if (unlikely(newfd < 0)) { err = newfd; sock_release(newsock); @@ -1491,6 +1504,50 @@ out_fd: goto out_put; } +asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, + const sigset_t __user *sigmask, + size_t sigsetsize, int flags) +{ + sigset_t ksigmask, sigsaved; + int ret; + + if (sigmask) { + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_accept(fd, upeer_sockaddr, upeer_addrlen, flags); + + if (ret < 0 && signal_pending(current)) { + /* + * Don't restore the signal mask yet. Let do_signal() deliver + * the signal on the way back to userspace, before the signal + * mask is restored. + */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_restore_sigmask(); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen) +{ + return do_accept(fd, upeer_sockaddr, upeer_addrlen, 0); +} + /* * Attempt to connect to a socket with the server address. The address * is in user space so we verify it is OK and move it to kernel space. @@ -2011,10 +2068,11 @@ out: /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned long)) -static const unsigned char nargs[18]={ +static const unsigned char nargs[19]={ AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), - AL(6),AL(2),AL(5),AL(5),AL(3),AL(3) + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3), + AL(6) }; #undef AL @@ -2033,7 +2091,7 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) unsigned long a0, a1; int err; - if (call < 1 || call > SYS_RECVMSG) + if (call < 1 || call > SYS_PACCEPT) return -EINVAL; /* copy_from_user should be SMP safe. */ @@ -2062,8 +2120,8 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) break; case SYS_ACCEPT: err = - sys_accept(a0, (struct sockaddr __user *)a1, - (int __user *)a[2]); + do_accept(a0, (struct sockaddr __user *)a1, + (int __user *)a[2], 0); break; case SYS_GETSOCKNAME: err = @@ -2110,6 +2168,13 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) case SYS_RECVMSG: err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]); break; + case SYS_PACCEPT: + err = + sys_paccept(a0, (struct sockaddr __user *)a1, + (int __user *)a[2], + (const sigset_t __user *) a[3], + a[4], a[5]); + break; default: err = -EINVAL; break; -- cgit v1.2.3 From c019bbc612f6633ede7ed67725cbf68de45ae8a4 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:21 -0700 Subject: flag parameters: paccept w/out set_restore_sigmask Some platforms do not have support to restore the signal mask in the return path from a syscall. For those platforms syscalls like pselect are not defined at all. This is, I think, not a good choice for paccept() since paccept() adds more value on top of accept() than just the signal mask handling. Therefore this patch defines a scaled down version of the sys_paccept function for those platforms. It returns -EINVAL in case the signal mask is non-NULL but behaves the same otherwise. Note that I explicitly included . I saw that it is currently included but indirectly two levels down. There is too much risk in relying on this. The header might change and then suddenly the function definition would change without anyone immediately noticing. Signed-off-by: Ulrich Drepper Cc: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/net.h | 3 +++ net/socket.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 3a9b06d4d0fe..39a23af059b4 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -102,6 +102,9 @@ enum sock_type { /* Flags for socket, socketpair, paccept */ #define SOCK_CLOEXEC O_CLOEXEC +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK O_NONBLOCK +#endif #endif /* ARCH_HAS_SOCKET_TYPES */ diff --git a/net/socket.c b/net/socket.c index a0ce8ad72252..d163adff95bf 100644 --- a/net/socket.c +++ b/net/socket.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -1504,6 +1505,7 @@ out_fd: goto out_put; } +#ifdef HAVE_SET_RESTORE_SIGMASK asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, const sigset_t __user *sigmask, @@ -1541,6 +1543,21 @@ asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, return ret; } +#else +asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, + const sigset_t __user *sigmask, + size_t sigsetsize, int flags) +{ + /* The platform does not support restoring the signal mask in the + * return path. So we do not allow using paccept() with a signal + * mask. */ + if (sigmask) + return -EINVAL; + + return do_accept(fd, upeer_sockaddr, upeer_addrlen, flags); +} +#endif asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen) -- cgit v1.2.3 From 7d9dbca34240ebb6ff88d8a29c6c7bffd098f0c1 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:22 -0700 Subject: flag parameters: anon_inode_getfd extension This patch just extends the anon_inode_getfd interface to take an additional parameter with a flag value. The flag value is passed on to get_unused_fd_flags in anticipation for a use with the O_CLOEXEC flag. No actual semantic changes here, the changed callers all pass 0 for now. [akpm@linux-foundation.org: KVM fix] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/anon_inodes.c | 9 +++++---- fs/eventfd.c | 2 +- fs/eventpoll.c | 2 +- fs/signalfd.c | 3 ++- fs/timerfd.c | 2 +- include/linux/anon_inodes.h | 2 +- virt/kvm/kvm_main.c | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 977ef208c051..1a4eee620b0d 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -58,8 +58,9 @@ static struct dentry_operations anon_inodefs_dentry_operations = { * of the file * * @name: [in] name of the "class" of the new file - * @fops [in] file operations for the new file - * @priv [in] private data for the new file (will be file's private_data) + * @fops: [in] file operations for the new file + * @priv: [in] private data for the new file (will be file's private_data) + * @flags: [in] flags * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. @@ -68,7 +69,7 @@ static struct dentry_operations anon_inodefs_dentry_operations = { * setup. Returns new descriptor or -error. */ int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv) + void *priv, int flags) { struct qstr this; struct dentry *dentry; @@ -78,7 +79,7 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, if (IS_ERR(anon_inode_inode)) return -ENODEV; - error = get_unused_fd(); + error = get_unused_fd_flags(flags); if (error < 0) return error; fd = error; diff --git a/fs/eventfd.c b/fs/eventfd.c index 343942deeec1..6094265ca409 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -214,7 +214,7 @@ asmlinkage long sys_eventfd(unsigned int count) * When we call this, the initialization must be complete, since * anon_inode_getfd() will install the fd. */ - fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx); + fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, 0); if (fd < 0) kfree(ctx); return fd; diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 990c01d2d66b..9392dd968125 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1068,7 +1068,7 @@ asmlinkage long sys_epoll_create(int size) * Creates all the items needed to setup an eventpoll file. That is, * a file structure and a free file descriptor. */ - fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep); + fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, 0); if (fd < 0) ep_free(ep); diff --git a/fs/signalfd.c b/fs/signalfd.c index 619725644c75..ddb328b74bde 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -227,7 +227,8 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas * When we call this, the initialization must be complete, since * anon_inode_getfd() will install the fd. */ - ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx); + ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, + 0); if (ufd < 0) kfree(ctx); } else { diff --git a/fs/timerfd.c b/fs/timerfd.c index d87d354ec424..77c2bc92cbee 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -198,7 +198,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) ctx->clockid = clockid; hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); - ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx); + ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 0); if (ufd < 0) kfree(ctx); diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index 6129e58ca7c9..e0a0cdc2da43 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -9,7 +9,7 @@ #define _LINUX_ANON_INODES_H int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv); + void *priv, int flags); #endif /* _LINUX_ANON_INODES_H */ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 904d7b7bd780..a845890b6800 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -902,7 +902,7 @@ static const struct file_operations kvm_vcpu_fops = { */ static int create_vcpu_fd(struct kvm_vcpu *vcpu) { - int fd = anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu); + int fd = anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu, 0); if (fd < 0) kvm_put_kvm(vcpu->kvm); return fd; @@ -1261,7 +1261,7 @@ static int kvm_dev_ioctl_create_vm(void) kvm = kvm_create_vm(); if (IS_ERR(kvm)) return PTR_ERR(kvm); - fd = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm); + fd = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, 0); if (fd < 0) kvm_put_kvm(kvm); -- cgit v1.2.3 From 9deb27baedb79759c3ab9435a7d8b841842d56e9 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:24 -0700 Subject: flag parameters: signalfd This patch adds the new signalfd4 syscall. It extends the old signalfd syscall by one parameter which is meant to hold a flag value. In this patch the only flag support is SFD_CLOEXEC which causes the close-on-exec flag for the returned file descriptor to be set. A new name SFD_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_signalfd4 # ifdef __x86_64__ # define __NR_signalfd4 289 # elif defined __i386__ # define __NR_signalfd4 327 # else # error "need __NR_signalfd4" # endif #endif #define SFD_CLOEXEC O_CLOEXEC int main (void) { sigset_t ss; sigemptyset (&ss); sigaddset (&ss, SIGUSR1); int fd = syscall (__NR_signalfd4, -1, &ss, 8, 0); if (fd == -1) { puts ("signalfd4(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("signalfd4(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_signalfd4, -1, &ss, 8, SFD_CLOEXEC); if (fd == -1) { puts ("signalfd4(SFD_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("signalfd4(SFD_CLOEXEC) does not set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/compat.c | 14 ++++++++++---- fs/signalfd.c | 14 ++++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/signalfd.h | 5 +++++ include/linux/syscalls.h | 1 + kernel/sys_ni.c | 1 + 9 files changed, 34 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 021d71bc69b5..c308128b9251 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -826,4 +826,5 @@ ia32_sys_call_table: .quad sys32_fallocate .quad compat_sys_timerfd_settime /* 325 */ .quad compat_sys_timerfd_gettime + .quad compat_sys_signalfd4 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index adff5562f5fd..c12a36c9fd51 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -326,3 +326,4 @@ ENTRY(sys_call_table) .long sys_fallocate .long sys_timerfd_settime /* 325 */ .long sys_timerfd_gettime + .long sys_signalfd4 diff --git a/fs/compat.c b/fs/compat.c index b46604281766..106eba28ec5a 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -2131,9 +2131,9 @@ asmlinkage long compat_sys_epoll_pwait(int epfd, #ifdef CONFIG_SIGNALFD -asmlinkage long compat_sys_signalfd(int ufd, - const compat_sigset_t __user *sigmask, - compat_size_t sigsetsize) +asmlinkage long compat_sys_signalfd4(int ufd, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize, int flags) { compat_sigset_t ss32; sigset_t tmp; @@ -2148,9 +2148,15 @@ asmlinkage long compat_sys_signalfd(int ufd, if (copy_to_user(ksigmask, &tmp, sizeof(sigset_t))) return -EFAULT; - return sys_signalfd(ufd, ksigmask, sizeof(sigset_t)); + return sys_signalfd4(ufd, ksigmask, sizeof(sigset_t), flags); } +asmlinkage long compat_sys_signalfd(int ufd, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize) +{ + return compat_sys_signalfd4(ufd, sigmask, sigsetsize, 0); +} #endif /* CONFIG_SIGNALFD */ #ifdef CONFIG_TIMERFD diff --git a/fs/signalfd.c b/fs/signalfd.c index ddb328b74bde..c8609fa51a13 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -205,11 +205,15 @@ static const struct file_operations signalfd_fops = { .read = signalfd_read, }; -asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemask) +asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, + size_t sizemask, int flags) { sigset_t sigmask; struct signalfd_ctx *ctx; + if (flags & ~SFD_CLOEXEC) + return -EINVAL; + if (sizemask != sizeof(sigset_t) || copy_from_user(&sigmask, user_mask, sizeof(sigmask))) return -EINVAL; @@ -228,7 +232,7 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas * anon_inode_getfd() will install the fd. */ ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, - 0); + flags & O_CLOEXEC); if (ufd < 0) kfree(ctx); } else { @@ -250,3 +254,9 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas return ufd; } + +asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, + size_t sizemask) +{ + return sys_signalfd4(ufd, user_mask, sizemask, 0); +} diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index 8317d94771d3..c310371f5613 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -332,6 +332,7 @@ #define __NR_fallocate 324 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 +#define __NR_signalfd4 327 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index e323994a370f..e0a9b45b2346 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -641,6 +641,8 @@ __SYSCALL(__NR_timerfd_settime, sys_timerfd_settime) __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) #define __NR_paccept 288 __SYSCALL(__NR_paccept, sys_paccept) +#define __NR_signalfd4 289 +__SYSCALL(__NR_signalfd4, sys_signalfd4) #ifndef __NO_STUBS diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h index ea037f28df91..8b3f7b7420a1 100644 --- a/include/linux/signalfd.h +++ b/include/linux/signalfd.h @@ -8,6 +8,11 @@ #ifndef _LINUX_SIGNALFD_H #define _LINUX_SIGNALFD_H +/* For O_CLOEXEC */ +#include + +/* Flags for signalfd4. */ +#define SFD_CLOEXEC O_CLOEXEC struct signalfd_siginfo { __u32 ssi_signo; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 2a2a40af6b2c..1c2707797845 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -610,6 +610,7 @@ asmlinkage long sys_set_robust_list(struct robust_list_head __user *head, size_t len); asmlinkage long sys_getcpu(unsigned __user *cpu, unsigned __user *node, struct getcpu_cache __user *cache); asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemask); +asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, size_t sizemask, int flags); asmlinkage long sys_timerfd_create(int clockid, int flags); asmlinkage long sys_timerfd_settime(int ufd, int flags, const struct itimerspec __user *utmr, diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 2f0b8a2e600f..8627c89ae9e8 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -156,6 +156,7 @@ cond_syscall(sys_ioprio_get); /* New file descriptors */ cond_syscall(sys_signalfd); +cond_syscall(sys_signalfd4); cond_syscall(compat_sys_signalfd); cond_syscall(sys_timerfd_create); cond_syscall(sys_timerfd_settime); -- cgit v1.2.3 From b087498eb5605673b0f260a7620d91818cd72304 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:25 -0700 Subject: flag parameters: eventfd This patch adds the new eventfd2 syscall. It extends the old eventfd syscall by one parameter which is meant to hold a flag value. In this patch the only flag support is EFD_CLOEXEC which causes the close-on-exec flag for the returned file descriptor to be set. A new name EFD_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_eventfd2 # ifdef __x86_64__ # define __NR_eventfd2 290 # elif defined __i386__ # define __NR_eventfd2 328 # else # error "need __NR_eventfd2" # endif #endif #define EFD_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_eventfd2, 1, 0); if (fd == -1) { puts ("eventfd2(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("eventfd2(0) sets close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_eventfd2, 1, EFD_CLOEXEC); if (fd == -1) { puts ("eventfd2(EFD_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("eventfd2(EFD_CLOEXEC) does not set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/eventfd.c | 13 +++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/eventfd.h | 6 ++++++ include/linux/syscalls.h | 1 + kernel/sys_ni.c | 1 + 8 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index c308128b9251..cf0eb31745ca 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -827,4 +827,5 @@ ia32_sys_call_table: .quad compat_sys_timerfd_settime /* 325 */ .quad compat_sys_timerfd_gettime .quad compat_sys_signalfd4 + .quad sys_eventfd2 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index c12a36c9fd51..cf112cb11c37 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -327,3 +327,4 @@ ENTRY(sys_call_table) .long sys_timerfd_settime /* 325 */ .long sys_timerfd_gettime .long sys_signalfd4 + .long sys_eventfd2 diff --git a/fs/eventfd.c b/fs/eventfd.c index 6094265ca409..bd420e6478ad 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -198,11 +198,14 @@ struct file *eventfd_fget(int fd) return file; } -asmlinkage long sys_eventfd(unsigned int count) +asmlinkage long sys_eventfd2(unsigned int count, int flags) { int fd; struct eventfd_ctx *ctx; + if (flags & ~EFD_CLOEXEC) + return -EINVAL; + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -214,9 +217,15 @@ asmlinkage long sys_eventfd(unsigned int count) * When we call this, the initialization must be complete, since * anon_inode_getfd() will install the fd. */ - fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, 0); + fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, + flags & O_CLOEXEC); if (fd < 0) kfree(ctx); return fd; } +asmlinkage long sys_eventfd(unsigned int count) +{ + return sys_eventfd2(count, 0); +} + diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index c310371f5613..edbd8723c939 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -333,6 +333,7 @@ #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327 +#define __NR_eventfd2 328 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index e0a9b45b2346..fb059a6feeb1 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -643,6 +643,8 @@ __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) __SYSCALL(__NR_paccept, sys_paccept) #define __NR_signalfd4 289 __SYSCALL(__NR_signalfd4, sys_signalfd4) +#define __NR_eventfd2 290 +__SYSCALL(__NR_eventfd2, sys_eventfd2) #ifndef __NO_STUBS diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h index a701399b7fed..a6c0eaedb1b0 100644 --- a/include/linux/eventfd.h +++ b/include/linux/eventfd.h @@ -10,6 +10,12 @@ #ifdef CONFIG_EVENTFD +/* For O_CLOEXEC */ +#include + +/* Flags for eventfd2. */ +#define EFD_CLOEXEC O_CLOEXEC + struct file *eventfd_fget(int fd); int eventfd_signal(struct file *file, int n); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1c2707797845..9ab09926a7f2 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -617,6 +617,7 @@ asmlinkage long sys_timerfd_settime(int ufd, int flags, struct itimerspec __user *otmr); asmlinkage long sys_timerfd_gettime(int ufd, struct itimerspec __user *otmr); asmlinkage long sys_eventfd(unsigned int count); +asmlinkage long sys_eventfd2(unsigned int count, int flags); asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); int kernel_execve(const char *filename, char *const argv[], char *const envp[]); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 8627c89ae9e8..2a361ccdc7ca 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -164,3 +164,4 @@ cond_syscall(sys_timerfd_gettime); cond_syscall(compat_sys_timerfd_settime); cond_syscall(compat_sys_timerfd_gettime); cond_syscall(sys_eventfd); +cond_syscall(sys_eventfd2); -- cgit v1.2.3 From 11fcb6c14676023d0bd437841f5dcd670e7990a0 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:26 -0700 Subject: flag parameters: timerfd_create The timerfd_create syscall already has a flags parameter. It just is unused so far. This patch changes this by introducing the TFD_CLOEXEC flag to set the close-on-exec flag for the returned file descriptor. A new name TFD_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_timerfd_create # ifdef __x86_64__ # define __NR_timerfd_create 283 # elif defined __i386__ # define __NR_timerfd_create 322 # else # error "need __NR_timerfd_create" # endif #endif #define TFD_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, 0); if (fd == -1) { puts ("timerfd_create(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("timerfd_create(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, TFD_CLOEXEC); if (fd == -1) { puts ("timerfd_create(TFD_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("timerfd_create(TFD_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/timerfd.c | 5 +++-- include/linux/timerfd.h | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/timerfd.c b/fs/timerfd.c index 77c2bc92cbee..c6ef5e33cb34 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -184,7 +184,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) int ufd; struct timerfd_ctx *ctx; - if (flags) + if (flags & ~TFD_CLOEXEC) return -EINVAL; if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME) @@ -198,7 +198,8 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) ctx->clockid = clockid; hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); - ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 0); + ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, + flags & O_CLOEXEC); if (ufd < 0) kfree(ctx); diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h index cf2b10d75731..96ed97dff00f 100644 --- a/include/linux/timerfd.h +++ b/include/linux/timerfd.h @@ -8,9 +8,14 @@ #ifndef _LINUX_TIMERFD_H #define _LINUX_TIMERFD_H +/* For O_CLOEXEC */ +#include +/* Flags for timerfd_settime. */ #define TFD_TIMER_ABSTIME (1 << 0) +/* Flags for timerfd_create. */ +#define TFD_CLOEXEC O_CLOEXEC #endif /* _LINUX_TIMERFD_H */ -- cgit v1.2.3 From a0998b50c3f0b8fdd265c63e0032f86ebe377dbf Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:27 -0700 Subject: flag parameters: epoll_create This patch adds the new epoll_create2 syscall. It extends the old epoll_create syscall by one parameter which is meant to hold a flag value. In this patch the only flag support is EPOLL_CLOEXEC which causes the close-on-exec flag for the returned file descriptor to be set. A new name EPOLL_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_epoll_create2 # ifdef __x86_64__ # define __NR_epoll_create2 291 # elif defined __i386__ # define __NR_epoll_create2 329 # else # error "need __NR_epoll_create2" # endif #endif #define EPOLL_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_epoll_create2, 1, 0); if (fd == -1) { puts ("epoll_create2(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("epoll_create2(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_epoll_create2, 1, EPOLL_CLOEXEC); if (fd == -1) { puts ("epoll_create2(EPOLL_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("epoll_create2(EPOLL_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/eventpoll.c | 13 +++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/eventpoll.h | 4 ++++ include/linux/syscalls.h | 1 + 7 files changed, 21 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index cf0eb31745ca..04366f08f424 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -828,4 +828,5 @@ ia32_sys_call_table: .quad compat_sys_timerfd_gettime .quad compat_sys_signalfd4 .quad sys_eventfd2 + .quad sys_epoll_create2 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index cf112cb11c37..4d7007ca263d 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -328,3 +328,4 @@ ENTRY(sys_call_table) .long sys_timerfd_gettime .long sys_signalfd4 .long sys_eventfd2 + .long sys_epoll_create2 diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 9392dd968125..3fd4014f3c5a 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1046,11 +1046,14 @@ retry: * RB tree. With the current implementation, the "size" parameter is ignored * (besides sanity checks). */ -asmlinkage long sys_epoll_create(int size) +asmlinkage long sys_epoll_create2(int size, int flags) { int error, fd = -1; struct eventpoll *ep; + if (flags & ~EPOLL_CLOEXEC) + return -EINVAL; + DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n", current, size)); @@ -1068,7 +1071,8 @@ asmlinkage long sys_epoll_create(int size) * Creates all the items needed to setup an eventpoll file. That is, * a file structure and a free file descriptor. */ - fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, 0); + fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, + flags & O_CLOEXEC); if (fd < 0) ep_free(ep); @@ -1079,6 +1083,11 @@ error_return: return fd; } +asmlinkage long sys_epoll_create(int size) +{ + return sys_epoll_create2(size, 0); +} + /* * The following function implements the controller interface for * the eventpoll file that enables the insertion/removal/change of diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index edbd8723c939..a37d6b0c4e1e 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -334,6 +334,7 @@ #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327 #define __NR_eventfd2 328 +#define __NR_epoll_create2 329 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index fb059a6feeb1..a1a4a5b6e5ee 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -645,6 +645,8 @@ __SYSCALL(__NR_paccept, sys_paccept) __SYSCALL(__NR_signalfd4, sys_signalfd4) #define __NR_eventfd2 290 __SYSCALL(__NR_eventfd2, sys_eventfd2) +#define __NR_epoll_create2 291 +__SYSCALL(__NR_epoll_create2, sys_epoll_create2) #ifndef __NO_STUBS diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index cf79853967ff..1cfaa40059c8 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -14,8 +14,12 @@ #ifndef _LINUX_EVENTPOLL_H #define _LINUX_EVENTPOLL_H +/* For O_CLOEXEC */ +#include #include +/* Flags for epoll_create2. */ +#define EPOLL_CLOEXEC O_CLOEXEC /* Valid opcodes to issue to sys_epoll_ctl() */ #define EPOLL_CTL_ADD 1 diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 9ab09926a7f2..85953240f28c 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -430,6 +430,7 @@ asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp); asmlinkage long sys_epoll_create(int size); +asmlinkage long sys_epoll_create2(int size, int flags); asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event); asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, -- cgit v1.2.3 From 336dd1f70ff62d7dd8655228caed4c5bfc818c56 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:29 -0700 Subject: flag parameters: dup2 This patch adds the new dup3 syscall. It extends the old dup2 syscall by one parameter which is meant to hold a flag value. Support for the O_CLOEXEC flag is added in this patch. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_dup3 # ifdef __x86_64__ # define __NR_dup3 292 # elif defined __i386__ # define __NR_dup3 330 # else # error "need __NR_dup3" # endif #endif int main (void) { int fd = syscall (__NR_dup3, 1, 4, 0); if (fd == -1) { puts ("dup3(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("dup3(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_dup3, 1, 4, O_CLOEXEC); if (fd == -1) { puts ("dup3(O_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("dup3(O_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/fcntl.c | 15 +++++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/syscalls.h | 1 + 6 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 04366f08f424..5614a8f7bed4 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -829,4 +829,5 @@ ia32_sys_call_table: .quad compat_sys_signalfd4 .quad sys_eventfd2 .quad sys_epoll_create2 + .quad sys_dup3 /* 330 */ ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 4d7007ca263d..24a3f1ea6a0e 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -329,3 +329,4 @@ ENTRY(sys_call_table) .long sys_signalfd4 .long sys_eventfd2 .long sys_epoll_create2 + .long sys_dup3 /* 330 */ diff --git a/fs/fcntl.c b/fs/fcntl.c index 330a7d782591..9679fcbdeaa0 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -125,13 +125,16 @@ static int dupfd(struct file *file, unsigned int start, int cloexec) return fd; } -asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) +asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) { int err = -EBADF; struct file * file, *tofree; struct files_struct * files = current->files; struct fdtable *fdt; + if ((flags & ~O_CLOEXEC) != 0) + return -EINVAL; + spin_lock(&files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; @@ -163,7 +166,10 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) rcu_assign_pointer(fdt->fd[newfd], file); FD_SET(newfd, fdt->open_fds); - FD_CLR(newfd, fdt->close_on_exec); + if (flags & O_CLOEXEC) + FD_SET(newfd, fdt->close_on_exec); + else + FD_CLR(newfd, fdt->close_on_exec); spin_unlock(&files->file_lock); if (tofree) @@ -181,6 +187,11 @@ out_fput: goto out; } +asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + return sys_dup3(oldfd, newfd, 0); +} + asmlinkage long sys_dup(unsigned int fildes) { int ret = -EBADF; diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index a37d6b0c4e1e..a1f6383bf695 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -335,6 +335,7 @@ #define __NR_signalfd4 327 #define __NR_eventfd2 328 #define __NR_epoll_create2 329 +#define __NR_dup3 330 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index a1a4a5b6e5ee..f0fb2bd40cdb 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -647,6 +647,8 @@ __SYSCALL(__NR_signalfd4, sys_signalfd4) __SYSCALL(__NR_eventfd2, sys_eventfd2) #define __NR_epoll_create2 291 __SYSCALL(__NR_epoll_create2, sys_epoll_create2) +#define __NR_dup3 292 +__SYSCALL(__NR_dup3, sys_dup3) #ifndef __NO_STUBS diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 85953240f28c..034d3358549e 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -305,6 +305,7 @@ asmlinkage long sys_fcntl64(unsigned int fd, #endif asmlinkage long sys_dup(unsigned int fildes); asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd); +asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags); asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); -- cgit v1.2.3 From ed8cae8ba01348bfd83333f4648dd807b04d7f08 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:30 -0700 Subject: flag parameters: pipe This patch introduces the new syscall pipe2 which is like pipe but it also takes an additional parameter which takes a flag value. This patch implements the handling of O_CLOEXEC for the flag. I did not add support for the new syscall for the architectures which have a special sys_pipe implementation. I think the maintainers of those archs have the chance to go with the unified implementation but that's up to them. The implementation introduces do_pipe_flags. I did that instead of changing all callers of do_pipe because some of the callers are written in assembler. I would probably screw up changing the assembly code. To avoid breaking code do_pipe is now a small wrapper around do_pipe_flags. Once all callers are changed over to do_pipe_flags the old do_pipe function can be removed. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_pipe2 # ifdef __x86_64__ # define __NR_pipe2 293 # elif defined __i386__ # define __NR_pipe2 331 # else # error "need __NR_pipe2" # endif #endif int main (void) { int fd[2]; if (syscall (__NR_pipe2, fd, 0) != 0) { puts ("pipe2(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { int coe = fcntl (fd[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { printf ("pipe2(0) set close-on-exit for fd[%d]\n", i); return 1; } } close (fd[0]); close (fd[1]); if (syscall (__NR_pipe2, fd, O_CLOEXEC) != 0) { puts ("pipe2(O_CLOEXEC) failed"); return 1; } for (int i = 0; i < 2; ++i) { int coe = fcntl (fd[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { printf ("pipe2(O_CLOEXEC) does not set close-on-exit for fd[%d]\n", i); return 1; } } close (fd[0]); close (fd[1]); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/ia32/sys_ia32.c | 2 +- arch/ia64/kernel/sys_ia64.c | 2 +- arch/mips/kernel/syscall.c | 2 +- arch/parisc/hpux/sys_hpux.c | 2 +- arch/sh/kernel/sys_sh32.c | 2 +- arch/sparc/kernel/sys_sparc.c | 2 +- arch/sparc64/kernel/sys_sparc.c | 2 +- arch/x86/ia32/ia32entry.S | 1 + arch/x86/ia32/sys_ia32.c | 2 +- arch/x86/kernel/syscall_table_32.S | 1 + arch/xtensa/kernel/syscall.c | 2 +- fs/pipe.c | 23 ++++++++++++++++++----- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/fs.h | 1 + 15 files changed, 33 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 7e028ceb93ba..465116aecb85 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -1139,7 +1139,7 @@ sys32_pipe (int __user *fd) int retval; int fds[2]; - retval = do_pipe(fds); + retval = do_pipe_flags(fds, 0); if (retval) goto out; if (copy_to_user(fd, fds, sizeof(fds))) diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c index 1eda194b9559..bcbb6d8792d3 100644 --- a/arch/ia64/kernel/sys_ia64.c +++ b/arch/ia64/kernel/sys_ia64.c @@ -160,7 +160,7 @@ sys_pipe (void) int fd[2]; int retval; - retval = do_pipe(fd); + retval = do_pipe_flags(fd, 0); if (retval) goto out; retval = fd[0]; diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 3523c8d12eda..343015a2f418 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -52,7 +52,7 @@ asmlinkage int sysm_pipe(nabi_no_regargs volatile struct pt_regs regs) int fd[2]; int error, res; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (error) { res = error; goto out; diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c index 0c5b9dabb475..be255ebb609c 100644 --- a/arch/parisc/hpux/sys_hpux.c +++ b/arch/parisc/hpux/sys_hpux.c @@ -448,7 +448,7 @@ int hpux_pipe(int *kstack_fildes) int error; lock_kernel(); - error = do_pipe(kstack_fildes); + error = do_pipe_flags(kstack_fildes, 0); unlock_kernel(); return error; } diff --git a/arch/sh/kernel/sys_sh32.c b/arch/sh/kernel/sys_sh32.c index 125e493ead82..f0aa5c398656 100644 --- a/arch/sh/kernel/sys_sh32.c +++ b/arch/sh/kernel/sys_sh32.c @@ -29,7 +29,7 @@ asmlinkage int sys_pipe(unsigned long r4, unsigned long r5, int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (!error) { regs->regs[1] = fd[1]; return fd[0]; diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 3c6b49a53ae8..4d73421559c3 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -97,7 +97,7 @@ asmlinkage int sparc_pipe(struct pt_regs *regs) int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (error) goto out; regs->u_regs[UREG_I1] = fd[1]; diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index e1f4eba2e576..39749e32dc7e 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -418,7 +418,7 @@ asmlinkage long sparc_pipe(struct pt_regs *regs) int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (error) goto out; regs->u_regs[UREG_I1] = fd[1]; diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 5614a8f7bed4..18808b164570 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -830,4 +830,5 @@ ia32_sys_call_table: .quad sys_eventfd2 .quad sys_epoll_create2 .quad sys_dup3 /* 330 */ + .quad sys_pipe2 ia32_syscall_end: diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c index f00afdf61e67..d3c64088b981 100644 --- a/arch/x86/ia32/sys_ia32.c +++ b/arch/x86/ia32/sys_ia32.c @@ -238,7 +238,7 @@ asmlinkage long sys32_pipe(int __user *fd) int retval; int fds[2]; - retval = do_pipe(fds); + retval = do_pipe_flags(fds, 0); if (retval) goto out; if (copy_to_user(fd, fds, sizeof(fds))) diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 24a3f1ea6a0e..66154769d52f 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -330,3 +330,4 @@ ENTRY(sys_call_table) .long sys_eventfd2 .long sys_epoll_create2 .long sys_dup3 /* 330 */ + .long sys_pipe2 diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c index f3e16efcd47a..ac15ecbdf919 100644 --- a/arch/xtensa/kernel/syscall.c +++ b/arch/xtensa/kernel/syscall.c @@ -49,7 +49,7 @@ asmlinkage long xtensa_pipe(int __user *userfds) int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (!error) { if (copy_to_user(userfds, fd, 2 * sizeof(int))) error = -EFAULT; diff --git a/fs/pipe.c b/fs/pipe.c index 700f4e0d9572..68e82061070c 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1027,12 +1027,15 @@ struct file *create_read_pipe(struct file *wrf) return f; } -int do_pipe(int *fd) +int do_pipe_flags(int *fd, int flags) { struct file *fw, *fr; int error; int fdw, fdr; + if (flags & ~O_CLOEXEC) + return -EINVAL; + fw = create_write_pipe(); if (IS_ERR(fw)) return PTR_ERR(fw); @@ -1041,12 +1044,12 @@ int do_pipe(int *fd) if (IS_ERR(fr)) goto err_write_pipe; - error = get_unused_fd(); + error = get_unused_fd_flags(flags); if (error < 0) goto err_read_pipe; fdr = error; - error = get_unused_fd(); + error = get_unused_fd_flags(flags); if (error < 0) goto err_fdr; fdw = error; @@ -1074,16 +1077,21 @@ int do_pipe(int *fd) return error; } +int do_pipe(int *fd) +{ + return do_pipe_flags(fd, 0); +} + /* * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way Unix traditionally does this, though. */ -asmlinkage long __weak sys_pipe(int __user *fildes) +asmlinkage long __weak sys_pipe2(int __user *fildes, int flags) { int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, flags); if (!error) { if (copy_to_user(fildes, fd, sizeof(fd))) { sys_close(fd[0]); @@ -1094,6 +1102,11 @@ asmlinkage long __weak sys_pipe(int __user *fildes) return error; } +asmlinkage long __weak sys_pipe(int __user *fildes) +{ + return sys_pipe2(fildes, 0); +} + /* * pipefs should _never_ be mounted by userland - too much of security hassle, * no real gain from having the whole whorehouse mounted. So we don't need diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index a1f6383bf695..748a05c77da4 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -336,6 +336,7 @@ #define __NR_eventfd2 328 #define __NR_epoll_create2 329 #define __NR_dup3 330 +#define __NR_pipe2 331 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index f0fb2bd40cdb..d2284b43ad58 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -649,6 +649,8 @@ __SYSCALL(__NR_eventfd2, sys_eventfd2) __SYSCALL(__NR_epoll_create2, sys_epoll_create2) #define __NR_dup3 292 __SYSCALL(__NR_dup3, sys_dup3) +#define __NR_pipe2 293 +__SYSCALL(__NR_pipe2, sys_pipe2) #ifndef __NO_STUBS diff --git a/include/linux/fs.h b/include/linux/fs.h index e5e6a244096c..0e80cd717d32 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1777,6 +1777,7 @@ static inline void allow_write_access(struct file *file) atomic_inc(&file->f_path.dentry->d_inode->i_writecount); } extern int do_pipe(int *); +extern int do_pipe_flags(int *, int); extern struct file *create_read_pipe(struct file *f); extern struct file *create_write_pipe(void); extern void free_write_pipe(struct file *); -- cgit v1.2.3 From 4006553b06306b34054529477b06b68a1c66249b Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:32 -0700 Subject: flag parameters: inotify_init This patch introduces the new syscall inotify_init1 (note: the 1 stands for the one parameter the syscall takes, as opposed to no parameter before). The values accepted for this parameter are function-specific and defined in the inotify.h header. Here the values must match the O_* flags, though. In this patch CLOEXEC support is introduced. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_inotify_init1 # ifdef __x86_64__ # define __NR_inotify_init1 294 # elif defined __i386__ # define __NR_inotify_init1 332 # else # error "need __NR_inotify_init1" # endif #endif #define IN_CLOEXEC O_CLOEXEC int main (void) { int fd; fd = syscall (__NR_inotify_init1, 0); if (fd == -1) { puts ("inotify_init1(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("inotify_init1(0) set close-on-exit"); return 1; } close (fd); fd = syscall (__NR_inotify_init1, IN_CLOEXEC); if (fd == -1) { puts ("inotify_init1(IN_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("inotify_init1(O_CLOEXEC) does not set close-on-exit"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/inotify_user.c | 12 ++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/inotify.h | 5 +++++ include/linux/syscalls.h | 1 + kernel/sys_ni.c | 1 + 8 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 18808b164570..4541073dd837 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -831,4 +831,5 @@ ia32_sys_call_table: .quad sys_epoll_create2 .quad sys_dup3 /* 330 */ .quad sys_pipe2 + .quad sys_inotify_init1 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 66154769d52f..f59aba5ff0f0 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -331,3 +331,4 @@ ENTRY(sys_call_table) .long sys_epoll_create2 .long sys_dup3 /* 330 */ .long sys_pipe2 + .long sys_inotify_init1 diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 6676c06bb7c1..851005998cd4 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -566,7 +566,7 @@ static const struct inotify_operations inotify_user_ops = { .destroy_watch = free_inotify_user_watch, }; -asmlinkage long sys_inotify_init(void) +asmlinkage long sys_inotify_init1(int flags) { struct inotify_device *dev; struct inotify_handle *ih; @@ -574,7 +574,10 @@ asmlinkage long sys_inotify_init(void) struct file *filp; int fd, ret; - fd = get_unused_fd(); + if (flags & ~IN_CLOEXEC) + return -EINVAL; + + fd = get_unused_fd_flags(flags & O_CLOEXEC); if (fd < 0) return fd; @@ -638,6 +641,11 @@ out_put_fd: return ret; } +asmlinkage long sys_inotify_init(void) +{ + return sys_inotify_init1(0); +} + asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) { struct inode *inode; diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index 748a05c77da4..b3daf503ab93 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -337,6 +337,7 @@ #define __NR_epoll_create2 329 #define __NR_dup3 330 #define __NR_pipe2 331 +#define __NR_inotify_init1 332 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index d2284b43ad58..c8cb88d70c6b 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -651,6 +651,8 @@ __SYSCALL(__NR_epoll_create2, sys_epoll_create2) __SYSCALL(__NR_dup3, sys_dup3) #define __NR_pipe2 293 __SYSCALL(__NR_pipe2, sys_pipe2) +#define __NR_inotify_init1 294 +__SYSCALL(__NR_inotify_init1, sys_inotify_init1) #ifndef __NO_STUBS diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 742b917e7d1b..72ef82120512 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -7,6 +7,8 @@ #ifndef _LINUX_INOTIFY_H #define _LINUX_INOTIFY_H +/* For O_CLOEXEC */ +#include #include /* @@ -63,6 +65,9 @@ struct inotify_event { IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \ IN_MOVE_SELF) +/* Flags for sys_inotify_init1. */ +#define IN_CLOEXEC O_CLOEXEC + #ifdef __KERNEL__ #include diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 034d3358549e..93a7e7f017a6 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -547,6 +547,7 @@ asmlinkage long sys_get_mempolicy(int __user *policy, unsigned long addr, unsigned long flags); asmlinkage long sys_inotify_init(void); +asmlinkage long sys_inotify_init1(int flags); asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask); asmlinkage long sys_inotify_rm_watch(int fd, u32 wd); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 2a361ccdc7ca..bd66ac5406f3 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -96,6 +96,7 @@ cond_syscall(sys_keyctl); cond_syscall(compat_sys_keyctl); cond_syscall(compat_sys_socketcall); cond_syscall(sys_inotify_init); +cond_syscall(sys_inotify_init1); cond_syscall(sys_inotify_add_watch); cond_syscall(sys_inotify_rm_watch); cond_syscall(sys_migrate_pages); -- cgit v1.2.3 From 77d2720059618b9b6e827a8b73831eb6c6fad63c Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:35 -0700 Subject: flag parameters: NONBLOCK in socket and socketpair This patch introduces support for the SOCK_NONBLOCK flag in socket, socketpair, and paccept. To do this the internal function sock_attach_fd gets an additional parameter which it uses to set the appropriate flag for the file descriptor. Given that in modern, scalable programs almost all socket connections are non-blocking and the minimal additional cost for the new functionality I see no reason not to add this code. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #include #include #ifndef __NR_paccept # ifdef __x86_64__ # define __NR_paccept 288 # elif defined __i386__ # define SYS_PACCEPT 18 # define USE_SOCKETCALL 1 # else # error "need __NR_paccept" # endif #endif #ifdef USE_SOCKETCALL # define paccept(fd, addr, addrlen, mask, flags) \ ({ long args[6] = { \ (long) fd, (long) addr, (long) addrlen, (long) mask, 8, (long) flags }; \ syscall (__NR_socketcall, SYS_PACCEPT, args); }) #else # define paccept(fd, addr, addrlen, mask, flags) \ syscall (__NR_paccept, fd, addr, addrlen, mask, 8, flags) #endif #define PORT 57392 #define SOCK_NONBLOCK O_NONBLOCK static pthread_barrier_t b; static void * tf (void *arg) { pthread_barrier_wait (&b); int s = socket (AF_INET, SOCK_STREAM, 0); struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); pthread_barrier_wait (&b); s = socket (AF_INET, SOCK_STREAM, 0); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); return NULL; } int main (void) { int fd; fd = socket (PF_INET, SOCK_STREAM, 0); if (fd == -1) { puts ("socket(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("socket(0) set non-blocking mode"); return 1; } close (fd); fd = socket (PF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0); if (fd == -1) { puts ("socket(SOCK_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("socket(SOCK_NONBLOCK) does not set non-blocking mode"); return 1; } close (fd); int fds[2]; if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1) { puts ("socketpair(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { printf ("socketpair(0) set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } if (socketpair (PF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0, fds) == -1) { puts ("socketpair(SOCK_NONBLOCK) failed"); return 1; } for (int i = 0; i < 2; ++i) { fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { printf ("socketpair(SOCK_NONBLOCK) does not set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } pthread_barrier_init (&b, NULL, 2); struct sockaddr_in sin; pthread_t th; if (pthread_create (&th, NULL, tf, NULL) != 0) { puts ("pthread_create failed"); return 1; } int s = socket (AF_INET, SOCK_STREAM, 0); int reuse = 1; setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); bind (s, (struct sockaddr *) &sin, sizeof (sin)); listen (s, SOMAXCONN); pthread_barrier_wait (&b); int s2 = paccept (s, NULL, 0, NULL, 0); if (s2 < 0) { puts ("paccept(0) failed"); return 1; } fl = fcntl (s2, F_GETFL); if (fl & O_NONBLOCK) { puts ("paccept(0) set non-blocking mode"); return 1; } close (s2); close (s); pthread_barrier_wait (&b); s = socket (AF_INET, SOCK_STREAM, 0); sin.sin_port = htons (PORT); setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); bind (s, (struct sockaddr *) &sin, sizeof (sin)); listen (s, SOMAXCONN); pthread_barrier_wait (&b); s2 = paccept (s, NULL, 0, NULL, SOCK_NONBLOCK); if (s2 < 0) { puts ("paccept(SOCK_NONBLOCK) failed"); return 1; } fl = fcntl (s2, F_GETFL); if ((fl & O_NONBLOCK) == 0) { puts ("paccept(SOCK_NONBLOCK) does not set non-blocking mode"); return 1; } close (s2); close (s); pthread_barrier_wait (&b); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/net.h | 2 +- net/socket.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 39a23af059b4..2f999fbb188d 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -20,7 +20,7 @@ #include #include -#include /* For O_CLOEXEC */ +#include /* For O_CLOEXEC and O_NONBLOCK */ #include struct poll_table_struct; diff --git a/net/socket.c b/net/socket.c index d163adff95bf..31105f9048a8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -369,7 +369,7 @@ static int sock_alloc_fd(struct file **filep, int flags) return fd; } -static int sock_attach_fd(struct socket *sock, struct file *file) +static int sock_attach_fd(struct socket *sock, struct file *file, int flags) { struct dentry *dentry; struct qstr name = { .name = "" }; @@ -391,7 +391,7 @@ static int sock_attach_fd(struct socket *sock, struct file *file) init_file(file, sock_mnt, dentry, FMODE_READ | FMODE_WRITE, &socket_file_ops); SOCK_INODE(sock)->i_fop = &socket_file_ops; - file->f_flags = O_RDWR; + file->f_flags = O_RDWR | (flags & O_NONBLOCK); file->f_pos = 0; file->private_data = sock; @@ -404,7 +404,7 @@ int sock_map_fd(struct socket *sock, int flags) int fd = sock_alloc_fd(&newfile, flags); if (likely(fd >= 0)) { - int err = sock_attach_fd(sock, newfile); + int err = sock_attach_fd(sock, newfile, flags); if (unlikely(err < 0)) { put_filp(newfile); @@ -1223,7 +1223,7 @@ asmlinkage long sys_socket(int family, int type, int protocol) int flags; flags = type & ~SOCK_TYPE_MASK; - if (flags & ~SOCK_CLOEXEC) + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; @@ -1234,7 +1234,7 @@ asmlinkage long sys_socket(int family, int type, int protocol) if (retval < 0) goto out; - retval = sock_map_fd(sock, flags & O_CLOEXEC); + retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); if (retval < 0) goto out_release; @@ -1260,7 +1260,7 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, int flags; flags = type & ~SOCK_TYPE_MASK; - if (flags & ~SOCK_CLOEXEC) + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; @@ -1298,12 +1298,12 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, goto out_release_both; } - err = sock_attach_fd(sock1, newfile1); + err = sock_attach_fd(sock1, newfile1, flags & O_NONBLOCK); if (unlikely(err < 0)) { goto out_fd2; } - err = sock_attach_fd(sock2, newfile2); + err = sock_attach_fd(sock2, newfile2, flags & O_NONBLOCK); if (unlikely(err < 0)) { fput(newfile1); goto out_fd1; @@ -1429,7 +1429,7 @@ long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, int err, len, newfd, fput_needed; struct sockaddr_storage address; - if (flags & ~SOCK_CLOEXEC) + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) @@ -1459,7 +1459,7 @@ long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, goto out_put; } - err = sock_attach_fd(newsock, newfile); + err = sock_attach_fd(newsock, newfile, flags & O_NONBLOCK); if (err < 0) goto out_fd_simple; -- cgit v1.2.3 From 5fb5e04926a54bc1c22bba7ca166840f4476196f Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:37 -0700 Subject: flag parameters: NONBLOCK in signalfd This patch adds support for the SFD_NONBLOCK flag to signalfd4. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_signalfd4 # ifdef __x86_64__ # define __NR_signalfd4 289 # elif defined __i386__ # define __NR_signalfd4 327 # else # error "need __NR_signalfd4" # endif #endif #define SFD_NONBLOCK O_NONBLOCK int main (void) { sigset_t ss; sigemptyset (&ss); sigaddset (&ss, SIGUSR1); int fd = syscall (__NR_signalfd4, -1, &ss, 8, 0); if (fd == -1) { puts ("signalfd4(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("signalfd4(0) set non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_signalfd4, -1, &ss, 8, SFD_NONBLOCK); if (fd == -1) { puts ("signalfd4(SFD_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("signalfd4(SFD_NONBLOCK) does not set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/signalfd.c | 4 ++-- include/linux/signalfd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/signalfd.c b/fs/signalfd.c index c8609fa51a13..5441a4bca772 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -211,7 +211,7 @@ asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, sigset_t sigmask; struct signalfd_ctx *ctx; - if (flags & ~SFD_CLOEXEC) + if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK)) return -EINVAL; if (sizemask != sizeof(sigset_t) || @@ -232,7 +232,7 @@ asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, * anon_inode_getfd() will install the fd. */ ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, - flags & O_CLOEXEC); + flags & (O_CLOEXEC | O_NONBLOCK)); if (ufd < 0) kfree(ctx); } else { diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h index 8b3f7b7420a1..bef0c46d4713 100644 --- a/include/linux/signalfd.h +++ b/include/linux/signalfd.h @@ -8,11 +8,12 @@ #ifndef _LINUX_SIGNALFD_H #define _LINUX_SIGNALFD_H -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include /* Flags for signalfd4. */ #define SFD_CLOEXEC O_CLOEXEC +#define SFD_NONBLOCK O_NONBLOCK struct signalfd_siginfo { __u32 ssi_signo; -- cgit v1.2.3 From e7d476dfdf0bcfed478a207aecfdc84f81efecaf Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:38 -0700 Subject: flag parameters: NONBLOCK in eventfd This patch adds support for the EFD_NONBLOCK flag to eventfd2. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_eventfd2 # ifdef __x86_64__ # define __NR_eventfd2 290 # elif defined __i386__ # define __NR_eventfd2 328 # else # error "need __NR_eventfd2" # endif #endif #define EFD_NONBLOCK O_NONBLOCK int main (void) { int fd = syscall (__NR_eventfd2, 1, 0); if (fd == -1) { puts ("eventfd2(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("eventfd2(0) sets non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_eventfd2, 1, EFD_NONBLOCK); if (fd == -1) { puts ("eventfd2(EFD_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("eventfd2(EFD_NONBLOCK) does not set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/eventfd.c | 4 ++-- include/linux/eventfd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/eventfd.c b/fs/eventfd.c index bd420e6478ad..3ed4466177a7 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -203,7 +203,7 @@ asmlinkage long sys_eventfd2(unsigned int count, int flags) int fd; struct eventfd_ctx *ctx; - if (flags & ~EFD_CLOEXEC) + if (flags & ~(EFD_CLOEXEC | EFD_NONBLOCK)) return -EINVAL; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); @@ -218,7 +218,7 @@ asmlinkage long sys_eventfd2(unsigned int count, int flags) * anon_inode_getfd() will install the fd. */ fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, - flags & O_CLOEXEC); + flags & (O_CLOEXEC | O_NONBLOCK)); if (fd < 0) kfree(ctx); return fd; diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h index a6c0eaedb1b0..a667637b54e3 100644 --- a/include/linux/eventfd.h +++ b/include/linux/eventfd.h @@ -10,11 +10,12 @@ #ifdef CONFIG_EVENTFD -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include /* Flags for eventfd2. */ #define EFD_CLOEXEC O_CLOEXEC +#define EFD_NONBLOCK O_NONBLOCK struct file *eventfd_fget(int fd); int eventfd_signal(struct file *file, int n); -- cgit v1.2.3 From 6b1ef0e60d42f2fdaec26baee8327eb156347b4f Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:39 -0700 Subject: flag parameters: NONBLOCK in timerfd_create This patch adds support for the TFD_NONBLOCK flag to timerfd_create. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_timerfd_create # ifdef __x86_64__ # define __NR_timerfd_create 283 # elif defined __i386__ # define __NR_timerfd_create 322 # else # error "need __NR_timerfd_create" # endif #endif #define TFD_NONBLOCK O_NONBLOCK int main (void) { int fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, 0); if (fd == -1) { puts ("timerfd_create(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("timerfd_create(0) set non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, TFD_NONBLOCK); if (fd == -1) { puts ("timerfd_create(TFD_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("timerfd_create(TFD_NONBLOCK) set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/timerfd.c | 4 ++-- include/linux/timerfd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/timerfd.c b/fs/timerfd.c index c6ef5e33cb34..75d44efe346c 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -184,7 +184,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) int ufd; struct timerfd_ctx *ctx; - if (flags & ~TFD_CLOEXEC) + if (flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) return -EINVAL; if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME) @@ -199,7 +199,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, - flags & O_CLOEXEC); + flags & (O_CLOEXEC | O_NONBLOCK)); if (ufd < 0) kfree(ctx); diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h index 96ed97dff00f..86cb0501d3e2 100644 --- a/include/linux/timerfd.h +++ b/include/linux/timerfd.h @@ -8,7 +8,7 @@ #ifndef _LINUX_TIMERFD_H #define _LINUX_TIMERFD_H -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include /* Flags for timerfd_settime. */ @@ -16,6 +16,7 @@ /* Flags for timerfd_create. */ #define TFD_CLOEXEC O_CLOEXEC +#define TFD_NONBLOCK O_NONBLOCK #endif /* _LINUX_TIMERFD_H */ -- cgit v1.2.3 From be61a86d7237dd80510615f38ae21d6e1e98660c Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:40 -0700 Subject: flag parameters: NONBLOCK in pipe This patch adds O_NONBLOCK support to pipe2. It is minimally more involved than the patches for eventfd et.al but still trivial. The interfaces of the create_write_pipe and create_read_pipe helper functions were changed and the one other caller as well. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_pipe2 # ifdef __x86_64__ # define __NR_pipe2 293 # elif defined __i386__ # define __NR_pipe2 331 # else # error "need __NR_pipe2" # endif #endif int main (void) { int fds[2]; if (syscall (__NR_pipe2, fds, 0) == -1) { puts ("pipe2(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { int fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { printf ("pipe2(0) set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } if (syscall (__NR_pipe2, fds, O_NONBLOCK) == -1) { puts ("pipe2(O_NONBLOCK) failed"); return 1; } for (int i = 0; i < 2; ++i) { int fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { printf ("pipe2(O_NONBLOCK) does not set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/pipe.c | 14 +++++++------- include/linux/fs.h | 4 ++-- kernel/kmod.c | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index 68e82061070c..10c4e9aa5c49 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -950,7 +950,7 @@ fail_inode: return NULL; } -struct file *create_write_pipe(void) +struct file *create_write_pipe(int flags) { int err; struct inode *inode; @@ -983,7 +983,7 @@ struct file *create_write_pipe(void) goto err_dentry; f->f_mapping = inode->i_mapping; - f->f_flags = O_WRONLY; + f->f_flags = O_WRONLY | (flags & O_NONBLOCK); f->f_version = 0; return f; @@ -1007,7 +1007,7 @@ void free_write_pipe(struct file *f) put_filp(f); } -struct file *create_read_pipe(struct file *wrf) +struct file *create_read_pipe(struct file *wrf, int flags) { struct file *f = get_empty_filp(); if (!f) @@ -1019,7 +1019,7 @@ struct file *create_read_pipe(struct file *wrf) f->f_mapping = wrf->f_path.dentry->d_inode->i_mapping; f->f_pos = 0; - f->f_flags = O_RDONLY; + f->f_flags = O_RDONLY | (flags & O_NONBLOCK); f->f_op = &read_pipe_fops; f->f_mode = FMODE_READ; f->f_version = 0; @@ -1033,13 +1033,13 @@ int do_pipe_flags(int *fd, int flags) int error; int fdw, fdr; - if (flags & ~O_CLOEXEC) + if (flags & ~(O_CLOEXEC | O_NONBLOCK)) return -EINVAL; - fw = create_write_pipe(); + fw = create_write_pipe(flags); if (IS_ERR(fw)) return PTR_ERR(fw); - fr = create_read_pipe(fw); + fr = create_read_pipe(fw, flags); error = PTR_ERR(fr); if (IS_ERR(fr)) goto err_write_pipe; diff --git a/include/linux/fs.h b/include/linux/fs.h index 0e80cd717d32..4b86f806014c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1778,8 +1778,8 @@ static inline void allow_write_access(struct file *file) } extern int do_pipe(int *); extern int do_pipe_flags(int *, int); -extern struct file *create_read_pipe(struct file *f); -extern struct file *create_write_pipe(void); +extern struct file *create_read_pipe(struct file *f, int flags); +extern struct file *create_write_pipe(int flags); extern void free_write_pipe(struct file *); extern struct file *do_filp_open(int dfd, const char *pathname, diff --git a/kernel/kmod.c b/kernel/kmod.c index 90d7af1c1655..2989f67c4446 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -417,12 +417,12 @@ int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, { struct file *f; - f = create_write_pipe(); + f = create_write_pipe(0); if (IS_ERR(f)) return PTR_ERR(f); *filp = f; - f = create_read_pipe(f); + f = create_read_pipe(f, 0); if (IS_ERR(f)) { free_write_pipe(*filp); return PTR_ERR(f); -- cgit v1.2.3 From 510df2dd482496083e1c3b1a8c9b6afd5fa4c7d7 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:41 -0700 Subject: flag parameters: NONBLOCK in inotify_init This patch adds non-blocking support for inotify_init1. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_inotify_init1 # ifdef __x86_64__ # define __NR_inotify_init1 294 # elif defined __i386__ # define __NR_inotify_init1 332 # else # error "need __NR_inotify_init1" # endif #endif #define IN_NONBLOCK O_NONBLOCK int main (void) { int fd = syscall (__NR_inotify_init1, 0); if (fd == -1) { puts ("inotify_init1(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("inotify_init1(0) set non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_inotify_init1, IN_NONBLOCK); if (fd == -1) { puts ("inotify_init1(IN_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("inotify_init1(IN_NONBLOCK) set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/inotify_user.c | 4 ++-- include/linux/inotify.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 851005998cd4..dc7e1f619748 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -574,7 +574,7 @@ asmlinkage long sys_inotify_init1(int flags) struct file *filp; int fd, ret; - if (flags & ~IN_CLOEXEC) + if (flags & ~(IN_CLOEXEC | IN_NONBLOCK)) return -EINVAL; fd = get_unused_fd_flags(flags & O_CLOEXEC); @@ -613,7 +613,7 @@ asmlinkage long sys_inotify_init1(int flags) filp->f_path.dentry = dget(inotify_mnt->mnt_root); filp->f_mapping = filp->f_path.dentry->d_inode->i_mapping; filp->f_mode = FMODE_READ; - filp->f_flags = O_RDONLY; + filp->f_flags = O_RDONLY | (flags & O_NONBLOCK); filp->private_data = dev; INIT_LIST_HEAD(&dev->events); diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 72ef82120512..bd578578a8b9 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -7,7 +7,7 @@ #ifndef _LINUX_INOTIFY_H #define _LINUX_INOTIFY_H -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include #include @@ -67,6 +67,7 @@ struct inotify_event { /* Flags for sys_inotify_init1. */ #define IN_CLOEXEC O_CLOEXEC +#define IN_NONBLOCK O_NONBLOCK #ifdef __KERNEL__ -- cgit v1.2.3 From 9fe5ad9c8cef9ad5873d8ee55d1cf00d9b607df0 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:43 -0700 Subject: flag parameters add-on: remove epoll_create size param Remove the size parameter from the new epoll_create syscall and renames the syscall itself. The updated test program follows. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_epoll_create2 # ifdef __x86_64__ # define __NR_epoll_create2 291 # elif defined __i386__ # define __NR_epoll_create2 329 # else # error "need __NR_epoll_create2" # endif #endif #define EPOLL_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_epoll_create2, 0); if (fd == -1) { puts ("epoll_create2(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("epoll_create2(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_epoll_create2, EPOLL_CLOEXEC); if (fd == -1) { puts ("epoll_create2(EPOLL_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("epoll_create2(EPOLL_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 2 +- arch/x86/kernel/syscall_table_32.S | 2 +- fs/eventpoll.c | 18 ++++++++++-------- include/asm-x86/unistd_32.h | 2 +- include/asm-x86/unistd_64.h | 4 ++-- include/linux/eventpoll.h | 2 +- include/linux/syscalls.h | 2 +- 7 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 4541073dd837..e4bd1793a5e4 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -828,7 +828,7 @@ ia32_sys_call_table: .quad compat_sys_timerfd_gettime .quad compat_sys_signalfd4 .quad sys_eventfd2 - .quad sys_epoll_create2 + .quad sys_epoll_create1 .quad sys_dup3 /* 330 */ .quad sys_pipe2 .quad sys_inotify_init1 diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index f59aba5ff0f0..d44395ff34c3 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -328,7 +328,7 @@ ENTRY(sys_call_table) .long sys_timerfd_gettime .long sys_signalfd4 .long sys_eventfd2 - .long sys_epoll_create2 + .long sys_epoll_create1 .long sys_dup3 /* 330 */ .long sys_pipe2 .long sys_inotify_init1 diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 2fdad4204044..0c87474f7917 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1046,7 +1046,7 @@ retry: * RB tree. With the current implementation, the "size" parameter is ignored * (besides sanity checks). */ -asmlinkage long sys_epoll_create2(int size, int flags) +asmlinkage long sys_epoll_create1(int flags) { int error, fd = -1; struct eventpoll *ep; @@ -1058,14 +1058,13 @@ asmlinkage long sys_epoll_create2(int size, int flags) return -EINVAL; DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n", - current, size)); + current, flags)); /* - * Sanity check on the size parameter, and create the internal data - * structure ( "struct eventpoll" ). + * Create the internal data structure ( "struct eventpoll" ). */ - error = -EINVAL; - if (size <= 0 || (error = ep_alloc(&ep)) < 0) { + error = ep_alloc(&ep); + if (error < 0) { fd = error; goto error_return; } @@ -1081,14 +1080,17 @@ asmlinkage long sys_epoll_create2(int size, int flags) error_return: DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n", - current, size, fd)); + current, flags, fd)); return fd; } asmlinkage long sys_epoll_create(int size) { - return sys_epoll_create2(size, 0); + if (size < 0) + return -EINVAL; + + return sys_epoll_create1(0); } /* diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index b3daf503ab93..d7394673b772 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -334,7 +334,7 @@ #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327 #define __NR_eventfd2 328 -#define __NR_epoll_create2 329 +#define __NR_epoll_create1 329 #define __NR_dup3 330 #define __NR_pipe2 331 #define __NR_inotify_init1 332 diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index c8cb88d70c6b..3a341d791792 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -645,8 +645,8 @@ __SYSCALL(__NR_paccept, sys_paccept) __SYSCALL(__NR_signalfd4, sys_signalfd4) #define __NR_eventfd2 290 __SYSCALL(__NR_eventfd2, sys_eventfd2) -#define __NR_epoll_create2 291 -__SYSCALL(__NR_epoll_create2, sys_epoll_create2) +#define __NR_epoll_create1 291 +__SYSCALL(__NR_epoll_create1, sys_epoll_create1) #define __NR_dup3 292 __SYSCALL(__NR_dup3, sys_dup3) #define __NR_pipe2 293 diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 1cfaa40059c8..f1e1d3c47125 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -18,7 +18,7 @@ #include #include -/* Flags for epoll_create2. */ +/* Flags for epoll_create1. */ #define EPOLL_CLOEXEC O_CLOEXEC /* Valid opcodes to issue to sys_epoll_ctl() */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 93a7e7f017a6..06f2bf76c030 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -431,7 +431,7 @@ asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp); asmlinkage long sys_epoll_create(int size); -asmlinkage long sys_epoll_create2(int size, int flags); +asmlinkage long sys_epoll_create1(int flags); asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event); asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, -- cgit v1.2.3 From 102eb97564c73ea73645b38599c5cbe6f54b030c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 23 Jul 2008 21:29:55 -0700 Subject: spi: make spi_board_info.modalias a char array Currently, 'modalias' in the spi_device structure is a 'const char *'. The spi_new_device() function fills in the modalias value from a passed in spi_board_info data block. Since it is a pointer copy, the new spi_device remains dependent on the spi_board_info structure after the new spi_device is registered (no other fields in spi_device directly depend on the spi_board_info structure; all of the other data is copied). This causes a problem when dynamically propulating the list of attached SPI devices. For example, in arch/powerpc, the list of SPI devices can be populated from data in the device tree. With the current code, the device tree adapter must kmalloc() a new spi_board_info structure for each new SPI device it finds in the device tree, and there is no simple mechanism in place for keeping track of these allocations. This patch changes modalias from a 'const char *' to a fixed char array. By copying the modalias string instead of referencing it, the dependency on the spi_board_info structure is eliminated and an outside caller does not need to maintain a separate spi_board_info allocation for each device. If searched through the code to the best of my ability for any references to modalias which may be affected by this change and haven't found anything. It has been tested with the lite5200b platform in arch/powerpc. [dbrownell@users.sourceforge.net: cope with linux-next changes: KOBJ_NAME_LEN obliterated, etc] Signed-off-by: Grant Likely Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi.c | 4 +++- include/linux/spi/spi.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1771b2456bfa..ecca4a6a6f94 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -218,6 +218,8 @@ struct spi_device *spi_new_device(struct spi_master *master, if (!spi_master_get(master)) return NULL; + WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); + proxy = kzalloc(sizeof *proxy, GFP_KERNEL); if (!proxy) { dev_err(dev, "can't alloc dev for cs%d\n", @@ -229,7 +231,7 @@ struct spi_device *spi_new_device(struct spi_master *master, proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; - proxy->modalias = chip->modalias; + strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id, "%s.%u", master->dev.bus_id, diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index b9a76c972084..a9cc29d46653 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -82,7 +82,7 @@ struct spi_device { int irq; void *controller_state; void *controller_data; - const char *modalias; + char modalias[32]; /* * likely need more hooks for more protocol options affecting how -- cgit v1.2.3 From aa55ddf340c9fa3f303ee16bbf35887e42c50304 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Wed, 23 Jul 2008 21:30:29 -0700 Subject: autofs4: remove unused ioctls The ioctls AUTOFS_IOC_TOGGLEREGHOST and AUTOFS_IOC_ASKREGHOST were added several years ago but what they were intended for has never been implemented (as far as I'm aware noone uses them) so remove them. Signed-off-by: Ian Kent Reviewed-by: Jeff Moyer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/root.c | 68 +----------------------------------------------- fs/compat_ioctl.c | 2 -- include/linux/auto_fs4.h | 2 -- 3 files changed, 1 insertion(+), 71 deletions(-) (limited to 'include/linux') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index ae22bde0bbd7..bcfb2dc0a61b 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -25,7 +25,6 @@ static int autofs4_dir_rmdir(struct inode *,struct dentry *); static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); static int autofs4_dir_open(struct inode *inode, struct file *file); -static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static void *autofs4_follow_link(struct dentry *, struct nameidata *); @@ -36,7 +35,7 @@ const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, .release = dcache_dir_close, .read = generic_read_dir, - .readdir = autofs4_root_readdir, + .readdir = dcache_readdir, .ioctl = autofs4_root_ioctl, }; @@ -71,28 +70,6 @@ const struct inode_operations autofs4_dir_inode_operations = { .rmdir = autofs4_dir_rmdir, }; -static int autofs4_root_readdir(struct file *file, void *dirent, - filldir_t filldir) -{ - struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); - int oz_mode = autofs4_oz_mode(sbi); - - DPRINTK("called, filp->f_pos = %lld", file->f_pos); - - /* - * Don't set reghost flag if: - * 1) f_pos is larger than zero -- we've already been here. - * 2) we haven't even enabled reghosting in the 1st place. - * 3) this is the daemon doing a readdir - */ - if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) - sbi->needs_reghost = 1; - - DPRINTK("needs_reghost = %d", sbi->needs_reghost); - - return dcache_readdir(file, dirent, filldir); -} - static int autofs4_dir_open(struct inode *inode, struct file *file) { struct dentry *dentry = file->f_path.dentry; @@ -858,44 +835,6 @@ static inline int autofs4_get_protosubver(struct autofs_sb_info *sbi, int __user return put_user(sbi->sub_version, p); } -/* - * Tells the daemon whether we need to reghost or not. Also, clears - * the reghost_needed flag. - */ -static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) -{ - int status; - - DPRINTK("returning %d", sbi->needs_reghost); - - status = put_user(sbi->needs_reghost, p); - if (status) - return status; - - sbi->needs_reghost = 0; - return 0; -} - -/* - * Enable / Disable reghosting ioctl() operation - */ -static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) -{ - int status; - int val; - - status = get_user(val, p); - - DPRINTK("reghost = %d", val); - - if (status) - return status; - - /* turn on/off reghosting, with the val */ - sbi->reghost_enabled = val; - return 0; -} - /* * Tells the daemon whether it can umount the autofs mount. */ @@ -960,11 +899,6 @@ static int autofs4_root_ioctl(struct inode *inode, struct file *filp, case AUTOFS_IOC_SETTIMEOUT: return autofs4_get_set_timeout(sbi, p); - case AUTOFS_IOC_TOGGLEREGHOST: - return autofs4_toggle_reghost(sbi, p); - case AUTOFS_IOC_ASKREGHOST: - return autofs4_ask_reghost(sbi, p); - case AUTOFS_IOC_ASKUMOUNT: return autofs4_ask_umount(filp->f_path.mnt, p); diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 7b3a03c7c6a9..18e2c548161d 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -2297,8 +2297,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) -COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) -COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) /* Raw devices */ COMPATIBLE_IOCTL(RAW_SETBIND) diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 31a29541b504..b785c6f8644d 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -98,8 +98,6 @@ union autofs_v5_packet_union { #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) -#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) -#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) -- cgit v1.2.3 From 5ad31a575157147b43fa84ef1e21471661653878 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 23 Jul 2008 21:30:33 -0700 Subject: rtc: remove BKL for ioctl() Remove implicit use of BKL in ioctl() from the RTC framework. Instead, the rtc->ops_lock is used. That's the same lock that already protects the RTC operations when they're issued through the exported rtc_*() calls in drivers/rtc/interface.c ... making this a bugfix, not just a cleanup, since both ioctl calls and set_alarm() need to update IRQ enable flags and that implies a common lock (which RTC drivers as a rule do not provide on their own). A new comment at the declaration of "struct rtc_class_ops" summarizes current locking rules. It's not clear to me that the exceptions listed there should exist ... if not, those are pre-existing problems which can be fixed in a patch that doesn't relate to BKL removal. Signed-off-by: David Brownell Cc: Alan Cox Cc: Jonathan Corbet Acked-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-dev.c | 58 +++++++++++++++++++++++++++++++++------------------ include/linux/rtc.h | 17 +++++++++++++++ 2 files changed, 55 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 0114a78b7cbb..0a870b7e5c32 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -209,7 +209,7 @@ static unsigned int rtc_dev_poll(struct file *file, poll_table *wait) return (data != 0) ? (POLLIN | POLLRDNORM) : 0; } -static int rtc_dev_ioctl(struct inode *inode, struct file *file, +static long rtc_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; @@ -219,6 +219,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, struct rtc_wkalrm alarm; void __user *uarg = (void __user *) arg; + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + /* check that the calling task has appropriate permissions * for certain ioctls. doing this check here is useful * to avoid duplicate code in each driver. @@ -227,26 +231,31 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, case RTC_EPOCH_SET: case RTC_SET_TIME: if (!capable(CAP_SYS_TIME)) - return -EACCES; + err = -EACCES; break; case RTC_IRQP_SET: if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - return -EACCES; + err = -EACCES; break; case RTC_PIE_ON: if (rtc->irq_freq > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - return -EACCES; + err = -EACCES; break; } + if (err) + goto done; + /* try the driver's ioctl interface */ if (ops->ioctl) { err = ops->ioctl(rtc->dev.parent, cmd, arg); - if (err != -ENOIOCTLCMD) + if (err != -ENOIOCTLCMD) { + mutex_unlock(&rtc->ops_lock); return err; + } } /* if the driver does not provide the ioctl interface @@ -265,15 +274,19 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, switch (cmd) { case RTC_ALM_READ: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_alarm(rtc, &alarm); if (err < 0) return err; if (copy_to_user(uarg, &alarm.time, sizeof(tm))) - return -EFAULT; - break; + err = -EFAULT; + return err; case RTC_ALM_SET: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&alarm.time, uarg, sizeof(tm))) return -EFAULT; @@ -321,24 +334,26 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, } } - err = rtc_set_alarm(rtc, &alarm); - break; + return rtc_set_alarm(rtc, &alarm); case RTC_RD_TIME: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_time(rtc, &tm); if (err < 0) return err; if (copy_to_user(uarg, &tm, sizeof(tm))) - return -EFAULT; - break; + err = -EFAULT; + return err; case RTC_SET_TIME: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&tm, uarg, sizeof(tm))) return -EFAULT; - err = rtc_set_time(rtc, &tm); - break; + return rtc_set_time(rtc, &tm); case RTC_PIE_ON: err = rtc_irq_set_state(rtc, NULL, 1); @@ -376,34 +391,37 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, break; #endif case RTC_WKALM_SET: + mutex_unlock(&rtc->ops_lock); if (copy_from_user(&alarm, uarg, sizeof(alarm))) return -EFAULT; - err = rtc_set_alarm(rtc, &alarm); - break; + return rtc_set_alarm(rtc, &alarm); case RTC_WKALM_RD: + mutex_unlock(&rtc->ops_lock); err = rtc_read_alarm(rtc, &alarm); if (err < 0) return err; if (copy_to_user(uarg, &alarm, sizeof(alarm))) - return -EFAULT; - break; + err = -EFAULT; + return err; #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL case RTC_UIE_OFF: clear_uie(rtc); - return 0; + break; case RTC_UIE_ON: - return set_uie(rtc); + err = set_uie(rtc); #endif default: err = -ENOTTY; break; } +done: + mutex_unlock(&rtc->ops_lock); return err; } @@ -432,7 +450,7 @@ static const struct file_operations rtc_dev_fops = { .llseek = no_llseek, .read = rtc_dev_read, .poll = rtc_dev_poll, - .ioctl = rtc_dev_ioctl, + .unlocked_ioctl = rtc_dev_ioctl, .open = rtc_dev_open, .release = rtc_dev_release, .fasync = rtc_dev_fasync, diff --git a/include/linux/rtc.h b/include/linux/rtc.h index f2d0d1527721..b01fe004cb5e 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -115,6 +115,23 @@ extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm); extern struct class *rtc_class; +/* + * For these RTC methods the device parameter is the physical device + * on whatever bus holds the hardware (I2C, Platform, SPI, etc), which + * was passed to rtc_device_register(). Its driver_data normally holds + * device state, including the rtc_device pointer for the RTC. + * + * Most of these methods are called with rtc_device.ops_lock held, + * through the rtc_*(struct rtc_device *, ...) calls. + * + * The (current) exceptions are mostly filesystem hooks: + * - the proc() hook for procfs + * - non-ioctl() chardev hooks: open(), release(), read_callback() + * - periodic irq calls: irq_set_state(), irq_set_freq() + * + * REVISIT those periodic irq calls *do* have ops_lock when they're + * issued through ioctl() ... + */ struct rtc_class_ops { int (*open)(struct device *); void (*release)(struct device *); -- cgit v1.2.3 From 53e84b672c1a8190af2b376c35c7a39cf1214f59 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 23 Jul 2008 21:30:36 -0700 Subject: rtc: ds1305/ds1306 driver Support the Dallas/Maxim DS1305 and DS1306 RTC chips. These use SPI, and support alarms, NVRAM, and a trickle charger for use when their backup power supply is a supercap or rechargeable cell. This basic driver doesn't yet support suspend/resume or wakealarms. Signed-off-by: David Brownell Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ds1305.c | 847 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/ds1305.h | 35 ++ 4 files changed, 893 insertions(+) create mode 100644 drivers/rtc/rtc-ds1305.c create mode 100644 include/linux/spi/ds1305.h (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index beffb834c440..90ab73825401 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -282,6 +282,16 @@ config RTC_DRV_M41T94 This driver can also be built as a module. If so, the module will be called rtc-m41t94. +config RTC_DRV_DS1305 + tristate "Dallas/Maxim DS1305/DS1306" + help + Select this driver to get support for the Dallas/Maxim DS1305 + and DS1306 real time clock chips. These support a trickle + charger, alarms, and NVRAM in addition to the clock. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1305. + config RTC_DRV_MAX6902 tristate "Maxim MAX6902" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index b0e1af54f800..18622ef84cab 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o +obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c new file mode 100644 index 000000000000..b91d02a3ace9 --- /dev/null +++ b/drivers/rtc/rtc-ds1305.c @@ -0,0 +1,847 @@ +/* + * rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips + * + * Copyright (C) 2008 David Brownell + * + * 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 +#include +#include +#include +#include + +#include +#include + + +/* + * Registers ... mask DS1305_WRITE into register address to write, + * otherwise you're reading it. All non-bitmask values are BCD. + */ +#define DS1305_WRITE 0x80 + + +/* RTC date/time ... the main special cases are that we: + * - Need fancy "hours" encoding in 12hour mode + * - Don't rely on the "day-of-week" field (or tm_wday) + * - Are a 21st-century clock (2000 <= year < 2100) + */ +#define DS1305_RTC_LEN 7 /* bytes for RTC regs */ + +#define DS1305_SEC 0x00 /* register addresses */ +#define DS1305_MIN 0x01 +#define DS1305_HOUR 0x02 +# define DS1305_HR_12 0x40 /* set == 12 hr mode */ +# define DS1305_HR_PM 0x20 /* set == PM (12hr mode) */ +#define DS1305_WDAY 0x03 +#define DS1305_MDAY 0x04 +#define DS1305_MON 0x05 +#define DS1305_YEAR 0x06 + + +/* The two alarms have only sec/min/hour/wday fields (ALM_LEN). + * DS1305_ALM_DISABLE disables a match field (some combos are bad). + * + * NOTE that since we don't use WDAY, we limit ourselves to alarms + * only one day into the future (vs potentially up to a week). + * + * NOTE ALSO that while we could generate once-a-second IRQs (UIE), we + * don't currently support them. We'd either need to do it only when + * no alarm is pending (not the standard model), or to use the second + * alarm (implying that this is a DS1305 not DS1306, *and* that either + * it's wired up a second IRQ we know, or that INTCN is set) + */ +#define DS1305_ALM_LEN 4 /* bytes for ALM regs */ +#define DS1305_ALM_DISABLE 0x80 + +#define DS1305_ALM0(r) (0x07 + (r)) /* register addresses */ +#define DS1305_ALM1(r) (0x0b + (r)) + + +/* three control registers */ +#define DS1305_CONTROL_LEN 3 /* bytes of control regs */ + +#define DS1305_CONTROL 0x0f /* register addresses */ +# define DS1305_nEOSC 0x80 /* low enables oscillator */ +# define DS1305_WP 0x40 /* write protect */ +# define DS1305_INTCN 0x04 /* clear == only int0 used */ +# define DS1306_1HZ 0x04 /* enable 1Hz output */ +# define DS1305_AEI1 0x02 /* enable ALM1 IRQ */ +# define DS1305_AEI0 0x01 /* enable ALM0 IRQ */ +#define DS1305_STATUS 0x10 +/* status has just AEIx bits, mirrored as IRQFx */ +#define DS1305_TRICKLE 0x11 +/* trickle bits are defined in */ + +/* a bunch of NVRAM */ +#define DS1305_NVRAM_LEN 96 /* bytes of NVRAM */ + +#define DS1305_NVRAM 0x20 /* register addresses */ + + +struct ds1305 { + struct spi_device *spi; + struct rtc_device *rtc; + + struct work_struct work; + + unsigned long flags; +#define FLAG_EXITING 0 + + bool hr12; + u8 ctrl[DS1305_CONTROL_LEN]; +}; + + +/*----------------------------------------------------------------------*/ + +/* + * Utilities ... tolerate 12-hour AM/PM notation in case of non-Linux + * software (like a bootloader) which may require it. + */ + +static unsigned bcd2hour(u8 bcd) +{ + if (bcd & DS1305_HR_12) { + unsigned hour = 0; + + bcd &= ~DS1305_HR_12; + if (bcd & DS1305_HR_PM) { + hour = 12; + bcd &= ~DS1305_HR_PM; + } + hour += BCD2BIN(bcd); + return hour - 1; + } + return BCD2BIN(bcd); +} + +static u8 hour2bcd(bool hr12, int hour) +{ + if (hr12) { + hour++; + if (hour <= 12) + return DS1305_HR_12 | BIN2BCD(hour); + hour -= 12; + return DS1305_HR_12 | DS1305_HR_PM | BIN2BCD(hour); + } + return BIN2BCD(hour); +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface to RTC framework + */ + +#ifdef CONFIG_RTC_INTF_DEV + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_ioctl(struct device *dev, unsigned cmd, unsigned long arg) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[2]; + int status = -ENOIOCTLCMD; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + + switch (cmd) { + case RTC_AIE_OFF: + status = 0; + if (!(buf[1] & DS1305_AEI0)) + goto done; + buf[1] &= ~DS1305_AEI0; + break; + + case RTC_AIE_ON: + status = 0; + if (ds1305->ctrl[0] & DS1305_AEI0) + goto done; + buf[1] |= DS1305_AEI0; + break; + } + if (status == 0) { + status = spi_write_then_read(ds1305->spi, buf, sizeof buf, + NULL, 0); + if (status >= 0) + ds1305->ctrl[0] = buf[1]; + } + +done: + return status; +} + +#else +#define ds1305_ioctl NULL +#endif + +/* + * Get/set of date and time is pretty normal. + */ + +static int ds1305_get_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 addr = DS1305_SEC; + u8 buf[DS1305_RTC_LEN]; + int status; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + status = spi_write_then_read(ds1305->spi, &addr, sizeof addr, + buf, sizeof buf); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n", + "read", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6]); + + /* Decode the registers */ + time->tm_sec = BCD2BIN(buf[DS1305_SEC]); + time->tm_min = BCD2BIN(buf[DS1305_MIN]); + time->tm_hour = bcd2hour(buf[DS1305_HOUR]); + time->tm_wday = buf[DS1305_WDAY] - 1; + time->tm_mday = BCD2BIN(buf[DS1305_MDAY]); + time->tm_mon = BCD2BIN(buf[DS1305_MON]) - 1; + time->tm_year = BCD2BIN(buf[DS1305_YEAR]) + 100; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Time may not be set */ + return rtc_valid_tm(time); +} + +static int ds1305_set_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[1 + DS1305_RTC_LEN]; + u8 *bp = buf; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Write registers starting at the first time/date address. */ + *bp++ = DS1305_WRITE | DS1305_SEC; + + *bp++ = BIN2BCD(time->tm_sec); + *bp++ = BIN2BCD(time->tm_min); + *bp++ = hour2bcd(ds1305->hr12, time->tm_hour); + *bp++ = (time->tm_wday < 7) ? (time->tm_wday + 1) : 1; + *bp++ = BIN2BCD(time->tm_mday); + *bp++ = BIN2BCD(time->tm_mon + 1); + *bp++ = BIN2BCD(time->tm_year - 100); + + dev_dbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n", + "write", buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(ds1305->spi, buf, sizeof buf, + NULL, 0); +} + +/* + * Get/set of alarm is a bit funky: + * + * - First there's the inherent raciness of getting the (partitioned) + * status of an alarm that could trigger while we're reading parts + * of that status. + * + * - Second there's its limited range (we could increase it a bit by + * relying on WDAY), which means it will easily roll over. + * + * - Third there's the choice of two alarms and alarm signals. + * Here we use ALM0 and expect that nINT0 (open drain) is used; + * that's the only real option for DS1306 runtime alarms, and is + * natural on DS1305. + * + * - Fourth, there's also ALM1, and a second interrupt signal: + * + On DS1305 ALM1 uses nINT1 (when INTCN=1) else nINT0; + * + On DS1306 ALM1 only uses INT1 (an active high pulse) + * and it won't work when VCC1 is active. + * + * So to be most general, we should probably set both alarms to the + * same value, letting ALM1 be the wakeup event source on DS1306 + * and handling several wiring options on DS1305. + * + * - Fifth, we support the polled mode (as well as possible; why not?) + * even when no interrupt line is wired to an IRQ. + */ + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + u8 addr; + int status; + u8 buf[DS1305_ALM_LEN]; + + /* Refresh control register cache BEFORE reading ALM0 registers, + * since reading alarm registers acks any pending IRQ. That + * makes returning "pending" status a bit of a lie, but that bit + * of EFI status is at best fragile anyway (given IRQ handlers). + */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof addr, + ds1305->ctrl, sizeof ds1305->ctrl); + if (status < 0) + return status; + + alm->enabled = !!(ds1305->ctrl[0] & DS1305_AEI0); + alm->pending = !!(ds1305->ctrl[1] & DS1305_AEI0); + + /* get and check ALM0 registers */ + addr = DS1305_ALM0(DS1305_SEC); + status = spi_write_then_read(spi, &addr, sizeof addr, + buf, sizeof buf); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 read", buf[DS1305_SEC], buf[DS1305_MIN], + buf[DS1305_HOUR], buf[DS1305_WDAY]); + + if ((DS1305_ALM_DISABLE & buf[DS1305_SEC]) + || (DS1305_ALM_DISABLE & buf[DS1305_MIN]) + || (DS1305_ALM_DISABLE & buf[DS1305_HOUR])) + return -EIO; + + /* Stuff these values into alm->time and let RTC framework code + * fill in the rest ... and also handle rollover to tomorrow when + * that's needed. + */ + alm->time.tm_sec = BCD2BIN(buf[DS1305_SEC]); + alm->time.tm_min = BCD2BIN(buf[DS1305_MIN]); + alm->time.tm_hour = bcd2hour(buf[DS1305_HOUR]); + alm->time.tm_mday = -1; + alm->time.tm_mon = -1; + alm->time.tm_year = -1; + /* next three fields are unused by Linux */ + alm->time.tm_wday = -1; + alm->time.tm_mday = -1; + alm->time.tm_isdst = -1; + + return 0; +} + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + unsigned long now, later; + struct rtc_time tm; + int status; + u8 buf[1 + DS1305_ALM_LEN]; + + /* convert desired alarm to time_t */ + status = rtc_tm_to_time(&alm->time, &later); + if (status < 0) + return status; + + /* Read current time as time_t */ + status = ds1305_get_time(dev, &tm); + if (status < 0) + return status; + status = rtc_tm_to_time(&tm, &now); + if (status < 0) + return status; + + /* make sure alarm fires within the next 24 hours */ + if (later <= now) + return -EINVAL; + if ((later - now) > 24 * 60 * 60) + return -EDOM; + + /* disable alarm if needed */ + if (ds1305->ctrl[0] & DS1305_AEI0) { + ds1305->ctrl[0] &= ~DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + if (status < 0) + return status; + } + + /* write alarm */ + buf[0] = DS1305_WRITE | DS1305_ALM0(DS1305_SEC); + buf[1 + DS1305_SEC] = BIN2BCD(alm->time.tm_sec); + buf[1 + DS1305_MIN] = BIN2BCD(alm->time.tm_min); + buf[1 + DS1305_HOUR] = hour2bcd(ds1305->hr12, alm->time.tm_hour); + buf[1 + DS1305_WDAY] = DS1305_ALM_DISABLE; + + dev_dbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN], + buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]); + + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + if (status < 0) + return status; + + /* enable alarm if requested */ + if (alm->enabled) { + ds1305->ctrl[0] |= DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + } + + return status; +} + +#ifdef CONFIG_PROC_FS + +static int ds1305_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + char *diodes = "no"; + char *resistors = ""; + + /* ctrl[2] is treated as read-only; no locking needed */ + if ((ds1305->ctrl[2] & 0xf0) == DS1305_TRICKLE_MAGIC) { + switch (ds1305->ctrl[2] & 0x0c) { + case DS1305_TRICKLE_DS2: + diodes = "2 diodes, "; + break; + case DS1305_TRICKLE_DS1: + diodes = "1 diode, "; + break; + default: + goto done; + } + switch (ds1305->ctrl[2] & 0x03) { + case DS1305_TRICKLE_2K: + resistors = "2k Ohm"; + break; + case DS1305_TRICKLE_4K: + resistors = "4k Ohm"; + break; + case DS1305_TRICKLE_8K: + resistors = "8k Ohm"; + break; + default: + diodes = "no"; + break; + } + } + +done: + return seq_printf(seq, + "trickle_charge\t: %s%s\n", + diodes, resistors); +} + +#else +#define ds1305_proc NULL +#endif + +static const struct rtc_class_ops ds1305_ops = { + .ioctl = ds1305_ioctl, + .read_time = ds1305_get_time, + .set_time = ds1305_set_time, + .read_alarm = ds1305_get_alarm, + .set_alarm = ds1305_set_alarm, + .proc = ds1305_proc, +}; + +static void ds1305_work(struct work_struct *work) +{ + struct ds1305 *ds1305 = container_of(work, struct ds1305, work); + struct mutex *lock = &ds1305->rtc->ops_lock; + struct spi_device *spi = ds1305->spi; + u8 buf[3]; + int status; + + /* lock to protect ds1305->ctrl */ + mutex_lock(lock); + + /* Disable the IRQ, and clear its status ... for now, we "know" + * that if more than one alarm is active, they're in sync. + * Note that reading ALM data registers also clears IRQ status. + */ + ds1305->ctrl[0] &= ~(DS1305_AEI1 | DS1305_AEI0); + ds1305->ctrl[1] = 0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = 0; + + status = spi_write_then_read(spi, buf, sizeof buf, + NULL, 0); + if (status < 0) + dev_dbg(&spi->dev, "clear irq --> %d\n", status); + + mutex_unlock(lock); + + if (!test_bit(FLAG_EXITING, &ds1305->flags)) + enable_irq(spi->irq); + + /* rtc_update_irq() requires an IRQ-disabled context */ + local_irq_disable(); + rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF); + local_irq_enable(); +} + +/* + * This "real" IRQ handler hands off to a workqueue mostly to allow + * mutex locking for ds1305->ctrl ... unlike I2C, we could issue async + * I/O requests in IRQ context (to clear the IRQ status). + */ +static irqreturn_t ds1305_irq(int irq, void *p) +{ + struct ds1305 *ds1305 = p; + + disable_irq(irq); + schedule_work(&ds1305->work); + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface for NVRAM + */ + +static void msg_init(struct spi_message *m, struct spi_transfer *x, + u8 *addr, size_t count, char *tx, char *rx) +{ + spi_message_init(m); + memset(x, 0, 2 * sizeof(*x)); + + x->tx_buf = addr; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + + x->tx_buf = tx; + x->rx_buf = rx; + x->len = count; + spi_message_add_tail(x, m); +} + +static ssize_t +ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spi_device *spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + int status; + + spi = container_of(kobj, struct spi_device, dev.kobj); + + if (unlikely(off >= DS1305_NVRAM_LEN)) + return 0; + if (count >= DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN; + if ((off + count) > DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN - off; + if (unlikely(!count)) + return count; + + addr = DS1305_NVRAM + off; + msg_init(&m, x, &addr, count, NULL, buf); + + status = spi_sync(spi, &m); + if (status < 0) + dev_err(&spi->dev, "nvram %s error %d\n", "read", status); + return (status < 0) ? status : count; +} + +static ssize_t +ds1305_nvram_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spi_device *spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + int status; + + spi = container_of(kobj, struct spi_device, dev.kobj); + + if (unlikely(off >= DS1305_NVRAM_LEN)) + return -EFBIG; + if (count >= DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN; + if ((off + count) > DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN - off; + if (unlikely(!count)) + return count; + + addr = (DS1305_WRITE | DS1305_NVRAM) + off; + msg_init(&m, x, &addr, count, buf, NULL); + + status = spi_sync(spi, &m); + if (status < 0) + dev_err(&spi->dev, "nvram %s error %d\n", "write", status); + return (status < 0) ? status : count; +} + +static struct bin_attribute nvram = { + .attr.name = "nvram", + .attr.mode = S_IRUGO | S_IWUSR, + .attr.owner = THIS_MODULE, + .read = ds1305_nvram_read, + .write = ds1305_nvram_write, + .size = DS1305_NVRAM_LEN, +}; + +/*----------------------------------------------------------------------*/ + +/* + * Interface to SPI stack + */ + +static int __devinit ds1305_probe(struct spi_device *spi) +{ + struct ds1305 *ds1305; + struct rtc_device *rtc; + int status; + u8 addr, value; + struct ds1305_platform_data *pdata = spi->dev.platform_data; + bool write_ctrl = false; + + /* Sanity check board setup data. This may be hooked up + * in 3wire mode, but we don't care. Note that unless + * there's an inverter in place, this needs SPI_CS_HIGH! + */ + if ((spi->bits_per_word && spi->bits_per_word != 8) + || (spi->max_speed_hz > 2000000) + || !(spi->mode & SPI_CPHA)) + return -EINVAL; + + /* set up driver data */ + ds1305 = kzalloc(sizeof *ds1305, GFP_KERNEL); + if (!ds1305) + return -ENOMEM; + ds1305->spi = spi; + spi_set_drvdata(spi, ds1305); + + /* read and cache control registers */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof addr, + ds1305->ctrl, sizeof ds1305->ctrl); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "read", status); + goto fail0; + } + + dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", + "read", ds1305->ctrl[0], + ds1305->ctrl[1], ds1305->ctrl[2]); + + /* Sanity check register values ... partially compensating for the + * fact that SPI has no device handshake. A pullup on MISO would + * make these tests fail; but not all systems will have one. If + * some register is neither 0x00 nor 0xff, a chip is likely there. + */ + if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) { + dev_dbg(&spi->dev, "RTC chip is not present\n"); + status = -ENODEV; + goto fail0; + } + if (ds1305->ctrl[2] == 0) + dev_dbg(&spi->dev, "chip may not be present\n"); + + /* enable writes if needed ... if we were paranoid it would + * make sense to enable them only when absolutely necessary. + */ + if (ds1305->ctrl[0] & DS1305_WP) { + u8 buf[2]; + + ds1305->ctrl[0] &= ~DS1305_WP; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + + dev_dbg(&spi->dev, "clear WP --> %d\n", status); + if (status < 0) + goto fail0; + } + + /* on DS1305, maybe start oscillator; like most low power + * oscillators, it may take a second to stabilize + */ + if (ds1305->ctrl[0] & DS1305_nEOSC) { + ds1305->ctrl[0] &= ~DS1305_nEOSC; + write_ctrl = true; + dev_warn(&spi->dev, "SET TIME!\n"); + } + + /* ack any pending IRQs */ + if (ds1305->ctrl[1]) { + ds1305->ctrl[1] = 0; + write_ctrl = true; + } + + /* this may need one-time (re)init */ + if (pdata) { + /* maybe enable trickle charge */ + if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) { + ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC + | pdata->trickle; + write_ctrl = true; + } + + /* on DS1306, configure 1 Hz signal */ + if (pdata->is_ds1306) { + if (pdata->en_1hz) { + if (!(ds1305->ctrl[0] & DS1306_1HZ)) { + ds1305->ctrl[0] |= DS1306_1HZ; + write_ctrl = true; + } + } else { + if (ds1305->ctrl[0] & DS1306_1HZ) { + ds1305->ctrl[0] &= ~DS1306_1HZ; + write_ctrl = true; + } + } + } + } + + if (write_ctrl) { + u8 buf[4]; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = ds1305->ctrl[1]; + buf[3] = ds1305->ctrl[2]; + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "write", status); + goto fail0; + } + + dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", + "write", ds1305->ctrl[0], + ds1305->ctrl[1], ds1305->ctrl[2]); + } + + /* see if non-Linux software set up AM/PM mode */ + addr = DS1305_HOUR; + status = spi_write_then_read(spi, &addr, sizeof addr, + &value, sizeof value); + if (status < 0) { + dev_dbg(&spi->dev, "read HOUR --> %d\n", status); + goto fail0; + } + + ds1305->hr12 = (DS1305_HR_12 & value) != 0; + if (ds1305->hr12) + dev_dbg(&spi->dev, "AM/PM\n"); + + /* register RTC ... from here on, ds1305->ctrl needs locking */ + rtc = rtc_device_register("ds1305", &spi->dev, + &ds1305_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + status = PTR_ERR(rtc); + dev_dbg(&spi->dev, "register rtc --> %d\n", status); + goto fail0; + } + ds1305->rtc = rtc; + + /* Maybe set up alarm IRQ; be ready to handle it triggering right + * away. NOTE that we don't share this. The signal is active low, + * and we can't ack it before a SPI message delay. We temporarily + * disable the IRQ until it's acked, which lets us work with more + * IRQ trigger modes (not all IRQ controllers can do falling edge). + */ + if (spi->irq) { + INIT_WORK(&ds1305->work, ds1305_work); + status = request_irq(spi->irq, ds1305_irq, + 0, dev_name(&rtc->dev), ds1305); + if (status < 0) { + dev_dbg(&spi->dev, "request_irq %d --> %d\n", + spi->irq, status); + goto fail1; + } + } + + /* export NVRAM */ + status = sysfs_create_bin_file(&spi->dev.kobj, &nvram); + if (status < 0) { + dev_dbg(&spi->dev, "register nvram --> %d\n", status); + goto fail2; + } + + return 0; + +fail2: + free_irq(spi->irq, ds1305); +fail1: + rtc_device_unregister(rtc); +fail0: + kfree(ds1305); + return status; +} + +static int __devexit ds1305_remove(struct spi_device *spi) +{ + struct ds1305 *ds1305 = spi_get_drvdata(spi); + + sysfs_remove_bin_file(&spi->dev.kobj, &nvram); + + /* carefully shut down irq and workqueue, if present */ + if (spi->irq) { + set_bit(FLAG_EXITING, &ds1305->flags); + free_irq(spi->irq, ds1305); + flush_scheduled_work(); + } + + rtc_device_unregister(ds1305->rtc); + spi_set_drvdata(spi, NULL); + kfree(ds1305); + return 0; +} + +static struct spi_driver ds1305_driver = { + .driver.name = "rtc-ds1305", + .driver.owner = THIS_MODULE, + .probe = ds1305_probe, + .remove = __devexit_p(ds1305_remove), + /* REVISIT add suspend/resume */ +}; + +static int __init ds1305_init(void) +{ + return spi_register_driver(&ds1305_driver); +} +module_init(ds1305_init); + +static void __exit ds1305_exit(void) +{ + spi_unregister_driver(&ds1305_driver); +} +module_exit(ds1305_exit); + +MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/ds1305.h b/include/linux/spi/ds1305.h new file mode 100644 index 000000000000..287ec830eab7 --- /dev/null +++ b/include/linux/spi/ds1305.h @@ -0,0 +1,35 @@ +#ifndef __LINUX_SPI_DS1305_H +#define __LINUX_SPI_DS1305_H + +/* + * One-time configuration for ds1305 and ds1306 RTC chips. + * + * Put a pointer to this in spi_board_info.platform_data if you want to + * be sure that Linux (re)initializes this as needed ... after losing + * backup power, and potentially on the first boot. + */ +struct ds1305_platform_data { + + /* Trickle charge configuration: it's OK to leave out the MAGIC + * bitmask; mask in either DS1 or DS2, and then one of 2K/4k/8K. + */ +#define DS1305_TRICKLE_MAGIC 0xa0 +#define DS1305_TRICKLE_DS2 0x08 /* two diodes */ +#define DS1305_TRICKLE_DS1 0x04 /* one diode */ +#define DS1305_TRICKLE_2K 0x01 /* 2 KOhm resistance */ +#define DS1305_TRICKLE_4K 0x02 /* 4 KOhm resistance */ +#define DS1305_TRICKLE_8K 0x03 /* 8 KOhm resistance */ + u8 trickle; + + /* set only on ds1306 parts */ + bool is_ds1306; + + /* ds1306 only: enable 1 Hz output */ + bool en_1hz; + + /* REVISIT: the driver currently expects nINT0 to be wired + * as the alarm IRQ. ALM1 may also need to be set up ... + */ +}; + +#endif /* __LINUX_SPI_DS1305_H */ -- cgit v1.2.3 From d3de851a445123f24ad8ece18662014b5e8a8b4e Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 23 Jul 2008 21:30:37 -0700 Subject: rtc: BCD codeshrink This updates to define the key routines as constant functions, which the macros will then call. Newer code can now call bcd2bin() instead of SCREAMING BCD2BIN() TO THE FOUR WINDS. This lets each driver shrink their codespace by using N function calls to a single (global) copy of those routines, instead of N inlined copies of these functions per driver. These routines aren't used in speed-critical code. Almost all callers are in the RTC framework. Typical per-driver savings is near 300 bytes. Signed-off-by: David Brownell Acked-by: Adrian Bunk Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bcd.h | 9 +++++++-- lib/Makefile | 2 +- lib/bcd.c | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 lib/bcd.c (limited to 'include/linux') diff --git a/include/linux/bcd.h b/include/linux/bcd.h index c545308125b0..7ac518e3c152 100644 --- a/include/linux/bcd.h +++ b/include/linux/bcd.h @@ -10,8 +10,13 @@ #ifndef _BCD_H #define _BCD_H -#define BCD2BIN(val) (((val) & 0x0f) + ((val)>>4)*10) -#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10) +#include + +unsigned bcd2bin(unsigned char val) __attribute_const__; +unsigned char bin2bcd(unsigned val) __attribute_const__; + +#define BCD2BIN(val) bcd2bin(val) +#define BIN2BCD(val) bin2bcd(val) /* backwards compat */ #define BCD_TO_BIN(val) ((val)=BCD2BIN(val)) diff --git a/lib/Makefile b/lib/Makefile index 818c4d455518..9085ad6fa53d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -18,7 +18,7 @@ lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o kref.o klist.o -obj-y += div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ +obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) diff --git a/lib/bcd.c b/lib/bcd.c new file mode 100644 index 000000000000..d74257fd0fe7 --- /dev/null +++ b/lib/bcd.c @@ -0,0 +1,14 @@ +#include +#include + +unsigned bcd2bin(unsigned char val) +{ + return (val & 0x0f) + (val >> 4) * 10; +} +EXPORT_SYMBOL(bcd2bin); + +unsigned char bin2bcd(unsigned val) +{ + return ((val / 10) << 4) + val % 10; +} +EXPORT_SYMBOL(bin2bcd); -- cgit v1.2.3 From 01a2d9ed85c945fc8a672622780533a1a0b7caf5 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Wed, 23 Jul 2008 21:31:04 -0700 Subject: tridentfb: acceleration constants change This patch replaces deprecated constant FB_ACCELF_TEXT with FBINFO_HWACCEL_DISABLED and adds constants for Trident families of accelerators. The FBINFO_HWACCEL_DISABLED is correctly used so noaccel parameter works now. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tridentfb.c | 45 +++++++++++++++++++++++++++++++-------------- include/linux/fb.h | 4 ++++ 2 files changed, 35 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index ff82ec1e5e4d..279411523ed9 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -499,6 +499,10 @@ static void tridentfb_fillrect(struct fb_info *info, struct tridentfb_par *par = info->par; int col; + if (info->flags & FBINFO_HWACCEL_DISABLED) { + cfb_fillrect(info, fr); + return; + } if (info->var.bits_per_pixel == 8) { col = fr->color; col |= col << 8; @@ -516,6 +520,10 @@ static void tridentfb_copyarea(struct fb_info *info, { struct tridentfb_par *par = info->par; + if (info->flags & FBINFO_HWACCEL_DISABLED) { + cfb_copyarea(info, ca); + return; + } par->wait_engine(par); par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy, ca->width, ca->height); @@ -525,7 +533,8 @@ static int tridentfb_sync(struct fb_info *info) { struct tridentfb_par *par = info->par; - par->wait_engine(par); + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + par->wait_engine(par); return 0; } #else @@ -855,8 +864,9 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var, if (var->yres_virtual > 0xffff) return -EINVAL; line_length = var->xres_virtual * bpp / 8; -#ifdef CONFIG_FB_TRIDENT_ACCEL - if (!is3Dchip(par->chip_id)) { + + if (!is3Dchip(par->chip_id) && + !(info->flags & FBINFO_HWACCEL_DISABLED)) { /* acceleration requires line length to be power of 2 */ if (line_length <= 512) var->xres_virtual = 512 * 8 / bpp; @@ -873,7 +883,7 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var, line_length = var->xres_virtual * bpp / 8; } -#endif + if (var->yres > var->yres_virtual) var->yres_virtual = var->yres; if (line_length * var->yres_virtual > info->fix.smem_len) @@ -1190,9 +1200,9 @@ static int tridentfb_set_par(struct fb_info *info) set_number_of_lines(par, info->var.yres); info->fix.line_length = info->var.xres_virtual * bpp / 8; set_lwidth(par, info->fix.line_length / 8); -#ifdef CONFIG_FB_TRIDENT_ACCEL - par->init_accel(par, info->var.xres_virtual, bpp); -#endif + + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + par->init_accel(par, info->var.xres_virtual, bpp); info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; info->cmap.len = (bpp == 8) ? 256 : 16; @@ -1326,6 +1336,9 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, output("*** Please do use cyblafb, Cyberblade/i1 support " "will soon be removed from tridentfb!\n"); +#ifndef CONFIG_FB_TRIDENT_ACCEL + noaccel = 1; +#endif /* If PCI id is 0x9660 then further detect chip type */ @@ -1370,21 +1383,25 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, default_par->wait_engine = xp_wait_engine; default_par->fill_rect = xp_fill_rect; default_par->copy_rect = xp_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP; } else if (is_blade(chip_id)) { default_par->init_accel = blade_init_accel; default_par->wait_engine = blade_wait_engine; default_par->fill_rect = blade_fill_rect; default_par->copy_rect = blade_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D; } else if (chip3D) { /* 3DImage family left */ default_par->init_accel = image_init_accel; default_par->wait_engine = image_wait_engine; default_par->fill_rect = image_fill_rect; default_par->copy_rect = image_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE; } else { /* TGUI 9440/96XX family */ default_par->init_accel = tgui_init_accel; default_par->wait_engine = xp_wait_engine; default_par->fill_rect = tgui_fill_rect; default_par->copy_rect = tgui_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI; } default_par->chip_id = chip_id; @@ -1441,9 +1458,13 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, info->pseudo_palette = default_par->pseudo_pal; info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; -#ifdef CONFIG_FB_TRIDENT_ACCEL - info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; -#endif + if (!noaccel && default_par->init_accel) { + info->flags &= ~FBINFO_HWACCEL_DISABLED; + info->flags |= FBINFO_HWACCEL_COPYAREA; + info->flags |= FBINFO_HWACCEL_FILLRECT; + } else + info->flags |= FBINFO_HWACCEL_DISABLED; + if (!fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, bpp)) { err = -EINVAL; @@ -1453,10 +1474,6 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, if (err < 0) goto out_unmap2; - if (!noaccel && default_par->init_accel) - info->var.accel_flags |= FB_ACCELF_TEXT; - else - info->var.accel_flags &= ~FB_ACCELF_TEXT; info->var.activate |= FB_ACTIVATE_NOW; info->device = &dev->dev; if (register_framebuffer(info) < 0) { diff --git a/include/linux/fb.h b/include/linux/fb.h index 72295b099228..a084d1335869 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -120,6 +120,10 @@ struct dentry; #define FB_ACCEL_XGI_VOLARI_V 47 /* XGI Volari V3XT, V5, V8 */ #define FB_ACCEL_XGI_VOLARI_Z 48 /* XGI Volari Z7 */ #define FB_ACCEL_OMAP1610 49 /* TI OMAP16xx */ +#define FB_ACCEL_TRIDENT_TGUI 50 /* Trident TGUI */ +#define FB_ACCEL_TRIDENT_3DIMAGE 51 /* Trident 3DImage */ +#define FB_ACCEL_TRIDENT_BLADE3D 52 /* Trident Blade3D */ +#define FB_ACCEL_TRIDENT_BLADEXP 53 /* Trident BladeXP */ #define FB_ACCEL_NEOMAGIC_NM2070 90 /* NeoMagic NM2070 */ #define FB_ACCEL_NEOMAGIC_NM2090 91 /* NeoMagic NM2090 */ #define FB_ACCEL_NEOMAGIC_NM2093 92 /* NeoMagic NM2093 */ -- cgit v1.2.3 From 206c5d69d0540024faffd423fc703f1e457332d7 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 23 Jul 2008 21:31:35 -0700 Subject: sm501: add inversion controls for VBIASEN and FPEN Add flags to allow the driver to invert the sense of both VBIASEN and FPEN signals comming from the SM501. Signed-off-by: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sm501fb.c | 26 ++++++++++++++++++++++---- include/linux/sm501.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 15d4a768b1f6..122a0f8495c8 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -663,15 +663,25 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) sm501fb_sync_regs(fbi); mdelay(10); + /* VBIASEN */ + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { - control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + else + control |= SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { - control |= SM501_DC_PANEL_CONTROL_FPEN; + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + else + control |= SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); @@ -679,14 +689,22 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { /* disable panel power */ if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { - control &= ~SM501_DC_PANEL_CONTROL_FPEN; + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control |= SM501_DC_PANEL_CONTROL_FPEN; + else + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { - control &= ~SM501_DC_PANEL_CONTROL_BIAS; + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control |= SM501_DC_PANEL_CONTROL_BIAS; + else + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 95c1c39ba445..b530fa6a1d34 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -73,6 +73,8 @@ extern unsigned long sm501_gpio_get(struct device *dev, #define SM501FB_FLAG_USE_HWACCEL (1<<3) #define SM501FB_FLAG_PANEL_NO_FPEN (1<<4) #define SM501FB_FLAG_PANEL_NO_VBIASEN (1<<5) +#define SM501FB_FLAG_PANEL_INV_FPEN (1<<6) +#define SM501FB_FLAG_PANEL_INV_VBIASEN (1<<7) struct sm501_platdata_fbsub { struct fb_videomode *def_mode; -- cgit v1.2.3 From 0c531360ed504aa0ce995fcb8ef08e82b6534d0b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 23 Jul 2008 21:31:38 -0700 Subject: lcd: add lcd_device to check_fb() entry in lcd_ops Add the lcd_device being checked to the check_fb entry of lcd_ops. This ensures that any driver using this to check against it's own state can do so, and also makes all the calls in lcd_ops more orthogonal in their arguments. Signed-off-by: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lcd.c | 2 +- drivers/video/bf54x-lq043fb.c | 2 +- drivers/video/bfin-t350mcqb-fb.c | 2 +- include/linux/lcd.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 299fd318dd45..b15b2b84a6f7 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -33,7 +33,7 @@ static int fb_notifier_callback(struct notifier_block *self, ld = container_of(self, struct lcd_device, fb_notif); mutex_lock(&ld->ops_lock); if (ld->ops) - if (!ld->ops->check_fb || ld->ops->check_fb(evdata->info)) + if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) ld->ops->set_power(ld, *(int *)evdata->data); mutex_unlock(&ld->ops_lock); return 0; diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 49834a67a623..940467aed13f 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -478,7 +478,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) return 0; } -static int bfin_lcd_check_fb(struct fb_info *fi) +static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi) { if (!fi || (fi == &bfin_bf54x_fb)) return 1; diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c index 135d6dd7e672..7d1b819e501c 100644 --- a/drivers/video/bfin-t350mcqb-fb.c +++ b/drivers/video/bfin-t350mcqb-fb.c @@ -396,7 +396,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) return 0; } -static int bfin_lcd_check_fb(struct fb_info *fi) +static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi) { if (!fi || (fi == &bfin_t350mcqb_fb)) return 1; diff --git a/include/linux/lcd.h b/include/linux/lcd.h index 1d379787f2e7..173febac6656 100644 --- a/include/linux/lcd.h +++ b/include/linux/lcd.h @@ -47,7 +47,7 @@ struct lcd_ops { int (*set_contrast)(struct lcd_device *, int contrast); /* Check if given framebuffer device is the one LCD is bound to; return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */ - int (*check_fb)(struct fb_info *); + int (*check_fb)(struct lcd_device *, struct fb_info *); }; struct lcd_device { -- cgit v1.2.3 From 3e074058d72486676f6fdf6fe803200c62dcb403 Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Wed, 23 Jul 2008 21:31:48 -0700 Subject: fbdev: LCD backlight driver using Atmel PWM driver This patch adds a platform driver using the ATMEL PWM driver to control a backlight which requires a PWM signal and optional GPIO signal for discrete on/off signal. It has been tested on Favr-32 board from EarthLCD. The driver is configurable by supplying a struct with the platform data. See the include/linux/atmel-pwm-bl.h for details. The board code for Favr-32 will be submitted to the AVR32 kernel list. Signed-off-by: Hans-Christian Egtvedt Cc: Krzysztof Helt Cc: Haavard Skinnemoen Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/Kconfig | 12 ++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/atmel-pwm-bl.c | 244 +++++++++++++++++++++++++++++++++ include/linux/atmel-pwm-bl.h | 43 ++++++ 4 files changed, 300 insertions(+) create mode 100644 drivers/video/backlight/atmel-pwm-bl.c create mode 100644 include/linux/atmel-pwm-bl.h (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index b289e197e55d..98d9faf4970d 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -87,6 +87,18 @@ config BACKLIGHT_ATMEL_LCDC If in doubt, it's safe to enable this option; it doesn't kick in unless the board's description says it's wired that way. +config BACKLIGHT_ATMEL_PWM + tristate "Atmel PWM backlight control" + depends on BACKLIGHT_CLASS_DEVICE && ATMEL_PWM + default n + help + Say Y here if you want to use the PWM peripheral in Atmel AT91 and + AVR32 devices. This driver will need additional platform data to know + which PWM instance to use and how to configure it. + + To compile this driver as a module, choose M here: the module will be + called atmel-pwm-bl. + config BACKLIGHT_CORGI tristate "Generic (aka Sharp Corgi) Backlight Driver" depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 7d31c14088aa..d8a08e468cc7 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o +obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c new file mode 100644 index 000000000000..505c0823a105 --- /dev/null +++ b/drivers/video/backlight/atmel-pwm-bl.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2008 Atmel Corporation + * + * Backlight driver using Atmel PWM peripheral. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct atmel_pwm_bl { + const struct atmel_pwm_bl_platform_data *pdata; + struct backlight_device *bldev; + struct platform_device *pdev; + struct pwm_channel pwmc; + int gpio_on; +}; + +static int atmel_pwm_bl_set_intensity(struct backlight_device *bd) +{ + struct atmel_pwm_bl *pwmbl = bl_get_data(bd); + int intensity = bd->props.brightness; + int pwm_duty; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + if (pwmbl->pdata->pwm_active_low) + pwm_duty = pwmbl->pdata->pwm_duty_min + intensity; + else + pwm_duty = pwmbl->pdata->pwm_duty_max - intensity; + + if (pwm_duty > pwmbl->pdata->pwm_duty_max) + pwm_duty = pwmbl->pdata->pwm_duty_max; + if (pwm_duty < pwmbl->pdata->pwm_duty_min) + pwm_duty = pwmbl->pdata->pwm_duty_min; + + if (!intensity) { + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, + 0 ^ pwmbl->pdata->on_active_low); + } + pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); + pwm_channel_disable(&pwmbl->pwmc); + } else { + pwm_channel_enable(&pwmbl->pwmc); + pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, + 1 ^ pwmbl->pdata->on_active_low); + } + } + + return 0; +} + +static int atmel_pwm_bl_get_intensity(struct backlight_device *bd) +{ + struct atmel_pwm_bl *pwmbl = bl_get_data(bd); + u8 intensity; + + if (pwmbl->pdata->pwm_active_low) { + intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) - + pwmbl->pdata->pwm_duty_min; + } else { + intensity = pwmbl->pdata->pwm_duty_max - + pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY); + } + + return intensity; +} + +static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl) +{ + unsigned long pwm_rate = pwmbl->pwmc.mck; + unsigned long prescale = DIV_ROUND_UP(pwm_rate, + (pwmbl->pdata->pwm_frequency * + pwmbl->pdata->pwm_compare_max)) - 1; + + /* + * Prescale must be power of two and maximum 0xf in size because of + * hardware limit. PWM speed will be: + * PWM module clock speed / (2 ^ prescale). + */ + prescale = fls(prescale); + if (prescale > 0xf) + prescale = 0xf; + + pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale); + pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY, + pwmbl->pdata->pwm_duty_min + + pwmbl->bldev->props.brightness); + pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD, + pwmbl->pdata->pwm_compare_max); + + dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver " + "(%lu Hz)\n", pwmbl->pwmc.mck / + pwmbl->pdata->pwm_compare_max / + (1 << prescale)); + + return pwm_channel_enable(&pwmbl->pwmc); +} + +static struct backlight_ops atmel_pwm_bl_ops = { + .get_brightness = atmel_pwm_bl_get_intensity, + .update_status = atmel_pwm_bl_set_intensity, +}; + +static int atmel_pwm_bl_probe(struct platform_device *pdev) +{ + const struct atmel_pwm_bl_platform_data *pdata; + struct backlight_device *bldev; + struct atmel_pwm_bl *pwmbl; + int retval; + + pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL); + if (!pwmbl) + return -ENOMEM; + + pwmbl->pdev = pdev; + + pdata = pdev->dev.platform_data; + if (!pdata) { + retval = -ENODEV; + goto err_free_mem; + } + + if (pdata->pwm_compare_max < pdata->pwm_duty_max || + pdata->pwm_duty_min > pdata->pwm_duty_max || + pdata->pwm_frequency == 0) { + retval = -EINVAL; + goto err_free_mem; + } + + pwmbl->pdata = pdata; + pwmbl->gpio_on = pdata->gpio_on; + + retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc); + if (retval) + goto err_free_mem; + + if (pwmbl->gpio_on != -1) { + retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl"); + if (retval) { + pwmbl->gpio_on = -1; + goto err_free_pwm; + } + + /* Turn display off by defatult. */ + retval = gpio_direction_output(pwmbl->gpio_on, + 0 ^ pdata->on_active_low); + if (retval) + goto err_free_gpio; + } + + bldev = backlight_device_register("atmel-pwm-bl", + &pdev->dev, pwmbl, &atmel_pwm_bl_ops); + if (IS_ERR(bldev)) { + retval = PTR_ERR(bldev); + goto err_free_gpio; + } + + pwmbl->bldev = bldev; + + platform_set_drvdata(pdev, pwmbl); + + /* Power up the backlight by default at middle intesity. */ + bldev->props.power = FB_BLANK_UNBLANK; + bldev->props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min; + bldev->props.brightness = bldev->props.max_brightness / 2; + + retval = atmel_pwm_bl_init_pwm(pwmbl); + if (retval) + goto err_free_bl_dev; + + atmel_pwm_bl_set_intensity(bldev); + + return 0; + +err_free_bl_dev: + platform_set_drvdata(pdev, NULL); + backlight_device_unregister(bldev); +err_free_gpio: + if (pwmbl->gpio_on != -1) + gpio_free(pwmbl->gpio_on); +err_free_pwm: + pwm_channel_free(&pwmbl->pwmc); +err_free_mem: + kfree(pwmbl); + return retval; +} + +static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) +{ + struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); + + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, 0); + gpio_free(pwmbl->gpio_on); + } + pwm_channel_disable(&pwmbl->pwmc); + pwm_channel_free(&pwmbl->pwmc); + backlight_device_unregister(pwmbl->bldev); + platform_set_drvdata(pdev, NULL); + kfree(pwmbl); + + return 0; +} + +static struct platform_driver atmel_pwm_bl_driver = { + .driver = { + .name = "atmel-pwm-bl", + }, + /* REVISIT add suspend() and resume() */ + .remove = __exit_p(atmel_pwm_bl_remove), +}; + +static int __init atmel_pwm_bl_init(void) +{ + return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe); +} +module_init(atmel_pwm_bl_init); + +static void __exit atmel_pwm_bl_exit(void) +{ + platform_driver_unregister(&atmel_pwm_bl_driver); +} +module_exit(atmel_pwm_bl_exit); + +MODULE_AUTHOR("Hans-Christian egtvedt "); +MODULE_DESCRIPTION("Atmel PWM backlight driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/atmel-pwm-bl.h b/include/linux/atmel-pwm-bl.h new file mode 100644 index 000000000000..0153a47806c2 --- /dev/null +++ b/include/linux/atmel-pwm-bl.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * 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 __INCLUDE_ATMEL_PWM_BL_H +#define __INCLUDE_ATMEL_PWM_BL_H + +/** + * struct atmel_pwm_bl_platform_data + * @pwm_channel: which PWM channel in the PWM module to use. + * @pwm_frequency: PWM frequency to generate, the driver will try to be as + * close as the prescaler allows. + * @pwm_compare_max: value to use in the PWM channel compare register. + * @pwm_duty_max: maximum duty cycle value, must be less than or equal to + * pwm_compare_max. + * @pwm_duty_min: minimum duty cycle value, must be less than pwm_duty_max. + * @pwm_active_low: set to one if the low part of the PWM signal increases the + * brightness of the backlight. + * @gpio_on: GPIO line to control the backlight on/off, set to -1 if not used. + * @on_active_low: set to one if the on/off signal is on when GPIO is low. + * + * This struct must be added to the platform device in the board code. It is + * used by the atmel-pwm-bl driver to setup the GPIO to control on/off and the + * PWM device. + */ +struct atmel_pwm_bl_platform_data { + unsigned int pwm_channel; + unsigned int pwm_frequency; + unsigned int pwm_compare_max; + unsigned int pwm_duty_max; + unsigned int pwm_duty_min; + unsigned int pwm_active_low; + int gpio_on; + unsigned int on_active_low; +}; + +#endif /* __INCLUDE_ATMEL_PWM_BL_H */ -- cgit v1.2.3 From 5bb49fcd501aa9fd3d321a22b7c01d9b0db7ab36 Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Wed, 23 Jul 2008 21:31:50 -0700 Subject: video/fb: cleanup FB_MAJOR usage Currently, linux/major.h defines a GRAPHDEV_MAJOR (29) that nobody uses, and linux/fb.h defines the real FB_MAJOR (also 29), that only fbmem.c needs. Drop GRAPHDEV_MAJOR from major.h, move FB_MAJOR definition from fb.h to major.h, and fix fbmem.c to use major.h's definition. Signed-off-by: Philippe De Muyter Cc: Krzysztof Helt Cc: "Antonino A. Daplas" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/fbmem.c | 1 + include/linux/fb.h | 1 - include/linux/major.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 6b487801eeae..5d84b3431098 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -35,6 +35,7 @@ #include #include #include +#include #include diff --git a/include/linux/fb.h b/include/linux/fb.h index a084d1335869..3b8870e32afd 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -8,7 +8,6 @@ struct dentry; /* Definitions of frame buffers */ -#define FB_MAJOR 29 #define FB_MAX 32 /* sufficient for now */ /* ioctls diff --git a/include/linux/major.h b/include/linux/major.h index 0cb98053537a..53d5fafd85c3 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -53,7 +53,7 @@ #define STL_SIOMEMMAJOR 28 #define ACSI_MAJOR 28 #define AZTECH_CDROM_MAJOR 29 -#define GRAPHDEV_MAJOR 29 /* SparcLinux & Linux/68k /dev/fb */ +#define FB_MAJOR 29 /* /dev/fb* framebuffers */ #define CM206_CDROM_MAJOR 32 #define IDE2_MAJOR 33 #define IDE3_MAJOR 34 -- cgit v1.2.3 From f9247273cb69ba101877e946d2d83044409cc8c5 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 24 Jul 2008 17:22:13 +0100 Subject: UFS: add const to parser token table This patch adds a "const" to the parser token table. I've done an allmodconfig build to see if this produces any warnings/failures and the patch includes a fix for the only warning that was produced. Signed-off-by: Steven Whitehouse Acked-by: Alexander Viro Acked-by: Evgeniy Dushistov Signed-off-by: Linus Torvalds --- fs/ufs/super.c | 2 +- include/linux/parser.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 85b22b5977fa..506f724055c2 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1232,7 +1232,7 @@ static int ufs_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct ufs_sb_info *sbi = UFS_SB(vfs->mnt_sb); unsigned mval = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE; - struct match_token *tp = tokens; + const struct match_token *tp = tokens; while (tp->token != Opt_onerror_panic && tp->token != mval) ++tp; diff --git a/include/linux/parser.h b/include/linux/parser.h index 7dcd05075756..cc554ca8bc78 100644 --- a/include/linux/parser.h +++ b/include/linux/parser.h @@ -14,7 +14,7 @@ struct match_token { const char *pattern; }; -typedef struct match_token match_table_t[]; +typedef const struct match_token match_table_t[]; /* Maximum number of arguments that match_token will find in a pattern */ enum {MAX_OPT_ARGS = 3}; -- cgit v1.2.3 From 6cdf6eb357c2681596b7b1672b92396ba82333d4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:14 +0200 Subject: ide: add ->dev and ->host_priv fields to struct ide_host * Add 'struct device *dev[2]' and 'void *host_priv' fields to struct ide_host. * Set ->dev[] in ide_host_alloc_all()/ide_setup_pci_device[s](). * Pass 'void *priv' argument to ide_setup_pci_device[s]() and use it to set ->host_priv. * Set PCI dev's ->driver_data to point to the struct ide_host instance if PCI host driver wants to use ->host_priv. * Rename ide_setup_pci_device[s]() to ide_pci_init_{one,two}(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 3 +++ drivers/ide/pci/aec62xx.c | 2 +- drivers/ide/pci/alim15x3.c | 2 +- drivers/ide/pci/amd74xx.c | 2 +- drivers/ide/pci/atiixp.c | 2 +- drivers/ide/pci/cmd64x.c | 2 +- drivers/ide/pci/cs5530.c | 2 +- drivers/ide/pci/cs5535.c | 2 +- drivers/ide/pci/cy82c693.c | 2 +- drivers/ide/pci/generic.c | 2 +- drivers/ide/pci/hpt34x.c | 2 +- drivers/ide/pci/hpt366.c | 4 ++-- drivers/ide/pci/it8213.c | 2 +- drivers/ide/pci/it821x.c | 2 +- drivers/ide/pci/jmicron.c | 2 +- drivers/ide/pci/ns87415.c | 2 +- drivers/ide/pci/opti621.c | 2 +- drivers/ide/pci/pdc202xx_new.c | 4 ++-- drivers/ide/pci/pdc202xx_old.c | 2 +- drivers/ide/pci/piix.c | 2 +- drivers/ide/pci/rz1000.c | 2 +- drivers/ide/pci/sc1200.c | 2 +- drivers/ide/pci/serverworks.c | 2 +- drivers/ide/pci/siimage.c | 2 +- drivers/ide/pci/sis5513.c | 2 +- drivers/ide/pci/sl82c105.c | 2 +- drivers/ide/pci/slc90e66.c | 2 +- drivers/ide/pci/tc86c001.c | 2 +- drivers/ide/pci/triflex.c | 2 +- drivers/ide/pci/trm290.c | 2 +- drivers/ide/pci/via82cxxx.c | 2 +- drivers/ide/setup-pci.c | 52 ++++++++++++++++++++++++++++++++++++------ include/linux/ide.h | 7 ++++-- 33 files changed, 85 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 4aa76c453755..890c15b1b3ae 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1604,6 +1604,9 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, return NULL; } + if (hws[0]) + host->dev[0] = hws[0]->dev; + return host; } EXPORT_SYMBOL_GPL(ide_host_alloc_all); diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index fbc43e121e6b..7a5d246fe9b1 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -273,7 +273,7 @@ static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_devi } } - err = ide_setup_pci_device(dev, &d); + err = ide_pci_init_one(dev, &d, NULL); if (err) pci_disable_device(dev); diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 5ef7817ac64f..7f96e7ca3864 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -565,7 +565,7 @@ static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_dev if (idx == 0) d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index ef7d971031ee..b6a475313c7c 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -302,7 +302,7 @@ static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_ d.name, pci_name(dev), dev->revision, amd_dma[fls(d.udma_mask) - 1]); - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id amd74xx_pci_tbl[] = { diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 8b637181681a..b483a68b39f6 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -167,7 +167,7 @@ static const struct ide_port_info atiixp_pci_info[] __devinitdata = { static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]); + return ide_pci_init_one(dev, &atiixp_pci_info[id->driver_data], NULL); } static const struct pci_device_id atiixp_pci_tbl[] = { diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index ce58bfcdb3c6..fc0333c9a4e5 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -507,7 +507,7 @@ static int __devinit cmd64x_init_one(struct pci_dev *dev, const struct pci_devic } } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id cmd64x_pci_tbl[] = { diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index f5534c1ff349..ba82bad8bf4e 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -256,7 +256,7 @@ static const struct ide_port_info cs5530_chipset __devinitdata = { static int __devinit cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &cs5530_chipset); + return ide_pci_init_one(dev, &cs5530_chipset, NULL); } static const struct pci_device_id cs5530_pci_tbl[] = { diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index 5404fe4f701d..2161f43ca1b8 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -180,7 +180,7 @@ static const struct ide_port_info cs5535_chipset __devinitdata = { static int __devinit cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &cs5535_chipset); + return ide_pci_init_one(dev, &cs5535_chipset, NULL); } static const struct pci_device_id cs5535_pci_tbl[] = { diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index e14ad5530fa4..abd27ed7c30c 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -419,7 +419,7 @@ static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_dev if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && PCI_FUNC(dev->devfn) == 1) { dev2 = pci_get_slot(dev->bus, dev->devfn + 1); - ret = ide_setup_pci_devices(dev, dev2, &cy82c693_chipset); + ret = ide_pci_init_two(dev, dev2, &cy82c693_chipset, NULL); /* We leak pci refs here but thats ok - we can't be unloaded */ } return ret; diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index 041720e22762..dd0caea5e4f3 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -139,7 +139,7 @@ static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_devi goto out; } } - ret = ide_setup_pci_device(dev, d); + ret = ide_pci_init_one(dev, d, NULL); out: return ret; } diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 9e1d1c4741da..3d70c5150ac6 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -156,7 +156,7 @@ static int __devinit hpt34x_init_one(struct pci_dev *dev, const struct pci_devic d = &hpt34x_chipsets[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0]; - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id hpt34x_pci_tbl[] = { diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 1f1135ce7cd6..b23b7a278005 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1608,13 +1608,13 @@ static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_devic d.host_flags &= ~IDE_HFLAG_NON_BOOTABLE; } - ret = ide_setup_pci_devices(dev, dev2, &d); + ret = ide_pci_init_two(dev, dev2, &d, NULL); if (ret < 0) pci_dev_put(dev2); return ret; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id hpt366_pci_tbl[] __devinitconst = { diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c index 2b71bdf74e73..18219fa9ef01 100644 --- a/drivers/ide/pci/it8213.c +++ b/drivers/ide/pci/it8213.c @@ -184,7 +184,7 @@ static const struct ide_port_info it8213_chipsets[] __devinitdata = { static int __devinit it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &it8213_chipsets[id->driver_data]); + return ide_pci_init_one(dev, &it8213_chipsets[id->driver_data], NULL); } static const struct pci_device_id it8213_pci_tbl[] = { diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index cbf647202994..40186f9e56aa 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -664,7 +664,7 @@ static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_devic pci_set_drvdata(dev, itdevs); - return ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]); + return ide_pci_init_one(dev, &it821x_chipsets[id->driver_data], NULL); } static const struct pci_device_id it821x_pci_tbl[] = { diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c index 96ef7394f283..a7e3c14f7b07 100644 --- a/drivers/ide/pci/jmicron.c +++ b/drivers/ide/pci/jmicron.c @@ -121,7 +121,7 @@ static const struct ide_port_info jmicron_chipset __devinitdata = { static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &jmicron_chipset); + return ide_pci_init_one(dev, &jmicron_chipset, NULL); } /* All JMB PATA controllers have and will continue to have the same diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 5cd2b32ff0ef..a45c33c0c792 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -324,7 +324,7 @@ static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_devi d.tp_ops = &superio_tp_ops; } #endif - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id ns87415_pci_tbl[] = { diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index 725c80508d90..edb9132ffbe4 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -209,7 +209,7 @@ static const struct ide_port_info opti621_chipset __devinitdata = { static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &opti621_chipset); + return ide_pci_init_one(dev, &opti621_chipset, NULL); } static const struct pci_device_id opti621_pci_tbl[] = { diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 070df8ab3b21..71a420feb981 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -524,7 +524,7 @@ static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_de dev2 = pdc20270_get_dev2(dev); if (dev2) { - int ret = ide_setup_pci_devices(dev, dev2, d); + int ret = ide_pci_init_two(dev, dev2, d, NULL); if (ret < 0) pci_dev_put(dev2); return ret; @@ -540,7 +540,7 @@ static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_de return -ENODEV; } - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id pdc202new_pci_tbl[] = { diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index e54dc653b8c4..eba1d60a73a0 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -412,7 +412,7 @@ static int __devinit pdc202xx_init_one(struct pci_dev *dev, const struct pci_dev } } - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id pdc202xx_pci_tbl[] = { diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 0ce41b4dddaf..359f65ddcbf9 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -394,7 +394,7 @@ static const struct ide_port_info piix_pci_info[] __devinitdata = { static int __devinit piix_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &piix_pci_info[id->driver_data]); + return ide_pci_init_one(dev, &piix_pci_info[id->driver_data], NULL); } /** diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c index 532154adba29..860ffdeca095 100644 --- a/drivers/ide/pci/rz1000.c +++ b/drivers/ide/pci/rz1000.c @@ -48,7 +48,7 @@ static const struct ide_port_info rz1000_chipset __devinitdata = { static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &rz1000_chipset); + return ide_pci_init_one(dev, &rz1000_chipset, NULL); } static const struct pci_device_id rz1000_pci_tbl[] = { diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 14c787b5d95f..8fd9cc2119d6 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -317,7 +317,7 @@ static const struct ide_port_info sc1200_chipset __devinitdata = { static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &sc1200_chipset); + return ide_pci_init_one(dev, &sc1200_chipset, NULL); } static const struct pci_device_id sc1200_pci_tbl[] = { diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index 127ccb45e261..34abdfc8d567 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -422,7 +422,7 @@ static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device d.host_flags &= ~IDE_HFLAG_SINGLE; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id svwks_pci_tbl[] = { diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 5965a35d94ae..48124133601a 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -795,7 +795,7 @@ static int __devinit siimage_init_one(struct pci_dev *dev, d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id siimage_pci_tbl[] = { diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 2389945ca95d..a2330c4ac75b 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -583,7 +583,7 @@ static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_devi d.udma_mask = udma_rates[chipset_family]; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id sis5513_pci_tbl[] = { diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index f82a6502c1b7..be22f8125d71 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -335,7 +335,7 @@ static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_dev d.host_flags &= ~IDE_HFLAG_SERIALIZE_DMA; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id sl82c105_pci_tbl[] = { diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c index dae6e2c94d86..2fc2f2cf2206 100644 --- a/drivers/ide/pci/slc90e66.c +++ b/drivers/ide/pci/slc90e66.c @@ -144,7 +144,7 @@ static const struct ide_port_info slc90e66_chipset __devinitdata = { static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &slc90e66_chipset); + return ide_pci_init_one(dev, &slc90e66_chipset, NULL); } static const struct pci_device_id slc90e66_pci_tbl[] = { diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index 477e19790102..e16e79d21772 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -215,7 +215,7 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = { static int __devinit tc86c001_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &tc86c001_chipset); + return ide_pci_init_one(dev, &tc86c001_chipset, NULL); } static const struct pci_device_id tc86c001_pci_tbl[] = { diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c index db65a558d4ec..60dcb645d1b0 100644 --- a/drivers/ide/pci/triflex.c +++ b/drivers/ide/pci/triflex.c @@ -104,7 +104,7 @@ static const struct ide_port_info triflex_device __devinitdata = { static int __devinit triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &triflex_device); + return ide_pci_init_one(dev, &triflex_device, NULL); } static const struct pci_device_id triflex_pci_tbl[] = { diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index a8a3138682ef..d8127b51a542 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -340,7 +340,7 @@ static const struct ide_port_info trm290_chipset __devinitdata = { static int __devinit trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &trm290_chipset); + return ide_pci_init_one(dev, &trm290_chipset, NULL); } static const struct pci_device_id trm290_pci_tbl[] = { diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 09dc4803ef9d..2f22abfe003b 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -466,7 +466,7 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i d.udma_mask = via_config->udma_mask; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id via_pci_tbl[] = { diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index b85de71fdc88..ca17bf8896df 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -525,8 +525,10 @@ out: return ret; } -int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) +int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d, + void *priv) { + struct ide_host *host; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; @@ -536,6 +538,19 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) ide_pci_setup_ports(dev, d, 0, &hw[0], &hws[0]); + host = ide_host_alloc(d, hws); + if (host == NULL) { + ret = -ENOMEM; + goto out; + } + + host->dev[0] = &dev->dev; + + host->host_priv = priv; + + if (priv) + pci_set_drvdata(dev, host); + ret = do_ide_setup_pci_device(dev, d, 1); if (ret < 0) goto out; @@ -543,16 +558,19 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) /* fixup IRQ */ hw[1].irq = hw[0].irq = ret; - ret = ide_host_add(d, hws, NULL); + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); out: return ret; } -EXPORT_SYMBOL_GPL(ide_setup_pci_device); +EXPORT_SYMBOL_GPL(ide_pci_init_one); -int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, - const struct ide_port_info *d) +int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2, + const struct ide_port_info *d, void *priv) { struct pci_dev *pdev[] = { dev1, dev2 }; + struct ide_host *host; int ret, i; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; @@ -562,7 +580,25 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, goto out; ide_pci_setup_ports(pdev[i], d, 0, &hw[i*2], &hws[i*2]); + } + host = ide_host_alloc(d, hws); + if (host == NULL) { + ret = -ENOMEM; + goto out; + } + + host->dev[0] = &dev1->dev; + host->dev[1] = &dev2->dev; + + host->host_priv = priv; + + if (priv) { + pci_set_drvdata(pdev[0], host); + pci_set_drvdata(pdev[1], host); + } + + for (i = 0; i < 2; i++) { ret = do_ide_setup_pci_device(pdev[i], d, !i); /* @@ -576,8 +612,10 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, hw[i*2 + 1].irq = hw[i*2].irq = ret; } - ret = ide_host_add(d, hws, NULL); + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); out: return ret; } -EXPORT_SYMBOL_GPL(ide_setup_pci_devices); +EXPORT_SYMBOL_GPL(ide_pci_init_two); diff --git a/include/linux/ide.h b/include/linux/ide.h index d67ccca2b964..776c574c9640 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -626,6 +626,8 @@ typedef struct hwif_s { struct ide_host { ide_hwif_t *ports[MAX_HWIFS]; unsigned int n_ports; + struct device *dev[2]; + void *host_priv; }; /* @@ -1201,8 +1203,9 @@ struct ide_port_info { u8 udma_mask; }; -int ide_setup_pci_device(struct pci_dev *, const struct ide_port_info *); -int ide_setup_pci_devices(struct pci_dev *, struct pci_dev *, const struct ide_port_info *); +int ide_pci_init_one(struct pci_dev *, const struct ide_port_info *, void *); +int ide_pci_init_two(struct pci_dev *, struct pci_dev *, + const struct ide_port_info *, void *); void ide_map_sg(ide_drive_t *, struct request *); void ide_init_sg_cmd(ide_drive_t *, struct request *); -- cgit v1.2.3 From 08da591e14cf87247ec09b17c350235157a92fc3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:15 +0200 Subject: ide: add ide_device_{get,put}() helpers * Add 'struct ide_host *host' field to ide_hwif_t and set it in ide_host_alloc_all(). * Add ide_device_{get,put}() helpers loosely based on SCSI's scsi_device_{get,put}() ones. * Convert IDE device drivers to use ide_device_{get,put}(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 12 +++++++++--- drivers/ide/ide-disk.c | 12 +++++++++--- drivers/ide/ide-floppy.c | 12 +++++++++--- drivers/ide/ide-probe.c | 2 ++ drivers/ide/ide-tape.c | 12 +++++++++--- drivers/ide/ide.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/ide-scsi.c | 8 +++++++- include/linux/ide.h | 7 +++++++ 8 files changed, 99 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 4e73aeee4053..8f253e5f26a8 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -57,23 +57,29 @@ static DEFINE_MUTEX(idecd_ref_mutex); #define ide_cd_g(disk) \ container_of((disk)->private_data, struct cdrom_info, driver) +static void ide_cd_release(struct kref *); + static struct cdrom_info *ide_cd_get(struct gendisk *disk) { struct cdrom_info *cd = NULL; mutex_lock(&idecd_ref_mutex); cd = ide_cd_g(disk); - if (cd) + if (cd) { kref_get(&cd->kref); + if (ide_device_get(cd->drive)) { + kref_put(&cd->kref, ide_cd_release); + cd = NULL; + } + } mutex_unlock(&idecd_ref_mutex); return cd; } -static void ide_cd_release(struct kref *); - static void ide_cd_put(struct cdrom_info *cd) { mutex_lock(&idecd_ref_mutex); + ide_device_put(cd->drive); kref_put(&cd->kref, ide_cd_release); mutex_unlock(&idecd_ref_mutex); } diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index df5fe5756871..28d85b410f7c 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -56,23 +56,29 @@ static DEFINE_MUTEX(idedisk_ref_mutex); #define ide_disk_g(disk) \ container_of((disk)->private_data, struct ide_disk_obj, driver) +static void ide_disk_release(struct kref *); + static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) { struct ide_disk_obj *idkp = NULL; mutex_lock(&idedisk_ref_mutex); idkp = ide_disk_g(disk); - if (idkp) + if (idkp) { kref_get(&idkp->kref); + if (ide_device_get(idkp->drive)) { + kref_put(&idkp->kref, ide_disk_release); + idkp = NULL; + } + } mutex_unlock(&idedisk_ref_mutex); return idkp; } -static void ide_disk_release(struct kref *); - static void ide_disk_put(struct ide_disk_obj *idkp) { mutex_lock(&idedisk_ref_mutex); + ide_device_put(idkp->drive); kref_put(&idkp->kref, ide_disk_release); mutex_unlock(&idedisk_ref_mutex); } diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 3d8e6dd0f41e..ca11a26746f1 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -158,23 +158,29 @@ static DEFINE_MUTEX(idefloppy_ref_mutex); #define ide_floppy_g(disk) \ container_of((disk)->private_data, struct ide_floppy_obj, driver) +static void idefloppy_cleanup_obj(struct kref *); + static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk) { struct ide_floppy_obj *floppy = NULL; mutex_lock(&idefloppy_ref_mutex); floppy = ide_floppy_g(disk); - if (floppy) + if (floppy) { kref_get(&floppy->kref); + if (ide_device_get(floppy->drive)) { + kref_put(&floppy->kref, idefloppy_cleanup_obj); + floppy = NULL; + } + } mutex_unlock(&idefloppy_ref_mutex); return floppy; } -static void idefloppy_cleanup_obj(struct kref *); - static void ide_floppy_put(struct ide_floppy_obj *floppy) { mutex_lock(&idefloppy_ref_mutex); + ide_device_put(floppy->drive); kref_put(&floppy->kref, idefloppy_cleanup_obj); mutex_unlock(&idefloppy_ref_mutex); } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 890c15b1b3ae..9ab5892eaea1 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1595,6 +1595,8 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, ide_init_port_data(hwif, idx); + hwif->host = host; + host->ports[i] = hwif; host->n_ports++; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 6962ca4891a1..789f3428f072 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -322,23 +322,29 @@ static struct class *idetape_sysfs_class; #define ide_tape_g(disk) \ container_of((disk)->private_data, struct ide_tape_obj, driver) +static void ide_tape_release(struct kref *); + static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) { struct ide_tape_obj *tape = NULL; mutex_lock(&idetape_ref_mutex); tape = ide_tape_g(disk); - if (tape) + if (tape) { kref_get(&tape->kref); + if (ide_device_get(tape->drive)) { + kref_put(&tape->kref, ide_tape_release); + tape = NULL; + } + } mutex_unlock(&idetape_ref_mutex); return tape; } -static void ide_tape_release(struct kref *); - static void ide_tape_put(struct ide_tape_obj *tape) { mutex_lock(&idetape_ref_mutex); + ide_device_put(tape->drive); kref_put(&tape->kref, ide_tape_release); mutex_unlock(&idetape_ref_mutex); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 60f0ca66aa93..772451600e4d 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -618,6 +618,53 @@ set_val: EXPORT_SYMBOL(generic_ide_ioctl); +/** + * ide_device_get - get an additional reference to a ide_drive_t + * @drive: device to get a reference to + * + * Gets a reference to the ide_drive_t and increments the use count of the + * underlying LLDD module. + */ +int ide_device_get(ide_drive_t *drive) +{ + struct device *host_dev; + struct module *module; + + if (!get_device(&drive->gendev)) + return -ENXIO; + + host_dev = drive->hwif->host->dev[0]; + module = host_dev ? host_dev->driver->owner : NULL; + + if (module && !try_module_get(module)) { + put_device(&drive->gendev); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_device_get); + +/** + * ide_device_put - release a reference to a ide_drive_t + * @drive: device to release a reference on + * + * Release a reference to the ide_drive_t and decrements the use count of + * the underlying LLDD module. + */ +void ide_device_put(ide_drive_t *drive) +{ +#ifdef CONFIG_MODULE_UNLOAD + struct device *host_dev = drive->hwif->host->dev[0]; + struct module *module = host_dev ? host_dev->driver->owner : NULL; + + if (module) + module_put(module); +#endif + put_device(&drive->gendev); +} +EXPORT_SYMBOL_GPL(ide_device_put); + static int ide_bus_match(struct device *dev, struct device_driver *drv) { return 1; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 538552495d48..318ef382448f 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -101,8 +101,13 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) mutex_lock(&idescsi_ref_mutex); scsi = ide_scsi_g(disk); - if (scsi) + if (scsi) { scsi_host_get(scsi->host); + if (ide_device_get(scsi->drive)) { + scsi_host_put(scsi->host); + scsi = NULL; + } + } mutex_unlock(&idescsi_ref_mutex); return scsi; } @@ -110,6 +115,7 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) static void ide_scsi_put(struct ide_scsi_obj *scsi) { mutex_lock(&idescsi_ref_mutex); + ide_device_put(scsi->drive); scsi_host_put(scsi->host); mutex_unlock(&idescsi_ref_mutex); } diff --git a/include/linux/ide.h b/include/linux/ide.h index 776c574c9640..3eccac0a2a36 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -532,12 +532,16 @@ struct ide_dma_ops { void (*dma_timeout)(struct ide_drive_s *); }; +struct ide_host; + typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ struct hwif_s *mate; /* other hwif from same PCI chip */ struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ + struct ide_host *host; + char name[6]; /* name of interface, eg. "ide0" */ struct ide_io_ports io_ports; @@ -876,6 +880,9 @@ struct ide_driver_s { #define to_ide_driver(drv) container_of(drv, ide_driver_t, gen_driver) +int ide_device_get(ide_drive_t *); +void ide_device_put(ide_drive_t *); + int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsigned, unsigned long); extern int ide_vlb_clk; -- cgit v1.2.3 From ef0b04276d8f719d754c092434fbd62c2aeb5307 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:19 +0200 Subject: ide: add ide_pci_remove() helper * Add 'unsigned long host_flags' field to struct ide_host. * Set ->host_flags in ide_host_alloc_all(). * Always set PCI dev's ->driver_data in ide_pci_init_{one,two}(). * Add ide_pci_remove() helper (the default implementation for struct pci_driver's ->remove method). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 3 +++ drivers/ide/setup-pci.c | 39 +++++++++++++++++++++++++++++++++------ include/linux/ide.h | 2 ++ 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9ab5892eaea1..f0c162488ec4 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1609,6 +1609,9 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, if (hws[0]) host->dev[0] = hws[0]->dev; + if (d) + host->host_flags = d->host_flags; + return host; } EXPORT_SYMBOL_GPL(ide_host_alloc_all); diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index ca17bf8896df..20f0ee004695 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -548,8 +548,7 @@ int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d, host->host_priv = priv; - if (priv) - pci_set_drvdata(dev, host); + pci_set_drvdata(dev, host); ret = do_ide_setup_pci_device(dev, d, 1); if (ret < 0) @@ -593,10 +592,8 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2, host->host_priv = priv; - if (priv) { - pci_set_drvdata(pdev[0], host); - pci_set_drvdata(pdev[1], host); - } + pci_set_drvdata(pdev[0], host); + pci_set_drvdata(pdev[1], host); for (i = 0; i < 2; i++) { ret = do_ide_setup_pci_device(pdev[i], d, !i); @@ -619,3 +616,33 @@ out: return ret; } EXPORT_SYMBOL_GPL(ide_pci_init_two); + +void ide_pci_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + int bars; + + if (host->host_flags & IDE_HFLAG_SINGLE) + bars = (1 << 2) - 1; + else + bars = (1 << 4) - 1; + + if ((host->host_flags & IDE_HFLAG_NO_DMA) == 0) { + if (host->host_flags & IDE_HFLAG_CS5520) + bars |= (1 << 2); + else + bars |= (1 << 4); + } + + ide_host_remove(host); + + if (dev2) + pci_release_selected_regions(dev2, bars); + pci_release_selected_regions(dev, bars); + + if (dev2) + pci_disable_device(dev2); + pci_disable_device(dev); +} +EXPORT_SYMBOL_GPL(ide_pci_remove); diff --git a/include/linux/ide.h b/include/linux/ide.h index 3eccac0a2a36..dbd0aeb3a56d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -631,6 +631,7 @@ struct ide_host { ide_hwif_t *ports[MAX_HWIFS]; unsigned int n_ports; struct device *dev[2]; + unsigned long host_flags; void *host_priv; }; @@ -1213,6 +1214,7 @@ struct ide_port_info { int ide_pci_init_one(struct pci_dev *, const struct ide_port_info *, void *); int ide_pci_init_two(struct pci_dev *, struct pci_dev *, const struct ide_port_info *, void *); +void ide_pci_remove(struct pci_dev *); void ide_map_sg(ide_drive_t *, struct request *); void ide_init_sg_cmd(ide_drive_t *, struct request *); -- cgit v1.2.3 From d83b8b85cd56a083d30df73f3fd5e4714591b910 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:30 +0200 Subject: ide: define MAX_HWIFS in * Now that ide_hwif_t instances are allocated dynamically the difference between MAX_HWIFS == 2 and MAX_HWIFS == 10 is ~100 bytes (x86-32) so use MAX_HWIFS == 10 on all archs except these ones that use MAX_HWIFS == 1. * Define MAX_HWIFS in instead of . [ Please note that avr32/cris/v850 have no and alpha/ia64/sh always define CONFIG_IDE_MAX_HWIFS. ] Signed-off-by: Bartlomiej Zolnierkiewicz --- include/asm-arm/ide.h | 4 ---- include/asm-blackfin/ide.h | 2 -- include/asm-frv/ide.h | 4 ---- include/asm-h8300/ide.h | 2 -- include/asm-m32r/ide.h | 8 -------- include/asm-m68k/ide.h | 4 ---- include/asm-mips/mach-generic/ide.h | 8 -------- include/asm-mn10300/ide.h | 4 ---- include/asm-parisc/ide.h | 4 ---- include/asm-powerpc/ide.h | 8 -------- include/asm-sparc/ide.h | 3 --- include/asm-x86/ide.h | 9 --------- include/asm-xtensa/ide.h | 5 ----- include/linux/ide.h | 8 ++++++++ 14 files changed, 8 insertions(+), 65 deletions(-) (limited to 'include/linux') diff --git a/include/asm-arm/ide.h b/include/asm-arm/ide.h index 88f4d231ce4f..a48019f99d08 100644 --- a/include/asm-arm/ide.h +++ b/include/asm-arm/ide.h @@ -13,10 +13,6 @@ #ifdef __KERNEL__ -#ifndef MAX_HWIFS -#define MAX_HWIFS 4 -#endif - #define __ide_mm_insw(port,addr,len) readsw(port,addr,len) #define __ide_mm_insl(port,addr,len) readsl(port,addr,len) #define __ide_mm_outsw(port,addr,len) writesw(port,addr,len) diff --git a/include/asm-blackfin/ide.h b/include/asm-blackfin/ide.h index 5b88de115bf4..90bc50bd22e9 100644 --- a/include/asm-blackfin/ide.h +++ b/include/asm-blackfin/ide.h @@ -17,8 +17,6 @@ #ifdef __KERNEL__ /****************************************************************************/ -#define MAX_HWIFS 1 - #include /****************************************************************************/ diff --git a/include/asm-frv/ide.h b/include/asm-frv/ide.h index 8c9a540d4344..7ebcc56a2229 100644 --- a/include/asm-frv/ide.h +++ b/include/asm-frv/ide.h @@ -18,10 +18,6 @@ #include #include -#ifndef MAX_HWIFS -#define MAX_HWIFS 8 -#endif - /****************************************************************************/ /* * some bits needed for parts of the IDE subsystem to compile diff --git a/include/asm-h8300/ide.h b/include/asm-h8300/ide.h index f8535ce7476e..8f79ba2ff929 100644 --- a/include/asm-h8300/ide.h +++ b/include/asm-h8300/ide.h @@ -16,8 +16,6 @@ #ifdef __KERNEL__ /****************************************************************************/ -#define MAX_HWIFS 1 - #include /****************************************************************************/ diff --git a/include/asm-m32r/ide.h b/include/asm-m32r/ide.h index 72798d624221..d755d41b9931 100644 --- a/include/asm-m32r/ide.h +++ b/include/asm-m32r/ide.h @@ -15,14 +15,6 @@ #include -#ifndef MAX_HWIFS -# ifdef CONFIG_BLK_DEV_IDEPCI -#define MAX_HWIFS 10 -# else -#define MAX_HWIFS 2 -# endif -#endif - static __inline__ int ide_default_irq(unsigned long base) { switch (base) { diff --git a/include/asm-m68k/ide.h b/include/asm-m68k/ide.h index 909c6dfd3851..1daf6cbdd9f0 100644 --- a/include/asm-m68k/ide.h +++ b/include/asm-m68k/ide.h @@ -45,10 +45,6 @@ #include #endif -#ifndef MAX_HWIFS -#define MAX_HWIFS 4 /* same as the other archs */ -#endif - /* * Get rid of defs from io.h - ide has its private and conflicting versions * Since so far no single m68k platform uses ISA/PCI I/O space for IDE, we diff --git a/include/asm-mips/mach-generic/ide.h b/include/asm-mips/mach-generic/ide.h index f34740ee6775..8ee6bff030d9 100644 --- a/include/asm-mips/mach-generic/ide.h +++ b/include/asm-mips/mach-generic/ide.h @@ -19,14 +19,6 @@ #include #include -#ifndef MAX_HWIFS -# ifdef CONFIG_BLK_DEV_IDEPCI -#define MAX_HWIFS 10 -# else -#define MAX_HWIFS 6 -# endif -#endif - static __inline__ int ide_probe_legacy(void) { #ifdef CONFIG_PCI diff --git a/include/asm-mn10300/ide.h b/include/asm-mn10300/ide.h index dc235121ec42..6adcdd92e83d 100644 --- a/include/asm-mn10300/ide.h +++ b/include/asm-mn10300/ide.h @@ -23,10 +23,6 @@ #undef SUPPORT_VLB_SYNC #define SUPPORT_VLB_SYNC 0 -#ifndef MAX_HWIFS -#define MAX_HWIFS 8 -#endif - /* * some bits needed for parts of the IDE subsystem to compile */ diff --git a/include/asm-parisc/ide.h b/include/asm-parisc/ide.h index db0c94410095..c246ef75017d 100644 --- a/include/asm-parisc/ide.h +++ b/include/asm-parisc/ide.h @@ -13,10 +13,6 @@ #ifdef __KERNEL__ -#ifndef MAX_HWIFS -#define MAX_HWIFS 2 -#endif - #define ide_request_irq(irq,hand,flg,dev,id) request_irq((irq),(hand),(flg),(dev),(id)) #define ide_free_irq(irq,dev_id) free_irq((irq), (dev_id)) #define ide_request_region(from,extent,name) request_region((from), (extent), (name)) diff --git a/include/asm-powerpc/ide.h b/include/asm-powerpc/ide.h index 3d90bf7d3d73..262def6a9f0a 100644 --- a/include/asm-powerpc/ide.h +++ b/include/asm-powerpc/ide.h @@ -14,14 +14,6 @@ #endif #include -#ifndef MAX_HWIFS -#ifdef __powerpc64__ -#define MAX_HWIFS 10 -#else -#define MAX_HWIFS 8 -#endif -#endif - #define __ide_mm_insw(p, a, c) readsw((void __iomem *)(p), (a), (c)) #define __ide_mm_insl(p, a, c) readsl((void __iomem *)(p), (a), (c)) #define __ide_mm_outsw(p, a, c) writesw((void __iomem *)(p), (a), (c)) diff --git a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h index 879fcec72dc1..b7af3d658239 100644 --- a/include/asm-sparc/ide.h +++ b/include/asm-sparc/ide.h @@ -21,9 +21,6 @@ #include #endif -#undef MAX_HWIFS -#define MAX_HWIFS 2 - #define __ide_insl(data_reg, buffer, wcount) \ __ide_insw(data_reg, buffer, (wcount)<<1) #define __ide_outsl(data_reg, buffer, wcount) \ diff --git a/include/asm-x86/ide.h b/include/asm-x86/ide.h index 34050747f38c..bc54879daed1 100644 --- a/include/asm-x86/ide.h +++ b/include/asm-x86/ide.h @@ -11,15 +11,6 @@ #ifdef __KERNEL__ - -#ifndef MAX_HWIFS -# ifdef CONFIG_BLK_DEV_IDEPCI -#define MAX_HWIFS 10 -# else -#define MAX_HWIFS 6 -# endif -#endif - static __inline__ int ide_default_irq(unsigned long base) { switch (base) { diff --git a/include/asm-xtensa/ide.h b/include/asm-xtensa/ide.h index cb995701c42d..18342a2cc774 100644 --- a/include/asm-xtensa/ide.h +++ b/include/asm-xtensa/ide.h @@ -14,11 +14,6 @@ #ifdef __KERNEL__ - -#ifndef MAX_HWIFS -# define MAX_HWIFS 1 -#endif - #include #endif /* __KERNEL__ */ diff --git a/include/linux/ide.h b/include/linux/ide.h index dbd0aeb3a56d..76fe00b24b51 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -213,6 +213,14 @@ static inline int __ide_default_irq(unsigned long base) #include +#ifndef MAX_HWIFS +#if defined(CONFIG_BLACKFIN) || defined(CONFIG_H8300) || defined(CONFIG_XTENSA) +# define MAX_HWIFS 1 +#else +# define MAX_HWIFS 10 +#endif +#endif + #if !defined(MAX_HWIFS) || defined(CONFIG_EMBEDDED) #undef MAX_HWIFS #define MAX_HWIFS CONFIG_IDE_MAX_HWIFS -- cgit v1.2.3 From 2a8f7450f828eaee49d66f41f99ac2e54f1160a6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:31 +0200 Subject: ide: remove for some archs * Remove include from ( includes which is enough). * Remove for alpha/blackfin/h8300/ia64/m32r/sh/x86/xtensa (this leaves us with arm/frv/m68k/mips/mn10300/parisc/powerpc/sparc[64]). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- include/asm-alpha/ide.h | 20 -------------------- include/asm-blackfin/ide.h | 25 ------------------------- include/asm-h8300/ide.h | 24 ------------------------ include/asm-ia64/ide.h | 22 ---------------------- include/asm-m32r/ide.h | 20 -------------------- include/asm-sh/ide.h | 21 --------------------- include/asm-x86/ide.h | 18 ------------------ include/asm-xtensa/ide.h | 21 --------------------- include/linux/ide.h | 6 ++++++ 9 files changed, 6 insertions(+), 171 deletions(-) delete mode 100644 include/asm-alpha/ide.h delete mode 100644 include/asm-blackfin/ide.h delete mode 100644 include/asm-h8300/ide.h delete mode 100644 include/asm-ia64/ide.h delete mode 100644 include/asm-m32r/ide.h delete mode 100644 include/asm-sh/ide.h delete mode 100644 include/asm-x86/ide.h delete mode 100644 include/asm-xtensa/ide.h (limited to 'include/linux') diff --git a/include/asm-alpha/ide.h b/include/asm-alpha/ide.h deleted file mode 100644 index 55f9f6870249..000000000000 --- a/include/asm-alpha/ide.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/include/asm-alpha/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the alpha architecture specific IDE code. - */ - -#ifndef __ASMalpha_IDE_H -#define __ASMalpha_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASMalpha_IDE_H */ diff --git a/include/asm-blackfin/ide.h b/include/asm-blackfin/ide.h deleted file mode 100644 index 90bc50bd22e9..000000000000 --- a/include/asm-blackfin/ide.h +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************/ - -/* - * linux/include/asm-blackfin/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - * Copyright (C) 2001 Lineo Inc., davidm@snapgear.com - * Copyright (C) 2002 Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 2002 Yoshinori Sato (ysato@users.sourceforge.jp) - * Copyright (C) 2005 Hennerich Michael (hennerich@blackfin.uclinux.org) - */ - -/****************************************************************************/ -#ifndef _BLACKFIN_IDE_H -#define _BLACKFIN_IDE_H -/****************************************************************************/ -#ifdef __KERNEL__ -/****************************************************************************/ - -#include - -/****************************************************************************/ -#endif /* __KERNEL__ */ -#endif /* _BLACKFIN_IDE_H */ -/****************************************************************************/ diff --git a/include/asm-h8300/ide.h b/include/asm-h8300/ide.h deleted file mode 100644 index 8f79ba2ff929..000000000000 --- a/include/asm-h8300/ide.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************/ - -/* - * linux/include/asm-h8300/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - * Copyright (C) 2001 Lineo Inc., davidm@snapgear.com - * Copyright (C) 2002 Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 2002 Yoshinori Sato (ysato@users.sourceforge.jp) - */ - -/****************************************************************************/ -#ifndef _H8300_IDE_H -#define _H8300_IDE_H -/****************************************************************************/ -#ifdef __KERNEL__ -/****************************************************************************/ - -#include - -/****************************************************************************/ -#endif /* __KERNEL__ */ -#endif /* _H8300_IDE_H */ -/****************************************************************************/ diff --git a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h deleted file mode 100644 index 5a0aedea4764..000000000000 --- a/include/asm-ia64/ide.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * linux/include/asm-ia64/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the ia64 architecture specific IDE code. - */ - -#ifndef __ASM_IA64_IDE_H -#define __ASM_IA64_IDE_H - -#ifdef __KERNEL__ - -#include - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASM_IA64_IDE_H */ diff --git a/include/asm-m32r/ide.h b/include/asm-m32r/ide.h deleted file mode 100644 index 0f1ec6973879..000000000000 --- a/include/asm-m32r/ide.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_M32R_IDE_H -#define _ASM_M32R_IDE_H - -/* - * linux/include/asm-m32r/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the i386 architecture specific IDE code. - */ - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* _ASM_M32R_IDE_H */ diff --git a/include/asm-sh/ide.h b/include/asm-sh/ide.h deleted file mode 100644 index 58e0bdd52be4..000000000000 --- a/include/asm-sh/ide.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * linux/include/asm-sh/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the i386 architecture specific IDE code. - * In future, SuperH code. - */ - -#ifndef __ASM_SH_IDE_H -#define __ASM_SH_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASM_SH_IDE_H */ diff --git a/include/asm-x86/ide.h b/include/asm-x86/ide.h deleted file mode 100644 index 0289baf9ce0a..000000000000 --- a/include/asm-x86/ide.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the i386 architecture specific IDE code. - */ - -#ifndef __ASMi386_IDE_H -#define __ASMi386_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASMi386_IDE_H */ diff --git a/include/asm-xtensa/ide.h b/include/asm-xtensa/ide.h deleted file mode 100644 index 18342a2cc774..000000000000 --- a/include/asm-xtensa/ide.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * include/asm-xtensa/ide.h - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1994 - 1996 Linus Torvalds & authors - * Copyright (C) 2001 - 2005 Tensilica Inc. - */ - -#ifndef _XTENSA_IDE_H -#define _XTENSA_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* _XTENSA_IDE_H */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 76fe00b24b51..fd78b401b036 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -211,7 +211,13 @@ static inline int __ide_default_irq(unsigned long base) return 0; } +#if defined(CONFIG_ARM) || defined(CONFIG_FRV) || defined(CONFIG_M68K) || \ + defined(CONFIG_MIPS) || defined(CONFIG_MN10300) || defined(CONFIG_PARISC) \ + || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || defined(CONFIG_SPARC64) #include +#else +#include +#endif #ifndef MAX_HWIFS #if defined(CONFIG_BLACKFIN) || defined(CONFIG_H8300) || defined(CONFIG_XTENSA) -- cgit v1.2.3 From a326b02b0c576001353dbc489154959b0889c6bf Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:33 +0200 Subject: ide: drop 'name' parameter from ->init_chipset method There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pci/aec62xx.c | 2 +- drivers/ide/pci/alim15x3.c | 5 ++--- drivers/ide/pci/amd74xx.c | 19 ++++++++----------- drivers/ide/pci/cmd64x.c | 2 +- drivers/ide/pci/cs5530.c | 7 +++---- drivers/ide/pci/cy82c693.c | 10 +++++----- drivers/ide/pci/hpt34x.c | 2 +- drivers/ide/pci/hpt366.c | 3 ++- drivers/ide/pci/it821x.c | 2 +- drivers/ide/pci/pdc202xx_new.c | 3 ++- drivers/ide/pci/pdc202xx_old.c | 3 +-- drivers/ide/pci/piix.c | 3 +-- drivers/ide/pci/serverworks.c | 6 +++--- drivers/ide/pci/siimage.c | 8 +++----- drivers/ide/pci/sis5513.c | 3 +-- drivers/ide/pci/sl82c105.c | 2 +- drivers/ide/pci/via82cxxx.c | 3 +-- drivers/ide/setup-pci.c | 2 +- include/linux/ide.h | 2 +- 19 files changed, 39 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index f6dc6c20f3af..e0c8fe7d9fea 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -140,7 +140,7 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0); } -static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev) { /* These are necessary to get AEC6280 Macintosh cards to work */ if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) || diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index a099c4dd599d..b582687e0cd4 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -209,13 +209,12 @@ static int ali15x3_dma_setup(ide_drive_t *drive) /** * init_chipset_ali15x3 - Initialise an ALi IDE controller * @dev: PCI device - * @name: Name of the controller * * This function initializes the ALI IDE controller and where * appropriate also sets up the 1533 southbridge. */ - -static unsigned int __devinit init_chipset_ali15x3 (struct pci_dev *dev, const char *name) + +static unsigned int __devinit init_chipset_ali15x3(struct pci_dev *dev) { unsigned long flags; u8 tmpbyte; diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index cbf78edfe00b..2cea7bf51a0f 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -112,15 +112,13 @@ static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio) amd_set_drive(drive, XFER_PIO_0 + pio); } -static void __devinit amd7409_cable_detect(struct pci_dev *dev, - const char *name) +static void __devinit amd7409_cable_detect(struct pci_dev *dev) { /* no host side cable detection */ amd_80w = 0x03; } -static void __devinit amd7411_cable_detect(struct pci_dev *dev, - const char *name) +static void __devinit amd7411_cable_detect(struct pci_dev *dev) { int i; u32 u = 0; @@ -131,9 +129,9 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev, amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0); for (i = 24; i >= 0; i -= 8) if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) { - printk(KERN_WARNING "%s %s: BIOS didn't set cable bits " - "correctly. Enabling workaround.\n", - name, pci_name(dev)); + printk(KERN_WARNING DRV_NAME " %s: BIOS didn't set " + "cable bits correctly. Enabling workaround.\n", + pci_name(dev)); amd_80w |= (1 << (1 - (i >> 4))); } } @@ -142,8 +140,7 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev, * The initialization callback. Initialize drive independent registers. */ -static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev) { u8 t = 0, offset = amd_offset(dev); @@ -156,9 +153,9 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, ; /* no UDMA > 2 */ else if (dev->vendor == PCI_VENDOR_ID_AMD && dev->device == PCI_DEVICE_ID_AMD_VIPER_7409) - amd7409_cable_detect(dev, name); + amd7409_cable_detect(dev); else - amd7411_cable_detect(dev, name); + amd7411_cable_detect(dev); /* * Take care of prefetch & postwrite. diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index 3d84debaf81f..1360b4fa9fd3 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -332,7 +332,7 @@ static int cmd646_1_dma_end(ide_drive_t *drive) return (dma_stat & 7) != 4; } -static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev) { u8 mrdmode = 0; diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index 5543c8677a5a..f235db8c678b 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -129,12 +129,11 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode) /** * init_chipset_5530 - set up 5530 bridge * @dev: PCI device - * @name: device name * * Initialize the cs5530 bridge for reliable IDE DMA operation. */ -static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cs5530(struct pci_dev *dev) { struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; @@ -153,11 +152,11 @@ static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const ch } } if (!master_0) { - printk(KERN_ERR "%s: unable to locate PCI MASTER function\n", name); + printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n"); goto out; } if (!cs5530_0) { - printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name); + printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n"); goto out; } diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index 41c7f3351eb6..bfae2f882f48 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -332,7 +332,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) /* * this function is called during init and is used to setup the cy82c693 chip */ -static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev) { if (PCI_FUNC(dev->devfn) != 1) return 0; @@ -351,8 +351,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c data = inb(CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", - name, data); + printk(KERN_INFO DRV_NAME ": Peripheral Configuration Register: 0x%X\n", + data); #endif /* CY82C693_DEBUG_INFO */ /* @@ -373,8 +373,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c outb(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", - name, data); + printk(KERN_INFO ": New Peripheral Configuration Register: 0x%X\n", + data); #endif /* CY82C693_DEBUG_INFO */ #endif /* CY82C693_SETDMA_CLOCK */ diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index baabb4ce0d78..6009b0b9655d 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -79,7 +79,7 @@ static void hpt34x_set_pio_mode(ide_drive_t *drive, const u8 pio) */ #define HPT34X_PCI_INIT_REG 0x80 -static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev) { int i = 0; unsigned long hpt34xIoBase = pci_resource_start(dev, 4); diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 6a1c65c3be3e..5271b246b88c 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -970,11 +970,12 @@ static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f return 1; } -static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev) { unsigned long io_base = pci_resource_start(dev, 4); struct ide_host *host = pci_get_drvdata(dev); struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]); + const char *name = DRV_NAME; u8 pci_clk, dpll_clk = 0; /* PCI and DPLL clock in MHz */ u8 chip_type; enum ata_clock clock; diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 74173352741f..e16a1d113a2a 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -605,7 +605,7 @@ static void __devinit it8212_disable_raid(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); } -static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev) { u8 conf; static char *mode[2] = { "pass through", "smart" }; diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 1f6791957227..998615fa285f 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -326,8 +326,9 @@ static void __devinit apple_kiwi_init(struct pci_dev *pdev) } #endif /* CONFIG_PPC_PMAC */ -static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev) { + const char *name = DRV_NAME; unsigned long dma_base = pci_resource_start(dev, 4); unsigned long sec_dma_base = dma_base + 0x08; long pll_input, pll_output, ratio; diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index da92d127868f..6ff2def58da0 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -265,8 +265,7 @@ static void pdc202xx_dma_timeout(ide_drive_t *drive) ide_dma_timeout(drive); } -static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev) { unsigned long dmabase = pci_resource_start(dev, 4); u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0; diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 9eb411f5c358..7fc3022dcf68 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -200,13 +200,12 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed) /** * init_chipset_ich - set up the ICH chipset * @dev: PCI device to set up - * @name: Name of the device * * Initialize the PCI device as required. For the ICH this turns * out to be nice and simple. */ -static unsigned int __devinit init_chipset_ich(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_ich(struct pci_dev *dev) { u32 extra = 0; diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index e26bc8326dbb..d173f2937722 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -174,7 +174,7 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed) pci_write_config_byte(dev, 0x54, ultra_enable); } -static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_svwks(struct pci_dev *dev) { unsigned int reg; u8 btr; @@ -190,8 +190,8 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha pci_read_config_dword(isa_dev, 0x64, ®); reg &= ~0x00002000; /* disable 600ns interrupt mask */ if(!(reg & 0x00004000)) - printk(KERN_DEBUG "%s %s: UDMA not BIOS " - "enabled.\n", name, pci_name(dev)); + printk(KERN_DEBUG DRV_NAME " %s: UDMA not BIOS " + "enabled.\n", pci_name(dev)); reg |= 0x00004000; /* enable UDMA/33 support */ pci_write_config_dword(isa_dev, 0x64, reg); } diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 572b479a3922..b8ad9ad6cf0d 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -457,14 +457,12 @@ static void sil_sata_pre_reset(ide_drive_t *drive) /** * init_chipset_siimage - set up an SI device * @dev: PCI device - * @name: device name * * Perform the initial PCI set up for this device. Attempt to switch * to 133 MHz clocking if the system isn't already set up to do it. */ -static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev) { struct ide_host *host = pci_get_drvdata(dev); void __iomem *ioaddr = host->host_priv; @@ -541,8 +539,8 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, { "== 100", "== 133", "== 2X PCI", "DISABLED!" }; tmp >>= 4; - printk(KERN_INFO "%s %s: BASE CLOCK %s\n", - name, pci_name(dev), clk_str[tmp & 3]); + printk(KERN_INFO DRV_NAME " %s: BASE CLOCK %s\n", + pci_name(dev), clk_str[tmp & 3]); } return 0; diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 6fcb46c87871..cc95f90b53b7 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -448,8 +448,7 @@ static int __devinit sis_find_family(struct pci_dev *dev) return chipset_family; } -static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev) { /* Make general config ops here 1/ tell IDE channels to operate in Compatibility mode only diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index fa720db3de10..73905bcc08fb 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -272,7 +272,7 @@ static u8 sl82c105_bridge_revision(struct pci_dev *dev) * channel 0 here at least, but channel 1 has to be enabled by * firmware or arch code. We still set both to 16 bits mode. */ -static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const char *msg) +static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev) { u32 val; diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 170e058f1fbd..454d2bf62dce 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -262,13 +262,12 @@ static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u) /** * init_chipset_via82cxxx - initialization handler * @dev: PCI device - * @name: Name of interface * * The initialization callback. Here we determine the IDE chip type * and initialize its drive independent registers. */ -static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev) { struct ide_host *host = pci_get_drvdata(dev); struct via82cxxx_dev *vdev = host->host_priv; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index d9655aeb013b..a8e9e8a69a52 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -515,7 +515,7 @@ static int do_ide_setup_pci_device(struct pci_dev *dev, * space, place chipset into init-mode, and/or preserve * an interrupt if the card is not native ide support. */ - ret = d->init_chipset ? d->init_chipset(dev, d->name) : 0; + ret = d->init_chipset ? d->init_chipset(dev) : 0; if (ret < 0) goto out; diff --git a/include/linux/ide.h b/include/linux/ide.h index fd78b401b036..b846bc44a27e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1206,7 +1206,7 @@ enum { struct ide_port_info { char *name; - unsigned int (*init_chipset)(struct pci_dev *, const char *); + unsigned int (*init_chipset)(struct pci_dev *); void (*init_iops)(ide_hwif_t *); void (*init_hwif)(ide_hwif_t *); int (*init_dma)(ide_hwif_t *, -- cgit v1.2.3 From 674bfc23c585b34c42263d73fb51710d49762a23 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:03 -0500 Subject: virtio: clarify that ABI is usable by any implementations We want others to implement and use virtio, so it makes sense to BSD license the non-__KERNEL__ parts of the headers to make this crystal clear. Signed-off-by: Rusty Russell Acked-by: Christian Borntraeger Acked-by: Mark McLoughlin Acked-by: Ryan Harper Acked-by: Eric Van Hensbergen Acked-by: Anthony Liguori --- include/linux/virtio_9p.h | 2 ++ include/linux/virtio_balloon.h | 2 ++ include/linux/virtio_blk.h | 2 ++ include/linux/virtio_config.h | 3 +++ include/linux/virtio_console.h | 2 ++ include/linux/virtio_net.h | 2 ++ include/linux/virtio_pci.h | 5 ++--- include/linux/virtio_rng.h | 2 ++ 8 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/virtio_9p.h b/include/linux/virtio_9p.h index 8eff0b53910b..b3c4a60ceeb3 100644 --- a/include/linux/virtio_9p.h +++ b/include/linux/virtio_9p.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_9P_H #define _LINUX_VIRTIO_9P_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio console */ diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h index 979524ee75b7..c30c7bfbf39b 100644 --- a/include/linux/virtio_balloon.h +++ b/include/linux/virtio_balloon.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_BALLOON_H #define _LINUX_VIRTIO_BALLOON_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_balloon */ diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index 5f79a5f9de79..6a66c7f30bcb 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_BLK_H #define _LINUX_VIRTIO_BLK_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_block */ diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index f364bbf63c34..7eb4b34d13bb 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -1,5 +1,8 @@ #ifndef _LINUX_VIRTIO_CONFIG_H #define _LINUX_VIRTIO_CONFIG_H +/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. */ + /* Virtio devices use a standardized configuration space to define their * features and pass configuration information, but each implementation can * store and access that space differently. */ diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index ed2d4ead7eb7..19a0da0dba41 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -1,6 +1,8 @@ #ifndef _LINUX_VIRTIO_CONSOLE_H #define _LINUX_VIRTIO_CONSOLE_H #include +/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. */ /* The ID for virtio console */ #define VIRTIO_ID_CONSOLE 3 diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 38c0571820fb..5e33761b9b8a 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_NET_H #define _LINUX_VIRTIO_NET_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_net */ diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h index b3151659cf49..cdef35742932 100644 --- a/include/linux/virtio_pci.h +++ b/include/linux/virtio_pci.h @@ -9,9 +9,8 @@ * Authors: * Anthony Liguori * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #ifndef _LINUX_VIRTIO_PCI_H diff --git a/include/linux/virtio_rng.h b/include/linux/virtio_rng.h index 331afb6c9f62..1a85dab8a940 100644 --- a/include/linux/virtio_rng.h +++ b/include/linux/virtio_rng.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_RNG_H #define _LINUX_VIRTIO_RNG_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_rng */ -- cgit v1.2.3 From 066f4d82a67f621ddd547bfa4b9c94631d8457b0 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Thu, 29 May 2008 11:08:26 +0200 Subject: virtio_blk: check for hardsector size from host Currently virtio_blk assumes a 512 byte hard sector size. This can cause trouble / performance issues if the backing has a different block size (like a file on an ext3 file system formatted with 4k block size or a dasd). Lets add a feature flag that tells the guest to use a different hard sector size than 512 byte. Signed-off-by: Christian Borntraeger Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 10 +++++++++- include/linux/virtio_blk.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index dd7ea203f940..42251095134f 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -196,6 +196,7 @@ static int virtblk_probe(struct virtio_device *vdev) int err; u64 cap; u32 v; + u32 blk_size; if (index_to_minor(index) >= 1 << MINORBITS) return -ENOSPC; @@ -290,6 +291,13 @@ static int virtblk_probe(struct virtio_device *vdev) if (!err) blk_queue_max_hw_segments(vblk->disk->queue, v); + /* Host can optionally specify the block size of the device */ + err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE, + offsetof(struct virtio_blk_config, blk_size), + &blk_size); + if (!err) + blk_queue_hardsect_size(vblk->disk->queue, blk_size); + add_disk(vblk->disk); return 0; @@ -330,7 +338,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, - VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, }; static struct virtio_driver virtio_blk = { diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index 6a66c7f30bcb..c1aef85243bf 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -13,6 +13,7 @@ #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ #define VIRTIO_BLK_F_GEOMETRY 4 /* Legacy geometry available */ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ struct virtio_blk_config { @@ -28,6 +29,8 @@ struct virtio_blk_config __u8 heads; __u8 sectors; } geometry; + /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + __u32 blk_size; } __attribute__((packed)); /* These two define direction. */ -- cgit v1.2.3 From dd7c7bc46211785a1aa7d70feb15830f62682b3c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:07 -0500 Subject: virtio: Formally reserve bits 28-31 to be 'transport' features. We assign feature bits as required, but it makes sense to reserve some for the particular transport, rather than the particular device. Signed-off-by: Rusty Russell --- drivers/virtio/virtio.c | 5 +++++ include/linux/virtio_config.h | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index fc85cba64578..baf103361e3a 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -113,6 +113,11 @@ static int virtio_dev_probe(struct device *_d) set_bit(f, dev->features); } + /* Transport features are always preserved to pass to set_features. */ + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) + if (device_features & (1 << i)) + set_bit(i, dev->features); + err = drv->probe(dev); if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 7eb4b34d13bb..5a30cfb7934b 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -18,6 +18,12 @@ /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 +/* Some virtio feature bits (currently bits 28 through 31) are reserved for the + * transport being used (eg. virtio_ring), the rest are per-device feature + * bits. */ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 32 + /* Do we get callbacks when the ring is completely used, even if we've * suppressed them? */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 -- cgit v1.2.3 From c624896e488ba2bff5ae497782cfb265c8b00646 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:07 -0500 Subject: virtio: Rename set_features to finalize_features Rather than explicitly handing the features to the lower-level, we just hand the virtio_device and have it set the features. This make it clear that it has the chance to manipulate the features of the device at this point (and that all feature negotiation is already done). Signed-off-by: Rusty Russell --- drivers/lguest/lguest_device.c | 11 ++++++----- drivers/s390/kvm/kvm_virtio.c | 11 ++++++----- drivers/virtio/virtio.c | 5 ++--- drivers/virtio/virtio_pci.c | 10 ++++++---- include/linux/virtio_config.h | 7 ++++--- 5 files changed, 24 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 1a8de57289eb..54fdc2aa4806 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -98,16 +98,17 @@ static u32 lg_get_features(struct virtio_device *vdev) return features; } -static void lg_set_features(struct virtio_device *vdev, u32 features) +static void lg_finalize_features(struct virtio_device *vdev) { - unsigned int i; + unsigned int i, bits; struct lguest_device_desc *desc = to_lgdev(vdev)->desc; /* Second half of bitmap is features we accept. */ u8 *out_features = lg_features(desc) + desc->feature_len; memset(out_features, 0, desc->feature_len); - for (i = 0; i < min(desc->feature_len * 8, 32); i++) { - if (features & (1 << i)) + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) out_features[i / 8] |= (1 << (i % 8)); } } @@ -297,7 +298,7 @@ static void lg_del_vq(struct virtqueue *vq) /* The ops structure which hooks everything together. */ static struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features, - .set_features = lg_set_features, + .finalize_features = lg_finalize_features, .get = lg_get, .set = lg_set, .get_status = lg_get_status, diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index d41f234bb2c2..5953510e7d5f 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -88,16 +88,17 @@ static u32 kvm_get_features(struct virtio_device *vdev) return features; } -static void kvm_set_features(struct virtio_device *vdev, u32 features) +static void kvm_finalize_features(struct virtio_device *vdev) { - unsigned int i; + unsigned int i, bits; struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; /* Second half of bitmap is features we accept. */ u8 *out_features = kvm_vq_features(desc) + desc->feature_len; memset(out_features, 0, desc->feature_len); - for (i = 0; i < min(desc->feature_len * 8, 32); i++) { - if (features & (1 << i)) + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) out_features[i / 8] |= (1 << (i % 8)); } } @@ -223,7 +224,7 @@ static void kvm_del_vq(struct virtqueue *vq) */ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_features = kvm_get_features, - .set_features = kvm_set_features, + .finalize_features = kvm_finalize_features, .get = kvm_get, .set = kvm_set, .get_status = kvm_get_status, diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index baf103361e3a..5b78fd0aff0a 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -113,7 +113,7 @@ static int virtio_dev_probe(struct device *_d) set_bit(f, dev->features); } - /* Transport features are always preserved to pass to set_features. */ + /* Transport features always preserved to pass to finalize_features. */ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) if (device_features & (1 << i)) set_bit(i, dev->features); @@ -122,8 +122,7 @@ static int virtio_dev_probe(struct device *_d) if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); else { - /* They should never have set feature bits beyond 32 */ - dev->config->set_features(dev, dev->features[0]); + dev->config->finalize_features(dev); add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } return err; diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index eae7236310e4..9855975a72a3 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -94,12 +94,14 @@ static u32 vp_get_features(struct virtio_device *vdev) return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); } -/* virtio config->set_features() implementation */ -static void vp_set_features(struct virtio_device *vdev, u32 features) +/* virtio config->finalize_features() implementation */ +static void vp_finalize_features(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - iowrite32(features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); + /* We only support 32 feature bits. */ + BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1); + iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES); } /* virtio config->get() implementation */ @@ -297,7 +299,7 @@ static struct virtio_config_ops virtio_pci_config_ops = { .find_vq = vp_find_vq, .del_vq = vp_del_vq, .get_features = vp_get_features, - .set_features = vp_set_features, + .finalize_features = vp_finalize_features, }; /* the PCI probing function */ diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 5a30cfb7934b..bf8ec283b232 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -61,9 +61,10 @@ * @get_features: get the array of feature bits for this device. * vdev: the virtio_device * Returns the first 32 feature bits (all we currently need). - * @set_features: confirm what device features we'll be using. + * @finalize_features: confirm what device features we'll be using. * vdev: the virtio_device - * feature: the first 32 feature bits + * This gives the final feature bits for the device: it can change + * the dev->feature bits if it wants. */ struct virtio_config_ops { @@ -79,7 +80,7 @@ struct virtio_config_ops void (*callback)(struct virtqueue *)); void (*del_vq)(struct virtqueue *vq); u32 (*get_features)(struct virtio_device *vdev); - void (*set_features)(struct virtio_device *vdev, u32 features); + void (*finalize_features)(struct virtio_device *vdev); }; /* If driver didn't advertise the feature, it will never appear. */ -- cgit v1.2.3 From e34f87256794b87e7f4a8f1812538be7b7b5214c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:13 -0500 Subject: virtio: Add transport feature handling stub for virtio_ring. To prepare for virtio_ring transport feature bits, hook in a call in all the users to manipulate them. This currently just clears all the bits, since it doesn't understand any features. Signed-off-by: Rusty Russell --- drivers/lguest/lguest_device.c | 3 +++ drivers/s390/kvm/kvm_virtio.c | 3 +++ drivers/virtio/virtio_pci.c | 3 +++ drivers/virtio/virtio_ring.c | 16 ++++++++++++++++ include/linux/virtio_ring.h | 2 ++ 5 files changed, 27 insertions(+) (limited to 'include/linux') diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 54fdc2aa4806..37344aaee22f 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -105,6 +105,9 @@ static void lg_finalize_features(struct virtio_device *vdev) /* Second half of bitmap is features we accept. */ u8 *out_features = lg_features(desc) + desc->feature_len; + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + memset(out_features, 0, desc->feature_len); bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; for (i = 0; i < bits; i++) { diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 5953510e7d5f..79954bd6bfa5 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -95,6 +95,9 @@ static void kvm_finalize_features(struct virtio_device *vdev) /* Second half of bitmap is features we accept. */ u8 *out_features = kvm_vq_features(desc) + desc->feature_len; + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + memset(out_features, 0, desc->feature_len); bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; for (i = 0; i < bits; i++) { diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 9855975a72a3..c7dc37c7cce9 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -99,6 +99,9 @@ static void vp_finalize_features(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + /* We only support 32 feature bits. */ BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1); iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 21d9a62767af..6eb5303fed11 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #ifdef DEBUG @@ -323,4 +324,19 @@ void vring_del_virtqueue(struct virtqueue *vq) } EXPORT_SYMBOL_GPL(vring_del_virtqueue); +/* Manipulates transport-specific feature bits. */ +void vring_transport_features(struct virtio_device *vdev) +{ + unsigned int i; + + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) { + switch (i) { + default: + /* We don't understand this bit. */ + clear_bit(i, vdev->features); + } + } +} +EXPORT_SYMBOL_GPL(vring_transport_features); + MODULE_LICENSE("GPL"); diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index abe481ed990e..c4a598fb3826 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -120,6 +120,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num, void (*notify)(struct virtqueue *vq), void (*callback)(struct virtqueue *vq)); void vring_del_virtqueue(struct virtqueue *vq); +/* Filter out transport-specific feature bits. */ +void vring_transport_features(struct virtio_device *vdev); irqreturn_t vring_interrupt(int irq, void *_vq); #endif /* __KERNEL__ */ -- cgit v1.2.3 From ed9559d38a87a44e3bda87d73a50aab92471d7dc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:11:09 +1000 Subject: Label kthread_create() with printf attribute tag. Obvious misc patch been in my queue (& linux-next) for over a cycle. Signed-off-by: Rusty Russell Signed-off-by: Linus Torvalds --- include/linux/kthread.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 00dd957e245b..aabc8a13ba71 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -6,7 +6,8 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, - const char namefmt[], ...); + const char namefmt[], ...) + __attribute__((format(printf, 3, 4))); /** * kthread_run - create and wake a thread. -- cgit v1.2.3 From 483fad1c3fa1060d7e6710e84a065ad514571739 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Tue, 22 Jul 2008 04:48:46 +1000 Subject: ELF loader support for auxvec base platform string Some IBM POWER-based platforms have the ability to run in a mode which mostly appears to the OS as a different processor from the actual hardware. For example, a Power6 system may appear to be a Power5+, which makes the AT_PLATFORM value "power5+". This means that programs are restricted to the ISA supported by Power5+; Power6-specific instructions are treated as illegal. However, some applications (virtual machines, optimized libraries) can benefit from knowledge of the underlying CPU model. A new aux vector entry, AT_BASE_PLATFORM, will denote the actual hardware. For example, on a Power6 system in Power5+ compatibility mode, AT_PLATFORM will be "power5+" and AT_BASE_PLATFORM will be "power6". The idea is that AT_PLATFORM indicates the instruction set supported, while AT_BASE_PLATFORM indicates the underlying microarchitecture. If the architecture has defined ELF_BASE_PLATFORM, copy that value to the user stack in the same manner as ELF_PLATFORM. Signed-off-by: Nathan Lynch Acked-by: Andrew Morton Signed-off-by: Benjamin Herrenschmidt --- fs/binfmt_elf.c | 28 ++++++++++++++++++++++++++++ include/linux/auxvec.h | 6 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 639d2d8b5710..742c8f530481 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -131,6 +131,15 @@ static int padzero(unsigned long elf_bss) #define STACK_ALLOC(sp, len) ({ sp -= len ; sp; }) #endif +#ifndef ELF_BASE_PLATFORM +/* + * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture. + * If the arch defines ELF_BASE_PLATFORM (in asm/elf.h), the value + * will be copied to the user stack in the same manner as AT_PLATFORM. + */ +#define ELF_BASE_PLATFORM NULL +#endif + static int create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, unsigned long load_addr, unsigned long interp_load_addr) @@ -142,7 +151,9 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, elf_addr_t __user *envp; elf_addr_t __user *sp; elf_addr_t __user *u_platform; + elf_addr_t __user *u_base_platform; const char *k_platform = ELF_PLATFORM; + const char *k_base_platform = ELF_BASE_PLATFORM; int items; elf_addr_t *elf_info; int ei_index = 0; @@ -172,6 +183,19 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, return -EFAULT; } + /* + * If this architecture has a "base" platform capability + * string, copy it to userspace. + */ + u_base_platform = NULL; + if (k_base_platform) { + size_t len = strlen(k_base_platform) + 1; + + u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len); + if (__copy_to_user(u_base_platform, k_base_platform, len)) + return -EFAULT; + } + /* Create the ELF interpreter info */ elf_info = (elf_addr_t *)current->mm->saved_auxv; /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */ @@ -209,6 +233,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, NEW_AUX_ENT(AT_PLATFORM, (elf_addr_t)(unsigned long)u_platform); } + if (k_base_platform) { + NEW_AUX_ENT(AT_BASE_PLATFORM, + (elf_addr_t)(unsigned long)u_base_platform); + } if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) { NEW_AUX_ENT(AT_EXECFD, bprm->interp_data); } diff --git a/include/linux/auxvec.h b/include/linux/auxvec.h index 0da17d14fd13..d7afa9dd6635 100644 --- a/include/linux/auxvec.h +++ b/include/linux/auxvec.h @@ -26,9 +26,13 @@ #define AT_SECURE 23 /* secure mode boolean */ +#define AT_BASE_PLATFORM 24 /* string identifying real platform, may + * differ from AT_PLATFORM. */ + #define AT_EXECFN 31 /* filename of program */ + #ifdef __KERNEL__ -#define AT_VECTOR_SIZE_BASE 17 /* NEW_AUX_ENT entries in auxiliary table */ +#define AT_VECTOR_SIZE_BASE 18 /* NEW_AUX_ENT entries in auxiliary table */ /* number of "#define AT_.*" above, minus {AT_NULL, AT_IGNORE, AT_NOTELF} */ #endif -- cgit v1.2.3 From 95984f62c9b0bf6d89ef4f514b1afe73623481de Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 22 Jul 2008 18:41:10 +0200 Subject: firewire: fw-ohci: TSB43AB22/A dualbuffer workaround Isochronous reception in dualbuffer mode is reportedly broken with TI TSB43AB22A on x86-64. Descriptor addresses above 2G have been determined as the trigger: https://bugzilla.redhat.com/show_bug.cgi?id=435550 Two fixes are possible: - pci_set_consistent_dma_mask(pdev, DMA_31BIT_MASK); at least when IR descriptors are allocated, or - simply don't use dualbuffer. This fix implements the latter workaround. But we keep using dualbuffer on x86-32 which won't give us highmen (and thus physical addresses outside the 31bit range) in coherent DMA memory allocations. Right now we could for example also whitelist PPC32, but DMA mapping implementation details are expected to change there. Signed-off-by: Stefan Richter Signed-off-by: Jarod Wilson --- drivers/firewire/fw-ohci.c | 37 ++++++++++++++++++++++++------------- include/linux/pci_ids.h | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 333b12544dd1..a4eff32621b5 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -171,7 +171,6 @@ struct iso_context { struct fw_ohci { struct fw_card card; - u32 version; __iomem char *registers; dma_addr_t self_id_bus; __le32 *self_id_cpu; @@ -180,6 +179,8 @@ struct fw_ohci { int generation; int request_generation; /* for timestamping incoming requests */ u32 bus_seconds; + + bool use_dualbuffer; bool old_uninorth; bool bus_reset_packet_quirk; @@ -1885,7 +1886,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size) } else { mask = &ohci->ir_context_mask; list = ohci->ir_context_list; - if (ohci->version >= OHCI_VERSION_1_1) + if (ohci->use_dualbuffer) callback = handle_ir_dualbuffer_packet; else callback = handle_ir_packet_per_buffer; @@ -1949,7 +1950,7 @@ static int ohci_start_iso(struct fw_iso_context *base, } else { index = ctx - ohci->ir_context_list; control = IR_CONTEXT_ISOCH_HEADER; - if (ohci->version >= OHCI_VERSION_1_1) + if (ohci->use_dualbuffer) control |= IR_CONTEXT_DUAL_BUFFER_MODE; match = (tags << 28) | (sync << 8) | ctx->base.channel; if (cycle >= 0) { @@ -2279,7 +2280,7 @@ ohci_queue_iso(struct fw_iso_context *base, spin_lock_irqsave(&ctx->context.ohci->lock, flags); if (base->type == FW_ISO_CONTEXT_TRANSMIT) retval = ohci_queue_iso_transmit(base, packet, buffer, payload); - else if (ctx->context.ohci->version >= OHCI_VERSION_1_1) + else if (ctx->context.ohci->use_dualbuffer) retval = ohci_queue_iso_receive_dualbuffer(base, packet, buffer, payload); else @@ -2341,7 +2342,7 @@ static int __devinit pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { struct fw_ohci *ohci; - u32 bus_options, max_receive, link_speed; + u32 bus_options, max_receive, link_speed, version; u64 guid; int err; size_t size; @@ -2366,12 +2367,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); pci_set_drvdata(dev, ohci); -#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) - ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW; -#endif - ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; - spin_lock_init(&ohci->lock); tasklet_init(&ohci->bus_reset_tasklet, @@ -2390,6 +2385,23 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) goto fail_iomem; } + version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + ohci->use_dualbuffer = version >= OHCI_VERSION_1_1; + +/* x86-32 currently doesn't use highmem for dma_alloc_coherent */ +#if !defined(CONFIG_X86_32) + /* dual-buffer mode is broken with descriptor addresses above 2G */ + if (dev->vendor == PCI_VENDOR_ID_TI && + dev->device == PCI_DEVICE_ID_TI_TSB43AB22) + ohci->use_dualbuffer = false; +#endif + +#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) + ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE && + dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW; +#endif + ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; + ar_context_init(&ohci->ar_request_ctx, ohci, OHCI1394_AsReqRcvContextControlSet); @@ -2441,9 +2453,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) if (err < 0) goto fail_self_id; - ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n", - dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff); + dev->dev.bus_id, version >> 16, version & 0xff); return 0; fail_self_id: diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 65953822c9cb..720d67554106 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -748,6 +748,7 @@ #define PCI_VENDOR_ID_TI 0x104c #define PCI_DEVICE_ID_TI_TVP4020 0x3d07 #define PCI_DEVICE_ID_TI_4450 0x8011 +#define PCI_DEVICE_ID_TI_TSB43AB22 0x8023 #define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 #define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 #define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 -- cgit v1.2.3 From 3d45955962496879dead8d4dd70bb9a23b07154b Mon Sep 17 00:00:00 2001 From: Alexey Korolev Date: Thu, 15 May 2008 17:23:18 +0100 Subject: [MTD] [NAND] subpage read feature as a way to increase performance. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch enables NAND subpage read functionality. If upper layer drivers are requesting to read non page aligned data NAND subpage-read functionality reads the only whose ECC regions which include requested data when original code reads whole page. This significantly improves performance in many cases. Here are some digits : UBI volume mount time No subpage reads: 5.75 seconds Subpage read patch: 2.42 seconds Open/stat time for files on JFFS2 volume: No subpage read 0m 5.36s Subpage read 0m 2.88s Signed-off-by Alexey Korolev Acked-by: Artem Bityutskiy Acked-by: Jörn Engel Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 87 +++++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/nand.h | 5 +++ 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba1bdf787323..d1129bae6c27 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -797,6 +797,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +/** + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @dataofs offset of requested data within the page + * @readlen data length + * @buf: buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + + /* Column address wihin the page aligned to ECC size (256bytes). */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + + /* Data size aligned to ECC ecc.size*/ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* The performance is faster if to position offsets + according to ecc.pos. Let make sure here that + there are no gaps in ecc positions */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != + eccpos[i + start_step * chip->ecc.bytes + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* send the command to read the particular ecc bytes */ + /* take care about buswidth alignment in read_buf */ + aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + /** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure @@ -994,6 +1075,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) + ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) @@ -1001,7 +1084,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Transfer not aligned data */ if (!aligned) { - chip->pagebuf = realpage; + if (!NAND_SUBPAGE_READ(chip) && !oob) + chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -2521,6 +2605,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 863e22a0ddb5..83f678702dff 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -177,6 +177,7 @@ typedef enum { #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) +#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT)) /* Mask to zero out the chip options, which come from the id table */ #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) @@ -274,6 +275,10 @@ struct nand_ecc_ctrl { int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf); + int (*read_subpage)(struct mtd_info *mtd, + struct nand_chip *chip, + uint32_t offs, uint32_t len, + uint8_t *buf); void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf); -- cgit v1.2.3 From 3d6f4a20cc287a8980c6186624834cf10a70752b Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 24 Jul 2008 23:38:31 -0700 Subject: endian: Always evaluate arguments. Changeset 7fa897b91a3ea0f16c2873b869d7a0eef05acff4 ("ide: trivial sparse annotations") created an IDE bootup regression on big-endian systems. In drivers/ide/ide-iops.c, function ide_fixstring() we now have the loop: for (p = end ; p != s;) be16_to_cpus((u16 *)(p -= 2)); which will never terminate on big-endian because in such a configuration be16_to_cpus() evaluates to "do { } while (0)" Therefore, always evaluate the arguments to nop endian transformation operations. Signed-off-by: David S. Miller Signed-off-by: Linus Torvalds --- include/linux/byteorder/big_endian.h | 12 ++++++------ include/linux/byteorder/little_endian.h | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/byteorder/big_endian.h b/include/linux/byteorder/big_endian.h index 961ed4b48d8e..44f95b92393b 100644 --- a/include/linux/byteorder/big_endian.h +++ b/include/linux/byteorder/big_endian.h @@ -94,12 +94,12 @@ static inline __u16 __be16_to_cpup(const __be16 *p) #define __le32_to_cpus(x) __swab32s((x)) #define __cpu_to_le16s(x) __swab16s((x)) #define __le16_to_cpus(x) __swab16s((x)) -#define __cpu_to_be64s(x) do {} while (0) -#define __be64_to_cpus(x) do {} while (0) -#define __cpu_to_be32s(x) do {} while (0) -#define __be32_to_cpus(x) do {} while (0) -#define __cpu_to_be16s(x) do {} while (0) -#define __be16_to_cpus(x) do {} while (0) +#define __cpu_to_be64s(x) do { (void)(x); } while (0) +#define __be64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be32s(x) do { (void)(x); } while (0) +#define __be32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be16s(x) do { (void)(x); } while (0) +#define __be16_to_cpus(x) do { (void)(x); } while (0) #ifdef __KERNEL__ #include diff --git a/include/linux/byteorder/little_endian.h b/include/linux/byteorder/little_endian.h index 05dc7c35b3b2..4cc170a31762 100644 --- a/include/linux/byteorder/little_endian.h +++ b/include/linux/byteorder/little_endian.h @@ -88,12 +88,12 @@ static inline __u16 __be16_to_cpup(const __be16 *p) { return __swab16p((__u16 *)p); } -#define __cpu_to_le64s(x) do {} while (0) -#define __le64_to_cpus(x) do {} while (0) -#define __cpu_to_le32s(x) do {} while (0) -#define __le32_to_cpus(x) do {} while (0) -#define __cpu_to_le16s(x) do {} while (0) -#define __le16_to_cpus(x) do {} while (0) +#define __cpu_to_le64s(x) do { (void)(x); } while (0) +#define __le64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le32s(x) do { (void)(x); } while (0) +#define __le32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le16s(x) do { (void)(x); } while (0) +#define __le16_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_be64s(x) __swab64s((x)) #define __be64_to_cpus(x) __swab64s((x)) #define __cpu_to_be32s(x) __swab32s((x)) -- cgit v1.2.3 From 25c94d010a8ae8605dc4d5453e0c82fa97da5d12 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Fri, 25 Jul 2008 10:30:06 -0700 Subject: mlx4_core: Add VLAN tag field to WQE control segment struct Add fields for VLAN tag and insert VLAN tag flag to the control section struct. These fields will be used for sending ethernet packets. Signed-off-by: Yevgeny Petrilin Signed-off-by: Roland Dreier --- include/linux/mlx4/qp.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index e27082cd650e..bf8f11982dae 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -164,11 +164,13 @@ enum { MLX4_WQE_CTRL_SOLICITED = 1 << 1, MLX4_WQE_CTRL_IP_CSUM = 1 << 4, MLX4_WQE_CTRL_TCP_UDP_CSUM = 1 << 5, + MLX4_WQE_CTRL_INS_VLAN = 1 << 6, }; struct mlx4_wqe_ctrl_seg { __be32 owner_opcode; - u8 reserved2[3]; + __be16 vlan_tag; + u8 ins_vlan; u8 fence_size; /* * High 24 bits are SRC remote buffer; low 8 bits are flags: -- cgit v1.2.3 From e0deaff470900a4c3222ca7139f6c9639e26a2f5 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 25 Jul 2008 01:45:24 -0700 Subject: split the typecheck macros out of include/linux/kernel.h Needed to fix up a recursive include snafu in locking-add-typecheck-on-irqsave-and-friends-for-correct-flags.patch Cc: Steven Rostedt Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 21 +-------------------- include/linux/typecheck.h | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 include/linux/typecheck.h (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index f9cd7a513f9c..5c4b1251e110 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -441,26 +442,6 @@ static inline char *pack_hex_byte(char *buf, u8 byte) const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) -/* - * Check at compile time that something is of a particular type. - * Always evaluates to 1 so you may use it easily in comparisons. - */ -#define typecheck(type,x) \ -({ type __dummy; \ - typeof(x) __dummy2; \ - (void)(&__dummy == &__dummy2); \ - 1; \ -}) - -/* - * Check at compile time that 'function' is a certain type, or is a pointer - * to that type (needs to use typedef for the function type.) - */ -#define typecheck_fn(type,function) \ -({ typeof(type) __tmp = function; \ - (void)__tmp; \ -}) - struct sysinfo; extern int do_sysinfo(struct sysinfo *info); diff --git a/include/linux/typecheck.h b/include/linux/typecheck.h new file mode 100644 index 000000000000..eb5b74a575be --- /dev/null +++ b/include/linux/typecheck.h @@ -0,0 +1,24 @@ +#ifndef TYPECHECK_H_INCLUDED +#define TYPECHECK_H_INCLUDED + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +/* + * Check at compile time that 'function' is a certain type, or is a pointer + * to that type (needs to use typedef for the function type.) + */ +#define typecheck_fn(type,function) \ +({ typeof(type) __tmp = function; \ + (void)__tmp; \ +}) + +#endif /* TYPECHECK_H_INCLUDED */ -- cgit v1.2.3 From 3f307891ce0e7b0438c432af1aacd656a092ff45 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 25 Jul 2008 01:45:25 -0700 Subject: locking: add typecheck on irqsave and friends for correct flags There haave been several areas in the kernel where an int has been used for flags in local_irq_save() and friends instead of a long. This can cause some hard to debug problems on some architectures. This patch adds a typecheck inside the irqsave and restore functions to flag these cases. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: build fix] Signed-off-by: Steven Rostedt Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/irqflags.h | 54 ++++++++++++++++++++++++++---------- include/linux/spinlock.h | 72 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 95 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 2b1c2e58566e..74bde13224c9 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -11,6 +11,8 @@ #ifndef _LINUX_TRACE_IRQFLAGS_H #define _LINUX_TRACE_IRQFLAGS_H +#include + #ifdef CONFIG_TRACE_IRQFLAGS extern void trace_softirqs_on(unsigned long ip); extern void trace_softirqs_off(unsigned long ip); @@ -58,18 +60,24 @@ do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0) #define local_irq_disable() \ do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0) -#define local_irq_save(flags) \ - do { raw_local_irq_save(flags); trace_hardirqs_off(); } while (0) +#define local_irq_save(flags) \ + do { \ + typecheck(unsigned long, flags); \ + raw_local_irq_save(flags); \ + trace_hardirqs_off(); \ + } while (0) -#define local_irq_restore(flags) \ - do { \ - if (raw_irqs_disabled_flags(flags)) { \ - raw_local_irq_restore(flags); \ - trace_hardirqs_off(); \ - } else { \ - trace_hardirqs_on(); \ - raw_local_irq_restore(flags); \ - } \ + +#define local_irq_restore(flags) \ + do { \ + typecheck(unsigned long, flags); \ + if (raw_irqs_disabled_flags(flags)) { \ + raw_local_irq_restore(flags); \ + trace_hardirqs_off(); \ + } else { \ + trace_hardirqs_on(); \ + raw_local_irq_restore(flags); \ + } \ } while (0) #else /* !CONFIG_TRACE_IRQFLAGS_SUPPORT */ /* @@ -78,8 +86,16 @@ */ # define raw_local_irq_disable() local_irq_disable() # define raw_local_irq_enable() local_irq_enable() -# define raw_local_irq_save(flags) local_irq_save(flags) -# define raw_local_irq_restore(flags) local_irq_restore(flags) +# define raw_local_irq_save(flags) \ + do { \ + typecheck(unsigned long, flags); \ + local_irq_save(flags); \ + } while (0) +# define raw_local_irq_restore(flags) \ + do { \ + typecheck(unsigned long, flags); \ + local_irq_restore(flags); \ + } while (0) #endif /* CONFIG_TRACE_IRQFLAGS_SUPPORT */ #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT @@ -89,7 +105,11 @@ raw_safe_halt(); \ } while (0) -#define local_save_flags(flags) raw_local_save_flags(flags) +#define local_save_flags(flags) \ + do { \ + typecheck(unsigned long, flags); \ + raw_local_save_flags(flags); \ + } while (0) #define irqs_disabled() \ ({ \ @@ -99,7 +119,11 @@ raw_irqs_disabled_flags(_flags); \ }) -#define irqs_disabled_flags(flags) raw_irqs_disabled_flags(flags) +#define irqs_disabled_flags(flags) \ +({ \ + typecheck(unsigned long, flags); \ + raw_irqs_disabled_flags(flags); \ +}) #endif /* CONFIG_X86 */ #endif diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index d311a090fae7..61e5610ad165 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -46,6 +46,7 @@ * linux/spinlock.h: builds the final spin_*() APIs. */ +#include #include #include #include @@ -191,23 +192,53 @@ do { \ #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) -#define spin_lock_irqsave(lock, flags) flags = _spin_lock_irqsave(lock) -#define read_lock_irqsave(lock, flags) flags = _read_lock_irqsave(lock) -#define write_lock_irqsave(lock, flags) flags = _write_lock_irqsave(lock) +#define spin_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _spin_lock_irqsave(lock); \ + } while (0) +#define read_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _read_lock_irqsave(lock); \ + } while (0) +#define write_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _write_lock_irqsave(lock); \ + } while (0) #ifdef CONFIG_DEBUG_LOCK_ALLOC -#define spin_lock_irqsave_nested(lock, flags, subclass) \ - flags = _spin_lock_irqsave_nested(lock, subclass) +#define spin_lock_irqsave_nested(lock, flags, subclass) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _spin_lock_irqsave_nested(lock, subclass); \ + } while (0) #else -#define spin_lock_irqsave_nested(lock, flags, subclass) \ - flags = _spin_lock_irqsave(lock) +#define spin_lock_irqsave_nested(lock, flags, subclass) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _spin_lock_irqsave(lock); \ + } while (0) #endif #else -#define spin_lock_irqsave(lock, flags) _spin_lock_irqsave(lock, flags) -#define read_lock_irqsave(lock, flags) _read_lock_irqsave(lock, flags) -#define write_lock_irqsave(lock, flags) _write_lock_irqsave(lock, flags) +#define spin_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _spin_lock_irqsave(lock, flags); \ + } while (0) +#define read_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _read_lock_irqsave(lock, flags); \ + } while (0) +#define write_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _write_lock_irqsave(lock, flags); \ + } while (0) #define spin_lock_irqsave_nested(lock, flags, subclass) \ spin_lock_irqsave(lock, flags) @@ -260,16 +291,25 @@ do { \ } while (0) #endif -#define spin_unlock_irqrestore(lock, flags) \ - _spin_unlock_irqrestore(lock, flags) +#define spin_unlock_irqrestore(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _spin_unlock_irqrestore(lock, flags); \ + } while (0) #define spin_unlock_bh(lock) _spin_unlock_bh(lock) -#define read_unlock_irqrestore(lock, flags) \ - _read_unlock_irqrestore(lock, flags) +#define read_unlock_irqrestore(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _read_unlock_irqrestore(lock, flags); \ + } while (0) #define read_unlock_bh(lock) _read_unlock_bh(lock) -#define write_unlock_irqrestore(lock, flags) \ - _write_unlock_irqrestore(lock, flags) +#define write_unlock_irqrestore(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _write_unlock_irqrestore(lock, flags); \ + } while (0) #define write_unlock_bh(lock) _write_unlock_bh(lock) #define spin_trylock_bh(lock) __cond_lock(lock, _spin_trylock_bh(lock)) -- cgit v1.2.3 From 8b5ac31e27135a6f2c210c40d03bf8f1b3a86b77 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Fri, 25 Jul 2008 01:45:26 -0700 Subject: include: use get/put_unaligned_* helpers Signed-off-by: Harvey Harrison Cc: "John W. Linville" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/reiserfs_fs.h | 4 ++-- include/linux/smb_fs.h | 19 +++++++------------ include/net/ieee80211_radiotap.h | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 4aacaeecb56f..e9963af16cda 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -526,8 +526,8 @@ struct item_head { ** p is the array of __u32, i is the index into the array, v is the value ** to store there. */ -#define get_block_num(p, i) le32_to_cpu(get_unaligned((p) + (i))) -#define put_block_num(p, i, v) put_unaligned(cpu_to_le32(v), (p) + (i)) +#define get_block_num(p, i) get_unaligned_le32((p) + (i)) +#define put_block_num(p, i, v) put_unaligned_le32((v), (p) + (i)) // // in old version uniqueness field shows key type diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h index 2c5cd55f44ff..923cd8a247b1 100644 --- a/include/linux/smb_fs.h +++ b/include/linux/smb_fs.h @@ -43,18 +43,13 @@ static inline struct smb_inode_info *SMB_I(struct inode *inode) } /* macro names are short for word, double-word, long value (?) */ -#define WVAL(buf,pos) \ - (le16_to_cpu(get_unaligned((__le16 *)((u8 *)(buf) + (pos))))) -#define DVAL(buf,pos) \ - (le32_to_cpu(get_unaligned((__le32 *)((u8 *)(buf) + (pos))))) -#define LVAL(buf,pos) \ - (le64_to_cpu(get_unaligned((__le64 *)((u8 *)(buf) + (pos))))) -#define WSET(buf,pos,val) \ - put_unaligned(cpu_to_le16((u16)(val)), (__le16 *)((u8 *)(buf) + (pos))) -#define DSET(buf,pos,val) \ - put_unaligned(cpu_to_le32((u32)(val)), (__le32 *)((u8 *)(buf) + (pos))) -#define LSET(buf,pos,val) \ - put_unaligned(cpu_to_le64((u64)(val)), (__le64 *)((u8 *)(buf) + (pos))) +#define WVAL(buf, pos) (get_unaligned_le16((u8 *)(buf) + (pos))) +#define DVAL(buf, pos) (get_unaligned_le32((u8 *)(buf) + (pos))) +#define LVAL(buf, pos) (get_unaligned_le64((u8 *)(buf) + (pos))) + +#define WSET(buf, pos, val) put_unaligned_le16((val), (u8 *)(buf) + (pos)) +#define DSET(buf, pos, val) put_unaligned_le32((val), (u8 *)(buf) + (pos)) +#define LSET(buf, pos, val) put_unaligned_le64((val), (u8 *)(buf) + (pos)) /* where to find the base of the SMB packet proper */ #define smb_base(buf) ((u8 *)(((u8 *)(buf))+4)) diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index dfd8bf66ce27..d364fd594ea4 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -262,7 +262,7 @@ static inline int ieee80211_get_radiotap_len(unsigned char *data) struct ieee80211_radiotap_header *hdr = (struct ieee80211_radiotap_header *)data; - return le16_to_cpu(get_unaligned(&hdr->it_len)); + return get_unaligned_le16(&hdr->it_len); } #endif /* IEEE80211_RADIOTAP_H */ -- cgit v1.2.3 From b39c08cb692cb8898c30e0d8187c7cbe27cc905c Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 25 Jul 2008 01:45:29 -0700 Subject: Remove apparently unused fd1772.h header file. This header file has been unused for quite some time, and the corresponding source files appear to have been removed back in commit 99eb8a550dbccc0e1f6c7e866fe421810e0585f6 ("Remove the arm26 port") Signed-off-by: Robert P. J. Day Cc: Adrian Bunk Cc: Ian Molton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fd1772.h | 80 -------------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 include/linux/fd1772.h (limited to 'include/linux') diff --git a/include/linux/fd1772.h b/include/linux/fd1772.h deleted file mode 100644 index 871d6e4c677e..000000000000 --- a/include/linux/fd1772.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef _LINUX_FD1772REG_H -#define _LINUX_FD1772REG_H - -/* -** WD1772 stuff - originally from the M68K Linux - * Modified for Archimedes by Dave Gilbert (gilbertd@cs.man.ac.uk) - */ - -/* register codes */ - -#define FDC1772SELREG_STP (0x80) /* command/status register */ -#define FDC1772SELREG_TRA (0x82) /* track register */ -#define FDC1772SELREG_SEC (0x84) /* sector register */ -#define FDC1772SELREG_DTA (0x86) /* data register */ - -/* register names for FDC1772_READ/WRITE macros */ - -#define FDC1772REG_CMD 0 -#define FDC1772REG_STATUS 0 -#define FDC1772REG_TRACK 2 -#define FDC1772REG_SECTOR 4 -#define FDC1772REG_DATA 6 - -/* command opcodes */ - -#define FDC1772CMD_RESTORE (0x00) /* - */ -#define FDC1772CMD_SEEK (0x10) /* | */ -#define FDC1772CMD_STEP (0x20) /* | TYP 1 Commands */ -#define FDC1772CMD_STIN (0x40) /* | */ -#define FDC1772CMD_STOT (0x60) /* - */ -#define FDC1772CMD_RDSEC (0x80) /* - TYP 2 Commands */ -#define FDC1772CMD_WRSEC (0xa0) /* - " */ -#define FDC1772CMD_RDADR (0xc0) /* - */ -#define FDC1772CMD_RDTRA (0xe0) /* | TYP 3 Commands */ -#define FDC1772CMD_WRTRA (0xf0) /* - */ -#define FDC1772CMD_FORCI (0xd0) /* - TYP 4 Command */ - -/* command modifier bits */ - -#define FDC1772CMDADD_SR6 (0x00) /* step rate settings */ -#define FDC1772CMDADD_SR12 (0x01) -#define FDC1772CMDADD_SR2 (0x02) -#define FDC1772CMDADD_SR3 (0x03) -#define FDC1772CMDADD_V (0x04) /* verify */ -#define FDC1772CMDADD_H (0x08) /* wait for spin-up */ -#define FDC1772CMDADD_U (0x10) /* update track register */ -#define FDC1772CMDADD_M (0x10) /* multiple sector access */ -#define FDC1772CMDADD_E (0x04) /* head settling flag */ -#define FDC1772CMDADD_P (0x02) /* precompensation */ -#define FDC1772CMDADD_A0 (0x01) /* DAM flag */ - -/* status register bits */ - -#define FDC1772STAT_MOTORON (0x80) /* motor on */ -#define FDC1772STAT_WPROT (0x40) /* write protected (FDC1772CMD_WR*) */ -#define FDC1772STAT_SPINUP (0x20) /* motor speed stable (Type I) */ -#define FDC1772STAT_DELDAM (0x20) /* sector has deleted DAM (Type II+III) */ -#define FDC1772STAT_RECNF (0x10) /* record not found */ -#define FDC1772STAT_CRC (0x08) /* CRC error */ -#define FDC1772STAT_TR00 (0x04) /* Track 00 flag (Type I) */ -#define FDC1772STAT_LOST (0x04) /* Lost Data (Type II+III) */ -#define FDC1772STAT_IDX (0x02) /* Index status (Type I) */ -#define FDC1772STAT_DRQ (0x02) /* DRQ status (Type II+III) */ -#define FDC1772STAT_BUSY (0x01) /* FDC1772 is busy */ - - -/* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1 1 -> Side 2 */ -#define DSKSIDE (0x01) - -#define DSKDRVNONE (0x06) -#define DSKDRV0 (0x02) -#define DSKDRV1 (0x04) - -/* step rates */ -#define FDC1772STEP_6 0x00 -#define FDC1772STEP_12 0x01 -#define FDC1772STEP_2 0x02 -#define FDC1772STEP_3 0x03 - -#endif -- cgit v1.2.3 From e0ce0da9fefcc723dc006c35a7f91a32750abd40 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 25 Jul 2008 01:45:32 -0700 Subject: lists: remove a redundant conditional definition of list_add() Remove the conditional surrounding the definition of list_add() from list.h since, if you define CONFIG_DEBUG_LIST, the definition you will subsequently pick up from lib/list_debug.c will be absolutely identical, at which point you can remove that redundant definition from list_debug.c as well. Signed-off-by: Robert P. J. Day Cc: Dave Jones Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/list.h | 4 ---- lib/list_debug.c | 14 -------------- 2 files changed, 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index 139ec41d9c2e..453916bc0412 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -61,14 +61,10 @@ extern void __list_add(struct list_head *new, * Insert a new entry after the specified head. * This is good for implementing stacks. */ -#ifndef CONFIG_DEBUG_LIST static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } -#else -extern void list_add(struct list_head *new, struct list_head *head); -#endif /** diff --git a/lib/list_debug.c b/lib/list_debug.c index 4350ba9655bd..45c03fd608dd 100644 --- a/lib/list_debug.c +++ b/lib/list_debug.c @@ -39,20 +39,6 @@ void __list_add(struct list_head *new, } EXPORT_SYMBOL(__list_add); -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} -EXPORT_SYMBOL(list_add); - /** * list_del - deletes entry from list. * @entry: the element to delete from the list. -- cgit v1.2.3 From b03f6489f9f27dc519a4c60ebf39cc7b8a58eae7 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:45:35 -0700 Subject: build kernel/profile.o only when requested Build kernel/profile.o only if CONFIG_PROFILING is enabled. This makes CONFIG_PROFILING=n kernels smaller. As a bonus, some profile_tick() calls and one branch from schedule() are now eliminated with CONFIG_PROFILING=n (but I doubt these are measurable effects). This patch changes the effects of CONFIG_PROFILING=n, but I don't think having more than two choices would be the better choice. This patch also adds the name of the first parameter to the prototypes of profile_{hits,tick}() since I anyway had to add them for the dummy functions. Signed-off-by: Adrian Bunk Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/profile.h | 56 ++++++++++++++++++++++++++++++++++--------------- kernel/Makefile | 3 ++- kernel/profile.c | 4 ---- 3 files changed, 41 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/profile.h b/include/linux/profile.h index 05c1cc736937..4081fa31081f 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -8,8 +8,6 @@ #include -extern int prof_on __read_mostly; - #define CPU_PROFILING 1 #define SCHED_PROFILING 2 #define SLEEP_PROFILING 3 @@ -19,14 +17,29 @@ struct proc_dir_entry; struct pt_regs; struct notifier_block; +#if defined(CONFIG_PROFILING) && defined(CONFIG_PROC_FS) +void create_prof_cpu_mask(struct proc_dir_entry *); +#else +#define create_prof_cpu_mask(x) do { (void)(x); } while (0) +#endif + +enum profile_type { + PROFILE_TASK_EXIT, + PROFILE_MUNMAP +}; + +#ifdef CONFIG_PROFILING + +extern int prof_on __read_mostly; + /* init basic kernel profiler */ void __init profile_init(void); -void profile_tick(int); +void profile_tick(int type); /* * Add multiple profiler hits to a given address: */ -void profile_hits(int, void *ip, unsigned int nr_hits); +void profile_hits(int type, void *ip, unsigned int nr_hits); /* * Single profiler hit: @@ -40,19 +53,6 @@ static inline void profile_hit(int type, void *ip) profile_hits(type, ip, 1); } -#ifdef CONFIG_PROC_FS -void create_prof_cpu_mask(struct proc_dir_entry *); -#else -#define create_prof_cpu_mask(x) do { (void)(x); } while (0) -#endif - -enum profile_type { - PROFILE_TASK_EXIT, - PROFILE_MUNMAP -}; - -#ifdef CONFIG_PROFILING - struct task_struct; struct mm_struct; @@ -80,6 +80,28 @@ struct pt_regs; #else +#define prof_on 0 + +static inline void profile_init(void) +{ + return; +} + +static inline void profile_tick(int type) +{ + return; +} + +static inline void profile_hits(int type, void *ip, unsigned int nr_hits) +{ + return; +} + +static inline void profile_hit(int type, void *ip) +{ + return; +} + static inline int task_handoff_register(struct notifier_block * n) { return -ENOSYS; diff --git a/kernel/Makefile b/kernel/Makefile index 15ab63ffe64d..54f69837d35a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel. # -obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ +obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ cpu.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o \ @@ -24,6 +24,7 @@ CFLAGS_REMOVE_sched_clock.o = -pg CFLAGS_REMOVE_sched.o = -mno-spe -pg endif +obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ diff --git a/kernel/profile.c b/kernel/profile.c index 58926411eb2a..cd26bed4cc26 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -112,8 +112,6 @@ void __init profile_init(void) /* Profile event notifications */ -#ifdef CONFIG_PROFILING - static BLOCKING_NOTIFIER_HEAD(task_exit_notifier); static ATOMIC_NOTIFIER_HEAD(task_free_notifier); static BLOCKING_NOTIFIER_HEAD(munmap_notifier); @@ -203,8 +201,6 @@ void unregister_timer_hook(int (*hook)(struct pt_regs *)) } EXPORT_SYMBOL_GPL(unregister_timer_hook); -#endif /* CONFIG_PROFILING */ - #ifdef CONFIG_SMP /* -- cgit v1.2.3 From cebbd3fb803603b12408458ba17c29ce1e15a5f2 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 25 Jul 2008 01:45:35 -0700 Subject: build-kernel-profileo-only-when-requested-cleanups Cc: Adrian Bunk Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/profile.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/profile.h b/include/linux/profile.h index 4081fa31081f..7e7087239af5 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -18,9 +18,11 @@ struct pt_regs; struct notifier_block; #if defined(CONFIG_PROFILING) && defined(CONFIG_PROC_FS) -void create_prof_cpu_mask(struct proc_dir_entry *); +void create_prof_cpu_mask(struct proc_dir_entry *de); #else -#define create_prof_cpu_mask(x) do { (void)(x); } while (0) +static inline void create_prof_cpu_mask(struct proc_dir_entry *de) +{ +} #endif enum profile_type { -- cgit v1.2.3 From ac331d158e198d2a91a5b0a3ec4ca9991fdb57af Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Fri, 25 Jul 2008 01:45:38 -0700 Subject: call_usermodehelper(): increase reliability Presently call_usermodehelper_setup() uses GFP_ATOMIC. but it can return NULL _very_ easily. GFP_ATOMIC is needed only when we can't sleep. and, GFP_KERNEL is robust and better. thus, I add gfp_mask argument to call_usermodehelper_setup(). So, its callers pass the gfp_t as below: call_usermodehelper() and call_usermodehelper_keys(): depend on 'wait' argument. call_usermodehelper_pipe(): always GFP_KERNEL because always run under process context. orderly_poweroff(): pass to GFP_ATOMIC because may run under interrupt context. Signed-off-by: KOSAKI Motohiro Cc: "Paul Menage" Reviewed-by: Li Zefan Acked-by: Jeremy Fitzhardinge Cc: Rusty Russell Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kmod.h | 11 +++++++---- kernel/kmod.c | 9 +++++---- kernel/sys.c | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 0509c4ce4857..a1a91577813c 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -41,8 +42,8 @@ struct file; struct subprocess_info; /* Allocate a subprocess_info structure */ -struct subprocess_info *call_usermodehelper_setup(char *path, - char **argv, char **envp); +struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, + char **envp, gfp_t gfp_mask); /* Set various pieces of state into the subprocess_info structure */ void call_usermodehelper_setkeys(struct subprocess_info *info, @@ -69,8 +70,9 @@ static inline int call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait) { struct subprocess_info *info; + gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; - info = call_usermodehelper_setup(path, argv, envp); + info = call_usermodehelper_setup(path, argv, envp, gfp_mask); if (info == NULL) return -ENOMEM; return call_usermodehelper_exec(info, wait); @@ -81,8 +83,9 @@ call_usermodehelper_keys(char *path, char **argv, char **envp, struct key *session_keyring, enum umh_wait wait) { struct subprocess_info *info; + gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; - info = call_usermodehelper_setup(path, argv, envp); + info = call_usermodehelper_setup(path, argv, envp, gfp_mask); if (info == NULL) return -ENOMEM; diff --git a/kernel/kmod.c b/kernel/kmod.c index 2989f67c4446..2456d1a0befb 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -352,16 +352,17 @@ static inline void register_pm_notifier_callback(void) {} * @path: path to usermode executable * @argv: arg vector for process * @envp: environment for process + * @gfp_mask: gfp mask for memory allocation * * Returns either %NULL on allocation failure, or a subprocess_info * structure. This should be passed to call_usermodehelper_exec to * exec the process and free the structure. */ -struct subprocess_info *call_usermodehelper_setup(char *path, - char **argv, char **envp) +struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, + char **envp, gfp_t gfp_mask) { struct subprocess_info *sub_info; - sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC); + sub_info = kzalloc(sizeof(struct subprocess_info), gfp_mask); if (!sub_info) goto out; @@ -494,7 +495,7 @@ int call_usermodehelper_pipe(char *path, char **argv, char **envp, struct subprocess_info *sub_info; int ret; - sub_info = call_usermodehelper_setup(path, argv, envp); + sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL); if (sub_info == NULL) return -ENOMEM; diff --git a/kernel/sys.c b/kernel/sys.c index 14e97282eb6c..6c2188046048 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1795,7 +1795,7 @@ int orderly_poweroff(bool force) goto out; } - info = call_usermodehelper_setup(argv[0], argv, envp); + info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC); if (info == NULL) { argv_free(argv); goto out; -- cgit v1.2.3 From 4500d067eeb3d00679335d9cf5c6536e79cd3ef4 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 25 Jul 2008 01:45:49 -0700 Subject: init.h: remove obsolete content Remove apparently obsolete content from init.h referring to gcc 2.9x and to "no_module_init". Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/init.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/init.h b/include/linux/init.h index 21d658cdfa27..42ae95411a93 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -275,13 +275,7 @@ void __init parse_early_param(void); #define security_initcall(fn) module_init(fn) -/* These macros create a dummy inline: gcc 2.9x does not count alias - as usage, hence the `unused function' warning when __init functions - are declared static. We use the dummy __*_module_inline functions - both to kill the warning and check the type of the init/cleanup - function. */ - -/* Each module must use one module_init(), or one no_module_init */ +/* Each module must use one module_init(). */ #define module_init(initfn) \ static inline initcall_t __inittest(void) \ { return initfn; } \ -- cgit v1.2.3 From b6c63937001889af6fe431aaba97e59d04e028e7 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Fri, 25 Jul 2008 01:45:52 -0700 Subject: Rename WARN() to WARNING() to clear the namespace We want to use WARN() as a variant of WARN_ON(), however a few drivers are using WARN() internally. This patch renames these to WARNING() to avoid the namespace clash. A few cases were defining but not using the thing, for those cases I just deleted the definition. Signed-off-by: Arjan van de Ven Acked-by: Greg KH Cc: Karsten Keil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/isdn/hisax/st5481.h | 4 ++-- drivers/isdn/hisax/st5481_b.c | 4 ++-- drivers/isdn/hisax/st5481_d.c | 6 +++--- drivers/isdn/hisax/st5481_usb.c | 18 +++++++++--------- drivers/usb/gadget/at91_udc.h | 2 +- drivers/usb/gadget/cdc2.c | 2 +- drivers/usb/gadget/ether.c | 2 +- drivers/usb/gadget/file_storage.c | 14 +++++++------- drivers/usb/gadget/fsl_usb2_udc.c | 2 +- drivers/usb/gadget/fsl_usb2_udc.h | 2 +- drivers/usb/gadget/gmidi.c | 2 -- drivers/usb/gadget/goku_udc.c | 2 +- drivers/usb/gadget/goku_udc.h | 2 +- drivers/usb/gadget/inode.c | 2 -- drivers/usb/gadget/net2280.c | 2 +- drivers/usb/gadget/net2280.h | 2 +- drivers/usb/gadget/omap_udc.c | 6 +++--- drivers/usb/gadget/omap_udc.h | 2 +- drivers/usb/gadget/printer.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.h | 2 +- drivers/usb/gadget/u_ether.c | 3 --- drivers/usb/host/isp116x-hcd.c | 2 +- drivers/usb/host/isp116x.h | 2 +- drivers/usb/host/sl811-hcd.c | 2 +- drivers/usb/host/sl811.h | 2 +- drivers/usb/misc/usbtest.c | 4 ++-- include/linux/usb/composite.h | 2 +- 28 files changed, 48 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h index 2044e7173ab4..cff7a6354334 100644 --- a/drivers/isdn/hisax/st5481.h +++ b/drivers/isdn/hisax/st5481.h @@ -220,7 +220,7 @@ enum { #define ERR(format, arg...) \ printk(KERN_ERR "%s:%s: " format "\n" , __FILE__, __func__ , ## arg) -#define WARN(format, arg...) \ +#define WARNING(format, arg...) \ printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__, __func__ , ## arg) #define INFO(format, arg...) \ @@ -412,7 +412,7 @@ struct st5481_adapter { ({ \ int status; \ if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \ - WARN("usb_submit_urb failed,status=%d", status); \ + WARNING("usb_submit_urb failed,status=%d", status); \ } \ status; \ }) diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c index fa64115cd7c7..0074b600a0ef 100644 --- a/drivers/isdn/hisax/st5481_b.c +++ b/drivers/isdn/hisax/st5481_b.c @@ -180,7 +180,7 @@ static void usb_b_out_complete(struct urb *urb) DBG(4,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); if (b_out->busy == 0) { st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL); } @@ -372,6 +372,6 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg) B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); break; default: - WARN("pr %#x\n", pr); + WARNING("pr %#x\n", pr); } } diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c index b8c4855cc889..077991c1cd05 100644 --- a/drivers/isdn/hisax/st5481_d.c +++ b/drivers/isdn/hisax/st5481_d.c @@ -389,7 +389,7 @@ static void usb_d_out_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); break; default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); if (d_out->busy == 0) { st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter); } @@ -420,7 +420,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) isdnhdlc_out_init(&d_out->hdlc_state, 1, 0); if (test_and_set_bit(buf_nr, &d_out->busy)) { - WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); + WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); return; } urb = d_out->urb[buf_nr]; @@ -601,7 +601,7 @@ void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL); break; default: - WARN("pr %#x\n", pr); + WARNING("pr %#x\n", pr); break; } } diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c index 427a8b0520f5..ec3c0e507669 100644 --- a/drivers/isdn/hisax/st5481_usb.c +++ b/drivers/isdn/hisax/st5481_usb.c @@ -66,7 +66,7 @@ static void usb_ctrl_msg(struct st5481_adapter *adapter, struct ctrl_msg *ctrl_msg; if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) { - WARN("control msg FIFO full"); + WARNING("control msg FIFO full"); return; } ctrl_msg = &ctrl->msg_fifo.data[w_index]; @@ -139,7 +139,7 @@ static void usb_ctrl_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); break; } } @@ -198,7 +198,7 @@ static void usb_int_complete(struct urb *urb) DBG(2, "urb shutting down with status: %d", urb->status); return; default: - WARN("nonzero urb status received: %d", urb->status); + WARNING("nonzero urb status received: %d", urb->status); goto exit; } @@ -235,7 +235,7 @@ static void usb_int_complete(struct urb *urb) exit: status = usb_submit_urb (urb, GFP_ATOMIC); if (status) - WARN("usb_submit_urb failed with result %d", status); + WARNING("usb_submit_urb failed with result %d", status); } /* ====================================================================== @@ -257,7 +257,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) DBG(2,""); if ((status = usb_reset_configuration (dev)) < 0) { - WARN("reset_configuration failed,status=%d",status); + WARNING("reset_configuration failed,status=%d",status); return status; } @@ -269,7 +269,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) // Check if the config is sane if ( altsetting->desc.bNumEndpoints != 7 ) { - WARN("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints); + WARNING("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints); return -EINVAL; } @@ -279,7 +279,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) // Use alternative setting 3 on interface 0 to have 2B+D if ((status = usb_set_interface (dev, 0, 3)) < 0) { - WARN("usb_set_interface failed,status=%d",status); + WARNING("usb_set_interface failed,status=%d",status); return status; } @@ -497,7 +497,7 @@ static void usb_in_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); break; } } @@ -523,7 +523,7 @@ static void usb_in_complete(struct urb *urb) DBG(4,"count=%d",status); DBG_PACKET(0x400, in->rcvbuf, status); if (!(skb = dev_alloc_skb(status))) { - WARN("receive out of memory\n"); + WARNING("receive out of memory\n"); break; } memcpy(skb_put(skb, status), in->rcvbuf, status); diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index a973f2a50fb9..c65d62295890 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -171,7 +171,7 @@ struct at91_request { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index d490d0289507..a39a4b940c33 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -170,7 +170,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) * but if the controller isn't recognized at all then * that assumption is a bit more likely to be wrong. */ - WARN(cdev, "controller '%s' not recognized; trying %s\n", + WARNING(cdev, "controller '%s' not recognized; trying %s\n", gadget->name, cdc_config_driver.label); device_desc.bcdDevice = diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d7aaaa29b1e1..bcac2e68660d 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -293,7 +293,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev) * but if the controller isn't recognized at all then * that assumption is a bit more likely to be wrong. */ - WARN(cdev, "controller '%s' not recognized; trying %s\n", + WARNING(cdev, "controller '%s' not recognized; trying %s\n", gadget->name, eth_config_driver.label); device_desc.bcdDevice = diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 15c24edbb61a..ea2c31d18080 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL"); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ +#define WARNING(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) @@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg) if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", fsg->ep0->name, rc); } return rc; @@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) /* Save the command for later */ if (fsg->cbbuf_cmnd_size) - WARN(fsg, "CB[I] overwriting previous command\n"); + WARNING(fsg, "CB[I] overwriting previous command\n"); fsg->cbbuf_cmnd_size = req->actual; memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); @@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, * submissions if DMA is enabled. */ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc); } } @@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint halt\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_halt -> %d\n", rc); + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); rc = 0; break; } @@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint wedge\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_wedge -> %d\n", rc); + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); rc = 0; break; } @@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg) if (gcnum >= 0) mod_data.release = 0x0300 + gcnum; else { - WARN(fsg, "controller '%s' not recognized\n", + WARNING(fsg, "controller '%s' not recognized\n", fsg->gadget->name); mod_data.release = 0x0399; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 1695382f30fe..1cfccf102a2d 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (curr_ep->name == NULL) { - WARN("Invalid EP?"); + WARNING("Invalid EP?"); continue; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 98b1483ef6a5..6131752a38bc 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 7f4d4828e3aa..ea8651e3da1a 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 48f1c63b7013..60aa04847b18 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) * usb_gadget_driver_{register,unregister}() must change. */ if (the_controller) { - WARN(dev, "ignoring %s\n", pci_name(pdev)); + WARNING(dev, "ignoring %s\n", pci_name(pdev)); return -EBUSY; } if (!pdev->irq) { diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index bc4eb1e0b507..566cb2319056 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -285,7 +285,7 @@ struct goku_udc { #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 04692d59fc1c..f4585d3e90d7 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -262,8 +262,6 @@ static const char *CHIP; #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index b67ab677af72..5cfb5ebf3881 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep) * 0122, and 0124; not all cases trigger the warning. */ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARN (ep->dev, "%s lost packet sync!\n", + WARNING (ep->dev, "%s lost packet sync!\n", ep->ep.name); req->req.status = -EOVERFLOW; } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index 1f2af398a9a4..81a71dbdc2c6 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev) #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 4b79a8509e84..395bd1844482 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1120,7 +1120,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) status = -EINVAL; else if (value) { if (ep->udc->ep0_set_config) { - WARN("error changing config?\n"); + WARNING("error changing config?\n"); omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } omap_writew(UDC_STALL_CMD, UDC_SYSCON2); @@ -1764,7 +1764,7 @@ do_stall: u.r.bRequestType, u.r.bRequest, status); if (udc->ep0_set_config) { if (udc->ep0_reset_config) - WARN("error resetting config?\n"); + WARNING("error resetting config?\n"); else omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } @@ -3076,7 +3076,7 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message) * which would prevent entry to deep sleep... */ if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { - WARN("session active; suspend requires disconnect\n"); + WARNING("session active; suspend requires disconnect\n"); omap_pullup(&udc->gadget, 0); } diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 8522bbb12278..29edc51b6b22 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -188,7 +188,7 @@ struct omap_udc { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 49cd9e145a9b..e0090085b78e 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define ERROR(dev, fmt, args...) \ xprintk(dev, KERN_ERR, fmt, ## args) -#define WARN(dev, fmt, args...) \ +#define WARNING(dev, fmt, args...) \ xprintk(dev, KERN_WARNING, fmt, ## args) #define INFO(dev, fmt, args...) \ xprintk(dev, KERN_INFO, fmt, ## args) diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 8fb0066609bb..7e6725d89976 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -342,7 +342,7 @@ pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) struct pxa25x_request *req; req = container_of (_req, struct pxa25x_request, req); - WARN_ON (!list_empty (&req->queue)); + WARN_ON(!list_empty (&req->queue)); kfree(req); } @@ -1556,7 +1556,7 @@ config_change: * tell us about config change events, * so later ones may fail... */ - WARN("config change %02x fail %d?\n", + WARNING("config change %02x fail %d?\n", u.r.bRequest, i); return; /* TODO experiment: if has_cfr, @@ -2330,7 +2330,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) unsigned long flags; if (!udc->mach->gpio_pullup && !udc->mach->udc_command) - WARN("USB host won't detect disconnect!\n"); + WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; local_irq_save(flags); diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 4d11ece7c95f..c8a13215e02c 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -259,7 +259,7 @@ dump_state(struct pxa25x_udc *dev) #define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 5458f43a8668..3791e6271903 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -116,7 +116,6 @@ static inline int qlen(struct usb_gadget *gadget) #undef DBG #undef VDBG #undef ERROR -#undef WARN #undef INFO #define xprintk(d, level, fmt, args...) \ @@ -140,8 +139,6 @@ static inline int qlen(struct usb_gadget *gadget) #define ERROR(dev, fmt, args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev, fmt, args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev, fmt, args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 31178e10cbbe..ce1ca0ba0515 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -882,7 +882,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd, for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++) msleep(3); if (!list_empty(&hep->urb_list)) - WARN("ep %p not empty?\n", ep); + WARNING("ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h index 595b90a99848..aa211bafcff9 100644 --- a/drivers/usb/host/isp116x.h +++ b/drivers/usb/host/isp116x.h @@ -338,7 +338,7 @@ struct isp116x_ep { #endif #define ERR(stuff...) printk(KERN_ERR "116x: " stuff) -#define WARN(stuff...) printk(KERN_WARNING "116x: " stuff) +#define WARNING(stuff...) printk(KERN_WARNING "116x: " stuff) #define INFO(stuff...) printk(KERN_INFO "116x: " stuff) /* ------------------------------------------------- */ diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 340d72da554a..8a74bbb57d08 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1026,7 +1026,7 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (!list_empty(&hep->urb_list)) msleep(3); if (!list_empty(&hep->urb_list)) - WARN("ep %p not empty?\n", ep); + WARNING("ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h index 7690d98e42a7..b6b8c1f233dd 100644 --- a/drivers/usb/host/sl811.h +++ b/drivers/usb/host/sl811.h @@ -261,6 +261,6 @@ sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) #endif #define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) -#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff) +#define WARNING(stuff...) printk(KERN_WARNING "sl811: " stuff) #define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 054dedd28127..b358c4e1cf21 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -81,7 +81,7 @@ static struct usb_device *testdev_to_usbdev (struct usbtest_dev *test) #define ERROR(tdev, fmt, args...) \ dev_err(&(tdev)->intf->dev , fmt , ## args) -#define WARN(tdev, fmt, args...) \ +#define WARNING(tdev, fmt, args...) \ dev_warn(&(tdev)->intf->dev , fmt , ## args) /*-------------------------------------------------------------------------*/ @@ -1946,7 +1946,7 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id) status = get_endpoints (dev, intf); if (status < 0) { - WARN(dev, "couldn't get endpoints, %d\n", + WARNING(dev, "couldn't get endpoints, %d\n", status); return status; } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 747c3a49cdc9..c932390c6da0 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -330,7 +330,7 @@ extern int usb_string_id(struct usb_composite_dev *c); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ +#define WARNING(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) -- cgit v1.2.3 From 2711b793eb62a5873a0ba583a69252040aef176e Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Fri, 25 Jul 2008 01:45:56 -0700 Subject: kallsyms: unify 32- and 64-bit code Use the %p format string which already accounts for the padding you need with a pointer type on a particular architecture. Also replace the macro with a static inline function to match the rest of the file. Cc: Heiko Carstens Cc: Arjan van de Ven Signed-off-by: Vegard Nossum Cc: Sam Ravnborg Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kallsyms.h | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index 00c1801099fa..57aefa160a92 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -6,6 +6,7 @@ #define _LINUX_KALLSYMS_H #include +#include #include #define KSYM_NAME_LEN 128 @@ -105,18 +106,10 @@ static inline void print_fn_descriptor_symbol(const char *fmt, void *addr) print_symbol(fmt, (unsigned long)addr); } -#ifndef CONFIG_64BIT -#define print_ip_sym(ip) \ -do { \ - printk("[<%08lx>]", ip); \ - print_symbol(" %s\n", ip); \ -} while(0) -#else -#define print_ip_sym(ip) \ -do { \ - printk("[<%016lx>]", ip); \ - print_symbol(" %s\n", ip); \ -} while(0) -#endif +static inline void print_ip_sym(unsigned long ip) +{ + printk("[<%p>]", (void *) ip); + print_symbol(" %s\n", ip); +} #endif /*_LINUX_KALLSYMS_H*/ -- cgit v1.2.3 From 717115e1a5856b57af0f71e1df7149108294fc10 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Fri, 25 Jul 2008 01:45:58 -0700 Subject: printk ratelimiting rewrite All ratelimit user use same jiffies and burst params, so some messages (callbacks) will be lost. For example: a call printk_ratelimit(5 * HZ, 1) b call printk_ratelimit(5 * HZ, 1) before the 5*HZ timeout of a, then b will will be supressed. - rewrite __ratelimit, and use a ratelimit_state as parameter. Thanks for hints from andrew. - Add WARN_ON_RATELIMIT, update rcupreempt.h - remove __printk_ratelimit - use __ratelimit in net_ratelimit Signed-off-by: Dave Young Cc: "David S. Miller" Cc: "Paul E. McKenney" Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bug.h | 3 +++ include/linux/kernel.h | 8 ++----- include/linux/net.h | 3 +-- include/linux/ratelimit.h | 27 +++++++++++++++++++++++ include/linux/rcupreempt.h | 9 ++++++-- kernel/printk.c | 17 +++----------- kernel/sysctl.c | 4 ++-- lib/ratelimit.c | 55 +++++++++++++++++++++++++--------------------- net/core/sysctl_net_core.c | 4 ++-- net/core/utils.c | 5 ++--- 10 files changed, 79 insertions(+), 56 deletions(-) create mode 100644 include/linux/ratelimit.h (limited to 'include/linux') diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index a346e744e770..a3f738cffdb6 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -97,6 +97,9 @@ extern void warn_slowpath(const char *file, const int line, unlikely(__ret_warn_once); \ }) +#define WARN_ON_RATELIMIT(condition, state) \ + WARN_ON((condition) && __ratelimit(state)) + #ifdef CONFIG_SMP # define WARN_ON_SMP(x) WARN_ON(x) #else diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 5c4b1251e110..fdbbf72ca2eb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -189,11 +190,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) asmlinkage int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))) __cold; -extern int printk_ratelimit_jiffies; -extern int printk_ratelimit_burst; +extern struct ratelimit_state printk_ratelimit_state; extern int printk_ratelimit(void); -extern int __ratelimit(int ratelimit_jiffies, int ratelimit_burst); -extern int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst); extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, unsigned int interval_msec); #else @@ -204,8 +202,6 @@ static inline int printk(const char *s, ...) __attribute__ ((format (printf, 1, 2))); static inline int __cold printk(const char *s, ...) { return 0; } static inline int printk_ratelimit(void) { return 0; } -static inline int __printk_ratelimit(int ratelimit_jiffies, \ - int ratelimit_burst) { return 0; } static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \ unsigned int interval_msec) \ { return false; } diff --git a/include/linux/net.h b/include/linux/net.h index 2f999fbb188d..4a9a30f2d68f 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -351,8 +351,7 @@ static const struct proto_ops name##_ops = { \ #ifdef CONFIG_SYSCTL #include -extern int net_msg_cost; -extern int net_msg_burst; +extern struct ratelimit_state net_ratelimit_state; #endif #endif /* __KERNEL__ */ diff --git a/include/linux/ratelimit.h b/include/linux/ratelimit.h new file mode 100644 index 000000000000..18a5b9ba9d40 --- /dev/null +++ b/include/linux/ratelimit.h @@ -0,0 +1,27 @@ +#ifndef _LINUX_RATELIMIT_H +#define _LINUX_RATELIMIT_H +#include + +#define DEFAULT_RATELIMIT_INTERVAL (5 * HZ) +#define DEFAULT_RATELIMIT_BURST 10 + +struct ratelimit_state { + int interval; + int burst; + int printed; + int missed; + unsigned long begin; +}; + +#define DEFINE_RATELIMIT_STATE(name, interval, burst) \ + struct ratelimit_state name = {interval, burst,} + +extern int __ratelimit(struct ratelimit_state *rs); + +static inline int ratelimit(void) +{ + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + return __ratelimit(&rs); +} +#endif diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index f04b64eca636..0967f03b0705 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h @@ -115,16 +115,21 @@ DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); static inline void rcu_enter_nohz(void) { + static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); + smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ __get_cpu_var(rcu_dyntick_sched).dynticks++; - WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1); + WARN_ON_RATELIMIT(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1, &rs); } static inline void rcu_exit_nohz(void) { + static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); + smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ __get_cpu_var(rcu_dyntick_sched).dynticks++; - WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1)); + WARN_ON_RATELIMIT(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1), + &rs); } #else /* CONFIG_NO_HZ */ diff --git a/kernel/printk.c b/kernel/printk.c index 3f7a2a94583b..a7f7559c5f6c 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1308,6 +1308,8 @@ void tty_write_message(struct tty_struct *tty, char *msg) } #if defined CONFIG_PRINTK + +DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10); /* * printk rate limiting, lifted from the networking subsystem. * @@ -1315,22 +1317,9 @@ void tty_write_message(struct tty_struct *tty, char *msg) * every printk_ratelimit_jiffies to make a denial-of-service * attack impossible. */ -int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst) -{ - return __ratelimit(ratelimit_jiffies, ratelimit_burst); -} -EXPORT_SYMBOL(__printk_ratelimit); - -/* minimum time in jiffies between messages */ -int printk_ratelimit_jiffies = 5 * HZ; - -/* number of messages we send before ratelimiting */ -int printk_ratelimit_burst = 10; - int printk_ratelimit(void) { - return __printk_ratelimit(printk_ratelimit_jiffies, - printk_ratelimit_burst); + return __ratelimit(&printk_ratelimit_state); } EXPORT_SYMBOL(printk_ratelimit); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1a8299d1fe59..35a50db9b6ce 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -624,7 +624,7 @@ static struct ctl_table kern_table[] = { { .ctl_name = KERN_PRINTK_RATELIMIT, .procname = "printk_ratelimit", - .data = &printk_ratelimit_jiffies, + .data = &printk_ratelimit_state.interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, @@ -633,7 +633,7 @@ static struct ctl_table kern_table[] = { { .ctl_name = KERN_PRINTK_RATELIMIT_BURST, .procname = "printk_ratelimit_burst", - .data = &printk_ratelimit_burst, + .data = &printk_ratelimit_state.burst, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, diff --git a/lib/ratelimit.c b/lib/ratelimit.c index 485e3040dcd4..35136671b215 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -3,6 +3,9 @@ * * Isolated from kernel/printk.c by Dave Young * + * 2008-05-01 rewrite the function and use a ratelimit_state data struct as + * parameter. Now every user can use their own standalone ratelimit_state. + * * This file is released under the GPLv2. * */ @@ -11,41 +14,43 @@ #include #include +static DEFINE_SPINLOCK(ratelimit_lock); +static unsigned long flags; + /* * __ratelimit - rate limiting - * @ratelimit_jiffies: minimum time in jiffies between two callbacks - * @ratelimit_burst: number of callbacks we do before ratelimiting + * @rs: ratelimit_state data * - * This enforces a rate limit: not more than @ratelimit_burst callbacks - * in every ratelimit_jiffies + * This enforces a rate limit: not more than @rs->ratelimit_burst callbacks + * in every @rs->ratelimit_jiffies */ -int __ratelimit(int ratelimit_jiffies, int ratelimit_burst) +int __ratelimit(struct ratelimit_state *rs) { - static DEFINE_SPINLOCK(ratelimit_lock); - static unsigned toks = 10 * 5 * HZ; - static unsigned long last_msg; - static int missed; - unsigned long flags; - unsigned long now = jiffies; + if (!rs->interval) + return 1; spin_lock_irqsave(&ratelimit_lock, flags); - toks += now - last_msg; - last_msg = now; - if (toks > (ratelimit_burst * ratelimit_jiffies)) - toks = ratelimit_burst * ratelimit_jiffies; - if (toks >= ratelimit_jiffies) { - int lost = missed; + if (!rs->begin) + rs->begin = jiffies; - missed = 0; - toks -= ratelimit_jiffies; - spin_unlock_irqrestore(&ratelimit_lock, flags); - if (lost) - printk(KERN_WARNING "%s: %d messages suppressed\n", - __func__, lost); - return 1; + if (time_is_before_jiffies(rs->begin + rs->interval)) { + if (rs->missed) + printk(KERN_WARNING "%s: %d callbacks suppressed\n", + __func__, rs->missed); + rs->begin = 0; + rs->printed = 0; + rs->missed = 0; } - missed++; + if (rs->burst && rs->burst > rs->printed) + goto print; + + rs->missed++; spin_unlock_irqrestore(&ratelimit_lock, flags); return 0; + +print: + rs->printed++; + spin_unlock_irqrestore(&ratelimit_lock, flags); + return 1; } EXPORT_SYMBOL(__ratelimit); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index a570e2af22cb..f686467ff12b 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -67,7 +67,7 @@ static struct ctl_table net_core_table[] = { { .ctl_name = NET_CORE_MSG_COST, .procname = "message_cost", - .data = &net_msg_cost, + .data = &net_ratelimit_state.interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, @@ -76,7 +76,7 @@ static struct ctl_table net_core_table[] = { { .ctl_name = NET_CORE_MSG_BURST, .procname = "message_burst", - .data = &net_msg_burst, + .data = &net_ratelimit_state.burst, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, diff --git a/net/core/utils.c b/net/core/utils.c index 8031eb59054e..72e0ebe964a0 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -31,17 +31,16 @@ #include #include -int net_msg_cost __read_mostly = 5*HZ; -int net_msg_burst __read_mostly = 10; int net_msg_warn __read_mostly = 1; EXPORT_SYMBOL(net_msg_warn); +DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10); /* * All net warning printk()s should be guarded by this function. */ int net_ratelimit(void) { - return __printk_ratelimit(net_msg_cost, net_msg_burst); + return __ratelimit(&net_ratelimit_state); } EXPORT_SYMBOL(net_ratelimit); -- cgit v1.2.3 From 472dba7d117844c746be97db6be26c2810d79b62 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 25 Jul 2008 01:45:58 -0700 Subject: sm501: add power control callback Add callback to get or set the power control if the device has the sleep connected to some form of GPIO. Signed-off-by: Ben Dooks Cc: Arnaud Patard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/sm501.c | 31 +++++++++++++++++++++++++++++++ include/linux/sm501.h | 7 +++++++ 2 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index e2530df4d85c..9296b2673b52 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1138,8 +1138,31 @@ static int sm501_plat_probe(struct platform_device *dev) } #ifdef CONFIG_PM + /* power management support */ +static void sm501_set_power(struct sm501_devdata *sm, int on) +{ + struct sm501_platdata *pd = sm->platdata; + + if (pd == NULL) + return; + + if (pd->get_power) { + if (pd->get_power(sm->dev) == on) { + dev_dbg(sm->dev, "is already %d\n", on); + return; + } + } + + if (pd->set_power) { + dev_dbg(sm->dev, "setting power to %d\n", on); + + pd->set_power(sm->dev, on); + sm501_mdelay(sm, 10); + } +} + static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) { struct sm501_devdata *sm = platform_get_drvdata(pdev); @@ -1148,6 +1171,12 @@ static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL); sm501_dump_regs(sm); + + if (sm->platdata) { + if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF) + sm501_set_power(sm, 0); + } + return 0; } @@ -1155,6 +1184,8 @@ static int sm501_plat_resume(struct platform_device *pdev) { struct sm501_devdata *sm = platform_get_drvdata(pdev); + sm501_set_power(sm, 1); + sm501_dump_regs(sm); sm501_dump_gate(sm); sm501_dump_clk(sm); diff --git a/include/linux/sm501.h b/include/linux/sm501.h index b530fa6a1d34..145405bf9efa 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -157,6 +157,8 @@ struct sm501_init_gpio { struct sm501_reg_init gpio_ddr_high; }; +#define SM501_FLAG_SUSPEND_OFF (1<<4) + /* sm501_platdata * * This is passed with the platform device to allow the board @@ -170,6 +172,11 @@ struct sm501_platdata { struct sm501_init_gpio *init_gpiop; struct sm501_platdata_fb *fb; + int flags; + + int (*get_power)(struct device *dev); + int (*set_power)(struct device *dev, unsigned int on); + struct sm501_platdata_gpio_i2c *gpio_i2c; unsigned int gpio_i2c_nr; }; -- cgit v1.2.3 From f61be273d3699d174bc1438e6804f9f9e52bb932 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 25 Jul 2008 01:45:59 -0700 Subject: sm501: add gpiolib support Add support for exporting the GPIOs on the SM501 via gpiolib. Signed-off-by: Ben Dooks Cc: Arnaud Patard Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/Kconfig | 8 ++ drivers/mfd/sm501.c | 299 +++++++++++++++++++++++++++++++++++++++++--------- include/linux/sm501.h | 20 +--- 3 files changed, 257 insertions(+), 70 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9f93c29fed35..bac9e973ece0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -19,6 +19,14 @@ config MFD_SM501 interface. The device may be connected by PCI or local bus with varying functions enabled. +config MFD_SM501_GPIO + bool "Export GPIO via GPIO layer" + depends on MFD_SM501 && HAVE_GPIO_LIB + ---help--- + This option uses the gpio library layer to export the 64 GPIO + lines on the SM501. The platform data is used to supply the + base number for the first GPIO line to register. + config MFD_ASIC3 bool "Support for Compaq ASIC3" depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 9296b2673b52..be8713908125 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,10 +32,29 @@ struct sm501_device { struct platform_device pdev; }; +struct sm501_gpio; + +struct sm501_gpio_chip { + struct gpio_chip gpio; + struct sm501_gpio *ourgpio; /* to get back to parent. */ + void __iomem *regbase; +}; + +struct sm501_gpio { + struct sm501_gpio_chip low; + struct sm501_gpio_chip high; + spinlock_t lock; + + unsigned int registered : 1; + void __iomem *regs; + struct resource *regs_res; +}; + struct sm501_devdata { spinlock_t reg_lock; struct mutex clock_lock; struct list_head devices; + struct sm501_gpio gpio; struct device *dev; struct resource *io_res; @@ -42,6 +62,7 @@ struct sm501_devdata { struct resource *regs_claim; struct sm501_platdata *platdata; + unsigned int in_suspend; unsigned long pm_misc; @@ -52,6 +73,7 @@ struct sm501_devdata { unsigned int rev; }; + #define MHZ (1000 * 1000) #ifdef DEBUG @@ -276,58 +298,6 @@ unsigned long sm501_modify_reg(struct device *dev, EXPORT_SYMBOL_GPL(sm501_modify_reg); -unsigned long sm501_gpio_get(struct device *dev, - unsigned long gpio) -{ - struct sm501_devdata *sm = dev_get_drvdata(dev); - unsigned long result; - unsigned long reg; - - reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; - result = readl(sm->regs + reg); - - result >>= (gpio & 31); - return result & 1UL; -} - -EXPORT_SYMBOL_GPL(sm501_gpio_get); - -void sm501_gpio_set(struct device *dev, - unsigned long gpio, - unsigned int to, - unsigned int dir) -{ - struct sm501_devdata *sm = dev_get_drvdata(dev); - - unsigned long bit = 1 << (gpio & 31); - unsigned long base; - unsigned long save; - unsigned long val; - - base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; - base += SM501_GPIO; - - spin_lock_irqsave(&sm->reg_lock, save); - - val = readl(sm->regs + base) & ~bit; - if (to) - val |= bit; - writel(val, sm->regs + base); - - val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit; - if (dir) - val |= bit; - - writel(val, sm->regs + SM501_GPIO_DDR_LOW); - sm501_sync_regs(sm); - - spin_unlock_irqrestore(&sm->reg_lock, save); - -} - -EXPORT_SYMBOL_GPL(sm501_gpio_set); - - /* sm501_unit_power * * alters the power active gate to set specific units on or off @@ -906,6 +876,226 @@ static int sm501_register_display(struct sm501_devdata *sm, return sm501_register_device(sm, pdev); } +#ifdef CONFIG_MFD_SM501_GPIO + +static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct sm501_gpio_chip, gpio); +} + +static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio) +{ + return container_of(gpio, struct sm501_devdata, gpio); +} + +static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) + +{ + struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip); + unsigned long result; + + result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW); + result >>= offset; + + return result & 1UL; +} + +static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + unsigned long bit = 1 << offset; + void __iomem *regs = smchip->regbase; + unsigned long save; + unsigned long val; + + dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", + __func__, chip, offset); + + spin_lock_irqsave(&smgpio->lock, save); + + val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit; + if (value) + val |= bit; + writel(val, regs); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); +} + +static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + void __iomem *regs = smchip->regbase; + unsigned long bit = 1 << offset; + unsigned long save; + unsigned long ddr; + + dev_info(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", + __func__, chip, offset); + + spin_lock_irqsave(&smgpio->lock, save); + + ddr = readl(regs + SM501_GPIO_DDR_LOW); + writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); + + return 0; +} + +static int sm501_gpio_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + unsigned long bit = 1 << offset; + void __iomem *regs = smchip->regbase; + unsigned long save; + unsigned long val; + unsigned long ddr; + + dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n", + __func__, chip, offset, value); + + spin_lock_irqsave(&smgpio->lock, save); + + val = readl(regs + SM501_GPIO_DATA_LOW); + if (value) + val |= bit; + else + val &= ~bit; + writel(val, regs); + + ddr = readl(regs + SM501_GPIO_DDR_LOW); + writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + writel(val, regs + SM501_GPIO_DATA_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); + + return 0; +} + +static struct gpio_chip gpio_chip_template = { + .ngpio = 32, + .direction_input = sm501_gpio_input, + .direction_output = sm501_gpio_output, + .set = sm501_gpio_set, + .get = sm501_gpio_get, +}; + +static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, + struct sm501_gpio *gpio, + struct sm501_gpio_chip *chip) +{ + struct sm501_platdata *pdata = sm->platdata; + struct gpio_chip *gchip = &chip->gpio; + unsigned base = pdata->gpio_base; + + memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip)); + + if (chip == &gpio->high) { + base += 32; + chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; + gchip->label = "SM501-HIGH"; + } else { + chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW; + gchip->label = "SM501-LOW"; + } + + gchip->base = base; + chip->ourgpio = gpio; + + return gpiochip_add(gchip); +} + +static int sm501_register_gpio(struct sm501_devdata *sm) +{ + struct sm501_gpio *gpio = &sm->gpio; + resource_size_t iobase = sm->io_res->start + SM501_GPIO; + int ret; + int tmp; + + dev_dbg(sm->dev, "registering gpio block %08llx\n", + (unsigned long long)iobase); + + spin_lock_init(&gpio->lock); + + gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio"); + if (gpio->regs_res == NULL) { + dev_err(sm->dev, "gpio: failed to request region\n"); + return -ENXIO; + } + + gpio->regs = ioremap(iobase, 0x20); + if (gpio->regs == NULL) { + dev_err(sm->dev, "gpio: failed to remap registers\n"); + ret = -ENXIO; + goto err_mapped; + } + + /* Register both our chips. */ + + ret = sm501_gpio_register_chip(sm, gpio, &gpio->low); + if (ret) { + dev_err(sm->dev, "failed to add low chip\n"); + goto err_mapped; + } + + ret = sm501_gpio_register_chip(sm, gpio, &gpio->high); + if (ret) { + dev_err(sm->dev, "failed to add high chip\n"); + goto err_low_chip; + } + + gpio->registered = 1; + + return 0; + + err_low_chip: + tmp = gpiochip_remove(&gpio->low.gpio); + if (tmp) { + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); + return ret; + } + + err_mapped: + release_resource(gpio->regs_res); + kfree(gpio->regs_res); + + return ret; +} + +static void sm501_gpio_remove(struct sm501_devdata *sm) +{ + int ret; + + ret = gpiochip_remove(&sm->gpio.low.gpio); + if (ret) + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); + + ret = gpiochip_remove(&sm->gpio.high.gpio); + if (ret) + dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n"); +} + +#else +static int sm501_register_gpio(struct sm501_devdata *sm) +{ + return 0; +} + +static void sm501_gpio_remove(struct sm501_devdata *sm) +{ +} +#endif + /* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers @@ -1059,6 +1249,8 @@ static int sm501_init_dev(struct sm501_devdata *sm) sm501_register_usbhost(sm, &mem_avail); if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) sm501_register_uart(sm, idata->devices); + if (idata->devices & SM501_USE_GPIO) + sm501_register_gpio(sm); } ret = sm501_check_clocks(sm); @@ -1366,6 +1558,9 @@ static void sm501_dev_remove(struct sm501_devdata *sm) sm501_remove_sub(sm, smdev); device_remove_file(sm->dev, &dev_attr_dbg_regs); + + if (sm->gpio.registered) + sm501_gpio_remove(sm); } static void sm501_pci_remove(struct pci_dev *dev) diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 145405bf9efa..6ea39007c8a3 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -46,24 +46,6 @@ extern unsigned long sm501_modify_reg(struct device *dev, unsigned long set, unsigned long clear); -/* sm501_gpio_set - * - * set the state of the given GPIO line -*/ - -extern void sm501_gpio_set(struct device *dev, - unsigned long gpio, - unsigned int to, - unsigned int dir); - -/* sm501_gpio_get - * - * get the state of the given GPIO line -*/ - -extern unsigned long sm501_gpio_get(struct device *dev, - unsigned long gpio); - /* Platform data definitions */ @@ -131,6 +113,7 @@ struct sm501_reg_init { #define SM501_USE_FBACCEL (1<<6) #define SM501_USE_AC97 (1<<7) #define SM501_USE_I2S (1<<8) +#define SM501_USE_GPIO (1<<9) #define SM501_USE_ALL (0xffffffff) @@ -173,6 +156,7 @@ struct sm501_platdata { struct sm501_platdata_fb *fb; int flags; + unsigned gpio_base; int (*get_power)(struct device *dev); int (*set_power)(struct device *dev, unsigned int on); -- cgit v1.2.3 From 60e540d617b40eb3d37f1dd99c97af588ff9b70b Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Fri, 25 Jul 2008 01:46:00 -0700 Subject: sm501: gpio dynamic registration for PCI devices The SM501 PCI card requires a dyanmic gpio allocation as the number of cards is not known at compile time. Fixup the platform data and registration to deal with this. Acked-by: Ben Dooks Signed-off-by: Arnaud Patard Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/sm501.c | 6 ++++-- include/linux/sm501.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index be8713908125..c3e5a48f6148 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -996,12 +996,13 @@ static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, { struct sm501_platdata *pdata = sm->platdata; struct gpio_chip *gchip = &chip->gpio; - unsigned base = pdata->gpio_base; + int base = pdata->gpio_base; memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip)); if (chip == &gpio->high) { - base += 32; + if (base > 0) + base += 32; chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; gchip->label = "SM501-HIGH"; } else { @@ -1452,6 +1453,7 @@ static struct sm501_platdata_fb sm501_fb_pdata = { static struct sm501_platdata sm501_pci_platdata = { .init = &sm501_pci_initdata, .fb = &sm501_fb_pdata, + .gpio_base = -1, }; static int sm501_pci_probe(struct pci_dev *dev, diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 6ea39007c8a3..a8d02f36ad32 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -156,7 +156,7 @@ struct sm501_platdata { struct sm501_platdata_fb *fb; int flags; - unsigned gpio_base; + int gpio_base; int (*get_power)(struct device *dev); int (*set_power)(struct device *dev, unsigned int on); -- cgit v1.2.3 From 42cd2366fb9b58cdfc1855be32b31a78e40b2079 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 25 Jul 2008 01:46:01 -0700 Subject: sm501: gpio I2C support Add support for adding the GPIO based I2C resources. Signed-off-by: Ben Dooks Cc: Arnaud Patard Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/sm501.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/sm501.h | 10 ++++++- 2 files changed, 84 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index c3e5a48f6148..107215b28805 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1086,6 +1087,11 @@ static void sm501_gpio_remove(struct sm501_devdata *sm) dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n"); } +static int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) +{ + struct sm501_gpio *gpio = &sm->gpio; + return pin + (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base; +} #else static int sm501_register_gpio(struct sm501_devdata *sm) { @@ -1095,8 +1101,66 @@ static int sm501_register_gpio(struct sm501_devdata *sm) static void sm501_gpio_remove(struct sm501_devdata *sm) { } + +static int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) +{ + return -1; +} #endif +static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, + struct sm501_platdata_gpio_i2c *iic) +{ + struct i2c_gpio_platform_data *icd; + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "i2c-gpio", 0, + sizeof(struct i2c_gpio_platform_data)); + if (!pdev) + return -ENOMEM; + + icd = pdev->dev.platform_data; + + /* We keep the pin_sda and pin_scl fields relative in case the + * same platform data is passed to >1 SM501. + */ + + icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda); + icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl); + icd->timeout = iic->timeout; + icd->udelay = iic->udelay; + + /* note, we can't use either of the pin numbers, as the i2c-gpio + * driver uses the platform.id field to generate the bus number + * to register with the i2c core; The i2c core doesn't have enough + * entries to deal with anything we currently use. + */ + + pdev->id = iic->bus_num; + + dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n", + iic->bus_num, + icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl); + + return sm501_register_device(sm, pdev); +} + +static int sm501_register_gpio_i2c(struct sm501_devdata *sm, + struct sm501_platdata *pdata) +{ + struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c; + int index; + int ret; + + for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) { + ret = sm501_register_gpio_i2c_instance(sm, iic); + if (ret < 0) + return ret; + } + + return 0; +} + /* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers @@ -1204,6 +1268,7 @@ static unsigned int sm501_mem_local[] = { static int sm501_init_dev(struct sm501_devdata *sm) { struct sm501_initdata *idata; + struct sm501_platdata *pdata; resource_size_t mem_avail; unsigned long dramctrl; unsigned long devid; @@ -1242,7 +1307,9 @@ static int sm501_init_dev(struct sm501_devdata *sm) /* check to see if we have some device initialisation */ - idata = sm->platdata ? sm->platdata->init : NULL; + pdata = sm->platdata; + idata = pdata ? pdata->init : NULL; + if (idata) { sm501_init_regs(sm, idata); @@ -1254,6 +1321,13 @@ static int sm501_init_dev(struct sm501_devdata *sm) sm501_register_gpio(sm); } + if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) { + if (!sm->gpio.registered) + dev_err(sm->dev, "no gpio registered for i2c gpio.\n"); + else + sm501_register_gpio_i2c(sm, pdata); + } + ret = sm501_check_clocks(sm); if (ret) { dev_err(sm->dev, "M1X and M clocks sourced from different " diff --git a/include/linux/sm501.h b/include/linux/sm501.h index a8d02f36ad32..214f93209b8c 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -86,11 +86,19 @@ struct sm501_platdata_fb { struct sm501_platdata_fbsub *fb_pnl; }; -/* gpio i2c */ +/* gpio i2c + * + * Note, we have to pass in the bus number, as the number used will be + * passed to the i2c-gpio driver's platform_device.id, subsequently used + * to register the i2c bus. +*/ struct sm501_platdata_gpio_i2c { + unsigned int bus_num; unsigned int pin_sda; unsigned int pin_scl; + int udelay; + int timeout; }; /* sm501_initdata -- cgit v1.2.3 From ef53d9c5e4da147ecaa43c44c5e5945eb83970a2 Mon Sep 17 00:00:00 2001 From: Srinivasa D S Date: Fri, 25 Jul 2008 01:46:04 -0700 Subject: kprobes: improve kretprobe scalability with hashed locking Currently list of kretprobe instances are stored in kretprobe object (as used_instances,free_instances) and in kretprobe hash table. We have one global kretprobe lock to serialise the access to these lists. This causes only one kretprobe handler to execute at a time. Hence affects system performance, particularly on SMP systems and when return probe is set on lot of functions (like on all systemcalls). Solution proposed here gives fine-grain locks that performs better on SMP system compared to present kretprobe implementation. Solution: 1) Instead of having one global lock to protect kretprobe instances present in kretprobe object and kretprobe hash table. We will have two locks, one lock for protecting kretprobe hash table and another lock for kretporbe object. 2) We hold lock present in kretprobe object while we modify kretprobe instance in kretprobe object and we hold per-hash-list lock while modifying kretprobe instances present in that hash list. To prevent deadlock, we never grab a per-hash-list lock while holding a kretprobe lock. 3) We can remove used_instances from struct kretprobe, as we can track used instances of kretprobe instances using kretprobe hash table. Time duration for kernel compilation ("make -j 8") on a 8-way ppc64 system with return probes set on all systemcalls looks like this. cacheline non-cacheline Un-patched kernel aligned patch aligned patch =============================================================================== real 9m46.784s 9m54.412s 10m2.450s user 40m5.715s 40m7.142s 40m4.273s sys 2m57.754s 2m58.583s 3m17.430s =========================================================== Time duration for kernel compilation ("make -j 8) on the same system, when kernel is not probed. ========================= real 9m26.389s user 40m8.775s sys 2m7.283s ========================= Signed-off-by: Srinivasa DS Signed-off-by: Jim Keniston Acked-by: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Cc: David S. Miller Cc: Masami Hiramatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/kernel/kprobes.c | 6 +- arch/ia64/kernel/kprobes.c | 6 +- arch/powerpc/kernel/kprobes.c | 6 +- arch/s390/kernel/kprobes.c | 6 +- arch/sparc64/kernel/kprobes.c | 11 ++-- arch/x86/kernel/kprobes.c | 6 +- include/linux/kprobes.h | 7 ++- kernel/kprobes.c | 127 +++++++++++++++++++++++++++++------------- 8 files changed, 108 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index 5ee39e10c8d1..d28513f14d05 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c @@ -296,8 +296,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -337,7 +336,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) } kretprobe_assert(ri, orig_ret_address, trampoline_address); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); @@ -347,7 +346,6 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) return (void *)orig_ret_address; } -/* Called with kretprobe_lock held. */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 233434f4f88f..f07688da947c 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -429,8 +429,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) ((struct fnptr *)kretprobe_trampoline)->ip; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -485,7 +484,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) kretprobe_assert(ri, orig_ret_address, trampoline_address); reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { @@ -500,7 +499,6 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) return 1; } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index 4ba2af125450..de79915452c8 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -144,7 +144,6 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, kcb->kprobe_saved_msr = regs->msr; } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { @@ -312,8 +311,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -352,7 +350,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, regs->nip = orig_ret_address; reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 288ad490a6dd..4f82e5b5f879 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -270,7 +270,6 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, __ctl_store(kcb->kprobe_saved_ctl, 9, 11); } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { @@ -377,8 +376,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -417,7 +415,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, regs->psw.addr = orig_ret_address | PSW_ADDR_AMODE; reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index f43b5d755354..201a6e547e4a 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c @@ -478,9 +478,9 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) return 0; } -/* Called with kretprobe_lock held. The value stored in the return - * address register is actually 2 instructions before where the - * callee will return to. Sequences usually look something like this +/* The value stored in the return address register is actually 2 + * instructions before where the callee will return to. + * Sequences usually look something like this * * call some_function <--- return register points here * nop <--- call delay slot @@ -512,8 +512,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -553,7 +552,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) regs->tnpc = orig_ret_address + 4; reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 43c019f85f0d..6c27679ec6aa 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -431,7 +431,6 @@ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) regs->ip = (unsigned long)p->ainsn.insn; } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { @@ -682,8 +681,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* fixup registers */ #ifdef CONFIG_X86_64 regs->cs = __KERNEL_CS; @@ -732,7 +730,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) kretprobe_assert(ri, orig_ret_address, trampoline_address); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 04a3556bdea6..0be7795655fa 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -157,11 +157,10 @@ struct kretprobe { int nmissed; size_t data_size; struct hlist_head free_instances; - struct hlist_head used_instances; + spinlock_t lock; }; struct kretprobe_instance { - struct hlist_node uflist; /* either on free list or used list */ struct hlist_node hlist; struct kretprobe *rp; kprobe_opcode_t *ret_addr; @@ -201,7 +200,6 @@ static inline int init_test_probes(void) } #endif /* CONFIG_KPROBES_SANITY_TEST */ -extern spinlock_t kretprobe_lock; extern struct mutex kprobe_mutex; extern int arch_prepare_kprobe(struct kprobe *p); extern void arch_arm_kprobe(struct kprobe *p); @@ -214,6 +212,9 @@ extern void kprobes_inc_nmissed_count(struct kprobe *p); /* Get the kprobe at this addr (if any) - called with preemption disabled */ struct kprobe *get_kprobe(void *addr); +void kretprobe_hash_lock(struct task_struct *tsk, + struct hlist_head **head, unsigned long *flags); +void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags); struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk); /* kprobe_running() will just return the current_kprobe on this CPU */ diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 1485ca8d0e00..cb0b3bde3617 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -62,6 +62,7 @@ addr = ((kprobe_opcode_t *)(kallsyms_lookup_name(name))) #endif +static int kprobes_initialized; static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; @@ -69,8 +70,15 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; static bool kprobe_enabled; DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ -DEFINE_SPINLOCK(kretprobe_lock); /* Protects kretprobe_inst_table */ static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; +static struct { + spinlock_t lock ____cacheline_aligned; +} kretprobe_table_locks[KPROBE_TABLE_SIZE]; + +static spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) +{ + return &(kretprobe_table_locks[hash].lock); +} /* * Normally, functions that we'd want to prohibit kprobes in, are marked @@ -368,26 +376,53 @@ void __kprobes kprobes_inc_nmissed_count(struct kprobe *p) return; } -/* Called with kretprobe_lock held */ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head) { + struct kretprobe *rp = ri->rp; + /* remove rp inst off the rprobe_inst_table */ hlist_del(&ri->hlist); - if (ri->rp) { - /* remove rp inst off the used list */ - hlist_del(&ri->uflist); - /* put rp inst back onto the free list */ - INIT_HLIST_NODE(&ri->uflist); - hlist_add_head(&ri->uflist, &ri->rp->free_instances); + INIT_HLIST_NODE(&ri->hlist); + if (likely(rp)) { + spin_lock(&rp->lock); + hlist_add_head(&ri->hlist, &rp->free_instances); + spin_unlock(&rp->lock); } else /* Unregistering */ hlist_add_head(&ri->hlist, head); } -struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) +void kretprobe_hash_lock(struct task_struct *tsk, + struct hlist_head **head, unsigned long *flags) +{ + unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS); + spinlock_t *hlist_lock; + + *head = &kretprobe_inst_table[hash]; + hlist_lock = kretprobe_table_lock_ptr(hash); + spin_lock_irqsave(hlist_lock, *flags); +} + +void kretprobe_table_lock(unsigned long hash, unsigned long *flags) { - return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]; + spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash); + spin_lock_irqsave(hlist_lock, *flags); +} + +void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags) +{ + unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS); + spinlock_t *hlist_lock; + + hlist_lock = kretprobe_table_lock_ptr(hash); + spin_unlock_irqrestore(hlist_lock, *flags); +} + +void kretprobe_table_unlock(unsigned long hash, unsigned long *flags) +{ + spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash); + spin_unlock_irqrestore(hlist_lock, *flags); } /* @@ -401,17 +436,21 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) struct kretprobe_instance *ri; struct hlist_head *head, empty_rp; struct hlist_node *node, *tmp; - unsigned long flags = 0; + unsigned long hash, flags = 0; - INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(tk); + if (unlikely(!kprobes_initialized)) + /* Early boot. kretprobe_table_locks not yet initialized. */ + return; + + hash = hash_ptr(tk, KPROBE_HASH_BITS); + head = &kretprobe_inst_table[hash]; + kretprobe_table_lock(hash, &flags); hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { if (ri->task == tk) recycle_rp_inst(ri, &empty_rp); } - spin_unlock_irqrestore(&kretprobe_lock, flags); - + kretprobe_table_unlock(hash, &flags); + INIT_HLIST_HEAD(&empty_rp); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); @@ -423,24 +462,29 @@ static inline void free_rp_inst(struct kretprobe *rp) struct kretprobe_instance *ri; struct hlist_node *pos, *next; - hlist_for_each_entry_safe(ri, pos, next, &rp->free_instances, uflist) { - hlist_del(&ri->uflist); + hlist_for_each_entry_safe(ri, pos, next, &rp->free_instances, hlist) { + hlist_del(&ri->hlist); kfree(ri); } } static void __kprobes cleanup_rp_inst(struct kretprobe *rp) { - unsigned long flags; + unsigned long flags, hash; struct kretprobe_instance *ri; struct hlist_node *pos, *next; + struct hlist_head *head; + /* No race here */ - spin_lock_irqsave(&kretprobe_lock, flags); - hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) { - ri->rp = NULL; - hlist_del(&ri->uflist); + for (hash = 0; hash < KPROBE_TABLE_SIZE; hash++) { + kretprobe_table_lock(hash, &flags); + head = &kretprobe_inst_table[hash]; + hlist_for_each_entry_safe(ri, pos, next, head, hlist) { + if (ri->rp == rp) + ri->rp = NULL; + } + kretprobe_table_unlock(hash, &flags); } - spin_unlock_irqrestore(&kretprobe_lock, flags); free_rp_inst(rp); } @@ -831,32 +875,37 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs) { struct kretprobe *rp = container_of(p, struct kretprobe, kp); - unsigned long flags = 0; + unsigned long hash, flags = 0; + struct kretprobe_instance *ri; /*TODO: consider to only swap the RA after the last pre_handler fired */ - spin_lock_irqsave(&kretprobe_lock, flags); + hash = hash_ptr(current, KPROBE_HASH_BITS); + spin_lock_irqsave(&rp->lock, flags); if (!hlist_empty(&rp->free_instances)) { - struct kretprobe_instance *ri; - ri = hlist_entry(rp->free_instances.first, - struct kretprobe_instance, uflist); + struct kretprobe_instance, hlist); + hlist_del(&ri->hlist); + spin_unlock_irqrestore(&rp->lock, flags); + ri->rp = rp; ri->task = current; if (rp->entry_handler && rp->entry_handler(ri, regs)) { - spin_unlock_irqrestore(&kretprobe_lock, flags); + spin_unlock_irqrestore(&rp->lock, flags); return 0; } arch_prepare_kretprobe(ri, regs); /* XXX(hch): why is there no hlist_move_head? */ - hlist_del(&ri->uflist); - hlist_add_head(&ri->uflist, &ri->rp->used_instances); - hlist_add_head(&ri->hlist, kretprobe_inst_table_head(ri->task)); - } else + INIT_HLIST_NODE(&ri->hlist); + kretprobe_table_lock(hash, &flags); + hlist_add_head(&ri->hlist, &kretprobe_inst_table[hash]); + kretprobe_table_unlock(hash, &flags); + } else { rp->nmissed++; - spin_unlock_irqrestore(&kretprobe_lock, flags); + spin_unlock_irqrestore(&rp->lock, flags); + } return 0; } @@ -892,7 +941,7 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp, rp->maxactive = NR_CPUS; #endif } - INIT_HLIST_HEAD(&rp->used_instances); + spin_lock_init(&rp->lock); INIT_HLIST_HEAD(&rp->free_instances); for (i = 0; i < rp->maxactive; i++) { inst = kmalloc(sizeof(struct kretprobe_instance) + @@ -901,8 +950,8 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp, free_rp_inst(rp); return -ENOMEM; } - INIT_HLIST_NODE(&inst->uflist); - hlist_add_head(&inst->uflist, &rp->free_instances); + INIT_HLIST_NODE(&inst->hlist); + hlist_add_head(&inst->hlist, &rp->free_instances); } rp->nmissed = 0; @@ -1009,6 +1058,7 @@ static int __init init_kprobes(void) for (i = 0; i < KPROBE_TABLE_SIZE; i++) { INIT_HLIST_HEAD(&kprobe_table[i]); INIT_HLIST_HEAD(&kretprobe_inst_table[i]); + spin_lock_init(&(kretprobe_table_locks[i].lock)); } /* @@ -1050,6 +1100,7 @@ static int __init init_kprobes(void) err = arch_init_kprobes(); if (!err) err = register_die_notifier(&kprobe_exceptions_nb); + kprobes_initialized = (err == 0); if (!err) init_test_probes(); -- cgit v1.2.3 From d8f388d8dc8d4f36539dd37c1fff62cc404ea0fc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 25 Jul 2008 01:46:07 -0700 Subject: gpio: sysfs interface This adds a simple sysfs interface for GPIOs. /sys/class/gpio /export ... asks the kernel to export a GPIO to userspace /unexport ... to return a GPIO to the kernel /gpioN ... for each exported GPIO #N /value ... always readable, writes fail for input GPIOs /direction ... r/w as: in, out (default low); write high, low /gpiochipN ... for each gpiochip; #N is its first GPIO /base ... (r/o) same as N /label ... (r/o) descriptive, not necessarily unique /ngpio ... (r/o) number of GPIOs; numbered N .. N+(ngpio - 1) GPIOs claimed by kernel code may be exported by its owner using a new gpio_export() call, which should be most useful for driver debugging. Such exports may optionally be done without a "direction" attribute. Userspace may ask to take over a GPIO by writing to a sysfs control file, helping to cope with incomplete board support or other "one-off" requirements that don't merit full kernel support: echo 23 > /sys/class/gpio/export ... will gpio_request(23, "sysfs") and gpio_export(23); use /sys/class/gpio/gpio-23/direction to (re)configure it, when that GPIO can be used as both input and output. echo 23 > /sys/class/gpio/unexport ... will gpio_free(23), when it was exported as above The extra D-space footprint is a few hundred bytes, except for the sysfs resources associated with each exported GPIO. The additional I-space footprint is about two thirds of the current size of gpiolib (!). Since no /dev node creation is involved, no "udev" support is needed. Related changes: * This adds a device pointer to "struct gpio_chip". When GPIO providers initialize that, sysfs gpio class devices become children of that device instead of being "virtual" devices. * The (few) gpio_chip providers which have such a device node have been updated. * Some gpio_chip drivers also needed to update their module "owner" field ... for which missing kerneldoc was added. * Some gpio_chips don't support input GPIOs. Those GPIOs are now flagged appropriately when the chip is registered. Based on previous patches, and discussion both on and off LKML. A Documentation/ABI/testing/sysfs-gpio update is ready to submit once this merges to mainline. [akpm@linux-foundation.org: a few maintenance build fixes] Signed-off-by: David Brownell Cc: Guennadi Liakhovetski Cc: Greg KH Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gpio.txt | 123 +++++++++- arch/arm/plat-omap/gpio.c | 3 + arch/avr32/mach-at32ap/pio.c | 2 + drivers/gpio/Kconfig | 15 ++ drivers/gpio/gpiolib.c | 536 +++++++++++++++++++++++++++++++++++++++++-- drivers/gpio/mcp23s08.c | 1 + drivers/gpio/pca953x.c | 1 + drivers/gpio/pcf857x.c | 1 + drivers/i2c/chips/tps65010.c | 2 + drivers/mfd/htc-egpio.c | 2 + include/asm-generic/gpio.h | 33 ++- include/linux/gpio.h | 13 ++ 12 files changed, 712 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index c35ca9e40d4c..8b69811a9642 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -347,15 +347,12 @@ necessarily be nonportable. Dynamic definition of GPIOs is not currently standard; for example, as a side effect of configuring an add-on board with some GPIO expanders. -These calls are purely for kernel space, but a userspace API could be built -on top of them. - GPIO implementor's framework (OPTIONAL) ======================================= As noted earlier, there is an optional implementation framework making it easier for platforms to support different kinds of GPIO controller using -the same programming interface. +the same programming interface. This framework is called "gpiolib". As a debugging aid, if debugfs is available a /sys/kernel/debug/gpio file will be found there. That will list all the controllers registered through @@ -439,4 +436,120 @@ becomes available. That may mean the device should not be registered until calls for that GPIO can work. One way to address such dependencies is for such gpio_chip controllers to provide setup() and teardown() callbacks to board specific code; those board specific callbacks would register devices -once all the necessary resources are available. +once all the necessary resources are available, and remove them later when +the GPIO controller device becomes unavailable. + + +Sysfs Interface for Userspace (OPTIONAL) +======================================== +Platforms which use the "gpiolib" implementors framework may choose to +configure a sysfs user interface to GPIOs. This is different from the +debugfs interface, since it provides control over GPIO direction and +value instead of just showing a gpio state summary. Plus, it could be +present on production systems without debugging support. + +Given approprate hardware documentation for the system, userspace could +know for example that GPIO #23 controls the write protect line used to +protect boot loader segments in flash memory. System upgrade procedures +may need to temporarily remove that protection, first importing a GPIO, +then changing its output state, then updating the code before re-enabling +the write protection. In normal use, GPIO #23 would never be touched, +and the kernel would have no need to know about it. + +Again depending on appropriate hardware documentation, on some systems +userspace GPIO can be used to determine system configuration data that +standard kernels won't know about. And for some tasks, simple userspace +GPIO drivers could be all that the system really needs. + +Note that standard kernel drivers exist for common "LEDs and Buttons" +GPIO tasks: "leds-gpio" and "gpio_keys", respectively. Use those +instead of talking directly to the GPIOs; they integrate with kernel +frameworks better than your userspace code could. + + +Paths in Sysfs +-------------- +There are three kinds of entry in /sys/class/gpio: + + - Control interfaces used to get userspace control over GPIOs; + + - GPIOs themselves; and + + - GPIO controllers ("gpio_chip" instances). + +That's in addition to standard files including the "device" symlink. + +The control interfaces are write-only: + + /sys/class/gpio/ + + "export" ... Userspace may ask the kernel to export control of + a GPIO to userspace by writing its number to this file. + + Example: "echo 19 > export" will create a "gpio19" node + for GPIO #19, if that's not requested by kernel code. + + "unexport" ... Reverses the effect of exporting to userspace. + + Example: "echo 19 > unexport" will remove a "gpio19" + node exported using the "export" file. + +GPIO signals have paths like /sys/class/gpio/gpio42/ (for GPIO #42) +and have the following read/write attributes: + + /sys/class/gpio/gpioN/ + + "direction" ... reads as either "in" or "out". This value may + normally be written. Writing as "out" defaults to + initializing the value as low. To ensure glitch free + operation, values "low" and "high" may be written to + configure the GPIO as an output with that initial value. + + Note that this attribute *will not exist* if the kernel + doesn't support changing the direction of a GPIO, or + it was exported by kernel code that didn't explicitly + allow userspace to reconfigure this GPIO's direction. + + "value" ... reads as either 0 (low) or 1 (high). If the GPIO + is configured as an output, this value may be written; + any nonzero value is treated as high. + +GPIO controllers have paths like /sys/class/gpio/chipchip42/ (for the +controller implementing GPIOs starting at #42) and have the following +read-only attributes: + + /sys/class/gpio/gpiochipN/ + + "base" ... same as N, the first GPIO managed by this chip + + "label" ... provided for diagnostics (not always unique) + + "ngpio" ... how many GPIOs this manges (N to N + ngpio - 1) + +Board documentation should in most cases cover what GPIOs are used for +what purposes. However, those numbers are not always stable; GPIOs on +a daughtercard might be different depending on the base board being used, +or other cards in the stack. In such cases, you may need to use the +gpiochip nodes (possibly in conjunction with schematics) to determine +the correct GPIO number to use for a given signal. + + +Exporting from Kernel code +-------------------------- +Kernel code can explicitly manage exports of GPIOs which have already been +requested using gpio_request(): + + /* export the GPIO to userspace */ + int gpio_export(unsigned gpio, bool direction_may_change); + + /* reverse gpio_export() */ + void gpio_unexport(); + +After a kernel driver requests a GPIO, it may only be made available in +the sysfs interface by gpio_export(). The driver can control whether the +signal direction may change. This helps drivers prevent userspace code +from accidentally clobbering important system state. + +This explicit exporting can help with debugging (by making some kinds +of experiments easier), or can provide an always-there interface that's +suitable for documenting as part of a board support package. diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index 1903a3491ee9..d8e9c2c3f0f6 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -1488,6 +1488,9 @@ static int __init _omap_gpio_init(void) bank->chip.set = gpio_set; if (bank_is_mpuio(bank)) { bank->chip.label = "mpuio"; +#ifdef CONFIG_ARCH_OMAP1 + bank->chip.dev = &omap_mpuio_device.dev; +#endif bank->chip.base = OMAP_MPUIO(0); } else { bank->chip.label = "gpio"; diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 60da03ba7117..296294f8ed81 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -360,6 +360,8 @@ static int __init pio_probe(struct platform_device *pdev) pio->chip.label = pio->name; pio->chip.base = pdev->id * 32; pio->chip.ngpio = 32; + pio->chip.dev = &pdev->dev; + pio->chip.owner = THIS_MODULE; pio->chip.direction_input = direction_input; pio->chip.get = gpio_get; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index fced1909cbba..6ec0e35b98e3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -23,6 +23,21 @@ config DEBUG_GPIO slower. The diagnostics help catch the type of setup errors that are most common when setting up new platforms or boards. +config GPIO_SYSFS + bool "/sys/class/gpio/... (sysfs interface)" + depends on SYSFS && EXPERIMENTAL + help + Say Y here to add a sysfs interface for GPIOs. + + This is mostly useful to work around omissions in a system's + kernel support. Those are common in custom and semicustom + hardware assembled using standard kernels with a minimum of + custom patches. In those cases, userspace code may import + a given GPIO from the kernel, if no kernel driver requested it. + + Kernel drivers may also request that a particular GPIO be + exported to userspace; this can be useful when debugging. + # put expanders in the right section, in alphabetical order comment "I2C GPIO expanders:" diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index beaf6b3a37dc..8d2940517c99 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,8 +2,11 @@ #include #include #include - -#include +#include +#include +#include +#include +#include /* Optional implementation infrastructure for GPIO interfaces. @@ -44,6 +47,8 @@ struct gpio_desc { #define FLAG_REQUESTED 0 #define FLAG_IS_OUT 1 #define FLAG_RESERVED 2 +#define FLAG_EXPORT 3 /* protected by sysfs_lock */ +#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ #ifdef CONFIG_DEBUG_FS const char *label; @@ -151,6 +156,482 @@ err: return ret; } +#ifdef CONFIG_GPIO_SYSFS + +/* lock protects against unexport_gpio() being called while + * sysfs files are active. + */ +static DEFINE_MUTEX(sysfs_lock); + +/* + * /sys/class/gpio/gpioN... only for GPIOs that are exported + * /direction + * * MAY BE OMITTED if kernel won't allow direction changes + * * is read/write as "in" or "out" + * * may also be written as "high" or "low", initializing + * output value as specified ("out" implies "low") + * /value + * * always readable, subject to hardware behavior + * * may be writable, as zero/nonzero + * + * REVISIT there will likely be an attribute for configuring async + * notifications, e.g. to specify polling interval or IRQ trigger type + * that would for example trigger a poll() on the "value". + */ + +static ssize_t gpio_direction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%s\n", + test_bit(FLAG_IS_OUT, &desc->flags) + ? "out" : "in"); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_direction_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (sysfs_streq(buf, "high")) + status = gpio_direction_output(gpio, 1); + else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) + status = gpio_direction_output(gpio, 0); + else if (sysfs_streq(buf, "in")) + status = gpio_direction_input(gpio); + else + status = -EINVAL; + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static const DEVICE_ATTR(direction, 0644, + gpio_direction_show, gpio_direction_store); + +static ssize_t gpio_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%d\n", gpio_get_value_cansleep(gpio)); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_value_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (!test_bit(FLAG_IS_OUT, &desc->flags)) + status = -EPERM; + else { + long value; + + status = strict_strtol(buf, 0, &value); + if (status == 0) { + gpio_set_value_cansleep(gpio, value != 0); + status = size; + } + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static /*const*/ DEVICE_ATTR(value, 0644, + gpio_value_show, gpio_value_store); + +static const struct attribute *gpio_attrs[] = { + &dev_attr_direction.attr, + &dev_attr_value.attr, + NULL, +}; + +static const struct attribute_group gpio_attr_group = { + .attrs = (struct attribute **) gpio_attrs, +}; + +/* + * /sys/class/gpio/gpiochipN/ + * /base ... matching gpio_chip.base (N) + * /label ... matching gpio_chip.label + * /ngpio ... matching gpio_chip.ngpio + */ + +static ssize_t chip_base_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", chip->base); +} +static DEVICE_ATTR(base, 0444, chip_base_show, NULL); + +static ssize_t chip_label_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", chip->label ? : ""); +} +static DEVICE_ATTR(label, 0444, chip_label_show, NULL); + +static ssize_t chip_ngpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", chip->ngpio); +} +static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); + +static const struct attribute *gpiochip_attrs[] = { + &dev_attr_base.attr, + &dev_attr_label.attr, + &dev_attr_ngpio.attr, + NULL, +}; + +static const struct attribute_group gpiochip_attr_group = { + .attrs = (struct attribute **) gpiochip_attrs, +}; + +/* + * /sys/class/gpio/export ... write-only + * integer N ... number of GPIO to export (full access) + * /sys/class/gpio/unexport ... write-only + * integer N ... number of GPIO to unexport + */ +static ssize_t export_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + + status = gpio_request(gpio, "sysfs"); + if (status < 0) + goto done; + + status = gpio_export(gpio, true); + if (status < 0) + gpio_free(gpio); + else + set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags); + +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static ssize_t unexport_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + status = -EINVAL; + + /* reject bogus commands (gpio_unexport ignores them) */ + if (!gpio_is_valid(gpio)) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) { + status = 0; + gpio_free(gpio); + } +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static struct class_attribute gpio_class_attrs[] = { + __ATTR(export, 0200, NULL, export_store), + __ATTR(unexport, 0200, NULL, unexport_store), + __ATTR_NULL, +}; + +static struct class gpio_class = { + .name = "gpio", + .owner = THIS_MODULE, + + .class_attrs = gpio_class_attrs, +}; + + +/** + * gpio_export - export a GPIO through sysfs + * @gpio: gpio to make available, already requested + * @direction_may_change: true if userspace may change gpio direction + * Context: arch_initcall or later + * + * When drivers want to make a GPIO accessible to userspace after they + * have requested it -- perhaps while debugging, or as part of their + * public interface -- they may use this routine. If the GPIO can + * change direction (some can't) and the caller allows it, userspace + * will see "direction" sysfs attribute which may be used to change + * the gpio's direction. A "value" attribute will always be provided. + * + * Returns zero on success, else an error. + */ +int gpio_export(unsigned gpio, bool direction_may_change) +{ + unsigned long flags; + struct gpio_desc *desc; + int status = -EINVAL; + + /* can't export until sysfs is available ... */ + if (!gpio_class.p) { + pr_debug("%s: called too early!\n", __func__); + return -ENOENT; + } + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + spin_lock_irqsave(&gpio_lock, flags); + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_REQUESTED, &desc->flags) + && !test_bit(FLAG_EXPORT, &desc->flags)) { + status = 0; + if (!desc->chip->direction_input + || !desc->chip->direction_output) + direction_may_change = false; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) { + struct device *dev; + + dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), + desc, "gpio%d", gpio); + if (dev) { + if (direction_may_change) + status = sysfs_create_group(&dev->kobj, + &gpio_attr_group); + else + status = device_create_file(dev, + &dev_attr_value); + if (status != 0) + device_unregister(dev); + } else + status = -ENODEV; + if (status == 0) + set_bit(FLAG_EXPORT, &desc->flags); + } + + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_export); + +static int match_export(struct device *dev, void *data) +{ + return dev_get_drvdata(dev) == data; +} + +/** + * gpio_unexport - reverse effect of gpio_export() + * @gpio: gpio to make unavailable + * + * This is implicit on gpio_free(). + */ +void gpio_unexport(unsigned gpio) +{ + struct gpio_desc *desc; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_EXPORT, &desc->flags)) { + struct device *dev = NULL; + + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (dev) { + clear_bit(FLAG_EXPORT, &desc->flags); + put_device(dev); + device_unregister(dev); + status = 0; + } else + status = -ENODEV; + } + + mutex_unlock(&sysfs_lock); +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); +} +EXPORT_SYMBOL_GPL(gpio_unexport); + +static int gpiochip_export(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + /* Many systems register gpio chips for SOC support very early, + * before driver model support is available. In those cases we + * export this later, in gpiolib_sysfs_init() ... here we just + * verify that _some_ field of gpio_class got initialized. + */ + if (!gpio_class.p) + return 0; + + /* use chip->base for the ID; it's already known to be unique */ + mutex_lock(&sysfs_lock); + dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, + "gpiochip%d", chip->base); + if (dev) { + status = sysfs_create_group(&dev->kobj, + &gpiochip_attr_group); + } else + status = -ENODEV; + chip->exported = (status == 0); + mutex_unlock(&sysfs_lock); + + if (status) { + unsigned long flags; + unsigned gpio; + + spin_lock_irqsave(&gpio_lock, flags); + gpio = chip->base; + while (gpio_desc[gpio].chip == chip) + gpio_desc[gpio++].chip = NULL; + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); + } + + return status; +} + +static void gpiochip_unexport(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + mutex_lock(&sysfs_lock); + dev = class_find_device(&gpio_class, NULL, chip, match_export); + if (dev) { + put_device(dev); + device_unregister(dev); + chip->exported = 0; + status = 0; + } else + status = -ENODEV; + mutex_unlock(&sysfs_lock); + + if (status) + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); +} + +static int __init gpiolib_sysfs_init(void) +{ + int status; + unsigned long flags; + unsigned gpio; + + status = class_register(&gpio_class); + if (status < 0) + return status; + + /* Scan and register the gpio_chips which registered very + * early (e.g. before the class_register above was called). + * + * We run before arch_initcall() so chip->dev nodes can have + * registered, and so arch_initcall() can always gpio_export(). + */ + spin_lock_irqsave(&gpio_lock, flags); + for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { + struct gpio_chip *chip; + + chip = gpio_desc[gpio].chip; + if (!chip || chip->exported) + continue; + + spin_unlock_irqrestore(&gpio_lock, flags); + status = gpiochip_export(chip); + spin_lock_irqsave(&gpio_lock, flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); + + + return status; +} +postcore_initcall(gpiolib_sysfs_init); + +#else +static inline int gpiochip_export(struct gpio_chip *chip) +{ + return 0; +} + +static inline void gpiochip_unexport(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_GPIO_SYSFS */ + /** * gpiochip_add() - register a gpio_chip * @chip: the chip to register, with chip->base initialized @@ -160,6 +641,11 @@ err: * because the chip->base is invalid or already associated with a * different chip. Otherwise it returns zero as a success code. * + * When gpiochip_add() is called very early during boot, so that GPIOs + * can be freely used, the chip->dev device must be registered before + * the gpio framework's arch_initcall(). Otherwise sysfs initialization + * for GPIOs will fail rudely. + * * If chip->base is negative, this requests dynamic assignment of * a range of valid GPIOs. */ @@ -182,7 +668,7 @@ int gpiochip_add(struct gpio_chip *chip) base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; - goto fail_unlock; + goto unlock; } chip->base = base; } @@ -197,12 +683,23 @@ int gpiochip_add(struct gpio_chip *chip) if (status == 0) { for (id = base; id < base + chip->ngpio; id++) { gpio_desc[id].chip = chip; - gpio_desc[id].flags = 0; + + /* REVISIT: most hardware initializes GPIOs as + * inputs (often with pullups enabled) so power + * usage is minimized. Linux code should set the + * gpio direction first thing; but until it does, + * we may expose the wrong direction in sysfs. + */ + gpio_desc[id].flags = !chip->direction_input + ? (1 << FLAG_IS_OUT) + : 0; } } -fail_unlock: +unlock: spin_unlock_irqrestore(&gpio_lock, flags); + if (status == 0) + status = gpiochip_export(chip); fail: /* failures here can mean systems won't boot... */ if (status) @@ -239,6 +736,10 @@ int gpiochip_remove(struct gpio_chip *chip) } spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) + gpiochip_unexport(chip); + return status; } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -296,6 +797,8 @@ void gpio_free(unsigned gpio) return; } + gpio_unexport(gpio); + spin_lock_irqsave(&gpio_lock, flags); desc = &gpio_desc[gpio]; @@ -534,10 +1037,6 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); #ifdef CONFIG_DEBUG_FS -#include -#include - - static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; @@ -614,17 +1113,28 @@ static int gpiolib_show(struct seq_file *s, void *unused) /* REVISIT this isn't locked against gpio_chip removal ... */ for (gpio = 0; gpio_is_valid(gpio); gpio++) { + struct device *dev; + if (chip == gpio_desc[gpio].chip) continue; chip = gpio_desc[gpio].chip; if (!chip) continue; - seq_printf(s, "%sGPIOs %d-%d, %s%s:\n", + seq_printf(s, "%sGPIOs %d-%d", started ? "\n" : "", - chip->base, chip->base + chip->ngpio - 1, - chip->label ? : "generic", - chip->can_sleep ? ", can sleep" : ""); + chip->base, chip->base + chip->ngpio - 1); + dev = chip->dev; + if (dev) + seq_printf(s, ", %s/%s", + dev->bus ? dev->bus->name : "no-bus", + dev->bus_id); + if (chip->label) + seq_printf(s, ", %s", chip->label); + if (chip->can_sleep) + seq_printf(s, ", can sleep"); + seq_printf(s, ":\n"); + started = 1; if (chip->dbg_show) chip->dbg_show(s, chip); diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index 7f92fdd5f0e2..7efd7d3a81f9 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c @@ -239,6 +239,7 @@ static int mcp23s08_probe(struct spi_device *spi) mcp->chip.base = pdata->base; mcp->chip.ngpio = 8; mcp->chip.can_sleep = 1; + mcp->chip.dev = &spi->dev; mcp->chip.owner = THIS_MODULE; spi_set_drvdata(spi, mcp); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index a380730b61ab..cc8468692ae0 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -188,6 +188,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->base = chip->gpio_start; gc->ngpio = gpios; gc->label = chip->client->name; + gc->dev = &chip->client->dev; gc->owner = THIS_MODULE; } diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c index d25d356c4f20..fc9c6ae739ee 100644 --- a/drivers/gpio/pcf857x.c +++ b/drivers/gpio/pcf857x.c @@ -200,6 +200,7 @@ static int pcf857x_probe(struct i2c_client *client, gpio->chip.base = pdata->gpio_base; gpio->chip.can_sleep = 1; + gpio->chip.dev = &client->dev; gpio->chip.owner = THIS_MODULE; /* NOTE: the OnSemi jlc1562b is also largely compatible with diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 85949685191b..cf02e8fceb42 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -636,6 +636,8 @@ static int tps65010_probe(struct i2c_client *client, tps->outmask = board->outmask; tps->chip.label = client->name; + tps->chip.dev = &client->dev; + tps->chip.owner = THIS_MODULE; tps->chip.set = tps65010_gpio_set; tps->chip.direction_output = tps65010_output; diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 8872cc077519..6be43172dc65 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -318,6 +318,8 @@ static int __init egpio_probe(struct platform_device *pdev) ei->chip[i].dev = &(pdev->dev); chip = &(ei->chip[i].chip); chip->label = "htc-egpio"; + chip->dev = &pdev->dev; + chip->owner = THIS_MODULE; chip->get = egpio_get; chip->set = egpio_set; chip->direction_input = egpio_direction_input; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 6be061d09da9..1beff5166e53 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -32,6 +32,8 @@ struct module; /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics + * @dev: optional device providing the GPIOs + * @owner: helps prevent removal of modules exporting active GPIOs * @direction_input: configures signal "offset" as input, or returns error * @get: returns value for signal "offset"; for output signals this * returns either the value actually sensed, or zero @@ -59,6 +61,7 @@ struct module; */ struct gpio_chip { char *label; + struct device *dev; struct module *owner; int (*direction_input)(struct gpio_chip *chip, @@ -74,6 +77,7 @@ struct gpio_chip { int base; u16 ngpio; unsigned can_sleep:1; + unsigned exported:1; }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, @@ -108,7 +112,18 @@ extern void __gpio_set_value(unsigned gpio, int value); extern int __gpio_cansleep(unsigned gpio); -#else +#ifdef CONFIG_GPIO_SYSFS + +/* + * A sysfs interface can be exported by individual drivers if they want, + * but more typically is configured entirely from userspace. + */ +extern int gpio_export(unsigned gpio, bool direction_may_change); +extern void gpio_unexport(unsigned gpio); + +#endif /* CONFIG_GPIO_SYSFS */ + +#else /* !CONFIG_HAVE_GPIO_LIB */ static inline int gpio_is_valid(int number) { @@ -137,6 +152,20 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) gpio_set_value(gpio, value); } -#endif +#endif /* !CONFIG_HAVE_GPIO_LIB */ + +#ifndef CONFIG_GPIO_SYSFS + +/* sysfs support is only available with gpiolib, where it's optional */ + +static inline int gpio_export(unsigned gpio, bool direction_may_change) +{ + return -ENOSYS; +} + +static inline void gpio_unexport(unsigned gpio) +{ +} +#endif /* CONFIG_GPIO_SYSFS */ #endif /* _ASM_GENERIC_GPIO_H */ diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 98be6c5762b9..730a20b83576 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -79,6 +79,19 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) WARN_ON(1); } +static inline int gpio_export(unsigned gpio, bool direction_may_change) +{ + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); + return -EINVAL; +} + +static inline void gpio_unexport(unsigned gpio) +{ + /* GPIO can never have been exported */ + WARN_ON(1); +} + static inline int gpio_to_irq(unsigned gpio) { /* GPIO can never have been requested or set as input */ -- cgit v1.2.3 From 8f1cc3b10e6ee0c5c7c8ed27f8771c4f252b4862 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 25 Jul 2008 01:46:09 -0700 Subject: gpio: mcp23s08 handles multiple chips per chipselect Teach the mcp23s08 driver about a curious feature of these chips: up to four of them can share the same chipselect, with the SPI signals wired in parallel, by matching two bits in the first protocol byte against two address lines on the chip. This is handled by three software changes: * Platform data now holds an array of per-chip structs, not just one chip's address and pullup configuration. * Probe() and remove() now use another level of structure, wrapping an instance of the original structure for each mcp23s08 chip sharing that chipselect. * The HAEN bit is set, so that the hardware address bits can no longer be ignored (boot firmware may not have enabled them). The "one struct per chip" preserves the guts of the current code, but platform_data will need minor changes. OLD: /* incorrect "slave" ID may not have mattered */ .slave = 3, .pullups = BIT(3) | BIT(1) | BIT(0), NEW: /* slave address _must_ match chip's wiring */ .chip[3] = { .is_present = true, .pullups = BIT(3) | BIT(1) | BIT(0), }, There's no change in how things _behave_ for spi_device nodes with a single mcp23s08 chip. New multi-chip configurations assign GPIOs in sequence, without holes. The spi_device just resembles a bigger controller, but internally it has multiple gpio_chip instances. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/mcp23s08.c | 133 +++++++++++++++++++++++++++++++++---------- include/linux/spi/mcp23s08.h | 25 +++++--- 2 files changed, 118 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index 7efd7d3a81f9..8a1b405fefda 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c @@ -40,15 +40,26 @@ struct mcp23s08 { struct spi_device *spi; u8 addr; + u8 cache[11]; /* lock protects the cached values */ struct mutex lock; - u8 cache[11]; struct gpio_chip chip; struct work_struct work; }; +/* A given spi_device can represent up to four mcp23s08 chips + * sharing the same chipselect but using different addresses + * (e.g. chips #0 and #3 might be populated, but not #1 or $2). + * Driver data holds all the per-chip data. + */ +struct mcp23s08_driver_data { + unsigned ngpio; + struct mcp23s08 *mcp[4]; + struct mcp23s08 chip[]; +}; + static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) { u8 tx[2], rx[1]; @@ -208,25 +219,18 @@ done: /*----------------------------------------------------------------------*/ -static int mcp23s08_probe(struct spi_device *spi) +static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, + unsigned base, unsigned pullups) { - struct mcp23s08 *mcp; - struct mcp23s08_platform_data *pdata; + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08 *mcp = data->mcp[addr]; int status; int do_update = 0; - pdata = spi->dev.platform_data; - if (!pdata || pdata->slave > 3 || !pdata->base) - return -ENODEV; - - mcp = kzalloc(sizeof *mcp, GFP_KERNEL); - if (!mcp) - return -ENOMEM; - mutex_init(&mcp->lock); mcp->spi = spi; - mcp->addr = 0x40 | (pdata->slave << 1); + mcp->addr = 0x40 | (addr << 1); mcp->chip.label = "mcp23s08", @@ -236,27 +240,28 @@ static int mcp23s08_probe(struct spi_device *spi) mcp->chip.set = mcp23s08_set; mcp->chip.dbg_show = mcp23s08_dbg_show; - mcp->chip.base = pdata->base; + mcp->chip.base = base; mcp->chip.ngpio = 8; mcp->chip.can_sleep = 1; mcp->chip.dev = &spi->dev; mcp->chip.owner = THIS_MODULE; - spi_set_drvdata(spi, mcp); - - /* verify MCP_IOCON.SEQOP = 0, so sequential reads work */ + /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, + * and MCP_IOCON.HAEN = 1, so we work with all chips. + */ status = mcp23s08_read(mcp, MCP_IOCON); if (status < 0) goto fail; - if (status & IOCON_SEQOP) { + if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { status &= ~IOCON_SEQOP; + status |= IOCON_HAEN; status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); if (status < 0) goto fail; } /* configure ~100K pullups */ - status = mcp23s08_write(mcp, MCP_GPPU, pdata->pullups); + status = mcp23s08_write(mcp, MCP_GPPU, pullups); if (status < 0) goto fail; @@ -283,11 +288,58 @@ static int mcp23s08_probe(struct spi_device *spi) tx[1] = MCP_IPOL; memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); - - /* FIXME check status... */ + if (status < 0) + goto fail; } status = gpiochip_add(&mcp->chip); +fail: + if (status < 0) + dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", + addr, status); + return status; +} + +static int mcp23s08_probe(struct spi_device *spi) +{ + struct mcp23s08_platform_data *pdata; + unsigned addr; + unsigned chips = 0; + struct mcp23s08_driver_data *data; + int status; + unsigned base; + + pdata = spi->dev.platform_data; + if (!pdata || !gpio_is_valid(pdata->base)) + return -ENODEV; + + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips++; + } + if (!chips) + return -ENODEV; + + data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), + GFP_KERNEL); + if (!data) + return -ENOMEM; + spi_set_drvdata(spi, data); + + base = pdata->base; + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips--; + data->mcp[addr] = &data->chip[chips]; + status = mcp23s08_probe_one(spi, addr, base, + pdata->chip[addr].pullups); + if (status < 0) + goto fail; + base += 8; + } + data->ngpio = base - pdata->base; /* NOTE: these chips have a relatively sane IRQ framework, with * per-signal masking and level/edge triggering. It's not yet @@ -295,8 +347,9 @@ static int mcp23s08_probe(struct spi_device *spi) */ if (pdata->setup) { - status = pdata->setup(spi, mcp->chip.base, - mcp->chip.ngpio, pdata->context); + status = pdata->setup(spi, + pdata->base, data->ngpio, + pdata->context); if (status < 0) dev_dbg(&spi->dev, "setup --> %d\n", status); } @@ -304,19 +357,29 @@ static int mcp23s08_probe(struct spi_device *spi) return 0; fail: - kfree(mcp); + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + } + kfree(data); return status; } static int mcp23s08_remove(struct spi_device *spi) { - struct mcp23s08 *mcp = spi_get_drvdata(spi); + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); struct mcp23s08_platform_data *pdata = spi->dev.platform_data; + unsigned addr; int status = 0; if (pdata->teardown) { status = pdata->teardown(spi, - mcp->chip.base, mcp->chip.ngpio, + pdata->base, data->ngpio, pdata->context); if (status < 0) { dev_err(&spi->dev, "%s --> %d\n", "teardown", status); @@ -324,11 +387,20 @@ static int mcp23s08_remove(struct spi_device *spi) } } - status = gpiochip_remove(&mcp->chip); + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) { + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + status = tmp; + } + } if (status == 0) - kfree(mcp); - else - dev_err(&spi->dev, "%s --> %d\n", "remove", status); + kfree(data); return status; } @@ -356,4 +428,3 @@ static void __exit mcp23s08_exit(void) module_exit(mcp23s08_exit); MODULE_LICENSE("GPL"); - diff --git a/include/linux/spi/mcp23s08.h b/include/linux/spi/mcp23s08.h index 835ddf47d45c..22ef107d7704 100644 --- a/include/linux/spi/mcp23s08.h +++ b/include/linux/spi/mcp23s08.h @@ -1,18 +1,25 @@ -/* FIXME driver should be able to handle all four slaves that - * can be hooked up to each chipselect, as well as IRQs... - */ +/* FIXME driver should be able to handle IRQs... */ + +struct mcp23s08_chip_info { + bool is_present; /* true iff populated */ + u8 pullups; /* BIT(x) means enable pullup x */ +}; struct mcp23s08_platform_data { - /* four slaves can share one SPI chipselect */ - u8 slave; + /* Four slaves (numbered 0..3) can share one SPI chipselect, and + * will provide 8..32 GPIOs using 1..4 gpio_chip instances. + */ + struct mcp23s08_chip_info chip[4]; - /* number assigned to the first GPIO */ + /* "base" is the number of the first GPIO. Dynamic assignment is + * not currently supported, and even if there are gaps in chip + * addressing the GPIO numbers are sequential .. so for example + * if only slaves 0 and 3 are present, their GPIOs range from + * base to base+15. + */ unsigned base; - /* pins with pullups */ - u8 pullups; - void *context; /* param to setup/teardown */ int (*setup)(struct spi_device *spi, -- cgit v1.2.3 From bbcd6d543de335bf81e96477f46a60a8bf51039c Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 25 Jul 2008 01:46:14 -0700 Subject: gpio: max732x driver This adds a driver supporting a family of I2C port expanders from Maxim, which includes the MAX7319 and MAX7320-7327 chips. [dbrownell@users.sourceforge.net: minor fixes] Signed-off-by: Jack Ren Signed-off-by: Eric Miao Acked-by: Jean Delvare Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/Kconfig | 19 +++ drivers/gpio/Makefile | 1 + drivers/gpio/max732x.c | 385 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/max732x.h | 19 +++ 4 files changed, 424 insertions(+) create mode 100644 drivers/gpio/max732x.c create mode 100644 include/linux/i2c/max732x.h (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5a355f829167..dbd42d6c93a7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -67,6 +67,25 @@ config GPIO_SYSFS comment "I2C GPIO expanders:" +config GPIO_MAX732X + tristate "MAX7319, MAX7320-7327 I2C Port Expanders" + depends on I2C + help + Say yes here to support the MAX7319, MAX7320-7327 series of I2C + Port Expanders. Each IO port on these chips has a fixed role of + Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain + Input and Output (designed by 'P'). The combinations are listed + below: + + 8 bits: max7319 (8I), max7320 (8O), max7321 (8P), + max7322 (4I4O), max7323 (4P4O) + + 16 bits: max7324 (8I8O), max7325 (8P8O), + max7326 (4I12O), max7327 (4P12O) + + Board setup code must specify the model to use, and the start + number for these GPIOs. + config GPIO_PCA953X tristate "PCA953x, PCA955x, and MAX7310 I/O ports" depends on I2C diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8c45948d1fe7..01b4bbde1956 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIO_MAX7301) += max7301.o +obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c new file mode 100644 index 000000000000..b51c8135ca28 --- /dev/null +++ b/drivers/gpio/max732x.c @@ -0,0 +1,385 @@ +/* + * max732x.c - I2C Port Expander with 8/16 I/O + * + * Copyright (C) 2007 Marvell International Ltd. + * Copyright (C) 2008 Jack Ren + * Copyright (C) 2008 Eric Miao + * + * Derived from drivers/gpio/pca953x.c + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include + +#include +#include + + +/* + * Each port of MAX732x (including MAX7319) falls into one of the + * following three types: + * + * - Push Pull Output + * - Input + * - Open Drain I/O + * + * designated by 'O', 'I' and 'P' individually according to MAXIM's + * datasheets. + * + * There are two groups of I/O ports, each group usually includes + * up to 8 I/O ports, and is accessed by a specific I2C address: + * + * - Group A : by I2C address 0b'110xxxx + * - Group B : by I2C address 0b'101xxxx + * + * where 'xxxx' is decided by the connections of pin AD2/AD0. The + * address used also affects the initial state of output signals. + * + * Within each group of ports, there are five known combinations of + * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for + * the detailed organization of these ports. + * + * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16', + * and GPIOs from GROUP_A are numbered before those from GROUP_B + * (if there are two groups). + * + * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so + * they are not supported by this driver. + */ + +#define PORT_NONE 0x0 /* '/' No Port */ +#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */ +#define PORT_INPUT 0x2 /* 'I' Input Only */ +#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */ + +#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */ +#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */ +#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */ +#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */ +#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */ + +#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */ +#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */ + +static const struct i2c_device_id max732x_id[] = { + { "max7319", GROUP_A(IO_8I) }, + { "max7320", GROUP_B(IO_8O) }, + { "max7321", GROUP_A(IO_8P) }, + { "max7322", GROUP_A(IO_4I4O) }, + { "max7323", GROUP_A(IO_4P4O) }, + { "max7324", GROUP_A(IO_8I) | GROUP_B(IO_8O) }, + { "max7325", GROUP_A(IO_8P) | GROUP_B(IO_8O) }, + { "max7326", GROUP_A(IO_4I4O) | GROUP_B(IO_8O) }, + { "max7327", GROUP_A(IO_4P4O) | GROUP_B(IO_8O) }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max732x_id); + +struct max732x_chip { + struct gpio_chip gpio_chip; + + struct i2c_client *client; /* "main" client */ + struct i2c_client *client_dummy; + struct i2c_client *client_group_a; + struct i2c_client *client_group_b; + + unsigned int mask_group_a; + unsigned int dir_input; + unsigned int dir_output; + + struct mutex lock; + uint8_t reg_out[2]; +}; + +static int max732x_write(struct max732x_chip *chip, int group_a, uint8_t val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_write_byte(client, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing\n"); + return ret; + } + + return 0; +} + +static int max732x_read(struct max732x_chip *chip, int group_a, uint8_t *val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "failed reading\n"); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +static inline int is_group_a(struct max732x_chip *chip, unsigned off) +{ + return (1u << off) & chip->mask_group_a; +} + +static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + uint8_t reg_val; + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + ret = max732x_read(chip, is_group_a(chip, off), ®_val); + if (ret < 0) + return 0; + + return reg_val & (1u << (off & 0x7)); +} + +static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct max732x_chip *chip; + uint8_t reg_out, mask = 1u << (off & 0x7); + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + mutex_lock(&chip->lock); + + reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; + reg_out = (val) ? reg_out | mask : reg_out & ~mask; + + ret = max732x_write(chip, is_group_a(chip, off), reg_out); + if (ret < 0) + goto out; + + /* update the shadow register then */ + if (off > 7) + chip->reg_out[1] = reg_out; + else + chip->reg_out[0] = reg_out; +out: + mutex_unlock(&chip->lock); +} + +static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_input) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is output only\n", + chip->client->name, off); + return -EACCES; + } + + return 0; +} + +static int max732x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_output) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is input only\n", + chip->client->name, off); + return -EACCES; + } + + max732x_gpio_set_value(gc, off, val); + return 0; +} + +static int __devinit max732x_setup_gpio(struct max732x_chip *chip, + const struct i2c_device_id *id, + unsigned gpio_start) +{ + struct gpio_chip *gc = &chip->gpio_chip; + uint32_t id_data = id->driver_data; + int i, port = 0; + + for (i = 0; i < 16; i++, id_data >>= 2) { + unsigned int mask = 1 << port; + + switch (id_data & 0x3) { + case PORT_OUTPUT: + chip->dir_output |= mask; + break; + case PORT_INPUT: + chip->dir_input |= mask; + break; + case PORT_OPENDRAIN: + chip->dir_output |= mask; + chip->dir_input |= mask; + break; + default: + continue; + } + + if (i < 8) + chip->mask_group_a |= mask; + port++; + } + + if (chip->dir_input) + gc->direction_input = max732x_gpio_direction_input; + if (chip->dir_output) { + gc->direction_output = max732x_gpio_direction_output; + gc->set = max732x_gpio_set_value; + } + gc->get = max732x_gpio_get_value; + gc->can_sleep = 1; + + gc->base = gpio_start; + gc->ngpio = port; + gc->label = chip->client->name; + gc->owner = THIS_MODULE; + + return port; +} + +static int __devinit max732x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max732x_platform_data *pdata; + struct max732x_chip *chip; + struct i2c_client *c; + uint16_t addr_a, addr_b; + int ret, nr_port; + + pdata = client->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->client = client; + + nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base); + + addr_a = (client->addr & 0x0f) | 0x60; + addr_b = (client->addr & 0x0f) | 0x50; + + switch (client->addr & 0x70) { + case 0x60: + chip->client_group_a = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_b); + chip->client_group_b = chip->client_dummy = c; + } + break; + case 0x50: + chip->client_group_b = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_a); + chip->client_group_a = chip->client_dummy = c; + } + break; + default: + dev_err(&client->dev, "invalid I2C address specified %02x\n", + client->addr); + ret = -EINVAL; + goto out_failed; + } + + mutex_init(&chip->lock); + + max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]); + if (nr_port > 7) + max732x_read(chip, is_group_a(chip, 8), &chip->reg_out[1]); + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip); + return ret; +} + +static int __devexit max732x_remove(struct i2c_client *client) +{ + struct max732x_platform_data *pdata = client->dev.platform_data; + struct max732x_chip *chip = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + /* unregister any dummy i2c_client */ + if (chip->client_dummy) + i2c_unregister_device(chip->client_dummy); + + kfree(chip); + return 0; +} + +static struct i2c_driver max732x_driver = { + .driver = { + .name = "max732x", + .owner = THIS_MODULE, + }, + .probe = max732x_probe, + .remove = __devexit_p(max732x_remove), + .id_table = max732x_id, +}; + +static int __init max732x_init(void) +{ + return i2c_add_driver(&max732x_driver); +} +module_init(max732x_init); + +static void __exit max732x_exit(void) +{ + i2c_del_driver(&max732x_driver); +} +module_exit(max732x_exit); + +MODULE_AUTHOR("Eric Miao "); +MODULE_DESCRIPTION("GPIO expander driver for MAX732X"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/max732x.h b/include/linux/i2c/max732x.h new file mode 100644 index 000000000000..e10336631c62 --- /dev/null +++ b/include/linux/i2c/max732x.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_I2C_MAX732X_H +#define __LINUX_I2C_MAX732X_H + +/* platform data for the MAX732x 8/16-bit I/O expander driver */ + +struct max732x_platform_data { + /* number of the first GPIO */ + unsigned gpio_base; + + void *context; /* param to setup/teardown */ + + int (*setup)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); + int (*teardown)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); +}; +#endif /* __LINUX_I2C_MAX732X_H */ -- cgit v1.2.3 From 50c33a84db4aa5082e3af8d873b22344ae2ebea8 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 25 Jul 2008 01:46:16 -0700 Subject: ext2: fix typo in Hurd part of include/linux/ext2_fs.h Fix typo in Hurd part of include/linux/ext2_fs.h The ';' here is redundant or can even pose problem. This is actually not used by the Linux kernel, but it is exposed in GNU/Hurd. Signed-off-by: Samuel Thibault Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ext2_fs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 84cec2aa9f1e..2efe7b863cff 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -284,8 +284,8 @@ struct ext2_inode { #ifdef __hurd__ #define i_translator osd1.hurd1.h_i_translator -#define i_frag osd2.hurd2.h_i_frag; -#define i_fsize osd2.hurd2.h_i_fsize; +#define i_frag osd2.hurd2.h_i_frag +#define i_fsize osd2.hurd2.h_i_fsize #define i_uid_high osd2.hurd2.h_i_uid_high #define i_gid_high osd2.hurd2.h_i_gid_high #define i_author osd2.hurd2.h_i_author -- cgit v1.2.3 From ae76dd9a6b5bbe5315fb7028e03f68f75b8538f3 Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Fri, 25 Jul 2008 01:46:23 -0700 Subject: ext3: handle corrupted orphan list at mount If the orphan node list includes valid, untruncatable nodes with nlink > 0 the ext3_orphan_cleanup loop which attempts to delete them will not do so, causing it to loop forever. Fix by checking for such nodes in the ext3_orphan_get function. This patch fixes the second case (image hdb.20000009.softlockup.gz) reported in http://bugzilla.kernel.org/show_bug.cgi?id=10882. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: printk warning fix] Signed-off-by: Duane Griffin Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/ialloc.c | 9 +++++++++ fs/ext3/inode.c | 20 ++++++++++++++------ include/linux/ext3_fs.h | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 77126821b2e9..47b678d73e7a 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -669,6 +669,14 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) if (IS_ERR(inode)) goto iget_failed; + /* + * If the orphans has i_nlinks > 0 then it should be able to be + * truncated, otherwise it won't be removed from the orphan list + * during processing and an infinite loop will result. + */ + if (inode->i_nlink && !ext3_can_truncate(inode)) + goto bad_orphan; + if (NEXT_ORPHAN(inode) > max_ino) goto bad_orphan; brelse(bitmap_bh); @@ -690,6 +698,7 @@ bad_orphan: printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", NEXT_ORPHAN(inode)); printk(KERN_NOTICE "max_ino=%lu\n", max_ino); + printk(KERN_NOTICE "i_nlink=%u\n", inode->i_nlink); /* Avoid freeing blocks if we got a bad deleted inode */ if (inode->i_nlink == 0) inode->i_blocks = 0; diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 6ae4ecf3ce40..74b432fa166b 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2253,6 +2253,19 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode, } } +int ext3_can_truncate(struct inode *inode) +{ + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return 0; + if (S_ISREG(inode->i_mode)) + return 1; + if (S_ISDIR(inode->i_mode)) + return 1; + if (S_ISLNK(inode->i_mode)) + return !ext3_inode_is_fast_symlink(inode); + return 0; +} + /* * ext3_truncate() * @@ -2297,12 +2310,7 @@ void ext3_truncate(struct inode *inode) unsigned blocksize = inode->i_sb->s_blocksize; struct page *page; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - if (ext3_inode_is_fast_symlink(inode)) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (!ext3_can_truncate(inode)) return; /* diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 36c540396377..80171ee89a22 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -832,6 +832,7 @@ extern void ext3_discard_reservation (struct inode *); extern void ext3_dirty_inode(struct inode *); extern int ext3_change_inode_journal_flag(struct inode *, int); extern int ext3_get_inode_loc(struct inode *, struct ext3_iloc *); +extern int ext3_can_truncate(struct inode *inode); extern void ext3_truncate (struct inode *); extern void ext3_set_inode_flags(struct inode *); extern void ext3_get_inode_flags(struct ext3_inode_info *); -- cgit v1.2.3 From de0ca06a99c33df8333955642843331ab6b6e7ff Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:46:34 -0700 Subject: coda: remove CODA_FS_OLD_API While fixing CONFIG_ leakages to the userspace kernel headers I ran into CODA_FS_OLD_API. After five years, are there still people using the old API left? Especially considering that you have to choose at compile time which API to support in the kernel (and distributions tend to offer the new API for some time). Jan: "The old API can definitely go. Around the time the new interface went in there were some non-Coda userspace file system implementations that took a while longer to convert to the new API, but by now they all switched to the new interface or in some cases to a FUSE-based solution." Signed-off-by: Adrian Bunk Acked-by: Jan Harkes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/Kconfig | 14 -------------- fs/coda/coda_linux.c | 6 ++---- fs/coda/psdev.c | 4 ---- fs/coda/upcall.c | 15 +-------------- include/linux/coda.h | 43 ------------------------------------------- 5 files changed, 3 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/fs/Kconfig b/fs/Kconfig index 37db79a2ff95..ed563b9e352a 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -2093,20 +2093,6 @@ config CODA_FS To compile the coda client support as a module, choose M here: the module will be called coda. -config CODA_FS_OLD_API - bool "Use 96-bit Coda file identifiers" - depends on CODA_FS - help - A new kernel-userspace API had to be introduced for Coda v6.0 - to support larger 128-bit file identifiers as needed by the - new realms implementation. - - However this new API is not backward compatible with older - clients. If you really need to run the old Coda userspace - cache manager then say Y. - - For most cases you probably want to say N. - config AFS_FS tristate "Andrew File System support (AFS) (EXPERIMENTAL)" depends on INET && EXPERIMENTAL diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index e1c854890f94..bf4a3fd3c8e3 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -28,11 +28,9 @@ int coda_fake_statfs; char * coda_f2s(struct CodaFid *f) { static char s[60]; -#ifdef CONFIG_CODA_FS_OLD_API - sprintf(s, "(%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2]); -#else + sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]); -#endif + return s; } diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 40c36f7352a6..0d9b80ec689c 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -378,11 +378,7 @@ MODULE_AUTHOR("Jan Harkes, Peter J. Braam"); MODULE_DESCRIPTION("Coda Distributed File System VFS interface"); MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR); MODULE_LICENSE("GPL"); -#ifdef CONFIG_CODA_FS_OLD_API -MODULE_VERSION("5.3.21"); -#else MODULE_VERSION("6.6"); -#endif static int __init init_coda(void) { diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 359e531094dd..ce432bca95d1 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -52,12 +52,8 @@ static void *alloc_upcall(int opcode, int size) inp->ih.opcode = opcode; inp->ih.pid = current->pid; inp->ih.pgid = task_pgrp_nr(current); -#ifdef CONFIG_CODA_FS_OLD_API - memset(&inp->ih.cred, 0, sizeof(struct coda_cred)); - inp->ih.cred.cr_fsuid = current->fsuid; -#else inp->ih.uid = current->fsuid; -#endif + return (void*)inp; } @@ -166,20 +162,11 @@ int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, union inputArgs *inp; union outputArgs *outp; int insize, outsize, error; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred = { 0, }; - cred.cr_fsuid = uid; -#endif insize = SIZE(release); UPARG(CODA_CLOSE); -#ifdef CONFIG_CODA_FS_OLD_API - memcpy(&(inp->ih.cred), &cred, sizeof(cred)); -#else inp->ih.uid = uid; -#endif - inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; diff --git a/include/linux/coda.h b/include/linux/coda.h index b5cf0780c51a..96c87693800b 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -199,28 +199,6 @@ typedef u_int32_t vuid_t; typedef u_int32_t vgid_t; #endif /*_VUID_T_ */ -#ifdef CONFIG_CODA_FS_OLD_API -struct CodaFid { - u_int32_t opaque[3]; -}; - -static __inline__ ino_t coda_f2i(struct CodaFid *fid) -{ - if ( ! fid ) - return 0; - if (fid->opaque[1] == 0xfffffffe || fid->opaque[1] == 0xffffffff) - return ((fid->opaque[0] << 20) | (fid->opaque[2] & 0xfffff)); - else - return (fid->opaque[2] + (fid->opaque[1]<<10) + (fid->opaque[0]<<20)); -} - -struct coda_cred { - vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/ - vgid_t cr_groupid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */ -}; - -#else /* not defined(CONFIG_CODA_FS_OLD_API) */ - struct CodaFid { u_int32_t opaque[4]; }; @@ -228,8 +206,6 @@ struct CodaFid { #define coda_f2i(fid)\ (fid ? (fid->opaque[3] ^ (fid->opaque[2]<<10) ^ (fid->opaque[1]<<20) ^ fid->opaque[0]) : 0) -#endif - #ifndef _VENUS_VATTR_T_ #define _VENUS_VATTR_T_ /* @@ -313,15 +289,7 @@ struct coda_statfs { #define CIOC_KERNEL_VERSION _IOWR('c', 10, size_t) -#if 0 -#define CODA_KERNEL_VERSION 0 /* don't care about kernel version number */ -#define CODA_KERNEL_VERSION 1 /* The old venus 4.6 compatible interface */ -#endif -#ifdef CONFIG_CODA_FS_OLD_API -#define CODA_KERNEL_VERSION 2 /* venus_lookup got an extra parameter */ -#else #define CODA_KERNEL_VERSION 3 /* 128-bit file identifiers */ -#endif /* * Venus <-> Coda RPC arguments @@ -329,16 +297,9 @@ struct coda_statfs { struct coda_in_hdr { u_int32_t opcode; u_int32_t unique; /* Keep multiple outstanding msgs distinct */ -#ifdef CONFIG_CODA_FS_OLD_API - u_int16_t pid; /* Common to all */ - u_int16_t pgid; /* Common to all */ - u_int16_t sid; /* Common to all */ - struct coda_cred cred; /* Common to all */ -#else pid_t pid; pid_t pgid; vuid_t uid; -#endif }; /* Really important that opcode and unique are 1st two fields! */ @@ -613,11 +574,7 @@ struct coda_vget_out { /* CODA_PURGEUSER is a venus->kernel call */ struct coda_purgeuser_out { struct coda_out_hdr oh; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred; -#else vuid_t uid; -#endif }; /* coda_zapfile: */ -- cgit v1.2.3 From f68215c4640a38d66429014e524a627bf572d26a Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 25 Jul 2008 01:46:38 -0700 Subject: reiserfs: convert j_lock to mutex j_lock is a semaphore but uses it as if it were a mutex. This patch converts it to a mutex. Signed-off-by: Jeff Mahoney Cc: Matthew Wilcox Cc: Chris Mason Cc: Edward Shishkin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 6 +++--- include/linux/reiserfs_fs_sb.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index e396b2fa4743..0f7b1e807e60 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -558,13 +558,13 @@ static inline void insert_journal_hash(struct reiserfs_journal_cnode **table, static inline void lock_journal(struct super_block *p_s_sb) { PROC_INFO_INC(p_s_sb, journal.lock_journal); - down(&SB_JOURNAL(p_s_sb)->j_lock); + mutex_lock(&SB_JOURNAL(p_s_sb)->j_mutex); } /* unlock the current transaction */ static inline void unlock_journal(struct super_block *p_s_sb) { - up(&SB_JOURNAL(p_s_sb)->j_lock); + mutex_unlock(&SB_JOURNAL(p_s_sb)->j_mutex); } static inline void get_journal_list(struct reiserfs_journal_list *jl) @@ -2837,7 +2837,7 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name, journal->j_last = NULL; journal->j_first = NULL; init_waitqueue_head(&(journal->j_join_wait)); - sema_init(&journal->j_lock, 1); + mutex_init(&journal->j_mutex); sema_init(&journal->j_flush_sem, 1); journal->j_trans_id = 10; diff --git a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h index 336ee43ed7d8..49b639b88bac 100644 --- a/include/linux/reiserfs_fs_sb.h +++ b/include/linux/reiserfs_fs_sb.h @@ -193,7 +193,7 @@ struct reiserfs_journal { struct buffer_head *j_header_bh; time_t j_trans_start_time; /* time this transaction started */ - struct semaphore j_lock; + struct mutex j_mutex; struct semaphore j_flush_sem; wait_queue_head_t j_join_wait; /* wait for current transaction to finish before starting new one */ atomic_t j_jlock; /* lock for j_join_wait */ -- cgit v1.2.3 From afe70259076fff0446001eaa1a287f615241a357 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 25 Jul 2008 01:46:39 -0700 Subject: reiserfs: convert j_flush_sem to mutex j_flush_sem is a semaphore but uses it as if it were a mutex. This patch converts it to a mutex. [akpm@linux-foundation.org: fix mutex_trylock retval treatment] Signed-off-by: Jeff Mahoney Cc: Matthew Wilcox Cc: Chris Mason Cc: Edward Shishkin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 14 +++++++------- include/linux/reiserfs_fs_sb.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 0f7b1e807e60..3cb4a562030e 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -1411,8 +1411,8 @@ static int flush_journal_list(struct super_block *s, /* if flushall == 0, the lock is already held */ if (flushall) { - down(&journal->j_flush_sem); - } else if (!down_trylock(&journal->j_flush_sem)) { + mutex_lock(&journal->j_flush_mutex); + } else if (mutex_trylock(&journal->j_flush_mutex)) { BUG(); } @@ -1642,7 +1642,7 @@ static int flush_journal_list(struct super_block *s, jl->j_state = 0; put_journal_list(s, jl); if (flushall) - up(&journal->j_flush_sem); + mutex_unlock(&journal->j_flush_mutex); put_fs_excl(); return err; } @@ -1772,12 +1772,12 @@ static int kupdate_transactions(struct super_block *s, struct reiserfs_journal *journal = SB_JOURNAL(s); chunk.nr = 0; - down(&journal->j_flush_sem); + mutex_lock(&journal->j_flush_mutex); if (!journal_list_still_alive(s, orig_trans_id)) { goto done; } - /* we've got j_flush_sem held, nobody is going to delete any + /* we've got j_flush_mutex held, nobody is going to delete any * of these lists out from underneath us */ while ((num_trans && transactions_flushed < num_trans) || @@ -1812,7 +1812,7 @@ static int kupdate_transactions(struct super_block *s, } done: - up(&journal->j_flush_sem); + mutex_unlock(&journal->j_flush_mutex); return ret; } @@ -2838,7 +2838,7 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name, journal->j_first = NULL; init_waitqueue_head(&(journal->j_join_wait)); mutex_init(&journal->j_mutex); - sema_init(&journal->j_flush_sem, 1); + mutex_init(&journal->j_flush_mutex); journal->j_trans_id = 10; journal->j_mount_id = 10; diff --git a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h index 49b639b88bac..c0751724ee64 100644 --- a/include/linux/reiserfs_fs_sb.h +++ b/include/linux/reiserfs_fs_sb.h @@ -194,7 +194,7 @@ struct reiserfs_journal { time_t j_trans_start_time; /* time this transaction started */ struct mutex j_mutex; - struct semaphore j_flush_sem; + struct mutex j_flush_mutex; wait_queue_head_t j_join_wait; /* wait for current transaction to finish before starting new one */ atomic_t j_jlock; /* lock for j_join_wait */ int j_list_bitmap_index; /* number of next list bitmap to use */ -- cgit v1.2.3 From 90415deac75a761a25239af6f56381546f8d2201 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 25 Jul 2008 01:46:40 -0700 Subject: reiserfs: convert j_commit_lock to mutex j_commit_lock is a semaphore but uses it as if it were a mutex. This patch converts it to a mutex. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Jeff Mahoney Cc: Matthew Wilcox Cc: Chris Mason Cc: Edward Shishkin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 22 ++++++++++------------ include/linux/reiserfs_fs_sb.h | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 3cb4a562030e..c8f60ee183b5 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -34,15 +34,10 @@ ** from within kupdate, it will ignore the immediate flag */ -#include -#include - #include #include - #include #include - #include #include #include @@ -54,6 +49,9 @@ #include #include #include +#include + +#include /* gets a struct reiserfs_journal_list * from a list head */ #define JOURNAL_LIST_ENTRY(h) (list_entry((h), struct reiserfs_journal_list, \ @@ -1045,9 +1043,9 @@ static int flush_commit_list(struct super_block *s, } /* make sure nobody is trying to flush this one at the same time */ - down(&jl->j_commit_lock); + mutex_lock(&jl->j_commit_mutex); if (!journal_list_still_alive(s, trans_id)) { - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); goto put_jl; } BUG_ON(jl->j_trans_id == 0); @@ -1057,7 +1055,7 @@ static int flush_commit_list(struct super_block *s, if (flushall) { atomic_set(&(jl->j_older_commits_done), 1); } - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); goto put_jl; } @@ -1181,7 +1179,7 @@ static int flush_commit_list(struct super_block *s, if (flushall) { atomic_set(&(jl->j_older_commits_done), 1); } - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); put_jl: put_journal_list(s, jl); @@ -2556,7 +2554,7 @@ static struct reiserfs_journal_list *alloc_journal_list(struct super_block *s) INIT_LIST_HEAD(&jl->j_working_list); INIT_LIST_HEAD(&jl->j_tail_bh_list); INIT_LIST_HEAD(&jl->j_bh_list); - sema_init(&jl->j_commit_lock, 1); + mutex_init(&jl->j_commit_mutex); SB_JOURNAL(s)->j_num_lists++; get_journal_list(jl); return jl; @@ -4030,7 +4028,7 @@ static int do_journal_end(struct reiserfs_transaction_handle *th, * the new transaction is fully setup, and we've already flushed the * ordered bh list */ - down(&jl->j_commit_lock); + mutex_lock(&jl->j_commit_mutex); /* save the transaction id in case we need to commit it later */ commit_trans_id = jl->j_trans_id; @@ -4196,7 +4194,7 @@ static int do_journal_end(struct reiserfs_transaction_handle *th, lock_kernel(); } BUG_ON(!list_empty(&jl->j_tail_bh_list)); - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); /* honor the flush wishes from the caller, simple commits can ** be done outside the journal lock, they are done below diff --git a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h index c0751724ee64..315517e8bfa1 100644 --- a/include/linux/reiserfs_fs_sb.h +++ b/include/linux/reiserfs_fs_sb.h @@ -152,7 +152,7 @@ struct reiserfs_journal_list { atomic_t j_nonzerolen; atomic_t j_commit_left; atomic_t j_older_commits_done; /* all commits older than this on disk */ - struct semaphore j_commit_lock; + struct mutex j_commit_mutex; unsigned long j_trans_id; time_t j_timestamp; struct reiserfs_list_bitmap *j_list_bitmap; -- cgit v1.2.3 From 4596c8aaf96e8634ca755c9f34b91420a39bebd4 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Fri, 25 Jul 2008 01:46:42 -0700 Subject: fat: fix VFAT_IOCTL_READDIR_xxx and cleanup for userland "struct dirent" is a kernel type here, but is a **different type** in userspace! This means both the structure and the IOCTL number is wrong! So, this adds new "struct __fat_dirent" to generate correct IOCTL number. And kernel stuff moves to under __KERNEL__. Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/msdos_fs.h | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 81cd36b735b0..5161394c7894 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -2,11 +2,11 @@ #define _LINUX_MSDOS_FS_H #include +#include /* * The MS-DOS filesystem constants/structures */ -#include #define SECTOR_SIZE 512 /* sector size (bytes) */ #define SECTOR_BITS 9 /* log2(SECTOR_SIZE) */ @@ -89,24 +89,22 @@ #define IS_FSINFO(x) (le32_to_cpu((x)->signature1) == FAT_FSINFO_SIG1 \ && le32_to_cpu((x)->signature2) == FAT_FSINFO_SIG2) +struct __fat_dirent { + long d_ino; + __kernel_off_t d_off; + unsigned short d_reclen; + char d_name[256]; /* We must not include limits.h! */ +}; + /* * ioctl commands */ -#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2]) -#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2]) +#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct __fat_dirent[2]) +#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct __fat_dirent[2]) /* has used 0x72 ('r') in collision, so skip a few */ #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) #define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) -/* - * vfat shortname flags - */ -#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */ -#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */ -#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */ -#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ -#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ - struct fat_boot_sector { __u8 ignored[3]; /* Boot strap short or near jump */ __u8 system_id[8]; /* Name - can be used to special case @@ -168,14 +166,6 @@ struct msdos_dir_slot { __u8 name11_12[4]; /* last 2 characters in name */ }; -struct fat_slot_info { - loff_t i_pos; /* on-disk position of directory entry */ - loff_t slot_off; /* offset for slot or de start */ - int nr_slots; /* number of slots + 1(de) in filename */ - struct msdos_dir_entry *de; - struct buffer_head *bh; -}; - #ifdef __KERNEL__ #include @@ -184,6 +174,15 @@ struct fat_slot_info { #include #include +/* + * vfat shortname flags + */ +#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */ +#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */ +#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */ +#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ +#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ + struct fat_mount_options { uid_t fs_uid; gid_t fs_gid; @@ -267,6 +266,14 @@ struct msdos_inode_info { struct inode vfs_inode; }; +struct fat_slot_info { + loff_t i_pos; /* on-disk position of directory entry */ + loff_t slot_off; /* offset for slot or de start */ + int nr_slots; /* number of slots + 1(de) in filename */ + struct msdos_dir_entry *de; + struct buffer_head *bh; +}; + static inline struct msdos_sb_info *MSDOS_SB(struct super_block *sb) { return sb->s_fs_info; -- cgit v1.2.3 From 7557bc66be629d19a402e752673708bfbb8b5e86 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 01:46:45 -0700 Subject: msdos fs: remove unsettable atari option It has been impossible to set the option 'atari' of the MSDOS filesystem for several years. Since nobody seems to have missed it, let's remove its remains. Signed-off-by: Rene Scharfe Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/msdos/namei.c | 18 ++++++------------ include/linux/msdos_fs.h | 1 - 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 1f7f2956412a..e4ad6c6b753e 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -14,12 +14,7 @@ /* Characters that are undesirable in an MS-DOS file name */ static unsigned char bad_chars[] = "*?<>|\""; -static unsigned char bad_if_strict_pc[] = "+=,; "; -/* GEMDOS is less restrictive */ -static unsigned char bad_if_strict_atari[] = " "; - -#define bad_if_strict(opts) \ - ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc) +static unsigned char bad_if_strict[] = "+=,; "; /***** Formats an MS-DOS file name. Rejects invalid names. */ static int msdos_format_name(const unsigned char *name, int len, @@ -40,21 +35,20 @@ static int msdos_format_name(const unsigned char *name, int len, /* Get rid of dot - test for it elsewhere */ name++; len--; - } else if (!opts->atari) + } else return -EINVAL; } /* - * disallow names that _really_ start with a dot for MS-DOS, - * GEMDOS does not care + * disallow names that _really_ start with a dot */ - space = !opts->atari; + space = 1; c = 0; for (walk = res; len && walk - res < 8; walk++) { c = *name++; len--; if (opts->name_check != 'r' && strchr(bad_chars, c)) return -EINVAL; - if (opts->name_check == 's' && strchr(bad_if_strict(opts), c)) + if (opts->name_check == 's' && strchr(bad_if_strict, c)) return -EINVAL; if (c >= 'A' && c <= 'Z' && opts->name_check == 's') return -EINVAL; @@ -94,7 +88,7 @@ static int msdos_format_name(const unsigned char *name, int len, if (opts->name_check != 'r' && strchr(bad_chars, c)) return -EINVAL; if (opts->name_check == 's' && - strchr(bad_if_strict(opts), c)) + strchr(bad_if_strict, c)) return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 5161394c7894..3346c9c8f17a 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -201,7 +201,6 @@ struct fat_mount_options { utf8:1, /* Use of UTF-8 character set (Default) */ unicode_xlate:1, /* create escape sequences for unhandled Unicode */ numtail:1, /* Does first alias have a numeric '~1' type tail? */ - atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */ flush:1, /* write things quickly */ nocase:1, /* Does this need case conversion? 0=need case conversion*/ usefree:1; /* Use free_clusters for FAT32 */ -- cgit v1.2.3 From cf6ae8b50e0ee3f764392dadd1970e3f03c40773 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:46:46 -0700 Subject: remove the in-kernel struct dirent{,64} The kernel struct dirent{,64} were different from the ones in userspace. Even worse, we exported the kernel ones to userspace. But after the fat usages are fixed we can remove the conflicting kernel versions. Reviewed-by: H. Peter Anvin Signed-off-by: Adrian Bunk Cc: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/Kbuild | 1 - include/linux/dirent.h | 20 -------------------- 2 files changed, 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 71d70d1fbce2..a18008ce7aba 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -189,7 +189,6 @@ unifdef-y += connector.h unifdef-y += cuda.h unifdef-y += cyclades.h unifdef-y += dccp.h -unifdef-y += dirent.h unifdef-y += dlm.h unifdef-y += dlm_plock.h unifdef-y += edd.h diff --git a/include/linux/dirent.h b/include/linux/dirent.h index 5d6023b87800..f072fb8d10a3 100644 --- a/include/linux/dirent.h +++ b/include/linux/dirent.h @@ -1,23 +1,6 @@ #ifndef _LINUX_DIRENT_H #define _LINUX_DIRENT_H -struct dirent { - long d_ino; - __kernel_off_t d_off; - unsigned short d_reclen; - char d_name[256]; /* We must not include limits.h! */ -}; - -struct dirent64 { - __u64 d_ino; - __s64 d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -#ifdef __KERNEL__ - struct linux_dirent64 { u64 d_ino; s64 d_off; @@ -26,7 +9,4 @@ struct linux_dirent64 { char d_name[0]; }; -#endif /* __KERNEL__ */ - - #endif -- cgit v1.2.3 From e8938a62a85d1f487e02c3b01955b47c9598f6d2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:46:46 -0700 Subject: remove unused #include 's Remove some unused #include 's. Signed-off-by: Adrian Bunk Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/kernel/linux32.c | 1 - fs/compat_ioctl.c | 1 - fs/smbfs/cache.c | 1 - fs/smbfs/proc.c | 1 - include/linux/nfsd/nfsd.h | 1 - 5 files changed, 5 deletions(-) (limited to 'include/linux') diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index c266211ed653..2fefb14414b7 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 18e2c548161d..5235c67e7594 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c index 8182f0542a21..8c177eb7e344 100644 --- a/fs/smbfs/cache.c +++ b/fs/smbfs/cache.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index d517a27b7f4b..ee536e8a649a 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index a2861d95ecc3..108f47e5fd95 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -12,7 +12,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From b271e067c896ad4082b15e96077675d08db40625 Mon Sep 17 00:00:00 2001 From: Joe Peterson Date: Fri, 25 Jul 2008 01:46:47 -0700 Subject: fatfs: add UTC timestamp option Provide a new mount option ("tz=UTC") for DOS (vfat/msdos) filesystems, allowing timestamps to be in coordinated universal time (UTC) rather than local time in applications where doing this is advantageous. In particular, portable devices that use fat/vfat (such as digital cameras) can benefit from using UTC in their internal clocks, thus avoiding daylight saving time errors and general time ambiguity issues. The user of the device does not have to worry about changing the time when moving from place or when daylight saving changes. The new mount option, when set, disables the counter-adjustment that Linux currently makes to FAT timestamp info in anticipation of the normal userspace time zone correction. When used in this new mode, all daylight saving time and time zone handling is done in userspace as is normal for many other filesystems (like ext3). The default mode, which remains unchanged, is still appropriate when mounting volumes written in Windows (because of its use of local time). I originally based this patch on one submitted last year by Paul Collins, but I updated it to work with current source and changed variable/option naming. Ogawa Hirofumi (who maintains these filesystems) and I discussed this patch at length on lkml, and he suggested using the option name in the attached version of the patch. Barry Bouwsma pointed out a good addition to the patch as well. Signed-off-by: Joe Peterson Signed-off-by: Paul Collins Acked-by: OGAWA Hirofumi Cc: Barry Bouwsma Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/dir.c | 2 +- fs/fat/inode.c | 27 ++++++++++++++++++++------- fs/fat/misc.c | 10 ++++++---- fs/msdos/namei.c | 3 ++- fs/vfat/namei.c | 2 +- include/linux/msdos_fs.h | 8 +++++--- 6 files changed, 35 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 4c35477bc94c..cd4a0162e10d 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -1101,7 +1101,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) goto error_free; } - fat_date_unix2dos(ts->tv_sec, &time, &date); + fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); de = (struct msdos_dir_entry *)bhs[0]->b_data; /* filling the new directory slots ("." and ".." entries) */ diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 60deb5fd1188..23676f9d79ce 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -382,17 +382,20 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) & ~((loff_t)sbi->cluster_size - 1)) >> 9; inode->i_mtime.tv_sec = - date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date)); + date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date), + sbi->options.tz_utc); inode->i_mtime.tv_nsec = 0; if (sbi->options.isvfat) { int secs = de->ctime_cs / 100; int csecs = de->ctime_cs % 100; inode->i_ctime.tv_sec = date_dos2unix(le16_to_cpu(de->ctime), - le16_to_cpu(de->cdate)) + secs; + le16_to_cpu(de->cdate), + sbi->options.tz_utc) + secs; inode->i_ctime.tv_nsec = csecs * 10000000; inode->i_atime.tv_sec = - date_dos2unix(0, le16_to_cpu(de->adate)); + date_dos2unix(0, le16_to_cpu(de->adate), + sbi->options.tz_utc); inode->i_atime.tv_nsec = 0; } else inode->i_ctime = inode->i_atime = inode->i_mtime; @@ -591,11 +594,14 @@ retry: raw_entry->attr = fat_attr(inode); raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); - fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date); + fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, + &raw_entry->date, sbi->options.tz_utc); if (sbi->options.isvfat) { __le16 atime; - fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate); - fat_date_unix2dos(inode->i_atime.tv_sec,&atime,&raw_entry->adate); + fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime, + &raw_entry->cdate, sbi->options.tz_utc); + fat_date_unix2dos(inode->i_atime.tv_sec, &atime, + &raw_entry->adate, sbi->options.tz_utc); raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + inode->i_ctime.tv_nsec / 10000000; } @@ -836,6 +842,8 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt) } if (sbi->options.flush) seq_puts(m, ",flush"); + if (opts->tz_utc) + seq_puts(m, ",tz=UTC"); return 0; } @@ -848,7 +856,7 @@ enum { Opt_charset, Opt_shortname_lower, Opt_shortname_win95, Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, - Opt_obsolate, Opt_flush, Opt_err, + Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, }; static match_table_t fat_tokens = { @@ -883,6 +891,7 @@ static match_table_t fat_tokens = { {Opt_obsolate, "cvf_options=%100s"}, {Opt_obsolate, "posix"}, {Opt_flush, "flush"}, + {Opt_tz_utc, "tz=UTC"}, {Opt_err, NULL}, }; static match_table_t msdos_tokens = { @@ -947,6 +956,7 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, opts->utf8 = opts->unicode_xlate = 0; opts->numtail = 1; opts->usefree = opts->nocase = 0; + opts->tz_utc = 0; *debug = 0; if (!options) @@ -1036,6 +1046,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, case Opt_flush: opts->flush = 1; break; + case Opt_tz_utc: + opts->tz_utc = 1; + break; /* msdos specific */ case Opt_dots: diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 61f23511eacf..79fb98ad36d4 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -142,7 +142,7 @@ static int day_n[] = { }; /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ -int date_dos2unix(unsigned short time, unsigned short date) +int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) { int month, year, secs; @@ -156,16 +156,18 @@ int date_dos2unix(unsigned short time, unsigned short date) ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ - secs += sys_tz.tz_minuteswest*60; + if (!tz_utc) + secs += sys_tz.tz_minuteswest*60; return secs; } /* Convert linear UNIX date to a MS-DOS time/date pair. */ -void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date) +void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) { int day, year, nl_day, month; - unix_date -= sys_tz.tz_minuteswest*60; + if (!tz_utc) + unix_date -= sys_tz.tz_minuteswest*60; /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ if (unix_date < 315532800) diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index e4ad6c6b753e..e844b9809d27 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -237,6 +237,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name, int is_dir, int is_hid, int cluster, struct timespec *ts, struct fat_slot_info *sinfo) { + struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); struct msdos_dir_entry de; __le16 time, date; int err; @@ -246,7 +247,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name, if (is_hid) de.attr |= ATTR_HIDDEN; de.lcase = 0; - fat_date_unix2dos(ts->tv_sec, &time, &date); + fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); de.cdate = de.adate = 0; de.ctime = 0; de.ctime_cs = 0; diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index b546ba69be82..155c10b4adbd 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -621,7 +621,7 @@ shortname: memcpy(de->name, msdos_name, MSDOS_NAME); de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; de->lcase = lcase; - fat_date_unix2dos(ts->tv_sec, &time, &date); + fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); de->time = de->ctime = time; de->date = de->cdate = de->adate = date; de->ctime_cs = 0; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 3346c9c8f17a..ba63858056c7 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -203,7 +203,8 @@ struct fat_mount_options { numtail:1, /* Does first alias have a numeric '~1' type tail? */ flush:1, /* write things quickly */ nocase:1, /* Does this need case conversion? 0=need case conversion*/ - usefree:1; /* Use free_clusters for FAT32 */ + usefree:1, /* Use free_clusters for FAT32 */ + tz_utc:1; /* Filesystem timestamps are in UTC */ }; #define FAT_HASH_BITS 8 @@ -434,8 +435,9 @@ extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); extern void fat_clusters_flush(struct super_block *sb); extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); -extern int date_dos2unix(unsigned short time, unsigned short date); -extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date); +extern int date_dos2unix(unsigned short time, unsigned short date, int tz_utc); +extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, + int tz_utc); extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); int fat_cache_init(void); -- cgit v1.2.3 From b85f4b87a511bea86dac68c4f0fabaee2cac6c4c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:50 -0700 Subject: quota: rename quota functions from upper case, make bigger ones non-inline Cleanup quotaops.h: Rename functions from uppercase to lowercase (and define backward compatibility macros), move larger functions to dquot.c and make them non-inline. Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dquot.c | 53 +++++++++++ include/linux/quotaops.h | 226 ++++++++++++++++++++++------------------------- 2 files changed, 160 insertions(+), 119 deletions(-) (limited to 'include/linux') diff --git a/fs/dquot.c b/fs/dquot.c index ad88cf6fcbaf..0bcaf970bbb4 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1153,6 +1153,28 @@ int dquot_drop(struct inode *inode) return 0; } +/* Wrapper to remove references to quota structures from inode */ +void vfs_dq_drop(struct inode *inode) +{ + /* Here we can get arbitrary inode from clear_inode() so we have + * to be careful. OTOH we don't need locking as quota operations + * are allowed to change only at mount time */ + if (!IS_NOQUOTA(inode) && inode->i_sb && inode->i_sb->dq_op + && inode->i_sb->dq_op->drop) { + int cnt; + /* Test before calling to rule out calls from proc and such + * where we are not allowed to block. Note that this is + * actually reliable test even without the lock - the caller + * must assure that nobody can come after the DQUOT_DROP and + * add quota pointers back anyway */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (inode->i_dquot[cnt] != NODQUOT) + break; + if (cnt < MAXQUOTAS) + inode->i_sb->dq_op->drop(inode); + } +} + /* * Following four functions update i_blocks+i_bytes fields and * quota information (together with appropriate checks) @@ -1426,6 +1448,18 @@ warn_put_all: return ret; } +/* Wrapper for transferring ownership of an inode */ +int vfs_dq_transfer(struct inode *inode, struct iattr *iattr) +{ + if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) { + vfs_dq_init(inode); + if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) + return 1; + } + return 0; +} + + /* * Write info of quota file to disk */ @@ -1766,6 +1800,22 @@ out: return error; } +/* Wrapper to turn on quotas when remounting rw */ +int vfs_dq_quota_on_remount(struct super_block *sb) +{ + int cnt; + int ret = 0, err; + + if (!sb->s_qcop || !sb->s_qcop->quota_on) + return -ENOSYS; + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + err = sb->s_qcop->quota_on(sb, cnt, 0, NULL, 1); + if (err < 0 && !ret) + ret = err; + } + return ret; +} + /* Generic routine for getting common part of quota structure */ static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di) { @@ -2101,8 +2151,11 @@ EXPORT_SYMBOL(dquot_release); EXPORT_SYMBOL(dquot_mark_dquot_dirty); EXPORT_SYMBOL(dquot_initialize); EXPORT_SYMBOL(dquot_drop); +EXPORT_SYMBOL(vfs_dq_drop); EXPORT_SYMBOL(dquot_alloc_space); EXPORT_SYMBOL(dquot_alloc_inode); EXPORT_SYMBOL(dquot_free_space); EXPORT_SYMBOL(dquot_free_inode); EXPORT_SYMBOL(dquot_transfer); +EXPORT_SYMBOL(vfs_dq_transfer); +EXPORT_SYMBOL(vfs_dq_quota_on_remount); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index f86702053853..0c8f9fe462af 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -19,34 +19,38 @@ /* * declaration of quota_function calls in kernel. */ -extern void sync_dquots(struct super_block *sb, int type); - -extern int dquot_initialize(struct inode *inode, int type); -extern int dquot_drop(struct inode *inode); - -extern int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); -extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); - -extern int dquot_free_space(struct inode *inode, qsize_t number); -extern int dquot_free_inode(const struct inode *inode, unsigned long number); - -extern int dquot_transfer(struct inode *inode, struct iattr *iattr); -extern int dquot_commit(struct dquot *dquot); -extern int dquot_acquire(struct dquot *dquot); -extern int dquot_release(struct dquot *dquot); -extern int dquot_commit_info(struct super_block *sb, int type); -extern int dquot_mark_dquot_dirty(struct dquot *dquot); - -extern int vfs_quota_on(struct super_block *sb, int type, int format_id, - char *path, int remount); -extern int vfs_quota_on_mount(struct super_block *sb, char *qf_name, - int format_id, int type); -extern int vfs_quota_off(struct super_block *sb, int type, int remount); -extern int vfs_quota_sync(struct super_block *sb, int type); -extern int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); -extern int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); -extern int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); -extern int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); +void sync_dquots(struct super_block *sb, int type); + +int dquot_initialize(struct inode *inode, int type); +int dquot_drop(struct inode *inode); + +int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); +int dquot_alloc_inode(const struct inode *inode, unsigned long number); + +int dquot_free_space(struct inode *inode, qsize_t number); +int dquot_free_inode(const struct inode *inode, unsigned long number); + +int dquot_transfer(struct inode *inode, struct iattr *iattr); +int dquot_commit(struct dquot *dquot); +int dquot_acquire(struct dquot *dquot); +int dquot_release(struct dquot *dquot); +int dquot_commit_info(struct super_block *sb, int type); +int dquot_mark_dquot_dirty(struct dquot *dquot); + +int vfs_quota_on(struct super_block *sb, int type, int format_id, + char *path, int remount); +int vfs_quota_on_mount(struct super_block *sb, char *qf_name, + int format_id, int type); +int vfs_quota_off(struct super_block *sb, int type, int remount); +int vfs_quota_sync(struct super_block *sb, int type); +int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); +int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); + +void vfs_dq_drop(struct inode *inode); +int vfs_dq_transfer(struct inode *inode, struct iattr *iattr); +int vfs_dq_quota_on_remount(struct super_block *sb); /* * Operations supported for diskquotas. @@ -59,38 +63,16 @@ extern struct quotactl_ops vfs_quotactl_ops; /* It is better to call this function outside of any transaction as it might * need a lot of space in journal for dquot structure allocation. */ -static inline void DQUOT_INIT(struct inode *inode) +static inline void vfs_dq_init(struct inode *inode) { BUG_ON(!inode->i_sb); if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) inode->i_sb->dq_op->initialize(inode, -1); } -/* The same as with DQUOT_INIT */ -static inline void DQUOT_DROP(struct inode *inode) -{ - /* Here we can get arbitrary inode from clear_inode() so we have - * to be careful. OTOH we don't need locking as quota operations - * are allowed to change only at mount time */ - if (!IS_NOQUOTA(inode) && inode->i_sb && inode->i_sb->dq_op - && inode->i_sb->dq_op->drop) { - int cnt; - /* Test before calling to rule out calls from proc and such - * where we are not allowed to block. Note that this is - * actually reliable test even without the lock - the caller - * must assure that nobody can come after the DQUOT_DROP and - * add quota pointers back anyway */ - for (cnt = 0; cnt < MAXQUOTAS; cnt++) - if (inode->i_dquot[cnt] != NODQUOT) - break; - if (cnt < MAXQUOTAS) - inode->i_sb->dq_op->drop(inode); - } -} - /* The following allocation/freeing/transfer functions *must* be called inside * a transaction (deadlocks possible otherwise) */ -static inline int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) { /* Used space is updated in alloc_space() */ @@ -102,15 +84,15 @@ static inline int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) return 0; } -static inline int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr) { int ret; - if (!(ret = DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr))) + if (!(ret = vfs_dq_prealloc_space_nodirty(inode, nr))) mark_inode_dirty(inode); return ret; } -static inline int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) { /* Used space is updated in alloc_space() */ @@ -122,25 +104,25 @@ static inline int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) return 0; } -static inline int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr) { int ret; - if (!(ret = DQUOT_ALLOC_SPACE_NODIRTY(inode, nr))) + if (!(ret = vfs_dq_alloc_space_nodirty(inode, nr))) mark_inode_dirty(inode); return ret; } -static inline int DQUOT_ALLOC_INODE(struct inode *inode) +static inline int vfs_dq_alloc_inode(struct inode *inode) { if (sb_any_quota_enabled(inode->i_sb)) { - DQUOT_INIT(inode); + vfs_dq_init(inode); if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) return 1; } return 0; } -static inline void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) inode->i_sb->dq_op->free_space(inode, nr); @@ -148,35 +130,25 @@ static inline void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) inode_sub_bytes(inode, nr); } -static inline void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE_NODIRTY(inode, nr); + vfs_dq_free_space_nodirty(inode, nr); mark_inode_dirty(inode); } -static inline void DQUOT_FREE_INODE(struct inode *inode) +static inline void vfs_dq_free_inode(struct inode *inode) { if (sb_any_quota_enabled(inode->i_sb)) inode->i_sb->dq_op->free_inode(inode, 1); } -static inline int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) -{ - if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) { - DQUOT_INIT(inode); - if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) - return 1; - } - return 0; -} - /* The following two functions cannot be called inside a transaction */ -static inline void DQUOT_SYNC(struct super_block *sb) +static inline void vfs_dq_sync(struct super_block *sb) { sync_dquots(sb, -1); } -static inline int DQUOT_OFF(struct super_block *sb, int remount) +static inline int vfs_dq_off(struct super_block *sb, int remount) { int ret = -ENOSYS; @@ -185,21 +157,6 @@ static inline int DQUOT_OFF(struct super_block *sb, int remount) return ret; } -static inline int DQUOT_ON_REMOUNT(struct super_block *sb) -{ - int cnt; - int ret = 0, err; - - if (!sb->s_qcop || !sb->s_qcop->quota_on) - return -ENOSYS; - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - err = sb->s_qcop->quota_on(sb, cnt, 0, NULL, 1); - if (err < 0 && !ret) - ret = err; - } - return ret; -} - #else /* @@ -208,113 +165,144 @@ static inline int DQUOT_ON_REMOUNT(struct super_block *sb) #define sb_dquot_ops (NULL) #define sb_quotactl_ops (NULL) -static inline void DQUOT_INIT(struct inode *inode) +static inline void vfs_dq_init(struct inode *inode) { } -static inline void DQUOT_DROP(struct inode *inode) +static inline void vfs_dq_drop(struct inode *inode) { } -static inline int DQUOT_ALLOC_INODE(struct inode *inode) +static inline int vfs_dq_alloc_inode(struct inode *inode) { return 0; } -static inline void DQUOT_FREE_INODE(struct inode *inode) +static inline void vfs_dq_free_inode(struct inode *inode) { } -static inline void DQUOT_SYNC(struct super_block *sb) +static inline void vfs_dq_sync(struct super_block *sb) { } -static inline int DQUOT_OFF(struct super_block *sb, int remount) +static inline int vfs_dq_off(struct super_block *sb, int remount) { return 0; } -static inline int DQUOT_ON_REMOUNT(struct super_block *sb) +static inline int vfs_dq_quota_on_remount(struct super_block *sb) { return 0; } -static inline int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) +static inline int vfs_dq_transfer(struct inode *inode, struct iattr *iattr) { return 0; } -static inline int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr) { inode_add_bytes(inode, nr); return 0; } -static inline int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr) { - DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr); + vfs_dq_prealloc_space_nodirty(inode, nr); mark_inode_dirty(inode); return 0; } -static inline int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr) { inode_add_bytes(inode, nr); return 0; } -static inline int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr) { - DQUOT_ALLOC_SPACE_NODIRTY(inode, nr); + vfs_dq_alloc_space_nodirty(inode, nr); mark_inode_dirty(inode); return 0; } -static inline void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr) { inode_sub_bytes(inode, nr); } -static inline void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE_NODIRTY(inode, nr); + vfs_dq_free_space_nodirty(inode, nr); mark_inode_dirty(inode); } #endif /* CONFIG_QUOTA */ -static inline int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_block_nodirty(struct inode *inode, qsize_t nr) { - return DQUOT_PREALLOC_SPACE_NODIRTY(inode, + return vfs_dq_prealloc_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline int DQUOT_PREALLOC_BLOCK(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_block(struct inode *inode, qsize_t nr) { - return DQUOT_PREALLOC_SPACE(inode, + return vfs_dq_prealloc_space(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_block_nodirty(struct inode *inode, qsize_t nr) { - return DQUOT_ALLOC_SPACE_NODIRTY(inode, + return vfs_dq_alloc_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline int DQUOT_ALLOC_BLOCK(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_block(struct inode *inode, qsize_t nr) { - return DQUOT_ALLOC_SPACE(inode, + return vfs_dq_alloc_space(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_block_nodirty(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE_NODIRTY(inode, nr << inode->i_sb->s_blocksize_bits); + vfs_dq_free_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline void DQUOT_FREE_BLOCK(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_block(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE(inode, nr << inode->i_sb->s_blocksize_bits); + vfs_dq_free_space(inode, nr << inode->i_sb->s_blocksize_bits); } +/* + * Define uppercase equivalents for compatibility with old function names + * Can go away when we think all users have been converted (15/04/2008) + */ +#define DQUOT_INIT(inode) vfs_dq_init(inode) +#define DQUOT_DROP(inode) vfs_dq_drop(inode) +#define DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr) \ + vfs_dq_prealloc_space_nodirty(inode, nr) +#define DQUOT_PREALLOC_SPACE(inode, nr) vfs_dq_prealloc_space(inode, nr) +#define DQUOT_ALLOC_SPACE_NODIRTY(inode, nr) \ + vfs_dq_alloc_space_nodirty(inode, nr) +#define DQUOT_ALLOC_SPACE(inode, nr) vfs_dq_alloc_space(inode, nr) +#define DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr) \ + vfs_dq_prealloc_block_nodirty(inode, nr) +#define DQUOT_PREALLOC_BLOCK(inode, nr) vfs_dq_prealloc_block(inode, nr) +#define DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr) \ + vfs_dq_alloc_block_nodirty(inode, nr) +#define DQUOT_ALLOC_BLOCK(inode, nr) vfs_dq_alloc_block(inode, nr) +#define DQUOT_ALLOC_INODE(inode) vfs_dq_alloc_inode(inode) +#define DQUOT_FREE_SPACE_NODIRTY(inode, nr) \ + vfs_dq_free_space_nodirty(inode, nr) +#define DQUOT_FREE_SPACE(inode, nr) vfs_dq_free_space(inode, nr) +#define DQUOT_FREE_BLOCK_NODIRTY(inode, nr) \ + vfs_dq_free_block_nodirty(inode, nr) +#define DQUOT_FREE_BLOCK(inode, nr) vfs_dq_free_block(inode, nr) +#define DQUOT_FREE_INODE(inode) vfs_dq_free_inode(inode) +#define DQUOT_TRANSFER(inode, iattr) vfs_dq_transfer(inode, iattr) +#define DQUOT_SYNC(sb) vfs_dq_sync(sb) +#define DQUOT_OFF(sb, remount) vfs_dq_off(sb, remount) +#define DQUOT_ON_REMOUNT(sb) vfs_dq_quota_on_remount(sb) + #endif /* _LINUX_QUOTAOPS_ */ -- cgit v1.2.3 From 02a55ca87185e114e5d298a8d00608501dbabf67 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:50 -0700 Subject: quota: cleanup loop in sync_dquots() Make loop in sync_dquots() checking whether there's something to write more readable, remove useless variable and macro info_any_dirty() which is used only in this place. Signed-off-by: Jan Kara Cc: "Vegard Nossum" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/quota.c | 18 ++++++++++++------ include/linux/quota.h | 2 -- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/quota.c b/fs/quota.c index db1cc9f3c7aa..7f4386ebc23a 100644 --- a/fs/quota.c +++ b/fs/quota.c @@ -186,7 +186,7 @@ static void quota_sync_sb(struct super_block *sb, int type) void sync_dquots(struct super_block *sb, int type) { - int cnt, dirty; + int cnt; if (sb) { if (sb->s_qcop->quota_sync) @@ -198,11 +198,17 @@ void sync_dquots(struct super_block *sb, int type) restart: list_for_each_entry(sb, &super_blocks, s_list) { /* This test just improves performance so it needn't be reliable... */ - for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++) - if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt) - && info_any_dirty(&sb_dqopt(sb)->info[cnt])) - dirty = 1; - if (!dirty) + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (type != -1 && type != cnt) + continue; + if (!sb_has_quota_enabled(sb, cnt)) + continue; + if (!info_dirty(&sb_dqopt(sb)->info[cnt]) && + list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list)) + continue; + break; + } + if (cnt == MAXQUOTAS) continue; sb->s_count++; spin_unlock(&sb_lock); diff --git a/include/linux/quota.h b/include/linux/quota.h index dcddfb200947..6f1d97ddf828 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -224,8 +224,6 @@ struct super_block; extern void mark_info_dirty(struct super_block *sb, int type); #define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) -#define info_any_dquot_dirty(info) (!list_empty(&(info)->dqi_dirty_list)) -#define info_any_dirty(info) (info_dirty(info) || info_any_dquot_dirty(info)) #define sb_dqopt(sb) (&(sb)->s_dquot) #define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) -- cgit v1.2.3 From 74abb9890dafb12a50dc140de215ed477beb1b88 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:51 -0700 Subject: quota: move function-macros from quota.h to quotaops.h Move declarations of some macros, which should be in fact functions to quotaops.h. This way they can be later converted to inline functions because we can now use declarations from quota.h. Also add necessary includes of quotaops.h to a few files. [akpm@linux-foundation.org: fix JFS build] [akpm@linux-foundation.org: fix UFS build] [vegard.nossum@gmail.com: fix QUOTA=n build] Signed-off-by: Jan Kara Cc: Vegard Nossum Cc: Arjen Pool Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext2/super.c | 1 + fs/jfs/super.c | 1 + fs/quota_v1.c | 1 + fs/quota_v2.c | 1 + fs/reiserfs/super.c | 1 + fs/ufs/super.c | 1 + include/linux/quota.h | 22 +++------------------- include/linux/quotaops.h | 26 ++++++++++++++++++++++++++ 8 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index ef50cbc792db..31308a3b0b8b 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "ext2.h" #include "xattr.h" diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 0288e6d7936a..359c091d8965 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/quota_v1.c b/fs/quota_v1.c index a6cf9269105c..5ae15b13eeb0 100644 --- a/fs/quota_v1.c +++ b/fs/quota_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/quota_v2.c b/fs/quota_v2.c index 234ada903633..b53827dc02d9 100644 --- a/fs/quota_v2.c +++ b/fs/quota_v2.c @@ -11,6 +11,7 @@ #include #include #include +#include #include diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index a10a6d2a8870..2ec748ba0bd3 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 506f724055c2..227c9d700040 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -76,6 +76,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/quota.h b/include/linux/quota.h index 6f1d97ddf828..f9983ea0ff88 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -41,9 +41,6 @@ #define __DQUOT_VERSION__ "dquot_6.5.1" #define __DQUOT_NUM_VERSION__ 6*10000+5*100+1 -typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */ -typedef __u64 qsize_t; /* Type in which we store sizes */ - /* Size of blocks in which are counted size limits */ #define QUOTABLOCK_BITS 10 #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) @@ -172,6 +169,9 @@ enum { #include +typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */ +typedef __u64 qsize_t; /* Type in which we store sizes */ + extern spinlock_t dq_data_lock; /* Maximal numbers of writes for quota operation (insert/delete/update) @@ -225,9 +225,6 @@ struct super_block; extern void mark_info_dirty(struct super_block *sb, int type); #define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) -#define sb_dqopt(sb) (&(sb)->s_dquot) -#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) - struct dqstats { int lookups; int drops; @@ -335,19 +332,6 @@ struct quota_info { struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ }; -#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \ - (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)) - -#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \ - sb_has_quota_enabled(sb, GRPQUOTA)) - -#define sb_has_quota_suspended(sb, type) \ - ((type) == USRQUOTA ? (sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED) : \ - (sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED)) - -#define sb_any_quota_suspended(sb) (sb_has_quota_suspended(sb, USRQUOTA) | \ - sb_has_quota_suspended(sb, GRPQUOTA)) - int register_quota_format(struct quota_format_type *fmt); void unregister_quota_format(struct quota_format_type *fmt); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 0c8f9fe462af..38218c1334b1 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -14,6 +14,8 @@ #include +#define sb_dqopt(sb) (&(sb)->s_dquot) + #if defined(CONFIG_QUOTA) /* @@ -52,6 +54,25 @@ void vfs_dq_drop(struct inode *inode); int vfs_dq_transfer(struct inode *inode, struct iattr *iattr); int vfs_dq_quota_on_remount(struct super_block *sb); +#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) + +/* + * Functions for checking status of quota + */ + +#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \ + (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)) + +#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \ + sb_has_quota_enabled(sb, GRPQUOTA)) + +#define sb_has_quota_suspended(sb, type) \ + ((type) == USRQUOTA ? (sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED) : \ + (sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED)) + +#define sb_any_quota_suspended(sb) (sb_has_quota_suspended(sb, USRQUOTA) | \ + sb_has_quota_suspended(sb, GRPQUOTA)) + /* * Operations supported for diskquotas. */ @@ -159,6 +180,11 @@ static inline int vfs_dq_off(struct super_block *sb, int remount) #else +#define sb_has_quota_enabled(sb, type) 0 +#define sb_any_quota_enabled(sb) 0 +#define sb_has_quota_suspended(sb, type) 0 +#define sb_any_quota_suspended(sb) 0 + /* * NO-OP when quota not configured. */ -- cgit v1.2.3 From 03b063436ca1076301de58d9d628f610ab5404ad Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:52 -0700 Subject: quota: convert macros to inline functions Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/quota.h | 5 +++- include/linux/quotaops.h | 65 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/quota.h b/include/linux/quota.h index f9983ea0ff88..4e004fef8134 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -223,7 +223,10 @@ struct super_block; #define DQF_INFO_DIRTY (1 << DQF_INFO_DIRTY_B) /* Is info dirty? */ extern void mark_info_dirty(struct super_block *sb, int type); -#define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) +static inline int info_dirty(struct mem_dqinfo *info) +{ + return test_bit(DQF_INFO_DIRTY_B, &info->dqi_flags); +} struct dqstats { int lookups; diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 38218c1334b1..742187f7a05c 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -11,10 +11,12 @@ #define _LINUX_QUOTAOPS_ #include - #include -#define sb_dqopt(sb) (&(sb)->s_dquot) +static inline struct quota_info *sb_dqopt(struct super_block *sb) +{ + return &sb->s_dquot; +} #if defined(CONFIG_QUOTA) @@ -54,24 +56,40 @@ void vfs_dq_drop(struct inode *inode); int vfs_dq_transfer(struct inode *inode, struct iattr *iattr); int vfs_dq_quota_on_remount(struct super_block *sb); -#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) +static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type) +{ + return sb_dqopt(sb)->info + type; +} /* * Functions for checking status of quota */ -#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \ - (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)) +static inline int sb_has_quota_enabled(struct super_block *sb, int type) +{ + if (type == USRQUOTA) + return sb_dqopt(sb)->flags & DQUOT_USR_ENABLED; + return sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED; +} -#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \ - sb_has_quota_enabled(sb, GRPQUOTA)) +static inline int sb_any_quota_enabled(struct super_block *sb) +{ + return sb_has_quota_enabled(sb, USRQUOTA) || + sb_has_quota_enabled(sb, GRPQUOTA); +} -#define sb_has_quota_suspended(sb, type) \ - ((type) == USRQUOTA ? (sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED) : \ - (sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED)) +static inline int sb_has_quota_suspended(struct super_block *sb, int type) +{ + if (type == USRQUOTA) + return sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED; + return sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED; +} -#define sb_any_quota_suspended(sb) (sb_has_quota_suspended(sb, USRQUOTA) | \ - sb_has_quota_suspended(sb, GRPQUOTA)) +static inline int sb_any_quota_suspended(struct super_block *sb) +{ + return sb_has_quota_suspended(sb, USRQUOTA) || + sb_has_quota_suspended(sb, GRPQUOTA); +} /* * Operations supported for diskquotas. @@ -180,10 +198,25 @@ static inline int vfs_dq_off(struct super_block *sb, int remount) #else -#define sb_has_quota_enabled(sb, type) 0 -#define sb_any_quota_enabled(sb) 0 -#define sb_has_quota_suspended(sb, type) 0 -#define sb_any_quota_suspended(sb) 0 +static inline int sb_has_quota_enabled(struct super_block *sb, int type) +{ + return 0; +} + +static inline int sb_any_quota_enabled(struct super_block *sb) +{ + return 0; +} + +static inline int sb_has_quota_suspended(struct super_block *sb, int type) +{ + return 0; +} + +static inline int sb_any_quota_suspended(struct super_block *sb) +{ + return 0; +} /* * NO-OP when quota not configured. -- cgit v1.2.3 From 657d3bfa98e542271b449f8cd84c7501ae2b2255 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:52 -0700 Subject: quota: implement sending information via netlink about user below quota Sometimes it may be useful for userspace to know (e.g. for some hosting guys) that some user stopped exceeding his hardlimit or softlimit in quotas. Implement sending of such events to userspace via quota netlink protocol so that they don't have to poll for such events. Based on idea and initial implementation by Vladislav Bogdanov. Cc: Vladislav Bogdanov Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dquot.c | 60 +++++++++++++++++++++++++++++++++++++++++++++------ include/linux/quota.h | 4 ++++ 2 files changed, 58 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/dquot.c b/fs/dquot.c index 0bcaf970bbb4..1346eebe74ce 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -889,7 +889,10 @@ static void print_warning(struct dquot *dquot, const int warntype) char *msg = NULL; struct tty_struct *tty; - if (!need_print_warning(dquot)) + if (warntype == QUOTA_NL_IHARDBELOW || + warntype == QUOTA_NL_ISOFTBELOW || + warntype == QUOTA_NL_BHARDBELOW || + warntype == QUOTA_NL_BSOFTBELOW || !need_print_warning(dquot)) return; mutex_lock(&tty_mutex); @@ -1097,6 +1100,35 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war return QUOTA_OK; } +static int info_idq_free(struct dquot *dquot, ulong inodes) +{ + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) || + dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit) + return QUOTA_NL_NOWARN; + + if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit) + return QUOTA_NL_ISOFTBELOW; + if (dquot->dq_dqb.dqb_curinodes >= dquot->dq_dqb.dqb_ihardlimit && + dquot->dq_dqb.dqb_curinodes - inodes < dquot->dq_dqb.dqb_ihardlimit) + return QUOTA_NL_IHARDBELOW; + return QUOTA_NL_NOWARN; +} + +static int info_bdq_free(struct dquot *dquot, qsize_t space) +{ + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) || + toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit) + return QUOTA_NL_NOWARN; + + if (toqb(dquot->dq_dqb.dqb_curspace - space) <= + dquot->dq_dqb.dqb_bsoftlimit) + return QUOTA_NL_BSOFTBELOW; + if (toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bhardlimit && + toqb(dquot->dq_dqb.dqb_curspace - space) < + dquot->dq_dqb.dqb_bhardlimit) + return QUOTA_NL_BHARDBELOW; + return QUOTA_NL_NOWARN; +} /* * Initialize quota pointers in inode * Transaction must be started at entry @@ -1284,6 +1316,7 @@ warn_put_all: int dquot_free_space(struct inode *inode, qsize_t number) { unsigned int cnt; + char warntype[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ @@ -1292,6 +1325,7 @@ out_sub: inode_sub_bytes(inode, number); return QUOTA_OK; } + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ if (IS_NOQUOTA(inode)) { @@ -1302,6 +1336,7 @@ out_sub: for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; + warntype[cnt] = info_bdq_free(inode->i_dquot[cnt], number); dquot_decr_space(inode->i_dquot[cnt], number); } inode_sub_bytes(inode, number); @@ -1310,6 +1345,7 @@ out_sub: for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (inode->i_dquot[cnt]) mark_dquot_dirty(inode->i_dquot[cnt]); + flush_warnings(inode->i_dquot, warntype); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return QUOTA_OK; } @@ -1320,11 +1356,13 @@ out_sub: int dquot_free_inode(const struct inode *inode, unsigned long number) { unsigned int cnt; + char warntype[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ if (IS_NOQUOTA(inode)) return QUOTA_OK; + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ if (IS_NOQUOTA(inode)) { @@ -1335,6 +1373,7 @@ int dquot_free_inode(const struct inode *inode, unsigned long number) for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; + warntype[cnt] = info_idq_free(inode->i_dquot[cnt], number); dquot_decr_inodes(inode->i_dquot[cnt], number); } spin_unlock(&dq_data_lock); @@ -1342,6 +1381,7 @@ int dquot_free_inode(const struct inode *inode, unsigned long number) for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (inode->i_dquot[cnt]) mark_dquot_dirty(inode->i_dquot[cnt]); + flush_warnings(inode->i_dquot, warntype); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return QUOTA_OK; } @@ -1359,7 +1399,8 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) struct dquot *transfer_to[MAXQUOTAS]; int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid, chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid; - char warntype[MAXQUOTAS]; + char warntype_to[MAXQUOTAS]; + char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ @@ -1368,7 +1409,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) /* Clear the arrays */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { transfer_to[cnt] = transfer_from[cnt] = NODQUOT; - warntype[cnt] = QUOTA_NL_NOWARN; + warntype_to[cnt] = QUOTA_NL_NOWARN; } down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ @@ -1400,8 +1441,9 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) if (transfer_to[cnt] == NODQUOT) continue; transfer_from[cnt] = inode->i_dquot[cnt]; - if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA || - check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA) + if (check_idq(transfer_to[cnt], 1, warntype_to + cnt) == + NO_QUOTA || check_bdq(transfer_to[cnt], space, 0, + warntype_to + cnt) == NO_QUOTA) goto warn_put_all; } @@ -1417,6 +1459,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) /* Due to IO error we might not have transfer_from[] structure */ if (transfer_from[cnt]) { + warntype_from_inodes[cnt] = + info_idq_free(transfer_from[cnt], 1); + warntype_from_space[cnt] = + info_bdq_free(transfer_from[cnt], space); dquot_decr_inodes(transfer_from[cnt], 1); dquot_decr_space(transfer_from[cnt], space); } @@ -1436,7 +1482,9 @@ warn_put_all: if (transfer_to[cnt]) mark_dquot_dirty(transfer_to[cnt]); } - flush_warnings(transfer_to, warntype); + flush_warnings(transfer_to, warntype_to); + flush_warnings(transfer_from, warntype_from_inodes); + flush_warnings(transfer_from, warntype_from_space); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (ret == QUOTA_OK && transfer_from[cnt] != NODQUOT) diff --git a/include/linux/quota.h b/include/linux/quota.h index 4e004fef8134..376a05048bc5 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -135,6 +135,10 @@ struct if_dqinfo { #define QUOTA_NL_BHARDWARN 4 /* Block hardlimit reached */ #define QUOTA_NL_BSOFTLONGWARN 5 /* Block grace time expired */ #define QUOTA_NL_BSOFTWARN 6 /* Block softlimit reached */ +#define QUOTA_NL_IHARDBELOW 7 /* Usage got below inode hardlimit */ +#define QUOTA_NL_ISOFTBELOW 8 /* Usage got below inode softlimit */ +#define QUOTA_NL_BHARDBELOW 9 /* Usage got below block hardlimit */ +#define QUOTA_NL_BSOFTBELOW 10 /* Usage got below block softlimit */ enum { QUOTA_NL_C_UNSPEC, -- cgit v1.2.3 From f2992db2a4f7ae10f61d5bc68c7c1528cec639e2 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:46:55 -0700 Subject: Mark res_counter_charge(_locked) with __must_check Ignoring their return values may result in counter underflow in the future - when the value charged will be uncharged (or in "leaks" - when the value is not uncharged). This also prevents from using charging routines to decrement the counter value (i.e. uncharge it) ;) (Current code works OK with res_counter, however :) ) Signed-off-by: Pavel Emelyanov Cc: Balbir Singh Cc: Paul Menage Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 6d9e1fca098c..125660e7793f 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -95,8 +95,10 @@ void res_counter_init(struct res_counter *counter); * counter->limit _locked call expects the counter->lock to be taken */ -int res_counter_charge_locked(struct res_counter *counter, unsigned long val); -int res_counter_charge(struct res_counter *counter, unsigned long val); +int __must_check res_counter_charge_locked(struct res_counter *counter, + unsigned long val); +int __must_check res_counter_charge(struct res_counter *counter, + unsigned long val); /* * uncharge - tell that some portion of the resource is released -- cgit v1.2.3 From ce16b49d37e748574f7fabc2726268d542d0aa1a Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:46:57 -0700 Subject: cgroup files: clean up whitespace in struct cftype This patch removes some extraneous spaces from method declarations in struct cftype, to fit in with conventional kernel style. Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Cc: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index e155aa78d859..88a734edccbc 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -205,48 +205,48 @@ struct cftype { * subsystem, followed by a period */ char name[MAX_CFTYPE_NAME]; int private; - int (*open) (struct inode *inode, struct file *file); - ssize_t (*read) (struct cgroup *cgrp, struct cftype *cft, - struct file *file, - char __user *buf, size_t nbytes, loff_t *ppos); + int (*open)(struct inode *inode, struct file *file); + ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, + struct file *file, + char __user *buf, size_t nbytes, loff_t *ppos); /* * read_u64() is a shortcut for the common case of returning a * single integer. Use it in place of read() */ - u64 (*read_u64) (struct cgroup *cgrp, struct cftype *cft); + u64 (*read_u64)(struct cgroup *cgrp, struct cftype *cft); /* * read_s64() is a signed version of read_u64() */ - s64 (*read_s64) (struct cgroup *cgrp, struct cftype *cft); + s64 (*read_s64)(struct cgroup *cgrp, struct cftype *cft); /* * read_map() is used for defining a map of key/value * pairs. It should call cb->fill(cb, key, value) for each * entry. The key/value pairs (and their ordering) should not * change between reboots. */ - int (*read_map) (struct cgroup *cont, struct cftype *cft, - struct cgroup_map_cb *cb); + int (*read_map)(struct cgroup *cont, struct cftype *cft, + struct cgroup_map_cb *cb); /* * read_seq_string() is used for outputting a simple sequence * using seqfile. */ - int (*read_seq_string) (struct cgroup *cont, struct cftype *cft, - struct seq_file *m); + int (*read_seq_string)(struct cgroup *cont, struct cftype *cft, + struct seq_file *m); - ssize_t (*write) (struct cgroup *cgrp, struct cftype *cft, - struct file *file, - const char __user *buf, size_t nbytes, loff_t *ppos); + ssize_t (*write)(struct cgroup *cgrp, struct cftype *cft, + struct file *file, + const char __user *buf, size_t nbytes, loff_t *ppos); /* * write_u64() is a shortcut for the common case of accepting * a single integer (as parsed by simple_strtoull) from * userspace. Use in place of write(); return 0 or error. */ - int (*write_u64) (struct cgroup *cgrp, struct cftype *cft, u64 val); + int (*write_u64)(struct cgroup *cgrp, struct cftype *cft, u64 val); /* * write_s64() is a signed version of write_u64() */ - int (*write_s64) (struct cgroup *cgrp, struct cftype *cft, s64 val); + int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); /* * trigger() callback can be used to get some kick from the @@ -256,7 +256,7 @@ struct cftype { */ int (*trigger)(struct cgroup *cgrp, unsigned int event); - int (*release) (struct inode *inode, struct file *file); + int (*release)(struct inode *inode, struct file *file); }; struct cgroup_scanner { -- cgit v1.2.3 From db3b14978abc02041046ed8353f0899cb58ffffc Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:46:58 -0700 Subject: cgroup files: add write_string cgroup control file method This patch adds a write_string() method for cgroups control files. The semantics are that a buffer is copied from userspace to kernelspace and the handler function invoked on that buffer. The buffer is guaranteed to be nul-terminated, and no longer than max_write_len (defaulting to 64 bytes if unspecified). Later patches will convert existing raw file write handlers in control group subsystems to use this method. Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Acked-by: Balbir Singh Acked-by: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 14 ++++++++++++++ kernel/cgroup.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 88a734edccbc..f5379455bb59 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -205,6 +205,13 @@ struct cftype { * subsystem, followed by a period */ char name[MAX_CFTYPE_NAME]; int private; + + /* + * If non-zero, defines the maximum length of string that can + * be passed to write_string; defaults to 64 + */ + size_t max_write_len; + int (*open)(struct inode *inode, struct file *file); ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -248,6 +255,13 @@ struct cftype { */ int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); + /* + * write_string() is passed a nul-terminated kernelspace + * buffer of maximum length determined by max_write_len. + * Returns 0 or -ve error code. + */ + int (*write_string)(struct cgroup *cgrp, struct cftype *cft, + const char *buffer); /* * trigger() callback can be used to get some kick from the * userspace, when the actual string written is not important diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 70d083c6fb6b..3a99cc2df860 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1363,6 +1363,39 @@ static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, return retval; } +static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, + struct file *file, + const char __user *userbuf, + size_t nbytes, loff_t *unused_ppos) +{ + char local_buffer[64]; + int retval = 0; + size_t max_bytes = cft->max_write_len; + char *buffer = local_buffer; + + if (!max_bytes) + max_bytes = sizeof(local_buffer) - 1; + if (nbytes >= max_bytes) + return -E2BIG; + /* Allocate a dynamic buffer if we need one */ + if (nbytes >= sizeof(local_buffer)) { + buffer = kmalloc(nbytes + 1, GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + } + if (nbytes && copy_from_user(buffer, userbuf, nbytes)) + return -EFAULT; + + buffer[nbytes] = 0; /* nul-terminate */ + strstrip(buffer); + retval = cft->write_string(cgrp, cft, buffer); + if (!retval) + retval = nbytes; + if (buffer != local_buffer) + kfree(buffer); + return retval; +} + static ssize_t cgroup_common_file_write(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -1440,6 +1473,8 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *buf, return cft->write(cgrp, cft, file, buf, nbytes, ppos); if (cft->write_u64 || cft->write_s64) return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos); + if (cft->write_string) + return cgroup_write_string(cgrp, cft, file, buf, nbytes, ppos); if (cft->trigger) { int ret = cft->trigger(cgrp, (unsigned int)cft->private); return ret ? ret : nbytes; -- cgit v1.2.3 From e788e066c651b1bbf4a927dc95395c1aa13be436 Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:46:59 -0700 Subject: cgroup files: move the release_agent file to use typed handlers Adds cgroup_release_agent_write() and cgroup_release_agent_show() methods to handle writing/reading the path to a cgroup hierarchy's release agent. As a result, cgroup_common_file_read() is now unnecessary. As part of the change, a previously-tolerated race in cgroup_release_agent() is avoided by copying the current release_agent_path prior to calling call_usermode_helper(). Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Acked-by: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 2 + kernel/cgroup.c | 125 ++++++++++++++++++++++--------------------------- 2 files changed, 59 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f5379455bb59..e78377a91a74 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -295,6 +295,8 @@ int cgroup_add_files(struct cgroup *cgrp, int cgroup_is_removed(const struct cgroup *cgrp); +int cgroup_lock_live_group(struct cgroup *cgrp); + int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); int cgroup_task_count(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3a99cc2df860..0120b5d67a73 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -89,11 +89,7 @@ struct cgroupfs_root { /* Hierarchy-specific flags */ unsigned long flags; - /* The path to use for release notifications. No locking - * between setting and use - so if userspace updates this - * while child cgroups exist, you could miss a - * notification. We ensure that it's always a valid - * NUL-terminated string */ + /* The path to use for release notifications. */ char release_agent_path[PATH_MAX]; }; @@ -1329,6 +1325,45 @@ enum cgroup_filetype { FILE_RELEASE_AGENT, }; +/** + * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. + * @cgrp: the cgroup to be checked for liveness + * + * Returns true (with lock held) on success, or false (with no lock + * held) on failure. + */ +int cgroup_lock_live_group(struct cgroup *cgrp) +{ + mutex_lock(&cgroup_mutex); + if (cgroup_is_removed(cgrp)) { + mutex_unlock(&cgroup_mutex); + return false; + } + return true; +} + +static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, + const char *buffer) +{ + BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); + if (!cgroup_lock_live_group(cgrp)) + return -ENODEV; + strcpy(cgrp->root->release_agent_path, buffer); + mutex_unlock(&cgroup_mutex); + return 0; +} + +static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, + struct seq_file *seq) +{ + if (!cgroup_lock_live_group(cgrp)) + return -ENODEV; + seq_puts(seq, cgrp->root->release_agent_path); + seq_putc(seq, '\n'); + mutex_unlock(&cgroup_mutex); + return 0; +} + static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, struct file *file, const char __user *userbuf, @@ -1443,10 +1478,6 @@ static ssize_t cgroup_common_file_write(struct cgroup *cgrp, else clear_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); break; - case FILE_RELEASE_AGENT: - BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); - strcpy(cgrp->root->release_agent_path, buffer); - break; default: retval = -EINVAL; goto out2; @@ -1506,49 +1537,6 @@ static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, return simple_read_from_buffer(buf, nbytes, ppos, tmp, len); } -static ssize_t cgroup_common_file_read(struct cgroup *cgrp, - struct cftype *cft, - struct file *file, - char __user *buf, - size_t nbytes, loff_t *ppos) -{ - enum cgroup_filetype type = cft->private; - char *page; - ssize_t retval = 0; - char *s; - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) - return -ENOMEM; - - s = page; - - switch (type) { - case FILE_RELEASE_AGENT: - { - struct cgroupfs_root *root; - size_t n; - mutex_lock(&cgroup_mutex); - root = cgrp->root; - n = strnlen(root->release_agent_path, - sizeof(root->release_agent_path)); - n = min(n, (size_t) PAGE_SIZE); - strncpy(s, root->release_agent_path, n); - mutex_unlock(&cgroup_mutex); - s += n; - break; - } - default: - retval = -EINVAL; - goto out; - } - *s++ = '\n'; - - retval = simple_read_from_buffer(buf, nbytes, ppos, page, s - page); -out: - free_page((unsigned long)page); - return retval; -} - static ssize_t cgroup_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { @@ -1606,6 +1594,7 @@ int cgroup_seqfile_release(struct inode *inode, struct file *file) static struct file_operations cgroup_seqfile_operations = { .read = seq_read, + .write = cgroup_file_write, .llseek = seq_lseek, .release = cgroup_seqfile_release, }; @@ -2283,8 +2272,9 @@ static struct cftype files[] = { static struct cftype cft_release_agent = { .name = "release_agent", - .read = cgroup_common_file_read, - .write = cgroup_common_file_write, + .read_seq_string = cgroup_release_agent_show, + .write_string = cgroup_release_agent_write, + .max_write_len = PATH_MAX, .private = FILE_RELEASE_AGENT, }; @@ -3111,27 +3101,24 @@ static void cgroup_release_agent(struct work_struct *work) while (!list_empty(&release_list)) { char *argv[3], *envp[3]; int i; - char *pathbuf; + char *pathbuf = NULL, *agentbuf = NULL; struct cgroup *cgrp = list_entry(release_list.next, struct cgroup, release_list); list_del_init(&cgrp->release_list); spin_unlock(&release_list_lock); pathbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!pathbuf) { - spin_lock(&release_list_lock); - continue; - } - - if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0) { - kfree(pathbuf); - spin_lock(&release_list_lock); - continue; - } + if (!pathbuf) + goto continue_free; + if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0) + goto continue_free; + agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL); + if (!agentbuf) + goto continue_free; i = 0; - argv[i++] = cgrp->root->release_agent_path; - argv[i++] = (char *)pathbuf; + argv[i++] = agentbuf; + argv[i++] = pathbuf; argv[i] = NULL; i = 0; @@ -3145,8 +3132,10 @@ static void cgroup_release_agent(struct work_struct *work) * be a slow process */ mutex_unlock(&cgroup_mutex); call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); - kfree(pathbuf); mutex_lock(&cgroup_mutex); + continue_free: + kfree(pathbuf); + kfree(agentbuf); spin_lock(&release_list_lock); } spin_unlock(&release_list_lock); -- cgit v1.2.3 From 84eea842886ac35020be6043e04748ed22014359 Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:47:00 -0700 Subject: cgroups: misc cleanups to write_string patchset This patch contains cleanups suggested by reviewers for the recent write_string() patchset: - pair cgroup_lock_live_group() with cgroup_unlock() in cgroup.c for clarity, rather than directly unlocking cgroup_mutex. - make the return type of cgroup_lock_live_group() a bool - use a #define'd constant for the local buffer size in read/write functions Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Acked-by: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 4 ++-- kernel/cgroup.c | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index e78377a91a74..cc59d3a21d87 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -21,11 +21,13 @@ struct cgroupfs_root; struct cgroup_subsys; struct inode; +struct cgroup; extern int cgroup_init_early(void); extern int cgroup_init(void); extern void cgroup_init_smp(void); extern void cgroup_lock(void); +extern bool cgroup_lock_live_group(struct cgroup *cgrp); extern void cgroup_unlock(void); extern void cgroup_fork(struct task_struct *p); extern void cgroup_fork_callbacks(struct task_struct *p); @@ -295,8 +297,6 @@ int cgroup_add_files(struct cgroup *cgrp, int cgroup_is_removed(const struct cgroup *cgrp); -int cgroup_lock_live_group(struct cgroup *cgrp); - int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); int cgroup_task_count(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0120b5d67a73..a14122ecaa5e 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1329,10 +1329,10 @@ enum cgroup_filetype { * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. * @cgrp: the cgroup to be checked for liveness * - * Returns true (with lock held) on success, or false (with no lock - * held) on failure. + * On success, returns true; the lock should be later released with + * cgroup_unlock(). On failure returns false with no lock held. */ -int cgroup_lock_live_group(struct cgroup *cgrp) +bool cgroup_lock_live_group(struct cgroup *cgrp) { mutex_lock(&cgroup_mutex); if (cgroup_is_removed(cgrp)) { @@ -1349,7 +1349,7 @@ static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, if (!cgroup_lock_live_group(cgrp)) return -ENODEV; strcpy(cgrp->root->release_agent_path, buffer); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return 0; } @@ -1360,16 +1360,19 @@ static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, return -ENODEV; seq_puts(seq, cgrp->root->release_agent_path); seq_putc(seq, '\n'); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return 0; } +/* A buffer size big enough for numbers or short strings */ +#define CGROUP_LOCAL_BUFFER_SIZE 64 + static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, struct file *file, const char __user *userbuf, size_t nbytes, loff_t *unused_ppos) { - char buffer[64]; + char buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; char *end; @@ -1403,7 +1406,7 @@ static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, const char __user *userbuf, size_t nbytes, loff_t *unused_ppos) { - char local_buffer[64]; + char local_buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; size_t max_bytes = cft->max_write_len; char *buffer = local_buffer; @@ -1518,7 +1521,7 @@ static ssize_t cgroup_read_u64(struct cgroup *cgrp, struct cftype *cft, char __user *buf, size_t nbytes, loff_t *ppos) { - char tmp[64]; + char tmp[CGROUP_LOCAL_BUFFER_SIZE]; u64 val = cft->read_u64(cgrp, cft); int len = sprintf(tmp, "%llu\n", (unsigned long long) val); @@ -1530,7 +1533,7 @@ static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, char __user *buf, size_t nbytes, loff_t *ppos) { - char tmp[64]; + char tmp[CGROUP_LOCAL_BUFFER_SIZE]; s64 val = cft->read_s64(cgrp, cft); int len = sprintf(tmp, "%lld\n", (long long) val); -- cgit v1.2.3 From 856c13aa1ff6136c1968414fdea5938ea9d5ebf2 Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:47:04 -0700 Subject: cgroup files: convert res_counter_write() to be a cgroups write_string() handler Currently res_counter_write() is a raw file handler even though it's ultimately taking a number, since in some cases it wants to pre-process the string when converting it to a number. This patch converts res_counter_write() from a raw file handler to a write_string() handler; this allows some of the boilerplate copying/locking/checking to be removed, and simplies the cleanup path, since these functions are now performed by the cgroups framework. [lizf@cn.fujitsu.com: build fix] Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Cc: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Li Zefan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 11 ++++++++--- kernel/res_counter.c | 48 ++++++++++++++++++++------------------------- mm/memcontrol.c | 24 +++++------------------ 3 files changed, 34 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 125660e7793f..290205dfe094 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -63,9 +63,14 @@ u64 res_counter_read_u64(struct res_counter *counter, int member); ssize_t res_counter_read(struct res_counter *counter, int member, const char __user *buf, size_t nbytes, loff_t *pos, int (*read_strategy)(unsigned long long val, char *s)); -ssize_t res_counter_write(struct res_counter *counter, int member, - const char __user *buf, size_t nbytes, loff_t *pos, - int (*write_strategy)(char *buf, unsigned long long *val)); + +typedef int (*write_strategy_fn)(const char *buf, unsigned long long *val); + +int res_counter_memparse_write_strategy(const char *buf, + unsigned long long *res); + +int res_counter_write(struct res_counter *counter, int member, + const char *buffer, write_strategy_fn write_strategy); /* * the field descriptors. one for each member of res_counter diff --git a/kernel/res_counter.c b/kernel/res_counter.c index d3c61b4ebef2..f275c8eca772 100644 --- a/kernel/res_counter.c +++ b/kernel/res_counter.c @@ -13,6 +13,7 @@ #include #include #include +#include void res_counter_init(struct res_counter *counter) { @@ -102,44 +103,37 @@ u64 res_counter_read_u64(struct res_counter *counter, int member) return *res_counter_member(counter, member); } -ssize_t res_counter_write(struct res_counter *counter, int member, - const char __user *userbuf, size_t nbytes, loff_t *pos, - int (*write_strategy)(char *st_buf, unsigned long long *val)) +int res_counter_memparse_write_strategy(const char *buf, + unsigned long long *res) { - int ret; - char *buf, *end; - unsigned long flags; - unsigned long long tmp, *val; - - buf = kmalloc(nbytes + 1, GFP_KERNEL); - ret = -ENOMEM; - if (buf == NULL) - goto out; + char *end; + /* FIXME - make memparse() take const char* args */ + *res = memparse((char *)buf, &end); + if (*end != '\0') + return -EINVAL; - buf[nbytes] = '\0'; - ret = -EFAULT; - if (copy_from_user(buf, userbuf, nbytes)) - goto out_free; + *res = PAGE_ALIGN(*res); + return 0; +} - ret = -EINVAL; +int res_counter_write(struct res_counter *counter, int member, + const char *buf, write_strategy_fn write_strategy) +{ + char *end; + unsigned long flags; + unsigned long long tmp, *val; - strstrip(buf); if (write_strategy) { - if (write_strategy(buf, &tmp)) { - goto out_free; - } + if (write_strategy(buf, &tmp)) + return -EINVAL; } else { tmp = simple_strtoull(buf, &end, 10); if (*end != '\0') - goto out_free; + return -EINVAL; } spin_lock_irqsave(&counter->lock, flags); val = res_counter_member(counter, member); *val = tmp; spin_unlock_irqrestore(&counter->lock, flags); - ret = nbytes; -out_free: - kfree(buf); -out: - return ret; + return 0; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e46451e1d9b7..7385d58fb061 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -838,32 +838,18 @@ out: return ret; } -static int mem_cgroup_write_strategy(char *buf, unsigned long long *tmp) -{ - *tmp = memparse(buf, &buf); - if (*buf != '\0') - return -EINVAL; - - /* - * Round up the value to the closest page size - */ - *tmp = ((*tmp + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; - return 0; -} - static u64 mem_cgroup_read(struct cgroup *cont, struct cftype *cft) { return res_counter_read_u64(&mem_cgroup_from_cont(cont)->res, cft->private); } -static ssize_t mem_cgroup_write(struct cgroup *cont, struct cftype *cft, - struct file *file, const char __user *userbuf, - size_t nbytes, loff_t *ppos) +static int mem_cgroup_write(struct cgroup *cont, struct cftype *cft, + const char *buffer) { return res_counter_write(&mem_cgroup_from_cont(cont)->res, - cft->private, userbuf, nbytes, ppos, - mem_cgroup_write_strategy); + cft->private, buffer, + res_counter_memparse_write_strategy); } static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) @@ -940,7 +926,7 @@ static struct cftype mem_cgroup_files[] = { { .name = "limit_in_bytes", .private = RES_LIMIT, - .write = mem_cgroup_write, + .write_string = mem_cgroup_write, .read_u64 = mem_cgroup_read, }, { -- cgit v1.2.3 From e885dcde75685e09f23cffae1f6d5169c105b8a0 Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Fri, 25 Jul 2008 01:47:06 -0700 Subject: cgroup_clone: use pid of newly created task for new cgroup cgroup_clone creates a new cgroup with the pid of the task. This works correctly for unshare, but for clone cgroup_clone is called from copy_namespaces inside copy_process, which happens before the new pid is created. As a result, the new cgroup was created with current's pid. This patch: 1. Moves the call inside copy_process to after the new pid is created 2. Passes the struct pid into ns_cgroup_clone (as it is not yet attached to the task) 3. Passes a name from ns_cgroup_clone() into cgroup_clone() so as to keep cgroup_clone() itself simpler 4. Uses pid_vnr() to get the process id value, so that the pid used to name the new cgroup is always the pid as it would be known to the task which did the cloning or unsharing. I think that is the most intuitive thing to do. This way, task t1 does clone(CLONE_NEWPID) to get t2, which does clone(CLONE_NEWPID) to get t3, then the cgroup for t3 will be named for the pid by which t2 knows t3. (Thanks to Dan Smith for finding the main bug) Changelog: June 11: Incorporate Paul Menage's feedback: don't pass NULL to ns_cgroup_clone from unshare, and reduce patch size by using 'nodename' in cgroup_clone. June 10: Original version [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Serge Hallyn Acked-by: Paul Menage Tested-by: Dan Smith Cc: Balbir Singh Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 3 ++- include/linux/nsproxy.h | 7 +++++-- kernel/cgroup.c | 7 +++---- kernel/fork.c | 6 ++++++ kernel/ns_cgroup.c | 8 ++++++-- kernel/nsproxy.c | 8 +------- 6 files changed, 23 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index cc59d3a21d87..c98dd7cb7076 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -364,7 +364,8 @@ static inline struct cgroup* task_cgroup(struct task_struct *task, return task_subsys_state(task, subsys_id)->cgroup; } -int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *ss); +int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *ss, + char *nodename); /* A cgroup_iter should be treated as an opaque object */ struct cgroup_iter { diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index 0e66b57631fc..c8a768e59640 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h @@ -82,9 +82,12 @@ static inline void get_nsproxy(struct nsproxy *ns) } #ifdef CONFIG_CGROUP_NS -int ns_cgroup_clone(struct task_struct *tsk); +int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid); #else -static inline int ns_cgroup_clone(struct task_struct *tsk) { return 0; } +static inline int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid) +{ + return 0; +} #endif #endif diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 86b71e714e13..66ec9fd21e0c 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2848,16 +2848,17 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) * cgroup_clone - clone the cgroup the given subsystem is attached to * @tsk: the task to be moved * @subsys: the given subsystem + * @nodename: the name for the new cgroup * * Duplicate the current cgroup in the hierarchy that the given * subsystem is attached to, and move this task into the new * child. */ -int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys) +int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys, + char *nodename) { struct dentry *dentry; int ret = 0; - char nodename[MAX_CGROUP_TYPE_NAMELEN]; struct cgroup *parent, *child; struct inode *inode; struct css_set *cg; @@ -2882,8 +2883,6 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys) cg = tsk->cgroups; parent = task_cgroup(tsk, subsys->subsys_id); - snprintf(nodename, MAX_CGROUP_TYPE_NAMELEN, "%d", tsk->pid); - /* Pin the hierarchy */ atomic_inc(&parent->root->sb->s_active); diff --git a/kernel/fork.c b/kernel/fork.c index 5a5d6fef341d..228f80c9155a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1107,6 +1107,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (clone_flags & CLONE_THREAD) p->tgid = current->tgid; + if (current->nsproxy != p->nsproxy) { + retval = ns_cgroup_clone(p, pid); + if (retval) + goto bad_fork_free_pid; + } + p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; /* * Clear TID on mm_release()? diff --git a/kernel/ns_cgroup.c b/kernel/ns_cgroup.c index 48d7ed6fc3a4..43c2111cd54d 100644 --- a/kernel/ns_cgroup.c +++ b/kernel/ns_cgroup.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -24,9 +25,12 @@ static inline struct ns_cgroup *cgroup_to_ns( struct ns_cgroup, css); } -int ns_cgroup_clone(struct task_struct *task) +int ns_cgroup_clone(struct task_struct *task, struct pid *pid) { - return cgroup_clone(task, &ns_subsys); + char name[PROC_NUMBUF]; + + snprintf(name, PROC_NUMBUF, "%d", pid_vnr(pid)); + return cgroup_clone(task, &ns_subsys, name); } /* diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index adc785146a1c..21575fc46d05 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -157,12 +157,6 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) goto out; } - err = ns_cgroup_clone(tsk); - if (err) { - put_nsproxy(new_ns); - goto out; - } - tsk->nsproxy = new_ns; out: @@ -209,7 +203,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags, goto out; } - err = ns_cgroup_clone(current); + err = ns_cgroup_clone(current, task_pid(current)); if (err) put_nsproxy(*new_nsp); -- cgit v1.2.3 From e8589cc189f96b87348ae83ea4db38eaac624135 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:10 -0700 Subject: memcg: better migration handling This patch changes page migration under memory controller to use a different algorithm. (thanks to Christoph for new idea.) Before: - page_cgroup is migrated from an old page to a new page. After: - a new page is accounted , no reuse of page_cgroup. Pros: - We can avoid compliated lock depndencies and races in migration. Cons: - new param to mem_cgroup_charge_common(). - mem_cgroup_getref() is added for handling ref_cnt ping-pong. This version simplifies complicated lock dependency in page migraiton under memory resource controller. new refcnt sequence is following. a mapped page: prepage_migration() ..... +1 to NEW page try_to_unmap() ..... all refs to OLD page is gone. move_pages() ..... +1 to NEW page if page cache. remap... ..... all refs from *map* is added to NEW one. end_migration() ..... -1 to New page. page's mapcount + (page_is_cache) refs are added to NEW one. Signed-off-by: KAMEZAWA Hiroyuki Cc: Balbir Singh Cc: Pavel Emelyanov Cc: Li Zefan Cc: YAMAMOTO Takashi Cc: Hugh Dickins Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 11 ++-- mm/memcontrol.c | 128 +++++++++++++++++++++++---------------------- mm/migrate.c | 22 +++++--- 3 files changed, 86 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index e6608776bc96..84ead2aa6f18 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -50,9 +50,10 @@ extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); #define mm_match_cgroup(mm, cgroup) \ ((cgroup) == mem_cgroup_from_task((mm)->owner)) -extern int mem_cgroup_prepare_migration(struct page *page); +extern int +mem_cgroup_prepare_migration(struct page *page, struct page *newpage); extern void mem_cgroup_end_migration(struct page *page); -extern void mem_cgroup_page_migration(struct page *page, struct page *newpage); +extern int mem_cgroup_getref(struct page *page); /* * For memory reclaim. @@ -112,7 +113,8 @@ static inline int task_in_mem_cgroup(struct task_struct *task, return 1; } -static inline int mem_cgroup_prepare_migration(struct page *page) +static inline int +mem_cgroup_prepare_migration(struct page *page, struct page *newpage) { return 0; } @@ -121,8 +123,7 @@ static inline void mem_cgroup_end_migration(struct page *page) { } -static inline void -mem_cgroup_page_migration(struct page *page, struct page *newpage) +static inline void mem_cgroup_getref(struct page *page) { } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 90ccc1326356..da5912b84551 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -524,7 +524,8 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, * < 0 if the cgroup is over its limit */ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, - gfp_t gfp_mask, enum charge_type ctype) + gfp_t gfp_mask, enum charge_type ctype, + struct mem_cgroup *memcg) { struct mem_cgroup *mem; struct page_cgroup *pc; @@ -569,16 +570,21 @@ retry: * thread group leader migrates. It's possible that mm is not * set, if so charge the init_mm (happens for pagecache usage). */ - if (!mm) - mm = &init_mm; + if (!memcg) { + if (!mm) + mm = &init_mm; - rcu_read_lock(); - mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); - /* - * For every charge from the cgroup, increment reference count - */ - css_get(&mem->css); - rcu_read_unlock(); + rcu_read_lock(); + mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); + /* + * For every charge from the cgroup, increment reference count + */ + css_get(&mem->css); + rcu_read_unlock(); + } else { + mem = memcg; + css_get(&memcg->css); + } while (res_counter_charge(&mem->res, PAGE_SIZE)) { if (!(gfp_mask & __GFP_WAIT)) @@ -648,7 +654,7 @@ err: int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) { return mem_cgroup_charge_common(page, mm, gfp_mask, - MEM_CGROUP_CHARGE_TYPE_MAPPED); + MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); } int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, @@ -657,7 +663,22 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, if (!mm) mm = &init_mm; return mem_cgroup_charge_common(page, mm, gfp_mask, - MEM_CGROUP_CHARGE_TYPE_CACHE); + MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); +} + +int mem_cgroup_getref(struct page *page) +{ + struct page_cgroup *pc; + + if (mem_cgroup_subsys.disabled) + return 0; + + lock_page_cgroup(page); + pc = page_get_page_cgroup(page); + VM_BUG_ON(!pc); + pc->ref_cnt++; + unlock_page_cgroup(page); + return 0; } /* @@ -707,65 +728,39 @@ unlock: } /* - * Returns non-zero if a page (under migration) has valid page_cgroup member. - * Refcnt of page_cgroup is incremented. + * Before starting migration, account against new page. */ -int mem_cgroup_prepare_migration(struct page *page) +int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) { struct page_cgroup *pc; + struct mem_cgroup *mem = NULL; + enum charge_type ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED; + int ret = 0; if (mem_cgroup_subsys.disabled) return 0; lock_page_cgroup(page); pc = page_get_page_cgroup(page); - if (pc) - pc->ref_cnt++; + if (pc) { + mem = pc->mem_cgroup; + css_get(&mem->css); + if (pc->flags & PAGE_CGROUP_FLAG_CACHE) + ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; + } unlock_page_cgroup(page); - return pc != NULL; -} - -void mem_cgroup_end_migration(struct page *page) -{ - mem_cgroup_uncharge_page(page); + if (mem) { + ret = mem_cgroup_charge_common(newpage, NULL, GFP_KERNEL, + ctype, mem); + css_put(&mem->css); + } + return ret; } -/* - * We know both *page* and *newpage* are now not-on-LRU and PG_locked. - * And no race with uncharge() routines because page_cgroup for *page* - * has extra one reference by mem_cgroup_prepare_migration. - */ -void mem_cgroup_page_migration(struct page *page, struct page *newpage) +/* remove redundant charge */ +void mem_cgroup_end_migration(struct page *newpage) { - struct page_cgroup *pc; - struct mem_cgroup_per_zone *mz; - unsigned long flags; - - lock_page_cgroup(page); - pc = page_get_page_cgroup(page); - if (!pc) { - unlock_page_cgroup(page); - return; - } - - mz = page_cgroup_zoneinfo(pc); - spin_lock_irqsave(&mz->lru_lock, flags); - __mem_cgroup_remove_list(mz, pc); - spin_unlock_irqrestore(&mz->lru_lock, flags); - - page_assign_page_cgroup(page, NULL); - unlock_page_cgroup(page); - - pc->page = newpage; - lock_page_cgroup(newpage); - page_assign_page_cgroup(newpage, pc); - - mz = page_cgroup_zoneinfo(pc); - spin_lock_irqsave(&mz->lru_lock, flags); - __mem_cgroup_add_list(mz, pc); - spin_unlock_irqrestore(&mz->lru_lock, flags); - - unlock_page_cgroup(newpage); + mem_cgroup_uncharge_page(newpage); } /* @@ -795,12 +790,19 @@ static void mem_cgroup_force_empty_list(struct mem_cgroup *mem, page = pc->page; get_page(page); spin_unlock_irqrestore(&mz->lru_lock, flags); - mem_cgroup_uncharge_page(page); - put_page(page); - if (--count <= 0) { - count = FORCE_UNCHARGE_BATCH; + /* + * Check if this page is on LRU. !LRU page can be found + * if it's under page migration. + */ + if (PageLRU(page)) { + mem_cgroup_uncharge_page(page); + put_page(page); + if (--count <= 0) { + count = FORCE_UNCHARGE_BATCH; + cond_resched(); + } + } else cond_resched(); - } spin_lock_irqsave(&mz->lru_lock, flags); } spin_unlock_irqrestore(&mz->lru_lock, flags); diff --git a/mm/migrate.c b/mm/migrate.c index 376cceba82f9..f6d7f8efd1a8 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -358,6 +358,10 @@ static int migrate_page_move_mapping(struct address_space *mapping, __inc_zone_page_state(newpage, NR_FILE_PAGES); write_unlock_irq(&mapping->tree_lock); + if (!PageSwapCache(newpage)) { + mem_cgroup_uncharge_page(page); + mem_cgroup_getref(newpage); + } return 0; } @@ -611,7 +615,6 @@ static int move_to_new_page(struct page *newpage, struct page *page) rc = fallback_migrate_page(mapping, newpage, page); if (!rc) { - mem_cgroup_page_migration(page, newpage); remove_migration_ptes(page, newpage); } else newpage->mapping = NULL; @@ -641,6 +644,14 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, /* page was freed from under us. So we are done. */ goto move_newpage; + charge = mem_cgroup_prepare_migration(page, newpage); + if (charge == -ENOMEM) { + rc = -ENOMEM; + goto move_newpage; + } + /* prepare cgroup just returns 0 or -ENOMEM */ + BUG_ON(charge); + rc = -EAGAIN; if (TestSetPageLocked(page)) { if (!force) @@ -692,19 +703,14 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, goto rcu_unlock; } - charge = mem_cgroup_prepare_migration(page); /* Establish migration ptes or remove ptes */ try_to_unmap(page, 1); if (!page_mapped(page)) rc = move_to_new_page(newpage, page); - if (rc) { + if (rc) remove_migration_ptes(page, page); - if (charge) - mem_cgroup_end_migration(page); - } else if (charge) - mem_cgroup_end_migration(newpage); rcu_unlock: if (rcu_locked) rcu_read_unlock(); @@ -725,6 +731,8 @@ unlock: } move_newpage: + if (!charge) + mem_cgroup_end_migration(newpage); /* * Move the new page to the LRU. If migration was not successful * then this will free the page. -- cgit v1.2.3 From 69029cd550284e32de13d6dd2f77b723c8a0e444 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:14 -0700 Subject: memcg: remove refcnt from page_cgroup memcg: performance improvements Patch Description 1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed) 2/5 ... swapcache handling patch 3/5 ... add helper function for shmem's memory reclaim patch 4/5 ... optimize by likely/unlikely ppatch 5/5 ... remove redundunt check patch (shmem handling is fixed.) Unix bench result. == 2.6.26-rc2-mm1 + memory resource controller Execl Throughput 2915.4 lps (29.6 secs, 3 samples) C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples) Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples) Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples) Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples) File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples) File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples) File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples) File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples) File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples) File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples) File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples) File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples) File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples) Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples) INDEX VALUES TEST BASELINE RESULT INDEX Execl Throughput 43.0 2915.4 678.0 File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0 File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5 File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8 Shell Scripts (8 concurrent) 6.0 1097.7 1829.5 ========= FINAL SCORE 991.3 == 2.6.26-rc2-mm1 + this set == Execl Throughput 3012.9 lps (29.9 secs, 3 samples) C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples) Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples) Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples) Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples) File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples) File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples) File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples) File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples) File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples) File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples) File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples) File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples) File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples) Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples) INDEX VALUES TEST BASELINE RESULT INDEX Execl Throughput 43.0 3012.9 700.7 File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7 File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3 File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0 Shell Scripts (8 concurrent) 6.0 1120.3 1867.2 ========= FINAL SCORE 1004.6 This patch: Remove refcnt from page_cgroup(). After this, * A page is charged only when !page_mapped() && no page_cgroup is assigned. * Anon page is newly mapped. * File page is added to mapping->tree. * A page is uncharged only when * Anon page is fully unmapped. * File page is removed from LRU. There is no change in behavior from user's view. This patch also removes unnecessary calls in rmap.c which was used only for refcnt mangement. [akpm@linux-foundation.org: fix warning] [hugh@veritas.com: fix shmem_unuse_inode charging] Signed-off-by: KAMEZAWA Hiroyuki Cc: Balbir Singh Cc: "Eric W. Biederman" Cc: Pavel Emelyanov Cc: Li Zefan Cc: Hugh Dickins Cc: YAMAMOTO Takashi Cc: Paul Menage Cc: David Rientjes Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 10 ++--- mm/filemap.c | 6 +-- mm/memcontrol.c | 109 ++++++++++++++++++++++++++------------------- mm/migrate.c | 3 +- mm/rmap.c | 14 +----- mm/shmem.c | 35 ++++++++++----- 6 files changed, 97 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 84ead2aa6f18..b4980b8f048e 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -35,6 +35,7 @@ extern int mem_cgroup_charge(struct page *page, struct mm_struct *mm, extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask); extern void mem_cgroup_uncharge_page(struct page *page); +extern void mem_cgroup_uncharge_cache_page(struct page *page); extern void mem_cgroup_move_lists(struct page *page, bool active); extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, struct list_head *dst, @@ -53,7 +54,6 @@ extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); extern int mem_cgroup_prepare_migration(struct page *page, struct page *newpage); extern void mem_cgroup_end_migration(struct page *page); -extern int mem_cgroup_getref(struct page *page); /* * For memory reclaim. @@ -98,6 +98,10 @@ static inline void mem_cgroup_uncharge_page(struct page *page) { } +static inline void mem_cgroup_uncharge_cache_page(struct page *page) +{ +} + static inline void mem_cgroup_move_lists(struct page *page, bool active) { } @@ -123,10 +127,6 @@ static inline void mem_cgroup_end_migration(struct page *page) { } -static inline void mem_cgroup_getref(struct page *page) -{ -} - static inline int mem_cgroup_calc_mapped_ratio(struct mem_cgroup *mem) { return 0; diff --git a/mm/filemap.c b/mm/filemap.c index 5d4c880d7cd9..2d3ec1ffc66e 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -115,7 +115,7 @@ void __remove_from_page_cache(struct page *page) { struct address_space *mapping = page->mapping; - mem_cgroup_uncharge_page(page); + mem_cgroup_uncharge_cache_page(page); radix_tree_delete(&mapping->page_tree, page->index); page->mapping = NULL; mapping->nrpages--; @@ -474,12 +474,12 @@ int add_to_page_cache(struct page *page, struct address_space *mapping, mapping->nrpages++; __inc_zone_page_state(page, NR_FILE_PAGES); } else - mem_cgroup_uncharge_page(page); + mem_cgroup_uncharge_cache_page(page); write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } else - mem_cgroup_uncharge_page(page); + mem_cgroup_uncharge_cache_page(page); out: return error; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index da5912b84551..a61706193c31 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -166,7 +166,6 @@ struct page_cgroup { struct list_head lru; /* per cgroup LRU list */ struct page *page; struct mem_cgroup *mem_cgroup; - int ref_cnt; /* cached, mapped, migrating */ int flags; }; #define PAGE_CGROUP_FLAG_CACHE (0x1) /* charged as cache */ @@ -185,6 +184,7 @@ static enum zone_type page_cgroup_zid(struct page_cgroup *pc) enum charge_type { MEM_CGROUP_CHARGE_TYPE_CACHE = 0, MEM_CGROUP_CHARGE_TYPE_MAPPED, + MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ }; /* @@ -552,9 +552,7 @@ retry: */ if (pc) { VM_BUG_ON(pc->page != page); - VM_BUG_ON(pc->ref_cnt <= 0); - - pc->ref_cnt++; + VM_BUG_ON(!pc->mem_cgroup); unlock_page_cgroup(page); goto done; } @@ -570,10 +568,7 @@ retry: * thread group leader migrates. It's possible that mm is not * set, if so charge the init_mm (happens for pagecache usage). */ - if (!memcg) { - if (!mm) - mm = &init_mm; - + if (likely(!memcg)) { rcu_read_lock(); mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); /* @@ -609,7 +604,6 @@ retry: } } - pc->ref_cnt = 1; pc->mem_cgroup = mem; pc->page = page; /* @@ -653,6 +647,17 @@ err: int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) { + /* + * If already mapped, we don't have to account. + * If page cache, page->mapping has address_space. + * But page->mapping may have out-of-use anon_vma pointer, + * detecit it by PageAnon() check. newly-mapped-anon's page->mapping + * is NULL. + */ + if (page_mapped(page) || (page->mapping && !PageAnon(page))) + return 0; + if (unlikely(!mm)) + mm = &init_mm; return mem_cgroup_charge_common(page, mm, gfp_mask, MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); } @@ -660,32 +665,17 @@ int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) { - if (!mm) + if (unlikely(!mm)) mm = &init_mm; return mem_cgroup_charge_common(page, mm, gfp_mask, MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); } -int mem_cgroup_getref(struct page *page) -{ - struct page_cgroup *pc; - - if (mem_cgroup_subsys.disabled) - return 0; - - lock_page_cgroup(page); - pc = page_get_page_cgroup(page); - VM_BUG_ON(!pc); - pc->ref_cnt++; - unlock_page_cgroup(page); - return 0; -} - /* - * Uncharging is always a welcome operation, we never complain, simply - * uncharge. + * uncharge if !page_mapped(page) */ -void mem_cgroup_uncharge_page(struct page *page) +static void +__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) { struct page_cgroup *pc; struct mem_cgroup *mem; @@ -704,29 +694,41 @@ void mem_cgroup_uncharge_page(struct page *page) goto unlock; VM_BUG_ON(pc->page != page); - VM_BUG_ON(pc->ref_cnt <= 0); - if (--(pc->ref_cnt) == 0) { - mz = page_cgroup_zoneinfo(pc); - spin_lock_irqsave(&mz->lru_lock, flags); - __mem_cgroup_remove_list(mz, pc); - spin_unlock_irqrestore(&mz->lru_lock, flags); + if ((ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED) + && ((pc->flags & PAGE_CGROUP_FLAG_CACHE) + || page_mapped(page))) + goto unlock; - page_assign_page_cgroup(page, NULL); - unlock_page_cgroup(page); + mz = page_cgroup_zoneinfo(pc); + spin_lock_irqsave(&mz->lru_lock, flags); + __mem_cgroup_remove_list(mz, pc); + spin_unlock_irqrestore(&mz->lru_lock, flags); - mem = pc->mem_cgroup; - res_counter_uncharge(&mem->res, PAGE_SIZE); - css_put(&mem->css); + page_assign_page_cgroup(page, NULL); + unlock_page_cgroup(page); - kmem_cache_free(page_cgroup_cache, pc); - return; - } + mem = pc->mem_cgroup; + res_counter_uncharge(&mem->res, PAGE_SIZE); + css_put(&mem->css); + kmem_cache_free(page_cgroup_cache, pc); + return; unlock: unlock_page_cgroup(page); } +void mem_cgroup_uncharge_page(struct page *page) +{ + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_MAPPED); +} + +void mem_cgroup_uncharge_cache_page(struct page *page) +{ + VM_BUG_ON(page_mapped(page)); + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE); +} + /* * Before starting migration, account against new page. */ @@ -757,15 +759,29 @@ int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) return ret; } -/* remove redundant charge */ +/* remove redundant charge if migration failed*/ void mem_cgroup_end_migration(struct page *newpage) { - mem_cgroup_uncharge_page(newpage); + /* + * At success, page->mapping is not NULL. + * special rollback care is necessary when + * 1. at migration failure. (newpage->mapping is cleared in this case) + * 2. the newpage was moved but not remapped again because the task + * exits and the newpage is obsolete. In this case, the new page + * may be a swapcache. So, we just call mem_cgroup_uncharge_page() + * always for avoiding mess. The page_cgroup will be removed if + * unnecessary. File cache pages is still on radix-tree. Don't + * care it. + */ + if (!newpage->mapping) + __mem_cgroup_uncharge_common(newpage, + MEM_CGROUP_CHARGE_TYPE_FORCE); + else if (PageAnon(newpage)) + mem_cgroup_uncharge_page(newpage); } /* * This routine traverse page_cgroup in given list and drop them all. - * This routine ignores page_cgroup->ref_cnt. * *And* this routine doesn't reclaim page itself, just removes page_cgroup. */ #define FORCE_UNCHARGE_BATCH (128) @@ -795,7 +811,8 @@ static void mem_cgroup_force_empty_list(struct mem_cgroup *mem, * if it's under page migration. */ if (PageLRU(page)) { - mem_cgroup_uncharge_page(page); + __mem_cgroup_uncharge_common(page, + MEM_CGROUP_CHARGE_TYPE_FORCE); put_page(page); if (--count <= 0) { count = FORCE_UNCHARGE_BATCH; diff --git a/mm/migrate.c b/mm/migrate.c index f6d7f8efd1a8..d8c65a65c61d 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -359,8 +359,7 @@ static int migrate_page_move_mapping(struct address_space *mapping, write_unlock_irq(&mapping->tree_lock); if (!PageSwapCache(newpage)) { - mem_cgroup_uncharge_page(page); - mem_cgroup_getref(newpage); + mem_cgroup_uncharge_cache_page(page); } return 0; diff --git a/mm/rmap.c b/mm/rmap.c index bf0a5b7cfb8e..abbd29f7c43f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -576,14 +576,8 @@ void page_add_anon_rmap(struct page *page, VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end); if (atomic_inc_and_test(&page->_mapcount)) __page_set_anon_rmap(page, vma, address); - else { + else __page_check_anon_rmap(page, vma, address); - /* - * We unconditionally charged during prepare, we uncharge here - * This takes care of balancing the reference counts - */ - mem_cgroup_uncharge_page(page); - } } /** @@ -614,12 +608,6 @@ void page_add_file_rmap(struct page *page) { if (atomic_inc_and_test(&page->_mapcount)) __inc_zone_page_state(page, NR_FILE_MAPPED); - else - /* - * We unconditionally charged during prepare, we uncharge here - * This takes care of balancing the reference counts - */ - mem_cgroup_uncharge_page(page); } #ifdef CONFIG_DEBUG_VM diff --git a/mm/shmem.c b/mm/shmem.c index 9ffbea9b79e1..d58305e8a484 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -922,20 +922,26 @@ found: error = 1; if (!inode) goto out; - /* Precharge page while we can wait, compensate afterwards */ + /* Precharge page using GFP_KERNEL while we can wait */ error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL); if (error) goto out; error = radix_tree_preload(GFP_KERNEL); - if (error) - goto uncharge; + if (error) { + mem_cgroup_uncharge_cache_page(page); + goto out; + } error = 1; spin_lock(&info->lock); ptr = shmem_swp_entry(info, idx, NULL); - if (ptr && ptr->val == entry.val) + if (ptr && ptr->val == entry.val) { error = add_to_page_cache(page, inode->i_mapping, idx, GFP_NOWAIT); + /* does mem_cgroup_uncharge_cache_page on error */ + } else /* we must compensate for our precharge above */ + mem_cgroup_uncharge_cache_page(page); + if (error == -EEXIST) { struct page *filepage = find_get_page(inode->i_mapping, idx); error = 1; @@ -961,8 +967,6 @@ found: shmem_swp_unmap(ptr); spin_unlock(&info->lock); radix_tree_preload_end(); -uncharge: - mem_cgroup_uncharge_page(page); out: unlock_page(page); page_cache_release(page); @@ -1319,7 +1323,7 @@ repeat: page_cache_release(swappage); goto failed; } - mem_cgroup_uncharge_page(swappage); + mem_cgroup_uncharge_cache_page(swappage); } page_cache_release(swappage); goto repeat; @@ -1358,6 +1362,8 @@ repeat: } if (!filepage) { + int ret; + spin_unlock(&info->lock); filepage = shmem_alloc_page(gfp, info, idx); if (!filepage) { @@ -1386,10 +1392,18 @@ repeat: swap = *entry; shmem_swp_unmap(entry); } - if (error || swap.val || 0 != add_to_page_cache_lru( - filepage, mapping, idx, GFP_NOWAIT)) { + ret = error || swap.val; + if (ret) + mem_cgroup_uncharge_cache_page(filepage); + else + ret = add_to_page_cache_lru(filepage, mapping, + idx, GFP_NOWAIT); + /* + * At add_to_page_cache_lru() failure, uncharge will + * be done automatically. + */ + if (ret) { spin_unlock(&info->lock); - mem_cgroup_uncharge_page(filepage); page_cache_release(filepage); shmem_unacct_blocks(info->flags, 1); shmem_free_blocks(inode, 1); @@ -1398,7 +1412,6 @@ repeat: goto failed; goto repeat; } - mem_cgroup_uncharge_page(filepage); info->flags |= SHMEM_PAGEIN; } -- cgit v1.2.3 From c9b0ed51483cc2fc42bb801b6675c4231b0e4634 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:15 -0700 Subject: memcg: helper function for relcaim from shmem. A new call, mem_cgroup_shrink_usage() is added for shmem handling and relacing non-standard usage of mem_cgroup_charge/uncharge. Now, shmem calls mem_cgroup_charge() just for reclaim some pages from mem_cgroup. In general, shmem is used by some process group and not for global resource (like file caches). So, it's reasonable to reclaim pages from mem_cgroup where shmem is mainly used. [hugh@veritas.com: shmem_getpage release page sooner] [hugh@veritas.com: mem_cgroup_shrink_usage css_put] Signed-off-by: KAMEZAWA Hiroyuki Cc: Balbir Singh Cc: "Eric W. Biederman" Cc: Pavel Emelyanov Cc: Li Zefan Cc: YAMAMOTO Takashi Cc: Paul Menage Cc: David Rientjes Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 7 +++++++ mm/memcontrol.c | 26 ++++++++++++++++++++++++++ mm/shmem.c | 11 ++++------- 3 files changed, 37 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index b4980b8f048e..fdf3967e1397 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -37,6 +37,8 @@ extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, extern void mem_cgroup_uncharge_page(struct page *page); extern void mem_cgroup_uncharge_cache_page(struct page *page); extern void mem_cgroup_move_lists(struct page *page, bool active); +extern int mem_cgroup_shrink_usage(struct mm_struct *mm, gfp_t gfp_mask); + extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, struct list_head *dst, unsigned long *scanned, int order, @@ -102,6 +104,11 @@ static inline void mem_cgroup_uncharge_cache_page(struct page *page) { } +static inline int mem_cgroup_shrink_usage(struct mm_struct *mm, gfp_t gfp_mask) +{ + return 0; +} + static inline void mem_cgroup_move_lists(struct page *page, bool active) { } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a61706193c31..f46b8615de6c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -780,6 +780,32 @@ void mem_cgroup_end_migration(struct page *newpage) mem_cgroup_uncharge_page(newpage); } +/* + * A call to try to shrink memory usage under specified resource controller. + * This is typically used for page reclaiming for shmem for reducing side + * effect of page allocation from shmem, which is used by some mem_cgroup. + */ +int mem_cgroup_shrink_usage(struct mm_struct *mm, gfp_t gfp_mask) +{ + struct mem_cgroup *mem; + int progress = 0; + int retry = MEM_CGROUP_RECLAIM_RETRIES; + + rcu_read_lock(); + mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); + css_get(&mem->css); + rcu_read_unlock(); + + do { + progress = try_to_free_mem_cgroup_pages(mem, gfp_mask); + } while (!progress && --retry); + + css_put(&mem->css); + if (!retry) + return -ENOMEM; + return 0; +} + /* * This routine traverse page_cgroup in given list and drop them all. * *And* this routine doesn't reclaim page itself, just removes page_cgroup. diff --git a/mm/shmem.c b/mm/shmem.c index d58305e8a484..f92fea94d037 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1315,17 +1315,14 @@ repeat: shmem_swp_unmap(entry); spin_unlock(&info->lock); unlock_page(swappage); + page_cache_release(swappage); if (error == -ENOMEM) { /* allow reclaim from this memory cgroup */ - error = mem_cgroup_cache_charge(swappage, - current->mm, gfp & ~__GFP_HIGHMEM); - if (error) { - page_cache_release(swappage); + error = mem_cgroup_shrink_usage(current->mm, + gfp); + if (error) goto failed; - } - mem_cgroup_uncharge_cache_page(swappage); } - page_cache_release(swappage); goto repeat; } } else if (sgp == SGP_READ && !filepage) { -- cgit v1.2.3 From 12b9804419cfb1c1bdac413f6c373af3b88d154b Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:19 -0700 Subject: res_counter: limit change support ebusy Add an interface to set limit. This is necessary to memory resource controller because it shrinks usage at set limit. Other controllers may not need this interface to shrink usage because shrinking is not necessary or impossible. Acked-by: Balbir Singh Acked-by: Pavel Emelyanov Signed-off-by: KAMEZAWA Hiroyuki Cc: Paul Menage Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 290205dfe094..fdeadd9740dc 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -158,4 +158,20 @@ static inline void res_counter_reset_failcnt(struct res_counter *cnt) cnt->failcnt = 0; spin_unlock_irqrestore(&cnt->lock, flags); } + +static inline int res_counter_set_limit(struct res_counter *cnt, + unsigned long long limit) +{ + unsigned long flags; + int ret = -EBUSY; + + spin_lock_irqsave(&cnt->lock, flags); + if (cnt->usage < limit) { + cnt->limit = limit; + ret = 0; + } + spin_unlock_irqrestore(&cnt->lock, flags); + return ret; +} + #endif -- cgit v1.2.3 From 364d3c13c17f45da6d638011078d4c4d3070d719 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:36 -0700 Subject: ptrace: give more respect to SIGKILL ptrace_stop() has some complicated checks to prevent the scheduling in the TASK_TRACED state with the pending SIGKILL, but these checks are racy, and they depend on arch_ptrace_stop_needed(). This patch assumes that the traced task should die asap if it was killed by SIGKILL, in that case schedule()->signal_pending_state() has no reason to ignore the TASK_WAKEKILL part of TASK_TRACED, and we can kill this nasty special case. Note: do_exit()->ptrace_notify() is special, the killed task can already dequeue SIGKILL at this point. Another indication that fatal_signal_pending() is not exactly right. Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Cc: Matthew Wilcox Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 6aca4a16e377..79e749dbf81e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2054,9 +2054,6 @@ static inline int signal_pending_state(long state, struct task_struct *p) if (!signal_pending(p)) return 0; - if (state & (__TASK_STOPPED | __TASK_TRACED)) - return 0; - return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); } -- cgit v1.2.3 From 7b34e4283c685f5cc6ba6d30e939906eee0d4bcf Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:37 -0700 Subject: introduce PF_KTHREAD flag Introduce the new PF_KTHREAD flag to mark the kernel threads. It is set by INIT_TASK() and copied to the forked childs (we could set it in kthreadd() along with PF_NOFREEZE instead). daemonize() was changed as well. In that case testing of PF_KTHREAD is racy, but daemonize() is hopeless anyway. This flag is cleared in do_execve(), before search_binary_handler(). Probably not the best place, we can do this in exec_mmap() or in start_thread(), or clear it along with PF_FORKNOEXEC. But I think this doesn't matter in practice, and if do_execve() fails kthread should die soon. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 1 + include/linux/init_task.h | 2 +- include/linux/sched.h | 1 + kernel/exit.c | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index af249af4ccab..cd2e8c9b1249 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1326,6 +1326,7 @@ int do_execve(char * filename, if (retval < 0) goto out; + current->flags &= ~PF_KTHREAD; retval = search_binary_handler(bprm,regs); if (retval >= 0) { /* execve success */ diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 93c45acf249a..021d8e720c79 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -122,7 +122,7 @@ extern struct group_info init_groups; .state = 0, \ .stack = &init_thread_info, \ .usage = ATOMIC_INIT(2), \ - .flags = 0, \ + .flags = PF_KTHREAD, \ .lock_depth = -1, \ .prio = MAX_PRIO-20, \ .static_prio = MAX_PRIO-20, \ diff --git a/include/linux/sched.h b/include/linux/sched.h index 79e749dbf81e..eec64a4adb9d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1483,6 +1483,7 @@ static inline void put_task_struct(struct task_struct *t) #define PF_EXITING 0x00000004 /* getting shut down */ #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ #define PF_VCPU 0x00000010 /* I'm a virtual CPU */ +#define PF_KTHREAD 0x00000020 /* I am a kernel thread */ #define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */ #define PF_SUPERPRIV 0x00000100 /* used super-user privileges */ #define PF_DUMPCORE 0x00000200 /* dumped core */ diff --git a/kernel/exit.c b/kernel/exit.c index a7799d8a6404..28a44a2612dc 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -430,7 +430,7 @@ void daemonize(const char *name, ...) * We don't want to have TIF_FREEZE set if the system-wide hibernation * or suspend transition begins right now. */ - current->flags |= PF_NOFREEZE; + current->flags |= (PF_NOFREEZE | PF_KTHREAD); if (current->nsproxy != &init_nsproxy) { get_nsproxy(&init_nsproxy); -- cgit v1.2.3 From 246bb0b1deb29726990620d8b5e55ca29f331362 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:38 -0700 Subject: kill PF_BORROWED_MM in favour of PF_KTHREAD Kill PF_BORROWED_MM. Change use_mm/unuse_mm to not play with ->flags, and do s/PF_BORROWED_MM/PF_KTHREAD/ for a couple of other users. No functional changes yet. But this allows us to do further fixes/cleanups. oom_kill/ptrace/etc often check "p->mm != NULL" to filter out the kthreads, this is wrong because of use_mm(). The problem with PF_BORROWED_MM is that we need task_lock() to avoid races. With this patch we can check PF_KTHREAD directly, or use a simple lockless helper: /* The result must not be dereferenced !!! */ struct mm_struct *__get_task_mm(struct task_struct *tsk) { if (tsk->flags & PF_KTHREAD) return NULL; return tsk->mm; } Note also ecard_task(). It runs with ->mm != NULL, but it's the kernel thread without PF_BORROWED_MM. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/aio.c | 2 -- include/linux/sched.h | 3 +-- kernel/fork.c | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/aio.c b/fs/aio.c index 0fb3117ddd93..0051fd94b44e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -586,7 +586,6 @@ static void use_mm(struct mm_struct *mm) struct task_struct *tsk = current; task_lock(tsk); - tsk->flags |= PF_BORROWED_MM; active_mm = tsk->active_mm; atomic_inc(&mm->mm_count); tsk->mm = mm; @@ -610,7 +609,6 @@ static void unuse_mm(struct mm_struct *mm) struct task_struct *tsk = current; task_lock(tsk); - tsk->flags &= ~PF_BORROWED_MM; tsk->mm = NULL; /* active_mm is still 'mm' */ enter_lazy_tlb(mm, tsk); diff --git a/include/linux/sched.h b/include/linux/sched.h index eec64a4adb9d..0560999eb1db 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1483,7 +1483,6 @@ static inline void put_task_struct(struct task_struct *t) #define PF_EXITING 0x00000004 /* getting shut down */ #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ #define PF_VCPU 0x00000010 /* I'm a virtual CPU */ -#define PF_KTHREAD 0x00000020 /* I am a kernel thread */ #define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */ #define PF_SUPERPRIV 0x00000100 /* used super-user privileges */ #define PF_DUMPCORE 0x00000200 /* dumped core */ @@ -1497,7 +1496,7 @@ static inline void put_task_struct(struct task_struct *t) #define PF_KSWAPD 0x00040000 /* I am kswapd */ #define PF_SWAPOFF 0x00080000 /* I am in swapoff */ #define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */ -#define PF_BORROWED_MM 0x00200000 /* I am a kthread doing use_mm */ +#define PF_KTHREAD 0x00200000 /* I am a kernel thread */ #define PF_RANDOMIZE 0x00400000 /* randomize virtual address space */ #define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */ #define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */ diff --git a/kernel/fork.c b/kernel/fork.c index 228f80c9155a..eeaec6893b0d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -474,7 +474,7 @@ EXPORT_SYMBOL_GPL(mmput); /** * get_task_mm - acquire a reference to the task's mm * - * Returns %NULL if the task has no mm. Checks PF_BORROWED_MM (meaning + * Returns %NULL if the task has no mm. Checks PF_KTHREAD (meaning * this kernel workthread has transiently adopted a user mm with use_mm, * to do its AIO) is not set and if so returns a reference to it, after * bumping up the use count. User must release the mm via mmput() @@ -487,7 +487,7 @@ struct mm_struct *get_task_mm(struct task_struct *task) task_lock(task); mm = task->mm; if (mm) { - if (task->flags & PF_BORROWED_MM) + if (task->flags & PF_KTHREAD) mm = NULL; else atomic_inc(&mm->mm_users); -- cgit v1.2.3 From 32ecb1f26dd50eeaac4e3f4dea4541c97848e459 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:41 -0700 Subject: coredump: turn mm->core_startup_done into the pointer to struct core_state mm->core_startup_done points to "struct completion startup_done" allocated on the coredump_wait()'s stack. Introduce the new structure, core_state, which holds this "struct completion". This way we can add more info visible to the threads participating in coredump without enlarging mm_struct. No changes in affected .o files. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 8 ++++---- include/linux/mm_types.h | 7 ++++++- kernel/exit.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index e347e6ed1617..71734568f018 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1597,13 +1597,13 @@ static int coredump_wait(int exit_code) { struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - struct completion startup_done; + struct core_state core_state; struct completion *vfork_done; int core_waiters; init_completion(&mm->core_done); - init_completion(&startup_done); - mm->core_startup_done = &startup_done; + init_completion(&core_state.startup); + mm->core_state = &core_state; core_waiters = zap_threads(tsk, mm, exit_code); up_write(&mm->mmap_sem); @@ -1622,7 +1622,7 @@ static int coredump_wait(int exit_code) } if (core_waiters) - wait_for_completion(&startup_done); + wait_for_completion(&core_state.startup); fail: BUG_ON(mm->core_waiters); return core_waiters; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 02a27ae78539..97819efd2333 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -159,6 +159,10 @@ struct vm_area_struct { #endif }; +struct core_state { + struct completion startup; +}; + struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; @@ -220,7 +224,8 @@ struct mm_struct { unsigned long flags; /* Must use atomic bitops to access the bits */ /* coredumping support */ - struct completion *core_startup_done, core_done; + struct core_state *core_state; + struct completion core_done; /* aio bits */ rwlock_t ioctx_list_lock; /* aio lock */ diff --git a/kernel/exit.c b/kernel/exit.c index 28a44a2612dc..f7fa21dbced4 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -680,7 +680,7 @@ static void exit_mm(struct task_struct * tsk) up_read(&mm->mmap_sem); down_write(&mm->mmap_sem); if (!--mm->core_waiters) - complete(mm->core_startup_done); + complete(&mm->core_state->startup); up_write(&mm->mmap_sem); wait_for_completion(&mm->core_done); -- cgit v1.2.3 From 999d9fc1670bc082928b93b11d1f2e0e417d973c Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:41 -0700 Subject: coredump: move mm->core_waiters into struct core_state Move mm->core_waiters into "struct core_state" allocated on stack. This shrinks mm_struct a little bit and allows further changes. This patch mostly does s/core_waiters/core_state. The only essential change is that coredump_wait() must clear mm->core_state before return. The coredump_wait()'s path is uglified and .text grows by 30 bytes, this is fixed by the next patch. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 21 +++++++++++---------- include/linux/mm_types.h | 2 +- kernel/exit.c | 8 ++++---- kernel/fork.c | 2 +- kernel/signal.c | 4 ++-- 5 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index 71734568f018..50de3aaff4d0 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -722,12 +722,10 @@ static int exec_mmap(struct mm_struct *mm) * Make sure that if there is a core dump in progress * for the old mm, we get out and die instead of going * through with the exec. We must hold mmap_sem around - * checking core_waiters and changing tsk->mm. The - * core-inducing thread will increment core_waiters for - * each thread whose ->mm == old_mm. + * checking core_state and changing tsk->mm. */ down_read(&old_mm->mmap_sem); - if (unlikely(old_mm->core_waiters)) { + if (unlikely(old_mm->core_state)) { up_read(&old_mm->mmap_sem); return -EINTR; } @@ -1514,7 +1512,7 @@ static void zap_process(struct task_struct *start) t = start; do { if (t != current && t->mm) { - t->mm->core_waiters++; + t->mm->core_state->nr_threads++; sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } @@ -1538,11 +1536,11 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, if (err) return err; - if (atomic_read(&mm->mm_users) == mm->core_waiters + 1) + if (atomic_read(&mm->mm_users) == mm->core_state->nr_threads + 1) goto done; /* * We should find and kill all tasks which use this mm, and we should - * count them correctly into mm->core_waiters. We don't take tasklist + * count them correctly into ->nr_threads. We don't take tasklist * lock, but this is safe wrt: * * fork: @@ -1590,7 +1588,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, } rcu_read_unlock(); done: - return mm->core_waiters; + return mm->core_state->nr_threads; } static int coredump_wait(int exit_code) @@ -1603,9 +1601,12 @@ static int coredump_wait(int exit_code) init_completion(&mm->core_done); init_completion(&core_state.startup); + core_state.nr_threads = 0; mm->core_state = &core_state; core_waiters = zap_threads(tsk, mm, exit_code); + if (core_waiters < 0) + mm->core_state = NULL; up_write(&mm->mmap_sem); if (unlikely(core_waiters < 0)) @@ -1623,8 +1624,8 @@ static int coredump_wait(int exit_code) if (core_waiters) wait_for_completion(&core_state.startup); + mm->core_state = NULL; fail: - BUG_ON(mm->core_waiters); return core_waiters; } @@ -1702,7 +1703,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) /* * If another thread got here first, or we are not dumpable, bail out. */ - if (mm->core_waiters || !get_dumpable(mm)) { + if (mm->core_state || !get_dumpable(mm)) { up_write(&mm->mmap_sem); goto fail; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 97819efd2333..c0b1747b61a5 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -160,6 +160,7 @@ struct vm_area_struct { }; struct core_state { + int nr_threads; struct completion startup; }; @@ -179,7 +180,6 @@ struct mm_struct { atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ - int core_waiters; struct rw_semaphore mmap_sem; spinlock_t page_table_lock; /* Protects page tables and some counters */ diff --git a/kernel/exit.c b/kernel/exit.c index f7fa21dbced4..988e232254e9 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -670,16 +670,16 @@ static void exit_mm(struct task_struct * tsk) return; /* * Serialize with any possible pending coredump. - * We must hold mmap_sem around checking core_waiters + * We must hold mmap_sem around checking core_state * and clearing tsk->mm. The core-inducing thread - * will increment core_waiters for each thread in the + * will increment ->nr_threads for each thread in the * group with ->mm != NULL. */ down_read(&mm->mmap_sem); - if (mm->core_waiters) { + if (mm->core_state) { up_read(&mm->mmap_sem); down_write(&mm->mmap_sem); - if (!--mm->core_waiters) + if (!--mm->core_state->nr_threads) complete(&mm->core_state->startup); up_write(&mm->mmap_sem); diff --git a/kernel/fork.c b/kernel/fork.c index eeaec6893b0d..813d5c89b9d5 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -400,7 +400,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p) INIT_LIST_HEAD(&mm->mmlist); mm->flags = (current->mm) ? current->mm->flags : MMF_DUMP_FILTER_DEFAULT; - mm->core_waiters = 0; + mm->core_state = NULL; mm->nr_ptes = 0; set_mm_counter(mm, file_rss, 0); set_mm_counter(mm, anon_rss, 0); diff --git a/kernel/signal.c b/kernel/signal.c index 39c1706edf03..5c7b7eaa0dc6 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1480,10 +1480,10 @@ static inline int may_ptrace_stop(void) * is a deadlock situation, and pointless because our tracer * is dead so don't allow us to stop. * If SIGKILL was already sent before the caller unlocked - * ->siglock we must see ->core_waiters != 0. Otherwise it + * ->siglock we must see ->core_state != NULL. Otherwise it * is safe to enter schedule(). */ - if (unlikely(current->mm->core_waiters) && + if (unlikely(current->mm->core_state) && unlikely(current->mm == current->parent->mm)) return 0; -- cgit v1.2.3 From c5f1cc8c1828486a61ab3e575da6e2c62b34d399 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:42 -0700 Subject: coredump: turn core_state->nr_threads into atomic_t Turn core_state->nr_threads into atomic_t and kill now unneeded down_write(&mm->mmap_sem) in exit_mm(). Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 +- include/linux/mm_types.h | 2 +- kernel/exit.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index c74bb34eeeff..15d493fe8aa3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1591,7 +1591,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, } rcu_read_unlock(); done: - core_state->nr_threads = nr; + atomic_set(&core_state->nr_threads, nr); return nr; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index c0b1747b61a5..ae99a28ba6ae 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -160,7 +160,7 @@ struct vm_area_struct { }; struct core_state { - int nr_threads; + atomic_t nr_threads; struct completion startup; }; diff --git a/kernel/exit.c b/kernel/exit.c index 988e232254e9..63d82957baae 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -678,10 +678,9 @@ static void exit_mm(struct task_struct * tsk) down_read(&mm->mmap_sem); if (mm->core_state) { up_read(&mm->mmap_sem); - down_write(&mm->mmap_sem); - if (!--mm->core_state->nr_threads) + + if (atomic_dec_and_test(&mm->core_state->nr_threads)) complete(&mm->core_state->startup); - up_write(&mm->mmap_sem); wait_for_completion(&mm->core_done); down_read(&mm->mmap_sem); -- cgit v1.2.3 From b564daf806d492dd4f7afe9b6c83b8d35d137669 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:44 -0700 Subject: coredump: construct the list of coredumping threads at startup time binfmt->core_dump() has to iterate over the all threads in system in order to find the coredumping threads and construct the list using the GFP_ATOMIC allocations. With this patch each thread allocates the list node on exit_mm()'s stack and adds itself to the list. This allows us to do further changes: - simplify ->core_dump() - change exit_mm() to clear ->mm first, then wait for ->core_done. this makes the coredumping process visible to oom_kill - kill mm->core_done Signed-off-by: Oleg Nesterov Acked-by: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 ++ include/linux/mm_types.h | 6 ++++++ kernel/exit.c | 15 ++++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index b8ee842d93cd..fe2873b8037f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1604,6 +1604,8 @@ static int coredump_wait(int exit_code, struct core_state *core_state) init_completion(&mm->core_done); init_completion(&core_state->startup); + core_state->dumper.task = tsk; + core_state->dumper.next = NULL; core_waiters = zap_threads(tsk, mm, core_state, exit_code); up_write(&mm->mmap_sem); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index ae99a28ba6ae..4d0d0abc79fe 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -159,8 +159,14 @@ struct vm_area_struct { #endif }; +struct core_thread { + struct task_struct *task; + struct core_thread *next; +}; + struct core_state { atomic_t nr_threads; + struct core_thread dumper; struct completion startup; }; diff --git a/kernel/exit.c b/kernel/exit.c index 63d82957baae..b66f0d55c791 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -664,6 +664,7 @@ assign_new_owner: static void exit_mm(struct task_struct * tsk) { struct mm_struct *mm = tsk->mm; + struct core_state *core_state; mm_release(tsk, mm); if (!mm) @@ -676,11 +677,19 @@ static void exit_mm(struct task_struct * tsk) * group with ->mm != NULL. */ down_read(&mm->mmap_sem); - if (mm->core_state) { + core_state = mm->core_state; + if (core_state) { + struct core_thread self; up_read(&mm->mmap_sem); - if (atomic_dec_and_test(&mm->core_state->nr_threads)) - complete(&mm->core_state->startup); + self.task = tsk; + self.next = xchg(&core_state->dumper.next, &self); + /* + * Implies mb(), the result of xchg() must be visible + * to core_state->dumper. + */ + if (atomic_dec_and_test(&core_state->nr_threads)) + complete(&core_state->startup); wait_for_completion(&mm->core_done); down_read(&mm->mmap_sem); -- cgit v1.2.3 From a94e2d408eaedbd85aae259621d46fafc10479a2 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:46 -0700 Subject: coredump: kill mm->core_done Now that we have core_state->dumper list we can use it to wake up the sub-threads waiting for the coredump completion. This uglifies the code and .text grows by 47 bytes, but otoh mm_struct lessens by sizeof(struct completion). Also, with this change we can decouple exit_mm() from the coredumping code. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 25 ++++++++++++++++++++++--- include/linux/mm_types.h | 4 +--- kernel/exit.c | 8 +++++++- 3 files changed, 30 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index fe2873b8037f..bff43aeb235e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1602,7 +1602,6 @@ static int coredump_wait(int exit_code, struct core_state *core_state) struct completion *vfork_done; int core_waiters; - init_completion(&mm->core_done); init_completion(&core_state->startup); core_state->dumper.task = tsk; core_state->dumper.next = NULL; @@ -1628,6 +1627,27 @@ fail: return core_waiters; } +static void coredump_finish(struct mm_struct *mm) +{ + struct core_thread *curr, *next; + struct task_struct *task; + + next = mm->core_state->dumper.next; + while ((curr = next) != NULL) { + next = curr->next; + task = curr->task; + /* + * see exit_mm(), curr->task must not see + * ->task == NULL before we read ->next. + */ + smp_mb(); + curr->task = NULL; + wake_up_process(task); + } + + mm->core_state = NULL; +} + /* * set_dumpable converts traditional three-value dumpable to two flags and * stores them into mm->flags. It modifies lower two bits of mm->flags, but @@ -1812,8 +1832,7 @@ fail_unlock: argv_free(helper_argv); current->fsuid = fsuid; - complete_all(&mm->core_done); - mm->core_state = NULL; + coredump_finish(mm); fail: return retval; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 4d0d0abc79fe..746f975b58ef 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -229,9 +229,7 @@ struct mm_struct { unsigned long flags; /* Must use atomic bitops to access the bits */ - /* coredumping support */ - struct core_state *core_state; - struct completion core_done; + struct core_state *core_state; /* coredumping support */ /* aio bits */ rwlock_t ioctx_list_lock; /* aio lock */ diff --git a/kernel/exit.c b/kernel/exit.c index b66f0d55c791..8a4d4d12e294 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -691,7 +691,13 @@ static void exit_mm(struct task_struct * tsk) if (atomic_dec_and_test(&core_state->nr_threads)) complete(&core_state->startup); - wait_for_completion(&mm->core_done); + for (;;) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!self.task) /* see coredump_finish() */ + break; + schedule(); + } + __set_task_state(tsk, TASK_RUNNING); down_read(&mm->mmap_sem); } atomic_inc(&mm->mm_count); -- cgit v1.2.3 From db700897224b5ebdf852f2d38920ce428940d059 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:49 -0700 Subject: workqueues: implement flush_work() Most of users of flush_workqueue() can be changed to use cancel_work_sync(), but sometimes we really need to wait for the completion and cancelling is not an option. schedule_on_each_cpu() is good example. Add the new helper, flush_work(work), which waits for the completion of the specific work_struct. More precisely, it "flushes" the result of of the last queue_work() which is visible to the caller. For example, this code queue_work(wq, work); /* WINDOW */ queue_work(wq, work); flush_work(work); doesn't necessary work "as expected". What can happen in the WINDOW above is - wq starts the execution of work->func() - the caller migrates to another CPU now, after the 2nd queue_work() this work is active on the previous CPU, and at the same time it is queued on another. In this case flush_work(work) may return before the first work->func() completes. It is trivial to add another helper int flush_work_sync(struct work_struct *work) { return flush_work(work) || wait_on_work(work); } which works "more correctly", but it has to iterate over all CPUs and thus it much slower than flush_work(). Signed-off-by: Oleg Nesterov Acked-by: Max Krasnyansky Acked-by: Jarek Poplawski Cc: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 2 ++ kernel/workqueue.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include/linux') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 14d47120682b..5c158c477ac7 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -201,6 +201,8 @@ extern int keventd_up(void); extern void init_workqueues(void); int execute_in_process_context(work_func_t fn, struct execute_work *); +extern int flush_work(struct work_struct *work); + extern int cancel_work_sync(struct work_struct *work); /* diff --git a/kernel/workqueue.c b/kernel/workqueue.c index d9a2d65cc63e..ee41cf857d55 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -423,6 +423,52 @@ void flush_workqueue(struct workqueue_struct *wq) } EXPORT_SYMBOL_GPL(flush_workqueue); +/** + * flush_work - block until a work_struct's callback has terminated + * @work: the work which is to be flushed + * + * It is expected that, prior to calling flush_work(), the caller has + * arranged for the work to not be requeued, otherwise it doesn't make + * sense to use this function. + */ +int flush_work(struct work_struct *work) +{ + struct cpu_workqueue_struct *cwq; + struct list_head *prev; + struct wq_barrier barr; + + might_sleep(); + cwq = get_wq_data(work); + if (!cwq) + return 0; + + prev = NULL; + spin_lock_irq(&cwq->lock); + if (!list_empty(&work->entry)) { + /* + * See the comment near try_to_grab_pending()->smp_rmb(). + * If it was re-queued under us we are not going to wait. + */ + smp_rmb(); + if (unlikely(cwq != get_wq_data(work))) + goto out; + prev = &work->entry; + } else { + if (cwq->current_work != work) + goto out; + prev = &cwq->worklist; + } + insert_wq_barrier(cwq, &barr, prev->next); +out: + spin_unlock_irq(&cwq->lock); + if (!prev) + return 0; + + wait_for_completion(&barr.done); + return 1; +} +EXPORT_SYMBOL_GPL(flush_work); + /* * Upon a successful return (>= 0), the caller "owns" WORK_STRUCT_PENDING bit, * so this work can't be re-armed in any way. -- cgit v1.2.3 From 3da1c84c00c7e5fa8348336bd8c342f9128b0f14 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:50 -0700 Subject: workqueues: make get_online_cpus() useable for work->func() workqueue_cpu_callback(CPU_DEAD) flushes cwq->thread under cpu_maps_update_begin(). This means that the multithreaded workqueues can't use get_online_cpus() due to the possible deadlock, very bad and very old problem. Introduce the new state, CPU_POST_DEAD, which is called after cpu_hotplug_done() but before cpu_maps_update_done(). Change workqueue_cpu_callback() to use CPU_POST_DEAD instead of CPU_DEAD. This means that create/destroy functions can't rely on get_online_cpus() any longer and should take cpu_add_remove_lock instead. [akpm@linux-foundation.org: fix CONFIG_SMP=n] Signed-off-by: Oleg Nesterov Acked-by: Gautham R Shenoy Cc: Heiko Carstens Cc: Max Krasnyansky Cc: Paul Jackson Cc: Paul Menage Cc: Peter Zijlstra Cc: Vegard Nossum Cc: Martin Schwidefsky Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpu.h | 15 +++++++++++---- include/linux/notifier.h | 2 ++ kernel/cpu.c | 5 +++++ kernel/workqueue.c | 18 +++++++++--------- 4 files changed, 27 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 7464ba3b4333..d7faf8808497 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -69,10 +69,11 @@ static inline void unregister_cpu_notifier(struct notifier_block *nb) #endif int cpu_up(unsigned int cpu); - extern void cpu_hotplug_init(void); +extern void cpu_maps_update_begin(void); +extern void cpu_maps_update_done(void); -#else +#else /* CONFIG_SMP */ static inline int register_cpu_notifier(struct notifier_block *nb) { @@ -87,10 +88,16 @@ static inline void cpu_hotplug_init(void) { } +static inline void cpu_maps_update_begin(void) +{ +} + +static inline void cpu_maps_update_done(void) +{ +} + #endif /* CONFIG_SMP */ extern struct sysdev_class cpu_sysdev_class; -extern void cpu_maps_update_begin(void); -extern void cpu_maps_update_done(void); #ifdef CONFIG_HOTPLUG_CPU /* Stop CPUs going up and down. */ diff --git a/include/linux/notifier.h b/include/linux/notifier.h index bd3d72ddf333..da2698b0fdd1 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -214,6 +214,8 @@ static inline int notifier_to_errno(int ret) #define CPU_DEAD 0x0007 /* CPU (unsigned)v dead */ #define CPU_DYING 0x0008 /* CPU (unsigned)v not running any task, * not handling interrupts, soon dead */ +#define CPU_POST_DEAD 0x0009 /* CPU (unsigned)v dead, cpu_hotplug + * lock is dropped */ /* Used for CPU hotplug events occuring while tasks are frozen due to a suspend * operation in progress diff --git a/kernel/cpu.c b/kernel/cpu.c index 2cc409ce0a8f..10ba5f1004a5 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -285,6 +285,11 @@ out_allowed: set_cpus_allowed_ptr(current, &old_allowed); out_release: cpu_hotplug_done(); + if (!err) { + if (raw_notifier_call_chain(&cpu_chain, CPU_POST_DEAD | mod, + hcpu) == NOTIFY_BAD) + BUG(); + } return err; } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5fbffd302eb5..828e58230cbc 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -828,7 +828,7 @@ struct workqueue_struct *__create_workqueue_key(const char *name, err = create_workqueue_thread(cwq, singlethread_cpu); start_workqueue_thread(cwq, -1); } else { - get_online_cpus(); + cpu_maps_update_begin(); spin_lock(&workqueue_lock); list_add(&wq->list, &workqueues); spin_unlock(&workqueue_lock); @@ -840,7 +840,7 @@ struct workqueue_struct *__create_workqueue_key(const char *name, err = create_workqueue_thread(cwq, cpu); start_workqueue_thread(cwq, cpu); } - put_online_cpus(); + cpu_maps_update_done(); } if (err) { @@ -854,8 +854,8 @@ EXPORT_SYMBOL_GPL(__create_workqueue_key); static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) { /* - * Our caller is either destroy_workqueue() or CPU_DEAD, - * get_online_cpus() protects cwq->thread. + * Our caller is either destroy_workqueue() or CPU_POST_DEAD, + * cpu_add_remove_lock protects cwq->thread. */ if (cwq->thread == NULL) return; @@ -865,7 +865,7 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) flush_cpu_workqueue(cwq); /* - * If the caller is CPU_DEAD and cwq->worklist was not empty, + * If the caller is CPU_POST_DEAD and cwq->worklist was not empty, * a concurrent flush_workqueue() can insert a barrier after us. * However, in that case run_workqueue() won't return and check * kthread_should_stop() until it flushes all work_struct's. @@ -889,14 +889,14 @@ void destroy_workqueue(struct workqueue_struct *wq) const cpumask_t *cpu_map = wq_cpu_map(wq); int cpu; - get_online_cpus(); + cpu_maps_update_begin(); spin_lock(&workqueue_lock); list_del(&wq->list); spin_unlock(&workqueue_lock); for_each_cpu_mask_nr(cpu, *cpu_map) cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu)); - put_online_cpus(); + cpu_maps_update_done(); free_percpu(wq->cpu_wq); kfree(wq); @@ -935,7 +935,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, case CPU_UP_CANCELED: start_workqueue_thread(cwq, -1); - case CPU_DEAD: + case CPU_POST_DEAD: cleanup_workqueue_thread(cwq); break; } @@ -943,7 +943,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_CANCELED: - case CPU_DEAD: + case CPU_POST_DEAD: cpu_clear(cpu, cpu_populated_map); } -- cgit v1.2.3 From 95b68dec0d52c7b8fea3698b3938cf3ab936436b Mon Sep 17 00:00:00 2001 From: Chandru Date: Fri, 25 Jul 2008 01:47:55 -0700 Subject: calgary iommu: use the first kernels TCE tables in kdump kdump kernel fails to boot with calgary iommu and aacraid driver on a x366 box. The ongoing dma's of aacraid from the first kernel continue to exist until the driver is loaded in the kdump kernel. Calgary is initialized prior to aacraid and creation of new tce tables causes wrong dma's to occur. Here we try to get the tce tables of the first kernel in kdump kernel and use them. While in the kdump kernel we do not allocate new tce tables but instead read the base address register contents of calgary iommu and use the tables that the registers point to. With these changes the kdump kernel and hence aacraid now boots normally. Signed-off-by: Chandru Siddalingappa Acked-by: Muli Ben-Yehuda Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kernel/pci-calgary_64.c | 85 +++++++++++++++++++++++++++++++++++++--- include/linux/crash_dump.h | 8 ++++ 2 files changed, 87 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index 151f2d171f7c..19e7fc7c2c4f 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -167,6 +168,8 @@ static void calgary_dump_error_regs(struct iommu_table *tbl); static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev); static void calioc2_tce_cache_blast(struct iommu_table *tbl); static void calioc2_dump_error_regs(struct iommu_table *tbl); +static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl); +static void get_tce_space_from_tar(void); static struct cal_chipset_ops calgary_chip_ops = { .handle_quirks = calgary_handle_quirks, @@ -830,7 +833,11 @@ static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar) tbl = pci_iommu(dev->bus); tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space; - tce_free(tbl, 0, tbl->it_size); + + if (is_kdump_kernel()) + calgary_init_bitmap_from_tce_table(tbl); + else + tce_free(tbl, 0, tbl->it_size); if (is_calgary(dev->device)) tbl->chip_ops = &calgary_chip_ops; @@ -1209,6 +1216,10 @@ static int __init calgary_init(void) if (ret) return ret; + /* Purely for kdump kernel case */ + if (is_kdump_kernel()) + get_tce_space_from_tar(); + do { dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev); if (!dev) @@ -1339,6 +1350,61 @@ static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev) return (val != 0xffffffff); } +/* + * calgary_init_bitmap_from_tce_table(): + * Funtion for kdump case. In the second/kdump kernel initialize + * the bitmap based on the tce table entries obtained from first kernel + */ +static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl) +{ + u64 *tp; + unsigned int index; + tp = ((u64 *)tbl->it_base); + for (index = 0 ; index < tbl->it_size; index++) { + if (*tp != 0x0) + set_bit(index, tbl->it_map); + tp++; + } +} + +/* + * get_tce_space_from_tar(): + * Function for kdump case. Get the tce tables from first kernel + * by reading the contents of the base adress register of calgary iommu + */ +static void get_tce_space_from_tar() +{ + int bus; + void __iomem *target; + unsigned long tce_space; + + for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { + struct calgary_bus_info *info = &bus_info[bus]; + unsigned short pci_device; + u32 val; + + val = read_pci_config(bus, 0, 0, 0); + pci_device = (val & 0xFFFF0000) >> 16; + + if (!is_cal_pci_dev(pci_device)) + continue; + if (info->translation_disabled) + continue; + + if (calgary_bus_has_devices(bus, pci_device) || + translate_empty_slots) { + target = calgary_reg(bus_info[bus].bbar, + tar_offset(bus)); + tce_space = be64_to_cpu(readq(target)); + tce_space = tce_space & TAR_SW_BITS; + + tce_space = tce_space & (~specified_table_size); + info->tce_space = (u64 *)__va(tce_space); + } + } + return; +} + void __init detect_calgary(void) { int bus; @@ -1394,7 +1460,8 @@ void __init detect_calgary(void) return; } - specified_table_size = determine_tce_table_size(max_pfn * PAGE_SIZE); + specified_table_size = determine_tce_table_size((is_kdump_kernel() ? + saved_max_pfn : max_pfn) * PAGE_SIZE); for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { struct calgary_bus_info *info = &bus_info[bus]; @@ -1412,10 +1479,16 @@ void __init detect_calgary(void) if (calgary_bus_has_devices(bus, pci_device) || translate_empty_slots) { - tbl = alloc_tce_table(); - if (!tbl) - goto cleanup; - info->tce_space = tbl; + /* + * If it is kdump kernel, find and use tce tables + * from first kernel, else allocate tce tables here + */ + if (!is_kdump_kernel()) { + tbl = alloc_tce_table(); + if (!tbl) + goto cleanup; + info->tce_space = tbl; + } calgary_found = 1; } } diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h index 22c7ac5cd80c..6cd39a927e1f 100644 --- a/include/linux/crash_dump.h +++ b/include/linux/crash_dump.h @@ -22,5 +22,13 @@ extern struct proc_dir_entry *proc_vmcore; #define vmcore_elf_check_arch(x) (elf_check_arch(x) || vmcore_elf_check_arch_cross(x)) +static inline int is_kdump_kernel(void) +{ + return (elfcorehdr_addr != ELFCORE_ADDR_MAX) ? 1 : 0; +} +#else /* !CONFIG_CRASH_DUMP */ +static inline int is_kdump_kernel(void) { return 0; } #endif /* CONFIG_CRASH_DUMP */ + +extern unsigned long saved_max_pfn; #endif /* LINUX_CRASHDUMP_H */ -- cgit v1.2.3 From 2027d1abc25ff770cc3bc936abd33570ce85d85a Mon Sep 17 00:00:00 2001 From: Nadia Derbey Date: Fri, 25 Jul 2008 01:47:57 -0700 Subject: idr: change the idr structure After scalability problems have been detected when using the sysV ipcs, I have proposed to use an RCU based implementation of the IDR api instead (see threads http://lkml.org/lkml/2008/4/11/212 and http://lkml.org/lkml/2008/4/29/295). This resulted in many people asking to convert the idr API and make it rcu safe (because most of the code was duplicated and thus unmaintanable and unreviewable). So here is a first attempt. The important change wrt to the idr API itself is during idr removes: idr layers are freed after a grace period, instead of being moved to the free list. The important change wrt to ipcs, is that idr_find() can now be called locklessly inside a rcu read critical section. Here are the results I've got for the pmsg test sent by Manfred: 2.6.25-rc3-mm1 2.6.25-rc3-mm1+ 2.6.25-mm1 Patched 2.6.25-mm1 1 1168441 1064021 876000 947488 2 1094264 921059 1549592 1730685 3 2082520 1738165 1694370 2324880 4 2079929 1695521 404553 2400408 5 2898758 406566 391283 3246580 6 2921417 261275 263249 3752148 7 3308761 126056 191742 4243142 8 3329456 100129 141722 4275780 1st column: stock 2.6.25-rc3-mm1 2nd column: 2.6.25-rc3-mm1 + ipc patches (store ipcs into idrs) 3nd column: stock 2.6.25-mm1 4th column: 2.6.25-mm1 + this pacth series. This patch: Add an rcu_head to the idr_layer structure in order to free it after a grace period. Signed-off-by: Nadia Derbey Reviewed-by: "Paul E. McKenney" Cc: Manfred Spraul Cc: Jim Houston Cc: Pierre Peiffer Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/idr.h b/include/linux/idr.h index 9a2d762124de..1af61d23be36 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -15,6 +15,7 @@ #include #include #include +#include #if BITS_PER_LONG == 32 # define IDR_BITS 5 @@ -51,6 +52,7 @@ struct idr_layer { unsigned long bitmap; /* A zero bit means "space here" */ struct idr_layer *ary[1< Date: Fri, 25 Jul 2008 01:47:59 -0700 Subject: idr: error checking factorization Do some code factorization in the return code analysis. Signed-off-by: Nadia Derbey Cc: "Paul E. McKenney" Cc: Manfred Spraul Cc: Jim Houston Cc: Pierre Peiffer Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 6 ++++++ lib/idr.c | 30 +++++++++--------------------- 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/idr.h b/include/linux/idr.h index 1af61d23be36..762c3f2c631d 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -73,6 +73,12 @@ struct idr { } #define DEFINE_IDR(name) struct idr name = IDR_INIT(name) +/* Actions to be taken after a call to _idr_sub_alloc */ +#define IDR_NEED_TO_GROW -2 +#define IDR_NOMORE_SPACE -3 + +#define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC) + /* * This is what we export. */ diff --git a/lib/idr.c b/lib/idr.c index 9d905b131ecb..80ba06f29d36 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -143,7 +143,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) /* if already at the top layer, we need to grow */ if (!(p = pa[l])) { *starting_id = id; - return -2; + return IDR_NEED_TO_GROW; } /* If we need to go up one layer, continue the @@ -160,7 +160,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) id = ((id >> sh) ^ n ^ m) << sh; } if ((id >= MAX_ID_BIT) || (id < 0)) - return -3; + return IDR_NOMORE_SPACE; if (l == 0) break; /* @@ -229,7 +229,7 @@ build_up: idp->top = p; idp->layers = layers; v = sub_alloc(idp, &id, pa); - if (v == -2) + if (v == IDR_NEED_TO_GROW) goto build_up; return(v); } @@ -278,12 +278,8 @@ int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id) * This is a cheap hack until the IDR code can be fixed to * return proper error values. */ - if (rv < 0) { - if (rv == -1) - return -EAGAIN; - else /* Will be -3 */ - return -ENOSPC; - } + if (rv < 0) + return _idr_rc_to_errno(rv); *id = rv; return 0; } @@ -313,12 +309,8 @@ int idr_get_new(struct idr *idp, void *ptr, int *id) * This is a cheap hack until the IDR code can be fixed to * return proper error values. */ - if (rv < 0) { - if (rv == -1) - return -EAGAIN; - else /* Will be -3 */ - return -ENOSPC; - } + if (rv < 0) + return _idr_rc_to_errno(rv); *id = rv; return 0; } @@ -696,12 +688,8 @@ int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) restart: /* get vacant slot */ t = idr_get_empty_slot(&ida->idr, idr_id, pa); - if (t < 0) { - if (t == -1) - return -EAGAIN; - else /* will be -3 */ - return -ENOSPC; - } + if (t < 0) + return _idr_rc_to_errno(t); if (t * IDA_BITMAP_BITS >= MAX_ID_BIT) return -ENOSPC; -- cgit v1.2.3 From f9c46d6ea5ce138a886c3a0f10a46130afab75f5 Mon Sep 17 00:00:00 2001 From: Nadia Derbey Date: Fri, 25 Jul 2008 01:48:01 -0700 Subject: idr: make idr_find rcu-safe Make idr_find rcu-safe: it can now be called inside an rcu_read critical section. Signed-off-by: Nadia Derbey Reviewed-by: "Paul E. McKenney" Cc: Manfred Spraul Cc: Jim Houston Cc: Pierre Peiffer Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 16 ++++++++++++++++ lib/idr.c | 11 ++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/idr.h b/include/linux/idr.h index 762c3f2c631d..fa035f96f2a3 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -79,6 +79,22 @@ struct idr { #define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC) +/** + * idr synchronization (stolen from radix-tree.h) + * + * idr_find() is able to be called locklessly, using RCU. The caller must + * ensure calls to this function are made within rcu_read_lock() regions. + * Other readers (lock-free or otherwise) and modifications may be running + * concurrently. + * + * It is still required that the caller manage the synchronization and + * lifetimes of the items. So if RCU lock-free lookups are used, typically + * this would mean that the items have their own locks, or are amenable to + * lock-free access; and that the items are freed by RCU (or only freed after + * having been deleted from the idr tree *and* a synchronize_rcu() grace + * period). + */ + /* * This is what we export. */ diff --git a/lib/idr.c b/lib/idr.c index 44ab3b2a4eba..21e12af1f231 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -456,7 +456,8 @@ EXPORT_SYMBOL(idr_destroy); * return indicates that @id is not valid or you passed %NULL in * idr_get_new(). * - * The caller must serialize idr_find() vs idr_get_new() and idr_remove(). + * This function can be called under rcu_read_lock(), given that the leaf + * pointers lifetimes are correctly managed. */ void *idr_find(struct idr *idp, int id) { @@ -464,7 +465,7 @@ void *idr_find(struct idr *idp, int id) struct idr_layer *p; n = idp->layers * IDR_BITS; - p = idp->top; + p = rcu_dereference(idp->top); /* Mask off upper bits we don't use for the search. */ id &= MAX_ID_MASK; @@ -474,7 +475,7 @@ void *idr_find(struct idr *idp, int id) while (n > 0 && p) { n -= IDR_BITS; - p = p->ary[(id >> n) & IDR_MASK]; + p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]); } return((void *)p); } @@ -507,7 +508,7 @@ int idr_for_each(struct idr *idp, struct idr_layer **paa = &pa[0]; n = idp->layers * IDR_BITS; - p = idp->top; + p = rcu_dereference(idp->top); max = 1 << n; id = 0; @@ -515,7 +516,7 @@ int idr_for_each(struct idr *idp, while (n > 0 && p) { n -= IDR_BITS; *paa++ = p; - p = p->ary[(id >> n) & IDR_MASK]; + p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]); } if (p) { -- cgit v1.2.3 From 4daa28f6d8f5cda8ea0f55048e3c8811c384cbdd Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:04 -0700 Subject: ipc/sem.c: convert undo structures to struct list_head The undo structures contain two linked lists, the attached patch replaces them with generic struct list_head lists. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul Cc: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 12 ++-- ipc/sem.c | 163 ++++++++++++++++++++++++++++------------------------ 2 files changed, 95 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index c8eaad9e4b72..6a1af1b49a13 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -95,7 +95,7 @@ struct sem_array { struct sem *sem_base; /* ptr to first semaphore in array */ struct sem_queue *sem_pending; /* pending operations to be processed */ struct sem_queue **sem_pending_last; /* last pending operation */ - struct sem_undo *undo; /* undo requests on this array */ + struct list_head list_id; /* undo requests on this array */ unsigned long sem_nsems; /* no. of semaphores in array */ }; @@ -118,8 +118,8 @@ struct sem_queue { * when the process exits. */ struct sem_undo { - struct sem_undo * proc_next; /* next entry on this process */ - struct sem_undo * id_next; /* next entry on this semaphore set */ + struct list_head list_proc; /* per-process list: all undos from one process */ + struct list_head list_id; /* per semaphore array list: all undos for one array */ int semid; /* semaphore set identifier */ short * semadj; /* array of adjustments, one per semaphore */ }; @@ -128,9 +128,9 @@ struct sem_undo { * that may be shared among all a CLONE_SYSVSEM task group. */ struct sem_undo_list { - atomic_t refcnt; - spinlock_t lock; - struct sem_undo *proc_list; + atomic_t refcnt; + spinlock_t lock; + struct list_head list_proc; }; struct sysv_sem { diff --git a/ipc/sem.c b/ipc/sem.c index e9418df5ff3e..4f26c7157356 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -274,7 +274,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) sma->sem_base = (struct sem *) &sma[1]; /* sma->sem_pending = NULL; */ sma->sem_pending_last = &sma->sem_pending; - /* sma->undo = NULL; */ + INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); sem_unlock(sma); @@ -536,7 +536,8 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) * (They will be freed without any further action in exit_sem() * or during the next semop.) */ - for (un = sma->undo; un; un = un->id_next) + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) un->semid = -1; /* Wake up all pending processes and let them fail with EIDRM. */ @@ -763,9 +764,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, for (i = 0; i < nsems; i++) sma->sem_base[i].semval = sem_io[i]; - for (un = sma->undo; un; un = un->id_next) + + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) { for (i = 0; i < nsems; i++) un->semadj[i] = 0; + } sma->sem_ctime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); @@ -797,12 +801,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, { int val = arg.val; struct sem_undo *un; + err = -ERANGE; if (val > SEMVMX || val < 0) goto out_unlock; - for (un = sma->undo; un; un = un->id_next) + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) un->semadj[semnum] = 0; + curr->semval = val; curr->sempid = task_tgid_vnr(current); sma->sem_ctime = get_seconds(); @@ -952,6 +959,8 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) return -ENOMEM; spin_lock_init(&undo_list->lock); atomic_set(&undo_list->refcnt, 1); + INIT_LIST_HEAD(&undo_list->list_proc); + current->sysvsem.undo_list = undo_list; } *undo_listp = undo_list; @@ -960,25 +969,30 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) { - struct sem_undo **last, *un; - - last = &ulp->proc_list; - un = *last; - while(un != NULL) { - if(un->semid==semid) - break; - if(un->semid==-1) { - *last=un->proc_next; - kfree(un); - } else { - last=&un->proc_next; + struct sem_undo *walk, *tmp; + + assert_spin_locked(&ulp->lock); + list_for_each_entry_safe(walk, tmp, &ulp->list_proc, list_proc) { + if (walk->semid == semid) + return walk; + if (walk->semid == -1) { + list_del(&walk->list_proc); + kfree(walk); } - un=*last; } - return un; + return NULL; } -static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) +/** + * find_alloc_undo - Lookup (and if not present create) undo array + * @ns: namespace + * @semid: semaphore array id + * + * The function looks up (and if not present creates) the undo structure. + * The size of the undo structure depends on the size of the semaphore + * array, thus the alloc path is not that straightforward. + */ +static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) { struct sem_array *sma; struct sem_undo_list *ulp; @@ -997,6 +1011,7 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) goto out; /* no undo structure around - allocate one. */ + /* step 1: figure out the size of the semaphore array */ sma = sem_lock_check(ns, semid); if (IS_ERR(sma)) return ERR_PTR(PTR_ERR(sma)); @@ -1004,15 +1019,19 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) nsems = sma->sem_nsems; sem_getref_and_unlock(sma); + /* step 2: allocate new undo structure */ new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); if (!new) { sem_putref(sma); return ERR_PTR(-ENOMEM); } - new->semadj = (short *) &new[1]; - new->semid = semid; + /* step 3: Acquire the lock on the undo list pointer */ spin_lock(&ulp->lock); + + /* step 4: check for races: someone else allocated the undo struct, + * semaphore array was destroyed. + */ un = lookup_undo(ulp, semid); if (un) { spin_unlock(&ulp->lock); @@ -1028,13 +1047,17 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) un = ERR_PTR(-EIDRM); goto out; } - new->proc_next = ulp->proc_list; - ulp->proc_list = new; - new->id_next = sma->undo; - sma->undo = new; + /* step 5: initialize & link new undo structure */ + new->semadj = (short *) &new[1]; + new->semid = semid; + assert_spin_locked(&ulp->lock); + list_add(&new->list_proc, &ulp->list_proc); + assert_spin_locked(&sma->sem_perm.lock); + list_add(&new->list_id, &sma->list_id); + sem_unlock(sma); - un = new; spin_unlock(&ulp->lock); + un = new; out: return un; } @@ -1090,9 +1113,8 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, alter = 1; } -retry_undos: if (undos) { - un = find_undo(ns, semid); + un = find_alloc_undo(ns, semid); if (IS_ERR(un)) { error = PTR_ERR(un); goto out_free; @@ -1107,14 +1129,14 @@ retry_undos: } /* - * semid identifiers are not unique - find_undo may have + * semid identifiers are not unique - find_alloc_undo may have * allocated an undo structure, it was invalidated by an RMID - * and now a new array with received the same id. Check and retry. + * and now a new array with received the same id. Check and fail. */ - if (un && un->semid == -1) { - sem_unlock(sma); - goto retry_undos; - } + error = -EIDRM; + if (un && un->semid == -1) + goto out_unlock_free; + error = -EFBIG; if (max >= sma->sem_nsems) goto out_unlock_free; @@ -1243,56 +1265,44 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) */ void exit_sem(struct task_struct *tsk) { - struct sem_undo_list *undo_list; - struct sem_undo *u, **up; - struct ipc_namespace *ns; + struct sem_undo_list *ulp; + struct sem_undo *un, *tmp; - undo_list = tsk->sysvsem.undo_list; - if (!undo_list) + ulp = tsk->sysvsem.undo_list; + if (!ulp) return; tsk->sysvsem.undo_list = NULL; - if (!atomic_dec_and_test(&undo_list->refcnt)) + if (!atomic_dec_and_test(&ulp->refcnt)) return; - ns = tsk->nsproxy->ipc_ns; - /* There's no need to hold the semundo list lock, as current - * is the last task exiting for this undo list. - */ - for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) { + spin_lock(&ulp->lock); + + list_for_each_entry_safe(un, tmp, &ulp->list_proc, list_proc) { struct sem_array *sma; - int nsems, i; - struct sem_undo *un, **unp; - int semid; - - semid = u->semid; - - if(semid == -1) - continue; - sma = sem_lock(ns, semid); + int i; + + if (un->semid == -1) + goto free; + + sma = sem_lock(tsk->nsproxy->ipc_ns, un->semid); if (IS_ERR(sma)) - continue; + goto free; - if (u->semid == -1) - goto next_entry; + if (un->semid == -1) + goto unlock_free; - BUG_ON(sem_checkid(sma, u->semid)); + BUG_ON(sem_checkid(sma, un->semid)); - /* remove u from the sma->undo list */ - for (unp = &sma->undo; (un = *unp); unp = &un->id_next) { - if (u == un) - goto found; - } - printk ("exit_sem undo list error id=%d\n", u->semid); - goto next_entry; -found: - *unp = un->id_next; - /* perform adjustments registered in u */ - nsems = sma->sem_nsems; - for (i = 0; i < nsems; i++) { + /* remove un from sma->list_id */ + assert_spin_locked(&sma->sem_perm.lock); + list_del(&un->list_id); + + /* perform adjustments registered in un */ + for (i = 0; i < sma->sem_nsems; i++) { struct sem * semaphore = &sma->sem_base[i]; - if (u->semadj[i]) { - semaphore->semval += u->semadj[i]; + if (un->semadj[i]) { + semaphore->semval += un->semadj[i]; /* * Range checks of the new semaphore value, * not defined by sus: @@ -1316,10 +1326,15 @@ found: sma->sem_otime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); -next_entry: +unlock_free: sem_unlock(sma); +free: + assert_spin_locked(&ulp->lock); + list_del(&un->list_proc); + kfree(un); } - kfree(undo_list); + spin_unlock(&ulp->lock); + kfree(ulp); } #ifdef CONFIG_PROC_FS -- cgit v1.2.3 From 2c0c29d414087f3b021059673c20a7088f5f1fff Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:05 -0700 Subject: ipc/sem.c: remove unused entries from struct sem_queue sem_queue.sma and sem_queue.id were never used, the attached patch removes them. Signed-off-by: Manfred Spraul Reviewed-by: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 2 -- ipc/sem.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index 6a1af1b49a13..87756ef1198e 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -107,8 +107,6 @@ struct sem_queue { struct sem_undo * undo; /* undo structure */ int pid; /* process id of requesting process */ int status; /* completion status of operation */ - struct sem_array * sma; /* semaphore array for operations */ - int id; /* internal sem id */ struct sembuf * sops; /* array of pending operations */ int nsops; /* number of operations */ int alter; /* does the operation alter the array? */ diff --git a/ipc/sem.c b/ipc/sem.c index 4f26c7157356..d5ce4000ca17 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1160,12 +1160,10 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, * task into the pending queue and go to sleep. */ - queue.sma = sma; queue.sops = sops; queue.nsops = nsops; queue.undo = un; queue.pid = task_tgid_vnr(current); - queue.id = semid; queue.alter = alter; if (alter) append_to_queue(sma ,&queue); -- cgit v1.2.3 From a1193f8ec091cd8fd309cc2982abe4499f6f2b4d Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:06 -0700 Subject: ipc/sem.c: convert sem_array.sem_pending to struct list_head sem_array.sem_pending is a double linked list, the attached patch converts it to struct list_head. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul Reviewed-by: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 12 +++---- ipc/sem.c | 92 ++++++++++++++++++++--------------------------------- 2 files changed, 40 insertions(+), 64 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index 87756ef1198e..d42599395d79 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -93,21 +93,19 @@ struct sem_array { time_t sem_otime; /* last semop time */ time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ - struct sem_queue *sem_pending; /* pending operations to be processed */ - struct sem_queue **sem_pending_last; /* last pending operation */ + struct list_head sem_pending; /* pending operations to be processed */ struct list_head list_id; /* undo requests on this array */ unsigned long sem_nsems; /* no. of semaphores in array */ }; /* One queue for each sleeping process in the system. */ struct sem_queue { - struct sem_queue * next; /* next entry in the queue */ - struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */ - struct task_struct* sleeper; /* this process */ - struct sem_undo * undo; /* undo structure */ + struct list_head list; /* queue of pending operations */ + struct task_struct *sleeper; /* this process */ + struct sem_undo *undo; /* undo structure */ int pid; /* process id of requesting process */ int status; /* completion status of operation */ - struct sembuf * sops; /* array of pending operations */ + struct sembuf *sops; /* array of pending operations */ int nsops; /* number of operations */ int alter; /* does the operation alter the array? */ }; diff --git a/ipc/sem.c b/ipc/sem.c index d5ce4000ca17..3ca232736b31 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -272,8 +272,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) ns->used_sems += nsems; sma->sem_base = (struct sem *) &sma[1]; - /* sma->sem_pending = NULL; */ - sma->sem_pending_last = &sma->sem_pending; + INIT_LIST_HEAD(&sma->sem_pending); INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); @@ -331,38 +330,6 @@ asmlinkage long sys_semget(key_t key, int nsems, int semflg) return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); } -/* Manage the doubly linked list sma->sem_pending as a FIFO: - * insert new queue elements at the tail sma->sem_pending_last. - */ -static inline void append_to_queue (struct sem_array * sma, - struct sem_queue * q) -{ - *(q->prev = sma->sem_pending_last) = q; - *(sma->sem_pending_last = &q->next) = NULL; -} - -static inline void prepend_to_queue (struct sem_array * sma, - struct sem_queue * q) -{ - q->next = sma->sem_pending; - *(q->prev = &sma->sem_pending) = q; - if (q->next) - q->next->prev = &q->next; - else /* sma->sem_pending_last == &sma->sem_pending */ - sma->sem_pending_last = &q->next; -} - -static inline void remove_from_queue (struct sem_array * sma, - struct sem_queue * q) -{ - *(q->prev) = q->next; - if (q->next) - q->next->prev = q->prev; - else /* sma->sem_pending_last == &q->next */ - sma->sem_pending_last = q->prev; - q->prev = NULL; /* mark as removed */ -} - /* * Determine whether a sequence of semaphore operations would succeed * all at once. Return 0 if yes, 1 if need to sleep, else return error code. @@ -438,16 +405,15 @@ static void update_queue (struct sem_array * sma) int error; struct sem_queue * q; - q = sma->sem_pending; - while(q) { + q = list_entry(sma->sem_pending.next, struct sem_queue, list); + while (&q->list != &sma->sem_pending) { error = try_atomic_semop(sma, q->sops, q->nsops, q->undo, q->pid); /* Does q->sleeper still need to sleep? */ if (error <= 0) { struct sem_queue *n; - remove_from_queue(sma,q); - q->status = IN_WAKEUP; + /* * Continue scanning. The next operation * that must be checked depends on the type of the @@ -458,11 +424,26 @@ static void update_queue (struct sem_array * sma) * for semaphore values to become 0. * - if the operation didn't modify the array, * then just continue. + * The order of list_del() and reading ->next + * is crucial: In the former case, the list_del() + * must be done first [because we might be the + * first entry in ->sem_pending], in the latter + * case the list_del() must be done last + * [because the list is invalid after the list_del()] */ - if (q->alter) - n = sma->sem_pending; - else - n = q->next; + if (q->alter) { + list_del(&q->list); + n = list_entry(sma->sem_pending.next, + struct sem_queue, list); + } else { + n = list_entry(q->list.next, struct sem_queue, + list); + list_del(&q->list); + } + + /* wake up the waiting thread */ + q->status = IN_WAKEUP; + wake_up_process(q->sleeper); /* hands-off: q will disappear immediately after * writing q->status. @@ -471,7 +452,7 @@ static void update_queue (struct sem_array * sma) q->status = error; q = n; } else { - q = q->next; + q = list_entry(q->list.next, struct sem_queue, list); } } } @@ -491,7 +472,7 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) struct sem_queue * q; semncnt = 0; - for (q = sma->sem_pending; q; q = q->next) { + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; int i; @@ -503,13 +484,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) } return semncnt; } + static int count_semzcnt (struct sem_array * sma, ushort semnum) { int semzcnt; struct sem_queue * q; semzcnt = 0; - for (q = sma->sem_pending; q; q = q->next) { + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; int i; @@ -529,7 +511,7 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) { struct sem_undo *un; - struct sem_queue *q; + struct sem_queue *q, *t; struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); /* Invalidate the existing undo structures for this semaphore set. @@ -541,17 +523,14 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) un->semid = -1; /* Wake up all pending processes and let them fail with EIDRM. */ - q = sma->sem_pending; - while(q) { - struct sem_queue *n; - /* lazy remove_from_queue: we are killing the whole queue */ - q->prev = NULL; - n = q->next; + + list_for_each_entry_safe(q, t, &sma->sem_pending, list) { + list_del(&q->list); + q->status = IN_WAKEUP; wake_up_process(q->sleeper); /* doesn't sleep */ smp_wmb(); q->status = -EIDRM; /* hands-off q */ - q = n; } /* Remove the semaphore set from the IDR */ @@ -1166,9 +1145,9 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, queue.pid = task_tgid_vnr(current); queue.alter = alter; if (alter) - append_to_queue(sma ,&queue); + list_add_tail(&queue.list, &sma->sem_pending); else - prepend_to_queue(sma ,&queue); + list_add(&queue.list, &sma->sem_pending); queue.status = -EINTR; queue.sleeper = current; @@ -1194,7 +1173,6 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, sma = sem_lock(ns, semid); if (IS_ERR(sma)) { - BUG_ON(queue.prev != NULL); error = -EIDRM; goto out_free; } @@ -1212,7 +1190,7 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, */ if (timeout && jiffies_left == 0) error = -EAGAIN; - remove_from_queue(sma,&queue); + list_del(&queue.list); goto out_unlock_free; out_unlock_free: -- cgit v1.2.3 From 380af1b33b3ff92df5cda96329b58f5d1b6b5a53 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:06 -0700 Subject: ipc/sem.c: rewrite undo list locking The attached patch: - reverses the locking order of ulp->lock and sem_lock: Previously, it was first ulp->lock, then inside sem_lock. Now it's the other way around. - converts the undo structure to rcu. Benefits: - With the old locking order, IPC_RMID could not kfree the undo structures. The stale entries remained in the linked lists and were released later. - The patch fixes a a race in semtimedop(): if both IPC_RMID and a semget() that recreates exactly the same id happen between find_alloc_undo() and sem_lock, then semtimedop() would access already kfree'd memory. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul Reviewed-by: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 6 ++- ipc/sem.c | 147 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 98 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index d42599395d79..1b191c176bcd 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -78,6 +78,7 @@ struct seminfo { #ifdef __KERNEL__ #include +#include struct task_struct; @@ -114,7 +115,10 @@ struct sem_queue { * when the process exits. */ struct sem_undo { - struct list_head list_proc; /* per-process list: all undos from one process */ + struct list_head list_proc; /* per-process list: all undos from one process. */ + /* rcu protected */ + struct rcu_head rcu; /* rcu struct for sem_undo() */ + struct sem_undo_list *ulp; /* sem_undo_list for the process */ struct list_head list_id; /* per semaphore array list: all undos for one array */ int semid; /* semaphore set identifier */ short * semadj; /* array of adjustments, one per semaphore */ diff --git a/ipc/sem.c b/ipc/sem.c index 3ca232736b31..bf1bc36cb7ee 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -504,27 +504,35 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) return semzcnt; } +void free_un(struct rcu_head *head) +{ + struct sem_undo *un = container_of(head, struct sem_undo, rcu); + kfree(un); +} + /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex * remains locked on exit. */ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) { - struct sem_undo *un; - struct sem_queue *q, *t; + struct sem_undo *un, *tu; + struct sem_queue *q, *tq; struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); - /* Invalidate the existing undo structures for this semaphore set. - * (They will be freed without any further action in exit_sem() - * or during the next semop.) - */ + /* Free the existing undo structures for this semaphore set. */ assert_spin_locked(&sma->sem_perm.lock); - list_for_each_entry(un, &sma->list_id, list_id) + list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { + list_del(&un->list_id); + spin_lock(&un->ulp->lock); un->semid = -1; + list_del_rcu(&un->list_proc); + spin_unlock(&un->ulp->lock); + call_rcu(&un->rcu, free_un); + } /* Wake up all pending processes and let them fail with EIDRM. */ - - list_for_each_entry_safe(q, t, &sma->sem_pending, list) { + list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { list_del(&q->list); q->status = IN_WAKEUP; @@ -948,16 +956,11 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) { - struct sem_undo *walk, *tmp; + struct sem_undo *walk; - assert_spin_locked(&ulp->lock); - list_for_each_entry_safe(walk, tmp, &ulp->list_proc, list_proc) { + list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) { if (walk->semid == semid) return walk; - if (walk->semid == -1) { - list_del(&walk->list_proc); - kfree(walk); - } } return NULL; } @@ -970,6 +973,8 @@ static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) * The function looks up (and if not present creates) the undo structure. * The size of the undo structure depends on the size of the semaphore * array, thus the alloc path is not that straightforward. + * Lifetime-rules: sem_undo is rcu-protected, on success, the function + * performs a rcu_read_lock(). */ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) { @@ -983,11 +988,13 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) if (error) return ERR_PTR(error); + rcu_read_lock(); spin_lock(&ulp->lock); un = lookup_undo(ulp, semid); spin_unlock(&ulp->lock); if (likely(un!=NULL)) goto out; + rcu_read_unlock(); /* no undo structure around - allocate one. */ /* step 1: figure out the size of the semaphore array */ @@ -1005,38 +1012,38 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) return ERR_PTR(-ENOMEM); } - /* step 3: Acquire the lock on the undo list pointer */ - spin_lock(&ulp->lock); - - /* step 4: check for races: someone else allocated the undo struct, - * semaphore array was destroyed. - */ - un = lookup_undo(ulp, semid); - if (un) { - spin_unlock(&ulp->lock); - kfree(new); - sem_putref(sma); - goto out; - } + /* step 3: Acquire the lock on semaphore array */ sem_lock_and_putref(sma); if (sma->sem_perm.deleted) { sem_unlock(sma); - spin_unlock(&ulp->lock); kfree(new); un = ERR_PTR(-EIDRM); goto out; } + spin_lock(&ulp->lock); + + /* + * step 4: check for races: did someone else allocate the undo struct? + */ + un = lookup_undo(ulp, semid); + if (un) { + kfree(new); + goto success; + } /* step 5: initialize & link new undo structure */ new->semadj = (short *) &new[1]; + new->ulp = ulp; new->semid = semid; assert_spin_locked(&ulp->lock); - list_add(&new->list_proc, &ulp->list_proc); + list_add_rcu(&new->list_proc, &ulp->list_proc); assert_spin_locked(&sma->sem_perm.lock); list_add(&new->list_id, &sma->list_id); + un = new; - sem_unlock(sma); +success: spin_unlock(&ulp->lock); - un = new; + rcu_read_lock(); + sem_unlock(sma); out: return un; } @@ -1103,6 +1110,8 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, sma = sem_lock_check(ns, semid); if (IS_ERR(sma)) { + if (un) + rcu_read_unlock(); error = PTR_ERR(sma); goto out_free; } @@ -1111,10 +1120,26 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, * semid identifiers are not unique - find_alloc_undo may have * allocated an undo structure, it was invalidated by an RMID * and now a new array with received the same id. Check and fail. + * This case can be detected checking un->semid. The existance of + * "un" itself is guaranteed by rcu. */ error = -EIDRM; - if (un && un->semid == -1) - goto out_unlock_free; + if (un) { + if (un->semid == -1) { + rcu_read_unlock(); + goto out_unlock_free; + } else { + /* + * rcu lock can be released, "un" cannot disappear: + * - sem_lock is acquired, thus IPC_RMID is + * impossible. + * - exit_sem is impossible, it always operates on + * current (or a dead task). + */ + + rcu_read_unlock(); + } + } error = -EFBIG; if (max >= sma->sem_nsems) @@ -1242,7 +1267,6 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) void exit_sem(struct task_struct *tsk) { struct sem_undo_list *ulp; - struct sem_undo *un, *tmp; ulp = tsk->sysvsem.undo_list; if (!ulp) @@ -1252,28 +1276,47 @@ void exit_sem(struct task_struct *tsk) if (!atomic_dec_and_test(&ulp->refcnt)) return; - spin_lock(&ulp->lock); - - list_for_each_entry_safe(un, tmp, &ulp->list_proc, list_proc) { + for (;;) { struct sem_array *sma; + struct sem_undo *un; + int semid; int i; - if (un->semid == -1) - goto free; + rcu_read_lock(); + un = list_entry(rcu_dereference(ulp->list_proc.next), + struct sem_undo, list_proc); + if (&un->list_proc == &ulp->list_proc) + semid = -1; + else + semid = un->semid; + rcu_read_unlock(); - sma = sem_lock(tsk->nsproxy->ipc_ns, un->semid); - if (IS_ERR(sma)) - goto free; + if (semid == -1) + break; - if (un->semid == -1) - goto unlock_free; + sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid); - BUG_ON(sem_checkid(sma, un->semid)); + /* exit_sem raced with IPC_RMID, nothing to do */ + if (IS_ERR(sma)) + continue; - /* remove un from sma->list_id */ + un = lookup_undo(ulp, semid); + if (un == NULL) { + /* exit_sem raced with IPC_RMID+semget() that created + * exactly the same semid. Nothing to do. + */ + sem_unlock(sma); + continue; + } + + /* remove un from the linked lists */ assert_spin_locked(&sma->sem_perm.lock); list_del(&un->list_id); + spin_lock(&ulp->lock); + list_del_rcu(&un->list_proc); + spin_unlock(&ulp->lock); + /* perform adjustments registered in un */ for (i = 0; i < sma->sem_nsems; i++) { struct sem * semaphore = &sma->sem_base[i]; @@ -1302,14 +1345,10 @@ void exit_sem(struct task_struct *tsk) sma->sem_otime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); -unlock_free: sem_unlock(sma); -free: - assert_spin_locked(&ulp->lock); - list_del(&un->list_proc); - kfree(un); + + call_rcu(&un->rcu, free_un); } - spin_unlock(&ulp->lock); kfree(ulp); } -- cgit v1.2.3 From 9eefe520c814f6f62c5d36a2ddcd3fb99dfdb30e Mon Sep 17 00:00:00 2001 From: Nadia Derbey Date: Fri, 25 Jul 2008 01:48:08 -0700 Subject: ipc: do not use a negative value to re-enable msgmni automatic recomputing This patch proposes an alternative to the "magical positive-versus-negative number trick" Andrew complained about last week in http://lkml.org/lkml/2008/6/24/418. This had been introduced with the patches that scale msgmni to the amount of lowmem. With these patches, msgmni has a registered notification routine that recomputes msgmni value upon memory add/remove or ipc namespace creation/ removal. When msgmni is changed from user space (i.e. value written to the proc file), that notification routine is unregistered, and the way to make it registered back is to write a negative value into the proc file. This is the "magical positive-versus-negative number trick". To fix this, a new proc file is introduced: /proc/sys/kernel/auto_msgmni. This file acts as ON/OFF for msgmni automatic recomputing. With this patch, the process is the following: 1) kernel boots in "automatic recomputing mode" /proc/sys/kernel/msgmni contains the value that has been computed (depends on lowmem) /proc/sys/kernel/automatic_msgmni contains "1" 2) echo > /proc/sys/kernel/msgmni . sets msg_ctlmni to . de-activates automatic recomputing (i.e. if, say, some memory is added msgmni won't be recomputed anymore) . /proc/sys/kernel/automatic_msgmni now contains "0" 3) echo "0" > /proc/sys/kernel/automatic_msgmni . de-activates msgmni automatic recomputing this has the same effect as 2) except that msg_ctlmni's value stays blocked at its current value) 3) echo "1" > /proc/sys/kernel/automatic_msgmni . recomputes msgmni's value based on the current available memory size and number of ipc namespaces . re-activates automatic recomputing for msgmni. Signed-off-by: Nadia Derbey Cc: Solofo Ramangalahy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ipc_namespace.h | 3 +- ipc/ipc_sysctl.c | 72 +++++++++++++++++++++++++++++++++++-------- ipc/ipcns_notifier.c | 20 +++++++++--- 3 files changed, 76 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h index ea6c18a8b0d4..ea330f9e7100 100644 --- a/include/linux/ipc_namespace.h +++ b/include/linux/ipc_namespace.h @@ -36,6 +36,7 @@ struct ipc_namespace { int msg_ctlmni; atomic_t msg_bytes; atomic_t msg_hdrs; + int auto_msgmni; size_t shm_ctlmax; size_t shm_ctlall; @@ -53,7 +54,7 @@ extern atomic_t nr_ipc_ns; extern int register_ipcns_notifier(struct ipc_namespace *); extern int cond_register_ipcns_notifier(struct ipc_namespace *); -extern int unregister_ipcns_notifier(struct ipc_namespace *); +extern void unregister_ipcns_notifier(struct ipc_namespace *); extern int ipcns_notify(unsigned long); #else /* CONFIG_SYSVIPC */ diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index d3497465cc0a..69bc85978ba0 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c @@ -27,15 +27,17 @@ static void *get_ipc(ctl_table *table) } /* - * Routine that is called when a tunable has successfully been changed by - * hand and it has a callback routine registered on the ipc namespace notifier - * chain: we don't want such tunables to be recomputed anymore upon memory - * add/remove or ipc namespace creation/removal. - * They can come back to a recomputable state by being set to a <0 value. + * Routine that is called when the file "auto_msgmni" has successfully been + * written. + * Two values are allowed: + * 0: unregister msgmni's callback routine from the ipc namespace notifier + * chain. This means that msgmni won't be recomputed anymore upon memory + * add/remove or ipc namespace creation/removal. + * 1: register back the callback routine. */ -static void tunable_set_callback(int val) +static void ipc_auto_callback(int val) { - if (val >= 0) + if (!val) unregister_ipcns_notifier(current->nsproxy->ipc_ns); else { /* @@ -71,7 +73,12 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write, rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); if (write && !rc && lenp_bef == *lenp) - tunable_set_callback(*((int *)(ipc_table.data))); + /* + * Tunable has successfully been changed by hand. Disable its + * automatic adjustment. This simply requires unregistering + * the notifiers that trigger recalculation. + */ + unregister_ipcns_notifier(current->nsproxy->ipc_ns); return rc; } @@ -87,10 +94,39 @@ static int proc_ipc_doulongvec_minmax(ctl_table *table, int write, lenp, ppos); } +static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct ctl_table ipc_table; + size_t lenp_bef = *lenp; + int oldval; + int rc; + + memcpy(&ipc_table, table, sizeof(ipc_table)); + ipc_table.data = get_ipc(table); + oldval = *((int *)(ipc_table.data)); + + rc = proc_dointvec_minmax(&ipc_table, write, filp, buffer, lenp, ppos); + + if (write && !rc && lenp_bef == *lenp) { + int newval = *((int *)(ipc_table.data)); + /* + * The file "auto_msgmni" has correctly been set. + * React by (un)registering the corresponding tunable, if the + * value has changed. + */ + if (newval != oldval) + ipc_auto_callback(newval); + } + + return rc; +} + #else #define proc_ipc_doulongvec_minmax NULL #define proc_ipc_dointvec NULL #define proc_ipc_callback_dointvec NULL +#define proc_ipcauto_dointvec_minmax NULL #endif #ifdef CONFIG_SYSCTL_SYSCALL @@ -142,14 +178,11 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval, newlen); - if (newval && newlen && rc > 0) { + if (newval && newlen && rc > 0) /* * Tunable has successfully been changed from userland */ - int *data = get_ipc(table); - - tunable_set_callback(*data); - } + unregister_ipcns_notifier(current->nsproxy->ipc_ns); return rc; } @@ -158,6 +191,9 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, #define sysctl_ipc_registered_data NULL #endif +static int zero; +static int one = 1; + static struct ctl_table ipc_kern_table[] = { { .ctl_name = KERN_SHMMAX, @@ -222,6 +258,16 @@ static struct ctl_table ipc_kern_table[] = { .proc_handler = proc_ipc_dointvec, .strategy = sysctl_ipc_data, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "auto_msgmni", + .data = &init_ipc_ns.auto_msgmni, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_ipcauto_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, {} }; diff --git a/ipc/ipcns_notifier.c b/ipc/ipcns_notifier.c index 70ff09183f7b..b9b31a4f77e1 100644 --- a/ipc/ipcns_notifier.c +++ b/ipc/ipcns_notifier.c @@ -55,25 +55,35 @@ static int ipcns_callback(struct notifier_block *self, int register_ipcns_notifier(struct ipc_namespace *ns) { + int rc; + memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); ns->ipcns_nb.notifier_call = ipcns_callback; ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; - return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); + rc = blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); + if (!rc) + ns->auto_msgmni = 1; + return rc; } int cond_register_ipcns_notifier(struct ipc_namespace *ns) { + int rc; + memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); ns->ipcns_nb.notifier_call = ipcns_callback; ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; - return blocking_notifier_chain_cond_register(&ipcns_chain, + rc = blocking_notifier_chain_cond_register(&ipcns_chain, &ns->ipcns_nb); + if (!rc) + ns->auto_msgmni = 1; + return rc; } -int unregister_ipcns_notifier(struct ipc_namespace *ns) +void unregister_ipcns_notifier(struct ipc_namespace *ns) { - return blocking_notifier_chain_unregister(&ipcns_chain, - &ns->ipcns_nb); + blocking_notifier_chain_unregister(&ipcns_chain, &ns->ipcns_nb); + ns->auto_msgmni = 0; } int ipcns_notify(unsigned long val) -- cgit v1.2.3 From d805dda412346225a50af2d399d958a4bc676c38 Mon Sep 17 00:00:00 2001 From: Abdel Benamrouche Date: Fri, 25 Jul 2008 01:48:25 -0700 Subject: fs/partition/check.c: fix return value warning fs/partitions/check.c:381: warning: ignoring return value of ___device_add___, declared with attribute warn_unused_result [akpm@linux-foundation.org: multiple-return-statements-per-function are evil] Signed-off-by: Abdel Benamrouche Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/partitions/check.c | 28 ++++++++++++++++++++++------ include/linux/genhd.h | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index efef715135d3..2e6413fbd2d8 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -344,18 +344,18 @@ static ssize_t whole_disk_show(struct device *dev, static DEVICE_ATTR(whole_disk, S_IRUSR | S_IRGRP | S_IROTH, whole_disk_show, NULL); -void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags) +int add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags) { struct hd_struct *p; int err; p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) - return; + return -ENOMEM; if (!init_part_stats(p)) { - kfree(p); - return; + err = -ENOMEM; + goto out0; } p->start_sect = start; p->nr_sects = len; @@ -378,15 +378,31 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, /* delay uevent until 'holders' subdir is created */ p->dev.uevent_suppress = 1; - device_add(&p->dev); + err = device_add(&p->dev); + if (err) + goto out1; partition_sysfs_add_subdir(p); p->dev.uevent_suppress = 0; - if (flags & ADDPART_FLAG_WHOLEDISK) + if (flags & ADDPART_FLAG_WHOLEDISK) { err = device_create_file(&p->dev, &dev_attr_whole_disk); + if (err) + goto out2; + } /* suppress uevent if the disk supresses it */ if (!disk->dev.uevent_suppress) kobject_uevent(&p->dev.kobj, KOBJ_ADD); + + return 0; + +out2: + device_del(&p->dev); +out1: + put_device(&p->dev); + free_part_stats(p); +out0: + kfree(p); + return err; } /* Not exported, helper to add_disk(). */ diff --git a/include/linux/genhd.h b/include/linux/genhd.h index e8787417f65a..118216f1bd3c 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -541,7 +541,7 @@ extern dev_t blk_lookup_devt(const char *name, int part); extern char *disk_name (struct gendisk *hd, int part, char *buf); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); -extern void add_partition(struct gendisk *, int, sector_t, sector_t, int); +extern int __must_check add_partition(struct gendisk *, int, sector_t, sector_t, int); extern void delete_partition(struct gendisk *, int); extern void printk_all_partitions(void); -- cgit v1.2.3 From 6e644c3126149b65460610fe5a00d8a162092abe Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:48:28 -0700 Subject: move proc_kmsg_operations to fs/proc/internal.h This patch moves the extern of struct proc_kmsg_operations to fs/proc/internal.h and adds an #include "internal.h" to fs/proc/kmsg.c so that the latter sees the former. Signed-off-by: Adrian Bunk Cc: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/internal.h | 1 + fs/proc/kmsg.c | 2 ++ include/linux/proc_fs.h | 1 - 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 28cbca805905..8d67616e7bb0 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -63,6 +63,7 @@ extern const struct file_operations proc_smaps_operations; extern const struct file_operations proc_clear_refs_operations; extern const struct file_operations proc_pagemap_operations; extern const struct file_operations proc_net_operations; +extern const struct file_operations proc_kmsg_operations; extern const struct inode_operations proc_net_inode_operations; void free_proc_entry(struct proc_dir_entry *de); diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index ff3b90b56e9d..9fd5df3f40ce 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -15,6 +15,8 @@ #include #include +#include "internal.h" + extern wait_queue_head_t log_wait; extern int do_syslog(int type, char __user *bug, int count); diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 15a9eaf4a802..cdabc2fc02f7 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -138,7 +138,6 @@ extern int proc_readdir(struct file *, void *, filldir_t); extern struct dentry *proc_lookup(struct inode *, struct dentry *, struct nameidata *); extern const struct file_operations proc_kcore_operations; -extern const struct file_operations proc_kmsg_operations; extern const struct file_operations ppc_htab_operations; extern int pid_ns_prepare_proc(struct pid_namespace *ns); -- cgit v1.2.3 From 881adb85358309ea9c6f707394002719982ec607 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 25 Jul 2008 01:48:29 -0700 Subject: proc: always do ->release Current two-stage scheme of removing PDE emphasizes one bug in proc: open rmmod remove_proc_entry close ->release won't be called because ->proc_fops were cleared. In simple cases it's small memory leak. For every ->open, ->release has to be done. List of openers is introduced which is traversed at remove_proc_entry() if neeeded. Discussions with Al long ago (sigh). Signed-off-by: Alexey Dobriyan Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/generic.c | 14 ++++++++++ fs/proc/inode.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- fs/proc/internal.h | 7 +++++ include/linux/proc_fs.h | 1 + 4 files changed, 92 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 43e54e86cefd..bc0a0dd2d844 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -597,6 +597,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, ent->pde_users = 0; spin_lock_init(&ent->pde_unload_lock); ent->pde_unload_completion = NULL; + INIT_LIST_HEAD(&ent->pde_openers); out: return ent; } @@ -789,6 +790,19 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) spin_unlock(&de->pde_unload_lock); continue_removing: + spin_lock(&de->pde_unload_lock); + while (!list_empty(&de->pde_openers)) { + struct pde_opener *pdeo; + + pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); + list_del(&pdeo->lh); + spin_unlock(&de->pde_unload_lock); + pdeo->release(pdeo->inode, pdeo->file); + kfree(pdeo); + spin_lock(&de->pde_unload_lock); + } + spin_unlock(&de->pde_unload_lock); + if (S_ISDIR(de->mode)) parent->nlink--; de->nlink = 0; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index b08d10017911..354c08485825 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -126,12 +126,17 @@ static const struct super_operations proc_sops = { .remount_fs = proc_remount, }; -static void pde_users_dec(struct proc_dir_entry *pde) +static void __pde_users_dec(struct proc_dir_entry *pde) { - spin_lock(&pde->pde_unload_lock); pde->pde_users--; if (pde->pde_unload_completion && pde->pde_users == 0) complete(pde->pde_unload_completion); +} + +static void pde_users_dec(struct proc_dir_entry *pde) +{ + spin_lock(&pde->pde_unload_lock); + __pde_users_dec(pde); spin_unlock(&pde->pde_unload_lock); } @@ -318,36 +323,97 @@ static int proc_reg_open(struct inode *inode, struct file *file) struct proc_dir_entry *pde = PDE(inode); int rv = 0; int (*open)(struct inode *, struct file *); + int (*release)(struct inode *, struct file *); + struct pde_opener *pdeo; + + /* + * What for, you ask? Well, we can have open, rmmod, remove_proc_entry + * sequence. ->release won't be called because ->proc_fops will be + * cleared. Depending on complexity of ->release, consequences vary. + * + * We can't wait for mercy when close will be done for real, it's + * deadlockable: rmmod foo release + * by hand in remove_proc_entry(). For this, save opener's credentials + * for later. + */ + pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); + if (!pdeo) + return -ENOMEM; spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); + kfree(pdeo); return rv; } pde->pde_users++; open = pde->proc_fops->open; + release = pde->proc_fops->release; spin_unlock(&pde->pde_unload_lock); if (open) rv = open(inode, file); - pde_users_dec(pde); + spin_lock(&pde->pde_unload_lock); + if (rv == 0 && release) { + /* To know what to release. */ + pdeo->inode = inode; + pdeo->file = file; + /* Strictly for "too late" ->release in proc_reg_release(). */ + pdeo->release = release; + list_add(&pdeo->lh, &pde->pde_openers); + } else + kfree(pdeo); + __pde_users_dec(pde); + spin_unlock(&pde->pde_unload_lock); return rv; } +static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, + struct inode *inode, struct file *file) +{ + struct pde_opener *pdeo; + + list_for_each_entry(pdeo, &pde->pde_openers, lh) { + if (pdeo->inode == inode && pdeo->file == file) + return pdeo; + } + return NULL; +} + static int proc_reg_release(struct inode *inode, struct file *file) { struct proc_dir_entry *pde = PDE(inode); int rv = 0; int (*release)(struct inode *, struct file *); + struct pde_opener *pdeo; spin_lock(&pde->pde_unload_lock); + pdeo = find_pde_opener(pde, inode, file); if (!pde->proc_fops) { - spin_unlock(&pde->pde_unload_lock); + /* + * Can't simply exit, __fput() will think that everything is OK, + * and move on to freeing struct file. remove_proc_entry() will + * find slacker in opener's list and will try to do non-trivial + * things with struct file. Therefore, remove opener from list. + * + * But if opener is removed from list, who will ->release it? + */ + if (pdeo) { + list_del(&pdeo->lh); + spin_unlock(&pde->pde_unload_lock); + rv = pdeo->release(inode, file); + kfree(pdeo); + } else + spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; release = pde->proc_fops->release; + if (pdeo) { + list_del(&pdeo->lh); + kfree(pdeo); + } spin_unlock(&pde->pde_unload_lock); if (release) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 8d67616e7bb0..442202314d53 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -89,3 +89,10 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *ino, struct dentry *dentry); int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent, filldir_t filldir); + +struct pde_opener { + struct inode *inode; + struct file *file; + int (*release)(struct inode *, struct file *); + struct list_head lh; +}; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index cdabc2fc02f7..f560d1705afe 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -79,6 +79,7 @@ struct proc_dir_entry { int pde_users; /* number of callers into module in progress */ spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ struct completion *pde_unload_completion; + struct list_head pde_openers; /* who did ->open, but not ->release */ }; struct kcore_list { -- cgit v1.2.3 From 3ae4eed34be0177a8e003411a84e4ee212adbced Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:48:34 -0700 Subject: proper pid{hash,map}_init() prototypes This patch adds proper prototypes for pid{hash,map}_init() in include/linux/pid_namespace.h Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid_namespace.h | 3 +++ init/main.c | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index caff5283d15c..1a49ab5ec7b9 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -85,4 +85,7 @@ static inline struct task_struct *task_child_reaper(struct task_struct *tsk) return tsk->nsproxy->pid_ns->child_reaper; } +void pidhash_init(void); +void pidmap_init(void); + #endif /* _LINUX_PID_NS_H */ diff --git a/init/main.c b/init/main.c index 2769dc031c62..0604cbcaf1e4 100644 --- a/init/main.c +++ b/init/main.c @@ -87,8 +87,6 @@ extern void init_IRQ(void); extern void fork_init(unsigned long); extern void mca_init(void); extern void sbus_init(void); -extern void pidhash_init(void); -extern void pidmap_init(void); extern void prio_tree_init(void); extern void radix_tree_init(void); extern void free_initmem(void); -- cgit v1.2.3 From 33166b1ffca5e1945246bcaa77d72a22b0d3e531 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 25 Jul 2008 01:48:35 -0700 Subject: shrink struct pid by removing padding on 64 bit builds When struct pid is built on a 64 bit platform gcc has to insert padding to maintain the correct alignment, by simply reordering its members the memory usage shrinks from 88 bytes to 80. I've successfully run with this patch on my desktop AMD64 machine. There are no significant kernel size changes to a default config.X86_64 on the latest git v2.6.26-rc1 text data bss dec hex filename 5404828 976760 734280 7115868 6c945c vmlinux 5404811 976760 734280 7115851 6c944b vmlinux.pid-patch Acked-by: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pid.h b/include/linux/pid.h index c21c7e8124a7..6f084b9e2c40 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -57,10 +57,10 @@ struct upid { struct pid { atomic_t count; + unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; - unsigned int level; struct upid numbers[1]; }; -- cgit v1.2.3 From 19b0cfcca41dd772065671ad0584e1cea0f3fd13 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:35 -0700 Subject: pidns: remove now unused kill_proc function This function operated on a pid_t to kill a task, which is no longer valid in a containerized system. It has finally lost all its users and we can safely remove it from the tree. Signed-off-by: Pavel Emelyanov Cc: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 1 - kernel/signal.c | 12 ------------ 2 files changed, 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 0560999eb1db..134cb5cb506c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1800,7 +1800,6 @@ extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); extern int send_sig(int, struct task_struct *, int); extern void zap_other_threads(struct task_struct *p); -extern int kill_proc(pid_t, int, int); extern struct sigqueue *sigqueue_alloc(void); extern void sigqueue_free(struct sigqueue *); extern int send_sigqueue(struct sigqueue *, struct task_struct *, int group); diff --git a/kernel/signal.c b/kernel/signal.c index 5c7b7eaa0dc6..82c3545596c5 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1228,17 +1228,6 @@ int kill_pid(struct pid *pid, int sig, int priv) } EXPORT_SYMBOL(kill_pid); -int -kill_proc(pid_t pid, int sig, int priv) -{ - int ret; - - rcu_read_lock(); - ret = kill_pid_info(sig, __si_special(priv), find_pid(pid)); - rcu_read_unlock(); - return ret; -} - /* * These functions support sending signals using preallocated sigqueue * structures. This is needed "because realtime applications cannot @@ -1906,7 +1895,6 @@ EXPORT_SYMBOL(recalc_sigpending); EXPORT_SYMBOL_GPL(dequeue_signal); EXPORT_SYMBOL(flush_signals); EXPORT_SYMBOL(force_sig); -EXPORT_SYMBOL(kill_proc); EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); -- cgit v1.2.3 From e49859e71e0318b564de1546bdc30fab738f9deb Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:36 -0700 Subject: pidns: remove now unused find_pid function. This one had the only users so far - the kill_proc, which is removed, so drop this (invalid in namespaced world) call too. And of course - erase all references on it from comments. Signed-off-by: Pavel Emelyanov Cc: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid.h | 4 +--- include/linux/sched.h | 2 +- kernel/pid.c | 8 +------- 3 files changed, 3 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pid.h b/include/linux/pid.h index 6f084b9e2c40..ff1b2a5814d4 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -48,7 +48,7 @@ enum pid_type */ struct upid { - /* Try to keep pid_chain in the same cacheline as nr for find_pid */ + /* Try to keep pid_chain in the same cacheline as nr for find_vpid */ int nr; struct pid_namespace *ns; struct hlist_node pid_chain; @@ -105,14 +105,12 @@ extern struct pid_namespace init_pid_ns; * or rcu_read_lock() held. * * find_pid_ns() finds the pid in the namespace specified - * find_pid() find the pid by its global id, i.e. in the init namespace * find_vpid() finr the pid by its virtual id, i.e. in the current namespace * * see also find_task_by_pid() set in include/linux/sched.h */ extern struct pid *find_pid_ns(int nr, struct pid_namespace *ns); extern struct pid *find_vpid(int nr); -extern struct pid *find_pid(int nr); /* * Lookup a PID in the hash table, and return with it's count elevated. diff --git a/include/linux/sched.h b/include/linux/sched.h index 134cb5cb506c..182da1550fad 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1718,7 +1718,7 @@ extern struct pid_namespace init_pid_ns; * find_task_by_pid(): * finds a task by its global pid * - * see also find_pid() etc in include/linux/pid.h + * see also find_vpid() etc in include/linux/pid.h */ extern struct task_struct *find_task_by_pid_type_ns(int type, int pid, diff --git a/kernel/pid.c b/kernel/pid.c index 753fd90d9ec1..064e76afa507 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -309,12 +309,6 @@ struct pid *find_vpid(int nr) } EXPORT_SYMBOL_GPL(find_vpid); -struct pid *find_pid(int nr) -{ - return find_pid_ns(nr, &init_pid_ns); -} -EXPORT_SYMBOL_GPL(find_pid); - /* * attach_pid() must be called with the tasklist_lock write-held. */ @@ -483,7 +477,7 @@ EXPORT_SYMBOL(task_session_nr_ns); /* * Used by proc to find the first pid that is greater then or equal to nr. * - * If there is a pid at nr this function is exactly the same as find_pid. + * If there is a pid at nr this function is exactly the same as find_pid_ns. */ struct pid *find_ge_pid(int nr, struct pid_namespace *ns) { -- cgit v1.2.3 From dbda0de52618d13d1b927c7ba7bb839cfddc4e8c Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:37 -0700 Subject: pidns: remove find_task_by_pid, unused for a long time It seems to me that it was a mistake marking this function as deprecated and scheduling it for removal, rather than resolutely removing it after the last caller's death. Anyway - better late, then never. Signed-off-by: Pavel Emelyanov Cc: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 18 ------------------ include/linux/pid.h | 2 +- include/linux/sched.h | 6 ------ 3 files changed, 1 insertion(+), 25 deletions(-) (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 09c4a1efb8e3..721c71b86e06 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -138,24 +138,6 @@ Who: Kay Sievers --------------------------- -What: find_task_by_pid -When: 2.6.26 -Why: With pid namespaces, calling this funciton will return the - wrong task when called from inside a namespace. - - The best way to save a task pid and find a task by this - pid later, is to find this task's struct pid pointer (or get - it directly from the task) and call pid_task() later. - - If someone really needs to get a task by its pid_t, then - he most likely needs the find_task_by_vpid() to get the - task from the same namespace as the current task is in, but - this may be not so in general. - -Who: Pavel Emelyanov - ---------------------------- - What: ACPI procfs interface When: July 2008 Why: ACPI sysfs conversion should be finished by January 2008. diff --git a/include/linux/pid.h b/include/linux/pid.h index ff1b2a5814d4..22921ac4cfd9 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -107,7 +107,7 @@ extern struct pid_namespace init_pid_ns; * find_pid_ns() finds the pid in the namespace specified * find_vpid() finr the pid by its virtual id, i.e. in the current namespace * - * see also find_task_by_pid() set in include/linux/sched.h + * see also find_task_by_vpid() set in include/linux/sched.h */ extern struct pid *find_pid_ns(int nr, struct pid_namespace *ns); extern struct pid *find_vpid(int nr); diff --git a/include/linux/sched.h b/include/linux/sched.h index 182da1550fad..354ef478a80d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1715,8 +1715,6 @@ extern struct pid_namespace init_pid_ns; * finds a task by its pid in the specified namespace * find_task_by_vpid(): * finds a task by its virtual pid - * find_task_by_pid(): - * finds a task by its global pid * * see also find_vpid() etc in include/linux/pid.h */ @@ -1724,10 +1722,6 @@ extern struct pid_namespace init_pid_ns; extern struct task_struct *find_task_by_pid_type_ns(int type, int pid, struct pid_namespace *ns); -static inline struct task_struct *__deprecated find_task_by_pid(pid_t nr) -{ - return find_task_by_pid_type_ns(PIDTYPE_PID, nr, &init_pid_ns); -} extern struct task_struct *find_task_by_vpid(pid_t nr); extern struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns); -- cgit v1.2.3 From 49b5cf34727a6c1be1568ab28e89a2d9a6bf51e0 Mon Sep 17 00:00:00 2001 From: Jonathan Lim Date: Fri, 25 Jul 2008 01:48:40 -0700 Subject: accounting: account for user time when updating memory integrals Adapt acct_update_integrals() to include user time when calculating the time difference. The units of acct_rss_mem1 and acct_vm_mem1 are also changed from pages-jiffies to pages-usecs to avoid calling jiffies_to_usecs() in xacct_add_tsk() which might overflow. Signed-off-by: Jonathan Lim Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 2 +- kernel/sched.c | 2 ++ kernel/tsacct.c | 21 ++++++++++++++------- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 354ef478a80d..af780f299c7c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1257,7 +1257,7 @@ struct task_struct { #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ - cputime_t acct_stimexpd;/* stime since last update */ + cputime_t acct_timexpd; /* stime + utime since last update */ #endif #ifdef CONFIG_CPUSETS nodemask_t mems_allowed; diff --git a/kernel/sched.c b/kernel/sched.c index 6acf749d3336..0047bd9b96aa 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4046,6 +4046,8 @@ void account_user_time(struct task_struct *p, cputime_t cputime) cpustat->nice = cputime64_add(cpustat->nice, tmp); else cpustat->user = cputime64_add(cpustat->user, tmp); + /* Account for user time used */ + acct_update_integrals(p); } /* diff --git a/kernel/tsacct.c b/kernel/tsacct.c index 4ab1b584961b..1da6990af8e0 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -84,9 +84,9 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) { struct mm_struct *mm; - /* convert pages-jiffies to Mbyte-usec */ - stats->coremem = jiffies_to_usecs(p->acct_rss_mem1) * PAGE_SIZE / MB; - stats->virtmem = jiffies_to_usecs(p->acct_vm_mem1) * PAGE_SIZE / MB; + /* convert pages-usec to Mbyte-usec */ + stats->coremem = p->acct_rss_mem1 * PAGE_SIZE / MB; + stats->virtmem = p->acct_vm_mem1 * PAGE_SIZE / MB; mm = get_task_mm(p); if (mm) { /* adjust to KB unit */ @@ -118,12 +118,19 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) void acct_update_integrals(struct task_struct *tsk) { if (likely(tsk->mm)) { - long delta = cputime_to_jiffies( - cputime_sub(tsk->stime, tsk->acct_stimexpd)); + cputime_t time, dtime; + struct timeval value; + u64 delta; + + time = tsk->stime + tsk->utime; + dtime = cputime_sub(time, tsk->acct_timexpd); + jiffies_to_timeval(cputime_to_jiffies(dtime), &value); + delta = value.tv_sec; + delta = delta * USEC_PER_SEC + value.tv_usec; if (delta == 0) return; - tsk->acct_stimexpd = tsk->stime; + tsk->acct_timexpd = time; tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm); tsk->acct_vm_mem1 += delta * tsk->mm->total_vm; } @@ -135,7 +142,7 @@ void acct_update_integrals(struct task_struct *tsk) */ void acct_clear_integrals(struct task_struct *tsk) { - tsk->acct_stimexpd = 0; + tsk->acct_timexpd = 0; tsk->acct_rss_mem1 = 0; tsk->acct_vm_mem1 = 0; } -- cgit v1.2.3 From 20fad13ac66ac001c19220d3d08b4de5b6cca6e1 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:43 -0700 Subject: pidns: add the struct bsd_acct_struct pointer on pid_namespace struct All the bsdacct-related info will be stored in the area, pointer by this one. It will be NULL automatically for all new namespaces. Signed-off-by: Pavel Emelyanov Cc: Balbir Singh Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid_namespace.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 1a49ab5ec7b9..1af82c4e17d4 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -14,6 +14,8 @@ struct pidmap { #define PIDMAP_ENTRIES ((PID_MAX_LIMIT + 8*PAGE_SIZE - 1)/PAGE_SIZE/8) +struct bsd_acct_struct; + struct pid_namespace { struct kref kref; struct pidmap pidmap[PIDMAP_ENTRIES]; @@ -25,6 +27,9 @@ struct pid_namespace { #ifdef CONFIG_PROC_FS struct vfsmount *proc_mnt; #endif +#ifdef CONFIG_BSD_PROCESS_ACCT + struct bsd_acct_struct *bacct; +#endif }; extern struct pid_namespace init_pid_ns; -- cgit v1.2.3 From 0b6b030fc30d169bb406b34b4fc60d99dde4a9c6 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:47 -0700 Subject: bsdacct: switch from global bsd_acct_struct instance to per-pidns one Allocate the structure on the first call to sys_acct(). After this each namespace, that ordered the accounting, will live with this structure till its own death. Two notes - routines, that close the accounting on fs umount time use the init_pid_ns's acct by now; - accounting routine accounts to dying task's namespace (also by now). Signed-off-by: Pavel Emelyanov Cc: Balbir Singh Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/acct.h | 3 ++ kernel/acct.c | 84 +++++++++++++++++++++++++++++++++++++++----------- kernel/pid_namespace.c | 2 ++ 3 files changed, 71 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/acct.h b/include/linux/acct.h index e8cae54e8d88..882dc7248766 100644 --- a/include/linux/acct.h +++ b/include/linux/acct.h @@ -120,17 +120,20 @@ struct acct_v3 struct vfsmount; struct super_block; struct pacct_struct; +struct pid_namespace; extern void acct_auto_close_mnt(struct vfsmount *m); extern void acct_auto_close(struct super_block *sb); extern void acct_init_pacct(struct pacct_struct *pacct); extern void acct_collect(long exitcode, int group_dead); extern void acct_process(void); +extern void acct_exit_ns(struct pid_namespace *); #else #define acct_auto_close_mnt(x) do { } while (0) #define acct_auto_close(x) do { } while (0) #define acct_init_pacct(x) do { } while (0) #define acct_collect(x,y) do { } while (0) #define acct_process() do { } while (0) +#define acct_exit_ns(ns) do { } while (0) #endif /* diff --git a/kernel/acct.c b/kernel/acct.c index 72d4760c8da8..febbbc67157e 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -93,8 +93,6 @@ struct bsd_acct_struct { static DEFINE_SPINLOCK(acct_lock); -static struct bsd_acct_struct acct_globals __cacheline_aligned; - /* * Called whenever the timer says to check the free space. */ @@ -176,7 +174,8 @@ out: * * NOTE: acct_lock MUST be held on entry and exit. */ -static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file) +static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file, + struct pid_namespace *ns) { struct file *old_acct = NULL; struct pid_namespace *old_ns = NULL; @@ -188,10 +187,11 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file) acct->active = 0; acct->needcheck = 0; acct->file = NULL; + acct->ns = NULL; } if (file) { acct->file = file; - acct->ns = get_pid_ns(task_active_pid_ns(current)); + acct->ns = ns; acct->needcheck = 0; acct->active = 1; /* It's been deleted if it was used before so this is safe */ @@ -204,7 +204,6 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file) spin_unlock(&acct_lock); do_acct_process(acct, old_ns, old_acct); filp_close(old_acct, NULL); - put_pid_ns(old_ns); spin_lock(&acct_lock); } } @@ -213,6 +212,8 @@ static int acct_on(char *name) { struct file *file; int error; + struct pid_namespace *ns; + struct bsd_acct_struct *acct = NULL; /* Difference from BSD - they don't do O_APPEND */ file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); @@ -229,18 +230,34 @@ static int acct_on(char *name) return -EIO; } + ns = task_active_pid_ns(current); + if (ns->bacct == NULL) { + acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); + if (acct == NULL) { + filp_close(file, NULL); + return -ENOMEM; + } + } + error = security_acct(file); if (error) { + kfree(acct); filp_close(file, NULL); return error; } spin_lock(&acct_lock); + if (ns->bacct == NULL) { + ns->bacct = acct; + acct = NULL; + } + mnt_pin(file->f_path.mnt); - acct_file_reopen(&acct_globals, file); + acct_file_reopen(ns->bacct, file, ns); spin_unlock(&acct_lock); mntput(file->f_path.mnt); /* it's pinned, now give up active reference */ + kfree(acct); return 0; } @@ -270,10 +287,16 @@ asmlinkage long sys_acct(const char __user *name) error = acct_on(tmp); putname(tmp); } else { + struct bsd_acct_struct *acct; + + acct = task_active_pid_ns(current)->bacct; + if (acct == NULL) + return 0; + error = security_acct(NULL); if (!error) { spin_lock(&acct_lock); - acct_file_reopen(&acct_globals, NULL); + acct_file_reopen(acct, NULL, NULL); spin_unlock(&acct_lock); } } @@ -289,9 +312,15 @@ asmlinkage long sys_acct(const char __user *name) */ void acct_auto_close_mnt(struct vfsmount *m) { + struct bsd_acct_struct *acct; + + acct = init_pid_ns.bacct; + if (acct == NULL) + return; + spin_lock(&acct_lock); - if (acct_globals.file && acct_globals.file->f_path.mnt == m) - acct_file_reopen(&acct_globals, NULL); + if (acct->file && acct->file->f_path.mnt == m) + acct_file_reopen(acct, NULL, NULL); spin_unlock(&acct_lock); } @@ -304,10 +333,29 @@ void acct_auto_close_mnt(struct vfsmount *m) */ void acct_auto_close(struct super_block *sb) { + struct bsd_acct_struct *acct; + + acct = init_pid_ns.bacct; + if (acct == NULL) + return; + spin_lock(&acct_lock); - if (acct_globals.file && - acct_globals.file->f_path.mnt->mnt_sb == sb) { - acct_file_reopen(&acct_globals, NULL); + if (acct->file && acct->file->f_path.mnt->mnt_sb == sb) + acct_file_reopen(acct, NULL, NULL); + spin_unlock(&acct_lock); +} + +void acct_exit_ns(struct pid_namespace *ns) +{ + struct bsd_acct_struct *acct; + + spin_lock(&acct_lock); + acct = ns->bacct; + if (acct != NULL) { + if (acct->file != NULL) + acct_file_reopen(acct, NULL, NULL); + + kfree(acct); } spin_unlock(&acct_lock); } @@ -587,25 +635,25 @@ void acct_collect(long exitcode, int group_dead) void acct_process(void) { struct file *file = NULL; - struct pid_namespace *ns; + struct pid_namespace *ns = task_active_pid_ns(current); + struct bsd_acct_struct *acct; + acct = ns->bacct; /* * accelerate the common fastpath: */ - if (!acct_globals.file) + if (!acct || !acct->file) return; spin_lock(&acct_lock); - file = acct_globals.file; + file = acct->file; if (unlikely(!file)) { spin_unlock(&acct_lock); return; } get_file(file); - ns = get_pid_ns(acct_globals.ns); spin_unlock(&acct_lock); - do_acct_process(&acct_globals, ns, file); + do_acct_process(acct, ns, file); fput(file); - put_pid_ns(ns); } diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index 06331cc1c3f5..ea567b78d1aa 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -12,6 +12,7 @@ #include #include #include +#include #define BITS_PER_PAGE (PAGE_SIZE*8) @@ -181,6 +182,7 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns) /* Child reaper for the pid namespace is going away */ pid_ns->child_reaper = NULL; + acct_exit_ns(pid_ns); return; } -- cgit v1.2.3 From 297c5d92634c809cef23d73e7b2556f2528ff7e2 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Fri, 25 Jul 2008 01:48:49 -0700 Subject: task IO accounting: provide distinct tgid/tid I/O statistics Report per-thread I/O statistics in /proc/pid/task/tid/io and aggregate parent I/O statistics in /proc/pid/io. This approach follows the same model used to account per-process and per-thread CPU times. As a practial application, this allows for example to quickly find the top I/O consumer when a process spawns many child threads that perform the actual I/O work, because the aggregated I/O statistics can always be found in /proc/pid/io. [ Oleg Nesterov points out that we should check that the task is still alive before we iterate over the threads, but also says that we can do that fixup on top of this later. - Linus ] Acked-by: Balbir Singh Signed-off-by: Andrea Righi Cc: Matt Heaton Cc: Shailabh Nagar Acked-by-with-comments: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/base.c | 86 ++++++++++++++++++++++++++++++++++++++++++--------- include/linux/sched.h | 4 +++ kernel/exit.c | 27 ++++++++++++++++ kernel/fork.c | 6 ++++ 4 files changed, 108 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index 58c3e6a8e15e..a891fe4cb43b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2376,29 +2376,82 @@ static int proc_base_fill_cache(struct file *filp, void *dirent, } #ifdef CONFIG_TASK_IO_ACCOUNTING -static int proc_pid_io_accounting(struct task_struct *task, char *buffer) -{ +static int do_io_accounting(struct task_struct *task, char *buffer, int whole) +{ + u64 rchar, wchar, syscr, syscw; + struct task_io_accounting ioac; + + if (!whole) { + rchar = task->rchar; + wchar = task->wchar; + syscr = task->syscr; + syscw = task->syscw; + memcpy(&ioac, &task->ioac, sizeof(ioac)); + } else { + unsigned long flags; + struct task_struct *t = task; + rchar = wchar = syscr = syscw = 0; + memset(&ioac, 0, sizeof(ioac)); + + rcu_read_lock(); + do { + rchar += t->rchar; + wchar += t->wchar; + syscr += t->syscr; + syscw += t->syscw; + + ioac.read_bytes += t->ioac.read_bytes; + ioac.write_bytes += t->ioac.write_bytes; + ioac.cancelled_write_bytes += + t->ioac.cancelled_write_bytes; + t = next_thread(t); + } while (t != task); + rcu_read_unlock(); + + if (lock_task_sighand(task, &flags)) { + struct signal_struct *sig = task->signal; + + rchar += sig->rchar; + wchar += sig->wchar; + syscr += sig->syscr; + syscw += sig->syscw; + + ioac.read_bytes += sig->ioac.read_bytes; + ioac.write_bytes += sig->ioac.write_bytes; + ioac.cancelled_write_bytes += + sig->ioac.cancelled_write_bytes; + + unlock_task_sighand(task, &flags); + } + } + return sprintf(buffer, -#ifdef CONFIG_TASK_XACCT "rchar: %llu\n" "wchar: %llu\n" "syscr: %llu\n" "syscw: %llu\n" -#endif "read_bytes: %llu\n" "write_bytes: %llu\n" "cancelled_write_bytes: %llu\n", -#ifdef CONFIG_TASK_XACCT - (unsigned long long)task->rchar, - (unsigned long long)task->wchar, - (unsigned long long)task->syscr, - (unsigned long long)task->syscw, -#endif - (unsigned long long)task->ioac.read_bytes, - (unsigned long long)task->ioac.write_bytes, - (unsigned long long)task->ioac.cancelled_write_bytes); + (unsigned long long)rchar, + (unsigned long long)wchar, + (unsigned long long)syscr, + (unsigned long long)syscw, + (unsigned long long)ioac.read_bytes, + (unsigned long long)ioac.write_bytes, + (unsigned long long)ioac.cancelled_write_bytes); +} + +static int proc_tid_io_accounting(struct task_struct *task, char *buffer) +{ + return do_io_accounting(task, buffer, 0); } -#endif + +static int proc_tgid_io_accounting(struct task_struct *task, char *buffer) +{ + return do_io_accounting(task, buffer, 1); +} +#endif /* CONFIG_TASK_IO_ACCOUNTING */ /* * Thread groups @@ -2470,7 +2523,7 @@ static const struct pid_entry tgid_base_stuff[] = { REG("coredump_filter", S_IRUGO|S_IWUSR, coredump_filter), #endif #ifdef CONFIG_TASK_IO_ACCOUNTING - INF("io", S_IRUGO, pid_io_accounting), + INF("io", S_IRUGO, tgid_io_accounting), #endif }; @@ -2797,6 +2850,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject), #endif +#ifdef CONFIG_TASK_IO_ACCOUNTING + INF("io", S_IRUGO, tid_io_accounting), +#endif }; static int proc_tid_base_readdir(struct file * filp, diff --git a/include/linux/sched.h b/include/linux/sched.h index af780f299c7c..d22ffe06d0eb 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -506,6 +506,10 @@ struct signal_struct { unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; unsigned long inblock, oublock, cinblock, coublock; +#ifdef CONFIG_TASK_XACCT + u64 rchar, wchar, syscr, syscw; +#endif + struct task_io_accounting ioac; /* * Cumulative ns of scheduled CPU time for dead threads in the diff --git a/kernel/exit.c b/kernel/exit.c index 8a4d4d12e294..ad933bb29ec7 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -120,6 +120,18 @@ static void __exit_signal(struct task_struct *tsk) sig->nivcsw += tsk->nivcsw; sig->inblock += task_io_get_inblock(tsk); sig->oublock += task_io_get_oublock(tsk); +#ifdef CONFIG_TASK_XACCT + sig->rchar += tsk->rchar; + sig->wchar += tsk->wchar; + sig->syscr += tsk->syscr; + sig->syscw += tsk->syscw; +#endif /* CONFIG_TASK_XACCT */ +#ifdef CONFIG_TASK_IO_ACCOUNTING + sig->ioac.read_bytes += tsk->ioac.read_bytes; + sig->ioac.write_bytes += tsk->ioac.write_bytes; + sig->ioac.cancelled_write_bytes += + tsk->ioac.cancelled_write_bytes; +#endif /* CONFIG_TASK_IO_ACCOUNTING */ sig->sum_sched_runtime += tsk->se.sum_exec_runtime; sig = NULL; /* Marker for below. */ } @@ -1366,6 +1378,21 @@ static int wait_task_zombie(struct task_struct *p, int options, psig->coublock += task_io_get_oublock(p) + sig->oublock + sig->coublock; +#ifdef CONFIG_TASK_XACCT + psig->rchar += p->rchar + sig->rchar; + psig->wchar += p->wchar + sig->wchar; + psig->syscr += p->syscr + sig->syscr; + psig->syscw += p->syscw + sig->syscw; +#endif /* CONFIG_TASK_XACCT */ +#ifdef CONFIG_TASK_IO_ACCOUNTING + psig->ioac.read_bytes += + p->ioac.read_bytes + sig->ioac.read_bytes; + psig->ioac.write_bytes += + p->ioac.write_bytes + sig->ioac.write_bytes; + psig->ioac.cancelled_write_bytes += + p->ioac.cancelled_write_bytes + + sig->ioac.cancelled_write_bytes; +#endif /* CONFIG_TASK_IO_ACCOUNTING */ spin_unlock_irq(&p->parent->sighand->siglock); } diff --git a/kernel/fork.c b/kernel/fork.c index 813d5c89b9d5..b99d73e971a4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -812,6 +812,12 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0; sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0; sig->inblock = sig->oublock = sig->cinblock = sig->coublock = 0; +#ifdef CONFIG_TASK_XACCT + sig->rchar = sig->wchar = sig->syscr = sig->syscw = 0; +#endif +#ifdef CONFIG_TASK_IO_ACCOUNTING + memset(&sig->ioac, 0, sizeof(sig->ioac)); +#endif sig->sum_sched_runtime = 0; INIT_LIST_HEAD(&sig->cpu_timers[0]); INIT_LIST_HEAD(&sig->cpu_timers[1]); -- cgit v1.2.3 From 873b47717732c2f33a4b14de02571a4295a02f0c Mon Sep 17 00:00:00 2001 From: Keika Kobayashi Date: Fri, 25 Jul 2008 01:48:52 -0700 Subject: per-task-delay-accounting: add memory reclaim delay Sometimes, application responses become bad under heavy memory load. Applications take a bit time to reclaim memory. The statistics, how long memory reclaim takes, will be useful to measure memory usage. This patch adds accounting memory reclaim to per-task-delay-accounting for accounting the time of do_try_to_free_pages(). - When System is under low memory load, memory reclaim may not occur. $ free total used free shared buffers cached Mem: 8197800 1577300 6620500 0 4808 1516724 -/+ buffers/cache: 55768 8142032 Swap: 16386292 0 16386292 $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 5069748 10612 3014060 0 0 0 0 3 26 0 0 100 0 0 0 0 5069748 10612 3014060 0 0 0 0 4 22 0 0 100 0 0 0 0 5069748 10612 3014060 0 0 0 0 3 18 0 0 100 0 Measure the time of tar command. $ ls -s test.dat 1501472 test.dat $ time tar cvf test.tar test.dat real 0m13.388s user 0m0.116s sys 0m5.304s $ ./delayget -d -p CPU count real total virtual total delay total 428 5528345500 5477116080 62749891 IO count delay total 338 8078977189 SWAP count delay total 0 0 RECLAIM count delay total 0 0 - When system is under heavy memory load memory reclaim may occur. $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 7159032 49724 1812 3012 0 0 0 0 3 24 0 0 100 0 0 0 7159032 49724 1812 3012 0 0 0 0 4 24 0 0 100 0 0 0 7159032 49848 1812 3012 0 0 0 0 3 22 0 0 100 0 In this case, one process uses more 8G memory by execution of malloc() and memset(). $ time tar cvf test.tar test.dat real 1m38.563s <- increased by 85 sec user 0m0.140s sys 0m7.060s $ ./delayget -d -p CPU count real total virtual total delay total 9021 7140446250 7315277975 923201824 IO count delay total 8965 90466349669 SWAP count delay total 3 21036367 RECLAIM count delay total 740 61011951153 In the later case, the value of RECLAIM is increasing. So, taskstats can show how much memory reclaim influences TAT. Signed-off-by: Keika Kobayashi Acked-by: Balbir Singh Acked-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/delayacct.h | 19 +++++++++++++++++++ include/linux/sched.h | 4 ++++ kernel/delayacct.c | 13 +++++++++++++ mm/vmscan.c | 5 +++++ 4 files changed, 41 insertions(+) (limited to 'include/linux') diff --git a/include/linux/delayacct.h b/include/linux/delayacct.h index ab94bc083558..f352f06fa063 100644 --- a/include/linux/delayacct.h +++ b/include/linux/delayacct.h @@ -39,6 +39,8 @@ extern void __delayacct_blkio_start(void); extern void __delayacct_blkio_end(void); extern int __delayacct_add_tsk(struct taskstats *, struct task_struct *); extern __u64 __delayacct_blkio_ticks(struct task_struct *); +extern void __delayacct_freepages_start(void); +extern void __delayacct_freepages_end(void); static inline int delayacct_is_task_waiting_on_io(struct task_struct *p) { @@ -107,6 +109,18 @@ static inline __u64 delayacct_blkio_ticks(struct task_struct *tsk) return 0; } +static inline void delayacct_freepages_start(void) +{ + if (current->delays) + __delayacct_freepages_start(); +} + +static inline void delayacct_freepages_end(void) +{ + if (current->delays) + __delayacct_freepages_end(); +} + #else static inline void delayacct_set_flag(int flag) {} @@ -129,6 +143,11 @@ static inline __u64 delayacct_blkio_ticks(struct task_struct *tsk) { return 0; } static inline int delayacct_is_task_waiting_on_io(struct task_struct *p) { return 0; } +static inline void delayacct_freepages_start(void) +{} +static inline void delayacct_freepages_end(void) +{} + #endif /* CONFIG_TASK_DELAY_ACCT */ #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index d22ffe06d0eb..42036ffe6b00 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -672,6 +672,10 @@ struct task_delay_info { /* io operations performed */ u32 swapin_count; /* total count of the number of swapin block */ /* io operations performed */ + + struct timespec freepages_start, freepages_end; + u64 freepages_delay; /* wait for memory reclaim */ + u32 freepages_count; /* total count of memory reclaim */ }; #endif /* CONFIG_TASK_DELAY_ACCT */ diff --git a/kernel/delayacct.c b/kernel/delayacct.c index 10e43fd8b721..84b6782a2ce4 100644 --- a/kernel/delayacct.c +++ b/kernel/delayacct.c @@ -165,3 +165,16 @@ __u64 __delayacct_blkio_ticks(struct task_struct *tsk) return ret; } +void __delayacct_freepages_start(void) +{ + delayacct_start(¤t->delays->freepages_start); +} + +void __delayacct_freepages_end(void) +{ + delayacct_end(¤t->delays->freepages_start, + ¤t->delays->freepages_end, + ¤t->delays->freepages_delay, + ¤t->delays->freepages_count); +} + diff --git a/mm/vmscan.c b/mm/vmscan.c index 967d30ccd92b..26672c6cd3ce 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -1316,6 +1317,8 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, struct zone *zone; enum zone_type high_zoneidx = gfp_zone(sc->gfp_mask); + delayacct_freepages_start(); + if (scan_global_lru(sc)) count_vm_event(ALLOCSTALL); /* @@ -1396,6 +1399,8 @@ out: } else mem_cgroup_record_reclaim_priority(sc->mem_cgroup, priority); + delayacct_freepages_end(); + return ret; } -- cgit v1.2.3 From 016ae219b920c4e606088761d3d6070cdf8ba706 Mon Sep 17 00:00:00 2001 From: Keika Kobayashi Date: Fri, 25 Jul 2008 01:48:53 -0700 Subject: per-task-delay-accounting: update taskstats for memory reclaim delay Add members for memory reclaim delay to taskstats, and accumulate them in __delayacct_add_tsk() . Signed-off-by: Keika Kobayashi Cc: Hiroshi Shimamoto Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/accounting/taskstats-struct.txt | 7 +++++++ include/linux/taskstats.h | 6 +++++- kernel/delayacct.c | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/accounting/taskstats-struct.txt b/Documentation/accounting/taskstats-struct.txt index cd784f46bf8a..b988d110db59 100644 --- a/Documentation/accounting/taskstats-struct.txt +++ b/Documentation/accounting/taskstats-struct.txt @@ -26,6 +26,8 @@ There are three different groups of fields in the struct taskstats: 5) Time accounting for SMT machines +6) Extended delay accounting fields for memory reclaim + Future extension should add fields to the end of the taskstats struct, and should not change the relative position of each field within the struct. @@ -170,4 +172,9 @@ struct taskstats { __u64 ac_utimescaled; /* utime scaled on frequency etc */ __u64 ac_stimescaled; /* stime scaled on frequency etc */ __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ + +6) Extended delay accounting fields for memory reclaim + /* Delay waiting for memory reclaim */ + __u64 freepages_count; + __u64 freepages_delay_total; } diff --git a/include/linux/taskstats.h b/include/linux/taskstats.h index 5d69c0744fff..18269e956a71 100644 --- a/include/linux/taskstats.h +++ b/include/linux/taskstats.h @@ -31,7 +31,7 @@ */ -#define TASKSTATS_VERSION 6 +#define TASKSTATS_VERSION 7 #define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN * in linux/sched.h */ @@ -157,6 +157,10 @@ struct taskstats { __u64 ac_utimescaled; /* utime scaled on frequency etc */ __u64 ac_stimescaled; /* stime scaled on frequency etc */ __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ + + /* Delay waiting for memory reclaim */ + __u64 freepages_count; + __u64 freepages_delay_total; }; diff --git a/kernel/delayacct.c b/kernel/delayacct.c index 84b6782a2ce4..b3179dad71be 100644 --- a/kernel/delayacct.c +++ b/kernel/delayacct.c @@ -145,8 +145,11 @@ int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk) d->blkio_delay_total = (tmp < d->blkio_delay_total) ? 0 : tmp; tmp = d->swapin_delay_total + tsk->delays->swapin_delay; d->swapin_delay_total = (tmp < d->swapin_delay_total) ? 0 : tmp; + tmp = d->freepages_delay_total + tsk->delays->freepages_delay; + d->freepages_delay_total = (tmp < d->freepages_delay_total) ? 0 : tmp; d->blkio_count += tsk->delays->blkio_count; d->swapin_count += tsk->delays->swapin_count; + d->freepages_count += tsk->delays->freepages_count; spin_unlock_irqrestore(&tsk->delays->lock, flags); done: -- cgit v1.2.3 From bde74e4bc64415b142e556a34d295a52a1b7da9d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 25 Jul 2008 01:48:57 -0700 Subject: locks: add special return value for asynchronous locks Use a special error value FILE_LOCK_DEFERRED to mean that a locking operation returned asynchronously. This is returned by posix_lock_file() for sleeping locks to mean that the lock has been queued on the block list, and will be woken up when it might become available and needs to be retried (either fl_lmops->fl_notify() is called or fl_wait is woken up). f_op->lock() to mean either the above, or that the filesystem will call back with fl_lmops->fl_grant() when the result of the locking operation is known. The filesystem can do this for sleeping as well as non-sleeping locks. This is to make sure, that return values of -EAGAIN and -EINPROGRESS by filesystems are not mistaken to mean an asynchronous locking. This also makes error handling in fs/locks.c and lockd/svclock.c slightly cleaner. Signed-off-by: Miklos Szeredi Cc: Trond Myklebust Cc: "J. Bruce Fields" Cc: Matthew Wilcox Cc: David Teigland Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dlm/plock.c | 2 +- fs/lockd/svclock.c | 13 ++++--------- fs/locks.c | 28 ++++++++++++++-------------- include/linux/fs.h | 6 ++++++ 4 files changed, 25 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/dlm/plock.c b/fs/dlm/plock.c index 78878c5781ca..eba87ff3177b 100644 --- a/fs/dlm/plock.c +++ b/fs/dlm/plock.c @@ -116,7 +116,7 @@ int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file, if (xop->callback == NULL) wait_event(recv_wq, (op->done != 0)); else { - rv = -EINPROGRESS; + rv = FILE_LOCK_DEFERRED; goto out; } diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 821b9acdfb66..cf0d5c2c318d 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -418,8 +418,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; case -EAGAIN: ret = nlm_lck_denied; - break; - case -EINPROGRESS: + goto out; + case FILE_LOCK_DEFERRED: if (wait) break; /* Filesystem lock operation is in progress @@ -434,10 +434,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } - ret = nlm_lck_denied; - if (!wait) - goto out; - ret = nlm_lck_blocked; /* Append to list of blocked */ @@ -507,7 +503,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, } error = vfs_test_lock(file->f_file, &lock->fl); - if (error == -EINPROGRESS) { + if (error == FILE_LOCK_DEFERRED) { ret = nlmsvc_defer_lock_rqst(rqstp, block); goto out; } @@ -731,8 +727,7 @@ nlmsvc_grant_blocked(struct nlm_block *block) switch (error) { case 0: break; - case -EAGAIN: - case -EINPROGRESS: + case FILE_LOCK_DEFERRED: dprintk("lockd: lock still blocked error %d\n", error); nlmsvc_insert_block(block, NLM_NEVER); nlmsvc_release_block(block); diff --git a/fs/locks.c b/fs/locks.c index dce8c747371c..1ce57b4b362c 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -779,8 +779,10 @@ find_conflict: if (!flock_locks_conflict(request, fl)) continue; error = -EAGAIN; - if (request->fl_flags & FL_SLEEP) - locks_insert_block(fl, request); + if (!(request->fl_flags & FL_SLEEP)) + goto out; + error = FILE_LOCK_DEFERRED; + locks_insert_block(fl, request); goto out; } if (request->fl_flags & FL_ACCESS) @@ -836,7 +838,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str error = -EDEADLK; if (posix_locks_deadlock(request, fl)) goto out; - error = -EAGAIN; + error = FILE_LOCK_DEFERRED; locks_insert_block(fl, request); goto out; } @@ -1035,7 +1037,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl) might_sleep (); for (;;) { error = posix_lock_file(filp, fl, NULL); - if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); if (!error) @@ -1107,9 +1109,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, for (;;) { error = __posix_lock_file(inode, &fl, NULL); - if (error != -EAGAIN) - break; - if (!(fl.fl_flags & FL_SLEEP)) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl.fl_wait, !fl.fl_next); if (!error) { @@ -1531,7 +1531,7 @@ int flock_lock_file_wait(struct file *filp, struct file_lock *fl) might_sleep(); for (;;) { error = flock_lock_file(filp, fl); - if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); if (!error) @@ -1716,17 +1716,17 @@ out: * fl_grant is set. Callers expecting ->lock() to return asynchronously * will only use F_SETLK, not F_SETLKW; they will set FL_SLEEP if (and only if) * the request is for a blocking lock. When ->lock() does return asynchronously, - * it must return -EINPROGRESS, and call ->fl_grant() when the lock + * it must return FILE_LOCK_DEFERRED, and call ->fl_grant() when the lock * request completes. * If the request is for non-blocking lock the file system should return - * -EINPROGRESS then try to get the lock and call the callback routine with - * the result. If the request timed out the callback routine will return a + * FILE_LOCK_DEFERRED then try to get the lock and call the callback routine + * with the result. If the request timed out the callback routine will return a * nonzero return code and the file system should release the lock. The file * system is also responsible to keep a corresponding posix lock when it * grants a lock so the VFS can find out which locks are locally held and do * the correct lock cleanup when required. * The underlying filesystem must not drop the kernel lock or call - * ->fl_grant() before returning to the caller with a -EINPROGRESS + * ->fl_grant() before returning to the caller with a FILE_LOCK_DEFERRED * return code. */ int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf) @@ -1804,7 +1804,7 @@ again: else { for (;;) { error = posix_lock_file(filp, file_lock, NULL); - if (error != -EAGAIN || cmd == F_SETLK) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(file_lock->fl_wait, !file_lock->fl_next); @@ -1941,7 +1941,7 @@ again: else { for (;;) { error = posix_lock_file(filp, file_lock, NULL); - if (error != -EAGAIN || cmd == F_SETLK64) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(file_lock->fl_wait, !file_lock->fl_next); diff --git a/include/linux/fs.h b/include/linux/fs.h index 4b86f806014c..49d8eb7a71be 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -885,6 +885,12 @@ static inline int file_check_writeable(struct file *filp) #define FL_CLOSE 64 /* unlock on close */ #define FL_SLEEP 128 /* A blocking lock */ +/* + * Special return value from posix_lock_file() and vfs_lock_file() for + * asynchronous locking. + */ +#define FILE_LOCK_DEFERRED 1 + /* * The POSIX file lock owner is determined by * the "struct files_struct" in the thread group -- cgit v1.2.3 From 33670fa296860283f04a7975b8c790f101e43a6e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 25 Jul 2008 01:49:02 -0700 Subject: fuse: nfs export special lookups Implement the get_parent export operation by sending a LOOKUP request with ".." as the name. Implement looking up an inode by node ID after it has been evicted from the cache. This is done by seding a LOOKUP request with "." as the name (for all file types, not just directories). The filesystem can set the FUSE_EXPORT_SUPPORT flag in the INIT reply, to indicate that it supports these special lookups. Thanks to John Muir for the original implementation of this feature. Signed-off-by: Miklos Szeredi Cc: "J. Bruce Fields" Cc: Trond Myklebust Cc: Matthew Wilcox Cc: David Teigland Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fuse/fuse_i.h | 6 +++++ fs/fuse/inode.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/fuse.h | 3 +++ 3 files changed, 72 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5d3146da64e6..3a876076bdd1 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -363,6 +363,9 @@ struct fuse_conn { /** Do not send separate SETATTR request before open(O_TRUNC) */ unsigned atomic_o_trunc : 1; + /** Filesystem supports NFS exporting. Only set in INIT */ + unsigned export_support : 1; + /* * The following bitfields are only for optimization purposes * and hence races in setting them will not cause malfunction @@ -473,6 +476,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, u64 attr_valid, u64 attr_version); +int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, + struct fuse_entry_out *outarg, struct inode **inode); + /** * Send FORGET command */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 71fa76a48a31..7d2f7d6e22e2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -562,6 +562,7 @@ struct fuse_inode_handle static struct dentry *fuse_get_dentry(struct super_block *sb, struct fuse_inode_handle *handle) { + struct fuse_conn *fc = get_fuse_conn_super(sb); struct inode *inode; struct dentry *entry; int err = -ESTALE; @@ -570,8 +571,27 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, goto out_err; inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); - if (!inode) - goto out_err; + if (!inode) { + struct fuse_entry_out outarg; + struct qstr name; + + if (!fc->export_support) + goto out_err; + + name.len = 1; + name.name = "."; + err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg, + &inode); + if (err && err != -ENOENT) + goto out_err; + if (err || !inode) { + err = -ESTALE; + goto out_err; + } + err = -EIO; + if (get_node_id(inode) != handle->nodeid) + goto out_iput; + } err = -ESTALE; if (inode->i_generation != handle->generation) goto out_iput; @@ -659,11 +679,46 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb, return fuse_get_dentry(sb, &parent); } +static struct dentry *fuse_get_parent(struct dentry *child) +{ + struct inode *child_inode = child->d_inode; + struct fuse_conn *fc = get_fuse_conn(child_inode); + struct inode *inode; + struct dentry *parent; + struct fuse_entry_out outarg; + struct qstr name; + int err; + + if (!fc->export_support) + return ERR_PTR(-ESTALE); + + name.len = 2; + name.name = ".."; + err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), + &name, &outarg, &inode); + if (err && err != -ENOENT) + return ERR_PTR(err); + if (err || !inode) + return ERR_PTR(-ESTALE); + + parent = d_alloc_anon(inode); + if (!parent) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + if (get_node_id(inode) != FUSE_ROOT_ID) { + parent->d_op = &fuse_dentry_operations; + fuse_invalidate_entry_cache(parent); + } + + return parent; +} static const struct export_operations fuse_export_operations = { .fh_to_dentry = fuse_fh_to_dentry, .fh_to_parent = fuse_fh_to_parent, .encode_fh = fuse_encode_fh, + .get_parent = fuse_get_parent, }; static const struct super_operations fuse_super_operations = { @@ -695,6 +750,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) fc->no_lock = 1; if (arg->flags & FUSE_ATOMIC_O_TRUNC) fc->atomic_o_trunc = 1; + if (arg->minor >= 9) { + /* LOOKUP has dependency on proto version */ + if (arg->flags & FUSE_EXPORT_SUPPORT) + fc->export_support = 1; + } if (arg->flags & FUSE_BIG_WRITES) fc->big_writes = 1; } else { @@ -721,7 +781,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) arg->minor = FUSE_KERNEL_MINOR_VERSION; arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC | - FUSE_BIG_WRITES; + FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES; req->in.h.opcode = FUSE_INIT; req->in.numargs = 1; req->in.args[0].size = sizeof(*arg); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index d48282197696..265635dc9908 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -104,11 +104,14 @@ struct fuse_file_lock { /** * INIT request/reply flags + * + * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_FILE_OPS (1 << 2) #define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_EXPORT_SUPPORT (1 << 4) #define FUSE_BIG_WRITES (1 << 5) /** -- cgit v1.2.3 From 8f421c595a9145959d8aab09172743132abdffdb Mon Sep 17 00:00:00 2001 From: Arthur Jones Date: Fri, 25 Jul 2008 01:49:04 -0700 Subject: edac: i5100 new intel chipset driver Preliminary support for the Intel 5100 MCH. CE and UE errors are reported along with the current DIMM label information and other memory parameters. Reasons why this is preliminary: 1) This chip has 2 independent memory controllers which, for best perforance, use interleaved accesses to the DDR2 memory. This architecture does not map very well to the current edac data structures which depend on symmetric channel access to the interleaved data. Without core changes, the best I could do for now is to map both memory controllers to different csrows (first all ranks of controller 0, then all ranks of controller 1). Someone much more familiar with the edac core than I will probably need to come up with a more general data structure to handle the interleaving and de-interleaving of the two memory controllers. 2) I have not yet tackled the de-interleaving of the rank/controller address space into the physical address space of the CPU. There is nothing fundamentally missing, it is just ending up to be a lot of code, and I'd rather keep it separate for now, esp since it doesn't work yet... 3) The code depends on a particular i5100 chip select to DIMM mainboard chip select mapping. This mapping seems obvious to me in order to support dual and single ranked memory, but it is not unique and DIMM labels could be wrong on other mainboards. There is no way to query this mapping that I know of. 4) The code requires that the i5100 is in 32GB mode. Only 4 ranks per controller, 2 ranks per DIMM are supported. I do not have hardware (nor do I expect to have hardware anytime soon) for the 48GB (6 ranks per controller) mode. 5) The serial presence detect code should be broken out into a "real" i2c driver so that decode-dimms.pl can work. Signed-off-by: Arthur Jones Signed-off-by: Doug Thompson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/edac/Kconfig | 7 + drivers/edac/Makefile | 1 + drivers/edac/i5100_edac.c | 827 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 + 4 files changed, 838 insertions(+) create mode 100644 drivers/edac/i5100_edac.c (limited to 'include/linux') diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 6e6c3c4aea6b..5a11e3cbcae2 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -123,6 +123,13 @@ config EDAC_I5000 Support for error detection and correction the Intel Greekcreek/Blackford chipsets. +config EDAC_I5100 + tristate "Intel San Clemente MCH" + depends on EDAC_MM_EDAC && X86 && PCI + help + Support for error detection and correction the Intel + San Clemente MCH. + config EDAC_MPC85XX tristate "Freescale MPC85xx" depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 83807731d4a9..e5e9104b5520 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -19,6 +19,7 @@ endif obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o obj-$(CONFIG_EDAC_I5000) += i5000_edac.o +obj-$(CONFIG_EDAC_I5100) += i5100_edac.o obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c new file mode 100644 index 000000000000..43430bf70181 --- /dev/null +++ b/drivers/edac/i5100_edac.c @@ -0,0 +1,827 @@ +/* + * Intel 5100 Memory Controllers kernel module + * + * This file may be distributed under the terms of the + * GNU General Public License. + * + * This module is based on the following document: + * + * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet + * http://download.intel.com/design/chipsets/datashts/318378.pdf + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edac_core.h" + +/* register addresses and bit field accessors... */ + +/* device 16, func 1 */ +#define I5100_MS 0x44 /* Memory Status Register */ +#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ +#define I5100_SPDDATA_RDO(a) ((a) >> 15 & 1) +#define I5100_SPDDATA_SBE(a) ((a) >> 13 & 1) +#define I5100_SPDDATA_BUSY(a) ((a) >> 12 & 1) +#define I5100_SPDDATA_DATA(a) ((a) & ((1 << 8) - 1)) +#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ +#define I5100_SPDCMD_DTI(a) (((a) & ((1 << 4) - 1)) << 28) +#define I5100_SPDCMD_CKOVRD(a) (((a) & 1) << 27) +#define I5100_SPDCMD_SA(a) (((a) & ((1 << 3) - 1)) << 24) +#define I5100_SPDCMD_BA(a) (((a) & ((1 << 8) - 1)) << 16) +#define I5100_SPDCMD_DATA(a) (((a) & ((1 << 8) - 1)) << 8) +#define I5100_SPDCMD_CMD(a) ((a) & 1) +#define I5100_TOLM 0x6c /* Top of Low Memory */ +#define I5100_TOLM_TOLM(a) ((a) >> 12 & ((1 << 4) - 1)) +#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */ +#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */ +#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */ +#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */ +#define I5100_MIR_LIMIT(a) ((a) >> 4 & ((1 << 12) - 1)) +#define I5100_MIR_WAY1(a) ((a) >> 1 & 1) +#define I5100_MIR_WAY0(a) ((a) & 1) +#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */ +#define I5100_FERR_NF_MEM_CHAN_INDX(a) ((a) >> 28 & 1) +#define I5100_FERR_NF_MEM_SPD_MASK (1 << 18) +#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16) +#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15) +#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14) +#define I5100_FERR_NF_MEM_ +#define I5100_FERR_NF_MEM_ +#define I5100_FERR_NF_MEM_ANY_MASK \ + (I5100_FERR_NF_MEM_M16ERR_MASK | \ + I5100_FERR_NF_MEM_M15ERR_MASK | \ + I5100_FERR_NF_MEM_M14ERR_MASK) +#define I5100_FERR_NF_MEM_ANY(a) ((a) & I5100_FERR_NF_MEM_ANY_MASK) +#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */ +#define I5100_NERR_NF_MEM_ANY(a) I5100_FERR_NF_MEM_ANY(a) + +/* device 21 and 22, func 0 */ +#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */ +#define I5100_DMIR 0x15c /* DIMM Interleave Range */ +#define I5100_DMIR_LIMIT(a) ((a) >> 16 & ((1 << 11) - 1)) +#define I5100_DMIR_RANK(a, i) ((a) >> (4 * i) & ((1 << 2) - 1)) +#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */ +#define I5100_MTR_PRESENT(a) ((a) >> 10 & 1) +#define I5100_MTR_ETHROTTLE(a) ((a) >> 9 & 1) +#define I5100_MTR_WIDTH(a) ((a) >> 8 & 1) +#define I5100_MTR_NUMBANK(a) ((a) >> 6 & 1) +#define I5100_MTR_NUMROW(a) ((a) >> 2 & ((1 << 2) - 1)) +#define I5100_MTR_NUMCOL(a) ((a) & ((1 << 2) - 1)) +#define I5100_VALIDLOG 0x18c /* Valid Log Markers */ +#define I5100_VALIDLOG_REDMEMVALID(a) ((a) >> 2 & 1) +#define I5100_VALIDLOG_RECMEMVALID(a) ((a) >> 1 & 1) +#define I5100_VALIDLOG_NRECMEMVALID(a) ((a) & 1) +#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */ +#define I5100_NRECMEMA_MERR(a) ((a) >> 15 & ((1 << 5) - 1)) +#define I5100_NRECMEMA_BANK(a) ((a) >> 12 & ((1 << 3) - 1)) +#define I5100_NRECMEMA_RANK(a) ((a) >> 8 & ((1 << 3) - 1)) +#define I5100_NRECMEMA_DM_BUF_ID(a) ((a) & ((1 << 8) - 1)) +#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */ +#define I5100_NRECMEMB_CAS(a) ((a) >> 16 & ((1 << 13) - 1)) +#define I5100_NRECMEMB_RAS(a) ((a) & ((1 << 16) - 1)) +#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */ +#define I5100_REDMEMA_SYNDROME(a) (a) +#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */ +#define I5100_REDMEMB_ECC_LOCATOR(a) ((a) & ((1 << 18) - 1)) +#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */ +#define I5100_RECMEMA_MERR(a) I5100_NRECMEMA_MERR(a) +#define I5100_RECMEMA_BANK(a) I5100_NRECMEMA_BANK(a) +#define I5100_RECMEMA_RANK(a) I5100_NRECMEMA_RANK(a) +#define I5100_RECMEMA_DM_BUF_ID(a) I5100_NRECMEMA_DM_BUF_ID(a) +#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */ +#define I5100_RECMEMB_CAS(a) I5100_NRECMEMB_CAS(a) +#define I5100_RECMEMB_RAS(a) I5100_NRECMEMB_RAS(a) + +/* some generic limits */ +#define I5100_MAX_RANKS_PER_CTLR 6 +#define I5100_MAX_CTLRS 2 +#define I5100_MAX_RANKS_PER_DIMM 4 +#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ +#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4 +#define I5100_MAX_RANK_INTERLEAVE 4 +#define I5100_MAX_DMIRS 5 + +struct i5100_priv { + /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ + int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR]; + + /* + * mainboard chip select map -- maps i5100 chip selects to + * DIMM slot chip selects. In the case of only 4 ranks per + * controller, the mapping is fairly obvious but not unique. + * we map -1 -> NC and assume both controllers use the same + * map... + * + */ + int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM]; + + /* memory interleave range */ + struct { + u64 limit; + unsigned way[2]; + } mir[I5100_MAX_CTLRS]; + + /* adjusted memory interleave range register */ + unsigned amir[I5100_MAX_CTLRS]; + + /* dimm interleave range */ + struct { + unsigned rank[I5100_MAX_RANK_INTERLEAVE]; + u64 limit; + } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS]; + + /* memory technology registers... */ + struct { + unsigned present; /* 0 or 1 */ + unsigned ethrottle; /* 0 or 1 */ + unsigned width; /* 4 or 8 bits */ + unsigned numbank; /* 2 or 3 lines */ + unsigned numrow; /* 13 .. 16 lines */ + unsigned numcol; /* 11 .. 12 lines */ + } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR]; + + u64 tolm; /* top of low memory in bytes */ + unsigned ranksperctlr; /* number of ranks per controller */ + + struct pci_dev *mc; /* device 16 func 1 */ + struct pci_dev *ch0mm; /* device 21 func 0 */ + struct pci_dev *ch1mm; /* device 22 func 0 */ +}; + +/* map a rank/ctlr to a slot number on the mainboard */ +static int i5100_rank_to_slot(const struct mem_ctl_info *mci, + int ctlr, int rank) +{ + const struct i5100_priv *priv = mci->pvt_info; + int i; + + for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { + int j; + const int numrank = priv->dimm_numrank[ctlr][i]; + + for (j = 0; j < numrank; j++) + if (priv->dimm_csmap[i][j] == rank) + return i * 2 + ctlr; + } + + return -1; +} + +/* + * The processor bus memory addresses are broken into three + * pieces, whereas the controller addresses are contiguous. + * + * here we map from the controller address space to the + * processor address space: + * + * Processor Address Space + * +-----------------------------+ + * | | + * | "high" memory addresses | + * | | + * +-----------------------------+ <- 4GB on the i5100 + * | | + * | other non-memory addresses | + * | | + * +-----------------------------+ <- top of low memory + * | | + * | "low" memory addresses | + * | | + * +-----------------------------+ + */ +static unsigned long i5100_ctl_page_to_phys(struct mem_ctl_info *mci, + unsigned long cntlr_addr) +{ + const struct i5100_priv *priv = mci->pvt_info; + + if (cntlr_addr < priv->tolm) + return cntlr_addr; + + return (1ULL << 32) + (cntlr_addr - priv->tolm); +} + +static const char *i5100_err_msg(unsigned err) +{ + const char *merrs[] = { + "unknown", /* 0 */ + "uncorrectable data ECC on replay", /* 1 */ + "unknown", /* 2 */ + "unknown", /* 3 */ + "aliased uncorrectable demand data ECC", /* 4 */ + "aliased uncorrectable spare-copy data ECC", /* 5 */ + "aliased uncorrectable patrol data ECC", /* 6 */ + "unknown", /* 7 */ + "unknown", /* 8 */ + "unknown", /* 9 */ + "non-aliased uncorrectable demand data ECC", /* 10 */ + "non-aliased uncorrectable spare-copy data ECC", /* 11 */ + "non-aliased uncorrectable patrol data ECC", /* 12 */ + "unknown", /* 13 */ + "correctable demand data ECC", /* 14 */ + "correctable spare-copy data ECC", /* 15 */ + "correctable patrol data ECC", /* 16 */ + "unknown", /* 17 */ + "SPD protocol error", /* 18 */ + "unknown", /* 19 */ + "spare copy initiated", /* 20 */ + "spare copy completed", /* 21 */ + }; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(merrs); i++) + if (1 << i & err) + return merrs[i]; + + return "none"; +} + +/* convert csrow index into a rank (per controller -- 0..5) */ +static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return csrow % priv->ranksperctlr; +} + +/* convert csrow index into a controller (0..1) */ +static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return csrow / priv->ranksperctlr; +} + +static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci, + int ctlr, int rank) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return ctlr * priv->ranksperctlr + rank; +} + +static void i5100_handle_ce(struct mem_ctl_info *mci, + int ctlr, + unsigned bank, + unsigned rank, + unsigned long syndrome, + unsigned cas, + unsigned ras, + const char *msg) +{ + const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); + + printk(KERN_ERR + "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " + "cas %u, ras %u, csrow %u, label \"%s\": %s\n", + ctlr, bank, rank, syndrome, cas, ras, + csrow, mci->csrows[csrow].channels[0].label, msg); + + mci->ce_count++; + mci->csrows[csrow].ce_count++; + mci->csrows[csrow].channels[0].ce_count++; +} + +static void i5100_handle_ue(struct mem_ctl_info *mci, + int ctlr, + unsigned bank, + unsigned rank, + unsigned long syndrome, + unsigned cas, + unsigned ras, + const char *msg) +{ + const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); + + printk(KERN_ERR + "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " + "cas %u, ras %u, csrow %u, label \"%s\": %s\n", + ctlr, bank, rank, syndrome, cas, ras, + csrow, mci->csrows[csrow].channels[0].label, msg); + + mci->ue_count++; + mci->csrows[csrow].ue_count++; +} + +static void i5100_read_log(struct mem_ctl_info *mci, int ctlr, + u32 ferr, u32 nerr) +{ + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm; + u32 dw; + u32 dw2; + unsigned syndrome = 0; + unsigned ecc_loc = 0; + unsigned merr; + unsigned bank; + unsigned rank; + unsigned cas; + unsigned ras; + + pci_read_config_dword(pdev, I5100_VALIDLOG, &dw); + + if (I5100_VALIDLOG_REDMEMVALID(dw)) { + pci_read_config_dword(pdev, I5100_REDMEMA, &dw2); + syndrome = I5100_REDMEMA_SYNDROME(dw2); + pci_read_config_dword(pdev, I5100_REDMEMB, &dw2); + ecc_loc = I5100_REDMEMB_ECC_LOCATOR(dw2); + } + + if (I5100_VALIDLOG_RECMEMVALID(dw)) { + const char *msg; + + pci_read_config_dword(pdev, I5100_RECMEMA, &dw2); + merr = I5100_RECMEMA_MERR(dw2); + bank = I5100_RECMEMA_BANK(dw2); + rank = I5100_RECMEMA_RANK(dw2); + + pci_read_config_dword(pdev, I5100_RECMEMB, &dw2); + cas = I5100_RECMEMB_CAS(dw2); + ras = I5100_RECMEMB_RAS(dw2); + + /* FIXME: not really sure if this is what merr is... + */ + if (!merr) + msg = i5100_err_msg(ferr); + else + msg = i5100_err_msg(nerr); + + i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg); + } + + if (I5100_VALIDLOG_NRECMEMVALID(dw)) { + const char *msg; + + pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2); + merr = I5100_NRECMEMA_MERR(dw2); + bank = I5100_NRECMEMA_BANK(dw2); + rank = I5100_NRECMEMA_RANK(dw2); + + pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2); + cas = I5100_NRECMEMB_CAS(dw2); + ras = I5100_NRECMEMB_RAS(dw2); + + /* FIXME: not really sure if this is what merr is... + */ + if (!merr) + msg = i5100_err_msg(ferr); + else + msg = i5100_err_msg(nerr); + + i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg); + } + + pci_write_config_dword(pdev, I5100_VALIDLOG, dw); +} + +static void i5100_check_error(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + u32 dw; + + + pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw); + if (I5100_FERR_NF_MEM_ANY(dw)) { + u32 dw2; + + pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2); + if (dw2) + pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, + dw2); + pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw); + + i5100_read_log(mci, I5100_FERR_NF_MEM_CHAN_INDX(dw), + I5100_FERR_NF_MEM_ANY(dw), + I5100_NERR_NF_MEM_ANY(dw2)); + } +} + +static struct pci_dev *pci_get_device_func(unsigned vendor, + unsigned device, + unsigned func) +{ + struct pci_dev *ret = NULL; + + while (1) { + ret = pci_get_device(vendor, device, ret); + + if (!ret) + break; + + if (PCI_FUNC(ret->devfn) == func) + break; + } + + return ret; +} + +static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci, + int csrow) +{ + struct i5100_priv *priv = mci->pvt_info; + const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow); + const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow); + unsigned addr_lines; + + /* dimm present? */ + if (!priv->mtr[ctlr][ctlr_rank].present) + return 0ULL; + + addr_lines = + I5100_DIMM_ADDR_LINES + + priv->mtr[ctlr][ctlr_rank].numcol + + priv->mtr[ctlr][ctlr_rank].numrow + + priv->mtr[ctlr][ctlr_rank].numbank; + + return (unsigned long) + ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); +} + +static void __devinit i5100_init_mtr(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; + int i; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + struct pci_dev *pdev = mms[i]; + + for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) { + const unsigned addr = + (j < 4) ? I5100_MTR_0 + j * 2 : + I5100_MTR_4 + (j - 4) * 2; + u16 w; + + pci_read_config_word(pdev, addr, &w); + + priv->mtr[i][j].present = I5100_MTR_PRESENT(w); + priv->mtr[i][j].ethrottle = I5100_MTR_ETHROTTLE(w); + priv->mtr[i][j].width = 4 + 4 * I5100_MTR_WIDTH(w); + priv->mtr[i][j].numbank = 2 + I5100_MTR_NUMBANK(w); + priv->mtr[i][j].numrow = 13 + I5100_MTR_NUMROW(w); + priv->mtr[i][j].numcol = 10 + I5100_MTR_NUMCOL(w); + } + } +} + +/* + * FIXME: make this into a real i2c adapter (so that dimm-decode + * will work)? + */ +static int i5100_read_spd_byte(const struct mem_ctl_info *mci, + u8 ch, u8 slot, u8 addr, u8 *byte) +{ + struct i5100_priv *priv = mci->pvt_info; + u16 w; + u32 dw; + unsigned long et; + + pci_read_config_word(priv->mc, I5100_SPDDATA, &w); + if (I5100_SPDDATA_BUSY(w)) + return -1; + + dw = I5100_SPDCMD_DTI(0xa) | + I5100_SPDCMD_CKOVRD(1) | + I5100_SPDCMD_SA(ch * 4 + slot) | + I5100_SPDCMD_BA(addr) | + I5100_SPDCMD_DATA(0) | + I5100_SPDCMD_CMD(0); + pci_write_config_dword(priv->mc, I5100_SPDCMD, dw); + + /* wait up to 100ms */ + et = jiffies + HZ / 10; + udelay(100); + while (1) { + pci_read_config_word(priv->mc, I5100_SPDDATA, &w); + if (!I5100_SPDDATA_BUSY(w)) + break; + udelay(100); + } + + if (!I5100_SPDDATA_RDO(w) || I5100_SPDDATA_SBE(w)) + return -1; + + *byte = I5100_SPDDATA_DATA(w); + + return 0; +} + +/* + * fill dimm chip select map + * + * FIXME: + * o only valid for 4 ranks per controller + * o not the only way to may chip selects to dimm slots + * o investigate if there is some way to obtain this map from the bios + */ +static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + int i; + + WARN_ON(priv->ranksperctlr != 4); + + for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { + int j; + + for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) + priv->dimm_csmap[i][j] = -1; /* default NC */ + } + + /* only 2 chip selects per slot... */ + priv->dimm_csmap[0][0] = 0; + priv->dimm_csmap[0][1] = 3; + priv->dimm_csmap[1][0] = 1; + priv->dimm_csmap[1][1] = 2; + priv->dimm_csmap[2][0] = 2; + priv->dimm_csmap[3][0] = 3; +} + +static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev, + struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + int i; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + + for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) { + u8 rank; + + if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) + priv->dimm_numrank[i][j] = 0; + else + priv->dimm_numrank[i][j] = (rank & 3) + 1; + } + } + + i5100_init_dimm_csmap(mci); +} + +static void __devinit i5100_init_interleaving(struct pci_dev *pdev, + struct mem_ctl_info *mci) +{ + u16 w; + u32 dw; + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; + int i; + + pci_read_config_word(pdev, I5100_TOLM, &w); + priv->tolm = (u64) I5100_TOLM_TOLM(w) * 256 * 1024 * 1024; + + pci_read_config_word(pdev, I5100_MIR0, &w); + priv->mir[0].limit = (u64) I5100_MIR_LIMIT(w) << 28; + priv->mir[0].way[1] = I5100_MIR_WAY1(w); + priv->mir[0].way[0] = I5100_MIR_WAY0(w); + + pci_read_config_word(pdev, I5100_MIR1, &w); + priv->mir[1].limit = (u64) I5100_MIR_LIMIT(w) << 28; + priv->mir[1].way[1] = I5100_MIR_WAY1(w); + priv->mir[1].way[0] = I5100_MIR_WAY0(w); + + pci_read_config_word(pdev, I5100_AMIR_0, &w); + priv->amir[0] = w; + pci_read_config_word(pdev, I5100_AMIR_1, &w); + priv->amir[1] = w; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + + for (j = 0; j < 5; j++) { + int k; + + pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw); + + priv->dmir[i][j].limit = + (u64) I5100_DMIR_LIMIT(dw) << 28; + for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++) + priv->dmir[i][j].rank[k] = + I5100_DMIR_RANK(dw, k); + } + } + + i5100_init_mtr(mci); +} + +static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) +{ + int i; + unsigned long total_pages = 0UL; + struct i5100_priv *priv = mci->pvt_info; + + for (i = 0; i < mci->nr_csrows; i++) { + const unsigned long npages = i5100_npages(mci, i); + const unsigned cntlr = i5100_csrow_to_cntlr(mci, i); + const unsigned rank = i5100_csrow_to_rank(mci, i); + + if (!npages) + continue; + + /* + * FIXME: these two are totally bogus -- I don't see how to + * map them correctly to this structure... + */ + mci->csrows[i].first_page = total_pages; + mci->csrows[i].last_page = total_pages + npages - 1; + mci->csrows[i].page_mask = 0UL; + + mci->csrows[i].nr_pages = npages; + mci->csrows[i].grain = 32; + mci->csrows[i].csrow_idx = i; + mci->csrows[i].dtype = + (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8; + mci->csrows[i].ue_count = 0; + mci->csrows[i].ce_count = 0; + mci->csrows[i].mtype = MEM_RDDR2; + mci->csrows[i].edac_mode = EDAC_SECDED; + mci->csrows[i].mci = mci; + mci->csrows[i].nr_channels = 1; + mci->csrows[i].channels[0].chan_idx = 0; + mci->csrows[i].channels[0].ce_count = 0; + mci->csrows[i].channels[0].csrow = mci->csrows + i; + snprintf(mci->csrows[i].channels[0].label, + sizeof(mci->csrows[i].channels[0].label), + "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank)); + + total_pages += npages; + } +} + +static int __devinit i5100_init_one(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int rc; + struct mem_ctl_info *mci; + struct i5100_priv *priv; + struct pci_dev *ch0mm, *ch1mm; + int ret = 0; + u32 dw; + int ranksperch; + + if (PCI_FUNC(pdev->devfn) != 1) + return -ENODEV; + + rc = pci_enable_device(pdev); + if (rc < 0) { + ret = rc; + goto bail; + } + + /* figure out how many ranks, from strapped state of 48GB_Mode input */ + pci_read_config_dword(pdev, I5100_MS, &dw); + ranksperch = !!(dw & (1 << 8)) * 2 + 4; + + if (ranksperch != 4) { + /* FIXME: get 6 ranks / controller to work - need hw... */ + printk(KERN_INFO "i5100_edac: unsupported configuration.\n"); + ret = -ENODEV; + goto bail; + } + + /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */ + ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_5100_21, 0); + if (!ch0mm) + return -ENODEV; + + rc = pci_enable_device(ch0mm); + if (rc < 0) { + ret = rc; + goto bail_ch0; + } + + /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */ + ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_5100_22, 0); + if (!ch1mm) { + ret = -ENODEV; + goto bail_ch0; + } + + rc = pci_enable_device(ch1mm); + if (rc < 0) { + ret = rc; + goto bail_ch1; + } + + mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0); + if (!mci) { + ret = -ENOMEM; + goto bail_ch1; + } + + mci->dev = &pdev->dev; + + priv = mci->pvt_info; + priv->ranksperctlr = ranksperch; + priv->mc = pdev; + priv->ch0mm = ch0mm; + priv->ch1mm = ch1mm; + + i5100_init_dimm_layout(pdev, mci); + i5100_init_interleaving(pdev, mci); + + mci->mtype_cap = MEM_FLAG_FB_DDR2; + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = "i5100_edac.c"; + mci->mod_ver = "not versioned"; + mci->ctl_name = "i5100"; + mci->dev_name = pci_name(pdev); + mci->ctl_page_to_phys = i5100_ctl_page_to_phys; + + mci->edac_check = i5100_check_error; + + i5100_init_csrows(mci); + + /* this strange construction seems to be in every driver, dunno why */ + switch (edac_op_state) { + case EDAC_OPSTATE_POLL: + case EDAC_OPSTATE_NMI: + break; + default: + edac_op_state = EDAC_OPSTATE_POLL; + break; + } + + if (edac_mc_add_mc(mci)) { + ret = -ENODEV; + goto bail_mc; + } + + goto bail; + +bail_mc: + edac_mc_free(mci); + +bail_ch1: + pci_dev_put(ch1mm); + +bail_ch0: + pci_dev_put(ch0mm); + +bail: + return ret; +} + +static void __devexit i5100_remove_one(struct pci_dev *pdev) +{ + struct mem_ctl_info *mci; + struct i5100_priv *priv; + + mci = edac_mc_del_mc(&pdev->dev); + + if (!mci) + return; + + priv = mci->pvt_info; + pci_dev_put(priv->ch0mm); + pci_dev_put(priv->ch1mm); + + edac_mc_free(mci); +} + +static const struct pci_device_id i5100_pci_tbl[] __devinitdata = { + /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, i5100_pci_tbl); + +static struct pci_driver i5100_driver = { + .name = KBUILD_BASENAME, + .probe = i5100_init_one, + .remove = __devexit_p(i5100_remove_one), + .id_table = i5100_pci_tbl, +}; + +static int __init i5100_init(void) +{ + int pci_rc; + + pci_rc = pci_register_driver(&i5100_driver); + + return (pci_rc < 0) ? pci_rc : 0; +} + +static void __exit i5100_exit(void) +{ + pci_unregister_driver(&i5100_driver); +} + +module_init(i5100_init); +module_exit(i5100_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR + ("Arthur Jones "); +MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers"); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 119ae7b8f028..c3b1761aba26 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2400,6 +2400,9 @@ #define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 #define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 +#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 +#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 #define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 #define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 -- cgit v1.2.3 From 7dcf2a9fced59e58e4694cdcf15850c01fdba89b Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 1 Jul 2008 19:27:16 +0300 Subject: remove dummy asm/kvm.h files This patch removes the dummy asm/kvm.h files on architectures not (yet) supporting KVM and uses the same conditional headers installation as already used for a.out.h . Also removed are superfluous install rules in the s390 and x86 Kbuild files (they are already in Kbuild.asm). Signed-off-by: Adrian Bunk Acked-by: Sam Ravnborg Signed-off-by: David Woodhouse --- include/asm-alpha/kvm.h | 6 ------ include/asm-arm/kvm.h | 6 ------ include/asm-avr32/kvm.h | 6 ------ include/asm-blackfin/kvm.h | 6 ------ include/asm-cris/kvm.h | 6 ------ include/asm-frv/kvm.h | 6 ------ include/asm-generic/Kbuild.asm | 2 ++ include/asm-h8300/kvm.h | 6 ------ include/asm-m32r/kvm.h | 6 ------ include/asm-m68k/kvm.h | 6 ------ include/asm-m68knommu/kvm.h | 6 ------ include/asm-mips/kvm.h | 6 ------ include/asm-mn10300/kvm.h | 6 ------ include/asm-parisc/kvm.h | 6 ------ include/asm-s390/Kbuild | 1 - include/asm-sh/kvm.h | 6 ------ include/asm-sparc/kvm.h | 6 ------ include/asm-sparc64/kvm.h | 1 - include/asm-um/kvm.h | 6 ------ include/asm-x86/Kbuild | 1 - include/asm-xtensa/kvm.h | 6 ------ include/linux/Kbuild | 2 ++ 22 files changed, 4 insertions(+), 105 deletions(-) delete mode 100644 include/asm-alpha/kvm.h delete mode 100644 include/asm-arm/kvm.h delete mode 100644 include/asm-avr32/kvm.h delete mode 100644 include/asm-blackfin/kvm.h delete mode 100644 include/asm-cris/kvm.h delete mode 100644 include/asm-frv/kvm.h delete mode 100644 include/asm-h8300/kvm.h delete mode 100644 include/asm-m32r/kvm.h delete mode 100644 include/asm-m68k/kvm.h delete mode 100644 include/asm-m68knommu/kvm.h delete mode 100644 include/asm-mips/kvm.h delete mode 100644 include/asm-mn10300/kvm.h delete mode 100644 include/asm-parisc/kvm.h delete mode 100644 include/asm-sh/kvm.h delete mode 100644 include/asm-sparc/kvm.h delete mode 100644 include/asm-sparc64/kvm.h delete mode 100644 include/asm-um/kvm.h delete mode 100644 include/asm-xtensa/kvm.h (limited to 'include/linux') diff --git a/include/asm-alpha/kvm.h b/include/asm-alpha/kvm.h deleted file mode 100644 index b9daec429689..000000000000 --- a/include/asm-alpha/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_ALPHA_H -#define __LINUX_KVM_ALPHA_H - -/* alpha does not support KVM */ - -#endif diff --git a/include/asm-arm/kvm.h b/include/asm-arm/kvm.h deleted file mode 100644 index cb3c08cbcb9e..000000000000 --- a/include/asm-arm/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_ARM_H -#define __LINUX_KVM_ARM_H - -/* arm does not support KVM */ - -#endif diff --git a/include/asm-avr32/kvm.h b/include/asm-avr32/kvm.h deleted file mode 100644 index 8c5777020e2c..000000000000 --- a/include/asm-avr32/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_AVR32_H -#define __LINUX_KVM_AVR32_H - -/* avr32 does not support KVM */ - -#endif diff --git a/include/asm-blackfin/kvm.h b/include/asm-blackfin/kvm.h deleted file mode 100644 index e3477d77c014..000000000000 --- a/include/asm-blackfin/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_BLACKFIN_H -#define __LINUX_KVM_BLACKFIN_H - -/* blackfin does not support KVM */ - -#endif diff --git a/include/asm-cris/kvm.h b/include/asm-cris/kvm.h deleted file mode 100644 index c860f51149f0..000000000000 --- a/include/asm-cris/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_CRIS_H -#define __LINUX_KVM_CRIS_H - -/* cris does not support KVM */ - -#endif diff --git a/include/asm-frv/kvm.h b/include/asm-frv/kvm.h deleted file mode 100644 index 9c8a4f08d0a9..000000000000 --- a/include/asm-frv/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_FRV_H -#define __LINUX_KVM_FRV_H - -/* frv does not support KVM */ - -#endif diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index 7cd25b8e7c9a..1170dc60e638 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -1,4 +1,6 @@ +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/kvm.h),) header-y += kvm.h +endif ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h diff --git a/include/asm-h8300/kvm.h b/include/asm-h8300/kvm.h deleted file mode 100644 index bdbed7b987e1..000000000000 --- a/include/asm-h8300/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_H8300_H -#define __LINUX_KVM_H8300_H - -/* h8300 does not support KVM */ - -#endif diff --git a/include/asm-m32r/kvm.h b/include/asm-m32r/kvm.h deleted file mode 100644 index 99a40515b77e..000000000000 --- a/include/asm-m32r/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_M32R_H -#define __LINUX_KVM_M32R_H - -/* m32r does not support KVM */ - -#endif diff --git a/include/asm-m68k/kvm.h b/include/asm-m68k/kvm.h deleted file mode 100644 index 7ed27fce5240..000000000000 --- a/include/asm-m68k/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_M68K_H -#define __LINUX_KVM_M68K_H - -/* m68k does not support KVM */ - -#endif diff --git a/include/asm-m68knommu/kvm.h b/include/asm-m68knommu/kvm.h deleted file mode 100644 index b49d4258dabb..000000000000 --- a/include/asm-m68knommu/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_M68KNOMMU_H -#define __LINUX_KVM_M68KNOMMU_H - -/* m68knommu does not support KVM */ - -#endif diff --git a/include/asm-mips/kvm.h b/include/asm-mips/kvm.h deleted file mode 100644 index 093a5b7f796b..000000000000 --- a/include/asm-mips/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_MIPS_H -#define __LINUX_KVM_MIPS_H - -/* mips does not support KVM */ - -#endif diff --git a/include/asm-mn10300/kvm.h b/include/asm-mn10300/kvm.h deleted file mode 100644 index f6b609ff4a57..000000000000 --- a/include/asm-mn10300/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_MN10300_H -#define __LINUX_KVM_MN10300_H - -/* mn10300 does not support KVM */ - -#endif diff --git a/include/asm-parisc/kvm.h b/include/asm-parisc/kvm.h deleted file mode 100644 index 00cc45812547..000000000000 --- a/include/asm-parisc/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_PARISC_H -#define __LINUX_KVM_PARISC_H - -/* parisc does not support KVM */ - -#endif diff --git a/include/asm-s390/Kbuild b/include/asm-s390/Kbuild index bb5e9edb9825..63a23415fba6 100644 --- a/include/asm-s390/Kbuild +++ b/include/asm-s390/Kbuild @@ -7,7 +7,6 @@ header-y += tape390.h header-y += ucontext.h header-y += vtoc.h header-y += zcrypt.h -header-y += kvm.h header-y += chsc.h unifdef-y += cmb.h diff --git a/include/asm-sh/kvm.h b/include/asm-sh/kvm.h deleted file mode 100644 index 6af51dbab2d0..000000000000 --- a/include/asm-sh/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_SH_H -#define __LINUX_KVM_SH_H - -/* sh does not support KVM */ - -#endif diff --git a/include/asm-sparc/kvm.h b/include/asm-sparc/kvm.h deleted file mode 100644 index 2e5478da3819..000000000000 --- a/include/asm-sparc/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_SPARC_H -#define __LINUX_KVM_SPARC_H - -/* sparc does not support KVM */ - -#endif diff --git a/include/asm-sparc64/kvm.h b/include/asm-sparc64/kvm.h deleted file mode 100644 index 53564ad86b15..000000000000 --- a/include/asm-sparc64/kvm.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-um/kvm.h b/include/asm-um/kvm.h deleted file mode 100644 index 66aa77094551..000000000000 --- a/include/asm-um/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_UM_H -#define __LINUX_KVM_UM_H - -/* um does not support KVM */ - -#endif diff --git a/include/asm-x86/Kbuild b/include/asm-x86/Kbuild index 1e3554596f72..811e9828ccb3 100644 --- a/include/asm-x86/Kbuild +++ b/include/asm-x86/Kbuild @@ -3,7 +3,6 @@ include include/asm-generic/Kbuild.asm header-y += boot.h header-y += bootparam.h header-y += debugreg.h -header-y += kvm.h header-y += ldt.h header-y += msr-index.h header-y += prctl.h diff --git a/include/asm-xtensa/kvm.h b/include/asm-xtensa/kvm.h deleted file mode 100644 index bda4e331e98c..000000000000 --- a/include/asm-xtensa/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_XTENSA_H -#define __LINUX_KVM_XTENSA_H - -/* xtensa does not support KVM */ - -#endif diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 71d70d1fbce2..402c8f55d713 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -256,7 +256,9 @@ unifdef-y += kd.h unifdef-y += kernelcapi.h unifdef-y += kernel.h unifdef-y += keyboard.h +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/kvm.h),) unifdef-y += kvm.h +endif unifdef-y += llc.h unifdef-y += loop.h unifdef-y += lp.h -- cgit v1.2.3 From c6af5e9f8a57467df2e55e428316a43480174521 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Fri, 25 Jul 2008 15:48:04 +0200 Subject: bootmem: Move node allocation macros back to !HAVE_ARCH_BOOTMEM_NODE These got unintentionally moved, put them back as x86 provides its own versions. Signed-off-by: Johannes Weiner Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 4ddf2922fc8d..652470b687c9 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -103,17 +103,16 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, __alloc_bootmem(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages(x) \ __alloc_bootmem_low(x, PAGE_SIZE, 0) -#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ - -extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, - int flags); - #define alloc_bootmem_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_pages_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages_node(pgdat, x) \ __alloc_bootmem_low_node(pgdat, x, PAGE_SIZE, 0) +#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ + +extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, + int flags); extern void *alloc_bootmem_section(unsigned long size, unsigned long section_nr); -- cgit v1.2.3 From b4615e69b6c6353878b734a8202b65efbc554df4 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Fri, 25 Jul 2008 13:19:22 -0700 Subject: sys_paccept definition missing __user annotation Introduced by commit aaca0bdca573f3f51ea03139f9c7289541e7bca3 ("flag parameters: paccept"): net/socket.c:1515:17: error: symbol 'sys_paccept' redeclared with different type (originally declared at include/linux/syscalls.h:413) - incompatible argument 4 (different address spaces) Signed-off-by: Harvey Harrison Signed-off-by: Linus Torvalds --- include/linux/syscalls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 06f2bf76c030..d6ff145919ca 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -411,7 +411,7 @@ asmlinkage long sys_bind(int, struct sockaddr __user *, int); asmlinkage long sys_connect(int, struct sockaddr __user *, int); asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *); asmlinkage long sys_paccept(int, struct sockaddr __user *, int __user *, - const sigset_t *, size_t, int); + const __user sigset_t *, size_t, int); asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *); asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *); asmlinkage long sys_send(int, void __user *, size_t, unsigned); -- cgit v1.2.3 From 3f07af494dfa6de43137dae430431c9fbf929c0c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 25 Jul 2008 22:25:13 -0400 Subject: of: adapt of_find_i2c_driver() to be usable by SPI also SPI has a similar problem as I2C in that it needs to determine an appropriate modalias value for each device node. This patch adapts the of_i2c of_find_i2c_driver() function to be usable by of_spi also. Signed-off-by: Grant Likely --- drivers/of/base.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/of/of_i2c.c | 64 ++------------------------------------ include/linux/of.h | 1 + 3 files changed, 92 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index 23ffb7c0caf2..ad8ac1a8af28 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -385,3 +385,91 @@ struct device_node *of_find_matching_node(struct device_node *from, return np; } EXPORT_SYMBOL(of_find_matching_node); + +/** + * of_modalias_table: Table of explicit compatible ==> modalias mappings + * + * This table allows particulare compatible property values to be mapped + * to modalias strings. This is useful for busses which do not directly + * understand the OF device tree but are populated based on data contained + * within the device tree. SPI and I2C are the two current users of this + * table. + * + * In most cases, devices do not need to be listed in this table because + * the modalias value can be derived directly from the compatible table. + * However, if for any reason a value cannot be derived, then this table + * provides a method to override the implicit derivation. + * + * At the moment, a single table is used for all bus types because it is + * assumed that the data size is small and that the compatible values + * should already be distinct enough to differentiate between SPI, I2C + * and other devices. + */ +struct of_modalias_table { + char *of_device; + char *modalias; +}; +static struct of_modalias_table of_modalias_table[] = { + /* Empty for now; add entries as needed */ +}; + +/** + * of_modalias_node - Lookup appropriate modalias for a device node + * @node: pointer to a device tree node + * @modalias: Pointer to buffer that modalias value will be copied into + * @len: Length of modalias value + * + * Based on the value of the compatible property, this routine will determine + * an appropriate modalias value for a particular device tree node. Three + * separate methods are used to derive a modalias value. + * + * First method is to lookup the compatible value in of_modalias_table. + * Second is to look for a "linux," entry in the compatible list + * and used that for modalias. Third is to strip off the manufacturer + * prefix from the first compatible entry and use the remainder as modalias + * + * This routine returns 0 on success + */ +int of_modalias_node(struct device_node *node, char *modalias, int len) +{ + int i, cplen; + const char *compatible; + const char *p; + + /* 1. search for exception list entry */ + for (i = 0; i < ARRAY_SIZE(of_modalias_table); i++) { + compatible = of_modalias_table[i].of_device; + if (!of_device_is_compatible(node, compatible)) + continue; + strlcpy(modalias, of_modalias_table[i].modalias, len); + return 0; + } + + compatible = of_get_property(node, "compatible", &cplen); + if (!compatible) + return -ENODEV; + + /* 2. search for linux, entry */ + p = compatible; + while (cplen > 0) { + if (!strncmp(p, "linux,", 6)) { + p += 6; + strlcpy(modalias, p, len); + return 0; + } + + i = strlen(p) + 1; + p += i; + cplen -= i; + } + + /* 3. take first compatible entry and strip manufacturer */ + p = strchr(compatible, ','); + if (!p) + return -ENODEV; + p++; + strlcpy(modalias, p, len); + return 0; +} +EXPORT_SYMBOL_GPL(of_modalias_node); + diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 344e1b03dd8b..6a98dc8aa30b 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -16,62 +16,6 @@ #include #include -struct i2c_driver_device { - char *of_device; - char *i2c_type; -}; - -static struct i2c_driver_device i2c_devices[] = { -}; - -static int of_find_i2c_driver(struct device_node *node, - struct i2c_board_info *info) -{ - int i, cplen; - const char *compatible; - const char *p; - - /* 1. search for exception list entry */ - for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) { - if (!of_device_is_compatible(node, i2c_devices[i].of_device)) - continue; - if (strlcpy(info->type, i2c_devices[i].i2c_type, - I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - - return 0; - } - - compatible = of_get_property(node, "compatible", &cplen); - if (!compatible) - return -ENODEV; - - /* 2. search for linux, entry */ - p = compatible; - while (cplen > 0) { - if (!strncmp(p, "linux,", 6)) { - p += 6; - if (strlcpy(info->type, p, - I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - return 0; - } - - i = strlen(p) + 1; - p += i; - cplen -= i; - } - - /* 3. take fist compatible entry and strip manufacturer */ - p = strchr(compatible, ','); - if (!p) - return -ENODEV; - p++; - if (strlcpy(info->type, p, I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - return 0; -} - void of_register_i2c_devices(struct i2c_adapter *adap, struct device_node *adap_node) { @@ -83,6 +27,9 @@ void of_register_i2c_devices(struct i2c_adapter *adap, const u32 *addr; int len; + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) + continue; + addr = of_get_property(node, "reg", &len); if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) { printk(KERN_ERR @@ -92,11 +39,6 @@ void of_register_i2c_devices(struct i2c_adapter *adap, info.irq = irq_of_parse_and_map(node, 0); - if (of_find_i2c_driver(node, &info) < 0) { - irq_dispose_mapping(info.irq); - continue; - } - info.addr = *addr; request_module(info.type); diff --git a/include/linux/of.h b/include/linux/of.h index 59a61bdc98b6..79886ade070f 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -70,5 +70,6 @@ extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( const struct of_device_id *matches, const struct device_node *node); +extern int of_modalias_node(struct device_node *node, char *modalias, int len); #endif /* _LINUX_OF_H */ -- cgit v1.2.3 From dc87c98e8f635a718f1abb2c3e15fc77c0001651 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 15 May 2008 16:50:22 -0600 Subject: spi: split up spi_new_device() to allow two stage registration. spi_new_device() allocates and registers an spi device all in one swoop. If the driver needs to add extra data to the spi_device before it is registered, then this causes problems. This is needed for OF device tree support so that the SPI device tree helper can add a pointer to the device node after the device is allocated, but before the device is registered. OF aware SPI devices can then retrieve data out of the device node to populate a platform data structure. This patch splits the allocation and registration portions of code out of spi_new_device() and creates two new functions; spi_alloc_device() and spi_register_device(). spi_new_device() is modified to use the new functions for allocation and registration. None of the existing users of spi_new_device() should be affected by this change. Drivers using the new API can forego the use of spi_board_info structure to describe the device layout and populate data into the spi_device structure directly. This change is in preparation for adding an OF device tree parser to generate spi_devices based on data in the device tree. Signed-off-by: Grant Likely Acked-by: David Brownell --- drivers/spi/spi.c | 139 +++++++++++++++++++++++++++++++++--------------- include/linux/spi/spi.h | 12 +++++ 2 files changed, 107 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ecca4a6a6f94..964124b60db2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -178,6 +178,96 @@ struct boardinfo { static LIST_HEAD(board_list); static DEFINE_MUTEX(board_lock); +/** + * spi_alloc_device - Allocate a new SPI device + * @master: Controller to which device is connected + * Context: can sleep + * + * Allows a driver to allocate and initialize a spi_device without + * registering it immediately. This allows a driver to directly + * fill the spi_device with device parameters before calling + * spi_add_device() on it. + * + * Caller is responsible to call spi_add_device() on the returned + * spi_device structure to add it to the SPI master. If the caller + * needs to discard the spi_device without adding it, then it should + * call spi_dev_put() on it. + * + * Returns a pointer to the new device, or NULL. + */ +struct spi_device *spi_alloc_device(struct spi_master *master) +{ + struct spi_device *spi; + struct device *dev = master->dev.parent; + + if (!spi_master_get(master)) + return NULL; + + spi = kzalloc(sizeof *spi, GFP_KERNEL); + if (!spi) { + dev_err(dev, "cannot alloc spi_device\n"); + spi_master_put(master); + return NULL; + } + + spi->master = master; + spi->dev.parent = dev; + spi->dev.bus = &spi_bus_type; + spi->dev.release = spidev_release; + device_initialize(&spi->dev); + return spi; +} +EXPORT_SYMBOL_GPL(spi_alloc_device); + +/** + * spi_add_device - Add spi_device allocated with spi_alloc_device + * @spi: spi_device to register + * + * Companion function to spi_alloc_device. Devices allocated with + * spi_alloc_device can be added onto the spi bus with this function. + * + * Returns 0 on success; non-zero on failure + */ +int spi_add_device(struct spi_device *spi) +{ + struct device *dev = spi->master->dev.parent; + int status; + + /* Chipselects are numbered 0..max; validate. */ + if (spi->chip_select >= spi->master->num_chipselect) { + dev_err(dev, "cs%d >= max %d\n", + spi->chip_select, + spi->master->num_chipselect); + return -EINVAL; + } + + /* Set the bus ID string */ + snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id, + "%s.%u", spi->master->dev.bus_id, + spi->chip_select); + + /* drivers may modify this initial i/o setup */ + status = spi->master->setup(spi); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "setup", spi->dev.bus_id, status); + return status; + } + + /* driver core catches callers that misbehave by defining + * devices that already exist. + */ + status = device_add(&spi->dev); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "add", spi->dev.bus_id, status); + return status; + } + + dev_dbg(dev, "registered child %s\n", spi->dev.bus_id); + return 0; +} +EXPORT_SYMBOL_GPL(spi_add_device); /** * spi_new_device - instantiate one new SPI device @@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; - struct device *dev = master->dev.parent; int status; /* NOTE: caller did any chip->bus_num checks necessary. @@ -207,66 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master, * suggests syslogged diagnostics are best here (ugh). */ - /* Chipselects are numbered 0..max; validate. */ - if (chip->chip_select >= master->num_chipselect) { - dev_err(dev, "cs%d > max %d\n", - chip->chip_select, - master->num_chipselect); - return NULL; - } - - if (!spi_master_get(master)) + proxy = spi_alloc_device(master); + if (!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); - proxy = kzalloc(sizeof *proxy, GFP_KERNEL); - if (!proxy) { - dev_err(dev, "can't alloc dev for cs%d\n", - chip->chip_select); - goto fail; - } - proxy->master = master; proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); - - snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id, - "%s.%u", master->dev.bus_id, - chip->chip_select); - proxy->dev.parent = dev; - proxy->dev.bus = &spi_bus_type; proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; - proxy->dev.release = spidev_release; - /* drivers may modify this initial i/o setup */ - status = master->setup(proxy); + status = spi_add_device(proxy); if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "setup", proxy->dev.bus_id, status); - goto fail; + spi_dev_put(proxy); + return NULL; } - /* driver core catches callers that misbehave by defining - * devices that already exist. - */ - status = device_register(&proxy->dev); - if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "add", proxy->dev.bus_id, status); - goto fail; - } - dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id); return proxy; - -fail: - spi_master_put(master); - kfree(proxy); - return NULL; } EXPORT_SYMBOL_GPL(spi_new_device); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index a9cc29d46653..4be01bb44377 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -778,7 +778,19 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) * use spi_new_device() to describe each device. You can also call * spi_unregister_device() to start making that device vanish, but * normally that would be handled by spi_unregister_master(). + * + * You can also use spi_alloc_device() and spi_add_device() to use a two + * stage registration sequence for each spi_device. This gives the caller + * some more control over the spi_device structure before it is registered, + * but requires that caller to initialize fields that would otherwise + * be defined using the board info. */ +extern struct spi_device * +spi_alloc_device(struct spi_master *master); + +extern int +spi_add_device(struct spi_device *spi); + extern struct spi_device * spi_new_device(struct spi_master *, struct spi_board_info *); -- cgit v1.2.3 From 284b01897340974000bcc84de87a4e1becc8a83d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 16 May 2008 11:37:09 -0600 Subject: spi: Add OF binding support for SPI busses This patch adds support for populating an SPI bus based on data in the OF device tree. This is useful for powerpc platforms which use the device tree instead of discrete code for describing platform layout. Signed-off-by: Grant Likely --- drivers/of/Kconfig | 6 ++++ drivers/of/Makefile | 1 + drivers/of/of_spi.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_spi.h | 18 ++++++++++ 4 files changed, 118 insertions(+) create mode 100644 drivers/of/of_spi.c create mode 100644 include/linux/of_spi.h (limited to 'include/linux') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 1d7ec3129349..f821dbc952a4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -13,3 +13,9 @@ config OF_I2C depends on PPC_OF && I2C help OpenFirmware I2C accessors + +config OF_SPI + def_tristate SPI + depends on OF && PPC_OF && SPI + help + OpenFirmware SPI accessors diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 548772e871fd..4c3c6f8e36f5 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -2,3 +2,4 @@ obj-y = base.o obj-$(CONFIG_OF_DEVICE) += device.o platform.o obj-$(CONFIG_OF_GPIO) += gpio.o obj-$(CONFIG_OF_I2C) += of_i2c.o +obj-$(CONFIG_OF_SPI) += of_spi.o diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c new file mode 100644 index 000000000000..b01eec026f68 --- /dev/null +++ b/drivers/of/of_spi.c @@ -0,0 +1,93 @@ +/* + * SPI OF support routines + * Copyright (C) 2008 Secret Lab Technologies Ltd. + * + * Support routines for deriving SPI device attachments from the device + * tree. + */ + +#include +#include +#include +#include + +/** + * of_register_spi_devices - Register child devices onto the SPI bus + * @master: Pointer to spi_master device + * @np: parent node of SPI device nodes + * + * Registers an spi_device for each child node of 'np' which has a 'reg' + * property. + */ +void of_register_spi_devices(struct spi_master *master, struct device_node *np) +{ + struct spi_device *spi; + struct device_node *nc; + const u32 *prop; + int rc; + int len; + + for_each_child_of_node(np, nc) { + /* Alloc an spi_device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(&master->dev, "spi_device alloc error for %s\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + + /* Select device driver */ + if (of_modalias_node(nc, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(&master->dev, "cannot find modalias for %s\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + + /* Device address */ + prop = of_get_property(nc, "reg", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'reg' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + spi->chip_select = *prop; + + /* Mode (clock phase/polarity/etc.) */ + if (of_find_property(nc, "spi-cpha", NULL)) + spi->mode |= SPI_CPHA; + if (of_find_property(nc, "spi-cpol", NULL)) + spi->mode |= SPI_CPOL; + + /* Device speed */ + prop = of_get_property(nc, "spi-max-frequency", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'spi-max-frequency' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + spi->max_speed_hz = *prop; + + /* IRQ */ + spi->irq = irq_of_parse_and_map(nc, 0); + + /* Store a pointer to the node in the device structure */ + of_node_get(nc); + spi->dev.archdata.of_node = nc; + + /* Register the new device */ + request_module(spi->modalias); + rc = spi_add_device(spi); + if (rc) { + dev_err(&master->dev, "spi_device register error %s\n", + nc->full_name); + spi_dev_put(spi); + } + + } +} +EXPORT_SYMBOL(of_register_spi_devices); diff --git a/include/linux/of_spi.h b/include/linux/of_spi.h new file mode 100644 index 000000000000..5f71ee8c0868 --- /dev/null +++ b/include/linux/of_spi.h @@ -0,0 +1,18 @@ +/* + * OpenFirmware SPI support routines + * Copyright (C) 2008 Secret Lab Technologies Ltd. + * + * Support routines for deriving SPI device attachments from the device + * tree. + */ + +#ifndef __LINUX_OF_SPI_H +#define __LINUX_OF_SPI_H + +#include +#include + +extern void of_register_spi_devices(struct spi_master *master, + struct device_node *np); + +#endif /* __LINUX_OF_SPI */ -- cgit v1.2.3 From ec34c702ca8b7d6f0aa54379c3b0d0ec10b8ff23 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 25 Jul 2008 21:45:49 -0700 Subject: net: drop unused BUG_TRAP() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index f4d386c191f5..ca643b13b026 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -755,13 +755,6 @@ extern void __rtnl_unlock(void); } \ } while(0) -#define BUG_TRAP(x) do { \ - if (unlikely(!(x))) { \ - printk(KERN_ERR "KERNEL: assertion (%s) failed at %s (%d)\n", \ - #x, __FILE__ , __LINE__); \ - } \ -} while(0) - static inline u32 rtm_get_table(struct rtattr **rta, u8 table) { return RTA_GET_U32(rta[RTA_TABLE-1]); -- cgit v1.2.3 From 36ac26171afa8dbf29226199699fe955d4a0b6f6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sat, 26 Jul 2008 11:22:33 +0200 Subject: crashdump: fix undefined reference to `elfcorehdr_addr' fix build bug introduced by 95b68dec0d5 "calgary iommu: use the first kernels TCE tables in kdump": arch/x86/kernel/built-in.o: In function `calgary_iommu_init': (.init.text+0x8399): undefined reference to `elfcorehdr_addr' arch/x86/kernel/built-in.o: In function `calgary_iommu_init': (.init.text+0x856c): undefined reference to `elfcorehdr_addr' arch/x86/kernel/built-in.o: In function `detect_calgary': (.init.text+0x8c68): undefined reference to `elfcorehdr_addr' arch/x86/kernel/built-in.o: In function `detect_calgary': (.init.text+0x8d0c): undefined reference to `elfcorehdr_addr' make elfcorehdr_addr a generally available symbol. Signed-off-by: Ingo Molnar --- include/linux/crash_dump.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h index 6cd39a927e1f..025e4f575103 100644 --- a/include/linux/crash_dump.h +++ b/include/linux/crash_dump.h @@ -8,7 +8,13 @@ #include #define ELFCORE_ADDR_MAX (-1ULL) + +#ifdef CONFIG_PROC_VMCORE extern unsigned long long elfcorehdr_addr; +#else +static const unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX; +#endif + extern ssize_t copy_oldmem_page(unsigned long, char *, size_t, unsigned long, int); extern const struct file_operations proc_vmcore_operations; -- cgit v1.2.3 From 021f8b75e78f9da67421a2c2e320e8934a90914a Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 22 Jul 2008 21:08:45 +0200 Subject: x86: add PCI IDs for AMD Barcelona PCI devices Signed-off-by: Robert Richter Cc: oprofile-list Cc: Barry Kasindorf Signed-off-by: Ingo Molnar --- include/linux/pci_ids.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c3b1761aba26..917d48e5ea06 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -497,6 +497,11 @@ #define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 #define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 #define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 #define PCI_DEVICE_ID_AMD_SCSI 0x2020 -- cgit v1.2.3 From ee648bc77f11b57d15a68d336fc30e343198f893 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 22 Jul 2008 21:08:53 +0200 Subject: OProfile: add IBS code macros Signed-off-by: Robert Richter Cc: oprofile-list Cc: Barry Kasindorf Signed-off-by: Ingo Molnar --- include/linux/oprofile.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 041bb31100f4..bcb8f725427c 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -36,6 +36,8 @@ #define XEN_ENTER_SWITCH_CODE 10 #define SPU_PROFILING_CODE 11 #define SPU_CTX_SWITCH_CODE 12 +#define IBS_FETCH_CODE 13 +#define IBS_OP_CODE 14 struct super_block; struct dentry; -- cgit v1.2.3 From 3bc9f79ee1ddc913be0a6d3592036683ef8a3148 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 25 Jul 2008 14:57:58 +0200 Subject: iommu: add iommu_num_pages helper function Calculating the number of pages from given address and length numbers is a task required in multiple IOMMU implementations. So implement this as a generic function into the IOMMU helper code. Signed-off-by: Joerg Roedel Cc: iommu@lists.linux-foundation.org Cc: bhavna.sarathy@amd.com Cc: robert.richter@amd.com Cc: FUJITA Tomonori Signed-off-by: Ingo Molnar --- include/linux/iommu-helper.h | 1 + lib/iommu-helper.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h index c975caf75385..f8598f583944 100644 --- a/include/linux/iommu-helper.h +++ b/include/linux/iommu-helper.h @@ -8,3 +8,4 @@ extern unsigned long iommu_area_alloc(unsigned long *map, unsigned long size, unsigned long align_mask); extern void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr); +extern unsigned long iommu_num_pages(unsigned long addr, unsigned long len); diff --git a/lib/iommu-helper.c b/lib/iommu-helper.c index a3b8d4c3f77a..889ddce2021e 100644 --- a/lib/iommu-helper.c +++ b/lib/iommu-helper.c @@ -80,3 +80,11 @@ void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr) } } EXPORT_SYMBOL(iommu_area_free); + +unsigned long iommu_num_pages(unsigned long addr, unsigned long len) +{ + unsigned long size = roundup((addr & ~PAGE_MASK) + len, PAGE_SIZE); + + return size >> PAGE_SHIFT; +} +EXPORT_SYMBOL(iommu_num_pages); -- cgit v1.2.3 From 0af36739af81f152cc24a0fdfa0754ef657afe3d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 24 Jul 2008 17:27:57 -0700 Subject: usb: move ehci reg def prepare x86: usb debug port early console move ehci struct def to linux/usrb/ehci_def.h from host/ehci.h Signed-off-by: Yinghai Lu Acked-by: David Brownell Cc: Andrew Morton Cc: Andi Kleen Cc: "Arjan van de Ven" Cc: "Eric W. Biederman" Cc: "Greg KH" Signed-off-by: Ingo Molnar --- drivers/usb/host/ehci.h | 138 +------------------------------------ include/linux/usb/ehci_def.h | 160 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 137 deletions(-) create mode 100644 include/linux/usb/ehci_def.h (limited to 'include/linux') diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 5799298364fb..b697a13364ec 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -210,143 +210,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) /*-------------------------------------------------------------------------*/ -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct ehci_caps { - /* these fields are specified as 8 and 16 bit registers, - * but some hosts can't perform 8 or 16 bit PCI accesses. - */ - u32 hc_capbase; -#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ -#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ - u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ -#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ -#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ -#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ -#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ -#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ -#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ -#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ -#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ -#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ - u8 portroute [8]; /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); - - -/* Section 2.3 Host Controller Operational Registers */ -struct ehci_regs { - - /* USBCMD: offset 0x00 */ - u32 command; -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK (1<<11) /* enable "park" on async qh */ -#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ -#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -#define CMD_ASE (1<<5) /* async schedule enable */ -#define CMD_PSE (1<<4) /* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ - - /* USBSTS: offset 0x04 */ - u32 status; -#define STS_ASS (1<<15) /* Async Schedule Status */ -#define STS_PSS (1<<14) /* Periodic Schedule Status */ -#define STS_RECL (1<<13) /* Reclamation */ -#define STS_HALT (1<<12) /* Not running (any reason) */ -/* some bits reserved */ - /* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA (1<<5) /* Interrupted on async advance */ -#define STS_FATAL (1<<4) /* such as some PCI access errors */ -#define STS_FLR (1<<3) /* frame list rolled over */ -#define STS_PCD (1<<2) /* port change detect */ -#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -#define STS_INT (1<<0) /* "normal" completion (short, ...) */ - - /* USBINTR: offset 0x08 */ - u32 intr_enable; - - /* FRINDEX: offset 0x0C */ - u32 frame_index; /* current microframe number */ - /* CTRLDSSEGMENT: offset 0x10 */ - u32 segment; /* address bits 63:32 if needed */ - /* PERIODICLISTBASE: offset 0x14 */ - u32 frame_list; /* points to periodic list */ - /* ASYNCLISTADDR: offset 0x18 */ - u32 async_next; /* address of next async queue head */ - - u32 reserved [9]; - - /* CONFIGFLAG: offset 0x40 */ - u32 configured_flag; -#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ - - /* PORTSC: offset 0x44 */ - u32 port_status [0]; /* up to N_PORTS */ -/* 31:23 reserved */ -#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ -#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ -#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ -/* 19:16 for port testing */ -#define PORT_LED_OFF (0<<14) -#define PORT_LED_AMBER (1<<14) -#define PORT_LED_GREEN (2<<14) -#define PORT_LED_MASK (3<<14) -#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ -#define PORT_POWER (1<<12) /* true: has power (see PPC) */ -#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ -/* 9 reserved */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_OCC (1<<5) /* over current change */ -#define PORT_OC (1<<4) /* over current active */ -#define PORT_PEC (1<<3) /* port enable change */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -} __attribute__ ((packed)); - -#define USBMODE 0x68 /* USB Device mode */ -#define USBMODE_SDIS (1<<3) /* Stream disable */ -#define USBMODE_BE (1<<2) /* BE/LE endianness select */ -#define USBMODE_CM_HC (3<<0) /* host controller mode */ -#define USBMODE_CM_IDLE (0<<0) /* idle state */ - -/* Appendix C, Debug port ... intended for use with special "debug devices" - * that can help if there's no serial console. (nonstandard enumeration.) - */ -struct ehci_dbg_port { - u32 control; -#define DBGP_OWNER (1<<30) -#define DBGP_ENABLED (1<<28) -#define DBGP_DONE (1<<16) -#define DBGP_INUSE (1<<10) -#define DBGP_ERRCODE(x) (((x)>>7)&0x07) -# define DBGP_ERR_BAD 1 -# define DBGP_ERR_SIGNAL 2 -#define DBGP_ERROR (1<<6) -#define DBGP_GO (1<<5) -#define DBGP_OUT (1<<4) -#define DBGP_LEN(x) (((x)>>0)&0x0f) - u32 pids; -#define DBGP_PID_GET(x) (((x)>>16)&0xff) -#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok)) - u32 data03; - u32 data47; - u32 address; -#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep)) -} __attribute__ ((packed)); +#include /*-------------------------------------------------------------------------*/ diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h new file mode 100644 index 000000000000..5b88e36c9103 --- /dev/null +++ b/include/linux/usb/ehci_def.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001-2002 by David Brownell + * + * 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. + * + * 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, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_USB_EHCI_DEF_H +#define __LINUX_USB_EHCI_DEF_H + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute [8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved [9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status [0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1<<3) /* Stream disable */ +#define USBMODE_BE (1<<2) /* BE/LE endianness select */ +#define USBMODE_CM_HC (3<<0) /* host controller mode */ +#define USBMODE_CM_IDLE (0<<0) /* idle state */ + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __attribute__ ((packed)); + +#endif /* __LINUX_USB_EHCI_DEF_H */ -- cgit v1.2.3 From b8d317d10cca76cabe6b03ebfeb23cc99118b731 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Thu, 24 Jul 2008 18:21:29 -0700 Subject: cpumask: make cpumask_of_cpu_map generic If an arch doesn't define cpumask_of_cpu_map, create a generic statically-initialized one for them. This allows removal of the buggy cpumask_of_cpu() macro (&cpumask_of_cpu() gives address of out-of-scope var). An arch with NR_CPUS of 4096 probably wants to allocate this itself based on the actual number of CPUs, since otherwise they're using 2MB of rodata (1024 cpus means 128k). That's what CONFIG_HAVE_CPUMASK_OF_CPU_MAP is for (only x86/64 does so at the moment). In future as we support more CPUs, we'll need to resort to a get_cpu_map()/put_cpu_map() allocation scheme. Signed-off-by: Mike Travis Signed-off-by: Rusty Russell Cc: Andrew Morton Cc: Jack Steiner Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 41 ++---------------- kernel/cpu.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 1b5c98e7fef7..8fa3b6d4a320 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -62,15 +62,7 @@ * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids * * cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set - *ifdef CONFIG_HAS_CPUMASK_OF_CPU - * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t *v - * cpumask_of_cpu_ptr_next(v, cpu) Sets v = &cpumask_of_cpu_map[cpu] - * cpumask_of_cpu_ptr(v, cpu) Combines above two operations - *else - * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t _v and *v = &_v - * cpumask_of_cpu_ptr_next(v, cpu) Sets _v = cpumask_of_cpu(cpu) - * cpumask_of_cpu_ptr(v, cpu) Combines above two operations - *endif + * (can be used as an lvalue) * CPU_MASK_ALL Initializer - all bits set * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask @@ -274,36 +266,9 @@ static inline void __cpus_shift_left(cpumask_t *dstp, } -#ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP -extern cpumask_t *cpumask_of_cpu_map; +/* cpumask_of_cpu_map[] is in kernel/cpu.c */ +extern const cpumask_t *cpumask_of_cpu_map; #define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) -#define cpumask_of_cpu_ptr(v, cpu) \ - const cpumask_t *v = &cpumask_of_cpu(cpu) -#define cpumask_of_cpu_ptr_declare(v) \ - const cpumask_t *v -#define cpumask_of_cpu_ptr_next(v, cpu) \ - v = &cpumask_of_cpu(cpu) -#else -#define cpumask_of_cpu(cpu) \ -({ \ - typeof(_unused_cpumask_arg_) m; \ - if (sizeof(m) == sizeof(unsigned long)) { \ - m.bits[0] = 1UL<<(cpu); \ - } else { \ - cpus_clear(m); \ - cpu_set((cpu), m); \ - } \ - m; \ -}) -#define cpumask_of_cpu_ptr(v, cpu) \ - cpumask_t _##v = cpumask_of_cpu(cpu); \ - const cpumask_t *v = &_##v -#define cpumask_of_cpu_ptr_declare(v) \ - cpumask_t _##v; \ - const cpumask_t *v = &_##v -#define cpumask_of_cpu_ptr_next(v, cpu) \ - _##v = cpumask_of_cpu(cpu) -#endif #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/kernel/cpu.c b/kernel/cpu.c index 10ba5f1004a5..fe31ff3d3809 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -461,3 +461,112 @@ out: #endif /* CONFIG_PM_SLEEP_SMP */ #endif /* CONFIG_SMP */ + +#ifndef CONFIG_HAVE_CPUMASK_OF_CPU_MAP +/* 64 bits of zeros, for initializers. */ +#if BITS_PER_LONG == 32 +#define Z64 0, 0 +#else +#define Z64 0 +#endif + +/* Initializer macros. */ +#define CMI0(n) { .bits = { 1UL << (n) } } +#define CMI(n, ...) { .bits = { __VA_ARGS__, 1UL << ((n) % BITS_PER_LONG) } } + +#define CMI8(n, ...) \ + CMI((n), __VA_ARGS__), CMI((n)+1, __VA_ARGS__), \ + CMI((n)+2, __VA_ARGS__), CMI((n)+3, __VA_ARGS__), \ + CMI((n)+4, __VA_ARGS__), CMI((n)+5, __VA_ARGS__), \ + CMI((n)+6, __VA_ARGS__), CMI((n)+7, __VA_ARGS__) + +#if BITS_PER_LONG == 32 +#define CMI64(n, ...) \ + CMI8((n), __VA_ARGS__), CMI8((n)+8, __VA_ARGS__), \ + CMI8((n)+16, __VA_ARGS__), CMI8((n)+24, __VA_ARGS__), \ + CMI8((n)+32, 0, __VA_ARGS__), CMI8((n)+40, 0, __VA_ARGS__), \ + CMI8((n)+48, 0, __VA_ARGS__), CMI8((n)+56, 0, __VA_ARGS__) +#else +#define CMI64(n, ...) \ + CMI8((n), __VA_ARGS__), CMI8((n)+8, __VA_ARGS__), \ + CMI8((n)+16, __VA_ARGS__), CMI8((n)+24, __VA_ARGS__), \ + CMI8((n)+32, __VA_ARGS__), CMI8((n)+40, __VA_ARGS__), \ + CMI8((n)+48, __VA_ARGS__), CMI8((n)+56, __VA_ARGS__) +#endif + +#define CMI256(n, ...) \ + CMI64((n), __VA_ARGS__), CMI64((n)+64, Z64, __VA_ARGS__), \ + CMI64((n)+128, Z64, Z64, __VA_ARGS__), \ + CMI64((n)+192, Z64, Z64, Z64, __VA_ARGS__) +#define Z256 Z64, Z64, Z64, Z64 + +#define CMI1024(n, ...) \ + CMI256((n), __VA_ARGS__), \ + CMI256((n)+256, Z256, __VA_ARGS__), \ + CMI256((n)+512, Z256, Z256, __VA_ARGS__), \ + CMI256((n)+768, Z256, Z256, Z256, __VA_ARGS__) +#define Z1024 Z256, Z256, Z256, Z256 + +/* We want this statically initialized, just to be safe. We try not + * to waste too much space, either. */ +static const cpumask_t cpumask_map[] = { + CMI0(0), CMI0(1), CMI0(2), CMI0(3), +#if NR_CPUS > 4 + CMI0(4), CMI0(5), CMI0(6), CMI0(7), +#endif +#if NR_CPUS > 8 + CMI0(8), CMI0(9), CMI0(10), CMI0(11), + CMI0(12), CMI0(13), CMI0(14), CMI0(15), +#endif +#if NR_CPUS > 16 + CMI0(16), CMI0(17), CMI0(18), CMI0(19), + CMI0(20), CMI0(21), CMI0(22), CMI0(23), + CMI0(24), CMI0(25), CMI0(26), CMI0(27), + CMI0(28), CMI0(29), CMI0(30), CMI0(31), +#endif +#if NR_CPUS > 32 +#if BITS_PER_LONG == 32 + CMI(32, 0), CMI(33, 0), CMI(34, 0), CMI(35, 0), + CMI(36, 0), CMI(37, 0), CMI(38, 0), CMI(39, 0), + CMI(40, 0), CMI(41, 0), CMI(42, 0), CMI(43, 0), + CMI(44, 0), CMI(45, 0), CMI(46, 0), CMI(47, 0), + CMI(48, 0), CMI(49, 0), CMI(50, 0), CMI(51, 0), + CMI(52, 0), CMI(53, 0), CMI(54, 0), CMI(55, 0), + CMI(56, 0), CMI(57, 0), CMI(58, 0), CMI(59, 0), + CMI(60, 0), CMI(61, 0), CMI(62, 0), CMI(63, 0), +#else + CMI0(32), CMI0(33), CMI0(34), CMI0(35), + CMI0(36), CMI0(37), CMI0(38), CMI0(39), + CMI0(40), CMI0(41), CMI0(42), CMI0(43), + CMI0(44), CMI0(45), CMI0(46), CMI0(47), + CMI0(48), CMI0(49), CMI0(50), CMI0(51), + CMI0(52), CMI0(53), CMI0(54), CMI0(55), + CMI0(56), CMI0(57), CMI0(58), CMI0(59), + CMI0(60), CMI0(61), CMI0(62), CMI0(63), +#endif /* BITS_PER_LONG == 64 */ +#endif +#if NR_CPUS > 64 + CMI64(64, Z64), +#endif +#if NR_CPUS > 128 + CMI64(128, Z64, Z64), CMI64(192, Z64, Z64, Z64), +#endif +#if NR_CPUS > 256 + CMI256(256, Z256), +#endif +#if NR_CPUS > 512 + CMI256(512, Z256, Z256), CMI256(768, Z256, Z256, Z256), +#endif +#if NR_CPUS > 1024 + CMI1024(1024, Z1024), +#endif +#if NR_CPUS > 2048 + CMI1024(2048, Z1024, Z1024), CMI1024(3072, Z1024, Z1024, Z1024), +#endif +#if NR_CPUS > 4096 +#error NR_CPUS too big. Fix initializers or set CONFIG_HAVE_CPUMASK_OF_CPU_MAP +#endif +}; + +const cpumask_t *cpumask_of_cpu_map = cpumask_map; +#endif /* !CONFIG_HAVE_CPUMASK_OF_CPU_MAP */ -- cgit v1.2.3 From fdd2a7e2dac56a3384068802be46b822f2aed703 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2008 13:25:25 -0300 Subject: V4L/DVB (8500a): videotext.h: whitespace cleanup Signed-off-by: Mauro Carvalho Chehab --- include/linux/videotext.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/videotext.h b/include/linux/videotext.h index 018f92047ff8..3e68c8d1c7f7 100644 --- a/include/linux/videotext.h +++ b/include/linux/videotext.h @@ -45,10 +45,10 @@ #define VTXIOCCLRCACHE_OLD 0x710b /* clear cache on VTX-interface (if avail.) */ #define VTXIOCSETVIRT_OLD 0x710c /* turn on virtual mode (this disables TV-display) */ -/* +/* * Definitions for VTXIOCGETINFO */ - + #define SAA5243 0 #define SAA5246 1 #define SAA5249 2 @@ -57,10 +57,10 @@ typedef struct { int version_major, version_minor; /* version of driver; if version_major changes, driver */ - /* is not backward compatible!!! CHECK THIS!!! */ + /* is not backward compatible!!! CHECK THIS!!! */ int numpages; /* number of page-buffers of vtx-chipset */ int cct_type; /* type of vtx-chipset (SAA5243, SAA5246, SAA5248 or - * SAA5249) */ + * SAA5249) */ } vtx_info_t; @@ -81,7 +81,7 @@ vtx_info_t; #define PGMASK_HOUR (HR_TEN | HR_UNIT) #define PGMASK_MINUTE (MIN_TEN | MIN_UNIT) -typedef struct +typedef struct { int page; /* number of requested page (hexadecimal) */ int hour; /* requested hour (hexadecimal) */ @@ -98,11 +98,11 @@ vtx_pagereq_t; /* * Definitions for VTXIOC{GETSTAT,PUTSTAT} */ - + #define VTX_PAGESIZE (40 * 24) #define VTX_VIRTUALSIZE (40 * 49) -typedef struct +typedef struct { int pagenum; /* number of page (hexadecimal) */ int hour; /* hour (hexadecimal) */ @@ -121,5 +121,5 @@ typedef struct unsigned hamming : 1; /* hamming-error occurred */ } vtx_pageinfo_t; - + #endif /* _VTX_H */ -- cgit v1.2.3 From 16d69265b930f7e2fa9eea381715696f780718f4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 25 Jul 2008 19:44:36 -0700 Subject: uninline arch_pick_mmap_layout() Fix this, on avr32: include/linux/utsname.h:35, from init/main.c:20: include/linux/sched.h: In function 'arch_pick_mmap_layout': include/linux/sched.h:2149: error: implicit declaration of function 'PAGE_ALIGN' Reported-by: Adrian Bunk Cc: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 9 --------- mm/util.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 42036ffe6b00..3260a5c42b91 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2139,16 +2139,7 @@ static inline void set_task_cpu(struct task_struct *p, unsigned int cpu) #endif /* CONFIG_SMP */ -#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT extern void arch_pick_mmap_layout(struct mm_struct *mm); -#else -static inline void arch_pick_mmap_layout(struct mm_struct *mm) -{ - mm->mmap_base = TASK_UNMAPPED_BASE; - mm->get_unmapped_area = arch_get_unmapped_area; - mm->unmap_area = arch_unmap_area; -} -#endif #ifdef CONFIG_TRACING extern void diff --git a/mm/util.c b/mm/util.c index 8f18683825bc..0efd83097ecf 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -136,3 +137,12 @@ char *strndup_user(const char __user *s, long n) return p; } EXPORT_SYMBOL(strndup_user); + +#ifndef HAVE_ARCH_PICK_MMAP_LAYOUT +void arch_pick_mmap_layout(struct mm_struct *mm) +{ + mm->mmap_base = TASK_UNMAPPED_BASE; + mm->get_unmapped_area = arch_get_unmapped_area; + mm->unmap_area = arch_unmap_area; +} +#endif -- cgit v1.2.3 From 8d8bb39b9eba32dd70e87fd5ad5c5dd4ba118e06 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 25 Jul 2008 19:44:49 -0700 Subject: dma-mapping: add the device argument to dma_mapping_error() Add per-device dma_mapping_ops support for CONFIG_X86_64 as POWER architecture does: This enables us to cleanly fix the Calgary IOMMU issue that some devices are not behind the IOMMU (http://lkml.org/lkml/2008/5/8/423). I think that per-device dma_mapping_ops support would be also helpful for KVM people to support PCI passthrough but Andi thinks that this makes it difficult to support the PCI passthrough (see the above thread). So I CC'ed this to KVM camp. Comments are appreciated. A pointer to dma_mapping_ops to struct dev_archdata is added. If the pointer is non NULL, DMA operations in asm/dma-mapping.h use it. If it's NULL, the system-wide dma_ops pointer is used as before. If it's useful for KVM people, I plan to implement a mechanism to register a hook called when a new pci (or dma capable) device is created (it works with hot plugging). It enables IOMMUs to set up an appropriate dma_mapping_ops per device. The major obstacle is that dma_mapping_error doesn't take a pointer to the device unlike other DMA operations. So x86 can't have dma_mapping_ops per device. Note all the POWER IOMMUs use the same dma_mapping_error function so this is not a problem for POWER but x86 IOMMUs use different dma_mapping_error functions. The first patch adds the device argument to dma_mapping_error. The patch is trivial but large since it touches lots of drivers and dma-mapping.h in all the architecture. This patch: dma_mapping_error() doesn't take a pointer to the device unlike other DMA operations. So we can't have dma_mapping_ops per device. Note that POWER already has dma_mapping_ops per device but all the POWER IOMMUs use the same dma_mapping_error function. x86 IOMMUs use device argument. [akpm@linux-foundation.org: fix sge] [akpm@linux-foundation.org: fix svc_rdma] [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: fix bnx2x] [akpm@linux-foundation.org: fix s2io] [akpm@linux-foundation.org: fix pasemi_mac] [akpm@linux-foundation.org: fix sdhci] [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: fix sparc] [akpm@linux-foundation.org: fix ibmvscsi] Signed-off-by: FUJITA Tomonori Cc: Muli Ben-Yehuda Cc: Andi Kleen Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Avi Kivity Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/DMA-API.txt | 4 +- arch/arm/common/dmabounce.c | 2 +- arch/ia64/hp/common/hwsw_iommu.c | 5 +- arch/ia64/hp/common/sba_iommu.c | 2 +- arch/ia64/sn/pci/pci_dma.c | 2 +- arch/mips/mm/dma-default.c | 2 +- arch/powerpc/platforms/cell/celleb_scc_pciex.c | 2 +- arch/powerpc/platforms/cell/spider-pci.c | 2 +- arch/powerpc/platforms/iseries/mf.c | 2 +- arch/x86/kernel/pci-calgary_64.c | 2 +- arch/x86/kernel/pci-dma.c | 27 ++++--- arch/x86/kernel/pci-gart_64.c | 3 +- arch/x86/kernel/pci-nommu.c | 14 +--- arch/x86/kernel/pci-swiotlb_64.c | 2 +- drivers/firewire/fw-iso.c | 2 +- drivers/firewire/fw-ohci.c | 2 +- drivers/firewire/fw-sbp2.c | 8 +-- drivers/infiniband/hw/ipath/ipath_sdma.c | 2 +- drivers/infiniband/hw/ipath/ipath_user_sdma.c | 6 +- drivers/infiniband/hw/mthca/mthca_eq.c | 2 +- drivers/media/dvb/pluto2/pluto2.c | 2 +- drivers/mmc/host/sdhci.c | 4 +- drivers/net/arm/ep93xx_eth.c | 4 +- drivers/net/bnx2x_main.c | 4 +- drivers/net/cxgb3/sge.c | 2 +- drivers/net/e100.c | 2 +- drivers/net/e1000e/ethtool.c | 4 +- drivers/net/e1000e/netdev.c | 11 +-- drivers/net/ibmveth.c | 38 +++++----- drivers/net/iseries_veth.c | 4 +- drivers/net/mlx4/eq.c | 2 +- drivers/net/pasemi_mac.c | 6 +- drivers/net/qla3xxx.c | 12 ++-- drivers/net/s2io.c | 48 +++++++------ drivers/net/sfc/rx.c | 4 +- drivers/net/sfc/tx.c | 7 +- drivers/net/spider_net.c | 4 +- drivers/net/tc35815.c | 4 +- drivers/net/wireless/ath5k/base.c | 4 +- drivers/scsi/ibmvscsi/ibmvfc.c | 4 +- drivers/scsi/ibmvscsi/ibmvscsi.c | 4 +- drivers/scsi/ibmvscsi/ibmvstgt.c | 2 +- drivers/scsi/ibmvscsi/rpa_vscsi.c | 2 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/au1550_spi.c | 6 +- drivers/spi/omap2_mcspi.c | 4 +- drivers/spi/pxa2xx_spi.c | 4 +- drivers/spi/spi_imx.c | 6 +- include/asm-alpha/dma-mapping.h | 6 +- include/asm-alpha/pci.h | 2 +- include/asm-arm/dma-mapping.h | 2 +- include/asm-avr32/dma-mapping.h | 2 +- include/asm-cris/dma-mapping.h | 2 +- include/asm-frv/dma-mapping.h | 2 +- include/asm-generic/dma-mapping-broken.h | 2 +- include/asm-generic/dma-mapping.h | 4 +- include/asm-generic/pci-dma-compat.h | 4 +- include/asm-ia64/machvec.h | 2 +- include/asm-m68k/dma-mapping.h | 2 +- include/asm-mips/dma-mapping.h | 2 +- include/asm-mn10300/dma-mapping.h | 2 +- include/asm-parisc/dma-mapping.h | 2 +- include/asm-powerpc/dma-mapping.h | 2 +- include/asm-sh/dma-mapping.h | 2 +- include/asm-sparc/dma-mapping_64.h | 2 +- include/asm-sparc/pci_32.h | 3 +- include/asm-sparc/pci_64.h | 5 +- include/asm-x86/device.h | 3 + include/asm-x86/dma-mapping.h | 99 ++++++++++++++++++-------- include/asm-x86/swiotlb.h | 2 +- include/asm-xtensa/dma-mapping.h | 2 +- include/linux/i2o.h | 2 +- include/linux/ssb/ssb.h | 4 +- include/rdma/ib_verbs.h | 2 +- lib/swiotlb.c | 4 +- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 3 +- 76 files changed, 256 insertions(+), 210 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 80d150458c80..d8b63d164e41 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -298,10 +298,10 @@ recommended that you never use these unless you really know what the cache width is. int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) int -pci_dma_mapping_error(dma_addr_t dma_addr) +pci_dma_mapping_error(struct pci_dev *hwdev, dma_addr_t dma_addr) In some circumstances dma_map_single and dma_map_page will fail to create a mapping. A driver can check for these errors by testing the returned diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index dd2947342604..69130f365904 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -280,7 +280,7 @@ unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, /* * Trying to unmap an invalid mapping */ - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(dev, dma_addr)) { dev_err(dev, "Trying to unmap invalid mapping\n"); return; } diff --git a/arch/ia64/hp/common/hwsw_iommu.c b/arch/ia64/hp/common/hwsw_iommu.c index 1c44ec2a1d58..88b6e6f3fd88 100644 --- a/arch/ia64/hp/common/hwsw_iommu.c +++ b/arch/ia64/hp/common/hwsw_iommu.c @@ -186,9 +186,10 @@ hwsw_dma_supported (struct device *dev, u64 mask) } int -hwsw_dma_mapping_error (dma_addr_t dma_addr) +hwsw_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - return hwiommu_dma_mapping_error (dma_addr) || swiotlb_dma_mapping_error(dma_addr); + return hwiommu_dma_mapping_error(dev, dma_addr) || + swiotlb_dma_mapping_error(dev, dma_addr); } EXPORT_SYMBOL(hwsw_dma_mapping_error); diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index 34421aed1e2a..4956be40d7b5 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -2147,7 +2147,7 @@ sba_dma_supported (struct device *dev, u64 mask) } int -sba_dma_mapping_error (dma_addr_t dma_addr) +sba_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c index 52175af299a0..53ebb6484495 100644 --- a/arch/ia64/sn/pci/pci_dma.c +++ b/arch/ia64/sn/pci/pci_dma.c @@ -350,7 +350,7 @@ void sn_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, } EXPORT_SYMBOL(sn_dma_sync_sg_for_device); -int sn_dma_mapping_error(dma_addr_t dma_addr) +int sn_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index ae39dd88b9aa..891312f8e5a6 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -348,7 +348,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nele EXPORT_SYMBOL(dma_sync_sg_for_device); -int dma_mapping_error(dma_addr_t dma_addr) +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index 0e04f8fb152a..3e7e0f1568ef 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -281,7 +281,7 @@ static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data) dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dummy_page_da)) { + if (dma_mapping_error(bus->phb->parent, dummy_page_da)) { pr_err("PCIEX:Map dummy page failed.\n"); kfree(dummy_page_va); return -1; diff --git a/arch/powerpc/platforms/cell/spider-pci.c b/arch/powerpc/platforms/cell/spider-pci.c index 418b605ac35a..5122ec145271 100644 --- a/arch/powerpc/platforms/cell/spider-pci.c +++ b/arch/powerpc/platforms/cell/spider-pci.c @@ -111,7 +111,7 @@ static int __init spiderpci_pci_setup_chip(struct pci_controller *phb, dummy_page_da = dma_map_single(phb->parent, dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dummy_page_da)) { + if (dma_mapping_error(phb->parent, dummy_page_da)) { pr_err("SPIDER-IOWA:Map dummy page filed.\n"); kfree(dummy_page_va); return -1; diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c index 1dc7295746da..731d7b157749 100644 --- a/arch/powerpc/platforms/iseries/mf.c +++ b/arch/powerpc/platforms/iseries/mf.c @@ -871,7 +871,7 @@ static int proc_mf_dump_cmdline(char *page, char **start, off_t off, count = 256 - off; dma_addr = iseries_hv_map(page, off + count, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_addr)) + if (dma_mapping_error(NULL, dma_addr)) return -ENOMEM; memset(page, 0, off + count); memset(&vsp_cmd, 0, sizeof(vsp_cmd)); diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index 19e7fc7c2c4f..1eb86be93d7a 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -544,7 +544,7 @@ error: return ret; } -static const struct dma_mapping_ops calgary_dma_ops = { +static struct dma_mapping_ops calgary_dma_ops = { .alloc_coherent = calgary_alloc_coherent, .map_single = calgary_map_single, .unmap_single = calgary_unmap_single, diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index cbecb05551bb..37544123896d 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -11,7 +11,7 @@ static int forbid_dac __read_mostly; -const struct dma_mapping_ops *dma_ops; +struct dma_mapping_ops *dma_ops; EXPORT_SYMBOL(dma_ops); static int iommu_sac_force __read_mostly; @@ -312,6 +312,8 @@ static int dma_release_coherent(struct device *dev, int order, void *vaddr) int dma_supported(struct device *dev, u64 mask) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + #ifdef CONFIG_PCI if (mask > 0xffffffff && forbid_dac > 0) { dev_info(dev, "PCI: Disallowing DAC for device\n"); @@ -319,8 +321,8 @@ int dma_supported(struct device *dev, u64 mask) } #endif - if (dma_ops->dma_supported) - return dma_ops->dma_supported(dev, mask); + if (ops->dma_supported) + return ops->dma_supported(dev, mask); /* Copied from i386. Doesn't make much sense, because it will only work for pci_alloc_coherent. @@ -367,6 +369,7 @@ void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp) { + struct dma_mapping_ops *ops = get_dma_ops(dev); void *memory = NULL; struct page *page; unsigned long dma_mask = 0; @@ -435,8 +438,8 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, /* Let low level make its own zone decisions */ gfp &= ~(GFP_DMA32|GFP_DMA); - if (dma_ops->alloc_coherent) - return dma_ops->alloc_coherent(dev, size, + if (ops->alloc_coherent) + return ops->alloc_coherent(dev, size, dma_handle, gfp); return NULL; } @@ -448,14 +451,14 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, } } - if (dma_ops->alloc_coherent) { + if (ops->alloc_coherent) { free_pages((unsigned long)memory, get_order(size)); gfp &= ~(GFP_DMA|GFP_DMA32); - return dma_ops->alloc_coherent(dev, size, dma_handle, gfp); + return ops->alloc_coherent(dev, size, dma_handle, gfp); } - if (dma_ops->map_simple) { - *dma_handle = dma_ops->map_simple(dev, virt_to_phys(memory), + if (ops->map_simple) { + *dma_handle = ops->map_simple(dev, virt_to_phys(memory), size, PCI_DMA_BIDIRECTIONAL); if (*dma_handle != bad_dma_address) @@ -477,12 +480,14 @@ EXPORT_SYMBOL(dma_alloc_coherent); void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t bus) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + int order = get_order(size); WARN_ON(irqs_disabled()); /* for portability */ if (dma_release_coherent(dev, order, vaddr)) return; - if (dma_ops->unmap_single) - dma_ops->unmap_single(dev, bus, size, 0); + if (ops->unmap_single) + ops->unmap_single(dev, bus, size, 0); free_pages((unsigned long)vaddr, order); } EXPORT_SYMBOL(dma_free_coherent); diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c index df5f142657d2..744126e64950 100644 --- a/arch/x86/kernel/pci-gart_64.c +++ b/arch/x86/kernel/pci-gart_64.c @@ -692,8 +692,7 @@ static __init int init_k8_gatt(struct agp_kern_info *info) extern int agp_amd64_init(void); -static const struct dma_mapping_ops gart_dma_ops = { - .mapping_error = NULL, +static struct dma_mapping_ops gart_dma_ops = { .map_single = gart_map_single, .map_simple = gart_map_simple, .unmap_single = gart_unmap_single, diff --git a/arch/x86/kernel/pci-nommu.c b/arch/x86/kernel/pci-nommu.c index 792b9179eff3..3f91f71cdc3e 100644 --- a/arch/x86/kernel/pci-nommu.c +++ b/arch/x86/kernel/pci-nommu.c @@ -72,21 +72,9 @@ static int nommu_map_sg(struct device *hwdev, struct scatterlist *sg, return nents; } -/* Make sure we keep the same behaviour */ -static int nommu_mapping_error(dma_addr_t dma_addr) -{ -#ifdef CONFIG_X86_32 - return 0; -#else - return (dma_addr == bad_dma_address); -#endif -} - - -const struct dma_mapping_ops nommu_dma_ops = { +struct dma_mapping_ops nommu_dma_ops = { .map_single = nommu_map_single, .map_sg = nommu_map_sg, - .mapping_error = nommu_mapping_error, .is_phys = 1, }; diff --git a/arch/x86/kernel/pci-swiotlb_64.c b/arch/x86/kernel/pci-swiotlb_64.c index 20df839b9c20..c4ce0332759e 100644 --- a/arch/x86/kernel/pci-swiotlb_64.c +++ b/arch/x86/kernel/pci-swiotlb_64.c @@ -18,7 +18,7 @@ swiotlb_map_single_phys(struct device *hwdev, phys_addr_t paddr, size_t size, return swiotlb_map_single(hwdev, phys_to_virt(paddr), size, direction); } -const struct dma_mapping_ops swiotlb_dma_ops = { +struct dma_mapping_ops swiotlb_dma_ops = { .mapping_error = swiotlb_dma_mapping_error, .alloc_coherent = swiotlb_alloc_coherent, .free_coherent = swiotlb_free_coherent, diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c index bcbe794a3ea5..e14c03dc0065 100644 --- a/drivers/firewire/fw-iso.c +++ b/drivers/firewire/fw-iso.c @@ -50,7 +50,7 @@ fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, address = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE, direction); - if (dma_mapping_error(address)) { + if (dma_mapping_error(card->device, address)) { __free_page(buffer->pages[i]); goto out_pages; } diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 333b12544dd1..566672e0bcff 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -953,7 +953,7 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet) payload_bus = dma_map_single(ohci->card.device, packet->payload, packet->payload_length, DMA_TO_DEVICE); - if (dma_mapping_error(payload_bus)) { + if (dma_mapping_error(ohci->card.device, payload_bus)) { packet->ack = RCODE_SEND_ERROR; return -1; } diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 53fc5a641e6d..aaff50ebba1d 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -543,7 +543,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, orb->response_bus = dma_map_single(device->card->device, &orb->response, sizeof(orb->response), DMA_FROM_DEVICE); - if (dma_mapping_error(orb->response_bus)) + if (dma_mapping_error(device->card->device, orb->response_bus)) goto fail_mapping_response; orb->request.response.high = 0; @@ -577,7 +577,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) goto fail_mapping_request; sbp2_send_orb(&orb->base, lu, node_id, generation, @@ -1424,7 +1424,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, orb->page_table_bus = dma_map_single(device->card->device, orb->page_table, sizeof(orb->page_table), DMA_TO_DEVICE); - if (dma_mapping_error(orb->page_table_bus)) + if (dma_mapping_error(device->card->device, orb->page_table_bus)) goto fail_page_table; /* @@ -1509,7 +1509,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) goto out; sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, lu->generation, diff --git a/drivers/infiniband/hw/ipath/ipath_sdma.c b/drivers/infiniband/hw/ipath/ipath_sdma.c index eaba03273e4f..284c9bca517e 100644 --- a/drivers/infiniband/hw/ipath/ipath_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_sdma.c @@ -698,7 +698,7 @@ retry: addr = dma_map_single(&dd->pcidev->dev, tx->txreq.map_addr, tx->map_len, DMA_TO_DEVICE); - if (dma_mapping_error(addr)) { + if (dma_mapping_error(&dd->pcidev->dev, addr)) { ret = -EIO; goto unlock; } diff --git a/drivers/infiniband/hw/ipath/ipath_user_sdma.c b/drivers/infiniband/hw/ipath/ipath_user_sdma.c index 86e016916cd1..82d9a0b5ca2f 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_user_sdma.c @@ -206,7 +206,7 @@ static int ipath_user_sdma_coalesce(const struct ipath_devdata *dd, dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto free_unmap; } @@ -301,7 +301,7 @@ static int ipath_user_sdma_pin_pages(const struct ipath_devdata *dd, pages[j], 0, flen, DMA_TO_DEVICE); unsigned long fofs = addr & ~PAGE_MASK; - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto done; } @@ -508,7 +508,7 @@ static int ipath_user_sdma_queue_pkts(const struct ipath_devdata *dd, if (page) { dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto free_pbc; } diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index 4e36aa7cb3d2..cc6858f0b65b 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -780,7 +780,7 @@ int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt) return -ENOMEM; dev->eq_table.icm_dma = pci_map_page(dev->pdev, dev->eq_table.icm_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->eq_table.icm_dma)) { + if (pci_dma_mapping_error(dev->pdev, dev->eq_table.icm_dma)) { __free_page(dev->eq_table.icm_page); return -ENOMEM; } diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index 1360403b88b6..a9653c63f4db 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -242,7 +242,7 @@ static int __devinit pluto_dma_map(struct pluto *pluto) pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, TS_DMA_BYTES, PCI_DMA_FROMDEVICE); - return pci_dma_mapping_error(pluto->dma_addr); + return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); } static void pluto_dma_unmap(struct pluto *pluto) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c3a5db72ddd7..5f95e10229b5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -337,7 +337,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->align_addr = dma_map_single(mmc_dev(host->mmc), host->align_buffer, 128 * 4, direction); - if (dma_mapping_error(host->align_addr)) + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto fail; BUG_ON(host->align_addr & 0x3); @@ -439,7 +439,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->adma_addr = dma_map_single(mmc_dev(host->mmc), host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); - if (dma_mapping_error(host->align_addr)) + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto unmap_entries; BUG_ON(host->adma_addr & 0x3); diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index 7a14980f3472..18d3eeb7eab2 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -482,7 +482,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) goto err; d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(d)) { + if (dma_mapping_error(NULL, d)) { free_page((unsigned long)page); goto err; } @@ -505,7 +505,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) goto err; d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE); - if (dma_mapping_error(d)) { + if (dma_mapping_error(NULL, d)) { free_page((unsigned long)page); goto err; } diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 0263bef9cc6d..c7cc760a1777 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -1020,7 +1020,7 @@ static inline int bnx2x_alloc_rx_sge(struct bnx2x *bp, mapping = pci_map_page(bp->pdev, page, 0, BCM_PAGE_SIZE*PAGES_PER_SGE, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(mapping))) { + if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) { __free_pages(page, PAGES_PER_SGE_SHIFT); return -ENOMEM; } @@ -1048,7 +1048,7 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(mapping))) { + if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) { dev_kfree_skb(skb); return -ENOMEM; } diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index a96331c875e6..1b0861d73ab7 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -386,7 +386,7 @@ static inline int add_one_rx_buf(void *va, unsigned int len, dma_addr_t mapping; mapping = pci_map_single(pdev, va, len, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(mapping))) + if (unlikely(pci_dma_mapping_error(pdev, mapping))) return -ENOMEM; pci_unmap_addr_set(sd, dma_addr, mapping); diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 1037b1332312..19d32a227be1 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1790,7 +1790,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(rx->dma_addr)) { + if (pci_dma_mapping_error(nic->pdev, rx->dma_addr)) { dev_kfree_skb_any(rx->skb); rx->skb = NULL; rx->dma_addr = 0; diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index a14561f40db0..9350564065e7 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -1090,7 +1090,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) tx_ring->buffer_info[i].dma = pci_map_single(pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(tx_ring->buffer_info[i].dma)) { + if (pci_dma_mapping_error(pdev, tx_ring->buffer_info[i].dma)) { ret_val = 4; goto err_nomem; } @@ -1153,7 +1153,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) rx_ring->buffer_info[i].dma = pci_map_single(pdev, skb->data, 2048, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rx_ring->buffer_info[i].dma)) { + if (pci_dma_mapping_error(pdev, rx_ring->buffer_info[i].dma)) { ret_val = 8; goto err_nomem; } diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 9c0f56b3c518..d13677899767 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -195,7 +195,7 @@ map_skb: buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_buffer_len, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(pdev, buffer_info->dma)) { dev_err(&pdev->dev, "RX DMA map failed\n"); adapter->rx_dma_failed++; break; @@ -265,7 +265,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, ps_page->page, 0, PAGE_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(ps_page->dma)) { + if (pci_dma_mapping_error(pdev, ps_page->dma)) { dev_err(&adapter->pdev->dev, "RX DMA page map failed\n"); adapter->rx_dma_failed++; @@ -300,7 +300,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_ps_bsize0, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(pdev, buffer_info->dma)) { dev_err(&pdev->dev, "RX DMA map failed\n"); adapter->rx_dma_failed++; /* cleanup skb */ @@ -3344,7 +3344,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter, skb->data + offset, size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(adapter->pdev, buffer_info->dma)) { dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); adapter->tx_dma_failed++; return -1; @@ -3382,7 +3382,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter, offset, size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(adapter->pdev, + buffer_info->dma)) { dev_err(&adapter->pdev->dev, "TX DMA page map failed\n"); adapter->tx_dma_failed++; diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index e5a6e2e84540..91ec9fdc7184 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -260,7 +260,7 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc dma_addr = dma_map_single(&adapter->vdev->dev, skb->data, pool->buff_size, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_addr)) + if (dma_mapping_error((&adapter->vdev->dev, dma_addr)) goto failure; pool->free_map[free_index] = IBM_VETH_INVALID_MAP; @@ -294,7 +294,7 @@ failure: pool->consumer_index = pool->size - 1; else pool->consumer_index--; - if (!dma_mapping_error(dma_addr)) + if (!dma_mapping_error((&adapter->vdev->dev, dma_addr)) dma_unmap_single(&adapter->vdev->dev, pool->dma_addr[index], pool->buff_size, DMA_FROM_DEVICE); @@ -448,11 +448,11 @@ static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) static void ibmveth_cleanup(struct ibmveth_adapter *adapter) { int i; + struct device *dev = &adapter->vdev->dev; if(adapter->buffer_list_addr != NULL) { - if(!dma_mapping_error(adapter->buffer_list_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->buffer_list_dma, 4096, + if (!dma_mapping_error(dev, adapter->buffer_list_dma)) { + dma_unmap_single(dev, adapter->buffer_list_dma, 4096, DMA_BIDIRECTIONAL); adapter->buffer_list_dma = DMA_ERROR_CODE; } @@ -461,9 +461,8 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) } if(adapter->filter_list_addr != NULL) { - if(!dma_mapping_error(adapter->filter_list_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->filter_list_dma, 4096, + if (!dma_mapping_error(dev, adapter->filter_list_dma)) { + dma_unmap_single(dev, adapter->filter_list_dma, 4096, DMA_BIDIRECTIONAL); adapter->filter_list_dma = DMA_ERROR_CODE; } @@ -472,8 +471,8 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) } if(adapter->rx_queue.queue_addr != NULL) { - if(!dma_mapping_error(adapter->rx_queue.queue_dma)) { - dma_unmap_single(&adapter->vdev->dev, + if (!dma_mapping_error(dev, adapter->rx_queue.queue_dma)) { + dma_unmap_single(dev, adapter->rx_queue.queue_dma, adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL); @@ -535,6 +534,7 @@ static int ibmveth_open(struct net_device *netdev) int rc; union ibmveth_buf_desc rxq_desc; int i; + struct device *dev; ibmveth_debug_printk("open starting\n"); @@ -563,17 +563,19 @@ static int ibmveth_open(struct net_device *netdev) return -ENOMEM; } - adapter->buffer_list_dma = dma_map_single(&adapter->vdev->dev, + dev = &adapter->vdev->dev; + + adapter->buffer_list_dma = dma_map_single(dev, adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL); - adapter->filter_list_dma = dma_map_single(&adapter->vdev->dev, + adapter->filter_list_dma = dma_map_single(dev, adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL); - adapter->rx_queue.queue_dma = dma_map_single(&adapter->vdev->dev, + adapter->rx_queue.queue_dma = dma_map_single(dev, adapter->rx_queue.queue_addr, adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL); - if((dma_mapping_error(adapter->buffer_list_dma) ) || - (dma_mapping_error(adapter->filter_list_dma)) || - (dma_mapping_error(adapter->rx_queue.queue_dma))) { + if ((dma_mapping_error(dev, adapter->buffer_list_dma)) || + (dma_mapping_error(dev, adapter->filter_list_dma)) || + (dma_mapping_error(dev, adapter->rx_queue.queue_dma))) { ibmveth_error_printk("unable to map filter or buffer list pages\n"); ibmveth_cleanup(adapter); napi_disable(&adapter->napi); @@ -645,7 +647,7 @@ static int ibmveth_open(struct net_device *netdev) adapter->bounce_buffer_dma = dma_map_single(&adapter->vdev->dev, adapter->bounce_buffer, netdev->mtu + IBMVETH_BUFF_OH, DMA_BIDIRECTIONAL); - if (dma_mapping_error(adapter->bounce_buffer_dma)) { + if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) { ibmveth_error_printk("unable to map bounce buffer\n"); ibmveth_cleanup(adapter); napi_disable(&adapter->napi); @@ -922,7 +924,7 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) buf[1] = 0; } - if (dma_mapping_error(data_dma_addr)) { + if (dma_mapping_error((&adapter->vdev->dev, data_dma_addr)) { if (!firmware_has_feature(FW_FEATURE_CMO)) ibmveth_error_printk("tx: unable to map xmit buffer\n"); skb_copy_from_linear_data(skb, adapter->bounce_buffer, diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index b8d0639c1cdf..c46864d626b2 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -1128,7 +1128,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, msg->data.addr[0] = dma_map_single(port->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(msg->data.addr[0])) + if (dma_mapping_error(port->dev, msg->data.addr[0])) goto recycle_and_drop; msg->dev = port->dev; @@ -1226,7 +1226,7 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, dma_address = msg->data.addr[0]; dma_length = msg->data.len[0]; - if (!dma_mapping_error(dma_address)) + if (!dma_mapping_error(msg->dev, dma_address)) dma_unmap_single(msg->dev, dma_address, dma_length, DMA_TO_DEVICE); diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index ea3a09aaa844..7df928d3a3d8 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -526,7 +526,7 @@ int mlx4_map_eq_icm(struct mlx4_dev *dev, u64 icm_virt) return -ENOMEM; priv->eq_table.icm_dma = pci_map_page(dev->pdev, priv->eq_table.icm_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(priv->eq_table.icm_dma)) { + if (pci_dma_mapping_error(dev->pdev, priv->eq_table.icm_dma)) { __free_page(priv->eq_table.icm_page); return -ENOMEM; } diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index 993d87c9296f..edc0fd588985 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -650,7 +650,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, mac->bufsz - LOCAL_SKB_ALIGN, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(dma))) { + if (unlikely(pci_dma_mapping_error(mac->dma_pdev, dma))) { dev_kfree_skb_irq(info->skb); break; } @@ -1519,7 +1519,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) map[0] = pci_map_single(mac->dma_pdev, skb->data, skb_headlen(skb), PCI_DMA_TODEVICE); map_size[0] = skb_headlen(skb); - if (dma_mapping_error(map[0])) + if (pci_dma_mapping_error(mac->dma_pdev, map[0])) goto out_err_nolock; for (i = 0; i < nfrags; i++) { @@ -1529,7 +1529,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) frag->page_offset, frag->size, PCI_DMA_TODEVICE); map_size[i+1] = frag->size; - if (dma_mapping_error(map[i+1])) { + if (pci_dma_mapping_error(mac->dma_pdev, map[i+1])) { nfrags = i; goto out_err_nolock; } diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index e7d48a352beb..e82b37bbd6c3 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -328,7 +328,7 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, qdev->lrg_buffer_len - QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -1919,7 +1919,7 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev) QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -2454,7 +2454,7 @@ static int ql_send_map(struct ql3_adapter *qdev, */ map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -2487,7 +2487,7 @@ static int ql_send_map(struct ql3_adapter *qdev, sizeof(struct oal), PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping outbound address list with error: %d\n", @@ -2514,7 +2514,7 @@ static int ql_send_map(struct ql3_adapter *qdev, frag->page_offset, frag->size, PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping frags failed with error: %d\n", qdev->ndev->name, err); @@ -2916,7 +2916,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 9dae40ccf048..86d77d05190a 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2512,8 +2512,8 @@ static void stop_nic(struct s2io_nic *nic) * Return Value: * SUCCESS on success or an appropriate -ve value on failure. */ - -static int fill_rx_buffers(struct ring_info *ring, int from_card_up) +static int fill_rx_buffers(struct s2io_nic *nic, struct ring_info *ring, + int from_card_up) { struct sk_buff *skb; struct RxD_t *rxdp; @@ -2602,7 +2602,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) rxdp1->Buffer0_ptr = pci_map_single (ring->pdev, skb->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if(pci_dma_mapping_error(rxdp1->Buffer0_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp1->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = @@ -2636,7 +2637,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) rxdp3->Buffer0_ptr = pci_map_single(ring->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer0_ptr)) goto pci_map_failed; } else pci_dma_sync_single_for_device(ring->pdev, @@ -2655,7 +2657,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) (ring->pdev, skb->data, ring->mtu + 4, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer2_ptr)) goto pci_map_failed; if (from_card_up) { @@ -2664,8 +2667,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error - (rxdp3->Buffer1_ptr)) { + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer1_ptr)) { pci_unmap_single (ring->pdev, (dma_addr_t)(unsigned long) @@ -2806,9 +2809,9 @@ static void free_rx_buffers(struct s2io_nic *sp) } } -static int s2io_chk_rx_buffers(struct ring_info *ring) +static int s2io_chk_rx_buffers(struct s2io_nic *nic, struct ring_info *ring) { - if (fill_rx_buffers(ring, 0) == -ENOMEM) { + if (fill_rx_buffers(nic, ring, 0) == -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", ring->dev->name); DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } @@ -2848,7 +2851,7 @@ static int s2io_poll_msix(struct napi_struct *napi, int budget) return 0; pkts_processed = rx_intr_handler(ring, budget); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(nic, ring); if (pkts_processed < budget_org) { netif_rx_complete(dev, napi); @@ -2882,7 +2885,7 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget) for (i = 0; i < config->rx_ring_num; i++) { ring = &mac_control->rings[i]; ring_pkts_processed = rx_intr_handler(ring, budget); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(nic, ring); pkts_processed += ring_pkts_processed; budget -= ring_pkts_processed; if (budget <= 0) @@ -2939,7 +2942,8 @@ static void s2io_netpoll(struct net_device *dev) rx_intr_handler(&mac_control->rings[i], 0); for (i = 0; i < config->rx_ring_num; i++) { - if (fill_rx_buffers(&mac_control->rings[i], 0) == -ENOMEM) { + if (fill_rx_buffers(nic, &mac_control->rings[i], 0) == + -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n"); break; @@ -4235,14 +4239,14 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Buffer_Pointer = pci_map_single(sp->pdev, fifo->ufo_in_band_v, sizeof(u64), PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(txdp->Buffer_Pointer)) + if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) goto pci_map_failed; txdp++; } txdp->Buffer_Pointer = pci_map_single (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(txdp->Buffer_Pointer)) + if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) goto pci_map_failed; txdp->Host_Control = (unsigned long) skb; @@ -4345,7 +4349,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id) netif_rx_schedule(dev, &ring->napi); } else { rx_intr_handler(ring, 0); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(sp, ring); } return IRQ_HANDLED; @@ -4826,7 +4830,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id) */ if (!config->napi) { for (i = 0; i < config->rx_ring_num; i++) - s2io_chk_rx_buffers(&mac_control->rings[i]); + s2io_chk_rx_buffers(sp, &mac_control->rings[i]); } writeq(sp->general_int_mask, &bar0->general_int_mask); readl(&bar0->general_int_status); @@ -6859,7 +6863,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single( sp->pdev, (*skb)->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp1->Buffer0_ptr)) + if (pci_dma_mapping_error(sp->pdev, rxdp1->Buffer0_ptr)) goto memalloc_failed; rxdp->Host_Control = (unsigned long) (*skb); } @@ -6886,12 +6890,13 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single(sp->pdev, (*skb)->data, dev->mtu + 4, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) + if (pci_dma_mapping_error(sp->pdev, rxdp3->Buffer2_ptr)) goto memalloc_failed; rxdp3->Buffer0_ptr = *temp0 = pci_map_single( sp->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) { + if (pci_dma_mapping_error(sp->pdev, + rxdp3->Buffer0_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer2_ptr, dev->mtu + 4, PCI_DMA_FROMDEVICE); @@ -6903,7 +6908,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, rxdp3->Buffer1_ptr = *temp1 = pci_map_single(sp->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { + if (pci_dma_mapping_error(sp->pdev, + rxdp3->Buffer1_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); @@ -7187,7 +7193,7 @@ static int s2io_card_up(struct s2io_nic * sp) for (i = 0; i < config->rx_ring_num; i++) { mac_control->rings[i].mtu = dev->mtu; - ret = fill_rx_buffers(&mac_control->rings[i], 1); + ret = fill_rx_buffers(sp, &mac_control->rings[i], 1); if (ret) { DBG_PRINT(ERR_DBG, "%s: Out of memory in Open\n", dev->name); diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index 601b001437c0..0d27dd39bc09 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -233,7 +233,7 @@ static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue, rx_buf->data, rx_buf->len, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, rx_buf->dma_addr))) { dev_kfree_skb_any(rx_buf->skb); rx_buf->skb = NULL; return -EIO; @@ -275,7 +275,7 @@ static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue, 0, efx_rx_buf_size(efx), PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, dma_addr))) { __free_pages(rx_buf->page, efx->rx_buffer_order); rx_buf->page = NULL; return -EIO; diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index 5cdd082ab8f6..5e8374ab28ee 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -172,7 +172,7 @@ static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue, /* Process all fragments */ while (1) { - if (unlikely(pci_dma_mapping_error(dma_addr))) + if (unlikely(pci_dma_mapping_error(pci_dev, dma_addr))) goto pci_err; /* Store fields for marking in the per-fragment final @@ -661,7 +661,8 @@ efx_tsoh_heap_alloc(struct efx_tx_queue *tx_queue, size_t header_len) tsoh->dma_addr = pci_map_single(tx_queue->efx->pci_dev, TSOH_BUFFER(tsoh), header_len, PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(tsoh->dma_addr))) { + if (unlikely(pci_dma_mapping_error(tx_queue->efx->pci_dev, + tsoh->dma_addr))) { kfree(tsoh); return NULL; } @@ -863,7 +864,7 @@ static inline int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, st->ifc.unmap_addr = pci_map_page(efx->pci_dev, page, page_off, len, PCI_DMA_TODEVICE); - if (likely(!pci_dma_mapping_error(st->ifc.unmap_addr))) { + if (likely(!pci_dma_mapping_error(efx->pci_dev, st->ifc.unmap_addr))) { st->ifc.unmap_len = len; st->ifc.len = len; st->ifc.dma_addr = st->ifc.unmap_addr; diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 00aa0b108cb9..b6435d0d71f9 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -452,7 +452,7 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, /* iommu-map the skb */ buf = pci_map_single(card->pdev, descr->skb->data, SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buf)) { + if (pci_dma_mapping_error(card->pdev, buf)) { dev_kfree_skb_any(descr->skb); descr->skb = NULL; if (netif_msg_rx_err(card) && net_ratelimit()) @@ -691,7 +691,7 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, unsigned long flags; buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buf)) { + if (pci_dma_mapping_error(card->pdev, buf)) { if (netif_msg_tx_err(card) && net_ratelimit()) dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). " "Dropping packet\n", skb->data, skb->len); diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index a645e5028c14..8487ace9d2e3 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -506,7 +506,7 @@ static void *alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle) return NULL; *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(*dma_handle)) { + if (pci_dma_mapping_error(hwdev, *dma_handle)) { free_page((unsigned long)buf); return NULL; } @@ -536,7 +536,7 @@ static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, return NULL; *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(*dma_handle)) { + if (pci_dma_mapping_error(hwdev, *dma_handle)) { dev_kfree_skb_any(skb); return NULL; } diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 217d506527a9..d9769c527346 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1166,7 +1166,7 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) bf->skb = skb; bf->skbaddr = pci_map_single(sc->pdev, skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(bf->skbaddr))) { + if (unlikely(pci_dma_mapping_error(sc->pdev, bf->skbaddr))) { ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); dev_kfree_skb(skb); bf->skb = NULL; @@ -1918,7 +1918,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " "skbaddr %llx\n", skb, skb->data, skb->len, (unsigned long long)bf->skbaddr); - if (pci_dma_mapping_error(bf->skbaddr)) { + if (pci_dma_mapping_error(sc->pdev, bf->skbaddr)) { ATH5K_ERR(sc, "beacon DMA mapping failed\n"); return -EIO; } diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index c4a7c06793c5..61f8fdea2d96 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3525,7 +3525,7 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE, DMA_BIDIRECTIONAL); - if (dma_mapping_error(crq->msg_token)) + if (dma_mapping_error(dev, crq->msg_token)) goto map_failed; retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, @@ -3618,7 +3618,7 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(async_q->msg_token)) { + if (dma_mapping_error(dev, async_q->msg_token)) { dev_err(dev, "Failed to map async queue\n"); goto free_async_crq; } diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 20000ec79b04..6b24b9cdb04c 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -859,7 +859,7 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) sizeof(hostdata->madapter_info), DMA_BIDIRECTIONAL); - if (dma_mapping_error(req->buffer)) { + if (dma_mapping_error(hostdata->dev, req->buffer)) { if (!firmware_has_feature(FW_FEATURE_CMO)) dev_err(hostdata->dev, "Unable to map request_buffer for " @@ -1407,7 +1407,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, length, DMA_BIDIRECTIONAL); - if (dma_mapping_error(host_config->buffer)) { + if (dma_mapping_error(hostdata->dev, host_config->buffer)) { if (!firmware_has_feature(FW_FEATURE_CMO)) dev_err(hostdata->dev, "dma_mapping error getting host config\n"); diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index 3b9514c8f1f1..2e13ec00172a 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -564,7 +564,7 @@ static int crq_queue_create(struct crq_queue *queue, struct srp_target *target) queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(queue->msg_token)) + if (dma_mapping_error(target->dev, queue->msg_token)) goto map_failed; err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 182146100dc1..462a8574dad9 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -253,7 +253,7 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(queue->msg_token)) + if (dma_mapping_error(hostdata->dev, queue->msg_token)) goto map_failed; gather_partition_info(); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index e81d59d78910..0c7165660853 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -313,14 +313,14 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) xfer->tx_dma = dma_map_single(dev, (void *) xfer->tx_buf, xfer->len, DMA_TO_DEVICE); - if (dma_mapping_error(xfer->tx_dma)) + if (dma_mapping_error(dev, xfer->tx_dma)) return -ENOMEM; } if (xfer->rx_buf) { xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); - if (dma_mapping_error(xfer->rx_dma)) { + if (dma_mapping_error(dev, xfer->rx_dma)) { if (xfer->tx_buf) dma_unmap_single(dev, xfer->tx_dma, xfer->len, diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 9149689c79d9..87b73e0169c5 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -334,7 +334,7 @@ static int au1550_spi_dma_rxtmp_alloc(struct au1550_spi *hw, unsigned size) hw->dma_rx_tmpbuf_size = size; hw->dma_rx_tmpbuf_addr = dma_map_single(hw->dev, hw->dma_rx_tmpbuf, size, DMA_FROM_DEVICE); - if (dma_mapping_error(hw->dma_rx_tmpbuf_addr)) { + if (dma_mapping_error(hw->dev, hw->dma_rx_tmpbuf_addr)) { kfree(hw->dma_rx_tmpbuf); hw->dma_rx_tmpbuf = 0; hw->dma_rx_tmpbuf_size = 0; @@ -378,7 +378,7 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) dma_rx_addr = dma_map_single(hw->dev, (void *)t->rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_rx_addr)) + if (dma_mapping_error(hw->dev, dma_rx_addr)) dev_err(hw->dev, "rx dma map error\n"); } } else { @@ -401,7 +401,7 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) dma_tx_addr = dma_map_single(hw->dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_tx_addr)) + if (dma_mapping_error(hw->dev, dma_tx_addr)) dev_err(hw->dev, "tx dma map error\n"); } } else { diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index b1cc148036c1..f6f987bb71ca 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -836,7 +836,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (tx_buf != NULL) { t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); - if (dma_mapping_error(t->tx_dma)) { + if (dma_mapping_error(&spi->dev, t->tx_dma)) { dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'T', len); return -EINVAL; @@ -845,7 +845,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (rx_buf != NULL) { t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(t->rx_dma)) { + if (dma_mapping_error(&spi->dev, t->rx_dma)) { dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'R', len); if (tx_buf != NULL) diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 0c452c46ab07..067299d6d192 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -353,7 +353,7 @@ static int map_dma_buffers(struct driver_data *drv_data) drv_data->rx_dma = dma_map_single(dev, drv_data->rx, drv_data->rx_map_len, DMA_FROM_DEVICE); - if (dma_mapping_error(drv_data->rx_dma)) + if (dma_mapping_error(dev, drv_data->rx_dma)) return 0; /* Stream map the tx buffer */ @@ -361,7 +361,7 @@ static int map_dma_buffers(struct driver_data *drv_data) drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) { + if (dma_mapping_error(dev, drv_data->tx_dma)) { dma_unmap_single(dev, drv_data->rx_dma, drv_data->rx_map_len, DMA_FROM_DEVICE); return 0; diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 54ac7bea5f8c..6fb77fcc4971 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -491,7 +491,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) + if (dma_mapping_error(dev, drv_data->tx_dma)) return -1; drv_data->tx_dma_needs_unmap = 1; @@ -516,7 +516,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->len, DMA_FROM_DEVICE); - if (dma_mapping_error(drv_data->rx_dma)) + if (dma_mapping_error(dev, drv_data->rx_dma)) return -1; drv_data->rx_dma_needs_unmap = 1; } @@ -534,7 +534,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) { + if (dma_mapping_error(dev, drv_data->tx_dma)) { if (drv_data->rx_dma) { dma_unmap_single(dev, drv_data->rx_dma, diff --git a/include/asm-alpha/dma-mapping.h b/include/asm-alpha/dma-mapping.h index db351d1296f4..a5801ae02e4b 100644 --- a/include/asm-alpha/dma-mapping.h +++ b/include/asm-alpha/dma-mapping.h @@ -24,8 +24,8 @@ pci_unmap_sg(alpha_gendev_to_pci(dev), sg, nents, dir) #define dma_supported(dev, mask) \ pci_dma_supported(alpha_gendev_to_pci(dev), mask) -#define dma_mapping_error(addr) \ - pci_dma_mapping_error(addr) +#define dma_mapping_error(dev, addr) \ + pci_dma_mapping_error(alpha_gendev_to_pci(dev), addr) #else /* no PCI - no IOMMU. */ @@ -45,7 +45,7 @@ int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, #define dma_unmap_page(dev, addr, size, dir) ((void)0) #define dma_unmap_sg(dev, sg, nents, dir) ((void)0) -#define dma_mapping_error(addr) (0) +#define dma_mapping_error(dev, addr) (0) #endif /* !CONFIG_PCI */ diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h index d31fd49ff79a..2a14302c17a3 100644 --- a/include/asm-alpha/pci.h +++ b/include/asm-alpha/pci.h @@ -106,7 +106,7 @@ extern dma_addr_t pci_map_page(struct pci_dev *, struct page *, /* Test for pci_map_single or pci_map_page having generated an error. */ static inline int -pci_dma_mapping_error(dma_addr_t dma_addr) +pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr) { return dma_addr == 0; } diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h index e99406a7bece..f41335ba6337 100644 --- a/include/asm-arm/dma-mapping.h +++ b/include/asm-arm/dma-mapping.h @@ -56,7 +56,7 @@ static inline int dma_is_consistent(struct device *dev, dma_addr_t handle) /* * DMA errors are defined by all-bits-set in the DMA address. */ -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return dma_addr == ~0; } diff --git a/include/asm-avr32/dma-mapping.h b/include/asm-avr32/dma-mapping.h index 57dc672bab8e..0399359ab5d8 100644 --- a/include/asm-avr32/dma-mapping.h +++ b/include/asm-avr32/dma-mapping.h @@ -35,7 +35,7 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask) /* * dma_map_single can't fail as it is implemented now. */ -static inline int dma_mapping_error(dma_addr_t addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t addr) { return 0; } diff --git a/include/asm-cris/dma-mapping.h b/include/asm-cris/dma-mapping.h index edc8d1bfaae2..cb2fb25ff8d9 100644 --- a/include/asm-cris/dma-mapping.h +++ b/include/asm-cris/dma-mapping.h @@ -120,7 +120,7 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, } static inline int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/asm-frv/dma-mapping.h b/include/asm-frv/dma-mapping.h index 2e8966ca030d..b2898877c07b 100644 --- a/include/asm-frv/dma-mapping.h +++ b/include/asm-frv/dma-mapping.h @@ -126,7 +126,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nele } static inline -int dma_mapping_error(dma_addr_t dma_addr) +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/asm-generic/dma-mapping-broken.h b/include/asm-generic/dma-mapping-broken.h index e2468f894d2a..82cd0cb1c3fe 100644 --- a/include/asm-generic/dma-mapping-broken.h +++ b/include/asm-generic/dma-mapping-broken.h @@ -61,7 +61,7 @@ dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, #define dma_sync_sg_for_device dma_sync_sg_for_cpu extern int -dma_mapping_error(dma_addr_t dma_addr); +dma_mapping_error(struct device *dev, dma_addr_t dma_addr); extern int dma_supported(struct device *dev, u64 mask); diff --git a/include/asm-generic/dma-mapping.h b/include/asm-generic/dma-mapping.h index 783ab9944d70..189486c3f92e 100644 --- a/include/asm-generic/dma-mapping.h +++ b/include/asm-generic/dma-mapping.h @@ -144,9 +144,9 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, } static inline int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - return pci_dma_mapping_error(dma_addr); + return pci_dma_mapping_error(to_pci_dev(dev), dma_addr); } diff --git a/include/asm-generic/pci-dma-compat.h b/include/asm-generic/pci-dma-compat.h index 25c10e96b2b7..37b3706226e7 100644 --- a/include/asm-generic/pci-dma-compat.h +++ b/include/asm-generic/pci-dma-compat.h @@ -99,9 +99,9 @@ pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, } static inline int -pci_dma_mapping_error(dma_addr_t dma_addr) +pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr) { - return dma_mapping_error(dma_addr); + return dma_mapping_error(&pdev->dev, dma_addr); } #endif diff --git a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h index 0721a5e8271e..a6d50c77b6bf 100644 --- a/include/asm-ia64/machvec.h +++ b/include/asm-ia64/machvec.h @@ -54,7 +54,7 @@ typedef void ia64_mv_dma_sync_single_for_cpu (struct device *, dma_addr_t, size_ typedef void ia64_mv_dma_sync_sg_for_cpu (struct device *, struct scatterlist *, int, int); typedef void ia64_mv_dma_sync_single_for_device (struct device *, dma_addr_t, size_t, int); typedef void ia64_mv_dma_sync_sg_for_device (struct device *, struct scatterlist *, int, int); -typedef int ia64_mv_dma_mapping_error (dma_addr_t dma_addr); +typedef int ia64_mv_dma_mapping_error(struct device *, dma_addr_t dma_addr); typedef int ia64_mv_dma_supported (struct device *, u64); typedef dma_addr_t ia64_mv_dma_map_single_attrs (struct device *, void *, size_t, int, struct dma_attrs *); diff --git a/include/asm-m68k/dma-mapping.h b/include/asm-m68k/dma-mapping.h index a26cdeb46a57..91f7944333d4 100644 --- a/include/asm-m68k/dma-mapping.h +++ b/include/asm-m68k/dma-mapping.h @@ -84,7 +84,7 @@ static inline void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *s { } -static inline int dma_mapping_error(dma_addr_t handle) +static inline int dma_mapping_error(struct device *dev, dma_addr_t handle) { return 0; } diff --git a/include/asm-mips/dma-mapping.h b/include/asm-mips/dma-mapping.h index 230b3f1b69b1..c64afb40cd06 100644 --- a/include/asm-mips/dma-mapping.h +++ b/include/asm-mips/dma-mapping.h @@ -42,7 +42,7 @@ extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); -extern int dma_mapping_error(dma_addr_t dma_addr); +extern int dma_mapping_error(struct device *dev, dma_addr_t dma_addr); extern int dma_supported(struct device *dev, u64 mask); static inline int diff --git a/include/asm-mn10300/dma-mapping.h b/include/asm-mn10300/dma-mapping.h index 7c882fca9ec8..ccae8f6c6326 100644 --- a/include/asm-mn10300/dma-mapping.h +++ b/include/asm-mn10300/dma-mapping.h @@ -182,7 +182,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, } static inline -int dma_mapping_error(dma_addr_t dma_addr) +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/asm-parisc/dma-mapping.h b/include/asm-parisc/dma-mapping.h index c6c0e9ff6bde..53af696f23d2 100644 --- a/include/asm-parisc/dma-mapping.h +++ b/include/asm-parisc/dma-mapping.h @@ -248,6 +248,6 @@ void * sba_get_iommu(struct parisc_device *dev); #endif /* At the moment, we panic on error for IOMMU resource exaustion */ -#define dma_mapping_error(x) 0 +#define dma_mapping_error(dev, x) 0 #endif diff --git a/include/asm-powerpc/dma-mapping.h b/include/asm-powerpc/dma-mapping.h index 74c549780987..c7ca45f97dd2 100644 --- a/include/asm-powerpc/dma-mapping.h +++ b/include/asm-powerpc/dma-mapping.h @@ -415,7 +415,7 @@ static inline void dma_sync_sg_for_device(struct device *dev, __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction); } -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { #ifdef CONFIG_PPC64 return (dma_addr == DMA_ERROR_CODE); diff --git a/include/asm-sh/dma-mapping.h b/include/asm-sh/dma-mapping.h index 22cc419389fe..6c0b8a2de143 100644 --- a/include/asm-sh/dma-mapping.h +++ b/include/asm-sh/dma-mapping.h @@ -171,7 +171,7 @@ static inline int dma_get_cache_alignment(void) return L1_CACHE_BYTES; } -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return dma_addr == 0; } diff --git a/include/asm-sparc/dma-mapping_64.h b/include/asm-sparc/dma-mapping_64.h index 38cbec76a33f..bfa64f9702d5 100644 --- a/include/asm-sparc/dma-mapping_64.h +++ b/include/asm-sparc/dma-mapping_64.h @@ -135,7 +135,7 @@ static inline void dma_sync_sg_for_device(struct device *dev, /* No flushing needed to sync cpu writes to the device. */ } -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return (dma_addr == DMA_ERROR_CODE); } diff --git a/include/asm-sparc/pci_32.h b/include/asm-sparc/pci_32.h index b93b6c79e08f..0ee949d220c0 100644 --- a/include/asm-sparc/pci_32.h +++ b/include/asm-sparc/pci_32.h @@ -154,7 +154,8 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, #define PCI_DMA_ERROR_CODE (~(dma_addr_t)0x0) -static inline int pci_dma_mapping_error(dma_addr_t dma_addr) +static inline int pci_dma_mapping_error(struct pci_dev *pdev, + dma_addr_t dma_addr) { return (dma_addr == PCI_DMA_ERROR_CODE); } diff --git a/include/asm-sparc/pci_64.h b/include/asm-sparc/pci_64.h index f59f2571295b..4f79a54948f6 100644 --- a/include/asm-sparc/pci_64.h +++ b/include/asm-sparc/pci_64.h @@ -140,9 +140,10 @@ extern int pci_dma_supported(struct pci_dev *hwdev, u64 mask); #define PCI64_REQUIRED_MASK (~(dma64_addr_t)0) #define PCI64_ADDR_BASE 0xfffc000000000000UL -static inline int pci_dma_mapping_error(dma_addr_t dma_addr) +static inline int pci_dma_mapping_error(struct pci_dev *pdev, + dma_addr_t dma_addr) { - return dma_mapping_error(dma_addr); + return dma_mapping_error(&pdev->dev, dma_addr); } #ifdef CONFIG_PCI diff --git a/include/asm-x86/device.h b/include/asm-x86/device.h index 87a715367a1b..3c034f48fdb0 100644 --- a/include/asm-x86/device.h +++ b/include/asm-x86/device.h @@ -5,6 +5,9 @@ struct dev_archdata { #ifdef CONFIG_ACPI void *acpi_handle; #endif +#ifdef CONFIG_X86_64 +struct dma_mapping_ops *dma_ops; +#endif #ifdef CONFIG_DMAR void *iommu; /* hook for IOMMU specific extension */ #endif diff --git a/include/asm-x86/dma-mapping.h b/include/asm-x86/dma-mapping.h index c2ddd3d1b883..0eaa9bf6011f 100644 --- a/include/asm-x86/dma-mapping.h +++ b/include/asm-x86/dma-mapping.h @@ -17,7 +17,8 @@ extern int panic_on_overflow; extern int force_iommu; struct dma_mapping_ops { - int (*mapping_error)(dma_addr_t dma_addr); + int (*mapping_error)(struct device *dev, + dma_addr_t dma_addr); void* (*alloc_coherent)(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp); void (*free_coherent)(struct device *dev, size_t size, @@ -56,14 +57,32 @@ struct dma_mapping_ops { int is_phys; }; -extern const struct dma_mapping_ops *dma_ops; +extern struct dma_mapping_ops *dma_ops; -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline struct dma_mapping_ops *get_dma_ops(struct device *dev) { - if (dma_ops->mapping_error) - return dma_ops->mapping_error(dma_addr); +#ifdef CONFIG_X86_32 + return dma_ops; +#else + if (unlikely(!dev) || !dev->archdata.dma_ops) + return dma_ops; + else + return dev->archdata.dma_ops; +#endif +} + +/* Make sure we keep the same behaviour */ +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ +#ifdef CONFIG_X86_32 + return 0; +#else + struct dma_mapping_ops *ops = get_dma_ops(dev); + if (ops->mapping_error) + return ops->mapping_error(dev, dma_addr); return (dma_addr == bad_dma_address); +#endif } #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) @@ -83,44 +102,53 @@ static inline dma_addr_t dma_map_single(struct device *hwdev, void *ptr, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - return dma_ops->map_single(hwdev, virt_to_phys(ptr), size, direction); + return ops->map_single(hwdev, virt_to_phys(ptr), size, direction); } static inline void dma_unmap_single(struct device *dev, dma_addr_t addr, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->unmap_single) - dma_ops->unmap_single(dev, addr, size, direction); + if (ops->unmap_single) + ops->unmap_single(dev, addr, size, direction); } static inline int dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - return dma_ops->map_sg(hwdev, sg, nents, direction); + return ops->map_sg(hwdev, sg, nents, direction); } static inline void dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->unmap_sg) - dma_ops->unmap_sg(hwdev, sg, nents, direction); + if (ops->unmap_sg) + ops->unmap_sg(hwdev, sg, nents, direction); } static inline void dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_for_cpu) - dma_ops->sync_single_for_cpu(hwdev, dma_handle, size, - direction); + if (ops->sync_single_for_cpu) + ops->sync_single_for_cpu(hwdev, dma_handle, size, direction); flush_write_buffers(); } @@ -128,10 +156,11 @@ static inline void dma_sync_single_for_device(struct device *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_for_device) - dma_ops->sync_single_for_device(hwdev, dma_handle, size, - direction); + if (ops->sync_single_for_device) + ops->sync_single_for_device(hwdev, dma_handle, size, direction); flush_write_buffers(); } @@ -139,11 +168,12 @@ static inline void dma_sync_single_range_for_cpu(struct device *hwdev, dma_addr_t dma_handle, unsigned long offset, size_t size, int direction) { - BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_range_for_cpu) - dma_ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, - size, direction); + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); + if (ops->sync_single_range_for_cpu) + ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, + size, direction); flush_write_buffers(); } @@ -152,11 +182,12 @@ dma_sync_single_range_for_device(struct device *hwdev, dma_addr_t dma_handle, unsigned long offset, size_t size, int direction) { - BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_range_for_device) - dma_ops->sync_single_range_for_device(hwdev, dma_handle, - offset, size, direction); + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); + if (ops->sync_single_range_for_device) + ops->sync_single_range_for_device(hwdev, dma_handle, + offset, size, direction); flush_write_buffers(); } @@ -164,9 +195,11 @@ static inline void dma_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, int nelems, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_sg_for_cpu) - dma_ops->sync_sg_for_cpu(hwdev, sg, nelems, direction); + if (ops->sync_sg_for_cpu) + ops->sync_sg_for_cpu(hwdev, sg, nelems, direction); flush_write_buffers(); } @@ -174,9 +207,11 @@ static inline void dma_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, int nelems, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_sg_for_device) - dma_ops->sync_sg_for_device(hwdev, sg, nelems, direction); + if (ops->sync_sg_for_device) + ops->sync_sg_for_device(hwdev, sg, nelems, direction); flush_write_buffers(); } @@ -185,9 +220,11 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, size_t offset, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + BUG_ON(!valid_dma_direction(direction)); - return dma_ops->map_single(dev, page_to_phys(page)+offset, - size, direction); + return ops->map_single(dev, page_to_phys(page) + offset, + size, direction); } static inline void dma_unmap_page(struct device *dev, dma_addr_t addr, diff --git a/include/asm-x86/swiotlb.h b/include/asm-x86/swiotlb.h index c706a7442633..2730b351afcf 100644 --- a/include/asm-x86/swiotlb.h +++ b/include/asm-x86/swiotlb.h @@ -35,7 +35,7 @@ extern int swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction); extern void swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction); -extern int swiotlb_dma_mapping_error(dma_addr_t dma_addr); +extern int swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr); extern void swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle); extern int swiotlb_dma_supported(struct device *hwdev, u64 mask); diff --git a/include/asm-xtensa/dma-mapping.h b/include/asm-xtensa/dma-mapping.h index 3c7d537dd15d..51882ae3db4d 100644 --- a/include/asm-xtensa/dma-mapping.h +++ b/include/asm-xtensa/dma-mapping.h @@ -139,7 +139,7 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, consistent_sync(sg_virt(sg), sg->length, dir); } static inline int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/linux/i2o.h b/include/linux/i2o.h index 7d51cbca49ab..75ae6d8aba4f 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -758,7 +758,7 @@ static inline dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr, } dma_addr = dma_map_single(&c->pdev->dev, ptr, size, direction); - if (!dma_mapping_error(dma_addr)) { + if (!dma_mapping_error(&c->pdev->dev, dma_addr)) { #ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 if ((sizeof(dma_addr_t) > 4) && c->pae_support) { *mptr++ = cpu_to_le32(0x7C020002); diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 4bf8cade9dbc..e530026eedf7 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -427,9 +427,9 @@ static inline int ssb_dma_mapping_error(struct ssb_device *dev, dma_addr_t addr) { switch (dev->bus->bustype) { case SSB_BUSTYPE_PCI: - return pci_dma_mapping_error(addr); + return pci_dma_mapping_error(dev->bus->host_pci, addr); case SSB_BUSTYPE_SSB: - return dma_mapping_error(addr); + return dma_mapping_error(dev->dev, addr); default: __ssb_dma_not_implemented(dev); } diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 90b529f7a154..936e333e7ce5 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1590,7 +1590,7 @@ static inline int ib_dma_mapping_error(struct ib_device *dev, u64 dma_addr) { if (dev->dma_ops) return dev->dma_ops->mapping_error(dev, dma_addr); - return dma_mapping_error(dma_addr); + return dma_mapping_error(dev->dma_device, dma_addr); } /** diff --git a/lib/swiotlb.c b/lib/swiotlb.c index d568894df8cc..977edbdbc1de 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -492,7 +492,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size, */ dma_addr_t handle; handle = swiotlb_map_single(NULL, NULL, size, DMA_FROM_DEVICE); - if (swiotlb_dma_mapping_error(handle)) + if (swiotlb_dma_mapping_error(hwdev, handle)) return NULL; ret = bus_to_virt(handle); @@ -824,7 +824,7 @@ swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, } int -swiotlb_dma_mapping_error(dma_addr_t dma_addr) +swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) { return (dma_addr == virt_to_bus(io_tlb_overflow_buffer)); } diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index a19b22b452a3..84d328329d98 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -169,7 +169,8 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, (void *) vec->sge[xdr_sge_no].iov_base + sge_off, sge_bytes, DMA_TO_DEVICE); - if (dma_mapping_error(sge[sge_no].addr)) + if (dma_mapping_error(xprt->sc_cm_id->device->dma_device, + sge[sge_no].addr)) goto err; sge_off = 0; sge_no++; -- cgit v1.2.3 From 929dfb24fbcd60e2544b2de7bfb4a68da4dfc747 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:44:54 -0700 Subject: parport/share.c: proper externs This patch adds proper externs for parport_default_timeslice and parport_default_spintime in include/linux/parport.h Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/parport/procfs.c | 3 --- include/linux/parport.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index d950fc34320a..554e11f9e1ce 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -429,9 +429,6 @@ struct parport_default_sysctl_table ctl_table dev_dir[2]; }; -extern unsigned long parport_default_timeslice; -extern int parport_default_spintime; - static struct parport_default_sysctl_table parport_default_sysctl_table = { .sysctl_header = NULL, diff --git a/include/linux/parport.h b/include/linux/parport.h index dcb9e01a69ca..6a0d7cdb5774 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -560,5 +560,8 @@ extern int parport_device_proc_unregister(struct pardevice *device); #endif /* !CONFIG_PARPORT_NOT_PC */ +extern unsigned long parport_default_timeslice; +extern int parport_default_spintime; + #endif /* __KERNEL__ */ #endif /* _PARPORT_H_ */ -- cgit v1.2.3 From b77899985bdfd85a8e5a6e485033a9b4713d2471 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 25 Jul 2008 19:45:00 -0700 Subject: memstick: allow "set_param" method to return an error code Some controllers (Jmicron, for instance) can report temporal failure condition during power-on. It is desirable to account for this using a return value of "set_param" device method. The return value can also be handy to distinguish between supported and unsupported device parameters in run time. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Alex Dubov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/core/memstick.c | 18 ++++++++--- drivers/memstick/host/jmb38x_ms.c | 67 ++++++++++++++++++++++++++++----------- drivers/memstick/host/tifm_ms.c | 17 +++++----- include/linux/memstick.h | 2 +- 4 files changed, 71 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index 61b98c333cb0..3c7d9a79c1ea 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -415,10 +415,14 @@ err_out: return NULL; } -static void memstick_power_on(struct memstick_host *host) +static int memstick_power_on(struct memstick_host *host) { - host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); - host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); + + if (!rc) + rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + + return rc; } static void memstick_check(struct work_struct *work) @@ -573,11 +577,15 @@ EXPORT_SYMBOL(memstick_suspend_host); */ void memstick_resume_host(struct memstick_host *host) { + int rc = 0; + mutex_lock(&host->lock); if (host->card) - memstick_power_on(host); + rc = memstick_power_on(host); mutex_unlock(&host->lock); - memstick_detect_change(host); + + if (!rc) + memstick_detect_change(host); } EXPORT_SYMBOL(memstick_resume_host); diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index 4e3bfbcdf155..9d82e67737db 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -609,36 +609,68 @@ static void jmb38x_ms_request(struct memstick_host *msh) spin_unlock_irqrestore(&host->lock, flags); } -static void jmb38x_ms_reset(struct jmb38x_ms_host *host) +static int jmb38x_ms_reset(struct jmb38x_ms_host *host) { - unsigned int host_ctl = readl(host->addr + HOST_CONTROL); + int cnt; - writel(HOST_CONTROL_RESET_REQ, host->addr + HOST_CONTROL); + writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN + | readl(host->addr + HOST_CONTROL), + host->addr + HOST_CONTROL); + mmiowb(); + + for (cnt = 0; cnt < 20; ++cnt) { + if (!(HOST_CONTROL_RESET_REQ + & readl(host->addr + HOST_CONTROL))) + goto reset_next; - while (HOST_CONTROL_RESET_REQ - & (host_ctl = readl(host->addr + HOST_CONTROL))) { ndelay(20); - dev_dbg(&host->chip->pdev->dev, "reset %08x\n", host_ctl); } + dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); + return -EIO; - writel(HOST_CONTROL_RESET, host->addr + HOST_CONTROL); +reset_next: + writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN + | readl(host->addr + HOST_CONTROL), + host->addr + HOST_CONTROL); + mmiowb(); + + for (cnt = 0; cnt < 20; ++cnt) { + if (!(HOST_CONTROL_RESET + & readl(host->addr + HOST_CONTROL))) + goto reset_ok; + + ndelay(20); + } + dev_dbg(&host->chip->pdev->dev, "reset timeout\n"); + return -EIO; + +reset_ok: mmiowb(); writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); + return 0; } -static void jmb38x_ms_set_param(struct memstick_host *msh, - enum memstick_param param, - int value) +static int jmb38x_ms_set_param(struct memstick_host *msh, + enum memstick_param param, + int value) { struct jmb38x_ms_host *host = memstick_priv(msh); unsigned int host_ctl = readl(host->addr + HOST_CONTROL); unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0; + int rc = 0; switch (param) { case MEMSTICK_POWER: if (value == MEMSTICK_POWER_ON) { - jmb38x_ms_reset(host); + rc = jmb38x_ms_reset(host); + if (rc) + return rc; + + host_ctl = 7; + host_ctl |= HOST_CONTROL_POWER_EN + | HOST_CONTROL_CLOCK_EN; + writel(host_ctl, host->addr + HOST_CONTROL); writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 : PAD_PU_PD_ON_MS_SOCK0, @@ -647,11 +679,7 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, writel(PAD_OUTPUT_ENABLE_MS, host->addr + PAD_OUTPUT_ENABLE); - host_ctl = 7; - host_ctl |= HOST_CONTROL_POWER_EN - | HOST_CONTROL_CLOCK_EN; - writel(host_ctl, host->addr + HOST_CONTROL); - + msleep(10); dev_dbg(&host->chip->pdev->dev, "power on\n"); } else if (value == MEMSTICK_POWER_OFF) { host_ctl &= ~(HOST_CONTROL_POWER_EN @@ -660,7 +688,8 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, writel(0, host->addr + PAD_OUTPUT_ENABLE); writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); dev_dbg(&host->chip->pdev->dev, "power off\n"); - } + } else + return -EINVAL; break; case MEMSTICK_INTERFACE: host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); @@ -686,12 +715,14 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, host_ctl &= ~HOST_CONTROL_REI; clock_ctl = CLOCK_CONTROL_60MHZ; clock_delay = 0; - } + } else + return -EINVAL; writel(host_ctl, host->addr + HOST_CONTROL); writel(clock_ctl, host->addr + CLOCK_CONTROL); writel(clock_delay, host->addr + CLOCK_DELAY); break; }; + return 0; } #ifdef CONFIG_PM diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c index 8577de4ebb0e..14458764588c 100644 --- a/drivers/memstick/host/tifm_ms.c +++ b/drivers/memstick/host/tifm_ms.c @@ -489,15 +489,12 @@ static void tifm_ms_request(struct memstick_host *msh) return; } -static void tifm_ms_set_param(struct memstick_host *msh, - enum memstick_param param, - int value) +static int tifm_ms_set_param(struct memstick_host *msh, + enum memstick_param param, + int value) { struct tifm_ms *host = memstick_priv(msh); struct tifm_dev *sock = host->dev; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); switch (param) { case MEMSTICK_POWER: @@ -512,7 +509,8 @@ static void tifm_ms_set_param(struct memstick_host *msh, writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, sock->addr + SOCK_MS_SYSTEM); writel(0xffffffff, sock->addr + SOCK_MS_STATUS); - } + } else + return -EINVAL; break; case MEMSTICK_INTERFACE: if (value == MEMSTICK_SERIAL) { @@ -525,11 +523,12 @@ static void tifm_ms_set_param(struct memstick_host *msh, writel(TIFM_CTRL_FAST_CLK | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); - } + } else + return -EINVAL; break; }; - spin_unlock_irqrestore(&sock->lock, flags); + return 0; } static void tifm_ms_abort(unsigned long data) diff --git a/include/linux/memstick.h b/include/linux/memstick.h index 37a5cdb03918..2fe599c66d52 100644 --- a/include/linux/memstick.h +++ b/include/linux/memstick.h @@ -284,7 +284,7 @@ struct memstick_host { /* Notify the host that some requests are pending. */ void (*request)(struct memstick_host *host); /* Set host IO parameters (power, clock, etc). */ - void (*set_param)(struct memstick_host *host, + int (*set_param)(struct memstick_host *host, enum memstick_param param, int value); unsigned long private[0] ____cacheline_aligned; -- cgit v1.2.3 From 17017d8d2c005734d7088d8281ce2daab8fcb097 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 25 Jul 2008 19:45:01 -0700 Subject: memstick: add "start" and "stop" methods to memstick device In some cases it may be desirable to ensure that associated driver is not going to access the media in some period of time. "start" and "stop" methods are provided therefore to allow it. Signed-off-by: Alex Dubov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/core/memstick.c | 11 ++++++++--- drivers/memstick/core/mspro_block.c | 33 +++++++++++++++++++++++++++++++++ include/linux/memstick.h | 4 ++++ 3 files changed, 45 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index 3c7d9a79c1ea..7162f772bbfb 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -433,8 +433,11 @@ static void memstick_check(struct work_struct *work) dev_dbg(&host->dev, "memstick_check started\n"); mutex_lock(&host->lock); - if (!host->card) - memstick_power_on(host); + if (!host->card) { + if (memstick_power_on(host)) + goto out_power_off; + } else + host->card->stop(host->card); card = memstick_alloc_card(host); @@ -452,7 +455,8 @@ static void memstick_check(struct work_struct *work) || !(host->card->check(host->card))) { device_unregister(&host->card->dev); host->card = NULL; - } + } else + host->card->start(host->card); } if (!host->card) { @@ -465,6 +469,7 @@ static void memstick_check(struct work_struct *work) kfree(card); } +out_power_off: if (!host->card) host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 477d0fb6e588..004ac4d176d9 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -752,6 +752,37 @@ static int mspro_block_has_request(struct mspro_block_data *msb) return rc; } +static void mspro_block_stop(struct memstick_dev *card) +{ + struct mspro_block_data *msb = memstick_get_drvdata(card); + int rc = 0; + unsigned long flags; + + while (1) { + spin_lock_irqsave(&msb->q_lock, flags); + if (!msb->has_request) { + blk_stop_queue(msb->queue); + rc = 1; + } + spin_unlock_irqrestore(&msb->q_lock, flags); + + if (rc) + break; + + wait_for_completion(&card->mrq_complete); + } +} + +static void mspro_block_start(struct memstick_dev *card) +{ + struct mspro_block_data *msb = memstick_get_drvdata(card); + unsigned long flags; + + spin_lock_irqsave(&msb->q_lock, flags); + blk_start_queue(msb->queue); + spin_unlock_irqrestore(&msb->q_lock, flags); +} + static int mspro_block_queue_thread(void *data) { struct memstick_dev *card = data; @@ -1272,6 +1303,8 @@ static int mspro_block_probe(struct memstick_dev *card) rc = mspro_block_init_disk(card); if (!rc) { card->check = mspro_block_check_card; + card->stop = mspro_block_stop; + card->start = mspro_block_start; return 0; } diff --git a/include/linux/memstick.h b/include/linux/memstick.h index 2fe599c66d52..a9f998a3f48b 100644 --- a/include/linux/memstick.h +++ b/include/linux/memstick.h @@ -263,6 +263,10 @@ struct memstick_dev { /* Get next request from the media driver. */ int (*next_request)(struct memstick_dev *card, struct memstick_request **mrq); + /* Tell the media driver to stop doing things */ + void (*stop)(struct memstick_dev *card); + /* Allow the media driver to continue */ + void (*start)(struct memstick_dev *card); struct device dev; }; -- cgit v1.2.3 From 3ab83521378268044a448113c6aa9a9e245f4d2f Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 25 Jul 2008 19:45:07 -0700 Subject: kexec jump This patch provides an enhancement to kexec/kdump. It implements the following features: - Backup/restore memory used by the original kernel before/after kexec. - Save/restore CPU state before/after kexec. The features of this patch can be used as a general method to call program in physical mode (paging turning off). This can be used to call BIOS code under Linux. kexec-tools needs to be patched to support kexec jump. The patches and the precompiled kexec can be download from the following URL: source: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-src_git_kh10.tar.bz2 patches: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-patches_git_kh10.tar.bz2 binary: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec_git_kh10 Usage example of calling some physical mode code and return: 1. Compile and install patched kernel with following options selected: CONFIG_X86_32=y CONFIG_KEXEC=y CONFIG_PM=y CONFIG_KEXEC_JUMP=y 2. Build patched kexec-tool or download the pre-built one. 3. Build some physical mode executable named such as "phy_mode" 4. Boot kernel compiled in step 1. 5. Load physical mode executable with /sbin/kexec. The shell command line can be as follow: /sbin/kexec --load-preserve-context --args-none phy_mode 6. Call physical mode executable with following shell command line: /sbin/kexec -e Implementation point: To support jumping without reserving memory. One shadow backup page (source page) is allocated for each page used by kexeced code image (destination page). When do kexec_load, the image of kexeced code is loaded into source pages, and before executing, the destination pages and the source pages are swapped, so the contents of destination pages are backupped. Before jumping to the kexeced code image and after jumping back to the original kernel, the destination pages and the source pages are swapped too. C ABI (calling convention) is used as communication protocol between kernel and called code. A flag named KEXEC_PRESERVE_CONTEXT for sys_kexec_load is added to indicate that the loaded kernel image is used for jumping back. Now, only the i386 architecture is supported. Signed-off-by: Huang Ying Acked-by: Vivek Goyal Cc: "Eric W. Biederman" Cc: Pavel Machek Cc: Nigel Cunningham Cc: "Rafael J. Wysocki" Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/machine_kexec.c | 2 +- arch/sh/kernel/machine_kexec.c | 2 +- arch/x86/Kconfig | 7 ++ arch/x86/kernel/machine_kexec_32.c | 27 ++++-- arch/x86/kernel/machine_kexec_64.c | 2 +- arch/x86/kernel/relocate_kernel_32.S | 174 ++++++++++++++++++++++++++++++----- include/asm-x86/kexec.h | 18 ++-- include/linux/kexec.h | 17 +++- kernel/kexec.c | 57 ++++++++++++ kernel/sys.c | 31 ++----- 10 files changed, 269 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index 29a0e039d436..aab76887a842 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -48,7 +48,7 @@ void machine_kexec_cleanup(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { if (ppc_md.machine_kexec) ppc_md.machine_kexec(image); diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 5c17de51987e..ec1eadce4aaa 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -70,7 +70,7 @@ static void kexec_info(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { unsigned long page_list; diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e3cba0b45600..7ecb679f0130 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1279,6 +1279,13 @@ config CRASH_DUMP (CONFIG_RELOCATABLE=y). For more details see Documentation/kdump/kdump.txt +config KEXEC_JUMP + bool "kexec jump (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on KEXEC && PM_SLEEP && X86_32 + help + Invoke code in physical address mode via KEXEC + config PHYSICAL_START hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP) default "0x1000000" if X86_NUMAQ diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index 8864230d55af..2b67609d0a1c 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -22,6 +22,7 @@ #include #include #include +#include #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) static u32 kexec_pgd[1024] PAGE_ALIGNED; @@ -85,10 +86,12 @@ static void load_segments(void) * reboot code buffer to allow us to avoid allocations * later. * - * Currently nothing. + * Make control page executable. */ int machine_kexec_prepare(struct kimage *image) { + if (nx_enabled) + set_pages_x(image->control_code_page, 1); return 0; } @@ -98,16 +101,24 @@ int machine_kexec_prepare(struct kimage *image) */ void machine_kexec_cleanup(struct kimage *image) { + if (nx_enabled) + set_pages_nx(image->control_code_page, 1); } /* * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { unsigned long page_list[PAGES_NR]; void *control_page; + asmlinkage unsigned long + (*relocate_kernel_ptr)(unsigned long indirection_page, + unsigned long control_page, + unsigned long start_address, + unsigned int has_pae, + unsigned int preserve_context); tracer_disable(); @@ -115,10 +126,11 @@ NORET_TYPE void machine_kexec(struct kimage *image) local_irq_disable(); control_page = page_address(image->control_code_page); - memcpy(control_page, relocate_kernel, PAGE_SIZE); + memcpy(control_page, relocate_kernel, PAGE_SIZE/2); + relocate_kernel_ptr = control_page; page_list[PA_CONTROL_PAGE] = __pa(control_page); - page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; + page_list[VA_CONTROL_PAGE] = (unsigned long)control_page; page_list[PA_PGD] = __pa(kexec_pgd); page_list[VA_PGD] = (unsigned long)kexec_pgd; #ifdef CONFIG_X86_PAE @@ -131,6 +143,7 @@ NORET_TYPE void machine_kexec(struct kimage *image) page_list[VA_PTE_0] = (unsigned long)kexec_pte0; page_list[PA_PTE_1] = __pa(kexec_pte1); page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page) << PAGE_SHIFT); /* The segment registers are funny things, they have both a * visible and an invisible part. Whenever the visible part is @@ -149,8 +162,10 @@ NORET_TYPE void machine_kexec(struct kimage *image) set_idt(phys_to_virt(0),0); /* now call it */ - relocate_kernel((unsigned long)image->head, (unsigned long)page_list, - image->start, cpu_has_pae); + image->start = relocate_kernel_ptr((unsigned long)image->head, + (unsigned long)page_list, + image->start, cpu_has_pae, + image->preserve_context); } void arch_crash_save_vmcoreinfo(void) diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index 9dd9262693a3..c43caa3a91f3 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -181,7 +181,7 @@ void machine_kexec_cleanup(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { unsigned long page_list[PAGES_NR]; void *control_page; diff --git a/arch/x86/kernel/relocate_kernel_32.S b/arch/x86/kernel/relocate_kernel_32.S index c30fe25d470d..703310a99023 100644 --- a/arch/x86/kernel/relocate_kernel_32.S +++ b/arch/x86/kernel/relocate_kernel_32.S @@ -20,11 +20,44 @@ #define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) #define PAE_PGD_ATTR (_PAGE_PRESENT) +/* control_page + PAGE_SIZE/2 ~ control_page + PAGE_SIZE * 3/4 are + * used to save some data for jumping back + */ +#define DATA(offset) (PAGE_SIZE/2+(offset)) + +/* Minimal CPU state */ +#define ESP DATA(0x0) +#define CR0 DATA(0x4) +#define CR3 DATA(0x8) +#define CR4 DATA(0xc) + +/* other data */ +#define CP_VA_CONTROL_PAGE DATA(0x10) +#define CP_PA_PGD DATA(0x14) +#define CP_PA_SWAP_PAGE DATA(0x18) +#define CP_PA_BACKUP_PAGES_MAP DATA(0x1c) + .text .align PAGE_SIZE .globl relocate_kernel relocate_kernel: - movl 8(%esp), %ebp /* list of pages */ + /* Save the CPU context, used for jumping back */ + + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + pushf + + movl 20+8(%esp), %ebp /* list of pages */ + movl PTR(VA_CONTROL_PAGE)(%ebp), %edi + movl %esp, ESP(%edi) + movl %cr0, %eax + movl %eax, CR0(%edi) + movl %cr3, %eax + movl %eax, CR3(%edi) + movl %cr4, %eax + movl %eax, CR4(%edi) #ifdef CONFIG_X86_PAE /* map the control page at its virtual address */ @@ -138,15 +171,25 @@ relocate_kernel: relocate_new_kernel: /* read the arguments and say goodbye to the stack */ - movl 4(%esp), %ebx /* page_list */ - movl 8(%esp), %ebp /* list of pages */ - movl 12(%esp), %edx /* start address */ - movl 16(%esp), %ecx /* cpu_has_pae */ + movl 20+4(%esp), %ebx /* page_list */ + movl 20+8(%esp), %ebp /* list of pages */ + movl 20+12(%esp), %edx /* start address */ + movl 20+16(%esp), %ecx /* cpu_has_pae */ + movl 20+20(%esp), %esi /* preserve_context */ /* zero out flags, and disable interrupts */ pushl $0 popfl + /* save some information for jumping back */ + movl PTR(VA_CONTROL_PAGE)(%ebp), %edi + movl %edi, CP_VA_CONTROL_PAGE(%edi) + movl PTR(PA_PGD)(%ebp), %eax + movl %eax, CP_PA_PGD(%edi) + movl PTR(PA_SWAP_PAGE)(%ebp), %eax + movl %eax, CP_PA_SWAP_PAGE(%edi) + movl %ebx, CP_PA_BACKUP_PAGES_MAP(%edi) + /* get physical address of control page now */ /* this is impossible after page table switch */ movl PTR(PA_CONTROL_PAGE)(%ebp), %edi @@ -197,8 +240,90 @@ identity_mapped: xorl %eax, %eax movl %eax, %cr3 + movl CP_PA_SWAP_PAGE(%edi), %eax + pushl %eax + pushl %ebx + call swap_pages + addl $8, %esp + + /* To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB, it's handy, and not processor dependent. + */ + xorl %eax, %eax + movl %eax, %cr3 + + /* set all of the registers to known values */ + /* leave %esp alone */ + + testl %esi, %esi + jnz 1f + xorl %edi, %edi + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %esi, %esi + xorl %ebp, %ebp + ret +1: + popl %edx + movl CP_PA_SWAP_PAGE(%edi), %esp + addl $PAGE_SIZE, %esp +2: + call *%edx + + /* get the re-entry point of the peer system */ + movl 0(%esp), %ebp + call 1f +1: + popl %ebx + subl $(1b - relocate_kernel), %ebx + movl CP_VA_CONTROL_PAGE(%ebx), %edi + lea PAGE_SIZE(%ebx), %esp + movl CP_PA_SWAP_PAGE(%ebx), %eax + movl CP_PA_BACKUP_PAGES_MAP(%ebx), %edx + pushl %eax + pushl %edx + call swap_pages + addl $8, %esp + movl CP_PA_PGD(%ebx), %eax + movl %eax, %cr3 + movl %cr0, %eax + orl $(1<<31), %eax + movl %eax, %cr0 + lea PAGE_SIZE(%edi), %esp + movl %edi, %eax + addl $(virtual_mapped - relocate_kernel), %eax + pushl %eax + ret + +virtual_mapped: + movl CR4(%edi), %eax + movl %eax, %cr4 + movl CR3(%edi), %eax + movl %eax, %cr3 + movl CR0(%edi), %eax + movl %eax, %cr0 + movl ESP(%edi), %esp + movl %ebp, %eax + + popf + popl %ebp + popl %edi + popl %esi + popl %ebx + ret + /* Do the copies */ - movl %ebx, %ecx +swap_pages: + movl 8(%esp), %edx + movl 4(%esp), %ecx + pushl %ebp + pushl %ebx + pushl %edi + pushl %esi + movl %ecx, %ebx jmp 1f 0: /* top, read another word from the indirection page */ @@ -226,27 +351,28 @@ identity_mapped: movl %ecx, %esi /* For every source page do a copy */ andl $0xfffff000, %esi + movl %edi, %eax + movl %esi, %ebp + + movl %edx, %edi movl $1024, %ecx rep ; movsl - jmp 0b -3: - - /* To be certain of avoiding problems with self-modifying code - * I need to execute a serializing instruction here. - * So I flush the TLB, it's handy, and not processor dependent. - */ - xorl %eax, %eax - movl %eax, %cr3 + movl %ebp, %edi + movl %eax, %esi + movl $1024, %ecx + rep ; movsl - /* set all of the registers to known values */ - /* leave %esp alone */ + movl %eax, %edi + movl %edx, %esi + movl $1024, %ecx + rep ; movsl - xorl %eax, %eax - xorl %ebx, %ebx - xorl %ecx, %ecx - xorl %edx, %edx - xorl %esi, %esi - xorl %edi, %edi - xorl %ebp, %ebp + lea PAGE_SIZE(%ebp), %esi + jmp 0b +3: + popl %esi + popl %edi + popl %ebx + popl %ebp ret diff --git a/include/asm-x86/kexec.h b/include/asm-x86/kexec.h index 8f855a15f64d..c0e52a14fd4d 100644 --- a/include/asm-x86/kexec.h +++ b/include/asm-x86/kexec.h @@ -10,14 +10,15 @@ # define VA_PTE_0 5 # define PA_PTE_1 6 # define VA_PTE_1 7 +# define PA_SWAP_PAGE 8 # ifdef CONFIG_X86_PAE -# define PA_PMD_0 8 -# define VA_PMD_0 9 -# define PA_PMD_1 10 -# define VA_PMD_1 11 -# define PAGES_NR 12 +# define PA_PMD_0 9 +# define VA_PMD_0 10 +# define PA_PMD_1 11 +# define VA_PMD_1 12 +# define PAGES_NR 13 # else -# define PAGES_NR 8 +# define PAGES_NR 9 # endif #else # define PA_CONTROL_PAGE 0 @@ -152,11 +153,12 @@ static inline void crash_setup_regs(struct pt_regs *newregs, } #ifdef CONFIG_X86_32 -asmlinkage NORET_TYPE void +asmlinkage unsigned long relocate_kernel(unsigned long indirection_page, unsigned long control_page, unsigned long start_address, - unsigned int has_pae) ATTRIB_NORET; + unsigned int has_pae, + unsigned int preserve_context); #else NORET_TYPE void relocate_kernel(unsigned long indirection_page, diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 3265968cd2cd..82f88a8a827b 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -83,6 +83,7 @@ struct kimage { unsigned long start; struct page *control_code_page; + struct page *swap_page; unsigned long nr_segments; struct kexec_segment segment[KEXEC_SEGMENT_MAX]; @@ -98,18 +99,20 @@ struct kimage { unsigned int type : 1; #define KEXEC_TYPE_DEFAULT 0 #define KEXEC_TYPE_CRASH 1 + unsigned int preserve_context : 1; }; /* kexec interface functions */ -extern NORET_TYPE void machine_kexec(struct kimage *image) ATTRIB_NORET; +extern void machine_kexec(struct kimage *image); extern int machine_kexec_prepare(struct kimage *image); extern void machine_kexec_cleanup(struct kimage *image); extern asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment __user *segments, unsigned long flags); +extern int kernel_kexec(void); #ifdef CONFIG_COMPAT extern asmlinkage long compat_sys_kexec_load(unsigned long entry, unsigned long nr_segments, @@ -156,8 +159,9 @@ extern struct kimage *kexec_crash_image; #define kexec_flush_icache_page(page) #endif -#define KEXEC_ON_CRASH 0x00000001 -#define KEXEC_ARCH_MASK 0xffff0000 +#define KEXEC_ON_CRASH 0x00000001 +#define KEXEC_PRESERVE_CONTEXT 0x00000002 +#define KEXEC_ARCH_MASK 0xffff0000 /* These values match the ELF architecture values. * Unless there is a good reason that should continue to be the case. @@ -174,7 +178,12 @@ extern struct kimage *kexec_crash_image; #define KEXEC_ARCH_MIPS_LE (10 << 16) #define KEXEC_ARCH_MIPS ( 8 << 16) -#define KEXEC_FLAGS (KEXEC_ON_CRASH) /* List of defined/legal kexec flags */ +/* List of defined/legal kexec flags */ +#ifndef CONFIG_KEXEC_JUMP +#define KEXEC_FLAGS KEXEC_ON_CRASH +#else +#define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT) +#endif #define VMCOREINFO_BYTES (4096) #define VMCOREINFO_NOTE_NAME "VMCOREINFO" diff --git a/kernel/kexec.c b/kernel/kexec.c index 6db42ff8d520..a0d920915b38 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -242,6 +244,12 @@ static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry, goto out; } + image->swap_page = kimage_alloc_control_pages(image, 0); + if (!image->swap_page) { + printk(KERN_ERR "Could not allocate swap buffer\n"); + goto out; + } + result = 0; out: if (result == 0) @@ -986,6 +994,8 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, if (result) goto out; + if (flags & KEXEC_PRESERVE_CONTEXT) + image->preserve_context = 1; result = machine_kexec_prepare(image); if (result) goto out; @@ -1411,3 +1421,50 @@ static int __init crash_save_vmcoreinfo_init(void) } module_init(crash_save_vmcoreinfo_init) + +/** + * kernel_kexec - reboot the system + * + * Move into place and start executing a preloaded standalone + * executable. If nothing was preloaded return an error. + */ +int kernel_kexec(void) +{ + int error = 0; + + if (xchg(&kexec_lock, 1)) + return -EBUSY; + if (!kexec_image) { + error = -EINVAL; + goto Unlock; + } + + if (kexec_image->preserve_context) { +#ifdef CONFIG_KEXEC_JUMP + local_irq_disable(); + save_processor_state(); +#endif + } else { + blocking_notifier_call_chain(&reboot_notifier_list, + SYS_RESTART, NULL); + system_state = SYSTEM_RESTART; + device_shutdown(); + sysdev_shutdown(); + printk(KERN_EMERG "Starting new kernel\n"); + machine_shutdown(); + } + + machine_kexec(kexec_image); + + if (kexec_image->preserve_context) { +#ifdef CONFIG_KEXEC_JUMP + restore_processor_state(); + local_irq_enable(); +#endif + } + + Unlock: + xchg(&kexec_lock, 0); + + return error; +} diff --git a/kernel/sys.c b/kernel/sys.c index 0c9d3fa1f5ff..c01858090a98 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -301,26 +301,6 @@ void kernel_restart(char *cmd) } EXPORT_SYMBOL_GPL(kernel_restart); -/** - * kernel_kexec - reboot the system - * - * Move into place and start executing a preloaded standalone - * executable. If nothing was preloaded return an error. - */ -static void kernel_kexec(void) -{ -#ifdef CONFIG_KEXEC - struct kimage *image; - image = xchg(&kexec_image, NULL); - if (!image) - return; - kernel_restart_prepare(NULL); - printk(KERN_EMERG "Starting new kernel\n"); - machine_shutdown(); - machine_kexec(image); -#endif -} - static void kernel_shutdown_prepare(enum system_states state) { blocking_notifier_call_chain(&reboot_notifier_list, @@ -425,10 +405,15 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user kernel_restart(buffer); break; +#ifdef CONFIG_KEXEC case LINUX_REBOOT_CMD_KEXEC: - kernel_kexec(); - unlock_kernel(); - return -EINVAL; + { + int ret; + ret = kernel_kexec(); + unlock_kernel(); + return ret; + } +#endif #ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND: -- cgit v1.2.3 From 89081d17f7bb81d89fa1aa9b70f821c5cf4d39e9 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 25 Jul 2008 19:45:10 -0700 Subject: kexec jump: save/restore device state This patch implements devices state save/restore before after kexec. This patch together with features in kexec_jump patch can be used for following: - A simple hibernation implementation without ACPI support. You can kexec a hibernating kernel, save the memory image of original system and shutdown the system. When resuming, you restore the memory image of original system via ordinary kexec load then jump back. - Kernel/system debug through making system snapshot. You can make system snapshot, jump back, do some thing and make another system snapshot. - Cooperative multi-kernel/system. With kexec jump, you can switch between several kernels/systems quickly without boot process except the first time. This appears like swap a whole kernel/system out/in. - A general method to call program in physical mode (paging turning off). This can be used to invoke BIOS code under Linux. The following user-space tools can be used with kexec jump: - kexec-tools needs to be patched to support kexec jump. The patches and the precompiled kexec can be download from the following URL: source: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-src_git_kh10.tar.bz2 patches: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-patches_git_kh10.tar.bz2 binary: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec_git_kh10 - makedumpfile with patches are used as memory image saving tool, it can exclude free pages from original kernel memory image file. The patches and the precompiled makedumpfile can be download from the following URL: source: http://khibernation.sourceforge.net/download/release_v10/makedumpfile/makedumpfile-src_cvs_kh10.tar.bz2 patches: http://khibernation.sourceforge.net/download/release_v10/makedumpfile/makedumpfile-patches_cvs_kh10.tar.bz2 binary: http://khibernation.sourceforge.net/download/release_v10/makedumpfile/makedumpfile_cvs_kh10 - An initramfs image can be used as the root file system of kexeced kernel. An initramfs image built with "BuildRoot" can be downloaded from the following URL: initramfs image: http://khibernation.sourceforge.net/download/release_v10/initramfs/rootfs_cvs_kh10.gz All user space tools above are included in the initramfs image. Usage example of simple hibernation: 1. Compile and install patched kernel with following options selected: CONFIG_X86_32=y CONFIG_RELOCATABLE=y CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y CONFIG_PM=y CONFIG_HIBERNATION=y CONFIG_KEXEC_JUMP=y 2. Build an initramfs image contains kexec-tool and makedumpfile, or download the pre-built initramfs image, called rootfs.gz in following text. 3. Prepare a partition to save memory image of original kernel, called hibernating partition in following text. 4. Boot kernel compiled in step 1 (kernel A). 5. In the kernel A, load kernel compiled in step 1 (kernel B) with /sbin/kexec. The shell command line can be as follow: /sbin/kexec --load-preserve-context /boot/bzImage --mem-min=0x100000 --mem-max=0xffffff --initrd=rootfs.gz 6. Boot the kernel B with following shell command line: /sbin/kexec -e 7. The kernel B will boot as normal kexec. In kernel B the memory image of kernel A can be saved into hibernating partition as follow: jump_back_entry=`cat /proc/cmdline | tr ' ' '\n' | grep kexec_jump_back_entry | cut -d '='` echo $jump_back_entry > kexec_jump_back_entry cp /proc/vmcore dump.elf Then you can shutdown the machine as normal. 8. Boot kernel compiled in step 1 (kernel C). Use the rootfs.gz as root file system. 9. In kernel C, load the memory image of kernel A as follow: /sbin/kexec -l --args-none --entry=`cat kexec_jump_back_entry` dump.elf 10. Jump back to the kernel A as follow: /sbin/kexec -e Then, kernel A is resumed. Implementation point: To support jumping between two kernels, before jumping to (executing) the new kernel and jumping back to the original kernel, the devices are put into quiescent state, and the state of devices and CPU is saved. After jumping back from kexeced kernel and jumping to the new kernel, the state of devices and CPU are restored accordingly. The devices/CPU state save/restore code of software suspend is called to implement corresponding function. Known issues: - Because the segment number supported by sys_kexec_load is limited, hibernation image with many segments may not be load. This is planned to be eliminated by adding a new flag to sys_kexec_load to make a image can be loaded with multiple sys_kexec_load invoking. Now, only the i386 architecture is supported. Signed-off-by: Huang Ying Acked-by: Vivek Goyal Cc: "Eric W. Biederman" Cc: Pavel Machek Cc: Nigel Cunningham Cc: "Rafael J. Wysocki" Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/Kconfig | 5 +++-- arch/x86/kernel/machine_kexec_32.c | 12 ++++++++++++ include/linux/suspend.h | 2 ++ kernel/kexec.c | 39 ++++++++++++++++++++++++++++++++++++++ kernel/power/power.h | 2 -- 5 files changed, 56 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7ecb679f0130..6b2debfabddc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1282,9 +1282,10 @@ config CRASH_DUMP config KEXEC_JUMP bool "kexec jump (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on KEXEC && PM_SLEEP && X86_32 + depends on KEXEC && HIBERNATION && X86_32 help - Invoke code in physical address mode via KEXEC + Jump between original kernel and kexeced kernel and invoke + code in physical address mode via KEXEC config PHYSICAL_START hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP) diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index 2b67609d0a1c..9fe478d98406 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -125,6 +125,18 @@ void machine_kexec(struct kimage *image) /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); + if (image->preserve_context) { +#ifdef CONFIG_X86_IO_APIC + /* We need to put APICs in legacy mode so that we can + * get timer interrupts in second kernel. kexec/kdump + * paths already have calls to disable_IO_APIC() in + * one form or other. kexec jump path also need + * one. + */ + disable_IO_APIC(); +#endif + } + control_page = page_address(image->control_code_page); memcpy(control_page, relocate_kernel, PAGE_SIZE/2); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index e8e69159af71..c63435095970 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -278,4 +278,6 @@ static inline void register_nosave_region_late(unsigned long b, unsigned long e) } #endif +extern struct mutex pm_mutex; + #endif /* _LINUX_SUSPEND_H */ diff --git a/kernel/kexec.c b/kernel/kexec.c index a0d920915b38..c8a4370e2a34 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -26,6 +26,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -1441,7 +1445,31 @@ int kernel_kexec(void) if (kexec_image->preserve_context) { #ifdef CONFIG_KEXEC_JUMP + mutex_lock(&pm_mutex); + pm_prepare_console(); + error = freeze_processes(); + if (error) { + error = -EBUSY; + goto Restore_console; + } + suspend_console(); + error = device_suspend(PMSG_FREEZE); + if (error) + goto Resume_console; + error = disable_nonboot_cpus(); + if (error) + goto Resume_devices; local_irq_disable(); + /* At this point, device_suspend() has been called, + * but *not* device_power_down(). We *must* + * device_power_down() now. Otherwise, drivers for + * some devices (e.g. interrupt controllers) become + * desynchronized with the actual state of the + * hardware at resume time, and evil weirdness ensues. + */ + error = device_power_down(PMSG_FREEZE); + if (error) + goto Enable_irqs; save_processor_state(); #endif } else { @@ -1459,7 +1487,18 @@ int kernel_kexec(void) if (kexec_image->preserve_context) { #ifdef CONFIG_KEXEC_JUMP restore_processor_state(); + device_power_up(PMSG_RESTORE); + Enable_irqs: local_irq_enable(); + enable_nonboot_cpus(); + Resume_devices: + device_resume(PMSG_RESTORE); + Resume_console: + resume_console(); + thaw_processes(); + Restore_console: + pm_restore_console(); + mutex_unlock(&pm_mutex); #endif } diff --git a/kernel/power/power.h b/kernel/power/power.h index 700f44ec8406..acc0c101dbd5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -53,8 +53,6 @@ extern int hibernation_platform_enter(void); extern int pfn_is_nosave(unsigned long); -extern struct mutex pm_mutex; - #define power_attr(_name) \ static struct kobj_attribute _name##_attr = { \ .attr = { \ -- cgit v1.2.3 From c2147a5092cfe13dbf3210e54e8a622015edeecc Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Fri, 25 Jul 2008 19:45:11 -0700 Subject: Better interface for hooking early initcalls Added early initcall (pre-SMP) support, using an identical interface to that of regular initcalls. Functions called from do_pre_smp_initcalls() could be converted to use this cleaner interface. This is required by CPU hotplug, because early users have to register notifiers before going SMP. One such CPU hotplug user is the relay interface with buffer-only channels, which needs to register such a notifier, to be usable in early code. This in turn is used by kmemtrace. Signed-off-by: Eduard - Gabriel Munteanu Cc: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/vmlinux.lds.h | 2 ++ include/linux/init.h | 7 +++++++ init/main.c | 13 +++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 729f6b0a60e9..9cd44b162ba1 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -359,6 +359,8 @@ } #define INITCALLS \ + *(.initcallearly.init) \ + __early_initcall_end = .; \ *(.initcall0.init) \ *(.initcall0s.init) \ *(.initcall1.init) \ diff --git a/include/linux/init.h b/include/linux/init.h index 42ae95411a93..11b84e106053 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -169,6 +169,13 @@ extern void (*late_time_init)(void); static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn +/* + * Early initcalls run before initializing SMP. + * + * Only for built-in code, not modules. + */ +#define early_initcall(fn) __define_initcall("early",fn,early) + /* * A "pure" initcall has no dependencies on anything else, and purely * initializes variables that couldn't be statically initialized. diff --git a/init/main.c b/init/main.c index 0604cbcaf1e4..b6fec08dbbef 100644 --- a/init/main.c +++ b/init/main.c @@ -743,13 +743,13 @@ static void __init do_one_initcall(initcall_t fn) } -extern initcall_t __initcall_start[], __initcall_end[]; +extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[]; static void __init do_initcalls(void) { initcall_t *call; - for (call = __initcall_start; call < __initcall_end; call++) + for (call = __early_initcall_end; call < __initcall_end; call++) do_one_initcall(*call); /* Make sure there is no pending stuff from the initcall sequence */ @@ -783,6 +783,14 @@ static int __init nosoftlockup_setup(char *str) } __setup("nosoftlockup", nosoftlockup_setup); +static void __init __do_pre_smp_initcalls(void) +{ + initcall_t *call; + + for (call = __initcall_start; call < __early_initcall_end; call++) + do_one_initcall(*call); +} + static void __init do_pre_smp_initcalls(void) { extern int spawn_ksoftirqd(void); @@ -865,6 +873,7 @@ static int __init kernel_init(void * unused) smp_prepare_cpus(setup_max_cpus); + __do_pre_smp_initcalls(); do_pre_smp_initcalls(); smp_init(); -- cgit v1.2.3 From 7babe8db99d305340cf4828ce1f5a1481d5622ef Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Fri, 25 Jul 2008 19:45:11 -0700 Subject: Full conversion to early_initcall() interface, remove old interface A previous patch added the early_initcall(), to allow a cleaner hooking of pre-SMP initcalls. Now we remove the older interface, converting all existing users to the new one. [akpm@linux-foundation.org: cleanups] [akpm@linux-foundation.org: build fix] [kosaki.motohiro@jp.fujitsu.com: warning fix] [kosaki.motohiro@jp.fujitsu.com: warning fix] Signed-off-by: Eduard - Gabriel Munteanu Cc: Tom Zanussi Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 9 --------- include/linux/smp.h | 5 ----- init/main.c | 23 +---------------------- kernel/sched.c | 5 ++++- kernel/smp.c | 4 +++- kernel/softirq.c | 3 ++- kernel/softlockup.c | 25 ++++++++++++++++++++++--- 7 files changed, 32 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 3260a5c42b91..adb8077dc463 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -292,7 +292,6 @@ extern void sched_show_task(struct task_struct *p); #ifdef CONFIG_DETECT_SOFTLOCKUP extern void softlockup_tick(void); -extern void spawn_softlockup_task(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); extern unsigned int softlockup_panic; @@ -2222,14 +2221,6 @@ static inline void inc_syscw(struct task_struct *tsk) } #endif -#ifdef CONFIG_SMP -void migration_init(void); -#else -static inline void migration_init(void) -{ -} -#endif - #ifndef TASK_SIZE_OF #define TASK_SIZE_OF(tsk) TASK_SIZE #endif diff --git a/include/linux/smp.h b/include/linux/smp.h index 48262f86c969..66484d4a8459 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -74,15 +74,10 @@ void __smp_call_function_single(int cpuid, struct call_single_data *data); #ifdef CONFIG_USE_GENERIC_SMP_HELPERS void generic_smp_call_function_single_interrupt(void); void generic_smp_call_function_interrupt(void); -void init_call_single_data(void); void ipi_call_lock(void); void ipi_call_unlock(void); void ipi_call_lock_irq(void); void ipi_call_unlock_irq(void); -#else -static inline void init_call_single_data(void) -{ -} #endif /* diff --git a/init/main.c b/init/main.c index b6fec08dbbef..20fdc9884b77 100644 --- a/init/main.c +++ b/init/main.c @@ -774,16 +774,7 @@ static void __init do_basic_setup(void) do_initcalls(); } -static int __initdata nosoftlockup; - -static int __init nosoftlockup_setup(char *str) -{ - nosoftlockup = 1; - return 1; -} -__setup("nosoftlockup", nosoftlockup_setup); - -static void __init __do_pre_smp_initcalls(void) +static void __init do_pre_smp_initcalls(void) { initcall_t *call; @@ -791,17 +782,6 @@ static void __init __do_pre_smp_initcalls(void) do_one_initcall(*call); } -static void __init do_pre_smp_initcalls(void) -{ - extern int spawn_ksoftirqd(void); - - init_call_single_data(); - migration_init(); - spawn_ksoftirqd(); - if (!nosoftlockup) - spawn_softlockup_task(); -} - static void run_init_process(char *init_filename) { argv_init[0] = init_filename; @@ -873,7 +853,6 @@ static int __init kernel_init(void * unused) smp_prepare_cpus(setup_max_cpus); - __do_pre_smp_initcalls(); do_pre_smp_initcalls(); smp_init(); diff --git a/kernel/sched.c b/kernel/sched.c index 0047bd9b96aa..fde1a1026359 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -6389,7 +6389,7 @@ static struct notifier_block __cpuinitdata migration_notifier = { .priority = 10 }; -void __init migration_init(void) +static int __init migration_init(void) { void *cpu = (void *)(long)smp_processor_id(); int err; @@ -6399,7 +6399,10 @@ void __init migration_init(void) BUG_ON(err == NOTIFY_BAD); migration_call(&migration_notifier, CPU_ONLINE, cpu); register_cpu_notifier(&migration_notifier); + + return err; } +early_initcall(migration_init); #endif #ifdef CONFIG_SMP diff --git a/kernel/smp.c b/kernel/smp.c index 462c785ca1ee..96fc7c0edc59 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -33,7 +33,7 @@ struct call_single_queue { spinlock_t lock; }; -void __cpuinit init_call_single_data(void) +static int __cpuinit init_call_single_data(void) { int i; @@ -43,7 +43,9 @@ void __cpuinit init_call_single_data(void) spin_lock_init(&q->lock); INIT_LIST_HEAD(&q->list); } + return 0; } +early_initcall(init_call_single_data); static void csd_flag_wait(struct call_single_data *data) { diff --git a/kernel/softirq.c b/kernel/softirq.c index f6b03d56c2bf..c506f266a6b9 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -630,7 +630,7 @@ static struct notifier_block __cpuinitdata cpu_nfb = { .notifier_call = cpu_callback }; -__init int spawn_ksoftirqd(void) +static __init int spawn_ksoftirqd(void) { void *cpu = (void *)(long)smp_processor_id(); int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); @@ -640,6 +640,7 @@ __init int spawn_ksoftirqd(void) register_cpu_notifier(&cpu_nfb); return 0; } +early_initcall(spawn_ksoftirqd); #ifdef CONFIG_SMP /* diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 7bd8d1aadd5d..b75b492fbfcf 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -338,14 +338,33 @@ static struct notifier_block __cpuinitdata cpu_nfb = { .notifier_call = cpu_callback }; -__init void spawn_softlockup_task(void) +static int __initdata nosoftlockup; + +static int __init nosoftlockup_setup(char *str) +{ + nosoftlockup = 1; + return 1; +} +__setup("nosoftlockup", nosoftlockup_setup); + +static int __init spawn_softlockup_task(void) { void *cpu = (void *)(long)smp_processor_id(); - int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); + int err; - BUG_ON(err == NOTIFY_BAD); + if (nosoftlockup) + return 0; + + err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); + if (err == NOTIFY_BAD) { + BUG(); + return 1; + } cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); register_cpu_notifier(&cpu_nfb); atomic_notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; } +early_initcall(spawn_softlockup_task); -- cgit v1.2.3 From 20d8b67c06fa5e74f44e80b0a0fd68c8327f7c6a Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Fri, 25 Jul 2008 19:45:12 -0700 Subject: relay: add buffer-only channels; useful for early logging Allows one to create and use a channel with no associated files. Files can be initialized later. This is useful in scenarios such as logging in early code, before VFS is up. Therefore, such channels can be created and used as soon as kmem_cache_init() completed. This is needed by kmemtrace to do tracing in early kernel code. [kosaki.motohiro@jp.fujitsu.com: build fix] Signed-off-by: Eduard - Gabriel Munteanu Cc: Tom Zanussi Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/relay.txt | 10 +++ include/linux/relay.h | 5 ++ kernel/relay.c | 170 ++++++++++++++++++++++++++++++------ 3 files changed, 156 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/relay.txt b/Documentation/filesystems/relay.txt index 094f2d2f38b1..510b722667ac 100644 --- a/Documentation/filesystems/relay.txt +++ b/Documentation/filesystems/relay.txt @@ -294,6 +294,16 @@ user-defined data with a channel, and is immediately available (including in create_buf_file()) via chan->private_data or buf->chan->private_data. +Buffer-only channels +-------------------- + +These channels have no files associated and can be created with +relay_open(NULL, NULL, ...). Such channels are useful in scenarios such +as when doing early tracing in the kernel, before the VFS is up. In these +cases, one may open a buffer-only channel and then call +relay_late_setup_files() when the kernel is ready to handle files, +to expose the buffered data to the userspace. + Channel 'modes' --------------- diff --git a/include/linux/relay.h b/include/linux/relay.h index 6cd8c4425fc7..953fc055e875 100644 --- a/include/linux/relay.h +++ b/include/linux/relay.h @@ -48,6 +48,7 @@ struct rchan_buf size_t *padding; /* padding counts per sub-buffer */ size_t prev_padding; /* temporary variable */ size_t bytes_consumed; /* bytes consumed in cur read subbuf */ + size_t early_bytes; /* bytes consumed before VFS inited */ unsigned int cpu; /* this buf's cpu */ } ____cacheline_aligned; @@ -68,6 +69,7 @@ struct rchan int is_global; /* One global buffer ? */ struct list_head list; /* for channel list */ struct dentry *parent; /* parent dentry passed to open */ + int has_base_filename; /* has a filename associated? */ char base_filename[NAME_MAX]; /* saved base filename */ }; @@ -169,6 +171,9 @@ struct rchan *relay_open(const char *base_filename, size_t n_subbufs, struct rchan_callbacks *cb, void *private_data); +extern int relay_late_setup_files(struct rchan *chan, + const char *base_filename, + struct dentry *parent); extern void relay_close(struct rchan *chan); extern void relay_flush(struct rchan *chan); extern void relay_subbufs_consumed(struct rchan *chan, diff --git a/kernel/relay.c b/kernel/relay.c index 7de644cdec43..04006ef970b8 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -407,6 +407,35 @@ void relay_reset(struct rchan *chan) } EXPORT_SYMBOL_GPL(relay_reset); +static inline void relay_set_buf_dentry(struct rchan_buf *buf, + struct dentry *dentry) +{ + buf->dentry = dentry; + buf->dentry->d_inode->i_size = buf->early_bytes; +} + +static struct dentry *relay_create_buf_file(struct rchan *chan, + struct rchan_buf *buf, + unsigned int cpu) +{ + struct dentry *dentry; + char *tmpname; + + tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL); + if (!tmpname) + return NULL; + snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu); + + /* Create file in fs */ + dentry = chan->cb->create_buf_file(tmpname, chan->parent, + S_IRUSR, buf, + &chan->is_global); + + kfree(tmpname); + + return dentry; +} + /* * relay_open_buf - create a new relay channel buffer * @@ -416,45 +445,34 @@ static struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu) { struct rchan_buf *buf = NULL; struct dentry *dentry; - char *tmpname; if (chan->is_global) return chan->buf[0]; - tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL); - if (!tmpname) - goto end; - snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu); - buf = relay_create_buf(chan); if (!buf) - goto free_name; + return NULL; + + if (chan->has_base_filename) { + dentry = relay_create_buf_file(chan, buf, cpu); + if (!dentry) + goto free_buf; + relay_set_buf_dentry(buf, dentry); + } buf->cpu = cpu; __relay_reset(buf, 1); - /* Create file in fs */ - dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR, - buf, &chan->is_global); - if (!dentry) - goto free_buf; - - buf->dentry = dentry; - if(chan->is_global) { chan->buf[0] = buf; buf->cpu = 0; } - goto free_name; + return buf; free_buf: relay_destroy_buf(buf); - buf = NULL; -free_name: - kfree(tmpname); -end: - return buf; + return NULL; } /** @@ -537,8 +555,8 @@ static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, /** * relay_open - create a new relay channel - * @base_filename: base name of files to create - * @parent: dentry of parent directory, %NULL for root directory + * @base_filename: base name of files to create, %NULL for buffering only + * @parent: dentry of parent directory, %NULL for root directory or buffer * @subbuf_size: size of sub-buffers * @n_subbufs: number of sub-buffers * @cb: client callback functions @@ -560,8 +578,6 @@ struct rchan *relay_open(const char *base_filename, { unsigned int i; struct rchan *chan; - if (!base_filename) - return NULL; if (!(subbuf_size && n_subbufs)) return NULL; @@ -576,7 +592,10 @@ struct rchan *relay_open(const char *base_filename, chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); chan->parent = parent; chan->private_data = private_data; - strlcpy(chan->base_filename, base_filename, NAME_MAX); + if (base_filename) { + chan->has_base_filename = 1; + strlcpy(chan->base_filename, base_filename, NAME_MAX); + } setup_callbacks(chan, cb); kref_init(&chan->kref); @@ -604,6 +623,94 @@ free_bufs: } EXPORT_SYMBOL_GPL(relay_open); +struct rchan_percpu_buf_dispatcher { + struct rchan_buf *buf; + struct dentry *dentry; +}; + +/* Called in atomic context. */ +static void __relay_set_buf_dentry(void *info) +{ + struct rchan_percpu_buf_dispatcher *p = info; + + relay_set_buf_dentry(p->buf, p->dentry); +} + +/** + * relay_late_setup_files - triggers file creation + * @chan: channel to operate on + * @base_filename: base name of files to create + * @parent: dentry of parent directory, %NULL for root directory + * + * Returns 0 if successful, non-zero otherwise. + * + * Use to setup files for a previously buffer-only channel. + * Useful to do early tracing in kernel, before VFS is up, for example. + */ +int relay_late_setup_files(struct rchan *chan, + const char *base_filename, + struct dentry *parent) +{ + int err = 0; + unsigned int i, curr_cpu; + unsigned long flags; + struct dentry *dentry; + struct rchan_percpu_buf_dispatcher disp; + + if (!chan || !base_filename) + return -EINVAL; + + strlcpy(chan->base_filename, base_filename, NAME_MAX); + + mutex_lock(&relay_channels_mutex); + /* Is chan already set up? */ + if (unlikely(chan->has_base_filename)) + return -EEXIST; + chan->has_base_filename = 1; + chan->parent = parent; + curr_cpu = get_cpu(); + /* + * The CPU hotplug notifier ran before us and created buffers with + * no files associated. So it's safe to call relay_setup_buf_file() + * on all currently online CPUs. + */ + for_each_online_cpu(i) { + if (unlikely(!chan->buf[i])) { + printk(KERN_ERR "relay_late_setup_files: CPU %u " + "has no buffer, it must have!\n", i); + BUG(); + err = -EINVAL; + break; + } + + dentry = relay_create_buf_file(chan, chan->buf[i], i); + if (unlikely(!dentry)) { + err = -EINVAL; + break; + } + + if (curr_cpu == i) { + local_irq_save(flags); + relay_set_buf_dentry(chan->buf[i], dentry); + local_irq_restore(flags); + } else { + disp.buf = chan->buf[i]; + disp.dentry = dentry; + smp_mb(); + /* relay_channels_mutex must be held, so wait. */ + err = smp_call_function_single(i, + __relay_set_buf_dentry, + &disp, 1); + } + if (unlikely(err)) + break; + } + put_cpu(); + mutex_unlock(&relay_channels_mutex); + + return err; +} + /** * relay_switch_subbuf - switch to a new sub-buffer * @buf: channel buffer @@ -627,8 +734,13 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length) old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; buf->padding[old_subbuf] = buf->prev_padding; buf->subbufs_produced++; - buf->dentry->d_inode->i_size += buf->chan->subbuf_size - - buf->padding[old_subbuf]; + if (buf->dentry) + buf->dentry->d_inode->i_size += + buf->chan->subbuf_size - + buf->padding[old_subbuf]; + else + buf->early_bytes += buf->chan->subbuf_size - + buf->padding[old_subbuf]; smp_mb(); if (waitqueue_active(&buf->read_wait)) /* @@ -1237,4 +1349,4 @@ static __init int relay_init(void) return 0; } -module_init(relay_init); +early_initcall(relay_init); -- cgit v1.2.3 From 080ccd4573607a930367c2128fc709814b2ade5d Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Fri, 25 Jul 2008 19:45:13 -0700 Subject: include/linux/aio.h: removed duplicated include Removed duplicated include in include/linux/aio.h Signed-off-by: Huang Weiyi Signed-off-by: Benjamin LaHaise Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/aio.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/aio.h b/include/linux/aio.h index b51ddd28444e..09b276c35227 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -7,7 +7,6 @@ #include #include -#include #define AIO_MAXSEGS 4 #define AIO_KIOGRP_NR_ATOMIC 8 -- cgit v1.2.3 From 21cc199baa815d7b3f1ace4be20b9558cbddc00f Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:22 -0700 Subject: mm: introduce get_user_pages_fast Introduce a new get_user_pages_fast mm API, which is basically a get_user_pages with a less general API (but still tends to be suited to the common case): - task and mm are always current and current->mm - force is always 0 - pages is always non-NULL - don't pass back vmas This restricted API can be implemented in a much more scalable way on many architectures when the ptes are present, by walking the page tables locklessly (no mmap_sem or page table locks). When the ptes are not populated, get_user_pages_fast() could be slower. This is implemented locklessly on x86, and used in some key direct IO call sites, in later patches, which provides nearly 10% performance improvement on a threaded database workload. Lots of other code could use this too, depending on use cases (eg. grep drivers/). And it might inspire some new and clever ways to use it. [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Nick Piggin Cc: Dave Kleikamp Cc: Andy Whitcroft Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Andi Kleen Cc: Dave Kleikamp Cc: Badari Pulavarty Cc: Zach Brown Cc: Jens Axboe Reviewed-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index d87a5a5fe87d..f3fd70d6029f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -833,6 +833,39 @@ extern int mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, unsigned long start, unsigned long end, unsigned long newflags); +#ifdef CONFIG_HAVE_GET_USER_PAGES_FAST +/* + * get_user_pages_fast provides equivalent functionality to get_user_pages, + * operating on current and current->mm (force=0 and doesn't return any vmas). + * + * get_user_pages_fast may take mmap_sem and page tables, so no assumptions + * can be made about locking. get_user_pages_fast is to be implemented in a + * way that is advantageous (vs get_user_pages()) when the user memory area is + * already faulted in and present in ptes. However if the pages have to be + * faulted in, it may turn out to be slightly slower). + */ +int get_user_pages_fast(unsigned long start, int nr_pages, int write, + struct page **pages); + +#else +/* + * Should probably be moved to asm-generic, and architectures can include it if + * they don't implement their own get_user_pages_fast. + */ +#define get_user_pages_fast(start, nr_pages, write, pages) \ +({ \ + struct mm_struct *mm = current->mm; \ + int ret; \ + \ + down_read(&mm->mmap_sem); \ + ret = get_user_pages(current, mm, start, nr_pages, \ + write, 0, pages, NULL); \ + up_read(&mm->mmap_sem); \ + \ + ret; \ +}) +#endif + /* * A callback you can register to apply pressure to ageable caches. * -- cgit v1.2.3 From 47feff2c8eefe85099f87c43d3096855f0085ca0 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:29 -0700 Subject: radix-tree: add gang_lookup_slot, gang_lookup_slot_tag Introduce gang_lookup_slot() and gang_lookup_slot_tag() functions, which are used by lockless pagecache. Signed-off-by: Nick Piggin Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Hugh Dickins Cc: "Paul E. McKenney" Reviewed-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/radix-tree.h | 12 ++- lib/radix-tree.c | 178 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 166 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index b8ce2b444bb5..a916c6660dfa 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -99,12 +99,15 @@ do { \ * * The notable exceptions to this rule are the following functions: * radix_tree_lookup + * radix_tree_lookup_slot * radix_tree_tag_get * radix_tree_gang_lookup + * radix_tree_gang_lookup_slot * radix_tree_gang_lookup_tag + * radix_tree_gang_lookup_tag_slot * radix_tree_tagged * - * The first 4 functions are able to be called locklessly, using RCU. The + * The first 7 functions are able to be called locklessly, using RCU. The * caller must ensure calls to these functions are made within rcu_read_lock() * regions. Other readers (lock-free or otherwise) and modifications may be * running concurrently. @@ -159,6 +162,9 @@ void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items); +unsigned int +radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items); unsigned long radix_tree_next_hole(struct radix_tree_root *root, unsigned long index, unsigned long max_scan); int radix_tree_preload(gfp_t gfp_mask); @@ -173,6 +179,10 @@ unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag); +unsigned int +radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items, + unsigned int tag); int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); static inline void radix_tree_preload_end(void) diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 56ec21a7f73d..9c4f1ffa2864 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -359,18 +359,17 @@ EXPORT_SYMBOL(radix_tree_insert); * Returns: the slot corresponding to the position @index in the * radix tree @root. This is useful for update-if-exists operations. * - * This function cannot be called under rcu_read_lock, it must be - * excluded from writers, as must the returned slot for subsequent - * use by radix_tree_deref_slot() and radix_tree_replace slot. - * Caller must hold tree write locked across slot lookup and - * replace. + * This function can be called under rcu_read_lock iff the slot is not + * modified by radix_tree_replace_slot, otherwise it must be called + * exclusive from other writers. Any dereference of the slot must be done + * using radix_tree_deref_slot. */ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) { unsigned int height, shift; struct radix_tree_node *node, **slot; - node = root->rnode; + node = rcu_dereference(root->rnode); if (node == NULL) return NULL; @@ -390,7 +389,7 @@ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) do { slot = (struct radix_tree_node **) (node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK)); - node = *slot; + node = rcu_dereference(*slot); if (node == NULL) return NULL; @@ -667,7 +666,7 @@ unsigned long radix_tree_next_hole(struct radix_tree_root *root, EXPORT_SYMBOL(radix_tree_next_hole); static unsigned int -__lookup(struct radix_tree_node *slot, void **results, unsigned long index, +__lookup(struct radix_tree_node *slot, void ***results, unsigned long index, unsigned int max_items, unsigned long *next_index) { unsigned int nr_found = 0; @@ -701,11 +700,9 @@ __lookup(struct radix_tree_node *slot, void **results, unsigned long index, /* Bottom level: grab some items */ for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) { - struct radix_tree_node *node; index++; - node = slot->slots[i]; - if (node) { - results[nr_found++] = rcu_dereference(node); + if (slot->slots[i]) { + results[nr_found++] = &(slot->slots[i]); if (nr_found == max_items) goto out; } @@ -759,13 +756,22 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, ret = 0; while (ret < max_items) { - unsigned int nr_found; + unsigned int nr_found, slots_found, i; unsigned long next_index; /* Index of next search */ if (cur_index > max_index) break; - nr_found = __lookup(node, results + ret, cur_index, + slots_found = __lookup(node, (void ***)results + ret, cur_index, max_items - ret, &next_index); + nr_found = 0; + for (i = 0; i < slots_found; i++) { + struct radix_tree_node *slot; + slot = *(((void ***)results)[ret + i]); + if (!slot) + continue; + results[ret + nr_found] = rcu_dereference(slot); + nr_found++; + } ret += nr_found; if (next_index == 0) break; @@ -776,12 +782,71 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, } EXPORT_SYMBOL(radix_tree_gang_lookup); +/** + * radix_tree_gang_lookup_slot - perform multiple slot lookup on radix tree + * @root: radix tree root + * @results: where the results of the lookup are placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * + * Performs an index-ascending scan of the tree for present items. Places + * their slots at *@results and returns the number of items which were + * placed at *@results. + * + * The implementation is naive. + * + * Like radix_tree_gang_lookup as far as RCU and locking goes. Slots must + * be dereferenced with radix_tree_deref_slot, and if using only RCU + * protection, radix_tree_deref_slot may fail requiring a retry. + */ +unsigned int +radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items) +{ + unsigned long max_index; + struct radix_tree_node *node; + unsigned long cur_index = first_index; + unsigned int ret; + + node = rcu_dereference(root->rnode); + if (!node) + return 0; + + if (!radix_tree_is_indirect_ptr(node)) { + if (first_index > 0) + return 0; + results[0] = (void **)&root->rnode; + return 1; + } + node = radix_tree_indirect_to_ptr(node); + + max_index = radix_tree_maxindex(node->height); + + ret = 0; + while (ret < max_items) { + unsigned int slots_found; + unsigned long next_index; /* Index of next search */ + + if (cur_index > max_index) + break; + slots_found = __lookup(node, results + ret, cur_index, + max_items - ret, &next_index); + ret += slots_found; + if (next_index == 0) + break; + cur_index = next_index; + } + + return ret; +} +EXPORT_SYMBOL(radix_tree_gang_lookup_slot); + /* * FIXME: the two tag_get()s here should use find_next_bit() instead of * open-coding the search. */ static unsigned int -__lookup_tag(struct radix_tree_node *slot, void **results, unsigned long index, +__lookup_tag(struct radix_tree_node *slot, void ***results, unsigned long index, unsigned int max_items, unsigned long *next_index, unsigned int tag) { unsigned int nr_found = 0; @@ -811,11 +876,9 @@ __lookup_tag(struct radix_tree_node *slot, void **results, unsigned long index, unsigned long j = index & RADIX_TREE_MAP_MASK; for ( ; j < RADIX_TREE_MAP_SIZE; j++) { - struct radix_tree_node *node; index++; if (!tag_get(slot, tag, j)) continue; - node = slot->slots[j]; /* * Even though the tag was found set, we need to * recheck that we have a non-NULL node, because @@ -826,9 +889,8 @@ __lookup_tag(struct radix_tree_node *slot, void **results, unsigned long index, * lookup ->slots[x] without a lock (ie. can't * rely on its value remaining the same). */ - if (node) { - node = rcu_dereference(node); - results[nr_found++] = node; + if (slot->slots[j]) { + results[nr_found++] = &(slot->slots[j]); if (nr_found == max_items) goto out; } @@ -887,13 +949,22 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, ret = 0; while (ret < max_items) { - unsigned int nr_found; + unsigned int nr_found, slots_found, i; unsigned long next_index; /* Index of next search */ if (cur_index > max_index) break; - nr_found = __lookup_tag(node, results + ret, cur_index, - max_items - ret, &next_index, tag); + slots_found = __lookup_tag(node, (void ***)results + ret, + cur_index, max_items - ret, &next_index, tag); + nr_found = 0; + for (i = 0; i < slots_found; i++) { + struct radix_tree_node *slot; + slot = *(((void ***)results)[ret + i]); + if (!slot) + continue; + results[ret + nr_found] = rcu_dereference(slot); + nr_found++; + } ret += nr_found; if (next_index == 0) break; @@ -904,6 +975,67 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, } EXPORT_SYMBOL(radix_tree_gang_lookup_tag); +/** + * radix_tree_gang_lookup_tag_slot - perform multiple slot lookup on a + * radix tree based on a tag + * @root: radix tree root + * @results: where the results of the lookup are placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * @tag: the tag index (< RADIX_TREE_MAX_TAGS) + * + * Performs an index-ascending scan of the tree for present items which + * have the tag indexed by @tag set. Places the slots at *@results and + * returns the number of slots which were placed at *@results. + */ +unsigned int +radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items, + unsigned int tag) +{ + struct radix_tree_node *node; + unsigned long max_index; + unsigned long cur_index = first_index; + unsigned int ret; + + /* check the root's tag bit */ + if (!root_tag_get(root, tag)) + return 0; + + node = rcu_dereference(root->rnode); + if (!node) + return 0; + + if (!radix_tree_is_indirect_ptr(node)) { + if (first_index > 0) + return 0; + results[0] = (void **)&root->rnode; + return 1; + } + node = radix_tree_indirect_to_ptr(node); + + max_index = radix_tree_maxindex(node->height); + + ret = 0; + while (ret < max_items) { + unsigned int slots_found; + unsigned long next_index; /* Index of next search */ + + if (cur_index > max_index) + break; + slots_found = __lookup_tag(node, results + ret, + cur_index, max_items - ret, &next_index, tag); + ret += slots_found; + if (next_index == 0) + break; + cur_index = next_index; + } + + return ret; +} +EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot); + + /** * radix_tree_shrink - shrink height of a radix tree to minimal * @root radix tree root -- cgit v1.2.3 From e286781d5f2e9c846e012a39653a166e9d31777d Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:30 -0700 Subject: mm: speculative page references If we can be sure that elevating the page_count on a pagecache page will pin it, we can speculatively run this operation, and subsequently check to see if we hit the right page rather than relying on holding a lock or otherwise pinning a reference to the page. This can be done if get_page/put_page behaves consistently throughout the whole tree (ie. if we "get" the page after it has been used for something else, we must be able to free it with a put_page). Actually, there is a period where the count behaves differently: when the page is free or if it is a constituent page of a compound page. We need an atomic_inc_not_zero operation to ensure we don't try to grab the page in either case. This patch introduces the core locking protocol to the pagecache (ie. adds page_cache_get_speculative, and tweaks some update-side code to make it work). Thanks to Hugh for pointing out an improvement to the algorithm setting page_count to zero when we have control of all references, in order to hold off speculative getters. [kamezawa.hiroyu@jp.fujitsu.com: fix migration_entry_wait()] [hugh@veritas.com: fix add_to_page_cache] [akpm@linux-foundation.org: repair a comment] Signed-off-by: Nick Piggin Cc: Jeff Garzik Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Hugh Dickins Cc: "Paul E. McKenney" Reviewed-by: Peter Zijlstra Signed-off-by: Daisuke Nishimura Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: KOSAKI Motohiro Signed-off-by: Hugh Dickins Acked-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/cassini.c | 12 ++++++ include/linux/pagemap.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++- mm/filemap.c | 32 ++++++++------ mm/migrate.c | 20 ++++++++- mm/shmem.c | 6 +-- mm/swap_state.c | 17 +++++--- mm/vmscan.c | 74 +++++++++++++++++++++++--------- 7 files changed, 227 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 83768df27806..f1936d51b458 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -576,6 +576,18 @@ static void cas_spare_recover(struct cas *cp, const gfp_t flags) list_for_each_safe(elem, tmp, &list) { cas_page_t *page = list_entry(elem, cas_page_t, list); + /* + * With the lockless pagecache, cassini buffering scheme gets + * slightly less accurate: we might find that a page has an + * elevated reference count here, due to a speculative ref, + * and skip it as in-use. Ideally we would be able to reclaim + * it. However this would be such a rare case, it doesn't + * matter too much as we should pick it up the next time round. + * + * Importantly, if we find that the page has a refcount of 1 + * here (our refcount), then we know it is definitely not inuse + * so we can reuse it. + */ if (page_count(page->buffer) > 1) continue; diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index ee1ec2c7723c..a81d81890422 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -12,6 +12,7 @@ #include #include #include +#include /* for in_interrupt() */ /* * Bits in mapping->flags. The lower __GFP_BITS_SHIFT bits are the page @@ -62,6 +63,98 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) #define page_cache_release(page) put_page(page) void release_pages(struct page **pages, int nr, int cold); +/* + * speculatively take a reference to a page. + * If the page is free (_count == 0), then _count is untouched, and 0 + * is returned. Otherwise, _count is incremented by 1 and 1 is returned. + * + * This function must be called inside the same rcu_read_lock() section as has + * been used to lookup the page in the pagecache radix-tree (or page table): + * this allows allocators to use a synchronize_rcu() to stabilize _count. + * + * Unless an RCU grace period has passed, the count of all pages coming out + * of the allocator must be considered unstable. page_count may return higher + * than expected, and put_page must be able to do the right thing when the + * page has been finished with, no matter what it is subsequently allocated + * for (because put_page is what is used here to drop an invalid speculative + * reference). + * + * This is the interesting part of the lockless pagecache (and lockless + * get_user_pages) locking protocol, where the lookup-side (eg. find_get_page) + * has the following pattern: + * 1. find page in radix tree + * 2. conditionally increment refcount + * 3. check the page is still in pagecache (if no, goto 1) + * + * Remove-side that cares about stability of _count (eg. reclaim) has the + * following (with tree_lock held for write): + * A. atomically check refcount is correct and set it to 0 (atomic_cmpxchg) + * B. remove page from pagecache + * C. free the page + * + * There are 2 critical interleavings that matter: + * - 2 runs before A: in this case, A sees elevated refcount and bails out + * - A runs before 2: in this case, 2 sees zero refcount and retries; + * subsequently, B will complete and 1 will find no page, causing the + * lookup to return NULL. + * + * It is possible that between 1 and 2, the page is removed then the exact same + * page is inserted into the same position in pagecache. That's OK: the + * old find_get_page using tree_lock could equally have run before or after + * such a re-insertion, depending on order that locks are granted. + * + * Lookups racing against pagecache insertion isn't a big problem: either 1 + * will find the page or it will not. Likewise, the old find_get_page could run + * either before the insertion or afterwards, depending on timing. + */ +static inline int page_cache_get_speculative(struct page *page) +{ + VM_BUG_ON(in_interrupt()); + +#if !defined(CONFIG_SMP) && defined(CONFIG_CLASSIC_RCU) +# ifdef CONFIG_PREEMPT + VM_BUG_ON(!in_atomic()); +# endif + /* + * Preempt must be disabled here - we rely on rcu_read_lock doing + * this for us. + * + * Pagecache won't be truncated from interrupt context, so if we have + * found a page in the radix tree here, we have pinned its refcount by + * disabling preempt, and hence no need for the "speculative get" that + * SMP requires. + */ + VM_BUG_ON(page_count(page) == 0); + atomic_inc(&page->_count); + +#else + if (unlikely(!get_page_unless_zero(page))) { + /* + * Either the page has been freed, or will be freed. + * In either case, retry here and the caller should + * do the right thing (see comments above). + */ + return 0; + } +#endif + VM_BUG_ON(PageTail(page)); + + return 1; +} + +static inline int page_freeze_refs(struct page *page, int count) +{ + return likely(atomic_cmpxchg(&page->_count, count, 0) == count); +} + +static inline void page_unfreeze_refs(struct page *page, int count) +{ + VM_BUG_ON(page_count(page) != 0); + VM_BUG_ON(count == 0); + + atomic_set(&page->_count, count); +} + #ifdef CONFIG_NUMA extern struct page *__page_cache_alloc(gfp_t gfp); #else @@ -133,13 +226,29 @@ static inline struct page *read_mapping_page(struct address_space *mapping, return read_cache_page(mapping, index, filler, data); } -int add_to_page_cache(struct page *page, struct address_space *mapping, +int add_to_page_cache_locked(struct page *page, struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); extern void remove_from_page_cache(struct page *page); extern void __remove_from_page_cache(struct page *page); +/* + * Like add_to_page_cache_locked, but used to add newly allocated pages: + * the page is new, so we can just run SetPageLocked() against it. + */ +static inline int add_to_page_cache(struct page *page, + struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) +{ + int error; + + SetPageLocked(page); + error = add_to_page_cache_locked(page, mapping, offset, gfp_mask); + if (unlikely(error)) + ClearPageLocked(page); + return error; +} + /* * Return byte-offset into filesystem object for page. */ diff --git a/mm/filemap.c b/mm/filemap.c index 2d3ec1ffc66e..4e182a9a14c0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -442,39 +442,43 @@ int filemap_write_and_wait_range(struct address_space *mapping, } /** - * add_to_page_cache - add newly allocated pagecache pages + * add_to_page_cache_locked - add a locked page to the pagecache * @page: page to add * @mapping: the page's address_space * @offset: page index * @gfp_mask: page allocation mode * - * This function is used to add newly allocated pagecache pages; - * the page is new, so we can just run SetPageLocked() against it. - * The other page state flags were set by rmqueue(). - * + * This function is used to add a page to the pagecache. It must be locked. * This function does not add the page to the LRU. The caller must do that. */ -int add_to_page_cache(struct page *page, struct address_space *mapping, +int add_to_page_cache_locked(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) { - int error = mem_cgroup_cache_charge(page, current->mm, + int error; + + VM_BUG_ON(!PageLocked(page)); + + error = mem_cgroup_cache_charge(page, current->mm, gfp_mask & ~__GFP_HIGHMEM); if (error) goto out; error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); if (error == 0) { + page_cache_get(page); + page->mapping = mapping; + page->index = offset; + write_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); - if (!error) { - page_cache_get(page); - SetPageLocked(page); - page->mapping = mapping; - page->index = offset; + if (likely(!error)) { mapping->nrpages++; __inc_zone_page_state(page, NR_FILE_PAGES); - } else + } else { + page->mapping = NULL; mem_cgroup_uncharge_cache_page(page); + page_cache_release(page); + } write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); @@ -483,7 +487,7 @@ int add_to_page_cache(struct page *page, struct address_space *mapping, out: return error; } -EXPORT_SYMBOL(add_to_page_cache); +EXPORT_SYMBOL(add_to_page_cache_locked); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) diff --git a/mm/migrate.c b/mm/migrate.c index d8c65a65c61d..3ca6392e82cc 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -285,7 +285,15 @@ void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, page = migration_entry_to_page(entry); - get_page(page); + /* + * Once radix-tree replacement of page migration started, page_count + * *must* be zero. And, we don't want to call wait_on_page_locked() + * against a page without get_page(). + * So, we use get_page_unless_zero(), here. Even failed, page fault + * will occur again. + */ + if (!get_page_unless_zero(page)) + goto out; pte_unmap_unlock(ptep, ptl); wait_on_page_locked(page); put_page(page); @@ -305,6 +313,7 @@ out: static int migrate_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page) { + int expected_count; void **pslot; if (!mapping) { @@ -319,12 +328,18 @@ static int migrate_page_move_mapping(struct address_space *mapping, pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); - if (page_count(page) != 2 + !!PagePrivate(page) || + expected_count = 2 + !!PagePrivate(page); + if (page_count(page) != expected_count || (struct page *)radix_tree_deref_slot(pslot) != page) { write_unlock_irq(&mapping->tree_lock); return -EAGAIN; } + if (!page_freeze_refs(page, expected_count)) { + write_unlock_irq(&mapping->tree_lock); + return -EAGAIN; + } + /* * Now we know that no one else is looking at the page. */ @@ -338,6 +353,7 @@ static int migrate_page_move_mapping(struct address_space *mapping, radix_tree_replace_slot(pslot, newpage); + page_unfreeze_refs(page, expected_count); /* * Drop cache reference from old page. * We know this isn't the last reference. diff --git a/mm/shmem.c b/mm/shmem.c index f92fea94d037..1089092aecaf 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -936,7 +936,7 @@ found: spin_lock(&info->lock); ptr = shmem_swp_entry(info, idx, NULL); if (ptr && ptr->val == entry.val) { - error = add_to_page_cache(page, inode->i_mapping, + error = add_to_page_cache_locked(page, inode->i_mapping, idx, GFP_NOWAIT); /* does mem_cgroup_uncharge_cache_page on error */ } else /* we must compensate for our precharge above */ @@ -1301,8 +1301,8 @@ repeat: SetPageUptodate(filepage); set_page_dirty(filepage); swap_free(swap); - } else if (!(error = add_to_page_cache( - swappage, mapping, idx, GFP_NOWAIT))) { + } else if (!(error = add_to_page_cache_locked(swappage, mapping, + idx, GFP_NOWAIT))) { info->flags |= SHMEM_PAGEIN; shmem_swp_set(info, entry, 0); shmem_swp_unmap(entry); diff --git a/mm/swap_state.c b/mm/swap_state.c index d8aadaf2a0ba..3e3381d6c7ee 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -64,7 +64,7 @@ void show_swap_cache_info(void) } /* - * add_to_swap_cache resembles add_to_page_cache on swapper_space, + * add_to_swap_cache resembles add_to_page_cache_locked on swapper_space, * but sets SwapCache flag and private instead of mapping and index. */ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) @@ -76,19 +76,26 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) BUG_ON(PagePrivate(page)); error = radix_tree_preload(gfp_mask); if (!error) { + page_cache_get(page); + SetPageSwapCache(page); + set_page_private(page, entry.val); + write_lock_irq(&swapper_space.tree_lock); error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); - if (!error) { - page_cache_get(page); - SetPageSwapCache(page); - set_page_private(page, entry.val); + if (likely(!error)) { total_swapcache_pages++; __inc_zone_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(add_total); } write_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); + + if (unlikely(error)) { + set_page_private(page, 0UL); + ClearPageSwapCache(page); + page_cache_release(page); + } } return error; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 26672c6cd3ce..0075eac1cd04 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -391,12 +391,10 @@ static pageout_t pageout(struct page *page, struct address_space *mapping, } /* - * Attempt to detach a locked page from its ->mapping. If it is dirty or if - * someone else has a ref on the page, abort and return 0. If it was - * successfully detached, return 1. Assumes the caller has a single ref on - * this page. + * Same as remove_mapping, but if the page is removed from the mapping, it + * gets returned with a refcount of 0. */ -int remove_mapping(struct address_space *mapping, struct page *page) +static int __remove_mapping(struct address_space *mapping, struct page *page) { BUG_ON(!PageLocked(page)); BUG_ON(mapping != page_mapping(page)); @@ -427,24 +425,24 @@ int remove_mapping(struct address_space *mapping, struct page *page) * Note that if SetPageDirty is always performed via set_page_dirty, * and thus under tree_lock, then this ordering is not required. */ - if (unlikely(page_count(page) != 2)) + if (!page_freeze_refs(page, 2)) goto cannot_free; - smp_rmb(); - if (unlikely(PageDirty(page))) + /* note: atomic_cmpxchg in page_freeze_refs provides the smp_rmb */ + if (unlikely(PageDirty(page))) { + page_unfreeze_refs(page, 2); goto cannot_free; + } if (PageSwapCache(page)) { swp_entry_t swap = { .val = page_private(page) }; __delete_from_swap_cache(page); write_unlock_irq(&mapping->tree_lock); swap_free(swap); - __put_page(page); /* The pagecache ref */ - return 1; + } else { + __remove_from_page_cache(page); + write_unlock_irq(&mapping->tree_lock); } - __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); - __put_page(page); return 1; cannot_free: @@ -452,6 +450,26 @@ cannot_free: return 0; } +/* + * Attempt to detach a locked page from its ->mapping. If it is dirty or if + * someone else has a ref on the page, abort and return 0. If it was + * successfully detached, return 1. Assumes the caller has a single ref on + * this page. + */ +int remove_mapping(struct address_space *mapping, struct page *page) +{ + if (__remove_mapping(mapping, page)) { + /* + * Unfreezing the refcount with 1 rather than 2 effectively + * drops the pagecache ref for us without requiring another + * atomic operation. + */ + page_unfreeze_refs(page, 1); + return 1; + } + return 0; +} + /* * shrink_page_list() returns the number of reclaimed pages */ @@ -598,18 +616,34 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (PagePrivate(page)) { if (!try_to_release_page(page, sc->gfp_mask)) goto activate_locked; - if (!mapping && page_count(page) == 1) - goto free_it; + if (!mapping && page_count(page) == 1) { + unlock_page(page); + if (put_page_testzero(page)) + goto free_it; + else { + /* + * rare race with speculative reference. + * the speculative reference will free + * this page shortly, so we may + * increment nr_reclaimed here (and + * leave it off the LRU). + */ + nr_reclaimed++; + continue; + } + } } - if (!mapping || !remove_mapping(mapping, page)) + if (!mapping || !__remove_mapping(mapping, page)) goto keep_locked; -free_it: unlock_page(page); +free_it: nr_reclaimed++; - if (!pagevec_add(&freed_pvec, page)) - __pagevec_release_nonlru(&freed_pvec); + if (!pagevec_add(&freed_pvec, page)) { + __pagevec_free(&freed_pvec); + pagevec_reinit(&freed_pvec); + } continue; activate_locked: @@ -623,7 +657,7 @@ keep: } list_splice(&ret_pages, page_list); if (pagevec_count(&freed_pvec)) - __pagevec_release_nonlru(&freed_pvec); + __pagevec_free(&freed_pvec); count_vm_events(PGACTIVATE, pgactivate); return nr_reclaimed; } -- cgit v1.2.3 From 19fd6231279be3c3bdd02ed99f9b0eb195978064 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:32 -0700 Subject: mm: spinlock tree_lock mapping->tree_lock has no read lockers. convert the lock from an rwlock to a spinlock. Signed-off-by: Nick Piggin Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Hugh Dickins Cc: "Paul E. McKenney" Reviewed-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 4 ++-- fs/inode.c | 2 +- include/asm-arm/cacheflush.h | 4 ++-- include/asm-parisc/cacheflush.h | 4 ++-- include/linux/fs.h | 2 +- mm/filemap.c | 10 +++++----- mm/migrate.c | 11 +++++------ mm/page-writeback.c | 12 ++++++------ mm/swap_state.c | 10 +++++----- mm/swapfile.c | 4 ++-- mm/truncate.c | 6 +++--- mm/vmscan.c | 8 ++++---- 12 files changed, 38 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index d48caee12e2a..109b261192d9 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -706,7 +706,7 @@ static int __set_page_dirty(struct page *page, if (TestSetPageDirty(page)) return 0; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); if (page->mapping) { /* Race with truncate? */ WARN_ON_ONCE(warn && !PageUptodate(page)); @@ -719,7 +719,7 @@ static int __set_page_dirty(struct page *page, radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); return 1; diff --git a/fs/inode.c b/fs/inode.c index c36d9480335c..35b6414522ea 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -209,7 +209,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_dentry); INIT_LIST_HEAD(&inode->i_devices); INIT_RADIX_TREE(&inode->i_data.page_tree, GFP_ATOMIC); - rwlock_init(&inode->i_data.tree_lock); + spin_lock_init(&inode->i_data.tree_lock); spin_lock_init(&inode->i_data.i_mmap_lock); INIT_LIST_HEAD(&inode->i_data.private_list); spin_lock_init(&inode->i_data.private_lock); diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index 70b0fe724b62..03cf1ee977b7 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h @@ -424,9 +424,9 @@ static inline void flush_anon_page(struct vm_area_struct *vma, } #define flush_dcache_mmap_lock(mapping) \ - write_lock_irq(&(mapping)->tree_lock) + spin_lock_irq(&(mapping)->tree_lock) #define flush_dcache_mmap_unlock(mapping) \ - write_unlock_irq(&(mapping)->tree_lock) + spin_unlock_irq(&(mapping)->tree_lock) #define flush_icache_user_range(vma,page,addr,len) \ flush_dcache_page(page) diff --git a/include/asm-parisc/cacheflush.h b/include/asm-parisc/cacheflush.h index 2f1e1b05440a..b7ca6dc7fddc 100644 --- a/include/asm-parisc/cacheflush.h +++ b/include/asm-parisc/cacheflush.h @@ -45,9 +45,9 @@ void flush_cache_mm(struct mm_struct *mm); extern void flush_dcache_page(struct page *page); #define flush_dcache_mmap_lock(mapping) \ - write_lock_irq(&(mapping)->tree_lock) + spin_lock_irq(&(mapping)->tree_lock) #define flush_dcache_mmap_unlock(mapping) \ - write_unlock_irq(&(mapping)->tree_lock) + spin_unlock_irq(&(mapping)->tree_lock) #define flush_icache_page(vma,page) do { \ flush_kernel_dcache_page(page); \ diff --git a/include/linux/fs.h b/include/linux/fs.h index 49d8eb7a71be..53d2edb709b3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -499,7 +499,7 @@ struct backing_dev_info; struct address_space { struct inode *host; /* owner: inode, block_device */ struct radix_tree_root page_tree; /* radix tree of all pages */ - rwlock_t tree_lock; /* and rwlock protecting it */ + spinlock_t tree_lock; /* and lock protecting it */ unsigned int i_mmap_writable;/* count VM_SHARED mappings */ struct prio_tree_root i_mmap; /* tree of private and shared mappings */ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ diff --git a/mm/filemap.c b/mm/filemap.c index feb8448d8618..2ed8b0389c51 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -109,7 +109,7 @@ /* * Remove a page from the page cache and free it. Caller has to make * sure the page is locked and that nobody else uses it - or that usage - * is safe. The caller must hold a write_lock on the mapping's tree_lock. + * is safe. The caller must hold the mapping's tree_lock. */ void __remove_from_page_cache(struct page *page) { @@ -141,9 +141,9 @@ void remove_from_page_cache(struct page *page) BUG_ON(!PageLocked(page)); - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); } static int sync_page(void *word) @@ -469,7 +469,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, page->mapping = mapping; page->index = offset; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); if (likely(!error)) { mapping->nrpages++; @@ -480,7 +480,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, page_cache_release(page); } - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } else mem_cgroup_uncharge_cache_page(page); diff --git a/mm/migrate.c b/mm/migrate.c index 3ca6392e82cc..153572fb60b8 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -323,7 +323,7 @@ static int migrate_page_move_mapping(struct address_space *mapping, return 0; } - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); @@ -331,12 +331,12 @@ static int migrate_page_move_mapping(struct address_space *mapping, expected_count = 2 + !!PagePrivate(page); if (page_count(page) != expected_count || (struct page *)radix_tree_deref_slot(pslot) != page) { - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } if (!page_freeze_refs(page, expected_count)) { - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } @@ -373,10 +373,9 @@ static int migrate_page_move_mapping(struct address_space *mapping, __dec_zone_page_state(page, NR_FILE_PAGES); __inc_zone_page_state(newpage, NR_FILE_PAGES); - write_unlock_irq(&mapping->tree_lock); - if (!PageSwapCache(newpage)) { + spin_unlock_irq(&mapping->tree_lock); + if (!PageSwapCache(newpage)) mem_cgroup_uncharge_cache_page(page); - } return 0; } diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 94c6d8988ab3..24de8b65fdbd 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1088,7 +1088,7 @@ int __set_page_dirty_nobuffers(struct page *page) if (!mapping) return 1; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); mapping2 = page_mapping(page); if (mapping2) { /* Race with truncate? */ BUG_ON(mapping2 != mapping); @@ -1102,7 +1102,7 @@ int __set_page_dirty_nobuffers(struct page *page) radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); if (mapping->host) { /* !PageAnon && !swapper_space */ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); @@ -1258,7 +1258,7 @@ int test_clear_page_writeback(struct page *page) struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned long flags; - write_lock_irqsave(&mapping->tree_lock, flags); + spin_lock_irqsave(&mapping->tree_lock, flags); ret = TestClearPageWriteback(page); if (ret) { radix_tree_tag_clear(&mapping->page_tree, @@ -1269,7 +1269,7 @@ int test_clear_page_writeback(struct page *page) __bdi_writeout_inc(bdi); } } - write_unlock_irqrestore(&mapping->tree_lock, flags); + spin_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestClearPageWriteback(page); } @@ -1287,7 +1287,7 @@ int test_set_page_writeback(struct page *page) struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned long flags; - write_lock_irqsave(&mapping->tree_lock, flags); + spin_lock_irqsave(&mapping->tree_lock, flags); ret = TestSetPageWriteback(page); if (!ret) { radix_tree_tag_set(&mapping->page_tree, @@ -1300,7 +1300,7 @@ int test_set_page_writeback(struct page *page) radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - write_unlock_irqrestore(&mapping->tree_lock, flags); + spin_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestSetPageWriteback(page); } diff --git a/mm/swap_state.c b/mm/swap_state.c index 3e3381d6c7ee..2c217e33d497 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -39,7 +39,7 @@ static struct backing_dev_info swap_backing_dev_info = { struct address_space swapper_space = { .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), - .tree_lock = __RW_LOCK_UNLOCKED(swapper_space.tree_lock), + .tree_lock = __SPIN_LOCK_UNLOCKED(swapper_space.tree_lock), .a_ops = &swap_aops, .i_mmap_nonlinear = LIST_HEAD_INIT(swapper_space.i_mmap_nonlinear), .backing_dev_info = &swap_backing_dev_info, @@ -80,7 +80,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) SetPageSwapCache(page); set_page_private(page, entry.val); - write_lock_irq(&swapper_space.tree_lock); + spin_lock_irq(&swapper_space.tree_lock); error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); if (likely(!error)) { @@ -88,7 +88,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) __inc_zone_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(add_total); } - write_unlock_irq(&swapper_space.tree_lock); + spin_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); if (unlikely(error)) { @@ -182,9 +182,9 @@ void delete_from_swap_cache(struct page *page) entry.val = page_private(page); - write_lock_irq(&swapper_space.tree_lock); + spin_lock_irq(&swapper_space.tree_lock); __delete_from_swap_cache(page); - write_unlock_irq(&swapper_space.tree_lock); + spin_unlock_irq(&swapper_space.tree_lock); swap_free(entry); page_cache_release(page); diff --git a/mm/swapfile.c b/mm/swapfile.c index 2f33edb8bee9..af283933c14e 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -369,13 +369,13 @@ int remove_exclusive_swap_page(struct page *page) retval = 0; if (p->swap_map[swp_offset(entry)] == 1) { /* Recheck the page count with the swapcache lock held.. */ - write_lock_irq(&swapper_space.tree_lock); + spin_lock_irq(&swapper_space.tree_lock); if ((page_count(page) == 2) && !PageWriteback(page)) { __delete_from_swap_cache(page); SetPageDirty(page); retval = 1; } - write_unlock_irq(&swapper_space.tree_lock); + spin_unlock_irq(&swapper_space.tree_lock); } spin_unlock(&swap_lock); diff --git a/mm/truncate.c b/mm/truncate.c index b8961cb63414..e68443d74567 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -349,18 +349,18 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page) if (PagePrivate(page) && !try_to_release_page(page, GFP_KERNEL)) return 0; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); if (PageDirty(page)) goto failed; BUG_ON(PagePrivate(page)); __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); ClearPageUptodate(page); page_cache_release(page); /* pagecache ref */ return 1; failed: - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return 0; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 0075eac1cd04..8f71761bc4b7 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -399,7 +399,7 @@ static int __remove_mapping(struct address_space *mapping, struct page *page) BUG_ON(!PageLocked(page)); BUG_ON(mapping != page_mapping(page)); - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); /* * The non racy check for a busy page. * @@ -436,17 +436,17 @@ static int __remove_mapping(struct address_space *mapping, struct page *page) if (PageSwapCache(page)) { swp_entry_t swap = { .val = page_private(page) }; __delete_from_swap_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); swap_free(swap); } else { __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); } return 1; cannot_free: - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return 0; } -- cgit v1.2.3 From 51cc50685a4275c6a02653670af9f108a64e01cf Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 25 Jul 2008 19:45:34 -0700 Subject: SL*B: drop kmem cache argument from constructor Kmem cache passed to constructor is only needed for constructors that are themselves multiplexeres. Nobody uses this "feature", nor does anybody uses passed kmem cache in non-trivial way, so pass only pointer to object. Non-trivial places are: arch/powerpc/mm/init_64.c arch/powerpc/mm/hugetlbpage.c This is flag day, yes. Signed-off-by: Alexey Dobriyan Acked-by: Pekka Enberg Acked-by: Christoph Lameter Cc: Jon Tollefson Cc: Nick Piggin Cc: Matt Mackall [akpm@linux-foundation.org: fix arch/powerpc/mm/hugetlbpage.c] [akpm@linux-foundation.org: fix mm/slab.c] [akpm@linux-foundation.org: fix ubifs] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-s3c24xx/dma.c | 2 +- arch/powerpc/kernel/rtas_flash.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 9 ++------- arch/powerpc/mm/init_64.c | 24 +++++++++--------------- arch/powerpc/platforms/cell/spufs/inode.c | 2 +- arch/sh/mm/pmb.c | 2 +- arch/xtensa/mm/init.c | 2 +- drivers/usb/mon/mon_text.c | 4 ++-- fs/adfs/super.c | 2 +- fs/affs/super.c | 2 +- fs/afs/super.c | 4 ++-- fs/befs/linuxvfs.c | 2 +- fs/bfs/inode.c | 2 +- fs/block_dev.c | 2 +- fs/buffer.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/coda/inode.c | 2 +- fs/ecryptfs/main.c | 4 ++-- fs/efs/super.c | 2 +- fs/ext2/super.c | 2 +- fs/ext3/super.c | 2 +- fs/ext4/super.c | 2 +- fs/fat/cache.c | 2 +- fs/fat/inode.c | 2 +- fs/fuse/inode.c | 2 +- fs/gfs2/main.c | 4 ++-- fs/hfs/super.c | 2 +- fs/hfsplus/super.c | 2 +- fs/hpfs/super.c | 2 +- fs/hugetlbfs/inode.c | 2 +- fs/inode.c | 2 +- fs/isofs/inode.c | 2 +- fs/jffs2/super.c | 2 +- fs/jfs/jfs_metapage.c | 2 +- fs/jfs/super.c | 2 +- fs/locks.c | 2 +- fs/minix/inode.c | 2 +- fs/ncpfs/inode.c | 2 +- fs/nfs/inode.c | 2 +- fs/ntfs/super.c | 2 +- fs/ocfs2/dlm/dlmfs.c | 3 +-- fs/ocfs2/super.c | 2 +- fs/openpromfs/inode.c | 2 +- fs/proc/inode.c | 2 +- fs/qnx4/inode.c | 2 +- fs/reiserfs/super.c | 2 +- fs/romfs/inode.c | 2 +- fs/smbfs/inode.c | 2 +- fs/sysv/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/udf/super.c | 2 +- fs/ufs/super.c | 2 +- fs/xfs/linux-2.6/kmem.h | 2 +- fs/xfs/linux-2.6/xfs_super.c | 1 - include/linux/slab.h | 2 +- include/linux/slub_def.h | 2 +- ipc/mqueue.c | 2 +- kernel/fork.c | 2 +- lib/idr.c | 2 +- lib/radix-tree.c | 2 +- mm/rmap.c | 2 +- mm/shmem.c | 2 +- mm/slab.c | 11 +++++------ mm/slob.c | 7 +++---- mm/slub.c | 13 ++++++------- net/socket.c | 2 +- net/sunrpc/rpc_pipe.c | 2 +- 67 files changed, 90 insertions(+), 106 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index 60f162dc4fad..8c5e656d5d8c 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -1304,7 +1304,7 @@ struct sysdev_class dma_sysclass = { /* kmem cache implementation */ -static void s3c2410_dma_cache_ctor(struct kmem_cache *c, void *p) +static void s3c2410_dma_cache_ctor(void *p) { memset(p, 0, sizeof(struct s3c2410_dma_buf)); } diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 09ded5c424a9..149cb112cd1a 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -286,7 +286,7 @@ static ssize_t rtas_flash_read(struct file *file, char __user *buf, } /* constructor for flash_block_cache */ -void rtas_block_ctor(struct kmem_cache *cache, void *ptr) +void rtas_block_ctor(void *ptr) { memset(ptr, 0, RTAS_BLK_SIZE); } diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index fb42c4dd3217..ed0aab0208a6 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -113,7 +113,7 @@ static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, unsigned long address, unsigned int psize) { - pte_t *new = kmem_cache_alloc(huge_pgtable_cache(psize), + pte_t *new = kmem_cache_zalloc(huge_pgtable_cache(psize), GFP_KERNEL|__GFP_REPEAT); if (! new) @@ -730,11 +730,6 @@ static int __init hugepage_setup_sz(char *str) } __setup("hugepagesz=", hugepage_setup_sz); -static void zero_ctor(struct kmem_cache *cache, void *addr) -{ - memset(addr, 0, kmem_cache_size(cache)); -} - static int __init hugetlbpage_init(void) { unsigned int psize; @@ -756,7 +751,7 @@ static int __init hugetlbpage_init(void) HUGEPTE_TABLE_SIZE(psize), HUGEPTE_TABLE_SIZE(psize), 0, - zero_ctor); + NULL); if (!huge_pgtable_cache(psize)) panic("hugetlbpage_init(): could not create %s"\ "\n", HUGEPTE_CACHE_NAME(psize)); diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index a41bc5aa2043..4f7df85129d8 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -136,9 +136,14 @@ static int __init setup_kcore(void) module_init(setup_kcore); #endif -static void zero_ctor(struct kmem_cache *cache, void *addr) +static void pgd_ctor(void *addr) { - memset(addr, 0, kmem_cache_size(cache)); + memset(addr, 0, PGD_TABLE_SIZE); +} + +static void pmd_ctor(void *addr) +{ + memset(addr, 0, PMD_TABLE_SIZE); } static const unsigned int pgtable_cache_size[2] = { @@ -163,19 +168,8 @@ struct kmem_cache *pgtable_cache[ARRAY_SIZE(pgtable_cache_size)]; void pgtable_cache_init(void) { - int i; - - for (i = 0; i < ARRAY_SIZE(pgtable_cache_size); i++) { - int size = pgtable_cache_size[i]; - const char *name = pgtable_cache_name[i]; - - pr_debug("Allocating page table cache %s (#%d) " - "for size: %08x...\n", name, i, size); - pgtable_cache[i] = kmem_cache_create(name, - size, size, - SLAB_PANIC, - zero_ctor); - } + pgtable_cache[0] = kmem_cache_create(pgtable_cache_name[0], PGD_TABLE_SIZE, PGD_TABLE_SIZE, SLAB_PANIC, pgd_ctor); + pgtable_cache[1] = kmem_cache_create(pgtable_cache_name[1], PMD_TABLE_SIZE, PMD_TABLE_SIZE, SLAB_PANIC, pmd_ctor); } #ifdef CONFIG_SPARSEMEM_VMEMMAP diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 7123472801d9..690ca7b0dcf6 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -78,7 +78,7 @@ spufs_destroy_inode(struct inode *inode) } static void -spufs_init_once(struct kmem_cache *cachep, void *p) +spufs_init_once(void *p) { struct spufs_inode_info *ei = p; diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 0b0ec6e04753..46911bcbf17b 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -293,7 +293,7 @@ void pmb_unmap(unsigned long addr) } while (pmbe); } -static void pmb_cache_ctor(struct kmem_cache *cachep, void *pmb) +static void pmb_cache_ctor(void *pmb) { struct pmb_entry *pmbe = pmb; diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 81d0560eaea2..ee261005b363 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -309,7 +309,7 @@ void show_mem(void) struct kmem_cache *pgtable_cache __read_mostly; -static void pgd_ctor(struct kmem_cache *cache, void* addr) +static void pgd_ctor(void* addr) { pte_t* ptep = (pte_t*)addr; int i; diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 5e3e4e9b6c77..1f715436d6d3 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -87,7 +87,7 @@ struct mon_reader_text { static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ -static void mon_text_ctor(struct kmem_cache *, void *); +static void mon_text_ctor(void *); struct mon_text_ptr { int cnt, limit; @@ -720,7 +720,7 @@ void mon_text_del(struct mon_bus *mbus) /* * Slab interface: constructor. */ -static void mon_text_ctor(struct kmem_cache *slab, void *mem) +static void mon_text_ctor(void *mem) { /* * Nothing to initialize. No, really! diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 9e421eeb672b..26f3b43726bb 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -249,7 +249,7 @@ static void adfs_destroy_inode(struct inode *inode) kmem_cache_free(adfs_inode_cachep, ADFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct adfs_inode_info *ei = (struct adfs_inode_info *) foo; diff --git a/fs/affs/super.c b/fs/affs/super.c index 4e0309566406..3a89094f93d0 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -90,7 +90,7 @@ static void affs_destroy_inode(struct inode *inode) kmem_cache_free(affs_inode_cachep, AFFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct affs_inode_info *ei = (struct affs_inode_info *) foo; diff --git a/fs/afs/super.c b/fs/afs/super.c index 7e3faeef6818..250d8c4d66e4 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -27,7 +27,7 @@ #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ -static void afs_i_init_once(struct kmem_cache *cachep, void *foo); +static void afs_i_init_once(void *foo); static int afs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt); @@ -449,7 +449,7 @@ static void afs_put_super(struct super_block *sb) /* * initialise an inode cache slab element prior to any use */ -static void afs_i_init_once(struct kmem_cache *cachep, void *_vnode) +static void afs_i_init_once(void *_vnode) { struct afs_vnode *vnode = _vnode; diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index e8717de3bab3..02c6e62b72f8 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -289,7 +289,7 @@ befs_destroy_inode(struct inode *inode) kmem_cache_free(befs_inode_cachep, BEFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct befs_inode_info *bi = (struct befs_inode_info *) foo; diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 053e690ec9ed..0ed57b5ee012 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -264,7 +264,7 @@ static void bfs_destroy_inode(struct inode *inode) kmem_cache_free(bfs_inode_cachep, BFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct bfs_inode_info *bi = foo; diff --git a/fs/block_dev.c b/fs/block_dev.c index 10d8a0aa871a..dcf37cada369 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -271,7 +271,7 @@ static void bdev_destroy_inode(struct inode *inode) kmem_cache_free(bdev_cachep, bdi); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct bdev_inode *ei = (struct bdev_inode *) foo; struct block_device *bdev = &ei->bdev; diff --git a/fs/buffer.c b/fs/buffer.c index 109b261192d9..5fd497cdd6f3 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -3272,7 +3272,7 @@ int bh_submit_read(struct buffer_head *bh) EXPORT_SYMBOL(bh_submit_read); static void -init_buffer_head(struct kmem_cache *cachep, void *data) +init_buffer_head(void *data) { struct buffer_head *bh = data; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 22857c639df5..fe5f6809cba6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -766,7 +766,7 @@ const struct file_operations cifs_dir_ops = { }; static void -cifs_init_once(struct kmem_cache *cachep, void *inode) +cifs_init_once(void *inode) { struct cifsInodeInfo *cifsi = inode; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 2f58dfc70083..830f51abb971 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -58,7 +58,7 @@ static void coda_destroy_inode(struct inode *inode) kmem_cache_free(coda_inode_cachep, ITOC(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct coda_inode_info *ei = (struct coda_inode_info *) foo; diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 6f403cfba14f..448dfd597b5f 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -578,7 +578,7 @@ static struct file_system_type ecryptfs_fs_type = { * Initializes the ecryptfs_inode_info_cache when it is created */ static void -inode_info_init_once(struct kmem_cache *cachep, void *vptr) +inode_info_init_once(void *vptr) { struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr; @@ -589,7 +589,7 @@ static struct ecryptfs_cache_info { struct kmem_cache **cache; const char *name; size_t size; - void (*ctor)(struct kmem_cache *cache, void *obj); + void (*ctor)(void *obj); } ecryptfs_cache_infos[] = { { .cache = &ecryptfs_auth_tok_list_item_cache, diff --git a/fs/efs/super.c b/fs/efs/super.c index d733531b55e2..567b134fa1f1 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -70,7 +70,7 @@ static void efs_destroy_inode(struct inode *inode) kmem_cache_free(efs_inode_cachep, INODE_INFO(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct efs_inode_info *ei = (struct efs_inode_info *) foo; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 31308a3b0b8b..fd88c7b43e66 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -159,7 +159,7 @@ static void ext2_destroy_inode(struct inode *inode) kmem_cache_free(ext2_inode_cachep, EXT2_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct ext2_inode_info *ei = (struct ext2_inode_info *) foo; diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 615788c6843a..8ddced384674 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -472,7 +472,7 @@ static void ext3_destroy_inode(struct inode *inode) kmem_cache_free(ext3_inode_cachep, EXT3_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct ext3_inode_info *ei = (struct ext3_inode_info *) foo; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 1cb371dcd609..b5479b1dff14 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -595,7 +595,7 @@ static void ext4_destroy_inode(struct inode *inode) kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct ext4_inode_info *ei = (struct ext4_inode_info *) foo; diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 3a9ecac8d61f..3222f51c41cf 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -36,7 +36,7 @@ static inline int fat_max_cache(struct inode *inode) static struct kmem_cache *fat_cache_cachep; -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct fat_cache *cache = (struct fat_cache *)foo; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 23676f9d79ce..6d266d793e2c 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -498,7 +498,7 @@ static void fat_destroy_inode(struct inode *inode) kmem_cache_free(fat_inode_cachep, MSDOS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct msdos_inode_info *ei = (struct msdos_inode_info *)foo; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7d2f7d6e22e2..d2249f174e20 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -956,7 +956,7 @@ static inline void unregister_fuseblk(void) } #endif -static void fuse_inode_init_once(struct kmem_cache *cachep, void *foo) +static void fuse_inode_init_once(void *foo) { struct inode * inode = foo; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index bcc668d0fadd..bb2cc303ac29 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -24,7 +24,7 @@ #include "util.h" #include "glock.h" -static void gfs2_init_inode_once(struct kmem_cache *cachep, void *foo) +static void gfs2_init_inode_once(void *foo) { struct gfs2_inode *ip = foo; @@ -33,7 +33,7 @@ static void gfs2_init_inode_once(struct kmem_cache *cachep, void *foo) ip->i_alloc = NULL; } -static void gfs2_init_glock_once(struct kmem_cache *cachep, void *foo) +static void gfs2_init_glock_once(void *foo) { struct gfs2_glock *gl = foo; diff --git a/fs/hfs/super.c b/fs/hfs/super.c index ac2ec5ef66e4..4abb1047c689 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -432,7 +432,7 @@ static struct file_system_type hfs_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static void hfs_init_once(struct kmem_cache *cachep, void *p) +static void hfs_init_once(void *p) { struct hfs_inode_info *i = p; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 3859118531c7..e834e578c93f 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -485,7 +485,7 @@ static struct file_system_type hfsplus_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static void hfsplus_init_once(struct kmem_cache *cachep, void *p) +static void hfsplus_init_once(void *p) { struct hfsplus_inode_info *i = p; diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index f63a699ec659..b8ae9c90ada0 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -173,7 +173,7 @@ static void hpfs_destroy_inode(struct inode *inode) kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index dbd01d262ca4..3f58923fb39b 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -705,7 +705,7 @@ static const struct address_space_operations hugetlbfs_aops = { }; -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo; diff --git a/fs/inode.c b/fs/inode.c index 35b6414522ea..b6726f644530 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -224,7 +224,7 @@ void inode_init_once(struct inode *inode) EXPORT_SYMBOL(inode_init_once); -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct inode * inode = (struct inode *) foo; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 044a254d526b..26948a6033b6 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -73,7 +73,7 @@ static void isofs_destroy_inode(struct inode *inode) kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct iso_inode_info *ei = foo; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 7da69eae49e4..efd401257ed9 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -44,7 +44,7 @@ static void jffs2_destroy_inode(struct inode *inode) kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); } -static void jffs2_i_init_once(struct kmem_cache *cachep, void *foo) +static void jffs2_i_init_once(void *foo) { struct jffs2_inode_info *f = foo; diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 854ff0ec574f..c350057087dd 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -182,7 +182,7 @@ static inline void remove_metapage(struct page *page, struct metapage *mp) #endif -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct metapage *mp = (struct metapage *)foo; diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 359c091d8965..3630718be395 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -760,7 +760,7 @@ static struct file_system_type jfs_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct jfs_inode_info *jfs_ip = (struct jfs_inode_info *) foo; diff --git a/fs/locks.c b/fs/locks.c index 01490300f7cb..5eb259e3cd38 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -201,7 +201,7 @@ EXPORT_SYMBOL(locks_init_lock); * Initialises the fields of the file lock which are invariant for * free file_locks. */ -static void init_once(struct kmem_cache *cache, void *foo) +static void init_once(void *foo) { struct file_lock *lock = (struct file_lock *) foo; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 523d73713418..d1d1eb84679d 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -68,7 +68,7 @@ static void minix_destroy_inode(struct inode *inode) kmem_cache_free(minix_inode_cachep, minix_i(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct minix_inode_info *ei = (struct minix_inode_info *) foo; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 2e5ab1204dec..d642f0e5b365 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -64,7 +64,7 @@ static void ncp_destroy_inode(struct inode *inode) kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct ncp_inode_info *ei = (struct ncp_inode_info *) foo; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index df23f987da6b..52daefa2f521 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1242,7 +1242,7 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi) #endif } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct nfs_inode *nfsi = (struct nfs_inode *) foo; diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 3e76f3b216bc..4a46743b5077 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -3080,7 +3080,7 @@ struct kmem_cache *ntfs_inode_cache; struct kmem_cache *ntfs_big_inode_cache; /* Init once constructor for the inode slab cache. */ -static void ntfs_big_inode_init_once(struct kmem_cache *cachep, void *foo) +static void ntfs_big_inode_init_once(void *foo) { ntfs_inode *ni = (ntfs_inode *)foo; diff --git a/fs/ocfs2/dlm/dlmfs.c b/fs/ocfs2/dlm/dlmfs.c index e48aba698b77..533a789c3ef8 100644 --- a/fs/ocfs2/dlm/dlmfs.c +++ b/fs/ocfs2/dlm/dlmfs.c @@ -267,8 +267,7 @@ static ssize_t dlmfs_file_write(struct file *filp, return writelen; } -static void dlmfs_init_once(struct kmem_cache *cachep, - void *foo) +static void dlmfs_init_once(void *foo) { struct dlmfs_inode_private *ip = (struct dlmfs_inode_private *) foo; diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index ccecfe5094fa..2560b33889aa 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1118,7 +1118,7 @@ bail: return status; } -static void ocfs2_inode_init_once(struct kmem_cache *cachep, void *data) +static void ocfs2_inode_init_once(void *data) { struct ocfs2_inode_info *oi = data; diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index d17b4fd204e1..9f5b054f06b9 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -430,7 +430,7 @@ static struct file_system_type openprom_fs_type = { .kill_sb = kill_anon_super, }; -static void op_inode_init_once(struct kmem_cache * cachep, void *data) +static void op_inode_init_once(void *data) { struct op_inode_info *oi = (struct op_inode_info *) data; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 02eca2ed9dd7..b37f25dc45a5 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -94,7 +94,7 @@ static void proc_destroy_inode(struct inode *inode) kmem_cache_free(proc_inode_cachep, PROC_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct proc_inode *ei = (struct proc_inode *) foo; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index b31ab78052b3..2aad1044b84c 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -553,7 +553,7 @@ static void qnx4_destroy_inode(struct inode *inode) kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 2ec748ba0bd3..879e54d35c2d 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -521,7 +521,7 @@ static void reiserfs_destroy_inode(struct inode *inode) kmem_cache_free(reiserfs_inode_cachep, REISERFS_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct reiserfs_inode_info *ei = (struct reiserfs_inode_info *)foo; diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 3f13d491c7c7..8e51a2aaa977 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -577,7 +577,7 @@ static void romfs_destroy_inode(struct inode *inode) kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct romfs_inode_info *ei = foo; diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 376ef3ee6ed7..3528f40ffb0f 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -67,7 +67,7 @@ static void smb_destroy_inode(struct inode *inode) kmem_cache_free(smb_inode_cachep, SMB_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct smb_inode_info *ei = (struct smb_inode_info *) foo; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index c5d60de0658f..df0d435baa48 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -326,7 +326,7 @@ static void sysv_destroy_inode(struct inode *inode) kmem_cache_free(sysv_inode_cachep, SYSV_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *p) +static void init_once(void *p) { struct sysv_inode_info *si = (struct sysv_inode_info *)p; diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 00eb9c68ad03..ca1e2d4e03cc 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1841,7 +1841,7 @@ static struct file_system_type ubifs_fs_type = { /* * Inode slab cache constructor. */ -static void inode_slab_ctor(struct kmem_cache *cachep, void *obj) +static void inode_slab_ctor(void *obj) { struct ubifs_inode *ui = obj; inode_init_once(&ui->vfs_inode); diff --git a/fs/udf/super.c b/fs/udf/super.c index 44cc702f96cc..5698bbf83bbf 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -148,7 +148,7 @@ static void udf_destroy_inode(struct inode *inode) kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct udf_inode_info *ei = (struct udf_inode_info *)foo; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 227c9d700040..3e30e40aa24d 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1302,7 +1302,7 @@ static void ufs_destroy_inode(struct inode *inode) kmem_cache_free(ufs_inode_cachep, UFS_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct ufs_inode_info *ei = (struct ufs_inode_info *) foo; diff --git a/fs/xfs/linux-2.6/kmem.h b/fs/xfs/linux-2.6/kmem.h index 5e9564902976..a20683cf74dd 100644 --- a/fs/xfs/linux-2.6/kmem.h +++ b/fs/xfs/linux-2.6/kmem.h @@ -79,7 +79,7 @@ kmem_zone_init(int size, char *zone_name) static inline kmem_zone_t * kmem_zone_init_flags(int size, char *zone_name, unsigned long flags, - void (*construct)(kmem_zone_t *, void *)) + void (*construct)(void *)) { return kmem_cache_create(zone_name, size, 0, flags, construct); } diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 742b2c7852c1..943381284e2e 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -843,7 +843,6 @@ xfs_fs_destroy_inode( STATIC void xfs_fs_inode_init_once( - kmem_zone_t *zonep, void *vnode) { inode_init_once(vn_to_inode((bhv_vnode_t *)vnode)); diff --git a/include/linux/slab.h b/include/linux/slab.h index 41103910f8a2..9ff8e8499403 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -58,7 +58,7 @@ int slab_is_available(void); struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, - void (*)(struct kmem_cache *, void *)); + void (*)(void *)); void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index d117ea2825a9..5bad61a93f65 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -85,7 +85,7 @@ struct kmem_cache { struct kmem_cache_order_objects min; gfp_t allocflags; /* gfp flags to use on each alloc */ int refcount; /* Refcount for slab cache destroy */ - void (*ctor)(struct kmem_cache *, void *); + void (*ctor)(void *); int inuse; /* Offset to metadata */ int align; /* Alignment */ const char *name; /* Name (only for display!) */ diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 1fdc2eb2f6d8..474984f9e032 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -207,7 +207,7 @@ static int mqueue_get_sb(struct file_system_type *fs_type, return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; diff --git a/kernel/fork.c b/kernel/fork.c index b99d73e971a4..80e83e459b17 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1442,7 +1442,7 @@ long do_fork(unsigned long clone_flags, #define ARCH_MIN_MMSTRUCT_ALIGN 0 #endif -static void sighand_ctor(struct kmem_cache *cachep, void *data) +static void sighand_ctor(void *data) { struct sighand_struct *sighand = data; diff --git a/lib/idr.c b/lib/idr.c index 3476f8203e97..e728c7fccc4d 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -607,7 +607,7 @@ void *idr_replace(struct idr *idp, void *ptr, int id) } EXPORT_SYMBOL(idr_replace); -static void idr_cache_ctor(struct kmem_cache *idr_layer_cache, void *idr_layer) +static void idr_cache_ctor(void *idr_layer) { memset(idr_layer, 0, sizeof(struct idr_layer)); } diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 9c4f1ffa2864..be86b32bc874 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1183,7 +1183,7 @@ int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) EXPORT_SYMBOL(radix_tree_tagged); static void -radix_tree_node_ctor(struct kmem_cache *cachep, void *node) +radix_tree_node_ctor(void *node) { memset(node, 0, sizeof(struct radix_tree_node)); } diff --git a/mm/rmap.c b/mm/rmap.c index abbd29f7c43f..39ae5a9bf382 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -138,7 +138,7 @@ void anon_vma_unlink(struct vm_area_struct *vma) anon_vma_free(anon_vma); } -static void anon_vma_ctor(struct kmem_cache *cachep, void *data) +static void anon_vma_ctor(void *data) { struct anon_vma *anon_vma = data; diff --git a/mm/shmem.c b/mm/shmem.c index 1089092aecaf..952d361774bb 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2352,7 +2352,7 @@ static void shmem_destroy_inode(struct inode *inode) kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct shmem_inode_info *p = (struct shmem_inode_info *) foo; diff --git a/mm/slab.c b/mm/slab.c index 052e7d64537e..918f04f7fef1 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -406,7 +406,7 @@ struct kmem_cache { unsigned int dflags; /* dynamic flags */ /* constructor func */ - void (*ctor)(struct kmem_cache *, void *); + void (*ctor)(void *obj); /* 5) cache creation/removal */ const char *name; @@ -2137,8 +2137,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep) */ struct kmem_cache * kmem_cache_create (const char *name, size_t size, size_t align, - unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + unsigned long flags, void (*ctor)(void *)) { size_t left_over, slab_size, ralign; struct kmem_cache *cachep = NULL, *pc; @@ -2653,7 +2652,7 @@ static void cache_init_objs(struct kmem_cache *cachep, * They must also be threaded. */ if (cachep->ctor && !(cachep->flags & SLAB_POISON)) - cachep->ctor(cachep, objp + obj_offset(cachep)); + cachep->ctor(objp + obj_offset(cachep)); if (cachep->flags & SLAB_RED_ZONE) { if (*dbg_redzone2(cachep, objp) != RED_INACTIVE) @@ -2669,7 +2668,7 @@ static void cache_init_objs(struct kmem_cache *cachep, cachep->buffer_size / PAGE_SIZE, 0); #else if (cachep->ctor) - cachep->ctor(cachep, objp); + cachep->ctor(objp); #endif slab_bufctl(slabp)[i] = i + 1; } @@ -3093,7 +3092,7 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, #endif objp += obj_offset(cachep); if (cachep->ctor && cachep->flags & SLAB_POISON) - cachep->ctor(cachep, objp); + cachep->ctor(objp); #if ARCH_SLAB_MINALIGN if ((u32)objp & (ARCH_SLAB_MINALIGN-1)) { printk(KERN_ERR "0x%p: not aligned to ARCH_SLAB_MINALIGN=%d\n", diff --git a/mm/slob.c b/mm/slob.c index de268eb7ac70..d8fbd4d1bfa7 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -525,12 +525,11 @@ struct kmem_cache { unsigned int size, align; unsigned long flags; const char *name; - void (*ctor)(struct kmem_cache *, void *); + void (*ctor)(void *); }; struct kmem_cache *kmem_cache_create(const char *name, size_t size, - size_t align, unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *c; @@ -575,7 +574,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *c, gfp_t flags, int node) b = slob_new_page(flags, get_order(c->size), node); if (c->ctor) - c->ctor(c, b); + c->ctor(b); return b; } diff --git a/mm/slub.c b/mm/slub.c index 77c21cf53ff9..b7e2cd5d82db 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1012,7 +1012,7 @@ __setup("slub_debug", setup_slub_debug); static unsigned long kmem_cache_flags(unsigned long objsize, unsigned long flags, const char *name, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { /* * Enable debugging if selected on the kernel commandline. @@ -1040,7 +1040,7 @@ static inline int check_object(struct kmem_cache *s, struct page *page, static inline void add_full(struct kmem_cache_node *n, struct page *page) {} static inline unsigned long kmem_cache_flags(unsigned long objsize, unsigned long flags, const char *name, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { return flags; } @@ -1103,7 +1103,7 @@ static void setup_object(struct kmem_cache *s, struct page *page, { setup_object_debug(s, page, object); if (unlikely(s->ctor)) - s->ctor(s, object); + s->ctor(object); } static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) @@ -2286,7 +2286,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags, const char *name, size_t size, size_t align, unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { memset(s, 0, kmem_size); s->name = name; @@ -3042,7 +3042,7 @@ static int slab_unmergeable(struct kmem_cache *s) static struct kmem_cache *find_mergeable(size_t size, size_t align, unsigned long flags, const char *name, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { struct kmem_cache *s; @@ -3082,8 +3082,7 @@ static struct kmem_cache *find_mergeable(size_t size, } struct kmem_cache *kmem_cache_create(const char *name, size_t size, - size_t align, unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *s; diff --git a/net/socket.c b/net/socket.c index 1310a82cbba7..8ef8ba81b9e2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -265,7 +265,7 @@ static void sock_destroy_inode(struct inode *inode) container_of(inode, struct socket_alloc, vfs_inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct socket_alloc *ei = (struct socket_alloc *)foo; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 5a9b0e7828cd..23a2b8f6dc49 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -897,7 +897,7 @@ static struct file_system_type rpc_pipe_fs_type = { }; static void -init_once(struct kmem_cache * cachep, void *foo) +init_once(void *foo) { struct rpc_inode *rpci = (struct rpc_inode *) foo; -- cgit v1.2.3 From 88ac2921a71f788ed693bcd44731dd6bc1994640 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:43 -0700 Subject: tracehook: add linux/tracehook.h This patch series introduces the "tracehook" interface layer of inlines in . There are more details in the log entry for patch 01/23 and in the header file comments inside that patch. Most of these changes move code around with little or no change, and they should not break anything or change any behavior. This sets a new standard for uniform arch support to enable clean arch-independent implementations of new debugging and tracing stuff, denoted by CONFIG_HAVE_ARCH_TRACEHOOK. Patch 20/23 adds that symbol to arch/Kconfig, with comments listing everything an arch has to do before setting "select HAVE_ARCH_TRACEHOOK". These are elaborted a bit at: http://sourceware.org/systemtap/wiki/utrace/arch/HowTo The new inlines that arch code must define or call have detailed kerneldoc comments in the generic header files that say what is required. No arch is obligated to do any work, and no arch's build should be broken by these changes. There are several steps that each arch should take so it can set HAVE_ARCH_TRACEHOOK. Most of these are simple. Providing this support will let new things people add for doing debugging and tracing of user-level threads "just work" for your arch in the future. For an arch that does not provide HAVE_ARCH_TRACEHOOK, some new options for such features will not be available for config. I have done some arch work and will submit this to the arch maintainers after the generic tracehook series settles in. For now, that work is available in my GIT repositories, and in patch and mbox-of-patches form at http://people.redhat.com/roland/utrace/2.6-current/ This paves the way for my "utrace" work, to be submitted later. But it is not innately tied to that. I hope that the tracehook series can go in soon regardless of what eventually does or doesn't go on top of it. For anyone implementing any kind of new tracing/debugging plan, or just understanding all the context of the existing ptrace implementation, having tracehook.h makes things much easier to find and understand. This patch: This adds the new kernel-internal header file . This is not yet used at all. The comments in the header introduce what the following series of patches is about. The aim is to formalize and consolidate all the places that the core kernel code and the arch code now ties into the ptrace implementation. These patches mostly don't cause any functional change. They just move the details of ptrace logic out of core code into tracehook.h inlines, where they are mostly compiled away to the same as before. All that changes is that everything is thoroughly documented and any future reworking of ptrace, or addition of something new, would not have to touch core code all over, just change the tracehook.h inlines. The new linux/ptrace.h inlines are used by the following patches in the new tracehook_*() inlines. Using these helpers for the ptrace event stops makes it simple to change or disable the old ptrace implementation of these stops conditionally later. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 33 ++++++++++++++++++++++++++++++ include/linux/tracehook.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 include/linux/tracehook.h (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c6f5f9dd0cee..c74abfc4c7e8 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -121,6 +121,39 @@ static inline void ptrace_unlink(struct task_struct *child) int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data); int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data); +/** + * task_ptrace - return %PT_* flags that apply to a task + * @task: pointer to &task_struct in question + * + * Returns the %PT_* flags that apply to @task. + */ +static inline int task_ptrace(struct task_struct *task) +{ + return task->ptrace; +} + +/** + * ptrace_event - possibly stop for a ptrace event notification + * @mask: %PT_* bit to check in @current->ptrace + * @event: %PTRACE_EVENT_* value to report if @mask is set + * @message: value for %PTRACE_GETEVENTMSG to return + * + * This checks the @mask bit to see if ptrace wants stops for this event. + * If so we stop, reporting @event and @message to the ptrace parent. + * + * Returns nonzero if we did a ptrace notification, zero if not. + * + * Called without locks. + */ +static inline int ptrace_event(int mask, int event, unsigned long message) +{ + if (mask && likely(!(current->ptrace & mask))) + return 0; + current->ptrace_message = message; + ptrace_notify((event << 8) | SIGTRAP); + return 1; +} + #ifndef force_successful_syscall_return /* * System call handlers that, upon successful completion, need to return a diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h new file mode 100644 index 000000000000..bea0f3eeff54 --- /dev/null +++ b/include/linux/tracehook.h @@ -0,0 +1,52 @@ +/* + * Tracing hooks + * + * Copyright (C) 2008 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * This file defines hook entry points called by core code where + * user tracing/debugging support might need to do something. These + * entry points are called tracehook_*(). Each hook declared below + * has a detailed kerneldoc comment giving the context (locking et + * al) from which it is called, and the meaning of its return value. + * + * Each function here typically has only one call site, so it is ok + * to have some nontrivial tracehook_*() inlines. In all cases, the + * fast path when no tracing is enabled should be very short. + * + * The purpose of this file and the tracehook_* layer is to consolidate + * the interface that the kernel core and arch code uses to enable any + * user debugging or tracing facility (such as ptrace). The interfaces + * here are carefully documented so that maintainers of core and arch + * code do not need to think about the implementation details of the + * tracing facilities. Likewise, maintainers of the tracing code do not + * need to understand all the calling core or arch code in detail, just + * documented circumstances of each call, such as locking conditions. + * + * If the calling core code changes so that locking is different, then + * it is ok to change the interface documented here. The maintainer of + * core code changing should notify the maintainers of the tracing code + * that they need to work out the change. + * + * Some tracehook_*() inlines take arguments that the current tracing + * implementations might not necessarily use. These function signatures + * are chosen to pass in all the information that is on hand in the + * caller and might conceivably be relevant to a tracer, so that the + * core code won't have to be updated when tracing adds more features. + * If a call site changes so that some of those parameters are no longer + * already on hand without extra work, then the tracehook_* interface + * can change so there is no make-work burden on the core code. The + * maintainer of core code changing should notify the maintainers of the + * tracing code that they need to work out the change. + */ + +#ifndef _LINUX_TRACEHOOK_H +#define _LINUX_TRACEHOOK_H 1 + +#include +#include + +#endif /* */ -- cgit v1.2.3 From 6341c393fcc37d58727865f1ee2f65e632e9d4f0 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:44 -0700 Subject: tracehook: exec This moves all the ptrace hooks related to exec into tracehook.h inlines. This also lifts the calls for tracing out of the binfmt load_binary hooks into search_binary_handler() after it calls into the binfmt module. This change has no effect, since all the binfmt modules' load_binary functions did the call at the end on success, and now search_binary_handler() does it immediately after return if successful. We consolidate the repeated code, and binfmt modules no longer need to import ptrace_notify(). Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32_aout.c | 6 ------ fs/binfmt_aout.c | 6 ------ fs/binfmt_elf.c | 6 ------ fs/binfmt_elf_fdpic.c | 7 ------- fs/binfmt_flat.c | 3 --- fs/binfmt_som.c | 2 -- fs/exec.c | 12 ++++-------- include/linux/tracehook.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 50 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c index 58cccb6483b0..a0e1dbe67dc1 100644 --- a/arch/x86/ia32/ia32_aout.c +++ b/arch/x86/ia32/ia32_aout.c @@ -441,12 +441,6 @@ beyond_if: regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0; set_fs(USER_DS); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } return 0; } diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index ba4cddb92f1d..204cfd1d7676 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -444,12 +444,6 @@ beyond_if: regs->gp = ex.a_gpvalue; #endif start_thread(regs, ex.a_entry, current->mm->start_stack); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } return 0; } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3b6ff854d983..655ed8d30a86 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1003,12 +1003,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) #endif start_thread(regs, elf_entry, bprm->p); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } retval = 0; out: kfree(loc); diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 1b59b1edf26d..fdeadab2f18b 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -433,13 +433,6 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; start_thread(regs, entryaddr, current->mm->start_stack); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } - retval = 0; error: diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index 2cb1acda3a82..56372ecf1690 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -920,9 +920,6 @@ static int load_flat_binary(struct linux_binprm * bprm, struct pt_regs * regs) start_thread(regs, start_addr, current->mm->start_stack); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); - return 0; } diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c index fdc36bfd6a7b..68be580ba289 100644 --- a/fs/binfmt_som.c +++ b/fs/binfmt_som.c @@ -274,8 +274,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) map_hpux_gateway_page(current,current->mm); start_thread_som(regs, som_entry, bprm->p); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); return 0; /* error cleanup */ diff --git a/fs/exec.c b/fs/exec.c index 5e559013e303..b8792a131533 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -42,13 +42,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -1071,13 +1071,8 @@ EXPORT_SYMBOL(prepare_binprm); static int unsafe_exec(struct task_struct *p) { - int unsafe = 0; - if (p->ptrace & PT_PTRACED) { - if (p->ptrace & PT_PTRACE_CAP) - unsafe |= LSM_UNSAFE_PTRACE_CAP; - else - unsafe |= LSM_UNSAFE_PTRACE; - } + int unsafe = tracehook_unsafe_exec(p); + if (atomic_read(&p->fs->count) > 1 || atomic_read(&p->files->count) > 1 || atomic_read(&p->sighand->count) > 1) @@ -1214,6 +1209,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) read_unlock(&binfmt_lock); retval = fn(bprm, regs); if (retval >= 0) { + tracehook_report_exec(fmt, bprm, regs); put_binfmt(fmt); allow_write_access(bprm->file); if (bprm->file) diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index bea0f3eeff54..6276353709c1 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -48,5 +48,51 @@ #include #include +#include +struct linux_binprm; + +/** + * tracehook_unsafe_exec - check for exec declared unsafe due to tracing + * @task: current task doing exec + * + * Return %LSM_UNSAFE_* bits applied to an exec because of tracing. + * + * Called with task_lock() held on @task. + */ +static inline int tracehook_unsafe_exec(struct task_struct *task) +{ + int unsafe = 0; + int ptrace = task_ptrace(task); + if (ptrace & PT_PTRACED) { + if (ptrace & PT_PTRACE_CAP) + unsafe |= LSM_UNSAFE_PTRACE_CAP; + else + unsafe |= LSM_UNSAFE_PTRACE; + } + return unsafe; +} + +/** + * tracehook_report_exec - a successful exec was completed + * @fmt: &struct linux_binfmt that performed the exec + * @bprm: &struct linux_binprm containing exec details + * @regs: user-mode register state + * + * An exec just completed, we are shortly going to return to user mode. + * The freshly initialized register state can be seen and changed in @regs. + * The name, file and other pointers in @bprm are still on hand to be + * inspected, but will be freed as soon as this returns. + * + * Called with no locks, but with some kernel resources held live + * and a reference on @fmt->module. + */ +static inline void tracehook_report_exec(struct linux_binfmt *fmt, + struct linux_binprm *bprm, + struct pt_regs *regs) +{ + if (!ptrace_event(PT_TRACE_EXEC, PTRACE_EVENT_EXEC, 0) && + unlikely(task_ptrace(current) & PT_PTRACED)) + send_sig(SIGTRAP, current, 0); +} #endif /* */ -- cgit v1.2.3 From 30199f5a46aee204bf437a4f5b0740f3efe448b7 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:46 -0700 Subject: tracehook: exit This moves the PTRACE_EVENT_EXIT tracing into a tracehook.h inline, tracehook_report_exec(). The change has no effect, just clean-up. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 15 +++++++++++++++ kernel/exit.c | 6 ++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6276353709c1..967ab473afbc 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -95,4 +95,19 @@ static inline void tracehook_report_exec(struct linux_binfmt *fmt, send_sig(SIGTRAP, current, 0); } +/** + * tracehook_report_exit - task has begun to exit + * @exit_code: pointer to value destined for @current->exit_code + * + * @exit_code points to the value passed to do_exit(), which tracing + * might change here. This is almost the first thing in do_exit(), + * before freeing any resources or setting the %PF_EXITING flag. + * + * Called with no locks held. + */ +static inline void tracehook_report_exit(long *exit_code) +{ + ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code); +} + #endif /* */ diff --git a/kernel/exit.c b/kernel/exit.c index ad933bb29ec7..c3691cbc220a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -1029,10 +1030,7 @@ NORET_TYPE void do_exit(long code) if (unlikely(!tsk->pid)) panic("Attempted to kill the idle task!"); - if (unlikely(current->ptrace & PT_TRACE_EXIT)) { - current->ptrace_message = code; - ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); - } + tracehook_report_exit(&code); /* * We're taking recursive faults here in do_exit. Safest is to just -- cgit v1.2.3 From 09a05394fe2448a4139b014936330af23fa7ec83 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:47 -0700 Subject: tracehook: clone This moves all the ptrace initialization and tracing logic for task creation into tracehook.h and ptrace.h inlines. It reorganizes the code slightly, but should not change any behavior. There are four tracehook entry points, at each important stage of task creation. This keeps the interface from the core fork.c code fairly clean, while supporting the complex setup required for ptrace or something like it. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 22 ++++++++++ include/linux/tracehook.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/fork.c | 69 +++++++++++++------------------- 3 files changed, 150 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c74abfc4c7e8..dae6d85520fb 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -154,6 +154,28 @@ static inline int ptrace_event(int mask, int event, unsigned long message) return 1; } +/** + * ptrace_init_task - initialize ptrace state for a new child + * @child: new child task + * @ptrace: true if child should be ptrace'd by parent's tracer + * + * This is called immediately after adding @child to its parent's children + * list. @ptrace is false in the normal case, and true to ptrace @child. + * + * Called with current's siglock and write_lock_irq(&tasklist_lock) held. + */ +static inline void ptrace_init_task(struct task_struct *child, bool ptrace) +{ + INIT_LIST_HEAD(&child->ptrace_entry); + INIT_LIST_HEAD(&child->ptraced); + child->parent = child->real_parent; + child->ptrace = 0; + if (unlikely(ptrace)) { + child->ptrace = current->ptrace; + __ptrace_link(child, current->parent); + } +} + #ifndef force_successful_syscall_return /* * System call handlers that, upon successful completion, need to return a diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 967ab473afbc..3ebc58b59766 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -110,4 +110,104 @@ static inline void tracehook_report_exit(long *exit_code) ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code); } +/** + * tracehook_prepare_clone - prepare for new child to be cloned + * @clone_flags: %CLONE_* flags from clone/fork/vfork system call + * + * This is called before a new user task is to be cloned. + * Its return value will be passed to tracehook_finish_clone(). + * + * Called with no locks held. + */ +static inline int tracehook_prepare_clone(unsigned clone_flags) +{ + if (clone_flags & CLONE_UNTRACED) + return 0; + + if (clone_flags & CLONE_VFORK) { + if (current->ptrace & PT_TRACE_VFORK) + return PTRACE_EVENT_VFORK; + } else if ((clone_flags & CSIGNAL) != SIGCHLD) { + if (current->ptrace & PT_TRACE_CLONE) + return PTRACE_EVENT_CLONE; + } else if (current->ptrace & PT_TRACE_FORK) + return PTRACE_EVENT_FORK; + + return 0; +} + +/** + * tracehook_finish_clone - new child created and being attached + * @child: new child task + * @clone_flags: %CLONE_* flags from clone/fork/vfork system call + * @trace: return value from tracehook_clone_prepare() + * + * This is called immediately after adding @child to its parent's children list. + * The @trace value is that returned by tracehook_prepare_clone(). + * + * Called with current's siglock and write_lock_irq(&tasklist_lock) held. + */ +static inline void tracehook_finish_clone(struct task_struct *child, + unsigned long clone_flags, int trace) +{ + ptrace_init_task(child, (clone_flags & CLONE_PTRACE) || trace); +} + +/** + * tracehook_report_clone - in parent, new child is about to start running + * @trace: return value from tracehook_clone_prepare() + * @regs: parent's user register state + * @clone_flags: flags from parent's system call + * @pid: new child's PID in the parent's namespace + * @child: new child task + * + * Called after a child is set up, but before it has been started running. + * The @trace value is that returned by tracehook_clone_prepare(). + * This is not a good place to block, because the child has not started yet. + * Suspend the child here if desired, and block in tracehook_clone_complete(). + * This must prevent the child from self-reaping if tracehook_clone_complete() + * uses the @child pointer; otherwise it might have died and been released by + * the time tracehook_report_clone_complete() is called. + * + * Called with no locks held, but the child cannot run until this returns. + */ +static inline void tracehook_report_clone(int trace, struct pt_regs *regs, + unsigned long clone_flags, + pid_t pid, struct task_struct *child) +{ + if (unlikely(trace)) { + /* + * The child starts up with an immediate SIGSTOP. + */ + sigaddset(&child->pending.signal, SIGSTOP); + set_tsk_thread_flag(child, TIF_SIGPENDING); + } +} + +/** + * tracehook_report_clone_complete - new child is running + * @trace: return value from tracehook_clone_prepare() + * @regs: parent's user register state + * @clone_flags: flags from parent's system call + * @pid: new child's PID in the parent's namespace + * @child: child task, already running + * + * This is called just after the child has started running. This is + * just before the clone/fork syscall returns, or blocks for vfork + * child completion if @clone_flags has the %CLONE_VFORK bit set. + * The @child pointer may be invalid if a self-reaping child died and + * tracehook_report_clone() took no action to prevent it from self-reaping. + * + * Called with no locks held. + */ +static inline void tracehook_report_clone_complete(int trace, + struct pt_regs *regs, + unsigned long clone_flags, + pid_t pid, + struct task_struct *child) +{ + if (unlikely(trace)) + ptrace_event(0, trace, pid); +} + #endif /* */ diff --git a/kernel/fork.c b/kernel/fork.c index 80e83e459b17..b42f8ed23611 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -865,8 +866,7 @@ static void copy_flags(unsigned long clone_flags, struct task_struct *p) new_flags &= ~PF_SUPERPRIV; new_flags |= PF_FORKNOEXEC; - if (!(clone_flags & CLONE_PTRACE)) - p->ptrace = 0; + new_flags |= PF_STARTING; p->flags = new_flags; clear_freeze_flag(p); } @@ -907,7 +907,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, struct pt_regs *regs, unsigned long stack_size, int __user *child_tidptr, - struct pid *pid) + struct pid *pid, + int trace) { int retval; struct task_struct *p; @@ -1163,8 +1164,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, */ p->group_leader = p; INIT_LIST_HEAD(&p->thread_group); - INIT_LIST_HEAD(&p->ptrace_entry); - INIT_LIST_HEAD(&p->ptraced); /* Now that the task is set up, run cgroup callbacks if * necessary. We need to run them before the task is visible @@ -1195,7 +1194,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->real_parent = current->real_parent; else p->real_parent = current; - p->parent = p->real_parent; spin_lock(¤t->sighand->siglock); @@ -1237,8 +1235,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (likely(p->pid)) { list_add_tail(&p->sibling, &p->real_parent->children); - if (unlikely(p->ptrace & PT_PTRACED)) - __ptrace_link(p, current->parent); + tracehook_finish_clone(p, clone_flags, trace); if (thread_group_leader(p)) { if (clone_flags & CLONE_NEWPID) @@ -1323,29 +1320,13 @@ struct task_struct * __cpuinit fork_idle(int cpu) struct pt_regs regs; task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, - &init_struct_pid); + &init_struct_pid, 0); if (!IS_ERR(task)) init_idle(task, cpu); return task; } -static int fork_traceflag(unsigned clone_flags) -{ - if (clone_flags & CLONE_UNTRACED) - return 0; - else if (clone_flags & CLONE_VFORK) { - if (current->ptrace & PT_TRACE_VFORK) - return PTRACE_EVENT_VFORK; - } else if ((clone_flags & CSIGNAL) != SIGCHLD) { - if (current->ptrace & PT_TRACE_CLONE) - return PTRACE_EVENT_CLONE; - } else if (current->ptrace & PT_TRACE_FORK) - return PTRACE_EVENT_FORK; - - return 0; -} - /* * Ok, this is the main fork-routine. * @@ -1380,14 +1361,14 @@ long do_fork(unsigned long clone_flags, } } - if (unlikely(current->ptrace)) { - trace = fork_traceflag (clone_flags); - if (trace) - clone_flags |= CLONE_PTRACE; - } + /* + * When called from kernel_thread, don't do user tracing stuff. + */ + if (likely(user_mode(regs))) + trace = tracehook_prepare_clone(clone_flags); p = copy_process(clone_flags, stack_start, regs, stack_size, - child_tidptr, NULL); + child_tidptr, NULL, trace); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. @@ -1405,24 +1386,30 @@ long do_fork(unsigned long clone_flags, init_completion(&vfork); } - if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) { + tracehook_report_clone(trace, regs, clone_flags, nr, p); + + /* + * We set PF_STARTING at creation in case tracing wants to + * use this to distinguish a fully live task from one that + * hasn't gotten to tracehook_report_clone() yet. Now we + * clear it and set the child going. + */ + p->flags &= ~PF_STARTING; + + if (unlikely(clone_flags & CLONE_STOPPED)) { /* * We'll start up with an immediate SIGSTOP. */ sigaddset(&p->pending.signal, SIGSTOP); set_tsk_thread_flag(p, TIF_SIGPENDING); - } - - if (!(clone_flags & CLONE_STOPPED)) - wake_up_new_task(p, clone_flags); - else __set_task_state(p, TASK_STOPPED); - - if (unlikely (trace)) { - current->ptrace_message = nr; - ptrace_notify ((trace << 8) | SIGTRAP); + } else { + wake_up_new_task(p, clone_flags); } + tracehook_report_clone_complete(trace, regs, + clone_flags, nr, p); + if (clone_flags & CLONE_VFORK) { freezer_do_not_count(); wait_for_completion(&vfork); -- cgit v1.2.3 From daded34be96b1975ff8539ff62ad8b158ce7d842 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:47 -0700 Subject: tracehook: vfork-done This moves the PTRACE_EVENT_VFORK_DONE tracing into a tracehook.h inline, tracehook_report_vfork_done(). The change has no effect, just clean-up. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 18 ++++++++++++++++++ kernel/fork.c | 5 +---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 3ebc58b59766..830e6e16097d 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -210,4 +210,22 @@ static inline void tracehook_report_clone_complete(int trace, ptrace_event(0, trace, pid); } +/** + * tracehook_report_vfork_done - vfork parent's child has exited or exec'd + * @child: child task, already running + * @pid: new child's PID in the parent's namespace + * + * Called after a %CLONE_VFORK parent has waited for the child to complete. + * The clone/vfork system call will return immediately after this. + * The @child pointer may be invalid if a self-reaping child died and + * tracehook_report_clone() took no action to prevent it from self-reaping. + * + * Called with no locks held. + */ +static inline void tracehook_report_vfork_done(struct task_struct *child, + pid_t pid) +{ + ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid); +} + #endif /* */ diff --git a/kernel/fork.c b/kernel/fork.c index b42f8ed23611..abb3ed6298f6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1414,10 +1414,7 @@ long do_fork(unsigned long clone_flags, freezer_do_not_count(); wait_for_completion(&vfork); freezer_count(); - if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) { - current->ptrace_message = nr; - ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); - } + tracehook_report_vfork_done(p, nr); } } else { nr = PTR_ERR(p); -- cgit v1.2.3 From dae33574dcf5211e1f43c7e45fa29f73ba3e00cb Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:48 -0700 Subject: tracehook: release_task This moves the ptrace-related logic from release_task into tracehook.h and ptrace.h inlines. It provides clean hooks both before and after locking tasklist_lock, for future tracing logic to do more cleanup without the lock. This also changes release_task() itself in the rare "zap_leader" case to set the leader to EXIT_DEAD before iterating. This maintains the invariant that release_task() only ever handles a task in EXIT_DEAD. This is a common-sense invariant that is already always true except in this one arcane case of zombie leader whose parent ignores SIGCHLD. This change is harmless and only costs one store in this one rare case. It keeps the expected state more consisently sane, which is nicer when debugging weirdness in release_task(). It also lets some future code in the tracehook entry points rely on this invariant for bookkeeping. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 13 +++++++++++++ include/linux/tracehook.h | 28 ++++++++++++++++++++++++++++ kernel/exit.c | 21 +++++++++------------ 3 files changed, 50 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index dae6d85520fb..ed69c03692d9 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -176,6 +176,19 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace) } } +/** + * ptrace_release_task - final ptrace-related cleanup of a zombie being reaped + * @task: task in %EXIT_DEAD state + * + * Called with write_lock(&tasklist_lock) held. + */ +static inline void ptrace_release_task(struct task_struct *task) +{ + BUG_ON(!list_empty(&task->ptraced)); + ptrace_unlink(task); + BUG_ON(!list_empty(&task->ptrace_entry)); +} + #ifndef force_successful_syscall_return /* * System call handlers that, upon successful completion, need to return a diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 830e6e16097d..9a5b3be2503a 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -228,4 +228,32 @@ static inline void tracehook_report_vfork_done(struct task_struct *child, ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid); } +/** + * tracehook_prepare_release_task - task is being reaped, clean up tracing + * @task: task in %EXIT_DEAD state + * + * This is called in release_task() just before @task gets finally reaped + * and freed. This would be the ideal place to remove and clean up any + * tracing-related state for @task. + * + * Called with no locks held. + */ +static inline void tracehook_prepare_release_task(struct task_struct *task) +{ +} + +/** + * tracehook_finish_release_task - task is being reaped, clean up tracing + * @task: task in %EXIT_DEAD state + * + * This is called in release_task() when @task is being in the middle of + * being reaped. After this, there must be no tracing entanglements. + * + * Called with write_lock_irq(&tasklist_lock) held. + */ +static inline void tracehook_finish_release_task(struct task_struct *task) +{ + ptrace_release_task(task); +} + #endif /* */ diff --git a/kernel/exit.c b/kernel/exit.c index c3691cbc220a..da28745f7c38 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -163,27 +163,17 @@ static void delayed_put_task_struct(struct rcu_head *rhp) put_task_struct(container_of(rhp, struct task_struct, rcu)); } -/* - * Do final ptrace-related cleanup of a zombie being reaped. - * - * Called with write_lock(&tasklist_lock) held. - */ -static void ptrace_release_task(struct task_struct *p) -{ - BUG_ON(!list_empty(&p->ptraced)); - ptrace_unlink(p); - BUG_ON(!list_empty(&p->ptrace_entry)); -} void release_task(struct task_struct * p) { struct task_struct *leader; int zap_leader; repeat: + tracehook_prepare_release_task(p); atomic_dec(&p->user->processes); proc_flush_task(p); write_lock_irq(&tasklist_lock); - ptrace_release_task(p); + tracehook_finish_release_task(p); __exit_signal(p); /* @@ -205,6 +195,13 @@ repeat: * that case. */ zap_leader = task_detached(leader); + + /* + * This maintains the invariant that release_task() + * only runs on a task in EXIT_DEAD, just for sanity. + */ + if (zap_leader) + leader->exit_state = EXIT_DEAD; } write_unlock_irq(&tasklist_lock); -- cgit v1.2.3 From 0d094efeb1e98010c6b99923f1eb7e17bf1e3a74 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:49 -0700 Subject: tracehook: tracehook_tracer_task This adds the tracehook_tracer_task() hook to consolidate all forms of "Who is using ptrace on me?" logic. This is used for "TracerPid:" in /proc and for permission checks. We also clean up the selinux code the called an identical accessor. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/array.c | 9 +++++++-- fs/proc/base.c | 13 +++++++++---- include/linux/tracehook.h | 18 ++++++++++++++++++ security/selinux/hooks.c | 22 +++------------------- 4 files changed, 37 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/array.c b/fs/proc/array.c index 797d775e0354..0d6eb33597c6 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -168,8 +169,12 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, rcu_read_lock(); ppid = pid_alive(p) ? task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; - tpid = pid_alive(p) && p->ptrace ? - task_pid_nr_ns(rcu_dereference(p->parent), ns) : 0; + tpid = 0; + if (pid_alive(p)) { + struct task_struct *tracer = tracehook_tracer_task(p); + if (tracer) + tpid = task_pid_nr_ns(tracer, ns); + } seq_printf(m, "State:\t%s\n" "Tgid:\t%d\n" diff --git a/fs/proc/base.c b/fs/proc/base.c index a891fe4cb43b..4b74dba69a6d 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -231,10 +232,14 @@ static int check_mem_permission(struct task_struct *task) * If current is actively ptrace'ing, and would also be * permitted to freshly attach with ptrace now, permit it. */ - if (task->parent == current && (task->ptrace & PT_PTRACED) && - task_is_stopped_or_traced(task) && - ptrace_may_access(task, PTRACE_MODE_ATTACH)) - return 0; + if (task_is_stopped_or_traced(task)) { + int match; + rcu_read_lock(); + match = (tracehook_tracer_task(task) == current); + rcu_read_unlock(); + if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) + return 0; + } /* * Noone else is allowed. diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 9a5b3be2503a..6468ca0fe69b 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -72,6 +72,24 @@ static inline int tracehook_unsafe_exec(struct task_struct *task) return unsafe; } +/** + * tracehook_tracer_task - return the task that is tracing the given task + * @tsk: task to consider + * + * Returns NULL if noone is tracing @task, or the &struct task_struct + * pointer to its tracer. + * + * Must called under rcu_read_lock(). The pointer returned might be kept + * live only by RCU. During exec, this may be called with task_lock() + * held on @task, still held from when tracehook_unsafe_exec() was called. + */ +static inline struct task_struct *tracehook_tracer_task(struct task_struct *tsk) +{ + if (task_ptrace(tsk) & PT_PTRACED) + return rcu_dereference(tsk->parent); + return NULL; +} + /** * tracehook_report_exec - a successful exec was completed * @fmt: &struct linux_binfmt that performed the exec diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 63f131fc42e4..3481cde5bf15 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -1971,22 +1971,6 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) return __vm_enough_memory(mm, pages, cap_sys_admin); } -/** - * task_tracer_task - return the task that is tracing the given task - * @task: task to consider - * - * Returns NULL if noone is tracing @task, or the &struct task_struct - * pointer to its tracer. - * - * Must be called under rcu_read_lock(). - */ -static struct task_struct *task_tracer_task(struct task_struct *task) -{ - if (task->ptrace & PT_PTRACED) - return rcu_dereference(task->parent); - return NULL; -} - /* binprm security operations */ static int selinux_bprm_alloc_security(struct linux_binprm *bprm) @@ -2238,7 +2222,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) u32 ptsid = 0; rcu_read_lock(); - tracer = task_tracer_task(current); + tracer = tracehook_tracer_task(current); if (likely(tracer != NULL)) { sec = tracer->security; ptsid = sec->sid; @@ -5247,7 +5231,7 @@ static int selinux_setprocattr(struct task_struct *p, Otherwise, leave SID unchanged and fail. */ task_lock(p); rcu_read_lock(); - tracer = task_tracer_task(p); + tracer = tracehook_tracer_task(p); if (tracer != NULL) { struct task_security_struct *ptsec = tracer->security; u32 ptsid = ptsec->sid; -- cgit v1.2.3 From fa8e26ccd485216fc45c8c2dd1ec3b7ef1a0a2f8 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:50 -0700 Subject: tracehook: tracehook_expect_breakpoints This adds tracehook_expect_breakpoints() as a formal hook for the nommu code to use for its, "Is text-poking likely?" check at mmap time. This names the actual semantics the code means to test, and documents it. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 15 +++++++++++++++ mm/nommu.c | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6468ca0fe69b..e113e09b0341 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -51,6 +51,21 @@ #include struct linux_binprm; +/** + * tracehook_expect_breakpoints - guess if task memory might be touched + * @task: current task, making a new mapping + * + * Return nonzero if @task is expected to want breakpoint insertion in + * its memory at some point. A zero return is no guarantee it won't + * be done, but this is a hint that it's known to be likely. + * + * May be called with @task->mm->mmap_sem held for writing. + */ +static inline int tracehook_expect_breakpoints(struct task_struct *task) +{ + return (task_ptrace(task) & PT_PTRACED) != 0; +} + /** * tracehook_unsafe_exec - check for exec declared unsafe due to tracing * @task: current task doing exec diff --git a/mm/nommu.c b/mm/nommu.c index 4462b6a3fcb9..5edccd9c9218 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -745,7 +745,7 @@ static unsigned long determine_vm_flags(struct file *file, * it's being traced - otherwise breakpoints set in it may interfere * with another untraced process */ - if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED)) + if ((flags & MAP_PRIVATE) && tracehook_expect_breakpoints(current)) vm_flags &= ~VM_MAYSHARE; return vm_flags; -- cgit v1.2.3 From c45aea27617d6a1e0aacddc3b0233f704222fcbd Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:50 -0700 Subject: tracehook: tracehook_signal_handler This defines tracehook_signal_handler() as a hook for the arch signal handling code to call. It gives ptrace the opportunity to stop for a pseudo-single-step trap immediately after signal handler setup is done. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index e113e09b0341..2d1426f8e33b 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -289,4 +289,27 @@ static inline void tracehook_finish_release_task(struct task_struct *task) ptrace_release_task(task); } +/** + * tracehook_signal_handler - signal handler setup is complete + * @sig: number of signal being delivered + * @info: siginfo_t of signal being delivered + * @ka: sigaction setting that chose the handler + * @regs: user register state + * @stepping: nonzero if debugger single-step or block-step in use + * + * Called by the arch code after a signal handler has been set up. + * Register and stack state reflects the user handler about to run. + * Signal mask changes have already been made. + * + * Called without locks, shortly before returning to user mode + * (or handling more signals). + */ +static inline void tracehook_signal_handler(int sig, siginfo_t *info, + const struct k_sigaction *ka, + struct pt_regs *regs, int stepping) +{ + if (stepping) + ptrace_notify(SIGTRAP); +} + #endif /* */ -- cgit v1.2.3 From 35de254dc60f91004b3b5ebb1fc7b2c3093d6032 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:51 -0700 Subject: tracehook: tracehook_consider_ignored_signal This defines tracehook_consider_ignored_signal() has a fine-grained hook for deciding to prevent the normal short-circuit of sending an ignored signal, as ptrace does. There is no change, only cleanup. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 19 +++++++++++++++++++ kernel/signal.c | 27 ++++++++++++++++----------- 2 files changed, 35 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 2d1426f8e33b..8cffd34f88d5 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -312,4 +312,23 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info, ptrace_notify(SIGTRAP); } +/** + * tracehook_consider_ignored_signal - suppress short-circuit of ignored signal + * @task: task receiving the signal + * @sig: signal number being sent + * @handler: %SIG_IGN or %SIG_DFL + * + * Return zero iff tracing doesn't care to examine this ignored signal, + * so it can short-circuit normal delivery and never even get queued. + * Either @handler is %SIG_DFL and @sig's default is ignore, or it's %SIG_IGN. + * + * Called with @task->sighand->siglock held. + */ +static inline int tracehook_consider_ignored_signal(struct task_struct *task, + int sig, + void __user *handler) +{ + return (task_ptrace(task) & PT_PTRACED) != 0; +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 8715c18b27b9..9efd1cee6d0b 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -39,24 +40,21 @@ static struct kmem_cache *sigqueue_cachep; -static int __sig_ignored(struct task_struct *t, int sig) +static void __user *sig_handler(struct task_struct *t, int sig) { - void __user *handler; + return t->sighand->action[sig - 1].sa.sa_handler; +} +static int sig_handler_ignored(void __user *handler, int sig) +{ /* Is it explicitly or implicitly ignored? */ - - handler = t->sighand->action[sig - 1].sa.sa_handler; return handler == SIG_IGN || (handler == SIG_DFL && sig_kernel_ignore(sig)); } static int sig_ignored(struct task_struct *t, int sig) { - /* - * Tracers always want to know about signals.. - */ - if (t->ptrace & PT_PTRACED) - return 0; + void __user *handler; /* * Blocked signals are never ignored, since the @@ -66,7 +64,14 @@ static int sig_ignored(struct task_struct *t, int sig) if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig)) return 0; - return __sig_ignored(t, sig); + handler = sig_handler(t, sig); + if (!sig_handler_ignored(handler, sig)) + return 0; + + /* + * Tracers may want to know about even ignored signals. + */ + return !tracehook_consider_ignored_signal(t, sig, handler); } /* @@ -2298,7 +2303,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) * (for example, SIGCHLD), shall cause the pending signal to * be discarded, whether or not it is blocked" */ - if (__sig_ignored(t, sig)) { + if (sig_handler_ignored(sig_handler(t, sig), sig)) { sigemptyset(&mask); sigaddset(&mask, sig); rm_from_queue_full(&mask, &t->signal->shared_pending); -- cgit v1.2.3 From 445a91d2fe3667fb8fc251433645f686933cf56a Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:52 -0700 Subject: tracehook: tracehook_consider_fatal_signal This defines tracehook_consider_fatal_signal() has a fine-grained hook for deciding to skip the special cases for a fatal signal, as ptrace does. There is no change, only cleanup. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 21 +++++++++++++++++++++ kernel/signal.c | 9 +++++---- 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 8cffd34f88d5..8b4c15e208fe 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -331,4 +331,25 @@ static inline int tracehook_consider_ignored_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } +/** + * tracehook_consider_fatal_signal - suppress special handling of fatal signal + * @task: task receiving the signal + * @sig: signal number being sent + * @handler: %SIG_DFL or %SIG_IGN + * + * Return nonzero to prevent special handling of this termination signal. + * Normally @handler is %SIG_DFL. It can be %SIG_IGN if @sig is ignored, + * in which case force_sig() is about to reset it to %SIG_DFL. + * When this returns zero, this signal might cause a quick termination + * that does not give the debugger a chance to intercept the signal. + * + * Called with or without @task->sighand->siglock held. + */ +static inline int tracehook_consider_fatal_signal(struct task_struct *task, + int sig, + void __user *handler) +{ + return (task_ptrace(task) & PT_PTRACED) != 0; +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 9efd1cee6d0b..1a942ce32ba0 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -300,12 +300,12 @@ flush_signal_handlers(struct task_struct *t, int force_default) int unhandled_signal(struct task_struct *tsk, int sig) { + void __user *handler = tsk->sighand->action[sig-1].sa.sa_handler; if (is_global_init(tsk)) return 1; - if (tsk->ptrace & PT_PTRACED) + if (handler != SIG_IGN && handler != SIG_DFL) return 0; - return (tsk->sighand->action[sig-1].sa.sa_handler == SIG_IGN) || - (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL); + return !tracehook_consider_fatal_signal(tsk, sig, handler); } @@ -761,7 +761,8 @@ static void complete_signal(int sig, struct task_struct *p, int group) if (sig_fatal(p, sig) && !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) && !sigismember(&t->real_blocked, sig) && - (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) { + (sig == SIGKILL || + !tracehook_consider_fatal_signal(t, sig, SIG_DFL))) { /* * This signal will be fatal to the whole group. */ -- cgit v1.2.3 From 283d7559e7712f95a05331eb0a85394c6368101b Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:52 -0700 Subject: tracehook: syscall This adds standard tracehook.h inlines for arch code to call when TIF_SYSCALL_TRACE has been set. This replaces having each arch implement the ptrace guts for its syscall tracing support. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 8b4c15e208fe..3548694a24db 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -66,6 +66,76 @@ static inline int tracehook_expect_breakpoints(struct task_struct *task) return (task_ptrace(task) & PT_PTRACED) != 0; } +/* + * ptrace report for syscall entry and exit looks identical. + */ +static inline void ptrace_report_syscall(struct pt_regs *regs) +{ + int ptrace = task_ptrace(current); + + if (!(ptrace & PT_PTRACED)) + return; + + ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + +/** + * tracehook_report_syscall_entry - task is about to attempt a system call + * @regs: user register state of current task + * + * This will be called if %TIF_SYSCALL_TRACE has been set, when the + * current task has just entered the kernel for a system call. + * Full user register state is available here. Changing the values + * in @regs can affect the system call number and arguments to be tried. + * It is safe to block here, preventing the system call from beginning. + * + * Returns zero normally, or nonzero if the calling arch code should abort + * the system call. That must prevent normal entry so no system call is + * made. If @task ever returns to user mode after this, its register state + * is unspecified, but should be something harmless like an %ENOSYS error + * return. + * + * Called without locks, just after entering kernel mode. + */ +static inline __must_check int tracehook_report_syscall_entry( + struct pt_regs *regs) +{ + ptrace_report_syscall(regs); + return 0; +} + +/** + * tracehook_report_syscall_exit - task has just finished a system call + * @regs: user register state of current task + * @step: nonzero if simulating single-step or block-step + * + * This will be called if %TIF_SYSCALL_TRACE has been set, when the + * current task has just finished an attempted system call. Full + * user register state is available here. It is safe to block here, + * preventing signals from being processed. + * + * If @step is nonzero, this report is also in lieu of the normal + * trap that would follow the system call instruction because + * user_enable_block_step() or user_enable_single_step() was used. + * In this case, %TIF_SYSCALL_TRACE might not be set. + * + * Called without locks, just before checking for pending signals. + */ +static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step) +{ + ptrace_report_syscall(regs); +} + /** * tracehook_unsafe_exec - check for exec declared unsafe due to tracing * @task: current task doing exec -- cgit v1.2.3 From 7bcf6a2ca5f639b038c48711ebe6c4eca2036641 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:53 -0700 Subject: tracehook: get_signal_to_deliver This defines the tracehook_get_signal() hook to allow tracing code to slip in before normal signal dequeuing. This lays the groundwork for new tracing features that can inject synthetic signals outside the normal queue or control the disposition of delivered signals. The calling convention lets tracehook_get_signal() decide both exactly what will happen and what signal number to report in the handler/exit. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 29 +++++++++++++++++++++++++++++ kernel/signal.c | 38 +++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 3548694a24db..42a0d7b11959 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -422,4 +422,33 @@ static inline int tracehook_consider_fatal_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } +/** + * tracehook_get_signal - deliver synthetic signal to traced task + * @task: @current + * @regs: task_pt_regs(@current) + * @info: details of synthetic signal + * @return_ka: sigaction for synthetic signal + * + * Return zero to check for a real pending signal normally. + * Return -1 after releasing the siglock to repeat the check. + * Return a signal number to induce an artifical signal delivery, + * setting *@info and *@return_ka to specify its details and behavior. + * + * The @return_ka->sa_handler value controls the disposition of the + * signal, no matter the signal number. For %SIG_DFL, the return value + * is a representative signal to indicate the behavior (e.g. %SIGTERM + * for death, %SIGQUIT for core dump, %SIGSTOP for job control stop, + * %SIGTSTP for stop unless in an orphaned pgrp), but the signal number + * reported will be @info->si_signo instead. + * + * Called with @task->sighand->siglock held, before dequeuing pending signals. + */ +static inline int tracehook_get_signal(struct task_struct *task, + struct pt_regs *regs, + siginfo_t *info, + struct k_sigaction *return_ka) +{ + return 0; +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 1a942ce32ba0..10b31ecdd9c8 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1754,17 +1754,33 @@ relock: do_signal_stop(0)) goto relock; - signr = dequeue_signal(current, ¤t->blocked, info); - if (!signr) - break; /* will return 0 */ + /* + * Tracing can induce an artifical signal and choose sigaction. + * The return value in @signr determines the default action, + * but @info->si_signo is the signal number we will report. + */ + signr = tracehook_get_signal(current, regs, info, return_ka); + if (unlikely(signr < 0)) + goto relock; + if (unlikely(signr != 0)) + ka = return_ka; + else { + signr = dequeue_signal(current, ¤t->blocked, + info); - if (signr != SIGKILL) { - signr = ptrace_signal(signr, info, regs, cookie); if (!signr) - continue; + break; /* will return 0 */ + + if (signr != SIGKILL) { + signr = ptrace_signal(signr, info, + regs, cookie); + if (!signr) + continue; + } + + ka = &sighand->action[signr-1]; } - ka = &sighand->action[signr-1]; if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */ continue; if (ka->sa.sa_handler != SIG_DFL) { @@ -1812,7 +1828,7 @@ relock: spin_lock_irq(&sighand->siglock); } - if (likely(do_signal_stop(signr))) { + if (likely(do_signal_stop(info->si_signo))) { /* It released the siglock. */ goto relock; } @@ -1833,7 +1849,7 @@ relock: if (sig_kernel_coredump(signr)) { if (print_fatal_signals) - print_fatal_signal(regs, signr); + print_fatal_signal(regs, info->si_signo); /* * If it was able to dump core, this kills all * other threads in the group and synchronizes with @@ -1842,13 +1858,13 @@ relock: * first and our do_group_exit call below will use * that value and ignore the one we pass it. */ - do_coredump((long)signr, signr, regs); + do_coredump(info->si_signo, info->si_signo, regs); } /* * Death signals, no core dump. */ - do_group_exit(signr); + do_group_exit(info->si_signo); /* NOTREACHED */ } spin_unlock_irq(&sighand->siglock); -- cgit v1.2.3 From fa00b80b3c41a845b3d56f866fb40a2e98754c51 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:54 -0700 Subject: tracehook: job control This defines the tracehook_notify_jctl() hook to formalize the ptrace effects on the job control notifications. There is no change, only cleanup. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 20 ++++++++++++++++++++ kernel/signal.c | 10 +++++----- 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 42a0d7b11959..6dc428dd2f38 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -451,4 +451,24 @@ static inline int tracehook_get_signal(struct task_struct *task, return 0; } +/** + * tracehook_notify_jctl - report about job control stop/continue + * @notify: nonzero if this is the last thread in the group to stop + * @why: %CLD_STOPPED or %CLD_CONTINUED + * + * This is called when we might call do_notify_parent_cldstop(). + * It's called when about to stop for job control; we are already in + * %TASK_STOPPED state, about to call schedule(). It's also called when + * a delayed %CLD_STOPPED or %CLD_CONTINUED report is ready to be made. + * + * Return nonzero to generate a %SIGCHLD with @why, which is + * normal if @notify is nonzero. + * + * Called with no locks held. + */ +static inline int tracehook_notify_jctl(int notify, int why) +{ + return notify || (current->ptrace & PT_PTRACED); +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 10b31ecdd9c8..e9e699f4b1bd 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -596,9 +596,6 @@ static int check_kill_permission(int sig, struct siginfo *info, return security_task_kill(t, info, sig, 0); } -/* forward decl */ -static void do_notify_parent_cldstop(struct task_struct *tsk, int why); - /* * Handle magic process-wide effects of stop/continue signals. Unlike * the signal actions, these happen immediately at signal-generation @@ -1605,7 +1602,7 @@ finish_stop(int stop_count) * a group stop in progress and we are the last to stop, * report to the parent. When ptraced, every thread reports itself. */ - if (stop_count == 0 || (current->ptrace & PT_PTRACED)) { + if (tracehook_notify_jctl(stop_count == 0, CLD_STOPPED)) { read_lock(&tasklist_lock); do_notify_parent_cldstop(current, CLD_STOPPED); read_unlock(&tasklist_lock); @@ -1741,6 +1738,9 @@ relock: signal->flags &= ~SIGNAL_CLD_MASK; spin_unlock_irq(&sighand->siglock); + if (unlikely(!tracehook_notify_jctl(1, why))) + goto relock; + read_lock(&tasklist_lock); do_notify_parent_cldstop(current->group_leader, why); read_unlock(&tasklist_lock); @@ -1906,7 +1906,7 @@ void exit_signals(struct task_struct *tsk) out: spin_unlock_irq(&tsk->sighand->siglock); - if (unlikely(group_stop)) { + if (unlikely(group_stop) && tracehook_notify_jctl(1, CLD_STOPPED)) { read_lock(&tasklist_lock); do_notify_parent_cldstop(tsk, CLD_STOPPED); read_unlock(&tasklist_lock); -- cgit v1.2.3 From 2b2a1ff64afbadac842bbc58c5166962cf4f7664 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:54 -0700 Subject: tracehook: death This moves the ptrace logic in task death (exit_notify) into tracehook.h inlines. Some code is rearranged slightly to make things nicer. There is no change, only cleanup. There is one hook called with the tasklist_lock write-locked, as ptrace needs. There is also a new hook called after exit_state changes and without locks. This is a better place for tracing work to be in the future, since it doesn't delay the whole system with locking. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 2 +- include/linux/tracehook.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/exit.c | 26 ++++++++---------------- kernel/signal.c | 10 ++++++--- 4 files changed, 69 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index adb8077dc463..a95d84d0da95 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1796,7 +1796,7 @@ extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_ extern int kill_pgrp(struct pid *pid, int sig, int priv); extern int kill_pid(struct pid *pid, int sig, int priv); extern int kill_proc_info(int, struct siginfo *, pid_t); -extern void do_notify_parent(struct task_struct *, int); +extern int do_notify_parent(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); extern int send_sig(int, struct task_struct *, int); diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6dc428dd2f38..4c50e1b57349 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -471,4 +471,56 @@ static inline int tracehook_notify_jctl(int notify, int why) return notify || (current->ptrace & PT_PTRACED); } +/** + * tracehook_notify_death - task is dead, ready to notify parent + * @task: @current task now exiting + * @death_cookie: value to pass to tracehook_report_death() + * @group_dead: nonzero if this was the last thread in the group to die + * + * Return the signal number to send our parent with do_notify_parent(), or + * zero to send no signal and leave a zombie, or -1 to self-reap right now. + * + * Called with write_lock_irq(&tasklist_lock) held. + */ +static inline int tracehook_notify_death(struct task_struct *task, + void **death_cookie, int group_dead) +{ + if (task->exit_signal == -1) + return task->ptrace ? SIGCHLD : -1; + + /* + * If something other than our normal parent is ptracing us, then + * send it a SIGCHLD instead of honoring exit_signal. exit_signal + * only has special meaning to our real parent. + */ + if (thread_group_empty(task) && !ptrace_reparented(task)) + return task->exit_signal; + + return task->ptrace ? SIGCHLD : 0; +} + +/** + * tracehook_report_death - task is dead and ready to be reaped + * @task: @current task now exiting + * @signal: signal number sent to parent, or 0 or -1 + * @death_cookie: value passed back from tracehook_notify_death() + * @group_dead: nonzero if this was the last thread in the group to die + * + * Thread has just become a zombie or is about to self-reap. If positive, + * @signal is the signal number just sent to the parent (usually %SIGCHLD). + * If @signal is -1, this thread will self-reap. If @signal is 0, this is + * a delayed_group_leader() zombie. The @death_cookie was passed back by + * tracehook_notify_death(). + * + * If normal reaping is not inhibited, @task->exit_state might be changing + * in parallel. + * + * Called without locks. + */ +static inline void tracehook_report_death(struct task_struct *task, + int signal, void *death_cookie, + int group_dead) +{ +} + #endif /* */ diff --git a/kernel/exit.c b/kernel/exit.c index da28745f7c38..6cdf60712bd2 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -885,7 +885,8 @@ static void forget_original_parent(struct task_struct *father) */ static void exit_notify(struct task_struct *tsk, int group_dead) { - int state; + int signal; + void *cookie; /* * This does two things: @@ -922,22 +923,11 @@ static void exit_notify(struct task_struct *tsk, int group_dead) !capable(CAP_KILL)) tsk->exit_signal = SIGCHLD; - /* If something other than our normal parent is ptracing us, then - * send it a SIGCHLD instead of honoring exit_signal. exit_signal - * only has special meaning to our real parent. - */ - if (!task_detached(tsk) && thread_group_empty(tsk)) { - int signal = ptrace_reparented(tsk) ? - SIGCHLD : tsk->exit_signal; - do_notify_parent(tsk, signal); - } else if (tsk->ptrace) { - do_notify_parent(tsk, SIGCHLD); - } + signal = tracehook_notify_death(tsk, &cookie, group_dead); + if (signal > 0) + signal = do_notify_parent(tsk, signal); - state = EXIT_ZOMBIE; - if (task_detached(tsk) && likely(!tsk->ptrace)) - state = EXIT_DEAD; - tsk->exit_state = state; + tsk->exit_state = signal < 0 ? EXIT_DEAD : EXIT_ZOMBIE; /* mt-exec, de_thread() is waiting for us */ if (thread_group_leader(tsk) && @@ -947,8 +937,10 @@ static void exit_notify(struct task_struct *tsk, int group_dead) write_unlock_irq(&tasklist_lock); + tracehook_report_death(tsk, signal, cookie, group_dead); + /* If the process is dead, release it - nobody will wait for it */ - if (state == EXIT_DEAD) + if (signal < 0) release_task(tsk); } diff --git a/kernel/signal.c b/kernel/signal.c index e9e699f4b1bd..0e862d3130ff 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1326,9 +1326,11 @@ static inline void __wake_up_parent(struct task_struct *p, /* * Let a parent know about the death of a child. * For a stopped/continued status change, use do_notify_parent_cldstop instead. + * + * Returns -1 if our parent ignored us and so we've switched to + * self-reaping, or else @sig. */ - -void do_notify_parent(struct task_struct *tsk, int sig) +int do_notify_parent(struct task_struct *tsk, int sig) { struct siginfo info; unsigned long flags; @@ -1399,12 +1401,14 @@ void do_notify_parent(struct task_struct *tsk, int sig) */ tsk->exit_signal = -1; if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) - sig = 0; + sig = -1; } if (valid_signal(sig) && sig > 0) __group_send_sig_info(sig, &info, tsk->parent); __wake_up_parent(tsk, tsk->parent); spin_unlock_irqrestore(&psig->siglock, flags); + + return sig; } static void do_notify_parent_cldstop(struct task_struct *tsk, int why) -- cgit v1.2.3 From b787f7ba677840da16a2228c16571ce8a1fcb799 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:55 -0700 Subject: tracehook: force signal_pending() This defines a new hook tracehook_force_sigpending() that lets tracing code decide to force TIF_SIGPENDING on in recalc_sigpending(). This is not used yet, so it compiles away to nothing for now. It lays the groundwork for new tracing code that can interrupt a task synthetically without actually sending a signal. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 14 ++++++++++++++ kernel/signal.c | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 4c50e1b57349..43bc51b6bd33 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -422,6 +422,20 @@ static inline int tracehook_consider_fatal_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } +/** + * tracehook_force_sigpending - let tracing force signal_pending(current) on + * + * Called when recomputing our signal_pending() flag. Return nonzero + * to force the signal_pending() flag on, so that tracehook_get_signal() + * will be called before the next return to user mode. + * + * Called with @current->sighand->siglock held. + */ +static inline int tracehook_force_sigpending(void) +{ + return 0; +} + /** * tracehook_get_signal - deliver synthetic signal to traced task * @task: @current diff --git a/kernel/signal.c b/kernel/signal.c index 0e862d3130ff..954f77d7e3bc 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -134,7 +134,9 @@ void recalc_sigpending_and_wake(struct task_struct *t) void recalc_sigpending(void) { - if (!recalc_sigpending_tsk(current) && !freezing(current)) + if (unlikely(tracehook_force_sigpending())) + set_thread_flag(TIF_SIGPENDING); + else if (!recalc_sigpending_tsk(current) && !freezing(current)) clear_thread_flag(TIF_SIGPENDING); } -- cgit v1.2.3 From 64b1208d5b0ef8859fd52ea7ae286a3eb994669b Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:56 -0700 Subject: tracehook: TIF_NOTIFY_RESUME This adds tracehook.h inlines to enable a new arch feature in support of user debugging/tracing. This is not used yet, but it lays the groundwork for a debugger to be able to wrangle a task that's possibly running, without interrupting its syscalls in progress. Each arch should define TIF_NOTIFY_RESUME, and in their entry.S code treat it much like TIF_SIGPENDING. That is, it causes you to take the slow path when returning to user mode, where you get the full user-mode state accessible as for signal handling or ptrace. The arch code should check TIF_NOTIFY_RESUME after handling TIF_SIGPENDING. When it's set, clear it and then call tracehook_notify_resume(). In future, tracing code will call set_notify_resume() when it wants to get a callback in tracehook_notify_resume(). Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 43bc51b6bd33..32867ab86c70 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -537,4 +537,38 @@ static inline void tracehook_report_death(struct task_struct *task, { } +#ifdef TIF_NOTIFY_RESUME +/** + * set_notify_resume - cause tracehook_notify_resume() to be called + * @task: task that will call tracehook_notify_resume() + * + * Calling this arranges that @task will call tracehook_notify_resume() + * before returning to user mode. If it's already running in user mode, + * it will enter the kernel and call tracehook_notify_resume() soon. + * If it's blocked, it will not be woken. + */ +static inline void set_notify_resume(struct task_struct *task) +{ + if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_RESUME)) + kick_process(task); +} + +/** + * tracehook_notify_resume - report when about to return to user mode + * @regs: user-mode registers of @current task + * + * This is called when %TIF_NOTIFY_RESUME has been set. Now we are + * about to return to user mode, and the user state in @regs can be + * inspected or adjusted. The caller in arch code has cleared + * %TIF_NOTIFY_RESUME before the call. If the flag gets set again + * asynchronously, this will be called again before we return to + * user mode. + * + * Called without locks. + */ +static inline void tracehook_notify_resume(struct pt_regs *regs) +{ +} +#endif /* TIF_NOTIFY_RESUME */ + #endif /* */ -- cgit v1.2.3 From 828c365cc8b8d38c346fccb19fa80d28f2240831 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:57 -0700 Subject: tracehook: asm/syscall.h This adds asm-generic/syscall.h, which documents what a real asm-ARCH/syscall.h file should define. This is not used yet, but will provide all the machine-dependent details of examining a user system call about to begin, in progress, or just ended. Each arch should add an asm-ARCH/syscall.h that defines all the entry points documented in asm-generic/syscall.h, as short inlines if possible. This lets us write new tracing code that understands user system call registers, without any new arch-specific work. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/syscall.h | 141 ++++++++++++++++++++++++++++++++++++++++++ include/linux/tracehook.h | 3 +- 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 include/asm-generic/syscall.h (limited to 'include/linux') diff --git a/include/asm-generic/syscall.h b/include/asm-generic/syscall.h new file mode 100644 index 000000000000..abcf34c2fdc7 --- /dev/null +++ b/include/asm-generic/syscall.h @@ -0,0 +1,141 @@ +/* + * Access to user system call parameters and results + * + * Copyright (C) 2008 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * This file is a stub providing documentation for what functions + * asm-ARCH/syscall.h files need to define. Most arch definitions + * will be simple inlines. + * + * All of these functions expect to be called with no locks, + * and only when the caller is sure that the task of interest + * cannot return to user mode while we are looking at it. + */ + +#ifndef _ASM_SYSCALL_H +#define _ASM_SYSCALL_H 1 + +struct task_struct; +struct pt_regs; + +/** + * syscall_get_nr - find what system call a task is executing + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * + * If @task is executing a system call or is at system call + * tracing about to attempt one, returns the system call number. + * If @task is not executing a system call, i.e. it's blocked + * inside the kernel for a fault or signal, returns -1. + * + * It's only valid to call this when @task is known to be blocked. + */ +long syscall_get_nr(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_rollback - roll back registers after an aborted system call + * @task: task of interest, must be in system call exit tracing + * @regs: task_pt_regs() of @task + * + * It's only valid to call this when @task is stopped for system + * call exit tracing (due to TIF_SYSCALL_TRACE or TIF_SYSCALL_AUDIT), + * after tracehook_report_syscall_entry() returned nonzero to prevent + * the system call from taking place. + * + * This rolls back the register state in @regs so it's as if the + * system call instruction was a no-op. The registers containing + * the system call number and arguments are as they were before the + * system call instruction. This may not be the same as what the + * register state looked like at system call entry tracing. + */ +void syscall_rollback(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_get_error - check result of traced system call + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * + * Returns 0 if the system call succeeded, or -ERRORCODE if it failed. + * + * It's only valid to call this when @task is stopped for tracing on exit + * from a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + */ +long syscall_get_error(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_get_return_value - get the return value of a traced system call + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * + * Returns the return value of the successful system call. + * This value is meaningless if syscall_get_error() returned nonzero. + * + * It's only valid to call this when @task is stopped for tracing on exit + * from a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + */ +long syscall_get_return_value(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_set_return_value - change the return value of a traced system call + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * @error: negative error code, or zero to indicate success + * @val: user return value if @error is zero + * + * This changes the results of the system call that user mode will see. + * If @error is zero, the user sees a successful system call with a + * return value of @val. If @error is nonzero, it's a negated errno + * code; the user sees a failed system call with this errno code. + * + * It's only valid to call this when @task is stopped for tracing on exit + * from a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + */ +void syscall_set_return_value(struct task_struct *task, struct pt_regs *regs, + int error, long val); + +/** + * syscall_get_arguments - extract system call parameter values + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * @i: argument index [0,5] + * @n: number of arguments; n+i must be [1,6]. + * @args: array filled with argument values + * + * Fetches @n arguments to the system call starting with the @i'th argument + * (from 0 through 5). Argument @i is stored in @args[0], and so on. + * An arch inline version is probably optimal when @i and @n are constants. + * + * It's only valid to call this when @task is stopped for tracing on + * entry to a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + * It's invalid to call this with @i + @n > 6; we only support system calls + * taking up to 6 arguments. + */ +void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, unsigned long *args); + +/** + * syscall_set_arguments - change system call parameter value + * @task: task of interest, must be in system call entry tracing + * @regs: task_pt_regs() of @task + * @i: argument index [0,5] + * @n: number of arguments; n+i must be [1,6]. + * @args: array of argument values to store + * + * Changes @n arguments to the system call starting with the @i'th argument. + * @n'th argument to @val. Argument @i gets value @args[0], and so on. + * An arch inline version is probably optimal when @i and @n are constants. + * + * It's only valid to call this when @task is stopped for tracing on + * entry to a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + * It's invalid to call this with @i + @n > 6; we only support system calls + * taking up to 6 arguments. + */ +void syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, + const unsigned long *args); + +#endif /* _ASM_SYSCALL_H */ diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 32867ab86c70..589f429619c9 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -103,7 +103,8 @@ static inline void ptrace_report_syscall(struct pt_regs *regs) * the system call. That must prevent normal entry so no system call is * made. If @task ever returns to user mode after this, its register state * is unspecified, but should be something harmless like an %ENOSYS error - * return. + * return. It should preserve enough information so that syscall_rollback() + * can work (see asm-generic/syscall.h). * * Called without locks, just after entering kernel mode. */ -- cgit v1.2.3 From 85ba2d862e521375a8ee01526c5c46b1f24bb4af Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:58 -0700 Subject: tracehook: wait_task_inactive This extends wait_task_inactive() with a new argument so it can be used in a "soft" mode where it will check for the task changing state unexpectedly and back off. There is no change to existing callers. This lays the groundwork to allow robust, noninvasive tracing that can try to sample a blocked thread but back off safely if it wakes up. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/perfmon.c | 4 ++-- include/linux/sched.h | 8 ++++++-- kernel/kthread.c | 2 +- kernel/ptrace.c | 2 +- kernel/sched.c | 29 +++++++++++++++++++++++++++-- 5 files changed, 37 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 19d4493c6193..fc8f3509df27 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2626,7 +2626,7 @@ pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task) /* * make sure the task is off any CPU */ - wait_task_inactive(task); + wait_task_inactive(task, 0); /* more to come... */ @@ -4774,7 +4774,7 @@ recheck: UNPROTECT_CTX(ctx, flags); - wait_task_inactive(task); + wait_task_inactive(task, 0); PROTECT_CTX(ctx, flags); diff --git a/include/linux/sched.h b/include/linux/sched.h index a95d84d0da95..f59318a0099b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1882,9 +1882,13 @@ extern void set_task_comm(struct task_struct *tsk, char *from); extern char *get_task_comm(char *to, struct task_struct *tsk); #ifdef CONFIG_SMP -extern void wait_task_inactive(struct task_struct * p); +extern unsigned long wait_task_inactive(struct task_struct *, long match_state); #else -#define wait_task_inactive(p) do { } while (0) +static inline unsigned long wait_task_inactive(struct task_struct *p, + long match_state) +{ + return 1; +} #endif #define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks) diff --git a/kernel/kthread.c b/kernel/kthread.c index 6111c27491b1..96cff2f8710b 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -176,7 +176,7 @@ void kthread_bind(struct task_struct *k, unsigned int cpu) return; } /* Must have done schedule() in kthread() before we set_task_cpu */ - wait_task_inactive(k); + wait_task_inactive(k, 0); set_task_cpu(k, cpu); k->cpus_allowed = cpumask_of_cpu(cpu); k->rt.nr_cpus_allowed = 1; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 8392a9da6450..082b3fcb32a0 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -107,7 +107,7 @@ int ptrace_check_attach(struct task_struct *child, int kill) read_unlock(&tasklist_lock); if (!ret && !kill) - wait_task_inactive(child); + ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH; /* All systems go.. */ return ret; diff --git a/kernel/sched.c b/kernel/sched.c index fde1a1026359..0236958addcb 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1867,16 +1867,24 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req) /* * wait_task_inactive - wait for a thread to unschedule. * + * If @match_state is nonzero, it's the @p->state value just checked and + * not expected to change. If it changes, i.e. @p might have woken up, + * then return zero. When we succeed in waiting for @p to be off its CPU, + * we return a positive number (its total switch count). If a second call + * a short while later returns the same number, the caller can be sure that + * @p has remained unscheduled the whole time. + * * The caller must ensure that the task *will* unschedule sometime soon, * else this function might spin for a *long* time. This function can't * be called with interrupts off, or it may introduce deadlock with * smp_call_function() if an IPI is sent by the same process we are * waiting to become inactive. */ -void wait_task_inactive(struct task_struct *p) +unsigned long wait_task_inactive(struct task_struct *p, long match_state) { unsigned long flags; int running, on_rq; + unsigned long ncsw; struct rq *rq; for (;;) { @@ -1899,8 +1907,11 @@ void wait_task_inactive(struct task_struct *p) * return false if the runqueue has changed and p * is actually now running somewhere else! */ - while (task_running(rq, p)) + while (task_running(rq, p)) { + if (match_state && unlikely(p->state != match_state)) + return 0; cpu_relax(); + } /* * Ok, time to look more closely! We need the rq @@ -1910,8 +1921,20 @@ void wait_task_inactive(struct task_struct *p) rq = task_rq_lock(p, &flags); running = task_running(rq, p); on_rq = p->se.on_rq; + ncsw = 0; + if (!match_state || p->state == match_state) { + ncsw = p->nivcsw + p->nvcsw; + if (unlikely(!ncsw)) + ncsw = 1; + } task_rq_unlock(rq, &flags); + /* + * If it changed from the expected state, bail out now. + */ + if (unlikely(!ncsw)) + break; + /* * Was it really running after all now that we * checked with the proper locks actually held? @@ -1944,6 +1967,8 @@ void wait_task_inactive(struct task_struct *p) */ break; } + + return ncsw; } /*** -- cgit v1.2.3 From bbc698636ed48b6fcd323964e0f847a6a796325d Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:59 -0700 Subject: task_current_syscall This adds the new function task_current_syscall() on machines where the asm/syscall.h interface is supported (CONFIG_HAVE_ARCH_TRACEHOOK). It's exported for modules to use in the future. This function safely samples the state of a blocked thread to collect what system call it is blocked in, and the six system call argument registers. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 4 +++ lib/Makefile | 2 ++ lib/syscall.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 lib/syscall.c (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index ed69c03692d9..fd31756e1a00 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -314,6 +314,10 @@ static inline void user_enable_block_step(struct task_struct *task) #define arch_ptrace_stop(code, info) do { } while (0) #endif +extern int task_current_syscall(struct task_struct *target, long *callno, + unsigned long args[6], unsigned int maxargs, + unsigned long *sp, unsigned long *pc); + #endif #endif diff --git a/lib/Makefile b/lib/Makefile index 9085ad6fa53d..942c7250f603 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -78,6 +78,8 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o obj-$(CONFIG_HAVE_LMB) += lmb.o +obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/syscall.c b/lib/syscall.c new file mode 100644 index 000000000000..a4f7067f72fa --- /dev/null +++ b/lib/syscall.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +static int collect_syscall(struct task_struct *target, long *callno, + unsigned long args[6], unsigned int maxargs, + unsigned long *sp, unsigned long *pc) +{ + struct pt_regs *regs = task_pt_regs(target); + if (unlikely(!regs)) + return -EAGAIN; + + *sp = user_stack_pointer(regs); + *pc = instruction_pointer(regs); + + *callno = syscall_get_nr(target, regs); + if (*callno != -1L && maxargs > 0) + syscall_get_arguments(target, regs, 0, maxargs, args); + + return 0; +} + +/** + * task_current_syscall - Discover what a blocked task is doing. + * @target: thread to examine + * @callno: filled with system call number or -1 + * @args: filled with @maxargs system call arguments + * @maxargs: number of elements in @args to fill + * @sp: filled with user stack pointer + * @pc: filled with user PC + * + * If @target is blocked in a system call, returns zero with *@callno + * set to the the call's number and @args filled in with its arguments. + * Registers not used for system call arguments may not be available and + * it is not kosher to use &struct user_regset calls while the system + * call is still in progress. Note we may get this result if @target + * has finished its system call but not yet returned to user mode, such + * as when it's stopped for signal handling or syscall exit tracing. + * + * If @target is blocked in the kernel during a fault or exception, + * returns zero with *@callno set to -1 and does not fill in @args. + * If so, it's now safe to examine @target using &struct user_regset + * get() calls as long as we're sure @target won't return to user mode. + * + * Returns -%EAGAIN if @target does not remain blocked. + * + * Returns -%EINVAL if @maxargs is too large (maximum is six). + */ +int task_current_syscall(struct task_struct *target, long *callno, + unsigned long args[6], unsigned int maxargs, + unsigned long *sp, unsigned long *pc) +{ + long state; + unsigned long ncsw; + + if (unlikely(maxargs > 6)) + return -EINVAL; + + if (target == current) + return collect_syscall(target, callno, args, maxargs, sp, pc); + + state = target->state; + if (unlikely(!state)) + return -EAGAIN; + + ncsw = wait_task_inactive(target, state); + if (unlikely(!ncsw) || + unlikely(collect_syscall(target, callno, args, maxargs, sp, pc)) || + unlikely(wait_task_inactive(target, state) != ncsw)) + return -EAGAIN; + + return 0; +} +EXPORT_SYMBOL_GPL(task_current_syscall); -- cgit v1.2.3 From 9d8fddfb17aaee4ffc5e3d0560620d0fa8b50a42 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:23 -0700 Subject: mm/allocpercpu.c: make 4 functions static This patch makes the following needlessly global functions static: - percpu_depopulate() - __percpu_depopulate_mask() - percpu_populate() - __percpu_populate_mask() Signed-off-by: Adrian Bunk Acked-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/percpu.h | 29 ----------------------------- mm/allocpercpu.c | 20 +++++++++++--------- 2 files changed, 11 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 4cdd393e71e1..fac3337547eb 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -74,11 +74,6 @@ struct percpu_data { (__typeof__(ptr))__p->ptrs[(cpu)]; \ }) -extern void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu); -extern void percpu_depopulate(void *__pdata, int cpu); -extern int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, - cpumask_t *mask); -extern void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask); extern void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask); extern void percpu_free(void *__pdata); @@ -86,26 +81,6 @@ extern void percpu_free(void *__pdata); #define percpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); }) -static inline void percpu_depopulate(void *__pdata, int cpu) -{ -} - -static inline void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask) -{ -} - -static inline void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, - int cpu) -{ - return percpu_ptr(__pdata, cpu); -} - -static inline int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, - cpumask_t *mask) -{ - return 0; -} - static __always_inline void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask) { return kzalloc(size, gfp); @@ -118,10 +93,6 @@ static inline void percpu_free(void *__pdata) #endif /* CONFIG_SMP */ -#define percpu_populate_mask(__pdata, size, gfp, mask) \ - __percpu_populate_mask((__pdata), (size), (gfp), &(mask)) -#define percpu_depopulate_mask(__pdata, mask) \ - __percpu_depopulate_mask((__pdata), &(mask)) #define percpu_alloc_mask(size, gfp, mask) \ __percpu_alloc_mask((size), (gfp), &(mask)) diff --git a/mm/allocpercpu.c b/mm/allocpercpu.c index 843364594e23..4297bc41bfd2 100644 --- a/mm/allocpercpu.c +++ b/mm/allocpercpu.c @@ -18,27 +18,28 @@ * Depopulating per-cpu data for a cpu going offline would be a typical * use case. You need to register a cpu hotplug handler for that purpose. */ -void percpu_depopulate(void *__pdata, int cpu) +static void percpu_depopulate(void *__pdata, int cpu) { struct percpu_data *pdata = __percpu_disguise(__pdata); kfree(pdata->ptrs[cpu]); pdata->ptrs[cpu] = NULL; } -EXPORT_SYMBOL_GPL(percpu_depopulate); /** * percpu_depopulate_mask - depopulate per-cpu data for some cpu's * @__pdata: per-cpu data to depopulate * @mask: depopulate per-cpu data for cpu's selected through mask bits */ -void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask) +static void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask) { int cpu; for_each_cpu_mask_nr(cpu, *mask) percpu_depopulate(__pdata, cpu); } -EXPORT_SYMBOL_GPL(__percpu_depopulate_mask); + +#define percpu_depopulate_mask(__pdata, mask) \ + __percpu_depopulate_mask((__pdata), &(mask)) /** * percpu_populate - populate per-cpu data for given cpu @@ -51,7 +52,7 @@ EXPORT_SYMBOL_GPL(__percpu_depopulate_mask); * use case. You need to register a cpu hotplug handler for that purpose. * Per-cpu object is populated with zeroed buffer. */ -void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu) +static void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu) { struct percpu_data *pdata = __percpu_disguise(__pdata); int node = cpu_to_node(cpu); @@ -68,7 +69,6 @@ void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu) pdata->ptrs[cpu] = kzalloc(size, gfp); return pdata->ptrs[cpu]; } -EXPORT_SYMBOL_GPL(percpu_populate); /** * percpu_populate_mask - populate per-cpu data for more cpu's @@ -79,8 +79,8 @@ EXPORT_SYMBOL_GPL(percpu_populate); * * Per-cpu objects are populated with zeroed buffers. */ -int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, - cpumask_t *mask) +static int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, + cpumask_t *mask) { cpumask_t populated; int cpu; @@ -94,7 +94,9 @@ int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, cpu_set(cpu, populated); return 0; } -EXPORT_SYMBOL_GPL(__percpu_populate_mask); + +#define percpu_populate_mask(__pdata, size, gfp, mask) \ + __percpu_populate_mask((__pdata), (size), (gfp), &(mask)) /** * percpu_alloc_mask - initial setup of per-cpu data -- cgit v1.2.3 From 15f59adae001766a2c7f7fe4f196387bb04bcff5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:23 -0700 Subject: make mm/memory.c:print_bad_pte() static This patch makes the needlessly global print_bad_pte() static. Signed-off-by: Adrian Bunk Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 - mm/memory.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index f3fd70d6029f..6e695eaab4ce 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -810,7 +810,6 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void * int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas); -void print_bad_pte(struct vm_area_struct *, pte_t, unsigned long); extern int try_to_release_page(struct page * page, gfp_t gfp_mask); extern void do_invalidatepage(struct page *page, unsigned long offset); diff --git a/mm/memory.c b/mm/memory.c index 262e3eb6601a..a8ca04faaea6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -374,7 +374,8 @@ static inline void add_mm_rss(struct mm_struct *mm, int file_rss, int anon_rss) * * The calling function must still handle the error. */ -void print_bad_pte(struct vm_area_struct *vma, pte_t pte, unsigned long vaddr) +static void print_bad_pte(struct vm_area_struct *vma, pte_t pte, + unsigned long vaddr) { printk(KERN_ERR "Bad pte = %08llx, process = %s, " "vm_flags = %lx, vaddr = %lx\n", -- cgit v1.2.3 From 7c363b8c6536f26934172d3c46f0bbec01a97c61 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:24 -0700 Subject: mm/swapfile.c: make code static This patch makes the following needlessly global code static: - swap_lock - nr_swapfiles - struct swap_list Signed-off-by: Adrian Bunk Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 3 --- mm/swapfile.c | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 0b3377650c85..de40f169a4e4 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -237,7 +237,6 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t, /* linux/mm/swapfile.c */ extern long total_swap_pages; -extern unsigned int nr_swapfiles; extern void si_swapinfo(struct sysinfo *); extern swp_entry_t get_swap_page(void); extern swp_entry_t get_swap_page_of_type(int); @@ -254,8 +253,6 @@ extern int can_share_swap_page(struct page *); extern int remove_exclusive_swap_page(struct page *); struct backing_dev_info; -extern spinlock_t swap_lock; - /* linux/mm/thrash.c */ extern struct mm_struct * swap_token_mm; extern void grab_swap_token(void); diff --git a/mm/swapfile.c b/mm/swapfile.c index af283933c14e..6beb6251e99d 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -33,8 +33,8 @@ #include #include -DEFINE_SPINLOCK(swap_lock); -unsigned int nr_swapfiles; +static DEFINE_SPINLOCK(swap_lock); +static unsigned int nr_swapfiles; long total_swap_pages; static int swap_overflow; static int least_priority; @@ -44,7 +44,7 @@ static const char Unused_file[] = "Unused swap file entry "; static const char Bad_offset[] = "Bad swap offset entry "; static const char Unused_offset[] = "Unused swap offset entry "; -struct swap_list_t swap_list = {-1, -1}; +static struct swap_list_t swap_list = {-1, -1}; static struct swap_info_struct swap_info[MAX_SWAPFILES]; -- cgit v1.2.3 From 9580d85f9cdb076c4bfb467bc6c0d3c5e499957a Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:25 -0700 Subject: drivers/char/rtc.c: make 2 functions static The following functions can now become static: - rtc_interrupt() - rtc_get_rtc_time() Signed-off-by: Adrian Bunk Acked-by: Bernhard Walle Acked-by: Paul Gortmaker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/rtc.c | 5 +++-- include/linux/rtc.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index dbefbb30ed44..d9799e2bcfbf 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -144,6 +144,7 @@ static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static void rtc_get_rtc_time(struct rtc_time *rtc_tm); #ifdef RTC_IRQ static unsigned int rtc_poll(struct file *file, poll_table *wait); @@ -235,7 +236,7 @@ static inline unsigned char rtc_is_updating(void) * (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.) */ -irqreturn_t rtc_interrupt(int irq, void *dev_id) +static irqreturn_t rtc_interrupt(int irq, void *dev_id) { /* * Can be an alarm interrupt, update complete interrupt, @@ -1303,7 +1304,7 @@ static int rtc_proc_open(struct inode *inode, struct file *file) } #endif -void rtc_get_rtc_time(struct rtc_time *rtc_tm) +static void rtc_get_rtc_time(struct rtc_time *rtc_tm) { unsigned long uip_watchdog = jiffies, flags; unsigned char ctrl; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index b01fe004cb5e..91f597ad6acc 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -225,8 +225,6 @@ typedef struct rtc_task { int rtc_register(rtc_task_t *task); int rtc_unregister(rtc_task_t *task); int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); -void rtc_get_rtc_time(struct rtc_time *rtc_tm); -irqreturn_t rtc_interrupt(int irq, void *dev_id); #endif /* __KERNEL__ */ -- cgit v1.2.3 From a9906a19193db69ad0158f289f839edf8aaf103f Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 26 Jul 2008 14:41:26 -0700 Subject: tracehook: comment fixes This fixes some typos and errors in comments. No code changes. Signed-off-by: Roland McGrath --- include/linux/tracehook.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 589f429619c9..b1875582c1a1 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -244,7 +244,7 @@ static inline int tracehook_prepare_clone(unsigned clone_flags) * tracehook_finish_clone - new child created and being attached * @child: new child task * @clone_flags: %CLONE_* flags from clone/fork/vfork system call - * @trace: return value from tracehook_clone_prepare() + * @trace: return value from tracehook_prepare_clone() * * This is called immediately after adding @child to its parent's children list. * The @trace value is that returned by tracehook_prepare_clone(). @@ -259,19 +259,20 @@ static inline void tracehook_finish_clone(struct task_struct *child, /** * tracehook_report_clone - in parent, new child is about to start running - * @trace: return value from tracehook_clone_prepare() + * @trace: return value from tracehook_prepare_clone() * @regs: parent's user register state * @clone_flags: flags from parent's system call * @pid: new child's PID in the parent's namespace * @child: new child task * - * Called after a child is set up, but before it has been started running. - * The @trace value is that returned by tracehook_clone_prepare(). - * This is not a good place to block, because the child has not started yet. - * Suspend the child here if desired, and block in tracehook_clone_complete(). - * This must prevent the child from self-reaping if tracehook_clone_complete() - * uses the @child pointer; otherwise it might have died and been released by - * the time tracehook_report_clone_complete() is called. + * Called after a child is set up, but before it has been started + * running. @trace is the value returned by tracehook_prepare_clone(). + * This is not a good place to block, because the child has not started + * yet. Suspend the child here if desired, and then block in + * tracehook_report_clone_complete(). This must prevent the child from + * self-reaping if tracehook_report_clone_complete() uses the @child + * pointer; otherwise it might have died and been released by the time + * tracehook_report_report_clone_complete() is called. * * Called with no locks held, but the child cannot run until this returns. */ @@ -290,7 +291,7 @@ static inline void tracehook_report_clone(int trace, struct pt_regs *regs, /** * tracehook_report_clone_complete - new child is running - * @trace: return value from tracehook_clone_prepare() + * @trace: return value from tracehook_prepare_clone() * @regs: parent's user register state * @clone_flags: flags from parent's system call * @pid: new child's PID in the parent's namespace @@ -347,7 +348,7 @@ static inline void tracehook_prepare_release_task(struct task_struct *task) } /** - * tracehook_finish_release_task - task is being reaped, clean up tracing + * tracehook_finish_release_task - final tracing clean-up * @task: task in %EXIT_DEAD state * * This is called in release_task() when @task is being in the middle of -- cgit v1.2.3 From 6edd8ee60ac9b974bd6ec3b1bcb2aab02762fa8c Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Thu, 24 Jul 2008 14:18:57 +0200 Subject: mmc: Export internal host state through debugfs When CONFIG_DEBUG_FS is set, create a few files under /sys/kernel/debug containing information about an mmc host's internal state. Currently, just a single file is created, "ios", which contains information about the current operating parameters for the bus (clock speed, bus width, etc.) Host drivers can add additional files and directories under the host's root directory by passing the debugfs_root field in struct mmc_host as the 'parent' parameter to debugfs_create_*. Signed-off-by: Haavard Skinnemoen Signed-off-by: Pierre Ossman --- drivers/mmc/core/Makefile | 1 + drivers/mmc/core/core.h | 4 ++ drivers/mmc/core/debugfs.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/host.c | 8 +++ include/linux/mmc/host.h | 2 + 5 files changed, 179 insertions(+) create mode 100644 drivers/mmc/core/debugfs.c (limited to 'include/linux') diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 19a1a254a0c5..889e5f898f6f 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -12,3 +12,4 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o +mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index cdb332b7dedc..745da9881aa7 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -52,5 +52,9 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr); extern int use_spi_crc; +/* Debugfs information for hosts and cards */ +void mmc_add_host_debugfs(struct mmc_host *host); +void mmc_remove_host_debugfs(struct mmc_host *host); + #endif diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c new file mode 100644 index 000000000000..133c6e51f26b --- /dev/null +++ b/drivers/mmc/core/debugfs.c @@ -0,0 +1,164 @@ +/* + * Debugfs support for hosts and cards + * + * Copyright (C) 2008 Atmel Corporation + * + * 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 +#include +#include +#include + +#include + +#include "core.h" + +/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ +static int mmc_ios_show(struct seq_file *s, void *data) +{ + static const char *vdd_str[] = { + [8] = "2.0", + [9] = "2.1", + [10] = "2.2", + [11] = "2.3", + [12] = "2.4", + [13] = "2.5", + [14] = "2.6", + [15] = "2.7", + [16] = "2.8", + [17] = "2.9", + [18] = "3.0", + [19] = "3.1", + [20] = "3.2", + [21] = "3.3", + [22] = "3.4", + [23] = "3.5", + [24] = "3.6", + }; + struct mmc_host *host = s->private; + struct mmc_ios *ios = &host->ios; + const char *str; + + seq_printf(s, "clock:\t\t%u Hz\n", ios->clock); + seq_printf(s, "vdd:\t\t%u ", ios->vdd); + if ((1 << ios->vdd) & MMC_VDD_165_195) + seq_printf(s, "(1.65 - 1.95 V)\n"); + else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1) + && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1]) + seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd], + vdd_str[ios->vdd + 1]); + else + seq_printf(s, "(invalid)\n"); + + switch (ios->bus_mode) { + case MMC_BUSMODE_OPENDRAIN: + str = "open drain"; + break; + case MMC_BUSMODE_PUSHPULL: + str = "push-pull"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str); + + switch (ios->chip_select) { + case MMC_CS_DONTCARE: + str = "don't care"; + break; + case MMC_CS_HIGH: + str = "active high"; + break; + case MMC_CS_LOW: + str = "active low"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + str = "off"; + break; + case MMC_POWER_UP: + str = "up"; + break; + case MMC_POWER_ON: + str = "on"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str); + seq_printf(s, "bus width:\t%u (%u bits)\n", + ios->bus_width, 1 << ios->bus_width); + + switch (ios->timing) { + case MMC_TIMING_LEGACY: + str = "legacy"; + break; + case MMC_TIMING_MMC_HS: + str = "mmc high-speed"; + break; + case MMC_TIMING_SD_HS: + str = "sd high-speed"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str); + + return 0; +} + +static int mmc_ios_open(struct inode *inode, struct file *file) +{ + return single_open(file, mmc_ios_show, inode->i_private); +} + +static const struct file_operations mmc_ios_fops = { + .open = mmc_ios_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void mmc_add_host_debugfs(struct mmc_host *host) +{ + struct dentry *root; + + root = debugfs_create_dir(mmc_hostname(host), NULL); + if (IS_ERR(root)) + /* Don't complain -- debugfs just isn't enabled */ + return; + if (!root) + /* Complain -- debugfs is enabled, but it failed to + * create the directory. */ + goto err_root; + + host->debugfs_root = root; + + if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops)) + goto err_ios; + + return; + +err_ios: + debugfs_remove_recursive(root); + host->debugfs_root = NULL; +err_root: + dev_err(&host->class_dev, "failed to initialize debugfs\n"); +} + +void mmc_remove_host_debugfs(struct mmc_host *host) +{ + debugfs_remove_recursive(host->debugfs_root); +} diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1d795c5379b5..6da80fd4d974 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -127,6 +127,10 @@ int mmc_add_host(struct mmc_host *host) if (err) return err; +#ifdef CONFIG_DEBUG_FS + mmc_add_host_debugfs(host); +#endif + mmc_start_host(host); return 0; @@ -146,6 +150,10 @@ void mmc_remove_host(struct mmc_host *host) { mmc_stop_host(host); +#ifdef CONFIG_DEBUG_FS + mmc_remove_host_debugfs(host); +#endif + device_del(&host->class_dev); led_trigger_unregister_simple(host->led); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 10a2080086ca..9c288c909878 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -157,6 +157,8 @@ struct mmc_host { struct led_trigger *led; /* activity led */ #endif + struct dentry *debugfs_root; + unsigned long private[0] ____cacheline_aligned; }; -- cgit v1.2.3 From f4b7f927b531ca350cfc4ca1bdc3377dac7f9a32 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Thu, 24 Jul 2008 14:18:58 +0200 Subject: mmc: Add per-card debugfs support For each card successfully added to the bus, create a subdirectory under the host's debugfs root with information about the card. At the moment, only a single file is added to the card directory for all cards: "state". It reflects the "state" field in struct mmc_card, indicating whether the card is present, readonly, etc. For MMC and SD cards (not SDIO), another file is added: "status". Reading this file will ask the card about its current status and return it. This can be useful if the card just refuses to respond to any commands, which might indicate that the card state is not what the MMC core thinks it is (due to a missing stop command, for example.) Signed-off-by: Haavard Skinnemoen Signed-off-by: Pierre Ossman --- drivers/mmc/core/bus.c | 8 ++++++ drivers/mmc/core/core.h | 3 +++ drivers/mmc/core/debugfs.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/card.h | 2 ++ 4 files changed, 74 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index fd95b18e988b..0d9b2d6f9ebf 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -252,6 +252,10 @@ int mmc_add_card(struct mmc_card *card) if (ret) return ret; +#ifdef CONFIG_DEBUG_FS + mmc_add_card_debugfs(card); +#endif + mmc_card_set_present(card); return 0; @@ -263,6 +267,10 @@ int mmc_add_card(struct mmc_card *card) */ void mmc_remove_card(struct mmc_card *card) { +#ifdef CONFIG_DEBUG_FS + mmc_remove_card_debugfs(card); +#endif + if (mmc_card_present(card)) { if (mmc_host_is_spi(card->host)) { printk(KERN_INFO "%s: SPI card removed\n", diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 745da9881aa7..c819effa1032 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -56,5 +56,8 @@ extern int use_spi_crc; void mmc_add_host_debugfs(struct mmc_host *host); void mmc_remove_host_debugfs(struct mmc_host *host); +void mmc_add_card_debugfs(struct mmc_card *card); +void mmc_remove_card_debugfs(struct mmc_card *card); + #endif diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 133c6e51f26b..1237bb4c722b 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -12,9 +12,11 @@ #include #include +#include #include #include "core.h" +#include "mmc_ops.h" /* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ static int mmc_ios_show(struct seq_file *s, void *data) @@ -162,3 +164,62 @@ void mmc_remove_host_debugfs(struct mmc_host *host) { debugfs_remove_recursive(host->debugfs_root); } + +static int mmc_dbg_card_status_get(void *data, u64 *val) +{ + struct mmc_card *card = data; + u32 status; + int ret; + + mmc_claim_host(card->host); + + ret = mmc_send_status(data, &status); + if (!ret) + *val = status; + + mmc_release_host(card->host); + + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get, + NULL, "%08llx\n"); + +void mmc_add_card_debugfs(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + struct dentry *root; + + if (!host->debugfs_root) + return; + + root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root); + if (IS_ERR(root)) + /* Don't complain -- debugfs just isn't enabled */ + return; + if (!root) + /* Complain -- debugfs is enabled, but it failed to + * create the directory. */ + goto err; + + card->debugfs_root = root; + + if (!debugfs_create_x32("state", S_IRUSR, root, &card->state)) + goto err; + + if (mmc_card_mmc(card) || mmc_card_sd(card)) + if (!debugfs_create_file("status", S_IRUSR, root, card, + &mmc_dbg_card_status_fops)) + goto err; + + return; + +err: + debugfs_remove_recursive(root); + card->debugfs_root = NULL; + dev_err(&card->dev, "failed to initialize debugfs\n"); +} + +void mmc_remove_card_debugfs(struct mmc_card *card) +{ + debugfs_remove_recursive(card->debugfs_root); +} diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 0d508ac17d64..ee6e822d5994 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -111,6 +111,8 @@ struct mmc_card { unsigned num_info; /* number of info strings */ const char **info; /* info strings */ struct sdio_func_tuple *tuples; /* unknown common tuples */ + + struct dentry *debugfs_root; }; #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) -- cgit v1.2.3 From 04578dd330f1ec6bc9c4233833bee0d0ca73ff09 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sat, 26 Jul 2008 18:52:34 +0200 Subject: Define AF_ISDN and PF_ISDN Define the address and protocol family value for mISDN. Signed-off-by: Karsten Keil --- include/linux/socket.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/socket.h b/include/linux/socket.h index 950af631e7fb..dc5086fe7736 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -189,7 +189,8 @@ struct ucred { #define AF_BLUETOOTH 31 /* Bluetooth sockets */ #define AF_IUCV 32 /* IUCV sockets */ #define AF_RXRPC 33 /* RxRPC sockets */ -#define AF_MAX 34 /* For now.. */ +#define AF_ISDN 34 /* mISDN sockets */ +#define AF_MAX 35 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -225,6 +226,7 @@ struct ucred { #define PF_BLUETOOTH AF_BLUETOOTH #define PF_IUCV AF_IUCV #define PF_RXRPC AF_RXRPC +#define PF_ISDN AF_ISDN #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ -- cgit v1.2.3 From 1b2b03f8e514e4f68e293846ba511a948b80243c Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sun, 27 Jul 2008 01:54:58 +0200 Subject: Add mISDN core files Add mISDN core files Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/Kconfig | 9 + drivers/isdn/mISDN/Makefile | 9 + drivers/isdn/mISDN/core.c | 244 +++++ drivers/isdn/mISDN/core.h | 77 ++ drivers/isdn/mISDN/fsm.c | 183 ++++ drivers/isdn/mISDN/fsm.h | 67 ++ drivers/isdn/mISDN/hwchannel.c | 365 +++++++ drivers/isdn/mISDN/layer1.c | 403 ++++++++ drivers/isdn/mISDN/layer1.h | 26 + drivers/isdn/mISDN/layer2.c | 2216 ++++++++++++++++++++++++++++++++++++++++ drivers/isdn/mISDN/layer2.h | 140 +++ drivers/isdn/mISDN/socket.c | 781 ++++++++++++++ drivers/isdn/mISDN/stack.c | 674 ++++++++++++ drivers/isdn/mISDN/tei.c | 1340 ++++++++++++++++++++++++ drivers/isdn/mISDN/timerdev.c | 301 ++++++ include/linux/mISDNhw.h | 193 ++++ include/linux/mISDNif.h | 487 +++++++++ 17 files changed, 7515 insertions(+) create mode 100644 drivers/isdn/mISDN/Kconfig create mode 100644 drivers/isdn/mISDN/Makefile create mode 100644 drivers/isdn/mISDN/core.c create mode 100644 drivers/isdn/mISDN/core.h create mode 100644 drivers/isdn/mISDN/fsm.c create mode 100644 drivers/isdn/mISDN/fsm.h create mode 100644 drivers/isdn/mISDN/hwchannel.c create mode 100644 drivers/isdn/mISDN/layer1.c create mode 100644 drivers/isdn/mISDN/layer1.h create mode 100644 drivers/isdn/mISDN/layer2.c create mode 100644 drivers/isdn/mISDN/layer2.h create mode 100644 drivers/isdn/mISDN/socket.c create mode 100644 drivers/isdn/mISDN/stack.c create mode 100644 drivers/isdn/mISDN/tei.c create mode 100644 drivers/isdn/mISDN/timerdev.c create mode 100644 include/linux/mISDNhw.h create mode 100644 include/linux/mISDNif.h (limited to 'include/linux') diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig new file mode 100644 index 000000000000..231bd0d08316 --- /dev/null +++ b/drivers/isdn/mISDN/Kconfig @@ -0,0 +1,9 @@ +# +# modularer ISDN driver +# + +menuconfig MISDN + tristate "Modular ISDN driver" + help + Enable support for the modular ISDN driver. + diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile new file mode 100644 index 000000000000..87c563d33612 --- /dev/null +++ b/drivers/isdn/mISDN/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the modular ISDN driver +# + +obj-$(CONFIG_MISDN) += mISDN_core.o + +# multi objects + +mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c new file mode 100644 index 000000000000..33068177b7c9 --- /dev/null +++ b/drivers/isdn/mISDN/core.c @@ -0,0 +1,244 @@ +/* + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include "core.h" + +static u_int debug; + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); + +static LIST_HEAD(devices); +DEFINE_RWLOCK(device_lock); +static u64 device_ids; +#define MAX_DEVICE_ID 63 + +static LIST_HEAD(Bprotocols); +DEFINE_RWLOCK(bp_lock); + +struct mISDNdevice +*get_mdevice(u_int id) +{ + struct mISDNdevice *dev; + + read_lock(&device_lock); + list_for_each_entry(dev, &devices, D.list) + if (dev->id == id) { + read_unlock(&device_lock); + return dev; + } + read_unlock(&device_lock); + return NULL; +} + +int +get_mdevice_count(void) +{ + struct mISDNdevice *dev; + int cnt = 0; + + read_lock(&device_lock); + list_for_each_entry(dev, &devices, D.list) + cnt++; + read_unlock(&device_lock); + return cnt; +} + +static int +get_free_devid(void) +{ + u_int i; + + for (i = 0; i <= MAX_DEVICE_ID; i++) + if (!test_and_set_bit(i, (u_long *)&device_ids)) + return i; + return -1; +} + +int +mISDN_register_device(struct mISDNdevice *dev, char *name) +{ + u_long flags; + int err; + + dev->id = get_free_devid(); + if (dev->id < 0) + return -EBUSY; + if (name && name[0]) + strcpy(dev->name, name); + else + sprintf(dev->name, "mISDN%d", dev->id); + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "mISDN_register %s %d\n", + dev->name, dev->id); + err = create_stack(dev); + if (err) + return err; + write_lock_irqsave(&device_lock, flags); + list_add_tail(&dev->D.list, &devices); + write_unlock_irqrestore(&device_lock, flags); + return 0; +} +EXPORT_SYMBOL(mISDN_register_device); + +void +mISDN_unregister_device(struct mISDNdevice *dev) { + u_long flags; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "mISDN_unregister %s %d\n", + dev->name, dev->id); + write_lock_irqsave(&device_lock, flags); + list_del(&dev->D.list); + write_unlock_irqrestore(&device_lock, flags); + test_and_clear_bit(dev->id, (u_long *)&device_ids); + delete_stack(dev); +} +EXPORT_SYMBOL(mISDN_unregister_device); + +u_int +get_all_Bprotocols(void) +{ + struct Bprotocol *bp; + u_int m = 0; + + read_lock(&bp_lock); + list_for_each_entry(bp, &Bprotocols, list) + m |= bp->Bprotocols; + read_unlock(&bp_lock); + return m; +} + +struct Bprotocol * +get_Bprotocol4mask(u_int m) +{ + struct Bprotocol *bp; + + read_lock(&bp_lock); + list_for_each_entry(bp, &Bprotocols, list) + if (bp->Bprotocols & m) { + read_unlock(&bp_lock); + return bp; + } + read_unlock(&bp_lock); + return NULL; +} + +struct Bprotocol * +get_Bprotocol4id(u_int id) +{ + u_int m; + + if (id < ISDN_P_B_START || id > 63) { + printk(KERN_WARNING "%s id not in range %d\n", + __func__, id); + return NULL; + } + m = 1 << (id & ISDN_P_B_MASK); + return get_Bprotocol4mask(m); +} + +int +mISDN_register_Bprotocol(struct Bprotocol *bp) +{ + u_long flags; + struct Bprotocol *old; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "%s: %s/%x\n", __func__, + bp->name, bp->Bprotocols); + old = get_Bprotocol4mask(bp->Bprotocols); + if (old) { + printk(KERN_WARNING + "register duplicate protocol old %s/%x new %s/%x\n", + old->name, old->Bprotocols, bp->name, bp->Bprotocols); + return -EBUSY; + } + write_lock_irqsave(&bp_lock, flags); + list_add_tail(&bp->list, &Bprotocols); + write_unlock_irqrestore(&bp_lock, flags); + return 0; +} +EXPORT_SYMBOL(mISDN_register_Bprotocol); + +void +mISDN_unregister_Bprotocol(struct Bprotocol *bp) +{ + u_long flags; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, + bp->Bprotocols); + write_lock_irqsave(&bp_lock, flags); + list_del(&bp->list); + write_unlock_irqrestore(&bp_lock, flags); +} +EXPORT_SYMBOL(mISDN_unregister_Bprotocol); + +int +mISDNInit(void) +{ + int err; + + printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", + MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); + mISDN_initstack(&debug); + err = mISDN_inittimer(&debug); + if (err) + goto error; + err = l1_init(&debug); + if (err) { + mISDN_timer_cleanup(); + goto error; + } + err = Isdnl2_Init(&debug); + if (err) { + mISDN_timer_cleanup(); + l1_cleanup(); + goto error; + } + err = misdn_sock_init(&debug); + if (err) { + mISDN_timer_cleanup(); + l1_cleanup(); + Isdnl2_cleanup(); + } +error: + return err; +} + +void mISDN_cleanup(void) +{ + misdn_sock_cleanup(); + mISDN_timer_cleanup(); + l1_cleanup(); + Isdnl2_cleanup(); + + if (!list_empty(&devices)) + printk(KERN_ERR "%s devices still registered\n", __func__); + + if (!list_empty(&Bprotocols)) + printk(KERN_ERR "%s Bprotocols still registered\n", __func__); + printk(KERN_DEBUG "mISDNcore unloaded\n"); +} + +module_init(mISDNInit); +module_exit(mISDN_cleanup); + diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h new file mode 100644 index 000000000000..7da7233b4c1a --- /dev/null +++ b/drivers/isdn/mISDN/core.h @@ -0,0 +1,77 @@ +/* + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#ifndef mISDN_CORE_H +#define mISDN_CORE_H + +extern struct mISDNdevice *get_mdevice(u_int); +extern int get_mdevice_count(void); + +/* stack status flag */ +#define mISDN_STACK_ACTION_MASK 0x0000ffff +#define mISDN_STACK_COMMAND_MASK 0x000f0000 +#define mISDN_STACK_STATUS_MASK 0xfff00000 +/* action bits 0-15 */ +#define mISDN_STACK_WORK 0 +#define mISDN_STACK_SETUP 1 +#define mISDN_STACK_CLEARING 2 +#define mISDN_STACK_RESTART 3 +#define mISDN_STACK_WAKEUP 4 +#define mISDN_STACK_ABORT 15 +/* command bits 16-19 */ +#define mISDN_STACK_STOPPED 16 +#define mISDN_STACK_INIT 17 +#define mISDN_STACK_THREADSTART 18 +/* status bits 20-31 */ +#define mISDN_STACK_BCHANNEL 20 +#define mISDN_STACK_ACTIVE 29 +#define mISDN_STACK_RUNNING 30 +#define mISDN_STACK_KILLED 31 + + +/* manager options */ +#define MGR_OPT_USER 24 +#define MGR_OPT_NETWORK 25 + +extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); +extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); +extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); + +extern int create_stack(struct mISDNdevice *); +extern int create_teimanager(struct mISDNdevice *); +extern void delete_teimanager(struct mISDNchannel *); +extern void delete_channel(struct mISDNchannel *); +extern void delete_stack(struct mISDNdevice *); +extern void mISDN_initstack(u_int *); +extern int misdn_sock_init(u_int *); +extern void misdn_sock_cleanup(void); +extern void add_layer2(struct mISDNchannel *, struct mISDNstack *); +extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *); + +extern u_int get_all_Bprotocols(void); +struct Bprotocol *get_Bprotocol4mask(u_int); +struct Bprotocol *get_Bprotocol4id(u_int); + +extern int mISDN_inittimer(u_int *); +extern void mISDN_timer_cleanup(void); + +extern int l1_init(u_int *); +extern void l1_cleanup(void); +extern int Isdnl2_Init(u_int *); +extern void Isdnl2_cleanup(void); + +#endif diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c new file mode 100644 index 000000000000..b5d6553f2dc8 --- /dev/null +++ b/drivers/isdn/mISDN/fsm.c @@ -0,0 +1,183 @@ +/* + * finite state machine implementation + * + * Author Karsten Keil + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include +#include +#include +#include "fsm.h" + +#define FSM_TIMER_DEBUG 0 + +void +mISDN_FsmNew(struct Fsm *fsm, + struct FsmNode *fnlist, int fncount) +{ + int i; + + fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count * + fsm->event_count, GFP_KERNEL); + + for (i = 0; i < fncount; i++) + if ((fnlist[i].state >= fsm->state_count) || + (fnlist[i].event >= fsm->event_count)) { + printk(KERN_ERR + "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n", + i, (long)fnlist[i].state, (long)fsm->state_count, + (long)fnlist[i].event, (long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; +} +EXPORT_SYMBOL(mISDN_FsmNew); + +void +mISDN_FsmFree(struct Fsm *fsm) +{ + kfree((void *) fsm->jumpmatrix); +} +EXPORT_SYMBOL(mISDN_FsmFree); + +int +mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) +{ + FSMFNPTR r; + + if ((fi->state >= fi->fsm->state_count) || + (event >= fi->fsm->event_count)) { + printk(KERN_ERR + "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state, (long)fi->fsm->state_count, event, + (long)fi->fsm->event_count); + return 1; + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if (r) { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + r(fi, event, arg); + return 0; + } else { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s no action", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + return 1; + } +} +EXPORT_SYMBOL(mISDN_FsmEvent); + +void +mISDN_FsmChangeState(struct FsmInst *fi, int newstate) +{ + fi->state = newstate; + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", + fi->fsm->strState[newstate]); +} +EXPORT_SYMBOL(mISDN_FsmChangeState); + +static void +FsmExpireTimer(struct FsmTimer *ft) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); +#endif + mISDN_FsmEvent(ft->fi, ft->event, ft->arg); +} + +void +mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + ft->tl.function = (void *) FsmExpireTimer; + ft->tl.data = (long) ft; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); +#endif + init_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmInitTimer); + +void +mISDN_FsmDelTimer(struct FsmTimer *ft, int where) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", + (long) ft, where); +#endif + del_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmDelTimer); + +int +mISDN_FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) { + if (ft->fi->debug) { + printk(KERN_WARNING + "mISDN_FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, + "mISDN_FsmAddTimer already active!"); + } + return -1; + } + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); + return 0; +} +EXPORT_SYMBOL(mISDN_FsmAddTimer); + +void +mISDN_FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmRestartTimer); diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h new file mode 100644 index 000000000000..928f5be192c1 --- /dev/null +++ b/drivers/isdn/mISDN/fsm.h @@ -0,0 +1,67 @@ +/* + * + * Author Karsten Keil + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#ifndef _MISDN_FSM_H +#define _MISDN_FSM_H + +#include + +/* Statemachine */ + +struct FsmInst; + +typedef void (*FSMFNPTR)(struct FsmInst *, int, void *); + +struct Fsm { + FSMFNPTR *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *, ...); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + struct timer_list tl; + int event; + void *arg; +}; + +extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); +extern void mISDN_FsmFree(struct Fsm *); +extern int mISDN_FsmEvent(struct FsmInst *, int , void *); +extern void mISDN_FsmChangeState(struct FsmInst *, int); +extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); +extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmDelTimer(struct FsmTimer *, int); + +#endif diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c new file mode 100644 index 000000000000..2596fba4e614 --- /dev/null +++ b/drivers/isdn/mISDN/hwchannel.c @@ -0,0 +1,365 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include + +static void +dchannel_bh(struct work_struct *ws) +{ + struct dchannel *dch = container_of(ws, struct dchannel, workq); + struct sk_buff *skb; + int err; + + if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { + while ((skb = skb_dequeue(&dch->rqueue))) { + if (likely(dch->dev.D.peer)) { + err = dch->dev.D.recv(dch->dev.D.peer, skb); + if (err) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } + } + if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { + if (dch->phfunc) + dch->phfunc(dch); + } +} + +static void +bchannel_bh(struct work_struct *ws) +{ + struct bchannel *bch = container_of(ws, struct bchannel, workq); + struct sk_buff *skb; + int err; + + if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { + while ((skb = skb_dequeue(&bch->rqueue))) { + if (bch->rcount >= 64) + printk(KERN_WARNING "B-channel %p receive " + "queue if full, but empties...\n", bch); + bch->rcount--; + if (likely(bch->ch.peer)) { + err = bch->ch.recv(bch->ch.peer, skb); + if (err) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } + } +} + +int +mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) +{ + test_and_set_bit(FLG_HDLC, &ch->Flags); + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + ch->phfunc = phf; + skb_queue_head_init(&ch->squeue); + skb_queue_head_init(&ch->rqueue); + INIT_LIST_HEAD(&ch->dev.bchannels); + INIT_WORK(&ch->workq, dchannel_bh); + return 0; +} +EXPORT_SYMBOL(mISDN_initdchannel); + +int +mISDN_initbchannel(struct bchannel *ch, int maxlen) +{ + ch->Flags = 0; + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + skb_queue_head_init(&ch->rqueue); + ch->rcount = 0; + ch->next_skb = NULL; + INIT_WORK(&ch->workq, bchannel_bh); + return 0; +} +EXPORT_SYMBOL(mISDN_initbchannel); + +int +mISDN_freedchannel(struct dchannel *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + skb_queue_purge(&ch->squeue); + skb_queue_purge(&ch->rqueue); + flush_scheduled_work(); + return 0; +} +EXPORT_SYMBOL(mISDN_freedchannel); + +int +mISDN_freebchannel(struct bchannel *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + if (ch->next_skb) { + dev_kfree_skb(ch->next_skb); + ch->next_skb = NULL; + } + skb_queue_purge(&ch->rqueue); + ch->rcount = 0; + flush_scheduled_work(); + return 0; +} +EXPORT_SYMBOL(mISDN_freebchannel); + +static inline u_int +get_sapi_tei(u_char *p) +{ + u_int sapi, tei; + + sapi = *p >> 2; + tei = p[1] >> 1; + return sapi | (tei << 8); +} + +void +recv_Dchannel(struct dchannel *dch) +{ + struct mISDNhead *hh; + + if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + return; + } + hh = mISDN_HEAD_P(dch->rx_skb); + hh->prim = PH_DATA_IND; + hh->id = get_sapi_tei(dch->rx_skb->data); + skb_queue_tail(&dch->rqueue, dch->rx_skb); + dch->rx_skb = NULL; + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Dchannel); + +void +recv_Bchannel(struct bchannel *bch) +{ + struct mISDNhead *hh; + + hh = mISDN_HEAD_P(bch->rx_skb); + hh->prim = PH_DATA_IND; + hh->id = MISDN_ID_ANY; + if (bch->rcount >= 64) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, bch->rx_skb); + bch->rx_skb = NULL; + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Bchannel); + +void +recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) +{ + skb_queue_tail(&dch->rqueue, skb); + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Dchannel_skb); + +void +recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) +{ + if (bch->rcount >= 64) { + dev_kfree_skb(skb); + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, skb); + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Bchannel_skb); + +static void +confirm_Dsend(struct dchannel *dch) +{ + struct sk_buff *skb; + + skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), + 0, NULL, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: no skb id %x\n", __func__, + mISDN_HEAD_ID(dch->tx_skb)); + return; + } + skb_queue_tail(&dch->rqueue, skb); + schedule_event(dch, FLG_RECVQUEUE); +} + +int +get_next_dframe(struct dchannel *dch) +{ + dch->tx_idx = 0; + dch->tx_skb = skb_dequeue(&dch->squeue); + if (dch->tx_skb) { + confirm_Dsend(dch); + return 1; + } + dch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + return 0; +} +EXPORT_SYMBOL(get_next_dframe); + +void +confirm_Bsend(struct bchannel *bch) +{ + struct sk_buff *skb; + + if (bch->rcount >= 64) + return; + skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), + 0, NULL, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: no skb id %x\n", __func__, + mISDN_HEAD_ID(bch->tx_skb)); + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, skb); + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(confirm_Bsend); + +int +get_next_bframe(struct bchannel *bch) +{ + bch->tx_idx = 0; + if (test_bit(FLG_TX_NEXT, &bch->Flags)) { + bch->tx_skb = bch->next_skb; + if (bch->tx_skb) { + bch->next_skb = NULL; + test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); /* not for transparent */ + return 1; + } else { + test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); + printk(KERN_WARNING "B TX_NEXT without skb\n"); + } + } + bch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + return 0; +} +EXPORT_SYMBOL(get_next_bframe); + +void +queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) +{ + struct mISDNhead *hh; + + if (!skb) { + _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); + } else { + if (ch->peer) { + hh = mISDN_HEAD_P(skb); + hh->prim = pr; + hh->id = id; + if (!ch->recv(ch->peer, skb)) + return; + } + dev_kfree_skb(skb); + } +} +EXPORT_SYMBOL(queue_ch_frame); + +int +dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) +{ + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __func__); + return -EINVAL; + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __func__, skb->len, ch->maxlen); + return -EINVAL; + } + /* HW lock must be obtained */ + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + skb_queue_tail(&ch->squeue, skb); + return 0; + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + return 1; + } +} +EXPORT_SYMBOL(dchannel_senddata); + +int +bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) +{ + + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __func__); + return -EINVAL; + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __func__, skb->len, ch->maxlen); + return -EINVAL; + } + /* HW lock must be obtained */ + /* check for pending next_skb */ + if (ch->next_skb) { + printk(KERN_WARNING + "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", + __func__, skb->len, ch->next_skb->len); + return -EBUSY; + } + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + test_and_set_bit(FLG_TX_NEXT, &ch->Flags); + ch->next_skb = skb; + return 0; + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + return 1; + } +} +EXPORT_SYMBOL(bchannel_senddata); diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c new file mode 100644 index 000000000000..fced1a2755f8 --- /dev/null +++ b/drivers/isdn/mISDN/layer1.c @@ -0,0 +1,403 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + + +#include +#include +#include "layer1.h" +#include "fsm.h" + +static int *debug; + +struct layer1 { + u_long Flags; + struct FsmInst l1m; + struct FsmTimer timer; + int delay; + struct dchannel *dch; + dchannel_l1callback *dcb; +}; + +#define TIMER3_VALUE 7000 + +static +struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1S_STATE_COUNT (ST_L1_F8+1) + +static char *strL1SState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_ACTIVATE, + EV_PH_DEACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_ANYSIG_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_PH_DEACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_ANYSIG_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; + +static void +l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct layer1 *l1 = fi->userdata; + va_list va; + + va_start(va, fmt); + printk(KERN_DEBUG "%s: ", l1->dch->dev.name); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +static void +l1_reset(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) + l1->dcb(l1->dch, HW_POWERUP_REQ); +} + +static void +l1_deact_req_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); +} + +static void +l1_power_up_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + mISDN_FsmChangeState(fi, ST_L1_F4); + l1->dcb(l1->dch, INFO3_P8); + } else + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F5); +} + +static void +l1_go_F8(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F6); + l1->dcb(l1->dch, INFO3_P8); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F7); + l1->dcb(l1->dch, INFO3_P8); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 3); + mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + } + if (l1->l1m.state != ST_L1_F6) { + mISDN_FsmChangeState(fi, ST_L1_F3); + l1->dcb(l1->dch, HW_POWERUP_REQ); + } +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); + l1->dcb(l1->dch, PH_ACTIVATE_IND); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + l1->dcb(l1->dch, HW_DEACT_REQ); +} + +static void +l1_activate_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); + l1->dcb(l1->dch, HW_RESET_REQ); +} + +static void +l1_activate_no(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && + (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { + test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + } +} + +static struct FsmNode L1SFnList[] = +{ + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, + {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, + {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5}, + {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8}, + {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; + +static void +release_l1(struct layer1 *l1) { + mISDN_FsmDelTimer(&l1->timer, 0); + if (l1->dch) + l1->dch->l1 = NULL; + module_put(THIS_MODULE); + kfree(l1); +} + +int +l1_event(struct layer1 *l1, u_int event) +{ + int err = 0; + + if (!l1) + return -EINVAL; + switch (event) { + case HW_RESET_IND: + mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); + break; + case HW_DEACT_IND: + mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); + break; + case HW_POWERUP_IND: + mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); + break; + case HW_DEACT_CNF: + mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); + break; + case ANYSIGNAL: + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + break; + case LOSTFRAMING: + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + break; + case INFO2: + mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); + break; + case INFO4_P8: + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + break; + case INFO4_P10: + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + break; + case PH_ACTIVATE_REQ: + if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) + l1->dcb(l1->dch, PH_ACTIVATE_IND); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); + mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); + } + break; + case CLOSE_CHANNEL: + release_l1(l1); + break; + default: + if (*debug & DEBUG_L1) + printk(KERN_DEBUG "%s %x unhandled\n", + __func__, event); + err = -EINVAL; + } + return err; +} +EXPORT_SYMBOL(l1_event); + +int +create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { + struct layer1 *nl1; + + nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC); + if (!nl1) { + printk(KERN_ERR "kmalloc struct layer1 failed\n"); + return -ENOMEM; + } + nl1->l1m.fsm = &l1fsm_s; + nl1->l1m.state = ST_L1_F3; + nl1->Flags = 0; + nl1->l1m.debug = *debug & DEBUG_L1_FSM; + nl1->l1m.userdata = nl1; + nl1->l1m.userint = 0; + nl1->l1m.printdebug = l1m_debug; + nl1->dch = dch; + nl1->dcb = dcb; + mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer); + __module_get(THIS_MODULE); + dch->l1 = nl1; + return 0; +} +EXPORT_SYMBOL(create_l1); + +int +l1_init(u_int *deb) +{ + debug = deb; + l1fsm_s.state_count = L1S_STATE_COUNT; + l1fsm_s.event_count = L1_EVENT_COUNT; + l1fsm_s.strEvent = strL1Event; + l1fsm_s.strState = strL1SState; + mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); + return 0; +} + +void +l1_cleanup(void) +{ + mISDN_FsmFree(&l1fsm_s); +} diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h new file mode 100644 index 000000000000..9c8125fd89af --- /dev/null +++ b/drivers/isdn/mISDN/layer1.h @@ -0,0 +1,26 @@ +/* + * + * Layer 1 defines + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 +#define FLG_L1_UINT 7 +#define FLG_L1_DBLOCKED 8 + diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c new file mode 100644 index 000000000000..f5ad888ee71e --- /dev/null +++ b/drivers/isdn/mISDN/layer2.c @@ -0,0 +1,2216 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include "fsm.h" +#include "layer2.h" + +static int *debug; + +static +struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; + +static char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_2", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +enum { + EV_L2_UI, + EV_L2_SABME, + EV_L2_DISC, + EV_L2_DM, + EV_L2_UA, + EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, + EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNITDATA, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, + EV_L2_MDL_ASSIGN, + EV_L2_MDL_REMOVE, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, + EV_L2_T200, + EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, +}; + +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) + +static char *strL2Event[] = +{ + "EV_L2_UI", + "EV_L2_SABME", + "EV_L2_DISC", + "EV_L2_DM", + "EV_L2_UA", + "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", + "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNITDATA", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", + "EV_L2_MDL_ASSIGN", + "EV_L2_MDL_REMOVE", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", + "EV_L2_T200", + "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", +}; + +static void +l2m_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct layer2 *l2 = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_FSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "l2 (tei %d): ", l2->tei); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +inline u_int +l2headersize(struct layer2 *l2, int ui) +{ + return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); +} + +inline u_int +l2addrsize(struct layer2 *l2) +{ + return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1; +} + +static u_int +l2_newid(struct layer2 *l2) +{ + u_int id; + + id = l2->next_id++; + if (id == 0x7fff) + l2->next_id = 1; + id <<= 16; + id |= l2->tei << 8; + id |= l2->sapi; + return id; +} + +static void +l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb) +{ + int err; + + if (!l2->up) + return; + mISDN_HEAD_PRIM(skb) = prim; + mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr; + err = l2->up->send(l2->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static void +l2up_create(struct layer2 *l2, u_int prim, int len, void *arg) +{ + struct sk_buff *skb; + struct mISDNhead *hh; + int err; + + if (!l2->up) + return; + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = (l2->ch.nr << 16) | l2->ch.addr; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = l2->up->send(l2->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static int +l2down_skb(struct layer2 *l2, struct sk_buff *skb) { + int ret; + + ret = l2->ch.recv(l2->ch.peer, skb); + if (ret && (*debug & DEBUG_L2_RECV)) + printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret); + return ret; +} + +static int +l2down_raw(struct layer2 *l2, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + if (hh->prim == PH_DATA_REQ) { + if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { + skb_queue_tail(&l2->down_queue, skb); + return 0; + } + l2->down_id = mISDN_HEAD_ID(skb); + } + return l2down_skb(l2, skb); +} + +static int +l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + hh->prim = prim; + hh->id = id; + return l2down_raw(l2, skb); +} + +static int +l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg) +{ + struct sk_buff *skb; + int err; + struct mISDNhead *hh; + + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = id; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = l2down_raw(l2, skb); + if (err) + dev_kfree_skb(skb); + return err; +} + +static int +ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) { + struct sk_buff *nskb = skb; + int ret = -EAGAIN; + + if (test_bit(FLG_L1_NOTREADY, &l2->flag)) { + if (hh->id == l2->down_id) { + nskb = skb_dequeue(&l2->down_queue); + if (nskb) { + l2->down_id = mISDN_HEAD_ID(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + } + } else + l2->down_id = MISDN_ID_NONE; + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + if (l2->down_id == MISDN_ID_NONE) { + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } + } + } + if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { + nskb = skb_dequeue(&l2->down_queue); + if (nskb) { + l2->down_id = mISDN_HEAD_ID(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + } + } else + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + } + return ret; +} + +static int +l2mgr(struct layer2 *l2, u_int prim, void *arg) { + long c = (long)arg; + + printk(KERN_WARNING + "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c); + if (test_bit(FLG_LAPD, &l2->flag) && + !test_bit(FLG_FIXED_TEI, &l2->flag)) { + switch (c) { + case 'C': + case 'D': + case 'G': + case 'H': + l2_tei(l2, prim, (u_long)arg); + break; + } + } + return 0; +} + +static void +set_peer_busy(struct layer2 *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(struct layer2 *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +InitWin(struct layer2 *l2) +{ + int i; + + for (i = 0; i < MAX_WINDOW; i++) + l2->windowar[i] = NULL; +} + +static int +freewin(struct layer2 *l2) +{ + int i, cnt = 0; + + for (i = 0; i < MAX_WINDOW; i++) { + if (l2->windowar[i]) { + cnt++; + dev_kfree_skb(l2->windowar[i]); + l2->windowar[i] = NULL; + } + } + return cnt; +} + +static void +ReleaseWin(struct layer2 *l2) +{ + int cnt = freewin(l2); + + if (cnt) + printk(KERN_WARNING + "isdnl2 freed %d skbuffs in release\n", cnt); +} + +inline unsigned int +cansend(struct layer2 *l2) +{ + unsigned int p1; + + if (test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag); +} + +inline void +clear_exception(struct layer2 *l2) +{ + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + clear_peer_busy(l2); +} + +static int +sethdraddr(struct layer2 *l2, u_char *header, int rsp) +{ + u_char *ptr = header; + int crbit = rsp; + + if (test_bit(FLG_LAPD, &l2->flag)) { + if (test_bit(FLG_LAPD_NET, &l2->flag)) + crbit = !crbit; + *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; + return 2; + } else { + if (test_bit(FLG_ORIG, &l2->flag)) + crbit = !crbit; + if (crbit) + *ptr++ = l2->addr.B; + else + *ptr++ = l2->addr.A; + return 1; + } +} + +static inline void +enqueue_super(struct layer2 *l2, struct sk_buff *skb) +{ + if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +static inline void +enqueue_ui(struct layer2 *l2, struct sk_buff *skb) +{ + if (l2->tm) + l2_tei(l2, MDL_STATUS_UI_IND, 0); + if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +inline int +IsUI(u_char *data) +{ + return (data[0] & 0xef) == UI; +} + +inline int +IsUA(u_char *data) +{ + return (data[0] & 0xef) == UA; +} + +inline int +IsDM(u_char *data) +{ + return (data[0] & 0xef) == DM; +} + +inline int +IsDISC(u_char *data) +{ + return (data[0] & 0xef) == DISC; +} + +inline int +IsRR(u_char *data, struct layer2 *l2) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return data[0] == RR; + else + return (data[0] & 0xf) == 1; +} + +inline int +IsSFrame(u_char *data, struct layer2 *l2) +{ + register u_char d = *data; + + if (!test_bit(FLG_MOD128, &l2->flag)) + d &= 0xf; + return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c); +} + +inline int +IsSABME(u_char *data, struct layer2 *l2) +{ + u_char d = data[0] & ~0x10; + + return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM; +} + +inline int +IsREJ(u_char *data, struct layer2 *l2) +{ + return test_bit(FLG_MOD128, &l2->flag) ? + data[0] == REJ : (data[0] & 0xf) == REJ; +} + +inline int +IsFRMR(u_char *data) +{ + return (data[0] & 0xef) == FRMR; +} + +inline int +IsRNR(u_char *data, struct layer2 *l2) +{ + return test_bit(FLG_MOD128, &l2->flag) ? + data[0] == RNR : (data[0] & 0xf) == RNR; +} + +int +iframe_error(struct layer2 *l2, struct sk_buff *skb) +{ + u_int i; + int rsp = *skb->data & 0x2; + + i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len < i) + return 'N'; + if ((skb->len - i) > l2->maxlen) + return 'O'; + return 0; +} + +int +super_error(struct layer2 *l2, struct sk_buff *skb) +{ + if (skb->len != l2addrsize(l2) + + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) + return 'N'; + return 0; +} + +int +unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp) +{ + int rsp = (*skb->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp != wantrsp) + return 'L'; + if (skb->len != l2addrsize(l2) + 1) + return 'N'; + return 0; +} + +int +UI_error(struct layer2 *l2, struct sk_buff *skb) +{ + int rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len > l2->maxlen + l2addrsize(l2) + 1) + return 'O'; + return 0; +} + +int +FRMR_error(struct layer2 *l2, struct sk_buff *skb) +{ + u_int headers = l2addrsize(l2) + 1; + u_char *datap = skb->data + headers; + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (!rsp) + return 'L'; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len < headers + 5) + return 'N'; + else if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, + "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], datap[3], datap[4]); + } else { + if (skb->len < headers + 3) + return 'N'; + else if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, + "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + return 0; +} + +static unsigned int +legalnr(struct layer2 *l2, unsigned int nr) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); +} + +static void +setva(struct layer2 *l2, unsigned int nr) +{ + struct sk_buff *skb; + + while (l2->va != nr) { + l2->va++; + if (test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 8; + if (l2->windowar[l2->sow]) { + skb_trim(l2->windowar[l2->sow], 0); + skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + } + l2->sow = (l2->sow + 1) % l2->window; + } + skb = skb_dequeue(&l2->tmp_queue); + while (skb) { + dev_kfree_skb(skb); + skb = skb_dequeue(&l2->tmp_queue); + } +} + +static void +send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr) +{ + u_char tmp[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + tmp[i++] = cmd; + if (skb) + skb_trim(skb, 0); + else { + skb = mI_alloc_skb(i, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: can't alloc skbuff\n", + __func__); + return; + } + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + + +inline u_char +get_PollFlag(struct layer2 *l2, struct sk_buff *skb) +{ + return skb->data[l2addrsize(l2)] & 0x10; +} + +inline u_char +get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(l2, skb); + dev_kfree_skb(skb); + return PF; +} + +inline void +start_t200(struct layer2 *l2, int i) +{ + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +restart_t200(struct layer2 *l2, int i) +{ + mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +stop_t200(struct layer2 *l2, int i) +{ + if (test_and_clear_bit(FLG_T200_RUN, &l2->flag)) + mISDN_FsmDelTimer(&l2->t200, i); +} + +inline void +st5_dl_release_l2l3(struct layer2 *l2) +{ + int pr; + + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + pr = DL_RELEASE_CNF; + else + pr = DL_RELEASE_IND; + l2up_create(l2, pr, 0, NULL); +} + +inline void +lapb_dl_release_l2l3(struct layer2 *l2, int f) +{ + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL); + l2up_create(l2, f, 0, NULL); +} + +static void +establishlink(struct FsmInst *fi) +{ + struct layer2 *l2 = fi->userdata; + u_char cmd; + + clear_exception(l2); + l2->rc = 0; + cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; + send_uframe(l2, NULL, cmd, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 1); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'C'); + else + l2mgr(l2, MDL_ERROR_IND, (void *) 'D'); + +} + +static void +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); + else + l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + dev_kfree_skb((struct sk_buff *)arg); + mISDN_FsmChangeState(fi, ST_L2_3); +} + +static void +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L2_3); + dev_kfree_skb((struct sk_buff *)arg); + l2_tei(l2, MDL_ASSIGN_IND, 0); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + mISDN_FsmChangeState(fi, ST_L2_2); + l2_tei(l2, MDL_ASSIGN_IND, 0); +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); +} + +static void +tx_ui(struct layer2 *l2) +{ + struct sk_buff *skb; + u_char header[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_LAPD_NET, &l2->flag)) + header[1] = 0xff; /* tei 127 */ + header[i++] = UI; + while ((skb = skb_dequeue(&l2->ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(l2, skb); + } +} + +static void +l2_send_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + tx_ui(l2); +} + +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2headersize(l2, 1)); +/* + * in states 1-3 for broadcast + */ + + if (l2->tm) + l2_tei(l2, MDL_STATUS_UI_IND, 0); + l2up(l2, DL_UNITDATA_IND, skb); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_trim(skb, 0); + l2up(l2, DL_RELEASE_CNF, skb); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_6); + l2->rc = 0; + send_uframe(l2, NULL, DISC | 0x10, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 2); + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + clear_exception(l2); + send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + skb_trim(skb, 0); + l2up(l2, DL_ESTABLISH_IND, skb); + if (l2->tm) + l2_tei(l2, MDL_STATUS_UP_IND, 0); +} + +static void +l2_send_UA(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int est = 0; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + + l2mgr(l2, MDL_ERROR_IND, (void *) 'F'); + + if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + est = 1; + } + + clear_exception(l2); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + mISDN_FsmChangeState(fi, ST_L2_7); + stop_t200(l2, 3); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + + if (est) + l2up_create(l2, DL_ESTABLISH_IND, 0, NULL); +/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, + * MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED, + * 0, NULL, 0); + */ + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_stop_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_L2_4); + mISDN_FsmDelTimer(&l2->t203, 3); + stop_t200(l2, 4); + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + skb_queue_purge(&l2->i_queue); + freewin(l2); + lapb_dl_release_l2l3(l2, DL_RELEASE_IND); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_connected(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int pr = -1; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + l2_disconnect(fi, event, NULL); + if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { + pr = DL_ESTABLISH_CNF; + } else if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + pr = DL_ESTABLISH_IND; + } + stop_t200(l2, 5); + l2->vr = 0; + l2->vs = 0; + l2->va = 0; + l2->sow = 0; + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); + if (pr != -1) + l2up_create(l2, pr, 0, NULL); + + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + + if (l2->tm) + l2_tei(l2, MDL_STATUS_UP_IND, 0); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + stop_t200(l2, 6); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlagFree(l2, skb)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 7); + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_purge(&l2->i_queue); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } +} + +static void +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 8); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } +} + +void +enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf) +{ + struct sk_buff *skb; + u_char tmp[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + if (test_bit(FLG_MOD128, &l2->flag)) { + tmp[i++] = typ; + tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); + } else + tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); + skb = mI_alloc_skb(i, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING + "isdnl2 can't alloc sbbuff for enquiry_cr\n"); + return; + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + +inline void +enquiry_response(struct layer2 *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, RSP, 1); + else + enquiry_cr(l2, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); +} + +inline void +transmit_enquiry(struct layer2 *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, CMD, 1); + else + enquiry_cr(l2, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + start_t200(l2, 9); +} + + +static void +nrerrorrecovery(struct FsmInst *fi) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, (void *) 'J'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +invoke_retransmission(struct layer2 *l2, unsigned int nr) +{ + u_int p1; + + if (l2->vs != nr) { + while (l2->vs != nr) { + (l2->vs)--; + if (test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 8; + } + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + else + printk(KERN_WARNING + "%s: windowar[%d] is NULL\n", + __func__, p1); + l2->windowar[p1] = NULL; + } + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } +} + +static void +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, typ = RR; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + typ = RNR; + } else + clear_peer_busy(l2); + if (IsREJ(skb->data, l2)) + typ = REJ; + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + + if (PollFlag) { + if (rsp) + l2mgr(l2, MDL_ERROR_IND, (void *) 'A'); + else + enquiry_response(l2); + } + if (legalnr(l2, nr)) { + if (typ == REJ) { + setva(l2, nr); + invoke_retransmission(l2, nr); + stop_t200(l2, 10); + if (mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(l2, nr); + stop_t200(l2, 11); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(l2, nr); + if (typ != RR) + mISDN_FsmDelTimer(&l2->t203, 9); + restart_t200(l2, 12); + } + if (skb_queue_len(&l2->i_queue) && (typ == RR)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); +} + +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_tail(&l2->i_queue, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); +} + +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, i; + u_int ns, nr; + + i = l2addrsize(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; + } else { + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + dev_kfree_skb(skb); + if (PollFlag) + enquiry_response(l2); + } else { + if (l2->vr == ns) { + l2->vr++; + if (test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) + enquiry_response(l2); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); + skb_pull(skb, l2headersize(l2, 0)); + l2up(l2, DL_DATA_IND, skb); + } else { + /* n(s)!=v(r) */ + dev_kfree_skb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(l2); + } else { + enquiry_cr(l2, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + } + } + if (legalnr(l2, nr)) { + if (!test_bit(FLG_PEER_BUSY, &l2->flag) && + (fi->state == ST_L2_7)) { + if (nr == l2->vs) { + stop_t200(l2, 13); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if (nr != l2->va) + restart_t200(l2, 14); + } + setva(l2, nr); + } else { + nrerrorrecovery(fi); + return; + } + if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) + enquiry_cr(l2, RR, RSP, 0); +} + +static void +l2_got_tei(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + u_int info; + + l2->tei = (signed char)(long)arg; + set_channel_address(&l2->ch, l2->sapi, l2->tei); + info = DL_INFO_L2_CONNECT; + l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } else + mISDN_FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&l2->ui_queue)) + tx_ui(l2); +} + +static void +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + skb_queue_purge(&l2->i_queue); + l2mgr(l2, MDL_ERROR_IND, (void *) 'G'); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + st5_dl_release_l2l3(l2); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? + SABME : SABM) | 0x10, CMD); + } +} + +static void +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2mgr(l2, MDL_ERROR_IND, (void *) 'H'); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, + NULL, 9); + send_uframe(l2, NULL, DISC | 0x10, CMD); + } +} + +static void +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2->rc = 0; + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + if (l2->rc == l2->N200) { + l2mgr(l2, MDL_ERROR_IND, (void *) 'I'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } else { + transmit_enquiry(l2); + l2->rc++; + } +} + +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); + return; + } + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc = 0; +} + +static void +l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb, *nskb, *oskb; + u_char header[MAX_L2HEADER_LEN]; + u_int i, p1; + + if (!cansend(l2)) + return; + + skb = skb_dequeue(&l2->i_queue); + if (!skb) + return; + + if (test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) { + printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", + p1); + dev_kfree_skb(l2->windowar[p1]); + } + l2->windowar[p1] = skb; + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_MOD128, &l2->flag)) { + header[i++] = l2->vs << 1; + header[i++] = l2->vr << 1; + l2->vs = (l2->vs + 1) % 128; + } else { + header[i++] = (l2->vr << 5) | (l2->vs << 1); + l2->vs = (l2->vs + 1) % 8; + } + + nskb = skb_clone(skb, GFP_ATOMIC); + p1 = skb_headroom(nskb); + if (p1 >= i) + memcpy(skb_push(nskb, i), header, i); + else { + printk(KERN_WARNING + "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = nskb; + nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC); + if (!nskb) { + dev_kfree_skb(oskb); + printk(KERN_WARNING "%s: no skb mem\n", __func__); + return; + } + memcpy(skb_put(nskb, i), header, i); + memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len); + dev_kfree_skb(oskb); + } + l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { + mISDN_FsmDelTimer(&l2->t203, 13); + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); + } +} + +static void +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, rnr = 0; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + rnr = 1; + } else + clear_peer_busy(l2); + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + if (rsp && PollFlag) { + if (legalnr(l2, nr)) { + if (rnr) { + restart_t200(l2, 15); + } else { + stop_t200(l2, 16); + mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + setva(l2, nr); + } + invoke_retransmission(l2, nr); + mISDN_FsmChangeState(fi, ST_L2_7); + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); + } else { + if (!rsp && PollFlag) + enquiry_response(l2); + if (legalnr(l2, nr)) + setva(l2, nr); + else + nrerrorrecovery(fi); + } +} + +static void +l2_got_FRMR(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2addrsize(l2) + 1); + + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data) && (fi->state == ST_L2_7))) { + l2mgr(l2, MDL_ERROR_IND, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } + dev_kfree_skb(skb); +} + +static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + l2up_create(l2, DL_RELEASE_IND, 0, NULL); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = GROUP_TEI; + stop_t200(l2, 17); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + stop_t200(l2, 18); + l2up_create(l2, DL_RELEASE_IND, 0, NULL); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = GROUP_TEI; + stop_t200(l2, 17); + mISDN_FsmDelTimer(&l2->t203, 19); + l2up_create(l2, DL_RELEASE_IND, 0, NULL); +/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, + * MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED, + * 0, NULL, 0); + */ + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + l2up(l2, DL_RELEASE_IND, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + dev_kfree_skb(skb); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->ui_queue); + stop_t200(l2, 20); + l2up(l2, DL_RELEASE_CNF, skb); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + mISDN_FsmDelTimer(&l2->t203, 19); + l2up(l2, DL_RELEASE_IND, skb); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static struct FsmNode L2FnList[] = +{ + {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, + {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, + {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, + {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, + {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, + {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, +}; + +#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) + +static int +ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) +{ + u_char *datap = skb->data; + int ret = -EINVAL; + int psapi, ptei; + u_int l; + int c = 0; + + l = l2addrsize(l2); + if (skb->len <= l) { + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); + return ret; + } + if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ + psapi = *datap++; + ptei = *datap++; + if ((psapi & 1) || !(ptei & 1)) { + printk(KERN_WARNING + "l2 D-channel frame wrong EA0/EA1\n"); + return ret; + } + psapi >>= 2; + ptei >>= 1; + if (psapi != l2->sapi) { + /* not our bussiness + * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n", + * __func__, + * psapi, l2->sapi); + */ + dev_kfree_skb(skb); + return 0; + } + if ((ptei != l2->tei) && (ptei != GROUP_TEI)) { + /* not our bussiness + * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n", + * __func__, + * ptei, l2->tei, psapi); + */ + dev_kfree_skb(skb); + return 0; + } + } else + datap += l; + if (!(*datap & 1)) { /* I-Frame */ + c = iframe_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); + } else if (IsSFrame(datap, l2)) { /* S-Frame */ + c = super_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); + } else if (IsUI(datap)) { + c = UI_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, l2)) { + c = unnum_error(l2, skb, CMD); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { + c = unnum_error(l2, skb, RSP); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); + } else if (IsDISC(datap)) { + c = unnum_error(l2, skb, CMD); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); + } else if (IsDM(datap)) { + c = unnum_error(l2, skb, RSP); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); + } else if (IsFRMR(datap)) { + c = FRMR_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); + } else + c = 'L'; + if (c) { + printk(KERN_WARNING "l2 D-channel frame error %c\n", c); + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); + } + return ret; +} + +static int +l2_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct layer2 *l2 = container_of(ch, struct layer2, ch); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + + if (*debug & DEBUG_L2_RECV) + printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n", + __func__, hh->prim, hh->id, l2->tei); + switch (hh->prim) { + case PH_DATA_IND: + ret = ph_data_indication(l2, hh, skb); + break; + case PH_DATA_CNF: + ret = ph_data_confirm(l2, hh, skb); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_L1_ACTIV, &l2->flag); + l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); + l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb); + break; + case MPH_INFORMATION_IND: + if (!l2->up) + break; + ret = l2->up->send(l2->up, skb); + break; + case DL_DATA_REQ: + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb); + break; + case DL_UNITDATA_REQ: + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); + break; + case DL_ESTABLISH_REQ: + if (test_bit(FLG_LAPB, &l2->flag)) + test_and_set_bit(FLG_ORIG, &l2->flag); + if (test_bit(FLG_L1_ACTIV, &l2->flag)) { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + } else { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + test_and_set_bit(FLG_ESTAB_PEND, + &l2->flag); + } + ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2), + skb); + } + break; + case DL_RELEASE_REQ: + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, + skb); + break; + default: + if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, "l2 unknown pr %04x", + hh->prim); + } + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + return ret; +} + +int +tei_l2(struct layer2 *l2, u_int cmd, u_long arg) +{ + int ret = -EINVAL; + + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); + switch (cmd) { + case (MDL_ASSIGN_REQ): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg); + break; + case (MDL_REMOVE_REQ): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL); + break; + case (MDL_ERROR_IND): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); + break; + case (MDL_ERROR_RSP): + /* ETS 300-125 5.3.2.1 Test: TC13010 */ + printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n"); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); + break; + } + return ret; +} + +static void +release_l2(struct layer2 *l2) +{ + mISDN_FsmDelTimer(&l2->t200, 21); + mISDN_FsmDelTimer(&l2->t203, 16); + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + skb_queue_purge(&l2->down_queue); + ReleaseWin(l2); + if (test_bit(FLG_LAPD, &l2->flag)) { + release_tei(l2); + if (l2->ch.st) + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, + CLOSE_CHANNEL, NULL); + } + kfree(l2); +} + +static int +l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct layer2 *l2 = container_of(ch, struct layer2, ch); + u_int info; + + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); + + switch (cmd) { + case OPEN_CHANNEL: + if (test_bit(FLG_LAPD, &l2->flag)) { + set_channel_address(&l2->ch, l2->sapi, l2->tei); + info = DL_INFO_L2_CONNECT; + l2up_create(l2, DL_INFORMATION_IND, + sizeof(info), &info); + } + break; + case CLOSE_CHANNEL: + if (l2->ch.peer) + l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL); + release_l2(l2); + break; + } + return 0; +} + +struct layer2 * +create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg) +{ + struct layer2 *l2; + struct channel_req rq; + + l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL); + if (!l2) { + printk(KERN_ERR "kzalloc layer2 failed\n"); + return NULL; + } + l2->next_id = 1; + l2->down_id = MISDN_ID_NONE; + l2->up = ch; + l2->ch.st = ch->st; + l2->ch.send = l2_send; + l2->ch.ctrl = l2_ctrl; + switch (protocol) { + case ISDN_P_LAPD_NT: + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_LAPD_NET, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); + l2->sapi = 0; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; + else + l2->window = 1; + if (test_bit(OPTION_L2_PTP, &options)) + test_and_set_bit(FLG_PTP, &l2->flag); + if (test_bit(OPTION_L2_FIXEDTEI, &options)) + test_and_set_bit(FLG_FIXED_TEI, &l2->flag); + l2->tei = (u_int)arg; + l2->T200 = 1000; + l2->N200 = 3; + l2->T203 = 10000; + if (test_bit(OPTION_L2_PMX, &options)) + rq.protocol = ISDN_P_NT_E1; + else + rq.protocol = ISDN_P_NT_S0; + rq.adr.channel = 0; + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); + break; + case ISDN_P_LAPD_TE: + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); + test_and_set_bit(FLG_ORIG, &l2->flag); + l2->sapi = 0; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; + else + l2->window = 1; + if (test_bit(OPTION_L2_PTP, &options)) + test_and_set_bit(FLG_PTP, &l2->flag); + if (test_bit(OPTION_L2_FIXEDTEI, &options)) + test_and_set_bit(FLG_FIXED_TEI, &l2->flag); + l2->tei = (u_int)arg; + l2->T200 = 1000; + l2->N200 = 3; + l2->T203 = 10000; + if (test_bit(OPTION_L2_PMX, &options)) + rq.protocol = ISDN_P_TE_E1; + else + rq.protocol = ISDN_P_TE_S0; + rq.adr.channel = 0; + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); + break; + case ISDN_P_B_X75SLP: + test_and_set_bit(FLG_LAPB, &l2->flag); + l2->window = 7; + l2->maxlen = MAX_DATA_SIZE; + l2->T200 = 1000; + l2->N200 = 4; + l2->T203 = 5000; + l2->addr.A = 3; + l2->addr.B = 1; + break; + default: + printk(KERN_ERR "layer2 create failed prt %x\n", + protocol); + kfree(l2); + return NULL; + } + skb_queue_head_init(&l2->i_queue); + skb_queue_head_init(&l2->ui_queue); + skb_queue_head_init(&l2->down_queue); + skb_queue_head_init(&l2->tmp_queue); + InitWin(l2); + l2->l2m.fsm = &l2fsm; + if (test_bit(FLG_LAPB, &l2->flag) || + test_bit(FLG_PTP, &l2->flag) || + test_bit(FLG_LAPD_NET, &l2->flag)) + l2->l2m.state = ST_L2_4; + else + l2->l2m.state = ST_L2_1; + l2->l2m.debug = *debug; + l2->l2m.userdata = l2; + l2->l2m.userint = 0; + l2->l2m.printdebug = l2m_debug; + + mISDN_FsmInitTimer(&l2->l2m, &l2->t200); + mISDN_FsmInitTimer(&l2->l2m, &l2->t203); + return l2; +} + +static int +x75create(struct channel_req *crq) +{ + struct layer2 *l2; + + if (crq->protocol != ISDN_P_B_X75SLP) + return -EPROTONOSUPPORT; + l2 = create_l2(crq->ch, crq->protocol, 0, 0); + if (!l2) + return -ENOMEM; + crq->ch = &l2->ch; + crq->protocol = ISDN_P_B_HDLC; + return 0; +} + +static struct Bprotocol X75SLP = { + .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)), + .name = "X75SLP", + .create = x75create +}; + +int +Isdnl2_Init(u_int *deb) +{ + debug = deb; + mISDN_register_Bprotocol(&X75SLP); + l2fsm.state_count = L2_STATE_COUNT; + l2fsm.event_count = L2_EVENT_COUNT; + l2fsm.strEvent = strL2Event; + l2fsm.strState = strL2State; + mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); + TEIInit(deb); + return 0; +} + +void +Isdnl2_cleanup(void) +{ + mISDN_unregister_Bprotocol(&X75SLP); + TEIFree(); + mISDN_FsmFree(&l2fsm); +} + diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h new file mode 100644 index 000000000000..de2dd02056a3 --- /dev/null +++ b/drivers/isdn/mISDN/layer2.h @@ -0,0 +1,140 @@ +/* + * Layer 2 defines + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include +#include "fsm.h" + +#define MAX_WINDOW 8 + +struct manager { + struct mISDNchannel ch; + struct mISDNchannel bcast; + u_long options; + struct list_head layer2; + rwlock_t lock; + struct FsmInst deact; + struct FsmTimer datimer; + struct sk_buff_head sendq; + struct mISDNchannel *up; + u_int nextid; + u_int lastid; +}; + +struct teimgr { + int ri; + int rcnt; + struct FsmInst tei_m; + struct FsmTimer timer; + int tval, nval; + struct layer2 *l2; + struct manager *mgr; +}; + +struct laddr { + u_char A; + u_char B; +}; + +struct layer2 { + struct list_head list; + struct mISDNchannel ch; + u_long flag; + int id; + struct mISDNchannel *up; + signed char sapi; + signed char tei; + struct laddr addr; + u_int maxlen; + struct teimgr *tm; + u_int vs, va, vr; + int rc; + u_int window; + u_int sow; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + u_int next_id; + u_int down_id; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; + struct sk_buff_head down_queue; + struct sk_buff_head tmp_queue; +}; + +enum { + ST_L2_1, + ST_L2_2, + ST_L2_3, + ST_L2_4, + ST_L2_5, + ST_L2_6, + ST_L2_7, + ST_L2_8, +}; + +#define L2_STATE_COUNT (ST_L2_8+1) + +extern struct layer2 *create_l2(struct mISDNchannel *, u_int, + u_long, u_long); +extern int tei_l2(struct layer2 *, u_int, u_long arg); + + +/* from tei.c */ +extern int l2_tei(struct layer2 *, u_int, u_long arg); +extern void release_tei(struct layer2 *); +extern int TEIInit(u_int *); +extern void TEIFree(void); + +#define MAX_L2HEADER_LEN 4 + +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define SABME 0x6f +#define SABM 0x2f +#define DM 0x0f +#define UI 0x03 +#define DISC 0x43 +#define UA 0x63 +#define FRMR 0x87 +#define XID 0xaf + +#define CMD 0 +#define RSP 1 + +#define LC_FLUSH_WAIT 1 + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 +#define FLG_L1_NOTREADY 17 +#define FLG_LAPD_NET 18 diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c new file mode 100644 index 000000000000..4ba4cc364c9e --- /dev/null +++ b/drivers/isdn/mISDN/socket.c @@ -0,0 +1,781 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include "core.h" + +static int *debug; + +static struct proto mISDN_proto = { + .name = "misdn", + .owner = THIS_MODULE, + .obj_size = sizeof(struct mISDN_sock) +}; + +#define _pms(sk) ((struct mISDN_sock *)sk) + +static struct mISDN_sock_list data_sockets = { + .lock = __RW_LOCK_UNLOCKED(data_sockets.lock) +}; + +static struct mISDN_sock_list base_sockets = { + .lock = __RW_LOCK_UNLOCKED(base_sockets.lock) +}; + +#define L2_HEADER_LEN 4 + +static inline struct sk_buff * +_l2_alloc_skb(unsigned int len, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask); + if (likely(skb)) + skb_reserve(skb, L2_HEADER_LEN); + return skb; +} + +static void +mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk) +{ + write_lock_bh(&l->lock); + sk_add_node(sk, &l->head); + write_unlock_bh(&l->lock); +} + +static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk) +{ + write_lock_bh(&l->lock); + sk_del_node_init(sk); + write_unlock_bh(&l->lock); +} + +static int +mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDN_sock *msk; + int err; + + msk = container_of(ch, struct mISDN_sock, ch); + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb); + if (msk->sk.sk_state == MISDN_CLOSED) + return -EUNATCH; + __net_timestamp(skb); + err = sock_queue_rcv_skb(&msk->sk, skb); + if (err) + printk(KERN_WARNING "%s: error %d\n", __func__, err); + return err; +} + +static int +mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDN_sock *msk; + + msk = container_of(ch, struct mISDN_sock, ch); + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + msk->sk.sk_state = MISDN_CLOSED; + break; + } + return 0; +} + +static inline void +mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) +{ + struct timeval tv; + + if (_pms(sk)->cmask & MISDN_TIME_STAMP) { + skb_get_timestamp(skb, &tv); + put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv); + } +} + +static int +mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sk_buff *skb; + struct sock *sk = sock->sk; + struct sockaddr_mISDN *maddr; + + int copied, err; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n", + __func__, (int)len, flags, _pms(sk)->ch.nr, + sk->sk_protocol); + if (flags & (MSG_OOB)) + return -EOPNOTSUPP; + + if (sk->sk_state == MISDN_CLOSED) + return 0; + + skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err); + if (!skb) + return err; + + if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { + msg->msg_namelen = sizeof(struct sockaddr_mISDN); + maddr = (struct sockaddr_mISDN *)msg->msg_name; + maddr->family = AF_ISDN; + maddr->dev = _pms(sk)->dev->id; + if ((sk->sk_protocol == ISDN_P_LAPD_TE) || + (sk->sk_protocol == ISDN_P_LAPD_NT)) { + maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff; + maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff; + maddr->sapi = mISDN_HEAD_ID(skb) & 0xff; + } else { + maddr->channel = _pms(sk)->ch.nr; + maddr->sapi = _pms(sk)->ch.addr & 0xFF; + maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF; + } + } else { + if (msg->msg_namelen) + printk(KERN_WARNING "%s: too small namelen %d\n", + __func__, msg->msg_namelen); + msg->msg_namelen = 0; + } + + copied = skb->len + MISDN_HEADER_LEN; + if (len < copied) { + if (flags & MSG_PEEK) + atomic_dec(&skb->users); + else + skb_queue_head(&sk->sk_receive_queue, skb); + return -ENOSPC; + } + memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb), + MISDN_HEADER_LEN); + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + + mISDN_sock_cmsg(sk, msg, skb); + + skb_free_datagram(sk, skb); + + return err ? : copied; +} + +static int +mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct sk_buff *skb; + int err = -ENOMEM; + struct sockaddr_mISDN *maddr; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n", + __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr, + sk->sk_protocol); + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) + return -EINVAL; + + if (len < MISDN_HEADER_LEN) + return -EINVAL; + + if (sk->sk_state != MISDN_BOUND) + return -EBADFD; + + lock_sock(sk); + + skb = _l2_alloc_skb(len, GFP_KERNEL); + if (!skb) + goto done; + + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + err = -EFAULT; + goto drop; + } + + memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN); + skb_pull(skb, MISDN_HEADER_LEN); + + if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { + /* if we have a address, we use it */ + maddr = (struct sockaddr_mISDN *)msg->msg_name; + mISDN_HEAD_ID(skb) = maddr->channel; + } else { /* use default for L2 messages */ + if ((sk->sk_protocol == ISDN_P_LAPD_TE) || + (sk->sk_protocol == ISDN_P_LAPD_NT)) + mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr; + } + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: ID:%x\n", + __func__, mISDN_HEAD_ID(skb)); + + err = -ENODEV; + if (!_pms(sk)->ch.peer || + (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb))) + goto drop; + + err = len; + +done: + release_sock(sk); + return err; + +drop: + kfree_skb(skb); + goto done; +} + +static int +data_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (!sk) + return 0; + switch (sk->sk_protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (sk->sk_state == MISDN_BOUND) + delete_channel(&_pms(sk)->ch); + else + mISDN_sock_unlink(&data_sockets, sk); + break; + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + delete_channel(&_pms(sk)->ch); + mISDN_sock_unlink(&data_sockets, sk); + break; + } + + lock_sock(sk); + + sock_orphan(sk); + skb_queue_purge(&sk->sk_receive_queue); + + release_sock(sk); + sock_put(sk); + + return 0; +} + +static int +data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p) +{ + struct mISDN_ctrl_req cq; + int err = -EINVAL, val; + struct mISDNchannel *bchan, *next; + + lock_sock(sk); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + switch (cmd) { + case IMCTRLREQ: + if (copy_from_user(&cq, p, sizeof(cq))) { + err = -EFAULT; + break; + } + if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) { + list_for_each_entry_safe(bchan, next, + &_pms(sk)->dev->bchannels, list) { + if (bchan->nr == cq.channel) { + err = bchan->ctrl(bchan, + CONTROL_CHANNEL, &cq); + break; + } + } + } else + err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D, + CONTROL_CHANNEL, &cq); + if (err) + break; + if (copy_to_user(p, &cq, sizeof(cq))) + err = -EFAULT; + break; + case IMCLEAR_L2: + if (sk->sk_protocol != ISDN_P_LAPD_NT) { + err = -EINVAL; + break; + } + if (get_user(val, (int __user *)p)) { + err = -EFAULT; + break; + } + err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, + CONTROL_CHANNEL, &val); + break; + default: + err = -EINVAL; + break; + } +done: + release_sock(sk); + return err; +} + +static int +data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err = 0, id; + struct sock *sk = sock->sk; + struct mISDNdevice *dev; + struct mISDNversion ver; + + switch (cmd) { + case IMGETVERSION: + ver.major = MISDN_MAJOR_VERSION; + ver.minor = MISDN_MINOR_VERSION; + ver.release = MISDN_RELEASE; + if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) + err = -EFAULT; + break; + case IMGETCOUNT: + id = get_mdevice_count(); + if (put_user(id, (int __user *)arg)) + err = -EFAULT; + break; + case IMGETDEVINFO: + if (get_user(id, (int __user *)arg)) { + err = -EFAULT; + break; + } + dev = get_mdevice(id); + if (dev) { + struct mISDN_devinfo di; + + di.id = dev->id; + di.Dprotocols = dev->Dprotocols; + di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); + di.protocol = dev->D.protocol; + memcpy(di.channelmap, dev->channelmap, + MISDN_CHMAP_SIZE * 4); + di.nrbchan = dev->nrbchan; + strcpy(di.name, dev->name); + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + err = -EFAULT; + } else + err = -ENODEV; + break; + default: + if (sk->sk_state == MISDN_BOUND) + err = data_sock_ioctl_bound(sk, cmd, + (void __user *)arg); + else + err = -ENOTCONN; + } + return err; +} + +static int data_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int len) +{ + struct sock *sk = sock->sk; + int err = 0, opt = 0; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock, + level, optname, optval, len); + + lock_sock(sk); + + switch (optname) { + case MISDN_TIME_STAMP: + if (get_user(opt, (int __user *)optval)) { + err = -EFAULT; + break; + } + + if (opt) + _pms(sk)->cmask |= MISDN_TIME_STAMP; + else + _pms(sk)->cmask &= ~MISDN_TIME_STAMP; + break; + default: + err = -ENOPROTOOPT; + break; + } + release_sock(sk); + return err; +} + +static int data_sock_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, opt; + + if (get_user(len, optlen)) + return -EFAULT; + + switch (optname) { + case MISDN_TIME_STAMP: + if (_pms(sk)->cmask & MISDN_TIME_STAMP) + opt = 1; + else + opt = 0; + + if (put_user(opt, optval)) + return -EFAULT; + break; + default: + return -ENOPROTOOPT; + } + + return 0; +} + +static int +data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + int err = 0; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (addr_len != sizeof(struct sockaddr_mISDN)) + return -EINVAL; + if (!maddr || maddr->family != AF_ISDN) + return -EINVAL; + + lock_sock(sk); + + if (_pms(sk)->dev) { + err = -EALREADY; + goto done; + } + _pms(sk)->dev = get_mdevice(maddr->dev); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + _pms(sk)->ch.send = mISDN_send; + _pms(sk)->ch.ctrl = mISDN_ctrl; + + switch (sk->sk_protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + mISDN_sock_unlink(&data_sockets, sk); + err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + if (err) + mISDN_sock_link(&data_sockets, sk); + break; + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + break; + default: + err = -EPROTONOSUPPORT; + } + if (err) + goto done; + sk->sk_state = MISDN_BOUND; + _pms(sk)->ch.protocol = sk->sk_protocol; + +done: + release_sock(sk); + return err; +} + +static int +data_sock_getname(struct socket *sock, struct sockaddr *addr, + int *addr_len, int peer) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + + if (!_pms(sk)->dev) + return -EBADFD; + + lock_sock(sk); + + *addr_len = sizeof(*maddr); + maddr->dev = _pms(sk)->dev->id; + maddr->channel = _pms(sk)->ch.nr; + maddr->sapi = _pms(sk)->ch.addr & 0xff; + maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff; + release_sock(sk); + return 0; +} + +static const struct proto_ops data_sock_ops = { + .family = PF_ISDN, + .owner = THIS_MODULE, + .release = data_sock_release, + .ioctl = data_sock_ioctl, + .bind = data_sock_bind, + .getname = data_sock_getname, + .sendmsg = mISDN_sock_sendmsg, + .recvmsg = mISDN_sock_recvmsg, + .poll = datagram_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = data_sock_setsockopt, + .getsockopt = data_sock_getsockopt, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .mmap = sock_no_mmap +}; + +static int +data_sock_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + + if (sock->type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + + sock->ops = &data_sock_ops; + sock->state = SS_UNCONNECTED; + sock_reset_flag(sk, SOCK_ZAPPED); + + sk->sk_protocol = protocol; + sk->sk_state = MISDN_OPEN; + mISDN_sock_link(&data_sockets, sk); + + return 0; +} + +static int +base_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (!sk) + return 0; + + mISDN_sock_unlink(&base_sockets, sk); + sock_orphan(sk); + sock_put(sk); + + return 0; +} + +static int +base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err = 0, id; + struct mISDNdevice *dev; + struct mISDNversion ver; + + switch (cmd) { + case IMGETVERSION: + ver.major = MISDN_MAJOR_VERSION; + ver.minor = MISDN_MINOR_VERSION; + ver.release = MISDN_RELEASE; + if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) + err = -EFAULT; + break; + case IMGETCOUNT: + id = get_mdevice_count(); + if (put_user(id, (int __user *)arg)) + err = -EFAULT; + break; + case IMGETDEVINFO: + if (get_user(id, (int __user *)arg)) { + err = -EFAULT; + break; + } + dev = get_mdevice(id); + if (dev) { + struct mISDN_devinfo di; + + di.id = dev->id; + di.Dprotocols = dev->Dprotocols; + di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); + di.protocol = dev->D.protocol; + memcpy(di.channelmap, dev->channelmap, + MISDN_CHMAP_SIZE * 4); + di.nrbchan = dev->nrbchan; + strcpy(di.name, dev->name); + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + err = -EFAULT; + } else + err = -ENODEV; + break; + default: + err = -EINVAL; + } + return err; +} + +static int +base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + int err = 0; + + if (!maddr || maddr->family != AF_ISDN) + return -EINVAL; + + lock_sock(sk); + + if (_pms(sk)->dev) { + err = -EALREADY; + goto done; + } + + _pms(sk)->dev = get_mdevice(maddr->dev); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + sk->sk_state = MISDN_BOUND; + +done: + release_sock(sk); + return err; +} + +static const struct proto_ops base_sock_ops = { + .family = PF_ISDN, + .owner = THIS_MODULE, + .release = base_sock_release, + .ioctl = base_sock_ioctl, + .bind = base_sock_bind, + .getname = sock_no_getname, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .poll = sock_no_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .mmap = sock_no_mmap +}; + + +static int +base_sock_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + + if (sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->ops = &base_sock_ops; + sock->state = SS_UNCONNECTED; + sock_reset_flag(sk, SOCK_ZAPPED); + sk->sk_protocol = protocol; + sk->sk_state = MISDN_OPEN; + mISDN_sock_link(&base_sockets, sk); + + return 0; +} + +static int +mISDN_sock_create(struct net *net, struct socket *sock, int proto) +{ + int err = -EPROTONOSUPPORT; + + switch (proto) { + case ISDN_P_BASE: + err = base_sock_create(net, sock, proto); + break; + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + err = data_sock_create(net, sock, proto); + break; + default: + return err; + } + + return err; +} + +static struct +net_proto_family mISDN_sock_family_ops = { + .owner = THIS_MODULE, + .family = PF_ISDN, + .create = mISDN_sock_create, +}; + +int +misdn_sock_init(u_int *deb) +{ + int err; + + debug = deb; + err = sock_register(&mISDN_sock_family_ops); + if (err) + printk(KERN_ERR "%s: error(%d)\n", __func__, err); + return err; +} + +void +misdn_sock_cleanup(void) +{ + sock_unregister(PF_ISDN); +} + diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c new file mode 100644 index 000000000000..54cfddcc4784 --- /dev/null +++ b/drivers/isdn/mISDN/stack.c @@ -0,0 +1,674 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include +#include "core.h" + +static u_int *debug; + +static inline void +_queue_message(struct mISDNstack *st, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + if (*debug & DEBUG_QUEUE_FUNC) + printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", + __func__, hh->prim, hh->id, skb); + skb_queue_tail(&st->msgq, skb); + if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { + test_and_set_bit(mISDN_STACK_WORK, &st->status); + wake_up_interruptible(&st->workq); + } +} + +int +mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb) +{ + _queue_message(ch->st, skb); + return 0; +} + +static struct mISDNchannel * +get_channel4id(struct mISDNstack *st, u_int id) +{ + struct mISDNchannel *ch; + + mutex_lock(&st->lmutex); + list_for_each_entry(ch, &st->layer2, list) { + if (id == ch->nr) + goto unlock; + } + ch = NULL; +unlock: + mutex_unlock(&st->lmutex); + return ch; +} + +static void +send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) +{ + struct hlist_node *node; + struct sock *sk; + struct sk_buff *cskb = NULL; + + read_lock(&sl->lock); + sk_for_each(sk, node, &sl->head) { + if (sk->sk_state != MISDN_BOUND) + continue; + if (!cskb) + cskb = skb_copy(skb, GFP_KERNEL); + if (!cskb) { + printk(KERN_WARNING "%s no skb\n", __func__); + break; + } + if (!sock_queue_rcv_skb(sk, cskb)) + cskb = NULL; + } + read_unlock(&sl->lock); + if (cskb) + dev_kfree_skb(cskb); +} + +static void +send_layer2(struct mISDNstack *st, struct sk_buff *skb) +{ + struct sk_buff *cskb; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct mISDNchannel *ch; + int ret; + + if (!st) + return; + mutex_lock(&st->lmutex); + if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */ + list_for_each_entry(ch, &st->layer2, list) { + if (list_is_last(&ch->list, &st->layer2)) { + cskb = skb; + skb = NULL; + } else { + cskb = skb_copy(skb, GFP_KERNEL); + } + if (cskb) { + ret = ch->send(ch, cskb); + if (ret) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d prim(%x) addr(%x)" + " err %d\n", + __func__, ch->nr, + hh->prim, ch->addr, ret); + dev_kfree_skb(cskb); + } + } else { + printk(KERN_WARNING "%s ch%d addr %x no mem\n", + __func__, ch->nr, ch->addr); + goto out; + } + } + } else { + list_for_each_entry(ch, &st->layer2, list) { + if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) { + ret = ch->send(ch, skb); + if (!ret) + skb = NULL; + goto out; + } + } + ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb); + if (!ret) + skb = NULL; + else if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d mgr prim(%x) addr(%x) err %d\n", + __func__, ch->nr, hh->prim, ch->addr, ret); + } +out: + mutex_unlock(&st->lmutex); + if (skb) + dev_kfree_skb(skb); +} + +static inline int +send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct mISDNchannel *ch; + int lm; + + lm = hh->prim & MISDN_LAYERMASK; + if (*debug & DEBUG_QUEUE_FUNC) + printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", + __func__, hh->prim, hh->id, skb); + if (lm == 0x1) { + if (!hlist_empty(&st->l1sock.head)) { + __net_timestamp(skb); + send_socklist(&st->l1sock, skb); + } + return st->layer1->send(st->layer1, skb); + } else if (lm == 0x2) { + if (!hlist_empty(&st->l1sock.head)) + send_socklist(&st->l1sock, skb); + send_layer2(st, skb); + return 0; + } else if (lm == 0x4) { + ch = get_channel4id(st, hh->id); + if (ch) + return ch->send(ch, skb); + else + printk(KERN_WARNING + "%s: dev(%s) prim(%x) id(%x) no channel\n", + __func__, st->dev->name, hh->prim, hh->id); + } else if (lm == 0x8) { + WARN_ON(lm == 0x8); + ch = get_channel4id(st, hh->id); + if (ch) + return ch->send(ch, skb); + else + printk(KERN_WARNING + "%s: dev(%s) prim(%x) id(%x) no channel\n", + __func__, st->dev->name, hh->prim, hh->id); + } else { + /* broadcast not handled yet */ + printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", + __func__, st->dev->name, hh->prim); + } + return -ESRCH; +} + +static void +do_clear_stack(struct mISDNstack *st) +{ +} + +static int +mISDNStackd(void *data) +{ + struct mISDNstack *st = data; + int err = 0; + +#ifdef CONFIG_SMP + lock_kernel(); +#endif + sigfillset(¤t->blocked); +#ifdef CONFIG_SMP + unlock_kernel(); +#endif + if (*debug & DEBUG_MSG_THREAD) + printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name); + + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } + + for (;;) { + struct sk_buff *skb; + + if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { + test_and_clear_bit(mISDN_STACK_WORK, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + } else + test_and_set_bit(mISDN_STACK_RUNNING, &st->status); + while (test_bit(mISDN_STACK_WORK, &st->status)) { + skb = skb_dequeue(&st->msgq); + if (!skb) { + test_and_clear_bit(mISDN_STACK_WORK, + &st->status); + /* test if a race happens */ + skb = skb_dequeue(&st->msgq); + if (!skb) + continue; + test_and_set_bit(mISDN_STACK_WORK, + &st->status); + } +#ifdef MISDN_MSG_STATS + st->msg_cnt++; +#endif + err = send_msg_to_layer(st, skb); + if (unlikely(err)) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s: %s prim(%x) id(%x) " + "send call(%d)\n", + __func__, st->dev->name, + mISDN_HEAD_PRIM(skb), + mISDN_HEAD_ID(skb), err); + dev_kfree_skb(skb); + continue; + } + if (unlikely(test_bit(mISDN_STACK_STOPPED, + &st->status))) { + test_and_clear_bit(mISDN_STACK_WORK, + &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, + &st->status); + break; + } + } + if (test_bit(mISDN_STACK_CLEARING, &st->status)) { + test_and_set_bit(mISDN_STACK_STOPPED, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + do_clear_stack(st); + test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); + test_and_set_bit(mISDN_STACK_RESTART, &st->status); + } + if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { + test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); + test_and_set_bit(mISDN_STACK_RUNNING, &st->status); + if (!skb_queue_empty(&st->msgq)) + test_and_set_bit(mISDN_STACK_WORK, + &st->status); + } + if (test_bit(mISDN_STACK_ABORT, &st->status)) + break; + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } +#ifdef MISDN_MSG_STATS + st->sleep_cnt++; +#endif + test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); + wait_event_interruptible(st->workq, (st->status & + mISDN_STACK_ACTION_MASK)); + if (*debug & DEBUG_MSG_THREAD) + printk(KERN_DEBUG "%s: %s wake status %08lx\n", + __func__, st->dev->name, st->status); + test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); + + test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); + + if (test_bit(mISDN_STACK_STOPPED, &st->status)) { + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); +#ifdef MISDN_MSG_STATS + st->stopped_cnt++; +#endif + } + } +#ifdef MISDN_MSG_STATS + printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " + "msg %d sleep %d stopped\n", + st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt); + printk(KERN_DEBUG + "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n", + st->dev->name, st->thread->utime, st->thread->stime); + printk(KERN_DEBUG + "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", + st->dev->name, st->thread->nvcsw, st->thread->nivcsw); + printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", + st->dev->name); +#endif + test_and_set_bit(mISDN_STACK_KILLED, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); + test_and_clear_bit(mISDN_STACK_ABORT, &st->status); + skb_queue_purge(&st->msgq); + st->thread = NULL; + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } + return 0; +} + +static int +l1_receive(struct mISDNchannel *ch, struct sk_buff *skb) +{ + if (!ch->st) + return -ENODEV; + __net_timestamp(skb); + _queue_message(ch->st, skb); + return 0; +} + +void +set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei) +{ + ch->addr = sapi | (tei << 8); +} + +void +__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) +{ + list_add_tail(&ch->list, &st->layer2); +} + +void +add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) +{ + mutex_lock(&st->lmutex); + __add_layer2(ch, st); + mutex_unlock(&st->lmutex); +} + +static int +st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + if (!ch->st || ch->st->layer1) + return -EINVAL; + return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg); +} + +int +create_stack(struct mISDNdevice *dev) +{ + struct mISDNstack *newst; + int err; + DECLARE_COMPLETION_ONSTACK(done); + + newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL); + if (!newst) { + printk(KERN_ERR "kmalloc mISDN_stack failed\n"); + return -ENOMEM; + } + newst->dev = dev; + INIT_LIST_HEAD(&newst->layer2); + INIT_HLIST_HEAD(&newst->l1sock.head); + rwlock_init(&newst->l1sock.lock); + init_waitqueue_head(&newst->workq); + skb_queue_head_init(&newst->msgq); + mutex_init(&newst->lmutex); + dev->D.st = newst; + err = create_teimanager(dev); + if (err) { + printk(KERN_ERR "kmalloc teimanager failed\n"); + kfree(newst); + return err; + } + dev->teimgr->peer = &newst->own; + dev->teimgr->recv = mISDN_queue_message; + dev->teimgr->st = newst; + newst->layer1 = &dev->D; + dev->D.recv = l1_receive; + dev->D.peer = &newst->own; + newst->own.st = newst; + newst->own.ctrl = st_own_ctrl; + newst->own.send = mISDN_queue_message; + newst->own.recv = mISDN_queue_message; + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name); + newst->notify = &done; + newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", + newst->dev->name); + if (IS_ERR(newst->thread)) { + err = PTR_ERR(newst->thread); + printk(KERN_ERR + "mISDN:cannot create kernel thread for %s (%d)\n", + newst->dev->name, err); + delete_teimanager(dev->teimgr); + kfree(newst); + } else + wait_for_completion(&done); + return err; +} + +int +connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); + struct channel_req rq; + int err; + + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, adr->dev, adr->channel, + adr->sapi, adr->tei); + switch (protocol) { + case ISDN_P_NT_S0: + case ISDN_P_NT_E1: + case ISDN_P_TE_S0: + case ISDN_P_TE_E1: +#ifdef PROTOCOL_CHECK + /* this should be enhanced */ + if (!list_empty(&dev->D.st->layer2) + && dev->D.protocol != protocol) + return -EBUSY; + if (!hlist_empty(&dev->D.st->l1sock.head) + && dev->D.protocol != protocol) + return -EBUSY; +#endif + ch->recv = mISDN_queue_message; + ch->peer = &dev->D.st->own; + ch->st = dev->D.st; + rq.protocol = protocol; + rq.adr.channel = 0; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + if (err) + return err; + write_lock_bh(&dev->D.st->l1sock.lock); + sk_add_node(&msk->sk, &dev->D.st->l1sock.head); + write_unlock_bh(&dev->D.st->l1sock.lock); + break; + default: + return -ENOPROTOOPT; + } + return 0; +} + +int +connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct channel_req rq, rq2; + int pmask, err; + struct Bprotocol *bp; + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, + adr->dev, adr->channel, adr->sapi, + adr->tei); + ch->st = dev->D.st; + pmask = 1 << (protocol & ISDN_P_B_MASK); + if (pmask & dev->Bprotocols) { + rq.protocol = protocol; + rq.adr = *adr; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + if (err) + return err; + ch->recv = rq.ch->send; + ch->peer = rq.ch; + rq.ch->recv = ch->send; + rq.ch->peer = ch; + rq.ch->st = dev->D.st; + } else { + bp = get_Bprotocol4mask(pmask); + if (!bp) + return -ENOPROTOOPT; + rq2.protocol = protocol; + rq2.adr = *adr; + rq2.ch = ch; + err = bp->create(&rq2); + if (err) + return err; + ch->recv = rq2.ch->send; + ch->peer = rq2.ch; + rq2.ch->st = dev->D.st; + rq.protocol = rq2.protocol; + rq.adr = *adr; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + if (err) { + rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL); + return err; + } + rq2.ch->recv = rq.ch->send; + rq2.ch->peer = rq.ch; + rq.ch->recv = rq2.ch->send; + rq.ch->peer = rq2.ch; + rq.ch->st = dev->D.st; + } + ch->protocol = protocol; + ch->nr = rq.ch->nr; + return 0; +} + +int +create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct channel_req rq; + int err; + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, + adr->dev, adr->channel, adr->sapi, + adr->tei); + rq.protocol = ISDN_P_TE_S0; + if (dev->Dprotocols & (1 << ISDN_P_TE_E1)) + rq.protocol = ISDN_P_TE_E1; + switch (protocol) { + case ISDN_P_LAPD_NT: + rq.protocol = ISDN_P_NT_S0; + if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) + rq.protocol = ISDN_P_NT_E1; + case ISDN_P_LAPD_TE: +#ifdef PROTOCOL_CHECK + /* this should be enhanced */ + if (!list_empty(&dev->D.st->layer2) + && dev->D.protocol != protocol) + return -EBUSY; + if (!hlist_empty(&dev->D.st->l1sock.head) + && dev->D.protocol != protocol) + return -EBUSY; +#endif + ch->recv = mISDN_queue_message; + ch->peer = &dev->D.st->own; + ch->st = dev->D.st; + rq.adr.channel = 0; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + if (err) + break; + rq.protocol = protocol; + rq.adr = *adr; + rq.ch = ch; + err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err); + if (!err) { + if ((protocol == ISDN_P_LAPD_NT) && !rq.ch) + break; + add_layer2(rq.ch, dev->D.st); + rq.ch->recv = mISDN_queue_message; + rq.ch->peer = &dev->D.st->own; + rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */ + } + break; + default: + err = -EPROTONOSUPPORT; + } + return err; +} + +void +delete_channel(struct mISDNchannel *ch) +{ + struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); + struct mISDNchannel *pch; + + if (!ch->st) { + printk(KERN_WARNING "%s: no stack\n", __func__); + return; + } + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, + ch->st->dev->name, ch->protocol); + if (ch->protocol >= ISDN_P_B_START) { + if (ch->peer) { + ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); + ch->peer = NULL; + } + return; + } + switch (ch->protocol) { + case ISDN_P_NT_S0: + case ISDN_P_TE_S0: + case ISDN_P_NT_E1: + case ISDN_P_TE_E1: + write_lock_bh(&ch->st->l1sock.lock); + sk_del_node_init(&msk->sk); + write_unlock_bh(&ch->st->l1sock.lock); + ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL); + break; + case ISDN_P_LAPD_TE: + pch = get_channel4id(ch->st, ch->nr); + if (pch) { + mutex_lock(&ch->st->lmutex); + list_del(&pch->list); + mutex_unlock(&ch->st->lmutex); + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + pch = ch->st->dev->teimgr; + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + } else + printk(KERN_WARNING "%s: no l2 channel\n", + __func__); + break; + case ISDN_P_LAPD_NT: + pch = ch->st->dev->teimgr; + if (pch) { + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + } else + printk(KERN_WARNING "%s: no l2 channel\n", + __func__); + break; + default: + break; + } + return; +} + +void +delete_stack(struct mISDNdevice *dev) +{ + struct mISDNstack *st = dev->D.st; + DECLARE_COMPLETION_ONSTACK(done); + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s)\n", __func__, + st->dev->name); + if (dev->teimgr) + delete_teimanager(dev->teimgr); + if (st->thread) { + if (st->notify) { + printk(KERN_WARNING "%s: notifier in use\n", + __func__); + complete(st->notify); + } + st->notify = &done; + test_and_set_bit(mISDN_STACK_ABORT, &st->status); + test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); + wake_up_interruptible(&st->workq); + wait_for_completion(&done); + } + if (!list_empty(&st->layer2)) + printk(KERN_WARNING "%s: layer2 list not empty\n", + __func__); + if (!hlist_empty(&st->l1sock.head)) + printk(KERN_WARNING "%s: layer1 list not empty\n", + __func__); + kfree(st); +} + +void +mISDN_initstack(u_int *dp) +{ + debug = dp; +} diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c new file mode 100644 index 000000000000..56a76a0ffddd --- /dev/null +++ b/drivers/isdn/mISDN/tei.c @@ -0,0 +1,1340 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ +#include "layer2.h" +#include +#include "core.h" + +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 + +#define TEI_ENTITY_ID 0xf + +#define MGR_PH_ACTIVE 16 +#define MGR_PH_NOTREADY 17 + +#define DATIMER_VAL 10000 + +static u_int *debug; + +static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL}; +static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL}; +static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_DEACT, + ST_L1_DEACT_PENDING, + ST_L1_ACTIV, +}; +#define DEACT_STATE_COUNT (ST_L1_ACTIV+1) + +static char *strDeactState[] = +{ + "ST_L1_DEACT", + "ST_L1_DEACT_PENDING", + "ST_L1_ACTIV", +}; + +enum { + EV_ACTIVATE, + EV_ACTIVATE_IND, + EV_DEACTIVATE, + EV_DEACTIVATE_IND, + EV_UI, + EV_DATIMER, +}; + +#define DEACT_EVENT_COUNT (EV_DATIMER+1) + +static char *strDeactEvent[] = +{ + "EV_ACTIVATE", + "EV_ACTIVATE_IND", + "EV_DEACTIVATE", + "EV_DEACTIVATE_IND", + "EV_UI", + "EV_DATIMER", +}; + +static void +da_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct manager *mgr = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_TEIFSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +static void +da_activate(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + + if (fi->state == ST_L1_DEACT_PENDING) + mISDN_FsmDelTimer(&mgr->datimer, 1); + mISDN_FsmChangeState(fi, ST_L1_ACTIV); +} + +static void +da_deactivate_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_DEACT); +} + +static void +da_deactivate(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + struct layer2 *l2; + u_long flags; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->l2m.state > ST_L2_4) { + /* have still activ TEI */ + read_unlock_irqrestore(&mgr->lock, flags); + return; + } + } + read_unlock_irqrestore(&mgr->lock, flags); + /* All TEI are inactiv */ + mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1); + mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING); +} + +static void +da_ui(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + + /* restart da timer */ + mISDN_FsmDelTimer(&mgr->datimer, 2); + mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2); + +} + +static void +da_timer(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + struct layer2 *l2; + u_long flags; + + /* check again */ + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->l2m.state > ST_L2_4) { + /* have still activ TEI */ + read_unlock_irqrestore(&mgr->lock, flags); + mISDN_FsmChangeState(fi, ST_L1_ACTIV); + return; + } + } + read_unlock_irqrestore(&mgr->lock, flags); + /* All TEI are inactiv */ + mISDN_FsmChangeState(fi, ST_L1_DEACT); + _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); +} + +static struct FsmNode DeactFnList[] = +{ + {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate}, + {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind}, + {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate}, + {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate}, + {ST_L1_DEACT_PENDING, EV_UI, da_ui}, + {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer}, +}; + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = +{ + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; + +enum { + EV_IDREQ, + EV_ASSIGN, + EV_ASSIGN_REQ, + EV_DENIED, + EV_CHKREQ, + EV_CHKRESP, + EV_REMOVE, + EV_VERIFY, + EV_TIMER, +}; + +#define TEI_EVENT_COUNT (EV_TIMER+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_ASSIGN_REQ", + "EV_DENIED", + "EV_CHKREQ", + "EV_CHKRESP", + "EV_REMOVE", + "EV_VERIFY", + "EV_TIMER", +}; + +static void +tei_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct teimgr *tm = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_TEIFSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "tei(%d): ", tm->l2->tei); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + + + +static int +get_free_id(struct manager *mgr) +{ + u64 ids = 0; + int i; + struct layer2 *l2; + + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->ch.nr > 63) { + printk(KERN_WARNING + "%s: more as 63 layer2 for one device\n", + __func__); + return -EBUSY; + } + test_and_set_bit(l2->ch.nr, (u_long *)&ids); + } + for (i = 1; i < 64; i++) + if (!test_bit(i, (u_long *)&ids)) + return i; + printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", + __func__); + return -EBUSY; +} + +static int +get_free_tei(struct manager *mgr) +{ + u64 ids = 0; + int i; + struct layer2 *l2; + + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->ch.nr == 0) + continue; + if ((l2->ch.addr & 0xff) != 0) + continue; + i = l2->ch.addr >> 8; + if (i < 64) + continue; + i -= 64; + + test_and_set_bit(i, (u_long *)&ids); + } + for (i = 0; i < 64; i++) + if (!test_bit(i, (u_long *)&ids)) + return i + 64; + printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", + __func__); + return -1; +} + +static void +teiup_create(struct manager *mgr, u_int prim, int len, void *arg) +{ + struct sk_buff *skb; + struct mISDNhead *hh; + int err; + + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = (mgr->ch.nr << 16) | mgr->ch.addr; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = mgr->up->send(mgr->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static u_int +new_id(struct manager *mgr) +{ + u_int id; + + id = mgr->nextid++; + if (id == 0x7fff) + mgr->nextid = 1; + id <<= 16; + id |= GROUP_TEI << 8; + id |= TEI_SAPI; + return id; +} + +static void +do_send(struct manager *mgr) +{ + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) + return; + + if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) { + struct sk_buff *skb = skb_dequeue(&mgr->sendq); + + if (!skb) { + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + return; + } + mgr->lastid = mISDN_HEAD_ID(skb); + mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); + if (mgr->ch.recv(mgr->ch.peer, skb)) { + dev_kfree_skb(skb); + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + mgr->lastid = MISDN_ID_NONE; + } + } +} + +static void +do_ack(struct manager *mgr, u_int id) +{ + if (test_bit(MGR_PH_NOTREADY, &mgr->options)) { + if (id == mgr->lastid) { + if (test_bit(MGR_PH_ACTIVE, &mgr->options)) { + struct sk_buff *skb; + + skb = skb_dequeue(&mgr->sendq); + if (skb) { + mgr->lastid = mISDN_HEAD_ID(skb); + if (!mgr->ch.recv(mgr->ch.peer, skb)) + return; + dev_kfree_skb(skb); + } + } + mgr->lastid = MISDN_ID_NONE; + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + } + } +} + +static void +mgr_send_down(struct manager *mgr, struct sk_buff *skb) +{ + skb_queue_tail(&mgr->sendq, skb); + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) { + _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + } else { + do_send(mgr); + } +} + +static int +dl_unit_data(struct manager *mgr, struct sk_buff *skb) +{ + if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */ + return -EINVAL; + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) + _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + skb_push(skb, 3); + skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */ + skb->data[1] = 0xff; /* TEI 127 */ + skb->data[2] = UI; /* UI frame */ + mISDN_HEAD_PRIM(skb) = PH_DATA_REQ; + mISDN_HEAD_ID(skb) = new_id(mgr); + skb_queue_tail(&mgr->sendq, skb); + do_send(mgr); + return 0; +} + +unsigned int +random_ri(void) +{ + u16 x; + + get_random_bytes(&x, sizeof(x)); + return x; +} + +static struct layer2 * +findtei(struct manager *mgr, int tei) +{ + struct layer2 *l2; + u_long flags; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if ((l2->sapi == 0) && (l2->tei > 0) && + (l2->tei != GROUP_TEI) && (l2->tei == tei)) + goto done; + } + l2 = NULL; +done: + read_unlock_irqrestore(&mgr->lock, flags); + return l2; +} + +static void +put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, u_char tei) +{ + struct sk_buff *skb; + u_char bp[8]; + + bp[0] = (TEI_SAPI << 2); + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) + bp[0] |= 2; /* CR:=1 for net command */ + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; + bp[3] = TEI_ENTITY_ID; + bp[4] = ri >> 8; + bp[5] = ri & 0xff; + bp[6] = m_id; + bp[7] = (tei << 1) | 1; + skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr), + 8, bp, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: no skb for tei msg\n", __func__); + return; + } + mgr_send_down(mgr, skb); +} + +static void +tei_id_request(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (tm->l2->tei != GROUP_TEI) { + tm->tei_m.printdebug(&tm->tei_m, + "assign request for allready assigned tei %d", + tm->l2->tei); + return; + } + tm->ri = random_ri(); + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, + "assign request ri %d", tm->ri); + put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); + mISDN_FsmChangeState(fi, ST_TEI_IDREQ); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1); + tm->nval = 3; +} + +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + struct layer2 *l2; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", + ri, tei); + l2 = findtei(tm->mgr, tei); + if (l2) { /* same tei is in use */ + if (ri != l2->tm->ri) { + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + tei_l2(l2, MDL_ERROR_RSP, 0); + } + } else if (ri == tm->ri) { + mISDN_FsmDelTimer(&tm->timer, 1); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + tei_l2(tm->l2, MDL_ASSIGN_REQ, tei); + } +} + +static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + struct layer2 *l2; + u_char *dp = arg; + int tei, ri; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", + ri, tei); + l2 = findtei(tm->mgr, tei); + if (l2) { /* same tei is in use */ + if (ri != l2->tm->ri) { /* and it wasn't our request */ + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL); + } + } +} + +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", + ri, tei); +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity check req tei %d", tei); + if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) || + (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->timer, 4); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei); + } +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity remove tei %d", tei); + if ((tm->l2->tei != GROUP_TEI) && + ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->timer, 5); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + tei_l2(tm->l2, MDL_REMOVE_REQ, 0); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "id verify request for tei %d", + tm->l2->tei); + put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); + tm->nval = 2; +} + +static void +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (--tm->nval) { + tm->ri = random_ri(); + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "assign req(%d) ri %d", + 4 - tm->nval, tm->ri); + put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3); + } else { + tm->tei_m.printdebug(fi, "assign req failed"); + tei_l2(tm->l2, MDL_ERROR_RSP, 0); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (--tm->nval) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "id verify req(%d) for tei %d", + 3 - tm->nval, tm->l2->tei); + put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "verify req for tei %d failed", + tm->l2->tei); + tei_l2(tm->l2, MDL_REMOVE_REQ, 0); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static struct FsmNode TeiFnListUser[] = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, + {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; + +static void +tei_l2remove(struct layer2 *l2) +{ + put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei); + tei_l2(l2, MDL_REMOVE_REQ, 0); + list_del(&l2->ch.list); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); +} + +static void +tei_assign_req(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + + if (tm->l2->tei == GROUP_TEI) { + tm->tei_m.printdebug(&tm->tei_m, + "net tei assign request without tei"); + return; + } + tm->ri = ((unsigned int) *dp++ << 8); + tm->ri += *dp++; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, + "net assign request ri %d teim %d", tm->ri, *dp); + put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); +} + +static void +tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "id check request for tei %d", + tm->l2->tei); + tm->rcnt = 0; + put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); + tm->nval = 2; +} + +static void +tei_id_chk_resp(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = dp[3] >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity check resp tei %d", tei); + if (tei == tm->l2->tei) + tm->rcnt++; +} + +static void +tei_id_verify_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = dp[3] >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity verify req tei %d/%d", + tei, tm->l2->tei); + if (tei == tm->l2->tei) + tei_id_chk_req_net(fi, event, arg); +} + +static void +tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (tm->rcnt == 1) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "check req for tei %d sucessful\n", tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } else if (tm->rcnt > 1) { + /* duplicate assignment; remove */ + tei_l2remove(tm->l2); + } else if (--tm->nval) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "id check req(%d) for tei %d", + 3 - tm->nval, tm->l2->tei); + put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "check req for tei %d failed", + tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + tei_l2remove(tm->l2); + } +} + +static struct FsmNode TeiFnListNet[] = +{ + {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net}, + {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net}, + {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp}, +}; + +static void +tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len) +{ + if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) + return; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); + if (mt == ID_ASSIGNED) + mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); + else if (mt == ID_DENIED) + mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); + else if (mt == ID_CHK_REQ) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); + else if (mt == ID_REMOVE) + mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); + else if (mt == ID_VERIFY) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp); + else if (mt == ID_CHK_RES) + mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp); +} + +static struct layer2 * +create_new_tei(struct manager *mgr, int tei) +{ + u_long opt = 0; + u_long flags; + int id; + struct layer2 *l2; + + if (!mgr->up) + return NULL; + if (tei < 64) + test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); + if (mgr->ch.st->dev->Dprotocols + & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) + test_and_set_bit(OPTION_L2_PMX, &opt); + l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei); + if (!l2) { + printk(KERN_WARNING "%s:no memory for layer2\n", __func__); + return NULL; + } + l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL); + if (!l2->tm) { + kfree(l2); + printk(KERN_WARNING "%s:no memory for teimgr\n", __func__); + return NULL; + } + l2->tm->mgr = mgr; + l2->tm->l2 = l2; + l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; + l2->tm->tei_m.userdata = l2->tm; + l2->tm->tei_m.printdebug = tei_debug; + l2->tm->tei_m.fsm = &teifsmn; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 2000; /* T202 2 sec */ + mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); + write_lock_irqsave(&mgr->lock, flags); + id = get_free_id(mgr); + list_add_tail(&l2->list, &mgr->layer2); + write_unlock_irqrestore(&mgr->lock, flags); + if (id < 0) { + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + printk(KERN_WARNING "%s:no free id\n", __func__); + return NULL; + } else { + l2->ch.nr = id; + __add_layer2(&l2->ch, mgr->ch.st); + l2->ch.recv = mgr->ch.recv; + l2->ch.peer = mgr->ch.peer; + l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); + } + return l2; +} + +static void +new_tei_req(struct manager *mgr, u_char *dp) +{ + int tei, ri; + struct layer2 *l2; + + ri = dp[0] << 8; + ri += dp[1]; + if (!mgr->up) + goto denied; + tei = get_free_tei(mgr); + if (tei < 0) { + printk(KERN_WARNING "%s:No free tei\n", __func__); + goto denied; + } + l2 = create_new_tei(mgr, tei); + if (!l2) + goto denied; + else + mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); + return; +denied: + put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI); +} + +static int +ph_data_ind(struct manager *mgr, struct sk_buff *skb) +{ + int ret = -EINVAL; + struct layer2 *l2; + u_long flags; + u_char mt; + + if (skb->len < 8) { + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: short mgr frame %d/8\n", + __func__, skb->len); + goto done; + } + if (*debug & DEBUG_L2_TEI) + + if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */ + goto done; + if (skb->data[0] & 1) /* EA0 formal error */ + goto done; + if (!(skb->data[1] & 1)) /* EA1 formal error */ + goto done; + if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */ + goto done; + if ((skb->data[2] & 0xef) != UI) /* not UI */ + goto done; + if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */ + goto done; + mt = skb->data[6]; + switch (mt) { + case ID_REQUEST: + case ID_CHK_RES: + case ID_VERIFY: + if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) + goto done; + break; + case ID_ASSIGNED: + case ID_DENIED: + case ID_CHK_REQ: + case ID_REMOVE: + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) + goto done; + break; + default: + goto done; + } + ret = 0; + if (mt == ID_REQUEST) { + new_tei_req(mgr, &skb->data[4]); + goto done; + } + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4); + } + read_unlock_irqrestore(&mgr->lock, flags); +done: + return ret; +} + +int +l2_tei(struct layer2 *l2, u_int cmd, u_long arg) +{ + struct teimgr *tm = l2->tm; + + if (test_bit(FLG_FIXED_TEI, &l2->flag)) + return 0; + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); + switch (cmd) { + case MDL_ASSIGN_IND: + mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); + break; + case MDL_ERROR_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei); + if (test_bit(MGR_OPT_USER, &tm->mgr->options)) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); + break; + case MDL_STATUS_UP_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL); + break; + case MDL_STATUS_DOWN_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL); + break; + case MDL_STATUS_UI_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL); + break; + } + return 0; +} + +void +release_tei(struct layer2 *l2) +{ + struct teimgr *tm = l2->tm; + u_long flags; + + mISDN_FsmDelTimer(&tm->timer, 1); + write_lock_irqsave(&tm->mgr->lock, flags); + list_del(&l2->list); + write_unlock_irqrestore(&tm->mgr->lock, flags); + l2->tm = NULL; + kfree(tm); +} + +static int +create_teimgr(struct manager *mgr, struct channel_req *crq) +{ + struct layer2 *l2; + u_long opt = 0; + u_long flags; + int id; + + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, mgr->ch.st->dev->name, crq->protocol, + crq->adr.dev, crq->adr.channel, crq->adr.sapi, + crq->adr.tei); + if (crq->adr.sapi != 0) /* not supported yet */ + return -EINVAL; + if (crq->adr.tei > GROUP_TEI) + return -EINVAL; + if (crq->adr.tei < 64) + test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); + if (crq->adr.tei == 0) + test_and_set_bit(OPTION_L2_PTP, &opt); + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { + if (crq->protocol == ISDN_P_LAPD_TE) + return -EPROTONOSUPPORT; + if ((crq->adr.tei != 0) && (crq->adr.tei != 127)) + return -EINVAL; + if (mgr->up) { + printk(KERN_WARNING + "%s: only one network manager is allowed\n", + __func__); + return -EBUSY; + } + } else if (test_bit(MGR_OPT_USER, &mgr->options)) { + if (crq->protocol == ISDN_P_LAPD_NT) + return -EPROTONOSUPPORT; + if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI)) + return -EINVAL; /* dyn tei */ + } else { + if (crq->protocol == ISDN_P_LAPD_NT) + test_and_set_bit(MGR_OPT_NETWORK, &mgr->options); + if (crq->protocol == ISDN_P_LAPD_TE) + test_and_set_bit(MGR_OPT_USER, &mgr->options); + } + if (mgr->ch.st->dev->Dprotocols + & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) + test_and_set_bit(OPTION_L2_PMX, &opt); + if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) { + mgr->up = crq->ch; + id = DL_INFO_L2_CONNECT; + teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id); + crq->ch = NULL; + if (!list_empty(&mgr->layer2)) { + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + l2->up = mgr->up; + l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); + } + read_unlock_irqrestore(&mgr->lock, flags); + } + return 0; + } + l2 = create_l2(crq->ch, crq->protocol, (u_int)opt, + (u_long)crq->adr.tei); + if (!l2) + return -ENOMEM; + l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL); + if (!l2->tm) { + kfree(l2); + printk(KERN_ERR "kmalloc teimgr failed\n"); + return -ENOMEM; + } + l2->tm->mgr = mgr; + l2->tm->l2 = l2; + l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; + l2->tm->tei_m.userdata = l2->tm; + l2->tm->tei_m.printdebug = tei_debug; + if (crq->protocol == ISDN_P_LAPD_TE) { + l2->tm->tei_m.fsm = &teifsmu; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 1000; /* T201 1 sec */ + } else { + l2->tm->tei_m.fsm = &teifsmn; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 2000; /* T202 2 sec */ + } + mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); + write_lock_irqsave(&mgr->lock, flags); + id = get_free_id(mgr); + list_add_tail(&l2->list, &mgr->layer2); + write_unlock_irqrestore(&mgr->lock, flags); + if (id < 0) { + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } else { + l2->ch.nr = id; + l2->up->nr = id; + crq->ch = &l2->ch; + id = 0; + } + return id; +} + +static int +mgr_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct manager *mgr; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + + mgr = container_of(ch, struct manager, ch); + if (*debug & DEBUG_L2_RECV) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", + __func__, hh->prim, hh->id); + switch (hh->prim) { + case PH_DATA_IND: + mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); + ret = ph_data_ind(mgr, skb); + break; + case PH_DATA_CNF: + do_ack(mgr, hh->id); + ret = 0; + break; + case PH_ACTIVATE_IND: + test_and_set_bit(MGR_PH_ACTIVE, &mgr->options); + mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL); + do_send(mgr); + ret = 0; + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options); + mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL); + ret = 0; + break; + case DL_UNITDATA_REQ: + return dl_unit_data(mgr, skb); + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +free_teimanager(struct manager *mgr) +{ + struct layer2 *l2, *nl2; + + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { + /* not locked lock is taken in release tei */ + mgr->up = NULL; + if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) { + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + put_tei_msg(mgr, ID_REMOVE, 0, l2->tei); + mutex_lock(&mgr->ch.st->lmutex); + list_del(&l2->ch.list); + mutex_unlock(&mgr->ch.st->lmutex); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } + test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options); + } else { + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + l2->up = NULL; + } + } + } + if (test_bit(MGR_OPT_USER, &mgr->options)) { + if (list_empty(&mgr->layer2)) + test_and_clear_bit(MGR_OPT_USER, &mgr->options); + } + mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL); + return 0; +} + +static int +ctrl_teimanager(struct manager *mgr, void *arg) +{ + /* currently we only have one option */ + int clean = *((int *)arg); + + if (clean) + test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options); + else + test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options); + return 0; +} + +/* This function does create a L2 for fixed TEI in NT Mode */ +static int +check_data(struct manager *mgr, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret, tei; + struct layer2 *l2; + + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", + __func__, hh->prim, hh->id); + if (test_bit(MGR_OPT_USER, &mgr->options)) + return -ENOTCONN; + if (hh->prim != PH_DATA_IND) + return -ENOTCONN; + if (skb->len != 3) + return -ENOTCONN; + if (skb->data[0] != 0) + /* only SAPI 0 command */ + return -ENOTCONN; + if (!(skb->data[1] & 1)) /* invalid EA1 */ + return -EINVAL; + tei = skb->data[1] >> 0; + if (tei > 63) /* not a fixed tei */ + return -ENOTCONN; + if ((skb->data[2] & ~0x10) != SABME) + return -ENOTCONN; + /* We got a SABME for a fixed TEI */ + l2 = create_new_tei(mgr, tei); + if (!l2) + return -ENOMEM; + ret = l2->ch.send(&l2->ch, skb); + return ret; +} + +void +delete_teimanager(struct mISDNchannel *ch) +{ + struct manager *mgr; + struct layer2 *l2, *nl2; + + mgr = container_of(ch, struct manager, ch); + /* not locked lock is taken in release tei */ + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + mutex_lock(&mgr->ch.st->lmutex); + list_del(&l2->ch.list); + mutex_unlock(&mgr->ch.st->lmutex); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } + list_del(&mgr->ch.list); + list_del(&mgr->bcast.list); + skb_queue_purge(&mgr->sendq); + kfree(mgr); +} + +static int +mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct manager *mgr; + int ret = -EINVAL; + + mgr = container_of(ch, struct manager, ch); + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + ret = create_teimgr(mgr, arg); + break; + case CLOSE_CHANNEL: + ret = free_teimanager(mgr); + break; + case CONTROL_CHANNEL: + ret = ctrl_teimanager(mgr, arg); + break; + case CHECK_DATA: + ret = check_data(mgr, arg); + break; + } + return ret; +} + +static int +mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct manager *mgr = container_of(ch, struct manager, bcast); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct sk_buff *cskb = NULL; + struct layer2 *l2; + u_long flags; + int ret; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if ((hh->id & MISDN_ID_SAPI_MASK) == + (l2->ch.addr & MISDN_ID_SAPI_MASK)) { + if (list_is_last(&l2->list, &mgr->layer2)) { + cskb = skb; + skb = NULL; + } else { + if (!cskb) + cskb = skb_copy(skb, GFP_KERNEL); + } + if (cskb) { + ret = l2->ch.send(&l2->ch, cskb); + if (ret) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d prim(%x) addr(%x)" + " err %d\n", + __func__, l2->ch.nr, + hh->prim, l2->ch.addr, ret); + } else + cskb = NULL; + } else { + printk(KERN_WARNING "%s ch%d addr %x no mem\n", + __func__, ch->nr, ch->addr); + goto out; + } + } + } +out: + read_unlock_irqrestore(&mgr->lock, flags); + if (cskb) + dev_kfree_skb(cskb); + if (skb) + dev_kfree_skb(skb); + return 0; +} + +static int +mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + + return -EINVAL; +} + +int +create_teimanager(struct mISDNdevice *dev) +{ + struct manager *mgr; + + mgr = kzalloc(sizeof(struct manager), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + INIT_LIST_HEAD(&mgr->layer2); + mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock); + skb_queue_head_init(&mgr->sendq); + mgr->nextid = 1; + mgr->lastid = MISDN_ID_NONE; + mgr->ch.send = mgr_send; + mgr->ch.ctrl = mgr_ctrl; + mgr->ch.st = dev->D.st; + set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI); + add_layer2(&mgr->ch, dev->D.st); + mgr->bcast.send = mgr_bcast; + mgr->bcast.ctrl = mgr_bcast_ctrl; + mgr->bcast.st = dev->D.st; + set_channel_address(&mgr->bcast, 0, GROUP_TEI); + add_layer2(&mgr->bcast, dev->D.st); + mgr->deact.debug = *debug & DEBUG_MANAGER; + mgr->deact.userdata = mgr; + mgr->deact.printdebug = da_debug; + mgr->deact.fsm = &deactfsm; + mgr->deact.state = ST_L1_DEACT; + mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer); + dev->teimgr = &mgr->ch; + return 0; +} + +int TEIInit(u_int *deb) +{ + debug = deb; + teifsmu.state_count = TEI_STATE_COUNT; + teifsmu.event_count = TEI_EVENT_COUNT; + teifsmu.strEvent = strTeiEvent; + teifsmu.strState = strTeiState; + mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); + teifsmn.state_count = TEI_STATE_COUNT; + teifsmn.event_count = TEI_EVENT_COUNT; + teifsmn.strEvent = strTeiEvent; + teifsmn.strState = strTeiState; + mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); + deactfsm.state_count = DEACT_STATE_COUNT; + deactfsm.event_count = DEACT_EVENT_COUNT; + deactfsm.strEvent = strDeactEvent; + deactfsm.strState = strDeactState; + mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); + return 0; +} + +void TEIFree(void) +{ + mISDN_FsmFree(&teifsmu); + mISDN_FsmFree(&teifsmn); + mISDN_FsmFree(&deactfsm); +} diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c new file mode 100644 index 000000000000..b5fabc7019d8 --- /dev/null +++ b/drivers/isdn/mISDN/timerdev.c @@ -0,0 +1,301 @@ +/* + * + * general timer device for using in ISDN stacks + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +static int *debug; + + +struct mISDNtimerdev { + int next_id; + struct list_head pending; + struct list_head expired; + wait_queue_head_t wait; + u_int work; + spinlock_t lock; /* protect lists */ +}; + +struct mISDNtimer { + struct list_head list; + struct mISDNtimerdev *dev; + struct timer_list tl; + int id; +}; + +static int +mISDN_open(struct inode *ino, struct file *filep) +{ + struct mISDNtimerdev *dev; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); + dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->next_id = 1; + INIT_LIST_HEAD(&dev->pending); + INIT_LIST_HEAD(&dev->expired); + spin_lock_init(&dev->lock); + dev->work = 0; + init_waitqueue_head(&dev->wait); + filep->private_data = dev; + __module_get(THIS_MODULE); + return 0; +} + +static int +mISDN_close(struct inode *ino, struct file *filep) +{ + struct mISDNtimerdev *dev = filep->private_data; + struct mISDNtimer *timer, *next; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); + list_for_each_entry_safe(timer, next, &dev->pending, list) { + del_timer(&timer->tl); + kfree(timer); + } + list_for_each_entry_safe(timer, next, &dev->expired, list) { + kfree(timer); + } + kfree(dev); + module_put(THIS_MODULE); + return 0; +} + +static ssize_t +mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off) +{ + struct mISDNtimerdev *dev = filep->private_data; + struct mISDNtimer *timer; + u_long flags; + int ret = 0; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__, + filep, buf, (int)count, off); + if (*off != filep->f_pos) + return -ESPIPE; + + if (list_empty(&dev->expired) && (dev->work == 0)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + wait_event_interruptible(dev->wait, (dev->work || + !list_empty(&dev->expired))); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (count < sizeof(int)) + return -ENOSPC; + if (dev->work) + dev->work = 0; + if (!list_empty(&dev->expired)) { + spin_lock_irqsave(&dev->lock, flags); + timer = (struct mISDNtimer *)dev->expired.next; + list_del(&timer->list); + spin_unlock_irqrestore(&dev->lock, flags); + if (put_user(timer->id, (int *)buf)) + ret = -EFAULT; + else + ret = sizeof(int); + kfree(timer); + } + return ret; +} + +static loff_t +mISDN_llseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +static ssize_t +mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off) +{ + return -EOPNOTSUPP; +} + +static unsigned int +mISDN_poll(struct file *filep, poll_table *wait) +{ + struct mISDNtimerdev *dev = filep->private_data; + unsigned int mask = POLLERR; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait); + if (dev) { + poll_wait(filep, &dev->wait, wait); + mask = 0; + if (dev->work || !list_empty(&dev->expired)) + mask |= (POLLIN | POLLRDNORM); + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__, + dev->work, list_empty(&dev->expired)); + } + return mask; +} + +static void +dev_expire_timer(struct mISDNtimer *timer) +{ + u_long flags; + + spin_lock_irqsave(&timer->dev->lock, flags); + list_del(&timer->list); + list_add_tail(&timer->list, &timer->dev->expired); + spin_unlock_irqrestore(&timer->dev->lock, flags); + wake_up_interruptible(&timer->dev->wait); +} + +static int +misdn_add_timer(struct mISDNtimerdev *dev, int timeout) +{ + int id; + u_long flags; + struct mISDNtimer *timer; + + if (!timeout) { + dev->work = 1; + wake_up_interruptible(&dev->wait); + id = 0; + } else { + timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + spin_lock_irqsave(&dev->lock, flags); + timer->id = dev->next_id++; + if (dev->next_id < 0) + dev->next_id = 1; + list_add_tail(&timer->list, &dev->pending); + spin_unlock_irqrestore(&dev->lock, flags); + timer->dev = dev; + timer->tl.data = (long)timer; + timer->tl.function = (void *) dev_expire_timer; + init_timer(&timer->tl); + timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000); + add_timer(&timer->tl); + id = timer->id; + } + return id; +} + +static int +misdn_del_timer(struct mISDNtimerdev *dev, int id) +{ + u_long flags; + struct mISDNtimer *timer; + int ret = 0; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_entry(timer, &dev->pending, list) { + if (timer->id == id) { + list_del_init(&timer->list); + del_timer(&timer->tl); + ret = timer->id; + kfree(timer); + goto unlock; + } + } +unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int +mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct mISDNtimerdev *dev = filep->private_data; + int id, tout, ret = 0; + + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__, + filep, cmd, arg); + switch (cmd) { + case IMADDTIMER: + if (get_user(tout, (int __user *)arg)) { + ret = -EFAULT; + break; + } + id = misdn_add_timer(dev, tout); + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s add %d id %d\n", __func__, + tout, id); + if (id < 0) { + ret = id; + break; + } + if (put_user(id, (int __user *)arg)) + ret = -EFAULT; + break; + case IMDELTIMER: + if (get_user(id, (int __user *)arg)) { + ret = -EFAULT; + break; + } + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s del id %d\n", __func__, id); + id = misdn_del_timer(dev, id); + if (put_user(id, (int __user *)arg)) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static struct file_operations mISDN_fops = { + .llseek = mISDN_llseek, + .read = mISDN_read, + .write = mISDN_write, + .poll = mISDN_poll, + .ioctl = mISDN_ioctl, + .open = mISDN_open, + .release = mISDN_close, +}; + +static struct miscdevice mISDNtimer = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mISDNtimer", + .fops = &mISDN_fops, +}; + +int +mISDN_inittimer(int *deb) +{ + int err; + + debug = deb; + err = misc_register(&mISDNtimer); + if (err) + printk(KERN_WARNING "mISDN: Could not register timer device\n"); + return err; +} + +void mISDN_timer_cleanup(void) +{ + misc_deregister(&mISDNtimer); +} diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h new file mode 100644 index 000000000000..e794dfb87504 --- /dev/null +++ b/include/linux/mISDNhw.h @@ -0,0 +1,193 @@ +/* + * + * Author Karsten Keil + * + * Basic declarations for the mISDN HW channels + * + * Copyright 2008 by Karsten Keil + * + * 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. + * + * 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. + * + */ + +#ifndef MISDNHW_H +#define MISDNHW_H +#include +#include + +/* + * HW DEBUG 0xHHHHGGGG + * H - hardware driver specific bits + * G - for all drivers + */ + +#define DEBUG_HW 0x00000001 +#define DEBUG_HW_OPEN 0x00000002 +#define DEBUG_HW_DCHANNEL 0x00000100 +#define DEBUG_HW_DFIFO 0x00000200 +#define DEBUG_HW_BCHANNEL 0x00001000 +#define DEBUG_HW_BFIFO 0x00002000 + +#define MAX_DFRAME_LEN_L1 300 +#define MAX_MON_FRAME 32 +#define MAX_LOG_SPACE 2048 +#define MISDN_COPY_SIZE 32 + +/* channel->Flags bit field */ +#define FLG_TX_BUSY 0 /* tx_buf in use */ +#define FLG_TX_NEXT 1 /* next_skb in use */ +#define FLG_L1_BUSY 2 /* L1 is permanent busy */ +#define FLG_L2_ACTIVATED 3 /* activated from L2 */ +#define FLG_OPEN 5 /* channel is in use */ +#define FLG_ACTIVE 6 /* channel is activated */ +#define FLG_BUSY_TIMER 7 +/* channel type */ +#define FLG_DCHANNEL 8 /* channel is D-channel */ +#define FLG_BCHANNEL 9 /* channel is B-channel */ +#define FLG_ECHANNEL 10 /* channel is E-channel */ +#define FLG_TRANSPARENT 12 /* channel use transparent data */ +#define FLG_HDLC 13 /* channel use hdlc data */ +#define FLG_L2DATA 14 /* channel use L2 DATA primitivs */ +#define FLG_ORIGIN 15 /* channel is on origin site */ +/* channel specific stuff */ +/* arcofi specific */ +#define FLG_ARCOFI_TIMER 16 +#define FLG_ARCOFI_ERROR 17 +/* isar specific */ +#define FLG_INITIALIZED 16 +#define FLG_DLEETX 17 +#define FLG_LASTDLE 18 +#define FLG_FIRST 19 +#define FLG_LASTDATA 20 +#define FLG_NMD_DATA 21 +#define FLG_FTI_RUN 22 +#define FLG_LL_OK 23 +#define FLG_LL_CONN 24 +#define FLG_DTMFSEND 25 + +/* workq events */ +#define FLG_RECVQUEUE 30 +#define FLG_PHCHANGE 31 + +#define schedule_event(s, ev) do { \ + test_and_set_bit(ev, &((s)->Flags)); \ + schedule_work(&((s)->workq)); \ + } while (0) + +struct dchannel { + struct mISDNdevice dev; + u_long Flags; + struct work_struct workq; + void (*phfunc) (struct dchannel *); + u_int state; + void *l1; + /* HW access */ + u_char (*read_reg) (void *, u_char); + void (*write_reg) (void *, u_char, u_char); + void (*read_fifo) (void *, u_char *, int); + void (*write_fifo) (void *, u_char *, int); + void *hw; + int slot; /* multiport card channel slot */ + struct timer_list timer; + /* receive data */ + struct sk_buff *rx_skb; + int maxlen; + /* send data */ + struct sk_buff_head squeue; + struct sk_buff_head rqueue; + struct sk_buff *tx_skb; + int tx_idx; + int debug; + /* statistics */ + int err_crc; + int err_tx; + int err_rx; +}; + +typedef int (dchannel_l1callback)(struct dchannel *, u_int); +extern int create_l1(struct dchannel *, dchannel_l1callback *); + +/* private L1 commands */ +#define INFO0 0x8002 +#define INFO1 0x8102 +#define INFO2 0x8202 +#define INFO3_P8 0x8302 +#define INFO3_P10 0x8402 +#define INFO4_P8 0x8502 +#define INFO4_P10 0x8602 +#define LOSTFRAMING 0x8702 +#define ANYSIGNAL 0x8802 +#define HW_POWERDOWN 0x8902 +#define HW_RESET_REQ 0x8a02 +#define HW_POWERUP_REQ 0x8b02 +#define HW_DEACT_REQ 0x8c02 +#define HW_ACTIVATE_REQ 0x8e02 +#define HW_D_NOBLOCKED 0x8f02 +#define HW_RESET_IND 0x9002 +#define HW_POWERUP_IND 0x9102 +#define HW_DEACT_IND 0x9202 +#define HW_ACTIVATE_IND 0x9302 +#define HW_DEACT_CNF 0x9402 +#define HW_TESTLOOP 0x9502 +#define HW_TESTRX_RAW 0x9602 +#define HW_TESTRX_HDLC 0x9702 +#define HW_TESTRX_OFF 0x9802 + +struct layer1; +extern int l1_event(struct layer1 *, u_int); + + +struct bchannel { + struct mISDNchannel ch; + int nr; + u_long Flags; + struct work_struct workq; + u_int state; + /* HW access */ + u_char (*read_reg) (void *, u_char); + void (*write_reg) (void *, u_char, u_char); + void (*read_fifo) (void *, u_char *, int); + void (*write_fifo) (void *, u_char *, int); + void *hw; + int slot; /* multiport card channel slot */ + struct timer_list timer; + /* receive data */ + struct sk_buff *rx_skb; + int maxlen; + /* send data */ + struct sk_buff *next_skb; + struct sk_buff *tx_skb; + struct sk_buff_head rqueue; + int rcount; + int tx_idx; + int debug; + /* statistics */ + int err_crc; + int err_tx; + int err_rx; +}; + +extern int mISDN_initdchannel(struct dchannel *, int, void *); +extern int mISDN_initbchannel(struct bchannel *, int); +extern int mISDN_freedchannel(struct dchannel *); +extern int mISDN_freebchannel(struct bchannel *); +extern void queue_ch_frame(struct mISDNchannel *, u_int, + int, struct sk_buff *); +extern int dchannel_senddata(struct dchannel *, struct sk_buff *); +extern int bchannel_senddata(struct bchannel *, struct sk_buff *); +extern void recv_Dchannel(struct dchannel *); +extern void recv_Bchannel(struct bchannel *); +extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *); +extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *); +extern void confirm_Bsend(struct bchannel *bch); +extern int get_next_bframe(struct bchannel *); +extern int get_next_dframe(struct dchannel *); + +#endif diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h new file mode 100644 index 000000000000..5c948f337817 --- /dev/null +++ b/include/linux/mISDNif.h @@ -0,0 +1,487 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE + * version 2.1 as published by the Free Software Foundation. + * + * This code 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 LESSER GENERAL PUBLIC LICENSE for more details. + * + */ + +#ifndef mISDNIF_H +#define mISDNIF_H + +#include +#include +#include +#include + +/* + * ABI Version 32 bit + * + * <8 bit> Major version + * - changed if any interface become backwards incompatible + * + * <8 bit> Minor version + * - changed if any interface is extended but backwards compatible + * + * <16 bit> Release number + * - should be incremented on every checkin + */ +#define MISDN_MAJOR_VERSION 1 +#define MISDN_MINOR_VERSION 0 +#define MISDN_RELEASE 18 + +/* primitives for information exchange + * generell format + * <16 bit 0 > + * <8 bit command> + * BIT 8 = 1 LAYER private + * BIT 7 = 1 answer + * BIT 6 = 1 DATA + * <8 bit target layer mask> + * + * Layer = 00 is reserved for general commands + Layer = 01 L2 -> HW + Layer = 02 HW -> L2 + Layer = 04 L3 -> L2 + Layer = 08 L2 -> L3 + * Layer = FF is reserved for broadcast commands + */ + +#define MISDN_CMDMASK 0xff00 +#define MISDN_LAYERMASK 0x00ff + +/* generell commands */ +#define OPEN_CHANNEL 0x0100 +#define CLOSE_CHANNEL 0x0200 +#define CONTROL_CHANNEL 0x0300 +#define CHECK_DATA 0x0400 + +/* layer 2 -> layer 1 */ +#define PH_ACTIVATE_REQ 0x0101 +#define PH_DEACTIVATE_REQ 0x0201 +#define PH_DATA_REQ 0x2001 +#define MPH_ACTIVATE_REQ 0x0501 +#define MPH_DEACTIVATE_REQ 0x0601 +#define MPH_INFORMATION_REQ 0x0701 +#define PH_CONTROL_REQ 0x0801 + +/* layer 1 -> layer 2 */ +#define PH_ACTIVATE_IND 0x0102 +#define PH_ACTIVATE_CNF 0x4102 +#define PH_DEACTIVATE_IND 0x0202 +#define PH_DEACTIVATE_CNF 0x4202 +#define PH_DATA_IND 0x2002 +#define MPH_ACTIVATE_IND 0x0502 +#define MPH_DEACTIVATE_IND 0x0602 +#define MPH_INFORMATION_IND 0x0702 +#define PH_DATA_CNF 0x6002 +#define PH_CONTROL_IND 0x0802 +#define PH_CONTROL_CNF 0x4802 + +/* layer 3 -> layer 2 */ +#define DL_ESTABLISH_REQ 0x1004 +#define DL_RELEASE_REQ 0x1104 +#define DL_DATA_REQ 0x3004 +#define DL_UNITDATA_REQ 0x3104 +#define DL_INFORMATION_REQ 0x0004 + +/* layer 2 -> layer 3 */ +#define DL_ESTABLISH_IND 0x1008 +#define DL_ESTABLISH_CNF 0x5008 +#define DL_RELEASE_IND 0x1108 +#define DL_RELEASE_CNF 0x5108 +#define DL_DATA_IND 0x3008 +#define DL_UNITDATA_IND 0x3108 +#define DL_INFORMATION_IND 0x0008 + +/* intern layer 2 managment */ +#define MDL_ASSIGN_REQ 0x1804 +#define MDL_ASSIGN_IND 0x1904 +#define MDL_REMOVE_REQ 0x1A04 +#define MDL_REMOVE_IND 0x1B04 +#define MDL_STATUS_UP_IND 0x1C04 +#define MDL_STATUS_DOWN_IND 0x1D04 +#define MDL_STATUS_UI_IND 0x1E04 +#define MDL_ERROR_IND 0x1F04 +#define MDL_ERROR_RSP 0x5F04 + +/* DL_INFORMATION_IND types */ +#define DL_INFO_L2_CONNECT 0x0001 +#define DL_INFO_L2_REMOVED 0x0002 + +/* PH_CONTROL types */ +/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ +#define DTMF_TONE_VAL 0x2000 +#define DTMF_TONE_MASK 0x007F +#define DTMF_TONE_START 0x2100 +#define DTMF_TONE_STOP 0x2200 +#define DTMF_HFC_COEF 0x4000 +#define DSP_CONF_JOIN 0x2403 +#define DSP_CONF_SPLIT 0x2404 +#define DSP_RECEIVE_OFF 0x2405 +#define DSP_RECEIVE_ON 0x2406 +#define DSP_ECHO_ON 0x2407 +#define DSP_ECHO_OFF 0x2408 +#define DSP_MIX_ON 0x2409 +#define DSP_MIX_OFF 0x240a +#define DSP_DELAY 0x240b +#define DSP_JITTER 0x240c +#define DSP_TXDATA_ON 0x240d +#define DSP_TXDATA_OFF 0x240e +#define DSP_TX_DEJITTER 0x240f +#define DSP_TX_DEJ_OFF 0x2410 +#define DSP_TONE_PATT_ON 0x2411 +#define DSP_TONE_PATT_OFF 0x2412 +#define DSP_VOL_CHANGE_TX 0x2413 +#define DSP_VOL_CHANGE_RX 0x2414 +#define DSP_BF_ENABLE_KEY 0x2415 +#define DSP_BF_DISABLE 0x2416 +#define DSP_BF_ACCEPT 0x2416 +#define DSP_BF_REJECT 0x2417 +#define DSP_PIPELINE_CFG 0x2418 +#define HFC_VOL_CHANGE_TX 0x2601 +#define HFC_VOL_CHANGE_RX 0x2602 +#define HFC_SPL_LOOP_ON 0x2603 +#define HFC_SPL_LOOP_OFF 0x2604 + +/* DSP_TONE_PATT_ON parameter */ +#define TONE_OFF 0x0000 +#define TONE_GERMAN_DIALTONE 0x0001 +#define TONE_GERMAN_OLDDIALTONE 0x0002 +#define TONE_AMERICAN_DIALTONE 0x0003 +#define TONE_GERMAN_DIALPBX 0x0004 +#define TONE_GERMAN_OLDDIALPBX 0x0005 +#define TONE_AMERICAN_DIALPBX 0x0006 +#define TONE_GERMAN_RINGING 0x0007 +#define TONE_GERMAN_OLDRINGING 0x0008 +#define TONE_AMERICAN_RINGPBX 0x000b +#define TONE_GERMAN_RINGPBX 0x000c +#define TONE_GERMAN_OLDRINGPBX 0x000d +#define TONE_AMERICAN_RINGING 0x000e +#define TONE_GERMAN_BUSY 0x000f +#define TONE_GERMAN_OLDBUSY 0x0010 +#define TONE_AMERICAN_BUSY 0x0011 +#define TONE_GERMAN_HANGUP 0x0012 +#define TONE_GERMAN_OLDHANGUP 0x0013 +#define TONE_AMERICAN_HANGUP 0x0014 +#define TONE_SPECIAL_INFO 0x0015 +#define TONE_GERMAN_GASSENBESETZT 0x0016 +#define TONE_GERMAN_AUFSCHALTTON 0x0016 + +/* MPH_INFORMATION_IND */ +#define L1_SIGNAL_LOS_OFF 0x0010 +#define L1_SIGNAL_LOS_ON 0x0011 +#define L1_SIGNAL_AIS_OFF 0x0012 +#define L1_SIGNAL_AIS_ON 0x0013 +#define L1_SIGNAL_RDI_OFF 0x0014 +#define L1_SIGNAL_RDI_ON 0x0015 +#define L1_SIGNAL_SLIP_RX 0x0020 +#define L1_SIGNAL_SLIP_TX 0x0021 + +/* + * protocol ids + * D channel 1-31 + * B channel 33 - 63 + */ + +#define ISDN_P_NONE 0 +#define ISDN_P_BASE 0 +#define ISDN_P_TE_S0 0x01 +#define ISDN_P_NT_S0 0x02 +#define ISDN_P_TE_E1 0x03 +#define ISDN_P_NT_E1 0x04 +#define ISDN_P_LAPD_TE 0x10 +#define ISDN_P_LAPD_NT 0x11 + +#define ISDN_P_B_MASK 0x1f +#define ISDN_P_B_START 0x20 + +#define ISDN_P_B_RAW 0x21 +#define ISDN_P_B_HDLC 0x22 +#define ISDN_P_B_X75SLP 0x23 +#define ISDN_P_B_L2DTMF 0x24 +#define ISDN_P_B_L2DSP 0x25 +#define ISDN_P_B_L2DSPHDLC 0x26 + +#define OPTION_L2_PMX 1 +#define OPTION_L2_PTP 2 +#define OPTION_L2_FIXEDTEI 3 +#define OPTION_L2_CLEANUP 4 + +/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ +#define MISDN_MAX_IDLEN 20 + +struct mISDNhead { + unsigned int prim; + unsigned int id; +} __attribute__((packed)); + +#define MISDN_HEADER_LEN sizeof(struct mISDNhead) +#define MAX_DATA_SIZE 2048 +#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) +#define MAX_DFRAME_LEN 260 + +#define MISDN_ID_ADDR_MASK 0xFFFF +#define MISDN_ID_TEI_MASK 0xFF00 +#define MISDN_ID_SAPI_MASK 0x00FF +#define MISDN_ID_TEI_ANY 0x7F00 + +#define MISDN_ID_ANY 0xFFFF +#define MISDN_ID_NONE 0xFFFE + +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 + +#define MISDN_CHMAP_SIZE 4 + +#define SOL_MISDN 0 + +struct sockaddr_mISDN { + sa_family_t family; + unsigned char dev; + unsigned char channel; + unsigned char sapi; + unsigned char tei; +}; + +/* timer device ioctl */ +#define IMADDTIMER _IOR('I', 64, int) +#define IMDELTIMER _IOR('I', 65, int) +/* socket ioctls */ +#define IMGETVERSION _IOR('I', 66, int) +#define IMGETCOUNT _IOR('I', 67, int) +#define IMGETDEVINFO _IOR('I', 68, int) +#define IMCTRLREQ _IOR('I', 69, int) +#define IMCLEAR_L2 _IOR('I', 70, int) + +struct mISDNversion { + unsigned char major; + unsigned char minor; + unsigned short release; +}; + +struct mISDN_devinfo { + u_int id; + u_int Dprotocols; + u_int Bprotocols; + u_int protocol; + u_long channelmap[MISDN_CHMAP_SIZE]; + u_int nrbchan; + char name[MISDN_MAX_IDLEN]; +}; + +/* CONTROL_CHANNEL parameters */ +#define MISDN_CTRL_GETOP 0x0000 +#define MISDN_CTRL_LOOP 0x0001 +#define MISDN_CTRL_CONNECT 0x0002 +#define MISDN_CTRL_DISCONNECT 0x0004 +#define MISDN_CTRL_PCMCONNECT 0x0010 +#define MISDN_CTRL_PCMDISCONNECT 0x0020 +#define MISDN_CTRL_SETPEER 0x0040 +#define MISDN_CTRL_UNSETPEER 0x0080 +#define MISDN_CTRL_RX_OFF 0x0100 +#define MISDN_CTRL_HW_FEATURES_OP 0x2000 +#define MISDN_CTRL_HW_FEATURES 0x2001 +#define MISDN_CTRL_HFC_OP 0x4000 +#define MISDN_CTRL_HFC_PCM_CONN 0x4001 +#define MISDN_CTRL_HFC_PCM_DISC 0x4002 +#define MISDN_CTRL_HFC_CONF_JOIN 0x4003 +#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 +#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 +#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 +#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 +#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 + + +/* socket options */ +#define MISDN_TIME_STAMP 0x0001 + +struct mISDN_ctrl_req { + int op; + int channel; + int p1; + int p2; +}; + +/* muxer options */ +#define MISDN_OPT_ALL 1 +#define MISDN_OPT_TEIMGR 2 + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include + +#define DEBUG_CORE 0x000000ff +#define DEBUG_CORE_FUNC 0x00000002 +#define DEBUG_SOCKET 0x00000004 +#define DEBUG_MANAGER 0x00000008 +#define DEBUG_SEND_ERR 0x00000010 +#define DEBUG_MSG_THREAD 0x00000020 +#define DEBUG_QUEUE_FUNC 0x00000040 +#define DEBUG_L1 0x0000ff00 +#define DEBUG_L1_FSM 0x00000200 +#define DEBUG_L2 0x00ff0000 +#define DEBUG_L2_FSM 0x00020000 +#define DEBUG_L2_CTRL 0x00040000 +#define DEBUG_L2_RECV 0x00080000 +#define DEBUG_L2_TEI 0x00100000 +#define DEBUG_L2_TEIFSM 0x00200000 +#define DEBUG_TIMER 0x01000000 + +#define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0]) +#define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim) +#define mISDN_HEAD_ID(s) (((struct mISDNhead *)&s->cb[0])->id) + +/* socket states */ +#define MISDN_OPEN 1 +#define MISDN_BOUND 2 +#define MISDN_CLOSED 3 + +struct mISDNchannel; +struct mISDNdevice; +struct mISDNstack; + +struct channel_req { + u_int protocol; + struct sockaddr_mISDN adr; + struct mISDNchannel *ch; +}; + +typedef int (ctrl_func_t)(struct mISDNchannel *, u_int, void *); +typedef int (send_func_t)(struct mISDNchannel *, struct sk_buff *); +typedef int (create_func_t)(struct channel_req *); + +struct Bprotocol { + struct list_head list; + char *name; + u_int Bprotocols; + create_func_t *create; +}; + +struct mISDNchannel { + struct list_head list; + u_int protocol; + u_int nr; + u_long opt; + u_int addr; + struct mISDNstack *st; + struct mISDNchannel *peer; + send_func_t *send; + send_func_t *recv; + ctrl_func_t *ctrl; +}; + +struct mISDN_sock_list { + struct hlist_head head; + rwlock_t lock; +}; + +struct mISDN_sock { + struct sock sk; + struct mISDNchannel ch; + u_int cmask; + struct mISDNdevice *dev; +}; + + + +struct mISDNdevice { + struct mISDNchannel D; + u_int id; + char name[MISDN_MAX_IDLEN]; + u_int Dprotocols; + u_int Bprotocols; + u_int nrbchan; + u_long channelmap[MISDN_CHMAP_SIZE]; + struct list_head bchannels; + struct mISDNchannel *teimgr; + struct device dev; +}; + +struct mISDNstack { + u_long status; + struct mISDNdevice *dev; + struct task_struct *thread; + struct completion *notify; + wait_queue_head_t workq; + struct sk_buff_head msgq; + struct list_head layer2; + struct mISDNchannel *layer1; + struct mISDNchannel own; + struct mutex lmutex; /* protect lists */ + struct mISDN_sock_list l1sock; +#ifdef MISDN_MSG_STATS + u_int msg_cnt; + u_int sleep_cnt; + u_int stopped_cnt; +#endif +}; + +/* global alloc/queue dunctions */ + +static inline struct sk_buff * +mI_alloc_skb(unsigned int len, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask); + if (likely(skb)) + skb_reserve(skb, MISDN_HEADER_LEN); + return skb; +} + +static inline struct sk_buff * +_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask) +{ + struct sk_buff *skb = mI_alloc_skb(len, gfp_mask); + struct mISDNhead *hh; + + if (!skb) + return NULL; + if (len) + memcpy(skb_put(skb, len), dp, len); + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = id; + return skb; +} + +static inline void +_queue_data(struct mISDNchannel *ch, u_int prim, + u_int id, u_int len, void *dp, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + if (!ch->peer) + return; + skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask); + if (!skb) + return; + if (ch->recv(ch->peer, skb)) + dev_kfree_skb(skb); +} + +/* global register/unregister functions */ + +extern int mISDN_register_device(struct mISDNdevice *, char *name); +extern void mISDN_unregister_device(struct mISDNdevice *); +extern int mISDN_register_Bprotocol(struct Bprotocol *); +extern void mISDN_unregister_Bprotocol(struct Bprotocol *); + +extern void set_channel_address(struct mISDNchannel *, u_int, u_int); + +#endif /* __KERNEL__ */ +#endif /* mISDNIF_H */ -- cgit v1.2.3 From 960366cf8dbb3359afaca30cf7fdbf69a6d6dda7 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sun, 27 Jul 2008 01:56:38 +0200 Subject: Add mISDN DSP Enable support for digital audio processing capability. This module may be used for special applications that require cross connecting of bchannels, conferencing, dtmf decoding echo cancelation, tone generation, and Blowfish encryption and decryption. It may use hardware features if available. Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/Kconfig | 18 + drivers/isdn/mISDN/Makefile | 2 + drivers/isdn/mISDN/dsp.h | 263 ++++++ drivers/isdn/mISDN/dsp_audio.c | 434 +++++++++ drivers/isdn/mISDN/dsp_biquad.h | 65 ++ drivers/isdn/mISDN/dsp_blowfish.c | 672 +++++++++++++ drivers/isdn/mISDN/dsp_cmx.c | 1886 +++++++++++++++++++++++++++++++++++++ drivers/isdn/mISDN/dsp_core.c | 1191 +++++++++++++++++++++++ drivers/isdn/mISDN/dsp_dtmf.c | 303 ++++++ drivers/isdn/mISDN/dsp_ecdis.h | 110 +++ drivers/isdn/mISDN/dsp_hwec.c | 138 +++ drivers/isdn/mISDN/dsp_hwec.h | 10 + drivers/isdn/mISDN/dsp_pipeline.c | 348 +++++++ drivers/isdn/mISDN/dsp_tones.c | 551 +++++++++++ include/linux/mISDNdsp.h | 37 + 15 files changed, 6028 insertions(+) create mode 100644 drivers/isdn/mISDN/dsp.h create mode 100644 drivers/isdn/mISDN/dsp_audio.c create mode 100644 drivers/isdn/mISDN/dsp_biquad.h create mode 100644 drivers/isdn/mISDN/dsp_blowfish.c create mode 100644 drivers/isdn/mISDN/dsp_cmx.c create mode 100644 drivers/isdn/mISDN/dsp_core.c create mode 100644 drivers/isdn/mISDN/dsp_dtmf.c create mode 100644 drivers/isdn/mISDN/dsp_ecdis.h create mode 100644 drivers/isdn/mISDN/dsp_hwec.c create mode 100644 drivers/isdn/mISDN/dsp_hwec.h create mode 100644 drivers/isdn/mISDN/dsp_pipeline.c create mode 100644 drivers/isdn/mISDN/dsp_tones.c create mode 100644 include/linux/mISDNdsp.h (limited to 'include/linux') diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig index 231bd0d08316..6a97e86e7f21 100644 --- a/drivers/isdn/mISDN/Kconfig +++ b/drivers/isdn/mISDN/Kconfig @@ -7,3 +7,21 @@ menuconfig MISDN help Enable support for the modular ISDN driver. +if MISDN != n + +config MISDN_DSP + tristate "Digital Audio Processing of transparent data" + depends on MISDN + help + Enable support for digital audio processing capability. + This module may be used for special applications that require + cross connecting of bchannels, conferencing, dtmf decoding + echo cancelation, tone generation, and Blowfish encryption and + decryption. + It may use hardware features if available. + E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu + and get more informations about this module and it's usage. + If unsure, say 'N'. + + source "drivers/isdn/hardware/mISDN/Kconfig" +endif #MISDN diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile index 87c563d33612..7f1a21804208 100644 --- a/drivers/isdn/mISDN/Makefile +++ b/drivers/isdn/mISDN/Makefile @@ -3,7 +3,9 @@ # obj-$(CONFIG_MISDN) += mISDN_core.o +obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o # multi objects mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o +mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h new file mode 100644 index 000000000000..6c3fed6b8d4f --- /dev/null +++ b/drivers/isdn/mISDN/dsp.h @@ -0,0 +1,263 @@ +/* + * Audio support data for ISDN4Linux. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#define DEBUG_DSP_CTRL 0x0001 +#define DEBUG_DSP_CORE 0x0002 +#define DEBUG_DSP_DTMF 0x0004 +#define DEBUG_DSP_CMX 0x0010 +#define DEBUG_DSP_TONE 0x0020 +#define DEBUG_DSP_BLOWFISH 0x0040 +#define DEBUG_DSP_DELAY 0x0100 +#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ + +/* options may be: + * + * bit 0 = use ulaw instead of alaw + * bit 1 = enable hfc hardware accelleration for all channels + * + */ +#define DSP_OPT_ULAW (1<<0) +#define DSP_OPT_NOHARDWARE (1<<1) + +#include +#include + +#include "dsp_ecdis.h" + +extern int dsp_options; +extern int dsp_debug; +extern int dsp_poll; +extern int dsp_tics; +extern spinlock_t dsp_lock; +extern struct work_struct dsp_workq; +extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */ + +/*************** + * audio stuff * + ***************/ + +extern s32 dsp_audio_alaw_to_s32[256]; +extern s32 dsp_audio_ulaw_to_s32[256]; +extern s32 *dsp_audio_law_to_s32; +extern u8 dsp_audio_s16_to_law[65536]; +extern u8 dsp_audio_alaw_to_ulaw[256]; +extern u8 dsp_audio_mix_law[65536]; +extern u8 dsp_audio_seven2law[128]; +extern u8 dsp_audio_law2seven[256]; +extern void dsp_audio_generate_law_tables(void); +extern void dsp_audio_generate_s2law_table(void); +extern void dsp_audio_generate_seven(void); +extern void dsp_audio_generate_mix_table(void); +extern void dsp_audio_generate_ulaw_samples(void); +extern void dsp_audio_generate_volume_changes(void); +extern u8 dsp_silence; + + +/************* + * cmx stuff * + *************/ + +#define MAX_POLL 256 /* maximum number of send-chunks */ + +#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ +#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ +#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ + +/* how many seconds will we check the lowest delay until the jitter buffer + is reduced by that delay */ +#define MAX_SECONDS_JITTER_CHECK 5 + +extern struct timer_list dsp_spl_tl; +extern u32 dsp_spl_jiffies; + +/* the structure of conferences: + * + * each conference has a unique number, given by user space. + * the conferences are linked in a chain. + * each conference has members linked in a chain. + * each dsplayer points to a member, each member points to a dsplayer. + */ + +/* all members within a conference (this is linked 1:1 with the dsp) */ +struct dsp; +struct dsp_conf_member { + struct list_head list; + struct dsp *dsp; +}; + +/* the list of all conferences */ +struct dsp_conf { + struct list_head list; + u32 id; + /* all cmx stacks with the same ID are + connected */ + struct list_head mlist; + int software; /* conf is processed by software */ + int hardware; /* conf is processed by hardware */ + /* note: if both unset, has only one member */ +}; + + +/************** + * DTMF stuff * + **************/ + +#define DSP_DTMF_NPOINTS 102 + +#define ECHOCAN_BUFLEN (4*128) + +struct dsp_dtmf { + int treshold; /* above this is dtmf (square of) */ + int software; /* dtmf uses software decoding */ + int hardware; /* dtmf uses hardware decoding */ + int size; /* number of bytes in buffer */ + signed short buffer[DSP_DTMF_NPOINTS]; + /* buffers one full dtmf frame */ + u8 lastwhat, lastdigit; + int count; + u8 digits[16]; /* just the dtmf result */ +}; + + +/****************** + * pipeline stuff * + ******************/ +struct dsp_pipeline { + rwlock_t lock; + struct list_head list; + int inuse; +}; + +/*************** + * tones stuff * + ***************/ + +struct dsp_tone { + int software; /* tones are generated by software */ + int hardware; /* tones are generated by hardware */ + int tone; + void *pattern; + int count; + int index; + struct timer_list tl; +}; + +/***************** + * general stuff * + *****************/ + +struct dsp { + struct list_head list; + struct mISDNchannel ch; + struct mISDNchannel *up; + unsigned char name[64]; + int b_active; + int echo; /* echo is enabled */ + int rx_disabled; /* what the user wants */ + int rx_is_off; /* what the card is */ + int tx_mix; + struct dsp_tone tone; + struct dsp_dtmf dtmf; + int tx_volume, rx_volume; + + /* queue for sending frames */ + struct work_struct workq; + struct sk_buff_head sendq; + int hdlc; /* if mode is hdlc */ + int data_pending; /* currently an unconfirmed frame */ + + /* conference stuff */ + u32 conf_id; + struct dsp_conf *conf; + struct dsp_conf_member + *member; + + /* buffer stuff */ + int rx_W; /* current write pos for data without timestamp */ + int rx_R; /* current read pos for transmit clock */ + int rx_init; /* if set, pointers will be adjusted first */ + int tx_W; /* current write pos for transmit data */ + int tx_R; /* current read pos for transmit clock */ + int rx_delay[MAX_SECONDS_JITTER_CHECK]; + int tx_delay[MAX_SECONDS_JITTER_CHECK]; + u8 tx_buff[CMX_BUFF_SIZE]; + u8 rx_buff[CMX_BUFF_SIZE]; + int last_tx; /* if set, we transmitted last poll interval */ + int cmx_delay; /* initial delay of buffers, + or 0 for dynamic jitter buffer */ + int tx_dejitter; /* if set, dejitter tx buffer */ + int tx_data; /* enables tx-data of CMX to upper layer */ + + /* hardware stuff */ + struct dsp_features features; + int features_rx_off; /* set if rx_off is featured */ + int pcm_slot_rx; /* current PCM slot (or -1) */ + int pcm_bank_rx; + int pcm_slot_tx; + int pcm_bank_tx; + int hfc_conf; /* unique id of current conference (or -1) */ + + /* encryption stuff */ + int bf_enable; + u32 bf_p[18]; + u32 bf_s[1024]; + int bf_crypt_pos; + u8 bf_data_in[9]; + u8 bf_crypt_out[9]; + int bf_decrypt_in_pos; + int bf_decrypt_out_pos; + u8 bf_crypt_inring[16]; + u8 bf_data_out[9]; + int bf_sync; + + struct dsp_pipeline + pipeline; +}; + +/* functions */ + +extern void dsp_change_volume(struct sk_buff *skb, int volume); + +extern struct list_head dsp_ilist; +extern struct list_head conf_ilist; +extern void dsp_cmx_debug(struct dsp *dsp); +extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); +extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); +extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); +extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); +extern void dsp_cmx_send(void *arg); +extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); +extern int dsp_cmx_del_conf_member(struct dsp *dsp); +extern int dsp_cmx_del_conf(struct dsp_conf *conf); + +extern void dsp_dtmf_goertzel_init(struct dsp *dsp); +extern void dsp_dtmf_hardware(struct dsp *dsp); +extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, + int fmt); + +extern int dsp_tone(struct dsp *dsp, int tone); +extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len); +extern void dsp_tone_timeout(void *arg); + +extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len); +extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len); +extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen); +extern void dsp_bf_cleanup(struct dsp *dsp); + +extern int dsp_pipeline_module_init(void); +extern void dsp_pipeline_module_exit(void); +extern int dsp_pipeline_init(struct dsp_pipeline *pipeline); +extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline); +extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg); +extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, + int len); +extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, + int len); + diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c new file mode 100644 index 000000000000..1c2dd5694773 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_audio.c @@ -0,0 +1,434 @@ +/* + * Audio support data for mISDN_dsp. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * Rewritten by Peter + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include +#include "core.h" +#include "dsp.h" + +/* ulaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_ulaw_to_s32[256]; +/* alaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_alaw_to_s32[256]; + +s32 *dsp_audio_law_to_s32; +EXPORT_SYMBOL(dsp_audio_law_to_s32); + +/* signed 16-bit -> law */ +u8 dsp_audio_s16_to_law[65536]; +EXPORT_SYMBOL(dsp_audio_s16_to_law); + +/* alaw -> ulaw */ +u8 dsp_audio_alaw_to_ulaw[256]; +/* ulaw -> alaw */ +u8 dsp_audio_ulaw_to_alaw[256]; +u8 dsp_silence; + + +/***************************************************** + * generate table for conversion of s16 to alaw/ulaw * + *****************************************************/ + +#define AMI_MASK 0x55 + +static inline unsigned char linear2alaw(short int linear) +{ + int mask; + int seg; + int pcm_val; + static int seg_end[8] = { + 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF + }; + + pcm_val = linear; + if (pcm_val >= 0) { + /* Sign (7th) bit = 1 */ + mask = AMI_MASK | 0x80; + } else { + /* Sign bit = 0 */ + mask = AMI_MASK; + pcm_val = -pcm_val; + } + + /* Convert the scaled magnitude to segment number. */ + for (seg = 0; seg < 8; seg++) { + if (pcm_val <= seg_end[seg]) + break; + } + /* Combine the sign, segment, and quantization bits. */ + return ((seg << 4) | + ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; +} + + +static inline short int alaw2linear(unsigned char alaw) +{ + int i; + int seg; + + alaw ^= AMI_MASK; + i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x100) << (seg - 1); + return (short int) ((alaw & 0x80) ? i : -i); +} + +static inline short int ulaw2linear(unsigned char ulaw) +{ + short mu, e, f, y; + static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764}; + + mu = 255 - ulaw; + e = (mu & 0x70) / 16; + f = mu & 0x0f; + y = f * (1 << (e + 3)); + y += etab[e]; + if (mu & 0x80) + y = -y; + return y; +} + +#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ + +static unsigned char linear2ulaw(short sample) +{ + static int exp_lut[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if (sign != 0) + sample = -sample; /* get magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[(sample >> 7) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return ulawbyte; +} + +static int reverse_bits(int i) +{ + int z, j; + z = 0; + + for (j = 0; j < 8; j++) { + if ((i & (1 << j)) != 0) + z |= 1 << (7 - j); + } + return z; +} + + +void dsp_audio_generate_law_tables(void) +{ + int i; + for (i = 0; i < 256; i++) + dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i)); + + for (i = 0; i < 256; i++) + dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i)); + + for (i = 0; i < 256; i++) { + dsp_audio_alaw_to_ulaw[i] = + linear2ulaw(dsp_audio_alaw_to_s32[i]); + dsp_audio_ulaw_to_alaw[i] = + linear2alaw(dsp_audio_ulaw_to_s32[i]); + } +} + +void +dsp_audio_generate_s2law_table(void) +{ + int i; + + if (dsp_options & DSP_OPT_ULAW) { + /* generating ulaw-table */ + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2ulaw(i)); + } + } else { + /* generating alaw-table */ + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2alaw(i)); + } + } +} + + +/* + * the seven bit sample is the number of every second alaw-sample ordered by + * aplitude. 0x00 is negative, 0x7f is positive amplitude. + */ +u8 dsp_audio_seven2law[128]; +u8 dsp_audio_law2seven[256]; + +/******************************************************************** + * generate table for conversion law from/to 7-bit alaw-like sample * + ********************************************************************/ + +void +dsp_audio_generate_seven(void) +{ + int i, j, k; + u8 spl; + u8 sorted_alaw[256]; + + /* generate alaw table, sorted by the linear value */ + for (i = 0; i < 256; i++) { + j = 0; + for (k = 0; k < 256; k++) { + if (dsp_audio_alaw_to_s32[k] + < dsp_audio_alaw_to_s32[i]) { + j++; + } + } + sorted_alaw[j] = i; + } + + /* generate tabels */ + for (i = 0; i < 256; i++) { + /* spl is the source: the law-sample (converted to alaw) */ + spl = i; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_ulaw_to_alaw[i]; + /* find the 7-bit-sample */ + for (j = 0; j < 256; j++) { + if (sorted_alaw[j] == spl) + break; + } + /* write 7-bit audio value */ + dsp_audio_law2seven[i] = j >> 1; + } + for (i = 0; i < 128; i++) { + spl = sorted_alaw[i << 1]; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_alaw_to_ulaw[spl]; + dsp_audio_seven2law[i] = spl; + } +} + + +/* mix 2*law -> law */ +u8 dsp_audio_mix_law[65536]; + +/****************************************************** + * generate mix table to mix two law samples into one * + ******************************************************/ + +void +dsp_audio_generate_mix_table(void) +{ + int i, j; + s32 sample; + + i = 0; + while (i < 256) { + j = 0; + while (j < 256) { + sample = dsp_audio_law_to_s32[i]; + sample += dsp_audio_law_to_s32[j]; + if (sample > 32767) + sample = 32767; + if (sample < -32768) + sample = -32768; + dsp_audio_mix_law[(i<<8)|j] = + dsp_audio_s16_to_law[sample & 0xffff]; + j++; + } + i++; + } +} + + +/************************************* + * generate different volume changes * + *************************************/ + +static u8 dsp_audio_reduce8[256]; +static u8 dsp_audio_reduce7[256]; +static u8 dsp_audio_reduce6[256]; +static u8 dsp_audio_reduce5[256]; +static u8 dsp_audio_reduce4[256]; +static u8 dsp_audio_reduce3[256]; +static u8 dsp_audio_reduce2[256]; +static u8 dsp_audio_reduce1[256]; +static u8 dsp_audio_increase1[256]; +static u8 dsp_audio_increase2[256]; +static u8 dsp_audio_increase3[256]; +static u8 dsp_audio_increase4[256]; +static u8 dsp_audio_increase5[256]; +static u8 dsp_audio_increase6[256]; +static u8 dsp_audio_increase7[256]; +static u8 dsp_audio_increase8[256]; + +static u8 *dsp_audio_volume_change[16] = { + dsp_audio_reduce8, + dsp_audio_reduce7, + dsp_audio_reduce6, + dsp_audio_reduce5, + dsp_audio_reduce4, + dsp_audio_reduce3, + dsp_audio_reduce2, + dsp_audio_reduce1, + dsp_audio_increase1, + dsp_audio_increase2, + dsp_audio_increase3, + dsp_audio_increase4, + dsp_audio_increase5, + dsp_audio_increase6, + dsp_audio_increase7, + dsp_audio_increase8, +}; + +void +dsp_audio_generate_volume_changes(void) +{ + register s32 sample; + int i; + int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; + int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; + + i = 0; + while (i < 256) { + dsp_audio_reduce8[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff]; + dsp_audio_reduce7[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; + dsp_audio_reduce6[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; + dsp_audio_reduce5[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; + dsp_audio_reduce4[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; + dsp_audio_reduce3[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; + dsp_audio_reduce2[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; + dsp_audio_reduce1[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; + + i++; + } +} + + +/************************************** + * change the volume of the given skb * + **************************************/ + +/* this is a helper function for changing volume of skb. the range may be + * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 + */ +void +dsp_change_volume(struct sk_buff *skb, int volume) +{ + u8 *volume_change; + int i, ii; + u8 *p; + int shift; + + if (volume == 0) + return; + + /* get correct conversion table */ + if (volume < 0) { + shift = volume + 8; + if (shift < 0) + shift = 0; + } else { + shift = volume + 7; + if (shift > 15) + shift = 15; + } + volume_change = dsp_audio_volume_change[shift]; + i = 0; + ii = skb->len; + p = skb->data; + /* change volume */ + while (i < ii) { + *p = volume_change[*p]; + p++; + i++; + } +} + diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h new file mode 100644 index 000000000000..038191bc45f5 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_biquad.h @@ -0,0 +1,65 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * biquad.h - General telephony bi-quad section routines (currently this just + * handles canonic/type 2 form) + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + * 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. + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +struct biquad2_state { + int32_t gain; + int32_t a1; + int32_t a2; + int32_t b1; + int32_t b2; + + int32_t z1; + int32_t z2; +}; + +static inline void biquad2_init(struct biquad2_state *bq, + int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2) +{ + bq->gain = gain; + bq->a1 = a1; + bq->a2 = a2; + bq->b1 = b1; + bq->b2 = b2; + + bq->z1 = 0; + bq->z2 = 0; +} + +static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample) +{ + int32_t y; + int32_t z0; + + z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2; + y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2; + + bq->z2 = bq->z1; + bq->z1 = z0 >> 15; + y >>= 15; + return y; +} diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c new file mode 100644 index 000000000000..18e411e95bba --- /dev/null +++ b/drivers/isdn/mISDN/dsp_blowfish.c @@ -0,0 +1,672 @@ +/* + * Blowfish encryption/decryption for mISDN_dsp. + * + * Copyright Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "core.h" +#include "dsp.h" + +/* + * how to encode a sample stream to 64-bit blocks that will be encryped + * + * first of all, data is collected until a block of 9 samples are received. + * of course, a packet may have much more than 9 sample, but is may have + * not excacly the multiple of 9 samples. if there is a rest, the next + * received data will complete the block. + * + * the block is then converted to 9 uLAW samples without the least sigificant + * bit. the result is a 7-bit encoded sample. + * + * the samples will be reoganised to form 8 bytes of data: + * (5(6) means: encoded sample no. 5, bit 6) + * + * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) + * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) + * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) + * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) + * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) + * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) + * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) + * + * the missing bit 0 of the last byte is filled with some + * random noise, to fill all 8 bytes. + * + * the 8 bytes will be encrypted using blowfish. + * + * the result will be converted into 9 bytes. the bit 7 is used for + * checksumme (CS) for sync (0, 1) and for the last bit: + * (5(6) means: crypted byte 5, bit 6) + * + * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) + * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) + * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) + * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) + * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) + * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) + * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) + * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) + * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * + * the checksum is used to detect transmission errors and frame drops. + * + * synchronisation of received block is done by shifting the upper bit of each + * byte (bit 7) to a shift register. if the rigister has the first five bits + * (10000), this is used to find the sync. only if sync has been found, the + * current block of 9 received bytes are decrypted. before that the check + * sum is calculated. if it is incorrect the block is dropped. + * this will avoid loud noise due to corrupt encrypted data. + * + * if the last block is corrupt, the current decoded block is repeated + * until a valid block has been received. + */ + +/* + * some blowfish parts are taken from the + * crypto-api for faster implementation + */ + +struct bf_ctx { + u32 p[18]; + u32 s[1024]; +}; + +static const u32 bf_pbox[16 + 2] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b, +}; + +static const u32 bf_sbox[256 * 4] = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +}; + +/* + * Round loop unrolling macros, S is a pointer to a S-Box array + * organized in 4 unsigned longs at a row. + */ +#define GET32_3(x) (((x) & 0xff)) +#define GET32_2(x) (((x) >> (8)) & (0xff)) +#define GET32_1(x) (((x) >> (16)) & (0xff)) +#define GET32_0(x) (((x) >> (24)) & (0xff)) + +#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ + S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) + +#define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0) +#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0) + + +/* + * encrypt isdn data frame + * every block with 9 samples is encrypted + */ +void +dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len) +{ + int i = 0, j = dsp->bf_crypt_pos; + u8 *bf_data_in = dsp->bf_data_in; + u8 *bf_crypt_out = dsp->bf_crypt_out; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u32 cs; + u8 nibble; + + while (i < len) { + /* collect a block of 9 samples */ + if (j < 9) { + bf_data_in[j] = *data; + *data++ = bf_crypt_out[j++]; + i++; + continue; + } + j = 0; + /* transcode 9 samples xlaw to 8 bytes */ + yl = dsp_audio_law2seven[bf_data_in[0]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]]; + nibble = dsp_audio_law2seven[bf_data_in[4]]; + yr = nibble; + yl = (yl<<4) | (nibble>>3); + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]]; + yr = (yr<<1) | (bf_data_in[0] & 1); + + /* fill unused bit with random noise of audio input */ + /* encrypt */ + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + yl ^= P[16]; + yr ^= P[17]; + + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + + /* + * transcode 8 crypted bytes to 9 data bytes with sync + * and checksum information + */ + bf_crypt_out[0] = (yl>>25) | 0x80; + bf_crypt_out[1] = (yl>>18) & 0x7f; + bf_crypt_out[2] = (yl>>11) & 0x7f; + bf_crypt_out[3] = (yl>>4) & 0x7f; + bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07); + bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80); + bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80); + bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7); + bf_crypt_out[8] = yr; + } + + /* write current count */ + dsp->bf_crypt_pos = j; + +} + + +/* + * decrypt isdn data frame + * every block with 9 bytes is decrypted + */ +void +dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len) +{ + int i = 0; + u8 j = dsp->bf_decrypt_in_pos; + u8 k = dsp->bf_decrypt_out_pos; + u8 *bf_crypt_inring = dsp->bf_crypt_inring; + u8 *bf_data_out = dsp->bf_data_out; + u16 sync = dsp->bf_sync; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u8 nibble; + u8 cs, cs0, cs1, cs2; + + while (i < len) { + /* + * shift upper bit and rotate data to buffer ring + * send current decrypted data + */ + sync = (sync<<1) | ((*data)>>7); + bf_crypt_inring[j++ & 15] = *data; + *data++ = bf_data_out[k++]; + i++; + if (k == 9) + k = 0; /* repeat if no sync has been found */ + /* check if not in sync */ + if ((sync&0x1f0) != 0x100) + continue; + j -= 9; + /* transcode receive data to 64 bit block of encrypted data */ + yl = bf_crypt_inring[j++ & 15]; + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yr = nibble; + yl = (yl<<4) | (nibble>>3); + cs2 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs2 & 0x7f); + cs1 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs1 & 0x7f); + cs0 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs0 & 0x7f); + yr = (yr<<8) | bf_crypt_inring[j++ & 15]; + + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + + /* check if frame is valid */ + if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) { + if (dsp_debug & DEBUG_DSP_BLOWFISH) + printk(KERN_DEBUG + "DSP BLOWFISH: received corrupt frame, " + "checksumme is not correct\n"); + continue; + } + + /* decrypt */ + yr ^= P[17]; + yl ^= P[16]; + DROUND(yl, yr, 15); + DROUND(yr, yl, 14); + DROUND(yl, yr, 13); + DROUND(yr, yl, 12); + DROUND(yl, yr, 11); + DROUND(yr, yl, 10); + DROUND(yl, yr, 9); + DROUND(yr, yl, 8); + DROUND(yl, yr, 7); + DROUND(yr, yl, 6); + DROUND(yl, yr, 5); + DROUND(yr, yl, 4); + DROUND(yl, yr, 3); + DROUND(yr, yl, 2); + DROUND(yl, yr, 1); + DROUND(yr, yl, 0); + + /* transcode 8 crypted bytes to 9 sample bytes */ + bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f]; + bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f]; + bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f]; + bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f]; + bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) | + ((yr>>29) & 0x07)]; + + bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f]; + bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f]; + bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f]; + bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f]; + k = 0; /* start with new decoded frame */ + } + + /* write current count and sync */ + dsp->bf_decrypt_in_pos = j; + dsp->bf_decrypt_out_pos = k; + dsp->bf_sync = sync; +} + + +/* used to encrypt S and P boxes */ +static inline void +encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) +{ + u32 yl = src[0]; + u32 yr = src[1]; + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + + yl ^= P[16]; + yr ^= P[17]; + + dst[0] = yr; + dst[1] = yl; +} + +/* + * initialize the dsp for encryption and decryption using the same key + * Calculates the blowfish S and P boxes for encryption and decryption. + * The margin of keylen must be 4-56 bytes. + * returns 0 if ok. + */ +int +dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen) +{ + short i, j, count; + u32 data[2], temp; + u32 *P = (u32 *)dsp->bf_p; + u32 *S = (u32 *)dsp->bf_s; + + if (keylen < 4 || keylen > 56) + return 1; + + /* Set dsp states */ + i = 0; + while (i < 9) { + dsp->bf_crypt_out[i] = 0xff; + dsp->bf_data_out[i] = dsp_silence; + i++; + } + dsp->bf_crypt_pos = 0; + dsp->bf_decrypt_in_pos = 0; + dsp->bf_decrypt_out_pos = 0; + dsp->bf_sync = 0x1ff; + dsp->bf_enable = 1; + + /* Copy the initialization s-boxes */ + for (i = 0, count = 0; i < 256; i++) + for (j = 0; j < 4; j++, count++) + S[count] = bf_sbox[count]; + + /* Set the p-boxes */ + for (i = 0; i < 16 + 2; i++) + P[i] = bf_pbox[i]; + + /* Actual subkey generation */ + for (j = 0, i = 0; i < 16 + 2; i++) { + temp = (((u32)key[j] << 24) | + ((u32)key[(j + 1) % keylen] << 16) | + ((u32)key[(j + 2) % keylen] << 8) | + ((u32)key[(j + 3) % keylen])); + + P[i] = P[i] ^ temp; + j = (j + 4) % keylen; + } + + data[0] = 0x00000000; + data[1] = 0x00000000; + + for (i = 0; i < 16 + 2; i += 2) { + encrypt_block(P, S, data, data); + + P[i] = data[0]; + P[i + 1] = data[1]; + } + + for (i = 0; i < 4; i++) { + for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { + encrypt_block(P, S, data, data); + + S[count] = data[0]; + S[count + 1] = data[1]; + } + } + + return 0; +} + + +/* + * turn encryption off + */ +void +dsp_bf_cleanup(struct dsp *dsp) +{ + dsp->bf_enable = 0; +} diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c new file mode 100644 index 000000000000..e92b1ba4b45e --- /dev/null +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -0,0 +1,1886 @@ +/* + * Audio crossconnecting/conferrencing (hardware level). + * + * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * The process of adding and removing parties to/from a conference: + * + * There is a chain of struct dsp_conf which has one or more members in a chain + * of struct dsp_conf_member. + * + * After a party is added, the conference is checked for hardware capability. + * Also if a party is removed, the conference is checked again. + * + * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect + * 1-n = hardware-conference. The n will give the conference number. + * + * Depending on the change after removal or insertion of a party, hardware + * commands are given. + * + * The current solution is stored within the struct dsp_conf entry. + */ + +/* + * HOW THE CMX WORKS: + * + * There are 3 types of interaction: One member is alone, in this case only + * data flow from upper to lower layer is done. + * Two members will also exchange their data so they are crossconnected. + * Three or more members will be added in a conference and will hear each + * other but will not receive their own speech (echo) if not enabled. + * + * Features of CMX are: + * - Crossconnecting or even conference, if more than two members are together. + * - Force mixing of transmit data with other crossconnect/conference members. + * - Echo generation to benchmark the delay of audio processing. + * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. + * - Dejittering and clock generation. + * + * There are 2 buffers: + * + * + * RX-Buffer + * R W + * | | + * ----------------+-------------+------------------- + * + * The rx-buffer is a ring buffer used to store the received data for each + * individual member. This is only the case if data needs to be dejittered + * or in case of a conference where different clocks require reclocking. + * The transmit-clock (R) will read the buffer. + * If the clock overruns the write-pointer, we will have a buffer underrun. + * If the write pointer always has a certain distance from the transmit- + * clock, we will have a delay. The delay will dynamically be increased and + * reduced. + * + * + * TX-Buffer + * R W + * | | + * -----------------+--------+----------------------- + * + * The tx-buffer is a ring buffer to queue the transmit data from user space + * until it will be mixed or sent. There are two pointers, R and W. If the write + * pointer W would reach or overrun R, the buffer would overrun. In this case + * (some) data is dropped so that it will not overrun. + * Additionally a dynamic dejittering can be enabled. this allows data from + * user space that have jitter and different clock source. + * + * + * Clock: + * + * A Clock is not required, if the data source has exactly one clock. In this + * case the data source is forwarded to the destination. + * + * A Clock is required, because the data source + * - has multiple clocks. + * - has no usable clock due to jitter or packet loss (VoIP). + * In this case the system's clock is used. The clock resolution depends on + * the jiffie resolution. + * + * If a member joins a conference: + * + * - If a member joins, its rx_buff is set to silence and change read pointer + * to transmit clock. + * + * The procedure of received data from card is explained in cmx_receive. + * The procedure of received data from user space is explained in cmx_transmit. + * The procedure of transmit data to card is cmx_send. + * + * + * Interaction with other features: + * + * DTMF: + * DTMF decoding is done before the data is crossconnected. + * + * Volume change: + * Changing rx-volume is done before the data is crossconnected. The tx-volume + * must be changed whenever data is transmitted to the card by the cmx. + * + * Tones: + * If a tone is enabled, it will be processed whenever data is transmitted to + * the card. It will replace the tx-data from the user space. + * If tones are generated by hardware, this conference member is removed for + * this time. + * + * Disable rx-data: + * If cmx is realized in hardware, rx data will be disabled if requested by + * the upper layer. If dtmf decoding is done by software and enabled, rx data + * will not be diabled but blocked to the upper layer. + * + * HFC conference engine: + * If it is possible to realize all features using hardware, hardware will be + * used if not forbidden by control command. Disabling rx-data provides + * absolutely traffic free audio processing. (except for the quick 1-frame + * upload of a tone loop, only once for a new tone) + * + */ + +/* delay.h is required for hw_lock.h */ + +#include +#include +#include +#include "core.h" +#include "dsp.h" +/* + * debugging of multi party conference, + * by using conference even with two members + */ + +/* #define CMX_CONF_DEBUG */ + +/*#define CMX_DEBUG * massive read/write pointer output */ +/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ + +static inline int +count_list_member(struct list_head *head) +{ + int cnt = 0; + struct list_head *m; + + list_for_each(m, head) + cnt++; + return cnt; +} + +/* + * debug cmx memory structure + */ +void +dsp_cmx_debug(struct dsp *dsp) +{ + struct dsp_conf *conf; + struct dsp_conf_member *member; + struct dsp *odsp; + + printk(KERN_DEBUG "-----Current DSP\n"); + list_for_each_entry(odsp, &dsp_ilist, list) { + printk(KERN_DEBUG "* %s echo=%d txmix=%d", + odsp->name, odsp->echo, odsp->tx_mix); + if (odsp->conf) + printk(" (Conf %d)", odsp->conf->id); + if (dsp == odsp) + printk(" *this*"); + printk("\n"); + } + printk(KERN_DEBUG "-----Current Conf:\n"); + list_for_each_entry(conf, &conf_ilist, list) { + printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); + list_for_each_entry(member, &conf->mlist, list) { + printk(KERN_DEBUG + " - member = %s (slot_tx %d, bank_tx %d, " + "slot_rx %d, bank_rx %d hfc_conf %d)%s\n", + member->dsp->name, member->dsp->pcm_slot_tx, + member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, + member->dsp->pcm_bank_rx, member->dsp->hfc_conf, + (member->dsp == dsp) ? " *this*" : ""); + } + } + printk(KERN_DEBUG "-----end\n"); +} + +/* + * search conference + */ +static struct dsp_conf * +dsp_cmx_search_conf(u32 id) +{ + struct dsp_conf *conf; + + if (!id) { + printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); + return NULL; + } + + /* search conference */ + list_for_each_entry(conf, &conf_ilist, list) + if (conf->id == id) + return conf; + + return NULL; +} + + +/* + * add member to conference + */ +static int +dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) +{ + struct dsp_conf_member *member; + + if (!conf || !dsp) { + printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); + return -EINVAL; + } + if (dsp->member) { + printk(KERN_WARNING "%s: dsp is already member in a conf.\n", + __func__); + return -EINVAL; + } + + if (dsp->conf) { + printk(KERN_WARNING "%s: dsp is already in a conf.\n", + __func__); + return -EINVAL; + } + + member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC); + if (!member) { + printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n"); + return -ENOMEM; + } + member->dsp = dsp; + /* clear rx buffer */ + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ + dsp->rx_W = 0; + dsp->rx_R = 0; + + list_add_tail(&member->list, &conf->mlist); + + dsp->conf = conf; + dsp->member = member; + + return 0; +} + + +/* + * del member from conference + */ +int +dsp_cmx_del_conf_member(struct dsp *dsp) +{ + struct dsp_conf_member *member; + + if (!dsp) { + printk(KERN_WARNING "%s: dsp is 0.\n", + __func__); + return -EINVAL; + } + + if (!dsp->conf) { + printk(KERN_WARNING "%s: dsp is not in a conf.\n", + __func__); + return -EINVAL; + } + + if (list_empty(&dsp->conf->mlist)) { + printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", + __func__); + return -EINVAL; + } + + /* find us in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (member->dsp == dsp) { + list_del(&member->list); + dsp->conf = NULL; + dsp->member = NULL; + kfree(member); + return 0; + } + } + printk(KERN_WARNING + "%s: dsp is not present in its own conf_meber list.\n", + __func__); + + return -EINVAL; +} + + +/* + * new conference + */ +static struct dsp_conf +*dsp_cmx_new_conf(u32 id) +{ + struct dsp_conf *conf; + + if (!id) { + printk(KERN_WARNING "%s: id is 0.\n", + __func__); + return NULL; + } + + conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC); + if (!conf) { + printk(KERN_ERR "kmalloc struct dsp_conf failed\n"); + return NULL; + } + INIT_LIST_HEAD(&conf->mlist); + conf->id = id; + + list_add_tail(&conf->list, &conf_ilist); + + return conf; +} + + +/* + * del conference + */ +int +dsp_cmx_del_conf(struct dsp_conf *conf) +{ + if (!conf) { + printk(KERN_WARNING "%s: conf is null.\n", + __func__); + return -EINVAL; + } + + if (!list_empty(&conf->mlist)) { + printk(KERN_WARNING "%s: conf not empty.\n", + __func__); + return -EINVAL; + } + list_del(&conf->list); + kfree(conf); + + return 0; +} + + +/* + * send HW message to hfc card + */ +static void +dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, + u32 param3, u32 param4) +{ + struct mISDN_ctrl_req cq; + + memset(&cq, 0, sizeof(cq)); + cq.op = message; + cq.p1 = param1 | (param2 << 8); + cq.p2 = param3 | (param4 << 8); + if (dsp->ch.peer) + dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); +} + + +/* + * do hardware update and set the software/hardware flag + * + * either a conference or a dsp instance can be given + * if only dsp instance is given, the instance is not associated with a conf + * and therefore removed. if a conference is given, the dsp is expected to + * be member of that conference. + */ +void +dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) +{ + struct dsp_conf_member *member, *nextm; + struct dsp *finddsp; + int memb = 0, i, ii, i1, i2; + int freeunits[8]; + u_char freeslots[256]; + int same_hfc = -1, same_pcm = -1, current_conf = -1, + all_conf = 1; + + /* dsp gets updated (no conf) */ + if (!conf) { + if (!dsp) + return; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking dsp %s\n", + __func__, dsp->name); +one_member: + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d " + "because dsp is split\n", __func__, + dsp->name, dsp->hfc_conf); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, + 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* process hw echo */ + if (dsp->features.pcm_banks < 1) + return; + if (!dsp->echo) { + /* NO ECHO: remove PCM slot if assigned */ + if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from" + " PCM slot %d (TX) %d (RX) because" + " dsp is split (no echo)\n", + __func__, dsp->name, + dsp->pcm_slot_tx, dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, + 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + return; + } + /* ECHO: already echo */ + if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && + dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) + return; + /* ECHO: if slot already assigned */ + if (dsp->pcm_slot_tx >= 0) { + dsp->pcm_slot_rx = dsp->pcm_slot_tx; + dsp->pcm_bank_tx = 2; /* 2 means loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s refresh %s for echo using slot %d\n", + __func__, dsp->name, + dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, + dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + /* ECHO: find slot */ + dsp->pcm_slot_tx = -1; + dsp->pcm_slot_rx = -1; + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(finddsp, &dsp_ilist, list) { + if (finddsp->features.pcm_id == dsp->features.pcm_id) { + if (finddsp->pcm_slot_rx >= 0 && + finddsp->pcm_slot_rx < sizeof(freeslots)) + freeslots[finddsp->pcm_slot_tx] = 0; + if (finddsp->pcm_slot_tx >= 0 && + finddsp->pcm_slot_tx < sizeof(freeslots)) + freeslots[finddsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available for echo\n", + __func__); + /* no more slots available */ + return; + } + /* assign free slot */ + dsp->pcm_slot_tx = i; + dsp->pcm_slot_rx = i; + dsp->pcm_bank_tx = 2; /* loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s assign echo for %s using slot %d\n", + __func__, dsp->name, dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, + dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + + /* conf gets updated (all members) */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking conference %d\n", + __func__, conf->id); + + if (list_empty(&conf->mlist)) { + printk(KERN_ERR "%s: conference whithout members\n", + __func__); + return; + } + member = list_entry(conf->mlist.next, struct dsp_conf_member, list); + same_hfc = member->dsp->features.hfc_id; + same_pcm = member->dsp->features.pcm_id; + /* check all members in our conference */ + list_for_each_entry(member, &conf->mlist, list) { + /* check if member uses mixing */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_mix is turned on\n", __func__, + member->dsp->name); +conf_software: + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC " + "conf %d because not " + "possible with hardware\n", + __func__, + dsp->name, + dsp->hfc_conf); + dsp_cmx_hw_message(dsp, + MISDN_CTRL_HFC_CONF_SPLIT, + 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* remove PCM slot if assigned */ + if (dsp->pcm_slot_tx >= 0 || + dsp->pcm_slot_rx >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing " + "%s from PCM slot %d (TX)" + " slot %d (RX) because not" + " possible with hardware\n", + __func__, + dsp->name, + dsp->pcm_slot_tx, + dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, + MISDN_CTRL_HFC_PCM_DISC, + 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + } + conf->hardware = 0; + conf->software = 1; + return; + } + /* check if member has echo turned on */ + if (member->dsp->echo) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "echo is turned on\n", __func__, + member->dsp->name); + goto conf_software; + } + /* check if member has tx_mix turned on */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_mix is turned on\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if member changes volume at an not suppoted level */ + if (member->dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_volume is changed\n", + __func__, member->dsp->name); + goto conf_software; + } + if (member->dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "rx_volume is changed\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if tx-data turned on */ + if (member->dsp->tx_data) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_data is turned on\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if pipeline exists */ + if (member->dsp->pipeline.inuse) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "pipeline exists\n", __func__, + member->dsp->name); + goto conf_software; + } + /* check if encryption is enabled */ + if (member->dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a " + "conf, because encryption is enabled\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if member is on a card with PCM support */ + if (member->dsp->features.pcm_id < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "dsp has no PCM bus\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if relations are on the same PCM bus */ + if (member->dsp->features.pcm_id != same_pcm) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "dsp is on a different PCM bus than the " + "first dsp\n", + __func__, member->dsp->name); + goto conf_software; + } + /* determine if members are on the same hfc chip */ + if (same_hfc != member->dsp->features.hfc_id) + same_hfc = -1; + /* if there are members already in a conference */ + if (current_conf < 0 && member->dsp->hfc_conf >= 0) + current_conf = member->dsp->hfc_conf; + /* if any member is not in a conference */ + if (member->dsp->hfc_conf < 0) + all_conf = 0; + + memb++; + } + + /* if no member, this is an error */ + if (memb < 1) + return; + + /* one member */ + if (memb == 1) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conf %d cannot form a HW conference, " + "because dsp is alone\n", __func__, conf->id); + conf->hardware = 0; + conf->software = 0; + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + dsp = member->dsp; + goto one_member; + } + + /* + * ok, now we are sure that all members are on the same pcm. + * now we will see if we have only two members, so we can do + * crossconnections, which don't have any limitations. + */ + + /* if we have only two members */ + if (memb == 2) { + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + nextm = list_entry(member->list.next, struct dsp_conf_member, + list); + /* remove HFC conference if enabled */ + if (member->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d because " + "two parties require only a PCM slot\n", + __func__, member->dsp->name, + member->dsp->hfc_conf); + dsp_cmx_hw_message(member->dsp, + MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); + member->dsp->hfc_conf = -1; + } + if (nextm->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d because " + "two parties require only a PCM slot\n", + __func__, nextm->dsp->name, + nextm->dsp->hfc_conf); + dsp_cmx_hw_message(nextm->dsp, + MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); + nextm->dsp->hfc_conf = -1; + } + /* if members have two banks (and not on the same chip) */ + if (member->dsp->features.pcm_banks > 1 && + nextm->dsp->features.pcm_banks > 1 && + member->dsp->features.hfc_id != + nextm->dsp->features.hfc_id) { + /* if both members have same slots with crossed banks */ + if (member->dsp->pcm_slot_tx >= 0 && + member->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx >= 0 && + nextm->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_rx && + nextm->dsp->pcm_slot_rx == + member->dsp->pcm_slot_tx && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_tx && + member->dsp->pcm_bank_tx != + member->dsp->pcm_bank_rx && + nextm->dsp->pcm_bank_tx != + nextm->dsp->pcm_bank_rx) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s & %s stay joined on " + "PCM slot %d bank %d (TX) bank %d " + "(RX) (on different chips)\n", + __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_bank_tx, + member->dsp->pcm_bank_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find a new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp != member->dsp && + dsp != nextm->dsp && + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available for " + "%s & %s\n", __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + /* assign free slot */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + nextm->dsp->pcm_slot_tx = i; + nextm->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 1; + nextm->dsp->pcm_bank_rx = 1; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s adding %s & %s to new PCM slot %d " + "(TX and RX on different chips) because " + "both members have not same slots\n", + __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx); + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, + nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + /* if members have one bank (or on the same chip) */ + } else { + /* if both members have different crossed slots */ + if (member->dsp->pcm_slot_tx >= 0 && + member->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx >= 0 && + nextm->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_rx && + nextm->dsp->pcm_slot_rx == + member->dsp->pcm_slot_tx && + member->dsp->pcm_slot_tx != + member->dsp->pcm_slot_rx && + member->dsp->pcm_bank_tx == 0 && + member->dsp->pcm_bank_rx == 0 && + nextm->dsp->pcm_bank_tx == 0 && + nextm->dsp->pcm_bank_rx == 0) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s & %s stay joined on PCM " + "slot %d (TX) %d (RX) on same chip " + "or one bank PCM)\n", __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find two new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp != member->dsp && + dsp != nextm->dsp && + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i1 = 0; + ii = member->dsp->features.pcm_slots; + while (i1 < ii) { + if (freeslots[i1]) + break; + i1++; + } + if (i1 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available " + "for %s & %s\n", __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + i2 = i1+1; + while (i2 < ii) { + if (freeslots[i2]) + break; + i2++; + } + if (i2 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available " + "for %s & %s\n", + __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + /* assign free slots */ + member->dsp->pcm_slot_tx = i1; + member->dsp->pcm_slot_rx = i2; + nextm->dsp->pcm_slot_tx = i2; + nextm->dsp->pcm_slot_rx = i1; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 0; + nextm->dsp->pcm_bank_rx = 0; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s adding %s & %s to new PCM slot %d " + "(TX) %d (RX) on same chip or one bank " + "PCM, because both members have not " + "crossed slots\n", __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, + nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + } + } + + /* + * if we have more than two, we may check if we have a conference + * unit available on the chip. also all members must be on the same + */ + + /* if not the same HFC chip */ + if (same_hfc < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed, because " + "members are on different chips or not " + "on HFC chip\n", + __func__, conf->id); + goto conf_software; + } + + /* for more than two members.. */ + + /* in case of hdlc, we change to software */ + if (dsp->hdlc) + goto conf_software; + + /* if all members already have the same conference */ + if (all_conf) + return; + + /* + * if there is an existing conference, but not all members have joined + */ + if (current_conf >= 0) { +join_members: + list_for_each_entry(member, &conf->mlist, list) { + /* join to current conference */ + if (member->dsp->hfc_conf == current_conf) + continue; + /* get a free timeslot first */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + /* + * not checking current member, because + * slot will be overwritten. + */ + if ( + dsp != member->dsp && + /* dsp must be on the same PCM */ + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + /* dsp must be on a slot */ + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + /* no more slots available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed," + " because no slot free\n", + __func__, conf->id); + goto conf_software; + } + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s changing dsp %s to HW conference " + "%d slot %d\n", __func__, + member->dsp->name, current_conf, i); + /* assign free slot & set PCM & join conf */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_tx = 2; /* loop */ + member->dsp->pcm_bank_rx = 2; + member->dsp->hfc_conf = current_conf; + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + i, 2, i, 2); + dsp_cmx_hw_message(member->dsp, + MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); + } + return; + } + + /* + * no member is in a conference yet, so we find a free one + */ + memset(freeunits, 1, sizeof(freeunits)); + list_for_each_entry(dsp, &dsp_ilist, list) { + /* dsp must be on the same chip */ + if (dsp->features.hfc_id == same_hfc && + /* dsp must have joined a HW conference */ + dsp->hfc_conf >= 0 && + /* slot must be within range */ + dsp->hfc_conf < 8) + freeunits[dsp->hfc_conf] = 0; + } + i = 0; + ii = 8; + while (i < ii) { + if (freeunits[i]) + break; + i++; + } + if (i == ii) { + /* no more conferences available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed, because " + "no conference number free\n", + __func__, conf->id); + goto conf_software; + } + /* join all members */ + current_conf = i; + goto join_members; +} + + +/* + * conf_id != 0: join or change conference + * conf_id == 0: split from conference if not already + */ +int +dsp_cmx_conf(struct dsp *dsp, u32 conf_id) +{ + int err; + struct dsp_conf *conf; + struct dsp_conf_member *member; + + /* if conference doesn't change */ + if (dsp->conf_id == conf_id) + return 0; + + /* first remove us from current conf */ + if (dsp->conf_id) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "removing us from conference %d\n", + dsp->conf->id); + /* remove us from conf */ + conf = dsp->conf; + err = dsp_cmx_del_conf_member(dsp); + if (err) + return err; + dsp->conf_id = 0; + + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + + /* conf now empty? */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "conference is empty, so we remove it.\n"); + err = dsp_cmx_del_conf(conf); + if (err) + return err; + } else { + /* update members left on conf */ + dsp_cmx_hardware(conf, NULL); + } + } + + /* if split */ + if (!conf_id) + return 0; + + /* now add us to conf */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "searching conference %d\n", + conf_id); + conf = dsp_cmx_search_conf(conf_id); + if (!conf) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "conference doesn't exist yet, creating.\n"); + /* the conference doesn't exist, so we create */ + conf = dsp_cmx_new_conf(conf_id); + if (!conf) + return -EINVAL; + } else if (!list_empty(&conf->mlist)) { + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + if (dsp->hdlc && !member->dsp->hdlc) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cannot join transparent conference.\n"); + return -EINVAL; + } + if (!dsp->hdlc && member->dsp->hdlc) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cannot join hdlc conference.\n"); + return -EINVAL; + } + } + /* add conference member */ + err = dsp_cmx_add_conf_member(dsp, conf); + if (err) + return err; + dsp->conf_id = conf_id; + + /* if we are alone, we do nothing! */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "we are alone in this conference, so exit.\n"); + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + return 0; + } + + /* update members on conf */ + dsp_cmx_hardware(conf, NULL); + + return 0; +} + + +/* + * audio data is received from card + */ +void +dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) +{ + u8 *d, *p; + int len = skb->len; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int w, i, ii; + + /* check if we have sompen */ + if (len < 1) + return; + + /* half of the buffer should be larger than maximum packet size */ + if (len >= CMX_BUFF_HALF) { + printk(KERN_ERR + "%s line %d: packet from card is too large (%d bytes). " + "please make card send smaller packets OR increase " + "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); + return; + } + + /* + * initialize pointers if not already - + * also add delay if requested by PH_SIGNAL + */ + if (dsp->rx_init) { + dsp->rx_init = 0; + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + } + /* if frame contains time code, write directly */ + if (dsp->features.unordered) { + dsp->rx_W = (hh->id & CMX_BUFF_MASK); + /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ + } + /* + * if we underrun (or maybe overrun), + * we set our new read pointer, and write silence to buffer + */ + if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " + "maximum delay), adjusting read pointer! " + "(inst %s)\n", (u_long)dsp, dsp->name); + /* flush buffer */ + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + } + /* if we have reached double delay, jump back to middle */ + if (dsp->cmx_delay) + if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= + (dsp->cmx_delay << 1)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): OVERRUN (because " + "twice the delay is reached), adjusting " + "read pointer! (inst %s)\n", + (u_long)dsp, dsp->name); + /* flush buffer */ + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + } + + /* show where to write */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", + (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); +#endif + + /* write data into rx_buffer */ + p = skb->data; + d = dsp->rx_buff; + w = dsp->rx_W; + i = 0; + ii = len; + while (i < ii) { + d[w++ & CMX_BUFF_MASK] = *p++; + i++; + } + + /* increase write-pointer */ + dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK); +} + + +/* + * send (mixed) audio data to card and control jitter + */ +static void +dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) +{ + struct dsp_conf *conf = dsp->conf; + struct dsp *member, *other; + register s32 sample; + u8 *d, *p, *q, *o_q; + struct sk_buff *nskb, *txskb; + int r, rr, t, tt, o_r, o_rr; + int preload = 0; + struct mISDNhead *hh, *thh; + + /* don't process if: */ + if (!dsp->b_active) { /* if not active */ + dsp->last_tx = 0; + return; + } + if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */ + dsp->tx_R == dsp->tx_W && /* AND no tx-data */ + !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ + dsp->last_tx = 0; + return; + } + +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", + members, dsp->name, conf, dsp->rx_R, dsp->rx_W); +#endif + + /* preload if we have delay set */ + if (dsp->cmx_delay && !dsp->last_tx) { + preload = len; + if (preload < 128) + preload = 128; + } + + /* PREPARE RESULT */ + nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); + if (!nskb) { + printk(KERN_ERR + "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", + len + preload); + return; + } + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + dsp->last_tx = 1; + + /* set pointers, indexes and stuff */ + member = dsp; + p = dsp->tx_buff; /* transmit data */ + q = dsp->rx_buff; /* received data */ + d = skb_put(nskb, preload + len); /* result */ + t = dsp->tx_R; /* tx-pointers */ + tt = dsp->tx_W; + r = dsp->rx_R; /* rx-pointers */ + rr = (r + len) & CMX_BUFF_MASK; + + /* preload with silence, if required */ + if (preload) { + memset(d, dsp_silence, preload); + d += preload; + } + + /* PROCESS TONES/TX-DATA ONLY */ + if (dsp->tone.tone && dsp->tone.software) { + /* -> copy tone */ + dsp_tone_copy(dsp, d, len); + dsp->tx_R = 0; /* clear tx buffer */ + dsp->tx_W = 0; + goto send_packet; + } + /* if we have tx-data but do not use mixing */ + if (!dsp->tx_mix && t != tt) { + /* -> send tx-data and continue when not enough */ +#ifdef CMX_TX_DEBUG + sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); +#endif + while (r != rr && t != tt) { +#ifdef CMX_TX_DEBUG + if (strlen(debugbuf) < 48) + sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]); +#endif + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if (r == rr) { + dsp->tx_R = t; +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + goto send_packet; + } + } +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + + /* PROCESS DATA (one member / no conf) */ + if (!conf || members <= 1) { + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* -> send tx-data if available or use 0-volume */ + while (r != rr && t != tt) { + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if (r != rr) + memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); + /* -> if echo is enabled */ + } else { + /* + * -> mix tx-data with echo if available, + * or use echo only + */ + while (r != rr && t != tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]]; + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + *d++ = q[r]; /* echo */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + } + /* PROCESS DATA (two members) */ +#ifdef CMX_CONF_DEBUG + if (0) { +#else + if (members == 2) { +#endif + /* "other" becomes other party */ + other = (list_entry(conf->mlist.next, + struct dsp_conf_member, list))->dsp; + if (other == member) + other = (list_entry(conf->mlist.prev, + struct dsp_conf_member, list))->dsp; + o_q = other->rx_buff; /* received data */ + o_rr = (other->rx_R + len) & CMX_BUFF_MASK; + /* end of rx-pointer */ + o_r = (o_rr - rr + r) & CMX_BUFF_MASK; + /* start rx-pointer at current read position*/ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* + * -> copy other member's rx-data, + * if tx-data is available, mix + */ + while (o_r != o_rr && t != tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]]; + t = (t+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + while (o_r != o_rr) { + *d++ = o_q[o_r]; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* + * -> mix other member's rx-data with echo, + * if tx-data is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + + dsp_audio_law_to_s32[q[r]] + + dsp_audio_law_to_s32[o_q[o_r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* tx-data + rx_data + echo */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]]; + r = (r+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + } +#ifdef DSP_NEVER_DEFINED + } +#endif + /* PROCESS DATA (three or more members) */ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* + * -> substract rx-data from conf-data, + * if tx-data is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + *c++ - + dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf-rx+tx */ + r = (r+1) & CMX_BUFF_MASK; + t = (t+1) & CMX_BUFF_MASK; + } + while (r != rr) { + sample = *c++ - dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf-rx */ + r = (r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* + * -> encode conf-data, if tx-data + * is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + *c++; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf(echo)+tx */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + sample = *c++; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf(echo) */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + +send_packet: + /* + * send tx-data if enabled - don't filter, + * becuase we want what we send, not what we filtered + */ + if (dsp->tx_data) { + /* PREPARE RESULT */ + txskb = mI_alloc_skb(len, GFP_ATOMIC); + if (!txskb) { + printk(KERN_ERR + "FATAL ERROR in mISDN_dsp.o: " + "cannot alloc %d bytes\n", len); + } else { + thh = mISDN_HEAD_P(txskb); + thh->prim = DL_DATA_REQ; + thh->id = 0; + memcpy(skb_put(txskb, len), nskb->data+preload, len); + /* queue (trigger later) */ + skb_queue_tail(&dsp->sendq, txskb); + } + } + /* adjust volume */ + if (dsp->tx_volume) + dsp_change_volume(nskb, dsp->tx_volume); + /* pipeline */ + if (dsp->pipeline.inuse) + dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len); + /* crypt */ + if (dsp->bf_enable) + dsp_bf_encrypt(dsp, nskb->data, nskb->len); + /* queue and trigger */ + skb_queue_tail(&dsp->sendq, nskb); + schedule_work(&dsp->workq); +} + +u32 samplecount; +struct timer_list dsp_spl_tl; +u32 dsp_spl_jiffies; /* calculate the next time to fire */ +u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ +struct timeval dsp_start_tv; /* time at start of calculation */ + +void +dsp_cmx_send(void *arg) +{ + struct dsp_conf *conf; + struct dsp_conf_member *member; + struct dsp *dsp; + int mustmix, members; + s32 mixbuffer[MAX_POLL+100], *c; + u8 *p, *q; + int r, rr; + int jittercheck = 0, delay, i; + u_long flags; + struct timeval tv; + u32 elapsed; + s16 length; + + /* lock */ + spin_lock_irqsave(&dsp_lock, flags); + + if (!dsp_start_tv.tv_sec) { + do_gettimeofday(&dsp_start_tv); + length = dsp_poll; + } else { + do_gettimeofday(&tv); + elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) + + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); + dsp_start_tv.tv_sec = tv.tv_sec; + dsp_start_tv.tv_usec = tv.tv_usec; + length = elapsed; + } + if (length > MAX_POLL + 100) + length = MAX_POLL + 100; +/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", + length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, + dsp_poll_diff & 0xffff); + */ + + /* + * check if jitter needs to be checked + * (this is about every second = 8192 samples) + */ + samplecount += length; + if ((samplecount & 8191) < length) + jittercheck = 1; + + /* loop all members that do not require conference mixing */ + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp->hdlc) + continue; + conf = dsp->conf; + mustmix = 0; + members = 0; + if (conf) { + members = count_list_member(&conf->mlist); +#ifdef CMX_CONF_DEBUG + if (conf->software && members > 1) +#else + if (conf->software && members > 2) +#endif + mustmix = 1; + } + + /* transmission required */ + if (!mustmix) { + dsp_cmx_send_member(dsp, length, mixbuffer, members); + + /* + * unused mixbuffer is given to prevent a + * potential null-pointer-bug + */ + } + } + + /* loop all members that require conference mixing */ + list_for_each_entry(conf, &conf_ilist, list) { + /* count members and check hardware */ + members = count_list_member(&conf->mlist); +#ifdef CMX_CONF_DEBUG + if (conf->software && members > 1) { +#else + if (conf->software && members > 2) { +#endif + /* check for hdlc conf */ + member = list_entry(conf->mlist.next, + struct dsp_conf_member, list); + if (member->dsp->hdlc) + continue; + /* mix all data */ + memset(mixbuffer, 0, length*sizeof(s32)); + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* get range of data to mix */ + c = mixbuffer; + q = dsp->rx_buff; + r = dsp->rx_R; + rr = (r + length) & CMX_BUFF_MASK; + /* add member's data */ + while (r != rr) { + *c++ += dsp_audio_law_to_s32[q[r]]; + r = (r+1) & CMX_BUFF_MASK; + } + } + + /* process each member */ + list_for_each_entry(member, &conf->mlist, list) { + /* transmission */ + dsp_cmx_send_member(member->dsp, length, + mixbuffer, members); + } + } + } + + /* delete rx-data, increment buffers, change pointers */ + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp->hdlc) + continue; + p = dsp->rx_buff; + q = dsp->tx_buff; + r = dsp->rx_R; + /* move receive pointer when receiving */ + if (!dsp->rx_is_off) { + rr = (r + length) & CMX_BUFF_MASK; + /* delete rx-data */ + while (r != rr) { + p[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->rx_R = r; /* write incremented read pointer */ + } + + /* check current rx_delay */ + delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; + if (delay >= CMX_BUFF_HALF) + delay = 0; /* will be the delay before next write */ + /* check for lower delay */ + if (delay < dsp->rx_delay[0]) + dsp->rx_delay[0] = delay; + /* check current tx_delay */ + delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; + if (delay >= CMX_BUFF_HALF) + delay = 0; /* will be the delay before next write */ + /* check for lower delay */ + if (delay < dsp->tx_delay[0]) + dsp->tx_delay[0] = delay; + if (jittercheck) { + /* find the lowest of all rx_delays */ + delay = dsp->rx_delay[0]; + i = 1; + while (i < MAX_SECONDS_JITTER_CHECK) { + if (delay > dsp->rx_delay[i]) + delay = dsp->rx_delay[i]; + i++; + } + /* + * remove rx_delay only if we have delay AND we + * have not preset cmx_delay + */ + if (delay && !dsp->cmx_delay) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s lowest rx_delay of %d bytes for" + " dsp %s are now removed.\n", + __func__, delay, + dsp->name); + r = dsp->rx_R; + rr = (r + delay) & CMX_BUFF_MASK; + /* delete rx-data */ + while (r != rr) { + p[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->rx_R = r; + /* write incremented read pointer */ + } + /* find the lowest of all tx_delays */ + delay = dsp->tx_delay[0]; + i = 1; + while (i < MAX_SECONDS_JITTER_CHECK) { + if (delay > dsp->tx_delay[i]) + delay = dsp->tx_delay[i]; + i++; + } + /* + * remove delay only if we have delay AND we + * have enabled tx_dejitter + */ + if (delay && dsp->tx_dejitter) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s lowest tx_delay of %d bytes for" + " dsp %s are now removed.\n", + __func__, delay, + dsp->name); + r = dsp->tx_R; + rr = (r + delay) & CMX_BUFF_MASK; + /* delete tx-data */ + while (r != rr) { + q[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->tx_R = r; + /* write incremented read pointer */ + } + /* scroll up delays */ + i = MAX_SECONDS_JITTER_CHECK - 1; + while (i) { + dsp->rx_delay[i] = dsp->rx_delay[i-1]; + dsp->tx_delay[i] = dsp->tx_delay[i-1]; + i--; + } + dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ + dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ + } + } + + /* if next event would be in the past ... */ + if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0) + dsp_spl_jiffies = jiffies + 1; + else + dsp_spl_jiffies += dsp_tics; + + dsp_spl_tl.expires = dsp_spl_jiffies; + add_timer(&dsp_spl_tl); + + /* unlock */ + spin_unlock_irqrestore(&dsp_lock, flags); +} + +/* + * audio data is transmitted from upper layer to the dsp + */ +void +dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) +{ + u_int w, ww; + u8 *d, *p; + int space; /* todo: , l = skb->len; */ +#ifdef CMX_TX_DEBUG + char debugbuf[256] = ""; +#endif + + /* check if there is enough space, and then copy */ + w = dsp->tx_W; + ww = dsp->tx_R; + p = dsp->tx_buff; + d = skb->data; + space = ww-w; + if (space <= 0) + space += CMX_BUFF_SIZE; + /* write-pointer should not overrun nor reach read pointer */ + if (space-1 < skb->len) + /* write to the space we have left */ + ww = (ww - 1) & CMX_BUFF_MASK; + else + /* write until all byte are copied */ + ww = (w + skb->len) & CMX_BUFF_MASK; + dsp->tx_W = ww; + + /* show current buffer */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", + (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name); +#endif + + /* copy transmit data to tx-buffer */ +#ifdef CMX_TX_DEBUG + sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); +#endif + while (w != ww) { +#ifdef CMX_TX_DEBUG + if (strlen(debugbuf) < 48) + sprintf(debugbuf+strlen(debugbuf), " %02x", *d); +#endif + p[w] = *d++; + w = (w+1) & CMX_BUFF_MASK; + } +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + +} + +/* + * hdlc data is received from card and sent to all members. + */ +void +dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) +{ + struct sk_buff *nskb = NULL; + struct dsp_conf_member *member; + struct mISDNhead *hh; + + /* not if not active */ + if (!dsp->b_active) + return; + + /* check if we have sompen */ + if (skb->len < 1) + return; + + /* no conf */ + if (!dsp->conf) { + /* in case of hardware (echo) */ + if (dsp->pcm_slot_tx >= 0) + return; + if (dsp->echo) + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + skb_queue_tail(&dsp->sendq, nskb); + schedule_work(&dsp->workq); + } + return; + } + /* in case of hardware conference */ + if (dsp->conf->hardware) + return; + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (dsp->echo || member->dsp != dsp) { + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + skb_queue_tail(&member->dsp->sendq, nskb); + schedule_work(&member->dsp->workq); + } + } + } +} + + diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c new file mode 100644 index 000000000000..2f10ed82c0db --- /dev/null +++ b/drivers/isdn/mISDN/dsp_core.c @@ -0,0 +1,1191 @@ +/* + * Author Andreas Eversberg (jolly@eversberg.eu) + * Based on source code structure by + * Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Karsten Keil (great drivers) + * Cologne Chip (great chips) + * + * This module does: + * Real-time tone generation + * DTMF detection + * Real-time cross-connection and conferrence + * Compensate jitter due to system load and hardware fault. + * All features are done in kernel space and will be realized + * using hardware, if available and supported by chip set. + * Blowfish encryption/decryption + */ + +/* STRUCTURE: + * + * The dsp module provides layer 2 for b-channels (64kbit). It provides + * transparent audio forwarding with special digital signal processing: + * + * - (1) generation of tones + * - (2) detection of dtmf tones + * - (3) crossconnecting and conferences (clocking) + * - (4) echo generation for delay test + * - (5) volume control + * - (6) disable receive data + * - (7) pipeline + * - (8) encryption/decryption + * + * Look: + * TX RX + * ------upper layer------ + * | ^ + * | |(6) + * v | + * +-----+-------------+-----+ + * |(3)(4) | + * | CMX | + * | | + * | +-------------+ + * | | ^ + * | | | + * |+---------+| +----+----+ + * ||(1) || |(2) | + * || || | | + * || Tones || | DTMF | + * || || | | + * || || | | + * |+----+----+| +----+----+ + * +-----+-----+ ^ + * | | + * v | + * +----+----+ +----+----+ + * |(5) | |(5) | + * | | | | + * |TX Volume| |RX Volume| + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * +----+-------------+----+ + * |(7) | + * | | + * | Pipeline Processing | + * | | + * | | + * +----+-------------+----+ + * | ^ + * | | + * v | + * +----+----+ +----+----+ + * |(8) | |(8) | + * | | | | + * | Encrypt | | Decrypt | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * ------card layer------ + * TX RX + * + * Above you can see the logical data flow. If software is used to do the + * process, it is actually the real data flow. If hardware is used, data + * may not flow, but hardware commands to the card, to provide the data flow + * as shown. + * + * NOTE: The channel must be activated in order to make dsp work, even if + * no data flow to the upper layer is intended. Activation can be done + * after and before controlling the setting using PH_CONTROL requests. + * + * DTMF: Will be detected by hardware if possible. It is done before CMX + * processing. + * + * Tones: Will be generated via software if endless looped audio fifos are + * not supported by hardware. Tones will override all data from CMX. + * It is not required to join a conference to use tones at any time. + * + * CMX: Is transparent when not used. When it is used, it will do + * crossconnections and conferences via software if not possible through + * hardware. If hardware capability is available, hardware is used. + * + * Echo: Is generated by CMX and is used to check performane of hard and + * software CMX. + * + * The CMX has special functions for conferences with one, two and more + * members. It will allow different types of data flow. Receive and transmit + * data to/form upper layer may be swithed on/off individually without loosing + * features of CMX, Tones and DTMF. + * + * Echo Cancellation: Sometimes we like to cancel echo from the interface. + * Note that a VoIP call may not have echo caused by the IP phone. The echo + * is generated by the telephone line connected to it. Because the delay + * is high, it becomes an echo. RESULT: Echo Cachelation is required if + * both echo AND delay is applied to an interface. + * Remember that software CMX always generates a more or less delay. + * + * If all used features can be realized in hardware, and if transmit and/or + * receive data ist disabled, the card may not send/receive any data at all. + * Not receiving is usefull if only announcements are played. Not sending is + * usefull if an answering machine records audio. Not sending and receiving is + * usefull during most states of the call. If supported by hardware, tones + * will be played without cpu load. Small PBXs and NT-Mode applications will + * not need expensive hardware when processing calls. + * + * + * LOCKING: + * + * When data is received from upper or lower layer (card), the complete dsp + * module is locked by a global lock. This lock MUST lock irq, because it + * must lock timer events by DSP poll timer. + * When data is ready to be transmitted down, the data is queued and sent + * outside lock and timer event. + * PH_CONTROL must not change any settings, join or split conference members + * during process of data. + * + * HDLC: + * + * It works quite the same as transparent, except that HDLC data is forwarded + * to all other conference members if no hardware bridging is possible. + * Send data will be writte to sendq. Sendq will be sent if confirm is received. + * Conference cannot join, if one member is not hdlc. + * + */ + +#include +#include +#include +#include +#include +#include "core.h" +#include "dsp.h" + +const char *mISDN_dsp_revision = "2.0"; + +static int debug; +static int options; +static int poll; +static int dtmfthreshold = 100; + +MODULE_AUTHOR("Andreas Eversberg"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(options, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); +MODULE_LICENSE("GPL"); + +/*int spinnest = 0;*/ + +spinlock_t dsp_lock; /* global dsp lock */ +struct list_head dsp_ilist; +struct list_head conf_ilist; +int dsp_debug; +int dsp_options; +int dsp_poll, dsp_tics; + +/* check if rx may be turned off or must be turned on */ +static void +dsp_rx_off_member(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + int rx_off = 1; + + if (!dsp->features_rx_off) + return; + + /* not disabled */ + if (!dsp->rx_disabled) + rx_off = 0; + /* software dtmf */ + else if (dsp->dtmf.software) + rx_off = 0; + /* echo in software */ + else if (dsp->echo && dsp->pcm_slot_tx < 0) + rx_off = 0; + /* bridge in software */ + else if (dsp->conf) { + if (dsp->conf->software) + rx_off = 0; + } + + if (rx_off == dsp->rx_is_off) + return; + + if (!dsp->ch.peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no rx_off\n", + __func__); + return; + } + cq.op = MISDN_CTRL_RX_OFF; + cq.p1 = rx_off; + if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", + __func__); + return; + } + dsp->rx_is_off = rx_off; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: %s set rx_off = %d\n", + __func__, dsp->name, rx_off); +} +static void +dsp_rx_off(struct dsp *dsp) +{ + struct dsp_conf_member *member; + + if (dsp_options & DSP_OPT_NOHARDWARE) + return; + + /* no conf */ + if (!dsp->conf) { + dsp_rx_off_member(dsp); + return; + } + /* check all members in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + dsp_rx_off_member(member->dsp); + } +} + +static int +dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) +{ + struct sk_buff *nskb; + int ret = 0; + int cont; + u8 *data; + int len; + + if (skb->len < sizeof(int)) + printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); + cont = *((int *)skb->data); + len = skb->len - sizeof(int); + data = skb->data + sizeof(int); + + switch (cont) { + case DTMF_TONE_START: /* turn on DTMF */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: start dtmf\n", __func__); + if (len == sizeof(int)) { + printk(KERN_NOTICE "changing DTMF Threshold " + "to %d\n", *((int *)data)); + dsp->dtmf.treshold = (*(int *)data) * 10000; + } + /* init goertzel */ + dsp_dtmf_goertzel_init(dsp); + + /* check dtmf hardware */ + dsp_dtmf_hardware(dsp); + break; + case DTMF_TONE_STOP: /* turn off DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: stop dtmf\n", __func__); + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 0; + break; + case DSP_CONF_JOIN: /* join / update conference */ + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + if (*((u32 *)data) == 0) + goto conf_split; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: join conference %d\n", + __func__, *((u32 *)data)); + ret = dsp_cmx_conf(dsp, *((u32 *)data)); + /* dsp_cmx_hardware will also be called here */ + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_CONF_SPLIT: /* remove from conference */ +conf_split: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: release conference\n", __func__); + ret = dsp_cmx_conf(dsp, 0); + /* dsp_cmx_hardware will also be called here */ + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + dsp_rx_off(dsp); + break; + case DSP_TONE_PATT_ON: /* play tone */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone 0x%x on\n", + __func__, *((int *)skb->data)); + ret = dsp_tone(dsp, *((int *)data)); + if (!ret) { + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + } + if (!dsp->tone.tone) + goto tone_off; + break; + case DSP_TONE_PATT_OFF: /* stop tone */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone off\n", __func__); + dsp_tone(dsp, 0); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + /* reset tx buffers (user space data) */ +tone_off: + dsp->rx_W = 0; + dsp->rx_R = 0; + break; + case DSP_VOL_CHANGE_TX: /* change volume */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->tx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx vol to %d\n", + __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + case DSP_VOL_CHANGE_RX: /* change volume */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->rx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change rx vol to %d\n", + __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + case DSP_ECHO_ON: /* enable echo */ + dsp->echo = 1; /* soft echo */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_ECHO_OFF: /* disable echo */ + dsp->echo = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_RECEIVE_ON: /* enable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable receive to user " + "space\n", __func__); + dsp->rx_disabled = 0; + dsp_rx_off(dsp); + break; + case DSP_RECEIVE_OFF: /* disable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable receive to " + "user space\n", __func__); + dsp->rx_disabled = 1; + dsp_rx_off(dsp); + break; + case DSP_MIX_ON: /* enable mixing of tx data */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable mixing of " + "tx-data with conf mebers\n", __func__); + dsp->tx_mix = 1; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_MIX_OFF: /* disable mixing of tx data */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable mixing of " + "tx-data with conf mebers\n", __func__); + dsp->tx_mix = 0; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_TXDATA_ON: /* enable txdata */ + dsp->tx_data = 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable tx-data\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_TXDATA_OFF: /* disable txdata */ + dsp->tx_data = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable tx-data\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_DELAY: /* use delay algorithm instead of dynamic + jitter algorithm */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->cmx_delay = (*((int *)data)) << 3; + /* miliseconds to samples */ + if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1)) + /* clip to half of maximum usable buffer + (half of half buffer) */ + dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use delay algorithm to " + "compensate jitter (%d samples)\n", + __func__, dsp->cmx_delay); + break; + case DSP_JITTER: /* use dynamic jitter algorithm instead of + delay algorithm */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->cmx_delay = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use jitter algorithm to " + "compensate jitter\n", __func__); + break; + case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->tx_dejitter = 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use dejitter on TX " + "buffer\n", __func__); + break; + case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->tx_dejitter = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use TX buffer without " + "dejittering\n", __func__); + break; + case DSP_PIPELINE_CFG: + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len > 0 && ((char *)data)[len - 1]) { + printk(KERN_DEBUG "%s: pipeline config string " + "is not NULL terminated!\n", __func__); + ret = -EINVAL; + } else { + dsp->pipeline.inuse = 1; + dsp_cmx_hardware(dsp->conf, dsp); + ret = dsp_pipeline_build(&dsp->pipeline, + len > 0 ? (char *)data : NULL); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + } + break; + case DSP_BF_ENABLE_KEY: /* turn blowfish on */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < 4 || len > 56) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish on (key " + "not shown)\n", __func__); + ret = dsp_bf_init(dsp, (u8 *)data, len); + /* set new cont */ + if (!ret) + cont = DSP_BF_ACCEPT; + else + cont = DSP_BF_REJECT; + /* send indication if it worked to set it */ + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY, + sizeof(int), &cont, GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send(dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + if (!ret) { + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + } + break; + case DSP_BF_DISABLE: /* turn blowfish off */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish off\n", __func__); + dsp_bf_cleanup(dsp); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", + __func__, cont); + ret = -EINVAL; + } + return ret; +} + +static void +get_features(struct mISDNchannel *ch) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + struct mISDN_ctrl_req cq; + + if (dsp_options & DSP_OPT_NOHARDWARE) + return; + if (!ch->peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no features\n", + __func__); + return; + } + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_GETOP; + if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } + if (cq.op & MISDN_CTRL_RX_OFF) + dsp->features_rx_off = 1; + if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { + cq.op = MISDN_CTRL_HW_FEATURES; + *((u_long *)&cq.p1) = (u_long)&dsp->features; + if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", + __func__); + } + } else + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: features not supported for %s\n", + __func__, dsp->name); +} + +static int +dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + struct mISDNhead *hh; + int ret = 0; + u8 *digits; + int cont; + struct sk_buff *nskb; + u_long flags; + + hh = mISDN_HEAD_P(skb); + switch (hh->prim) { + /* FROM DOWN */ + case (PH_DATA_CNF): + dsp->data_pending = 0; + /* trigger next hdlc frame, if any */ + if (dsp->hdlc) { + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->b_active) + schedule_work(&dsp->workq); + spin_unlock_irqrestore(&dsp_lock, flags); + } + break; + case (PH_DATA_IND): + case (DL_DATA_IND): + if (skb->len < 1) { + ret = -EINVAL; + break; + } + if (dsp->rx_is_off) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: rx-data during rx_off" + " for %s\n", + __func__, dsp->name); + } + if (dsp->hdlc) { + /* hdlc */ + spin_lock_irqsave(&dsp_lock, flags); + dsp_cmx_hdlc(dsp, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + if (dsp->rx_disabled) { + /* if receive is not allowed */ + break; + } + hh->prim = DL_DATA_IND; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + } + + /* decrypt if enabled */ + if (dsp->bf_enable) + dsp_bf_decrypt(dsp, skb->data, skb->len); + /* pipeline */ + if (dsp->pipeline.inuse) + dsp_pipeline_process_rx(&dsp->pipeline, skb->data, + skb->len); + /* change volume if requested */ + if (dsp->rx_volume) + dsp_change_volume(skb, dsp->rx_volume); + + /* check if dtmf soft decoding is turned on */ + if (dsp->dtmf.software) { + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, + skb->len, (dsp_options&DSP_OPT_ULAW)?1:0); + while (*digits) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: digit" + "(%c) to layer %s\n", + __func__, *digits, dsp->name); + cont = DTMF_TONE_VAL | *digits; + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(int), &cont, + GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send( + dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + digits++; + } + } + /* we need to process receive data if software */ + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) { + /* process data from card at cmx */ + dsp_cmx_receive(dsp, skb); + } + spin_unlock_irqrestore(&dsp_lock, flags); + + if (dsp->rx_disabled) { + /* if receive is not allowed */ + break; + } + hh->prim = DL_DATA_IND; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + case (PH_CONTROL_IND): + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " + "received: %x (len %d) %s\n", __func__, + hh->id, skb->len, dsp->name); + switch (hh->id) { + case (DTMF_HFC_COEF): /* getting coefficients */ + if (!dsp->dtmf.hardware) { + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: ignoring DTMF " + "coefficients from HFC\n", + __func__); + break; + } + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, + skb->len, 2); + while (*digits) { + int k; + struct sk_buff *nskb; + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: digit" + "(%c) to layer %s\n", + __func__, *digits, dsp->name); + k = *digits | DTMF_TONE_VAL; + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(int), &k, + GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send( + dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + digits++; + } + break; + case (HFC_VOL_CHANGE_TX): /* change volume */ + if (skb->len != sizeof(int)) { + ret = -EINVAL; + break; + } + spin_lock_irqsave(&dsp_lock, flags); + dsp->tx_volume = *((int *)skb->data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx volume to " + "%d\n", __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl ind %x unhandled " + "%s\n", __func__, hh->id, dsp->name); + ret = -EINVAL; + } + break; + case (PH_ACTIVATE_IND): + case (PH_ACTIVATE_CNF): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now active %s\n", + __func__, dsp->name); + /* bchannel now active */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 1; + dsp->data_pending = 0; + dsp->rx_init = 1; + /* rx_W and rx_R will be adjusted on first frame */ + dsp->rx_W = 0; + dsp->rx_R = 0; + memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: done with activation, sending " + "confirm to user space. %s\n", __func__, + dsp->name); + /* send activation to upper layer */ + hh->prim = DL_ESTABLISH_CNF; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + case (PH_DEACTIVATE_IND): + case (PH_DEACTIVATE_CNF): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", + __func__, dsp->name); + /* bchannel now inactive */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 0; + dsp->data_pending = 0; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + hh->prim = DL_RELEASE_CNF; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + /* FROM UP */ + case (DL_DATA_REQ): + case (PH_DATA_REQ): + if (skb->len < 1) { + ret = -EINVAL; + break; + } + if (dsp->hdlc) { + /* hdlc */ + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->b_active) { + skb_queue_tail(&dsp->sendq, skb); + schedule_work(&dsp->workq); + } + spin_unlock_irqrestore(&dsp_lock, flags); + return 0; + } + /* send data to tx-buffer (if no tone is played) */ + if (!dsp->tone.tone) { + spin_lock_irqsave(&dsp_lock, flags); + dsp_cmx_transmit(dsp, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + } + break; + case (PH_CONTROL_REQ): + spin_lock_irqsave(&dsp_lock, flags); + ret = dsp_control_req(dsp, hh, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + break; + case (DL_ESTABLISH_REQ): + case (PH_ACTIVATE_REQ): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: activating b_channel %s\n", + __func__, dsp->name); + if (dsp->dtmf.hardware || dsp->dtmf.software) + dsp_dtmf_goertzel_init(dsp); + get_features(ch); + /* send ph_activate */ + hh->prim = PH_ACTIVATE_REQ; + if (ch->peer) + return ch->recv(ch->peer, skb); + break; + case (DL_RELEASE_REQ): + case (PH_DEACTIVATE_REQ): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: releasing b_channel %s\n", + __func__, dsp->name); + spin_lock_irqsave(&dsp_lock, flags); + dsp->tone.tone = 0; + dsp->tone.hardware = 0; + dsp->tone.software = 0; + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + if (dsp->conf) + dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be + called here */ + skb_queue_purge(&dsp->sendq); + spin_unlock_irqrestore(&dsp_lock, flags); + hh->prim = PH_DEACTIVATE_REQ; + if (ch->peer) + return ch->recv(ch->peer, skb); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", + __func__, hh->prim, dsp->name); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + u_long flags; + int err = 0; + + if (debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); + + switch (cmd) { + case OPEN_CHANNEL: + break; + case CLOSE_CHANNEL: + if (dsp->ch.peer) + dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL); + + /* wait until workqueue has finished, + * must lock here, or we may hit send-process currently + * queueing. */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 0; + spin_unlock_irqrestore(&dsp_lock, flags); + /* MUST not be locked, because it waits until queue is done. */ + cancel_work_sync(&dsp->workq); + spin_lock_irqsave(&dsp_lock, flags); + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + skb_queue_purge(&dsp->sendq); + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: releasing member %s\n", + __func__, dsp->name); + dsp->b_active = 0; + dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called + here */ + dsp_pipeline_destroy(&dsp->pipeline); + + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: remove & destroy object %s\n", + __func__, dsp->name); + list_del(&dsp->list); + spin_unlock_irqrestore(&dsp_lock, flags); + + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: dsp instance released\n", + __func__); + vfree(dsp); + module_put(THIS_MODULE); + break; + } + return err; +} + +static void +dsp_send_bh(struct work_struct *work) +{ + struct dsp *dsp = container_of(work, struct dsp, workq); + struct sk_buff *skb; + struct mISDNhead *hh; + + if (dsp->hdlc && dsp->data_pending) + return; /* wait until data has been acknowledged */ + + /* send queued data */ + while ((skb = skb_dequeue(&dsp->sendq))) { + /* in locked date, we must have still data in queue */ + if (dsp->data_pending) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: fifo full %s, this is " + "no bug!\n", __func__, dsp->name); + /* flush transparent data, if not acked */ + dev_kfree_skb(skb); + continue; + } + hh = mISDN_HEAD_P(skb); + if (hh->prim == DL_DATA_REQ) { + /* send packet up */ + if (dsp->up) { + if (dsp->up->send(dsp->up, skb)) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } else { + /* send packet down */ + if (dsp->ch.peer) { + dsp->data_pending = 1; + if (dsp->ch.recv(dsp->ch.peer, skb)) { + dev_kfree_skb(skb); + dsp->data_pending = 0; + } + } else + dev_kfree_skb(skb); + } + } +} + +static int +dspcreate(struct channel_req *crq) +{ + struct dsp *ndsp; + u_long flags; + + if (crq->protocol != ISDN_P_B_L2DSP + && crq->protocol != ISDN_P_B_L2DSPHDLC) + return -EPROTONOSUPPORT; + ndsp = vmalloc(sizeof(struct dsp)); + if (!ndsp) { + printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); + return -ENOMEM; + } + memset(ndsp, 0, sizeof(struct dsp)); + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); + + /* default enabled */ + INIT_WORK(&ndsp->workq, (void *)dsp_send_bh); + skb_queue_head_init(&ndsp->sendq); + ndsp->ch.send = dsp_function; + ndsp->ch.ctrl = dsp_ctrl; + ndsp->up = crq->ch; + crq->ch = &ndsp->ch; + if (crq->protocol == ISDN_P_B_L2DSP) { + crq->protocol = ISDN_P_B_RAW; + ndsp->hdlc = 0; + } else { + crq->protocol = ISDN_P_B_HDLC; + ndsp->hdlc = 1; + } + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", + __func__); + + sprintf(ndsp->name, "DSP_C%x(0x%p)", + ndsp->up->st->dev->id + 1, ndsp); + /* set frame size to start */ + ndsp->features.hfc_id = -1; /* current PCM id */ + ndsp->features.pcm_id = -1; /* current PCM id */ + ndsp->pcm_slot_rx = -1; /* current CPM slot */ + ndsp->pcm_slot_tx = -1; + ndsp->pcm_bank_rx = -1; + ndsp->pcm_bank_tx = -1; + ndsp->hfc_conf = -1; /* current conference number */ + /* set tone timer */ + ndsp->tone.tl.function = (void *)dsp_tone_timeout; + ndsp->tone.tl.data = (long) ndsp; + init_timer(&ndsp->tone.tl); + + if (dtmfthreshold < 20 || dtmfthreshold > 500) + dtmfthreshold = 200; + ndsp->dtmf.treshold = dtmfthreshold*10000; + + /* init pipeline append to list */ + spin_lock_irqsave(&dsp_lock, flags); + dsp_pipeline_init(&ndsp->pipeline); + list_add_tail(&ndsp->list, &dsp_ilist); + spin_unlock_irqrestore(&dsp_lock, flags); + + return 0; +} + + +static struct Bprotocol DSP = { + .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK)) + | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)), + .name = "dsp", + .create = dspcreate +}; + +static int dsp_init(void) +{ + int err; + int tics; + + printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision); + + dsp_options = options; + dsp_debug = debug; + + /* set packet size */ + dsp_poll = poll; + if (dsp_poll) { + if (dsp_poll > MAX_POLL) { + printk(KERN_ERR "%s: Wrong poll value (%d), use %d " + "maximum.\n", __func__, poll, MAX_POLL); + err = -EINVAL; + return err; + } + if (dsp_poll < 8) { + printk(KERN_ERR "%s: Wrong poll value (%d), use 8 " + "minimum.\n", __func__, dsp_poll); + err = -EINVAL; + return err; + } + dsp_tics = poll * HZ / 8000; + if (dsp_tics * 8000 != poll * HZ) { + printk(KERN_INFO "mISDN_dsp: Cannot clock every %d " + "samples (0,125 ms). It is not a multiple of " + "%d HZ.\n", poll, HZ); + err = -EINVAL; + return err; + } + } else { + poll = 8; + while (poll <= MAX_POLL) { + tics = poll * HZ / 8000; + if (tics * 8000 == poll * HZ) { + dsp_tics = tics; + dsp_poll = poll; + if (poll >= 64) + break; + } + poll++; + } + } + if (dsp_poll == 0) { + printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel " + "clock that equals exactly the duration of 8-256 " + "samples. (Choose kernel clock speed like 100, 250, " + "300, 1000)\n"); + err = -EINVAL; + return err; + } + printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals " + "%d jiffies.\n", dsp_poll, dsp_tics); + + spin_lock_init(&dsp_lock); + INIT_LIST_HEAD(&dsp_ilist); + INIT_LIST_HEAD(&conf_ilist); + + /* init conversion tables */ + dsp_audio_generate_law_tables(); + dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; + dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32: + dsp_audio_alaw_to_s32; + dsp_audio_generate_s2law_table(); + dsp_audio_generate_seven(); + dsp_audio_generate_mix_table(); + if (dsp_options & DSP_OPT_ULAW) + dsp_audio_generate_ulaw_samples(); + dsp_audio_generate_volume_changes(); + + err = dsp_pipeline_module_init(); + if (err) { + printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, " + "error(%d)\n", err); + return err; + } + + err = mISDN_register_Bprotocol(&DSP); + if (err) { + printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err); + return err; + } + + /* set sample timer */ + dsp_spl_tl.function = (void *)dsp_cmx_send; + dsp_spl_tl.data = 0; + init_timer(&dsp_spl_tl); + dsp_spl_tl.expires = jiffies + dsp_tics; + dsp_spl_jiffies = dsp_spl_tl.expires; + add_timer(&dsp_spl_tl); + + return 0; +} + + +static void dsp_cleanup(void) +{ + mISDN_unregister_Bprotocol(&DSP); + + if (timer_pending(&dsp_spl_tl)) + del_timer(&dsp_spl_tl); + + if (!list_empty(&dsp_ilist)) { + printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " + "empty.\n"); + } + if (!list_empty(&conf_ilist)) { + printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " + "all memory freed.\n"); + } + + dsp_pipeline_module_exit(); +} + +module_init(dsp_init); +module_exit(dsp_cleanup); + diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c new file mode 100644 index 000000000000..efc371c1f0dc --- /dev/null +++ b/drivers/isdn/mISDN/dsp_dtmf.c @@ -0,0 +1,303 @@ +/* + * DTMF decoder. + * + * Copyright by Andreas Eversberg (jolly@eversberg.eu) + * based on different decoders such as ISDN4Linux + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "core.h" +#include "dsp.h" + +#define NCOEFF 8 /* number of frequencies to be analyzed */ + +/* For DTMF recognition: + * 2 * cos(2 * PI * k / N) precalculated for all k + */ +static u64 cos2pik[NCOEFF] = +{ + /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ + 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 +}; + +/* digit matrix */ +static char dtmf_matrix[4][4] = +{ + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +/* dtmf detection using goertzel algorithm + * init function + */ +void dsp_dtmf_goertzel_init(struct dsp *dsp) +{ + dsp->dtmf.size = 0; + dsp->dtmf.lastwhat = '\0'; + dsp->dtmf.lastdigit = '\0'; + dsp->dtmf.count = 0; +} + +/* check for hardware or software features + */ +void dsp_dtmf_hardware(struct dsp *dsp) +{ + int hardware = 1; + + if (!dsp->features.hfc_dtmf) + hardware = 0; + + /* check for volume change */ + if (dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because tx_volume is changed\n", + __func__, dsp->name); + hardware = 0; + } + if (dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because rx_volume is changed\n", + __func__, dsp->name); + hardware = 0; + } + /* check if encryption is enabled */ + if (dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because encryption is enabled\n", + __func__, dsp->name); + hardware = 0; + } + /* check if pipeline exists */ + if (dsp->pipeline.inuse) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because pipeline exists.\n", + __func__, dsp->name); + hardware = 0; + } + + dsp->dtmf.hardware = hardware; + dsp->dtmf.software = !hardware; +} + + +/************************************************************* + * calculate the coefficients of the given sample and decode * + *************************************************************/ + +/* the given sample is decoded. if the sample is not long enough for a + * complete frame, the decoding is finished and continued with the next + * call of this function. + * + * the algorithm is very good for detection with a minimum of errors. i + * tested it allot. it even works with very short tones (40ms). the only + * disadvantage is, that it doesn't work good with different volumes of both + * tones. this will happen, if accoustically coupled dialers are used. + * it sometimes detects tones during speach, which is normal for decoders. + * use sequences to given commands during calls. + * + * dtmf - points to a structure of the current dtmf state + * spl and len - the sample + * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder + */ + +u8 +*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) +{ + u8 what; + int size; + signed short *buf; + s32 sk, sk1, sk2; + int k, n, i; + s32 *hfccoeff; + s32 result[NCOEFF], tresh, treshl; + int lowgroup, highgroup; + s64 cos2pik_; + + dsp->dtmf.digits[0] = '\0'; + + /* Note: The function will loop until the buffer has not enough samples + * left to decode a full frame. + */ +again: + /* convert samples */ + size = dsp->dtmf.size; + buf = dsp->dtmf.buffer; + switch (fmt) { + case 0: /* alaw */ + case 1: /* ulaw */ + while (size < DSP_DTMF_NPOINTS && len) { + buf[size++] = dsp_audio_law_to_s32[*data++]; + len--; + } + break; + + case 2: /* HFC coefficients */ + default: + if (len < 64) { + if (len > 0) + printk(KERN_ERR "%s: coefficients have invalid " + "size. (is=%d < must=%d)\n", + __func__, len, 64); + return dsp->dtmf.digits; + } + hfccoeff = (s32 *)data; + for (k = 0; k < NCOEFF; k++) { + sk2 = (*hfccoeff++)>>4; + sk = (*hfccoeff++)>>4; + if (sk > 32767 || sk < -32767 || sk2 > 32767 + || sk2 < -32767) + printk(KERN_WARNING + "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + data += 64; + len -= 64; + goto coefficients; + break; + } + dsp->dtmf.size = size; + + if (size < DSP_DTMF_NPOINTS) + return dsp->dtmf.digits; + + dsp->dtmf.size = 0; + + /* now we have a full buffer of signed long samples - we do goertzel */ + for (k = 0; k < NCOEFF; k++) { + sk = 0; + sk1 = 0; + sk2 = 0; + buf = dsp->dtmf.buffer; + cos2pik_ = cos2pik[k]; + for (n = 0; n < DSP_DTMF_NPOINTS; n++) { + sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++); + sk2 = sk1; + sk1 = sk; + } + sk >>= 8; + sk2 >>= 8; + if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) + printk(KERN_WARNING "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + + /* our (squared) coefficients have been calculated, we need to process + * them. + */ +coefficients: + tresh = 0; + for (i = 0; i < NCOEFF; i++) { + if (result[i] < 0) + result[i] = 0; + if (result[i] > dsp->dtmf.treshold) { + if (result[i] > tresh) + tresh = result[i]; + } + } + + if (tresh == 0) { + what = 0; + goto storedigit; + } + + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" + " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", + result[0]/10000, result[1]/10000, result[2]/10000, + result[3]/10000, result[4]/10000, result[5]/10000, + result[6]/10000, result[7]/10000, tresh/10000, + result[0]/(tresh/100), result[1]/(tresh/100), + result[2]/(tresh/100), result[3]/(tresh/100), + result[4]/(tresh/100), result[5]/(tresh/100), + result[6]/(tresh/100), result[7]/(tresh/100)); + + /* calc digit (lowgroup/highgroup) */ + lowgroup = -1; + highgroup = -1; + treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ + tresh = tresh >> 2; /* touchtones must match within 6 dB */ + for (i = 0; i < NCOEFF; i++) { + if (result[i] < treshl) + continue; /* ignore */ + if (result[i] < tresh) { + lowgroup = -1; + highgroup = -1; + break; /* noise inbetween */ + } + /* good level found. This is allowed only one time per group */ + if (i < NCOEFF/2) { + /* lowgroup */ + if (lowgroup >= 0) { + /* Bad. Another tone found. */ + lowgroup = -1; + break; + } else + lowgroup = i; + } else { + /* higroup */ + if (highgroup >= 0) { + /* Bad. Another tone found. */ + highgroup = -1; + break; + } else + highgroup = i-(NCOEFF/2); + } + } + + /* get digit or null */ + what = 0; + if (lowgroup >= 0 && highgroup >= 0) + what = dtmf_matrix[lowgroup][highgroup]; + +storedigit: + if (what && (dsp_debug & DEBUG_DSP_DTMF)) + printk(KERN_DEBUG "DTMF what: %c\n", what); + + if (dsp->dtmf.lastwhat != what) + dsp->dtmf.count = 0; + + /* the tone (or no tone) must remain 3 times without change */ + if (dsp->dtmf.count == 2) { + if (dsp->dtmf.lastdigit != what) { + dsp->dtmf.lastdigit = what; + if (what) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "DTMF digit: %c\n", + what); + if ((strlen(dsp->dtmf.digits)+1) + < sizeof(dsp->dtmf.digits)) { + dsp->dtmf.digits[strlen( + dsp->dtmf.digits)+1] = '\0'; + dsp->dtmf.digits[strlen( + dsp->dtmf.digits)] = what; + } + } + } + } else + dsp->dtmf.count++; + + dsp->dtmf.lastwhat = what; + + goto again; +} + + diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h new file mode 100644 index 000000000000..8a20af43308b --- /dev/null +++ b/drivers/isdn/mISDN/dsp_ecdis.h @@ -0,0 +1,110 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * ec_disable_detector.h - A detector which should eventually meet the + * G.164/G.165 requirements for detecting the + * 2100Hz echo cancellor disable tone. + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + * 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. + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "dsp_biquad.h" + +struct ec_disable_detector_state { + struct biquad2_state notch; + int notch_level; + int channel_level; + int tone_present; + int tone_cycle_duration; + int good_cycles; + int hit; +}; + + +#define FALSE 0 +#define TRUE (!FALSE) + +static inline void +echo_can_disable_detector_init(struct ec_disable_detector_state *det) +{ + /* Elliptic notch */ + /* This is actually centred at 2095Hz, but gets the balance we want, due + to the asymmetric walls of the notch */ + biquad2_init(&det->notch, + (int32_t) (-0.7600000*32768.0), + (int32_t) (-0.1183852*32768.0), + (int32_t) (-0.5104039*32768.0), + (int32_t) (0.1567596*32768.0), + (int32_t) (1.0000000*32768.0)); + + det->channel_level = 0; + det->notch_level = 0; + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + det->hit = 0; +} +/*- End of function --------------------------------------------------------*/ + +static inline int +echo_can_disable_detector_update(struct ec_disable_detector_state *det, +int16_t amp) +{ + int16_t notched; + + notched = biquad2(&det->notch, amp); + /* Estimate the overall energy in the channel, and the energy in + the notch (i.e. overall channel energy - tone energy => noise). + Use abs instead of multiply for speed (is it really faster?). + Damp the overall energy a little more for a stable result. + Damp the notch energy a little less, so we don't damp out the + blip every time the phase reverses */ + det->channel_level += ((abs(amp) - det->channel_level) >> 5); + det->notch_level += ((abs(notched) - det->notch_level) >> 4); + if (det->channel_level > 280) { + /* There is adequate energy in the channel. + Is it mostly at 2100Hz? */ + if (det->notch_level*6 < det->channel_level) { + /* The notch says yes, so we have the tone. */ + if (!det->tone_present) { + /* Do we get a kick every 450+-25ms? */ + if (det->tone_cycle_duration >= 425*8 + && det->tone_cycle_duration <= 475*8) { + det->good_cycles++; + if (det->good_cycles > 2) + det->hit = TRUE; + } + det->tone_cycle_duration = 0; + } + det->tone_present = TRUE; + } else + det->tone_present = FALSE; + det->tone_cycle_duration++; + } else { + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + } + return det->hit; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c new file mode 100644 index 000000000000..eb892d9dd5c6 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.c @@ -0,0 +1,138 @@ +/* + * dsp_hwec.c: + * builtin mISDN dsp pipeline element for enabling the hw echocanceller + * + * Copyright (C) 2007, Nadi Sarrar + * + * Nadi Sarrar + * + * 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. + * + * 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, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +#include +#include +#include +#include +#include "core.h" +#include "dsp.h" +#include "dsp_hwec.h" + +static struct mISDN_dsp_element_arg args[] = { + { "deftaps", "128", "Set the number of taps of cancellation." }, +}; + +static struct mISDN_dsp_element dsp_hwec_p = { + .name = "hwec", + .new = NULL, + .free = NULL, + .process_tx = NULL, + .process_rx = NULL, + .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg), + .args = args, +}; +struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p; + +void dsp_hwec_enable(struct dsp *dsp, const char *arg) +{ + int deftaps = 128, + len; + struct mISDN_ctrl_req cq; + + if (!dsp) { + printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n", + __func__); + return; + } + + if (!arg) + goto _do; + + len = strlen(arg); + if (!len) + goto _do; + + { + char _dup[len + 1]; + char *dup, *tok, *name, *val; + int tmp; + + strcpy(_dup, arg); + dup = _dup; + + while ((tok = strsep(&dup, ","))) { + if (!strlen(tok)) + continue; + name = strsep(&tok, "="); + val = tok; + + if (!val) + continue; + + if (!strcmp(name, "deftaps")) { + if (sscanf(val, "%d", &tmp) == 1) + deftaps = tmp; + } + } + } + +_do: + printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n", + __func__, deftaps); + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_HFC_ECHOCAN_ON; + cq.p1 = deftaps; + if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } +} + +void dsp_hwec_disable(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + + if (!dsp) { + printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n", + __func__); + return; + } + + printk(KERN_DEBUG "%s: disabling hwec\n", __func__); + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF; + if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } +} + +int dsp_hwec_init(void) +{ + mISDN_dsp_element_register(dsp_hwec); + + return 0; +} + +void dsp_hwec_exit(void) +{ + mISDN_dsp_element_unregister(dsp_hwec); +} + diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h new file mode 100644 index 000000000000..eebe80c3f713 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.h @@ -0,0 +1,10 @@ +/* + * dsp_hwec.h + */ + +extern struct mISDN_dsp_element *dsp_hwec; +extern void dsp_hwec_enable(struct dsp *dsp, const char *arg); +extern void dsp_hwec_disable(struct dsp *dsp); +extern int dsp_hwec_init(void); +extern void dsp_hwec_exit(void); + diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c new file mode 100644 index 000000000000..850260ab57d0 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -0,0 +1,348 @@ +/* + * dsp_pipeline.c: pipelined audio processing + * + * Copyright (C) 2007, Nadi Sarrar + * + * Nadi Sarrar + * + * 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. + * + * 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, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +#include +#include +#include +#include +#include +#include "dsp.h" +#include "dsp_hwec.h" + +/* uncomment for debugging */ +/*#define PIPELINE_DEBUG*/ + +struct dsp_pipeline_entry { + struct mISDN_dsp_element *elem; + void *p; + struct list_head list; +}; +struct dsp_element_entry { + struct mISDN_dsp_element *elem; + struct device dev; + struct list_head list; +}; + +static LIST_HEAD(dsp_elements); + +/* sysfs */ +static struct class *elements_class; + +static ssize_t +attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mISDN_dsp_element *elem = dev_get_drvdata(dev); + ssize_t len = 0; + int i = 0; + + *buf = 0; + for (; i < elem->num_args; ++i) + len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n" + "\n", buf, + elem->args[i].name, + elem->args[i].def ? "Default: " : "", + elem->args[i].def ? elem->args[i].def : "", + elem->args[i].def ? "\n" : "", + elem->args[i].desc); + + return len; +} + +static struct device_attribute element_attributes[] = { + __ATTR(args, 0444, attr_show_args, NULL), +}; + +int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) +{ + struct dsp_element_entry *entry; + int ret, i; + + if (!elem) + return -EINVAL; + + entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->elem = elem; + + entry->dev.class = elements_class; + dev_set_drvdata(&entry->dev, elem); + snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name); + ret = device_register(&entry->dev); + if (ret) { + printk(KERN_ERR "%s: failed to register %s\n", + __func__, elem->name); + goto err1; + } + + for (i = 0; i < (sizeof(element_attributes) + / sizeof(struct device_attribute)); ++i) + ret = device_create_file(&entry->dev, + &element_attributes[i]); + if (ret) { + printk(KERN_ERR "%s: failed to create device file\n", + __func__); + goto err2; + } + + list_add_tail(&entry->list, &dsp_elements); + + printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); + + return 0; + +err2: + device_unregister(&entry->dev); +err1: + kfree(entry); + return ret; +} +EXPORT_SYMBOL(mISDN_dsp_element_register); + +void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) +{ + struct dsp_element_entry *entry, *n; + + if (!elem) + return; + + list_for_each_entry_safe(entry, n, &dsp_elements, list) + if (entry->elem == elem) { + list_del(&entry->list); + device_unregister(&entry->dev); + kfree(entry); + printk(KERN_DEBUG "%s: %s unregistered\n", + __func__, elem->name); + return; + } + printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); +} +EXPORT_SYMBOL(mISDN_dsp_element_unregister); + +int dsp_pipeline_module_init(void) +{ + elements_class = class_create(THIS_MODULE, "dsp_pipeline"); + if (IS_ERR(elements_class)) + return PTR_ERR(elements_class); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__); +#endif + + dsp_hwec_init(); + + return 0; +} + +void dsp_pipeline_module_exit(void) +{ + struct dsp_element_entry *entry, *n; + + dsp_hwec_exit(); + + class_destroy(elements_class); + + list_for_each_entry_safe(entry, n, &dsp_elements, list) { + list_del(&entry->list); + printk(KERN_WARNING "%s: element was still registered: %s\n", + __func__, entry->elem->name); + kfree(entry); + } + + printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); +} + +int dsp_pipeline_init(struct dsp_pipeline *pipeline) +{ + if (!pipeline) + return -EINVAL; + + INIT_LIST_HEAD(&pipeline->list); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__); +#endif + + return 0; +} + +static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) +{ + struct dsp_pipeline_entry *entry, *n; + + list_for_each_entry_safe(entry, n, &pipeline->list, list) { + list_del(&entry->list); + if (entry->elem == dsp_hwec) + dsp_hwec_disable(container_of(pipeline, struct dsp, + pipeline)); + else + entry->elem->free(entry->p); + kfree(entry); + } +} + +void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) +{ + + if (!pipeline) + return; + + _dsp_pipeline_destroy(pipeline); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__); +#endif +} + +int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) +{ + int len, incomplete = 0, found = 0; + char *dup, *tok, *name, *args; + struct dsp_element_entry *entry, *n; + struct dsp_pipeline_entry *pipeline_entry; + struct mISDN_dsp_element *elem; + + if (!pipeline) + return -EINVAL; + + if (!list_empty(&pipeline->list)) + _dsp_pipeline_destroy(pipeline); + + if (!cfg) + return 0; + + len = strlen(cfg); + if (!len) + return 0; + + dup = kmalloc(len + 1, GFP_KERNEL); + if (!dup) + return 0; + strcpy(dup, cfg); + while ((tok = strsep(&dup, "|"))) { + if (!strlen(tok)) + continue; + name = strsep(&tok, "("); + args = strsep(&tok, ")"); + if (args && !*args) + args = 0; + + list_for_each_entry_safe(entry, n, &dsp_elements, list) + if (!strcmp(entry->elem->name, name)) { + elem = entry->elem; + + pipeline_entry = kmalloc(sizeof(struct + dsp_pipeline_entry), GFP_KERNEL); + if (!pipeline_entry) { + printk(KERN_DEBUG "%s: failed to add " + "entry to pipeline: %s (out of " + "memory)\n", __func__, elem->name); + incomplete = 1; + goto _out; + } + pipeline_entry->elem = elem; + + if (elem == dsp_hwec) { + /* This is a hack to make the hwec + available as a pipeline module */ + dsp_hwec_enable(container_of(pipeline, + struct dsp, pipeline), args); + list_add_tail(&pipeline_entry->list, + &pipeline->list); + } else { + pipeline_entry->p = elem->new(args); + if (pipeline_entry->p) { + list_add_tail(&pipeline_entry-> + list, &pipeline->list); +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: created " + "instance of %s%s%s\n", + __func__, name, args ? + " with args " : "", args ? + args : ""); +#endif + } else { + printk(KERN_DEBUG "%s: failed " + "to add entry to pipeline: " + "%s (new() returned NULL)\n", + __func__, elem->name); + kfree(pipeline_entry); + incomplete = 1; + } + } + found = 1; + break; + } + + if (found) + found = 0; + else { + printk(KERN_DEBUG "%s: element not found, skipping: " + "%s\n", __func__, name); + incomplete = 1; + } + } + +_out: + if (!list_empty(&pipeline->list)) + pipeline->inuse = 1; + else + pipeline->inuse = 0; + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", + __func__, incomplete ? " incomplete" : "", cfg); +#endif + kfree(dup); + return 0; +} + +void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) +{ + struct dsp_pipeline_entry *entry; + + if (!pipeline) + return; + + list_for_each_entry(entry, &pipeline->list, list) + if (entry->elem->process_tx) + entry->elem->process_tx(entry->p, data, len); +} + +void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len) +{ + struct dsp_pipeline_entry *entry; + + if (!pipeline) + return; + + list_for_each_entry_reverse(entry, &pipeline->list, list) + if (entry->elem->process_rx) + entry->elem->process_rx(entry->p, data, len); +} + + diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c new file mode 100644 index 000000000000..23dd0dd21524 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_tones.c @@ -0,0 +1,551 @@ +/* + * Audio support data for ISDN4Linux. + * + * Copyright Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "core.h" +#include "dsp.h" + + +#define DATA_S sample_silence +#define SIZE_S (&sizeof_silence) +#define DATA_GA sample_german_all +#define SIZE_GA (&sizeof_german_all) +#define DATA_GO sample_german_old +#define SIZE_GO (&sizeof_german_old) +#define DATA_DT sample_american_dialtone +#define SIZE_DT (&sizeof_american_dialtone) +#define DATA_RI sample_american_ringing +#define SIZE_RI (&sizeof_american_ringing) +#define DATA_BU sample_american_busy +#define SIZE_BU (&sizeof_american_busy) +#define DATA_S1 sample_special1 +#define SIZE_S1 (&sizeof_special1) +#define DATA_S2 sample_special2 +#define SIZE_S2 (&sizeof_special2) +#define DATA_S3 sample_special3 +#define SIZE_S3 (&sizeof_special3) + +/***************/ +/* tones loops */ +/***************/ + +/* all tones are alaw encoded */ +/* the last sample+1 is in phase with the first sample. the error is low */ + +static u8 sample_german_all[] = { + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, +}; +static u32 sizeof_german_all = sizeof(sample_german_all); + +static u8 sample_german_old[] = { + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, +}; +static u32 sizeof_german_old = sizeof(sample_german_old); + +static u8 sample_american_dialtone[] = { + 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, + 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, + 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, + 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, + 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, + 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, + 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, + 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, + 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, + 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, + 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, + 0x6d, 0x91, 0x19, +}; +static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); + +static u8 sample_american_ringing[] = { + 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, + 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, + 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, + 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, + 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, + 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, + 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, + 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, + 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, + 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, + 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, + 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, + 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, + 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, + 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, + 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, + 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, + 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, + 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, + 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, + 0x4d, 0xbd, 0x0d, 0xad, 0xe1, +}; +static u32 sizeof_american_ringing = sizeof(sample_american_ringing); + +static u8 sample_american_busy[] = { + 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, + 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, + 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, + 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, + 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, + 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, + 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, + 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, + 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, + 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, + 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, + 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, + 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, + 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, + 0x4d, 0x4d, 0x6d, 0x01, +}; +static u32 sizeof_american_busy = sizeof(sample_american_busy); + +static u8 sample_special1[] = { + 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, + 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, + 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, + 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, + 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, + 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, + 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, + 0x6d, 0xbd, 0x2d, +}; +static u32 sizeof_special1 = sizeof(sample_special1); + +static u8 sample_special2[] = { + 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, + 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, + 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, + 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, + 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, + 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, + 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, + 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, + 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, + 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, +}; +static u32 sizeof_special2 = sizeof(sample_special2); + +static u8 sample_special3[] = { + 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, + 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, + 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, + 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, + 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, + 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, + 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, + 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, + 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, + 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, +}; +static u32 sizeof_special3 = sizeof(sample_special3); + +static u8 sample_silence[] = { + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, +}; +static u32 sizeof_silence = sizeof(sample_silence); + +struct tones_samples { + u32 *len; + u8 *data; +}; +static struct +tones_samples samples[] = { + {&sizeof_german_all, sample_german_all}, + {&sizeof_german_old, sample_german_old}, + {&sizeof_american_dialtone, sample_american_dialtone}, + {&sizeof_american_ringing, sample_american_ringing}, + {&sizeof_american_busy, sample_american_busy}, + {&sizeof_special1, sample_special1}, + {&sizeof_special2, sample_special2}, + {&sizeof_special3, sample_special3}, + {NULL, NULL}, +}; + +/*********************************** + * generate ulaw from alaw samples * + ***********************************/ + +void +dsp_audio_generate_ulaw_samples(void) +{ + int i, j; + + i = 0; + while (samples[i].len) { + j = 0; + while (j < (*samples[i].len)) { + samples[i].data[j] = + dsp_audio_alaw_to_ulaw[samples[i].data[j]]; + j++; + } + i++; + } +} + + +/**************************** + * tone sequence definition * + ****************************/ + +struct pattern { + int tone; + u8 *data[10]; + u32 *siz[10]; + u32 seq[10]; +} pattern[] = { + {TONE_GERMAN_DIALTONE, + {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDDIALTONE, + {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_DIALTONE, + {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_DIALPBX, + {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDDIALPBX, + {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_AMERICAN_DIALPBX, + {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0}, + {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_GERMAN_RINGING, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDRINGING, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_RINGING, + {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_RINGPBX, + {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDRINGPBX, + {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_RINGPBX, + {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_BUSY, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDBUSY, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_BUSY, + {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_HANGUP, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDHANGUP, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_HANGUP, + {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_SPECIAL_INFO, + {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0}, + {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_GASSENBESETZT, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_AUFSCHALTTON, + {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, + + {0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +/****************** + * copy tone data * + ******************/ + +/* an sk_buff is generated from the number of samples needed. + * the count will be changed and may begin from 0 each pattern period. + * the clue is to precalculate the pointers and legths to use only one + * memcpy per function call, or two memcpy if the tone sequence changes. + * + * pattern - the type of the pattern + * count - the sample from the beginning of the pattern (phase) + * len - the number of bytes + * + * return - the sk_buff with the sample + * + * if tones has finished (e.g. knocking tone), dsp->tones is turned off + */ +void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) +{ + int index, count, start, num; + struct pattern *pat; + struct dsp_tone *tone = &dsp->tone; + + /* if we have no tone, we copy silence */ + if (!tone->tone) { + memset(data, dsp_silence, len); + return; + } + + /* process pattern */ + pat = (struct pattern *)tone->pattern; + /* points to the current pattern */ + index = tone->index; /* gives current sequence index */ + count = tone->count; /* gives current sample */ + + /* copy sample */ + while (len) { + /* find sample to start with */ + while (42) { + /* warp arround */ + if (!pat->seq[index]) { + count = 0; + index = 0; + } + /* check if we are currently playing this tone */ + if (count < pat->seq[index]) + break; + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: reaching next sequence " + "(index=%d)\n", __func__, index); + count -= pat->seq[index]; + index++; + } + /* calculate start and number of samples */ + start = count % (*(pat->siz[index])); + num = len; + if (num+count > pat->seq[index]) + num = pat->seq[index] - count; + if (num+start > (*(pat->siz[index]))) + num = (*(pat->siz[index])) - start; + /* copy memory */ + memcpy(data, pat->data[index]+start, num); + /* reduce length */ + data += num; + count += num; + len -= num; + } + tone->index = index; + tone->count = count; + + /* return sk_buff */ + return; +} + + +/******************************* + * send HW message to hfc card * + *******************************/ + +static void +dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) +{ + struct sk_buff *nskb; + + /* unlocking is not required, because we don't expect a response */ + nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, + (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample, + GFP_ATOMIC); + if (nskb) { + if (dsp->ch.peer) { + if (dsp->ch.recv(dsp->ch.peer, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } +} + + +/***************** + * timer expires * + *****************/ +void +dsp_tone_timeout(void *arg) +{ + struct dsp *dsp = arg; + struct dsp_tone *tone = &dsp->tone; + struct pattern *pat = (struct pattern *)tone->pattern; + int index = tone->index; + + if (!tone->tone) + return; + + index++; + if (!pat->seq[index]) + index = 0; + tone->index = index; + + /* set next tone */ + if (pat->data[index] == DATA_S) + dsp_tone_hw_message(dsp, 0, 0); + else + dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); + /* set timer */ + init_timer(&tone->tl); + tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; + add_timer(&tone->tl); +} + + +/******************** + * set/release tone * + ********************/ + +/* + * tones are relaized by streaming or by special loop commands if supported + * by hardware. when hardware is used, the patterns will be controlled by + * timers. + */ +int +dsp_tone(struct dsp *dsp, int tone) +{ + struct pattern *pat; + int i; + struct dsp_tone *tonet = &dsp->tone; + + tonet->software = 0; + tonet->hardware = 0; + + /* we turn off the tone */ + if (!tone) { + if (dsp->features.hfc_loops) + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + if (dsp->features.hfc_loops) + dsp_tone_hw_message(dsp, NULL, 0); + tonet->tone = 0; + return 0; + } + + pat = NULL; + i = 0; + while (pattern[i].tone) { + if (pattern[i].tone == tone) { + pat = &pattern[i]; + break; + } + i++; + } + if (!pat) { + printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); + return -EINVAL; + } + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", + __func__, tone, 0); + tonet->tone = tone; + tonet->pattern = pat; + tonet->index = 0; + tonet->count = 0; + + if (dsp->features.hfc_loops) { + tonet->hardware = 1; + /* set first tone */ + dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); + /* set timer */ + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + init_timer(&tonet->tl); + tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; + add_timer(&tonet->tl); + } else { + tonet->software = 1; + } + + return 0; +} + + + + + diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h new file mode 100644 index 000000000000..6b71d2dce508 --- /dev/null +++ b/include/linux/mISDNdsp.h @@ -0,0 +1,37 @@ +#ifndef __mISDNdsp_H__ +#define __mISDNdsp_H__ + +struct mISDN_dsp_element_arg { + char *name; + char *def; + char *desc; +}; + +struct mISDN_dsp_element { + char *name; + void *(*new)(const char *arg); + void (*free)(void *p); + void (*process_tx)(void *p, unsigned char *data, int len); + void (*process_rx)(void *p, unsigned char *data, int len); + int num_args; + struct mISDN_dsp_element_arg + *args; +}; + +extern int mISDN_dsp_element_register(struct mISDN_dsp_element *elem); +extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem); + +struct dsp_features { + int hfc_id; /* unique id to identify the chip (or -1) */ + int hfc_dtmf; /* set if HFCmulti card supports dtmf */ + int hfc_loops; /* set if card supports tone loops */ + int hfc_echocanhw; /* set if card supports echocancelation*/ + int pcm_id; /* unique id to identify the pcm bus (or -1) */ + int pcm_slots; /* number of slots on the pcm bus */ + int pcm_banks; /* number of IO banks of pcm bus */ + int unclocked; /* data is not clocked (has jitter/loss) */ + int unordered; /* data is unordered (packets have index) */ +}; + +#endif + -- cgit v1.2.3 From af69fb3a8ffa37e986db00ed93099dc44babeef4 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sun, 27 Jul 2008 02:00:43 +0200 Subject: Add mISDN HFC multiport driver Enable support for cards with Cologne Chip AG's HFC multiport chip. Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/Kconfig | 12 + drivers/isdn/hardware/mISDN/Makefile | 1 + drivers/isdn/hardware/mISDN/hfc_multi.h | 1204 +++++++ drivers/isdn/hardware/mISDN/hfcmulti.c | 5320 +++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 33 + 5 files changed, 6570 insertions(+) create mode 100644 drivers/isdn/hardware/mISDN/hfc_multi.h create mode 100644 drivers/isdn/hardware/mISDN/hfcmulti.c (limited to 'include/linux') diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index f62dc8752be9..14793480c453 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -11,3 +11,15 @@ config MISDN_HFCPCI Enable support for cards with Cologne Chip AG's HFC PCI chip. +config MISDN_HFCMULTI + tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" + depends on PCI + depends on MISDN + help + Enable support for cards with Cologne Chip AG's HFC multiport + chip. There are three types of chips that are quite similar, + but the interface is different: + * HFC-4S (4 S/T interfaces on one chip) + * HFC-8S (8 S/T interfaces on one chip) + * HFC-E1 (E1 interface for 2Mbit ISDN) + diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile index 6f20a40b9d54..1e7ca5332ad7 100644 --- a/drivers/isdn/hardware/mISDN/Makefile +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o +obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h new file mode 100644 index 000000000000..a33d87afc843 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -0,0 +1,1204 @@ +/* + * see notice in hfc_multi.c + */ + +extern void ztdummy_extern_interrupt(void); +extern void ztdummy_register_interrupt(void); +extern int ztdummy_unregister_interrupt(void); + +#define DEBUG_HFCMULTI_FIFO 0x00010000 +#define DEBUG_HFCMULTI_CRC 0x00020000 +#define DEBUG_HFCMULTI_INIT 0x00040000 +#define DEBUG_HFCMULTI_PLXSD 0x00080000 +#define DEBUG_HFCMULTI_MODE 0x00100000 +#define DEBUG_HFCMULTI_MSG 0x00200000 +#define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_SYNC 0x01000000 +#define DEBUG_HFCMULTI_DTMF 0x02000000 +#define DEBUG_HFCMULTI_LOCK 0x80000000 + +#define PCI_ENA_REGIO 0x01 +#define PCI_ENA_MEMIO 0x02 + +/* + * NOTE: some registers are assigned multiple times due to different modes + * also registers are assigned differen for HFC-4s/8s and HFC-E1 + */ + +/* +#define MAX_FRAME_SIZE 2048 +*/ + +struct hfc_chan { + struct dchannel *dch; /* link if channel is a D-channel */ + struct bchannel *bch; /* link if channel is a B-channel */ + int port; /* the interface port this */ + /* channel is associated with */ + int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ + int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ + int jitter; + u_long cfg; /* port configuration */ + int sync; /* sync state (used by E1) */ + u_int protocol; /* current protocol */ + int slot_tx; /* current pcm slot */ + int bank_tx; /* current pcm bank */ + int slot_rx; + int bank_rx; + int conf; /* conference setting of TX slot */ + int txpending; /* if there is currently data in */ + /* the FIFO 0=no, 1=yes, 2=splloop */ + int rx_off; /* set to turn fifo receive off */ + int coeff_count; /* curren coeff block */ + s32 *coeff; /* memory pointer to 8 coeff blocks */ +}; + + +struct hfcm_hw { + u_char r_ctrl; + u_char r_irq_ctrl; + u_char r_cirm; + u_char r_ram_sz; + u_char r_pcm_md0; + u_char r_irqmsk_misc; + u_char r_dtmf; + u_char r_st_sync; + u_char r_sci_msk; + u_char r_tx0, r_tx1; + u_char a_st_ctrl0[8]; + timer_t timer; +}; + + +/* for each stack these flags are used (cfg) */ +#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ +#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ +#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ +#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ +#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ +#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ +#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ +#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ +#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ +#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ + /* use double frame instead. */ + +#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ +#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ +#define HFC_CHIP_REVISION0 2 /* old fifo handling */ +#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ +#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ +#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ +#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ +#define HFC_CHIP_ULAW 7 /* ULAW mode */ +#define HFC_CHIP_CLOCK2 8 /* double clock mode */ +#define HFC_CHIP_E1CLOCK_GET 9 /* always get clock from E1 interface */ +#define HFC_CHIP_E1CLOCK_PUT 10 /* always put clock from E1 interface */ +#define HFC_CHIP_WATCHDOG 11 /* whether we should send signals */ + /* to the watchdog */ +#define HFC_CHIP_B410P 12 /* whether we have a b410p with echocan in */ + /* hw */ +#define HFC_CHIP_PLXSD 13 /* whether we have a Speech-Design PLX */ + +#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ +#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ +#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ + +/* table entry in the PCI devices list */ +struct hm_map { + char *vendor_name; + char *card_name; + int type; + int ports; + int clock2; + int leds; + int opticalsupport; + int dip_type; + int io_mode; +}; + +struct hfc_multi { + struct list_head list; + struct hm_map *mtyp; + int id; + int pcm; /* id of pcm bus */ + int type; + int ports; + + u_int irq; /* irq used by card */ + u_int irqcnt; + struct pci_dev *pci_dev; + int io_mode; /* selects mode */ +#ifdef HFC_REGISTER_DEBUG + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + void (*HFC_wait)(struct hfc_multi *hc, + const char *function, int line); + void (*HFC_wait_nodebug)(struct hfc_multi *hc, + const char *function, int line); +#else + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); + void (*HFC_wait)(struct hfc_multi *hc); + void (*HFC_wait_nodebug)(struct hfc_multi *hc); +#endif + void (*read_fifo)(struct hfc_multi *hc, u_char *data, + int len); + void (*write_fifo)(struct hfc_multi *hc, u_char *data, + int len); + u_long pci_origmembase, plx_origmembase, dsp_origmembase; + u_char *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */ + u_char *plx_membase; /* PLX memory */ + u_char *dsp_membase; /* DSP on PLX */ + u_long pci_iobase; /* PCI IO */ + struct hfcm_hw hw; /* remember data of write-only-registers */ + + u_long chip; /* chip configuration */ + int masterclk; /* port that provides master clock -1=off */ + int dtmf; /* flag that dtmf is currently in process */ + int Flen; /* F-buffer size */ + int Zlen; /* Z-buffer size (must be int for calculation)*/ + int max_trans; /* maximum transparent fifo fill */ + int Zmin; /* Z-buffer offset */ + int DTMFbase; /* base address of DTMF coefficients */ + + u_int slots; /* number of PCM slots */ + u_int leds; /* type of leds */ + u_int ledcount; /* used to animate leds */ + u_long ledstate; /* save last state of leds */ + int opticalsupport; /* has the e1 board */ + /* an optical Interface */ + int dslot; /* channel # of d-channel (E1) default 16 */ + + u_long wdcount; /* every 500 ms we need to */ + /* send the watchdog a signal */ + u_char wdbyte; /* watchdog toggle byte */ + u_int activity[8]; /* if there is any action on this */ + /* port (will be cleared after */ + /* showing led-states) */ + int e1_state; /* keep track of last state */ + int e1_getclock; /* if sync is retrieved from interface */ + int syncronized; /* keep track of existing sync interface */ + int e1_resync; /* resync jobs */ + + spinlock_t lock; /* the lock */ + + /* + * the channel index is counted from 0, regardless where the channel + * is located on the hfc-channel. + * the bch->channel is equvalent to the hfc-channel + */ + struct hfc_chan chan[32]; + u_char created[8]; /* what port is created */ + signed char slot_owner[256]; /* owner channel of slot */ +}; + +/* PLX GPIOs */ +#define PLX_GPIO4_DIR_BIT 13 +#define PLX_GPIO4_BIT 14 +#define PLX_GPIO5_DIR_BIT 16 +#define PLX_GPIO5_BIT 17 +#define PLX_GPIO6_DIR_BIT 19 +#define PLX_GPIO6_BIT 20 +#define PLX_GPIO7_DIR_BIT 22 +#define PLX_GPIO7_BIT 23 +#define PLX_GPIO8_DIR_BIT 25 +#define PLX_GPIO8_BIT 26 + +#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) +#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) +#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) +#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) +#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) + +#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) +#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) +#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) +#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) +#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) + +#define PLX_TERM_ON PLX_GPIO7 +#define PLX_SLAVE_EN_N PLX_GPIO5 +#define PLX_MASTER_EN PLX_GPIO6 +#define PLX_SYNC_O_EN PLX_GPIO4 +#define PLX_DSP_RES_N PLX_GPIO8 +/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ +#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ + | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) + +/* PLX Interrupt Control/STATUS */ +#define PLX_INTCSR_LINTI1_ENABLE 0x01 +#define PLX_INTCSR_LINTI1_STATUS 0x04 +#define PLX_INTCSR_LINTI2_ENABLE 0x08 +#define PLX_INTCSR_LINTI2_STATUS 0x20 +#define PLX_INTCSR_PCIINT_ENABLE 0x40 + +/* PLX Registers */ +#define PLX_INTCSR 0x4c +#define PLX_CNTRL 0x50 +#define PLX_GPIOC 0x54 + + +/* + * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* write only registers */ +#define R_CIRM 0x00 +#define R_CTRL 0x01 +#define R_BRG_PCM_CFG 0x02 +#define R_RAM_ADDR0 0x08 +#define R_RAM_ADDR1 0x09 +#define R_RAM_ADDR2 0x0A +#define R_FIRST_FIFO 0x0B +#define R_RAM_SZ 0x0C +#define R_FIFO_MD 0x0D +#define R_INC_RES_FIFO 0x0E +#define R_FSM_IDX 0x0F +#define R_FIFO 0x0F +#define R_SLOT 0x10 +#define R_IRQMSK_MISC 0x11 +#define R_SCI_MSK 0x12 +#define R_IRQ_CTRL 0x13 +#define R_PCM_MD0 0x14 +#define R_PCM_MD1 0x15 +#define R_PCM_MD2 0x15 +#define R_SH0H 0x15 +#define R_SH1H 0x15 +#define R_SH0L 0x15 +#define R_SH1L 0x15 +#define R_SL_SEL0 0x15 +#define R_SL_SEL1 0x15 +#define R_SL_SEL2 0x15 +#define R_SL_SEL3 0x15 +#define R_SL_SEL4 0x15 +#define R_SL_SEL5 0x15 +#define R_SL_SEL6 0x15 +#define R_SL_SEL7 0x15 +#define R_ST_SEL 0x16 +#define R_ST_SYNC 0x17 +#define R_CONF_EN 0x18 +#define R_TI_WD 0x1A +#define R_BERT_WD_MD 0x1B +#define R_DTMF 0x1C +#define R_DTMF_N 0x1D +#define R_E1_WR_STA 0x20 +#define R_E1_RD_STA 0x20 +#define R_LOS0 0x22 +#define R_LOS1 0x23 +#define R_RX0 0x24 +#define R_RX_FR0 0x25 +#define R_RX_FR1 0x26 +#define R_TX0 0x28 +#define R_TX1 0x29 +#define R_TX_FR0 0x2C + +#define R_TX_FR1 0x2D +#define R_TX_FR2 0x2E +#define R_JATT_ATT 0x2F /* undocumented */ +#define A_ST_RD_STATE 0x30 +#define A_ST_WR_STATE 0x30 +#define R_RX_OFF 0x30 +#define A_ST_CTRL0 0x31 +#define R_SYNC_OUT 0x31 +#define A_ST_CTRL1 0x32 +#define A_ST_CTRL2 0x33 +#define A_ST_SQ_WR 0x34 +#define R_TX_OFF 0x34 +#define R_SYNC_CTRL 0x35 +#define A_ST_CLK_DLY 0x37 +#define R_PWM0 0x38 +#define R_PWM1 0x39 +#define A_ST_B1_TX 0x3C +#define A_ST_B2_TX 0x3D +#define A_ST_D_TX 0x3E +#define R_GPIO_OUT0 0x40 +#define R_GPIO_OUT1 0x41 +#define R_GPIO_EN0 0x42 +#define R_GPIO_EN1 0x43 +#define R_GPIO_SEL 0x44 +#define R_BRG_CTRL 0x45 +#define R_PWM_MD 0x46 +#define R_BRG_MD 0x47 +#define R_BRG_TIM0 0x48 +#define R_BRG_TIM1 0x49 +#define R_BRG_TIM2 0x4A +#define R_BRG_TIM3 0x4B +#define R_BRG_TIM_SEL01 0x4C +#define R_BRG_TIM_SEL23 0x4D +#define R_BRG_TIM_SEL45 0x4E +#define R_BRG_TIM_SEL67 0x4F +#define A_SL_CFG 0xD0 +#define A_CONF 0xD1 +#define A_CH_MSK 0xF4 +#define A_CON_HDLC 0xFA +#define A_SUBCH_CFG 0xFB +#define A_CHANNEL 0xFC +#define A_FIFO_SEQ 0xFD +#define A_IRQ_MSK 0xFF + +/* read only registers */ +#define A_Z12 0x04 +#define A_Z1L 0x04 +#define A_Z1 0x04 +#define A_Z1H 0x05 +#define A_Z2L 0x06 +#define A_Z2 0x06 +#define A_Z2H 0x07 +#define A_F1 0x0C +#define A_F12 0x0C +#define A_F2 0x0D +#define R_IRQ_OVIEW 0x10 +#define R_IRQ_MISC 0x11 +#define R_IRQ_STATECH 0x12 +#define R_CONF_OFLOW 0x14 +#define R_RAM_USE 0x15 +#define R_CHIP_ID 0x16 +#define R_BERT_STA 0x17 +#define R_F0_CNTL 0x18 +#define R_F0_CNTH 0x19 +#define R_BERT_EC 0x1A +#define R_BERT_ECL 0x1A +#define R_BERT_ECH 0x1B +#define R_STATUS 0x1C +#define R_CHIP_RV 0x1F +#define R_STATE 0x20 +#define R_SYNC_STA 0x24 +#define R_RX_SL0_0 0x25 +#define R_RX_SL0_1 0x26 +#define R_RX_SL0_2 0x27 +#define R_JATT_DIR 0x2b /* undocumented */ +#define R_SLIP 0x2c +#define A_ST_RD_STA 0x30 +#define R_FAS_EC 0x30 +#define R_FAS_ECL 0x30 +#define R_FAS_ECH 0x31 +#define R_VIO_EC 0x32 +#define R_VIO_ECL 0x32 +#define R_VIO_ECH 0x33 +#define A_ST_SQ_RD 0x34 +#define R_CRC_EC 0x34 +#define R_CRC_ECL 0x34 +#define R_CRC_ECH 0x35 +#define R_E_EC 0x36 +#define R_E_ECL 0x36 +#define R_E_ECH 0x37 +#define R_SA6_SA13_EC 0x38 +#define R_SA6_SA13_ECL 0x38 +#define R_SA6_SA13_ECH 0x39 +#define R_SA6_SA23_EC 0x3A +#define R_SA6_SA23_ECL 0x3A +#define R_SA6_SA23_ECH 0x3B +#define A_ST_B1_RX 0x3C +#define A_ST_B2_RX 0x3D +#define A_ST_D_RX 0x3E +#define A_ST_E_RX 0x3F +#define R_GPIO_IN0 0x40 +#define R_GPIO_IN1 0x41 +#define R_GPI_IN0 0x44 +#define R_GPI_IN1 0x45 +#define R_GPI_IN2 0x46 +#define R_GPI_IN3 0x47 +#define R_INT_DATA 0x88 +#define R_IRQ_FIFO_BL0 0xC8 +#define R_IRQ_FIFO_BL1 0xC9 +#define R_IRQ_FIFO_BL2 0xCA +#define R_IRQ_FIFO_BL3 0xCB +#define R_IRQ_FIFO_BL4 0xCC +#define R_IRQ_FIFO_BL5 0xCD +#define R_IRQ_FIFO_BL6 0xCE +#define R_IRQ_FIFO_BL7 0xCF + +/* read and write registers */ +#define A_FIFO_DATA0 0x80 +#define A_FIFO_DATA1 0x80 +#define A_FIFO_DATA2 0x80 +#define A_FIFO_DATA0_NOINC 0x84 +#define A_FIFO_DATA1_NOINC 0x84 +#define A_FIFO_DATA2_NOINC 0x84 +#define R_RAM_DATA 0xC0 + + +/* + * BIT SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* chapter 2: universal bus interface */ +/* R_CIRM */ +#define V_IRQ_SEL 0x01 +#define V_SRES 0x08 +#define V_HFCRES 0x10 +#define V_PCMRES 0x20 +#define V_STRES 0x40 +#define V_ETRES 0x40 +#define V_RLD_EPR 0x80 +/* R_CTRL */ +#define V_FIFO_LPRIO 0x02 +#define V_SLOW_RD 0x04 +#define V_EXT_RAM 0x08 +#define V_CLK_OFF 0x20 +#define V_ST_CLK 0x40 +/* R_RAM_ADDR0 */ +#define V_RAM_ADDR2 0x01 +#define V_ADDR_RES 0x40 +#define V_ADDR_INC 0x80 +/* R_RAM_SZ */ +#define V_RAM_SZ 0x01 +#define V_PWM0_16KHZ 0x10 +#define V_PWM1_16KHZ 0x20 +#define V_FZ_MD 0x80 +/* R_CHIP_ID */ +#define V_PNP_IRQ 0x01 +#define V_CHIP_ID 0x10 + +/* chapter 3: data flow */ +/* R_FIRST_FIFO */ +#define V_FIRST_FIRO_DIR 0x01 +#define V_FIRST_FIFO_NUM 0x02 +/* R_FIFO_MD */ +#define V_FIFO_MD 0x01 +#define V_CSM_MD 0x04 +#define V_FSM_MD 0x08 +#define V_FIFO_SZ 0x10 +/* R_FIFO */ +#define V_FIFO_DIR 0x01 +#define V_FIFO_NUM 0x02 +#define V_REV 0x80 +/* R_SLOT */ +#define V_SL_DIR 0x01 +#define V_SL_NUM 0x02 +/* A_SL_CFG */ +#define V_CH_DIR 0x01 +#define V_CH_SEL 0x02 +#define V_ROUTING 0x40 +/* A_CON_HDLC */ +#define V_IFF 0x01 +#define V_HDLC_TRP 0x02 +#define V_TRP_IRQ 0x04 +#define V_DATA_FLOW 0x20 +/* A_SUBCH_CFG */ +#define V_BIT_CNT 0x01 +#define V_START_BIT 0x08 +#define V_LOOP_FIFO 0x40 +#define V_INV_DATA 0x80 +/* A_CHANNEL */ +#define V_CH_DIR0 0x01 +#define V_CH_NUM0 0x02 +/* A_FIFO_SEQ */ +#define V_NEXT_FIFO_DIR 0x01 +#define V_NEXT_FIFO_NUM 0x02 +#define V_SEQ_END 0x40 + +/* chapter 4: FIFO handling and HDLC controller */ +/* R_INC_RES_FIFO */ +#define V_INC_F 0x01 +#define V_RES_F 0x02 +#define V_RES_LOST 0x04 + +/* chapter 5: S/T interface */ +/* R_SCI_MSK */ +#define V_SCI_MSK_ST0 0x01 +#define V_SCI_MSK_ST1 0x02 +#define V_SCI_MSK_ST2 0x04 +#define V_SCI_MSK_ST3 0x08 +#define V_SCI_MSK_ST4 0x10 +#define V_SCI_MSK_ST5 0x20 +#define V_SCI_MSK_ST6 0x40 +#define V_SCI_MSK_ST7 0x80 +/* R_ST_SEL */ +#define V_ST_SEL 0x01 +#define V_MULT_ST 0x08 +/* R_ST_SYNC */ +#define V_SYNC_SEL 0x01 +#define V_AUTO_SYNC 0x08 +/* A_ST_WR_STA */ +#define V_ST_SET_STA 0x01 +#define V_ST_LD_STA 0x10 +#define V_ST_ACT 0x20 +#define V_SET_G2_G3 0x80 +/* A_ST_CTRL0 */ +#define V_B1_EN 0x01 +#define V_B2_EN 0x02 +#define V_ST_MD 0x04 +#define V_D_PRIO 0x08 +#define V_SQ_EN 0x10 +#define V_96KHZ 0x20 +#define V_TX_LI 0x40 +#define V_ST_STOP 0x80 +/* A_ST_CTRL1 */ +#define V_G2_G3_EN 0x01 +#define V_D_HI 0x04 +#define V_E_IGNO 0x08 +#define V_E_LO 0x10 +#define V_B12_SWAP 0x80 +/* A_ST_CTRL2 */ +#define V_B1_RX_EN 0x01 +#define V_B2_RX_EN 0x02 +#define V_ST_TRIS 0x40 +/* A_ST_CLK_DLY */ +#define V_ST_CK_DLY 0x01 +#define V_ST_SMPL 0x10 +/* A_ST_D_TX */ +#define V_ST_D_TX 0x40 +/* R_IRQ_STATECH */ +#define V_SCI_ST0 0x01 +#define V_SCI_ST1 0x02 +#define V_SCI_ST2 0x04 +#define V_SCI_ST3 0x08 +#define V_SCI_ST4 0x10 +#define V_SCI_ST5 0x20 +#define V_SCI_ST6 0x40 +#define V_SCI_ST7 0x80 +/* A_ST_RD_STA */ +#define V_ST_STA 0x01 +#define V_FR_SYNC_ST 0x10 +#define V_TI2_EXP 0x20 +#define V_INFO0 0x40 +#define V_G2_G3 0x80 +/* A_ST_SQ_RD */ +#define V_ST_SQ 0x01 +#define V_MF_RX_RDY 0x10 +#define V_MF_TX_RDY 0x80 +/* A_ST_D_RX */ +#define V_ST_D_RX 0x40 +/* A_ST_E_RX */ +#define V_ST_E_RX 0x40 + +/* chapter 5: E1 interface */ +/* R_E1_WR_STA */ +/* R_E1_RD_STA */ +#define V_E1_SET_STA 0x01 +#define V_E1_LD_STA 0x10 +/* R_RX0 */ +#define V_RX_CODE 0x01 +#define V_RX_FBAUD 0x04 +#define V_RX_CMI 0x08 +#define V_RX_INV_CMI 0x10 +#define V_RX_INV_CLK 0x20 +#define V_RX_INV_DATA 0x40 +#define V_AIS_ITU 0x80 +/* R_RX_FR0 */ +#define V_NO_INSYNC 0x01 +#define V_AUTO_RESYNC 0x02 +#define V_AUTO_RECO 0x04 +#define V_SWORD_COND 0x08 +#define V_SYNC_LOSS 0x10 +#define V_XCRC_SYNC 0x20 +#define V_MF_RESYNC 0x40 +#define V_RESYNC 0x80 +/* R_RX_FR1 */ +#define V_RX_MF 0x01 +#define V_RX_MF_SYNC 0x02 +#define V_RX_SL0_RAM 0x04 +#define V_ERR_SIM 0x20 +#define V_RES_NMF 0x40 +/* R_TX0 */ +#define V_TX_CODE 0x01 +#define V_TX_FBAUD 0x04 +#define V_TX_CMI_CODE 0x08 +#define V_TX_INV_CMI_CODE 0x10 +#define V_TX_INV_CLK 0x20 +#define V_TX_INV_DATA 0x40 +#define V_OUT_EN 0x80 +/* R_TX1 */ +#define V_INV_CLK 0x01 +#define V_EXCHG_DATA_LI 0x02 +#define V_AIS_OUT 0x04 +#define V_ATX 0x20 +#define V_NTRI 0x40 +#define V_AUTO_ERR_RES 0x80 +/* R_TX_FR0 */ +#define V_TRP_FAS 0x01 +#define V_TRP_NFAS 0x02 +#define V_TRP_RAL 0x04 +#define V_TRP_SA 0x08 +/* R_TX_FR1 */ +#define V_TX_FAS 0x01 +#define V_TX_NFAS 0x02 +#define V_TX_RAL 0x04 +#define V_TX_SA 0x08 +/* R_TX_FR2 */ +#define V_TX_MF 0x01 +#define V_TRP_SL0 0x02 +#define V_TX_SL0_RAM 0x04 +#define V_TX_E 0x10 +#define V_NEG_E 0x20 +#define V_XS12_ON 0x40 +#define V_XS15_ON 0x80 +/* R_RX_OFF */ +#define V_RX_SZ 0x01 +#define V_RX_INIT 0x04 +/* R_SYNC_OUT */ +#define V_SYNC_E1_RX 0x01 +#define V_IPATS0 0x20 +#define V_IPATS1 0x40 +#define V_IPATS2 0x80 +/* R_TX_OFF */ +#define V_TX_SZ 0x01 +#define V_TX_INIT 0x04 +/* R_SYNC_CTRL */ +#define V_EXT_CLK_SYNC 0x01 +#define V_SYNC_OFFS 0x02 +#define V_PCM_SYNC 0x04 +#define V_NEG_CLK 0x08 +#define V_HCLK 0x10 +/* +#define V_JATT_AUTO_DEL 0x20 +#define V_JATT_AUTO 0x40 +*/ +#define V_JATT_OFF 0x80 +/* R_STATE */ +#define V_E1_STA 0x01 +#define V_ALT_FR_RX 0x40 +#define V_ALT_FR_TX 0x80 +/* R_SYNC_STA */ +#define V_RX_STA 0x01 +#define V_FR_SYNC_E1 0x04 +#define V_SIG_LOS 0x08 +#define V_MFA_STA 0x10 +#define V_AIS 0x40 +#define V_NO_MF_SYNC 0x80 +/* R_RX_SL0_0 */ +#define V_SI_FAS 0x01 +#define V_SI_NFAS 0x02 +#define V_A 0x04 +#define V_CRC_OK 0x08 +#define V_TX_E1 0x10 +#define V_TX_E2 0x20 +#define V_RX_E1 0x40 +#define V_RX_E2 0x80 +/* R_SLIP */ +#define V_SLIP_RX 0x01 +#define V_FOSLIP_RX 0x08 +#define V_SLIP_TX 0x10 +#define V_FOSLIP_TX 0x80 + +/* chapter 6: PCM interface */ +/* R_PCM_MD0 */ +#define V_PCM_MD 0x01 +#define V_C4_POL 0x02 +#define V_F0_NEG 0x04 +#define V_F0_LEN 0x08 +#define V_PCM_ADDR 0x10 +/* R_SL_SEL0 */ +#define V_SL_SEL0 0x01 +#define V_SH_SEL0 0x80 +/* R_SL_SEL1 */ +#define V_SL_SEL1 0x01 +#define V_SH_SEL1 0x80 +/* R_SL_SEL2 */ +#define V_SL_SEL2 0x01 +#define V_SH_SEL2 0x80 +/* R_SL_SEL3 */ +#define V_SL_SEL3 0x01 +#define V_SH_SEL3 0x80 +/* R_SL_SEL4 */ +#define V_SL_SEL4 0x01 +#define V_SH_SEL4 0x80 +/* R_SL_SEL5 */ +#define V_SL_SEL5 0x01 +#define V_SH_SEL5 0x80 +/* R_SL_SEL6 */ +#define V_SL_SEL6 0x01 +#define V_SH_SEL6 0x80 +/* R_SL_SEL7 */ +#define V_SL_SEL7 0x01 +#define V_SH_SEL7 0x80 +/* R_PCM_MD1 */ +#define V_ODEC_CON 0x01 +#define V_PLL_ADJ 0x04 +#define V_PCM_DR 0x10 +#define V_PCM_LOOP 0x40 +/* R_PCM_MD2 */ +#define V_SYNC_PLL 0x02 +#define V_SYNC_SRC 0x04 +#define V_SYNC_OUT 0x08 +#define V_ICR_FR_TIME 0x40 +#define V_EN_PLL 0x80 + +/* chapter 7: pulse width modulation */ +/* R_PWM_MD */ +#define V_EXT_IRQ_EN 0x08 +#define V_PWM0_MD 0x10 +#define V_PWM1_MD 0x40 + +/* chapter 8: multiparty audio conferences */ +/* R_CONF_EN */ +#define V_CONF_EN 0x01 +#define V_ULAW 0x80 +/* A_CONF */ +#define V_CONF_NUM 0x01 +#define V_NOISE_SUPPR 0x08 +#define V_ATT_LEV 0x20 +#define V_CONF_SL 0x80 +/* R_CONF_OFLOW */ +#define V_CONF_OFLOW0 0x01 +#define V_CONF_OFLOW1 0x02 +#define V_CONF_OFLOW2 0x04 +#define V_CONF_OFLOW3 0x08 +#define V_CONF_OFLOW4 0x10 +#define V_CONF_OFLOW5 0x20 +#define V_CONF_OFLOW6 0x40 +#define V_CONF_OFLOW7 0x80 + +/* chapter 9: DTMF contoller */ +/* R_DTMF0 */ +#define V_DTMF_EN 0x01 +#define V_HARM_SEL 0x02 +#define V_DTMF_RX_CH 0x04 +#define V_DTMF_STOP 0x08 +#define V_CHBL_SEL 0x10 +#define V_RST_DTMF 0x40 +#define V_ULAW_SEL 0x80 + +/* chapter 10: BERT */ +/* R_BERT_WD_MD */ +#define V_PAT_SEQ 0x01 +#define V_BERT_ERR 0x08 +#define V_AUTO_WD_RES 0x20 +#define V_WD_RES 0x80 +/* R_BERT_STA */ +#define V_BERT_SYNC_SRC 0x01 +#define V_BERT_SYNC 0x10 +#define V_BERT_INV_DATA 0x20 + +/* chapter 11: auxiliary interface */ +/* R_BRG_PCM_CFG */ +#define V_BRG_EN 0x01 +#define V_BRG_MD 0x02 +#define V_PCM_CLK 0x20 +#define V_ADDR_WRDLY 0x40 +/* R_BRG_CTRL */ +#define V_BRG_CS 0x01 +#define V_BRG_ADDR 0x08 +#define V_BRG_CS_SRC 0x80 +/* R_BRG_MD */ +#define V_BRG_MD0 0x01 +#define V_BRG_MD1 0x02 +#define V_BRG_MD2 0x04 +#define V_BRG_MD3 0x08 +#define V_BRG_MD4 0x10 +#define V_BRG_MD5 0x20 +#define V_BRG_MD6 0x40 +#define V_BRG_MD7 0x80 +/* R_BRG_TIM0 */ +#define V_BRG_TIM0_IDLE 0x01 +#define V_BRG_TIM0_CLK 0x10 +/* R_BRG_TIM1 */ +#define V_BRG_TIM1_IDLE 0x01 +#define V_BRG_TIM1_CLK 0x10 +/* R_BRG_TIM2 */ +#define V_BRG_TIM2_IDLE 0x01 +#define V_BRG_TIM2_CLK 0x10 +/* R_BRG_TIM3 */ +#define V_BRG_TIM3_IDLE 0x01 +#define V_BRG_TIM3_CLK 0x10 +/* R_BRG_TIM_SEL01 */ +#define V_BRG_WR_SEL0 0x01 +#define V_BRG_RD_SEL0 0x04 +#define V_BRG_WR_SEL1 0x10 +#define V_BRG_RD_SEL1 0x40 +/* R_BRG_TIM_SEL23 */ +#define V_BRG_WR_SEL2 0x01 +#define V_BRG_RD_SEL2 0x04 +#define V_BRG_WR_SEL3 0x10 +#define V_BRG_RD_SEL3 0x40 +/* R_BRG_TIM_SEL45 */ +#define V_BRG_WR_SEL4 0x01 +#define V_BRG_RD_SEL4 0x04 +#define V_BRG_WR_SEL5 0x10 +#define V_BRG_RD_SEL5 0x40 +/* R_BRG_TIM_SEL67 */ +#define V_BRG_WR_SEL6 0x01 +#define V_BRG_RD_SEL6 0x04 +#define V_BRG_WR_SEL7 0x10 +#define V_BRG_RD_SEL7 0x40 + +/* chapter 12: clock, reset, interrupt, timer and watchdog */ +/* R_IRQMSK_MISC */ +#define V_STA_IRQMSK 0x01 +#define V_TI_IRQMSK 0x02 +#define V_PROC_IRQMSK 0x04 +#define V_DTMF_IRQMSK 0x08 +#define V_IRQ1S_MSK 0x10 +#define V_SA6_IRQMSK 0x20 +#define V_RX_EOMF_MSK 0x40 +#define V_TX_EOMF_MSK 0x80 +/* R_IRQ_CTRL */ +#define V_FIFO_IRQ 0x01 +#define V_GLOB_IRQ_EN 0x08 +#define V_IRQ_POL 0x10 +/* R_TI_WD */ +#define V_EV_TS 0x01 +#define V_WD_TS 0x10 +/* A_IRQ_MSK */ +#define V_IRQ 0x01 +#define V_BERT_EN 0x02 +#define V_MIX_IRQ 0x04 +/* R_IRQ_OVIEW */ +#define V_IRQ_FIFO_BL0 0x01 +#define V_IRQ_FIFO_BL1 0x02 +#define V_IRQ_FIFO_BL2 0x04 +#define V_IRQ_FIFO_BL3 0x08 +#define V_IRQ_FIFO_BL4 0x10 +#define V_IRQ_FIFO_BL5 0x20 +#define V_IRQ_FIFO_BL6 0x40 +#define V_IRQ_FIFO_BL7 0x80 +/* R_IRQ_MISC */ +#define V_STA_IRQ 0x01 +#define V_TI_IRQ 0x02 +#define V_IRQ_PROC 0x04 +#define V_DTMF_IRQ 0x08 +#define V_IRQ1S 0x10 +#define V_SA6_IRQ 0x20 +#define V_RX_EOMF 0x40 +#define V_TX_EOMF 0x80 +/* R_STATUS */ +#define V_BUSY 0x01 +#define V_PROC 0x02 +#define V_DTMF_STA 0x04 +#define V_LOST_STA 0x08 +#define V_SYNC_IN 0x10 +#define V_EXT_IRQSTA 0x20 +#define V_MISC_IRQSTA 0x40 +#define V_FR_IRQSTA 0x80 +/* R_IRQ_FIFO_BL0 */ +#define V_IRQ_FIFO0_TX 0x01 +#define V_IRQ_FIFO0_RX 0x02 +#define V_IRQ_FIFO1_TX 0x04 +#define V_IRQ_FIFO1_RX 0x08 +#define V_IRQ_FIFO2_TX 0x10 +#define V_IRQ_FIFO2_RX 0x20 +#define V_IRQ_FIFO3_TX 0x40 +#define V_IRQ_FIFO3_RX 0x80 +/* R_IRQ_FIFO_BL1 */ +#define V_IRQ_FIFO4_TX 0x01 +#define V_IRQ_FIFO4_RX 0x02 +#define V_IRQ_FIFO5_TX 0x04 +#define V_IRQ_FIFO5_RX 0x08 +#define V_IRQ_FIFO6_TX 0x10 +#define V_IRQ_FIFO6_RX 0x20 +#define V_IRQ_FIFO7_TX 0x40 +#define V_IRQ_FIFO7_RX 0x80 +/* R_IRQ_FIFO_BL2 */ +#define V_IRQ_FIFO8_TX 0x01 +#define V_IRQ_FIFO8_RX 0x02 +#define V_IRQ_FIFO9_TX 0x04 +#define V_IRQ_FIFO9_RX 0x08 +#define V_IRQ_FIFO10_TX 0x10 +#define V_IRQ_FIFO10_RX 0x20 +#define V_IRQ_FIFO11_TX 0x40 +#define V_IRQ_FIFO11_RX 0x80 +/* R_IRQ_FIFO_BL3 */ +#define V_IRQ_FIFO12_TX 0x01 +#define V_IRQ_FIFO12_RX 0x02 +#define V_IRQ_FIFO13_TX 0x04 +#define V_IRQ_FIFO13_RX 0x08 +#define V_IRQ_FIFO14_TX 0x10 +#define V_IRQ_FIFO14_RX 0x20 +#define V_IRQ_FIFO15_TX 0x40 +#define V_IRQ_FIFO15_RX 0x80 +/* R_IRQ_FIFO_BL4 */ +#define V_IRQ_FIFO16_TX 0x01 +#define V_IRQ_FIFO16_RX 0x02 +#define V_IRQ_FIFO17_TX 0x04 +#define V_IRQ_FIFO17_RX 0x08 +#define V_IRQ_FIFO18_TX 0x10 +#define V_IRQ_FIFO18_RX 0x20 +#define V_IRQ_FIFO19_TX 0x40 +#define V_IRQ_FIFO19_RX 0x80 +/* R_IRQ_FIFO_BL5 */ +#define V_IRQ_FIFO20_TX 0x01 +#define V_IRQ_FIFO20_RX 0x02 +#define V_IRQ_FIFO21_TX 0x04 +#define V_IRQ_FIFO21_RX 0x08 +#define V_IRQ_FIFO22_TX 0x10 +#define V_IRQ_FIFO22_RX 0x20 +#define V_IRQ_FIFO23_TX 0x40 +#define V_IRQ_FIFO23_RX 0x80 +/* R_IRQ_FIFO_BL6 */ +#define V_IRQ_FIFO24_TX 0x01 +#define V_IRQ_FIFO24_RX 0x02 +#define V_IRQ_FIFO25_TX 0x04 +#define V_IRQ_FIFO25_RX 0x08 +#define V_IRQ_FIFO26_TX 0x10 +#define V_IRQ_FIFO26_RX 0x20 +#define V_IRQ_FIFO27_TX 0x40 +#define V_IRQ_FIFO27_RX 0x80 +/* R_IRQ_FIFO_BL7 */ +#define V_IRQ_FIFO28_TX 0x01 +#define V_IRQ_FIFO28_RX 0x02 +#define V_IRQ_FIFO29_TX 0x04 +#define V_IRQ_FIFO29_RX 0x08 +#define V_IRQ_FIFO30_TX 0x10 +#define V_IRQ_FIFO30_RX 0x20 +#define V_IRQ_FIFO31_TX 0x40 +#define V_IRQ_FIFO31_RX 0x80 + +/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ +/* R_GPIO_OUT0 */ +#define V_GPIO_OUT0 0x01 +#define V_GPIO_OUT1 0x02 +#define V_GPIO_OUT2 0x04 +#define V_GPIO_OUT3 0x08 +#define V_GPIO_OUT4 0x10 +#define V_GPIO_OUT5 0x20 +#define V_GPIO_OUT6 0x40 +#define V_GPIO_OUT7 0x80 +/* R_GPIO_OUT1 */ +#define V_GPIO_OUT8 0x01 +#define V_GPIO_OUT9 0x02 +#define V_GPIO_OUT10 0x04 +#define V_GPIO_OUT11 0x08 +#define V_GPIO_OUT12 0x10 +#define V_GPIO_OUT13 0x20 +#define V_GPIO_OUT14 0x40 +#define V_GPIO_OUT15 0x80 +/* R_GPIO_EN0 */ +#define V_GPIO_EN0 0x01 +#define V_GPIO_EN1 0x02 +#define V_GPIO_EN2 0x04 +#define V_GPIO_EN3 0x08 +#define V_GPIO_EN4 0x10 +#define V_GPIO_EN5 0x20 +#define V_GPIO_EN6 0x40 +#define V_GPIO_EN7 0x80 +/* R_GPIO_EN1 */ +#define V_GPIO_EN8 0x01 +#define V_GPIO_EN9 0x02 +#define V_GPIO_EN10 0x04 +#define V_GPIO_EN11 0x08 +#define V_GPIO_EN12 0x10 +#define V_GPIO_EN13 0x20 +#define V_GPIO_EN14 0x40 +#define V_GPIO_EN15 0x80 +/* R_GPIO_SEL */ +#define V_GPIO_SEL0 0x01 +#define V_GPIO_SEL1 0x02 +#define V_GPIO_SEL2 0x04 +#define V_GPIO_SEL3 0x08 +#define V_GPIO_SEL4 0x10 +#define V_GPIO_SEL5 0x20 +#define V_GPIO_SEL6 0x40 +#define V_GPIO_SEL7 0x80 +/* R_GPIO_IN0 */ +#define V_GPIO_IN0 0x01 +#define V_GPIO_IN1 0x02 +#define V_GPIO_IN2 0x04 +#define V_GPIO_IN3 0x08 +#define V_GPIO_IN4 0x10 +#define V_GPIO_IN5 0x20 +#define V_GPIO_IN6 0x40 +#define V_GPIO_IN7 0x80 +/* R_GPIO_IN1 */ +#define V_GPIO_IN8 0x01 +#define V_GPIO_IN9 0x02 +#define V_GPIO_IN10 0x04 +#define V_GPIO_IN11 0x08 +#define V_GPIO_IN12 0x10 +#define V_GPIO_IN13 0x20 +#define V_GPIO_IN14 0x40 +#define V_GPIO_IN15 0x80 +/* R_GPI_IN0 */ +#define V_GPI_IN0 0x01 +#define V_GPI_IN1 0x02 +#define V_GPI_IN2 0x04 +#define V_GPI_IN3 0x08 +#define V_GPI_IN4 0x10 +#define V_GPI_IN5 0x20 +#define V_GPI_IN6 0x40 +#define V_GPI_IN7 0x80 +/* R_GPI_IN1 */ +#define V_GPI_IN8 0x01 +#define V_GPI_IN9 0x02 +#define V_GPI_IN10 0x04 +#define V_GPI_IN11 0x08 +#define V_GPI_IN12 0x10 +#define V_GPI_IN13 0x20 +#define V_GPI_IN14 0x40 +#define V_GPI_IN15 0x80 +/* R_GPI_IN2 */ +#define V_GPI_IN16 0x01 +#define V_GPI_IN17 0x02 +#define V_GPI_IN18 0x04 +#define V_GPI_IN19 0x08 +#define V_GPI_IN20 0x10 +#define V_GPI_IN21 0x20 +#define V_GPI_IN22 0x40 +#define V_GPI_IN23 0x80 +/* R_GPI_IN3 */ +#define V_GPI_IN24 0x01 +#define V_GPI_IN25 0x02 +#define V_GPI_IN26 0x04 +#define V_GPI_IN27 0x08 +#define V_GPI_IN28 0x10 +#define V_GPI_IN29 0x20 +#define V_GPI_IN30 0x40 +#define V_GPI_IN31 0x80 + +/* map of all registers, used for debugging */ + +#ifdef HFC_REGISTER_DEBUG +struct hfc_register_names { + char *name; + u_char reg; +} hfc_register_names[] = { + /* write registers */ + {"R_CIRM", 0x00}, + {"R_CTRL", 0x01}, + {"R_BRG_PCM_CFG ", 0x02}, + {"R_RAM_ADDR0", 0x08}, + {"R_RAM_ADDR1", 0x09}, + {"R_RAM_ADDR2", 0x0A}, + {"R_FIRST_FIFO", 0x0B}, + {"R_RAM_SZ", 0x0C}, + {"R_FIFO_MD", 0x0D}, + {"R_INC_RES_FIFO", 0x0E}, + {"R_FIFO / R_FSM_IDX", 0x0F}, + {"R_SLOT", 0x10}, + {"R_IRQMSK_MISC", 0x11}, + {"R_SCI_MSK", 0x12}, + {"R_IRQ_CTRL", 0x13}, + {"R_PCM_MD0", 0x14}, + {"R_0x15", 0x15}, + {"R_ST_SEL", 0x16}, + {"R_ST_SYNC", 0x17}, + {"R_CONF_EN", 0x18}, + {"R_TI_WD", 0x1A}, + {"R_BERT_WD_MD", 0x1B}, + {"R_DTMF", 0x1C}, + {"R_DTMF_N", 0x1D}, + {"R_E1_XX_STA", 0x20}, + {"R_LOS0", 0x22}, + {"R_LOS1", 0x23}, + {"R_RX0", 0x24}, + {"R_RX_FR0", 0x25}, + {"R_RX_FR1", 0x26}, + {"R_TX0", 0x28}, + {"R_TX1", 0x29}, + {"R_TX_FR0", 0x2C}, + {"R_TX_FR1", 0x2D}, + {"R_TX_FR2", 0x2E}, + {"R_JATT_ATT", 0x2F}, + {"A_ST_xx_STA/R_RX_OFF", 0x30}, + {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, + {"A_ST_CTRL1", 0x32}, + {"A_ST_CTRL2", 0x33}, + {"A_ST_SQ_WR", 0x34}, + {"R_TX_OFF", 0x34}, + {"R_SYNC_CTRL", 0x35}, + {"A_ST_CLK_DLY", 0x37}, + {"R_PWM0", 0x38}, + {"R_PWM1", 0x39}, + {"A_ST_B1_TX", 0x3C}, + {"A_ST_B2_TX", 0x3D}, + {"A_ST_D_TX", 0x3E}, + {"R_GPIO_OUT0", 0x40}, + {"R_GPIO_OUT1", 0x41}, + {"R_GPIO_EN0", 0x42}, + {"R_GPIO_EN1", 0x43}, + {"R_GPIO_SEL", 0x44}, + {"R_BRG_CTRL", 0x45}, + {"R_PWM_MD", 0x46}, + {"R_BRG_MD", 0x47}, + {"R_BRG_TIM0", 0x48}, + {"R_BRG_TIM1", 0x49}, + {"R_BRG_TIM2", 0x4A}, + {"R_BRG_TIM3", 0x4B}, + {"R_BRG_TIM_SEL01", 0x4C}, + {"R_BRG_TIM_SEL23", 0x4D}, + {"R_BRG_TIM_SEL45", 0x4E}, + {"R_BRG_TIM_SEL67", 0x4F}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_RAM_DATA", 0xC0}, + {"A_SL_CFG", 0xD0}, + {"A_CONF", 0xD1}, + {"A_CH_MSK", 0xF4}, + {"A_CON_HDLC", 0xFA}, + {"A_SUBCH_CFG", 0xFB}, + {"A_CHANNEL", 0xFC}, + {"A_FIFO_SEQ", 0xFD}, + {"A_IRQ_MSK", 0xFF}, + {NULL, 0}, + + /* read registers */ + {"A_Z1", 0x04}, + {"A_Z1H", 0x05}, + {"A_Z2", 0x06}, + {"A_Z2H", 0x07}, + {"A_F1", 0x0C}, + {"A_F2", 0x0D}, + {"R_IRQ_OVIEW", 0x10}, + {"R_IRQ_MISC", 0x11}, + {"R_IRQ_STATECH", 0x12}, + {"R_CONF_OFLOW", 0x14}, + {"R_RAM_USE", 0x15}, + {"R_CHIP_ID", 0x16}, + {"R_BERT_STA", 0x17}, + {"R_F0_CNTL", 0x18}, + {"R_F0_CNTH", 0x19}, + {"R_BERT_ECL", 0x1A}, + {"R_BERT_ECH", 0x1B}, + {"R_STATUS", 0x1C}, + {"R_CHIP_RV", 0x1F}, + {"R_STATE", 0x20}, + {"R_SYNC_STA", 0x24}, + {"R_RX_SL0_0", 0x25}, + {"R_RX_SL0_1", 0x26}, + {"R_RX_SL0_2", 0x27}, + {"R_JATT_DIR", 0x2b}, + {"R_SLIP", 0x2c}, + {"A_ST_RD_STA", 0x30}, + {"R_FAS_ECL", 0x30}, + {"R_FAS_ECH", 0x31}, + {"R_VIO_ECL", 0x32}, + {"R_VIO_ECH", 0x33}, + {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, + {"R_CRC_ECH", 0x35}, + {"R_E_ECL", 0x36}, + {"R_E_ECH", 0x37}, + {"R_SA6_SA13_ECL", 0x38}, + {"R_SA6_SA13_ECH", 0x39}, + {"R_SA6_SA23_ECL", 0x3A}, + {"R_SA6_SA23_ECH", 0x3B}, + {"A_ST_B1_RX", 0x3C}, + {"A_ST_B2_RX", 0x3D}, + {"A_ST_D_RX", 0x3E}, + {"A_ST_E_RX", 0x3F}, + {"R_GPIO_IN0", 0x40}, + {"R_GPIO_IN1", 0x41}, + {"R_GPI_IN0", 0x44}, + {"R_GPI_IN1", 0x45}, + {"R_GPI_IN2", 0x46}, + {"R_GPI_IN3", 0x47}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_INT_DATA", 0x88}, + {"R_RAM_DATA", 0xC0}, + {"R_IRQ_FIFO_BL0", 0xC8}, + {"R_IRQ_FIFO_BL1", 0xC9}, + {"R_IRQ_FIFO_BL2", 0xCA}, + {"R_IRQ_FIFO_BL3", 0xCB}, + {"R_IRQ_FIFO_BL4", 0xCC}, + {"R_IRQ_FIFO_BL5", 0xCD}, + {"R_IRQ_FIFO_BL6", 0xCE}, + {"R_IRQ_FIFO_BL7", 0xCF}, +}; +#endif /* HFC_REGISTER_DEBUG */ + diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c new file mode 100644 index 000000000000..2649ea55a9e8 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -0,0 +1,5320 @@ +/* + * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * ported to mqueue mechanism: + * Peter Sprenger (sprengermoving-bytes.de) + * + * inspired by existing hfc-pci driver: + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil (kkeil@suse.de) + * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Thanks to Cologne Chip AG for this great controller! + */ + +/* + * module parameters: + * type: + * By default (0), the card is automatically detected. + * Or use the following combinations: + * Bit 0-7 = 0x00001 = HFC-E1 (1 port) + * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) + * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) + * Bit 8 = 0x00100 = uLaw (instead of aLaw) + * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware + * Bit 10 = spare + * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwhise auto) + * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwhise auto) + * Bit 13 = spare + * Bit 14 = 0x04000 = Use external ram (128K) + * Bit 15 = 0x08000 = Use external ram (512K) + * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 + * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else + * Bit 18 = spare + * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) + * (all other bits are reserved and shall be 0) + * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM + * bus (PCM master) + * + * port: (optional or required for all ports on all installed cards) + * HFC-4S/HFC-8S only bits: + * Bit 0 = 0x001 = Use master clock for this S/T interface + * (ony once per chip). + * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) + * Don't use this unless you know what you are doing! + * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) + * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock + * received from port 1 + * + * HFC-E1 only bits: + * Bit 0 = 0x0001 = interface: 0=copper, 1=optical + * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) + * Bit 2 = 0x0004 = Report LOS + * Bit 3 = 0x0008 = Report AIS + * Bit 4 = 0x0010 = Report SLIP + * Bit 5 = 0x0020 = Report RDI + * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame + * mode instead. + * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. + * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. + * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. + * (E1 only) + * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 + * for default. + * (all other bits are reserved and shall be 0) + * + * debug: + * NOTE: only one debug value must be given for all cards + * enable debugging (see hfc_multi.h for debug options) + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * Valid is 8, 16, 32, 64, 128, 256. + * + * pcm: + * NOTE: only one pcm value must be given for every card. + * The PCM bus id tells the mISDNdsp module about the connected PCM bus. + * By default (0), the PCM bus id is 100 for the card that is PCM master. + * If multiple cards are PCM master (because they are not interconnected), + * each card with PCM master will have increasing PCM id. + * All PCM busses with the same ID are expected to be connected and have + * common time slots slots. + * Only one chip of the PCM bus must be master, the others slave. + * -1 means no support of PCM bus not even. + * Omit this value, if all cards are interconnected or none is connected. + * If unsure, don't give this parameter. + * + * dslot: + * NOTE: only one poll value must be given for every card. + * Also this value must be given for non-E1 cards. If omitted, the E1 + * card has D-channel on time slot 16, which is default. + * If 1..15 or 17..31, an alternate time slot is used for D-channel. + * In this case, the application must be able to handle this. + * If -1 is given, the D-channel is disabled and all 31 slots can be used + * for B-channel. (only for specific applications) + * If you don't know how to use it, you don't need it! + * + * iomode: + * NOTE: only one mode value must be given for every card. + * -> See hfc_multi.h for HFC_IO_MODE_* values + * By default, the IO mode is pci memory IO (MEMIO). + * Some cards requre specific IO mode, so it cannot be changed. + * It may be usefull to set IO mode to register io (REGIO) to solve + * PCI bridge problems. + * If unsure, don't give this parameter. + * + * clockdelay_nt: + * NOTE: only one clockdelay_nt value must be given once for all cards. + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in NT mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clockdelay_te: + * NOTE: only one clockdelay_te value must be given once + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in TE mode. + * This register is needed for the TBR3 certification, so don't change it. + */ + +/* + * debug register access (never use this, it will flood your system log) + * #define HFC_REGISTER_DEBUG + */ + +static const char *hfcmulti_revision = "2.00"; + +#include +#include +#include +#include +#include + +/* +#define IRQCOUNT_DEBUG +#define IRQ_DEBUG +*/ + +#include "hfc_multi.h" +#ifdef ECHOPREP +#include "gaintab.h" +#endif + +#define MAX_CARDS 8 +#define MAX_PORTS (8 * MAX_CARDS) + +static LIST_HEAD(HFClist); +static spinlock_t HFClock; /* global hfc list lock */ + +static void ph_state_change(struct dchannel *); +static void (*hfc_interrupt)(void); +static void (*register_interrupt)(void); +static int (*unregister_interrupt)(void); +static int interrupt_registered; + +static struct hfc_multi *syncmaster; +int plxsd_master; /* if we have a master card (yet) */ +static spinlock_t plx_lock; /* may not acquire other lock inside */ +EXPORT_SYMBOL(plx_lock); + +#define TYP_E1 1 +#define TYP_4S 4 +#define TYP_8S 8 + +static int poll_timer = 6; /* default = 128 samples = 16ms */ +/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ +static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode + (0x60 MUST be included!) */ +static u_char silence = 0xff; /* silence by LAW */ + +#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ +#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ +#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ + +/* + * module stuff + */ + +static uint type[MAX_CARDS]; +static uint pcm[MAX_CARDS]; +static uint dslot[MAX_CARDS]; +static uint iomode[MAX_CARDS]; +static uint port[MAX_PORTS]; +static uint debug; +static uint poll; +static uint timer; +static uint clockdelay_te = CLKDEL_TE; +static uint clockdelay_nt = CLKDEL_NT; + +static int HFC_cnt, Port_cnt, PCM_cnt = 99; + +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(timer, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); +module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); + +#ifdef HFC_REGISTER_DEBUG +#define HFC_outb(hc, reg, val) \ + (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) +#define HFC_outb_nodebug(hc, reg, val) \ + (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) +#define HFC_inb(hc, reg) \ + (hc->HFC_inb(hc, reg, __func__, __LINE__)) +#define HFC_inb_nodebug(hc, reg) \ + (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_inw(hc, reg) \ + (hc->HFC_inw(hc, reg, __func__, __LINE__)) +#define HFC_inw_nodebug(hc, reg) \ + (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_wait(hc) \ + (hc->HFC_wait(hc, __func__, __LINE__)) +#define HFC_wait_nodebug(hc) \ + (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) +#else +#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) +#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) +#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) +#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) +#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) +#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) +#define HFC_wait(hc) (hc->HFC_wait(hc)) +#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) +#endif + +/* HFC_IO_MODE_PCIMEM */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + writeb(val, (hc->pci_membase)+reg); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readb((hc->pci_membase)+reg); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readw((hc->pci_membase)+reg); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) +#else +HFC_wait_pcimem(struct hfc_multi *hc) +#endif +{ + while (readb((hc->pci_membase)+R_STATUS) & V_BUSY); +} + +/* HFC_IO_MODE_REGIO */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + outb(val, hc->pci_iobase); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inb_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + return inb(hc->pci_iobase); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inw_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + return inw(hc->pci_iobase); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) +#else +HFC_wait_regio(struct hfc_multi *hc) +#endif +{ + outb(R_STATUS, (hc->pci_iobase)+4); + while (inb(hc->pci_iobase) & V_BUSY); +} + +#ifdef HFC_REGISTER_DEBUG +static void +HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + int i; + + i = -1; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(val&1)); + bits[6] = '0'+(!!(val&2)); + bits[5] = '0'+(!!(val&4)); + bits[4] = '0'+(!!(val&8)); + bits[3] = '0'+(!!(val&16)); + bits[2] = '0'+(!!(val&32)); + bits[1] = '0'+(!!(val&64)); + bits[0] = '0'+(!!(val&128)); + printk(KERN_DEBUG + "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + HFC_outb_nodebug(hc, reg, val); +} +static u_char +HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + u_char val = HFC_inb_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(val&1)); + bits[6] = '0'+(!!(val&2)); + bits[5] = '0'+(!!(val&4)); + bits[4] = '0'+(!!(val&8)); + bits[3] = '0'+(!!(val&16)); + bits[2] = '0'+(!!(val&32)); + bits[1] = '0'+(!!(val&64)); + bits[0] = '0'+(!!(val&128)); + printk(KERN_DEBUG + "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + return val; +} +static u_short +HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = ""; + u_short val = HFC_inw_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + printk(KERN_DEBUG + "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", + hc->id, reg, regname, val, function, line); + return val; +} +static void +HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) +{ + printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", + hc->id, function, line); + HFC_wait_nodebug(hc); +} +#endif + +/* write fifo data (REGIO) */ +void +write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase)+4); + while (len>>2) { + outl(*(u32 *)data, hc->pci_iobase); + data += 4; + len -= 4; + } + while (len>>1) { + outw(*(u16 *)data, hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + outb(*data, hc->pci_iobase); + data++; + len--; + } +} +/* write fifo data (PCIMEM) */ +void +write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len>>2) { + writel(*(u32 *)data, (hc->pci_membase)+A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len>>1) { + writew(*(u16 *)data, (hc->pci_membase)+A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + writeb(*data, (hc->pci_membase)+A_FIFO_DATA0); + data++; + len--; + } +} +/* read fifo data (REGIO) */ +void +read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase)+4); + while (len>>2) { + *(u32 *)data = inl(hc->pci_iobase); + data += 4; + len -= 4; + } + while (len>>1) { + *(u16 *)data = inw(hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + *data = inb(hc->pci_iobase); + data++; + len--; + } +} + +/* read fifo data (PCIMEM) */ +void +read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len>>2) { + *(u32 *)data = + readl((hc->pci_membase)+A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len>>1) { + *(u16 *)data = + readw((hc->pci_membase)+A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + *data = readb((hc->pci_membase)+A_FIFO_DATA0); + data++; + len--; + } +} + + +static void +enable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +static void +disable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +#define NUM_EC 2 +#define MAX_TDM_CHAN 32 + + +inline void +enablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ +} + +inline void +disablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ +} + +inline unsigned char +readpcibridge(struct hfc_multi *hc, unsigned char address) +{ + unsigned short cipv; + unsigned char data; + + if (!hc->pci_iobase) + return 0; + + /* slow down a PCI read access by 1 PCI clock cycle */ + HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + /* data = HFC_inb(c, cipv); * was _io before */ + outw(cipv, hc->pci_iobase + 4); + data = inb(hc->pci_iobase); + + /* restore R_CTRL for normal PCI read cycle speed */ + HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ + + return data; +} + +inline void +writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) +{ + unsigned short cipv; + unsigned int datav; + + if (!hc->pci_iobase) + return; + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + outw(cipv, hc->pci_iobase + 4); + /* define a 32 bit dword with 4 identical bytes for write sequence */ + datav = data | ((__u32) data << 8) | ((__u32) data << 16) | + ((__u32) data << 24); + + /* + * write this 32 bit dword to the bridge data port + * this will initiate a write sequence of up to 4 writes to the same + * address on the local bus interface the number of write accesses + * is undefined but >=1 and depends on the next PCI transaction + * during write sequence on the local bus + */ + outl(datav, hc->pci_iobase); +} + +inline void +cpld_set_reg(struct hfc_multi *hc, unsigned char reg) +{ + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); +} + +inline void +cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) +{ + cpld_set_reg(hc, reg); + + enablepcibridge(hc); + writepcibridge(hc, 1, val); + disablepcibridge(hc); + + return; +} + +inline unsigned char +cpld_read_reg(struct hfc_multi *hc, unsigned char reg) +{ + unsigned char bytein; + + cpld_set_reg(hc, reg); + + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); + + enablepcibridge(hc); + bytein = readpcibridge(hc, 1); + disablepcibridge(hc); + + return bytein; +} + +inline void +vpm_write_address(struct hfc_multi *hc, unsigned short addr) +{ + cpld_write_reg(hc, 0, 0xff & addr); + cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); +} + +inline unsigned short +vpm_read_address(struct hfc_multi *c) +{ + unsigned short addr; + unsigned short highbit; + + addr = cpld_read_reg(c, 0); + highbit = cpld_read_reg(c, 1); + + addr = addr | (highbit << 8); + + return addr & 0x1ff; +} + +inline unsigned char +vpm_in(struct hfc_multi *c, int which, unsigned short addr) +{ + unsigned char res; + + vpm_write_address(c, addr); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + enablepcibridge(c); + res = readpcibridge(c, 1); + disablepcibridge(c); + + cpld_set_reg(c, 0); + + return res; +} + +inline void +vpm_out(struct hfc_multi *c, int which, unsigned short addr, + unsigned char data) +{ + vpm_write_address(c, addr); + + enablepcibridge(c); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + writepcibridge(c, 1, data); + + cpld_set_reg(c, 0); + + disablepcibridge(c); + + { + unsigned char regin; + regin = vpm_in(c, which, addr); + if (regin != data) + printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " + "0x%x\n", data, addr, regin); + } + +} + + +void +vpm_init(struct hfc_multi *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int i, x, y; + unsigned int ver; + + for (x = 0; x < NUM_EC; x++) { + /* Setup GPIO's */ + if (!x) { + ver = vpm_in(wc, x, 0x1a0); + printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); + } + + for (y = 0; y < 4; y++) { + vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = vpm_in(wc, x, 0x1a3); /* misc_con */ + vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup Echo length (256 taps) */ + vpm_out(wc, x, 0x022, 1); + vpm_out(wc, x, 0x023, 0xff); + + /* Setup timeslots */ + vpm_out(wc, x, 0x02f, 0x00); + mask = 0x02020202 << (x * 4); + + /* Setup the tdm channel masks for all chips */ + for (i = 0; i < 4; i++) + vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + printk(KERN_DEBUG "VPM: A-law mode\n"); + reg = 0x00 | 0x10 | 0x01; + vpm_out(wc, x, 0x20, reg); + printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); + /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ + + vpm_out(wc, x, 0x24, 0x02); + reg = vpm_in(wc, x, 0x24); + printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); + + /* Initialize echo cans */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x00); + } + + /* + * ARM arch at least disallows a udelay of + * more than 2ms... it gives a fake "__bad_udelay" + * reference at link-time. + * long delays in kernel code are pretty sucky anyway + * for now work around it using 5 x 2ms instead of 1 x 10ms + */ + + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + + /* Put in bypass mode */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x01); + } + + /* Enable bypass */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, 0x78 + i, 0x01); + } + + } +} + +void +vpm_check(struct hfc_multi *hctmp) +{ + unsigned char gpi2; + + gpi2 = HFC_inb(hctmp, R_GPI_IN2); + + if ((gpi2 & 0x3) != 0x3) + printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); +} + + +/* + * Interface to enable/disable the HW Echocan + * + * these functions are called within a spin_lock_irqsave on + * the channel instance lock, so we are not disturbed by irqs + * + * we can later easily change the interface to make other + * things configurable, for now we configure the taps + * + */ + +void +vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = -4; + struct sk_buff *skb; +#endif + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", + taps, timeslot); + + vpm_out(hc, unit, timeslot, 0x7e); +} + +void +vpm_echocan_off(struct hfc_multi *hc, int ch) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = 0; + struct sk_buff *skb; +#endif + + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", + timeslot); + /* FILLME */ + vpm_out(hc, unit, timeslot, 0x01); +} + + +/* + * Speech Design resync feature + * NOTE: This is called sometimes outside interrupt handler. + * We must lock irqsave, so no other interrupt (other card) will occurr! + * Also multiple interrupts may nest, so must lock each access (lists, card)! + */ +static inline void +hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) +{ + struct hfc_multi *hc, *next, *pcmmaster = 0; + u_int *plx_acc_32, pv; + u_long flags; + + spin_lock_irqsave(&HFClock, flags); + spin_lock(&plx_lock); /* must be locked inside other locks */ + + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", + __func__, syncmaster); + + /* select new master */ + if (newmaster) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "using provided controller\n"); + } else { + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (hc->syncronized) { + newmaster = hc; + break; + } + } + } + } + + /* Disable sync of all cards */ + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv &= ~PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + pcmmaster = hc; + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule SYNC_I\n"); + hc->e1_resync |= 1; /* get SYNC_I */ + } + } + } + } + + if (newmaster) { + hc = newmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "id=%d (0x%p) = syncronized with " + "interface.\n", hc->id, hc); + /* Enable new sync master */ + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + /* switch to jatt PLL, if not disabled by RX_SYNC */ + if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Schedule jatt PLL\n"); + hc->e1_resync |= 2; /* switch to jatt */ + } + } else { + if (pcmmaster) { + hc = pcmmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "id=%d (0x%p) = PCM master syncronized " + "with QUARTZ\n", hc->id, hc); + if (hc->type == 1) { + /* Use the crystal clock for the PCM + master card */ + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule QUARTZ for HFC-E1\n"); + hc->e1_resync |= 4; /* switch quartz */ + } else { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "QUARTZ is automatically " + "enabled by HFC-%dS\n", hc->type); + } + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + } else + if (!rm) + printk(KERN_ERR "%s no pcm master, this MUST " + "not happen!\n", __func__); + } + syncmaster = newmaster; + + spin_unlock(&plx_lock); + spin_unlock_irqrestore(&HFClock, flags); +} + +/* This must be called AND hc must be locked irqsave!!! */ +inline void +plxsd_checksync(struct hfc_multi *hc, int rm) +{ + if (hc->syncronized) { + if (syncmaster == NULL) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_WARNING "%s: GOT sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, hc, rm); + } + } else { + if (syncmaster == hc) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_WARNING "%s: LOST sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, NULL, rm); + } + } +} + + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcmulti(struct hfc_multi *hc) +{ + u_int *plx_acc_32, pv; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + /* soft reset also masks all interrupts */ + hc->hw.r_cirm |= V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); + hc->hw.r_cirm &= ~V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); /* instead of 'wait' that may cause locking */ + + /* release Speech Design card, if PLX was initialized */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: release PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* Termination off */ + pv &= ~PLX_TERM_ON; + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n", + __func__, pv); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* disable memory mapped ports / io ports */ + test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ + pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); + if (hc->pci_membase) + iounmap((void *)hc->pci_membase); + if (hc->plx_membase) + iounmap((void *)hc->plx_membase); + if (hc->pci_iobase) + release_region(hc->pci_iobase, 8); + + if (hc->pci_dev) { + pci_disable_device(hc->pci_dev); + pci_set_drvdata(hc->pci_dev, NULL); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +} + +/* + * function called to reset the HFC chip. A complete software reset of chip + * and fifos is done. All configuration of the chip is done. + */ + +static int +init_chip(struct hfc_multi *hc) +{ + u_long flags, val, val2 = 0, rev; + int i, err = 0; + u_char r_conf_en, rval; + u_int *plx_acc_32, pv; + u_long plx_flags, hfc_flags; + int plx_count; + struct hfc_multi *pos, *next, *plx_last_hc; + + spin_lock_irqsave(&hc->lock, flags); + /* reset all registers */ + memset(&hc->hw, 0, sizeof(struct hfcm_hw)); + + /* revision check */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + val = HFC_inb(hc, R_CHIP_ID)>>4; + if (val != 0x8 && val != 0xc && val != 0xe) { + printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); + err = -EIO; + goto out; + } + rev = HFC_inb(hc, R_CHIP_RV); + printk(KERN_INFO + "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", + val, rev, (rev == 0) ? " (old FIFO handling)" : ""); + if (rev == 0) { + test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); + printk(KERN_WARNING + "HFC_multi: NOTE: Your chip is revision 0, " + "ask Cologne Chip for update. Newer chips " + "have a better FIFO handling. Old chips " + "still work but may have slightly lower " + "HDLC transmit performance.\n"); + } + if (rev > 1) { + printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " + "consider chip revision = %ld. The chip / " + "bridge may not work.\n", rev); + } + + /* set s-ram size */ + hc->Flen = 0x10; + hc->Zmin = 0x80; + hc->Zlen = 384; + hc->DTMFbase = 0x1000; + if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 1; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 1856; + hc->DTMFbase = 0x2000; + } + if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 2; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 8000; + hc->DTMFbase = 0x2000; + } + hc->max_trans = poll << 1; + if (hc->max_trans > hc->Zlen) + hc->max_trans = hc->Zlen; + + /* Speech Design PLX bridge */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* The first and the last cards are terminating the PCM bus */ + pv |= PLX_TERM_ON; /* hc is currently the last */ + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n", + __func__, pv); + /* + * If we are the 3rd PLXSD card or higher, we must turn + * termination of last PLXSD card off. + */ + spin_lock_irqsave(&HFClock, hfc_flags); + plx_count = 0; + plx_last_hc = NULL; + list_for_each_entry_safe(pos, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { + plx_count++; + if (pos != hc) + plx_last_hc = pos; + } + } + if (plx_count >= 3) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: card %d is between, so " + "we disable termination\n", + __func__, plx_last_hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(plx_last_hc->plx_membase + + PLX_GPIOC); + pv = readl(plx_acc_32); + pv &= ~PLX_TERM_ON; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n", + __func__, pv); + } + spin_unlock_irqrestore(&HFClock, hfc_flags); + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + } + + /* we only want the real Z2 read-pointer for revision > 0 */ + if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) + hc->hw.r_ram_sz |= V_FZ_MD; + + /* select pcm mode */ + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into slave mode\n", + __func__); + } else + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into master mode\n", + __func__); + hc->hw.r_pcm_md0 |= V_PCM_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: performing PCM auto detect\n", + __func__); + } + + /* soft reset */ + HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + HFC_outb(hc, R_FIFO_MD, 0); + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + hc->hw.r_cirm = 0; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + + /* Speech Design PLX bridge pcm and sync mode */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + /* Connect PCM */ + if (hc->hw.r_pcm_md0 & V_PCM_MD) { + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n", + __func__, pv); + } else { + pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); + pv &= ~PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n", + __func__, pv); + } + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* PCM setup */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); + if (hc->slots == 32) + HFC_outb(hc, R_PCM_MD1, 0x00); + if (hc->slots == 64) + HFC_outb(hc, R_PCM_MD1, 0x10); + if (hc->slots == 128) + HFC_outb(hc, R_PCM_MD1, 0x20); + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ + else + HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_SLOT, i); + HFC_outb_nodebug(hc, A_SL_CFG, 0); + HFC_outb_nodebug(hc, A_CONF, 0); + hc->slot_owner[i] = -1; + } + + /* set clock speed */ + if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: setting double clock\n", __func__); + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + } + + /* B410P GPIO */ + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + printk(KERN_NOTICE "Setting GPIOs\n"); + HFC_outb(hc, R_GPIO_SEL, 0x30); + HFC_outb(hc, R_GPIO_EN1, 0x3); + udelay(1000); + printk(KERN_NOTICE "calling vpm_init\n"); + vpm_init(hc); + } + + /* check if R_F0_CNT counts (8 kHz frame count) */ + val = HFC_inb(hc, R_F0_CNTL); + val += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after reset\n", val); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", + val2); + if (val2 >= val+8) { /* 1 ms */ + /* it counts, so we keep the pcm mode */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + printk(KERN_INFO "controller is PCM bus MASTER\n"); + else + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) + printk(KERN_INFO "controller is PCM bus SLAVE\n"); + else { + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + printk(KERN_INFO "controller is PCM bus SLAVE " + "(auto detected)\n"); + } + } else { + /* does not count */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { +controller_fail: + printk(KERN_ERR "HFC_multi ERROR, getting no 125us " + "pulse. Seems that controller fails.\n"); + err = -EIO; + goto out; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "controller is PCM bus SLAVE " + "(ignoring missing PCM clock)\n"); + } else { + /* only one pcm master */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && plxsd_master) { + printk(KERN_ERR "HFC_multi ERROR, no clock " + "on another Speech Design card found. " + "Please be sure to connect PCM cable.\n"); + err = -EIO; + goto out; + } + /* retry with master clock */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase + + PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: master: PLX_GPIO" + "=%x\n", __func__, pv); + } + hc->hw.r_pcm_md0 |= V_PCM_MD; + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " + "10 ms (2nd try)\n", val2); + if (val2 >= val+8) { /* 1 ms */ + test_and_set_bit(HFC_CHIP_PCM_MASTER, + &hc->chip); + printk(KERN_INFO "controller is PCM bus MASTER " + "(auto detected)\n"); + } else + goto controller_fail; + } + } + + /* Release the DSP Reset */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + plxsd_master = 1; + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n", + __func__, pv); + } + + /* pcm id */ + if (hc->pcm) + printk(KERN_INFO "controller has given PCM BUS ID %d\n", + hc->pcm); + else { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) + || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + PCM_cnt++; /* SD has proprietary bridging */ + } + hc->pcm = PCM_cnt; + printk(KERN_INFO "controller has PCM BUS ID %d " + "(auto selected)\n", hc->pcm); + } + + /* set up timer */ + HFC_outb(hc, R_TI_WD, poll_timer); + hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; + + /* + * set up 125us interrupt, only if function pointer is available + * and module parameter timer is set + */ + if (timer && hfc_interrupt && register_interrupt) { + /* only one chip should use this interrupt */ + timer = 0; + interrupt_registered = 1; + hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK; + /* deactivate other interrupts in ztdummy */ + register_interrupt(); + } + + /* set E1 state machine IRQ */ + if (hc->type == 1) + hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; + + /* set DTMF detection */ + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: enabling DTMF detection " + "for all B-channel\n", __func__); + hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + hc->hw.r_dtmf |= V_ULAW_SEL; + HFC_outb(hc, R_DTMF_N, 102 - 1); + hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; + } + + /* conference engine */ + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + r_conf_en = V_CONF_EN | V_ULAW; + else + r_conf_en = V_CONF_EN; + HFC_outb(hc, R_CONF_EN, r_conf_en); + + /* setting leds */ + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + HFC_outb(hc, R_GPIO_SEL, 0x32); + else + HFC_outb(hc, R_GPIO_SEL, 0x30); + + HFC_outb(hc, R_GPIO_EN1, 0x0f); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + break; + + case 2: /* HFC-4S OEM */ + case 3: + HFC_outb(hc, R_GPIO_SEL, 0xf0); + HFC_outb(hc, R_GPIO_EN1, 0xff); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + break; + } + + /* set master clock */ + if (hc->masterclk >= 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting ST master clock " + "to port %d (0..%d)\n", + __func__, hc->masterclk, hc->ports-1); + hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC; + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + /* setting misc irq */ + HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", + hc->hw.r_irqmsk_misc); + + /* RAM access test */ + HFC_outb(hc, R_RAM_ADDR0, 0); + HFC_outb(hc, R_RAM_ADDR1, 0); + HFC_outb(hc, R_RAM_ADDR2, 0); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff)); + } + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_inb_nodebug(hc, R_RAM_DATA); + rval = HFC_inb_nodebug(hc, R_INT_DATA); + if (rval != ((i * 3) & 0xff)) { + printk(KERN_DEBUG + "addr:%x val:%x should:%x\n", i, rval, + (i * 3) & 0xff); + err++; + } + } + if (err) { + printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); + err = -EIO; + goto out; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +out: + spin_unlock_irqrestore(&hc->lock, flags); + return err; +} + + +/* + * control the watchdog + */ +static void +hfcmulti_watchdog(struct hfc_multi *hc) +{ + hc->wdcount++; + + if (hc->wdcount > 10) { + hc->wdcount = 0; + hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? + V_GPIO_OUT3 : V_GPIO_OUT2; + + /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); + } +} + + + +/* + * output leds + */ +static void +hfcmulti_leds(struct hfc_multi *hc) +{ + unsigned long lled; + unsigned long leddw; + int i, state, active, leds; + struct dchannel *dch; + int led[4]; + + hc->ledcount += poll; + if (hc->ledcount > 4096) { + hc->ledcount -= 4096; + hc->ledstate = 0xAFFEAFFE; + } + + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + /* 2 red blinking: NT mode deactivate + * 2 red steady: TE mode deactivate + * left green: L1 active + * left red: frame sync, but no L1 + * right green: L2 active + */ + if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */ + if (hc->chan[hc->dslot].dch->dev.D.protocol + != ISDN_P_NT_E1) { + led[0] = 1; + led[1] = 1; + } else if (hc->ledcount>>11) { + led[0] = 1; + led[1] = 1; + } else { + led[0] = 0; + led[1] = 0; + } + led[2] = 0; + led[3] = 0; + } else { /* with frame sync */ + /* TODO make it work */ + led[0] = 0; + led[1] = 0; + led[2] = 0; + led[3] = 1; + } + leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; + /* leds are inverted */ + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); + hc->ledstate = leds; + } + break; + + case 2: /* HFC-4S OEM */ + /* red blinking = PH_DEACTIVATE NT Mode + * red steady = PH_DEACTIVATE TE Mode + * green steady = PH_ACTIVATE + */ + for (i = 0; i < 4; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + } else + if (dch->dev.D.protocol == ISDN_P_TE_S0) + /* TE mode: led red */ + led[i] = 2; + else + if (hc->ledcount>>11) + /* led red */ + led[i] = 2; + else + /* led off */ + led[i] = 0; + } else + led[i] = 0; /* led off */ + } + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + leds = 0; + for (i = 0; i < 4; i++) { + if (led[i] == 1) { + /*green*/ + leds |= (0x2 << (i * 2)); + } else if (led[i] == 2) { + /*red*/ + leds |= (0x1 << (i * 2)); + } + } + if (leds != (int)hc->ledstate) { + vpm_out(hc, 0, 0x1a8 + 3, leds); + hc->ledstate = leds; + } + } else { + leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | + ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | + ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | + ((led[0] & 1) << 6) | ((led[2] & 1) << 7); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); + hc->ledstate = leds; + } + } + break; + + case 3: /* HFC 1S/2S Beronet */ + /* red blinking = PH_DEACTIVATE NT Mode + * red steady = PH_DEACTIVATE TE Mode + * green steady = PH_ACTIVATE + */ + for (i = 0; i < 2; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + } else + if (dch->dev.D.protocol == ISDN_P_TE_S0) + /* TE mode: led red */ + led[i] = 2; + else + if (hc->ledcount >> 11) + /* led red */ + led[i] = 2; + else + /* led off */ + led[i] = 0; + } else + led[i] = 0; /* led off */ + } + + + leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2) + | ((led[1]&1)<<3); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, + ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); + HFC_outb_nodebug(hc, R_GPIO_OUT1, + ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); + hc->ledstate = leds; + } + break; + case 8: /* HFC 8S+ Beronet */ + lled = 0; + + for (i = 0; i < 8; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + lled |= 0 << i; + } else + if (hc->ledcount >> 11) + lled |= 0 << i; + else + lled |= 1 << i; + } else + lled |= 1 << i; + } + leddw = lled << 24 | lled << 16 | lled << 8 | lled; + if (leddw != hc->ledstate) { + /* HFC_outb(hc, R_BRG_PCM_CFG, 1); + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ + /* was _io before */ + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + outw(0x4000, hc->pci_iobase + 4); + outl(leddw, hc->pci_iobase); + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); + hc->ledstate = leddw; + } + break; + } +} +/* + * read dtmf coefficients + */ + +static void +hfcmulti_dtmf(struct hfc_multi *hc) +{ + s32 *coeff; + u_int mantissa; + int co, ch; + struct bchannel *bch = NULL; + u8 exponent; + int dtmf = 0; + int addr; + u16 w_float; + struct sk_buff *skb; + struct mISDNhead *hh; + + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); + for (ch = 0; ch <= 31; ch++) { + /* only process enabled B-channels */ + bch = hc->chan[ch].bch; + if (!bch) + continue; + if (!hc->created[hc->chan[ch].port]) + continue; + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + continue; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf channel %d:", + __func__, ch); + coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); + dtmf = 1; + for (co = 0; co < 8; co++) { + /* read W(n-1) coefficient */ + addr = hc->DTMFbase + ((co<<7) | (ch<<2)); + HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); + HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8); + HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16) + | V_ADDR_INC); + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[co<<1] = mantissa; + + /* read W(n) coefficient */ + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[(co<<1)|1] = mantissa; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk("%s: DTMF ready %08x %08x %08x %08x " + "%08x %08x %08x %08x\n", __func__, + coeff[0], coeff[1], coeff[2], coeff[3], + coeff[4], coeff[5], coeff[6], coeff[7]); + hc->chan[ch].coeff_count++; + if (hc->chan[ch].coeff_count == 8) { + hc->chan[ch].coeff_count = 0; + skb = mI_alloc_skb(512, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: No memory for skb\n", + __func__); + continue; + } + hh = mISDN_HEAD_P(skb); + hh->prim = PH_CONTROL_IND; + hh->id = DTMF_HFC_COEF; + memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512); + recv_Bchannel_skb(bch, skb); + } + } + + /* restart DTMF processing */ + hc->dtmf = dtmf; + if (dtmf) + HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); +} + + +/* + * fill fifo as much as possible + */ + +static void +hfcmulti_tx(struct hfc_multi *hc, int ch) +{ + int i, ii, temp, len = 0; + int Zspace, z1, z2; /* must be int for calculation */ + int Fspace, f1, f2; + u_char *d; + int *txpending, slot_tx; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff **sp = NULL; + int *idxp; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + + txpending = &hc->chan[ch].txpending; + slot_tx = hc->chan[ch].slot_tx; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->tx_skb; + idxp = &dch->tx_idx; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->tx_skb; + idxp = &bch->tx_idx; + } + if (*sp) + len = (*sp)->len; + + if ((!len) && *txpending != 1) + return; /* no data */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); + else + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + + if (*txpending == 2) { + /* reset fifo */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_SUBCH_CFG, 0); + *txpending = 1; + } +next_frame: + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + f2 = HFC_inb_nodebug(hc, A_F2); + while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f2 because %d!=%d\n", + __func__, hc->id + 1, temp, f2); + f2 = temp; /* repeat until F2 is equal */ + } + Fspace = f2 - f1 - 1; + if (Fspace < 0) + Fspace += hc->Flen; + /* + * Old FIFO handling doesn't give us the current Z2 read + * pointer, so we cannot send the next frame before the fifo + * is empty. It makes no difference except for a slightly + * lower performance. + */ + if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { + if (f1 != f2) + Fspace = 0; + else + Fspace = 1; + } + /* one frame only for ST D-channels, to allow resending */ + if (hc->type != 1 && dch) { + if (f1 != f2) + Fspace = 0; + } + /* F-counter full condition */ + if (Fspace == 0) + return; + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z2 = temp; /* repeat unti Z2 is equal */ + } + Zspace = z2 - z1; + if (Zspace <= 0) + Zspace += hc->Zlen; + Zspace -= 4; /* keep not too full, so pointers will not overrun */ + /* fill transparent data only to maxinum transparent load (minus 4) */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + Zspace = Zspace - hc->Zlen + hc->max_trans; + if (Zspace <= 0) /* no space of 4 bytes */ + return; + + /* if no data */ + if (!len) { + if (z1 == z2) { /* empty */ + /* if done with FIFO audio data during PCM connection */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && + *txpending && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: reconnecting PCM due to no " + "more FIFO data: channel %d " + "slot_tx %d\n", + __func__, ch, slot_tx); + /* connect slot */ + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1); + HFC_wait_nodebug(hc); + } + *txpending = 0; + } + return; /* no data */ + } + + /* if audio data and connected slot */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) + && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to " + "FIFO data: channel %d slot_tx %d\n", + __func__, ch, slot_tx); + /* disconnect slot */ + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1); + HFC_wait_nodebug(hc); + } + *txpending = 1; + + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* fill fifo to what we have left */ + ii = len; + if (dch || test_bit(FLG_HDLC, &bch->Flags)) + temp = 1; + else + temp = 0; + i = *idxp; + d = (*sp)->data + i; + if (ii - i > Zspace) + ii = Zspace + i; + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " + "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", + __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, + temp ? "HDLC":"TRANS"); + + + /* Have to prep the audio data */ + hc->write_fifo(hc, d, ii - i); + *idxp = ii; + + /* if not all data has been written */ + if (ii != len) { + /* NOTE: fifo is started by the calling function */ + return; + } + + /* if all data has been written, terminate frame */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + /* increment f-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + } + + /* send confirm, since get_net_bframe will not do it with trans */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); + + /* check for next frame */ + dev_kfree_skb(*sp); + if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */ + len = (*sp)->len; + goto next_frame; + } + if (dch && get_next_dframe(dch)) { + len = (*sp)->len; + goto next_frame; + } + + /* + * now we have no more data, so in case of transparent, + * we set the last byte in fifo to 'silence' in case we will get + * no more data at all. this prevents sending an undefined value. + */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); +} + + +/* NOTE: only called if E1 card is in active state */ +static void +hfcmulti_rx(struct hfc_multi *hc, int ch) +{ + int temp; + int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ + int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ + int again = 0; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff *skb, **sp = NULL; + int maxlen; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->rx_skb; + maxlen = dch->maxlen; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->rx_skb; + maxlen = bch->maxlen; + } +next_frame: + /* on first AND before getting next valid frame, R_FIFO must be written + to. */ + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1); + else + HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1); + HFC_wait_nodebug(hc); + + /* ignore if rx is off BUT change fifo (above) to start pending TX */ + if (hc->chan[ch].rx_off) + return; + + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f1 because %d!=%d\n", + __func__, hc->id + 1, temp, f1); + f1 = temp; /* repeat until F1 is equal */ + } + f2 = HFC_inb_nodebug(hc, A_F2); + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z1 = temp; /* repeat until Z1 is equal */ + } + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + Zsize = z1 - z2; + if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) + /* complete hdlc frame */ + Zsize++; + if (Zsize < 0) + Zsize += hc->Zlen; + /* if buffer is empty */ + if (Zsize <= 0) + return; + + if (*sp == NULL) { + *sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC); + if (*sp == NULL) { + printk(KERN_DEBUG "%s: No mem for rx_skb\n", + __func__); + return; + } + } + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* empty fifo with what we have */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " + "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " + "got=%d (again %d)\n", __func__, hc->id + 1, ch, + Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", + f1, f2, Zsize + (*sp)->len, again); + /* HDLC */ + if ((Zsize + (*sp)->len) > (maxlen + 3)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): hdlc-frame too large.\n", + __func__, hc->id + 1); + skb_trim(*sp, 0); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + return; + } + + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + + if (f1 != f2) { + /* increment Z2,F2-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + /* check size */ + if ((*sp)->len < 4) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): Frame below minimum " + "size\n", __func__, hc->id + 1); + skb_trim(*sp, 0); + goto next_frame; + } + /* there is at least one complete frame, check crc */ + if ((*sp)->data[(*sp)->len - 1]) { + if (debug & DEBUG_HFCMULTI_CRC) + printk(KERN_DEBUG + "%s: CRC-error\n", __func__); + skb_trim(*sp, 0); + goto next_frame; + } + skb_trim(*sp, (*sp)->len - 3); + if ((*sp)->len < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + memcpy(skb_put(*sp, skb->len), + skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", + __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) { + printk(KERN_DEBUG "%s(card %d):", + __func__, hc->id + 1); + temp = 0; + while (temp < (*sp)->len) + printk(" %02x", (*sp)->data[temp++]); + printk("\n"); + } + if (dch) + recv_Dchannel(dch); + else + recv_Bchannel(bch); + *sp = skb; + again++; + goto next_frame; + } + /* there is an incomplete frame */ + } else { + /* transparent */ + if (Zsize > skb_tailroom(*sp)) + Zsize = skb_tailroom(*sp); + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + if (((*sp)->len) < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + memcpy(skb_put(*sp, skb->len), + skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): fifo(%d) reading %d bytes " + "(z1=%04x, z2=%04x) TRANS\n", + __func__, hc->id + 1, ch, Zsize, z1, z2); + /* only bch is transparent */ + recv_Bchannel(bch); + *sp = skb; + } +} + + +/* + * Interrupt handler + */ +static void +signal_state_up(struct dchannel *dch, int info, char *msg) +{ + struct sk_buff *skb; + int id, data = info; + + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: %s\n", __func__, msg); + + id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ + + skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, + GFP_ATOMIC); + if (!skb) + return; + recv_Dchannel_skb(dch, skb); +} + +static inline void +handle_timer_irq(struct hfc_multi *hc) +{ + int ch, temp; + struct dchannel *dch; + u_long flags; + + /* process queued resync jobs */ + if (hc->e1_resync) { + /* lock, so e1_resync gets not changed */ + spin_lock_irqsave(&HFClock, flags); + if (hc->e1_resync & 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable SYNC_I\n"); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); + /* disable JATT, if RX_SYNC is set */ + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + } + if (hc->e1_resync & 2) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable jatt PLL\n"); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } + if (hc->e1_resync & 4) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Enable QUARTZ for HFC-E1\n"); + /* set jatt to quartz */ + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC + | V_JATT_OFF); + /* switch to JATT, in case it is not already */ + HFC_outb(hc, R_SYNC_OUT, 0); + } + hc->e1_resync = 0; + spin_unlock_irqrestore(&HFClock, flags); + } + + if (hc->type != 1 || hc->e1_state == 1) + for (ch = 0; ch <= 31; ch++) { + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch); + if (hc->chan[ch].dch && + hc->chan[ch].nt_timer > -1) { + dch = hc->chan[ch].dch; + if (!(--hc->chan[ch].nt_timer)) { + schedule_event(dch, + FLG_PHCHANGE); + if (debug & + DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: nt_timer at " + "state %x\n", + __func__, + dch->state); + } + } + } + } + if (hc->type == 1 && hc->created[0]) { + dch = hc->chan[hc->dslot].dch; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { + /* LOS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; + if (!temp && hc->chan[hc->dslot].los) + signal_state_up(dch, L1_SIGNAL_LOS_ON, + "LOS detected"); + if (temp && !hc->chan[hc->dslot].los) + signal_state_up(dch, L1_SIGNAL_LOS_OFF, + "LOS gone"); + hc->chan[hc->dslot].los = temp; + } + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) { + /* AIS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; + if (!temp && hc->chan[hc->dslot].ais) + signal_state_up(dch, L1_SIGNAL_AIS_ON, + "AIS detected"); + if (temp && !hc->chan[hc->dslot].ais) + signal_state_up(dch, L1_SIGNAL_AIS_OFF, + "AIS gone"); + hc->chan[hc->dslot].ais = temp; + } + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) { + /* SLIP */ + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; + if (!temp && hc->chan[hc->dslot].slip_rx) + signal_state_up(dch, L1_SIGNAL_SLIP_RX, + " bit SLIP detected RX"); + hc->chan[hc->dslot].slip_rx = temp; + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; + if (!temp && hc->chan[hc->dslot].slip_tx) + signal_state_up(dch, L1_SIGNAL_SLIP_TX, + " bit SLIP detected TX"); + hc->chan[hc->dslot].slip_tx = temp; + } + if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) { + /* RDI */ + temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; + if (!temp && hc->chan[hc->dslot].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_ON, + "RDI detected"); + if (temp && !hc->chan[hc->dslot].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_OFF, + "RDI gone"); + hc->chan[hc->dslot].rdi = temp; + } + temp = HFC_inb_nodebug(hc, R_JATT_DIR); + switch (hc->chan[hc->dslot].sync) { + case 0: + if ((temp & 0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 now " + "in clock sync\n", + __func__, hc->id); + HFC_outb(hc, R_RX_OFF, + hc->chan[hc->dslot].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, + hc->chan[hc->dslot].jitter | V_RX_INIT); + hc->chan[hc->dslot].sync = 1; + goto check_framesync; + } + break; + case 1: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost clock sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 0; + break; + } +check_framesync: + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp == 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "now in frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 2; + } + break; + case 2: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 lost " + "clock & frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 0; + break; + } + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp != 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 1; + } + break; + } + } + + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + hfcmulti_watchdog(hc); + + if (hc->leds) + hfcmulti_leds(hc); +} + +static void +ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) +{ + struct dchannel *dch; + int ch; + int active; + u_char st_status, temp; + + /* state machine */ + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (r_irq_statech & 1) { + HFC_outb_nodebug(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* undocumented: status changes during read */ + st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); + while (st_status != (temp = + HFC_inb_nodebug(hc, A_ST_RD_STATE))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + st_status); + st_status = temp; /* repeat */ + } + + /* Speech Design TE-sync indication */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && + dch->dev.D.protocol == ISDN_P_TE_S0) { + if (st_status & V_FR_SYNC_ST) + hc->syncronized |= + (1 << hc->chan[ch].port); + else + hc->syncronized &= + ~(1 << hc->chan[ch].port); + } + dch->state = st_status & 0x0f; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + if (dch->state == active) { + HFC_outb_nodebug(hc, R_FIFO, + (ch << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + dch->tx_idx = 0; + } + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T newstate %x port %d\n", + __func__, dch->state, + hc->chan[ch].port); + } + r_irq_statech >>= 1; + } + } + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); +} + +static void +fifo_irq(struct hfc_multi *hc, int block) +{ + int ch, j; + struct dchannel *dch; + struct bchannel *bch; + u_char r_irq_fifo_bl; + + r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); + j = 0; + while (j < 8) { + ch = (block << 2) + (j >> 1); + dch = hc->chan[ch].dch; + bch = hc->chan[ch].bch; + if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { + j += 2; + continue; + } + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + j++; + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_rx(hc, ch); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_rx(hc, ch); + } + j++; + } +} + +#ifdef IRQ_DEBUG +int irqsem; +#endif +static irqreturn_t +hfcmulti_interrupt(int intno, void *dev_id) +{ +#ifdef IRQCOUNT_DEBUG + static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, + iq5 = 0, iq6 = 0, iqcnt = 0; +#endif + static int count; + struct hfc_multi *hc = dev_id; + struct dchannel *dch; + u_char r_irq_statech, status, r_irq_misc, r_irq_oview; + int i; + u_short *plx_acc, wval; + u_char e1_syncsta, temp; + u_long flags; + + if (!hc) { + printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); + return IRQ_NONE; + } + + spin_lock(&hc->lock); + +#ifdef IRQ_DEBUG + if (irqsem) + printk(KERN_ERR "irq for card %d during irq from " + "card %d, this is no bug.\n", hc->id + 1, irqsem); + irqsem = hc->id + 1; +#endif + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, flags); + plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR); + wval = readw(plx_acc); + spin_unlock_irqrestore(&plx_lock, flags); + if (!(wval & PLX_INTCSR_LINTI1_STATUS)) + goto irq_notforus; + } + + status = HFC_inb_nodebug(hc, R_STATUS); + r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); +#ifdef IRQCOUNT_DEBUG + if (r_irq_statech) + iq1++; + if (status & V_DTMF_STA) + iq2++; + if (status & V_LOST_STA) + iq3++; + if (status & V_EXT_IRQSTA) + iq4++; + if (status & V_MISC_IRQSTA) + iq5++; + if (status & V_FR_IRQSTA) + iq6++; + if (iqcnt++ > 5000) { + printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", + iq1, iq2, iq3, iq4, iq5, iq6); + iqcnt = 0; + } +#endif + if (!r_irq_statech && + !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | + V_MISC_IRQSTA | V_FR_IRQSTA))) { + /* irq is not for us */ + goto irq_notforus; + } + hc->irqcnt++; + if (r_irq_statech) { + if (hc->type != 1) + ph_state_irq(hc, r_irq_statech); + } + if (status & V_EXT_IRQSTA) + ; /* external IRQ */ + if (status & V_LOST_STA) { + /* LOST IRQ */ + HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ + } + if (status & V_MISC_IRQSTA) { + /* misc IRQ */ + r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + if (r_irq_misc & V_STA_IRQ) { + if (hc->type == 1) { + /* state machine */ + dch = hc->chan[hc->dslot].dch; + e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && hc->e1_getclock) { + if (e1_syncsta & V_FR_SYNC_E1) + hc->syncronized = 1; + else + hc->syncronized = 0; + } + /* undocumented: status changes during read */ + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA); + while (dch->state != (temp = + HFC_inb_nodebug(hc, R_E1_RD_STA))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + dch->state); + dch->state = temp; /* repeat */ + } + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) + & 0x7; + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); + } + } + if (r_irq_misc & V_TI_IRQ) + handle_timer_irq(hc); + + if (r_irq_misc & V_DTMF_IRQ) { + /* -> DTMF IRQ */ + hfcmulti_dtmf(hc); + } + /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable */ + if (r_irq_misc & V_IRQ_PROC) { + /* IRQ every 125us */ + count++; + /* generate 1kHz signal */ + if (count == 8) { + if (hfc_interrupt) + hfc_interrupt(); + count = 0; + } + } + + } + if (status & V_FR_IRQSTA) { + /* FIFO IRQ */ + r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); + for (i = 0; i < 8; i++) { + if (r_irq_oview & (1 << i)) + fifo_irq(hc, i); + } + } + +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_HANDLED; + +irq_notforus: +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_NONE; +} + + +/* + * timer callback for D-chan busy resolution. Currently no function + */ + +static void +hfcmulti_dbusy_timer(struct hfc_multi *hc) +{ +} + + +/* + * activate/deactivate hardware for selected channels and mode + * + * configure B-channel with the given protocol + * ch eqals to the HFC-channel (0-31) + * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 + * for S/T, 1-31 for E1) + * the hdlc interrupts will be set/unset + */ +static int +mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, + int bank_tx, int slot_rx, int bank_rx) +{ + int flow_tx = 0, flow_rx = 0, routing = 0; + int oslot_tx, oslot_rx; + int conf; + + if (ch < 0 || ch > 31) + return EINVAL; + oslot_tx = hc->chan[ch].slot_tx; + oslot_rx = hc->chan[ch].slot_rx; + conf = hc->chan[ch].conf; + + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: card %d channel %d protocol %x slot old=%d new=%d " + "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", + __func__, hc->id, ch, protocol, oslot_tx, slot_tx, + bank_tx, oslot_rx, slot_rx, bank_rx); + + if (oslot_tx >= 0 && slot_tx != oslot_tx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", + __func__, oslot_tx); + if (hc->slot_owner[oslot_tx<<1] == ch) { + HFC_outb(hc, R_SLOT, oslot_tx << 1); + HFC_outb(hc, A_SL_CFG, 0); + HFC_outb(hc, A_CONF, 0); + hc->slot_owner[oslot_tx<<1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this tx slot " + "anymore, channel %d is.\n", + __func__, hc->slot_owner[oslot_tx<<1]); + } + } + + if (oslot_rx >= 0 && slot_rx != oslot_rx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: remove from slot %d (RX)\n", + __func__, oslot_rx); + if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { + HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, 0); + hc->slot_owner[(oslot_rx << 1) | 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this rx slot " + "anymore, channel %d is.\n", + __func__, + hc->slot_owner[(oslot_rx << 1) | 1]); + } + } + + if (slot_tx < 0) { + flow_tx = 0x80; /* FIFO->ST */ + /* disable pcm slot */ + hc->chan[ch].slot_tx = -1; + hc->chan[ch].bank_tx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_tx = 0x80; /* FIFO->ST */ + else + flow_tx = 0xc0; /* PCM->ST */ + /* put on slot */ + routing = bank_tx ? 0xc0 : 0x80; + if (conf >= 0 || bank_tx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (TX)\n", + __func__, ch, slot_tx, bank_tx, + flow_tx, routing, conf); + HFC_outb(hc, R_SLOT, slot_tx << 1); + HFC_outb(hc, A_SL_CFG, (ch<<1) | routing); + HFC_outb(hc, A_CONF, (conf < 0) ? 0 : (conf | V_CONF_SL)); + hc->slot_owner[slot_tx << 1] = ch; + hc->chan[ch].slot_tx = slot_tx; + hc->chan[ch].bank_tx = bank_tx; + } + if (slot_rx < 0) { + /* disable pcm slot */ + flow_rx = 0x80; /* ST->FIFO */ + hc->chan[ch].slot_rx = -1; + hc->chan[ch].bank_rx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_rx = 0x80; /* ST->FIFO */ + else + flow_rx = 0xc0; /* ST->(FIFO,PCM) */ + /* put on slot */ + routing = bank_rx?0x80:0xc0; /* reversed */ + if (conf >= 0 || bank_rx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (RX)\n", + __func__, ch, slot_rx, bank_rx, + flow_rx, routing, conf); + HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing); + hc->slot_owner[(slot_rx<<1)|1] = ch; + hc->chan[ch].slot_rx = slot_rx; + hc->chan[ch].bank_rx = bank_rx; + } + + switch (protocol) { + case (ISDN_P_NONE): + /* disable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* disable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch && hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] &= + ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) { + test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + } + break; + case (ISDN_P_B_RAW): /* B-channel */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) { + + printk(KERN_DEBUG + "Setting B-channel %d to echo cancelable " + "state on PCM slot %d\n", ch, + ((ch / 4) * 8) + ((ch % 4) * 4) + 1); + printk(KERN_DEBUG + "Enabling pass through for channel\n"); + vpm_out(hc, ch, ((ch / 4) * 8) + + ((ch % 4) * 4) + 1, 0x01); + /* rx path */ + /* S/T -> PCM */ + HFC_outb(hc, R_FIFO, (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); + + /* PCM -> FIFO */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); + + /* tx path */ + /* PCM -> S/T */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4)) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); + + /* FIFO -> PCM */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4)) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); + } else { + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) + test_and_set_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + break; + case (ISDN_P_B_HDLC): /* B-channel */ + case (ISDN_P_TE_S0): /* D-channel */ + case (ISDN_P_NT_S0): + case (ISDN_P_TE_E1): + case (ISDN_P_NT_E1): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + if (hc->type == 1 || hc->chan[ch].bch) { + /* E1 or B-channel */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + } else { + /* D-Channel without HDLC fill flags */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 2); + } + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + if (hc->type == 1 || hc->chan[ch].bch) + HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ + else + HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch) { + test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch&0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + } + break; + default: + printk(KERN_DEBUG "%s: protocol not known %x\n", + __func__, protocol); + hc->chan[ch].protocol = ISDN_P_NONE; + return -ENOPROTOOPT; + } + hc->chan[ch].protocol = protocol; + return 0; +} + + +/* + * connect/disconnect PCM + */ + +static void +hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, + int slot_rx, int bank_rx) +{ + if (slot_rx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { + /* disable PCM */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); + return; + } + + /* enable pcm */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, + slot_rx, bank_rx); +} + +/* + * set/disable conference + */ + +static void +hfcmulti_conf(struct hfc_multi *hc, int ch, int num) +{ + if (num >= 0 && num <= 7) + hc->chan[ch].conf = num; + else + hc->chan[ch].conf = -1; + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, + hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, + hc->chan[ch].bank_rx); +} + + +/* + * set/disable sample loop + */ + +/* NOTE: this function is experimental and therefore disabled */ + +/* + * Layer 1 callback function + */ +static int +hfcm_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_multi *hc = dch->hw; + u_long flags; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + break; + case HW_RESET_REQ: + /* start activation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_RESET_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); + HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3)); + /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + /* start deactivation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_DEACT_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); + /* deactivate */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_POWERUP_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Layer2 -> Layer 1 Transfer + */ + +static int +handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len < 1) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, dch->slot); + ret = 0; + /* start fifo */ + HFC_outb(hc, R_FIFO, 0); + HFC_wait(hc); + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + ret = 0; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_ACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports-1); + /* start activation */ + if (hc->type == 1) { + ph_state_change(dch); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 report state %x \n", + __func__, dch->state); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); + /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | + (V_ST_ACT*3)); /* activate */ + dch->state = 1; + } + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports-1); + /* start deactivation */ + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + dch->state = 1; + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_multi *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + hc->chan[bch->slot].coeff_count = 0; + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + hc->chan[bch->slot].rx_off = 0; + hc->chan[bch->slot].conf = -1; + mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0); + spin_unlock_irqrestore(&hc->lock, flags); +} + +static int +handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (!skb->len) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, bch->slot); + ret = 0; + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) { + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", + __func__, bch->slot); + spin_lock_irqsave(&hc->lock, flags); + /* activate B-channel if not already activated */ + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hc->chan[bch->slot].txpending = 0; + ret = mode_hfcmulti(hc, bch->slot, + ch->protocol, + hc->chan[bch->slot].slot_tx, + hc->chan[bch->slot].bank_tx, + hc->chan[bch->slot].slot_rx, + hc->chan[bch->slot].bank_rx); + if (!ret) { + if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf + && test_bit(HFC_CHIP_DTMF, &hc->chip)) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG + "%s: start dtmf decoder\n", + __func__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | + V_RST_DTMF); + } + } + } else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + break; + case PH_CONTROL_REQ: + spin_lock_irqsave(&hc->lock, flags); + switch (hh->id) { + case HFC_SPL_LOOP_ON: /* set sample loop */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_SPL_LOOP_ON (len = %d)\n", + __func__, skb->len); + ret = 0; + break; + case HFC_SPL_LOOP_OFF: /* set silence */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", + __func__); + ret = 0; + break; + default: + printk(KERN_ERR + "%s: unknown PH_CONTROL_REQ info %x\n", + __func__, hh->id); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); /* locked there */ + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * bchannel control function + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct dsp_features *features = + (struct dsp_features *)(*((u_long *)&cq->p1)); + struct hfc_multi *hc = bch->hw; + int slot_tx; + int bank_tx; + int slot_rx; + int bank_rx; + int num; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP + | MISDN_CTRL_RX_OFF; + break; + case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ + hc->chan[bch->slot].rx_off = !!cq->p1; + if (!hc->chan[bch->slot].rx_off) { + /* reset fifo on rx on */ + HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", + __func__, bch->nr, hc->chan[bch->slot].rx_off); + break; + case MISDN_CTRL_HW_FEATURES: /* fill features structure */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", + __func__); + /* create confirm */ + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + features->hfc_loops = 0; + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + features->hfc_echocanhw = 1; + } else { + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + } + break; + case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ + slot_tx = cq->p1 & 0xff; + bank_tx = cq->p1 >> 8; + slot_rx = cq->p2 & 0xff; + bank_rx = cq->p2 >> 8; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX)\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + if (slot_tx < hc->slots && bank_tx <= 2 && + slot_rx < hc->slots && bank_rx <= 2) + hfcmulti_pcm(hc, bch->slot, + slot_tx, bank_tx, slot_rx, bank_rx); + else { + printk(KERN_WARNING + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX) out of range\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", + __func__); + hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); + break; + case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ + num = cq->p1 & 0xff; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", + __func__, num); + if (num <= 7) + hfcmulti_conf(hc, bch->slot, num); + else { + printk(KERN_WARNING + "%s: HW_CONF_JOIN conf %d out of range\n", + __func__, num); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); + hfcmulti_conf(hc, bch->slot, -1); + break; + case MISDN_CTRL_HFC_ECHOCAN_ON: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_on(hc, bch->slot, cq->p1); + else + ret = -EINVAL; + break; + + case MISDN_CTRL_HFC_ECHOCAN_OFF: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", + __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_off(hc, bch->slot); + else + ret = -EINVAL; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int err = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + if (test_bit(FLG_ACTIVE, &bch->Flags)) + deactivate_bchannel(bch); /* locked there */ + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + err = 0; + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_bctrl(bch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return err; +} + +/* + * handle D-channel events + * + * handle state change event + */ +static void +ph_state_change(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + int ch, i; + + if (!dch) { + printk(KERN_WARNING "%s: ERROR given dch is NULL\n", + __func__); + return; + } + ch = dch->slot; + + if (hc->type == 1) { + if (dch->dev.D.protocol == ISDN_P_TE_E1) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 TE (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 NT (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } + switch (dch->state) { + case (1): + if (hc->e1_state != 1) { + for (i = 1; i <= 31; i++) { + /* reset fifos on e1 activation */ + HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + } + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + + default: + if (hc->e1_state != 1) + return; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } + hc->e1_state = dch->state; + } else { + if (dch->dev.D.protocol == ISDN_P_TE_S0) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (0): + l1_event(dch->l1, HW_RESET_IND); + break; + case (3): + l1_event(dch->l1, HW_DEACT_IND); + break; + case (5): + case (8): + l1_event(dch->l1, ANYSIGNAL); + break; + case (6): + l1_event(dch->l1, INFO2); + break; + case (7): + l1_event(dch->l1, INFO4_P8); + break; + } + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (2): + if (hc->chan[ch].nt_timer == 0) { + hc->chan[ch].nt_timer = -1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 4 | + V_ST_LD_STA); /* G4 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 4); + dch->state = 4; + } else { + /* one extra count for the next event */ + hc->chan[ch].nt_timer = + nt_t1_count[poll_timer] + 1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* allow G2 -> G3 transition */ + HFC_outb(hc, A_ST_WR_STATE, 2 | + V_SET_G2_G3); + } + break; + case (1): + hc->chan[ch].nt_timer = -1; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case (4): + hc->chan[ch].nt_timer = -1; + break; + case (3): + hc->chan[ch].nt_timer = -1; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + } + } +} + +/* + * called for card mode init message + */ + +static void +hfcmulti_initmode(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + u_char a_st_wr_state, r_e1_wr_sta; + int i, pt; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + if (hc->type == 1) { + hc->chan[hc->dslot].slot_tx = -1; + hc->chan[hc->dslot].slot_rx = -1; + hc->chan[hc->dslot].conf = -1; + if (hc->dslot) { + mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol, + -1, 0, -1, 0); + dch->timer.function = (void *) hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + } + for (i = 1; i <= 31; i++) { + if (i == hc->dslot) + continue; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); + } + /* E1 */ + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) { + HFC_outb(hc, R_RX0, 0); + hc->hw.r_tx0 = 0 | V_OUT_EN; + } else { + HFC_outb(hc, R_RX0, 1); + hc->hw.r_tx0 = 1 | V_OUT_EN; + } + hc->hw.r_tx1 = V_ATX | V_NTRI; + HFC_outb(hc, R_TX0, hc->hw.r_tx0); + HFC_outb(hc, R_TX1, hc->hw.r_tx1); + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + + if (dch->dev.D.protocol == ISDN_P_NT_E1) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is NT-mode\n", + __func__); + r_e1_wr_sta = 0; /* G0 */ + hc->e1_getclock = 0; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is TE-mode\n", + __func__); + r_e1_wr_sta = 0; /* F0 */ + hc->e1_getclock = 1; + } + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + else + HFC_outb(hc, R_SYNC_OUT, 0); + if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) + hc->e1_getclock = 1; + if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) + hc->e1_getclock = 0; + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + /* SLAVE (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock master " + "(clock from PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); + } else { + if (hc->e1_getclock) { + /* MASTER (clock slave) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock slave " + "(clock to PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } else { + /* MASTER (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is " + "clock master " + "(clock from QUARTZ)\n", + __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | + V_PCM_SYNC | V_JATT_OFF); + HFC_outb(hc, R_SYNC_OUT, 0); + } + } + HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ + HFC_outb(hc, R_PWM_MD, V_PWM0_MD); + HFC_outb(hc, R_PWM0, 0x50); + HFC_outb(hc, R_PWM1, 0xff); + /* state machine setup */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 0); + } + } else { + i = dch->slot; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); + dch->timer.function = (void *)hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + hc->chan[i - 2].slot_tx = -1; + hc->chan[i - 2].slot_rx = -1; + hc->chan[i - 2].conf = -1; + mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); + hc->chan[i - 1].slot_tx = -1; + hc->chan[i - 1].slot_rx = -1; + hc->chan[i - 1].conf = -1; + mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); + /* ST */ + pt = hc->chan[i].port; + /* select interface */ + HFC_outb(hc, R_ST_SEL, pt); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + if (dch->dev.D.protocol == ISDN_P_NT_S0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is NT-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); + a_st_wr_state = 1; /* G1 */ + hc->hw.a_st_ctrl0[pt] = V_ST_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is TE-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); + a_st_wr_state = 2; /* F2 */ + hc->hw.a_st_ctrl0[pt] = 0; + } + if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) + hc->hw.a_st_ctrl0[pt] |= V_TX_LI; + /* line setup */ + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); + /* disable E-channel */ + if ((dch->dev.D.protocol == ISDN_P_NT_S0) || + test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) + HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); + else + HFC_outb(hc, A_ST_CTRL1, 0); + /* enable B-channel receive */ + HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + /* state machine setup */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); + hc->hw.r_sci_msk |= 1 << pt; + /* state machine interrupts */ + HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); + /* unset sync on port */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: done\n", __func__); +} + + +static int +open_dchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + int err = 0; + u_long flags; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + dch->dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && + (dch->dev.D.protocol != rq->protocol)) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_WARNING "%s: change protocol %x to %x\n", + __func__, dch->dev.D.protocol, rq->protocol); + } + if ((dch->dev.D.protocol == ISDN_P_TE_S0) + && (rq->protocol != ISDN_P_TE_S0)) + l1_event(dch->l1, CLOSE_CHANNEL); + if (dch->dev.D.protocol != rq->protocol) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(dch, hfcm_l1callback); + if (err) + return err; + } + dch->dev.D.protocol = rq->protocol; + spin_lock_irqsave(&hc->lock, flags); + hfcmulti_initmode(dch); + spin_unlock_irqrestore(&hc->lock, flags); + } + + if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) || + ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) || + ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) || + ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) { + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = &dch->dev.D; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + struct bchannel *bch; + int ch; + + if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0])) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (hc->type == 1) + ch = rq->adr.channel; + else + ch = (rq->adr.channel - 1) + (dch->slot - 2); + bch = hc->chan[ch].bch; + if (!bch) { + printk(KERN_ERR "%s:internal error ch %d has no bch\n", + __func__, ch); + return -EINVAL; + } + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + hc->chan[ch].rx_off = 0; + rq->ch = &bch->ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = 0; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct channel_req *rq; + int err = 0; + u_long flags; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + switch (rq->protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + if (hc->type == 1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (hc->type != 1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + default: + spin_lock_irqsave(&hc->lock, flags); + err = open_bchannel(hc, dch, rq); + spin_unlock_irqrestore(&hc->lock, flags); + } + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_dctrl(dch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + err = -EINVAL; + } + return err; +} + +/* + * initialize the card + */ + +/* + * start timer irq, wait some time and check if we have interrupts. + * if not, reset chip and try again. + */ +static int +init_card(struct hfc_multi *hc) +{ + int err = -EIO; + u_long flags; + u_short *plx_acc; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + spin_lock_irqsave(&hc->lock, flags); + /* set interrupts but leave global interrupt disabled */ + hc->hw.r_irq_ctrl = V_FIFO_IRQ; + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED, + "HFC-multi", hc)) { + printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", + hc->pci_dev->irq); + return -EIO; + } + hc->irq = hc->pci_dev->irq; + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR); + writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), + plx_acc); /* enable PCI & LINT1 irq */ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + err = init_chip(hc); + if (err) + goto error; + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is allready + * established for this HFC, so don't do that earlier + */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* printk(KERN_DEBUG "no master irq set!!!\n"); */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + /* turn IRQ off until chip is completely initialized */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + if (hc->irqcnt) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); + + return 0; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "ignoring missing interrupts\n"); + return 0; + } + + printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", + hc->irq); + + err = -EIO; + +error: + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR); + writew(0x00, plx_acc); /*disable IRQs*/ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq); + if (hc->irq) { + free_irq(hc->irq, hc); + hc->irq = 0; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); + return err; +} + +/* + * find pci device and set it up + */ + +static int +setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = pdev; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + if (ent->device == 0xB410) { + test_and_set_bit(HFC_CHIP_B410P, &hc->chip); + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + hc->slots = 32; + } + + if (hc->pci_dev->irq <= 0) { + printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); + return -EIO; + } + if (pci_enable_device(hc->pci_dev)) { + printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); + return -EIO; + } + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); + hc->slots = 128; /* required */ + /* fall through */ + case HFC_IO_MODE_PCIMEM: + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + break; + case HFC_IO_MODE_REGIO: + hc->HFC_outb = HFC_outb_regio; + hc->HFC_inb = HFC_inb_regio; + hc->HFC_inw = HFC_inw_regio; + hc->HFC_wait = HFC_wait_regio; + hc->read_fifo = read_fifo_regio; + hc->write_fifo = write_fifo_regio; + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + hc->HFC_outb_nodebug = hc->HFC_outb; + hc->HFC_inb_nodebug = hc->HFC_inb; + hc->HFC_inw_nodebug = hc->HFC_inw; + hc->HFC_wait_nodebug = hc->HFC_wait; +#ifdef HFC_REGISTER_DEBUG + hc->HFC_outb = HFC_outb_debug; + hc->HFC_inb = HFC_inb_debug; + hc->HFC_inw = HFC_inw_debug; + hc->HFC_wait = HFC_wait_debug; +#endif + hc->pci_iobase = 0; + hc->pci_membase = NULL; + hc->plx_membase = NULL; + + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + hc->plx_origmembase = hc->pci_dev->resource[0].start; + /* MEMBASE 1 is PLX PCI Bridge */ + + if (!hc->plx_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); + if (!hc->plx_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap plx address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO + "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", + (u_long)hc->plx_membase, hc->plx_origmembase); + + hc->pci_origmembase = hc->pci_dev->resource[2].start; + /* MEMBASE 1 is PLX PCI Bridge */ + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); + if (!hc->pci_membase) { + printk(KERN_WARNING "HFC-multi: failed to remap io " + "address space. (internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " + "leds-type %d\n", + hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_PCIMEM: + hc->pci_origmembase = hc->pci_dev->resource[1].start; + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 256); + if (!hc->pci_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap io address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d " + "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, + hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_REGIO: + hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; + if (!hc->pci_iobase) { + printk(KERN_WARNING + "HFC-multi: No IO for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "HFC-multi: failed to request " + "address space at 0x%08lx (internal error)\n", + hc->pci_iobase); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", + m->vendor_name, m->card_name, (u_int) hc->pci_iobase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + pci_set_drvdata(hc->pci_dev, hc); + + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + return 0; +} + + +/* + * remove port + */ + +static void +release_port(struct hfc_multi *hc, struct dchannel *dch) +{ + int pt, ci, i = 0; + u_long flags; + struct bchannel *pb; + + ci = dch->slot; + pt = hc->chan[ci].port; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered for port %d\n", + __func__, pt + 1); + + if (pt >= hc->ports) { + printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", + __func__, pt + 1); + return; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: releasing port=%d\n", + __func__, pt + 1); + + if (dch->dev.D.protocol == ISDN_P_TE_S0) + l1_event(dch->l1, CLOSE_CHANNEL); + + hc->chan[ci].dch = NULL; + + if (hc->created[pt]) { + hc->created[pt] = 0; + mISDN_unregister_device(&dch->dev); + } + + spin_lock_irqsave(&hc->lock, flags); + + if (dch->timer.function) { + del_timer(&dch->timer); + dch->timer.function = NULL; + } + + if (hc->type == 1) { /* E1 */ + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 1); + } + /* free channels */ + for (i = 0; i <= 31; i++) { + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[i].port+1, i); + pb = hc->chan[i].bch; + hc->chan[i].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[i].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + } else { + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[ci].port); + plxsd_checksync(hc, 1); + } + /* free channels */ + if (hc->chan[ci - 2].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 2].port+1, + ci - 2); + pb = hc->chan[ci - 2].bch; + hc->chan[ci - 2].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 2].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + if (hc->chan[ci - 1].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 1].port+1, + ci - 1); + pb = hc->chan[ci - 1].bch; + hc->chan[ci - 1].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 1].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + + spin_unlock_irqrestore(&hc->lock, flags); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt); + mISDN_freedchannel(dch); + kfree(dch); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done!\n", __func__); +} + +static void +release_card(struct hfc_multi *hc) +{ + u_long flags; + int ch; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: release card (%d) entered\n", + __func__, hc->id); + + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + udelay(1000); + + /* dimm leds */ + if (hc->leds) + hfcmulti_leds(hc); + + /* disable D-channels & B-channels */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: disable all channels (d and b)\n", + __func__); + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) + release_port(hc, hc->chan[ch].dch); + } + + /* release hardware & irq */ + if (hc->irq) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", + __func__, hc->irq); + free_irq(hc->irq, hc); + hc->irq = 0; + + } + release_io_hfcmulti(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: remove instance from list\n", + __func__); + list_del(&hc->list); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: delete instance\n", __func__); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: card successfully removed\n", + __func__); +} + +static int +init_e1_port(struct hfc_multi *hc, struct hm_map *m) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = (hc->dslot)?30:31; + dch->slot = hc->dslot; + hc->chan[hc->dslot].dch = dch; + hc->chan[hc->dslot].port = 0; + hc->chan[hc->dslot].nt_timer = -1; + for (ch = 1; ch <= 31; ch++) { + if (ch == hc->dslot) /* skip dchannel */ + continue; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + bch->nr = ch; + bch->slot = ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[ch].bch = bch; + hc->chan[ch].port = 0; + test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + } + /* set optical line type */ + if (port[Port_cnt] & 0x001) { + if (!m->opticalsupport) { + printk(KERN_INFO + "This board has no optical " + "support\n"); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set optical " + "interfacs: card(%d) " + "port(%d)\n", + __func__, + HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_OPTICAL, + &hc->chan[hc->dslot].cfg); + } + } + /* set LOS report */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "LOS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_LOS, + &hc->chan[hc->dslot].cfg); + } + /* set AIS report */ + if (port[Port_cnt] & 0x008) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "AIS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_AIS, + &hc->chan[hc->dslot].cfg); + } + /* set SLIP report */ + if (port[Port_cnt] & 0x010) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set SLIP report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_SLIP, + &hc->chan[hc->dslot].cfg); + } + /* set RDI report */ + if (port[Port_cnt] & 0x020) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set RDI report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_RDI, + &hc->chan[hc->dslot].cfg); + } + /* set CRC-4 Mode */ + if (!(port[Port_cnt] & 0x100)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" + " card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_CRC4, + &hc->chan[hc->dslot].cfg); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn off CRC4" + " report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + } + /* set forced clock */ + if (port[Port_cnt] & 0x0200) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force getting clock from " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); + } else + if (port[Port_cnt] & 0x0400) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force putting clock to " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); + } + /* set JATT PLL */ + if (port[Port_cnt] & 0x0800) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT disable JATT PLL on " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); + } + /* set elastic jitter buffer */ + if (port[Port_cnt] & 0x3000) { + hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set elastic " + "buffer to %d: card(%d) port(%d)\n", + __func__, hc->chan[hc->dslot].jitter, + HFC_cnt + 1, 1); + } else + hc->chan[hc->dslot].jitter = 2; /* default */ + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + ret = mISDN_register_device(&dch->dev, name); + if (ret) + goto free_chan; + hc->created[0] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +init_multi_port(struct hfc_multi *hc, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, i, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = 2; + i = pt << 2; + dch->slot = i + 2; + hc->chan[i + 2].dch = dch; + hc->chan[i + 2].port = pt; + hc->chan[i + 2].nt_timer = -1; + for (ch = 0; ch < dch->dev.nrbchan; ch++) { + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[i + ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + bch->nr = ch + 1; + bch->slot = i + ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch + 1; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[i + ch].bch = bch; + hc->chan[i + ch].port = pt; + test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + } + /* set master clock */ + if (port[Port_cnt] & 0x001) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set master clock: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) is only" + " possible with TE-mode\n", + pt + 1, HFC_cnt + 1); + ret = -EINVAL; + goto free_chan; + } + if (hc->masterclk >= 0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) already " + "defined for port(%d)\n", + pt + 1, HFC_cnt + 1, hc->masterclk+1); + ret = -EINVAL; + goto free_chan; + } + hc->masterclk = pt; + } + /* set transmitter line to non capacitive */ + if (port[Port_cnt] & 0x002) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set non capacitive " + "transmitter: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_NONCAP_TX, + &hc->chan[i + 2].cfg); + } + /* disable E-channel */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL disable E-channel: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_DIS_ECHANNEL, + &hc->chan[i + 2].cfg); + } + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d", + hc->type, HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, name); + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret_err = 0; + int pt; + struct hfc_multi *hc; + u_long flags; + u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + + if (HFC_cnt >= MAX_CARDS) { + printk(KERN_ERR "too many cards (max=%d).\n", + MAX_CARDS); + return -EINVAL; + } + if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { + printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " + "type[%d] %d was supplied as module parameter\n", + m->vendor_name, m->card_name, m->type, HFC_cnt, + type[HFC_cnt] & 0xff); + printk(KERN_WARNING "HFC-MULTI: Load module without parameters " + "first, to see cards and their types."); + return -EINVAL; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", + __func__, m->vendor_name, m->card_name, m->type, + type[HFC_cnt]); + + /* allocate card+fifo structure */ + hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL); + if (!hc) { + printk(KERN_ERR "No kmem for HFC-Multi card\n"); + return -ENOMEM; + } + spin_lock_init(&hc->lock); + hc->mtyp = m; + hc->type = m->type; + hc->ports = m->ports; + hc->id = HFC_cnt; + hc->pcm = pcm[HFC_cnt]; + hc->io_mode = iomode[HFC_cnt]; + if (dslot[HFC_cnt] < 0) { + hc->dslot = 0; + printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " + "31 B-channels\n"); + } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) { + hc->dslot = dslot[HFC_cnt]; + printk(KERN_INFO "HFC-E1 card has alternating D-channel on " + "time slot %d\n", dslot[HFC_cnt]); + } else + hc->dslot = 16; + + /* set chip specific features */ + hc->masterclk = -1; + if (type[HFC_cnt] & 0x100) { + test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); + silence = 0xff; /* ulaw silence */ + } else + silence = 0x2a; /* alaw silence */ + if (!(type[HFC_cnt] & 0x200)) + test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); + + if (type[HFC_cnt] & 0x800) + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + if (type[HFC_cnt] & 0x1000) { + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + } + if (type[HFC_cnt] & 0x4000) + test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); + if (type[HFC_cnt] & 0x8000) + test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); + hc->slots = 32; + if (type[HFC_cnt] & 0x10000) + hc->slots = 64; + if (type[HFC_cnt] & 0x20000) + hc->slots = 128; + if (type[HFC_cnt] & 0x80000) { + test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); + hc->wdcount = 0; + hc->wdbyte = V_GPIO_OUT2; + printk(KERN_NOTICE "Watchdog enabled\n"); + } + + /* setup pci, hc->slots may change due to PLXSD */ + ret_err = setup_pci(hc, pdev, ent); + if (ret_err) { + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; + } + + /* crate channels */ + for (pt = 0; pt < hc->ports; pt++) { + if (Port_cnt >= MAX_PORTS) { + printk(KERN_ERR "too many ports (max=%d).\n", + MAX_PORTS); + ret_err = -EINVAL; + goto free_card; + } + if (hc->type == 1) + ret_err = init_e1_port(hc, m); + else + ret_err = init_multi_port(hc, pt); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: Registering D-channel, card(%d) port(%d)" + "result %d\n", + __func__, HFC_cnt + 1, pt, ret_err); + + if (ret_err) { + while (pt) { /* release already registered ports */ + pt--; + release_port(hc, hc->chan[(pt << 2) + 2].dch); + } + goto free_card; + } + Port_cnt++; + } + + /* disp switches */ + switch (m->dip_type) { + case DIP_4S: + /* + * get DIP Setting for beroNet 1S/2S/4S cards + * check if Port Jumper config matches + * module param 'protocol' + * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + + * GPI 19/23 (R_GPI_IN2)) + */ + dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | + ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | + (~HFC_inb(hc, R_GPI_IN2) & 0x08); + + /* Port mode (TE/NT) jumpers */ + pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); + + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + pmj = ~pmj & 0xf; + + printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", + m->vendor_name, m->card_name, dips, pmj); + break; + case DIP_8S: + /* + * get DIP Setting for beroNet 8S0+ cards + * + * enable PCI auxbridge function + */ + HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + /* prepare access to auxport */ + outw(0x4000, hc->pci_iobase + 4); + /* + * some dummy reads are required to + * read valid DIP switch data + */ + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = ~inb(hc->pci_iobase) & 0x3F; + outw(0x0, hc->pci_iobase + 4); + /* disable PCI auxbridge function */ + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + case DIP_E1: + /* + * get DIP Setting for beroNet E1 cards + * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) + */ + dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4; + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + } + + /* add to list */ + spin_lock_irqsave(&HFClock, flags); + list_add_tail(&hc->list, &HFClist); + spin_unlock_irqrestore(&HFClock, flags); + + /* initialize hardware */ + ret_err = init_card(hc); + if (ret_err) { + printk(KERN_ERR "init card returns %d\n", ret_err); + release_card(hc); + return ret_err; + } + + /* start IRQ and return */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + return 0; + +free_card: + release_io_hfcmulti(hc); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; +} + +static void __devexit hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_multi *card = pci_get_drvdata(pdev); + u_long flags; + + if (debug) + printk(KERN_INFO "removing hfc_multi card vendor:%x " + "device:%x subvendor:%x subdevice:%x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + if (card) { + spin_lock_irqsave(&HFClock, flags); + release_card(card); + spin_unlock_irqrestore(&HFClock, flags); + } else { + if (debug) + printk(KERN_WARNING "%s: drvdata allready removed\n", + __func__); + } +} + +#define VENDOR_CCD "Cologne Chip AG" +#define VENDOR_BN "beroNet GmbH" +#define VENDOR_DIG "Digium Inc." +#define VENDOR_JH "Junghanns.NET GmbH" +#define VENDOR_PRIM "PrimuX" + +static const struct hm_map hfcm_map[] = { +/*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0}, +/*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S}, +/*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0}, +/*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0}, +/*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0}, +/*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0}, +/*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, 0, 0}, +/*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0}, +/*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO}, +/*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0}, +/*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0}, +/*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0}, + +/*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0}, +/*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, + HFC_IO_MODE_REGIO}, +/*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0}, +/*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0}, + +/*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0}, +/*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0}, +/*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0}, + +/*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0}, +/*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0}, +/*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0}, +/*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0}, + +/*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0}, +/*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0}, +/*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0}, + +/*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD}, +/*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD}, +/*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0}, +/*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0}, +/*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0}, +}; + +#undef H +#define H(x) ((unsigned long)&hfcm_map[x]) +static struct pci_device_id hfmultipci_ids[] __devinitdata = { + + /* Cards with HFC-4S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ + { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, + PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ + + /* Cards with HFC-8S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, + /* IOB8ST Recording */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ + + + /* Cards with HFC-E1 Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + {0, } +}; +#undef H + +MODULE_DEVICE_TABLE(pci, hfmultipci_ids); + +static int +hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret; + + if (m == NULL) { + if (ent->vendor == PCI_VENDOR_ID_CCD) + if (ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1) + printk(KERN_ERR + "unknown HFC multiport controller " + "(vendor:%x device:%x subvendor:%x " + "subdevice:%x) Please contact the " + "driver maintainer for support.\n", + ent->vendor, ent->device, + ent->subvendor, ent->subdevice); + return -ENODEV; + } + ret = hfcmulti_init(pdev, ent); + if (ret) + return ret; + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + return 0; +} + +static struct pci_driver hfcmultipci_driver = { + .name = "hfc_multi", + .probe = hfcmulti_probe, + .remove = __devexit_p(hfc_remove_pci), + .id_table = hfmultipci_ids, +}; + +static void __exit +HFCmulti_cleanup(void) +{ + struct hfc_multi *card, *next; + + /* unload interrupt function symbol */ + if (hfc_interrupt) + symbol_put(ztdummy_extern_interrupt); + if (register_interrupt) + symbol_put(ztdummy_register_interrupt); + if (unregister_interrupt) { + if (interrupt_registered) { + interrupt_registered = 0; + unregister_interrupt(); + } + symbol_put(ztdummy_unregister_interrupt); + } + + list_for_each_entry_safe(card, next, &HFClist, list) + release_card(card); + /* get rid of all devices of this driver */ + pci_unregister_driver(&hfcmultipci_driver); +} + +static int __init +HFCmulti_init(void) +{ + int err; + +#ifdef IRQ_DEBUG + printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__); +#endif + + spin_lock_init(&HFClock); + spin_lock_init(&plx_lock); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: init entered\n", __func__); + +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + hfc_interrupt = symbol_get(ztdummy_extern_interrupt); + register_interrupt = symbol_get(ztdummy_register_interrupt); + unregister_interrupt = symbol_get(ztdummy_unregister_interrupt); + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", + hfcmulti_revision); + + switch (poll) { + case 0: + poll_timer = 6; + poll = 128; + break; + /* + * wenn dieses break nochmal verschwindet, + * gibt es heisse ohren :-) + * "without the break you will get hot ears ???" + */ + case 8: + poll_timer = 2; + break; + case 16: + poll_timer = 3; + break; + case 32: + poll_timer = 4; + break; + case 64: + poll_timer = 5; + break; + case 128: + poll_timer = 6; + break; + case 256: + poll_timer = 7; + break; + default: + printk(KERN_ERR + "%s: Wrong poll value (%d).\n", __func__, poll); + err = -EINVAL; + return err; + + } + + err = pci_register_driver(&hfcmultipci_driver); + if (err < 0) { + printk(KERN_ERR "error registering pci driver: %x\n", err); + if (hfc_interrupt) + symbol_put(ztdummy_extern_interrupt); + if (register_interrupt) + symbol_put(ztdummy_register_interrupt); + if (unregister_interrupt) { + if (interrupt_registered) { + interrupt_registered = 0; + unregister_interrupt(); + } + symbol_put(ztdummy_unregister_interrupt); + } + return err; + } + return 0; +} + + +module_init(HFCmulti_init); +module_exit(HFCmulti_cleanup); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c3b1761aba26..ffe479ba0779 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1832,7 +1832,13 @@ #define PCI_DEVICE_ID_MOXA_C320 0x3200 #define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 +#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 +#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 #define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 +#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 +#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 +#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 #define PCI_DEVICE_ID_CCD_B000 0xb000 #define PCI_DEVICE_ID_CCD_B006 0xb006 #define PCI_DEVICE_ID_CCD_B007 0xb007 @@ -1842,8 +1848,32 @@ #define PCI_DEVICE_ID_CCD_B00B 0xb00b #define PCI_DEVICE_ID_CCD_B00C 0xb00c #define PCI_DEVICE_ID_CCD_B100 0xb100 +#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 +#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 +#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 +#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 +#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 +#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 +#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 +#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 +#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 +#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 +#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 +#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 +#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 +#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 +#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A +#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B +#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 +#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 #define PCI_DEVICE_ID_CCD_B700 0xb700 #define PCI_DEVICE_ID_CCD_B701 0xb701 +#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 +#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 +#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 +#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 #define PCI_VENDOR_ID_EXAR 0x13a8 #define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 @@ -2523,6 +2553,9 @@ #define PCI_VENDOR_ID_3COM_2 0xa727 +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 + #define PCI_SUBVENDOR_ID_EXSYS 0xd84d #define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 #define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 -- cgit v1.2.3 From 93bc4e89c260d91576840c4881d1066d84ccd422 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sat, 26 Jul 2008 17:49:33 -0700 Subject: netfilter: fix double-free and use-after free As suggested by Patrick McHardy, introduce a __krealloc() that doesn't free the original buffer to fix a double-free and use-after-free bug introduced by me in netfilter that uses RCU. Reported-by: Patrick McHardy Signed-off-by: Pekka Enberg Tested-by: Dieter Ries Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/slab.h | 1 + mm/util.c | 44 ++++++++++++++++++++++++++++--------- net/netfilter/nf_conntrack_extend.c | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/slab.h b/include/linux/slab.h index 9aa90a6f20e0..be6f1d40b66a 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -96,6 +96,7 @@ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr); /* * Common kmalloc functions provided by all allocators */ +void * __must_check __krealloc(const void *, size_t, gfp_t); void * __must_check krealloc(const void *, size_t, gfp_t); void kfree(const void *); size_t ksize(const void *); diff --git a/mm/util.c b/mm/util.c index 8f18683825bc..6ef9e9943f62 100644 --- a/mm/util.c +++ b/mm/util.c @@ -68,25 +68,22 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp) EXPORT_SYMBOL(kmemdup); /** - * krealloc - reallocate memory. The contents will remain unchanged. + * __krealloc - like krealloc() but don't free @p. * @p: object to reallocate memory for. * @new_size: how many bytes of memory are required. * @flags: the type of memory to allocate. * - * The contents of the object pointed to are preserved up to the - * lesser of the new and old sizes. If @p is %NULL, krealloc() - * behaves exactly like kmalloc(). If @size is 0 and @p is not a - * %NULL pointer, the object pointed to is freed. + * This function is like krealloc() except it never frees the originally + * allocated buffer. Use this if you don't want to free the buffer immediately + * like, for example, with RCU. */ -void *krealloc(const void *p, size_t new_size, gfp_t flags) +void *__krealloc(const void *p, size_t new_size, gfp_t flags) { void *ret; size_t ks = 0; - if (unlikely(!new_size)) { - kfree(p); + if (unlikely(!new_size)) return ZERO_SIZE_PTR; - } if (p) ks = ksize(p); @@ -95,10 +92,37 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags) return (void *)p; ret = kmalloc_track_caller(new_size, flags); - if (ret && p) { + if (ret && p) memcpy(ret, p, ks); + + return ret; +} +EXPORT_SYMBOL(__krealloc); + +/** + * krealloc - reallocate memory. The contents will remain unchanged. + * @p: object to reallocate memory for. + * @new_size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * The contents of the object pointed to are preserved up to the + * lesser of the new and old sizes. If @p is %NULL, krealloc() + * behaves exactly like kmalloc(). If @size is 0 and @p is not a + * %NULL pointer, the object pointed to is freed. + */ +void *krealloc(const void *p, size_t new_size, gfp_t flags) +{ + void *ret; + + if (unlikely(!new_size)) { kfree(p); + return ZERO_SIZE_PTR; } + + ret = __krealloc(p, new_size, flags); + if (ret && p != ret) + kfree(p); + return ret; } EXPORT_SYMBOL(krealloc); diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 3469bc71a385..c956ef7eeecb 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -95,7 +95,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) newlen = newoff + t->len; rcu_read_unlock(); - new = krealloc(ct->ext, newlen, gfp); + new = __krealloc(ct->ext, newlen, gfp); if (!new) return NULL; -- cgit v1.2.3 From d2d9648ec6858e19d16a0b16da62534e85888653 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 1 Jul 2008 14:16:09 +0200 Subject: [PATCH] reuse xxx_fifo_fops for xxx_pipe_fops Merge fifo and pipe file_operations. Signed-off-by: Denys Vlasenko Signed-off-by: Al Viro --- fs/fifo.c | 8 ++++---- fs/pipe.c | 51 ++++++++------------------------------------------- include/linux/fs.h | 6 +++--- 3 files changed, 15 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/fs/fifo.c b/fs/fifo.c index 9785e36f81e7..987bf9411495 100644 --- a/fs/fifo.c +++ b/fs/fifo.c @@ -57,7 +57,7 @@ static int fifo_open(struct inode *inode, struct file *filp) * POSIX.1 says that O_NONBLOCK means return with the FIFO * opened, even when there is no process writing the FIFO. */ - filp->f_op = &read_fifo_fops; + filp->f_op = &read_pipefifo_fops; pipe->r_counter++; if (pipe->readers++ == 0) wake_up_partner(inode); @@ -86,7 +86,7 @@ static int fifo_open(struct inode *inode, struct file *filp) if ((filp->f_flags & O_NONBLOCK) && !pipe->readers) goto err; - filp->f_op = &write_fifo_fops; + filp->f_op = &write_pipefifo_fops; pipe->w_counter++; if (!pipe->writers++) wake_up_partner(inode); @@ -105,7 +105,7 @@ static int fifo_open(struct inode *inode, struct file *filp) * This implementation will NEVER block on a O_RDWR open, since * the process can at least talk to itself. */ - filp->f_op = &rdwr_fifo_fops; + filp->f_op = &rdwr_pipefifo_fops; pipe->readers++; pipe->writers++; @@ -151,5 +151,5 @@ err_nocleanup: * depending on the access mode of the file... */ const struct file_operations def_fifo_fops = { - .open = fifo_open, /* will set read or write pipe_fops */ + .open = fifo_open, /* will set read_ or write_pipefifo_fops */ }; diff --git a/fs/pipe.c b/fs/pipe.c index 10c4e9aa5c49..fcba6542b8d0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -777,45 +777,10 @@ pipe_rdwr_open(struct inode *inode, struct file *filp) /* * The file_operations structs are not static because they * are also used in linux/fs/fifo.c to do operations on FIFOs. + * + * Pipes reuse fifos' file_operations structs. */ -const struct file_operations read_fifo_fops = { - .llseek = no_llseek, - .read = do_sync_read, - .aio_read = pipe_read, - .write = bad_pipe_w, - .poll = pipe_poll, - .unlocked_ioctl = pipe_ioctl, - .open = pipe_read_open, - .release = pipe_read_release, - .fasync = pipe_read_fasync, -}; - -const struct file_operations write_fifo_fops = { - .llseek = no_llseek, - .read = bad_pipe_r, - .write = do_sync_write, - .aio_write = pipe_write, - .poll = pipe_poll, - .unlocked_ioctl = pipe_ioctl, - .open = pipe_write_open, - .release = pipe_write_release, - .fasync = pipe_write_fasync, -}; - -const struct file_operations rdwr_fifo_fops = { - .llseek = no_llseek, - .read = do_sync_read, - .aio_read = pipe_read, - .write = do_sync_write, - .aio_write = pipe_write, - .poll = pipe_poll, - .unlocked_ioctl = pipe_ioctl, - .open = pipe_rdwr_open, - .release = pipe_rdwr_release, - .fasync = pipe_rdwr_fasync, -}; - -static const struct file_operations read_pipe_fops = { +const struct file_operations read_pipefifo_fops = { .llseek = no_llseek, .read = do_sync_read, .aio_read = pipe_read, @@ -827,7 +792,7 @@ static const struct file_operations read_pipe_fops = { .fasync = pipe_read_fasync, }; -static const struct file_operations write_pipe_fops = { +const struct file_operations write_pipefifo_fops = { .llseek = no_llseek, .read = bad_pipe_r, .write = do_sync_write, @@ -839,7 +804,7 @@ static const struct file_operations write_pipe_fops = { .fasync = pipe_write_fasync, }; -static const struct file_operations rdwr_pipe_fops = { +const struct file_operations rdwr_pipefifo_fops = { .llseek = no_llseek, .read = do_sync_read, .aio_read = pipe_read, @@ -927,7 +892,7 @@ static struct inode * get_pipe_inode(void) inode->i_pipe = pipe; pipe->readers = pipe->writers = 1; - inode->i_fop = &rdwr_pipe_fops; + inode->i_fop = &rdwr_pipefifo_fops; /* * Mark the inode dirty from the very beginning, @@ -978,7 +943,7 @@ struct file *create_write_pipe(int flags) d_instantiate(dentry, inode); err = -ENFILE; - f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipe_fops); + f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipefifo_fops); if (!f) goto err_dentry; f->f_mapping = inode->i_mapping; @@ -1020,7 +985,7 @@ struct file *create_read_pipe(struct file *wrf, int flags) f->f_pos = 0; f->f_flags = O_RDONLY | (flags & O_NONBLOCK); - f->f_op = &read_pipe_fops; + f->f_op = &read_pipefifo_fops; f->f_mode = FMODE_READ; f->f_version = 0; diff --git a/include/linux/fs.h b/include/linux/fs.h index 53d2edb709b3..7721a2ac9c0e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1696,9 +1696,9 @@ extern void init_special_inode(struct inode *, umode_t, dev_t); extern void make_bad_inode(struct inode *); extern int is_bad_inode(struct inode *); -extern const struct file_operations read_fifo_fops; -extern const struct file_operations write_fifo_fops; -extern const struct file_operations rdwr_fifo_fops; +extern const struct file_operations read_pipefifo_fops; +extern const struct file_operations write_pipefifo_fops; +extern const struct file_operations rdwr_pipefifo_fops; extern int fs_may_remount_ro(struct super_block *); -- cgit v1.2.3 From 734550921e9b7ab924a43aa3d0bd4239dac4fbf1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 14 Jul 2008 21:22:20 -0400 Subject: [PATCH] beginning of sysctl cleanup - ctl_table_set New object: set of sysctls [currently - root and per-net-ns]. Contains: pointer to parent set, list of tables and "should I see this set?" method (->is_seen(set)). Current lists of tables are subsumed by that; net-ns contains such a beast. ->lookup() for ctl_table_root returns pointer to ctl_table_set instead of that to ->list of that ctl_table_set. [folded compile fixes by rdd for configs without sysctl] Signed-off-by: Al Viro --- include/linux/sysctl.h | 15 +++++++++++++-- include/net/net_namespace.h | 4 +++- kernel/sysctl.c | 41 +++++++++++++++++++++++++++++++---------- net/sysctl_net.c | 22 ++++++++++------------ 4 files changed, 57 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 24141b4d1a11..c1e0cf408af9 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -947,6 +947,16 @@ struct ctl_table; struct nsproxy; struct ctl_table_root; +struct ctl_table_set { + struct list_head list; + struct ctl_table_set *parent; + int (*is_seen)(struct ctl_table_set *); +}; + +extern void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)); + extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, struct ctl_table_header *prev); @@ -1049,8 +1059,8 @@ struct ctl_table struct ctl_table_root { struct list_head root_list; - struct list_head header_list; - struct list_head *(*lookup)(struct ctl_table_root *root, + struct ctl_table_set default_set; + struct ctl_table_set *(*lookup)(struct ctl_table_root *root, struct nsproxy *namespaces); int (*permissions)(struct ctl_table_root *root, struct nsproxy *namespaces, struct ctl_table *table); @@ -1066,6 +1076,7 @@ struct ctl_table_header struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; + struct ctl_table_set *set; }; /* struct ctl_path describes where in the hierarchy a table is added */ diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 3855620b78a9..a8eb43cf0c7e 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -38,7 +38,9 @@ struct net { struct proc_dir_entry *proc_net; struct proc_dir_entry *proc_net_stat; - struct list_head sysctl_table_headers; +#ifdef CONFIG_SYSCTL + struct ctl_table_set sysctls; +#endif struct net_device *loopback_dev; /* The loopback */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 35a50db9b6ce..8ee4a0619fbb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -160,12 +160,13 @@ static struct ctl_table root_table[]; static struct ctl_table_root sysctl_table_root; static struct ctl_table_header root_table_header = { .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), .root = &sysctl_table_root, + .set = &sysctl_table_root.default_set, }; static struct ctl_table_root sysctl_table_root = { .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), - .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), + .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), }; static struct ctl_table kern_table[]; @@ -1403,14 +1404,20 @@ void sysctl_head_finish(struct ctl_table_header *head) spin_unlock(&sysctl_lock); } +static struct ctl_table_set * +lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) +{ + struct ctl_table_set *set = &root->default_set; + if (root->lookup) + set = root->lookup(root, namespaces); + return set; +} + static struct list_head * lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) { - struct list_head *header_list; - header_list = &root->header_list; - if (root->lookup) - header_list = root->lookup(root, namespaces); - return header_list; + struct ctl_table_set *set = lookup_header_set(root, namespaces); + return &set->list; } struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, @@ -1720,7 +1727,6 @@ struct ctl_table_header *__register_sysctl_paths( struct nsproxy *namespaces, const struct ctl_path *path, struct ctl_table *table) { - struct list_head *header_list; struct ctl_table_header *header; struct ctl_table *new, **prevp; unsigned int n, npath; @@ -1772,8 +1778,8 @@ struct ctl_table_header *__register_sysctl_paths( } #endif spin_lock(&sysctl_lock); - header_list = lookup_header_list(root, namespaces); - list_add_tail(&header->ctl_entry, header_list); + header->set = lookup_header_set(root, namespaces); + list_add_tail(&header->ctl_entry, &header->set->list); spin_unlock(&sysctl_lock); return header; @@ -1832,6 +1838,15 @@ void unregister_sysctl_table(struct ctl_table_header * header) kfree(header); } +void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)) +{ + INIT_LIST_HEAD(&p->list); + p->parent = parent ? parent : &sysctl_table_root.default_set; + p->is_seen = is_seen; +} + #else /* !CONFIG_SYSCTL */ struct ctl_table_header *register_sysctl_table(struct ctl_table * table) { @@ -1848,6 +1863,12 @@ void unregister_sysctl_table(struct ctl_table_header * table) { } +void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)) +{ +} + #endif /* CONFIG_SYSCTL */ /* diff --git a/net/sysctl_net.c b/net/sysctl_net.c index 63ada437fc2f..cefbc367d8be 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -29,10 +29,15 @@ #include #endif -static struct list_head * +static struct ctl_table_set * net_ctl_header_lookup(struct ctl_table_root *root, struct nsproxy *namespaces) { - return &namespaces->net_ns->sysctl_table_headers; + return &namespaces->net_ns->sysctls; +} + +static int is_seen(struct ctl_table_set *set) +{ + return ¤t->nsproxy->net_ns->sysctls == set; } /* Return standard mode bits for table entry. */ @@ -53,13 +58,6 @@ static struct ctl_table_root net_sysctl_root = { .permissions = net_ctl_permissions, }; -static LIST_HEAD(net_sysctl_ro_tables); -static struct list_head *net_ctl_ro_header_lookup(struct ctl_table_root *root, - struct nsproxy *namespaces) -{ - return &net_sysctl_ro_tables; -} - static int net_ctl_ro_header_perms(struct ctl_table_root *root, struct nsproxy *namespaces, struct ctl_table *table) { @@ -70,19 +68,18 @@ static int net_ctl_ro_header_perms(struct ctl_table_root *root, } static struct ctl_table_root net_sysctl_ro_root = { - .lookup = net_ctl_ro_header_lookup, .permissions = net_ctl_ro_header_perms, }; static int sysctl_net_init(struct net *net) { - INIT_LIST_HEAD(&net->sysctl_table_headers); + setup_sysctl_set(&net->sysctls, NULL, is_seen); return 0; } static void sysctl_net_exit(struct net *net) { - WARN_ON(!list_empty(&net->sysctl_table_headers)); + WARN_ON(!list_empty(&net->sysctls.list)); return; } @@ -98,6 +95,7 @@ static __init int sysctl_init(void) if (ret) goto out; register_sysctl_root(&net_sysctl_root); + setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL, NULL); register_sysctl_root(&net_sysctl_ro_root); out: return ret; -- cgit v1.2.3 From f7e6ced4061da509f737541ca4dbd44d83a6e82f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 01:44:23 -0400 Subject: [PATCH] allow delayed freeing of ctl_table_header Refcount the sucker; instead of freeing it by the end of unregistration just drop the refcount and free only when it hits zero. Make sure that we _always_ make ->unregistering non-NULL in start_unregistering(). That allows anybody to get a reference to such puppy, preventing its freeing and reuse. It does *not* block unregistration. Anybody who holds such a reference can * try to grab a "use" reference (ctl_head_grab()); that will succeeds if and only if it hadn't entered unregistration yet. If it succeeds, we can use it in all normal ways until we release the "use" reference (with ctl_head_finish()). Note that this relies on having ->unregistering become non-NULL in all cases when one starts to unregister the sucker. * keep pointers to ctl_table entries; they *can* be freed if the entire thing is unregistered. However, if ctl_head_grab() succeeds, we know that unregistration had not happened (and will not happen until ctl_head_finish()) and such pointers can be used safely. IOW, now we can have inodes under /proc/sys keep references to ctl_table entries, protecting them with references to ctl_table_header and grabbing the latter for the duration of operations that require access to ctl_table. That won't cause deadlocks, since unregistration will not be stopped by mere keeping a reference to ctl_table_header. Signed-off-by: Al Viro --- include/linux/sysctl.h | 6 ++++++ kernel/sysctl.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index c1e0cf408af9..956264d09ba0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -957,6 +957,11 @@ extern void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)); +struct ctl_table_header; + +extern void sysctl_head_get(struct ctl_table_header *); +extern void sysctl_head_put(struct ctl_table_header *); +extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, struct ctl_table_header *prev); @@ -1073,6 +1078,7 @@ struct ctl_table_header struct ctl_table *ctl_table; struct list_head ctl_entry; int used; + int count; struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8ee4a0619fbb..60d9357e7172 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1387,6 +1387,9 @@ static void start_unregistering(struct ctl_table_header *p) spin_unlock(&sysctl_lock); wait_for_completion(&wait); spin_lock(&sysctl_lock); + } else { + /* anything non-NULL; we'll never dereference it */ + p->unregistering = ERR_PTR(-EINVAL); } /* * do not remove from the list until nobody holds it; walking the @@ -1395,6 +1398,32 @@ static void start_unregistering(struct ctl_table_header *p) list_del_init(&p->ctl_entry); } +void sysctl_head_get(struct ctl_table_header *head) +{ + spin_lock(&sysctl_lock); + head->count++; + spin_unlock(&sysctl_lock); +} + +void sysctl_head_put(struct ctl_table_header *head) +{ + spin_lock(&sysctl_lock); + if (!--head->count) + kfree(head); + spin_unlock(&sysctl_lock); +} + +struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) +{ + if (!head) + BUG(); + spin_lock(&sysctl_lock); + if (!use_table(head)) + head = ERR_PTR(-ENOENT); + spin_unlock(&sysctl_lock); + return head; +} + void sysctl_head_finish(struct ctl_table_header *head) { if (!head) @@ -1771,6 +1800,7 @@ struct ctl_table_header *__register_sysctl_paths( header->unregistering = NULL; header->root = root; sysctl_set_parent(NULL, header->ctl_table); + header->count = 1; #ifdef CONFIG_SYSCTL_SYSCALL_CHECK if (sysctl_check_table(namespaces, header->ctl_table)) { kfree(header); @@ -1834,8 +1864,9 @@ void unregister_sysctl_table(struct ctl_table_header * header) spin_lock(&sysctl_lock); start_unregistering(header); + if (!--header->count) + kfree(header); spin_unlock(&sysctl_lock); - kfree(header); } void setup_sysctl_set(struct ctl_table_set *p, @@ -1869,6 +1900,10 @@ void setup_sysctl_set(struct ctl_table_set *p, { } +void sysctl_head_put(struct ctl_table_header *head) +{ +} + #endif /* CONFIG_SYSCTL */ /* -- cgit v1.2.3 From ae7edecc9b8810770a8e5cb9a466ea4bdcfa8401 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 06:33:31 -0400 Subject: [PATCH] sysctl: keep track of tree relationships In a sense, that's the heart of the series. It's based on the following property of the trees we are actually asked to add: they can be split into stem that is already covered by registered trees and crown that is entirely new. IOW, if a/b and a/c/d are introduced by our tree, then a/c is also introduced by it. That allows to associate tree and table entry with each node in the union; while directory nodes might be covered by many trees, only one will cover the node by its crown. And that will allow much saner logics for /proc/sys in the next patches. This patch introduces the data structures needed to keep track of that. When adding a sysctl table, we find a "parent" one. Which is to say, find the deepest node on its stem that already is present in one of the tables from our table set or its ancestor sets. That table will be our parent and that node in it - attachment point. Add our table to list anchored in parent, have it refer the parent and contents of attachment point. Also remember where its crown lives. Signed-off-by: Al Viro --- include/linux/sysctl.h | 3 +++ kernel/sysctl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 956264d09ba0..3f6599aeb0db 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1083,6 +1083,9 @@ struct ctl_table_header struct ctl_table *ctl_table_arg; struct ctl_table_root *root; struct ctl_table_set *set; + struct ctl_table *attached_by; + struct ctl_table *attached_to; + struct ctl_table_header *parent; }; /* struct ctl_path describes where in the hierarchy a table is added */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 60d9357e7172..c9a0af887033 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1680,6 +1680,52 @@ static __init int sysctl_init(void) core_initcall(sysctl_init); +static int is_branch_in(struct ctl_table *branch, struct ctl_table *table) +{ + struct ctl_table *p; + const char *s = branch->procname; + + /* branch should have named subdirectory as its first element */ + if (!s || !branch->child) + return 0; + + /* ... and nothing else */ + if (branch[1].procname || branch[1].ctl_name) + return 0; + + /* table should contain subdirectory with the same name */ + for (p = table; p->procname || p->ctl_name; p++) { + if (!p->child) + continue; + if (p->procname && strcmp(p->procname, s) == 0) + return 1; + } + return 0; +} + +/* see if attaching q to p would be an improvement */ +static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) +{ + struct ctl_table *to = p->ctl_table, *by = q->ctl_table; + int is_better = 0; + int not_in_parent = !p->attached_by; + + while (is_branch_in(by, to)) { + if (by == q->attached_by) + is_better = 1; + if (to == p->attached_by) + not_in_parent = 1; + by = by->child; + to = to->child; + } + + if (is_better && not_in_parent) { + q->attached_by = by; + q->attached_to = to; + q->parent = p; + } +} + /** * __register_sysctl_paths - register a sysctl hierarchy * @root: List of sysctl headers to register on @@ -1759,6 +1805,7 @@ struct ctl_table_header *__register_sysctl_paths( struct ctl_table_header *header; struct ctl_table *new, **prevp; unsigned int n, npath; + struct ctl_table_set *set; /* Count the path components */ for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) @@ -1809,6 +1856,18 @@ struct ctl_table_header *__register_sysctl_paths( #endif spin_lock(&sysctl_lock); header->set = lookup_header_set(root, namespaces); + header->attached_by = header->ctl_table; + header->attached_to = root_table; + header->parent = &root_table_header; + for (set = header->set; set; set = set->parent) { + struct ctl_table_header *p; + list_for_each_entry(p, &set->list, ctl_entry) { + if (p->unregistering) + continue; + try_attach(p, header); + } + } + header->parent->count++; list_add_tail(&header->ctl_entry, &header->set->list); spin_unlock(&sysctl_lock); @@ -1864,6 +1923,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) spin_lock(&sysctl_lock); start_unregistering(header); + if (!--header->parent->count) { + WARN_ON(1); + kfree(header->parent); + } if (!--header->count) kfree(header); spin_unlock(&sysctl_lock); -- cgit v1.2.3 From 9043476f726802f4b00c96d0c4f418dde48d1304 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 08:54:06 -0400 Subject: [PATCH] sanitize proc_sysctl * keep references to ctl_table_head and ctl_table in /proc/sys inodes * grab the former during operations, use the latter for access to entry if that succeeds * have ->d_compare() check if table should be seen for one who does lookup; that allows us to avoid flipping inodes - if we have the same name resolve to different things, we'll just keep several dentries and ->d_compare() will reject the wrong ones. * have ->lookup() and ->readdir() scan the table of our inode first, then walk all ctl_table_header and scan ->attached_by for those that are attached to our directory. * implement ->getattr(). * get rid of insane amounts of tree-walking * get rid of the need to know dentry in ->permission() and of the contortions induced by that. Signed-off-by: Al Viro --- fs/proc/inode.c | 5 + fs/proc/proc_sysctl.c | 427 ++++++++++++++++++++++-------------------------- include/linux/proc_fs.h | 5 + include/linux/sysctl.h | 1 + kernel/sysctl.c | 15 ++ 5 files changed, 218 insertions(+), 235 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/inode.c b/fs/proc/inode.c index b37f25dc45a5..8bb03f056c28 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,8 @@ static void proc_delete_inode(struct inode *inode) module_put(de->owner); de_put(de); } + if (PROC_I(inode)->sysctl) + sysctl_head_put(PROC_I(inode)->sysctl); clear_inode(inode); } @@ -84,6 +87,8 @@ static struct inode *proc_alloc_inode(struct super_block *sb) ei->fd = 0; ei->op.proc_get_link = NULL; ei->pde = NULL; + ei->sysctl = NULL; + ei->sysctl_entry = NULL; inode = &ei->vfs_inode; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; return inode; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 5acc001d49f6..fa1ec2433e44 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -10,149 +10,110 @@ static struct dentry_operations proc_sys_dentry_operations; static const struct file_operations proc_sys_file_operations; static const struct inode_operations proc_sys_inode_operations; +static const struct file_operations proc_sys_dir_file_operations; +static const struct inode_operations proc_sys_dir_operations; -static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table) -{ - /* Refresh the cached information bits in the inode */ - if (table) { - inode->i_uid = 0; - inode->i_gid = 0; - inode->i_mode = table->mode; - if (table->proc_handler) { - inode->i_mode |= S_IFREG; - inode->i_nlink = 1; - } else { - inode->i_mode |= S_IFDIR; - inode->i_nlink = 0; /* It is too hard to figure out */ - } - } -} - -static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table) +static struct inode *proc_sys_make_inode(struct super_block *sb, + struct ctl_table_header *head, struct ctl_table *table) { struct inode *inode; - struct proc_inode *dir_ei, *ei; - int depth; + struct proc_inode *ei; - inode = new_inode(dir->i_sb); + inode = new_inode(sb); if (!inode) goto out; - /* A directory is always one deeper than it's parent */ - dir_ei = PROC_I(dir); - depth = dir_ei->fd + 1; - + sysctl_head_get(head); ei = PROC_I(inode); - ei->fd = depth; + ei->sysctl = head; + ei->sysctl_entry = table; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_op = &proc_sys_inode_operations; - inode->i_fop = &proc_sys_file_operations; inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */ - proc_sys_refresh_inode(inode, table); + inode->i_mode = table->mode; + if (!table->child) { + inode->i_mode |= S_IFREG; + inode->i_op = &proc_sys_inode_operations; + inode->i_fop = &proc_sys_file_operations; + } else { + inode->i_mode |= S_IFDIR; + inode->i_nlink = 0; + inode->i_op = &proc_sys_dir_operations; + inode->i_fop = &proc_sys_dir_file_operations; + } out: return inode; } -static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth) -{ - for (;;) { - struct proc_inode *ei; - - ei = PROC_I(dentry->d_inode); - if (ei->fd == depth) - break; /* found */ - - dentry = dentry->d_parent; - } - return dentry; -} - -static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table, - struct qstr *name) +static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name) { int len; - for ( ; table->ctl_name || table->procname; table++) { + for ( ; p->ctl_name || p->procname; p++) { - if (!table->procname) + if (!p->procname) continue; - len = strlen(table->procname); + len = strlen(p->procname); if (len != name->len) continue; - if (memcmp(table->procname, name->name, len) != 0) + if (memcmp(p->procname, name->name, len) != 0) continue; /* I have a match */ - return table; + return p; } return NULL; } -static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry, - struct ctl_table *table) +struct ctl_table_header *grab_header(struct inode *inode) { - struct dentry *ancestor; - struct proc_inode *ei; - int depth, i; + if (PROC_I(inode)->sysctl) + return sysctl_head_grab(PROC_I(inode)->sysctl); + else + return sysctl_head_next(NULL); +} - ei = PROC_I(dentry->d_inode); - depth = ei->fd; +static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct ctl_table_header *head = grab_header(dir); + struct ctl_table *table = PROC_I(dir)->sysctl_entry; + struct ctl_table_header *h = NULL; + struct qstr *name = &dentry->d_name; + struct ctl_table *p; + struct inode *inode; + struct dentry *err = ERR_PTR(-ENOENT); - if (depth == 0) - return table; + if (IS_ERR(head)) + return ERR_CAST(head); - for (i = 1; table && (i <= depth); i++) { - ancestor = proc_sys_ancestor(dentry, i); - table = proc_sys_lookup_table_one(table, &ancestor->d_name); - if (table) - table = table->child; + if (table && !table->child) { + WARN_ON(1); + goto out; } - return table; - -} -static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent, - struct qstr *name, - struct ctl_table *table) -{ - table = proc_sys_lookup_table(dparent, table); - if (table) - table = proc_sys_lookup_table_one(table, name); - return table; -} -static struct ctl_table *do_proc_sys_lookup(struct dentry *parent, - struct qstr *name, - struct ctl_table_header **ptr) -{ - struct ctl_table_header *head; - struct ctl_table *table = NULL; + table = table ? table->child : head->ctl_table; - for (head = sysctl_head_next(NULL); head; - head = sysctl_head_next(head)) { - table = proc_sys_lookup_entry(parent, name, head->ctl_table); - if (table) - break; + p = find_in_table(table, name); + if (!p) { + for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) { + if (h->attached_to != table) + continue; + p = find_in_table(h->attached_by, name); + if (p) + break; + } } - *ptr = head; - return table; -} - -static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - struct ctl_table_header *head; - struct inode *inode; - struct dentry *err; - struct ctl_table *table; - err = ERR_PTR(-ENOENT); - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); - if (!table) + if (!p) goto out; err = ERR_PTR(-ENOMEM); - inode = proc_sys_make_inode(dir, table); + inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p); + if (h) + sysctl_head_finish(h); + if (!inode) goto out; @@ -168,22 +129,14 @@ out: static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, size_t count, loff_t *ppos, int write) { - struct dentry *dentry = filp->f_dentry; - struct ctl_table_header *head; - struct ctl_table *table; + struct inode *inode = filp->f_path.dentry->d_inode; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; ssize_t error; size_t res; - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); - /* Has the sysctl entry disappeared on us? */ - error = -ENOENT; - if (!table) - goto out; - - /* Has the sysctl entry been replaced by a directory? */ - error = -EISDIR; - if (!table->proc_handler) - goto out; + if (IS_ERR(head)) + return PTR_ERR(head); /* * At this point we know that the sysctl was not unregistered @@ -193,6 +146,11 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, if (sysctl_perm(head->root, table, write ? MAY_WRITE : MAY_READ)) goto out; + /* if that can happen at all, it should be -EINVAL, not -EISDIR */ + error = -EINVAL; + if (!table->proc_handler) + goto out; + /* careful: calling conventions are nasty here */ res = count; error = table->proc_handler(table, write, filp, buf, &res, ppos); @@ -218,82 +176,86 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf, static int proc_sys_fill_cache(struct file *filp, void *dirent, - filldir_t filldir, struct ctl_table *table) + filldir_t filldir, + struct ctl_table_header *head, + struct ctl_table *table) { - struct ctl_table_header *head; - struct ctl_table *child_table = NULL; struct dentry *child, *dir = filp->f_path.dentry; struct inode *inode; struct qstr qname; ino_t ino = 0; unsigned type = DT_UNKNOWN; - int ret; qname.name = table->procname; qname.len = strlen(table->procname); qname.hash = full_name_hash(qname.name, qname.len); - /* Suppress duplicates. - * Only fill a directory entry if it is the value that - * an ordinary lookup of that name returns. Hide all - * others. - * - * If we ever cache this translation in the dcache - * I should do a dcache lookup first. But for now - * it is just simpler not to. - */ - ret = 0; - child_table = do_proc_sys_lookup(dir, &qname, &head); - sysctl_head_finish(head); - if (child_table != table) - return 0; - child = d_lookup(dir, &qname); if (!child) { - struct dentry *new; - new = d_alloc(dir, &qname); - if (new) { - inode = proc_sys_make_inode(dir->d_inode, table); - if (!inode) - child = ERR_PTR(-ENOMEM); - else { - new->d_op = &proc_sys_dentry_operations; - d_add(new, inode); + child = d_alloc(dir, &qname); + if (child) { + inode = proc_sys_make_inode(dir->d_sb, head, table); + if (!inode) { + dput(child); + return -ENOMEM; + } else { + child->d_op = &proc_sys_dentry_operations; + d_add(child, inode); } - if (child) - dput(new); - else - child = new; + } else { + return -ENOMEM; } } - if (!child || IS_ERR(child) || !child->d_inode) - goto end_instantiate; inode = child->d_inode; - if (inode) { - ino = inode->i_ino; - type = inode->i_mode >> 12; - } + ino = inode->i_ino; + type = inode->i_mode >> 12; dput(child); -end_instantiate: - if (!ino) - ino= find_inode_number(dir, &qname); - if (!ino) - ino = 1; - return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); + return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); +} + +static int scan(struct ctl_table_header *head, ctl_table *table, + unsigned long *pos, struct file *file, + void *dirent, filldir_t filldir) +{ + + for (; table->ctl_name || table->procname; table++, (*pos)++) { + int res; + + /* Can't do anything without a proc name */ + if (!table->procname) + continue; + + if (*pos < file->f_pos) + continue; + + res = proc_sys_fill_cache(file, dirent, filldir, head, table); + if (res) + return res; + + file->f_pos = *pos + 1; + } + return 0; } static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct dentry *dentry = filp->f_dentry; + struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; - struct ctl_table_header *head = NULL; - struct ctl_table *table; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; + struct ctl_table_header *h = NULL; unsigned long pos; - int ret; + int ret = -EINVAL; + + if (IS_ERR(head)) + return PTR_ERR(head); - ret = -ENOTDIR; - if (!S_ISDIR(inode->i_mode)) + if (table && !table->child) { + WARN_ON(1); goto out; + } + + table = table ? table->child : head->ctl_table; ret = 0; /* Avoid a switch here: arm builds fail with missing __cmpdi2 */ @@ -311,30 +273,17 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) } pos = 2; - /* - Find each instance of the directory - * - Read all entries in each instance - * - Before returning an entry to user space lookup the entry - * by name and if I find a different entry don't return - * this one because it means it is a buried dup. - * For sysctl this should only happen for directory entries. - */ - for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) { - table = proc_sys_lookup_table(dentry, head->ctl_table); + ret = scan(head, table, &pos, filp, dirent, filldir); + if (ret) + goto out; - if (!table) + for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) { + if (h->attached_to != table) continue; - - for (; table->ctl_name || table->procname; table++, pos++) { - /* Can't do anything without a proc name */ - if (!table->procname) - continue; - - if (pos < filp->f_pos) - continue; - - if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0) - goto out; - filp->f_pos = pos + 1; + ret = scan(h, h->attached_by, &pos, filp, dirent, filldir); + if (ret) { + sysctl_head_finish(h); + break; } } ret = 1; @@ -349,47 +298,18 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata * * sysctl entries that are not writeable, * are _NOT_ writeable, capabilities or not. */ - struct ctl_table_header *head; - struct ctl_table *table; - struct dentry *dentry; - int mode; - int depth; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; int error; - head = NULL; - depth = PROC_I(inode)->fd; - - /* First check the cached permissions, in case we don't have - * enough information to lookup the sysctl table entry. - */ - error = -EACCES; - mode = inode->i_mode; - - if (current->euid == 0) - mode >>= 6; - else if (in_group_p(0)) - mode >>= 3; - - if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask) - error = 0; - - /* If we can't get a sysctl table entry the permission - * checks on the cached mode will have to be enough. - */ - if (!nd || !depth) - goto out; + if (IS_ERR(head)) + return PTR_ERR(head); - dentry = nd->path.dentry; - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); + if (!table) /* global root - r-xr-xr-x */ + error = mask & MAY_WRITE ? -EACCES : 0; + else /* Use the permissions on the sysctl table entry */ + error = sysctl_perm(head->root, table, mask); - /* If the entry does not exist deny permission */ - error = -EACCES; - if (!table) - goto out; - - /* Use the permissions on the sysctl table entry */ - error = sysctl_perm(head->root, table, mask); -out: sysctl_head_finish(head); return error; } @@ -409,33 +329,70 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr) return error; } -/* I'm lazy and don't distinguish between files and directories, - * until access time. - */ +static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; + + if (IS_ERR(head)) + return PTR_ERR(head); + + generic_fillattr(inode, stat); + if (table) + stat->mode = (stat->mode & S_IFMT) | table->mode; + + sysctl_head_finish(head); + return 0; +} + static const struct file_operations proc_sys_file_operations = { .read = proc_sys_read, .write = proc_sys_write, +}; + +static const struct file_operations proc_sys_dir_file_operations = { .readdir = proc_sys_readdir, }; static const struct inode_operations proc_sys_inode_operations = { + .permission = proc_sys_permission, + .setattr = proc_sys_setattr, + .getattr = proc_sys_getattr, +}; + +static const struct inode_operations proc_sys_dir_operations = { .lookup = proc_sys_lookup, .permission = proc_sys_permission, .setattr = proc_sys_setattr, + .getattr = proc_sys_getattr, }; static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct ctl_table_header *head; - struct ctl_table *table; - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); - proc_sys_refresh_inode(dentry->d_inode, table); - sysctl_head_finish(head); - return !!table; + return !PROC_I(dentry->d_inode)->sysctl->unregistering; +} + +static int proc_sys_delete(struct dentry *dentry) +{ + return !!PROC_I(dentry->d_inode)->sysctl->unregistering; +} + +static int proc_sys_compare(struct dentry *dir, struct qstr *qstr, + struct qstr *name) +{ + struct dentry *dentry = container_of(qstr, struct dentry, d_name); + if (qstr->len != name->len) + return 1; + if (memcmp(qstr->name, name->name, name->len)) + return 1; + return !sysctl_is_seen(PROC_I(dentry->d_inode)->sysctl); } static struct dentry_operations proc_sys_dentry_operations = { .d_revalidate = proc_sys_revalidate, + .d_delete = proc_sys_delete, + .d_compare = proc_sys_compare, }; static struct proc_dir_entry *proc_sys_root; @@ -443,8 +400,8 @@ static struct proc_dir_entry *proc_sys_root; int proc_sys_init(void) { proc_sys_root = proc_mkdir("sys", NULL); - proc_sys_root->proc_iops = &proc_sys_inode_operations; - proc_sys_root->proc_fops = &proc_sys_file_operations; + proc_sys_root->proc_iops = &proc_sys_dir_operations; + proc_sys_root->proc_fops = &proc_sys_dir_file_operations; proc_sys_root->nlink = 0; return 0; } diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index f560d1705afe..fb61850d1cfc 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -282,11 +282,16 @@ union proc_op { struct task_struct *task); }; +struct ctl_table_header; +struct ctl_table; + struct proc_inode { struct pid *pid; int fd; union proc_op op; struct proc_dir_entry *pde; + struct ctl_table_header *sysctl; + struct ctl_table *sysctl_entry; struct inode vfs_inode; }; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 3f6599aeb0db..d0437f36921f 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -961,6 +961,7 @@ struct ctl_table_header; extern void sysctl_head_get(struct ctl_table_header *); extern void sysctl_head_put(struct ctl_table_header *); +extern int sysctl_is_seen(struct ctl_table_header *); extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c9a0af887033..ff5abcca5ddf 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1932,6 +1932,21 @@ void unregister_sysctl_table(struct ctl_table_header * header) spin_unlock(&sysctl_lock); } +int sysctl_is_seen(struct ctl_table_header *p) +{ + struct ctl_table_set *set = p->set; + int res; + spin_lock(&sysctl_lock); + if (p->unregistering) + res = 0; + else if (!set->is_seen) + res = 1; + else + res = set->is_seen(set); + spin_unlock(&sysctl_lock); + return res; +} + void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)) -- cgit v1.2.3 From e6305c43eda10ebfd2ad9e35d6e172ccc7bb3695 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 21:03:57 -0400 Subject: [PATCH] sanitize ->permission() prototype * kill nameidata * argument; map the 3 bits in ->flags anybody cares about to new MAY_... ones and pass with the mask. * kill redundant gfs2_iop_permission() * sanitize ecryptfs_permission() * fix remaining places where ->permission() instances might barf on new MAY_... found in mask. The obvious next target in that direction is permission(9) folded fix for nfs_permission() breakage from Miklos Szeredi Signed-off-by: Al Viro --- fs/afs/internal.h | 4 +--- fs/afs/security.c | 2 +- fs/bad_inode.c | 3 +-- fs/cifs/cifsfs.c | 2 +- fs/coda/dir.c | 4 +++- fs/coda/pioctl.c | 6 ++---- fs/ecryptfs/inode.c | 17 ++--------------- fs/ext2/acl.c | 2 +- fs/ext2/acl.h | 2 +- fs/ext3/acl.c | 2 +- fs/ext3/acl.h | 2 +- fs/ext4/acl.c | 2 +- fs/ext4/acl.h | 2 +- fs/fuse/dir.c | 6 +++--- fs/gfs2/ops_inode.c | 12 +++--------- fs/hfs/inode.c | 3 +-- fs/hfsplus/inode.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/jffs2/acl.c | 2 +- fs/jffs2/acl.h | 2 +- fs/jfs/acl.c | 2 +- fs/jfs/jfs_acl.h | 2 +- fs/namei.c | 23 +++++++++++++++++------ fs/nfs/dir.c | 11 +++++------ fs/ocfs2/file.c | 2 +- fs/ocfs2/file.h | 3 +-- fs/proc/base.c | 3 +-- fs/proc/proc_sysctl.c | 2 +- fs/reiserfs/xattr.c | 2 +- fs/smbfs/file.c | 4 ++-- fs/xfs/linux-2.6/xfs_iops.c | 3 +-- include/linux/coda_linux.h | 2 +- include/linux/fs.h | 5 ++++- include/linux/nfs_fs.h | 2 +- include/linux/reiserfs_xattr.h | 2 +- include/linux/shmem_fs.h | 2 +- kernel/sysctl.c | 10 +++++----- mm/shmem_acl.c | 2 +- 38 files changed, 74 insertions(+), 87 deletions(-) (limited to 'include/linux') diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 7102824ba847..3cb6920ff30b 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -469,8 +469,6 @@ extern bool afs_cm_incoming_call(struct afs_call *); extern const struct inode_operations afs_dir_inode_operations; extern const struct file_operations afs_dir_file_operations; -extern int afs_permission(struct inode *, int, struct nameidata *); - /* * file.c */ @@ -605,7 +603,7 @@ extern void afs_clear_permits(struct afs_vnode *); extern void afs_cache_permit(struct afs_vnode *, struct key *, long); extern void afs_zap_permits(struct rcu_head *); extern struct key *afs_request_key(struct afs_cell *); -extern int afs_permission(struct inode *, int, struct nameidata *); +extern int afs_permission(struct inode *, int); /* * server.c diff --git a/fs/afs/security.c b/fs/afs/security.c index 3bcbeceba1bb..3ef504370034 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -284,7 +284,7 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key, * - AFS ACLs are attached to directories only, and a file is controlled by its * parent directory's ACL */ -int afs_permission(struct inode *inode, int mask, struct nameidata *nd) +int afs_permission(struct inode *inode, int mask) { struct afs_vnode *vnode = AFS_FS_I(inode); afs_access_t uninitialized_var(access); diff --git a/fs/bad_inode.c b/fs/bad_inode.c index f1c2ea8342f5..5f1538c03b1b 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -243,8 +243,7 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer, return -EIO; } -static int bad_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int bad_inode_permission(struct inode *inode, int mask) { return -EIO; } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index fe5f6809cba6..1ec7076f7b24 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -267,7 +267,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } -static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd) +static int cifs_permission(struct inode *inode, int mask) { struct cifs_sb_info *cifs_sb; diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 3d2580e00a3e..c5916228243c 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -137,9 +137,11 @@ exit: } -int coda_permission(struct inode *inode, int mask, struct nameidata *nd) +int coda_permission(struct inode *inode, int mask) { int error = 0; + + mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (!mask) return 0; diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c21a1f552a63..c38a98974fb0 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -24,8 +24,7 @@ #include /* pioctl ops */ -static int coda_ioctl_permission(struct inode *inode, int mask, - struct nameidata *nd); +static int coda_ioctl_permission(struct inode *inode, int mask); static int coda_pioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long user_data); @@ -42,8 +41,7 @@ const struct file_operations coda_ioctl_operations = { }; /* the coda pioctl inode ops */ -static int coda_ioctl_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int coda_ioctl_permission(struct inode *inode, int mask) { return 0; } diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index d755455e3bff..32f4228efcd5 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -830,22 +830,9 @@ out: } static int -ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) +ecryptfs_permission(struct inode *inode, int mask) { - int rc; - - if (nd) { - struct vfsmount *vfsmnt_save = nd->path.mnt; - struct dentry *dentry_save = nd->path.dentry; - - nd->path.mnt = ecryptfs_dentry_to_lower_mnt(nd->path.dentry); - nd->path.dentry = ecryptfs_dentry_to_lower(nd->path.dentry); - rc = permission(ecryptfs_inode_to_lower(inode), mask, nd); - nd->path.mnt = vfsmnt_save; - nd->path.dentry = dentry_save; - } else - rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL); - return rc; + return permission(ecryptfs_inode_to_lower(inode), mask, NULL); } /** diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index e58669e1b87c..ae8c4f850b27 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -294,7 +294,7 @@ ext2_check_acl(struct inode *inode, int mask) } int -ext2_permission(struct inode *inode, int mask, struct nameidata *nd) +ext2_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext2_check_acl); } diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h index 0bde85bafe38..b42cf578554b 100644 --- a/fs/ext2/acl.h +++ b/fs/ext2/acl.h @@ -58,7 +58,7 @@ static inline int ext2_acl_count(size_t size) #define EXT2_ACL_NOT_CACHED ((void *)-1) /* acl.c */ -extern int ext2_permission (struct inode *, int, struct nameidata *); +extern int ext2_permission (struct inode *, int); extern int ext2_acl_chmod (struct inode *); extern int ext2_init_acl (struct inode *, struct inode *); diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index a754d1848173..b60bb241880c 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -299,7 +299,7 @@ ext3_check_acl(struct inode *inode, int mask) } int -ext3_permission(struct inode *inode, int mask, struct nameidata *nd) +ext3_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext3_check_acl); } diff --git a/fs/ext3/acl.h b/fs/ext3/acl.h index 0d1e6279cbfd..42da16b8cac0 100644 --- a/fs/ext3/acl.h +++ b/fs/ext3/acl.h @@ -58,7 +58,7 @@ static inline int ext3_acl_count(size_t size) #define EXT3_ACL_NOT_CACHED ((void *)-1) /* acl.c */ -extern int ext3_permission (struct inode *, int, struct nameidata *); +extern int ext3_permission (struct inode *, int); extern int ext3_acl_chmod (struct inode *); extern int ext3_init_acl (handle_t *, struct inode *, struct inode *); diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 3c8dab880d91..c7d04e165446 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -299,7 +299,7 @@ ext4_check_acl(struct inode *inode, int mask) } int -ext4_permission(struct inode *inode, int mask, struct nameidata *nd) +ext4_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext4_check_acl); } diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h index 26a5c1abf147..cd2b855a07d6 100644 --- a/fs/ext4/acl.h +++ b/fs/ext4/acl.h @@ -58,7 +58,7 @@ static inline int ext4_acl_count(size_t size) #define EXT4_ACL_NOT_CACHED ((void *)-1) /* acl.c */ -extern int ext4_permission (struct inode *, int, struct nameidata *); +extern int ext4_permission (struct inode *, int); extern int ext4_acl_chmod (struct inode *); extern int ext4_init_acl (handle_t *, struct inode *, struct inode *); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 51d0035ff07e..48a7934cb950 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -898,7 +898,7 @@ static int fuse_access(struct inode *inode, int mask) return PTR_ERR(req); memset(&inarg, 0, sizeof(inarg)); - inarg.mask = mask; + inarg.mask = mask & (MAY_READ | MAY_WRITE | MAY_EXEC); req->in.h.opcode = FUSE_ACCESS; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; @@ -927,7 +927,7 @@ static int fuse_access(struct inode *inode, int mask) * access request is sent. Execute permission is still checked * locally based on file mode. */ -static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) +static int fuse_permission(struct inode *inode, int mask) { struct fuse_conn *fc = get_fuse_conn(inode); bool refreshed = false; @@ -962,7 +962,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) exist. So if permissions are revoked this won't be noticed immediately, only after the attribute timeout has expired */ - } else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) { + } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 1e252dfc5294..4e982532f085 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -915,12 +915,6 @@ int gfs2_permission(struct inode *inode, int mask) return error; } -static int gfs2_iop_permission(struct inode *inode, int mask, - struct nameidata *nd) -{ - return gfs2_permission(inode, mask); -} - static int setattr_size(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); @@ -1150,7 +1144,7 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name) } const struct inode_operations gfs2_file_iops = { - .permission = gfs2_iop_permission, + .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, .setxattr = gfs2_setxattr, @@ -1169,7 +1163,7 @@ const struct inode_operations gfs2_dir_iops = { .rmdir = gfs2_rmdir, .mknod = gfs2_mknod, .rename = gfs2_rename, - .permission = gfs2_iop_permission, + .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, .setxattr = gfs2_setxattr, @@ -1181,7 +1175,7 @@ const struct inode_operations gfs2_dir_iops = { const struct inode_operations gfs2_symlink_iops = { .readlink = gfs2_readlink, .follow_link = gfs2_follow_link, - .permission = gfs2_iop_permission, + .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, .setxattr = gfs2_setxattr, diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index dc4ec640e875..aa73f3fd5dd9 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -511,8 +511,7 @@ void hfs_clear_inode(struct inode *inode) } } -static int hfs_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int hfs_permission(struct inode *inode, int mask) { if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) return 0; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index cc3b5e24339b..d4014e3044d2 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -238,7 +238,7 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); } -static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd) +static int hfsplus_permission(struct inode *inode, int mask) { /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup, * open_exec has the same test, so it's still not executable, if a x bit diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 5222345ddccf..d6ecabf4d231 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -822,7 +822,7 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, return err; } -int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd) +int hostfs_permission(struct inode *ino, int desired) { char *name; int r = 0, w = 0, x = 0, err; diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 4c80404a9aba..d98713777a1b 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -314,7 +314,7 @@ static int jffs2_check_acl(struct inode *inode, int mask) return -EAGAIN; } -int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) +int jffs2_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, jffs2_check_acl); } diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index 0bb7f003fd80..8ca058aed384 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -28,7 +28,7 @@ struct jffs2_acl_header { #define JFFS2_ACL_NOT_CACHED ((void *)-1) -extern int jffs2_permission(struct inode *, int, struct nameidata *); +extern int jffs2_permission(struct inode *, int); extern int jffs2_acl_chmod(struct inode *); extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *); extern int jffs2_init_acl_post(struct inode *); diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index 4d84bdc88299..d3e5c33665de 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -140,7 +140,7 @@ static int jfs_check_acl(struct inode *inode, int mask) return -EAGAIN; } -int jfs_permission(struct inode *inode, int mask, struct nameidata *nd) +int jfs_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, jfs_check_acl); } diff --git a/fs/jfs/jfs_acl.h b/fs/jfs/jfs_acl.h index 455fa4292045..88475f10a389 100644 --- a/fs/jfs/jfs_acl.h +++ b/fs/jfs/jfs_acl.h @@ -20,7 +20,7 @@ #ifdef CONFIG_JFS_POSIX_ACL -int jfs_permission(struct inode *, int, struct nameidata *); +int jfs_permission(struct inode *, int); int jfs_init_acl(tid_t, struct inode *, struct inode *); int jfs_setattr(struct dentry *, struct iattr *); diff --git a/fs/namei.c b/fs/namei.c index 3b26a240ade9..46af98ed136b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -185,6 +185,8 @@ int generic_permission(struct inode *inode, int mask, { umode_t mode = inode->i_mode; + mask &= MAY_READ | MAY_WRITE | MAY_EXEC; + if (current->fsuid == inode->i_uid) mode >>= 6; else { @@ -203,7 +205,7 @@ int generic_permission(struct inode *inode, int mask, /* * If the DACs are ok we don't need any capability check. */ - if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)) + if ((mask & ~mode) == 0) return 0; check_capabilities: @@ -228,7 +230,7 @@ int generic_permission(struct inode *inode, int mask, int permission(struct inode *inode, int mask, struct nameidata *nd) { - int retval, submask; + int retval; struct vfsmount *mnt = NULL; if (nd) @@ -261,9 +263,17 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) } /* Ordinary permission routines do not understand MAY_APPEND. */ - submask = mask & ~MAY_APPEND; if (inode->i_op && inode->i_op->permission) { - retval = inode->i_op->permission(inode, submask, nd); + int extra = 0; + if (nd) { + if (nd->flags & LOOKUP_ACCESS) + extra |= MAY_ACCESS; + if (nd->flags & LOOKUP_CHDIR) + extra |= MAY_CHDIR; + if (nd->flags & LOOKUP_OPEN) + extra |= MAY_OPEN; + } + retval = inode->i_op->permission(inode, mask | extra); if (!retval) { /* * Exec permission on a regular file is denied if none @@ -277,7 +287,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) return -EACCES; } } else { - retval = generic_permission(inode, submask, NULL); + retval = generic_permission(inode, mask, NULL); } if (retval) return retval; @@ -286,7 +296,8 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) if (retval) return retval; - return security_inode_permission(inode, mask, nd); + return security_inode_permission(inode, + mask & (MAY_READ|MAY_WRITE|MAY_EXEC), nd); } /** diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 28a238dab23a..74f92b717f78 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1884,7 +1884,7 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) return status; nfs_access_add_cache(inode, &cache); out: - if ((cache.mask & mask) == mask) + if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) return 0; return -EACCES; } @@ -1907,17 +1907,17 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags)); } -int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) +int nfs_permission(struct inode *inode, int mask) { struct rpc_cred *cred; int res = 0; nfs_inc_stats(inode, NFSIOS_VFSACCESS); - if (mask == 0) + if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) goto out; /* Is this sys_access() ? */ - if (nd != NULL && (nd->flags & LOOKUP_ACCESS)) + if (mask & MAY_ACCESS) goto force_lookup; switch (inode->i_mode & S_IFMT) { @@ -1926,8 +1926,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) case S_IFREG: /* NFSv4 has atomic_open... */ if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN) - && nd != NULL - && (nd->flags & LOOKUP_OPEN)) + && (mask & MAY_OPEN)) goto out; break; case S_IFDIR: diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index e8514e8b6ce8..be2dd95d3a1d 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1176,7 +1176,7 @@ bail: return err; } -int ocfs2_permission(struct inode *inode, int mask, struct nameidata *nd) +int ocfs2_permission(struct inode *inode, int mask) { int ret; diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index 048ddcaf5c80..1e27b4d017ea 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -62,8 +62,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, int ocfs2_setattr(struct dentry *dentry, struct iattr *attr); int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); -int ocfs2_permission(struct inode *inode, int mask, - struct nameidata *nd); +int ocfs2_permission(struct inode *inode, int mask); int ocfs2_should_update_atime(struct inode *inode, struct vfsmount *vfsmnt); diff --git a/fs/proc/base.c b/fs/proc/base.c index 81bce6791bfc..d82d800389f6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1859,8 +1859,7 @@ static const struct file_operations proc_fd_operations = { * /proc/pid/fd needs a special permission handler so that a process can still * access /proc/self/fd after it has executed a setuid(). */ -static int proc_fd_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int proc_fd_permission(struct inode *inode, int mask) { int rv; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index fa1ec2433e44..f9a8b892718f 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -292,7 +292,7 @@ out: return ret; } -static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd) +static int proc_sys_permission(struct inode *inode, int mask) { /* * sysctl entries that are not writeable, diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index d7c4935c1034..bb3cb5b7cdb2 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -1250,7 +1250,7 @@ static int reiserfs_check_acl(struct inode *inode, int mask) return error; } -int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd) +int reiserfs_permission(struct inode *inode, int mask) { /* * We don't do permission checks on the internal objects. diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 2294783320cb..e4f8d51a5553 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -408,7 +408,7 @@ smb_file_release(struct inode *inode, struct file * file) * privileges, so we need our own check for this. */ static int -smb_file_permission(struct inode *inode, int mask, struct nameidata *nd) +smb_file_permission(struct inode *inode, int mask) { int mode = inode->i_mode; int error = 0; @@ -417,7 +417,7 @@ smb_file_permission(struct inode *inode, int mask, struct nameidata *nd) /* Look at user permissions */ mode >>= 6; - if ((mode & 7 & mask) != mask) + if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) error = -EACCES; return error; } diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 2bf287ef5489..5fc61c824bb9 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -589,8 +589,7 @@ xfs_check_acl( STATIC int xfs_vn_permission( struct inode *inode, - int mask, - struct nameidata *nd) + int mask) { return generic_permission(inode, mask, xfs_check_acl); } diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h index 31b75311e2ca..dcc228aa335a 100644 --- a/include/linux/coda_linux.h +++ b/include/linux/coda_linux.h @@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations; /* operations shared over more than one file */ int coda_open(struct inode *i, struct file *f); int coda_release(struct inode *i, struct file *f); -int coda_permission(struct inode *inode, int mask, struct nameidata *nd); +int coda_permission(struct inode *inode, int mask); int coda_revalidate_inode(struct dentry *); int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *); int coda_setattr(struct dentry *, struct iattr *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 7721a2ac9c0e..6c923c9b79bc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -60,6 +60,9 @@ extern int dir_notify_enable; #define MAY_WRITE 2 #define MAY_READ 4 #define MAY_APPEND 8 +#define MAY_ACCESS 16 +#define MAY_CHDIR 32 +#define MAY_OPEN 64 #define FMODE_READ 1 #define FMODE_WRITE 2 @@ -1272,7 +1275,7 @@ struct inode_operations { void * (*follow_link) (struct dentry *, struct nameidata *); void (*put_link) (struct dentry *, struct nameidata *, void *); void (*truncate) (struct inode *); - int (*permission) (struct inode *, int, struct nameidata *); + int (*permission) (struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 29d261918734..f08f9ca602af 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -332,7 +332,7 @@ extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr); extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); -extern int nfs_permission(struct inode *, int, struct nameidata *); +extern int nfs_permission(struct inode *, int); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); extern int nfs_attribute_timeout(struct inode *inode); diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h index 66a96814d614..af135ae895db 100644 --- a/include/linux/reiserfs_xattr.h +++ b/include/linux/reiserfs_xattr.h @@ -55,7 +55,7 @@ int reiserfs_removexattr(struct dentry *dentry, const char *name); int reiserfs_delete_xattrs(struct inode *inode); int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs); int reiserfs_xattr_init(struct super_block *sb, int mount_flags); -int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd); +int reiserfs_permission(struct inode *inode, int mask); int reiserfs_xattr_del(struct inode *, const char *); int reiserfs_xattr_get(const struct inode *, const char *, void *, size_t); diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index f2d12d5a21b8..fd83f2584b15 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -43,7 +43,7 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) } #ifdef CONFIG_TMPFS_POSIX_ACL -int shmem_permission(struct inode *, int, struct nameidata *); +int shmem_permission(struct inode *, int); int shmem_acl_init(struct inode *, struct inode *); void shmem_acl_destroy_inode(struct inode *); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ff5abcca5ddf..911d846f0503 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1516,9 +1516,9 @@ static int do_sysctl_strategy(struct ctl_table_root *root, int op = 0, rc; if (oldval) - op |= 004; + op |= MAY_READ; if (newval) - op |= 002; + op |= MAY_WRITE; if (sysctl_perm(root, table, op)) return -EPERM; @@ -1560,7 +1560,7 @@ repeat: if (n == table->ctl_name) { int error; if (table->child) { - if (sysctl_perm(root, table, 001)) + if (sysctl_perm(root, table, MAY_EXEC)) return -EPERM; name++; nlen--; @@ -1635,7 +1635,7 @@ static int test_perm(int mode, int op) mode >>= 6; else if (in_egroup_p(0)) mode >>= 3; - if ((mode & op & 0007) == op) + if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) return 0; return -EACCES; } @@ -1645,7 +1645,7 @@ int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) int error; int mode; - error = security_sysctl(table, op); + error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); if (error) return error; diff --git a/mm/shmem_acl.c b/mm/shmem_acl.c index f5664c5b9eb1..8e5aadd7dcd6 100644 --- a/mm/shmem_acl.c +++ b/mm/shmem_acl.c @@ -191,7 +191,7 @@ shmem_check_acl(struct inode *inode, int mask) * shmem_permission - permission() inode operation */ int -shmem_permission(struct inode *inode, int mask, struct nameidata *nd) +shmem_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, shmem_check_acl); } -- cgit v1.2.3 From 2f1936b87783a3a56c9441b27b9ba7a747f11e8e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 24 Jun 2008 16:50:14 +0200 Subject: [patch 3/5] vfs: change remove_suid() to file_remove_suid() All calls to remove_suid() are made with a file pointer, because (similarly to file_update_time) it is called when the file is written. Clean up callers by passing in a file instead of a dentry. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 2 +- fs/ntfs/file.c | 2 +- fs/splice.c | 4 ++-- fs/xfs/linux-2.6/xfs_lrw.c | 2 +- include/linux/fs.h | 2 +- mm/filemap.c | 7 ++++--- mm/filemap_xip.c | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 67ff2c6a8f63..2bada6bbc317 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -893,7 +893,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (count == 0) goto out; - err = remove_suid(file->f_path.dentry); + err = file_remove_suid(file); if (err) goto out; diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 3c5550cd11d6..d020866d4232 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2118,7 +2118,7 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb, goto out; if (!count) goto out; - err = remove_suid(file->f_path.dentry); + err = file_remove_suid(file); if (err) goto out; file_update_time(file); diff --git a/fs/splice.c b/fs/splice.c index 47dc1a445d1f..b30311ba8af6 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -772,7 +772,7 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out, ssize_t ret; int err; - err = remove_suid(out->f_path.dentry); + err = file_remove_suid(out); if (unlikely(err)) return err; @@ -830,7 +830,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, ssize_t ret; inode_double_lock(inode, pipe->inode); - ret = remove_suid(out->f_path.dentry); + ret = file_remove_suid(out); if (likely(!ret)) ret = __splice_from_pipe(pipe, &sd, pipe_to_file); inode_double_unlock(inode, pipe->inode); diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c index 5e3b57516ec7..82333b3e118e 100644 --- a/fs/xfs/linux-2.6/xfs_lrw.c +++ b/fs/xfs/linux-2.6/xfs_lrw.c @@ -711,7 +711,7 @@ start: !capable(CAP_FSETID)) { error = xfs_write_clear_setuid(xip); if (likely(!error)) - error = -remove_suid(file->f_path.dentry); + error = -file_remove_suid(file); if (unlikely(error)) { goto out_unlock_internal; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 6c923c9b79bc..1a3546e69f9e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1834,7 +1834,7 @@ extern void clear_inode(struct inode *); extern void destroy_inode(struct inode *); extern struct inode *new_inode(struct super_block *); extern int should_remove_suid(struct dentry *); -extern int remove_suid(struct dentry *); +extern int file_remove_suid(struct file *); extern void __insert_inode_hash(struct inode *, unsigned long hashval); extern void remove_inode_hash(struct inode *); diff --git a/mm/filemap.c b/mm/filemap.c index 2ed8b0389c51..5de7633e1dbe 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1758,8 +1758,9 @@ static int __remove_suid(struct dentry *dentry, int kill) return notify_change(dentry, &newattrs); } -int remove_suid(struct dentry *dentry) +int file_remove_suid(struct file *file) { + struct dentry *dentry = file->f_path.dentry; int killsuid = should_remove_suid(dentry); int killpriv = security_inode_need_killpriv(dentry); int error = 0; @@ -1773,7 +1774,7 @@ int remove_suid(struct dentry *dentry) return error; } -EXPORT_SYMBOL(remove_suid); +EXPORT_SYMBOL(file_remove_suid); static size_t __iovec_copy_from_user_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) @@ -2529,7 +2530,7 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, if (count == 0) goto out; - err = remove_suid(file->f_path.dentry); + err = file_remove_suid(file); if (err) goto out; diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 3e744abcce9d..98a3f31ccd6a 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -380,7 +380,7 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len, if (count == 0) goto out_backing; - ret = remove_suid(filp->f_path.dentry); + ret = file_remove_suid(filp); if (ret) goto out_backing; -- cgit v1.2.3 From db2e747b14991a4c6a5c98b0e5f552a193237c03 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 24 Jun 2008 16:50:16 +0200 Subject: [patch 5/5] vfs: remove mode parameter from vfs_symlink() Remove the unused mode parameter from vfs_symlink and callers. Thanks to Tetsuo Handa for noticing. CC: Tetsuo Handa Signed-off-by: Miklos Szeredi --- fs/ecryptfs/inode.c | 4 +--- fs/namei.c | 4 ++-- fs/nfsd/vfs.c | 10 ++-------- include/linux/fs.h | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 32f4228efcd5..f25caf2b0887 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -465,7 +465,6 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; - umode_t mode; char *encoded_symname; int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; @@ -473,7 +472,6 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - mode = S_IALLUGO; encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, strlen(symname), &encoded_symname); @@ -482,7 +480,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, goto out_lock; } rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, - encoded_symname, mode); + encoded_symname); kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; diff --git a/fs/namei.c b/fs/namei.c index 3b67be7631dc..ae0e56fdb742 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2434,7 +2434,7 @@ asmlinkage long sys_unlink(const char __user *pathname) return do_unlinkat(AT_FDCWD, pathname); } -int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode) +int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) { int error = may_create(dir, dentry, NULL); @@ -2483,7 +2483,7 @@ asmlinkage long sys_symlinkat(const char __user *oldname, error = mnt_want_write(nd.path.mnt); if (error) goto out_dput; - error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO); + error = vfs_symlink(nd.path.dentry->d_inode, dentry, from); mnt_drop_write(nd.path.mnt); out_dput: dput(dentry); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 0f4481e0502d..ad1ad59e3742 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1516,7 +1516,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, struct dentry *dentry, *dnew; __be32 err, cerr; int host_err; - umode_t mode; err = nfserr_noent; if (!flen || !plen) @@ -1535,11 +1534,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, if (IS_ERR(dnew)) goto out_nfserr; - mode = S_IALLUGO; - /* Only the MODE ATTRibute is even vaguely meaningful */ - if (iap && (iap->ia_valid & ATTR_MODE)) - mode = iap->ia_mode & S_IALLUGO; - host_err = mnt_want_write(fhp->fh_export->ex_path.mnt); if (host_err) goto out_nfserr; @@ -1551,11 +1545,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, else { strncpy(path_alloced, path, plen); path_alloced[plen] = 0; - host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode); + host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced); kfree(path_alloced); } } else - host_err = vfs_symlink(dentry->d_inode, dnew, path, mode); + host_err = vfs_symlink(dentry->d_inode, dnew, path); if (!host_err) { if (EX_ISSYNC(fhp->fh_export)) diff --git a/include/linux/fs.h b/include/linux/fs.h index 1a3546e69f9e..25998e803fc2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1139,7 +1139,7 @@ extern int vfs_permission(struct nameidata *, int); extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *); extern int vfs_mkdir(struct inode *, struct dentry *, int); extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t); -extern int vfs_symlink(struct inode *, struct dentry *, const char *, int); +extern int vfs_symlink(struct inode *, struct dentry *, const char *); extern int vfs_link(struct dentry *, struct inode *, struct dentry *); extern int vfs_rmdir(struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *); -- cgit v1.2.3 From 8bb79224b87aab92071e94d46e70bd160d89bf34 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 16 Jul 2008 09:51:03 -0400 Subject: [PATCH] permission checks for chdir need special treatment only on the last step ... so we ought to pass MAY_CHDIR to vfs_permission() instead of having it triggered on every step of preceding pathname resolution. LOOKUP_CHDIR is killed by that. Signed-off-by: Al Viro --- fs/namei.c | 2 -- fs/open.c | 5 ++--- include/linux/namei.h | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/namei.c b/fs/namei.c index ae0e56fdb742..6c76e1ee9c45 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -268,8 +268,6 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) if (nd) { if (nd->flags & LOOKUP_ACCESS) extra |= MAY_ACCESS; - if (nd->flags & LOOKUP_CHDIR) - extra |= MAY_CHDIR; if (nd->flags & LOOKUP_OPEN) extra |= MAY_OPEN; } diff --git a/fs/open.c b/fs/open.c index b2e4c93aed03..8e02d42bfe44 100644 --- a/fs/open.c +++ b/fs/open.c @@ -501,12 +501,11 @@ asmlinkage long sys_chdir(const char __user * filename) struct nameidata nd; int error; - error = __user_walk(filename, - LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd); + error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd); if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC); + error = vfs_permission(&nd, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; diff --git a/include/linux/namei.h b/include/linux/namei.h index 24d88e98a626..3cf62d26d493 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -55,7 +55,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_OPEN (0x0100) #define LOOKUP_CREATE (0x0200) #define LOOKUP_ACCESS (0x0400) -#define LOOKUP_CHDIR (0x0800) extern int __user_walk(const char __user *, unsigned, struct nameidata *); extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *); -- cgit v1.2.3 From 7f2da1e7d0330395e5e9e350b879b98a1ea495df Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 May 2008 20:44:54 -0400 Subject: [PATCH] kill altroot long overdue... Signed-off-by: Al Viro --- fs/namei.c | 89 +------------------------------------------ fs/namespace.c | 8 +--- fs/open.c | 3 +- include/asm-alpha/namei.h | 17 --------- include/asm-arm/namei.h | 25 ------------ include/asm-avr32/namei.h | 7 ---- include/asm-blackfin/namei.h | 19 --------- include/asm-cris/namei.h | 17 --------- include/asm-frv/namei.h | 18 --------- include/asm-h8300/namei.h | 17 --------- include/asm-ia64/namei.h | 25 ------------ include/asm-m32r/namei.h | 17 --------- include/asm-m68k/namei.h | 17 --------- include/asm-m68knommu/namei.h | 1 - include/asm-mips/namei.h | 11 ------ include/asm-mn10300/namei.h | 22 ----------- include/asm-parisc/namei.h | 17 --------- include/asm-powerpc/namei.h | 20 ---------- include/asm-s390/namei.h | 21 ---------- include/asm-sh/namei.h | 17 --------- include/asm-sparc/namei.h | 8 ---- include/asm-sparc64/namei.h | 1 - include/asm-um/namei.h | 6 --- include/asm-v850/namei.h | 17 --------- include/asm-x86/namei.h | 11 ------ include/asm-xtensa/namei.h | 26 ------------- include/linux/fs_struct.h | 3 +- include/linux/namei.h | 1 - kernel/exec_domain.c | 1 - kernel/exit.c | 2 - kernel/fork.c | 7 ---- 31 files changed, 5 insertions(+), 466 deletions(-) delete mode 100644 include/asm-alpha/namei.h delete mode 100644 include/asm-arm/namei.h delete mode 100644 include/asm-avr32/namei.h delete mode 100644 include/asm-blackfin/namei.h delete mode 100644 include/asm-cris/namei.h delete mode 100644 include/asm-frv/namei.h delete mode 100644 include/asm-h8300/namei.h delete mode 100644 include/asm-ia64/namei.h delete mode 100644 include/asm-m32r/namei.h delete mode 100644 include/asm-m68k/namei.h delete mode 100644 include/asm-m68knommu/namei.h delete mode 100644 include/asm-mips/namei.h delete mode 100644 include/asm-mn10300/namei.h delete mode 100644 include/asm-parisc/namei.h delete mode 100644 include/asm-powerpc/namei.h delete mode 100644 include/asm-s390/namei.h delete mode 100644 include/asm-sh/namei.h delete mode 100644 include/asm-sparc/namei.h delete mode 100644 include/asm-sparc64/namei.h delete mode 100644 include/asm-um/namei.h delete mode 100644 include/asm-v850/namei.h delete mode 100644 include/asm-x86/namei.h delete mode 100644 include/asm-xtensa/namei.h (limited to 'include/linux') diff --git a/fs/namei.c b/fs/namei.c index 6c76e1ee9c45..095818089ac1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) @@ -562,27 +561,16 @@ out_unlock: return result; } -static int __emul_lookup_dentry(const char *, struct nameidata *); - /* SMP-safe */ -static __always_inline int +static __always_inline void walk_init_root(const char *name, struct nameidata *nd) { struct fs_struct *fs = current->fs; read_lock(&fs->lock); - if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) { - nd->path = fs->altroot; - path_get(&fs->altroot); - read_unlock(&fs->lock); - if (__emul_lookup_dentry(name,nd)) - return 0; - read_lock(&fs->lock); - } nd->path = fs->root; path_get(&fs->root); read_unlock(&fs->lock); - return 1; } /* @@ -623,12 +611,9 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l if (*link == '/') { path_put(&nd->path); - if (!walk_init_root(link, nd)) - /* weird __emul_prefix() stuff did it */ - goto out; + walk_init_root(link, nd); } res = link_path_walk(link, nd); -out: if (nd->depth || res || nd->last_type!=LAST_NORM) return res; /* @@ -1077,67 +1062,6 @@ static int path_walk(const char *name, struct nameidata *nd) return link_path_walk(name, nd); } -/* - * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if - * everything is done. Returns 0 and drops input nd, if lookup failed; - */ -static int __emul_lookup_dentry(const char *name, struct nameidata *nd) -{ - if (path_walk(name, nd)) - return 0; /* something went wrong... */ - - if (!nd->path.dentry->d_inode || - S_ISDIR(nd->path.dentry->d_inode->i_mode)) { - struct path old_path = nd->path; - struct qstr last = nd->last; - int last_type = nd->last_type; - struct fs_struct *fs = current->fs; - - /* - * NAME was not found in alternate root or it's a directory. - * Try to find it in the normal root: - */ - nd->last_type = LAST_ROOT; - read_lock(&fs->lock); - nd->path = fs->root; - path_get(&fs->root); - read_unlock(&fs->lock); - if (path_walk(name, nd) == 0) { - if (nd->path.dentry->d_inode) { - path_put(&old_path); - return 1; - } - path_put(&nd->path); - } - nd->path = old_path; - nd->last = last; - nd->last_type = last_type; - } - return 1; -} - -void set_fs_altroot(void) -{ - char *emul = __emul_prefix(); - struct nameidata nd; - struct path path = {}, old_path; - int err; - struct fs_struct *fs = current->fs; - - if (!emul) - goto set_it; - err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd); - if (!err) - path = nd.path; -set_it: - write_lock(&fs->lock); - old_path = fs->altroot; - fs->altroot = path; - write_unlock(&fs->lock); - if (old_path.dentry) - path_put(&old_path); -} - /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ static int do_path_lookup(int dfd, const char *name, unsigned int flags, struct nameidata *nd) @@ -1153,14 +1077,6 @@ static int do_path_lookup(int dfd, const char *name, if (*name=='/') { read_lock(&fs->lock); - if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) { - nd->path = fs->altroot; - path_get(&fs->altroot); - read_unlock(&fs->lock); - if (__emul_lookup_dentry(name,nd)) - goto out; /* found in altroot */ - read_lock(&fs->lock); - } nd->path = fs->root; path_get(&fs->root); read_unlock(&fs->lock); @@ -1194,7 +1110,6 @@ static int do_path_lookup(int dfd, const char *name, } retval = path_walk(name, nd); -out: if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && nd->path.dentry->d_inode)) audit_inode(name, nd->path.dentry); diff --git a/fs/namespace.c b/fs/namespace.c index f30b11e2240e..c4fcf48acef8 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1972,7 +1972,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, struct fs_struct *fs) { struct mnt_namespace *new_ns; - struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL; + struct vfsmount *rootmnt = NULL, *pwdmnt = NULL; struct vfsmount *p, *q; new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); @@ -2015,10 +2015,6 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, pwdmnt = p; fs->pwd.mnt = mntget(q); } - if (p == fs->altroot.mnt) { - altrootmnt = p; - fs->altroot.mnt = mntget(q); - } } p = next_mnt(p, mnt_ns->root); q = next_mnt(q, new_ns->root); @@ -2029,8 +2025,6 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, mntput(rootmnt); if (pwdmnt) mntput(pwdmnt); - if (altrootmnt) - mntput(altrootmnt); return new_ns; } diff --git a/fs/open.c b/fs/open.c index 8e02d42bfe44..d3a2a00f52dc 100644 --- a/fs/open.c +++ b/fs/open.c @@ -548,7 +548,7 @@ asmlinkage long sys_chroot(const char __user * filename) struct nameidata nd; int error; - error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); + error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); if (error) goto out; @@ -561,7 +561,6 @@ asmlinkage long sys_chroot(const char __user * filename) goto dput_and_out; set_fs_root(current->fs, &nd.path); - set_fs_altroot(); error = 0; dput_and_out: path_put(&nd.path); diff --git a/include/asm-alpha/namei.h b/include/asm-alpha/namei.h deleted file mode 100644 index 5cc9bb39499d..000000000000 --- a/include/asm-alpha/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $ - * linux/include/asm-alpha/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __ALPHA_NAMEI_H -#define __ALPHA_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __ALPHA_NAMEI_H */ diff --git a/include/asm-arm/namei.h b/include/asm-arm/namei.h deleted file mode 100644 index a402d3b9d0f7..000000000000 --- a/include/asm-arm/namei.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * linux/include/asm-arm/namei.h - * - * Routines to handle famous /usr/gnemul - * Derived from the Sparc version of this file - * - * Included from linux/fs/namei.c - */ - -#ifndef __ASMARM_NAMEI_H -#define __ASMARM_NAMEI_H - -#define ARM_BSD_EMUL "usr/gnemul/bsd/" - -static inline char *__emul_prefix(void) -{ - switch (current->personality) { - case PER_BSD: - return ARM_BSD_EMUL; - default: - return NULL; - } -} - -#endif /* __ASMARM_NAMEI_H */ diff --git a/include/asm-avr32/namei.h b/include/asm-avr32/namei.h deleted file mode 100644 index f0a26de06cab..000000000000 --- a/include/asm-avr32/namei.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __ASM_AVR32_NAMEI_H -#define __ASM_AVR32_NAMEI_H - -/* This dummy routine may be changed to something useful */ -#define __emul_prefix() NULL - -#endif /* __ASM_AVR32_NAMEI_H */ diff --git a/include/asm-blackfin/namei.h b/include/asm-blackfin/namei.h deleted file mode 100644 index 8b89a2d65cb4..000000000000 --- a/include/asm-blackfin/namei.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * linux/include/asm/namei.h - * - * Included from linux/fs/namei.c - * - * Changes made by Lineo Inc. May 2001 - */ - -#ifndef __BFIN_NAMEI_H -#define __BFIN_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif diff --git a/include/asm-cris/namei.h b/include/asm-cris/namei.h deleted file mode 100644 index 8a3be7a6d9f6..000000000000 --- a/include/asm-cris/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ - * linux/include/asm-cris/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __CRIS_NAMEI_H -#define __CRIS_NAMEI_H - -/* used to find file-system prefixes for doing emulations - * see for example asm-sparc/namei.h - * we don't use it... - */ - -#define __emul_prefix() NULL - -#endif /* __CRIS_NAMEI_H */ diff --git a/include/asm-frv/namei.h b/include/asm-frv/namei.h deleted file mode 100644 index 4ea57171d951..000000000000 --- a/include/asm-frv/namei.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * include/asm-frv/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __ASM_NAMEI_H -#define __ASM_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif - diff --git a/include/asm-h8300/namei.h b/include/asm-h8300/namei.h deleted file mode 100644 index ab6f196db6e0..000000000000 --- a/include/asm-h8300/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/include/asm-h8300/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __H8300_NAMEI_H -#define __H8300_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif diff --git a/include/asm-ia64/namei.h b/include/asm-ia64/namei.h deleted file mode 100644 index 78e768079083..000000000000 --- a/include/asm-ia64/namei.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _ASM_IA64_NAMEI_H -#define _ASM_IA64_NAMEI_H - -/* - * Modified 1998, 1999, 2001 - * David Mosberger-Tang , Hewlett-Packard Co - */ - -#include -#include - -#define EMUL_PREFIX_LINUX_IA32 "/emul/ia32-linux/" - -static inline char * -__emul_prefix (void) -{ - switch (current->personality) { - case PER_LINUX32: - return EMUL_PREFIX_LINUX_IA32; - default: - return NULL; - } -} - -#endif /* _ASM_IA64_NAMEI_H */ diff --git a/include/asm-m32r/namei.h b/include/asm-m32r/namei.h deleted file mode 100644 index 210f8056b805..000000000000 --- a/include/asm-m32r/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ASM_M32R_NAMEI_H -#define _ASM_M32R_NAMEI_H - -/* - * linux/include/asm-m32r/namei.h - * - * Included from linux/fs/namei.c - */ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_M32R_NAMEI_H */ diff --git a/include/asm-m68k/namei.h b/include/asm-m68k/namei.h deleted file mode 100644 index f33f243b644a..000000000000 --- a/include/asm-m68k/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/include/asm-m68k/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __M68K_NAMEI_H -#define __M68K_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif diff --git a/include/asm-m68knommu/namei.h b/include/asm-m68knommu/namei.h deleted file mode 100644 index 31a85d27b931..000000000000 --- a/include/asm-m68knommu/namei.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h deleted file mode 100644 index a6605a752469..000000000000 --- a/include/asm-mips/namei.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_NAMEI_H -#define _ASM_NAMEI_H - -/* - * This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_NAMEI_H */ diff --git a/include/asm-mn10300/namei.h b/include/asm-mn10300/namei.h deleted file mode 100644 index bd9ce94aeb65..000000000000 --- a/include/asm-mn10300/namei.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Emulation stuff - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#ifndef _ASM_NAMEI_H -#define _ASM_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_NAMEI_H */ diff --git a/include/asm-parisc/namei.h b/include/asm-parisc/namei.h deleted file mode 100644 index 8d29b3d9fb33..000000000000 --- a/include/asm-parisc/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $ - * linux/include/asm-parisc/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __PARISC_NAMEI_H -#define __PARISC_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __PARISC_NAMEI_H */ diff --git a/include/asm-powerpc/namei.h b/include/asm-powerpc/namei.h deleted file mode 100644 index 657443474a6a..000000000000 --- a/include/asm-powerpc/namei.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_POWERPC_NAMEI_H -#define _ASM_POWERPC_NAMEI_H - -#ifdef __KERNEL__ - -/* - * Adapted from include/asm-alpha/namei.h - * - * Included from fs/namei.c - */ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __KERNEL__ */ -#endif /* _ASM_POWERPC_NAMEI_H */ diff --git a/include/asm-s390/namei.h b/include/asm-s390/namei.h deleted file mode 100644 index 3e286bdde4b0..000000000000 --- a/include/asm-s390/namei.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * include/asm-s390/namei.h - * - * S390 version - * - * Derived from "include/asm-i386/namei.h" - * - * Included from linux/fs/namei.c - */ - -#ifndef __S390_NAMEI_H -#define __S390_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __S390_NAMEI_H */ diff --git a/include/asm-sh/namei.h b/include/asm-sh/namei.h deleted file mode 100644 index 338a5d947143..000000000000 --- a/include/asm-sh/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.3 2000/07/04 06:24:49 gniibe Exp $ - * linux/include/asm-sh/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __ASM_SH_NAMEI_H -#define __ASM_SH_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __ASM_SH_NAMEI_H */ diff --git a/include/asm-sparc/namei.h b/include/asm-sparc/namei.h deleted file mode 100644 index eff944b8e321..000000000000 --- a/include/asm-sparc/namei.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_NAMEI_H -#define ___ASM_SPARC_NAMEI_H -#if defined(__sparc__) && defined(__arch64__) -#include -#else -#include -#endif -#endif diff --git a/include/asm-sparc64/namei.h b/include/asm-sparc64/namei.h deleted file mode 100644 index 1344a910ba2f..000000000000 --- a/include/asm-sparc64/namei.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-um/namei.h b/include/asm-um/namei.h deleted file mode 100644 index 002984d5bc85..000000000000 --- a/include/asm-um/namei.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __UM_NAMEI_H -#define __UM_NAMEI_H - -#include "asm/arch/namei.h" - -#endif diff --git a/include/asm-v850/namei.h b/include/asm-v850/namei.h deleted file mode 100644 index ee8339b23843..000000000000 --- a/include/asm-v850/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/include/asm-v850/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __V850_NAMEI_H__ -#define __V850_NAMEI_H__ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __V850_NAMEI_H__ */ diff --git a/include/asm-x86/namei.h b/include/asm-x86/namei.h deleted file mode 100644 index 415ef5d9550e..000000000000 --- a/include/asm-x86/namei.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_X86_NAMEI_H -#define _ASM_X86_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_X86_NAMEI_H */ diff --git a/include/asm-xtensa/namei.h b/include/asm-xtensa/namei.h deleted file mode 100644 index 3fdff039d27d..000000000000 --- a/include/asm-xtensa/namei.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-xtensa/namei.h - * - * Included from linux/fs/namei.c - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2001 - 2005 Tensilica Inc. - */ - -#ifndef _XTENSA_NAMEI_H -#define _XTENSA_NAMEI_H - -#ifdef __KERNEL__ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __KERNEL__ */ -#endif /* _XTENSA_NAMEI_H */ diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index 282f54219129..9e5a06e78d02 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -7,7 +7,7 @@ struct fs_struct { atomic_t count; rwlock_t lock; int umask; - struct path root, pwd, altroot; + struct path root, pwd; }; #define INIT_FS { \ @@ -19,7 +19,6 @@ struct fs_struct { extern struct kmem_cache *fs_cachep; extern void exit_fs(struct task_struct *); -extern void set_fs_altroot(void); extern void set_fs_root(struct fs_struct *, struct path *); extern void set_fs_pwd(struct fs_struct *, struct path *); extern struct fs_struct *copy_fs_struct(struct fs_struct *); diff --git a/include/linux/namei.h b/include/linux/namei.h index 3cf62d26d493..768773d57857 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -47,7 +47,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_DIRECTORY 2 #define LOOKUP_CONTINUE 4 #define LOOKUP_PARENT 16 -#define LOOKUP_NOALT 32 #define LOOKUP_REVAL 64 /* * Intent data diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index c1ef192aa655..0d407e886735 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -168,7 +168,6 @@ __set_personality(u_long personality) current->personality = personality; oep = current_thread_info()->exec_domain; current_thread_info()->exec_domain = ep; - set_fs_altroot(); module_put(oep->module); return 0; diff --git a/kernel/exit.c b/kernel/exit.c index 6cdf60712bd2..0caf590548a0 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -565,8 +565,6 @@ void put_fs_struct(struct fs_struct *fs) if (atomic_dec_and_test(&fs->count)) { path_put(&fs->root); path_put(&fs->pwd); - if (fs->altroot.dentry) - path_put(&fs->altroot); kmem_cache_free(fs_cachep, fs); } } diff --git a/kernel/fork.c b/kernel/fork.c index abb3ed6298f6..5e050c1317c4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -657,13 +657,6 @@ static struct fs_struct *__copy_fs_struct(struct fs_struct *old) path_get(&old->root); fs->pwd = old->pwd; path_get(&old->pwd); - if (old->altroot.dentry) { - fs->altroot = old->altroot; - path_get(&old->altroot); - } else { - fs->altroot.mnt = NULL; - fs->altroot.dentry = NULL; - } read_unlock(&old->lock); } return fs; -- cgit v1.2.3 From a110343f0d6d41f68b7cf8c00b57a3172c67f816 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Jul 2008 09:19:08 -0400 Subject: [PATCH] fix MAY_CHDIR/MAY_ACCESS/LOOKUP_ACCESS mess * MAY_CHDIR is redundant - it's an equivalent of MAY_ACCESS * MAY_ACCESS on fuse should affect only the last step of pathname resolution * fchdir() and chroot() should pass MAY_ACCESS, for the same reason why chdir() needs that. * now that we pass MAY_ACCESS explicitly in all cases, LOOKUP_ACCESS can be removed; it has no business being in nameidata. Signed-off-by: Al Viro --- fs/fuse/dir.c | 2 +- fs/namei.c | 2 -- fs/open.c | 10 +++++----- include/linux/fs.h | 3 +-- include/linux/namei.h | 1 - 5 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 48a7934cb950..fd03330cadeb 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -962,7 +962,7 @@ static int fuse_permission(struct inode *inode, int mask) exist. So if permissions are revoked this won't be noticed immediately, only after the attribute timeout has expired */ - } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { + } else if (mask & MAY_ACCESS) { err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { diff --git a/fs/namei.c b/fs/namei.c index 095818089ac1..33dcaf025c49 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -265,8 +265,6 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) if (inode->i_op && inode->i_op->permission) { int extra = 0; if (nd) { - if (nd->flags & LOOKUP_ACCESS) - extra |= MAY_ACCESS; if (nd->flags & LOOKUP_OPEN) extra |= MAY_OPEN; } diff --git a/fs/open.c b/fs/open.c index d3a2a00f52dc..3317e1909b2c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -457,11 +457,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) old_cap = cap_set_effective(current->cap_permitted); } - res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); + res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (res) goto out; - res = vfs_permission(&nd, mode); + res = vfs_permission(&nd, mode | MAY_ACCESS); /* SuS v2 requires we report a read only fs too */ if(res || !(mode & S_IWOTH) || special_file(nd.path.dentry->d_inode->i_mode)) @@ -505,7 +505,7 @@ asmlinkage long sys_chdir(const char __user * filename) if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC | MAY_CHDIR); + error = vfs_permission(&nd, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; @@ -534,7 +534,7 @@ asmlinkage long sys_fchdir(unsigned int fd) if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = file_permission(file, MAY_EXEC); + error = file_permission(file, MAY_EXEC | MAY_ACCESS); if (!error) set_fs_pwd(current->fs, &file->f_path); out_putf: @@ -552,7 +552,7 @@ asmlinkage long sys_chroot(const char __user * filename) if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC); + error = vfs_permission(&nd, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 25998e803fc2..d8721e818b45 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -61,8 +61,7 @@ extern int dir_notify_enable; #define MAY_READ 4 #define MAY_APPEND 8 #define MAY_ACCESS 16 -#define MAY_CHDIR 32 -#define MAY_OPEN 64 +#define MAY_OPEN 32 #define FMODE_READ 1 #define FMODE_WRITE 2 diff --git a/include/linux/namei.h b/include/linux/namei.h index 768773d57857..60e35a02f6cb 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -53,7 +53,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; */ #define LOOKUP_OPEN (0x0100) #define LOOKUP_CREATE (0x0200) -#define LOOKUP_ACCESS (0x0400) extern int __user_walk(const char __user *, unsigned, struct nameidata *); extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *); -- cgit v1.2.3 From b77b0646ef4efe31a7449bb3d9360fd00f95433d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Jul 2008 09:37:02 -0400 Subject: [PATCH] pass MAY_OPEN to vfs_permission() explicitly ... and get rid of the last "let's deduce mask from nameidata->flags" bit. Signed-off-by: Al Viro --- fs/exec.c | 4 ++-- fs/namei.c | 13 ++++--------- include/linux/security.h | 7 +++---- security/capability.c | 3 +-- security/security.c | 4 ++-- security/selinux/hooks.c | 5 ++--- security/smack/smack_lsm.c | 3 +-- 7 files changed, 15 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index b8792a131533..0ba5d355c5a1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -118,7 +118,7 @@ asmlinkage long sys_uselib(const char __user * library) if (!S_ISREG(nd.path.dentry->d_inode->i_mode)) goto exit; - error = vfs_permission(&nd, MAY_READ | MAY_EXEC); + error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN); if (error) goto exit; @@ -666,7 +666,7 @@ struct file *open_exec(const char *name) struct inode *inode = nd.path.dentry->d_inode; file = ERR_PTR(-EACCES); if (S_ISREG(inode->i_mode)) { - int err = vfs_permission(&nd, MAY_EXEC); + int err = vfs_permission(&nd, MAY_EXEC | MAY_OPEN); file = ERR_PTR(err); if (!err) { file = nameidata_to_filp(&nd, diff --git a/fs/namei.c b/fs/namei.c index 33dcaf025c49..6b0e8e5e079e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -263,12 +263,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) /* Ordinary permission routines do not understand MAY_APPEND. */ if (inode->i_op && inode->i_op->permission) { - int extra = 0; - if (nd) { - if (nd->flags & LOOKUP_OPEN) - extra |= MAY_OPEN; - } - retval = inode->i_op->permission(inode, mask | extra); + retval = inode->i_op->permission(inode, mask); if (!retval) { /* * Exec permission on a regular file is denied if none @@ -292,7 +287,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) return retval; return security_inode_permission(inode, - mask & (MAY_READ|MAY_WRITE|MAY_EXEC), nd); + mask & (MAY_READ|MAY_WRITE|MAY_EXEC)); } /** @@ -492,7 +487,7 @@ static int exec_permission_lite(struct inode *inode, return -EACCES; ok: - return security_inode_permission(inode, MAY_EXEC, nd); + return security_inode_permission(inode, MAY_EXEC); } /* @@ -1692,7 +1687,7 @@ struct file *do_filp_open(int dfd, const char *pathname, int will_write; int flag = open_to_namei_flags(open_flag); - acc_mode = ACC_MODE(flag); + acc_mode = MAY_OPEN | ACC_MODE(flag); /* O_TRUNC implies we need access checks for write permissions */ if (flag & O_TRUNC) diff --git a/include/linux/security.h b/include/linux/security.h index f0e9adb22ac2..fd96e7f8a6f9 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1362,7 +1362,7 @@ struct security_operations { struct inode *new_dir, struct dentry *new_dentry); int (*inode_readlink) (struct dentry *dentry); int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); - int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd); + int (*inode_permission) (struct inode *inode, int mask); int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); void (*inode_delete) (struct inode *inode); @@ -1628,7 +1628,7 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); int security_inode_readlink(struct dentry *dentry); int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); -int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd); +int security_inode_permission(struct inode *inode, int mask); int security_inode_setattr(struct dentry *dentry, struct iattr *attr); int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); void security_inode_delete(struct inode *inode); @@ -2021,8 +2021,7 @@ static inline int security_inode_follow_link(struct dentry *dentry, return 0; } -static inline int security_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static inline int security_inode_permission(struct inode *inode, int mask) { return 0; } diff --git a/security/capability.c b/security/capability.c index 5b01c0b02422..63d10da515a5 100644 --- a/security/capability.c +++ b/security/capability.c @@ -211,8 +211,7 @@ static int cap_inode_follow_link(struct dentry *dentry, return 0; } -static int cap_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int cap_inode_permission(struct inode *inode, int mask) { return 0; } diff --git a/security/security.c b/security/security.c index 59f23b5918b3..78ed3ffde242 100644 --- a/security/security.c +++ b/security/security.c @@ -429,11 +429,11 @@ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) return security_ops->inode_follow_link(dentry, nd); } -int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd) +int security_inode_permission(struct inode *inode, int mask) { if (unlikely(IS_PRIVATE(inode))) return 0; - return security_ops->inode_permission(inode, mask, nd); + return security_ops->inode_permission(inode, mask); } int security_inode_setattr(struct dentry *dentry, struct iattr *attr) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3481cde5bf15..5ba13908b5b4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2624,12 +2624,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na return dentry_has_perm(current, NULL, dentry, FILE__READ); } -static int selinux_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int selinux_inode_permission(struct inode *inode, int mask) { int rc; - rc = secondary_ops->inode_permission(inode, mask, nd); + rc = secondary_ops->inode_permission(inode, mask); if (rc) return rc; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ee5a51cbc5eb..1b40e558f983 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -522,8 +522,7 @@ static int smack_inode_rename(struct inode *old_inode, * * Returns 0 if access is permitted, -EACCES otherwise */ -static int smack_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int smack_inode_permission(struct inode *inode, int mask) { /* * No permission to check. Existence test. Yup, it's there. -- cgit v1.2.3 From 88b387824fdaecb6ba0f471acf0aadf7d24739fd Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 21 Jul 2008 18:06:36 +0800 Subject: [PATCH] vfs: use kstrdup() and check failing allocation - use kstrdup() instead of kmalloc() + memcpy() - return NULL if allocating ->mnt_devname failed - mnt_devname should be const Signed-off-by: Li Zefan Acked-by: Cyrill Gorcunov Signed-off-by: Al Viro --- fs/namespace.c | 24 +++++++++++++----------- include/linux/mount.h | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/namespace.c b/fs/namespace.c index c4fcf48acef8..26380f599534 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -112,9 +112,13 @@ struct vfsmount *alloc_vfsmnt(const char *name) int err; err = mnt_alloc_id(mnt); - if (err) { - kmem_cache_free(mnt_cache, mnt); - return NULL; + if (err) + goto out_free_cache; + + if (name) { + mnt->mnt_devname = kstrdup(name, GFP_KERNEL); + if (!mnt->mnt_devname) + goto out_free_id; } atomic_set(&mnt->mnt_count, 1); @@ -127,16 +131,14 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); atomic_set(&mnt->__mnt_writers, 0); - if (name) { - int size = strlen(name) + 1; - char *newname = kmalloc(size, GFP_KERNEL); - if (newname) { - memcpy(newname, name, size); - mnt->mnt_devname = newname; - } - } } return mnt; + +out_free_id: + mnt_free_id(mnt); +out_free_cache: + kmem_cache_free(mnt_cache, mnt); + return NULL; } /* diff --git a/include/linux/mount.h b/include/linux/mount.h index 4374d1adeb4b..b5efaa2132ab 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -47,7 +47,7 @@ struct vfsmount { struct list_head mnt_child; /* and going through their mnt_child */ int mnt_flags; /* 4 bytes hole on 64bits arches */ - char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ + const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ -- cgit v1.2.3 From 9767d74957450da6365c363d69e3d02d605d7375 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 1 Jul 2008 15:01:26 +0200 Subject: [patch 1/4] vfs: utimes: move owner check into inode_change_ok() Add a new ia_valid flag: ATTR_TIMES_SET, to handle the UTIMES_OMIT/UTIMES_NOW and UTIMES_NOW/UTIMES_OMIT cases. In these cases neither ATTR_MTIME_SET nor ATTR_ATIME_SET is in the flags, yet the POSIX draft specifies that permission checking is performed the same way as if one or both of the times was explicitly set to a timestamp. See the path "vfs: utimensat(): fix error checking for {UTIME_NOW,UTIME_OMIT} case" by Michael Kerrisk for the patch introducing this behavior. This is a cleanup, as well as allowing filesystems (NFS/fuse/...) to perform their own permission checking instead of the default. CC: Ulrich Drepper CC: Michael Kerrisk Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/attr.c | 2 +- fs/utimes.c | 17 ++++------------- include/linux/fs.h | 33 +++++++++++++++++---------------- 3 files changed, 22 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/fs/attr.c b/fs/attr.c index 966b73e25f82..765fc75fab3b 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -51,7 +51,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr) } /* Check for setting the inode time. */ - if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { + if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { if (!is_owner_or_cap(inode)) goto error; } diff --git a/fs/utimes.c b/fs/utimes.c index b6b664e7145e..ecf8941ba34a 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -101,7 +101,6 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags times[1].tv_nsec == UTIME_NOW) times = NULL; - /* In most cases, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; @@ -123,21 +122,13 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } - /* - * For the UTIME_OMIT/UTIME_NOW and UTIME_NOW/UTIME_OMIT - * cases, we need to make an extra check that is not done by - * inode_change_ok(). + * Tell inode_change_ok(), that this is an explicit time + * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET + * were used. */ - if (((times[0].tv_nsec == UTIME_NOW && - times[1].tv_nsec == UTIME_OMIT) - || - (times[0].tv_nsec == UTIME_OMIT && - times[1].tv_nsec == UTIME_NOW)) - && !is_owner_or_cap(inode)) - goto mnt_drop_write_and_out; + newattrs.ia_valid |= ATTR_TIMES_SET; } else { - /* * If times is NULL (or both times are UTIME_NOW), * then we need to check permissions, because diff --git a/include/linux/fs.h b/include/linux/fs.h index d8721e818b45..527b9e482f99 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -320,22 +320,23 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, * Attribute flags. These should be or-ed together to figure out what * has been changed! */ -#define ATTR_MODE 1 -#define ATTR_UID 2 -#define ATTR_GID 4 -#define ATTR_SIZE 8 -#define ATTR_ATIME 16 -#define ATTR_MTIME 32 -#define ATTR_CTIME 64 -#define ATTR_ATIME_SET 128 -#define ATTR_MTIME_SET 256 -#define ATTR_FORCE 512 /* Not a change, but a change it */ -#define ATTR_ATTR_FLAG 1024 -#define ATTR_KILL_SUID 2048 -#define ATTR_KILL_SGID 4096 -#define ATTR_FILE 8192 -#define ATTR_KILL_PRIV 16384 -#define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */ +#define ATTR_MODE (1 << 0) +#define ATTR_UID (1 << 1) +#define ATTR_GID (1 << 2) +#define ATTR_SIZE (1 << 3) +#define ATTR_ATIME (1 << 4) +#define ATTR_MTIME (1 << 5) +#define ATTR_CTIME (1 << 6) +#define ATTR_ATIME_SET (1 << 7) +#define ATTR_MTIME_SET (1 << 8) +#define ATTR_FORCE (1 << 9) /* Not a change, but a change it */ +#define ATTR_ATTR_FLAG (1 << 10) +#define ATTR_KILL_SUID (1 << 11) +#define ATTR_KILL_SGID (1 << 12) +#define ATTR_FILE (1 << 13) +#define ATTR_KILL_PRIV (1 << 14) +#define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */ +#define ATTR_TIMES_SET (1 << 16) /* * This is the Inode Attributes structure, used for notify_change(). It -- cgit v1.2.3 From f419a2e3b64def707e1384ee38abb77f99af5f6d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Jul 2008 00:07:17 -0400 Subject: [PATCH] kill nameidata passing to permission(), rename to inode_permission() Incidentally, the name that gives hundreds of false positives on grep is not a good idea... Signed-off-by: Al Viro --- fs/ecryptfs/inode.c | 2 +- fs/namei.c | 22 +++++++++------------- fs/nfsd/nfsfh.c | 2 +- fs/nfsd/vfs.c | 4 ++-- fs/utimes.c | 2 +- fs/xattr.c | 2 +- include/linux/fs.h | 2 +- ipc/mqueue.c | 2 +- 8 files changed, 17 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index f25caf2b0887..89209f00f9c7 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -830,7 +830,7 @@ out: static int ecryptfs_permission(struct inode *inode, int mask) { - return permission(ecryptfs_inode_to_lower(inode), mask, NULL); + return inode_permission(ecryptfs_inode_to_lower(inode), mask); } /** diff --git a/fs/namei.c b/fs/namei.c index 396cb3e5c364..5029b93ebbd5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -227,13 +227,9 @@ int generic_permission(struct inode *inode, int mask, return -EACCES; } -int permission(struct inode *inode, int mask, struct nameidata *nd) +int inode_permission(struct inode *inode, int mask) { int retval; - struct vfsmount *mnt = NULL; - - if (nd) - mnt = nd->path.mnt; if (mask & MAY_WRITE) { umode_t mode = inode->i_mode; @@ -293,7 +289,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) */ int vfs_permission(struct nameidata *nd, int mask) { - return permission(nd->path.dentry->d_inode, mask, nd); + return inode_permission(nd->path.dentry->d_inode, mask); } /** @@ -310,7 +306,7 @@ int vfs_permission(struct nameidata *nd, int mask) */ int file_permission(struct file *file, int mask) { - return permission(file->f_path.dentry->d_inode, mask, NULL); + return inode_permission(file->f_path.dentry->d_inode, mask); } /* @@ -1262,7 +1258,7 @@ static struct dentry *lookup_hash(struct nameidata *nd) { int err; - err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd); + err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC); if (err) return ERR_PTR(err); return __lookup_hash(&nd->last, nd->path.dentry, nd); @@ -1310,7 +1306,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) if (err) return ERR_PTR(err); - err = permission(base->d_inode, MAY_EXEC, NULL); + err = inode_permission(base->d_inode, MAY_EXEC); if (err) return ERR_PTR(err); return __lookup_hash(&this, base, NULL); @@ -1400,7 +1396,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) BUG_ON(victim->d_parent->d_inode != dir); audit_inode_child(victim->d_name.name, victim, dir); - error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) @@ -1437,7 +1433,7 @@ static inline int may_create(struct inode *dir, struct dentry *child, return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - return permission(dir,MAY_WRITE | MAY_EXEC, nd); + return inode_permission(dir, MAY_WRITE | MAY_EXEC); } /* @@ -2543,7 +2539,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, * we'll need to flip '..'. */ if (new_dir != old_dir) { - error = permission(old_dentry->d_inode, MAY_WRITE, NULL); + error = inode_permission(old_dentry->d_inode, MAY_WRITE); if (error) return error; } @@ -2897,7 +2893,7 @@ EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(path_lookup); EXPORT_SYMBOL(vfs_path_lookup); -EXPORT_SYMBOL(permission); +EXPORT_SYMBOL(inode_permission); EXPORT_SYMBOL(vfs_permission); EXPORT_SYMBOL(file_permission); EXPORT_SYMBOL(unlock_rename); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index f45451eb1e38..ea37c96f0445 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -51,7 +51,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry) /* make sure parents give x permission to user */ int err; parent = dget_parent(tdentry); - err = permission(parent->d_inode, MAY_EXEC, NULL); + err = inode_permission(parent->d_inode, MAY_EXEC); if (err < 0) { dput(parent); break; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index ad1ad59e3742..18060bed5267 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1953,12 +1953,12 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, return 0; /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */ - err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL); + err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC)); /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) - err = permission(inode, MAY_EXEC, NULL); + err = inode_permission(inode, MAY_EXEC); return err? nfserrno(err) : 0; } diff --git a/fs/utimes.c b/fs/utimes.c index dad679d3a158..dc28b7826259 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -96,7 +96,7 @@ static int utimes_common(struct path *path, struct timespec *times) goto mnt_drop_write_and_out; if (!is_owner_or_cap(inode)) { - error = permission(inode, MAY_WRITE, NULL); + error = inode_permission(inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; } diff --git a/fs/xattr.c b/fs/xattr.c index 4706a8b1f495..b96222e05ba0 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -63,7 +63,7 @@ xattr_permission(struct inode *inode, const char *name, int mask) return -EPERM; } - return permission(inode, mask, NULL); + return inode_permission(inode, mask); } int diff --git a/include/linux/fs.h b/include/linux/fs.h index 527b9e482f99..9d2de4cadabd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1770,7 +1770,7 @@ extern int do_remount_sb(struct super_block *sb, int flags, extern sector_t bmap(struct inode *, sector_t); #endif extern int notify_change(struct dentry *, struct iattr *); -extern int permission(struct inode *, int, struct nameidata *); +extern int inode_permission(struct inode *, int); extern int generic_permission(struct inode *, int, int (*check_acl)(struct inode *, int)); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 474984f9e032..96fb36cd9874 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -638,7 +638,7 @@ static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, return ERR_PTR(-EINVAL); } - if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) { + if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) { dput(dentry); mntput(mqueue_mnt); return ERR_PTR(-EACCES); -- cgit v1.2.3 From 2d8f30380ab8c706f4e0a8f1aaa22b5886e9ac8a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Jul 2008 09:59:21 -0400 Subject: [PATCH] sanitize __user_walk_fd() et.al. * do not pass nameidata; struct path is all the callers want. * switch to new helpers: user_path_at(dfd, pathname, flags, &path) user_path(pathname, &path) user_lpath(pathname, &path) user_path_dir(pathname, &path) (fail if not a directory) The last 3 are trivial macro wrappers for the first one. * remove nameidata in callers. Signed-off-by: Al Viro --- arch/alpha/kernel/osf_sys.c | 10 ++-- arch/parisc/hpux/sys_hpux.c | 10 ++-- fs/coda/pioctl.c | 14 ++--- fs/compat.c | 20 +++---- fs/inotify_user.c | 22 ++++---- fs/namei.c | 36 ++++++------- fs/namespace.c | 74 +++++++++++++------------- fs/open.c | 124 +++++++++++++++++++++---------------------- fs/stat.c | 32 +++++------ fs/utimes.c | 8 +-- fs/xattr.c | 96 ++++++++++++++++----------------- fs/xfs/linux-2.6/xfs_ioctl.c | 14 +++-- include/linux/namei.h | 13 ++--- 13 files changed, 235 insertions(+), 238 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 32ca1b927307..6e943135f0e0 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -253,15 +253,15 @@ do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer, } asmlinkage int -osf_statfs(char __user *path, struct osf_statfs __user *buffer, unsigned long bufsiz) +osf_statfs(char __user *pathname, struct osf_statfs __user *buffer, unsigned long bufsiz) { - struct nameidata nd; + struct path path; int retval; - retval = user_path_walk(path, &nd); + retval = user_path(pathname, &path); if (!retval) { - retval = do_osf_statfs(nd.path.dentry, buffer, bufsiz); - path_put(&nd.path); + retval = do_osf_statfs(path.dentry, buffer, bufsiz); + path_put(&path); } return retval; } diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c index be255ebb609c..18072e03a019 100644 --- a/arch/parisc/hpux/sys_hpux.c +++ b/arch/parisc/hpux/sys_hpux.c @@ -210,19 +210,19 @@ static int vfs_statfs_hpux(struct dentry *dentry, struct hpux_statfs *buf) } /* hpux statfs */ -asmlinkage long hpux_statfs(const char __user *path, +asmlinkage long hpux_statfs(const char __user *pathname, struct hpux_statfs __user *buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct hpux_statfs tmp; - error = vfs_statfs_hpux(nd.path.dentry, &tmp); + error = vfs_statfs_hpux(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_put(&nd.path); + path_put(&path); } return error; } diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c38a98974fb0..c51365422aa8 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -49,7 +49,7 @@ static int coda_ioctl_permission(struct inode *inode, int mask) static int coda_pioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long user_data) { - struct nameidata nd; + struct path path; int error; struct PioctlData data; struct inode *target_inode = NULL; @@ -64,21 +64,21 @@ static int coda_pioctl(struct inode * inode, struct file * filp, * Look up the pathname. Note that the pathname is in * user memory, and namei takes care of this */ - if ( data.follow ) { - error = user_path_walk(data.path, &nd); + if (data.follow) { + error = user_path(data.path, &path); } else { - error = user_path_walk_link(data.path, &nd); + error = user_lpath(data.path, &path); } if ( error ) { return error; } else { - target_inode = nd.path.dentry->d_inode; + target_inode = path.dentry->d_inode; } /* return if it is not a Coda inode */ if ( target_inode->i_sb != inode->i_sb ) { - path_put(&nd.path); + path_put(&path); return -EINVAL; } @@ -87,7 +87,7 @@ static int coda_pioctl(struct inode * inode, struct file * filp, error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); - path_put(&nd.path); + path_put(&path); return error; } diff --git a/fs/compat.c b/fs/compat.c index 106eba28ec5a..c9d1472e65c5 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -234,18 +234,18 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs * * The following statfs calls are copies of code from fs/open.c and * should be checked against those from time to time */ -asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf) +asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct kstatfs tmp; - error = vfs_statfs(nd.path.dentry, &tmp); + error = vfs_statfs(path.dentry, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); - path_put(&nd.path); + path_put(&path); } return error; } @@ -299,21 +299,21 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat return 0; } -asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf) +asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf) { - struct nameidata nd; + struct path path; int error; if (sz != sizeof(*buf)) return -EINVAL; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct kstatfs tmp; - error = vfs_statfs(nd.path.dentry, &tmp); + error = vfs_statfs(path.dentry, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); - path_put(&nd.path); + path_put(&path); } return error; } diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 9b99ebf28884..60249429a253 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -354,20 +354,20 @@ static void inotify_dev_event_dequeue(struct inotify_device *dev) } /* - * find_inode - resolve a user-given path to a specific inode and return a nd + * find_inode - resolve a user-given path to a specific inode */ -static int find_inode(const char __user *dirname, struct nameidata *nd, +static int find_inode(const char __user *dirname, struct path *path, unsigned flags) { int error; - error = __user_walk(dirname, flags, nd); + error = user_path_at(AT_FDCWD, dirname, flags, path); if (error) return error; /* you can only watch an inode if you have read permissions on it */ - error = inode_permission(nd->path.dentry->d_inode, MAY_READ); + error = inode_permission(path->dentry->d_inode, MAY_READ); if (error) - path_put(&nd->path); + path_put(path); return error; } @@ -650,11 +650,11 @@ asmlinkage long sys_inotify_init(void) return sys_inotify_init1(0); } -asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) +asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask) { struct inode *inode; struct inotify_device *dev; - struct nameidata nd; + struct path path; struct file *filp; int ret, fput_needed; unsigned flags = 0; @@ -674,12 +674,12 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) if (mask & IN_ONLYDIR) flags |= LOOKUP_DIRECTORY; - ret = find_inode(path, &nd, flags); + ret = find_inode(pathname, &path, flags); if (unlikely(ret)) goto fput_and_out; - /* inode held in place by reference to nd; dev by fget on fd */ - inode = nd.path.dentry->d_inode; + /* inode held in place by reference to path; dev by fget on fd */ + inode = path.dentry->d_inode; dev = filp->private_data; mutex_lock(&dev->up_mutex); @@ -688,7 +688,7 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) ret = create_watch(dev, inode, mask); mutex_unlock(&dev->up_mutex); - path_put(&nd.path); + path_put(&path); fput_and_out: fput_light(filp, fput_needed); return ret; diff --git a/fs/namei.c b/fs/namei.c index 5029b93ebbd5..edb5e973f9b3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1334,24 +1334,24 @@ struct dentry *lookup_one_noperm(const char *name, struct dentry *base) return __lookup_hash(&this, base, NULL); } -int __user_walk_fd(int dfd, const char __user *name, unsigned flags, - struct nameidata *nd) +int user_path_at(int dfd, const char __user *name, unsigned flags, + struct path *path) { + struct nameidata nd; char *tmp = getname(name); int err = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { - err = do_path_lookup(dfd, tmp, flags, nd); + + BUG_ON(flags & LOOKUP_PARENT); + + err = do_path_lookup(dfd, tmp, flags, &nd); putname(tmp); + if (!err) + *path = nd.path; } return err; } -int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) -{ - return __user_walk_fd(AT_FDCWD, name, flags, nd); -} - /* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. @@ -2446,7 +2446,8 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, int flags) { struct dentry *new_dentry; - struct nameidata nd, old_nd; + struct nameidata nd; + struct path old_path; int error; char * to; @@ -2457,16 +2458,16 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, if (IS_ERR(to)) return PTR_ERR(to); - error = __user_walk_fd(olddfd, oldname, - flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, - &old_nd); + error = user_path_at(olddfd, oldname, + flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, + &old_path); if (error) goto exit; error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); if (error) goto out; error = -EXDEV; - if (old_nd.path.mnt != nd.path.mnt) + if (old_path.mnt != nd.path.mnt) goto out_release; new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); @@ -2475,7 +2476,7 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, error = mnt_want_write(nd.path.mnt); if (error) goto out_dput; - error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry); + error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry); mnt_drop_write(nd.path.mnt); out_dput: dput(new_dentry); @@ -2484,7 +2485,7 @@ out_unlock: out_release: path_put(&nd.path); out: - path_put(&old_nd.path); + path_put(&old_path); exit: putname(to); @@ -2877,8 +2878,7 @@ const struct inode_operations page_symlink_inode_operations = { .put_link = page_put_link, }; -EXPORT_SYMBOL(__user_walk); -EXPORT_SYMBOL(__user_walk_fd); +EXPORT_SYMBOL(user_path_at); EXPORT_SYMBOL(follow_down); EXPORT_SYMBOL(follow_up); EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ diff --git a/fs/namespace.c b/fs/namespace.c index 26380f599534..411728c0c8bb 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1130,27 +1130,27 @@ static int do_umount(struct vfsmount *mnt, int flags) asmlinkage long sys_umount(char __user * name, int flags) { - struct nameidata nd; + struct path path; int retval; - retval = __user_walk(name, LOOKUP_FOLLOW, &nd); + retval = user_path(name, &path); if (retval) goto out; retval = -EINVAL; - if (nd.path.dentry != nd.path.mnt->mnt_root) + if (path.dentry != path.mnt->mnt_root) goto dput_and_out; - if (!check_mnt(nd.path.mnt)) + if (!check_mnt(path.mnt)) goto dput_and_out; retval = -EPERM; if (!capable(CAP_SYS_ADMIN)) goto dput_and_out; - retval = do_umount(nd.path.mnt, flags); + retval = do_umount(path.mnt, flags); dput_and_out: /* we mustn't call path_put() as that would clear mnt_expiry_mark */ - dput(nd.path.dentry); - mntput_no_expire(nd.path.mnt); + dput(path.dentry); + mntput_no_expire(path.mnt); out: return retval; } @@ -2179,28 +2179,26 @@ asmlinkage long sys_pivot_root(const char __user * new_root, const char __user * put_old) { struct vfsmount *tmp; - struct nameidata new_nd, old_nd; - struct path parent_path, root_parent, root; + struct path new, old, parent_path, root_parent, root; int error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, - &new_nd); + error = user_path_dir(new_root, &new); if (error) goto out0; error = -EINVAL; - if (!check_mnt(new_nd.path.mnt)) + if (!check_mnt(new.mnt)) goto out1; - error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd); + error = user_path_dir(put_old, &old); if (error) goto out1; - error = security_sb_pivotroot(&old_nd.path, &new_nd.path); + error = security_sb_pivotroot(&old, &new); if (error) { - path_put(&old_nd.path); + path_put(&old); goto out1; } @@ -2209,69 +2207,69 @@ asmlinkage long sys_pivot_root(const char __user * new_root, path_get(¤t->fs->root); read_unlock(¤t->fs->lock); down_write(&namespace_sem); - mutex_lock(&old_nd.path.dentry->d_inode->i_mutex); + mutex_lock(&old.dentry->d_inode->i_mutex); error = -EINVAL; - if (IS_MNT_SHARED(old_nd.path.mnt) || - IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) || + if (IS_MNT_SHARED(old.mnt) || + IS_MNT_SHARED(new.mnt->mnt_parent) || IS_MNT_SHARED(root.mnt->mnt_parent)) goto out2; if (!check_mnt(root.mnt)) goto out2; error = -ENOENT; - if (IS_DEADDIR(new_nd.path.dentry->d_inode)) + if (IS_DEADDIR(new.dentry->d_inode)) goto out2; - if (d_unhashed(new_nd.path.dentry) && !IS_ROOT(new_nd.path.dentry)) + if (d_unhashed(new.dentry) && !IS_ROOT(new.dentry)) goto out2; - if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry)) + if (d_unhashed(old.dentry) && !IS_ROOT(old.dentry)) goto out2; error = -EBUSY; - if (new_nd.path.mnt == root.mnt || - old_nd.path.mnt == root.mnt) + if (new.mnt == root.mnt || + old.mnt == root.mnt) goto out2; /* loop, on the same file system */ error = -EINVAL; if (root.mnt->mnt_root != root.dentry) goto out2; /* not a mountpoint */ if (root.mnt->mnt_parent == root.mnt) goto out2; /* not attached */ - if (new_nd.path.mnt->mnt_root != new_nd.path.dentry) + if (new.mnt->mnt_root != new.dentry) goto out2; /* not a mountpoint */ - if (new_nd.path.mnt->mnt_parent == new_nd.path.mnt) + if (new.mnt->mnt_parent == new.mnt) goto out2; /* not attached */ /* make sure we can reach put_old from new_root */ - tmp = old_nd.path.mnt; + tmp = old.mnt; spin_lock(&vfsmount_lock); - if (tmp != new_nd.path.mnt) { + if (tmp != new.mnt) { for (;;) { if (tmp->mnt_parent == tmp) goto out3; /* already mounted on put_old */ - if (tmp->mnt_parent == new_nd.path.mnt) + if (tmp->mnt_parent == new.mnt) break; tmp = tmp->mnt_parent; } - if (!is_subdir(tmp->mnt_mountpoint, new_nd.path.dentry)) + if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) goto out3; - } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry)) + } else if (!is_subdir(old.dentry, new.dentry)) goto out3; - detach_mnt(new_nd.path.mnt, &parent_path); + detach_mnt(new.mnt, &parent_path); detach_mnt(root.mnt, &root_parent); /* mount old root on put_old */ - attach_mnt(root.mnt, &old_nd.path); + attach_mnt(root.mnt, &old); /* mount new_root on / */ - attach_mnt(new_nd.path.mnt, &root_parent); + attach_mnt(new.mnt, &root_parent); touch_mnt_namespace(current->nsproxy->mnt_ns); spin_unlock(&vfsmount_lock); - chroot_fs_refs(&root, &new_nd.path); - security_sb_post_pivotroot(&root, &new_nd.path); + chroot_fs_refs(&root, &new); + security_sb_post_pivotroot(&root, &new); error = 0; path_put(&root_parent); path_put(&parent_path); out2: - mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex); + mutex_unlock(&old.dentry->d_inode->i_mutex); up_write(&namespace_sem); path_put(&root); - path_put(&old_nd.path); + path_put(&old); out1: - path_put(&new_nd.path); + path_put(&new); out0: return error; out3: diff --git a/fs/open.c b/fs/open.c index e94266700eda..3fe1a6857c75 100644 --- a/fs/open.c +++ b/fs/open.c @@ -122,37 +122,37 @@ static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf) return 0; } -asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf) +asmlinkage long sys_statfs(const char __user *pathname, struct statfs __user * buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct statfs tmp; - error = vfs_statfs_native(nd.path.dentry, &tmp); + error = vfs_statfs_native(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_put(&nd.path); + path_put(&path); } return error; } -asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf) +asmlinkage long sys_statfs64(const char __user *pathname, size_t sz, struct statfs64 __user *buf) { - struct nameidata nd; + struct path path; long error; if (sz != sizeof(*buf)) return -EINVAL; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct statfs64 tmp; - error = vfs_statfs64(nd.path.dentry, &tmp); + error = vfs_statfs64(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_put(&nd.path); + path_put(&path); } return error; } @@ -223,20 +223,20 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, return err; } -static long do_sys_truncate(const char __user * path, loff_t length) +static long do_sys_truncate(const char __user *pathname, loff_t length) { - struct nameidata nd; - struct inode * inode; + struct path path; + struct inode *inode; int error; error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) goto out; - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ error = -EISDIR; @@ -247,7 +247,7 @@ static long do_sys_truncate(const char __user * path, loff_t length) if (!S_ISREG(inode->i_mode)) goto dput_and_out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto dput_and_out; @@ -274,15 +274,15 @@ static long do_sys_truncate(const char __user * path, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); - error = do_truncate(nd.path.dentry, length, 0, NULL); + error = do_truncate(path.dentry, length, 0, NULL); } put_write_and_out: put_write_access(inode); mnt_drop_write_and_out: - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path.mnt); dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -425,7 +425,7 @@ out: */ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { - struct nameidata nd; + struct path path; struct inode *inode; int old_fsuid, old_fsgid; kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */ @@ -449,7 +449,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) * FIXME: There is a race here against sys_capset. The * capabilities can change yet we will restore the old * value below. We should hold task_capabilities_lock, - * but we cannot because user_path_walk can sleep. + * but we cannot because user_path_at can sleep. */ #endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */ if (current->uid) @@ -458,11 +458,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) old_cap = cap_set_effective(current->cap_permitted); } - res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); + res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); if (res) goto out; - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* @@ -470,7 +470,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) * with the "noexec" flag. */ res = -EACCES; - if (nd.path.mnt->mnt_flags & MNT_NOEXEC) + if (path.mnt->mnt_flags & MNT_NOEXEC) goto out_path_release; } @@ -488,11 +488,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) * inherently racy and know that the fs may change * state before we even see this result. */ - if (__mnt_is_readonly(nd.path.mnt)) + if (__mnt_is_readonly(path.mnt)) res = -EROFS; out_path_release: - path_put(&nd.path); + path_put(&path); out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; @@ -510,21 +510,21 @@ asmlinkage long sys_access(const char __user *filename, int mode) asmlinkage long sys_chdir(const char __user * filename) { - struct nameidata nd; + struct path path; int error; - error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd); + error = user_path_dir(filename, &path); if (error) goto out; - error = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; - set_fs_pwd(current->fs, &nd.path); + set_fs_pwd(current->fs, &path); dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -557,14 +557,14 @@ out: asmlinkage long sys_chroot(const char __user * filename) { - struct nameidata nd; + struct path path; int error; - error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); + error = user_path_dir(filename, &path); if (error) goto out; - error = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; @@ -572,10 +572,10 @@ asmlinkage long sys_chroot(const char __user * filename) if (!capable(CAP_SYS_CHROOT)) goto dput_and_out; - set_fs_root(current->fs, &nd.path); + set_fs_root(current->fs, &path); error = 0; dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -617,17 +617,17 @@ out: asmlinkage long sys_fchmodat(int dfd, const char __user *filename, mode_t mode) { - struct nameidata nd; - struct inode * inode; + struct path path; + struct inode *inode; int error; struct iattr newattrs; - error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); + error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); if (error) goto out; - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto dput_and_out; mutex_lock(&inode->i_mutex); @@ -635,11 +635,11 @@ asmlinkage long sys_fchmodat(int dfd, const char __user *filename, mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(nd.path.dentry, &newattrs); + error = notify_change(path.dentry, &newattrs); mutex_unlock(&inode->i_mutex); - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path.mnt); dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -676,19 +676,19 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group) asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(filename, &nd); + error = user_path(filename, &path); if (error) goto out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, user, group); - mnt_drop_write(nd.path.mnt); + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); out_release: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -696,7 +696,7 @@ out: asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag) { - struct nameidata nd; + struct path path; int error = -EINVAL; int follow; @@ -704,35 +704,35 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, goto out; follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; - error = __user_walk_fd(dfd, filename, follow, &nd); + error = user_path_at(dfd, filename, follow, &path); if (error) goto out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, user, group); - mnt_drop_write(nd.path.mnt); + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); out_release: - path_put(&nd.path); + path_put(&path); out: return error; } asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(filename, &nd); + error = user_lpath(filename, &path); if (error) goto out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, user, group); - mnt_drop_write(nd.path.mnt); + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); out_release: - path_put(&nd.path); + path_put(&path); out: return error; } diff --git a/fs/stat.c b/fs/stat.c index 9cf41f719d50..7c46fbeb8b76 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -57,13 +57,13 @@ EXPORT_SYMBOL(vfs_getattr); int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat) { - struct nameidata nd; + struct path path; int error; - error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd); + error = user_path_at(dfd, name, LOOKUP_FOLLOW, &path); if (!error) { - error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat); - path_put(&nd.path); + error = vfs_getattr(path.mnt, path.dentry, stat); + path_put(&path); } return error; } @@ -77,13 +77,13 @@ EXPORT_SYMBOL(vfs_stat); int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat) { - struct nameidata nd; + struct path path; int error; - error = __user_walk_fd(dfd, name, 0, &nd); + error = user_path_at(dfd, name, 0, &path); if (!error) { - error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat); - path_put(&nd.path); + error = vfs_getattr(path.mnt, path.dentry, stat); + path_put(&path); } return error; } @@ -291,29 +291,29 @@ asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf) return error; } -asmlinkage long sys_readlinkat(int dfd, const char __user *path, +asmlinkage long sys_readlinkat(int dfd, const char __user *pathname, char __user *buf, int bufsiz) { - struct nameidata nd; + struct path path; int error; if (bufsiz <= 0) return -EINVAL; - error = __user_walk_fd(dfd, path, 0, &nd); + error = user_path_at(dfd, pathname, 0, &path); if (!error) { - struct inode *inode = nd.path.dentry->d_inode; + struct inode *inode = path.dentry->d_inode; error = -EINVAL; if (inode->i_op && inode->i_op->readlink) { - error = security_inode_readlink(nd.path.dentry); + error = security_inode_readlink(path.dentry); if (!error) { - touch_atime(nd.path.mnt, nd.path.dentry); - error = inode->i_op->readlink(nd.path.dentry, + touch_atime(path.mnt, path.dentry); + error = inode->i_op->readlink(path.dentry, buf, bufsiz); } } - path_put(&nd.path); + path_put(&path); } return error; } diff --git a/fs/utimes.c b/fs/utimes.c index dc28b7826259..6929e3e91d05 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -152,18 +152,18 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags error = utimes_common(&file->f_path, times); fput(file); } else { - struct nameidata nd; + struct path path; int lookup_flags = 0; if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - error = __user_walk_fd(dfd, filename, lookup_flags, &nd); + error = user_path_at(dfd, filename, lookup_flags, &path); if (error) goto out; - error = utimes_common(&nd.path, times); - path_put(&nd.path); + error = utimes_common(&path, times); + path_put(&path); } out: diff --git a/fs/xattr.c b/fs/xattr.c index b96222e05ba0..468377e66531 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -252,40 +252,40 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, } asmlinkage long -sys_setxattr(const char __user *path, const char __user *name, +sys_setxattr(const char __user *pathname, const char __user *name, const void __user *value, size_t size, int flags) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = setxattr(nd.path.dentry, name, value, size, flags); - mnt_drop_write(nd.path.mnt); + error = setxattr(path.dentry, name, value, size, flags); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } asmlinkage long -sys_lsetxattr(const char __user *path, const char __user *name, +sys_lsetxattr(const char __user *pathname, const char __user *name, const void __user *value, size_t size, int flags) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = setxattr(nd.path.dentry, name, value, size, flags); - mnt_drop_write(nd.path.mnt); + error = setxattr(path.dentry, name, value, size, flags); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } @@ -350,32 +350,32 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, } asmlinkage ssize_t -sys_getxattr(const char __user *path, const char __user *name, +sys_getxattr(const char __user *pathname, const char __user *name, void __user *value, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = getxattr(nd.path.dentry, name, value, size); - path_put(&nd.path); + error = getxattr(path.dentry, name, value, size); + path_put(&path); return error; } asmlinkage ssize_t -sys_lgetxattr(const char __user *path, const char __user *name, void __user *value, +sys_lgetxattr(const char __user *pathname, const char __user *name, void __user *value, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = getxattr(nd.path.dentry, name, value, size); - path_put(&nd.path); + error = getxattr(path.dentry, name, value, size); + path_put(&path); return error; } @@ -425,30 +425,30 @@ listxattr(struct dentry *d, char __user *list, size_t size) } asmlinkage ssize_t -sys_listxattr(const char __user *path, char __user *list, size_t size) +sys_listxattr(const char __user *pathname, char __user *list, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = listxattr(nd.path.dentry, list, size); - path_put(&nd.path); + error = listxattr(path.dentry, list, size); + path_put(&path); return error; } asmlinkage ssize_t -sys_llistxattr(const char __user *path, char __user *list, size_t size) +sys_llistxattr(const char __user *pathname, char __user *list, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = listxattr(nd.path.dentry, list, size); - path_put(&nd.path); + error = listxattr(path.dentry, list, size); + path_put(&path); return error; } @@ -486,38 +486,38 @@ removexattr(struct dentry *d, const char __user *name) } asmlinkage long -sys_removexattr(const char __user *path, const char __user *name) +sys_removexattr(const char __user *pathname, const char __user *name) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = removexattr(nd.path.dentry, name); - mnt_drop_write(nd.path.mnt); + error = removexattr(path.dentry, name); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } asmlinkage long -sys_lremovexattr(const char __user *path, const char __user *name) +sys_lremovexattr(const char __user *pathname, const char __user *name) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = removexattr(nd.path.dentry, name); - mnt_drop_write(nd.path.mnt); + error = removexattr(path.dentry, name); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index a42ba9d71156..01939ba2d8de 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -84,17 +84,15 @@ xfs_find_handle( switch (cmd) { case XFS_IOC_PATH_TO_FSHANDLE: case XFS_IOC_PATH_TO_HANDLE: { - struct nameidata nd; - int error; - - error = user_path_walk_link((const char __user *)hreq.path, &nd); + struct path path; + int error = user_lpath((const char __user *)hreq.path, &path); if (error) return error; - ASSERT(nd.path.dentry); - ASSERT(nd.path.dentry->d_inode); - inode = igrab(nd.path.dentry->d_inode); - path_put(&nd.path); + ASSERT(path.dentry); + ASSERT(path.dentry->d_inode); + inode = igrab(path.dentry->d_inode); + path_put(&path); break; } diff --git a/include/linux/namei.h b/include/linux/namei.h index 60e35a02f6cb..00888ff69504 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -54,12 +54,13 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_OPEN (0x0100) #define LOOKUP_CREATE (0x0200) -extern int __user_walk(const char __user *, unsigned, struct nameidata *); -extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *); -#define user_path_walk(name,nd) \ - __user_walk_fd(AT_FDCWD, name, LOOKUP_FOLLOW, nd) -#define user_path_walk_link(name,nd) \ - __user_walk_fd(AT_FDCWD, name, 0, nd) +extern int user_path_at(int, const char __user *, unsigned, struct path *); + +#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) +#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path) +#define user_path_dir(name, path) \ + user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path) + extern int path_lookup(const char *, unsigned, struct nameidata *); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct nameidata *); -- cgit v1.2.3 From 516e0cc5646f377ab80fcc2ee639892eccb99853 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Jul 2008 00:39:17 -0400 Subject: [PATCH] f_count may wrap around make it atomic_long_t; while we are at it, get rid of useless checks in affs, hfs and hpfs - ->open() always has it equal to 1, ->release() - to 0. Signed-off-by: Al Viro --- drivers/net/ppp_generic.c | 6 +++--- fs/affs/file.c | 4 ---- fs/aio.c | 6 +++--- fs/file_table.c | 10 +++++----- fs/hfs/inode.c | 4 ---- fs/hfsplus/inode.c | 4 ---- include/linux/fs.h | 6 +++--- include/net/af_unix.h | 2 +- net/sched/sch_atm.c | 4 ++-- net/unix/af_unix.c | 2 +- net/unix/garbage.c | 18 +++++++++--------- 11 files changed, 27 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 739b3ab7bccc..ddccc074a76a 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -581,12 +581,12 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (file == ppp->owner) ppp_shutdown_interface(ppp); } - if (atomic_read(&file->f_count) <= 2) { + if (atomic_long_read(&file->f_count) <= 2) { ppp_release(NULL, file); err = 0; } else - printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n", - atomic_read(&file->f_count)); + printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n", + atomic_long_read(&file->f_count)); unlock_kernel(); return err; } diff --git a/fs/affs/file.c b/fs/affs/file.c index 6eac7bdeec94..1377b1240b6e 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -46,8 +46,6 @@ const struct inode_operations affs_file_inode_operations = { static int affs_file_open(struct inode *inode, struct file *filp) { - if (atomic_read(&filp->f_count) != 1) - return 0; pr_debug("AFFS: open(%lu,%d)\n", inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt)); atomic_inc(&AFFS_I(inode)->i_opencnt); @@ -57,8 +55,6 @@ affs_file_open(struct inode *inode, struct file *filp) static int affs_file_release(struct inode *inode, struct file *filp) { - if (atomic_read(&filp->f_count) != 0) - return 0; pr_debug("AFFS: release(%lu, %d)\n", inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt)); diff --git a/fs/aio.c b/fs/aio.c index 0051fd94b44e..f658441d5666 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -512,8 +512,8 @@ static void aio_fput_routine(struct work_struct *data) */ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) { - dprintk(KERN_DEBUG "aio_put(%p): f_count=%d\n", - req, atomic_read(&req->ki_filp->f_count)); + dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n", + req, atomic_long_read(&req->ki_filp->f_count)); assert_spin_locked(&ctx->ctx_lock); @@ -528,7 +528,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) /* Must be done under the lock to serialise against cancellation. * Call this aio_fput as it duplicates fput via the fput_work. */ - if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) { + if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) { get_ioctx(ctx); spin_lock(&fput_lock); list_add(&req->ki_list, &fput_head); diff --git a/fs/file_table.c b/fs/file_table.c index 83084225b4c3..f45a4493f9e7 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -120,7 +120,7 @@ struct file *get_empty_filp(void) tsk = current; INIT_LIST_HEAD(&f->f_u.fu_list); - atomic_set(&f->f_count, 1); + atomic_long_set(&f->f_count, 1); rwlock_init(&f->f_owner.lock); f->f_uid = tsk->fsuid; f->f_gid = tsk->fsgid; @@ -219,7 +219,7 @@ EXPORT_SYMBOL(init_file); void fput(struct file *file) { - if (atomic_dec_and_test(&file->f_count)) + if (atomic_long_dec_and_test(&file->f_count)) __fput(file); } @@ -294,7 +294,7 @@ struct file *fget(unsigned int fd) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (!atomic_inc_not_zero(&file->f_count)) { + if (!atomic_long_inc_not_zero(&file->f_count)) { /* File object ref couldn't be taken */ rcu_read_unlock(); return NULL; @@ -326,7 +326,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (atomic_inc_not_zero(&file->f_count)) + if (atomic_long_inc_not_zero(&file->f_count)) *fput_needed = 1; else /* Didn't get the reference, someone's freed */ @@ -341,7 +341,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed) void put_filp(struct file *file) { - if (atomic_dec_and_test(&file->f_count)) { + if (atomic_long_dec_and_test(&file->f_count)) { security_file_free(file); file_kill(file); file_free(file); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index aa73f3fd5dd9..7e19835efa2e 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -522,8 +522,6 @@ static int hfs_file_open(struct inode *inode, struct file *file) { if (HFS_IS_RSRC(inode)) inode = HFS_I(inode)->rsrc_inode; - if (atomic_read(&file->f_count) != 1) - return 0; atomic_inc(&HFS_I(inode)->opencnt); return 0; } @@ -534,8 +532,6 @@ static int hfs_file_release(struct inode *inode, struct file *file) if (HFS_IS_RSRC(inode)) inode = HFS_I(inode)->rsrc_inode; - if (atomic_read(&file->f_count) != 0) - return 0; if (atomic_dec_and_test(&HFS_I(inode)->opencnt)) { mutex_lock(&inode->i_mutex); hfs_file_truncate(inode); diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index d4014e3044d2..b085d64a2b67 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -254,8 +254,6 @@ static int hfsplus_file_open(struct inode *inode, struct file *file) { if (HFSPLUS_IS_RSRC(inode)) inode = HFSPLUS_I(inode).rsrc_inode; - if (atomic_read(&file->f_count) != 1) - return 0; atomic_inc(&HFSPLUS_I(inode).opencnt); return 0; } @@ -266,8 +264,6 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) if (HFSPLUS_IS_RSRC(inode)) inode = HFSPLUS_I(inode).rsrc_inode; - if (atomic_read(&file->f_count) != 0) - return 0; if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) { mutex_lock(&inode->i_mutex); hfsplus_file_truncate(inode); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d2de4cadabd..7676fa1c20ae 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -795,7 +795,7 @@ struct file { #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt const struct file_operations *f_op; - atomic_t f_count; + atomic_long_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; @@ -824,8 +824,8 @@ extern spinlock_t files_lock; #define file_list_lock() spin_lock(&files_lock); #define file_list_unlock() spin_unlock(&files_lock); -#define get_file(x) atomic_inc(&(x)->f_count) -#define file_count(x) atomic_read(&(x)->f_count) +#define get_file(x) atomic_long_inc(&(x)->f_count) +#define file_count(x) atomic_long_read(&(x)->f_count) #ifdef CONFIG_DEBUG_WRITECOUNT static inline void file_take_write(struct file *f) diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 2dfa96b0575e..7dd29b7e461d 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -51,7 +51,7 @@ struct unix_sock { struct sock *peer; struct sock *other; struct list_head link; - atomic_t inflight; + atomic_long_t inflight; spinlock_t lock; unsigned int gc_candidate : 1; wait_queue_head_t peer_wait; diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 04faa835be17..6b517b9dac5b 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -162,7 +162,7 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl) qdisc_destroy(flow->q); tcf_destroy_chain(&flow->filter_list); if (flow->sock) { - pr_debug("atm_tc_put: f_count %d\n", + pr_debug("atm_tc_put: f_count %ld\n", file_count(flow->sock->file)); flow->vcc->pop = flow->old_pop; sockfd_put(flow->sock); @@ -259,7 +259,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, sock = sockfd_lookup(fd, &error); if (!sock) return error; /* f_count++ */ - pr_debug("atm_tc_change: f_count %d\n", file_count(sock->file)); + pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file)); if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) { error = -EPROTOTYPE; goto err_out; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 70ceb1604ad8..6e7fec74bdb3 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -603,7 +603,7 @@ static struct sock * unix_create1(struct net *net, struct socket *sock) u->dentry = NULL; u->mnt = NULL; spin_lock_init(&u->lock); - atomic_set(&u->inflight, 0); + atomic_long_set(&u->inflight, 0); INIT_LIST_HEAD(&u->link); mutex_init(&u->readlock); /* single task reading lock */ init_waitqueue_head(&u->peer_wait); diff --git a/net/unix/garbage.c b/net/unix/garbage.c index ebdff3d877a1..2a27b84f740b 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -127,7 +127,7 @@ void unix_inflight(struct file *fp) if(s) { struct unix_sock *u = unix_sk(s); spin_lock(&unix_gc_lock); - if (atomic_inc_return(&u->inflight) == 1) { + if (atomic_long_inc_return(&u->inflight) == 1) { BUG_ON(!list_empty(&u->link)); list_add_tail(&u->link, &gc_inflight_list); } else { @@ -145,7 +145,7 @@ void unix_notinflight(struct file *fp) struct unix_sock *u = unix_sk(s); spin_lock(&unix_gc_lock); BUG_ON(list_empty(&u->link)); - if (atomic_dec_and_test(&u->inflight)) + if (atomic_long_dec_and_test(&u->inflight)) list_del_init(&u->link); unix_tot_inflight--; spin_unlock(&unix_gc_lock); @@ -237,17 +237,17 @@ static void scan_children(struct sock *x, void (*func)(struct unix_sock *), static void dec_inflight(struct unix_sock *usk) { - atomic_dec(&usk->inflight); + atomic_long_dec(&usk->inflight); } static void inc_inflight(struct unix_sock *usk) { - atomic_inc(&usk->inflight); + atomic_long_inc(&usk->inflight); } static void inc_inflight_move_tail(struct unix_sock *u) { - atomic_inc(&u->inflight); + atomic_long_inc(&u->inflight); /* * If this is still a candidate, move it to the end of the * list, so that it's checked even if it was already passed @@ -288,11 +288,11 @@ void unix_gc(void) * before the detach without atomicity guarantees. */ list_for_each_entry_safe(u, next, &gc_inflight_list, link) { - int total_refs; - int inflight_refs; + long total_refs; + long inflight_refs; total_refs = file_count(u->sk.sk_socket->file); - inflight_refs = atomic_read(&u->inflight); + inflight_refs = atomic_long_read(&u->inflight); BUG_ON(inflight_refs < 1); BUG_ON(total_refs < inflight_refs); @@ -324,7 +324,7 @@ void unix_gc(void) /* Move cursor to after the current position. */ list_move(&cursor, &u->link); - if (atomic_read(&u->inflight) > 0) { + if (atomic_long_read(&u->inflight) > 0) { list_move_tail(&u->link, &gc_inflight_list); u->gc_candidate = 0; scan_children(&u->sk, inc_inflight_move_tail, NULL); -- cgit v1.2.3 From 964bd183624c03680796b63b4ab97ee3905a806a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Jul 2008 03:33:14 -0400 Subject: [PATCH] get rid of __user_path_lookup_open Signed-off-by: Al Viro --- fs/exec.c | 14 ++++++++++---- fs/namei.c | 13 ------------- include/linux/namei.h | 1 - 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index eca58c29eded..9696bbf0f0b1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -106,11 +106,17 @@ static inline void put_binfmt(struct linux_binfmt * fmt) */ asmlinkage long sys_uselib(const char __user * library) { - struct file * file; + struct file *file; struct nameidata nd; - int error; - - error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC); + char *tmp = getname(library); + int error = PTR_ERR(tmp); + + if (!IS_ERR(tmp)) { + error = path_lookup_open(AT_FDCWD, tmp, + LOOKUP_FOLLOW, &nd, + FMODE_READ|FMODE_EXEC); + putname(tmp); + } if (error) goto out; diff --git a/fs/namei.c b/fs/namei.c index 38ceb6e06eba..a7b0a0b80128 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1193,19 +1193,6 @@ static int path_lookup_create(int dfd, const char *name, nd, open_flags, create_mode); } -int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags, - struct nameidata *nd, int open_flags) -{ - char *tmp = getname(name); - int err = PTR_ERR(tmp); - - if (!IS_ERR(tmp)) { - err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0); - putname(tmp); - } - return err; -} - static struct dentry *__lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd) { diff --git a/include/linux/namei.h b/include/linux/namei.h index 00888ff69504..68f8c3203c89 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -65,7 +65,6 @@ extern int path_lookup(const char *, unsigned, struct nameidata *); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct nameidata *); -extern int __user_path_lookup_open(const char __user *, unsigned lookup_flags, struct nameidata *nd, int open_flags); extern int path_lookup_open(int dfd, const char *name, unsigned lookup_flags, struct nameidata *, int open_flags); extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, int (*open)(struct inode *, struct file *)); -- cgit v1.2.3 From 3f8206d496e9e9495afb1d4e70d29712b4d403c9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Jul 2008 03:46:43 -0400 Subject: [PATCH] get rid of indirect users of namei.h fs.h needs path.h, not namei.h; nfs_fs.h doesn't need it at all. Several places in the tree needed direct include. Signed-off-by: Al Viro --- fs/nfsd/nfsctl.c | 1 + fs/ubifs/file.c | 1 + include/linux/fs.h | 2 +- include/linux/nfs_fs.h | 1 - kernel/cgroup.c | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 1955a2702e60..c53e65f8f3a2 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 005a3b854d96..8565e586e533 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -53,6 +53,7 @@ #include "ubifs.h" #include +#include static int read_block(struct inode *inode, void *addr, unsigned int block, struct ubifs_data_node *dn) diff --git a/include/linux/fs.h b/include/linux/fs.h index 7676fa1c20ae..8252b045e624 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -279,7 +279,7 @@ extern int dir_notify_enable; #include #include #include -#include +#include #include #include #include diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index f08f9ca602af..78a5922a2f11 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 89bd6fb7894f..657f8f8d93a5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -45,6 +45,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 510a35d4a47802f4a0028aa6bd2ca2170da5e32f Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Sat, 26 Jul 2008 15:22:27 -0700 Subject: hugetlb: remove unused variable warning Remove the following warning when CONFIG_HUGETLB_PAGE is not set: ipc/shm.c: In function `shm_get_stat': ipc/shm.c:565: warning: unused variable `h' [akpm@linux-foundation.org: use tabs, not spaces] Signed-off-by: Andrea Righi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 9a71d4cc88c8..32e0ef0f6e1f 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -273,7 +273,10 @@ struct hstate {}; #define huge_page_mask(h) PAGE_MASK #define huge_page_order(h) 0 #define huge_page_shift(h) PAGE_SHIFT -#define pages_per_huge_page(h) 1 +static inline unsigned int pages_per_huge_page(struct hstate *h) +{ + return 1; +} #endif #endif /* _LINUX_HUGETLB_H */ -- cgit v1.2.3 From 9993e51c0c47ec69dce1f26c2321af6bb9165e9e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2008 13:53:46 -0300 Subject: V4L/DVB (8502): videodev2.h: CodingStyle cleanups Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 378 ++++++++++++++++++++-------------------------- 1 file changed, 166 insertions(+), 212 deletions(-) (limited to 'include/linux') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 2e66a95e8d32..cc0c8952323b 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -91,8 +91,8 @@ */ /* Four-character-code (FOURCC) */ -#define v4l2_fourcc(a,b,c,d)\ - (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) +#define v4l2_fourcc(a, b, c, d)\ + ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24)) /* * E N U M S @@ -226,8 +226,7 @@ struct v4l2_fract { /* * D R I V E R C A P A B I L I T I E S */ -struct v4l2_capability -{ +struct v4l2_capability { __u8 driver[16]; /* i.e. "bttv" */ __u8 card[32]; /* i.e. "Hauppauge WinTV" */ __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */ @@ -259,8 +258,7 @@ struct v4l2_capability /* * V I D E O I M A G E F O R M A T */ -struct v4l2_pix_format -{ +struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; @@ -272,68 +270,67 @@ struct v4l2_pix_format }; /* Pixel format FOURCC depth Description */ -#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R','G','B','1') /* 8 RGB-3-3-2 */ -#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R','4','4','4') /* 16 xxxxrrrr ggggbbbb */ -#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R','G','B','O') /* 16 RGB-5-5-5 */ -#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R','G','B','P') /* 16 RGB-5-6-5 */ -#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R','G','B','Q') /* 16 RGB-5-5-5 BE */ -#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R','G','B','R') /* 16 RGB-5-6-5 BE */ -#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B','G','R','3') /* 24 BGR-8-8-8 */ -#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R','G','B','3') /* 24 RGB-8-8-8 */ -#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B','G','R','4') /* 32 BGR-8-8-8-8 */ -#define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R','G','B','4') /* 32 RGB-8-8-8-8 */ -#define V4L2_PIX_FMT_GREY v4l2_fourcc('G','R','E','Y') /* 8 Greyscale */ -#define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y','1','6',' ') /* 16 Greyscale */ -#define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P','A','L','8') /* 8 8-bit palette */ -#define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y','V','U','9') /* 9 YVU 4:1:0 */ -#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y','V','1','2') /* 12 YVU 4:2:0 */ -#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y','U','Y','V') /* 16 YUV 4:2:2 */ -#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U','Y','V','Y') /* 16 YUV 4:2:2 */ -#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4','2','2','P') /* 16 YVU422 planar */ -#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4','1','1','P') /* 16 YVU411 planar */ -#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y','4','1','P') /* 12 YUV 4:1:1 */ -#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y','4','4','4') /* 16 xxxxyyyy uuuuvvvv */ -#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y','U','V','O') /* 16 YUV-5-5-5 */ -#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y','U','V','P') /* 16 YUV-5-6-5 */ -#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y','U','V','4') /* 32 YUV-8-8-8-8 */ +#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1') /* 8 RGB-3-3-2 */ +#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4') /* 16 xxxxrrrr ggggbbbb */ +#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O') /* 16 RGB-5-5-5 */ +#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */ +#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ +#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ +#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ +#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ +#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ +#define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R', 'G', 'B', '4') /* 32 RGB-8-8-8-8 */ +#define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ +#define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */ +#define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ +#define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y', 'V', 'U', '9') /* 9 YVU 4:1:0 */ +#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */ +#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16 YVU422 planar */ +#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16 YVU411 planar */ +#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y', '4', '1', 'P') /* 12 YUV 4:1:1 */ +#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y', '4', '4', '4') /* 16 xxxxyyyy uuuuvvvv */ +#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */ +#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */ +#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */ /* two planes -- one Y, one Cr + Cb interleaved */ -#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N','V','1','2') /* 12 Y/CbCr 4:2:0 */ -#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N','V','2','1') /* 12 Y/CrCb 4:2:0 */ +#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */ /* The following formats are not defined in the V4L2 specification */ -#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y','U','V','9') /* 9 YUV 4:1:0 */ -#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y','U','1','2') /* 12 YUV 4:2:0 */ -#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ -#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ -#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H','M','1','2') /* 8 YUV 4:2:0 16x16 macroblocks */ +#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y', 'U', 'V', '9') /* 9 YUV 4:1:0 */ +#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */ +#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */ +#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ -#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ -#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') /* 8 GBGB.. RGRG.. */ -#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B','Y','R','2') /* 16 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */ /* compressed formats */ -#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ -#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J','P','E','G') /* JFIF JPEG */ -#define V4L2_PIX_FMT_DV v4l2_fourcc('d','v','s','d') /* 1394 */ -#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M','P','E','G') /* MPEG-1/2/4 */ +#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */ +#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG */ +#define V4L2_PIX_FMT_DV v4l2_fourcc('d', 'v', 's', 'd') /* 1394 */ +#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 */ /* Vendor-specific formats */ -#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W','N','V','A') /* Winnov hw compress */ -#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ -#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */ -#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ -#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ -#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ -#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed GBRG bayer */ -#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */ +#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */ +#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */ +#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ +#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ +#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ /* * F O R M A T E N U M E R A T I O N */ -struct v4l2_fmtdesc -{ +struct v4l2_fmtdesc { __u32 index; /* Format number */ enum v4l2_buf_type type; /* buffer type */ __u32 flags; @@ -349,21 +346,18 @@ struct v4l2_fmtdesc /* * F R A M E S I Z E E N U M E R A T I O N */ -enum v4l2_frmsizetypes -{ +enum v4l2_frmsizetypes { V4L2_FRMSIZE_TYPE_DISCRETE = 1, V4L2_FRMSIZE_TYPE_CONTINUOUS = 2, V4L2_FRMSIZE_TYPE_STEPWISE = 3, }; -struct v4l2_frmsize_discrete -{ +struct v4l2_frmsize_discrete { __u32 width; /* Frame width [pixel] */ __u32 height; /* Frame height [pixel] */ }; -struct v4l2_frmsize_stepwise -{ +struct v4l2_frmsize_stepwise { __u32 min_width; /* Minimum frame width [pixel] */ __u32 max_width; /* Maximum frame width [pixel] */ __u32 step_width; /* Frame width step size [pixel] */ @@ -372,8 +366,7 @@ struct v4l2_frmsize_stepwise __u32 step_height; /* Frame height step size [pixel] */ }; -struct v4l2_frmsizeenum -{ +struct v4l2_frmsizeenum { __u32 index; /* Frame size number */ __u32 pixel_format; /* Pixel format */ __u32 type; /* Frame size type the device supports. */ @@ -389,22 +382,19 @@ struct v4l2_frmsizeenum /* * F R A M E R A T E E N U M E R A T I O N */ -enum v4l2_frmivaltypes -{ +enum v4l2_frmivaltypes { V4L2_FRMIVAL_TYPE_DISCRETE = 1, V4L2_FRMIVAL_TYPE_CONTINUOUS = 2, V4L2_FRMIVAL_TYPE_STEPWISE = 3, }; -struct v4l2_frmival_stepwise -{ +struct v4l2_frmival_stepwise { struct v4l2_fract min; /* Minimum frame interval [s] */ struct v4l2_fract max; /* Maximum frame interval [s] */ struct v4l2_fract step; /* Frame interval step size [s] */ }; -struct v4l2_frmivalenum -{ +struct v4l2_frmivalenum { __u32 index; /* Frame format index */ __u32 pixel_format; /* Pixel format */ __u32 width; /* Frame width */ @@ -423,8 +413,7 @@ struct v4l2_frmivalenum /* * T I M E C O D E */ -struct v4l2_timecode -{ +struct v4l2_timecode { __u32 type; __u32 flags; __u8 frames; @@ -449,8 +438,7 @@ struct v4l2_timecode #define V4L2_TC_USERBITS_8BITCHARS 0x0008 /* The above is based on SMPTE timecodes */ -struct v4l2_jpegcompression -{ +struct v4l2_jpegcompression { int quality; int APPn; /* Number of APP segment to be written, @@ -482,16 +470,14 @@ struct v4l2_jpegcompression /* * M E M O R Y - M A P P I N G B U F F E R S */ -struct v4l2_requestbuffers -{ +struct v4l2_requestbuffers { __u32 count; enum v4l2_buf_type type; enum v4l2_memory memory; __u32 reserved[2]; }; -struct v4l2_buffer -{ +struct v4l2_buffer { __u32 index; enum v4l2_buf_type type; __u32 bytesused; @@ -525,13 +511,12 @@ struct v4l2_buffer /* * O V E R L A Y P R E V I E W */ -struct v4l2_framebuffer -{ +struct v4l2_framebuffer { __u32 capability; __u32 flags; /* FIXME: in theory we should pass something like PCI device + memory * region + offset instead of some physical address */ - void* base; + void *base; struct v4l2_pix_format fmt; }; /* Flags for the 'capability' field. Read only */ @@ -550,14 +535,12 @@ struct v4l2_framebuffer #define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010 #define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA 0x0020 -struct v4l2_clip -{ +struct v4l2_clip { struct v4l2_rect c; struct v4l2_clip __user *next; }; -struct v4l2_window -{ +struct v4l2_window { struct v4l2_rect w; enum v4l2_field field; __u32 chromakey; @@ -570,8 +553,7 @@ struct v4l2_window /* * C A P T U R E P A R A M E T E R S */ -struct v4l2_captureparm -{ +struct v4l2_captureparm { __u32 capability; /* Supported modes */ __u32 capturemode; /* Current mode */ struct v4l2_fract timeperframe; /* Time per frame in .1us units */ @@ -584,8 +566,7 @@ struct v4l2_captureparm #define V4L2_MODE_HIGHQUALITY 0x0001 /* High quality imaging mode */ #define V4L2_CAP_TIMEPERFRAME 0x1000 /* timeperframe field is supported */ -struct v4l2_outputparm -{ +struct v4l2_outputparm { __u32 capability; /* Supported modes */ __u32 outputmode; /* Current mode */ struct v4l2_fract timeperframe; /* Time per frame in seconds */ @@ -702,8 +683,7 @@ typedef __u64 v4l2_std_id; #define V4L2_STD_ALL (V4L2_STD_525_60 |\ V4L2_STD_625_50) -struct v4l2_standard -{ +struct v4l2_standard { __u32 index; v4l2_std_id id; __u8 name[24]; @@ -715,8 +695,7 @@ struct v4l2_standard /* * V I D E O I N P U T S */ -struct v4l2_input -{ +struct v4l2_input { __u32 index; /* Which input */ __u8 name[32]; /* Label */ __u32 type; /* Type of input */ @@ -753,8 +732,7 @@ struct v4l2_input /* * V I D E O O U T P U T S */ -struct v4l2_output -{ +struct v4l2_output { __u32 index; /* Which output */ __u8 name[32]; /* Label */ __u32 type; /* Type of output */ @@ -771,14 +749,12 @@ struct v4l2_output /* * C O N T R O L S */ -struct v4l2_control -{ +struct v4l2_control { __u32 id; __s32 value; }; -struct v4l2_ext_control -{ +struct v4l2_ext_control { __u32 id; __u32 reserved2[2]; union { @@ -788,8 +764,7 @@ struct v4l2_ext_control }; } __attribute__ ((packed)); -struct v4l2_ext_controls -{ +struct v4l2_ext_controls { __u32 ctrl_class; __u32 count; __u32 error_idx; @@ -807,8 +782,7 @@ struct v4l2_ext_controls #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ -struct v4l2_queryctrl -{ +struct v4l2_queryctrl { __u32 id; enum v4l2_ctrl_type type; __u8 name[32]; /* Whatever */ @@ -821,8 +795,7 @@ struct v4l2_queryctrl }; /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ -struct v4l2_querymenu -{ +struct v4l2_querymenu { __u32 id; __u32 index; __u8 name[32]; /* Whatever */ @@ -1104,8 +1077,7 @@ enum v4l2_exposure_auto_type { /* * T U N I N G */ -struct v4l2_tuner -{ +struct v4l2_tuner { __u32 index; __u8 name[32]; enum v4l2_tuner_type type; @@ -1119,8 +1091,7 @@ struct v4l2_tuner __u32 reserved[4]; }; -struct v4l2_modulator -{ +struct v4l2_modulator { __u32 index; __u8 name[32]; __u32 capability; @@ -1153,8 +1124,7 @@ struct v4l2_modulator #define V4L2_TUNER_MODE_LANG1 0x0003 #define V4L2_TUNER_MODE_LANG1_LANG2 0x0004 -struct v4l2_frequency -{ +struct v4l2_frequency { __u32 tuner; enum v4l2_tuner_type type; __u32 frequency; @@ -1172,8 +1142,7 @@ struct v4l2_hw_freq_seek { /* * A U D I O */ -struct v4l2_audio -{ +struct v4l2_audio { __u32 index; __u8 name[32]; __u32 capability; @@ -1188,8 +1157,7 @@ struct v4l2_audio /* Flags for the 'mode' field */ #define V4L2_AUDMODE_AVL 0x00001 -struct v4l2_audioout -{ +struct v4l2_audioout { __u32 index; __u8 name[32]; __u32 capability; @@ -1253,8 +1221,7 @@ struct v4l2_encoder_cmd { */ /* Raw VBI */ -struct v4l2_vbi_format -{ +struct v4l2_vbi_format { __u32 sampling_rate; /* in 1 Hz */ __u32 offset; __u32 samples_per_line; @@ -1266,8 +1233,8 @@ struct v4l2_vbi_format }; /* VBI flags */ -#define V4L2_VBI_UNSYNC (1<< 0) -#define V4L2_VBI_INTERLACED (1<< 1) +#define V4L2_VBI_UNSYNC (1 << 0) +#define V4L2_VBI_INTERLACED (1 << 1) /* Sliced VBI * @@ -1276,8 +1243,7 @@ struct v4l2_vbi_format * notice in the definitive implementation. */ -struct v4l2_sliced_vbi_format -{ +struct v4l2_sliced_vbi_format { __u16 service_set; /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field @@ -1301,8 +1267,7 @@ struct v4l2_sliced_vbi_format #define V4L2_SLICED_VBI_525 (V4L2_SLICED_CAPTION_525) #define V4L2_SLICED_VBI_625 (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625) -struct v4l2_sliced_vbi_cap -{ +struct v4l2_sliced_vbi_cap { __u16 service_set; /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field @@ -1313,8 +1278,7 @@ struct v4l2_sliced_vbi_cap __u32 reserved[3]; /* must be 0 */ }; -struct v4l2_sliced_vbi_data -{ +struct v4l2_sliced_vbi_data { __u32 id; __u32 field; /* 0: first field, 1: second field */ __u32 line; /* 1-23 */ @@ -1328,27 +1292,23 @@ struct v4l2_sliced_vbi_data /* Stream data format */ -struct v4l2_format -{ +struct v4l2_format { enum v4l2_buf_type type; - union - { - struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE - struct v4l2_window win; // V4L2_BUF_TYPE_VIDEO_OVERLAY - struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE - struct v4l2_sliced_vbi_format sliced; // V4L2_BUF_TYPE_SLICED_VBI_CAPTURE - __u8 raw_data[200]; // user-defined + union { + struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ + struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ + struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ + struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ + __u8 raw_data[200]; /* user-defined */ } fmt; }; /* Stream type-dependent parameters */ -struct v4l2_streamparm -{ +struct v4l2_streamparm { enum v4l2_buf_type type; - union - { + union { struct v4l2_captureparm capture; struct v4l2_outputparm output; __u8 raw_data[200]; /* user-defined */ @@ -1386,92 +1346,86 @@ struct v4l2_chip_ident { * I O C T L C O D E S F O R V I D E O D E V I C E S * */ -#define VIDIOC_QUERYCAP _IOR ('V', 0, struct v4l2_capability) -#define VIDIOC_RESERVED _IO ('V', 1) -#define VIDIOC_ENUM_FMT _IOWR ('V', 2, struct v4l2_fmtdesc) -#define VIDIOC_G_FMT _IOWR ('V', 4, struct v4l2_format) -#define VIDIOC_S_FMT _IOWR ('V', 5, struct v4l2_format) -#define VIDIOC_REQBUFS _IOWR ('V', 8, struct v4l2_requestbuffers) -#define VIDIOC_QUERYBUF _IOWR ('V', 9, struct v4l2_buffer) -#define VIDIOC_G_FBUF _IOR ('V', 10, struct v4l2_framebuffer) -#define VIDIOC_S_FBUF _IOW ('V', 11, struct v4l2_framebuffer) -#define VIDIOC_OVERLAY _IOW ('V', 14, int) -#define VIDIOC_QBUF _IOWR ('V', 15, struct v4l2_buffer) -#define VIDIOC_DQBUF _IOWR ('V', 17, struct v4l2_buffer) -#define VIDIOC_STREAMON _IOW ('V', 18, int) -#define VIDIOC_STREAMOFF _IOW ('V', 19, int) -#define VIDIOC_G_PARM _IOWR ('V', 21, struct v4l2_streamparm) -#define VIDIOC_S_PARM _IOWR ('V', 22, struct v4l2_streamparm) -#define VIDIOC_G_STD _IOR ('V', 23, v4l2_std_id) -#define VIDIOC_S_STD _IOW ('V', 24, v4l2_std_id) -#define VIDIOC_ENUMSTD _IOWR ('V', 25, struct v4l2_standard) -#define VIDIOC_ENUMINPUT _IOWR ('V', 26, struct v4l2_input) -#define VIDIOC_G_CTRL _IOWR ('V', 27, struct v4l2_control) -#define VIDIOC_S_CTRL _IOWR ('V', 28, struct v4l2_control) -#define VIDIOC_G_TUNER _IOWR ('V', 29, struct v4l2_tuner) -#define VIDIOC_S_TUNER _IOW ('V', 30, struct v4l2_tuner) -#define VIDIOC_G_AUDIO _IOR ('V', 33, struct v4l2_audio) -#define VIDIOC_S_AUDIO _IOW ('V', 34, struct v4l2_audio) -#define VIDIOC_QUERYCTRL _IOWR ('V', 36, struct v4l2_queryctrl) -#define VIDIOC_QUERYMENU _IOWR ('V', 37, struct v4l2_querymenu) -#define VIDIOC_G_INPUT _IOR ('V', 38, int) -#define VIDIOC_S_INPUT _IOWR ('V', 39, int) -#define VIDIOC_G_OUTPUT _IOR ('V', 46, int) -#define VIDIOC_S_OUTPUT _IOWR ('V', 47, int) -#define VIDIOC_ENUMOUTPUT _IOWR ('V', 48, struct v4l2_output) -#define VIDIOC_G_AUDOUT _IOR ('V', 49, struct v4l2_audioout) -#define VIDIOC_S_AUDOUT _IOW ('V', 50, struct v4l2_audioout) -#define VIDIOC_G_MODULATOR _IOWR ('V', 54, struct v4l2_modulator) -#define VIDIOC_S_MODULATOR _IOW ('V', 55, struct v4l2_modulator) -#define VIDIOC_G_FREQUENCY _IOWR ('V', 56, struct v4l2_frequency) -#define VIDIOC_S_FREQUENCY _IOW ('V', 57, struct v4l2_frequency) -#define VIDIOC_CROPCAP _IOWR ('V', 58, struct v4l2_cropcap) -#define VIDIOC_G_CROP _IOWR ('V', 59, struct v4l2_crop) -#define VIDIOC_S_CROP _IOW ('V', 60, struct v4l2_crop) -#define VIDIOC_G_JPEGCOMP _IOR ('V', 61, struct v4l2_jpegcompression) -#define VIDIOC_S_JPEGCOMP _IOW ('V', 62, struct v4l2_jpegcompression) -#define VIDIOC_QUERYSTD _IOR ('V', 63, v4l2_std_id) -#define VIDIOC_TRY_FMT _IOWR ('V', 64, struct v4l2_format) -#define VIDIOC_ENUMAUDIO _IOWR ('V', 65, struct v4l2_audio) -#define VIDIOC_ENUMAUDOUT _IOWR ('V', 66, struct v4l2_audioout) -#define VIDIOC_G_PRIORITY _IOR ('V', 67, enum v4l2_priority) -#define VIDIOC_S_PRIORITY _IOW ('V', 68, enum v4l2_priority) -#define VIDIOC_G_SLICED_VBI_CAP _IOWR ('V', 69, struct v4l2_sliced_vbi_cap) -#define VIDIOC_LOG_STATUS _IO ('V', 70) -#define VIDIOC_G_EXT_CTRLS _IOWR ('V', 71, struct v4l2_ext_controls) -#define VIDIOC_S_EXT_CTRLS _IOWR ('V', 72, struct v4l2_ext_controls) -#define VIDIOC_TRY_EXT_CTRLS _IOWR ('V', 73, struct v4l2_ext_controls) +#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) +#define VIDIOC_RESERVED _IO('V', 1) +#define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc) +#define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format) +#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) +#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers) +#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer) +#define VIDIOC_G_FBUF _IOR('V', 10, struct v4l2_framebuffer) +#define VIDIOC_S_FBUF _IOW('V', 11, struct v4l2_framebuffer) +#define VIDIOC_OVERLAY _IOW('V', 14, int) +#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) +#define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer) +#define VIDIOC_STREAMON _IOW('V', 18, int) +#define VIDIOC_STREAMOFF _IOW('V', 19, int) +#define VIDIOC_G_PARM _IOWR('V', 21, struct v4l2_streamparm) +#define VIDIOC_S_PARM _IOWR('V', 22, struct v4l2_streamparm) +#define VIDIOC_G_STD _IOR('V', 23, v4l2_std_id) +#define VIDIOC_S_STD _IOW('V', 24, v4l2_std_id) +#define VIDIOC_ENUMSTD _IOWR('V', 25, struct v4l2_standard) +#define VIDIOC_ENUMINPUT _IOWR('V', 26, struct v4l2_input) +#define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) +#define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) +#define VIDIOC_G_TUNER _IOWR('V', 29, struct v4l2_tuner) +#define VIDIOC_S_TUNER _IOW('V', 30, struct v4l2_tuner) +#define VIDIOC_G_AUDIO _IOR('V', 33, struct v4l2_audio) +#define VIDIOC_S_AUDIO _IOW('V', 34, struct v4l2_audio) +#define VIDIOC_QUERYCTRL _IOWR('V', 36, struct v4l2_queryctrl) +#define VIDIOC_QUERYMENU _IOWR('V', 37, struct v4l2_querymenu) +#define VIDIOC_G_INPUT _IOR('V', 38, int) +#define VIDIOC_S_INPUT _IOWR('V', 39, int) +#define VIDIOC_G_OUTPUT _IOR('V', 46, int) +#define VIDIOC_S_OUTPUT _IOWR('V', 47, int) +#define VIDIOC_ENUMOUTPUT _IOWR('V', 48, struct v4l2_output) +#define VIDIOC_G_AUDOUT _IOR('V', 49, struct v4l2_audioout) +#define VIDIOC_S_AUDOUT _IOW('V', 50, struct v4l2_audioout) +#define VIDIOC_G_MODULATOR _IOWR('V', 54, struct v4l2_modulator) +#define VIDIOC_S_MODULATOR _IOW('V', 55, struct v4l2_modulator) +#define VIDIOC_G_FREQUENCY _IOWR('V', 56, struct v4l2_frequency) +#define VIDIOC_S_FREQUENCY _IOW('V', 57, struct v4l2_frequency) +#define VIDIOC_CROPCAP _IOWR('V', 58, struct v4l2_cropcap) +#define VIDIOC_G_CROP _IOWR('V', 59, struct v4l2_crop) +#define VIDIOC_S_CROP _IOW('V', 60, struct v4l2_crop) +#define VIDIOC_G_JPEGCOMP _IOR('V', 61, struct v4l2_jpegcompression) +#define VIDIOC_S_JPEGCOMP _IOW('V', 62, struct v4l2_jpegcompression) +#define VIDIOC_QUERYSTD _IOR('V', 63, v4l2_std_id) +#define VIDIOC_TRY_FMT _IOWR('V', 64, struct v4l2_format) +#define VIDIOC_ENUMAUDIO _IOWR('V', 65, struct v4l2_audio) +#define VIDIOC_ENUMAUDOUT _IOWR('V', 66, struct v4l2_audioout) +#define VIDIOC_G_PRIORITY _IOR('V', 67, enum v4l2_priority) +#define VIDIOC_S_PRIORITY _IOW('V', 68, enum v4l2_priority) +#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap) +#define VIDIOC_LOG_STATUS _IO('V', 70) +#define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls) +#define VIDIOC_S_EXT_CTRLS _IOWR('V', 72, struct v4l2_ext_controls) +#define VIDIOC_TRY_EXT_CTRLS _IOWR('V', 73, struct v4l2_ext_controls) #if 1 -#define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) -#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) -#define VIDIOC_G_ENC_INDEX _IOR ('V', 76, struct v4l2_enc_idx) -#define VIDIOC_ENCODER_CMD _IOWR ('V', 77, struct v4l2_encoder_cmd) -#define VIDIOC_TRY_ENCODER_CMD _IOWR ('V', 78, struct v4l2_encoder_cmd) +#define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) +#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum) +#define VIDIOC_G_ENC_INDEX _IOR('V', 76, struct v4l2_enc_idx) +#define VIDIOC_ENCODER_CMD _IOWR('V', 77, struct v4l2_encoder_cmd) +#define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd) /* Experimental, only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ -#define VIDIOC_DBG_S_REGISTER _IOW ('V', 79, struct v4l2_register) -#define VIDIOC_DBG_G_REGISTER _IOWR ('V', 80, struct v4l2_register) +#define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_register) +#define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_register) -#define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) +#define VIDIOC_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_chip_ident) #endif -#define VIDIOC_S_HW_FREQ_SEEK _IOW ('V', 82, struct v4l2_hw_freq_seek) +#define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ -#define VIDIOC_OVERLAY_OLD _IOWR ('V', 14, int) -#define VIDIOC_S_PARM_OLD _IOW ('V', 22, struct v4l2_streamparm) -#define VIDIOC_S_CTRL_OLD _IOW ('V', 28, struct v4l2_control) -#define VIDIOC_G_AUDIO_OLD _IOWR ('V', 33, struct v4l2_audio) -#define VIDIOC_G_AUDOUT_OLD _IOWR ('V', 49, struct v4l2_audioout) -#define VIDIOC_CROPCAP_OLD _IOR ('V', 58, struct v4l2_cropcap) +#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) +#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) +#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) +#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) +#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) +#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) #endif #define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ #endif /* __LINUX_VIDEODEV2_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v1.2.3 From 1250ac6d4ab716dafe0ac245fd31cd3a7cbc0a98 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 26 Jul 2008 08:02:47 -0300 Subject: V4L/DVB (8518): gspca: Remove the remaining frame decoding functions from the subdrivers. SPCA505 and SPCA508 added in the pixel formats. Decode functions and associated resources removed in spca505, 506 and 508. The decode routines are now found in the V4L library. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/spca505.c | 105 ++++++++++-------------------------- drivers/media/video/gspca/spca506.c | 105 ++++++++++-------------------------- drivers/media/video/gspca/spca508.c | 91 +++++++------------------------ include/linux/videodev2.h | 2 + 4 files changed, 76 insertions(+), 227 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index 284d549e4d3e..32ffe5556061 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -31,10 +31,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */ - unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ - unsigned char brightness; char subtype; @@ -64,29 +60,29 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 5}, - {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 4}, - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640 * 3, + .sizeimage = 640 * 480 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -760,77 +756,30 @@ static void sd_close(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); } -/* convert YYUV per line to YUYV (YUV 4:2:2) */ -static void yyuv_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - yi1 = yi + width; - Ui = yi1 + width; - Vi = Ui + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yyuv_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 2c281a0563e5..6fe715c80ad2 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -33,10 +33,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - __u8 tmpbuf[640 * 480 * 3]; /* YYUV per line */ - __u8 tmpbuf2[640 * 480 * 2]; /* YUYV */ - unsigned char brightness; unsigned char contrast; unsigned char colors; @@ -115,29 +111,29 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 5}, - {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 4}, - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640 * 3, + .sizeimage = 640 * 480 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -572,77 +568,30 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* convert YYUV per line to YUYV (YUV 4:2:2) */ -static void yyuv_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - yi1 = yi + width; - Ui = yi1 + width; - Vi = Ui + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yyuv_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index af531d62856c..4378e966edcc 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -30,10 +30,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */ - unsigned char tmpbuf2[352 * 288 * 2]; /* YUYV */ - unsigned char brightness; char subtype; @@ -68,23 +64,23 @@ static struct ctrl sd_ctrls[] = { static struct v4l2_pix_format sif_mode[] = { {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 3}, {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -1567,77 +1563,30 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* convert YUVY per line to YUYV (YUV 4:2:2) */ -static void yuvy_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - Ui = yi + width; - Vi = Ui + width / 2; - yi1 = Vi + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yuvy_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA508_OFFSET_DATA; len -= SPCA508_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index cc0c8952323b..7d9ac046389e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -324,6 +324,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ +#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S','5','0','5') /* YYUV per line */ +#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S','5','0','8') /* YUVY per line */ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ -- cgit v1.2.3 From c1d7f4f1648cb8efd87f1b9560c40af2297e7c05 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 26 Jul 2008 08:33:47 -0300 Subject: V4L/DVB (8524): videodev: copy the VID_TYPE defines to videodev.h The VID_TYPE defines are V4L1 specific, so copy them back to videodev.h. In videodev2.h ensure that they are not used in the kernel (you need to include videodev.h instead) and mark them are deprecated. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev.h | 15 +++++++++++++++ include/linux/videodev2.h | 6 ++++++ 2 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/videodev.h b/include/linux/videodev.h index 9385a566aed8..15a653d41132 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -17,6 +17,21 @@ #if defined(CONFIG_VIDEO_V4L1_COMPAT) || !defined (__KERNEL__) +#define VID_TYPE_CAPTURE 1 /* Can capture */ +#define VID_TYPE_TUNER 2 /* Can tune */ +#define VID_TYPE_TELETEXT 4 /* Does teletext */ +#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ +#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ +#define VID_TYPE_CLIPPING 32 /* Can clip */ +#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ +#define VID_TYPE_SCALES 128 /* Scalable */ +#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ +#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ +#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ +#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ +#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ +#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ + struct video_capability { char name[32]; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 7d9ac046389e..f7195351a1e7 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -71,6 +71,11 @@ */ #define VIDEO_MAX_FRAME 32 +#ifndef __KERNEL__ + +/* These defines are V4L1 specific and should not be used with the V4L2 API! + They will be removed from this header in the future. */ + #define VID_TYPE_CAPTURE 1 /* Can capture */ #define VID_TYPE_TUNER 2 /* Can tune */ #define VID_TYPE_TELETEXT 4 /* Does teletext */ @@ -85,6 +90,7 @@ #define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ #define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ #define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ +#endif /* * M I S C E L L A N E O U S -- cgit v1.2.3 From 9fa0f6db3a201bef49f28e69f80802559a38586b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2008 08:55:17 -0300 Subject: V4L/DVB (8522): videodev2: Fix merge conflict Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index f7195351a1e7..e466bd54a50e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -330,8 +330,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ -#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S','5','0','5') /* YYUV per line */ -#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S','5','0','8') /* YUVY per line */ +#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */ +#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ -- cgit v1.2.3 From 5995477ab7f3522c497c9c4a1c55373e9d655574 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Sun, 27 Jul 2008 17:29:15 +0200 Subject: task IO accounting: improve code readability Put all i/o statistics in struct proc_io_accounting and use inline functions to initialize and increment statistics, removing a lot of single variable assignments. This also reduces the kernel size as following (with CONFIG_TASK_XACCT=y and CONFIG_TASK_IO_ACCOUNTING=y). text data bss dec hex filename 11651 0 0 11651 2d83 kernel/exit.o.before 11619 0 0 11619 2d63 kernel/exit.o.after 10886 132 136 11154 2b92 kernel/fork.o.before 10758 132 136 11026 2b12 kernel/fork.o.after 3082029 807968 4818600 8708597 84e1f5 vmlinux.o.before 3081869 807968 4818600 8708437 84e155 vmlinux.o.after Signed-off-by: Andrea Righi Acked-by: Oleg Nesterov Signed-off-by: Linus Torvalds --- fs/proc/base.c | 57 ++++++++++------------------------ include/linux/sched.h | 19 ++++-------- include/linux/task_io_accounting.h | 27 ++++++++++++++-- include/linux/task_io_accounting_ops.h | 56 +++++++++++++++++++++++++++------ kernel/exit.c | 30 ++---------------- kernel/fork.c | 15 ++------- kernel/tsacct.c | 14 ++++----- 7 files changed, 104 insertions(+), 114 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index e74308bdabd3..3d94906c7aa8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -2402,44 +2403,17 @@ static int proc_base_fill_cache(struct file *filp, void *dirent, #ifdef CONFIG_TASK_IO_ACCOUNTING static int do_io_accounting(struct task_struct *task, char *buffer, int whole) { - u64 rchar, wchar, syscr, syscw; - struct task_io_accounting ioac; - - rchar = task->rchar; - wchar = task->wchar; - syscr = task->syscr; - syscw = task->syscw; - memcpy(&ioac, &task->ioac, sizeof(ioac)); - - if (whole) { - unsigned long flags; - - if (lock_task_sighand(task, &flags)) { - struct signal_struct *sig = task->signal; - struct task_struct *t = task; - - rchar += sig->rchar; - wchar += sig->wchar; - syscr += sig->syscr; - syscw += sig->syscw; - - ioac.read_bytes += sig->ioac.read_bytes; - ioac.write_bytes += sig->ioac.write_bytes; - ioac.cancelled_write_bytes += - sig->ioac.cancelled_write_bytes; - while_each_thread(task, t) { - rchar += t->rchar; - wchar += t->wchar; - syscr += t->syscr; - syscw += t->syscw; - - ioac.read_bytes += t->ioac.read_bytes; - ioac.write_bytes += t->ioac.write_bytes; - ioac.cancelled_write_bytes += - t->ioac.cancelled_write_bytes; - } - unlock_task_sighand(task, &flags); - } + struct proc_io_accounting acct = task->ioac; + unsigned long flags; + + if (whole && lock_task_sighand(task, &flags)) { + struct task_struct *t = task; + + task_io_accounting_add(&acct, &task->signal->ioac); + while_each_thread(task, t) + task_io_accounting_add(&acct, &t->ioac); + + unlock_task_sighand(task, &flags); } return sprintf(buffer, "rchar: %llu\n" @@ -2449,9 +2423,10 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) "read_bytes: %llu\n" "write_bytes: %llu\n" "cancelled_write_bytes: %llu\n", - rchar, wchar, syscr, syscw, - ioac.read_bytes, ioac.write_bytes, - ioac.cancelled_write_bytes); + acct.chr.rchar, acct.chr.wchar, + acct.chr.syscr, acct.chr.syscw, + acct.blk.read_bytes, acct.blk.write_bytes, + acct.blk.cancelled_write_bytes); } static int proc_tid_io_accounting(struct task_struct *task, char *buffer) diff --git a/include/linux/sched.h b/include/linux/sched.h index f59318a0099b..034c1ca6b332 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -505,10 +505,7 @@ struct signal_struct { unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; unsigned long inblock, oublock, cinblock, coublock; -#ifdef CONFIG_TASK_XACCT - u64 rchar, wchar, syscr, syscw; -#endif - struct task_io_accounting ioac; + struct proc_io_accounting ioac; /* * Cumulative ns of scheduled CPU time for dead threads in the @@ -1256,11 +1253,7 @@ struct task_struct { unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ -#ifdef CONFIG_TASK_XACCT -/* i/o counters(bytes read/written, #syscalls */ - u64 rchar, wchar, syscr, syscw; -#endif - struct task_io_accounting ioac; + struct proc_io_accounting ioac; #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ @@ -2190,22 +2183,22 @@ extern long sched_group_rt_period(struct task_group *tg); #ifdef CONFIG_TASK_XACCT static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { - tsk->rchar += amt; + tsk->ioac.chr.rchar += amt; } static inline void add_wchar(struct task_struct *tsk, ssize_t amt) { - tsk->wchar += amt; + tsk->ioac.chr.wchar += amt; } static inline void inc_syscr(struct task_struct *tsk) { - tsk->syscr++; + tsk->ioac.chr.syscr++; } static inline void inc_syscw(struct task_struct *tsk) { - tsk->syscw++; + tsk->ioac.chr.syscw++; } #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 44d00e9cceea..165390f8b936 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -1,5 +1,5 @@ /* - * task_io_accounting: a structure which is used for recording a single task's + * proc_io_accounting: a structure which is used for recording a single task's * IO statistics. * * Don't include this header file directly - it is designed to be dragged in via @@ -8,6 +8,22 @@ * Blame akpm@osdl.org for all this. */ +#ifdef CONFIG_TASK_XACCT +struct task_chr_io_accounting { + /* bytes read */ + u64 rchar; + /* bytes written */ + u64 wchar; + /* # of read syscalls */ + u64 syscr; + /* # of write syscalls */ + u64 syscw; +}; +#else /* CONFIG_TASK_XACCT */ +struct task_chr_io_accounting { +}; +#endif /* CONFIG_TASK_XACCT */ + #ifdef CONFIG_TASK_IO_ACCOUNTING struct task_io_accounting { /* @@ -31,7 +47,12 @@ struct task_io_accounting { */ u64 cancelled_write_bytes; }; -#else +#else /* CONFIG_TASK_IO_ACCOUNTING */ struct task_io_accounting { }; -#endif +#endif /* CONFIG_TASK_IO_ACCOUNTING */ + +struct proc_io_accounting { + struct task_chr_io_accounting chr; + struct task_io_accounting blk; +}; diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index ff46c6fad79d..e6f958ebe97f 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -9,7 +9,7 @@ #ifdef CONFIG_TASK_IO_ACCOUNTING static inline void task_io_account_read(size_t bytes) { - current->ioac.read_bytes += bytes; + current->ioac.blk.read_bytes += bytes; } /* @@ -18,12 +18,12 @@ static inline void task_io_account_read(size_t bytes) */ static inline unsigned long task_io_get_inblock(const struct task_struct *p) { - return p->ioac.read_bytes >> 9; + return p->ioac.blk.read_bytes >> 9; } static inline void task_io_account_write(size_t bytes) { - current->ioac.write_bytes += bytes; + current->ioac.blk.write_bytes += bytes; } /* @@ -32,17 +32,25 @@ static inline void task_io_account_write(size_t bytes) */ static inline unsigned long task_io_get_oublock(const struct task_struct *p) { - return p->ioac.write_bytes >> 9; + return p->ioac.blk.write_bytes >> 9; } static inline void task_io_account_cancelled_write(size_t bytes) { - current->ioac.cancelled_write_bytes += bytes; + current->ioac.blk.cancelled_write_bytes += bytes; } -static inline void task_io_accounting_init(struct task_struct *tsk) +static inline void task_io_accounting_init(struct proc_io_accounting *ioac) { - memset(&tsk->ioac, 0, sizeof(tsk->ioac)); + memset(ioac, 0, sizeof(*ioac)); +} + +static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ + dst->blk.read_bytes += src->blk.read_bytes; + dst->blk.write_bytes += src->blk.write_bytes; + dst->blk.cancelled_write_bytes += src->blk.cancelled_write_bytes; } #else @@ -69,9 +77,37 @@ static inline void task_io_account_cancelled_write(size_t bytes) { } -static inline void task_io_accounting_init(struct task_struct *tsk) +static inline void task_io_accounting_init(struct proc_io_accounting *ioac) +{ +} + +static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) { } -#endif /* CONFIG_TASK_IO_ACCOUNTING */ -#endif /* __TASK_IO_ACCOUNTING_OPS_INCLUDED */ +#endif /* CONFIG_TASK_IO_ACCOUNTING */ + +#ifdef CONFIG_TASK_XACCT +static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ + dst->chr.rchar += src->chr.rchar; + dst->chr.wchar += src->chr.wchar; + dst->chr.syscr += src->chr.syscr; + dst->chr.syscw += src->chr.syscw; +} +#else +static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ +} +#endif /* CONFIG_TASK_XACCT */ + +static inline void task_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ + task_chr_io_accounting_add(dst, src); + task_blk_io_accounting_add(dst, src); +} +#endif /* __TASK_IO_ACCOUNTING_OPS_INCLUDED */ diff --git a/kernel/exit.c b/kernel/exit.c index 0caf590548a0..eb4d6470d1d0 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -121,18 +121,7 @@ static void __exit_signal(struct task_struct *tsk) sig->nivcsw += tsk->nivcsw; sig->inblock += task_io_get_inblock(tsk); sig->oublock += task_io_get_oublock(tsk); -#ifdef CONFIG_TASK_XACCT - sig->rchar += tsk->rchar; - sig->wchar += tsk->wchar; - sig->syscr += tsk->syscr; - sig->syscw += tsk->syscw; -#endif /* CONFIG_TASK_XACCT */ -#ifdef CONFIG_TASK_IO_ACCOUNTING - sig->ioac.read_bytes += tsk->ioac.read_bytes; - sig->ioac.write_bytes += tsk->ioac.write_bytes; - sig->ioac.cancelled_write_bytes += - tsk->ioac.cancelled_write_bytes; -#endif /* CONFIG_TASK_IO_ACCOUNTING */ + task_io_accounting_add(&sig->ioac, &tsk->ioac); sig->sum_sched_runtime += tsk->se.sum_exec_runtime; sig = NULL; /* Marker for below. */ } @@ -1363,21 +1352,8 @@ static int wait_task_zombie(struct task_struct *p, int options, psig->coublock += task_io_get_oublock(p) + sig->oublock + sig->coublock; -#ifdef CONFIG_TASK_XACCT - psig->rchar += p->rchar + sig->rchar; - psig->wchar += p->wchar + sig->wchar; - psig->syscr += p->syscr + sig->syscr; - psig->syscw += p->syscw + sig->syscw; -#endif /* CONFIG_TASK_XACCT */ -#ifdef CONFIG_TASK_IO_ACCOUNTING - psig->ioac.read_bytes += - p->ioac.read_bytes + sig->ioac.read_bytes; - psig->ioac.write_bytes += - p->ioac.write_bytes + sig->ioac.write_bytes; - psig->ioac.cancelled_write_bytes += - p->ioac.cancelled_write_bytes + - sig->ioac.cancelled_write_bytes; -#endif /* CONFIG_TASK_IO_ACCOUNTING */ + task_io_accounting_add(&psig->ioac, &p->ioac); + task_io_accounting_add(&psig->ioac, &sig->ioac); spin_unlock_irq(&p->parent->sighand->siglock); } diff --git a/kernel/fork.c b/kernel/fork.c index 5e050c1317c4..8214ba7c8bb1 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -806,12 +806,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0; sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0; sig->inblock = sig->oublock = sig->cinblock = sig->coublock = 0; -#ifdef CONFIG_TASK_XACCT - sig->rchar = sig->wchar = sig->syscr = sig->syscw = 0; -#endif -#ifdef CONFIG_TASK_IO_ACCOUNTING - memset(&sig->ioac, 0, sizeof(sig->ioac)); -#endif + task_io_accounting_init(&sig->ioac); sig->sum_sched_runtime = 0; INIT_LIST_HEAD(&sig->cpu_timers[0]); INIT_LIST_HEAD(&sig->cpu_timers[1]); @@ -994,13 +989,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->last_switch_timestamp = 0; #endif -#ifdef CONFIG_TASK_XACCT - p->rchar = 0; /* I/O counter: bytes read */ - p->wchar = 0; /* I/O counter: bytes written */ - p->syscr = 0; /* I/O counter: read syscalls */ - p->syscw = 0; /* I/O counter: write syscalls */ -#endif - task_io_accounting_init(p); + task_io_accounting_init(&p->ioac); acct_clear_integrals(p); p->it_virt_expires = cputime_zero; diff --git a/kernel/tsacct.c b/kernel/tsacct.c index 3da47ccdc5e5..f9cd2561689c 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -94,14 +94,14 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) stats->hiwater_vm = mm->hiwater_vm * PAGE_SIZE / KB; mmput(mm); } - stats->read_char = p->rchar; - stats->write_char = p->wchar; - stats->read_syscalls = p->syscr; - stats->write_syscalls = p->syscw; + stats->read_char = p->ioac.chr.rchar; + stats->write_char = p->ioac.chr.wchar; + stats->read_syscalls = p->ioac.chr.syscr; + stats->write_syscalls = p->ioac.chr.syscw; #ifdef CONFIG_TASK_IO_ACCOUNTING - stats->read_bytes = p->ioac.read_bytes; - stats->write_bytes = p->ioac.write_bytes; - stats->cancelled_write_bytes = p->ioac.cancelled_write_bytes; + stats->read_bytes = p->ioac.blk.read_bytes; + stats->write_bytes = p->ioac.blk.write_bytes; + stats->cancelled_write_bytes = p->ioac.blk.cancelled_write_bytes; #else stats->read_bytes = 0; stats->write_bytes = 0; -- cgit v1.2.3 From 940389b8afad6495211614c13eb91ef7001773ec Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Mon, 28 Jul 2008 00:48:12 +0200 Subject: task IO accounting: move all IO statistics in struct task_io_accounting Simplify the code of include/linux/task_io_accounting.h. It is also more reasonable to have all the task i/o-related statistics in a single struct (task_io_accounting). Signed-off-by: Andrea Righi Signed-off-by: Oleg Nesterov Signed-off-by: Linus Torvalds --- fs/proc/base.c | 10 +++---- include/linux/sched.h | 12 ++++----- include/linux/task_io_accounting.h | 17 ++---------- include/linux/task_io_accounting_ops.h | 48 +++++++++++++++++----------------- kernel/tsacct.c | 14 +++++----- 5 files changed, 44 insertions(+), 57 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index 3d94906c7aa8..01ed610f9b87 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2403,7 +2403,7 @@ static int proc_base_fill_cache(struct file *filp, void *dirent, #ifdef CONFIG_TASK_IO_ACCOUNTING static int do_io_accounting(struct task_struct *task, char *buffer, int whole) { - struct proc_io_accounting acct = task->ioac; + struct task_io_accounting acct = task->ioac; unsigned long flags; if (whole && lock_task_sighand(task, &flags)) { @@ -2423,10 +2423,10 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) "read_bytes: %llu\n" "write_bytes: %llu\n" "cancelled_write_bytes: %llu\n", - acct.chr.rchar, acct.chr.wchar, - acct.chr.syscr, acct.chr.syscw, - acct.blk.read_bytes, acct.blk.write_bytes, - acct.blk.cancelled_write_bytes); + acct.rchar, acct.wchar, + acct.syscr, acct.syscw, + acct.read_bytes, acct.write_bytes, + acct.cancelled_write_bytes); } static int proc_tid_io_accounting(struct task_struct *task, char *buffer) diff --git a/include/linux/sched.h b/include/linux/sched.h index 034c1ca6b332..5270d449ff9d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -505,7 +505,7 @@ struct signal_struct { unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; unsigned long inblock, oublock, cinblock, coublock; - struct proc_io_accounting ioac; + struct task_io_accounting ioac; /* * Cumulative ns of scheduled CPU time for dead threads in the @@ -1253,7 +1253,7 @@ struct task_struct { unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ - struct proc_io_accounting ioac; + struct task_io_accounting ioac; #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ @@ -2183,22 +2183,22 @@ extern long sched_group_rt_period(struct task_group *tg); #ifdef CONFIG_TASK_XACCT static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { - tsk->ioac.chr.rchar += amt; + tsk->ioac.rchar += amt; } static inline void add_wchar(struct task_struct *tsk, ssize_t amt) { - tsk->ioac.chr.wchar += amt; + tsk->ioac.wchar += amt; } static inline void inc_syscr(struct task_struct *tsk) { - tsk->ioac.chr.syscr++; + tsk->ioac.syscr++; } static inline void inc_syscw(struct task_struct *tsk) { - tsk->ioac.chr.syscw++; + tsk->ioac.syscw++; } #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 165390f8b936..5e88afc9a2fb 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -1,5 +1,5 @@ /* - * proc_io_accounting: a structure which is used for recording a single task's + * task_io_accounting: a structure which is used for recording a single task's * IO statistics. * * Don't include this header file directly - it is designed to be dragged in via @@ -8,8 +8,8 @@ * Blame akpm@osdl.org for all this. */ +struct task_io_accounting { #ifdef CONFIG_TASK_XACCT -struct task_chr_io_accounting { /* bytes read */ u64 rchar; /* bytes written */ @@ -18,14 +18,9 @@ struct task_chr_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; -}; -#else /* CONFIG_TASK_XACCT */ -struct task_chr_io_accounting { -}; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING -struct task_io_accounting { /* * The number of bytes which this task has caused to be read from * storage. @@ -46,13 +41,5 @@ struct task_io_accounting { * information loss in doing that. */ u64 cancelled_write_bytes; -}; -#else /* CONFIG_TASK_IO_ACCOUNTING */ -struct task_io_accounting { -}; #endif /* CONFIG_TASK_IO_ACCOUNTING */ - -struct proc_io_accounting { - struct task_chr_io_accounting chr; - struct task_io_accounting blk; }; diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index e6f958ebe97f..4d090f9ee608 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -9,7 +9,7 @@ #ifdef CONFIG_TASK_IO_ACCOUNTING static inline void task_io_account_read(size_t bytes) { - current->ioac.blk.read_bytes += bytes; + current->ioac.read_bytes += bytes; } /* @@ -18,12 +18,12 @@ static inline void task_io_account_read(size_t bytes) */ static inline unsigned long task_io_get_inblock(const struct task_struct *p) { - return p->ioac.blk.read_bytes >> 9; + return p->ioac.read_bytes >> 9; } static inline void task_io_account_write(size_t bytes) { - current->ioac.blk.write_bytes += bytes; + current->ioac.write_bytes += bytes; } /* @@ -32,25 +32,25 @@ static inline void task_io_account_write(size_t bytes) */ static inline unsigned long task_io_get_oublock(const struct task_struct *p) { - return p->ioac.blk.write_bytes >> 9; + return p->ioac.write_bytes >> 9; } static inline void task_io_account_cancelled_write(size_t bytes) { - current->ioac.blk.cancelled_write_bytes += bytes; + current->ioac.cancelled_write_bytes += bytes; } -static inline void task_io_accounting_init(struct proc_io_accounting *ioac) +static inline void task_io_accounting_init(struct task_io_accounting *ioac) { memset(ioac, 0, sizeof(*ioac)); } -static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_blk_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { - dst->blk.read_bytes += src->blk.read_bytes; - dst->blk.write_bytes += src->blk.write_bytes; - dst->blk.cancelled_write_bytes += src->blk.cancelled_write_bytes; + dst->read_bytes += src->read_bytes; + dst->write_bytes += src->write_bytes; + dst->cancelled_write_bytes += src->cancelled_write_bytes; } #else @@ -77,35 +77,35 @@ static inline void task_io_account_cancelled_write(size_t bytes) { } -static inline void task_io_accounting_init(struct proc_io_accounting *ioac) +static inline void task_io_accounting_init(struct task_io_accounting *ioac) { } -static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_blk_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { } #endif /* CONFIG_TASK_IO_ACCOUNTING */ #ifdef CONFIG_TASK_XACCT -static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { - dst->chr.rchar += src->chr.rchar; - dst->chr.wchar += src->chr.wchar; - dst->chr.syscr += src->chr.syscr; - dst->chr.syscw += src->chr.syscw; + dst->rchar += src->rchar; + dst->wchar += src->wchar; + dst->syscr += src->syscr; + dst->syscw += src->syscw; } #else -static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { } #endif /* CONFIG_TASK_XACCT */ -static inline void task_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { task_chr_io_accounting_add(dst, src); task_blk_io_accounting_add(dst, src); diff --git a/kernel/tsacct.c b/kernel/tsacct.c index f9cd2561689c..8ebcd8532dfb 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -94,14 +94,14 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) stats->hiwater_vm = mm->hiwater_vm * PAGE_SIZE / KB; mmput(mm); } - stats->read_char = p->ioac.chr.rchar; - stats->write_char = p->ioac.chr.wchar; - stats->read_syscalls = p->ioac.chr.syscr; - stats->write_syscalls = p->ioac.chr.syscw; + stats->read_char = p->ioac.rchar; + stats->write_char = p->ioac.wchar; + stats->read_syscalls = p->ioac.syscr; + stats->write_syscalls = p->ioac.syscw; #ifdef CONFIG_TASK_IO_ACCOUNTING - stats->read_bytes = p->ioac.blk.read_bytes; - stats->write_bytes = p->ioac.blk.write_bytes; - stats->cancelled_write_bytes = p->ioac.blk.cancelled_write_bytes; + stats->read_bytes = p->ioac.read_bytes; + stats->write_bytes = p->ioac.write_bytes; + stats->cancelled_write_bytes = p->ioac.cancelled_write_bytes; #else stats->read_bytes = 0; stats->write_bytes = 0; -- cgit v1.2.3 From 5c2aed622571ac7c3c6ec182d6d3c318e4b45c8b Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Thu, 28 Feb 2008 11:33:03 -0500 Subject: stop_machine: add ALL_CPUS option -allow stop_mahcine_run() to call a function on all cpus. Calling stop_machine_run() with a 'ALL_CPUS' invokes this new behavior. stop_machine_run() proceeds as normal until the calling cpu has invoked 'fn'. Then, we tell all the other cpus to call 'fn'. Signed-off-by: Jason Baron Signed-off-by: Mathieu Desnoyers Signed-off-by: Rusty Russell CC: Adrian Bunk CC: Andi Kleen CC: Alexey Dobriyan CC: Christoph Hellwig CC: mingo@elte.hu CC: akpm@osdl.org --- include/linux/stop_machine.h | 8 +++++++- kernel/stop_machine.c | 32 +++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 5bfc553bdb21..18af011c13af 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -8,11 +8,17 @@ #include #if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP) + +#define ALL_CPUS ~0U + /** * stop_machine_run: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn() - * @cpu: the cpu to run @fn() on (or any, if @cpu == NR_CPUS. + * @cpu: if @cpu == n, run @fn() on cpu n + * if @cpu == NR_CPUS, run @fn() on any cpu + * if @cpu == ALL_CPUS, run @fn() first on the calling cpu, and then + * concurrently on all the other cpus * * Description: This causes a thread to be scheduled on every other cpu, * each of which disables interrupts, and finally interrupts are disabled diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 738b411ff2d3..a473bd0cb71b 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -22,9 +22,17 @@ enum stopmachine_state { STOPMACHINE_WAIT, STOPMACHINE_PREPARE, STOPMACHINE_DISABLE_IRQ, + STOPMACHINE_RUN, STOPMACHINE_EXIT, }; +struct stop_machine_data { + int (*fn)(void *); + void *data; + struct completion done; + int run_all; +} smdata; + static enum stopmachine_state stopmachine_state; static unsigned int stopmachine_num_threads; static atomic_t stopmachine_thread_ack; @@ -33,6 +41,7 @@ static int stopmachine(void *cpu) { int irqs_disabled = 0; int prepared = 0; + int ran = 0; cpumask_of_cpu_ptr(cpumask, (int)(long)cpu); set_cpus_allowed_ptr(current, cpumask); @@ -58,6 +67,11 @@ static int stopmachine(void *cpu) prepared = 1; smp_mb(); /* Must read state first. */ atomic_inc(&stopmachine_thread_ack); + } else if (stopmachine_state == STOPMACHINE_RUN && !ran) { + smdata.fn(smdata.data); + ran = 1; + smp_mb(); /* Must read state first. */ + atomic_inc(&stopmachine_thread_ack); } /* Yield in first stage: migration threads need to * help our sisters onto their CPUs. */ @@ -136,11 +150,10 @@ static void restart_machine(void) preempt_enable_no_resched(); } -struct stop_machine_data { - int (*fn)(void *); - void *data; - struct completion done; -}; +static void run_other_cpus(void) +{ + stopmachine_set_state(STOPMACHINE_RUN); +} static int do_stop(void *_smdata) { @@ -150,6 +163,8 @@ static int do_stop(void *_smdata) ret = stop_machine(); if (ret == 0) { ret = smdata->fn(smdata->data); + if (smdata->run_all) + run_other_cpus(); restart_machine(); } @@ -173,14 +188,17 @@ struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, struct stop_machine_data smdata; struct task_struct *p; + mutex_lock(&stopmachine_mutex); + smdata.fn = fn; smdata.data = data; + smdata.run_all = (cpu == ALL_CPUS) ? 1 : 0; init_completion(&smdata.done); - mutex_lock(&stopmachine_mutex); + smp_wmb(); /* make sure other cpus see smdata updates */ /* If they don't care which CPU fn runs on, bind to any online one. */ - if (cpu == NR_CPUS) + if (cpu == NR_CPUS || cpu == ALL_CPUS) cpu = raw_smp_processor_id(); p = kthread_create(do_stop, &smdata, "kstopmachine"); -- cgit v1.2.3 From ffdb5976c47609c862917d4c186ecbb5706d2dda Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Jul 2008 12:16:28 -0500 Subject: Simplify stop_machine stop_machine creates a kthread which creates kernel threads. We can create those threads directly and simplify things a little. Some care must be taken with CPU hotunplug, which has special needs, but that code seems more robust than it was in the past. Signed-off-by: Rusty Russell Acked-by: Christian Borntraeger --- include/linux/stop_machine.h | 20 ++- kernel/cpu.c | 13 +- kernel/stop_machine.c | 293 ++++++++++++++++++------------------------- 3 files changed, 136 insertions(+), 190 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 18af011c13af..36c2c7284eb3 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -17,13 +17,12 @@ * @data: the data ptr for the @fn() * @cpu: if @cpu == n, run @fn() on cpu n * if @cpu == NR_CPUS, run @fn() on any cpu - * if @cpu == ALL_CPUS, run @fn() first on the calling cpu, and then - * concurrently on all the other cpus + * if @cpu == ALL_CPUS, run @fn() on every online CPU. * - * Description: This causes a thread to be scheduled on every other cpu, - * each of which disables interrupts, and finally interrupts are disabled - * on the current CPU. The result is that noone is holding a spinlock - * or inside any other preempt-disabled region when @fn() runs. + * Description: This causes a thread to be scheduled on every cpu, + * each of which disables interrupts. The result is that noone is + * holding a spinlock or inside any other preempt-disabled region when + * @fn() runs. * * This can be thought of as a very heavy write lock, equivalent to * grabbing every spinlock in the kernel. */ @@ -35,13 +34,10 @@ int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); * @data: the data ptr for the @fn * @cpu: the cpu to run @fn on (or any, if @cpu == NR_CPUS. * - * Description: This is a special version of the above, which returns the - * thread which has run @fn(): kthread_stop will return the return value - * of @fn(). Used by hotplug cpu. + * Description: This is a special version of the above, which assumes cpus + * won't come or go while it's being called. Used by hotplug cpu. */ -struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu); - +int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); #else static inline int stop_machine_run(int (*fn)(void *), void *data, diff --git a/kernel/cpu.c b/kernel/cpu.c index 10ba5f1004a5..cf79bb911371 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -216,7 +216,6 @@ static int __ref take_cpu_down(void *_param) static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) { int err, nr_calls = 0; - struct task_struct *p; cpumask_t old_allowed, tmp; void *hcpu = (void *)(long)cpu; unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; @@ -250,19 +249,15 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) cpu_clear(cpu, tmp); set_cpus_allowed_ptr(current, &tmp); - p = __stop_machine_run(take_cpu_down, &tcd_param, cpu); + err = __stop_machine_run(take_cpu_down, &tcd_param, cpu); - if (IS_ERR(p) || cpu_online(cpu)) { + if (err || cpu_online(cpu)) { /* CPU didn't die: tell everyone. Can't complain. */ if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, hcpu) == NOTIFY_BAD) BUG(); - if (IS_ERR(p)) { - err = PTR_ERR(p); - goto out_allowed; - } - goto out_thread; + goto out_allowed; } /* Wait for it to sleep (leaving idle task). */ @@ -279,8 +274,6 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) check_for_tasks(cpu); -out_thread: - err = kthread_stop(p); out_allowed: set_cpus_allowed_ptr(current, &old_allowed); out_release: diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index a473bd0cb71b..35882dccc943 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -1,4 +1,4 @@ -/* Copyright 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation. +/* Copyright 2008, 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation. * GPL v2 and any later version. */ #include @@ -13,220 +13,177 @@ #include #include -/* Since we effect priority and affinity (both of which are visible - * to, and settable by outside processes) we do indirection via a - * kthread. */ - -/* Thread to stop each CPU in user context. */ +/* This controls the threads on each CPU. */ enum stopmachine_state { - STOPMACHINE_WAIT, + /* Dummy starting state for thread. */ + STOPMACHINE_NONE, + /* Awaiting everyone to be scheduled. */ STOPMACHINE_PREPARE, + /* Disable interrupts. */ STOPMACHINE_DISABLE_IRQ, + /* Run the function */ STOPMACHINE_RUN, + /* Exit */ STOPMACHINE_EXIT, }; +static enum stopmachine_state state; struct stop_machine_data { int (*fn)(void *); void *data; - struct completion done; - int run_all; -} smdata; + int fnret; +}; -static enum stopmachine_state stopmachine_state; -static unsigned int stopmachine_num_threads; -static atomic_t stopmachine_thread_ack; +/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ +static unsigned int num_threads; +static atomic_t thread_ack; +static struct completion finished; +static DEFINE_MUTEX(lock); -static int stopmachine(void *cpu) +static void set_state(enum stopmachine_state newstate) { - int irqs_disabled = 0; - int prepared = 0; - int ran = 0; - cpumask_of_cpu_ptr(cpumask, (int)(long)cpu); - - set_cpus_allowed_ptr(current, cpumask); - - /* Ack: we are alive */ - smp_mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ - atomic_inc(&stopmachine_thread_ack); - - /* Simple state machine */ - while (stopmachine_state != STOPMACHINE_EXIT) { - if (stopmachine_state == STOPMACHINE_DISABLE_IRQ - && !irqs_disabled) { - local_irq_disable(); - hard_irq_disable(); - irqs_disabled = 1; - /* Ack: irqs disabled. */ - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - } else if (stopmachine_state == STOPMACHINE_PREPARE - && !prepared) { - /* Everyone is in place, hold CPU. */ - preempt_disable(); - prepared = 1; - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - } else if (stopmachine_state == STOPMACHINE_RUN && !ran) { - smdata.fn(smdata.data); - ran = 1; - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - } - /* Yield in first stage: migration threads need to - * help our sisters onto their CPUs. */ - if (!prepared && !irqs_disabled) - yield(); - cpu_relax(); - } - - /* Ack: we are exiting. */ - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - - if (irqs_disabled) - local_irq_enable(); - if (prepared) - preempt_enable(); - - return 0; + /* Reset ack counter. */ + atomic_set(&thread_ack, num_threads); + smp_wmb(); + state = newstate; } -/* Change the thread state */ -static void stopmachine_set_state(enum stopmachine_state state) +/* Last one to ack a state moves to the next state. */ +static void ack_state(void) { - atomic_set(&stopmachine_thread_ack, 0); - smp_wmb(); - stopmachine_state = state; - while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) - cpu_relax(); + if (atomic_dec_and_test(&thread_ack)) { + /* If we're the last one to ack the EXIT, we're finished. */ + if (state == STOPMACHINE_EXIT) + complete(&finished); + else + set_state(state + 1); + } } -static int stop_machine(void) +/* This is the actual thread which stops the CPU. It exits by itself rather + * than waiting for kthread_stop(), because it's easier for hotplug CPU. */ +static int stop_cpu(struct stop_machine_data *smdata) { - int i, ret = 0; - - atomic_set(&stopmachine_thread_ack, 0); - stopmachine_num_threads = 0; - stopmachine_state = STOPMACHINE_WAIT; + enum stopmachine_state curstate = STOPMACHINE_NONE; + int uninitialized_var(ret); - for_each_online_cpu(i) { - if (i == raw_smp_processor_id()) - continue; - ret = kernel_thread(stopmachine, (void *)(long)i,CLONE_KERNEL); - if (ret < 0) - break; - stopmachine_num_threads++; - } - - /* Wait for them all to come to life. */ - while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) { - yield(); + /* Simple state machine */ + do { + /* Chill out and ensure we re-read stopmachine_state. */ cpu_relax(); - } - - /* If some failed, kill them all. */ - if (ret < 0) { - stopmachine_set_state(STOPMACHINE_EXIT); - return ret; - } - - /* Now they are all started, make them hold the CPUs, ready. */ - preempt_disable(); - stopmachine_set_state(STOPMACHINE_PREPARE); - - /* Make them disable irqs. */ - local_irq_disable(); - hard_irq_disable(); - stopmachine_set_state(STOPMACHINE_DISABLE_IRQ); - - return 0; -} + if (state != curstate) { + curstate = state; + switch (curstate) { + case STOPMACHINE_DISABLE_IRQ: + local_irq_disable(); + hard_irq_disable(); + break; + case STOPMACHINE_RUN: + /* |= allows error detection if functions on + * multiple CPUs. */ + smdata->fnret |= smdata->fn(smdata->data); + break; + default: + break; + } + ack_state(); + } + } while (curstate != STOPMACHINE_EXIT); -static void restart_machine(void) -{ - stopmachine_set_state(STOPMACHINE_EXIT); local_irq_enable(); - preempt_enable_no_resched(); + do_exit(0); } -static void run_other_cpus(void) +/* Callback for CPUs which aren't supposed to do anything. */ +static int chill(void *unused) { - stopmachine_set_state(STOPMACHINE_RUN); + return 0; } -static int do_stop(void *_smdata) +int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) { - struct stop_machine_data *smdata = _smdata; - int ret; + int i, err; + struct stop_machine_data active, idle; + struct task_struct **threads; + + active.fn = fn; + active.data = data; + active.fnret = 0; + idle.fn = chill; + idle.data = NULL; + + /* If they don't care which cpu fn runs on, just pick one. */ + if (cpu == NR_CPUS) + cpu = any_online_cpu(cpu_online_map); + + /* This could be too big for stack on large machines. */ + threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL); + if (!threads) + return -ENOMEM; + + /* Set up initial state. */ + mutex_lock(&lock); + init_completion(&finished); + num_threads = num_online_cpus(); + set_state(STOPMACHINE_PREPARE); - ret = stop_machine(); - if (ret == 0) { - ret = smdata->fn(smdata->data); - if (smdata->run_all) - run_other_cpus(); - restart_machine(); - } + for_each_online_cpu(i) { + struct stop_machine_data *smdata; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - /* We're done: you can kthread_stop us now */ - complete(&smdata->done); + if (cpu == ALL_CPUS || i == cpu) + smdata = &active; + else + smdata = &idle; + + threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u", + i); + if (IS_ERR(threads[i])) { + err = PTR_ERR(threads[i]); + threads[i] = NULL; + goto kill_threads; + } - /* Wait for kthread_stop */ - set_current_state(TASK_INTERRUPTIBLE); - while (!kthread_should_stop()) { - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - __set_current_state(TASK_RUNNING); - return ret; -} + /* Place it onto correct cpu. */ + kthread_bind(threads[i], i); -struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu) -{ - static DEFINE_MUTEX(stopmachine_mutex); - struct stop_machine_data smdata; - struct task_struct *p; + /* Make it highest prio. */ + if (sched_setscheduler_nocheck(threads[i], SCHED_FIFO, ¶m)) + BUG(); + } - mutex_lock(&stopmachine_mutex); + /* We've created all the threads. Wake them all: hold this CPU so one + * doesn't hit this CPU until we're ready. */ + cpu = get_cpu(); + for_each_online_cpu(i) + wake_up_process(threads[i]); - smdata.fn = fn; - smdata.data = data; - smdata.run_all = (cpu == ALL_CPUS) ? 1 : 0; - init_completion(&smdata.done); + /* This will release the thread on our CPU. */ + put_cpu(); + wait_for_completion(&finished); + mutex_unlock(&lock); - smp_wmb(); /* make sure other cpus see smdata updates */ + kfree(threads); - /* If they don't care which CPU fn runs on, bind to any online one. */ - if (cpu == NR_CPUS || cpu == ALL_CPUS) - cpu = raw_smp_processor_id(); + return active.fnret; - p = kthread_create(do_stop, &smdata, "kstopmachine"); - if (!IS_ERR(p)) { - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; +kill_threads: + for_each_online_cpu(i) + if (threads[i]) + kthread_stop(threads[i]); + mutex_unlock(&lock); - /* One high-prio thread per cpu. We'll do this one. */ - sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m); - kthread_bind(p, cpu); - wake_up_process(p); - wait_for_completion(&smdata.done); - } - mutex_unlock(&stopmachine_mutex); - return p; + kfree(threads); + return err; } int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) { - struct task_struct *p; int ret; /* No CPUs can come up or down during this. */ get_online_cpus(); - p = __stop_machine_run(fn, data, cpu); - if (!IS_ERR(p)) - ret = kthread_stop(p); - else - ret = PTR_ERR(p); + ret = __stop_machine_run(fn, data, cpu); put_online_cpus(); return ret; -- cgit v1.2.3 From eeec4fad963490821348a331cca6102ae1c4a7a3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Jul 2008 12:16:30 -0500 Subject: stop_machine(): stop_machine_run() changed to use cpu mask Instead of a "cpu" arg with magic values NR_CPUS (any cpu) and ~0 (all cpus), pass a cpumask_t. Allow NULL for the common case (where we don't care which CPU the function is run on): temporary cpumask_t's are usually considered bad for stack space. This deprecates stop_machine_run, to be removed soon when all the callers are dead. Signed-off-by: Rusty Russell --- include/linux/stop_machine.h | 34 ++++++++++++++++++++++++---------- kernel/cpu.c | 3 ++- kernel/stop_machine.c | 27 +++++++++++++-------------- 3 files changed, 39 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 36c2c7284eb3..f1cb0ba6d715 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -5,19 +5,19 @@ (and more). So the "read" side to such a lock is anything which diables preeempt. */ #include +#include #include #if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP) +/* Deprecated, but useful for transition. */ #define ALL_CPUS ~0U /** - * stop_machine_run: freeze the machine on all CPUs and run this function + * stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn() - * @cpu: if @cpu == n, run @fn() on cpu n - * if @cpu == NR_CPUS, run @fn() on any cpu - * if @cpu == ALL_CPUS, run @fn() on every online CPU. + * @cpus: the cpus to run the @fn() on (NULL = any online cpu) * * Description: This causes a thread to be scheduled on every cpu, * each of which disables interrupts. The result is that noone is @@ -26,22 +26,22 @@ * * This can be thought of as a very heavy write lock, equivalent to * grabbing every spinlock in the kernel. */ -int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); +int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); /** - * __stop_machine_run: freeze the machine on all CPUs and run this function + * __stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn - * @cpu: the cpu to run @fn on (or any, if @cpu == NR_CPUS. + * @cpus: the cpus to run the @fn() on (NULL = any online cpu) * * Description: This is a special version of the above, which assumes cpus * won't come or go while it's being called. Used by hotplug cpu. */ -int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); +int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); #else -static inline int stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu) +static inline int stop_machine(int (*fn)(void *), void *data, + const cpumask_t *cpus) { int ret; local_irq_disable(); @@ -50,4 +50,18 @@ static inline int stop_machine_run(int (*fn)(void *), void *data, return ret; } #endif /* CONFIG_SMP */ + +static inline int __deprecated stop_machine_run(int (*fn)(void *), void *data, + unsigned int cpu) +{ + /* If they don't care which cpu fn runs on, just pick one. */ + if (cpu == NR_CPUS) + return stop_machine(fn, data, NULL); + else if (cpu == ~0U) + return stop_machine(fn, data, &cpu_possible_map); + else { + cpumask_t cpus = cpumask_of_cpu(cpu); + return stop_machine(fn, data, &cpus); + } +} #endif /* _LINUX_STOP_MACHINE */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 53cf508f975a..29510d68338a 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -248,8 +248,9 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) cpus_setall(tmp); cpu_clear(cpu, tmp); set_cpus_allowed_ptr(current, &tmp); + tmp = cpumask_of_cpu(cpu); - err = __stop_machine_run(take_cpu_down, &tcd_param, cpu); + err = __stop_machine(take_cpu_down, &tcd_param, &tmp); if (err) { /* CPU didn't die: tell everyone. Can't complain. */ if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 35882dccc943..e446c7c7d6a9 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -100,7 +100,7 @@ static int chill(void *unused) return 0; } -int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) +int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) { int i, err; struct stop_machine_data active, idle; @@ -112,10 +112,6 @@ int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) idle.fn = chill; idle.data = NULL; - /* If they don't care which cpu fn runs on, just pick one. */ - if (cpu == NR_CPUS) - cpu = any_online_cpu(cpu_online_map); - /* This could be too big for stack on large machines. */ threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL); if (!threads) @@ -128,13 +124,16 @@ int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) set_state(STOPMACHINE_PREPARE); for_each_online_cpu(i) { - struct stop_machine_data *smdata; + struct stop_machine_data *smdata = &idle; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - if (cpu == ALL_CPUS || i == cpu) - smdata = &active; - else - smdata = &idle; + if (!cpus) { + if (i == first_cpu(cpu_online_map)) + smdata = &active; + } else { + if (cpu_isset(i, *cpus)) + smdata = &active; + } threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u", i); @@ -154,7 +153,7 @@ int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) /* We've created all the threads. Wake them all: hold this CPU so one * doesn't hit this CPU until we're ready. */ - cpu = get_cpu(); + get_cpu(); for_each_online_cpu(i) wake_up_process(threads[i]); @@ -177,15 +176,15 @@ kill_threads: return err; } -int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) +int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) { int ret; /* No CPUs can come up or down during this. */ get_online_cpus(); - ret = __stop_machine_run(fn, data, cpu); + ret = __stop_machine(fn, data, cpus); put_online_cpus(); return ret; } -EXPORT_SYMBOL_GPL(stop_machine_run); +EXPORT_SYMBOL_GPL(stop_machine); -- cgit v1.2.3 From 9403540c0653122ca34884a180439ddbfcbcb524 Mon Sep 17 00:00:00 2001 From: Barry Naujok Date: Wed, 21 May 2008 16:50:46 +1000 Subject: dcache: Add case-insensitive support d_ci_add() routine This add a dcache entry to the dcache for lookup, but changing the name that is associated with the entry rather than the one passed in to the lookup routine. First, it sees if the case-exact match already exists in the dcache and uses it if one exists. Otherwise, it allocates a new node with the new name and splices it into the dcache. Original code from ntfs_lookup in fs/ntfs/namei.c by Anton Altaparmakov. Signed-off-by: Barry Naujok Signed-off-by: Anton Altaparmakov Acked-by: Christoph Hellwig --- fs/dcache.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dcache.h | 1 + 2 files changed, 103 insertions(+) (limited to 'include/linux') diff --git a/fs/dcache.c b/fs/dcache.c index f2584d22cb45..101663d15e9f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1220,6 +1220,107 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) return new; } +/** + * d_add_ci - lookup or allocate new dentry with case-exact name + * @inode: the inode case-insensitive lookup has found + * @dentry: the negative dentry that was passed to the parent's lookup func + * @name: the case-exact name to be associated with the returned dentry + * + * This is to avoid filling the dcache with case-insensitive names to the + * same inode, only the actual correct case is stored in the dcache for + * case-insensitive filesystems. + * + * For a case-insensitive lookup match and if the the case-exact dentry + * already exists in in the dcache, use it and return it. + * + * If no entry exists with the exact case name, allocate new dentry with + * the exact case, and return the spliced entry. + */ +struct dentry *d_add_ci(struct inode *inode, struct dentry *dentry, + struct qstr *name) +{ + int error; + struct dentry *found; + struct dentry *new; + + /* Does a dentry matching the name exist already? */ + found = d_hash_and_lookup(dentry->d_parent, name); + /* If not, create it now and return */ + if (!found) { + new = d_alloc(dentry->d_parent, name); + if (!new) { + error = -ENOMEM; + goto err_out; + } + found = d_splice_alias(inode, new); + if (found) { + dput(new); + return found; + } + return new; + } + /* Matching dentry exists, check if it is negative. */ + if (found->d_inode) { + if (unlikely(found->d_inode != inode)) { + /* This can't happen because bad inodes are unhashed. */ + BUG_ON(!is_bad_inode(inode)); + BUG_ON(!is_bad_inode(found->d_inode)); + } + /* + * Already have the inode and the dentry attached, decrement + * the reference count to balance the iget() done + * earlier on. We found the dentry using d_lookup() so it + * cannot be disconnected and thus we do not need to worry + * about any NFS/disconnectedness issues here. + */ + iput(inode); + return found; + } + /* + * Negative dentry: instantiate it unless the inode is a directory and + * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED), + * in which case d_move() that in place of the found dentry. + */ + if (!S_ISDIR(inode->i_mode)) { + /* Not a directory; everything is easy. */ + d_instantiate(found, inode); + return found; + } + spin_lock(&dcache_lock); + if (list_empty(&inode->i_dentry)) { + /* + * Directory without a 'disconnected' dentry; we need to do + * d_instantiate() by hand because it takes dcache_lock which + * we already hold. + */ + list_add(&found->d_alias, &inode->i_dentry); + found->d_inode = inode; + spin_unlock(&dcache_lock); + security_d_instantiate(found, inode); + return found; + } + /* + * Directory with a 'disconnected' dentry; get a reference to the + * 'disconnected' dentry. + */ + new = list_entry(inode->i_dentry.next, struct dentry, d_alias); + dget_locked(new); + spin_unlock(&dcache_lock); + /* Do security vodoo. */ + security_d_instantiate(found, inode); + /* Move new in place of found. */ + d_move(new, found); + /* Balance the iget() we did above. */ + iput(inode); + /* Throw away found. */ + dput(found); + /* Use new as the actual dentry. */ + return new; + +err_out: + iput(inode); + return ERR_PTR(error); +} /** * d_lookup - search for a dentry @@ -2254,6 +2355,7 @@ EXPORT_SYMBOL(d_path); EXPORT_SYMBOL(d_prune_aliases); EXPORT_SYMBOL(d_rehash); EXPORT_SYMBOL(d_splice_alias); +EXPORT_SYMBOL(d_add_ci); EXPORT_SYMBOL(d_validate); EXPORT_SYMBOL(dget_locked); EXPORT_SYMBOL(dput); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 98202c672fde..07aa198f19ed 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -230,6 +230,7 @@ extern void d_delete(struct dentry *); extern struct dentry * d_alloc(struct dentry *, const struct qstr *); extern struct dentry * d_alloc_anon(struct inode *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *); +extern struct dentry * d_add_ci(struct inode *, struct dentry *, struct qstr *); extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); extern void shrink_dcache_for_umount(struct super_block *); -- cgit v1.2.3 From 306cfd630a4d121cf4e08b894d8b4c4cf106e57e Mon Sep 17 00:00:00 2001 From: Adrian McMenamin Date: Sun, 15 Jun 2008 20:48:09 +0100 Subject: maple: tidy maple_driver code by removing redundant connect/disconnect The connect and disconnect functions are unnecessary - everything they do can be accomplished in the initial probe - so remove them. Signed-off-by: Adrian McMenamin Signed-off-by: Paul Mundt --- include/linux/maple.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/maple.h b/include/linux/maple.h index d31e36ebb436..523a286bb477 100644 --- a/include/linux/maple.h +++ b/include/linux/maple.h @@ -61,8 +61,6 @@ struct maple_device { struct maple_driver { unsigned long function; - int (*connect) (struct maple_device * dev); - void (*disconnect) (struct maple_device * dev); struct device_driver drv; }; -- cgit v1.2.3 From d974ae379a2fbe8948f01eabbc6b19c0a80f09bc Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 24 Jul 2008 16:27:46 -0700 Subject: generic, memparse(): constify argument memparse()'s first argument can be const, so it should be. Signed-off-by: Jeremy Fitzhardinge Cc: Andrew Morton Signed-off-by: Ingo Molnar --- include/linux/kernel.h | 2 +- lib/cmdline.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index fdbbf72ca2eb..7889c2f9b75d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -176,7 +176,7 @@ extern int vsscanf(const char *, const char *, va_list) extern int get_option(char **str, int *pint); extern char *get_options(const char *str, int nints, int *ints); -extern unsigned long long memparse(char *ptr, char **retptr); +extern unsigned long long memparse(const char *ptr, char **retptr); extern int core_kernel_text(unsigned long addr); extern int __kernel_text_address(unsigned long addr); diff --git a/lib/cmdline.c b/lib/cmdline.c index 5ba8a942a478..f5f3ad8b62ff 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -126,7 +126,7 @@ char *get_options(const char *str, int nints, int *ints) * megabyte, or one gigabyte, respectively. */ -unsigned long long memparse(char *ptr, char **retptr) +unsigned long long memparse(const char *ptr, char **retptr) { char *endptr; /* local pointer to end of parsed string */ -- cgit v1.2.3 From 7f71ac9374fec066e428892a68db158946cee1fb Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 Jul 2008 18:29:09 +0200 Subject: mfd: Coding style fixes Fix some coding style fixes in the mfd core driver. Signed-off-by: Ben Dooks Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 15 +++++++-------- include/linux/mfd/core.h | 17 ++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 4dc861a7ac56..50207700140c 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -16,9 +16,9 @@ #include static int mfd_add_device(struct platform_device *parent, - const struct mfd_cell *cell, - struct resource *mem_base, - int irq_base) + const struct mfd_cell *cell, + struct resource *mem_base, + int irq_base) { struct resource res[cell->num_resources]; struct platform_device *pdev; @@ -75,11 +75,10 @@ fail_alloc: return ret; } -int mfd_add_devices( - struct platform_device *parent, - const struct mfd_cell *cells, int n_devs, - struct resource *mem_base, - int irq_base) +int mfd_add_devices(struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base) { int i; int ret = 0; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index bb3dd0545928..b7cbb9968339 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -1,5 +1,3 @@ -#ifndef MFD_CORE_H -#define MFD_CORE_H /* * drivers/mfd/mfd-core.h * @@ -13,6 +11,9 @@ * */ +#ifndef MFD_CORE_H +#define MFD_CORE_H + #include /* @@ -38,17 +39,15 @@ struct mfd_cell { const struct resource *resources; }; -static inline struct mfd_cell * -mfd_get_cell(struct platform_device *pdev) +static inline struct mfd_cell *mfd_get_cell(struct platform_device *pdev) { return (struct mfd_cell *)pdev->dev.platform_data; } -extern int mfd_add_devices( - struct platform_device *parent, - const struct mfd_cell *cells, int n_devs, - struct resource *mem_base, - int irq_base); +extern int mfd_add_devices(struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base); extern void mfd_remove_devices(struct platform_device *parent); -- cgit v1.2.3 From e56b3bc7942982ac2589c942fb345e38bc7a341a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 28 Jul 2008 11:32:33 -0700 Subject: cpu masks: optimize and clean up cpumask_of_cpu() Clean up and optimize cpumask_of_cpu(), by sharing all the zero words. Instead of stupidly generating all possible i=0...NR_CPUS 2^i patterns creating a huge array of constant bitmasks, realize that the zero words can be shared. In other words, on a 64-bit architecture, we only ever need 64 of these arrays - with a different bit set in one single world (with enough zero words around it so that we can create any bitmask by just offsetting in that big array). And then we just put enough zeroes around it that we can point every single cpumask to be one of those things. So when we have 4k CPU's, instead of having 4k arrays (of 4k bits each, with one bit set in each array - 2MB memory total), we have exactly 64 arrays instead, each 8k bits in size (64kB total). And then we just point cpumask(n) to the right position (which we can calculate dynamically). Once we have the right arrays, getting "cpumask(n)" ends up being: static inline const cpumask_t *get_cpu_mask(unsigned int cpu) { const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG]; p -= cpu / BITS_PER_LONG; return (const cpumask_t *)p; } This brings other advantages and simplifications as well: - we are not wasting memory that is just filled with a single bit in various different places - we don't need all those games to re-create the arrays in some dense format, because they're already going to be dense enough. if we compile a kernel for up to 4k CPU's, "wasting" that 64kB of memory is a non-issue (especially since by doing this "overlapping" trick we probably get better cache behaviour anyway). [ mingo@elte.hu: Converted Linus's mails into a commit. See: http://lkml.org/lkml/2008/7/27/156 http://lkml.org/lkml/2008/7/28/320 Also applied a family filter - which also has the side-effect of leaving out the bits where Linus calls me an idio... Oh, never mind ;-) ] Signed-off-by: Ingo Molnar Cc: Rusty Russell Cc: Andrew Morton Cc: Al Viro Cc: Mike Travis Signed-off-by: Ingo Molnar --- arch/x86/kernel/setup_percpu.c | 23 -------- include/linux/cpumask.h | 26 ++++++++- kernel/cpu.c | 128 +++++++---------------------------------- 3 files changed, 43 insertions(+), 134 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 1cd53dfcd309..76e305e064f9 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -80,26 +80,6 @@ static void __init setup_per_cpu_maps(void) #endif } -#ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP -/* - * Replace static cpumask_of_cpu_map in the initdata section, - * with one that's allocated sized by the possible number of cpus. - * - * (requires nr_cpu_ids to be initialized) - */ -static void __init setup_cpumask_of_cpu(void) -{ - int i; - - /* alloc_bootmem zeroes memory */ - cpumask_of_cpu_map = alloc_bootmem_low(sizeof(cpumask_t) * nr_cpu_ids); - for (i = 0; i < nr_cpu_ids; i++) - cpu_set(i, cpumask_of_cpu_map[i]); -} -#else -static inline void setup_cpumask_of_cpu(void) { } -#endif - #ifdef CONFIG_X86_32 /* * Great future not-so-futuristic plan: make i386 and x86_64 do it @@ -199,9 +179,6 @@ void __init setup_per_cpu_areas(void) /* Setup node to cpumask map */ setup_node_to_cpumask_map(); - - /* Setup cpumask_of_cpu map */ - setup_cpumask_of_cpu(); } #endif diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 8fa3b6d4a320..96d0509fb8d8 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -265,10 +265,30 @@ static inline void __cpus_shift_left(cpumask_t *dstp, bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } +/* + * Special-case data structure for "single bit set only" constant CPU masks. + * + * We pre-generate all the 64 (or 32) possible bit positions, with enough + * padding to the left and the right, and return the constant pointer + * appropriately offset. + */ +extern const unsigned long + cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)]; + +static inline const cpumask_t *get_cpu_mask(unsigned int cpu) +{ + const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG]; + p -= cpu / BITS_PER_LONG; + return (const cpumask_t *)p; +} + +/* + * In cases where we take the address of the cpumask immediately, + * gcc optimizes it out (it's a constant) and there's no huge stack + * variable created: + */ +#define cpumask_of_cpu(cpu) ({ *get_cpu_mask(cpu); }) -/* cpumask_of_cpu_map[] is in kernel/cpu.c */ -extern const cpumask_t *cpumask_of_cpu_map; -#define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/kernel/cpu.c b/kernel/cpu.c index a35d8995dc8c..06a8358bb418 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -462,115 +462,27 @@ out: #endif /* CONFIG_SMP */ -/* 64 bits of zeros, for initializers. */ -#if BITS_PER_LONG == 32 -#define Z64 0, 0 -#else -#define Z64 0 -#endif +/* + * cpu_bit_bitmap[] is a special, "compressed" data structure that + * represents all NR_CPUS bits binary values of 1< 4 - CMI0(4), CMI0(5), CMI0(6), CMI0(7), -#endif -#if NR_CPUS > 8 - CMI0(8), CMI0(9), CMI0(10), CMI0(11), - CMI0(12), CMI0(13), CMI0(14), CMI0(15), -#endif -#if NR_CPUS > 16 - CMI0(16), CMI0(17), CMI0(18), CMI0(19), - CMI0(20), CMI0(21), CMI0(22), CMI0(23), - CMI0(24), CMI0(25), CMI0(26), CMI0(27), - CMI0(28), CMI0(29), CMI0(30), CMI0(31), -#endif -#if NR_CPUS > 32 -#if BITS_PER_LONG == 32 - CMI(32, 0), CMI(33, 0), CMI(34, 0), CMI(35, 0), - CMI(36, 0), CMI(37, 0), CMI(38, 0), CMI(39, 0), - CMI(40, 0), CMI(41, 0), CMI(42, 0), CMI(43, 0), - CMI(44, 0), CMI(45, 0), CMI(46, 0), CMI(47, 0), - CMI(48, 0), CMI(49, 0), CMI(50, 0), CMI(51, 0), - CMI(52, 0), CMI(53, 0), CMI(54, 0), CMI(55, 0), - CMI(56, 0), CMI(57, 0), CMI(58, 0), CMI(59, 0), - CMI(60, 0), CMI(61, 0), CMI(62, 0), CMI(63, 0), -#else - CMI0(32), CMI0(33), CMI0(34), CMI0(35), - CMI0(36), CMI0(37), CMI0(38), CMI0(39), - CMI0(40), CMI0(41), CMI0(42), CMI0(43), - CMI0(44), CMI0(45), CMI0(46), CMI0(47), - CMI0(48), CMI0(49), CMI0(50), CMI0(51), - CMI0(52), CMI0(53), CMI0(54), CMI0(55), - CMI0(56), CMI0(57), CMI0(58), CMI0(59), - CMI0(60), CMI0(61), CMI0(62), CMI0(63), -#endif /* BITS_PER_LONG == 64 */ -#endif -#if NR_CPUS > 64 - CMI64(64, Z64), -#endif -#if NR_CPUS > 128 - CMI64(128, Z64, Z64), CMI64(192, Z64, Z64, Z64), -#endif -#if NR_CPUS > 256 - CMI256(256, Z256), -#endif -#if NR_CPUS > 512 - CMI256(512, Z256, Z256), CMI256(768, Z256, Z256, Z256), -#endif -#if NR_CPUS > 1024 - CMI1024(1024, Z1024), -#endif -#if NR_CPUS > 2048 - CMI1024(2048, Z1024, Z1024), CMI1024(3072, Z1024, Z1024, Z1024), -#endif -#if NR_CPUS > 4096 -#error NR_CPUS too big. Fix initializers or set CONFIG_HAVE_CPUMASK_OF_CPU_MAP +const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = { + + MASK_DECLARE_8(0), MASK_DECLARE_8(8), + MASK_DECLARE_8(16), MASK_DECLARE_8(24), +#if BITS_PER_LONG > 32 + MASK_DECLARE_8(32), MASK_DECLARE_8(40), + MASK_DECLARE_8(48), MASK_DECLARE_8(56), #endif }; - -const cpumask_t *cpumask_of_cpu_map = cpumask_map; - -EXPORT_SYMBOL_GPL(cpumask_of_cpu_map); +EXPORT_SYMBOL_GPL(cpu_bit_bitmap); -- cgit v1.2.3 From 5fde244d39b88625ac578d83e6625138714de031 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 23 Jul 2008 10:32:24 +0800 Subject: PCI: disable ASPM per ACPI FADT setting The ACPI FADT table includes an ASPM control bit. If the bit is set, do not enable ASPM since it may indicate that the platform doesn't actually support the feature. Tested-by: Jack Howarth Signed-off-by: Shaohua Li Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 7 +++++++ drivers/pci/pcie/aspm.c | 5 +++++ include/acpi/actbl.h | 1 + include/linux/pci-aspm.h | 5 +++++ 4 files changed, 18 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 7764768b6a0e..89a2f0fa10f9 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -372,6 +373,12 @@ static int __init acpi_pci_init(void) printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); pci_no_msi(); } + + if (acpi_gbl_FADT.boot_flags & BAF_PCIE_ASPM_CONTROL) { + printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); + pcie_no_aspm(); + } + ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index f82495583e63..759c51a4e399 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -808,6 +808,11 @@ static int __init pcie_aspm_disable(char *str) __setup("pcie_noaspm", pcie_aspm_disable); +void pcie_no_aspm(void) +{ + aspm_disabled = 1; +} + #ifdef CONFIG_ACPI #include #include diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 1ebbe883f786..13a3d9ad92db 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -277,6 +277,7 @@ enum acpi_prefered_pm_profiles { #define BAF_LEGACY_DEVICES 0x0001 #define BAF_8042_KEYBOARD_CONTROLLER 0x0002 #define BAF_MSI_NOT_SUPPORTED 0x0008 +#define BAF_PCIE_ASPM_CONTROL 0x0010 #define FADT2_REVISION_ID 3 #define FADT2_MINUS_REVISION_ID 2 diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h index a1a1e618e996..91ba0b338b47 100644 --- a/include/linux/pci-aspm.h +++ b/include/linux/pci-aspm.h @@ -27,6 +27,7 @@ extern void pcie_aspm_init_link_state(struct pci_dev *pdev); extern void pcie_aspm_exit_link_state(struct pci_dev *pdev); extern void pcie_aspm_pm_state_change(struct pci_dev *pdev); extern void pci_disable_link_state(struct pci_dev *pdev, int state); +extern void pcie_no_aspm(void); #else static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { @@ -40,6 +41,10 @@ static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) static inline void pci_disable_link_state(struct pci_dev *pdev, int state) { } + +static inline void pcie_no_aspm(void) +{ +} #endif #ifdef CONFIG_PCIEASPM_DEBUG /* this depends on CONFIG_PCIEASPM */ -- cgit v1.2.3 From 149e16372a2066c5474d8a8db9b252afd57eb427 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 23 Jul 2008 10:32:31 +0800 Subject: PCI: disable ASPM on pre-1.1 PCIe devices Disable ASPM on pre-1.1 PCIe devices, as many of them don't implement it correctly. Tested-by: Jack Howarth Signed-off-by: Shaohua Li Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 13 +++++++++++++ drivers/pci/probe.c | 3 ++- include/linux/pci_regs.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 759c51a4e399..704605298c5e 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -510,6 +510,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) { struct pci_dev *child_dev; int child_pos; + u32 reg32; /* * Some functions in a slot might not all be PCIE functions, very @@ -519,6 +520,18 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); if (!child_pos) return -EINVAL; + + /* + * Disable ASPM for pre-1.1 PCIe device, we follow MS to use + * RBER bit to determine if a function is 1.1 version device + */ + pci_read_config_dword(child_dev, child_pos + PCI_EXP_DEVCAP, + ®32); + if (!(reg32 & PCI_EXP_DEVCAP_RBER)) { + printk("Pre-1.1 PCIe device detected, " + "disable ASPM for %s\n", pci_name(pdev)); + return -EINVAL; + } } return 0; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 203630065839..7098dfb07449 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1057,7 +1057,8 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) } } - if (bus->self) + /* only one slot has pcie device */ + if (bus->self && nr) pcie_aspm_init_link_state(bus->self); return nr; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 19958b929905..450684f7eaac 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -374,6 +374,7 @@ #define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ #define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ #define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ #define PCI_EXP_DEVCTL 8 /* Device Control */ -- cgit v1.2.3 From 979b1791e5b8f8b556faeec4c48339e7ed63af9f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 24 Jul 2008 17:18:38 +0100 Subject: PCI: add D3 power state avoidance quirk Libata has some hacks to deal with certain controllers going silly in D3 state. The right way to handle this is to keep a PCI device flag for such devices. That can then be generalised for no ATA devices with power problems. Signed-off-by: Alan Cox Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 4 ++++ drivers/pci/quirks.c | 13 +++++++++++++ include/linux/pci.h | 2 ++ 3 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c95f77d65718..0a3d856833fc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -572,6 +572,10 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (!ret) pci_update_current_state(dev); } + /* This device is quirked not to be put into D3, so + don't put it in D3 */ + if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + return 0; error = pci_raw_set_power_state(dev, state); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 12d489395fad..0fb365074288 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -923,6 +923,19 @@ static void __init quirk_ide_samemode(struct pci_dev *pdev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode); +/* + * Some ATA devices break if put into D3 + */ + +static void __devinit quirk_no_ata_d3(struct pci_dev *pdev) +{ + /* Quirk the legacy ATA devices only. The AHCI ones are ok */ + if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) + pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, quirk_no_ata_d3); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, quirk_no_ata_d3); + /* This was originally an Alpha specific thing, but it really fits here. * The i82375 PCI/EISA bridge appears as non-classified. Fix that. */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 1d296d31abe0..825be3878f68 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -124,6 +124,8 @@ enum pci_dev_flags { * generation too. */ PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1, + /* Device configuration is irrevocably lost if disabled into D3 */ + PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, }; typedef unsigned short __bitwise pci_bus_flags_t; -- cgit v1.2.3 From 56edb58be157a06dc147a988af3588059556d392 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 29 Jul 2008 01:23:32 +0200 Subject: mfd: add platform_data to mfd_cell Adding platform_data to mfd_cell allows passing of platform data directly to the platform_device created for each cell and thus reuse of existing drivers. On the other side it can be used as a hook to mfd_cell itself removing the need in mfd_get_cell method. Signed-off-by: Mike Rapoport Acked-by: Dmitry Baryshkov Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 2 +- drivers/mfd/tc6393xb.c | 4 ++++ include/linux/mfd/core.h | 13 +++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 50207700140c..ad4e4d16a36a 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -32,7 +32,7 @@ static int mfd_add_device(struct platform_device *parent, pdev->dev.parent = &parent->dev; ret = platform_device_add_data(pdev, - cell, sizeof(struct mfd_cell)); + cell->platform_data, cell->data_size); if (ret) goto fail_device; diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 94e55e8e7ce6..9908aaa4881a 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -466,6 +466,10 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) tc6393xb_attach_irq(dev); tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; + tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = + &tc6393xb_cells[TC6393XB_CELL_NAND]; + tc6393xb_cells[TC6393XB_CELL_NAND].data_size = + sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); retval = mfd_add_devices(dev, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index b7cbb9968339..ea45d4a5a2ac 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -29,7 +29,13 @@ struct mfd_cell { int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); - void *driver_data; /* driver-specific data */ + /* driver-specific data for MFD-aware "cell" drivers */ + void *driver_data; + + /* platform_data can be used to either pass data to "generic" + driver or as a hook to mfd_cell for the "cell" drivers */ + void *platform_data; + size_t data_size; /* * This resources can be specified relatievly to the parent device. @@ -39,11 +45,6 @@ struct mfd_cell { const struct resource *resources; }; -static inline struct mfd_cell *mfd_get_cell(struct platform_device *pdev) -{ - return (struct mfd_cell *)pdev->dev.platform_data; -} - extern int mfd_add_devices(struct platform_device *parent, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, -- cgit v1.2.3 From 6beeac76f5f96590fb751af5e138fbc3f62e8460 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Mon, 28 Jul 2008 15:46:22 -0700 Subject: mmu-notifiers: add list_del_init_rcu() Introduce list_del_init_rcu() and document it. Signed-off-by: Andrea Arcangeli Acked-by: Linus Torvalds Cc: "Paul E. McKenney" Cc: Ingo Molnar Cc: Christoph Lameter Cc: Jack Steiner Cc: Robin Holt Cc: Nick Piggin Cc: Peter Zijlstra Cc: Kanoj Sarcar Cc: Roland Dreier Cc: Steve Wise Cc: Avi Kivity Cc: Hugh Dickins Cc: Rusty Russell Cc: Anthony Liguori Cc: Chris Wright Cc: Marcelo Tosatti Cc: Eric Dumazet Cc: "Paul E. McKenney" Cc: Izik Eidus Cc: Anthony Liguori Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rculist.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index b0f39be08b6c..eb4443c7e05b 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -97,6 +97,34 @@ static inline void list_del_rcu(struct list_head *entry) entry->prev = LIST_POISON2; } +/** + * hlist_del_init_rcu - deletes entry from hash list with re-initialization + * @n: the element to delete from the hash list. + * + * Note: list_unhashed() on the node return true after this. It is + * useful for RCU based read lockfree traversal if the writer side + * must know if the list entry is still hashed or already unhashed. + * + * In particular, it means that we can not poison the forward pointers + * that may still be used for walking the hash list and we can only + * zero the pprev pointer so list_unhashed() will return true after + * this. + * + * The caller must take whatever precautions are necessary (such as + * holding appropriate locks) to avoid racing with another + * list-mutation primitive, such as hlist_add_head_rcu() or + * hlist_del_rcu(), running on this same list. However, it is + * perfectly legal to run concurrently with the _rcu list-traversal + * primitives, such as hlist_for_each_entry_rcu(). + */ +static inline void hlist_del_init_rcu(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + n->pprev = NULL; + } +} + /** * list_replace_rcu - replace old entry by new one * @old : the element to be replaced -- cgit v1.2.3 From 7906d00cd1f687268f0a3599442d113767795ae6 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Mon, 28 Jul 2008 15:46:26 -0700 Subject: mmu-notifiers: add mm_take_all_locks() operation mm_take_all_locks holds off reclaim from an entire mm_struct. This allows mmu notifiers to register into the mm at any time with the guarantee that no mmu operation is in progress on the mm. This operation locks against the VM for all pte/vma/mm related operations that could ever happen on a certain mm. This includes vmtruncate, try_to_unmap, and all page faults. The caller must take the mmap_sem in write mode before calling mm_take_all_locks(). The caller isn't allowed to release the mmap_sem until mm_drop_all_locks() returns. mmap_sem in write mode is required in order to block all operations that could modify pagetables and free pages without need of altering the vma layout (for example populate_range() with nonlinear vmas). It's also needed in write mode to avoid new anon_vmas to be associated with existing vmas. A single task can't take more than one mm_take_all_locks() in a row or it would deadlock. mm_take_all_locks() and mm_drop_all_locks are expensive operations that may have to take thousand of locks. mm_take_all_locks() can fail if it's interrupted by signals. When mmu_notifier_register returns, we must be sure that the driver is notified if some task is in the middle of a vmtruncate for the 'mm' where the mmu notifier was registered (mmu_notifier_invalidate_range_start/end is run around the vmtruncation but mmu_notifier_register can run after mmu_notifier_invalidate_range_start and before mmu_notifier_invalidate_range_end). Same problem for rmap paths. And we've to remove page pinning to avoid replicating the tlb_gather logic inside KVM (and GRU doesn't work well with page pinning regardless of needing tlb_gather), so without mm_take_all_locks when vmtruncate frees the page, kvm would have no way to notice that it mapped into sptes a page that is going into the freelist without a chance of any further mmu_notifier notification. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Andrea Arcangeli Acked-by: Linus Torvalds Cc: Christoph Lameter Cc: Jack Steiner Cc: Robin Holt Cc: Nick Piggin Cc: Peter Zijlstra Cc: Kanoj Sarcar Cc: Roland Dreier Cc: Steve Wise Cc: Avi Kivity Cc: Hugh Dickins Cc: Rusty Russell Cc: Anthony Liguori Cc: Chris Wright Cc: Marcelo Tosatti Cc: Eric Dumazet Cc: "Paul E. McKenney" Cc: Izik Eidus Cc: Anthony Liguori Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 3 + include/linux/pagemap.h | 1 + include/linux/rmap.h | 8 +++ mm/mmap.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 6e695eaab4ce..866a3dbe5c75 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1104,6 +1104,9 @@ extern struct vm_area_struct *copy_vma(struct vm_area_struct **, unsigned long addr, unsigned long len, pgoff_t pgoff); extern void exit_mmap(struct mm_struct *); +extern int mm_take_all_locks(struct mm_struct *mm); +extern void mm_drop_all_locks(struct mm_struct *mm); + #ifdef CONFIG_PROC_FS /* From fs/proc/base.c. callers must _not_ hold the mm's exe_file_lock */ extern void added_exe_file_vma(struct mm_struct *mm); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index a81d81890422..a39b38ccdc97 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -20,6 +20,7 @@ */ #define AS_EIO (__GFP_BITS_SHIFT + 0) /* IO error on async write */ #define AS_ENOSPC (__GFP_BITS_SHIFT + 1) /* ENOSPC on async write */ +#define AS_MM_ALL_LOCKS (__GFP_BITS_SHIFT + 2) /* under mm_take_all_locks() */ static inline void mapping_set_error(struct address_space *mapping, int error) { diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 1383692ac5bd..69407f85e10b 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -26,6 +26,14 @@ */ struct anon_vma { spinlock_t lock; /* Serialize access to vma list */ + /* + * NOTE: the LSB of the head.next is set by + * mm_take_all_locks() _after_ taking the above lock. So the + * head must only be read/written after taking the above lock + * to be sure to see a valid next pointer. The LSB bit itself + * is serialized by a system wide lock only visible to + * mm_take_all_locks() (mm_all_locks_mutex). + */ struct list_head head; /* List of private "related" vmas */ }; diff --git a/mm/mmap.c b/mm/mmap.c index 5e0cc99e9cd5..e5f9cb83d6d4 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2268,3 +2268,161 @@ int install_special_mapping(struct mm_struct *mm, return 0; } + +static DEFINE_MUTEX(mm_all_locks_mutex); + +static void vm_lock_anon_vma(struct anon_vma *anon_vma) +{ + if (!test_bit(0, (unsigned long *) &anon_vma->head.next)) { + /* + * The LSB of head.next can't change from under us + * because we hold the mm_all_locks_mutex. + */ + spin_lock(&anon_vma->lock); + /* + * We can safely modify head.next after taking the + * anon_vma->lock. If some other vma in this mm shares + * the same anon_vma we won't take it again. + * + * No need of atomic instructions here, head.next + * can't change from under us thanks to the + * anon_vma->lock. + */ + if (__test_and_set_bit(0, (unsigned long *) + &anon_vma->head.next)) + BUG(); + } +} + +static void vm_lock_mapping(struct address_space *mapping) +{ + if (!test_bit(AS_MM_ALL_LOCKS, &mapping->flags)) { + /* + * AS_MM_ALL_LOCKS can't change from under us because + * we hold the mm_all_locks_mutex. + * + * Operations on ->flags have to be atomic because + * even if AS_MM_ALL_LOCKS is stable thanks to the + * mm_all_locks_mutex, there may be other cpus + * changing other bitflags in parallel to us. + */ + if (test_and_set_bit(AS_MM_ALL_LOCKS, &mapping->flags)) + BUG(); + spin_lock(&mapping->i_mmap_lock); + } +} + +/* + * This operation locks against the VM for all pte/vma/mm related + * operations that could ever happen on a certain mm. This includes + * vmtruncate, try_to_unmap, and all page faults. + * + * The caller must take the mmap_sem in write mode before calling + * mm_take_all_locks(). The caller isn't allowed to release the + * mmap_sem until mm_drop_all_locks() returns. + * + * mmap_sem in write mode is required in order to block all operations + * that could modify pagetables and free pages without need of + * altering the vma layout (for example populate_range() with + * nonlinear vmas). It's also needed in write mode to avoid new + * anon_vmas to be associated with existing vmas. + * + * A single task can't take more than one mm_take_all_locks() in a row + * or it would deadlock. + * + * The LSB in anon_vma->head.next and the AS_MM_ALL_LOCKS bitflag in + * mapping->flags avoid to take the same lock twice, if more than one + * vma in this mm is backed by the same anon_vma or address_space. + * + * We can take all the locks in random order because the VM code + * taking i_mmap_lock or anon_vma->lock outside the mmap_sem never + * takes more than one of them in a row. Secondly we're protected + * against a concurrent mm_take_all_locks() by the mm_all_locks_mutex. + * + * mm_take_all_locks() and mm_drop_all_locks are expensive operations + * that may have to take thousand of locks. + * + * mm_take_all_locks() can fail if it's interrupted by signals. + */ +int mm_take_all_locks(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + int ret = -EINTR; + + BUG_ON(down_read_trylock(&mm->mmap_sem)); + + mutex_lock(&mm_all_locks_mutex); + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (signal_pending(current)) + goto out_unlock; + if (vma->anon_vma) + vm_lock_anon_vma(vma->anon_vma); + if (vma->vm_file && vma->vm_file->f_mapping) + vm_lock_mapping(vma->vm_file->f_mapping); + } + ret = 0; + +out_unlock: + if (ret) + mm_drop_all_locks(mm); + + return ret; +} + +static void vm_unlock_anon_vma(struct anon_vma *anon_vma) +{ + if (test_bit(0, (unsigned long *) &anon_vma->head.next)) { + /* + * The LSB of head.next can't change to 0 from under + * us because we hold the mm_all_locks_mutex. + * + * We must however clear the bitflag before unlocking + * the vma so the users using the anon_vma->head will + * never see our bitflag. + * + * No need of atomic instructions here, head.next + * can't change from under us until we release the + * anon_vma->lock. + */ + if (!__test_and_clear_bit(0, (unsigned long *) + &anon_vma->head.next)) + BUG(); + spin_unlock(&anon_vma->lock); + } +} + +static void vm_unlock_mapping(struct address_space *mapping) +{ + if (test_bit(AS_MM_ALL_LOCKS, &mapping->flags)) { + /* + * AS_MM_ALL_LOCKS can't change to 0 from under us + * because we hold the mm_all_locks_mutex. + */ + spin_unlock(&mapping->i_mmap_lock); + if (!test_and_clear_bit(AS_MM_ALL_LOCKS, + &mapping->flags)) + BUG(); + } +} + +/* + * The mmap_sem cannot be released by the caller until + * mm_drop_all_locks() returns. + */ +void mm_drop_all_locks(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + + BUG_ON(down_read_trylock(&mm->mmap_sem)); + BUG_ON(!mutex_is_locked(&mm_all_locks_mutex)); + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (vma->anon_vma) + vm_unlock_anon_vma(vma->anon_vma); + if (vma->vm_file && vma->vm_file->f_mapping) + vm_unlock_mapping(vma->vm_file->f_mapping); + } + + mutex_unlock(&mm_all_locks_mutex); +} -- cgit v1.2.3 From cddb8a5c14aa89810b40495d94d3d2a0faee6619 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Mon, 28 Jul 2008 15:46:29 -0700 Subject: mmu-notifiers: core With KVM/GFP/XPMEM there isn't just the primary CPU MMU pointing to pages. There are secondary MMUs (with secondary sptes and secondary tlbs) too. sptes in the kvm case are shadow pagetables, but when I say spte in mmu-notifier context, I mean "secondary pte". In GRU case there's no actual secondary pte and there's only a secondary tlb because the GRU secondary MMU has no knowledge about sptes and every secondary tlb miss event in the MMU always generates a page fault that has to be resolved by the CPU (this is not the case of KVM where the a secondary tlb miss will walk sptes in hardware and it will refill the secondary tlb transparently to software if the corresponding spte is present). The same way zap_page_range has to invalidate the pte before freeing the page, the spte (and secondary tlb) must also be invalidated before any page is freed and reused. Currently we take a page_count pin on every page mapped by sptes, but that means the pages can't be swapped whenever they're mapped by any spte because they're part of the guest working set. Furthermore a spte unmap event can immediately lead to a page to be freed when the pin is released (so requiring the same complex and relatively slow tlb_gather smp safe logic we have in zap_page_range and that can be avoided completely if the spte unmap event doesn't require an unpin of the page previously mapped in the secondary MMU). The mmu notifiers allow kvm/GRU/XPMEM to attach to the tsk->mm and know when the VM is swapping or freeing or doing anything on the primary MMU so that the secondary MMU code can drop sptes before the pages are freed, avoiding all page pinning and allowing 100% reliable swapping of guest physical address space. Furthermore it avoids the code that teardown the mappings of the secondary MMU, to implement a logic like tlb_gather in zap_page_range that would require many IPI to flush other cpu tlbs, for each fixed number of spte unmapped. To make an example: if what happens on the primary MMU is a protection downgrade (from writeable to wrprotect) the secondary MMU mappings will be invalidated, and the next secondary-mmu-page-fault will call get_user_pages and trigger a do_wp_page through get_user_pages if it called get_user_pages with write=1, and it'll re-establishing an updated spte or secondary-tlb-mapping on the copied page. Or it will setup a readonly spte or readonly tlb mapping if it's a guest-read, if it calls get_user_pages with write=0. This is just an example. This allows to map any page pointed by any pte (and in turn visible in the primary CPU MMU), into a secondary MMU (be it a pure tlb like GRU, or an full MMU with both sptes and secondary-tlb like the shadow-pagetable layer with kvm), or a remote DMA in software like XPMEM (hence needing of schedule in XPMEM code to send the invalidate to the remote node, while no need to schedule in kvm/gru as it's an immediate event like invalidating primary-mmu pte). At least for KVM without this patch it's impossible to swap guests reliably. And having this feature and removing the page pin allows several other optimizations that simplify life considerably. Dependencies: 1) mm_take_all_locks() to register the mmu notifier when the whole VM isn't doing anything with "mm". This allows mmu notifier users to keep track if the VM is in the middle of the invalidate_range_begin/end critical section with an atomic counter incraese in range_begin and decreased in range_end. No secondary MMU page fault is allowed to map any spte or secondary tlb reference, while the VM is in the middle of range_begin/end as any page returned by get_user_pages in that critical section could later immediately be freed without any further ->invalidate_page notification (invalidate_range_begin/end works on ranges and ->invalidate_page isn't called immediately before freeing the page). To stop all page freeing and pagetable overwrites the mmap_sem must be taken in write mode and all other anon_vma/i_mmap locks must be taken too. 2) It'd be a waste to add branches in the VM if nobody could possibly run KVM/GRU/XPMEM on the kernel, so mmu notifiers will only enabled if CONFIG_KVM=m/y. In the current kernel kvm won't yet take advantage of mmu notifiers, but this already allows to compile a KVM external module against a kernel with mmu notifiers enabled and from the next pull from kvm.git we'll start using them. And GRU/XPMEM will also be able to continue the development by enabling KVM=m in their config, until they submit all GRU/XPMEM GPLv2 code to the mainline kernel. Then they can also enable MMU_NOTIFIERS in the same way KVM does it (even if KVM=n). This guarantees nobody selects MMU_NOTIFIER=y if KVM and GRU and XPMEM are all =n. The mmu_notifier_register call can fail because mm_take_all_locks may be interrupted by a signal and return -EINTR. Because mmu_notifier_reigster is used when a driver startup, a failure can be gracefully handled. Here an example of the change applied to kvm to register the mmu notifiers. Usually when a driver startups other allocations are required anyway and -ENOMEM failure paths exists already. struct kvm *kvm_arch_create_vm(void) { struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL); + int err; if (!kvm) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); + kvm->arch.mmu_notifier.ops = &kvm_mmu_notifier_ops; + err = mmu_notifier_register(&kvm->arch.mmu_notifier, current->mm); + if (err) { + kfree(kvm); + return ERR_PTR(err); + } + return kvm; } mmu_notifier_unregister returns void and it's reliable. The patch also adds a few needed but missing includes that would prevent kernel to compile after these changes on non-x86 archs (x86 didn't need them by luck). [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: fix mm/filemap_xip.c build] [akpm@linux-foundation.org: fix mm/mmu_notifier.c build] Signed-off-by: Andrea Arcangeli Signed-off-by: Nick Piggin Signed-off-by: Christoph Lameter Cc: Jack Steiner Cc: Robin Holt Cc: Nick Piggin Cc: Peter Zijlstra Cc: Kanoj Sarcar Cc: Roland Dreier Cc: Steve Wise Cc: Avi Kivity Cc: Hugh Dickins Cc: Rusty Russell Cc: Anthony Liguori Cc: Chris Wright Cc: Marcelo Tosatti Cc: Eric Dumazet Cc: "Paul E. McKenney" Cc: Izik Eidus Cc: Anthony Liguori Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kvm/Kconfig | 1 + include/linux/mm_types.h | 4 + include/linux/mmu_notifier.h | 279 +++++++++++++++++++++++++++++++++++++++++++ kernel/fork.c | 3 + mm/Kconfig | 3 + mm/Makefile | 1 + mm/filemap_xip.c | 3 +- mm/fremap.c | 3 + mm/hugetlb.c | 3 + mm/memory.c | 35 +++++- mm/mmap.c | 2 + mm/mmu_notifier.c | 277 ++++++++++++++++++++++++++++++++++++++++++ mm/mprotect.c | 3 + mm/mremap.c | 6 + mm/rmap.c | 13 +- 15 files changed, 623 insertions(+), 13 deletions(-) create mode 100644 include/linux/mmu_notifier.h create mode 100644 mm/mmu_notifier.c (limited to 'include/linux') diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 8d45fabc5f3b..ce3251ce5504 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -21,6 +21,7 @@ config KVM tristate "Kernel-based Virtual Machine (KVM) support" depends on HAVE_KVM select PREEMPT_NOTIFIERS + select MMU_NOTIFIER select ANON_INODES ---help--- Support hosting fully virtualized guest machines using hardware diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 746f975b58ef..386edbe2cb4e 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -253,6 +254,9 @@ struct mm_struct { struct file *exe_file; unsigned long num_exe_file_vmas; #endif +#ifdef CONFIG_MMU_NOTIFIER + struct mmu_notifier_mm *mmu_notifier_mm; +#endif }; #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h new file mode 100644 index 000000000000..b77486d152cd --- /dev/null +++ b/include/linux/mmu_notifier.h @@ -0,0 +1,279 @@ +#ifndef _LINUX_MMU_NOTIFIER_H +#define _LINUX_MMU_NOTIFIER_H + +#include +#include +#include + +struct mmu_notifier; +struct mmu_notifier_ops; + +#ifdef CONFIG_MMU_NOTIFIER + +/* + * The mmu notifier_mm structure is allocated and installed in + * mm->mmu_notifier_mm inside the mm_take_all_locks() protected + * critical section and it's released only when mm_count reaches zero + * in mmdrop(). + */ +struct mmu_notifier_mm { + /* all mmu notifiers registerd in this mm are queued in this list */ + struct hlist_head list; + /* to serialize the list modifications and hlist_unhashed */ + spinlock_t lock; +}; + +struct mmu_notifier_ops { + /* + * Called either by mmu_notifier_unregister or when the mm is + * being destroyed by exit_mmap, always before all pages are + * freed. This can run concurrently with other mmu notifier + * methods (the ones invoked outside the mm context) and it + * should tear down all secondary mmu mappings and freeze the + * secondary mmu. If this method isn't implemented you've to + * be sure that nothing could possibly write to the pages + * through the secondary mmu by the time the last thread with + * tsk->mm == mm exits. + * + * As side note: the pages freed after ->release returns could + * be immediately reallocated by the gart at an alias physical + * address with a different cache model, so if ->release isn't + * implemented because all _software_ driven memory accesses + * through the secondary mmu are terminated by the time the + * last thread of this mm quits, you've also to be sure that + * speculative _hardware_ operations can't allocate dirty + * cachelines in the cpu that could not be snooped and made + * coherent with the other read and write operations happening + * through the gart alias address, so leading to memory + * corruption. + */ + void (*release)(struct mmu_notifier *mn, + struct mm_struct *mm); + + /* + * clear_flush_young is called after the VM is + * test-and-clearing the young/accessed bitflag in the + * pte. This way the VM will provide proper aging to the + * accesses to the page through the secondary MMUs and not + * only to the ones through the Linux pte. + */ + int (*clear_flush_young)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long address); + + /* + * Before this is invoked any secondary MMU is still ok to + * read/write to the page previously pointed to by the Linux + * pte because the page hasn't been freed yet and it won't be + * freed until this returns. If required set_page_dirty has to + * be called internally to this method. + */ + void (*invalidate_page)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long address); + + /* + * invalidate_range_start() and invalidate_range_end() must be + * paired and are called only when the mmap_sem and/or the + * locks protecting the reverse maps are held. The subsystem + * must guarantee that no additional references are taken to + * the pages in the range established between the call to + * invalidate_range_start() and the matching call to + * invalidate_range_end(). + * + * Invalidation of multiple concurrent ranges may be + * optionally permitted by the driver. Either way the + * establishment of sptes is forbidden in the range passed to + * invalidate_range_begin/end for the whole duration of the + * invalidate_range_begin/end critical section. + * + * invalidate_range_start() is called when all pages in the + * range are still mapped and have at least a refcount of one. + * + * invalidate_range_end() is called when all pages in the + * range have been unmapped and the pages have been freed by + * the VM. + * + * The VM will remove the page table entries and potentially + * the page between invalidate_range_start() and + * invalidate_range_end(). If the page must not be freed + * because of pending I/O or other circumstances then the + * invalidate_range_start() callback (or the initial mapping + * by the driver) must make sure that the refcount is kept + * elevated. + * + * If the driver increases the refcount when the pages are + * initially mapped into an address space then either + * invalidate_range_start() or invalidate_range_end() may + * decrease the refcount. If the refcount is decreased on + * invalidate_range_start() then the VM can free pages as page + * table entries are removed. If the refcount is only + * droppped on invalidate_range_end() then the driver itself + * will drop the last refcount but it must take care to flush + * any secondary tlb before doing the final free on the + * page. Pages will no longer be referenced by the linux + * address space but may still be referenced by sptes until + * the last refcount is dropped. + */ + void (*invalidate_range_start)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, unsigned long end); + void (*invalidate_range_end)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, unsigned long end); +}; + +/* + * The notifier chains are protected by mmap_sem and/or the reverse map + * semaphores. Notifier chains are only changed when all reverse maps and + * the mmap_sem locks are taken. + * + * Therefore notifier chains can only be traversed when either + * + * 1. mmap_sem is held. + * 2. One of the reverse map locks is held (i_mmap_lock or anon_vma->lock). + * 3. No other concurrent thread can access the list (release) + */ +struct mmu_notifier { + struct hlist_node hlist; + const struct mmu_notifier_ops *ops; +}; + +static inline int mm_has_notifiers(struct mm_struct *mm) +{ + return unlikely(mm->mmu_notifier_mm); +} + +extern int mmu_notifier_register(struct mmu_notifier *mn, + struct mm_struct *mm); +extern int __mmu_notifier_register(struct mmu_notifier *mn, + struct mm_struct *mm); +extern void mmu_notifier_unregister(struct mmu_notifier *mn, + struct mm_struct *mm); +extern void __mmu_notifier_mm_destroy(struct mm_struct *mm); +extern void __mmu_notifier_release(struct mm_struct *mm); +extern int __mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address); +extern void __mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address); +extern void __mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end); +extern void __mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end); + +static inline void mmu_notifier_release(struct mm_struct *mm) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_release(mm); +} + +static inline int mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address) +{ + if (mm_has_notifiers(mm)) + return __mmu_notifier_clear_flush_young(mm, address); + return 0; +} + +static inline void mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_invalidate_page(mm, address); +} + +static inline void mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_invalidate_range_start(mm, start, end); +} + +static inline void mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_invalidate_range_end(mm, start, end); +} + +static inline void mmu_notifier_mm_init(struct mm_struct *mm) +{ + mm->mmu_notifier_mm = NULL; +} + +static inline void mmu_notifier_mm_destroy(struct mm_struct *mm) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_mm_destroy(mm); +} + +/* + * These two macros will sometime replace ptep_clear_flush. + * ptep_clear_flush is impleemnted as macro itself, so this also is + * implemented as a macro until ptep_clear_flush will converted to an + * inline function, to diminish the risk of compilation failure. The + * invalidate_page method over time can be moved outside the PT lock + * and these two macros can be later removed. + */ +#define ptep_clear_flush_notify(__vma, __address, __ptep) \ +({ \ + pte_t __pte; \ + struct vm_area_struct *___vma = __vma; \ + unsigned long ___address = __address; \ + __pte = ptep_clear_flush(___vma, ___address, __ptep); \ + mmu_notifier_invalidate_page(___vma->vm_mm, ___address); \ + __pte; \ +}) + +#define ptep_clear_flush_young_notify(__vma, __address, __ptep) \ +({ \ + int __young; \ + struct vm_area_struct *___vma = __vma; \ + unsigned long ___address = __address; \ + __young = ptep_clear_flush_young(___vma, ___address, __ptep); \ + __young |= mmu_notifier_clear_flush_young(___vma->vm_mm, \ + ___address); \ + __young; \ +}) + +#else /* CONFIG_MMU_NOTIFIER */ + +static inline void mmu_notifier_release(struct mm_struct *mm) +{ +} + +static inline int mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address) +{ + return 0; +} + +static inline void mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address) +{ +} + +static inline void mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + +static inline void mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + +static inline void mmu_notifier_mm_init(struct mm_struct *mm) +{ +} + +static inline void mmu_notifier_mm_destroy(struct mm_struct *mm) +{ +} + +#define ptep_clear_flush_young_notify ptep_clear_flush_young +#define ptep_clear_flush_notify ptep_clear_flush + +#endif /* CONFIG_MMU_NOTIFIER */ + +#endif /* _LINUX_MMU_NOTIFIER_H */ diff --git a/kernel/fork.c b/kernel/fork.c index 8214ba7c8bb1..7ce2ebe84796 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p) if (likely(!mm_alloc_pgd(mm))) { mm->def_flags = 0; + mmu_notifier_mm_init(mm); return mm; } @@ -446,6 +448,7 @@ void __mmdrop(struct mm_struct *mm) BUG_ON(mm == &init_mm); mm_free_pgd(mm); destroy_context(mm); + mmu_notifier_mm_destroy(mm); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop); diff --git a/mm/Kconfig b/mm/Kconfig index efee5d379df4..446c6588c753 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -208,3 +208,6 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config MMU_NOTIFIER + bool diff --git a/mm/Makefile b/mm/Makefile index 06ca2381fef1..da4ccf015aea 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SHMEM) += shmem.o obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o obj-$(CONFIG_SLOB) += slob.o +obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 98a3f31ccd6a..380ab402d711 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -188,7 +189,7 @@ __xip_unmap (struct address_space * mapping, if (pte) { /* Nuke the page table entry. */ flush_cache_page(vma, address, pte_pfn(*pte)); - pteval = ptep_clear_flush(vma, address, pte); + pteval = ptep_clear_flush_notify(vma, address, pte); page_remove_rmap(page, vma); dec_mm_counter(mm, file_rss); BUG_ON(pte_dirty(pteval)); diff --git a/mm/fremap.c b/mm/fremap.c index 07a9c82ce1a3..7881638e4a12 100644 --- a/mm/fremap.c +++ b/mm/fremap.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,9 @@ asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, spin_unlock(&mapping->i_mmap_lock); } + mmu_notifier_invalidate_range_start(mm, start, start + size); err = populate_range(mm, vma, start, size, pgoff); + mmu_notifier_invalidate_range_end(mm, start, start + size); if (!err && !(flags & MAP_NONBLOCK)) { if (unlikely(has_write_lock)) { downgrade_write(&mm->mmap_sem); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 3be79dc18c5c..80eb0d31d0d3 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1672,6 +1673,7 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, BUG_ON(start & ~huge_page_mask(h)); BUG_ON(end & ~huge_page_mask(h)); + mmu_notifier_invalidate_range_start(mm, start, end); spin_lock(&mm->page_table_lock); for (address = start; address < end; address += sz) { ptep = huge_pte_offset(mm, address); @@ -1713,6 +1715,7 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, } spin_unlock(&mm->page_table_lock); flush_tlb_range(vma, start, end); + mmu_notifier_invalidate_range_end(mm, start, end); list_for_each_entry_safe(page, tmp, &page_list, lru) { list_del(&page->lru); put_page(page); diff --git a/mm/memory.c b/mm/memory.c index a8ca04faaea6..67f0ab9077d9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -652,6 +653,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, unsigned long next; unsigned long addr = vma->vm_start; unsigned long end = vma->vm_end; + int ret; /* * Don't copy ptes where a page fault will fill them correctly. @@ -667,17 +669,33 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, if (is_vm_hugetlb_page(vma)) return copy_hugetlb_page_range(dst_mm, src_mm, vma); + /* + * We need to invalidate the secondary MMU mappings only when + * there could be a permission downgrade on the ptes of the + * parent mm. And a permission downgrade will only happen if + * is_cow_mapping() returns true. + */ + if (is_cow_mapping(vma->vm_flags)) + mmu_notifier_invalidate_range_start(src_mm, addr, end); + + ret = 0; dst_pgd = pgd_offset(dst_mm, addr); src_pgd = pgd_offset(src_mm, addr); do { next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(src_pgd)) continue; - if (copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd, - vma, addr, next)) - return -ENOMEM; + if (unlikely(copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd, + vma, addr, next))) { + ret = -ENOMEM; + break; + } } while (dst_pgd++, src_pgd++, addr = next, addr != end); - return 0; + + if (is_cow_mapping(vma->vm_flags)) + mmu_notifier_invalidate_range_end(src_mm, + vma->vm_start, end); + return ret; } static unsigned long zap_pte_range(struct mmu_gather *tlb, @@ -881,7 +899,9 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, unsigned long start = start_addr; spinlock_t *i_mmap_lock = details? details->i_mmap_lock: NULL; int fullmm = (*tlbp)->fullmm; + struct mm_struct *mm = vma->vm_mm; + mmu_notifier_invalidate_range_start(mm, start_addr, end_addr); for ( ; vma && vma->vm_start < end_addr; vma = vma->vm_next) { unsigned long end; @@ -946,6 +966,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, } } out: + mmu_notifier_invalidate_range_end(mm, start_addr, end_addr); return start; /* which is now the end (or restart) address */ } @@ -1616,10 +1637,11 @@ int apply_to_page_range(struct mm_struct *mm, unsigned long addr, { pgd_t *pgd; unsigned long next; - unsigned long end = addr + size; + unsigned long start = addr, end = addr + size; int err; BUG_ON(addr >= end); + mmu_notifier_invalidate_range_start(mm, start, end); pgd = pgd_offset(mm, addr); do { next = pgd_addr_end(addr, end); @@ -1627,6 +1649,7 @@ int apply_to_page_range(struct mm_struct *mm, unsigned long addr, if (err) break; } while (pgd++, addr = next, addr != end); + mmu_notifier_invalidate_range_end(mm, start, end); return err; } EXPORT_SYMBOL_GPL(apply_to_page_range); @@ -1839,7 +1862,7 @@ gotten: * seen in the presence of one thread doing SMC and another * thread doing COW. */ - ptep_clear_flush(vma, address, page_table); + ptep_clear_flush_notify(vma, address, page_table); set_pte_at(mm, address, page_table, entry); update_mmu_cache(vma, address, entry); lru_cache_add_active(new_page); diff --git a/mm/mmap.c b/mm/mmap.c index e5f9cb83d6d4..245c3d69067b 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -2061,6 +2062,7 @@ void exit_mmap(struct mm_struct *mm) /* mm's last user has gone, and its about to be pulled down */ arch_exit_mmap(mm); + mmu_notifier_release(mm); lru_add_drain(); flush_cache_mm(mm); diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c new file mode 100644 index 000000000000..5f4ef0250bee --- /dev/null +++ b/mm/mmu_notifier.c @@ -0,0 +1,277 @@ +/* + * linux/mm/mmu_notifier.c + * + * Copyright (C) 2008 Qumranet, Inc. + * Copyright (C) 2008 SGI + * Christoph Lameter + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This function can't run concurrently against mmu_notifier_register + * because mm->mm_users > 0 during mmu_notifier_register and exit_mmap + * runs with mm_users == 0. Other tasks may still invoke mmu notifiers + * in parallel despite there being no task using this mm any more, + * through the vmas outside of the exit_mmap context, such as with + * vmtruncate. This serializes against mmu_notifier_unregister with + * the mmu_notifier_mm->lock in addition to RCU and it serializes + * against the other mmu notifiers with RCU. struct mmu_notifier_mm + * can't go away from under us as exit_mmap holds an mm_count pin + * itself. + */ +void __mmu_notifier_release(struct mm_struct *mm) +{ + struct mmu_notifier *mn; + + spin_lock(&mm->mmu_notifier_mm->lock); + while (unlikely(!hlist_empty(&mm->mmu_notifier_mm->list))) { + mn = hlist_entry(mm->mmu_notifier_mm->list.first, + struct mmu_notifier, + hlist); + /* + * We arrived before mmu_notifier_unregister so + * mmu_notifier_unregister will do nothing other than + * to wait ->release to finish and + * mmu_notifier_unregister to return. + */ + hlist_del_init_rcu(&mn->hlist); + /* + * RCU here will block mmu_notifier_unregister until + * ->release returns. + */ + rcu_read_lock(); + spin_unlock(&mm->mmu_notifier_mm->lock); + /* + * if ->release runs before mmu_notifier_unregister it + * must be handled as it's the only way for the driver + * to flush all existing sptes and stop the driver + * from establishing any more sptes before all the + * pages in the mm are freed. + */ + if (mn->ops->release) + mn->ops->release(mn, mm); + rcu_read_unlock(); + spin_lock(&mm->mmu_notifier_mm->lock); + } + spin_unlock(&mm->mmu_notifier_mm->lock); + + /* + * synchronize_rcu here prevents mmu_notifier_release to + * return to exit_mmap (which would proceed freeing all pages + * in the mm) until the ->release method returns, if it was + * invoked by mmu_notifier_unregister. + * + * The mmu_notifier_mm can't go away from under us because one + * mm_count is hold by exit_mmap. + */ + synchronize_rcu(); +} + +/* + * If no young bitflag is supported by the hardware, ->clear_flush_young can + * unmap the address and return 1 or 0 depending if the mapping previously + * existed or not. + */ +int __mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + int young = 0; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->clear_flush_young) + young |= mn->ops->clear_flush_young(mn, mm, address); + } + rcu_read_unlock(); + + return young; +} + +void __mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->invalidate_page) + mn->ops->invalidate_page(mn, mm, address); + } + rcu_read_unlock(); +} + +void __mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->invalidate_range_start) + mn->ops->invalidate_range_start(mn, mm, start, end); + } + rcu_read_unlock(); +} + +void __mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->invalidate_range_end) + mn->ops->invalidate_range_end(mn, mm, start, end); + } + rcu_read_unlock(); +} + +static int do_mmu_notifier_register(struct mmu_notifier *mn, + struct mm_struct *mm, + int take_mmap_sem) +{ + struct mmu_notifier_mm *mmu_notifier_mm; + int ret; + + BUG_ON(atomic_read(&mm->mm_users) <= 0); + + ret = -ENOMEM; + mmu_notifier_mm = kmalloc(sizeof(struct mmu_notifier_mm), GFP_KERNEL); + if (unlikely(!mmu_notifier_mm)) + goto out; + + if (take_mmap_sem) + down_write(&mm->mmap_sem); + ret = mm_take_all_locks(mm); + if (unlikely(ret)) + goto out_cleanup; + + if (!mm_has_notifiers(mm)) { + INIT_HLIST_HEAD(&mmu_notifier_mm->list); + spin_lock_init(&mmu_notifier_mm->lock); + mm->mmu_notifier_mm = mmu_notifier_mm; + mmu_notifier_mm = NULL; + } + atomic_inc(&mm->mm_count); + + /* + * Serialize the update against mmu_notifier_unregister. A + * side note: mmu_notifier_release can't run concurrently with + * us because we hold the mm_users pin (either implicitly as + * current->mm or explicitly with get_task_mm() or similar). + * We can't race against any other mmu notifier method either + * thanks to mm_take_all_locks(). + */ + spin_lock(&mm->mmu_notifier_mm->lock); + hlist_add_head(&mn->hlist, &mm->mmu_notifier_mm->list); + spin_unlock(&mm->mmu_notifier_mm->lock); + + mm_drop_all_locks(mm); +out_cleanup: + if (take_mmap_sem) + up_write(&mm->mmap_sem); + /* kfree() does nothing if mmu_notifier_mm is NULL */ + kfree(mmu_notifier_mm); +out: + BUG_ON(atomic_read(&mm->mm_users) <= 0); + return ret; +} + +/* + * Must not hold mmap_sem nor any other VM related lock when calling + * this registration function. Must also ensure mm_users can't go down + * to zero while this runs to avoid races with mmu_notifier_release, + * so mm has to be current->mm or the mm should be pinned safely such + * as with get_task_mm(). If the mm is not current->mm, the mm_users + * pin should be released by calling mmput after mmu_notifier_register + * returns. mmu_notifier_unregister must be always called to + * unregister the notifier. mm_count is automatically pinned to allow + * mmu_notifier_unregister to safely run at any time later, before or + * after exit_mmap. ->release will always be called before exit_mmap + * frees the pages. + */ +int mmu_notifier_register(struct mmu_notifier *mn, struct mm_struct *mm) +{ + return do_mmu_notifier_register(mn, mm, 1); +} +EXPORT_SYMBOL_GPL(mmu_notifier_register); + +/* + * Same as mmu_notifier_register but here the caller must hold the + * mmap_sem in write mode. + */ +int __mmu_notifier_register(struct mmu_notifier *mn, struct mm_struct *mm) +{ + return do_mmu_notifier_register(mn, mm, 0); +} +EXPORT_SYMBOL_GPL(__mmu_notifier_register); + +/* this is called after the last mmu_notifier_unregister() returned */ +void __mmu_notifier_mm_destroy(struct mm_struct *mm) +{ + BUG_ON(!hlist_empty(&mm->mmu_notifier_mm->list)); + kfree(mm->mmu_notifier_mm); + mm->mmu_notifier_mm = LIST_POISON1; /* debug */ +} + +/* + * This releases the mm_count pin automatically and frees the mm + * structure if it was the last user of it. It serializes against + * running mmu notifiers with RCU and against mmu_notifier_unregister + * with the unregister lock + RCU. All sptes must be dropped before + * calling mmu_notifier_unregister. ->release or any other notifier + * method may be invoked concurrently with mmu_notifier_unregister, + * and only after mmu_notifier_unregister returned we're guaranteed + * that ->release or any other method can't run anymore. + */ +void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm) +{ + BUG_ON(atomic_read(&mm->mm_count) <= 0); + + spin_lock(&mm->mmu_notifier_mm->lock); + if (!hlist_unhashed(&mn->hlist)) { + hlist_del_rcu(&mn->hlist); + + /* + * RCU here will force exit_mmap to wait ->release to finish + * before freeing the pages. + */ + rcu_read_lock(); + spin_unlock(&mm->mmu_notifier_mm->lock); + /* + * exit_mmap will block in mmu_notifier_release to + * guarantee ->release is called before freeing the + * pages. + */ + if (mn->ops->release) + mn->ops->release(mn, mm); + rcu_read_unlock(); + } else + spin_unlock(&mm->mmu_notifier_mm->lock); + + /* + * Wait any running method to finish, of course including + * ->release if it was run by mmu_notifier_relase instead of us. + */ + synchronize_rcu(); + + BUG_ON(atomic_read(&mm->mm_count) <= 0); + + mmdrop(mm); +} +EXPORT_SYMBOL_GPL(mmu_notifier_unregister); diff --git a/mm/mprotect.c b/mm/mprotect.c index abd645a3b0a0..fded06f923f4 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -203,10 +204,12 @@ success: dirty_accountable = 1; } + mmu_notifier_invalidate_range_start(mm, start, end); if (is_vm_hugetlb_page(vma)) hugetlb_change_protection(vma, start, end, vma->vm_page_prot); else change_protection(vma, start, end, vma->vm_page_prot, dirty_accountable); + mmu_notifier_invalidate_range_end(mm, start, end); vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); vm_stat_account(mm, newflags, vma->vm_file, nrpages); return 0; diff --git a/mm/mremap.c b/mm/mremap.c index 08e3c7f2bd15..1a7743923c8c 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,11 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, struct mm_struct *mm = vma->vm_mm; pte_t *old_pte, *new_pte, pte; spinlock_t *old_ptl, *new_ptl; + unsigned long old_start; + old_start = old_addr; + mmu_notifier_invalidate_range_start(vma->vm_mm, + old_start, old_end); if (vma->vm_file) { /* * Subtle point from Rajesh Venkatasubramanian: before @@ -116,6 +121,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, pte_unmap_unlock(old_pte - 1, old_ptl); if (mapping) spin_unlock(&mapping->i_mmap_lock); + mmu_notifier_invalidate_range_end(vma->vm_mm, old_start, old_end); } #define LATENCY_LIMIT (64 * PAGE_SIZE) diff --git a/mm/rmap.c b/mm/rmap.c index 39ae5a9bf382..99bc3f9cd796 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -287,7 +288,7 @@ static int page_referenced_one(struct page *page, if (vma->vm_flags & VM_LOCKED) { referenced++; *mapcount = 1; /* break early from loop */ - } else if (ptep_clear_flush_young(vma, address, pte)) + } else if (ptep_clear_flush_young_notify(vma, address, pte)) referenced++; /* Pretend the page is referenced if the task has the @@ -457,7 +458,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma) pte_t entry; flush_cache_page(vma, address, pte_pfn(*pte)); - entry = ptep_clear_flush(vma, address, pte); + entry = ptep_clear_flush_notify(vma, address, pte); entry = pte_wrprotect(entry); entry = pte_mkclean(entry); set_pte_at(mm, address, pte, entry); @@ -705,14 +706,14 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, * skipped over this mm) then we should reactivate it. */ if (!migration && ((vma->vm_flags & VM_LOCKED) || - (ptep_clear_flush_young(vma, address, pte)))) { + (ptep_clear_flush_young_notify(vma, address, pte)))) { ret = SWAP_FAIL; goto out_unmap; } /* Nuke the page table entry. */ flush_cache_page(vma, address, page_to_pfn(page)); - pteval = ptep_clear_flush(vma, address, pte); + pteval = ptep_clear_flush_notify(vma, address, pte); /* Move the dirty bit to the physical page now the pte is gone. */ if (pte_dirty(pteval)) @@ -837,12 +838,12 @@ static void try_to_unmap_cluster(unsigned long cursor, page = vm_normal_page(vma, address, *pte); BUG_ON(!page || PageAnon(page)); - if (ptep_clear_flush_young(vma, address, pte)) + if (ptep_clear_flush_young_notify(vma, address, pte)) continue; /* Nuke the page table entry. */ flush_cache_page(vma, address, pte_pfn(*pte)); - pteval = ptep_clear_flush(vma, address, pte); + pteval = ptep_clear_flush_notify(vma, address, pte); /* If nonlinear, store the file page offset in the pte. */ if (page->index != linear_page_index(vma, address)) -- cgit v1.2.3 From 8ab22b9abb5c55413802e4adc9aa6223324547c3 Mon Sep 17 00:00:00 2001 From: Hisashi Hifumi Date: Mon, 28 Jul 2008 15:46:36 -0700 Subject: vfs: pagecache usage optimization for pagesize!=blocksize When we read some part of a file through pagecache, if there is a pagecache of corresponding index but this page is not uptodate, read IO is issued and this page will be uptodate. I think this is good for pagesize == blocksize environment but there is room for improvement on pagesize != blocksize environment. Because in this case a page can have multiple buffers and even if a page is not uptodate, some buffers can be uptodate. So I suggest that when all buffers which correspond to a part of a file that we want to read are uptodate, use this pagecache and copy data from this pagecache to user buffer even if a page is not uptodate. This can reduce read IO and improve system throughput. I wrote a benchmark program and got result number with this program. This benchmark do: 1: mount and open a test file. 2: create a 512MB file. 3: close a file and umount. 4: mount and again open a test file. 5: pwrite randomly 300000 times on a test file. offset is aligned by IO size(1024bytes). 6: measure time of preading randomly 100000 times on a test file. The result was: 2.6.26 330 sec 2.6.26-patched 226 sec Arch:i386 Filesystem:ext3 Blocksize:1024 bytes Memory: 1GB On ext3/4, a file is written through buffer/block. So random read/write mixed workloads or random read after random write workloads are optimized with this patch under pagesize != blocksize environment. This test result showed this. The benchmark program is as follows: #include #include #include #include #include #include #include #include #include #define LEN 1024 #define LOOP 1024*512 /* 512MB */ main(void) { unsigned long i, offset, filesize; int fd; char buf[LEN]; time_t t1, t2; if (mount("/dev/sda1", "/root/test1/", "ext3", 0, 0) < 0) { perror("cannot mount\n"); exit(1); } memset(buf, 0, LEN); fd = open("/root/test1/testfile", O_CREAT|O_RDWR|O_TRUNC); if (fd < 0) { perror("cannot open file\n"); exit(1); } for (i = 0; i < LOOP; i++) write(fd, buf, LEN); close(fd); if (umount("/root/test1/") < 0) { perror("cannot umount\n"); exit(1); } if (mount("/dev/sda1", "/root/test1/", "ext3", 0, 0) < 0) { perror("cannot mount\n"); exit(1); } fd = open("/root/test1/testfile", O_RDWR); if (fd < 0) { perror("cannot open file\n"); exit(1); } filesize = LEN * LOOP; for (i = 0; i < 300000; i++){ offset = (random() % filesize) & (~(LEN - 1)); pwrite(fd, buf, LEN, offset); } printf("start test\n"); time(&t1); for (i = 0; i < 100000; i++){ offset = (random() % filesize) & (~(LEN - 1)); pread(fd, buf, LEN, offset); } time(&t2); printf("%ld sec\n", t2-t1); close(fd); if (umount("/root/test1/") < 0) { perror("cannot umount\n"); exit(1); } } Signed-off-by: Hisashi Hifumi Cc: Nick Piggin Cc: Christoph Hellwig Cc: Jan Kara Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 46 +++++++++++++++++++++++ fs/ext2/inode.c | 1 + fs/ext3/inode.c | 67 +++++++++++++++++---------------- fs/ext4/inode.c | 92 +++++++++++++++++++++++---------------------- include/linux/buffer_head.h | 2 + include/linux/fs.h | 44 +++++++++++----------- mm/filemap.c | 14 ++++++- 7 files changed, 167 insertions(+), 99 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index f95805019639..ca12a6bb82b1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2095,6 +2095,52 @@ int generic_write_end(struct file *file, struct address_space *mapping, } EXPORT_SYMBOL(generic_write_end); +/* + * block_is_partially_uptodate checks whether buffers within a page are + * uptodate or not. + * + * Returns true if all buffers which correspond to a file portion + * we want to read are uptodate. + */ +int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, + unsigned long from) +{ + struct inode *inode = page->mapping->host; + unsigned block_start, block_end, blocksize; + unsigned to; + struct buffer_head *bh, *head; + int ret = 1; + + if (!page_has_buffers(page)) + return 0; + + blocksize = 1 << inode->i_blkbits; + to = min_t(unsigned, PAGE_CACHE_SIZE - from, desc->count); + to = from + to; + if (from < blocksize && to > PAGE_CACHE_SIZE - blocksize) + return 0; + + head = page_buffers(page); + bh = head; + block_start = 0; + do { + block_end = block_start + blocksize; + if (block_end > from && block_start < to) { + if (!buffer_uptodate(bh)) { + ret = 0; + break; + } + if (block_end >= to) + break; + } + block_start = block_end; + bh = bh->b_this_page; + } while (bh != head); + + return ret; +} +EXPORT_SYMBOL(block_is_partially_uptodate); + /* * Generic "read page" function for block devices that have the normal * get_block functionality. This is most of the block device filesystems. diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 384fc0d1dd74..991d6dfeb51f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -791,6 +791,7 @@ const struct address_space_operations ext2_aops = { .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; const struct address_space_operations ext2_aops_xip = { diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 3bf07d70b914..507d8689b111 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1767,44 +1767,47 @@ static int ext3_journalled_set_page_dirty(struct page *page) } static const struct address_space_operations ext3_ordered_aops = { - .readpage = ext3_readpage, - .readpages = ext3_readpages, - .writepage = ext3_ordered_writepage, - .sync_page = block_sync_page, - .write_begin = ext3_write_begin, - .write_end = ext3_ordered_write_end, - .bmap = ext3_bmap, - .invalidatepage = ext3_invalidatepage, - .releasepage = ext3_releasepage, - .direct_IO = ext3_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext3_readpage, + .readpages = ext3_readpages, + .writepage = ext3_ordered_writepage, + .sync_page = block_sync_page, + .write_begin = ext3_write_begin, + .write_end = ext3_ordered_write_end, + .bmap = ext3_bmap, + .invalidatepage = ext3_invalidatepage, + .releasepage = ext3_releasepage, + .direct_IO = ext3_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext3_writeback_aops = { - .readpage = ext3_readpage, - .readpages = ext3_readpages, - .writepage = ext3_writeback_writepage, - .sync_page = block_sync_page, - .write_begin = ext3_write_begin, - .write_end = ext3_writeback_write_end, - .bmap = ext3_bmap, - .invalidatepage = ext3_invalidatepage, - .releasepage = ext3_releasepage, - .direct_IO = ext3_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext3_readpage, + .readpages = ext3_readpages, + .writepage = ext3_writeback_writepage, + .sync_page = block_sync_page, + .write_begin = ext3_write_begin, + .write_end = ext3_writeback_write_end, + .bmap = ext3_bmap, + .invalidatepage = ext3_invalidatepage, + .releasepage = ext3_releasepage, + .direct_IO = ext3_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext3_journalled_aops = { - .readpage = ext3_readpage, - .readpages = ext3_readpages, - .writepage = ext3_journalled_writepage, - .sync_page = block_sync_page, - .write_begin = ext3_write_begin, - .write_end = ext3_journalled_write_end, - .set_page_dirty = ext3_journalled_set_page_dirty, - .bmap = ext3_bmap, - .invalidatepage = ext3_invalidatepage, - .releasepage = ext3_releasepage, + .readpage = ext3_readpage, + .readpages = ext3_readpages, + .writepage = ext3_journalled_writepage, + .sync_page = block_sync_page, + .write_begin = ext3_write_begin, + .write_end = ext3_journalled_write_end, + .set_page_dirty = ext3_journalled_set_page_dirty, + .bmap = ext3_bmap, + .invalidatepage = ext3_invalidatepage, + .releasepage = ext3_releasepage, + .is_partially_uptodate = block_is_partially_uptodate, }; void ext3_set_aops(struct inode *inode) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8ca2763df091..9843b046c235 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2806,59 +2806,63 @@ static int ext4_journalled_set_page_dirty(struct page *page) } static const struct address_space_operations ext4_ordered_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_normal_writepage, - .sync_page = block_sync_page, - .write_begin = ext4_write_begin, - .write_end = ext4_ordered_write_end, - .bmap = ext4_bmap, - .invalidatepage = ext4_invalidatepage, - .releasepage = ext4_releasepage, - .direct_IO = ext4_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_normal_writepage, + .sync_page = block_sync_page, + .write_begin = ext4_write_begin, + .write_end = ext4_ordered_write_end, + .bmap = ext4_bmap, + .invalidatepage = ext4_invalidatepage, + .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext4_writeback_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_normal_writepage, - .sync_page = block_sync_page, - .write_begin = ext4_write_begin, - .write_end = ext4_writeback_write_end, - .bmap = ext4_bmap, - .invalidatepage = ext4_invalidatepage, - .releasepage = ext4_releasepage, - .direct_IO = ext4_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_normal_writepage, + .sync_page = block_sync_page, + .write_begin = ext4_write_begin, + .write_end = ext4_writeback_write_end, + .bmap = ext4_bmap, + .invalidatepage = ext4_invalidatepage, + .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext4_journalled_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_journalled_writepage, - .sync_page = block_sync_page, - .write_begin = ext4_write_begin, - .write_end = ext4_journalled_write_end, - .set_page_dirty = ext4_journalled_set_page_dirty, - .bmap = ext4_bmap, - .invalidatepage = ext4_invalidatepage, - .releasepage = ext4_releasepage, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_journalled_writepage, + .sync_page = block_sync_page, + .write_begin = ext4_write_begin, + .write_end = ext4_journalled_write_end, + .set_page_dirty = ext4_journalled_set_page_dirty, + .bmap = ext4_bmap, + .invalidatepage = ext4_invalidatepage, + .releasepage = ext4_releasepage, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext4_da_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_da_writepage, - .writepages = ext4_da_writepages, - .sync_page = block_sync_page, - .write_begin = ext4_da_write_begin, - .write_end = ext4_da_write_end, - .bmap = ext4_bmap, - .invalidatepage = ext4_da_invalidatepage, - .releasepage = ext4_releasepage, - .direct_IO = ext4_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_da_writepage, + .writepages = ext4_da_writepages, + .sync_page = block_sync_page, + .write_begin = ext4_da_write_begin, + .write_end = ext4_da_write_end, + .bmap = ext4_bmap, + .invalidatepage = ext4_da_invalidatepage, + .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; void ext4_set_aops(struct inode *inode) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 82aa36c53ea7..50cfe8ceb478 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -205,6 +205,8 @@ void block_invalidatepage(struct page *page, unsigned long offset); int block_write_full_page(struct page *page, get_block_t *get_block, struct writeback_control *wbc); int block_read_full_page(struct page*, get_block_t*); +int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, + unsigned long from); int block_write_begin(struct file *, struct address_space *, loff_t, unsigned, unsigned, struct page **, void **, get_block_t*); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8252b045e624..580b513668fe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -443,6 +443,27 @@ static inline size_t iov_iter_count(struct iov_iter *i) return i->count; } +/* + * "descriptor" for what we're up to with a read. + * This allows us to use the same read code yet + * have multiple different users of the data that + * we read from a file. + * + * The simplest case just copies the data to user + * mode. + */ +typedef struct { + size_t written; + size_t count; + union { + char __user *buf; + void *data; + } arg; + int error; +} read_descriptor_t; + +typedef int (*read_actor_t)(read_descriptor_t *, struct page *, + unsigned long, unsigned long); struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); @@ -484,6 +505,8 @@ struct address_space_operations { int (*migratepage) (struct address_space *, struct page *, struct page *); int (*launder_page) (struct page *); + int (*is_partially_uptodate) (struct page *, read_descriptor_t *, + unsigned long); }; /* @@ -1198,27 +1221,6 @@ struct block_device_operations { struct module *owner; }; -/* - * "descriptor" for what we're up to with a read. - * This allows us to use the same read code yet - * have multiple different users of the data that - * we read from a file. - * - * The simplest case just copies the data to user - * mode. - */ -typedef struct { - size_t written; - size_t count; - union { - char __user * buf; - void *data; - } arg; - int error; -} read_descriptor_t; - -typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long); - /* These macros are for out of kernel modules to test that * the kernel supports the unlocked_ioctl and compat_ioctl * fields in struct file_operations. */ diff --git a/mm/filemap.c b/mm/filemap.c index 5de7633e1dbe..42bbc6909ba4 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1023,8 +1023,17 @@ find_page: ra, filp, page, index, last_index - index); } - if (!PageUptodate(page)) - goto page_not_up_to_date; + if (!PageUptodate(page)) { + if (inode->i_blkbits == PAGE_CACHE_SHIFT || + !mapping->a_ops->is_partially_uptodate) + goto page_not_up_to_date; + if (TestSetPageLocked(page)) + goto page_not_up_to_date; + if (!mapping->a_ops->is_partially_uptodate(page, + desc, offset)) + goto page_not_up_to_date_locked; + unlock_page(page); + } page_ok: /* * i_size must be checked after we know the page is Uptodate. @@ -1094,6 +1103,7 @@ page_not_up_to_date: if (lock_page_killable(page)) goto readpage_eio; +page_not_up_to_date_locked: /* Did it get truncated before we got the lock? */ if (!page->mapping) { unlock_page(page); -- cgit v1.2.3 From 424f525a1241351da947fb48a938128ddd774511 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 29 Jul 2008 01:30:26 +0200 Subject: mfd: accept pure device as a parent, not only platform_device Signed-off-by: Dmitry Baryshkov Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 14 +++++++------- drivers/mfd/tc6393xb.c | 4 ++-- include/linux/mfd/core.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ad4e4d16a36a..9c9c126ed334 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -15,7 +15,7 @@ #include #include -static int mfd_add_device(struct platform_device *parent, +static int mfd_add_device(struct device *parent, int id, const struct mfd_cell *cell, struct resource *mem_base, int irq_base) @@ -25,11 +25,11 @@ static int mfd_add_device(struct platform_device *parent, int ret = -ENOMEM; int r; - pdev = platform_device_alloc(cell->name, parent->id); + pdev = platform_device_alloc(cell->name, id); if (!pdev) goto fail_alloc; - pdev->dev.parent = &parent->dev; + pdev->dev.parent = parent; ret = platform_device_add_data(pdev, cell->platform_data, cell->data_size); @@ -75,7 +75,7 @@ fail_alloc: return ret; } -int mfd_add_devices(struct platform_device *parent, +int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base) @@ -84,7 +84,7 @@ int mfd_add_devices(struct platform_device *parent, int ret = 0; for (i = 0; i < n_devs; i++) { - ret = mfd_add_device(parent, cells + i, mem_base, irq_base); + ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); if (ret) break; } @@ -102,9 +102,9 @@ static int mfd_remove_devices_fn(struct device *dev, void *unused) return 0; } -void mfd_remove_devices(struct platform_device *parent) +void mfd_remove_devices(struct device *parent) { - device_for_each_child(&parent->dev, NULL, mfd_remove_devices_fn); + device_for_each_child(parent, NULL, mfd_remove_devices_fn); } EXPORT_SYMBOL(mfd_remove_devices); diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 9908aaa4881a..f4fd797c1590 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -471,7 +471,7 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) tc6393xb_cells[TC6393XB_CELL_NAND].data_size = sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); - retval = mfd_add_devices(dev, + retval = mfd_add_devices(&dev->dev, dev->id, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), iomem, tcpd->irq_base); @@ -505,7 +505,7 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) struct tc6393xb *tc6393xb = platform_get_drvdata(dev); int ret; - mfd_remove_devices(dev); + mfd_remove_devices(&dev->dev); if (tc6393xb->irq) tc6393xb_detach_irq(dev); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index ea45d4a5a2ac..49ef857cdb2d 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -45,11 +45,11 @@ struct mfd_cell { const struct resource *resources; }; -extern int mfd_add_devices(struct platform_device *parent, +extern int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base); -extern void mfd_remove_devices(struct platform_device *parent); +extern void mfd_remove_devices(struct device *parent); #endif -- cgit v1.2.3 From e930bffe95e1e886a1ede80726ea38df5838d067 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Fri, 25 Jul 2008 16:24:52 +0200 Subject: KVM: Synchronize guest physical memory map to host virtual memory map Synchronize changes to host virtual addresses which are part of a KVM memory slot to the KVM shadow mmu. This allows pte operations like swapping, page migration, and madvise() to transparently work with KVM. Signed-off-by: Andrea Arcangeli Signed-off-by: Avi Kivity --- arch/x86/kvm/mmu.c | 100 +++++++++++++++++++++++++++++++++ arch/x86/kvm/paging_tmpl.h | 12 ++++ include/asm-x86/kvm_host.h | 6 ++ include/linux/kvm_host.h | 24 ++++++++ virt/kvm/kvm_main.c | 135 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 277 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 2fa231923cf7..0bfe2bd305eb 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -653,6 +653,84 @@ static void rmap_write_protect(struct kvm *kvm, u64 gfn) account_shadowed(kvm, gfn); } +static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp) +{ + u64 *spte; + int need_tlb_flush = 0; + + while ((spte = rmap_next(kvm, rmapp, NULL))) { + BUG_ON(!(*spte & PT_PRESENT_MASK)); + rmap_printk("kvm_rmap_unmap_hva: spte %p %llx\n", spte, *spte); + rmap_remove(kvm, spte); + set_shadow_pte(spte, shadow_trap_nonpresent_pte); + need_tlb_flush = 1; + } + return need_tlb_flush; +} + +static int kvm_handle_hva(struct kvm *kvm, unsigned long hva, + int (*handler)(struct kvm *kvm, unsigned long *rmapp)) +{ + int i; + int retval = 0; + + /* + * If mmap_sem isn't taken, we can look the memslots with only + * the mmu_lock by skipping over the slots with userspace_addr == 0. + */ + for (i = 0; i < kvm->nmemslots; i++) { + struct kvm_memory_slot *memslot = &kvm->memslots[i]; + unsigned long start = memslot->userspace_addr; + unsigned long end; + + /* mmu_lock protects userspace_addr */ + if (!start) + continue; + + end = start + (memslot->npages << PAGE_SHIFT); + if (hva >= start && hva < end) { + gfn_t gfn_offset = (hva - start) >> PAGE_SHIFT; + retval |= handler(kvm, &memslot->rmap[gfn_offset]); + retval |= handler(kvm, + &memslot->lpage_info[ + gfn_offset / + KVM_PAGES_PER_HPAGE].rmap_pde); + } + } + + return retval; +} + +int kvm_unmap_hva(struct kvm *kvm, unsigned long hva) +{ + return kvm_handle_hva(kvm, hva, kvm_unmap_rmapp); +} + +static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp) +{ + u64 *spte; + int young = 0; + + spte = rmap_next(kvm, rmapp, NULL); + while (spte) { + int _young; + u64 _spte = *spte; + BUG_ON(!(_spte & PT_PRESENT_MASK)); + _young = _spte & PT_ACCESSED_MASK; + if (_young) { + young = 1; + clear_bit(PT_ACCESSED_SHIFT, (unsigned long *)spte); + } + spte = rmap_next(kvm, rmapp, spte); + } + return young; +} + +int kvm_age_hva(struct kvm *kvm, unsigned long hva) +{ + return kvm_handle_hva(kvm, hva, kvm_age_rmapp); +} + #ifdef MMU_DEBUG static int is_empty_shadow_page(u64 *spt) { @@ -1203,6 +1281,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) int r; int largepage = 0; pfn_t pfn; + unsigned long mmu_seq; down_read(¤t->mm->mmap_sem); if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) { @@ -1210,6 +1289,8 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) largepage = 1; } + mmu_seq = vcpu->kvm->mmu_notifier_seq; + /* implicit mb(), we'll read before PT lock is unlocked */ pfn = gfn_to_pfn(vcpu->kvm, gfn); up_read(¤t->mm->mmap_sem); @@ -1220,6 +1301,8 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) } spin_lock(&vcpu->kvm->mmu_lock); + if (mmu_notifier_retry(vcpu, mmu_seq)) + goto out_unlock; kvm_mmu_free_some_pages(vcpu); r = __direct_map(vcpu, v, write, largepage, gfn, pfn, PT32E_ROOT_LEVEL); @@ -1227,6 +1310,11 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) return r; + +out_unlock: + spin_unlock(&vcpu->kvm->mmu_lock); + kvm_release_pfn_clean(pfn); + return 0; } @@ -1345,6 +1433,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, int r; int largepage = 0; gfn_t gfn = gpa >> PAGE_SHIFT; + unsigned long mmu_seq; ASSERT(vcpu); ASSERT(VALID_PAGE(vcpu->arch.mmu.root_hpa)); @@ -1358,6 +1447,8 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, gfn &= ~(KVM_PAGES_PER_HPAGE-1); largepage = 1; } + mmu_seq = vcpu->kvm->mmu_notifier_seq; + /* implicit mb(), we'll read before PT lock is unlocked */ pfn = gfn_to_pfn(vcpu->kvm, gfn); up_read(¤t->mm->mmap_sem); if (is_error_pfn(pfn)) { @@ -1365,12 +1456,19 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, return 1; } spin_lock(&vcpu->kvm->mmu_lock); + if (mmu_notifier_retry(vcpu, mmu_seq)) + goto out_unlock; kvm_mmu_free_some_pages(vcpu); r = __direct_map(vcpu, gpa, error_code & PFERR_WRITE_MASK, largepage, gfn, pfn, kvm_x86_ops->get_tdp_level()); spin_unlock(&vcpu->kvm->mmu_lock); return r; + +out_unlock: + spin_unlock(&vcpu->kvm->mmu_lock); + kvm_release_pfn_clean(pfn); + return 0; } static void nonpaging_free(struct kvm_vcpu *vcpu) @@ -1670,6 +1768,8 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, gfn &= ~(KVM_PAGES_PER_HPAGE-1); vcpu->arch.update_pte.largepage = 1; } + vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq; + /* implicit mb(), we'll read before PT lock is unlocked */ pfn = gfn_to_pfn(vcpu->kvm, gfn); up_read(¤t->mm->mmap_sem); diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 4d918220baeb..f72ac1fa35f0 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -263,6 +263,8 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *page, pfn = vcpu->arch.update_pte.pfn; if (is_error_pfn(pfn)) return; + if (mmu_notifier_retry(vcpu, vcpu->arch.update_pte.mmu_seq)) + return; kvm_get_pfn(pfn); mmu_set_spte(vcpu, spte, page->role.access, pte_access, 0, 0, gpte & PT_DIRTY_MASK, NULL, largepage, gpte_to_gfn(gpte), @@ -380,6 +382,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, int r; pfn_t pfn; int largepage = 0; + unsigned long mmu_seq; pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code); kvm_mmu_audit(vcpu, "pre page fault"); @@ -413,6 +416,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, largepage = 1; } } + mmu_seq = vcpu->kvm->mmu_notifier_seq; + /* implicit mb(), we'll read before PT lock is unlocked */ pfn = gfn_to_pfn(vcpu->kvm, walker.gfn); up_read(¤t->mm->mmap_sem); @@ -424,6 +429,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, } spin_lock(&vcpu->kvm->mmu_lock); + if (mmu_notifier_retry(vcpu, mmu_seq)) + goto out_unlock; kvm_mmu_free_some_pages(vcpu); shadow_pte = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault, largepage, &write_pt, pfn); @@ -439,6 +446,11 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, spin_unlock(&vcpu->kvm->mmu_lock); return write_pt; + +out_unlock: + spin_unlock(&vcpu->kvm->mmu_lock); + kvm_release_pfn_clean(pfn); + return 0; } static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr) diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h index bc34dc21f178..0f3c53114614 100644 --- a/include/asm-x86/kvm_host.h +++ b/include/asm-x86/kvm_host.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -251,6 +252,7 @@ struct kvm_vcpu_arch { gfn_t gfn; /* presumed gfn during guest pte update */ pfn_t pfn; /* pfn corresponding to that gfn */ int largepage; + unsigned long mmu_seq; } update_pte; struct i387_fxsave_struct host_fx_image; @@ -729,4 +731,8 @@ asmlinkage void kvm_handle_fault_on_reboot(void); KVM_EX_ENTRY " 666b, 667b \n\t" \ ".popsection" +#define KVM_ARCH_WANT_MMU_NOTIFIER +int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); +int kvm_age_hva(struct kvm *kvm, unsigned long hva); + #endif diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 07d68a8ae8e9..8525afc53107 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -121,6 +121,12 @@ struct kvm { struct kvm_coalesced_mmio_dev *coalesced_mmio_dev; struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; #endif + +#ifdef KVM_ARCH_WANT_MMU_NOTIFIER + struct mmu_notifier mmu_notifier; + unsigned long mmu_notifier_seq; + long mmu_notifier_count; +#endif }; /* The guest did something we don't support. */ @@ -332,4 +338,22 @@ int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg) #define kvm_trace_cleanup() ((void)0) #endif +#ifdef KVM_ARCH_WANT_MMU_NOTIFIER +static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq) +{ + if (unlikely(vcpu->kvm->mmu_notifier_count)) + return 1; + /* + * Both reads happen under the mmu_lock and both values are + * modified under mmu_lock, so there's no need of smb_rmb() + * here in between, otherwise mmu_notifier_count should be + * read before mmu_notifier_seq, see + * mmu_notifier_invalidate_range_end write side. + */ + if (vcpu->kvm->mmu_notifier_seq != mmu_seq) + return 1; + return 0; +} +#endif + #endif diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 3735212cd3f8..7dd9b0b85e4e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -192,6 +192,123 @@ void kvm_vcpu_uninit(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_vcpu_uninit); +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) +static inline struct kvm *mmu_notifier_to_kvm(struct mmu_notifier *mn) +{ + return container_of(mn, struct kvm, mmu_notifier); +} + +static void kvm_mmu_notifier_invalidate_page(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long address) +{ + struct kvm *kvm = mmu_notifier_to_kvm(mn); + int need_tlb_flush; + + /* + * When ->invalidate_page runs, the linux pte has been zapped + * already but the page is still allocated until + * ->invalidate_page returns. So if we increase the sequence + * here the kvm page fault will notice if the spte can't be + * established because the page is going to be freed. If + * instead the kvm page fault establishes the spte before + * ->invalidate_page runs, kvm_unmap_hva will release it + * before returning. + * + * The sequence increase only need to be seen at spin_unlock + * time, and not at spin_lock time. + * + * Increasing the sequence after the spin_unlock would be + * unsafe because the kvm page fault could then establish the + * pte after kvm_unmap_hva returned, without noticing the page + * is going to be freed. + */ + spin_lock(&kvm->mmu_lock); + kvm->mmu_notifier_seq++; + need_tlb_flush = kvm_unmap_hva(kvm, address); + spin_unlock(&kvm->mmu_lock); + + /* we've to flush the tlb before the pages can be freed */ + if (need_tlb_flush) + kvm_flush_remote_tlbs(kvm); + +} + +static void kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + struct kvm *kvm = mmu_notifier_to_kvm(mn); + int need_tlb_flush = 0; + + spin_lock(&kvm->mmu_lock); + /* + * The count increase must become visible at unlock time as no + * spte can be established without taking the mmu_lock and + * count is also read inside the mmu_lock critical section. + */ + kvm->mmu_notifier_count++; + for (; start < end; start += PAGE_SIZE) + need_tlb_flush |= kvm_unmap_hva(kvm, start); + spin_unlock(&kvm->mmu_lock); + + /* we've to flush the tlb before the pages can be freed */ + if (need_tlb_flush) + kvm_flush_remote_tlbs(kvm); +} + +static void kvm_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + struct kvm *kvm = mmu_notifier_to_kvm(mn); + + spin_lock(&kvm->mmu_lock); + /* + * This sequence increase will notify the kvm page fault that + * the page that is going to be mapped in the spte could have + * been freed. + */ + kvm->mmu_notifier_seq++; + /* + * The above sequence increase must be visible before the + * below count decrease but both values are read by the kvm + * page fault under mmu_lock spinlock so we don't need to add + * a smb_wmb() here in between the two. + */ + kvm->mmu_notifier_count--; + spin_unlock(&kvm->mmu_lock); + + BUG_ON(kvm->mmu_notifier_count < 0); +} + +static int kvm_mmu_notifier_clear_flush_young(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long address) +{ + struct kvm *kvm = mmu_notifier_to_kvm(mn); + int young; + + spin_lock(&kvm->mmu_lock); + young = kvm_age_hva(kvm, address); + spin_unlock(&kvm->mmu_lock); + + if (young) + kvm_flush_remote_tlbs(kvm); + + return young; +} + +static const struct mmu_notifier_ops kvm_mmu_notifier_ops = { + .invalidate_page = kvm_mmu_notifier_invalidate_page, + .invalidate_range_start = kvm_mmu_notifier_invalidate_range_start, + .invalidate_range_end = kvm_mmu_notifier_invalidate_range_end, + .clear_flush_young = kvm_mmu_notifier_clear_flush_young, +}; +#endif /* CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER */ + static struct kvm *kvm_create_vm(void) { struct kvm *kvm = kvm_arch_create_vm(); @@ -212,6 +329,21 @@ static struct kvm *kvm_create_vm(void) (struct kvm_coalesced_mmio_ring *)page_address(page); #endif +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) + { + int err; + kvm->mmu_notifier.ops = &kvm_mmu_notifier_ops; + err = mmu_notifier_register(&kvm->mmu_notifier, current->mm); + if (err) { +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + put_page(page); +#endif + kfree(kvm); + return ERR_PTR(err); + } + } +#endif + kvm->mm = current->mm; atomic_inc(&kvm->mm->mm_count); spin_lock_init(&kvm->mmu_lock); @@ -271,6 +403,9 @@ static void kvm_destroy_vm(struct kvm *kvm) #ifdef KVM_COALESCED_MMIO_PAGE_OFFSET if (kvm->coalesced_mmio_ring != NULL) free_page((unsigned long)kvm->coalesced_mmio_ring); +#endif +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) + mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm); #endif kvm_arch_destroy_vm(kvm); mmdrop(mm); -- cgit v1.2.3 From ed8486243379ef3e6c61363df915882945c0eaec Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 29 Jul 2008 11:30:57 +0300 Subject: KVM: Advertise synchronized mmu support to userspace Signed-off-by: Avi Kivity --- arch/x86/kvm/x86.c | 1 + include/linux/kvm.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c7b01efe0646..0d682fc6aeb3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -883,6 +883,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_PIT: case KVM_CAP_NOP_IO_DELAY: case KVM_CAP_MP_STATE: + case KVM_CAP_SYNC_MMU: r = 1; break; case KVM_CAP_COALESCED_MMIO: diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 0ea064cbfbc8..69511f74f912 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -371,6 +371,7 @@ struct kvm_trace_rec { #define KVM_CAP_PV_MMU 13 #define KVM_CAP_MP_STATE 14 #define KVM_CAP_COALESCED_MMIO 15 +#define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */ /* * ioctls for VM fds -- cgit v1.2.3 From 8978b74253280d59e97cf49a3ec2c0cbccd5b801 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 29 Jul 2008 13:38:53 +0900 Subject: generic, x86: fix add iommu_num_pages helper function This IOMMU helper function doesn't work for some architectures: http://marc.info/?l=linux-kernel&m=121699304403202&w=2 It also breaks POWER and SPARC builds: http://marc.info/?l=linux-kernel&m=121730388001890&w=2 Currently, only x86 IOMMUs use this so let's move it to x86 for now. Reported-by: Stephen Rothwell Signed-off-by: FUJITA Tomonori Signed-off-by: Ingo Molnar --- arch/x86/kernel/pci-dma.c | 8 ++++++++ include/asm-x86/iommu.h | 2 ++ include/linux/iommu-helper.h | 1 - lib/iommu-helper.c | 8 -------- 4 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 8dbffb846de9..87d4d6964ec2 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -123,6 +123,14 @@ void __init pci_iommu_alloc(void) pci_swiotlb_init(); } + +unsigned long iommu_num_pages(unsigned long addr, unsigned long len) +{ + unsigned long size = roundup((addr & ~PAGE_MASK) + len, PAGE_SIZE); + + return size >> PAGE_SHIFT; +} +EXPORT_SYMBOL(iommu_num_pages); #endif /* diff --git a/include/asm-x86/iommu.h b/include/asm-x86/iommu.h index ecc8061904a9..5f888cc5be49 100644 --- a/include/asm-x86/iommu.h +++ b/include/asm-x86/iommu.h @@ -7,6 +7,8 @@ extern struct dma_mapping_ops nommu_dma_ops; extern int force_iommu, no_iommu; extern int iommu_detected; +extern unsigned long iommu_num_pages(unsigned long addr, unsigned long len); + #ifdef CONFIG_GART_IOMMU extern int gart_iommu_aperture; extern int gart_iommu_aperture_allowed; diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h index f8598f583944..c975caf75385 100644 --- a/include/linux/iommu-helper.h +++ b/include/linux/iommu-helper.h @@ -8,4 +8,3 @@ extern unsigned long iommu_area_alloc(unsigned long *map, unsigned long size, unsigned long align_mask); extern void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr); -extern unsigned long iommu_num_pages(unsigned long addr, unsigned long len); diff --git a/lib/iommu-helper.c b/lib/iommu-helper.c index 889ddce2021e..a3b8d4c3f77a 100644 --- a/lib/iommu-helper.c +++ b/lib/iommu-helper.c @@ -80,11 +80,3 @@ void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr) } } EXPORT_SYMBOL(iommu_area_free); - -unsigned long iommu_num_pages(unsigned long addr, unsigned long len) -{ - unsigned long size = roundup((addr & ~PAGE_MASK) + len, PAGE_SIZE); - - return size >> PAGE_SHIFT; -} -EXPORT_SYMBOL(iommu_num_pages); -- cgit v1.2.3 From 1795cf48b322b4d19230a40dbe7181acedd34a94 Mon Sep 17 00:00:00 2001 From: Adrian McMenamin Date: Tue, 29 Jul 2008 22:10:56 +0900 Subject: sh/maple: clean maple bus code This patch cleans up the handling of the maple bus queue to remove the risk of races when adding packets. It also removes references to the redundant connect and disconnect functions. Signed-off-by: Adrian McMenamin Signed-off-by: Paul Mundt --- drivers/sh/maple/maple.c | 265 ++++++++++++++++++++++++++++++++--------------- include/linux/maple.h | 6 +- 2 files changed, 189 insertions(+), 82 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index 617efb1640b1..be97789fa5fd 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -24,13 +24,12 @@ #include #include #include +#include #include #include #include -#include -#include -#include -#include +#include +#include MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin"); MODULE_DESCRIPTION("Maple bus driver for Dreamcast"); @@ -46,14 +45,15 @@ static DECLARE_WORK(maple_vblank_process, maple_vblank_handler); static LIST_HEAD(maple_waitq); static LIST_HEAD(maple_sentq); -static DEFINE_MUTEX(maple_list_lock); +/* mutex to protect queue of waiting packets */ +static DEFINE_MUTEX(maple_wlist_lock); static struct maple_driver maple_dummy_driver; static struct device maple_bus; static int subdevice_map[MAPLE_PORTS]; static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; static unsigned long maple_pnp_time; -static int started, scanning, liststatus, fullscan; +static int started, scanning, fullscan; static struct kmem_cache *maple_queue_cache; struct maple_device_specify { @@ -129,35 +129,124 @@ static void maple_release_device(struct device *dev) kfree(mdev); } -/** +/* * maple_add_packet - add a single instruction to the queue - * @mq: instruction to add to waiting queue + * @mdev - maple device + * @function - function on device being queried + * @command - maple command to add + * @length - length of command string (in 32 bit words) + * @data - remainder of command string */ -void maple_add_packet(struct mapleq *mq) +int maple_add_packet(struct maple_device *mdev, u32 function, u32 command, + size_t length, void *data) { - mutex_lock(&maple_list_lock); - list_add(&mq->list, &maple_waitq); - mutex_unlock(&maple_list_lock); + int locking, ret = 0; + void *sendbuf = NULL; + + mutex_lock(&maple_wlist_lock); + /* bounce if device already locked */ + locking = mutex_is_locked(&mdev->mq->mutex); + if (locking) { + ret = -EBUSY; + goto out; + } + + mutex_lock(&mdev->mq->mutex); + + if (length) { + sendbuf = kmalloc(length * 4, GFP_KERNEL); + if (!sendbuf) { + mutex_unlock(&mdev->mq->mutex); + ret = -ENOMEM; + goto out; + } + ((__be32 *)sendbuf)[0] = cpu_to_be32(function); + } + + mdev->mq->command = command; + mdev->mq->length = length; + if (length > 1) + memcpy(sendbuf + 4, data, (length - 1) * 4); + mdev->mq->sendbuf = sendbuf; + + list_add(&mdev->mq->list, &maple_waitq); +out: + mutex_unlock(&maple_wlist_lock); + return ret; } EXPORT_SYMBOL_GPL(maple_add_packet); +/* + * maple_add_packet_sleeps - add a single instruction to the queue + * - waits for lock to be free + * @mdev - maple device + * @function - function on device being queried + * @command - maple command to add + * @length - length of command string (in 32 bit words) + * @data - remainder of command string + */ +int maple_add_packet_sleeps(struct maple_device *mdev, u32 function, + u32 command, size_t length, void *data) +{ + int locking, ret = 0; + void *sendbuf = NULL; + + locking = mutex_lock_interruptible(&mdev->mq->mutex); + if (locking) { + ret = -EIO; + goto out; + } + + if (length) { + sendbuf = kmalloc(length * 4, GFP_KERNEL); + if (!sendbuf) { + mutex_unlock(&mdev->mq->mutex); + ret = -ENOMEM; + goto out; + } + ((__be32 *)sendbuf)[0] = cpu_to_be32(function); + } + + mdev->mq->command = command; + mdev->mq->length = length; + if (length > 1) + memcpy(sendbuf + 4, data, (length - 1) * 4); + mdev->mq->sendbuf = sendbuf; + + mutex_lock(&maple_wlist_lock); + list_add(&mdev->mq->list, &maple_waitq); + mutex_unlock(&maple_wlist_lock); +out: + return ret; +} +EXPORT_SYMBOL_GPL(maple_add_packet_sleeps); + static struct mapleq *maple_allocq(struct maple_device *mdev) { struct mapleq *mq; mq = kmalloc(sizeof(*mq), GFP_KERNEL); if (!mq) - return NULL; + goto failed_nomem; mq->dev = mdev; mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL); mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp); - if (!mq->recvbuf) { - kfree(mq); - return NULL; - } + if (!mq->recvbuf) + goto failed_p2; + /* + * most devices do not need the mutex - but + * anything that injects block reads or writes + * will rely on it + */ + mutex_init(&mq->mutex); return mq; + +failed_p2: + kfree(mq); +failed_nomem: + return NULL; } static struct maple_device *maple_alloc_dev(int port, int unit) @@ -178,7 +267,6 @@ static struct maple_device *maple_alloc_dev(int port, int unit) } mdev->dev.bus = &maple_bus_type; mdev->dev.parent = &maple_bus; - mdev->function = 0; return mdev; } @@ -216,7 +304,6 @@ static void maple_build_block(struct mapleq *mq) *maple_sendptr++ = PHYSADDR(mq->recvbuf); *maple_sendptr++ = mq->command | (to << 8) | (from << 16) | (len << 24); - while (len-- > 0) *maple_sendptr++ = *lsendbuf++; } @@ -224,22 +311,27 @@ static void maple_build_block(struct mapleq *mq) /* build up command queue */ static void maple_send(void) { - int i; - int maple_packets; + int i, maple_packets = 0; struct mapleq *mq, *nmq; if (!list_empty(&maple_sentq)) return; - if (list_empty(&maple_waitq) || !maple_dma_done()) + mutex_lock(&maple_wlist_lock); + if (list_empty(&maple_waitq) || !maple_dma_done()) { + mutex_unlock(&maple_wlist_lock); return; - maple_packets = 0; - maple_sendptr = maple_lastptr = maple_sendbuf; + } + mutex_unlock(&maple_wlist_lock); + maple_lastptr = maple_sendbuf; + maple_sendptr = maple_sendbuf; + mutex_lock(&maple_wlist_lock); list_for_each_entry_safe(mq, nmq, &maple_waitq, list) { maple_build_block(mq); list_move(&mq->list, &maple_sentq); if (maple_packets++ > MAPLE_MAXPACKETS) break; } + mutex_unlock(&maple_wlist_lock); if (maple_packets > 0) { for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++) dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE, @@ -247,7 +339,8 @@ static void maple_send(void) } } -static int attach_matching_maple_driver(struct device_driver *driver, +/* check if there is a driver registered likely to match this device */ +static int check_matching_maple_driver(struct device_driver *driver, void *devptr) { struct maple_driver *maple_drv; @@ -255,12 +348,8 @@ static int attach_matching_maple_driver(struct device_driver *driver, mdev = devptr; maple_drv = to_maple_driver(driver); - if (mdev->devinfo.function & be32_to_cpu(maple_drv->function)) { - if (maple_drv->connect(mdev) == 0) { - mdev->driver = maple_drv; - return 1; - } - } + if (mdev->devinfo.function & cpu_to_be32(maple_drv->function)) + return 1; return 0; } @@ -268,11 +357,6 @@ static void maple_detach_driver(struct maple_device *mdev) { if (!mdev) return; - if (mdev->driver) { - if (mdev->driver->disconnect) - mdev->driver->disconnect(mdev); - } - mdev->driver = NULL; device_unregister(&mdev->dev); mdev = NULL; } @@ -328,8 +412,8 @@ static void maple_attach_driver(struct maple_device *mdev) mdev->port, mdev->unit, function); matched = - bus_for_each_drv(&maple_bus_type, NULL, mdev, - attach_matching_maple_driver); + bus_for_each_drv(&maple_bus_type, NULL, mdev, + check_matching_maple_driver); if (matched == 0) { /* Driver does not exist yet */ @@ -373,45 +457,48 @@ static int detach_maple_device(struct device *device, void *portptr) static int setup_maple_commands(struct device *device, void *ignored) { + int add; struct maple_device *maple_dev = to_maple_dev(device); if ((maple_dev->interval > 0) && time_after(jiffies, maple_dev->when)) { - maple_dev->when = jiffies + maple_dev->interval; - maple_dev->mq->command = MAPLE_COMMAND_GETCOND; - maple_dev->mq->sendbuf = &maple_dev->function; - maple_dev->mq->length = 1; - maple_add_packet(maple_dev->mq); - liststatus++; + /* bounce if we cannot lock */ + add = maple_add_packet(maple_dev, + be32_to_cpu(maple_dev->devinfo.function), + MAPLE_COMMAND_GETCOND, 1, NULL); + if (!add) + maple_dev->when = jiffies + maple_dev->interval; } else { - if (time_after(jiffies, maple_pnp_time)) { - maple_dev->mq->command = MAPLE_COMMAND_DEVINFO; - maple_dev->mq->length = 0; - maple_add_packet(maple_dev->mq); - liststatus++; - } + if (time_after(jiffies, maple_pnp_time)) + /* This will also bounce */ + maple_add_packet(maple_dev, 0, + MAPLE_COMMAND_DEVINFO, 0, NULL); } - return 0; } /* VBLANK bottom half - implemented via workqueue */ static void maple_vblank_handler(struct work_struct *work) { - if (!maple_dma_done()) - return; - if (!list_empty(&maple_sentq)) + if (!list_empty(&maple_sentq) || !maple_dma_done()) return; + ctrl_outl(0, MAPLE_ENABLE); - liststatus = 0; + bus_for_each_dev(&maple_bus_type, NULL, NULL, setup_maple_commands); + if (time_after(jiffies, maple_pnp_time)) maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL; - if (liststatus && list_empty(&maple_sentq)) { - INIT_LIST_HEAD(&maple_sentq); + + mutex_lock(&maple_wlist_lock); + if (!list_empty(&maple_waitq) && list_empty(&maple_sentq)) { + mutex_unlock(&maple_wlist_lock); maple_send(); + } else { + mutex_unlock(&maple_wlist_lock); } + maplebus_dma_reset(); } @@ -422,8 +509,8 @@ static void maple_map_subunits(struct maple_device *mdev, int submask) struct maple_device *mdev_add; struct maple_device_specify ds; + ds.port = mdev->port; for (k = 0; k < 5; k++) { - ds.port = mdev->port; ds.unit = k + 1; retval = bus_for_each_dev(&maple_bus_type, NULL, &ds, @@ -437,9 +524,9 @@ static void maple_map_subunits(struct maple_device *mdev, int submask) mdev_add = maple_alloc_dev(mdev->port, k + 1); if (!mdev_add) return; - mdev_add->mq->command = MAPLE_COMMAND_DEVINFO; - mdev_add->mq->length = 0; - maple_add_packet(mdev_add->mq); + maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO, + 0, NULL); + /* mark that we are checking sub devices */ scanning = 1; } submask = submask >> 1; @@ -505,6 +592,28 @@ static void maple_response_devinfo(struct maple_device *mdev, } } +static void maple_port_rescan(void) +{ + int i; + struct maple_device *mdev; + + fullscan = 1; + for (i = 0; i < MAPLE_PORTS; i++) { + if (checked[i] == false) { + fullscan = 0; + mdev = baseunits[i]; + /* + * test lock in case scan has failed + * but device is still locked + */ + if (mutex_is_locked(&mdev->mq->mutex)) + mutex_unlock(&mdev->mq->mutex); + maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO, + 0, NULL); + } + } +} + /* maple dma end bottom half - implemented via workqueue */ static void maple_dma_handler(struct work_struct *work) { @@ -512,7 +621,6 @@ static void maple_dma_handler(struct work_struct *work) struct maple_device *dev; char *recvbuf; enum maple_code code; - int i; if (!maple_dma_done()) return; @@ -522,6 +630,10 @@ static void maple_dma_handler(struct work_struct *work) recvbuf = mq->recvbuf; code = recvbuf[0]; dev = mq->dev; + kfree(mq->sendbuf); + mutex_unlock(&mq->mutex); + list_del_init(&mq->list); + switch (code) { case MAPLE_RESPONSE_NONE: maple_response_none(dev, mq); @@ -558,26 +670,16 @@ static void maple_dma_handler(struct work_struct *work) break; } } - INIT_LIST_HEAD(&maple_sentq); + /* if scanning is 1 then we have subdevices to check */ if (scanning == 1) { maple_send(); scanning = 2; } else scanning = 0; - - if (!fullscan) { - fullscan = 1; - for (i = 0; i < MAPLE_PORTS; i++) { - if (checked[i] == false) { - fullscan = 0; - dev = baseunits[i]; - dev->mq->command = - MAPLE_COMMAND_DEVINFO; - dev->mq->length = 0; - maple_add_packet(dev->mq); - } - } - } + /*check if we have actually tested all ports yet */ + if (!fullscan) + maple_port_rescan(); + /* mark that we have been through the first scan */ if (started == 0) started = 1; } @@ -631,7 +733,7 @@ static int match_maple_bus_driver(struct device *devptr, if (maple_dev->devinfo.function == 0xFFFFFFFF) return 0; else if (maple_dev->devinfo.function & - be32_to_cpu(maple_drv->function)) + cpu_to_be32(maple_drv->function)) return 1; return 0; } @@ -713,6 +815,9 @@ static int __init maple_bus_init(void) if (!maple_queue_cache) goto cleanup_bothirqs; + INIT_LIST_HEAD(&maple_waitq); + INIT_LIST_HEAD(&maple_sentq); + /* setup maple ports */ for (i = 0; i < MAPLE_PORTS; i++) { checked[i] = false; @@ -723,9 +828,7 @@ static int __init maple_bus_init(void) maple_free_dev(mdev[i]); goto cleanup_cache; } - mdev[i]->mq->command = MAPLE_COMMAND_DEVINFO; - mdev[i]->mq->length = 0; - maple_add_packet(mdev[i]->mq); + maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL); subdevice_map[i] = 0; } diff --git a/include/linux/maple.h b/include/linux/maple.h index 523a286bb477..c853b1066018 100644 --- a/include/linux/maple.h +++ b/include/linux/maple.h @@ -2,6 +2,7 @@ #define __LINUX_MAPLE_H #include +#include extern struct bus_type maple_bus_type; @@ -33,6 +34,7 @@ struct mapleq { void *sendbuf, *recvbuf, *recvbufdcsp; unsigned char length; enum maple_code command; + struct mutex mutex; }; struct maple_devinfo { @@ -69,7 +71,9 @@ void maple_getcond_callback(struct maple_device *dev, unsigned long interval, unsigned long function); int maple_driver_register(struct device_driver *drv); -void maple_add_packet(struct mapleq *mq); +int maple_add_packet_sleeps(struct maple_device *mdev, u32 function, + u32 command, u32 length, void *data); +void maple_clear_dev(struct maple_device *mdev); #define to_maple_dev(n) container_of(n, struct maple_device, dev) #define to_maple_driver(n) container_of(n, struct maple_driver, drv) -- cgit v1.2.3 From f1b23361a0f15497d4c6795a2935b2e98064ddfb Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 21:18:19 -0300 Subject: rfkill: document the rfkill struct locking (v2) Reorder fields in struct rfkill and add comments to make it clear which fields are protected by rfkill->mutex. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- include/linux/rfkill.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index c5f6e54ec6ae..741d1a62cc3f 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -68,7 +68,8 @@ enum rfkill_state { * @user_claim_unsupported: Whether the hardware supports exclusive * RF-kill control by userspace. Set this before registering. * @user_claim: Set when the switch is controlled exlusively by userspace. - * @mutex: Guards switch state transitions + * @mutex: Guards switch state transitions. It serializes callbacks + * and also protects the state. * @data: Pointer to the RF button drivers private data which will be * passed along when toggling radio state. * @toggle_radio(): Mandatory handler to control state of the radio. @@ -89,12 +90,13 @@ struct rfkill { const char *name; enum rfkill_type type; - enum rfkill_state state; bool user_claim_unsupported; bool user_claim; + /* the mutex serializes callbacks and also protects + * the state */ struct mutex mutex; - + enum rfkill_state state; void *data; int (*toggle_radio)(void *data, enum rfkill_state state); int (*get_state)(void *data, enum rfkill_state *state); -- cgit v1.2.3 From d0f09804144fd9471a13cf4d80e66842c7fa114f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jul 2008 11:32:07 +0200 Subject: mac80211: partially fix skb->cb use This patch fixes mac80211 to not use the skb->cb over the queue step from virtual interfaces to the master. The patch also, for now, disables aggregation because that would still require requeuing, will fix that in a separate patch. There are two other places (software requeue and powersaving stations) where requeue can happen, but that is not currently used by any drivers/not possible to use respectively. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 2 +- drivers/net/wireless/b43/xmit.c | 2 +- drivers/net/wireless/b43legacy/xmit.c | 2 +- drivers/net/wireless/iwlwifi/iwl-tx.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- include/linux/skbuff.h | 5 ++- include/net/mac80211.h | 6 ---- net/core/skbuff.c | 3 ++ net/mac80211/main.c | 8 +---- net/mac80211/mlme.c | 8 ++--- net/mac80211/tx.c | 47 +++++++++++++---------------- net/mac80211/wme.c | 3 ++ 13 files changed, 40 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 1106d1c06298..ff3fad794b61 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1237,7 +1237,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) pktlen = skb->len; - if (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) { + if (info->control.hw_key) { keyidx = info->control.hw_key->hw_key_idx; pktlen += info->control.icv_len; } diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 8d54502222a6..9dda8169f7cc 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -192,7 +192,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, const struct b43_phy *phy = &dev->phy; const struct ieee80211_hdr *wlhdr = (const struct ieee80211_hdr *)fragment_data; - int use_encryption = (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)); + int use_encryption = !!info->control.hw_key; __le16 fctl = wlhdr->frame_control; struct ieee80211_rate *fbrate; u8 rate, rate_fb; diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index e969ed8d412d..68e1f8c78727 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -192,7 +192,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, u16 cookie) { const struct ieee80211_hdr *wlhdr; - int use_encryption = (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)); + int use_encryption = !!info->control.hw_key; u16 fctl; u8 rate; struct ieee80211_rate *rate_fb; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 9b50b1052b09..f72cd0bf6aa3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -906,7 +906,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) * first entry */ iwl_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); - if (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) + if (info->control.hw_key) iwl_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id); /* Set up TFD's 2nd entry to point directly to remainder of skb, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 05121f395c4e..7c82ecfa30a4 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2667,7 +2667,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) * first entry */ iwl3945_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); - if (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) + if (info->control.hw_key) iwl3945_build_tx_cmd_hwcrypto(priv, info, out_cmd, skb, 0); /* Set up TFD's 2nd entry to point directly to remainder of skb, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 042ab00d8bd2..c3ee4ecba792 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -63,7 +63,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, */ memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); rts_info = IEEE80211_SKB_CB(skb); - rts_info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; + rts_info->control.hw_key = NULL; rts_info->flags &= ~IEEE80211_TX_CTL_USE_RTS_CTS; rts_info->flags &= ~IEEE80211_TX_CTL_USE_CTS_PROTECT; rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 7ea44f6621f2..a640385e0598 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -316,7 +316,10 @@ struct sk_buff { #ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2; #endif - /* 14 bit hole */ +#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE) + __u8 do_not_encrypt:1; +#endif + /* 0/13/14 bit hole */ #ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 74487f268237..b52721008be8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -206,8 +206,6 @@ struct ieee80211_bss_conf { * These flags are used with the @flags member of &ieee80211_tx_info. * * @IEEE80211_TX_CTL_REQ_TX_STATUS: request TX status callback for this frame. - * @IEEE80211_TX_CTL_DO_NOT_ENCRYPT: send this frame without encryption; - * e.g., for EAPOL frame * @IEEE80211_TX_CTL_USE_RTS_CTS: use RTS-CTS before sending frame * @IEEE80211_TX_CTL_USE_CTS_PROTECT: use CTS protection for the frame (e.g., * for combined 802.11g / 802.11b networks) @@ -220,7 +218,6 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_SHORT_PREAMBLE: TBD * @IEEE80211_TX_CTL_LONG_RETRY_LIMIT: this frame should be send using the * through set_retry_limit configured long retry value - * @IEEE80211_TX_CTL_EAPOL_FRAME: internal to mac80211 * @IEEE80211_TX_CTL_SEND_AFTER_DTIM: send this frame after DTIM beacon * @IEEE80211_TX_CTL_AMPDU: this frame should be sent as part of an A-MPDU * @IEEE80211_TX_CTL_OFDM_HT: this frame can be sent in HT OFDM rates. number @@ -253,7 +250,6 @@ struct ieee80211_bss_conf { */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), - IEEE80211_TX_CTL_DO_NOT_ENCRYPT = BIT(1), IEEE80211_TX_CTL_USE_RTS_CTS = BIT(2), IEEE80211_TX_CTL_USE_CTS_PROTECT = BIT(3), IEEE80211_TX_CTL_NO_ACK = BIT(4), @@ -263,7 +259,6 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(8), IEEE80211_TX_CTL_SHORT_PREAMBLE = BIT(9), IEEE80211_TX_CTL_LONG_RETRY_LIMIT = BIT(10), - IEEE80211_TX_CTL_EAPOL_FRAME = BIT(11), IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(12), IEEE80211_TX_CTL_AMPDU = BIT(13), IEEE80211_TX_CTL_OFDM_HT = BIT(14), @@ -323,7 +318,6 @@ struct ieee80211_tx_info { struct ieee80211_vif *vif; struct ieee80211_key_conf *hw_key; unsigned long jiffies; - int ifindex; u16 aid; s8 rts_cts_rate_idx, alt_retry_rate_idx; u8 retry_limit; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4e0c92274189..84640172d65d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -485,6 +485,9 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb) C(head); C(data); C(truesize); +#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE) + C(do_not_encrypt); +#endif atomic_set(&n->users, 1); atomic_inc(&(skb_shinfo(skb)->dataref)); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b5830f7055cf..a4c5b90de769 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1233,18 +1233,12 @@ static void ieee80211_tasklet_handler(unsigned long data) /* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to * make a prepared TX frame (one that has been given to hw) to look like brand * new IEEE 802.11 frame that is ready to go through TX processing again. - * Also, tx_packet_data in cb is restored from tx_control. */ + */ static void ieee80211_remove_tx_extra(struct ieee80211_local *local, struct ieee80211_key *key, struct sk_buff *skb) { int hdrlen, iv_len, mic_len; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - info->flags &= IEEE80211_TX_CTL_REQ_TX_STATUS | - IEEE80211_TX_CTL_DO_NOT_ENCRYPT | - IEEE80211_TX_CTL_REQUEUE | - IEEE80211_TX_CTL_EAPOL_FRAME; hdrlen = ieee80211_get_hdrlen_from_skb(skb); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d7c371e36bf0..35eb767cbcbe 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -606,7 +606,6 @@ void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, int encrypt) { struct ieee80211_sub_if_data *sdata; - struct ieee80211_tx_info *info; sdata = IEEE80211_DEV_TO_SUB_IF(dev); skb->dev = sdata->local->mdev; @@ -614,11 +613,8 @@ void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(struct ieee80211_tx_info)); - info->control.ifindex = sdata->dev->ifindex; - if (!encrypt) - info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; + skb->iif = sdata->dev->ifindex; + skb->do_not_encrypt = !encrypt; dev_queue_xmit(skb); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c5f78059c6ca..69019e943873 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -439,14 +439,14 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); u16 fc = tx->fc; - if (unlikely(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) + if (unlikely(tx->skb->do_not_encrypt)) tx->key = NULL; else if (tx->sta && (key = rcu_dereference(tx->sta->key))) tx->key = key; else if ((key = rcu_dereference(tx->sdata->default_key))) tx->key = key; else if (tx->sdata->drop_unencrypted && - !(info->flags & IEEE80211_TX_CTL_EAPOL_FRAME) && + (tx->skb->protocol != cpu_to_be16(ETH_P_PAE)) && !(info->flags & IEEE80211_TX_CTL_INJECTED)) { I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); return TX_DROP; @@ -476,7 +476,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) } if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) - info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; + tx->skb->do_not_encrypt = 1; return TX_CONTINUE; } @@ -732,6 +732,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) memcpy(skb_put(frag, copylen), pos, copylen); memcpy(frag->cb, first->cb, sizeof(frag->cb)); skb_copy_queue_mapping(frag, first); + frag->do_not_encrypt = first->do_not_encrypt; pos += copylen; left -= copylen; @@ -852,7 +853,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, sband = tx->local->hw.wiphy->bands[tx->channel->band]; - info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; + skb->do_not_encrypt = 1; info->flags |= IEEE80211_TX_CTL_INJECTED; tx->flags &= ~IEEE80211_TX_FRAGMENTED; @@ -925,8 +926,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, skb_trim(skb, skb->len - FCS_LEN); } if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) - info->flags &= - ~IEEE80211_TX_CTL_DO_NOT_ENCRYPT; + tx->skb->do_not_encrypt = 0; if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) tx->flags |= IEEE80211_TX_FRAGMENTED; break; @@ -1042,10 +1042,9 @@ static int ieee80211_tx_prepare(struct ieee80211_tx_data *tx, struct sk_buff *skb, struct net_device *mdev) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct net_device *dev; - dev = dev_get_by_index(&init_net, info->control.ifindex); + dev = dev_get_by_index(&init_net, skb->iif); if (unlikely(dev && !is_ieee80211_device(dev, mdev))) { dev_put(dev); dev = NULL; @@ -1306,8 +1305,8 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, bool may_encrypt; int ret; - if (info->control.ifindex) - odev = dev_get_by_index(&init_net, info->control.ifindex); + if (skb->iif) + odev = dev_get_by_index(&init_net, skb->iif); if (unlikely(odev && !is_ieee80211_device(odev, dev))) { dev_put(odev); odev = NULL; @@ -1321,9 +1320,13 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, return 0; } + memset(info, 0, sizeof(*info)); + + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + osdata = IEEE80211_DEV_TO_SUB_IF(odev); - may_encrypt = !(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT); + may_encrypt = !skb->do_not_encrypt; headroom = osdata->local->tx_headroom; if (may_encrypt) @@ -1348,7 +1351,6 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; u16 len_rthdr; @@ -1371,11 +1373,11 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, skb->dev = local->mdev; /* needed because we set skb device to master */ - info->control.ifindex = dev->ifindex; + skb->iif = dev->ifindex; - info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; - /* Interfaces should always request a status report */ - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + /* sometimes we do encrypt injected frames, will be fixed + * up in radiotap parser if not wanted */ + skb->do_not_encrypt = 0; /* * fix up the pointers accounting for the radiotap @@ -1419,7 +1421,6 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata; int ret = 1, head_need; u16 ethertype, hdrlen, meshhdrlen = 0; @@ -1645,14 +1646,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, nh_pos += hdrlen; h_pos += hdrlen; - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - info->control.ifindex = dev->ifindex; - if (ethertype == ETH_P_PAE) - info->flags |= IEEE80211_TX_CTL_EAPOL_FRAME; - - /* Interfaces should always request a status report */ - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + skb->iif = dev->ifindex; skb->dev = local->mdev; dev->stats.tx_packets++; @@ -1922,6 +1916,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); + skb->do_not_encrypt = 1; + info->band = band; rate_control_get_rate(local->mdev, sband, skb, &rsel); @@ -1940,7 +1936,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, info->tx_rate_idx = rsel.rate_idx; info->flags |= IEEE80211_TX_CTL_NO_ACK; - info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; if (sdata->bss_conf.use_short_preamble && diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 07edda0b8a5c..28437f0001db 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -188,6 +188,9 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, { int i; + /* XXX: currently broken due to cb/requeue use */ + return -EPERM; + /* prepare the filter and save it for the SW queue * matching the received HW queue */ -- cgit v1.2.3 From ce0ad7f0952581ba75ab6aee55bb1ed9bb22cf4f Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Wed, 30 Jul 2008 15:23:13 +1000 Subject: powerpc/mm: Lockless get_user_pages_fast() for 64-bit (v3) Implement lockless get_user_pages_fast for 64-bit powerpc. Page table existence is guaranteed with RCU, and speculative page references are used to take a reference to the pages without having a prior existence guarantee on them. Signed-off-by: Nick Piggin Signed-off-by: Dave Kleikamp Signed-off-by: Andrew Morton Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 3 + arch/powerpc/mm/Makefile | 3 +- arch/powerpc/mm/gup.c | 280 ++++++++++++++++++++++++++++++++++++ include/asm-powerpc/pgtable-ppc64.h | 2 + include/linux/pagemap.h | 23 +++ 5 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/mm/gup.c (limited to 'include/linux') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 587da5e0990f..63c9cafda9c4 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -42,6 +42,9 @@ config GENERIC_HARDIRQS bool default y +config HAVE_GET_USER_PAGES_FAST + def_bool PPC64 + config HAVE_SETUP_PER_CPU_AREA def_bool PPC64 diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index 1c00e0196f6c..e7392b45a5ef 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -12,7 +12,8 @@ obj-y := fault.o mem.o \ mmu_context_$(CONFIG_WORD_SIZE).o hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o obj-$(CONFIG_PPC64) += hash_utils_64.o \ - slb_low.o slb.o stab.o mmap.o $(hash-y) + slb_low.o slb.o stab.o \ + gup.o mmap.o $(hash-y) obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o obj-$(CONFIG_PPC_STD_MMU) += hash_low_$(CONFIG_WORD_SIZE).o \ tlb_$(CONFIG_WORD_SIZE).o diff --git a/arch/powerpc/mm/gup.c b/arch/powerpc/mm/gup.c new file mode 100644 index 000000000000..9fdf4d6335e4 --- /dev/null +++ b/arch/powerpc/mm/gup.c @@ -0,0 +1,280 @@ +/* + * Lockless get_user_pages_fast for powerpc + * + * Copyright (C) 2008 Nick Piggin + * Copyright (C) 2008 Novell Inc. + */ +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include + +/* + * The performance critical leaf functions are made noinline otherwise gcc + * inlines everything into a single function which results in too much + * register pressure. + */ +static noinline int gup_pte_range(pmd_t pmd, unsigned long addr, + unsigned long end, int write, struct page **pages, int *nr) +{ + unsigned long mask, result; + pte_t *ptep; + + result = _PAGE_PRESENT|_PAGE_USER; + if (write) + result |= _PAGE_RW; + mask = result | _PAGE_SPECIAL; + + ptep = pte_offset_kernel(&pmd, addr); + do { + pte_t pte = *ptep; + struct page *page; + + if ((pte_val(pte) & mask) != result) + return 0; + VM_BUG_ON(!pfn_valid(pte_pfn(pte))); + page = pte_page(pte); + if (!page_cache_get_speculative(page)) + return 0; + if (unlikely(pte != *ptep)) { + put_page(page); + return 0; + } + pages[*nr] = page; + (*nr)++; + + } while (ptep++, addr += PAGE_SIZE, addr != end); + + return 1; +} + +#ifdef CONFIG_HUGETLB_PAGE +static noinline int gup_huge_pte(pte_t *ptep, struct hstate *hstate, + unsigned long *addr, unsigned long end, + int write, struct page **pages, int *nr) +{ + unsigned long mask; + unsigned long pte_end; + struct page *head, *page; + pte_t pte; + int refs; + + pte_end = (*addr + huge_page_size(hstate)) & huge_page_mask(hstate); + if (pte_end < end) + end = pte_end; + + pte = *ptep; + mask = _PAGE_PRESENT|_PAGE_USER; + if (write) + mask |= _PAGE_RW; + if ((pte_val(pte) & mask) != mask) + return 0; + /* hugepages are never "special" */ + VM_BUG_ON(!pfn_valid(pte_pfn(pte))); + + refs = 0; + head = pte_page(pte); + page = head + ((*addr & ~huge_page_mask(hstate)) >> PAGE_SHIFT); + do { + VM_BUG_ON(compound_head(page) != head); + pages[*nr] = page; + (*nr)++; + page++; + refs++; + } while (*addr += PAGE_SIZE, *addr != end); + + if (!page_cache_add_speculative(head, refs)) { + *nr -= refs; + return 0; + } + if (unlikely(pte != *ptep)) { + /* Could be optimized better */ + while (*nr) { + put_page(page); + (*nr)--; + } + } + + return 1; +} +#endif /* CONFIG_HUGETLB_PAGE */ + +static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end, + int write, struct page **pages, int *nr) +{ + unsigned long next; + pmd_t *pmdp; + + pmdp = pmd_offset(&pud, addr); + do { + pmd_t pmd = *pmdp; + + next = pmd_addr_end(addr, end); + if (pmd_none(pmd)) + return 0; + if (!gup_pte_range(pmd, addr, next, write, pages, nr)) + return 0; + } while (pmdp++, addr = next, addr != end); + + return 1; +} + +static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end, + int write, struct page **pages, int *nr) +{ + unsigned long next; + pud_t *pudp; + + pudp = pud_offset(&pgd, addr); + do { + pud_t pud = *pudp; + + next = pud_addr_end(addr, end); + if (pud_none(pud)) + return 0; + if (!gup_pmd_range(pud, addr, next, write, pages, nr)) + return 0; + } while (pudp++, addr = next, addr != end); + + return 1; +} + +int get_user_pages_fast(unsigned long start, int nr_pages, int write, + struct page **pages) +{ + struct mm_struct *mm = current->mm; + unsigned long addr, len, end; + unsigned long next; + pgd_t *pgdp; + int psize, nr = 0; + unsigned int shift; + + pr_debug("%s(%lx,%x,%s)\n", __func__, start, nr_pages, write ? "write" : "read"); + + start &= PAGE_MASK; + addr = start; + len = (unsigned long) nr_pages << PAGE_SHIFT; + end = start + len; + + if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ, + start, len))) + goto slow_irqon; + + pr_debug(" aligned: %lx .. %lx\n", start, end); + +#ifdef CONFIG_HUGETLB_PAGE + /* We bail out on slice boundary crossing when hugetlb is + * enabled in order to not have to deal with two different + * page table formats + */ + if (addr < SLICE_LOW_TOP) { + if (end > SLICE_LOW_TOP) + goto slow_irqon; + + if (unlikely(GET_LOW_SLICE_INDEX(addr) != + GET_LOW_SLICE_INDEX(end - 1))) + goto slow_irqon; + } else { + if (unlikely(GET_HIGH_SLICE_INDEX(addr) != + GET_HIGH_SLICE_INDEX(end - 1))) + goto slow_irqon; + } +#endif /* CONFIG_HUGETLB_PAGE */ + + /* + * XXX: batch / limit 'nr', to avoid large irq off latency + * needs some instrumenting to determine the common sizes used by + * important workloads (eg. DB2), and whether limiting the batch size + * will decrease performance. + * + * It seems like we're in the clear for the moment. Direct-IO is + * the main guy that batches up lots of get_user_pages, and even + * they are limited to 64-at-a-time which is not so many. + */ + /* + * This doesn't prevent pagetable teardown, but does prevent + * the pagetables from being freed on powerpc. + * + * So long as we atomically load page table pointers versus teardown, + * we can follow the address down to the the page and take a ref on it. + */ + local_irq_disable(); + + psize = get_slice_psize(mm, addr); + shift = mmu_psize_defs[psize].shift; + +#ifdef CONFIG_HUGETLB_PAGE + if (unlikely(mmu_huge_psizes[psize])) { + pte_t *ptep; + unsigned long a = addr; + unsigned long sz = ((1UL) << shift); + struct hstate *hstate = size_to_hstate(sz); + + BUG_ON(!hstate); + /* + * XXX: could be optimized to avoid hstate + * lookup entirely (just use shift) + */ + + do { + VM_BUG_ON(shift != mmu_psize_defs[get_slice_psize(mm, a)].shift); + ptep = huge_pte_offset(mm, a); + pr_debug(" %016lx: huge ptep %p\n", a, ptep); + if (!ptep || !gup_huge_pte(ptep, hstate, &a, end, write, pages, + &nr)) + goto slow; + } while (a != end); + } else +#endif /* CONFIG_HUGETLB_PAGE */ + { + pgdp = pgd_offset(mm, addr); + do { + pgd_t pgd = *pgdp; + + VM_BUG_ON(shift != mmu_psize_defs[get_slice_psize(mm, addr)].shift); + pr_debug(" %016lx: normal pgd %p\n", addr, (void *)pgd); + next = pgd_addr_end(addr, end); + if (pgd_none(pgd)) + goto slow; + if (!gup_pud_range(pgd, addr, next, write, pages, &nr)) + goto slow; + } while (pgdp++, addr = next, addr != end); + } + local_irq_enable(); + + VM_BUG_ON(nr != (end - start) >> PAGE_SHIFT); + return nr; + + { + int ret; + +slow: + local_irq_enable(); +slow_irqon: + pr_debug(" slow path ! nr = %d\n", nr); + + /* Try to get the remaining pages with get_user_pages */ + start += nr << PAGE_SHIFT; + pages += nr; + + down_read(&mm->mmap_sem); + ret = get_user_pages(current, mm, start, + (end - start) >> PAGE_SHIFT, write, 0, pages, NULL); + up_read(&mm->mmap_sem); + + /* Have to be a bit careful with return values */ + if (nr > 0) { + if (ret < 0) + ret = nr; + else + ret += nr; + } + + return ret; + } +} diff --git a/include/asm-powerpc/pgtable-ppc64.h b/include/asm-powerpc/pgtable-ppc64.h index 5fc78c0be302..74c6f380b805 100644 --- a/include/asm-powerpc/pgtable-ppc64.h +++ b/include/asm-powerpc/pgtable-ppc64.h @@ -461,6 +461,8 @@ void pgtable_cache_init(void); return pt; } +pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long address); + #endif /* __ASSEMBLY__ */ #endif /* _ASM_POWERPC_PGTABLE_PPC64_H_ */ diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index a39b38ccdc97..69ed3cb1197a 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -143,6 +143,29 @@ static inline int page_cache_get_speculative(struct page *page) return 1; } +/* + * Same as above, but add instead of inc (could just be merged) + */ +static inline int page_cache_add_speculative(struct page *page, int count) +{ + VM_BUG_ON(in_interrupt()); + +#if !defined(CONFIG_SMP) && defined(CONFIG_CLASSIC_RCU) +# ifdef CONFIG_PREEMPT + VM_BUG_ON(!in_atomic()); +# endif + VM_BUG_ON(page_count(page) == 0); + atomic_add(count, &page->_count); + +#else + if (unlikely(!atomic_add_unless(&page->_count, count, 0))) + return 0; +#endif + VM_BUG_ON(PageCompound(page) && page != compound_head(page)); + + return 1; +} + static inline int page_freeze_refs(struct page *page, int count) { return likely(atomic_cmpxchg(&page->_count, count, 0) == count); -- cgit v1.2.3 From e2ce4eaa76214f65a3f328ec5b45c30248115768 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:10:07 +0100 Subject: regulator: consumer device interface Add support to allow consumer device drivers to control their regulator power supply. This uses a similar API to the kernel clock interface in that consumer drivers can get and put a regulator (like they can with clocks atm) and get/set voltage, current limit, mode, enable and disable. This should allow consumers complete control over their supply voltage and current limit. This also compiles out if not in use so drivers can be reused in systems with no regulator based power control. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 284 +++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 include/linux/regulator/consumer.h (limited to 'include/linux') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h new file mode 100644 index 000000000000..afdc4558bb94 --- /dev/null +++ b/include/linux/regulator/consumer.h @@ -0,0 +1,284 @@ +/* + * consumer.h -- SoC Regulator consumer support. + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + * Regulator Consumer Interface. + * + * A Power Management Regulator framework for SoC based devices. + * Features:- + * o Voltage and current level control. + * o Operating mode control. + * o Regulator status. + * o sysfs entries for showing client devices and status + * + * EXPERIMENTAL FEATURES: + * Dynamic Regulator operating Mode Switching (DRMS) - allows regulators + * to use most efficient operating mode depending upon voltage and load and + * is transparent to client drivers. + * + * e.g. Devices x,y,z share regulator r. Device x and y draw 20mA each during + * IO and 1mA at idle. Device z draws 100mA when under load and 5mA when + * idling. Regulator r has > 90% efficiency in NORMAL mode at loads > 100mA + * but this drops rapidly to 60% when below 100mA. Regulator r has > 90% + * efficiency in IDLE mode at loads < 10mA. Thus regulator r will operate + * in normal mode for loads > 10mA and in IDLE mode for load <= 10mA. + * + */ + +#ifndef __LINUX_REGULATOR_CONSUMER_H_ +#define __LINUX_REGULATOR_CONSUMER_H_ + +/* + * Regulator operating modes. + * + * Regulators can run in a variety of different operating modes depending on + * output load. This allows further system power savings by selecting the + * best (and most efficient) regulator mode for a desired load. + * + * Most drivers will only care about NORMAL. The modes below are generic and + * will probably not match the naming convention of your regulator data sheet + * but should match the use cases in the datasheet. + * + * In order of power efficiency (least efficient at top). + * + * Mode Description + * FAST Regulator can handle fast changes in it's load. + * e.g. useful in CPU voltage & frequency scaling where + * load can quickly increase with CPU frequency increases. + * + * NORMAL Normal regulator power supply mode. Most drivers will + * use this mode. + * + * IDLE Regulator runs in a more efficient mode for light + * loads. Can be used for devices that have a low power + * requirement during periods of inactivity. This mode + * may be more noisy than NORMAL and may not be able + * to handle fast load switching. + * + * STANDBY Regulator runs in the most efficient mode for very + * light loads. Can be used by devices when they are + * in a sleep/standby state. This mode is likely to be + * the most noisy and may not be able to handle fast load + * switching. + * + * NOTE: Most regulators will only support a subset of these modes. Some + * will only just support NORMAL. + * + * These modes can be OR'ed together to make up a mask of valid register modes. + */ + +#define REGULATOR_MODE_FAST 0x1 +#define REGULATOR_MODE_NORMAL 0x2 +#define REGULATOR_MODE_IDLE 0x4 +#define REGULATOR_MODE_STANDBY 0x8 + +/* + * Regulator notifier events. + * + * UNDER_VOLTAGE Regulator output is under voltage. + * OVER_CURRENT Regulator output current is too high. + * REGULATION_OUT Regulator output is out of regulation. + * FAIL Regulator output has failed. + * OVER_TEMP Regulator over temp. + * FORCE_DISABLE Regulator shut down by software. + * + * NOTE: These events can be OR'ed together when passed into handler. + */ + +#define REGULATOR_EVENT_UNDER_VOLTAGE 0x01 +#define REGULATOR_EVENT_OVER_CURRENT 0x02 +#define REGULATOR_EVENT_REGULATION_OUT 0x04 +#define REGULATOR_EVENT_FAIL 0x08 +#define REGULATOR_EVENT_OVER_TEMP 0x10 +#define REGULATOR_EVENT_FORCE_DISABLE 0x20 + +struct regulator; + +/** + * struct regulator_bulk_data - Data used for bulk regulator operations. + * + * @supply The name of the supply. Initialised by the user before + * using the bulk regulator APIs. + * @consumer The regulator consumer for the supply. This will be managed + * by the bulk API. + * + * The regulator APIs provide a series of regulator_bulk_() API calls as + * a convenience to consumers which require multiple supplies. This + * structure is used to manage data for these calls. + */ +struct regulator_bulk_data { + const char *supply; + struct regulator *consumer; +}; + +#if defined(CONFIG_REGULATOR) + +/* regulator get and put */ +struct regulator *__must_check regulator_get(struct device *dev, + const char *id); +void regulator_put(struct regulator *regulator); + +/* regulator output control and status */ +int regulator_enable(struct regulator *regulator); +int regulator_disable(struct regulator *regulator); +int regulator_force_disable(struct regulator *regulator); +int regulator_is_enabled(struct regulator *regulator); + +int regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); +int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers); +int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers); +void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers); + +int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); +int regulator_get_voltage(struct regulator *regulator); +int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA); +int regulator_get_current_limit(struct regulator *regulator); + +int regulator_set_mode(struct regulator *regulator, unsigned int mode); +unsigned int regulator_get_mode(struct regulator *regulator); +int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); + +/* regulator notifier block */ +int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb); +int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb); + +/* driver data - core doesn't touch */ +void *regulator_get_drvdata(struct regulator *regulator); +void regulator_set_drvdata(struct regulator *regulator, void *data); + +#else + +/* + * Make sure client drivers will still build on systems with no software + * controllable voltage or current regulators. + */ +static inline struct regulator *__must_check regulator_get(struct device *dev, + const char *id) +{ + /* Nothing except the stubbed out regulator API should be + * looking at the value except to check if it is an error + * value so the actual return value doesn't matter. + */ + return (struct regulator *)id; +} +static inline void regulator_put(struct regulator *regulator) +{ +} + +static inline int regulator_enable(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_disable(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_is_enabled(struct regulator *regulator) +{ + return 1; +} + +static inline int regulator_bulk_get(struct device *dev, + int num_consumers, + struct regulator_bulk_data *consumers) +{ + return 0; +} + +static inline int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + return 0; +} + +static inline int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + return 0; +} + +static inline void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers) +{ +} + +static inline int regulator_set_voltage(struct regulator *regulator, + int min_uV, int max_uV) +{ + return 0; +} + +static inline int regulator_get_voltage(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA) +{ + return 0; +} + +static inline int regulator_get_current_limit(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_set_mode(struct regulator *regulator, + unsigned int mode) +{ + return 0; +} + +static inline unsigned int regulator_get_mode(struct regulator *regulator) +{ + return REGULATOR_MODE_NORMAL; +} + +static inline int regulator_set_optimum_mode(struct regulator *regulator, + int load_uA) +{ + return REGULATOR_MODE_NORMAL; +} + +static inline int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return 0; +} + +static inline int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return 0; +} + +static inline void *regulator_get_drvdata(struct regulator *regulator) +{ + return NULL; +} + +static inline void regulator_set_drvdata(struct regulator *regulator, + void *data) +{ +} + +#endif + +#endif -- cgit v1.2.3 From 571a354b1542a274d88617e1f6703f3fe7a517f1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:42:28 +0100 Subject: regulator: regulator driver interface This allows regulator drivers to register their regulators and provide operations to the core. It also has a notifier call chain for propagating regulator events to clients. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 99 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 include/linux/regulator/driver.h (limited to 'include/linux') diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h new file mode 100644 index 000000000000..1d712c7172a2 --- /dev/null +++ b/include/linux/regulator/driver.h @@ -0,0 +1,99 @@ +/* + * driver.h -- SoC Regulator driver support. + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + * Regulator Driver Interface. + */ + +#ifndef __LINUX_REGULATOR_DRIVER_H_ +#define __LINUX_REGULATOR_DRIVER_H_ + +#include +#include + +struct regulator_constraints; +struct regulator_dev; + +/** + * struct regulator_ops - regulator operations. + * + * This struct describes regulator operations. + */ +struct regulator_ops { + + /* get/set regulator voltage */ + int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV); + int (*get_voltage) (struct regulator_dev *); + + /* get/set regulator current */ + int (*set_current_limit) (struct regulator_dev *, + int min_uA, int max_uA); + int (*get_current_limit) (struct regulator_dev *); + + /* enable/disable regulator */ + int (*enable) (struct regulator_dev *); + int (*disable) (struct regulator_dev *); + int (*is_enabled) (struct regulator_dev *); + + /* get/set regulator operating mode (defined in regulator.h) */ + int (*set_mode) (struct regulator_dev *, unsigned int mode); + unsigned int (*get_mode) (struct regulator_dev *); + + /* get most efficient regulator operating mode for load */ + unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, + int output_uV, int load_uA); + + /* the operations below are for configuration of regulator state when + * it's parent PMIC enters a global STANBY/HIBERNATE state */ + + /* set regulator suspend voltage */ + int (*set_suspend_voltage) (struct regulator_dev *, int uV); + + /* enable/disable regulator in suspend state */ + int (*set_suspend_enable) (struct regulator_dev *); + int (*set_suspend_disable) (struct regulator_dev *); + + /* set regulator suspend operating mode (defined in regulator.h) */ + int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode); +}; + +/* + * Regulators can either control voltage or current. + */ +enum regulator_type { + REGULATOR_VOLTAGE, + REGULATOR_CURRENT, +}; + +/** + * struct regulator_desc - Regulator descriptor + * + */ +struct regulator_desc { + const char *name; + int id; + struct regulator_ops *ops; + int irq; + enum regulator_type type; + struct module *owner; +}; + + +struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, + void *reg_data); +void regulator_unregister(struct regulator_dev *rdev); + +int regulator_notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data); + +void *rdev_get_drvdata(struct regulator_dev *rdev); +int rdev_get_id(struct regulator_dev *rdev); + +#endif -- cgit v1.2.3 From 4c1184e85cb381121a5273ea20ad31ca3faa0a4f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:46:09 +0100 Subject: regulator: machine driver interface This interface is for machine specific code and allows the creation of voltage/current domains (with constraints) for each regulator. It can provide regulator constraints that will prevent device damage through overvoltage or over current caused by buggy client drivers. It also allows the creation of a regulator tree whereby some regulators are supplied by others (similar to a clock tree). Signed-off-by: Liam Girdwood Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- include/linux/regulator/machine.h | 104 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 include/linux/regulator/machine.h (limited to 'include/linux') diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h new file mode 100644 index 000000000000..11e737dbfcf2 --- /dev/null +++ b/include/linux/regulator/machine.h @@ -0,0 +1,104 @@ +/* + * machine.h -- SoC Regulator support, machine/board driver API. + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + * Regulator Machine/Board Interface. + */ + +#ifndef __LINUX_REGULATOR_MACHINE_H_ +#define __LINUX_REGULATOR_MACHINE_H_ + +#include +#include + +struct regulator; + +/* + * Regulator operation constraint flags. These flags are used to enable + * certain regulator operations and can be OR'ed together. + * + * VOLTAGE: Regulator output voltage can be changed by software on this + * board/machine. + * CURRENT: Regulator output current can be changed by software on this + * board/machine. + * MODE: Regulator operating mode can be changed by software on this + * board/machine. + * STATUS: Regulator can be enabled and disabled. + * DRMS: Dynamic Regulator Mode Switching is enabled for this regulator. + */ + +#define REGULATOR_CHANGE_VOLTAGE 0x1 +#define REGULATOR_CHANGE_CURRENT 0x2 +#define REGULATOR_CHANGE_MODE 0x4 +#define REGULATOR_CHANGE_STATUS 0x8 +#define REGULATOR_CHANGE_DRMS 0x10 + +/** + * struct regulator_state - regulator state during low power syatem states + * + * This describes a regulators state during a system wide low power state. + */ +struct regulator_state { + int uV; /* suspend voltage */ + unsigned int mode; /* suspend regulator operating mode */ + int enabled; /* is regulator enabled in this suspend state */ +}; + +/** + * struct regulation_constraints - regulator operating constraints. + * + * This struct describes regulator and board/machine specific constraints. + */ +struct regulation_constraints { + + char *name; + + /* voltage output range (inclusive) - for voltage control */ + int min_uV; + int max_uV; + + /* current output range (inclusive) - for current control */ + int min_uA; + int max_uA; + + /* valid regulator operating modes for this machine */ + unsigned int valid_modes_mask; + + /* valid operations for regulator on this machine */ + unsigned int valid_ops_mask; + + /* regulator input voltage - only if supply is another regulator */ + int input_uV; + + /* regulator suspend states for global PMIC STANDBY/HIBERNATE */ + struct regulator_state state_disk; + struct regulator_state state_mem; + struct regulator_state state_standby; + suspend_state_t initial_state; /* suspend state to set at init */ + + /* constriant flags */ + unsigned always_on:1; /* regulator never off when system is on */ + unsigned boot_on:1; /* bootloader/firmware enabled regulator */ + unsigned apply_uV:1; /* apply uV constraint iff min == max */ +}; + +int regulator_set_supply(const char *regulator, const char *regulator_supply); + +const char *regulator_get_supply(const char *regulator); + +int regulator_set_machine_constraints(const char *regulator, + struct regulation_constraints *constraints); + +int regulator_set_device_supply(const char *regulator, struct device *dev, + const char *supply); + +int regulator_suspend_prepare(suspend_state_t state); + +#endif -- cgit v1.2.3 From 48d335ba3164ce99cb8847513d0e3b6ee604eb20 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Apr 2008 15:50:21 +0100 Subject: regulator: fixed regulator interface This patch adds support for fixed regulators. This class of regulator is not software controllable but can coexist on machines with software controlable regulators. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- include/linux/regulator/fixed.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/linux/regulator/fixed.h (limited to 'include/linux') diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h new file mode 100644 index 000000000000..1387a5d2190e --- /dev/null +++ b/include/linux/regulator/fixed.h @@ -0,0 +1,22 @@ +/* + * fixed.h + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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. + */ + +#ifndef __REGULATOR_FIXED_H +#define __REGULATOR_FIXED_H + +struct fixed_voltage_config { + const char *supply_name; + int microvolts; +}; + +#endif -- cgit v1.2.3 From 0eb5d5ab3ec99bfd22ff16797d95835369ffb25b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2008 17:28:06 +0200 Subject: regulator: TI bq24022 Li-Ion Charger driver This adds a regulator driver for the TI bq24022 Single-Chip Li-Ion Charger with its nCE and ISET2 pins connected to GPIOs. Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 10 +++ drivers/regulator/Makefile | 2 + drivers/regulator/bq24022.c | 167 ++++++++++++++++++++++++++++++++++++++ include/linux/regulator/bq24022.h | 21 +++++ 4 files changed, 200 insertions(+) create mode 100644 drivers/regulator/bq24022.c create mode 100644 include/linux/regulator/bq24022.h (limited to 'include/linux') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 84f89ecce69e..a656128f1fdd 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -46,4 +46,14 @@ config REGULATOR_VIRTUAL_CONSUMER If unsure, say no. +config REGULATOR_BQ24022 + tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" + default n + select REGULATOR + help + This driver controls a TI bq24022 Charger attached via + GPIOs. The provided current regulator can enable/disable + charging select between 100 mA and 500 mA charging current + limit. + endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 29528b78c8de..ac2c64efe65c 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -7,4 +7,6 @@ obj-$(CONFIG_REGULATOR) += core.o obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o +obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o + ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c new file mode 100644 index 000000000000..263699d6152d --- /dev/null +++ b/drivers/regulator/bq24022.c @@ -0,0 +1,167 @@ +/* + * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater) + * 1-Cell Li-Ion Charger connected via GPIOs. + * + * Copyright (c) 2008 Philipp Zabel + * + * 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 +#include +#include +#include +#include +#include +#include + +static int bq24022_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "setting current limit to %s mA\n", + max_uA >= 500000 ? "500" : "100"); + + /* REVISIT: maybe return error if min_uA != 0 ? */ + gpio_set_value(pdata->gpio_iset2, max_uA >= 500000); + return 0; +} + +static int bq24022_get_current_limit(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000; +} + +static int bq24022_enable(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "enabling charger\n"); + + gpio_set_value(pdata->gpio_nce, 0); + return 0; +} + +static int bq24022_disable(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "disabling charger\n"); + + gpio_set_value(pdata->gpio_nce, 1); + return 0; +} + +static int bq24022_is_enabled(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + return !gpio_get_value(pdata->gpio_nce); +} + +static struct regulator_ops bq24022_ops = { + .set_current_limit = bq24022_set_current_limit, + .get_current_limit = bq24022_get_current_limit, + .enable = bq24022_enable, + .disable = bq24022_disable, + .is_enabled = bq24022_is_enabled, +}; + +static struct regulator_desc bq24022_desc = { + .name = "bq24022", + .ops = &bq24022_ops, + .type = REGULATOR_CURRENT, +}; + +static int __init bq24022_probe(struct platform_device *pdev) +{ + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct regulator_dev *bq24022; + int ret; + + if (!pdata || !pdata->gpio_nce || !pdata->gpio_iset2) + return -EINVAL; + + ret = gpio_request(pdata->gpio_nce, "ncharge_en"); + if (ret) { + dev_dbg(&pdev->dev, "couldn't request nCE GPIO: %d\n", + pdata->gpio_nce); + goto err_ce; + } + ret = gpio_request(pdata->gpio_iset2, "charge_mode"); + if (ret) { + dev_dbg(&pdev->dev, "couldn't request ISET2 GPIO: %d\n", + pdata->gpio_iset2); + goto err_iset2; + } + ret = gpio_direction_output(pdata->gpio_iset2, 0); + ret = gpio_direction_output(pdata->gpio_nce, 1); + + bq24022 = regulator_register(&bq24022_desc, pdev); + if (IS_ERR(bq24022)) { + dev_dbg(&pdev->dev, "couldn't register regulator\n"); + ret = PTR_ERR(bq24022); + goto err_reg; + } + platform_set_drvdata(pdev, bq24022); + dev_dbg(&pdev->dev, "registered regulator\n"); + + return 0; +err_reg: + gpio_free(pdata->gpio_iset2); +err_iset2: + gpio_free(pdata->gpio_nce); +err_ce: + return ret; +} + +static int __devexit bq24022_remove(struct platform_device *pdev) +{ + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct regulator_dev *bq24022 = platform_get_drvdata(pdev); + + regulator_unregister(bq24022); + gpio_free(pdata->gpio_iset2); + gpio_free(pdata->gpio_nce); + + return 0; +} + +static struct platform_driver bq24022_driver = { + .driver = { + .name = "bq24022", + }, + .remove = __devexit_p(bq24022_remove), +}; + +static int __init bq24022_init(void) +{ + return platform_driver_probe(&bq24022_driver, bq24022_probe); +} + +static void __exit bq24022_exit(void) +{ + platform_driver_unregister(&bq24022_driver); +} + +/* + * make sure this is probed before gpio_vbus and pda_power, + * but after asic3 or other GPIO expander drivers. + */ +subsys_initcall(bq24022_init); +module_exit(bq24022_exit); + +MODULE_AUTHOR("Philipp Zabel"); +MODULE_DESCRIPTION("TI bq24022 Li-Ion Charger driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/bq24022.h b/include/linux/regulator/bq24022.h new file mode 100644 index 000000000000..e84b0a9feda5 --- /dev/null +++ b/include/linux/regulator/bq24022.h @@ -0,0 +1,21 @@ +/* + * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater) + * 1-Cell Li-Ion Charger connected via GPIOs. + * + * Copyright (c) 2008 Philipp Zabel + * + * 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. + * + */ + +/** + * bq24022_mach_info - platform data for bq24022 + * @gpio_nce: GPIO line connected to the nCE pin, used to enable / disable charging + * @gpio_iset2: GPIO line connected to the ISET2 pin, used to limit charging current to 100 mA / 500 mA + */ +struct bq24022_mach_info { + int gpio_nce; + int gpio_iset2; +}; -- cgit v1.2.3 From 785957d3e8c6fb37b18bf671923a76dbd8240025 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 30 Jul 2008 03:03:15 -0700 Subject: tcp: MD5: Use MIB counter instead of warning for MD5 mismatch. From a report by Matti Aarnio, and preliminary patch by Adam Langley. Signed-off-by: David S. Miller --- include/linux/snmp.h | 2 ++ net/ipv4/proc.c | 2 ++ net/ipv4/tcp_ipv4.c | 10 ++-------- net/ipv6/tcp_ipv6.c | 27 ++++++++------------------- 4 files changed, 14 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 5df62ef1280c..7a6e6bba4a71 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -214,6 +214,8 @@ enum LINUX_MIB_TCPDSACKIGNOREDOLD, /* TCPSACKIgnoredOld */ LINUX_MIB_TCPDSACKIGNOREDNOUNDO, /* TCPSACKIgnoredNoUndo */ LINUX_MIB_TCPSPURIOUSRTOS, /* TCPSpuriousRTOs */ + LINUX_MIB_TCPMD5NOTFOUND, /* TCPMD5NotFound */ + LINUX_MIB_TCPMD5UNEXPECTED, /* TCPMD5Unexpected */ __LINUX_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 834356ea99df..8f5a403f6f6b 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -232,6 +232,8 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPDSACKIgnoredOld", LINUX_MIB_TCPDSACKIGNOREDOLD), SNMP_MIB_ITEM("TCPDSACKIgnoredNoUndo", LINUX_MIB_TCPDSACKIGNOREDNOUNDO), SNMP_MIB_ITEM("TCPSpuriousRTOs", LINUX_MIB_TCPSPURIOUSRTOS), + SNMP_MIB_ITEM("TCPMD5NotFound", LINUX_MIB_TCPMD5NOTFOUND), + SNMP_MIB_ITEM("TCPMD5Unexpected", LINUX_MIB_TCPMD5UNEXPECTED), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a2b06d0cc26b..b3875c0d83c7 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1116,18 +1116,12 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb) return 0; if (hash_expected && !hash_location) { - LIMIT_NETDEBUG(KERN_INFO "MD5 Hash expected but NOT found " - "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)\n", - NIPQUAD(iph->saddr), ntohs(th->source), - NIPQUAD(iph->daddr), ntohs(th->dest)); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); return 1; } if (!hash_expected && hash_location) { - LIMIT_NETDEBUG(KERN_INFO "MD5 Hash NOT expected but found " - "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)\n", - NIPQUAD(iph->saddr), ntohs(th->source), - NIPQUAD(iph->daddr), ntohs(th->dest)); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); return 1; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index cff778b23a7f..1db45216b232 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -849,28 +849,17 @@ static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb) hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr); hash_location = tcp_parse_md5sig_option(th); - /* do we have a hash as expected? */ - if (!hash_expected) { - if (!hash_location) - return 0; - if (net_ratelimit()) { - printk(KERN_INFO "MD5 Hash NOT expected but found " - "(" NIP6_FMT ", %u)->" - "(" NIP6_FMT ", %u)\n", - NIP6(ip6h->saddr), ntohs(th->source), - NIP6(ip6h->daddr), ntohs(th->dest)); - } + /* We've parsed the options - do we have a hash? */ + if (!hash_expected && !hash_location) + return 0; + + if (hash_expected && !hash_location) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); return 1; } - if (!hash_location) { - if (net_ratelimit()) { - printk(KERN_INFO "MD5 Hash expected but NOT found " - "(" NIP6_FMT ", %u)->" - "(" NIP6_FMT ", %u)\n", - NIP6(ip6h->saddr), ntohs(th->source), - NIP6(ip6h->daddr), ntohs(th->dest)); - } + if (!hash_expected && hash_location) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); return 1; } -- cgit v1.2.3 From 96d8b647cfff90c8ff07863866aacdcd9d13cead Mon Sep 17 00:00:00 2001 From: Alexey Korolev Date: Tue, 29 Jul 2008 13:54:11 +0100 Subject: [MTD] [NAND] fix subpage read for small page NAND Current implementation of subpage read feature for NAND has issues with small page devices. Small page NAND do not support RNDOUT command. So subpage feature is not applicable for them. This patch disables support of subpage for small page NAND. The code is verified on nandsim(SP NAND simulation) and on LP NAND devices. Thanks a lot to Artem for finding this issue. Signed-off-by: Alexey Korolev Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- include/linux/mtd/nand.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 83f678702dff..81774e5facf4 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -177,7 +177,9 @@ typedef enum { #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) -#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT)) +/* Large page NAND with SOFT_ECC should support subpage reads */ +#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT) \ + && (chip->page_shift > 9)) /* Mask to zero out the chip options, which come from the id table */ #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) -- cgit v1.2.3 From 95b1bc20532c18e3f19cd460c8350350c84ffbb2 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 29 Jul 2008 22:28:12 -0700 Subject: [MTD] MTD_DEBUG always does compile-time typechecks The current style for debug messages is to ensure they're always parsed by the compiler and then subjected to dead code removal. That way builds won't break only when debug options get enabled, which is common when they are stripped out early by CPP. This patch makes CONFIG_MTD_DEBUG adopt that convention. Signed-off-by: David Brownell Signed-off-by: David Woodhouse --- include/linux/mtd/mtd.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4ed40caff4e5..922636548558 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -272,7 +272,11 @@ static inline void mtd_erase_callback(struct erase_info *instr) printk(KERN_INFO args); \ } while(0) #else /* CONFIG_MTD_DEBUG */ -#define DEBUG(n, args...) do { } while(0) +#define DEBUG(n, args...) \ + do { \ + if (0) \ + printk(KERN_INFO args); \ + } while(0) #endif /* CONFIG_MTD_DEBUG */ -- cgit v1.2.3 From 1a4e564b7db999fbe5d88318c96ac8747699d417 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 29 Jul 2008 22:32:57 -0700 Subject: resource: add resource_size() Avoid one-off errors by introducing a resource_size() function. Signed-off-by: Magnus Damm Cc: Ben Dooks Cc: Jean Delvare Cc: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ioport.h | 4 ++++ kernel/resource.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 2cd07cc29687..22d2115458c6 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -118,6 +118,10 @@ extern int allocate_resource(struct resource *root, struct resource *new, int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); resource_size_t resource_alignment(struct resource *res); +static inline resource_size_t resource_size(struct resource *res) +{ + return res->end - res->start + 1; +} /* Convenience shorthand with allocation */ #define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name)) diff --git a/kernel/resource.c b/kernel/resource.c index 74af2d7cb5a1..f5b518eabefe 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -490,7 +490,7 @@ resource_size_t resource_alignment(struct resource *res) { switch (res->flags & (IORESOURCE_SIZEALIGN | IORESOURCE_STARTALIGN)) { case IORESOURCE_SIZEALIGN: - return res->end - res->start + 1; + return resource_size(res); case IORESOURCE_STARTALIGN: return res->start; default: -- cgit v1.2.3 From a1531acd43310a7e4571d52e8846640667f4c74b Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 29 Jul 2008 22:32:58 -0700 Subject: cpufreq acpi: only call _PPC after cpufreq ACPI init funcs got called already Ingo Molnar provided a fix to not call _PPC at processor driver initialization time in "[PATCH] ACPI: fix cpufreq regression" (git commit e4233dec749a3519069d9390561b5636a75c7579) But it can still happen that _PPC is called at processor driver initialization time. This patch should make sure that this is not possible anymore. Signed-off-by: Thomas Renninger Cc: Andi Kleen Cc: Len Brown Cc: Dave Jones Cc: Ingo Molnar Cc: Venkatesh Pallipadi Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c | 6 ++++++ drivers/acpi/processor_perflib.c | 15 +++++++++++++-- drivers/cpufreq/cpufreq.c | 3 +++ include/linux/cpufreq.h | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c index 69288f653144..3233fe84d158 100644 --- a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c +++ b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c @@ -96,6 +96,12 @@ static int pmi_notifier(struct notifier_block *nb, struct cpufreq_frequency_table *cbe_freqs; u8 node; + /* Should this really be called for CPUFREQ_ADJUST, CPUFREQ_INCOMPATIBLE + * and CPUFREQ_NOTIFY policy events?) + */ + if (event == CPUFREQ_START) + return 0; + cbe_freqs = cpufreq_frequency_get_table(policy->cpu); node = cbe_cpu_to_node(policy->cpu); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index b4749969c6b4..e98071a64810 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -64,7 +64,13 @@ static DEFINE_MUTEX(performance_mutex); * policy is adjusted accordingly. */ -static unsigned int ignore_ppc = 0; +/* ignore_ppc: + * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet + * ignore _PPC + * 0 -> cpufreq low level drivers initialized -> consider _PPC values + * 1 -> ignore _PPC totally -> forced by user through boot param + */ +static unsigned int ignore_ppc = -1; module_param(ignore_ppc, uint, 0644); MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ "limited by BIOS, this should help"); @@ -72,7 +78,7 @@ MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ #define PPC_REGISTERED 1 #define PPC_IN_USE 2 -static int acpi_processor_ppc_status = 0; +static int acpi_processor_ppc_status; static int acpi_processor_ppc_notifier(struct notifier_block *nb, unsigned long event, void *data) @@ -81,6 +87,11 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb, struct acpi_processor *pr; unsigned int ppc = 0; + if (event == CPUFREQ_START && ignore_ppc <= 0) { + ignore_ppc = 0; + return 0; + } + if (ignore_ppc) return 0; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8d6a3ff02672..8a67f16987db 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -825,6 +825,9 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) policy->user_policy.min = policy->cpuinfo.min_freq; policy->user_policy.max = policy->cpuinfo.max_freq; + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_START, policy); + #ifdef CONFIG_SMP #ifdef CONFIG_HOTPLUG_CPU diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 2270ca5ec631..6fd5668aa572 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -106,6 +106,7 @@ struct cpufreq_policy { #define CPUFREQ_ADJUST (0) #define CPUFREQ_INCOMPATIBLE (1) #define CPUFREQ_NOTIFY (2) +#define CPUFREQ_START (3) #define CPUFREQ_SHARED_TYPE_NONE (0) /* None */ #define CPUFREQ_SHARED_TYPE_HW (1) /* HW does needed coordination */ -- cgit v1.2.3 From 1d1958f05095a7e9ecbba86235122784a3d1b561 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 29 Jul 2008 22:33:16 -0700 Subject: mm: remove find_max_pfn_with_active_regions It has no user now Also print out info about adding/removing active regions. Signed-off-by: Yinghai Lu Acked-by: Mel Gorman Acked-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 - mm/page_alloc.c | 17 ----------------- 2 files changed, 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 866a3dbe5c75..5e2c8af49998 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1041,7 +1041,6 @@ extern unsigned long absent_pages_in_range(unsigned long start_pfn, extern void get_pfn_range_for_nid(unsigned int nid, unsigned long *start_pfn, unsigned long *end_pfn); extern unsigned long find_min_pfn_with_active_regions(void); -extern unsigned long find_max_pfn_with_active_regions(void); extern void free_bootmem_with_active_regions(int nid, unsigned long max_low_pfn); typedef int (*work_fn_t)(unsigned long, unsigned long, void *); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 3cf3d05b6bd4..401d104d2bb6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3753,23 +3753,6 @@ unsigned long __init find_min_pfn_with_active_regions(void) return find_min_pfn_for_node(MAX_NUMNODES); } -/** - * find_max_pfn_with_active_regions - Find the maximum PFN registered - * - * It returns the maximum PFN based on information provided via - * add_active_range(). - */ -unsigned long __init find_max_pfn_with_active_regions(void) -{ - int i; - unsigned long max_pfn = 0; - - for (i = 0; i < nr_nodemap_entries; i++) - max_pfn = max(max_pfn, early_node_map[i].end_pfn); - - return max_pfn; -} - /* * early_calculate_totalpages() * Sum pages in active regions for movable zone. -- cgit v1.2.3 From 3f1712bac586069d6c891a8201457283b27e8abe Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Tue, 29 Jul 2008 22:33:32 -0700 Subject: print_ip_sym(): use %pS Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kallsyms.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index 57aefa160a92..b96144887444 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -108,8 +108,7 @@ static inline void print_fn_descriptor_symbol(const char *fmt, void *addr) static inline void print_ip_sym(unsigned long ip) { - printk("[<%p>]", (void *) ip); - print_symbol(" %s\n", ip); + printk("[<%p>] %pS\n", (void *) ip, (void *) ip); } #endif /*_LINUX_KALLSYMS_H*/ -- cgit v1.2.3 From 2c203003f64de5fe55ae35712942100d270667fa Mon Sep 17 00:00:00 2001 From: Jerome Arbez-Gindre Date: Tue, 29 Jul 2008 22:33:33 -0700 Subject: connector: add a BlackBoard user to connector Add a BlackBoard user to connector. BlackBoard is part of the TSP GPL sampling framework (http://savannah.nongnu.org/p/tsp) [akpm@linux-foundation.org: add comment] Signed-off-by: Jerome Arbez-Gindre Acked-by: Evgeniy Polyakov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/connector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/connector.h b/include/linux/connector.h index 96a89d3d6727..5c7f9468f753 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -38,8 +38,9 @@ #define CN_W1_VAL 0x1 #define CN_IDX_V86D 0x4 #define CN_VAL_V86D_UVESAFB 0x1 +#define CN_IDX_BB 0x5 /* BlackBoard, from the TSP GPL sampling framework */ -#define CN_NETLINK_USERS 5 +#define CN_NETLINK_USERS 6 /* * Maximum connector's message size. -- cgit v1.2.3 From 204b885e7322656284626949e51f292fe61313fa Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jul 2008 22:33:42 -0700 Subject: introduce lower_32_bits() macro The file kernel.h contains the upper_32_bits macro. This patch adds the other part, the lower_32_bits macro. Its first use will be in the driver for AMD IOMMU. Cc: H. Peter Anvin Signed-off-by: Joerg Roedel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index fdbbf72ca2eb..aaa998f65c7a 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -75,6 +75,12 @@ extern const char linux_proc_banner[]; */ #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) +/** + * lower_32_bits - return bits 0-31 of a number + * @n: the number we're accessing + */ +#define lower_32_bits(n) ((u32)(n)) + #define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ -- cgit v1.2.3 From c627f9cc046c7cd93b4525d89377fb409e170a18 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Tue, 29 Jul 2008 22:33:53 -0700 Subject: mm: add zap_vma_ptes(): a library function to unmap driver ptes zap_vma_ptes() is intended to be used by drivers to unmap ptes assigned to the driver private vmas. This interface is similar to zap_page_range() but is less general & less likely to be abused. Needed by the GRU driver. Signed-off-by: Jack Steiner Cc: Nick Piggin Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 2 ++ mm/memory.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 5e2c8af49998..335288bff1b7 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -744,6 +744,8 @@ struct zap_details { struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr, pte_t pte); +int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address, + unsigned long size); unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned long size, struct zap_details *); unsigned long unmap_vmas(struct mmu_gather **tlb, diff --git a/mm/memory.c b/mm/memory.c index 67f0ab9077d9..6793b9c68107 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -994,6 +994,29 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address, return end; } +/** + * zap_vma_ptes - remove ptes mapping the vma + * @vma: vm_area_struct holding ptes to be zapped + * @address: starting address of pages to zap + * @size: number of bytes to zap + * + * This function only unmaps ptes assigned to VM_PFNMAP vmas. + * + * The entire address range must be fully contained within the vma. + * + * Returns 0 if successful. + */ +int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address, + unsigned long size) +{ + if (address < vma->vm_start || address + size > vma->vm_end || + !(vma->vm_flags & VM_PFNMAP)) + return -1; + zap_page_range(vma, address, size, NULL); + return 0; +} +EXPORT_SYMBOL_GPL(zap_vma_ptes); + /* * Do a quick page-table lookup for a single page. */ -- cgit v1.2.3 From 3dd730f2b49f101b90d283c3efc4e6cd826dd8f6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 29 Jul 2008 16:07:37 +1000 Subject: cpumask: statement expressions confuse some versions of gcc when you take the address of the result. Noticed on a sparc64 compile using a version 3.4.5 cross compiler. kernel/time/tick-common.c: In function `tick_check_new_device': kernel/time/tick-common.c:210: error: invalid lvalue in unary `&' ... Just make it a regular expression. Signed-off-by: Stephen Rothwell Acked-by: Ingo Molnar Signed-off-by: Linus Torvalds --- include/linux/cpumask.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 96d0509fb8d8..d3219d73f8e6 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -287,7 +287,7 @@ static inline const cpumask_t *get_cpu_mask(unsigned int cpu) * gcc optimizes it out (it's a constant) and there's no huge stack * variable created: */ -#define cpumask_of_cpu(cpu) ({ *get_cpu_mask(cpu); }) +#define cpumask_of_cpu(cpu) (*get_cpu_mask(cpu)) #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) -- cgit v1.2.3 From 1f938d060a7bc01b5f82d46db3e38cd501b445a6 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Mon, 21 Jul 2008 00:06:19 +0400 Subject: libata.h: replace __FUNCTION__ with __func__ Signed-off-by: Alexander Beregalov Signed-off-by: Jeff Garzik --- include/linux/libata.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/libata.h b/include/linux/libata.h index 5b247b8a6b3b..d4b8e5fa3e8b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -60,9 +60,9 @@ /* note: prints function name for you */ #ifdef ATA_DEBUG -#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args) #ifdef ATA_VERBOSE_DEBUG -#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args) #else #define VPRINTK(fmt, args...) #endif /* ATA_VERBOSE_DEBUG */ @@ -71,7 +71,7 @@ #define VPRINTK(fmt, args...) #endif /* ATA_DEBUG */ -#define BPRINTK(fmt, args...) if (ap->flags & ATA_FLAG_DEBUGMSG) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#define BPRINTK(fmt, args...) if (ap->flags & ATA_FLAG_DEBUGMSG) printk(KERN_ERR "%s: " fmt, __func__, ## args) /* NEW: debug levels */ #define HAVE_LIBATA_MSG 1 -- cgit v1.2.3 From 963e4975c6f93c148ca809d986d412201df9af89 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 24 Jul 2008 17:16:06 +0100 Subject: pata_it821x: Driver updates and reworking - Add support for the RDC 1010 variant - Rework the core library to have a read_id method. This allows the hacky bits of it821x to go and prepares us for pata_hd - Switch from WARN to BUG in ata_id_string as it will reboot if you get it wrong so WARN won't be seen - Allow the issue of command 0xFC on the 821x. This is needed to query rebuild status. - Tidy up printk formatting - Do more ident rewriting on RAID volumes to handle firmware provided ident data which is rather wonky - Report the firmware revision and device layout in RAID mode - Don't try and disable raid on the 8211 or RDC - they don't have the relevant bits Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 31 +++++- drivers/ata/pata_it821x.c | 270 ++++++++++++++++++++++++++++++++++++++++------ include/linux/libata.h | 3 + 3 files changed, 265 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index f69d1548b562..5ba96c5052c8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1132,6 +1132,8 @@ void ata_id_string(const u16 *id, unsigned char *s, { unsigned int c; + BUG_ON(len & 1); + while (len > 0) { c = id[ofs] >> 8; *s = c; @@ -1165,8 +1167,6 @@ void ata_id_c_string(const u16 *id, unsigned char *s, { unsigned char *p; - WARN_ON(!(len & 1)); - ata_id_string(id, s, ofs, len - 1); p = s + strnlen(s, len - 1); @@ -1885,6 +1885,23 @@ static u32 ata_pio_mask_no_iordy(const struct ata_device *adev) return 3 << ATA_SHIFT_PIO; } +/** + * ata_do_dev_read_id - default ID read method + * @dev: device + * @tf: proposed taskfile + * @id: data buffer + * + * Issue the identify taskfile and hand back the buffer containing + * identify data. For some RAID controllers and for pre ATA devices + * this function is wrapped or replaced by the driver + */ +unsigned int ata_do_dev_read_id(struct ata_device *dev, + struct ata_taskfile *tf, u16 *id) +{ + return ata_exec_internal(dev, tf, NULL, DMA_FROM_DEVICE, + id, sizeof(id[0]) * ATA_ID_WORDS, 0); +} + /** * ata_dev_read_id - Read ID data from the specified device * @dev: target device @@ -1920,7 +1937,7 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, if (ata_msg_ctl(ap)) ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __func__); - retry: +retry: ata_tf_init(dev, &tf); switch (class) { @@ -1948,8 +1965,11 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, */ tf.flags |= ATA_TFLAG_POLLING; - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, - id, sizeof(id[0]) * ATA_ID_WORDS, 0); + if (ap->ops->read_id) + err_mask = ap->ops->read_id(dev, &tf, id); + else + err_mask = ata_do_dev_read_id(dev, &tf, id); + if (err_mask) { if (err_mask & AC_ERR_NODEV_HINT) { ata_dev_printk(dev, KERN_DEBUG, @@ -6283,6 +6303,7 @@ EXPORT_SYMBOL_GPL(ata_host_resume); #endif /* CONFIG_PM */ EXPORT_SYMBOL_GPL(ata_id_string); EXPORT_SYMBOL_GPL(ata_id_c_string); +EXPORT_SYMBOL_GPL(ata_do_dev_read_id); EXPORT_SYMBOL_GPL(ata_scsi_simulate); EXPORT_SYMBOL_GPL(ata_pio_need_iordy); diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index e10816931b2f..27843c70eb9d 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -80,7 +80,7 @@ #define DRV_NAME "pata_it821x" -#define DRV_VERSION "0.3.8" +#define DRV_VERSION "0.4.0" struct it821x_dev { @@ -425,6 +425,8 @@ static unsigned int it821x_smart_qc_issue(struct ata_queued_cmd *qc) case ATA_CMD_WRITE_MULTI: case ATA_CMD_WRITE_MULTI_EXT: case ATA_CMD_ID_ATA: + case ATA_CMD_INIT_DEV_PARAMS: + case 0xFC: /* Internal 'report rebuild state' */ /* Arguably should just no-op this one */ case ATA_CMD_SET_FEATURES: return ata_sff_qc_issue(qc); @@ -509,7 +511,7 @@ static void it821x_dev_config(struct ata_device *adev) if (strstr(model_num, "Integrated Technology Express")) { /* RAID mode */ - printk(KERN_INFO "IT821x %sRAID%d volume", + ata_dev_printk(adev, KERN_INFO, "%sRAID%d volume", adev->id[147]?"Bootable ":"", adev->id[129]); if (adev->id[129] != 1) @@ -519,37 +521,51 @@ static void it821x_dev_config(struct ata_device *adev) /* This is a controller firmware triggered funny, don't report the drive faulty! */ adev->horkage &= ~ATA_HORKAGE_DIAGNOSTIC; + /* No HPA in 'smart' mode */ + adev->horkage |= ATA_HORKAGE_BROKEN_HPA; } /** - * it821x_ident_hack - Hack identify data up - * @ap: Port + * it821x_read_id - Hack identify data up + * @adev: device to read + * @tf: proposed taskfile + * @id: buffer for returned ident data * - * Walk the devices on this firmware driven port and slightly + * Query the devices on this firmware driven port and slightly * mash the identify data to stop us and common tools trying to * use features not firmware supported. The firmware itself does * some masking (eg SMART) but not enough. - * - * This is a bit of an abuse of the cable method, but it is the - * only method called at the right time. We could modify the libata - * core specifically for ident hacking but while we have one offender - * it seems better to keep the fallout localised. */ -static int it821x_ident_hack(struct ata_port *ap) +static unsigned int it821x_read_id(struct ata_device *adev, + struct ata_taskfile *tf, u16 *id) { - struct ata_device *adev; - ata_link_for_each_dev(adev, &ap->link) { - if (ata_dev_enabled(adev)) { - adev->id[84] &= ~(1 << 6); /* No FUA */ - adev->id[85] &= ~(1 << 10); /* No HPA */ - adev->id[76] = 0; /* No NCQ/AN etc */ - } + unsigned int err_mask; + unsigned char model_num[ATA_ID_PROD_LEN + 1]; + + err_mask = ata_do_dev_read_id(adev, tf, id); + if (err_mask) + return err_mask; + ata_id_c_string(id, model_num, ATA_ID_PROD, sizeof(model_num)); + + id[83] &= ~(1 << 12); /* Cache flush is firmware handled */ + id[83] &= ~(1 << 13); /* Ditto for LBA48 flushes */ + id[84] &= ~(1 << 6); /* No FUA */ + id[85] &= ~(1 << 10); /* No HPA */ + id[76] = 0; /* No NCQ/AN etc */ + + if (strstr(model_num, "Integrated Technology Express")) { + /* Set feature bits the firmware neglects */ + id[49] |= 0x0300; /* LBA, DMA */ + id[82] |= 0x0400; /* LBA48 */ + id[83] &= 0x7FFF; + id[83] |= 0x4000; /* Word 83 is valid */ + id[86] |= 0x0400; /* LBA48 on */ + id[ATA_ID_MAJOR_VER] |= 0x1F; } - return ata_cable_unknown(ap); + return err_mask; } - /** * it821x_check_atapi_dma - ATAPI DMA handler * @qc: Command we are about to issue @@ -577,6 +593,136 @@ static int it821x_check_atapi_dma(struct ata_queued_cmd *qc) return 0; } +/** + * it821x_display_disk - display disk setup + * @n: Device number + * @buf: Buffer block from firmware + * + * Produce a nice informative display of the device setup as provided + * by the firmware. + */ + +static void it821x_display_disk(int n, u8 *buf) +{ + unsigned char id[41]; + int mode = 0; + char *mtype; + char mbuf[8]; + char *cbl = "(40 wire cable)"; + + static const char *types[5] = { + "RAID0", "RAID1" "RAID 0+1", "JBOD", "DISK" + }; + + if (buf[52] > 4) /* No Disk */ + return; + + ata_id_c_string((u16 *)buf, id, 0, 41); + + if (buf[51]) { + mode = ffs(buf[51]); + mtype = "UDMA"; + } else if (buf[49]) { + mode = ffs(buf[49]); + mtype = "MWDMA"; + } + + if (buf[76]) + cbl = ""; + + if (mode) + snprintf(mbuf, 8, "%5s%d", mtype, mode - 1); + else + strcpy(mbuf, "PIO"); + if (buf[52] == 4) + printk(KERN_INFO "%d: %-6s %-8s %s %s\n", + n, mbuf, types[buf[52]], id, cbl); + else + printk(KERN_INFO "%d: %-6s %-8s Volume: %1d %s %s\n", + n, mbuf, types[buf[52]], buf[53], id, cbl); + if (buf[125] < 100) + printk(KERN_INFO "%d: Rebuilding: %d%%\n", n, buf[125]); +} + +/** + * it821x_firmware_command - issue firmware command + * @ap: IT821x port to interrogate + * @cmd: command + * @len: length + * + * Issue firmware commands expecting data back from the controller. We + * use this to issue commands that do not go via the normal paths. Other + * commands such as 0xFC can be issued normally. + */ + +static u8 *it821x_firmware_command(struct ata_port *ap, u8 cmd, int len) +{ + u8 status; + int n = 0; + u16 *buf = kmalloc(len, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "it821x_firmware_command: Out of memory\n"); + return NULL; + } + /* This isn't quite a normal ATA command as we are talking to the + firmware not the drives */ + ap->ctl |= ATA_NIEN; + iowrite8(ap->ctl, ap->ioaddr.ctl_addr); + ata_wait_idle(ap); + iowrite8(ATA_DEVICE_OBS, ap->ioaddr.device_addr); + iowrite8(cmd, ap->ioaddr.command_addr); + udelay(1); + /* This should be almost immediate but a little paranoia goes a long + way. */ + while(n++ < 10) { + status = ioread8(ap->ioaddr.status_addr); + if (status & ATA_ERR) { + kfree(buf); + printk(KERN_ERR "it821x_firmware_command: rejected\n"); + return NULL; + } + if (status & ATA_DRQ) { + ioread16_rep(ap->ioaddr.data_addr, buf, len/2); + return (u8 *)buf; + } + mdelay(1); + } + kfree(buf); + printk(KERN_ERR "it821x_firmware_command: timeout\n"); + return NULL; +} + +/** + * it821x_probe_firmware - firmware reporting/setup + * @ap: IT821x port being probed + * + * Probe the firmware of the controller by issuing firmware command + * 0xFA and analysing the returned data. + */ + +static void it821x_probe_firmware(struct ata_port *ap) +{ + u8 *buf; + int i; + + /* This is a bit ugly as we can't just issue a task file to a device + as this is controller magic */ + + buf = it821x_firmware_command(ap, 0xFA, 512); + + if (buf != NULL) { + printk(KERN_INFO "pata_it821x: Firmware %02X/%02X/%02X%02X\n", + buf[505], + buf[506], + buf[507], + buf[508]); + for (i = 0; i < 4; i++) + it821x_display_disk(i, buf + 128 * i); + kfree(buf); + } +} + + /** * it821x_port_start - port setup @@ -610,6 +756,8 @@ static int it821x_port_start(struct ata_port *ap) /* Long I/O's although allowed in LBA48 space cause the onboard firmware to enter the twighlight zone */ /* No ATAPI DMA in this mode either */ + if (ap->port_no == 0) + it821x_probe_firmware(ap); } /* Pull the current clocks from 0x50 */ if (conf & (1 << (1 + ap->port_no))) @@ -631,6 +779,25 @@ static int it821x_port_start(struct ata_port *ap) return 0; } +/** + * it821x_rdc_cable - Cable detect for RDC1010 + * @ap: port we are checking + * + * Return the RDC1010 cable type. Unlike the IT821x we know how to do + * this and can do host side cable detect + */ + +static int it821x_rdc_cable(struct ata_port *ap) +{ + u16 r40; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + + pci_read_config_word(pdev, 0x40, &r40); + if (r40 & (1 << (2 + ap->port_no))) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + static struct scsi_host_template it821x_sht = { ATA_BMDMA_SHT(DRV_NAME), }; @@ -641,9 +808,10 @@ static struct ata_port_operations it821x_smart_port_ops = { .check_atapi_dma= it821x_check_atapi_dma, .qc_issue = it821x_smart_qc_issue, - .cable_detect = it821x_ident_hack, + .cable_detect = ata_cable_80wire, .set_mode = it821x_smart_set_mode, .dev_config = it821x_dev_config, + .read_id = it821x_read_id, .port_start = it821x_port_start, }; @@ -664,8 +832,29 @@ static struct ata_port_operations it821x_passthru_port_ops = { .port_start = it821x_port_start, }; +static struct ata_port_operations it821x_rdc_port_ops = { + .inherits = &ata_bmdma_port_ops, + + .check_atapi_dma= it821x_check_atapi_dma, + .sff_dev_select = it821x_passthru_dev_select, + .bmdma_start = it821x_passthru_bmdma_start, + .bmdma_stop = it821x_passthru_bmdma_stop, + .qc_issue = it821x_passthru_qc_issue, + + .cable_detect = it821x_rdc_cable, + .set_piomode = it821x_passthru_set_piomode, + .set_dmamode = it821x_passthru_set_dmamode, + + .port_start = it821x_port_start, +}; + static void it821x_disable_raid(struct pci_dev *pdev) { + /* Neither the RDC nor the IT8211 */ + if (pdev->vendor != PCI_VENDOR_ID_ITE || + pdev->device != PCI_DEVICE_ID_ITE_8212) + return; + /* Reset local CPU, and set BIOS not ready */ pci_write_config_byte(pdev, 0x5E, 0x01); @@ -690,6 +879,7 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = 0x1f, .mwdma_mask = 0x07, + .udma_mask = ATA_UDMA6, .port_ops = &it821x_smart_port_ops }; static const struct ata_port_info info_passthru = { @@ -699,6 +889,13 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .udma_mask = ATA_UDMA6, .port_ops = &it821x_passthru_port_ops }; + static const struct ata_port_info info_rdc = { + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + /* No UDMA */ + .port_ops = &it821x_rdc_port_ops + }; const struct ata_port_info *ppi[] = { NULL, NULL }; static char *mode[2] = { "pass through", "smart" }; @@ -707,21 +904,25 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) rc = pcim_enable_device(pdev); if (rc) return rc; + + if (pdev->vendor == PCI_VENDOR_ID_RDC) { + ppi[0] = &info_rdc; + } else { + /* Force the card into bypass mode if so requested */ + if (it8212_noraid) { + printk(KERN_INFO DRV_NAME ": forcing bypass mode.\n"); + it821x_disable_raid(pdev); + } + pci_read_config_byte(pdev, 0x50, &conf); + conf &= 1; - /* Force the card into bypass mode if so requested */ - if (it8212_noraid) { - printk(KERN_INFO DRV_NAME ": forcing bypass mode.\n"); - it821x_disable_raid(pdev); + printk(KERN_INFO DRV_NAME": controller in %s mode.\n", + mode[conf]); + if (conf == 0) + ppi[0] = &info_passthru; + else + ppi[0] = &info_smart; } - pci_read_config_byte(pdev, 0x50, &conf); - conf &= 1; - - printk(KERN_INFO DRV_NAME ": controller in %s mode.\n", mode[conf]); - if (conf == 0) - ppi[0] = &info_passthru; - else - ppi[0] = &info_smart; - return ata_pci_sff_init_one(pdev, ppi, &it821x_sht, NULL); } @@ -745,6 +946,7 @@ static int it821x_reinit_one(struct pci_dev *pdev) static const struct pci_device_id it821x[] = { { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), }, { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), }, + { PCI_VDEVICE(RDC, 0x1010), }, { }, }; diff --git a/include/linux/libata.h b/include/linux/libata.h index d4b8e5fa3e8b..06b80337303b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -750,6 +750,7 @@ struct ata_port_operations { void (*set_piomode)(struct ata_port *ap, struct ata_device *dev); void (*set_dmamode)(struct ata_port *ap, struct ata_device *dev); int (*set_mode)(struct ata_link *link, struct ata_device **r_failed_dev); + unsigned int (*read_id)(struct ata_device *dev, struct ata_taskfile *tf, u16 *id); void (*dev_config)(struct ata_device *dev); @@ -951,6 +952,8 @@ extern void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len); extern void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len); +extern unsigned int ata_do_dev_read_id(struct ata_device *dev, + struct ata_taskfile *tf, u16 *id); extern void ata_qc_complete(struct ata_queued_cmd *qc); extern int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active); extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd, -- cgit v1.2.3 From ae375044d31075a31de5a839e07ded7f67b660aa Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 31 Jul 2008 00:38:01 -0700 Subject: netfilter: nf_conntrack_tcp: decrease timeouts while data in unacknowledged In order to time out dead connections quicker, keep track of outstanding data and cap the timeout. Suggested by Herbert Xu. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/nf_conntrack_tcp.h | 3 +++ net/netfilter/nf_conntrack_proto_tcp.c | 29 ++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nf_conntrack_tcp.h b/include/linux/netfilter/nf_conntrack_tcp.h index 22ce29995f13..a049df4f2236 100644 --- a/include/linux/netfilter/nf_conntrack_tcp.h +++ b/include/linux/netfilter/nf_conntrack_tcp.h @@ -30,6 +30,9 @@ enum tcp_conntrack { /* Be liberal in window checking */ #define IP_CT_TCP_FLAG_BE_LIBERAL 0x08 +/* Has unacknowledged data */ +#define IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED 0x10 + struct nf_ct_tcp_flags { u_int8_t flags; u_int8_t mask; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 420a10d8eb1e..6f61261888ef 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -67,7 +67,8 @@ static const char *const tcp_conntrack_names[] = { /* RFC1122 says the R2 limit should be at least 100 seconds. Linux uses 15 packets as limit, which corresponds to ~13-30min depending on RTO. */ -static unsigned int nf_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS; +static unsigned int nf_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS; +static unsigned int nf_ct_tcp_timeout_unacknowledged __read_mostly = 5 MINS; static unsigned int tcp_timeouts[TCP_CONNTRACK_MAX] __read_mostly = { [TCP_CONNTRACK_SYN_SENT] = 2 MINS, @@ -625,8 +626,10 @@ static bool tcp_in_window(const struct nf_conn *ct, swin = win + (sack - ack); if (sender->td_maxwin < swin) sender->td_maxwin = swin; - if (after(end, sender->td_end)) + if (after(end, sender->td_end)) { sender->td_end = end; + sender->flags |= IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED; + } /* * Update receiver data. */ @@ -637,6 +640,8 @@ static bool tcp_in_window(const struct nf_conn *ct, if (win == 0) receiver->td_maxend++; } + if (ack == receiver->td_end) + receiver->flags &= ~IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED; /* * Check retransmissions. @@ -951,9 +956,16 @@ static int tcp_packet(struct nf_conn *ct, if (old_state != new_state && new_state == TCP_CONNTRACK_FIN_WAIT) ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; - timeout = ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans - && tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans - ? nf_ct_tcp_timeout_max_retrans : tcp_timeouts[new_state]; + + if (ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans && + tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans) + timeout = nf_ct_tcp_timeout_max_retrans; + else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) & + IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && + tcp_timeouts[new_state] > nf_ct_tcp_timeout_unacknowledged) + timeout = nf_ct_tcp_timeout_unacknowledged; + else + timeout = tcp_timeouts[new_state]; write_unlock_bh(&tcp_lock); nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); @@ -1235,6 +1247,13 @@ static struct ctl_table tcp_sysctl_table[] = { .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, + { + .procname = "nf_conntrack_tcp_timeout_unacknowledged", + .data = &nf_ct_tcp_timeout_unacknowledged, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + }, { .ctl_name = NET_NF_CONNTRACK_TCP_LOOSE, .procname = "nf_conntrack_tcp_loose", -- cgit v1.2.3 From e4e4e534faa3c2be4e165ce414f44b76ada7208c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 14 Apr 2008 08:50:02 +0200 Subject: sched clock: revert various sched_clock() changes Found an interactivity problem on a quad core test-system - simple CPU loops would occasionally delay the system un an unacceptable way. After much debugging with Peter Zijlstra it turned out that the problem is caused by the string of sched_clock() changes - they caused the CPU clock to jump backwards a bit - which confuses the scheduler arithmetics. (which is unsigned for performance reasons) So revert: # c300ba2: sched_clock: and multiplier for TSC to gtod drift # c0c8773: sched_clock: only update deltas with local reads. # af52a90: sched_clock: stop maximum check on NO HZ # f7cce27: sched_clock: widen the max and min time This solves the interactivity problems. Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Mike Galbraith --- include/linux/sched.h | 17 +------- kernel/sched_clock.c | 109 ++++++----------------------------------------- kernel/time/tick-sched.c | 2 - 3 files changed, 14 insertions(+), 114 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5270d449ff9d..ea436bc1a0e2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1572,28 +1572,13 @@ static inline void sched_clock_idle_sleep_event(void) static inline void sched_clock_idle_wakeup_event(u64 delta_ns) { } - -#ifdef CONFIG_NO_HZ -static inline void sched_clock_tick_stop(int cpu) -{ -} - -static inline void sched_clock_tick_start(int cpu) -{ -} -#endif - -#else /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */ +#else extern void sched_clock_init(void); extern u64 sched_clock_cpu(int cpu); extern void sched_clock_tick(void); extern void sched_clock_idle_sleep_event(void); extern void sched_clock_idle_wakeup_event(u64 delta_ns); -#ifdef CONFIG_NO_HZ -extern void sched_clock_tick_stop(int cpu); -extern void sched_clock_tick_start(int cpu); #endif -#endif /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */ /* * For kernel-internal use: high-speed (but slightly incorrect) per-cpu diff --git a/kernel/sched_clock.c b/kernel/sched_clock.c index 5a2dc7d8fd98..9a7844158ae8 100644 --- a/kernel/sched_clock.c +++ b/kernel/sched_clock.c @@ -44,11 +44,6 @@ unsigned long long __attribute__((weak)) sched_clock(void) #ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK -#define MULTI_SHIFT 15 -/* Max is double, Min is 1/2 */ -#define MAX_MULTI (2LL << MULTI_SHIFT) -#define MIN_MULTI (1LL << (MULTI_SHIFT-1)) - struct sched_clock_data { /* * Raw spinlock - this is a special case: this might be called @@ -62,10 +57,6 @@ struct sched_clock_data { u64 tick_raw; u64 tick_gtod; u64 clock; - s64 multi; -#ifdef CONFIG_NO_HZ - int check_max; -#endif }; static DEFINE_PER_CPU_SHARED_ALIGNED(struct sched_clock_data, sched_clock_data); @@ -97,53 +88,18 @@ void sched_clock_init(void) scd->tick_raw = 0; scd->tick_gtod = ktime_now; scd->clock = ktime_now; - scd->multi = 1 << MULTI_SHIFT; -#ifdef CONFIG_NO_HZ - scd->check_max = 1; -#endif } sched_clock_running = 1; } -#ifdef CONFIG_NO_HZ -/* - * The dynamic ticks makes the delta jiffies inaccurate. This - * prevents us from checking the maximum time update. - * Disable the maximum check during stopped ticks. - */ -void sched_clock_tick_stop(int cpu) -{ - struct sched_clock_data *scd = cpu_sdc(cpu); - - scd->check_max = 0; -} - -void sched_clock_tick_start(int cpu) -{ - struct sched_clock_data *scd = cpu_sdc(cpu); - - scd->check_max = 1; -} - -static int check_max(struct sched_clock_data *scd) -{ - return scd->check_max; -} -#else -static int check_max(struct sched_clock_data *scd) -{ - return 1; -} -#endif /* CONFIG_NO_HZ */ - /* * update the percpu scd from the raw @now value * * - filter out backward motion * - use jiffies to generate a min,max window to clip the raw values */ -static void __update_sched_clock(struct sched_clock_data *scd, u64 now, u64 *time) +static void __update_sched_clock(struct sched_clock_data *scd, u64 now) { unsigned long now_jiffies = jiffies; long delta_jiffies = now_jiffies - scd->tick_jiffies; @@ -152,31 +108,16 @@ static void __update_sched_clock(struct sched_clock_data *scd, u64 now, u64 *tim s64 delta = now - scd->prev_raw; WARN_ON_ONCE(!irqs_disabled()); - - /* - * At schedule tick the clock can be just under the gtod. We don't - * want to push it too prematurely. - */ - min_clock = scd->tick_gtod + (delta_jiffies * TICK_NSEC); - if (min_clock > TICK_NSEC) - min_clock -= TICK_NSEC / 2; + min_clock = scd->tick_gtod + delta_jiffies * TICK_NSEC; if (unlikely(delta < 0)) { clock++; goto out; } - /* - * The clock must stay within a jiffie of the gtod. - * But since we may be at the start of a jiffy or the end of one - * we add another jiffy buffer. - */ - max_clock = scd->tick_gtod + (2 + delta_jiffies) * TICK_NSEC; - - delta *= scd->multi; - delta >>= MULTI_SHIFT; + max_clock = min_clock + TICK_NSEC; - if (unlikely(clock + delta > max_clock) && check_max(scd)) { + if (unlikely(clock + delta > max_clock)) { if (clock < max_clock) clock = max_clock; else @@ -189,12 +130,9 @@ static void __update_sched_clock(struct sched_clock_data *scd, u64 now, u64 *tim if (unlikely(clock < min_clock)) clock = min_clock; - if (time) - *time = clock; - else { - scd->prev_raw = now; - scd->clock = clock; - } + scd->prev_raw = now; + scd->tick_jiffies = now_jiffies; + scd->clock = clock; } static void lock_double_clock(struct sched_clock_data *data1, @@ -238,26 +176,21 @@ u64 sched_clock_cpu(int cpu) now -= scd->tick_gtod; __raw_spin_unlock(&my_scd->lock); - - __update_sched_clock(scd, now, &clock); - - __raw_spin_unlock(&scd->lock); - } else { __raw_spin_lock(&scd->lock); - __update_sched_clock(scd, now, NULL); - clock = scd->clock; - __raw_spin_unlock(&scd->lock); } + __update_sched_clock(scd, now); + clock = scd->clock; + + __raw_spin_unlock(&scd->lock); + return clock; } void sched_clock_tick(void) { struct sched_clock_data *scd = this_scd(); - unsigned long now_jiffies = jiffies; - s64 mult, delta_gtod, delta_raw; u64 now, now_gtod; if (unlikely(!sched_clock_running)) @@ -269,29 +202,14 @@ void sched_clock_tick(void) now = sched_clock(); __raw_spin_lock(&scd->lock); - __update_sched_clock(scd, now, NULL); + __update_sched_clock(scd, now); /* * update tick_gtod after __update_sched_clock() because that will * already observe 1 new jiffy; adding a new tick_gtod to that would * increase the clock 2 jiffies. */ - delta_gtod = now_gtod - scd->tick_gtod; - delta_raw = now - scd->tick_raw; - - if ((long)delta_raw > 0) { - mult = delta_gtod << MULTI_SHIFT; - do_div(mult, delta_raw); - scd->multi = mult; - if (scd->multi > MAX_MULTI) - scd->multi = MAX_MULTI; - else if (scd->multi < MIN_MULTI) - scd->multi = MIN_MULTI; - } else - scd->multi = 1 << MULTI_SHIFT; - scd->tick_raw = now; scd->tick_gtod = now_gtod; - scd->tick_jiffies = now_jiffies; __raw_spin_unlock(&scd->lock); } @@ -321,7 +239,6 @@ void sched_clock_idle_wakeup_event(u64 delta_ns) __raw_spin_lock(&scd->lock); scd->prev_raw = now; scd->clock += delta_ns; - scd->multi = 1 << MULTI_SHIFT; __raw_spin_unlock(&scd->lock); touch_softlockup_watchdog(); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 825b4c00fe44..f5da526424a9 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -289,7 +289,6 @@ void tick_nohz_stop_sched_tick(int inidle) ts->tick_stopped = 1; ts->idle_jiffies = last_jiffies; rcu_enter_nohz(); - sched_clock_tick_stop(cpu); } /* @@ -392,7 +391,6 @@ void tick_nohz_restart_sched_tick(void) select_nohz_load_balancer(0); now = ktime_get(); tick_do_update_jiffies64(now); - sched_clock_tick_start(cpu); cpu_clear(cpu, nohz_cpu_mask); /* -- cgit v1.2.3 From 419ca3f13532793b81aff09f80c60af3eacbb43d Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 29 Jul 2008 21:45:03 -0700 Subject: lockdep: fix combinatorial explosion in lock subgraph traversal When we traverse the graph, either forwards or backwards, we are interested in whether a certain property exists somewhere in a node reachable in the graph. Therefore it is never necessary to traverse through a node more than once to get a correct answer to the given query. Take advantage of this property using a global ID counter so that we need not clear all the markers in all the lock_class entries before doing a traversal. A new ID is choosen when we start to traverse, and we continue through a lock_class only if it's ID hasn't been marked with the new value yet. This short-circuiting is essential especially for high CPU count systems. The scheduler has a runqueue per cpu, and needs to take two runqueue locks at a time, which leads to long chains of backwards and forwards subgraphs from these runqueue lock nodes. Without the short-circuit implemented here, a graph traversal on a runqueue lock can take up to (1 << (N - 1)) checks on a system with N cpus. For anything more than 16 cpus or so, lockdep will eventually bring the machine to a complete standstill. Signed-off-by: David S. Miller Acked-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 1 + kernel/lockdep.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/lockdep_internals.h | 3 ++ kernel/lockdep_proc.c | 34 ++---------------- 4 files changed, 93 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 2486eb4edbf1..1bfdc30bb0af 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -89,6 +89,7 @@ struct lock_class { struct lockdep_subclass_key *key; unsigned int subclass; + unsigned int dep_gen_id; /* * IRQ/softirq usage tracking bits: diff --git a/kernel/lockdep.c b/kernel/lockdep.c index d38a64362973..6999e64fc248 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -372,6 +372,19 @@ unsigned int nr_process_chains; unsigned int max_lockdep_depth; unsigned int max_recursion_depth; +static unsigned int lockdep_dependency_gen_id; + +static bool lockdep_dependency_visit(struct lock_class *source, + unsigned int depth) +{ + if (!depth) + lockdep_dependency_gen_id++; + if (source->dep_gen_id == lockdep_dependency_gen_id) + return true; + source->dep_gen_id = lockdep_dependency_gen_id; + return false; +} + #ifdef CONFIG_DEBUG_LOCKDEP /* * We cannot printk in early bootup code. Not even early_printk() @@ -558,6 +571,9 @@ static void print_lock_dependencies(struct lock_class *class, int depth) { struct lock_list *entry; + if (lockdep_dependency_visit(class, depth)) + return; + if (DEBUG_LOCKS_WARN_ON(depth >= 20)) return; @@ -959,6 +975,67 @@ static int noinline print_infinite_recursion_bug(void) return 0; } +unsigned long __lockdep_count_forward_deps(struct lock_class *class, + unsigned int depth) +{ + struct lock_list *entry; + unsigned long ret = 1; + + if (lockdep_dependency_visit(class, depth)) + return 0; + + /* + * Recurse this class's dependency list: + */ + list_for_each_entry(entry, &class->locks_after, entry) + ret += __lockdep_count_forward_deps(entry->class, depth + 1); + + return ret; +} + +unsigned long lockdep_count_forward_deps(struct lock_class *class) +{ + unsigned long ret, flags; + + local_irq_save(flags); + __raw_spin_lock(&lockdep_lock); + ret = __lockdep_count_forward_deps(class, 0); + __raw_spin_unlock(&lockdep_lock); + local_irq_restore(flags); + + return ret; +} + +unsigned long __lockdep_count_backward_deps(struct lock_class *class, + unsigned int depth) +{ + struct lock_list *entry; + unsigned long ret = 1; + + if (lockdep_dependency_visit(class, depth)) + return 0; + /* + * Recurse this class's dependency list: + */ + list_for_each_entry(entry, &class->locks_before, entry) + ret += __lockdep_count_backward_deps(entry->class, depth + 1); + + return ret; +} + +unsigned long lockdep_count_backward_deps(struct lock_class *class) +{ + unsigned long ret, flags; + + local_irq_save(flags); + __raw_spin_lock(&lockdep_lock); + ret = __lockdep_count_backward_deps(class, 0); + __raw_spin_unlock(&lockdep_lock); + local_irq_restore(flags); + + return ret; +} + /* * Prove that the dependency graph starting at can not * lead to . Print an error and return 0 if it does. @@ -968,6 +1045,9 @@ check_noncircular(struct lock_class *source, unsigned int depth) { struct lock_list *entry; + if (lockdep_dependency_visit(source, depth)) + return 1; + debug_atomic_inc(&nr_cyclic_check_recursions); if (depth > max_recursion_depth) max_recursion_depth = depth; @@ -1011,6 +1091,9 @@ find_usage_forwards(struct lock_class *source, unsigned int depth) struct lock_list *entry; int ret; + if (lockdep_dependency_visit(source, depth)) + return 1; + if (depth > max_recursion_depth) max_recursion_depth = depth; if (depth >= RECURSION_LIMIT) @@ -1050,6 +1133,9 @@ find_usage_backwards(struct lock_class *source, unsigned int depth) struct lock_list *entry; int ret; + if (lockdep_dependency_visit(source, depth)) + return 1; + if (!__raw_spin_is_locked(&lockdep_lock)) return DEBUG_LOCKS_WARN_ON(1); diff --git a/kernel/lockdep_internals.h b/kernel/lockdep_internals.h index c3600a091a28..68d44ec77ab5 100644 --- a/kernel/lockdep_internals.h +++ b/kernel/lockdep_internals.h @@ -53,6 +53,9 @@ extern unsigned int nr_process_chains; extern unsigned int max_lockdep_depth; extern unsigned int max_recursion_depth; +extern unsigned long lockdep_count_forward_deps(struct lock_class *); +extern unsigned long lockdep_count_backward_deps(struct lock_class *); + #ifdef CONFIG_DEBUG_LOCKDEP /* * Various lockdep statistics: diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c index 9b0e940e2545..6252ff799d19 100644 --- a/kernel/lockdep_proc.c +++ b/kernel/lockdep_proc.c @@ -63,34 +63,6 @@ static void l_stop(struct seq_file *m, void *v) { } -static unsigned long count_forward_deps(struct lock_class *class) -{ - struct lock_list *entry; - unsigned long ret = 1; - - /* - * Recurse this class's dependency list: - */ - list_for_each_entry(entry, &class->locks_after, entry) - ret += count_forward_deps(entry->class); - - return ret; -} - -static unsigned long count_backward_deps(struct lock_class *class) -{ - struct lock_list *entry; - unsigned long ret = 1; - - /* - * Recurse this class's dependency list: - */ - list_for_each_entry(entry, &class->locks_before, entry) - ret += count_backward_deps(entry->class); - - return ret; -} - static void print_name(struct seq_file *m, struct lock_class *class) { char str[128]; @@ -124,10 +96,10 @@ static int l_show(struct seq_file *m, void *v) #ifdef CONFIG_DEBUG_LOCKDEP seq_printf(m, " OPS:%8ld", class->ops); #endif - nr_forward_deps = count_forward_deps(class); + nr_forward_deps = lockdep_count_forward_deps(class); seq_printf(m, " FD:%5ld", nr_forward_deps); - nr_backward_deps = count_backward_deps(class); + nr_backward_deps = lockdep_count_backward_deps(class); seq_printf(m, " BD:%5ld", nr_backward_deps); get_usage_chars(class, &c1, &c2, &c3, &c4); @@ -350,7 +322,7 @@ static int lockdep_stats_show(struct seq_file *m, void *v) if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ) nr_hardirq_read_unsafe++; - sum_forward_deps += count_forward_deps(class); + sum_forward_deps += lockdep_count_forward_deps(class); } #ifdef CONFIG_DEBUG_LOCKDEP DEBUG_LOCKS_WARN_ON(debug_atomic_read(&nr_unused_locks) != nr_unused); -- cgit v1.2.3 From 64a76f667d987a559ad0726b4692c987800b22bc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 29 Jul 2008 12:47:38 -0700 Subject: hpet: /dev/hpet - fixes and cleanup Minor /dev/hpet updates and bugfixes: * Remove dead code, mostly remnants of an incomplete/unusable kernel interface ... noted when addressing "sparse" warnings: + hpet_unregister() and a routine it calls + hpet_task and all references, including hpet_task_lock + hpet_data.hd_flags (and HPET_DATA_PLATFORM) * Correct and improve boot message: + displays *counter* (shared between comparators) bit width, not *timer* bit widths (which are often mixed) + relabel "timers" as "comparators"; this is less confusing, they are not independent like normal timers are (sigh) + display MHz not Hz; it's never less than 10 MHz. * Tighten and correct the userspace interface code + don't accidentally program comparators in 64-bit mode using 32-bit values ... always force comparators into 32-bit mode + provide the correct bit definition flagging comparators with periodic capability ... the ABI is unchanged * Update Documentation/hpet.txt + be more correct and current + expand description a bit + don't mention that now-gone kernel interface Plus, add a FIXME comment for something that could cause big trouble on systems with more capable HPETs than at least Intel seems to ship. It seems that few folk use this userspace interface; it's not very usable given the general lack of HPET IRQ routing. I'm told that the only real point of it any more is to mmap for fast timestamps; IMO that's handled better through the gettimeofday() vsyscall. Signed-off-by: David Brownell Acked-by: Clemens Ladisch Signed-off-by: Ingo Molnar --- Documentation/timers/hpet.txt | 43 ++++++++++----------- arch/x86/kernel/hpet.c | 6 ++- drivers/char/hpet.c | 90 ++++++++++++------------------------------- include/linux/hpet.h | 11 +----- 4 files changed, 51 insertions(+), 99 deletions(-) (limited to 'include/linux') diff --git a/Documentation/timers/hpet.txt b/Documentation/timers/hpet.txt index 6ad52d9dad6c..e7c09abcfab4 100644 --- a/Documentation/timers/hpet.txt +++ b/Documentation/timers/hpet.txt @@ -1,21 +1,32 @@ High Precision Event Timer Driver for Linux -The High Precision Event Timer (HPET) hardware is the future replacement -for the 8254 and Real Time Clock (RTC) periodic timer functionality. -Each HPET can have up to 32 timers. It is possible to configure the -first two timers as legacy replacements for 8254 and RTC periodic timers. -A specification done by Intel and Microsoft can be found at -. +The High Precision Event Timer (HPET) hardware follows a specification +by Intel and Microsoft which can be found at + + http://www.intel.com/technology/architecture/hpetspec.htm + +Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision") +and up to 32 comparators. Normally three or more comparators are provided, +each of which can generate oneshot interupts and at least one of which has +additional hardware to support periodic interrupts. The comparators are +also called "timers", which can be misleading since usually timers are +independent of each other ... these share a counter, complicating resets. + +HPET devices can support two interrupt routing modes. In one mode, the +comparators are additional interrupt sources with no particular system +role. Many x86 BIOS writers don't route HPET interrupts at all, which +prevents use of that mode. They support the other "legacy replacement" +mode where the first two comparators block interrupts from 8254 timers +and from the RTC. The driver supports detection of HPET driver allocation and initialization of the HPET before the driver module_init routine is called. This enables platform code which uses timer 0 or 1 as the main timer to intercept HPET initialization. An example of this initialization can be found in -arch/i386/kernel/time_hpet.c. +arch/x86/kernel/hpet.c. -The driver provides two APIs which are very similar to the API found in -the rtc.c driver. There is a user space API and a kernel space API. -An example user space program is provided below. +The driver provides a userspace API which resembles the API found in the +RTC driver framework. An example user space program is provided below. #include #include @@ -286,15 +297,3 @@ out: return; } - -The kernel API has three interfaces exported from the driver: - - hpet_register(struct hpet_task *tp, int periodic) - hpet_unregister(struct hpet_task *tp) - hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) - -The kernel module using this interface fills in the ht_func and ht_data -members of the hpet_task structure before calling hpet_register. -hpet_control simply vectors to the hpet_ioctl routine and has the same -commands and respective arguments as the user API. hpet_unregister -is used to terminate usage of the HPET timer reserved by hpet_register. diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index ad2b15a1334d..82d459186fd8 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -115,13 +115,17 @@ static void hpet_reserve_platform_timers(unsigned long id) hd.hd_phys_address = hpet_address; hd.hd_address = hpet; hd.hd_nirqs = nrtimers; - hd.hd_flags = HPET_DATA_PLATFORM; hpet_reserve_timer(&hd, 0); #ifdef CONFIG_HPET_EMULATE_RTC hpet_reserve_timer(&hd, 1); #endif + /* + * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254 + * is wrong for i8259!) not the output IRQ. Many BIOS writers + * don't bother configuring *any* comparator interrupts. + */ hd.hd_irq[0] = HPET_LEGACY_8254; hd.hd_irq[1] = HPET_LEGACY_RTC; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index f3981ffe20f0..4bc1da4d4f80 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -53,6 +53,11 @@ #define HPET_RANGE_SIZE 1024 /* from HPET spec */ + +/* WARNING -- don't get confused. These macros are never used + * to write the (single) counter, and rarely to read it. + * They're badly named; to fix, someday. + */ #if BITS_PER_LONG == 64 #define write_counter(V, MC) writeq(V, MC) #define read_counter(MC) readq(MC) @@ -77,7 +82,7 @@ static struct clocksource clocksource_hpet = { .rating = 250, .read = read_hpet, .mask = CLOCKSOURCE_MASK(64), - .mult = 0, /*to be caluclated*/ + .mult = 0, /* to be calculated */ .shift = 10, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -86,8 +91,6 @@ static struct clocksource *hpet_clocksource; /* A lock for concurrent access by app and isr hpet activity. */ static DEFINE_SPINLOCK(hpet_lock); -/* A lock for concurrent intermodule access to hpet and isr hpet activity. */ -static DEFINE_SPINLOCK(hpet_task_lock); #define HPET_DEV_NAME (7) @@ -99,7 +102,6 @@ struct hpet_dev { unsigned long hd_irqdata; wait_queue_head_t hd_waitqueue; struct fasync_struct *hd_async_queue; - struct hpet_task *hd_task; unsigned int hd_flags; unsigned int hd_irq; unsigned int hd_hdwirq; @@ -173,11 +175,6 @@ static irqreturn_t hpet_interrupt(int irq, void *data) writel(isr, &devp->hd_hpet->hpet_isr); spin_unlock(&hpet_lock); - spin_lock(&hpet_task_lock); - if (devp->hd_task) - devp->hd_task->ht_func(devp->hd_task->ht_data); - spin_unlock(&hpet_task_lock); - wake_up_interruptible(&devp->hd_waitqueue); kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN); @@ -260,8 +257,7 @@ static int hpet_open(struct inode *inode, struct file *file) for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next) for (i = 0; i < hpetp->hp_ntimer; i++) - if (hpetp->hp_dev[i].hd_flags & HPET_OPEN - || hpetp->hp_dev[i].hd_task) + if (hpetp->hp_dev[i].hd_flags & HPET_OPEN) continue; else { devp = &hpetp->hp_dev[i]; @@ -504,7 +500,11 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) devp->hd_irq = irq; t = devp->hd_ireqfreq; v = readq(&timer->hpet_config); - g = v | Tn_INT_ENB_CNF_MASK; + + /* 64-bit comparators are not yet supported through the ioctls, + * so force this into 32-bit mode if it supports both modes + */ + g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK; if (devp->hd_flags & HPET_PERIODIC) { write_counter(t, &timer->hpet_compare); @@ -514,6 +514,12 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) v |= Tn_VAL_SET_CNF_MASK; writeq(v, &timer->hpet_config); local_irq_save(flags); + + /* NOTE: what we modify here is a hidden accumulator + * register supported by periodic-capable comparators. + * We never want to modify the (single) counter; that + * would affect all the comparators. + */ m = read_counter(&hpet->hpet_mc); write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); } else { @@ -667,57 +673,6 @@ static int hpet_is_known(struct hpet_data *hdp) return 0; } -static inline int hpet_tpcheck(struct hpet_task *tp) -{ - struct hpet_dev *devp; - struct hpets *hpetp; - - devp = tp->ht_opaque; - - if (!devp) - return -ENXIO; - - for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) - if (devp >= hpetp->hp_dev - && devp < (hpetp->hp_dev + hpetp->hp_ntimer) - && devp->hd_hpet == hpetp->hp_hpet) - return 0; - - return -ENXIO; -} - -#if 0 -int hpet_unregister(struct hpet_task *tp) -{ - struct hpet_dev *devp; - struct hpet_timer __iomem *timer; - int err; - - if ((err = hpet_tpcheck(tp))) - return err; - - spin_lock_irq(&hpet_task_lock); - spin_lock(&hpet_lock); - - devp = tp->ht_opaque; - if (devp->hd_task != tp) { - spin_unlock(&hpet_lock); - spin_unlock_irq(&hpet_task_lock); - return -ENXIO; - } - - timer = devp->hd_timer; - writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK), - &timer->hpet_config); - devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC); - devp->hd_task = NULL; - spin_unlock(&hpet_lock); - spin_unlock_irq(&hpet_task_lock); - - return 0; -} -#endif /* 0 */ - static ctl_table hpet_table[] = { { .ctl_name = CTL_UNNUMBERED, @@ -872,9 +827,12 @@ int hpet_alloc(struct hpet_data *hdp) printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); printk("\n"); - printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n", - hpetp->hp_which, hpetp->hp_ntimer, - cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq); + printk(KERN_INFO + "hpet%u: %u comparators, %d-bit %u.%06u MHz counter\n", + hpetp->hp_which, hpetp->hp_ntimer, + cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, + (unsigned) (hpetp->hp_tick_freq / 1000000), + (unsigned) (hpetp->hp_tick_freq % 1000000)); mcfg = readq(&hpet->hpet_config); if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { diff --git a/include/linux/hpet.h b/include/linux/hpet.h index 6d2626b63a9a..79f63a27bcef 100644 --- a/include/linux/hpet.h +++ b/include/linux/hpet.h @@ -92,23 +92,14 @@ struct hpet { * exported interfaces */ -struct hpet_task { - void (*ht_func) (void *); - void *ht_data; - void *ht_opaque; -}; - struct hpet_data { unsigned long hd_phys_address; void __iomem *hd_address; unsigned short hd_nirqs; - unsigned short hd_flags; unsigned int hd_state; /* timer allocated */ unsigned int hd_irq[HPET_MAX_TIMERS]; }; -#define HPET_DATA_PLATFORM 0x0001 /* platform call to hpet_alloc */ - static inline void hpet_reserve_timer(struct hpet_data *hd, int timer) { hd->hd_state |= (1 << timer); @@ -126,7 +117,7 @@ struct hpet_info { unsigned short hi_timer; }; -#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */ +#define HPET_INFO_PERIODIC 0x0010 /* periodic-capable comparator */ #define HPET_IE_ON _IO('h', 0x01) /* interrupt on */ #define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */ -- cgit v1.2.3 From dacdd0e04768da1fd2b24a6ee274c582b40d0c5b Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 17 Jul 2008 16:54:19 -0700 Subject: [PATCH] configfs: Include linux/err.h in linux/configfs.h We now use PTR_ERR() in the ->make_item() and ->make_group() operations. Folks including configfs.h need err.h. Signed-off-by: Joel Becker Signed-off-by: Mark Fasheh --- fs/configfs/dir.c | 2 +- include/linux/configfs.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 179589be063a..2495f23e33f4 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1094,7 +1094,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) kfree(name); if (ret) { /* - * If item == NULL, then link_obj() was never called. + * If ret != 0, then link_obj() was never called. * There are no extra references to clean up. */ goto out_put; diff --git a/include/linux/configfs.h b/include/linux/configfs.h index d62c19ff041c..0a5491baf0bc 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -40,6 +40,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From ecb3d28c7edd58b54f16838c434b342ba9195bec Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Wed, 18 Jun 2008 19:29:05 -0700 Subject: [PATCH] configfs: Convenience macros for attribute definition. Sysfs has the _ATTR() and _ATTR_RO() macros to make defining extended form attributes easier. configfs should have something similiar. - _CONFIGFS_ATTR() and _CONFIGFS_ATTR_RO() are the counterparts to the sysfs macros. - CONFIGFS_ATTR_STRUCT() creates the extended form attribute structure. - CONFIGFS_ATTR_OPS() defines the show_attribute()/store_attribute() operations that call the show()/store() operations of the extended form configfs_attributes. Signed-off-by: Joel Becker Signed-off-by: Mark Fasheh --- Documentation/filesystems/configfs/configfs.txt | 17 +- .../filesystems/configfs/configfs_example.c | 485 --------------------- .../configfs/configfs_example_explicit.c | 485 +++++++++++++++++++++ .../filesystems/configfs/configfs_example_macros.c | 448 +++++++++++++++++++ include/linux/configfs.h | 67 ++- 5 files changed, 1012 insertions(+), 490 deletions(-) delete mode 100644 Documentation/filesystems/configfs/configfs_example.c create mode 100644 Documentation/filesystems/configfs/configfs_example_explicit.c create mode 100644 Documentation/filesystems/configfs/configfs_example_macros.c (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index 44c97e6accb2..fabcb0e00f25 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -311,9 +311,20 @@ the subsystem must be ready for it. [An Example] The best example of these basic concepts is the simple_children -subsystem/group and the simple_child item in configfs_example.c It -shows a trivial object displaying and storing an attribute, and a simple -group creating and destroying these children. +subsystem/group and the simple_child item in configfs_example_explicit.c +and configfs_example_macros.c. It shows a trivial object displaying and +storing an attribute, and a simple group creating and destroying these +children. + +The only difference between configfs_example_explicit.c and +configfs_example_macros.c is how the attributes of the childless item +are defined. The childless item has extended attributes, each with +their own show()/store() operation. This follows a convention commonly +used in sysfs. configfs_example_explicit.c creates these attributes +by explicitly defining the structures involved. Conversely +configfs_example_macros.c uses some convenience macros from configfs.h +to define the attributes. These macros are similar to their sysfs +counterparts. [Hierarchy Navigation and the Subsystem Mutex] diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c deleted file mode 100644 index 039648791701..000000000000 --- a/Documentation/filesystems/configfs/configfs_example.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * vim: noexpandtab ts=8 sts=0 sw=8: - * - * configfs_example.c - This file is a demonstration module containing - * a number of configfs subsystems. - * - * 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. - * - * 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, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - * - * Based on sysfs: - * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel - * - * configfs Copyright (C) 2005 Oracle. All rights reserved. - */ - -#include -#include -#include - -#include - - - -/* - * 01-childless - * - * This first example is a childless subsystem. It cannot create - * any config_items. It just has attributes. - * - * Note that we are enclosing the configfs_subsystem inside a container. - * This is not necessary if a subsystem has no attributes directly - * on the subsystem. See the next example, 02-simple-children, for - * such a subsystem. - */ - -struct childless { - struct configfs_subsystem subsys; - int showme; - int storeme; -}; - -struct childless_attribute { - struct configfs_attribute attr; - ssize_t (*show)(struct childless *, char *); - ssize_t (*store)(struct childless *, const char *, size_t); -}; - -static inline struct childless *to_childless(struct config_item *item) -{ - return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct childless, subsys) : NULL; -} - -static ssize_t childless_showme_read(struct childless *childless, - char *page) -{ - ssize_t pos; - - pos = sprintf(page, "%d\n", childless->showme); - childless->showme++; - - return pos; -} - -static ssize_t childless_storeme_read(struct childless *childless, - char *page) -{ - return sprintf(page, "%d\n", childless->storeme); -} - -static ssize_t childless_storeme_write(struct childless *childless, - const char *page, - size_t count) -{ - unsigned long tmp; - char *p = (char *) page; - - tmp = simple_strtoul(p, &p, 10); - if (!p || (*p && (*p != '\n'))) - return -EINVAL; - - if (tmp > INT_MAX) - return -ERANGE; - - childless->storeme = tmp; - - return count; -} - -static ssize_t childless_description_read(struct childless *childless, - char *page) -{ - return sprintf(page, -"[01-childless]\n" -"\n" -"The childless subsystem is the simplest possible subsystem in\n" -"configfs. It does not support the creation of child config_items.\n" -"It only has a few attributes. In fact, it isn't much different\n" -"than a directory in /proc.\n"); -} - -static struct childless_attribute childless_attr_showme = { - .attr = { .ca_owner = THIS_MODULE, .ca_name = "showme", .ca_mode = S_IRUGO }, - .show = childless_showme_read, -}; -static struct childless_attribute childless_attr_storeme = { - .attr = { .ca_owner = THIS_MODULE, .ca_name = "storeme", .ca_mode = S_IRUGO | S_IWUSR }, - .show = childless_storeme_read, - .store = childless_storeme_write, -}; -static struct childless_attribute childless_attr_description = { - .attr = { .ca_owner = THIS_MODULE, .ca_name = "description", .ca_mode = S_IRUGO }, - .show = childless_description_read, -}; - -static struct configfs_attribute *childless_attrs[] = { - &childless_attr_showme.attr, - &childless_attr_storeme.attr, - &childless_attr_description.attr, - NULL, -}; - -static ssize_t childless_attr_show(struct config_item *item, - struct configfs_attribute *attr, - char *page) -{ - struct childless *childless = to_childless(item); - struct childless_attribute *childless_attr = - container_of(attr, struct childless_attribute, attr); - ssize_t ret = 0; - - if (childless_attr->show) - ret = childless_attr->show(childless, page); - return ret; -} - -static ssize_t childless_attr_store(struct config_item *item, - struct configfs_attribute *attr, - const char *page, size_t count) -{ - struct childless *childless = to_childless(item); - struct childless_attribute *childless_attr = - container_of(attr, struct childless_attribute, attr); - ssize_t ret = -EINVAL; - - if (childless_attr->store) - ret = childless_attr->store(childless, page, count); - return ret; -} - -static struct configfs_item_operations childless_item_ops = { - .show_attribute = childless_attr_show, - .store_attribute = childless_attr_store, -}; - -static struct config_item_type childless_type = { - .ct_item_ops = &childless_item_ops, - .ct_attrs = childless_attrs, - .ct_owner = THIS_MODULE, -}; - -static struct childless childless_subsys = { - .subsys = { - .su_group = { - .cg_item = { - .ci_namebuf = "01-childless", - .ci_type = &childless_type, - }, - }, - }, -}; - - -/* ----------------------------------------------------------------- */ - -/* - * 02-simple-children - * - * This example merely has a simple one-attribute child. Note that - * there is no extra attribute structure, as the child's attribute is - * known from the get-go. Also, there is no container for the - * subsystem, as it has no attributes of its own. - */ - -struct simple_child { - struct config_item item; - int storeme; -}; - -static inline struct simple_child *to_simple_child(struct config_item *item) -{ - return item ? container_of(item, struct simple_child, item) : NULL; -} - -static struct configfs_attribute simple_child_attr_storeme = { - .ca_owner = THIS_MODULE, - .ca_name = "storeme", - .ca_mode = S_IRUGO | S_IWUSR, -}; - -static struct configfs_attribute *simple_child_attrs[] = { - &simple_child_attr_storeme, - NULL, -}; - -static ssize_t simple_child_attr_show(struct config_item *item, - struct configfs_attribute *attr, - char *page) -{ - ssize_t count; - struct simple_child *simple_child = to_simple_child(item); - - count = sprintf(page, "%d\n", simple_child->storeme); - - return count; -} - -static ssize_t simple_child_attr_store(struct config_item *item, - struct configfs_attribute *attr, - const char *page, size_t count) -{ - struct simple_child *simple_child = to_simple_child(item); - unsigned long tmp; - char *p = (char *) page; - - tmp = simple_strtoul(p, &p, 10); - if (!p || (*p && (*p != '\n'))) - return -EINVAL; - - if (tmp > INT_MAX) - return -ERANGE; - - simple_child->storeme = tmp; - - return count; -} - -static void simple_child_release(struct config_item *item) -{ - kfree(to_simple_child(item)); -} - -static struct configfs_item_operations simple_child_item_ops = { - .release = simple_child_release, - .show_attribute = simple_child_attr_show, - .store_attribute = simple_child_attr_store, -}; - -static struct config_item_type simple_child_type = { - .ct_item_ops = &simple_child_item_ops, - .ct_attrs = simple_child_attrs, - .ct_owner = THIS_MODULE, -}; - - -struct simple_children { - struct config_group group; -}; - -static inline struct simple_children *to_simple_children(struct config_item *item) -{ - return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; -} - -static struct config_item *simple_children_make_item(struct config_group *group, const char *name) -{ - struct simple_child *simple_child; - - simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); - if (!simple_child) - return ERR_PTR(-ENOMEM); - - - config_item_init_type_name(&simple_child->item, name, - &simple_child_type); - - simple_child->storeme = 0; - - return &simple_child->item; -} - -static struct configfs_attribute simple_children_attr_description = { - .ca_owner = THIS_MODULE, - .ca_name = "description", - .ca_mode = S_IRUGO, -}; - -static struct configfs_attribute *simple_children_attrs[] = { - &simple_children_attr_description, - NULL, -}; - -static ssize_t simple_children_attr_show(struct config_item *item, - struct configfs_attribute *attr, - char *page) -{ - return sprintf(page, -"[02-simple-children]\n" -"\n" -"This subsystem allows the creation of child config_items. These\n" -"items have only one attribute that is readable and writeable.\n"); -} - -static void simple_children_release(struct config_item *item) -{ - kfree(to_simple_children(item)); -} - -static struct configfs_item_operations simple_children_item_ops = { - .release = simple_children_release, - .show_attribute = simple_children_attr_show, -}; - -/* - * Note that, since no extra work is required on ->drop_item(), - * no ->drop_item() is provided. - */ -static struct configfs_group_operations simple_children_group_ops = { - .make_item = simple_children_make_item, -}; - -static struct config_item_type simple_children_type = { - .ct_item_ops = &simple_children_item_ops, - .ct_group_ops = &simple_children_group_ops, - .ct_attrs = simple_children_attrs, - .ct_owner = THIS_MODULE, -}; - -static struct configfs_subsystem simple_children_subsys = { - .su_group = { - .cg_item = { - .ci_namebuf = "02-simple-children", - .ci_type = &simple_children_type, - }, - }, -}; - - -/* ----------------------------------------------------------------- */ - -/* - * 03-group-children - * - * This example reuses the simple_children group from above. However, - * the simple_children group is not the subsystem itself, it is a - * child of the subsystem. Creation of a group in the subsystem creates - * a new simple_children group. That group can then have simple_child - * children of its own. - */ - -static struct config_group *group_children_make_group(struct config_group *group, const char *name) -{ - struct simple_children *simple_children; - - simple_children = kzalloc(sizeof(struct simple_children), - GFP_KERNEL); - if (!simple_children) - return ERR_PTR(-ENOMEM); - - - config_group_init_type_name(&simple_children->group, name, - &simple_children_type); - - return &simple_children->group; -} - -static struct configfs_attribute group_children_attr_description = { - .ca_owner = THIS_MODULE, - .ca_name = "description", - .ca_mode = S_IRUGO, -}; - -static struct configfs_attribute *group_children_attrs[] = { - &group_children_attr_description, - NULL, -}; - -static ssize_t group_children_attr_show(struct config_item *item, - struct configfs_attribute *attr, - char *page) -{ - return sprintf(page, -"[03-group-children]\n" -"\n" -"This subsystem allows the creation of child config_groups. These\n" -"groups are like the subsystem simple-children.\n"); -} - -static struct configfs_item_operations group_children_item_ops = { - .show_attribute = group_children_attr_show, -}; - -/* - * Note that, since no extra work is required on ->drop_item(), - * no ->drop_item() is provided. - */ -static struct configfs_group_operations group_children_group_ops = { - .make_group = group_children_make_group, -}; - -static struct config_item_type group_children_type = { - .ct_item_ops = &group_children_item_ops, - .ct_group_ops = &group_children_group_ops, - .ct_attrs = group_children_attrs, - .ct_owner = THIS_MODULE, -}; - -static struct configfs_subsystem group_children_subsys = { - .su_group = { - .cg_item = { - .ci_namebuf = "03-group-children", - .ci_type = &group_children_type, - }, - }, -}; - -/* ----------------------------------------------------------------- */ - -/* - * We're now done with our subsystem definitions. - * For convenience in this module, here's a list of them all. It - * allows the init function to easily register them. Most modules - * will only have one subsystem, and will only call register_subsystem - * on it directly. - */ -static struct configfs_subsystem *example_subsys[] = { - &childless_subsys.subsys, - &simple_children_subsys, - &group_children_subsys, - NULL, -}; - -static int __init configfs_example_init(void) -{ - int ret; - int i; - struct configfs_subsystem *subsys; - - for (i = 0; example_subsys[i]; i++) { - subsys = example_subsys[i]; - - config_group_init(&subsys->su_group); - mutex_init(&subsys->su_mutex); - ret = configfs_register_subsystem(subsys); - if (ret) { - printk(KERN_ERR "Error %d while registering subsystem %s\n", - ret, - subsys->su_group.cg_item.ci_namebuf); - goto out_unregister; - } - } - - return 0; - -out_unregister: - for (; i >= 0; i--) { - configfs_unregister_subsystem(example_subsys[i]); - } - - return ret; -} - -static void __exit configfs_example_exit(void) -{ - int i; - - for (i = 0; example_subsys[i]; i++) { - configfs_unregister_subsystem(example_subsys[i]); - } -} - -module_init(configfs_example_init); -module_exit(configfs_example_exit); -MODULE_LICENSE("GPL"); diff --git a/Documentation/filesystems/configfs/configfs_example_explicit.c b/Documentation/filesystems/configfs/configfs_example_explicit.c new file mode 100644 index 000000000000..d428cc9f07f3 --- /dev/null +++ b/Documentation/filesystems/configfs/configfs_example_explicit.c @@ -0,0 +1,485 @@ +/* + * vim: noexpandtab ts=8 sts=0 sw=8: + * + * configfs_example_explicit.c - This file is a demonstration module + * containing a number of configfs subsystems. It explicitly defines + * each structure without using the helper macros defined in + * configfs.h. + * + * 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. + * + * 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, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + * Based on sysfs: + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel + * + * configfs Copyright (C) 2005 Oracle. All rights reserved. + */ + +#include +#include +#include + +#include + + + +/* + * 01-childless + * + * This first example is a childless subsystem. It cannot create + * any config_items. It just has attributes. + * + * Note that we are enclosing the configfs_subsystem inside a container. + * This is not necessary if a subsystem has no attributes directly + * on the subsystem. See the next example, 02-simple-children, for + * such a subsystem. + */ + +struct childless { + struct configfs_subsystem subsys; + int showme; + int storeme; +}; + +struct childless_attribute { + struct configfs_attribute attr; + ssize_t (*show)(struct childless *, char *); + ssize_t (*store)(struct childless *, const char *, size_t); +}; + +static inline struct childless *to_childless(struct config_item *item) +{ + return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct childless, subsys) : NULL; +} + +static ssize_t childless_showme_read(struct childless *childless, + char *page) +{ + ssize_t pos; + + pos = sprintf(page, "%d\n", childless->showme); + childless->showme++; + + return pos; +} + +static ssize_t childless_storeme_read(struct childless *childless, + char *page) +{ + return sprintf(page, "%d\n", childless->storeme); +} + +static ssize_t childless_storeme_write(struct childless *childless, + const char *page, + size_t count) +{ + unsigned long tmp; + char *p = (char *) page; + + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + if (tmp > INT_MAX) + return -ERANGE; + + childless->storeme = tmp; + + return count; +} + +static ssize_t childless_description_read(struct childless *childless, + char *page) +{ + return sprintf(page, +"[01-childless]\n" +"\n" +"The childless subsystem is the simplest possible subsystem in\n" +"configfs. It does not support the creation of child config_items.\n" +"It only has a few attributes. In fact, it isn't much different\n" +"than a directory in /proc.\n"); +} + +static struct childless_attribute childless_attr_showme = { + .attr = { .ca_owner = THIS_MODULE, .ca_name = "showme", .ca_mode = S_IRUGO }, + .show = childless_showme_read, +}; +static struct childless_attribute childless_attr_storeme = { + .attr = { .ca_owner = THIS_MODULE, .ca_name = "storeme", .ca_mode = S_IRUGO | S_IWUSR }, + .show = childless_storeme_read, + .store = childless_storeme_write, +}; +static struct childless_attribute childless_attr_description = { + .attr = { .ca_owner = THIS_MODULE, .ca_name = "description", .ca_mode = S_IRUGO }, + .show = childless_description_read, +}; + +static struct configfs_attribute *childless_attrs[] = { + &childless_attr_showme.attr, + &childless_attr_storeme.attr, + &childless_attr_description.attr, + NULL, +}; + +static ssize_t childless_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct childless *childless = to_childless(item); + struct childless_attribute *childless_attr = + container_of(attr, struct childless_attribute, attr); + ssize_t ret = 0; + + if (childless_attr->show) + ret = childless_attr->show(childless, page); + return ret; +} + +static ssize_t childless_attr_store(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t count) +{ + struct childless *childless = to_childless(item); + struct childless_attribute *childless_attr = + container_of(attr, struct childless_attribute, attr); + ssize_t ret = -EINVAL; + + if (childless_attr->store) + ret = childless_attr->store(childless, page, count); + return ret; +} + +static struct configfs_item_operations childless_item_ops = { + .show_attribute = childless_attr_show, + .store_attribute = childless_attr_store, +}; + +static struct config_item_type childless_type = { + .ct_item_ops = &childless_item_ops, + .ct_attrs = childless_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct childless childless_subsys = { + .subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "01-childless", + .ci_type = &childless_type, + }, + }, + }, +}; + + +/* ----------------------------------------------------------------- */ + +/* + * 02-simple-children + * + * This example merely has a simple one-attribute child. Note that + * there is no extra attribute structure, as the child's attribute is + * known from the get-go. Also, there is no container for the + * subsystem, as it has no attributes of its own. + */ + +struct simple_child { + struct config_item item; + int storeme; +}; + +static inline struct simple_child *to_simple_child(struct config_item *item) +{ + return item ? container_of(item, struct simple_child, item) : NULL; +} + +static struct configfs_attribute simple_child_attr_storeme = { + .ca_owner = THIS_MODULE, + .ca_name = "storeme", + .ca_mode = S_IRUGO | S_IWUSR, +}; + +static struct configfs_attribute *simple_child_attrs[] = { + &simple_child_attr_storeme, + NULL, +}; + +static ssize_t simple_child_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + ssize_t count; + struct simple_child *simple_child = to_simple_child(item); + + count = sprintf(page, "%d\n", simple_child->storeme); + + return count; +} + +static ssize_t simple_child_attr_store(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t count) +{ + struct simple_child *simple_child = to_simple_child(item); + unsigned long tmp; + char *p = (char *) page; + + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + if (tmp > INT_MAX) + return -ERANGE; + + simple_child->storeme = tmp; + + return count; +} + +static void simple_child_release(struct config_item *item) +{ + kfree(to_simple_child(item)); +} + +static struct configfs_item_operations simple_child_item_ops = { + .release = simple_child_release, + .show_attribute = simple_child_attr_show, + .store_attribute = simple_child_attr_store, +}; + +static struct config_item_type simple_child_type = { + .ct_item_ops = &simple_child_item_ops, + .ct_attrs = simple_child_attrs, + .ct_owner = THIS_MODULE, +}; + + +struct simple_children { + struct config_group group; +}; + +static inline struct simple_children *to_simple_children(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; +} + +static struct config_item *simple_children_make_item(struct config_group *group, const char *name) +{ + struct simple_child *simple_child; + + simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); + if (!simple_child) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&simple_child->item, name, + &simple_child_type); + + simple_child->storeme = 0; + + return &simple_child->item; +} + +static struct configfs_attribute simple_children_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "description", + .ca_mode = S_IRUGO, +}; + +static struct configfs_attribute *simple_children_attrs[] = { + &simple_children_attr_description, + NULL, +}; + +static ssize_t simple_children_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + return sprintf(page, +"[02-simple-children]\n" +"\n" +"This subsystem allows the creation of child config_items. These\n" +"items have only one attribute that is readable and writeable.\n"); +} + +static void simple_children_release(struct config_item *item) +{ + kfree(to_simple_children(item)); +} + +static struct configfs_item_operations simple_children_item_ops = { + .release = simple_children_release, + .show_attribute = simple_children_attr_show, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +static struct configfs_group_operations simple_children_group_ops = { + .make_item = simple_children_make_item, +}; + +static struct config_item_type simple_children_type = { + .ct_item_ops = &simple_children_item_ops, + .ct_group_ops = &simple_children_group_ops, + .ct_attrs = simple_children_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem simple_children_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "02-simple-children", + .ci_type = &simple_children_type, + }, + }, +}; + + +/* ----------------------------------------------------------------- */ + +/* + * 03-group-children + * + * This example reuses the simple_children group from above. However, + * the simple_children group is not the subsystem itself, it is a + * child of the subsystem. Creation of a group in the subsystem creates + * a new simple_children group. That group can then have simple_child + * children of its own. + */ + +static struct config_group *group_children_make_group(struct config_group *group, const char *name) +{ + struct simple_children *simple_children; + + simple_children = kzalloc(sizeof(struct simple_children), + GFP_KERNEL); + if (!simple_children) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&simple_children->group, name, + &simple_children_type); + + return &simple_children->group; +} + +static struct configfs_attribute group_children_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "description", + .ca_mode = S_IRUGO, +}; + +static struct configfs_attribute *group_children_attrs[] = { + &group_children_attr_description, + NULL, +}; + +static ssize_t group_children_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + return sprintf(page, +"[03-group-children]\n" +"\n" +"This subsystem allows the creation of child config_groups. These\n" +"groups are like the subsystem simple-children.\n"); +} + +static struct configfs_item_operations group_children_item_ops = { + .show_attribute = group_children_attr_show, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +static struct configfs_group_operations group_children_group_ops = { + .make_group = group_children_make_group, +}; + +static struct config_item_type group_children_type = { + .ct_item_ops = &group_children_item_ops, + .ct_group_ops = &group_children_group_ops, + .ct_attrs = group_children_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem group_children_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "03-group-children", + .ci_type = &group_children_type, + }, + }, +}; + +/* ----------------------------------------------------------------- */ + +/* + * We're now done with our subsystem definitions. + * For convenience in this module, here's a list of them all. It + * allows the init function to easily register them. Most modules + * will only have one subsystem, and will only call register_subsystem + * on it directly. + */ +static struct configfs_subsystem *example_subsys[] = { + &childless_subsys.subsys, + &simple_children_subsys, + &group_children_subsys, + NULL, +}; + +static int __init configfs_example_init(void) +{ + int ret; + int i; + struct configfs_subsystem *subsys; + + for (i = 0; example_subsys[i]; i++) { + subsys = example_subsys[i]; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + ret = configfs_register_subsystem(subsys); + if (ret) { + printk(KERN_ERR "Error %d while registering subsystem %s\n", + ret, + subsys->su_group.cg_item.ci_namebuf); + goto out_unregister; + } + } + + return 0; + +out_unregister: + for (; i >= 0; i--) { + configfs_unregister_subsystem(example_subsys[i]); + } + + return ret; +} + +static void __exit configfs_example_exit(void) +{ + int i; + + for (i = 0; example_subsys[i]; i++) { + configfs_unregister_subsystem(example_subsys[i]); + } +} + +module_init(configfs_example_init); +module_exit(configfs_example_exit); +MODULE_LICENSE("GPL"); diff --git a/Documentation/filesystems/configfs/configfs_example_macros.c b/Documentation/filesystems/configfs/configfs_example_macros.c new file mode 100644 index 000000000000..d8e30a0378aa --- /dev/null +++ b/Documentation/filesystems/configfs/configfs_example_macros.c @@ -0,0 +1,448 @@ +/* + * vim: noexpandtab ts=8 sts=0 sw=8: + * + * configfs_example_macros.c - This file is a demonstration module + * containing a number of configfs subsystems. It uses the helper + * macros defined by configfs.h + * + * 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. + * + * 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, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + * Based on sysfs: + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel + * + * configfs Copyright (C) 2005 Oracle. All rights reserved. + */ + +#include +#include +#include + +#include + + + +/* + * 01-childless + * + * This first example is a childless subsystem. It cannot create + * any config_items. It just has attributes. + * + * Note that we are enclosing the configfs_subsystem inside a container. + * This is not necessary if a subsystem has no attributes directly + * on the subsystem. See the next example, 02-simple-children, for + * such a subsystem. + */ + +struct childless { + struct configfs_subsystem subsys; + int showme; + int storeme; +}; + +static inline struct childless *to_childless(struct config_item *item) +{ + return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct childless, subsys) : NULL; +} + +CONFIGFS_ATTR_STRUCT(childless); +#define CHILDLESS_ATTR(_name, _mode, _show, _store) \ +struct childless_attribute childless_attr_##_name = __CONFIGFS_ATTR(_name, _mode, _show, _store) +#define CHILDLESS_ATTR_RO(_name, _show) \ +struct childless_attribute childless_attr_##_name = __CONFIGFS_ATTR_RO(_name, _show); + +static ssize_t childless_showme_read(struct childless *childless, + char *page) +{ + ssize_t pos; + + pos = sprintf(page, "%d\n", childless->showme); + childless->showme++; + + return pos; +} + +static ssize_t childless_storeme_read(struct childless *childless, + char *page) +{ + return sprintf(page, "%d\n", childless->storeme); +} + +static ssize_t childless_storeme_write(struct childless *childless, + const char *page, + size_t count) +{ + unsigned long tmp; + char *p = (char *) page; + + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + if (tmp > INT_MAX) + return -ERANGE; + + childless->storeme = tmp; + + return count; +} + +static ssize_t childless_description_read(struct childless *childless, + char *page) +{ + return sprintf(page, +"[01-childless]\n" +"\n" +"The childless subsystem is the simplest possible subsystem in\n" +"configfs. It does not support the creation of child config_items.\n" +"It only has a few attributes. In fact, it isn't much different\n" +"than a directory in /proc.\n"); +} + +CHILDLESS_ATTR_RO(showme, childless_showme_read); +CHILDLESS_ATTR(storeme, S_IRUGO | S_IWUSR, childless_storeme_read, + childless_storeme_write); +CHILDLESS_ATTR_RO(description, childless_description_read); + +static struct configfs_attribute *childless_attrs[] = { + &childless_attr_showme.attr, + &childless_attr_storeme.attr, + &childless_attr_description.attr, + NULL, +}; + +CONFIGFS_ATTR_OPS(childless); +static struct configfs_item_operations childless_item_ops = { + .show_attribute = childless_attr_show, + .store_attribute = childless_attr_store, +}; + +static struct config_item_type childless_type = { + .ct_item_ops = &childless_item_ops, + .ct_attrs = childless_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct childless childless_subsys = { + .subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "01-childless", + .ci_type = &childless_type, + }, + }, + }, +}; + + +/* ----------------------------------------------------------------- */ + +/* + * 02-simple-children + * + * This example merely has a simple one-attribute child. Note that + * there is no extra attribute structure, as the child's attribute is + * known from the get-go. Also, there is no container for the + * subsystem, as it has no attributes of its own. + */ + +struct simple_child { + struct config_item item; + int storeme; +}; + +static inline struct simple_child *to_simple_child(struct config_item *item) +{ + return item ? container_of(item, struct simple_child, item) : NULL; +} + +static struct configfs_attribute simple_child_attr_storeme = { + .ca_owner = THIS_MODULE, + .ca_name = "storeme", + .ca_mode = S_IRUGO | S_IWUSR, +}; + +static struct configfs_attribute *simple_child_attrs[] = { + &simple_child_attr_storeme, + NULL, +}; + +static ssize_t simple_child_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + ssize_t count; + struct simple_child *simple_child = to_simple_child(item); + + count = sprintf(page, "%d\n", simple_child->storeme); + + return count; +} + +static ssize_t simple_child_attr_store(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t count) +{ + struct simple_child *simple_child = to_simple_child(item); + unsigned long tmp; + char *p = (char *) page; + + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + if (tmp > INT_MAX) + return -ERANGE; + + simple_child->storeme = tmp; + + return count; +} + +static void simple_child_release(struct config_item *item) +{ + kfree(to_simple_child(item)); +} + +static struct configfs_item_operations simple_child_item_ops = { + .release = simple_child_release, + .show_attribute = simple_child_attr_show, + .store_attribute = simple_child_attr_store, +}; + +static struct config_item_type simple_child_type = { + .ct_item_ops = &simple_child_item_ops, + .ct_attrs = simple_child_attrs, + .ct_owner = THIS_MODULE, +}; + + +struct simple_children { + struct config_group group; +}; + +static inline struct simple_children *to_simple_children(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; +} + +static struct config_item *simple_children_make_item(struct config_group *group, const char *name) +{ + struct simple_child *simple_child; + + simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); + if (!simple_child) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&simple_child->item, name, + &simple_child_type); + + simple_child->storeme = 0; + + return &simple_child->item; +} + +static struct configfs_attribute simple_children_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "description", + .ca_mode = S_IRUGO, +}; + +static struct configfs_attribute *simple_children_attrs[] = { + &simple_children_attr_description, + NULL, +}; + +static ssize_t simple_children_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + return sprintf(page, +"[02-simple-children]\n" +"\n" +"This subsystem allows the creation of child config_items. These\n" +"items have only one attribute that is readable and writeable.\n"); +} + +static void simple_children_release(struct config_item *item) +{ + kfree(to_simple_children(item)); +} + +static struct configfs_item_operations simple_children_item_ops = { + .release = simple_children_release, + .show_attribute = simple_children_attr_show, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +static struct configfs_group_operations simple_children_group_ops = { + .make_item = simple_children_make_item, +}; + +static struct config_item_type simple_children_type = { + .ct_item_ops = &simple_children_item_ops, + .ct_group_ops = &simple_children_group_ops, + .ct_attrs = simple_children_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem simple_children_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "02-simple-children", + .ci_type = &simple_children_type, + }, + }, +}; + + +/* ----------------------------------------------------------------- */ + +/* + * 03-group-children + * + * This example reuses the simple_children group from above. However, + * the simple_children group is not the subsystem itself, it is a + * child of the subsystem. Creation of a group in the subsystem creates + * a new simple_children group. That group can then have simple_child + * children of its own. + */ + +static struct config_group *group_children_make_group(struct config_group *group, const char *name) +{ + struct simple_children *simple_children; + + simple_children = kzalloc(sizeof(struct simple_children), + GFP_KERNEL); + if (!simple_children) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&simple_children->group, name, + &simple_children_type); + + return &simple_children->group; +} + +static struct configfs_attribute group_children_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "description", + .ca_mode = S_IRUGO, +}; + +static struct configfs_attribute *group_children_attrs[] = { + &group_children_attr_description, + NULL, +}; + +static ssize_t group_children_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + return sprintf(page, +"[03-group-children]\n" +"\n" +"This subsystem allows the creation of child config_groups. These\n" +"groups are like the subsystem simple-children.\n"); +} + +static struct configfs_item_operations group_children_item_ops = { + .show_attribute = group_children_attr_show, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +static struct configfs_group_operations group_children_group_ops = { + .make_group = group_children_make_group, +}; + +static struct config_item_type group_children_type = { + .ct_item_ops = &group_children_item_ops, + .ct_group_ops = &group_children_group_ops, + .ct_attrs = group_children_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem group_children_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "03-group-children", + .ci_type = &group_children_type, + }, + }, +}; + +/* ----------------------------------------------------------------- */ + +/* + * We're now done with our subsystem definitions. + * For convenience in this module, here's a list of them all. It + * allows the init function to easily register them. Most modules + * will only have one subsystem, and will only call register_subsystem + * on it directly. + */ +static struct configfs_subsystem *example_subsys[] = { + &childless_subsys.subsys, + &simple_children_subsys, + &group_children_subsys, + NULL, +}; + +static int __init configfs_example_init(void) +{ + int ret; + int i; + struct configfs_subsystem *subsys; + + for (i = 0; example_subsys[i]; i++) { + subsys = example_subsys[i]; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + ret = configfs_register_subsystem(subsys); + if (ret) { + printk(KERN_ERR "Error %d while registering subsystem %s\n", + ret, + subsys->su_group.cg_item.ci_namebuf); + goto out_unregister; + } + } + + return 0; + +out_unregister: + for (; i >= 0; i--) { + configfs_unregister_subsystem(example_subsys[i]); + } + + return ret; +} + +static void __exit configfs_example_exit(void) +{ + int i; + + for (i = 0; example_subsys[i]; i++) { + configfs_unregister_subsystem(example_subsys[i]); + } +} + +module_init(configfs_example_init); +module_exit(configfs_example_exit); +MODULE_LICENSE("GPL"); diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 0a5491baf0bc..7f627775c947 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -130,8 +130,25 @@ struct configfs_attribute { /* * Users often need to create attribute structures for their configurable * attributes, containing a configfs_attribute member and function pointers - * for the show() and store() operations on that attribute. They can use - * this macro (similar to sysfs' __ATTR) to make defining attributes easier. + * for the show() and store() operations on that attribute. If they don't + * need anything else on the extended attribute structure, they can use + * this macro to define it The argument _item is the name of the + * config_item structure. + */ +#define CONFIGFS_ATTR_STRUCT(_item) \ +struct _item##_attribute { \ + struct configfs_attribute attr; \ + ssize_t (*show)(struct _item *, char *); \ + ssize_t (*store)(struct _item *, const char *, size_t); \ +} + +/* + * With the extended attribute structure, users can use this macro + * (similar to sysfs' __ATTR) to make defining attributes easier. + * An example: + * #define MYITEM_ATTR(_name, _mode, _show, _store) \ + * struct myitem_attribute childless_attr_##_name = \ + * __CONFIGFS_ATTR(_name, _mode, _show, _store) */ #define __CONFIGFS_ATTR(_name, _mode, _show, _store) \ { \ @@ -143,6 +160,52 @@ struct configfs_attribute { .show = _show, \ .store = _store, \ } +/* Here is a readonly version, only requiring a show() operation */ +#define __CONFIGFS_ATTR_RO(_name, _show) \ +{ \ + .attr = { \ + .ca_name = __stringify(_name), \ + .ca_mode = 0444, \ + .ca_owner = THIS_MODULE, \ + }, \ + .show = _show, \ +} + +/* + * With these extended attributes, the simple show_attribute() and + * store_attribute() operations need to call the show() and store() of the + * attributes. This is a common pattern, so we provide a macro to define + * them. The argument _item is the name of the config_item structure. + * This macro expects the attributes to be named "struct _attribute" + * and the function to_() to exist; + */ +#define CONFIGFS_ATTR_OPS(_item) \ +static ssize_t _item##_attr_show(struct config_item *item, \ + struct configfs_attribute *attr, \ + char *page) \ +{ \ + struct _item *_item = to_##_item(item); \ + struct _item##_attribute *_item##_attr = \ + container_of(attr, struct _item##_attribute, attr); \ + ssize_t ret = 0; \ + \ + if (_item##_attr->show) \ + ret = _item##_attr->show(_item, page); \ + return ret; \ +} \ +static ssize_t _item##_attr_store(struct config_item *item, \ + struct configfs_attribute *attr, \ + const char *page, size_t count) \ +{ \ + struct _item *_item = to_##_item(item); \ + struct _item##_attribute *_item##_attr = \ + container_of(attr, struct _item##_attribute, attr); \ + ssize_t ret = -EINVAL; \ + \ + if (_item##_attr->store) \ + ret = _item##_attr->store(_item, page, count); \ + return ret; \ +} /* * If allow_link() exists, the item can symlink(2) out to other -- cgit v1.2.3 From c3f26a269c2421f97f10cf8ed05d5099b573af4d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 31 Jul 2008 16:58:50 -0700 Subject: netdev: Fix lockdep warnings in multiqueue configurations. When support for multiple TX queues were added, the netif_tx_lock() routines we converted to iterate over all TX queues and grab each queue's spinlock. This causes heartburn for lockdep and it's not a healthy thing to do with lots of TX queues anyways. So modify this to use a top-level lock and a "frozen" state for the individual TX queues. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 12 ++++--- include/linux/netdevice.h | 86 ++++++++++++++++++++++++++++++----------------- net/core/dev.c | 1 + net/core/netpoll.c | 1 + net/core/pktgen.c | 7 ++-- net/sched/sch_generic.c | 6 ++-- net/sched/sch_teql.c | 9 ++--- 7 files changed, 78 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 0960e69b2da4..e4fbefc8c82f 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -69,18 +69,20 @@ static void ri_tasklet(unsigned long dev) struct net_device *_dev = (struct net_device *)dev; struct ifb_private *dp = netdev_priv(_dev); struct net_device_stats *stats = &_dev->stats; + struct netdev_queue *txq; struct sk_buff *skb; + txq = netdev_get_tx_queue(_dev, 0); dp->st_task_enter++; if ((skb = skb_peek(&dp->tq)) == NULL) { dp->st_txq_refl_try++; - if (netif_tx_trylock(_dev)) { + if (__netif_tx_trylock(txq)) { dp->st_rxq_enter++; while ((skb = skb_dequeue(&dp->rq)) != NULL) { skb_queue_tail(&dp->tq, skb); dp->st_rx2tx_tran++; } - netif_tx_unlock(_dev); + __netif_tx_unlock(txq); } else { /* reschedule */ dp->st_rxq_notenter++; @@ -115,7 +117,7 @@ static void ri_tasklet(unsigned long dev) BUG(); } - if (netif_tx_trylock(_dev)) { + if (__netif_tx_trylock(txq)) { dp->st_rxq_check++; if ((skb = skb_peek(&dp->rq)) == NULL) { dp->tasklet_pending = 0; @@ -123,10 +125,10 @@ static void ri_tasklet(unsigned long dev) netif_wake_queue(_dev); } else { dp->st_rxq_rsch++; - netif_tx_unlock(_dev); + __netif_tx_unlock(txq); goto resched; } - netif_tx_unlock(_dev); + __netif_tx_unlock(txq); } else { resched: dp->tasklet_pending = 1; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b4d056ceab96..ee583f642a9f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -440,6 +440,7 @@ static inline void napi_synchronize(const struct napi_struct *n) enum netdev_queue_state_t { __QUEUE_STATE_XOFF, + __QUEUE_STATE_FROZEN, }; struct netdev_queue { @@ -636,7 +637,7 @@ struct net_device unsigned int real_num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ - + spinlock_t tx_global_lock; /* * One part is mostly used on xmit path (device) */ @@ -1099,6 +1100,11 @@ static inline int netif_queue_stopped(const struct net_device *dev) return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); } +static inline int netif_tx_queue_frozen(const struct netdev_queue *dev_queue) +{ + return test_bit(__QUEUE_STATE_FROZEN, &dev_queue->state); +} + /** * netif_running - test if up * @dev: network device @@ -1475,6 +1481,26 @@ static inline void __netif_tx_lock_bh(struct netdev_queue *txq) txq->xmit_lock_owner = smp_processor_id(); } +static inline int __netif_tx_trylock(struct netdev_queue *txq) +{ + int ok = spin_trylock(&txq->_xmit_lock); + if (likely(ok)) + txq->xmit_lock_owner = smp_processor_id(); + return ok; +} + +static inline void __netif_tx_unlock(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock(&txq->_xmit_lock); +} + +static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock_bh(&txq->_xmit_lock); +} + /** * netif_tx_lock - grab network device transmit lock * @dev: network device @@ -1484,12 +1510,23 @@ static inline void __netif_tx_lock_bh(struct netdev_queue *txq) */ static inline void netif_tx_lock(struct net_device *dev) { - int cpu = smp_processor_id(); unsigned int i; + int cpu; + spin_lock(&dev->tx_global_lock); + cpu = smp_processor_id(); for (i = 0; i < dev->num_tx_queues; i++) { struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + + /* We are the only thread of execution doing a + * freeze, but we have to grab the _xmit_lock in + * order to synchronize with threads which are in + * the ->hard_start_xmit() handler and already + * checked the frozen bit. + */ __netif_tx_lock(txq, cpu); + set_bit(__QUEUE_STATE_FROZEN, &txq->state); + __netif_tx_unlock(txq); } } @@ -1499,40 +1536,22 @@ static inline void netif_tx_lock_bh(struct net_device *dev) netif_tx_lock(dev); } -static inline int __netif_tx_trylock(struct netdev_queue *txq) -{ - int ok = spin_trylock(&txq->_xmit_lock); - if (likely(ok)) - txq->xmit_lock_owner = smp_processor_id(); - return ok; -} - -static inline int netif_tx_trylock(struct net_device *dev) -{ - return __netif_tx_trylock(netdev_get_tx_queue(dev, 0)); -} - -static inline void __netif_tx_unlock(struct netdev_queue *txq) -{ - txq->xmit_lock_owner = -1; - spin_unlock(&txq->_xmit_lock); -} - -static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) -{ - txq->xmit_lock_owner = -1; - spin_unlock_bh(&txq->_xmit_lock); -} - static inline void netif_tx_unlock(struct net_device *dev) { unsigned int i; for (i = 0; i < dev->num_tx_queues; i++) { struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - __netif_tx_unlock(txq); - } + /* No need to grab the _xmit_lock here. If the + * queue is not stopped for another reason, we + * force a schedule. + */ + clear_bit(__QUEUE_STATE_FROZEN, &txq->state); + if (!test_bit(__QUEUE_STATE_XOFF, &txq->state)) + __netif_schedule(txq->qdisc); + } + spin_unlock(&dev->tx_global_lock); } static inline void netif_tx_unlock_bh(struct net_device *dev) @@ -1556,13 +1575,18 @@ static inline void netif_tx_unlock_bh(struct net_device *dev) static inline void netif_tx_disable(struct net_device *dev) { unsigned int i; + int cpu; - netif_tx_lock_bh(dev); + local_bh_disable(); + cpu = smp_processor_id(); for (i = 0; i < dev->num_tx_queues; i++) { struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + + __netif_tx_lock(txq, cpu); netif_tx_stop_queue(txq); + __netif_tx_unlock(txq); } - netif_tx_unlock_bh(dev); + local_bh_enable(); } static inline void netif_addr_lock(struct net_device *dev) diff --git a/net/core/dev.c b/net/core/dev.c index 63d6bcddbf46..69320a56a084 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4200,6 +4200,7 @@ static void netdev_init_queues(struct net_device *dev) { netdev_init_one_queue(dev, &dev->rx_queue, NULL); netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); + spin_lock_init(&dev->tx_global_lock); } /** diff --git a/net/core/netpoll.c b/net/core/netpoll.c index c12720895ecf..6c7af390be0a 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -70,6 +70,7 @@ static void queue_process(struct work_struct *work) local_irq_save(flags); __netif_tx_lock(txq, smp_processor_id()); if (netif_tx_queue_stopped(txq) || + netif_tx_queue_frozen(txq) || dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) { skb_queue_head(&npinfo->txq, skb); __netif_tx_unlock(txq); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index c7d484f7e1c4..3284605f2ec7 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3305,6 +3305,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) txq = netdev_get_tx_queue(odev, queue_map); if (netif_tx_queue_stopped(txq) || + netif_tx_queue_frozen(txq) || need_resched()) { idle_start = getCurUs(); @@ -3320,7 +3321,8 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->idle_acc += getCurUs() - idle_start; - if (netif_tx_queue_stopped(txq)) { + if (netif_tx_queue_stopped(txq) || + netif_tx_queue_frozen(txq)) { pkt_dev->next_tx_us = getCurUs(); /* TODO */ pkt_dev->next_tx_ns = 0; goto out; /* Try the next interface */ @@ -3352,7 +3354,8 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) txq = netdev_get_tx_queue(odev, queue_map); __netif_tx_lock_bh(txq); - if (!netif_tx_queue_stopped(txq)) { + if (!netif_tx_queue_stopped(txq) && + !netif_tx_queue_frozen(txq)) { atomic_inc(&(pkt_dev->skb->users)); retry_now: diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 345838a2e369..9c9cd4d94890 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -135,7 +135,8 @@ static inline int qdisc_restart(struct Qdisc *q) txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); HARD_TX_LOCK(dev, txq, smp_processor_id()); - if (!netif_subqueue_stopped(dev, skb)) + if (!netif_tx_queue_stopped(txq) && + !netif_tx_queue_frozen(txq)) ret = dev_hard_start_xmit(skb, dev, txq); HARD_TX_UNLOCK(dev, txq); @@ -162,7 +163,8 @@ static inline int qdisc_restart(struct Qdisc *q) break; } - if (ret && netif_tx_queue_stopped(txq)) + if (ret && (netif_tx_queue_stopped(txq) || + netif_tx_queue_frozen(txq))) ret = 0; return ret; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 537223642b6e..2c35c678563b 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -305,10 +305,11 @@ restart: switch (teql_resolve(skb, skb_res, slave)) { case 0: - if (netif_tx_trylock(slave)) { - if (!__netif_subqueue_stopped(slave, subq) && + if (__netif_tx_trylock(slave_txq)) { + if (!netif_tx_queue_stopped(slave_txq) && + !netif_tx_queue_frozen(slave_txq) && slave->hard_start_xmit(skb, slave) == 0) { - netif_tx_unlock(slave); + __netif_tx_unlock(slave_txq); master->slaves = NEXT_SLAVE(q); netif_wake_queue(dev); master->stats.tx_packets++; @@ -316,7 +317,7 @@ restart: qdisc_pkt_len(skb); return 0; } - netif_tx_unlock(slave); + __netif_tx_unlock(slave_txq); } if (netif_queue_stopped(dev)) busy = 1; -- cgit v1.2.3 From bc4768eb081a67642c0c44c34ea597c273bdedcb Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Thu, 31 Jul 2008 20:45:24 -0700 Subject: ipvs: Move userspace definitions to include/linux/ip_vs.h Current versions of ipvsadm include "/usr/src/linux/include/net/ip_vs.h" directly. This file also contains kernel-only definitions. Normally, public definitions should live in include/linux, so this patch moves the definitions shared with userspace to a new file, "include/linux/ip_vs.h". This also removes the unused NFC_IPVS_PROPERTY bitmask, which was once used to point into skb->nfcache. To make old ipvsadms still compile with this, the old header file includes the new one. Thanks to Dave Miller and Horms for noting/adding the missing Kbuild entry for the new header file. Signed-off-by: Julius Volz Acked-by: Simon Horman Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/ip_vs.h | 245 ++++++++++++++++++++++++++++++++++++++++++++++++ include/net/ip_vs.h | 253 ++------------------------------------------------ 3 files changed, 254 insertions(+), 245 deletions(-) create mode 100644 include/linux/ip_vs.h (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 4c4142c5aa6e..a26f565e8189 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -97,6 +97,7 @@ header-y += ioctl.h header-y += ip6_tunnel.h header-y += ipmi_msgdefs.h header-y += ipsec.h +header-y += ip_vs.h header-y += ipx.h header-y += irda.h header-y += iso_fs.h diff --git a/include/linux/ip_vs.h b/include/linux/ip_vs.h new file mode 100644 index 000000000000..ec6eb49af2d8 --- /dev/null +++ b/include/linux/ip_vs.h @@ -0,0 +1,245 @@ +/* + * IP Virtual Server + * data structure and functionality definitions + */ + +#ifndef _IP_VS_H +#define _IP_VS_H + +#include /* For __beXX types in userland */ + +#define IP_VS_VERSION_CODE 0x010201 +#define NVERSION(version) \ + (version >> 16) & 0xFF, \ + (version >> 8) & 0xFF, \ + version & 0xFF + +/* + * Virtual Service Flags + */ +#define IP_VS_SVC_F_PERSISTENT 0x0001 /* persistent port */ +#define IP_VS_SVC_F_HASHED 0x0002 /* hashed entry */ + +/* + * Destination Server Flags + */ +#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */ +#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ + +/* + * IPVS sync daemon states + */ +#define IP_VS_STATE_NONE 0x0000 /* daemon is stopped */ +#define IP_VS_STATE_MASTER 0x0001 /* started as master */ +#define IP_VS_STATE_BACKUP 0x0002 /* started as backup */ + +/* + * IPVS socket options + */ +#define IP_VS_BASE_CTL (64+1024+64) /* base */ + +#define IP_VS_SO_SET_NONE IP_VS_BASE_CTL /* just peek */ +#define IP_VS_SO_SET_INSERT (IP_VS_BASE_CTL+1) +#define IP_VS_SO_SET_ADD (IP_VS_BASE_CTL+2) +#define IP_VS_SO_SET_EDIT (IP_VS_BASE_CTL+3) +#define IP_VS_SO_SET_DEL (IP_VS_BASE_CTL+4) +#define IP_VS_SO_SET_FLUSH (IP_VS_BASE_CTL+5) +#define IP_VS_SO_SET_LIST (IP_VS_BASE_CTL+6) +#define IP_VS_SO_SET_ADDDEST (IP_VS_BASE_CTL+7) +#define IP_VS_SO_SET_DELDEST (IP_VS_BASE_CTL+8) +#define IP_VS_SO_SET_EDITDEST (IP_VS_BASE_CTL+9) +#define IP_VS_SO_SET_TIMEOUT (IP_VS_BASE_CTL+10) +#define IP_VS_SO_SET_STARTDAEMON (IP_VS_BASE_CTL+11) +#define IP_VS_SO_SET_STOPDAEMON (IP_VS_BASE_CTL+12) +#define IP_VS_SO_SET_RESTORE (IP_VS_BASE_CTL+13) +#define IP_VS_SO_SET_SAVE (IP_VS_BASE_CTL+14) +#define IP_VS_SO_SET_ZERO (IP_VS_BASE_CTL+15) +#define IP_VS_SO_SET_MAX IP_VS_SO_SET_ZERO + +#define IP_VS_SO_GET_VERSION IP_VS_BASE_CTL +#define IP_VS_SO_GET_INFO (IP_VS_BASE_CTL+1) +#define IP_VS_SO_GET_SERVICES (IP_VS_BASE_CTL+2) +#define IP_VS_SO_GET_SERVICE (IP_VS_BASE_CTL+3) +#define IP_VS_SO_GET_DESTS (IP_VS_BASE_CTL+4) +#define IP_VS_SO_GET_DEST (IP_VS_BASE_CTL+5) /* not used now */ +#define IP_VS_SO_GET_TIMEOUT (IP_VS_BASE_CTL+6) +#define IP_VS_SO_GET_DAEMON (IP_VS_BASE_CTL+7) +#define IP_VS_SO_GET_MAX IP_VS_SO_GET_DAEMON + + +/* + * IPVS Connection Flags + */ +#define IP_VS_CONN_F_FWD_MASK 0x0007 /* mask for the fwd methods */ +#define IP_VS_CONN_F_MASQ 0x0000 /* masquerading/NAT */ +#define IP_VS_CONN_F_LOCALNODE 0x0001 /* local node */ +#define IP_VS_CONN_F_TUNNEL 0x0002 /* tunneling */ +#define IP_VS_CONN_F_DROUTE 0x0003 /* direct routing */ +#define IP_VS_CONN_F_BYPASS 0x0004 /* cache bypass */ +#define IP_VS_CONN_F_SYNC 0x0020 /* entry created by sync */ +#define IP_VS_CONN_F_HASHED 0x0040 /* hashed entry */ +#define IP_VS_CONN_F_NOOUTPUT 0x0080 /* no output packets */ +#define IP_VS_CONN_F_INACTIVE 0x0100 /* not established */ +#define IP_VS_CONN_F_OUT_SEQ 0x0200 /* must do output seq adjust */ +#define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */ +#define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */ +#define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */ +#define IP_VS_CONN_F_TEMPLATE 0x1000 /* template, not connection */ + +#define IP_VS_SCHEDNAME_MAXLEN 16 +#define IP_VS_IFNAME_MAXLEN 16 + + +/* + * The struct ip_vs_service_user and struct ip_vs_dest_user are + * used to set IPVS rules through setsockopt. + */ +struct ip_vs_service_user { + /* virtual service addresses */ + u_int16_t protocol; + __be32 addr; /* virtual ip address */ + __be16 port; + u_int32_t fwmark; /* firwall mark of service */ + + /* virtual service options */ + char sched_name[IP_VS_SCHEDNAME_MAXLEN]; + unsigned flags; /* virtual service flags */ + unsigned timeout; /* persistent timeout in sec */ + __be32 netmask; /* persistent netmask */ +}; + + +struct ip_vs_dest_user { + /* destination server address */ + __be32 addr; + __be16 port; + + /* real server options */ + unsigned conn_flags; /* connection flags */ + int weight; /* destination weight */ + + /* thresholds for active connections */ + u_int32_t u_threshold; /* upper threshold */ + u_int32_t l_threshold; /* lower threshold */ +}; + + +/* + * IPVS statistics object (for user space) + */ +struct ip_vs_stats_user +{ + __u32 conns; /* connections scheduled */ + __u32 inpkts; /* incoming packets */ + __u32 outpkts; /* outgoing packets */ + __u64 inbytes; /* incoming bytes */ + __u64 outbytes; /* outgoing bytes */ + + __u32 cps; /* current connection rate */ + __u32 inpps; /* current in packet rate */ + __u32 outpps; /* current out packet rate */ + __u32 inbps; /* current in byte rate */ + __u32 outbps; /* current out byte rate */ +}; + + +/* The argument to IP_VS_SO_GET_INFO */ +struct ip_vs_getinfo { + /* version number */ + unsigned int version; + + /* size of connection hash table */ + unsigned int size; + + /* number of virtual services */ + unsigned int num_services; +}; + + +/* The argument to IP_VS_SO_GET_SERVICE */ +struct ip_vs_service_entry { + /* which service: user fills in these */ + u_int16_t protocol; + __be32 addr; /* virtual address */ + __be16 port; + u_int32_t fwmark; /* firwall mark of service */ + + /* service options */ + char sched_name[IP_VS_SCHEDNAME_MAXLEN]; + unsigned flags; /* virtual service flags */ + unsigned timeout; /* persistent timeout */ + __be32 netmask; /* persistent netmask */ + + /* number of real servers */ + unsigned int num_dests; + + /* statistics */ + struct ip_vs_stats_user stats; +}; + + +struct ip_vs_dest_entry { + __be32 addr; /* destination address */ + __be16 port; + unsigned conn_flags; /* connection flags */ + int weight; /* destination weight */ + + u_int32_t u_threshold; /* upper threshold */ + u_int32_t l_threshold; /* lower threshold */ + + u_int32_t activeconns; /* active connections */ + u_int32_t inactconns; /* inactive connections */ + u_int32_t persistconns; /* persistent connections */ + + /* statistics */ + struct ip_vs_stats_user stats; +}; + + +/* The argument to IP_VS_SO_GET_DESTS */ +struct ip_vs_get_dests { + /* which service: user fills in these */ + u_int16_t protocol; + __be32 addr; /* virtual address */ + __be16 port; + u_int32_t fwmark; /* firwall mark of service */ + + /* number of real servers */ + unsigned int num_dests; + + /* the real servers */ + struct ip_vs_dest_entry entrytable[0]; +}; + + +/* The argument to IP_VS_SO_GET_SERVICES */ +struct ip_vs_get_services { + /* number of virtual services */ + unsigned int num_services; + + /* service table */ + struct ip_vs_service_entry entrytable[0]; +}; + + +/* The argument to IP_VS_SO_GET_TIMEOUT */ +struct ip_vs_timeout_user { + int tcp_timeout; + int tcp_fin_timeout; + int udp_timeout; +}; + + +/* The argument to IP_VS_SO_GET_DAEMON */ +struct ip_vs_daemon_user { + /* sync daemon state (master/backup) */ + int state; + + /* multicast interface name */ + char mcast_ifn[IP_VS_IFNAME_MAXLEN]; + + /* SyncID we belong to */ + int syncid; +}; + +#endif /* _IP_VS_H */ diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 9a51ebad3f1f..cbb59ebed4ae 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -3,254 +3,17 @@ * data structure and functionality definitions */ -#ifndef _IP_VS_H -#define _IP_VS_H - -#include /* For __uXX types */ -#include /* For __beXX types in userland */ - -#include /* For ctl_path */ - -#define IP_VS_VERSION_CODE 0x010201 -#define NVERSION(version) \ - (version >> 16) & 0xFF, \ - (version >> 8) & 0xFF, \ - version & 0xFF - -/* - * Virtual Service Flags - */ -#define IP_VS_SVC_F_PERSISTENT 0x0001 /* persistent port */ -#define IP_VS_SVC_F_HASHED 0x0002 /* hashed entry */ - -/* - * Destination Server Flags - */ -#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */ -#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ - -/* - * IPVS sync daemon states - */ -#define IP_VS_STATE_NONE 0x0000 /* daemon is stopped */ -#define IP_VS_STATE_MASTER 0x0001 /* started as master */ -#define IP_VS_STATE_BACKUP 0x0002 /* started as backup */ - -/* - * IPVS socket options - */ -#define IP_VS_BASE_CTL (64+1024+64) /* base */ - -#define IP_VS_SO_SET_NONE IP_VS_BASE_CTL /* just peek */ -#define IP_VS_SO_SET_INSERT (IP_VS_BASE_CTL+1) -#define IP_VS_SO_SET_ADD (IP_VS_BASE_CTL+2) -#define IP_VS_SO_SET_EDIT (IP_VS_BASE_CTL+3) -#define IP_VS_SO_SET_DEL (IP_VS_BASE_CTL+4) -#define IP_VS_SO_SET_FLUSH (IP_VS_BASE_CTL+5) -#define IP_VS_SO_SET_LIST (IP_VS_BASE_CTL+6) -#define IP_VS_SO_SET_ADDDEST (IP_VS_BASE_CTL+7) -#define IP_VS_SO_SET_DELDEST (IP_VS_BASE_CTL+8) -#define IP_VS_SO_SET_EDITDEST (IP_VS_BASE_CTL+9) -#define IP_VS_SO_SET_TIMEOUT (IP_VS_BASE_CTL+10) -#define IP_VS_SO_SET_STARTDAEMON (IP_VS_BASE_CTL+11) -#define IP_VS_SO_SET_STOPDAEMON (IP_VS_BASE_CTL+12) -#define IP_VS_SO_SET_RESTORE (IP_VS_BASE_CTL+13) -#define IP_VS_SO_SET_SAVE (IP_VS_BASE_CTL+14) -#define IP_VS_SO_SET_ZERO (IP_VS_BASE_CTL+15) -#define IP_VS_SO_SET_MAX IP_VS_SO_SET_ZERO - -#define IP_VS_SO_GET_VERSION IP_VS_BASE_CTL -#define IP_VS_SO_GET_INFO (IP_VS_BASE_CTL+1) -#define IP_VS_SO_GET_SERVICES (IP_VS_BASE_CTL+2) -#define IP_VS_SO_GET_SERVICE (IP_VS_BASE_CTL+3) -#define IP_VS_SO_GET_DESTS (IP_VS_BASE_CTL+4) -#define IP_VS_SO_GET_DEST (IP_VS_BASE_CTL+5) /* not used now */ -#define IP_VS_SO_GET_TIMEOUT (IP_VS_BASE_CTL+6) -#define IP_VS_SO_GET_DAEMON (IP_VS_BASE_CTL+7) -#define IP_VS_SO_GET_MAX IP_VS_SO_GET_DAEMON - - -/* - * IPVS Connection Flags - */ -#define IP_VS_CONN_F_FWD_MASK 0x0007 /* mask for the fwd methods */ -#define IP_VS_CONN_F_MASQ 0x0000 /* masquerading/NAT */ -#define IP_VS_CONN_F_LOCALNODE 0x0001 /* local node */ -#define IP_VS_CONN_F_TUNNEL 0x0002 /* tunneling */ -#define IP_VS_CONN_F_DROUTE 0x0003 /* direct routing */ -#define IP_VS_CONN_F_BYPASS 0x0004 /* cache bypass */ -#define IP_VS_CONN_F_SYNC 0x0020 /* entry created by sync */ -#define IP_VS_CONN_F_HASHED 0x0040 /* hashed entry */ -#define IP_VS_CONN_F_NOOUTPUT 0x0080 /* no output packets */ -#define IP_VS_CONN_F_INACTIVE 0x0100 /* not established */ -#define IP_VS_CONN_F_OUT_SEQ 0x0200 /* must do output seq adjust */ -#define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */ -#define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */ -#define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */ -#define IP_VS_CONN_F_TEMPLATE 0x1000 /* template, not connection */ - -/* Move it to better place one day, for now keep it unique */ -#define NFC_IPVS_PROPERTY 0x10000 - -#define IP_VS_SCHEDNAME_MAXLEN 16 -#define IP_VS_IFNAME_MAXLEN 16 - - -/* - * The struct ip_vs_service_user and struct ip_vs_dest_user are - * used to set IPVS rules through setsockopt. - */ -struct ip_vs_service_user { - /* virtual service addresses */ - u_int16_t protocol; - __be32 addr; /* virtual ip address */ - __be16 port; - u_int32_t fwmark; /* firwall mark of service */ - - /* virtual service options */ - char sched_name[IP_VS_SCHEDNAME_MAXLEN]; - unsigned flags; /* virtual service flags */ - unsigned timeout; /* persistent timeout in sec */ - __be32 netmask; /* persistent netmask */ -}; - - -struct ip_vs_dest_user { - /* destination server address */ - __be32 addr; - __be16 port; - - /* real server options */ - unsigned conn_flags; /* connection flags */ - int weight; /* destination weight */ - - /* thresholds for active connections */ - u_int32_t u_threshold; /* upper threshold */ - u_int32_t l_threshold; /* lower threshold */ -}; - - -/* - * IPVS statistics object (for user space) - */ -struct ip_vs_stats_user -{ - __u32 conns; /* connections scheduled */ - __u32 inpkts; /* incoming packets */ - __u32 outpkts; /* outgoing packets */ - __u64 inbytes; /* incoming bytes */ - __u64 outbytes; /* outgoing bytes */ - - __u32 cps; /* current connection rate */ - __u32 inpps; /* current in packet rate */ - __u32 outpps; /* current out packet rate */ - __u32 inbps; /* current in byte rate */ - __u32 outbps; /* current out byte rate */ -}; - - -/* The argument to IP_VS_SO_GET_INFO */ -struct ip_vs_getinfo { - /* version number */ - unsigned int version; - - /* size of connection hash table */ - unsigned int size; - - /* number of virtual services */ - unsigned int num_services; -}; - - -/* The argument to IP_VS_SO_GET_SERVICE */ -struct ip_vs_service_entry { - /* which service: user fills in these */ - u_int16_t protocol; - __be32 addr; /* virtual address */ - __be16 port; - u_int32_t fwmark; /* firwall mark of service */ - - /* service options */ - char sched_name[IP_VS_SCHEDNAME_MAXLEN]; - unsigned flags; /* virtual service flags */ - unsigned timeout; /* persistent timeout */ - __be32 netmask; /* persistent netmask */ - - /* number of real servers */ - unsigned int num_dests; - - /* statistics */ - struct ip_vs_stats_user stats; -}; - - -struct ip_vs_dest_entry { - __be32 addr; /* destination address */ - __be16 port; - unsigned conn_flags; /* connection flags */ - int weight; /* destination weight */ - - u_int32_t u_threshold; /* upper threshold */ - u_int32_t l_threshold; /* lower threshold */ - - u_int32_t activeconns; /* active connections */ - u_int32_t inactconns; /* inactive connections */ - u_int32_t persistconns; /* persistent connections */ - - /* statistics */ - struct ip_vs_stats_user stats; -}; - - -/* The argument to IP_VS_SO_GET_DESTS */ -struct ip_vs_get_dests { - /* which service: user fills in these */ - u_int16_t protocol; - __be32 addr; /* virtual address */ - __be16 port; - u_int32_t fwmark; /* firwall mark of service */ - - /* number of real servers */ - unsigned int num_dests; - - /* the real servers */ - struct ip_vs_dest_entry entrytable[0]; -}; - - -/* The argument to IP_VS_SO_GET_SERVICES */ -struct ip_vs_get_services { - /* number of virtual services */ - unsigned int num_services; - - /* service table */ - struct ip_vs_service_entry entrytable[0]; -}; - - -/* The argument to IP_VS_SO_GET_TIMEOUT */ -struct ip_vs_timeout_user { - int tcp_timeout; - int tcp_fin_timeout; - int udp_timeout; -}; - - -/* The argument to IP_VS_SO_GET_DAEMON */ -struct ip_vs_daemon_user { - /* sync daemon state (master/backup) */ - int state; - - /* multicast interface name */ - char mcast_ifn[IP_VS_IFNAME_MAXLEN]; - - /* SyncID we belong to */ - int syncid; -}; +#ifndef _NET_IP_VS_H +#define _NET_IP_VS_H +#include /* definitions shared with userland */ +/* old ipvsadm versions still include this file directly */ #ifdef __KERNEL__ +#include /* for __uXX types */ + +#include /* for ctl_path */ #include /* for struct list_head */ #include /* for struct rwlock_t */ #include /* for struct atomic_t */ @@ -981,4 +744,4 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) #endif /* __KERNEL__ */ -#endif /* _IP_VS_H */ +#endif /* _NET_IP_VS_H */ -- cgit v1.2.3 From 4a7b61d23505854dff7d04cc11944566cffdd0ee Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 31 Jul 2008 20:52:08 -0700 Subject: skbuff: add missing kernel-doc for do_not_encrypt Add missing kernel-doc notation to sk_buff: Warning(linux-2.6.27-rc1-git2//include/linux/skbuff.h:345): No description found for parameter 'do_not_encrypt' Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/skbuff.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a640385e0598..cfcc45b3bef0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -243,6 +243,7 @@ typedef unsigned char *sk_buff_data_t; * @tc_index: Traffic control index * @tc_verd: traffic control verdict * @ndisc_nodetype: router type (from link layer) + * @do_not_encrypt: set to prevent encryption of this frame * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions * @secmark: security marking -- cgit v1.2.3 From a4b526b3ba6353cd89a38e41da48ed83b0ead16f Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 1 Aug 2008 16:39:12 +0200 Subject: [S390] Optimize storage key operations for anon pages For anonymous pages without a swap cache backing the check in page_remove_rmap for the physical dirty bit in page_remove_rmap is unnecessary. The instructions that are used to check and reset the dirty bit are expensive. Removing the check noticably speeds up process exit. In addition the clearing of the dirty bit in __SetPageUptodate is pointless as well. With these two changes there is no storage key operation for an anonymous page anymore if it does not hit the swap space. The micro benchmark which repeatedly executes an empty shell script gets about 5% faster. Signed-off-by: Martin Schwidefsky --- include/linux/page-flags.h | 3 --- mm/rmap.c | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 54590a9a103e..25aaccdb2f26 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -239,9 +239,6 @@ static inline void __SetPageUptodate(struct page *page) { smp_wmb(); __set_bit(PG_uptodate, &(page)->flags); -#ifdef CONFIG_S390 - page_clear_dirty(page); -#endif } static inline void SetPageUptodate(struct page *page) diff --git a/mm/rmap.c b/mm/rmap.c index 99bc3f9cd796..94a5246a3f98 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -667,7 +667,8 @@ void page_remove_rmap(struct page *page, struct vm_area_struct *vma) * Leaving it set also helps swapoff to reinstate ptes * faster for those pages still in swapcache. */ - if (page_test_dirty(page)) { + if ((!PageAnon(page) || PageSwapCache(page)) && + page_test_dirty(page)) { page_clear_dirty(page); set_page_dirty(page); } -- cgit v1.2.3 From 1027abe8827b47f7e9c4ed6514fde3d44f79963c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 Jul 2008 04:13:04 -0400 Subject: [PATCH] merge locate_fd() and get_unused_fd() New primitive: alloc_fd(start, flags). get_unused_fd() and get_unused_fd_flags() become wrappers on top of it. Signed-off-by: Al Viro --- fs/fcntl.c | 87 +++++++++------------------------------------------- fs/file.c | 61 ++++++++++++++++++++++++++++++++++++ fs/open.c | 56 --------------------------------- include/linux/file.h | 3 +- 4 files changed, 77 insertions(+), 130 deletions(-) (limited to 'include/linux') diff --git a/fs/fcntl.c b/fs/fcntl.c index 61d625136813..2e40799daad6 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -49,73 +49,6 @@ static int get_close_on_exec(unsigned int fd) return res; } -/* - * locate_fd finds a free file descriptor in the open_fds fdset, - * expanding the fd arrays if necessary. Must be called with the - * file_lock held for write. - */ - -static int locate_fd(unsigned int orig_start, int cloexec) -{ - struct files_struct *files = current->files; - unsigned int newfd; - unsigned int start; - int error; - struct fdtable *fdt; - - spin_lock(&files->file_lock); -repeat: - fdt = files_fdtable(files); - /* - * Someone might have closed fd's in the range - * orig_start..fdt->next_fd - */ - start = orig_start; - if (start < files->next_fd) - start = files->next_fd; - - newfd = start; - if (start < fdt->max_fds) - newfd = find_next_zero_bit(fdt->open_fds->fds_bits, - fdt->max_fds, start); - - error = expand_files(files, newfd); - if (error < 0) - goto out; - - /* - * If we needed to expand the fs array we - * might have blocked - try again. - */ - if (error) - goto repeat; - - if (start <= files->next_fd) - files->next_fd = newfd + 1; - - FD_SET(newfd, fdt->open_fds); - if (cloexec) - FD_SET(newfd, fdt->close_on_exec); - else - FD_CLR(newfd, fdt->close_on_exec); - error = newfd; - -out: - spin_unlock(&files->file_lock); - return error; -} - -static int dupfd(struct file *file, unsigned int start, int cloexec) -{ - int fd = locate_fd(start, cloexec); - if (fd >= 0) - fd_install(fd, file); - else - fput(file); - - return fd; -} - asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) { int err = -EBADF; @@ -194,10 +127,15 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) asmlinkage long sys_dup(unsigned int fildes) { int ret = -EBADF; - struct file * file = fget(fildes); - - if (file) - ret = dupfd(file, 0, 0); + struct file *file = fget(fildes); + + if (file) { + ret = get_unused_fd(); + if (ret >= 0) + fd_install(ret, file); + else + fput(file); + } return ret; } @@ -322,8 +260,11 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_DUPFD_CLOEXEC: if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) break; - get_file(filp); - err = dupfd(filp, arg, cmd == F_DUPFD_CLOEXEC); + err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0); + if (err >= 0) { + get_file(filp); + fd_install(err, filp); + } break; case F_GETFD: err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; diff --git a/fs/file.c b/fs/file.c index d8773b19fe47..f313314f996f 100644 --- a/fs/file.c +++ b/fs/file.c @@ -6,6 +6,7 @@ * Manage the dynamic fd arrays in the process files_struct. */ +#include #include #include #include @@ -432,3 +433,63 @@ struct files_struct init_files = { }, .file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock), }; + +/* + * allocate a file descriptor, mark it busy. + */ +int alloc_fd(unsigned start, unsigned flags) +{ + struct files_struct *files = current->files; + unsigned int fd; + int error; + struct fdtable *fdt; + + spin_lock(&files->file_lock); +repeat: + fdt = files_fdtable(files); + fd = start; + if (fd < files->next_fd) + fd = files->next_fd; + + if (fd < fdt->max_fds) + fd = find_next_zero_bit(fdt->open_fds->fds_bits, + fdt->max_fds, fd); + + error = expand_files(files, fd); + if (error < 0) + goto out; + + /* + * If we needed to expand the fs array we + * might have blocked - try again. + */ + if (error) + goto repeat; + + if (start <= files->next_fd) + files->next_fd = fd + 1; + + FD_SET(fd, fdt->open_fds); + if (flags & O_CLOEXEC) + FD_SET(fd, fdt->close_on_exec); + else + FD_CLR(fd, fdt->close_on_exec); + error = fd; +#if 1 + /* Sanity check */ + if (rcu_dereference(fdt->fd[fd]) != NULL) { + printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd); + rcu_assign_pointer(fdt->fd[fd], NULL); + } +#endif + +out: + spin_unlock(&files->file_lock); + return error; +} + +int get_unused_fd(void) +{ + return alloc_fd(0, 0); +} +EXPORT_SYMBOL(get_unused_fd); diff --git a/fs/open.c b/fs/open.c index 52647be277a2..07da9359481c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -963,62 +963,6 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) } EXPORT_SYMBOL(dentry_open); -/* - * Find an empty file descriptor entry, and mark it busy. - */ -int get_unused_fd_flags(int flags) -{ - struct files_struct * files = current->files; - int fd, error; - struct fdtable *fdt; - - spin_lock(&files->file_lock); - -repeat: - fdt = files_fdtable(files); - fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds, - files->next_fd); - - /* Do we need to expand the fd array or fd set? */ - error = expand_files(files, fd); - if (error < 0) - goto out; - - if (error) { - /* - * If we needed to expand the fs array we - * might have blocked - try again. - */ - goto repeat; - } - - FD_SET(fd, fdt->open_fds); - if (flags & O_CLOEXEC) - FD_SET(fd, fdt->close_on_exec); - else - FD_CLR(fd, fdt->close_on_exec); - files->next_fd = fd + 1; -#if 1 - /* Sanity check */ - if (fdt->fd[fd] != NULL) { - printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); - fdt->fd[fd] = NULL; - } -#endif - error = fd; - -out: - spin_unlock(&files->file_lock); - return error; -} - -int get_unused_fd(void) -{ - return get_unused_fd_flags(0); -} - -EXPORT_SYMBOL(get_unused_fd); - static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); diff --git a/include/linux/file.h b/include/linux/file.h index 27c64bdc68c9..a20259e248a5 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -34,8 +34,9 @@ extern struct file *fget(unsigned int fd); extern struct file *fget_light(unsigned int fd, int *fput_needed); extern void set_close_on_exec(unsigned int fd, int flag); extern void put_filp(struct file *); +extern int alloc_fd(unsigned start, unsigned flags); extern int get_unused_fd(void); -extern int get_unused_fd_flags(int flags); +#define get_unused_fd_flags(flags) alloc_fd(0, (flags)) extern void put_unused_fd(unsigned int fd); extern void fd_install(unsigned int fd, struct file *file); -- cgit v1.2.3 From 77e69dac3cefacee939cb107ae9cd520a62338e0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Aug 2008 04:29:18 -0400 Subject: [PATCH] fix races and leaks in vfs_quota_on() users * new helper: vfs_quota_on_path(); equivalent of vfs_quota_on() sans the pathname resolution. * callers of vfs_quota_on() that do their own pathname resolution and checks based on it are switched to vfs_quota_on_path(); that way we avoid the races. * reiserfs leaked dentry/vfsmount references on several failure exits. Signed-off-by: Al Viro --- fs/dquot.c | 33 ++++++++++++++++++++------------- fs/ext3/super.c | 3 ++- fs/ext4/super.c | 3 ++- fs/reiserfs/super.c | 16 +++++++++------- include/linux/quotaops.h | 2 ++ 5 files changed, 35 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/fs/dquot.c b/fs/dquot.c index 1346eebe74ce..8ec4d6cc7633 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type) return ret; } +int vfs_quota_on_path(struct super_block *sb, int type, int format_id, + struct path *path) +{ + int error = security_quota_on(path->dentry); + if (error) + return error; + /* Quota file not on the same filesystem? */ + if (path->mnt->mnt_sb != sb) + error = -EXDEV; + else + error = vfs_quota_on_inode(path->dentry->d_inode, type, + format_id); + return error; +} + /* Actual function called from quotactl() */ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, int remount) @@ -1804,19 +1819,10 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, return vfs_quota_on_remount(sb, type); error = path_lookup(path, LOOKUP_FOLLOW, &nd); - if (error < 0) - return error; - error = security_quota_on(nd.path.dentry); - if (error) - goto out_path; - /* Quota file not on the same filesystem? */ - if (nd.path.mnt->mnt_sb != sb) - error = -EXDEV; - else - error = vfs_quota_on_inode(nd.path.dentry->d_inode, type, - format_id); -out_path: - path_put(&nd.path); + if (!error) { + error = vfs_quota_on_path(sb, type, format_id, &nd.path); + path_put(&nd.path); + } return error; } @@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format); EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dq_data_lock); EXPORT_SYMBOL(vfs_quota_on); +EXPORT_SYMBOL(vfs_quota_on_path); EXPORT_SYMBOL(vfs_quota_on_mount); EXPORT_SYMBOL(vfs_quota_off); EXPORT_SYMBOL(vfs_quota_sync); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 8ddced384674..f38a5afc39a1 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -2810,8 +2810,9 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, journal_unlock_updates(EXT3_SB(sb)->s_journal); } + err = vfs_quota_on_path(sb, type, format_id, &nd.path); path_put(&nd.path); - return vfs_quota_on(sb, type, format_id, path, remount); + return err; } /* Read data from quotafile - avoid pagecache and such because we cannot afford diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b5479b1dff14..1e69f29a8c55 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3352,8 +3352,9 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); } + err = vfs_quota_on_path(sb, type, format_id, &nd.path); path_put(&nd.path); - return vfs_quota_on(sb, type, format_id, path, remount); + return err; } /* Read data from quotafile - avoid pagecache and such because we cannot afford diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 879e54d35c2d..282a13596c70 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -2076,8 +2076,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, return err; /* Quotafile not on the same filesystem? */ if (nd.path.mnt->mnt_sb != sb) { - path_put(&nd.path); - return -EXDEV; + err = -EXDEV; + goto out; } inode = nd.path.dentry->d_inode; /* We must not pack tails for quota files on reiserfs for quota IO to work */ @@ -2087,8 +2087,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, reiserfs_warning(sb, "reiserfs: Unpacking tail of quota file failed" " (%d). Cannot turn on quotas.", err); - path_put(&nd.path); - return -EINVAL; + err = -EINVAL; + goto out; } mark_inode_dirty(inode); } @@ -2109,13 +2109,15 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, /* Just start temporary transaction and finish it */ err = journal_begin(&th, sb, 1); if (err) - return err; + goto out; err = journal_end_sync(&th, sb, 1); if (err) - return err; + goto out; } + err = vfs_quota_on_path(sb, type, format_id, &nd.path); +out: path_put(&nd.path); - return vfs_quota_on(sb, type, format_id, path, 0); + return err; } /* Read data from quotafile - avoid pagecache and such because we cannot afford diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 742187f7a05c..ca6b9b5c8d52 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -43,6 +43,8 @@ int dquot_mark_dquot_dirty(struct dquot *dquot); int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, int remount); +int vfs_quota_on_path(struct super_block *sb, int type, int format_id, + struct path *path); int vfs_quota_on_mount(struct super_block *sb, char *qf_name, int format_id, int type); int vfs_quota_off(struct super_block *sb, int type, int remount); -- cgit v1.2.3 From 8d66bf5481002b0960aa49aed0987c73f5d7816c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Aug 2008 09:05:54 -0400 Subject: [PATCH] pass struct path * to do_add_mount() Signed-off-by: Al Viro --- fs/afs/mntpt.c | 2 +- fs/cifs/cifs_dfs_ref.c | 2 +- fs/namespace.c | 16 ++++++++-------- fs/nfs/namespace.c | 2 +- include/linux/mount.h | 3 ++- 5 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 2f5503902c37..78db4953a800 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -232,7 +232,7 @@ static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) } mntget(newmnt); - err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts); + err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts); switch (err) { case 0: path_put(&nd->path); diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index d82374c9e329..d2c8eef84f3c 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -226,7 +226,7 @@ static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, int err; mntget(newmnt); - err = do_add_mount(newmnt, nd, nd->path.mnt->mnt_flags, mntlist); + err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags, mntlist); switch (err) { case 0: path_put(&nd->path); diff --git a/fs/namespace.c b/fs/namespace.c index 411728c0c8bb..6e283c93b50d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1667,31 +1667,31 @@ static noinline int do_new_mount(struct nameidata *nd, char *type, int flags, if (IS_ERR(mnt)) return PTR_ERR(mnt); - return do_add_mount(mnt, nd, mnt_flags, NULL); + return do_add_mount(mnt, &nd->path, mnt_flags, NULL); } /* * add a mount into a namespace's mount tree * - provide the option of adding the new mount to an expiration list */ -int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, +int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags, struct list_head *fslist) { int err; down_write(&namespace_sem); /* Something was mounted here while we slept */ - while (d_mountpoint(nd->path.dentry) && - follow_down(&nd->path.mnt, &nd->path.dentry)) + while (d_mountpoint(path->dentry) && + follow_down(&path->mnt, &path->dentry)) ; err = -EINVAL; - if (!check_mnt(nd->path.mnt)) + if (!check_mnt(path->mnt)) goto unlock; /* Refuse the same filesystem on the same mount point */ err = -EBUSY; - if (nd->path.mnt->mnt_sb == newmnt->mnt_sb && - nd->path.mnt->mnt_root == nd->path.dentry) + if (path->mnt->mnt_sb == newmnt->mnt_sb && + path->mnt->mnt_root == path->dentry) goto unlock; err = -EINVAL; @@ -1699,7 +1699,7 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, goto unlock; newmnt->mnt_flags = mnt_flags; - if ((err = graft_tree(newmnt, &nd->path))) + if ((err = graft_tree(newmnt, path))) goto unlock; if (fslist) /* add to the specified expiration list */ diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 2f285ef76399..66df08dd1caf 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -129,7 +129,7 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) goto out_err; mntget(mnt); - err = do_add_mount(mnt, nd, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, + err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, &nfs_automount_list); if (err < 0) { mntput(mnt); diff --git a/include/linux/mount.h b/include/linux/mount.h index b5efaa2132ab..30a1d63b6fb5 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -105,7 +105,8 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type, struct nameidata; -extern int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, +struct path; +extern int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags, struct list_head *fslist); extern void mark_mounts_for_expiry(struct list_head *mounts); -- cgit v1.2.3 From 6c5e0c4d518a37e1d5d794c14433e80284415079 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 1 Aug 2008 20:31:32 +0200 Subject: block: add a blk_plug_device_unlocked() that grabs the queue lock blk_plug_device() must be called with the queue lock held, so callers often just grab and release the lock for that purpose. Add a helper that does just that. Signed-off-by: Jens Axboe --- block/blk-core.c | 18 ++++++++++++++++++ include/linux/blkdev.h | 1 + 2 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index fef79ccb2a11..4889eb86a39e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -212,6 +212,24 @@ void blk_plug_device(struct request_queue *q) } EXPORT_SYMBOL(blk_plug_device); +/** + * blk_plug_device_unlocked - plug a device without queue lock held + * @q: The &struct request_queue to plug + * + * Description: + * Like @blk_plug_device(), but grabs the queue lock and disables + * interrupts. + **/ +void blk_plug_device_unlocked(struct request_queue *q) +{ + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + blk_plug_device(q); + spin_unlock_irqrestore(q->queue_lock, flags); +} +EXPORT_SYMBOL(blk_plug_device_unlocked); + /* * remove the queue from the plugged list, if present. called with * queue lock held and interrupts disabled. diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 88d68081a0f1..e61f22be4d0e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -655,6 +655,7 @@ extern struct request *blk_get_request(struct request_queue *, int, gfp_t); extern void blk_insert_request(struct request_queue *, struct request *, int, void *); extern void blk_requeue_request(struct request_queue *, struct request *); extern void blk_plug_device(struct request_queue *); +extern void blk_plug_device_unlocked(struct request_queue *); extern int blk_remove_plug(struct request_queue *); extern void blk_recount_segments(struct request_queue *, struct bio *); extern int scsi_cmd_ioctl(struct file *, struct request_queue *, -- cgit v1.2.3 From 5c7edcd7ee6b77b88252fe4096dce1a46a60c829 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Thu, 31 Jul 2008 02:04:09 -0700 Subject: tracehook: fix exit_signal=0 case My commit 2b2a1ff64afbadac842bbc58c5166962cf4f7664 introduced a regression (sorry about that) for the odd case of exit_signal=0 (e.g. clone_flags=0). This is not a normal use, but it's used by a case in the glibc test suite. Dying with exit_signal=0 sends no signal, but it's supposed to wake up a parent's blocked wait*() calls (unlike the delayed_group_leader case). This fixes tracehook_notify_death() and its caller to distinguish a "signal 0" wakeup from the delayed_group_leader case (with no wakeup). Signed-off-by: Roland McGrath Tested-by: Serge Hallyn Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 21 +++++++++++++-------- kernel/exit.c | 6 +++--- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index b1875582c1a1..12532839f508 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -493,16 +493,21 @@ static inline int tracehook_notify_jctl(int notify, int why) * @death_cookie: value to pass to tracehook_report_death() * @group_dead: nonzero if this was the last thread in the group to die * - * Return the signal number to send our parent with do_notify_parent(), or - * zero to send no signal and leave a zombie, or -1 to self-reap right now. + * A return value >= 0 means call do_notify_parent() with that signal + * number. Negative return value can be %DEATH_REAP to self-reap right + * now, or %DEATH_DELAYED_GROUP_LEADER to a zombie without notifying our + * parent. Note that a return value of 0 means a do_notify_parent() call + * that sends no signal, but still wakes up a parent blocked in wait*(). * * Called with write_lock_irq(&tasklist_lock) held. */ +#define DEATH_REAP -1 +#define DEATH_DELAYED_GROUP_LEADER -2 static inline int tracehook_notify_death(struct task_struct *task, void **death_cookie, int group_dead) { if (task->exit_signal == -1) - return task->ptrace ? SIGCHLD : -1; + return task->ptrace ? SIGCHLD : DEATH_REAP; /* * If something other than our normal parent is ptracing us, then @@ -512,21 +517,21 @@ static inline int tracehook_notify_death(struct task_struct *task, if (thread_group_empty(task) && !ptrace_reparented(task)) return task->exit_signal; - return task->ptrace ? SIGCHLD : 0; + return task->ptrace ? SIGCHLD : DEATH_DELAYED_GROUP_LEADER; } /** * tracehook_report_death - task is dead and ready to be reaped * @task: @current task now exiting - * @signal: signal number sent to parent, or 0 or -1 + * @signal: return value from tracheook_notify_death() * @death_cookie: value passed back from tracehook_notify_death() * @group_dead: nonzero if this was the last thread in the group to die * * Thread has just become a zombie or is about to self-reap. If positive, * @signal is the signal number just sent to the parent (usually %SIGCHLD). - * If @signal is -1, this thread will self-reap. If @signal is 0, this is - * a delayed_group_leader() zombie. The @death_cookie was passed back by - * tracehook_notify_death(). + * If @signal is %DEATH_REAP, this thread will self-reap. If @signal is + * %DEATH_DELAYED_GROUP_LEADER, this is a delayed_group_leader() zombie. + * The @death_cookie was passed back by tracehook_notify_death(). * * If normal reaping is not inhibited, @task->exit_state might be changing * in parallel. diff --git a/kernel/exit.c b/kernel/exit.c index eb4d6470d1d0..38ec40630149 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -911,10 +911,10 @@ static void exit_notify(struct task_struct *tsk, int group_dead) tsk->exit_signal = SIGCHLD; signal = tracehook_notify_death(tsk, &cookie, group_dead); - if (signal > 0) + if (signal >= 0) signal = do_notify_parent(tsk, signal); - tsk->exit_state = signal < 0 ? EXIT_DEAD : EXIT_ZOMBIE; + tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE; /* mt-exec, de_thread() is waiting for us */ if (thread_group_leader(tsk) && @@ -927,7 +927,7 @@ static void exit_notify(struct task_struct *tsk, int group_dead) tracehook_report_death(tsk, signal, cookie, group_dead); /* If the process is dead, release it - nobody will wait for it */ - if (signal < 0) + if (signal == DEATH_REAP) release_task(tsk); } -- cgit v1.2.3 From 4744b43431e8613f920c5cba88346756f53c5165 Mon Sep 17 00:00:00 2001 From: Tim Bird Date: Fri, 1 Aug 2008 14:05:50 -0700 Subject: embedded: fix vc_translate operator precedence This fixes a bug in operator precedence in the newly introduced vc_translate macro. Without this fix, the translation of some characters on the kernel console is garbled. This patch was copied to the e-mail list previously for testing. Now, all reports confirm that it works, so this is an official post for application. Signed-off-by: Tim Bird Signed-off-by: David Woodhouse --- include/linux/vt_kern.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 14c0e91be9b5..8c8119ffee12 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -74,7 +74,7 @@ void con_protect_unimap(struct vc_data *vc, int rdonly); int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); #define vc_translate(vc, c) ((vc)->vc_translate[(c) | \ - (vc)->vc_toggle_meta ? 0x80 : 0]) + ((vc)->vc_toggle_meta ? 0x80 : 0)]) #else #define con_set_trans_old(arg) (0) #define con_get_trans_old(arg) (-EINVAL) -- cgit v1.2.3 From ff4cc1de2401ad44ae084c3f5a9e898af0879520 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Wed, 30 Jul 2008 18:26:58 +0200 Subject: mISDN cleanup user interface The channelmap should have the same size on 32 and 64 bit systems and should not depend on endianess. Thanks to David Woodhouse for spotting this. Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcmulti.c | 6 +++--- drivers/isdn/hardware/mISDN/hfcpci.c | 2 +- drivers/isdn/mISDN/l1oip_core.c | 6 ++---- drivers/isdn/mISDN/socket.c | 4 ++-- include/linux/mISDNif.h | 32 +++++++++++++++++++++++++++----- 5 files changed, 35 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 2649ea55a9e8..10144e871c06 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -3971,7 +3971,7 @@ open_bchannel(struct hfc_multi *hc, struct dchannel *dch, struct bchannel *bch; int ch; - if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0])) + if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; @@ -4587,7 +4587,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) list_add(&bch->ch.list, &dch->dev.bchannels); hc->chan[ch].bch = bch; hc->chan[ch].port = 0; - test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + set_channelmap(bch->nr, dch->dev.channelmap); } /* set optical line type */ if (port[Port_cnt] & 0x001) { @@ -4755,7 +4755,7 @@ init_multi_port(struct hfc_multi *hc, int pt) list_add(&bch->ch.list, &dch->dev.bchannels); hc->chan[i + ch].bch = bch; hc->chan[i + ch].port = pt; - test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + set_channelmap(bch->nr, dch->dev.channelmap); } /* set master clock */ if (port[Port_cnt] & 0x001) { diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 3231814e7efa..9cf5edbb1a9b 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -2056,7 +2056,7 @@ setup_card(struct hfc_pci *card) card->dch.dev.nrbchan = 2; for (i = 0; i < 2; i++) { card->bch[i].nr = i + 1; - test_and_set_bit(i + 1, &card->dch.dev.channelmap[0]); + set_channelmap(i + 1, card->dch.dev.channelmap); card->bch[i].debug = debug; mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM); card->bch[i].hw = card; diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 155b99780c4f..e42150a57780 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -1006,8 +1006,7 @@ open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) struct bchannel *bch; int ch; - if (!test_bit(rq->adr.channel & 0x1f, - &dch->dev.channelmap[rq->adr.channel >> 5])) + if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; @@ -1412,8 +1411,7 @@ init_card(struct l1oip *hc, int pri, int bundle) bch->ch.nr = i + ch; list_add(&bch->ch.list, &dch->dev.bchannels); hc->chan[i + ch].bch = bch; - test_and_set_bit(bch->nr & 0x1f, - &dch->dev.channelmap[bch->nr >> 5]); + set_channelmap(bch->nr, dch->dev.channelmap); } ret = mISDN_register_device(&dch->dev, hc->name); if (ret) diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 4ba4cc364c9e..e5a20f9542d1 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -379,7 +379,7 @@ data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); di.protocol = dev->D.protocol; memcpy(di.channelmap, dev->channelmap, - MISDN_CHMAP_SIZE * 4); + sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; strcpy(di.name, dev->name); if (copy_to_user((void __user *)arg, &di, sizeof(di))) @@ -637,7 +637,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); di.protocol = dev->D.protocol; memcpy(di.channelmap, dev->channelmap, - MISDN_CHMAP_SIZE * 4); + sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; strcpy(di.name, dev->name); if (copy_to_user((void __user *)arg, &di, sizeof(di))) diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 5c948f337817..8f2d60da04e7 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -37,7 +37,7 @@ */ #define MISDN_MAJOR_VERSION 1 #define MISDN_MINOR_VERSION 0 -#define MISDN_RELEASE 18 +#define MISDN_RELEASE 19 /* primitives for information exchange * generell format @@ -242,7 +242,8 @@ struct mISDNhead { #define TEI_SAPI 63 #define CTRL_SAPI 0 -#define MISDN_CHMAP_SIZE 4 +#define MISDN_MAX_CHANNEL 127 +#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3) #define SOL_MISDN 0 @@ -275,11 +276,32 @@ struct mISDN_devinfo { u_int Dprotocols; u_int Bprotocols; u_int protocol; - u_long channelmap[MISDN_CHMAP_SIZE]; + u_char channelmap[MISDN_CHMAP_SIZE]; u_int nrbchan; char name[MISDN_MAX_IDLEN]; }; +static inline int +test_channelmap(u_int nr, u_char *map) +{ + if (nr <= MISDN_MAX_CHANNEL) + return map[nr >> 3] & (1 << (nr & 7)); + else + return 0; +} + +static inline void +set_channelmap(u_int nr, u_char *map) +{ + map[nr >> 3] |= (1 << (nr & 7)); +} + +static inline void +clear_channelmap(u_int nr, u_char *map) +{ + map[nr >> 3] &= ~(1 << (nr & 7)); +} + /* CONTROL_CHANNEL parameters */ #define MISDN_CTRL_GETOP 0x0000 #define MISDN_CTRL_LOOP 0x0001 @@ -405,7 +427,7 @@ struct mISDNdevice { u_int Dprotocols; u_int Bprotocols; u_int nrbchan; - u_long channelmap[MISDN_CHMAP_SIZE]; + u_char channelmap[MISDN_CHMAP_SIZE]; struct list_head bchannels; struct mISDNchannel *teimgr; struct device dev; @@ -430,7 +452,7 @@ struct mISDNstack { #endif }; -/* global alloc/queue dunctions */ +/* global alloc/queue functions */ static inline struct sk_buff * mI_alloc_skb(unsigned int len, gfp_t gfp_mask) -- cgit v1.2.3 From 85ebd00334099fd5d296bcae74a66c943d46686d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sat, 2 Aug 2008 19:12:23 +0200 Subject: Fix IHEX firmware generation/loading Fix both the IHEX firmware generation (len field always null, and EOF marker a byte too short) and loading (struct ihex_binrec needs to be packed to reflect the on-disk structure). Signed-off-by: Marc Zyngier Signed-off-by: David Woodhouse --- firmware/ihex2fw.c | 6 +++--- include/linux/ihex.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/firmware/ihex2fw.c b/firmware/ihex2fw.c index 660b191ed75e..8f7fdaa9e010 100644 --- a/firmware/ihex2fw.c +++ b/firmware/ihex2fw.c @@ -250,19 +250,19 @@ static void file_record(struct ihex_binrec *record) static int output_records(int outfd) { - unsigned char zeroes[5] = {0, 0, 0, 0, 0}; + unsigned char zeroes[6] = {0, 0, 0, 0, 0, 0}; struct ihex_binrec *p = records; while (p) { uint16_t writelen = (p->len + 9) & ~3; p->addr = htonl(p->addr); - p->len = htonl(p->len); + p->len = htons(p->len); write(outfd, &p->addr, writelen); p = p->next; } /* EOF record is zero length, since we don't bother to represent the type field in the binary version */ - write(outfd, zeroes, 5); + write(outfd, zeroes, 6); return 0; } diff --git a/include/linux/ihex.h b/include/linux/ihex.h index 2baace2788a7..31d8629e75a1 100644 --- a/include/linux/ihex.h +++ b/include/linux/ihex.h @@ -18,7 +18,7 @@ struct ihex_binrec { __be32 addr; __be16 len; uint8_t data[0]; -} __attribute__((aligned(4))); +} __attribute__((packed)); /* Find the next record, taking into account the 4-byte alignment */ static inline const struct ihex_binrec * -- cgit v1.2.3 From cf368d2f9aced8adc8bd6b1f04294a71551d5fce Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Sun, 3 Aug 2008 03:03:57 +0400 Subject: drivers/video/console/promcon.c: fix build error drivers/video/console/promcon.c:158: error: implicit declaration of function 'con_protect_unimap' Introduced by commit a29ccf6f823a84d89e1c7aaaf221cf7282022024 ("embedded: fix vc_translate operator precedence"). Signed-off-by: Alexander Beregalov Cc: Tim Bird Signed-off-by: David Woodhouse --- include/linux/vt_kern.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 8c8119ffee12..1c78d56c57e5 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -86,6 +86,7 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); #define con_copy_unimap(d, s) (0) #define con_get_unimap(vc, ct, uct, list) (-EINVAL) #define con_free_unimap(vc) do { ; } while (0) +#define con_protect_unimap(vc, rdonly) do { ; } while (0) #define vc_translate(vc, c) (c) #endif -- cgit v1.2.3 From 63870295de9adb365cd121dab94379b8cfdf986a Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 4 Aug 2008 10:39:46 +0900 Subject: maple: Clean up maple_driver_register/unregister routines. These were completely inconsistent. Clean these up to take a maple_driver pointer directly for consistency. Signed-off-by: Paul Mundt --- drivers/input/keyboard/maple_keyb.c | 6 +++--- drivers/sh/maple/maple.c | 37 ++++++++++++++++++++++++++----------- include/linux/maple.h | 4 +++- 3 files changed, 32 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 42f5d4ec39ab..3f5151a0fd15 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -235,17 +235,17 @@ static struct maple_driver dc_kbd_driver = { .name = "Dreamcast_keyboard", .probe = probe_maple_kbd, .remove = remove_maple_kbd, - }, + }, }; static int __init dc_kbd_init(void) { - return maple_driver_register(&dc_kbd_driver.drv); + return maple_driver_register(&dc_kbd_driver); } static void __exit dc_kbd_exit(void) { - driver_unregister(&dc_kbd_driver.drv); + maple_driver_unregister(&dc_kbd_driver); } module_init(dc_kbd_init); diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index be97789fa5fd..a6b4dc3cfcba 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -2,6 +2,7 @@ * Core maple bus functionality * * Copyright (C) 2007, 2008 Adrian McMenamin + * Copyright (C) 2001 - 2008 Paul Mundt * * Based on 2.4 code by: * @@ -31,7 +32,7 @@ #include #include -MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin"); +MODULE_AUTHOR("Yaegashi Takeshi, Paul Mundt, M. R. Brown, Adrian McMenamin"); MODULE_DESCRIPTION("Maple bus driver for Dreamcast"); MODULE_LICENSE("GPL v2"); MODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}"); @@ -65,19 +66,35 @@ static bool checked[4]; static struct maple_device *baseunits[4]; /** - * maple_driver_register - register a device driver - * automatically makes the driver bus a maple bus - * @drv: the driver to be registered + * maple_driver_register - register a maple driver + * @drv: maple driver to be registered. + * + * Registers the passed in @drv, while updating the bus type. + * Devices with matching function IDs will be automatically probed. */ -int maple_driver_register(struct device_driver *drv) +int maple_driver_register(struct maple_driver *drv) { if (!drv) return -EINVAL; - drv->bus = &maple_bus_type; - return driver_register(drv); + + drv->drv.bus = &maple_bus_type; + + return driver_register(&drv->drv); } EXPORT_SYMBOL_GPL(maple_driver_register); +/** + * maple_driver_unregister - unregister a maple driver. + * @drv: maple driver to unregister. + * + * Cleans up after maple_driver_register(). To be invoked in the exit + * path of any module drivers. + */ +void maple_driver_unregister(struct maple_driver *drv) +{ + driver_unregister(&drv->drv); +} + /* set hardware registers to enable next round of dma */ static void maplebus_dma_reset(void) { @@ -724,11 +741,9 @@ static int maple_get_dma_buffer(void) static int match_maple_bus_driver(struct device *devptr, struct device_driver *drvptr) { - struct maple_driver *maple_drv; - struct maple_device *maple_dev; + struct maple_driver *maple_drv = to_maple_driver(drvptr); + struct maple_device *maple_dev = to_maple_dev(devptr); - maple_drv = container_of(drvptr, struct maple_driver, drv); - maple_dev = container_of(devptr, struct maple_device, dev); /* Trap empty port case */ if (maple_dev->devinfo.function == 0xFFFFFFFF) return 0; diff --git a/include/linux/maple.h b/include/linux/maple.h index c853b1066018..b2b7ce0fb1f7 100644 --- a/include/linux/maple.h +++ b/include/linux/maple.h @@ -70,7 +70,9 @@ void maple_getcond_callback(struct maple_device *dev, void (*callback) (struct mapleq * mq), unsigned long interval, unsigned long function); -int maple_driver_register(struct device_driver *drv); +int maple_driver_register(struct maple_driver *); +void maple_driver_unregister(struct maple_driver *); + int maple_add_packet_sleeps(struct maple_device *mdev, u32 function, u32 command, u32 length, void *data); void maple_clear_dev(struct maple_device *mdev); -- cgit v1.2.3 From 617870632de6739fca0893f3e6648e9ae1bd0ddb Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 4 Aug 2008 10:58:24 +0900 Subject: maple: Kill useless private_data pointer. We can simply wrap in to the dev_set/get_drvdata(), there's no reason to track an extra level of private data on top of the struct device. Signed-off-by: Paul Mundt --- drivers/input/keyboard/maple_keyb.c | 15 ++++++++------- drivers/sh/maple/maple.c | 1 + include/linux/maple.h | 4 +++- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 3f5151a0fd15..22f17a593be7 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -139,7 +139,7 @@ static void dc_scan_kbd(struct dc_kbd *kbd) static void dc_kbd_callback(struct mapleq *mq) { struct maple_device *mapledev = mq->dev; - struct dc_kbd *kbd = mapledev->private_data; + struct dc_kbd *kbd = maple_get_drvdata(mapledev); unsigned long *buf = mq->recvbuf; /* @@ -175,8 +175,6 @@ static int probe_maple_kbd(struct device *dev) goto fail; } - mdev->private_data = kbd; - kbd->dev = idev; memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); @@ -204,27 +202,30 @@ static int probe_maple_kbd(struct device *dev) MAPLE_FUNC_KEYBOARD); mdev->driver = mdrv; + + maple_set_drvdata(mdev, kbd); + return error; fail: input_free_device(idev); kfree(kbd); - mdev->private_data = NULL; + maple_set_drvdata(mdev, NULL); return error; } static int remove_maple_kbd(struct device *dev) { struct maple_device *mdev = to_maple_dev(dev); - struct dc_kbd *kbd; + struct dc_kbd *kbd = maple_get_drvdata(mdev); mutex_lock(&maple_keyb_mutex); - kbd = mdev->private_data; - mdev->private_data = NULL; input_unregister_device(kbd->dev); kfree(kbd); + maple_set_drvdata(mdev, NULL); + mutex_unlock(&maple_keyb_mutex); return 0; } diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index a6b4dc3cfcba..be77a39f224c 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -94,6 +94,7 @@ void maple_driver_unregister(struct maple_driver *drv) { driver_unregister(&drv->drv); } +EXPORT_SYMBOL_GPL(maple_driver_unregister); /* set hardware registers to enable next round of dma */ static void maplebus_dma_reset(void) diff --git a/include/linux/maple.h b/include/linux/maple.h index b2b7ce0fb1f7..c23d3f51ba40 100644 --- a/include/linux/maple.h +++ b/include/linux/maple.h @@ -51,7 +51,6 @@ struct maple_devinfo { struct maple_device { struct maple_driver *driver; struct mapleq *mq; - void *private_data; void (*callback) (struct mapleq * mq); unsigned long when, interval, function; struct maple_devinfo devinfo; @@ -80,4 +79,7 @@ void maple_clear_dev(struct maple_device *mdev); #define to_maple_dev(n) container_of(n, struct maple_device, dev) #define to_maple_driver(n) container_of(n, struct maple_driver, drv) +#define maple_get_drvdata(d) dev_get_drvdata(&(d)->dev) +#define maple_set_drvdata(d,p) dev_set_drvdata(&(d)->dev, (p)) + #endif /* __LINUX_MAPLE_H */ -- cgit v1.2.3 From 98f7dfd86cbbd377e2cbc293529681b914296f68 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 18 Jul 2008 13:52:59 +0800 Subject: mac80211: pass dtim_period to low level driver This patch adds the dtim_period in ieee80211_bss_conf, this allows the low level driver to know the dtim_period, and to plan power save accordingly. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 13 +++++++++++++ include/net/mac80211.h | 4 +++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 11 +++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a1630ba0b87c..7f4df7c7659d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -506,6 +506,19 @@ struct ieee80211_channel_sw_ie { u8 count; } __attribute__ ((packed)); +/** + * struct ieee80211_tim + * + * This structure refers to "Traffic Indication Map information element" + */ +struct ieee80211_tim_ie { + u8 dtim_count; + u8 dtim_period; + u8 bitmap_ctrl; + /* variable size: 1 - 251 bytes */ + u8 virtual_map[0]; +} __attribute__ ((packed)); + struct ieee80211_mgmt { __le16 frame_control; __le16 duration; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b52721008be8..9d99f2e0a204 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -177,9 +177,10 @@ enum ieee80211_bss_change { * @aid: association ID number, valid only when @assoc is true * @use_cts_prot: use CTS protection * @use_short_preamble: use 802.11b short preamble + * @dtim_period: num of beacons before the next DTIM, for PSM * @timestamp: beacon timestamp * @beacon_int: beacon interval - * @assoc_capability: capabbilities taken from assoc resp + * @assoc_capability: capabilities taken from assoc resp * @assoc_ht: association in HT mode * @ht_conf: ht capabilities * @ht_bss_conf: ht extended capabilities @@ -191,6 +192,7 @@ struct ieee80211_bss_conf { /* erp related data */ bool use_cts_prot; bool use_short_preamble; + u8 dtim_period; u16 beacon_int; u16 assoc_capability; u64 timestamp; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a2e200f9811e..ec59345af65b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -82,6 +82,7 @@ struct ieee80211_sta_bss { u8 bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 dtim_period; u16 capability; /* host byte order */ enum ieee80211_band band; int freq; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index acb04133a95d..591e6331c427 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -551,6 +551,7 @@ static void ieee80211_set_associated(struct net_device *dev, /* set timing information */ sdata->bss_conf.beacon_int = bss->beacon_int; sdata->bss_conf.timestamp = bss->timestamp; + sdata->bss_conf.dtim_period = bss->dtim_period; changed |= ieee80211_handle_bss_capability(sdata, bss); @@ -2688,6 +2689,16 @@ static void ieee80211_rx_bss_info(struct net_device *dev, bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); + if (elems->tim) { + struct ieee80211_tim_ie *tim_ie = + (struct ieee80211_tim_ie *)elems->tim; + bss->dtim_period = tim_ie->dtim_period; + } + + /* set default value for buggy APs */ + if (!elems->tim || bss->dtim_period == 0) + bss->dtim_period = 1; + bss->supp_rates_len = 0; if (elems->supp_rates) { clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; -- cgit v1.2.3 From 1a3f7d98e5f50f21ce6fb1406a35531d9596c5c6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 4 Aug 2008 16:50:38 -0700 Subject: Revert "UFS: add const to parser token table" This reverts commit f9247273cb69ba101877e946d2d83044409cc8c5 (and fb2e405fc1fc8b20d9c78eaa1c7fd5a297efde43 - "fix fs/nfs/nfsroot.c compilation" - that fixed a missed conversion). The changes cause problems for at least the sparc build. Let's re-do them when the exact issues are resolved. Requested-by: Andrew Morton Requested-by: Steven Whitehouse Cc: David Miller Signed-off-by: Linus Torvalds --- fs/nfs/nfsroot.c | 2 +- fs/ufs/super.c | 2 +- include/linux/parser.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 8478fc25daee..46763d1cd397 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -127,7 +127,7 @@ enum { Opt_err }; -static match_table_t __initconst tokens = { +static match_table_t __initdata tokens = { {Opt_port, "port=%u"}, {Opt_rsize, "rsize=%u"}, {Opt_wsize, "wsize=%u"}, diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 3e30e40aa24d..3141969b456d 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1233,7 +1233,7 @@ static int ufs_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct ufs_sb_info *sbi = UFS_SB(vfs->mnt_sb); unsigned mval = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE; - const struct match_token *tp = tokens; + struct match_token *tp = tokens; while (tp->token != Opt_onerror_panic && tp->token != mval) ++tp; diff --git a/include/linux/parser.h b/include/linux/parser.h index cc554ca8bc78..7dcd05075756 100644 --- a/include/linux/parser.h +++ b/include/linux/parser.h @@ -14,7 +14,7 @@ struct match_token { const char *pattern; }; -typedef const struct match_token match_table_t[]; +typedef struct match_token match_table_t[]; /* Maximum number of arguments that match_token will find in a pattern */ enum {MAX_OPT_ARGS = 3}; -- cgit v1.2.3 From 115a326c1e5cab457924356123bbfd7d783ecf9d Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Mon, 4 Aug 2008 13:56:01 -0700 Subject: tracehook: kerneldoc fix My last change to tracehook.h made it confuse the kerneldoc parser. Move the #define's before the comment so it's happy again. Signed-off-by: Roland McGrath Acked-by: Randy Dunlap Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 12532839f508..ab3ef7aefa95 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -487,6 +487,9 @@ static inline int tracehook_notify_jctl(int notify, int why) return notify || (current->ptrace & PT_PTRACED); } +#define DEATH_REAP -1 +#define DEATH_DELAYED_GROUP_LEADER -2 + /** * tracehook_notify_death - task is dead, ready to notify parent * @task: @current task now exiting @@ -501,8 +504,6 @@ static inline int tracehook_notify_jctl(int notify, int why) * * Called with write_lock_irq(&tasklist_lock) held. */ -#define DEATH_REAP -1 -#define DEATH_DELAYED_GROUP_LEADER -2 static inline int tracehook_notify_death(struct task_struct *task, void **death_cookie, int group_dead) { -- cgit v1.2.3 From 529ae9aaa08378cfe2a4350bded76f32cc8ff0ce Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 2 Aug 2008 12:01:03 +0200 Subject: mm: rename page trylock Converting page lock to new locking bitops requires a change of page flag operation naming, so we might as well convert it to something nicer (!TestSetPageLocked_Lock => trylock_page, SetPageLocked => set_page_locked). This also facilitates lockdeping of page lock. Signed-off-by: Nick Piggin Acked-by: KOSAKI Motohiro Acked-by: Peter Zijlstra Acked-by: Andrew Morton Acked-by: Benjamin Herrenschmidt Signed-off-by: Linus Torvalds --- drivers/scsi/sg.c | 2 +- fs/afs/write.c | 2 +- fs/cifs/file.c | 2 +- fs/jbd/commit.c | 4 +-- fs/jbd2/commit.c | 2 +- fs/reiserfs/journal.c | 2 +- fs/splice.c | 2 +- fs/xfs/linux-2.6/xfs_aops.c | 4 +-- include/linux/page-flags.h | 2 +- include/linux/pagemap.h | 67 +++++++++++++++++++++++++++------------------ mm/filemap.c | 12 ++++---- mm/memory.c | 2 +- mm/migrate.c | 4 +-- mm/rmap.c | 2 +- mm/shmem.c | 4 +-- mm/swap.c | 2 +- mm/swap_state.c | 8 +++--- mm/swapfile.c | 2 +- mm/truncate.c | 4 +-- mm/vmscan.c | 4 +-- 20 files changed, 74 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index d3b8ebb83776..3d36270a8b4d 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1747,7 +1747,7 @@ st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, */ flush_dcache_page(pages[i]); /* ?? Is locking needed? I don't think so */ - /* if (TestSetPageLocked(pages[i])) + /* if (!trylock_page(pages[i])) goto out_unlock; */ } diff --git a/fs/afs/write.c b/fs/afs/write.c index 9a849ad3c489..065b4e10681a 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -404,7 +404,7 @@ static int afs_write_back_from_locked_page(struct afs_writeback *wb, page = pages[loop]; if (page->index > wb->last) break; - if (TestSetPageLocked(page)) + if (!trylock_page(page)) break; if (!PageDirty(page) || page_private(page) != (unsigned long) wb) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0aac824371a5..e692c42f24b5 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1280,7 +1280,7 @@ retry: if (first < 0) lock_page(page); - else if (TestSetPageLocked(page)) + else if (!trylock_page(page)) break; if (unlikely(page->mapping != mapping)) { diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 2eccbfaa1d48..81a9ad7177ca 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -63,7 +63,7 @@ static void release_buffer_page(struct buffer_head *bh) goto nope; /* OK, it's a truncated page */ - if (TestSetPageLocked(page)) + if (!trylock_page(page)) goto nope; page_cache_get(page); @@ -446,7 +446,7 @@ void journal_commit_transaction(journal_t *journal) spin_lock(&journal->j_list_lock); } if (unlikely(!buffer_uptodate(bh))) { - if (TestSetPageLocked(bh->b_page)) { + if (!trylock_page(bh->b_page)) { spin_unlock(&journal->j_list_lock); lock_page(bh->b_page); spin_lock(&journal->j_list_lock); diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index adf0395f318e..f2ad061e95ec 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -67,7 +67,7 @@ static void release_buffer_page(struct buffer_head *bh) goto nope; /* OK, it's a truncated page */ - if (TestSetPageLocked(page)) + if (!trylock_page(page)) goto nope; page_cache_get(page); diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index c8f60ee183b5..ce2208b27118 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -627,7 +627,7 @@ static int journal_list_still_alive(struct super_block *s, static void release_buffer_page(struct buffer_head *bh) { struct page *page = bh->b_page; - if (!page->mapping && !TestSetPageLocked(page)) { + if (!page->mapping && trylock_page(page)) { page_cache_get(page); put_bh(bh); if (!page->mapping) diff --git a/fs/splice.c b/fs/splice.c index b30311ba8af6..1bbc6f4bb09c 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -371,7 +371,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, * for an in-flight io page */ if (flags & SPLICE_F_NONBLOCK) { - if (TestSetPageLocked(page)) { + if (!trylock_page(page)) { error = -EAGAIN; break; } diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 0b211cba1909..fa73179233ad 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -675,7 +675,7 @@ xfs_probe_cluster( } else pg_offset = PAGE_CACHE_SIZE; - if (page->index == tindex && !TestSetPageLocked(page)) { + if (page->index == tindex && trylock_page(page)) { pg_len = xfs_probe_page(page, pg_offset, mapped); unlock_page(page); } @@ -759,7 +759,7 @@ xfs_convert_page( if (page->index != tindex) goto fail; - if (TestSetPageLocked(page)) + if (!trylock_page(page)) goto fail; if (PageWriteback(page)) goto fail_unlock_page; diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 25aaccdb2f26..c74d3e875314 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -163,7 +163,7 @@ static inline int Page##uname(struct page *page) \ struct page; /* forward declaration */ -PAGEFLAG(Locked, locked) TESTSCFLAG(Locked, locked) +TESTPAGEFLAG(Locked, locked) PAGEFLAG(Error, error) PAGEFLAG(Referenced, referenced) TESTCLEARFLAG(Referenced, referenced) PAGEFLAG(Dirty, dirty) TESTSCFLAG(Dirty, dirty) __CLEARPAGEFLAG(Dirty, dirty) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 69ed3cb1197a..5da31c12101c 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -250,29 +250,6 @@ static inline struct page *read_mapping_page(struct address_space *mapping, return read_cache_page(mapping, index, filler, data); } -int add_to_page_cache_locked(struct page *page, struct address_space *mapping, - pgoff_t index, gfp_t gfp_mask); -int add_to_page_cache_lru(struct page *page, struct address_space *mapping, - pgoff_t index, gfp_t gfp_mask); -extern void remove_from_page_cache(struct page *page); -extern void __remove_from_page_cache(struct page *page); - -/* - * Like add_to_page_cache_locked, but used to add newly allocated pages: - * the page is new, so we can just run SetPageLocked() against it. - */ -static inline int add_to_page_cache(struct page *page, - struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) -{ - int error; - - SetPageLocked(page); - error = add_to_page_cache_locked(page, mapping, offset, gfp_mask); - if (unlikely(error)) - ClearPageLocked(page); - return error; -} - /* * Return byte-offset into filesystem object for page. */ @@ -294,13 +271,28 @@ extern int __lock_page_killable(struct page *page); extern void __lock_page_nosync(struct page *page); extern void unlock_page(struct page *page); +static inline void set_page_locked(struct page *page) +{ + set_bit(PG_locked, &page->flags); +} + +static inline void clear_page_locked(struct page *page) +{ + clear_bit(PG_locked, &page->flags); +} + +static inline int trylock_page(struct page *page) +{ + return !test_and_set_bit(PG_locked, &page->flags); +} + /* * lock_page may only be called if we have the page's inode pinned. */ static inline void lock_page(struct page *page) { might_sleep(); - if (TestSetPageLocked(page)) + if (!trylock_page(page)) __lock_page(page); } @@ -312,7 +304,7 @@ static inline void lock_page(struct page *page) static inline int lock_page_killable(struct page *page) { might_sleep(); - if (TestSetPageLocked(page)) + if (!trylock_page(page)) return __lock_page_killable(page); return 0; } @@ -324,7 +316,7 @@ static inline int lock_page_killable(struct page *page) static inline void lock_page_nosync(struct page *page) { might_sleep(); - if (TestSetPageLocked(page)) + if (!trylock_page(page)) __lock_page_nosync(page); } @@ -409,4 +401,27 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size) return ret; } +int add_to_page_cache_locked(struct page *page, struct address_space *mapping, + pgoff_t index, gfp_t gfp_mask); +int add_to_page_cache_lru(struct page *page, struct address_space *mapping, + pgoff_t index, gfp_t gfp_mask); +extern void remove_from_page_cache(struct page *page); +extern void __remove_from_page_cache(struct page *page); + +/* + * Like add_to_page_cache_locked, but used to add newly allocated pages: + * the page is new, so we can just run set_page_locked() against it. + */ +static inline int add_to_page_cache(struct page *page, + struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) +{ + int error; + + set_page_locked(page); + error = add_to_page_cache_locked(page, mapping, offset, gfp_mask); + if (unlikely(error)) + clear_page_locked(page); + return error; +} + #endif /* _LINUX_PAGEMAP_H */ diff --git a/mm/filemap.c b/mm/filemap.c index d97d1ad55473..54e968650855 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -558,14 +558,14 @@ EXPORT_SYMBOL(wait_on_page_bit); * But that's OK - sleepers in wait_on_page_writeback() just go back to sleep. * * The first mb is necessary to safely close the critical section opened by the - * TestSetPageLocked(), the second mb is necessary to enforce ordering between - * the clear_bit and the read of the waitqueue (to avoid SMP races with a - * parallel wait_on_page_locked()). + * test_and_set_bit() to lock the page; the second mb is necessary to enforce + * ordering between the clear_bit and the read of the waitqueue (to avoid SMP + * races with a parallel wait_on_page_locked()). */ void unlock_page(struct page *page) { smp_mb__before_clear_bit(); - if (!TestClearPageLocked(page)) + if (!test_and_clear_bit(PG_locked, &page->flags)) BUG(); smp_mb__after_clear_bit(); wake_up_page(page, PG_locked); @@ -931,7 +931,7 @@ grab_cache_page_nowait(struct address_space *mapping, pgoff_t index) struct page *page = find_get_page(mapping, index); if (page) { - if (!TestSetPageLocked(page)) + if (trylock_page(page)) return page; page_cache_release(page); return NULL; @@ -1027,7 +1027,7 @@ find_page: if (inode->i_blkbits == PAGE_CACHE_SHIFT || !mapping->a_ops->is_partially_uptodate) goto page_not_up_to_date; - if (TestSetPageLocked(page)) + if (!trylock_page(page)) goto page_not_up_to_date; if (!mapping->a_ops->is_partially_uptodate(page, desc, offset)) diff --git a/mm/memory.c b/mm/memory.c index a472bcd4b061..1002f473f497 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1789,7 +1789,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, * not dirty accountable. */ if (PageAnon(old_page)) { - if (!TestSetPageLocked(old_page)) { + if (trylock_page(old_page)) { reuse = can_share_swap_page(old_page); unlock_page(old_page); } diff --git a/mm/migrate.c b/mm/migrate.c index 153572fb60b8..2a80136b23bb 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -605,7 +605,7 @@ static int move_to_new_page(struct page *newpage, struct page *page) * establishing additional references. We are the only one * holding a reference to the new page at this point. */ - if (TestSetPageLocked(newpage)) + if (!trylock_page(newpage)) BUG(); /* Prepare mapping for the new page.*/ @@ -667,7 +667,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, BUG_ON(charge); rc = -EAGAIN; - if (TestSetPageLocked(page)) { + if (!trylock_page(page)) { if (!force) goto move_newpage; lock_page(page); diff --git a/mm/rmap.c b/mm/rmap.c index 94a5246a3f98..1ea4e6fcee77 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -422,7 +422,7 @@ int page_referenced(struct page *page, int is_locked, referenced += page_referenced_anon(page, mem_cont); else if (is_locked) referenced += page_referenced_file(page, mem_cont); - else if (TestSetPageLocked(page)) + else if (!trylock_page(page)) referenced++; else { if (page->mapping) diff --git a/mm/shmem.c b/mm/shmem.c index c1e5a3b4f758..04fb4f1ab88e 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1265,7 +1265,7 @@ repeat: } /* We have to do this with page locked to prevent races */ - if (TestSetPageLocked(swappage)) { + if (!trylock_page(swappage)) { shmem_swp_unmap(entry); spin_unlock(&info->lock); wait_on_page_locked(swappage); @@ -1329,7 +1329,7 @@ repeat: shmem_swp_unmap(entry); filepage = find_get_page(mapping, idx); if (filepage && - (!PageUptodate(filepage) || TestSetPageLocked(filepage))) { + (!PageUptodate(filepage) || !trylock_page(filepage))) { spin_unlock(&info->lock); wait_on_page_locked(filepage); page_cache_release(filepage); diff --git a/mm/swap.c b/mm/swap.c index 7417a2adbe50..9e0cb3118079 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -444,7 +444,7 @@ void pagevec_strip(struct pagevec *pvec) for (i = 0; i < pagevec_count(pvec); i++) { struct page *page = pvec->pages[i]; - if (PagePrivate(page) && !TestSetPageLocked(page)) { + if (PagePrivate(page) && trylock_page(page)) { if (PagePrivate(page)) try_to_release_page(page, 0); unlock_page(page); diff --git a/mm/swap_state.c b/mm/swap_state.c index b8035b055129..167cf2dc8a03 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -201,7 +201,7 @@ void delete_from_swap_cache(struct page *page) */ static inline void free_swap_cache(struct page *page) { - if (PageSwapCache(page) && !TestSetPageLocked(page)) { + if (PageSwapCache(page) && trylock_page(page)) { remove_exclusive_swap_page(page); unlock_page(page); } @@ -302,9 +302,9 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, * re-using the just freed swap entry for an existing page. * May fail (-ENOMEM) if radix-tree node allocation failed. */ - SetPageLocked(new_page); + set_page_locked(new_page); err = add_to_swap_cache(new_page, entry, gfp_mask & GFP_KERNEL); - if (!err) { + if (likely(!err)) { /* * Initiate read into locked page and return. */ @@ -312,7 +312,7 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, swap_readpage(NULL, new_page); return new_page; } - ClearPageLocked(new_page); + clear_page_locked(new_page); swap_free(entry); } while (err != -ENOMEM); diff --git a/mm/swapfile.c b/mm/swapfile.c index bb7f79641f9e..1e330f2998fa 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -403,7 +403,7 @@ void free_swap_and_cache(swp_entry_t entry) if (p) { if (swap_entry_free(p, swp_offset(entry)) == 1) { page = find_get_page(&swapper_space, entry.val); - if (page && unlikely(TestSetPageLocked(page))) { + if (page && unlikely(!trylock_page(page))) { page_cache_release(page); page = NULL; } diff --git a/mm/truncate.c b/mm/truncate.c index 894e9a70699f..250505091d37 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -187,7 +187,7 @@ void truncate_inode_pages_range(struct address_space *mapping, if (page_index > next) next = page_index; next++; - if (TestSetPageLocked(page)) + if (!trylock_page(page)) continue; if (PageWriteback(page)) { unlock_page(page); @@ -280,7 +280,7 @@ unsigned long __invalidate_mapping_pages(struct address_space *mapping, pgoff_t index; int lock_failed; - lock_failed = TestSetPageLocked(page); + lock_failed = !trylock_page(page); /* * We really shouldn't be looking at the ->index of an diff --git a/mm/vmscan.c b/mm/vmscan.c index 75be453628bf..1ff1a58e7c10 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -496,7 +496,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, page = lru_to_page(page_list); list_del(&page->lru); - if (TestSetPageLocked(page)) + if (!trylock_page(page)) goto keep; VM_BUG_ON(PageActive(page)); @@ -582,7 +582,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, * A synchronous write - probably a ramdisk. Go * ahead and try to reclaim the page. */ - if (TestSetPageLocked(page)) + if (!trylock_page(page)) goto keep; if (PageDirty(page) || PageWriteback(page)) goto keep_locked; -- cgit v1.2.3 From ca5de404ff036a29b25e9a83f6919c9f606c5841 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 2 Aug 2008 12:02:13 +0200 Subject: fs: rename buffer trylock Like the page lock change, this also requires name change, so convert the raw test_and_set bitop to a trylock. Signed-off-by: Nick Piggin Signed-off-by: Linus Torvalds --- fs/buffer.c | 4 ++-- fs/jbd/commit.c | 2 +- fs/ntfs/aops.c | 2 +- fs/ntfs/compress.c | 2 +- fs/ntfs/mft.c | 4 ++-- fs/reiserfs/inode.c | 2 +- fs/reiserfs/journal.c | 4 ++-- fs/xfs/linux-2.6/xfs_aops.c | 2 +- include/linux/buffer_head.h | 8 ++++++-- 9 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index 4dbe52948e8f..38653e36e225 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1720,7 +1720,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, */ if (wbc->sync_mode != WB_SYNC_NONE || !wbc->nonblocking) { lock_buffer(bh); - } else if (test_set_buffer_locked(bh)) { + } else if (!trylock_buffer(bh)) { redirty_page_for_writepage(wbc, page); continue; } @@ -3000,7 +3000,7 @@ void ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) if (rw == SWRITE || rw == SWRITE_SYNC) lock_buffer(bh); - else if (test_set_buffer_locked(bh)) + else if (!trylock_buffer(bh)) continue; if (rw == WRITE || rw == SWRITE || rw == SWRITE_SYNC) { diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 81a9ad7177ca..ae08c057e751 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -221,7 +221,7 @@ write_out_data: * blocking lock_buffer(). */ if (buffer_dirty(bh)) { - if (test_set_buffer_locked(bh)) { + if (!trylock_buffer(bh)) { BUFFER_TRACE(bh, "needs blocking lock"); spin_unlock(&journal->j_list_lock); /* Write out all data to prevent deadlocks */ diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 00e9ccde8e42..b38f944f0667 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -1194,7 +1194,7 @@ lock_retry_remap: tbh = bhs[i]; if (!tbh) continue; - if (unlikely(test_set_buffer_locked(tbh))) + if (!trylock_buffer(tbh)) BUG(); /* The buffer dirty state is now irrelevant, just clean it. */ clear_buffer_dirty(tbh); diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c index 33ff314cc507..9669541d0119 100644 --- a/fs/ntfs/compress.c +++ b/fs/ntfs/compress.c @@ -665,7 +665,7 @@ lock_retry_remap: for (i = 0; i < nr_bhs; i++) { struct buffer_head *tbh = bhs[i]; - if (unlikely(test_set_buffer_locked(tbh))) + if (!trylock_buffer(tbh)) continue; if (unlikely(buffer_uptodate(tbh))) { unlock_buffer(tbh); diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index 790defb847e7..17d32ca6bc35 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -586,7 +586,7 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, for (i_bhs = 0; i_bhs < nr_bhs; i_bhs++) { struct buffer_head *tbh = bhs[i_bhs]; - if (unlikely(test_set_buffer_locked(tbh))) + if (!trylock_buffer(tbh)) BUG(); BUG_ON(!buffer_uptodate(tbh)); clear_buffer_dirty(tbh); @@ -779,7 +779,7 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync) for (i_bhs = 0; i_bhs < nr_bhs; i_bhs++) { struct buffer_head *tbh = bhs[i_bhs]; - if (unlikely(test_set_buffer_locked(tbh))) + if (!trylock_buffer(tbh)) BUG(); BUG_ON(!buffer_uptodate(tbh)); clear_buffer_dirty(tbh); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 192269698a8a..5699171212ae 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2435,7 +2435,7 @@ static int reiserfs_write_full_page(struct page *page, if (wbc->sync_mode != WB_SYNC_NONE || !wbc->nonblocking) { lock_buffer(bh); } else { - if (test_set_buffer_locked(bh)) { + if (!trylock_buffer(bh)) { redirty_page_for_writepage(wbc, page); continue; } diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index ce2208b27118..c21df71943a6 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -855,7 +855,7 @@ static int write_ordered_buffers(spinlock_t * lock, jh = JH_ENTRY(list->next); bh = jh->bh; get_bh(bh); - if (test_set_buffer_locked(bh)) { + if (!trylock_buffer(bh)) { if (!buffer_dirty(bh)) { list_move(&jh->list, &tmp); goto loop_next; @@ -3871,7 +3871,7 @@ int reiserfs_prepare_for_journal(struct super_block *p_s_sb, { PROC_INFO_INC(p_s_sb, journal.prepare); - if (test_set_buffer_locked(bh)) { + if (!trylock_buffer(bh)) { if (!wait) return 0; lock_buffer(bh); diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index fa73179233ad..fa47e43b8b41 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1104,7 +1104,7 @@ xfs_page_state_convert( * that we are writing into for the first time. */ type = IOMAP_NEW; - if (!test_and_set_bit(BH_Lock, &bh->b_state)) { + if (trylock_buffer(bh)) { ASSERT(buffer_mapped(bh)); if (iomap_valid) all_bh = 1; diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 50cfe8ceb478..eadaab44015f 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -115,7 +115,6 @@ BUFFER_FNS(Uptodate, uptodate) BUFFER_FNS(Dirty, dirty) TAS_BUFFER_FNS(Dirty, dirty) BUFFER_FNS(Lock, locked) -TAS_BUFFER_FNS(Lock, locked) BUFFER_FNS(Req, req) TAS_BUFFER_FNS(Req, req) BUFFER_FNS(Mapped, mapped) @@ -321,10 +320,15 @@ static inline void wait_on_buffer(struct buffer_head *bh) __wait_on_buffer(bh); } +static inline int trylock_buffer(struct buffer_head *bh) +{ + return likely(!test_and_set_bit(BH_Lock, &bh->b_state)); +} + static inline void lock_buffer(struct buffer_head *bh) { might_sleep(); - if (test_set_buffer_locked(bh)) + if (!trylock_buffer(bh)) __lock_buffer(bh); } -- cgit v1.2.3 From 378a2f090f7a478704a372a4869b8a9ac206234e Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Mon, 4 Aug 2008 22:31:03 -0700 Subject: net_sched: Add qdisc __NET_XMIT_STOLEN flag Patrick McHardy noticed: "The other problem that affects all qdiscs supporting actions is TC_ACT_QUEUED/TC_ACT_STOLEN getting mapped to NET_XMIT_SUCCESS even though the packet is not queued, corrupting upper qdiscs' qlen counters." and later explained: "The reason why it translates it at all seems to be to not increase the drops counter. Within a single qdisc this could be avoided by other means easily, upper qdiscs would still increase the counter when we return anything besides NET_XMIT_SUCCESS though. This means we need a new NET_XMIT return value to indicate this to the upper qdiscs. So I'd suggest to introduce NET_XMIT_STOLEN, return that to upper qdiscs and translate it to NET_XMIT_SUCCESS in dev_queue_xmit, similar to NET_XMIT_BYPASS." David Miller noticed: "Maybe these NET_XMIT_* values being passed around should be a set of bits. They could be composed of base meanings, combined with specific attributes. So you could say "NET_XMIT_DROP | __NET_XMIT_NO_DROP_COUNT" The attributes get masked out by the top-level ->enqueue() caller, such that the base meanings are the only thing that make their way up into the stack. If it's only about communication within the qdisc tree, let's simply code it that way." This patch is trying to realize these ideas. Signed-off-by: Jarek Poplawski Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/sch_generic.h | 14 +++++++++++++- net/sched/sch_atm.c | 12 +++++++----- net/sched/sch_cbq.c | 23 +++++++++++++++-------- net/sched/sch_dsmark.c | 8 +++++--- net/sched/sch_hfsc.c | 8 +++++--- net/sched/sch_htb.c | 18 +++++++++++------- net/sched/sch_netem.c | 3 ++- net/sched/sch_prio.c | 8 +++++--- net/sched/sch_red.c | 2 +- net/sched/sch_sfq.c | 2 +- net/sched/sch_tbf.c | 3 ++- 12 files changed, 68 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ee583f642a9f..abbf5d52ec86 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -64,6 +64,7 @@ struct wireless_dev; #define NET_XMIT_BYPASS 4 /* packet does not leave via dequeue; (TC use only - dev_queue_xmit returns this as NET_XMIT_SUCCESS) */ +#define NET_XMIT_MASK 0xFFFF /* qdisc flags in net/sch_generic.h */ /* Backlog congestion levels */ #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index c5bb13065051..f15b045a85e9 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -343,6 +343,18 @@ static inline unsigned int qdisc_pkt_len(struct sk_buff *skb) return qdisc_skb_cb(skb)->pkt_len; } +#ifdef CONFIG_NET_CLS_ACT +/* additional qdisc xmit flags */ +enum net_xmit_qdisc_t { + __NET_XMIT_STOLEN = 0x00010000, +}; + +#define net_xmit_drop_count(e) ((e) & __NET_XMIT_STOLEN ? 0 : 1) + +#else +#define net_xmit_drop_count(e) (1) +#endif + static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) { #ifdef CONFIG_NET_SCHED @@ -355,7 +367,7 @@ static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch) { qdisc_skb_cb(skb)->pkt_len = skb->len; - return qdisc_enqueue(skb, sch); + return qdisc_enqueue(skb, sch) & NET_XMIT_MASK; } static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 6b517b9dac5b..27dd773481bc 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -415,7 +415,7 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) case TC_ACT_QUEUED: case TC_ACT_STOLEN: kfree_skb(skb); - return NET_XMIT_SUCCESS; + return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: kfree_skb(skb); goto drop; @@ -432,9 +432,11 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) ret = qdisc_enqueue(skb, flow->q); if (ret != 0) { drop: __maybe_unused - sch->qstats.drops++; - if (flow) - flow->qstats.drops++; + if (net_xmit_drop_count(ret)) { + sch->qstats.drops++; + if (flow) + flow->qstats.drops++; + } return ret; } sch->bstats.bytes += qdisc_pkt_len(skb); @@ -530,7 +532,7 @@ static int atm_tc_requeue(struct sk_buff *skb, struct Qdisc *sch) if (!ret) { sch->q.qlen++; sch->qstats.requeues++; - } else { + } else if (net_xmit_drop_count(ret)) { sch->qstats.drops++; p->link.qstats.drops++; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 14954bf4a683..765ae5659000 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -256,7 +256,7 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) switch (result) { case TC_ACT_QUEUED: case TC_ACT_STOLEN: - *qerr = NET_XMIT_SUCCESS; + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return NULL; case TC_ACT_RECLASSIFY: @@ -397,9 +397,11 @@ cbq_enqueue(struct sk_buff *skb, struct Qdisc *sch) return ret; } - sch->qstats.drops++; - cbq_mark_toplevel(q, cl); - cl->qstats.drops++; + if (net_xmit_drop_count(ret)) { + sch->qstats.drops++; + cbq_mark_toplevel(q, cl); + cl->qstats.drops++; + } return ret; } @@ -430,8 +432,10 @@ cbq_requeue(struct sk_buff *skb, struct Qdisc *sch) cbq_activate_class(cl); return 0; } - sch->qstats.drops++; - cl->qstats.drops++; + if (net_xmit_drop_count(ret)) { + sch->qstats.drops++; + cl->qstats.drops++; + } return ret; } @@ -664,13 +668,15 @@ static int cbq_reshape_fail(struct sk_buff *skb, struct Qdisc *child) q->rx_class = NULL; if (cl && (cl = cbq_reclassify(skb, cl)) != NULL) { + int ret; cbq_mark_toplevel(q, cl); q->rx_class = cl; cl->q->__parent = sch; - if (qdisc_enqueue(skb, cl->q) == 0) { + ret = qdisc_enqueue(skb, cl->q); + if (ret == NET_XMIT_SUCCESS) { sch->q.qlen++; sch->bstats.packets++; sch->bstats.bytes += qdisc_pkt_len(skb); @@ -678,7 +684,8 @@ static int cbq_reshape_fail(struct sk_buff *skb, struct Qdisc *child) cbq_activate_class(cl); return 0; } - sch->qstats.drops++; + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; return 0; } diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index a935676987e2..7170275d9f99 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -236,7 +236,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch) case TC_ACT_QUEUED: case TC_ACT_STOLEN: kfree_skb(skb); - return NET_XMIT_SUCCESS; + return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: goto drop; @@ -254,7 +254,8 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch) err = qdisc_enqueue(skb, p->q); if (err != NET_XMIT_SUCCESS) { - sch->qstats.drops++; + if (net_xmit_drop_count(err)) + sch->qstats.drops++; return err; } @@ -321,7 +322,8 @@ static int dsmark_requeue(struct sk_buff *skb, struct Qdisc *sch) err = p->q->ops->requeue(skb, p->q); if (err != NET_XMIT_SUCCESS) { - sch->qstats.drops++; + if (net_xmit_drop_count(err)) + sch->qstats.drops++; return err; } diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 0ae7d19dcba8..5cf9ae716118 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1166,7 +1166,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) switch (result) { case TC_ACT_QUEUED: case TC_ACT_STOLEN: - *qerr = NET_XMIT_SUCCESS; + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return NULL; } @@ -1586,8 +1586,10 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch) err = qdisc_enqueue(skb, cl->qdisc); if (unlikely(err != NET_XMIT_SUCCESS)) { - cl->qstats.drops++; - sch->qstats.drops++; + if (net_xmit_drop_count(err)) { + cl->qstats.drops++; + sch->qstats.drops++; + } return err; } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 75a40951c4f2..538d79b489ae 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -221,7 +221,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch, switch (result) { case TC_ACT_QUEUED: case TC_ACT_STOLEN: - *qerr = NET_XMIT_SUCCESS; + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return NULL; } @@ -572,9 +572,11 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch) kfree_skb(skb); return ret; #endif - } else if (qdisc_enqueue(skb, cl->un.leaf.q) != NET_XMIT_SUCCESS) { - sch->qstats.drops++; - cl->qstats.drops++; + } else if ((ret = qdisc_enqueue(skb, cl->un.leaf.q)) != NET_XMIT_SUCCESS) { + if (net_xmit_drop_count(ret)) { + sch->qstats.drops++; + cl->qstats.drops++; + } return NET_XMIT_DROP; } else { cl->bstats.packets += @@ -615,10 +617,12 @@ static int htb_requeue(struct sk_buff *skb, struct Qdisc *sch) kfree_skb(skb); return ret; #endif - } else if (cl->un.leaf.q->ops->requeue(skb, cl->un.leaf.q) != + } else if ((ret = cl->un.leaf.q->ops->requeue(skb, cl->un.leaf.q)) != NET_XMIT_SUCCESS) { - sch->qstats.drops++; - cl->qstats.drops++; + if (net_xmit_drop_count(ret)) { + sch->qstats.drops++; + cl->qstats.drops++; + } return NET_XMIT_DROP; } else htb_activate(q, cl); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index a59085700678..6cd6f2bc749e 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -240,8 +240,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) sch->q.qlen++; sch->bstats.bytes += qdisc_pkt_len(skb); sch->bstats.packets++; - } else + } else if (net_xmit_drop_count(ret)) { sch->qstats.drops++; + } pr_debug("netem: enqueue ret %d\n", ret); return ret; diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index f849243eb095..adb1a52b77d3 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -45,7 +45,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) switch (err) { case TC_ACT_STOLEN: case TC_ACT_QUEUED: - *qerr = NET_XMIT_SUCCESS; + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return NULL; } @@ -88,7 +88,8 @@ prio_enqueue(struct sk_buff *skb, struct Qdisc *sch) sch->q.qlen++; return NET_XMIT_SUCCESS; } - sch->qstats.drops++; + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; return ret; } @@ -114,7 +115,8 @@ prio_requeue(struct sk_buff *skb, struct Qdisc* sch) sch->qstats.requeues++; return 0; } - sch->qstats.drops++; + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; return NET_XMIT_DROP; } diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 3f2d1d7f3bbd..5da05839e225 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -97,7 +97,7 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch) sch->bstats.bytes += qdisc_pkt_len(skb); sch->bstats.packets++; sch->q.qlen++; - } else { + } else if (net_xmit_drop_count(ret)) { q->stats.pdrop++; sch->qstats.drops++; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 8589da666568..3a456e1b829a 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -178,7 +178,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, switch (result) { case TC_ACT_STOLEN: case TC_ACT_QUEUED: - *qerr = NET_XMIT_SUCCESS; + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return 0; } diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index b296672f7632..7d3b7ff3bf07 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -135,7 +135,8 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch) ret = qdisc_enqueue(skb, q->qdisc); if (ret != 0) { - sch->qstats.drops++; + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; return ret; } -- cgit v1.2.3 From cc6533e98a7f3cb7fce9d740da49195c7aa523a4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 4 Aug 2008 23:04:08 -0700 Subject: net: Kill plain NET_XMIT_BYPASS. dst_input() was doing something completely absurd, looping on skb->dst->input() if NET_XMIT_BYPASS was seen, but these functions never return such an error. And as a result plain ole' NET_XMIT_BYPASS has no more references and can be completely killed off. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 --- include/net/dst.h | 12 +----------- 2 files changed, 1 insertion(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index abbf5d52ec86..488c56e649b5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -61,9 +61,6 @@ struct wireless_dev; #define NET_XMIT_DROP 1 /* skb dropped */ #define NET_XMIT_CN 2 /* congestion notification */ #define NET_XMIT_POLICED 3 /* skb is shot by police */ -#define NET_XMIT_BYPASS 4 /* packet does not leave via dequeue; - (TC use only - dev_queue_xmit - returns this as NET_XMIT_SUCCESS) */ #define NET_XMIT_MASK 0xFFFF /* qdisc flags in net/sch_generic.h */ /* Backlog congestion levels */ diff --git a/include/net/dst.h b/include/net/dst.h index c5c318a628f8..8a8b71e5f3f1 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -252,17 +252,7 @@ static inline int dst_output(struct sk_buff *skb) /* Input packet from network to transport. */ static inline int dst_input(struct sk_buff *skb) { - int err; - - for (;;) { - err = skb->dst->input(skb); - - if (likely(err == 0)) - return err; - /* Oh, Jamal... Seems, I will not forgive you this mess. :-) */ - if (unlikely(err != NET_XMIT_BYPASS)) - return err; - } + return skb->dst->input(skb); } static inline struct dst_entry *dst_check(struct dst_entry *dst, u32 cookie) -- cgit v1.2.3 From 5595cffc8248e4672c5803547445e85e4053c8fc Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 5 Aug 2008 09:28:47 +0300 Subject: SLUB: dynamic per-cache MIN_PARTIAL This patch changes the static MIN_PARTIAL to a dynamic per-cache ->min_partial value that is calculated from object size. The bigger the object size, the more pages we keep on the partial list. I tested SLAB, SLUB, and SLUB with this patch on Jens Axboe's 'netio' example script of the fio benchmarking tool. The script stresses the networking subsystem which should also give a fairly good beating of kmalloc() et al. To run the test yourself, first clone the fio repository: git clone git://git.kernel.dk/fio.git and then run the following command n times on your machine: time ./fio examples/netio The results on my 2-way 64-bit x86 machine are as follows: [ the minimum, maximum, and average are captured from 50 individual runs ] real time (seconds) min max avg sd SLAB 22.76 23.38 22.98 0.17 SLUB 22.80 25.78 23.46 0.72 SLUB (dynamic) 22.74 23.54 23.00 0.20 sys time (seconds) min max avg sd SLAB 6.90 8.28 7.70 0.28 SLUB 7.42 16.95 8.89 2.28 SLUB (dynamic) 7.17 8.64 7.73 0.29 user time (seconds) min max avg sd SLAB 36.89 38.11 37.50 0.29 SLUB 30.85 37.99 37.06 1.67 SLUB (dynamic) 36.75 38.07 37.59 0.32 As you can see from the above numbers, this patch brings SLUB to the same level as SLAB for this particular workload fixing a ~2% regression. I'd expect this change to help similar workloads that allocate a lot of objects that are close to the size of a page. Cc: Matthew Wilcox Cc: Andrew Morton Acked-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slub_def.h | 1 + mm/slub.c | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 5bad61a93f65..2f5c16b1aacd 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -46,6 +46,7 @@ struct kmem_cache_cpu { struct kmem_cache_node { spinlock_t list_lock; /* Protect partial list and nr_partial */ unsigned long nr_partial; + unsigned long min_partial; struct list_head partial; #ifdef CONFIG_SLUB_DEBUG atomic_long_t nr_slabs; diff --git a/mm/slub.c b/mm/slub.c index c26d4c36fba9..4f5b96149458 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1329,7 +1329,7 @@ static struct page *get_any_partial(struct kmem_cache *s, gfp_t flags) n = get_node(s, zone_to_nid(zone)); if (n && cpuset_zone_allowed_hardwall(zone, flags) && - n->nr_partial > MIN_PARTIAL) { + n->nr_partial > n->min_partial) { page = get_partial_node(n); if (page) return page; @@ -1381,7 +1381,7 @@ static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail) slab_unlock(page); } else { stat(c, DEACTIVATE_EMPTY); - if (n->nr_partial < MIN_PARTIAL) { + if (n->nr_partial < n->min_partial) { /* * Adding an empty slab to the partial slabs in order * to avoid page allocator overhead. This slab needs @@ -1913,9 +1913,21 @@ static void init_kmem_cache_cpu(struct kmem_cache *s, #endif } -static void init_kmem_cache_node(struct kmem_cache_node *n) +static void +init_kmem_cache_node(struct kmem_cache_node *n, struct kmem_cache *s) { n->nr_partial = 0; + + /* + * The larger the object size is, the more pages we want on the partial + * list to avoid pounding the page allocator excessively. + */ + n->min_partial = ilog2(s->size); + if (n->min_partial < MIN_PARTIAL) + n->min_partial = MIN_PARTIAL; + else if (n->min_partial > MAX_PARTIAL) + n->min_partial = MAX_PARTIAL; + spin_lock_init(&n->list_lock); INIT_LIST_HEAD(&n->partial); #ifdef CONFIG_SLUB_DEBUG @@ -2087,7 +2099,7 @@ static struct kmem_cache_node *early_kmem_cache_node_alloc(gfp_t gfpflags, init_object(kmalloc_caches, n, 1); init_tracking(kmalloc_caches, n); #endif - init_kmem_cache_node(n); + init_kmem_cache_node(n, kmalloc_caches); inc_slabs_node(kmalloc_caches, node, page->objects); /* @@ -2144,7 +2156,7 @@ static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t gfpflags) } s->node[node] = n; - init_kmem_cache_node(n); + init_kmem_cache_node(n, s); } return 1; } @@ -2155,7 +2167,7 @@ static void free_kmem_cache_nodes(struct kmem_cache *s) static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t gfpflags) { - init_kmem_cache_node(&s->local_node); + init_kmem_cache_node(&s->local_node, s); return 1; } #endif @@ -2889,7 +2901,7 @@ static int slab_mem_going_online_callback(void *arg) ret = -ENOMEM; goto out; } - init_kmem_cache_node(n); + init_kmem_cache_node(n, s); s->node[nid] = n; } out: -- cgit v1.2.3 From 39b986a6c73434d122967dc86efb295ab9a28437 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 5 Aug 2008 18:16:57 +0200 Subject: ide: sanitize struct ide_port_ops documentation (take 2) v2: Add missing '@'-s. (Noticed by Randy Dunlap) Cc: Randy Dunlap Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index b846bc44a27e..b1fb15f10a00 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -509,24 +509,33 @@ struct ide_tp_ops { extern const struct ide_tp_ops default_tp_ops; +/** + * struct ide_port_ops - IDE port operations + * + * @init_dev: host specific initialization of a device + * @set_pio_mode: routine to program host for PIO mode + * @set_dma_mode: routine to program host for DMA mode + * @selectproc: tweaks hardware to select drive + * @reset_poll: chipset polling based on hba specifics + * @pre_reset: chipset specific changes to default for device-hba resets + * @resetproc: routine to reset controller after a disk reset + * @maskproc: special host masking for drive selection + * @quirkproc: check host's drive quirk list + * + * @mdma_filter: filter MDMA modes + * @udma_filter: filter UDMA modes + * + * @cable_detect: detect cable type + */ struct ide_port_ops { - /* host specific initialization of a device */ void (*init_dev)(ide_drive_t *); - /* routine to program host for PIO mode */ void (*set_pio_mode)(ide_drive_t *, const u8); - /* routine to program host for DMA mode */ void (*set_dma_mode)(ide_drive_t *, const u8); - /* tweaks hardware to select drive */ void (*selectproc)(ide_drive_t *); - /* chipset polling based on hba specifics */ int (*reset_poll)(ide_drive_t *); - /* chipset specific changes to default for device-hba resets */ void (*pre_reset)(ide_drive_t *); - /* routine to reset controller after a disk reset */ void (*resetproc)(ide_drive_t *); - /* special host masking for drive selection */ void (*maskproc)(ide_drive_t *, int); - /* check host's drive quirk list */ void (*quirkproc)(ide_drive_t *); u8 (*mdma_filter)(ide_drive_t *); -- cgit v1.2.3 From c5bfc3757f1d843a8e1261840c1f53c5062f8e92 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 5 Aug 2008 18:17:01 +0200 Subject: ide: remove CONFIG_IDE_MAX_HWIFS The benefits of a user settable CONFIG_IDE_MAX_HWIFS have become pretty tiny and are no longer considered worth the trouble of an own option. Simply always #define MAX_HWIFS to 10. Signed-off-by: Adrian Bunk Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 10 ---------- include/linux/ide.h | 13 +------------ 2 files changed, 1 insertion(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 130ef64b44f7..a34758d29516 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -54,16 +54,6 @@ menuconfig IDE if IDE -config IDE_MAX_HWIFS - int "Max IDE interfaces" - depends on ALPHA || SUPERH || IA64 || EMBEDDED - range 1 10 - default 4 - help - This is the maximum number of IDE hardware interfaces that will - be supported by the driver. Make sure it is at least as high as - the number of IDE interfaces in your system. - config BLK_DEV_IDE tristate "Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support" ---help--- diff --git a/include/linux/ide.h b/include/linux/ide.h index b1fb15f10a00..87c12ed96954 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -219,18 +219,7 @@ static inline int __ide_default_irq(unsigned long base) #include #endif -#ifndef MAX_HWIFS -#if defined(CONFIG_BLACKFIN) || defined(CONFIG_H8300) || defined(CONFIG_XTENSA) -# define MAX_HWIFS 1 -#else -# define MAX_HWIFS 10 -#endif -#endif - -#if !defined(MAX_HWIFS) || defined(CONFIG_EMBEDDED) -#undef MAX_HWIFS -#define MAX_HWIFS CONFIG_IDE_MAX_HWIFS -#endif +#define MAX_HWIFS 10 /* Currently only m68k, apus and m8xx need it */ #ifndef IDE_ARCH_ACK_INTR -- cgit v1.2.3 From c6e2bee26eee190b20cd87e71b288bca6a5357a4 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Tue, 5 Aug 2008 13:01:05 -0700 Subject: kdump: report actual value of VMCOREINFO_OSRELEASE in VMCOREINFO The current implementation reports the structure name as VMCOREINFO_OSRELEASE in VMCOREINFO, e.g. VMCOREINFO_OSRELEASE=init_uts_ns.name.release That doesn't make sense because it's always the same. Instead, use the value, e.g. VMCOREINFO_OSRELEASE=2.6.26-rc3 That's also what the 'makedumpfile -g' does. Signed-off-by: Bernhard Walle Cc: "Ken'ichi Ohmichi" Acked-by: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kexec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 82f88a8a827b..32110cede64f 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -130,8 +130,8 @@ void vmcoreinfo_append_str(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); unsigned long paddr_vmcoreinfo_note(void); -#define VMCOREINFO_OSRELEASE(name) \ - vmcoreinfo_append_str("OSRELEASE=%s\n", #name) +#define VMCOREINFO_OSRELEASE(value) \ + vmcoreinfo_append_str("OSRELEASE=%s\n", value) #define VMCOREINFO_PAGESIZE(value) \ vmcoreinfo_append_str("PAGESIZE=%ld\n", value) #define VMCOREINFO_SYMBOL(name) \ -- cgit v1.2.3 From 60cadec9da7b6c91aca51f408c828f7e74a68379 Mon Sep 17 00:00:00 2001 From: Shadi Ammouri Date: Tue, 5 Aug 2008 13:01:09 -0700 Subject: spi: new orion_spi driver This adds an SPI driver for the SPI controller found in various Marvell Orion ARM SoCs. It currently supports only one slave, which must use SPI mode 0. [dbrownell@users.sourceforge.net: cleanups, meet specs, pass "sparse"] Signed-off-by: Shadi Ammouri Signed-off-by: Saeed Bishara Signed-off-by: Lennert Buytenhek Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/orion_spi.c | 574 ++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/orion_spi.h | 17 ++ 4 files changed, 598 insertions(+) create mode 100644 drivers/spi/orion_spi.c create mode 100644 include/linux/spi/orion_spi.h (limited to 'include/linux') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2303521b4f09..b9d0efb6803f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -149,6 +149,12 @@ config SPI_OMAP24XX SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI (McSPI) modules. +config SPI_ORION + tristate "Orion SPI master (EXPERIMENTAL)" + depends on PLAT_ORION && EXPERIMENTAL + help + This enables using the SPI master controller on the Orion chips. + config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on ARCH_PXA && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 7fca043ce723..ccf18de34e1e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o +obj-$(CONFIG_SPI_ORION) += orion_spi.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o diff --git a/drivers/spi/orion_spi.c b/drivers/spi/orion_spi.c new file mode 100644 index 000000000000..c4eaacd6e553 --- /dev/null +++ b/drivers/spi/orion_spi.c @@ -0,0 +1,574 @@ +/* + * orion_spi.c -- Marvell Orion SPI controller driver + * + * Author: Shadi Ammouri + * Copyright (C) 2007-2008 Marvell Ltd. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "orion_spi" + +#define ORION_NUM_CHIPSELECTS 1 /* only one slave is supported*/ +#define ORION_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ + +#define ORION_SPI_IF_CTRL_REG 0x00 +#define ORION_SPI_IF_CONFIG_REG 0x04 +#define ORION_SPI_DATA_OUT_REG 0x08 +#define ORION_SPI_DATA_IN_REG 0x0c +#define ORION_SPI_INT_CAUSE_REG 0x10 + +#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5) +#define ORION_SPI_CLK_PRESCALE_MASK 0x1F + +struct orion_spi { + struct work_struct work; + + /* Lock access to transfer list. */ + spinlock_t lock; + + struct list_head msg_queue; + struct spi_master *master; + void __iomem *base; + unsigned int max_speed; + unsigned int min_speed; + struct orion_spi_info *spi_info; +}; + +static struct workqueue_struct *orion_spi_wq; + +static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) +{ + return orion_spi->base + reg; +} + +static inline void +orion_spi_setbits(struct orion_spi *orion_spi, u32 reg, u32 mask) +{ + void __iomem *reg_addr = spi_reg(orion_spi, reg); + u32 val; + + val = readl(reg_addr); + val |= mask; + writel(val, reg_addr); +} + +static inline void +orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask) +{ + void __iomem *reg_addr = spi_reg(orion_spi, reg); + u32 val; + + val = readl(reg_addr); + val &= ~mask; + writel(val, reg_addr); +} + +static int orion_spi_set_transfer_size(struct orion_spi *orion_spi, int size) +{ + if (size == 16) { + orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG, + ORION_SPI_IF_8_16_BIT_MODE); + } else if (size == 8) { + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG, + ORION_SPI_IF_8_16_BIT_MODE); + } else { + pr_debug("Bad bits per word value %d (only 8 or 16 are " + "allowed).\n", size); + return -EINVAL; + } + + return 0; +} + +static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) +{ + u32 tclk_hz; + u32 rate; + u32 prescale; + u32 reg; + struct orion_spi *orion_spi; + + orion_spi = spi_master_get_devdata(spi->master); + + tclk_hz = orion_spi->spi_info->tclk; + + /* + * the supported rates are: 4,6,8...30 + * round up as we look for equal or less speed + */ + rate = DIV_ROUND_UP(tclk_hz, speed); + rate = roundup(rate, 2); + + /* check if requested speed is too small */ + if (rate > 30) + return -EINVAL; + + if (rate < 4) + rate = 4; + + /* Convert the rate to SPI clock divisor value. */ + prescale = 0x10 + rate/2; + + reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); + reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale); + writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); + + return 0; +} + +/* + * called only when no transfer is active on the bus + */ +static int +orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct orion_spi *orion_spi; + unsigned int speed = spi->max_speed_hz; + unsigned int bits_per_word = spi->bits_per_word; + int rc; + + orion_spi = spi_master_get_devdata(spi->master); + + if ((t != NULL) && t->speed_hz) + speed = t->speed_hz; + + if ((t != NULL) && t->bits_per_word) + bits_per_word = t->bits_per_word; + + rc = orion_spi_baudrate_set(spi, speed); + if (rc) + return rc; + + return orion_spi_set_transfer_size(orion_spi, bits_per_word); +} + +static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable) +{ + if (enable) + orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); + else + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); +} + +static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi) +{ + int i; + + for (i = 0; i < ORION_SPI_WAIT_RDY_MAX_LOOP; i++) { + if (readl(spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG))) + return 1; + else + udelay(1); + } + + return -1; +} + +static inline int +orion_spi_write_read_8bit(struct spi_device *spi, + const u8 **tx_buf, u8 **rx_buf) +{ + void __iomem *tx_reg, *rx_reg, *int_reg; + struct orion_spi *orion_spi; + + orion_spi = spi_master_get_devdata(spi->master); + tx_reg = spi_reg(orion_spi, ORION_SPI_DATA_OUT_REG); + rx_reg = spi_reg(orion_spi, ORION_SPI_DATA_IN_REG); + int_reg = spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG); + + /* clear the interrupt cause register */ + writel(0x0, int_reg); + + if (tx_buf && *tx_buf) + writel(*(*tx_buf)++, tx_reg); + else + writel(0, tx_reg); + + if (orion_spi_wait_till_ready(orion_spi) < 0) { + dev_err(&spi->dev, "TXS timed out\n"); + return -1; + } + + if (rx_buf && *rx_buf) + *(*rx_buf)++ = readl(rx_reg); + + return 1; +} + +static inline int +orion_spi_write_read_16bit(struct spi_device *spi, + const u16 **tx_buf, u16 **rx_buf) +{ + void __iomem *tx_reg, *rx_reg, *int_reg; + struct orion_spi *orion_spi; + + orion_spi = spi_master_get_devdata(spi->master); + tx_reg = spi_reg(orion_spi, ORION_SPI_DATA_OUT_REG); + rx_reg = spi_reg(orion_spi, ORION_SPI_DATA_IN_REG); + int_reg = spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG); + + /* clear the interrupt cause register */ + writel(0x0, int_reg); + + if (tx_buf && *tx_buf) + writel(__cpu_to_le16(get_unaligned((*tx_buf)++)), tx_reg); + else + writel(0, tx_reg); + + if (orion_spi_wait_till_ready(orion_spi) < 0) { + dev_err(&spi->dev, "TXS timed out\n"); + return -1; + } + + if (rx_buf && *rx_buf) + put_unaligned(__le16_to_cpu(readl(rx_reg)), (*rx_buf)++); + + return 1; +} + +static unsigned int +orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct orion_spi *orion_spi; + unsigned int count; + int word_len; + + orion_spi = spi_master_get_devdata(spi->master); + word_len = spi->bits_per_word; + count = xfer->len; + + if (word_len == 8) { + const u8 *tx = xfer->tx_buf; + u8 *rx = xfer->rx_buf; + + do { + if (orion_spi_write_read_8bit(spi, &tx, &rx) < 0) + goto out; + count--; + } while (count); + } else if (word_len == 16) { + const u16 *tx = xfer->tx_buf; + u16 *rx = xfer->rx_buf; + + do { + if (orion_spi_write_read_16bit(spi, &tx, &rx) < 0) + goto out; + count -= 2; + } while (count); + } + +out: + return xfer->len - count; +} + + +static void orion_spi_work(struct work_struct *work) +{ + struct orion_spi *orion_spi = + container_of(work, struct orion_spi, work); + + spin_lock_irq(&orion_spi->lock); + while (!list_empty(&orion_spi->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int par_override = 0; + int status = 0; + int cs_active = 0; + + m = container_of(orion_spi->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + spin_unlock_irq(&orion_spi->lock); + + spi = m->spi; + + /* Load defaults */ + status = orion_spi_setup_transfer(spi, NULL); + + if (status < 0) + goto msg_done; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (par_override || t->speed_hz || t->bits_per_word) { + par_override = 1; + status = orion_spi_setup_transfer(spi, t); + if (status < 0) + break; + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; + } + + if (!cs_active) { + orion_spi_set_cs(orion_spi, 1); + cs_active = 1; + } + + if (t->len) + m->actual_length += + orion_spi_write_read(spi, t); + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (t->cs_change) { + orion_spi_set_cs(orion_spi, 0); + cs_active = 0; + } + } + +msg_done: + if (cs_active) + orion_spi_set_cs(orion_spi, 0); + + m->status = status; + m->complete(m->context); + + spin_lock_irq(&orion_spi->lock); + } + + spin_unlock_irq(&orion_spi->lock); +} + +static int __init orion_spi_reset(struct orion_spi *orion_spi) +{ + /* Verify that the CS is deasserted */ + orion_spi_set_cs(orion_spi, 0); + + return 0; +} + +static int orion_spi_setup(struct spi_device *spi) +{ + struct orion_spi *orion_spi; + + orion_spi = spi_master_get_devdata(spi->master); + + if (spi->mode) { + dev_err(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode); + return -EINVAL; + } + + if (spi->bits_per_word == 0) + spi->bits_per_word = 8; + + if ((spi->max_speed_hz == 0) + || (spi->max_speed_hz > orion_spi->max_speed)) + spi->max_speed_hz = orion_spi->max_speed; + + if (spi->max_speed_hz < orion_spi->min_speed) { + dev_err(&spi->dev, "setup: requested speed too low %d Hz\n", + spi->max_speed_hz); + return -EINVAL; + } + + /* + * baudrate & width will be set orion_spi_setup_transfer + */ + return 0; +} + +static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct orion_spi *orion_spi; + struct spi_transfer *t = NULL; + unsigned long flags; + + m->actual_length = 0; + m->status = 0; + + /* reject invalid messages and transfers */ + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + orion_spi = spi_master_get_devdata(spi->master); + + list_for_each_entry(t, &m->transfers, transfer_list) { + unsigned int bits_per_word = spi->bits_per_word; + + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + dev_err(&spi->dev, + "message rejected : " + "invalid transfer data buffers\n"); + goto msg_rejected; + } + + if ((t != NULL) && t->bits_per_word) + bits_per_word = t->bits_per_word; + + if ((bits_per_word != 8) && (bits_per_word != 16)) { + dev_err(&spi->dev, + "message rejected : " + "invalid transfer bits_per_word (%d bits)\n", + bits_per_word); + goto msg_rejected; + } + /*make sure buffer length is even when working in 16 bit mode*/ + if ((t != NULL) && (t->bits_per_word == 16) && (t->len & 1)) { + dev_err(&spi->dev, + "message rejected : " + "odd data length (%d) while in 16 bit mode\n", + t->len); + goto msg_rejected; + } + + if (t->speed_hz < orion_spi->min_speed) { + dev_err(&spi->dev, + "message rejected : " + "device min speed (%d Hz) exceeds " + "required transfer speed (%d Hz)\n", + orion_spi->min_speed, t->speed_hz); + goto msg_rejected; + } + } + + + spin_lock_irqsave(&orion_spi->lock, flags); + list_add_tail(&m->queue, &orion_spi->msg_queue); + queue_work(orion_spi_wq, &orion_spi->work); + spin_unlock_irqrestore(&orion_spi->lock, flags); + + return 0; +msg_rejected: + /* Message rejected and not queued */ + m->status = -EINVAL; + if (m->complete) + m->complete(m->context); + return -EINVAL; +} + +static int __init orion_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct orion_spi *spi; + struct resource *r; + struct orion_spi_info *spi_info; + int status = 0; + + spi_info = pdev->dev.platform_data; + + master = spi_alloc_master(&pdev->dev, sizeof *spi); + if (master == NULL) { + dev_dbg(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + if (pdev->id != -1) + master->bus_num = pdev->id; + + master->setup = orion_spi_setup; + master->transfer = orion_spi_transfer; + master->num_chipselect = ORION_NUM_CHIPSELECTS; + + dev_set_drvdata(&pdev->dev, master); + + spi = spi_master_get_devdata(master); + spi->master = master; + spi->spi_info = spi_info; + + spi->max_speed = DIV_ROUND_UP(spi_info->tclk, 4); + spi->min_speed = DIV_ROUND_UP(spi_info->tclk, 30); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + status = -ENODEV; + goto out; + } + + if (!request_mem_region(r->start, (r->end - r->start) + 1, + pdev->dev.bus_id)) { + status = -EBUSY; + goto out; + } + spi->base = ioremap(r->start, SZ_1K); + + INIT_WORK(&spi->work, orion_spi_work); + + spin_lock_init(&spi->lock); + INIT_LIST_HEAD(&spi->msg_queue); + + if (orion_spi_reset(spi) < 0) + goto out_rel_mem; + + status = spi_register_master(master); + if (status < 0) + goto out_rel_mem; + + return status; + +out_rel_mem: + release_mem_region(r->start, (r->end - r->start) + 1); + +out: + spi_master_put(master); + return status; +} + + +static int __exit orion_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct orion_spi *spi; + struct resource *r; + + master = dev_get_drvdata(&pdev->dev); + spi = spi_master_get_devdata(master); + + cancel_work_sync(&spi->work); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, (r->end - r->start) + 1); + + spi_unregister_master(master); + + return 0; +} + +MODULE_ALIAS("platform:" DRIVER_NAME); + +static struct platform_driver orion_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .remove = __exit_p(orion_spi_remove), +}; + +static int __init orion_spi_init(void) +{ + orion_spi_wq = create_singlethread_workqueue( + orion_spi_driver.driver.name); + if (orion_spi_wq == NULL) + return -ENOMEM; + + return platform_driver_probe(&orion_spi_driver, orion_spi_probe); +} +module_init(orion_spi_init); + +static void __exit orion_spi_exit(void) +{ + flush_workqueue(orion_spi_wq); + platform_driver_unregister(&orion_spi_driver); + + destroy_workqueue(orion_spi_wq); +} +module_exit(orion_spi_exit); + +MODULE_DESCRIPTION("Orion SPI driver"); +MODULE_AUTHOR("Shadi Ammouri "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/orion_spi.h b/include/linux/spi/orion_spi.h new file mode 100644 index 000000000000..b4d9fa6f797c --- /dev/null +++ b/include/linux/spi/orion_spi.h @@ -0,0 +1,17 @@ +/* + * orion_spi.h + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __LINUX_SPI_ORION_SPI_H +#define __LINUX_SPI_ORION_SPI_H + +struct orion_spi_info { + u32 tclk; /* no support yet */ +}; + + +#endif -- cgit v1.2.3 From f6ac436dcc4c34709bcde355f3f2254ac0a183d4 Mon Sep 17 00:00:00 2001 From: Mark Asselstine Date: Tue, 5 Aug 2008 13:01:24 -0700 Subject: Remove the deprecated cli() sti() functions These functions have been deprecated for some time now but remained until all legacy callers could be removed. With a few commits in 2.6.26 this has happened so now we can remove these deprecated functions. Signed-off-by: Mark Asselstine Reviewed-by: Matthew Wilcox Cc: Alan Cox Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/00-INDEX | 2 - Documentation/cli-sti-removal.txt | 133 -------------------------------------- include/linux/interrupt.h | 29 --------- 3 files changed, 164 deletions(-) delete mode 100644 Documentation/cli-sti-removal.txt (limited to 'include/linux') diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 6de71308a906..5b5aba404aac 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -89,8 +89,6 @@ cciss.txt - info, major/minor #'s for Compaq's SMART Array Controllers. cdrom/ - directory with information on the CD-ROM drivers that Linux has. -cli-sti-removal.txt - - cli()/sti() removal guide. computone.txt - info on Computone Intelliport II/Plus Multiport Serial Driver. connector/ diff --git a/Documentation/cli-sti-removal.txt b/Documentation/cli-sti-removal.txt deleted file mode 100644 index 60932b02fcb3..000000000000 --- a/Documentation/cli-sti-removal.txt +++ /dev/null @@ -1,133 +0,0 @@ - -#### cli()/sti() removal guide, started by Ingo Molnar - - -as of 2.5.28, five popular macros have been removed on SMP, and -are being phased out on UP: - - cli(), sti(), save_flags(flags), save_flags_cli(flags), restore_flags(flags) - -until now it was possible to protect driver code against interrupt -handlers via a cli(), but from now on other, more lightweight methods -have to be used for synchronization, such as spinlocks or semaphores. - -for example, driver code that used to do something like: - - struct driver_data; - - irq_handler (...) - { - .... - driver_data.finish = 1; - driver_data.new_work = 0; - .... - } - - ... - - ioctl_func (...) - { - ... - cli(); - ... - driver_data.finish = 0; - driver_data.new_work = 2; - ... - sti(); - ... - } - -was SMP-correct because the cli() function ensured that no -interrupt handler (amongst them the above irq_handler()) function -would execute while the cli()-ed section is executing. - -but from now on a more direct method of locking has to be used: - - DEFINE_SPINLOCK(driver_lock); - struct driver_data; - - irq_handler (...) - { - unsigned long flags; - .... - spin_lock_irqsave(&driver_lock, flags); - .... - driver_data.finish = 1; - driver_data.new_work = 0; - .... - spin_unlock_irqrestore(&driver_lock, flags); - .... - } - - ... - - ioctl_func (...) - { - ... - spin_lock_irq(&driver_lock); - ... - driver_data.finish = 0; - driver_data.new_work = 2; - ... - spin_unlock_irq(&driver_lock); - ... - } - -the above code has a number of advantages: - -- the locking relation is easier to understand - actual lock usage - pinpoints the critical sections. cli() usage is too opaque. - Easier to understand means it's easier to debug. - -- it's faster, because spinlocks are faster to acquire than the - potentially heavily-used IRQ lock. Furthermore, your driver does - not have to wait eg. for a big heavy SCSI interrupt to finish, - because the driver_lock spinlock is only used by your driver. - cli() on the other hand was used by many drivers, and extended - the critical section to the whole IRQ handler function - creating - serious lock contention. - - -to make the transition easier, we've still kept the cli(), sti(), -save_flags(), save_flags_cli() and restore_flags() macros defined -on UP systems - but their usage will be phased out until 2.6 is -released. - -drivers that want to disable local interrupts (interrupts on the -current CPU), can use the following five macros: - - local_irq_disable(), local_irq_enable(), local_save_flags(flags), - local_irq_save(flags), local_irq_restore(flags) - -but beware, their meaning and semantics are much simpler, far from -that of the old cli(), sti(), save_flags(flags) and restore_flags(flags) -SMP meaning: - - local_irq_disable() => turn local IRQs off - - local_irq_enable() => turn local IRQs on - - local_save_flags(flags) => save the current IRQ state into flags. The - state can be on or off. (on some - architectures there's even more bits in it.) - - local_irq_save(flags) => save the current IRQ state into flags and - disable interrupts. - - local_irq_restore(flags) => restore the IRQ state from flags. - -(local_irq_save can save both irqs on and irqs off state, and -local_irq_restore can restore into both irqs on and irqs off state.) - -another related change is that synchronize_irq() now takes a parameter: -synchronize_irq(irq). This change too has the purpose of making SMP -synchronization more lightweight - this way you can wait for your own -interrupt handler to finish, no need to wait for other IRQ sources. - - -why were these changes done? The main reason was the architectural burden -of maintaining the cli()/sti() interface - it became a real problem. The -new interrupt system is much more streamlined, easier to understand, debug, -and it's also a bit faster - the same happened to it that will happen to -cli()/sti() using drivers once they convert to spinlocks :-) - diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 62aa4f895abe..58ff4e74b2f3 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -223,35 +223,6 @@ static inline int disable_irq_wake(unsigned int irq) #define or_softirq_pending(x) (local_softirq_pending() |= (x)) #endif -/* - * Temporary defines for UP kernels, until all code gets fixed. - */ -#ifndef CONFIG_SMP -static inline void __deprecated cli(void) -{ - local_irq_disable(); -} -static inline void __deprecated sti(void) -{ - local_irq_enable(); -} -static inline void __deprecated save_flags(unsigned long *x) -{ - local_save_flags(*x); -} -#define save_flags(x) save_flags(&x) -static inline void __deprecated restore_flags(unsigned long x) -{ - local_irq_restore(x); -} - -static inline void __deprecated save_and_cli(unsigned long *x) -{ - local_irq_save(*x); -} -#define save_and_cli(x) save_and_cli(&x) -#endif /* CONFIG_SMP */ - /* Some architectures might implement lazy enabling/disabling of * interrupts. In some cases, such as stop_machine, we might want * to ensure that after a local_irq_disable(), interrupts have -- cgit v1.2.3 From bf1db69fbf4ff511e88736ce2e6318846f34492b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 5 Aug 2008 13:01:35 -0700 Subject: pm_qos: spelling fixes A documentation cleanup patch. With a minor tweak to clarify units for kbs. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: mark gross Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/power/pm_qos_interface.txt | 7 ++++++- include/linux/pm_qos_params.h | 2 +- kernel/pm_qos_params.c | 16 ++++++++-------- 3 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index 49adb1a33514..c40866e8b957 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -1,4 +1,4 @@ -PM quality of Service interface. +PM Quality Of Service Interface. This interface provides a kernel and user mode interface for registering performance expectations by drivers, subsystems and user space applications on @@ -7,6 +7,11 @@ one of the parameters. Currently we have {cpu_dma_latency, network_latency, network_throughput} as the initial set of pm_qos parameters. +Each parameters have defined units: + * latency: usec + * timeout: usec + * throughput: kbs (kilo bit / sec) + The infrastructure exposes multiple misc device nodes one per implemented parameter. The set of parameters implement is defined by pm_qos_power_init() and pm_qos_params.h. This is done because having the available parameters diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h index 2e4e97bd19f7..d74f75ed1e47 100644 --- a/include/linux/pm_qos_params.h +++ b/include/linux/pm_qos_params.h @@ -1,6 +1,6 @@ /* interface for the pm_qos_power infrastructure of the linux kernel. * - * Mark Gross + * Mark Gross */ #include #include diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c index 8cb757026386..da9c2dda6a4e 100644 --- a/kernel/pm_qos_params.c +++ b/kernel/pm_qos_params.c @@ -24,7 +24,7 @@ * requirement that the application has is cleaned up when closes the file * pointer or exits the pm_qos_object will get an opportunity to clean up. * - * mark gross mgross@linux.intel.com + * Mark Gross */ #include @@ -211,8 +211,8 @@ EXPORT_SYMBOL_GPL(pm_qos_requirement); * @value: defines the qos request * * This function inserts a new entry in the pm_qos_class list of requested qos - * performance charactoistics. It recomputes the agregate QoS expectations for - * the pm_qos_class of parrameters. + * performance characteristics. It recomputes the aggregate QoS expectations + * for the pm_qos_class of parameters. */ int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) { @@ -250,10 +250,10 @@ EXPORT_SYMBOL_GPL(pm_qos_add_requirement); * @name: identifies the request * @value: defines the qos request * - * Updates an existing qos requierement for the pm_qos_class of parameters along + * Updates an existing qos requirement for the pm_qos_class of parameters along * with updating the target pm_qos_class value. * - * If the named request isn't in the lest then no change is made. + * If the named request isn't in the list then no change is made. */ int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) { @@ -287,7 +287,7 @@ EXPORT_SYMBOL_GPL(pm_qos_update_requirement); * @pm_qos_class: identifies which list of qos request to us * @name: identifies the request * - * Will remove named qos request from pm_qos_class list of parrameters and + * Will remove named qos request from pm_qos_class list of parameters and * recompute the current target value for the pm_qos_class. */ void pm_qos_remove_requirement(int pm_qos_class, char *name) @@ -319,7 +319,7 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); * @notifier: notifier block managed by caller. * * will register the notifier into a notification chain that gets called - * uppon changes to the pm_qos_class target value. + * upon changes to the pm_qos_class target value. */ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { @@ -338,7 +338,7 @@ EXPORT_SYMBOL_GPL(pm_qos_add_notifier); * @notifier: notifier block to be removed. * * will remove the notifier from the notification chain that gets called - * uppon changes to the pm_qos_class target value. + * upon changes to the pm_qos_class target value. */ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) { -- cgit v1.2.3 From f780a9f119caa48088b230836a7fa73d1096de7c Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Wed, 6 Aug 2008 20:14:06 -0700 Subject: mlx4_core: Add ethernet fields to CQE struct Add ethernet-related fields to struct mlx4_cqe so that the mlx4_en ethernet NIC driver can share the same definition. Signed-off-by: Yevgeny Petrilin Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/cq.c | 33 ++++++++++++++++----------------- include/linux/mlx4/cq.h | 36 ++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index a1464574bfdd..d0866a3636e2 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -515,17 +515,17 @@ static void mlx4_ib_handle_error_cqe(struct mlx4_err_cqe *cqe, wc->vendor_err = cqe->vendor_err_syndrome; } -static int mlx4_ib_ipoib_csum_ok(__be32 status, __be16 checksum) +static int mlx4_ib_ipoib_csum_ok(__be16 status, __be16 checksum) { - return ((status & cpu_to_be32(MLX4_CQE_IPOIB_STATUS_IPV4 | - MLX4_CQE_IPOIB_STATUS_IPV4F | - MLX4_CQE_IPOIB_STATUS_IPV4OPT | - MLX4_CQE_IPOIB_STATUS_IPV6 | - MLX4_CQE_IPOIB_STATUS_IPOK)) == - cpu_to_be32(MLX4_CQE_IPOIB_STATUS_IPV4 | - MLX4_CQE_IPOIB_STATUS_IPOK)) && - (status & cpu_to_be32(MLX4_CQE_IPOIB_STATUS_UDP | - MLX4_CQE_IPOIB_STATUS_TCP)) && + return ((status & cpu_to_be16(MLX4_CQE_STATUS_IPV4 | + MLX4_CQE_STATUS_IPV4F | + MLX4_CQE_STATUS_IPV4OPT | + MLX4_CQE_STATUS_IPV6 | + MLX4_CQE_STATUS_IPOK)) == + cpu_to_be16(MLX4_CQE_STATUS_IPV4 | + MLX4_CQE_STATUS_IPOK)) && + (status & cpu_to_be16(MLX4_CQE_STATUS_UDP | + MLX4_CQE_STATUS_TCP)) && checksum == cpu_to_be16(0xffff); } @@ -582,17 +582,17 @@ repoll: } if (!*cur_qp || - (be32_to_cpu(cqe->my_qpn) & 0xffffff) != (*cur_qp)->mqp.qpn) { + (be32_to_cpu(cqe->vlan_my_qpn) & MLX4_CQE_QPN_MASK) != (*cur_qp)->mqp.qpn) { /* * We do not have to take the QP table lock here, * because CQs will be locked while QPs are removed * from the table. */ mqp = __mlx4_qp_lookup(to_mdev(cq->ibcq.device)->dev, - be32_to_cpu(cqe->my_qpn)); + be32_to_cpu(cqe->vlan_my_qpn)); if (unlikely(!mqp)) { printk(KERN_WARNING "CQ %06x with entry for unknown QPN %06x\n", - cq->mcq.cqn, be32_to_cpu(cqe->my_qpn) & 0xffffff); + cq->mcq.cqn, be32_to_cpu(cqe->vlan_my_qpn) & MLX4_CQE_QPN_MASK); return -EINVAL; } @@ -692,14 +692,13 @@ repoll: } wc->slid = be16_to_cpu(cqe->rlid); - wc->sl = cqe->sl >> 4; + wc->sl = be16_to_cpu(cqe->sl_vid >> 12); g_mlpath_rqpn = be32_to_cpu(cqe->g_mlpath_rqpn); wc->src_qp = g_mlpath_rqpn & 0xffffff; wc->dlid_path_bits = (g_mlpath_rqpn >> 24) & 0x7f; wc->wc_flags |= g_mlpath_rqpn & 0x80000000 ? IB_WC_GRH : 0; wc->pkey_index = be32_to_cpu(cqe->immed_rss_invalid) & 0x7f; - wc->csum_ok = mlx4_ib_ipoib_csum_ok(cqe->ipoib_status, - cqe->checksum); + wc->csum_ok = mlx4_ib_ipoib_csum_ok(cqe->status, cqe->checksum); } return 0; @@ -767,7 +766,7 @@ void __mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq) */ while ((int) --prod_index - (int) cq->mcq.cons_index >= 0) { cqe = get_cqe(cq, prod_index & cq->ibcq.cqe); - if ((be32_to_cpu(cqe->my_qpn) & 0xffffff) == qpn) { + if ((be32_to_cpu(cqe->vlan_my_qpn) & MLX4_CQE_QPN_MASK) == qpn) { if (srq && !(cqe->owner_sr_opcode & MLX4_CQE_IS_SEND_MASK)) mlx4_ib_free_srq_wqe(srq, be16_to_cpu(cqe->wqe_index)); ++nfreed; diff --git a/include/linux/mlx4/cq.h b/include/linux/mlx4/cq.h index 071cf96cf01f..6f65b2c8bb89 100644 --- a/include/linux/mlx4/cq.h +++ b/include/linux/mlx4/cq.h @@ -39,17 +39,18 @@ #include struct mlx4_cqe { - __be32 my_qpn; + __be32 vlan_my_qpn; __be32 immed_rss_invalid; __be32 g_mlpath_rqpn; - u8 sl; - u8 reserved1; + __be16 sl_vid; __be16 rlid; - __be32 ipoib_status; + __be16 status; + u8 ipv6_ext_mask; + u8 badfcs_enc; __be32 byte_cnt; __be16 wqe_index; __be16 checksum; - u8 reserved2[3]; + u8 reserved[3]; u8 owner_sr_opcode; }; @@ -63,6 +64,11 @@ struct mlx4_err_cqe { u8 owner_sr_opcode; }; +enum { + MLX4_CQE_VLAN_PRESENT_MASK = 1 << 29, + MLX4_CQE_QPN_MASK = 0xffffff, +}; + enum { MLX4_CQE_OWNER_MASK = 0x80, MLX4_CQE_IS_SEND_MASK = 0x40, @@ -86,13 +92,19 @@ enum { }; enum { - MLX4_CQE_IPOIB_STATUS_IPV4 = 1 << 22, - MLX4_CQE_IPOIB_STATUS_IPV4F = 1 << 23, - MLX4_CQE_IPOIB_STATUS_IPV6 = 1 << 24, - MLX4_CQE_IPOIB_STATUS_IPV4OPT = 1 << 25, - MLX4_CQE_IPOIB_STATUS_TCP = 1 << 26, - MLX4_CQE_IPOIB_STATUS_UDP = 1 << 27, - MLX4_CQE_IPOIB_STATUS_IPOK = 1 << 28, + MLX4_CQE_STATUS_IPV4 = 1 << 6, + MLX4_CQE_STATUS_IPV4F = 1 << 7, + MLX4_CQE_STATUS_IPV6 = 1 << 8, + MLX4_CQE_STATUS_IPV4OPT = 1 << 9, + MLX4_CQE_STATUS_TCP = 1 << 10, + MLX4_CQE_STATUS_UDP = 1 << 11, + MLX4_CQE_STATUS_IPOK = 1 << 12, +}; + +enum { + MLX4_CQE_LLC = 1, + MLX4_CQE_SNAP = 1 << 1, + MLX4_CQE_BAD_FCS = 1 << 4, }; static inline void mlx4_cq_arm(struct mlx4_cq *cq, u32 cmd, -- cgit v1.2.3 From b11f8d8cc3bb2fa6fa55286babc1a5ebb2e932c4 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Tue, 15 Jul 2008 02:18:41 -0700 Subject: ethtool: Expand ethtool_cmd.speed to 32 bits Introduce the speed_hi field to ethtool_cmd, using the reserved space, to expand the speed field to 2^32 Megabits/second. Making this field expansion now gives us plenty of time to fix up the user-space pieces that use SIOCETHTOOL before hardware faster than 64 Gb/s is available. Signed-off-by: Brandon Philips Signed-off-by: Jeff Garzik --- include/linux/ethtool.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 8bb5e87df365..b4b038b89ee6 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -27,9 +27,24 @@ struct ethtool_cmd { __u8 autoneg; /* Enable or disable autonegotiation */ __u32 maxtxpkt; /* Tx pkts before generating tx int */ __u32 maxrxpkt; /* Rx pkts before generating rx int */ - __u32 reserved[4]; + __u16 speed_hi; + __u16 reserved2; + __u32 reserved[3]; }; +static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + + ep->speed = (__u16)speed; + ep->speed_hi = (__u16)(speed >> 16); +} + +static inline __u32 ethtool_cmd_speed(struct ethtool_cmd *ep) +{ + return (ep->speed_hi << 16) | ep->speed; +} + #define ETHTOOL_BUSINFO_LEN 32 /* these strings are set to whatever the driver author decides... */ struct ethtool_drvinfo { -- cgit v1.2.3 From fe414248551e2880fe8913577699003ff145ab9d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 23 Jul 2008 17:41:52 +0200 Subject: dm9000: Support MAC address setting through platform data. The dm9000 driver reads the chip's MAC address from the attached EEPROM. When no EEPROM is present, or when the MAC address is invalid, it falls back to reading the address from the chip. This patch lets platform code set the desired MAC address through platform data. Signed-off-by: Laurent Pinchart Signed-off-by: Jeff Garzik --- drivers/net/dm9000.c | 5 +++++ include/linux/dm9000.h | 1 + 2 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 0b0f1c407a7e..f42c23f42652 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -1374,6 +1374,11 @@ dm9000_probe(struct platform_device *pdev) for (i = 0; i < 6; i += 2) dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i); + if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { + mac_src = "platform data"; + memcpy(ndev->dev_addr, pdata->dev_addr, 6); + } + if (!is_valid_ether_addr(ndev->dev_addr)) { /* try reading from mac */ diff --git a/include/linux/dm9000.h b/include/linux/dm9000.h index fc82446b6425..c30879cf93bc 100644 --- a/include/linux/dm9000.h +++ b/include/linux/dm9000.h @@ -27,6 +27,7 @@ struct dm9000_plat_data { unsigned int flags; + unsigned char dev_addr[6]; /* allow replacement IO routines */ -- cgit v1.2.3 From 7d283aee50351ec19eaf654a8690d77c4e1dff50 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 6 Aug 2008 15:21:26 -0700 Subject: list.h: Add list_splice_tail() and list_splice_tail_init() If you are using linked lists for queues list_splice() will not do what you would expect even if you use the elements passed reversed. We need to handle these differently. We add list_splice_tail() and list_splice_tail_init(). Signed-off-by: Peter Zijlstra Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/dma/ioat_dma.c | 2 +- drivers/usb/host/ehci-q.c | 2 +- include/linux/list.h | 47 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index a52156e56886..bc8c6e3470ca 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -551,7 +551,7 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) /* write address into NextDescriptor field of last desc in chain */ to_ioat_desc(ioat_chan->used_desc.prev)->hw->next = first->async_tx.phys; - __list_splice(&new_chain, ioat_chan->used_desc.prev); + list_splice_tail(&new_chain, &ioat_chan->used_desc); ioat_chan->dmacount += desc_count; ioat_chan->pending += desc_count; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 2622b6596d7c..3712b925b315 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -932,7 +932,7 @@ static struct ehci_qh *qh_append_tds ( list_del (&qtd->qtd_list); list_add (&dummy->qtd_list, qtd_list); - __list_splice (qtd_list, qh->qtd_list.prev); + list_splice_tail(qtd_list, &qh->qtd_list); ehci_qtd_init(ehci, qtd, qtd->qtd_dma); qh->dummy = qtd; diff --git a/include/linux/list.h b/include/linux/list.h index 453916bc0412..a886f27a1181 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -215,21 +215,21 @@ static inline int list_is_singular(const struct list_head *head) } static inline void __list_splice(const struct list_head *list, - struct list_head *head) + struct list_head *prev, + struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; - struct list_head *at = head->next; - first->prev = head; - head->next = first; + first->prev = prev; + prev->next = first; - last->next = at; - at->prev = last; + last->next = next; + next->prev = last; } /** - * list_splice - join two lists + * list_splice - join two lists, this is designed for stacks * @list: the new list to add. * @head: the place to add it in the first list. */ @@ -237,7 +237,19 @@ static inline void list_splice(const struct list_head *list, struct list_head *head) { if (!list_empty(list)) - __list_splice(list, head); + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); } /** @@ -251,7 +263,24 @@ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { - __list_splice(list, head); + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists, each list being a queue, and + * reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); INIT_LIST_HEAD(list); } } -- cgit v1.2.3 From 00e8a4da8cf0d7dba8cc4b0da28ea0f12dcf6b36 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 6 Aug 2008 13:28:54 -0700 Subject: list.h: add list_cut_position() This adds list_cut_position() which lets you cut a list into two lists given a pivot in the list. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/linux/list.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index a886f27a1181..1d109e2ef0a9 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -214,6 +214,46 @@ static inline int list_is_singular(const struct list_head *head) return !list_empty(head) && (head->next == head->prev); } +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + static inline void __list_splice(const struct list_head *list, struct list_head *prev, struct list_head *next) -- cgit v1.2.3 From bba81165867313766534dd31603de51bdd36ef9b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 30 Jul 2008 12:07:04 -0700 Subject: PCI: make pci_register_driver() a macro alpha: CC [M] drivers/usb/gadget/u_ether.o In file included from include/asm/dma-mapping.h:7, from include/linux/dma-mapping.h:52, from include/linux/dmaengine.h:29, from include/linux/skbuff.h:29, from include/linux/if_ether.h:114, from include/linux/etherdevice.h:27, from drivers/usb/gadget/u_ether.c:29: include/linux/pci.h: In function 'pci_register_driver': include/linux/pci.h:673: error: 'KBUILD_MODNAME' undeclared (first use in this function) include/linux/pci.h:673: error: (Each undeclared identifier is reported only once include/linux/pci.h:673: error: for each function it appears in.) Sam says: The problem is that u_ether.o is used by two modules so when we build it KBUILD_MODNAME is not defined because kbuild does not know what value to use. And in pci.h we have the following inline: static inline int __must_check pci_register_driver(struct pci_driver *driver) { return __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME); } And alpha uses dma-mapping.h to nullify a number of functions that seem to require something from pci.h. Making it a macro fixes this particular problem. However, the underlying issue of a file using KBUILD_MODNAME and being shared between multiple modules is *not* addressed. I guess the answer there is "don't do that". Cc: Sam Ravnborg Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- include/linux/pci.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 825be3878f68..b0269492c34e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -680,10 +680,12 @@ void pci_enable_bridges(struct pci_bus *bus); /* Proper probing supporting hot-pluggable devices */ int __must_check __pci_register_driver(struct pci_driver *, struct module *, const char *mod_name); -static inline int __must_check pci_register_driver(struct pci_driver *driver) -{ - return __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME); -} + +/* + * pci_register_driver must be a macro so that KBUILD_MODNAME can be expanded + */ +#define pci_register_driver(driver) \ + __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) void pci_unregister_driver(struct pci_driver *dev); void pci_remove_behind_bridge(struct pci_dev *dev); -- cgit v1.2.3 From 7bed523a95425b70af7a59df61d5adb422ef2038 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Tue, 5 Aug 2008 14:07:53 -0700 Subject: PCI: remove duplicate symbol from pci_ids.h pci.ids.h: remove a duplicated symbol Cc: Doug Thompson Signed-off-by: Grant Coady Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- include/linux/pci_ids.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 35a78415accc..9ec2bcce8e83 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2177,8 +2177,6 @@ #define PCI_DEVICE_ID_HERC_WIN 0x5732 #define PCI_DEVICE_ID_HERC_UNI 0x5832 -#define PCI_VENDOR_ID_RDC 0x17f3 - #define PCI_VENDOR_ID_SITECOM 0x182d #define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 -- cgit v1.2.3 From 5a6c9b60b4cc15b22d3102b0033e5cb842125456 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Aug 2008 00:14:24 +0200 Subject: PCI PM: Export pci_pme_active to drivers Export pci_pme_active() to drivers, so that they can clear the PME_status bit and disable PME# for their devices without involving ACPI. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 3 ++- include/linux/pci.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0a3d856833fc..c9884bba22de 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1060,7 +1060,7 @@ bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) * The caller must verify that the device is capable of generating PME# before * calling this function with @enable equal to 'true'. */ -static void pci_pme_active(struct pci_dev *dev, bool enable) +void pci_pme_active(struct pci_dev *dev, bool enable) { u16 pmcsr; @@ -1941,6 +1941,7 @@ EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); EXPORT_SYMBOL(pci_pme_capable); +EXPORT_SYMBOL(pci_pme_active); EXPORT_SYMBOL(pci_enable_wake); EXPORT_SYMBOL(pci_target_state); EXPORT_SYMBOL(pci_prepare_to_sleep); diff --git a/include/linux/pci.h b/include/linux/pci.h index b0269492c34e..c0e14008a3c2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -641,6 +641,7 @@ int pci_restore_state(struct pci_dev *dev); int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); +void pci_pme_active(struct pci_dev *dev, bool enable); int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); -- cgit v1.2.3 From 5861bbfcc10fc0358abf52c7d22850c8d180f0b0 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Thu, 7 Aug 2008 16:55:03 -0700 Subject: tracehook: fix CLONE_PTRACE In the change in commit 09a05394fe2448a4139b014936330af23fa7ec83, I overlooked two nits in the logic and this broke using CLONE_PTRACE when PTRACE_O_TRACE* are not being used. A parent that is itself traced at all but not using PTRACE_O_TRACE*, using CLONE_PTRACE would have its new child fail to be traced. A parent that is not itself traced at all that uses CLONE_PTRACE (which should be a no-op in this case) would confuse the bookkeeping and lead to a crash at exit time. This restores the missing checks and fixes both failure modes. Reported-by: Eduardo Habkost Signed-off-by: Roland McGrath --- include/linux/ptrace.h | 2 +- include/linux/tracehook.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index fd31756e1a00..ea7416c901d1 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -172,7 +172,7 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace) child->ptrace = 0; if (unlikely(ptrace)) { child->ptrace = current->ptrace; - __ptrace_link(child, current->parent); + ptrace_link(child, current->parent); } } diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index ab3ef7aefa95..b48d81969574 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -280,7 +280,7 @@ static inline void tracehook_report_clone(int trace, struct pt_regs *regs, unsigned long clone_flags, pid_t pid, struct task_struct *child) { - if (unlikely(trace)) { + if (unlikely(trace) || unlikely(clone_flags & CLONE_PTRACE)) { /* * The child starts up with an immediate SIGSTOP. */ -- cgit v1.2.3 From 2727f226a65e034f93846def7fab314dee430df3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 8 Aug 2008 15:13:27 +0100 Subject: [ARM] fix pnx4008 build errors include/linux/i2c-pnx.h was missed when moving the include files. Fix it now; it doesn't really need to include mach/i2c.h at all. Successfully build tested with pnx4008_defconfig, which had failed in linux-next. Signed-off-by: Russell King --- arch/arm/mach-pnx4008/include/mach/i2c.h | 3 --- include/linux/i2c-pnx.h | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-pnx4008/include/mach/i2c.h b/arch/arm/mach-pnx4008/include/mach/i2c.h index 92e8d65006f7..259ac53abf40 100644 --- a/arch/arm/mach-pnx4008/include/mach/i2c.h +++ b/arch/arm/mach-pnx4008/include/mach/i2c.h @@ -12,9 +12,6 @@ #ifndef __ASM_ARCH_I2C_H__ #define __ASM_ARCH_I2C_H__ -#include -#include - enum { mstatus_tdi = 0x00000001, mstatus_afi = 0x00000002, diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h index e6e9c814da61..f13255e06406 100644 --- a/include/linux/i2c-pnx.h +++ b/include/linux/i2c-pnx.h @@ -12,7 +12,9 @@ #ifndef __I2C_PNX_H__ #define __I2C_PNX_H__ -#include +#include + +struct platform_device; struct i2c_pnx_mif { int ret; /* Return value */ -- cgit v1.2.3 From 6724cce8fb4b408ae1a2fab455050f3407c80144 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 8 Aug 2008 13:56:20 -0700 Subject: list.h: fix fatal kernel-doc error Fix fatal multi-line kernel-doc error in list.h: function short description must be on one line. Error(linux-2.6.27-rc2-git3//include/linux/list.h:318): duplicate section name 'Description' Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds --- include/linux/list.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index 1d109e2ef0a9..db35ef02e745 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -309,11 +309,11 @@ static inline void list_splice_init(struct list_head *list, } /** - * list_splice_tail_init - join two lists, each list being a queue, and - * reinitialise the emptied list. + * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * + * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init(struct list_head *list, -- cgit v1.2.3 From d3a2f71853ce543c5515d4982e202751e15b0b6d Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Thu, 31 Jul 2008 20:44:28 +0200 Subject: mfd: TMIO MMC structures and accessors. Signed-off-by: Ian Molton Signed-off-by: Samuel Ortiz --- include/linux/mfd/tmio.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 9438d8c9ac1c..ec612e66391c 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -1,6 +1,21 @@ #ifndef MFD_TMIO_H #define MFD_TMIO_H +#define tmio_ioread8(addr) readb(addr) +#define tmio_ioread16(addr) readw(addr) +#define tmio_ioread16_rep(r, b, l) readsw(r, b, l) +#define tmio_ioread32(addr) \ + (((u32) readw((addr))) | (((u32) readw((addr) + 2)) << 16)) + +#define tmio_iowrite8(val, addr) writeb((val), (addr)) +#define tmio_iowrite16(val, addr) writew((val), (addr)) +#define tmio_iowrite16_rep(r, b, l) writesw(r, b, l) +#define tmio_iowrite32(val, addr) \ + do { \ + writew((val), (addr)); \ + writew((val) >> 16, (addr) + 2); \ + } while (0) + /* * data for the NAND controller */ @@ -10,8 +25,4 @@ struct tmio_nand_data { unsigned int num_partitions; }; -#define TMIO_NAND_CONFIG "tmio-nand-config" -#define TMIO_NAND_CONTROL "tmio-nand-control" -#define TMIO_NAND_IRQ "tmio-nand" - #endif -- cgit v1.2.3 From 1f192015ca5b2f4d0a79c191f03f64e72fd8fc29 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Tue, 15 Jul 2008 15:09:43 +0100 Subject: mfd: driver for the T7L66XB TMIO SoC This patchset provides support for the core functinality of the T7L66XB SoC from Toshiba. Supported in this patchset is the IRQ MUX, MMC controller and NAND flash controller. Signed-off-by: Ian Molton Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 1 + drivers/mfd/t7l66xb.c | 409 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/t7l66xb.h | 36 ++++ 4 files changed, 452 insertions(+) create mode 100644 drivers/mfd/t7l66xb.c create mode 100644 include/linux/mfd/t7l66xb.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 883e7ea31de2..fc7c919693bf 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -50,6 +50,12 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. +config MFD_T7L66XB + bool "Support Toshiba T7L66XB" + select MFD_CORE + help + Support for Toshiba Mobile IO Controller T7L66XB + config MFD_TC6393XB bool "Support Toshiba TC6393XB" depends on GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 33daa2f45dd8..3531ad2a276c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o +obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o obj-$(CONFIG_MFD_CORE) += mfd-core.o diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c new file mode 100644 index 000000000000..5be42054f739 --- /dev/null +++ b/drivers/mfd/t7l66xb.c @@ -0,0 +1,409 @@ +/* + * + * Toshiba T7L66XB core mfd support + * + * Copyright (c) 2005, 2007, 2008 Ian Molton + * Copyright (c) 2008 Dmitry Baryshkov + * + * 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. + * + * T7L66 features: + * + * Supported in this driver: + * SD/MMC + * SM/NAND flash controller + * + * As yet not supported + * GPIO interface (on NAND pins) + * Serial interface + * TFT 'interface converter' + * PCMCIA interface logic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + T7L66XB_CELL_NAND, + T7L66XB_CELL_MMC, +}; + +#define SCR_REVID 0x08 /* b Revision ID */ +#define SCR_IMR 0x42 /* b Interrupt Mask */ +#define SCR_DEV_CTL 0xe0 /* b Device control */ +#define SCR_ISR 0xe1 /* b Interrupt Status */ +#define SCR_GPO_OC 0xf0 /* b GPO output control */ +#define SCR_GPO_OS 0xf1 /* b GPO output enable */ +#define SCR_GPI_S 0xf2 /* w GPI status */ +#define SCR_APDC 0xf8 /* b Active pullup down ctrl */ + +#define SCR_DEV_CTL_USB BIT(0) /* USB enable */ +#define SCR_DEV_CTL_MMC BIT(1) /* MMC enable */ + +/*--------------------------------------------------------------------------*/ + +struct t7l66xb { + void __iomem *scr; + /* Lock to protect registers requiring read/modify/write ops. */ + spinlock_t lock; + + struct resource rscr; + int irq; + int irq_base; +}; + +/*--------------------------------------------------------------------------*/ + +static int t7l66xb_mmc_enable(struct platform_device *mmc) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct t7l66xb_platform_data *pdata = dev->dev.platform_data; + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + unsigned long flags; + u8 dev_ctl; + + if (pdata->enable_clk32k) + pdata->enable_clk32k(dev); + + spin_lock_irqsave(&t7l66xb->lock, flags); + + dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); + dev_ctl |= SCR_DEV_CTL_MMC; + tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); + + spin_unlock_irqrestore(&t7l66xb->lock, flags); + + return 0; +} + +static int t7l66xb_mmc_disable(struct platform_device *mmc) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct t7l66xb_platform_data *pdata = dev->dev.platform_data; + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + unsigned long flags; + u8 dev_ctl; + + spin_lock_irqsave(&t7l66xb->lock, flags); + + dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); + dev_ctl &= ~SCR_DEV_CTL_MMC; + tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); + + spin_unlock_irqrestore(&t7l66xb->lock, flags); + + if (pdata->disable_clk32k) + pdata->disable_clk32k(dev); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +const static struct resource t7l66xb_mmc_resources[] = { + { + .start = 0x800, + .end = 0x9ff, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x200, + .end = 0x2ff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_T7L66XB_MMC, + .end = IRQ_T7L66XB_MMC, + .flags = IORESOURCE_IRQ, + }, +}; + +const static struct resource t7l66xb_nand_resources[] = { + { + .start = 0xc00, + .end = 0xc07, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x0100, + .end = 0x01ff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_T7L66XB_NAND, + .end = IRQ_T7L66XB_NAND, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell t7l66xb_cells[] = { + [T7L66XB_CELL_MMC] = { + .name = "tmio-mmc", + .enable = t7l66xb_mmc_enable, + .disable = t7l66xb_mmc_disable, + .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), + .resources = t7l66xb_mmc_resources, + }, + [T7L66XB_CELL_NAND] = { + .name = "tmio-nand", + .num_resources = ARRAY_SIZE(t7l66xb_nand_resources), + .resources = t7l66xb_nand_resources, + }, +}; + +/*--------------------------------------------------------------------------*/ + +/* Handle the T7L66XB interrupt mux */ +static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc) +{ + struct t7l66xb *t7l66xb = get_irq_data(irq); + unsigned int isr; + unsigned int i, irq_base; + + irq_base = t7l66xb->irq_base; + + while ((isr = tmio_ioread8(t7l66xb->scr + SCR_ISR) & + ~tmio_ioread8(t7l66xb->scr + SCR_IMR))) + for (i = 0; i < T7L66XB_NR_IRQS; i++) + if (isr & (1 << i)) + generic_handle_irq(irq_base + i); +} + +static void t7l66xb_irq_mask(unsigned int irq) +{ + struct t7l66xb *t7l66xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&t7l66xb->lock, flags); + imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); + imr |= 1 << (irq - t7l66xb->irq_base); + tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); + spin_unlock_irqrestore(&t7l66xb->lock, flags); +} + +static void t7l66xb_irq_unmask(unsigned int irq) +{ + struct t7l66xb *t7l66xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&t7l66xb->lock, flags); + imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); + imr &= ~(1 << (irq - t7l66xb->irq_base)); + tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); + spin_unlock_irqrestore(&t7l66xb->lock, flags); +} + +static struct irq_chip t7l66xb_chip = { + .name = "t7l66xb", + .ack = t7l66xb_irq_mask, + .mask = t7l66xb_irq_mask, + .unmask = t7l66xb_irq_unmask, +}; + +/*--------------------------------------------------------------------------*/ + +/* Install the IRQ handler */ +static void t7l66xb_attach_irq(struct platform_device *dev) +{ + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = t7l66xb->irq_base; + + for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { + set_irq_chip(irq, &t7l66xb_chip); + set_irq_chip_data(irq, t7l66xb); + set_irq_handler(irq, handle_level_irq); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); +#endif + } + + set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING); + set_irq_data(t7l66xb->irq, t7l66xb); + set_irq_chained_handler(t7l66xb->irq, t7l66xb_irq); +} + +static void t7l66xb_detach_irq(struct platform_device *dev) +{ + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = t7l66xb->irq_base; + + set_irq_chained_handler(t7l66xb->irq, NULL); + set_irq_data(t7l66xb->irq, NULL); + + for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } +} + +/*--------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM +static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) +{ + struct t7l66xb_platform_data *pdata = dev->dev.platform_data; + + if (pdata && pdata->suspend) + pdata->suspend(dev); + + return 0; +} + +static int t7l66xb_resume(struct platform_device *dev) +{ + struct t7l66xb_platform_data *pdata = dev->dev.platform_data; + + if (pdata && pdata->resume) + pdata->resume(dev); + + return 0; +} +#else +#define t7l66xb_suspend NULL +#define t7l66xb_resume NULL +#endif + +/*--------------------------------------------------------------------------*/ + +static int t7l66xb_probe(struct platform_device *dev) +{ + struct t7l66xb_platform_data *pdata = dev->dev.platform_data; + struct t7l66xb *t7l66xb; + struct resource *iomem, *rscr; + int ret; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) + return -EINVAL; + + t7l66xb = kzalloc(sizeof *t7l66xb, GFP_KERNEL); + if (!t7l66xb) + return -ENOMEM; + + spin_lock_init(&t7l66xb->lock); + + platform_set_drvdata(dev, t7l66xb); + + ret = platform_get_irq(dev, 0); + if (ret >= 0) + t7l66xb->irq = ret; + else + goto err_noirq; + + t7l66xb->irq_base = pdata->irq_base; + + rscr = &t7l66xb->rscr; + rscr->name = "t7l66xb-core"; + rscr->start = iomem->start; + rscr->end = iomem->start + 0xff; + rscr->flags = IORESOURCE_MEM; + + ret = request_resource(iomem, rscr); + if (ret) + goto err_request_scr; + + t7l66xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); + if (!t7l66xb->scr) { + ret = -ENOMEM; + goto err_ioremap; + } + + if (pdata && pdata->enable) + pdata->enable(dev); + + /* Mask all interrupts */ + tmio_iowrite8(0xbf, t7l66xb->scr + SCR_IMR); + + printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n", + dev->name, tmio_ioread8(t7l66xb->scr + SCR_REVID), + (unsigned long)iomem->start, t7l66xb->irq); + + t7l66xb_attach_irq(dev); + + t7l66xb_cells[T7L66XB_CELL_NAND].driver_data = pdata->nand_data; + + ret = mfd_add_devices(dev, t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells), + iomem, t7l66xb->irq_base); + + if (!ret) + return 0; + + t7l66xb_detach_irq(dev); + iounmap(t7l66xb->scr); +err_ioremap: + release_resource(&t7l66xb->rscr); +err_noirq: +err_request_scr: + kfree(t7l66xb); + return ret; +} + +static int t7l66xb_remove(struct platform_device *dev) +{ + struct t7l66xb_platform_data *pdata = dev->dev.platform_data; + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + int ret; + + ret = pdata->disable(dev); + + t7l66xb_detach_irq(dev); + iounmap(t7l66xb->scr); + release_resource(&t7l66xb->rscr); + mfd_remove_devices(dev); + platform_set_drvdata(dev, NULL); + kfree(t7l66xb); + + return ret; + +} + +static struct platform_driver t7l66xb_platform_driver = { + .driver = { + .name = "t7l66xb", + .owner = THIS_MODULE, + }, + .suspend = t7l66xb_suspend, + .resume = t7l66xb_resume, + .probe = t7l66xb_probe, + .remove = t7l66xb_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init t7l66xb_init(void) +{ + int retval = 0; + + retval = platform_driver_register(&t7l66xb_platform_driver); + return retval; +} + +static void __exit t7l66xb_exit(void) +{ + platform_driver_unregister(&t7l66xb_platform_driver); +} + +module_init(t7l66xb_init); +module_exit(t7l66xb_exit); + +MODULE_DESCRIPTION("Toshiba T7L66XB core driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ian Molton"); +MODULE_ALIAS("platform:t7l66xb"); diff --git a/include/linux/mfd/t7l66xb.h b/include/linux/mfd/t7l66xb.h new file mode 100644 index 000000000000..e83c7f2036f9 --- /dev/null +++ b/include/linux/mfd/t7l66xb.h @@ -0,0 +1,36 @@ +/* + * This file contains the definitions for the T7L66XB + * + * (C) Copyright 2005 Ian Molton + * + * 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 MFD_T7L66XB_H +#define MFD_T7L66XB_H + +#include +#include + +struct t7l66xb_platform_data { + int (*enable_clk32k)(struct platform_device *dev); + void (*disable_clk32k)(struct platform_device *dev); + int (*enable)(struct platform_device *dev); + int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); + + int irq_base; /* The base for subdevice irqs */ + + struct tmio_nand_data *nand_data; +}; + + +#define IRQ_T7L66XB_MMC (1) +#define IRQ_T7L66XB_NAND (3) + +#define T7L66XB_NR_IRQS 8 + +#endif -- cgit v1.2.3 From cbdfb426392557d49b1a0e7cb59b16c20dc42955 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Tue, 15 Jul 2008 15:12:52 +0100 Subject: mfd: driver for the TC6387XB TMIO controller. This patch adds support for the TC6387XB. Unlike other TMIO devices this one has only one subdevice and no interrupt mux, however using the MFD framework allows it to share the TMIO MMC driver. Signed-off-by: Ian Molton Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 6 ++ drivers/mfd/Makefile | 1 + drivers/mfd/tc6387xb.c | 172 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tc6387xb.h | 23 ++++++ 4 files changed, 202 insertions(+) create mode 100644 drivers/mfd/tc6387xb.c create mode 100644 include/linux/mfd/tc6387xb.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fc7c919693bf..5beff5b7ef2e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -56,6 +56,12 @@ config MFD_T7L66XB help Support for Toshiba Mobile IO Controller T7L66XB +config MFD_TC6387XB + bool "Support Toshiba TC6387XB" + select MFD_CORE + help + Support for Toshiba Mobile IO Controller TC6387XB + config MFD_TC6393XB bool "Support Toshiba TC6393XB" depends on GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 3531ad2a276c..03ad239ecef0 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o +obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o obj-$(CONFIG_MFD_CORE) += mfd-core.o diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c new file mode 100644 index 000000000000..03718feda4d8 --- /dev/null +++ b/drivers/mfd/tc6387xb.c @@ -0,0 +1,172 @@ +/* + * Toshiba TC6387XB support + * Copyright (c) 2005 Ian Molton + * + * 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. + * + * This file contains TC6387XB base support. + * + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM +static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) +{ + struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev); + + if (pdata && pdata->suspend) + pdata->suspend(dev); + + return 0; +} + +static int tc6387xb_resume(struct platform_device *dev) +{ + struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev); + + if (pdata && pdata->resume) + pdata->resume(dev); + + return 0; +} +#else +#define tc6387xb_suspend NULL +#define tc6387xb_resume NULL +#endif + +/*--------------------------------------------------------------------------*/ + +static int tc6387xb_mmc_enable(struct platform_device *mmc) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data; + + if (tc6387xb->enable_clk32k) + tc6387xb->enable_clk32k(dev); + + return 0; +} + +static int tc6387xb_mmc_disable(struct platform_device *mmc) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data; + + if (tc6387xb->disable_clk32k) + tc6387xb->disable_clk32k(dev); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +static struct resource tc6387xb_mmc_resources[] = { + { + .start = 0x800, + .end = 0x9ff, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x200, + .end = 0x2ff, + .flags = IORESOURCE_MEM, + }, + { + .start = 0, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell tc6387xb_cells[] = { + { + .name = "tmio-mmc", + .enable = tc6387xb_mmc_enable, + .disable = tc6387xb_mmc_disable, + .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources), + .resources = tc6387xb_mmc_resources, + }, +}; + +static int tc6387xb_probe(struct platform_device *dev) +{ + struct tc6387xb_platform_data *data = platform_get_drvdata(dev); + struct resource *iomem; + int irq, ret; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + ret = -EINVAL; + goto err_resource; + } + + ret = platform_get_irq(dev, 0); + if (ret >= 0) + irq = ret; + else + goto err_resource; + + if (data && data->enable) + data->enable(dev); + + printk(KERN_INFO "Toshiba tc6387xb initialised\n"); + + ret = mfd_add_devices(dev, tc6387xb_cells, + ARRAY_SIZE(tc6387xb_cells), iomem, irq); + + if (!ret) + return 0; + +err_resource: + return ret; +} + +static int tc6387xb_remove(struct platform_device *dev) +{ + struct tc6387xb_platform_data *data = platform_get_drvdata(dev); + + if (data && data->disable) + data->disable(dev); + + /* FIXME - free the resources! */ + + return 0; +} + + +static struct platform_driver tc6387xb_platform_driver = { + .driver = { + .name = "tc6387xb", + }, + .probe = tc6387xb_probe, + .remove = tc6387xb_remove, + .suspend = tc6387xb_suspend, + .resume = tc6387xb_resume, +}; + + +static int __init tc6387xb_init(void) +{ + return platform_driver_register(&tc6387xb_platform_driver); +} + +static void __exit tc6387xb_exit(void) +{ + platform_driver_unregister(&tc6387xb_platform_driver); +} + +module_init(tc6387xb_init); +module_exit(tc6387xb_exit); + +MODULE_DESCRIPTION("Toshiba TC6387XB core driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ian Molton"); +MODULE_ALIAS("platform:tc6387xb"); diff --git a/include/linux/mfd/tc6387xb.h b/include/linux/mfd/tc6387xb.h new file mode 100644 index 000000000000..fa06e0610b8e --- /dev/null +++ b/include/linux/mfd/tc6387xb.h @@ -0,0 +1,23 @@ +/* + * This file contains the definitions for the TC6387XB + * + * (C) Copyright 2005 Ian Molton + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + */ +#ifndef MFD_TC6387XB_H +#define MFD_TC6387XB_H + +struct tc6387xb_platform_data { + int (*enable_clk32k)(struct platform_device *dev); + void (*disable_clk32k)(struct platform_device *dev); + + int (*enable)(struct platform_device *dev); + int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); +}; + +#endif -- cgit v1.2.3 From 25d6cbd840d958aada29a342c9ee370590ff7b21 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Sun, 10 Aug 2008 23:32:07 +0200 Subject: mfd: tc6393 cleanup and update This patchset cleans up the TC6393XB support. * Add provision for the MMC subdevice * Disable / enable clocks on suspend / resume * Remove fragments of badly merged code (eg. linux/fb include etc.) * Use a device specific clock name to break dependancy on ARM/PXA2XX * Drop unnecessary resource names * Switch to tmio_io* accessors Signed-off-by: Ian Molton Signed-off-by: Samuel Ortiz --- drivers/mfd/tc6393xb.c | 156 ++++++++++++++++++++++++++----------------- include/linux/mfd/tc6393xb.h | 9 +-- 2 files changed, 96 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 81e2605ea10d..e4c1c788b5f8 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -112,6 +112,7 @@ struct tc6393xb { enum { TC6393XB_CELL_NAND, + TC6393XB_CELL_MMC, }; /*--------------------------------------------------------------------------*/ @@ -126,7 +127,7 @@ static int tc6393xb_nand_enable(struct platform_device *nand) /* SMD buffer on */ dev_dbg(&dev->dev, "SMD buffer on\n"); - iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); + tmio_iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); spin_unlock_irqrestore(&tc6393xb->lock, flags); @@ -135,13 +136,13 @@ static int tc6393xb_nand_enable(struct platform_device *nand) static struct resource __devinitdata tc6393xb_nand_resources[] = { { - .start = 0x0100, - .end = 0x01ff, + .start = 0x1000, + .end = 0x1007, .flags = IORESOURCE_MEM, }, { - .start = 0x1000, - .end = 0x1007, + .start = 0x0100, + .end = 0x01ff, .flags = IORESOURCE_MEM, }, { @@ -151,6 +152,24 @@ static struct resource __devinitdata tc6393xb_nand_resources[] = { }, }; +static struct resource __devinitdata tc6393xb_mmc_resources[] = { + { + .start = 0x800, + .end = 0x9ff, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x200, + .end = 0x2ff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TC6393_MMC, + .end = IRQ_TC6393_MMC, + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell __devinitdata tc6393xb_cells[] = { [TC6393XB_CELL_NAND] = { .name = "tmio-nand", @@ -158,6 +177,11 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = { .num_resources = ARRAY_SIZE(tc6393xb_nand_resources), .resources = tc6393xb_nand_resources, }, + [TC6393XB_CELL_MMC] = { + .name = "tmio-mmc", + .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), + .resources = tc6393xb_mmc_resources, + }, }; /*--------------------------------------------------------------------------*/ @@ -168,7 +192,7 @@ static int tc6393xb_gpio_get(struct gpio_chip *chip, struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); /* XXX: does dsr also represent inputs? */ - return ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)) + return tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)) & TC_GPIO_BIT(offset); } @@ -178,13 +202,13 @@ static void __tc6393xb_gpio_set(struct gpio_chip *chip, struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); u8 dsr; - dsr = ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)); + dsr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)); if (value) dsr |= TC_GPIO_BIT(offset); else dsr &= ~TC_GPIO_BIT(offset); - iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8)); + tmio_iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8)); } static void tc6393xb_gpio_set(struct gpio_chip *chip, @@ -209,9 +233,9 @@ static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, spin_lock_irqsave(&tc6393xb->lock, flags); - doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); doecr &= ~TC_GPIO_BIT(offset); - iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); spin_unlock_irqrestore(&tc6393xb->lock, flags); @@ -229,9 +253,9 @@ static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, __tc6393xb_gpio_set(chip, offset, value); - doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); doecr |= TC_GPIO_BIT(offset); - iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); spin_unlock_irqrestore(&tc6393xb->lock, flags); @@ -262,8 +286,8 @@ tc6393xb_irq(unsigned int irq, struct irq_desc *desc) irq_base = tc6393xb->irq_base; - while ((isr = ioread8(tc6393xb->scr + SCR_ISR) & - ~ioread8(tc6393xb->scr + SCR_IMR))) + while ((isr = tmio_ioread8(tc6393xb->scr + SCR_ISR) & + ~tmio_ioread8(tc6393xb->scr + SCR_IMR))) for (i = 0; i < TC6393XB_NR_IRQS; i++) { if (isr & (1 << i)) generic_handle_irq(irq_base + i); @@ -281,9 +305,9 @@ static void tc6393xb_irq_mask(unsigned int irq) u8 imr; spin_lock_irqsave(&tc6393xb->lock, flags); - imr = ioread8(tc6393xb->scr + SCR_IMR); + imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); imr |= 1 << (irq - tc6393xb->irq_base); - iowrite8(imr, tc6393xb->scr + SCR_IMR); + tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); spin_unlock_irqrestore(&tc6393xb->lock, flags); } @@ -294,9 +318,9 @@ static void tc6393xb_irq_unmask(unsigned int irq) u8 imr; spin_lock_irqsave(&tc6393xb->lock, flags); - imr = ioread8(tc6393xb->scr + SCR_IMR); + imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); imr &= ~(1 << (irq - tc6393xb->irq_base)); - iowrite8(imr, tc6393xb->scr + SCR_IMR); + tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); spin_unlock_irqrestore(&tc6393xb->lock, flags); } @@ -377,9 +401,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) { struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; struct tc6393xb *tc6393xb; - struct resource *iomem; - struct resource *rscr; - int retval, temp; + struct resource *iomem, *rscr; + int ret, temp; int i; iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -388,20 +411,26 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL); if (!tc6393xb) { - retval = -ENOMEM; + ret = -ENOMEM; goto err_kzalloc; } spin_lock_init(&tc6393xb->lock); platform_set_drvdata(dev, tc6393xb); + + ret = platform_get_irq(dev, 0); + if (ret >= 0) + tc6393xb->irq = ret; + else + goto err_noirq; + tc6393xb->iomem = iomem; - tc6393xb->irq = platform_get_irq(dev, 0); tc6393xb->irq_base = tcpd->irq_base; - tc6393xb->clk = clk_get(&dev->dev, "GPIO27_CLK" /* "CK3P6MI" */); + tc6393xb->clk = clk_get(&dev->dev, "CLK_CK3P6MI"); if (IS_ERR(tc6393xb->clk)) { - retval = PTR_ERR(tc6393xb->clk); + ret = PTR_ERR(tc6393xb->clk); goto err_clk_get; } @@ -411,71 +440,73 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) rscr->end = iomem->start + 0xff; rscr->flags = IORESOURCE_MEM; - retval = request_resource(iomem, rscr); - if (retval) + ret = request_resource(iomem, rscr); + if (ret) goto err_request_scr; tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); if (!tc6393xb->scr) { - retval = -ENOMEM; + ret = -ENOMEM; goto err_ioremap; } - retval = clk_enable(tc6393xb->clk); - if (retval) + ret = clk_enable(tc6393xb->clk); + if (ret) goto err_clk_enable; - retval = tcpd->enable(dev); - if (retval) + ret = tcpd->enable(dev); + if (ret) goto err_enable; tc6393xb->suspend_state.fer = 0; + for (i = 0; i < 3; i++) { tc6393xb->suspend_state.gpo_dsr[i] = (tcpd->scr_gpo_dsr >> (8 * i)) & 0xff; tc6393xb->suspend_state.gpo_doecr[i] = (tcpd->scr_gpo_doecr >> (8 * i)) & 0xff; } - /* - * It may be necessary to change this back to - * platform-dependant code - */ + tc6393xb->suspend_state.ccr = SCR_CCR_UNK1 | SCR_CCR_HCLK_48; - retval = tc6393xb_hw_init(dev); - if (retval) + ret = tc6393xb_hw_init(dev); + if (ret) goto err_hw_init; printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", - ioread8(tc6393xb->scr + SCR_REVID), + tmio_ioread8(tc6393xb->scr + SCR_REVID), (unsigned long) iomem->start, tc6393xb->irq); tc6393xb->gpio.base = -1; if (tcpd->gpio_base >= 0) { - retval = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base); - if (retval) + ret = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base); + if (ret) goto err_gpio_add; } - if (tc6393xb->irq) - tc6393xb_attach_irq(dev); + tc6393xb_attach_irq(dev); tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = &tc6393xb_cells[TC6393XB_CELL_NAND]; tc6393xb_cells[TC6393XB_CELL_NAND].data_size = sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); + tc6393xb_cells[TC6393XB_CELL_MMC].platform_data = + &tc6393xb_cells[TC6393XB_CELL_MMC]; + tc6393xb_cells[TC6393XB_CELL_MMC].data_size = + sizeof(tc6393xb_cells[TC6393XB_CELL_MMC]); + - retval = mfd_add_devices(&dev->dev, dev->id, + ret = mfd_add_devices(&dev->dev, dev->id, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), iomem, tcpd->irq_base); - return 0; + if (!ret) + return 0; - if (tc6393xb->irq) - tc6393xb_detach_irq(dev); + tc6393xb_detach_irq(dev); err_gpio_add: if (tc6393xb->gpio.base != -1) @@ -490,10 +521,11 @@ err_ioremap: release_resource(&tc6393xb->rscr); err_request_scr: clk_put(tc6393xb->clk); +err_noirq: err_clk_get: kfree(tc6393xb); err_kzalloc: - return retval; + return ret; } static int __devexit tc6393xb_remove(struct platform_device *dev) @@ -503,9 +535,7 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) int ret; mfd_remove_devices(&dev->dev); - - if (tc6393xb->irq) - tc6393xb_detach_irq(dev); + tc6393xb_detach_irq(dev); if (tc6393xb->gpio.base != -1) { ret = gpiochip_remove(&tc6393xb->gpio); @@ -516,17 +546,11 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) } ret = tcpd->disable(dev); - clk_disable(tc6393xb->clk); - iounmap(tc6393xb->scr); - release_resource(&tc6393xb->rscr); - platform_set_drvdata(dev, NULL); - clk_put(tc6393xb->clk); - kfree(tc6393xb); return ret; @@ -537,8 +561,7 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) { struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; struct tc6393xb *tc6393xb = platform_get_drvdata(dev); - int i; - + int i, ret; tc6393xb->suspend_state.ccr = ioread16(tc6393xb->scr + SCR_CCR); tc6393xb->suspend_state.fer = ioread8(tc6393xb->scr + SCR_FER); @@ -551,14 +574,21 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) tc6393xb->suspend_state.gpi_bcr[i] = ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); } + ret = tcpd->suspend(dev); + clk_disable(tc6393xb->clk); - return tcpd->suspend(dev); + return ret; } static int tc6393xb_resume(struct platform_device *dev) { struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; - int ret = tcpd->resume(dev); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int ret; + + clk_enable(tc6393xb->clk); + + ret = tcpd->resume(dev); if (ret) return ret; @@ -595,7 +625,7 @@ static void __exit tc6393xb_exit(void) subsys_initcall(tc6393xb_init); module_exit(tc6393xb_exit); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); MODULE_ALIAS("platform:tc6393xb"); diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h index 7cc824a58f7c..fec7b3f7a81f 100644 --- a/include/linux/mfd/tc6393xb.h +++ b/include/linux/mfd/tc6393xb.h @@ -14,8 +14,8 @@ * published by the Free Software Foundation. */ -#ifndef TC6393XB_H -#define TC6393XB_H +#ifndef MFD_TC6393XB_H +#define MFD_TC6393XB_H /* Also one should provide the CK3P6MI clock */ struct tc6393xb_platform_data { @@ -29,7 +29,7 @@ struct tc6393xb_platform_data { int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); - int irq_base; /* a base for cascaded irq */ + int irq_base; /* base for subdevice irqs */ int gpio_base; struct tmio_nand_data *nand_data; @@ -40,9 +40,6 @@ struct tc6393xb_platform_data { */ #define IRQ_TC6393_NAND 0 #define IRQ_TC6393_MMC 1 -#define IRQ_TC6393_OHCI 2 -#define IRQ_TC6393_SERIAL 3 -#define IRQ_TC6393_FB 4 #define TC6393XB_NR_IRQS 8 -- cgit v1.2.3 From c1955a3d4762e7a9bf84035eb3c4886a900f0d15 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 11 Aug 2008 08:59:03 +0200 Subject: sched_clock: delay using sched_clock() Some arch's can't handle sched_clock() being called too early - delay this until sched_clock_init() has been called. Reported-by: Bill Gatliff Signed-off-by: Peter Zijlstra Tested-by: Nishanth Aravamudan CC: Russell King - ARM Linux Signed-off-by: Ingo Molnar --- include/linux/sched.h | 14 +++----------- kernel/sched_clock.c | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index ea436bc1a0e2..5850bfb968a8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1551,16 +1551,10 @@ static inline int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) extern unsigned long long sched_clock(void); -#ifndef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK -static inline void sched_clock_init(void) -{ -} - -static inline u64 sched_clock_cpu(int cpu) -{ - return sched_clock(); -} +extern void sched_clock_init(void); +extern u64 sched_clock_cpu(int cpu); +#ifndef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK static inline void sched_clock_tick(void) { } @@ -1573,8 +1567,6 @@ static inline void sched_clock_idle_wakeup_event(u64 delta_ns) { } #else -extern void sched_clock_init(void); -extern u64 sched_clock_cpu(int cpu); extern void sched_clock_tick(void); extern void sched_clock_idle_sleep_event(void); extern void sched_clock_idle_wakeup_event(u64 delta_ns); diff --git a/kernel/sched_clock.c b/kernel/sched_clock.c index 074edc989379..204991a0bfa7 100644 --- a/kernel/sched_clock.c +++ b/kernel/sched_clock.c @@ -42,6 +42,8 @@ unsigned long long __attribute__((weak)) sched_clock(void) return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ); } +static __read_mostly int sched_clock_running; + #ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK struct sched_clock_data { @@ -70,8 +72,6 @@ static inline struct sched_clock_data *cpu_sdc(int cpu) return &per_cpu(sched_clock_data, cpu); } -static __read_mostly int sched_clock_running; - void sched_clock_init(void) { u64 ktime_now = ktime_to_ns(ktime_get()); @@ -248,6 +248,21 @@ void sched_clock_idle_wakeup_event(u64 delta_ns) } EXPORT_SYMBOL_GPL(sched_clock_idle_wakeup_event); +#else /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */ + +void sched_clock_init(void) +{ + sched_clock_running = 1; +} + +u64 sched_clock_cpu(int cpu) +{ + if (unlikely(!sched_clock_running)) + return 0; + + return sched_clock(); +} + #endif unsigned long long cpu_clock(int cpu) -- cgit v1.2.3 From 64aa348edc617dea17bbd01ddee4e47886d5ec8c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 11 Aug 2008 09:30:21 +0200 Subject: lockdep: lock_set_subclass - reset a held lock's subclass this can be used to reset a held lock's subclass, for arbitrary-depth iterated data structures such as trees or lists which have per-node locks. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 4 +++ kernel/lockdep.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 1bfdc30bb0af..f270ce1582ff 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -300,6 +300,9 @@ extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass, extern void lock_release(struct lockdep_map *lock, int nested, unsigned long ip); +extern void lock_set_subclass(struct lockdep_map *lock, unsigned int subclass, + unsigned long ip); + # define INIT_LOCKDEP .lockdep_recursion = 0, #define lockdep_depth(tsk) (debug_locks ? (tsk)->lockdep_depth : 0) @@ -316,6 +319,7 @@ static inline void lockdep_on(void) # define lock_acquire(l, s, t, r, c, i) do { } while (0) # define lock_release(l, n, i) do { } while (0) +# define lock_set_subclass(l, s, i) do { } while (0) # define lockdep_init() do { } while (0) # define lockdep_info() do { } while (0) # define lockdep_init_map(lock, name, key, sub) do { (void)(key); } while (0) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 6999e64fc248..e14d383dcb0b 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -2660,6 +2660,55 @@ static int check_unlock(struct task_struct *curr, struct lockdep_map *lock, return 1; } +static int +__lock_set_subclass(struct lockdep_map *lock, + unsigned int subclass, unsigned long ip) +{ + struct task_struct *curr = current; + struct held_lock *hlock, *prev_hlock; + struct lock_class *class; + unsigned int depth; + int i; + + depth = curr->lockdep_depth; + if (DEBUG_LOCKS_WARN_ON(!depth)) + return 0; + + prev_hlock = NULL; + for (i = depth-1; i >= 0; i--) { + hlock = curr->held_locks + i; + /* + * We must not cross into another context: + */ + if (prev_hlock && prev_hlock->irq_context != hlock->irq_context) + break; + if (hlock->instance == lock) + goto found_it; + prev_hlock = hlock; + } + return print_unlock_inbalance_bug(curr, lock, ip); + +found_it: + class = register_lock_class(lock, subclass, 0); + hlock->class = class; + + curr->lockdep_depth = i; + curr->curr_chain_key = hlock->prev_chain_key; + + for (; i < depth; i++) { + hlock = curr->held_locks + i; + if (!__lock_acquire(hlock->instance, + hlock->class->subclass, hlock->trylock, + hlock->read, hlock->check, hlock->hardirqs_off, + hlock->acquire_ip)) + return 0; + } + + if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth)) + return 0; + return 1; +} + /* * Remove the lock to the list of currently held locks in a * potentially non-nested (out of order) manner. This is a @@ -2824,6 +2873,26 @@ static void check_flags(unsigned long flags) #endif } +void +lock_set_subclass(struct lockdep_map *lock, + unsigned int subclass, unsigned long ip) +{ + unsigned long flags; + + if (unlikely(current->lockdep_recursion)) + return; + + raw_local_irq_save(flags); + current->lockdep_recursion = 1; + check_flags(flags); + if (__lock_set_subclass(lock, subclass, ip)) + check_chain_key(current); + current->lockdep_recursion = 0; + raw_local_irq_restore(flags); +} + +EXPORT_SYMBOL_GPL(lock_set_subclass); + /* * We are not always called with irqs disabled - do that here, * and also avoid lockdep recursion: -- cgit v1.2.3 From f82b217e3513fe3af342c0f3ee1494e86250c21c Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 11 Aug 2008 09:30:23 +0200 Subject: lockdep: shrink held_lock structure struct held_lock { u64 prev_chain_key; /* 0 8 */ struct lock_class * class; /* 8 8 */ long unsigned int acquire_ip; /* 16 8 */ struct lockdep_map * instance; /* 24 8 */ int irq_context; /* 32 4 */ int trylock; /* 36 4 */ int read; /* 40 4 */ int check; /* 44 4 */ int hardirqs_off; /* 48 4 */ /* size: 56, cachelines: 1 */ /* padding: 4 */ /* last cacheline: 56 bytes */ }; struct held_lock { u64 prev_chain_key; /* 0 8 */ long unsigned int acquire_ip; /* 8 8 */ struct lockdep_map * instance; /* 16 8 */ unsigned int class_idx:11; /* 24:21 4 */ unsigned int irq_context:2; /* 24:19 4 */ unsigned int trylock:1; /* 24:18 4 */ unsigned int read:2; /* 24:16 4 */ unsigned int check:2; /* 24:14 4 */ unsigned int hardirqs_off:1; /* 24:13 4 */ /* size: 32, cachelines: 1 */ /* padding: 4 */ /* bit_padding: 13 bits */ /* last cacheline: 32 bytes */ }; [mingo@elte.hu: shrunk hlock->class too] [peterz@infradead.org: fixup bit sizes] Signed-off-by: Dave Jones Signed-off-by: Ingo Molnar Signed-off-by: Peter Zijlstra --- include/linux/lockdep.h | 16 ++++--- kernel/lockdep.c | 113 ++++++++++++++++++++++++++------------------- kernel/lockdep_internals.h | 3 -- 3 files changed, 74 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index f270ce1582ff..b49bfa8e4a5c 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -190,6 +190,9 @@ struct lock_chain { u64 chain_key; }; +#define MAX_LOCKDEP_KEYS_BITS 11 +#define MAX_LOCKDEP_KEYS (1UL << MAX_LOCKDEP_KEYS_BITS) + struct held_lock { /* * One-way hash of the dependency chain up to this point. We @@ -206,14 +209,13 @@ struct held_lock { * with zero), here we store the previous hash value: */ u64 prev_chain_key; - struct lock_class *class; unsigned long acquire_ip; struct lockdep_map *instance; - #ifdef CONFIG_LOCK_STAT u64 waittime_stamp; u64 holdtime_stamp; #endif + unsigned int class_idx:MAX_LOCKDEP_KEYS_BITS; /* * The lock-stack is unified in that the lock chains of interrupt * contexts nest ontop of process context chains, but we 'separate' @@ -227,11 +229,11 @@ struct held_lock { * The following field is used to detect when we cross into an * interrupt context: */ - int irq_context; - int trylock; - int read; - int check; - int hardirqs_off; + unsigned int irq_context:2; /* bit 0 - soft, bit 1 - hard */ + unsigned int trylock:1; + unsigned int read:2; /* see lock_acquire() comment */ + unsigned int check:2; /* see lock_acquire() comment */ + unsigned int hardirqs_off:1; }; /* diff --git a/kernel/lockdep.c b/kernel/lockdep.c index e14d383dcb0b..d3c72ad8d09e 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -124,6 +124,15 @@ static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES]; unsigned long nr_lock_classes; static struct lock_class lock_classes[MAX_LOCKDEP_KEYS]; +static inline struct lock_class *hlock_class(struct held_lock *hlock) +{ + if (!hlock->class_idx) { + DEBUG_LOCKS_WARN_ON(1); + return NULL; + } + return lock_classes + hlock->class_idx - 1; +} + #ifdef CONFIG_LOCK_STAT static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], lock_stats); @@ -222,7 +231,7 @@ static void lock_release_holdtime(struct held_lock *hlock) holdtime = sched_clock() - hlock->holdtime_stamp; - stats = get_lock_stats(hlock->class); + stats = get_lock_stats(hlock_class(hlock)); if (hlock->read) lock_time_inc(&stats->read_holdtime, holdtime); else @@ -518,7 +527,7 @@ static void print_lockdep_cache(struct lockdep_map *lock) static void print_lock(struct held_lock *hlock) { - print_lock_name(hlock->class); + print_lock_name(hlock_class(hlock)); printk(", at: "); print_ip_sym(hlock->acquire_ip); } @@ -948,7 +957,7 @@ static noinline int print_circular_bug_tail(void) if (debug_locks_silent) return 0; - this.class = check_source->class; + this.class = hlock_class(check_source); if (!save_trace(&this.trace)) return 0; @@ -1057,7 +1066,7 @@ check_noncircular(struct lock_class *source, unsigned int depth) * Check this lock's dependency list: */ list_for_each_entry(entry, &source->locks_after, entry) { - if (entry->class == check_target->class) + if (entry->class == hlock_class(check_target)) return print_circular_bug_header(entry, depth+1); debug_atomic_inc(&nr_cyclic_checks); if (!check_noncircular(entry->class, depth+1)) @@ -1150,6 +1159,11 @@ find_usage_backwards(struct lock_class *source, unsigned int depth) return 2; } + if (!source && debug_locks_off_graph_unlock()) { + WARN_ON(1); + return 0; + } + /* * Check this lock's dependency list: */ @@ -1189,9 +1203,9 @@ print_bad_irq_dependency(struct task_struct *curr, printk("\nand this task is already holding:\n"); print_lock(prev); printk("which would create a new lock dependency:\n"); - print_lock_name(prev->class); + print_lock_name(hlock_class(prev)); printk(" ->"); - print_lock_name(next->class); + print_lock_name(hlock_class(next)); printk("\n"); printk("\nbut this new dependency connects a %s-irq-safe lock:\n", @@ -1232,12 +1246,12 @@ check_usage(struct task_struct *curr, struct held_lock *prev, find_usage_bit = bit_backwards; /* fills in */ - ret = find_usage_backwards(prev->class, 0); + ret = find_usage_backwards(hlock_class(prev), 0); if (!ret || ret == 1) return ret; find_usage_bit = bit_forwards; - ret = find_usage_forwards(next->class, 0); + ret = find_usage_forwards(hlock_class(next), 0); if (!ret || ret == 1) return ret; /* ret == 2 */ @@ -1362,7 +1376,7 @@ check_deadlock(struct task_struct *curr, struct held_lock *next, for (i = 0; i < curr->lockdep_depth; i++) { prev = curr->held_locks + i; - if (prev->class != next->class) + if (hlock_class(prev) != hlock_class(next)) continue; /* * Allow read-after-read recursion of the same @@ -1415,7 +1429,7 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, */ check_source = next; check_target = prev; - if (!(check_noncircular(next->class, 0))) + if (!(check_noncircular(hlock_class(next), 0))) return print_circular_bug_tail(); if (!check_prev_add_irq(curr, prev, next)) @@ -1439,8 +1453,8 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, * chains - the second one will be new, but L1 already has * L2 added to its dependency list, due to the first chain.) */ - list_for_each_entry(entry, &prev->class->locks_after, entry) { - if (entry->class == next->class) { + list_for_each_entry(entry, &hlock_class(prev)->locks_after, entry) { + if (entry->class == hlock_class(next)) { if (distance == 1) entry->distance = 1; return 2; @@ -1451,26 +1465,28 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, * Ok, all validations passed, add the new lock * to the previous lock's dependency list: */ - ret = add_lock_to_list(prev->class, next->class, - &prev->class->locks_after, next->acquire_ip, distance); + ret = add_lock_to_list(hlock_class(prev), hlock_class(next), + &hlock_class(prev)->locks_after, + next->acquire_ip, distance); if (!ret) return 0; - ret = add_lock_to_list(next->class, prev->class, - &next->class->locks_before, next->acquire_ip, distance); + ret = add_lock_to_list(hlock_class(next), hlock_class(prev), + &hlock_class(next)->locks_before, + next->acquire_ip, distance); if (!ret) return 0; /* * Debugging printouts: */ - if (verbose(prev->class) || verbose(next->class)) { + if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) { graph_unlock(); printk("\n new dependency: "); - print_lock_name(prev->class); + print_lock_name(hlock_class(prev)); printk(" => "); - print_lock_name(next->class); + print_lock_name(hlock_class(next)); printk("\n"); dump_stack(); return graph_lock(); @@ -1567,7 +1583,7 @@ static inline int lookup_chain_cache(struct task_struct *curr, struct held_lock *hlock, u64 chain_key) { - struct lock_class *class = hlock->class; + struct lock_class *class = hlock_class(hlock); struct list_head *hash_head = chainhashentry(chain_key); struct lock_chain *chain; struct held_lock *hlock_curr, *hlock_next; @@ -1640,7 +1656,7 @@ cache_hit: if (likely(cn + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) { chain->base = cn; for (j = 0; j < chain->depth - 1; j++, i++) { - int lock_id = curr->held_locks[i].class - lock_classes; + int lock_id = curr->held_locks[i].class_idx - 1; chain_hlocks[chain->base + j] = lock_id; } chain_hlocks[chain->base + j] = class - lock_classes; @@ -1736,7 +1752,7 @@ static void check_chain_key(struct task_struct *curr) WARN_ON(1); return; } - id = hlock->class - lock_classes; + id = hlock->class_idx - 1; if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS)) return; @@ -1781,7 +1797,7 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this, print_lock(this); printk("{%s} state was registered at:\n", usage_str[prev_bit]); - print_stack_trace(this->class->usage_traces + prev_bit, 1); + print_stack_trace(hlock_class(this)->usage_traces + prev_bit, 1); print_irqtrace_events(curr); printk("\nother info that might help us debug this:\n"); @@ -1800,7 +1816,7 @@ static inline int valid_state(struct task_struct *curr, struct held_lock *this, enum lock_usage_bit new_bit, enum lock_usage_bit bad_bit) { - if (unlikely(this->class->usage_mask & (1 << bad_bit))) + if (unlikely(hlock_class(this)->usage_mask & (1 << bad_bit))) return print_usage_bug(curr, this, bad_bit, new_bit); return 1; } @@ -1839,7 +1855,7 @@ print_irq_inversion_bug(struct task_struct *curr, struct lock_class *other, lockdep_print_held_locks(curr); printk("\nthe first lock's dependencies:\n"); - print_lock_dependencies(this->class, 0); + print_lock_dependencies(hlock_class(this), 0); printk("\nthe second lock's dependencies:\n"); print_lock_dependencies(other, 0); @@ -1862,7 +1878,7 @@ check_usage_forwards(struct task_struct *curr, struct held_lock *this, find_usage_bit = bit; /* fills in */ - ret = find_usage_forwards(this->class, 0); + ret = find_usage_forwards(hlock_class(this), 0); if (!ret || ret == 1) return ret; @@ -1881,7 +1897,7 @@ check_usage_backwards(struct task_struct *curr, struct held_lock *this, find_usage_bit = bit; /* fills in */ - ret = find_usage_backwards(this->class, 0); + ret = find_usage_backwards(hlock_class(this), 0); if (!ret || ret == 1) return ret; @@ -1947,7 +1963,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, LOCK_ENABLED_HARDIRQS_READ, "hard-read")) return 0; #endif - if (hardirq_verbose(this->class)) + if (hardirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_USED_IN_SOFTIRQ: @@ -1972,7 +1988,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, LOCK_ENABLED_SOFTIRQS_READ, "soft-read")) return 0; #endif - if (softirq_verbose(this->class)) + if (softirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_USED_IN_HARDIRQ_READ: @@ -1985,7 +2001,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, if (!check_usage_forwards(curr, this, LOCK_ENABLED_HARDIRQS, "hard")) return 0; - if (hardirq_verbose(this->class)) + if (hardirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_USED_IN_SOFTIRQ_READ: @@ -1998,7 +2014,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, if (!check_usage_forwards(curr, this, LOCK_ENABLED_SOFTIRQS, "soft")) return 0; - if (softirq_verbose(this->class)) + if (softirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_ENABLED_HARDIRQS: @@ -2024,7 +2040,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, LOCK_USED_IN_HARDIRQ_READ, "hard-read")) return 0; #endif - if (hardirq_verbose(this->class)) + if (hardirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_ENABLED_SOFTIRQS: @@ -2050,7 +2066,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, LOCK_USED_IN_SOFTIRQ_READ, "soft-read")) return 0; #endif - if (softirq_verbose(this->class)) + if (softirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_ENABLED_HARDIRQS_READ: @@ -2065,7 +2081,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, LOCK_USED_IN_HARDIRQ, "hard")) return 0; #endif - if (hardirq_verbose(this->class)) + if (hardirq_verbose(hlock_class(this))) ret = 2; break; case LOCK_ENABLED_SOFTIRQS_READ: @@ -2080,7 +2096,7 @@ static int mark_lock_irq(struct task_struct *curr, struct held_lock *this, LOCK_USED_IN_SOFTIRQ, "soft")) return 0; #endif - if (softirq_verbose(this->class)) + if (softirq_verbose(hlock_class(this))) ret = 2; break; default: @@ -2396,7 +2412,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this, * If already set then do not dirty the cacheline, * nor do any checks: */ - if (likely(this->class->usage_mask & new_mask)) + if (likely(hlock_class(this)->usage_mask & new_mask)) return 1; if (!graph_lock()) @@ -2404,14 +2420,14 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this, /* * Make sure we didnt race: */ - if (unlikely(this->class->usage_mask & new_mask)) { + if (unlikely(hlock_class(this)->usage_mask & new_mask)) { graph_unlock(); return 1; } - this->class->usage_mask |= new_mask; + hlock_class(this)->usage_mask |= new_mask; - if (!save_trace(this->class->usage_traces + new_bit)) + if (!save_trace(hlock_class(this)->usage_traces + new_bit)) return 0; switch (new_bit) { @@ -2545,8 +2561,9 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, return 0; hlock = curr->held_locks + depth; - - hlock->class = class; + if (DEBUG_LOCKS_WARN_ON(!class)) + return 0; + hlock->class_idx = class - lock_classes + 1; hlock->acquire_ip = ip; hlock->instance = lock; hlock->trylock = trylock; @@ -2690,7 +2707,7 @@ __lock_set_subclass(struct lockdep_map *lock, found_it: class = register_lock_class(lock, subclass, 0); - hlock->class = class; + hlock->class_idx = class - lock_classes + 1; curr->lockdep_depth = i; curr->curr_chain_key = hlock->prev_chain_key; @@ -2698,7 +2715,7 @@ found_it: for (; i < depth; i++) { hlock = curr->held_locks + i; if (!__lock_acquire(hlock->instance, - hlock->class->subclass, hlock->trylock, + hlock_class(hlock)->subclass, hlock->trylock, hlock->read, hlock->check, hlock->hardirqs_off, hlock->acquire_ip)) return 0; @@ -2759,7 +2776,7 @@ found_it: for (i++; i < depth; i++) { hlock = curr->held_locks + i; if (!__lock_acquire(hlock->instance, - hlock->class->subclass, hlock->trylock, + hlock_class(hlock)->subclass, hlock->trylock, hlock->read, hlock->check, hlock->hardirqs_off, hlock->acquire_ip)) return 0; @@ -2804,7 +2821,7 @@ static int lock_release_nested(struct task_struct *curr, #ifdef CONFIG_DEBUG_LOCKDEP hlock->prev_chain_key = 0; - hlock->class = NULL; + hlock->class_idx = 0; hlock->acquire_ip = 0; hlock->irq_context = 0; #endif @@ -3000,9 +3017,9 @@ __lock_contended(struct lockdep_map *lock, unsigned long ip) found_it: hlock->waittime_stamp = sched_clock(); - point = lock_contention_point(hlock->class, ip); + point = lock_contention_point(hlock_class(hlock), ip); - stats = get_lock_stats(hlock->class); + stats = get_lock_stats(hlock_class(hlock)); if (point < ARRAY_SIZE(stats->contention_point)) stats->contention_point[i]++; if (lock->cpu != smp_processor_id()) @@ -3048,7 +3065,7 @@ found_it: hlock->holdtime_stamp = now; } - stats = get_lock_stats(hlock->class); + stats = get_lock_stats(hlock_class(hlock)); if (waittime) { if (hlock->read) lock_time_inc(&stats->read_waittime, waittime); diff --git a/kernel/lockdep_internals.h b/kernel/lockdep_internals.h index 68d44ec77ab5..55db193d366d 100644 --- a/kernel/lockdep_internals.h +++ b/kernel/lockdep_internals.h @@ -17,9 +17,6 @@ */ #define MAX_LOCKDEP_ENTRIES 8192UL -#define MAX_LOCKDEP_KEYS_BITS 11 -#define MAX_LOCKDEP_KEYS (1UL << MAX_LOCKDEP_KEYS_BITS) - #define MAX_LOCKDEP_CHAINS_BITS 14 #define MAX_LOCKDEP_CHAINS (1UL << MAX_LOCKDEP_CHAINS_BITS) -- cgit v1.2.3 From 4f3e7524b2e703d9f8b02ac338153a53dd7ede66 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 11 Aug 2008 09:30:23 +0200 Subject: lockdep: map_acquire Most the free-standing lock_acquire() usages look remarkably similar, sweep them into a new helper. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- fs/jbd/transaction.c | 4 ++-- fs/jbd2/transaction.c | 4 ++-- include/linux/lockdep.h | 12 ++++++++++++ kernel/workqueue.c | 24 ++++++++++++------------ 4 files changed, 28 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index 8dee32007500..31a4bd7f78d7 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -291,7 +291,7 @@ handle_t *journal_start(journal_t *journal, int nblocks) goto out; } - lock_acquire(&handle->h_lockdep_map, 0, 0, 0, 2, _THIS_IP_); + map_acquire(&handle->h_lockdep_map); out: return handle; @@ -1448,7 +1448,7 @@ int journal_stop(handle_t *handle) spin_unlock(&journal->j_state_lock); } - lock_release(&handle->h_lockdep_map, 1, _THIS_IP_); + map_release(&handle->h_lockdep_map); jbd_free_handle(handle); return err; diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 4f7cadbb19fa..c074971215ed 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -301,7 +301,7 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks) goto out; } - lock_acquire(&handle->h_lockdep_map, 0, 0, 0, 2, _THIS_IP_); + map_acquire(&handle->h_lockdep_map); out: return handle; } @@ -1279,7 +1279,7 @@ int jbd2_journal_stop(handle_t *handle) spin_unlock(&journal->j_state_lock); } - lock_release(&handle->h_lockdep_map, 1, _THIS_IP_); + map_release(&handle->h_lockdep_map); jbd2_free_handle(handle); return err; diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index b49bfa8e4a5c..e431d1d6eaf3 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -459,4 +459,16 @@ static inline void print_irqtrace_events(struct task_struct *curr) # define rwsem_release(l, n, i) do { } while (0) #endif +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# ifdef CONFIG_PROVE_LOCKING +# define map_acquire(l) lock_acquire(l, 0, 0, 0, 2, _THIS_IP_) +# else +# define map_acquire(l) lock_acquire(l, 0, 0, 0, 1, _THIS_IP_) +# endif +# define map_release(l) lock_release(l, 1, _THIS_IP_) +#else +# define map_acquire(l) do { } while (0) +# define map_release(l) do { } while (0) +#endif + #endif /* __LINUX_LOCKDEP_H */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ec7e4f62aaff..53564ae894a6 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -290,11 +290,11 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) BUG_ON(get_wq_data(work) != cwq); work_clear_pending(work); - lock_acquire(&cwq->wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_); - lock_acquire(&lockdep_map, 0, 0, 0, 2, _THIS_IP_); + map_acquire(&cwq->wq->lockdep_map); + map_acquire(&lockdep_map); f(work); - lock_release(&lockdep_map, 1, _THIS_IP_); - lock_release(&cwq->wq->lockdep_map, 1, _THIS_IP_); + map_release(&lockdep_map); + map_release(&cwq->wq->lockdep_map); if (unlikely(in_atomic() || lockdep_depth(current) > 0)) { printk(KERN_ERR "BUG: workqueue leaked lock or atomic: " @@ -413,8 +413,8 @@ void flush_workqueue(struct workqueue_struct *wq) int cpu; might_sleep(); - lock_acquire(&wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_); - lock_release(&wq->lockdep_map, 1, _THIS_IP_); + map_acquire(&wq->lockdep_map); + map_release(&wq->lockdep_map); for_each_cpu_mask_nr(cpu, *cpu_map) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); } @@ -441,8 +441,8 @@ int flush_work(struct work_struct *work) if (!cwq) return 0; - lock_acquire(&cwq->wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_); - lock_release(&cwq->wq->lockdep_map, 1, _THIS_IP_); + map_acquire(&cwq->wq->lockdep_map); + map_release(&cwq->wq->lockdep_map); prev = NULL; spin_lock_irq(&cwq->lock); @@ -536,8 +536,8 @@ static void wait_on_work(struct work_struct *work) might_sleep(); - lock_acquire(&work->lockdep_map, 0, 0, 0, 2, _THIS_IP_); - lock_release(&work->lockdep_map, 1, _THIS_IP_); + map_acquire(&work->lockdep_map); + map_release(&work->lockdep_map); cwq = get_wq_data(work); if (!cwq) @@ -861,8 +861,8 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) if (cwq->thread == NULL) return; - lock_acquire(&cwq->wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_); - lock_release(&cwq->wq->lockdep_map, 1, _THIS_IP_); + map_acquire(&cwq->wq->lockdep_map); + map_release(&cwq->wq->lockdep_map); flush_cpu_workqueue(cwq); /* -- cgit v1.2.3 From 7531e2f34d1d551b096143f19111139f0dd84c8b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 11 Aug 2008 09:30:24 +0200 Subject: lockdep: lock protection locks On Fri, 2008-08-01 at 16:26 -0700, Linus Torvalds wrote: > On Fri, 1 Aug 2008, David Miller wrote: > > > > Taking more than a few locks of the same class at once is bad > > news and it's better to find an alternative method. > > It's not always wrong. > > If you can guarantee that anybody that takes more than one lock of a > particular class will always take a single top-level lock _first_, then > that's all good. You can obviously screw up and take the same lock _twice_ > (which will deadlock), but at least you cannot get into ABBA situations. > > So maybe the right thing to do is to just teach lockdep about "lock > protection locks". That would have solved the multi-queue issues for > networking too - all the actual network drivers would still have taken > just their single queue lock, but the one case that needs to take all of > them would have taken a separate top-level lock first. > > Never mind that the multi-queue locks were always taken in the same order: > it's never wrong to just have some top-level serialization, and anybody > who needs to take locks might as well do , because they sure as > hell aren't going to be on _any_ fastpaths. > > So the simplest solution really sounds like just teaching lockdep about > that one special case. It's not "nesting" exactly, although it's obviously > related to it. Do as Linus suggested. The lock protection lock is called nest_lock. Note that we still have the MAX_LOCK_DEPTH (48) limit to consider, so anything that spills that it still up shit creek. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 34 ++++++++++++++++++---------------- include/linux/rcuclassic.h | 2 +- kernel/lockdep.c | 26 +++++++++++++++++++++----- 3 files changed, 40 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index e431d1d6eaf3..93a8cc02a033 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -211,6 +211,7 @@ struct held_lock { u64 prev_chain_key; unsigned long acquire_ip; struct lockdep_map *instance; + struct lockdep_map *nest_lock; #ifdef CONFIG_LOCK_STAT u64 waittime_stamp; u64 holdtime_stamp; @@ -297,7 +298,8 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name, * 2: full validation */ extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass, - int trylock, int read, int check, unsigned long ip); + int trylock, int read, int check, + struct lockdep_map *nest_lock, unsigned long ip); extern void lock_release(struct lockdep_map *lock, int nested, unsigned long ip); @@ -319,7 +321,7 @@ static inline void lockdep_on(void) { } -# define lock_acquire(l, s, t, r, c, i) do { } while (0) +# define lock_acquire(l, s, t, r, c, n, i) do { } while (0) # define lock_release(l, n, i) do { } while (0) # define lock_set_subclass(l, s, i) do { } while (0) # define lockdep_init() do { } while (0) @@ -407,9 +409,9 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING -# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) +# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) # else -# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) +# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) # endif # define spin_release(l, n, i) lock_release(l, n, i) #else @@ -419,11 +421,11 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING -# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) -# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 2, i) +# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) +# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 2, NULL, i) # else -# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) -# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 1, i) +# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) +# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 1, NULL, i) # endif # define rwlock_release(l, n, i) lock_release(l, n, i) #else @@ -434,9 +436,9 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING -# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) +# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) # else -# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) +# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) # endif # define mutex_release(l, n, i) lock_release(l, n, i) #else @@ -446,11 +448,11 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING -# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) -# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, i) +# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) +# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, NULL, i) # else -# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) -# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, i) +# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) +# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, NULL, i) # endif # define rwsem_release(l, n, i) lock_release(l, n, i) #else @@ -461,9 +463,9 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING -# define map_acquire(l) lock_acquire(l, 0, 0, 0, 2, _THIS_IP_) +# define map_acquire(l) lock_acquire(l, 0, 0, 0, 2, NULL, _THIS_IP_) # else -# define map_acquire(l) lock_acquire(l, 0, 0, 0, 1, _THIS_IP_) +# define map_acquire(l) lock_acquire(l, 0, 0, 0, 1, NULL, _THIS_IP_) # endif # define map_release(l) lock_release(l, 1, _THIS_IP_) #else diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index 8c774905dcfe..4ab843622727 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -117,7 +117,7 @@ extern int rcu_needs_cpu(int cpu); #ifdef CONFIG_DEBUG_LOCK_ALLOC extern struct lockdep_map rcu_lock_map; # define rcu_read_acquire() \ - lock_acquire(&rcu_lock_map, 0, 0, 2, 1, _THIS_IP_) + lock_acquire(&rcu_lock_map, 0, 0, 2, 1, NULL, _THIS_IP_) # define rcu_read_release() lock_release(&rcu_lock_map, 1, _THIS_IP_) #else # define rcu_read_acquire() do { } while (0) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index d3c72ad8d09e..410c3365ad8f 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -1372,18 +1372,32 @@ check_deadlock(struct task_struct *curr, struct held_lock *next, struct lockdep_map *next_instance, int read) { struct held_lock *prev; + struct held_lock *nest = NULL; int i; for (i = 0; i < curr->lockdep_depth; i++) { prev = curr->held_locks + i; + + if (prev->instance == next->nest_lock) + nest = prev; + if (hlock_class(prev) != hlock_class(next)) continue; + /* * Allow read-after-read recursion of the same * lock class (i.e. read_lock(lock)+read_lock(lock)): */ if ((read == 2) && prev->read) return 2; + + /* + * We're holding the nest_lock, which serializes this lock's + * nesting behaviour. + */ + if (nest) + return 2; + return print_deadlock_bug(curr, prev, next); } return 1; @@ -2507,7 +2521,7 @@ EXPORT_SYMBOL_GPL(lockdep_init_map); */ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, int trylock, int read, int check, int hardirqs_off, - unsigned long ip) + struct lockdep_map *nest_lock, unsigned long ip) { struct task_struct *curr = current; struct lock_class *class = NULL; @@ -2566,6 +2580,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, hlock->class_idx = class - lock_classes + 1; hlock->acquire_ip = ip; hlock->instance = lock; + hlock->nest_lock = nest_lock; hlock->trylock = trylock; hlock->read = read; hlock->check = check; @@ -2717,7 +2732,7 @@ found_it: if (!__lock_acquire(hlock->instance, hlock_class(hlock)->subclass, hlock->trylock, hlock->read, hlock->check, hlock->hardirqs_off, - hlock->acquire_ip)) + hlock->nest_lock, hlock->acquire_ip)) return 0; } @@ -2778,7 +2793,7 @@ found_it: if (!__lock_acquire(hlock->instance, hlock_class(hlock)->subclass, hlock->trylock, hlock->read, hlock->check, hlock->hardirqs_off, - hlock->acquire_ip)) + hlock->nest_lock, hlock->acquire_ip)) return 0; } @@ -2915,7 +2930,8 @@ EXPORT_SYMBOL_GPL(lock_set_subclass); * and also avoid lockdep recursion: */ void lock_acquire(struct lockdep_map *lock, unsigned int subclass, - int trylock, int read, int check, unsigned long ip) + int trylock, int read, int check, + struct lockdep_map *nest_lock, unsigned long ip) { unsigned long flags; @@ -2930,7 +2946,7 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass, current->lockdep_recursion = 1; __lock_acquire(lock, subclass, trylock, read, check, - irqs_disabled_flags(flags), ip); + irqs_disabled_flags(flags), nest_lock, ip); current->lockdep_recursion = 0; raw_local_irq_restore(flags); } -- cgit v1.2.3 From b7d39aff91454f2534db2275f55908656ec0470c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 11 Aug 2008 09:30:24 +0200 Subject: lockdep: spin_lock_nest_lock() Expose the new lock protection lock. This can be used to annotate places where we take multiple locks of the same class and avoid deadlocks by always taking another (top-level) lock first. NOTE: we're still bound to the MAX_LOCK_DEPTH (48) limit. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 2 ++ include/linux/spinlock.h | 6 ++++++ include/linux/spinlock_api_smp.h | 2 ++ kernel/spinlock.c | 11 +++++++++++ 4 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 93a8cc02a033..4452c04a7f6e 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -410,8 +410,10 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING # define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) +# define spin_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i) # else # define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) +# define spin_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, NULL, i) # endif # define spin_release(l, n, i) lock_release(l, n, i) #else diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 61e5610ad165..e0c0fccced46 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -183,8 +183,14 @@ do { \ #ifdef CONFIG_DEBUG_LOCK_ALLOC # define spin_lock_nested(lock, subclass) _spin_lock_nested(lock, subclass) +# define spin_lock_nest_lock(lock, nest_lock) \ + do { \ + typecheck(struct lockdep_map *, &(nest_lock)->dep_map);\ + _spin_lock_nest_lock(lock, &(nest_lock)->dep_map); \ + } while (0) #else # define spin_lock_nested(lock, subclass) _spin_lock(lock) +# define spin_lock_nest_lock(lock, nest_lock) _spin_lock(lock) #endif #define write_lock(lock) _write_lock(lock) diff --git a/include/linux/spinlock_api_smp.h b/include/linux/spinlock_api_smp.h index 8a2307ce7296..d79845d034b5 100644 --- a/include/linux/spinlock_api_smp.h +++ b/include/linux/spinlock_api_smp.h @@ -22,6 +22,8 @@ int in_lock_functions(unsigned long addr); void __lockfunc _spin_lock(spinlock_t *lock) __acquires(lock); void __lockfunc _spin_lock_nested(spinlock_t *lock, int subclass) __acquires(lock); +void __lockfunc _spin_lock_nest_lock(spinlock_t *lock, struct lockdep_map *map) + __acquires(lock); void __lockfunc _read_lock(rwlock_t *lock) __acquires(lock); void __lockfunc _write_lock(rwlock_t *lock) __acquires(lock); void __lockfunc _spin_lock_bh(spinlock_t *lock) __acquires(lock); diff --git a/kernel/spinlock.c b/kernel/spinlock.c index a1fb54c93cdd..44baeea94ab9 100644 --- a/kernel/spinlock.c +++ b/kernel/spinlock.c @@ -292,6 +292,7 @@ void __lockfunc _spin_lock_nested(spinlock_t *lock, int subclass) } EXPORT_SYMBOL(_spin_lock_nested); + unsigned long __lockfunc _spin_lock_irqsave_nested(spinlock_t *lock, int subclass) { unsigned long flags; @@ -314,6 +315,16 @@ unsigned long __lockfunc _spin_lock_irqsave_nested(spinlock_t *lock, int subclas EXPORT_SYMBOL(_spin_lock_irqsave_nested); +void __lockfunc _spin_lock_nest_lock(spinlock_t *lock, + struct lockdep_map *nest_lock) +{ + preempt_disable(); + spin_acquire_nest(&lock->dep_map, 0, 0, nest_lock, _RET_IP_); + LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock); +} + +EXPORT_SYMBOL(_spin_lock_nest_lock); + #endif void __lockfunc _spin_unlock(spinlock_t *lock) -- cgit v1.2.3 From 3295f0ef9ff048a4619ede597ad9ec9cab725654 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 11 Aug 2008 10:30:30 +0200 Subject: lockdep: rename map_[acquire|release]() => lock_map_[acquire|release]() the names were too generic: drivers/uio/uio.c:87: error: expected identifier or '(' before 'do' drivers/uio/uio.c:87: error: expected identifier or '(' before 'while' drivers/uio/uio.c:113: error: 'map_release' undeclared here (not in a function) Signed-off-by: Ingo Molnar --- fs/jbd/transaction.c | 4 ++-- fs/jbd2/transaction.c | 4 ++-- include/linux/lockdep.h | 10 +++++----- kernel/workqueue.c | 24 ++++++++++++------------ 4 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index 31a4bd7f78d7..0540ca27a446 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -291,7 +291,7 @@ handle_t *journal_start(journal_t *journal, int nblocks) goto out; } - map_acquire(&handle->h_lockdep_map); + lock_map_acquire(&handle->h_lockdep_map); out: return handle; @@ -1448,7 +1448,7 @@ int journal_stop(handle_t *handle) spin_unlock(&journal->j_state_lock); } - map_release(&handle->h_lockdep_map); + lock_map_release(&handle->h_lockdep_map); jbd_free_handle(handle); return err; diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index c074971215ed..e5d540588fa9 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -301,7 +301,7 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks) goto out; } - map_acquire(&handle->h_lockdep_map); + lock_map_acquire(&handle->h_lockdep_map); out: return handle; } @@ -1279,7 +1279,7 @@ int jbd2_journal_stop(handle_t *handle) spin_unlock(&journal->j_state_lock); } - map_release(&handle->h_lockdep_map); + lock_map_release(&handle->h_lockdep_map); jbd2_free_handle(handle); return err; diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 4452c04a7f6e..67f42b300c65 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -465,14 +465,14 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING -# define map_acquire(l) lock_acquire(l, 0, 0, 0, 2, NULL, _THIS_IP_) +# define lock_map_acquire(l) lock_acquire(l, 0, 0, 0, 2, NULL, _THIS_IP_) # else -# define map_acquire(l) lock_acquire(l, 0, 0, 0, 1, NULL, _THIS_IP_) +# define lock_map_acquire(l) lock_acquire(l, 0, 0, 0, 1, NULL, _THIS_IP_) # endif -# define map_release(l) lock_release(l, 1, _THIS_IP_) +# define lock_map_release(l) lock_release(l, 1, _THIS_IP_) #else -# define map_acquire(l) do { } while (0) -# define map_release(l) do { } while (0) +# define lock_map_acquire(l) do { } while (0) +# define lock_map_release(l) do { } while (0) #endif #endif /* __LINUX_LOCKDEP_H */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 53564ae894a6..8bb5b68fb3a9 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -290,11 +290,11 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) BUG_ON(get_wq_data(work) != cwq); work_clear_pending(work); - map_acquire(&cwq->wq->lockdep_map); - map_acquire(&lockdep_map); + lock_map_acquire(&cwq->wq->lockdep_map); + lock_map_acquire(&lockdep_map); f(work); - map_release(&lockdep_map); - map_release(&cwq->wq->lockdep_map); + lock_map_release(&lockdep_map); + lock_map_release(&cwq->wq->lockdep_map); if (unlikely(in_atomic() || lockdep_depth(current) > 0)) { printk(KERN_ERR "BUG: workqueue leaked lock or atomic: " @@ -413,8 +413,8 @@ void flush_workqueue(struct workqueue_struct *wq) int cpu; might_sleep(); - map_acquire(&wq->lockdep_map); - map_release(&wq->lockdep_map); + lock_map_acquire(&wq->lockdep_map); + lock_map_release(&wq->lockdep_map); for_each_cpu_mask_nr(cpu, *cpu_map) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); } @@ -441,8 +441,8 @@ int flush_work(struct work_struct *work) if (!cwq) return 0; - map_acquire(&cwq->wq->lockdep_map); - map_release(&cwq->wq->lockdep_map); + lock_map_acquire(&cwq->wq->lockdep_map); + lock_map_release(&cwq->wq->lockdep_map); prev = NULL; spin_lock_irq(&cwq->lock); @@ -536,8 +536,8 @@ static void wait_on_work(struct work_struct *work) might_sleep(); - map_acquire(&work->lockdep_map); - map_release(&work->lockdep_map); + lock_map_acquire(&work->lockdep_map); + lock_map_release(&work->lockdep_map); cwq = get_wq_data(work); if (!cwq) @@ -861,8 +861,8 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) if (cwq->thread == NULL) return; - map_acquire(&cwq->wq->lockdep_map); - map_release(&cwq->wq->lockdep_map); + lock_map_acquire(&cwq->wq->lockdep_map); + lock_map_release(&cwq->wq->lockdep_map); flush_cpu_workqueue(cwq); /* -- cgit v1.2.3 From b42e737e576339c795d9ac77a1fce6057f6bc0cf Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 11 Aug 2008 12:34:42 +0200 Subject: lockdep: fix overflow in the hlock shrinkage code There is a overflow by 1 case in the new shrunken hlock code. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 67f42b300c65..c88aa3d8e87f 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -191,7 +191,12 @@ struct lock_chain { }; #define MAX_LOCKDEP_KEYS_BITS 11 -#define MAX_LOCKDEP_KEYS (1UL << MAX_LOCKDEP_KEYS_BITS) +/* + * Subtract one because we offset hlock->class_idx by 1 in order + * to make 0 mean no class. This avoids overflowing the class_idx + * bitfield and hitting the BUG in hlock_class(). + */ +#define MAX_LOCKDEP_KEYS ((1UL << MAX_LOCKDEP_KEYS_BITS) - 1) struct held_lock { /* -- cgit v1.2.3 From 13fa00a8780885edcdf0bc53b81e5d0fec71119a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 11 Aug 2008 20:59:59 +1000 Subject: powerpc: Remove include/linux/harrier_defs.h It was only used by code in arch/ppc, and arch/ppc is gone, so remove the unused harrier_defs.h as well. Signed-off-by: Paul Mackerras --- include/linux/harrier_defs.h | 212 ------------------------------------------- 1 file changed, 212 deletions(-) delete mode 100644 include/linux/harrier_defs.h (limited to 'include/linux') diff --git a/include/linux/harrier_defs.h b/include/linux/harrier_defs.h deleted file mode 100644 index efef11db790f..000000000000 --- a/include/linux/harrier_defs.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * include/linux/harrier_defs.h - * - * Definitions for Motorola MCG Harrier North Bridge & Memory controller - * - * Author: Dale Farnsworth - * dale.farnsworth@mvista.com - * - * Extracted from asm-ppc/harrier.h by: - * Randy Vinson - * rvinson@mvista.com - * - * Copyright 2001-2002 MontaVista Software Inc. - * - * 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. - */ - -#ifndef __ASMPPC_HARRIER_DEFS_H -#define __ASMPPC_HARRIER_DEFS_H - -#define HARRIER_DEFAULT_XCSR_BASE 0xfeff0000 - -#define HARRIER_VEND_DEV_ID 0x1057480b - -#define HARRIER_VENI_OFF 0x00 - -#define HARRIER_REVI_OFF 0x05 -#define HARRIER_UCTL_OFF 0xd0 -#define HARRIER_XTAL64_MASK 0x02 - -#define HARRIER_MISC_CSR_OFF 0x1c -#define HARRIER_RSTOUT 0x01000000 -#define HARRIER_SYSCON 0x08000000 -#define HARRIER_EREADY 0x10000000 -#define HARRIER_ERDYS 0x20000000 - -/* Function exception registers */ -#define HARRIER_FEEN_OFF 0x40 /* enable */ -#define HARRIER_FEST_OFF 0x44 /* status */ -#define HARRIER_FEMA_OFF 0x48 /* mask */ -#define HARRIER_FECL_OFF 0x4c /* clear */ - -#define HARRIER_FE_DMA 0x80 -#define HARRIER_FE_MIDB 0x40 -#define HARRIER_FE_MIM0 0x20 -#define HARRIER_FE_MIM1 0x10 -#define HARRIER_FE_MIP 0x08 -#define HARRIER_FE_UA0 0x04 -#define HARRIER_FE_UA1 0x02 -#define HARRIER_FE_ABT 0x01 - -#define HARRIER_SERIAL_0_OFF 0xc0 - -#define HARRIER_MBAR_OFF 0xe0 -#define HARRIER_MBAR_MSK 0xfffc0000 -#define HARRIER_MPIC_CSR_OFF 0xe4 -#define HARRIER_MPIC_OPI_ENABLE 0x40 -#define HARRIER_MPIC_IFEVP_OFF 0x10200 -#define HARRIER_MPIC_IFEVP_VECT_MSK 0xff -#define HARRIER_MPIC_IFEDE_OFF 0x10210 - -/* - * Define the Memory Controller register offsets. - */ -#define HARRIER_SDBA_OFF 0x110 -#define HARRIER_SDBB_OFF 0x114 -#define HARRIER_SDBC_OFF 0x118 -#define HARRIER_SDBD_OFF 0x11c -#define HARRIER_SDBE_OFF 0x120 -#define HARRIER_SDBF_OFF 0x124 -#define HARRIER_SDBG_OFF 0x128 -#define HARRIER_SDBH_OFF 0x12c - -#define HARRIER_SDB_ENABLE 0x00000100 -#define HARRIER_SDB_SIZE_MASK 0xf -#define HARRIER_SDB_SIZE_SHIFT 16 -#define HARRIER_SDB_BASE_MASK 0xff -#define HARRIER_SDB_BASE_SHIFT 24 - -/* - * Define outbound register offsets. - */ -#define HARRIER_OTAD0_OFF 0x220 -#define HARRIER_OTOF0_OFF 0x224 -#define HARRIER_OTAD1_OFF 0x228 -#define HARRIER_OTOF1_OFF 0x22c -#define HARRIER_OTAD2_OFF 0x230 -#define HARRIER_OTOF2_OFF 0x234 -#define HARRIER_OTAD3_OFF 0x238 -#define HARRIER_OTOF3_OFF 0x23c - -#define HARRIER_OTADX_START_MSK 0xffff0000UL -#define HARRIER_OTADX_END_MSK 0x0000ffffUL - -#define HARRIER_OTOFX_OFF_MSK 0xffff0000UL -#define HARRIER_OTOFX_ENA 0x80UL -#define HARRIER_OTOFX_WPE 0x10UL -#define HARRIER_OTOFX_SGE 0x08UL -#define HARRIER_OTOFX_RAE 0x04UL -#define HARRIER_OTOFX_MEM 0x02UL -#define HARRIER_OTOFX_IOM 0x01UL - -/* - * Define generic message passing register offsets - */ -/* Mirrored registers (visible from both PowerPC and PCI space) */ -#define HARRIER_XCSR_MP_BASE_OFF 0x290 /* base offset in XCSR space */ -#define HARRIER_PMEP_MP_BASE_OFF 0x100 /* base offset in PMEM space */ -#define HARRIER_MGOM0_OFF 0x00 /* outbound msg 0 */ -#define HARRIER_MGOM1_OFF 0x04 /* outbound msg 1 */ -#define HARRIER_MGOD_OFF 0x08 /* outbound doorbells */ - -#define HARRIER_MGIM0_OFF 0x10 /* inbound msg 0 */ -#define HARRIER_MGIM1_OFF 0x14 /* inbound msg 1 */ -#define HARRIER_MGID_OFF 0x18 /* inbound doorbells */ - -/* PowerPC-only registers */ -#define HARRIER_MGIDM_OFF 0x20 /* inbound doorbell mask */ - -/* PCI-only registers */ -#define HARRIER_PMEP_MGST_OFF 0x20 /* (outbound) interrupt status */ -#define HARRIER_PMEP_MGMS_OFF 0x24 /* (outbound) interrupt mask */ -#define HARRIER_MG_OMI0 (1<<4) -#define HARRIER_MG_OMI1 (1<<5) - -#define HARRIER_PMEP_MGODM_OFF 0x28 /* outbound doorbell mask */ - -/* - * Define PCI configuration space register offsets - */ -#define HARRIER_XCSR_TO_PCFS_OFF 0x300 - -/* - * Define message passing attribute register offset - */ -#define HARRIER_MPAT_OFF 0x44 - -/* - * Define inbound attribute register offsets. - */ -#define HARRIER_ITSZ0_OFF 0x48 -#define HARRIER_ITAT0_OFF 0x4c - -#define HARRIER_ITSZ1_OFF 0x50 -#define HARRIER_ITAT1_OFF 0x54 - -#define HARRIER_ITSZ2_OFF 0x58 -#define HARRIER_ITAT2_OFF 0x5c - -#define HARRIER_ITSZ3_OFF 0x60 -#define HARRIER_ITAT3_OFF 0x64 - -/* inbound translation size constants */ -#define HARRIER_ITSZ_MSK 0xff -#define HARRIER_ITSZ_4KB 0x00 -#define HARRIER_ITSZ_8KB 0x01 -#define HARRIER_ITSZ_16KB 0x02 -#define HARRIER_ITSZ_32KB 0x03 -#define HARRIER_ITSZ_64KB 0x04 -#define HARRIER_ITSZ_128KB 0x05 -#define HARRIER_ITSZ_256KB 0x06 -#define HARRIER_ITSZ_512KB 0x07 -#define HARRIER_ITSZ_1MB 0x08 -#define HARRIER_ITSZ_2MB 0x09 -#define HARRIER_ITSZ_4MB 0x0A -#define HARRIER_ITSZ_8MB 0x0B -#define HARRIER_ITSZ_16MB 0x0C -#define HARRIER_ITSZ_32MB 0x0D -#define HARRIER_ITSZ_64MB 0x0E -#define HARRIER_ITSZ_128MB 0x0F -#define HARRIER_ITSZ_256MB 0x10 -#define HARRIER_ITSZ_512MB 0x11 -#define HARRIER_ITSZ_1GB 0x12 -#define HARRIER_ITSZ_2GB 0x13 - -/* inbound translation offset */ -#define HARRIER_ITOF_SHIFT 0x10 -#define HARRIER_ITOF_MSK 0xffff - -/* inbound translation atttributes */ -#define HARRIER_ITAT_PRE (1<<3) -#define HARRIER_ITAT_RAE (1<<4) -#define HARRIER_ITAT_WPE (1<<5) -#define HARRIER_ITAT_MEM (1<<6) -#define HARRIER_ITAT_ENA (1<<7) -#define HARRIER_ITAT_GBL (1<<16) - -#define HARRIER_LBA_OFF 0x80 -#define HARRIER_LBA_MSK (1<<31) - -#define HARRIER_XCSR_SIZE 1024 - -/* macros to calculate message passing register offsets */ -#define HARRIER_MP_XCSR(x) ((u32)HARRIER_XCSR_MP_BASE_OFF + (u32)x) - -#define HARRIER_MP_PMEP(x) ((u32)HARRIER_PMEP_MP_BASE_OFF + (u32)x) - -/* - * Define PCI configuration space register offsets - */ -#define HARRIER_MPBAR_OFF PCI_BASE_ADDRESS_0 -#define HARRIER_ITBAR0_OFF PCI_BASE_ADDRESS_1 -#define HARRIER_ITBAR1_OFF PCI_BASE_ADDRESS_2 -#define HARRIER_ITBAR2_OFF PCI_BASE_ADDRESS_3 -#define HARRIER_ITBAR3_OFF PCI_BASE_ADDRESS_4 - -#define HARRIER_XCSR_CONFIG(x) ((u32)HARRIER_XCSR_TO_PCFS_OFF + (u32)x) - -#endif /* __ASMPPC_HARRIER_DEFS_H */ -- cgit v1.2.3 From 67182ae1c42206e516f7efb292b745e826497b24 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 10 Aug 2008 18:35:38 -0700 Subject: rcu, debug: detect stalled grace periods this is a diagnostic patch for Classic RCU. The approach is to record a timestamp at the beginning of the grace period (in rcu_start_batch()), then have rcu_check_callbacks() complain if: 1. it is running on a CPU that has holding up grace periods for a long time (say one second). This will identify the culprit assuming that the culprit has not disabled hardware irqs, instruction execution, or some such. 2. it is running on a CPU that is not holding up grace periods, but grace periods have been held up for an even longer time (say two seconds). It is enabled via the default-off CONFIG_DEBUG_RCU_STALL kernel parameter. Rather than exponential backoff, it backs off to once per 30 seconds. My feeling upon thinking on it was that if you have stalled RCU grace periods for that long, a few extra printk() messages are probably the least of your worries... Signed-off-by: Paul E. McKenney Cc: Peter Zijlstra Cc: Yinghai Lu Cc: David Witbrodt Signed-off-by: Ingo Molnar --- include/linux/rcuclassic.h | 3 ++ kernel/rcuclassic.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig.debug | 13 ++++++++ 3 files changed, 96 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index 04c728147be0..16589958b40e 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -46,6 +46,9 @@ struct rcu_ctrlblk { long cur; /* Current batch number. */ long completed; /* Number of the last completed batch */ long pending; /* Number of the last pending batch */ +#ifdef CONFIG_DEBUG_RCU_STALL + unsigned long gp_check; /* Time grace period should end, in seconds. */ +#endif /* #ifdef CONFIG_DEBUG_RCU_STALL */ int signaled; diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index d4271146a9bd..d7ec731de75c 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key rcu_lock_key; @@ -286,6 +287,81 @@ static void rcu_do_batch(struct rcu_data *rdp) * rcu_check_quiescent_state calls rcu_start_batch(0) to start the next grace * period (if necessary). */ + +#ifdef CONFIG_DEBUG_RCU_STALL + +static inline void record_gp_check_time(struct rcu_ctrlblk *rcp) +{ + rcp->gp_check = get_seconds() + 3; +} +static void print_other_cpu_stall(struct rcu_ctrlblk *rcp) +{ + int cpu; + long delta; + + /* Only let one CPU complain about others per time interval. */ + + spin_lock(&rcp->lock); + delta = get_seconds() - rcp->gp_check; + if (delta < 2L || + cpus_empty(rcp->cpumask)) { + spin_unlock(&rcp->lock); + return; + rcp->gp_check = get_seconds() + 30; + } + spin_unlock(&rcp->lock); + + /* OK, time to rat on our buddy... */ + + printk(KERN_ERR "RCU detected CPU stalls:"); + for_each_cpu_mask(cpu, rcp->cpumask) + printk(" %d", cpu); + printk(" (detected by %d, t=%lu/%lu)\n", + smp_processor_id(), get_seconds(), rcp->gp_check); +} +static void print_cpu_stall(struct rcu_ctrlblk *rcp) +{ + printk(KERN_ERR "RCU detected CPU %d stall (t=%lu/%lu)\n", + smp_processor_id(), get_seconds(), rcp->gp_check); + dump_stack(); + spin_lock(&rcp->lock); + if ((long)(get_seconds() - rcp->gp_check) >= 0L) + rcp->gp_check = get_seconds() + 30; + spin_unlock(&rcp->lock); +} +static inline void check_cpu_stall(struct rcu_ctrlblk *rcp, + struct rcu_data *rdp) +{ + long delta; + + delta = get_seconds() - rcp->gp_check; + if (cpu_isset(smp_processor_id(), rcp->cpumask) && delta >= 0L) { + + /* We haven't checked in, so go dump stack. */ + + print_cpu_stall(rcp); + + } else if (!cpus_empty(rcp->cpumask) && delta >= 2L) { + + /* They had two seconds to dump stack, so complain. */ + + print_other_cpu_stall(rcp); + + } +} + +#else /* #ifdef CONFIG_DEBUG_RCU_STALL */ + +static inline void record_gp_check_time(struct rcu_ctrlblk *rcp) +{ +} +static inline void check_cpu_stall(struct rcu_ctrlblk *rcp, + struct rcu_data *rdp) +{ +} + +#endif /* #else #ifdef CONFIG_DEBUG_RCU_STALL */ + /* * Register a new batch of callbacks, and start it up if there is currently no * active batch and the batch to be registered has not already occurred. @@ -296,6 +372,7 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) if (rcp->cur != rcp->pending && rcp->completed == rcp->cur) { rcp->cur++; + record_gp_check_time(rcp); /* * Accessing nohz_cpu_mask before incrementing rcp->cur needs a @@ -489,6 +566,9 @@ static void rcu_process_callbacks(struct softirq_action *unused) static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) { + /* Check for CPU stalls, if enabled. */ + check_cpu_stall(rcp, rdp); + if (rdp->nxtlist) { /* * This cpu has pending rcu entries and the grace period diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e1d4764435ed..2fb6d90bf1e6 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -597,6 +597,19 @@ config RCU_TORTURE_TEST_RUNNABLE Say N here if you want the RCU torture tests to start only after being manually enabled via /proc. +config RCU_CPU_STALL + bool "Check for stalled CPUs delaying RCU grace periods" + depends on CLASSIC_RCU + default n + help + This option causes RCU to printk information on which + CPUs are delaying the current grace period, but only when + the grace period extends for excessive time periods. + + Say Y if you want RCU to perform such checks. + + Say N if you are unsure. + config KPROBES_SANITY_TEST bool "Kprobes sanity tests" depends on DEBUG_KERNEL -- cgit v1.2.3 From e5f363e358cf16e4ad13a6826e15088c5495efe9 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 11 Aug 2008 12:37:27 +0200 Subject: lockdep: increase MAX_LOCKDEP_KEYS certain configs produce: [ 70.076229] BUG: MAX_LOCKDEP_KEYS too low! [ 70.080230] turning off the locking correctness validator. tune them up. Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index c88aa3d8e87f..331e5f1c2d8e 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -190,7 +190,7 @@ struct lock_chain { u64 chain_key; }; -#define MAX_LOCKDEP_KEYS_BITS 11 +#define MAX_LOCKDEP_KEYS_BITS 13 /* * Subtract one because we offset hlock->class_idx by 1 in order * to make 0 mean no class. This avoids overflowing the class_idx -- cgit v1.2.3 From a8c84df9f71e4a7b14bdd41687a70d366c087eef Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 31 Jul 2008 15:48:07 +1000 Subject: intel/agp: rewrite GTT on resume On my Intel chipset (965GM), the GTT is entirely erased across suspend/resume. This patch simply re-plays the current mapping at resume time to restore the table.=20 I noticed this once I started relying on persistent GTT mappings across VT switch in our GEM work -- the old X server and DRM code carefully unbind all memory from the GTT on VT switch, but GEM does not bother. I placed the list management and rewrite code in the generic layer on the assumption that it will be needed on other hardware, but I did not add the rewrite call to anything other than the Intel resume function. Keep a list of current GATT mappings. At resume time, rewrite them into the GATT. This is needed on Intel (at least) as the entire GATT is cleared across suspend/resume. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Keith Packard Cc: Dave Jones Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- drivers/char/agp/agp.h | 3 +++ drivers/char/agp/backend.c | 2 ++ drivers/char/agp/generic.c | 28 ++++++++++++++++++++++++++++ drivers/char/agp/intel-agp.c | 5 +++++ include/linux/agp_backend.h | 5 +++++ 5 files changed, 43 insertions(+) (limited to 'include/linux') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 81e14bea54bd..4bada0e8b812 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -148,6 +148,9 @@ struct agp_bridge_data { char minor_version; struct list_head list; u32 apbase_config; + /* list of agp_memory mapped to the aperture */ + struct list_head mapped_list; + spinlock_t mapped_lock; }; #define KB(x) ((x) * 1024) diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 9626d3bda096..3a3cc03d401c 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -185,6 +185,8 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) rc = -EINVAL; goto err_out; } + INIT_LIST_HEAD(&bridge->mapped_list); + spin_lock_init(&bridge->mapped_lock); return 0; diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 54c91000646f..118dbde25dc7 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -429,6 +429,10 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start) curr->is_bound = true; curr->pg_start = pg_start; + spin_lock(&agp_bridge->mapped_lock); + list_add(&curr->mapped_list, &agp_bridge->mapped_list); + spin_unlock(&agp_bridge->mapped_lock); + return 0; } EXPORT_SYMBOL(agp_bind_memory); @@ -461,10 +465,34 @@ int agp_unbind_memory(struct agp_memory *curr) curr->is_bound = false; curr->pg_start = 0; + spin_lock(&curr->bridge->mapped_lock); + list_del(&curr->mapped_list); + spin_unlock(&curr->bridge->mapped_lock); return 0; } EXPORT_SYMBOL(agp_unbind_memory); +/** + * agp_rebind_emmory - Rewrite the entire GATT, useful on resume + */ +int agp_rebind_memory(void) +{ + struct agp_memory *curr; + int ret_val = 0; + + spin_lock(&agp_bridge->mapped_lock); + list_for_each_entry(curr, &agp_bridge->mapped_list, mapped_list) { + ret_val = curr->bridge->driver->insert_memory(curr, + curr->pg_start, + curr->type); + if (ret_val != 0) + break; + } + spin_unlock(&agp_bridge->mapped_lock); + return ret_val; +} +EXPORT_SYMBOL(agp_rebind_memory); + /* End - Routines for handling swapping of agp_memory into the GATT */ diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 57c552ee046f..016fdf0623a4 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -2244,6 +2244,7 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev) static int agp_intel_resume(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); + int ret_val; pci_restore_state(pdev); @@ -2271,6 +2272,10 @@ static int agp_intel_resume(struct pci_dev *pdev) else if (bridge->driver == &intel_i965_driver) intel_i915_configure(); + ret_val = agp_rebind_memory(); + if (ret_val != 0) + return ret_val; + return 0; } #endif diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 972b12bcfb36..2b8df8b420fd 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -30,6 +30,8 @@ #ifndef _AGP_BACKEND_H #define _AGP_BACKEND_H 1 +#include + enum chipset_type { NOT_SUPPORTED, SUPPORTED, @@ -78,6 +80,8 @@ struct agp_memory { bool is_bound; bool is_flushed; bool vmalloc_flag; + /* list of agp_memory mapped to the aperture */ + struct list_head mapped_list; }; #define AGP_NORMAL_MEMORY 0 @@ -96,6 +100,7 @@ extern struct agp_memory *agp_allocate_memory(struct agp_bridge_data *, size_t, extern int agp_copy_info(struct agp_bridge_data *, struct agp_kern_info *); extern int agp_bind_memory(struct agp_memory *, off_t); extern int agp_unbind_memory(struct agp_memory *); +extern int agp_rebind_memory(void); extern void agp_enable(struct agp_bridge_data *, u32); extern struct agp_bridge_data *agp_backend_acquire(struct pci_dev *); extern void agp_backend_release(struct agp_bridge_data *); -- cgit v1.2.3 From 987c402ac31988f7ecdb38b657bcfeea5831d479 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Mon, 11 Aug 2008 18:17:17 -0700 Subject: skbuff: Code readability NiT Inserting a space between the `-' improved the C readability (some languages allow hyphens within functions and variable names, which is confusing). Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index cfcc45b3bef0..358661c9990e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -901,7 +901,7 @@ extern unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta); static inline unsigned char *__pskb_pull(struct sk_buff *skb, unsigned int len) { if (len > skb_headlen(skb) && - !__pskb_pull_tail(skb, len-skb_headlen(skb))) + !__pskb_pull_tail(skb, len - skb_headlen(skb))) return NULL; skb->len -= len; return skb->data += len; @@ -918,7 +918,7 @@ static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len) return 1; if (unlikely(len > skb->len)) return 0; - return __pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL; + return __pskb_pull_tail(skb, len - skb_headlen(skb)) != NULL; } /** @@ -1321,7 +1321,7 @@ static inline int skb_padto(struct sk_buff *skb, unsigned int len) unsigned int size = skb->len; if (likely(size >= len)) return 0; - return skb_pad(skb, len-size); + return skb_pad(skb, len - size); } static inline int skb_add_data(struct sk_buff *skb, -- cgit v1.2.3 From 912985dce45ef18fcdd9f5439fef054e0e22302a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Aug 2008 17:52:52 -0500 Subject: mm: Make generic weak get_user_pages_fast and EXPORT_GPL it Out of line get_user_pages_fast fallback implementation, make it a weak symbol, get rid of CONFIG_HAVE_GET_USER_PAGES_FAST. Export the symbol to modules so lguest can use it. Signed-off-by: Nick Piggin Signed-off-by: Rusty Russell --- arch/powerpc/Kconfig | 3 --- arch/x86/Kconfig | 1 - arch/x86/mm/Makefile | 3 +-- include/linux/mm.h | 20 -------------------- mm/Kconfig | 3 --- mm/util.c | 15 +++++++++++++++ 6 files changed, 16 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 63c9cafda9c4..587da5e0990f 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -42,9 +42,6 @@ config GENERIC_HARDIRQS bool default y -config HAVE_GET_USER_PAGES_FAST - def_bool PPC64 - config HAVE_SETUP_PER_CPU_AREA def_bool PPC64 diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 3d0f2b6a5a16..ac2fb0641a04 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -22,7 +22,6 @@ config X86 select HAVE_IDE select HAVE_OPROFILE select HAVE_IOREMAP_PROT - select HAVE_GET_USER_PAGES_FAST select HAVE_KPROBES select ARCH_WANT_OPTIONAL_GPIOLIB select HAVE_KRETPROBES diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 2977ea37791f..dfb932dcf136 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -1,7 +1,6 @@ obj-y := init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \ - pat.o pgtable.o + pat.o pgtable.o gup.o -obj-$(CONFIG_HAVE_GET_USER_PAGES_FAST) += gup.o obj-$(CONFIG_X86_32) += pgtable_32.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o diff --git a/include/linux/mm.h b/include/linux/mm.h index 335288bff1b7..fa651609b65d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -834,7 +834,6 @@ extern int mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, unsigned long start, unsigned long end, unsigned long newflags); -#ifdef CONFIG_HAVE_GET_USER_PAGES_FAST /* * get_user_pages_fast provides equivalent functionality to get_user_pages, * operating on current and current->mm (force=0 and doesn't return any vmas). @@ -848,25 +847,6 @@ extern int mprotect_fixup(struct vm_area_struct *vma, int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); -#else -/* - * Should probably be moved to asm-generic, and architectures can include it if - * they don't implement their own get_user_pages_fast. - */ -#define get_user_pages_fast(start, nr_pages, write, pages) \ -({ \ - struct mm_struct *mm = current->mm; \ - int ret; \ - \ - down_read(&mm->mmap_sem); \ - ret = get_user_pages(current, mm, start, nr_pages, \ - write, 0, pages, NULL); \ - up_read(&mm->mmap_sem); \ - \ - ret; \ -}) -#endif - /* * A callback you can register to apply pressure to ageable caches. * diff --git a/mm/Kconfig b/mm/Kconfig index 446c6588c753..0bd9c2dbb2a0 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -77,9 +77,6 @@ config FLAT_NODE_MEM_MAP def_bool y depends on !SPARSEMEM -config HAVE_GET_USER_PAGES_FAST - bool - # # Both the NUMA code and DISCONTIGMEM use arrays of pg_data_t's # to represent different areas of memory. This variable allows diff --git a/mm/util.c b/mm/util.c index 9341ca77bd88..cb00b748ce47 100644 --- a/mm/util.c +++ b/mm/util.c @@ -171,3 +171,18 @@ void arch_pick_mmap_layout(struct mm_struct *mm) mm->unmap_area = arch_unmap_area; } #endif + +int __attribute__((weak)) get_user_pages_fast(unsigned long start, + int nr_pages, int write, struct page **pages) +{ + struct mm_struct *mm = current->mm; + int ret; + + down_read(&mm->mmap_sem); + ret = get_user_pages(current, mm, start, nr_pages, + write, 0, pages, NULL); + up_read(&mm->mmap_sem); + + return ret; +} +EXPORT_SYMBOL_GPL(get_user_pages_fast); -- cgit v1.2.3 From 4bceba417a795b78a5146e3f85291cb7bb2402ef Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 8 Aug 2008 11:15:07 +0200 Subject: export virtio_rng.h Hello Rusty, The entropy device was added after we exported all virtio headers. This patch adds virtio_rng.h to the exportable userspace headers. Signed-off-by: Christian Borntraeger Signed-off-by: Rusty Russell --- include/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index a26f565e8189..327f60658d94 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -356,6 +356,7 @@ unifdef-y += virtio_balloon.h unifdef-y += virtio_console.h unifdef-y += virtio_pci.h unifdef-y += virtio_ring.h +unifdef-y += virtio_rng.h unifdef-y += vt.h unifdef-y += wait.h unifdef-y += wanrouter.h -- cgit v1.2.3 From 59f9415ffb9759e950d775f4c400f747b332cc02 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Wed, 30 Jul 2008 12:49:02 -0700 Subject: modules: extend initcall_debug functionality to the module loader The kernel has this really nice facility where if you put "initcall_debug" on the kernel commandline, it'll print which function it's going to execute just before calling an initcall, and then after the call completes it will 1) print if it had an error code 2) checks for a few simple bugs (like leaving irqs off) and 3) print how long the init call took in milliseconds. While trying to optimize the boot speed of my laptop, I have been loving number 3 to figure out what to optimize... ... and then I wished that the same thing was done for module loading. This patch makes the module loader use this exact same functionality; it's a logical extension in my view (since modules are just sort of late binding initcalls anyway) and so far I've found it quite useful in finding where things are too slow in my boot. Signed-off-by: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Rusty Russell --- include/linux/init.h | 1 + init/main.c | 6 ++++-- kernel/module.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/init.h b/include/linux/init.h index 11b84e106053..93538b696e3d 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -139,6 +139,7 @@ extern initcall_t __con_initcall_start[], __con_initcall_end[]; extern initcall_t __security_initcall_start[], __security_initcall_end[]; /* Defined in init/main.c */ +extern int do_one_initcall(initcall_t fn); extern char __initdata boot_command_line[]; extern char *saved_command_line; extern unsigned int reset_devices; diff --git a/init/main.c b/init/main.c index 0bc7e167bf45..f6f7042331dc 100644 --- a/init/main.c +++ b/init/main.c @@ -691,7 +691,7 @@ asmlinkage void __init start_kernel(void) rest_init(); } -static int __initdata initcall_debug; +static int initcall_debug; static int __init initcall_debug_setup(char *str) { @@ -700,7 +700,7 @@ static int __init initcall_debug_setup(char *str) } __setup("initcall_debug", initcall_debug_setup); -static void __init do_one_initcall(initcall_t fn) +int do_one_initcall(initcall_t fn) { int count = preempt_count(); ktime_t t0, t1, delta; @@ -740,6 +740,8 @@ static void __init do_one_initcall(initcall_t fn) print_fn_descriptor_symbol(KERN_WARNING "initcall %s", fn); printk(" returned with %s\n", msgbuf); } + + return result; } diff --git a/kernel/module.c b/kernel/module.c index 61d212120df4..08864d257eb0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2288,7 +2288,7 @@ sys_init_module(void __user *umod, /* Start the module */ if (mod->init != NULL) - ret = mod->init(); + ret = do_one_initcall(mod->init); if (ret < 0) { /* Init routine failed: abort. Try to protect us from buggy refcounters. */ -- cgit v1.2.3 From 74768ed833344bb0f82b97cee46320a3d7f09ecd Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 12 Aug 2008 15:08:39 -0700 Subject: page allocator: use no-panic variant of alloc_bootmem() in alloc_large_system_hash() .. since a failed allocation is being (initially) handled gracefully, and panic()-ed upon failure explicitly in the function if retries with smaller sizes failed. Signed-off-by: Jan Beulich Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 4 ++++ mm/page_alloc.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 652470b687c9..95837bfb5256 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -97,10 +97,14 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE #define alloc_bootmem(x) \ __alloc_bootmem(x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_nopanic(x) \ + __alloc_bootmem_nopanic(x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low(x) \ __alloc_bootmem_low(x, SMP_CACHE_BYTES, 0) #define alloc_bootmem_pages(x) \ __alloc_bootmem(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_pages_nopanic(x) \ + __alloc_bootmem_nopanic(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages(x) \ __alloc_bootmem_low(x, PAGE_SIZE, 0) #define alloc_bootmem_node(pgdat, x) \ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 401d104d2bb6..af982f7cdb2a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4437,7 +4437,7 @@ void *__init alloc_large_system_hash(const char *tablename, do { size = bucketsize << log2qty; if (flags & HASH_EARLY) - table = alloc_bootmem(size); + table = alloc_bootmem_nopanic(size); else if (hashdist) table = __vmalloc(size, GFP_ATOMIC, PAGE_KERNEL); else { -- cgit v1.2.3 From 10546355323e4826d13e62f85ac6198385a817a9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 12 Aug 2008 15:08:55 -0700 Subject: matrox maven: convert to a new-style i2c driver The legacy i2c model is going away soon, so switch to the new model. Signed-off-by: Jean Delvare Acked-by: Krzysztof Helt Cc: Petr Vandrovec Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/matrox/i2c-matroxfb.c | 12 ++++- drivers/video/matrox/matroxfb_maven.c | 95 ++++++++++++++--------------------- include/linux/i2c-id.h | 2 - 3 files changed, 50 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c index 9478439b73d6..c14e3e2212b3 100644 --- a/drivers/video/matrox/i2c-matroxfb.c +++ b/drivers/video/matrox/i2c-matroxfb.c @@ -107,7 +107,6 @@ static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, b->mask.data = data; b->mask.clock = clock; b->adapter.owner = THIS_MODULE; - b->adapter.id = I2C_HW_B_G400; snprintf(b->adapter.name, sizeof(b->adapter.name), name, minfo->fbcon.node); i2c_set_adapdata(&b->adapter, b); @@ -182,6 +181,17 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); if (err) printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); + else { + struct i2c_board_info maven_info = { + I2C_BOARD_INFO("maven", 0x1b), + }; + unsigned short const addr_list[2] = { + 0x1b, I2C_CLIENT_END + }; + + i2c_new_probed_device(&m2info->maven.adapter, + &maven_info, addr_list); + } } return m2info; fail_ddc1:; diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 2ad06b0125c3..042408a8c631 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -19,8 +19,6 @@ #include #include -#define MAVEN_I2CID (0x1B) - #define MGATVO_B 1 #define MGATVO_C 2 @@ -128,7 +126,7 @@ static int get_ctrl_id(__u32 v4l2_id) { struct maven_data { struct matrox_fb_info* primary_head; - struct i2c_client client; + struct i2c_client *client; int version; }; @@ -974,7 +972,7 @@ static inline int maven_compute_timming(struct maven_data* md, static int maven_program_timming(struct maven_data* md, const struct mavenregs* m) { - struct i2c_client* c = &md->client; + struct i2c_client *c = md->client; if (m->mode == MATROXFB_OUTPUT_MODE_MONITOR) { LR(0x80); @@ -1011,7 +1009,7 @@ static int maven_program_timming(struct maven_data* md, } static inline int maven_resync(struct maven_data* md) { - struct i2c_client* c = &md->client; + struct i2c_client *c = md->client; maven_set_reg(c, 0x95, 0x20); /* start whole thing */ return 0; } @@ -1069,48 +1067,48 @@ static int maven_set_control (struct maven_data* md, maven_compute_bwlevel(md, &blacklevel, &whitelevel); blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8); whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8); - maven_set_reg_pair(&md->client, 0x0e, blacklevel); - maven_set_reg_pair(&md->client, 0x1e, whitelevel); + maven_set_reg_pair(md->client, 0x0e, blacklevel); + maven_set_reg_pair(md->client, 0x1e, whitelevel); } break; case V4L2_CID_SATURATION: { - maven_set_reg(&md->client, 0x20, p->value); - maven_set_reg(&md->client, 0x22, p->value); + maven_set_reg(md->client, 0x20, p->value); + maven_set_reg(md->client, 0x22, p->value); } break; case V4L2_CID_HUE: { - maven_set_reg(&md->client, 0x25, p->value); + maven_set_reg(md->client, 0x25, p->value); } break; case V4L2_CID_GAMMA: { const struct maven_gamma* g; g = maven_compute_gamma(md); - maven_set_reg(&md->client, 0x83, g->reg83); - maven_set_reg(&md->client, 0x84, g->reg84); - maven_set_reg(&md->client, 0x85, g->reg85); - maven_set_reg(&md->client, 0x86, g->reg86); - maven_set_reg(&md->client, 0x87, g->reg87); - maven_set_reg(&md->client, 0x88, g->reg88); - maven_set_reg(&md->client, 0x89, g->reg89); - maven_set_reg(&md->client, 0x8a, g->reg8a); - maven_set_reg(&md->client, 0x8b, g->reg8b); + maven_set_reg(md->client, 0x83, g->reg83); + maven_set_reg(md->client, 0x84, g->reg84); + maven_set_reg(md->client, 0x85, g->reg85); + maven_set_reg(md->client, 0x86, g->reg86); + maven_set_reg(md->client, 0x87, g->reg87); + maven_set_reg(md->client, 0x88, g->reg88); + maven_set_reg(md->client, 0x89, g->reg89); + maven_set_reg(md->client, 0x8a, g->reg8a); + maven_set_reg(md->client, 0x8b, g->reg8b); } break; case MATROXFB_CID_TESTOUT: { unsigned char val - = maven_get_reg(&md->client,0x8d); + = maven_get_reg(md->client, 0x8d); if (p->value) val |= 0x10; else val &= ~0x10; - maven_set_reg(&md->client, 0x8d, val); + maven_set_reg(md->client, 0x8d, val); } break; case MATROXFB_CID_DEFLICKER: { - maven_set_reg(&md->client, 0x93, maven_compute_deflicker(md)); + maven_set_reg(md->client, 0x93, maven_compute_deflicker(md)); } break; } @@ -1189,6 +1187,7 @@ static int maven_init_client(struct i2c_client* clnt) { MINFO_FROM(container_of(clnt->adapter, struct i2c_bit_adapter, adapter)->minfo); md->primary_head = MINFO; + md->client = clnt; down_write(&ACCESS_FBINFO(altout.lock)); ACCESS_FBINFO(outputs[1]).output = &maven_altout; ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src; @@ -1232,14 +1231,11 @@ static int maven_shutdown_client(struct i2c_client* clnt) { return 0; } -static const unsigned short normal_i2c[] = { MAVEN_I2CID, I2C_CLIENT_END }; -I2C_CLIENT_INSMOD; - -static struct i2c_driver maven_driver; - -static int maven_detect_client(struct i2c_adapter* adapter, int address, int kind) { - int err = 0; - struct i2c_client* new_client; +static int maven_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + int err = -ENODEV; struct maven_data* data; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA | @@ -1250,50 +1246,37 @@ static int maven_detect_client(struct i2c_adapter* adapter, int address, int kin err = -ENOMEM; goto ERROR0; } - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &maven_driver; - new_client->flags = 0; - strlcpy(new_client->name, "maven", I2C_NAME_SIZE); - if ((err = i2c_attach_client(new_client))) - goto ERROR3; - err = maven_init_client(new_client); + i2c_set_clientdata(client, data); + err = maven_init_client(client); if (err) goto ERROR4; return 0; ERROR4:; - i2c_detach_client(new_client); -ERROR3:; kfree(data); ERROR0:; return err; } -static int maven_attach_adapter(struct i2c_adapter* adapter) { - if (adapter->id == I2C_HW_B_G400) - return i2c_probe(adapter, &addr_data, &maven_detect_client); - return 0; -} - -static int maven_detach_client(struct i2c_client* client) { - int err; - - if ((err = i2c_detach_client(client))) - return err; +static int maven_remove(struct i2c_client *client) +{ maven_shutdown_client(client); kfree(i2c_get_clientdata(client)); return 0; } +static const struct i2c_device_id maven_id[] = { + { "maven", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, maven_id); + static struct i2c_driver maven_driver={ .driver = { .name = "maven", }, - .id = I2C_DRIVERID_MGATVO, - .attach_adapter = maven_attach_adapter, - .detach_client = maven_detach_client, + .probe = maven_probe, + .remove = maven_remove, + .id_table = maven_id, }; static int __init matroxfb_maven_init(void) diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 4862398e05bf..bf34c5f4c051 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -39,7 +39,6 @@ #define I2C_DRIVERID_SAA7111A 8 /* video input processor */ #define I2C_DRIVERID_SAA7185B 13 /* video encoder */ #define I2C_DRIVERID_SAA7110 22 /* video decoder */ -#define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */ #define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */ #define I2C_DRIVERID_PCF8583 25 /* real time clock */ #define I2C_DRIVERID_SAB3036 26 /* SAB3036 tuner */ @@ -95,7 +94,6 @@ #define I2C_HW_B_BT848 0x010005 /* BT848 video boards */ #define I2C_HW_B_VIA 0x010007 /* Via vt82c586b */ #define I2C_HW_B_HYDRA 0x010008 /* Apple Hydra Mac I/O */ -#define I2C_HW_B_G400 0x010009 /* Matrox G400 */ #define I2C_HW_B_I810 0x01000a /* Intel I810 */ #define I2C_HW_B_VOO 0x01000b /* 3dfx Voodoo 3 / Banshee */ #define I2C_HW_B_SCX200 0x01000e /* Nat'l Semi SCx200 I2C */ -- cgit v1.2.3 From 070cb06593006e7d565d4763380f3edd8dbdc134 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Aug 2008 15:08:59 -0700 Subject: move kernel-doc comment for might_sleep directly before its defining block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Ingo Molnar Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index aaa998f65c7a..2651f805ba6d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -108,6 +108,13 @@ struct completion; struct pt_regs; struct user; +#ifdef CONFIG_PREEMPT_VOLUNTARY +extern int _cond_resched(void); +# define might_resched() _cond_resched() +#else +# define might_resched() do { } while (0) +#endif + /** * might_sleep - annotation for functions that can sleep * @@ -118,13 +125,6 @@ struct user; * be bitten later when the calling function happens to sleep when it is not * supposed to. */ -#ifdef CONFIG_PREEMPT_VOLUNTARY -extern int _cond_resched(void); -# define might_resched() _cond_resched() -#else -# define might_resched() do { } while (0) -#endif - #ifdef CONFIG_DEBUG_SPINLOCK_SLEEP void __might_sleep(char *file, int line); # define might_sleep() \ -- cgit v1.2.3 From 50ac2d694f2dd1658341cf97bcf2ffb836d772cb Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 12 Aug 2008 15:09:02 -0700 Subject: seq_file: add seq_cpumask(), seq_nodemask() Short enough reads from /proc/irq/*/smp_affinity return -EINVAL for no good reason. This became noticed with NR_CPUS=4096 patches, when length of printed representation of cpumask becase 1152, but cat(1) continued to read with 1024-byte chunks. bitmap_scnprintf() in good faith fills buffer, returns 1023, check returns -EINVAL. Fix it by switching to seq_file, so handler will just fill buffer and doesn't care about offsets, length, filling EOF and all this crap. For that add seq_bitmap(), and wrappers around it -- seq_cpumask() and seq_nodemask(). Signed-off-by: Alexey Dobriyan Reviewed-by: Paul Jackson Cc: Mike Travis Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/seq_file.c | 14 ++++++++++++++ include/linux/bitmap.h | 1 + include/linux/seq_file.h | 12 ++++++++++++ lib/bitmap.c | 11 +++++++++++ 4 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/fs/seq_file.c b/fs/seq_file.c index 3f54dbd6c49b..5d54205e486b 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -443,6 +443,20 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc) return -1; } +int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits) +{ + size_t len = bitmap_scnprintf_len(nr_bits); + + if (m->count + len < m->size) { + bitmap_scnprintf(m->buf + m->count, m->size - m->count, + bits, nr_bits); + m->count += len; + return 0; + } + m->count = m->size; + return -1; +} + static void *single_start(struct seq_file *p, loff_t *pos) { return NULL + (*pos == 0); diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 1abfe664c444..89781fd48859 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -110,6 +110,7 @@ extern int __bitmap_weight(const unsigned long *bitmap, int bits); extern int bitmap_scnprintf(char *buf, unsigned int len, const unsigned long *src, int nbits); +extern int bitmap_scnprintf_len(unsigned int nr_bits); extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user, unsigned long *dst, int nbits); extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen, diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index a66304a09955..a1783b229ef4 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include struct seq_operations; struct file; @@ -47,6 +49,16 @@ int seq_path(struct seq_file *, struct path *, char *); int seq_dentry(struct seq_file *, struct dentry *, char *); int seq_path_root(struct seq_file *m, struct path *path, struct path *root, char *esc); +int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits); +static inline int seq_cpumask(struct seq_file *m, cpumask_t *mask) +{ + return seq_bitmap(m, mask->bits, NR_CPUS); +} + +static inline int seq_nodemask(struct seq_file *m, nodemask_t *mask) +{ + return seq_bitmap(m, mask->bits, MAX_NUMNODES); +} int single_open(struct file *, int (*)(struct seq_file *, void *), void *); int single_release(struct inode *, struct file *); diff --git a/lib/bitmap.c b/lib/bitmap.c index 482df94ea21e..06fb57c86de0 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -315,6 +315,17 @@ int bitmap_scnprintf(char *buf, unsigned int buflen, } EXPORT_SYMBOL(bitmap_scnprintf); +/** + * bitmap_scnprintf_len - return buffer length needed to convert + * bitmap to an ASCII hex string + * @nr_bits: number of bits to be converted + */ +int bitmap_scnprintf_len(unsigned int nr_bits) +{ + unsigned int nr_nibbles = ALIGN(nr_bits, 4) / 4; + return nr_nibbles + ALIGN(nr_nibbles, CHUNKSZ / 4) / (CHUNKSZ / 4) - 1; +} + /** * __bitmap_parse - convert an ASCII hex string into a bitmap. * @buf: pointer to buffer containing string. -- cgit v1.2.3 From 40c9f22210f2d22f45d4fb430c94f472d19407d6 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 12 Aug 2008 15:09:04 -0700 Subject: byteorder: add a new include/linux/swab.h to define byteswapping functions Collect the implementations from include/linux/byteorder/swab.h, swabb.h in swab.h The functionality provided covers: u16 swab16(u16 val) - return a byteswapped 16 bit value u32 swab32(u32 val) - return a byteswapped 32 bit value u64 swab64(u64 val) - return a byteswapped 64 bit value u32 swahw32(u32 val) - return a wordswapped 32 bit value u32 swahb32(u32 val) - return a high/low byteswapped 32 bit value Similar to above, but return swapped value from a naturally-aligned pointer u16 swab16p(u16 *p) u32 swab32p(u32 *p) u64 swab64p(u64 *p) u32 swahw32p(u32 *p) u32 swahb32p(u32 *p) Similar to above, but swap the value in-place (in-situ) void swab16s(u16 *p) void swab32s(u32 *p) void swab64s(u64 *p) void swahw32s(u32 *p) void swahb32s(u32 *p) Arches can override any of these with an optimized version by defining an inline in their asm/byteorder.h (example given for swab16()): u16 __arch_swab16() {} #define __arch_swab16 __arch_swab16 Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swab.h | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 include/linux/swab.h (limited to 'include/linux') diff --git a/include/linux/swab.h b/include/linux/swab.h new file mode 100644 index 000000000000..270d5c208a89 --- /dev/null +++ b/include/linux/swab.h @@ -0,0 +1,309 @@ +#ifndef _LINUX_SWAB_H +#define _LINUX_SWAB_H + +#include +#include +#include + +/* + * casts are necessary for constants, because we never know how for sure + * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way. + */ +#define __const_swab16(x) ((__u16)( \ + (((__u16)(x) & (__u16)0x00ffU) << 8) | \ + (((__u16)(x) & (__u16)0xff00U) >> 8))) + +#define __const_swab32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ + (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ + (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ + (((__u32)(x) & (__u32)0xff000000UL) >> 24))) + +#define __const_swab64(x) ((__u64)( \ + (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \ + (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \ + (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \ + (((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | \ + (((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | \ + (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \ + (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \ + (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56))) + +#define __const_swahw32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \ + (((__u32)(x) & (__u32)0xffff0000UL) >> 16))) + +#define __const_swahb32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \ + (((__u32)(x) & (__u32)0xff00ff00UL) >> 8))) + +/* + * Implement the following as inlines, but define the interface using + * macros to allow constant folding when possible: + * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32 + */ + +static inline __attribute_const__ __u16 ___swab16(__u16 val) +{ +#ifdef __arch_swab16 + return __arch_swab16(val); +#elif defined(__arch_swab16p) + return __arch_swab16p(&val); +#else + return __const_swab16(val); +#endif +} + +static inline __attribute_const__ __u32 ___swab32(__u32 val) +{ +#ifdef __arch_swab32 + return __arch_swab32(val); +#elif defined(__arch_swab32p) + return __arch_swab32p(&val); +#else + return __const_swab32(val); +#endif +} + +static inline __attribute_const__ __u64 ___swab64(__u64 val) +{ +#ifdef __arch_swab64 + return __arch_swab64(val); +#elif defined(__arch_swab64p) + return __arch_swab64p(&val); +#elif defined(__SWAB_64_THRU_32__) + __u32 h = val >> 32; + __u32 l = val & ((1ULL << 32) - 1); + return (((__u64)___swab32(l)) << 32) | ((__u64)(___swab32(h))); +#else + return __const_swab64(val); +#endif +} + +static inline __attribute_const__ __u32 ___swahw32(__u32 val) +{ +#ifdef __arch_swahw32 + return __arch_swahw32(val); +#elif defined(__arch_swahw32p) + return __arch_swahw32p(&val); +#else + return __const_swahw32(val); +#endif +} + +static inline __attribute_const__ __u32 ___swahb32(__u32 val) +{ +#ifdef __arch_swahb32 + return __arch_swahb32(val); +#elif defined(__arch_swahb32p) + return __arch_swahb32p(&val); +#else + return __const_swahb32(val); +#endif +} + +/** + * __swab16 - return a byteswapped 16-bit value + * @x: value to byteswap + */ +#define __swab16(x) \ + (__builtin_constant_p((__u16)(x)) ? \ + __const_swab16((x)) : \ + ___swab16((x))) + +/** + * __swab32 - return a byteswapped 32-bit value + * @x: value to byteswap + */ +#define __swab32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + __const_swab32((x)) : \ + ___swab32((x))) + +/** + * __swab64 - return a byteswapped 64-bit value + * @x: value to byteswap + */ +#define __swab64(x) \ + (__builtin_constant_p((__u64)(x)) ? \ + __const_swab64((x)) : \ + ___swab64((x))) + +/** + * __swahw32 - return a word-swapped 32-bit value + * @x: value to wordswap + * + * __swahw32(0x12340000) is 0x00001234 + */ +#define __swahw32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + __const_swahw32((x)) : \ + ___swahw32((x))) + +/** + * __swahb32 - return a high and low byte-swapped 32-bit value + * @x: value to byteswap + * + * __swahb32(0x12345678) is 0x34127856 + */ +#define __swahb32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + __const_swahb32((x)) : \ + ___swahb32((x))) + +/** + * __swab16p - return a byteswapped 16-bit value from a pointer + * @p: pointer to a naturally-aligned 16-bit value + */ +static inline __u16 __swab16p(const __u16 *p) +{ +#ifdef __arch_swab16p + return __arch_swab16p(p); +#else + return __swab16(*p); +#endif +} + +/** + * __swab32p - return a byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + */ +static inline __u32 __swab32p(const __u32 *p) +{ +#ifdef __arch_swab32p + return __arch_swab32p(p); +#else + return __swab32(*p); +#endif +} + +/** + * __swab64p - return a byteswapped 64-bit value from a pointer + * @p: pointer to a naturally-aligned 64-bit value + */ +static inline __u64 __swab64p(const __u64 *p) +{ +#ifdef __arch_swab64p + return __arch_swab64p(p); +#else + return __swab64(*p); +#endif +} + +/** + * __swahw32p - return a wordswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping. + */ +static inline __u32 __swahw32p(const __u32 *p) +{ +#ifdef __arch_swahw32p + return __arch_swahw32p(p); +#else + return __swahw32(*p); +#endif +} + +/** + * __swahb32p - return a high and low byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high/low byteswapping. + */ +static inline __u32 __swahb32p(const __u32 *p) +{ +#ifdef __arch_swahb32p + return __arch_swahb32p(p); +#else + return __swahb32(*p); +#endif +} + +/** + * __swab16s - byteswap a 16-bit value in-place + * @p: pointer to a naturally-aligned 16-bit value + */ +static inline void __swab16s(__u16 *p) +{ +#ifdef __arch_swab16s + __arch_swab16s(p); +#else + *p = __swab16p(p); +#endif +} +/** + * __swab32s - byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + */ +static inline void __swab32s(__u32 *p) +{ +#ifdef __arch_swab32s + __arch_swab32s(p); +#else + *p = __swab32p(p); +#endif +} + +/** + * __swab64s - byteswap a 64-bit value in-place + * @p: pointer to a naturally-aligned 64-bit value + */ +static inline void __swab64s(__u64 *p) +{ +#ifdef __arch_swab64s + __arch_swab64s(p); +#else + *p = __swab64p(p); +#endif +} + +/** + * __swahw32s - wordswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping + */ +static inline void __swahw32s(__u32 *p) +{ +#ifdef __arch_swahw32s + __arch_swahw32s(p); +#else + *p = __swahw32p(p); +#endif +} + +/** + * __swahb32s - high and low byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high and low byte swapping + */ +static inline void __swahb32s(__u32 *p) +{ +#ifdef __arch_swahb32s + __arch_swahb32s(p); +#else + *p = __swahb32p(p); +#endif +} + +#ifdef __KERNEL__ +# define swab16 __swab16 +# define swab32 __swab32 +# define swab64 __swab64 +# define swahw32 __swahw32 +# define swahb32 __swahb32 +# define swab16p __swab16p +# define swab32p __swab32p +# define swab64p __swab64p +# define swahw32p __swahw32p +# define swahb32p __swahb32p +# define swab16s __swab16s +# define swab32s __swab32s +# define swab64s __swab64s +# define swahw32s __swahw32s +# define swahb32s __swahb32s +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SWAB_H */ -- cgit v1.2.3 From bc2aa80e18a1b43ea2b8066500006b729c4ba4a7 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 12 Aug 2008 15:09:05 -0700 Subject: byteorder: add include/linux/byteorder.h to define endian helpers Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/byteorder.h | 372 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 include/linux/byteorder.h (limited to 'include/linux') diff --git a/include/linux/byteorder.h b/include/linux/byteorder.h new file mode 100644 index 000000000000..29f002d73d98 --- /dev/null +++ b/include/linux/byteorder.h @@ -0,0 +1,372 @@ +#ifndef _LINUX_BYTEORDER_H +#define _LINUX_BYTEORDER_H + +#include +#include + +#if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) +# error Fix asm/byteorder.h to define one endianness +#endif + +#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) +# error Fix asm/byteorder.h to define arch endianness +#endif + +#ifdef __LITTLE_ENDIAN +# undef __LITTLE_ENDIAN +# define __LITTLE_ENDIAN 1234 +#endif + +#ifdef __BIG_ENDIAN +# undef __BIG_ENDIAN +# define __BIG_ENDIAN 4321 +#endif + +#if defined(__LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN_BITFIELD) +# define __LITTLE_ENDIAN_BITFIELD +#endif + +#if defined(__BIG_ENDIAN) && !defined(__BIG_ENDIAN_BITFIELD) +# define __BIG_ENDIAN_BITFIELD +#endif + +#ifdef __LITTLE_ENDIAN +# define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) +# define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) +# define __le64_to_cpu(x) ((__force __u64)(__le64)(x)) +# define __cpu_to_le16(x) ((__force __le16)(__u16)(x)) +# define __cpu_to_le32(x) ((__force __le32)(__u32)(x)) +# define __cpu_to_le64(x) ((__force __le64)(__u64)(x)) + +# define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x)) +# define __be32_to_cpu(x) __swab32((__force __u32)(__be32)(x)) +# define __be64_to_cpu(x) __swab64((__force __u64)(__be64)(x)) +# define __cpu_to_be16(x) ((__force __be16)__swab16(x)) +# define __cpu_to_be32(x) ((__force __be32)__swab32(x)) +# define __cpu_to_be64(x) ((__force __be64)__swab64(x)) +#endif + +#ifdef __BIG_ENDIAN +# define __be16_to_cpu(x) ((__force __u16)(__be16)(x)) +# define __be32_to_cpu(x) ((__force __u32)(__be32)(x)) +# define __be64_to_cpu(x) ((__force __u64)(__be64)(x)) +# define __cpu_to_be16(x) ((__force __be16)(__u16)(x)) +# define __cpu_to_be32(x) ((__force __be32)(__u32)(x)) +# define __cpu_to_be64(x) ((__force __be64)(__u64)(x)) + +# define __le16_to_cpu(x) __swab16((__force __u16)(__le16)(x)) +# define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) +# define __le64_to_cpu(x) __swab64((__force __u64)(__le64)(x)) +# define __cpu_to_le16(x) ((__force __le16)__swab16(x)) +# define __cpu_to_le32(x) ((__force __le32)__swab32(x)) +# define __cpu_to_le64(x) ((__force __le64)__swab64(x)) +#endif + +/* + * These helpers could be phased out over time as the base version + * handles constant folding. + */ +#define __constant_htonl(x) __cpu_to_be32(x) +#define __constant_ntohl(x) __be32_to_cpu(x) +#define __constant_htons(x) __cpu_to_be16(x) +#define __constant_ntohs(x) __be16_to_cpu(x) + +#define __constant_le16_to_cpu(x) __le16_to_cpu(x) +#define __constant_le32_to_cpu(x) __le32_to_cpu(x) +#define __constant_le64_to_cpu(x) __le64_to_cpu(x) +#define __constant_be16_to_cpu(x) __be16_to_cpu(x) +#define __constant_be32_to_cpu(x) __be32_to_cpu(x) +#define __constant_be64_to_cpu(x) __be64_to_cpu(x) + +#define __constant_cpu_to_le16(x) __cpu_to_le16(x) +#define __constant_cpu_to_le32(x) __cpu_to_le32(x) +#define __constant_cpu_to_le64(x) __cpu_to_le64(x) +#define __constant_cpu_to_be16(x) __cpu_to_be16(x) +#define __constant_cpu_to_be32(x) __cpu_to_be32(x) +#define __constant_cpu_to_be64(x) __cpu_to_be64(x) + +static inline void __le16_to_cpus(__u16 *p) +{ +#ifdef __BIG_ENDIAN + __swab16s(p); +#endif +} + +static inline void __cpu_to_le16s(__u16 *p) +{ +#ifdef __BIG_ENDIAN + __swab16s(p); +#endif +} + +static inline void __le32_to_cpus(__u32 *p) +{ +#ifdef __BIG_ENDIAN + __swab32s(p); +#endif +} + +static inline void __cpu_to_le32s(__u32 *p) +{ +#ifdef __BIG_ENDIAN + __swab32s(p); +#endif +} + +static inline void __le64_to_cpus(__u64 *p) +{ +#ifdef __BIG_ENDIAN + __swab64s(p); +#endif +} + +static inline void __cpu_to_le64s(__u64 *p) +{ +#ifdef __BIG_ENDIAN + __swab64s(p); +#endif +} + +static inline void __be16_to_cpus(__u16 *p) +{ +#ifdef __LITTLE_ENDIAN + __swab16s(p); +#endif +} + +static inline void __cpu_to_be16s(__u16 *p) +{ +#ifdef __LITTLE_ENDIAN + __swab16s(p); +#endif +} + +static inline void __be32_to_cpus(__u32 *p) +{ +#ifdef __LITTLE_ENDIAN + __swab32s(p); +#endif +} + +static inline void __cpu_to_be32s(__u32 *p) +{ +#ifdef __LITTLE_ENDIAN + __swab32s(p); +#endif +} + +static inline void __be64_to_cpus(__u64 *p) +{ +#ifdef __LITTLE_ENDIAN + __swab64s(p); +#endif +} + +static inline void __cpu_to_be64s(__u64 *p) +{ +#ifdef __LITTLE_ENDIAN + __swab64s(p); +#endif +} + +static inline __u16 __le16_to_cpup(const __le16 *p) +{ +#ifdef __LITTLE_ENDIAN + return (__force __u16)*p; +#else + return __swab16p((__force __u16 *)p); +#endif +} + +static inline __u32 __le32_to_cpup(const __le32 *p) +{ +#ifdef __LITTLE_ENDIAN + return (__force __u32)*p; +#else + return __swab32p((__force __u32 *)p); +#endif +} + +static inline __u64 __le64_to_cpup(const __le64 *p) +{ +#ifdef __LITTLE_ENDIAN + return (__force __u64)*p; +#else + return __swab64p((__force __u64 *)p); +#endif +} + +static inline __le16 __cpu_to_le16p(const __u16 *p) +{ +#ifdef __LITTLE_ENDIAN + return (__force __le16)*p; +#else + return (__force __le16)__swab16p(p); +#endif +} + +static inline __le32 __cpu_to_le32p(const __u32 *p) +{ +#ifdef __LITTLE_ENDIAN + return (__force __le32)*p; +#else + return (__force __le32)__swab32p(p); +#endif +} + +static inline __le64 __cpu_to_le64p(const __u64 *p) +{ +#ifdef __LITTLE_ENDIAN + return (__force __le64)*p; +#else + return (__force __le64)__swab64p(p); +#endif +} + +static inline __u16 __be16_to_cpup(const __be16 *p) +{ +#ifdef __BIG_ENDIAN + return (__force __u16)*p; +#else + return __swab16p((__force __u16 *)p); +#endif +} + +static inline __u32 __be32_to_cpup(const __be32 *p) +{ +#ifdef __BIG_ENDIAN + return (__force __u32)*p; +#else + return __swab32p((__force __u32 *)p); +#endif +} + +static inline __u64 __be64_to_cpup(const __be64 *p) +{ +#ifdef __BIG_ENDIAN + return (__force __u64)*p; +#else + return __swab64p((__force __u64 *)p); +#endif +} + +static inline __be16 __cpu_to_be16p(const __u16 *p) +{ +#ifdef __BIG_ENDIAN + return (__force __be16)*p; +#else + return (__force __be16)__swab16p(p); +#endif +} + +static inline __be32 __cpu_to_be32p(const __u32 *p) +{ +#ifdef __BIG_ENDIAN + return (__force __be32)*p; +#else + return (__force __be32)__swab32p(p); +#endif +} + +static inline __be64 __cpu_to_be64p(const __u64 *p) +{ +#ifdef __BIG_ENDIAN + return (__force __be64)*p; +#else + return (__force __be64)__swab64p(p); +#endif +} + +#ifdef __KERNEL__ + +# define le16_to_cpu __le16_to_cpu +# define le32_to_cpu __le32_to_cpu +# define le64_to_cpu __le64_to_cpu +# define be16_to_cpu __be16_to_cpu +# define be32_to_cpu __be32_to_cpu +# define be64_to_cpu __be64_to_cpu +# define cpu_to_le16 __cpu_to_le16 +# define cpu_to_le32 __cpu_to_le32 +# define cpu_to_le64 __cpu_to_le64 +# define cpu_to_be16 __cpu_to_be16 +# define cpu_to_be32 __cpu_to_be32 +# define cpu_to_be64 __cpu_to_be64 + +# define le16_to_cpup __le16_to_cpup +# define le32_to_cpup __le32_to_cpup +# define le64_to_cpup __le64_to_cpup +# define be16_to_cpup __be16_to_cpup +# define be32_to_cpup __be32_to_cpup +# define be64_to_cpup __be64_to_cpup +# define cpu_to_le16p __cpu_to_le16p +# define cpu_to_le32p __cpu_to_le32p +# define cpu_to_le64p __cpu_to_le64p +# define cpu_to_be16p __cpu_to_be16p +# define cpu_to_be32p __cpu_to_be32p +# define cpu_to_be64p __cpu_to_be64p + +# define le16_to_cpus __le16_to_cpus +# define le32_to_cpus __le32_to_cpus +# define le64_to_cpus __le64_to_cpus +# define be16_to_cpus __be16_to_cpus +# define be32_to_cpus __be32_to_cpus +# define be64_to_cpus __be64_to_cpus +# define cpu_to_le16s __cpu_to_le16s +# define cpu_to_le32s __cpu_to_le32s +# define cpu_to_le64s __cpu_to_le64s +# define cpu_to_be16s __cpu_to_be16s +# define cpu_to_be32s __cpu_to_be32s +# define cpu_to_be64s __cpu_to_be64s + +/* + * They have to be macros in order to do the constant folding + * correctly - if the argument passed into a inline function + * it is no longer constant according to gcc.. + */ +# undef ntohl +# undef ntohs +# undef htonl +# undef htons + +# define ___htonl(x) __cpu_to_be32(x) +# define ___htons(x) __cpu_to_be16(x) +# define ___ntohl(x) __be32_to_cpu(x) +# define ___ntohs(x) __be16_to_cpu(x) + +# define htonl(x) ___htonl(x) +# define ntohl(x) ___ntohl(x) +# define htons(x) ___htons(x) +# define ntohs(x) ___ntohs(x) + +static inline void le16_add_cpu(__le16 *var, u16 val) +{ + *var = cpu_to_le16(le16_to_cpup(var) + val); +} + +static inline void le32_add_cpu(__le32 *var, u32 val) +{ + *var = cpu_to_le32(le32_to_cpup(var) + val); +} + +static inline void le64_add_cpu(__le64 *var, u64 val) +{ + *var = cpu_to_le64(le64_to_cpup(var) + val); +} + +static inline void be16_add_cpu(__be16 *var, u16 val) +{ + *var = cpu_to_be16(be16_to_cpup(var) + val); +} + +static inline void be32_add_cpu(__be32 *var, u32 val) +{ + *var = cpu_to_be32(be32_to_cpup(var) + val); +} + +static inline void be64_add_cpu(__be64 *var, u64 val) +{ + *var = cpu_to_be64(be64_to_cpup(var) + val); +} + +#endif /* __KERNEL__ */ +#endif /* _LINUX_BYTEORDER_H */ -- cgit v1.2.3 From 31bad9246b5e17d547430697791acca5e9712333 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Tue, 12 Aug 2008 15:09:14 -0700 Subject: firmware/memmap: cleanup Various cleanup the drivers/firmware/memmap (after review by AKPM): - fix kdoc to conform to the standard - move kdoc from header to implementation files - remove superfluous WARN_ON() after kmalloc() - WARN_ON(x); if (!x) -> if(!WARN_ON(x)) - improve some comments Signed-off-by: Bernhard Walle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/firmware/memmap.c | 61 +++++++++++++++++++++++++++++++------------- include/linux/firmware-map.h | 26 ------------------- 2 files changed, 43 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 001622eb86f9..3bf8ee120d42 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -84,20 +84,23 @@ static struct kobj_type memmap_ktype = { */ /* - * Firmware memory map entries + * Firmware memory map entries. No locking is needed because the + * firmware_map_add() and firmware_map_add_early() functions are called + * in firmware initialisation code in one single thread of execution. */ static LIST_HEAD(map_entries); /** - * Common implementation of firmware_map_add() and firmware_map_add_early() - * which expects a pre-allocated struct firmware_map_entry. - * + * firmware_map_add_entry() - Does the real work to add a firmware memmap entry. * @start: Start of the memory range. * @end: End of the memory range (inclusive). * @type: Type of the memory range. * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised * entry. - */ + * + * Common implementation of firmware_map_add() and firmware_map_add_early() + * which expects a pre-allocated struct firmware_map_entry. + **/ static int firmware_map_add_entry(resource_size_t start, resource_size_t end, const char *type, struct firmware_map_entry *entry) @@ -115,33 +118,52 @@ static int firmware_map_add_entry(resource_size_t start, resource_size_t end, return 0; } -/* - * See for documentation. - */ +/** + * firmware_map_add() - Adds a firmware mapping entry. + * @start: Start of the memory range. + * @end: End of the memory range (inclusive). + * @type: Type of the memory range. + * + * This function uses kmalloc() for memory + * allocation. Use firmware_map_add_early() if you want to use the bootmem + * allocator. + * + * That function must be called before late_initcall. + * + * Returns 0 on success, or -ENOMEM if no memory could be allocated. + **/ int firmware_map_add(resource_size_t start, resource_size_t end, const char *type) { struct firmware_map_entry *entry; entry = kmalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); - WARN_ON(!entry); if (!entry) return -ENOMEM; return firmware_map_add_entry(start, end, type, entry); } -/* - * See for documentation. - */ +/** + * firmware_map_add_early() - Adds a firmware mapping entry. + * @start: Start of the memory range. + * @end: End of the memory range (inclusive). + * @type: Type of the memory range. + * + * Adds a firmware mapping entry. This function uses the bootmem allocator + * for memory allocation. Use firmware_map_add() if you want to use kmalloc(). + * + * That function must be called before late_initcall. + * + * Returns 0 on success, or -ENOMEM if no memory could be allocated. + **/ int __init firmware_map_add_early(resource_size_t start, resource_size_t end, const char *type) { struct firmware_map_entry *entry; entry = alloc_bootmem_low(sizeof(struct firmware_map_entry)); - WARN_ON(!entry); - if (!entry) + if (WARN_ON(!entry)) return -ENOMEM; return firmware_map_add_entry(start, end, type, entry); @@ -183,7 +205,10 @@ static ssize_t memmap_attr_show(struct kobject *kobj, /* * Initialises stuff and adds the entries in the map_entries list to * sysfs. Important is that firmware_map_add() and firmware_map_add_early() - * must be called before late_initcall. + * must be called before late_initcall. That's just because that function + * is called as late_initcall() function, which means that if you call + * firmware_map_add() or firmware_map_add_early() afterwards, the entries + * are not added to sysfs. */ static int __init memmap_init(void) { @@ -192,13 +217,13 @@ static int __init memmap_init(void) struct kset *memmap_kset; memmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj); - WARN_ON(!memmap_kset); - if (!memmap_kset) + if (WARN_ON(!memmap_kset)) return -ENOMEM; list_for_each_entry(entry, &map_entries, list) { entry->kobj.kset = memmap_kset; - kobject_add(&entry->kobj, NULL, "%d", i++); + if (kobject_add(&entry->kobj, NULL, "%d", i++)) + kobject_put(&entry->kobj); } return 0; diff --git a/include/linux/firmware-map.h b/include/linux/firmware-map.h index acbdbcc16051..6e199c8dfacc 100644 --- a/include/linux/firmware-map.h +++ b/include/linux/firmware-map.h @@ -24,34 +24,8 @@ */ #ifdef CONFIG_FIRMWARE_MEMMAP -/** - * Adds a firmware mapping entry. This function uses kmalloc() for memory - * allocation. Use firmware_map_add_early() if you want to use the bootmem - * allocator. - * - * That function must be called before late_initcall. - * - * @start: Start of the memory range. - * @end: End of the memory range (inclusive). - * @type: Type of the memory range. - * - * Returns 0 on success, or -ENOMEM if no memory could be allocated. - */ int firmware_map_add(resource_size_t start, resource_size_t end, const char *type); - -/** - * Adds a firmware mapping entry. This function uses the bootmem allocator - * for memory allocation. Use firmware_map_add() if you want to use kmalloc(). - * - * That function must be called before late_initcall. - * - * @start: Start of the memory range. - * @end: End of the memory range (inclusive). - * @type: Type of the memory range. - * - * Returns 0 on success, or -ENOMEM if no memory could be allocated. - */ int firmware_map_add_early(resource_size_t start, resource_size_t end, const char *type); -- cgit v1.2.3 From 39d2f1ab2a36ac527a6c41cfe689f50c239eaca3 Mon Sep 17 00:00:00 2001 From: David Chinner Date: Wed, 13 Aug 2008 16:40:43 +1000 Subject: [XFS] extend completions to provide XFS object flush requirements XFS object flushing doesn't quite match existing completion semantics. It mixed exclusive access with completion. That is, we need to mark an object as being flushed before flushing it to disk, and then block any other attempt to flush it until the completion occurs. We do this but adding an extra count to the completion before we start using them. However, we still need to determine if there is a completion in progress, and allow no-blocking attempts fo completions to decrement the count. To do this we introduce: int try_wait_for_completion(struct completion *x) returns a failure status if done == 0, otherwise decrements done to zero and returns a "started" status. This is provided to allow counted completions to begin safely while holding object locks in inverted order. int completion_done(struct completion *x) returns 1 if there is no waiter, 0 if there is a waiter (i.e. a completion in progress). This replaces the use of semaphores for providing this exclusion and completion mechanism. SGI-PV: 981498 SGI-Modid: xfs-linux-melb:xfs-kern:31816a Signed-off-by: David Chinner Signed-off-by: Lachlan McIlroy --- include/linux/completion.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'include/linux') diff --git a/include/linux/completion.h b/include/linux/completion.h index d2961b66d53d..57faa60de9bd 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -55,4 +55,49 @@ extern void complete_all(struct completion *); #define INIT_COMPLETION(x) ((x).done = 0) + +/** + * try_wait_for_completion - try to decrement a completion without blocking + * @x: completion structure + * + * Returns: 0 if a decrement cannot be done without blocking + * 1 if a decrement succeeded. + * + * If a completion is being used as a counting completion, + * attempt to decrement the counter without blocking. This + * enables us to avoid waiting if the resource the completion + * is protecting is not available. + */ +static inline bool try_wait_for_completion(struct completion *x) +{ + int ret = 1; + + spin_lock_irq(&x->wait.lock); + if (!x->done) + ret = 0; + else + x->done--; + spin_unlock_irq(&x->wait.lock); + return ret; +} + +/** + * completion_done - Test to see if a completion has any waiters + * @x: completion structure + * + * Returns: 0 if there are waiters (wait_for_completion() in progress) + * 1 if there are no waiters. + * + */ +static inline bool completion_done(struct completion *x) +{ + int ret = 1; + + spin_lock_irq(&x->wait.lock); + if (!x->done) + ret = 0; + spin_unlock_irq(&x->wait.lock); + return ret; +} + #endif -- cgit v1.2.3 From 24b8b44780a2c53ecb738f4a1c08d114f5eda27c Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 13 Aug 2008 11:05:41 -0500 Subject: svcrdma: Fix race between svc_rdma_recvfrom thread and the dto_tasklet RDMA_READ completions are kept on a separate queue from the general I/O request queue. Since a separate lock is used to protect the RDMA_READ completion queue, a race exists between the dto_tasklet and the svc_rdma_recvfrom thread where the dto_tasklet sets the XPT_DATA bit and adds I/O to the read-completion queue. Concurrently, the recvfrom thread checks the generic queue, finds it empty and resets the XPT_DATA bit. A subsequent svc_xprt_enqueue will fail to enqueue the transport for I/O and cause the transport to "stall". The fix is to protect both lists with the same lock and set the XPT_DATA bit with this lock held. Signed-off-by: Tom Tucker Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 1 - net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 8 ++++---- net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index ef2e3a20bf3b..dc05b54bd3a3 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -143,7 +143,6 @@ struct svcxprt_rdma { unsigned long sc_flags; struct list_head sc_dto_q; /* DTO tasklet I/O pending Q */ struct list_head sc_read_complete_q; - spinlock_t sc_read_complete_lock; struct work_struct sc_work; }; /* sc_flags */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index b4b17f44cb29..74de31a06616 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -443,18 +443,18 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) dprintk("svcrdma: rqstp=%p\n", rqstp); - spin_lock_bh(&rdma_xprt->sc_read_complete_lock); + spin_lock_bh(&rdma_xprt->sc_rq_dto_lock); if (!list_empty(&rdma_xprt->sc_read_complete_q)) { ctxt = list_entry(rdma_xprt->sc_read_complete_q.next, struct svc_rdma_op_ctxt, dto_q); list_del_init(&ctxt->dto_q); } - spin_unlock_bh(&rdma_xprt->sc_read_complete_lock); - if (ctxt) + if (ctxt) { + spin_unlock_bh(&rdma_xprt->sc_rq_dto_lock); return rdma_read_complete(rqstp, ctxt); + } - spin_lock_bh(&rdma_xprt->sc_rq_dto_lock); if (!list_empty(&rdma_xprt->sc_rq_dto_q)) { ctxt = list_entry(rdma_xprt->sc_rq_dto_q.next, struct svc_rdma_op_ctxt, diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 19ddc382b777..900cb69728c6 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -359,11 +359,11 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt) if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; BUG_ON(!read_hdr); + spin_lock_bh(&xprt->sc_rq_dto_lock); set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); - spin_lock_bh(&xprt->sc_read_complete_lock); list_add_tail(&read_hdr->dto_q, &xprt->sc_read_complete_q); - spin_unlock_bh(&xprt->sc_read_complete_lock); + spin_unlock_bh(&xprt->sc_rq_dto_lock); svc_xprt_enqueue(&xprt->sc_xprt); } svc_rdma_put_context(ctxt, 0); @@ -428,7 +428,6 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, init_waitqueue_head(&cma_xprt->sc_send_wait); spin_lock_init(&cma_xprt->sc_lock); - spin_lock_init(&cma_xprt->sc_read_complete_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); cma_xprt->sc_ord = svcrdma_ord; -- cgit v1.2.3 From 9e2b2dc4133f65272a6d3c5dcb2ce63f8a87cae9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 13 Aug 2008 16:20:04 +0100 Subject: CRED: Introduce credential access wrappers The patches that are intended to introduce copy-on-write credentials for 2.6.28 require abstraction of access to some fields of the task structure, particularly for the case of one task accessing another's credentials where RCU will have to be observed. Introduced here are trivial no-op versions of the desired accessors for current and other tasks so that other subsystems can start to be converted over more easily. Wrappers are introduced into a new header (linux/cred.h) for UID/GID, EUID/EGID, SUID/SGID, FSUID/FSGID, cap_effective and current's subscribed user_struct. These wrappers are macros because the ordering between header files mitigates against making them inline functions. linux/cred.h is #included from linux/sched.h. Further, XFS is modified such that it no longer defines and uses parameterised versions of current_fs[ug]id(), thus getting rid of the namespace collision otherwise incurred. Signed-off-by: David Howells Signed-off-by: James Morris --- fs/xfs/linux-2.6/xfs_linux.h | 2 -- fs/xfs/xfs_inode.c | 4 ++-- fs/xfs/xfs_vnodeops.c | 8 +++---- include/linux/cred.h | 50 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/sched.h | 1 + 5 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 include/linux/cred.h (limited to 'include/linux') diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index 3b7c4ff48ba0..cc0f7b3a9795 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -126,8 +126,6 @@ #define current_cpu() (raw_smp_processor_id()) #define current_pid() (current->pid) -#define current_fsuid(cred) (current->fsuid) -#define current_fsgid(cred) (current->fsgid) #define current_test_flags(f) (current->flags & (f)) #define current_set_flags_nested(sp, f) \ (*(sp) = current->flags, current->flags |= (f)) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 358511b85ced..00e80df9dd9d 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1081,8 +1081,8 @@ xfs_ialloc( ip->i_d.di_onlink = 0; ip->i_d.di_nlink = nlink; ASSERT(ip->i_d.di_nlink == nlink); - ip->i_d.di_uid = current_fsuid(cr); - ip->i_d.di_gid = current_fsgid(cr); + ip->i_d.di_uid = current_fsuid(); + ip->i_d.di_gid = current_fsgid(); ip->i_d.di_projid = prid; memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad)); diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 588bb4aa215d..aa238c8fbd7a 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -182,7 +182,7 @@ xfs_setattr( xfs_ilock(ip, lock_flags); /* boolean: are we the file owner? */ - file_owner = (current_fsuid(credp) == ip->i_d.di_uid); + file_owner = (current_fsuid() == ip->i_d.di_uid); /* * Change various properties of a file. @@ -1533,7 +1533,7 @@ xfs_create( * Make sure that we have allocated dquot(s) on disk. */ error = XFS_QM_DQVOPALLOC(mp, dp, - current_fsuid(credp), current_fsgid(credp), prid, + current_fsuid(), current_fsgid(), prid, XFS_QMOPT_QUOTALL|XFS_QMOPT_INHERIT, &udqp, &gdqp); if (error) goto std_return; @@ -2269,7 +2269,7 @@ xfs_mkdir( * Make sure that we have allocated dquot(s) on disk. */ error = XFS_QM_DQVOPALLOC(mp, dp, - current_fsuid(credp), current_fsgid(credp), prid, + current_fsuid(), current_fsgid(), prid, XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp); if (error) goto std_return; @@ -2495,7 +2495,7 @@ xfs_symlink( * Make sure that we have allocated dquot(s) on disk. */ error = XFS_QM_DQVOPALLOC(mp, dp, - current_fsuid(credp), current_fsgid(credp), prid, + current_fsuid(), current_fsgid(), prid, XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp); if (error) goto std_return; diff --git a/include/linux/cred.h b/include/linux/cred.h new file mode 100644 index 000000000000..b69222cc1fd2 --- /dev/null +++ b/include/linux/cred.h @@ -0,0 +1,50 @@ +/* Credentials management + * + * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_CRED_H +#define _LINUX_CRED_H + +#define get_current_user() (get_uid(current->user)) + +#define task_uid(task) ((task)->uid) +#define task_gid(task) ((task)->gid) +#define task_euid(task) ((task)->euid) +#define task_egid(task) ((task)->egid) + +#define current_uid() (current->uid) +#define current_gid() (current->gid) +#define current_euid() (current->euid) +#define current_egid() (current->egid) +#define current_suid() (current->suid) +#define current_sgid() (current->sgid) +#define current_fsuid() (current->fsuid) +#define current_fsgid() (current->fsgid) +#define current_cap() (current->cap_effective) + +#define current_uid_gid(_uid, _gid) \ +do { \ + *(_uid) = current->uid; \ + *(_gid) = current->gid; \ +} while(0) + +#define current_euid_egid(_uid, _gid) \ +do { \ + *(_uid) = current->euid; \ + *(_gid) = current->egid; \ +} while(0) + +#define current_fsuid_fsgid(_uid, _gid) \ +do { \ + *(_uid) = current->fsuid; \ + *(_gid) = current->fsgid; \ +} while(0) + +#endif /* _LINUX_CRED_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 5850bfb968a8..cfb0d87b99fc 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -87,6 +87,7 @@ struct sched_param { #include #include #include +#include #include -- cgit v1.2.3 From f4f4d58734916e816d4b4a7cf61b3fc22ce02683 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 28 Jul 2008 10:39:28 -0400 Subject: USB: add missing kerneldoc line for "needs_binding" This patch (as1117) adds a kerneldoc line for the "needs_binding" field in struct usb_interface. It was accidentally omitted when the field was added. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index 5811c5da69f9..0924cd9c30f6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -110,6 +110,8 @@ enum usb_interface_condition { * @sysfs_files_created: sysfs attributes exist * @needs_remote_wakeup: flag set when the driver requires remote-wakeup * capability during autosuspend. + * @needs_binding: flag set when the driver should be re-probed or unbound + * following a reset or suspend operation it doesn't support. * @dev: driver model's view of this device * @usb_dev: if an interface is bound to the USB major, this will point * to the sysfs representation for that device. -- cgit v1.2.3 From 0282b7f2a874e72c18fcd5a112ccf67f71ba7f5c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 29 Jul 2008 12:01:04 -0400 Subject: usb-serial: don't release unregistered minors This patch (as1121) fixes a bug in the USB serial core. When a device is unregistered, the core will give back its minors -- even if the device hasn't been assigned any! The patch reserves the highest minor value (255) to mean that no minor was assigned. It also removes some dead code and does a small style fixup. Signed-off-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 7 +++---- include/linux/usb/serial.h | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 8c2d531eedea..b157c48e8b78 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -122,9 +122,6 @@ static void return_serial(struct usb_serial *serial) dbg("%s", __func__); - if (serial == NULL) - return; - for (i = 0; i < serial->num_ports; ++i) serial_table[serial->minor + i] = NULL; } @@ -142,7 +139,8 @@ static void destroy_serial(struct kref *kref) serial->type->shutdown(serial); /* return the minor range that this device had */ - return_serial(serial); + if (serial->minor != SERIAL_TTY_NO_MINOR) + return_serial(serial); for (i = 0; i < serial->num_ports; ++i) serial->port[i]->port.count = 0; @@ -575,6 +573,7 @@ static struct usb_serial *create_serial(struct usb_device *dev, serial->interface = interface; kref_init(&serial->kref); mutex_init(&serial->disc_mutex); + serial->minor = SERIAL_TTY_NO_MINOR; return serial; } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 09a3e6a7518f..655341d0f534 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -17,7 +17,8 @@ #include #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ -#define SERIAL_TTY_MINORS 255 /* loads of devices :) */ +#define SERIAL_TTY_MINORS 254 /* loads of devices :) */ +#define SERIAL_TTY_NO_MINOR 255 /* No minor was assigned */ /* The maximum number of ports one device can grab at once */ #define MAX_NUM_PORTS 8 -- cgit v1.2.3 From 550a7375fe720924241f0eb76e4a5c1a3eb8c32f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jul 2008 12:27:36 +0300 Subject: USB: Add MUSB and TUSB support This patch adds support for MUSB and TUSB controllers integrated into omap2430 and davinci. It also adds support for external tusb6010 controller. Cc: David Brownell Cc: Tony Lindgren Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 + drivers/Makefile | 1 + drivers/usb/Kconfig | 2 + drivers/usb/gadget/Kconfig | 10 + drivers/usb/musb/Kconfig | 176 +++ drivers/usb/musb/Makefile | 86 ++ drivers/usb/musb/cppi_dma.c | 1540 ++++++++++++++++++++++++ drivers/usb/musb/cppi_dma.h | 133 +++ drivers/usb/musb/davinci.c | 462 ++++++++ drivers/usb/musb/davinci.h | 100 ++ drivers/usb/musb/musb_core.c | 2266 ++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_core.h | 517 ++++++++ drivers/usb/musb/musb_debug.h | 66 ++ drivers/usb/musb/musb_dma.h | 172 +++ drivers/usb/musb/musb_gadget.c | 2033 ++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_gadget.h | 108 ++ drivers/usb/musb/musb_gadget_ep0.c | 981 ++++++++++++++++ drivers/usb/musb/musb_host.c | 2170 ++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_host.h | 110 ++ drivers/usb/musb/musb_io.h | 115 ++ drivers/usb/musb/musb_procfs.c | 830 +++++++++++++ drivers/usb/musb/musb_regs.h | 300 +++++ drivers/usb/musb/musb_virthub.c | 425 +++++++ drivers/usb/musb/musbhsdma.c | 433 +++++++ drivers/usb/musb/omap2430.c | 324 ++++++ drivers/usb/musb/omap2430.h | 56 + drivers/usb/musb/tusb6010.c | 1151 ++++++++++++++++++ drivers/usb/musb/tusb6010.h | 402 +++++++ drivers/usb/musb/tusb6010_omap.c | 719 ++++++++++++ include/linux/usb/musb.h | 70 ++ 30 files changed, 15764 insertions(+) create mode 100644 drivers/usb/musb/Kconfig create mode 100644 drivers/usb/musb/Makefile create mode 100644 drivers/usb/musb/cppi_dma.c create mode 100644 drivers/usb/musb/cppi_dma.h create mode 100644 drivers/usb/musb/davinci.c create mode 100644 drivers/usb/musb/davinci.h create mode 100644 drivers/usb/musb/musb_core.c create mode 100644 drivers/usb/musb/musb_core.h create mode 100644 drivers/usb/musb/musb_debug.h create mode 100644 drivers/usb/musb/musb_dma.h create mode 100644 drivers/usb/musb/musb_gadget.c create mode 100644 drivers/usb/musb/musb_gadget.h create mode 100644 drivers/usb/musb/musb_gadget_ep0.c create mode 100644 drivers/usb/musb/musb_host.c create mode 100644 drivers/usb/musb/musb_host.h create mode 100644 drivers/usb/musb/musb_io.h create mode 100644 drivers/usb/musb/musb_procfs.c create mode 100644 drivers/usb/musb/musb_regs.h create mode 100644 drivers/usb/musb/musb_virthub.c create mode 100644 drivers/usb/musb/musbhsdma.c create mode 100644 drivers/usb/musb/omap2430.c create mode 100644 drivers/usb/musb/omap2430.h create mode 100644 drivers/usb/musb/tusb6010.c create mode 100644 drivers/usb/musb/tusb6010.h create mode 100644 drivers/usb/musb/tusb6010_omap.c create mode 100644 include/linux/usb/musb.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 0c42dc25e0e7..773d6bc3a9a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2928,6 +2928,12 @@ M: jirislaby@gmail.com L: linux-kernel@vger.kernel.org S: Maintained +MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER +P: Felipe Balbi +M: felipe.balbi@nokia.com +L: linux-usb@vger.kernel.org +S: Maintained + MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE) P: Andrew Gallatin M: gallatin@myri.com diff --git a/drivers/Makefile b/drivers/Makefile index a280ab3d0833..2735bde73475 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_USB) += usb/ +obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_SERIO) += input/serio/ diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 4f9b5ecfb721..bcefbddeba50 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -99,6 +99,8 @@ source "drivers/usb/mon/Kconfig" source "drivers/usb/host/Kconfig" +source "drivers/usb/musb/Kconfig" + source "drivers/usb/class/Kconfig" source "drivers/usb/storage/Kconfig" diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c6a8c6b1116a..acc95b2ac6f8 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -284,6 +284,16 @@ config USB_LH7A40X default USB_GADGET select USB_GADGET_SELECTED +# built in ../musb along with host support +config USB_GADGET_MUSB_HDRC + boolean "Inventra HDRC USB Peripheral (TI, ...)" + depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) + select USB_GADGET_DUALSPEED + select USB_GADGET_SELECTED + help + This OTG-capable silicon IP is used in dual designs including + the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. + config USB_GADGET_OMAP boolean "OMAP USB Device Controller" depends on ARCH_OMAP diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig new file mode 100644 index 000000000000..faca4333f27a --- /dev/null +++ b/drivers/usb/musb/Kconfig @@ -0,0 +1,176 @@ +# +# USB Dual Role (OTG-ready) Controller Drivers +# for silicon based on Mentor Graphics INVENTRA designs +# + +comment "Enable Host or Gadget support to see Inventra options" + depends on !USB && USB_GADGET=n + +# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller +config USB_MUSB_HDRC + depends on (USB || USB_GADGET) && HAVE_CLK + select TWL4030_USB if MACH_OMAP_3430SDP + tristate 'Inventra Highspeed Dual Role Controller (TI, ...)' + help + Say Y here if your system has a dual role high speed USB + controller based on the Mentor Graphics silicon IP. Then + configure options to match your silicon and the board + it's being used with, including the USB peripheral role, + or the USB host role, or both. + + Texas Instruments parts using this IP include DaVinci 644x, + OMAP 243x, OMAP 343x, and TUSB 6010. + + If you do not know what this is, please say N. + + To compile this driver as a module, choose M here; the + module will be called "musb_hdrc". + +config USB_MUSB_SOC + boolean + depends on USB_MUSB_HDRC + default y if ARCH_DAVINCI + default y if ARCH_OMAP2430 + default y if ARCH_OMAP34XX + help + Use a static file to describe how the + controller is configured (endpoints, mechanisms, etc) on the + current iteration of a given system-on-chip. + +comment "DaVinci 644x USB support" + depends on USB_MUSB_HDRC && ARCH_DAVINCI + +comment "OMAP 243x high speed USB support" + depends on USB_MUSB_HDRC && ARCH_OMAP2430 + +comment "OMAP 343x high speed USB support" + depends on USB_MUSB_HDRC && ARCH_OMAP34XX + +config USB_TUSB6010 + boolean "TUSB 6010 support" + depends on USB_MUSB_HDRC && !USB_MUSB_SOC + default y + help + The TUSB 6010 chip, from Texas Instruments, connects a discrete + HDRC core using a 16-bit parallel bus (NOR flash style) or VLYNQ + (a high speed serial link). It can use system-specific external + DMA controllers. + +choice + prompt "Driver Mode" + depends on USB_MUSB_HDRC + help + Dual-Role devices can support both host and peripheral roles, + as well as a the special "OTG Device" role which can switch + between both roles as needed. + +# use USB_MUSB_HDRC_HCD not USB_MUSB_HOST to #ifdef host side support; +# OTG needs both roles, not just USB_MUSB_HOST. +config USB_MUSB_HOST + depends on USB + bool "USB Host" + help + Say Y here if your system supports the USB host role. + If it has a USB "A" (rectangular), "Mini-A" (uncommon), + or "Mini-AB" connector, it supports the host role. + (With a "Mini-AB" connector, you should enable USB OTG.) + +# use USB_GADGET_MUSB_HDRC not USB_MUSB_PERIPHERAL to #ifdef peripheral +# side support ... OTG needs both roles +config USB_MUSB_PERIPHERAL + depends on USB_GADGET + bool "USB Peripheral (gadget stack)" + select USB_GADGET_MUSB_HDRC + help + Say Y here if your system supports the USB peripheral role. + If it has a USB "B" (squarish), "Mini-B", or "Mini-AB" + connector, it supports the peripheral role. + (With a "Mini-AB" connector, you should enable USB OTG.) + +config USB_MUSB_OTG + depends on USB && USB_GADGET && PM && EXPERIMENTAL + bool "Both host and peripheral: USB OTG (On The Go) Device" + select USB_GADGET_MUSB_HDRC + select USB_OTG + help + The most notable feature of USB OTG is support for a + "Dual-Role" device, which can act as either a device + or a host. The initial role choice can be changed + later, when two dual-role devices talk to each other. + + At this writing, the OTG support in this driver is incomplete, + omitting the mandatory HNP or SRP protocols. However, some + of the cable based role switching works. (That is, grounding + the ID pin switches the controller to host mode, while leaving + it floating leaves it in peripheral mode.) + + Select this if your system has a Mini-AB connector, or + to simplify certain kinds of configuration. + + To implement your OTG Targeted Peripherals List (TPL), enable + USB_OTG_WHITELIST and update "drivers/usb/core/otg_whitelist.h" + to match your requirements. + +endchoice + +# enable peripheral support (including with OTG) +config USB_GADGET_MUSB_HDRC + bool + depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) +# default y +# select USB_GADGET_DUALSPEED +# select USB_GADGET_SELECTED + +# enables host support (including with OTG) +config USB_MUSB_HDRC_HCD + bool + depends on USB_MUSB_HDRC && (USB_MUSB_HOST || USB_MUSB_OTG) + select USB_OTG if USB_GADGET_MUSB_HDRC + default y + + +config MUSB_PIO_ONLY + bool 'Disable DMA (always use PIO)' + depends on USB_MUSB_HDRC + default y if USB_TUSB6010 + help + All data is copied between memory and FIFO by the CPU. + DMA controllers are ignored. + + Do not select 'n' here unless DMA support for your SOC or board + is unavailable (or unstable). When DMA is enabled at compile time, + you can still disable it at run time using the "use_dma=n" module + parameter. + +config USB_INVENTRA_DMA + bool + depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY + default ARCH_OMAP2430 || ARCH_OMAP34XX + help + Enable DMA transfers using Mentor's engine. + +config USB_TI_CPPI_DMA + bool + depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY + default ARCH_DAVINCI + help + Enable DMA transfers when TI CPPI DMA is available. + +config USB_TUSB_OMAP_DMA + bool + depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY + depends on USB_TUSB6010 + depends on ARCH_OMAP + default y + help + Enable DMA transfers on TUSB 6010 when OMAP DMA is available. + +config USB_MUSB_LOGLEVEL + depends on USB_MUSB_HDRC + int 'Logging Level (0 - none / 3 - annoying / ... )' + default 0 + help + Set the logging level. 0 disables the debugging altogether, + although when USB_DEBUG is set the value is at least 1. + Starting at level 3, per-transfer (urb, usb_request, packet, + or dma transfer) tracing may kick in. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile new file mode 100644 index 000000000000..88eb67de08ae --- /dev/null +++ b/drivers/usb/musb/Makefile @@ -0,0 +1,86 @@ +# +# for USB OTG silicon based on Mentor Graphics INVENTRA designs +# + +musb_hdrc-objs := musb_core.o + +obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o + +ifeq ($(CONFIG_ARCH_DAVINCI),y) + musb_hdrc-objs += davinci.o +endif + +ifeq ($(CONFIG_USB_TUSB6010),y) + musb_hdrc-objs += tusb6010.o +endif + +ifeq ($(CONFIG_ARCH_OMAP2430),y) + musb_hdrc-objs += omap2430.o +endif + +ifeq ($(CONFIG_ARCH_OMAP3430),y) + musb_hdrc-objs += omap2430.o +endif + +ifeq ($(CONFIG_USB_GADGET_MUSB_HDRC),y) + musb_hdrc-objs += musb_gadget_ep0.o musb_gadget.o +endif + +ifeq ($(CONFIG_USB_MUSB_HDRC_HCD),y) + musb_hdrc-objs += musb_virthub.o musb_host.o +endif + +# the kconfig must guarantee that only one of the +# possible I/O schemes will be enabled at a time ... +# PIO only, or DMA (several potential schemes). +# though PIO is always there to back up DMA, and for ep0 + +ifneq ($(CONFIG_MUSB_PIO_ONLY),y) + + ifeq ($(CONFIG_USB_INVENTRA_DMA),y) + musb_hdrc-objs += musbhsdma.o + + else + ifeq ($(CONFIG_USB_TI_CPPI_DMA),y) + musb_hdrc-objs += cppi_dma.o + + else + ifeq ($(CONFIG_USB_TUSB_OMAP_DMA),y) + musb_hdrc-objs += tusb6010_omap.o + + endif + endif + endif +endif + + +################################################################################ + +# FIXME remove all these extra "-DMUSB_* things, stick to CONFIG_* + +ifeq ($(CONFIG_USB_INVENTRA_MUSB_HAS_AHB_ID),y) + EXTRA_CFLAGS += -DMUSB_AHB_ID +endif + +# Debugging + +MUSB_DEBUG:=$(CONFIG_USB_MUSB_LOGLEVEL) + +ifeq ("$(strip $(MUSB_DEBUG))","") + ifdef CONFIG_USB_DEBUG + MUSB_DEBUG:=1 + else + MUSB_DEBUG:=0 + endif +endif + +ifneq ($(MUSB_DEBUG),0) + EXTRA_CFLAGS += -DDEBUG + + ifeq ($(CONFIG_PROC_FS),y) + musb_hdrc-objs += musb_procfs.o + endif + +endif + +EXTRA_CFLAGS += -DMUSB_DEBUG=$(MUSB_DEBUG) diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c new file mode 100644 index 000000000000..5ad6d0893cbe --- /dev/null +++ b/drivers/usb/musb/cppi_dma.c @@ -0,0 +1,1540 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file implements a DMA interface using TI's CPPI DMA. + * For now it's DaVinci-only, but CPPI isn't specific to DaVinci or USB. + * The TUSB6020, using VLYNQ, has CPPI that looks much like DaVinci. + */ + +#include + +#include "musb_core.h" +#include "cppi_dma.h" + + +/* CPPI DMA status 7-mar-2006: + * + * - See musb_{host,gadget}.c for more info + * + * - Correct RX DMA generally forces the engine into irq-per-packet mode, + * which can easily saturate the CPU under non-mass-storage loads. + * + * NOTES 24-aug-2006 (2.6.18-rc4): + * + * - peripheral RXDMA wedged in a test with packets of length 512/512/1. + * evidently after the 1 byte packet was received and acked, the queue + * of BDs got garbaged so it wouldn't empty the fifo. (rxcsr 0x2003, + * and RX DMA0: 4 left, 80000000 8feff880, 8feff860 8feff860; 8f321401 + * 004001ff 00000001 .. 8feff860) Host was just getting NAKed on tx + * of its next (512 byte) packet. IRQ issues? + * + * REVISIT: the "transfer DMA" glue between CPPI and USB fifos will + * evidently also directly update the RX and TX CSRs ... so audit all + * host and peripheral side DMA code to avoid CSR access after DMA has + * been started. + */ + +/* REVISIT now we can avoid preallocating these descriptors; or + * more simply, switch to a global freelist not per-channel ones. + * Note: at full speed, 64 descriptors == 4K bulk data. + */ +#define NUM_TXCHAN_BD 64 +#define NUM_RXCHAN_BD 64 + +static inline void cpu_drain_writebuffer(void) +{ + wmb(); +#ifdef CONFIG_CPU_ARM926T + /* REVISIT this "should not be needed", + * but lack of it sure seemed to hurt ... + */ + asm("mcr p15, 0, r0, c7, c10, 4 @ drain write buffer\n"); +#endif +} + +static inline struct cppi_descriptor *cppi_bd_alloc(struct cppi_channel *c) +{ + struct cppi_descriptor *bd = c->freelist; + + if (bd) + c->freelist = bd->next; + return bd; +} + +static inline void +cppi_bd_free(struct cppi_channel *c, struct cppi_descriptor *bd) +{ + if (!bd) + return; + bd->next = c->freelist; + c->freelist = bd; +} + +/* + * Start DMA controller + * + * Initialize the DMA controller as necessary. + */ + +/* zero out entire rx state RAM entry for the channel */ +static void cppi_reset_rx(struct cppi_rx_stateram __iomem *rx) +{ + musb_writel(&rx->rx_skipbytes, 0, 0); + musb_writel(&rx->rx_head, 0, 0); + musb_writel(&rx->rx_sop, 0, 0); + musb_writel(&rx->rx_current, 0, 0); + musb_writel(&rx->rx_buf_current, 0, 0); + musb_writel(&rx->rx_len_len, 0, 0); + musb_writel(&rx->rx_cnt_cnt, 0, 0); +} + +/* zero out entire tx state RAM entry for the channel */ +static void cppi_reset_tx(struct cppi_tx_stateram __iomem *tx, u32 ptr) +{ + musb_writel(&tx->tx_head, 0, 0); + musb_writel(&tx->tx_buf, 0, 0); + musb_writel(&tx->tx_current, 0, 0); + musb_writel(&tx->tx_buf_current, 0, 0); + musb_writel(&tx->tx_info, 0, 0); + musb_writel(&tx->tx_rem_len, 0, 0); + /* musb_writel(&tx->tx_dummy, 0, 0); */ + musb_writel(&tx->tx_complete, 0, ptr); +} + +static void __init cppi_pool_init(struct cppi *cppi, struct cppi_channel *c) +{ + int j; + + /* initialize channel fields */ + c->head = NULL; + c->tail = NULL; + c->last_processed = NULL; + c->channel.status = MUSB_DMA_STATUS_UNKNOWN; + c->controller = cppi; + c->is_rndis = 0; + c->freelist = NULL; + + /* build the BD Free list for the channel */ + for (j = 0; j < NUM_TXCHAN_BD + 1; j++) { + struct cppi_descriptor *bd; + dma_addr_t dma; + + bd = dma_pool_alloc(cppi->pool, GFP_KERNEL, &dma); + bd->dma = dma; + cppi_bd_free(c, bd); + } +} + +static int cppi_channel_abort(struct dma_channel *); + +static void cppi_pool_free(struct cppi_channel *c) +{ + struct cppi *cppi = c->controller; + struct cppi_descriptor *bd; + + (void) cppi_channel_abort(&c->channel); + c->channel.status = MUSB_DMA_STATUS_UNKNOWN; + c->controller = NULL; + + /* free all its bds */ + bd = c->last_processed; + do { + if (bd) + dma_pool_free(cppi->pool, bd, bd->dma); + bd = cppi_bd_alloc(c); + } while (bd); + c->last_processed = NULL; +} + +static int __init cppi_controller_start(struct dma_controller *c) +{ + struct cppi *controller; + void __iomem *tibase; + int i; + + controller = container_of(c, struct cppi, controller); + + /* do whatever is necessary to start controller */ + for (i = 0; i < ARRAY_SIZE(controller->tx); i++) { + controller->tx[i].transmit = true; + controller->tx[i].index = i; + } + for (i = 0; i < ARRAY_SIZE(controller->rx); i++) { + controller->rx[i].transmit = false; + controller->rx[i].index = i; + } + + /* setup BD list on a per channel basis */ + for (i = 0; i < ARRAY_SIZE(controller->tx); i++) + cppi_pool_init(controller, controller->tx + i); + for (i = 0; i < ARRAY_SIZE(controller->rx); i++) + cppi_pool_init(controller, controller->rx + i); + + tibase = controller->tibase; + INIT_LIST_HEAD(&controller->tx_complete); + + /* initialise tx/rx channel head pointers to zero */ + for (i = 0; i < ARRAY_SIZE(controller->tx); i++) { + struct cppi_channel *tx_ch = controller->tx + i; + struct cppi_tx_stateram __iomem *tx; + + INIT_LIST_HEAD(&tx_ch->tx_complete); + + tx = tibase + DAVINCI_TXCPPI_STATERAM_OFFSET(i); + tx_ch->state_ram = tx; + cppi_reset_tx(tx, 0); + } + for (i = 0; i < ARRAY_SIZE(controller->rx); i++) { + struct cppi_channel *rx_ch = controller->rx + i; + struct cppi_rx_stateram __iomem *rx; + + INIT_LIST_HEAD(&rx_ch->tx_complete); + + rx = tibase + DAVINCI_RXCPPI_STATERAM_OFFSET(i); + rx_ch->state_ram = rx; + cppi_reset_rx(rx); + } + + /* enable individual cppi channels */ + musb_writel(tibase, DAVINCI_TXCPPI_INTENAB_REG, + DAVINCI_DMA_ALL_CHANNELS_ENABLE); + musb_writel(tibase, DAVINCI_RXCPPI_INTENAB_REG, + DAVINCI_DMA_ALL_CHANNELS_ENABLE); + + /* enable tx/rx CPPI control */ + musb_writel(tibase, DAVINCI_TXCPPI_CTRL_REG, DAVINCI_DMA_CTRL_ENABLE); + musb_writel(tibase, DAVINCI_RXCPPI_CTRL_REG, DAVINCI_DMA_CTRL_ENABLE); + + /* disable RNDIS mode, also host rx RNDIS autorequest */ + musb_writel(tibase, DAVINCI_RNDIS_REG, 0); + musb_writel(tibase, DAVINCI_AUTOREQ_REG, 0); + + return 0; +} + +/* + * Stop DMA controller + * + * De-Init the DMA controller as necessary. + */ + +static int cppi_controller_stop(struct dma_controller *c) +{ + struct cppi *controller; + void __iomem *tibase; + int i; + + controller = container_of(c, struct cppi, controller); + + tibase = controller->tibase; + /* DISABLE INDIVIDUAL CHANNEL Interrupts */ + musb_writel(tibase, DAVINCI_TXCPPI_INTCLR_REG, + DAVINCI_DMA_ALL_CHANNELS_ENABLE); + musb_writel(tibase, DAVINCI_RXCPPI_INTCLR_REG, + DAVINCI_DMA_ALL_CHANNELS_ENABLE); + + DBG(1, "Tearing down RX and TX Channels\n"); + for (i = 0; i < ARRAY_SIZE(controller->tx); i++) { + /* FIXME restructure of txdma to use bds like rxdma */ + controller->tx[i].last_processed = NULL; + cppi_pool_free(controller->tx + i); + } + for (i = 0; i < ARRAY_SIZE(controller->rx); i++) + cppi_pool_free(controller->rx + i); + + /* in Tx Case proper teardown is supported. We resort to disabling + * Tx/Rx CPPI after cleanup of Tx channels. Before TX teardown is + * complete TX CPPI cannot be disabled. + */ + /*disable tx/rx cppi */ + musb_writel(tibase, DAVINCI_TXCPPI_CTRL_REG, DAVINCI_DMA_CTRL_DISABLE); + musb_writel(tibase, DAVINCI_RXCPPI_CTRL_REG, DAVINCI_DMA_CTRL_DISABLE); + + return 0; +} + +/* While dma channel is allocated, we only want the core irqs active + * for fault reports, otherwise we'd get irqs that we don't care about. + * Except for TX irqs, where dma done != fifo empty and reusable ... + * + * NOTE: docs don't say either way, but irq masking **enables** irqs. + * + * REVISIT same issue applies to pure PIO usage too, and non-cppi dma... + */ +static inline void core_rxirq_disable(void __iomem *tibase, unsigned epnum) +{ + musb_writel(tibase, DAVINCI_USB_INT_MASK_CLR_REG, 1 << (epnum + 8)); +} + +static inline void core_rxirq_enable(void __iomem *tibase, unsigned epnum) +{ + musb_writel(tibase, DAVINCI_USB_INT_MASK_SET_REG, 1 << (epnum + 8)); +} + + +/* + * Allocate a CPPI Channel for DMA. With CPPI, channels are bound to + * each transfer direction of a non-control endpoint, so allocating + * (and deallocating) is mostly a way to notice bad housekeeping on + * the software side. We assume the irqs are always active. + */ +static struct dma_channel * +cppi_channel_allocate(struct dma_controller *c, + struct musb_hw_ep *ep, u8 transmit) +{ + struct cppi *controller; + u8 index; + struct cppi_channel *cppi_ch; + void __iomem *tibase; + + controller = container_of(c, struct cppi, controller); + tibase = controller->tibase; + + /* ep0 doesn't use DMA; remember cppi indices are 0..N-1 */ + index = ep->epnum - 1; + + /* return the corresponding CPPI Channel Handle, and + * probably disable the non-CPPI irq until we need it. + */ + if (transmit) { + if (index >= ARRAY_SIZE(controller->tx)) { + DBG(1, "no %cX%d CPPI channel\n", 'T', index); + return NULL; + } + cppi_ch = controller->tx + index; + } else { + if (index >= ARRAY_SIZE(controller->rx)) { + DBG(1, "no %cX%d CPPI channel\n", 'R', index); + return NULL; + } + cppi_ch = controller->rx + index; + core_rxirq_disable(tibase, ep->epnum); + } + + /* REVISIT make this an error later once the same driver code works + * with the other DMA engine too + */ + if (cppi_ch->hw_ep) + DBG(1, "re-allocating DMA%d %cX channel %p\n", + index, transmit ? 'T' : 'R', cppi_ch); + cppi_ch->hw_ep = ep; + cppi_ch->channel.status = MUSB_DMA_STATUS_FREE; + + DBG(4, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R'); + return &cppi_ch->channel; +} + +/* Release a CPPI Channel. */ +static void cppi_channel_release(struct dma_channel *channel) +{ + struct cppi_channel *c; + void __iomem *tibase; + + /* REVISIT: for paranoia, check state and abort if needed... */ + + c = container_of(channel, struct cppi_channel, channel); + tibase = c->controller->tibase; + if (!c->hw_ep) + DBG(1, "releasing idle DMA channel %p\n", c); + else if (!c->transmit) + core_rxirq_enable(tibase, c->index + 1); + + /* for now, leave its cppi IRQ enabled (we won't trigger it) */ + c->hw_ep = NULL; + channel->status = MUSB_DMA_STATUS_UNKNOWN; +} + +/* Context: controller irqlocked */ +static void +cppi_dump_rx(int level, struct cppi_channel *c, const char *tag) +{ + void __iomem *base = c->controller->mregs; + struct cppi_rx_stateram __iomem *rx = c->state_ram; + + musb_ep_select(base, c->index + 1); + + DBG(level, "RX DMA%d%s: %d left, csr %04x, " + "%08x H%08x S%08x C%08x, " + "B%08x L%08x %08x .. %08x" + "\n", + c->index, tag, + musb_readl(c->controller->tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + 4 * c->index), + musb_readw(c->hw_ep->regs, MUSB_RXCSR), + + musb_readl(&rx->rx_skipbytes, 0), + musb_readl(&rx->rx_head, 0), + musb_readl(&rx->rx_sop, 0), + musb_readl(&rx->rx_current, 0), + + musb_readl(&rx->rx_buf_current, 0), + musb_readl(&rx->rx_len_len, 0), + musb_readl(&rx->rx_cnt_cnt, 0), + musb_readl(&rx->rx_complete, 0) + ); +} + +/* Context: controller irqlocked */ +static void +cppi_dump_tx(int level, struct cppi_channel *c, const char *tag) +{ + void __iomem *base = c->controller->mregs; + struct cppi_tx_stateram __iomem *tx = c->state_ram; + + musb_ep_select(base, c->index + 1); + + DBG(level, "TX DMA%d%s: csr %04x, " + "H%08x S%08x C%08x %08x, " + "F%08x L%08x .. %08x" + "\n", + c->index, tag, + musb_readw(c->hw_ep->regs, MUSB_TXCSR), + + musb_readl(&tx->tx_head, 0), + musb_readl(&tx->tx_buf, 0), + musb_readl(&tx->tx_current, 0), + musb_readl(&tx->tx_buf_current, 0), + + musb_readl(&tx->tx_info, 0), + musb_readl(&tx->tx_rem_len, 0), + /* dummy/unused word 6 */ + musb_readl(&tx->tx_complete, 0) + ); +} + +/* Context: controller irqlocked */ +static inline void +cppi_rndis_update(struct cppi_channel *c, int is_rx, + void __iomem *tibase, int is_rndis) +{ + /* we may need to change the rndis flag for this cppi channel */ + if (c->is_rndis != is_rndis) { + u32 value = musb_readl(tibase, DAVINCI_RNDIS_REG); + u32 temp = 1 << (c->index); + + if (is_rx) + temp <<= 16; + if (is_rndis) + value |= temp; + else + value &= ~temp; + musb_writel(tibase, DAVINCI_RNDIS_REG, value); + c->is_rndis = is_rndis; + } +} + +static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) +{ + pr_debug("RXBD/%s %08x: " + "nxt %08x buf %08x off.blen %08x opt.plen %08x\n", + tag, bd->dma, + bd->hw_next, bd->hw_bufp, bd->hw_off_len, + bd->hw_options); +} + +static void cppi_dump_rxq(int level, const char *tag, struct cppi_channel *rx) +{ +#if MUSB_DEBUG > 0 + struct cppi_descriptor *bd; + + if (!_dbg_level(level)) + return; + cppi_dump_rx(level, rx, tag); + if (rx->last_processed) + cppi_dump_rxbd("last", rx->last_processed); + for (bd = rx->head; bd; bd = bd->next) + cppi_dump_rxbd("active", bd); +#endif +} + + +/* NOTE: DaVinci autoreq is ignored except for host side "RNDIS" mode RX; + * so we won't ever use it (see "CPPI RX Woes" below). + */ +static inline int cppi_autoreq_update(struct cppi_channel *rx, + void __iomem *tibase, int onepacket, unsigned n_bds) +{ + u32 val; + +#ifdef RNDIS_RX_IS_USABLE + u32 tmp; + /* assert(is_host_active(musb)) */ + + /* start from "AutoReq never" */ + tmp = musb_readl(tibase, DAVINCI_AUTOREQ_REG); + val = tmp & ~((0x3) << (rx->index * 2)); + + /* HCD arranged reqpkt for packet #1. we arrange int + * for all but the last one, maybe in two segments. + */ + if (!onepacket) { +#if 0 + /* use two segments, autoreq "all" then the last "never" */ + val |= ((0x3) << (rx->index * 2)); + n_bds--; +#else + /* one segment, autoreq "all-but-last" */ + val |= ((0x1) << (rx->index * 2)); +#endif + } + + if (val != tmp) { + int n = 100; + + /* make sure that autoreq is updated before continuing */ + musb_writel(tibase, DAVINCI_AUTOREQ_REG, val); + do { + tmp = musb_readl(tibase, DAVINCI_AUTOREQ_REG); + if (tmp == val) + break; + cpu_relax(); + } while (n-- > 0); + } +#endif + + /* REQPKT is turned off after each segment */ + if (n_bds && rx->channel.actual_len) { + void __iomem *regs = rx->hw_ep->regs; + + val = musb_readw(regs, MUSB_RXCSR); + if (!(val & MUSB_RXCSR_H_REQPKT)) { + val |= MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_H_WZC_BITS; + musb_writew(regs, MUSB_RXCSR, val); + /* flush writebufer */ + val = musb_readw(regs, MUSB_RXCSR); + } + } + return n_bds; +} + + +/* Buffer enqueuing Logic: + * + * - RX builds new queues each time, to help handle routine "early + * termination" cases (faults, including errors and short reads) + * more correctly. + * + * - for now, TX reuses the same queue of BDs every time + * + * REVISIT long term, we want a normal dynamic model. + * ... the goal will be to append to the + * existing queue, processing completed "dma buffers" (segments) on the fly. + * + * Otherwise we force an IRQ latency between requests, which slows us a lot + * (especially in "transparent" dma). Unfortunately that model seems to be + * inherent in the DMA model from the Mentor code, except in the rare case + * of transfers big enough (~128+ KB) that we could append "middle" segments + * in the TX paths. (RX can't do this, see below.) + * + * That's true even in the CPPI- friendly iso case, where most urbs have + * several small segments provided in a group and where the "packet at a time" + * "transparent" DMA model is always correct, even on the RX side. + */ + +/* + * CPPI TX: + * ======== + * TX is a lot more reasonable than RX; it doesn't need to run in + * irq-per-packet mode very often. RNDIS mode seems to behave too + * (except how it handles the exactly-N-packets case). Building a + * txdma queue with multiple requests (urb or usb_request) looks + * like it would work ... but fault handling would need much testing. + * + * The main issue with TX mode RNDIS relates to transfer lengths that + * are an exact multiple of the packet length. It appears that there's + * a hiccup in that case (maybe the DMA completes before the ZLP gets + * written?) boiling down to not being able to rely on CPPI writing any + * terminating zero length packet before the next transfer is written. + * So that's punted to PIO; better yet, gadget drivers can avoid it. + * + * Plus, there's allegedly an undocumented constraint that rndis transfer + * length be a multiple of 64 bytes ... but the chip doesn't act that + * way, and we really don't _want_ that behavior anyway. + * + * On TX, "transparent" mode works ... although experiments have shown + * problems trying to use the SOP/EOP bits in different USB packets. + * + * REVISIT try to handle terminating zero length packets using CPPI + * instead of doing it by PIO after an IRQ. (Meanwhile, make Ethernet + * links avoid that issue by forcing them to avoid zlps.) + */ +static void +cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx) +{ + unsigned maxpacket = tx->maxpacket; + dma_addr_t addr = tx->buf_dma + tx->offset; + size_t length = tx->buf_len - tx->offset; + struct cppi_descriptor *bd; + unsigned n_bds; + unsigned i; + struct cppi_tx_stateram __iomem *tx_ram = tx->state_ram; + int rndis; + + /* TX can use the CPPI "rndis" mode, where we can probably fit this + * transfer in one BD and one IRQ. The only time we would NOT want + * to use it is when hardware constraints prevent it, or if we'd + * trigger the "send a ZLP?" confusion. + */ + rndis = (maxpacket & 0x3f) == 0 + && length < 0xffff + && (length % maxpacket) != 0; + + if (rndis) { + maxpacket = length; + n_bds = 1; + } else { + n_bds = length / maxpacket; + if (!length || (length % maxpacket)) + n_bds++; + n_bds = min(n_bds, (unsigned) NUM_TXCHAN_BD); + length = min(n_bds * maxpacket, length); + } + + DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%x len %u\n", + tx->index, + maxpacket, + rndis ? "rndis" : "transparent", + n_bds, + addr, length); + + cppi_rndis_update(tx, 0, musb->ctrl_base, rndis); + + /* assuming here that channel_program is called during + * transfer initiation ... current code maintains state + * for one outstanding request only (no queues, not even + * the implicit ones of an iso urb). + */ + + bd = tx->freelist; + tx->head = bd; + tx->last_processed = NULL; + + /* FIXME use BD pool like RX side does, and just queue + * the minimum number for this request. + */ + + /* Prepare queue of BDs first, then hand it to hardware. + * All BDs except maybe the last should be of full packet + * size; for RNDIS there _is_ only that last packet. + */ + for (i = 0; i < n_bds; ) { + if (++i < n_bds && bd->next) + bd->hw_next = bd->next->dma; + else + bd->hw_next = 0; + + bd->hw_bufp = tx->buf_dma + tx->offset; + + /* FIXME set EOP only on the last packet, + * SOP only on the first ... avoid IRQs + */ + if ((tx->offset + maxpacket) <= tx->buf_len) { + tx->offset += maxpacket; + bd->hw_off_len = maxpacket; + bd->hw_options = CPPI_SOP_SET | CPPI_EOP_SET + | CPPI_OWN_SET | maxpacket; + } else { + /* only this one may be a partial USB Packet */ + u32 partial_len; + + partial_len = tx->buf_len - tx->offset; + tx->offset = tx->buf_len; + bd->hw_off_len = partial_len; + + bd->hw_options = CPPI_SOP_SET | CPPI_EOP_SET + | CPPI_OWN_SET | partial_len; + if (partial_len == 0) + bd->hw_options |= CPPI_ZERO_SET; + } + + DBG(5, "TXBD %p: nxt %08x buf %08x len %04x opt %08x\n", + bd, bd->hw_next, bd->hw_bufp, + bd->hw_off_len, bd->hw_options); + + /* update the last BD enqueued to the list */ + tx->tail = bd; + bd = bd->next; + } + + /* BDs live in DMA-coherent memory, but writes might be pending */ + cpu_drain_writebuffer(); + + /* Write to the HeadPtr in state RAM to trigger */ + musb_writel(&tx_ram->tx_head, 0, (u32)tx->freelist->dma); + + cppi_dump_tx(5, tx, "/S"); +} + +/* + * CPPI RX Woes: + * ============= + * Consider a 1KB bulk RX buffer in two scenarios: (a) it's fed two 300 byte + * packets back-to-back, and (b) it's fed two 512 byte packets back-to-back. + * (Full speed transfers have similar scenarios.) + * + * The correct behavior for Linux is that (a) fills the buffer with 300 bytes, + * and the next packet goes into a buffer that's queued later; while (b) fills + * the buffer with 1024 bytes. How to do that with CPPI? + * + * - RX queues in "rndis" mode -- one single BD -- handle (a) correctly, but + * (b) loses **BADLY** because nothing (!) happens when that second packet + * fills the buffer, much less when a third one arrives. (Which makes this + * not a "true" RNDIS mode. In the RNDIS protocol short-packet termination + * is optional, and it's fine if peripherals -- not hosts! -- pad messages + * out to end-of-buffer. Standard PCI host controller DMA descriptors + * implement that mode by default ... which is no accident.) + * + * - RX queues in "transparent" mode -- two BDs with 512 bytes each -- have + * converse problems: (b) is handled right, but (a) loses badly. CPPI RX + * ignores SOP/EOP markings and processes both of those BDs; so both packets + * are loaded into the buffer (with a 212 byte gap between them), and the next + * buffer queued will NOT get its 300 bytes of data. (It seems like SOP/EOP + * are intended as outputs for RX queues, not inputs...) + * + * - A variant of "transparent" mode -- one BD at a time -- is the only way to + * reliably make both cases work, with software handling both cases correctly + * and at the significant penalty of needing an IRQ per packet. (The lack of + * I/O overlap can be slightly ameliorated by enabling double buffering.) + * + * So how to get rid of IRQ-per-packet? The transparent multi-BD case could + * be used in special cases like mass storage, which sets URB_SHORT_NOT_OK + * (or maybe its peripheral side counterpart) to flag (a) scenarios as errors + * with guaranteed driver level fault recovery and scrubbing out what's left + * of that garbaged datastream. + * + * But there seems to be no way to identify the cases where CPPI RNDIS mode + * is appropriate -- which do NOT include RNDIS host drivers, but do include + * the CDC Ethernet driver! -- and the documentation is incomplete/wrong. + * So we can't _ever_ use RX RNDIS mode ... except by using a heuristic + * that applies best on the peripheral side (and which could fail rudely). + * + * Leaving only "transparent" mode; we avoid multi-bd modes in almost all + * cases other than mass storage class. Otherwise we're correct but slow, + * since CPPI penalizes our need for a "true RNDIS" default mode. + */ + + +/* Heuristic, intended to kick in for ethernet/rndis peripheral ONLY + * + * IFF + * (a) peripheral mode ... since rndis peripherals could pad their + * writes to hosts, causing i/o failure; or we'd have to cope with + * a largely unknowable variety of host side protocol variants + * (b) and short reads are NOT errors ... since full reads would + * cause those same i/o failures + * (c) and read length is + * - less than 64KB (max per cppi descriptor) + * - not a multiple of 4096 (g_zero default, full reads typical) + * - N (>1) packets long, ditto (full reads not EXPECTED) + * THEN + * try rx rndis mode + * + * Cost of heuristic failing: RXDMA wedges at the end of transfers that + * fill out the whole buffer. Buggy host side usb network drivers could + * trigger that, but "in the field" such bugs seem to be all but unknown. + * + * So this module parameter lets the heuristic be disabled. When using + * gadgetfs, the heuristic will probably need to be disabled. + */ +static int cppi_rx_rndis = 1; + +module_param(cppi_rx_rndis, bool, 0); +MODULE_PARM_DESC(cppi_rx_rndis, "enable/disable RX RNDIS heuristic"); + + +/** + * cppi_next_rx_segment - dma read for the next chunk of a buffer + * @musb: the controller + * @rx: dma channel + * @onepacket: true unless caller treats short reads as errors, and + * performs fault recovery above usbcore. + * Context: controller irqlocked + * + * See above notes about why we can't use multi-BD RX queues except in + * rare cases (mass storage class), and can never use the hardware "rndis" + * mode (since it's not a "true" RNDIS mode) with complete safety.. + * + * It's ESSENTIAL that callers specify "onepacket" mode unless they kick in + * code to recover from corrupted datastreams after each short transfer. + */ +static void +cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) +{ + unsigned maxpacket = rx->maxpacket; + dma_addr_t addr = rx->buf_dma + rx->offset; + size_t length = rx->buf_len - rx->offset; + struct cppi_descriptor *bd, *tail; + unsigned n_bds; + unsigned i; + void __iomem *tibase = musb->ctrl_base; + int is_rndis = 0; + struct cppi_rx_stateram __iomem *rx_ram = rx->state_ram; + + if (onepacket) { + /* almost every USB driver, host or peripheral side */ + n_bds = 1; + + /* maybe apply the heuristic above */ + if (cppi_rx_rndis + && is_peripheral_active(musb) + && length > maxpacket + && (length & ~0xffff) == 0 + && (length & 0x0fff) != 0 + && (length & (maxpacket - 1)) == 0) { + maxpacket = length; + is_rndis = 1; + } + } else { + /* virtually nothing except mass storage class */ + if (length > 0xffff) { + n_bds = 0xffff / maxpacket; + length = n_bds * maxpacket; + } else { + n_bds = length / maxpacket; + if (length % maxpacket) + n_bds++; + } + if (n_bds == 1) + onepacket = 1; + else + n_bds = min(n_bds, (unsigned) NUM_RXCHAN_BD); + } + + /* In host mode, autorequest logic can generate some IN tokens; it's + * tricky since we can't leave REQPKT set in RXCSR after the transfer + * finishes. So: multipacket transfers involve two or more segments. + * And always at least two IRQs ... RNDIS mode is not an option. + */ + if (is_host_active(musb)) + n_bds = cppi_autoreq_update(rx, tibase, onepacket, n_bds); + + cppi_rndis_update(rx, 1, musb->ctrl_base, is_rndis); + + length = min(n_bds * maxpacket, length); + + DBG(4, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) " + "dma 0x%x len %u %u/%u\n", + rx->index, maxpacket, + onepacket + ? (is_rndis ? "rndis" : "onepacket") + : "multipacket", + n_bds, + musb_readl(tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4)) + & 0xffff, + addr, length, rx->channel.actual_len, rx->buf_len); + + /* only queue one segment at a time, since the hardware prevents + * correct queue shutdown after unexpected short packets + */ + bd = cppi_bd_alloc(rx); + rx->head = bd; + + /* Build BDs for all packets in this segment */ + for (i = 0, tail = NULL; bd && i < n_bds; i++, tail = bd) { + u32 bd_len; + + if (i) { + bd = cppi_bd_alloc(rx); + if (!bd) + break; + tail->next = bd; + tail->hw_next = bd->dma; + } + bd->hw_next = 0; + + /* all but the last packet will be maxpacket size */ + if (maxpacket < length) + bd_len = maxpacket; + else + bd_len = length; + + bd->hw_bufp = addr; + addr += bd_len; + rx->offset += bd_len; + + bd->hw_off_len = (0 /*offset*/ << 16) + bd_len; + bd->buflen = bd_len; + + bd->hw_options = CPPI_OWN_SET | (i == 0 ? length : 0); + length -= bd_len; + } + + /* we always expect at least one reusable BD! */ + if (!tail) { + WARNING("rx dma%d -- no BDs? need %d\n", rx->index, n_bds); + return; + } else if (i < n_bds) + WARNING("rx dma%d -- only %d of %d BDs\n", rx->index, i, n_bds); + + tail->next = NULL; + tail->hw_next = 0; + + bd = rx->head; + rx->tail = tail; + + /* short reads and other faults should terminate this entire + * dma segment. we want one "dma packet" per dma segment, not + * one per USB packet, terminating the whole queue at once... + * NOTE that current hardware seems to ignore SOP and EOP. + */ + bd->hw_options |= CPPI_SOP_SET; + tail->hw_options |= CPPI_EOP_SET; + + if (debug >= 5) { + struct cppi_descriptor *d; + + for (d = rx->head; d; d = d->next) + cppi_dump_rxbd("S", d); + } + + /* in case the preceding transfer left some state... */ + tail = rx->last_processed; + if (tail) { + tail->next = bd; + tail->hw_next = bd->dma; + } + + core_rxirq_enable(tibase, rx->index + 1); + + /* BDs live in DMA-coherent memory, but writes might be pending */ + cpu_drain_writebuffer(); + + /* REVISIT specs say to write this AFTER the BUFCNT register + * below ... but that loses badly. + */ + musb_writel(&rx_ram->rx_head, 0, bd->dma); + + /* bufferCount must be at least 3, and zeroes on completion + * unless it underflows below zero, or stops at two, or keeps + * growing ... grr. + */ + i = musb_readl(tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4)) + & 0xffff; + + if (!i) + musb_writel(tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4), + n_bds + 2); + else if (n_bds > (i - 3)) + musb_writel(tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4), + n_bds - (i - 3)); + + i = musb_readl(tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4)) + & 0xffff; + if (i < (2 + n_bds)) { + DBG(2, "bufcnt%d underrun - %d (for %d)\n", + rx->index, i, n_bds); + musb_writel(tibase, + DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4), + n_bds + 2); + } + + cppi_dump_rx(4, rx, "/S"); +} + +/** + * cppi_channel_program - program channel for data transfer + * @ch: the channel + * @maxpacket: max packet size + * @mode: For RX, 1 unless the usb protocol driver promised to treat + * all short reads as errors and kick in high level fault recovery. + * For TX, ignored because of RNDIS mode races/glitches. + * @dma_addr: dma address of buffer + * @len: length of buffer + * Context: controller irqlocked + */ +static int cppi_channel_program(struct dma_channel *ch, + u16 maxpacket, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct cppi_channel *cppi_ch; + struct cppi *controller; + struct musb *musb; + + cppi_ch = container_of(ch, struct cppi_channel, channel); + controller = cppi_ch->controller; + musb = controller->musb; + + switch (ch->status) { + case MUSB_DMA_STATUS_BUS_ABORT: + case MUSB_DMA_STATUS_CORE_ABORT: + /* fault irq handler should have handled cleanup */ + WARNING("%cX DMA%d not cleaned up after abort!\n", + cppi_ch->transmit ? 'T' : 'R', + cppi_ch->index); + /* WARN_ON(1); */ + break; + case MUSB_DMA_STATUS_BUSY: + WARNING("program active channel? %cX DMA%d\n", + cppi_ch->transmit ? 'T' : 'R', + cppi_ch->index); + /* WARN_ON(1); */ + break; + case MUSB_DMA_STATUS_UNKNOWN: + DBG(1, "%cX DMA%d not allocated!\n", + cppi_ch->transmit ? 'T' : 'R', + cppi_ch->index); + /* FALLTHROUGH */ + case MUSB_DMA_STATUS_FREE: + break; + } + + ch->status = MUSB_DMA_STATUS_BUSY; + + /* set transfer parameters, then queue up its first segment */ + cppi_ch->buf_dma = dma_addr; + cppi_ch->offset = 0; + cppi_ch->maxpacket = maxpacket; + cppi_ch->buf_len = len; + + /* TX channel? or RX? */ + if (cppi_ch->transmit) + cppi_next_tx_segment(musb, cppi_ch); + else + cppi_next_rx_segment(musb, cppi_ch, mode); + + return true; +} + +static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) +{ + struct cppi_channel *rx = &cppi->rx[ch]; + struct cppi_rx_stateram __iomem *state = rx->state_ram; + struct cppi_descriptor *bd; + struct cppi_descriptor *last = rx->last_processed; + bool completed = false; + bool acked = false; + int i; + dma_addr_t safe2ack; + void __iomem *regs = rx->hw_ep->regs; + + cppi_dump_rx(6, rx, "/K"); + + bd = last ? last->next : rx->head; + if (!bd) + return false; + + /* run through all completed BDs */ + for (i = 0, safe2ack = musb_readl(&state->rx_complete, 0); + (safe2ack || completed) && bd && i < NUM_RXCHAN_BD; + i++, bd = bd->next) { + u16 len; + + /* catch latest BD writes from CPPI */ + rmb(); + if (!completed && (bd->hw_options & CPPI_OWN_SET)) + break; + + DBG(5, "C/RXBD %08x: nxt %08x buf %08x " + "off.len %08x opt.len %08x (%d)\n", + bd->dma, bd->hw_next, bd->hw_bufp, + bd->hw_off_len, bd->hw_options, + rx->channel.actual_len); + + /* actual packet received length */ + if ((bd->hw_options & CPPI_SOP_SET) && !completed) + len = bd->hw_off_len & CPPI_RECV_PKTLEN_MASK; + else + len = 0; + + if (bd->hw_options & CPPI_EOQ_MASK) + completed = true; + + if (!completed && len < bd->buflen) { + /* NOTE: when we get a short packet, RXCSR_H_REQPKT + * must have been cleared, and no more DMA packets may + * active be in the queue... TI docs didn't say, but + * CPPI ignores those BDs even though OWN is still set. + */ + completed = true; + DBG(3, "rx short %d/%d (%d)\n", + len, bd->buflen, + rx->channel.actual_len); + } + + /* If we got here, we expect to ack at least one BD; meanwhile + * CPPI may completing other BDs while we scan this list... + * + * RACE: we can notice OWN cleared before CPPI raises the + * matching irq by writing that BD as the completion pointer. + * In such cases, stop scanning and wait for the irq, avoiding + * lost acks and states where BD ownership is unclear. + */ + if (bd->dma == safe2ack) { + musb_writel(&state->rx_complete, 0, safe2ack); + safe2ack = musb_readl(&state->rx_complete, 0); + acked = true; + if (bd->dma == safe2ack) + safe2ack = 0; + } + + rx->channel.actual_len += len; + + cppi_bd_free(rx, last); + last = bd; + + /* stop scanning on end-of-segment */ + if (bd->hw_next == 0) + completed = true; + } + rx->last_processed = last; + + /* dma abort, lost ack, or ... */ + if (!acked && last) { + int csr; + + if (safe2ack == 0 || safe2ack == rx->last_processed->dma) + musb_writel(&state->rx_complete, 0, safe2ack); + if (safe2ack == 0) { + cppi_bd_free(rx, last); + rx->last_processed = NULL; + + /* if we land here on the host side, H_REQPKT will + * be clear and we need to restart the queue... + */ + WARN_ON(rx->head); + } + musb_ep_select(cppi->mregs, rx->index + 1); + csr = musb_readw(regs, MUSB_RXCSR); + if (csr & MUSB_RXCSR_DMAENAB) { + DBG(4, "list%d %p/%p, last %08x%s, csr %04x\n", + rx->index, + rx->head, rx->tail, + rx->last_processed + ? rx->last_processed->dma + : 0, + completed ? ", completed" : "", + csr); + cppi_dump_rxq(4, "/what?", rx); + } + } + if (!completed) { + int csr; + + rx->head = bd; + + /* REVISIT seems like "autoreq all but EOP" doesn't... + * setting it here "should" be racey, but seems to work + */ + csr = musb_readw(rx->hw_ep->regs, MUSB_RXCSR); + if (is_host_active(cppi->musb) + && bd + && !(csr & MUSB_RXCSR_H_REQPKT)) { + csr |= MUSB_RXCSR_H_REQPKT; + musb_writew(regs, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | csr); + csr = musb_readw(rx->hw_ep->regs, MUSB_RXCSR); + } + } else { + rx->head = NULL; + rx->tail = NULL; + } + + cppi_dump_rx(6, rx, completed ? "/completed" : "/cleaned"); + return completed; +} + +void cppi_completion(struct musb *musb, u32 rx, u32 tx) +{ + void __iomem *tibase; + int i, index; + struct cppi *cppi; + struct musb_hw_ep *hw_ep = NULL; + + cppi = container_of(musb->dma_controller, struct cppi, controller); + + tibase = musb->ctrl_base; + + /* process TX channels */ + for (index = 0; tx; tx = tx >> 1, index++) { + struct cppi_channel *tx_ch; + struct cppi_tx_stateram __iomem *tx_ram; + bool completed = false; + struct cppi_descriptor *bd; + + if (!(tx & 1)) + continue; + + tx_ch = cppi->tx + index; + tx_ram = tx_ch->state_ram; + + /* FIXME need a cppi_tx_scan() routine, which + * can also be called from abort code + */ + + cppi_dump_tx(5, tx_ch, "/E"); + + bd = tx_ch->head; + + if (NULL == bd) { + DBG(1, "null BD\n"); + continue; + } + + /* run through all completed BDs */ + for (i = 0; !completed && bd && i < NUM_TXCHAN_BD; + i++, bd = bd->next) { + u16 len; + + /* catch latest BD writes from CPPI */ + rmb(); + if (bd->hw_options & CPPI_OWN_SET) + break; + + DBG(5, "C/TXBD %p n %x b %x off %x opt %x\n", + bd, bd->hw_next, bd->hw_bufp, + bd->hw_off_len, bd->hw_options); + + len = bd->hw_off_len & CPPI_BUFFER_LEN_MASK; + tx_ch->channel.actual_len += len; + + tx_ch->last_processed = bd; + + /* write completion register to acknowledge + * processing of completed BDs, and possibly + * release the IRQ; EOQ might not be set ... + * + * REVISIT use the same ack strategy as rx + * + * REVISIT have observed bit 18 set; huh?? + */ + /* if ((bd->hw_options & CPPI_EOQ_MASK)) */ + musb_writel(&tx_ram->tx_complete, 0, bd->dma); + + /* stop scanning on end-of-segment */ + if (bd->hw_next == 0) + completed = true; + } + + /* on end of segment, maybe go to next one */ + if (completed) { + /* cppi_dump_tx(4, tx_ch, "/complete"); */ + + /* transfer more, or report completion */ + if (tx_ch->offset >= tx_ch->buf_len) { + tx_ch->head = NULL; + tx_ch->tail = NULL; + tx_ch->channel.status = MUSB_DMA_STATUS_FREE; + + hw_ep = tx_ch->hw_ep; + + /* Peripheral role never repurposes the + * endpoint, so immediate completion is + * safe. Host role waits for the fifo + * to empty (TXPKTRDY irq) before going + * to the next queued bulk transfer. + */ + if (is_host_active(cppi->musb)) { +#if 0 + /* WORKAROUND because we may + * not always get TXKPTRDY ... + */ + int csr; + + csr = musb_readw(hw_ep->regs, + MUSB_TXCSR); + if (csr & MUSB_TXCSR_TXPKTRDY) +#endif + completed = false; + } + if (completed) + musb_dma_completion(musb, index + 1, 1); + + } else { + /* Bigger transfer than we could fit in + * that first batch of descriptors... + */ + cppi_next_tx_segment(musb, tx_ch); + } + } else + tx_ch->head = bd; + } + + /* Start processing the RX block */ + for (index = 0; rx; rx = rx >> 1, index++) { + + if (rx & 1) { + struct cppi_channel *rx_ch; + + rx_ch = cppi->rx + index; + + /* let incomplete dma segments finish */ + if (!cppi_rx_scan(cppi, index)) + continue; + + /* start another dma segment if needed */ + if (rx_ch->channel.actual_len != rx_ch->buf_len + && rx_ch->channel.actual_len + == rx_ch->offset) { + cppi_next_rx_segment(musb, rx_ch, 1); + continue; + } + + /* all segments completed! */ + rx_ch->channel.status = MUSB_DMA_STATUS_FREE; + + hw_ep = rx_ch->hw_ep; + + core_rxirq_disable(tibase, index + 1); + musb_dma_completion(musb, index + 1, 0); + } + } + + /* write to CPPI EOI register to re-enable interrupts */ + musb_writel(tibase, DAVINCI_CPPI_EOI_REG, 0); +} + +/* Instantiate a software object representing a DMA controller. */ +struct dma_controller *__init +dma_controller_create(struct musb *musb, void __iomem *mregs) +{ + struct cppi *controller; + + controller = kzalloc(sizeof *controller, GFP_KERNEL); + if (!controller) + return NULL; + + controller->mregs = mregs; + controller->tibase = mregs - DAVINCI_BASE_OFFSET; + + controller->musb = musb; + controller->controller.start = cppi_controller_start; + controller->controller.stop = cppi_controller_stop; + controller->controller.channel_alloc = cppi_channel_allocate; + controller->controller.channel_release = cppi_channel_release; + controller->controller.channel_program = cppi_channel_program; + controller->controller.channel_abort = cppi_channel_abort; + + /* NOTE: allocating from on-chip SRAM would give the least + * contention for memory access, if that ever matters here. + */ + + /* setup BufferPool */ + controller->pool = dma_pool_create("cppi", + controller->musb->controller, + sizeof(struct cppi_descriptor), + CPPI_DESCRIPTOR_ALIGN, 0); + if (!controller->pool) { + kfree(controller); + return NULL; + } + + return &controller->controller; +} + +/* + * Destroy a previously-instantiated DMA controller. + */ +void dma_controller_destroy(struct dma_controller *c) +{ + struct cppi *cppi; + + cppi = container_of(c, struct cppi, controller); + + /* assert: caller stopped the controller first */ + dma_pool_destroy(cppi->pool); + + kfree(cppi); +} + +/* + * Context: controller irqlocked, endpoint selected + */ +static int cppi_channel_abort(struct dma_channel *channel) +{ + struct cppi_channel *cppi_ch; + struct cppi *controller; + void __iomem *mbase; + void __iomem *tibase; + void __iomem *regs; + u32 value; + struct cppi_descriptor *queue; + + cppi_ch = container_of(channel, struct cppi_channel, channel); + + controller = cppi_ch->controller; + + switch (channel->status) { + case MUSB_DMA_STATUS_BUS_ABORT: + case MUSB_DMA_STATUS_CORE_ABORT: + /* from RX or TX fault irq handler */ + case MUSB_DMA_STATUS_BUSY: + /* the hardware needs shutting down */ + regs = cppi_ch->hw_ep->regs; + break; + case MUSB_DMA_STATUS_UNKNOWN: + case MUSB_DMA_STATUS_FREE: + return 0; + default: + return -EINVAL; + } + + if (!cppi_ch->transmit && cppi_ch->head) + cppi_dump_rxq(3, "/abort", cppi_ch); + + mbase = controller->mregs; + tibase = controller->tibase; + + queue = cppi_ch->head; + cppi_ch->head = NULL; + cppi_ch->tail = NULL; + + /* REVISIT should rely on caller having done this, + * and caller should rely on us not changing it. + * peripheral code is safe ... check host too. + */ + musb_ep_select(mbase, cppi_ch->index + 1); + + if (cppi_ch->transmit) { + struct cppi_tx_stateram __iomem *tx_ram; + int enabled; + + /* mask interrupts raised to signal teardown complete. */ + enabled = musb_readl(tibase, DAVINCI_TXCPPI_INTENAB_REG) + & (1 << cppi_ch->index); + if (enabled) + musb_writel(tibase, DAVINCI_TXCPPI_INTCLR_REG, + (1 << cppi_ch->index)); + + /* REVISIT put timeouts on these controller handshakes */ + + cppi_dump_tx(6, cppi_ch, " (teardown)"); + + /* teardown DMA engine then usb core */ + do { + value = musb_readl(tibase, DAVINCI_TXCPPI_TEAR_REG); + } while (!(value & CPPI_TEAR_READY)); + musb_writel(tibase, DAVINCI_TXCPPI_TEAR_REG, cppi_ch->index); + + tx_ram = cppi_ch->state_ram; + do { + value = musb_readl(&tx_ram->tx_complete, 0); + } while (0xFFFFFFFC != value); + musb_writel(&tx_ram->tx_complete, 0, 0xFFFFFFFC); + + /* FIXME clean up the transfer state ... here? + * the completion routine should get called with + * an appropriate status code. + */ + + value = musb_readw(regs, MUSB_TXCSR); + value &= ~MUSB_TXCSR_DMAENAB; + value |= MUSB_TXCSR_FLUSHFIFO; + musb_writew(regs, MUSB_TXCSR, value); + musb_writew(regs, MUSB_TXCSR, value); + + /* re-enable interrupt */ + if (enabled) + musb_writel(tibase, DAVINCI_TXCPPI_INTENAB_REG, + (1 << cppi_ch->index)); + + /* While we scrub the TX state RAM, ensure that we clean + * up any interrupt that's currently asserted: + * 1. Write to completion Ptr value 0x1(bit 0 set) + * (write back mode) + * 2. Write to completion Ptr value 0x0(bit 0 cleared) + * (compare mode) + * Value written is compared(for bits 31:2) and when + * equal, interrupt is deasserted. + */ + cppi_reset_tx(tx_ram, 1); + musb_writel(&tx_ram->tx_complete, 0, 0); + + cppi_dump_tx(5, cppi_ch, " (done teardown)"); + + /* REVISIT tx side _should_ clean up the same way + * as the RX side ... this does no cleanup at all! + */ + + } else /* RX */ { + u16 csr; + + /* NOTE: docs don't guarantee any of this works ... we + * expect that if the usb core stops telling the cppi core + * to pull more data from it, then it'll be safe to flush + * current RX DMA state iff any pending fifo transfer is done. + */ + + core_rxirq_disable(tibase, cppi_ch->index + 1); + + /* for host, ensure ReqPkt is never set again */ + if (is_host_active(cppi_ch->controller->musb)) { + value = musb_readl(tibase, DAVINCI_AUTOREQ_REG); + value &= ~((0x3) << (cppi_ch->index * 2)); + musb_writel(tibase, DAVINCI_AUTOREQ_REG, value); + } + + csr = musb_readw(regs, MUSB_RXCSR); + + /* for host, clear (just) ReqPkt at end of current packet(s) */ + if (is_host_active(cppi_ch->controller->musb)) { + csr |= MUSB_RXCSR_H_WZC_BITS; + csr &= ~MUSB_RXCSR_H_REQPKT; + } else + csr |= MUSB_RXCSR_P_WZC_BITS; + + /* clear dma enable */ + csr &= ~(MUSB_RXCSR_DMAENAB); + musb_writew(regs, MUSB_RXCSR, csr); + csr = musb_readw(regs, MUSB_RXCSR); + + /* Quiesce: wait for current dma to finish (if not cleanup). + * We can't use bit zero of stateram->rx_sop, since that + * refers to an entire "DMA packet" not just emptying the + * current fifo. Most segments need multiple usb packets. + */ + if (channel->status == MUSB_DMA_STATUS_BUSY) + udelay(50); + + /* scan the current list, reporting any data that was + * transferred and acking any IRQ + */ + cppi_rx_scan(controller, cppi_ch->index); + + /* clobber the existing state once it's idle + * + * NOTE: arguably, we should also wait for all the other + * RX channels to quiesce (how??) and then temporarily + * disable RXCPPI_CTRL_REG ... but it seems that we can + * rely on the controller restarting from state ram, with + * only RXCPPI_BUFCNT state being bogus. BUFCNT will + * correct itself after the next DMA transfer though. + * + * REVISIT does using rndis mode change that? + */ + cppi_reset_rx(cppi_ch->state_ram); + + /* next DMA request _should_ load cppi head ptr */ + + /* ... we don't "free" that list, only mutate it in place. */ + cppi_dump_rx(5, cppi_ch, " (done abort)"); + + /* clean up previously pending bds */ + cppi_bd_free(cppi_ch, cppi_ch->last_processed); + cppi_ch->last_processed = NULL; + + while (queue) { + struct cppi_descriptor *tmp = queue->next; + + cppi_bd_free(cppi_ch, queue); + queue = tmp; + } + } + + channel->status = MUSB_DMA_STATUS_FREE; + cppi_ch->buf_dma = 0; + cppi_ch->offset = 0; + cppi_ch->buf_len = 0; + cppi_ch->maxpacket = 0; + return 0; +} + +/* TBD Queries: + * + * Power Management ... probably turn off cppi during suspend, restart; + * check state ram? Clocking is presumably shared with usb core. + */ diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h new file mode 100644 index 000000000000..fc5216b5d2c5 --- /dev/null +++ b/drivers/usb/musb/cppi_dma.h @@ -0,0 +1,133 @@ +/* Copyright (C) 2005-2006 by Texas Instruments */ + +#ifndef _CPPI_DMA_H_ +#define _CPPI_DMA_H_ + +#include +#include +#include +#include +#include + +#include "musb_dma.h" +#include "musb_core.h" + + +/* FIXME fully isolate CPPI from DaVinci ... the "CPPI generic" registers + * would seem to be shared with the TUSB6020 (over VLYNQ). + */ + +#include "davinci.h" + + +/* CPPI RX/TX state RAM */ + +struct cppi_tx_stateram { + u32 tx_head; /* "DMA packet" head descriptor */ + u32 tx_buf; + u32 tx_current; /* current descriptor */ + u32 tx_buf_current; + u32 tx_info; /* flags, remaining buflen */ + u32 tx_rem_len; + u32 tx_dummy; /* unused */ + u32 tx_complete; +}; + +struct cppi_rx_stateram { + u32 rx_skipbytes; + u32 rx_head; + u32 rx_sop; /* "DMA packet" head descriptor */ + u32 rx_current; /* current descriptor */ + u32 rx_buf_current; + u32 rx_len_len; + u32 rx_cnt_cnt; + u32 rx_complete; +}; + +/* hw_options bits in CPPI buffer descriptors */ +#define CPPI_SOP_SET ((u32)(1 << 31)) +#define CPPI_EOP_SET ((u32)(1 << 30)) +#define CPPI_OWN_SET ((u32)(1 << 29)) /* owned by cppi */ +#define CPPI_EOQ_MASK ((u32)(1 << 28)) +#define CPPI_ZERO_SET ((u32)(1 << 23)) /* rx saw zlp; tx issues one */ +#define CPPI_RXABT_MASK ((u32)(1 << 19)) /* need more rx buffers */ + +#define CPPI_RECV_PKTLEN_MASK 0xFFFF +#define CPPI_BUFFER_LEN_MASK 0xFFFF + +#define CPPI_TEAR_READY ((u32)(1 << 31)) + +/* CPPI data structure definitions */ + +#define CPPI_DESCRIPTOR_ALIGN 16 /* bytes; 5-dec docs say 4-byte align */ + +struct cppi_descriptor { + /* hardware overlay */ + u32 hw_next; /* next buffer descriptor Pointer */ + u32 hw_bufp; /* i/o buffer pointer */ + u32 hw_off_len; /* buffer_offset16, buffer_length16 */ + u32 hw_options; /* flags: SOP, EOP etc*/ + + struct cppi_descriptor *next; + dma_addr_t dma; /* address of this descriptor */ + u32 buflen; /* for RX: original buffer length */ +} __attribute__ ((aligned(CPPI_DESCRIPTOR_ALIGN))); + + +struct cppi; + +/* CPPI Channel Control structure */ +struct cppi_channel { + struct dma_channel channel; + + /* back pointer to the DMA controller structure */ + struct cppi *controller; + + /* which direction of which endpoint? */ + struct musb_hw_ep *hw_ep; + bool transmit; + u8 index; + + /* DMA modes: RNDIS or "transparent" */ + u8 is_rndis; + + /* book keeping for current transfer request */ + dma_addr_t buf_dma; + u32 buf_len; + u32 maxpacket; + u32 offset; /* dma requested */ + + void __iomem *state_ram; /* CPPI state */ + + struct cppi_descriptor *freelist; + + /* BD management fields */ + struct cppi_descriptor *head; + struct cppi_descriptor *tail; + struct cppi_descriptor *last_processed; + + /* use tx_complete in host role to track endpoints waiting for + * FIFONOTEMPTY to clear. + */ + struct list_head tx_complete; +}; + +/* CPPI DMA controller object */ +struct cppi { + struct dma_controller controller; + struct musb *musb; + void __iomem *mregs; /* Mentor regs */ + void __iomem *tibase; /* TI/CPPI regs */ + + struct cppi_channel tx[MUSB_C_NUM_EPT - 1]; + struct cppi_channel rx[MUSB_C_NUM_EPR - 1]; + + struct dma_pool *pool; + + struct list_head tx_complete; +}; + +/* irq handling hook */ +extern void cppi_completion(struct musb *, u32 rx, u32 tx); + +#endif /* end of ifndef _CPPI_DMA_H_ */ diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c new file mode 100644 index 000000000000..75baf181a8cd --- /dev/null +++ b/drivers/usb/musb/davinci.c @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux 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. + * + * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "musb_core.h" + +#ifdef CONFIG_MACH_DAVINCI_EVM +#include +#endif + +#include "davinci.h" +#include "cppi_dma.h" + + +/* REVISIT (PM) we should be able to keep the PHY in low power mode most + * of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0 + * and, when in host mode, autosuspending idle root ports... PHYPLLON + * (overriding SUSPENDM?) then likely needs to stay off. + */ + +static inline void phy_on(void) +{ + /* start the on-chip PHY and its PLL */ + __raw_writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON, + (void __force __iomem *) IO_ADDRESS(USBPHY_CTL_PADDR)); + while ((__raw_readl((void __force __iomem *) + IO_ADDRESS(USBPHY_CTL_PADDR)) + & USBPHY_PHYCLKGD) == 0) + cpu_relax(); +} + +static inline void phy_off(void) +{ + /* powerdown the on-chip PHY and its oscillator */ + __raw_writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, (void __force __iomem *) + IO_ADDRESS(USBPHY_CTL_PADDR)); +} + +static int dma_off = 1; + +void musb_platform_enable(struct musb *musb) +{ + u32 tmp, old, val; + + /* workaround: setup irqs through both register sets */ + tmp = (musb->epmask & DAVINCI_USB_TX_ENDPTS_MASK) + << DAVINCI_USB_TXINT_SHIFT; + musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp); + old = tmp; + tmp = (musb->epmask & (0xfffe & DAVINCI_USB_RX_ENDPTS_MASK)) + << DAVINCI_USB_RXINT_SHIFT; + musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp); + tmp |= old; + + val = ~MUSB_INTR_SOF; + tmp |= ((val & 0x01ff) << DAVINCI_USB_USBINT_SHIFT); + musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp); + + if (is_dma_capable() && !dma_off) + printk(KERN_WARNING "%s %s: dma not reactivated\n", + __FILE__, __func__); + else + dma_off = 0; + + /* force a DRVVBUS irq so we can start polling for ID change */ + if (is_otg_enabled(musb)) + musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG, + DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT); +} + +/* + * Disable the HDRC and flush interrupts + */ +void musb_platform_disable(struct musb *musb) +{ + /* because we don't set CTRLR.UINT, "important" to: + * - not read/write INTRUSB/INTRUSBE + * - (except during initial setup, as workaround) + * - use INTSETR/INTCLRR instead + */ + musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_CLR_REG, + DAVINCI_USB_USBINT_MASK + | DAVINCI_USB_TXINT_MASK + | DAVINCI_USB_RXINT_MASK); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_writel(musb->ctrl_base, DAVINCI_USB_EOI_REG, 0); + + if (is_dma_capable() && !dma_off) + WARNING("dma still active\n"); +} + + +/* REVISIT it's not clear whether DaVinci can support full OTG. */ + +static int vbus_state = -1; + +#ifdef CONFIG_USB_MUSB_HDRC_HCD +#define portstate(stmt) stmt +#else +#define portstate(stmt) +#endif + + +/* VBUS SWITCHING IS BOARD-SPECIFIC */ + +#ifdef CONFIG_MACH_DAVINCI_EVM +#ifndef CONFIG_MACH_DAVINCI_EVM_OTG + +/* I2C operations are always synchronous, and require a task context. + * With unloaded systems, using the shared workqueue seems to suffice + * to satisfy the 100msec A_WAIT_VRISE timeout... + */ +static void evm_deferred_drvvbus(struct work_struct *ignored) +{ + davinci_i2c_expander_op(0x3a, USB_DRVVBUS, vbus_state); + vbus_state = !vbus_state; +} +static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus); + +#endif /* modified board */ +#endif /* EVM */ + +static void davinci_source_power(struct musb *musb, int is_on, int immediate) +{ + if (is_on) + is_on = 1; + + if (vbus_state == is_on) + return; + vbus_state = !is_on; /* 0/1 vs "-1 == unknown/init" */ + +#ifdef CONFIG_MACH_DAVINCI_EVM + if (machine_is_davinci_evm()) { +#ifdef CONFIG_MACH_DAVINCI_EVM_OTG + /* modified EVM board switching VBUS with GPIO(6) not I2C + * NOTE: PINMUX0.RGB888 (bit23) must be clear + */ + if (is_on) + gpio_set(GPIO(6)); + else + gpio_clear(GPIO(6)); + immediate = 1; +#else + if (immediate) + davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on); + else + schedule_work(&evm_vbus_work); +#endif + } +#endif + if (immediate) + vbus_state = is_on; +} + +static void davinci_set_vbus(struct musb *musb, int is_on) +{ + WARN_ON(is_on && is_peripheral_active(musb)); + davinci_source_power(musb, is_on, 0); +} + + +#define POLL_SECONDS 2 + +static struct timer_list otg_workaround; + +static void otg_timer(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + void __iomem *mregs = musb->mregs; + u8 devctl; + unsigned long flags; + + /* We poll because DaVinci's won't expose several OTG-critical + * status change events (from the transceiver) otherwise. + */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv.state) { + case OTG_STATE_A_WAIT_VFALL: + /* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL + * seems to mis-handle session "start" otherwise (or in our + * case "recover"), in routine "VBUS was valid by the time + * VBUSERR got reported during enumeration" cases. + */ + if (devctl & MUSB_DEVCTL_VBUS) { + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + break; + } + musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG, + MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT); + break; + case OTG_STATE_B_IDLE: + if (!is_peripheral_enabled(musb)) + break; + + /* There's no ID-changed IRQ, so we have no good way to tell + * when to switch to the A-Default state machine (by setting + * the DEVCTL.SESSION flag). + * + * Workaround: whenever we're in B_IDLE, try setting the + * session flag every few seconds. If it works, ID was + * grounded and we're now in the A-Default state machine. + * + * NOTE setting the session flag is _supposed_ to trigger + * SRP, but clearly it doesn't. + */ + musb_writeb(mregs, MUSB_DEVCTL, + devctl | MUSB_DEVCTL_SESSION); + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + else + musb->xceiv.state = OTG_STATE_A_IDLE; + break; + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +static irqreturn_t davinci_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + void __iomem *tibase = musb->ctrl_base; + u32 tmp; + + spin_lock_irqsave(&musb->lock, flags); + + /* NOTE: DaVinci shadows the Mentor IRQs. Don't manage them through + * the Mentor registers (except for setup), use the TI ones and EOI. + * + * Docs describe irq "vector" registers asociated with the CPPI and + * USB EOI registers. These hold a bitmask corresponding to the + * current IRQ, not an irq handler address. Would using those bits + * resolve some of the races observed in this dispatch code?? + */ + + /* CPPI interrupts share the same IRQ line, but have their own + * mask, state, "vector", and EOI registers. + */ + if (is_cppi_enabled()) { + u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG); + u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG); + + if (cppi_tx || cppi_rx) { + DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx); + cppi_completion(musb, cppi_rx, cppi_tx); + retval = IRQ_HANDLED; + } + } + + /* ack and handle non-CPPI interrupts */ + tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG); + musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp); + DBG(4, "IRQ %08x\n", tmp); + + musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK) + >> DAVINCI_USB_RXINT_SHIFT; + musb->int_tx = (tmp & DAVINCI_USB_TXINT_MASK) + >> DAVINCI_USB_TXINT_SHIFT; + musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK) + >> DAVINCI_USB_USBINT_SHIFT; + + /* DRVVBUS irqs are the only proxy we have (a very poor one!) for + * DaVinci's missing ID change IRQ. We need an ID change IRQ to + * switch appropriately between halves of the OTG state machine. + * Managing DEVCTL.SESSION per Mentor docs requires we know its + * value, but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. + * Also, DRVVBUS pulses for SRP (but not at 5V) ... + */ + if (tmp & (DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT)) { + int drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG); + void __iomem *mregs = musb->mregs; + u8 devctl = musb_readb(mregs, MUSB_DEVCTL); + int err = musb->int_usb & MUSB_INTR_VBUSERROR; + + err = is_host_enabled(musb) + && (musb->int_usb & MUSB_INTR_VBUSERROR); + if (err) { + /* The Mentor core doesn't debounce VBUS as needed + * to cope with device connect current spikes. This + * means it's not uncommon for bus-powered devices + * to get VBUS errors during enumeration. + * + * This is a workaround, but newer RTL from Mentor + * seems to allow a better one: "re"starting sessions + * without waiting (on EVM, a **long** time) for VBUS + * to stop registering in devctl. + */ + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + WARNING("VBUS error workaround (delay coming)\n"); + } else if (is_host_enabled(musb) && drvvbus) { + musb->is_active = 1; + MUSB_HST_MODE(musb); + musb->xceiv.default_a = 1; + musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + portstate(musb->port1_status |= USB_PORT_STAT_POWER); + del_timer(&otg_workaround); + } else { + musb->is_active = 0; + MUSB_DEV_MODE(musb); + musb->xceiv.default_a = 0; + musb->xceiv.state = OTG_STATE_B_IDLE; + portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); + } + + /* NOTE: this must complete poweron within 100 msec */ + davinci_source_power(musb, drvvbus, 0); + DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + drvvbus ? "on" : "off", + otg_state_string(musb), + err ? " ERROR" : "", + devctl); + retval = IRQ_HANDLED; + } + + if (musb->int_tx || musb->int_rx || musb->int_usb) + retval |= musb_interrupt(musb); + + /* irq stays asserted until EOI is written */ + musb_writel(tibase, DAVINCI_USB_EOI_REG, 0); + + /* poll for ID change */ + if (is_otg_enabled(musb) + && musb->xceiv.state == OTG_STATE_B_IDLE) + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + + spin_unlock_irqrestore(&musb->lock, flags); + + /* REVISIT we sometimes get unhandled IRQs + * (e.g. ep0). not clear why... + */ + if (retval != IRQ_HANDLED) + DBG(5, "unhandled? %08x\n", tmp); + return IRQ_HANDLED; +} + +int __init musb_platform_init(struct musb *musb) +{ + void __iomem *tibase = musb->ctrl_base; + u32 revision; + + musb->mregs += DAVINCI_BASE_OFFSET; +#if 0 + /* REVISIT there's something odd about clocking, this + * didn't appear do the job ... + */ + musb->clock = clk_get(pDevice, "usb"); + if (IS_ERR(musb->clock)) + return PTR_ERR(musb->clock); + + status = clk_enable(musb->clock); + if (status < 0) + return -ENODEV; +#endif + + /* returns zero if e.g. not clocked */ + revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG); + if (revision == 0) + return -ENODEV; + + if (is_host_enabled(musb)) + setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); + + musb->board_set_vbus = davinci_set_vbus; + davinci_source_power(musb, 0, 1); + + /* reset the controller */ + musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1); + + /* start the on-chip PHY and its PLL */ + phy_on(); + + msleep(5); + + /* NOTE: irqs are in mixed mode, not bypass to pure-musb */ + pr_debug("DaVinci OTG revision %08x phy %03x control %02x\n", + revision, __raw_readl((void __force __iomem *) + IO_ADDRESS(USBPHY_CTL_PADDR)), + musb_readb(tibase, DAVINCI_USB_CTRL_REG)); + + musb->isr = davinci_interrupt; + return 0; +} + +int musb_platform_exit(struct musb *musb) +{ + if (is_host_enabled(musb)) + del_timer_sync(&otg_workaround); + + davinci_source_power(musb, 0 /*off*/, 1); + + /* delay, to avoid problems with module reload */ + if (is_host_enabled(musb) && musb->xceiv.default_a) { + int maxdelay = 30; + u8 devctl, warn = 0; + + /* if there's no peripheral connected, this can take a + * long time to fall, especially on EVM with huge C133. + */ + do { + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (!(devctl & MUSB_DEVCTL_VBUS)) + break; + if ((devctl & MUSB_DEVCTL_VBUS) != warn) { + warn = devctl & MUSB_DEVCTL_VBUS; + DBG(1, "VBUS %d\n", + warn >> MUSB_DEVCTL_VBUS_SHIFT); + } + msleep(1000); + maxdelay--; + } while (maxdelay > 0); + + /* in OTG mode, another host might be connected */ + if (devctl & MUSB_DEVCTL_VBUS) + DBG(1, "VBUS off timeout (devctl %02x)\n", devctl); + } + + phy_off(); + return 0; +} diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h new file mode 100644 index 000000000000..7fb6238e270f --- /dev/null +++ b/drivers/usb/musb/davinci.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * + * The Inventra Controller Driver for Linux 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 __MUSB_HDRDF_H__ +#define __MUSB_HDRDF_H__ + +/* + * DaVinci-specific definitions + */ + +/* Integrated highspeed/otg PHY */ +#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) +#define USBPHY_PHYCLKGD (1 << 8) +#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ +#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ +#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ +#define USBPHY_CLKO1SEL (1 << 3) +#define USBPHY_OSCPDWN (1 << 2) +#define USBPHY_PHYPDWN (1 << 0) + +/* For now include usb OTG module registers here */ +#define DAVINCI_USB_VERSION_REG 0x00 +#define DAVINCI_USB_CTRL_REG 0x04 +#define DAVINCI_USB_STAT_REG 0x08 +#define DAVINCI_RNDIS_REG 0x10 +#define DAVINCI_AUTOREQ_REG 0x14 +#define DAVINCI_USB_INT_SOURCE_REG 0x20 +#define DAVINCI_USB_INT_SET_REG 0x24 +#define DAVINCI_USB_INT_SRC_CLR_REG 0x28 +#define DAVINCI_USB_INT_MASK_REG 0x2c +#define DAVINCI_USB_INT_MASK_SET_REG 0x30 +#define DAVINCI_USB_INT_MASK_CLR_REG 0x34 +#define DAVINCI_USB_INT_SRC_MASKED_REG 0x38 +#define DAVINCI_USB_EOI_REG 0x3c +#define DAVINCI_USB_EOI_INTVEC 0x40 + +/* BEGIN CPPI-generic (?) */ + +/* CPPI related registers */ +#define DAVINCI_TXCPPI_CTRL_REG 0x80 +#define DAVINCI_TXCPPI_TEAR_REG 0x84 +#define DAVINCI_CPPI_EOI_REG 0x88 +#define DAVINCI_CPPI_INTVEC_REG 0x8c +#define DAVINCI_TXCPPI_MASKED_REG 0x90 +#define DAVINCI_TXCPPI_RAW_REG 0x94 +#define DAVINCI_TXCPPI_INTENAB_REG 0x98 +#define DAVINCI_TXCPPI_INTCLR_REG 0x9c + +#define DAVINCI_RXCPPI_CTRL_REG 0xC0 +#define DAVINCI_RXCPPI_MASKED_REG 0xD0 +#define DAVINCI_RXCPPI_RAW_REG 0xD4 +#define DAVINCI_RXCPPI_INTENAB_REG 0xD8 +#define DAVINCI_RXCPPI_INTCLR_REG 0xDC + +#define DAVINCI_RXCPPI_BUFCNT0_REG 0xE0 +#define DAVINCI_RXCPPI_BUFCNT1_REG 0xE4 +#define DAVINCI_RXCPPI_BUFCNT2_REG 0xE8 +#define DAVINCI_RXCPPI_BUFCNT3_REG 0xEC + +/* CPPI state RAM entries */ +#define DAVINCI_CPPI_STATERAM_BASE_OFFSET 0x100 + +#define DAVINCI_TXCPPI_STATERAM_OFFSET(chnum) \ + (DAVINCI_CPPI_STATERAM_BASE_OFFSET + ((chnum) * 0x40)) +#define DAVINCI_RXCPPI_STATERAM_OFFSET(chnum) \ + (DAVINCI_CPPI_STATERAM_BASE_OFFSET + 0x20 + ((chnum) * 0x40)) + +/* CPPI masks */ +#define DAVINCI_DMA_CTRL_ENABLE 1 +#define DAVINCI_DMA_CTRL_DISABLE 0 + +#define DAVINCI_DMA_ALL_CHANNELS_ENABLE 0xF +#define DAVINCI_DMA_ALL_CHANNELS_DISABLE 0xF + +/* END CPPI-generic (?) */ + +#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ + +#define DAVINCI_USB_USBINT_SHIFT 16 +#define DAVINCI_USB_TXINT_SHIFT 0 +#define DAVINCI_USB_RXINT_SHIFT 8 + +#define DAVINCI_INTR_DRVVBUS 0x0100 + +#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DAVINCI_USB_TXINT_MASK \ + (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) +#define DAVINCI_USB_RXINT_MASK \ + (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) + +#define DAVINCI_BASE_OFFSET 0x400 + +#endif /* __MUSB_HDRDF_H__ */ diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c new file mode 100644 index 000000000000..462586d06da9 --- /dev/null +++ b/drivers/usb/musb/musb_core.c @@ -0,0 +1,2266 @@ +/* + * MUSB OTG driver core code + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * Inventra (Multipoint) Dual-Role Controller Driver for Linux. + * + * This consists of a Host Controller Driver (HCD) and a peripheral + * controller driver implementing the "Gadget" API; OTG support is + * in the works. These are normal Linux-USB controller drivers which + * use IRQs and have no dedicated thread. + * + * This version of the driver has only been used with products from + * Texas Instruments. Those products integrate the Inventra logic + * with other DMA, IRQ, and bus modules, as well as other logic that + * needs to be reflected in this driver. + * + * + * NOTE: the original Mentor code here was pretty much a collection + * of mechanisms that don't seem to have been fully integrated/working + * for *any* Linux kernel version. This version aims at Linux 2.6.now, + * Key open issues include: + * + * - Lack of host-side transaction scheduling, for all transfer types. + * The hardware doesn't do it; instead, software must. + * + * This is not an issue for OTG devices that don't support external + * hubs, but for more "normal" USB hosts it's a user issue that the + * "multipoint" support doesn't scale in the expected ways. That + * includes DaVinci EVM in a common non-OTG mode. + * + * * Control and bulk use dedicated endpoints, and there's as + * yet no mechanism to either (a) reclaim the hardware when + * peripherals are NAKing, which gets complicated with bulk + * endpoints, or (b) use more than a single bulk endpoint in + * each direction. + * + * RESULT: one device may be perceived as blocking another one. + * + * * Interrupt and isochronous will dynamically allocate endpoint + * hardware, but (a) there's no record keeping for bandwidth; + * (b) in the common case that few endpoints are available, there + * is no mechanism to reuse endpoints to talk to multiple devices. + * + * RESULT: At one extreme, bandwidth can be overcommitted in + * some hardware configurations, no faults will be reported. + * At the other extreme, the bandwidth capabilities which do + * exist tend to be severely undercommitted. You can't yet hook + * up both a keyboard and a mouse to an external USB hub. + */ + +/* + * This gets many kinds of configuration information: + * - Kconfig for everything user-configurable + * - for SOC or family details + * - platform_device for addressing, irq, and platform_data + * - platform_data is mostly for board-specific informarion + * + * Most of the conditional compilation will (someday) vanish. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARM +#include +#include +#include +#endif + +#include "musb_core.h" + + +#ifdef CONFIG_ARCH_DAVINCI +#include "davinci.h" +#endif + + + +#if MUSB_DEBUG > 0 +unsigned debug = MUSB_DEBUG; +module_param(debug, uint, 0); +MODULE_PARM_DESC(debug, "initial debug message level"); + +#define MUSB_VERSION_SUFFIX "/dbg" +#endif + +#define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" +#define DRIVER_DESC "Inventra Dual-Role USB Controller Driver" + +#define MUSB_VERSION_BASE "6.0" + +#ifndef MUSB_VERSION_SUFFIX +#define MUSB_VERSION_SUFFIX "" +#endif +#define MUSB_VERSION MUSB_VERSION_BASE MUSB_VERSION_SUFFIX + +#define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION + +#define MUSB_DRIVER_NAME "musb_hdrc" +const char musb_driver_name[] = MUSB_DRIVER_NAME; + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" MUSB_DRIVER_NAME); + + +/*-------------------------------------------------------------------------*/ + +static inline struct musb *dev_to_musb(struct device *dev) +{ +#ifdef CONFIG_USB_MUSB_HDRC_HCD + /* usbcore insists dev->driver_data is a "struct hcd *" */ + return hcd_to_musb(dev_get_drvdata(dev)); +#else + return dev_get_drvdata(dev); +#endif +} + +/*-------------------------------------------------------------------------*/ + +#ifndef CONFIG_USB_TUSB6010 +/* + * Load an endpoint's FIFO + */ +void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) +{ + void __iomem *fifo = hw_ep->fifo; + + prefetch((u8 *)src); + + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + 'T', hw_ep->epnum, fifo, len, src); + + /* we can't assume unaligned reads work */ + if (likely((0x01 & (unsigned long) src) == 0)) { + u16 index = 0; + + /* best case is 32bit-aligned source address */ + if ((0x02 & (unsigned long) src) == 0) { + if (len >= 4) { + writesl(fifo, src + index, len >> 2); + index += len & ~0x03; + } + if (len & 0x02) { + musb_writew(fifo, 0, *(u16 *)&src[index]); + index += 2; + } + } else { + if (len >= 2) { + writesw(fifo, src + index, len >> 1); + index += len & ~0x01; + } + } + if (len & 0x01) + musb_writeb(fifo, 0, src[index]); + } else { + /* byte aligned */ + writesb(fifo, src, len); + } +} + +/* + * Unload an endpoint's FIFO + */ +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + void __iomem *fifo = hw_ep->fifo; + + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + 'R', hw_ep->epnum, fifo, len, dst); + + /* we can't assume unaligned writes work */ + if (likely((0x01 & (unsigned long) dst) == 0)) { + u16 index = 0; + + /* best case is 32bit-aligned destination address */ + if ((0x02 & (unsigned long) dst) == 0) { + if (len >= 4) { + readsl(fifo, dst, len >> 2); + index = len & ~0x03; + } + if (len & 0x02) { + *(u16 *)&dst[index] = musb_readw(fifo, 0); + index += 2; + } + } else { + if (len >= 2) { + readsw(fifo, dst, len >> 1); + index = len & ~0x01; + } + } + if (len & 0x01) + dst[index] = musb_readb(fifo, 0); + } else { + /* byte aligned */ + readsb(fifo, dst, len); + } +} + +#endif /* normal PIO */ + + +/*-------------------------------------------------------------------------*/ + +/* for high speed test mode; see USB 2.0 spec 7.1.20 */ +static const u8 musb_test_packet[53] = { + /* implicit SYNC then DATA0 to start */ + + /* JKJKJKJK x9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK x8 */ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + /* JJJJKKKK x8 */ + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + /* JJJJJJJKKKKKKK x8 */ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* JJJJJJJK x8 */ + 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, + /* JKKKKKKK x10, JK */ + 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e + + /* implicit CRC16 then EOP to end */ +}; + +void musb_load_testpacket(struct musb *musb) +{ + void __iomem *regs = musb->endpoints[0].regs; + + musb_ep_select(musb->mregs, 0); + musb_write_fifo(musb->control_ep, + sizeof(musb_test_packet), musb_test_packet); + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_TXPKTRDY); +} + +/*-------------------------------------------------------------------------*/ + +const char *otg_state_string(struct musb *musb) +{ + switch (musb->xceiv.state) { + case OTG_STATE_A_IDLE: return "a_idle"; + case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; + case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; + case OTG_STATE_A_HOST: return "a_host"; + case OTG_STATE_A_SUSPEND: return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; + case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; + case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; + case OTG_STATE_B_IDLE: return "b_idle"; + case OTG_STATE_B_SRP_INIT: return "b_srp_init"; + case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; + case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; + case OTG_STATE_B_HOST: return "b_host"; + default: return "UNDEFINED"; + } +} + +#ifdef CONFIG_USB_MUSB_OTG + +/* + * See also USB_OTG_1-3.pdf 6.6.5 Timers + * REVISIT: Are the other timers done in the hardware? + */ +#define TB_ASE0_BRST 100 /* Min 3.125 ms */ + +/* + * Handles OTG hnp timeouts, such as b_ase0_brst + */ +void musb_otg_timer_func(unsigned long data) +{ + struct musb *musb = (struct musb *)data; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv.state) { + case OTG_STATE_B_WAIT_ACON: + DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n"); + musb_g_disconnect(musb); + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->is_active = 0; + break; + case OTG_STATE_A_WAIT_BCON: + DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n"); + musb_hnp_stop(musb); + break; + default: + DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); + } + musb->ignore_disconnect = 0; + spin_unlock_irqrestore(&musb->lock, flags); +} + +static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0); + +/* + * Stops the B-device HNP state. Caller must take care of locking. + */ +void musb_hnp_stop(struct musb *musb) +{ + struct usb_hcd *hcd = musb_to_hcd(musb); + void __iomem *mbase = musb->mregs; + u8 reg; + + switch (musb->xceiv.state) { + case OTG_STATE_A_PERIPHERAL: + case OTG_STATE_A_WAIT_VFALL: + case OTG_STATE_A_WAIT_BCON: + DBG(1, "HNP: Switching back to A-host\n"); + musb_g_disconnect(musb); + musb->xceiv.state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + musb->is_active = 0; + break; + case OTG_STATE_B_HOST: + DBG(1, "HNP: Disabling HR\n"); + hcd->self.is_b_host = 0; + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + MUSB_DEV_MODE(musb); + reg = musb_readb(mbase, MUSB_POWER); + reg |= MUSB_POWER_SUSPENDM; + musb_writeb(mbase, MUSB_POWER, reg); + /* REVISIT: Start SESSION_REQUEST here? */ + break; + default: + DBG(1, "HNP: Stopping in unknown state %s\n", + otg_state_string(musb)); + } + + /* + * When returning to A state after HNP, avoid hub_port_rebounce(), + * which cause occasional OPT A "Did not receive reset after connect" + * errors. + */ + musb->port1_status &= + ~(1 << USB_PORT_FEAT_C_CONNECTION); +} + +#endif + +/* + * Interrupt Service Routine to record USB "global" interrupts. + * Since these do not happen often and signify things of + * paramount importance, it seems OK to check them individually; + * the order of the tests is specified in the manual + * + * @param musb instance pointer + * @param int_usb register contents + * @param devctl + * @param power + */ + +#define STAGE0_MASK (MUSB_INTR_RESUME | MUSB_INTR_SESSREQ \ + | MUSB_INTR_VBUSERROR | MUSB_INTR_CONNECT \ + | MUSB_INTR_RESET) + +static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, + u8 devctl, u8 power) +{ + irqreturn_t handled = IRQ_NONE; + void __iomem *mbase = musb->mregs; + + DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, + int_usb); + + /* in host mode, the peripheral may issue remote wakeup. + * in peripheral mode, the host may resume the link. + * spurious RESUME irqs happen too, paired with SUSPEND. + */ + if (int_usb & MUSB_INTR_RESUME) { + handled = IRQ_HANDLED; + DBG(3, "RESUME (%s)\n", otg_state_string(musb)); + + if (devctl & MUSB_DEVCTL_HM) { +#ifdef CONFIG_USB_MUSB_HDRC_HCD + switch (musb->xceiv.state) { + case OTG_STATE_A_SUSPEND: + /* remote wakeup? later, GetPortStatus + * will stop RESUME signaling + */ + + if (power & MUSB_POWER_SUSPENDM) { + /* spurious */ + musb->int_usb &= ~MUSB_INTR_SUSPEND; + DBG(2, "Spurious SUSPENDM\n"); + break; + } + + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(mbase, MUSB_POWER, + power | MUSB_POWER_RESUME); + + musb->port1_status |= + (USB_PORT_STAT_C_SUSPEND << 16) + | MUSB_PORT_STAT_RESUME; + musb->rh_timer = jiffies + + msecs_to_jiffies(20); + + musb->xceiv.state = OTG_STATE_A_HOST; + musb->is_active = 1; + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + break; + case OTG_STATE_B_WAIT_ACON: + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->is_active = 1; + MUSB_DEV_MODE(musb); + break; + default: + WARNING("bogus %s RESUME (%s)\n", + "host", + otg_state_string(musb)); + } +#endif + } else { + switch (musb->xceiv.state) { +#ifdef CONFIG_USB_MUSB_HDRC_HCD + case OTG_STATE_A_SUSPEND: + /* possibly DISCONNECT is upcoming */ + musb->xceiv.state = OTG_STATE_A_HOST; + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + break; +#endif +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_PERIPHERAL: + /* disconnect while suspended? we may + * not get a disconnect irq... + */ + if ((devctl & MUSB_DEVCTL_VBUS) + != (3 << MUSB_DEVCTL_VBUS_SHIFT) + ) { + musb->int_usb |= MUSB_INTR_DISCONNECT; + musb->int_usb &= ~MUSB_INTR_SUSPEND; + break; + } + musb_g_resume(musb); + break; + case OTG_STATE_B_IDLE: + musb->int_usb &= ~MUSB_INTR_SUSPEND; + break; +#endif + default: + WARNING("bogus %s RESUME (%s)\n", + "peripheral", + otg_state_string(musb)); + } + } + } + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + /* see manual for the order of the tests */ + if (int_usb & MUSB_INTR_SESSREQ) { + DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(musb)); + + /* IRQ arrives from ID pin sense or (later, if VBUS power + * is removed) SRP. responses are time critical: + * - turn on VBUS (with silicon-specific mechanism) + * - go through A_WAIT_VRISE + * - ... to A_WAIT_BCON. + * a_wait_vrise_tmout triggers VBUS_ERROR transitions + */ + musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); + musb->ep0_stage = MUSB_EP0_START; + musb->xceiv.state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + musb_set_vbus(musb, 1); + + handled = IRQ_HANDLED; + } + + if (int_usb & MUSB_INTR_VBUSERROR) { + int ignore = 0; + + /* During connection as an A-Device, we may see a short + * current spikes causing voltage drop, because of cable + * and peripheral capacitance combined with vbus draw. + * (So: less common with truly self-powered devices, where + * vbus doesn't act like a power supply.) + * + * Such spikes are short; usually less than ~500 usec, max + * of ~2 msec. That is, they're not sustained overcurrent + * errors, though they're reported using VBUSERROR irqs. + * + * Workarounds: (a) hardware: use self powered devices. + * (b) software: ignore non-repeated VBUS errors. + * + * REVISIT: do delays from lots of DEBUG_KERNEL checks + * make trouble here, keeping VBUS < 4.4V ? + */ + switch (musb->xceiv.state) { + case OTG_STATE_A_HOST: + /* recovery is dicey once we've gotten past the + * initial stages of enumeration, but if VBUS + * stayed ok at the other end of the link, and + * another reset is due (at least for high speed, + * to redo the chirp etc), it might work OK... + */ + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_WAIT_VRISE: + if (musb->vbuserr_retry) { + musb->vbuserr_retry--; + ignore = 1; + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(mbase, MUSB_DEVCTL, devctl); + } else { + musb->port1_status |= + (1 << USB_PORT_FEAT_OVER_CURRENT) + | (1 << USB_PORT_FEAT_C_OVER_CURRENT); + } + break; + default: + break; + } + + DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", + otg_state_string(musb), + devctl, + ({ char *s; + switch (devctl & MUSB_DEVCTL_VBUS) { + case 0 << MUSB_DEVCTL_VBUS_SHIFT: + s = "vbuserr_retry, + musb->port1_status); + + /* go through A_WAIT_VFALL then start a new session */ + if (!ignore) + musb_set_vbus(musb, 0); + handled = IRQ_HANDLED; + } + + if (int_usb & MUSB_INTR_CONNECT) { + struct usb_hcd *hcd = musb_to_hcd(musb); + + handled = IRQ_HANDLED; + musb->is_active = 1; + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + musb->ep0_stage = MUSB_EP0_START; + +#ifdef CONFIG_USB_MUSB_OTG + /* flush endpoints when transitioning from Device Mode */ + if (is_peripheral_active(musb)) { + /* REVISIT HNP; just force disconnect */ + } + musb_writew(mbase, MUSB_INTRTXE, musb->epmask); + musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe); + musb_writeb(mbase, MUSB_INTRUSBE, 0xf7); +#endif + musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED + |USB_PORT_STAT_HIGH_SPEED + |USB_PORT_STAT_ENABLE + ); + musb->port1_status |= USB_PORT_STAT_CONNECTION + |(USB_PORT_STAT_C_CONNECTION << 16); + + /* high vs full speed is just a guess until after reset */ + if (devctl & MUSB_DEVCTL_LSDEV) + musb->port1_status |= USB_PORT_STAT_LOW_SPEED; + + if (hcd->status_urb) + usb_hcd_poll_rh_status(hcd); + else + usb_hcd_resume_root_hub(hcd); + + MUSB_HST_MODE(musb); + + /* indicate new connection to OTG machine */ + switch (musb->xceiv.state) { + case OTG_STATE_B_PERIPHERAL: + if (int_usb & MUSB_INTR_SUSPEND) { + DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); + musb->xceiv.state = OTG_STATE_B_HOST; + hcd->self.is_b_host = 1; + int_usb &= ~MUSB_INTR_SUSPEND; + } else + DBG(1, "CONNECT as b_peripheral???\n"); + break; + case OTG_STATE_B_WAIT_ACON: + DBG(1, "HNP: Waiting to switch to b_host state\n"); + musb->xceiv.state = OTG_STATE_B_HOST; + hcd->self.is_b_host = 1; + break; + default: + if ((devctl & MUSB_DEVCTL_VBUS) + == (3 << MUSB_DEVCTL_VBUS_SHIFT)) { + musb->xceiv.state = OTG_STATE_A_HOST; + hcd->self.is_b_host = 0; + } + break; + } + DBG(1, "CONNECT (%s) devctl %02x\n", + otg_state_string(musb), devctl); + } +#endif /* CONFIG_USB_MUSB_HDRC_HCD */ + + /* mentor saves a bit: bus reset and babble share the same irq. + * only host sees babble; only peripheral sees bus reset. + */ + if (int_usb & MUSB_INTR_RESET) { + if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) { + /* + * Looks like non-HS BABBLE can be ignored, but + * HS BABBLE is an error condition. For HS the solution + * is to avoid babble in the first place and fix what + * caused BABBLE. When HS BABBLE happens we can only + * stop the session. + */ + if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) + DBG(1, "BABBLE devctl: %02x\n", devctl); + else { + ERR("Stopping host session -- babble\n"); + musb_writeb(mbase, MUSB_DEVCTL, 0); + } + } else if (is_peripheral_capable()) { + DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); + switch (musb->xceiv.state) { +#ifdef CONFIG_USB_OTG + case OTG_STATE_A_SUSPEND: + /* We need to ignore disconnect on suspend + * otherwise tusb 2.0 won't reconnect after a + * power cycle, which breaks otg compliance. + */ + musb->ignore_disconnect = 1; + musb_g_reset(musb); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ + DBG(1, "HNP: Setting timer as %s\n", + otg_state_string(musb)); + musb_otg_timer.data = (unsigned long)musb; + mod_timer(&musb_otg_timer, jiffies + + msecs_to_jiffies(100)); + break; + case OTG_STATE_A_PERIPHERAL: + musb_hnp_stop(musb); + break; + case OTG_STATE_B_WAIT_ACON: + DBG(1, "HNP: RESET (%s), to b_peripheral\n", + otg_state_string(musb)); + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb_g_reset(musb); + break; +#endif + case OTG_STATE_B_IDLE: + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + /* FALLTHROUGH */ + case OTG_STATE_B_PERIPHERAL: + musb_g_reset(musb); + break; + default: + DBG(1, "Unhandled BUS RESET as %s\n", + otg_state_string(musb)); + } + } + + handled = IRQ_HANDLED; + } + schedule_work(&musb->irq_work); + + return handled; +} + +/* + * Interrupt Service Routine to record USB "global" interrupts. + * Since these do not happen often and signify things of + * paramount importance, it seems OK to check them individually; + * the order of the tests is specified in the manual + * + * @param musb instance pointer + * @param int_usb register contents + * @param devctl + * @param power + */ +static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, + u8 devctl, u8 power) +{ + irqreturn_t handled = IRQ_NONE; + +#if 0 +/* REVISIT ... this would be for multiplexing periodic endpoints, or + * supporting transfer phasing to prevent exceeding ISO bandwidth + * limits of a given frame or microframe. + * + * It's not needed for peripheral side, which dedicates endpoints; + * though it _might_ use SOF irqs for other purposes. + * + * And it's not currently needed for host side, which also dedicates + * endpoints, relies on TX/RX interval registers, and isn't claimed + * to support ISO transfers yet. + */ + if (int_usb & MUSB_INTR_SOF) { + void __iomem *mbase = musb->mregs; + struct musb_hw_ep *ep; + u8 epnum; + u16 frame; + + DBG(6, "START_OF_FRAME\n"); + handled = IRQ_HANDLED; + + /* start any periodic Tx transfers waiting for current frame */ + frame = musb_readw(mbase, MUSB_FRAME); + ep = musb->endpoints; + for (epnum = 1; (epnum < musb->nr_endpoints) + && (musb->epmask >= (1 << epnum)); + epnum++, ep++) { + /* + * FIXME handle framecounter wraps (12 bits) + * eliminate duplicated StartUrb logic + */ + if (ep->dwWaitFrame >= frame) { + ep->dwWaitFrame = 0; + pr_debug("SOF --> periodic TX%s on %d\n", + ep->tx_channel ? " DMA" : "", + epnum); + if (!ep->tx_channel) + musb_h_tx_start(musb, epnum); + else + cppi_hostdma_start(musb, epnum); + } + } /* end of for loop */ + } +#endif + + if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { + DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", + otg_state_string(musb), + MUSB_MODE(musb), devctl); + handled = IRQ_HANDLED; + + switch (musb->xceiv.state) { +#ifdef CONFIG_USB_MUSB_HDRC_HCD + case OTG_STATE_A_HOST: + case OTG_STATE_A_SUSPEND: + musb_root_disconnect(musb); + if (musb->a_wait_bcon != 0) + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon)); + break; +#endif /* HOST */ +#ifdef CONFIG_USB_MUSB_OTG + case OTG_STATE_B_HOST: + musb_hnp_stop(musb); + break; + case OTG_STATE_A_PERIPHERAL: + musb_hnp_stop(musb); + musb_root_disconnect(musb); + /* FALLTHROUGH */ + case OTG_STATE_B_WAIT_ACON: + /* FALLTHROUGH */ +#endif /* OTG */ +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_IDLE: + musb_g_disconnect(musb); + break; +#endif /* GADGET */ + default: + WARNING("unhandled DISCONNECT transition (%s)\n", + otg_state_string(musb)); + break; + } + + schedule_work(&musb->irq_work); + } + + if (int_usb & MUSB_INTR_SUSPEND) { + DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", + otg_state_string(musb), devctl, power); + handled = IRQ_HANDLED; + + switch (musb->xceiv.state) { +#ifdef CONFIG_USB_MUSB_OTG + case OTG_STATE_A_PERIPHERAL: + /* + * We cannot stop HNP here, devctl BDEVICE might be + * still set. + */ + break; +#endif + case OTG_STATE_B_PERIPHERAL: + musb_g_suspend(musb); + musb->is_active = is_otg_enabled(musb) + && musb->xceiv.gadget->b_hnp_enable; + if (musb->is_active) { +#ifdef CONFIG_USB_MUSB_OTG + musb->xceiv.state = OTG_STATE_B_WAIT_ACON; + DBG(1, "HNP: Setting timer for b_ase0_brst\n"); + musb_otg_timer.data = (unsigned long)musb; + mod_timer(&musb_otg_timer, jiffies + + msecs_to_jiffies(TB_ASE0_BRST)); +#endif + } + break; + case OTG_STATE_A_WAIT_BCON: + if (musb->a_wait_bcon != 0) + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon)); + break; + case OTG_STATE_A_HOST: + musb->xceiv.state = OTG_STATE_A_SUSPEND; + musb->is_active = is_otg_enabled(musb) + && musb->xceiv.host->b_hnp_enable; + break; + case OTG_STATE_B_HOST: + /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ + DBG(1, "REVISIT: SUSPEND as B_HOST\n"); + break; + default: + /* "should not happen" */ + musb->is_active = 0; + break; + } + schedule_work(&musb->irq_work); + } + + + return handled; +} + +/*-------------------------------------------------------------------------*/ + +/* +* Program the HDRC to start (enable interrupts, dma, etc.). +*/ +void musb_start(struct musb *musb) +{ + void __iomem *regs = musb->mregs; + u8 devctl = musb_readb(regs, MUSB_DEVCTL); + + DBG(2, "<== devctl %02x\n", devctl); + + /* Set INT enable registers, enable interrupts */ + musb_writew(regs, MUSB_INTRTXE, musb->epmask); + musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe); + musb_writeb(regs, MUSB_INTRUSBE, 0xf7); + + musb_writeb(regs, MUSB_TESTMODE, 0); + + /* put into basic highspeed mode and start session */ + musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE + | MUSB_POWER_SOFTCONN + | MUSB_POWER_HSENAB + /* ENSUSPEND wedges tusb */ + /* | MUSB_POWER_ENSUSPEND */ + ); + + musb->is_active = 0; + devctl = musb_readb(regs, MUSB_DEVCTL); + devctl &= ~MUSB_DEVCTL_SESSION; + + if (is_otg_enabled(musb)) { + /* session started after: + * (a) ID-grounded irq, host mode; + * (b) vbus present/connect IRQ, peripheral mode; + * (c) peripheral initiates, using SRP + */ + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->is_active = 1; + else + devctl |= MUSB_DEVCTL_SESSION; + + } else if (is_host_enabled(musb)) { + /* assume ID pin is hard-wired to ground */ + devctl |= MUSB_DEVCTL_SESSION; + + } else /* peripheral is enabled */ { + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->is_active = 1; + } + musb_platform_enable(musb); + musb_writeb(regs, MUSB_DEVCTL, devctl); +} + + +static void musb_generic_disable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + u16 temp; + + /* disable interrupts */ + musb_writeb(mbase, MUSB_INTRUSBE, 0); + musb_writew(mbase, MUSB_INTRTXE, 0); + musb_writew(mbase, MUSB_INTRRXE, 0); + + /* off */ + musb_writeb(mbase, MUSB_DEVCTL, 0); + + /* flush pending interrupts */ + temp = musb_readb(mbase, MUSB_INTRUSB); + temp = musb_readw(mbase, MUSB_INTRTX); + temp = musb_readw(mbase, MUSB_INTRRX); + +} + +/* + * Make the HDRC stop (disable interrupts, etc.); + * reversible by musb_start + * called on gadget driver unregister + * with controller locked, irqs blocked + * acts as a NOP unless some role activated the hardware + */ +void musb_stop(struct musb *musb) +{ + /* stop IRQs, timers, ... */ + musb_platform_disable(musb); + musb_generic_disable(musb); + DBG(3, "HDRC disabled\n"); + + /* FIXME + * - mark host and/or peripheral drivers unusable/inactive + * - disable DMA (and enable it in HdrcStart) + * - make sure we can musb_start() after musb_stop(); with + * OTG mode, gadget driver module rmmod/modprobe cycles that + * - ... + */ + musb_platform_try_idle(musb, 0); +} + +static void musb_shutdown(struct platform_device *pdev) +{ + struct musb *musb = dev_to_musb(&pdev->dev); + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + musb_platform_disable(musb); + musb_generic_disable(musb); + if (musb->clock) { + clk_put(musb->clock); + musb->clock = NULL; + } + spin_unlock_irqrestore(&musb->lock, flags); + + /* FIXME power down */ +} + + +/*-------------------------------------------------------------------------*/ + +/* + * The silicon either has hard-wired endpoint configurations, or else + * "dynamic fifo" sizing. The driver has support for both, though at this + * writing only the dynamic sizing is very well tested. We use normal + * idioms to so both modes are compile-tested, but dead code elimination + * leaves only the relevant one in the object file. + * + * We don't currently use dynamic fifo setup capability to do anything + * more than selecting one of a bunch of predefined configurations. + */ +#ifdef MUSB_C_DYNFIFO_DEF +#define can_dynfifo() 1 +#else +#define can_dynfifo() 0 +#endif + +#if defined(CONFIG_USB_TUSB6010) || \ + defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +static ushort __initdata fifo_mode = 4; +#else +static ushort __initdata fifo_mode = 2; +#endif + +/* "modprobe ... fifo_mode=1" etc */ +module_param(fifo_mode, ushort, 0); +MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration"); + + +#define DYN_FIFO_SIZE (1<<(MUSB_C_RAM_BITS+2)) + +enum fifo_style { FIFO_RXTX, FIFO_TX, FIFO_RX } __attribute__ ((packed)); +enum buf_mode { BUF_SINGLE, BUF_DOUBLE } __attribute__ ((packed)); + +struct fifo_cfg { + u8 hw_ep_num; + enum fifo_style style; + enum buf_mode mode; + u16 maxpacket; +}; + +/* + * tables defining fifo_mode values. define more if you like. + * for host side, make sure both halves of ep1 are set up. + */ + +/* mode 0 - fits in 2KB */ +static struct fifo_cfg __initdata mode_0_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 1 - fits in 4KB */ +static struct fifo_cfg __initdata mode_1_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 2 - fits in 4KB */ +static struct fifo_cfg __initdata mode_2_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 3 - fits in 4KB */ +static struct fifo_cfg __initdata mode_3_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 4 - fits in 16KB */ +static struct fifo_cfg __initdata mode_4_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, +{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, +}; + + +/* + * configure a fifo; for non-shared endpoints, this may be called + * once for a tx fifo and once for an rx fifo. + * + * returns negative errno or offset for next fifo. + */ +static int __init +fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, + const struct fifo_cfg *cfg, u16 offset) +{ + void __iomem *mbase = musb->mregs; + int size = 0; + u16 maxpacket = cfg->maxpacket; + u16 c_off = offset >> 3; + u8 c_size; + + /* expect hw_ep has already been zero-initialized */ + + size = ffs(max(maxpacket, (u16) 8)) - 1; + maxpacket = 1 << size; + + c_size = size - 3; + if (cfg->mode == BUF_DOUBLE) { + if ((offset + (maxpacket << 1)) > DYN_FIFO_SIZE) + return -EMSGSIZE; + c_size |= MUSB_FIFOSZ_DPB; + } else { + if ((offset + maxpacket) > DYN_FIFO_SIZE) + return -EMSGSIZE; + } + + /* configure the FIFO */ + musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum); + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + /* EP0 reserved endpoint for control, bidirectional; + * EP1 reserved for bulk, two unidirection halves. + */ + if (hw_ep->epnum == 1) + musb->bulk_ep = hw_ep; + /* REVISIT error check: be sure ep0 can both rx and tx ... */ +#endif + switch (cfg->style) { + case FIFO_TX: + musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); + musb_writew(mbase, MUSB_TXFIFOADD, c_off); + hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); + hw_ep->max_packet_sz_tx = maxpacket; + break; + case FIFO_RX: + musb_writeb(mbase, MUSB_RXFIFOSZ, c_size); + musb_writew(mbase, MUSB_RXFIFOADD, c_off); + hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); + hw_ep->max_packet_sz_rx = maxpacket; + break; + case FIFO_RXTX: + musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); + musb_writew(mbase, MUSB_TXFIFOADD, c_off); + hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); + hw_ep->max_packet_sz_rx = maxpacket; + + musb_writeb(mbase, MUSB_RXFIFOSZ, c_size); + musb_writew(mbase, MUSB_RXFIFOADD, c_off); + hw_ep->tx_double_buffered = hw_ep->rx_double_buffered; + hw_ep->max_packet_sz_tx = maxpacket; + + hw_ep->is_shared_fifo = true; + break; + } + + /* NOTE rx and tx endpoint irqs aren't managed separately, + * which happens to be ok + */ + musb->epmask |= (1 << hw_ep->epnum); + + return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0)); +} + +static struct fifo_cfg __initdata ep0_cfg = { + .style = FIFO_RXTX, .maxpacket = 64, +}; + +static int __init ep_config_from_table(struct musb *musb) +{ + const struct fifo_cfg *cfg; + unsigned i, n; + int offset; + struct musb_hw_ep *hw_ep = musb->endpoints; + + switch (fifo_mode) { + default: + fifo_mode = 0; + /* FALLTHROUGH */ + case 0: + cfg = mode_0_cfg; + n = ARRAY_SIZE(mode_0_cfg); + break; + case 1: + cfg = mode_1_cfg; + n = ARRAY_SIZE(mode_1_cfg); + break; + case 2: + cfg = mode_2_cfg; + n = ARRAY_SIZE(mode_2_cfg); + break; + case 3: + cfg = mode_3_cfg; + n = ARRAY_SIZE(mode_3_cfg); + break; + case 4: + cfg = mode_4_cfg; + n = ARRAY_SIZE(mode_4_cfg); + break; + } + + printk(KERN_DEBUG "%s: setup fifo_mode %d\n", + musb_driver_name, fifo_mode); + + + offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0); + /* assert(offset > 0) */ + + /* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would + * be better than static MUSB_C_NUM_EPS and DYN_FIFO_SIZE... + */ + + for (i = 0; i < n; i++) { + u8 epn = cfg->hw_ep_num; + + if (epn >= MUSB_C_NUM_EPS) { + pr_debug("%s: invalid ep %d\n", + musb_driver_name, epn); + continue; + } + offset = fifo_setup(musb, hw_ep + epn, cfg++, offset); + if (offset < 0) { + pr_debug("%s: mem overrun, ep %d\n", + musb_driver_name, epn); + return -EINVAL; + } + epn++; + musb->nr_endpoints = max(epn, musb->nr_endpoints); + } + + printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n", + musb_driver_name, + n + 1, MUSB_C_NUM_EPS * 2 - 1, + offset, DYN_FIFO_SIZE); + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (!musb->bulk_ep) { + pr_debug("%s: missing bulk\n", musb_driver_name); + return -EINVAL; + } +#endif + + return 0; +} + + +/* + * ep_config_from_hw - when MUSB_C_DYNFIFO_DEF is false + * @param musb the controller + */ +static int __init ep_config_from_hw(struct musb *musb) +{ + u8 epnum = 0, reg; + struct musb_hw_ep *hw_ep; + void *mbase = musb->mregs; + + DBG(2, "<== static silicon ep config\n"); + + /* FIXME pick up ep0 maxpacket size */ + + for (epnum = 1; epnum < MUSB_C_NUM_EPS; epnum++) { + musb_ep_select(mbase, epnum); + hw_ep = musb->endpoints + epnum; + + /* read from core using indexed model */ + reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE); + if (!reg) { + /* 0's returned when no more endpoints */ + break; + } + musb->nr_endpoints++; + musb->epmask |= (1 << epnum); + + hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f); + + /* shared TX/RX FIFO? */ + if ((reg & 0xf0) == 0xf0) { + hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx; + hw_ep->is_shared_fifo = true; + continue; + } else { + hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4); + hw_ep->is_shared_fifo = false; + } + + /* FIXME set up hw_ep->{rx,tx}_double_buffered */ + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + /* pick an RX/TX endpoint for bulk */ + if (hw_ep->max_packet_sz_tx < 512 + || hw_ep->max_packet_sz_rx < 512) + continue; + + /* REVISIT: this algorithm is lazy, we should at least + * try to pick a double buffered endpoint. + */ + if (musb->bulk_ep) + continue; + musb->bulk_ep = hw_ep; +#endif + } + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (!musb->bulk_ep) { + pr_debug("%s: missing bulk\n", musb_driver_name); + return -EINVAL; + } +#endif + + return 0; +} + +enum { MUSB_CONTROLLER_MHDRC, MUSB_CONTROLLER_HDRC, }; + +/* Initialize MUSB (M)HDRC part of the USB hardware subsystem; + * configure endpoints, or take their config from silicon + */ +static int __init musb_core_init(u16 musb_type, struct musb *musb) +{ +#ifdef MUSB_AHB_ID + u32 data; +#endif + u8 reg; + char *type; + u16 hwvers, rev_major, rev_minor; + char aInfo[78], aRevision[32], aDate[12]; + void __iomem *mbase = musb->mregs; + int status = 0; + int i; + + /* log core options (read using indexed model) */ + musb_ep_select(mbase, 0); + reg = musb_readb(mbase, 0x10 + MUSB_CONFIGDATA); + + strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); + if (reg & MUSB_CONFIGDATA_DYNFIFO) + strcat(aInfo, ", dyn FIFOs"); + if (reg & MUSB_CONFIGDATA_MPRXE) { + strcat(aInfo, ", bulk combine"); +#ifdef C_MP_RX + musb->bulk_combine = true; +#else + strcat(aInfo, " (X)"); /* no driver support */ +#endif + } + if (reg & MUSB_CONFIGDATA_MPTXE) { + strcat(aInfo, ", bulk split"); +#ifdef C_MP_TX + musb->bulk_split = true; +#else + strcat(aInfo, " (X)"); /* no driver support */ +#endif + } + if (reg & MUSB_CONFIGDATA_HBRXE) { + strcat(aInfo, ", HB-ISO Rx"); + strcat(aInfo, " (X)"); /* no driver support */ + } + if (reg & MUSB_CONFIGDATA_HBTXE) { + strcat(aInfo, ", HB-ISO Tx"); + strcat(aInfo, " (X)"); /* no driver support */ + } + if (reg & MUSB_CONFIGDATA_SOFTCONE) + strcat(aInfo, ", SoftConn"); + + printk(KERN_DEBUG "%s: ConfigData=0x%02x (%s)\n", + musb_driver_name, reg, aInfo); + +#ifdef MUSB_AHB_ID + data = musb_readl(mbase, 0x404); + sprintf(aDate, "%04d-%02x-%02x", (data & 0xffff), + (data >> 16) & 0xff, (data >> 24) & 0xff); + /* FIXME ID2 and ID3 are unused */ + data = musb_readl(mbase, 0x408); + printk(KERN_DEBUG "ID2=%lx\n", (long unsigned)data); + data = musb_readl(mbase, 0x40c); + printk(KERN_DEBUG "ID3=%lx\n", (long unsigned)data); + reg = musb_readb(mbase, 0x400); + musb_type = ('M' == reg) ? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC; +#else + aDate[0] = 0; +#endif + if (MUSB_CONTROLLER_MHDRC == musb_type) { + musb->is_multipoint = 1; + type = "M"; + } else { + musb->is_multipoint = 0; + type = ""; +#ifdef CONFIG_USB_MUSB_HDRC_HCD +#ifndef CONFIG_USB_OTG_BLACKLIST_HUB + printk(KERN_ERR + "%s: kernel must blacklist external hubs\n", + musb_driver_name); +#endif +#endif + } + + /* log release info */ + hwvers = musb_readw(mbase, MUSB_HWVERS); + rev_major = (hwvers >> 10) & 0x1f; + rev_minor = hwvers & 0x3ff; + snprintf(aRevision, 32, "%d.%d%s", rev_major, + rev_minor, (hwvers & 0x8000) ? "RC" : ""); + printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n", + musb_driver_name, type, aRevision, aDate); + + /* configure ep0 */ + musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; + + /* discover endpoint configuration */ + musb->nr_endpoints = 1; + musb->epmask = 1; + + if (reg & MUSB_CONFIGDATA_DYNFIFO) { + if (can_dynfifo()) + status = ep_config_from_table(musb); + else { + ERR("reconfigure software for Dynamic FIFOs\n"); + status = -ENODEV; + } + } else { + if (!can_dynfifo()) + status = ep_config_from_hw(musb); + else { + ERR("reconfigure software for static FIFOs\n"); + return -ENODEV; + } + } + + if (status < 0) + return status; + + /* finish init, and print endpoint config */ + for (i = 0; i < musb->nr_endpoints; i++) { + struct musb_hw_ep *hw_ep = musb->endpoints + i; + + hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase; +#ifdef CONFIG_USB_TUSB6010 + hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i); + hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i); + hw_ep->fifo_sync_va = + musb->sync_va + 0x400 + MUSB_FIFO_OFFSET(i); + + if (i == 0) + hw_ep->conf = mbase - 0x400 + TUSB_EP0_CONF; + else + hw_ep->conf = mbase + 0x400 + (((i - 1) & 0xf) << 2); +#endif + + hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase; +#ifdef CONFIG_USB_MUSB_HDRC_HCD + hw_ep->target_regs = MUSB_BUSCTL_OFFSET(i, 0) + mbase; + hw_ep->rx_reinit = 1; + hw_ep->tx_reinit = 1; +#endif + + if (hw_ep->max_packet_sz_tx) { + printk(KERN_DEBUG + "%s: hw_ep %d%s, %smax %d\n", + musb_driver_name, i, + hw_ep->is_shared_fifo ? "shared" : "tx", + hw_ep->tx_double_buffered + ? "doublebuffer, " : "", + hw_ep->max_packet_sz_tx); + } + if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) { + printk(KERN_DEBUG + "%s: hw_ep %d%s, %smax %d\n", + musb_driver_name, i, + "rx", + hw_ep->rx_double_buffered + ? "doublebuffer, " : "", + hw_ep->max_packet_sz_rx); + } + if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx)) + DBG(1, "hw_ep %d not configured\n", i); + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) + +static irqreturn_t generic_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval = musb_interrupt(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + + /* REVISIT we sometimes get spurious IRQs on g_ep0 + * not clear why... + */ + if (retval != IRQ_HANDLED) + DBG(5, "spurious?\n"); + + return IRQ_HANDLED; +} + +#else +#define generic_interrupt NULL +#endif + +/* + * handle all the irqs defined by the HDRC core. for now we expect: other + * irq sources (phy, dma, etc) will be handled first, musb->int_* values + * will be assigned, and the irq will already have been acked. + * + * called in irq context with spinlock held, irqs blocked + */ +irqreturn_t musb_interrupt(struct musb *musb) +{ + irqreturn_t retval = IRQ_NONE; + u8 devctl, power; + int ep_num; + u32 reg; + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + power = musb_readb(musb->mregs, MUSB_POWER); + + DBG(4, "** IRQ %s usb%04x tx%04x rx%04x\n", + (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", + musb->int_usb, musb->int_tx, musb->int_rx); + + /* the core can interrupt us for multiple reasons; docs have + * a generic interrupt flowchart to follow + */ + if (musb->int_usb & STAGE0_MASK) + retval |= musb_stage0_irq(musb, musb->int_usb, + devctl, power); + + /* "stage 1" is handling endpoint irqs */ + + /* handle endpoint 0 first */ + if (musb->int_tx & 1) { + if (devctl & MUSB_DEVCTL_HM) + retval |= musb_h_ep0_irq(musb); + else + retval |= musb_g_ep0_irq(musb); + } + + /* RX on endpoints 1-15 */ + reg = musb->int_rx >> 1; + ep_num = 1; + while (reg) { + if (reg & 1) { + /* musb_ep_select(musb->mregs, ep_num); */ + /* REVISIT just retval = ep->rx_irq(...) */ + retval = IRQ_HANDLED; + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_rx(musb, ep_num); + } else { + if (is_peripheral_capable()) + musb_g_rx(musb, ep_num); + } + } + + reg >>= 1; + ep_num++; + } + + /* TX on endpoints 1-15 */ + reg = musb->int_tx >> 1; + ep_num = 1; + while (reg) { + if (reg & 1) { + /* musb_ep_select(musb->mregs, ep_num); */ + /* REVISIT just retval |= ep->tx_irq(...) */ + retval = IRQ_HANDLED; + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_tx(musb, ep_num); + } else { + if (is_peripheral_capable()) + musb_g_tx(musb, ep_num); + } + } + reg >>= 1; + ep_num++; + } + + /* finish handling "global" interrupts after handling fifos */ + if (musb->int_usb) + retval |= musb_stage2_irq(musb, + musb->int_usb, devctl, power); + + return retval; +} + + +#ifndef CONFIG_MUSB_PIO_ONLY +static int __initdata use_dma = 1; + +/* "modprobe ... use_dma=0" etc */ +module_param(use_dma, bool, 0); +MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); + +void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) +{ + u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + /* called with controller lock already held */ + + if (!epnum) { +#ifndef CONFIG_USB_TUSB_OMAP_DMA + if (!is_cppi_enabled()) { + /* endpoint 0 */ + if (devctl & MUSB_DEVCTL_HM) + musb_h_ep0_irq(musb); + else + musb_g_ep0_irq(musb); + } +#endif + } else { + /* endpoints 1..15 */ + if (transmit) { + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_tx(musb, epnum); + } else { + if (is_peripheral_capable()) + musb_g_tx(musb, epnum); + } + } else { + /* receive */ + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_rx(musb, epnum); + } else { + if (is_peripheral_capable()) + musb_g_rx(musb, epnum); + } + } + } +} + +#else +#define use_dma 0 +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_SYSFS + +static ssize_t +musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&musb->lock, flags); + ret = sprintf(buf, "%s\n", otg_state_string(musb)); + spin_unlock_irqrestore(&musb->lock, flags); + + return ret; +} + +static ssize_t +musb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + if (!strncmp(buf, "host", 4)) + musb_platform_set_mode(musb, MUSB_HOST); + if (!strncmp(buf, "peripheral", 10)) + musb_platform_set_mode(musb, MUSB_PERIPHERAL); + if (!strncmp(buf, "otg", 3)) + musb_platform_set_mode(musb, MUSB_OTG); + spin_unlock_irqrestore(&musb->lock, flags); + + return n; +} +static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store); + +static ssize_t +musb_vbus_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + unsigned long val; + + if (sscanf(buf, "%lu", &val) < 1) { + printk(KERN_ERR "Invalid VBUS timeout ms value\n"); + return -EINVAL; + } + + spin_lock_irqsave(&musb->lock, flags); + musb->a_wait_bcon = val; + if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON) + musb->is_active = 0; + musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val)); + spin_unlock_irqrestore(&musb->lock, flags); + + return n; +} + +static ssize_t +musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + unsigned long val; + int vbus; + + spin_lock_irqsave(&musb->lock, flags); + val = musb->a_wait_bcon; + vbus = musb_platform_get_vbus_status(musb); + spin_unlock_irqrestore(&musb->lock, flags); + + return sprintf(buf, "Vbus %s, timeout %lu\n", + vbus ? "on" : "off", val); +} +static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + +/* Gadget drivers can't know that a host is connected so they might want + * to start SRP, but users can. This allows userspace to trigger SRP. + */ +static ssize_t +musb_srp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned short srp; + + if (sscanf(buf, "%hu", &srp) != 1 + || (srp != 1)) { + printk(KERN_ERR "SRP: Value must be 1\n"); + return -EINVAL; + } + + if (srp == 1) + musb_g_wakeup(musb); + + return n; +} +static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store); + +#endif /* CONFIG_USB_GADGET_MUSB_HDRC */ + +#endif /* sysfs */ + +/* Only used to provide driver mode change events */ +static void musb_irq_work(struct work_struct *data) +{ + struct musb *musb = container_of(data, struct musb, irq_work); + static int old_state; + + if (musb->xceiv.state != old_state) { + old_state = musb->xceiv.state; + sysfs_notify(&musb->controller->kobj, NULL, "mode"); + } +} + +/* -------------------------------------------------------------------------- + * Init support + */ + +static struct musb *__init +allocate_instance(struct device *dev, void __iomem *mbase) +{ + struct musb *musb; + struct musb_hw_ep *ep; + int epnum; +#ifdef CONFIG_USB_MUSB_HDRC_HCD + struct usb_hcd *hcd; + + hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id); + if (!hcd) + return NULL; + /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */ + + musb = hcd_to_musb(hcd); + INIT_LIST_HEAD(&musb->control); + INIT_LIST_HEAD(&musb->in_bulk); + INIT_LIST_HEAD(&musb->out_bulk); + + hcd->uses_new_polling = 1; + + musb->vbuserr_retry = VBUSERR_RETRY_COUNT; +#else + musb = kzalloc(sizeof *musb, GFP_KERNEL); + if (!musb) + return NULL; + dev_set_drvdata(dev, musb); + +#endif + + musb->mregs = mbase; + musb->ctrl_base = mbase; + musb->nIrq = -ENODEV; + for (epnum = 0, ep = musb->endpoints; + epnum < MUSB_C_NUM_EPS; + epnum++, ep++) { + + ep->musb = musb; + ep->epnum = epnum; + } + + musb->controller = dev; + return musb; +} + +static void musb_free(struct musb *musb) +{ + /* this has multiple entry modes. it handles fault cleanup after + * probe(), where things may be partially set up, as well as rmmod + * cleanup after everything's been de-activated. + */ + +#ifdef CONFIG_SYSFS + device_remove_file(musb->controller, &dev_attr_mode); + device_remove_file(musb->controller, &dev_attr_vbus); +#ifdef CONFIG_USB_MUSB_OTG + device_remove_file(musb->controller, &dev_attr_srp); +#endif +#endif + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + musb_gadget_cleanup(musb); +#endif + + if (musb->nIrq >= 0) { + disable_irq_wake(musb->nIrq); + free_irq(musb->nIrq, musb); + } + if (is_dma_capable() && musb->dma_controller) { + struct dma_controller *c = musb->dma_controller; + + (void) c->stop(c); + dma_controller_destroy(c); + } + + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_platform_exit(musb); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + + if (musb->clock) { + clk_disable(musb->clock); + clk_put(musb->clock); + } + +#ifdef CONFIG_USB_MUSB_OTG + put_device(musb->xceiv.dev); +#endif + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + usb_put_hcd(musb_to_hcd(musb)); +#else + kfree(musb); +#endif +} + +/* + * Perform generic per-controller initialization. + * + * @pDevice: the controller (already clocked, etc) + * @nIrq: irq + * @mregs: virtual address of controller registers, + * not yet corrected for platform-specific offsets + */ +static int __init +musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) +{ + int status; + struct musb *musb; + struct musb_hdrc_platform_data *plat = dev->platform_data; + + /* The driver might handle more features than the board; OK. + * Fail when the board needs a feature that's not enabled. + */ + if (!plat) { + dev_dbg(dev, "no platform_data?\n"); + return -ENODEV; + } + switch (plat->mode) { + case MUSB_HOST: +#ifdef CONFIG_USB_MUSB_HDRC_HCD + break; +#else + goto bad_config; +#endif + case MUSB_PERIPHERAL: +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + break; +#else + goto bad_config; +#endif + case MUSB_OTG: +#ifdef CONFIG_USB_MUSB_OTG + break; +#else +bad_config: +#endif + default: + dev_err(dev, "incompatible Kconfig role setting\n"); + return -EINVAL; + } + + /* allocate */ + musb = allocate_instance(dev, ctrl); + if (!musb) + return -ENOMEM; + + spin_lock_init(&musb->lock); + musb->board_mode = plat->mode; + musb->board_set_power = plat->set_power; + musb->set_clock = plat->set_clock; + musb->min_power = plat->min_power; + + /* Clock usage is chip-specific ... functional clock (DaVinci, + * OMAP2430), or PHY ref (some TUSB6010 boards). All this core + * code does is make sure a clock handle is available; platform + * code manages it during start/stop and suspend/resume. + */ + if (plat->clock) { + musb->clock = clk_get(dev, plat->clock); + if (IS_ERR(musb->clock)) { + status = PTR_ERR(musb->clock); + musb->clock = NULL; + goto fail; + } + } + + /* assume vbus is off */ + + /* platform adjusts musb->mregs and musb->isr if needed, + * and activates clocks + */ + musb->isr = generic_interrupt; + status = musb_platform_init(musb); + + if (status < 0) + goto fail; + if (!musb->isr) { + status = -ENODEV; + goto fail2; + } + +#ifndef CONFIG_MUSB_PIO_ONLY + if (use_dma && dev->dma_mask) { + struct dma_controller *c; + + c = dma_controller_create(musb, musb->mregs); + musb->dma_controller = c; + if (c) + (void) c->start(c); + } +#endif + /* ideally this would be abstracted in platform setup */ + if (!is_dma_capable() || !musb->dma_controller) + dev->dma_mask = NULL; + + /* be sure interrupts are disabled before connecting ISR */ + musb_platform_disable(musb); + musb_generic_disable(musb); + + /* setup musb parts of the core (especially endpoints) */ + status = musb_core_init(plat->multipoint + ? MUSB_CONTROLLER_MHDRC + : MUSB_CONTROLLER_HDRC, musb); + if (status < 0) + goto fail2; + + /* Init IRQ workqueue before request_irq */ + INIT_WORK(&musb->irq_work, musb_irq_work); + + /* attach to the IRQ */ + if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) { + dev_err(dev, "request_irq %d failed!\n", nIrq); + status = -ENODEV; + goto fail2; + } + musb->nIrq = nIrq; +/* FIXME this handles wakeup irqs wrong */ + if (enable_irq_wake(nIrq) == 0) + device_init_wakeup(dev, 1); + + pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n", + musb_driver_name, + ({char *s; + switch (musb->board_mode) { + case MUSB_HOST: s = "Host"; break; + case MUSB_PERIPHERAL: s = "Peripheral"; break; + default: s = "OTG"; break; + }; s; }), + ctrl, + (is_dma_capable() && musb->dma_controller) + ? "DMA" : "PIO", + musb->nIrq); + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + /* host side needs more setup, except for no-host modes */ + if (musb->board_mode != MUSB_PERIPHERAL) { + struct usb_hcd *hcd = musb_to_hcd(musb); + + if (musb->board_mode == MUSB_OTG) + hcd->self.otg_port = 1; + musb->xceiv.host = &hcd->self; + hcd->power_budget = 2 * (plat->power ? : 250); + } +#endif /* CONFIG_USB_MUSB_HDRC_HCD */ + + /* For the host-only role, we can activate right away. + * (We expect the ID pin to be forcibly grounded!!) + * Otherwise, wait till the gadget driver hooks up. + */ + if (!is_otg_enabled(musb) && is_host_enabled(musb)) { + MUSB_HST_MODE(musb); + musb->xceiv.default_a = 1; + musb->xceiv.state = OTG_STATE_A_IDLE; + + status = usb_add_hcd(musb_to_hcd(musb), -1, 0); + + DBG(1, "%s mode, status %d, devctl %02x %c\n", + "HOST", status, + musb_readb(musb->mregs, MUSB_DEVCTL), + (musb_readb(musb->mregs, MUSB_DEVCTL) + & MUSB_DEVCTL_BDEVICE + ? 'B' : 'A')); + + } else /* peripheral is enabled */ { + MUSB_DEV_MODE(musb); + musb->xceiv.default_a = 0; + musb->xceiv.state = OTG_STATE_B_IDLE; + + status = musb_gadget_setup(musb); + + DBG(1, "%s mode, status %d, dev%02x\n", + is_otg_enabled(musb) ? "OTG" : "PERIPHERAL", + status, + musb_readb(musb->mregs, MUSB_DEVCTL)); + + } + + if (status == 0) + musb_debug_create("driver/musb_hdrc", musb); + else { +fail: + if (musb->clock) + clk_put(musb->clock); + device_init_wakeup(dev, 0); + musb_free(musb); + return status; + } + +#ifdef CONFIG_SYSFS + status = device_create_file(dev, &dev_attr_mode); + status = device_create_file(dev, &dev_attr_vbus); +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + status = device_create_file(dev, &dev_attr_srp); +#endif /* CONFIG_USB_GADGET_MUSB_HDRC */ + status = 0; +#endif + + return status; + +fail2: + musb_platform_exit(musb); + goto fail; +} + +/*-------------------------------------------------------------------------*/ + +/* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just + * bridge to a platform device; this driver then suffices. + */ + +#ifndef CONFIG_MUSB_PIO_ONLY +static u64 *orig_dma_mask; +#endif + +static int __init musb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq = platform_get_irq(pdev, 0); + struct resource *iomem; + void __iomem *base; + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem || irq == 0) + return -ENODEV; + + base = ioremap(iomem->start, iomem->end - iomem->start + 1); + if (!base) { + dev_err(dev, "ioremap failed\n"); + return -ENOMEM; + } + +#ifndef CONFIG_MUSB_PIO_ONLY + /* clobbered by use_dma=n */ + orig_dma_mask = dev->dma_mask; +#endif + return musb_init_controller(dev, irq, base); +} + +static int __devexit musb_remove(struct platform_device *pdev) +{ + struct musb *musb = dev_to_musb(&pdev->dev); + void __iomem *ctrl_base = musb->ctrl_base; + + /* this gets called on rmmod. + * - Host mode: host may still be active + * - Peripheral mode: peripheral is deactivated (or never-activated) + * - OTG mode: both roles are deactivated (or never-activated) + */ + musb_shutdown(pdev); + musb_debug_delete("driver/musb_hdrc", musb); +#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (musb->board_mode == MUSB_HOST) + usb_remove_hcd(musb_to_hcd(musb)); +#endif + musb_free(musb); + iounmap(ctrl_base); + device_init_wakeup(&pdev->dev, 0); +#ifndef CONFIG_MUSB_PIO_ONLY + pdev->dev.dma_mask = orig_dma_mask; +#endif + return 0; +} + +#ifdef CONFIG_PM + +static int musb_suspend(struct platform_device *pdev, pm_message_t message) +{ + unsigned long flags; + struct musb *musb = dev_to_musb(&pdev->dev); + + if (!musb->clock) + return 0; + + spin_lock_irqsave(&musb->lock, flags); + + if (is_peripheral_active(musb)) { + /* FIXME force disconnect unless we know USB will wake + * the system up quickly enough to respond ... + */ + } else if (is_host_active(musb)) { + /* we know all the children are suspended; sometimes + * they will even be wakeup-enabled. + */ + } + + if (musb->set_clock) + musb->set_clock(musb->clock, 0); + else + clk_disable(musb->clock); + spin_unlock_irqrestore(&musb->lock, flags); + return 0; +} + +static int musb_resume(struct platform_device *pdev) +{ + unsigned long flags; + struct musb *musb = dev_to_musb(&pdev->dev); + + if (!musb->clock) + return 0; + + spin_lock_irqsave(&musb->lock, flags); + + if (musb->set_clock) + musb->set_clock(musb->clock, 1); + else + clk_enable(musb->clock); + + /* for static cmos like DaVinci, register values were preserved + * unless for some reason the whole soc powered down and we're + * not treating that as a whole-system restart (e.g. swsusp) + */ + spin_unlock_irqrestore(&musb->lock, flags); + return 0; +} + +#else +#define musb_suspend NULL +#define musb_resume NULL +#endif + +static struct platform_driver musb_driver = { + .driver = { + .name = (char *)musb_driver_name, + .bus = &platform_bus_type, + .owner = THIS_MODULE, + }, + .remove = __devexit_p(musb_remove), + .shutdown = musb_shutdown, + .suspend = musb_suspend, + .resume = musb_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init musb_init(void) +{ +#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (usb_disabled()) + return 0; +#endif + + pr_info("%s: version " MUSB_VERSION ", " +#ifdef CONFIG_MUSB_PIO_ONLY + "pio" +#elif defined(CONFIG_USB_TI_CPPI_DMA) + "cppi-dma" +#elif defined(CONFIG_USB_INVENTRA_DMA) + "musb-dma" +#elif defined(CONFIG_USB_TUSB_OMAP_DMA) + "tusb-omap-dma" +#else + "?dma?" +#endif + ", " +#ifdef CONFIG_USB_MUSB_OTG + "otg (peripheral+host)" +#elif defined(CONFIG_USB_GADGET_MUSB_HDRC) + "peripheral" +#elif defined(CONFIG_USB_MUSB_HDRC_HCD) + "host" +#endif + ", debug=%d\n", + musb_driver_name, debug); + return platform_driver_probe(&musb_driver, musb_probe); +} + +/* make us init after usbcore and before usb + * gadget and host-side drivers start to register + */ +subsys_initcall(musb_init); + +static void __exit musb_cleanup(void) +{ + platform_driver_unregister(&musb_driver); +} +module_exit(musb_cleanup); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h new file mode 100644 index 000000000000..90035c12ab5f --- /dev/null +++ b/drivers/usb/musb/musb_core.h @@ -0,0 +1,517 @@ +/* + * MUSB OTG driver defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MUSB_CORE_H__ +#define __MUSB_CORE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct musb; +struct musb_hw_ep; +struct musb_ep; + + +#include "musb_debug.h" +#include "musb_dma.h" + +#ifdef CONFIG_USB_MUSB_SOC +/* + * Get core configuration from a header converted (by cfg_conv) + * from the Verilog config file generated by the core config utility + * + * For now we assume that header is provided along with other + * arch-specific files. Discrete chips will need a build tweak. + * So will using AHB IDs from silicon that provides them. + */ +#include +#endif + +#include "musb_io.h" +#include "musb_regs.h" + +#include "musb_gadget.h" +#include "../core/hcd.h" +#include "musb_host.h" + + + +#ifdef CONFIG_USB_MUSB_OTG + +#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST) +#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL) +#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG) + +/* NOTE: otg and peripheral-only state machines start at B_IDLE. + * OTG or host-only go to A_IDLE when ID is sensed. + */ +#define is_peripheral_active(m) (!(m)->is_host) +#define is_host_active(m) ((m)->is_host) + +#else +#define is_peripheral_enabled(musb) is_peripheral_capable() +#define is_host_enabled(musb) is_host_capable() +#define is_otg_enabled(musb) 0 + +#define is_peripheral_active(musb) is_peripheral_capable() +#define is_host_active(musb) is_host_capable() +#endif + +#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL) +/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always + * override that choice selection (often USB_GADGET_DUMMY_HCD). + */ +#ifndef CONFIG_USB_GADGET_MUSB_HDRC +#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC +#endif +#endif /* need MUSB gadget selection */ + + +#ifdef CONFIG_PROC_FS +#include +#define MUSB_CONFIG_PROC_FS +#endif + +/****************************** PERIPHERAL ROLE *****************************/ + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + +#define is_peripheral_capable() (1) + +extern irqreturn_t musb_g_ep0_irq(struct musb *); +extern void musb_g_tx(struct musb *, u8); +extern void musb_g_rx(struct musb *, u8); +extern void musb_g_reset(struct musb *); +extern void musb_g_suspend(struct musb *); +extern void musb_g_resume(struct musb *); +extern void musb_g_wakeup(struct musb *); +extern void musb_g_disconnect(struct musb *); + +#else + +#define is_peripheral_capable() (0) + +static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; } +static inline void musb_g_reset(struct musb *m) {} +static inline void musb_g_suspend(struct musb *m) {} +static inline void musb_g_resume(struct musb *m) {} +static inline void musb_g_wakeup(struct musb *m) {} +static inline void musb_g_disconnect(struct musb *m) {} + +#endif + +/****************************** HOST ROLE ***********************************/ + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + +#define is_host_capable() (1) + +extern irqreturn_t musb_h_ep0_irq(struct musb *); +extern void musb_host_tx(struct musb *, u8); +extern void musb_host_rx(struct musb *, u8); + +#else + +#define is_host_capable() (0) + +static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; } +static inline void musb_host_tx(struct musb *m, u8 e) {} +static inline void musb_host_rx(struct musb *m, u8 e) {} + +#endif + + +/****************************** CONSTANTS ********************************/ + +#ifndef MUSB_C_NUM_EPS +#define MUSB_C_NUM_EPS ((u8)16) +#endif + +#ifndef MUSB_MAX_END0_PACKET +#define MUSB_MAX_END0_PACKET ((u16)MUSB_EP0_FIFOSIZE) +#endif + +/* host side ep0 states */ +enum musb_h_ep0_state { + MUSB_EP0_IDLE, + MUSB_EP0_START, /* expect ack of setup */ + MUSB_EP0_IN, /* expect IN DATA */ + MUSB_EP0_OUT, /* expect ack of OUT DATA */ + MUSB_EP0_STATUS, /* expect ack of STATUS */ +} __attribute__ ((packed)); + +/* peripheral side ep0 states */ +enum musb_g_ep0_state { + MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */ + MUSB_EP0_STAGE_TX, /* IN data */ + MUSB_EP0_STAGE_RX, /* OUT data */ + MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */ + MUSB_EP0_STAGE_STATUSOUT, /* (after IN data) */ + MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */ +} __attribute__ ((packed)); + +/* OTG protocol constants */ +#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */ +#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */ +#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */ + +/*************************** REGISTER ACCESS ********************************/ + +/* Endpoint registers (other than dynfifo setup) can be accessed either + * directly with the "flat" model, or after setting up an index register. + */ + +#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \ + || defined(CONFIG_ARCH_OMAP3430) +/* REVISIT indexed access seemed to + * misbehave (on DaVinci) for at least peripheral IN ... + */ +#define MUSB_FLAT_REG +#endif + +/* TUSB mapping: "flat" plus ep0 special cases */ +#if defined(CONFIG_USB_TUSB6010) +#define musb_ep_select(_mbase, _epnum) \ + musb_writeb((_mbase), MUSB_INDEX, (_epnum)) +#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET + +/* "flat" mapping: each endpoint has its own i/o address */ +#elif defined(MUSB_FLAT_REG) +#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum))) +#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET + +/* "indexed" mapping: INDEX register controls register bank select */ +#else +#define musb_ep_select(_mbase, _epnum) \ + musb_writeb((_mbase), MUSB_INDEX, (_epnum)) +#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET +#endif + +/****************************** FUNCTIONS ********************************/ + +#define MUSB_HST_MODE(_musb)\ + { (_musb)->is_host = true; } +#define MUSB_DEV_MODE(_musb) \ + { (_musb)->is_host = false; } + +#define test_devctl_hst_mode(_x) \ + (musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM) + +#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral") + +/******************************** TYPES *************************************/ + +/* + * struct musb_hw_ep - endpoint hardware (bidirectional) + * + * Ordered slightly for better cacheline locality. + */ +struct musb_hw_ep { + struct musb *musb; + void __iomem *fifo; + void __iomem *regs; + +#ifdef CONFIG_USB_TUSB6010 + void __iomem *conf; +#endif + + /* index in musb->endpoints[] */ + u8 epnum; + + /* hardware configuration, possibly dynamic */ + bool is_shared_fifo; + bool tx_double_buffered; + bool rx_double_buffered; + u16 max_packet_sz_tx; + u16 max_packet_sz_rx; + + struct dma_channel *tx_channel; + struct dma_channel *rx_channel; + +#ifdef CONFIG_USB_TUSB6010 + /* TUSB has "asynchronous" and "synchronous" dma modes */ + dma_addr_t fifo_async; + dma_addr_t fifo_sync; + void __iomem *fifo_sync_va; +#endif + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + void __iomem *target_regs; + + /* currently scheduled peripheral endpoint */ + struct musb_qh *in_qh; + struct musb_qh *out_qh; + + u8 rx_reinit; + u8 tx_reinit; +#endif + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + /* peripheral side */ + struct musb_ep ep_in; /* TX */ + struct musb_ep ep_out; /* RX */ +#endif +}; + +static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep) +{ +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + return next_request(&hw_ep->ep_in); +#else + return NULL; +#endif +} + +static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep) +{ +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + return next_request(&hw_ep->ep_out); +#else + return NULL; +#endif +} + +/* + * struct musb - Driver instance data. + */ +struct musb { + /* device lock */ + spinlock_t lock; + struct clk *clock; + irqreturn_t (*isr)(int, void *); + struct work_struct irq_work; + +/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */ +#define MUSB_PORT_STAT_RESUME (1 << 31) + + u32 port1_status; + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + unsigned long rh_timer; + + enum musb_h_ep0_state ep0_stage; + + /* bulk traffic normally dedicates endpoint hardware, and each + * direction has its own ring of host side endpoints. + * we try to progress the transfer at the head of each endpoint's + * queue until it completes or NAKs too much; then we try the next + * endpoint. + */ + struct musb_hw_ep *bulk_ep; + + struct list_head control; /* of musb_qh */ + struct list_head in_bulk; /* of musb_qh */ + struct list_head out_bulk; /* of musb_qh */ + struct musb_qh *periodic[32]; /* tree of interrupt+iso */ +#endif + + /* called with IRQs blocked; ON/nonzero implies starting a session, + * and waiting at least a_wait_vrise_tmout. + */ + void (*board_set_vbus)(struct musb *, int is_on); + + struct dma_controller *dma_controller; + + struct device *controller; + void __iomem *ctrl_base; + void __iomem *mregs; + +#ifdef CONFIG_USB_TUSB6010 + dma_addr_t async; + dma_addr_t sync; + void __iomem *sync_va; +#endif + + /* passed down from chip/board specific irq handlers */ + u8 int_usb; + u16 int_rx; + u16 int_tx; + + struct otg_transceiver xceiv; + + int nIrq; + + struct musb_hw_ep endpoints[MUSB_C_NUM_EPS]; +#define control_ep endpoints + +#define VBUSERR_RETRY_COUNT 3 + u16 vbuserr_retry; + u16 epmask; + u8 nr_endpoints; + + u8 board_mode; /* enum musb_mode */ + int (*board_set_power)(int state); + + int (*set_clock)(struct clk *clk, int is_active); + + u8 min_power; /* vbus for periph, in mA/2 */ + + bool is_host; + + int a_wait_bcon; /* VBUS timeout in msecs */ + unsigned long idle_timeout; /* Next timeout in jiffies */ + + /* active means connected and not suspended */ + unsigned is_active:1; + + unsigned is_multipoint:1; + unsigned ignore_disconnect:1; /* during bus resets */ + +#ifdef C_MP_TX + unsigned bulk_split:1; +#define can_bulk_split(musb,type) \ + (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split) +#else +#define can_bulk_split(musb, type) 0 +#endif + +#ifdef C_MP_RX + unsigned bulk_combine:1; +#define can_bulk_combine(musb,type) \ + (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine) +#else +#define can_bulk_combine(musb, type) 0 +#endif + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + /* is_suspended means USB B_PERIPHERAL suspend */ + unsigned is_suspended:1; + + /* may_wakeup means remote wakeup is enabled */ + unsigned may_wakeup:1; + + /* is_self_powered is reported in device status and the + * config descriptor. is_bus_powered means B_PERIPHERAL + * draws some VBUS current; both can be true. + */ + unsigned is_self_powered:1; + unsigned is_bus_powered:1; + + unsigned set_address:1; + unsigned test_mode:1; + unsigned softconnect:1; + + u8 address; + u8 test_mode_nr; + u16 ackpend; /* ep0 */ + enum musb_g_ep0_state ep0_state; + struct usb_gadget g; /* the gadget */ + struct usb_gadget_driver *gadget_driver; /* its driver */ +#endif + +#ifdef MUSB_CONFIG_PROC_FS + struct proc_dir_entry *proc_entry; +#endif +}; + +static inline void musb_set_vbus(struct musb *musb, int is_on) +{ + musb->board_set_vbus(musb, is_on); +} + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC +static inline struct musb *gadget_to_musb(struct usb_gadget *g) +{ + return container_of(g, struct musb, g); +} +#endif + + +/***************************** Glue it together *****************************/ + +extern const char musb_driver_name[]; + +extern void musb_start(struct musb *musb); +extern void musb_stop(struct musb *musb); + +extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src); +extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst); + +extern void musb_load_testpacket(struct musb *); + +extern irqreturn_t musb_interrupt(struct musb *); + +extern void musb_platform_enable(struct musb *musb); +extern void musb_platform_disable(struct musb *musb); + +extern void musb_hnp_stop(struct musb *musb); + +extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode); + +#if defined(CONFIG_USB_TUSB6010) || \ + defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout); +#else +#define musb_platform_try_idle(x, y) do {} while (0) +#endif + +#ifdef CONFIG_USB_TUSB6010 +extern int musb_platform_get_vbus_status(struct musb *musb); +#else +#define musb_platform_get_vbus_status(x) 0 +#endif + +extern int __init musb_platform_init(struct musb *musb); +extern int musb_platform_exit(struct musb *musb); + +/*-------------------------- ProcFS definitions ---------------------*/ + +struct proc_dir_entry; + +#if (MUSB_DEBUG > 0) && defined(MUSB_CONFIG_PROC_FS) +extern struct proc_dir_entry *musb_debug_create(char *name, struct musb *data); +extern void musb_debug_delete(char *name, struct musb *data); + +#else +static inline struct proc_dir_entry * +musb_debug_create(char *name, struct musb *data) +{ + return NULL; +} +static inline void musb_debug_delete(char *name, struct musb *data) +{ +} +#endif + +#endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h new file mode 100644 index 000000000000..3bdb311e820d --- /dev/null +++ b/drivers/usb/musb/musb_debug.h @@ -0,0 +1,66 @@ +/* + * MUSB OTG driver debug defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MUSB_LINUX_DEBUG_H__ +#define __MUSB_LINUX_DEBUG_H__ + +#define yprintk(facility, format, args...) \ + do { printk(facility "%s %d: " format , \ + __func__, __LINE__ , ## args); } while (0) +#define WARNING(fmt, args...) yprintk(KERN_WARNING, fmt, ## args) +#define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args) +#define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args) + +#define xprintk(level, facility, format, args...) do { \ + if (_dbg_level(level)) { \ + printk(facility "%s %d: " format , \ + __func__, __LINE__ , ## args); \ + } } while (0) + +#if MUSB_DEBUG > 0 +extern unsigned debug; +#else +#define debug 0 +#endif + +static inline int _dbg_level(unsigned l) +{ + return debug >= l; +} + +#define DBG(level, fmt, args...) xprintk(level, KERN_DEBUG, fmt, ## args) + +extern const char *otg_state_string(struct musb *); + +#endif /* __MUSB_LINUX_DEBUG_H__ */ diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h new file mode 100644 index 000000000000..0a2c4e3602c1 --- /dev/null +++ b/drivers/usb/musb/musb_dma.h @@ -0,0 +1,172 @@ +/* + * MUSB OTG driver DMA controller abstraction + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MUSB_DMA_H__ +#define __MUSB_DMA_H__ + +struct musb_hw_ep; + +/* + * DMA Controller Abstraction + * + * DMA Controllers are abstracted to allow use of a variety of different + * implementations of DMA, as allowed by the Inventra USB cores. On the + * host side, usbcore sets up the DMA mappings and flushes caches; on the + * peripheral side, the gadget controller driver does. Responsibilities + * of a DMA controller driver include: + * + * - Handling the details of moving multiple USB packets + * in cooperation with the Inventra USB core, including especially + * the correct RX side treatment of short packets and buffer-full + * states (both of which terminate transfers). + * + * - Knowing the correlation between dma channels and the + * Inventra core's local endpoint resources and data direction. + * + * - Maintaining a list of allocated/available channels. + * + * - Updating channel status on interrupts, + * whether shared with the Inventra core or separate. + */ + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#ifndef CONFIG_MUSB_PIO_ONLY +#define is_dma_capable() (1) +#else +#define is_dma_capable() (0) +#endif + +#ifdef CONFIG_USB_TI_CPPI_DMA +#define is_cppi_enabled() 1 +#else +#define is_cppi_enabled() 0 +#endif + +#ifdef CONFIG_USB_TUSB_OMAP_DMA +#define tusb_dma_omap() 1 +#else +#define tusb_dma_omap() 0 +#endif + +/* + * DMA channel status ... updated by the dma controller driver whenever that + * status changes, and protected by the overall controller spinlock. + */ +enum dma_channel_status { + /* unallocated */ + MUSB_DMA_STATUS_UNKNOWN, + /* allocated ... but not busy, no errors */ + MUSB_DMA_STATUS_FREE, + /* busy ... transactions are active */ + MUSB_DMA_STATUS_BUSY, + /* transaction(s) aborted due to ... dma or memory bus error */ + MUSB_DMA_STATUS_BUS_ABORT, + /* transaction(s) aborted due to ... core error or USB fault */ + MUSB_DMA_STATUS_CORE_ABORT +}; + +struct dma_controller; + +/** + * struct dma_channel - A DMA channel. + * @private_data: channel-private data + * @max_len: the maximum number of bytes the channel can move in one + * transaction (typically representing many USB maximum-sized packets) + * @actual_len: how many bytes have been transferred + * @status: current channel status (updated e.g. on interrupt) + * @desired_mode: true if mode 1 is desired; false if mode 0 is desired + * + * channels are associated with an endpoint for the duration of at least + * one usb transfer. + */ +struct dma_channel { + void *private_data; + /* FIXME not void* private_data, but a dma_controller * */ + size_t max_len; + size_t actual_len; + enum dma_channel_status status; + bool desired_mode; +}; + +/* + * dma_channel_status - return status of dma channel + * @c: the channel + * + * Returns the software's view of the channel status. If that status is BUSY + * then it's possible that the hardware has completed (or aborted) a transfer, + * so the driver needs to update that status. + */ +static inline enum dma_channel_status +dma_channel_status(struct dma_channel *c) +{ + return (is_dma_capable() && c) ? c->status : MUSB_DMA_STATUS_UNKNOWN; +} + +/** + * struct dma_controller - A DMA Controller. + * @start: call this to start a DMA controller; + * return 0 on success, else negative errno + * @stop: call this to stop a DMA controller + * return 0 on success, else negative errno + * @channel_alloc: call this to allocate a DMA channel + * @channel_release: call this to release a DMA channel + * @channel_abort: call this to abort a pending DMA transaction, + * returning it to FREE (but allocated) state + * + * Controllers manage dma channels. + */ +struct dma_controller { + int (*start)(struct dma_controller *); + int (*stop)(struct dma_controller *); + struct dma_channel *(*channel_alloc)(struct dma_controller *, + struct musb_hw_ep *, u8 is_tx); + void (*channel_release)(struct dma_channel *); + int (*channel_program)(struct dma_channel *channel, + u16 maxpacket, u8 mode, + dma_addr_t dma_addr, + u32 length); + int (*channel_abort)(struct dma_channel *); +}; + +/* called after channel_program(), may indicate a fault */ +extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); + + +extern struct dma_controller *__init +dma_controller_create(struct musb *, void __iomem *); + +extern void dma_controller_destroy(struct dma_controller *); + +#endif /* __MUSB_DMA_H__ */ diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c new file mode 100644 index 000000000000..b3773f13ee0a --- /dev/null +++ b/drivers/usb/musb/musb_gadget.c @@ -0,0 +1,2033 @@ +/* + * MUSB OTG driver peripheral support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + + +/* MUSB PERIPHERAL status 3-mar-2006: + * + * - EP0 seems solid. It passes both USBCV and usbtest control cases. + * Minor glitches: + * + * + remote wakeup to Linux hosts work, but saw USBCV failures; + * in one test run (operator error?) + * + endpoint halt tests -- in both usbtest and usbcv -- seem + * to break when dma is enabled ... is something wrongly + * clearing SENDSTALL? + * + * - Mass storage behaved ok when last tested. Network traffic patterns + * (with lots of short transfers etc) need retesting; they turn up the + * worst cases of the DMA, since short packets are typical but are not + * required. + * + * - TX/IN + * + both pio and dma behave in with network and g_zero tests + * + no cppi throughput issues other than no-hw-queueing + * + failed with FLAT_REG (DaVinci) + * + seems to behave with double buffering, PIO -and- CPPI + * + with gadgetfs + AIO, requests got lost? + * + * - RX/OUT + * + both pio and dma behave in with network and g_zero tests + * + dma is slow in typical case (short_not_ok is clear) + * + double buffering ok with PIO + * + double buffering *FAILS* with CPPI, wrong data bytes sometimes + * + request lossage observed with gadgetfs + * + * - ISO not tested ... might work, but only weakly isochronous + * + * - Gadget driver disabling of softconnect during bind() is ignored; so + * drivers can't hold off host requests until userspace is ready. + * (Workaround: they can turn it off later.) + * + * - PORTABILITY (assumes PIO works): + * + DaVinci, basically works with cppi dma + * + OMAP 2430, ditto with mentor dma + * + TUSB 6010, platform-specific dma in the works + */ + +/* ----------------------------------------------------------------------- */ + +/* + * Immediately complete a request. + * + * @param request the request to complete + * @param status the status to complete the request with + * Context: controller locked, IRQs blocked. + */ +void musb_g_giveback( + struct musb_ep *ep, + struct usb_request *request, + int status) +__releases(ep->musb->lock) +__acquires(ep->musb->lock) +{ + struct musb_request *req; + struct musb *musb; + int busy = ep->busy; + + req = to_musb_request(request); + + list_del(&request->list); + if (req->request.status == -EINPROGRESS) + req->request.status = status; + musb = req->musb; + + ep->busy = 1; + spin_unlock(&musb->lock); + if (is_dma_capable()) { + if (req->mapped) { + dma_unmap_single(musb->controller, + req->request.dma, + req->request.length, + req->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->request.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else if (req->request.dma != DMA_ADDR_INVALID) + dma_sync_single_for_cpu(musb->controller, + req->request.dma, + req->request.length, + req->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + } + if (request->status == 0) + DBG(5, "%s done request %p, %d/%d\n", + ep->end_point.name, request, + req->request.actual, req->request.length); + else + DBG(2, "%s request %p, %d/%d fault %d\n", + ep->end_point.name, request, + req->request.actual, req->request.length, + request->status); + req->request.complete(&req->ep->end_point, &req->request); + spin_lock(&musb->lock); + ep->busy = busy; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Abort requests queued to an endpoint using the status. Synchronous. + * caller locked controller and blocked irqs, and selected this ep. + */ +static void nuke(struct musb_ep *ep, const int status) +{ + struct musb_request *req = NULL; + void __iomem *epio = ep->musb->endpoints[ep->current_epnum].regs; + + ep->busy = 1; + + if (is_dma_capable() && ep->dma) { + struct dma_controller *c = ep->musb->dma_controller; + int value; + if (ep->is_in) { + musb_writew(epio, MUSB_TXCSR, + 0 | MUSB_TXCSR_FLUSHFIFO); + musb_writew(epio, MUSB_TXCSR, + 0 | MUSB_TXCSR_FLUSHFIFO); + } else { + musb_writew(epio, MUSB_RXCSR, + 0 | MUSB_RXCSR_FLUSHFIFO); + musb_writew(epio, MUSB_RXCSR, + 0 | MUSB_RXCSR_FLUSHFIFO); + } + + value = c->channel_abort(ep->dma); + DBG(value ? 1 : 6, "%s: abort DMA --> %d\n", ep->name, value); + c->channel_release(ep->dma); + ep->dma = NULL; + } + + while (!list_empty(&(ep->req_list))) { + req = container_of(ep->req_list.next, struct musb_request, + request.list); + musb_g_giveback(ep, &req->request, status); + } +} + +/* ----------------------------------------------------------------------- */ + +/* Data transfers - pure PIO, pure DMA, or mixed mode */ + +/* + * This assumes the separate CPPI engine is responding to DMA requests + * from the usb core ... sequenced a bit differently from mentor dma. + */ + +static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep) +{ + if (can_bulk_split(musb, ep->type)) + return ep->hw_ep->max_packet_sz_tx; + else + return ep->packet_sz; +} + + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Peripheral tx (IN) using Mentor DMA works as follows: + Only mode 0 is used for transfers <= wPktSize, + mode 1 is used for larger transfers, + + One of the following happens: + - Host sends IN token which causes an endpoint interrupt + -> TxAvail + -> if DMA is currently busy, exit. + -> if queue is non-empty, txstate(). + + - Request is queued by the gadget driver. + -> if queue was previously empty, txstate() + + txstate() + -> start + /\ -> setup DMA + | (data is transferred to the FIFO, then sent out when + | IN token(s) are recd from Host. + | -> DMA interrupt on completion + | calls TxAvail. + | -> stop DMA, ~DmaEenab, + | -> set TxPktRdy for last short pkt or zlp + | -> Complete Request + | -> Continue next request (call txstate) + |___________________________________| + + * Non-Mentor DMA engines can of course work differently, such as by + * upleveling from irq-per-packet to irq-per-buffer. + */ + +#endif + +/* + * An endpoint is transmitting data. This can be called either from + * the IRQ routine or from ep.queue() to kickstart a request on an + * endpoint. + * + * Context: controller locked, IRQs blocked, endpoint selected + */ +static void txstate(struct musb *musb, struct musb_request *req) +{ + u8 epnum = req->epnum; + struct musb_ep *musb_ep; + void __iomem *epio = musb->endpoints[epnum].regs; + struct usb_request *request; + u16 fifo_count = 0, csr; + int use_dma = 0; + + musb_ep = req->ep; + + /* we shouldn't get here while DMA is active ... but we do ... */ + if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { + DBG(4, "dma pending...\n"); + return; + } + + /* read TXCSR before */ + csr = musb_readw(epio, MUSB_TXCSR); + + request = &req->request; + fifo_count = min(max_ep_writesize(musb, musb_ep), + (int)(request->length - request->actual)); + + if (csr & MUSB_TXCSR_TXPKTRDY) { + DBG(5, "%s old packet still ready , txcsr %03x\n", + musb_ep->end_point.name, csr); + return; + } + + if (csr & MUSB_TXCSR_P_SENDSTALL) { + DBG(5, "%s stalling, txcsr %03x\n", + musb_ep->end_point.name, csr); + return; + } + + DBG(4, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n", + epnum, musb_ep->packet_sz, fifo_count, + csr); + +#ifndef CONFIG_MUSB_PIO_ONLY + if (is_dma_capable() && musb_ep->dma) { + struct dma_controller *c = musb->dma_controller; + + use_dma = (request->dma != DMA_ADDR_INVALID); + + /* MUSB_TXCSR_P_ISO is still set correctly */ + +#ifdef CONFIG_USB_INVENTRA_DMA + { + size_t request_size; + + /* setup DMA, then program endpoint CSR */ + request_size = min(request->length, + musb_ep->dma->max_len); + if (request_size <= musb_ep->packet_sz) + musb_ep->dma->desired_mode = 0; + else + musb_ep->dma->desired_mode = 1; + + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + musb_ep->dma->desired_mode, + request->dma, request_size); + if (use_dma) { + if (musb_ep->dma->desired_mode == 0) { + /* ASSERT: DMAENAB is clear */ + csr &= ~(MUSB_TXCSR_AUTOSET | + MUSB_TXCSR_DMAMODE); + csr |= (MUSB_TXCSR_DMAENAB | + MUSB_TXCSR_MODE); + /* against programming guide */ + } else + csr |= (MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_MODE); + + csr &= ~MUSB_TXCSR_P_UNDERRUN; + musb_writew(epio, MUSB_TXCSR, csr); + } + } + +#elif defined(CONFIG_USB_TI_CPPI_DMA) + /* program endpoint CSR first, then setup DMA */ + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_P_UNDERRUN + | MUSB_TXCSR_TXPKTRDY); + csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB; + musb_writew(epio, MUSB_TXCSR, + (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN) + | csr); + + /* ensure writebuffer is empty */ + csr = musb_readw(epio, MUSB_TXCSR); + + /* NOTE host side sets DMAENAB later than this; both are + * OK since the transfer dma glue (between CPPI and Mentor + * fifos) just tells CPPI it could start. Data only moves + * to the USB TX fifo when both fifos are ready. + */ + + /* "mode" is irrelevant here; handle terminating ZLPs like + * PIO does, since the hardware RNDIS mode seems unreliable + * except for the last-packet-is-already-short case. + */ + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + 0, + request->dma, + request->length); + if (!use_dma) { + c->channel_release(musb_ep->dma); + musb_ep->dma = NULL; + /* ASSERT: DMAENAB clear */ + csr &= ~(MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE); + /* invariant: prequest->buf is non-null */ + } +#elif defined(CONFIG_USB_TUSB_OMAP_DMA) + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + request->zero, + request->dma, + request->length); +#endif + } +#endif + + if (!use_dma) { + musb_write_fifo(musb_ep->hw_ep, fifo_count, + (u8 *) (request->buf + request->actual)); + request->actual += fifo_count; + csr |= MUSB_TXCSR_TXPKTRDY; + csr &= ~MUSB_TXCSR_P_UNDERRUN; + musb_writew(epio, MUSB_TXCSR, csr); + } + + /* host may already have the data when this message shows... */ + DBG(3, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n", + musb_ep->end_point.name, use_dma ? "dma" : "pio", + request->actual, request->length, + musb_readw(epio, MUSB_TXCSR), + fifo_count, + musb_readw(epio, MUSB_TXMAXP)); +} + +/* + * FIFO state update (e.g. data ready). + * Called from IRQ, with controller locked. + */ +void musb_g_tx(struct musb *musb, u8 epnum) +{ + u16 csr; + struct usb_request *request; + u8 __iomem *mbase = musb->mregs; + struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in; + void __iomem *epio = musb->endpoints[epnum].regs; + struct dma_channel *dma; + + musb_ep_select(mbase, epnum); + request = next_request(musb_ep); + + csr = musb_readw(epio, MUSB_TXCSR); + DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); + + dma = is_dma_capable() ? musb_ep->dma : NULL; + do { + /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX + * probably rates reporting as a host error + */ + if (csr & MUSB_TXCSR_P_SENTSTALL) { + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~MUSB_TXCSR_P_SENTSTALL; + musb_writew(epio, MUSB_TXCSR, csr); + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + musb->dma_controller->channel_abort(dma); + } + + if (request) + musb_g_giveback(musb_ep, request, -EPIPE); + + break; + } + + if (csr & MUSB_TXCSR_P_UNDERRUN) { + /* we NAKed, no big deal ... little reason to care */ + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_P_UNDERRUN + | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); + DBG(20, "underrun on ep%d, req %p\n", epnum, request); + } + + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + /* SHOULD NOT HAPPEN ... has with cppi though, after + * changing SENDSTALL (and other cases); harmless? + */ + DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); + break; + } + + if (request) { + u8 is_dma = 0; + + if (dma && (csr & MUSB_TXCSR_DMAENAB)) { + is_dma = 1; + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_P_UNDERRUN + | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); + /* ensure writebuffer is empty */ + csr = musb_readw(epio, MUSB_TXCSR); + request->actual += musb_ep->dma->actual_len; + DBG(4, "TXCSR%d %04x, dma off, " + "len %zu, req %p\n", + epnum, csr, + musb_ep->dma->actual_len, + request); + } + + if (is_dma || request->actual == request->length) { + + /* First, maybe a terminating short packet. + * Some DMA engines might handle this by + * themselves. + */ + if ((request->zero + && request->length + && (request->length + % musb_ep->packet_sz) + == 0) +#ifdef CONFIG_USB_INVENTRA_DMA + || (is_dma && + ((!dma->desired_mode) || + (request->actual & + (musb_ep->packet_sz - 1)))) +#endif + ) { + /* on dma completion, fifo may not + * be available yet ... + */ + if (csr & MUSB_TXCSR_TXPKTRDY) + break; + + DBG(4, "sending zero pkt\n"); + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_MODE + | MUSB_TXCSR_TXPKTRDY); + request->zero = 0; + } + + /* ... or if not, then complete it */ + musb_g_giveback(musb_ep, request, 0); + + /* kickstart next transfer if appropriate; + * the packet that just completed might not + * be transmitted for hours or days. + * REVISIT for double buffering... + * FIXME revisit for stalls too... + */ + musb_ep_select(mbase, epnum); + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) + break; + request = musb_ep->desc + ? next_request(musb_ep) + : NULL; + if (!request) { + DBG(4, "%s idle now\n", + musb_ep->end_point.name); + break; + } + } + + txstate(musb, to_musb_request(request)); + } + + } while (0); +} + +/* ------------------------------------------------------------ */ + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Peripheral rx (OUT) using Mentor DMA works as follows: + - Only mode 0 is used. + + - Request is queued by the gadget class driver. + -> if queue was previously empty, rxstate() + + - Host sends OUT token which causes an endpoint interrupt + /\ -> RxReady + | -> if request queued, call rxstate + | /\ -> setup DMA + | | -> DMA interrupt on completion + | | -> RxReady + | | -> stop DMA + | | -> ack the read + | | -> if data recd = max expected + | | by the request, or host + | | sent a short packet, + | | complete the request, + | | and start the next one. + | |_____________________________________| + | else just wait for the host + | to send the next OUT token. + |__________________________________________________| + + * Non-Mentor DMA engines can of course work differently. + */ + +#endif + +/* + * Context: controller locked, IRQs blocked, endpoint selected + */ +static void rxstate(struct musb *musb, struct musb_request *req) +{ + u16 csr = 0; + const u8 epnum = req->epnum; + struct usb_request *request = &req->request; + struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; + void __iomem *epio = musb->endpoints[epnum].regs; + u16 fifo_count = 0; + u16 len = musb_ep->packet_sz; + + csr = musb_readw(epio, MUSB_RXCSR); + + if (is_cppi_enabled() && musb_ep->dma) { + struct dma_controller *c = musb->dma_controller; + struct dma_channel *channel = musb_ep->dma; + + /* NOTE: CPPI won't actually stop advancing the DMA + * queue after short packet transfers, so this is almost + * always going to run as IRQ-per-packet DMA so that + * faults will be handled correctly. + */ + if (c->channel_program(channel, + musb_ep->packet_sz, + !request->short_not_ok, + request->dma + request->actual, + request->length - request->actual)) { + + /* make sure that if an rxpkt arrived after the irq, + * the cppi engine will be ready to take it as soon + * as DMA is enabled + */ + csr &= ~(MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_DMAMODE); + csr |= MUSB_RXCSR_DMAENAB | MUSB_RXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + return; + } + } + + if (csr & MUSB_RXCSR_RXPKTRDY) { + len = musb_readw(epio, MUSB_RXCOUNT); + if (request->actual < request->length) { +#ifdef CONFIG_USB_INVENTRA_DMA + if (is_dma_capable() && musb_ep->dma) { + struct dma_controller *c; + struct dma_channel *channel; + int use_dma = 0; + + c = musb->dma_controller; + channel = musb_ep->dma; + + /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in + * mode 0 only. So we do not get endpoint interrupts due to DMA + * completion. We only get interrupts from DMA controller. + * + * We could operate in DMA mode 1 if we knew the size of the tranfer + * in advance. For mass storage class, request->length = what the host + * sends, so that'd work. But for pretty much everything else, + * request->length is routinely more than what the host sends. For + * most these gadgets, end of is signified either by a short packet, + * or filling the last byte of the buffer. (Sending extra data in + * that last pckate should trigger an overflow fault.) But in mode 1, + * we don't get DMA completion interrrupt for short packets. + * + * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1), + * to get endpoint interrupt on every DMA req, but that didn't seem + * to work reliably. + * + * REVISIT an updated g_file_storage can set req->short_not_ok, which + * then becomes usable as a runtime "use mode 1" hint... + */ + + csr |= MUSB_RXCSR_DMAENAB; +#ifdef USE_MODE1 + csr |= MUSB_RXCSR_AUTOCLEAR; + /* csr |= MUSB_RXCSR_DMAMODE; */ + + /* this special sequence (enabling and then + * disabling MUSB_RXCSR_DMAMODE) is required + * to get DMAReq to activate + */ + musb_writew(epio, MUSB_RXCSR, + csr | MUSB_RXCSR_DMAMODE); +#endif + musb_writew(epio, MUSB_RXCSR, csr); + + if (request->actual < request->length) { + int transfer_size = 0; +#ifdef USE_MODE1 + transfer_size = min(request->length, + channel->max_len); +#else + transfer_size = len; +#endif + if (transfer_size <= musb_ep->packet_sz) + musb_ep->dma->desired_mode = 0; + else + musb_ep->dma->desired_mode = 1; + + use_dma = c->channel_program( + channel, + musb_ep->packet_sz, + channel->desired_mode, + request->dma + + request->actual, + transfer_size); + } + + if (use_dma) + return; + } +#endif /* Mentor's DMA */ + + fifo_count = request->length - request->actual; + DBG(3, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", + musb_ep->end_point.name, + len, fifo_count, + musb_ep->packet_sz); + + fifo_count = min(len, fifo_count); + +#ifdef CONFIG_USB_TUSB_OMAP_DMA + if (tusb_dma_omap() && musb_ep->dma) { + struct dma_controller *c = musb->dma_controller; + struct dma_channel *channel = musb_ep->dma; + u32 dma_addr = request->dma + request->actual; + int ret; + + ret = c->channel_program(channel, + musb_ep->packet_sz, + channel->desired_mode, + dma_addr, + fifo_count); + if (ret) + return; + } +#endif + + musb_read_fifo(musb_ep->hw_ep, fifo_count, (u8 *) + (request->buf + request->actual)); + request->actual += fifo_count; + + /* REVISIT if we left anything in the fifo, flush + * it and report -EOVERFLOW + */ + + /* ack the read! */ + csr |= MUSB_RXCSR_P_WZC_BITS; + csr &= ~MUSB_RXCSR_RXPKTRDY; + musb_writew(epio, MUSB_RXCSR, csr); + } + } + + /* reach the end or short packet detected */ + if (request->actual == request->length || len < musb_ep->packet_sz) + musb_g_giveback(musb_ep, request, 0); +} + +/* + * Data ready for a request; called from IRQ + */ +void musb_g_rx(struct musb *musb, u8 epnum) +{ + u16 csr; + struct usb_request *request; + void __iomem *mbase = musb->mregs; + struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; + void __iomem *epio = musb->endpoints[epnum].regs; + struct dma_channel *dma; + + musb_ep_select(mbase, epnum); + + request = next_request(musb_ep); + + csr = musb_readw(epio, MUSB_RXCSR); + dma = is_dma_capable() ? musb_ep->dma : NULL; + + DBG(4, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name, + csr, dma ? " (dma)" : "", request); + + if (csr & MUSB_RXCSR_P_SENTSTALL) { + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + request->actual += musb_ep->dma->actual_len; + } + + csr |= MUSB_RXCSR_P_WZC_BITS; + csr &= ~MUSB_RXCSR_P_SENTSTALL; + musb_writew(epio, MUSB_RXCSR, csr); + + if (request) + musb_g_giveback(musb_ep, request, -EPIPE); + goto done; + } + + if (csr & MUSB_RXCSR_P_OVERRUN) { + /* csr |= MUSB_RXCSR_P_WZC_BITS; */ + csr &= ~MUSB_RXCSR_P_OVERRUN; + musb_writew(epio, MUSB_RXCSR, csr); + + DBG(3, "%s iso overrun on %p\n", musb_ep->name, request); + if (request && request->status == -EINPROGRESS) + request->status = -EOVERFLOW; + } + if (csr & MUSB_RXCSR_INCOMPRX) { + /* REVISIT not necessarily an error */ + DBG(4, "%s, incomprx\n", musb_ep->end_point.name); + } + + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + /* "should not happen"; likely RXPKTRDY pending for DMA */ + DBG((csr & MUSB_RXCSR_DMAENAB) ? 4 : 1, + "%s busy, csr %04x\n", + musb_ep->end_point.name, csr); + goto done; + } + + if (dma && (csr & MUSB_RXCSR_DMAENAB)) { + csr &= ~(MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_P_WZC_BITS | csr); + + request->actual += musb_ep->dma->actual_len; + + DBG(4, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n", + epnum, csr, + musb_readw(epio, MUSB_RXCSR), + musb_ep->dma->actual_len, request); + +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) + /* Autoclear doesn't clear RxPktRdy for short packets */ + if ((dma->desired_mode == 0) + || (dma->actual_len + & (musb_ep->packet_sz - 1))) { + /* ack the read! */ + csr &= ~MUSB_RXCSR_RXPKTRDY; + musb_writew(epio, MUSB_RXCSR, csr); + } + + /* incomplete, and not short? wait for next IN packet */ + if ((request->actual < request->length) + && (musb_ep->dma->actual_len + == musb_ep->packet_sz)) + goto done; +#endif + musb_g_giveback(musb_ep, request, 0); + + request = next_request(musb_ep); + if (!request) + goto done; + + /* don't start more i/o till the stall clears */ + musb_ep_select(mbase, epnum); + csr = musb_readw(epio, MUSB_RXCSR); + if (csr & MUSB_RXCSR_P_SENDSTALL) + goto done; + } + + + /* analyze request if the ep is hot */ + if (request) + rxstate(musb, to_musb_request(request)); + else + DBG(3, "packet waiting for %s%s request\n", + musb_ep->desc ? "" : "inactive ", + musb_ep->end_point.name); + +done: + return; +} + +/* ------------------------------------------------------------ */ + +static int musb_gadget_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + unsigned long flags; + struct musb_ep *musb_ep; + struct musb_hw_ep *hw_ep; + void __iomem *regs; + struct musb *musb; + void __iomem *mbase; + u8 epnum; + u16 csr; + unsigned tmp; + int status = -EINVAL; + + if (!ep || !desc) + return -EINVAL; + + musb_ep = to_musb_ep(ep); + hw_ep = musb_ep->hw_ep; + regs = hw_ep->regs; + musb = musb_ep->musb; + mbase = musb->mregs; + epnum = musb_ep->current_epnum; + + spin_lock_irqsave(&musb->lock, flags); + + if (musb_ep->desc) { + status = -EBUSY; + goto fail; + } + musb_ep->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* check direction and (later) maxpacket size against endpoint */ + if ((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != epnum) + goto fail; + + /* REVISIT this rules out high bandwidth periodic transfers */ + tmp = le16_to_cpu(desc->wMaxPacketSize); + if (tmp & ~0x07ff) + goto fail; + musb_ep->packet_sz = tmp; + + /* enable the interrupts for the endpoint, set the endpoint + * packet size (or fail), set the mode, clear the fifo + */ + musb_ep_select(mbase, epnum); + if (desc->bEndpointAddress & USB_DIR_IN) { + u16 int_txe = musb_readw(mbase, MUSB_INTRTXE); + + if (hw_ep->is_shared_fifo) + musb_ep->is_in = 1; + if (!musb_ep->is_in) + goto fail; + if (tmp > hw_ep->max_packet_sz_tx) + goto fail; + + int_txe |= (1 << epnum); + musb_writew(mbase, MUSB_INTRTXE, int_txe); + + /* REVISIT if can_bulk_split(), use by updating "tmp"; + * likewise high bandwidth periodic tx + */ + musb_writew(regs, MUSB_TXMAXP, tmp); + + csr = MUSB_TXCSR_MODE | MUSB_TXCSR_CLRDATATOG; + if (musb_readw(regs, MUSB_TXCSR) + & MUSB_TXCSR_FIFONOTEMPTY) + csr |= MUSB_TXCSR_FLUSHFIFO; + if (musb_ep->type == USB_ENDPOINT_XFER_ISOC) + csr |= MUSB_TXCSR_P_ISO; + + /* set twice in case of double buffering */ + musb_writew(regs, MUSB_TXCSR, csr); + /* REVISIT may be inappropriate w/o FIFONOTEMPTY ... */ + musb_writew(regs, MUSB_TXCSR, csr); + + } else { + u16 int_rxe = musb_readw(mbase, MUSB_INTRRXE); + + if (hw_ep->is_shared_fifo) + musb_ep->is_in = 0; + if (musb_ep->is_in) + goto fail; + if (tmp > hw_ep->max_packet_sz_rx) + goto fail; + + int_rxe |= (1 << epnum); + musb_writew(mbase, MUSB_INTRRXE, int_rxe); + + /* REVISIT if can_bulk_combine() use by updating "tmp" + * likewise high bandwidth periodic rx + */ + musb_writew(regs, MUSB_RXMAXP, tmp); + + /* force shared fifo to OUT-only mode */ + if (hw_ep->is_shared_fifo) { + csr = musb_readw(regs, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); + musb_writew(regs, MUSB_TXCSR, csr); + } + + csr = MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_CLRDATATOG; + if (musb_ep->type == USB_ENDPOINT_XFER_ISOC) + csr |= MUSB_RXCSR_P_ISO; + else if (musb_ep->type == USB_ENDPOINT_XFER_INT) + csr |= MUSB_RXCSR_DISNYET; + + /* set twice in case of double buffering */ + musb_writew(regs, MUSB_RXCSR, csr); + musb_writew(regs, MUSB_RXCSR, csr); + } + + /* NOTE: all the I/O code _should_ work fine without DMA, in case + * for some reason you run out of channels here. + */ + if (is_dma_capable() && musb->dma_controller) { + struct dma_controller *c = musb->dma_controller; + + musb_ep->dma = c->channel_alloc(c, hw_ep, + (desc->bEndpointAddress & USB_DIR_IN)); + } else + musb_ep->dma = NULL; + + musb_ep->desc = desc; + musb_ep->busy = 0; + status = 0; + + pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", + musb_driver_name, musb_ep->end_point.name, + ({ char *s; switch (musb_ep->type) { + case USB_ENDPOINT_XFER_BULK: s = "bulk"; break; + case USB_ENDPOINT_XFER_INT: s = "int"; break; + default: s = "iso"; break; + }; s; }), + musb_ep->is_in ? "IN" : "OUT", + musb_ep->dma ? "dma, " : "", + musb_ep->packet_sz); + + schedule_work(&musb->irq_work); + +fail: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +/* + * Disable an endpoint flushing all requests queued. + */ +static int musb_gadget_disable(struct usb_ep *ep) +{ + unsigned long flags; + struct musb *musb; + u8 epnum; + struct musb_ep *musb_ep; + void __iomem *epio; + int status = 0; + + musb_ep = to_musb_ep(ep); + musb = musb_ep->musb; + epnum = musb_ep->current_epnum; + epio = musb->endpoints[epnum].regs; + + spin_lock_irqsave(&musb->lock, flags); + musb_ep_select(musb->mregs, epnum); + + /* zero the endpoint sizes */ + if (musb_ep->is_in) { + u16 int_txe = musb_readw(musb->mregs, MUSB_INTRTXE); + int_txe &= ~(1 << epnum); + musb_writew(musb->mregs, MUSB_INTRTXE, int_txe); + musb_writew(epio, MUSB_TXMAXP, 0); + } else { + u16 int_rxe = musb_readw(musb->mregs, MUSB_INTRRXE); + int_rxe &= ~(1 << epnum); + musb_writew(musb->mregs, MUSB_INTRRXE, int_rxe); + musb_writew(epio, MUSB_RXMAXP, 0); + } + + musb_ep->desc = NULL; + + /* abort all pending DMA and requests */ + nuke(musb_ep, -ESHUTDOWN); + + schedule_work(&musb->irq_work); + + spin_unlock_irqrestore(&(musb->lock), flags); + + DBG(2, "%s\n", musb_ep->end_point.name); + + return status; +} + +/* + * Allocate a request for an endpoint. + * Reused by ep0 code. + */ +struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb_request *request = NULL; + + request = kzalloc(sizeof *request, gfp_flags); + if (request) { + INIT_LIST_HEAD(&request->request.list); + request->request.dma = DMA_ADDR_INVALID; + request->epnum = musb_ep->current_epnum; + request->ep = musb_ep; + } + + return &request->request; +} + +/* + * Free a request + * Reused by ep0 code. + */ +void musb_free_request(struct usb_ep *ep, struct usb_request *req) +{ + kfree(to_musb_request(req)); +} + +static LIST_HEAD(buffers); + +struct free_record { + struct list_head list; + struct device *dev; + unsigned bytes; + dma_addr_t dma; +}; + +/* + * Context: controller locked, IRQs blocked. + */ +static void musb_ep_restart(struct musb *musb, struct musb_request *req) +{ + DBG(3, "<== %s request %p len %u on hw_ep%d\n", + req->tx ? "TX/IN" : "RX/OUT", + &req->request, req->request.length, req->epnum); + + musb_ep_select(musb->mregs, req->epnum); + if (req->tx) + txstate(musb, req); + else + rxstate(musb, req); +} + +static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t gfp_flags) +{ + struct musb_ep *musb_ep; + struct musb_request *request; + struct musb *musb; + int status = 0; + unsigned long lockflags; + + if (!ep || !req) + return -EINVAL; + if (!req->buf) + return -ENODATA; + + musb_ep = to_musb_ep(ep); + musb = musb_ep->musb; + + request = to_musb_request(req); + request->musb = musb; + + if (request->ep != musb_ep) + return -EINVAL; + + DBG(4, "<== to %s request=%p\n", ep->name, req); + + /* request is mine now... */ + request->request.actual = 0; + request->request.status = -EINPROGRESS; + request->epnum = musb_ep->current_epnum; + request->tx = musb_ep->is_in; + + if (is_dma_capable() && musb_ep->dma) { + if (request->request.dma == DMA_ADDR_INVALID) { + request->request.dma = dma_map_single( + musb->controller, + request->request.buf, + request->request.length, + request->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + request->mapped = 1; + } else { + dma_sync_single_for_device(musb->controller, + request->request.dma, + request->request.length, + request->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + request->mapped = 0; + } + } else if (!req->buf) { + return -ENODATA; + } else + request->mapped = 0; + + spin_lock_irqsave(&musb->lock, lockflags); + + /* don't queue if the ep is down */ + if (!musb_ep->desc) { + DBG(4, "req %p queued to %s while ep %s\n", + req, ep->name, "disabled"); + status = -ESHUTDOWN; + goto cleanup; + } + + /* add request to the list */ + list_add_tail(&(request->request.list), &(musb_ep->req_list)); + + /* it this is the head of the queue, start i/o ... */ + if (!musb_ep->busy && &request->request.list == musb_ep->req_list.next) + musb_ep_restart(musb, request); + +cleanup: + spin_unlock_irqrestore(&musb->lock, lockflags); + return status; +} + +static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + struct usb_request *r; + unsigned long flags; + int status = 0; + struct musb *musb = musb_ep->musb; + + if (!ep || !request || to_musb_request(request)->ep != musb_ep) + return -EINVAL; + + spin_lock_irqsave(&musb->lock, flags); + + list_for_each_entry(r, &musb_ep->req_list, list) { + if (r == request) + break; + } + if (r != request) { + DBG(3, "request %p not queued to %s\n", request, ep->name); + status = -EINVAL; + goto done; + } + + /* if the hardware doesn't have the request, easy ... */ + if (musb_ep->req_list.next != &request->list || musb_ep->busy) + musb_g_giveback(musb_ep, request, -ECONNRESET); + + /* ... else abort the dma transfer ... */ + else if (is_dma_capable() && musb_ep->dma) { + struct dma_controller *c = musb->dma_controller; + + musb_ep_select(musb->mregs, musb_ep->current_epnum); + if (c->channel_abort) + status = c->channel_abort(musb_ep->dma); + else + status = -EBUSY; + if (status == 0) + musb_g_giveback(musb_ep, request, -ECONNRESET); + } else { + /* NOTE: by sticking to easily tested hardware/driver states, + * we leave counting of in-flight packets imprecise. + */ + musb_g_giveback(musb_ep, request, -ECONNRESET); + } + +done: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +/* + * Set or clear the halt bit of an endpoint. A halted enpoint won't tx/rx any + * data but will queue requests. + * + * exported to ep0 code + */ +int musb_gadget_set_halt(struct usb_ep *ep, int value) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + u8 epnum = musb_ep->current_epnum; + struct musb *musb = musb_ep->musb; + void __iomem *epio = musb->endpoints[epnum].regs; + void __iomem *mbase; + unsigned long flags; + u16 csr; + struct musb_request *request = NULL; + int status = 0; + + if (!ep) + return -EINVAL; + mbase = musb->mregs; + + spin_lock_irqsave(&musb->lock, flags); + + if ((USB_ENDPOINT_XFER_ISOC == musb_ep->type)) { + status = -EINVAL; + goto done; + } + + musb_ep_select(mbase, epnum); + + /* cannot portably stall with non-empty FIFO */ + request = to_musb_request(next_request(musb_ep)); + if (value && musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) { + DBG(3, "%s fifo busy, cannot halt\n", ep->name); + spin_unlock_irqrestore(&musb->lock, flags); + return -EAGAIN; + } + + } + + /* set/clear the stall and toggle bits */ + DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) + csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_P_WZC_BITS + | MUSB_TXCSR_CLRDATATOG; + if (value) + csr |= MUSB_TXCSR_P_SENDSTALL; + else + csr &= ~(MUSB_TXCSR_P_SENDSTALL + | MUSB_TXCSR_P_SENTSTALL); + csr &= ~MUSB_TXCSR_TXPKTRDY; + musb_writew(epio, MUSB_TXCSR, csr); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_P_WZC_BITS + | MUSB_RXCSR_FLUSHFIFO + | MUSB_RXCSR_CLRDATATOG; + if (value) + csr |= MUSB_RXCSR_P_SENDSTALL; + else + csr &= ~(MUSB_RXCSR_P_SENDSTALL + | MUSB_RXCSR_P_SENTSTALL); + musb_writew(epio, MUSB_RXCSR, csr); + } + +done: + + /* maybe start the first request in the queue */ + if (!musb_ep->busy && !value && request) { + DBG(3, "restarting the request\n"); + musb_ep_restart(musb, request); + } + + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +static int musb_gadget_fifo_status(struct usb_ep *ep) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + void __iomem *epio = musb_ep->hw_ep->regs; + int retval = -EINVAL; + + if (musb_ep->desc && !musb_ep->is_in) { + struct musb *musb = musb_ep->musb; + int epnum = musb_ep->current_epnum; + void __iomem *mbase = musb->mregs; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + + musb_ep_select(mbase, epnum); + /* FIXME return zero unless RXPKTRDY is set */ + retval = musb_readw(epio, MUSB_RXCOUNT); + + spin_unlock_irqrestore(&musb->lock, flags); + } + return retval; +} + +static void musb_gadget_fifo_flush(struct usb_ep *ep) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb *musb = musb_ep->musb; + u8 epnum = musb_ep->current_epnum; + void __iomem *epio = musb->endpoints[epnum].regs; + void __iomem *mbase; + unsigned long flags; + u16 csr, int_txe; + + mbase = musb->mregs; + + spin_lock_irqsave(&musb->lock, flags); + musb_ep_select(mbase, (u8) epnum); + + /* disable interrupts */ + int_txe = musb_readw(mbase, MUSB_INTRTXE); + musb_writew(mbase, MUSB_INTRTXE, int_txe & ~(1 << epnum)); + + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) { + csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_TXCSR, csr); + /* REVISIT may be inappropriate w/o FIFONOTEMPTY ... */ + musb_writew(epio, MUSB_TXCSR, csr); + } + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + } + + /* re-enable interrupt */ + musb_writew(mbase, MUSB_INTRTXE, int_txe); + spin_unlock_irqrestore(&musb->lock, flags); +} + +static const struct usb_ep_ops musb_ep_ops = { + .enable = musb_gadget_enable, + .disable = musb_gadget_disable, + .alloc_request = musb_alloc_request, + .free_request = musb_free_request, + .queue = musb_gadget_queue, + .dequeue = musb_gadget_dequeue, + .set_halt = musb_gadget_set_halt, + .fifo_status = musb_gadget_fifo_status, + .fifo_flush = musb_gadget_fifo_flush +}; + +/* ----------------------------------------------------------------------- */ + +static int musb_gadget_get_frame(struct usb_gadget *gadget) +{ + struct musb *musb = gadget_to_musb(gadget); + + return (int)musb_readw(musb->mregs, MUSB_FRAME); +} + +static int musb_gadget_wakeup(struct usb_gadget *gadget) +{ + struct musb *musb = gadget_to_musb(gadget); + void __iomem *mregs = musb->mregs; + unsigned long flags; + int status = -EINVAL; + u8 power, devctl; + int retries; + + spin_lock_irqsave(&musb->lock, flags); + + switch (musb->xceiv.state) { + case OTG_STATE_B_PERIPHERAL: + /* NOTE: OTG state machine doesn't include B_SUSPENDED; + * that's part of the standard usb 1.1 state machine, and + * doesn't affect OTG transitions. + */ + if (musb->may_wakeup && musb->is_suspended) + break; + goto done; + case OTG_STATE_B_IDLE: + /* Start SRP ... OTG not required. */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + DBG(2, "Sending SRP: devctl: %02x\n", devctl); + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(mregs, MUSB_DEVCTL, devctl); + devctl = musb_readb(mregs, MUSB_DEVCTL); + retries = 100; + while (!(devctl & MUSB_DEVCTL_SESSION)) { + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (retries-- < 1) + break; + } + retries = 10000; + while (devctl & MUSB_DEVCTL_SESSION) { + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (retries-- < 1) + break; + } + + /* Block idling for at least 1s */ + musb_platform_try_idle(musb, + jiffies + msecs_to_jiffies(1 * HZ)); + + status = 0; + goto done; + default: + DBG(2, "Unhandled wake: %s\n", otg_state_string(musb)); + goto done; + } + + status = 0; + + power = musb_readb(mregs, MUSB_POWER); + power |= MUSB_POWER_RESUME; + musb_writeb(mregs, MUSB_POWER, power); + DBG(2, "issue wakeup\n"); + + /* FIXME do this next chunk in a timer callback, no udelay */ + mdelay(2); + + power = musb_readb(mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + musb_writeb(mregs, MUSB_POWER, power); +done: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +static int +musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct musb *musb = gadget_to_musb(gadget); + + musb->is_self_powered = !!is_selfpowered; + return 0; +} + +static void musb_pullup(struct musb *musb, int is_on) +{ + u8 power; + + power = musb_readb(musb->mregs, MUSB_POWER); + if (is_on) + power |= MUSB_POWER_SOFTCONN; + else + power &= ~MUSB_POWER_SOFTCONN; + + /* FIXME if on, HdrcStart; if off, HdrcStop */ + + DBG(3, "gadget %s D+ pullup %s\n", + musb->gadget_driver->function, is_on ? "on" : "off"); + musb_writeb(musb->mregs, MUSB_POWER, power); +} + +#if 0 +static int musb_gadget_vbus_session(struct usb_gadget *gadget, int is_active) +{ + DBG(2, "<= %s =>\n", __func__); + + /* + * FIXME iff driver's softconnect flag is set (as it is during probe, + * though that can clear it), just musb_pullup(). + */ + + return -EINVAL; +} +#endif + +static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct musb *musb = gadget_to_musb(gadget); + + if (!musb->xceiv.set_power) + return -EOPNOTSUPP; + return otg_set_power(&musb->xceiv, mA); +} + +static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct musb *musb = gadget_to_musb(gadget); + unsigned long flags; + + is_on = !!is_on; + + /* NOTE: this assumes we are sensing vbus; we'd rather + * not pullup unless the B-session is active. + */ + spin_lock_irqsave(&musb->lock, flags); + if (is_on != musb->softconnect) { + musb->softconnect = is_on; + musb_pullup(musb, is_on); + } + spin_unlock_irqrestore(&musb->lock, flags); + return 0; +} + +static const struct usb_gadget_ops musb_gadget_operations = { + .get_frame = musb_gadget_get_frame, + .wakeup = musb_gadget_wakeup, + .set_selfpowered = musb_gadget_set_self_powered, + /* .vbus_session = musb_gadget_vbus_session, */ + .vbus_draw = musb_gadget_vbus_draw, + .pullup = musb_gadget_pullup, +}; + +/* ----------------------------------------------------------------------- */ + +/* Registration */ + +/* Only this registration code "knows" the rule (from USB standards) + * about there being only one external upstream port. It assumes + * all peripheral ports are external... + */ +static struct musb *the_gadget; + +static void musb_gadget_release(struct device *dev) +{ + /* kref_put(WHAT) */ + dev_dbg(dev, "%s\n", __func__); +} + + +static void __init +init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in) +{ + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + + memset(ep, 0, sizeof *ep); + + ep->current_epnum = epnum; + ep->musb = musb; + ep->hw_ep = hw_ep; + ep->is_in = is_in; + + INIT_LIST_HEAD(&ep->req_list); + + sprintf(ep->name, "ep%d%s", epnum, + (!epnum || hw_ep->is_shared_fifo) ? "" : ( + is_in ? "in" : "out")); + ep->end_point.name = ep->name; + INIT_LIST_HEAD(&ep->end_point.ep_list); + if (!epnum) { + ep->end_point.maxpacket = 64; + ep->end_point.ops = &musb_g_ep0_ops; + musb->g.ep0 = &ep->end_point; + } else { + if (is_in) + ep->end_point.maxpacket = hw_ep->max_packet_sz_tx; + else + ep->end_point.maxpacket = hw_ep->max_packet_sz_rx; + ep->end_point.ops = &musb_ep_ops; + list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list); + } +} + +/* + * Initialize the endpoints exposed to peripheral drivers, with backlinks + * to the rest of the driver state. + */ +static inline void __init musb_g_init_endpoints(struct musb *musb) +{ + u8 epnum; + struct musb_hw_ep *hw_ep; + unsigned count = 0; + + /* intialize endpoint list just once */ + INIT_LIST_HEAD(&(musb->g.ep_list)); + + for (epnum = 0, hw_ep = musb->endpoints; + epnum < musb->nr_endpoints; + epnum++, hw_ep++) { + if (hw_ep->is_shared_fifo /* || !epnum */) { + init_peripheral_ep(musb, &hw_ep->ep_in, epnum, 0); + count++; + } else { + if (hw_ep->max_packet_sz_tx) { + init_peripheral_ep(musb, &hw_ep->ep_in, + epnum, 1); + count++; + } + if (hw_ep->max_packet_sz_rx) { + init_peripheral_ep(musb, &hw_ep->ep_out, + epnum, 0); + count++; + } + } + } +} + +/* called once during driver setup to initialize and link into + * the driver model; memory is zeroed. + */ +int __init musb_gadget_setup(struct musb *musb) +{ + int status; + + /* REVISIT minor race: if (erroneously) setting up two + * musb peripherals at the same time, only the bus lock + * is probably held. + */ + if (the_gadget) + return -EBUSY; + the_gadget = musb; + + musb->g.ops = &musb_gadget_operations; + musb->g.is_dualspeed = 1; + musb->g.speed = USB_SPEED_UNKNOWN; + + /* this "gadget" abstracts/virtualizes the controller */ + strcpy(musb->g.dev.bus_id, "gadget"); + musb->g.dev.parent = musb->controller; + musb->g.dev.dma_mask = musb->controller->dma_mask; + musb->g.dev.release = musb_gadget_release; + musb->g.name = musb_driver_name; + + if (is_otg_enabled(musb)) + musb->g.is_otg = 1; + + musb_g_init_endpoints(musb); + + musb->is_active = 0; + musb_platform_try_idle(musb, 0); + + status = device_register(&musb->g.dev); + if (status != 0) + the_gadget = NULL; + return status; +} + +void musb_gadget_cleanup(struct musb *musb) +{ + if (musb != the_gadget) + return; + + device_unregister(&musb->g.dev); + the_gadget = NULL; +} + +/* + * Register the gadget driver. Used by gadget drivers when + * registering themselves with the controller. + * + * -EINVAL something went wrong (not driver) + * -EBUSY another gadget is already using the controller + * -ENOMEM no memeory to perform the operation + * + * @param driver the gadget driver + * @return <0 if error, 0 if everything is fine + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int retval; + unsigned long flags; + struct musb *musb = the_gadget; + + if (!driver + || driver->speed != USB_SPEED_HIGH + || !driver->bind + || !driver->setup) + return -EINVAL; + + /* driver must be initialized to support peripheral mode */ + if (!musb || !(musb->board_mode == MUSB_OTG + || musb->board_mode != MUSB_OTG)) { + DBG(1, "%s, no dev??\n", __func__); + return -ENODEV; + } + + DBG(3, "registering driver %s\n", driver->function); + spin_lock_irqsave(&musb->lock, flags); + + if (musb->gadget_driver) { + DBG(1, "%s is already bound to %s\n", + musb_driver_name, + musb->gadget_driver->driver.name); + retval = -EBUSY; + } else { + musb->gadget_driver = driver; + musb->g.dev.driver = &driver->driver; + driver->driver.bus = NULL; + musb->softconnect = 1; + retval = 0; + } + + spin_unlock_irqrestore(&musb->lock, flags); + + if (retval == 0) + retval = driver->bind(&musb->g); + if (retval != 0) { + DBG(3, "bind to driver %s failed --> %d\n", + driver->driver.name, retval); + musb->gadget_driver = NULL; + musb->g.dev.driver = NULL; + } + + /* start peripheral and/or OTG engines */ + if (retval == 0) { + spin_lock_irqsave(&musb->lock, flags); + + /* REVISIT always use otg_set_peripheral(), handling + * issues including the root hub one below ... + */ + musb->xceiv.gadget = &musb->g; + musb->xceiv.state = OTG_STATE_B_IDLE; + musb->is_active = 1; + + /* FIXME this ignores the softconnect flag. Drivers are + * allowed hold the peripheral inactive until for example + * userspace hooks up printer hardware or DSP codecs, so + * hosts only see fully functional devices. + */ + + if (!is_otg_enabled(musb)) + musb_start(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + + if (is_otg_enabled(musb)) { + DBG(3, "OTG startup...\n"); + + /* REVISIT: funcall to other code, which also + * handles power budgeting ... this way also + * ensures HdrcStart is indirectly called. + */ + retval = usb_add_hcd(musb_to_hcd(musb), -1, 0); + if (retval < 0) { + DBG(1, "add_hcd failed, %d\n", retval); + spin_lock_irqsave(&musb->lock, flags); + musb->xceiv.gadget = NULL; + musb->xceiv.state = OTG_STATE_UNDEFINED; + musb->gadget_driver = NULL; + musb->g.dev.driver = NULL; + spin_unlock_irqrestore(&musb->lock, flags); + } + } + } + + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) +{ + int i; + struct musb_hw_ep *hw_ep; + + /* don't disconnect if it's not connected */ + if (musb->g.speed == USB_SPEED_UNKNOWN) + driver = NULL; + else + musb->g.speed = USB_SPEED_UNKNOWN; + + /* deactivate the hardware */ + if (musb->softconnect) { + musb->softconnect = 0; + musb_pullup(musb, 0); + } + musb_stop(musb); + + /* killing any outstanding requests will quiesce the driver; + * then report disconnect + */ + if (driver) { + for (i = 0, hw_ep = musb->endpoints; + i < musb->nr_endpoints; + i++, hw_ep++) { + musb_ep_select(musb->mregs, i); + if (hw_ep->is_shared_fifo /* || !epnum */) { + nuke(&hw_ep->ep_in, -ESHUTDOWN); + } else { + if (hw_ep->max_packet_sz_tx) + nuke(&hw_ep->ep_in, -ESHUTDOWN); + if (hw_ep->max_packet_sz_rx) + nuke(&hw_ep->ep_out, -ESHUTDOWN); + } + } + + spin_unlock(&musb->lock); + driver->disconnect(&musb->g); + spin_lock(&musb->lock); + } +} + +/* + * Unregister the gadget driver. Used by gadget drivers when + * unregistering themselves from the controller. + * + * @param driver the gadget driver to unregister + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + unsigned long flags; + int retval = 0; + struct musb *musb = the_gadget; + + if (!driver || !driver->unbind || !musb) + return -EINVAL; + + /* REVISIT always use otg_set_peripheral() here too; + * this needs to shut down the OTG engine. + */ + + spin_lock_irqsave(&musb->lock, flags); + +#ifdef CONFIG_USB_MUSB_OTG + musb_hnp_stop(musb); +#endif + + if (musb->gadget_driver == driver) { + + (void) musb_gadget_vbus_draw(&musb->g, 0); + + musb->xceiv.state = OTG_STATE_UNDEFINED; + stop_activity(musb, driver); + + DBG(3, "unregistering driver %s\n", driver->function); + spin_unlock_irqrestore(&musb->lock, flags); + driver->unbind(&musb->g); + spin_lock_irqsave(&musb->lock, flags); + + musb->gadget_driver = NULL; + musb->g.dev.driver = NULL; + + musb->is_active = 0; + musb_platform_try_idle(musb, 0); + } else + retval = -EINVAL; + spin_unlock_irqrestore(&musb->lock, flags); + + if (is_otg_enabled(musb) && retval == 0) { + usb_remove_hcd(musb_to_hcd(musb)); + /* FIXME we need to be able to register another + * gadget driver here and have everything work; + * that currently misbehaves. + */ + } + + return retval; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/* ----------------------------------------------------------------------- */ + +/* lifecycle operations called through plat_uds.c */ + +void musb_g_resume(struct musb *musb) +{ + musb->is_suspended = 0; + switch (musb->xceiv.state) { + case OTG_STATE_B_IDLE: + break; + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_PERIPHERAL: + musb->is_active = 1; + if (musb->gadget_driver && musb->gadget_driver->resume) { + spin_unlock(&musb->lock); + musb->gadget_driver->resume(&musb->g); + spin_lock(&musb->lock); + } + break; + default: + WARNING("unhandled RESUME transition (%s)\n", + otg_state_string(musb)); + } +} + +/* called when SOF packets stop for 3+ msec */ +void musb_g_suspend(struct musb *musb) +{ + u8 devctl; + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + DBG(3, "devctl %02x\n", devctl); + + switch (musb->xceiv.state) { + case OTG_STATE_B_IDLE: + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_B_PERIPHERAL: + musb->is_suspended = 1; + if (musb->gadget_driver && musb->gadget_driver->suspend) { + spin_unlock(&musb->lock); + musb->gadget_driver->suspend(&musb->g); + spin_lock(&musb->lock); + } + break; + default: + /* REVISIT if B_HOST, clear DEVCTL.HOSTREQ; + * A_PERIPHERAL may need care too + */ + WARNING("unhandled SUSPEND transition (%s)\n", + otg_state_string(musb)); + } +} + +/* Called during SRP */ +void musb_g_wakeup(struct musb *musb) +{ + musb_gadget_wakeup(&musb->g); +} + +/* called when VBUS drops below session threshold, and in other cases */ +void musb_g_disconnect(struct musb *musb) +{ + void __iomem *mregs = musb->mregs; + u8 devctl = musb_readb(mregs, MUSB_DEVCTL); + + DBG(3, "devctl %02x\n", devctl); + + /* clear HR */ + musb_writeb(mregs, MUSB_DEVCTL, devctl & MUSB_DEVCTL_SESSION); + + /* don't draw vbus until new b-default session */ + (void) musb_gadget_vbus_draw(&musb->g, 0); + + musb->g.speed = USB_SPEED_UNKNOWN; + if (musb->gadget_driver && musb->gadget_driver->disconnect) { + spin_unlock(&musb->lock); + musb->gadget_driver->disconnect(&musb->g); + spin_lock(&musb->lock); + } + + switch (musb->xceiv.state) { + default: +#ifdef CONFIG_USB_MUSB_OTG + DBG(2, "Unhandled disconnect %s, setting a_idle\n", + otg_state_string(musb)); + musb->xceiv.state = OTG_STATE_A_IDLE; + break; + case OTG_STATE_A_PERIPHERAL: + musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_HOST: +#endif + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_IDLE: + musb->xceiv.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_B_SRP_INIT: + break; + } + + musb->is_active = 0; +} + +void musb_g_reset(struct musb *musb) +__releases(musb->lock) +__acquires(musb->lock) +{ + void __iomem *mbase = musb->mregs; + u8 devctl = musb_readb(mbase, MUSB_DEVCTL); + u8 power; + + DBG(3, "<== %s addr=%x driver '%s'\n", + (devctl & MUSB_DEVCTL_BDEVICE) + ? "B-Device" : "A-Device", + musb_readb(mbase, MUSB_FADDR), + musb->gadget_driver + ? musb->gadget_driver->driver.name + : NULL + ); + + /* report disconnect, if we didn't already (flushing EP state) */ + if (musb->g.speed != USB_SPEED_UNKNOWN) + musb_g_disconnect(musb); + + /* clear HR */ + else if (devctl & MUSB_DEVCTL_HR) + musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); + + + /* what speed did we negotiate? */ + power = musb_readb(mbase, MUSB_POWER); + musb->g.speed = (power & MUSB_POWER_HSMODE) + ? USB_SPEED_HIGH : USB_SPEED_FULL; + + /* start in USB_STATE_DEFAULT */ + musb->is_active = 1; + musb->is_suspended = 0; + MUSB_DEV_MODE(musb); + musb->address = 0; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + + musb->may_wakeup = 0; + musb->g.b_hnp_enable = 0; + musb->g.a_alt_hnp_support = 0; + musb->g.a_hnp_support = 0; + + /* Normal reset, as B-Device; + * or else after HNP, as A-Device + */ + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->g.is_a_peripheral = 0; + } else if (is_otg_enabled(musb)) { + musb->xceiv.state = OTG_STATE_A_PERIPHERAL; + musb->g.is_a_peripheral = 1; + } else + WARN_ON(1); + + /* start with default limits on VBUS power draw */ + (void) musb_gadget_vbus_draw(&musb->g, + is_otg_enabled(musb) ? 8 : 100); +} diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h new file mode 100644 index 000000000000..59502da9f739 --- /dev/null +++ b/drivers/usb/musb/musb_gadget.h @@ -0,0 +1,108 @@ +/* + * MUSB OTG driver peripheral defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MUSB_GADGET_H +#define __MUSB_GADGET_H + +struct musb_request { + struct usb_request request; + struct musb_ep *ep; + struct musb *musb; + u8 tx; /* endpoint direction */ + u8 epnum; + u8 mapped; +}; + +static inline struct musb_request *to_musb_request(struct usb_request *req) +{ + return req ? container_of(req, struct musb_request, request) : NULL; +} + +extern struct usb_request * +musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags); +extern void musb_free_request(struct usb_ep *ep, struct usb_request *req); + + +/* + * struct musb_ep - peripheral side view of endpoint rx or tx side + */ +struct musb_ep { + /* stuff towards the head is basically write-once. */ + struct usb_ep end_point; + char name[12]; + struct musb_hw_ep *hw_ep; + struct musb *musb; + u8 current_epnum; + + /* ... when enabled/disabled ... */ + u8 type; + u8 is_in; + u16 packet_sz; + const struct usb_endpoint_descriptor *desc; + struct dma_channel *dma; + + /* later things are modified based on usage */ + struct list_head req_list; + + /* true if lock must be dropped but req_list may not be advanced */ + u8 busy; +}; + +static inline struct musb_ep *to_musb_ep(struct usb_ep *ep) +{ + return ep ? container_of(ep, struct musb_ep, end_point) : NULL; +} + +static inline struct usb_request *next_request(struct musb_ep *ep) +{ + struct list_head *queue = &ep->req_list; + + if (list_empty(queue)) + return NULL; + return container_of(queue->next, struct usb_request, list); +} + +extern void musb_g_tx(struct musb *musb, u8 epnum); +extern void musb_g_rx(struct musb *musb, u8 epnum); + +extern const struct usb_ep_ops musb_g_ep0_ops; + +extern int musb_gadget_setup(struct musb *); +extern void musb_gadget_cleanup(struct musb *); + +extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int); + +extern int musb_gadget_set_halt(struct usb_ep *ep, int value); + +#endif /* __MUSB_GADGET_H */ diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c new file mode 100644 index 000000000000..48d7d3ccb243 --- /dev/null +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -0,0 +1,981 @@ +/* + * MUSB OTG peripheral driver ep0 handling + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + +/* ep0 is always musb->endpoints[0].ep_in */ +#define next_ep0_request(musb) next_in_request(&(musb)->endpoints[0]) + +/* + * locking note: we use only the controller lock, for simpler correctness. + * It's always held with IRQs blocked. + * + * It protects the ep0 request queue as well as ep0_state, not just the + * controller and indexed registers. And that lock stays held unless it + * needs to be dropped to allow reentering this driver ... like upcalls to + * the gadget driver, or adjusting endpoint halt status. + */ + +static char *decode_ep0stage(u8 stage) +{ + switch (stage) { + case MUSB_EP0_STAGE_SETUP: return "idle"; + case MUSB_EP0_STAGE_TX: return "in"; + case MUSB_EP0_STAGE_RX: return "out"; + case MUSB_EP0_STAGE_ACKWAIT: return "wait"; + case MUSB_EP0_STAGE_STATUSIN: return "in/status"; + case MUSB_EP0_STAGE_STATUSOUT: return "out/status"; + default: return "?"; + } +} + +/* handle a standard GET_STATUS request + * Context: caller holds controller lock + */ +static int service_tx_status_request( + struct musb *musb, + const struct usb_ctrlrequest *ctrlrequest) +{ + void __iomem *mbase = musb->mregs; + int handled = 1; + u8 result[2], epnum = 0; + const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK; + + result[1] = 0; + + switch (recip) { + case USB_RECIP_DEVICE: + result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED; + result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; +#ifdef CONFIG_USB_MUSB_OTG + if (musb->g.is_otg) { + result[0] |= musb->g.b_hnp_enable + << USB_DEVICE_B_HNP_ENABLE; + result[0] |= musb->g.a_alt_hnp_support + << USB_DEVICE_A_ALT_HNP_SUPPORT; + result[0] |= musb->g.a_hnp_support + << USB_DEVICE_A_HNP_SUPPORT; + } +#endif + break; + + case USB_RECIP_INTERFACE: + result[0] = 0; + break; + + case USB_RECIP_ENDPOINT: { + int is_in; + struct musb_ep *ep; + u16 tmp; + void __iomem *regs; + + epnum = (u8) ctrlrequest->wIndex; + if (!epnum) { + result[0] = 0; + break; + } + + is_in = epnum & USB_DIR_IN; + if (is_in) { + epnum &= 0x0f; + ep = &musb->endpoints[epnum].ep_in; + } else { + ep = &musb->endpoints[epnum].ep_out; + } + regs = musb->endpoints[epnum].regs; + + if (epnum >= MUSB_C_NUM_EPS || !ep->desc) { + handled = -EINVAL; + break; + } + + musb_ep_select(mbase, epnum); + if (is_in) + tmp = musb_readw(regs, MUSB_TXCSR) + & MUSB_TXCSR_P_SENDSTALL; + else + tmp = musb_readw(regs, MUSB_RXCSR) + & MUSB_RXCSR_P_SENDSTALL; + musb_ep_select(mbase, 0); + + result[0] = tmp ? 1 : 0; + } break; + + default: + /* class, vendor, etc ... delegate */ + handled = 0; + break; + } + + /* fill up the fifo; caller updates csr0 */ + if (handled > 0) { + u16 len = le16_to_cpu(ctrlrequest->wLength); + + if (len > 2) + len = 2; + musb_write_fifo(&musb->endpoints[0], len, result); + } + + return handled; +} + +/* + * handle a control-IN request, the end0 buffer contains the current request + * that is supposed to be a standard control request. Assumes the fifo to + * be at least 2 bytes long. + * + * @return 0 if the request was NOT HANDLED, + * < 0 when error + * > 0 when the request is processed + * + * Context: caller holds controller lock + */ +static int +service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest) +{ + int handled = 0; /* not handled */ + + if ((ctrlrequest->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD) { + switch (ctrlrequest->bRequest) { + case USB_REQ_GET_STATUS: + handled = service_tx_status_request(musb, + ctrlrequest); + break; + + /* case USB_REQ_SYNC_FRAME: */ + + default: + break; + } + } + return handled; +} + +/* + * Context: caller holds controller lock + */ +static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) +{ + musb_g_giveback(&musb->endpoints[0].ep_in, req, 0); + musb->ep0_state = MUSB_EP0_STAGE_SETUP; +} + +/* + * Tries to start B-device HNP negotiation if enabled via sysfs + */ +static inline void musb_try_b_hnp_enable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + u8 devctl; + + DBG(1, "HNP: Setting HR\n"); + devctl = musb_readb(mbase, MUSB_DEVCTL); + musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR); +} + +/* + * Handle all control requests with no DATA stage, including standard + * requests such as: + * USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized + * always delegated to the gadget driver + * USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE + * always handled here, except for class/vendor/... features + * + * Context: caller holds controller lock + */ +static int +service_zero_data_request(struct musb *musb, + struct usb_ctrlrequest *ctrlrequest) +__releases(musb->lock) +__acquires(musb->lock) +{ + int handled = -EINVAL; + void __iomem *mbase = musb->mregs; + const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK; + + /* the gadget driver handles everything except what we MUST handle */ + if ((ctrlrequest->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD) { + switch (ctrlrequest->bRequest) { + case USB_REQ_SET_ADDRESS: + /* change it after the status stage */ + musb->set_address = true; + musb->address = (u8) (ctrlrequest->wValue & 0x7f); + handled = 1; + break; + + case USB_REQ_CLEAR_FEATURE: + switch (recip) { + case USB_RECIP_DEVICE: + if (ctrlrequest->wValue + != USB_DEVICE_REMOTE_WAKEUP) + break; + musb->may_wakeup = 0; + handled = 1; + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT:{ + const u8 num = ctrlrequest->wIndex & 0x0f; + struct musb_ep *musb_ep; + + if (num == 0 + || num >= MUSB_C_NUM_EPS + || ctrlrequest->wValue + != USB_ENDPOINT_HALT) + break; + + if (ctrlrequest->wIndex & USB_DIR_IN) + musb_ep = &musb->endpoints[num].ep_in; + else + musb_ep = &musb->endpoints[num].ep_out; + if (!musb_ep->desc) + break; + + /* REVISIT do it directly, no locking games */ + spin_unlock(&musb->lock); + musb_gadget_set_halt(&musb_ep->end_point, 0); + spin_lock(&musb->lock); + + /* select ep0 again */ + musb_ep_select(mbase, 0); + handled = 1; + } break; + default: + /* class, vendor, etc ... delegate */ + handled = 0; + break; + } + break; + + case USB_REQ_SET_FEATURE: + switch (recip) { + case USB_RECIP_DEVICE: + handled = 1; + switch (ctrlrequest->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + musb->may_wakeup = 1; + break; + case USB_DEVICE_TEST_MODE: + if (musb->g.speed != USB_SPEED_HIGH) + goto stall; + if (ctrlrequest->wIndex & 0xff) + goto stall; + + switch (ctrlrequest->wIndex >> 8) { + case 1: + pr_debug("TEST_J\n"); + /* TEST_J */ + musb->test_mode_nr = + MUSB_TEST_J; + break; + case 2: + /* TEST_K */ + pr_debug("TEST_K\n"); + musb->test_mode_nr = + MUSB_TEST_K; + break; + case 3: + /* TEST_SE0_NAK */ + pr_debug("TEST_SE0_NAK\n"); + musb->test_mode_nr = + MUSB_TEST_SE0_NAK; + break; + case 4: + /* TEST_PACKET */ + pr_debug("TEST_PACKET\n"); + musb->test_mode_nr = + MUSB_TEST_PACKET; + break; + default: + goto stall; + } + + /* enter test mode after irq */ + if (handled > 0) + musb->test_mode = true; + break; +#ifdef CONFIG_USB_MUSB_OTG + case USB_DEVICE_B_HNP_ENABLE: + if (!musb->g.is_otg) + goto stall; + musb->g.b_hnp_enable = 1; + musb_try_b_hnp_enable(musb); + break; + case USB_DEVICE_A_HNP_SUPPORT: + if (!musb->g.is_otg) + goto stall; + musb->g.a_hnp_support = 1; + break; + case USB_DEVICE_A_ALT_HNP_SUPPORT: + if (!musb->g.is_otg) + goto stall; + musb->g.a_alt_hnp_support = 1; + break; +#endif +stall: + default: + handled = -EINVAL; + break; + } + break; + + case USB_RECIP_INTERFACE: + break; + + case USB_RECIP_ENDPOINT:{ + const u8 epnum = + ctrlrequest->wIndex & 0x0f; + struct musb_ep *musb_ep; + struct musb_hw_ep *ep; + void __iomem *regs; + int is_in; + u16 csr; + + if (epnum == 0 + || epnum >= MUSB_C_NUM_EPS + || ctrlrequest->wValue + != USB_ENDPOINT_HALT) + break; + + ep = musb->endpoints + epnum; + regs = ep->regs; + is_in = ctrlrequest->wIndex & USB_DIR_IN; + if (is_in) + musb_ep = &ep->ep_in; + else + musb_ep = &ep->ep_out; + if (!musb_ep->desc) + break; + + musb_ep_select(mbase, epnum); + if (is_in) { + csr = musb_readw(regs, + MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) + csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_P_SENDSTALL + | MUSB_TXCSR_CLRDATATOG + | MUSB_TXCSR_P_WZC_BITS; + musb_writew(regs, MUSB_TXCSR, + csr); + } else { + csr = musb_readw(regs, + MUSB_RXCSR); + csr |= MUSB_RXCSR_P_SENDSTALL + | MUSB_RXCSR_FLUSHFIFO + | MUSB_RXCSR_CLRDATATOG + | MUSB_TXCSR_P_WZC_BITS; + musb_writew(regs, MUSB_RXCSR, + csr); + } + + /* select ep0 again */ + musb_ep_select(mbase, 0); + handled = 1; + } break; + + default: + /* class, vendor, etc ... delegate */ + handled = 0; + break; + } + break; + default: + /* delegate SET_CONFIGURATION, etc */ + handled = 0; + } + } else + handled = 0; + return handled; +} + +/* we have an ep0out data packet + * Context: caller holds controller lock + */ +static void ep0_rxstate(struct musb *musb) +{ + void __iomem *regs = musb->control_ep->regs; + struct usb_request *req; + u16 tmp; + + req = next_ep0_request(musb); + + /* read packet and ack; or stall because of gadget driver bug: + * should have provided the rx buffer before setup() returned. + */ + if (req) { + void *buf = req->buf + req->actual; + unsigned len = req->length - req->actual; + + /* read the buffer */ + tmp = musb_readb(regs, MUSB_COUNT0); + if (tmp > len) { + req->status = -EOVERFLOW; + tmp = len; + } + musb_read_fifo(&musb->endpoints[0], tmp, buf); + req->actual += tmp; + tmp = MUSB_CSR0_P_SVDRXPKTRDY; + if (tmp < 64 || req->actual == req->length) { + musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; + tmp |= MUSB_CSR0_P_DATAEND; + } else + req = NULL; + } else + tmp = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL; + + + /* Completion handler may choose to stall, e.g. because the + * message just received holds invalid data. + */ + if (req) { + musb->ackpend = tmp; + musb_g_ep0_giveback(musb, req); + if (!musb->ackpend) + return; + musb->ackpend = 0; + } + musb_writew(regs, MUSB_CSR0, tmp); +} + +/* + * transmitting to the host (IN), this code might be called from IRQ + * and from kernel thread. + * + * Context: caller holds controller lock + */ +static void ep0_txstate(struct musb *musb) +{ + void __iomem *regs = musb->control_ep->regs; + struct usb_request *request = next_ep0_request(musb); + u16 csr = MUSB_CSR0_TXPKTRDY; + u8 *fifo_src; + u8 fifo_count; + + if (!request) { + /* WARN_ON(1); */ + DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0)); + return; + } + + /* load the data */ + fifo_src = (u8 *) request->buf + request->actual; + fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE, + request->length - request->actual); + musb_write_fifo(&musb->endpoints[0], fifo_count, fifo_src); + request->actual += fifo_count; + + /* update the flags */ + if (fifo_count < MUSB_MAX_END0_PACKET + || request->actual == request->length) { + musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT; + csr |= MUSB_CSR0_P_DATAEND; + } else + request = NULL; + + /* report completions as soon as the fifo's loaded; there's no + * win in waiting till this last packet gets acked. (other than + * very precise fault reporting, needed by USB TMC; possible with + * this hardware, but not usable from portable gadget drivers.) + */ + if (request) { + musb->ackpend = csr; + musb_g_ep0_giveback(musb, request); + if (!musb->ackpend) + return; + musb->ackpend = 0; + } + + /* send it out, triggering a "txpktrdy cleared" irq */ + musb_writew(regs, MUSB_CSR0, csr); +} + +/* + * Read a SETUP packet (struct usb_ctrlrequest) from the hardware. + * Fields are left in USB byte-order. + * + * Context: caller holds controller lock. + */ +static void +musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req) +{ + struct usb_request *r; + void __iomem *regs = musb->control_ep->regs; + + musb_read_fifo(&musb->endpoints[0], sizeof *req, (u8 *)req); + + /* NOTE: earlier 2.6 versions changed setup packets to host + * order, but now USB packets always stay in USB byte order. + */ + DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n", + req->bRequestType, + req->bRequest, + le16_to_cpu(req->wValue), + le16_to_cpu(req->wIndex), + le16_to_cpu(req->wLength)); + + /* clean up any leftover transfers */ + r = next_ep0_request(musb); + if (r) + musb_g_ep0_giveback(musb, r); + + /* For zero-data requests we want to delay the STATUS stage to + * avoid SETUPEND errors. If we read data (OUT), delay accepting + * packets until there's a buffer to store them in. + * + * If we write data, the controller acts happier if we enable + * the TX FIFO right away, and give the controller a moment + * to switch modes... + */ + musb->set_address = false; + musb->ackpend = MUSB_CSR0_P_SVDRXPKTRDY; + if (req->wLength == 0) { + if (req->bRequestType & USB_DIR_IN) + musb->ackpend |= MUSB_CSR0_TXPKTRDY; + musb->ep0_state = MUSB_EP0_STAGE_ACKWAIT; + } else if (req->bRequestType & USB_DIR_IN) { + musb->ep0_state = MUSB_EP0_STAGE_TX; + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDRXPKTRDY); + while ((musb_readw(regs, MUSB_CSR0) + & MUSB_CSR0_RXPKTRDY) != 0) + cpu_relax(); + musb->ackpend = 0; + } else + musb->ep0_state = MUSB_EP0_STAGE_RX; +} + +static int +forward_to_driver(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest) +__releases(musb->lock) +__acquires(musb->lock) +{ + int retval; + if (!musb->gadget_driver) + return -EOPNOTSUPP; + spin_unlock(&musb->lock); + retval = musb->gadget_driver->setup(&musb->g, ctrlrequest); + spin_lock(&musb->lock); + return retval; +} + +/* + * Handle peripheral ep0 interrupt + * + * Context: irq handler; we won't re-enter the driver that way. + */ +irqreturn_t musb_g_ep0_irq(struct musb *musb) +{ + u16 csr; + u16 len; + void __iomem *mbase = musb->mregs; + void __iomem *regs = musb->endpoints[0].regs; + irqreturn_t retval = IRQ_NONE; + + musb_ep_select(mbase, 0); /* select ep0 */ + csr = musb_readw(regs, MUSB_CSR0); + len = musb_readb(regs, MUSB_COUNT0); + + DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n", + csr, len, + musb_readb(mbase, MUSB_FADDR), + decode_ep0stage(musb->ep0_state)); + + /* I sent a stall.. need to acknowledge it now.. */ + if (csr & MUSB_CSR0_P_SENTSTALL) { + musb_writew(regs, MUSB_CSR0, + csr & ~MUSB_CSR0_P_SENTSTALL); + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + csr = musb_readw(regs, MUSB_CSR0); + } + + /* request ended "early" */ + if (csr & MUSB_CSR0_P_SETUPEND) { + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND); + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + csr = musb_readw(regs, MUSB_CSR0); + /* NOTE: request may need completion */ + } + + /* docs from Mentor only describe tx, rx, and idle/setup states. + * we need to handle nuances around status stages, and also the + * case where status and setup stages come back-to-back ... + */ + switch (musb->ep0_state) { + + case MUSB_EP0_STAGE_TX: + /* irq on clearing txpktrdy */ + if ((csr & MUSB_CSR0_TXPKTRDY) == 0) { + ep0_txstate(musb); + retval = IRQ_HANDLED; + } + break; + + case MUSB_EP0_STAGE_RX: + /* irq on set rxpktrdy */ + if (csr & MUSB_CSR0_RXPKTRDY) { + ep0_rxstate(musb); + retval = IRQ_HANDLED; + } + break; + + case MUSB_EP0_STAGE_STATUSIN: + /* end of sequence #2 (OUT/RX state) or #3 (no data) */ + + /* update address (if needed) only @ the end of the + * status phase per usb spec, which also guarantees + * we get 10 msec to receive this irq... until this + * is done we won't see the next packet. + */ + if (musb->set_address) { + musb->set_address = false; + musb_writeb(mbase, MUSB_FADDR, musb->address); + } + + /* enter test mode if needed (exit by reset) */ + else if (musb->test_mode) { + DBG(1, "entering TESTMODE\n"); + + if (MUSB_TEST_PACKET == musb->test_mode_nr) + musb_load_testpacket(musb); + + musb_writeb(mbase, MUSB_TESTMODE, + musb->test_mode_nr); + } + /* FALLTHROUGH */ + + case MUSB_EP0_STAGE_STATUSOUT: + /* end of sequence #1: write to host (TX state) */ + { + struct usb_request *req; + + req = next_ep0_request(musb); + if (req) + musb_g_ep0_giveback(musb, req); + } + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + /* FALLTHROUGH */ + + case MUSB_EP0_STAGE_SETUP: + if (csr & MUSB_CSR0_RXPKTRDY) { + struct usb_ctrlrequest setup; + int handled = 0; + + if (len != 8) { + ERR("SETUP packet len %d != 8 ?\n", len); + break; + } + musb_read_setup(musb, &setup); + retval = IRQ_HANDLED; + + /* sometimes the RESET won't be reported */ + if (unlikely(musb->g.speed == USB_SPEED_UNKNOWN)) { + u8 power; + + printk(KERN_NOTICE "%s: peripheral reset " + "irq lost!\n", + musb_driver_name); + power = musb_readb(mbase, MUSB_POWER); + musb->g.speed = (power & MUSB_POWER_HSMODE) + ? USB_SPEED_HIGH : USB_SPEED_FULL; + + } + + switch (musb->ep0_state) { + + /* sequence #3 (no data stage), includes requests + * we can't forward (notably SET_ADDRESS and the + * device/endpoint feature set/clear operations) + * plus SET_CONFIGURATION and others we must + */ + case MUSB_EP0_STAGE_ACKWAIT: + handled = service_zero_data_request( + musb, &setup); + + /* status stage might be immediate */ + if (handled > 0) { + musb->ackpend |= MUSB_CSR0_P_DATAEND; + musb->ep0_state = + MUSB_EP0_STAGE_STATUSIN; + } + break; + + /* sequence #1 (IN to host), includes GET_STATUS + * requests that we can't forward, GET_DESCRIPTOR + * and others that we must + */ + case MUSB_EP0_STAGE_TX: + handled = service_in_request(musb, &setup); + if (handled > 0) { + musb->ackpend = MUSB_CSR0_TXPKTRDY + | MUSB_CSR0_P_DATAEND; + musb->ep0_state = + MUSB_EP0_STAGE_STATUSOUT; + } + break; + + /* sequence #2 (OUT from host), always forward */ + default: /* MUSB_EP0_STAGE_RX */ + break; + } + + DBG(3, "handled %d, csr %04x, ep0stage %s\n", + handled, csr, + decode_ep0stage(musb->ep0_state)); + + /* unless we need to delegate this to the gadget + * driver, we know how to wrap this up: csr0 has + * not yet been written. + */ + if (handled < 0) + goto stall; + else if (handled > 0) + goto finish; + + handled = forward_to_driver(musb, &setup); + if (handled < 0) { + musb_ep_select(mbase, 0); +stall: + DBG(3, "stall (%d)\n", handled); + musb->ackpend |= MUSB_CSR0_P_SENDSTALL; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; +finish: + musb_writew(regs, MUSB_CSR0, + musb->ackpend); + musb->ackpend = 0; + } + } + break; + + case MUSB_EP0_STAGE_ACKWAIT: + /* This should not happen. But happens with tusb6010 with + * g_file_storage and high speed. Do nothing. + */ + retval = IRQ_HANDLED; + break; + + default: + /* "can't happen" */ + WARN_ON(1); + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL); + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + break; + } + + return retval; +} + + +static int +musb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) +{ + /* always enabled */ + return -EINVAL; +} + +static int musb_g_ep0_disable(struct usb_ep *e) +{ + /* always enabled */ + return -EINVAL; +} + +static int +musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags) +{ + struct musb_ep *ep; + struct musb_request *req; + struct musb *musb; + int status; + unsigned long lockflags; + void __iomem *regs; + + if (!e || !r) + return -EINVAL; + + ep = to_musb_ep(e); + musb = ep->musb; + regs = musb->control_ep->regs; + + req = to_musb_request(r); + req->musb = musb; + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->tx = ep->is_in; + + spin_lock_irqsave(&musb->lock, lockflags); + + if (!list_empty(&ep->req_list)) { + status = -EBUSY; + goto cleanup; + } + + switch (musb->ep0_state) { + case MUSB_EP0_STAGE_RX: /* control-OUT data */ + case MUSB_EP0_STAGE_TX: /* control-IN data */ + case MUSB_EP0_STAGE_ACKWAIT: /* zero-length data */ + status = 0; + break; + default: + DBG(1, "ep0 request queued in state %d\n", + musb->ep0_state); + status = -EINVAL; + goto cleanup; + } + + /* add request to the list */ + list_add_tail(&(req->request.list), &(ep->req_list)); + + DBG(3, "queue to %s (%s), length=%d\n", + ep->name, ep->is_in ? "IN/TX" : "OUT/RX", + req->request.length); + + musb_ep_select(musb->mregs, 0); + + /* sequence #1, IN ... start writing the data */ + if (musb->ep0_state == MUSB_EP0_STAGE_TX) + ep0_txstate(musb); + + /* sequence #3, no-data ... issue IN status */ + else if (musb->ep0_state == MUSB_EP0_STAGE_ACKWAIT) { + if (req->request.length) + status = -EINVAL; + else { + musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; + musb_writew(regs, MUSB_CSR0, + musb->ackpend | MUSB_CSR0_P_DATAEND); + musb->ackpend = 0; + musb_g_ep0_giveback(ep->musb, r); + } + + /* else for sequence #2 (OUT), caller provides a buffer + * before the next packet arrives. deferred responses + * (after SETUP is acked) are racey. + */ + } else if (musb->ackpend) { + musb_writew(regs, MUSB_CSR0, musb->ackpend); + musb->ackpend = 0; + } + +cleanup: + spin_unlock_irqrestore(&musb->lock, lockflags); + return status; +} + +static int musb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + /* we just won't support this */ + return -EINVAL; +} + +static int musb_g_ep0_halt(struct usb_ep *e, int value) +{ + struct musb_ep *ep; + struct musb *musb; + void __iomem *base, *regs; + unsigned long flags; + int status; + u16 csr; + + if (!e || !value) + return -EINVAL; + + ep = to_musb_ep(e); + musb = ep->musb; + base = musb->mregs; + regs = musb->control_ep->regs; + status = 0; + + spin_lock_irqsave(&musb->lock, flags); + + if (!list_empty(&ep->req_list)) { + status = -EBUSY; + goto cleanup; + } + + musb_ep_select(base, 0); + csr = musb->ackpend; + + switch (musb->ep0_state) { + + /* Stalls are usually issued after parsing SETUP packet, either + * directly in irq context from setup() or else later. + */ + case MUSB_EP0_STAGE_TX: /* control-IN data */ + case MUSB_EP0_STAGE_ACKWAIT: /* STALL for zero-length data */ + case MUSB_EP0_STAGE_RX: /* control-OUT data */ + csr = musb_readw(regs, MUSB_CSR0); + /* FALLTHROUGH */ + + /* It's also OK to issue stalls during callbacks when a non-empty + * DATA stage buffer has been read (or even written). + */ + case MUSB_EP0_STAGE_STATUSIN: /* control-OUT status */ + case MUSB_EP0_STAGE_STATUSOUT: /* control-IN status */ + + csr |= MUSB_CSR0_P_SENDSTALL; + musb_writew(regs, MUSB_CSR0, csr); + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + musb->ackpend = 0; + break; + default: + DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state); + status = -EINVAL; + } + +cleanup: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +const struct usb_ep_ops musb_g_ep0_ops = { + .enable = musb_g_ep0_enable, + .disable = musb_g_ep0_disable, + .alloc_request = musb_alloc_request, + .free_request = musb_free_request, + .queue = musb_g_ep0_queue, + .dequeue = musb_g_ep0_dequeue, + .set_halt = musb_g_ep0_halt, +}; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c new file mode 100644 index 000000000000..8b4be012669a --- /dev/null +++ b/drivers/usb/musb/musb_host.c @@ -0,0 +1,2170 @@ +/* + * MUSB OTG driver host support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" +#include "musb_host.h" + + +/* MUSB HOST status 22-mar-2006 + * + * - There's still lots of partial code duplication for fault paths, so + * they aren't handled as consistently as they need to be. + * + * - PIO mostly behaved when last tested. + * + including ep0, with all usbtest cases 9, 10 + * + usbtest 14 (ep0out) doesn't seem to run at all + * + double buffered OUT/TX endpoints saw stalls(!) with certain usbtest + * configurations, but otherwise double buffering passes basic tests. + * + for 2.6.N, for N > ~10, needs API changes for hcd framework. + * + * - DMA (CPPI) ... partially behaves, not currently recommended + * + about 1/15 the speed of typical EHCI implementations (PCI) + * + RX, all too often reqpkt seems to misbehave after tx + * + TX, no known issues (other than evident silicon issue) + * + * - DMA (Mentor/OMAP) ...has at least toggle update problems + * + * - Still no traffic scheduling code to make NAKing for bulk or control + * transfers unable to starve other requests; or to make efficient use + * of hardware with periodic transfers. (Note that network drivers + * commonly post bulk reads that stay pending for a long time; these + * would make very visible trouble.) + * + * - Not tested with HNP, but some SRP paths seem to behave. + * + * NOTE 24-August-2006: + * + * - Bulk traffic finally uses both sides of hardware ep1, freeing up an + * extra endpoint for periodic use enabling hub + keybd + mouse. That + * mostly works, except that with "usbnet" it's easy to trigger cases + * with "ping" where RX loses. (a) ping to davinci, even "ping -f", + * fine; but (b) ping _from_ davinci, even "ping -c 1", ICMP RX loses + * although ARP RX wins. (That test was done with a full speed link.) + */ + + +/* + * NOTE on endpoint usage: + * + * CONTROL transfers all go through ep0. BULK ones go through dedicated IN + * and OUT endpoints ... hardware is dedicated for those "async" queue(s). + * + * (Yes, bulk _could_ use more of the endpoints than that, and would even + * benefit from it ... one remote device may easily be NAKing while others + * need to perform transfers in that same direction. The same thing could + * be done in software though, assuming dma cooperates.) + * + * INTERUPPT and ISOCHRONOUS transfers are scheduled to the other endpoints. + * So far that scheduling is both dumb and optimistic: the endpoint will be + * "claimed" until its software queue is no longer refilled. No multiplexing + * of transfers between endpoints, or anything clever. + */ + + +static void musb_ep_program(struct musb *musb, u8 epnum, + struct urb *urb, unsigned int nOut, + u8 *buf, u32 len); + +/* + * Clear TX fifo. Needed to avoid BABBLE errors. + */ +static inline void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) +{ + void __iomem *epio = ep->regs; + u16 csr; + int retries = 1000; + + csr = musb_readw(epio, MUSB_TXCSR); + while (csr & MUSB_TXCSR_FIFONOTEMPTY) { + DBG(5, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + csr |= MUSB_TXCSR_FLUSHFIFO; + musb_writew(epio, MUSB_TXCSR, csr); + csr = musb_readw(epio, MUSB_TXCSR); + if (retries-- < 1) { + ERR("Could not flush host TX fifo: csr: %04x\n", csr); + return; + } + mdelay(1); + } +} + +/* + * Start transmit. Caller is responsible for locking shared resources. + * musb must be locked. + */ +static inline void musb_h_tx_start(struct musb_hw_ep *ep) +{ + u16 txcsr; + + /* NOTE: no locks here; caller should lock and select EP */ + if (ep->epnum) { + txcsr = musb_readw(ep->regs, MUSB_TXCSR); + txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_H_WZC_BITS; + musb_writew(ep->regs, MUSB_TXCSR, txcsr); + } else { + txcsr = MUSB_CSR0_H_SETUPPKT | MUSB_CSR0_TXPKTRDY; + musb_writew(ep->regs, MUSB_CSR0, txcsr); + } + +} + +static inline void cppi_host_txdma_start(struct musb_hw_ep *ep) +{ + u16 txcsr; + + /* NOTE: no locks here; caller should lock and select EP */ + txcsr = musb_readw(ep->regs, MUSB_TXCSR); + txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; + musb_writew(ep->regs, MUSB_TXCSR, txcsr); +} + +/* + * Start the URB at the front of an endpoint's queue + * end must be claimed from the caller. + * + * Context: controller locked, irqs blocked + */ +static void +musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) +{ + u16 frame; + u32 len; + void *buf; + void __iomem *mbase = musb->mregs; + struct urb *urb = next_urb(qh); + struct musb_hw_ep *hw_ep = qh->hw_ep; + unsigned pipe = urb->pipe; + u8 address = usb_pipedevice(pipe); + int epnum = hw_ep->epnum; + + /* initialize software qh state */ + qh->offset = 0; + qh->segsize = 0; + + /* gather right source of data */ + switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + /* control transfers always start with SETUP */ + is_in = 0; + hw_ep->out_qh = qh; + musb->ep0_stage = MUSB_EP0_START; + buf = urb->setup_packet; + len = 8; + break; + case USB_ENDPOINT_XFER_ISOC: + qh->iso_idx = 0; + qh->frame = 0; + buf = urb->transfer_buffer + urb->iso_frame_desc[0].offset; + len = urb->iso_frame_desc[0].length; + break; + default: /* bulk, interrupt */ + buf = urb->transfer_buffer; + len = urb->transfer_buffer_length; + } + + DBG(4, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", + qh, urb, address, qh->epnum, + is_in ? "in" : "out", + ({char *s; switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: s = ""; break; + case USB_ENDPOINT_XFER_BULK: s = "-bulk"; break; + case USB_ENDPOINT_XFER_ISOC: s = "-iso"; break; + default: s = "-intr"; break; + }; s; }), + epnum, buf, len); + + /* Configure endpoint */ + if (is_in || hw_ep->is_shared_fifo) + hw_ep->in_qh = qh; + else + hw_ep->out_qh = qh; + musb_ep_program(musb, epnum, urb, !is_in, buf, len); + + /* transmit may have more work: start it when it is time */ + if (is_in) + return; + + /* determine if the time is right for a periodic transfer */ + switch (qh->type) { + case USB_ENDPOINT_XFER_ISOC: + case USB_ENDPOINT_XFER_INT: + DBG(3, "check whether there's still time for periodic Tx\n"); + qh->iso_idx = 0; + frame = musb_readw(mbase, MUSB_FRAME); + /* FIXME this doesn't implement that scheduling policy ... + * or handle framecounter wrapping + */ + if ((urb->transfer_flags & URB_ISO_ASAP) + || (frame >= urb->start_frame)) { + /* REVISIT the SOF irq handler shouldn't duplicate + * this code; and we don't init urb->start_frame... + */ + qh->frame = 0; + goto start; + } else { + qh->frame = urb->start_frame; + /* enable SOF interrupt so we can count down */ + DBG(1, "SOF for %d\n", epnum); +#if 1 /* ifndef CONFIG_ARCH_DAVINCI */ + musb_writeb(mbase, MUSB_INTRUSBE, 0xff); +#endif + } + break; + default: +start: + DBG(4, "Start TX%d %s\n", epnum, + hw_ep->tx_channel ? "dma" : "pio"); + + if (!hw_ep->tx_channel) + musb_h_tx_start(hw_ep); + else if (is_cppi_enabled() || tusb_dma_omap()) + cppi_host_txdma_start(hw_ep); + } +} + +/* caller owns controller lock, irqs are blocked */ +static void +__musb_giveback(struct musb *musb, struct urb *urb, int status) +__releases(musb->lock) +__acquires(musb->lock) +{ + DBG(({ int level; switch (urb->status) { + case 0: + level = 4; + break; + /* common/boring faults */ + case -EREMOTEIO: + case -ESHUTDOWN: + case -ECONNRESET: + case -EPIPE: + level = 3; + break; + default: + level = 2; + break; + }; level; }), + "complete %p (%d), dev%d ep%d%s, %d/%d\n", + urb, urb->status, + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->actual_length, urb->transfer_buffer_length + ); + + spin_unlock(&musb->lock); + usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status); + spin_lock(&musb->lock); +} + +/* for bulk/interrupt endpoints only */ +static inline void +musb_save_toggle(struct musb_hw_ep *ep, int is_in, struct urb *urb) +{ + struct usb_device *udev = urb->dev; + u16 csr; + void __iomem *epio = ep->regs; + struct musb_qh *qh; + + /* FIXME: the current Mentor DMA code seems to have + * problems getting toggle correct. + */ + + if (is_in || ep->is_shared_fifo) + qh = ep->in_qh; + else + qh = ep->out_qh; + + if (!is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + usb_settoggle(udev, qh->epnum, 1, + (csr & MUSB_TXCSR_H_DATATOGGLE) + ? 1 : 0); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + usb_settoggle(udev, qh->epnum, 0, + (csr & MUSB_RXCSR_H_DATATOGGLE) + ? 1 : 0); + } +} + +/* caller owns controller lock, irqs are blocked */ +static struct musb_qh * +musb_giveback(struct musb_qh *qh, struct urb *urb, int status) +{ + int is_in; + struct musb_hw_ep *ep = qh->hw_ep; + struct musb *musb = ep->musb; + int ready = qh->is_ready; + + if (ep->is_shared_fifo) + is_in = 1; + else + is_in = usb_pipein(urb->pipe); + + /* save toggle eagerly, for paranoia */ + switch (qh->type) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + musb_save_toggle(ep, is_in, urb); + break; + case USB_ENDPOINT_XFER_ISOC: + if (status == 0 && urb->error_count) + status = -EXDEV; + break; + } + + usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb); + + qh->is_ready = 0; + __musb_giveback(musb, urb, status); + qh->is_ready = ready; + + /* reclaim resources (and bandwidth) ASAP; deschedule it, and + * invalidate qh as soon as list_empty(&hep->urb_list) + */ + if (list_empty(&qh->hep->urb_list)) { + struct list_head *head; + + if (is_in) + ep->rx_reinit = 1; + else + ep->tx_reinit = 1; + + /* clobber old pointers to this qh */ + if (is_in || ep->is_shared_fifo) + ep->in_qh = NULL; + else + ep->out_qh = NULL; + qh->hep->hcpriv = NULL; + + switch (qh->type) { + + case USB_ENDPOINT_XFER_ISOC: + case USB_ENDPOINT_XFER_INT: + /* this is where periodic bandwidth should be + * de-allocated if it's tracked and allocated; + * and where we'd update the schedule tree... + */ + musb->periodic[ep->epnum] = NULL; + kfree(qh); + qh = NULL; + break; + + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + /* fifo policy for these lists, except that NAKing + * should rotate a qh to the end (for fairness). + */ + head = qh->ring.prev; + list_del(&qh->ring); + kfree(qh); + qh = first_qh(head); + break; + } + } + return qh; +} + +/* + * Advance this hardware endpoint's queue, completing the specified urb and + * advancing to either the next urb queued to that qh, or else invalidating + * that qh and advancing to the next qh scheduled after the current one. + * + * Context: caller owns controller lock, irqs are blocked + */ +static void +musb_advance_schedule(struct musb *musb, struct urb *urb, + struct musb_hw_ep *hw_ep, int is_in) +{ + struct musb_qh *qh; + + if (is_in || hw_ep->is_shared_fifo) + qh = hw_ep->in_qh; + else + qh = hw_ep->out_qh; + + if (urb->status == -EINPROGRESS) + qh = musb_giveback(qh, urb, 0); + else + qh = musb_giveback(qh, urb, urb->status); + + if (qh && qh->is_ready && !list_empty(&qh->hep->urb_list)) { + DBG(4, "... next ep%d %cX urb %p\n", + hw_ep->epnum, is_in ? 'R' : 'T', + next_urb(qh)); + musb_start_urb(musb, is_in, qh); + } +} + +static inline u16 musb_h_flush_rxfifo(struct musb_hw_ep *hw_ep, u16 csr) +{ + /* we don't want fifo to fill itself again; + * ignore dma (various models), + * leave toggle alone (may not have been saved yet) + */ + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_RXPKTRDY; + csr &= ~(MUSB_RXCSR_H_REQPKT + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + + /* write 2x to allow double buffering */ + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + + /* flush writebuffer */ + return musb_readw(hw_ep->regs, MUSB_RXCSR); +} + +/* + * PIO RX for a packet (or part of it). + */ +static bool +musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) +{ + u16 rx_count; + u8 *buf; + u16 csr; + bool done = false; + u32 length; + int do_flush = 0; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->in_qh; + int pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + + /* musb_ep_select(mbase, epnum); */ + rx_count = musb_readw(epio, MUSB_RXCOUNT); + DBG(3, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count, + urb->transfer_buffer, qh->offset, + urb->transfer_buffer_length); + + /* unload FIFO */ + if (usb_pipeisoc(pipe)) { + int status = 0; + struct usb_iso_packet_descriptor *d; + + if (iso_err) { + status = -EILSEQ; + urb->error_count++; + } + + d = urb->iso_frame_desc + qh->iso_idx; + buf = buffer + d->offset; + length = d->length; + if (rx_count > length) { + if (status == 0) { + status = -EOVERFLOW; + urb->error_count++; + } + DBG(2, "** OVERFLOW %d into %d\n", rx_count, length); + do_flush = 1; + } else + length = rx_count; + urb->actual_length += length; + d->actual_length = length; + + d->status = status; + + /* see if we are done */ + done = (++qh->iso_idx >= urb->number_of_packets); + } else { + /* non-isoch */ + buf = buffer + qh->offset; + length = urb->transfer_buffer_length - qh->offset; + if (rx_count > length) { + if (urb->status == -EINPROGRESS) + urb->status = -EOVERFLOW; + DBG(2, "** OVERFLOW %d into %d\n", rx_count, length); + do_flush = 1; + } else + length = rx_count; + urb->actual_length += length; + qh->offset += length; + + /* see if we are done */ + done = (urb->actual_length == urb->transfer_buffer_length) + || (rx_count < qh->maxpacket) + || (urb->status != -EINPROGRESS); + if (done + && (urb->status == -EINPROGRESS) + && (urb->transfer_flags & URB_SHORT_NOT_OK) + && (urb->actual_length + < urb->transfer_buffer_length)) + urb->status = -EREMOTEIO; + } + + musb_read_fifo(hw_ep, length, buf); + + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_H_WZC_BITS; + if (unlikely(do_flush)) + musb_h_flush_rxfifo(hw_ep, csr); + else { + /* REVISIT this assumes AUTOCLEAR is never set */ + csr &= ~(MUSB_RXCSR_RXPKTRDY | MUSB_RXCSR_H_REQPKT); + if (!done) + csr |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, csr); + } + + return done; +} + +/* we don't always need to reinit a given side of an endpoint... + * when we do, use tx/rx reinit routine and then construct a new CSR + * to address data toggle, NYET, and DMA or PIO. + * + * it's possible that driver bugs (especially for DMA) or aborting a + * transfer might have left the endpoint busier than it should be. + * the busy/not-empty tests are basically paranoia. + */ +static void +musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) +{ + u16 csr; + + /* NOTE: we know the "rx" fifo reinit never triggers for ep0. + * That always uses tx_reinit since ep0 repurposes TX register + * offsets; the initial SETUP packet is also a kind of OUT. + */ + + /* if programmed for Tx, put it in RX mode */ + if (ep->is_shared_fifo) { + csr = musb_readw(ep->regs, MUSB_TXCSR); + if (csr & MUSB_TXCSR_MODE) { + musb_h_tx_flush_fifo(ep); + musb_writew(ep->regs, MUSB_TXCSR, + MUSB_TXCSR_FRCDATATOG); + } + /* clear mode (and everything else) to enable Rx */ + musb_writew(ep->regs, MUSB_TXCSR, 0); + + /* scrub all previous state, clearing toggle */ + } else { + csr = musb_readw(ep->regs, MUSB_RXCSR); + if (csr & MUSB_RXCSR_RXPKTRDY) + WARNING("rx%d, packet/%d ready?\n", ep->epnum, + musb_readw(ep->regs, MUSB_RXCOUNT)); + + musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG); + } + + /* target addr and (for multipoint) hub addr/port */ + if (musb->is_multipoint) { + musb_writeb(ep->target_regs, MUSB_RXFUNCADDR, + qh->addr_reg); + musb_writeb(ep->target_regs, MUSB_RXHUBADDR, + qh->h_addr_reg); + musb_writeb(ep->target_regs, MUSB_RXHUBPORT, + qh->h_port_reg); + } else + musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); + + /* protocol/endpoint, interval/NAKlimit, i/o size */ + musb_writeb(ep->regs, MUSB_RXTYPE, qh->type_reg); + musb_writeb(ep->regs, MUSB_RXINTERVAL, qh->intv_reg); + /* NOTE: bulk combining rewrites high bits of maxpacket */ + musb_writew(ep->regs, MUSB_RXMAXP, qh->maxpacket); + + ep->rx_reinit = 0; +} + + +/* + * Program an HDRC endpoint as per the given URB + * Context: irqs blocked, controller lock held + */ +static void musb_ep_program(struct musb *musb, u8 epnum, + struct urb *urb, unsigned int is_out, + u8 *buf, u32 len) +{ + struct dma_controller *dma_controller; + struct dma_channel *dma_channel; + u8 dma_ok; + void __iomem *mbase = musb->mregs; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh; + u16 packet_sz; + + if (!is_out || hw_ep->is_shared_fifo) + qh = hw_ep->in_qh; + else + qh = hw_ep->out_qh; + + packet_sz = qh->maxpacket; + + DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s " + "h_addr%02x h_port%02x bytes %d\n", + is_out ? "-->" : "<--", + epnum, urb, urb->dev->speed, + qh->addr_reg, qh->epnum, is_out ? "out" : "in", + qh->h_addr_reg, qh->h_port_reg, + len); + + musb_ep_select(mbase, epnum); + + /* candidate for DMA? */ + dma_controller = musb->dma_controller; + if (is_dma_capable() && epnum && dma_controller) { + dma_channel = is_out ? hw_ep->tx_channel : hw_ep->rx_channel; + if (!dma_channel) { + dma_channel = dma_controller->channel_alloc( + dma_controller, hw_ep, is_out); + if (is_out) + hw_ep->tx_channel = dma_channel; + else + hw_ep->rx_channel = dma_channel; + } + } else + dma_channel = NULL; + + /* make sure we clear DMAEnab, autoSet bits from previous run */ + + /* OUT/transmit/EP0 or IN/receive? */ + if (is_out) { + u16 csr; + u16 int_txe; + u16 load_count; + + csr = musb_readw(epio, MUSB_TXCSR); + + /* disable interrupt in case we flush */ + int_txe = musb_readw(mbase, MUSB_INTRTXE); + musb_writew(mbase, MUSB_INTRTXE, int_txe & ~(1 << epnum)); + + /* general endpoint setup */ + if (epnum) { + /* ASSERT: TXCSR_DMAENAB was already cleared */ + + /* flush all old state, set default */ + musb_h_tx_flush_fifo(hw_ep); + csr &= ~(MUSB_TXCSR_H_NAKTIMEOUT + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_FRCDATATOG + | MUSB_TXCSR_H_RXSTALL + | MUSB_TXCSR_H_ERROR + | MUSB_TXCSR_TXPKTRDY + ); + csr |= MUSB_TXCSR_MODE; + + if (usb_gettoggle(urb->dev, + qh->epnum, 1)) + csr |= MUSB_TXCSR_H_WR_DATATOGGLE + | MUSB_TXCSR_H_DATATOGGLE; + else + csr |= MUSB_TXCSR_CLRDATATOG; + + /* twice in case of double packet buffering */ + musb_writew(epio, MUSB_TXCSR, csr); + /* REVISIT may need to clear FLUSHFIFO ... */ + musb_writew(epio, MUSB_TXCSR, csr); + csr = musb_readw(epio, MUSB_TXCSR); + } else { + /* endpoint 0: just flush */ + musb_writew(epio, MUSB_CSR0, + csr | MUSB_CSR0_FLUSHFIFO); + musb_writew(epio, MUSB_CSR0, + csr | MUSB_CSR0_FLUSHFIFO); + } + + /* target addr and (for multipoint) hub addr/port */ + if (musb->is_multipoint) { + musb_writeb(mbase, + MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), + qh->addr_reg); + musb_writeb(mbase, + MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + qh->h_addr_reg); + musb_writeb(mbase, + MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + qh->h_port_reg); +/* FIXME if !epnum, do the same for RX ... */ + } else + musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); + + /* protocol/endpoint/interval/NAKlimit */ + if (epnum) { + musb_writeb(epio, MUSB_TXTYPE, qh->type_reg); + if (can_bulk_split(musb, qh->type)) + musb_writew(epio, MUSB_TXMAXP, + packet_sz + | ((hw_ep->max_packet_sz_tx / + packet_sz) - 1) << 11); + else + musb_writew(epio, MUSB_TXMAXP, + packet_sz); + musb_writeb(epio, MUSB_TXINTERVAL, qh->intv_reg); + } else { + musb_writeb(epio, MUSB_NAKLIMIT0, qh->intv_reg); + if (musb->is_multipoint) + musb_writeb(epio, MUSB_TYPE0, + qh->type_reg); + } + + if (can_bulk_split(musb, qh->type)) + load_count = min((u32) hw_ep->max_packet_sz_tx, + len); + else + load_count = min((u32) packet_sz, len); + +#ifdef CONFIG_USB_INVENTRA_DMA + if (dma_channel) { + + /* clear previous state */ + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_DMAENAB); + csr |= MUSB_TXCSR_MODE; + musb_writew(epio, MUSB_TXCSR, + csr | MUSB_TXCSR_MODE); + + qh->segsize = min(len, dma_channel->max_len); + + if (qh->segsize <= packet_sz) + dma_channel->desired_mode = 0; + else + dma_channel->desired_mode = 1; + + + if (dma_channel->desired_mode == 0) { + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAMODE); + csr |= (MUSB_TXCSR_DMAENAB); + /* against programming guide */ + } else + csr |= (MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_DMAMODE); + + musb_writew(epio, MUSB_TXCSR, csr); + + dma_ok = dma_controller->channel_program( + dma_channel, packet_sz, + dma_channel->desired_mode, + urb->transfer_dma, + qh->segsize); + if (dma_ok) { + load_count = 0; + } else { + dma_controller->channel_release(dma_channel); + if (is_out) + hw_ep->tx_channel = NULL; + else + hw_ep->rx_channel = NULL; + dma_channel = NULL; + } + } +#endif + + /* candidate for DMA */ + if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + + /* program endpoint CSRs first, then setup DMA. + * assume CPPI setup succeeds. + * defer enabling dma. + */ + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_DMAENAB); + csr |= MUSB_TXCSR_MODE; + musb_writew(epio, MUSB_TXCSR, + csr | MUSB_TXCSR_MODE); + + dma_channel->actual_len = 0L; + qh->segsize = len; + + /* TX uses "rndis" mode automatically, but needs help + * to identify the zero-length-final-packet case. + */ + dma_ok = dma_controller->channel_program( + dma_channel, packet_sz, + (urb->transfer_flags + & URB_ZERO_PACKET) + == URB_ZERO_PACKET, + urb->transfer_dma, + qh->segsize); + if (dma_ok) { + load_count = 0; + } else { + dma_controller->channel_release(dma_channel); + hw_ep->tx_channel = NULL; + dma_channel = NULL; + + /* REVISIT there's an error path here that + * needs handling: can't do dma, but + * there's no pio buffer address... + */ + } + } + + if (load_count) { + /* ASSERT: TXCSR_DMAENAB was already cleared */ + + /* PIO to load FIFO */ + qh->segsize = load_count; + musb_write_fifo(hw_ep, load_count, buf); + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_AUTOSET); + /* write CSR */ + csr |= MUSB_TXCSR_MODE; + + if (epnum) + musb_writew(epio, MUSB_TXCSR, csr); + } + + /* re-enable interrupt */ + musb_writew(mbase, MUSB_INTRTXE, int_txe); + + /* IN/receive */ + } else { + u16 csr; + + if (hw_ep->rx_reinit) { + musb_rx_reinit(musb, qh, hw_ep); + + /* init new state: toggle and NYET, maybe DMA later */ + if (usb_gettoggle(urb->dev, qh->epnum, 0)) + csr = MUSB_RXCSR_H_WR_DATATOGGLE + | MUSB_RXCSR_H_DATATOGGLE; + else + csr = 0; + if (qh->type == USB_ENDPOINT_XFER_INT) + csr |= MUSB_RXCSR_DISNYET; + + } else { + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + + if (csr & (MUSB_RXCSR_RXPKTRDY + | MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_REQPKT)) + ERR("broken !rx_reinit, ep%d csr %04x\n", + hw_ep->epnum, csr); + + /* scrub any stale state, leaving toggle alone */ + csr &= MUSB_RXCSR_DISNYET; + } + + /* kick things off */ + + if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + /* candidate for DMA */ + if (dma_channel) { + dma_channel->actual_len = 0L; + qh->segsize = len; + + /* AUTOREQ is in a DMA register */ + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + csr = musb_readw(hw_ep->regs, + MUSB_RXCSR); + + /* unless caller treats short rx transfers as + * errors, we dare not queue multiple transfers. + */ + dma_ok = dma_controller->channel_program( + dma_channel, packet_sz, + !(urb->transfer_flags + & URB_SHORT_NOT_OK), + urb->transfer_dma, + qh->segsize); + if (!dma_ok) { + dma_controller->channel_release( + dma_channel); + hw_ep->rx_channel = NULL; + dma_channel = NULL; + } else + csr |= MUSB_RXCSR_DMAENAB; + } + } + + csr |= MUSB_RXCSR_H_REQPKT; + DBG(7, "RXCSR%d := %04x\n", epnum, csr); + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + } +} + + +/* + * Service the default endpoint (ep0) as host. + * Return true until it's time to start the status stage. + */ +static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) +{ + bool more = false; + u8 *fifo_dest = NULL; + u16 fifo_count = 0; + struct musb_hw_ep *hw_ep = musb->control_ep; + struct musb_qh *qh = hw_ep->in_qh; + struct usb_ctrlrequest *request; + + switch (musb->ep0_stage) { + case MUSB_EP0_IN: + fifo_dest = urb->transfer_buffer + urb->actual_length; + fifo_count = min(len, ((u16) (urb->transfer_buffer_length + - urb->actual_length))); + if (fifo_count < len) + urb->status = -EOVERFLOW; + + musb_read_fifo(hw_ep, fifo_count, fifo_dest); + + urb->actual_length += fifo_count; + if (len < qh->maxpacket) { + /* always terminate on short read; it's + * rarely reported as an error. + */ + } else if (urb->actual_length < + urb->transfer_buffer_length) + more = true; + break; + case MUSB_EP0_START: + request = (struct usb_ctrlrequest *) urb->setup_packet; + + if (!request->wLength) { + DBG(4, "start no-DATA\n"); + break; + } else if (request->bRequestType & USB_DIR_IN) { + DBG(4, "start IN-DATA\n"); + musb->ep0_stage = MUSB_EP0_IN; + more = true; + break; + } else { + DBG(4, "start OUT-DATA\n"); + musb->ep0_stage = MUSB_EP0_OUT; + more = true; + } + /* FALLTHROUGH */ + case MUSB_EP0_OUT: + fifo_count = min(qh->maxpacket, ((u16) + (urb->transfer_buffer_length + - urb->actual_length))); + + if (fifo_count) { + fifo_dest = (u8 *) (urb->transfer_buffer + + urb->actual_length); + DBG(3, "Sending %d bytes to %p\n", + fifo_count, fifo_dest); + musb_write_fifo(hw_ep, fifo_count, fifo_dest); + + urb->actual_length += fifo_count; + more = true; + } + break; + default: + ERR("bogus ep0 stage %d\n", musb->ep0_stage); + break; + } + + return more; +} + +/* + * Handle default endpoint interrupt as host. Only called in IRQ time + * from the LinuxIsr() interrupt service routine. + * + * called with controller irqlocked + */ +irqreturn_t musb_h_ep0_irq(struct musb *musb) +{ + struct urb *urb; + u16 csr, len; + int status = 0; + void __iomem *mbase = musb->mregs; + struct musb_hw_ep *hw_ep = musb->control_ep; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->in_qh; + bool complete = false; + irqreturn_t retval = IRQ_NONE; + + /* ep0 only has one queue, "in" */ + urb = next_urb(qh); + + musb_ep_select(mbase, 0); + csr = musb_readw(epio, MUSB_CSR0); + len = (csr & MUSB_CSR0_RXPKTRDY) + ? musb_readb(epio, MUSB_COUNT0) + : 0; + + DBG(4, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n", + csr, qh, len, urb, musb->ep0_stage); + + /* if we just did status stage, we are done */ + if (MUSB_EP0_STATUS == musb->ep0_stage) { + retval = IRQ_HANDLED; + complete = true; + } + + /* prepare status */ + if (csr & MUSB_CSR0_H_RXSTALL) { + DBG(6, "STALLING ENDPOINT\n"); + status = -EPIPE; + + } else if (csr & MUSB_CSR0_H_ERROR) { + DBG(2, "no response, csr0 %04x\n", csr); + status = -EPROTO; + + } else if (csr & MUSB_CSR0_H_NAKTIMEOUT) { + DBG(2, "control NAK timeout\n"); + + /* NOTE: this code path would be a good place to PAUSE a + * control transfer, if another one is queued, so that + * ep0 is more likely to stay busy. + * + * if (qh->ring.next != &musb->control), then + * we have a candidate... NAKing is *NOT* an error + */ + musb_writew(epio, MUSB_CSR0, 0); + retval = IRQ_HANDLED; + } + + if (status) { + DBG(6, "aborting\n"); + retval = IRQ_HANDLED; + if (urb) + urb->status = status; + complete = true; + + /* use the proper sequence to abort the transfer */ + if (csr & MUSB_CSR0_H_REQPKT) { + csr &= ~MUSB_CSR0_H_REQPKT; + musb_writew(epio, MUSB_CSR0, csr); + csr &= ~MUSB_CSR0_H_NAKTIMEOUT; + musb_writew(epio, MUSB_CSR0, csr); + } else { + csr |= MUSB_CSR0_FLUSHFIFO; + musb_writew(epio, MUSB_CSR0, csr); + musb_writew(epio, MUSB_CSR0, csr); + csr &= ~MUSB_CSR0_H_NAKTIMEOUT; + musb_writew(epio, MUSB_CSR0, csr); + } + + musb_writeb(epio, MUSB_NAKLIMIT0, 0); + + /* clear it */ + musb_writew(epio, MUSB_CSR0, 0); + } + + if (unlikely(!urb)) { + /* stop endpoint since we have no place for its data, this + * SHOULD NEVER HAPPEN! */ + ERR("no URB for end 0\n"); + + musb_writew(epio, MUSB_CSR0, MUSB_CSR0_FLUSHFIFO); + musb_writew(epio, MUSB_CSR0, MUSB_CSR0_FLUSHFIFO); + musb_writew(epio, MUSB_CSR0, 0); + + goto done; + } + + if (!complete) { + /* call common logic and prepare response */ + if (musb_h_ep0_continue(musb, len, urb)) { + /* more packets required */ + csr = (MUSB_EP0_IN == musb->ep0_stage) + ? MUSB_CSR0_H_REQPKT : MUSB_CSR0_TXPKTRDY; + } else { + /* data transfer complete; perform status phase */ + if (usb_pipeout(urb->pipe) + || !urb->transfer_buffer_length) + csr = MUSB_CSR0_H_STATUSPKT + | MUSB_CSR0_H_REQPKT; + else + csr = MUSB_CSR0_H_STATUSPKT + | MUSB_CSR0_TXPKTRDY; + + /* flag status stage */ + musb->ep0_stage = MUSB_EP0_STATUS; + + DBG(5, "ep0 STATUS, csr %04x\n", csr); + + } + musb_writew(epio, MUSB_CSR0, csr); + retval = IRQ_HANDLED; + } else + musb->ep0_stage = MUSB_EP0_IDLE; + + /* call completion handler if done */ + if (complete) + musb_advance_schedule(musb, urb, hw_ep, 1); +done: + return retval; +} + + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Host side TX (OUT) using Mentor DMA works as follows: + submit_urb -> + - if queue was empty, Program Endpoint + - ... which starts DMA to fifo in mode 1 or 0 + + DMA Isr (transfer complete) -> TxAvail() + - Stop DMA (~DmaEnab) (<--- Alert ... currently happens + only in musb_cleanup_urb) + - TxPktRdy has to be set in mode 0 or for + short packets in mode 1. +*/ + +#endif + +/* Service a Tx-Available or dma completion irq for the endpoint */ +void musb_host_tx(struct musb *musb, u8 epnum) +{ + int pipe; + bool done = false; + u16 tx_csr; + size_t wLength = 0; + u8 *buf = NULL; + struct urb *urb; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->out_qh; + u32 status = 0; + void __iomem *mbase = musb->mregs; + struct dma_channel *dma; + + urb = next_urb(qh); + + musb_ep_select(mbase, epnum); + tx_csr = musb_readw(epio, MUSB_TXCSR); + + /* with CPPI, DMA sometimes triggers "extra" irqs */ + if (!urb) { + DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + goto finish; + } + + pipe = urb->pipe; + dma = is_dma_capable() ? hw_ep->tx_channel : NULL; + DBG(4, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, + dma ? ", dma" : ""); + + /* check for errors */ + if (tx_csr & MUSB_TXCSR_H_RXSTALL) { + /* dma was disabled, fifo flushed */ + DBG(3, "TX end %d stall\n", epnum); + + /* stall; record URB status */ + status = -EPIPE; + + } else if (tx_csr & MUSB_TXCSR_H_ERROR) { + /* (NON-ISO) dma was disabled, fifo flushed */ + DBG(3, "TX 3strikes on ep=%d\n", epnum); + + status = -ETIMEDOUT; + + } else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) { + DBG(6, "TX end=%d device not responding\n", epnum); + + /* NOTE: this code path would be a good place to PAUSE a + * transfer, if there's some other (nonperiodic) tx urb + * that could use this fifo. (dma complicates it...) + * + * if (bulk && qh->ring.next != &musb->out_bulk), then + * we have a candidate... NAKing is *NOT* an error + */ + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_H_WZC_BITS + | MUSB_TXCSR_TXPKTRDY); + goto finish; + } + + if (status) { + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + } + + /* do the proper sequence to abort the transfer in the + * usb core; the dma engine should already be stopped. + */ + musb_h_tx_flush_fifo(hw_ep); + tx_csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_H_ERROR + | MUSB_TXCSR_H_RXSTALL + | MUSB_TXCSR_H_NAKTIMEOUT + ); + + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_TXCSR, tx_csr); + /* REVISIT may need to clear FLUSHFIFO ... */ + musb_writew(epio, MUSB_TXCSR, tx_csr); + musb_writeb(epio, MUSB_TXINTERVAL, 0); + + done = true; + } + + /* second cppi case */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + goto finish; + + } + + /* REVISIT this looks wrong... */ + if (!status || dma || usb_pipeisoc(pipe)) { + if (dma) + wLength = dma->actual_len; + else + wLength = qh->segsize; + qh->offset += wLength; + + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = qh->segsize; + if (++qh->iso_idx >= urb->number_of_packets) { + done = true; + } else { + d++; + buf = urb->transfer_buffer + d->offset; + wLength = d->length; + } + } else if (dma) { + done = true; + } else { + /* see if we need to send more data, or ZLP */ + if (qh->segsize < qh->maxpacket) + done = true; + else if (qh->offset == urb->transfer_buffer_length + && !(urb->transfer_flags + & URB_ZERO_PACKET)) + done = true; + if (!done) { + buf = urb->transfer_buffer + + qh->offset; + wLength = urb->transfer_buffer_length + - qh->offset; + } + } + } + + /* urb->status != -EINPROGRESS means request has been faulted, + * so we must abort this transfer after cleanup + */ + if (urb->status != -EINPROGRESS) { + done = true; + if (status == 0) + status = urb->status; + } + + if (done) { + /* set status */ + urb->status = status; + urb->actual_length = qh->offset; + musb_advance_schedule(musb, urb, hw_ep, USB_DIR_OUT); + + } else if (!(tx_csr & MUSB_TXCSR_DMAENAB)) { + /* WARN_ON(!buf); */ + + /* REVISIT: some docs say that when hw_ep->tx_double_buffered, + * (and presumably, fifo is not half-full) we should write TWO + * packets before updating TXCSR ... other docs disagree ... + */ + /* PIO: start next packet in this URB */ + wLength = min(qh->maxpacket, (u16) wLength); + musb_write_fifo(hw_ep, wLength, buf); + qh->segsize = wLength; + + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); + } else + DBG(1, "not complete, but dma enabled?\n"); + +finish: + return; +} + + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Host side RX (IN) using Mentor DMA works as follows: + submit_urb -> + - if queue was empty, ProgramEndpoint + - first IN token is sent out (by setting ReqPkt) + LinuxIsr -> RxReady() + /\ => first packet is received + | - Set in mode 0 (DmaEnab, ~ReqPkt) + | -> DMA Isr (transfer complete) -> RxReady() + | - Ack receive (~RxPktRdy), turn off DMA (~DmaEnab) + | - if urb not complete, send next IN token (ReqPkt) + | | else complete urb. + | | + --------------------------- + * + * Nuances of mode 1: + * For short packets, no ack (+RxPktRdy) is sent automatically + * (even if AutoClear is ON) + * For full packets, ack (~RxPktRdy) and next IN token (+ReqPkt) is sent + * automatically => major problem, as collecting the next packet becomes + * difficult. Hence mode 1 is not used. + * + * REVISIT + * All we care about at this driver level is that + * (a) all URBs terminate with REQPKT cleared and fifo(s) empty; + * (b) termination conditions are: short RX, or buffer full; + * (c) fault modes include + * - iff URB_SHORT_NOT_OK, short RX status is -EREMOTEIO. + * (and that endpoint's dma queue stops immediately) + * - overflow (full, PLUS more bytes in the terminal packet) + * + * So for example, usb-storage sets URB_SHORT_NOT_OK, and would + * thus be a great candidate for using mode 1 ... for all but the + * last packet of one URB's transfer. + */ + +#endif + +/* + * Service an RX interrupt for the given IN endpoint; docs cover bulk, iso, + * and high-bandwidth IN transfer cases. + */ +void musb_host_rx(struct musb *musb, u8 epnum) +{ + struct urb *urb; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->in_qh; + size_t xfer_len; + void __iomem *mbase = musb->mregs; + int pipe; + u16 rx_csr, val; + bool iso_err = false; + bool done = false; + u32 status; + struct dma_channel *dma; + + musb_ep_select(mbase, epnum); + + urb = next_urb(qh); + dma = is_dma_capable() ? hw_ep->rx_channel : NULL; + status = 0; + xfer_len = 0; + + rx_csr = musb_readw(epio, MUSB_RXCSR); + val = rx_csr; + + if (unlikely(!urb)) { + /* REVISIT -- THIS SHOULD NEVER HAPPEN ... but, at least + * usbtest #11 (unlinks) triggers it regularly, sometimes + * with fifo full. (Only with DMA??) + */ + DBG(3, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, + musb_readw(epio, MUSB_RXCOUNT)); + musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); + return; + } + + pipe = urb->pipe; + + DBG(5, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", + epnum, rx_csr, urb->actual_length, + dma ? dma->actual_len : 0); + + /* check for errors, concurrent stall & unlink is not really + * handled yet! */ + if (rx_csr & MUSB_RXCSR_H_RXSTALL) { + DBG(3, "RX end %d STALL\n", epnum); + + /* stall; record URB status */ + status = -EPIPE; + + } else if (rx_csr & MUSB_RXCSR_H_ERROR) { + DBG(3, "end %d RX proto error\n", epnum); + + status = -EPROTO; + musb_writeb(epio, MUSB_RXINTERVAL, 0); + + } else if (rx_csr & MUSB_RXCSR_DATAERROR) { + + if (USB_ENDPOINT_XFER_ISOC != qh->type) { + /* NOTE this code path would be a good place to PAUSE a + * transfer, if there's some other (nonperiodic) rx urb + * that could use this fifo. (dma complicates it...) + * + * if (bulk && qh->ring.next != &musb->in_bulk), then + * we have a candidate... NAKing is *NOT* an error + */ + DBG(6, "RX end %d NAK timeout\n", epnum); + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS + | MUSB_RXCSR_H_REQPKT); + + goto finish; + } else { + DBG(4, "RX end %d ISO data error\n", epnum); + /* packet error reported later */ + iso_err = true; + } + } + + /* faults abort the transfer */ + if (status) { + /* clean up dma and collect transfer count */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + xfer_len = dma->actual_len; + } + musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); + musb_writeb(epio, MUSB_RXINTERVAL, 0); + done = true; + goto finish; + } + + if (unlikely(dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY)) { + /* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */ + ERR("RX%d dma busy, csr %04x\n", epnum, rx_csr); + goto finish; + } + + /* thorough shutdown for now ... given more precise fault handling + * and better queueing support, we might keep a DMA pipeline going + * while processing this irq for earlier completions. + */ + + /* FIXME this is _way_ too much in-line logic for Mentor DMA */ + +#ifndef CONFIG_USB_INVENTRA_DMA + if (rx_csr & MUSB_RXCSR_H_REQPKT) { + /* REVISIT this happened for a while on some short reads... + * the cleanup still needs investigation... looks bad... + * and also duplicates dma cleanup code above ... plus, + * shouldn't this be the "half full" double buffer case? + */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + xfer_len = dma->actual_len; + done = true; + } + + DBG(2, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, + xfer_len, dma ? ", dma" : ""); + rx_csr &= ~MUSB_RXCSR_H_REQPKT; + + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | rx_csr); + } +#endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { + xfer_len = dma->actual_len; + + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_RXPKTRDY); + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + +#ifdef CONFIG_USB_INVENTRA_DMA + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length + || dma->actual_len < qh->maxpacket); + + /* send IN token for next packet, without AUTOREQ */ + if (!done) { + val |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | val); + } + + DBG(4, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, + done ? "off" : "reset", + musb_readw(epio, MUSB_RXCSR), + musb_readw(epio, MUSB_RXCOUNT)); +#else + done = true; +#endif + } else if (urb->status == -EINPROGRESS) { + /* if no errors, be sure a packet is ready for unloading */ + if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { + status = -EPROTO; + ERR("Rx interrupt with no errors or packet!\n"); + + /* FIXME this is another "SHOULD NEVER HAPPEN" */ + +/* SCRUB (RX) */ + /* do the proper sequence to abort the transfer */ + musb_ep_select(mbase, epnum); + val &= ~MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, val); + goto finish; + } + + /* we are expecting IN packets */ +#ifdef CONFIG_USB_INVENTRA_DMA + if (dma) { + struct dma_controller *c; + u16 rx_count; + int ret; + + rx_count = musb_readw(epio, MUSB_RXCOUNT); + + DBG(2, "RX%d count %d, buffer 0x%x len %d/%d\n", + epnum, rx_count, + urb->transfer_dma + + urb->actual_length, + qh->offset, + urb->transfer_buffer_length); + + c = musb->dma_controller; + + dma->desired_mode = 0; +#ifdef USE_MODE1 + /* because of the issue below, mode 1 will + * only rarely behave with correct semantics. + */ + if ((urb->transfer_flags & + URB_SHORT_NOT_OK) + && (urb->transfer_buffer_length - + urb->actual_length) + > qh->maxpacket) + dma->desired_mode = 1; +#endif + +/* Disadvantage of using mode 1: + * It's basically usable only for mass storage class; essentially all + * other protocols also terminate transfers on short packets. + * + * Details: + * An extra IN token is sent at the end of the transfer (due to AUTOREQ) + * If you try to use mode 1 for (transfer_buffer_length - 512), and try + * to use the extra IN token to grab the last packet using mode 0, then + * the problem is that you cannot be sure when the device will send the + * last packet and RxPktRdy set. Sometimes the packet is recd too soon + * such that it gets lost when RxCSR is re-set at the end of the mode 1 + * transfer, while sometimes it is recd just a little late so that if you + * try to configure for mode 0 soon after the mode 1 transfer is + * completed, you will find rxcount 0. Okay, so you might think why not + * wait for an interrupt when the pkt is recd. Well, you won't get any! + */ + + val = musb_readw(epio, MUSB_RXCSR); + val &= ~MUSB_RXCSR_H_REQPKT; + + if (dma->desired_mode == 0) + val &= ~MUSB_RXCSR_H_AUTOREQ; + else + val |= MUSB_RXCSR_H_AUTOREQ; + val |= MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB; + + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | val); + + /* REVISIT if when actual_length != 0, + * transfer_buffer_length needs to be + * adjusted first... + */ + ret = c->channel_program( + dma, qh->maxpacket, + dma->desired_mode, + urb->transfer_dma + + urb->actual_length, + (dma->desired_mode == 0) + ? rx_count + : urb->transfer_buffer_length); + + if (!ret) { + c->channel_release(dma); + hw_ep->rx_channel = NULL; + dma = NULL; + /* REVISIT reset CSR */ + } + } +#endif /* Mentor DMA */ + + if (!dma) { + done = musb_host_packet_rx(musb, urb, + epnum, iso_err); + DBG(6, "read %spacket\n", done ? "last " : ""); + } + } + + if (dma && usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + int iso_stat = status; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length += xfer_len; + if (iso_err) { + iso_stat = -EILSEQ; + urb->error_count++; + } + d->status = iso_stat; + } + +finish: + urb->actual_length += xfer_len; + qh->offset += xfer_len; + if (done) { + if (urb->status == -EINPROGRESS) + urb->status = status; + musb_advance_schedule(musb, urb, hw_ep, USB_DIR_IN); + } +} + +/* schedule nodes correspond to peripheral endpoints, like an OHCI QH. + * the software schedule associates multiple such nodes with a given + * host side hardware endpoint + direction; scheduling may activate + * that hardware endpoint. + */ +static int musb_schedule( + struct musb *musb, + struct musb_qh *qh, + int is_in) +{ + int idle; + int best_diff; + int best_end, epnum; + struct musb_hw_ep *hw_ep = NULL; + struct list_head *head = NULL; + + /* use fixed hardware for control and bulk */ + switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + head = &musb->control; + hw_ep = musb->control_ep; + break; + case USB_ENDPOINT_XFER_BULK: + hw_ep = musb->bulk_ep; + if (is_in) + head = &musb->in_bulk; + else + head = &musb->out_bulk; + break; + } + if (head) { + idle = list_empty(head); + list_add_tail(&qh->ring, head); + goto success; + } + + /* else, periodic transfers get muxed to other endpoints */ + + /* FIXME this doesn't consider direction, so it can only + * work for one half of the endpoint hardware, and assumes + * the previous cases handled all non-shared endpoints... + */ + + /* we know this qh hasn't been scheduled, so all we need to do + * is choose which hardware endpoint to put it on ... + * + * REVISIT what we really want here is a regular schedule tree + * like e.g. OHCI uses, but for now musb->periodic is just an + * array of the _single_ logical endpoint associated with a + * given physical one (identity mapping logical->physical). + * + * that simplistic approach makes TT scheduling a lot simpler; + * there is none, and thus none of its complexity... + */ + best_diff = 4096; + best_end = -1; + + for (epnum = 1; epnum < musb->nr_endpoints; epnum++) { + int diff; + + if (musb->periodic[epnum]) + continue; + hw_ep = &musb->endpoints[epnum]; + if (hw_ep == musb->bulk_ep) + continue; + + if (is_in) + diff = hw_ep->max_packet_sz_rx - qh->maxpacket; + else + diff = hw_ep->max_packet_sz_tx - qh->maxpacket; + + if (diff > 0 && best_diff > diff) { + best_diff = diff; + best_end = epnum; + } + } + if (best_end < 0) + return -ENOSPC; + + idle = 1; + hw_ep = musb->endpoints + best_end; + musb->periodic[best_end] = qh; + DBG(4, "qh %p periodic slot %d\n", qh, best_end); +success: + qh->hw_ep = hw_ep; + qh->hep->hcpriv = qh; + if (idle) + musb_start_urb(musb, is_in, qh); + return 0; +} + +static int musb_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + unsigned long flags; + struct musb *musb = hcd_to_musb(hcd); + struct usb_host_endpoint *hep = urb->ep; + struct musb_qh *qh = hep->hcpriv; + struct usb_endpoint_descriptor *epd = &hep->desc; + int ret; + unsigned type_reg; + unsigned interval; + + /* host role must be active */ + if (!is_host_active(musb) || !musb->is_active) + return -ENODEV; + + spin_lock_irqsave(&musb->lock, flags); + ret = usb_hcd_link_urb_to_ep(hcd, urb); + spin_unlock_irqrestore(&musb->lock, flags); + if (ret) + return ret; + + /* DMA mapping was already done, if needed, and this urb is on + * hep->urb_list ... so there's little to do unless hep wasn't + * yet scheduled onto a live qh. + * + * REVISIT best to keep hep->hcpriv valid until the endpoint gets + * disabled, testing for empty qh->ring and avoiding qh setup costs + * except for the first urb queued after a config change. + */ + if (qh) { + urb->hcpriv = qh; + return 0; + } + + /* Allocate and initialize qh, minimizing the work done each time + * hw_ep gets reprogrammed, or with irqs blocked. Then schedule it. + * + * REVISIT consider a dedicated qh kmem_cache, so it's harder + * for bugs in other kernel code to break this driver... + */ + qh = kzalloc(sizeof *qh, mem_flags); + if (!qh) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + return -ENOMEM; + } + + qh->hep = hep; + qh->dev = urb->dev; + INIT_LIST_HEAD(&qh->ring); + qh->is_ready = 1; + + qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize); + + /* no high bandwidth support yet */ + if (qh->maxpacket & ~0x7ff) { + ret = -EMSGSIZE; + goto done; + } + + qh->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + qh->type = epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */ + qh->addr_reg = (u8) usb_pipedevice(urb->pipe); + + /* precompute rxtype/txtype/type0 register */ + type_reg = (qh->type << 4) | qh->epnum; + switch (urb->dev->speed) { + case USB_SPEED_LOW: + type_reg |= 0xc0; + break; + case USB_SPEED_FULL: + type_reg |= 0x80; + break; + default: + type_reg |= 0x40; + } + qh->type_reg = type_reg; + + /* precompute rxinterval/txinterval register */ + interval = min((u8)16, epd->bInterval); /* log encoding */ + switch (qh->type) { + case USB_ENDPOINT_XFER_INT: + /* fullspeed uses linear encoding */ + if (USB_SPEED_FULL == urb->dev->speed) { + interval = epd->bInterval; + if (!interval) + interval = 1; + } + /* FALLTHROUGH */ + case USB_ENDPOINT_XFER_ISOC: + /* iso always uses log encoding */ + break; + default: + /* REVISIT we actually want to use NAK limits, hinting to the + * transfer scheduling logic to try some other qh, e.g. try + * for 2 msec first: + * + * interval = (USB_SPEED_HIGH == urb->dev->speed) ? 16 : 2; + * + * The downside of disabling this is that transfer scheduling + * gets VERY unfair for nonperiodic transfers; a misbehaving + * peripheral could make that hurt. Or for reads, one that's + * perfectly normal: network and other drivers keep reads + * posted at all times, having one pending for a week should + * be perfectly safe. + * + * The upside of disabling it is avoidng transfer scheduling + * code to put this aside for while. + */ + interval = 0; + } + qh->intv_reg = interval; + + /* precompute addressing for external hub/tt ports */ + if (musb->is_multipoint) { + struct usb_device *parent = urb->dev->parent; + + if (parent != hcd->self.root_hub) { + qh->h_addr_reg = (u8) parent->devnum; + + /* set up tt info if needed */ + if (urb->dev->tt) { + qh->h_port_reg = (u8) urb->dev->ttport; + qh->h_addr_reg |= 0x80; + } + } + } + + /* invariant: hep->hcpriv is null OR the qh that's already scheduled. + * until we get real dma queues (with an entry for each urb/buffer), + * we only have work to do in the former case. + */ + spin_lock_irqsave(&musb->lock, flags); + if (hep->hcpriv) { + /* some concurrent activity submitted another urb to hep... + * odd, rare, error prone, but legal. + */ + kfree(qh); + ret = 0; + } else + ret = musb_schedule(musb, qh, + epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK); + + if (ret == 0) { + urb->hcpriv = qh; + /* FIXME set urb->start_frame for iso/intr, it's tested in + * musb_start_urb(), but otherwise only konicawc cares ... + */ + } + spin_unlock_irqrestore(&musb->lock, flags); + +done: + if (ret != 0) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + kfree(qh); + } + return ret; +} + + +/* + * abort a transfer that's at the head of a hardware queue. + * called with controller locked, irqs blocked + * that hardware queue advances to the next transfer, unless prevented + */ +static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in) +{ + struct musb_hw_ep *ep = qh->hw_ep; + void __iomem *epio = ep->regs; + unsigned hw_end = ep->epnum; + void __iomem *regs = ep->musb->mregs; + u16 csr; + int status = 0; + + musb_ep_select(regs, hw_end); + + if (is_dma_capable()) { + struct dma_channel *dma; + + dma = is_in ? ep->rx_channel : ep->tx_channel; + if (dma) { + status = ep->musb->dma_controller->channel_abort(dma); + DBG(status ? 1 : 3, + "abort %cX%d DMA for urb %p --> %d\n", + is_in ? 'R' : 'T', ep->epnum, + urb, status); + urb->actual_length += dma->actual_len; + } + } + + /* turn off DMA requests, discard state, stop polling ... */ + if (is_in) { + /* giveback saves bulk toggle */ + csr = musb_h_flush_rxfifo(ep, 0); + + /* REVISIT we still get an irq; should likely clear the + * endpoint's irq status here to avoid bogus irqs. + * clearing that status is platform-specific... + */ + } else { + musb_h_tx_flush_fifo(ep); + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_H_RXSTALL + | MUSB_TXCSR_H_NAKTIMEOUT + | MUSB_TXCSR_H_ERROR + | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); + /* REVISIT may need to clear FLUSHFIFO ... */ + musb_writew(epio, MUSB_TXCSR, csr); + /* flush cpu writebuffer */ + csr = musb_readw(epio, MUSB_TXCSR); + } + if (status == 0) + musb_advance_schedule(ep->musb, urb, ep, is_in); + return status; +} + +static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct musb *musb = hcd_to_musb(hcd); + struct musb_qh *qh; + struct list_head *sched; + unsigned long flags; + int ret; + + DBG(4, "urb=%p, dev%d ep%d%s\n", urb, + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + + spin_lock_irqsave(&musb->lock, flags); + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) + goto done; + + qh = urb->hcpriv; + if (!qh) + goto done; + + /* Any URB not actively programmed into endpoint hardware can be + * immediately given back. Such an URB must be at the head of its + * endpoint queue, unless someday we get real DMA queues. And even + * then, it might not be known to the hardware... + * + * Otherwise abort current transfer, pending dma, etc.; urb->status + * has already been updated. This is a synchronous abort; it'd be + * OK to hold off until after some IRQ, though. + */ + if (!qh->is_ready || urb->urb_list.prev != &qh->hep->urb_list) + ret = -EINPROGRESS; + else { + switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + sched = &musb->control; + break; + case USB_ENDPOINT_XFER_BULK: + if (usb_pipein(urb->pipe)) + sched = &musb->in_bulk; + else + sched = &musb->out_bulk; + break; + default: + /* REVISIT when we get a schedule tree, periodic + * transfers won't always be at the head of a + * singleton queue... + */ + sched = NULL; + break; + } + } + + /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ + if (ret < 0 || (sched && qh != first_qh(sched))) { + int ready = qh->is_ready; + + ret = 0; + qh->is_ready = 0; + __musb_giveback(musb, urb, 0); + qh->is_ready = ready; + } else + ret = musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN); +done: + spin_unlock_irqrestore(&musb->lock, flags); + return ret; +} + +/* disable an endpoint */ +static void +musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +{ + u8 epnum = hep->desc.bEndpointAddress; + unsigned long flags; + struct musb *musb = hcd_to_musb(hcd); + u8 is_in = epnum & USB_DIR_IN; + struct musb_qh *qh = hep->hcpriv; + struct urb *urb, *tmp; + struct list_head *sched; + + if (!qh) + return; + + spin_lock_irqsave(&musb->lock, flags); + + switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + sched = &musb->control; + break; + case USB_ENDPOINT_XFER_BULK: + if (is_in) + sched = &musb->in_bulk; + else + sched = &musb->out_bulk; + break; + default: + /* REVISIT when we get a schedule tree, periodic transfers + * won't always be at the head of a singleton queue... + */ + sched = NULL; + break; + } + + /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ + + /* kick first urb off the hardware, if needed */ + qh->is_ready = 0; + if (!sched || qh == first_qh(sched)) { + urb = next_urb(qh); + + /* make software (then hardware) stop ASAP */ + if (!urb->unlinked) + urb->status = -ESHUTDOWN; + + /* cleanup */ + musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN); + } else + urb = NULL; + + /* then just nuke all the others */ + list_for_each_entry_safe_from(urb, tmp, &hep->urb_list, urb_list) + musb_giveback(qh, urb, -ESHUTDOWN); + + spin_unlock_irqrestore(&musb->lock, flags); +} + +static int musb_h_get_frame_number(struct usb_hcd *hcd) +{ + struct musb *musb = hcd_to_musb(hcd); + + return musb_readw(musb->mregs, MUSB_FRAME); +} + +static int musb_h_start(struct usb_hcd *hcd) +{ + struct musb *musb = hcd_to_musb(hcd); + + /* NOTE: musb_start() is called when the hub driver turns + * on port power, or when (OTG) peripheral starts. + */ + hcd->state = HC_STATE_RUNNING; + musb->port1_status = 0; + return 0; +} + +static void musb_h_stop(struct usb_hcd *hcd) +{ + musb_stop(hcd_to_musb(hcd)); + hcd->state = HC_STATE_HALT; +} + +static int musb_bus_suspend(struct usb_hcd *hcd) +{ + struct musb *musb = hcd_to_musb(hcd); + + if (musb->xceiv.state == OTG_STATE_A_SUSPEND) + return 0; + + if (is_host_active(musb) && musb->is_active) { + WARNING("trying to suspend as %s is_active=%i\n", + otg_state_string(musb), musb->is_active); + return -EBUSY; + } else + return 0; +} + +static int musb_bus_resume(struct usb_hcd *hcd) +{ + /* resuming child port does the work */ + return 0; +} + +const struct hc_driver musb_hc_driver = { + .description = "musb-hcd", + .product_desc = "MUSB HDRC host driver", + .hcd_priv_size = sizeof(struct musb), + .flags = HCD_USB2 | HCD_MEMORY, + + /* not using irq handler or reset hooks from usbcore, since + * those must be shared with peripheral code for OTG configs + */ + + .start = musb_h_start, + .stop = musb_h_stop, + + .get_frame_number = musb_h_get_frame_number, + + .urb_enqueue = musb_urb_enqueue, + .urb_dequeue = musb_urb_dequeue, + .endpoint_disable = musb_h_disable, + + .hub_status_data = musb_hub_status_data, + .hub_control = musb_hub_control, + .bus_suspend = musb_bus_suspend, + .bus_resume = musb_bus_resume, + /* .start_port_reset = NULL, */ + /* .hub_irq_enable = NULL, */ +}; diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h new file mode 100644 index 000000000000..77bcdb9d5b32 --- /dev/null +++ b/drivers/usb/musb/musb_host.h @@ -0,0 +1,110 @@ +/* + * MUSB OTG driver host defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _MUSB_HOST_H +#define _MUSB_HOST_H + +static inline struct usb_hcd *musb_to_hcd(struct musb *musb) +{ + return container_of((void *) musb, struct usb_hcd, hcd_priv); +} + +static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) +{ + return (struct musb *) (hcd->hcd_priv); +} + +/* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */ +struct musb_qh { + struct usb_host_endpoint *hep; /* usbcore info */ + struct usb_device *dev; + struct musb_hw_ep *hw_ep; /* current binding */ + + struct list_head ring; /* of musb_qh */ + /* struct musb_qh *next; */ /* for periodic tree */ + + unsigned offset; /* in urb->transfer_buffer */ + unsigned segsize; /* current xfer fragment */ + + u8 type_reg; /* {rx,tx} type register */ + u8 intv_reg; /* {rx,tx} interval register */ + u8 addr_reg; /* device address register */ + u8 h_addr_reg; /* hub address register */ + u8 h_port_reg; /* hub port register */ + + u8 is_ready; /* safe to modify hw_ep */ + u8 type; /* XFERTYPE_* */ + u8 epnum; + u16 maxpacket; + u16 frame; /* for periodic schedule */ + unsigned iso_idx; /* in urb->iso_frame_desc[] */ +}; + +/* map from control or bulk queue head to the first qh on that ring */ +static inline struct musb_qh *first_qh(struct list_head *q) +{ + if (list_empty(q)) + return NULL; + return list_entry(q->next, struct musb_qh, ring); +} + + +extern void musb_root_disconnect(struct musb *musb); + +struct usb_hcd; + +extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf); +extern int musb_hub_control(struct usb_hcd *hcd, + u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength); + +extern const struct hc_driver musb_hc_driver; + +static inline struct urb *next_urb(struct musb_qh *qh) +{ +#ifdef CONFIG_USB_MUSB_HDRC_HCD + struct list_head *queue; + + if (!qh) + return NULL; + queue = &qh->hep->urb_list; + if (list_empty(queue)) + return NULL; + return list_entry(queue->next, struct urb, urb_list); +#else + return NULL; +#endif +} + +#endif /* _MUSB_HOST_H */ diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h new file mode 100644 index 000000000000..6bbedae83af8 --- /dev/null +++ b/drivers/usb/musb/musb_io.h @@ -0,0 +1,115 @@ +/* + * MUSB OTG driver register I/O + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MUSB_LINUX_PLATFORM_ARCH_H__ +#define __MUSB_LINUX_PLATFORM_ARCH_H__ + +#include + +#ifndef CONFIG_ARM +static inline void readsl(const void __iomem *addr, void *buf, int len) + { insl((unsigned long)addr, buf, len); } +static inline void readsw(const void __iomem *addr, void *buf, int len) + { insw((unsigned long)addr, buf, len); } +static inline void readsb(const void __iomem *addr, void *buf, int len) + { insb((unsigned long)addr, buf, len); } + +static inline void writesl(const void __iomem *addr, const void *buf, int len) + { outsl((unsigned long)addr, buf, len); } +static inline void writesw(const void __iomem *addr, const void *buf, int len) + { outsw((unsigned long)addr, buf, len); } +static inline void writesb(const void __iomem *addr, const void *buf, int len) + { outsb((unsigned long)addr, buf, len); } + +#endif + +/* NOTE: these offsets are all in bytes */ + +static inline u16 musb_readw(const void __iomem *addr, unsigned offset) + { return __raw_readw(addr + offset); } + +static inline u32 musb_readl(const void __iomem *addr, unsigned offset) + { return __raw_readl(addr + offset); } + + +static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) + { __raw_writew(data, addr + offset); } + +static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) + { __raw_writel(data, addr + offset); } + + +#ifdef CONFIG_USB_TUSB6010 + +/* + * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum. + */ +static inline u8 musb_readb(const void __iomem *addr, unsigned offset) +{ + u16 tmp; + u8 val; + + tmp = __raw_readw(addr + (offset & ~1)); + if (offset & 1) + val = (tmp >> 8); + else + val = tmp & 0xff; + + return val; +} + +static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) +{ + u16 tmp; + + tmp = __raw_readw(addr + (offset & ~1)); + if (offset & 1) + tmp = (data << 8) | (tmp & 0xff); + else + tmp = (tmp & 0xff00) | data; + + __raw_writew(tmp, addr + (offset & ~1)); +} + +#else + +static inline u8 musb_readb(const void __iomem *addr, unsigned offset) + { return __raw_readb(addr + offset); } + +static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) + { __raw_writeb(data, addr + offset); } + +#endif /* CONFIG_USB_TUSB6010 */ + +#endif diff --git a/drivers/usb/musb/musb_procfs.c b/drivers/usb/musb/musb_procfs.c new file mode 100644 index 000000000000..55e6b78bdccc --- /dev/null +++ b/drivers/usb/musb/musb_procfs.c @@ -0,0 +1,830 @@ +/* + * MUSB OTG driver debug support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include /* FIXME remove procfs writes */ +#include + +#include "musb_core.h" + +#include "davinci.h" + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + +static int dump_qh(struct musb_qh *qh, char *buf, unsigned max) +{ + int count; + int tmp; + struct usb_host_endpoint *hep = qh->hep; + struct urb *urb; + + count = snprintf(buf, max, " qh %p dev%d ep%d%s max%d\n", + qh, qh->dev->devnum, qh->epnum, + ({ char *s; switch (qh->type) { + case USB_ENDPOINT_XFER_BULK: + s = "-bulk"; break; + case USB_ENDPOINT_XFER_INT: + s = "-int"; break; + case USB_ENDPOINT_XFER_CONTROL: + s = ""; break; + default: + s = "iso"; break; + }; s; }), + qh->maxpacket); + if (count <= 0) + return 0; + buf += count; + max -= count; + + list_for_each_entry(urb, &hep->urb_list, urb_list) { + tmp = snprintf(buf, max, "\t%s urb %p %d/%d\n", + usb_pipein(urb->pipe) ? "in" : "out", + urb, urb->actual_length, + urb->transfer_buffer_length); + if (tmp <= 0) + break; + tmp = min(tmp, (int)max); + count += tmp; + buf += tmp; + max -= tmp; + } + return count; +} + +static int +dump_queue(struct list_head *q, char *buf, unsigned max) +{ + int count = 0; + struct musb_qh *qh; + + list_for_each_entry(qh, q, ring) { + int tmp; + + tmp = dump_qh(qh, buf, max); + if (tmp <= 0) + break; + tmp = min(tmp, (int)max); + count += tmp; + buf += tmp; + max -= tmp; + } + return count; +} + +#endif /* HCD */ + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC +static int dump_ep(struct musb_ep *ep, char *buffer, unsigned max) +{ + char *buf = buffer; + int code = 0; + void __iomem *regs = ep->hw_ep->regs; + char *mode = "1buf"; + + if (ep->is_in) { + if (ep->hw_ep->tx_double_buffered) + mode = "2buf"; + } else { + if (ep->hw_ep->rx_double_buffered) + mode = "2buf"; + } + + do { + struct usb_request *req; + + code = snprintf(buf, max, + "\n%s (hw%d): %s%s, csr %04x maxp %04x\n", + ep->name, ep->current_epnum, + mode, ep->dma ? " dma" : "", + musb_readw(regs, + (ep->is_in || !ep->current_epnum) + ? MUSB_TXCSR + : MUSB_RXCSR), + musb_readw(regs, ep->is_in + ? MUSB_TXMAXP + : MUSB_RXMAXP) + ); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + + if (is_cppi_enabled() && ep->current_epnum) { + unsigned cppi = ep->current_epnum - 1; + void __iomem *base = ep->musb->ctrl_base; + unsigned off1 = cppi << 2; + void __iomem *ram = base; + char tmp[16]; + + if (ep->is_in) { + ram += DAVINCI_TXCPPI_STATERAM_OFFSET(cppi); + tmp[0] = 0; + } else { + ram += DAVINCI_RXCPPI_STATERAM_OFFSET(cppi); + snprintf(tmp, sizeof tmp, "%d left, ", + musb_readl(base, + DAVINCI_RXCPPI_BUFCNT0_REG + off1)); + } + + code = snprintf(buf, max, "%cX DMA%d: %s" + "%08x %08x, %08x %08x; " + "%08x %08x %08x .. %08x\n", + ep->is_in ? 'T' : 'R', + ep->current_epnum - 1, tmp, + musb_readl(ram, 0 * 4), + musb_readl(ram, 1 * 4), + musb_readl(ram, 2 * 4), + musb_readl(ram, 3 * 4), + musb_readl(ram, 4 * 4), + musb_readl(ram, 5 * 4), + musb_readl(ram, 6 * 4), + musb_readl(ram, 7 * 4)); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + + if (list_empty(&ep->req_list)) { + code = snprintf(buf, max, "\t(queue empty)\n"); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + break; + } + list_for_each_entry(req, &ep->req_list, list) { + code = snprintf(buf, max, "\treq %p, %s%s%d/%d\n", + req, + req->zero ? "zero, " : "", + req->short_not_ok ? "!short, " : "", + req->actual, req->length); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } while (0); + return buf - buffer; +} +#endif + +static int +dump_end_info(struct musb *musb, u8 epnum, char *aBuffer, unsigned max) +{ + int code = 0; + char *buf = aBuffer; + struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + + do { + musb_ep_select(musb->mregs, epnum); +#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (is_host_active(musb)) { + int dump_rx, dump_tx; + void __iomem *regs = hw_ep->regs; + + /* TEMPORARY (!) until we have a real periodic + * schedule tree ... + */ + if (!epnum) { + /* control is shared, uses RX queue + * but (mostly) shadowed tx registers + */ + dump_tx = !list_empty(&musb->control); + dump_rx = 0; + } else if (hw_ep == musb->bulk_ep) { + dump_tx = !list_empty(&musb->out_bulk); + dump_rx = !list_empty(&musb->in_bulk); + } else if (musb->periodic[epnum]) { + struct usb_host_endpoint *hep; + + hep = musb->periodic[epnum]->hep; + dump_rx = hep->desc.bEndpointAddress + & USB_ENDPOINT_DIR_MASK; + dump_tx = !dump_rx; + } else + break; + /* END TEMPORARY */ + + + if (dump_rx) { + code = snprintf(buf, max, + "\nRX%d: %s rxcsr %04x interval %02x " + "max %04x type %02x; " + "dev %d hub %d port %d" + "\n", + epnum, + hw_ep->rx_double_buffered + ? "2buf" : "1buf", + musb_readw(regs, MUSB_RXCSR), + musb_readb(regs, MUSB_RXINTERVAL), + musb_readw(regs, MUSB_RXMAXP), + musb_readb(regs, MUSB_RXTYPE), + /* FIXME: assumes multipoint */ + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_RXFUNCADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_RXHUBADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_RXHUBPORT)) + ); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + + if (is_cppi_enabled() + && epnum + && hw_ep->rx_channel) { + unsigned cppi = epnum - 1; + unsigned off1 = cppi << 2; + void __iomem *base; + void __iomem *ram; + char tmp[16]; + + base = musb->ctrl_base; + ram = DAVINCI_RXCPPI_STATERAM_OFFSET( + cppi) + base; + snprintf(tmp, sizeof tmp, "%d left, ", + musb_readl(base, + DAVINCI_RXCPPI_BUFCNT0_REG + + off1)); + + code = snprintf(buf, max, + " rx dma%d: %s" + "%08x %08x, %08x %08x; " + "%08x %08x %08x .. %08x\n", + cppi, tmp, + musb_readl(ram, 0 * 4), + musb_readl(ram, 1 * 4), + musb_readl(ram, 2 * 4), + musb_readl(ram, 3 * 4), + musb_readl(ram, 4 * 4), + musb_readl(ram, 5 * 4), + musb_readl(ram, 6 * 4), + musb_readl(ram, 7 * 4)); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + + if (hw_ep == musb->bulk_ep + && !list_empty( + &musb->in_bulk)) { + code = dump_queue(&musb->in_bulk, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } else if (musb->periodic[epnum]) { + code = dump_qh(musb->periodic[epnum], + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } + + if (dump_tx) { + code = snprintf(buf, max, + "\nTX%d: %s txcsr %04x interval %02x " + "max %04x type %02x; " + "dev %d hub %d port %d" + "\n", + epnum, + hw_ep->tx_double_buffered + ? "2buf" : "1buf", + musb_readw(regs, MUSB_TXCSR), + musb_readb(regs, MUSB_TXINTERVAL), + musb_readw(regs, MUSB_TXMAXP), + musb_readb(regs, MUSB_TXTYPE), + /* FIXME: assumes multipoint */ + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_TXFUNCADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_TXHUBADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_TXHUBPORT)) + ); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + + if (is_cppi_enabled() + && epnum + && hw_ep->tx_channel) { + unsigned cppi = epnum - 1; + void __iomem *base; + void __iomem *ram; + + base = musb->ctrl_base; + ram = DAVINCI_RXCPPI_STATERAM_OFFSET( + cppi) + base; + code = snprintf(buf, max, + " tx dma%d: " + "%08x %08x, %08x %08x; " + "%08x %08x %08x .. %08x\n", + cppi, + musb_readl(ram, 0 * 4), + musb_readl(ram, 1 * 4), + musb_readl(ram, 2 * 4), + musb_readl(ram, 3 * 4), + musb_readl(ram, 4 * 4), + musb_readl(ram, 5 * 4), + musb_readl(ram, 6 * 4), + musb_readl(ram, 7 * 4)); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + + if (hw_ep == musb->control_ep + && !list_empty( + &musb->control)) { + code = dump_queue(&musb->control, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } else if (hw_ep == musb->bulk_ep + && !list_empty( + &musb->out_bulk)) { + code = dump_queue(&musb->out_bulk, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } else if (musb->periodic[epnum]) { + code = dump_qh(musb->periodic[epnum], + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } + } +#endif +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + if (is_peripheral_active(musb)) { + code = 0; + + if (hw_ep->ep_in.desc || !epnum) { + code = dump_ep(&hw_ep->ep_in, buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + if (hw_ep->ep_out.desc) { + code = dump_ep(&hw_ep->ep_out, buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } +#endif + } while (0); + + return buf - aBuffer; +} + +/* Dump the current status and compile options. + * @param musb the device driver instance + * @param buffer where to dump the status; it must be big enough to hold the + * result otherwise "BAD THINGS HAPPENS(TM)". + */ +static int dump_header_stats(struct musb *musb, char *buffer) +{ + int code, count = 0; + const void __iomem *mbase = musb->mregs; + + *buffer = 0; + count = sprintf(buffer, "Status: %sHDRC, Mode=%s " + "(Power=%02x, DevCtl=%02x)\n", + (musb->is_multipoint ? "M" : ""), MUSB_MODE(musb), + musb_readb(mbase, MUSB_POWER), + musb_readb(mbase, MUSB_DEVCTL)); + if (count <= 0) + return 0; + buffer += count; + + code = sprintf(buffer, "OTG state: %s; %sactive\n", + otg_state_string(musb), + musb->is_active ? "" : "in"); + if (code <= 0) + goto done; + buffer += code; + count += code; + + code = sprintf(buffer, + "Options: " +#ifdef CONFIG_MUSB_PIO_ONLY + "pio" +#elif defined(CONFIG_USB_TI_CPPI_DMA) + "cppi-dma" +#elif defined(CONFIG_USB_INVENTRA_DMA) + "musb-dma" +#elif defined(CONFIG_USB_TUSB_OMAP_DMA) + "tusb-omap-dma" +#else + "?dma?" +#endif + ", " +#ifdef CONFIG_USB_MUSB_OTG + "otg (peripheral+host)" +#elif defined(CONFIG_USB_GADGET_MUSB_HDRC) + "peripheral" +#elif defined(CONFIG_USB_MUSB_HDRC_HCD) + "host" +#endif + ", debug=%d [eps=%d]\n", + debug, + musb->nr_endpoints); + if (code <= 0) + goto done; + count += code; + buffer += code; + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + code = sprintf(buffer, "Peripheral address: %02x\n", + musb_readb(musb->ctrl_base, MUSB_FADDR)); + if (code <= 0) + goto done; + buffer += code; + count += code; +#endif + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + code = sprintf(buffer, "Root port status: %08x\n", + musb->port1_status); + if (code <= 0) + goto done; + buffer += code; + count += code; +#endif + +#ifdef CONFIG_ARCH_DAVINCI + code = sprintf(buffer, + "DaVinci: ctrl=%02x stat=%1x phy=%03x\n" + "\trndis=%05x auto=%04x intsrc=%08x intmsk=%08x" + "\n", + musb_readl(musb->ctrl_base, DAVINCI_USB_CTRL_REG), + musb_readl(musb->ctrl_base, DAVINCI_USB_STAT_REG), + __raw_readl((void __force __iomem *) + IO_ADDRESS(USBPHY_CTL_PADDR)), + musb_readl(musb->ctrl_base, DAVINCI_RNDIS_REG), + musb_readl(musb->ctrl_base, DAVINCI_AUTOREQ_REG), + musb_readl(musb->ctrl_base, + DAVINCI_USB_INT_SOURCE_REG), + musb_readl(musb->ctrl_base, + DAVINCI_USB_INT_MASK_REG)); + if (code <= 0) + goto done; + count += code; + buffer += code; +#endif /* DAVINCI */ + +#ifdef CONFIG_USB_TUSB6010 + code = sprintf(buffer, + "TUSB6010: devconf %08x, phy enable %08x drive %08x" + "\n\totg %03x timer %08x" + "\n\tprcm conf %08x mgmt %08x; int src %08x mask %08x" + "\n", + musb_readl(musb->ctrl_base, TUSB_DEV_CONF), + musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL_ENABLE), + musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL), + musb_readl(musb->ctrl_base, TUSB_DEV_OTG_STAT), + musb_readl(musb->ctrl_base, TUSB_DEV_OTG_TIMER), + musb_readl(musb->ctrl_base, TUSB_PRCM_CONF), + musb_readl(musb->ctrl_base, TUSB_PRCM_MNGMT), + musb_readl(musb->ctrl_base, TUSB_INT_SRC), + musb_readl(musb->ctrl_base, TUSB_INT_MASK)); + if (code <= 0) + goto done; + count += code; + buffer += code; +#endif /* DAVINCI */ + + if (is_cppi_enabled() && musb->dma_controller) { + code = sprintf(buffer, + "CPPI: txcr=%d txsrc=%01x txena=%01x; " + "rxcr=%d rxsrc=%01x rxena=%01x " + "\n", + musb_readl(musb->ctrl_base, + DAVINCI_TXCPPI_CTRL_REG), + musb_readl(musb->ctrl_base, + DAVINCI_TXCPPI_RAW_REG), + musb_readl(musb->ctrl_base, + DAVINCI_TXCPPI_INTENAB_REG), + musb_readl(musb->ctrl_base, + DAVINCI_RXCPPI_CTRL_REG), + musb_readl(musb->ctrl_base, + DAVINCI_RXCPPI_RAW_REG), + musb_readl(musb->ctrl_base, + DAVINCI_RXCPPI_INTENAB_REG)); + if (code <= 0) + goto done; + count += code; + buffer += code; + } + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + if (is_peripheral_enabled(musb)) { + code = sprintf(buffer, "Gadget driver: %s\n", + musb->gadget_driver + ? musb->gadget_driver->driver.name + : "(none)"); + if (code <= 0) + goto done; + count += code; + buffer += code; + } +#endif + +done: + return count; +} + +/* Write to ProcFS + * + * C soft-connect + * c soft-disconnect + * I enable HS + * i disable HS + * s stop session + * F force session (OTG-unfriendly) + * E rElinquish bus (OTG) + * H request host mode + * h cancel host request + * T start sending TEST_PACKET + * D set/query the debug level + */ +static int musb_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char cmd; + u8 reg; + struct musb *musb = (struct musb *)data; + void __iomem *mbase = musb->mregs; + + /* MOD_INC_USE_COUNT; */ + + if (unlikely(copy_from_user(&cmd, buffer, 1))) + return -EFAULT; + + switch (cmd) { + case 'C': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + | MUSB_POWER_SOFTCONN; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'c': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + & ~MUSB_POWER_SOFTCONN; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'I': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + | MUSB_POWER_HSENAB; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'i': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + & ~MUSB_POWER_HSENAB; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'F': + reg = musb_readb(mbase, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_SESSION; + musb_writeb(mbase, MUSB_DEVCTL, reg); + break; + + case 'H': + if (mbase) { + reg = musb_readb(mbase, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_HR; + musb_writeb(mbase, MUSB_DEVCTL, reg); + /* MUSB_HST_MODE( ((struct musb*)data) ); */ + /* WARNING("Host Mode\n"); */ + } + break; + + case 'h': + if (mbase) { + reg = musb_readb(mbase, MUSB_DEVCTL); + reg &= ~MUSB_DEVCTL_HR; + musb_writeb(mbase, MUSB_DEVCTL, reg); + } + break; + + case 'T': + if (mbase) { + musb_load_testpacket(musb); + musb_writeb(mbase, MUSB_TESTMODE, + MUSB_TEST_PACKET); + } + break; + +#if (MUSB_DEBUG > 0) + /* set/read debug level */ + case 'D':{ + if (count > 1) { + char digits[8], *p = digits; + int i = 0, level = 0, sign = 1; + int len = min(count - 1, (unsigned long)8); + + if (copy_from_user(&digits, &buffer[1], len)) + return -EFAULT; + + /* optional sign */ + if (*p == '-') { + len -= 1; + sign = -sign; + p++; + } + + /* read it */ + while (i++ < len && *p > '0' && *p < '9') { + level = level * 10 + (*p - '0'); + p++; + } + + level *= sign; + DBG(1, "debug level %d\n", level); + debug = level; + } + } + break; + + + case '?': + INFO("?: you are seeing it\n"); + INFO("C/c: soft connect enable/disable\n"); + INFO("I/i: hispeed enable/disable\n"); + INFO("F: force session start\n"); + INFO("H: host mode\n"); + INFO("T: start sending TEST_PACKET\n"); + INFO("D: set/read dbug level\n"); + break; +#endif + + default: + ERR("Command %c not implemented\n", cmd); + break; + } + + musb_platform_try_idle(musb, 0); + + return count; +} + +static int musb_proc_read(char *page, char **start, + off_t off, int count, int *eof, void *data) +{ + char *buffer = page; + int code = 0; + unsigned long flags; + struct musb *musb = data; + unsigned epnum; + + count -= off; + count -= 1; /* for NUL at end */ + if (count <= 0) + return -EINVAL; + + spin_lock_irqsave(&musb->lock, flags); + + code = dump_header_stats(musb, buffer); + if (code > 0) { + buffer += code; + count -= code; + } + + /* generate the report for the end points */ + /* REVISIT ... not unless something's connected! */ + for (epnum = 0; count >= 0 && epnum < musb->nr_endpoints; + epnum++) { + code = dump_end_info(musb, epnum, buffer, count); + if (code > 0) { + buffer += code; + count -= code; + } + } + + musb_platform_try_idle(musb, 0); + + spin_unlock_irqrestore(&musb->lock, flags); + *eof = 1; + + return buffer - page; +} + +void __devexit musb_debug_delete(char *name, struct musb *musb) +{ + if (musb->proc_entry) + remove_proc_entry(name, NULL); +} + +struct proc_dir_entry *__init +musb_debug_create(char *name, struct musb *data) +{ + struct proc_dir_entry *pde; + + /* FIXME convert everything to seq_file; then later, debugfs */ + + if (!name) + return NULL; + + pde = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, NULL); + data->proc_entry = pde; + if (pde) { + pde->data = data; + /* pde->owner = THIS_MODULE; */ + + pde->read_proc = musb_proc_read; + pde->write_proc = musb_proc_write; + + pde->size = 0; + + pr_debug("Registered /proc/%s\n", name); + } else { + pr_debug("Cannot create a valid proc file entry"); + } + + return pde; +} diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h new file mode 100644 index 000000000000..9c228661aa5a --- /dev/null +++ b/drivers/usb/musb/musb_regs.h @@ -0,0 +1,300 @@ +/* + * MUSB OTG driver register defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MUSB_REGS_H__ +#define __MUSB_REGS_H__ + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* + * Common USB registers + */ + +#define MUSB_FADDR 0x00 /* 8-bit */ +#define MUSB_POWER 0x01 /* 8-bit */ + +#define MUSB_INTRTX 0x02 /* 16-bit */ +#define MUSB_INTRRX 0x04 +#define MUSB_INTRTXE 0x06 +#define MUSB_INTRRXE 0x08 +#define MUSB_INTRUSB 0x0A /* 8 bit */ +#define MUSB_INTRUSBE 0x0B /* 8 bit */ +#define MUSB_FRAME 0x0C +#define MUSB_INDEX 0x0E /* 8 bit */ +#define MUSB_TESTMODE 0x0F /* 8 bit */ + +/* Get offset for a given FIFO from musb->mregs */ +#ifdef CONFIG_USB_TUSB6010 +#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20)) +#else +#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4)) +#endif + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL 0x60 /* 8 bit */ + +/* These are always controlled through the INDEX register */ +#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ +#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */ +#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */ +#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */ + +/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */ +#define MUSB_HWVERS 0x6C /* 8 bit */ + +#define MUSB_EPINFO 0x78 /* 8 bit */ +#define MUSB_RAMINFO 0x79 /* 8 bit */ +#define MUSB_LINKINFO 0x7a /* 8 bit */ +#define MUSB_VPLEN 0x7b /* 8 bit */ +#define MUSB_HS_EOF1 0x7c /* 8 bit */ +#define MUSB_FS_EOF1 0x7d /* 8 bit */ +#define MUSB_LS_EOF1 0x7e /* 8 bit */ + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x00 +#define MUSB_TXCSR 0x02 +#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ +#define MUSB_RXMAXP 0x04 +#define MUSB_RXCSR 0x06 +#define MUSB_RXCOUNT 0x08 +#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ +#define MUSB_TXTYPE 0x0A +#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ +#define MUSB_TXINTERVAL 0x0B +#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ +#define MUSB_RXTYPE 0x0C +#define MUSB_RXINTERVAL 0x0D +#define MUSB_FIFOSIZE 0x0F +#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */ + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ + (0x10 + (_offset)) + +/* Offsets to endpoint registers in flat models */ +#define MUSB_FLAT_OFFSET(_epnum, _offset) \ + (0x100 + (0x10*(_epnum)) + (_offset)) + +#ifdef CONFIG_USB_TUSB6010 +/* TUSB6010 EP0 configuration register is special */ +#define MUSB_TUSB_OFFSET(_epnum, _offset) \ + (0x10 + _offset) +#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */ +#endif + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MUSB_TXFUNCADDR 0x00 +#define MUSB_TXHUBADDR 0x02 +#define MUSB_TXHUBPORT 0x03 + +#define MUSB_RXFUNCADDR 0x04 +#define MUSB_RXHUBADDR 0x06 +#define MUSB_RXHUBPORT 0x07 + +#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ + (0x80 + (8*(_epnum)) + (_offset)) + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_MODE 0x2000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + +#endif /* __MUSB_REGS_H__ */ diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c new file mode 100644 index 000000000000..e0e9ce584175 --- /dev/null +++ b/drivers/usb/musb/musb_virthub.c @@ -0,0 +1,425 @@ +/* + * MUSB OTG driver virtual root hub support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "musb_core.h" + + +static void musb_port_suspend(struct musb *musb, bool do_suspend) +{ + u8 power; + void __iomem *mbase = musb->mregs; + + if (!is_host_active(musb)) + return; + + /* NOTE: this doesn't necessarily put PHY into low power mode, + * turning off its clock; that's a function of PHY integration and + * MUSB_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect + * SE0 changing to connect (J) or wakeup (K) states. + */ + power = musb_readb(mbase, MUSB_POWER); + if (do_suspend) { + int retries = 10000; + + power &= ~MUSB_POWER_RESUME; + power |= MUSB_POWER_SUSPENDM; + musb_writeb(mbase, MUSB_POWER, power); + + /* Needed for OPT A tests */ + power = musb_readb(mbase, MUSB_POWER); + while (power & MUSB_POWER_SUSPENDM) { + power = musb_readb(mbase, MUSB_POWER); + if (retries-- < 1) + break; + } + + DBG(3, "Root port suspended, power %02x\n", power); + + musb->port1_status |= USB_PORT_STAT_SUSPEND; + switch (musb->xceiv.state) { + case OTG_STATE_A_HOST: + musb->xceiv.state = OTG_STATE_A_SUSPEND; + musb->is_active = is_otg_enabled(musb) + && musb->xceiv.host->b_hnp_enable; + musb_platform_try_idle(musb, 0); + break; +#ifdef CONFIG_USB_MUSB_OTG + case OTG_STATE_B_HOST: + musb->xceiv.state = OTG_STATE_B_WAIT_ACON; + musb->is_active = is_otg_enabled(musb) + && musb->xceiv.host->b_hnp_enable; + musb_platform_try_idle(musb, 0); + break; +#endif + default: + DBG(1, "bogus rh suspend? %s\n", + otg_state_string(musb)); + } + } else if (power & MUSB_POWER_SUSPENDM) { + power &= ~MUSB_POWER_SUSPENDM; + power |= MUSB_POWER_RESUME; + musb_writeb(mbase, MUSB_POWER, power); + + DBG(3, "Root port resuming, power %02x\n", power); + + /* later, GetPortStatus will stop RESUME signaling */ + musb->port1_status |= MUSB_PORT_STAT_RESUME; + musb->rh_timer = jiffies + msecs_to_jiffies(20); + } +} + +static void musb_port_reset(struct musb *musb, bool do_reset) +{ + u8 power; + void __iomem *mbase = musb->mregs; + +#ifdef CONFIG_USB_MUSB_OTG + if (musb->xceiv.state == OTG_STATE_B_IDLE) { + DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n"); + musb->port1_status &= ~USB_PORT_STAT_RESET; + return; + } +#endif + + if (!is_host_active(musb)) + return; + + /* NOTE: caller guarantees it will turn off the reset when + * the appropriate amount of time has passed + */ + power = musb_readb(mbase, MUSB_POWER); + if (do_reset) { + + /* + * If RESUME is set, we must make sure it stays minimum 20 ms. + * Then we must clear RESUME and wait a bit to let musb start + * generating SOFs. If we don't do this, OPT HS A 6.8 tests + * fail with "Error! Did not receive an SOF before suspend + * detected". + */ + if (power & MUSB_POWER_RESUME) { + while (time_before(jiffies, musb->rh_timer)) + msleep(1); + musb_writeb(mbase, MUSB_POWER, + power & ~MUSB_POWER_RESUME); + msleep(1); + } + + musb->ignore_disconnect = true; + power &= 0xf0; + musb_writeb(mbase, MUSB_POWER, + power | MUSB_POWER_RESET); + + musb->port1_status |= USB_PORT_STAT_RESET; + musb->port1_status &= ~USB_PORT_STAT_ENABLE; + musb->rh_timer = jiffies + msecs_to_jiffies(50); + } else { + DBG(4, "root port reset stopped\n"); + musb_writeb(mbase, MUSB_POWER, + power & ~MUSB_POWER_RESET); + + musb->ignore_disconnect = false; + + power = musb_readb(mbase, MUSB_POWER); + if (power & MUSB_POWER_HSMODE) { + DBG(4, "high-speed device connected\n"); + musb->port1_status |= USB_PORT_STAT_HIGH_SPEED; + } + + musb->port1_status &= ~USB_PORT_STAT_RESET; + musb->port1_status |= USB_PORT_STAT_ENABLE + | (USB_PORT_STAT_C_RESET << 16) + | (USB_PORT_STAT_C_ENABLE << 16); + usb_hcd_poll_rh_status(musb_to_hcd(musb)); + + musb->vbuserr_retry = VBUSERR_RETRY_COUNT; + } +} + +void musb_root_disconnect(struct musb *musb) +{ + musb->port1_status = (1 << USB_PORT_FEAT_POWER) + | (1 << USB_PORT_FEAT_C_CONNECTION); + + usb_hcd_poll_rh_status(musb_to_hcd(musb)); + musb->is_active = 0; + + switch (musb->xceiv.state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_SUSPEND: + musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + musb->is_active = 0; + break; + case OTG_STATE_A_WAIT_VFALL: + musb->xceiv.state = OTG_STATE_B_IDLE; + break; + default: + DBG(1, "host disconnect (%s)\n", otg_state_string(musb)); + } +} + + +/*---------------------------------------------------------------------*/ + +/* Caller may or may not hold musb->lock */ +int musb_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct musb *musb = hcd_to_musb(hcd); + int retval = 0; + + /* called in_irq() via usb_hcd_poll_rh_status() */ + if (musb->port1_status & 0xffff0000) { + *buf = 0x02; + retval = 1; + } + return retval; +} + +int musb_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength) +{ + struct musb *musb = hcd_to_musb(hcd); + u32 temp; + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + spin_unlock_irqrestore(&musb->lock, flags); + return -ESHUTDOWN; + } + + /* hub features: always zero, setting is a NOP + * port features: reported, sometimes updated when host is active + * no indicators + */ + switch (typeReq) { + case ClearHubFeature: + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if ((wIndex & 0xff) != 1) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + break; + case USB_PORT_FEAT_SUSPEND: + musb_port_suspend(musb, false); + break; + case USB_PORT_FEAT_POWER: + if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) + musb_set_vbus(musb, 0); + break; + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + case USB_PORT_FEAT_C_SUSPEND: + break; + default: + goto error; + } + DBG(5, "clear feature %d\n", wValue); + musb->port1_status &= ~(1 << wValue); + break; + case GetHubDescriptor: + { + struct usb_hub_descriptor *desc = (void *)buf; + + desc->bDescLength = 9; + desc->bDescriptorType = 0x29; + desc->bNbrPorts = 1; + desc->wHubCharacteristics = __constant_cpu_to_le16( + 0x0001 /* per-port power switching */ + | 0x0010 /* no overcurrent reporting */ + ); + desc->bPwrOn2PwrGood = 5; /* msec/2 */ + desc->bHubContrCurrent = 0; + + /* workaround bogus struct definition */ + desc->DeviceRemovable[0] = 0x02; /* port 1 */ + desc->DeviceRemovable[1] = 0xff; + } + break; + case GetHubStatus: + temp = 0; + *(__le32 *) buf = cpu_to_le32(temp); + break; + case GetPortStatus: + if (wIndex != 1) + goto error; + + /* finish RESET signaling? */ + if ((musb->port1_status & USB_PORT_STAT_RESET) + && time_after_eq(jiffies, musb->rh_timer)) + musb_port_reset(musb, false); + + /* finish RESUME signaling? */ + if ((musb->port1_status & MUSB_PORT_STAT_RESUME) + && time_after_eq(jiffies, musb->rh_timer)) { + u8 power; + + power = musb_readb(musb->mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + DBG(4, "root port resume stopped, power %02x\n", + power); + musb_writeb(musb->mregs, MUSB_POWER, power); + + /* ISSUE: DaVinci (RTL 1.300) disconnects after + * resume of high speed peripherals (but not full + * speed ones). + */ + + musb->is_active = 1; + musb->port1_status &= ~(USB_PORT_STAT_SUSPEND + | MUSB_PORT_STAT_RESUME); + musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; + usb_hcd_poll_rh_status(musb_to_hcd(musb)); + /* NOTE: it might really be A_WAIT_BCON ... */ + musb->xceiv.state = OTG_STATE_A_HOST; + } + + put_unaligned(cpu_to_le32(musb->port1_status + & ~MUSB_PORT_STAT_RESUME), + (__le32 *) buf); + + /* port change status is more interesting */ + DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n", + musb->port1_status); + break; + case SetPortFeature: + if ((wIndex & 0xff) != 1) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_POWER: + /* NOTE: this controller has a strange state machine + * that involves "requesting sessions" according to + * magic side effects from incompletely-described + * rules about startup... + * + * This call is what really starts the host mode; be + * very careful about side effects if you reorder any + * initialization logic, e.g. for OTG, or change any + * logic relating to VBUS power-up. + */ + if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) + musb_start(musb); + break; + case USB_PORT_FEAT_RESET: + musb_port_reset(musb, true); + break; + case USB_PORT_FEAT_SUSPEND: + musb_port_suspend(musb, true); + break; + case USB_PORT_FEAT_TEST: + if (unlikely(is_host_active(musb))) + goto error; + + wIndex >>= 8; + switch (wIndex) { + case 1: + pr_debug("TEST_J\n"); + temp = MUSB_TEST_J; + break; + case 2: + pr_debug("TEST_K\n"); + temp = MUSB_TEST_K; + break; + case 3: + pr_debug("TEST_SE0_NAK\n"); + temp = MUSB_TEST_SE0_NAK; + break; + case 4: + pr_debug("TEST_PACKET\n"); + temp = MUSB_TEST_PACKET; + musb_load_testpacket(musb); + break; + case 5: + pr_debug("TEST_FORCE_ENABLE\n"); + temp = MUSB_TEST_FORCE_HOST + | MUSB_TEST_FORCE_HS; + + musb_writeb(musb->mregs, MUSB_DEVCTL, + MUSB_DEVCTL_SESSION); + break; + case 6: + pr_debug("TEST_FIFO_ACCESS\n"); + temp = MUSB_TEST_FIFO_ACCESS; + break; + default: + goto error; + } + musb_writeb(musb->mregs, MUSB_TESTMODE, temp); + break; + default: + goto error; + } + DBG(5, "set feature %d\n", wValue); + musb->port1_status |= 1 << wValue; + break; + + default: +error: + /* "protocol stall" on error */ + retval = -EPIPE; + } + spin_unlock_irqrestore(&musb->lock, flags); + return retval; +} diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c new file mode 100644 index 000000000000..9ba8fb7fcd24 --- /dev/null +++ b/drivers/usb/musb/musbhsdma.c @@ -0,0 +1,433 @@ +/* + * MUSB OTG driver - support for Mentor's DMA controller + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2007 by Texas Instruments + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include "musb_core.h" + +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) +#include "omap2430.h" +#endif + +#define MUSB_HSDMA_BASE 0x200 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x4 +#define MUSB_HSDMA_ADDRESS 0x8 +#define MUSB_HSDMA_COUNT 0xc + +#define MUSB_HSDMA_CHANNEL_OFFSET(_bChannel, _offset) \ + (MUSB_HSDMA_BASE + (_bChannel << 4) + _offset) + +/* control register (16-bit): */ +#define MUSB_HSDMA_ENABLE_SHIFT 0 +#define MUSB_HSDMA_TRANSMIT_SHIFT 1 +#define MUSB_HSDMA_MODE1_SHIFT 2 +#define MUSB_HSDMA_IRQENABLE_SHIFT 3 +#define MUSB_HSDMA_ENDPOINT_SHIFT 4 +#define MUSB_HSDMA_BUSERROR_SHIFT 8 +#define MUSB_HSDMA_BURSTMODE_SHIFT 9 +#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT) +#define MUSB_HSDMA_BURSTMODE_UNSPEC 0 +#define MUSB_HSDMA_BURSTMODE_INCR4 1 +#define MUSB_HSDMA_BURSTMODE_INCR8 2 +#define MUSB_HSDMA_BURSTMODE_INCR16 3 + +#define MUSB_HSDMA_CHANNELS 8 + +struct musb_dma_controller; + +struct musb_dma_channel { + struct dma_channel Channel; + struct musb_dma_controller *controller; + u32 dwStartAddress; + u32 len; + u16 wMaxPacketSize; + u8 bIndex; + u8 epnum; + u8 transmit; +}; + +struct musb_dma_controller { + struct dma_controller Controller; + struct musb_dma_channel aChannel[MUSB_HSDMA_CHANNELS]; + void *pDmaPrivate; + void __iomem *pCoreBase; + u8 bChannelCount; + u8 bmUsedChannels; + u8 irq; +}; + +static int dma_controller_start(struct dma_controller *c) +{ + /* nothing to do */ + return 0; +} + +static void dma_channel_release(struct dma_channel *pChannel); + +static int dma_controller_stop(struct dma_controller *c) +{ + struct musb_dma_controller *controller = + container_of(c, struct musb_dma_controller, Controller); + struct musb *musb = (struct musb *) controller->pDmaPrivate; + struct dma_channel *pChannel; + u8 bBit; + + if (controller->bmUsedChannels != 0) { + dev_err(musb->controller, + "Stopping DMA controller while channel active\n"); + + for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) { + if (controller->bmUsedChannels & (1 << bBit)) { + pChannel = &controller->aChannel[bBit].Channel; + dma_channel_release(pChannel); + + if (!controller->bmUsedChannels) + break; + } + } + } + return 0; +} + +static struct dma_channel *dma_channel_allocate(struct dma_controller *c, + struct musb_hw_ep *hw_ep, u8 transmit) +{ + u8 bBit; + struct dma_channel *pChannel = NULL; + struct musb_dma_channel *pImplChannel = NULL; + struct musb_dma_controller *controller = + container_of(c, struct musb_dma_controller, Controller); + + for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) { + if (!(controller->bmUsedChannels & (1 << bBit))) { + controller->bmUsedChannels |= (1 << bBit); + pImplChannel = &(controller->aChannel[bBit]); + pImplChannel->controller = controller; + pImplChannel->bIndex = bBit; + pImplChannel->epnum = hw_ep->epnum; + pImplChannel->transmit = transmit; + pChannel = &(pImplChannel->Channel); + pChannel->private_data = pImplChannel; + pChannel->status = MUSB_DMA_STATUS_FREE; + pChannel->max_len = 0x10000; + /* Tx => mode 1; Rx => mode 0 */ + pChannel->desired_mode = transmit; + pChannel->actual_len = 0; + break; + } + } + return pChannel; +} + +static void dma_channel_release(struct dma_channel *pChannel) +{ + struct musb_dma_channel *pImplChannel = + (struct musb_dma_channel *) pChannel->private_data; + + pChannel->actual_len = 0; + pImplChannel->dwStartAddress = 0; + pImplChannel->len = 0; + + pImplChannel->controller->bmUsedChannels &= + ~(1 << pImplChannel->bIndex); + + pChannel->status = MUSB_DMA_STATUS_UNKNOWN; +} + +static void configure_channel(struct dma_channel *pChannel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct musb_dma_channel *pImplChannel = + (struct musb_dma_channel *) pChannel->private_data; + struct musb_dma_controller *controller = pImplChannel->controller; + void __iomem *mbase = controller->pCoreBase; + u8 bChannel = pImplChannel->bIndex; + u16 csr = 0; + + DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", + pChannel, packet_sz, dma_addr, len, mode); + + if (mode) { + csr |= 1 << MUSB_HSDMA_MODE1_SHIFT; + BUG_ON(len < packet_sz); + + if (packet_sz >= 64) { + csr |= MUSB_HSDMA_BURSTMODE_INCR16 + << MUSB_HSDMA_BURSTMODE_SHIFT; + } else if (packet_sz >= 32) { + csr |= MUSB_HSDMA_BURSTMODE_INCR8 + << MUSB_HSDMA_BURSTMODE_SHIFT; + } else if (packet_sz >= 16) { + csr |= MUSB_HSDMA_BURSTMODE_INCR4 + << MUSB_HSDMA_BURSTMODE_SHIFT; + } + } + + csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT) + | (1 << MUSB_HSDMA_ENABLE_SHIFT) + | (1 << MUSB_HSDMA_IRQENABLE_SHIFT) + | (pImplChannel->transmit + ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT) + : 0); + + /* address/count */ + musb_writel(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS), + dma_addr); + musb_writel(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT), + len); + + /* control (this should start things) */ + musb_writew(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL), + csr); +} + +static int dma_channel_program(struct dma_channel *pChannel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct musb_dma_channel *pImplChannel = + (struct musb_dma_channel *) pChannel->private_data; + + DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", + pImplChannel->epnum, + pImplChannel->transmit ? "Tx" : "Rx", + packet_sz, dma_addr, len, mode); + + BUG_ON(pChannel->status == MUSB_DMA_STATUS_UNKNOWN || + pChannel->status == MUSB_DMA_STATUS_BUSY); + + pChannel->actual_len = 0; + pImplChannel->dwStartAddress = dma_addr; + pImplChannel->len = len; + pImplChannel->wMaxPacketSize = packet_sz; + pChannel->status = MUSB_DMA_STATUS_BUSY; + + if ((mode == 1) && (len >= packet_sz)) + configure_channel(pChannel, packet_sz, 1, dma_addr, len); + else + configure_channel(pChannel, packet_sz, 0, dma_addr, len); + + return true; +} + +static int dma_channel_abort(struct dma_channel *pChannel) +{ + struct musb_dma_channel *pImplChannel = + (struct musb_dma_channel *) pChannel->private_data; + u8 bChannel = pImplChannel->bIndex; + void __iomem *mbase = pImplChannel->controller->pCoreBase; + u16 csr; + + if (pChannel->status == MUSB_DMA_STATUS_BUSY) { + if (pImplChannel->transmit) { + + csr = musb_readw(mbase, + MUSB_EP_OFFSET(pImplChannel->epnum, + MUSB_TXCSR)); + csr &= ~(MUSB_TXCSR_AUTOSET | + MUSB_TXCSR_DMAENAB | + MUSB_TXCSR_DMAMODE); + musb_writew(mbase, + MUSB_EP_OFFSET(pImplChannel->epnum, + MUSB_TXCSR), + csr); + } else { + csr = musb_readw(mbase, + MUSB_EP_OFFSET(pImplChannel->epnum, + MUSB_RXCSR)); + csr &= ~(MUSB_RXCSR_AUTOCLEAR | + MUSB_RXCSR_DMAENAB | + MUSB_RXCSR_DMAMODE); + musb_writew(mbase, + MUSB_EP_OFFSET(pImplChannel->epnum, + MUSB_RXCSR), + csr); + } + + musb_writew(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL), + 0); + musb_writel(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS), + 0); + musb_writel(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT), + 0); + + pChannel->status = MUSB_DMA_STATUS_FREE; + } + return 0; +} + +static irqreturn_t dma_controller_irq(int irq, void *private_data) +{ + struct musb_dma_controller *controller = + (struct musb_dma_controller *)private_data; + struct musb_dma_channel *pImplChannel; + struct musb *musb = controller->pDmaPrivate; + void __iomem *mbase = controller->pCoreBase; + struct dma_channel *pChannel; + u8 bChannel; + u16 csr; + u32 dwAddress; + u8 int_hsdma; + irqreturn_t retval = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + + int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR); + if (!int_hsdma) + goto done; + + for (bChannel = 0; bChannel < MUSB_HSDMA_CHANNELS; bChannel++) { + if (int_hsdma & (1 << bChannel)) { + pImplChannel = (struct musb_dma_channel *) + &(controller->aChannel[bChannel]); + pChannel = &pImplChannel->Channel; + + csr = musb_readw(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bChannel, + MUSB_HSDMA_CONTROL)); + + if (csr & (1 << MUSB_HSDMA_BUSERROR_SHIFT)) + pImplChannel->Channel.status = + MUSB_DMA_STATUS_BUS_ABORT; + else { + u8 devctl; + + dwAddress = musb_readl(mbase, + MUSB_HSDMA_CHANNEL_OFFSET( + bChannel, + MUSB_HSDMA_ADDRESS)); + pChannel->actual_len = dwAddress + - pImplChannel->dwStartAddress; + + DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n", + pChannel, pImplChannel->dwStartAddress, + dwAddress, pChannel->actual_len, + pImplChannel->len, + (pChannel->actual_len + < pImplChannel->len) ? + "=> reconfig 0" : "=> complete"); + + devctl = musb_readb(mbase, MUSB_DEVCTL); + + pChannel->status = MUSB_DMA_STATUS_FREE; + + /* completed */ + if ((devctl & MUSB_DEVCTL_HM) + && (pImplChannel->transmit) + && ((pChannel->desired_mode == 0) + || (pChannel->actual_len & + (pImplChannel->wMaxPacketSize - 1))) + ) { + /* Send out the packet */ + musb_ep_select(mbase, + pImplChannel->epnum); + musb_writew(mbase, MUSB_EP_OFFSET( + pImplChannel->epnum, + MUSB_TXCSR), + MUSB_TXCSR_TXPKTRDY); + } else + musb_dma_completion( + musb, + pImplChannel->epnum, + pImplChannel->transmit); + } + } + } + retval = IRQ_HANDLED; +done: + spin_unlock_irqrestore(&musb->lock, flags); + return retval; +} + +void dma_controller_destroy(struct dma_controller *c) +{ + struct musb_dma_controller *controller; + + controller = container_of(c, struct musb_dma_controller, Controller); + if (!controller) + return; + + if (controller->irq) + free_irq(controller->irq, c); + + kfree(controller); +} + +struct dma_controller *__init +dma_controller_create(struct musb *musb, void __iomem *pCoreBase) +{ + struct musb_dma_controller *controller; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq(pdev, 1); + + if (irq == 0) { + dev_err(dev, "No DMA interrupt line!\n"); + return NULL; + } + + controller = kzalloc(sizeof(struct musb_dma_controller), GFP_KERNEL); + if (!controller) + return NULL; + + controller->bChannelCount = MUSB_HSDMA_CHANNELS; + controller->pDmaPrivate = musb; + controller->pCoreBase = pCoreBase; + + controller->Controller.start = dma_controller_start; + controller->Controller.stop = dma_controller_stop; + controller->Controller.channel_alloc = dma_channel_allocate; + controller->Controller.channel_release = dma_channel_release; + controller->Controller.channel_program = dma_channel_program; + controller->Controller.channel_abort = dma_channel_abort; + + if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, + musb->controller->bus_id, &controller->Controller)) { + dev_err(dev, "request_irq %d failed!\n", irq); + dma_controller_destroy(&controller->Controller); + return NULL; + } + + controller->irq = irq; + + return &controller->Controller; +} diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c new file mode 100644 index 000000000000..298b22e6ad0d --- /dev/null +++ b/drivers/usb/musb/omap2430.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2005-2007 by Texas Instruments + * Some code has been taken from tusb6010.c + * Copyrights for that are attributable to: + * Copyright (C) 2006 Nokia Corporation + * Jarkko Nikula + * Tony Lindgren + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux 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. + * + * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "musb_core.h" +#include "omap2430.h" + +#ifdef CONFIG_ARCH_OMAP3430 +#define get_cpu_rev() 2 +#endif + +#define MUSB_TIMEOUT_A_WAIT_BCON 1100 + +static struct timer_list musb_idle_timer; + +static void musb_do_idle(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + unsigned long flags; + u8 power; + u8 devctl; + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + spin_lock_irqsave(&musb->lock, flags); + + switch (musb->xceiv.state) { + case OTG_STATE_A_WAIT_BCON: + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv.state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } else { + musb->xceiv.state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + break; +#ifdef CONFIG_USB_MUSB_HDRC_HCD + case OTG_STATE_A_SUSPEND: + /* finish RESUME signaling? */ + if (musb->port1_status & MUSB_PORT_STAT_RESUME) { + power = musb_readb(musb->mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + DBG(1, "root port resume stopped, power %02x\n", power); + musb_writeb(musb->mregs, MUSB_POWER, power); + musb->is_active = 1; + musb->port1_status &= ~(USB_PORT_STAT_SUSPEND + | MUSB_PORT_STAT_RESUME); + musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; + usb_hcd_poll_rh_status(musb_to_hcd(musb)); + /* NOTE: it might really be A_WAIT_BCON ... */ + musb->xceiv.state = OTG_STATE_A_HOST; + } + break; +#endif +#ifdef CONFIG_USB_MUSB_HDRC_HCD + case OTG_STATE_A_HOST: + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + musb->xceiv.state = OTG_STATE_B_IDLE; + else + musb->xceiv.state = OTG_STATE_A_WAIT_BCON; +#endif + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + + +void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +{ + unsigned long default_timeout = jiffies + msecs_to_jiffies(3); + static unsigned long last_timer; + + if (timeout == 0) + timeout = default_timeout; + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || ((musb->a_wait_bcon == 0) + && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) { + DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + del_timer(&musb_idle_timer); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout)) { + if (!timer_pending(&musb_idle_timer)) + last_timer = timeout; + else { + DBG(4, "Longer idle timer already pending, ignoring\n"); + return; + } + } + last_timer = timeout; + + DBG(4, "%s inactive, for idle timer for %lu ms\n", + otg_state_string(musb), + (unsigned long)jiffies_to_msecs(timeout - jiffies)); + mod_timer(&musb_idle_timer, timeout); +} + +void musb_platform_enable(struct musb *musb) +{ +} +void musb_platform_disable(struct musb *musb) +{ +} +static void omap_vbus_power(struct musb *musb, int is_on, int sleeping) +{ +} + +static void omap_set_vbus(struct musb *musb, int is_on) +{ + u8 devctl; + /* HDRC controls CPEN, but beware current surges during device + * connect. They can trigger transient overcurrent conditions + * that must be ignored. + */ + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + if (is_on) { + musb->is_active = 1; + musb->xceiv.default_a = 1; + musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + devctl |= MUSB_DEVCTL_SESSION; + + MUSB_HST_MODE(musb); + } else { + musb->is_active = 0; + + /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and + * jumping right to B_IDLE... + */ + + musb->xceiv.default_a = 0; + musb->xceiv.state = OTG_STATE_B_IDLE; + devctl &= ~MUSB_DEVCTL_SESSION; + + MUSB_DEV_MODE(musb); + } + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + DBG(1, "VBUS %s, devctl %02x " + /* otg %3x conf %08x prcm %08x */ "\n", + otg_state_string(musb), + musb_readb(musb->mregs, MUSB_DEVCTL)); +} +static int omap_set_power(struct otg_transceiver *x, unsigned mA) +{ + return 0; +} + +static int musb_platform_resume(struct musb *musb); + +void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +{ + u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + switch (musb_mode) { + case MUSB_HOST: + otg_set_host(&musb->xceiv, musb->xceiv.host); + break; + case MUSB_PERIPHERAL: + otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget); + break; + case MUSB_OTG: + break; + } +} + +int __init musb_platform_init(struct musb *musb) +{ + u32 l; + +#if defined(CONFIG_ARCH_OMAP2430) + omap_cfg_reg(AE5_2430_USB0HS_STP); +#endif + + musb_platform_resume(musb); + + l = omap_readl(OTG_SYSCONFIG); + l &= ~ENABLEWAKEUP; /* disable wakeup */ + l &= ~NOSTDBY; /* remove possible nostdby */ + l |= SMARTSTDBY; /* enable smart standby */ + l &= ~AUTOIDLE; /* disable auto idle */ + l &= ~NOIDLE; /* remove possible noidle */ + l |= SMARTIDLE; /* enable smart idle */ + l |= AUTOIDLE; /* enable auto idle */ + omap_writel(l, OTG_SYSCONFIG); + + l = omap_readl(OTG_INTERFSEL); + l |= ULPI_12PIN; + omap_writel(l, OTG_INTERFSEL); + + pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, " + "sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n", + omap_readl(OTG_REVISION), omap_readl(OTG_SYSCONFIG), + omap_readl(OTG_SYSSTATUS), omap_readl(OTG_INTERFSEL), + omap_readl(OTG_SIMENABLE)); + + omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1); + + if (is_host_enabled(musb)) + musb->board_set_vbus = omap_set_vbus; + if (is_peripheral_enabled(musb)) + musb->xceiv.set_power = omap_set_power; + musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON; + + setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + + return 0; +} + +int musb_platform_suspend(struct musb *musb) +{ + u32 l; + + if (!musb->clock) + return 0; + + /* in any role */ + l = omap_readl(OTG_FORCESTDBY); + l |= ENABLEFORCE; /* enable MSTANDBY */ + omap_writel(l, OTG_FORCESTDBY); + + l = omap_readl(OTG_SYSCONFIG); + l |= ENABLEWAKEUP; /* enable wakeup */ + omap_writel(l, OTG_SYSCONFIG); + + if (musb->xceiv.set_suspend) + musb->xceiv.set_suspend(&musb->xceiv, 1); + + if (musb->set_clock) + musb->set_clock(musb->clock, 0); + else + clk_disable(musb->clock); + + return 0; +} + +static int musb_platform_resume(struct musb *musb) +{ + u32 l; + + if (!musb->clock) + return 0; + + if (musb->xceiv.set_suspend) + musb->xceiv.set_suspend(&musb->xceiv, 0); + + if (musb->set_clock) + musb->set_clock(musb->clock, 1); + else + clk_enable(musb->clock); + + l = omap_readl(OTG_SYSCONFIG); + l &= ~ENABLEWAKEUP; /* disable wakeup */ + omap_writel(l, OTG_SYSCONFIG); + + l = omap_readl(OTG_FORCESTDBY); + l &= ~ENABLEFORCE; /* disable MSTANDBY */ + omap_writel(l, OTG_FORCESTDBY); + + return 0; +} + + +int musb_platform_exit(struct musb *musb) +{ + + omap_vbus_power(musb, 0 /*off*/, 1); + + musb_platform_suspend(musb); + + clk_put(musb->clock); + musb->clock = 0; + + return 0; +} diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h new file mode 100644 index 000000000000..786a62071f72 --- /dev/null +++ b/drivers/usb/musb/omap2430.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * + * The Inventra Controller Driver for Linux 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 __MUSB_OMAP243X_H__ +#define __MUSB_OMAP243X_H__ + +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) +#include +#include + +/* + * OMAP2430-specific definitions + */ + +#define MENTOR_BASE_OFFSET 0 +#if defined(CONFIG_ARCH_OMAP2430) +#define OMAP_HSOTG_BASE (OMAP243X_HS_BASE) +#elif defined(CONFIG_ARCH_OMAP3430) +#define OMAP_HSOTG_BASE (OMAP34XX_HSUSB_OTG_BASE) +#endif +#define OMAP_HSOTG(offset) (OMAP_HSOTG_BASE + 0x400 + (offset)) +#define OTG_REVISION OMAP_HSOTG(0x0) +#define OTG_SYSCONFIG OMAP_HSOTG(0x4) +# define MIDLEMODE 12 /* bit position */ +# define FORCESTDBY (0 << MIDLEMODE) +# define NOSTDBY (1 << MIDLEMODE) +# define SMARTSTDBY (2 << MIDLEMODE) +# define SIDLEMODE 3 /* bit position */ +# define FORCEIDLE (0 << SIDLEMODE) +# define NOIDLE (1 << SIDLEMODE) +# define SMARTIDLE (2 << SIDLEMODE) +# define ENABLEWAKEUP (1 << 2) +# define SOFTRST (1 << 1) +# define AUTOIDLE (1 << 0) +#define OTG_SYSSTATUS OMAP_HSOTG(0x8) +# define RESETDONE (1 << 0) +#define OTG_INTERFSEL OMAP_HSOTG(0xc) +# define EXTCP (1 << 2) +# define PHYSEL 0 /* bit position */ +# define UTMI_8BIT (0 << PHYSEL) +# define ULPI_12PIN (1 << PHYSEL) +# define ULPI_8PIN (2 << PHYSEL) +#define OTG_SIMENABLE OMAP_HSOTG(0x10) +# define TM1 (1 << 0) +#define OTG_FORCESTDBY OMAP_HSOTG(0x14) +# define ENABLEFORCE (1 << 0) + +#endif /* CONFIG_ARCH_OMAP2430 */ + +#endif /* __MUSB_OMAP243X_H__ */ diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c new file mode 100644 index 000000000000..b73b036f3d77 --- /dev/null +++ b/drivers/usb/musb/tusb6010.c @@ -0,0 +1,1151 @@ +/* + * TUSB6010 USB 2.0 OTG Dual Role controller + * + * Copyright (C) 2006 Nokia Corporation + * Jarkko Nikula + * Tony Lindgren + * + * 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. + * + * Notes: + * - Driver assumes that interface to external host (main CPU) is + * configured for NOR FLASH interface instead of VLYNQ serial + * interface. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + +static void tusb_source_power(struct musb *musb, int is_on); + +#define TUSB_REV_MAJOR(reg_val) ((reg_val >> 4) & 0xf) +#define TUSB_REV_MINOR(reg_val) (reg_val & 0xf) + +/* + * Checks the revision. We need to use the DMA register as 3.0 does not + * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV. + */ +u8 tusb_get_revision(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + u32 die_id; + u8 rev; + + rev = musb_readl(tbase, TUSB_DMA_CTRL_REV) & 0xff; + if (TUSB_REV_MAJOR(rev) == 3) { + die_id = TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, + TUSB_DIDR1_HI)); + if (die_id >= TUSB_DIDR1_HI_REV_31) + rev |= 1; + } + + return rev; +} + +static int __init tusb_print_revision(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + u8 rev; + + rev = tusb_get_revision(musb); + + pr_info("tusb: %s%i.%i %s%i.%i %s%i.%i %s%i.%i %s%i %s%i.%i\n", + "prcm", + TUSB_REV_MAJOR(musb_readl(tbase, TUSB_PRCM_REV)), + TUSB_REV_MINOR(musb_readl(tbase, TUSB_PRCM_REV)), + "int", + TUSB_REV_MAJOR(musb_readl(tbase, TUSB_INT_CTRL_REV)), + TUSB_REV_MINOR(musb_readl(tbase, TUSB_INT_CTRL_REV)), + "gpio", + TUSB_REV_MAJOR(musb_readl(tbase, TUSB_GPIO_REV)), + TUSB_REV_MINOR(musb_readl(tbase, TUSB_GPIO_REV)), + "dma", + TUSB_REV_MAJOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)), + TUSB_REV_MINOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)), + "dieid", + TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, TUSB_DIDR1_HI)), + "rev", + TUSB_REV_MAJOR(rev), TUSB_REV_MINOR(rev)); + + return tusb_get_revision(musb); +} + +#define WBUS_QUIRK_MASK (TUSB_PHY_OTG_CTRL_TESTM2 | TUSB_PHY_OTG_CTRL_TESTM1 \ + | TUSB_PHY_OTG_CTRL_TESTM0) + +/* + * Workaround for spontaneous WBUS wake-up issue #2 for tusb3.0. + * Disables power detection in PHY for the duration of idle. + */ +static void tusb_wbus_quirk(struct musb *musb, int enabled) +{ + void __iomem *tbase = musb->ctrl_base; + static u32 phy_otg_ctrl, phy_otg_ena; + u32 tmp; + + if (enabled) { + phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL); + phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); + tmp = TUSB_PHY_OTG_CTRL_WRPROTECT + | phy_otg_ena | WBUS_QUIRK_MASK; + musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); + tmp = phy_otg_ena & ~WBUS_QUIRK_MASK; + tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2; + musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); + DBG(2, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", + musb_readl(tbase, TUSB_PHY_OTG_CTRL), + musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); + } else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE) + & TUSB_PHY_OTG_CTRL_TESTM2) { + tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl; + musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); + tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena; + musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); + DBG(2, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", + musb_readl(tbase, TUSB_PHY_OTG_CTRL), + musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); + phy_otg_ctrl = 0; + phy_otg_ena = 0; + } +} + +/* + * TUSB 6010 may use a parallel bus that doesn't support byte ops; + * so both loading and unloading FIFOs need explicit byte counts. + */ + +static inline void +tusb_fifo_write_unaligned(void __iomem *fifo, const u8 *buf, u16 len) +{ + u32 val; + int i; + + if (len > 4) { + for (i = 0; i < (len >> 2); i++) { + memcpy(&val, buf, 4); + musb_writel(fifo, 0, val); + buf += 4; + } + len %= 4; + } + if (len > 0) { + /* Write the rest 1 - 3 bytes to FIFO */ + memcpy(&val, buf, len); + musb_writel(fifo, 0, val); + } +} + +static inline void tusb_fifo_read_unaligned(void __iomem *fifo, + void __iomem *buf, u16 len) +{ + u32 val; + int i; + + if (len > 4) { + for (i = 0; i < (len >> 2); i++) { + val = musb_readl(fifo, 0); + memcpy(buf, &val, 4); + buf += 4; + } + len %= 4; + } + if (len > 0) { + /* Read the rest 1 - 3 bytes from FIFO */ + val = musb_readl(fifo, 0); + memcpy(buf, &val, len); + } +} + +void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) +{ + void __iomem *ep_conf = hw_ep->conf; + void __iomem *fifo = hw_ep->fifo; + u8 epnum = hw_ep->epnum; + + prefetch(buf); + + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + 'T', epnum, fifo, len, buf); + + if (epnum) + musb_writel(ep_conf, TUSB_EP_TX_OFFSET, + TUSB_EP_CONFIG_XFR_SIZE(len)); + else + musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_DIR_TX | + TUSB_EP0_CONFIG_XFR_SIZE(len)); + + if (likely((0x01 & (unsigned long) buf) == 0)) { + + /* Best case is 32bit-aligned destination address */ + if ((0x02 & (unsigned long) buf) == 0) { + if (len >= 4) { + writesl(fifo, buf, len >> 2); + buf += (len & ~0x03); + len &= 0x03; + } + } else { + if (len >= 2) { + u32 val; + int i; + + /* Cannot use writesw, fifo is 32-bit */ + for (i = 0; i < (len >> 2); i++) { + val = (u32)(*(u16 *)buf); + buf += 2; + val |= (*(u16 *)buf) << 16; + buf += 2; + musb_writel(fifo, 0, val); + } + len &= 0x03; + } + } + } + + if (len > 0) + tusb_fifo_write_unaligned(fifo, buf, len); +} + +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) +{ + void __iomem *ep_conf = hw_ep->conf; + void __iomem *fifo = hw_ep->fifo; + u8 epnum = hw_ep->epnum; + + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + 'R', epnum, fifo, len, buf); + + if (epnum) + musb_writel(ep_conf, TUSB_EP_RX_OFFSET, + TUSB_EP_CONFIG_XFR_SIZE(len)); + else + musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_XFR_SIZE(len)); + + if (likely((0x01 & (unsigned long) buf) == 0)) { + + /* Best case is 32bit-aligned destination address */ + if ((0x02 & (unsigned long) buf) == 0) { + if (len >= 4) { + readsl(fifo, buf, len >> 2); + buf += (len & ~0x03); + len &= 0x03; + } + } else { + if (len >= 2) { + u32 val; + int i; + + /* Cannot use readsw, fifo is 32-bit */ + for (i = 0; i < (len >> 2); i++) { + val = musb_readl(fifo, 0); + *(u16 *)buf = (u16)(val & 0xffff); + buf += 2; + *(u16 *)buf = (u16)(val >> 16); + buf += 2; + } + len &= 0x03; + } + } + } + + if (len > 0) + tusb_fifo_read_unaligned(fifo, buf, len); +} + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + +/* This is used by gadget drivers, and OTG transceiver logic, allowing + * at most mA current to be drawn from VBUS during a Default-B session + * (that is, while VBUS exceeds 4.4V). In Default-A (including pure host + * mode), or low power Default-B sessions, something else supplies power. + * Caller must take care of locking. + */ +static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) +{ + struct musb *musb = container_of(x, struct musb, xceiv); + void __iomem *tbase = musb->ctrl_base; + u32 reg; + + /* + * Keep clock active when enabled. Note that this is not tied to + * drawing VBUS, as with OTG mA can be less than musb->min_power. + */ + if (musb->set_clock) { + if (mA) + musb->set_clock(musb->clock, 1); + else + musb->set_clock(musb->clock, 0); + } + + /* tps65030 seems to consume max 100mA, with maybe 60mA available + * (measured on one board) for things other than tps and tusb. + * + * Boards sharing the CPU clock with CLKIN will need to prevent + * certain idle sleep states while the USB link is active. + * + * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }. + * The actual current usage would be very board-specific. For now, + * it's simpler to just use an aggregate (also board-specific). + */ + if (x->default_a || mA < (musb->min_power << 1)) + mA = 0; + + reg = musb_readl(tbase, TUSB_PRCM_MNGMT); + if (mA) { + musb->is_bus_powered = 1; + reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN; + } else { + musb->is_bus_powered = 0; + reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); + } + musb_writel(tbase, TUSB_PRCM_MNGMT, reg); + + DBG(2, "draw max %d mA VBUS\n", mA); + return 0; +} + +#else +#define tusb_draw_power NULL +#endif + +/* workaround for issue 13: change clock during chip idle + * (to be fixed in rev3 silicon) ... symptoms include disconnect + * or looping suspend/resume cycles + */ +static void tusb_set_clock_source(struct musb *musb, unsigned mode) +{ + void __iomem *tbase = musb->ctrl_base; + u32 reg; + + reg = musb_readl(tbase, TUSB_PRCM_CONF); + reg &= ~TUSB_PRCM_CONF_SYS_CLKSEL(0x3); + + /* 0 = refclk (clkin, XI) + * 1 = PHY 60 MHz (internal PLL) + * 2 = not supported + * 3 = what? + */ + if (mode > 0) + reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3); + + musb_writel(tbase, TUSB_PRCM_CONF, reg); + + /* FIXME tusb6010_platform_retime(mode == 0); */ +} + +/* + * Idle TUSB6010 until next wake-up event; NOR access always wakes. + * Other code ensures that we idle unless we're connected _and_ the + * USB link is not suspended ... and tells us the relevant wakeup + * events. SW_EN for voltage is handled separately. + */ +void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) +{ + void __iomem *tbase = musb->ctrl_base; + u32 reg; + + if ((wakeup_enables & TUSB_PRCM_WBUS) + && (tusb_get_revision(musb) == TUSB_REV_30)) + tusb_wbus_quirk(musb, 1); + + tusb_set_clock_source(musb, 0); + + wakeup_enables |= TUSB_PRCM_WNORCS; + musb_writel(tbase, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables); + + /* REVISIT writeup of WID implies that if WID set and ID is grounded, + * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared. + * Presumably that's mostly to save power, hence WID is immaterial ... + */ + + reg = musb_readl(tbase, TUSB_PRCM_MNGMT); + /* issue 4: when driving vbus, use hipower (vbus_det) comparator */ + if (is_host_active(musb)) { + reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; + reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN; + } else { + reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN; + reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; + } + reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE; + musb_writel(tbase, TUSB_PRCM_MNGMT, reg); + + DBG(6, "idle, wake on %02x\n", wakeup_enables); +} + +/* + * Updates cable VBUS status. Caller must take care of locking. + */ +int musb_platform_get_vbus_status(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + u32 otg_stat, prcm_mngmt; + int ret = 0; + + otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); + prcm_mngmt = musb_readl(tbase, TUSB_PRCM_MNGMT); + + /* Temporarily enable VBUS detection if it was disabled for + * suspend mode. Unless it's enabled otg_stat and devctl will + * not show correct VBUS state. + */ + if (!(prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) { + u32 tmp = prcm_mngmt; + tmp |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; + musb_writel(tbase, TUSB_PRCM_MNGMT, tmp); + otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); + musb_writel(tbase, TUSB_PRCM_MNGMT, prcm_mngmt); + } + + if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) + ret = 1; + + return ret; +} + +static struct timer_list musb_idle_timer; + +static void musb_do_idle(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + + switch (musb->xceiv.state) { + case OTG_STATE_A_WAIT_BCON: + if ((musb->a_wait_bcon != 0) + && (musb->idle_timeout == 0 + || time_after(jiffies, musb->idle_timeout))) { + DBG(4, "Nothing connected %s, turning off VBUS\n", + otg_state_string(musb)); + } + /* FALLTHROUGH */ + case OTG_STATE_A_IDLE: + tusb_source_power(musb, 0); + default: + break; + } + + if (!musb->is_active) { + u32 wakeups; + + /* wait until khubd handles port change status */ + if (is_host_active(musb) && (musb->port1_status >> 16)) + goto done; + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + if (is_peripheral_enabled(musb) && !musb->gadget_driver) + wakeups = 0; + else { + wakeups = TUSB_PRCM_WHOSTDISCON + | TUSB_PRCM_WBUS + | TUSB_PRCM_WVBUS; + if (is_otg_enabled(musb)) + wakeups |= TUSB_PRCM_WID; + } +#else + wakeups = TUSB_PRCM_WHOSTDISCON | TUSB_PRCM_WBUS; +#endif + tusb_allow_idle(musb, wakeups); + } +done: + spin_unlock_irqrestore(&musb->lock, flags); +} + +/* + * Maybe put TUSB6010 into idle mode mode depending on USB link status, + * like "disconnected" or "suspended". We'll be woken out of it by + * connect, resume, or disconnect. + * + * Needs to be called as the last function everywhere where there is + * register access to TUSB6010 because of NOR flash wake-up. + * Caller should own controller spinlock. + * + * Delay because peripheral enables D+ pullup 3msec after SE0, and + * we don't want to treat that full speed J as a wakeup event. + * ... peripherals must draw only suspend current after 10 msec. + */ +void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +{ + unsigned long default_timeout = jiffies + msecs_to_jiffies(3); + static unsigned long last_timer; + + if (timeout == 0) + timeout = default_timeout; + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || ((musb->a_wait_bcon == 0) + && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) { + DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + del_timer(&musb_idle_timer); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout)) { + if (!timer_pending(&musb_idle_timer)) + last_timer = timeout; + else { + DBG(4, "Longer idle timer already pending, ignoring\n"); + return; + } + } + last_timer = timeout; + + DBG(4, "%s inactive, for idle timer for %lu ms\n", + otg_state_string(musb), + (unsigned long)jiffies_to_msecs(timeout - jiffies)); + mod_timer(&musb_idle_timer, timeout); +} + +/* ticks of 60 MHz clock */ +#define DEVCLOCK 60000000 +#define OTG_TIMER_MS(msecs) ((msecs) \ + ? (TUSB_DEV_OTG_TIMER_VAL((DEVCLOCK/1000)*(msecs)) \ + | TUSB_DEV_OTG_TIMER_ENABLE) \ + : 0) + +static void tusb_source_power(struct musb *musb, int is_on) +{ + void __iomem *tbase = musb->ctrl_base; + u32 conf, prcm, timer; + u8 devctl; + + /* HDRC controls CPEN, but beware current surges during device + * connect. They can trigger transient overcurrent conditions + * that must be ignored. + */ + + prcm = musb_readl(tbase, TUSB_PRCM_MNGMT); + conf = musb_readl(tbase, TUSB_DEV_CONF); + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + if (is_on) { + if (musb->set_clock) + musb->set_clock(musb->clock, 1); + timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE); + musb->xceiv.default_a = 1; + musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + devctl |= MUSB_DEVCTL_SESSION; + + conf |= TUSB_DEV_CONF_USB_HOST_MODE; + MUSB_HST_MODE(musb); + } else { + u32 otg_stat; + + timer = 0; + + /* If ID pin is grounded, we want to be a_idle */ + otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); + if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) { + switch (musb->xceiv.state) { + case OTG_STATE_A_WAIT_VRISE: + case OTG_STATE_A_WAIT_BCON: + musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_A_WAIT_VFALL: + musb->xceiv.state = OTG_STATE_A_IDLE; + break; + default: + musb->xceiv.state = OTG_STATE_A_IDLE; + } + musb->is_active = 0; + musb->xceiv.default_a = 1; + MUSB_HST_MODE(musb); + } else { + musb->is_active = 0; + musb->xceiv.default_a = 0; + musb->xceiv.state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } + + devctl &= ~MUSB_DEVCTL_SESSION; + conf &= ~TUSB_DEV_CONF_USB_HOST_MODE; + if (musb->set_clock) + musb->set_clock(musb->clock, 0); + } + prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); + + musb_writel(tbase, TUSB_PRCM_MNGMT, prcm); + musb_writel(tbase, TUSB_DEV_OTG_TIMER, timer); + musb_writel(tbase, TUSB_DEV_CONF, conf); + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", + otg_state_string(musb), + musb_readb(musb->mregs, MUSB_DEVCTL), + musb_readl(tbase, TUSB_DEV_OTG_STAT), + conf, prcm); +} + +/* + * Sets the mode to OTG, peripheral or host by changing the ID detection. + * Caller must take care of locking. + * + * Note that if a mini-A cable is plugged in the ID line will stay down as + * the weak ID pull-up is not able to pull the ID up. + * + * REVISIT: It would be possible to add support for changing between host + * and peripheral modes in non-OTG configurations by reconfiguring hardware + * and then setting musb->board_mode. For now, only support OTG mode. + */ +void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +{ + void __iomem *tbase = musb->ctrl_base; + u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf; + + if (musb->board_mode != MUSB_OTG) { + ERR("Changing mode currently only supported in OTG mode\n"); + return; + } + + otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); + phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL); + phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); + dev_conf = musb_readl(tbase, TUSB_DEV_CONF); + + switch (musb_mode) { + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + case MUSB_HOST: /* Disable PHY ID detect, ground ID */ + phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + dev_conf |= TUSB_DEV_CONF_ID_SEL; + dev_conf &= ~TUSB_DEV_CONF_SOFT_ID; + break; +#endif + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + case MUSB_PERIPHERAL: /* Disable PHY ID detect, keep ID pull-up on */ + phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID); + break; +#endif + +#ifdef CONFIG_USB_MUSB_OTG + case MUSB_OTG: /* Use PHY ID detection */ + phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID); + break; +#endif + + default: + DBG(2, "Trying to set unknown mode %i\n", musb_mode); + } + + musb_writel(tbase, TUSB_PHY_OTG_CTRL, + TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl); + musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, + TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena); + musb_writel(tbase, TUSB_DEV_CONF, dev_conf); + + otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); + if ((musb_mode == MUSB_PERIPHERAL) && + !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) + INFO("Cannot be peripheral with mini-A cable " + "otg_stat: %08x\n", otg_stat); +} + +static inline unsigned long +tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) +{ + u32 otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); + unsigned long idle_timeout = 0; + + /* ID pin */ + if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) { + int default_a; + + if (is_otg_enabled(musb)) + default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS); + else + default_a = is_host_enabled(musb); + DBG(2, "Default-%c\n", default_a ? 'A' : 'B'); + musb->xceiv.default_a = default_a; + tusb_source_power(musb, default_a); + + /* Don't allow idling immediately */ + if (default_a) + idle_timeout = jiffies + (HZ * 3); + } + + /* VBUS state change */ + if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) { + + /* B-dev state machine: no vbus ~= disconnect */ + if ((is_otg_enabled(musb) && !musb->xceiv.default_a) + || !is_host_enabled(musb)) { +#ifdef CONFIG_USB_MUSB_HDRC_HCD + /* ? musb_root_disconnect(musb); */ + musb->port1_status &= + ~(USB_PORT_STAT_CONNECTION + | USB_PORT_STAT_ENABLE + | USB_PORT_STAT_LOW_SPEED + | USB_PORT_STAT_HIGH_SPEED + | USB_PORT_STAT_TEST + ); +#endif + + if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) { + DBG(1, "Forcing disconnect (no interrupt)\n"); + if (musb->xceiv.state != OTG_STATE_B_IDLE) { + /* INTR_DISCONNECT can hide... */ + musb->xceiv.state = OTG_STATE_B_IDLE; + musb->int_usb |= MUSB_INTR_DISCONNECT; + } + musb->is_active = 0; + } + DBG(2, "vbus change, %s, otg %03x\n", + otg_state_string(musb), otg_stat); + idle_timeout = jiffies + (1 * HZ); + schedule_work(&musb->irq_work); + + } else /* A-dev state machine */ { + DBG(2, "vbus change, %s, otg %03x\n", + otg_state_string(musb), otg_stat); + + switch (musb->xceiv.state) { + case OTG_STATE_A_IDLE: + DBG(2, "Got SRP, turning on VBUS\n"); + musb_set_vbus(musb, 1); + + /* CONNECT can wake if a_wait_bcon is set */ + if (musb->a_wait_bcon != 0) + musb->is_active = 0; + else + musb->is_active = 1; + + /* + * OPT FS A TD.4.6 needs few seconds for + * A_WAIT_VRISE + */ + idle_timeout = jiffies + (2 * HZ); + + break; + case OTG_STATE_A_WAIT_VRISE: + /* ignore; A-session-valid < VBUS_VALID/2, + * we monitor this with the timer + */ + break; + case OTG_STATE_A_WAIT_VFALL: + /* REVISIT this irq triggers during short + * spikes caused by enumeration ... + */ + if (musb->vbuserr_retry) { + musb->vbuserr_retry--; + tusb_source_power(musb, 1); + } else { + musb->vbuserr_retry + = VBUSERR_RETRY_COUNT; + tusb_source_power(musb, 0); + } + break; + default: + break; + } + } + } + + /* OTG timer expiration */ + if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { + u8 devctl; + + DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat); + + switch (musb->xceiv.state) { + case OTG_STATE_A_WAIT_VRISE: + /* VBUS has probably been valid for a while now, + * but may well have bounced out of range a bit + */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { + if ((devctl & MUSB_DEVCTL_VBUS) + != MUSB_DEVCTL_VBUS) { + DBG(2, "devctl %02x\n", devctl); + break; + } + musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + musb->is_active = 0; + idle_timeout = jiffies + + msecs_to_jiffies(musb->a_wait_bcon); + } else { + /* REVISIT report overcurrent to hub? */ + ERR("vbus too slow, devctl %02x\n", devctl); + tusb_source_power(musb, 0); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (musb->a_wait_bcon != 0) + idle_timeout = jiffies + + msecs_to_jiffies(musb->a_wait_bcon); + break; + case OTG_STATE_A_SUSPEND: + break; + case OTG_STATE_B_WAIT_ACON: + break; + default: + break; + } + } + schedule_work(&musb->irq_work); + + return idle_timeout; +} + +static irqreturn_t tusb_interrupt(int irq, void *__hci) +{ + struct musb *musb = __hci; + void __iomem *tbase = musb->ctrl_base; + unsigned long flags, idle_timeout = 0; + u32 int_mask, int_src; + + spin_lock_irqsave(&musb->lock, flags); + + /* Mask all interrupts to allow using both edge and level GPIO irq */ + int_mask = musb_readl(tbase, TUSB_INT_MASK); + musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); + + int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS; + DBG(3, "TUSB IRQ %08x\n", int_src); + + musb->int_usb = (u8) int_src; + + /* Acknowledge wake-up source interrupts */ + if (int_src & TUSB_INT_SRC_DEV_WAKEUP) { + u32 reg; + u32 i; + + if (tusb_get_revision(musb) == TUSB_REV_30) + tusb_wbus_quirk(musb, 0); + + /* there are issues re-locking the PLL on wakeup ... */ + + /* work around issue 8 */ + for (i = 0xf7f7f7; i > 0xf7f7f7 - 1000; i--) { + musb_writel(tbase, TUSB_SCRATCH_PAD, 0); + musb_writel(tbase, TUSB_SCRATCH_PAD, i); + reg = musb_readl(tbase, TUSB_SCRATCH_PAD); + if (reg == i) + break; + DBG(6, "TUSB NOR not ready\n"); + } + + /* work around issue 13 (2nd half) */ + tusb_set_clock_source(musb, 1); + + reg = musb_readl(tbase, TUSB_PRCM_WAKEUP_SOURCE); + musb_writel(tbase, TUSB_PRCM_WAKEUP_CLEAR, reg); + if (reg & ~TUSB_PRCM_WNORCS) { + musb->is_active = 1; + schedule_work(&musb->irq_work); + } + DBG(3, "wake %sactive %02x\n", + musb->is_active ? "" : "in", reg); + + /* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */ + } + + if (int_src & TUSB_INT_SRC_USB_IP_CONN) + del_timer(&musb_idle_timer); + + /* OTG state change reports (annoyingly) not issued by Mentor core */ + if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG + | TUSB_INT_SRC_OTG_TIMEOUT + | TUSB_INT_SRC_ID_STATUS_CHNG)) + idle_timeout = tusb_otg_ints(musb, int_src, tbase); + + /* TX dma callback must be handled here, RX dma callback is + * handled in tusb_omap_dma_cb. + */ + if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) { + u32 dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC); + u32 real_dma_src = musb_readl(tbase, TUSB_DMA_INT_MASK); + + DBG(3, "DMA IRQ %08x\n", dma_src); + real_dma_src = ~real_dma_src & dma_src; + if (tusb_dma_omap() && real_dma_src) { + int tx_source = (real_dma_src & 0xffff); + int i; + + for (i = 1; i <= 15; i++) { + if (tx_source & (1 << i)) { + DBG(3, "completing ep%i %s\n", i, "tx"); + musb_dma_completion(musb, i, 1); + } + } + } + musb_writel(tbase, TUSB_DMA_INT_CLEAR, dma_src); + } + + /* EP interrupts. In OCP mode tusb6010 mirrors the MUSB interrupts */ + if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX)) { + u32 musb_src = musb_readl(tbase, TUSB_USBIP_INT_SRC); + + musb_writel(tbase, TUSB_USBIP_INT_CLEAR, musb_src); + musb->int_rx = (((musb_src >> 16) & 0xffff) << 1); + musb->int_tx = (musb_src & 0xffff); + } else { + musb->int_rx = 0; + musb->int_tx = 0; + } + + if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff)) + musb_interrupt(musb); + + /* Acknowledge TUSB interrupts. Clear only non-reserved bits */ + musb_writel(tbase, TUSB_INT_SRC_CLEAR, + int_src & ~TUSB_INT_MASK_RESERVED_BITS); + + musb_platform_try_idle(musb, idle_timeout); + + musb_writel(tbase, TUSB_INT_MASK, int_mask); + spin_unlock_irqrestore(&musb->lock, flags); + + return IRQ_HANDLED; +} + +static int dma_off; + +/* + * Enables TUSB6010. Caller must take care of locking. + * REVISIT: + * - Check what is unnecessary in MGC_HdrcStart() + */ +void musb_platform_enable(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + + /* Setup TUSB6010 main interrupt mask. Enable all interrupts except SOF. + * REVISIT: Enable and deal with TUSB_INT_SRC_USB_IP_SOF */ + musb_writel(tbase, TUSB_INT_MASK, TUSB_INT_SRC_USB_IP_SOF); + + /* Setup TUSB interrupt, disable DMA and GPIO interrupts */ + musb_writel(tbase, TUSB_USBIP_INT_MASK, 0); + musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); + musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); + + /* Clear all subsystem interrups */ + musb_writel(tbase, TUSB_USBIP_INT_CLEAR, 0x7fffffff); + musb_writel(tbase, TUSB_DMA_INT_CLEAR, 0x7fffffff); + musb_writel(tbase, TUSB_GPIO_INT_CLEAR, 0x1ff); + + /* Acknowledge pending interrupt(s) */ + musb_writel(tbase, TUSB_INT_SRC_CLEAR, ~TUSB_INT_MASK_RESERVED_BITS); + + /* Only 0 clock cycles for minimum interrupt de-assertion time and + * interrupt polarity active low seems to work reliably here */ + musb_writel(tbase, TUSB_INT_CTRL_CONF, + TUSB_INT_CTRL_CONF_INT_RELCYC(0)); + + set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW); + + /* maybe force into the Default-A OTG state machine */ + if (!(musb_readl(tbase, TUSB_DEV_OTG_STAT) + & TUSB_DEV_OTG_STAT_ID_STATUS)) + musb_writel(tbase, TUSB_INT_SRC_SET, + TUSB_INT_SRC_ID_STATUS_CHNG); + + if (is_dma_capable() && dma_off) + printk(KERN_WARNING "%s %s: dma not reactivated\n", + __FILE__, __func__); + else + dma_off = 1; +} + +/* + * Disables TUSB6010. Caller must take care of locking. + */ +void musb_platform_disable(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + + /* FIXME stop DMA, IRQs, timers, ... */ + + /* disable all IRQs */ + musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); + musb_writel(tbase, TUSB_USBIP_INT_MASK, 0x7fffffff); + musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); + musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); + + del_timer(&musb_idle_timer); + + if (is_dma_capable() && !dma_off) { + printk(KERN_WARNING "%s %s: dma still active\n", + __FILE__, __func__); + dma_off = 1; + } +} + +/* + * Sets up TUSB6010 CPU interface specific signals and registers + * Note: Settings optimized for OMAP24xx + */ +static void __init tusb_setup_cpu_interface(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + + /* + * Disable GPIO[5:0] pullups (used as output DMA requests) + * Don't disable GPIO[7:6] as they are needed for wake-up. + */ + musb_writel(tbase, TUSB_PULLUP_1_CTRL, 0x0000003F); + + /* Disable all pullups on NOR IF, DMAREQ0 and DMAREQ1 */ + musb_writel(tbase, TUSB_PULLUP_2_CTRL, 0x01FFFFFF); + + /* Turn GPIO[5:0] to DMAREQ[5:0] signals */ + musb_writel(tbase, TUSB_GPIO_CONF, TUSB_GPIO_CONF_DMAREQ(0x3f)); + + /* Burst size 16x16 bits, all six DMA requests enabled, DMA request + * de-assertion time 2 system clocks p 62 */ + musb_writel(tbase, TUSB_DMA_REQ_CONF, + TUSB_DMA_REQ_CONF_BURST_SIZE(2) | + TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) | + TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2)); + + /* Set 0 wait count for synchronous burst access */ + musb_writel(tbase, TUSB_WAIT_COUNT, 1); +} + +static int __init tusb_start(struct musb *musb) +{ + void __iomem *tbase = musb->ctrl_base; + int ret = 0; + unsigned long flags; + u32 reg; + + if (musb->board_set_power) + ret = musb->board_set_power(1); + if (ret != 0) { + printk(KERN_ERR "tusb: Cannot enable TUSB6010\n"); + return ret; + } + + spin_lock_irqsave(&musb->lock, flags); + + if (musb_readl(tbase, TUSB_PROD_TEST_RESET) != + TUSB_PROD_TEST_RESET_VAL) { + printk(KERN_ERR "tusb: Unable to detect TUSB6010\n"); + goto err; + } + + ret = tusb_print_revision(musb); + if (ret < 2) { + printk(KERN_ERR "tusb: Unsupported TUSB6010 revision %i\n", + ret); + goto err; + } + + /* The uint bit for "USB non-PDR interrupt enable" has to be 1 when + * NOR FLASH interface is used */ + musb_writel(tbase, TUSB_VLYNQ_CTRL, 8); + + /* Select PHY free running 60MHz as a system clock */ + tusb_set_clock_source(musb, 1); + + /* VBus valid timer 1us, disable DFT/Debug and VLYNQ clocks for + * power saving, enable VBus detect and session end comparators, + * enable IDpullup, enable VBus charging */ + musb_writel(tbase, TUSB_PRCM_MNGMT, + TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(0xa) | + TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN | + TUSB_PRCM_MNGMT_OTG_SESS_END_EN | + TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN | + TUSB_PRCM_MNGMT_OTG_ID_PULLUP); + tusb_setup_cpu_interface(musb); + + /* simplify: always sense/pullup ID pins, as if in OTG mode */ + reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); + reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, reg); + + reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL); + reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; + musb_writel(tbase, TUSB_PHY_OTG_CTRL, reg); + + spin_unlock_irqrestore(&musb->lock, flags); + + return 0; + +err: + spin_unlock_irqrestore(&musb->lock, flags); + + if (musb->board_set_power) + musb->board_set_power(0); + + return -ENODEV; +} + +int __init musb_platform_init(struct musb *musb) +{ + struct platform_device *pdev; + struct resource *mem; + void __iomem *sync; + int ret; + + pdev = to_platform_device(musb->controller); + + /* dma address for async dma */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + musb->async = mem->start; + + /* dma address for sync dma */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!mem) { + pr_debug("no sync dma resource?\n"); + return -ENODEV; + } + musb->sync = mem->start; + + sync = ioremap(mem->start, mem->end - mem->start + 1); + if (!sync) { + pr_debug("ioremap for sync failed\n"); + return -ENOMEM; + } + musb->sync_va = sync; + + /* Offsets from base: VLYNQ at 0x000, MUSB regs at 0x400, + * FIFOs at 0x600, TUSB at 0x800 + */ + musb->mregs += TUSB_BASE_OFFSET; + + ret = tusb_start(musb); + if (ret) { + printk(KERN_ERR "Could not start tusb6010 (%d)\n", + ret); + return -ENODEV; + } + musb->isr = tusb_interrupt; + + if (is_host_enabled(musb)) + musb->board_set_vbus = tusb_source_power; + if (is_peripheral_enabled(musb)) + musb->xceiv.set_power = tusb_draw_power; + + setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + + return ret; +} + +int musb_platform_exit(struct musb *musb) +{ + del_timer_sync(&musb_idle_timer); + + if (musb->board_set_power) + musb->board_set_power(0); + + iounmap(musb->sync_va); + + return 0; +} diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h new file mode 100644 index 000000000000..db6dad0750ae --- /dev/null +++ b/drivers/usb/musb/tusb6010.h @@ -0,0 +1,402 @@ +/* + * Definitions for TUSB6010 USB 2.0 OTG Dual Role controller + * + * Copyright (C) 2006 Nokia Corporation + * Jarkko Nikula + * Tony Lindgren + * + * 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 __TUSB6010_H__ +#define __TUSB6010_H__ + +extern u8 tusb_get_revision(struct musb *musb); + +#ifdef CONFIG_USB_TUSB6010 +#define musb_in_tusb() 1 +#else +#define musb_in_tusb() 0 +#endif + +#ifdef CONFIG_USB_TUSB_OMAP_DMA +#define tusb_dma_omap() 1 +#else +#define tusb_dma_omap() 0 +#endif + +/* VLYNQ control register. 32-bit at offset 0x000 */ +#define TUSB_VLYNQ_CTRL 0x004 + +/* Mentor Graphics OTG core registers. 8,- 16- and 32-bit at offset 0x400 */ +#define TUSB_BASE_OFFSET 0x400 + +/* FIFO registers 32-bit at offset 0x600 */ +#define TUSB_FIFO_BASE 0x600 + +/* Device System & Control registers. 32-bit at offset 0x800 */ +#define TUSB_SYS_REG_BASE 0x800 + +#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) +#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) +#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) +#define TUSB_DEV_CONF_SOFT_ID (1 << 1) +#define TUSB_DEV_CONF_ID_SEL (1 << 0) + +#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) +#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) +#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) +#define TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP (1 << 23) +#define TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN (1 << 19) +#define TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN (1 << 18) +#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) +#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) +#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) +#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) +#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) +#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) +#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) +#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) +#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) +#define TUSB_PHY_OTG_CTRL_PHYREF_CLKSEL(v) (((v) & 3) << 7) +#define TUSB_PHY_OTG_CTRL_PD (1 << 6) +#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) +#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) +#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) +#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) +#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) +#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) + +/*OTG status register */ +#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) +#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) +#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) +#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) +#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) +#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) +#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) +#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) +#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) +#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) +#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) + +#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) +# define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) +# define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) +#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) + +/* PRCM configuration register */ +#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) +#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) +#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) + +/* PRCM management register */ +#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) +#define TUSB_PRCM_MNGMT_SRP_FIX_TIMER(v) (((v) & 0xf) << 25) +#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) +#define TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(v) (((v) & 0xf) << 20) +#define TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN (1 << 19) +#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) +#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) +#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) +#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) +#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) +#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) +#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) +#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) +#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) +#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) + +/* Wake-up source clear and mask registers */ +#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) +#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) +#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) +#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) +#define TUSB_PRCM_WGPIO_7 (1 << 12) +#define TUSB_PRCM_WGPIO_6 (1 << 11) +#define TUSB_PRCM_WGPIO_5 (1 << 10) +#define TUSB_PRCM_WGPIO_4 (1 << 9) +#define TUSB_PRCM_WGPIO_3 (1 << 8) +#define TUSB_PRCM_WGPIO_2 (1 << 7) +#define TUSB_PRCM_WGPIO_1 (1 << 6) +#define TUSB_PRCM_WGPIO_0 (1 << 5) +#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ +#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ +#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ +#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ +#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ + +#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) +#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) +#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) +#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) +#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) +#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) +#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) +#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) +#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) +#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) +#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) +#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) +#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) +#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) +#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) +#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) + +/* NOR flash interrupt source registers */ +#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) +#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) +#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) +#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) +#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) +#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) +#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) +#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) +#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) +#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) +#define TUSB_INT_SRC_DEV_READY (1 << 12) +#define TUSB_INT_SRC_USB_IP_TX (1 << 9) +#define TUSB_INT_SRC_USB_IP_RX (1 << 8) +#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) +#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) +#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) +#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) +#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) +#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) +#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) +#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) + +/* NOR flash interrupt registers reserved bits. Must be written as 0 */ +#define TUSB_INT_MASK_RESERVED_17 (0x3fff << 17) +#define TUSB_INT_MASK_RESERVED_13 (1 << 13) +#define TUSB_INT_MASK_RESERVED_8 (0xf << 8) +#define TUSB_INT_SRC_RESERVED_26 (0x1f << 26) +#define TUSB_INT_SRC_RESERVED_18 (0x3f << 18) +#define TUSB_INT_SRC_RESERVED_10 (0x03 << 10) + +/* Reserved bits for NOR flash interrupt mask and clear register */ +#define TUSB_INT_MASK_RESERVED_BITS (TUSB_INT_MASK_RESERVED_17 | \ + TUSB_INT_MASK_RESERVED_13 | \ + TUSB_INT_MASK_RESERVED_8) + +/* Reserved bits for NOR flash interrupt status register */ +#define TUSB_INT_SRC_RESERVED_BITS (TUSB_INT_SRC_RESERVED_26 | \ + TUSB_INT_SRC_RESERVED_18 | \ + TUSB_INT_SRC_RESERVED_10) + +#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) +#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) +#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) +#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) +#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) +#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) + +/* Offsets from each ep base register */ +#define TUSB_EP_TX_OFFSET 0x10c /* EP_IN in docs */ +#define TUSB_EP_RX_OFFSET 0x14c /* EP_OUT in docs */ +#define TUSB_EP_MAX_PACKET_SIZE_OFFSET 0x188 + +#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) +#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) +#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) + +/* Device System & Control register bitfields */ +#define TUSB_INT_CTRL_CONF_INT_RELCYC(v) (((v) & 0x7) << 18) +#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) +#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) +#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) +#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) +#define TUSB_DMA_REQ_CONF_DMA_REQ_EN(v) (((v) & 0x3f) << 20) +#define TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(v) (((v) & 0xf) << 16) +#define TUSB_EP0_CONFIG_SW_EN (1 << 8) +#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) +#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) +#define TUSB_EP_CONFIG_SW_EN (1 << 31) +#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) +#define TUSB_PROD_TEST_RESET_VAL 0xa596 +#define TUSB_EP_FIFO(ep) (TUSB_FIFO_BASE + (ep) * 0x20) + +#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) +#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) +#define TUSB_DIDR1_HI_CHIP_REV(v) (((v) >> 17) & 0xf) +#define TUSB_DIDR1_HI_REV_20 0 +#define TUSB_DIDR1_HI_REV_30 1 +#define TUSB_DIDR1_HI_REV_31 2 + +#define TUSB_REV_10 0x10 +#define TUSB_REV_20 0x20 +#define TUSB_REV_30 0x30 +#define TUSB_REV_31 0x31 + +/*----------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_TUSB6010 + +/* configuration parameters specific to this silicon */ + +/* Number of Tx endpoints. Legal values are 1 - 16 (this value includes EP0) */ +#define MUSB_C_NUM_EPT 16 + +/* Number of Rx endpoints. Legal values are 1 - 16 (this value includes EP0) */ +#define MUSB_C_NUM_EPR 16 + +/* Endpoint 1 to 15 direction types. C_EP1_DEF is defined if either Tx endpoint + * 1 or Rx endpoint 1 are used. + */ +#define MUSB_C_EP1_DEF + +/* C_EP1_TX_DEF is defined if Tx endpoint 1 is used */ +#define MUSB_C_EP1_TX_DEF + +/* C_EP1_RX_DEF is defined if Rx endpoint 1 is used */ +#define MUSB_C_EP1_RX_DEF + +/* C_EP1_TOR_DEF is defined if Tx endpoint 1 and Rx endpoint 1 share a FIFO */ +/* #define C_EP1_TOR_DEF */ + +/* C_EP1_TAR_DEF is defined if both Tx endpoint 1 and Rx endpoint 1 are used + * and do not share a FIFO. + */ +#define MUSB_C_EP1_TAR_DEF + +/* Similarly for all other used endpoints */ +#define MUSB_C_EP2_DEF +#define MUSB_C_EP2_TX_DEF +#define MUSB_C_EP2_RX_DEF +#define MUSB_C_EP2_TAR_DEF +#define MUSB_C_EP3_DEF +#define MUSB_C_EP3_TX_DEF +#define MUSB_C_EP3_RX_DEF +#define MUSB_C_EP3_TAR_DEF +#define MUSB_C_EP4_DEF +#define MUSB_C_EP4_TX_DEF +#define MUSB_C_EP4_RX_DEF +#define MUSB_C_EP4_TAR_DEF + +/* Endpoint 1 to 15 FIFO address bits. Legal values are 3 to 13 - corresponding + * to FIFO sizes of 8 to 8192 bytes. If an Tx endpoint shares a FIFO with an Rx + * endpoint then the Rx FIFO size must be the same as the Tx FIFO size. All + * endpoints 1 to 15 must be defined, unused endpoints should be set to 2. + */ +#define MUSB_C_EP1T_BITS 5 +#define MUSB_C_EP1R_BITS 5 +#define MUSB_C_EP2T_BITS 5 +#define MUSB_C_EP2R_BITS 5 +#define MUSB_C_EP3T_BITS 3 +#define MUSB_C_EP3R_BITS 3 +#define MUSB_C_EP4T_BITS 3 +#define MUSB_C_EP4R_BITS 3 + +#define MUSB_C_EP5T_BITS 2 +#define MUSB_C_EP5R_BITS 2 +#define MUSB_C_EP6T_BITS 2 +#define MUSB_C_EP6R_BITS 2 +#define MUSB_C_EP7T_BITS 2 +#define MUSB_C_EP7R_BITS 2 +#define MUSB_C_EP8T_BITS 2 +#define MUSB_C_EP8R_BITS 2 +#define MUSB_C_EP9T_BITS 2 +#define MUSB_C_EP9R_BITS 2 +#define MUSB_C_EP10T_BITS 2 +#define MUSB_C_EP10R_BITS 2 +#define MUSB_C_EP11T_BITS 2 +#define MUSB_C_EP11R_BITS 2 +#define MUSB_C_EP12T_BITS 2 +#define MUSB_C_EP12R_BITS 2 +#define MUSB_C_EP13T_BITS 2 +#define MUSB_C_EP13R_BITS 2 +#define MUSB_C_EP14T_BITS 2 +#define MUSB_C_EP14R_BITS 2 +#define MUSB_C_EP15T_BITS 2 +#define MUSB_C_EP15R_BITS 2 + +/* Define the following constant if the USB2.0 Transceiver Macrocell data width + * is 16-bits. + */ +/* #define C_UTM_16 */ + +/* Define this constant if the CPU uses big-endian byte ordering. */ +/* #define C_BIGEND */ + +/* Define the following constant if any Tx endpoint is required to support + * multiple bulk packets. + */ +/* #define C_MP_TX */ + +/* Define the following constant if any Rx endpoint is required to support + * multiple bulk packets. + */ +/* #define C_MP_RX */ + +/* Define the following constant if any Tx endpoint is required to support high + * bandwidth ISO. + */ +/* #define C_HB_TX */ + +/* Define the following constant if any Rx endpoint is required to support high + * bandwidth ISO. + */ +/* #define C_HB_RX */ + +/* Define the following constant if software connect/disconnect control is + * required. + */ +#define MUSB_C_SOFT_CON + +/* Define the following constant if Vendor Control Registers are required. */ +/* #define C_VEND_REG */ + +/* Vendor control register widths. */ +#define MUSB_C_VCTL_BITS 4 +#define MUSB_C_VSTAT_BITS 8 + +/* Define the following constant to include a DMA controller. */ +/* #define C_DMA */ + +/* Define the following constant if 2 or more DMA channels are required. */ +/* #define C_DMA2 */ + +/* Define the following constant if 3 or more DMA channels are required. */ +/* #define C_DMA3 */ + +/* Define the following constant if 4 or more DMA channels are required. */ +/* #define C_DMA4 */ + +/* Define the following constant if 5 or more DMA channels are required. */ +/* #define C_DMA5 */ + +/* Define the following constant if 6 or more DMA channels are required. */ +/* #define C_DMA6 */ + +/* Define the following constant if 7 or more DMA channels are required. */ +/* #define C_DMA7 */ + +/* Define the following constant if 8 or more DMA channels are required. */ +/* #define C_DMA8 */ + +/* Enable Dynamic FIFO Sizing */ +#define MUSB_C_DYNFIFO_DEF + +/* Derived constants. The following constants are derived from the previous + * configuration constants + */ + +/* Total number of endpoints. Legal values are 2 - 16. This must be equal to + * the larger of C_NUM_EPT, C_NUM_EPR + */ +/* #define MUSB_C_NUM_EPS 5 */ + +/* C_EPMAX_BITS is equal to the largest endpoint FIFO word address bits */ +#define MUSB_C_EPMAX_BITS 11 + +/* C_RAM_BITS is the number of address bits required to address the RAM (32-bit + * addresses). It is defined as log2 of the sum of 2** of all the endpoint FIFO + * dword address bits (rounded up). + */ +#define MUSB_C_RAM_BITS 12 + +#endif /* CONFIG_USB_TUSB6010 */ + +#endif /* __TUSB6010_H__ */ diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c new file mode 100644 index 000000000000..52f7f29cebda --- /dev/null +++ b/drivers/usb/musb/tusb6010_omap.c @@ -0,0 +1,719 @@ +/* + * TUSB6010 USB 2.0 OTG Dual Role controller OMAP DMA interface + * + * Copyright (C) 2006 Nokia Corporation + * Tony Lindgren + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + +#define to_chdat(c) ((struct tusb_omap_dma_ch *)(c)->private_data) + +#define MAX_DMAREQ 5 /* REVISIT: Really 6, but req5 not OK */ + +struct tusb_omap_dma_ch { + struct musb *musb; + void __iomem *tbase; + unsigned long phys_offset; + int epnum; + u8 tx; + struct musb_hw_ep *hw_ep; + + int ch; + s8 dmareq; + s8 sync_dev; + + struct tusb_omap_dma *tusb_dma; + + void __iomem *dma_addr; + + u32 len; + u16 packet_sz; + u16 transfer_packet_sz; + u32 transfer_len; + u32 completed_len; +}; + +struct tusb_omap_dma { + struct dma_controller controller; + struct musb *musb; + void __iomem *tbase; + + int ch; + s8 dmareq; + s8 sync_dev; + unsigned multichannel:1; +}; + +static int tusb_omap_dma_start(struct dma_controller *c) +{ + struct tusb_omap_dma *tusb_dma; + + tusb_dma = container_of(c, struct tusb_omap_dma, controller); + + /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ + + return 0; +} + +static int tusb_omap_dma_stop(struct dma_controller *c) +{ + struct tusb_omap_dma *tusb_dma; + + tusb_dma = container_of(c, struct tusb_omap_dma, controller); + + /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ + + return 0; +} + +/* + * Allocate dmareq0 to the current channel unless it's already taken + */ +static inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat) +{ + u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); + + if (reg != 0) { + DBG(3, "ep%i dmareq0 is busy for ep%i\n", + chdat->epnum, reg & 0xf); + return -EAGAIN; + } + + if (chdat->tx) + reg = (1 << 4) | chdat->epnum; + else + reg = chdat->epnum; + + musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg); + + return 0; +} + +static inline void tusb_omap_free_shared_dmareq(struct tusb_omap_dma_ch *chdat) +{ + u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); + + if ((reg & 0xf) != chdat->epnum) { + printk(KERN_ERR "ep%i trying to release dmareq0 for ep%i\n", + chdat->epnum, reg & 0xf); + return; + } + musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, 0); +} + +/* + * See also musb_dma_completion in plat_uds.c and musb_g_[tx|rx]() in + * musb_gadget.c. + */ +static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) +{ + struct dma_channel *channel = (struct dma_channel *)data; + struct tusb_omap_dma_ch *chdat = to_chdat(channel); + struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; + struct musb *musb = chdat->musb; + struct musb_hw_ep *hw_ep = chdat->hw_ep; + void __iomem *ep_conf = hw_ep->conf; + void __iomem *mbase = musb->mregs; + unsigned long remaining, flags, pio; + int ch; + + spin_lock_irqsave(&musb->lock, flags); + + if (tusb_dma->multichannel) + ch = chdat->ch; + else + ch = tusb_dma->ch; + + if (ch_status != OMAP_DMA_BLOCK_IRQ) + printk(KERN_ERR "TUSB DMA error status: %i\n", ch_status); + + DBG(3, "ep%i %s dma callback ch: %i status: %x\n", + chdat->epnum, chdat->tx ? "tx" : "rx", + ch, ch_status); + + if (chdat->tx) + remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET); + else + remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET); + + remaining = TUSB_EP_CONFIG_XFR_SIZE(remaining); + + /* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */ + if (unlikely(remaining > chdat->transfer_len)) { + DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n", + chdat->tx ? "tx" : "rx", chdat->ch, + remaining); + remaining = 0; + } + + channel->actual_len = chdat->transfer_len - remaining; + pio = chdat->len - channel->actual_len; + + DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); + + /* Transfer remaining 1 - 31 bytes */ + if (pio > 0 && pio < 32) { + u8 *buf; + + DBG(3, "Using PIO for remaining %lu bytes\n", pio); + buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len; + if (chdat->tx) { + dma_cache_maint(phys_to_virt((u32)chdat->dma_addr), + chdat->transfer_len, DMA_TO_DEVICE); + musb_write_fifo(hw_ep, pio, buf); + } else { + musb_read_fifo(hw_ep, pio, buf); + dma_cache_maint(phys_to_virt((u32)chdat->dma_addr), + chdat->transfer_len, DMA_FROM_DEVICE); + } + channel->actual_len += pio; + } + + if (!tusb_dma->multichannel) + tusb_omap_free_shared_dmareq(chdat); + + channel->status = MUSB_DMA_STATUS_FREE; + + /* Handle only RX callbacks here. TX callbacks must be handled based + * on the TUSB DMA status interrupt. + * REVISIT: Use both TUSB DMA status interrupt and OMAP DMA callback + * interrupt for RX and TX. + */ + if (!chdat->tx) + musb_dma_completion(musb, chdat->epnum, chdat->tx); + + /* We must terminate short tx transfers manually by setting TXPKTRDY. + * REVISIT: This same problem may occur with other MUSB dma as well. + * Easy to test with g_ether by pinging the MUSB board with ping -s54. + */ + if ((chdat->transfer_len < chdat->packet_sz) + || (chdat->transfer_len % chdat->packet_sz != 0)) { + u16 csr; + + if (chdat->tx) { + DBG(3, "terminating short tx packet\n"); + musb_ep_select(mbase, chdat->epnum); + csr = musb_readw(hw_ep->regs, MUSB_TXCSR); + csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY + | MUSB_TXCSR_P_WZC_BITS; + musb_writew(hw_ep->regs, MUSB_TXCSR, csr); + } + } + + spin_unlock_irqrestore(&musb->lock, flags); +} + +static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, + u8 rndis_mode, dma_addr_t dma_addr, u32 len) +{ + struct tusb_omap_dma_ch *chdat = to_chdat(channel); + struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; + struct musb *musb = chdat->musb; + struct musb_hw_ep *hw_ep = chdat->hw_ep; + void __iomem *mbase = musb->mregs; + void __iomem *ep_conf = hw_ep->conf; + dma_addr_t fifo = hw_ep->fifo_sync; + struct omap_dma_channel_params dma_params; + u32 dma_remaining; + int src_burst, dst_burst; + u16 csr; + int ch; + s8 dmareq; + s8 sync_dev; + + if (unlikely(dma_addr & 0x1) || (len < 32) || (len > packet_sz)) + return false; + + /* + * HW issue #10: Async dma will eventually corrupt the XFR_SIZE + * register which will cause missed DMA interrupt. We could try to + * use a timer for the callback, but it is unsafe as the XFR_SIZE + * register is corrupt, and we won't know if the DMA worked. + */ + if (dma_addr & 0x2) + return false; + + /* + * Because of HW issue #10, it seems like mixing sync DMA and async + * PIO access can confuse the DMA. Make sure XFR_SIZE is reset before + * using the channel for DMA. + */ + if (chdat->tx) + dma_remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET); + else + dma_remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET); + + dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining); + if (dma_remaining) { + DBG(2, "Busy %s dma ch%i, not using: %08x\n", + chdat->tx ? "tx" : "rx", chdat->ch, + dma_remaining); + return false; + } + + chdat->transfer_len = len & ~0x1f; + + if (len < packet_sz) + chdat->transfer_packet_sz = chdat->transfer_len; + else + chdat->transfer_packet_sz = packet_sz; + + if (tusb_dma->multichannel) { + ch = chdat->ch; + dmareq = chdat->dmareq; + sync_dev = chdat->sync_dev; + } else { + if (tusb_omap_use_shared_dmareq(chdat) != 0) { + DBG(3, "could not get dma for ep%i\n", chdat->epnum); + return false; + } + if (tusb_dma->ch < 0) { + /* REVISIT: This should get blocked earlier, happens + * with MSC ErrorRecoveryTest + */ + WARN_ON(1); + return false; + } + + ch = tusb_dma->ch; + dmareq = tusb_dma->dmareq; + sync_dev = tusb_dma->sync_dev; + omap_set_dma_callback(ch, tusb_omap_dma_cb, channel); + } + + chdat->packet_sz = packet_sz; + chdat->len = len; + channel->actual_len = 0; + chdat->dma_addr = (void __iomem *)dma_addr; + channel->status = MUSB_DMA_STATUS_BUSY; + + /* Since we're recycling dma areas, we need to clean or invalidate */ + if (chdat->tx) + dma_cache_maint(phys_to_virt(dma_addr), len, DMA_TO_DEVICE); + else + dma_cache_maint(phys_to_virt(dma_addr), len, DMA_FROM_DEVICE); + + /* Use 16-bit transfer if dma_addr is not 32-bit aligned */ + if ((dma_addr & 0x3) == 0) { + dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; + dma_params.elem_count = 8; /* Elements in frame */ + } else { + dma_params.data_type = OMAP_DMA_DATA_TYPE_S16; + dma_params.elem_count = 16; /* Elements in frame */ + fifo = hw_ep->fifo_async; + } + + dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */ + + DBG(3, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n", + chdat->epnum, chdat->tx ? "tx" : "rx", + ch, dma_addr, chdat->transfer_len, len, + chdat->transfer_packet_sz, packet_sz); + + /* + * Prepare omap DMA for transfer + */ + if (chdat->tx) { + dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; + dma_params.src_start = (unsigned long)dma_addr; + dma_params.src_ei = 0; + dma_params.src_fi = 0; + + dma_params.dst_amode = OMAP_DMA_AMODE_DOUBLE_IDX; + dma_params.dst_start = (unsigned long)fifo; + dma_params.dst_ei = 1; + dma_params.dst_fi = -31; /* Loop 32 byte window */ + + dma_params.trigger = sync_dev; + dma_params.sync_mode = OMAP_DMA_SYNC_FRAME; + dma_params.src_or_dst_synch = 0; /* Dest sync */ + + src_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 read */ + dst_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 write */ + } else { + dma_params.src_amode = OMAP_DMA_AMODE_DOUBLE_IDX; + dma_params.src_start = (unsigned long)fifo; + dma_params.src_ei = 1; + dma_params.src_fi = -31; /* Loop 32 byte window */ + + dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; + dma_params.dst_start = (unsigned long)dma_addr; + dma_params.dst_ei = 0; + dma_params.dst_fi = 0; + + dma_params.trigger = sync_dev; + dma_params.sync_mode = OMAP_DMA_SYNC_FRAME; + dma_params.src_or_dst_synch = 1; /* Source sync */ + + src_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 read */ + dst_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 write */ + } + + DBG(3, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n", + chdat->epnum, chdat->tx ? "tx" : "rx", + (dma_params.data_type == OMAP_DMA_DATA_TYPE_S32) ? 32 : 16, + ((dma_addr & 0x3) == 0) ? "sync" : "async", + dma_params.src_start, dma_params.dst_start); + + omap_set_dma_params(ch, &dma_params); + omap_set_dma_src_burst_mode(ch, src_burst); + omap_set_dma_dest_burst_mode(ch, dst_burst); + omap_set_dma_write_mode(ch, OMAP_DMA_WRITE_LAST_NON_POSTED); + + /* + * Prepare MUSB for DMA transfer + */ + if (chdat->tx) { + musb_ep_select(mbase, chdat->epnum); + csr = musb_readw(hw_ep->regs, MUSB_TXCSR); + csr |= (MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE); + csr &= ~MUSB_TXCSR_P_UNDERRUN; + musb_writew(hw_ep->regs, MUSB_TXCSR, csr); + } else { + musb_ep_select(mbase, chdat->epnum); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + csr |= MUSB_RXCSR_DMAENAB; + csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAMODE); + musb_writew(hw_ep->regs, MUSB_RXCSR, + csr | MUSB_RXCSR_P_WZC_BITS); + } + + /* + * Start DMA transfer + */ + omap_start_dma(ch); + + if (chdat->tx) { + /* Send transfer_packet_sz packets at a time */ + musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, + chdat->transfer_packet_sz); + + musb_writel(ep_conf, TUSB_EP_TX_OFFSET, + TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); + } else { + /* Receive transfer_packet_sz packets at a time */ + musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, + chdat->transfer_packet_sz << 16); + + musb_writel(ep_conf, TUSB_EP_RX_OFFSET, + TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); + } + + return true; +} + +static int tusb_omap_dma_abort(struct dma_channel *channel) +{ + struct tusb_omap_dma_ch *chdat = to_chdat(channel); + struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; + + if (!tusb_dma->multichannel) { + if (tusb_dma->ch >= 0) { + omap_stop_dma(tusb_dma->ch); + omap_free_dma(tusb_dma->ch); + tusb_dma->ch = -1; + } + + tusb_dma->dmareq = -1; + tusb_dma->sync_dev = -1; + } + + channel->status = MUSB_DMA_STATUS_FREE; + + return 0; +} + +static inline int tusb_omap_dma_allocate_dmareq(struct tusb_omap_dma_ch *chdat) +{ + u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); + int i, dmareq_nr = -1; + + const int sync_dev[6] = { + OMAP24XX_DMA_EXT_DMAREQ0, + OMAP24XX_DMA_EXT_DMAREQ1, + OMAP242X_DMA_EXT_DMAREQ2, + OMAP242X_DMA_EXT_DMAREQ3, + OMAP242X_DMA_EXT_DMAREQ4, + OMAP242X_DMA_EXT_DMAREQ5, + }; + + for (i = 0; i < MAX_DMAREQ; i++) { + int cur = (reg & (0xf << (i * 5))) >> (i * 5); + if (cur == 0) { + dmareq_nr = i; + break; + } + } + + if (dmareq_nr == -1) + return -EAGAIN; + + reg |= (chdat->epnum << (dmareq_nr * 5)); + if (chdat->tx) + reg |= ((1 << 4) << (dmareq_nr * 5)); + musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg); + + chdat->dmareq = dmareq_nr; + chdat->sync_dev = sync_dev[chdat->dmareq]; + + return 0; +} + +static inline void tusb_omap_dma_free_dmareq(struct tusb_omap_dma_ch *chdat) +{ + u32 reg; + + if (!chdat || chdat->dmareq < 0) + return; + + reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); + reg &= ~(0x1f << (chdat->dmareq * 5)); + musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg); + + chdat->dmareq = -1; + chdat->sync_dev = -1; +} + +static struct dma_channel *dma_channel_pool[MAX_DMAREQ]; + +static struct dma_channel * +tusb_omap_dma_allocate(struct dma_controller *c, + struct musb_hw_ep *hw_ep, + u8 tx) +{ + int ret, i; + const char *dev_name; + struct tusb_omap_dma *tusb_dma; + struct musb *musb; + void __iomem *tbase; + struct dma_channel *channel = NULL; + struct tusb_omap_dma_ch *chdat = NULL; + u32 reg; + + tusb_dma = container_of(c, struct tusb_omap_dma, controller); + musb = tusb_dma->musb; + tbase = musb->ctrl_base; + + reg = musb_readl(tbase, TUSB_DMA_INT_MASK); + if (tx) + reg &= ~(1 << hw_ep->epnum); + else + reg &= ~(1 << (hw_ep->epnum + 15)); + musb_writel(tbase, TUSB_DMA_INT_MASK, reg); + + /* REVISIT: Why does dmareq5 not work? */ + if (hw_ep->epnum == 0) { + DBG(3, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx"); + return NULL; + } + + for (i = 0; i < MAX_DMAREQ; i++) { + struct dma_channel *ch = dma_channel_pool[i]; + if (ch->status == MUSB_DMA_STATUS_UNKNOWN) { + ch->status = MUSB_DMA_STATUS_FREE; + channel = ch; + chdat = ch->private_data; + break; + } + } + + if (!channel) + return NULL; + + if (tx) { + chdat->tx = 1; + dev_name = "TUSB transmit"; + } else { + chdat->tx = 0; + dev_name = "TUSB receive"; + } + + chdat->musb = tusb_dma->musb; + chdat->tbase = tusb_dma->tbase; + chdat->hw_ep = hw_ep; + chdat->epnum = hw_ep->epnum; + chdat->dmareq = -1; + chdat->completed_len = 0; + chdat->tusb_dma = tusb_dma; + + channel->max_len = 0x7fffffff; + channel->desired_mode = 0; + channel->actual_len = 0; + + if (tusb_dma->multichannel) { + ret = tusb_omap_dma_allocate_dmareq(chdat); + if (ret != 0) + goto free_dmareq; + + ret = omap_request_dma(chdat->sync_dev, dev_name, + tusb_omap_dma_cb, channel, &chdat->ch); + if (ret != 0) + goto free_dmareq; + } else if (tusb_dma->ch == -1) { + tusb_dma->dmareq = 0; + tusb_dma->sync_dev = OMAP24XX_DMA_EXT_DMAREQ0; + + /* Callback data gets set later in the shared dmareq case */ + ret = omap_request_dma(tusb_dma->sync_dev, "TUSB shared", + tusb_omap_dma_cb, NULL, &tusb_dma->ch); + if (ret != 0) + goto free_dmareq; + + chdat->dmareq = -1; + chdat->ch = -1; + } + + DBG(3, "ep%i %s dma: %s dma%i dmareq%i sync%i\n", + chdat->epnum, + chdat->tx ? "tx" : "rx", + chdat->ch >= 0 ? "dedicated" : "shared", + chdat->ch >= 0 ? chdat->ch : tusb_dma->ch, + chdat->dmareq >= 0 ? chdat->dmareq : tusb_dma->dmareq, + chdat->sync_dev >= 0 ? chdat->sync_dev : tusb_dma->sync_dev); + + return channel; + +free_dmareq: + tusb_omap_dma_free_dmareq(chdat); + + DBG(3, "ep%i: Could not get a DMA channel\n", chdat->epnum); + channel->status = MUSB_DMA_STATUS_UNKNOWN; + + return NULL; +} + +static void tusb_omap_dma_release(struct dma_channel *channel) +{ + struct tusb_omap_dma_ch *chdat = to_chdat(channel); + struct musb *musb = chdat->musb; + void __iomem *tbase = musb->ctrl_base; + u32 reg; + + DBG(3, "ep%i ch%i\n", chdat->epnum, chdat->ch); + + reg = musb_readl(tbase, TUSB_DMA_INT_MASK); + if (chdat->tx) + reg |= (1 << chdat->epnum); + else + reg |= (1 << (chdat->epnum + 15)); + musb_writel(tbase, TUSB_DMA_INT_MASK, reg); + + reg = musb_readl(tbase, TUSB_DMA_INT_CLEAR); + if (chdat->tx) + reg |= (1 << chdat->epnum); + else + reg |= (1 << (chdat->epnum + 15)); + musb_writel(tbase, TUSB_DMA_INT_CLEAR, reg); + + channel->status = MUSB_DMA_STATUS_UNKNOWN; + + if (chdat->ch >= 0) { + omap_stop_dma(chdat->ch); + omap_free_dma(chdat->ch); + chdat->ch = -1; + } + + if (chdat->dmareq >= 0) + tusb_omap_dma_free_dmareq(chdat); + + channel = NULL; +} + +void dma_controller_destroy(struct dma_controller *c) +{ + struct tusb_omap_dma *tusb_dma; + int i; + + tusb_dma = container_of(c, struct tusb_omap_dma, controller); + for (i = 0; i < MAX_DMAREQ; i++) { + struct dma_channel *ch = dma_channel_pool[i]; + if (ch) { + kfree(ch->private_data); + kfree(ch); + } + } + + if (!tusb_dma->multichannel && tusb_dma && tusb_dma->ch >= 0) + omap_free_dma(tusb_dma->ch); + + kfree(tusb_dma); +} + +struct dma_controller *__init +dma_controller_create(struct musb *musb, void __iomem *base) +{ + void __iomem *tbase = musb->ctrl_base; + struct tusb_omap_dma *tusb_dma; + int i; + + /* REVISIT: Get dmareq lines used from board-*.c */ + + musb_writel(musb->ctrl_base, TUSB_DMA_INT_MASK, 0x7fffffff); + musb_writel(musb->ctrl_base, TUSB_DMA_EP_MAP, 0); + + musb_writel(tbase, TUSB_DMA_REQ_CONF, + TUSB_DMA_REQ_CONF_BURST_SIZE(2) + | TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) + | TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2)); + + tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL); + if (!tusb_dma) + goto cleanup; + + tusb_dma->musb = musb; + tusb_dma->tbase = musb->ctrl_base; + + tusb_dma->ch = -1; + tusb_dma->dmareq = -1; + tusb_dma->sync_dev = -1; + + tusb_dma->controller.start = tusb_omap_dma_start; + tusb_dma->controller.stop = tusb_omap_dma_stop; + tusb_dma->controller.channel_alloc = tusb_omap_dma_allocate; + tusb_dma->controller.channel_release = tusb_omap_dma_release; + tusb_dma->controller.channel_program = tusb_omap_dma_program; + tusb_dma->controller.channel_abort = tusb_omap_dma_abort; + + if (tusb_get_revision(musb) >= TUSB_REV_30) + tusb_dma->multichannel = 1; + + for (i = 0; i < MAX_DMAREQ; i++) { + struct dma_channel *ch; + struct tusb_omap_dma_ch *chdat; + + ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL); + if (!ch) + goto cleanup; + + dma_channel_pool[i] = ch; + + chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL); + if (!chdat) + goto cleanup; + + ch->status = MUSB_DMA_STATUS_UNKNOWN; + ch->private_data = chdat; + } + + return &tusb_dma->controller; + +cleanup: + dma_controller_destroy(&tusb_dma->controller); + + return NULL; +} diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h new file mode 100644 index 000000000000..d325a0d5bf4f --- /dev/null +++ b/include/linux/usb/musb.h @@ -0,0 +1,70 @@ +/* + * This is used to for host and peripheral modes of the driver for + * Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC. + * + * Board initialization should put one of these into dev->platform_data, + * probably on some platform_device named "musb_hdrc". It encapsulates + * key configuration differences between boards. + */ + +/* The USB role is defined by the connector used on the board, so long as + * standards are being followed. (Developer boards sometimes won't.) + */ +enum musb_mode { + MUSB_UNDEFINED = 0, + MUSB_HOST, /* A or Mini-A connector */ + MUSB_PERIPHERAL, /* B or Mini-B connector */ + MUSB_OTG /* Mini-AB connector */ +}; + +struct clk; + +struct musb_hdrc_platform_data { + /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */ + u8 mode; + + /* for clk_get() */ + const char *clock; + + /* (HOST or OTG) switch VBUS on/off */ + int (*set_vbus)(struct device *dev, int is_on); + + /* (HOST or OTG) mA/2 power supplied on (default = 8mA) */ + u8 power; + + /* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */ + u8 min_power; + + /* (HOST or OTG) msec/2 after VBUS on till power good */ + u8 potpgt; + + /* TBD: chip defaults should probably go someplace else, + * e.g. number of tx/rx endpoints, etc + */ + unsigned multipoint:1; + + /* Power the device on or off */ + int (*set_power)(int state); + + /* Turn device clock on or off */ + int (*set_clock)(struct clk *clock, int is_on); +}; + + +/* TUSB 6010 support */ + +#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */ +#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */ +#define TUSB6010_REFCLK_19 52083 /* psec/clk @ 19.2 MHz CLKIN */ + +#ifdef CONFIG_ARCH_OMAP2 + +extern int __init tusb6010_setup_interface( + struct musb_hdrc_platform_data *data, + unsigned ps_refclk, unsigned waitpin, + unsigned async_cs, unsigned sync_cs, + unsigned irq, unsigned dmachan); + +extern int tusb6010_platform_retime(unsigned is_refclk); + +#endif /* OMAP2 */ -- cgit v1.2.3 From ca6d1b1333bc2e61e37982de1f28d8604c232414 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Aug 2008 12:40:54 +0300 Subject: usb: musb: pass configuration specifics via pdata Use platform_data to pass musb configuration-specific details to musb driver. This patch will prevent that other platforms selecting HAVE_CLK and enabling musb won't break tree building. The other parts of it will come when linux-omap merge up more omap2/3 board-files. Signed-off-by: Felipe Balbi Acked-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-omap2/usb-tusb6010.c | 1 - drivers/usb/musb/musb_core.c | 37 ++++---- drivers/usb/musb/musb_core.h | 14 +-- drivers/usb/musb/tusb6010.h | 169 ------------------------------------- include/linux/usb/musb.h | 38 +++++++-- 5 files changed, 51 insertions(+), 208 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c index 1607c941d95f..10ef464d6be7 100644 --- a/arch/arm/mach-omap2/usb-tusb6010.c +++ b/arch/arm/mach-omap2/usb-tusb6010.c @@ -317,7 +317,6 @@ tusb6010_setup_interface(struct musb_hdrc_platform_data *data, printk(error, 6, status); return -ENODEV; } - data->multipoint = 1; tusb_device.dev.platform_data = data; /* REVISIT let the driver know what DMA channels work */ diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 462586d06da9..d68ec6daf335 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -990,12 +990,6 @@ static void musb_shutdown(struct platform_device *pdev) * We don't currently use dynamic fifo setup capability to do anything * more than selecting one of a bunch of predefined configurations. */ -#ifdef MUSB_C_DYNFIFO_DEF -#define can_dynfifo() 1 -#else -#define can_dynfifo() 0 -#endif - #if defined(CONFIG_USB_TUSB6010) || \ defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) static ushort __initdata fifo_mode = 4; @@ -1008,8 +1002,6 @@ module_param(fifo_mode, ushort, 0); MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration"); -#define DYN_FIFO_SIZE (1<<(MUSB_C_RAM_BITS+2)) - enum fifo_style { FIFO_RXTX, FIFO_TX, FIFO_RX } __attribute__ ((packed)); enum buf_mode { BUF_SINGLE, BUF_DOUBLE } __attribute__ ((packed)); @@ -1119,11 +1111,12 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, c_size = size - 3; if (cfg->mode == BUF_DOUBLE) { - if ((offset + (maxpacket << 1)) > DYN_FIFO_SIZE) + if ((offset + (maxpacket << 1)) > + (1 << (musb->config->ram_bits + 2))) return -EMSGSIZE; c_size |= MUSB_FIFOSZ_DPB; } else { - if ((offset + maxpacket) > DYN_FIFO_SIZE) + if ((offset + maxpacket) > (1 << (musb->config->ram_bits + 2))) return -EMSGSIZE; } @@ -1219,13 +1212,13 @@ static int __init ep_config_from_table(struct musb *musb) /* assert(offset > 0) */ /* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would - * be better than static MUSB_C_NUM_EPS and DYN_FIFO_SIZE... + * be better than static musb->config->num_eps and DYN_FIFO_SIZE... */ for (i = 0; i < n; i++) { u8 epn = cfg->hw_ep_num; - if (epn >= MUSB_C_NUM_EPS) { + if (epn >= musb->config->num_eps) { pr_debug("%s: invalid ep %d\n", musb_driver_name, epn); continue; @@ -1242,8 +1235,8 @@ static int __init ep_config_from_table(struct musb *musb) printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n", musb_driver_name, - n + 1, MUSB_C_NUM_EPS * 2 - 1, - offset, DYN_FIFO_SIZE); + n + 1, musb->config->num_eps * 2 - 1, + offset, (1 << (musb->config->ram_bits + 2))); #ifdef CONFIG_USB_MUSB_HDRC_HCD if (!musb->bulk_ep) { @@ -1270,7 +1263,7 @@ static int __init ep_config_from_hw(struct musb *musb) /* FIXME pick up ep0 maxpacket size */ - for (epnum = 1; epnum < MUSB_C_NUM_EPS; epnum++) { + for (epnum = 1; epnum < musb->config->num_eps; epnum++) { musb_ep_select(mbase, epnum); hw_ep = musb->endpoints + epnum; @@ -1424,14 +1417,14 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) musb->epmask = 1; if (reg & MUSB_CONFIGDATA_DYNFIFO) { - if (can_dynfifo()) + if (musb->config->dyn_fifo) status = ep_config_from_table(musb); else { ERR("reconfigure software for Dynamic FIFOs\n"); status = -ENODEV; } } else { - if (!can_dynfifo()) + if (!musb->config->dyn_fifo) status = ep_config_from_hw(musb); else { ERR("reconfigure software for static FIFOs\n"); @@ -1788,7 +1781,8 @@ static void musb_irq_work(struct work_struct *data) */ static struct musb *__init -allocate_instance(struct device *dev, void __iomem *mbase) +allocate_instance(struct device *dev, + struct musb_hdrc_config *config, void __iomem *mbase) { struct musb *musb; struct musb_hw_ep *ep; @@ -1820,8 +1814,9 @@ allocate_instance(struct device *dev, void __iomem *mbase) musb->mregs = mbase; musb->ctrl_base = mbase; musb->nIrq = -ENODEV; + musb->config = config; for (epnum = 0, ep = musb->endpoints; - epnum < MUSB_C_NUM_EPS; + epnum < musb->config->num_eps; epnum++, ep++) { ep->musb = musb; @@ -1929,7 +1924,7 @@ bad_config: } /* allocate */ - musb = allocate_instance(dev, ctrl); + musb = allocate_instance(dev, plat->config, ctrl); if (!musb) return -ENOMEM; @@ -1987,7 +1982,7 @@ bad_config: musb_generic_disable(musb); /* setup musb parts of the core (especially endpoints) */ - status = musb_core_init(plat->multipoint + status = musb_core_init(plat->config->multipoint ? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC, musb); if (status < 0) diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 90035c12ab5f..eade46d81708 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -56,18 +56,6 @@ struct musb_ep; #include "musb_debug.h" #include "musb_dma.h" -#ifdef CONFIG_USB_MUSB_SOC -/* - * Get core configuration from a header converted (by cfg_conv) - * from the Verilog config file generated by the core config utility - * - * For now we assume that header is provided along with other - * arch-specific files. Discrete chips will need a build tweak. - * So will using AHB IDs from silicon that provides them. - */ -#include -#endif - #include "musb_io.h" #include "musb_regs.h" @@ -440,6 +428,8 @@ struct musb { struct usb_gadget_driver *gadget_driver; /* its driver */ #endif + struct musb_hdrc_config *config; + #ifdef MUSB_CONFIG_PROC_FS struct proc_dir_entry *proc_entry; #endif diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index db6dad0750ae..ab8c96286ce6 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -230,173 +230,4 @@ extern u8 tusb_get_revision(struct musb *musb); #define TUSB_REV_30 0x30 #define TUSB_REV_31 0x31 -/*----------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_TUSB6010 - -/* configuration parameters specific to this silicon */ - -/* Number of Tx endpoints. Legal values are 1 - 16 (this value includes EP0) */ -#define MUSB_C_NUM_EPT 16 - -/* Number of Rx endpoints. Legal values are 1 - 16 (this value includes EP0) */ -#define MUSB_C_NUM_EPR 16 - -/* Endpoint 1 to 15 direction types. C_EP1_DEF is defined if either Tx endpoint - * 1 or Rx endpoint 1 are used. - */ -#define MUSB_C_EP1_DEF - -/* C_EP1_TX_DEF is defined if Tx endpoint 1 is used */ -#define MUSB_C_EP1_TX_DEF - -/* C_EP1_RX_DEF is defined if Rx endpoint 1 is used */ -#define MUSB_C_EP1_RX_DEF - -/* C_EP1_TOR_DEF is defined if Tx endpoint 1 and Rx endpoint 1 share a FIFO */ -/* #define C_EP1_TOR_DEF */ - -/* C_EP1_TAR_DEF is defined if both Tx endpoint 1 and Rx endpoint 1 are used - * and do not share a FIFO. - */ -#define MUSB_C_EP1_TAR_DEF - -/* Similarly for all other used endpoints */ -#define MUSB_C_EP2_DEF -#define MUSB_C_EP2_TX_DEF -#define MUSB_C_EP2_RX_DEF -#define MUSB_C_EP2_TAR_DEF -#define MUSB_C_EP3_DEF -#define MUSB_C_EP3_TX_DEF -#define MUSB_C_EP3_RX_DEF -#define MUSB_C_EP3_TAR_DEF -#define MUSB_C_EP4_DEF -#define MUSB_C_EP4_TX_DEF -#define MUSB_C_EP4_RX_DEF -#define MUSB_C_EP4_TAR_DEF - -/* Endpoint 1 to 15 FIFO address bits. Legal values are 3 to 13 - corresponding - * to FIFO sizes of 8 to 8192 bytes. If an Tx endpoint shares a FIFO with an Rx - * endpoint then the Rx FIFO size must be the same as the Tx FIFO size. All - * endpoints 1 to 15 must be defined, unused endpoints should be set to 2. - */ -#define MUSB_C_EP1T_BITS 5 -#define MUSB_C_EP1R_BITS 5 -#define MUSB_C_EP2T_BITS 5 -#define MUSB_C_EP2R_BITS 5 -#define MUSB_C_EP3T_BITS 3 -#define MUSB_C_EP3R_BITS 3 -#define MUSB_C_EP4T_BITS 3 -#define MUSB_C_EP4R_BITS 3 - -#define MUSB_C_EP5T_BITS 2 -#define MUSB_C_EP5R_BITS 2 -#define MUSB_C_EP6T_BITS 2 -#define MUSB_C_EP6R_BITS 2 -#define MUSB_C_EP7T_BITS 2 -#define MUSB_C_EP7R_BITS 2 -#define MUSB_C_EP8T_BITS 2 -#define MUSB_C_EP8R_BITS 2 -#define MUSB_C_EP9T_BITS 2 -#define MUSB_C_EP9R_BITS 2 -#define MUSB_C_EP10T_BITS 2 -#define MUSB_C_EP10R_BITS 2 -#define MUSB_C_EP11T_BITS 2 -#define MUSB_C_EP11R_BITS 2 -#define MUSB_C_EP12T_BITS 2 -#define MUSB_C_EP12R_BITS 2 -#define MUSB_C_EP13T_BITS 2 -#define MUSB_C_EP13R_BITS 2 -#define MUSB_C_EP14T_BITS 2 -#define MUSB_C_EP14R_BITS 2 -#define MUSB_C_EP15T_BITS 2 -#define MUSB_C_EP15R_BITS 2 - -/* Define the following constant if the USB2.0 Transceiver Macrocell data width - * is 16-bits. - */ -/* #define C_UTM_16 */ - -/* Define this constant if the CPU uses big-endian byte ordering. */ -/* #define C_BIGEND */ - -/* Define the following constant if any Tx endpoint is required to support - * multiple bulk packets. - */ -/* #define C_MP_TX */ - -/* Define the following constant if any Rx endpoint is required to support - * multiple bulk packets. - */ -/* #define C_MP_RX */ - -/* Define the following constant if any Tx endpoint is required to support high - * bandwidth ISO. - */ -/* #define C_HB_TX */ - -/* Define the following constant if any Rx endpoint is required to support high - * bandwidth ISO. - */ -/* #define C_HB_RX */ - -/* Define the following constant if software connect/disconnect control is - * required. - */ -#define MUSB_C_SOFT_CON - -/* Define the following constant if Vendor Control Registers are required. */ -/* #define C_VEND_REG */ - -/* Vendor control register widths. */ -#define MUSB_C_VCTL_BITS 4 -#define MUSB_C_VSTAT_BITS 8 - -/* Define the following constant to include a DMA controller. */ -/* #define C_DMA */ - -/* Define the following constant if 2 or more DMA channels are required. */ -/* #define C_DMA2 */ - -/* Define the following constant if 3 or more DMA channels are required. */ -/* #define C_DMA3 */ - -/* Define the following constant if 4 or more DMA channels are required. */ -/* #define C_DMA4 */ - -/* Define the following constant if 5 or more DMA channels are required. */ -/* #define C_DMA5 */ - -/* Define the following constant if 6 or more DMA channels are required. */ -/* #define C_DMA6 */ - -/* Define the following constant if 7 or more DMA channels are required. */ -/* #define C_DMA7 */ - -/* Define the following constant if 8 or more DMA channels are required. */ -/* #define C_DMA8 */ - -/* Enable Dynamic FIFO Sizing */ -#define MUSB_C_DYNFIFO_DEF - -/* Derived constants. The following constants are derived from the previous - * configuration constants - */ - -/* Total number of endpoints. Legal values are 2 - 16. This must be equal to - * the larger of C_NUM_EPT, C_NUM_EPR - */ -/* #define MUSB_C_NUM_EPS 5 */ - -/* C_EPMAX_BITS is equal to the largest endpoint FIFO word address bits */ -#define MUSB_C_EPMAX_BITS 11 - -/* C_RAM_BITS is the number of address bits required to address the RAM (32-bit - * addresses). It is defined as log2 of the sum of 2** of all the endpoint FIFO - * dword address bits (rounded up). - */ -#define MUSB_C_RAM_BITS 12 - -#endif /* CONFIG_USB_TUSB6010 */ - #endif /* __TUSB6010_H__ */ diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index d325a0d5bf4f..630962c04ca4 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -19,6 +19,36 @@ enum musb_mode { struct clk; +struct musb_hdrc_eps_bits { + const char name[16]; + u8 bits; +}; + +struct musb_hdrc_config { + /* MUSB configuration-specific details */ + unsigned multipoint:1; /* multipoint device */ + unsigned dyn_fifo:1; /* supports dynamic fifo sizing */ + unsigned soft_con:1; /* soft connect required */ + unsigned utm_16:1; /* utm data witdh is 16 bits */ + unsigned big_endian:1; /* true if CPU uses big-endian */ + unsigned mult_bulk_tx:1; /* Tx ep required for multbulk pkts */ + unsigned mult_bulk_rx:1; /* Rx ep required for multbulk pkts */ + unsigned high_iso_tx:1; /* Tx ep required for HB iso */ + unsigned high_iso_rx:1; /* Rx ep required for HD iso */ + unsigned dma:1; /* supports DMA */ + unsigned vendor_req:1; /* vendor registers required */ + + u8 num_eps; /* number of endpoints _with_ ep0 */ + u8 dma_channels; /* number of dma channels */ + u8 dyn_fifo_size; /* dynamic size in bytes */ + u8 vendor_ctrl; /* vendor control reg width */ + u8 vendor_stat; /* vendor status reg witdh */ + u8 dma_req_chan; /* bitmask for required dma channels */ + u8 ram_bits; /* ram address size */ + + struct musb_hdrc_eps_bits *eps_bits; +}; + struct musb_hdrc_platform_data { /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */ u8 mode; @@ -38,16 +68,14 @@ struct musb_hdrc_platform_data { /* (HOST or OTG) msec/2 after VBUS on till power good */ u8 potpgt; - /* TBD: chip defaults should probably go someplace else, - * e.g. number of tx/rx endpoints, etc - */ - unsigned multipoint:1; - /* Power the device on or off */ int (*set_power)(int state); /* Turn device clock on or off */ int (*set_clock)(struct clk *clock, int is_on); + + /* MUSB configuration-specific details */ + struct musb_hdrc_config *config; }; -- cgit v1.2.3 From 5cd9c58fbe9ec92b45b27e131719af4f2bd9eb40 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 14 Aug 2008 11:37:28 +0100 Subject: security: Fix setting of PF_SUPERPRIV by __capable() Fix the setting of PF_SUPERPRIV by __capable() as it could corrupt the flags the target process if that is not the current process and it is trying to change its own flags in a different way at the same time. __capable() is using neither atomic ops nor locking to protect t->flags. This patch removes __capable() and introduces has_capability() that doesn't set PF_SUPERPRIV on the process being queried. This patch further splits security_ptrace() in two: (1) security_ptrace_may_access(). This passes judgement on whether one process may access another only (PTRACE_MODE_ATTACH for ptrace() and PTRACE_MODE_READ for /proc), and takes a pointer to the child process. current is the parent. (2) security_ptrace_traceme(). This passes judgement on PTRACE_TRACEME only, and takes only a pointer to the parent process. current is the child. In Smack and commoncap, this uses has_capability() to determine whether the parent will be permitted to use PTRACE_ATTACH if normal checks fail. This does not set PF_SUPERPRIV. Two of the instances of __capable() actually only act on current, and so have been changed to calls to capable(). Of the places that were using __capable(): (1) The OOM killer calls __capable() thrice when weighing the killability of a process. All of these now use has_capability(). (2) cap_ptrace() and smack_ptrace() were using __capable() to check to see whether the parent was allowed to trace any process. As mentioned above, these have been split. For PTRACE_ATTACH and /proc, capable() is now used, and for PTRACE_TRACEME, has_capability() is used. (3) cap_safe_nice() only ever saw current, so now uses capable(). (4) smack_setprocattr() rejected accesses to tasks other than current just after calling __capable(), so the order of these two tests have been switched and capable() is used instead. (5) In smack_file_send_sigiotask(), we need to allow privileged processes to receive SIGIO on files they're manipulating. (6) In smack_task_wait(), we let a process wait for a privileged process, whether or not the process doing the waiting is privileged. I've tested this with the LTP SELinux and syscalls testscripts. Signed-off-by: David Howells Acked-by: Serge Hallyn Acked-by: Casey Schaufler Acked-by: Andrew G. Morgan Acked-by: Al Viro Signed-off-by: James Morris --- include/linux/capability.h | 15 ++++++++++++-- include/linux/security.h | 39 +++++++++++++++++++++++------------- kernel/capability.c | 21 ++++++++++++-------- kernel/ptrace.c | 5 ++--- mm/oom_kill.c | 6 ++++-- security/capability.c | 3 ++- security/commoncap.c | 24 ++++++++++++++++------- security/root_plug.c | 3 ++- security/security.c | 10 +++++++--- security/selinux/hooks.c | 25 ++++++++++++++++------- security/smack/smack_lsm.c | 49 ++++++++++++++++++++++++++++++++-------------- 11 files changed, 137 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/include/linux/capability.h b/include/linux/capability.h index 02673846d205..9d1fe30b6f6c 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -503,8 +503,19 @@ extern const kernel_cap_t __cap_init_eff_set; kernel_cap_t cap_set_effective(const kernel_cap_t pE_new); -int capable(int cap); -int __capable(struct task_struct *t, int cap); +/** + * has_capability - Determine if a task has a superior capability available + * @t: The task in question + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect, false if not. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +#define has_capability(t, cap) (security_capable((t), (cap)) == 0) + +extern int capable(int cap); #endif /* __KERNEL__ */ diff --git a/include/linux/security.h b/include/linux/security.h index fd96e7f8a6f9..2ee5ecfb2393 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -46,8 +46,8 @@ struct audit_krule; */ extern int cap_capable(struct task_struct *tsk, int cap); extern int cap_settime(struct timespec *ts, struct timezone *tz); -extern int cap_ptrace(struct task_struct *parent, struct task_struct *child, - unsigned int mode); +extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode); +extern int cap_ptrace_traceme(struct task_struct *parent); extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); @@ -1157,17 +1157,24 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @alter contains the flag indicating whether changes are to be made. * Return 0 if permission is granted. * - * @ptrace: - * Check permission before allowing the @parent process to trace the + * @ptrace_may_access: + * Check permission before allowing the current process to trace the * @child process. * Security modules may also want to perform a process tracing check * during an execve in the set_security or apply_creds hooks of * binprm_security_ops if the process is being traced and its security * attributes would be changed by the execve. - * @parent contains the task_struct structure for parent process. - * @child contains the task_struct structure for child process. + * @child contains the task_struct structure for the target process. * @mode contains the PTRACE_MODE flags indicating the form of access. * Return 0 if permission is granted. + * @ptrace_traceme: + * Check that the @parent process has sufficient permission to trace the + * current process before allowing the current process to present itself + * to the @parent process for tracing. + * The parent process will still have to undergo the ptrace_may_access + * checks before it is allowed to trace this one. + * @parent contains the task_struct structure for debugger process. + * Return 0 if permission is granted. * @capget: * Get the @effective, @inheritable, and @permitted capability sets for * the @target process. The hook may also perform permission checking to @@ -1287,8 +1294,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) struct security_operations { char name[SECURITY_NAME_MAX + 1]; - int (*ptrace) (struct task_struct *parent, struct task_struct *child, - unsigned int mode); + int (*ptrace_may_access) (struct task_struct *child, unsigned int mode); + int (*ptrace_traceme) (struct task_struct *parent); int (*capget) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); @@ -1560,8 +1567,8 @@ extern struct dentry *securityfs_create_dir(const char *name, struct dentry *par extern void securityfs_remove(struct dentry *dentry); /* Security operations */ -int security_ptrace(struct task_struct *parent, struct task_struct *child, - unsigned int mode); +int security_ptrace_may_access(struct task_struct *child, unsigned int mode); +int security_ptrace_traceme(struct task_struct *parent); int security_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, @@ -1742,11 +1749,15 @@ static inline int security_init(void) return 0; } -static inline int security_ptrace(struct task_struct *parent, - struct task_struct *child, - unsigned int mode) +static inline int security_ptrace_may_access(struct task_struct *child, + unsigned int mode) +{ + return cap_ptrace_may_access(child, mode); +} + +static inline int security_ptrace_traceme(struct task_struct *child) { - return cap_ptrace(parent, child, mode); + return cap_ptrace_traceme(parent); } static inline int security_capget(struct task_struct *target, diff --git a/kernel/capability.c b/kernel/capability.c index 0101e847603e..33e51e78c2d8 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -486,17 +486,22 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) return ret; } -int __capable(struct task_struct *t, int cap) +/** + * capable - Determine if the current task has a superior capability in effect + * @cap: The capability to be tested for + * + * Return true if the current task has the given superior capability currently + * available for use, false if not. + * + * This sets PF_SUPERPRIV on the task if the capability is available on the + * assumption that it's about to be used. + */ +int capable(int cap) { - if (security_capable(t, cap) == 0) { - t->flags |= PF_SUPERPRIV; + if (has_capability(current, cap)) { + current->flags |= PF_SUPERPRIV; return 1; } return 0; } - -int capable(int cap) -{ - return __capable(current, cap); -} EXPORT_SYMBOL(capable); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 082b3fcb32a0..356699a96d56 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -140,7 +140,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode) if (!dumpable && !capable(CAP_SYS_PTRACE)) return -EPERM; - return security_ptrace(current, task, mode); + return security_ptrace_may_access(task, mode); } bool ptrace_may_access(struct task_struct *task, unsigned int mode) @@ -499,8 +499,7 @@ repeat: goto repeat; } - ret = security_ptrace(current->parent, current, - PTRACE_MODE_ATTACH); + ret = security_ptrace_traceme(current->parent); /* * Set the ptrace bit in the process ptrace flags. diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 8a5467ee6265..64e5b4bcd964 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -26,6 +26,7 @@ #include #include #include +#include int sysctl_panic_on_oom; int sysctl_oom_kill_allocating_task; @@ -128,7 +129,8 @@ unsigned long badness(struct task_struct *p, unsigned long uptime) * Superuser processes are usually more important, so we make it * less likely that we kill those. */ - if (__capable(p, CAP_SYS_ADMIN) || __capable(p, CAP_SYS_RESOURCE)) + if (has_capability(p, CAP_SYS_ADMIN) || + has_capability(p, CAP_SYS_RESOURCE)) points /= 4; /* @@ -137,7 +139,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime) * tend to only have this flag set on applications they think * of as important. */ - if (__capable(p, CAP_SYS_RAWIO)) + if (has_capability(p, CAP_SYS_RAWIO)) points /= 4; /* diff --git a/security/capability.c b/security/capability.c index 63d10da515a5..245874819036 100644 --- a/security/capability.c +++ b/security/capability.c @@ -811,7 +811,8 @@ struct security_operations default_security_ops = { void security_fixup_ops(struct security_operations *ops) { - set_to_cap_if_null(ops, ptrace); + set_to_cap_if_null(ops, ptrace_may_access); + set_to_cap_if_null(ops, ptrace_traceme); set_to_cap_if_null(ops, capget); set_to_cap_if_null(ops, capset_check); set_to_cap_if_null(ops, capset_set); diff --git a/security/commoncap.c b/security/commoncap.c index 4afbece37a08..e4c4b3fc0c04 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -63,14 +63,24 @@ int cap_settime(struct timespec *ts, struct timezone *tz) return 0; } -int cap_ptrace (struct task_struct *parent, struct task_struct *child, - unsigned int mode) +int cap_ptrace_may_access(struct task_struct *child, unsigned int mode) { /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ - if (!cap_issubset(child->cap_permitted, parent->cap_permitted) && - !__capable(parent, CAP_SYS_PTRACE)) - return -EPERM; - return 0; + if (cap_issubset(child->cap_permitted, current->cap_permitted)) + return 0; + if (capable(CAP_SYS_PTRACE)) + return 0; + return -EPERM; +} + +int cap_ptrace_traceme(struct task_struct *parent) +{ + /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ + if (cap_issubset(current->cap_permitted, parent->cap_permitted)) + return 0; + if (has_capability(parent, CAP_SYS_PTRACE)) + return 0; + return -EPERM; } int cap_capget (struct task_struct *target, kernel_cap_t *effective, @@ -534,7 +544,7 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, static inline int cap_safe_nice(struct task_struct *p) { if (!cap_issubset(p->cap_permitted, current->cap_permitted) && - !__capable(current, CAP_SYS_NICE)) + !capable(CAP_SYS_NICE)) return -EPERM; return 0; } diff --git a/security/root_plug.c b/security/root_plug.c index be0ebec2580b..c3f68b5b372d 100644 --- a/security/root_plug.c +++ b/security/root_plug.c @@ -72,7 +72,8 @@ static int rootplug_bprm_check_security (struct linux_binprm *bprm) static struct security_operations rootplug_security_ops = { /* Use the capability functions for some of the hooks */ - .ptrace = cap_ptrace, + .ptrace_may_access = cap_ptrace_may_access, + .ptrace_traceme = cap_ptrace_traceme, .capget = cap_capget, .capset_check = cap_capset_check, .capset_set = cap_capset_set, diff --git a/security/security.c b/security/security.c index ff7068727757..3a4b4f55b33f 100644 --- a/security/security.c +++ b/security/security.c @@ -127,10 +127,14 @@ int register_security(struct security_operations *ops) /* Security operations */ -int security_ptrace(struct task_struct *parent, struct task_struct *child, - unsigned int mode) +int security_ptrace_may_access(struct task_struct *child, unsigned int mode) { - return security_ops->ptrace(parent, child, mode); + return security_ops->ptrace_may_access(child, mode); +} + +int security_ptrace_traceme(struct task_struct *parent) +{ + return security_ops->ptrace_traceme(parent); } int security_capget(struct task_struct *target, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3ae9bec5a508..03fc6a81ae32 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1738,24 +1738,34 @@ static inline u32 file_to_av(struct file *file) /* Hook functions begin here. */ -static int selinux_ptrace(struct task_struct *parent, - struct task_struct *child, - unsigned int mode) +static int selinux_ptrace_may_access(struct task_struct *child, + unsigned int mode) { int rc; - rc = secondary_ops->ptrace(parent, child, mode); + rc = secondary_ops->ptrace_may_access(child, mode); if (rc) return rc; if (mode == PTRACE_MODE_READ) { - struct task_security_struct *tsec = parent->security; + struct task_security_struct *tsec = current->security; struct task_security_struct *csec = child->security; return avc_has_perm(tsec->sid, csec->sid, SECCLASS_FILE, FILE__READ, NULL); } - return task_has_perm(parent, child, PROCESS__PTRACE); + return task_has_perm(current, child, PROCESS__PTRACE); +} + +static int selinux_ptrace_traceme(struct task_struct *parent) +{ + int rc; + + rc = secondary_ops->ptrace_traceme(parent); + if (rc) + return rc; + + return task_has_perm(parent, current, PROCESS__PTRACE); } static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, @@ -5346,7 +5356,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) static struct security_operations selinux_ops = { .name = "selinux", - .ptrace = selinux_ptrace, + .ptrace_may_access = selinux_ptrace_may_access, + .ptrace_traceme = selinux_ptrace_traceme, .capget = selinux_capget, .capset_check = selinux_capset_check, .capset_set = selinux_capset_set, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 1b40e558f983..87d75417ea93 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -87,27 +87,46 @@ struct inode_smack *new_inode_smack(char *smack) */ /** - * smack_ptrace - Smack approval on ptrace - * @ptp: parent task pointer + * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH * @ctp: child task pointer * * Returns 0 if access is OK, an error code otherwise * * Do the capability checks, and require read and write. */ -static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp, - unsigned int mode) +static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode) { int rc; - rc = cap_ptrace(ptp, ctp, mode); + rc = cap_ptrace_may_access(ctp, mode); if (rc != 0) return rc; - rc = smk_access(ptp->security, ctp->security, MAY_READWRITE); - if (rc != 0 && __capable(ptp, CAP_MAC_OVERRIDE)) + rc = smk_access(current->security, ctp->security, MAY_READWRITE); + if (rc != 0 && capable(CAP_MAC_OVERRIDE)) return 0; + return rc; +} + +/** + * smack_ptrace_traceme - Smack approval on PTRACE_TRACEME + * @ptp: parent task pointer + * + * Returns 0 if access is OK, an error code otherwise + * + * Do the capability checks, and require read and write. + */ +static int smack_ptrace_traceme(struct task_struct *ptp) +{ + int rc; + + rc = cap_ptrace_traceme(ptp); + if (rc != 0) + return rc; + rc = smk_access(ptp->security, current->security, MAY_READWRITE); + if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE)) + return 0; return rc; } @@ -923,7 +942,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, */ file = container_of(fown, struct file, f_owner); rc = smk_access(file->f_security, tsk->security, MAY_WRITE); - if (rc != 0 && __capable(tsk, CAP_MAC_OVERRIDE)) + if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) return 0; return rc; } @@ -1164,12 +1183,12 @@ static int smack_task_wait(struct task_struct *p) * account for the smack labels having gotten to * be different in the first place. * - * This breaks the strict subjet/object access + * This breaks the strict subject/object access * control ideal, taking the object's privilege * state into account in the decision as well as * the smack value. */ - if (capable(CAP_MAC_OVERRIDE) || __capable(p, CAP_MAC_OVERRIDE)) + if (capable(CAP_MAC_OVERRIDE) || has_capability(p, CAP_MAC_OVERRIDE)) return 0; return rc; @@ -2016,9 +2035,6 @@ static int smack_setprocattr(struct task_struct *p, char *name, { char *newsmack; - if (!__capable(p, CAP_MAC_ADMIN)) - return -EPERM; - /* * Changing another process' Smack value is too dangerous * and supports no sane use case. @@ -2026,6 +2042,9 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (p != current) return -EPERM; + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + if (value == NULL || size == 0 || size >= SMK_LABELLEN) return -EINVAL; @@ -2552,7 +2571,8 @@ static void smack_release_secctx(char *secdata, u32 seclen) struct security_operations smack_ops = { .name = "smack", - .ptrace = smack_ptrace, + .ptrace_may_access = smack_ptrace_may_access, + .ptrace_traceme = smack_ptrace_traceme, .capget = cap_capget, .capset_check = cap_capset_check, .capset_set = cap_capset_set, @@ -2729,4 +2749,3 @@ static __init int smack_init(void) * all processes and objects when they are created. */ security_initcall(smack_init); - -- cgit v1.2.3 From c1bc667e844c2677cdf927102ab384fe7b033762 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Thu, 7 Aug 2008 16:43:38 +0200 Subject: IPVS: Add genetlink interface definitions to ip_vs.h Add IPVS Generic Netlink interface definitions to include/linux/ip_vs.h. Signed-off-by: Julius Volz Signed-off-by: Simon Horman --- include/linux/ip_vs.h | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ip_vs.h b/include/linux/ip_vs.h index ec6eb49af2d8..0f434a28fb58 100644 --- a/include/linux/ip_vs.h +++ b/include/linux/ip_vs.h @@ -242,4 +242,164 @@ struct ip_vs_daemon_user { int syncid; }; +/* + * + * IPVS Generic Netlink interface definitions + * + */ + +/* Generic Netlink family info */ + +#define IPVS_GENL_NAME "IPVS" +#define IPVS_GENL_VERSION 0x1 + +struct ip_vs_flags { + __be32 flags; + __be32 mask; +}; + +/* Generic Netlink command attributes */ +enum { + IPVS_CMD_UNSPEC = 0, + + IPVS_CMD_NEW_SERVICE, /* add service */ + IPVS_CMD_SET_SERVICE, /* modify service */ + IPVS_CMD_DEL_SERVICE, /* delete service */ + IPVS_CMD_GET_SERVICE, /* get service info */ + + IPVS_CMD_NEW_DEST, /* add destination */ + IPVS_CMD_SET_DEST, /* modify destination */ + IPVS_CMD_DEL_DEST, /* delete destination */ + IPVS_CMD_GET_DEST, /* get destination info */ + + IPVS_CMD_NEW_DAEMON, /* start sync daemon */ + IPVS_CMD_DEL_DAEMON, /* stop sync daemon */ + IPVS_CMD_GET_DAEMON, /* get sync daemon status */ + + IPVS_CMD_SET_CONFIG, /* set config settings */ + IPVS_CMD_GET_CONFIG, /* get config settings */ + + IPVS_CMD_SET_INFO, /* only used in GET_INFO reply */ + IPVS_CMD_GET_INFO, /* get general IPVS info */ + + IPVS_CMD_ZERO, /* zero all counters and stats */ + IPVS_CMD_FLUSH, /* flush services and dests */ + + __IPVS_CMD_MAX, +}; + +#define IPVS_CMD_MAX (__IPVS_CMD_MAX - 1) + +/* Attributes used in the first level of commands */ +enum { + IPVS_CMD_ATTR_UNSPEC = 0, + IPVS_CMD_ATTR_SERVICE, /* nested service attribute */ + IPVS_CMD_ATTR_DEST, /* nested destination attribute */ + IPVS_CMD_ATTR_DAEMON, /* nested sync daemon attribute */ + IPVS_CMD_ATTR_TIMEOUT_TCP, /* TCP connection timeout */ + IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, /* TCP FIN wait timeout */ + IPVS_CMD_ATTR_TIMEOUT_UDP, /* UDP timeout */ + __IPVS_CMD_ATTR_MAX, +}; + +#define IPVS_CMD_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1) + +/* + * Attributes used to describe a service + * + * Used inside nested attribute IPVS_CMD_ATTR_SERVICE + */ +enum { + IPVS_SVC_ATTR_UNSPEC = 0, + IPVS_SVC_ATTR_AF, /* address family */ + IPVS_SVC_ATTR_PROTOCOL, /* virtual service protocol */ + IPVS_SVC_ATTR_ADDR, /* virtual service address */ + IPVS_SVC_ATTR_PORT, /* virtual service port */ + IPVS_SVC_ATTR_FWMARK, /* firewall mark of service */ + + IPVS_SVC_ATTR_SCHED_NAME, /* name of scheduler */ + IPVS_SVC_ATTR_FLAGS, /* virtual service flags */ + IPVS_SVC_ATTR_TIMEOUT, /* persistent timeout */ + IPVS_SVC_ATTR_NETMASK, /* persistent netmask */ + + IPVS_SVC_ATTR_STATS, /* nested attribute for service stats */ + __IPVS_SVC_ATTR_MAX, +}; + +#define IPVS_SVC_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1) + +/* + * Attributes used to describe a destination (real server) + * + * Used inside nested attribute IPVS_CMD_ATTR_DEST + */ +enum { + IPVS_DEST_ATTR_UNSPEC = 0, + IPVS_DEST_ATTR_ADDR, /* real server address */ + IPVS_DEST_ATTR_PORT, /* real server port */ + + IPVS_DEST_ATTR_FWD_METHOD, /* forwarding method */ + IPVS_DEST_ATTR_WEIGHT, /* destination weight */ + + IPVS_DEST_ATTR_U_THRESH, /* upper threshold */ + IPVS_DEST_ATTR_L_THRESH, /* lower threshold */ + + IPVS_DEST_ATTR_ACTIVE_CONNS, /* active connections */ + IPVS_DEST_ATTR_INACT_CONNS, /* inactive connections */ + IPVS_DEST_ATTR_PERSIST_CONNS, /* persistent connections */ + + IPVS_DEST_ATTR_STATS, /* nested attribute for dest stats */ + __IPVS_DEST_ATTR_MAX, +}; + +#define IPVS_DEST_ATTR_MAX (__IPVS_DEST_ATTR_MAX - 1) + +/* + * Attributes describing a sync daemon + * + * Used inside nested attribute IPVS_CMD_ATTR_DAEMON + */ +enum { + IPVS_DAEMON_ATTR_UNSPEC = 0, + IPVS_DAEMON_ATTR_STATE, /* sync daemon state (master/backup) */ + IPVS_DAEMON_ATTR_MCAST_IFN, /* multicast interface name */ + IPVS_DAEMON_ATTR_SYNC_ID, /* SyncID we belong to */ + __IPVS_DAEMON_ATTR_MAX, +}; + +#define IPVS_DAEMON_ATTR_MAX (__IPVS_DAEMON_ATTR_MAX - 1) + +/* + * Attributes used to describe service or destination entry statistics + * + * Used inside nested attributes IPVS_SVC_ATTR_STATS and IPVS_DEST_ATTR_STATS + */ +enum { + IPVS_STATS_ATTR_UNSPEC = 0, + IPVS_STATS_ATTR_CONNS, /* connections scheduled */ + IPVS_STATS_ATTR_INPKTS, /* incoming packets */ + IPVS_STATS_ATTR_OUTPKTS, /* outgoing packets */ + IPVS_STATS_ATTR_INBYTES, /* incoming bytes */ + IPVS_STATS_ATTR_OUTBYTES, /* outgoing bytes */ + + IPVS_STATS_ATTR_CPS, /* current connection rate */ + IPVS_STATS_ATTR_INPPS, /* current in packet rate */ + IPVS_STATS_ATTR_OUTPPS, /* current out packet rate */ + IPVS_STATS_ATTR_INBPS, /* current in byte rate */ + IPVS_STATS_ATTR_OUTBPS, /* current out byte rate */ + __IPVS_STATS_ATTR_MAX, +}; + +#define IPVS_STATS_ATTR_MAX (__IPVS_STATS_ATTR_MAX - 1) + +/* Attributes used in response to IPVS_CMD_GET_INFO command */ +enum { + IPVS_INFO_ATTR_UNSPEC = 0, + IPVS_INFO_ATTR_VERSION, /* IPVS version number */ + IPVS_INFO_ATTR_CONN_TAB_SIZE, /* size of connection hash table */ + __IPVS_INFO_ATTR_MAX, +}; + +#define IPVS_INFO_ATTR_MAX (__IPVS_INFO_ATTR_MAX - 1) + #endif /* _IP_VS_H */ -- cgit v1.2.3 From ff9cf2ce7afe76435d66c898cc9dacaa68e79d41 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 1 Aug 2008 14:10:02 -0700 Subject: rcu: fixes to include/linux/rcupreempt.h Hello! Compared tip/core/rcu to my latest patchset, and found the following issues: o the memory barrier in rcu_exit_nohz() somehow got out of place (it is correct in mainline as of 2.6.26-rc7). o There is a duplicate declaration of rcu_dyntick_sched. The attached patch fixes these. Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar --- include/linux/rcupreempt.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index 0967f03b0705..addb5e282f39 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h @@ -111,7 +111,6 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu); struct softirq_action; #ifdef CONFIG_NO_HZ -DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); static inline void rcu_enter_nohz(void) { @@ -126,8 +125,8 @@ static inline void rcu_exit_nohz(void) { static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); - smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ __get_cpu_var(rcu_dyntick_sched).dynticks++; + smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ WARN_ON_RATELIMIT(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1), &rs); } -- cgit v1.2.3 From 34d7c2b38d124219b7034356716e3455c439acd3 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 1 Aug 2008 14:11:05 -0700 Subject: rcu: remove list_for_each_rcu() All of the in-tree uses of list_for_each_rcu() have been converted to list_for_each_entry_rcu(), so list_for_each_rcu() can now be removed. Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar --- Documentation/RCU/checklist.txt | 2 +- Documentation/RCU/whatisRCU.txt | 2 -- include/linux/rculist.h | 14 -------------- 3 files changed, 1 insertion(+), 17 deletions(-) (limited to 'include/linux') diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index cf5562cbe356..6e253407b3dc 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -210,7 +210,7 @@ over a rather long period of time, but improvements are always welcome! number of updates per grace period. 9. All RCU list-traversal primitives, which include - rcu_dereference(), list_for_each_rcu(), list_for_each_entry_rcu(), + rcu_dereference(), list_for_each_entry_rcu(), list_for_each_continue_rcu(), and list_for_each_safe_rcu(), must be either within an RCU read-side critical section or must be protected by appropriate update-side locks. RCU diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index e04d643a9f57..96170824a717 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -786,8 +786,6 @@ RCU pointer/list traversal: list_for_each_entry_rcu hlist_for_each_entry_rcu - list_for_each_rcu (to be deprecated in favor of - list_for_each_entry_rcu) list_for_each_continue_rcu (to be deprecated in favor of new list_for_each_entry_continue_rcu) diff --git a/include/linux/rculist.h b/include/linux/rculist.h index eb4443c7e05b..e649bd3f2c97 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -198,20 +198,6 @@ static inline void list_splice_init_rcu(struct list_head *list, at->prev = last; } -/** - * list_for_each_rcu - iterate over an rcu-protected list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - * - * This list-traversal primitive may safely run concurrently with - * the _rcu list-mutation primitives such as list_add_rcu() - * as long as the traversal is guarded by rcu_read_lock(). - */ -#define list_for_each_rcu(pos, head) \ - for (pos = rcu_dereference((head)->next); \ - prefetch(pos->next), pos != (head); \ - pos = rcu_dereference(pos->next)) - #define __list_for_each_rcu(pos, head) \ for (pos = rcu_dereference((head)->next); \ pos != (head); \ -- cgit v1.2.3 From 07dd20e0324f4d3e33bde1944d4f7771a09c498c Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 1 Aug 2008 13:18:04 +0100 Subject: sched: reorder signal_struct to remove 8 bytes on 64 bit builds reorder structure to remove 8 bytes of padding on 64 bit builds Signed-off-by: Richard Kennedy Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index cfb0d87b99fc..5a8058e44f58 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -451,8 +451,8 @@ struct signal_struct { * - everyone except group_exit_task is stopped during signal delivery * of fatal signals, group_exit_task processes the signal. */ - struct task_struct *group_exit_task; int notify_count; + struct task_struct *group_exit_task; /* thread group stop support, overloads group_exit_code too */ int group_stop_count; -- cgit v1.2.3 From bee367ed066e26c14263d808136fba8eec3bd70a Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 1 Aug 2008 13:24:08 +0100 Subject: sched: reorder struct sched_rt_entity to remove padding on 64 bit builds remove 8 bytes of padding on 64 bit builds (also removes 8 bytes from task_struct) Signed-off-by: Richard Kennedy Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5a8058e44f58..08a87b5f29e1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1010,8 +1010,8 @@ struct sched_entity { struct sched_rt_entity { struct list_head run_list; - unsigned int time_slice; unsigned long timeout; + unsigned int time_slice; int nr_cpus_allowed; struct sched_rt_entity *back; -- cgit v1.2.3 From 3fb669dd6ec11e14819c0114a0e68a9ddcec65e1 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 1 Aug 2008 13:36:28 +0100 Subject: reorder struct prop_local_single to remove padding on 64 bit builds reorder structure to remove 8 bytes of padding on 64 bit builds (also removes 8 bytes from task_struct) Signed-off-by: Richard Kennedy Cc: peterz@infradead.org Signed-off-by: Ingo Molnar --- include/linux/proportions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/proportions.h b/include/linux/proportions.h index 5afc1b23346d..cf793bbbd05e 100644 --- a/include/linux/proportions.h +++ b/include/linux/proportions.h @@ -104,8 +104,8 @@ struct prop_local_single { * snapshot of the last seen global state * and a lock protecting this state */ - int shift; unsigned long period; + int shift; spinlock_t lock; /* protect the snapshot state */ }; -- cgit v1.2.3 From ce289e89726948b50a58c9e8f4e81174a8c9c254 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Fri, 15 Aug 2008 00:40:19 -0700 Subject: suspend: fix section mismatch warning - register_nosave_region WARNING: vmlinux.o(.text+0xe684): Section mismatch in reference from the function register_nosave_region() to the function .init.text:__register_nosave_region() The function register_nosave_region() references the function __init __register_nosave_region(). This is often because register_nosave_region lacks a __init annotation or the annotation of __register_nosave_region is wrong. register_nosave_region calls __init function and is called only from __init functions Signed-off-by: Marcin Slusarz Acked-by: Rafael J. Wysocki Cc: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/suspend.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index c63435095970..2ce8207686e2 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -217,11 +217,11 @@ struct platform_hibernation_ops { #ifdef CONFIG_HIBERNATION /* kernel/power/snapshot.c */ extern void __register_nosave_region(unsigned long b, unsigned long e, int km); -static inline void register_nosave_region(unsigned long b, unsigned long e) +static inline void __init register_nosave_region(unsigned long b, unsigned long e) { __register_nosave_region(b, e, 0); } -static inline void register_nosave_region_late(unsigned long b, unsigned long e) +static inline void __init register_nosave_region_late(unsigned long b, unsigned long e) { __register_nosave_region(b, e, 1); } -- cgit v1.2.3 From 163f6876f5c3ff8215e900b93779e960a56b3694 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 15 Aug 2008 00:40:22 -0700 Subject: kexec jump: rename KEXEC_CONTROL_CODE_SIZE to KEXEC_CONTROL_PAGE_SIZE Rename KEXEC_CONTROL_CODE_SIZE to KEXEC_CONTROL_PAGE_SIZE, because control page is used for not only code on some platform. For example in kexec jump, it is used for data and stack too. [akpm@linux-foundation.org: unbreak powerpc and arm, finish conversion] Signed-off-by: Huang Ying Cc: Pavel Machek Cc: "Rafael J. Wysocki" Cc: "Eric W. Biederman" Cc: Vivek Goyal Cc: Ingo Molnar Cc: Russell King Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/include/asm/kexec.h | 2 +- arch/arm/kernel/machine_kexec.c | 2 +- arch/ia64/include/asm/kexec.h | 2 +- arch/powerpc/include/asm/kexec.h | 2 +- arch/powerpc/kernel/machine_kexec_32.c | 2 +- arch/s390/include/asm/kexec.h | 2 +- arch/sh/include/asm/kexec.h | 2 +- arch/x86/kernel/machine_kexec_32.c | 2 +- include/asm-mips/kexec.h | 2 +- include/asm-x86/kexec.h | 4 ++-- include/linux/kexec.h | 4 ++-- kernel/kexec.c | 6 +++--- 12 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/include/asm/kexec.h b/arch/arm/include/asm/kexec.h index c8986bb99ed5..df15a0dc228e 100644 --- a/arch/arm/include/asm/kexec.h +++ b/arch/arm/include/asm/kexec.h @@ -10,7 +10,7 @@ /* Maximum address we can use for the control code buffer */ #define KEXEC_CONTROL_MEMORY_LIMIT (-1UL) -#define KEXEC_CONTROL_CODE_SIZE 4096 +#define KEXEC_CONTROL_PAGE_SIZE 4096 #define KEXEC_ARCH KEXEC_ARCH_ARM diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index db8f54a3451f..fae5beb3c3d6 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -71,7 +71,7 @@ void machine_kexec(struct kimage *image) flush_icache_range((unsigned long) reboot_code_buffer, - (unsigned long) reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); + (unsigned long) reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE); printk(KERN_INFO "Bye!\n"); cpu_proc_fin(); diff --git a/arch/ia64/include/asm/kexec.h b/arch/ia64/include/asm/kexec.h index 541be835fc5a..e1d58f819d78 100644 --- a/arch/ia64/include/asm/kexec.h +++ b/arch/ia64/include/asm/kexec.h @@ -9,7 +9,7 @@ /* Maximum address we can use for the control code buffer */ #define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE -#define KEXEC_CONTROL_CODE_SIZE (8192 + 8192 + 4096) +#define KEXEC_CONTROL_PAGE_SIZE (8192 + 8192 + 4096) /* The native architecture */ #define KEXEC_ARCH KEXEC_ARCH_IA_64 diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index acdcdc66f1b6..3736d9b33289 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -22,7 +22,7 @@ #define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE #endif -#define KEXEC_CONTROL_CODE_SIZE 4096 +#define KEXEC_CONTROL_PAGE_SIZE 4096 /* The native architecture */ #ifdef __powerpc64__ diff --git a/arch/powerpc/kernel/machine_kexec_32.c b/arch/powerpc/kernel/machine_kexec_32.c index cbaa34196797..ae63a964b858 100644 --- a/arch/powerpc/kernel/machine_kexec_32.c +++ b/arch/powerpc/kernel/machine_kexec_32.c @@ -51,7 +51,7 @@ void default_machine_kexec(struct kimage *image) relocate_new_kernel_size); flush_icache_range(reboot_code_buffer, - reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); + reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE); printk(KERN_INFO "Bye!\n"); /* now call it */ diff --git a/arch/s390/include/asm/kexec.h b/arch/s390/include/asm/kexec.h index f219c6411e0b..bb729b84a21e 100644 --- a/arch/s390/include/asm/kexec.h +++ b/arch/s390/include/asm/kexec.h @@ -31,7 +31,7 @@ #define KEXEC_CONTROL_MEMORY_LIMIT (1UL<<31) /* Allocate one page for the pdp and the second for the code */ -#define KEXEC_CONTROL_CODE_SIZE 4096 +#define KEXEC_CONTROL_PAGE_SIZE 4096 /* The native architecture */ #define KEXEC_ARCH KEXEC_ARCH_S390 diff --git a/arch/sh/include/asm/kexec.h b/arch/sh/include/asm/kexec.h index 00f4260ef09b..765a5e1660fc 100644 --- a/arch/sh/include/asm/kexec.h +++ b/arch/sh/include/asm/kexec.h @@ -21,7 +21,7 @@ /* Maximum address we can use for the control code buffer */ #define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE -#define KEXEC_CONTROL_CODE_SIZE 4096 +#define KEXEC_CONTROL_PAGE_SIZE 4096 /* The native architecture */ #define KEXEC_ARCH KEXEC_ARCH_SH diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index 9fe478d98406..466450167dea 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -78,7 +78,7 @@ static void load_segments(void) /* * A architecture hook called to validate the * proposed image and prepare the control pages - * as needed. The pages for KEXEC_CONTROL_CODE_SIZE + * as needed. The pages for KEXEC_CONTROL_PAGE_SIZE * have been allocated, but the segments have yet * been copied into the kernel. * diff --git a/include/asm-mips/kexec.h b/include/asm-mips/kexec.h index cdbab43b7d3a..4314892aaebb 100644 --- a/include/asm-mips/kexec.h +++ b/include/asm-mips/kexec.h @@ -16,7 +16,7 @@ /* Maximum address we can use for the control code buffer */ #define KEXEC_CONTROL_MEMORY_LIMIT (0x20000000) -#define KEXEC_CONTROL_CODE_SIZE 4096 +#define KEXEC_CONTROL_PAGE_SIZE 4096 /* The native architecture */ #define KEXEC_ARCH KEXEC_ARCH_MIPS diff --git a/include/asm-x86/kexec.h b/include/asm-x86/kexec.h index c0e52a14fd4d..f6fb3d21883c 100644 --- a/include/asm-x86/kexec.h +++ b/include/asm-x86/kexec.h @@ -63,7 +63,7 @@ /* Maximum address we can use for the control code buffer */ # define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE -# define KEXEC_CONTROL_CODE_SIZE 4096 +# define KEXEC_CONTROL_PAGE_SIZE 4096 /* The native architecture */ # define KEXEC_ARCH KEXEC_ARCH_386 @@ -79,7 +79,7 @@ # define KEXEC_CONTROL_MEMORY_LIMIT (0xFFFFFFFFFFUL) /* Allocate one page for the pdp and the second for the code */ -# define KEXEC_CONTROL_CODE_SIZE (4096UL + 4096UL) +# define KEXEC_CONTROL_PAGE_SIZE (4096UL + 4096UL) /* The native architecture */ # define KEXEC_ARCH KEXEC_ARCH_X86_64 diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 32110cede64f..17f76fc05173 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -25,8 +25,8 @@ #error KEXEC_CONTROL_MEMORY_LIMIT not defined #endif -#ifndef KEXEC_CONTROL_CODE_SIZE -#error KEXEC_CONTROL_CODE_SIZE not defined +#ifndef KEXEC_CONTROL_PAGE_SIZE +#error KEXEC_CONTROL_PAGE_SIZE not defined #endif #ifndef KEXEC_ARCH diff --git a/kernel/kexec.c b/kernel/kexec.c index bfbbd120623c..2810558802b6 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -77,7 +77,7 @@ int kexec_should_crash(struct task_struct *p) * * The code for the transition from the current kernel to the * the new kernel is placed in the control_code_buffer, whose size - * is given by KEXEC_CONTROL_CODE_SIZE. In the best case only a single + * is given by KEXEC_CONTROL_PAGE_SIZE. In the best case only a single * page of memory is necessary, but some architectures require more. * Because this memory must be identity mapped in the transition from * virtual to physical addresses it must live in the range @@ -242,7 +242,7 @@ static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry, */ result = -ENOMEM; image->control_code_page = kimage_alloc_control_pages(image, - get_order(KEXEC_CONTROL_CODE_SIZE)); + get_order(KEXEC_CONTROL_PAGE_SIZE)); if (!image->control_code_page) { printk(KERN_ERR "Could not allocate control_code_buffer\n"); goto out; @@ -317,7 +317,7 @@ static int kimage_crash_alloc(struct kimage **rimage, unsigned long entry, */ result = -ENOMEM; image->control_code_page = kimage_alloc_control_pages(image, - get_order(KEXEC_CONTROL_CODE_SIZE)); + get_order(KEXEC_CONTROL_PAGE_SIZE)); if (!image->control_code_page) { printk(KERN_ERR "Could not allocate control_code_buffer\n"); goto out; -- cgit v1.2.3 From ca195b7f6da3d5dde0bb85a7c322d7de73352653 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 15 Aug 2008 00:40:24 -0700 Subject: kexec jump: remove duplication of kexec_restart_prepare() Call kernel_restart_prepare() in kernel_kexec() instead of duplicating the code. Signed-off-by: Huang Ying Acked-by: Pavel Machek Acked-by: Vivek Goyal Cc: Pavel Machek Cc: "Rafael J. Wysocki" Cc: "Eric W. Biederman" Cc: Vivek Goyal Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/reboot.h | 1 + kernel/kexec.c | 6 +----- kernel/sys.c | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/reboot.h b/include/linux/reboot.h index b93b541cf111..988e55fe649b 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -59,6 +59,7 @@ extern void machine_crash_shutdown(struct pt_regs *); * Architecture independent implemenations of sys_reboot commands. */ +extern void kernel_restart_prepare(char *cmd); extern void kernel_restart(char *cmd); extern void kernel_halt(void); extern void kernel_power_off(void); diff --git a/kernel/kexec.c b/kernel/kexec.c index 2810558802b6..b81682312dc4 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1472,11 +1472,7 @@ int kernel_kexec(void) } else #endif { - blocking_notifier_call_chain(&reboot_notifier_list, - SYS_RESTART, NULL); - system_state = SYSTEM_RESTART; - device_shutdown(); - sysdev_shutdown(); + kernel_restart_prepare(NULL); printk(KERN_EMERG "Starting new kernel\n"); machine_shutdown(); } diff --git a/kernel/sys.c b/kernel/sys.c index c01858090a98..3dacb00a7f76 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -274,7 +274,7 @@ void emergency_restart(void) } EXPORT_SYMBOL_GPL(emergency_restart); -static void kernel_restart_prepare(char *cmd) +void kernel_restart_prepare(char *cmd) { blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; -- cgit v1.2.3 From 9bdeb7b5d34f197dea7859d24475943395ffea5e Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 15 Aug 2008 00:40:25 -0700 Subject: kexec jump: __ftrace_enabled_save/restore Add __ftrace_enabled_save/restore, used to disable ftrace for a while. Now, this is used by kexec jump, which need a version without lock, for general situation, a locked version should be used. Signed-off-by: Huang Ying Cc: Pavel Machek Cc: "Rafael J. Wysocki" Cc: "Eric W. Biederman" Cc: Vivek Goyal Cc: Ingo Molnar Cc: Steven Rostedt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ftrace.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index f368d041e02d..bb384068272e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -98,6 +98,27 @@ static inline void tracer_disable(void) #endif } +/* Ftrace disable/restore without lock. Some synchronization mechanism + * must be used to prevent ftrace_enabled to be changed between + * disable/restore. */ +static inline int __ftrace_enabled_save(void) +{ +#ifdef CONFIG_FTRACE + int saved_ftrace_enabled = ftrace_enabled; + ftrace_enabled = 0; + return saved_ftrace_enabled; +#else + return 0; +#endif +} + +static inline void __ftrace_enabled_restore(int enabled) +{ +#ifdef CONFIG_FTRACE + ftrace_enabled = enabled; +#endif +} + #ifdef CONFIG_FRAME_POINTER /* TODO: need to fix this for ARM */ # define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -- cgit v1.2.3 From be4de35263f59ca1f4740edfffbfb02cc3f2189e Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 15 Aug 2008 00:40:44 -0700 Subject: completions: uninline try_wait_for_completion and completion_done m68k fails to build with these functions inlined in completion.h. Move them out of line into sched.c and export them to avoid this problem. Signed-off-by: Dave Chinner Cc: Geert Uytterhoeven Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/completion.h | 46 ++-------------------------------------------- kernel/sched.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/include/linux/completion.h b/include/linux/completion.h index 57faa60de9bd..02ef8835999c 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -49,6 +49,8 @@ extern unsigned long wait_for_completion_timeout(struct completion *x, unsigned long timeout); extern unsigned long wait_for_completion_interruptible_timeout( struct completion *x, unsigned long timeout); +extern bool try_wait_for_completion(struct completion *x); +extern bool completion_done(struct completion *x); extern void complete(struct completion *); extern void complete_all(struct completion *); @@ -56,48 +58,4 @@ extern void complete_all(struct completion *); #define INIT_COMPLETION(x) ((x).done = 0) -/** - * try_wait_for_completion - try to decrement a completion without blocking - * @x: completion structure - * - * Returns: 0 if a decrement cannot be done without blocking - * 1 if a decrement succeeded. - * - * If a completion is being used as a counting completion, - * attempt to decrement the counter without blocking. This - * enables us to avoid waiting if the resource the completion - * is protecting is not available. - */ -static inline bool try_wait_for_completion(struct completion *x) -{ - int ret = 1; - - spin_lock_irq(&x->wait.lock); - if (!x->done) - ret = 0; - else - x->done--; - spin_unlock_irq(&x->wait.lock); - return ret; -} - -/** - * completion_done - Test to see if a completion has any waiters - * @x: completion structure - * - * Returns: 0 if there are waiters (wait_for_completion() in progress) - * 1 if there are no waiters. - * - */ -static inline bool completion_done(struct completion *x) -{ - int ret = 1; - - spin_lock_irq(&x->wait.lock); - if (!x->done) - ret = 0; - spin_unlock_irq(&x->wait.lock); - return ret; -} - #endif diff --git a/kernel/sched.c b/kernel/sched.c index d601fb0406ca..95e6ad3c231d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4669,6 +4669,52 @@ int __sched wait_for_completion_killable(struct completion *x) } EXPORT_SYMBOL(wait_for_completion_killable); +/** + * try_wait_for_completion - try to decrement a completion without blocking + * @x: completion structure + * + * Returns: 0 if a decrement cannot be done without blocking + * 1 if a decrement succeeded. + * + * If a completion is being used as a counting completion, + * attempt to decrement the counter without blocking. This + * enables us to avoid waiting if the resource the completion + * is protecting is not available. + */ +bool try_wait_for_completion(struct completion *x) +{ + int ret = 1; + + spin_lock_irq(&x->wait.lock); + if (!x->done) + ret = 0; + else + x->done--; + spin_unlock_irq(&x->wait.lock); + return ret; +} +EXPORT_SYMBOL(try_wait_for_completion); + +/** + * completion_done - Test to see if a completion has any waiters + * @x: completion structure + * + * Returns: 0 if there are waiters (wait_for_completion() in progress) + * 1 if there are no waiters. + * + */ +bool completion_done(struct completion *x) +{ + int ret = 1; + + spin_lock_irq(&x->wait.lock); + if (!x->done) + ret = 0; + spin_unlock_irq(&x->wait.lock); + return ret; +} +EXPORT_SYMBOL(completion_done); + static long __sched sleep_on_common(wait_queue_head_t *q, int state, long timeout) { -- cgit v1.2.3 From dd0078f4f04d939950a792c493d7d97d7ce663b8 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 30 Jul 2008 14:20:54 -0400 Subject: rcu: just rename call_rcu_bh instead of making it a macro Seems that I found a box that has a config that passes call_rcu_bh as a function pointer (see net/sctp/sm_make_chunk.c), so declaring the call_rcu_bh has a macro function isn't good enough. This patch makes it just another name of call_rcu for rcupreempt. Signed-off-by: Steven Rostedt Reviewed-by: "Paul E. McKenney" Signed-off-by: Ingo Molnar --- include/linux/rcupreempt.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index addb5e282f39..3e05c09b54a2 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h @@ -57,7 +57,13 @@ static inline void rcu_qsctr_inc(int cpu) rdssp->sched_qs++; } #define rcu_bh_qsctr_inc(cpu) -#define call_rcu_bh(head, rcu) call_rcu(head, rcu) + +/* + * Someone might want to pass call_rcu_bh as a function pointer. + * So this needs to just be a rename and not a macro function. + * (no parentheses) + */ +#define call_rcu_bh call_rcu /** * call_rcu_sched - Queue RCU callback for invocation after sched grace period. -- cgit v1.2.3 From 89499759dc0dd300528510f465b0bf532fc79a2a Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Mon, 11 Aug 2008 17:01:50 -0700 Subject: x86/PCI: irq and pci_ids patch for Intel Ibex Peak PCHs This patch adds the Intel Ibex Peak (PCH) LPC and SMBus Controller DeviceIDs. Signed-off-by: Seth Heasley Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 2 ++ include/linux/pci_ids.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index fec0123b33a9..8e077185e185 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -590,6 +590,8 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route case PCI_DEVICE_ID_INTEL_ICH10_1: case PCI_DEVICE_ID_INTEL_ICH10_2: case PCI_DEVICE_ID_INTEL_ICH10_3: + case PCI_DEVICE_ID_INTEL_PCH_0: + case PCI_DEVICE_ID_INTEL_PCH_1: r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9ec2bcce8e83..f1624b396754 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2428,6 +2428,9 @@ #define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a #define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 #define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 +#define PCI_DEVICE_ID_INTEL_PCH_0 0x3b10 +#define PCI_DEVICE_ID_INTEL_PCH_1 0x3b11 +#define PCI_DEVICE_ID_INTEL_PCH_2 0x3b30 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f #define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 #define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 -- cgit v1.2.3 From 8c9a9dd0fa3a269d380eaae2dc1bee39e865fae1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 15 Aug 2008 10:39:38 +0100 Subject: tty: remove resize window special case This moves it to being a tty operation. That removes special cases and now also means that resize can be picked up by um and other non vt consoles which may have a resize operation. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 72 ++++++++++++++++++++++------------------ drivers/char/vt.c | 82 +++++++++++++++++++++++++++++++++++----------- drivers/char/vt_ioctl.c | 4 ++- include/linux/tty.h | 2 ++ include/linux/tty_driver.h | 14 ++++++++ include/linux/vt_kern.h | 1 - 6 files changed, 121 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 0e6866fe0f96..a27160ba21d7 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2496,45 +2496,25 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) } /** - * tiocswinsz - implement window size set ioctl - * @tty; tty - * @arg: user buffer for result + * tty_do_resize - resize event + * @tty: tty being resized + * @real_tty: real tty (if using a pty/tty pair) + * @rows: rows (character) + * @cols: cols (character) * - * Copies the user idea of the window size to the kernel. Traditionally - * this is just advisory information but for the Linux console it - * actually has driver level meaning and triggers a VC resize. - * - * Locking: - * Called function use the console_sem is used to ensure we do - * not try and resize the console twice at once. - * The tty->termios_mutex is used to ensure we don't double - * resize and get confused. Lock order - tty->termios_mutex before - * console sem + * Update the termios variables and send the neccessary signals to + * peform a terminal resize correctly */ -static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize __user *arg) +int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, + struct winsize *ws) { - struct winsize tmp_ws; struct pid *pgrp, *rpgrp; unsigned long flags; - if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) - return -EFAULT; - mutex_lock(&tty->termios_mutex); - if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg))) + if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; - -#ifdef CONFIG_VT - if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) { - if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col, - tmp_ws.ws_row)) { - mutex_unlock(&tty->termios_mutex); - return -ENXIO; - } - } -#endif /* Get the PID values and reference them so we can avoid holding the tty ctrl lock while sending signals */ spin_lock_irqsave(&tty->ctrl_lock, flags); @@ -2550,13 +2530,41 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, put_pid(pgrp); put_pid(rpgrp); - tty->winsize = tmp_ws; - real_tty->winsize = tmp_ws; + tty->winsize = *ws; + real_tty->winsize = *ws; done: mutex_unlock(&tty->termios_mutex); return 0; } +/** + * tiocswinsz - implement window size set ioctl + * @tty; tty + * @arg: user buffer for result + * + * Copies the user idea of the window size to the kernel. Traditionally + * this is just advisory information but for the Linux console it + * actually has driver level meaning and triggers a VC resize. + * + * Locking: + * Driver dependant. The default do_resize method takes the + * tty termios mutex and ctrl_lock. The console takes its own lock + * then calls into the default method. + */ + +static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, + struct winsize __user *arg) +{ + struct winsize tmp_ws; + if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) + return -EFAULT; + + if (tty->ops->resize) + return tty->ops->resize(tty, real_tty, &tmp_ws); + else + return tty_do_resize(tty, real_tty, &tmp_ws); +} + /** * tioccons - allow admin to move logical console * @file: the file to become console diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 1bc00c9d860d..60359c360912 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -803,7 +803,25 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, */ #define VC_RESIZE_MAXCOL (32767) #define VC_RESIZE_MAXROW (32767) -int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) + +/** + * vc_do_resize - resizing method for the tty + * @tty: tty being resized + * @real_tty: real tty (different to tty if a pty/tty pair) + * @vc: virtual console private data + * @cols: columns + * @lines: lines + * + * Resize a virtual console, clipping according to the actual constraints. + * If the caller passes a tty structure then update the termios winsize + * information and perform any neccessary signal handling. + * + * Caller must hold the console semaphore. Takes the termios mutex and + * ctrl_lock of the tty IFF a tty is passed. + */ + +static int vc_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, + struct vc_data *vc, unsigned int cols, unsigned int lines) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned int old_cols, old_rows, old_row_size, old_screen_size; @@ -907,24 +925,15 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) gotoxy(vc, vc->vc_x, vc->vc_y); save_cur(vc); - if (vc->vc_tty) { - struct winsize ws, *cws = &vc->vc_tty->winsize; - struct pid *pgrp = NULL; - + if (tty) { + /* Rewrite the requested winsize data with the actual + resulting sizes */ + struct winsize ws; memset(&ws, 0, sizeof(ws)); ws.ws_row = vc->vc_rows; ws.ws_col = vc->vc_cols; ws.ws_ypixel = vc->vc_scan_lines; - - spin_lock_irq(&vc->vc_tty->ctrl_lock); - if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col)) - pgrp = get_pid(vc->vc_tty->pgrp); - spin_unlock_irq(&vc->vc_tty->ctrl_lock); - if (pgrp) { - kill_pgrp(vc->vc_tty->pgrp, SIGWINCH, 1); - put_pid(pgrp); - } - *cws = ws; + tty_do_resize(tty, real_tty, &ws); } if (CON_IS_VISIBLE(vc)) @@ -932,14 +941,47 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) return err; } -int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) +/** + * vc_resize - resize a VT + * @vc: virtual console + * @cols: columns + * @rows: rows + * + * Resize a virtual console as seen from the console end of things. We + * use the common vc_do_resize methods to update the structures. The + * caller must hold the console sem to protect console internals and + * vc->vc_tty + */ + +int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) +{ + return vc_do_resize(vc->vc_tty, vc->vc_tty, vc, cols, rows); +} + +/** + * vt_resize - resize a VT + * @tty: tty to resize + * @real_tty: tty if a pty/tty pair + * @ws: winsize attributes + * + * Resize a virtual terminal. This is called by the tty layer as we + * register our own handler for resizing. The mutual helper does all + * the actual work. + * + * Takes the console sem and the called methods then take the tty + * termios_mutex and the tty ctrl_lock in that order. + */ + +int vt_resize(struct tty_struct *tty, struct tty_struct *real_tty, + struct winsize *ws) { - int rc; + struct vc_data *vc = tty->driver_data; + int ret; acquire_console_sem(); - rc = vc_resize(vc, cols, lines); + ret = vc_do_resize(tty, real_tty, vc, ws->ws_col, ws->ws_row); release_console_sem(); - return rc; + return ret; } void vc_deallocate(unsigned int currcons) @@ -2907,6 +2949,7 @@ static const struct tty_operations con_ops = { .start = con_start, .throttle = con_throttle, .unthrottle = con_unthrottle, + .resize = vt_resize, }; int __init vty_init(void) @@ -4061,7 +4104,6 @@ EXPORT_SYMBOL(default_blu); EXPORT_SYMBOL(update_region); EXPORT_SYMBOL(redraw_screen); EXPORT_SYMBOL(vc_resize); -EXPORT_SYMBOL(vc_lock_resize); EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(console_blank_hook); EXPORT_SYMBOL(console_blanked); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 3211afd9d57e..c904e9ad4a71 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -947,14 +947,16 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, get_user(cc, &vtsizes->v_cols)) ret = -EFAULT; else { + acquire_console_sem(); for (i = 0; i < MAX_NR_CONSOLES; i++) { vc = vc_cons[i].d; if (vc) { vc->vc_resize_user = 1; - vc_lock_resize(vc_cons[i].d, cc, ll); + vc_resize(vc_cons[i].d, cc, ll); } } + release_console_sem(); } break; } diff --git a/include/linux/tty.h b/include/linux/tty.h index e3579cb086e0..0cbec74ec086 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -331,6 +331,8 @@ extern int tty_write_room(struct tty_struct *tty); extern void tty_driver_flush_buffer(struct tty_struct *tty); extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); +extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, + struct winsize *ws); extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index e1065ac0d922..16d27944c321 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -168,6 +168,18 @@ * * Optional: If not provided then the write method is called under * the atomic write lock to keep it serialized with the ldisc. + * + * int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, + * unsigned int rows, unsigned int cols); + * + * Called when a termios request is issued which changes the + * requested terminal geometry. + * + * Optional: the default action is to update the termios structure + * without error. This is usually the correct behaviour. Drivers should + * not force errors here if they are not resizable objects (eg a serial + * line). See tty_do_resize() if you need to wrap the standard method + * in your own logic - the usual case. */ #include @@ -206,6 +218,8 @@ struct tty_operations { int (*tiocmget)(struct tty_struct *tty, struct file *file); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); + int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, + struct winsize *ws); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 1c78d56c57e5..1cbd0a7db4e6 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -35,7 +35,6 @@ extern int fg_console, last_console, want_console; int vc_allocate(unsigned int console); int vc_cons_allocated(unsigned int console); int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines); -int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines); void vc_deallocate(unsigned int console); void reset_palette(struct vc_data *vc); void do_blank_screen(int entering_gfx); -- cgit v1.2.3 From e3b99556975907530aeb9745e7b3945a0da48f17 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Fri, 15 Aug 2008 15:09:56 -0700 Subject: tun: TUNGETIFF interface to query name and flags Add a TUNGETIFF interface so that userspace can query a tun/tap descriptor for its name and flags. This is needed because it is common for one app to create a tap interface, exec another app and pass it the file descriptor for the interface. Without TUNGETIFF the spawned app has no way of detecting wheter the interface has e.g. IFF_VNET_HDR set. Signed-off-by: Mark McLoughlin Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/if_tun.h | 1 + 2 files changed, 40 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index e6bbc639c2d0..95931a5a9883 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -748,6 +748,36 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) return err; } +static int tun_get_iff(struct net *net, struct file *file, struct ifreq *ifr) +{ + struct tun_struct *tun = file->private_data; + + if (!tun) + return -EBADFD; + + DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name); + + strcpy(ifr->ifr_name, tun->dev->name); + + ifr->ifr_flags = 0; + + if (ifr->ifr_flags & TUN_TUN_DEV) + ifr->ifr_flags |= IFF_TUN; + else + ifr->ifr_flags |= IFF_TAP; + + if (tun->flags & TUN_NO_PI) + ifr->ifr_flags |= IFF_NO_PI; + + if (tun->flags & TUN_ONE_QUEUE) + ifr->ifr_flags |= IFF_ONE_QUEUE; + + if (tun->flags & TUN_VNET_HDR) + ifr->ifr_flags |= IFF_VNET_HDR; + + return 0; +} + /* This is like a cut-down ethtool ops, except done via tun fd so no * privs required. */ static int set_offload(struct net_device *dev, unsigned long arg) @@ -833,6 +863,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd); switch (cmd) { + case TUNGETIFF: + ret = tun_get_iff(current->nsproxy->net_ns, file, &ifr); + if (ret) + return ret; + + if (copy_to_user(argp, &ifr, sizeof(ifr))) + return -EFAULT; + break; + case TUNSETNOCSUM: /* Disable/Enable checksum */ if (arg) diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 4c6307ad9fdb..8529f57ba263 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -45,6 +45,7 @@ #define TUNGETFEATURES _IOR('T', 207, unsigned int) #define TUNSETOFFLOAD _IOW('T', 208, unsigned int) #define TUNSETTXFILTER _IOW('T', 209, unsigned int) +#define TUNGETIFF _IOR('T', 210, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3 From db543c1f973cd1d557cc32ceee76737c1e4d2898 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Aug 2008 15:13:53 -0700 Subject: net: skb_copy_datagram_from_iovec() There's an skb_copy_datagram_iovec() to copy out of a paged skb, but nothing the other way around (because we don't do that). We want to allocate big skbs in tun.c, so let's add the function. It's a carbon copy of skb_copy_datagram_iovec() with enough changes to be annoying. Signed-off-by: Rusty Russell Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 +++ net/core/datagram.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 358661c9990e..909923717830 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1452,6 +1452,10 @@ extern int skb_copy_datagram_iovec(const struct sk_buff *from, extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen, struct iovec *iov); +extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, + int offset, + struct iovec *from, + int len); extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); extern int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags); diff --git a/net/core/datagram.c b/net/core/datagram.c index dd61dcad6019..52f577a0f544 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -339,6 +339,93 @@ fault: return -EFAULT; } +/** + * skb_copy_datagram_from_iovec - Copy a datagram from an iovec. + * @skb: buffer to copy + * @offset: offset in the buffer to start copying to + * @from: io vector to copy to + * @len: amount of data to copy to buffer from iovec + * + * Returns 0 or -EFAULT. + * Note: the iovec is modified during the copy. + */ +int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, + struct iovec *from, int len) +{ + int start = skb_headlen(skb); + int i, copy = start - offset; + + /* Copy header. */ + if (copy > 0) { + if (copy > len) + copy = len; + if (memcpy_fromiovec(skb->data + offset, from, copy)) + goto fault; + if ((len -= copy) == 0) + return 0; + offset += copy; + } + + /* Copy paged appendix. Hmm... why does this look so complicated? */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + int end; + + WARN_ON(start > offset + len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end - offset) > 0) { + int err; + u8 *vaddr; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + struct page *page = frag->page; + + if (copy > len) + copy = len; + vaddr = kmap(page); + err = memcpy_fromiovec(vaddr + frag->page_offset + + offset - start, from, copy); + kunmap(page); + if (err) + goto fault; + + if (!(len -= copy)) + return 0; + offset += copy; + } + start = end; + } + + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *list = skb_shinfo(skb)->frag_list; + + for (; list; list = list->next) { + int end; + + WARN_ON(start > offset + len); + + end = start + list->len; + if ((copy = end - offset) > 0) { + if (copy > len) + copy = len; + if (skb_copy_datagram_from_iovec(list, + offset - start, + from, copy)) + goto fault; + if ((len -= copy) == 0) + return 0; + offset += copy; + } + start = end; + } + } + if (!len) + return 0; + +fault: + return -EFAULT; +} +EXPORT_SYMBOL(skb_copy_datagram_from_iovec); + static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 __user *to, int len, __wsum *csump) -- cgit v1.2.3 From 605d9288b3e8a3d15e6f36185c2fc737b6979572 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Sat, 16 Aug 2008 11:07:21 +0100 Subject: mm: VM_flags comment fixes Try to comment away a little of the confusion between mm's vm_area_struct vm_flags and vmalloc's vm_struct flags: based on an idea by Ulrich Drepper. Signed-off-by: Hugh Dickins Signed-off-by: Linus Torvalds --- include/linux/mm.h | 2 +- include/linux/mm_types.h | 2 +- include/linux/vmalloc.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index fa651609b65d..72a15dc26bbf 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -73,7 +73,7 @@ extern unsigned int kobjsize(const void *objp); #endif /* - * vm_flags.. + * vm_flags in vm_area_struct, see mm_types.h. */ #define VM_READ 0x00000001 /* currently active flags */ #define VM_WRITE 0x00000002 diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 386edbe2cb4e..bf334138c7c1 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -113,7 +113,7 @@ struct vm_area_struct { struct vm_area_struct *vm_next; pgprot_t vm_page_prot; /* Access permissions of this VMA. */ - unsigned long vm_flags; /* Flags, listed below. */ + unsigned long vm_flags; /* Flags, see mm.h. */ struct rb_node vm_rb; diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 364789aae9f3..328eb4022727 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -4,9 +4,9 @@ #include #include /* pgprot_t */ -struct vm_area_struct; +struct vm_area_struct; /* vma defining user mapping in mm_types.h */ -/* bits in vm_struct->flags */ +/* bits in flags of vmalloc's vm_struct below */ #define VM_IOREMAP 0x00000001 /* ioremap() and friends */ #define VM_ALLOC 0x00000002 /* vmalloc() */ #define VM_MAP 0x00000004 /* vmap()ed pages */ -- cgit v1.2.3 From 5e6b83ed8c00f2e2ae5b2413c5907bed735b600d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 16 Aug 2008 11:55:04 +0100 Subject: Fix header export of videodev2.h, ivtv.h, ivtvfb.h The exported copy of videodev2.h contains this line: #define #include This is because for some reason it defines __user for itself -- despite the fact that we remove all instances of __user when exporting headers. _All_ pointers in userspace are user pointers. Fix it by removing the unnecessary '#define __user' from the file. The new headers ivtv.h and ivtvfb.h would have the same problem... if whoever put them there had actually remembered to add them to the Kbuild file while he was at it. Fix those too, and export them as was presumably intended. Note that includes of are also stripped by the header export process, so those don't need to be conditional. Signed-off-by: David Woodhouse Signed-off-by: Mauro Carvalho Chehab Acked-by: Hans Verkuil Signed-off-by: Linus Torvalds --- include/linux/Kbuild | 2 ++ include/linux/ivtv.h | 6 +----- include/linux/ivtvfb.h | 6 +----- include/linux/videodev2.h | 4 ++-- 4 files changed, 6 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 327f60658d94..7d970678f940 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -250,6 +250,8 @@ unifdef-y += isdn.h unifdef-y += isdnif.h unifdef-y += isdn_divertif.h unifdef-y += isdn_ppp.h +unifdef-y += ivtv.h +unifdef-y += ivtvfb.h unifdef-y += joystick.h unifdef-y += kdev_t.h unifdef-y += kd.h diff --git a/include/linux/ivtv.h b/include/linux/ivtv.h index 794b8daa9378..17ca64b5a66c 100644 --- a/include/linux/ivtv.h +++ b/include/linux/ivtv.h @@ -21,11 +21,7 @@ #ifndef __LINUX_IVTV_H__ #define __LINUX_IVTV_H__ -#ifdef __KERNEL__ -#include /* need __user */ -#else -#define __user -#endif +#include #include /* ivtv knows several distinct output modes: MPEG streaming, diff --git a/include/linux/ivtvfb.h b/include/linux/ivtvfb.h index e980ba62ddcc..e20af47b59ad 100644 --- a/include/linux/ivtvfb.h +++ b/include/linux/ivtvfb.h @@ -21,11 +21,7 @@ #ifndef __LINUX_IVTVFB_H__ #define __LINUX_IVTVFB_H__ -#ifdef __KERNEL__ -#include /* need __user */ -#else -#define __user -#endif +#include #include /* Framebuffer external API */ diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e466bd54a50e..e65a6bed4e3e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -55,13 +55,13 @@ */ #ifndef __LINUX_VIDEODEV2_H #define __LINUX_VIDEODEV2_H + #ifdef __KERNEL__ #include /* need struct timeval */ -#include /* need __user */ #else -#define __user #include #endif +#include #include #include -- cgit v1.2.3 From 5e186b57e7ede86aeb9db30e66315bde4e8b1815 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Sun, 17 Aug 2008 05:34:20 +0400 Subject: security.h: fix build failure security.h: fix build failure include/linux/security.h: In function 'security_ptrace_traceme': include/linux/security.h:1760: error: 'parent' undeclared (first use in this function) Signed-off-by: Alexander Beregalov Tested-by: Ingo Molnar Signed-off-by: James Morris --- include/linux/security.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 2ee5ecfb2393..80c4d002864c 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1755,7 +1755,7 @@ static inline int security_ptrace_may_access(struct task_struct *child, return cap_ptrace_may_access(child, mode); } -static inline int security_ptrace_traceme(struct task_struct *child) +static inline int security_ptrace_traceme(struct task_struct *parent) { return cap_ptrace_traceme(parent); } -- cgit v1.2.3 From ded00a56e99555c3f4000ef3eebfd5fe0d574565 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 17 Aug 2008 12:50:36 -0700 Subject: rcu: remove redundant ACCESS_ONCE definition from rcupreempt.c Remove the redundant definition of ACCESS_ONCE() from rcupreempt.c in favor of the one in compiler.h. Also merge the comment header from rcupreempt.c's definition into that in compiler.h. Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar --- include/linux/compiler.h | 4 +++- kernel/rcupreempt.c | 8 -------- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/compiler.h b/include/linux/compiler.h index c8bd2daf95ec..8322141ee480 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -190,7 +190,9 @@ extern void __chk_io_ptr(const volatile void __iomem *); * ACCESS_ONCE() in different C statements. * * This macro does absolutely -nothing- to prevent the CPU from reordering, - * merging, or refetching absolutely anything at any time. + * merging, or refetching absolutely anything at any time. Its main intended + * use is to mediate communication between process-level code and irq/NMI + * handlers, all running on the same CPU. */ #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index 27827931ca0d..ca4bbbe04aa4 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c @@ -58,14 +58,6 @@ #include #include -/* - * Macro that prevents the compiler from reordering accesses, but does - * absolutely -nothing- to prevent CPUs from reordering. This is used - * only to mediate communication between mainline code and hardware - * interrupt and NMI handlers. - */ -#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) - /* * PREEMPT_RCU data structures. */ -- cgit v1.2.3 From 37014c64079748c47fd109ef2d91ecd785a8c764 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 18 Aug 2008 21:40:05 +0200 Subject: ata: add missing ATA_ID_* defines (take 2) Add missing ATA_ID_* defines and update {ata,atapi}_*() inlines accordingly. The currently unused defines are needed for the forthcoming drivers/ide/ changes. v2: Add ATA_ID_SPG. Acked-by: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ata.h | 122 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ata.h b/include/linux/ata.h index 1c622e2b0504..03fff6239b3c 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -46,18 +46,48 @@ enum { ATA_MAX_SECTORS_TAPE = 65535, ATA_ID_WORDS = 256, + ATA_ID_CONFIG = 0, + ATA_ID_CYLS = 1, + ATA_ID_HEADS = 3, + ATA_ID_SECTORS = 6, ATA_ID_SERNO = 10, + ATA_ID_BUF_SIZE = 21, ATA_ID_FW_REV = 23, ATA_ID_PROD = 27, + ATA_ID_MAX_MULTSECT = 47, + ATA_ID_DWORD_IO = 48, + ATA_ID_CAPABILITY = 49, ATA_ID_OLD_PIO_MODES = 51, + ATA_ID_OLD_DMA_MODES = 52, ATA_ID_FIELD_VALID = 53, + ATA_ID_CUR_CYLS = 54, + ATA_ID_CUR_HEADS = 55, + ATA_ID_CUR_SECTORS = 56, + ATA_ID_MULTSECT = 59, + ATA_ID_LBA_CAPACITY = 60, + ATA_ID_SWDMA_MODES = 62, ATA_ID_MWDMA_MODES = 63, ATA_ID_PIO_MODES = 64, ATA_ID_EIDE_DMA_MIN = 65, + ATA_ID_EIDE_DMA_TIME = 66, ATA_ID_EIDE_PIO = 67, ATA_ID_EIDE_PIO_IORDY = 68, - ATA_ID_UDMA_MODES = 88, + ATA_ID_QUEUE_DEPTH = 75, ATA_ID_MAJOR_VER = 80, + ATA_ID_COMMAND_SET_1 = 82, + ATA_ID_COMMAND_SET_2 = 83, + ATA_ID_CFSSE = 84, + ATA_ID_CFS_ENABLE_1 = 85, + ATA_ID_CFS_ENABLE_2 = 86, + ATA_ID_CSF_DEFAULT = 87, + ATA_ID_UDMA_MODES = 88, + ATA_ID_HW_CONFIG = 93, + ATA_ID_SPG = 98, + ATA_ID_LBA_CAPACITY_2 = 100, + ATA_ID_LAST_LUN = 126, + ATA_ID_DLF = 128, + ATA_ID_CSFO = 129, + ATA_ID_CFA_POWER = 160, ATA_ID_PIO4 = (1 << 1), ATA_ID_SERNO_LEN = 20, @@ -438,17 +468,17 @@ static inline int ata_is_data(u8 prot) /* * id tests */ -#define ata_id_is_ata(id) (((id)[0] & (1 << 15)) == 0) -#define ata_id_has_lba(id) ((id)[49] & (1 << 9)) -#define ata_id_has_dma(id) ((id)[49] & (1 << 8)) +#define ata_id_is_ata(id) (((id)[ATA_ID_CONFIG] & (1 << 15)) == 0) +#define ata_id_has_lba(id) ((id)[ATA_ID_CAPABILITY] & (1 << 9)) +#define ata_id_has_dma(id) ((id)[ATA_ID_CAPABILITY] & (1 << 8)) #define ata_id_has_ncq(id) ((id)[76] & (1 << 8)) -#define ata_id_queue_depth(id) (((id)[75] & 0x1f) + 1) -#define ata_id_removeable(id) ((id)[0] & (1 << 7)) +#define ata_id_queue_depth(id) (((id)[ATA_ID_QUEUE_DEPTH] & 0x1f) + 1) +#define ata_id_removeable(id) ((id)[ATA_ID_CONFIG] & (1 << 7)) #define ata_id_has_atapi_AN(id) \ ( (((id)[76] != 0x0000) && ((id)[76] != 0xffff)) && \ ((id)[78] & (1 << 5)) ) -#define ata_id_iordy_disable(id) ((id)[49] & (1 << 10)) -#define ata_id_has_iordy(id) ((id)[49] & (1 << 11)) +#define ata_id_iordy_disable(id) ((id)[ATA_ID_CAPABILITY] & (1 << 10)) +#define ata_id_has_iordy(id) ((id)[ATA_ID_CAPABILITY] & (1 << 11)) #define ata_id_u32(id,n) \ (((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)])) #define ata_id_u64(id,n) \ @@ -457,7 +487,7 @@ static inline int ata_is_data(u8 prot) ((u64) (id)[(n) + 1] << 16) | \ ((u64) (id)[(n) + 0]) ) -#define ata_id_cdb_intr(id) (((id)[0] & 0x60) == 0x20) +#define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) static inline bool ata_id_has_hipm(const u16 *id) { @@ -482,75 +512,75 @@ static inline bool ata_id_has_dipm(const u16 *id) static inline int ata_id_has_fua(const u16 *id) { - if ((id[84] & 0xC000) != 0x4000) + if ((id[ATA_ID_CFSSE] & 0xC000) != 0x4000) return 0; - return id[84] & (1 << 6); + return id[ATA_ID_CFSSE] & (1 << 6); } static inline int ata_id_has_flush(const u16 *id) { - if ((id[83] & 0xC000) != 0x4000) + if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) return 0; - return id[83] & (1 << 12); + return id[ATA_ID_COMMAND_SET_2] & (1 << 12); } static inline int ata_id_has_flush_ext(const u16 *id) { - if ((id[83] & 0xC000) != 0x4000) + if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) return 0; - return id[83] & (1 << 13); + return id[ATA_ID_COMMAND_SET_2] & (1 << 13); } static inline int ata_id_has_lba48(const u16 *id) { - if ((id[83] & 0xC000) != 0x4000) + if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) return 0; - if (!ata_id_u64(id, 100)) + if (!ata_id_u64(id, ATA_ID_LBA_CAPACITY_2)) return 0; - return id[83] & (1 << 10); + return id[ATA_ID_COMMAND_SET_2] & (1 << 10); } static inline int ata_id_hpa_enabled(const u16 *id) { /* Yes children, word 83 valid bits cover word 82 data */ - if ((id[83] & 0xC000) != 0x4000) + if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) return 0; /* And 87 covers 85-87 */ - if ((id[87] & 0xC000) != 0x4000) + if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000) return 0; /* Check command sets enabled as well as supported */ - if ((id[85] & ( 1 << 10)) == 0) + if ((id[ATA_ID_CFS_ENABLE_1] & (1 << 10)) == 0) return 0; - return id[82] & (1 << 10); + return id[ATA_ID_COMMAND_SET_1] & (1 << 10); } static inline int ata_id_has_wcache(const u16 *id) { /* Yes children, word 83 valid bits cover word 82 data */ - if ((id[83] & 0xC000) != 0x4000) + if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) return 0; - return id[82] & (1 << 5); + return id[ATA_ID_COMMAND_SET_1] & (1 << 5); } static inline int ata_id_has_pm(const u16 *id) { - if ((id[83] & 0xC000) != 0x4000) + if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) return 0; - return id[82] & (1 << 3); + return id[ATA_ID_COMMAND_SET_1] & (1 << 3); } static inline int ata_id_rahead_enabled(const u16 *id) { - if ((id[87] & 0xC000) != 0x4000) + if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000) return 0; - return id[85] & (1 << 6); + return id[ATA_ID_CFS_ENABLE_1] & (1 << 6); } static inline int ata_id_wcache_enabled(const u16 *id) { - if ((id[87] & 0xC000) != 0x4000) + if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000) return 0; - return id[85] & (1 << 5); + return id[ATA_ID_CFS_ENABLE_1] & (1 << 5); } /** @@ -581,7 +611,7 @@ static inline unsigned int ata_id_major_version(const u16 *id) static inline int ata_id_is_sata(const u16 *id) { - return ata_id_major_version(id) >= 5 && id[93] == 0; + return ata_id_major_version(id) >= 5 && id[ATA_ID_HW_CONFIG] == 0; } static inline int ata_id_has_tpm(const u16 *id) @@ -599,7 +629,7 @@ static inline int ata_id_has_dword_io(const u16 *id) /* ATA 8 reuses this flag for "trusted" computing */ if (ata_id_major_version(id) > 7) return 0; - if (id[48] & (1 << 0)) + if (id[ATA_ID_DWORD_IO] & (1 << 0)) return 1; return 0; } @@ -608,22 +638,22 @@ static inline int ata_id_current_chs_valid(const u16 *id) { /* For ATA-1 devices, if the INITIALIZE DEVICE PARAMETERS command has not been issued to the device then the values of - id[54] to id[56] are vendor specific. */ - return (id[53] & 0x01) && /* Current translation valid */ - id[54] && /* cylinders in current translation */ - id[55] && /* heads in current translation */ - id[55] <= 16 && - id[56]; /* sectors in current translation */ + id[ATA_ID_CUR_CYLS] to id[ATA_ID_CUR_SECTORS] are vendor specific. */ + return (id[ATA_ID_FIELD_VALID] & 1) && /* Current translation valid */ + id[ATA_ID_CUR_CYLS] && /* cylinders in current translation */ + id[ATA_ID_CUR_HEADS] && /* heads in current translation */ + id[ATA_ID_CUR_HEADS] <= 16 && + id[ATA_ID_CUR_SECTORS]; /* sectors in current translation */ } static inline int ata_id_is_cfa(const u16 *id) { - u16 v = id[0]; - if (v == 0x848A) /* Standard CF */ + if (id[ATA_ID_CONFIG] == 0x848A) /* Standard CF */ return 1; /* Could be CF hiding as standard ATA */ - if (ata_id_major_version(id) >= 3 && id[82] != 0xFFFF && - (id[82] & ( 1 << 2))) + if (ata_id_major_version(id) >= 3 && + id[ATA_ID_COMMAND_SET_1] != 0xFFFF && + (id[ATA_ID_COMMAND_SET_1] & (1 << 2))) return 1; return 0; } @@ -632,21 +662,21 @@ static inline int ata_drive_40wire(const u16 *dev_id) { if (ata_id_is_sata(dev_id)) return 0; /* SATA */ - if ((dev_id[93] & 0xE000) == 0x6000) + if ((dev_id[ATA_ID_HW_CONFIG] & 0xE000) == 0x6000) return 0; /* 80 wire */ return 1; } static inline int ata_drive_40wire_relaxed(const u16 *dev_id) { - if ((dev_id[93] & 0x2000) == 0x2000) + if ((dev_id[ATA_ID_HW_CONFIG] & 0x2000) == 0x2000) return 0; /* 80 wire */ return 1; } static inline int atapi_cdb_len(const u16 *dev_id) { - u16 tmp = dev_id[0] & 0x3; + u16 tmp = dev_id[ATA_ID_CONFIG] & 0x3; switch (tmp) { case 0: return 12; case 1: return 16; @@ -656,7 +686,7 @@ static inline int atapi_cdb_len(const u16 *dev_id) static inline int atapi_command_packet_set(const u16 *dev_id) { - return (dev_id[0] >> 8) & 0x1f; + return (dev_id[ATA_ID_CONFIG] >> 8) & 0x1f; } static inline int atapi_id_dmadir(const u16 *dev_id) -- cgit v1.2.3 From 476d9894dde2da2c2b326d70b5bce5eccc593c8b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 18 Aug 2008 21:40:05 +0200 Subject: ata: add missing ATA_CMD_* defines Add missing ATA_CMD_* defines to . Also add ATA_EXABYTE_ENABLE_NEST, SETFEATURES_AAM_* and ATA_SMART_* defines while at it. Partially based on earlier work by Chris Wedgwood. Acked-by: Chris Wedgwood Acked-by: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ata.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ata.h b/include/linux/ata.h index 03fff6239b3c..cf4ef6d915ac 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -222,6 +222,13 @@ enum { ATA_CMD_PMP_WRITE = 0xE8, ATA_CMD_CONF_OVERLAY = 0xB1, ATA_CMD_SEC_FREEZE_LOCK = 0xF5, + ATA_CMD_SMART = 0xB0, + ATA_CMD_MEDIA_LOCK = 0xDE, + ATA_CMD_MEDIA_UNLOCK = 0xDF, + /* marked obsolete in the ATA/ATAPI-7 spec */ + ATA_CMD_RESTORE = 0x10, + /* EXABYTE specific */ + ATA_EXABYTE_ENABLE_NEST = 0xF0, /* READ_LOG_EXT pages */ ATA_LOG_SATA_NCQ = 0x10, @@ -262,6 +269,10 @@ enum { SETFEATURES_WC_ON = 0x02, /* Enable write cache */ SETFEATURES_WC_OFF = 0x82, /* Disable write cache */ + /* Enable/Disable Automatic Acoustic Management */ + SETFEATURES_AAM_ON = 0x42, + SETFEATURES_AAM_OFF = 0xC2, + SETFEATURES_SPINUP = 0x07, /* Spin-up drive */ SETFEATURES_SATA_ENABLE = 0x10, /* Enable use of SATA feature */ @@ -284,6 +295,15 @@ enum { ATA_DCO_IDENTIFY = 0xC2, ATA_DCO_SET = 0xC3, + /* feature values for SMART */ + ATA_SMART_ENABLE = 0xD8, + ATA_SMART_READ_VALUES = 0xD0, + ATA_SMART_READ_THRESHOLDS = 0xD1, + + /* password used in LBA Mid / LBA High for executing SMART commands */ + ATA_SMART_LBAM_PASS = 0x4F, + ATA_SMART_LBAH_PASS = 0xC2, + /* ATAPI stuff */ ATAPI_PKT_DMA = (1 << 0), ATAPI_DMADIR = (1 << 2), /* ATAPI data dir: -- cgit v1.2.3 From b59116205c54c89df9cc80721b59e1e8d14488f1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 18 Aug 2008 21:40:05 +0200 Subject: ata: add missing ATA_* defines Add missing ATA_* defines to . Also add ATAPI_{LFS,EOM,ILI,IO,CODE} defines while at it. Cc: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ata.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ata.h b/include/linux/ata.h index cf4ef6d915ac..1ce19c1ef0e9 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -153,13 +153,26 @@ enum { ATA_BUSY = (1 << 7), /* BSY status bit */ ATA_DRDY = (1 << 6), /* device ready */ ATA_DF = (1 << 5), /* device fault */ + ATA_DSC = (1 << 4), /* drive seek complete */ ATA_DRQ = (1 << 3), /* data request i/o */ + ATA_CORR = (1 << 2), /* corrected data error */ + ATA_IDX = (1 << 1), /* index */ ATA_ERR = (1 << 0), /* have an error */ ATA_SRST = (1 << 2), /* software reset */ ATA_ICRC = (1 << 7), /* interface CRC error */ + ATA_BBK = ATA_ICRC, /* pre-EIDE: block marked bad */ ATA_UNC = (1 << 6), /* uncorrectable media error */ + ATA_MC = (1 << 5), /* media changed */ ATA_IDNF = (1 << 4), /* ID not found */ + ATA_MCR = (1 << 3), /* media change requested */ ATA_ABORTED = (1 << 2), /* command aborted */ + ATA_TRK0NF = (1 << 1), /* track 0 not found */ + ATA_AMNF = (1 << 0), /* address mark not found */ + ATAPI_LFS = 0xF0, /* last failed sense */ + ATAPI_EOM = ATA_TRK0NF, /* end of media */ + ATAPI_ILI = ATA_AMNF, /* illegal length indication */ + ATAPI_IO = (1 << 1), + ATAPI_COD = (1 << 0), /* ATA command block registers */ ATA_REG_DATA = 0x00, -- cgit v1.2.3 From 056c58e8eb4d6765214757e541b68095e2eb2bd2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 18 Aug 2008 20:22:54 +0200 Subject: PCI: add acpi_find_root_bridge_handle Consolidate finding of a root bridge and getting its handle to the one inline function. It's cut & pasted on multiple places. Use this new inline in those. Cc: kristen.c.accardi@intel.com Acked-by: Alex Chiang Signed-off-by: Jiri Slaby Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 5 +---- drivers/pci/pcie/aer/aerdrv_acpi.c | 7 +------ include/linux/pci-acpi.h | 11 +++++++++++ 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index bd831970f00b..e17ef54f0efc 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -404,10 +404,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) * OSHP within the scope of the hotplug controller and its parents, * upto the host bridge under which this controller exists. */ - while (pdev->bus->self) - pdev = pdev->bus->self; - handle = acpi_get_pci_rootbridge_handle(pci_domain_nr(pdev->bus), - pdev->bus->number); + handle = acpi_find_root_bridge_handle(pdev); if (handle) { acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); dbg("Trying to get hotplug control for %s\n", diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 30f581b8791f..6dd7b13e9808 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -36,12 +36,7 @@ int aer_osc_setup(struct pcie_device *pciedev) if (acpi_pci_disabled) return -1; - /* Find root host bridge */ - while (pdev->bus->self) - pdev = pdev->bus->self; - handle = acpi_get_pci_rootbridge_handle( - pci_domain_nr(pdev->bus), pdev->bus->number); - + handle = acpi_find_root_bridge_handle(pdev); if (handle) { pcie_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT); status = pci_osc_control_set(handle, diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 3ba25065fa96..8837928fbf33 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -57,6 +57,15 @@ static inline acpi_status pcie_osc_support_set(u32 flags) { return __pci_osc_support_set(flags, PCI_EXPRESS_ROOT_HID_STRING); } +static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) +{ + /* Find root host bridge */ + while (pdev->bus->self) + pdev = pdev->bus->self; + + return acpi_get_pci_rootbridge_handle(pci_domain_nr(pdev->bus), + pdev->bus->number); +} #else #if !defined(AE_ERROR) typedef u32 acpi_status; @@ -66,6 +75,8 @@ static inline acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) {return AE_ERROR;} static inline acpi_status pci_osc_support_set(u32 flags) {return AE_ERROR;} static inline acpi_status pcie_osc_support_set(u32 flags) {return AE_ERROR;} +static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) +{ return NULL; } #endif #endif /* _PCI_ACPI_H_ */ -- cgit v1.2.3 From 1f7c14c62ce63805f9574664a6c6de3633d4a354 Mon Sep 17 00:00:00 2001 From: Mingming Cao Date: Thu, 9 Oct 2008 12:50:59 -0400 Subject: percpu counter: clean up percpu_counter_sum_and_set() percpu_counter_sum_and_set() and percpu_counter_sum() is the same except the former updates the global counter after accounting. Since we are taking the fbc->lock to calculate the precise value of the counter in percpu_counter_sum() anyway, it should simply set fbc->count too, as the percpu_counter_sum_and_set() does. This patch merges these two interfaces into one. Signed-off-by: Mingming Cao Acked-by: Peter Zijlstra Cc: Signed-off-by: Andrew Morton Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 2 +- include/linux/percpu_counter.h | 12 +++--------- lib/percpu_counter.c | 8 +++----- 3 files changed, 7 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index e9fa960ba6da..00a94d5866c2 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -1624,7 +1624,7 @@ ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi, #ifdef CONFIG_SMP if (free_blocks - root_blocks < FBC_BATCH) free_blocks = - percpu_counter_sum_and_set(&sbi->s_freeblocks_counter); + percpu_counter_sum(&sbi->s_freeblocks_counter); #endif if (free_blocks <= root_blocks) /* we don't have free space */ diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 208388835357..9007ccdfc112 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -35,7 +35,7 @@ int percpu_counter_init_irq(struct percpu_counter *fbc, s64 amount); void percpu_counter_destroy(struct percpu_counter *fbc); void percpu_counter_set(struct percpu_counter *fbc, s64 amount); void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch); -s64 __percpu_counter_sum(struct percpu_counter *fbc, int set); +s64 __percpu_counter_sum(struct percpu_counter *fbc); static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount) { @@ -44,19 +44,13 @@ static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount) static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc) { - s64 ret = __percpu_counter_sum(fbc, 0); + s64 ret = __percpu_counter_sum(fbc); return ret < 0 ? 0 : ret; } -static inline s64 percpu_counter_sum_and_set(struct percpu_counter *fbc) -{ - return __percpu_counter_sum(fbc, 1); -} - - static inline s64 percpu_counter_sum(struct percpu_counter *fbc) { - return __percpu_counter_sum(fbc, 0); + return __percpu_counter_sum(fbc); } static inline s64 percpu_counter_read(struct percpu_counter *fbc) diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 4a8ba4bf5f6f..a8663890a88c 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -52,7 +52,7 @@ EXPORT_SYMBOL(__percpu_counter_add); * Add up all the per-cpu counts, return the result. This is a more accurate * but much slower version of percpu_counter_read_positive() */ -s64 __percpu_counter_sum(struct percpu_counter *fbc, int set) +s64 __percpu_counter_sum(struct percpu_counter *fbc) { s64 ret; int cpu; @@ -62,11 +62,9 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc, int set) for_each_online_cpu(cpu) { s32 *pcount = per_cpu_ptr(fbc->counters, cpu); ret += *pcount; - if (set) - *pcount = 0; + *pcount = 0; } - if (set) - fbc->count = ret; + fbc->count = ret; spin_unlock(&fbc->lock); return ret; -- cgit v1.2.3 From e4464facd6f1404c2baeb6798d71e34423e7aeaa Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 20 Aug 2008 14:58:23 +0100 Subject: Reserve NFS fileid values for btrfs Purely cosmetic for now, but we might as well get it merged ASAP. Signed-off-by: David Woodhouse Signed-off-by: Linus Torvalds --- include/linux/exportfs.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index f5abd1306638..27e772cefb6a 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -34,6 +34,27 @@ enum fid_type { */ FILEID_INO32_GEN_PARENT = 2, + /* + * 64 bit object ID, 64 bit root object ID, + * 32 bit generation number. + */ + FILEID_BTRFS_WITHOUT_PARENT = 0x4d, + + /* + * 64 bit object ID, 64 bit root object ID, + * 32 bit generation number, + * 64 bit parent object ID, 32 bit parent generation. + */ + FILEID_BTRFS_WITH_PARENT = 0x4e, + + /* + * 64 bit object ID, 64 bit root object ID, + * 32 bit generation number, + * 64 bit parent object ID, 32 bit parent generation, + * 64 bit parent root object ID. + */ + FILEID_BTRFS_WITH_PARENT_ROOT = 0x4f, + /* * 32 bit block number, 16 bit partition reference, * 16 bit unused, 32 bit generation number. -- cgit v1.2.3 From d9105c2b01eedb620cae96073dde4f760367817f Mon Sep 17 00:00:00 2001 From: Marek VaÅ¡ut Date: Sun, 3 Aug 2008 21:34:08 +0100 Subject: [ARM] 5184/1: Split ucb1400_ts into core and touchscreen This patch splits ucb1400_ts into ucb1400_ts and ucb1400_core. Since this chip supports more features than only touchscreen, it was necessary to prepare it for feature addition. The previous functionality is preserved by applying this patch. [Build fixes for non-ARM by Stephen Rothwell and Takashi Iwai] Signed-off-by: Marek Vasut Signed-off-by: Russell King --- drivers/input/touchscreen/Kconfig | 1 + drivers/input/touchscreen/ucb1400_ts.c | 382 ++++++++++++--------------------- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/ucb1400_core.c | 106 +++++++++ include/linux/ucb1400.h | 161 ++++++++++++++ 6 files changed, 412 insertions(+), 248 deletions(-) create mode 100644 drivers/mfd/ucb1400_core.c create mode 100644 include/linux/ucb1400.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6e60a97a234c..fcabff9b39b1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -220,6 +220,7 @@ config TOUCHSCREEN_ATMEL_TSADCC config TOUCHSCREEN_UCB1400 tristate "Philips UCB1400 touchscreen" select AC97_BUS + depends on UCB1400_CORE help This enables support for the Philips UCB1400 touchscreen interface. The UCB1400 is an AC97 audio codec. The touchscreen interface diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index bce018e45bce..54986627def0 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -5,6 +5,10 @@ * Created: September 25, 2006 * Copyright: MontaVista Software, Inc. * + * Spliting done by: Marek Vasut + * If something doesnt work and it worked before spliting, e-mail me, + * dont bother Nicolas please ;-) + * * 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. @@ -25,124 +29,16 @@ #include #include #include - -#include -#include - - -/* - * Interesting UCB1400 AC-link registers - */ - -#define UCB_IE_RIS 0x5e -#define UCB_IE_FAL 0x60 -#define UCB_IE_STATUS 0x62 -#define UCB_IE_CLEAR 0x62 -#define UCB_IE_ADC (1 << 11) -#define UCB_IE_TSPX (1 << 12) - -#define UCB_TS_CR 0x64 -#define UCB_TS_CR_TSMX_POW (1 << 0) -#define UCB_TS_CR_TSPX_POW (1 << 1) -#define UCB_TS_CR_TSMY_POW (1 << 2) -#define UCB_TS_CR_TSPY_POW (1 << 3) -#define UCB_TS_CR_TSMX_GND (1 << 4) -#define UCB_TS_CR_TSPX_GND (1 << 5) -#define UCB_TS_CR_TSMY_GND (1 << 6) -#define UCB_TS_CR_TSPY_GND (1 << 7) -#define UCB_TS_CR_MODE_INT (0 << 8) -#define UCB_TS_CR_MODE_PRES (1 << 8) -#define UCB_TS_CR_MODE_POS (2 << 8) -#define UCB_TS_CR_BIAS_ENA (1 << 11) -#define UCB_TS_CR_TSPX_LOW (1 << 12) -#define UCB_TS_CR_TSMX_LOW (1 << 13) - -#define UCB_ADC_CR 0x66 -#define UCB_ADC_SYNC_ENA (1 << 0) -#define UCB_ADC_VREFBYP_CON (1 << 1) -#define UCB_ADC_INP_TSPX (0 << 2) -#define UCB_ADC_INP_TSMX (1 << 2) -#define UCB_ADC_INP_TSPY (2 << 2) -#define UCB_ADC_INP_TSMY (3 << 2) -#define UCB_ADC_INP_AD0 (4 << 2) -#define UCB_ADC_INP_AD1 (5 << 2) -#define UCB_ADC_INP_AD2 (6 << 2) -#define UCB_ADC_INP_AD3 (7 << 2) -#define UCB_ADC_EXT_REF (1 << 5) -#define UCB_ADC_START (1 << 7) -#define UCB_ADC_ENA (1 << 15) - -#define UCB_ADC_DATA 0x68 -#define UCB_ADC_DAT_VALID (1 << 15) -#define UCB_ADC_DAT_VALUE(x) ((x) & 0x3ff) - -#define UCB_ID 0x7e -#define UCB_ID_1400 0x4304 - - -struct ucb1400 { - struct snd_ac97 *ac97; - struct input_dev *ts_idev; - - int irq; - - wait_queue_head_t ts_wait; - struct task_struct *ts_task; - - unsigned int irq_pending; /* not bit field shared */ - unsigned int ts_restart:1; - unsigned int adcsync:1; -}; +#include static int adcsync; static int ts_delay = 55; /* us */ static int ts_delay_pressure; /* us */ -static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg) -{ - return ucb->ac97->bus->ops->read(ucb->ac97, reg); -} - -static inline void ucb1400_reg_write(struct ucb1400 *ucb, u16 reg, u16 val) -{ - ucb->ac97->bus->ops->write(ucb->ac97, reg, val); -} - -static inline void ucb1400_adc_enable(struct ucb1400 *ucb) -{ - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); -} - -static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel) -{ - unsigned int val; - - if (ucb->adcsync) - adc_channel |= UCB_ADC_SYNC_ENA; - - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel); - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | UCB_ADC_START); - - for (;;) { - val = ucb1400_reg_read(ucb, UCB_ADC_DATA); - if (val & UCB_ADC_DAT_VALID) - break; - /* yield to other processes */ - schedule_timeout_uninterruptible(1); - } - - return UCB_ADC_DAT_VALUE(val); -} - -static inline void ucb1400_adc_disable(struct ucb1400 *ucb) -{ - ucb1400_reg_write(ucb, UCB_ADC_CR, 0); -} - /* Switch to interrupt mode. */ -static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb) +static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ac97, UCB_TS_CR, UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_INT); @@ -152,14 +48,14 @@ static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb) * Switch to pressure mode, and read pressure. We don't need to wait * here, since both plates are being driven. */ -static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); udelay(ts_delay_pressure); - return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); } /* @@ -168,21 +64,21 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb) * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise. */ -static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); udelay(ts_delay); - return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); } /* @@ -191,63 +87,63 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb) * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise. */ -static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); udelay(ts_delay); - return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX); + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync); } /* * Switch to X plate resistance mode. Set MX to ground, PX to * supply. Measure current. */ -static inline unsigned int ucb1400_ts_read_xres(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - return ucb1400_adc_read(ucb, 0); + return ucb1400_adc_read(ucb->ac97, 0, adcsync); } /* * Switch to Y plate resistance mode. Set MY to ground, PY to * supply. Measure current. */ -static inline unsigned int ucb1400_ts_read_yres(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - return ucb1400_adc_read(ucb, 0); + return ucb1400_adc_read(ucb->ac97, 0, adcsync); } -static inline int ucb1400_ts_pen_down(struct ucb1400 *ucb) +static inline int ucb1400_ts_pen_down(struct snd_ac97 *ac97) { - unsigned short val = ucb1400_reg_read(ucb, UCB_TS_CR); - return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); + unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR); + return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); } -static inline void ucb1400_ts_irq_enable(struct ucb1400 *ucb) +static inline void ucb1400_ts_irq_enable(struct snd_ac97 *ac97) { - ucb1400_reg_write(ucb, UCB_IE_CLEAR, UCB_IE_TSPX); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); - ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_TSPX); + ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX); + ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ac97, UCB_IE_FAL, UCB_IE_TSPX); } -static inline void ucb1400_ts_irq_disable(struct ucb1400 *ucb) +static inline void ucb1400_ts_irq_disable(struct snd_ac97 *ac97) { - ucb1400_reg_write(ucb, UCB_IE_FAL, 0); + ucb1400_reg_write(ac97, UCB_IE_FAL, 0); } static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y) @@ -264,25 +160,24 @@ static void ucb1400_ts_event_release(struct input_dev *idev) input_sync(idev); } -static void ucb1400_handle_pending_irq(struct ucb1400 *ucb) +static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb) { unsigned int isr; - isr = ucb1400_reg_read(ucb, UCB_IE_STATUS); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, isr); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); - if (isr & UCB_IE_TSPX) - ucb1400_ts_irq_disable(ucb); - else + if (isr & UCB_IE_TSPX) { + ucb1400_ts_irq_disable(ucb->ac97); + enable_irq(ucb->irq); + } else printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr); - - enable_irq(ucb->irq); } static int ucb1400_ts_thread(void *_ucb) { - struct ucb1400 *ucb = _ucb; + struct ucb1400_ts *ucb = _ucb; struct task_struct *tsk = current; int valid = 0; struct sched_param param = { .sched_priority = 1 }; @@ -301,19 +196,19 @@ static int ucb1400_ts_thread(void *_ucb) ucb1400_handle_pending_irq(ucb); } - ucb1400_adc_enable(ucb); + ucb1400_adc_enable(ucb->ac97); x = ucb1400_ts_read_xpos(ucb); y = ucb1400_ts_read_ypos(ucb); p = ucb1400_ts_read_pressure(ucb); - ucb1400_adc_disable(ucb); + ucb1400_adc_disable(ucb->ac97); /* Switch back to interrupt mode. */ - ucb1400_ts_mode_int(ucb); + ucb1400_ts_mode_int(ucb->ac97); msleep(10); - if (ucb1400_ts_pen_down(ucb)) { - ucb1400_ts_irq_enable(ucb); + if (ucb1400_ts_pen_down(ucb->ac97)) { + ucb1400_ts_irq_enable(ucb->ac97); /* * If we spat out a valid sample set last time, @@ -332,8 +227,8 @@ static int ucb1400_ts_thread(void *_ucb) } wait_event_freezable_timeout(ucb->ts_wait, - ucb->irq_pending || ucb->ts_restart || kthread_should_stop(), - timeout); + ucb->irq_pending || ucb->ts_restart || + kthread_should_stop(), timeout); } /* Send the "pen off" if we are stopping with the pen still active */ @@ -356,7 +251,7 @@ static int ucb1400_ts_thread(void *_ucb) */ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) { - struct ucb1400 *ucb = devid; + struct ucb1400_ts *ucb = devid; if (irqnr == ucb->irq) { disable_irq(ucb->irq); @@ -369,7 +264,7 @@ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) static int ucb1400_ts_open(struct input_dev *idev) { - struct ucb1400 *ucb = input_get_drvdata(idev); + struct ucb1400_ts *ucb = input_get_drvdata(idev); int ret = 0; BUG_ON(ucb->ts_task); @@ -385,34 +280,14 @@ static int ucb1400_ts_open(struct input_dev *idev) static void ucb1400_ts_close(struct input_dev *idev) { - struct ucb1400 *ucb = input_get_drvdata(idev); + struct ucb1400_ts *ucb = input_get_drvdata(idev); if (ucb->ts_task) kthread_stop(ucb->ts_task); - ucb1400_ts_irq_disable(ucb); - ucb1400_reg_write(ucb, UCB_TS_CR, 0); -} - -#ifdef CONFIG_PM -static int ucb1400_ts_resume(struct device *dev) -{ - struct ucb1400 *ucb = dev_get_drvdata(dev); - - if (ucb->ts_task) { - /* - * Restart the TS thread to ensure the - * TS interrupt mode is set up again - * after sleep. - */ - ucb->ts_restart = 1; - wake_up(&ucb->ts_wait); - } - return 0; + ucb1400_ts_irq_disable(ucb->ac97); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); } -#else -#define ucb1400_ts_resume NULL -#endif #ifndef NO_IRQ #define NO_IRQ 0 @@ -422,25 +297,26 @@ static int ucb1400_ts_resume(struct device *dev) * Try to probe our interrupt, rather than relying on lots of * hard-coded machine dependencies. */ -static int ucb1400_detect_irq(struct ucb1400 *ucb) +static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb) { unsigned long mask, timeout; mask = probe_irq_on(); /* Enable the ADC interrupt. */ - ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); - ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); /* Cause an ADC interrupt. */ - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); /* Wait for the conversion to complete. */ timeout = jiffies + HZ/2; - while (!(ucb1400_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VALID)) { + while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) & + UCB_ADC_DAT_VALID)) { cpu_relax(); if (time_after(jiffies, timeout)) { printk(KERN_ERR "ucb1400: timed out in IRQ probe\n"); @@ -448,13 +324,13 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) return -ENODEV; } } - ucb1400_reg_write(ucb, UCB_ADC_CR, 0); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0); /* Disable and clear interrupt. */ - ucb1400_reg_write(ucb, UCB_IE_RIS, 0); - ucb1400_reg_write(ucb, UCB_IE_FAL, 0); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); /* Read triggered interrupt. */ ucb->irq = probe_irq_off(mask); @@ -464,36 +340,25 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) return 0; } -static int ucb1400_ts_probe(struct device *dev) +static int ucb1400_ts_probe(struct platform_device *dev) { - struct ucb1400 *ucb; - struct input_dev *idev; - int error, id, x_res, y_res; + int error, x_res, y_res; + struct ucb1400_ts *ucb = dev->dev.platform_data; - ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); - idev = input_allocate_device(); - if (!ucb || !idev) { + ucb->ts_idev = input_allocate_device(); + if (!ucb->ts_idev) { error = -ENOMEM; - goto err_free_devs; + goto err; } - ucb->ts_idev = idev; - ucb->adcsync = adcsync; - ucb->ac97 = to_ac97_t(dev); - init_waitqueue_head(&ucb->ts_wait); - - id = ucb1400_reg_read(ucb, UCB_ID); - if (id != UCB_ID_1400) { - error = -ENODEV; - goto err_free_devs; - } - - error = ucb1400_detect_irq(ucb); + error = ucb1400_ts_detect_irq(ucb); if (error) { printk(KERN_ERR "UCB1400: IRQ probe failed\n"); goto err_free_devs; } + init_waitqueue_head(&ucb->ts_wait); + error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING, "UCB1400", ucb); if (error) { @@ -503,80 +368,101 @@ static int ucb1400_ts_probe(struct device *dev) } printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); - input_set_drvdata(idev, ucb); + input_set_drvdata(ucb->ts_idev, ucb); - idev->dev.parent = dev; - idev->name = "UCB1400 touchscreen interface"; - idev->id.vendor = ucb1400_reg_read(ucb, AC97_VENDOR_ID1); - idev->id.product = id; - idev->open = ucb1400_ts_open; - idev->close = ucb1400_ts_close; - idev->evbit[0] = BIT_MASK(EV_ABS); + ucb->ts_idev->dev.parent = &dev->dev; + ucb->ts_idev->name = "UCB1400 touchscreen interface"; + ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97, + AC97_VENDOR_ID1); + ucb->ts_idev->id.product = ucb->id; + ucb->ts_idev->open = ucb1400_ts_open; + ucb->ts_idev->close = ucb1400_ts_close; + ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS); - ucb1400_adc_enable(ucb); + ucb1400_adc_enable(ucb->ac97); x_res = ucb1400_ts_read_xres(ucb); y_res = ucb1400_ts_read_yres(ucb); - ucb1400_adc_disable(ucb); + ucb1400_adc_disable(ucb->ac97); printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res); - input_set_abs_params(idev, ABS_X, 0, x_res, 0, 0); - input_set_abs_params(idev, ABS_Y, 0, y_res, 0, 0); - input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); - error = input_register_device(idev); + error = input_register_device(ucb->ts_idev); if (error) goto err_free_irq; - dev_set_drvdata(dev, ucb); return 0; - err_free_irq: +err_free_irq: free_irq(ucb->irq, ucb); - err_free_devs: - input_free_device(idev); - kfree(ucb); +err_free_devs: + input_free_device(ucb->ts_idev); +err: return error; + } -static int ucb1400_ts_remove(struct device *dev) +static int ucb1400_ts_remove(struct platform_device *dev) { - struct ucb1400 *ucb = dev_get_drvdata(dev); + struct ucb1400_ts *ucb = dev->dev.platform_data; free_irq(ucb->irq, ucb); input_unregister_device(ucb->ts_idev); - dev_set_drvdata(dev, NULL); - kfree(ucb); return 0; } -static struct device_driver ucb1400_ts_driver = { - .name = "ucb1400_ts", - .owner = THIS_MODULE, - .bus = &ac97_bus_type, - .probe = ucb1400_ts_probe, - .remove = ucb1400_ts_remove, - .resume = ucb1400_ts_resume, +#ifdef CONFIG_PM +static int ucb1400_ts_resume(struct platform_device *dev) +{ + struct ucb1400_ts *ucb = platform_get_drvdata(dev); + + if (ucb->ts_task) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ucb->ts_restart = 1; + wake_up(&ucb->ts_wait); + } + return 0; +} +#else +#define ucb1400_ts_resume NULL +#endif + +static struct platform_driver ucb1400_ts_driver = { + .probe = ucb1400_ts_probe, + .remove = ucb1400_ts_remove, + .resume = ucb1400_ts_resume, + .driver = { + .name = "ucb1400_ts", + }, }; static int __init ucb1400_ts_init(void) { - return driver_register(&ucb1400_ts_driver); + return platform_driver_register(&ucb1400_ts_driver); } static void __exit ucb1400_ts_exit(void) { - driver_unregister(&ucb1400_ts_driver); + platform_driver_unregister(&ucb1400_ts_driver); } module_param(adcsync, bool, 0444); MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); module_param(ts_delay, int, 0444); -MODULE_PARM_DESC(ts_delay, "Delay between panel setup and position read. Default = 55us."); +MODULE_PARM_DESC(ts_delay, "Delay between panel setup and" + " position read. Default = 55us."); module_param(ts_delay_pressure, int, 0444); MODULE_PARM_DESC(ts_delay_pressure, - "delay between panel setup and pressure read. Default = 0us."); + "delay between panel setup and pressure read." + " Default = 0us."); module_init(ucb1400_ts_init); module_exit(ucb1400_ts_exit); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 883e7ea31de2..371d22a98f19 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -50,6 +50,15 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. +config UCB1400_CORE + tristate "Philips UCB1400 Core driver" + help + This enables support for the Philips UCB1400 core functions. + The UCB1400 is an AC97 audio codec. + + To compile this driver as a module, choose M here: the + module will be called ucb1400_core. + config MFD_TC6393XB bool "Support Toshiba TC6393XB" depends on GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 33daa2f45dd8..f7cfd5b4119c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o ifeq ($(CONFIG_SA1100_ASSABET),y) obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o endif +obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c new file mode 100644 index 000000000000..178159e264ce --- /dev/null +++ b/drivers/mfd/ucb1400_core.c @@ -0,0 +1,106 @@ +/* + * Core functions for: + * Philips UCB1400 multifunction chip + * + * Based on ucb1400_ts.c: + * Author: Nicolas Pitre + * Created: September 25, 2006 + * Copyright: MontaVista Software, Inc. + * + * Spliting done by: Marek Vasut + * If something doesnt work and it worked before spliting, e-mail me, + * dont bother Nicolas please ;-) + * + * 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. + * + * This code is heavily based on ucb1x00-*.c copyrighted by Russell King + * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has + * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request. + */ + +#include +#include + +static int ucb1400_core_probe(struct device *dev) +{ + int err; + struct ucb1400 *ucb; + struct ucb1400_ts ucb_ts; + struct snd_ac97 *ac97; + + memset(&ucb_ts, 0, sizeof(ucb_ts)); + + ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); + if (!ucb) { + err = -ENOMEM; + goto err; + } + + dev_set_drvdata(dev, ucb); + + ac97 = to_ac97_t(dev); + + ucb_ts.id = ucb1400_reg_read(ac97, UCB_ID); + if (ucb_ts.id != UCB_ID_1400) { + err = -ENODEV; + goto err0; + } + + /* TOUCHSCREEN */ + ucb_ts.ac97 = ac97; + ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1); + if (!ucb->ucb1400_ts) { + err = -ENOMEM; + goto err0; + } + err = platform_device_add_data(ucb->ucb1400_ts, &ucb_ts, + sizeof(ucb_ts)); + if (err) + goto err1; + err = platform_device_add(ucb->ucb1400_ts); + if (err) + goto err1; + + return 0; + +err1: + platform_device_put(ucb->ucb1400_ts); +err0: + kfree(ucb); +err: + return err; +} + +static int ucb1400_core_remove(struct device *dev) +{ + struct ucb1400 *ucb = dev_get_drvdata(dev); + + platform_device_unregister(ucb->ucb1400_ts); + kfree(ucb); + return 0; +} + +static struct device_driver ucb1400_core_driver = { + .name = "ucb1400_core", + .bus = &ac97_bus_type, + .probe = ucb1400_core_probe, + .remove = ucb1400_core_remove, +}; + +static int __init ucb1400_core_init(void) +{ + return driver_register(&ucb1400_core_driver); +} + +static void __exit ucb1400_core_exit(void) +{ + driver_unregister(&ucb1400_core_driver); +} + +module_init(ucb1400_core_init); +module_exit(ucb1400_core_exit); + +MODULE_DESCRIPTION("Philips UCB1400 driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h new file mode 100644 index 000000000000..970473bf8d5a --- /dev/null +++ b/include/linux/ucb1400.h @@ -0,0 +1,161 @@ +/* + * Register definitions and functions for: + * Philips UCB1400 driver + * + * Based on ucb1400_ts: + * Author: Nicolas Pitre + * Created: September 25, 2006 + * Copyright: MontaVista Software, Inc. + * + * Spliting done by: Marek Vasut + * If something doesnt work and it worked before spliting, e-mail me, + * dont bother Nicolas please ;-) + * + * 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. + * + * This code is heavily based on ucb1x00-*.c copyrighted by Russell King + * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has + * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request. + */ + +#ifndef _LINUX__UCB1400_H +#define _LINUX__UCB1400_H + +#include +#include +#include + +/* + * UCB1400 AC-link registers + */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) + +#define UCB_TS_CR 0x64 +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_CR 0x66 +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DATA 0x68 +#define UCB_ADC_DAT_VALID (1 << 15) +#define UCB_ADC_DAT_MASK 0x3ff + +#define UCB_ID 0x7e +#define UCB_ID_1400 0x4304 + +struct ucb1400_ts { + struct input_dev *ts_idev; + struct task_struct *ts_task; + int id; + wait_queue_head_t ts_wait; + unsigned int ts_restart:1; + int irq; + unsigned int irq_pending; /* not bit field shared */ + struct snd_ac97 *ac97; +}; + +struct ucb1400 { + struct platform_device *ucb1400_ts; +}; + +static inline u16 ucb1400_reg_read(struct snd_ac97 *ac97, u16 reg) +{ + return ac97->bus->ops->read(ac97, reg); +} + +static inline void ucb1400_reg_write(struct snd_ac97 *ac97, u16 reg, u16 val) +{ + ac97->bus->ops->write(ac97, reg, val); +} + +static inline u16 ucb1400_gpio_get_value(struct snd_ac97 *ac97, u16 gpio) +{ + return ucb1400_reg_read(ac97, UCB_IO_DATA) & (1 << gpio); +} + +static inline void ucb1400_gpio_set_value(struct snd_ac97 *ac97, u16 gpio, + u16 val) +{ + ucb1400_reg_write(ac97, UCB_IO_DATA, val ? + ucb1400_reg_read(ac97, UCB_IO_DATA) | (1 << gpio) : + ucb1400_reg_read(ac97, UCB_IO_DATA) & ~(1 << gpio)); +} + +static inline u16 ucb1400_gpio_get_direction(struct snd_ac97 *ac97, u16 gpio) +{ + return ucb1400_reg_read(ac97, UCB_IO_DIR) & (1 << gpio); +} + +static inline void ucb1400_gpio_set_direction(struct snd_ac97 *ac97, u16 gpio, + u16 dir) +{ + ucb1400_reg_write(ac97, UCB_IO_DIR, dir ? + ucb1400_reg_read(ac97, UCB_IO_DIR) | (1 << gpio) : + ucb1400_reg_read(ac97, UCB_IO_DIR) & ~(1 << gpio)); +} + +static inline void ucb1400_adc_enable(struct snd_ac97 *ac97) +{ + ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA); +} + +static unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel, + int adcsync) +{ + unsigned int val; + + if (adcsync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel); + ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | + UCB_ADC_START); + + while (!((val = ucb1400_reg_read(ac97, UCB_ADC_DATA)) + & UCB_ADC_DAT_VALID)) + schedule_timeout_uninterruptible(1); + + return val & UCB_ADC_DAT_MASK; +} + +static inline void ucb1400_adc_disable(struct snd_ac97 *ac97) +{ + ucb1400_reg_write(ac97, UCB_ADC_CR, 0); +} + +#endif -- cgit v1.2.3 From 2d70b68d42b5196a48ccb639e3797f097ef5bea3 Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Wed, 20 Aug 2008 14:09:17 -0700 Subject: fix setpriority(PRIO_PGRP) thread iterator breakage When user calls sys_setpriority(PRIO_PGRP ...) on a NPTL style multi-LWP process, only the task leader of the process is affected, all other sibling LWP threads didn't receive the setting. The problem was that the iterator used in sys_setpriority() only iteartes over one task for each process, ignoring all other sibling thread. Introduce a new macro do_each_pid_thread / while_each_pid_thread to walk each thread of a process. Convert 4 call sites in {set/get}priority and ioprio_{set/get}. Signed-off-by: Ken Chen Cc: Oleg Nesterov Cc: Roland McGrath Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ioprio.c | 8 ++++---- include/linux/pid.h | 9 +++++++++ kernel/sys.c | 8 ++++---- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/ioprio.c b/fs/ioprio.c index c4a1c3c65aac..da3cc460d4df 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -115,11 +115,11 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio) pgrp = task_pgrp(current); else pgrp = find_vpid(who); - do_each_pid_task(pgrp, PIDTYPE_PGID, p) { + do_each_pid_thread(pgrp, PIDTYPE_PGID, p) { ret = set_task_ioprio(p, ioprio); if (ret) break; - } while_each_pid_task(pgrp, PIDTYPE_PGID, p); + } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case IOPRIO_WHO_USER: if (!who) @@ -204,7 +204,7 @@ asmlinkage long sys_ioprio_get(int which, int who) pgrp = task_pgrp(current); else pgrp = find_vpid(who); - do_each_pid_task(pgrp, PIDTYPE_PGID, p) { + do_each_pid_thread(pgrp, PIDTYPE_PGID, p) { tmpio = get_task_ioprio(p); if (tmpio < 0) continue; @@ -212,7 +212,7 @@ asmlinkage long sys_ioprio_get(int which, int who) ret = tmpio; else ret = ioprio_best(ret, tmpio); - } while_each_pid_task(pgrp, PIDTYPE_PGID, p); + } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case IOPRIO_WHO_USER: if (!who) diff --git a/include/linux/pid.h b/include/linux/pid.h index 22921ac4cfd9..d7e98ff8021e 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -161,4 +161,13 @@ pid_t pid_vnr(struct pid *pid); } \ } while (0) +#define do_each_pid_thread(pid, type, task) \ + do_each_pid_task(pid, type, task) { \ + struct task_struct *tg___ = task; \ + do { + +#define while_each_pid_thread(pid, type, task) \ + } while_each_thread(tg___, task); \ + task = tg___; \ + } while_each_pid_task(pid, type, task) #endif /* _LINUX_PID_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 3dacb00a7f76..038a7bc0901d 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -169,9 +169,9 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) pgrp = find_vpid(who); else pgrp = task_pgrp(current); - do_each_pid_task(pgrp, PIDTYPE_PGID, p) { + do_each_pid_thread(pgrp, PIDTYPE_PGID, p) { error = set_one_prio(p, niceval, error); - } while_each_pid_task(pgrp, PIDTYPE_PGID, p); + } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case PRIO_USER: user = current->user; @@ -229,11 +229,11 @@ asmlinkage long sys_getpriority(int which, int who) pgrp = find_vpid(who); else pgrp = task_pgrp(current); - do_each_pid_task(pgrp, PIDTYPE_PGID, p) { + do_each_pid_thread(pgrp, PIDTYPE_PGID, p) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; - } while_each_pid_task(pgrp, PIDTYPE_PGID, p); + } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case PRIO_USER: user = current->user; -- cgit v1.2.3 From 479db0bf408e65baa14d2a9821abfcbc0804b847 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Wed, 20 Aug 2008 14:09:18 -0700 Subject: mm: dirty page tracking race fix There is a race with dirty page accounting where a page may not properly be accounted for. clear_page_dirty_for_io() calls page_mkclean; then TestClearPageDirty. page_mkclean walks the rmaps for that page, and for each one it cleans and write protects the pte if it was dirty. It uses page_check_address to find the pte. That function has a shortcut to avoid the ptl if the pte is not present. Unfortunately, the pte can be switched to not-present then back to present by other code while holding the page table lock -- this should not be a signal for page_mkclean to ignore that pte, because it may be dirty. For example, powerpc64's set_pte_at will clear a previously present pte before setting it to the desired value. There may also be other code in core mm or in arch which do similar things. The consequence of the bug is loss of data integrity due to msync, and loss of dirty page accounting accuracy. XIP's __xip_unmap could easily also be unreliable (depending on the exact XIP locking scheme), which can lead to data corruption. Fix this by having an option to always take ptl to check the pte in page_check_address. It's possible to retain this optimization for page_referenced and try_to_unmap. Signed-off-by: Nick Piggin Cc: Jared Hulbert Cc: Carsten Otte Cc: Hugh Dickins Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rmap.h | 2 +- mm/filemap_xip.c | 2 +- mm/rmap.c | 14 +++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 69407f85e10b..fed6f5e0b411 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -102,7 +102,7 @@ int try_to_unmap(struct page *, int ignore_refs); * Called from mm/filemap_xip.c to unmap empty zero page */ pte_t *page_check_address(struct page *, struct mm_struct *, - unsigned long, spinlock_t **); + unsigned long, spinlock_t **, int); /* * Used by swapoff to help locate where page is expected in vma. diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 380ab402d711..8b710ca13247 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -185,7 +185,7 @@ __xip_unmap (struct address_space * mapping, address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); BUG_ON(address < vma->vm_start || address >= vma->vm_end); - pte = page_check_address(page, mm, address, &ptl); + pte = page_check_address(page, mm, address, &ptl, 1); if (pte) { /* Nuke the page table entry. */ flush_cache_page(vma, address, pte_pfn(*pte)); diff --git a/mm/rmap.c b/mm/rmap.c index 059774712c08..0383acfcb068 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -224,10 +224,14 @@ unsigned long page_address_in_vma(struct page *page, struct vm_area_struct *vma) /* * Check that @page is mapped at @address into @mm. * + * If @sync is false, page_check_address may perform a racy check to avoid + * the page table lock when the pte is not present (helpful when reclaiming + * highly shared pages). + * * On success returns with pte mapped and locked. */ pte_t *page_check_address(struct page *page, struct mm_struct *mm, - unsigned long address, spinlock_t **ptlp) + unsigned long address, spinlock_t **ptlp, int sync) { pgd_t *pgd; pud_t *pud; @@ -249,7 +253,7 @@ pte_t *page_check_address(struct page *page, struct mm_struct *mm, pte = pte_offset_map(pmd, address); /* Make a quick check before getting the lock */ - if (!pte_present(*pte)) { + if (!sync && !pte_present(*pte)) { pte_unmap(pte); return NULL; } @@ -281,7 +285,7 @@ static int page_referenced_one(struct page *page, if (address == -EFAULT) goto out; - pte = page_check_address(page, mm, address, &ptl); + pte = page_check_address(page, mm, address, &ptl, 0); if (!pte) goto out; @@ -450,7 +454,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma) if (address == -EFAULT) goto out; - pte = page_check_address(page, mm, address, &ptl); + pte = page_check_address(page, mm, address, &ptl, 1); if (!pte) goto out; @@ -704,7 +708,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, if (address == -EFAULT) goto out; - pte = page_check_address(page, mm, address, &ptl); + pte = page_check_address(page, mm, address, &ptl, 0); if (!pte) goto out; -- cgit v1.2.3 From d847471d063663b9f36927d265c66a270c0cfaab Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 20 Aug 2008 14:09:23 -0700 Subject: fbdefio: add set_page_dirty handler to deferred IO FB Fixes kernel BUG at lib/radix-tree.c:473. Previously the handler was incidentally provided by tmpfs but this was removed with: commit 14fcc23fdc78e9d32372553ccf21758a9bd56fa1 Author: Hugh Dickins Date: Mon Jul 28 15:46:19 2008 -0700 tmpfs: fix kernel BUG in shmem_delete_inode relying on this behaviour was incorrect in any case and the BUG also appeared when the device node was on an ext3 filesystem. v2: override a_ops at open() time rather than mmap() time to minimise races per AKPM's concerns. Signed-off-by: Ian Campbell Cc: Jaya Kumar Cc: Nick Piggin Cc: Peter Zijlstra Cc: Hugh Dickins Cc: Johannes Weiner Cc: Jeremy Fitzhardinge Cc: Kel Modderman Cc: Markus Armbruster Cc: Krzysztof Helt Cc: [14fcc23fd is in 2.6.25.14 and 2.6.26.1] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/fb_defio.c | 19 +++++++++++++++++++ drivers/video/fbmem.c | 4 ++++ include/linux/fb.h | 3 +++ 3 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 59df132cc375..4835bdc4e9f1 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -114,6 +114,17 @@ static struct vm_operations_struct fb_deferred_io_vm_ops = { .page_mkwrite = fb_deferred_io_mkwrite, }; +static int fb_deferred_io_set_page_dirty(struct page *page) +{ + if (!PageDirty(page)) + SetPageDirty(page); + return 0; +} + +static const struct address_space_operations fb_deferred_io_aops = { + .set_page_dirty = fb_deferred_io_set_page_dirty, +}; + static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) { vma->vm_ops = &fb_deferred_io_vm_ops; @@ -163,6 +174,14 @@ void fb_deferred_io_init(struct fb_info *info) } EXPORT_SYMBOL_GPL(fb_deferred_io_init); +void fb_deferred_io_open(struct fb_info *info, + struct inode *inode, + struct file *file) +{ + file->f_mapping->a_ops = &fb_deferred_io_aops; +} +EXPORT_SYMBOL_GPL(fb_deferred_io_open); + void fb_deferred_io_cleanup(struct fb_info *info) { void *screen_base = (void __force *) info->screen_base; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 6b487801eeae..98843c2ecf73 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1344,6 +1344,10 @@ fb_open(struct inode *inode, struct file *file) if (res) module_put(info->fbops->owner); } +#ifdef CONFIG_FB_DEFERRED_IO + if (info->fbdefio) + fb_deferred_io_open(info, inode, file); +#endif out: unlock_kernel(); return res; diff --git a/include/linux/fb.h b/include/linux/fb.h index 3b8870e32afd..531ccd5f5960 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -976,6 +976,9 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, /* drivers/video/fb_defio.c */ extern void fb_deferred_io_init(struct fb_info *info); +extern void fb_deferred_io_open(struct fb_info *info, + struct inode *inode, + struct file *file); extern void fb_deferred_io_cleanup(struct fb_info *info); extern int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync); -- cgit v1.2.3 From 3c4fbe5e01d7e5309be5045e7ae0db20a049e6dc Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Wed, 20 Aug 2008 16:37:38 -0700 Subject: nohz: fix wrong event handler after online an offlined cpu On the tickless system(CONFIG_NO_HZ=y and CONFIG_HIGH_RES_TIMERS=n), after I made an offlined cpu online, I found this cpu's event handler was tick_handle_periodic, not tick_nohz_handler. After debuging, I found this bug was caused by the wrong tick mode. the tick mode is not changed to NOHZ_MODE_INACTIVE when the cpu is offline. This patch fixes this bug. Signed-off-by: Miao Xie Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar --- include/linux/tick.h | 5 ++++- kernel/time/tick-sched.c | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tick.h b/include/linux/tick.h index d3c02695dc5d..8cf8cfe2cc97 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -74,10 +74,13 @@ extern struct tick_device *tick_get_device(int cpu); extern int tick_init_highres(void); extern int tick_program_event(ktime_t expires, int force); extern void tick_setup_sched_timer(void); +# endif + +# if defined CONFIG_NO_HZ || defined CONFIG_HIGH_RES_TIMERS extern void tick_cancel_sched_timer(int cpu); # else static inline void tick_cancel_sched_timer(int cpu) { } -# endif /* HIGHRES */ +# endif # ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST extern struct tick_device *tick_get_broadcast_device(void); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index f5da526424a9..7a46bde78c66 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -643,17 +643,21 @@ void tick_setup_sched_timer(void) ts->nohz_mode = NOHZ_MODE_HIGHRES; #endif } +#endif /* HIGH_RES_TIMERS */ +#if defined CONFIG_NO_HZ || defined CONFIG_HIGH_RES_TIMERS void tick_cancel_sched_timer(int cpu) { struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); +# ifdef CONFIG_HIGH_RES_TIMERS if (ts->sched_timer.base) hrtimer_cancel(&ts->sched_timer); +# endif ts->nohz_mode = NOHZ_MODE_INACTIVE; } -#endif /* HIGH_RES_TIMERS */ +#endif /** * Async notification about clocksource changes -- cgit v1.2.3 From bf9ca69fc8d19d4034391d3df4c35dccdef9d28c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 30 Jul 2008 12:29:21 -0700 Subject: dev_printk(): constify the `dev' argument Add const markings to dev_name and dev_driver_string to make it clear that dev_printk doesn't modify dev. This is a prerequisite to adding more const markings to other functions make it clearer, which functions can modify dev and which can't. Signed-off-by: Jean Delvare Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- include/linux/device.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index 068aa1c9538c..44bad73d8192 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -53,7 +53,7 @@ static inline int device_is_not_partition(struct device *dev) * it is attached to. If it is not attached to a bus either, an empty * string will be returned. */ -const char *dev_driver_string(struct device *dev) +const char *dev_driver_string(const struct device *dev) { return dev->driver ? dev->driver->name : (dev->bus ? dev->bus->name : diff --git a/include/linux/device.h b/include/linux/device.h index d24a47f80f9c..a701c1b5184c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -406,7 +406,7 @@ struct device { /* Get the wakeup routines, which depend on struct device */ #include -static inline const char *dev_name(struct device *dev) +static inline const char *dev_name(const struct device *dev) { /* will be changed into kobject_name(&dev->kobj) in the near future */ return dev->bus_id; @@ -518,7 +518,7 @@ extern void device_shutdown(void); extern void sysdev_shutdown(void); /* debugging and troubleshooting/diagnostic helpers. */ -extern const char *dev_driver_string(struct device *dev); +extern const char *dev_driver_string(const struct device *dev); #define dev_printk(level, dev, format, arg...) \ printk(level "%s %s: " format , dev_driver_string(dev) , \ dev_name(dev) , ## arg) -- cgit v1.2.3 From c906a48adc74fc455378137ac5124b13e7030a15 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 30 May 2008 10:45:12 -0700 Subject: driver core: add init_name to struct device This gives us a way to handle both the bus_id and init_name values being used for a while during the transition period. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 20 +++++++++++++------- include/linux/device.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index 40041319657d..d021c98605b3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -844,13 +844,19 @@ int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; - int error; + int error = -EINVAL; dev = get_device(dev); - if (!dev || !strlen(dev->bus_id)) { - error = -EINVAL; - goto Done; - } + if (!dev) + goto done; + + /* Temporarily support init_name if it is set. + * It will override bus_id for now */ + if (dev->init_name) + dev_set_name(dev, "%s", dev->init_name); + + if (!strlen(dev->bus_id)) + goto done; pr_debug("device: '%s': %s\n", dev->bus_id, __func__); @@ -919,7 +925,7 @@ int device_add(struct device *dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->class_mutex); } - Done: +done: put_device(dev); return error; DPMError: @@ -946,7 +952,7 @@ int device_add(struct device *dev) cleanup_device_parent(dev); if (parent) put_device(parent); - goto Done; + goto done; } /** diff --git a/include/linux/device.h b/include/linux/device.h index a701c1b5184c..4d8372d135df 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -358,6 +358,7 @@ struct device { struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ + const char *init_name; /* initial name of the device */ struct device_type *type; unsigned uevent_suppress:1; -- cgit v1.2.3 From 55151d7daba185f94e9dc561a5a2ba36b5f647dd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 12 Aug 2008 14:33:59 -0400 Subject: USB: Defer Set-Interface for suspended devices This patch (as1128) fixes one of the problems related to the new PM infrastructure. We are not allowed to register new child devices during the middle of a system sleep transition, but unbinding a USB driver causes the core to automatically install altsetting 0 and thereby create new endpoint pseudo-devices. The patch fixes this problem (and the related problem that installing altsetting 0 will fail if the device is suspended) by deferring the Set-Interface call until some later time when it is legal and can succeed. Possible later times are: when a new driver is being probed for the interface, and when the interface is being resumed. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 31 ++++++++++++++++++++++++++++--- include/linux/usb.h | 3 +++ 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ed1cc8530a93..637b2bea5563 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -230,6 +230,13 @@ static int usb_probe_interface(struct device *dev) */ intf->pm_usage_cnt = !(driver->supports_autosuspend); + /* Carry out a deferred switch to altsetting 0 */ + if (intf->needs_altsetting0) { + usb_set_interface(udev, intf->altsetting[0]. + desc.bInterfaceNumber, 0); + intf->needs_altsetting0 = 0; + } + error = driver->probe(intf, id); if (error) { mark_quiesced(intf); @@ -266,8 +273,17 @@ static int usb_unbind_interface(struct device *dev) driver->disconnect(intf); - /* reset other interface state */ - usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); + /* Reset other interface state. + * We cannot do a Set-Interface if the device is suspended or + * if it is prepared for a system sleep (since installing a new + * altsetting means creating new endpoint device entries). + * When either of these happens, defer the Set-Interface. + */ + if (!error && intf->dev.power.status == DPM_ON) + usb_set_interface(udev, intf->altsetting[0]. + desc.bInterfaceNumber, 0); + else + intf->needs_altsetting0 = 1; usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; @@ -975,8 +991,17 @@ static int usb_resume_interface(struct usb_device *udev, goto done; /* Can't resume it if it doesn't have a driver. */ - if (intf->condition == USB_INTERFACE_UNBOUND) + if (intf->condition == USB_INTERFACE_UNBOUND) { + + /* Carry out a deferred switch to altsetting 0 */ + if (intf->needs_altsetting0 && + intf->dev.power.status == DPM_ON) { + usb_set_interface(udev, intf->altsetting[0]. + desc.bInterfaceNumber, 0); + intf->needs_altsetting0 = 0; + } goto done; + } /* Don't resume if the interface is marked for rebinding */ if (intf->needs_binding) diff --git a/include/linux/usb.h b/include/linux/usb.h index 0924cd9c30f6..94ac74aba6b6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -110,6 +110,8 @@ enum usb_interface_condition { * @sysfs_files_created: sysfs attributes exist * @needs_remote_wakeup: flag set when the driver requires remote-wakeup * capability during autosuspend. + * @needs_altsetting0: flag set when a set-interface request for altsetting 0 + * has been deferred. * @needs_binding: flag set when the driver should be re-probed or unbound * following a reset or suspend operation it doesn't support. * @dev: driver model's view of this device @@ -162,6 +164,7 @@ struct usb_interface { unsigned is_active:1; /* the interface is not suspended */ unsigned sysfs_files_created:1; /* the sysfs attributes exist */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ + unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ unsigned needs_binding:1; /* needs delayed unbind/rebind */ struct device dev; /* interface specific device info */ -- cgit v1.2.3 From 05944bdf6fadb5394710269df6770dde447b23ca Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 13 Aug 2008 20:19:09 +0900 Subject: libata: implement no[hs]rst force params Implement force params nohrst, nosrst and norst. This is to work around reset related problems and ease debugging. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- Documentation/kernel-parameters.txt | 3 +++ drivers/ata/libata-core.c | 46 ++++++++++++++++++++++++------------- drivers/ata/libata-eh.c | 4 ++++ include/linux/libata.h | 1 + 4 files changed, 38 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a8976467a983..1150444a21ab 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1074,6 +1074,9 @@ and is between 256 and 4096 characters. It is defined in the file * [no]ncq: Turn on or off NCQ. + * nohrst, nosrst, norst: suppress hard, soft + and both resets. + If there are multiple matching configurations changing the same attribute, the last one is used. diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5ba96c5052c8..dddcb9fde35a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -104,6 +104,7 @@ struct ata_force_param { unsigned long xfer_mask; unsigned int horkage_on; unsigned int horkage_off; + unsigned int lflags; }; struct ata_force_ent { @@ -196,22 +197,23 @@ void ata_force_cbl(struct ata_port *ap) } /** - * ata_force_spd_limit - force SATA spd limit according to libata.force + * ata_force_link_limits - force link limits according to libata.force * @link: ATA link of interest * - * Force SATA spd limit according to libata.force and whine about - * it. When only the port part is specified (e.g. 1:), the limit - * applies to all links connected to both the host link and all - * fan-out ports connected via PMP. If the device part is - * specified as 0 (e.g. 1.00:), it specifies the first fan-out - * link not the host link. Device number 15 always points to the - * host link whether PMP is attached or not. + * Force link flags and SATA spd limit according to libata.force + * and whine about it. When only the port part is specified + * (e.g. 1:), the limit applies to all links connected to both + * the host link and all fan-out ports connected via PMP. If the + * device part is specified as 0 (e.g. 1.00:), it specifies the + * first fan-out link not the host link. Device number 15 always + * points to the host link whether PMP is attached or not. * * LOCKING: * EH context. */ -static void ata_force_spd_limit(struct ata_link *link) +static void ata_force_link_limits(struct ata_link *link) { + bool did_spd = false; int linkno, i; if (ata_is_host_link(link)) @@ -228,13 +230,22 @@ static void ata_force_spd_limit(struct ata_link *link) if (fe->device != -1 && fe->device != linkno) continue; - if (!fe->param.spd_limit) - continue; + /* only honor the first spd limit */ + if (!did_spd && fe->param.spd_limit) { + link->hw_sata_spd_limit = (1 << fe->param.spd_limit) - 1; + ata_link_printk(link, KERN_NOTICE, + "FORCE: PHY spd limit set to %s\n", + fe->param.name); + did_spd = true; + } - link->hw_sata_spd_limit = (1 << fe->param.spd_limit) - 1; - ata_link_printk(link, KERN_NOTICE, - "FORCE: PHY spd limit set to %s\n", fe->param.name); - return; + /* let lflags stack */ + if (fe->param.lflags) { + link->flags |= fe->param.lflags; + ata_link_printk(link, KERN_NOTICE, + "FORCE: link flag 0x%x forced -> 0x%x\n", + fe->param.lflags, link->flags); + } } } @@ -5200,7 +5211,7 @@ int sata_link_init_spd(struct ata_link *link) if (spd) link->hw_sata_spd_limit &= (1 << spd) - 1; - ata_force_spd_limit(link); + ata_force_link_limits(link); link->sata_spd_limit = link->hw_sata_spd_limit; @@ -5991,6 +6002,9 @@ static int __init ata_parse_force_one(char **cur, { "udma133", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) }, { "udma/133", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) }, { "udma7", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 7) }, + { "nohrst", .lflags = ATA_LFLAG_NO_HRST }, + { "nosrst", .lflags = ATA_LFLAG_NO_SRST }, + { "norst", .lflags = ATA_LFLAG_NO_HRST | ATA_LFLAG_NO_SRST }, }; char *start = *cur, *p = *cur; char *id, *val, *endp; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 58bdc538d229..a570ca47e239 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2210,6 +2210,10 @@ int ata_eh_reset(struct ata_link *link, int classify, */ while (ata_eh_reset_timeouts[max_tries] != ULONG_MAX) max_tries++; + if (link->flags & ATA_LFLAG_NO_HRST) + hardreset = NULL; + if (link->flags & ATA_LFLAG_NO_SRST) + softreset = NULL; now = jiffies; deadline = ata_deadline(ehc->last_reset, ATA_EH_RESET_COOL_DOWN); diff --git a/include/linux/libata.h b/include/linux/libata.h index 06b80337303b..5340d4c83fd9 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -163,6 +163,7 @@ enum { ATA_DEV_NONE = 9, /* no device */ /* struct ata_link flags */ + ATA_LFLAG_NO_HRST = (1 << 1), /* avoid hardreset */ ATA_LFLAG_NO_SRST = (1 << 2), /* avoid softreset */ ATA_LFLAG_ASSUME_ATA = (1 << 3), /* assume ATA class */ ATA_LFLAG_ASSUME_SEMB = (1 << 4), /* assume SEMB class */ -- cgit v1.2.3 From d127ea7b8643a93d14d1f3c542974407f14d3663 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 31 Jul 2008 16:09:34 +0900 Subject: libata: restore SControl on detach Save SControl during probing and restore it on detach. This prevents adjustments made by libata drivers to seep into the next driver which gets attached (be it a libata one or not). It's not clear whether SControl also needs to be restored on suspend. The next system to have control (ACPI or kexec'd kernel) would probably like to see the original SControl value but there's no guarantee that a link is gonna keep working after SControl is adjusted without a reset and adding a reset and modified recovery cycle soley for this is an overkill. For now, do it only for detach. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 10 +++++----- include/linux/libata.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index dddcb9fde35a..5f8f57a818fb 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5199,15 +5199,14 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) */ int sata_link_init_spd(struct ata_link *link) { - u32 scontrol; u8 spd; int rc; - rc = sata_scr_read(link, SCR_CONTROL, &scontrol); + rc = sata_scr_read(link, SCR_CONTROL, &link->saved_scontrol); if (rc) return rc; - spd = (scontrol >> 4) & 0xf; + spd = (link->saved_scontrol >> 4) & 0xf; if (spd) link->hw_sata_spd_limit &= (1 << spd) - 1; @@ -5794,9 +5793,10 @@ static void ata_port_detach(struct ata_port *ap) ata_port_wait_eh(ap); /* EH is now guaranteed to see UNLOADING - EH context belongs - * to us. Disable all existing devices. + * to us. Restore SControl and disable all existing devices. */ - ata_port_for_each_link(link, ap) { + __ata_port_for_each_link(link, ap) { + sata_scr_write(link, SCR_CONTROL, link->saved_scontrol); ata_link_for_each_dev(dev, link) ata_dev_disable(dev); } diff --git a/include/linux/libata.h b/include/linux/libata.h index 5340d4c83fd9..80233fdc159f 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -647,6 +647,7 @@ struct ata_link { unsigned int flags; /* ATA_LFLAG_xxx */ + u32 saved_scontrol; /* SControl on probe */ unsigned int hw_sata_spd_limit; unsigned int sata_spd_limit; unsigned int sata_spd; /* current SATA PHY speed */ -- cgit v1.2.3 From b15b3ebae102f89c25ccbcae0b2099af312f2e82 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 1 Aug 2008 09:18:34 +0100 Subject: libata: Fix a large collection of DMA mode mismatches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dave Müller sent a diff for the pata_oldpiix that highlighted a problem where a lot of the ATA drivers assume dma_mode == 0 means "no DMA" while the core code uses 0xFF. This turns out to have other consequences such as code doing >= XFER_UDMA_0 also catching 0xFF as UDMAlots. Fortunately it doesn't generally affect set_dma_mode, although some drivers call back into their own set mode code from other points. Having been through the drivers I've added helpers for using_udma/using_mwdma dma_enabled so that people don't open code ranges that may change (eg if UDMA8 appears somewhere) Thanks to David for the initial bits [and added fix for pata_oldpiix from and signed-off-by Dave Mueller -jg] Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 4 ++-- drivers/ata/pata_acpi.c | 2 +- drivers/ata/pata_atiixp.c | 2 +- drivers/ata/pata_cs5530.c | 6 +++--- drivers/ata/pata_oldpiix.c | 2 +- drivers/ata/pata_sc1200.c | 6 +++--- include/linux/libata.h | 22 ++++++++++++++++++++++ 7 files changed, 33 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5f8f57a818fb..79e3a8e7a84a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3288,7 +3288,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) dev->dma_mode = ata_xfer_mask2mode(dma_mask); found = 1; - if (dev->dma_mode != 0xff) + if (ata_dma_enabled(dev)) used_dma = 1; } if (!found) @@ -3313,7 +3313,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) /* step 3: set host DMA timings */ ata_link_for_each_dev(dev, link) { - if (!ata_dev_enabled(dev) || dev->dma_mode == 0xff) + if (!ata_dev_enabled(dev) || !ata_dma_enabled(dev)) continue; dev->xfer_mode = dev->dma_mode; diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index fbe605711554..eb919c16a03e 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -181,7 +181,7 @@ static unsigned int pacpi_qc_issue(struct ata_queued_cmd *qc) if (adev != acpi->last) { pacpi_set_piomode(ap, adev); - if (adev->dma_mode) + if (ata_dma_enabled(adev)) pacpi_set_dmamode(ap, adev); acpi->last = adev; } diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index d7de7baf58a8..e8a0d99d7356 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -183,7 +183,7 @@ static void atiixp_bmdma_start(struct ata_queued_cmd *qc) u16 tmp16; pci_read_config_word(pdev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); - if (adev->dma_mode >= XFER_UDMA_0) + if (ata_using_udma(adev)) tmp16 |= (1 << dn); else tmp16 &= ~(1 << dn); diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index 744beebaaf49..0c4b271a9d5a 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -149,10 +149,10 @@ static unsigned int cs5530_qc_issue(struct ata_queued_cmd *qc) struct ata_device *prev = ap->private_data; /* See if the DMA settings could be wrong */ - if (adev->dma_mode != 0 && adev != prev && prev != NULL) { + if (ata_dma_enabled(adev) && adev != prev && prev != NULL) { /* Maybe, but do the channels match MWDMA/UDMA ? */ - if ((adev->dma_mode >= XFER_UDMA_0 && prev->dma_mode < XFER_UDMA_0) || - (adev->dma_mode < XFER_UDMA_0 && prev->dma_mode >= XFER_UDMA_0)) + if ((ata_using_udma(adev) && !ata_using_udma(prev)) || + (ata_using_udma(prev) && !ata_using_udma(adev))) /* Switch the mode bits */ cs5530_set_dmamode(ap, adev); } diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index e678af383d13..df64f2443001 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -198,7 +198,7 @@ static unsigned int oldpiix_qc_issue(struct ata_queued_cmd *qc) if (adev != ap->private_data) { oldpiix_set_piomode(ap, adev); - if (adev->dma_mode) + if (ata_dma_enabled(adev)) oldpiix_set_dmamode(ap, adev); } return ata_sff_qc_issue(qc); diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index cbab397e3db7..0278fd2b8fb1 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -167,10 +167,10 @@ static unsigned int sc1200_qc_issue(struct ata_queued_cmd *qc) struct ata_device *prev = ap->private_data; /* See if the DMA settings could be wrong */ - if (adev->dma_mode != 0 && adev != prev && prev != NULL) { + if (ata_dma_enabled(adev) && adev != prev && prev != NULL) { /* Maybe, but do the channels match MWDMA/UDMA ? */ - if ((adev->dma_mode >= XFER_UDMA_0 && prev->dma_mode < XFER_UDMA_0) || - (adev->dma_mode < XFER_UDMA_0 && prev->dma_mode >= XFER_UDMA_0)) + if ((ata_using_udma(adev) && !ata_using_udma(prev)) || + (ata_using_udma(prev) && !ata_using_udma(adev))) /* Switch the mode bits */ sc1200_set_dmamode(ap, adev); } diff --git a/include/linux/libata.h b/include/linux/libata.h index 80233fdc159f..225bfc5bd9ec 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1429,6 +1429,28 @@ static inline unsigned long ata_deadline(unsigned long from_jiffies, return from_jiffies + msecs_to_jiffies(timeout_msecs); } +/* Don't open code these in drivers as there are traps. Firstly the range may + change in future hardware and specs, secondly 0xFF means 'no DMA' but is + > UDMA_0. Dyma ddreigiau */ + +static inline int ata_using_mwdma(struct ata_device *adev) +{ + if (adev->dma_mode >= XFER_MW_DMA_0 && adev->dma_mode <= XFER_MW_DMA_4) + return 1; + return 0; +} + +static inline int ata_using_udma(struct ata_device *adev) +{ + if (adev->dma_mode >= XFER_UDMA_0 && adev->dma_mode <= XFER_UDMA_7) + return 1; + return 0; +} + +static inline int ata_dma_enabled(struct ata_device *adev) +{ + return (adev->dma_mode == 0xFF ? 0 : 1); +} /************************************************************************** * PMP - drivers/ata/libata-pmp.c -- cgit v1.2.3 From 9961920199ec88d6b581d3e38502088935925c04 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 2 Aug 2008 15:10:58 -0300 Subject: rfkill: add default global states (v2) Add a second set of global states, "rfkill_default_states", to track the state that will be used when the first rfkill class of a given type is registered, and also to save "undo" information when rfkill_epo is called. Add a new exported function, rfkill_set_default(), which can be used by platform drivers to restore radio state saved by the platform across reboots or shutdown. Also, fix rfkill_epo to properly update rfkill_states, but still preserve a copy of the state so that we can undo the effect of rfkill_epo later if we want to. Add rfkill_restore_states() to restore rfkill_states from the copy. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- include/linux/rfkill.h | 1 + net/rfkill/rfkill-input.h | 1 + net/rfkill/rfkill.c | 127 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 117 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 741d1a62cc3f..aa3c7d5852f6 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -116,6 +116,7 @@ int rfkill_register(struct rfkill *rfkill); void rfkill_unregister(struct rfkill *rfkill); int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); +int rfkill_set_default(enum rfkill_type type, enum rfkill_state state); /** * rfkill_state_complement - return complementar state diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h index f63d05045685..bbfa646157c6 100644 --- a/net/rfkill/rfkill-input.h +++ b/net/rfkill/rfkill-input.h @@ -13,5 +13,6 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); void rfkill_epo(void); +void rfkill_restore_states(void); #endif /* __RFKILL_INPUT_H */ diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 1f23de20a85e..b995fa32cf84 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -44,7 +44,13 @@ module_param_named(default_state, rfkill_default_state, uint, 0444); MODULE_PARM_DESC(default_state, "Default initial state for all radio types, 0 = radio off"); -static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; +struct rfkill_gsw_state { + enum rfkill_state current_state; + enum rfkill_state default_state; +}; + +static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX]; +static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); @@ -213,22 +219,22 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, } /** - * rfkill_switch_all - Toggle state of all switches of given type + * __rfkill_switch_all - Toggle state of all switches of given type * @type: type of interfaces to be affected * @state: the new state * * This function toggles the state of all switches of given type, * unless a specific switch is claimed by userspace (in which case, * that switch is left alone) or suspended. + * + * Caller must have acquired rfkill_mutex. */ -void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) +static void __rfkill_switch_all(const enum rfkill_type type, + const enum rfkill_state state) { struct rfkill *rfkill; - mutex_lock(&rfkill_mutex); - - rfkill_states[type] = state; - + rfkill_global_states[type].current_state = state; list_for_each_entry(rfkill, &rfkill_list, node) { if ((!rfkill->user_claim) && (rfkill->type == type)) { mutex_lock(&rfkill->mutex); @@ -236,7 +242,20 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) mutex_unlock(&rfkill->mutex); } } +} +/** + * rfkill_switch_all - Toggle state of all switches of given type + * @type: type of interfaces to be affected + * @state: the new state + * + * Acquires rfkill_mutex and calls __rfkill_switch_all(@type, @state). + * Please refer to __rfkill_switch_all() for details. + */ +void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) +{ + mutex_lock(&rfkill_mutex); + __rfkill_switch_all(type, state); mutex_unlock(&rfkill_mutex); } EXPORT_SYMBOL(rfkill_switch_all); @@ -246,10 +265,14 @@ EXPORT_SYMBOL(rfkill_switch_all); * * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED, * ignoring everything in its path but rfkill_mutex and rfkill->mutex. + * + * The global state before the EPO is saved and can be restored later + * using rfkill_restore_states(). */ void rfkill_epo(void) { struct rfkill *rfkill; + int i; mutex_lock(&rfkill_mutex); list_for_each_entry(rfkill, &rfkill_list, node) { @@ -257,10 +280,34 @@ void rfkill_epo(void) rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); mutex_unlock(&rfkill->mutex); } + for (i = 0; i < RFKILL_TYPE_MAX; i++) { + rfkill_global_states[i].default_state = + rfkill_global_states[i].current_state; + rfkill_global_states[i].current_state = + RFKILL_STATE_SOFT_BLOCKED; + } mutex_unlock(&rfkill_mutex); } EXPORT_SYMBOL_GPL(rfkill_epo); +/** + * rfkill_restore_states - restore global states + * + * Restore (and sync switches to) the global state from the + * states in rfkill_default_states. This can undo the effects of + * a call to rfkill_epo(). + */ +void rfkill_restore_states(void) +{ + int i; + + mutex_lock(&rfkill_mutex); + for (i = 0; i < RFKILL_TYPE_MAX; i++) + __rfkill_switch_all(i, rfkill_global_states[i].default_state); + mutex_unlock(&rfkill_mutex); +} +EXPORT_SYMBOL_GPL(rfkill_restore_states); + /** * rfkill_force_state - Force the internal rfkill radio state * @rfkill: pointer to the rfkill class to modify. @@ -406,8 +453,8 @@ static ssize_t rfkill_claim_store(struct device *dev, if (!claim) { mutex_lock(&rfkill->mutex); rfkill_toggle_radio(rfkill, - rfkill_states[rfkill->type], - 0); + rfkill_global_states[rfkill->type].current_state, + 0); mutex_unlock(&rfkill->mutex); } rfkill->user_claim = claim; @@ -554,7 +601,16 @@ static int rfkill_add_switch(struct rfkill *rfkill) if (error < 0) goto unlock_out; - rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); + if (!error) { + /* lock default after first use */ + set_bit(rfkill->type, rfkill_states_lockdflt); + rfkill_global_states[rfkill->type].current_state = + rfkill_global_states[rfkill->type].default_state; + } + + rfkill_toggle_radio(rfkill, + rfkill_global_states[rfkill->type].current_state, + 0); list_add_tail(&rfkill->node, &rfkill_list); @@ -710,6 +766,53 @@ void rfkill_unregister(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_unregister); +/** + * rfkill_set_default - set initial value for a switch type + * @type - the type of switch to set the default state of + * @state - the new default state for that group of switches + * + * Sets the initial state rfkill should use for a given type. + * The following initial states are allowed: RFKILL_STATE_SOFT_BLOCKED + * and RFKILL_STATE_UNBLOCKED. + * + * This function is meant to be used by platform drivers for platforms + * that can save switch state across power down/reboot. + * + * The default state for each switch type can be changed exactly once. + * After a switch of that type is registered, the default state cannot + * be changed anymore. This guards against multiple drivers it the + * same platform trying to set the initial switch default state, which + * is not allowed. + * + * Returns -EPERM if the state has already been set once or is in use, + * so drivers likely want to either ignore or at most printk(KERN_NOTICE) + * if this function returns -EPERM. + * + * Returns 0 if the new default state was set, or an error if it + * could not be set. + */ +int rfkill_set_default(enum rfkill_type type, enum rfkill_state state) +{ + int error; + + if (type >= RFKILL_TYPE_MAX || + (state != RFKILL_STATE_SOFT_BLOCKED && + state != RFKILL_STATE_UNBLOCKED)) + return -EINVAL; + + mutex_lock(&rfkill_mutex); + + if (!test_and_set_bit(type, rfkill_states_lockdflt)) { + rfkill_global_states[type].default_state = state; + error = 0; + } else + error = -EPERM; + + mutex_unlock(&rfkill_mutex); + return error; +} +EXPORT_SYMBOL_GPL(rfkill_set_default); + /* * Rfkill module initialization/deinitialization. */ @@ -723,8 +826,8 @@ static int __init rfkill_init(void) rfkill_default_state != RFKILL_STATE_UNBLOCKED) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) - rfkill_states[i] = rfkill_default_state; + for (i = 0; i < RFKILL_TYPE_MAX; i++) + rfkill_global_states[i].default_state = rfkill_default_state; error = class_register(&rfkill_class); if (error) { -- cgit v1.2.3 From 77fba13ccc3a2a3db100892a4a6cc5e2f8290cc7 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 2 Aug 2008 15:10:59 -0300 Subject: rfkill: add __must_check annotations rfkill is not a small, mere detail in wireless support. Once it starts supporting rfkill and users start counting on that support, a wireless device is at risk of operating in dangerous conditions should rfkill support fail to properly activate. Therefore, add the required __must_check annotations on some key functions of the rfkill API, for which the wireless drivers absolutely MUST handle the failure mode safely in order to avoid a potentially dangerous situation where the wireless transmitter is left enabled when the user don't want it to. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Matthew Garrett Signed-off-by: John W. Linville --- include/linux/rfkill.h | 5 +++-- net/rfkill/rfkill.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index aa3c7d5852f6..e92d8e94bb88 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -110,9 +110,10 @@ struct rfkill { }; #define to_rfkill(d) container_of(d, struct rfkill, dev) -struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type); +struct rfkill * __must_check rfkill_allocate(struct device *parent, + enum rfkill_type type); void rfkill_free(struct rfkill *rfkill); -int rfkill_register(struct rfkill *rfkill); +int __must_check rfkill_register(struct rfkill *rfkill); void rfkill_unregister(struct rfkill *rfkill); int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index b995fa32cf84..fae7ffade9c9 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -645,7 +645,8 @@ static void rfkill_remove_switch(struct rfkill *rfkill) * NOTE: If registration fails the structure shoudl be freed by calling * rfkill_free() otherwise rfkill_unregister() should be used. */ -struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type) +struct rfkill * __must_check rfkill_allocate(struct device *parent, + enum rfkill_type type) { struct rfkill *rfkill; struct device *dev; @@ -716,7 +717,7 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill) * structure needs to be registered. Immediately from registration the * switch driver should be able to service calls to toggle_radio. */ -int rfkill_register(struct rfkill *rfkill) +int __must_check rfkill_register(struct rfkill *rfkill) { static atomic_t rfkill_no = ATOMIC_INIT(0); struct device *dev = &rfkill->dev; -- cgit v1.2.3 From 96c87607ac8f9b0e641d11ba6e57f8ec0214ea1c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 2 Aug 2008 15:11:00 -0300 Subject: rfkill: introduce RFKILL_STATE_MAX While it is interesting to not add last-enum-markers because it allows gcc to warn us of switch() statements missing a valid state, we really should be handling memory corruption on a rfkill state with default clauses, anyway. So add RFKILL_STATE_MAX and use it where applicable. It makes for safer code in the long run. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- include/linux/rfkill.h | 1 + net/rfkill/rfkill.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index e92d8e94bb88..4cd64b0d9825 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -49,6 +49,7 @@ enum rfkill_state { RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */ RFKILL_STATE_UNBLOCKED = 1, /* Radio output allowed */ RFKILL_STATE_HARD_BLOCKED = 2, /* Output blocked, non-overrideable */ + RFKILL_STATE_MAX, /* marker for last valid state */ }; /* diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index fae7ffade9c9..47e0b2d232e3 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -201,6 +201,8 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, * BLOCK even a transmitter that is already in state * RFKILL_STATE_HARD_BLOCKED */ break; + default: + return -EINVAL; } if (force || state != rfkill->state) { @@ -234,6 +236,9 @@ static void __rfkill_switch_all(const enum rfkill_type type, { struct rfkill *rfkill; + if (unlikely(state >= RFKILL_STATE_MAX)) + return; + rfkill_global_states[type].current_state = state; list_for_each_entry(rfkill, &rfkill_list, node) { if ((!rfkill->user_claim) && (rfkill->type == type)) { @@ -329,9 +334,7 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { enum rfkill_state oldstate; - if (state != RFKILL_STATE_SOFT_BLOCKED && - state != RFKILL_STATE_UNBLOCKED && - state != RFKILL_STATE_HARD_BLOCKED) + if (unlikely(state >= RFKILL_STATE_MAX)) return -EINVAL; mutex_lock(&rfkill->mutex); @@ -727,6 +730,8 @@ int __must_check rfkill_register(struct rfkill *rfkill) return -EINVAL; if (rfkill->type >= RFKILL_TYPE_MAX) return -EINVAL; + if (rfkill->state >= RFKILL_STATE_MAX) + return -EINVAL; snprintf(dev->bus_id, sizeof(dev->bus_id), "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); -- cgit v1.2.3 From fef1643bf0cdd092a52dc3378479e4811fd65152 Mon Sep 17 00:00:00 2001 From: Jasper Bryant-Greene Date: Sun, 3 Aug 2008 11:30:55 +1200 Subject: move ETH_P_PAE from ieee80211_i.h to if_ether.h ETH_P_PAE belongs in if_ether.h with the other ETH_P_* definitions. This patch moves it there. Signed-off-by: Jasper Bryant-Greene Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/if_ether.h | 1 + net/mac80211/ieee80211_i.h | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index e157c1399b61..5028e0b6082b 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -74,6 +74,7 @@ #define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport * over Ethernet */ +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ #define ETH_P_TIPC 0x88CA /* TIPC */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3cad0172b45e..168f845c1737 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -32,10 +32,6 @@ /* ieee80211.o internal definitions, etc. These are not included into * low-level drivers. */ -#ifndef ETH_P_PAE -#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ -#endif /* ETH_P_PAE */ - struct ieee80211_local; /* Maximum number of broadcast/multicast frames to buffer when some of the -- cgit v1.2.3 From 7a8fc9b248e77a4eab0613acf30a6811799786b3 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 17 Aug 2008 17:36:59 +0300 Subject: removed unused #include 's This patch lets the files using linux/version.h match the files that #include it. Signed-off-by: Adrian Bunk Signed-off-by: Linus Torvalds --- arch/arm/plat-omap/clock.c | 1 - arch/cris/arch-v32/kernel/fasttimer.c | 2 -- arch/mn10300/kernel/mn10300-serial.c | 1 - arch/powerpc/sysdev/bestcomm/gen_bd.c | 1 - arch/x86/mach-rdc321x/platform.c | 1 - drivers/atm/adummy.c | 1 - drivers/char/xilinx_hwicap/buffer_icap.h | 1 - drivers/char/xilinx_hwicap/fifo_icap.h | 1 - drivers/char/xilinx_hwicap/xilinx_hwicap.h | 1 - drivers/edac/edac_core.h | 1 - drivers/i2c/busses/i2c-at91.c | 1 - drivers/infiniband/hw/ehca/ehca_tools.h | 1 - drivers/infiniband/hw/ipath/ipath_fs.c | 1 - drivers/infiniband/hw/nes/nes.h | 1 - drivers/infiniband/ulp/iser/iser_verbs.c | 1 - drivers/input/keyboard/bf54x-keys.c | 1 - drivers/input/touchscreen/mainstone-wm97xx.c | 1 - drivers/mfd/asic3.c | 1 - drivers/misc/eeprom_93cx6.c | 1 - drivers/mtd/maps/amd76xrom.c | 1 - drivers/mtd/maps/ck804xrom.c | 1 - drivers/mtd/maps/esb2rom.c | 1 - drivers/mtd/nand/au1550nd.c | 1 - drivers/net/myri10ge/myri10ge.c | 1 - drivers/net/netxen/netxen_nic.h | 1 - drivers/net/netxen/netxen_nic_ethtool.c | 1 - drivers/net/netxen/netxen_nic_hdr.h | 2 -- drivers/net/tokenring/lanstreamer.c | 1 - drivers/net/tokenring/lanstreamer.h | 2 -- drivers/net/wireless/b43legacy/main.c | 1 - drivers/net/wireless/iwlwifi/iwl-3945-led.c | 1 - drivers/net/wireless/iwlwifi/iwl-led.c | 1 - drivers/net/wireless/iwlwifi/iwl-rfkill.c | 1 - drivers/rtc/rtc-max6902.c | 2 -- drivers/rtc/rtc-r9701.c | 1 - drivers/s390/net/ctcm_mpc.c | 1 - drivers/scsi/dpt/dpti_i2o.h | 1 - drivers/scsi/ips.c | 1 - drivers/scsi/ips.h | 1 - drivers/scsi/lpfc/lpfc_debugfs.c | 1 - drivers/scsi/nsp32.c | 1 - drivers/scsi/nsp32.h | 1 - drivers/scsi/pcmcia/nsp_cs.c | 1 - drivers/scsi/qla2xxx/qla_mid.c | 1 - drivers/usb/atm/ueagle-atm.c | 1 - drivers/usb/gadget/amd5536udc.c | 1 - drivers/usb/gadget/s3c2410_udc.c | 1 - drivers/usb/misc/iowarrior.c | 1 - drivers/usb/serial/garmin_gps.c | 2 -- drivers/video/arkfb.c | 1 - drivers/video/s3fb.c | 1 - drivers/video/vermilion/vermilion.h | 1 - drivers/video/vt8623fb.c | 1 - drivers/video/xilinxfb.c | 1 - fs/jffs2/jffs2_fs_i.h | 1 - fs/xfs/xfs_dmapi.h | 1 - include/asm-x86/xen/hypervisor.h | 1 - include/linux/fs_uart_pd.h | 1 - kernel/nsproxy.c | 1 - kernel/power/swap.c | 1 - kernel/user_namespace.c | 1 - kernel/utsname.c | 1 - kernel/utsname_sysctl.c | 1 - sound/mips/au1x00.c | 1 - sound/soc/at91/eti_b1_wm8731.c | 1 - sound/soc/codecs/wm8753.c | 1 - sound/soc/codecs/wm9712.c | 1 - 67 files changed, 72 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 23a070599993..197974defbe4 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -10,7 +10,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/arch/cris/arch-v32/kernel/fasttimer.c b/arch/cris/arch-v32/kernel/fasttimer.c index 2de9d5849ef0..111caa1a2efb 100644 --- a/arch/cris/arch-v32/kernel/fasttimer.c +++ b/arch/cris/arch-v32/kernel/fasttimer.c @@ -19,8 +19,6 @@ #include #include -#include - #include #include #include diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c index 8b054e7a8ae8..aa07d0cd1905 100644 --- a/arch/mn10300/kernel/mn10300-serial.c +++ b/arch/mn10300/kernel/mn10300-serial.c @@ -17,7 +17,6 @@ static const char serial_revdate[] = "2007-11-06"; #define SUPPORT_SYSRQ #endif -#include #include #include #include diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.c b/arch/powerpc/sysdev/bestcomm/gen_bd.c index a3a134c35b0a..e0a53e3147b2 100644 --- a/arch/powerpc/sysdev/bestcomm/gen_bd.c +++ b/arch/powerpc/sysdev/bestcomm/gen_bd.c @@ -11,7 +11,6 @@ * */ -#include #include #include #include diff --git a/arch/x86/mach-rdc321x/platform.c b/arch/x86/mach-rdc321x/platform.c index a037041817c7..4f4e50c3ad3b 100644 --- a/arch/x86/mach-rdc321x/platform.c +++ b/arch/x86/mach-rdc321x/platform.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/drivers/atm/adummy.c b/drivers/atm/adummy.c index 2ebd07f2ef81..5effec6f5458 100644 --- a/drivers/atm/adummy.c +++ b/drivers/atm/adummy.c @@ -3,7 +3,6 @@ */ #include -#include #include #include #include diff --git a/drivers/char/xilinx_hwicap/buffer_icap.h b/drivers/char/xilinx_hwicap/buffer_icap.h index c5b1840906b2..8b0252bf06e2 100644 --- a/drivers/char/xilinx_hwicap/buffer_icap.h +++ b/drivers/char/xilinx_hwicap/buffer_icap.h @@ -38,7 +38,6 @@ #include #include -#include #include #include diff --git a/drivers/char/xilinx_hwicap/fifo_icap.h b/drivers/char/xilinx_hwicap/fifo_icap.h index ffabd3ba2bd8..62bda453c90b 100644 --- a/drivers/char/xilinx_hwicap/fifo_icap.h +++ b/drivers/char/xilinx_hwicap/fifo_icap.h @@ -38,7 +38,6 @@ #include #include -#include #include #include diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h index 1f9c8b082dbe..24d0d9b938fb 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.h +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h @@ -38,7 +38,6 @@ #include #include -#include #include #include diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index b27b13c5eb5a..4b55ec607a88 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -34,7 +34,6 @@ #include #include #include -#include #define EDAC_MC_LABEL_LEN 31 #define EDAC_DEVICE_NAME_LEN 31 diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index c1adcdbf7979..9efb02137254 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/infiniband/hw/ehca/ehca_tools.h index ec950bf8c479..21f7d06f14ad 100644 --- a/drivers/infiniband/hw/ehca/ehca_tools.h +++ b/drivers/infiniband/hw/ehca/ehca_tools.h @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c index 23faba9d21eb..8bb5170b4e41 100644 --- a/drivers/infiniband/hw/ipath/ipath_fs.c +++ b/drivers/infiniband/hw/ipath/ipath_fs.c @@ -31,7 +31,6 @@ * SOFTWARE. */ -#include #include #include #include diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h index 39bd897b40c6..8eb7ae96974d 100644 --- a/drivers/infiniband/hw/nes/nes.h +++ b/drivers/infiniband/hw/nes/nes.h @@ -43,7 +43,6 @@ #include #include #include -#include #include #include diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 63462ecca147..26ff6214a81f 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -33,7 +33,6 @@ #include #include #include -#include #include "iscsi_iser.h" diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index 54ed8e2e1c02..6f227d3dbda1 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -29,7 +29,6 @@ */ #include -#include #include #include diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index 283f93a0cee2..37a555f37306 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index c6408a62d95e..bc2a807f210d 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -16,7 +16,6 @@ * */ -#include #include #include #include diff --git a/drivers/misc/eeprom_93cx6.c b/drivers/misc/eeprom_93cx6.c index ea55654e5948..15b1780025c8 100644 --- a/drivers/misc/eeprom_93cx6.c +++ b/drivers/misc/eeprom_93cx6.c @@ -26,7 +26,6 @@ #include #include -#include #include #include diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 948b86f35ef4..d1eec7d3243f 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index effaf7cdefab..1a6feb4474de 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index aa64a4752781..bbbcdd4c8d13 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 761946ea45b1..92c334ff4508 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 5d76cd09e246..54cd89cb0838 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index ab871df6b1db..244ab49c4337 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -45,7 +45,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 4ad3e0844b99..b974ca0fc530 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -38,7 +38,6 @@ #include #include #include -#include #include "netxen_nic.h" #include "netxen_nic_hw.h" diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index e8e8d73f6ed7..e80f9e3e5973 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -32,8 +32,6 @@ #include #include -#include - #include #include #include diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c index 47d84cd28097..59d1673f9387 100644 --- a/drivers/net/tokenring/lanstreamer.c +++ b/drivers/net/tokenring/lanstreamer.c @@ -119,7 +119,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h index e7bb3494afc7..13ccee6449c1 100644 --- a/drivers/net/tokenring/lanstreamer.h +++ b/drivers/net/tokenring/lanstreamer.h @@ -60,8 +60,6 @@ * */ -#include - /* MAX_INTR - the maximum number of times we can loop * inside the interrupt function before returning * control to the OS (maximum value is 256) diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 2541c81932f0..1cb77db5c292 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index d3336966b6b5..705c65bed9fd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index cb11c4a4d691..4eee1b163cd2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-rfkill.c b/drivers/net/wireless/iwlwifi/iwl-rfkill.c index e5e5846e9f25..5d642298f04c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rfkill.c +++ b/drivers/net/wireless/iwlwifi/iwl-rfkill.c @@ -27,7 +27,6 @@ *****************************************************************************/ #include #include -#include #include #include diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index 12f0310ae89c..78b2551fb19d 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c @@ -20,8 +20,6 @@ */ #include -#include - #include #include #include diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index b35f9bfa2af4..395985b339c9 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index 49ae1cd25caa..2de1e2fccbf9 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -19,7 +19,6 @@ #undef DEBUGDATA #undef DEBUGCCW -#include #include #include #include diff --git a/drivers/scsi/dpt/dpti_i2o.h b/drivers/scsi/dpt/dpti_i2o.h index 19406cea6d6a..179ad77f6cc9 100644 --- a/drivers/scsi/dpt/dpti_i2o.h +++ b/drivers/scsi/dpt/dpti_i2o.h @@ -21,7 +21,6 @@ #include -#include #include #include diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index 7c615c70ec5c..bc9e6ddf41df 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -165,7 +165,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index e0657b6f009c..4e49fbcfe8af 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -50,7 +50,6 @@ #ifndef _IPS_H_ #define _IPS_H_ -#include #include #include #include diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 90272e65957a..094b47e94b29 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c index edf9fdb3cb3c..22052bb7becb 100644 --- a/drivers/scsi/nsp32.c +++ b/drivers/scsi/nsp32.c @@ -23,7 +23,6 @@ * 1.2: PowerPC (big endian) support. */ -#include #include #include #include diff --git a/drivers/scsi/nsp32.h b/drivers/scsi/nsp32.h index 6715ecb3bfca..9565acf1aa72 100644 --- a/drivers/scsi/nsp32.h +++ b/drivers/scsi/nsp32.h @@ -16,7 +16,6 @@ #ifndef _NSP32_H #define _NSP32_H -#include //#define NSP32_DEBUG 9 /* diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index a221b6ef9fa9..24e6cb8396e3 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -25,7 +25,6 @@ ***********************************************************************/ -#include #include #include #include diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 50baf6a1d67c..93560cd72784 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -6,7 +6,6 @@ */ #include "qla_def.h" -#include #include #include #include diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index cb01b5106efd..b6483dd98acc 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -64,7 +64,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 1500e1b3c302..abf8192f89e8 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 538807384592..29d13ebe7500 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index e6ca9979e3ae..a4ef77ef917d 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -19,7 +19,6 @@ #include #include #include -#include #include /* Version Information */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 2e663f1afd5e..d95382088075 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -38,8 +38,6 @@ #include #include -#include - /* the mode to be set when the port ist opened */ static int initial_mode = 1; diff --git a/drivers/video/arkfb.c b/drivers/video/arkfb.c index 4bd569e479a7..314d18694b6a 100644 --- a/drivers/video/arkfb.c +++ b/drivers/video/arkfb.c @@ -11,7 +11,6 @@ * Code is based on s3fb */ -#include #include #include #include diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index 8361bd0e3df1..4dcec48a1d78 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c @@ -11,7 +11,6 @@ * which is based on the code of neofb. */ -#include #include #include #include diff --git a/drivers/video/vermilion/vermilion.h b/drivers/video/vermilion/vermilion.h index c4aba59d4809..7491abfcf1fc 100644 --- a/drivers/video/vermilion/vermilion.h +++ b/drivers/video/vermilion/vermilion.h @@ -30,7 +30,6 @@ #define _VERMILION_H_ #include -#include #include #include #include diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index 34aae7a2a62b..3df17dc8c3d7 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -12,7 +12,6 @@ * (http://davesdomain.org.uk/viafb/) */ -#include #include #include #include diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 7b3a8423f485..5da3d2423cc0 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index 31559f45fdde..4c41db91eaa4 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -12,7 +12,6 @@ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I -#include #include #include #include diff --git a/fs/xfs/xfs_dmapi.h b/fs/xfs/xfs_dmapi.h index cdc2d3464a1a..2813cdd72375 100644 --- a/fs/xfs/xfs_dmapi.h +++ b/fs/xfs/xfs_dmapi.h @@ -18,7 +18,6 @@ #ifndef __XFS_DMAPI_H__ #define __XFS_DMAPI_H__ -#include /* Values used to define the on-disk version of dm_attrname_t. All * on-disk attribute names start with the 8-byte string "SGI_DMI_". * diff --git a/include/asm-x86/xen/hypervisor.h b/include/asm-x86/xen/hypervisor.h index 8e15dd28c91f..04ee0610014a 100644 --- a/include/asm-x86/xen/hypervisor.h +++ b/include/asm-x86/xen/hypervisor.h @@ -35,7 +35,6 @@ #include #include -#include #include #include diff --git a/include/linux/fs_uart_pd.h b/include/linux/fs_uart_pd.h index 809bb9ffc788..36b61ff39277 100644 --- a/include/linux/fs_uart_pd.h +++ b/include/linux/fs_uart_pd.h @@ -12,7 +12,6 @@ #ifndef FS_UART_PD_H #define FS_UART_PD_H -#include #include enum fs_uart_id { diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 21575fc46d05..1d3ef29a2583 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include diff --git a/kernel/power/swap.c b/kernel/power/swap.c index a0abf9a463f9..80ccac849e46 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index a9ab0596de44..532858fa5b88 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -6,7 +6,6 @@ */ #include -#include #include #include #include diff --git a/kernel/utsname.c b/kernel/utsname.c index 64d398f12444..815237a55af8 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c index fe3a56c2256d..4ab9659d269e 100644 --- a/kernel/utsname_sysctl.c +++ b/kernel/utsname_sysctl.c @@ -12,7 +12,6 @@ #include #include #include -#include #include static void *get_uts(ctl_table *table, int write) diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index ee0741f9eb53..fbef38a9604a 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index b081e83766b7..b81d6b2cfa1d 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 8604809f0c36..dc7b18fd2782 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -34,7 +34,6 @@ #include #include -#include #include #include #include diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1fb7f9a7aecd..2f1c91b1d556 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From e45b590b976465c258f3e2a6cc84573fc19e16d3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 7 Aug 2008 23:49:07 +0200 Subject: [PATCH] change d_add_ci argument ordering As pointed out during review d_add_ci argument order should match d_add, so switch the dentry and inode arguments. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/dcache.c | 2 +- fs/xfs/linux-2.6/xfs_iops.c | 2 +- include/linux/dcache.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/dcache.c b/fs/dcache.c index 101663d15e9f..80e93956aced 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1236,7 +1236,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) * If no entry exists with the exact case name, allocate new dentry with * the exact case, and return the spliced entry. */ -struct dentry *d_add_ci(struct inode *inode, struct dentry *dentry, +struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, struct qstr *name) { int error; diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 91bcd979242c..095d271f3434 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -355,7 +355,7 @@ xfs_vn_ci_lookup( /* else case-insensitive match... */ dname.name = ci_name.name; dname.len = ci_name.len; - dentry = d_add_ci(VFS_I(ip), dentry, &dname); + dentry = d_add_ci(dentry, VFS_I(ip), &dname); kmem_free(ci_name.name); return dentry; } diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 07aa198f19ed..efba1de629ac 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -230,7 +230,7 @@ extern void d_delete(struct dentry *); extern struct dentry * d_alloc(struct dentry *, const struct qstr *); extern struct dentry * d_alloc_anon(struct inode *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *); -extern struct dentry * d_add_ci(struct inode *, struct dentry *, struct qstr *); +extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *); extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); extern void shrink_dcache_for_umount(struct super_block *); -- cgit v1.2.3 From bf2002967775cbb233876d51ff94e8daa7e77858 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Aug 2008 00:19:27 -0500 Subject: stop_machine: Remove deprecated stop_machine_run Everyone should be using stop_machine() now. The staged API transition helped life in linux-next. Signed-off-by: Rusty Russell --- include/linux/stop_machine.h | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index f1cb0ba6d715..faf1519b5adc 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -3,16 +3,13 @@ /* "Bogolock": stop the entire machine, disable interrupts. This is a very heavy lock, which is equivalent to grabbing every spinlock (and more). So the "read" side to such a lock is anything which - diables preeempt. */ + disables preeempt. */ #include #include #include #if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP) -/* Deprecated, but useful for transition. */ -#define ALL_CPUS ~0U - /** * stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run @@ -50,18 +47,4 @@ static inline int stop_machine(int (*fn)(void *), void *data, return ret; } #endif /* CONFIG_SMP */ - -static inline int __deprecated stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu) -{ - /* If they don't care which cpu fn runs on, just pick one. */ - if (cpu == NR_CPUS) - return stop_machine(fn, data, NULL); - else if (cpu == ~0U) - return stop_machine(fn, data, &cpu_possible_map); - else { - cpumask_t cpus = cpumask_of_cpu(cpu); - return stop_machine(fn, data, &cpus); - } -} #endif /* _LINUX_STOP_MACHINE */ -- cgit v1.2.3 From 1327138e294ffda120a50c8f31d792addb196c79 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 6 Aug 2008 01:36:09 +0300 Subject: KVM: fix userspace ABI breakage The following part of commit 9ef621d3be56e1188300476a8102ff54f7b6793f (KVM: Support mixed endian machines) changed on the size of a struct that is exported to userspace: include/linux/kvm.h: @@ -318,14 +318,14 @@ struct kvm_trace_rec { __u32 vcpu_id; union { struct { - __u32 cycle_lo, cycle_hi; + __u64 cycle_u64; __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } cycle; struct { __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } nocycle; } u; -}; +} __attribute__((packed)); Packing a struct was the correct idea, but it packed the wrong struct. Signed-off-by: Adrian Bunk Signed-off-by: Avi Kivity --- include/linux/kvm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 69511f74f912..70a30651cd12 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -320,12 +320,12 @@ struct kvm_trace_rec { struct { __u64 cycle_u64; __u32 extra_u32[KVM_TRC_EXTRA_MAX]; - } cycle; + } __attribute__((packed)) cycle; struct { __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } nocycle; } u; -} __attribute__((packed)); +}; #define KVMIO 0xAE -- cgit v1.2.3 From 65eb3dc609dec17deea48dcd4de2e549d29a9824 Mon Sep 17 00:00:00 2001 From: Kevin Diggs Date: Tue, 26 Aug 2008 10:26:54 +0200 Subject: sched: add kernel doc for the completion, fix kernel-doc-nano-HOWTO.txt This patch adds kernel doc for the completion feature. An error in the split-man.pl PERL snippet in kernel-doc-nano-HOWTO.txt is also fixed. Signed-off-by: Kevin Diggs Signed-off-by: Ingo Molnar --- Documentation/kernel-doc-nano-HOWTO.txt | 4 +-- include/linux/completion.h | 41 ++++++++++++++++++++++++ kernel/sched.c | 56 +++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-doc-nano-HOWTO.txt b/Documentation/kernel-doc-nano-HOWTO.txt index 0bd32748a467..c6841eee9598 100644 --- a/Documentation/kernel-doc-nano-HOWTO.txt +++ b/Documentation/kernel-doc-nano-HOWTO.txt @@ -168,10 +168,10 @@ if ($#ARGV < 0) { mkdir $ARGV[0],0777; $state = 0; while () { - if (/^\.TH \"[^\"]*\" 4 \"([^\"]*)\"/) { + if (/^\.TH \"[^\"]*\" 9 \"([^\"]*)\"/) { if ($state == 1) { close OUT } $state = 1; - $fn = "$ARGV[0]/$1.4"; + $fn = "$ARGV[0]/$1.9"; print STDERR "Creating $fn\n"; open OUT, ">$fn" or die "can't open $fn: $!\n"; print OUT $_; diff --git a/include/linux/completion.h b/include/linux/completion.h index 02ef8835999c..4a6b604ef7e4 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -10,6 +10,18 @@ #include +/** + * struct completion - structure used to maintain state for a "completion" + * + * This is the opaque structure used to maintain the state for a "completion". + * Completions currently use a FIFO to queue threads that have to wait for + * the "completion" event. + * + * See also: complete(), wait_for_completion() (and friends _timeout, + * _interruptible, _interruptible_timeout, and _killable), init_completion(), + * and macros DECLARE_COMPLETION(), DECLARE_COMPLETION_ONSTACK(), and + * INIT_COMPLETION(). + */ struct completion { unsigned int done; wait_queue_head_t wait; @@ -21,6 +33,14 @@ struct completion { #define COMPLETION_INITIALIZER_ONSTACK(work) \ ({ init_completion(&work); work; }) +/** + * DECLARE_COMPLETION: - declare and initialize a completion structure + * @work: identifier for the completion structure + * + * This macro declares and initializes a completion structure. Generally used + * for static declarations. You should use the _ONSTACK variant for automatic + * variables. + */ #define DECLARE_COMPLETION(work) \ struct completion work = COMPLETION_INITIALIZER(work) @@ -29,6 +49,13 @@ struct completion { * completions - so we use the _ONSTACK() variant for those that * are on the kernel stack: */ +/** + * DECLARE_COMPLETION_ONSTACK: - declare and initialize a completion structure + * @work: identifier for the completion structure + * + * This macro declares and initializes a completion structure on the kernel + * stack. + */ #ifdef CONFIG_LOCKDEP # define DECLARE_COMPLETION_ONSTACK(work) \ struct completion work = COMPLETION_INITIALIZER_ONSTACK(work) @@ -36,6 +63,13 @@ struct completion { # define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work) #endif +/** + * init_completion: - Initialize a dynamically allocated completion + * @x: completion structure that is to be initialized + * + * This inline function will initialize a dynamically created completion + * structure. + */ static inline void init_completion(struct completion *x) { x->done = 0; @@ -55,6 +89,13 @@ extern bool completion_done(struct completion *x); extern void complete(struct completion *); extern void complete_all(struct completion *); +/** + * INIT_COMPLETION: - reinitialize a completion structure + * @x: completion structure to be reinitialized + * + * This macro should be used to reinitialize a completion structure so it can + * be reused. This is especially important after complete_all() is used. + */ #define INIT_COMPLETION(x) ((x).done = 0) diff --git a/kernel/sched.c b/kernel/sched.c index 29e2ec0bd831..93f5ea08be97 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4565,6 +4565,15 @@ __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) } EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */ +/** + * complete: - signals a single thread waiting on this completion + * @x: holds the state of this particular completion + * + * This will wake up a single thread waiting on this completion. Threads will be + * awakened in the same order in which they were queued. + * + * See also complete_all(), wait_for_completion() and related routines. + */ void complete(struct completion *x) { unsigned long flags; @@ -4576,6 +4585,12 @@ void complete(struct completion *x) } EXPORT_SYMBOL(complete); +/** + * complete_all: - signals all threads waiting on this completion + * @x: holds the state of this particular completion + * + * This will wake up all threads waiting on this particular completion event. + */ void complete_all(struct completion *x) { unsigned long flags; @@ -4624,12 +4639,31 @@ wait_for_common(struct completion *x, long timeout, int state) return timeout; } +/** + * wait_for_completion: - waits for completion of a task + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It is NOT + * interruptible and there is no timeout. + * + * See also similar routines (i.e. wait_for_completion_timeout()) with timeout + * and interrupt capability. Also see complete(). + */ void __sched wait_for_completion(struct completion *x) { wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(wait_for_completion); +/** + * wait_for_completion_timeout: - waits for completion of a task (w/timeout) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. The timeout is in jiffies. It is not + * interruptible. + */ unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout) { @@ -4637,6 +4671,13 @@ wait_for_completion_timeout(struct completion *x, unsigned long timeout) } EXPORT_SYMBOL(wait_for_completion_timeout); +/** + * wait_for_completion_interruptible: - waits for completion of a task (w/intr) + * @x: holds the state of this particular completion + * + * This waits for completion of a specific task to be signaled. It is + * interruptible. + */ int __sched wait_for_completion_interruptible(struct completion *x) { long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE); @@ -4646,6 +4687,14 @@ int __sched wait_for_completion_interruptible(struct completion *x) } EXPORT_SYMBOL(wait_for_completion_interruptible); +/** + * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr)) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. It is interruptible. The timeout is in jiffies. + */ unsigned long __sched wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout) @@ -4654,6 +4703,13 @@ wait_for_completion_interruptible_timeout(struct completion *x, } EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); +/** + * wait_for_completion_killable: - waits for completion of a task (killable) + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It can be + * interrupted by a kill signal. + */ int __sched wait_for_completion_killable(struct completion *x) { long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE); -- cgit v1.2.3 From 5770a3fb5f8544d40ae03b010318345cdd05d662 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 26 Aug 2008 15:29:22 +0100 Subject: Fix userspace export of Including in the user-visible part of this header has caused build regressions with headers from 2.6.27-rc. Move it down to the #ifdef __KERNEL__ part, which is the only place it's needed. Move some other kernel-only things down there too, while we're at it. Signed-off-by: David Woodhouse Signed-off-by: Linus Torvalds --- include/linux/net.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 4a9a30f2d68f..6dc14a240042 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -18,16 +18,9 @@ #ifndef _LINUX_NET_H #define _LINUX_NET_H -#include #include -#include /* For O_CLOEXEC and O_NONBLOCK */ #include -struct poll_table_struct; -struct pipe_inode_info; -struct inode; -struct net; - #define NPROTO AF_MAX #define SYS_SOCKET 1 /* sys_socket(2) */ @@ -62,6 +55,13 @@ typedef enum { #ifdef __KERNEL__ #include #include +#include +#include /* For O_CLOEXEC and O_NONBLOCK */ + +struct poll_table_struct; +struct pipe_inode_info; +struct inode; +struct net; #define SOCK_ASYNC_NOSPACE 0 #define SOCK_ASYNC_WAITDATA 1 -- cgit v1.2.3 From abf5439370491dd6fbb4fe1a7939680d2a9bc9d4 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 16 Aug 2008 14:10:05 +0900 Subject: block: move cmdfilter from gendisk to request_queue cmd_filter works only for the block layer SG_IO with SCSI block devices. It breaks scsi/sg.c, bsg, and the block layer SG_IO with SCSI character devices (such as st). We hit a kernel crash with them. The problem is that cmd_filter code accesses to gendisk (having struct blk_scsi_cmd_filter) via inode->i_bdev->bd_disk. It works for only SCSI block device files. With character device files, inode->i_bdev leads you to struct cdev. inode->i_bdev->bd_disk->blk_scsi_cmd_filter isn't safe. SCSI ULDs don't expose gendisk; they keep it private. bsg needs to be independent on any protocols. We shouldn't change ULDs to expose their gendisk. This patch moves struct blk_scsi_cmd_filter from gendisk to request_queue, a common object, which eveyone can access to. The user interface doesn't change; users can change the filters via /sys/block/. gendisk has a pointer to request_queue so the cmd_filter code accesses to struct blk_scsi_cmd_filter. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- block/blk-core.c | 2 + block/bsg.c | 44 +++++------------- block/cmd-filter.c | 118 +++---------------------------------------------- block/scsi_ioctl.c | 94 +++++++++++++++++++++++++++++++++++++-- drivers/scsi/sg.c | 11 ++++- include/linux/blkdev.h | 16 +++++-- include/linux/genhd.h | 10 ----- 7 files changed, 132 insertions(+), 163 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 4889eb86a39e..2cba5ef97b2b 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -582,6 +582,8 @@ blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id) q->sg_reserved_size = INT_MAX; + blk_set_cmd_filter_defaults(&q->cmd_filter); + /* * all done */ diff --git a/block/bsg.c b/block/bsg.c index 5a68b09a69ba..0aae8d7ba99c 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -45,8 +45,6 @@ struct bsg_device { char name[BUS_ID_SIZE]; int max_queue; unsigned long flags; - struct blk_scsi_cmd_filter *cmd_filter; - mode_t *f_mode; }; enum { @@ -174,7 +172,8 @@ unlock: } static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, - struct sg_io_v4 *hdr, struct bsg_device *bd) + struct sg_io_v4 *hdr, struct bsg_device *bd, + int has_write_perm) { if (hdr->request_len > BLK_MAX_CDB) { rq->cmd = kzalloc(hdr->request_len, GFP_KERNEL); @@ -187,8 +186,7 @@ static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, return -EFAULT; if (hdr->subprotocol == BSG_SUB_PROTOCOL_SCSI_CMD) { - if (blk_cmd_filter_verify_command(bd->cmd_filter, rq->cmd, - bd->f_mode)) + if (blk_verify_command(&q->cmd_filter, rq->cmd, has_write_perm)) return -EPERM; } else if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -244,7 +242,7 @@ bsg_validate_sgv4_hdr(struct request_queue *q, struct sg_io_v4 *hdr, int *rw) * map sg_io_v4 to a request. */ static struct request * -bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) +bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) { struct request_queue *q = bd->queue; struct request *rq, *next_rq = NULL; @@ -266,7 +264,7 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) rq = blk_get_request(q, rw, GFP_KERNEL); if (!rq) return ERR_PTR(-ENOMEM); - ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, bd); + ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, bd, has_write_perm); if (ret) goto out; @@ -568,25 +566,6 @@ static inline void bsg_set_block(struct bsg_device *bd, struct file *file) set_bit(BSG_F_BLOCK, &bd->flags); } -static void bsg_set_cmd_filter(struct bsg_device *bd, - struct file *file) -{ - struct inode *inode; - struct gendisk *disk; - - if (!file) - return; - - inode = file->f_dentry->d_inode; - if (!inode) - return; - - disk = inode->i_bdev->bd_disk; - - bd->cmd_filter = &disk->cmd_filter; - bd->f_mode = &file->f_mode; -} - /* * Check if the error is a "real" error that we should return. */ @@ -608,7 +587,6 @@ bsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) dprintk("%s: read %Zd bytes\n", bd->name, count); bsg_set_block(bd, file); - bsg_set_cmd_filter(bd, file); bytes_read = 0; ret = __bsg_read(buf, count, bd, NULL, &bytes_read); @@ -621,7 +599,7 @@ bsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) } static int __bsg_write(struct bsg_device *bd, const char __user *buf, - size_t count, ssize_t *bytes_written) + size_t count, ssize_t *bytes_written, int has_write_perm) { struct bsg_command *bc; struct request *rq; @@ -652,7 +630,7 @@ static int __bsg_write(struct bsg_device *bd, const char __user *buf, /* * get a request, fill in the blanks, and add to request queue */ - rq = bsg_map_hdr(bd, &bc->hdr); + rq = bsg_map_hdr(bd, &bc->hdr, has_write_perm); if (IS_ERR(rq)) { ret = PTR_ERR(rq); rq = NULL; @@ -683,10 +661,11 @@ bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) dprintk("%s: write %Zd bytes\n", bd->name, count); bsg_set_block(bd, file); - bsg_set_cmd_filter(bd, file); bytes_written = 0; - ret = __bsg_write(bd, buf, count, &bytes_written); + ret = __bsg_write(bd, buf, count, &bytes_written, + file->f_mode & FMODE_WRITE); + *ppos = bytes_written; /* @@ -792,7 +771,6 @@ static struct bsg_device *bsg_add_device(struct inode *inode, bd->queue = rq; bsg_set_block(bd, file); - bsg_set_cmd_filter(bd, file); atomic_set(&bd->ref_count, 1); mutex_lock(&bsg_mutex); @@ -943,7 +921,7 @@ static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_from_user(&hdr, uarg, sizeof(hdr))) return -EFAULT; - rq = bsg_map_hdr(bd, &hdr); + rq = bsg_map_hdr(bd, &hdr, file->f_mode & FMODE_WRITE); if (IS_ERR(rq)) return PTR_ERR(rq); diff --git a/block/cmd-filter.c b/block/cmd-filter.c index eec4404fd357..c705c33361a0 100644 --- a/block/cmd-filter.c +++ b/block/cmd-filter.c @@ -27,8 +27,8 @@ #include #include -int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, - unsigned char *cmd, mode_t *f_mode) +int blk_verify_command(struct blk_scsi_cmd_filter *filter, + unsigned char *cmd, int has_write_perm) { /* root can do any command. */ if (capable(CAP_SYS_RAWIO)) @@ -43,30 +43,11 @@ int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, return 0; /* Write-safe commands require a writable open */ - if (test_bit(cmd[0], filter->write_ok) && (*f_mode & FMODE_WRITE)) + if (test_bit(cmd[0], filter->write_ok) && has_write_perm) return 0; return -EPERM; } -EXPORT_SYMBOL(blk_cmd_filter_verify_command); - -int blk_verify_command(struct file *file, unsigned char *cmd) -{ - struct gendisk *disk; - struct inode *inode; - - if (!file) - return -EINVAL; - - inode = file->f_dentry->d_inode; - if (!inode) - return -EINVAL; - - disk = inode->i_bdev->bd_disk; - - return blk_cmd_filter_verify_command(&disk->cmd_filter, - cmd, &file->f_mode); -} EXPORT_SYMBOL(blk_verify_command); /* and now, the sysfs stuff */ @@ -219,114 +200,27 @@ static struct kobj_type rcf_ktype = { .default_attrs = default_attrs, }; -#ifndef MAINTENANCE_IN_CMD -#define MAINTENANCE_IN_CMD 0xa3 -#endif - -static void rcf_set_defaults(struct blk_scsi_cmd_filter *filter) -{ - /* Basic read-only commands */ - __set_bit(TEST_UNIT_READY, filter->read_ok); - __set_bit(REQUEST_SENSE, filter->read_ok); - __set_bit(READ_6, filter->read_ok); - __set_bit(READ_10, filter->read_ok); - __set_bit(READ_12, filter->read_ok); - __set_bit(READ_16, filter->read_ok); - __set_bit(READ_BUFFER, filter->read_ok); - __set_bit(READ_DEFECT_DATA, filter->read_ok); - __set_bit(READ_CAPACITY, filter->read_ok); - __set_bit(READ_LONG, filter->read_ok); - __set_bit(INQUIRY, filter->read_ok); - __set_bit(MODE_SENSE, filter->read_ok); - __set_bit(MODE_SENSE_10, filter->read_ok); - __set_bit(LOG_SENSE, filter->read_ok); - __set_bit(START_STOP, filter->read_ok); - __set_bit(GPCMD_VERIFY_10, filter->read_ok); - __set_bit(VERIFY_16, filter->read_ok); - __set_bit(REPORT_LUNS, filter->read_ok); - __set_bit(SERVICE_ACTION_IN, filter->read_ok); - __set_bit(RECEIVE_DIAGNOSTIC, filter->read_ok); - __set_bit(MAINTENANCE_IN_CMD, filter->read_ok); - __set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok); - - /* Audio CD commands */ - __set_bit(GPCMD_PLAY_CD, filter->read_ok); - __set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok); - __set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok); - __set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok); - __set_bit(GPCMD_PAUSE_RESUME, filter->read_ok); - - /* CD/DVD data reading */ - __set_bit(GPCMD_READ_CD, filter->read_ok); - __set_bit(GPCMD_READ_CD_MSF, filter->read_ok); - __set_bit(GPCMD_READ_DISC_INFO, filter->read_ok); - __set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok); - __set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok); - __set_bit(GPCMD_READ_HEADER, filter->read_ok); - __set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok); - __set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok); - __set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok); - __set_bit(GPCMD_REPORT_KEY, filter->read_ok); - __set_bit(GPCMD_SCAN, filter->read_ok); - __set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok); - __set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok); - __set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok); - __set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok); - __set_bit(GPCMD_SEEK, filter->read_ok); - __set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok); - - /* Basic writing commands */ - __set_bit(WRITE_6, filter->write_ok); - __set_bit(WRITE_10, filter->write_ok); - __set_bit(WRITE_VERIFY, filter->write_ok); - __set_bit(WRITE_12, filter->write_ok); - __set_bit(WRITE_VERIFY_12, filter->write_ok); - __set_bit(WRITE_16, filter->write_ok); - __set_bit(WRITE_LONG, filter->write_ok); - __set_bit(WRITE_LONG_2, filter->write_ok); - __set_bit(ERASE, filter->write_ok); - __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); - __set_bit(MODE_SELECT, filter->write_ok); - __set_bit(LOG_SELECT, filter->write_ok); - __set_bit(GPCMD_BLANK, filter->write_ok); - __set_bit(GPCMD_CLOSE_TRACK, filter->write_ok); - __set_bit(GPCMD_FLUSH_CACHE, filter->write_ok); - __set_bit(GPCMD_FORMAT_UNIT, filter->write_ok); - __set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok); - __set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok); - __set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok); - __set_bit(GPCMD_SEND_EVENT, filter->write_ok); - __set_bit(GPCMD_SEND_KEY, filter->write_ok); - __set_bit(GPCMD_SEND_OPC, filter->write_ok); - __set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok); - __set_bit(GPCMD_SET_SPEED, filter->write_ok); - __set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok); - __set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok); - __set_bit(GPCMD_SET_STREAMING, filter->write_ok); -} - int blk_register_filter(struct gendisk *disk) { int ret; - struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; + struct blk_scsi_cmd_filter *filter = &disk->queue->cmd_filter; struct kobject *parent = kobject_get(disk->holder_dir->parent); if (!parent) return -ENODEV; ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, parent, - "%s", "cmd_filter"); + "%s", "cmd_filter"); if (ret < 0) return ret; - rcf_set_defaults(filter); return 0; } void blk_unregister_filter(struct gendisk *disk) { - struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; + struct blk_scsi_cmd_filter *filter = &disk->queue->cmd_filter; kobject_put(&filter->kobj); kobject_put(disk->holder_dir->parent); diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 12a5182173f6..d01ef5ee427e 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -105,12 +105,96 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) return put_user(1, p); } +void blk_set_cmd_filter_defaults(struct blk_scsi_cmd_filter *filter) +{ + /* Basic read-only commands */ + __set_bit(TEST_UNIT_READY, filter->read_ok); + __set_bit(REQUEST_SENSE, filter->read_ok); + __set_bit(READ_6, filter->read_ok); + __set_bit(READ_10, filter->read_ok); + __set_bit(READ_12, filter->read_ok); + __set_bit(READ_16, filter->read_ok); + __set_bit(READ_BUFFER, filter->read_ok); + __set_bit(READ_DEFECT_DATA, filter->read_ok); + __set_bit(READ_CAPACITY, filter->read_ok); + __set_bit(READ_LONG, filter->read_ok); + __set_bit(INQUIRY, filter->read_ok); + __set_bit(MODE_SENSE, filter->read_ok); + __set_bit(MODE_SENSE_10, filter->read_ok); + __set_bit(LOG_SENSE, filter->read_ok); + __set_bit(START_STOP, filter->read_ok); + __set_bit(GPCMD_VERIFY_10, filter->read_ok); + __set_bit(VERIFY_16, filter->read_ok); + __set_bit(REPORT_LUNS, filter->read_ok); + __set_bit(SERVICE_ACTION_IN, filter->read_ok); + __set_bit(RECEIVE_DIAGNOSTIC, filter->read_ok); + __set_bit(MAINTENANCE_IN, filter->read_ok); + __set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok); + + /* Audio CD commands */ + __set_bit(GPCMD_PLAY_CD, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok); + __set_bit(GPCMD_PAUSE_RESUME, filter->read_ok); + + /* CD/DVD data reading */ + __set_bit(GPCMD_READ_CD, filter->read_ok); + __set_bit(GPCMD_READ_CD_MSF, filter->read_ok); + __set_bit(GPCMD_READ_DISC_INFO, filter->read_ok); + __set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok); + __set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok); + __set_bit(GPCMD_READ_HEADER, filter->read_ok); + __set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok); + __set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok); + __set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok); + __set_bit(GPCMD_REPORT_KEY, filter->read_ok); + __set_bit(GPCMD_SCAN, filter->read_ok); + __set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok); + __set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok); + __set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok); + __set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok); + __set_bit(GPCMD_SEEK, filter->read_ok); + __set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok); + + /* Basic writing commands */ + __set_bit(WRITE_6, filter->write_ok); + __set_bit(WRITE_10, filter->write_ok); + __set_bit(WRITE_VERIFY, filter->write_ok); + __set_bit(WRITE_12, filter->write_ok); + __set_bit(WRITE_VERIFY_12, filter->write_ok); + __set_bit(WRITE_16, filter->write_ok); + __set_bit(WRITE_LONG, filter->write_ok); + __set_bit(WRITE_LONG_2, filter->write_ok); + __set_bit(ERASE, filter->write_ok); + __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); + __set_bit(MODE_SELECT, filter->write_ok); + __set_bit(LOG_SELECT, filter->write_ok); + __set_bit(GPCMD_BLANK, filter->write_ok); + __set_bit(GPCMD_CLOSE_TRACK, filter->write_ok); + __set_bit(GPCMD_FLUSH_CACHE, filter->write_ok); + __set_bit(GPCMD_FORMAT_UNIT, filter->write_ok); + __set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok); + __set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok); + __set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok); + __set_bit(GPCMD_SEND_EVENT, filter->write_ok); + __set_bit(GPCMD_SEND_KEY, filter->write_ok); + __set_bit(GPCMD_SEND_OPC, filter->write_ok); + __set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok); + __set_bit(GPCMD_SET_SPEED, filter->write_ok); + __set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok); + __set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok); + __set_bit(GPCMD_SET_STREAMING, filter->write_ok); +} +EXPORT_SYMBOL_GPL(blk_set_cmd_filter_defaults); + static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, struct sg_io_hdr *hdr, struct file *file) { if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len)) return -EFAULT; - if (blk_verify_command(file, rq->cmd)) + if (blk_verify_command(&q->cmd_filter, rq->cmd, + file->f_mode & FMODE_WRITE)) return -EPERM; /* @@ -298,7 +382,7 @@ int sg_scsi_ioctl(struct file *file, struct request_queue *q, struct gendisk *disk, struct scsi_ioctl_command __user *sic) { struct request *rq; - int err; + int err, write_perm = 0; unsigned int in_len, out_len, bytes, opcode, cmdlen; char *buffer = NULL, sense[SCSI_SENSE_BUFFERSIZE]; @@ -340,7 +424,11 @@ int sg_scsi_ioctl(struct file *file, struct request_queue *q, if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) goto error; - err = blk_verify_command(file, rq->cmd); + /* scsi_ioctl passes NULL */ + if (file && (file->f_mode & FMODE_WRITE)) + write_perm = 1; + + err = blk_verify_command(&q->cmd_filter, rq->cmd, write_perm); if (err) goto error; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 3d36270a8b4d..9d28b9f74d90 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -641,6 +641,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, unsigned char cmnd[MAX_COMMAND_SIZE]; int timeout; unsigned long ul_timeout; + struct request_queue *q; if (count < SZ_SG_IO_HDR) return -EINVAL; @@ -689,7 +690,9 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, sg_remove_request(sfp, srp); return -EFAULT; } - if (read_only && !blk_verify_command(file, cmnd)) { + q = sfp->parentdp->device->request_queue; + if (read_only && blk_verify_command(&q->cmd_filter, cmnd, + file->f_mode & FMODE_WRITE)) { sg_remove_request(sfp, srp); return -EPERM; } @@ -793,6 +796,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: %s, cmd=0x%x\n", sdp->disk->disk_name, (int) cmd_in)); read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); @@ -1057,11 +1061,14 @@ sg_ioctl(struct inode *inode, struct file *filp, return -ENODEV; if (read_only) { unsigned char opcode = WRITE_6; + struct request_queue *q = sdp->device->request_queue; Scsi_Ioctl_Command __user *siocp = p; if (copy_from_user(&opcode, siocp->data, 1)) return -EFAULT; - if (!blk_verify_command(filp, &opcode)) + if (blk_verify_command(&q->cmd_filter, + &opcode, + filp->f_mode & FMODE_WRITE)) return -EPERM; } return sg_scsi_ioctl(filp, sdp->device->request_queue, NULL, p); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e61f22be4d0e..d2d34e2774b2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -280,6 +280,15 @@ struct blk_queue_tag { atomic_t refcnt; /* map can be shared */ }; +#define BLK_SCSI_MAX_CMDS (256) +#define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) + +struct blk_scsi_cmd_filter { + unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; + unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; + struct kobject kobj; +}; + struct request_queue { /* @@ -398,6 +407,7 @@ struct request_queue #if defined(CONFIG_BLK_DEV_BSG) struct bsg_class_device bsg_dev; #endif + struct blk_scsi_cmd_filter cmd_filter; }; #define QUEUE_FLAG_CLUSTER 0 /* cluster several segments into 1 */ @@ -833,11 +843,11 @@ extern int blkdev_issue_flush(struct block_device *, sector_t *); /* * command filter functions */ -extern int blk_verify_command(struct file *file, unsigned char *cmd); -extern int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, - unsigned char *cmd, mode_t *f_mode); +extern int blk_verify_command(struct blk_scsi_cmd_filter *filter, + unsigned char *cmd, int has_write_perm); extern int blk_register_filter(struct gendisk *disk); extern void blk_unregister_filter(struct gendisk *disk); +extern void blk_set_cmd_filter_defaults(struct blk_scsi_cmd_filter *filter); #define MAX_PHYS_SEGMENTS 128 #define MAX_HW_SEGMENTS 128 diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 118216f1bd3c..be4f5e5bfe06 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -110,15 +110,6 @@ struct hd_struct { #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 #define GENHD_FL_FAIL 64 -#define BLK_SCSI_MAX_CMDS (256) -#define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) - -struct blk_scsi_cmd_filter { - unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; - unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; - struct kobject kobj; -}; - struct gendisk { int major; /* major number of driver */ int first_minor; @@ -128,7 +119,6 @@ struct gendisk { struct hd_struct **part; /* [indexed by minor] */ struct block_device_operations *fops; struct request_queue *queue; - struct blk_scsi_cmd_filter cmd_filter; void *private_data; sector_t capacity; -- cgit v1.2.3 From 4beab5c623fef4622f9a8593f85760ff10b5a3f7 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 26 Jul 2008 18:03:25 +0900 Subject: block: rename blk_scsi_cmd_filter to blk_cmd_filter Technically, the cmd_filter would be applied to other protocols though it's unlikely to happen. Putting SCSI stuff to request_queue is kinda layer violation. So let's rename it. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- block/cmd-filter.c | 30 +++++++++++++++--------------- block/scsi_ioctl.c | 2 +- include/linux/blkdev.h | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/block/cmd-filter.c b/block/cmd-filter.c index c705c33361a0..0e3a123944a8 100644 --- a/block/cmd-filter.c +++ b/block/cmd-filter.c @@ -27,7 +27,7 @@ #include #include -int blk_verify_command(struct blk_scsi_cmd_filter *filter, +int blk_verify_command(struct blk_cmd_filter *filter, unsigned char *cmd, int has_write_perm) { /* root can do any command. */ @@ -51,7 +51,7 @@ int blk_verify_command(struct blk_scsi_cmd_filter *filter, EXPORT_SYMBOL(blk_verify_command); /* and now, the sysfs stuff */ -static ssize_t rcf_cmds_show(struct blk_scsi_cmd_filter *filter, char *page, +static ssize_t rcf_cmds_show(struct blk_cmd_filter *filter, char *page, int rw) { char *npage = page; @@ -78,18 +78,18 @@ static ssize_t rcf_cmds_show(struct blk_scsi_cmd_filter *filter, char *page, return npage - page; } -static ssize_t rcf_readcmds_show(struct blk_scsi_cmd_filter *filter, char *page) +static ssize_t rcf_readcmds_show(struct blk_cmd_filter *filter, char *page) { return rcf_cmds_show(filter, page, READ); } -static ssize_t rcf_writecmds_show(struct blk_scsi_cmd_filter *filter, +static ssize_t rcf_writecmds_show(struct blk_cmd_filter *filter, char *page) { return rcf_cmds_show(filter, page, WRITE); } -static ssize_t rcf_cmds_store(struct blk_scsi_cmd_filter *filter, +static ssize_t rcf_cmds_store(struct blk_cmd_filter *filter, const char *page, size_t count, int rw) { ssize_t ret = 0; @@ -122,13 +122,13 @@ static ssize_t rcf_cmds_store(struct blk_scsi_cmd_filter *filter, return count; } -static ssize_t rcf_readcmds_store(struct blk_scsi_cmd_filter *filter, +static ssize_t rcf_readcmds_store(struct blk_cmd_filter *filter, const char *page, size_t count) { return rcf_cmds_store(filter, page, count, READ); } -static ssize_t rcf_writecmds_store(struct blk_scsi_cmd_filter *filter, +static ssize_t rcf_writecmds_store(struct blk_cmd_filter *filter, const char *page, size_t count) { return rcf_cmds_store(filter, page, count, WRITE); @@ -136,8 +136,8 @@ static ssize_t rcf_writecmds_store(struct blk_scsi_cmd_filter *filter, struct rcf_sysfs_entry { struct attribute attr; - ssize_t (*show)(struct blk_scsi_cmd_filter *, char *); - ssize_t (*store)(struct blk_scsi_cmd_filter *, const char *, size_t); + ssize_t (*show)(struct blk_cmd_filter *, char *); + ssize_t (*store)(struct blk_cmd_filter *, const char *, size_t); }; static struct rcf_sysfs_entry rcf_readcmds_entry = { @@ -164,9 +164,9 @@ static ssize_t rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page) { struct rcf_sysfs_entry *entry = to_rcf(attr); - struct blk_scsi_cmd_filter *filter; + struct blk_cmd_filter *filter; - filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); + filter = container_of(kobj, struct blk_cmd_filter, kobj); if (entry->show) return entry->show(filter, page); @@ -178,7 +178,7 @@ rcf_attr_store(struct kobject *kobj, struct attribute *attr, const char *page, size_t length) { struct rcf_sysfs_entry *entry = to_rcf(attr); - struct blk_scsi_cmd_filter *filter; + struct blk_cmd_filter *filter; if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -186,7 +186,7 @@ rcf_attr_store(struct kobject *kobj, struct attribute *attr, if (!entry->store) return -EINVAL; - filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); + filter = container_of(kobj, struct blk_cmd_filter, kobj); return entry->store(filter, page, length); } @@ -203,7 +203,7 @@ static struct kobj_type rcf_ktype = { int blk_register_filter(struct gendisk *disk) { int ret; - struct blk_scsi_cmd_filter *filter = &disk->queue->cmd_filter; + struct blk_cmd_filter *filter = &disk->queue->cmd_filter; struct kobject *parent = kobject_get(disk->holder_dir->parent); if (!parent) @@ -220,7 +220,7 @@ int blk_register_filter(struct gendisk *disk) void blk_unregister_filter(struct gendisk *disk) { - struct blk_scsi_cmd_filter *filter = &disk->queue->cmd_filter; + struct blk_cmd_filter *filter = &disk->queue->cmd_filter; kobject_put(&filter->kobj); kobject_put(disk->holder_dir->parent); diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index d01ef5ee427e..ec4b7f234626 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -105,7 +105,7 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) return put_user(1, p); } -void blk_set_cmd_filter_defaults(struct blk_scsi_cmd_filter *filter) +void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) { /* Basic read-only commands */ __set_bit(TEST_UNIT_READY, filter->read_ok); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d2d34e2774b2..ab247d589ad6 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -283,7 +283,7 @@ struct blk_queue_tag { #define BLK_SCSI_MAX_CMDS (256) #define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) -struct blk_scsi_cmd_filter { +struct blk_cmd_filter { unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; struct kobject kobj; @@ -407,7 +407,7 @@ struct request_queue #if defined(CONFIG_BLK_DEV_BSG) struct bsg_class_device bsg_dev; #endif - struct blk_scsi_cmd_filter cmd_filter; + struct blk_cmd_filter cmd_filter; }; #define QUEUE_FLAG_CLUSTER 0 /* cluster several segments into 1 */ @@ -843,11 +843,11 @@ extern int blkdev_issue_flush(struct block_device *, sector_t *); /* * command filter functions */ -extern int blk_verify_command(struct blk_scsi_cmd_filter *filter, +extern int blk_verify_command(struct blk_cmd_filter *filter, unsigned char *cmd, int has_write_perm); extern int blk_register_filter(struct gendisk *disk); extern void blk_unregister_filter(struct gendisk *disk); -extern void blk_set_cmd_filter_defaults(struct blk_scsi_cmd_filter *filter); +extern void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter); #define MAX_PHYS_SEGMENTS 128 #define MAX_HW_SEGMENTS 128 -- cgit v1.2.3 From 5168c47b4c294412f079dd3cc891e0276bb0479e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 26 Aug 2008 09:03:17 +0200 Subject: block: remove blk_queue_tag_depth() and blk_queue_tag_queue() They are unused and ->busy doesn't exist anymore. Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ab247d589ad6..44710d7e7bff 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -817,8 +817,6 @@ extern void blk_put_queue(struct request_queue *); /* * tag stuff */ -#define blk_queue_tag_depth(q) ((q)->queue_tags->busy) -#define blk_queue_tag_queue(q) ((q)->queue_tags->busy < (q)->queue_tags->max_depth) #define blk_rq_tagged(rq) ((rq)->cmd_flags & REQ_QUEUED) extern int blk_queue_start_tag(struct request_queue *, struct request *); extern struct request *blk_queue_find_tag(struct request_queue *, int); -- cgit v1.2.3 From da31894ed7b654e2e1741e7ac4ef6c15be0dd14b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 22 Aug 2008 11:35:57 -0400 Subject: securityfs: do not depend on CONFIG_SECURITY Add a new Kconfig option SECURITYFS which will build securityfs support but does not require CONFIG_SECURITY. The only current user of securityfs does not depend on CONFIG_SECURITY and there is no reason the full LSM needs to be built to build this fs. Signed-off-by: Eric Paris Signed-off-by: James Morris --- drivers/char/tpm/Kconfig | 1 + include/linux/security.h | 54 +++++++++++++++++++++++++++--------------------- security/Kconfig | 8 +++++++ security/Makefile | 3 ++- 4 files changed, 41 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 3738cfa209ff..f5fc64f89c5c 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -6,6 +6,7 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM depends on EXPERIMENTAL + select SECURITYFS ---help--- If you have a TPM security chip in your system, which implements the Trusted Computing Group's specification, diff --git a/include/linux/security.h b/include/linux/security.h index 80c4d002864c..f5c4a51eb42e 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1560,11 +1560,6 @@ struct security_operations { extern int security_init(void); extern int security_module_enable(struct security_operations *ops); extern int register_security(struct security_operations *ops); -extern struct dentry *securityfs_create_file(const char *name, mode_t mode, - struct dentry *parent, void *data, - const struct file_operations *fops); -extern struct dentry *securityfs_create_dir(const char *name, struct dentry *parent); -extern void securityfs_remove(struct dentry *dentry); /* Security operations */ int security_ptrace_may_access(struct task_struct *child, unsigned int mode); @@ -2424,25 +2419,6 @@ static inline int security_netlink_recv(struct sk_buff *skb, int cap) return cap_netlink_recv(skb, cap); } -static inline struct dentry *securityfs_create_dir(const char *name, - struct dentry *parent) -{ - return ERR_PTR(-ENODEV); -} - -static inline struct dentry *securityfs_create_file(const char *name, - mode_t mode, - struct dentry *parent, - void *data, - const struct file_operations *fops) -{ - return ERR_PTR(-ENODEV); -} - -static inline void securityfs_remove(struct dentry *dentry) -{ -} - static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return -EOPNOTSUPP; @@ -2806,5 +2782,35 @@ static inline void security_audit_rule_free(void *lsmrule) #endif /* CONFIG_SECURITY */ #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_SECURITYFS + +extern struct dentry *securityfs_create_file(const char *name, mode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); +extern struct dentry *securityfs_create_dir(const char *name, struct dentry *parent); +extern void securityfs_remove(struct dentry *dentry); + +#else /* CONFIG_SECURITYFS */ + +static inline struct dentry *securityfs_create_dir(const char *name, + struct dentry *parent) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct dentry *securityfs_create_file(const char *name, + mode_t mode, + struct dentry *parent, + void *data, + const struct file_operations *fops) +{ + return ERR_PTR(-ENODEV); +} + +static inline void securityfs_remove(struct dentry *dentry) +{} + +#endif + #endif /* ! __LINUX_SECURITY_H */ diff --git a/security/Kconfig b/security/Kconfig index 559293922a47..d9f47ce7e207 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -51,6 +51,14 @@ config SECURITY If you are unsure how to answer this question, answer N. +config SECURITYFS + bool "Enable the securityfs filesystem" + help + This will build the securityfs filesystem. It is currently used by + the TPM bios character driver. It is not used by SELinux or SMACK. + + If you are unsure how to answer this question, answer N. + config SECURITY_NETWORK bool "Socket and Networking Security Hooks" depends on SECURITY diff --git a/security/Makefile b/security/Makefile index f65426099aa6..c05c127fff9a 100644 --- a/security/Makefile +++ b/security/Makefile @@ -10,7 +10,8 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack obj-y += commoncap.o # Object file lists -obj-$(CONFIG_SECURITY) += security.o capability.o inode.o +obj-$(CONFIG_SECURITY) += security.o capability.o +obj-$(CONFIG_SECURITYFS) += inode.o # Must precede capability.o in order to stack properly. obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o -- cgit v1.2.3 From 96e21e4fbc1b83a3445553381ec74f904618562e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Aug 2008 08:33:23 +0200 Subject: i2c: Add missing kerneldoc descriptions Add missing kernel descriptions of struct i2c_driver members. Signed-off-by: Jean Delvare Acked-by: Randy Dunlap Cc: David Brownell --- include/linux/i2c.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 08be0d21864c..06115128047f 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -97,7 +97,19 @@ extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, /** * struct i2c_driver - represent an I2C device driver + * @id: Unique driver ID (optional) * @class: What kind of i2c device we instantiate (for detect) + * @attach_adapter: Callback for bus addition (for legacy drivers) + * @detach_adapter: Callback for bus removal (for legacy drivers) + * @detach_client: Callback for device removal (for legacy drivers) + * @probe: Callback for device binding (new-style drivers) + * @remove: Callback for device unbinding (new-style drivers) + * @shutdown: Callback for device shutdown + * @suspend: Callback for device suspend + * @resume: Callback for device resume + * @command: Callback for bus-wide signaling (optional) + * @driver: Device driver model driver + * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection * @address_data: The I2C addresses to probe, ignore or force (for detect) * @clients: List of detected clients we created (for i2c-core use only) -- cgit v1.2.3 From 0f8e0d9a317406612700426fad3efab0b7bbc467 Mon Sep 17 00:00:00 2001 From: David Teigland Date: Wed, 6 Aug 2008 13:30:24 -0500 Subject: dlm: allow multiple lockspace creates Add a count for lockspace create and release so that create can be called multiple times to use the lockspace from different places. Also add the new flag DLM_LSFL_NEWEXCL to create a lockspace with the previous behavior of returning -EEXIST if the lockspace already exists. Signed-off-by: David Teigland --- fs/dlm/dlm_internal.h | 6 ++- fs/dlm/lockspace.c | 107 +++++++++++++++++++++++++++++--------------- fs/dlm/user.c | 54 +++++++++++++--------- fs/dlm/user.h | 3 +- fs/gfs2/locking/dlm/mount.c | 3 +- include/linux/dlm.h | 5 ++- include/linux/dlm_device.h | 2 +- 7 files changed, 116 insertions(+), 64 deletions(-) (limited to 'include/linux') diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h index 5a7ac33b629c..9e0622aff496 100644 --- a/fs/dlm/dlm_internal.h +++ b/fs/dlm/dlm_internal.h @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -441,7 +441,9 @@ struct dlm_ls { uint32_t ls_global_id; /* global unique lockspace ID */ uint32_t ls_exflags; int ls_lvblen; - int ls_count; /* reference count */ + int ls_count; /* refcount of processes in + the dlm using this ls */ + int ls_create_count; /* create/release refcount */ unsigned long ls_flags; /* LSFL_ */ struct kobject ls_kobj; diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c index 499e16759e96..56eae4e4a954 100644 --- a/fs/dlm/lockspace.c +++ b/fs/dlm/lockspace.c @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -23,6 +23,7 @@ #include "lock.h" #include "recover.h" #include "requestqueue.h" +#include "user.h" static int ls_count; static struct mutex ls_lock; @@ -246,23 +247,6 @@ static void dlm_scand_stop(void) kthread_stop(scand_task); } -static struct dlm_ls *dlm_find_lockspace_name(char *name, int namelen) -{ - struct dlm_ls *ls; - - spin_lock(&lslist_lock); - - list_for_each_entry(ls, &lslist, ls_list) { - if (ls->ls_namelen == namelen && - memcmp(ls->ls_name, name, namelen) == 0) - goto out; - } - ls = NULL; - out: - spin_unlock(&lslist_lock); - return ls; -} - struct dlm_ls *dlm_find_lockspace_global(uint32_t id) { struct dlm_ls *ls; @@ -327,6 +311,7 @@ static void remove_lockspace(struct dlm_ls *ls) for (;;) { spin_lock(&lslist_lock); if (ls->ls_count == 0) { + WARN_ON(ls->ls_create_count != 0); list_del(&ls->ls_list); spin_unlock(&lslist_lock); return; @@ -381,7 +366,7 @@ static int new_lockspace(char *name, int namelen, void **lockspace, uint32_t flags, int lvblen) { struct dlm_ls *ls; - int i, size, error = -ENOMEM; + int i, size, error; int do_unreg = 0; if (namelen > DLM_LOCKSPACE_LEN) @@ -393,12 +378,32 @@ static int new_lockspace(char *name, int namelen, void **lockspace, if (!try_module_get(THIS_MODULE)) return -EINVAL; - ls = dlm_find_lockspace_name(name, namelen); - if (ls) { - *lockspace = ls; + error = 0; + + spin_lock(&lslist_lock); + list_for_each_entry(ls, &lslist, ls_list) { + WARN_ON(ls->ls_create_count <= 0); + if (ls->ls_namelen != namelen) + continue; + if (memcmp(ls->ls_name, name, namelen)) + continue; + if (flags & DLM_LSFL_NEWEXCL) { + error = -EEXIST; + break; + } + ls->ls_create_count++; module_put(THIS_MODULE); - return -EEXIST; + error = 1; /* not an error, return 0 */ + break; } + spin_unlock(&lslist_lock); + + if (error < 0) + goto out; + if (error) + goto ret_zero; + + error = -ENOMEM; ls = kzalloc(sizeof(struct dlm_ls) + namelen, GFP_KERNEL); if (!ls) @@ -418,8 +423,9 @@ static int new_lockspace(char *name, int namelen, void **lockspace, ls->ls_allocation = GFP_KERNEL; /* ls_exflags are forced to match among nodes, and we don't - need to require all nodes to have TIMEWARN or FS set */ - ls->ls_exflags = (flags & ~(DLM_LSFL_TIMEWARN | DLM_LSFL_FS)); + need to require all nodes to have some flags set */ + ls->ls_exflags = (flags & ~(DLM_LSFL_TIMEWARN | DLM_LSFL_FS | + DLM_LSFL_NEWEXCL)); size = dlm_config.ci_rsbtbl_size; ls->ls_rsbtbl_size = size; @@ -510,6 +516,7 @@ static int new_lockspace(char *name, int namelen, void **lockspace, down_write(&ls->ls_in_recovery); spin_lock(&lslist_lock); + ls->ls_create_count = 1; list_add(&ls->ls_list, &lslist); spin_unlock(&lslist_lock); @@ -548,7 +555,7 @@ static int new_lockspace(char *name, int namelen, void **lockspace, dlm_create_debug_file(ls); log_debug(ls, "join complete"); - + ret_zero: *lockspace = ls; return 0; @@ -635,11 +642,32 @@ static int release_lockspace(struct dlm_ls *ls, int force) struct dlm_lkb *lkb; struct dlm_rsb *rsb; struct list_head *head; - int i; - int busy = lockspace_busy(ls); + int i, busy, rv; + + busy = lockspace_busy(ls); + + spin_lock(&lslist_lock); + if (ls->ls_create_count == 1) { + if (busy > force) + rv = -EBUSY; + else { + /* remove_lockspace takes ls off lslist */ + ls->ls_create_count = 0; + rv = 0; + } + } else if (ls->ls_create_count > 1) { + rv = --ls->ls_create_count; + } else { + rv = -EINVAL; + } + spin_unlock(&lslist_lock); + + if (rv) { + log_debug(ls, "release_lockspace no remove %d", rv); + return rv; + } - if (busy > force) - return -EBUSY; + dlm_device_deregister(ls); if (force < 3) do_uevent(ls, 0); @@ -720,15 +748,10 @@ static int release_lockspace(struct dlm_ls *ls, int force) dlm_clear_members(ls); dlm_clear_members_gone(ls); kfree(ls->ls_node_array); + log_debug(ls, "release_lockspace final free"); kobject_put(&ls->ls_kobj); /* The ls structure will be freed when the kobject is done with */ - mutex_lock(&ls_lock); - ls_count--; - if (!ls_count) - threads_stop(); - mutex_unlock(&ls_lock); - module_put(THIS_MODULE); return 0; } @@ -750,11 +773,21 @@ static int release_lockspace(struct dlm_ls *ls, int force) int dlm_release_lockspace(void *lockspace, int force) { struct dlm_ls *ls; + int error; ls = dlm_find_lockspace_local(lockspace); if (!ls) return -EINVAL; dlm_put_lockspace(ls); - return release_lockspace(ls, force); + + mutex_lock(&ls_lock); + error = release_lockspace(ls, force); + if (!error) + ls_count--; + else if (!ls_count) + threads_stop(); + mutex_unlock(&ls_lock); + + return error; } diff --git a/fs/dlm/user.c b/fs/dlm/user.c index 34f14a14fb4e..6542110c0da4 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions @@ -340,10 +340,15 @@ static int device_user_deadlock(struct dlm_user_proc *proc, return error; } -static int create_misc_device(struct dlm_ls *ls, char *name) +static int dlm_device_register(struct dlm_ls *ls, char *name) { int error, len; + /* The device is already registered. This happens when the + lockspace is created multiple times from userspace. */ + if (ls->ls_device.name) + return 0; + error = -ENOMEM; len = strlen(name) + strlen(name_prefix) + 2; ls->ls_device.name = kzalloc(len, GFP_KERNEL); @@ -363,6 +368,22 @@ fail: return error; } +int dlm_device_deregister(struct dlm_ls *ls) +{ + int error; + + /* The device is not registered. This happens when the lockspace + was never used from userspace, or when device_create_lockspace() + calls dlm_release_lockspace() after the register fails. */ + if (!ls->ls_device.name) + return 0; + + error = misc_deregister(&ls->ls_device); + if (!error) + kfree(ls->ls_device.name); + return error; +} + static int device_user_purge(struct dlm_user_proc *proc, struct dlm_purge_params *params) { @@ -397,7 +418,7 @@ static int device_create_lockspace(struct dlm_lspace_params *params) if (!ls) return -ENOENT; - error = create_misc_device(ls, params->name); + error = dlm_device_register(ls, params->name); dlm_put_lockspace(ls); if (error) @@ -421,31 +442,22 @@ static int device_remove_lockspace(struct dlm_lspace_params *params) if (!ls) return -ENOENT; - /* Deregister the misc device first, so we don't have - * a device that's not attached to a lockspace. If - * dlm_release_lockspace fails then we can recreate it - */ - error = misc_deregister(&ls->ls_device); - if (error) { - dlm_put_lockspace(ls); - goto out; - } - kfree(ls->ls_device.name); - if (params->flags & DLM_USER_LSFLG_FORCEFREE) force = 2; lockspace = ls->ls_local_handle; + dlm_put_lockspace(ls); - /* dlm_release_lockspace waits for references to go to zero, - so all processes will need to close their device for the ls - before the release will procede */ + /* The final dlm_release_lockspace waits for references to go to + zero, so all processes will need to close their device for the + ls before the release will proceed. release also calls the + device_deregister above. Converting a positive return value + from release to zero means that userspace won't know when its + release was the final one, but it shouldn't need to know. */ - dlm_put_lockspace(ls); error = dlm_release_lockspace(lockspace, force); - if (error) - create_misc_device(ls, ls->ls_name); - out: + if (error > 0) + error = 0; return error; } diff --git a/fs/dlm/user.h b/fs/dlm/user.h index d38e9f3e4151..c528b6b2991b 100644 --- a/fs/dlm/user.h +++ b/fs/dlm/user.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions @@ -12,5 +12,6 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type); int dlm_user_init(void); void dlm_user_exit(void); +int dlm_device_deregister(struct dlm_ls *ls); #endif diff --git a/fs/gfs2/locking/dlm/mount.c b/fs/gfs2/locking/dlm/mount.c index 09d78c216f48..0c4cbe6c8285 100644 --- a/fs/gfs2/locking/dlm/mount.c +++ b/fs/gfs2/locking/dlm/mount.c @@ -144,7 +144,8 @@ static int gdlm_mount(char *table_name, char *host_data, error = dlm_new_lockspace(ls->fsname, strlen(ls->fsname), &ls->dlm_lockspace, - DLM_LSFL_FS | (nodir ? DLM_LSFL_NODIR : 0), + DLM_LSFL_FS | DLM_LSFL_NEWEXCL | + (nodir ? DLM_LSFL_NODIR : 0), GDLM_LVB_SIZE); if (error) { log_error("dlm_new_lockspace error %d", error); diff --git a/include/linux/dlm.h b/include/linux/dlm.h index 203a025e30e5..b9cd38603fd8 100644 --- a/include/linux/dlm.h +++ b/include/linux/dlm.h @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -65,9 +65,12 @@ struct dlm_lksb { char * sb_lvbptr; }; +/* dlm_new_lockspace() flags */ + #define DLM_LSFL_NODIR 0x00000001 #define DLM_LSFL_TIMEWARN 0x00000002 #define DLM_LSFL_FS 0x00000004 +#define DLM_LSFL_NEWEXCL 0x00000008 #ifdef __KERNEL__ diff --git a/include/linux/dlm_device.h b/include/linux/dlm_device.h index c6034508fed9..3060783c4191 100644 --- a/include/linux/dlm_device.h +++ b/include/linux/dlm_device.h @@ -26,7 +26,7 @@ /* Version of the device interface */ #define DLM_DEVICE_VERSION_MAJOR 6 #define DLM_DEVICE_VERSION_MINOR 0 -#define DLM_DEVICE_VERSION_PATCH 0 +#define DLM_DEVICE_VERSION_PATCH 1 /* struct passed to the lock write */ struct dlm_lock_params { -- cgit v1.2.3 From da7f033ddc9fdebb3223b0bf88a2a2ab5b797608 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 31 Jul 2008 17:08:25 +0800 Subject: crypto: cryptomgr - Add test infrastructure This patch moves the newly created alg_test infrastructure into cryptomgr. This shall allow us to use it for testing at algorithm registrations. Signed-off-by: Herbert Xu --- crypto/Kconfig | 8 +- crypto/Makefile | 2 + crypto/algboss.c | 238 ++ crypto/cryptomgr.c | 222 -- crypto/internal.h | 3 + crypto/tcrypt.c | 1708 +--------- crypto/tcrypt.h | 8713 +---------------------------------------------- crypto/testmgr.c | 1746 ++++++++++ crypto/testmgr.h | 8738 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/crypto.h | 2 + 10 files changed, 10747 insertions(+), 10633 deletions(-) create mode 100644 crypto/algboss.c delete mode 100644 crypto/cryptomgr.c create mode 100644 crypto/testmgr.c create mode 100644 crypto/testmgr.h (limited to 'include/linux') diff --git a/crypto/Kconfig b/crypto/Kconfig index 797b9e15d720..776f90d249a0 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -40,7 +40,9 @@ config CRYPTO_HASH config CRYPTO_MANAGER tristate "Cryptographic algorithm manager" - select CRYPTO_ALGAPI + select CRYPTO_AEAD + select CRYPTO_HASH + select CRYPTO_BLKCIPHER help Create default cryptographic template instantiations such as cbc(aes). @@ -85,9 +87,7 @@ config CRYPTO_AUTHENC config CRYPTO_TEST tristate "Testing module" depends on m - select CRYPTO_ALGAPI - select CRYPTO_AEAD - select CRYPTO_BLKCIPHER + select CRYPTO_MANAGER help Quick & dirty crypto test module. diff --git a/crypto/Makefile b/crypto/Makefile index d4f3ed857df0..59ab5008eb99 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -22,6 +22,8 @@ crypto_hash-objs := hash.o crypto_hash-objs += ahash.o obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o +cryptomgr-objs := algboss.o testmgr.o + obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o obj-$(CONFIG_CRYPTO_HMAC) += hmac.o obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o diff --git a/crypto/algboss.c b/crypto/algboss.c new file mode 100644 index 000000000000..2662ac014841 --- /dev/null +++ b/crypto/algboss.c @@ -0,0 +1,238 @@ +/* + * Create default crypto algorithm instances. + * + * Copyright (c) 2006 Herbert Xu + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +struct cryptomgr_param { + struct rtattr *tb[CRYPTO_MAX_ATTRS + 2]; + + struct { + struct rtattr attr; + struct crypto_attr_type data; + } type; + + union { + struct rtattr attr; + struct { + struct rtattr attr; + struct crypto_attr_alg data; + } alg; + struct { + struct rtattr attr; + struct crypto_attr_u32 data; + } nu32; + } attrs[CRYPTO_MAX_ATTRS]; + + char larval[CRYPTO_MAX_ALG_NAME]; + char template[CRYPTO_MAX_ALG_NAME]; +}; + +static int cryptomgr_probe(void *data) +{ + struct cryptomgr_param *param = data; + struct crypto_template *tmpl; + struct crypto_instance *inst; + int err; + + tmpl = crypto_lookup_template(param->template); + if (!tmpl) + goto err; + + do { + inst = tmpl->alloc(param->tb); + if (IS_ERR(inst)) + err = PTR_ERR(inst); + else if ((err = crypto_register_instance(tmpl, inst))) + tmpl->free(inst); + } while (err == -EAGAIN && !signal_pending(current)); + + crypto_tmpl_put(tmpl); + + if (err) + goto err; + +out: + kfree(param); + module_put_and_exit(0); + +err: + crypto_larval_error(param->larval, param->type.data.type, + param->type.data.mask); + goto out; +} + +static int cryptomgr_schedule_probe(struct crypto_larval *larval) +{ + struct task_struct *thread; + struct cryptomgr_param *param; + const char *name = larval->alg.cra_name; + const char *p; + unsigned int len; + int i; + + if (!try_module_get(THIS_MODULE)) + goto err; + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) + goto err_put_module; + + for (p = name; isalnum(*p) || *p == '-' || *p == '_'; p++) + ; + + len = p - name; + if (!len || *p != '(') + goto err_free_param; + + memcpy(param->template, name, len); + + i = 0; + for (;;) { + int notnum = 0; + + name = ++p; + len = 0; + + for (; isalnum(*p) || *p == '-' || *p == '_'; p++) + notnum |= !isdigit(*p); + + if (*p == '(') { + int recursion = 0; + + for (;;) { + if (!*++p) + goto err_free_param; + if (*p == '(') + recursion++; + else if (*p == ')' && !recursion--) + break; + } + + notnum = 1; + p++; + } + + len = p - name; + if (!len) + goto err_free_param; + + if (notnum) { + param->attrs[i].alg.attr.rta_len = + sizeof(param->attrs[i].alg); + param->attrs[i].alg.attr.rta_type = CRYPTOA_ALG; + memcpy(param->attrs[i].alg.data.name, name, len); + } else { + param->attrs[i].nu32.attr.rta_len = + sizeof(param->attrs[i].nu32); + param->attrs[i].nu32.attr.rta_type = CRYPTOA_U32; + param->attrs[i].nu32.data.num = + simple_strtol(name, NULL, 0); + } + + param->tb[i + 1] = ¶m->attrs[i].attr; + i++; + + if (i >= CRYPTO_MAX_ATTRS) + goto err_free_param; + + if (*p == ')') + break; + + if (*p != ',') + goto err_free_param; + } + + if (!i) + goto err_free_param; + + param->tb[i + 1] = NULL; + + param->type.attr.rta_len = sizeof(param->type); + param->type.attr.rta_type = CRYPTOA_TYPE; + param->type.data.type = larval->alg.cra_flags; + param->type.data.mask = larval->mask; + param->tb[0] = ¶m->type.attr; + + memcpy(param->larval, larval->alg.cra_name, CRYPTO_MAX_ALG_NAME); + + thread = kthread_run(cryptomgr_probe, param, "cryptomgr"); + if (IS_ERR(thread)) + goto err_free_param; + + return NOTIFY_STOP; + +err_free_param: + kfree(param); +err_put_module: + module_put(THIS_MODULE); +err: + return NOTIFY_OK; +} + +static int cryptomgr_notify(struct notifier_block *this, unsigned long msg, + void *data) +{ + switch (msg) { + case CRYPTO_MSG_ALG_REQUEST: + return cryptomgr_schedule_probe(data); + } + + return NOTIFY_DONE; +} + +static struct notifier_block cryptomgr_notifier = { + .notifier_call = cryptomgr_notify, +}; + +static int __init cryptomgr_init(void) +{ + int err; + + err = testmgr_init(); + if (err) + return err; + + err = crypto_register_notifier(&cryptomgr_notifier); + if (err) + goto free_testmgr; + + return 0; + +free_testmgr: + testmgr_exit(); + return err; +} + +static void __exit cryptomgr_exit(void) +{ + int err = crypto_unregister_notifier(&cryptomgr_notifier); + BUG_ON(err); + + testmgr_exit(); +} + +subsys_initcall(cryptomgr_init); +module_exit(cryptomgr_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Crypto Algorithm Manager"); diff --git a/crypto/cryptomgr.c b/crypto/cryptomgr.c deleted file mode 100644 index e5e3cf848d42..000000000000 --- a/crypto/cryptomgr.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Create default crypto algorithm instances. - * - * Copyright (c) 2006 Herbert Xu - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "internal.h" - -struct cryptomgr_param { - struct rtattr *tb[CRYPTO_MAX_ATTRS + 2]; - - struct { - struct rtattr attr; - struct crypto_attr_type data; - } type; - - union { - struct rtattr attr; - struct { - struct rtattr attr; - struct crypto_attr_alg data; - } alg; - struct { - struct rtattr attr; - struct crypto_attr_u32 data; - } nu32; - } attrs[CRYPTO_MAX_ATTRS]; - - char larval[CRYPTO_MAX_ALG_NAME]; - char template[CRYPTO_MAX_ALG_NAME]; -}; - -static int cryptomgr_probe(void *data) -{ - struct cryptomgr_param *param = data; - struct crypto_template *tmpl; - struct crypto_instance *inst; - int err; - - tmpl = crypto_lookup_template(param->template); - if (!tmpl) - goto err; - - do { - inst = tmpl->alloc(param->tb); - if (IS_ERR(inst)) - err = PTR_ERR(inst); - else if ((err = crypto_register_instance(tmpl, inst))) - tmpl->free(inst); - } while (err == -EAGAIN && !signal_pending(current)); - - crypto_tmpl_put(tmpl); - - if (err) - goto err; - -out: - kfree(param); - module_put_and_exit(0); - -err: - crypto_larval_error(param->larval, param->type.data.type, - param->type.data.mask); - goto out; -} - -static int cryptomgr_schedule_probe(struct crypto_larval *larval) -{ - struct task_struct *thread; - struct cryptomgr_param *param; - const char *name = larval->alg.cra_name; - const char *p; - unsigned int len; - int i; - - if (!try_module_get(THIS_MODULE)) - goto err; - - param = kzalloc(sizeof(*param), GFP_KERNEL); - if (!param) - goto err_put_module; - - for (p = name; isalnum(*p) || *p == '-' || *p == '_'; p++) - ; - - len = p - name; - if (!len || *p != '(') - goto err_free_param; - - memcpy(param->template, name, len); - - i = 0; - for (;;) { - int notnum = 0; - - name = ++p; - len = 0; - - for (; isalnum(*p) || *p == '-' || *p == '_'; p++) - notnum |= !isdigit(*p); - - if (*p == '(') { - int recursion = 0; - - for (;;) { - if (!*++p) - goto err_free_param; - if (*p == '(') - recursion++; - else if (*p == ')' && !recursion--) - break; - } - - notnum = 1; - p++; - } - - len = p - name; - if (!len) - goto err_free_param; - - if (notnum) { - param->attrs[i].alg.attr.rta_len = - sizeof(param->attrs[i].alg); - param->attrs[i].alg.attr.rta_type = CRYPTOA_ALG; - memcpy(param->attrs[i].alg.data.name, name, len); - } else { - param->attrs[i].nu32.attr.rta_len = - sizeof(param->attrs[i].nu32); - param->attrs[i].nu32.attr.rta_type = CRYPTOA_U32; - param->attrs[i].nu32.data.num = - simple_strtol(name, NULL, 0); - } - - param->tb[i + 1] = ¶m->attrs[i].attr; - i++; - - if (i >= CRYPTO_MAX_ATTRS) - goto err_free_param; - - if (*p == ')') - break; - - if (*p != ',') - goto err_free_param; - } - - if (!i) - goto err_free_param; - - param->tb[i + 1] = NULL; - - param->type.attr.rta_len = sizeof(param->type); - param->type.attr.rta_type = CRYPTOA_TYPE; - param->type.data.type = larval->alg.cra_flags; - param->type.data.mask = larval->mask; - param->tb[0] = ¶m->type.attr; - - memcpy(param->larval, larval->alg.cra_name, CRYPTO_MAX_ALG_NAME); - - thread = kthread_run(cryptomgr_probe, param, "cryptomgr"); - if (IS_ERR(thread)) - goto err_free_param; - - return NOTIFY_STOP; - -err_free_param: - kfree(param); -err_put_module: - module_put(THIS_MODULE); -err: - return NOTIFY_OK; -} - -static int cryptomgr_notify(struct notifier_block *this, unsigned long msg, - void *data) -{ - switch (msg) { - case CRYPTO_MSG_ALG_REQUEST: - return cryptomgr_schedule_probe(data); - } - - return NOTIFY_DONE; -} - -static struct notifier_block cryptomgr_notifier = { - .notifier_call = cryptomgr_notify, -}; - -static int __init cryptomgr_init(void) -{ - return crypto_register_notifier(&cryptomgr_notifier); -} - -static void __exit cryptomgr_exit(void) -{ - int err = crypto_unregister_notifier(&cryptomgr_notifier); - BUG_ON(err); -} - -module_init(cryptomgr_init); -module_exit(cryptomgr_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Crypto Algorithm Manager"); diff --git a/crypto/internal.h b/crypto/internal.h index 6d8d21310585..48cb70416d59 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -108,6 +108,9 @@ int crypto_register_instance(struct crypto_template *tmpl, int crypto_register_notifier(struct notifier_block *nb); int crypto_unregister_notifier(struct notifier_block *nb); +int __init testmgr_init(void); +void testmgr_exit(void); + static inline void crypto_alg_put(struct crypto_alg *alg) { if (atomic_dec_and_test(&alg->cra_refcnt) && alg->cra_destroy) diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index ed9d4ee42f7b..28a45a1e6f42 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -19,11 +19,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -34,80 +32,19 @@ * Need slab memory for testing (size in number of pages). */ #define TVMEMSIZE 4 -#define XBUFSIZE 8 /* - * Indexes into the xbuf to simulate cross-page access. - */ -#define IDX1 32 -#define IDX2 32400 -#define IDX3 1 -#define IDX4 8193 -#define IDX5 22222 -#define IDX6 17101 -#define IDX7 27333 -#define IDX8 3000 - -/* -* Used by test_cipher() +* Used by test_cipher_speed() */ #define ENCRYPT 1 #define DECRYPT 0 -struct tcrypt_result { - struct completion completion; - int err; -}; - -struct aead_test_suite { - struct { - struct aead_testvec *vecs; - unsigned int count; - } enc, dec; -}; - -struct cipher_test_suite { - struct { - struct cipher_testvec *vecs; - unsigned int count; - } enc, dec; -}; - -struct comp_test_suite { - struct { - struct comp_testvec *vecs; - unsigned int count; - } comp, decomp; -}; - -struct hash_test_suite { - struct hash_testvec *vecs; - unsigned int count; -}; - -struct alg_test_desc { - const char *alg; - int (*test)(const struct alg_test_desc *desc, const char *driver, - u32 type, u32 mask); - - union { - struct aead_test_suite aead; - struct cipher_test_suite cipher; - struct comp_test_suite comp; - struct hash_test_suite hash; - } suite; -}; - -static unsigned int IDX[8] = { IDX1, IDX2, IDX3, IDX4, IDX5, IDX6, IDX7, IDX8 }; - /* * Used by test_cipher_speed() */ static unsigned int sec; static int mode; -static char *xbuf[XBUFSIZE]; -static char *axbuf[XBUFSIZE]; static char *tvmem[TVMEMSIZE]; static char *check[] = { @@ -119,666 +56,6 @@ static char *check[] = { "lzo", "cts", NULL }; -static void hexdump(unsigned char *buf, unsigned int len) -{ - print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, - 16, 1, - buf, len, false); -} - -static void tcrypt_complete(struct crypto_async_request *req, int err) -{ - struct tcrypt_result *res = req->data; - - if (err == -EINPROGRESS) - return; - - res->err = err; - complete(&res->completion); -} - -static int test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, - unsigned int tcount) -{ - const char *algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm)); - unsigned int i, j, k, temp; - struct scatterlist sg[8]; - char result[64]; - struct ahash_request *req; - struct tcrypt_result tresult; - int ret; - void *hash_buff; - - init_completion(&tresult.completion); - - req = ahash_request_alloc(tfm, GFP_KERNEL); - if (!req) { - printk(KERN_ERR "alg: hash: Failed to allocate request for " - "%s\n", algo); - ret = -ENOMEM; - goto out_noreq; - } - ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - tcrypt_complete, &tresult); - - for (i = 0; i < tcount; i++) { - memset(result, 0, 64); - - hash_buff = xbuf[0]; - - memcpy(hash_buff, template[i].plaintext, template[i].psize); - sg_init_one(&sg[0], hash_buff, template[i].psize); - - if (template[i].ksize) { - crypto_ahash_clear_flags(tfm, ~0); - ret = crypto_ahash_setkey(tfm, template[i].key, - template[i].ksize); - if (ret) { - printk(KERN_ERR "alg: hash: setkey failed on " - "test %d for %s: ret=%d\n", i + 1, algo, - -ret); - goto out; - } - } - - ahash_request_set_crypt(req, sg, result, template[i].psize); - ret = crypto_ahash_digest(req); - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - ret = wait_for_completion_interruptible( - &tresult.completion); - if (!ret && !(ret = tresult.err)) { - INIT_COMPLETION(tresult.completion); - break; - } - /* fall through */ - default: - printk(KERN_ERR "alg: hash: digest failed on test %d " - "for %s: ret=%d\n", i + 1, algo, -ret); - goto out; - } - - if (memcmp(result, template[i].digest, - crypto_ahash_digestsize(tfm))) { - printk(KERN_ERR "alg: hash: Test %d failed for %s\n", - i + 1, algo); - hexdump(result, crypto_ahash_digestsize(tfm)); - ret = -EINVAL; - goto out; - } - } - - j = 0; - for (i = 0; i < tcount; i++) { - if (template[i].np) { - j++; - memset(result, 0, 64); - - temp = 0; - sg_init_table(sg, template[i].np); - for (k = 0; k < template[i].np; k++) { - sg_set_buf(&sg[k], - memcpy(xbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]), - template[i].plaintext + temp, - template[i].tap[k]), - template[i].tap[k]); - temp += template[i].tap[k]; - } - - if (template[i].ksize) { - crypto_ahash_clear_flags(tfm, ~0); - ret = crypto_ahash_setkey(tfm, template[i].key, - template[i].ksize); - - if (ret) { - printk(KERN_ERR "alg: hash: setkey " - "failed on chunking test %d " - "for %s: ret=%d\n", j, algo, - -ret); - goto out; - } - } - - ahash_request_set_crypt(req, sg, result, - template[i].psize); - ret = crypto_ahash_digest(req); - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - ret = wait_for_completion_interruptible( - &tresult.completion); - if (!ret && !(ret = tresult.err)) { - INIT_COMPLETION(tresult.completion); - break; - } - /* fall through */ - default: - printk(KERN_ERR "alg: hash: digest failed " - "on chunking test %d for %s: " - "ret=%d\n", j, algo, -ret); - goto out; - } - - if (memcmp(result, template[i].digest, - crypto_ahash_digestsize(tfm))) { - printk(KERN_ERR "alg: hash: Chunking test %d " - "failed for %s\n", j, algo); - hexdump(result, crypto_ahash_digestsize(tfm)); - ret = -EINVAL; - goto out; - } - } - } - - ret = 0; - -out: - ahash_request_free(req); -out_noreq: - return ret; -} - -static int test_aead(struct crypto_aead *tfm, int enc, - struct aead_testvec *template, unsigned int tcount) -{ - const char *algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)); - unsigned int i, j, k, n, temp; - int ret = 0; - char *q; - char *key; - struct aead_request *req; - struct scatterlist sg[8]; - struct scatterlist asg[8]; - const char *e; - struct tcrypt_result result; - unsigned int authsize; - void *input; - void *assoc; - char iv[MAX_IVLEN]; - - if (enc == ENCRYPT) - e = "encryption"; - else - e = "decryption"; - - init_completion(&result.completion); - - req = aead_request_alloc(tfm, GFP_KERNEL); - if (!req) { - printk(KERN_ERR "alg: aead: Failed to allocate request for " - "%s\n", algo); - ret = -ENOMEM; - goto out; - } - - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - tcrypt_complete, &result); - - for (i = 0, j = 0; i < tcount; i++) { - if (!template[i].np) { - j++; - - /* some tepmplates have no input data but they will - * touch input - */ - input = xbuf[0]; - assoc = axbuf[0]; - - memcpy(input, template[i].input, template[i].ilen); - memcpy(assoc, template[i].assoc, template[i].alen); - if (template[i].iv) - memcpy(iv, template[i].iv, MAX_IVLEN); - else - memset(iv, 0, MAX_IVLEN); - - crypto_aead_clear_flags(tfm, ~0); - if (template[i].wk) - crypto_aead_set_flags( - tfm, CRYPTO_TFM_REQ_WEAK_KEY); - - key = template[i].key; - - ret = crypto_aead_setkey(tfm, key, - template[i].klen); - if (!ret == template[i].fail) { - printk(KERN_ERR "alg: aead: setkey failed on " - "test %d for %s: flags=%x\n", j, algo, - crypto_aead_get_flags(tfm)); - goto out; - } else if (ret) - continue; - - authsize = abs(template[i].rlen - template[i].ilen); - ret = crypto_aead_setauthsize(tfm, authsize); - if (ret) { - printk(KERN_ERR "alg: aead: Failed to set " - "authsize to %u on test %d for %s\n", - authsize, j, algo); - goto out; - } - - sg_init_one(&sg[0], input, - template[i].ilen + (enc ? authsize : 0)); - - sg_init_one(&asg[0], assoc, template[i].alen); - - aead_request_set_crypt(req, sg, sg, - template[i].ilen, iv); - - aead_request_set_assoc(req, asg, template[i].alen); - - ret = enc ? - crypto_aead_encrypt(req) : - crypto_aead_decrypt(req); - - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - ret = wait_for_completion_interruptible( - &result.completion); - if (!ret && !(ret = result.err)) { - INIT_COMPLETION(result.completion); - break; - } - /* fall through */ - default: - printk(KERN_ERR "alg: aead: %s failed on test " - "%d for %s: ret=%d\n", e, j, algo, -ret); - goto out; - } - - q = input; - if (memcmp(q, template[i].result, template[i].rlen)) { - printk(KERN_ERR "alg: aead: Test %d failed on " - "%s for %s\n", j, e, algo); - hexdump(q, template[i].rlen); - ret = -EINVAL; - goto out; - } - } - } - - for (i = 0, j = 0; i < tcount; i++) { - if (template[i].np) { - j++; - - if (template[i].iv) - memcpy(iv, template[i].iv, MAX_IVLEN); - else - memset(iv, 0, MAX_IVLEN); - - crypto_aead_clear_flags(tfm, ~0); - if (template[i].wk) - crypto_aead_set_flags( - tfm, CRYPTO_TFM_REQ_WEAK_KEY); - key = template[i].key; - - ret = crypto_aead_setkey(tfm, key, template[i].klen); - if (!ret == template[i].fail) { - printk(KERN_ERR "alg: aead: setkey failed on " - "chunk test %d for %s: flags=%x\n", j, - algo, crypto_aead_get_flags(tfm)); - goto out; - } else if (ret) - continue; - - authsize = abs(template[i].rlen - template[i].ilen); - - ret = -EINVAL; - sg_init_table(sg, template[i].np); - for (k = 0, temp = 0; k < template[i].np; k++) { - if (WARN_ON(offset_in_page(IDX[k]) + - template[i].tap[k] > PAGE_SIZE)) - goto out; - - q = xbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); - - memcpy(q, template[i].input + temp, - template[i].tap[k]); - - n = template[i].tap[k]; - if (k == template[i].np - 1 && enc) - n += authsize; - if (offset_in_page(q) + n < PAGE_SIZE) - q[n] = 0; - - sg_set_buf(&sg[k], q, template[i].tap[k]); - temp += template[i].tap[k]; - } - - ret = crypto_aead_setauthsize(tfm, authsize); - if (ret) { - printk(KERN_ERR "alg: aead: Failed to set " - "authsize to %u on chunk test %d for " - "%s\n", authsize, j, algo); - goto out; - } - - if (enc) { - if (WARN_ON(sg[k - 1].offset + - sg[k - 1].length + authsize > - PAGE_SIZE)) { - ret = -EINVAL; - goto out; - } - - sg[k - 1].length += authsize; - } - - sg_init_table(asg, template[i].anp); - for (k = 0, temp = 0; k < template[i].anp; k++) { - sg_set_buf(&asg[k], - memcpy(axbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]), - template[i].assoc + temp, - template[i].atap[k]), - template[i].atap[k]); - temp += template[i].atap[k]; - } - - aead_request_set_crypt(req, sg, sg, - template[i].ilen, - iv); - - aead_request_set_assoc(req, asg, template[i].alen); - - ret = enc ? - crypto_aead_encrypt(req) : - crypto_aead_decrypt(req); - - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - ret = wait_for_completion_interruptible( - &result.completion); - if (!ret && !(ret = result.err)) { - INIT_COMPLETION(result.completion); - break; - } - /* fall through */ - default: - printk(KERN_ERR "alg: aead: %s failed on " - "chunk test %d for %s: ret=%d\n", e, j, - algo, -ret); - goto out; - } - - ret = -EINVAL; - for (k = 0, temp = 0; k < template[i].np; k++) { - q = xbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); - - n = template[i].tap[k]; - if (k == template[i].np - 1) - n += enc ? authsize : -authsize; - - if (memcmp(q, template[i].result + temp, n)) { - printk(KERN_ERR "alg: aead: Chunk " - "test %d failed on %s at page " - "%u for %s\n", j, e, k, algo); - hexdump(q, n); - goto out; - } - - q += n; - if (k == template[i].np - 1 && !enc) { - if (memcmp(q, template[i].input + - temp + n, authsize)) - n = authsize; - else - n = 0; - } else { - for (n = 0; offset_in_page(q + n) && - q[n]; n++) - ; - } - if (n) { - printk(KERN_ERR "alg: aead: Result " - "buffer corruption in chunk " - "test %d on %s at page %u for " - "%s: %u bytes:\n", j, e, k, - algo, n); - hexdump(q, n); - goto out; - } - - temp += template[i].tap[k]; - } - } - } - - ret = 0; - -out: - aead_request_free(req); - return ret; -} - -static int test_cipher(struct crypto_ablkcipher *tfm, int enc, - struct cipher_testvec *template, unsigned int tcount) -{ - const char *algo = - crypto_tfm_alg_driver_name(crypto_ablkcipher_tfm(tfm)); - unsigned int i, j, k, n, temp; - int ret; - char *q; - struct ablkcipher_request *req; - struct scatterlist sg[8]; - const char *e; - struct tcrypt_result result; - void *data; - char iv[MAX_IVLEN]; - - if (enc == ENCRYPT) - e = "encryption"; - else - e = "decryption"; - - init_completion(&result.completion); - - req = ablkcipher_request_alloc(tfm, GFP_KERNEL); - if (!req) { - printk(KERN_ERR "alg: cipher: Failed to allocate request for " - "%s\n", algo); - ret = -ENOMEM; - goto out; - } - - ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - tcrypt_complete, &result); - - j = 0; - for (i = 0; i < tcount; i++) { - if (template[i].iv) - memcpy(iv, template[i].iv, MAX_IVLEN); - else - memset(iv, 0, MAX_IVLEN); - - if (!(template[i].np)) { - j++; - - data = xbuf[0]; - memcpy(data, template[i].input, template[i].ilen); - - crypto_ablkcipher_clear_flags(tfm, ~0); - if (template[i].wk) - crypto_ablkcipher_set_flags( - tfm, CRYPTO_TFM_REQ_WEAK_KEY); - - ret = crypto_ablkcipher_setkey(tfm, template[i].key, - template[i].klen); - if (!ret == template[i].fail) { - printk(KERN_ERR "alg: cipher: setkey failed " - "on test %d for %s: flags=%x\n", j, - algo, crypto_ablkcipher_get_flags(tfm)); - goto out; - } else if (ret) - continue; - - sg_init_one(&sg[0], data, template[i].ilen); - - ablkcipher_request_set_crypt(req, sg, sg, - template[i].ilen, iv); - ret = enc ? - crypto_ablkcipher_encrypt(req) : - crypto_ablkcipher_decrypt(req); - - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - ret = wait_for_completion_interruptible( - &result.completion); - if (!ret && !((ret = result.err))) { - INIT_COMPLETION(result.completion); - break; - } - /* fall through */ - default: - printk(KERN_ERR "alg: cipher: %s failed on " - "test %d for %s: ret=%d\n", e, j, algo, - -ret); - goto out; - } - - q = data; - if (memcmp(q, template[i].result, template[i].rlen)) { - printk(KERN_ERR "alg: cipher: Test %d failed " - "on %s for %s\n", j, e, algo); - hexdump(q, template[i].rlen); - ret = -EINVAL; - goto out; - } - } - } - - j = 0; - for (i = 0; i < tcount; i++) { - - if (template[i].iv) - memcpy(iv, template[i].iv, MAX_IVLEN); - else - memset(iv, 0, MAX_IVLEN); - - if (template[i].np) { - j++; - - crypto_ablkcipher_clear_flags(tfm, ~0); - if (template[i].wk) - crypto_ablkcipher_set_flags( - tfm, CRYPTO_TFM_REQ_WEAK_KEY); - - ret = crypto_ablkcipher_setkey(tfm, template[i].key, - template[i].klen); - if (!ret == template[i].fail) { - printk(KERN_ERR "alg: cipher: setkey failed " - "on chunk test %d for %s: flags=%x\n", - j, algo, - crypto_ablkcipher_get_flags(tfm)); - goto out; - } else if (ret) - continue; - - temp = 0; - ret = -EINVAL; - sg_init_table(sg, template[i].np); - for (k = 0; k < template[i].np; k++) { - if (WARN_ON(offset_in_page(IDX[k]) + - template[i].tap[k] > PAGE_SIZE)) - goto out; - - q = xbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); - - memcpy(q, template[i].input + temp, - template[i].tap[k]); - - if (offset_in_page(q) + template[i].tap[k] < - PAGE_SIZE) - q[template[i].tap[k]] = 0; - - sg_set_buf(&sg[k], q, template[i].tap[k]); - - temp += template[i].tap[k]; - } - - ablkcipher_request_set_crypt(req, sg, sg, - template[i].ilen, iv); - - ret = enc ? - crypto_ablkcipher_encrypt(req) : - crypto_ablkcipher_decrypt(req); - - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - ret = wait_for_completion_interruptible( - &result.completion); - if (!ret && !((ret = result.err))) { - INIT_COMPLETION(result.completion); - break; - } - /* fall through */ - default: - printk(KERN_ERR "alg: cipher: %s failed on " - "chunk test %d for %s: ret=%d\n", e, j, - algo, -ret); - goto out; - } - - temp = 0; - ret = -EINVAL; - for (k = 0; k < template[i].np; k++) { - q = xbuf[IDX[k] >> PAGE_SHIFT] + - offset_in_page(IDX[k]); - - if (memcmp(q, template[i].result + temp, - template[i].tap[k])) { - printk(KERN_ERR "alg: cipher: Chunk " - "test %d failed on %s at page " - "%u for %s\n", j, e, k, algo); - hexdump(q, template[i].tap[k]); - goto out; - } - - q += template[i].tap[k]; - for (n = 0; offset_in_page(q + n) && q[n]; n++) - ; - if (n) { - printk(KERN_ERR "alg: cipher: " - "Result buffer corruption in " - "chunk test %d on %s at page " - "%u for %s: %u bytes:\n", j, e, - k, algo, n); - hexdump(q, n); - goto out; - } - temp += template[i].tap[k]; - } - } - } - - ret = 0; - -out: - ablkcipher_request_free(req); - return ret; -} - static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc, struct scatterlist *sg, int blen, int sec) { @@ -854,11 +131,11 @@ out: static u32 block_sizes[] = { 16, 64, 256, 1024, 8192, 0 }; static void test_cipher_speed(const char *algo, int enc, unsigned int sec, - struct cipher_testvec *template, + struct cipher_speed_template *template, unsigned int tcount, u8 *keysize) { unsigned int ret, i, j, iv_len; - unsigned char *key, iv[128]; + const char *key, iv[128]; struct crypto_blkcipher *tfm; struct blkcipher_desc desc; const char *e; @@ -901,7 +178,7 @@ static void test_cipher_speed(const char *algo, int enc, unsigned int sec, memset(tvmem[0], 0xff, PAGE_SIZE); /* set key, plain text and IV */ - key = (unsigned char *)tvmem[0]; + key = tvmem[0]; for (j = 0; j < tcount; j++) { if (template[j].klen == *keysize) { key = template[j].key; @@ -1175,68 +452,6 @@ out: crypto_free_hash(tfm); } -static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate, - struct comp_testvec *dtemplate, int ctcount, int dtcount) -{ - const char *algo = crypto_tfm_alg_driver_name(crypto_comp_tfm(tfm)); - unsigned int i; - char result[COMP_BUF_SIZE]; - int ret; - - for (i = 0; i < ctcount; i++) { - int ilen, dlen = COMP_BUF_SIZE; - - memset(result, 0, sizeof (result)); - - ilen = ctemplate[i].inlen; - ret = crypto_comp_compress(tfm, ctemplate[i].input, - ilen, result, &dlen); - if (ret) { - printk(KERN_ERR "alg: comp: compression failed " - "on test %d for %s: ret=%d\n", i + 1, algo, - -ret); - goto out; - } - - if (memcmp(result, ctemplate[i].output, dlen)) { - printk(KERN_ERR "alg: comp: Compression test %d " - "failed for %s\n", i + 1, algo); - hexdump(result, dlen); - ret = -EINVAL; - goto out; - } - } - - for (i = 0; i < dtcount; i++) { - int ilen, ret, dlen = COMP_BUF_SIZE; - - memset(result, 0, sizeof (result)); - - ilen = dtemplate[i].inlen; - ret = crypto_comp_decompress(tfm, dtemplate[i].input, - ilen, result, &dlen); - if (ret) { - printk(KERN_ERR "alg: comp: decompression failed " - "on test %d for %s: ret=%d\n", i + 1, algo, - -ret); - goto out; - } - - if (memcmp(result, dtemplate[i].output, dlen)) { - printk(KERN_ERR "alg: comp: Decompression test %d " - "failed for %s\n", i + 1, algo); - hexdump(result, dlen); - ret = -EINVAL; - goto out; - } - } - - ret = 0; - -out: - return ret; -} - static void test_available(void) { char **name = check; @@ -1249,895 +464,6 @@ static void test_available(void) } } -static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, - u32 type, u32 mask) -{ - struct crypto_aead *tfm; - int err = 0; - - tfm = crypto_alloc_aead(driver, type, mask); - if (IS_ERR(tfm)) { - printk(KERN_ERR "alg: aead: Failed to load transform for %s: " - "%ld\n", driver, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - if (desc->suite.aead.enc.vecs) { - err = test_aead(tfm, ENCRYPT, desc->suite.aead.enc.vecs, - desc->suite.aead.enc.count); - if (err) - goto out; - } - - if (!err && desc->suite.aead.dec.vecs) - err = test_aead(tfm, DECRYPT, desc->suite.aead.dec.vecs, - desc->suite.aead.dec.count); - -out: - crypto_free_aead(tfm); - return err; -} - -static int alg_test_cipher(const struct alg_test_desc *desc, - const char *driver, u32 type, u32 mask) -{ - struct crypto_ablkcipher *tfm; - int err = 0; - - tfm = crypto_alloc_ablkcipher(driver, type, mask); - if (IS_ERR(tfm)) { - printk(KERN_ERR "alg: cipher: Failed to load transform for " - "%s: %ld\n", driver, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - if (desc->suite.cipher.enc.vecs) { - err = test_cipher(tfm, ENCRYPT, desc->suite.cipher.enc.vecs, - desc->suite.cipher.enc.count); - if (err) - goto out; - } - - if (desc->suite.cipher.dec.vecs) - err = test_cipher(tfm, DECRYPT, desc->suite.cipher.dec.vecs, - desc->suite.cipher.dec.count); - -out: - crypto_free_ablkcipher(tfm); - return err; -} - -static int alg_test_comp(const struct alg_test_desc *desc, const char *driver, - u32 type, u32 mask) -{ - struct crypto_comp *tfm; - int err; - - tfm = crypto_alloc_comp(driver, type, mask); - if (IS_ERR(tfm)) { - printk(KERN_ERR "alg: comp: Failed to load transform for %s: " - "%ld\n", driver, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - err = test_comp(tfm, desc->suite.comp.comp.vecs, - desc->suite.comp.decomp.vecs, - desc->suite.comp.comp.count, - desc->suite.comp.decomp.count); - - crypto_free_comp(tfm); - return err; -} - -static int alg_test_hash(const struct alg_test_desc *desc, const char *driver, - u32 type, u32 mask) -{ - struct crypto_ahash *tfm; - int err; - - tfm = crypto_alloc_ahash(driver, type, mask); - if (IS_ERR(tfm)) { - printk(KERN_ERR "alg: hash: Failed to load transform for %s: " - "%ld\n", driver, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - err = test_hash(tfm, desc->suite.hash.vecs, desc->suite.hash.count); - - crypto_free_ahash(tfm); - return err; -} - -/* Please keep this list sorted by algorithm name. */ -static const struct alg_test_desc alg_test_descs[] = { - { - .alg = "cbc(aes)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = aes_cbc_enc_tv_template, - .count = AES_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_cbc_dec_tv_template, - .count = AES_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "cbc(anubis)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = anubis_cbc_enc_tv_template, - .count = ANUBIS_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = anubis_cbc_dec_tv_template, - .count = ANUBIS_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "cbc(blowfish)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = bf_cbc_enc_tv_template, - .count = BF_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = bf_cbc_dec_tv_template, - .count = BF_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "cbc(camellia)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = camellia_cbc_enc_tv_template, - .count = CAMELLIA_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = camellia_cbc_dec_tv_template, - .count = CAMELLIA_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "cbc(des)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = des_cbc_enc_tv_template, - .count = DES_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = des_cbc_dec_tv_template, - .count = DES_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "cbc(des3_ede)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = des3_ede_cbc_enc_tv_template, - .count = DES3_EDE_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = des3_ede_cbc_dec_tv_template, - .count = DES3_EDE_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "cbc(twofish)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = tf_cbc_enc_tv_template, - .count = TF_CBC_ENC_TEST_VECTORS - }, - .dec = { - .vecs = tf_cbc_dec_tv_template, - .count = TF_CBC_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ccm(aes)", - .test = alg_test_aead, - .suite = { - .aead = { - .enc = { - .vecs = aes_ccm_enc_tv_template, - .count = AES_CCM_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_ccm_dec_tv_template, - .count = AES_CCM_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "crc32c", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = crc32c_tv_template, - .count = CRC32C_TEST_VECTORS - } - } - }, { - .alg = "cts(cbc(aes))", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = cts_mode_enc_tv_template, - .count = CTS_MODE_ENC_TEST_VECTORS - }, - .dec = { - .vecs = cts_mode_dec_tv_template, - .count = CTS_MODE_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "deflate", - .test = alg_test_comp, - .suite = { - .comp = { - .comp = { - .vecs = deflate_comp_tv_template, - .count = DEFLATE_COMP_TEST_VECTORS - }, - .decomp = { - .vecs = deflate_decomp_tv_template, - .count = DEFLATE_DECOMP_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(aes)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = aes_enc_tv_template, - .count = AES_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_dec_tv_template, - .count = AES_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(anubis)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = anubis_enc_tv_template, - .count = ANUBIS_ENC_TEST_VECTORS - }, - .dec = { - .vecs = anubis_dec_tv_template, - .count = ANUBIS_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(arc4)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = arc4_enc_tv_template, - .count = ARC4_ENC_TEST_VECTORS - }, - .dec = { - .vecs = arc4_dec_tv_template, - .count = ARC4_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(blowfish)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = bf_enc_tv_template, - .count = BF_ENC_TEST_VECTORS - }, - .dec = { - .vecs = bf_dec_tv_template, - .count = BF_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(camellia)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = camellia_enc_tv_template, - .count = CAMELLIA_ENC_TEST_VECTORS - }, - .dec = { - .vecs = camellia_dec_tv_template, - .count = CAMELLIA_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(cast5)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = cast5_enc_tv_template, - .count = CAST5_ENC_TEST_VECTORS - }, - .dec = { - .vecs = cast5_dec_tv_template, - .count = CAST5_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(cast6)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = cast6_enc_tv_template, - .count = CAST6_ENC_TEST_VECTORS - }, - .dec = { - .vecs = cast6_dec_tv_template, - .count = CAST6_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(des)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = des_enc_tv_template, - .count = DES_ENC_TEST_VECTORS - }, - .dec = { - .vecs = des_dec_tv_template, - .count = DES_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(des3_ede)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = des3_ede_enc_tv_template, - .count = DES3_EDE_ENC_TEST_VECTORS - }, - .dec = { - .vecs = des3_ede_dec_tv_template, - .count = DES3_EDE_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(khazad)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = khazad_enc_tv_template, - .count = KHAZAD_ENC_TEST_VECTORS - }, - .dec = { - .vecs = khazad_dec_tv_template, - .count = KHAZAD_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(seed)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = seed_enc_tv_template, - .count = SEED_ENC_TEST_VECTORS - }, - .dec = { - .vecs = seed_dec_tv_template, - .count = SEED_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(serpent)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = serpent_enc_tv_template, - .count = SERPENT_ENC_TEST_VECTORS - }, - .dec = { - .vecs = serpent_dec_tv_template, - .count = SERPENT_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(tea)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = tea_enc_tv_template, - .count = TEA_ENC_TEST_VECTORS - }, - .dec = { - .vecs = tea_dec_tv_template, - .count = TEA_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(tnepres)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = tnepres_enc_tv_template, - .count = TNEPRES_ENC_TEST_VECTORS - }, - .dec = { - .vecs = tnepres_dec_tv_template, - .count = TNEPRES_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(twofish)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = tf_enc_tv_template, - .count = TF_ENC_TEST_VECTORS - }, - .dec = { - .vecs = tf_dec_tv_template, - .count = TF_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(xeta)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = xeta_enc_tv_template, - .count = XETA_ENC_TEST_VECTORS - }, - .dec = { - .vecs = xeta_dec_tv_template, - .count = XETA_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "ecb(xtea)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = xtea_enc_tv_template, - .count = XTEA_ENC_TEST_VECTORS - }, - .dec = { - .vecs = xtea_dec_tv_template, - .count = XTEA_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "gcm(aes)", - .test = alg_test_aead, - .suite = { - .aead = { - .enc = { - .vecs = aes_gcm_enc_tv_template, - .count = AES_GCM_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_gcm_dec_tv_template, - .count = AES_GCM_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "hmac(md5)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_md5_tv_template, - .count = HMAC_MD5_TEST_VECTORS - } - } - }, { - .alg = "hmac(rmd128)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_rmd128_tv_template, - .count = HMAC_RMD128_TEST_VECTORS - } - } - }, { - .alg = "hmac(rmd160)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_rmd160_tv_template, - .count = HMAC_RMD160_TEST_VECTORS - } - } - }, { - .alg = "hmac(sha1)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_sha1_tv_template, - .count = HMAC_SHA1_TEST_VECTORS - } - } - }, { - .alg = "hmac(sha224)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_sha224_tv_template, - .count = HMAC_SHA224_TEST_VECTORS - } - } - }, { - .alg = "hmac(sha256)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_sha256_tv_template, - .count = HMAC_SHA256_TEST_VECTORS - } - } - }, { - .alg = "hmac(sha384)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_sha384_tv_template, - .count = HMAC_SHA384_TEST_VECTORS - } - } - }, { - .alg = "hmac(sha512)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = hmac_sha512_tv_template, - .count = HMAC_SHA512_TEST_VECTORS - } - } - }, { - .alg = "lrw(aes)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = aes_lrw_enc_tv_template, - .count = AES_LRW_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_lrw_dec_tv_template, - .count = AES_LRW_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "lzo", - .test = alg_test_comp, - .suite = { - .comp = { - .comp = { - .vecs = lzo_comp_tv_template, - .count = LZO_COMP_TEST_VECTORS - }, - .decomp = { - .vecs = lzo_decomp_tv_template, - .count = LZO_DECOMP_TEST_VECTORS - } - } - } - }, { - .alg = "md4", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = md4_tv_template, - .count = MD4_TEST_VECTORS - } - } - }, { - .alg = "md5", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = md5_tv_template, - .count = MD5_TEST_VECTORS - } - } - }, { - .alg = "michael_mic", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = michael_mic_tv_template, - .count = MICHAEL_MIC_TEST_VECTORS - } - } - }, { - .alg = "pcbc(fcrypt)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = fcrypt_pcbc_enc_tv_template, - .count = FCRYPT_ENC_TEST_VECTORS - }, - .dec = { - .vecs = fcrypt_pcbc_dec_tv_template, - .count = FCRYPT_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "rfc3686(ctr(aes))", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = aes_ctr_enc_tv_template, - .count = AES_CTR_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_ctr_dec_tv_template, - .count = AES_CTR_DEC_TEST_VECTORS - } - } - } - }, { - .alg = "rmd128", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = rmd128_tv_template, - .count = RMD128_TEST_VECTORS - } - } - }, { - .alg = "rmd160", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = rmd160_tv_template, - .count = RMD160_TEST_VECTORS - } - } - }, { - .alg = "rmd256", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = rmd256_tv_template, - .count = RMD256_TEST_VECTORS - } - } - }, { - .alg = "rmd320", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = rmd320_tv_template, - .count = RMD320_TEST_VECTORS - } - } - }, { - .alg = "salsa20", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = salsa20_stream_enc_tv_template, - .count = SALSA20_STREAM_ENC_TEST_VECTORS - } - } - } - }, { - .alg = "sha1", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = sha1_tv_template, - .count = SHA1_TEST_VECTORS - } - } - }, { - .alg = "sha224", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = sha224_tv_template, - .count = SHA224_TEST_VECTORS - } - } - }, { - .alg = "sha256", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = sha256_tv_template, - .count = SHA256_TEST_VECTORS - } - } - }, { - .alg = "sha384", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = sha384_tv_template, - .count = SHA384_TEST_VECTORS - } - } - }, { - .alg = "sha512", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = sha512_tv_template, - .count = SHA512_TEST_VECTORS - } - } - }, { - .alg = "tgr128", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = tgr128_tv_template, - .count = TGR128_TEST_VECTORS - } - } - }, { - .alg = "tgr160", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = tgr160_tv_template, - .count = TGR160_TEST_VECTORS - } - } - }, { - .alg = "tgr192", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = tgr192_tv_template, - .count = TGR192_TEST_VECTORS - } - } - }, { - .alg = "wp256", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = wp256_tv_template, - .count = WP256_TEST_VECTORS - } - } - }, { - .alg = "wp384", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = wp384_tv_template, - .count = WP384_TEST_VECTORS - } - } - }, { - .alg = "wp512", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = wp512_tv_template, - .count = WP512_TEST_VECTORS - } - } - }, { - .alg = "xcbc(aes)", - .test = alg_test_hash, - .suite = { - .hash = { - .vecs = aes_xcbc128_tv_template, - .count = XCBC_AES_TEST_VECTORS - } - } - }, { - .alg = "xts(aes)", - .test = alg_test_cipher, - .suite = { - .cipher = { - .enc = { - .vecs = aes_xts_enc_tv_template, - .count = AES_XTS_ENC_TEST_VECTORS - }, - .dec = { - .vecs = aes_xts_dec_tv_template, - .count = AES_XTS_DEC_TEST_VECTORS - } - } - } - } -}; - -static int alg_test(const char *driver, const char *alg, u32 type, u32 mask) -{ - int start = 0; - int end = ARRAY_SIZE(alg_test_descs); - - while (start < end) { - int i = (start + end) / 2; - int diff = strcmp(alg_test_descs[i].alg, alg); - - if (diff > 0) { - end = i; - continue; - } - - if (diff < 0) { - start = i + 1; - continue; - } - - return alg_test_descs[i].test(alg_test_descs + i, driver, - type, mask); - } - - printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver); - return 0; -} - static inline int tcrypt_test(const char *alg) { return alg_test(alg, alg, 0, 0); @@ -2392,16 +718,16 @@ static void do_test(int m) case 201: test_cipher_speed("ecb(des3_ede)", ENCRYPT, sec, - des3_ede_enc_tv_template, DES3_EDE_ENC_TEST_VECTORS, + des3_speed_template, DES3_SPEED_VECTORS, speed_template_24); test_cipher_speed("ecb(des3_ede)", DECRYPT, sec, - des3_ede_enc_tv_template, DES3_EDE_ENC_TEST_VECTORS, + des3_speed_template, DES3_SPEED_VECTORS, speed_template_24); test_cipher_speed("cbc(des3_ede)", ENCRYPT, sec, - des3_ede_enc_tv_template, DES3_EDE_ENC_TEST_VECTORS, + des3_speed_template, DES3_SPEED_VECTORS, speed_template_24); test_cipher_speed("cbc(des3_ede)", DECRYPT, sec, - des3_ede_enc_tv_template, DES3_EDE_ENC_TEST_VECTORS, + des3_speed_template, DES3_SPEED_VECTORS, speed_template_24); break; @@ -2545,18 +871,6 @@ static int __init tcrypt_mod_init(void) goto err_free_tv; } - for (i = 0; i < XBUFSIZE; i++) { - xbuf[i] = (void *)__get_free_page(GFP_KERNEL); - if (!xbuf[i]) - goto err_free_xbuf; - } - - for (i = 0; i < XBUFSIZE; i++) { - axbuf[i] = (void *)__get_free_page(GFP_KERNEL); - if (!axbuf[i]) - goto err_free_axbuf; - } - do_test(mode); /* We intentionaly return -EAGAIN to prevent keeping @@ -2567,12 +881,6 @@ static int __init tcrypt_mod_init(void) */ err = -EAGAIN; -err_free_axbuf: - for (i = 0; i < XBUFSIZE && axbuf[i]; i++) - free_page((unsigned long)axbuf[i]); -err_free_xbuf: - for (i = 0; i < XBUFSIZE && xbuf[i]; i++) - free_page((unsigned long)xbuf[i]); err_free_tv: for (i = 0; i < TVMEMSIZE && tvmem[i]; i++) free_page((unsigned long)tvmem[i]); diff --git a/crypto/tcrypt.h b/crypto/tcrypt.h index c6254a1776fe..966bbfaf95b1 100644 --- a/crypto/tcrypt.h +++ b/crypto/tcrypt.h @@ -17,53 +17,9 @@ #ifndef _CRYPTO_TCRYPT_H #define _CRYPTO_TCRYPT_H -#define MAX_DIGEST_SIZE 64 -#define MAX_TAP 8 - -#define MAX_KEYLEN 56 -#define MAX_IVLEN 32 - -struct hash_testvec { - /* only used with keyed hash algorithms */ - char *key; - char *plaintext; - char *digest; - unsigned char tap[MAX_TAP]; - unsigned char psize; - unsigned char np; - unsigned char ksize; -}; - -struct cipher_testvec { - char *key; - char *iv; - char *input; - char *result; - unsigned short tap[MAX_TAP]; - int np; - unsigned char fail; - unsigned char wk; /* weak key flag */ - unsigned char klen; - unsigned short ilen; - unsigned short rlen; -}; - -struct aead_testvec { - char *key; - char *iv; - char *input; - char *assoc; - char *result; - unsigned char tap[MAX_TAP]; - unsigned char atap[MAX_TAP]; - int np; - int anp; - unsigned char fail; - unsigned char wk; /* weak key flag */ - unsigned char klen; - unsigned short ilen; - unsigned short alen; - unsigned short rlen; +struct cipher_speed_template { + const char *key; + unsigned int klen; }; struct hash_speed { @@ -71,8677 +27,20 @@ struct hash_speed { unsigned int plen; /* per-update length */ }; -static char zeroed_string[48]; - -/* - * MD4 test vectors from RFC1320 - */ -#define MD4_TEST_VECTORS 7 - -static struct hash_testvec md4_tv_template [] = { - { - .plaintext = "", - .digest = "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31" - "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0", - }, { - .plaintext = "a", - .psize = 1, - .digest = "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46" - "\x24\x5e\x05\xfb\xdb\xd6\xfb\x24", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xa4\x48\x01\x7a\xaf\x21\xd8\x52" - "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\xd9\x13\x0a\x81\x64\x54\x9f\xe8" - "\x18\x87\x48\x06\xe1\xc7\x01\x4b", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd" - "\xee\xa8\xed\x63\xdf\x41\x2d\xa9", - .np = 2, - .tap = { 13, 13 }, - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\x04\x3f\x85\x82\xf2\x41\xdb\x35" - "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4", - }, { - .plaintext = "123456789012345678901234567890123456789012345678901234567890123" - "45678901234567890", - .psize = 80, - .digest = "\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19" - "\x9c\x3e\x7b\x16\x4f\xcc\x05\x36", - }, -}; - -/* - * MD5 test vectors from RFC1321 - */ -#define MD5_TEST_VECTORS 7 - -static struct hash_testvec md5_tv_template[] = { - { - .digest = "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" - "\xe9\x80\x09\x98\xec\xf8\x42\x7e", - }, { - .plaintext = "a", - .psize = 1, - .digest = "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8" - "\x31\xc3\x99\xe2\x69\x77\x26\x61", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" - "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d" - "\x52\x5a\x2f\x31\xaa\xf1\x61\xd0", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00" - "\x7d\xfb\x49\x6c\xca\x67\xe1\x3b", - .np = 2, - .tap = {13, 13} - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" - "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", - }, { - .plaintext = "12345678901234567890123456789012345678901234567890123456789012" - "345678901234567890", - .psize = 80, - .digest = "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55" - "\xac\x49\xda\x2e\x21\x07\xb6\x7a", - } - -}; - -/* - * RIPEMD-128 test vectors from ISO/IEC 10118-3:2004(E) - */ -#define RMD128_TEST_VECTORS 10 - -static struct hash_testvec rmd128_tv_template[] = { - { - .digest = "\xcd\xf2\x62\x13\xa1\x50\xdc\x3e" - "\xcb\x61\x0f\x18\xf6\xb3\x8b\x46", - }, { - .plaintext = "a", - .psize = 1, - .digest = "\x86\xbe\x7a\xfa\x33\x9d\x0f\xc7" - "\xcf\xc7\x85\xe7\x2f\x57\x8d\x33", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xc1\x4a\x12\x19\x9c\x66\xe4\xba" - "\x84\x63\x6b\x0f\x69\x14\x4c\x77", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x9e\x32\x7b\x3d\x6e\x52\x30\x62" - "\xaf\xc1\x13\x2d\x7d\xf9\xd1\xb8", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xfd\x2a\xa6\x07\xf7\x1d\xc8\xf5" - "\x10\x71\x49\x22\xb3\x71\x83\x4e", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" - "fghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xd1\xe9\x59\xeb\x17\x9c\x91\x1f" - "\xae\xa4\x62\x4c\x60\xc5\xc7\x02", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x3f\x45\xef\x19\x47\x32\xc2\xdb" - "\xb2\xc4\xa2\xc7\x69\x79\x5f\xa3", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighij" - "hijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\xa1\xaa\x06\x89\xd0\xfa\xfa\x2d" - "\xdc\x22\xe8\x8b\x49\x13\x3a\x06", - .np = 2, - .tap = { 28, 28 }, - }, { - .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghi" - "jklmghijklmnhijklmnoijklmnopjklmnopqklmnopqr" - "lmnopqrsmnopqrstnopqrstu", - .psize = 112, - .digest = "\xd4\xec\xc9\x13\xe1\xdf\x77\x6b" - "\xf4\x8d\xe9\xd5\x5b\x1f\x25\x46", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijk", - .psize = 32, - .digest = "\x13\xfc\x13\xe8\xef\xff\x34\x7d" - "\xe1\x93\xff\x46\xdb\xac\xcf\xd4", - } -}; - -/* - * RIPEMD-160 test vectors from ISO/IEC 10118-3:2004(E) - */ -#define RMD160_TEST_VECTORS 10 - -static struct hash_testvec rmd160_tv_template[] = { - { - .digest = "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28" - "\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31", - }, { - .plaintext = "a", - .psize = 1, - .digest = "\x0b\xdc\x9d\x2d\x25\x6b\x3e\xe9\xda\xae" - "\x34\x7b\xe6\xf4\xdc\x83\x5a\x46\x7f\xfe", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\x8e\xb2\x08\xf7\xe0\x5d\x98\x7a\x9b\x04" - "\x4a\x8e\x98\xc6\xb0\x87\xf1\x5a\x0b\xfc", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x5d\x06\x89\xef\x49\xd2\xfa\xe5\x72\xb8" - "\x81\xb1\x23\xa8\x5f\xfa\x21\x59\x5f\x36", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xf7\x1c\x27\x10\x9c\x69\x2c\x1b\x56\xbb" - "\xdc\xeb\x5b\x9d\x28\x65\xb3\x70\x8d\xbc", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" - "fghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xb0\xe2\x0b\x6e\x31\x16\x64\x02\x86\xed" - "\x3a\x87\xa5\x71\x30\x79\xb2\x1f\x51\x89", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x9b\x75\x2e\x45\x57\x3d\x4b\x39\xf4\xdb" - "\xd3\x32\x3c\xab\x82\xbf\x63\x32\x6b\xfb", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighij" - "hijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x12\xa0\x53\x38\x4a\x9c\x0c\x88\xe4\x05" - "\xa0\x6c\x27\xdc\xf4\x9a\xda\x62\xeb\x2b", - .np = 2, - .tap = { 28, 28 }, - }, { - .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghi" - "jklmghijklmnhijklmnoijklmnopjklmnopqklmnopqr" - "lmnopqrsmnopqrstnopqrstu", - .psize = 112, - .digest = "\x6f\x3f\xa3\x9b\x6b\x50\x3c\x38\x4f\x91" - "\x9a\x49\xa7\xaa\x5c\x2c\x08\xbd\xfb\x45", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijk", - .psize = 32, - .digest = "\x94\xc2\x64\x11\x54\x04\xe6\x33\x79\x0d" - "\xfc\xc8\x7b\x58\x7d\x36\x77\x06\x7d\x9f", - } -}; - -/* - * RIPEMD-256 test vectors - */ -#define RMD256_TEST_VECTORS 8 - -static struct hash_testvec rmd256_tv_template[] = { - { - .digest = "\x02\xba\x4c\x4e\x5f\x8e\xcd\x18" - "\x77\xfc\x52\xd6\x4d\x30\xe3\x7a" - "\x2d\x97\x74\xfb\x1e\x5d\x02\x63" - "\x80\xae\x01\x68\xe3\xc5\x52\x2d", - }, { - .plaintext = "a", - .psize = 1, - .digest = "\xf9\x33\x3e\x45\xd8\x57\xf5\xd9" - "\x0a\x91\xba\xb7\x0a\x1e\xba\x0c" - "\xfb\x1b\xe4\xb0\x78\x3c\x9a\xcf" - "\xcd\x88\x3a\x91\x34\x69\x29\x25", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xaf\xbd\x6e\x22\x8b\x9d\x8c\xbb" - "\xce\xf5\xca\x2d\x03\xe6\xdb\xa1" - "\x0a\xc0\xbc\x7d\xcb\xe4\x68\x0e" - "\x1e\x42\xd2\xe9\x75\x45\x9b\x65", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x87\xe9\x71\x75\x9a\x1c\xe4\x7a" - "\x51\x4d\x5c\x91\x4c\x39\x2c\x90" - "\x18\xc7\xc4\x6b\xc1\x44\x65\x55" - "\x4a\xfc\xdf\x54\xa5\x07\x0c\x0e", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\x64\x9d\x30\x34\x75\x1e\xa2\x16" - "\x77\x6b\xf9\xa1\x8a\xcc\x81\xbc" - "\x78\x96\x11\x8a\x51\x97\x96\x87" - "\x82\xdd\x1f\xd9\x7d\x8d\x51\x33", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" - "fghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\x57\x40\xa4\x08\xac\x16\xb7\x20" - "\xb8\x44\x24\xae\x93\x1c\xbb\x1f" - "\xe3\x63\xd1\xd0\xbf\x40\x17\xf1" - "\xa8\x9f\x7e\xa6\xde\x77\xa0\xb8", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x06\xfd\xcc\x7a\x40\x95\x48\xaa" - "\xf9\x13\x68\xc0\x6a\x62\x75\xb5" - "\x53\xe3\xf0\x99\xbf\x0e\xa4\xed" - "\xfd\x67\x78\xdf\x89\xa8\x90\xdd", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighij" - "hijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x38\x43\x04\x55\x83\xaa\xc6\xc8" - "\xc8\xd9\x12\x85\x73\xe7\xa9\x80" - "\x9a\xfb\x2a\x0f\x34\xcc\xc3\x6e" - "\xa9\xe7\x2f\x16\xf6\x36\x8e\x3f", - .np = 2, - .tap = { 28, 28 }, - } -}; - -/* - * RIPEMD-320 test vectors - */ -#define RMD320_TEST_VECTORS 8 - -static struct hash_testvec rmd320_tv_template[] = { - { - .digest = "\x22\xd6\x5d\x56\x61\x53\x6c\xdc\x75\xc1" - "\xfd\xf5\xc6\xde\x7b\x41\xb9\xf2\x73\x25" - "\xeb\xc6\x1e\x85\x57\x17\x7d\x70\x5a\x0e" - "\xc8\x80\x15\x1c\x3a\x32\xa0\x08\x99\xb8", - }, { - .plaintext = "a", - .psize = 1, - .digest = "\xce\x78\x85\x06\x38\xf9\x26\x58\xa5\xa5" - "\x85\x09\x75\x79\x92\x6d\xda\x66\x7a\x57" - "\x16\x56\x2c\xfc\xf6\xfb\xe7\x7f\x63\x54" - "\x2f\x99\xb0\x47\x05\xd6\x97\x0d\xff\x5d", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xde\x4c\x01\xb3\x05\x4f\x89\x30\xa7\x9d" - "\x09\xae\x73\x8e\x92\x30\x1e\x5a\x17\x08" - "\x5b\xef\xfd\xc1\xb8\xd1\x16\x71\x3e\x74" - "\xf8\x2f\xa9\x42\xd6\x4c\xdb\xc4\x68\x2d", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x3a\x8e\x28\x50\x2e\xd4\x5d\x42\x2f\x68" - "\x84\x4f\x9d\xd3\x16\xe7\xb9\x85\x33\xfa" - "\x3f\x2a\x91\xd2\x9f\x84\xd4\x25\xc8\x8d" - "\x6b\x4e\xff\x72\x7d\xf6\x6a\x7c\x01\x97", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xca\xbd\xb1\x81\x0b\x92\x47\x0a\x20\x93" - "\xaa\x6b\xce\x05\x95\x2c\x28\x34\x8c\xf4" - "\x3f\xf6\x08\x41\x97\x51\x66\xbb\x40\xed" - "\x23\x40\x04\xb8\x82\x44\x63\xe6\xb0\x09", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" - "fghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xed\x54\x49\x40\xc8\x6d\x67\xf2\x50\xd2" - "\x32\xc3\x0b\x7b\x3e\x57\x70\xe0\xc6\x0c" - "\x8c\xb9\xa4\xca\xfe\x3b\x11\x38\x8a\xf9" - "\x92\x0e\x1b\x99\x23\x0b\x84\x3c\x86\xa4", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x55\x78\x88\xaf\x5f\x6d\x8e\xd6\x2a\xb6" - "\x69\x45\xc6\xd2\xa0\xa4\x7e\xcd\x53\x41" - "\xe9\x15\xeb\x8f\xea\x1d\x05\x24\x95\x5f" - "\x82\x5d\xc7\x17\xe4\xa0\x08\xab\x2d\x42", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighij" - "hijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\xd0\x34\xa7\x95\x0c\xf7\x22\x02\x1b\xa4" - "\xb8\x4d\xf7\x69\xa5\xde\x20\x60\xe2\x59" - "\xdf\x4c\x9b\xb4\xa4\x26\x8c\x0e\x93\x5b" - "\xbc\x74\x70\xa9\x69\xc9\xd0\x72\xa1\xac", - .np = 2, - .tap = { 28, 28 }, - } -}; - -/* - * SHA1 test vectors from from FIPS PUB 180-1 - */ -#define SHA1_TEST_VECTORS 2 - -static struct hash_testvec sha1_tv_template[] = { - { - .plaintext = "abc", - .psize = 3, - .digest = "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e" - "\x25\x71\x78\x50\xc2\x6c\x9c\xd0\xd8\x9d", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e\xba\xae" - "\x4a\xa1\xf9\x51\x29\xe5\xe5\x46\x70\xf1", - .np = 2, - .tap = { 28, 28 } - } -}; - - -/* - * SHA224 test vectors from from FIPS PUB 180-2 - */ -#define SHA224_TEST_VECTORS 2 - -static struct hash_testvec sha224_tv_template[] = { - { - .plaintext = "abc", - .psize = 3, - .digest = "\x23\x09\x7D\x22\x34\x05\xD8\x22" - "\x86\x42\xA4\x77\xBD\xA2\x55\xB3" - "\x2A\xAD\xBC\xE4\xBD\xA0\xB3\xF7" - "\xE3\x6C\x9D\xA7", - }, { - .plaintext = - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x75\x38\x8B\x16\x51\x27\x76\xCC" - "\x5D\xBA\x5D\xA1\xFD\x89\x01\x50" - "\xB0\xC6\x45\x5C\xB4\xF5\x8B\x19" - "\x52\x52\x25\x25", - .np = 2, - .tap = { 28, 28 } - } -}; - -/* - * SHA256 test vectors from from NIST - */ -#define SHA256_TEST_VECTORS 2 - -static struct hash_testvec sha256_tv_template[] = { - { - .plaintext = "abc", - .psize = 3, - .digest = "\xba\x78\x16\xbf\x8f\x01\xcf\xea" - "\x41\x41\x40\xde\x5d\xae\x22\x23" - "\xb0\x03\x61\xa3\x96\x17\x7a\x9c" - "\xb4\x10\xff\x61\xf2\x00\x15\xad", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x24\x8d\x6a\x61\xd2\x06\x38\xb8" - "\xe5\xc0\x26\x93\x0c\x3e\x60\x39" - "\xa3\x3c\xe4\x59\x64\xff\x21\x67" - "\xf6\xec\xed\xd4\x19\xdb\x06\xc1", - .np = 2, - .tap = { 28, 28 } - }, -}; - -/* - * SHA384 test vectors from from NIST and kerneli - */ -#define SHA384_TEST_VECTORS 4 - -static struct hash_testvec sha384_tv_template[] = { - { - .plaintext= "abc", - .psize = 3, - .digest = "\xcb\x00\x75\x3f\x45\xa3\x5e\x8b" - "\xb5\xa0\x3d\x69\x9a\xc6\x50\x07" - "\x27\x2c\x32\xab\x0e\xde\xd1\x63" - "\x1a\x8b\x60\x5a\x43\xff\x5b\xed" - "\x80\x86\x07\x2b\xa1\xe7\xcc\x23" - "\x58\xba\xec\xa1\x34\xc8\x25\xa7", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x33\x91\xfd\xdd\xfc\x8d\xc7\x39" - "\x37\x07\xa6\x5b\x1b\x47\x09\x39" - "\x7c\xf8\xb1\xd1\x62\xaf\x05\xab" - "\xfe\x8f\x45\x0d\xe5\xf3\x6b\xc6" - "\xb0\x45\x5a\x85\x20\xbc\x4e\x6f" - "\x5f\xe9\x5b\x1f\xe3\xc8\x45\x2b", - }, { - .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .psize = 112, - .digest = "\x09\x33\x0c\x33\xf7\x11\x47\xe8" - "\x3d\x19\x2f\xc7\x82\xcd\x1b\x47" - "\x53\x11\x1b\x17\x3b\x3b\x05\xd2" - "\x2f\xa0\x80\x86\xe3\xb0\xf7\x12" - "\xfc\xc7\xc7\x1a\x55\x7e\x2d\xb9" - "\x66\xc3\xe9\xfa\x91\x74\x60\x39", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd" - "efghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", - .psize = 104, - .digest = "\x3d\x20\x89\x73\xab\x35\x08\xdb" - "\xbd\x7e\x2c\x28\x62\xba\x29\x0a" - "\xd3\x01\x0e\x49\x78\xc1\x98\xdc" - "\x4d\x8f\xd0\x14\xe5\x82\x82\x3a" - "\x89\xe1\x6f\x9b\x2a\x7b\xbc\x1a" - "\xc9\x38\xe2\xd1\x99\xe8\xbe\xa4", - .np = 4, - .tap = { 26, 26, 26, 26 } - }, -}; - -/* - * SHA512 test vectors from from NIST and kerneli - */ -#define SHA512_TEST_VECTORS 4 - -static struct hash_testvec sha512_tv_template[] = { - { - .plaintext = "abc", - .psize = 3, - .digest = "\xdd\xaf\x35\xa1\x93\x61\x7a\xba" - "\xcc\x41\x73\x49\xae\x20\x41\x31" - "\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2" - "\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a" - "\x21\x92\x99\x2a\x27\x4f\xc1\xa8" - "\x36\xba\x3c\x23\xa3\xfe\xeb\xbd" - "\x45\x4d\x44\x23\x64\x3c\xe8\x0e" - "\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a" - "\x0c\xed\x7b\xeb\x8e\x08\xa4\x16" - "\x57\xc1\x6e\xf4\x68\xb2\x28\xa8" - "\x27\x9b\xe3\x31\xa7\x03\xc3\x35" - "\x96\xfd\x15\xc1\x3b\x1b\x07\xf9" - "\xaa\x1d\x3b\xea\x57\x78\x9c\xa0" - "\x31\xad\x85\xc7\xa7\x1d\xd7\x03" - "\x54\xec\x63\x12\x38\xca\x34\x45", - }, { - .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .psize = 112, - .digest = "\x8e\x95\x9b\x75\xda\xe3\x13\xda" - "\x8c\xf4\xf7\x28\x14\xfc\x14\x3f" - "\x8f\x77\x79\xc6\xeb\x9f\x7f\xa1" - "\x72\x99\xae\xad\xb6\x88\x90\x18" - "\x50\x1d\x28\x9e\x49\x00\xf7\xe4" - "\x33\x1b\x99\xde\xc4\xb5\x43\x3a" - "\xc7\xd3\x29\xee\xb6\xdd\x26\x54" - "\x5e\x96\xe5\x5b\x87\x4b\xe9\x09", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd" - "efghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", - .psize = 104, - .digest = "\x93\x0d\x0c\xef\xcb\x30\xff\x11" - "\x33\xb6\x89\x81\x21\xf1\xcf\x3d" - "\x27\x57\x8a\xfc\xaf\xe8\x67\x7c" - "\x52\x57\xcf\x06\x99\x11\xf7\x5d" - "\x8f\x58\x31\xb5\x6e\xbf\xda\x67" - "\xb2\x78\xe6\x6d\xff\x8b\x84\xfe" - "\x2b\x28\x70\xf7\x42\xa5\x80\xd8" - "\xed\xb4\x19\x87\x23\x28\x50\xc9", - .np = 4, - .tap = { 26, 26, 26, 26 } - }, -}; - - -/* - * WHIRLPOOL test vectors from Whirlpool package - * by Vincent Rijmen and Paulo S. L. M. Barreto as part of the NESSIE - * submission - */ -#define WP512_TEST_VECTORS 8 - -static struct hash_testvec wp512_tv_template[] = { - { - .plaintext = "", - .psize = 0, - .digest = "\x19\xFA\x61\xD7\x55\x22\xA4\x66" - "\x9B\x44\xE3\x9C\x1D\x2E\x17\x26" - "\xC5\x30\x23\x21\x30\xD4\x07\xF8" - "\x9A\xFE\xE0\x96\x49\x97\xF7\xA7" - "\x3E\x83\xBE\x69\x8B\x28\x8F\xEB" - "\xCF\x88\xE3\xE0\x3C\x4F\x07\x57" - "\xEA\x89\x64\xE5\x9B\x63\xD9\x37" - "\x08\xB1\x38\xCC\x42\xA6\x6E\xB3", - - - }, { - .plaintext = "a", - .psize = 1, - .digest = "\x8A\xCA\x26\x02\x79\x2A\xEC\x6F" - "\x11\xA6\x72\x06\x53\x1F\xB7\xD7" - "\xF0\xDF\xF5\x94\x13\x14\x5E\x69" - "\x73\xC4\x50\x01\xD0\x08\x7B\x42" - "\xD1\x1B\xC6\x45\x41\x3A\xEF\xF6" - "\x3A\x42\x39\x1A\x39\x14\x5A\x59" - "\x1A\x92\x20\x0D\x56\x01\x95\xE5" - "\x3B\x47\x85\x84\xFD\xAE\x23\x1A", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\x4E\x24\x48\xA4\xC6\xF4\x86\xBB" - "\x16\xB6\x56\x2C\x73\xB4\x02\x0B" - "\xF3\x04\x3E\x3A\x73\x1B\xCE\x72" - "\x1A\xE1\xB3\x03\xD9\x7E\x6D\x4C" - "\x71\x81\xEE\xBD\xB6\xC5\x7E\x27" - "\x7D\x0E\x34\x95\x71\x14\xCB\xD6" - "\xC7\x97\xFC\x9D\x95\xD8\xB5\x82" - "\xD2\x25\x29\x20\x76\xD4\xEE\xF5", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x37\x8C\x84\xA4\x12\x6E\x2D\xC6" - "\xE5\x6D\xCC\x74\x58\x37\x7A\xAC" - "\x83\x8D\x00\x03\x22\x30\xF5\x3C" - "\xE1\xF5\x70\x0C\x0F\xFB\x4D\x3B" - "\x84\x21\x55\x76\x59\xEF\x55\xC1" - "\x06\xB4\xB5\x2A\xC5\xA4\xAA\xA6" - "\x92\xED\x92\x00\x52\x83\x8F\x33" - "\x62\xE8\x6D\xBD\x37\xA8\x90\x3E", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xF1\xD7\x54\x66\x26\x36\xFF\xE9" - "\x2C\x82\xEB\xB9\x21\x2A\x48\x4A" - "\x8D\x38\x63\x1E\xAD\x42\x38\xF5" - "\x44\x2E\xE1\x3B\x80\x54\xE4\x1B" - "\x08\xBF\x2A\x92\x51\xC3\x0B\x6A" - "\x0B\x8A\xAE\x86\x17\x7A\xB4\xA6" - "\xF6\x8F\x67\x3E\x72\x07\x86\x5D" - "\x5D\x98\x19\xA3\xDB\xA4\xEB\x3B", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xDC\x37\xE0\x08\xCF\x9E\xE6\x9B" - "\xF1\x1F\x00\xED\x9A\xBA\x26\x90" - "\x1D\xD7\xC2\x8C\xDE\xC0\x66\xCC" - "\x6A\xF4\x2E\x40\xF8\x2F\x3A\x1E" - "\x08\xEB\xA2\x66\x29\x12\x9D\x8F" - "\xB7\xCB\x57\x21\x1B\x92\x81\xA6" - "\x55\x17\xCC\x87\x9D\x7B\x96\x21" - "\x42\xC6\x5F\x5A\x7A\xF0\x14\x67", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x46\x6E\xF1\x8B\xAB\xB0\x15\x4D" - "\x25\xB9\xD3\x8A\x64\x14\xF5\xC0" - "\x87\x84\x37\x2B\xCC\xB2\x04\xD6" - "\x54\x9C\x4A\xFA\xDB\x60\x14\x29" - "\x4D\x5B\xD8\xDF\x2A\x6C\x44\xE5" - "\x38\xCD\x04\x7B\x26\x81\xA5\x1A" - "\x2C\x60\x48\x1E\x88\xC5\xA2\x0B" - "\x2C\x2A\x80\xCF\x3A\x9A\x08\x3B", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijk", - .psize = 32, - .digest = "\x2A\x98\x7E\xA4\x0F\x91\x70\x61" - "\xF5\xD6\xF0\xA0\xE4\x64\x4F\x48" - "\x8A\x7A\x5A\x52\xDE\xEE\x65\x62" - "\x07\xC5\x62\xF9\x88\xE9\x5C\x69" - "\x16\xBD\xC8\x03\x1B\xC5\xBE\x1B" - "\x7B\x94\x76\x39\xFE\x05\x0B\x56" - "\x93\x9B\xAA\xA0\xAD\xFF\x9A\xE6" - "\x74\x5B\x7B\x18\x1C\x3B\xE3\xFD", - }, -}; - -#define WP384_TEST_VECTORS 8 - -static struct hash_testvec wp384_tv_template[] = { - { - .plaintext = "", - .psize = 0, - .digest = "\x19\xFA\x61\xD7\x55\x22\xA4\x66" - "\x9B\x44\xE3\x9C\x1D\x2E\x17\x26" - "\xC5\x30\x23\x21\x30\xD4\x07\xF8" - "\x9A\xFE\xE0\x96\x49\x97\xF7\xA7" - "\x3E\x83\xBE\x69\x8B\x28\x8F\xEB" - "\xCF\x88\xE3\xE0\x3C\x4F\x07\x57", - - - }, { - .plaintext = "a", - .psize = 1, - .digest = "\x8A\xCA\x26\x02\x79\x2A\xEC\x6F" - "\x11\xA6\x72\x06\x53\x1F\xB7\xD7" - "\xF0\xDF\xF5\x94\x13\x14\x5E\x69" - "\x73\xC4\x50\x01\xD0\x08\x7B\x42" - "\xD1\x1B\xC6\x45\x41\x3A\xEF\xF6" - "\x3A\x42\x39\x1A\x39\x14\x5A\x59", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\x4E\x24\x48\xA4\xC6\xF4\x86\xBB" - "\x16\xB6\x56\x2C\x73\xB4\x02\x0B" - "\xF3\x04\x3E\x3A\x73\x1B\xCE\x72" - "\x1A\xE1\xB3\x03\xD9\x7E\x6D\x4C" - "\x71\x81\xEE\xBD\xB6\xC5\x7E\x27" - "\x7D\x0E\x34\x95\x71\x14\xCB\xD6", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x37\x8C\x84\xA4\x12\x6E\x2D\xC6" - "\xE5\x6D\xCC\x74\x58\x37\x7A\xAC" - "\x83\x8D\x00\x03\x22\x30\xF5\x3C" - "\xE1\xF5\x70\x0C\x0F\xFB\x4D\x3B" - "\x84\x21\x55\x76\x59\xEF\x55\xC1" - "\x06\xB4\xB5\x2A\xC5\xA4\xAA\xA6", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xF1\xD7\x54\x66\x26\x36\xFF\xE9" - "\x2C\x82\xEB\xB9\x21\x2A\x48\x4A" - "\x8D\x38\x63\x1E\xAD\x42\x38\xF5" - "\x44\x2E\xE1\x3B\x80\x54\xE4\x1B" - "\x08\xBF\x2A\x92\x51\xC3\x0B\x6A" - "\x0B\x8A\xAE\x86\x17\x7A\xB4\xA6", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xDC\x37\xE0\x08\xCF\x9E\xE6\x9B" - "\xF1\x1F\x00\xED\x9A\xBA\x26\x90" - "\x1D\xD7\xC2\x8C\xDE\xC0\x66\xCC" - "\x6A\xF4\x2E\x40\xF8\x2F\x3A\x1E" - "\x08\xEB\xA2\x66\x29\x12\x9D\x8F" - "\xB7\xCB\x57\x21\x1B\x92\x81\xA6", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x46\x6E\xF1\x8B\xAB\xB0\x15\x4D" - "\x25\xB9\xD3\x8A\x64\x14\xF5\xC0" - "\x87\x84\x37\x2B\xCC\xB2\x04\xD6" - "\x54\x9C\x4A\xFA\xDB\x60\x14\x29" - "\x4D\x5B\xD8\xDF\x2A\x6C\x44\xE5" - "\x38\xCD\x04\x7B\x26\x81\xA5\x1A", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijk", - .psize = 32, - .digest = "\x2A\x98\x7E\xA4\x0F\x91\x70\x61" - "\xF5\xD6\xF0\xA0\xE4\x64\x4F\x48" - "\x8A\x7A\x5A\x52\xDE\xEE\x65\x62" - "\x07\xC5\x62\xF9\x88\xE9\x5C\x69" - "\x16\xBD\xC8\x03\x1B\xC5\xBE\x1B" - "\x7B\x94\x76\x39\xFE\x05\x0B\x56", - }, -}; - -#define WP256_TEST_VECTORS 8 - -static struct hash_testvec wp256_tv_template[] = { - { - .plaintext = "", - .psize = 0, - .digest = "\x19\xFA\x61\xD7\x55\x22\xA4\x66" - "\x9B\x44\xE3\x9C\x1D\x2E\x17\x26" - "\xC5\x30\x23\x21\x30\xD4\x07\xF8" - "\x9A\xFE\xE0\x96\x49\x97\xF7\xA7", - - - }, { - .plaintext = "a", - .psize = 1, - .digest = "\x8A\xCA\x26\x02\x79\x2A\xEC\x6F" - "\x11\xA6\x72\x06\x53\x1F\xB7\xD7" - "\xF0\xDF\xF5\x94\x13\x14\x5E\x69" - "\x73\xC4\x50\x01\xD0\x08\x7B\x42", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\x4E\x24\x48\xA4\xC6\xF4\x86\xBB" - "\x16\xB6\x56\x2C\x73\xB4\x02\x0B" - "\xF3\x04\x3E\x3A\x73\x1B\xCE\x72" - "\x1A\xE1\xB3\x03\xD9\x7E\x6D\x4C", - }, { - .plaintext = "message digest", - .psize = 14, - .digest = "\x37\x8C\x84\xA4\x12\x6E\x2D\xC6" - "\xE5\x6D\xCC\x74\x58\x37\x7A\xAC" - "\x83\x8D\x00\x03\x22\x30\xF5\x3C" - "\xE1\xF5\x70\x0C\x0F\xFB\x4D\x3B", - }, { - .plaintext = "abcdefghijklmnopqrstuvwxyz", - .psize = 26, - .digest = "\xF1\xD7\x54\x66\x26\x36\xFF\xE9" - "\x2C\x82\xEB\xB9\x21\x2A\x48\x4A" - "\x8D\x38\x63\x1E\xAD\x42\x38\xF5" - "\x44\x2E\xE1\x3B\x80\x54\xE4\x1B", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz0123456789", - .psize = 62, - .digest = "\xDC\x37\xE0\x08\xCF\x9E\xE6\x9B" - "\xF1\x1F\x00\xED\x9A\xBA\x26\x90" - "\x1D\xD7\xC2\x8C\xDE\xC0\x66\xCC" - "\x6A\xF4\x2E\x40\xF8\x2F\x3A\x1E", - }, { - .plaintext = "1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", - .psize = 80, - .digest = "\x46\x6E\xF1\x8B\xAB\xB0\x15\x4D" - "\x25\xB9\xD3\x8A\x64\x14\xF5\xC0" - "\x87\x84\x37\x2B\xCC\xB2\x04\xD6" - "\x54\x9C\x4A\xFA\xDB\x60\x14\x29", - }, { - .plaintext = "abcdbcdecdefdefgefghfghighijhijk", - .psize = 32, - .digest = "\x2A\x98\x7E\xA4\x0F\x91\x70\x61" - "\xF5\xD6\xF0\xA0\xE4\x64\x4F\x48" - "\x8A\x7A\x5A\x52\xDE\xEE\x65\x62" - "\x07\xC5\x62\xF9\x88\xE9\x5C\x69", - }, -}; - -/* - * TIGER test vectors from Tiger website - */ -#define TGR192_TEST_VECTORS 6 - -static struct hash_testvec tgr192_tv_template[] = { - { - .plaintext = "", - .psize = 0, - .digest = "\x24\xf0\x13\x0c\x63\xac\x93\x32" - "\x16\x16\x6e\x76\xb1\xbb\x92\x5f" - "\xf3\x73\xde\x2d\x49\x58\x4e\x7a", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xf2\x58\xc1\xe8\x84\x14\xab\x2a" - "\x52\x7a\xb5\x41\xff\xc5\xb8\xbf" - "\x93\x5f\x7b\x95\x1c\x13\x29\x51", - }, { - .plaintext = "Tiger", - .psize = 5, - .digest = "\x9f\x00\xf5\x99\x07\x23\x00\xdd" - "\x27\x6a\xbb\x38\xc8\xeb\x6d\xec" - "\x37\x79\x0c\x11\x6f\x9d\x2b\xdf", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-", - .psize = 64, - .digest = "\x87\xfb\x2a\x90\x83\x85\x1c\xf7" - "\x47\x0d\x2c\xf8\x10\xe6\xdf\x9e" - "\xb5\x86\x44\x50\x34\xa5\xa3\x86", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789", - .psize = 64, - .digest = "\x46\x7d\xb8\x08\x63\xeb\xce\x48" - "\x8d\xf1\xcd\x12\x61\x65\x5d\xe9" - "\x57\x89\x65\x65\x97\x5f\x91\x97", - }, { - .plaintext = "Tiger - A Fast New Hash Function, " - "by Ross Anderson and Eli Biham, " - "proceedings of Fast Software Encryption 3, " - "Cambridge, 1996.", - .psize = 125, - .digest = "\x3d\x9a\xeb\x03\xd1\xbd\x1a\x63" - "\x57\xb2\x77\x4d\xfd\x6d\x5b\x24" - "\xdd\x68\x15\x1d\x50\x39\x74\xfc", - }, -}; - -#define TGR160_TEST_VECTORS 6 - -static struct hash_testvec tgr160_tv_template[] = { - { - .plaintext = "", - .psize = 0, - .digest = "\x24\xf0\x13\x0c\x63\xac\x93\x32" - "\x16\x16\x6e\x76\xb1\xbb\x92\x5f" - "\xf3\x73\xde\x2d", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xf2\x58\xc1\xe8\x84\x14\xab\x2a" - "\x52\x7a\xb5\x41\xff\xc5\xb8\xbf" - "\x93\x5f\x7b\x95", - }, { - .plaintext = "Tiger", - .psize = 5, - .digest = "\x9f\x00\xf5\x99\x07\x23\x00\xdd" - "\x27\x6a\xbb\x38\xc8\xeb\x6d\xec" - "\x37\x79\x0c\x11", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-", - .psize = 64, - .digest = "\x87\xfb\x2a\x90\x83\x85\x1c\xf7" - "\x47\x0d\x2c\xf8\x10\xe6\xdf\x9e" - "\xb5\x86\x44\x50", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789", - .psize = 64, - .digest = "\x46\x7d\xb8\x08\x63\xeb\xce\x48" - "\x8d\xf1\xcd\x12\x61\x65\x5d\xe9" - "\x57\x89\x65\x65", - }, { - .plaintext = "Tiger - A Fast New Hash Function, " - "by Ross Anderson and Eli Biham, " - "proceedings of Fast Software Encryption 3, " - "Cambridge, 1996.", - .psize = 125, - .digest = "\x3d\x9a\xeb\x03\xd1\xbd\x1a\x63" - "\x57\xb2\x77\x4d\xfd\x6d\x5b\x24" - "\xdd\x68\x15\x1d", - }, -}; - -#define TGR128_TEST_VECTORS 6 - -static struct hash_testvec tgr128_tv_template[] = { - { - .plaintext = "", - .psize = 0, - .digest = "\x24\xf0\x13\x0c\x63\xac\x93\x32" - "\x16\x16\x6e\x76\xb1\xbb\x92\x5f", - }, { - .plaintext = "abc", - .psize = 3, - .digest = "\xf2\x58\xc1\xe8\x84\x14\xab\x2a" - "\x52\x7a\xb5\x41\xff\xc5\xb8\xbf", - }, { - .plaintext = "Tiger", - .psize = 5, - .digest = "\x9f\x00\xf5\x99\x07\x23\x00\xdd" - "\x27\x6a\xbb\x38\xc8\xeb\x6d\xec", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-", - .psize = 64, - .digest = "\x87\xfb\x2a\x90\x83\x85\x1c\xf7" - "\x47\x0d\x2c\xf8\x10\xe6\xdf\x9e", - }, { - .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789", - .psize = 64, - .digest = "\x46\x7d\xb8\x08\x63\xeb\xce\x48" - "\x8d\xf1\xcd\x12\x61\x65\x5d\xe9", - }, { - .plaintext = "Tiger - A Fast New Hash Function, " - "by Ross Anderson and Eli Biham, " - "proceedings of Fast Software Encryption 3, " - "Cambridge, 1996.", - .psize = 125, - .digest = "\x3d\x9a\xeb\x03\xd1\xbd\x1a\x63" - "\x57\xb2\x77\x4d\xfd\x6d\x5b\x24", - }, -}; - -/* - * HMAC-MD5 test vectors from RFC2202 - * (These need to be fixed to not use strlen). - */ -#define HMAC_MD5_TEST_VECTORS 7 - -static struct hash_testvec hmac_md5_tv_template[] = -{ - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", - .ksize = 16, - .plaintext = "Hi There", - .psize = 8, - .digest = "\x92\x94\x72\x7a\x36\x38\xbb\x1c" - "\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03" - "\xea\xa8\x6e\x31\x0a\x5d\xb7\x38", - .np = 2, - .tap = {14, 14} - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", - .ksize = 16, - .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", - .psize = 50, - .digest = "\x56\xbe\x34\x52\x1d\x14\x4c\x88" - "\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18\x19", - .ksize = 25, - .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", - .psize = 50, - .digest = "\x69\x7e\xaf\x0a\xca\x3a\x3a\xea" - "\x3a\x75\x16\x47\x46\xff\xaa\x79", - }, { - .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", - .ksize = 16, - .plaintext = "Test With Truncation", - .psize = 20, - .digest = "\x56\x46\x1e\xf2\x34\x2e\xdc\x00" - "\xf9\xba\xb9\x95\x69\x0e\xfd\x4c", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", - .psize = 54, - .digest = "\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f" - "\x0b\x62\xe6\xce\x61\xb9\xd0\xcd", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " - "Block-Size Data", - .psize = 73, - .digest = "\x6f\x63\x0f\xad\x67\xcd\xa0\xee" - "\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e", - }, -}; - -/* - * HMAC-RIPEMD128 test vectors from RFC2286 - */ -#define HMAC_RMD128_TEST_VECTORS 7 - -static struct hash_testvec hmac_rmd128_tv_template[] = { - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", - .ksize = 16, - .plaintext = "Hi There", - .psize = 8, - .digest = "\xfb\xf6\x1f\x94\x92\xaa\x4b\xbf" - "\x81\xc1\x72\xe8\x4e\x07\x34\xdb", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\x87\x5f\x82\x88\x62\xb6\xb3\x34" - "\xb4\x27\xc5\x5f\x9f\x7f\xf0\x9b", - .np = 2, - .tap = { 14, 14 }, - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", - .ksize = 16, - .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", - .psize = 50, - .digest = "\x09\xf0\xb2\x84\x6d\x2f\x54\x3d" - "\xa3\x63\xcb\xec\x8d\x62\xa3\x8d", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18\x19", - .ksize = 25, - .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", - .psize = 50, - .digest = "\xbd\xbb\xd7\xcf\x03\xe4\x4b\x5a" - "\xa6\x0a\xf8\x15\xbe\x4d\x22\x94", - }, { - .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", - .ksize = 16, - .plaintext = "Test With Truncation", - .psize = 20, - .digest = "\xe7\x98\x08\xf2\x4b\x25\xfd\x03" - "\x1c\x15\x5f\x0d\x55\x1d\x9a\x3a", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", - .psize = 54, - .digest = "\xdc\x73\x29\x28\xde\x98\x10\x4a" - "\x1f\x59\xd3\x73\xc1\x50\xac\xbb", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " - "Block-Size Data", - .psize = 73, - .digest = "\x5c\x6b\xec\x96\x79\x3e\x16\xd4" - "\x06\x90\xc2\x37\x63\x5f\x30\xc5", - }, -}; - -/* - * HMAC-RIPEMD160 test vectors from RFC2286 - */ -#define HMAC_RMD160_TEST_VECTORS 7 - -static struct hash_testvec hmac_rmd160_tv_template[] = { - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", - .ksize = 20, - .plaintext = "Hi There", - .psize = 8, - .digest = "\x24\xcb\x4b\xd6\x7d\x20\xfc\x1a\x5d\x2e" - "\xd7\x73\x2d\xcc\x39\x37\x7f\x0a\x56\x68", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\xdd\xa6\xc0\x21\x3a\x48\x5a\x9e\x24\xf4" - "\x74\x20\x64\xa7\xf0\x33\xb4\x3c\x40\x69", - .np = 2, - .tap = { 14, 14 }, - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", - .ksize = 20, - .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", - .psize = 50, - .digest = "\xb0\xb1\x05\x36\x0d\xe7\x59\x96\x0a\xb4" - "\xf3\x52\x98\xe1\x16\xe2\x95\xd8\xe7\xc1", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18\x19", - .ksize = 25, - .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", - .psize = 50, - .digest = "\xd5\xca\x86\x2f\x4d\x21\xd5\xe6\x10\xe1" - "\x8b\x4c\xf1\xbe\xb9\x7a\x43\x65\xec\xf4", - }, { - .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", - .ksize = 20, - .plaintext = "Test With Truncation", - .psize = 20, - .digest = "\x76\x19\x69\x39\x78\xf9\x1d\x90\x53\x9a" - "\xe7\x86\x50\x0f\xf3\xd8\xe0\x51\x8e\x39", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", - .psize = 54, - .digest = "\x64\x66\xca\x07\xac\x5e\xac\x29\xe1\xbd" - "\x52\x3e\x5a\xda\x76\x05\xb7\x91\xfd\x8b", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " - "Block-Size Data", - .psize = 73, - .digest = "\x69\xea\x60\x79\x8d\x71\x61\x6c\xce\x5f" - "\xd0\x87\x1e\x23\x75\x4c\xd7\x5d\x5a\x0a", - }, -}; - -/* - * HMAC-SHA1 test vectors from RFC2202 - */ -#define HMAC_SHA1_TEST_VECTORS 7 - -static struct hash_testvec hmac_sha1_tv_template[] = { - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", - .ksize = 20, - .plaintext = "Hi There", - .psize = 8, - .digest = "\xb6\x17\x31\x86\x55\x05\x72\x64" - "\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1" - "\x46\xbe", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74" - "\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79", - .np = 2, - .tap = { 14, 14 } - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", - .ksize = 20, - .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", - .psize = 50, - .digest = "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3" - "\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18\x19", - .ksize = 25, - .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", - .psize = 50, - .digest = "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84" - "\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda", - }, { - .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", - .ksize = 20, - .plaintext = "Test With Truncation", - .psize = 20, - .digest = "\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2" - "\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", - .psize = 54, - .digest = "\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70" - "\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " - "Block-Size Data", - .psize = 73, - .digest = "\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b" - "\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91", - }, -}; - - -/* - * SHA224 HMAC test vectors from RFC4231 - */ -#define HMAC_SHA224_TEST_VECTORS 4 - -static struct hash_testvec hmac_sha224_tv_template[] = { - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b", - .ksize = 20, - /* ("Hi There") */ - .plaintext = "\x48\x69\x20\x54\x68\x65\x72\x65", - .psize = 8, - .digest = "\x89\x6f\xb1\x12\x8a\xbb\xdf\x19" - "\x68\x32\x10\x7c\xd4\x9d\xf3\x3f" - "\x47\xb4\xb1\x16\x99\x12\xba\x4f" - "\x53\x68\x4b\x22", - }, { - .key = "Jefe", - .ksize = 4, - /* ("what do ya want for nothing?") */ - .plaintext = "\x77\x68\x61\x74\x20\x64\x6f\x20" - "\x79\x61\x20\x77\x61\x6e\x74\x20" - "\x66\x6f\x72\x20\x6e\x6f\x74\x68" - "\x69\x6e\x67\x3f", - .psize = 28, - .digest = "\xa3\x0e\x01\x09\x8b\xc6\xdb\xbf" - "\x45\x69\x0f\x3a\x7e\x9e\x6d\x0f" - "\x8b\xbe\xa2\xa3\x9e\x61\x48\x00" - "\x8f\xd0\x5e\x44", - .np = 4, - .tap = { 7, 7, 7, 7 } - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa", - .ksize = 131, - /* ("Test Using Larger Than Block-Size Key - Hash Key First") */ - .plaintext = "\x54\x65\x73\x74\x20\x55\x73\x69" - "\x6e\x67\x20\x4c\x61\x72\x67\x65" - "\x72\x20\x54\x68\x61\x6e\x20\x42" - "\x6c\x6f\x63\x6b\x2d\x53\x69\x7a" - "\x65\x20\x4b\x65\x79\x20\x2d\x20" - "\x48\x61\x73\x68\x20\x4b\x65\x79" - "\x20\x46\x69\x72\x73\x74", - .psize = 54, - .digest = "\x95\xe9\xa0\xdb\x96\x20\x95\xad" - "\xae\xbe\x9b\x2d\x6f\x0d\xbc\xe2" - "\xd4\x99\xf1\x12\xf2\xd2\xb7\x27" - "\x3f\xa6\x87\x0e", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa", - .ksize = 131, - /* ("This is a test using a larger than block-size key and a") - (" larger than block-size data. The key needs to be") - (" hashed before being used by the HMAC algorithm.") */ - .plaintext = "\x54\x68\x69\x73\x20\x69\x73\x20" - "\x61\x20\x74\x65\x73\x74\x20\x75" - "\x73\x69\x6e\x67\x20\x61\x20\x6c" - "\x61\x72\x67\x65\x72\x20\x74\x68" - "\x61\x6e\x20\x62\x6c\x6f\x63\x6b" - "\x2d\x73\x69\x7a\x65\x20\x6b\x65" - "\x79\x20\x61\x6e\x64\x20\x61\x20" - "\x6c\x61\x72\x67\x65\x72\x20\x74" - "\x68\x61\x6e\x20\x62\x6c\x6f\x63" - "\x6b\x2d\x73\x69\x7a\x65\x20\x64" - "\x61\x74\x61\x2e\x20\x54\x68\x65" - "\x20\x6b\x65\x79\x20\x6e\x65\x65" - "\x64\x73\x20\x74\x6f\x20\x62\x65" - "\x20\x68\x61\x73\x68\x65\x64\x20" - "\x62\x65\x66\x6f\x72\x65\x20\x62" - "\x65\x69\x6e\x67\x20\x75\x73\x65" - "\x64\x20\x62\x79\x20\x74\x68\x65" - "\x20\x48\x4d\x41\x43\x20\x61\x6c" - "\x67\x6f\x72\x69\x74\x68\x6d\x2e", - .psize = 152, - .digest = "\x3a\x85\x41\x66\xac\x5d\x9f\x02" - "\x3f\x54\xd5\x17\xd0\xb3\x9d\xbd" - "\x94\x67\x70\xdb\x9c\x2b\x95\xc9" - "\xf6\xf5\x65\xd1", - }, -}; - -/* - * HMAC-SHA256 test vectors from - * draft-ietf-ipsec-ciph-sha-256-01.txt - */ -#define HMAC_SHA256_TEST_VECTORS 10 - -static struct hash_testvec hmac_sha256_tv_template[] = { - { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", - .ksize = 32, - .plaintext = "abc", - .psize = 3, - .digest = "\xa2\x1b\x1f\x5d\x4c\xf4\xf7\x3a" - "\x4d\xd9\x39\x75\x0f\x7a\x06\x6a" - "\x7f\x98\xcc\x13\x1c\xb1\x6a\x66" - "\x92\x75\x90\x21\xcf\xab\x81\x81", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", - .ksize = 32, - .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 56, - .digest = "\x10\x4f\xdc\x12\x57\x32\x8f\x08" - "\x18\x4b\xa7\x31\x31\xc5\x3c\xae" - "\xe6\x98\xe3\x61\x19\x42\x11\x49" - "\xea\x8c\x71\x24\x56\x69\x7d\x30", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", - .ksize = 32, - .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .psize = 112, - .digest = "\x47\x03\x05\xfc\x7e\x40\xfe\x34" - "\xd3\xee\xb3\xe7\x73\xd9\x5a\xab" - "\x73\xac\xf0\xfd\x06\x04\x47\xa5" - "\xeb\x45\x95\xbf\x33\xa9\xd1\xa3", - }, { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b\x0b\x0b", - .ksize = 32, - .plaintext = "Hi There", - .psize = 8, - .digest = "\x19\x8a\x60\x7e\xb4\x4b\xfb\xc6" - "\x99\x03\xa0\xf1\xcf\x2b\xbd\xc5" - "\xba\x0a\xa3\xf3\xd9\xae\x3c\x1c" - "\x7a\x3b\x16\x96\xa0\xb6\x8c\xf7", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e" - "\x6a\x04\x24\x26\x08\x95\x75\xc7" - "\x5a\x00\x3f\x08\x9d\x27\x39\x83" - "\x9d\xec\x58\xb9\x64\xec\x38\x43", - .np = 2, - .tap = { 14, 14 } - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa", - .ksize = 32, - .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" - "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", - .psize = 50, - .digest = "\xcd\xcb\x12\x20\xd1\xec\xcc\xea" - "\x91\xe5\x3a\xba\x30\x92\xf9\x62" - "\xe5\x49\xfe\x6c\xe9\xed\x7f\xdc" - "\x43\x19\x1f\xbd\xe4\x5c\x30\xb0", - }, { - .key = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" - "\x21\x22\x23\x24\x25", - .ksize = 37, - .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" - "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", - .psize = 50, - .digest = "\xd4\x63\x3c\x17\xf6\xfb\x8d\x74" - "\x4c\x66\xde\xe0\xf8\xf0\x74\x55" - "\x6e\xc4\xaf\x55\xef\x07\x99\x85" - "\x41\x46\x8e\xb4\x9b\xd2\xe9\x17", - }, { - .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" - "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" - "\x0c\x0c\x0c\x0c\x0c\x0c", - .ksize = 32, - .plaintext = "Test With Truncation", - .psize = 20, - .digest = "\x75\x46\xaf\x01\x84\x1f\xc0\x9b" - "\x1a\xb9\xc3\x74\x9a\x5f\x1c\x17" - "\xd4\xf5\x89\x66\x8a\x58\x7b\x27" - "\x00\xa9\xc9\x7c\x11\x93\xcf\x42", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", - .psize = 54, - .digest = "\x69\x53\x02\x5e\xd9\x6f\x0c\x09" - "\xf8\x0a\x96\xf7\x8e\x65\x38\xdb" - "\xe2\xe7\xb8\x20\xe3\xdd\x97\x0e" - "\x7d\xdd\x39\x09\x1b\x32\x35\x2f", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa", - .ksize = 80, - .plaintext = "Test Using Larger Than Block-Size Key and Larger Than " - "One Block-Size Data", - .psize = 73, - .digest = "\x63\x55\xac\x22\xe8\x90\xd0\xa3" - "\xc8\x48\x1a\x5c\xa4\x82\x5b\xc8" - "\x84\xd3\xe7\xa1\xff\x98\xa2\xfc" - "\x2a\xc7\xd8\xe0\x64\xc3\xb2\xe6", - }, -}; - -#define XCBC_AES_TEST_VECTORS 6 - -static struct hash_testvec aes_xcbc128_tv_template[] = { - { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .plaintext = zeroed_string, - .digest = "\x75\xf0\x25\x1d\x52\x8a\xc0\x1c" - "\x45\x73\xdf\xd5\x84\xd7\x9f\x29", - .psize = 0, - .ksize = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .plaintext = "\x00\x01\x02", - .digest = "\x5b\x37\x65\x80\xae\x2f\x19\xaf" - "\xe7\x21\x9c\xee\xf1\x72\x75\x6f", - .psize = 3, - .ksize = 16, - } , { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .digest = "\xd2\xa2\x46\xfa\x34\x9b\x68\xa7" - "\x99\x98\xa4\x39\x4f\xf7\xa2\x63", - .psize = 16, - .ksize = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13", - .digest = "\x47\xf5\x1b\x45\x64\x96\x62\x15" - "\xb8\x98\x5c\x63\x05\x5e\xd3\x08", - .tap = { 10, 10 }, - .psize = 20, - .np = 2, - .ksize = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .digest = "\xf5\x4f\x0e\xc8\xd2\xb9\xf3\xd3" - "\x68\x07\x73\x4b\xd5\x28\x3f\xd4", - .psize = 32, - .ksize = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21", - .digest = "\xbe\xcb\xb3\xbc\xcd\xb5\x18\xa3" - "\x06\x77\xd5\x48\x1f\xb6\xb4\xd8", - .tap = { 17, 17 }, - .psize = 34, - .np = 2, - .ksize = 16, - } -}; - -/* - * SHA384 HMAC test vectors from RFC4231 - */ - -#define HMAC_SHA384_TEST_VECTORS 4 - -static struct hash_testvec hmac_sha384_tv_template[] = { - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b", - .ksize = 20, - .plaintext = "Hi There", - .psize = 8, - .digest = "\xaf\xd0\x39\x44\xd8\x48\x95\x62" - "\x6b\x08\x25\xf4\xab\x46\x90\x7f" - "\x15\xf9\xda\xdb\xe4\x10\x1e\xc6" - "\x82\xaa\x03\x4c\x7c\xeb\xc5\x9c" - "\xfa\xea\x9e\xa9\x07\x6e\xde\x7f" - "\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\xaf\x45\xd2\xe3\x76\x48\x40\x31" - "\x61\x7f\x78\xd2\xb5\x8a\x6b\x1b" - "\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47" - "\xe4\x2e\xc3\x73\x63\x22\x44\x5e" - "\x8e\x22\x40\xca\x5e\x69\xe2\xc7" - "\x8b\x32\x39\xec\xfa\xb2\x16\x49", - .np = 4, - .tap = { 7, 7, 7, 7 } - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa", - .ksize = 131, - .plaintext = "Test Using Larger Than Block-Siz" - "e Key - Hash Key First", - .psize = 54, - .digest = "\x4e\xce\x08\x44\x85\x81\x3e\x90" - "\x88\xd2\xc6\x3a\x04\x1b\xc5\xb4" - "\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f" - "\x3c\xd1\x1f\x05\x03\x3a\xc4\xc6" - "\x0c\x2e\xf6\xab\x40\x30\xfe\x82" - "\x96\x24\x8d\xf1\x63\xf4\x49\x52", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa", - .ksize = 131, - .plaintext = "This is a test u" - "sing a larger th" - "an block-size ke" - "y and a larger t" - "han block-size d" - "ata. The key nee" - "ds to be hashed " - "before being use" - "d by the HMAC al" - "gorithm.", - .psize = 152, - .digest = "\x66\x17\x17\x8e\x94\x1f\x02\x0d" - "\x35\x1e\x2f\x25\x4e\x8f\xd3\x2c" - "\x60\x24\x20\xfe\xb0\xb8\xfb\x9a" - "\xdc\xce\xbb\x82\x46\x1e\x99\xc5" - "\xa6\x78\xcc\x31\xe7\x99\x17\x6d" - "\x38\x60\xe6\x11\x0c\x46\x52\x3e", - }, -}; - -/* - * SHA512 HMAC test vectors from RFC4231 - */ - -#define HMAC_SHA512_TEST_VECTORS 4 - -static struct hash_testvec hmac_sha512_tv_template[] = { - { - .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" - "\x0b\x0b\x0b\x0b", - .ksize = 20, - .plaintext = "Hi There", - .psize = 8, - .digest = "\x87\xaa\x7c\xde\xa5\xef\x61\x9d" - "\x4f\xf0\xb4\x24\x1a\x1d\x6c\xb0" - "\x23\x79\xf4\xe2\xce\x4e\xc2\x78" - "\x7a\xd0\xb3\x05\x45\xe1\x7c\xde" - "\xda\xa8\x33\xb7\xd6\xb8\xa7\x02" - "\x03\x8b\x27\x4e\xae\xa3\xf4\xe4" - "\xbe\x9d\x91\x4e\xeb\x61\xf1\x70" - "\x2e\x69\x6c\x20\x3a\x12\x68\x54", - }, { - .key = "Jefe", - .ksize = 4, - .plaintext = "what do ya want for nothing?", - .psize = 28, - .digest = "\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2" - "\xe3\x95\xfb\xe7\x3b\x56\xe0\xa3" - "\x87\xbd\x64\x22\x2e\x83\x1f\xd6" - "\x10\x27\x0c\xd7\xea\x25\x05\x54" - "\x97\x58\xbf\x75\xc0\x5a\x99\x4a" - "\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd" - "\xca\xea\xb1\xa3\x4d\x4a\x6b\x4b" - "\x63\x6e\x07\x0a\x38\xbc\xe7\x37", - .np = 4, - .tap = { 7, 7, 7, 7 } - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa", - .ksize = 131, - .plaintext = "Test Using Large" - "r Than Block-Siz" - "e Key - Hash Key" - " First", - .psize = 54, - .digest = "\x80\xb2\x42\x63\xc7\xc1\xa3\xeb" - "\xb7\x14\x93\xc1\xdd\x7b\xe8\xb4" - "\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1" - "\x12\x1b\x01\x37\x83\xf8\xf3\x52" - "\x6b\x56\xd0\x37\xe0\x5f\x25\x98" - "\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52" - "\x95\xe6\x4f\x73\xf6\x3f\x0a\xec" - "\x8b\x91\x5a\x98\x5d\x78\x65\x98", - }, { - .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" - "\xaa\xaa\xaa", - .ksize = 131, - .plaintext = - "This is a test u" - "sing a larger th" - "an block-size ke" - "y and a larger t" - "han block-size d" - "ata. The key nee" - "ds to be hashed " - "before being use" - "d by the HMAC al" - "gorithm.", - .psize = 152, - .digest = "\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba" - "\xa4\xdf\xa9\xf9\x6e\x5e\x3f\xfd" - "\xde\xbd\x71\xf8\x86\x72\x89\x86" - "\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44" - "\xb6\x02\x2c\xac\x3c\x49\x82\xb1" - "\x0d\x5e\xeb\x55\xc3\xe4\xde\x15" - "\x13\x46\x76\xfb\x6d\xe0\x44\x60" - "\x65\xc9\x74\x40\xfa\x8c\x6a\x58", - }, -}; - /* * DES test vectors. */ -#define DES_ENC_TEST_VECTORS 10 -#define DES_DEC_TEST_VECTORS 4 -#define DES_CBC_ENC_TEST_VECTORS 5 -#define DES_CBC_DEC_TEST_VECTORS 4 -#define DES3_EDE_ENC_TEST_VECTORS 3 -#define DES3_EDE_DEC_TEST_VECTORS 3 -#define DES3_EDE_CBC_ENC_TEST_VECTORS 1 -#define DES3_EDE_CBC_DEC_TEST_VECTORS 1 - -static struct cipher_testvec des_enc_tv_template[] = { - { /* From Applied Cryptography */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7", - .ilen = 8, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", - .rlen = 8, - }, { /* Same key, different plaintext block */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x22\x33\x44\x55\x66\x77\x88\x99", - .ilen = 8, - .result = "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", - .rlen = 8, - }, { /* Sbox test from NBS */ - .key = "\x7c\xa1\x10\x45\x4a\x1a\x6e\x57", - .klen = 8, - .input = "\x01\xa1\xd6\xd0\x39\x77\x67\x42", - .ilen = 8, - .result = "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", - .rlen = 8, - }, { /* Three blocks */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\x22\x33\x44\x55\x66\x77\x88\x99" - "\xca\xfe\xba\xbe\xfe\xed\xbe\xef", - .ilen = 24, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b" - "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90", - .rlen = 24, - }, { /* Weak key */ - .fail = 1, - .wk = 1, - .key = "\x01\x01\x01\x01\x01\x01\x01\x01", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7", - .ilen = 8, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", - .rlen = 8, - }, { /* Two blocks -- for testing encryption across pages */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\x22\x33\x44\x55\x66\x77\x88\x99", - .ilen = 16, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", - .rlen = 16, - .np = 2, - .tap = { 8, 8 } - }, { /* Four blocks -- for testing encryption with chunking */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\x22\x33\x44\x55\x66\x77\x88\x99" - "\xca\xfe\xba\xbe\xfe\xed\xbe\xef" - "\x22\x33\x44\x55\x66\x77\x88\x99", - .ilen = 32, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b" - "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90" - "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", - .rlen = 32, - .np = 3, - .tap = { 14, 10, 8 } - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\x22\x33\x44\x55\x66\x77\x88\x99" - "\xca\xfe\xba\xbe\xfe\xed\xbe\xef", - .ilen = 24, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b" - "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90", - .rlen = 24, - .np = 4, - .tap = { 2, 1, 3, 18 } - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\x22\x33\x44\x55\x66\x77\x88\x99", - .ilen = 16, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", - .rlen = 16, - .np = 5, - .tap = { 2, 2, 2, 2, 8 } - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7", - .ilen = 8, - .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", - .rlen = 8, - .np = 8, - .tap = { 1, 1, 1, 1, 1, 1, 1, 1 } - }, -}; - -static struct cipher_testvec des_dec_tv_template[] = { - { /* From Applied Cryptography */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xe7", - .rlen = 8, - }, { /* Sbox test from NBS */ - .key = "\x7c\xa1\x10\x45\x4a\x1a\x6e\x57", - .klen = 8, - .input = "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", - .ilen = 8, - .result = "\x01\xa1\xd6\xd0\x39\x77\x67\x42", - .rlen = 8, - }, { /* Two blocks, for chunking test */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", - .ilen = 16, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\xa3\x99\x7b\xca\xaf\x69\xa0\xf5", - .rlen = 16, - .np = 2, - .tap = { 8, 8 } - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" - "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", - .ilen = 16, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xe7" - "\xa3\x99\x7b\xca\xaf\x69\xa0\xf5", - .rlen = 16, - .np = 3, - .tap = { 3, 12, 1 } - }, -}; - -static struct cipher_testvec des_cbc_enc_tv_template[] = { - { /* From OpenSSL */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .input = "\x37\x36\x35\x34\x33\x32\x31\x20" - "\x4e\x6f\x77\x20\x69\x73\x20\x74" - "\x68\x65\x20\x74\x69\x6d\x65\x20", - .ilen = 24, - .result = "\xcc\xd1\x73\xff\xab\x20\x39\xf4" - "\xac\xd8\xae\xfd\xdf\xd8\xa1\xeb" - "\x46\x8e\x91\x15\x78\x88\xba\x68", - .rlen = 24, - }, { /* FIPS Pub 81 */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\x12\x34\x56\x78\x90\xab\xcd\xef", - .input = "\x4e\x6f\x77\x20\x69\x73\x20\x74", - .ilen = 8, - .result = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", - .input = "\x68\x65\x20\x74\x69\x6d\x65\x20", - .ilen = 8, - .result = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", - .input = "\x66\x6f\x72\x20\x61\x6c\x6c\x20", - .ilen = 8, - .result = "\x68\x37\x88\x49\x9a\x7c\x05\xf6", - .rlen = 8, - }, { /* Copy of openssl vector for chunk testing */ - /* From OpenSSL */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .input = "\x37\x36\x35\x34\x33\x32\x31\x20" - "\x4e\x6f\x77\x20\x69\x73\x20\x74" - "\x68\x65\x20\x74\x69\x6d\x65\x20", - .ilen = 24, - .result = "\xcc\xd1\x73\xff\xab\x20\x39\xf4" - "\xac\xd8\xae\xfd\xdf\xd8\xa1\xeb" - "\x46\x8e\x91\x15\x78\x88\xba\x68", - .rlen = 24, - .np = 2, - .tap = { 13, 11 } - }, -}; - -static struct cipher_testvec des_cbc_dec_tv_template[] = { - { /* FIPS Pub 81 */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\x12\x34\x56\x78\x90\xab\xcd\xef", - .input = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", - .ilen = 8, - .result = "\x4e\x6f\x77\x20\x69\x73\x20\x74", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", - .input = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", - .ilen = 8, - .result = "\x68\x65\x20\x74\x69\x6d\x65\x20", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", - .input = "\x68\x37\x88\x49\x9a\x7c\x05\xf6", - .ilen = 8, - .result = "\x66\x6f\x72\x20\x61\x6c\x6c\x20", - .rlen = 8, - }, { /* Copy of above, for chunk testing */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .iv = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", - .input = "\x68\x37\x88\x49\x9a\x7c\x05\xf6", - .ilen = 8, - .result = "\x66\x6f\x72\x20\x61\x6c\x6c\x20", - .rlen = 8, - .np = 2, - .tap = { 4, 4 } - }, -}; - -static struct cipher_testvec des3_ede_enc_tv_template[] = { - { /* These are from openssl */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\x55\x55\x55\x55\x55\x55\x55\x55" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 24, - .input = "\x73\x6f\x6d\x65\x64\x61\x74\x61", - .ilen = 8, - .result = "\x18\xd7\x48\xe5\x63\x62\x05\x72", - .rlen = 8, - }, { - .key = "\x03\x52\x02\x07\x67\x20\x82\x17" - "\x86\x02\x87\x66\x59\x08\x21\x98" - "\x64\x05\x6a\xbd\xfe\xa9\x34\x57", - .klen = 24, - .input = "\x73\x71\x75\x69\x67\x67\x6c\x65", - .ilen = 8, - .result = "\xc0\x7d\x2a\x0f\xa5\x66\xfa\x30", - .rlen = 8, - }, { - .key = "\x10\x46\x10\x34\x89\x98\x80\x20" - "\x91\x07\xd0\x15\x89\x19\x01\x01" - "\x19\x07\x92\x10\x98\x1a\x01\x01", - .klen = 24, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 8, - .result = "\xe1\xef\x62\xc3\x32\xfe\x82\x5b", - .rlen = 8, - }, -}; - -static struct cipher_testvec des3_ede_dec_tv_template[] = { - { /* These are from openssl */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\x55\x55\x55\x55\x55\x55\x55\x55" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 24, - .input = "\x18\xd7\x48\xe5\x63\x62\x05\x72", - .ilen = 8, - .result = "\x73\x6f\x6d\x65\x64\x61\x74\x61", - .rlen = 8, - }, { - .key = "\x03\x52\x02\x07\x67\x20\x82\x17" - "\x86\x02\x87\x66\x59\x08\x21\x98" - "\x64\x05\x6a\xbd\xfe\xa9\x34\x57", - .klen = 24, - .input = "\xc0\x7d\x2a\x0f\xa5\x66\xfa\x30", - .ilen = 8, - .result = "\x73\x71\x75\x69\x67\x67\x6c\x65", - .rlen = 8, - }, { - .key = "\x10\x46\x10\x34\x89\x98\x80\x20" - "\x91\x07\xd0\x15\x89\x19\x01\x01" - "\x19\x07\x92\x10\x98\x1a\x01\x01", - .klen = 24, - .input = "\xe1\xef\x62\xc3\x32\xfe\x82\x5b", - .ilen = 8, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 8, - }, -}; - -static struct cipher_testvec des3_ede_cbc_enc_tv_template[] = { - { /* Generated from openssl */ - .key = "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24" - "\x44\x4D\x99\x5A\x12\xD6\x40\xC0" - "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8", - .klen = 24, - .iv = "\x7D\x33\x88\x93\x0F\x93\xB2\x42", - .input = "\x6f\x54\x20\x6f\x61\x4d\x79\x6e" - "\x53\x20\x63\x65\x65\x72\x73\x74" - "\x54\x20\x6f\x6f\x4d\x20\x6e\x61" - "\x20\x79\x65\x53\x72\x63\x74\x65" - "\x20\x73\x6f\x54\x20\x6f\x61\x4d" - "\x79\x6e\x53\x20\x63\x65\x65\x72" - "\x73\x74\x54\x20\x6f\x6f\x4d\x20" - "\x6e\x61\x20\x79\x65\x53\x72\x63" - "\x74\x65\x20\x73\x6f\x54\x20\x6f" - "\x61\x4d\x79\x6e\x53\x20\x63\x65" - "\x65\x72\x73\x74\x54\x20\x6f\x6f" - "\x4d\x20\x6e\x61\x20\x79\x65\x53" - "\x72\x63\x74\x65\x20\x73\x6f\x54" - "\x20\x6f\x61\x4d\x79\x6e\x53\x20" - "\x63\x65\x65\x72\x73\x74\x54\x20" - "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79", - .ilen = 128, - .result = "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4" - "\x67\x17\x21\xc7\x6e\x8a\xd5\x49" - "\x74\xb3\x49\x05\xc5\x1c\xd0\xed" - "\x12\x56\x5c\x53\x96\xb6\x00\x7d" - "\x90\x48\xfc\xf5\x8d\x29\x39\xcc" - "\x8a\xd5\x35\x18\x36\x23\x4e\xd7" - "\x76\xd1\xda\x0c\x94\x67\xbb\x04" - "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea" - "\x22\x64\x47\xaa\x8f\x75\x13\xbf" - "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a" - "\x71\x63\x2e\x89\x7b\x1e\x12\xca" - "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a" - "\xd6\xf9\x21\x31\x62\x44\x45\xa6" - "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc" - "\x9d\xde\xa5\x70\xe9\x42\x45\x8a" - "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19", - .rlen = 128, - }, -}; - -static struct cipher_testvec des3_ede_cbc_dec_tv_template[] = { - { /* Generated from openssl */ - .key = "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24" - "\x44\x4D\x99\x5A\x12\xD6\x40\xC0" - "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8", - .klen = 24, - .iv = "\x7D\x33\x88\x93\x0F\x93\xB2\x42", - .input = "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4" - "\x67\x17\x21\xc7\x6e\x8a\xd5\x49" - "\x74\xb3\x49\x05\xc5\x1c\xd0\xed" - "\x12\x56\x5c\x53\x96\xb6\x00\x7d" - "\x90\x48\xfc\xf5\x8d\x29\x39\xcc" - "\x8a\xd5\x35\x18\x36\x23\x4e\xd7" - "\x76\xd1\xda\x0c\x94\x67\xbb\x04" - "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea" - "\x22\x64\x47\xaa\x8f\x75\x13\xbf" - "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a" - "\x71\x63\x2e\x89\x7b\x1e\x12\xca" - "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a" - "\xd6\xf9\x21\x31\x62\x44\x45\xa6" - "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc" - "\x9d\xde\xa5\x70\xe9\x42\x45\x8a" - "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19", - .ilen = 128, - .result = "\x6f\x54\x20\x6f\x61\x4d\x79\x6e" - "\x53\x20\x63\x65\x65\x72\x73\x74" - "\x54\x20\x6f\x6f\x4d\x20\x6e\x61" - "\x20\x79\x65\x53\x72\x63\x74\x65" - "\x20\x73\x6f\x54\x20\x6f\x61\x4d" - "\x79\x6e\x53\x20\x63\x65\x65\x72" - "\x73\x74\x54\x20\x6f\x6f\x4d\x20" - "\x6e\x61\x20\x79\x65\x53\x72\x63" - "\x74\x65\x20\x73\x6f\x54\x20\x6f" - "\x61\x4d\x79\x6e\x53\x20\x63\x65" - "\x65\x72\x73\x74\x54\x20\x6f\x6f" - "\x4d\x20\x6e\x61\x20\x79\x65\x53" - "\x72\x63\x74\x65\x20\x73\x6f\x54" - "\x20\x6f\x61\x4d\x79\x6e\x53\x20" - "\x63\x65\x65\x72\x73\x74\x54\x20" - "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79", - .rlen = 128, - }, -}; - -/* - * Blowfish test vectors. - */ -#define BF_ENC_TEST_VECTORS 6 -#define BF_DEC_TEST_VECTORS 6 -#define BF_CBC_ENC_TEST_VECTORS 1 -#define BF_CBC_DEC_TEST_VECTORS 1 - -static struct cipher_testvec bf_enc_tv_template[] = { - { /* DES test vectors from OpenSSL */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 8, - .result = "\x4e\xf9\x97\x45\x61\x98\xdd\x78", - .rlen = 8, - }, { - .key = "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .ilen = 8, - .result = "\xa7\x90\x79\x51\x08\xea\x3c\xae", - .rlen = 8, - }, { - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 8, - .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 8, - .result = "\xe8\x7a\x24\x4e\x2c\xc8\x5e\x82", - .rlen = 8, - }, { /* Vary the keylength... */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" - "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f", - .klen = 16, - .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 8, - .result = "\x93\x14\x28\x87\xee\x3b\xe1\x5c", - .rlen = 8, - }, { - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" - "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" - "\x00\x11\x22\x33\x44", - .klen = 21, - .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 8, - .result = "\xe6\xf5\x1e\xd7\x9b\x9d\xb2\x1f", - .rlen = 8, - }, { /* Generated with bf488 */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" - "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x04\x68\x91\x04\xc2\xfd\x3b\x2f" - "\x58\x40\x23\x64\x1a\xba\x61\x76" - "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e" - "\xff\xff\xff\xff\xff\xff\xff\xff", - .klen = 56, - .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 8, - .result = "\xc0\x45\x04\x01\x2e\x4e\x1f\x53", - .rlen = 8, - }, -}; - -static struct cipher_testvec bf_dec_tv_template[] = { - { /* DES test vectors from OpenSSL */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .input = "\x4e\xf9\x97\x45\x61\x98\xdd\x78", - .ilen = 8, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 8, - }, { - .key = "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e", - .klen = 8, - .input = "\xa7\x90\x79\x51\x08\xea\x3c\xae", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .rlen = 8, - }, { - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 8, - .input = "\xe8\x7a\x24\x4e\x2c\xc8\x5e\x82", - .ilen = 8, - .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 8, - }, { /* Vary the keylength... */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" - "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f", - .klen = 16, - .input = "\x93\x14\x28\x87\xee\x3b\xe1\x5c", - .ilen = 8, - .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 8, - }, { - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" - "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" - "\x00\x11\x22\x33\x44", - .klen = 21, - .input = "\xe6\xf5\x1e\xd7\x9b\x9d\xb2\x1f", - .ilen = 8, - .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 8, - }, { /* Generated with bf488, using OpenSSL, Libgcrypt and Nettle */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" - "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x04\x68\x91\x04\xc2\xfd\x3b\x2f" - "\x58\x40\x23\x64\x1a\xba\x61\x76" - "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e" - "\xff\xff\xff\xff\xff\xff\xff\xff", - .klen = 56, - .input = "\xc0\x45\x04\x01\x2e\x4e\x1f\x53", - .ilen = 8, - .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 8, - }, -}; - -static struct cipher_testvec bf_cbc_enc_tv_template[] = { - { /* From OpenSSL */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 16, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .input = "\x37\x36\x35\x34\x33\x32\x31\x20" - "\x4e\x6f\x77\x20\x69\x73\x20\x74" - "\x68\x65\x20\x74\x69\x6d\x65\x20" - "\x66\x6f\x72\x20\x00\x00\x00\x00", - .ilen = 32, - .result = "\x6b\x77\xb4\xd6\x30\x06\xde\xe6" - "\x05\xb1\x56\xe2\x74\x03\x97\x93" - "\x58\xde\xb9\xe7\x15\x46\x16\xd9" - "\x59\xf1\x65\x2b\xd5\xff\x92\xcc", - .rlen = 32, - }, -}; - -static struct cipher_testvec bf_cbc_dec_tv_template[] = { - { /* From OpenSSL */ - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 16, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .input = "\x6b\x77\xb4\xd6\x30\x06\xde\xe6" - "\x05\xb1\x56\xe2\x74\x03\x97\x93" - "\x58\xde\xb9\xe7\x15\x46\x16\xd9" - "\x59\xf1\x65\x2b\xd5\xff\x92\xcc", - .ilen = 32, - .result = "\x37\x36\x35\x34\x33\x32\x31\x20" - "\x4e\x6f\x77\x20\x69\x73\x20\x74" - "\x68\x65\x20\x74\x69\x6d\x65\x20" - "\x66\x6f\x72\x20\x00\x00\x00\x00", - .rlen = 32, - }, -}; - -/* - * Twofish test vectors. - */ -#define TF_ENC_TEST_VECTORS 3 -#define TF_DEC_TEST_VECTORS 3 -#define TF_CBC_ENC_TEST_VECTORS 4 -#define TF_CBC_DEC_TEST_VECTORS 4 - -static struct cipher_testvec tf_enc_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = zeroed_string, - .ilen = 16, - .result = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77", - .klen = 24, - .input = zeroed_string, - .ilen = 16, - .result = "\xcf\xd1\xd2\xe5\xa9\xbe\x9c\xdf" - "\x50\x1f\x13\xb8\x92\xbd\x22\x48", - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .klen = 32, - .input = zeroed_string, - .ilen = 16, - .result = "\x37\x52\x7b\xe0\x05\x23\x34\xb8" - "\x9f\x0c\xfc\xca\xe8\x7c\xfa\x20", - .rlen = 16, - }, -}; +#define DES3_SPEED_VECTORS 1 -static struct cipher_testvec tf_dec_tv_template[] = { +static struct cipher_speed_template des3_speed_template[] = { { - .key = zeroed_string, - .klen = 16, - .input = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77", - .klen = 24, - .input = "\xcf\xd1\xd2\xe5\xa9\xbe\x9c\xdf" - "\x50\x1f\x13\xb8\x92\xbd\x22\x48", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .klen = 32, - .input = "\x37\x52\x7b\xe0\x05\x23\x34\xb8" - "\x9f\x0c\xfc\xca\xe8\x7c\xfa\x20", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, -}; - -static struct cipher_testvec tf_cbc_enc_tv_template[] = { - { /* Generated with Nettle */ - .key = zeroed_string, - .klen = 16, - .iv = zeroed_string, - .input = zeroed_string, - .ilen = 16, - .result = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .iv = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", - .input = zeroed_string, - .ilen = 16, - .result = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" - "\x86\xcb\x08\x6b\x78\x9f\x54\x19", - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .iv = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" - "\x86\xcb\x08\x6b\x78\x9f\x54\x19", - .input = zeroed_string, - .ilen = 16, - .result = "\x05\xef\x8c\x61\xa8\x11\x58\x26" - "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .iv = zeroed_string, - .input = zeroed_string, - .ilen = 48, - .result = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a" - "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" - "\x86\xcb\x08\x6b\x78\x9f\x54\x19" - "\x05\xef\x8c\x61\xa8\x11\x58\x26" - "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", - .rlen = 48, - }, -}; - -static struct cipher_testvec tf_cbc_dec_tv_template[] = { - { /* Reverse of the first four above */ - .key = zeroed_string, - .klen = 16, - .iv = zeroed_string, - .input = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .iv = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", - .input = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" - "\x86\xcb\x08\x6b\x78\x9f\x54\x19", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .iv = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" - "\x86\xcb\x08\x6b\x78\x9f\x54\x19", - .input = "\x05\xef\x8c\x61\xa8\x11\x58\x26" - "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .iv = zeroed_string, - .input = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" - "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a" - "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" - "\x86\xcb\x08\x6b\x78\x9f\x54\x19" - "\x05\xef\x8c\x61\xa8\x11\x58\x26" - "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", - .ilen = 48, - .result = zeroed_string, - .rlen = 48, - }, -}; - -/* - * Serpent test vectors. These are backwards because Serpent writes - * octet sequences in right-to-left mode. - */ -#define SERPENT_ENC_TEST_VECTORS 4 -#define SERPENT_DEC_TEST_VECTORS 4 - -#define TNEPRES_ENC_TEST_VECTORS 4 -#define TNEPRES_DEC_TEST_VECTORS 4 - -static struct cipher_testvec serpent_enc_tv_template[] = { - { - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .ilen = 16, - .result = "\x12\x07\xfc\xce\x9b\xd0\xd6\x47" - "\x6a\xe9\x8f\xbe\xd1\x43\xa0\xe2", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .ilen = 16, - .result = "\x4c\x7d\x8a\x32\x80\x72\xa2\x2c" - "\x82\x3e\x4a\x1f\x3a\xcd\xa1\x6d", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .klen = 32, - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .ilen = 16, - .result = "\xde\x26\x9f\xf8\x33\xe4\x32\xb8" - "\x5b\x2e\x88\xd2\x70\x1c\xe7\x5c", - .rlen = 16, - }, { - .key = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", - .klen = 16, - .input = zeroed_string, - .ilen = 16, - .result = "\xdd\xd2\x6b\x98\xa5\xff\xd8\x2c" - "\x05\x34\x5a\x9d\xad\xbf\xaf\x49", - .rlen = 16, - }, -}; - -static struct cipher_testvec tnepres_enc_tv_template[] = { - { /* KeySize=128, PT=0, I=1 */ - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .key = "\x80\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 16, - .ilen = 16, - .result = "\x49\xaf\xbf\xad\x9d\x5a\x34\x05" - "\x2c\xd8\xff\xa5\x98\x6b\xd2\xdd", - .rlen = 16, - }, { /* KeySize=192, PT=0, I=1 */ - .key = "\x80\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 24, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 16, - .result = "\xe7\x8e\x54\x02\xc7\x19\x55\x68" - "\xac\x36\x78\xf7\xa3\xf6\x0c\x66", - .rlen = 16, - }, { /* KeySize=256, PT=0, I=1 */ - .key = "\x80\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 32, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 16, - .result = "\xab\xed\x96\xe7\x66\xbf\x28\xcb" - "\xc0\xeb\xd2\x1a\x82\xef\x08\x19", - .rlen = 16, - }, { /* KeySize=256, I=257 */ - .key = "\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18" - "\x17\x16\x15\x14\x13\x12\x11\x10" - "\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08" - "\x07\x06\x05\x04\x03\x02\x01\x00", - .klen = 32, - .input = "\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08" - "\x07\x06\x05\x04\x03\x02\x01\x00", - .ilen = 16, - .result = "\x5c\xe7\x1c\x70\xd2\x88\x2e\x5b" - "\xb8\x32\xe4\x33\xf8\x9f\x26\xde", - .rlen = 16, - }, -}; - - -static struct cipher_testvec serpent_dec_tv_template[] = { - { - .input = "\x12\x07\xfc\xce\x9b\xd0\xd6\x47" - "\x6a\xe9\x8f\xbe\xd1\x43\xa0\xe2", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = "\x4c\x7d\x8a\x32\x80\x72\xa2\x2c" - "\x82\x3e\x4a\x1f\x3a\xcd\xa1\x6d", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .klen = 32, - .input = "\xde\x26\x9f\xf8\x33\xe4\x32\xb8" - "\x5b\x2e\x88\xd2\x70\x1c\xe7\x5c", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { - .key = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", - .klen = 16, - .input = "\xdd\xd2\x6b\x98\xa5\xff\xd8\x2c" - "\x05\x34\x5a\x9d\xad\xbf\xaf\x49", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, -}; - -static struct cipher_testvec tnepres_dec_tv_template[] = { - { - .input = "\x41\xcc\x6b\x31\x59\x31\x45\x97" - "\x6d\x6f\xbb\x38\x4b\x37\x21\x28", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = "\xea\xf4\xd7\xfc\xd8\x01\x34\x47" - "\x81\x45\x0b\xfa\x0c\xd6\xad\x6e", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .klen = 32, - .input = "\x64\xa9\x1a\x37\xed\x9f\xe7\x49" - "\xa8\x4e\x76\xd6\xf5\x0d\x78\xee", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { /* KeySize=128, I=121 */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", - .klen = 16, - .input = "\x3d\xda\xbf\xc0\x06\xda\xab\x06" - "\x46\x2a\xf4\xef\x81\x54\x4e\x26", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, -}; - - -/* Cast6 test vectors from RFC 2612 */ -#define CAST6_ENC_TEST_VECTORS 3 -#define CAST6_DEC_TEST_VECTORS 3 - -static struct cipher_testvec cast6_enc_tv_template[] = { - { - .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" - "\x0a\xf7\x56\x47\xf2\x9f\x61\x5d", - .klen = 16, - .input = zeroed_string, - .ilen = 16, - .result = "\xc8\x42\xa0\x89\x72\xb4\x3d\x20" - "\x83\x6c\x91\xd1\xb7\x53\x0f\x6b", - .rlen = 16, - }, { - .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" - "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" - "\xba\xc7\x7a\x77\x17\x94\x28\x63", - .klen = 24, - .input = zeroed_string, - .ilen = 16, - .result = "\x1b\x38\x6c\x02\x10\xdc\xad\xcb" - "\xdd\x0e\x41\xaa\x08\xa7\xa7\xe8", - .rlen = 16, - }, { - .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" - "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" - "\x8d\x7c\x47\xce\x26\x49\x08\x46" - "\x1c\xc1\xb5\x13\x7a\xe6\xb6\x04", - .klen = 32, - .input = zeroed_string, - .ilen = 16, - .result = "\x4f\x6a\x20\x38\x28\x68\x97\xb9" - "\xc9\x87\x01\x36\x55\x33\x17\xfa", - .rlen = 16, - }, -}; - -static struct cipher_testvec cast6_dec_tv_template[] = { - { - .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" - "\x0a\xf7\x56\x47\xf2\x9f\x61\x5d", - .klen = 16, - .input = "\xc8\x42\xa0\x89\x72\xb4\x3d\x20" - "\x83\x6c\x91\xd1\xb7\x53\x0f\x6b", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" - "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" - "\xba\xc7\x7a\x77\x17\x94\x28\x63", - .klen = 24, - .input = "\x1b\x38\x6c\x02\x10\xdc\xad\xcb" - "\xdd\x0e\x41\xaa\x08\xa7\xa7\xe8", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" - "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" - "\x8d\x7c\x47\xce\x26\x49\x08\x46" - "\x1c\xc1\xb5\x13\x7a\xe6\xb6\x04", - .klen = 32, - .input = "\x4f\x6a\x20\x38\x28\x68\x97\xb9" - "\xc9\x87\x01\x36\x55\x33\x17\xfa", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, -}; - - -/* - * AES test vectors. - */ -#define AES_ENC_TEST_VECTORS 3 -#define AES_DEC_TEST_VECTORS 3 -#define AES_CBC_ENC_TEST_VECTORS 4 -#define AES_CBC_DEC_TEST_VECTORS 4 -#define AES_LRW_ENC_TEST_VECTORS 8 -#define AES_LRW_DEC_TEST_VECTORS 8 -#define AES_XTS_ENC_TEST_VECTORS 4 -#define AES_XTS_DEC_TEST_VECTORS 4 -#define AES_CTR_ENC_TEST_VECTORS 7 -#define AES_CTR_DEC_TEST_VECTORS 6 -#define AES_GCM_ENC_TEST_VECTORS 9 -#define AES_GCM_DEC_TEST_VECTORS 8 -#define AES_CCM_ENC_TEST_VECTORS 7 -#define AES_CCM_DEC_TEST_VECTORS 7 - -static struct cipher_testvec aes_enc_tv_template[] = { - { /* From FIPS-197 */ - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .ilen = 16, - .result = "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30" - "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17", - .klen = 24, - .input = "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .ilen = 16, - .result = "\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0" - "\x6e\xaf\x70\xa0\xec\x0d\x71\x91", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .klen = 32, - .input = "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .ilen = 16, - .result = "\x8e\xa2\xb7\xca\x51\x67\x45\xbf" - "\xea\xfc\x49\x90\x4b\x49\x60\x89", - .rlen = 16, - }, -}; - -static struct cipher_testvec aes_dec_tv_template[] = { - { /* From FIPS-197 */ - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30" - "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a", - .ilen = 16, - .result = "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17", - .klen = 24, - .input = "\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0" - "\x6e\xaf\x70\xa0\xec\x0d\x71\x91", - .ilen = 16, - .result = "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .klen = 32, - .input = "\x8e\xa2\xb7\xca\x51\x67\x45\xbf" - "\xea\xfc\x49\x90\x4b\x49\x60\x89", - .ilen = 16, - .result = "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .rlen = 16, - }, -}; - -static struct cipher_testvec aes_cbc_enc_tv_template[] = { - { /* From RFC 3602 */ - .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" - "\x51\x2e\x03\xd5\x34\x12\x00\x06", - .klen = 16, - .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" - "\xb4\x22\xda\x80\x2c\x9f\xac\x41", - .input = "Single block msg", - .ilen = 16, - .result = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" - "\x27\x08\x94\x2d\xbe\x77\x18\x1a", - .rlen = 16, - }, { - .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" - "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", - .klen = 16, - .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" - "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .ilen = 32, - .result = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" - "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" - "\x75\x86\x60\x2d\x25\x3c\xff\xf9" - "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", - .rlen = 32, - }, { /* From NIST SP800-38A */ - .key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" - "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" - "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", - .klen = 24, - .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" - "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" - "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" - "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" - "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" - "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" - "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" - "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", - .ilen = 64, - .result = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" - "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" - "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" - "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" - "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" - "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" - "\x08\xb0\xe2\x79\x88\x59\x88\x81" - "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd", - .rlen = 64, - }, { - .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe" - "\x2b\x73\xae\xf0\x85\x7d\x77\x81" - "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" - "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", - .klen = 32, - .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" - "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" - "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" - "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" - "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" - "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" - "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" - "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", - .ilen = 64, - .result = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" - "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" - "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" - "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" - "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" - "\xa5\x30\xe2\x63\x04\x23\x14\x61" - "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" - "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", - .rlen = 64, - }, -}; - -static struct cipher_testvec aes_cbc_dec_tv_template[] = { - { /* From RFC 3602 */ - .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" - "\x51\x2e\x03\xd5\x34\x12\x00\x06", - .klen = 16, - .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" - "\xb4\x22\xda\x80\x2c\x9f\xac\x41", - .input = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" - "\x27\x08\x94\x2d\xbe\x77\x18\x1a", - .ilen = 16, - .result = "Single block msg", - .rlen = 16, - }, { - .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" - "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", - .klen = 16, - .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" - "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", - .input = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" - "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" - "\x75\x86\x60\x2d\x25\x3c\xff\xf9" - "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", - .ilen = 32, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .rlen = 32, - }, { /* From NIST SP800-38A */ - .key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" - "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" - "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", - .klen = 24, - .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .input = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" - "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" - "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" - "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" - "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" - "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" - "\x08\xb0\xe2\x79\x88\x59\x88\x81" - "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd", - .ilen = 64, - .result = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" - "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" - "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" - "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" - "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" - "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" - "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" - "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", - .rlen = 64, - }, { - .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe" - "\x2b\x73\xae\xf0\x85\x7d\x77\x81" - "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" - "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", - .klen = 32, - .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .input = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" - "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" - "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" - "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" - "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" - "\xa5\x30\xe2\x63\x04\x23\x14\x61" - "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" - "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", - .ilen = 64, - .result = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" - "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" - "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" - "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" - "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" - "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" - "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" - "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", - .rlen = 64, - }, -}; - -static struct cipher_testvec aes_lrw_enc_tv_template[] = { - /* from http://grouper.ieee.org/groups/1619/email/pdf00017.pdf */ - { /* LRW-32-AES 1 */ - .key = "\x45\x62\xac\x25\xf8\x28\x17\x6d" - "\x4c\x26\x84\x14\xb5\x68\x01\x85" - "\x25\x8e\x2a\x05\xe7\x3e\x9d\x03" - "\xee\x5a\x83\x0c\xcc\x09\x4c\x87", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\xf1\xb2\x73\xcd\x65\xa3\xdf\x5f" - "\xe9\x5d\x48\x92\x54\x63\x4e\xb8", - .rlen = 16, - }, { /* LRW-32-AES 2 */ - .key = "\x59\x70\x47\x14\xf5\x57\x47\x8c" - "\xd7\x79\xe8\x0f\x54\x88\x79\x44" - "\x0d\x48\xf0\xb7\xb1\x5a\x53\xea" - "\x1c\xaa\x6b\x29\xc2\xca\xfb\xaf", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x02", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\x00\xc8\x2b\xae\x95\xbb\xcd\xe5" - "\x27\x4f\x07\x69\xb2\x60\xe1\x36", - .rlen = 16, - }, { /* LRW-32-AES 3 */ - .key = "\xd8\x2a\x91\x34\xb2\x6a\x56\x50" - "\x30\xfe\x69\xe2\x37\x7f\x98\x47" - "\xcd\xf9\x0b\x16\x0c\x64\x8f\xb6" - "\xb0\x0d\x0d\x1b\xae\x85\x87\x1f", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x02\x00\x00\x00\x00", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\x76\x32\x21\x83\xed\x8f\xf1\x82" - "\xf9\x59\x62\x03\x69\x0e\x5e\x01", - .rlen = 16, - }, { /* LRW-32-AES 4 */ - .key = "\x0f\x6a\xef\xf8\xd3\xd2\xbb\x15" - "\x25\x83\xf7\x3c\x1f\x01\x28\x74" - "\xca\xc6\xbc\x35\x4d\x4a\x65\x54" - "\x90\xae\x61\xcf\x7b\xae\xbd\xcc" - "\xad\xe4\x94\xc5\x4a\x29\xae\x70", - .klen = 40, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\x9c\x0f\x15\x2f\x55\xa2\xd8\xf0" - "\xd6\x7b\x8f\x9e\x28\x22\xbc\x41", - .rlen = 16, - }, { /* LRW-32-AES 5 */ - .key = "\x8a\xd4\xee\x10\x2f\xbd\x81\xff" - "\xf8\x86\xce\xac\x93\xc5\xad\xc6" - "\xa0\x19\x07\xc0\x9d\xf7\xbb\xdd" - "\x52\x13\xb2\xb7\xf0\xff\x11\xd8" - "\xd6\x08\xd0\xcd\x2e\xb1\x17\x6f", - .klen = 40, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x02\x00\x00\x00\x00", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\xd4\x27\x6a\x7f\x14\x91\x3d\x65" - "\xc8\x60\x48\x02\x87\xe3\x34\x06", - .rlen = 16, - }, { /* LRW-32-AES 6 */ - .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" - "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" - "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" - "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" - "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" - "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", - .klen = 48, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\xbd\x06\xb8\xe1\xdb\x98\x89\x9e" - "\xc4\x98\xe4\x91\xcf\x1c\x70\x2b", - .rlen = 16, - }, { /* LRW-32-AES 7 */ - .key = "\xfb\x76\x15\xb2\x3d\x80\x89\x1d" - "\xd4\x70\x98\x0b\xc7\x95\x84\xc8" - "\xb2\xfb\x64\xce\x60\x97\x87\x8d" - "\x17\xfc\xe4\x5a\x49\xe8\x30\xb7" - "\x6e\x78\x17\xe7\x2d\x5e\x12\xd4" - "\x60\x64\x04\x7a\xf1\x2f\x9e\x0c", - .klen = 48, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x02\x00\x00\x00\x00", - .input = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .ilen = 16, - .result = "\x5b\x90\x8e\xc1\xab\xdd\x67\x5f" - "\x3d\x69\x8a\x95\x53\xc8\x9c\xe5", - .rlen = 16, - }, { -/* http://www.mail-archive.com/stds-p1619@listserv.ieee.org/msg00173.html */ - .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" - "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" - "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" - "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" - "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" - "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", - .klen = 48, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\x05\x11\xb7\x18\xab\xc6\x2d\xac" - "\x70\x5d\xf6\x22\x94\xcd\xe5\x6c" - "\x17\x6b\xf6\x1c\xf0\xf3\x6e\xf8" - "\x50\x38\x1f\x71\x49\xb6\x57\xd6" - "\x8f\xcb\x8d\x6b\xe3\xa6\x29\x90" - "\xfe\x2a\x62\x82\xae\x6d\x8b\xf6" - "\xad\x1e\x9e\x20\x5f\x38\xbe\x04" - "\xda\x10\x8e\xed\xa2\xa4\x87\xab" - "\xda\x6b\xb4\x0c\x75\xba\xd3\x7c" - "\xc9\xac\x42\x31\x95\x7c\xc9\x04" - "\xeb\xd5\x6e\x32\x69\x8a\xdb\xa6" - "\x15\xd7\x3f\x4f\x2f\x66\x69\x03" - "\x9c\x1f\x54\x0f\xde\x1f\xf3\x65" - "\x4c\x96\x12\xed\x7c\x92\x03\x01" - "\x6f\xbc\x35\x93\xac\xf1\x27\xf1" - "\xb4\x96\x82\x5a\x5f\xb0\xa0\x50" - "\x89\xa4\x8e\x66\x44\x85\xcc\xfd" - "\x33\x14\x70\xe3\x96\xb2\xc3\xd3" - "\xbb\x54\x5a\x1a\xf9\x74\xa2\xc5" - "\x2d\x64\x75\xdd\xb4\x54\xe6\x74" - "\x8c\xd3\x9d\x9e\x86\xab\x51\x53" - "\xb7\x93\x3e\x6f\xd0\x4e\x2c\x40" - "\xf6\xa8\x2e\x3e\x9d\xf4\x66\xa5" - "\x76\x12\x73\x44\x1a\x56\xd7\x72" - "\x88\xcd\x21\x8c\x4c\x0f\xfe\xda" - "\x95\xe0\x3a\xa6\xa5\x84\x46\xcd" - "\xd5\x3e\x9d\x3a\xe2\x67\xe6\x60" - "\x1a\xe2\x70\x85\x58\xc2\x1b\x09" - "\xe1\xd7\x2c\xca\xad\xa8\x8f\xf9" - "\xac\xb3\x0e\xdb\xca\x2e\xe2\xb8" - "\x51\x71\xd9\x3c\x6c\xf1\x56\xf8" - "\xea\x9c\xf1\xfb\x0c\xe6\xb7\x10" - "\x1c\xf8\xa9\x7c\xe8\x53\x35\xc1" - "\x90\x3e\x76\x4a\x74\xa4\x21\x2c" - "\xf6\x2c\x4e\x0f\x94\x3a\x88\x2e" - "\x41\x09\x6a\x33\x7d\xf6\xdd\x3f" - "\x8d\x23\x31\x74\x84\xeb\x88\x6e" - "\xcc\xb9\xbc\x22\x83\x19\x07\x22" - "\xa5\x2d\xdf\xa5\xf3\x80\x85\x78" - "\x84\x39\x6a\x6d\x6a\x99\x4f\xa5" - "\x15\xfe\x46\xb0\xe4\x6c\xa5\x41" - "\x3c\xce\x8f\x42\x60\x71\xa7\x75" - "\x08\x40\x65\x8a\x82\xbf\xf5\x43" - "\x71\x96\xa9\x4d\x44\x8a\x20\xbe" - "\xfa\x4d\xbb\xc0\x7d\x31\x96\x65" - "\xe7\x75\xe5\x3e\xfd\x92\x3b\xc9" - "\x55\xbb\x16\x7e\xf7\xc2\x8c\xa4" - "\x40\x1d\xe5\xef\x0e\xdf\xe4\x9a" - "\x62\x73\x65\xfd\x46\x63\x25\x3d" - "\x2b\xaf\xe5\x64\xfe\xa5\x5c\xcf" - "\x24\xf3\xb4\xac\x64\xba\xdf\x4b" - "\xc6\x96\x7d\x81\x2d\x8d\x97\xf7" - "\xc5\x68\x77\x84\x32\x2b\xcc\x85" - "\x74\x96\xf0\x12\x77\x61\xb9\xeb" - "\x71\xaa\x82\xcb\x1c\xdb\x89\xc8" - "\xc6\xb5\xe3\x5c\x7d\x39\x07\x24" - "\xda\x39\x87\x45\xc0\x2b\xbb\x01" - "\xac\xbc\x2a\x5c\x7f\xfc\xe8\xce" - "\x6d\x9c\x6f\xed\xd3\xc1\xa1\xd6" - "\xc5\x55\xa9\x66\x2f\xe1\xc8\x32" - "\xa6\x5d\xa4\x3a\x98\x73\xe8\x45" - "\xa4\xc7\xa8\xb4\xf6\x13\x03\xf6" - "\xe9\x2e\xc4\x29\x0f\x84\xdb\xc4" - "\x21\xc4\xc2\x75\x67\x89\x37\x0a", - .ilen = 512, - .result = "\x1a\x1d\xa9\x30\xad\xf9\x2f\x9b" - "\xb6\x1d\xae\xef\xf0\x2f\xf8\x5a" - "\x39\x3c\xbf\x2a\xb2\x45\xb2\x23" - "\x1b\x63\x3c\xcf\xaa\xbe\xcf\x4e" - "\xfa\xe8\x29\xc2\x20\x68\x2b\x3c" - "\x2e\x8b\xf7\x6e\x25\xbd\xe3\x3d" - "\x66\x27\xd6\xaf\xd6\x64\x3e\xe3" - "\xe8\x58\x46\x97\x39\x51\x07\xde" - "\xcb\x37\xbc\xa9\xc0\x5f\x75\xc3" - "\x0e\x84\x23\x1d\x16\xd4\x1c\x59" - "\x9c\x1a\x02\x55\xab\x3a\x97\x1d" - "\xdf\xdd\xc7\x06\x51\xd7\x70\xae" - "\x23\xc6\x8c\xf5\x1e\xa0\xe5\x82" - "\xb8\xb2\xbf\x04\xa0\x32\x8e\x68" - "\xeb\xaf\x6e\x2d\x94\x22\x2f\xce" - "\x4c\xb5\x59\xe2\xa2\x2f\xa0\x98" - "\x1a\x97\xc6\xd4\xb5\x00\x59\xf2" - "\x84\x14\x72\xb1\x9a\x6e\xa3\x7f" - "\xea\x20\xe7\xcb\x65\x77\x3a\xdf" - "\xc8\x97\x67\x15\xc2\x2a\x27\xcc" - "\x18\x55\xa1\x24\x0b\x24\x24\xaf" - "\x5b\xec\x68\xb8\xc8\xf5\xba\x63" - "\xff\xed\x89\xce\xd5\x3d\x88\xf3" - "\x25\xef\x05\x7c\x3a\xef\xeb\xd8" - "\x7a\x32\x0d\xd1\x1e\x58\x59\x99" - "\x90\x25\xb5\x26\xb0\xe3\x2b\x6c" - "\x4c\xa9\x8b\x84\x4f\x5e\x01\x50" - "\x41\x30\x58\xc5\x62\x74\x52\x1d" - "\x45\x24\x6a\x42\x64\x4f\x97\x1c" - "\xa8\x66\xb5\x6d\x79\xd4\x0d\x48" - "\xc5\x5f\xf3\x90\x32\xdd\xdd\xe1" - "\xe4\xa9\x9f\xfc\xc3\x52\x5a\x46" - "\xe4\x81\x84\x95\x36\x59\x7a\x6b" - "\xaa\xb3\x60\xad\xce\x9f\x9f\x28" - "\xe0\x01\x75\x22\xc4\x4e\xa9\x62" - "\x5c\x62\x0d\x00\xcb\x13\xe8\x43" - "\x72\xd4\x2d\x53\x46\xb5\xd1\x16" - "\x22\x18\xdf\x34\x33\xf5\xd6\x1c" - "\xb8\x79\x78\x97\x94\xff\x72\x13" - "\x4c\x27\xfc\xcb\xbf\x01\x53\xa6" - "\xb4\x50\x6e\xde\xdf\xb5\x43\xa4" - "\x59\xdf\x52\xf9\x7c\xe0\x11\x6f" - "\x2d\x14\x8e\x24\x61\x2c\xe1\x17" - "\xcc\xce\x51\x0c\x19\x8a\x82\x30" - "\x94\xd5\x3d\x6a\x53\x06\x5e\xbd" - "\xb7\xeb\xfa\xfd\x27\x51\xde\x85" - "\x1e\x86\x53\x11\x53\x94\x00\xee" - "\x2b\x8c\x08\x2a\xbf\xdd\xae\x11" - "\xcb\x1e\xa2\x07\x9a\x80\xcf\x62" - "\x9b\x09\xdc\x95\x3c\x96\x8e\xb1" - "\x09\xbd\xe4\xeb\xdb\xca\x70\x7a" - "\x9e\xfa\x31\x18\x45\x3c\x21\x33" - "\xb0\xb3\x2b\xea\xf3\x71\x2d\xe1" - "\x03\xad\x1b\x48\xd4\x67\x27\xf0" - "\x62\xe4\x3d\xfb\x9b\x08\x76\xe7" - "\xdd\x2b\x01\x39\x04\x5a\x58\x7a" - "\xf7\x11\x90\xec\xbd\x51\x5c\x32" - "\x6b\xd7\x35\x39\x02\x6b\xf2\xa6" - "\xd0\x0d\x07\xe1\x06\xc4\x5b\x7d" - "\xe4\x6a\xd7\xee\x15\x1f\x83\xb4" - "\xa3\xa7\x5e\xc3\x90\xb7\xef\xd3" - "\xb7\x4f\xf8\x92\x4c\xb7\x3c\x29" - "\xcd\x7e\x2b\x5d\x43\xea\x42\xe7" - "\x74\x3f\x7d\x58\x88\x75\xde\x3e", - .rlen = 512, - } -}; - -static struct cipher_testvec aes_lrw_dec_tv_template[] = { - /* from http://grouper.ieee.org/groups/1619/email/pdf00017.pdf */ - /* same as enc vectors with input and result reversed */ - { /* LRW-32-AES 1 */ - .key = "\x45\x62\xac\x25\xf8\x28\x17\x6d" - "\x4c\x26\x84\x14\xb5\x68\x01\x85" - "\x25\x8e\x2a\x05\xe7\x3e\x9d\x03" - "\xee\x5a\x83\x0c\xcc\x09\x4c\x87", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\xf1\xb2\x73\xcd\x65\xa3\xdf\x5f" - "\xe9\x5d\x48\x92\x54\x63\x4e\xb8", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { /* LRW-32-AES 2 */ - .key = "\x59\x70\x47\x14\xf5\x57\x47\x8c" - "\xd7\x79\xe8\x0f\x54\x88\x79\x44" - "\x0d\x48\xf0\xb7\xb1\x5a\x53\xea" - "\x1c\xaa\x6b\x29\xc2\xca\xfb\xaf", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x02", - .input = "\x00\xc8\x2b\xae\x95\xbb\xcd\xe5" - "\x27\x4f\x07\x69\xb2\x60\xe1\x36", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { /* LRW-32-AES 3 */ - .key = "\xd8\x2a\x91\x34\xb2\x6a\x56\x50" - "\x30\xfe\x69\xe2\x37\x7f\x98\x47" - "\xcd\xf9\x0b\x16\x0c\x64\x8f\xb6" - "\xb0\x0d\x0d\x1b\xae\x85\x87\x1f", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x02\x00\x00\x00\x00", - .input = "\x76\x32\x21\x83\xed\x8f\xf1\x82" - "\xf9\x59\x62\x03\x69\x0e\x5e\x01", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { /* LRW-32-AES 4 */ - .key = "\x0f\x6a\xef\xf8\xd3\xd2\xbb\x15" - "\x25\x83\xf7\x3c\x1f\x01\x28\x74" - "\xca\xc6\xbc\x35\x4d\x4a\x65\x54" - "\x90\xae\x61\xcf\x7b\xae\xbd\xcc" - "\xad\xe4\x94\xc5\x4a\x29\xae\x70", - .klen = 40, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\x9c\x0f\x15\x2f\x55\xa2\xd8\xf0" - "\xd6\x7b\x8f\x9e\x28\x22\xbc\x41", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { /* LRW-32-AES 5 */ - .key = "\x8a\xd4\xee\x10\x2f\xbd\x81\xff" - "\xf8\x86\xce\xac\x93\xc5\xad\xc6" - "\xa0\x19\x07\xc0\x9d\xf7\xbb\xdd" - "\x52\x13\xb2\xb7\xf0\xff\x11\xd8" - "\xd6\x08\xd0\xcd\x2e\xb1\x17\x6f", - .klen = 40, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x02\x00\x00\x00\x00", - .input = "\xd4\x27\x6a\x7f\x14\x91\x3d\x65" - "\xc8\x60\x48\x02\x87\xe3\x34\x06", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { /* LRW-32-AES 6 */ - .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" - "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" - "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" - "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" - "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" - "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", - .klen = 48, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\xbd\x06\xb8\xe1\xdb\x98\x89\x9e" - "\xc4\x98\xe4\x91\xcf\x1c\x70\x2b", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { /* LRW-32-AES 7 */ - .key = "\xfb\x76\x15\xb2\x3d\x80\x89\x1d" - "\xd4\x70\x98\x0b\xc7\x95\x84\xc8" - "\xb2\xfb\x64\xce\x60\x97\x87\x8d" - "\x17\xfc\xe4\x5a\x49\xe8\x30\xb7" - "\x6e\x78\x17\xe7\x2d\x5e\x12\xd4" - "\x60\x64\x04\x7a\xf1\x2f\x9e\x0c", - .klen = 48, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x02\x00\x00\x00\x00", - .input = "\x5b\x90\x8e\xc1\xab\xdd\x67\x5f" - "\x3d\x69\x8a\x95\x53\xc8\x9c\xe5", - .ilen = 16, - .result = "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x41\x42\x43\x44\x45\x46", - .rlen = 16, - }, { -/* http://www.mail-archive.com/stds-p1619@listserv.ieee.org/msg00173.html */ - .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" - "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" - "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" - "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" - "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" - "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", - .klen = 48, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x01", - .input = "\x1a\x1d\xa9\x30\xad\xf9\x2f\x9b" - "\xb6\x1d\xae\xef\xf0\x2f\xf8\x5a" - "\x39\x3c\xbf\x2a\xb2\x45\xb2\x23" - "\x1b\x63\x3c\xcf\xaa\xbe\xcf\x4e" - "\xfa\xe8\x29\xc2\x20\x68\x2b\x3c" - "\x2e\x8b\xf7\x6e\x25\xbd\xe3\x3d" - "\x66\x27\xd6\xaf\xd6\x64\x3e\xe3" - "\xe8\x58\x46\x97\x39\x51\x07\xde" - "\xcb\x37\xbc\xa9\xc0\x5f\x75\xc3" - "\x0e\x84\x23\x1d\x16\xd4\x1c\x59" - "\x9c\x1a\x02\x55\xab\x3a\x97\x1d" - "\xdf\xdd\xc7\x06\x51\xd7\x70\xae" - "\x23\xc6\x8c\xf5\x1e\xa0\xe5\x82" - "\xb8\xb2\xbf\x04\xa0\x32\x8e\x68" - "\xeb\xaf\x6e\x2d\x94\x22\x2f\xce" - "\x4c\xb5\x59\xe2\xa2\x2f\xa0\x98" - "\x1a\x97\xc6\xd4\xb5\x00\x59\xf2" - "\x84\x14\x72\xb1\x9a\x6e\xa3\x7f" - "\xea\x20\xe7\xcb\x65\x77\x3a\xdf" - "\xc8\x97\x67\x15\xc2\x2a\x27\xcc" - "\x18\x55\xa1\x24\x0b\x24\x24\xaf" - "\x5b\xec\x68\xb8\xc8\xf5\xba\x63" - "\xff\xed\x89\xce\xd5\x3d\x88\xf3" - "\x25\xef\x05\x7c\x3a\xef\xeb\xd8" - "\x7a\x32\x0d\xd1\x1e\x58\x59\x99" - "\x90\x25\xb5\x26\xb0\xe3\x2b\x6c" - "\x4c\xa9\x8b\x84\x4f\x5e\x01\x50" - "\x41\x30\x58\xc5\x62\x74\x52\x1d" - "\x45\x24\x6a\x42\x64\x4f\x97\x1c" - "\xa8\x66\xb5\x6d\x79\xd4\x0d\x48" - "\xc5\x5f\xf3\x90\x32\xdd\xdd\xe1" - "\xe4\xa9\x9f\xfc\xc3\x52\x5a\x46" - "\xe4\x81\x84\x95\x36\x59\x7a\x6b" - "\xaa\xb3\x60\xad\xce\x9f\x9f\x28" - "\xe0\x01\x75\x22\xc4\x4e\xa9\x62" - "\x5c\x62\x0d\x00\xcb\x13\xe8\x43" - "\x72\xd4\x2d\x53\x46\xb5\xd1\x16" - "\x22\x18\xdf\x34\x33\xf5\xd6\x1c" - "\xb8\x79\x78\x97\x94\xff\x72\x13" - "\x4c\x27\xfc\xcb\xbf\x01\x53\xa6" - "\xb4\x50\x6e\xde\xdf\xb5\x43\xa4" - "\x59\xdf\x52\xf9\x7c\xe0\x11\x6f" - "\x2d\x14\x8e\x24\x61\x2c\xe1\x17" - "\xcc\xce\x51\x0c\x19\x8a\x82\x30" - "\x94\xd5\x3d\x6a\x53\x06\x5e\xbd" - "\xb7\xeb\xfa\xfd\x27\x51\xde\x85" - "\x1e\x86\x53\x11\x53\x94\x00\xee" - "\x2b\x8c\x08\x2a\xbf\xdd\xae\x11" - "\xcb\x1e\xa2\x07\x9a\x80\xcf\x62" - "\x9b\x09\xdc\x95\x3c\x96\x8e\xb1" - "\x09\xbd\xe4\xeb\xdb\xca\x70\x7a" - "\x9e\xfa\x31\x18\x45\x3c\x21\x33" - "\xb0\xb3\x2b\xea\xf3\x71\x2d\xe1" - "\x03\xad\x1b\x48\xd4\x67\x27\xf0" - "\x62\xe4\x3d\xfb\x9b\x08\x76\xe7" - "\xdd\x2b\x01\x39\x04\x5a\x58\x7a" - "\xf7\x11\x90\xec\xbd\x51\x5c\x32" - "\x6b\xd7\x35\x39\x02\x6b\xf2\xa6" - "\xd0\x0d\x07\xe1\x06\xc4\x5b\x7d" - "\xe4\x6a\xd7\xee\x15\x1f\x83\xb4" - "\xa3\xa7\x5e\xc3\x90\xb7\xef\xd3" - "\xb7\x4f\xf8\x92\x4c\xb7\x3c\x29" - "\xcd\x7e\x2b\x5d\x43\xea\x42\xe7" - "\x74\x3f\x7d\x58\x88\x75\xde\x3e", - .ilen = 512, - .result = "\x05\x11\xb7\x18\xab\xc6\x2d\xac" - "\x70\x5d\xf6\x22\x94\xcd\xe5\x6c" - "\x17\x6b\xf6\x1c\xf0\xf3\x6e\xf8" - "\x50\x38\x1f\x71\x49\xb6\x57\xd6" - "\x8f\xcb\x8d\x6b\xe3\xa6\x29\x90" - "\xfe\x2a\x62\x82\xae\x6d\x8b\xf6" - "\xad\x1e\x9e\x20\x5f\x38\xbe\x04" - "\xda\x10\x8e\xed\xa2\xa4\x87\xab" - "\xda\x6b\xb4\x0c\x75\xba\xd3\x7c" - "\xc9\xac\x42\x31\x95\x7c\xc9\x04" - "\xeb\xd5\x6e\x32\x69\x8a\xdb\xa6" - "\x15\xd7\x3f\x4f\x2f\x66\x69\x03" - "\x9c\x1f\x54\x0f\xde\x1f\xf3\x65" - "\x4c\x96\x12\xed\x7c\x92\x03\x01" - "\x6f\xbc\x35\x93\xac\xf1\x27\xf1" - "\xb4\x96\x82\x5a\x5f\xb0\xa0\x50" - "\x89\xa4\x8e\x66\x44\x85\xcc\xfd" - "\x33\x14\x70\xe3\x96\xb2\xc3\xd3" - "\xbb\x54\x5a\x1a\xf9\x74\xa2\xc5" - "\x2d\x64\x75\xdd\xb4\x54\xe6\x74" - "\x8c\xd3\x9d\x9e\x86\xab\x51\x53" - "\xb7\x93\x3e\x6f\xd0\x4e\x2c\x40" - "\xf6\xa8\x2e\x3e\x9d\xf4\x66\xa5" - "\x76\x12\x73\x44\x1a\x56\xd7\x72" - "\x88\xcd\x21\x8c\x4c\x0f\xfe\xda" - "\x95\xe0\x3a\xa6\xa5\x84\x46\xcd" - "\xd5\x3e\x9d\x3a\xe2\x67\xe6\x60" - "\x1a\xe2\x70\x85\x58\xc2\x1b\x09" - "\xe1\xd7\x2c\xca\xad\xa8\x8f\xf9" - "\xac\xb3\x0e\xdb\xca\x2e\xe2\xb8" - "\x51\x71\xd9\x3c\x6c\xf1\x56\xf8" - "\xea\x9c\xf1\xfb\x0c\xe6\xb7\x10" - "\x1c\xf8\xa9\x7c\xe8\x53\x35\xc1" - "\x90\x3e\x76\x4a\x74\xa4\x21\x2c" - "\xf6\x2c\x4e\x0f\x94\x3a\x88\x2e" - "\x41\x09\x6a\x33\x7d\xf6\xdd\x3f" - "\x8d\x23\x31\x74\x84\xeb\x88\x6e" - "\xcc\xb9\xbc\x22\x83\x19\x07\x22" - "\xa5\x2d\xdf\xa5\xf3\x80\x85\x78" - "\x84\x39\x6a\x6d\x6a\x99\x4f\xa5" - "\x15\xfe\x46\xb0\xe4\x6c\xa5\x41" - "\x3c\xce\x8f\x42\x60\x71\xa7\x75" - "\x08\x40\x65\x8a\x82\xbf\xf5\x43" - "\x71\x96\xa9\x4d\x44\x8a\x20\xbe" - "\xfa\x4d\xbb\xc0\x7d\x31\x96\x65" - "\xe7\x75\xe5\x3e\xfd\x92\x3b\xc9" - "\x55\xbb\x16\x7e\xf7\xc2\x8c\xa4" - "\x40\x1d\xe5\xef\x0e\xdf\xe4\x9a" - "\x62\x73\x65\xfd\x46\x63\x25\x3d" - "\x2b\xaf\xe5\x64\xfe\xa5\x5c\xcf" - "\x24\xf3\xb4\xac\x64\xba\xdf\x4b" - "\xc6\x96\x7d\x81\x2d\x8d\x97\xf7" - "\xc5\x68\x77\x84\x32\x2b\xcc\x85" - "\x74\x96\xf0\x12\x77\x61\xb9\xeb" - "\x71\xaa\x82\xcb\x1c\xdb\x89\xc8" - "\xc6\xb5\xe3\x5c\x7d\x39\x07\x24" - "\xda\x39\x87\x45\xc0\x2b\xbb\x01" - "\xac\xbc\x2a\x5c\x7f\xfc\xe8\xce" - "\x6d\x9c\x6f\xed\xd3\xc1\xa1\xd6" - "\xc5\x55\xa9\x66\x2f\xe1\xc8\x32" - "\xa6\x5d\xa4\x3a\x98\x73\xe8\x45" - "\xa4\xc7\xa8\xb4\xf6\x13\x03\xf6" - "\xe9\x2e\xc4\x29\x0f\x84\xdb\xc4" - "\x21\xc4\xc2\x75\x67\x89\x37\x0a", - .rlen = 512, - } -}; - -static struct cipher_testvec aes_xts_enc_tv_template[] = { - /* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf */ - { /* XTS-AES 1 */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 32, - .result = "\x91\x7c\xf6\x9e\xbd\x68\xb2\xec" - "\x9b\x9f\xe9\xa3\xea\xdd\xa6\x92" - "\xcd\x43\xd2\xf5\x95\x98\xed\x85" - "\x8c\x02\xc2\x65\x2f\xbf\x92\x2e", - .rlen = 32, - }, { /* XTS-AES 2 */ - .key = "\x11\x11\x11\x11\x11\x11\x11\x11" - "\x11\x11\x11\x11\x11\x11\x11\x11" - "\x22\x22\x22\x22\x22\x22\x22\x22" - "\x22\x22\x22\x22\x22\x22\x22\x22", - .klen = 32, - .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44", - .ilen = 32, - .result = "\xc4\x54\x18\x5e\x6a\x16\x93\x6e" - "\x39\x33\x40\x38\xac\xef\x83\x8b" - "\xfb\x18\x6f\xff\x74\x80\xad\xc4" - "\x28\x93\x82\xec\xd6\xd3\x94\xf0", - .rlen = 32, - }, { /* XTS-AES 3 */ - .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" - "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" - "\x22\x22\x22\x22\x22\x22\x22\x22" - "\x22\x22\x22\x22\x22\x22\x22\x22", - .klen = 32, - .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44", - .ilen = 32, - .result = "\xaf\x85\x33\x6b\x59\x7a\xfc\x1a" - "\x90\x0b\x2e\xb2\x1e\xc9\x49\xd2" - "\x92\xdf\x4c\x04\x7e\x0b\x21\x53" - "\x21\x86\xa5\x97\x1a\x22\x7a\x89", - .rlen = 32, - }, { /* XTS-AES 4 */ - .key = "\x27\x18\x28\x18\x28\x45\x90\x45" - "\x23\x53\x60\x28\x74\x71\x35\x26" - "\x31\x41\x59\x26\x53\x58\x97\x93" - "\x23\x84\x62\x64\x33\x83\x27\x95", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" - "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" - "\x40\x41\x42\x43\x44\x45\x46\x47" - "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" - "\x50\x51\x52\x53\x54\x55\x56\x57" - "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" - "\x60\x61\x62\x63\x64\x65\x66\x67" - "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" - "\x70\x71\x72\x73\x74\x75\x76\x77" - "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" - "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" - "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" - "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" - "\x40\x41\x42\x43\x44\x45\x46\x47" - "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" - "\x50\x51\x52\x53\x54\x55\x56\x57" - "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" - "\x60\x61\x62\x63\x64\x65\x66\x67" - "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" - "\x70\x71\x72\x73\x74\x75\x76\x77" - "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" - "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" - "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", - .ilen = 512, - .result = "\x27\xa7\x47\x9b\xef\xa1\xd4\x76" - "\x48\x9f\x30\x8c\xd4\xcf\xa6\xe2" - "\xa9\x6e\x4b\xbe\x32\x08\xff\x25" - "\x28\x7d\xd3\x81\x96\x16\xe8\x9c" - "\xc7\x8c\xf7\xf5\xe5\x43\x44\x5f" - "\x83\x33\xd8\xfa\x7f\x56\x00\x00" - "\x05\x27\x9f\xa5\xd8\xb5\xe4\xad" - "\x40\xe7\x36\xdd\xb4\xd3\x54\x12" - "\x32\x80\x63\xfd\x2a\xab\x53\xe5" - "\xea\x1e\x0a\x9f\x33\x25\x00\xa5" - "\xdf\x94\x87\xd0\x7a\x5c\x92\xcc" - "\x51\x2c\x88\x66\xc7\xe8\x60\xce" - "\x93\xfd\xf1\x66\xa2\x49\x12\xb4" - "\x22\x97\x61\x46\xae\x20\xce\x84" - "\x6b\xb7\xdc\x9b\xa9\x4a\x76\x7a" - "\xae\xf2\x0c\x0d\x61\xad\x02\x65" - "\x5e\xa9\x2d\xc4\xc4\xe4\x1a\x89" - "\x52\xc6\x51\xd3\x31\x74\xbe\x51" - "\xa1\x0c\x42\x11\x10\xe6\xd8\x15" - "\x88\xed\xe8\x21\x03\xa2\x52\xd8" - "\xa7\x50\xe8\x76\x8d\xef\xff\xed" - "\x91\x22\x81\x0a\xae\xb9\x9f\x91" - "\x72\xaf\x82\xb6\x04\xdc\x4b\x8e" - "\x51\xbc\xb0\x82\x35\xa6\xf4\x34" - "\x13\x32\xe4\xca\x60\x48\x2a\x4b" - "\xa1\xa0\x3b\x3e\x65\x00\x8f\xc5" - "\xda\x76\xb7\x0b\xf1\x69\x0d\xb4" - "\xea\xe2\x9c\x5f\x1b\xad\xd0\x3c" - "\x5c\xcf\x2a\x55\xd7\x05\xdd\xcd" - "\x86\xd4\x49\x51\x1c\xeb\x7e\xc3" - "\x0b\xf1\x2b\x1f\xa3\x5b\x91\x3f" - "\x9f\x74\x7a\x8a\xfd\x1b\x13\x0e" - "\x94\xbf\xf9\x4e\xff\xd0\x1a\x91" - "\x73\x5c\xa1\x72\x6a\xcd\x0b\x19" - "\x7c\x4e\x5b\x03\x39\x36\x97\xe1" - "\x26\x82\x6f\xb6\xbb\xde\x8e\xcc" - "\x1e\x08\x29\x85\x16\xe2\xc9\xed" - "\x03\xff\x3c\x1b\x78\x60\xf6\xde" - "\x76\xd4\xce\xcd\x94\xc8\x11\x98" - "\x55\xef\x52\x97\xca\x67\xe9\xf3" - "\xe7\xff\x72\xb1\xe9\x97\x85\xca" - "\x0a\x7e\x77\x20\xc5\xb3\x6d\xc6" - "\xd7\x2c\xac\x95\x74\xc8\xcb\xbc" - "\x2f\x80\x1e\x23\xe5\x6f\xd3\x44" - "\xb0\x7f\x22\x15\x4b\xeb\xa0\xf0" - "\x8c\xe8\x89\x1e\x64\x3e\xd9\x95" - "\xc9\x4d\x9a\x69\xc9\xf1\xb5\xf4" - "\x99\x02\x7a\x78\x57\x2a\xee\xbd" - "\x74\xd2\x0c\xc3\x98\x81\xc2\x13" - "\xee\x77\x0b\x10\x10\xe4\xbe\xa7" - "\x18\x84\x69\x77\xae\x11\x9f\x7a" - "\x02\x3a\xb5\x8c\xca\x0a\xd7\x52" - "\xaf\xe6\x56\xbb\x3c\x17\x25\x6a" - "\x9f\x6e\x9b\xf1\x9f\xdd\x5a\x38" - "\xfc\x82\xbb\xe8\x72\xc5\x53\x9e" - "\xdb\x60\x9e\xf4\xf7\x9c\x20\x3e" - "\xbb\x14\x0f\x2e\x58\x3c\xb2\xad" - "\x15\xb4\xaa\x5b\x65\x50\x16\xa8" - "\x44\x92\x77\xdb\xd4\x77\xef\x2c" - "\x8d\x6c\x01\x7d\xb7\x38\xb1\x8d" - "\xeb\x4a\x42\x7d\x19\x23\xce\x3f" - "\xf2\x62\x73\x57\x79\xa4\x18\xf2" - "\x0a\x28\x2d\xf9\x20\x14\x7b\xea" - "\xbe\x42\x1e\xe5\x31\x9d\x05\x68", - .rlen = 512, - } -}; - -static struct cipher_testvec aes_xts_dec_tv_template[] = { - /* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf */ - { /* XTS-AES 1 */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x91\x7c\xf6\x9e\xbd\x68\xb2\xec" - "\x9b\x9f\xe9\xa3\xea\xdd\xa6\x92" - "\xcd\x43\xd2\xf5\x95\x98\xed\x85" - "\x8c\x02\xc2\x65\x2f\xbf\x92\x2e", - .ilen = 32, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 32, - }, { /* XTS-AES 2 */ - .key = "\x11\x11\x11\x11\x11\x11\x11\x11" - "\x11\x11\x11\x11\x11\x11\x11\x11" - "\x22\x22\x22\x22\x22\x22\x22\x22" - "\x22\x22\x22\x22\x22\x22\x22\x22", - .klen = 32, - .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\xc4\x54\x18\x5e\x6a\x16\x93\x6e" - "\x39\x33\x40\x38\xac\xef\x83\x8b" - "\xfb\x18\x6f\xff\x74\x80\xad\xc4" - "\x28\x93\x82\xec\xd6\xd3\x94\xf0", - .ilen = 32, - .result = "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44", - .rlen = 32, - }, { /* XTS-AES 3 */ - .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" - "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" - "\x22\x22\x22\x22\x22\x22\x22\x22" - "\x22\x22\x22\x22\x22\x22\x22\x22", - .klen = 32, - .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\xaf\x85\x33\x6b\x59\x7a\xfc\x1a" - "\x90\x0b\x2e\xb2\x1e\xc9\x49\xd2" - "\x92\xdf\x4c\x04\x7e\x0b\x21\x53" - "\x21\x86\xa5\x97\x1a\x22\x7a\x89", - .ilen = 32, - .result = "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44" - "\x44\x44\x44\x44\x44\x44\x44\x44", - .rlen = 32, - }, { /* XTS-AES 4 */ - .key = "\x27\x18\x28\x18\x28\x45\x90\x45" - "\x23\x53\x60\x28\x74\x71\x35\x26" - "\x31\x41\x59\x26\x53\x58\x97\x93" - "\x23\x84\x62\x64\x33\x83\x27\x95", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x27\xa7\x47\x9b\xef\xa1\xd4\x76" - "\x48\x9f\x30\x8c\xd4\xcf\xa6\xe2" - "\xa9\x6e\x4b\xbe\x32\x08\xff\x25" - "\x28\x7d\xd3\x81\x96\x16\xe8\x9c" - "\xc7\x8c\xf7\xf5\xe5\x43\x44\x5f" - "\x83\x33\xd8\xfa\x7f\x56\x00\x00" - "\x05\x27\x9f\xa5\xd8\xb5\xe4\xad" - "\x40\xe7\x36\xdd\xb4\xd3\x54\x12" - "\x32\x80\x63\xfd\x2a\xab\x53\xe5" - "\xea\x1e\x0a\x9f\x33\x25\x00\xa5" - "\xdf\x94\x87\xd0\x7a\x5c\x92\xcc" - "\x51\x2c\x88\x66\xc7\xe8\x60\xce" - "\x93\xfd\xf1\x66\xa2\x49\x12\xb4" - "\x22\x97\x61\x46\xae\x20\xce\x84" - "\x6b\xb7\xdc\x9b\xa9\x4a\x76\x7a" - "\xae\xf2\x0c\x0d\x61\xad\x02\x65" - "\x5e\xa9\x2d\xc4\xc4\xe4\x1a\x89" - "\x52\xc6\x51\xd3\x31\x74\xbe\x51" - "\xa1\x0c\x42\x11\x10\xe6\xd8\x15" - "\x88\xed\xe8\x21\x03\xa2\x52\xd8" - "\xa7\x50\xe8\x76\x8d\xef\xff\xed" - "\x91\x22\x81\x0a\xae\xb9\x9f\x91" - "\x72\xaf\x82\xb6\x04\xdc\x4b\x8e" - "\x51\xbc\xb0\x82\x35\xa6\xf4\x34" - "\x13\x32\xe4\xca\x60\x48\x2a\x4b" - "\xa1\xa0\x3b\x3e\x65\x00\x8f\xc5" - "\xda\x76\xb7\x0b\xf1\x69\x0d\xb4" - "\xea\xe2\x9c\x5f\x1b\xad\xd0\x3c" - "\x5c\xcf\x2a\x55\xd7\x05\xdd\xcd" - "\x86\xd4\x49\x51\x1c\xeb\x7e\xc3" - "\x0b\xf1\x2b\x1f\xa3\x5b\x91\x3f" - "\x9f\x74\x7a\x8a\xfd\x1b\x13\x0e" - "\x94\xbf\xf9\x4e\xff\xd0\x1a\x91" - "\x73\x5c\xa1\x72\x6a\xcd\x0b\x19" - "\x7c\x4e\x5b\x03\x39\x36\x97\xe1" - "\x26\x82\x6f\xb6\xbb\xde\x8e\xcc" - "\x1e\x08\x29\x85\x16\xe2\xc9\xed" - "\x03\xff\x3c\x1b\x78\x60\xf6\xde" - "\x76\xd4\xce\xcd\x94\xc8\x11\x98" - "\x55\xef\x52\x97\xca\x67\xe9\xf3" - "\xe7\xff\x72\xb1\xe9\x97\x85\xca" - "\x0a\x7e\x77\x20\xc5\xb3\x6d\xc6" - "\xd7\x2c\xac\x95\x74\xc8\xcb\xbc" - "\x2f\x80\x1e\x23\xe5\x6f\xd3\x44" - "\xb0\x7f\x22\x15\x4b\xeb\xa0\xf0" - "\x8c\xe8\x89\x1e\x64\x3e\xd9\x95" - "\xc9\x4d\x9a\x69\xc9\xf1\xb5\xf4" - "\x99\x02\x7a\x78\x57\x2a\xee\xbd" - "\x74\xd2\x0c\xc3\x98\x81\xc2\x13" - "\xee\x77\x0b\x10\x10\xe4\xbe\xa7" - "\x18\x84\x69\x77\xae\x11\x9f\x7a" - "\x02\x3a\xb5\x8c\xca\x0a\xd7\x52" - "\xaf\xe6\x56\xbb\x3c\x17\x25\x6a" - "\x9f\x6e\x9b\xf1\x9f\xdd\x5a\x38" - "\xfc\x82\xbb\xe8\x72\xc5\x53\x9e" - "\xdb\x60\x9e\xf4\xf7\x9c\x20\x3e" - "\xbb\x14\x0f\x2e\x58\x3c\xb2\xad" - "\x15\xb4\xaa\x5b\x65\x50\x16\xa8" - "\x44\x92\x77\xdb\xd4\x77\xef\x2c" - "\x8d\x6c\x01\x7d\xb7\x38\xb1\x8d" - "\xeb\x4a\x42\x7d\x19\x23\xce\x3f" - "\xf2\x62\x73\x57\x79\xa4\x18\xf2" - "\x0a\x28\x2d\xf9\x20\x14\x7b\xea" - "\xbe\x42\x1e\xe5\x31\x9d\x05\x68", - .ilen = 512, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" - "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" - "\x40\x41\x42\x43\x44\x45\x46\x47" - "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" - "\x50\x51\x52\x53\x54\x55\x56\x57" - "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" - "\x60\x61\x62\x63\x64\x65\x66\x67" - "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" - "\x70\x71\x72\x73\x74\x75\x76\x77" - "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" - "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" - "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" - "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" - "\x40\x41\x42\x43\x44\x45\x46\x47" - "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" - "\x50\x51\x52\x53\x54\x55\x56\x57" - "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" - "\x60\x61\x62\x63\x64\x65\x66\x67" - "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" - "\x70\x71\x72\x73\x74\x75\x76\x77" - "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" - "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" - "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", - .rlen = 512, - } -}; - - -static struct cipher_testvec aes_ctr_enc_tv_template[] = { - { /* From RFC 3686 */ - .key = "\xae\x68\x52\xf8\x12\x10\x67\xcc" - "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" - "\x00\x00\x00\x30", - .klen = 20, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "Single block msg", - .ilen = 16, - .result = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" - "\x2d\x61\x75\xa3\x26\x13\x11\xb8", - .rlen = 16, - }, { - .key = "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" - "\x43\xd6\xce\x1f\x32\x53\x91\x63" - "\x00\x6c\xb6\xdb", - .klen = 20, - .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .ilen = 32, - .result = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" - "\x79\x0d\x41\xee\x8e\xda\xd3\x88" - "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" - "\xfc\xe6\x30\xdf\x91\x41\xbe\x28", - .rlen = 32, - }, { - .key = "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" - "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" - "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" - "\x00\x00\x00\x48", - .klen = 28, - .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", - .input = "Single block msg", - .ilen = 16, - .result = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" - "\x4e\x79\x35\xa0\x03\xcb\xe9\x28", - .rlen = 16, - }, { - .key = "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" - "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" - "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" - "\x00\x96\xb0\x3b", - .klen = 28, - .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .ilen = 32, - .result = "\x45\x32\x43\xfc\x60\x9b\x23\x32" - "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" - "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" - "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00", - .rlen = 32, - }, { - .key = "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" - "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" - "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" - "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" - "\x00\x00\x00\x60", - .klen = 36, - .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", - .input = "Single block msg", - .ilen = 16, - .result = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" - "\x56\x08\x63\xdc\x71\xe3\xe0\xc0", - .rlen = 16, - }, { - .key = "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" - "\x07\x96\x36\x58\x79\xef\xf8\x86" - "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" - "\x4b\x50\x59\x0c\x87\xa2\x38\x84" - "\x00\xfa\xac\x24", - .klen = 36, - .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .ilen = 32, - .result = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" - "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" - "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" - "\x55\x30\x83\x1d\x93\x44\xaf\x1c", - .rlen = 32, - }, { - // generated using Crypto++ - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x00\x00\x00\x00", - .klen = 32 + 4, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" - "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" - "\x40\x41\x42\x43\x44\x45\x46\x47" - "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" - "\x50\x51\x52\x53\x54\x55\x56\x57" - "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" - "\x60\x61\x62\x63\x64\x65\x66\x67" - "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" - "\x70\x71\x72\x73\x74\x75\x76\x77" - "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" - "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" - "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - "\x00\x03\x06\x09\x0c\x0f\x12\x15" - "\x18\x1b\x1e\x21\x24\x27\x2a\x2d" - "\x30\x33\x36\x39\x3c\x3f\x42\x45" - "\x48\x4b\x4e\x51\x54\x57\x5a\x5d" - "\x60\x63\x66\x69\x6c\x6f\x72\x75" - "\x78\x7b\x7e\x81\x84\x87\x8a\x8d" - "\x90\x93\x96\x99\x9c\x9f\xa2\xa5" - "\xa8\xab\xae\xb1\xb4\xb7\xba\xbd" - "\xc0\xc3\xc6\xc9\xcc\xcf\xd2\xd5" - "\xd8\xdb\xde\xe1\xe4\xe7\xea\xed" - "\xf0\xf3\xf6\xf9\xfc\xff\x02\x05" - "\x08\x0b\x0e\x11\x14\x17\x1a\x1d" - "\x20\x23\x26\x29\x2c\x2f\x32\x35" - "\x38\x3b\x3e\x41\x44\x47\x4a\x4d" - "\x50\x53\x56\x59\x5c\x5f\x62\x65" - "\x68\x6b\x6e\x71\x74\x77\x7a\x7d" - "\x80\x83\x86\x89\x8c\x8f\x92\x95" - "\x98\x9b\x9e\xa1\xa4\xa7\xaa\xad" - "\xb0\xb3\xb6\xb9\xbc\xbf\xc2\xc5" - "\xc8\xcb\xce\xd1\xd4\xd7\xda\xdd" - "\xe0\xe3\xe6\xe9\xec\xef\xf2\xf5" - "\xf8\xfb\xfe\x01\x04\x07\x0a\x0d" - "\x10\x13\x16\x19\x1c\x1f\x22\x25" - "\x28\x2b\x2e\x31\x34\x37\x3a\x3d" - "\x40\x43\x46\x49\x4c\x4f\x52\x55" - "\x58\x5b\x5e\x61\x64\x67\x6a\x6d" - "\x70\x73\x76\x79\x7c\x7f\x82\x85" - "\x88\x8b\x8e\x91\x94\x97\x9a\x9d" - "\xa0\xa3\xa6\xa9\xac\xaf\xb2\xb5" - "\xb8\xbb\xbe\xc1\xc4\xc7\xca\xcd" - "\xd0\xd3\xd6\xd9\xdc\xdf\xe2\xe5" - "\xe8\xeb\xee\xf1\xf4\xf7\xfa\xfd" - "\x00\x05\x0a\x0f\x14\x19\x1e\x23" - "\x28\x2d\x32\x37\x3c\x41\x46\x4b" - "\x50\x55\x5a\x5f\x64\x69\x6e\x73" - "\x78\x7d\x82\x87\x8c\x91\x96\x9b" - "\xa0\xa5\xaa\xaf\xb4\xb9\xbe\xc3" - "\xc8\xcd\xd2\xd7\xdc\xe1\xe6\xeb" - "\xf0\xf5\xfa\xff\x04\x09\x0e\x13" - "\x18\x1d\x22\x27\x2c\x31\x36\x3b" - "\x40\x45\x4a\x4f\x54\x59\x5e\x63" - "\x68\x6d\x72\x77\x7c\x81\x86\x8b" - "\x90\x95\x9a\x9f\xa4\xa9\xae\xb3" - "\xb8\xbd\xc2\xc7\xcc\xd1\xd6\xdb" - "\xe0\xe5\xea\xef\xf4\xf9\xfe\x03" - "\x08\x0d\x12\x17\x1c\x21\x26\x2b" - "\x30\x35\x3a\x3f\x44\x49\x4e\x53" - "\x58\x5d\x62\x67\x6c\x71\x76\x7b" - "\x80\x85\x8a\x8f\x94\x99\x9e\xa3" - "\xa8\xad\xb2\xb7\xbc\xc1\xc6\xcb" - "\xd0\xd5\xda\xdf\xe4\xe9\xee\xf3" - "\xf8\xfd\x02\x07\x0c\x11\x16\x1b" - "\x20\x25\x2a\x2f\x34\x39\x3e\x43" - "\x48\x4d\x52\x57\x5c\x61\x66\x6b" - "\x70\x75\x7a\x7f\x84\x89\x8e\x93" - "\x98\x9d\xa2\xa7\xac\xb1\xb6\xbb" - "\xc0\xc5\xca\xcf\xd4\xd9\xde\xe3" - "\xe8\xed\xf2\xf7\xfc\x01\x06\x0b" - "\x10\x15\x1a\x1f\x24\x29\x2e\x33" - "\x38\x3d\x42\x47\x4c\x51\x56\x5b" - "\x60\x65\x6a\x6f\x74\x79\x7e\x83" - "\x88\x8d\x92\x97\x9c\xa1\xa6\xab" - "\xb0\xb5\xba\xbf\xc4\xc9\xce\xd3" - "\xd8\xdd\xe2\xe7\xec\xf1\xf6\xfb" - "\x00\x07\x0e\x15\x1c\x23\x2a\x31" - "\x38\x3f\x46\x4d\x54\x5b\x62\x69" - "\x70\x77\x7e\x85\x8c\x93\x9a\xa1" - "\xa8\xaf\xb6\xbd\xc4\xcb\xd2\xd9" - "\xe0\xe7\xee\xf5\xfc\x03\x0a\x11" - "\x18\x1f\x26\x2d\x34\x3b\x42\x49" - "\x50\x57\x5e\x65\x6c\x73\x7a\x81" - "\x88\x8f\x96\x9d\xa4\xab\xb2\xb9" - "\xc0\xc7\xce\xd5\xdc\xe3\xea\xf1" - "\xf8\xff\x06\x0d\x14\x1b\x22\x29" - "\x30\x37\x3e\x45\x4c\x53\x5a\x61" - "\x68\x6f\x76\x7d\x84\x8b\x92\x99" - "\xa0\xa7\xae\xb5\xbc\xc3\xca\xd1" - "\xd8\xdf\xe6\xed\xf4\xfb\x02\x09" - "\x10\x17\x1e\x25\x2c\x33\x3a\x41" - "\x48\x4f\x56\x5d\x64\x6b\x72\x79" - "\x80\x87\x8e\x95\x9c\xa3\xaa\xb1" - "\xb8\xbf\xc6\xcd\xd4\xdb\xe2\xe9" - "\xf0\xf7\xfe\x05\x0c\x13\x1a\x21" - "\x28\x2f\x36\x3d\x44\x4b\x52\x59" - "\x60\x67\x6e\x75\x7c\x83\x8a\x91" - "\x98\x9f\xa6\xad\xb4\xbb\xc2\xc9" - "\xd0\xd7\xde\xe5\xec\xf3\xfa\x01" - "\x08\x0f\x16\x1d\x24\x2b\x32\x39" - "\x40\x47\x4e\x55\x5c\x63\x6a\x71" - "\x78\x7f\x86\x8d\x94\x9b\xa2\xa9" - "\xb0\xb7\xbe\xc5\xcc\xd3\xda\xe1" - "\xe8\xef\xf6\xfd\x04\x0b\x12\x19" - "\x20\x27\x2e\x35\x3c\x43\x4a\x51" - "\x58\x5f\x66\x6d\x74\x7b\x82\x89" - "\x90\x97\x9e\xa5\xac\xb3\xba\xc1" - "\xc8\xcf\xd6\xdd\xe4\xeb\xf2\xf9" - "\x00\x09\x12\x1b\x24\x2d\x36\x3f" - "\x48\x51\x5a\x63\x6c\x75\x7e\x87" - "\x90\x99\xa2\xab\xb4\xbd\xc6\xcf" - "\xd8\xe1\xea\xf3\xfc\x05\x0e\x17" - "\x20\x29\x32\x3b\x44\x4d\x56\x5f" - "\x68\x71\x7a\x83\x8c\x95\x9e\xa7" - "\xb0\xb9\xc2\xcb\xd4\xdd\xe6\xef" - "\xf8\x01\x0a\x13\x1c\x25\x2e\x37" - "\x40\x49\x52\x5b\x64\x6d\x76\x7f" - "\x88\x91\x9a\xa3\xac\xb5\xbe\xc7" - "\xd0\xd9\xe2\xeb\xf4\xfd\x06\x0f" - "\x18\x21\x2a\x33\x3c\x45\x4e\x57" - "\x60\x69\x72\x7b\x84\x8d\x96\x9f" - "\xa8\xb1\xba\xc3\xcc\xd5\xde\xe7" - "\xf0\xf9\x02\x0b\x14\x1d\x26\x2f" - "\x38\x41\x4a\x53\x5c\x65\x6e\x77" - "\x80\x89\x92\x9b\xa4\xad\xb6\xbf" - "\xc8\xd1\xda\xe3\xec\xf5\xfe\x07" - "\x10\x19\x22\x2b\x34\x3d\x46\x4f" - "\x58\x61\x6a\x73\x7c\x85\x8e\x97" - "\xa0\xa9\xb2\xbb\xc4\xcd\xd6\xdf" - "\xe8\xf1\xfa\x03\x0c\x15\x1e\x27" - "\x30\x39\x42\x4b\x54\x5d\x66\x6f" - "\x78\x81\x8a\x93\x9c\xa5\xae\xb7" - "\xc0\xc9\xd2\xdb\xe4\xed\xf6\xff" - "\x08\x11\x1a\x23\x2c\x35\x3e\x47" - "\x50\x59\x62\x6b\x74\x7d\x86\x8f" - "\x98\xa1\xaa\xb3\xbc\xc5\xce\xd7" - "\xe0\xe9\xf2\xfb\x04\x0d\x16\x1f" - "\x28\x31\x3a\x43\x4c\x55\x5e\x67" - "\x70\x79\x82\x8b\x94\x9d\xa6\xaf" - "\xb8\xc1\xca\xd3\xdc\xe5\xee\xf7" - "\x00\x0b\x16\x21\x2c\x37\x42\x4d" - "\x58\x63\x6e\x79\x84\x8f\x9a\xa5" - "\xb0\xbb\xc6\xd1\xdc\xe7\xf2\xfd" - "\x08\x13\x1e\x29\x34\x3f\x4a\x55" - "\x60\x6b\x76\x81\x8c\x97\xa2\xad" - "\xb8\xc3\xce\xd9\xe4\xef\xfa\x05" - "\x10\x1b\x26\x31\x3c\x47\x52\x5d" - "\x68\x73\x7e\x89\x94\x9f\xaa\xb5" - "\xc0\xcb\xd6\xe1\xec\xf7\x02\x0d" - "\x18\x23\x2e\x39\x44\x4f\x5a\x65" - "\x70\x7b\x86\x91\x9c\xa7\xb2\xbd" - "\xc8\xd3\xde\xe9\xf4\xff\x0a\x15" - "\x20\x2b\x36\x41\x4c\x57\x62\x6d" - "\x78\x83\x8e\x99\xa4\xaf\xba\xc5" - "\xd0\xdb\xe6\xf1\xfc\x07\x12\x1d" - "\x28\x33\x3e\x49\x54\x5f\x6a\x75" - "\x80\x8b\x96\xa1\xac\xb7\xc2\xcd" - "\xd8\xe3\xee\xf9\x04\x0f\x1a\x25" - "\x30\x3b\x46\x51\x5c\x67\x72\x7d" - "\x88\x93\x9e\xa9\xb4\xbf\xca\xd5" - "\xe0\xeb\xf6\x01\x0c\x17\x22\x2d" - "\x38\x43\x4e\x59\x64\x6f\x7a\x85" - "\x90\x9b\xa6\xb1\xbc\xc7\xd2\xdd" - "\xe8\xf3\xfe\x09\x14\x1f\x2a\x35" - "\x40\x4b\x56\x61\x6c\x77\x82\x8d" - "\x98\xa3\xae\xb9\xc4\xcf\xda\xe5" - "\xf0\xfb\x06\x11\x1c\x27\x32\x3d" - "\x48\x53\x5e\x69\x74\x7f\x8a\x95" - "\xa0\xab\xb6\xc1\xcc\xd7\xe2\xed" - "\xf8\x03\x0e\x19\x24\x2f\x3a\x45" - "\x50\x5b\x66\x71\x7c\x87\x92\x9d" - "\xa8\xb3\xbe\xc9\xd4\xdf\xea\xf5" - "\x00\x0d\x1a\x27\x34\x41\x4e\x5b" - "\x68\x75\x82\x8f\x9c\xa9\xb6\xc3" - "\xd0\xdd\xea\xf7\x04\x11\x1e\x2b" - "\x38\x45\x52\x5f\x6c\x79\x86\x93" - "\xa0\xad\xba\xc7\xd4\xe1\xee\xfb" - "\x08\x15\x22\x2f\x3c\x49\x56\x63" - "\x70\x7d\x8a\x97\xa4\xb1\xbe\xcb" - "\xd8\xe5\xf2\xff\x0c\x19\x26\x33" - "\x40\x4d\x5a\x67\x74\x81\x8e\x9b" - "\xa8\xb5\xc2\xcf\xdc\xe9\xf6\x03" - "\x10\x1d\x2a\x37\x44\x51\x5e\x6b" - "\x78\x85\x92\x9f\xac\xb9\xc6\xd3" - "\xe0\xed\xfa\x07\x14\x21\x2e\x3b" - "\x48\x55\x62\x6f\x7c\x89\x96\xa3" - "\xb0\xbd\xca\xd7\xe4\xf1\xfe\x0b" - "\x18\x25\x32\x3f\x4c\x59\x66\x73" - "\x80\x8d\x9a\xa7\xb4\xc1\xce\xdb" - "\xe8\xf5\x02\x0f\x1c\x29\x36\x43" - "\x50\x5d\x6a\x77\x84\x91\x9e\xab" - "\xb8\xc5\xd2\xdf\xec\xf9\x06\x13" - "\x20\x2d\x3a\x47\x54\x61\x6e\x7b" - "\x88\x95\xa2\xaf\xbc\xc9\xd6\xe3" - "\xf0\xfd\x0a\x17\x24\x31\x3e\x4b" - "\x58\x65\x72\x7f\x8c\x99\xa6\xb3" - "\xc0\xcd\xda\xe7\xf4\x01\x0e\x1b" - "\x28\x35\x42\x4f\x5c\x69\x76\x83" - "\x90\x9d\xaa\xb7\xc4\xd1\xde\xeb" - "\xf8\x05\x12\x1f\x2c\x39\x46\x53" - "\x60\x6d\x7a\x87\x94\xa1\xae\xbb" - "\xc8\xd5\xe2\xef\xfc\x09\x16\x23" - "\x30\x3d\x4a\x57\x64\x71\x7e\x8b" - "\x98\xa5\xb2\xbf\xcc\xd9\xe6\xf3" - "\x00\x0f\x1e\x2d\x3c\x4b\x5a\x69" - "\x78\x87\x96\xa5\xb4\xc3\xd2\xe1" - "\xf0\xff\x0e\x1d\x2c\x3b\x4a\x59" - "\x68\x77\x86\x95\xa4\xb3\xc2\xd1" - "\xe0\xef\xfe\x0d\x1c\x2b\x3a\x49" - "\x58\x67\x76\x85\x94\xa3\xb2\xc1" - "\xd0\xdf\xee\xfd\x0c\x1b\x2a\x39" - "\x48\x57\x66\x75\x84\x93\xa2\xb1" - "\xc0\xcf\xde\xed\xfc\x0b\x1a\x29" - "\x38\x47\x56\x65\x74\x83\x92\xa1" - "\xb0\xbf\xce\xdd\xec\xfb\x0a\x19" - "\x28\x37\x46\x55\x64\x73\x82\x91" - "\xa0\xaf\xbe\xcd\xdc\xeb\xfa\x09" - "\x18\x27\x36\x45\x54\x63\x72\x81" - "\x90\x9f\xae\xbd\xcc\xdb\xea\xf9" - "\x08\x17\x26\x35\x44\x53\x62\x71" - "\x80\x8f\x9e\xad\xbc\xcb\xda\xe9" - "\xf8\x07\x16\x25\x34\x43\x52\x61" - "\x70\x7f\x8e\x9d\xac\xbb\xca\xd9" - "\xe8\xf7\x06\x15\x24\x33\x42\x51" - "\x60\x6f\x7e\x8d\x9c\xab\xba\xc9" - "\xd8\xe7\xf6\x05\x14\x23\x32\x41" - "\x50\x5f\x6e\x7d\x8c\x9b\xaa\xb9" - "\xc8\xd7\xe6\xf5\x04\x13\x22\x31" - "\x40\x4f\x5e\x6d\x7c\x8b\x9a\xa9" - "\xb8\xc7\xd6\xe5\xf4\x03\x12\x21" - "\x30\x3f\x4e\x5d\x6c\x7b\x8a\x99" - "\xa8\xb7\xc6\xd5\xe4\xf3\x02\x11" - "\x20\x2f\x3e\x4d\x5c\x6b\x7a\x89" - "\x98\xa7\xb6\xc5\xd4\xe3\xf2\x01" - "\x10\x1f\x2e\x3d\x4c\x5b\x6a\x79" - "\x88\x97\xa6\xb5\xc4\xd3\xe2\xf1" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff" - "\x10\x21\x32\x43\x54\x65\x76\x87" - "\x98\xa9\xba\xcb\xdc\xed\xfe\x0f" - "\x20\x31\x42\x53\x64\x75\x86\x97" - "\xa8\xb9\xca\xdb\xec\xfd\x0e\x1f" - "\x30\x41\x52\x63\x74\x85\x96\xa7" - "\xb8\xc9\xda\xeb\xfc\x0d\x1e\x2f" - "\x40\x51\x62\x73\x84\x95\xa6\xb7" - "\xc8\xd9\xea\xfb\x0c\x1d\x2e\x3f" - "\x50\x61\x72\x83\x94\xa5\xb6\xc7" - "\xd8\xe9\xfa\x0b\x1c\x2d\x3e\x4f" - "\x60\x71\x82\x93\xa4\xb5\xc6\xd7" - "\xe8\xf9\x0a\x1b\x2c\x3d\x4e\x5f" - "\x70\x81\x92\xa3\xb4\xc5\xd6\xe7" - "\xf8\x09\x1a\x2b\x3c\x4d\x5e\x6f" - "\x80\x91\xa2\xb3\xc4\xd5\xe6\xf7" - "\x08\x19\x2a\x3b\x4c\x5d\x6e\x7f" - "\x90\xa1\xb2\xc3\xd4\xe5\xf6\x07" - "\x18\x29\x3a\x4b\x5c\x6d\x7e\x8f" - "\xa0\xb1\xc2\xd3\xe4\xf5\x06\x17" - "\x28\x39\x4a\x5b\x6c\x7d\x8e\x9f" - "\xb0\xc1\xd2\xe3\xf4\x05\x16\x27" - "\x38\x49\x5a\x6b\x7c\x8d\x9e\xaf" - "\xc0\xd1\xe2\xf3\x04\x15\x26\x37" - "\x48\x59\x6a\x7b\x8c\x9d\xae\xbf" - "\xd0\xe1\xf2\x03\x14\x25\x36\x47" - "\x58\x69\x7a\x8b\x9c\xad\xbe\xcf" - "\xe0\xf1\x02\x13\x24\x35\x46\x57" - "\x68\x79\x8a\x9b\xac\xbd\xce\xdf" - "\xf0\x01\x12\x23\x34\x45\x56\x67" - "\x78\x89\x9a\xab\xbc\xcd\xde\xef" - "\x00\x13\x26\x39\x4c\x5f\x72\x85" - "\x98\xab\xbe\xd1\xe4\xf7\x0a\x1d" - "\x30\x43\x56\x69\x7c\x8f\xa2\xb5" - "\xc8\xdb\xee\x01\x14\x27\x3a\x4d" - "\x60\x73\x86\x99\xac\xbf\xd2\xe5" - "\xf8\x0b\x1e\x31\x44\x57\x6a\x7d" - "\x90\xa3\xb6\xc9\xdc\xef\x02\x15" - "\x28\x3b\x4e\x61\x74\x87\x9a\xad" - "\xc0\xd3\xe6\xf9\x0c\x1f\x32\x45" - "\x58\x6b\x7e\x91\xa4\xb7\xca\xdd" - "\xf0\x03\x16\x29\x3c\x4f\x62\x75" - "\x88\x9b\xae\xc1\xd4\xe7\xfa\x0d" - "\x20\x33\x46\x59\x6c\x7f\x92\xa5" - "\xb8\xcb\xde\xf1\x04\x17\x2a\x3d" - "\x50\x63\x76\x89\x9c\xaf\xc2\xd5" - "\xe8\xfb\x0e\x21\x34\x47\x5a\x6d" - "\x80\x93\xa6\xb9\xcc\xdf\xf2\x05" - "\x18\x2b\x3e\x51\x64\x77\x8a\x9d" - "\xb0\xc3\xd6\xe9\xfc\x0f\x22\x35" - "\x48\x5b\x6e\x81\x94\xa7\xba\xcd" - "\xe0\xf3\x06\x19\x2c\x3f\x52\x65" - "\x78\x8b\x9e\xb1\xc4\xd7\xea\xfd" - "\x10\x23\x36\x49\x5c\x6f\x82\x95" - "\xa8\xbb\xce\xe1\xf4\x07\x1a\x2d" - "\x40\x53\x66\x79\x8c\x9f\xb2\xc5" - "\xd8\xeb\xfe\x11\x24\x37\x4a\x5d" - "\x70\x83\x96\xa9\xbc\xcf\xe2\xf5" - "\x08\x1b\x2e\x41\x54\x67\x7a\x8d" - "\xa0\xb3\xc6\xd9\xec\xff\x12\x25" - "\x38\x4b\x5e\x71\x84\x97\xaa\xbd" - "\xd0\xe3\xf6\x09\x1c\x2f\x42\x55" - "\x68\x7b\x8e\xa1\xb4\xc7\xda\xed" - "\x00\x15\x2a\x3f\x54\x69\x7e\x93" - "\xa8\xbd\xd2\xe7\xfc\x11\x26\x3b" - "\x50\x65\x7a\x8f\xa4\xb9\xce\xe3" - "\xf8\x0d\x22\x37\x4c\x61\x76\x8b" - "\xa0\xb5\xca\xdf\xf4\x09\x1e\x33" - "\x48\x5d\x72\x87\x9c\xb1\xc6\xdb" - "\xf0\x05\x1a\x2f\x44\x59\x6e\x83" - "\x98\xad\xc2\xd7\xec\x01\x16\x2b" - "\x40\x55\x6a\x7f\x94\xa9\xbe\xd3" - "\xe8\xfd\x12\x27\x3c\x51\x66\x7b" - "\x90\xa5\xba\xcf\xe4\xf9\x0e\x23" - "\x38\x4d\x62\x77\x8c\xa1\xb6\xcb" - "\xe0\xf5\x0a\x1f\x34\x49\x5e\x73" - "\x88\x9d\xb2\xc7\xdc\xf1\x06\x1b" - "\x30\x45\x5a\x6f\x84\x99\xae\xc3" - "\xd8\xed\x02\x17\x2c\x41\x56\x6b" - "\x80\x95\xaa\xbf\xd4\xe9\xfe\x13" - "\x28\x3d\x52\x67\x7c\x91\xa6\xbb" - "\xd0\xe5\xfa\x0f\x24\x39\x4e\x63" - "\x78\x8d\xa2\xb7\xcc\xe1\xf6\x0b" - "\x20\x35\x4a\x5f\x74\x89\x9e\xb3" - "\xc8\xdd\xf2\x07\x1c\x31\x46\x5b" - "\x70\x85\x9a\xaf\xc4\xd9\xee\x03" - "\x18\x2d\x42\x57\x6c\x81\x96\xab" - "\xc0\xd5\xea\xff\x14\x29\x3e\x53" - "\x68\x7d\x92\xa7\xbc\xd1\xe6\xfb" - "\x10\x25\x3a\x4f\x64\x79\x8e\xa3" - "\xb8\xcd\xe2\xf7\x0c\x21\x36\x4b" - "\x60\x75\x8a\x9f\xb4\xc9\xde\xf3" - "\x08\x1d\x32\x47\x5c\x71\x86\x9b" - "\xb0\xc5\xda\xef\x04\x19\x2e\x43" - "\x58\x6d\x82\x97\xac\xc1\xd6\xeb" - "\x00\x17\x2e\x45\x5c\x73\x8a\xa1" - "\xb8\xcf\xe6\xfd\x14\x2b\x42\x59" - "\x70\x87\x9e\xb5\xcc\xe3\xfa\x11" - "\x28\x3f\x56\x6d\x84\x9b\xb2\xc9" - "\xe0\xf7\x0e\x25\x3c\x53\x6a\x81" - "\x98\xaf\xc6\xdd\xf4\x0b\x22\x39" - "\x50\x67\x7e\x95\xac\xc3\xda\xf1" - "\x08\x1f\x36\x4d\x64\x7b\x92\xa9" - "\xc0\xd7\xee\x05\x1c\x33\x4a\x61" - "\x78\x8f\xa6\xbd\xd4\xeb\x02\x19" - "\x30\x47\x5e\x75\x8c\xa3\xba\xd1" - "\xe8\xff\x16\x2d\x44\x5b\x72\x89" - "\xa0\xb7\xce\xe5\xfc\x13\x2a\x41" - "\x58\x6f\x86\x9d\xb4\xcb\xe2\xf9" - "\x10\x27\x3e\x55\x6c\x83\x9a\xb1" - "\xc8\xdf\xf6\x0d\x24\x3b\x52\x69" - "\x80\x97\xae\xc5\xdc\xf3\x0a\x21" - "\x38\x4f\x66\x7d\x94\xab\xc2\xd9" - "\xf0\x07\x1e\x35\x4c\x63\x7a\x91" - "\xa8\xbf\xd6\xed\x04\x1b\x32\x49" - "\x60\x77\x8e\xa5\xbc\xd3\xea\x01" - "\x18\x2f\x46\x5d\x74\x8b\xa2\xb9" - "\xd0\xe7\xfe\x15\x2c\x43\x5a\x71" - "\x88\x9f\xb6\xcd\xe4\xfb\x12\x29" - "\x40\x57\x6e\x85\x9c\xb3\xca\xe1" - "\xf8\x0f\x26\x3d\x54\x6b\x82\x99" - "\xb0\xc7\xde\xf5\x0c\x23\x3a\x51" - "\x68\x7f\x96\xad\xc4\xdb\xf2\x09" - "\x20\x37\x4e\x65\x7c\x93\xaa\xc1" - "\xd8\xef\x06\x1d\x34\x4b\x62\x79" - "\x90\xa7\xbe\xd5\xec\x03\x1a\x31" - "\x48\x5f\x76\x8d\xa4\xbb\xd2\xe9" - "\x00\x19\x32\x4b\x64\x7d\x96\xaf" - "\xc8\xe1\xfa\x13\x2c\x45\x5e\x77" - "\x90\xa9\xc2\xdb\xf4\x0d\x26\x3f" - "\x58\x71\x8a\xa3\xbc\xd5\xee\x07" - "\x20\x39\x52\x6b\x84\x9d\xb6\xcf" - "\xe8\x01\x1a\x33\x4c\x65\x7e\x97" - "\xb0\xc9\xe2\xfb\x14\x2d\x46\x5f" - "\x78\x91\xaa\xc3\xdc\xf5\x0e\x27" - "\x40\x59\x72\x8b\xa4\xbd\xd6\xef" - "\x08\x21\x3a\x53\x6c\x85\x9e\xb7" - "\xd0\xe9\x02\x1b\x34\x4d\x66\x7f" - "\x98\xb1\xca\xe3\xfc\x15\x2e\x47" - "\x60\x79\x92\xab\xc4\xdd\xf6\x0f" - "\x28\x41\x5a\x73\x8c\xa5\xbe\xd7" - "\xf0\x09\x22\x3b\x54\x6d\x86\x9f" - "\xb8\xd1\xea\x03\x1c\x35\x4e\x67" - "\x80\x99\xb2\xcb\xe4\xfd\x16\x2f" - "\x48\x61\x7a\x93\xac\xc5\xde\xf7" - "\x10\x29\x42\x5b\x74\x8d\xa6\xbf" - "\xd8\xf1\x0a\x23\x3c\x55\x6e\x87" - "\xa0\xb9\xd2\xeb\x04\x1d\x36\x4f" - "\x68\x81\x9a\xb3\xcc\xe5\xfe\x17" - "\x30\x49\x62\x7b\x94\xad\xc6\xdf" - "\xf8\x11\x2a\x43\x5c\x75\x8e\xa7" - "\xc0\xd9\xf2\x0b\x24\x3d\x56\x6f" - "\x88\xa1\xba\xd3\xec\x05\x1e\x37" - "\x50\x69\x82\x9b\xb4\xcd\xe6\xff" - "\x18\x31\x4a\x63\x7c\x95\xae\xc7" - "\xe0\xf9\x12\x2b\x44\x5d\x76\x8f" - "\xa8\xc1\xda\xf3\x0c\x25\x3e\x57" - "\x70\x89\xa2\xbb\xd4\xed\x06\x1f" - "\x38\x51\x6a\x83\x9c\xb5\xce\xe7" - "\x00\x1b\x36\x51\x6c\x87\xa2\xbd" - "\xd8\xf3\x0e\x29\x44\x5f\x7a\x95" - "\xb0\xcb\xe6\x01\x1c\x37\x52\x6d" - "\x88\xa3\xbe\xd9\xf4\x0f\x2a\x45" - "\x60\x7b\x96\xb1\xcc\xe7\x02\x1d" - "\x38\x53\x6e\x89\xa4\xbf\xda\xf5" - "\x10\x2b\x46\x61\x7c\x97\xb2\xcd" - "\xe8\x03\x1e\x39\x54\x6f\x8a\xa5" - "\xc0\xdb\xf6\x11\x2c\x47\x62\x7d" - "\x98\xb3\xce\xe9\x04\x1f\x3a\x55" - "\x70\x8b\xa6\xc1\xdc\xf7\x12\x2d" - "\x48\x63\x7e\x99\xb4\xcf\xea\x05" - "\x20\x3b\x56\x71\x8c\xa7\xc2\xdd" - "\xf8\x13\x2e\x49\x64\x7f\x9a\xb5" - "\xd0\xeb\x06\x21\x3c\x57\x72\x8d" - "\xa8\xc3\xde\xf9\x14\x2f\x4a\x65" - "\x80\x9b\xb6\xd1\xec\x07\x22\x3d" - "\x58\x73\x8e\xa9\xc4\xdf\xfa\x15" - "\x30\x4b\x66\x81\x9c\xb7\xd2\xed" - "\x08\x23\x3e\x59\x74\x8f\xaa\xc5" - "\xe0\xfb\x16\x31\x4c\x67\x82\x9d" - "\xb8\xd3\xee\x09\x24\x3f\x5a\x75" - "\x90\xab\xc6\xe1\xfc\x17\x32\x4d" - "\x68\x83\x9e\xb9\xd4\xef\x0a\x25" - "\x40\x5b\x76\x91\xac\xc7\xe2\xfd" - "\x18\x33\x4e\x69\x84\x9f\xba\xd5" - "\xf0\x0b\x26\x41\x5c\x77\x92\xad" - "\xc8\xe3\xfe\x19\x34\x4f\x6a\x85" - "\xa0\xbb\xd6\xf1\x0c\x27\x42\x5d" - "\x78\x93\xae\xc9\xe4\xff\x1a\x35" - "\x50\x6b\x86\xa1\xbc\xd7\xf2\x0d" - "\x28\x43\x5e\x79\x94\xaf\xca\xe5" - "\x00\x1d\x3a\x57\x74\x91\xae\xcb" - "\xe8\x05\x22\x3f\x5c\x79\x96\xb3" - "\xd0\xed\x0a\x27\x44\x61\x7e\x9b" - "\xb8\xd5\xf2\x0f\x2c\x49\x66\x83" - "\xa0\xbd\xda\xf7\x14\x31\x4e\x6b" - "\x88\xa5\xc2\xdf\xfc\x19\x36\x53" - "\x70\x8d\xaa\xc7\xe4\x01\x1e\x3b" - "\x58\x75\x92\xaf\xcc\xe9\x06\x23" - "\x40\x5d\x7a\x97\xb4\xd1\xee\x0b" - "\x28\x45\x62\x7f\x9c\xb9\xd6\xf3" - "\x10\x2d\x4a\x67\x84\xa1\xbe\xdb" - "\xf8\x15\x32\x4f\x6c\x89\xa6\xc3" - "\xe0\xfd\x1a\x37\x54\x71\x8e\xab" - "\xc8\xe5\x02\x1f\x3c\x59\x76\x93" - "\xb0\xcd\xea\x07\x24\x41\x5e\x7b" - "\x98\xb5\xd2\xef\x0c\x29\x46\x63" - "\x80\x9d\xba\xd7\xf4\x11\x2e\x4b" - "\x68\x85\xa2\xbf\xdc\xf9\x16\x33" - "\x50\x6d\x8a\xa7\xc4\xe1\xfe\x1b" - "\x38\x55\x72\x8f\xac\xc9\xe6\x03" - "\x20\x3d\x5a\x77\x94\xb1\xce\xeb" - "\x08\x25\x42\x5f\x7c\x99\xb6\xd3" - "\xf0\x0d\x2a\x47\x64\x81\x9e\xbb" - "\xd8\xf5\x12\x2f\x4c\x69\x86\xa3" - "\xc0\xdd\xfa\x17\x34\x51\x6e\x8b" - "\xa8\xc5\xe2\xff\x1c\x39\x56\x73" - "\x90\xad\xca\xe7\x04\x21\x3e\x5b" - "\x78\x95\xb2\xcf\xec\x09\x26\x43" - "\x60\x7d\x9a\xb7\xd4\xf1\x0e\x2b" - "\x48\x65\x82\x9f\xbc\xd9\xf6\x13" - "\x30\x4d\x6a\x87\xa4\xc1\xde\xfb" - "\x18\x35\x52\x6f\x8c\xa9\xc6\xe3" - "\x00\x1f\x3e\x5d\x7c\x9b\xba\xd9" - "\xf8\x17\x36\x55\x74\x93\xb2\xd1" - "\xf0\x0f\x2e\x4d\x6c\x8b\xaa\xc9" - "\xe8\x07\x26\x45\x64\x83\xa2\xc1" - "\xe0\xff\x1e\x3d\x5c\x7b\x9a\xb9" - "\xd8\xf7\x16\x35\x54\x73\x92\xb1" - "\xd0\xef\x0e\x2d\x4c\x6b\x8a\xa9" - "\xc8\xe7\x06\x25\x44\x63\x82\xa1" - "\xc0\xdf\xfe\x1d\x3c\x5b\x7a\x99" - "\xb8\xd7\xf6\x15\x34\x53\x72\x91" - "\xb0\xcf\xee\x0d\x2c\x4b\x6a\x89" - "\xa8\xc7\xe6\x05\x24\x43\x62\x81" - "\xa0\xbf\xde\xfd\x1c\x3b\x5a\x79" - "\x98\xb7\xd6\xf5\x14\x33\x52\x71" - "\x90\xaf\xce\xed\x0c\x2b\x4a\x69" - "\x88\xa7\xc6\xe5\x04\x23\x42\x61" - "\x80\x9f\xbe\xdd\xfc\x1b\x3a\x59" - "\x78\x97\xb6\xd5\xf4\x13\x32\x51" - "\x70\x8f\xae\xcd\xec\x0b\x2a\x49" - "\x68\x87\xa6\xc5\xe4\x03\x22\x41" - "\x60\x7f\x9e\xbd\xdc\xfb\x1a\x39" - "\x58\x77\x96\xb5\xd4\xf3\x12\x31" - "\x50\x6f\x8e\xad\xcc\xeb\x0a\x29" - "\x48\x67\x86\xa5\xc4\xe3\x02\x21" - "\x40\x5f\x7e\x9d\xbc\xdb\xfa\x19" - "\x38\x57\x76\x95\xb4\xd3\xf2\x11" - "\x30\x4f\x6e\x8d\xac\xcb\xea\x09" - "\x28\x47\x66\x85\xa4\xc3\xe2\x01" - "\x20\x3f\x5e\x7d\x9c\xbb\xda\xf9" - "\x18\x37\x56\x75\x94\xb3\xd2\xf1" - "\x10\x2f\x4e\x6d\x8c\xab\xca\xe9" - "\x08\x27\x46\x65\x84\xa3\xc2\xe1" - "\x00\x21\x42\x63", - .ilen = 4100, - .result = - "\xf0\x5c\x74\xad\x4e\xbc\x99\xe2" - "\xae\xff\x91\x3a\x44\xcf\x38\x32" - "\x1e\xad\xa7\xcd\xa1\x39\x95\xaa" - "\x10\xb1\xb3\x2e\x04\x31\x8f\x86" - "\xf2\x62\x74\x70\x0c\xa4\x46\x08" - "\xa8\xb7\x99\xa8\xe9\xd2\x73\x79" - "\x7e\x6e\xd4\x8f\x1e\xc7\x8e\x31" - "\x0b\xfa\x4b\xce\xfd\xf3\x57\x71" - "\xe9\x46\x03\xa5\x3d\x34\x00\xe2" - "\x18\xff\x75\x6d\x06\x2d\x00\xab" - "\xb9\x3e\x6c\x59\xc5\x84\x06\xb5" - "\x8b\xd0\x89\x9c\x4a\x79\x16\xc6" - "\x3d\x74\x54\xfa\x44\xcd\x23\x26" - "\x5c\xcf\x7e\x28\x92\x32\xbf\xdf" - "\xa7\x20\x3c\x74\x58\x2a\x9a\xde" - "\x61\x00\x1c\x4f\xff\x59\xc4\x22" - "\xac\x3c\xd0\xe8\x6c\xf9\x97\x1b" - "\x58\x9b\xad\x71\xe8\xa9\xb5\x0d" - "\xee\x2f\x04\x1f\x7f\xbc\x99\xee" - "\x84\xff\x42\x60\xdc\x3a\x18\xa5" - "\x81\xf9\xef\xdc\x7a\x0f\x65\x41" - "\x2f\xa3\xd3\xf9\xc2\xcb\xc0\x4d" - "\x8f\xd3\x76\x96\xad\x49\x6d\x38" - "\x3d\x39\x0b\x6c\x80\xb7\x54\x69" - "\xf0\x2c\x90\x02\x29\x0d\x1c\x12" - "\xad\x55\xc3\x8b\x68\xd9\xcc\xb3" - "\xb2\x64\x33\x90\x5e\xca\x4b\xe2" - "\xfb\x75\xdc\x63\xf7\x9f\x82\x74" - "\xf0\xc9\xaa\x7f\xe9\x2a\x9b\x33" - "\xbc\x88\x00\x7f\xca\xb2\x1f\x14" - "\xdb\xc5\x8e\x7b\x11\x3c\x3e\x08" - "\xf3\x83\xe8\xe0\x94\x86\x2e\x92" - "\x78\x6b\x01\xc9\xc7\x83\xba\x21" - "\x6a\x25\x15\x33\x4e\x45\x08\xec" - "\x35\xdb\xe0\x6e\x31\x51\x79\xa9" - "\x42\x44\x65\xc1\xa0\xf1\xf9\x2a" - "\x70\xd5\xb6\xc6\xc1\x8c\x39\xfc" - "\x25\xa6\x55\xd9\xdd\x2d\x4c\xec" - "\x49\xc6\xeb\x0e\xa8\x25\x2a\x16" - "\x1b\x66\x84\xda\xe2\x92\xe5\xc0" - "\xc8\x53\x07\xaf\x80\x84\xec\xfd" - "\xcd\xd1\x6e\xcd\x6f\x6a\xf5\x36" - "\xc5\x15\xe5\x25\x7d\x77\xd1\x1a" - "\x93\x36\xa9\xcf\x7c\xa4\x54\x4a" - "\x06\x51\x48\x4e\xf6\x59\x87\xd2" - "\x04\x02\xef\xd3\x44\xde\x76\x31" - "\xb3\x34\x17\x1b\x9d\x66\x11\x9f" - "\x1e\xcc\x17\xe9\xc7\x3c\x1b\xe7" - "\xcb\x50\x08\xfc\xdc\x2b\x24\xdb" - "\x65\x83\xd0\x3b\xe3\x30\xea\x94" - "\x6c\xe7\xe8\x35\x32\xc7\xdb\x64" - "\xb4\x01\xab\x36\x2c\x77\x13\xaf" - "\xf8\x2b\x88\x3f\x54\x39\xc4\x44" - "\xfe\xef\x6f\x68\x34\xbe\x0f\x05" - "\x16\x6d\xf6\x0a\x30\xe7\xe3\xed" - "\xc4\xde\x3c\x1b\x13\xd8\xdb\xfe" - "\x41\x62\xe5\x28\xd4\x8d\xa3\xc7" - "\x93\x97\xc6\x48\x45\x1d\x9f\x83" - "\xdf\x4b\x40\x3e\x42\x25\x87\x80" - "\x4c\x7d\xa8\xd4\x98\x23\x95\x75" - "\x41\x8c\xda\x41\x9b\xd4\xa7\x06" - "\xb5\xf1\x71\x09\x53\xbe\xca\xbf" - "\x32\x03\xed\xf0\x50\x1c\x56\x39" - "\x5b\xa4\x75\x18\xf7\x9b\x58\xef" - "\x53\xfc\x2a\x38\x23\x15\x75\xcd" - "\x45\xe5\x5a\x82\x55\xba\x21\xfa" - "\xd4\xbd\xc6\x94\x7c\xc5\x80\x12" - "\xf7\x4b\x32\xc4\x9a\x82\xd8\x28" - "\x8f\xd9\xc2\x0f\x60\x03\xbe\x5e" - "\x21\xd6\x5f\x58\xbf\x5c\xb1\x32" - "\x82\x8d\xa9\xe5\xf2\x66\x1a\xc0" - "\xa0\xbc\x58\x2f\x71\xf5\x2f\xed" - "\xd1\x26\xb9\xd8\x49\x5a\x07\x19" - "\x01\x7c\x59\xb0\xf8\xa4\xb7\xd3" - "\x7b\x1a\x8c\x38\xf4\x50\xa4\x59" - "\xb0\xcc\x41\x0b\x88\x7f\xe5\x31" - "\xb3\x42\xba\xa2\x7e\xd4\x32\x71" - "\x45\x87\x48\xa9\xc2\xf2\x89\xb3" - "\xe4\xa7\x7e\x52\x15\x61\xfa\xfe" - "\xc9\xdd\x81\xeb\x13\xab\xab\xc3" - "\x98\x59\xd8\x16\x3d\x14\x7a\x1c" - "\x3c\x41\x9a\x16\x16\x9b\xd2\xd2" - "\x69\x3a\x29\x23\xac\x86\x32\xa5" - "\x48\x9c\x9e\xf3\x47\x77\x81\x70" - "\x24\xe8\x85\xd2\xf5\xb5\xfa\xff" - "\x59\x6a\xd3\x50\x59\x43\x59\xde" - "\xd9\xf1\x55\xa5\x0c\xc3\x1a\x1a" - "\x18\x34\x0d\x1a\x63\x33\xed\x10" - "\xe0\x1d\x2a\x18\xd2\xc0\x54\xa8" - "\xca\xb5\x9a\xd3\xdd\xca\x45\x84" - "\x50\xe7\x0f\xfe\xa4\x99\x5a\xbe" - "\x43\x2d\x9a\xcb\x92\x3f\x5a\x1d" - "\x85\xd8\xc9\xdf\x68\xc9\x12\x80" - "\x56\x0c\xdc\x00\xdc\x3a\x7d\x9d" - "\xa3\xa2\xe8\x4d\xbf\xf9\x70\xa0" - "\xa4\x13\x4f\x6b\xaf\x0a\x89\x7f" - "\xda\xf0\xbf\x9b\xc8\x1d\xe5\xf8" - "\x2e\x8b\x07\xb5\x73\x1b\xcc\xa2" - "\xa6\xad\x30\xbc\x78\x3c\x5b\x10" - "\xfa\x5e\x62\x2d\x9e\x64\xb3\x33" - "\xce\xf9\x1f\x86\xe7\x8b\xa2\xb8" - "\xe8\x99\x57\x8c\x11\xed\x66\xd9" - "\x3c\x72\xb9\xc3\xe6\x4e\x17\x3a" - "\x6a\xcb\x42\x24\x06\xed\x3e\x4e" - "\xa3\xe8\x6a\x94\xda\x0d\x4e\xd5" - "\x14\x19\xcf\xb6\x26\xd8\x2e\xcc" - "\x64\x76\x38\x49\x4d\xfe\x30\x6d" - "\xe4\xc8\x8c\x7b\xc4\xe0\x35\xba" - "\x22\x6e\x76\xe1\x1a\xf2\x53\xc3" - "\x28\xa2\x82\x1f\x61\x69\xad\xc1" - "\x7b\x28\x4b\x1e\x6c\x85\x95\x9b" - "\x51\xb5\x17\x7f\x12\x69\x8c\x24" - "\xd5\xc7\x5a\x5a\x11\x54\xff\x5a" - "\xf7\x16\xc3\x91\xa6\xf0\xdc\x0a" - "\xb6\xa7\x4a\x0d\x7a\x58\xfe\xa5" - "\xf5\xcb\x8f\x7b\x0e\xea\x57\xe7" - "\xbd\x79\xd6\x1c\x88\x23\x6c\xf2" - "\x4d\x29\x77\x53\x35\x6a\x00\x8d" - "\xcd\xa3\x58\xbe\x77\x99\x18\xf8" - "\xe6\xe1\x8f\xe9\x37\x8f\xe3\xe2" - "\x5a\x8a\x93\x25\xaf\xf3\x78\x80" - "\xbe\xa6\x1b\xc6\xac\x8b\x1c\x91" - "\x58\xe1\x9f\x89\x35\x9d\x1d\x21" - "\x29\x9f\xf4\x99\x02\x27\x0f\xa8" - "\x4f\x79\x94\x2b\x33\x2c\xda\xa2" - "\x26\x39\x83\x94\xef\x27\xd8\x53" - "\x8f\x66\x0d\xe4\x41\x7d\x34\xcd" - "\x43\x7c\x95\x0a\x53\xef\x66\xda" - "\x7e\x9b\xf3\x93\xaf\xd0\x73\x71" - "\xba\x40\x9b\x74\xf8\xd7\xd7\x41" - "\x6d\xaf\x72\x9c\x8d\x21\x87\x3c" - "\xfd\x0a\x90\xa9\x47\x96\x9e\xd3" - "\x88\xee\x73\xcf\x66\x2f\x52\x56" - "\x6d\xa9\x80\x4c\xe2\x6f\x62\x88" - "\x3f\x0e\x54\x17\x48\x80\x5d\xd3" - "\xc3\xda\x25\x3d\xa1\xc8\xcb\x9f" - "\x9b\x70\xb3\xa1\xeb\x04\x52\xa1" - "\xf2\x22\x0f\xfc\xc8\x18\xfa\xf9" - "\x85\x9c\xf1\xac\xeb\x0c\x02\x46" - "\x75\xd2\xf5\x2c\xe3\xd2\x59\x94" - "\x12\xf3\x3c\xfc\xd7\x92\xfa\x36" - "\xba\x61\x34\x38\x7c\xda\x48\x3e" - "\x08\xc9\x39\x23\x5e\x02\x2c\x1a" - "\x18\x7e\xb4\xd9\xfd\x9e\x40\x02" - "\xb1\x33\x37\x32\xe7\xde\xd6\xd0" - "\x7c\x58\x65\x4b\xf8\x34\x27\x9c" - "\x44\xb4\xbd\xe9\xe9\x4c\x78\x7d" - "\x4b\x9f\xce\xb1\xcd\x47\xa5\x37" - "\xe5\x6d\xbd\xb9\x43\x94\x0a\xd4" - "\xd6\xf9\x04\x5f\xb5\x66\x6c\x1a" - "\x35\x12\xe3\x36\x28\x27\x36\x58" - "\x01\x2b\x79\xe4\xba\x6d\x10\x7d" - "\x65\xdf\x84\x95\xf4\xd5\xb6\x8f" - "\x2b\x9f\x96\x00\x86\x60\xf0\x21" - "\x76\xa8\x6a\x8c\x28\x1c\xb3\x6b" - "\x97\xd7\xb6\x53\x2a\xcc\xab\x40" - "\x9d\x62\x79\x58\x52\xe6\x65\xb7" - "\xab\x55\x67\x9c\x89\x7c\x03\xb0" - "\x73\x59\xc5\x81\xf5\x18\x17\x5c" - "\x89\xf3\x78\x35\x44\x62\x78\x72" - "\xd0\x96\xeb\x31\xe7\x87\x77\x14" - "\x99\x51\xf2\x59\x26\x9e\xb5\xa6" - "\x45\xfe\x6e\xbd\x07\x4c\x94\x5a" - "\xa5\x7d\xfc\xf1\x2b\x77\xe2\xfe" - "\x17\xd4\x84\xa0\xac\xb5\xc7\xda" - "\xa9\x1a\xb6\xf3\x74\x11\xb4\x9d" - "\xfb\x79\x2e\x04\x2d\x50\x28\x83" - "\xbf\xc6\x52\xd3\x34\xd6\xe8\x7a" - "\xb6\xea\xe7\xa8\x6c\x15\x1e\x2c" - "\x57\xbc\x48\x4e\x5f\x5c\xb6\x92" - "\xd2\x49\x77\x81\x6d\x90\x70\xae" - "\x98\xa1\x03\x0d\x6b\xb9\x77\x14" - "\xf1\x4e\x23\xd3\xf8\x68\xbd\xc2" - "\xfe\x04\xb7\x5c\xc5\x17\x60\x8f" - "\x65\x54\xa4\x7a\x42\xdc\x18\x0d" - "\xb5\xcf\x0f\xd3\xc7\x91\x66\x1b" - "\x45\x42\x27\x75\x50\xe5\xee\xb8" - "\x7f\x33\x2c\xba\x4a\x92\x4d\x2c" - "\x3c\xe3\x0d\x80\x01\xba\x0d\x29" - "\xd8\x3c\xe9\x13\x16\x57\xe6\xea" - "\x94\x52\xe7\x00\x4d\x30\xb0\x0f" - "\x35\xb8\xb8\xa7\xb1\xb5\x3b\x44" - "\xe1\x2f\xfd\x88\xed\x43\xe7\x52" - "\x10\x93\xb3\x8a\x30\x6b\x0a\xf7" - "\x23\xc6\x50\x9d\x4a\xb0\xde\xc3" - "\xdc\x9b\x2f\x01\x56\x36\x09\xc5" - "\x2f\x6b\xfe\xf1\xd8\x27\x45\x03" - "\x30\x5e\x5c\x5b\xb4\x62\x0e\x1a" - "\xa9\x21\x2b\x92\x94\x87\x62\x57" - "\x4c\x10\x74\x1a\xf1\x0a\xc5\x84" - "\x3b\x9e\x72\x02\xd7\xcc\x09\x56" - "\xbd\x54\xc1\xf0\xc3\xe3\xb3\xf8" - "\xd2\x0d\x61\xcb\xef\xce\x0d\x05" - "\xb0\x98\xd9\x8e\x4f\xf9\xbc\x93" - "\xa6\xea\xc8\xcf\x10\x53\x4b\xf1" - "\xec\xfc\x89\xf9\x64\xb0\x22\xbf" - "\x9e\x55\x46\x9f\x7c\x50\x8e\x84" - "\x54\x20\x98\xd7\x6c\x40\x1e\xdb" - "\x69\x34\x78\x61\x24\x21\x9c\x8a" - "\xb3\x62\x31\x8b\x6e\xf5\x2a\x35" - "\x86\x13\xb1\x6c\x64\x2e\x41\xa5" - "\x05\xf2\x42\xba\xd2\x3a\x0d\x8e" - "\x8a\x59\x94\x3c\xcf\x36\x27\x82" - "\xc2\x45\xee\x58\xcd\x88\xb4\xec" - "\xde\xb2\x96\x0a\xaf\x38\x6f\x88" - "\xd7\xd8\xe1\xdf\xb9\x96\xa9\x0a" - "\xb1\x95\x28\x86\x20\xe9\x17\x49" - "\xa2\x29\x38\xaa\xa5\xe9\x6e\xf1" - "\x19\x27\xc0\xd5\x2a\x22\xc3\x0b" - "\xdb\x7c\x73\x10\xb9\xba\x89\x76" - "\x54\xae\x7d\x71\xb3\x93\xf6\x32" - "\xe6\x47\x43\x55\xac\xa0\x0d\xc2" - "\x93\x27\x4a\x8e\x0e\x74\x15\xc7" - "\x0b\x85\xd9\x0c\xa9\x30\x7a\x3e" - "\xea\x8f\x85\x6d\x3a\x12\x4f\x72" - "\x69\x58\x7a\x80\xbb\xb5\x97\xf3" - "\xcf\x70\xd2\x5d\xdd\x4d\x21\x79" - "\x54\x4d\xe4\x05\xe8\xbd\xc2\x62" - "\xb1\x3b\x77\x1c\xd6\x5c\xf3\xa0" - "\x79\x00\xa8\x6c\x29\xd9\x18\x24" - "\x36\xa2\x46\xc0\x96\x65\x7f\xbd" - "\x2a\xed\x36\x16\x0c\xaa\x9f\xf4" - "\xc5\xb4\xe2\x12\xed\x69\xed\x4f" - "\x26\x2c\x39\x52\x89\x98\xe7\x2c" - "\x99\xa4\x9e\xa3\x9b\x99\x46\x7a" - "\x3a\xdc\xa8\x59\xa3\xdb\xc3\x3b" - "\x95\x0d\x3b\x09\x6e\xee\x83\x5d" - "\x32\x4d\xed\xab\xfa\x98\x14\x4e" - "\xc3\x15\x45\x53\x61\xc4\x93\xbd" - "\x90\xf4\x99\x95\x4c\xe6\x76\x92" - "\x29\x90\x46\x30\x92\x69\x7d\x13" - "\xf2\xa5\xcd\x69\x49\x44\xb2\x0f" - "\x63\x40\x36\x5f\x09\xe2\x78\xf8" - "\x91\xe3\xe2\xfa\x10\xf7\xc8\x24" - "\xa8\x89\x32\x5c\x37\x25\x1d\xb2" - "\xea\x17\x8a\x0a\xa9\x64\xc3\x7c" - "\x3c\x7c\xbd\xc6\x79\x34\xe7\xe2" - "\x85\x8e\xbf\xf8\xde\x92\xa0\xae" - "\x20\xc4\xf6\xbb\x1f\x38\x19\x0e" - "\xe8\x79\x9c\xa1\x23\xe9\x54\x7e" - "\x37\x2f\xe2\x94\x32\xaf\xa0\x23" - "\x49\xe4\xc0\xb3\xac\x00\x8f\x36" - "\x05\xc4\xa6\x96\xec\x05\x98\x4f" - "\x96\x67\x57\x1f\x20\x86\x1b\x2d" - "\x69\xe4\x29\x93\x66\x5f\xaf\x6b" - "\x88\x26\x2c\x67\x02\x4b\x52\xd0" - "\x83\x7a\x43\x1f\xc0\x71\x15\x25" - "\x77\x65\x08\x60\x11\x76\x4c\x8d" - "\xed\xa9\x27\xc6\xb1\x2a\x2c\x6a" - "\x4a\x97\xf5\xc6\xb7\x70\x42\xd3" - "\x03\xd1\x24\x95\xec\x6d\xab\x38" - "\x72\xce\xe2\x8b\x33\xd7\x51\x09" - "\xdc\x45\xe0\x09\x96\x32\xf3\xc4" - "\x84\xdc\x73\x73\x2d\x1b\x11\x98" - "\xc5\x0e\x69\x28\x94\xc7\xb5\x4d" - "\xc8\x8a\xd0\xaa\x13\x2e\x18\x74" - "\xdd\xd1\x1e\xf3\x90\xe8\xfc\x9a" - "\x72\x4a\x0e\xd1\xe4\xfb\x0d\x96" - "\xd1\x0c\x79\x85\x1b\x1c\xfe\xe1" - "\x62\x8f\x7a\x73\x32\xab\xc8\x18" - "\x69\xe3\x34\x30\xdf\x13\xa6\xe5" - "\xe8\x0e\x67\x7f\x81\x11\xb4\x60" - "\xc7\xbd\x79\x65\x50\xdc\xc4\x5b" - "\xde\x39\xa4\x01\x72\x63\xf3\xd1" - "\x64\x4e\xdf\xfc\x27\x92\x37\x0d" - "\x57\xcd\x11\x4f\x11\x04\x8e\x1d" - "\x16\xf7\xcd\x92\x9a\x99\x30\x14" - "\xf1\x7c\x67\x1b\x1f\x41\x0b\xe8" - "\x32\xe8\xb8\xc1\x4f\x54\x86\x4f" - "\xe5\x79\x81\x73\xcd\x43\x59\x68" - "\x73\x02\x3b\x78\x21\x72\x43\x00" - "\x49\x17\xf7\x00\xaf\x68\x24\x53" - "\x05\x0a\xc3\x33\xe0\x33\x3f\x69" - "\xd2\x84\x2f\x0b\xed\xde\x04\xf4" - "\x11\x94\x13\x69\x51\x09\x28\xde" - "\x57\x5c\xef\xdc\x9a\x49\x1c\x17" - "\x97\xf3\x96\xc1\x7f\x5d\x2e\x7d" - "\x55\xb8\xb3\x02\x09\xb3\x1f\xe7" - "\xc9\x8d\xa3\x36\x34\x8a\x77\x13" - "\x30\x63\x4c\xa5\xcd\xc3\xe0\x7e" - "\x05\xa1\x7b\x0c\xcb\x74\x47\x31" - "\x62\x03\x43\xf1\x87\xb4\xb0\x85" - "\x87\x8e\x4b\x25\xc7\xcf\xae\x4b" - "\x36\x46\x3e\x62\xbc\x6f\xeb\x5f" - "\x73\xac\xe6\x07\xee\xc1\xa1\xd6" - "\xc4\xab\xc9\xd6\x89\x45\xe1\xf1" - "\x04\x4e\x1a\x6f\xbb\x4f\x3a\xa3" - "\xa0\xcb\xa3\x0a\xd8\x71\x35\x55" - "\xe4\xbc\x2e\x04\x06\xe6\xff\x5b" - "\x1c\xc0\x11\x7c\xc5\x17\xf3\x38" - "\xcf\xe9\xba\x0f\x0e\xef\x02\xc2" - "\x8d\xc6\xbc\x4b\x67\x20\x95\xd7" - "\x2c\x45\x5b\x86\x44\x8c\x6f\x2e" - "\x7e\x9f\x1c\x77\xba\x6b\x0e\xa3" - "\x69\xdc\xab\x24\x57\x60\x47\xc1" - "\xd1\xa5\x9d\x23\xe6\xb1\x37\xfe" - "\x93\xd2\x4c\x46\xf9\x0c\xc6\xfb" - "\xd6\x9d\x99\x69\xab\x7a\x07\x0c" - "\x65\xe7\xc4\x08\x96\xe2\xa5\x01" - "\x3f\x46\x07\x05\x7e\xe8\x9a\x90" - "\x50\xdc\xe9\x7a\xea\xa1\x39\x6e" - "\x66\xe4\x6f\xa5\x5f\xb2\xd9\x5b" - "\xf5\xdb\x2a\x32\xf0\x11\x6f\x7c" - "\x26\x10\x8f\x3d\x80\xe9\x58\xf7" - "\xe0\xa8\x57\xf8\xdb\x0e\xce\x99" - "\x63\x19\x3d\xd5\xec\x1b\x77\x69" - "\x98\xf6\xe4\x5f\x67\x17\x4b\x09" - "\x85\x62\x82\x70\x18\xe2\x9a\x78" - "\xe2\x62\xbd\xb4\xf1\x42\xc6\xfb" - "\x08\xd0\xbd\xeb\x4e\x09\xf2\xc8" - "\x1e\xdc\x3d\x32\x21\x56\x9c\x4f" - "\x35\xf3\x61\x06\x72\x84\xc4\x32" - "\xf2\xf1\xfa\x0b\x2f\xc3\xdb\x02" - "\x04\xc2\xde\x57\x64\x60\x8d\xcf" - "\xcb\x86\x5d\x97\x3e\xb1\x9c\x01" - "\xd6\x28\x8f\x99\xbc\x46\xeb\x05" - "\xaf\x7e\xb8\x21\x2a\x56\x85\x1c" - "\xb3\x71\xa0\xde\xca\x96\xf1\x78" - "\x49\xa2\x99\x81\x80\x5c\x01\xf5" - "\xa0\xa2\x56\x63\xe2\x70\x07\xa5" - "\x95\xd6\x85\xeb\x36\x9e\xa9\x51" - "\x66\x56\x5f\x1d\x02\x19\xe2\xf6" - "\x4f\x73\x38\x09\x75\x64\x48\xe0" - "\xf1\x7e\x0e\xe8\x9d\xf9\xed\x94" - "\xfe\x16\x26\x62\x49\x74\xf4\xb0" - "\xd4\xa9\x6c\xb0\xfd\x53\xe9\x81" - "\xe0\x7a\xbf\xcf\xb5\xc4\x01\x81" - "\x79\x99\x77\x01\x3b\xe9\xa2\xb6" - "\xe6\x6a\x8a\x9e\x56\x1c\x8d\x1e" - "\x8f\x06\x55\x2c\x6c\xdc\x92\x87" - "\x64\x3b\x4b\x19\xa1\x13\x64\x1d" - "\x4a\xe9\xc0\x00\xb8\x95\xef\x6b" - "\x1a\x86\x6d\x37\x52\x02\xc2\xe0" - "\xc8\xbb\x42\x0c\x02\x21\x4a\xc9" - "\xef\xa0\x54\xe4\x5e\x16\x53\x81" - "\x70\x62\x10\xaf\xde\xb8\xb5\xd3" - "\xe8\x5e\x6c\xc3\x8a\x3e\x18\x07" - "\xf2\x2f\x7d\xa7\xe1\x3d\x4e\xb4" - "\x26\xa7\xa3\x93\x86\xb2\x04\x1e" - "\x53\x5d\x86\xd6\xde\x65\xca\xe3" - "\x4e\xc1\xcf\xef\xc8\x70\x1b\x83" - "\x13\xdd\x18\x8b\x0d\x76\xd2\xf6" - "\x37\x7a\x93\x7a\x50\x11\x9f\x96" - "\x86\x25\xfd\xac\xdc\xbe\x18\x93" - "\x19\x6b\xec\x58\x4f\xb9\x75\xa7" - "\xdd\x3f\x2f\xec\xc8\x5a\x84\xab" - "\xd5\xe4\x8a\x07\xf6\x4d\x23\xd6" - "\x03\xfb\x03\x6a\xea\x66\xbf\xd4" - "\xb1\x34\xfb\x78\xe9\x55\xdc\x7c" - "\x3d\x9c\xe5\x9a\xac\xc3\x7a\x80" - "\x24\x6d\xa0\xef\x25\x7c\xb7\xea" - "\xce\x4d\x5f\x18\x60\xce\x87\x22" - "\x66\x2f\xd5\xdd\xdd\x02\x21\x75" - "\x82\xa0\x1f\x58\xc6\xd3\x62\xf7" - "\x32\xd8\xaf\x1e\x07\x77\x51\x96" - "\xd5\x6b\x1e\x7e\x80\x02\xe8\x67" - "\xea\x17\x0b\x10\xd2\x3f\x28\x25" - "\x4f\x05\x77\x02\x14\x69\xf0\x2c" - "\xbe\x0c\xf1\x74\x30\xd1\xb9\x9b" - "\xfc\x8c\xbb\x04\x16\xd9\xba\xc3" - "\xbc\x91\x8a\xc4\x30\xa4\xb0\x12" - "\x4c\x21\x87\xcb\xc9\x1d\x16\x96" - "\x07\x6f\x23\x54\xb9\x6f\x79\xe5" - "\x64\xc0\x64\xda\xb1\xae\xdd\x60" - "\x6c\x1a\x9d\xd3\x04\x8e\x45\xb0" - "\x92\x61\xd0\x48\x81\xed\x5e\x1d" - "\xa0\xc9\xa4\x33\xc7\x13\x51\x5d" - "\x7f\x83\x73\xb6\x70\x18\x65\x3e" - "\x2f\x0e\x7a\x12\x39\x98\xab\xd8" - "\x7e\x6f\xa3\xd1\xba\x56\xad\xbd" - "\xf0\x03\x01\x1c\x85\x35\x9f\xeb" - "\x19\x63\xa1\xaf\xfe\x2d\x35\x50" - "\x39\xa0\x65\x7c\x95\x7e\x6b\xfe" - "\xc1\xac\x07\x7c\x98\x4f\xbe\x57" - "\xa7\x22\xec\xe2\x7e\x29\x09\x53" - "\xe8\xbf\xb4\x7e\x3f\x8f\xfc\x14" - "\xce\x54\xf9\x18\x58\xb5\xff\x44" - "\x05\x9d\xce\x1b\xb6\x82\x23\xc8" - "\x2e\xbc\x69\xbb\x4a\x29\x0f\x65" - "\x94\xf0\x63\x06\x0e\xef\x8c\xbd" - "\xff\xfd\xb0\x21\x6e\x57\x05\x75" - "\xda\xd5\xc4\xeb\x8d\x32\xf7\x50" - "\xd3\x6f\x22\xed\x5f\x8e\xa2\x5b" - "\x80\x8c\xc8\x78\x40\x24\x4b\x89" - "\x30\xce\x7a\x97\x0e\xc4\xaf\xef" - "\x9b\xb4\xcd\x66\x74\x14\x04\x2b" - "\xf7\xce\x0b\x1c\x6e\xc2\x78\x8c" - "\xca\xc5\xd0\x1c\x95\x4a\x91\x2d" - "\xa7\x20\xeb\x86\x52\xb7\x67\xd8" - "\x0c\xd6\x04\x14\xde\x51\x74\x75" - "\xe7\x11\xb4\x87\xa3\x3d\x2d\xad" - "\x4f\xef\xa0\x0f\x70\x00\x6d\x13" - "\x19\x1d\x41\x50\xe9\xd8\xf0\x32" - "\x71\xbc\xd3\x11\xf2\xac\xbe\xaf" - "\x75\x46\x65\x4e\x07\x34\x37\xa3" - "\x89\xfe\x75\xd4\x70\x4c\xc6\x3f" - "\x69\x24\x0e\x38\x67\x43\x8c\xde" - "\x06\xb5\xb8\xe7\xc4\xf0\x41\x8f" - "\xf0\xbd\x2f\x0b\xb9\x18\xf8\xde" - "\x64\xb1\xdb\xee\x00\x50\x77\xe1" - "\xc7\xff\xa6\xfa\xdd\x70\xf4\xe3" - "\x93\xe9\x77\x35\x3d\x4b\x2f\x2b" - "\x6d\x55\xf0\xfc\x88\x54\x4e\x89" - "\xc1\x8a\x23\x31\x2d\x14\x2a\xb8" - "\x1b\x15\xdd\x9e\x6e\x7b\xda\x05" - "\x91\x7d\x62\x64\x96\x72\xde\xfc" - "\xc1\xec\xf0\x23\x51\x6f\xdb\x5b" - "\x1d\x08\x57\xce\x09\xb8\xf6\xcd" - "\x8d\x95\xf2\x20\xbf\x0f\x20\x57" - "\x98\x81\x84\x4f\x15\x5c\x76\xe7" - "\x3e\x0a\x3a\x6c\xc4\x8a\xbe\x78" - "\x74\x77\xc3\x09\x4b\x5d\x48\xe4" - "\xc8\xcb\x0b\xea\x17\x28\xcf\xcf" - "\x31\x32\x44\xa4\xe5\x0e\x1a\x98" - "\x94\xc4\xf0\xff\xae\x3e\x44\xe8" - "\xa5\xb3\xb5\x37\x2f\xe8\xaf\x6f" - "\x28\xc1\x37\x5f\x31\xd2\xb9\x33" - "\xb1\xb2\x52\x94\x75\x2c\x29\x59" - "\x06\xc2\x25\xe8\x71\x65\x4e\xed" - "\xc0\x9c\xb1\xbb\x25\xdc\x6c\xe7" - "\x4b\xa5\x7a\x54\x7a\x60\xff\x7a" - "\xe0\x50\x40\x96\x35\x63\xe4\x0b" - "\x76\xbd\xa4\x65\x00\x1b\x57\x88" - "\xae\xed\x39\x88\x42\x11\x3c\xed" - "\x85\x67\x7d\xb9\x68\x82\xe9\x43" - "\x3c\x47\x53\xfa\xe8\xf8\x9f\x1f" - "\x9f\xef\x0f\xf7\x30\xd9\x30\x0e" - "\xb9\x9f\x69\x18\x2f\x7e\xf8\xf8" - "\xf8\x8c\x0f\xd4\x02\x4d\xea\xcd" - "\x0a\x9c\x6f\x71\x6d\x5a\x4c\x60" - "\xce\x20\x56\x32\xc6\xc5\x99\x1f" - "\x09\xe6\x4e\x18\x1a\x15\x13\xa8" - "\x7d\xb1\x6b\xc0\xb2\x6d\xf8\x26" - "\x66\xf8\x3d\x18\x74\x70\x66\x7a" - "\x34\x17\xde\xba\x47\xf1\x06\x18" - "\xcb\xaf\xeb\x4a\x1e\x8f\xa7\x77" - "\xe0\x3b\x78\x62\x66\xc9\x10\xea" - "\x1f\xb7\x29\x0a\x45\xa1\x1d\x1e" - "\x1d\xe2\x65\x61\x50\x9c\xd7\x05" - "\xf2\x0b\x5b\x12\x61\x02\xc8\xe5" - "\x63\x4f\x20\x0c\x07\x17\x33\x5e" - "\x03\x9a\x53\x0f\x2e\x55\xfe\x50" - "\x43\x7d\xd0\xb6\x7e\x5a\xda\xae" - "\x58\xef\x15\xa9\x83\xd9\x46\xb1" - "\x42\xaa\xf5\x02\x6c\xce\x92\x06" - "\x1b\xdb\x66\x45\x91\x79\xc2\x2d" - "\xe6\x53\xd3\x14\xfd\xbb\x44\x63" - "\xc6\xd7\x3d\x7a\x0c\x75\x78\x9d" - "\x5c\xa6\x39\xb3\xe5\x63\xca\x8b" - "\xfe\xd3\xef\x60\x83\xf6\x8e\x70" - "\xb6\x67\xc7\x77\xed\x23\xef\x4c" - "\xf0\xed\x2d\x07\x59\x6f\xc1\x01" - "\x34\x37\x08\xab\xd9\x1f\x09\xb1" - "\xce\x5b\x17\xff\x74\xf8\x9c\xd5" - "\x2c\x56\x39\x79\x0f\x69\x44\x75" - "\x58\x27\x01\xc4\xbf\xa7\xa1\x1d" - "\x90\x17\x77\x86\x5a\x3f\xd9\xd1" - "\x0e\xa0\x10\xf8\xec\x1e\xa5\x7f" - "\x5e\x36\xd1\xe3\x04\x2c\x70\xf7" - "\x8e\xc0\x98\x2f\x6c\x94\x2b\x41" - "\xb7\x60\x00\xb7\x2e\xb8\x02\x8d" - "\xb8\xb0\xd3\x86\xba\x1d\xd7\x90" - "\xd6\xb6\xe1\xfc\xd7\xd8\x28\x06" - "\x63\x9b\xce\x61\x24\x79\xc0\x70" - "\x52\xd0\xb6\xd4\x28\x95\x24\x87" - "\x03\x1f\xb7\x9a\xda\xa3\xfb\x52" - "\x5b\x68\xe7\x4c\x8c\x24\xe1\x42" - "\xf7\xd5\xfd\xad\x06\x32\x9f\xba" - "\xc1\xfc\xdd\xc6\xfc\xfc\xb3\x38" - "\x74\x56\x58\x40\x02\x37\x52\x2c" - "\x55\xcc\xb3\x9e\x7a\xe9\xd4\x38" - "\x41\x5e\x0c\x35\xe2\x11\xd1\x13" - "\xf8\xb7\x8d\x72\x6b\x22\x2a\xb0" - "\xdb\x08\xba\x35\xb9\x3f\xc8\xd3" - "\x24\x90\xec\x58\xd2\x09\xc7\x2d" - "\xed\x38\x80\x36\x72\x43\x27\x49" - "\x4a\x80\x8a\xa2\xe8\xd3\xda\x30" - "\x7d\xb6\x82\x37\x86\x92\x86\x3e" - "\x08\xb2\x28\x5a\x55\x44\x24\x7d" - "\x40\x48\x8a\xb6\x89\x58\x08\xa0" - "\xd6\x6d\x3a\x17\xbf\xf6\x54\xa2" - "\xf5\xd3\x8c\x0f\x78\x12\x57\x8b" - "\xd5\xc2\xfd\x58\x5b\x7f\x38\xe3" - "\xcc\xb7\x7c\x48\xb3\x20\xe8\x81" - "\x14\x32\x45\x05\xe0\xdb\x9f\x75" - "\x85\xb4\x6a\xfc\x95\xe3\x54\x22" - "\x12\xee\x30\xfe\xd8\x30\xef\x34" - "\x50\xab\x46\x30\x98\x2f\xb7\xc0" - "\x15\xa2\x83\xb6\xf2\x06\x21\xa2" - "\xc3\x26\x37\x14\xd1\x4d\xb5\x10" - "\x52\x76\x4d\x6a\xee\xb5\x2b\x15" - "\xb7\xf9\x51\xe8\x2a\xaf\xc7\xfa" - "\x77\xaf\xb0\x05\x4d\xd1\x68\x8e" - "\x74\x05\x9f\x9d\x93\xa5\x3e\x7f" - "\x4e\x5f\x9d\xcb\x09\xc7\x83\xe3" - "\x02\x9d\x27\x1f\xef\x85\x05\x8d" - "\xec\x55\x88\x0f\x0d\x7c\x4c\xe8" - "\xa1\x75\xa0\xd8\x06\x47\x14\xef" - "\xaa\x61\xcf\x26\x15\xad\xd8\xa3" - "\xaa\x75\xf2\x78\x4a\x5a\x61\xdf" - "\x8b\xc7\x04\xbc\xb2\x32\xd2\x7e" - "\x42\xee\xb4\x2f\x51\xff\x7b\x2e" - "\xd3\x02\xe8\xdc\x5d\x0d\x50\xdc" - "\xae\xb7\x46\xf9\xa8\xe6\xd0\x16" - "\xcc\xe6\x2c\x81\xc7\xad\xe9\xf0" - "\x05\x72\x6d\x3d\x0a\x7a\xa9\x02" - "\xac\x82\x93\x6e\xb6\x1c\x28\xfc" - "\x44\x12\xfb\x73\x77\xd4\x13\x39" - "\x29\x88\x8a\xf3\x5c\xa6\x36\xa0" - "\x2a\xed\x7e\xb1\x1d\xd6\x4c\x6b" - "\x41\x01\x18\x5d\x5d\x07\x97\xa6" - "\x4b\xef\x31\x18\xea\xac\xb1\x84" - "\x21\xed\xda\x86", - .rlen = 4100, - .np = 2, - .tap = { 4064, 36 }, - }, -}; - -static struct cipher_testvec aes_ctr_dec_tv_template[] = { - { /* From RFC 3686 */ - .key = "\xae\x68\x52\xf8\x12\x10\x67\xcc" - "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" - "\x00\x00\x00\x30", - .klen = 20, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" - "\x2d\x61\x75\xa3\x26\x13\x11\xb8", - .ilen = 16, - .result = "Single block msg", - .rlen = 16, - }, { - .key = "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" - "\x43\xd6\xce\x1f\x32\x53\x91\x63" - "\x00\x6c\xb6\xdb", - .klen = 20, - .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", - .input = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" - "\x79\x0d\x41\xee\x8e\xda\xd3\x88" - "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" - "\xfc\xe6\x30\xdf\x91\x41\xbe\x28", - .ilen = 32, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .rlen = 32, - }, { - .key = "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" - "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" - "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" - "\x00\x00\x00\x48", - .klen = 28, - .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", - .input = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" - "\x4e\x79\x35\xa0\x03\xcb\xe9\x28", - .ilen = 16, - .result = "Single block msg", - .rlen = 16, - }, { - .key = "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" - "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" - "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" - "\x00\x96\xb0\x3b", - .klen = 28, - .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", - .input = "\x45\x32\x43\xfc\x60\x9b\x23\x32" - "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" - "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" - "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00", - .ilen = 32, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .rlen = 32, - }, { - .key = "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" - "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" - "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" - "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" - "\x00\x00\x00\x60", - .klen = 36, - .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", - .input = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" - "\x56\x08\x63\xdc\x71\xe3\xe0\xc0", - .ilen = 16, - .result = "Single block msg", - .rlen = 16, - }, { - .key = "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" - "\x07\x96\x36\x58\x79\xef\xf8\x86" - "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" - "\x4b\x50\x59\x0c\x87\xa2\x38\x84" - "\x00\xfa\xac\x24", - .klen = 36, - .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", - .input = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" - "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" - "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" - "\x55\x30\x83\x1d\x93\x44\xaf\x1c", - .ilen = 32, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .rlen = 32, - }, -}; - -static struct aead_testvec aes_gcm_enc_tv_template[] = { - { /* From McGrew & Viega - http://citeseer.ist.psu.edu/656989.html */ - .key = zeroed_string, - .klen = 16, - .result = "\x58\xe2\xfc\xce\xfa\x7e\x30\x61" - "\x36\x7f\x1d\x57\xa4\xe7\x45\x5a", - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 16, - .input = zeroed_string, - .ilen = 16, - .result = "\x03\x88\xda\xce\x60\xb6\xa3\x92" - "\xf3\x28\xc2\xb9\x71\xb2\xfe\x78" - "\xab\x6e\x47\xd4\x2c\xec\x13\xbd" - "\xf5\x3a\x67\xb2\x12\x57\xbd\xdf", - .rlen = 32, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08", - .klen = 16, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", - .ilen = 64, - .result = "\x42\x83\x1e\xc2\x21\x77\x74\x24" - "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" - "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" - "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" - "\x21\xd5\x14\xb2\x54\x66\x93\x1c" - "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" - "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" - "\x3d\x58\xe0\x91\x47\x3f\x59\x85" - "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6" - "\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4", - .rlen = 80, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08", - .klen = 16, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39", - .ilen = 60, - .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xab\xad\xda\xd2", - .alen = 20, - .result = "\x42\x83\x1e\xc2\x21\x77\x74\x24" - "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" - "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" - "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" - "\x21\xd5\x14\xb2\x54\x66\x93\x1c" - "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" - "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" - "\x3d\x58\xe0\x91" - "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb" - "\x94\xfa\xe9\x5a\xe7\x12\x1a\x47", - .rlen = 76, - }, { - .key = zeroed_string, - .klen = 24, - .result = "\xcd\x33\xb2\x8a\xc7\x73\xf7\x4b" - "\xa0\x0e\xd1\xf3\x12\x57\x24\x35", - .rlen = 16, - }, { - .key = zeroed_string, - .klen = 24, - .input = zeroed_string, - .ilen = 16, - .result = "\x98\xe7\x24\x7c\x07\xf0\xfe\x41" - "\x1c\x26\x7e\x43\x84\xb0\xf6\x00" - "\x2f\xf5\x8d\x80\x03\x39\x27\xab" - "\x8e\xf4\xd4\x58\x75\x14\xf0\xfb", - .rlen = 32, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08" - "\xfe\xff\xe9\x92\x86\x65\x73\x1c", - .klen = 24, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", - .ilen = 64, - .result = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" - "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" - "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" - "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" - "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" - "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" - "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" - "\xcc\xda\x27\x10\xac\xad\xe2\x56" - "\x99\x24\xa7\xc8\x58\x73\x36\xbf" - "\xb1\x18\x02\x4d\xb8\x67\x4a\x14", - .rlen = 80, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08" - "\xfe\xff\xe9\x92\x86\x65\x73\x1c", - .klen = 24, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39", - .ilen = 60, - .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xab\xad\xda\xd2", - .alen = 20, - .result = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" - "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" - "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" - "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" - "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" - "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" - "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" - "\xcc\xda\x27\x10" - "\x25\x19\x49\x8e\x80\xf1\x47\x8f" - "\x37\xba\x55\xbd\x6d\x27\x61\x8c", - .rlen = 76, - .np = 2, - .tap = { 32, 28 }, - .anp = 2, - .atap = { 8, 12 } - }, { - .key = zeroed_string, - .klen = 32, - .result = "\x53\x0f\x8a\xfb\xc7\x45\x36\xb9" - "\xa9\x63\xb4\xf1\xc4\xcb\x73\x8b", - .rlen = 16, - } -}; - -static struct aead_testvec aes_gcm_dec_tv_template[] = { - { /* From McGrew & Viega - http://citeseer.ist.psu.edu/656989.html */ - .key = zeroed_string, - .klen = 32, - .input = "\xce\xa7\x40\x3d\x4d\x60\x6b\x6e" - "\x07\x4e\xc5\xd3\xba\xf3\x9d\x18" - "\xd0\xd1\xc8\xa7\x99\x99\x6b\xf0" - "\x26\x5b\x98\xb5\xd4\x8a\xb9\x19", - .ilen = 32, - .result = zeroed_string, - .rlen = 16, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08" - "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08", - .klen = 32, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\x52\x2d\xc1\xf0\x99\x56\x7d\x07" - "\xf4\x7f\x37\xa3\x2a\x84\x42\x7d" - "\x64\x3a\x8c\xdc\xbf\xe5\xc0\xc9" - "\x75\x98\xa2\xbd\x25\x55\xd1\xaa" - "\x8c\xb0\x8e\x48\x59\x0d\xbb\x3d" - "\xa7\xb0\x8b\x10\x56\x82\x88\x38" - "\xc5\xf6\x1e\x63\x93\xba\x7a\x0a" - "\xbc\xc9\xf6\x62\x89\x80\x15\xad" - "\xb0\x94\xda\xc5\xd9\x34\x71\xbd" - "\xec\x1a\x50\x22\x70\xe3\xcc\x6c", - .ilen = 80, - .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", - .rlen = 64, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08" - "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08", - .klen = 32, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\x52\x2d\xc1\xf0\x99\x56\x7d\x07" - "\xf4\x7f\x37\xa3\x2a\x84\x42\x7d" - "\x64\x3a\x8c\xdc\xbf\xe5\xc0\xc9" - "\x75\x98\xa2\xbd\x25\x55\xd1\xaa" - "\x8c\xb0\x8e\x48\x59\x0d\xbb\x3d" - "\xa7\xb0\x8b\x10\x56\x82\x88\x38" - "\xc5\xf6\x1e\x63\x93\xba\x7a\x0a" - "\xbc\xc9\xf6\x62" - "\x76\xfc\x6e\xce\x0f\x4e\x17\x68" - "\xcd\xdf\x88\x53\xbb\x2d\x55\x1b", - .ilen = 76, - .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xab\xad\xda\xd2", - .alen = 20, - .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39", - .rlen = 60, - .np = 2, - .tap = { 48, 28 }, - .anp = 3, - .atap = { 8, 8, 4 } - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08", - .klen = 16, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\x42\x83\x1e\xc2\x21\x77\x74\x24" - "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" - "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" - "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" - "\x21\xd5\x14\xb2\x54\x66\x93\x1c" - "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" - "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" - "\x3d\x58\xe0\x91\x47\x3f\x59\x85" - "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6" - "\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4", - .ilen = 80, - .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", - .rlen = 64, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08", - .klen = 16, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\x42\x83\x1e\xc2\x21\x77\x74\x24" - "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" - "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" - "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" - "\x21\xd5\x14\xb2\x54\x66\x93\x1c" - "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" - "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" - "\x3d\x58\xe0\x91" - "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb" - "\x94\xfa\xe9\x5a\xe7\x12\x1a\x47", - .ilen = 76, - .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xab\xad\xda\xd2", - .alen = 20, - .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39", - .rlen = 60, - }, { - .key = zeroed_string, - .klen = 24, - .input = "\x98\xe7\x24\x7c\x07\xf0\xfe\x41" - "\x1c\x26\x7e\x43\x84\xb0\xf6\x00" - "\x2f\xf5\x8d\x80\x03\x39\x27\xab" - "\x8e\xf4\xd4\x58\x75\x14\xf0\xfb", - .ilen = 32, - .result = zeroed_string, - .rlen = 16, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08" - "\xfe\xff\xe9\x92\x86\x65\x73\x1c", - .klen = 24, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" - "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" - "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" - "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" - "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" - "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" - "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" - "\xcc\xda\x27\x10\xac\xad\xe2\x56" - "\x99\x24\xa7\xc8\x58\x73\x36\xbf" - "\xb1\x18\x02\x4d\xb8\x67\x4a\x14", - .ilen = 80, - .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", - .rlen = 64, - }, { - .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" - "\x6d\x6a\x8f\x94\x67\x30\x83\x08" - "\xfe\xff\xe9\x92\x86\x65\x73\x1c", - .klen = 24, - .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" - "\xde\xca\xf8\x88", - .input = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" - "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" - "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" - "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" - "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" - "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" - "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" - "\xcc\xda\x27\x10" - "\x25\x19\x49\x8e\x80\xf1\x47\x8f" - "\x37\xba\x55\xbd\x6d\x27\x61\x8c", - .ilen = 76, - .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xfe\xed\xfa\xce\xde\xad\xbe\xef" - "\xab\xad\xda\xd2", - .alen = 20, - .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" - "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" - "\x86\xa7\xa9\x53\x15\x34\xf7\xda" - "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" - "\x1c\x3c\x0c\x95\x95\x68\x09\x53" - "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" - "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" - "\xba\x63\x7b\x39", - .rlen = 60, - } -}; - -static struct aead_testvec aes_ccm_enc_tv_template[] = { - { /* From RFC 3610 */ - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x03\x02\x01\x00" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", - .alen = 8, - .input = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e", - .ilen = 23, - .result = "\x58\x8c\x97\x9a\x61\xc6\x63\xd2" - "\xf0\x66\xd0\xc2\xc0\xf9\x89\x80" - "\x6d\x5f\x6b\x61\xda\xc3\x84\x17" - "\xe8\xd1\x2c\xfd\xf9\x26\xe0", - .rlen = 31, - }, { - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x07\x06\x05\x04" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b", - .alen = 12, - .input = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" - "\x14\x15\x16\x17\x18\x19\x1a\x1b" - "\x1c\x1d\x1e\x1f", - .ilen = 20, - .result = "\xdc\xf1\xfb\x7b\x5d\x9e\x23\xfb" - "\x9d\x4e\x13\x12\x53\x65\x8a\xd8" - "\x6e\xbd\xca\x3e\x51\xe8\x3f\x07" - "\x7d\x9c\x2d\x93", - .rlen = 28, - }, { - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x0b\x0a\x09\x08" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", - .alen = 8, - .input = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20", - .ilen = 25, - .result = "\x82\x53\x1a\x60\xcc\x24\x94\x5a" - "\x4b\x82\x79\x18\x1a\xb5\xc8\x4d" - "\xf2\x1c\xe7\xf9\xb7\x3f\x42\xe1" - "\x97\xea\x9c\x07\xe5\x6b\x5e\xb1" - "\x7e\x5f\x4e", - .rlen = 35, - }, { - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x0c\x0b\x0a\x09" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b", - .alen = 12, - .input = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" - "\x14\x15\x16\x17\x18\x19\x1a\x1b" - "\x1c\x1d\x1e", - .ilen = 19, - .result = "\x07\x34\x25\x94\x15\x77\x85\x15" - "\x2b\x07\x40\x98\x33\x0a\xbb\x14" - "\x1b\x94\x7b\x56\x6a\xa9\x40\x6b" - "\x4d\x99\x99\x88\xdd", - .rlen = 29, - }, { - .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" - "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", - .klen = 16, - .iv = "\x01\x00\x33\x56\x8e\xf7\xb2\x63" - "\x3c\x96\x96\x76\x6c\xfa\x00\x00", - .assoc = "\x63\x01\x8f\x76\xdc\x8a\x1b\xcb", - .alen = 8, - .input = "\x90\x20\xea\x6f\x91\xbd\xd8\x5a" - "\xfa\x00\x39\xba\x4b\xaf\xf9\xbf" - "\xb7\x9c\x70\x28\x94\x9c\xd0\xec", - .ilen = 24, - .result = "\x4c\xcb\x1e\x7c\xa9\x81\xbe\xfa" - "\xa0\x72\x6c\x55\xd3\x78\x06\x12" - "\x98\xc8\x5c\x92\x81\x4a\xbc\x33" - "\xc5\x2e\xe8\x1d\x7d\x77\xc0\x8a", - .rlen = 32, - }, { - .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" - "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", - .klen = 16, - .iv = "\x01\x00\xd5\x60\x91\x2d\x3f\x70" - "\x3c\x96\x96\x76\x6c\xfa\x00\x00", - .assoc = "\xcd\x90\x44\xd2\xb7\x1f\xdb\x81" - "\x20\xea\x60\xc0", - .alen = 12, - .input = "\x64\x35\xac\xba\xfb\x11\xa8\x2e" - "\x2f\x07\x1d\x7c\xa4\xa5\xeb\xd9" - "\x3a\x80\x3b\xa8\x7f", - .ilen = 21, - .result = "\x00\x97\x69\xec\xab\xdf\x48\x62" - "\x55\x94\xc5\x92\x51\xe6\x03\x57" - "\x22\x67\x5e\x04\xc8\x47\x09\x9e" - "\x5a\xe0\x70\x45\x51", - .rlen = 29, - }, { - .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" - "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", - .klen = 16, - .iv = "\x01\x00\x42\xff\xf8\xf1\x95\x1c" - "\x3c\x96\x96\x76\x6c\xfa\x00\x00", - .assoc = "\xd8\x5b\xc7\xe6\x9f\x94\x4f\xb8", - .alen = 8, - .input = "\x8a\x19\xb9\x50\xbc\xf7\x1a\x01" - "\x8e\x5e\x67\x01\xc9\x17\x87\x65" - "\x98\x09\xd6\x7d\xbe\xdd\x18", - .ilen = 23, - .result = "\xbc\x21\x8d\xaa\x94\x74\x27\xb6" - "\xdb\x38\x6a\x99\xac\x1a\xef\x23" - "\xad\xe0\xb5\x29\x39\xcb\x6a\x63" - "\x7c\xf9\xbe\xc2\x40\x88\x97\xc6" - "\xba", - .rlen = 33, - }, -}; - -static struct aead_testvec aes_ccm_dec_tv_template[] = { - { /* From RFC 3610 */ - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x03\x02\x01\x00" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", - .alen = 8, - .input = "\x58\x8c\x97\x9a\x61\xc6\x63\xd2" - "\xf0\x66\xd0\xc2\xc0\xf9\x89\x80" - "\x6d\x5f\x6b\x61\xda\xc3\x84\x17" - "\xe8\xd1\x2c\xfd\xf9\x26\xe0", - .ilen = 31, - .result = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e", - .rlen = 23, - }, { - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x07\x06\x05\x04" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b", - .alen = 12, - .input = "\xdc\xf1\xfb\x7b\x5d\x9e\x23\xfb" - "\x9d\x4e\x13\x12\x53\x65\x8a\xd8" - "\x6e\xbd\xca\x3e\x51\xe8\x3f\x07" - "\x7d\x9c\x2d\x93", - .ilen = 28, - .result = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" - "\x14\x15\x16\x17\x18\x19\x1a\x1b" - "\x1c\x1d\x1e\x1f", - .rlen = 20, - }, { - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x0b\x0a\x09\x08" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", - .alen = 8, - .input = "\x82\x53\x1a\x60\xcc\x24\x94\x5a" - "\x4b\x82\x79\x18\x1a\xb5\xc8\x4d" - "\xf2\x1c\xe7\xf9\xb7\x3f\x42\xe1" - "\x97\xea\x9c\x07\xe5\x6b\x5e\xb1" - "\x7e\x5f\x4e", - .ilen = 35, - .result = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20", - .rlen = 25, - }, { - .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", - .klen = 16, - .iv = "\x01\x00\x00\x00\x0c\x0b\x0a\x09" - "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", - .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b", - .alen = 12, - .input = "\x07\x34\x25\x94\x15\x77\x85\x15" - "\x2b\x07\x40\x98\x33\x0a\xbb\x14" - "\x1b\x94\x7b\x56\x6a\xa9\x40\x6b" - "\x4d\x99\x99\x88\xdd", - .ilen = 29, - .result = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" - "\x14\x15\x16\x17\x18\x19\x1a\x1b" - "\x1c\x1d\x1e", - .rlen = 19, - }, { - .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" - "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", - .klen = 16, - .iv = "\x01\x00\x33\x56\x8e\xf7\xb2\x63" - "\x3c\x96\x96\x76\x6c\xfa\x00\x00", - .assoc = "\x63\x01\x8f\x76\xdc\x8a\x1b\xcb", - .alen = 8, - .input = "\x4c\xcb\x1e\x7c\xa9\x81\xbe\xfa" - "\xa0\x72\x6c\x55\xd3\x78\x06\x12" - "\x98\xc8\x5c\x92\x81\x4a\xbc\x33" - "\xc5\x2e\xe8\x1d\x7d\x77\xc0\x8a", - .ilen = 32, - .result = "\x90\x20\xea\x6f\x91\xbd\xd8\x5a" - "\xfa\x00\x39\xba\x4b\xaf\xf9\xbf" - "\xb7\x9c\x70\x28\x94\x9c\xd0\xec", - .rlen = 24, - }, { - .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" - "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", - .klen = 16, - .iv = "\x01\x00\xd5\x60\x91\x2d\x3f\x70" - "\x3c\x96\x96\x76\x6c\xfa\x00\x00", - .assoc = "\xcd\x90\x44\xd2\xb7\x1f\xdb\x81" - "\x20\xea\x60\xc0", - .alen = 12, - .input = "\x00\x97\x69\xec\xab\xdf\x48\x62" - "\x55\x94\xc5\x92\x51\xe6\x03\x57" - "\x22\x67\x5e\x04\xc8\x47\x09\x9e" - "\x5a\xe0\x70\x45\x51", - .ilen = 29, - .result = "\x64\x35\xac\xba\xfb\x11\xa8\x2e" - "\x2f\x07\x1d\x7c\xa4\xa5\xeb\xd9" - "\x3a\x80\x3b\xa8\x7f", - .rlen = 21, - }, { - .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" - "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", - .klen = 16, - .iv = "\x01\x00\x42\xff\xf8\xf1\x95\x1c" - "\x3c\x96\x96\x76\x6c\xfa\x00\x00", - .assoc = "\xd8\x5b\xc7\xe6\x9f\x94\x4f\xb8", - .alen = 8, - .input = "\xbc\x21\x8d\xaa\x94\x74\x27\xb6" - "\xdb\x38\x6a\x99\xac\x1a\xef\x23" - "\xad\xe0\xb5\x29\x39\xcb\x6a\x63" - "\x7c\xf9\xbe\xc2\x40\x88\x97\xc6" - "\xba", - .ilen = 33, - .result = "\x8a\x19\xb9\x50\xbc\xf7\x1a\x01" - "\x8e\x5e\x67\x01\xc9\x17\x87\x65" - "\x98\x09\xd6\x7d\xbe\xdd\x18", - .rlen = 23, - }, -}; - -/* Cast5 test vectors from RFC 2144 */ -#define CAST5_ENC_TEST_VECTORS 3 -#define CAST5_DEC_TEST_VECTORS 3 - -static struct cipher_testvec cast5_enc_tv_template[] = { - { - .key = "\x01\x23\x45\x67\x12\x34\x56\x78" - "\x23\x45\x67\x89\x34\x56\x78\x9a", - .klen = 16, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .ilen = 8, - .result = "\x23\x8b\x4f\xe5\x84\x7e\x44\xb2", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x12\x34\x56\x78" - "\x23\x45", - .klen = 10, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .ilen = 8, - .result = "\xeb\x6a\x71\x1a\x2c\x02\x27\x1b", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x12", - .klen = 5, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .ilen = 8, - .result = "\x7a\xc8\x16\xd1\x6e\x9b\x30\x2e", - .rlen = 8, - }, -}; - -static struct cipher_testvec cast5_dec_tv_template[] = { - { - .key = "\x01\x23\x45\x67\x12\x34\x56\x78" - "\x23\x45\x67\x89\x34\x56\x78\x9a", - .klen = 16, - .input = "\x23\x8b\x4f\xe5\x84\x7e\x44\xb2", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x12\x34\x56\x78" - "\x23\x45", - .klen = 10, - .input = "\xeb\x6a\x71\x1a\x2c\x02\x27\x1b", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x12", - .klen = 5, - .input = "\x7a\xc8\x16\xd1\x6e\x9b\x30\x2e", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .rlen = 8, - }, -}; - -/* - * ARC4 test vectors from OpenSSL - */ -#define ARC4_ENC_TEST_VECTORS 7 -#define ARC4_DEC_TEST_VECTORS 7 - -static struct cipher_testvec arc4_enc_tv_template[] = { - { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .ilen = 8, - .result = "\x75\xb7\x87\x80\x99\xe0\xc5\x96", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 8, - .result = "\x74\x94\xc2\xe7\x10\x4b\x08\x79", - .rlen = 8, - }, { - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 8, - .result = "\xde\x18\x89\x41\xa3\x37\x5d\x3a", - .rlen = 8, - }, { - .key = "\xef\x01\x23\x45", - .klen = 4, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00", - .ilen = 20, - .result = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" - "\xbd\x61\x5a\x11\x62\xe1\xc7\xba" - "\x36\xb6\x78\x58", - .rlen = 20, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" - "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" - "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" - "\x12\x34\x56\x78", - .ilen = 28, - .result = "\x66\xa0\x94\x9f\x8a\xf7\xd6\x89" - "\x1f\x7f\x83\x2b\xa8\x33\xc0\x0c" - "\x89\x2e\xbe\x30\x14\x3c\xe2\x87" - "\x40\x01\x1e\xcf", - .rlen = 28, - }, { - .key = "\xef\x01\x23\x45", - .klen = 4, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00", - .ilen = 10, - .result = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" - "\xbd\x61", - .rlen = 10, - }, { - .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 16, - .input = "\x01\x23\x45\x67\x89\xAB\xCD\xEF", - .ilen = 8, - .result = "\x69\x72\x36\x59\x1B\x52\x42\xB1", - .rlen = 8, - }, -}; - -static struct cipher_testvec arc4_dec_tv_template[] = { - { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x75\xb7\x87\x80\x99\xe0\xc5\x96", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .rlen = 8, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x74\x94\xc2\xe7\x10\x4b\x08\x79", - .ilen = 8, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 8, - }, { - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .input = "\xde\x18\x89\x41\xa3\x37\x5d\x3a", - .ilen = 8, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 8, - }, { - .key = "\xef\x01\x23\x45", - .klen = 4, - .input = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" - "\xbd\x61\x5a\x11\x62\xe1\xc7\xba" - "\x36\xb6\x78\x58", - .ilen = 20, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00", - .rlen = 20, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", - .klen = 8, - .input = "\x66\xa0\x94\x9f\x8a\xf7\xd6\x89" - "\x1f\x7f\x83\x2b\xa8\x33\xc0\x0c" - "\x89\x2e\xbe\x30\x14\x3c\xe2\x87" - "\x40\x01\x1e\xcf", - .ilen = 28, - .result = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" - "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" - "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" - "\x12\x34\x56\x78", - .rlen = 28, - }, { - .key = "\xef\x01\x23\x45", - .klen = 4, - .input = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" - "\xbd\x61", - .ilen = 10, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00", - .rlen = 10, - }, { - .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 16, - .input = "\x69\x72\x36\x59\x1B\x52\x42\xB1", - .ilen = 8, - .result = "\x01\x23\x45\x67\x89\xAB\xCD\xEF", - .rlen = 8, - }, -}; - -/* - * TEA test vectors - */ -#define TEA_ENC_TEST_VECTORS 4 -#define TEA_DEC_TEST_VECTORS 4 - -static struct cipher_testvec tea_enc_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = zeroed_string, - .ilen = 8, - .result = "\x0a\x3a\xea\x41\x40\xa9\xba\x94", - .rlen = 8, - }, { - .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" - "\x77\x5d\x0e\x26\x6c\x28\x78\x43", - .klen = 16, - .input = "\x74\x65\x73\x74\x20\x6d\x65\x2e", - .ilen = 8, - .result = "\x77\x5d\x2a\x6a\xf6\xce\x92\x09", - .rlen = 8, - }, { - .key = "\x09\x65\x43\x11\x66\x44\x39\x25" - "\x51\x3a\x16\x10\x0a\x08\x12\x6e", - .klen = 16, - .input = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" - "\x65\x73\x74\x5f\x76\x65\x63\x74", - .ilen = 16, - .result = "\xbe\x7a\xbb\x81\x95\x2d\x1f\x1e" - "\xdd\x89\xa1\x25\x04\x21\xdf\x95", - .rlen = 16, - }, { - .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" - "\x5d\x04\x16\x36\x15\x72\x63\x2f", - .klen = 16, - .input = "\x54\x65\x61\x20\x69\x73\x20\x67" - "\x6f\x6f\x64\x20\x66\x6f\x72\x20" - "\x79\x6f\x75\x21\x21\x21\x20\x72" - "\x65\x61\x6c\x6c\x79\x21\x21\x21", - .ilen = 32, - .result = "\xe0\x4d\x5d\x3c\xb7\x8c\x36\x47" - "\x94\x18\x95\x91\xa9\xfc\x49\xf8" - "\x44\xd1\x2d\xc2\x99\xb8\x08\x2a" - "\x07\x89\x73\xc2\x45\x92\xc6\x90", - .rlen = 32, - } -}; - -static struct cipher_testvec tea_dec_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = "\x0a\x3a\xea\x41\x40\xa9\xba\x94", - .ilen = 8, - .result = zeroed_string, - .rlen = 8, - }, { - .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" - "\x77\x5d\x0e\x26\x6c\x28\x78\x43", - .klen = 16, - .input = "\x77\x5d\x2a\x6a\xf6\xce\x92\x09", - .ilen = 8, - .result = "\x74\x65\x73\x74\x20\x6d\x65\x2e", - .rlen = 8, - }, { - .key = "\x09\x65\x43\x11\x66\x44\x39\x25" - "\x51\x3a\x16\x10\x0a\x08\x12\x6e", - .klen = 16, - .input = "\xbe\x7a\xbb\x81\x95\x2d\x1f\x1e" - "\xdd\x89\xa1\x25\x04\x21\xdf\x95", - .ilen = 16, - .result = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" - "\x65\x73\x74\x5f\x76\x65\x63\x74", - .rlen = 16, - }, { - .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" - "\x5d\x04\x16\x36\x15\x72\x63\x2f", - .klen = 16, - .input = "\xe0\x4d\x5d\x3c\xb7\x8c\x36\x47" - "\x94\x18\x95\x91\xa9\xfc\x49\xf8" - "\x44\xd1\x2d\xc2\x99\xb8\x08\x2a" - "\x07\x89\x73\xc2\x45\x92\xc6\x90", - .ilen = 32, - .result = "\x54\x65\x61\x20\x69\x73\x20\x67" - "\x6f\x6f\x64\x20\x66\x6f\x72\x20" - "\x79\x6f\x75\x21\x21\x21\x20\x72" - "\x65\x61\x6c\x6c\x79\x21\x21\x21", - .rlen = 32, - } -}; - -/* - * XTEA test vectors - */ -#define XTEA_ENC_TEST_VECTORS 4 -#define XTEA_DEC_TEST_VECTORS 4 - -static struct cipher_testvec xtea_enc_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = zeroed_string, - .ilen = 8, - .result = "\xd8\xd4\xe9\xde\xd9\x1e\x13\xf7", - .rlen = 8, - }, { - .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" - "\x77\x5d\x0e\x26\x6c\x28\x78\x43", - .klen = 16, - .input = "\x74\x65\x73\x74\x20\x6d\x65\x2e", - .ilen = 8, - .result = "\x94\xeb\xc8\x96\x84\x6a\x49\xa8", - .rlen = 8, - }, { - .key = "\x09\x65\x43\x11\x66\x44\x39\x25" - "\x51\x3a\x16\x10\x0a\x08\x12\x6e", - .klen = 16, - .input = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" - "\x65\x73\x74\x5f\x76\x65\x63\x74", - .ilen = 16, - .result = "\x3e\xce\xae\x22\x60\x56\xa8\x9d" - "\x77\x4d\xd4\xb4\x87\x24\xe3\x9a", - .rlen = 16, - }, { - .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" - "\x5d\x04\x16\x36\x15\x72\x63\x2f", - .klen = 16, - .input = "\x54\x65\x61\x20\x69\x73\x20\x67" - "\x6f\x6f\x64\x20\x66\x6f\x72\x20" - "\x79\x6f\x75\x21\x21\x21\x20\x72" - "\x65\x61\x6c\x6c\x79\x21\x21\x21", - .ilen = 32, - .result = "\x99\x81\x9f\x5d\x6f\x4b\x31\x3a" - "\x86\xff\x6f\xd0\xe3\x87\x70\x07" - "\x4d\xb8\xcf\xf3\x99\x50\xb3\xd4" - "\x73\xa2\xfa\xc9\x16\x59\x5d\x81", - .rlen = 32, - } -}; - -static struct cipher_testvec xtea_dec_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = "\xd8\xd4\xe9\xde\xd9\x1e\x13\xf7", - .ilen = 8, - .result = zeroed_string, - .rlen = 8, - }, { - .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" - "\x77\x5d\x0e\x26\x6c\x28\x78\x43", - .klen = 16, - .input = "\x94\xeb\xc8\x96\x84\x6a\x49\xa8", - .ilen = 8, - .result = "\x74\x65\x73\x74\x20\x6d\x65\x2e", - .rlen = 8, - }, { - .key = "\x09\x65\x43\x11\x66\x44\x39\x25" - "\x51\x3a\x16\x10\x0a\x08\x12\x6e", - .klen = 16, - .input = "\x3e\xce\xae\x22\x60\x56\xa8\x9d" - "\x77\x4d\xd4\xb4\x87\x24\xe3\x9a", - .ilen = 16, - .result = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" - "\x65\x73\x74\x5f\x76\x65\x63\x74", - .rlen = 16, - }, { - .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" - "\x5d\x04\x16\x36\x15\x72\x63\x2f", - .klen = 16, - .input = "\x99\x81\x9f\x5d\x6f\x4b\x31\x3a" - "\x86\xff\x6f\xd0\xe3\x87\x70\x07" - "\x4d\xb8\xcf\xf3\x99\x50\xb3\xd4" - "\x73\xa2\xfa\xc9\x16\x59\x5d\x81", - .ilen = 32, - .result = "\x54\x65\x61\x20\x69\x73\x20\x67" - "\x6f\x6f\x64\x20\x66\x6f\x72\x20" - "\x79\x6f\x75\x21\x21\x21\x20\x72" - "\x65\x61\x6c\x6c\x79\x21\x21\x21", - .rlen = 32, - } -}; - -/* - * KHAZAD test vectors. - */ -#define KHAZAD_ENC_TEST_VECTORS 5 -#define KHAZAD_DEC_TEST_VECTORS 5 - -static struct cipher_testvec khazad_enc_tv_template[] = { - { - .key = "\x80\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 16, - .input = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 8, - .result = "\x49\xa4\xce\x32\xac\x19\x0e\x3f", - .rlen = 8, - }, { - .key = "\x38\x38\x38\x38\x38\x38\x38\x38" - "\x38\x38\x38\x38\x38\x38\x38\x38", - .klen = 16, - .input = "\x38\x38\x38\x38\x38\x38\x38\x38", - .ilen = 8, - .result = "\x7e\x82\x12\xa1\xd9\x5b\xe4\xf9", - .rlen = 8, - }, { - .key = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2" - "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", - .klen = 16, - .input = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", - .ilen = 8, - .result = "\xaa\xbe\xc1\x95\xc5\x94\x1a\x9c", - .rlen = 8, - }, { - .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" - "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .klen = 16, - .input = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .ilen = 8, - .result = "\x04\x74\xf5\x70\x50\x16\xd3\xb8", - .rlen = 8, - }, { - .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" - "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .klen = 16, - .input = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" - "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .ilen = 16, - .result = "\x04\x74\xf5\x70\x50\x16\xd3\xb8" - "\x04\x74\xf5\x70\x50\x16\xd3\xb8", - .rlen = 16, - }, -}; - -static struct cipher_testvec khazad_dec_tv_template[] = { - { - .key = "\x80\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 16, - .input = "\x49\xa4\xce\x32\xac\x19\x0e\x3f", - .ilen = 8, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 8, - }, { - .key = "\x38\x38\x38\x38\x38\x38\x38\x38" - "\x38\x38\x38\x38\x38\x38\x38\x38", - .klen = 16, - .input = "\x7e\x82\x12\xa1\xd9\x5b\xe4\xf9", - .ilen = 8, - .result = "\x38\x38\x38\x38\x38\x38\x38\x38", - .rlen = 8, - }, { - .key = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2" - "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", - .klen = 16, - .input = "\xaa\xbe\xc1\x95\xc5\x94\x1a\x9c", - .ilen = 8, - .result = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", - .rlen = 8, - }, { - .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" - "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .klen = 16, - .input = "\x04\x74\xf5\x70\x50\x16\xd3\xb8", - .ilen = 8, - .result = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .rlen = 8, - }, { - .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" - "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .klen = 16, - .input = "\x04\x74\xf5\x70\x50\x16\xd3\xb8" - "\x04\x74\xf5\x70\x50\x16\xd3\xb8", - .ilen = 16, - .result = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" - "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", - .rlen = 16, - }, -}; - -/* - * Anubis test vectors. - */ - -#define ANUBIS_ENC_TEST_VECTORS 5 -#define ANUBIS_DEC_TEST_VECTORS 5 -#define ANUBIS_CBC_ENC_TEST_VECTORS 2 -#define ANUBIS_CBC_DEC_TEST_VECTORS 2 - -static struct cipher_testvec anubis_enc_tv_template[] = { - { - .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .klen = 16, - .input = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .ilen = 16, - .result = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" - "\x08\xb7\x52\x8e\x6e\x6e\x86\x90", - .rlen = 16, - }, { - - .key = "\x03\x03\x03\x03\x03\x03\x03\x03" - "\x03\x03\x03\x03\x03\x03\x03\x03" - "\x03\x03\x03\x03", - .klen = 20, - .input = "\x03\x03\x03\x03\x03\x03\x03\x03" - "\x03\x03\x03\x03\x03\x03\x03\x03", - .ilen = 16, - .result = "\xdb\xf1\x42\xf4\xd1\x8a\xc7\x49" - "\x87\x41\x6f\x82\x0a\x98\x64\xae", - .rlen = 16, - }, { - .key = "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24", - .klen = 28, - .input = "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24\x24\x24\x24\x24", - .ilen = 16, - .result = "\xfd\x1b\x4a\xe3\xbf\xf0\xad\x3d" - "\x06\xd3\x61\x27\xfd\x13\x9e\xde", - .rlen = 16, - }, { - .key = "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25", - .klen = 32, - .input = "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25", - .ilen = 16, - .result = "\x1a\x91\xfb\x2b\xb7\x78\x6b\xc4" - "\x17\xd9\xff\x40\x3b\x0e\xe5\xfe", - .rlen = 16, - }, { - .key = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .klen = 40, - .input = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .ilen = 16, - .result = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" - "\x9e\xc6\x84\x0f\x17\x21\x07\xee", - .rlen = 16, - }, -}; - -static struct cipher_testvec anubis_dec_tv_template[] = { - { - .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .klen = 16, - .input = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" - "\x08\xb7\x52\x8e\x6e\x6e\x86\x90", - .ilen = 16, - .result = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .rlen = 16, - }, { - - .key = "\x03\x03\x03\x03\x03\x03\x03\x03" - "\x03\x03\x03\x03\x03\x03\x03\x03" - "\x03\x03\x03\x03", - .klen = 20, - .input = "\xdb\xf1\x42\xf4\xd1\x8a\xc7\x49" - "\x87\x41\x6f\x82\x0a\x98\x64\xae", - .ilen = 16, - .result = "\x03\x03\x03\x03\x03\x03\x03\x03" - "\x03\x03\x03\x03\x03\x03\x03\x03", - .rlen = 16, - }, { - .key = "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24", - .klen = 28, - .input = "\xfd\x1b\x4a\xe3\xbf\xf0\xad\x3d" - "\x06\xd3\x61\x27\xfd\x13\x9e\xde", - .ilen = 16, - .result = "\x24\x24\x24\x24\x24\x24\x24\x24" - "\x24\x24\x24\x24\x24\x24\x24\x24", - .rlen = 16, - }, { - .key = "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25", - .klen = 32, - .input = "\x1a\x91\xfb\x2b\xb7\x78\x6b\xc4" - "\x17\xd9\xff\x40\x3b\x0e\xe5\xfe", - .ilen = 16, - .result = "\x25\x25\x25\x25\x25\x25\x25\x25" - "\x25\x25\x25\x25\x25\x25\x25\x25", - .rlen = 16, - }, { - .key = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .input = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" - "\x9e\xc6\x84\x0f\x17\x21\x07\xee", - .klen = 40, - .ilen = 16, - .result = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .rlen = 16, - }, -}; - -static struct cipher_testvec anubis_cbc_enc_tv_template[] = { - { - .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .klen = 16, - .input = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .ilen = 32, - .result = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" - "\x08\xb7\x52\x8e\x6e\x6e\x86\x90" - "\x86\xd8\xb5\x6f\x98\x5e\x8a\x66" - "\x4f\x1f\x78\xa1\xbb\x37\xf1\xbe", - .rlen = 32, - }, { - .key = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .klen = 40, - .input = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .ilen = 32, - .result = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" - "\x9e\xc6\x84\x0f\x17\x21\x07\xee" - "\xa2\xbc\x06\x98\xc6\x4b\xda\x75" - "\x2e\xaa\xbe\x58\xce\x01\x5b\xc7", - .rlen = 32, - }, -}; - -static struct cipher_testvec anubis_cbc_dec_tv_template[] = { - { - .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .klen = 16, - .input = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" - "\x08\xb7\x52\x8e\x6e\x6e\x86\x90" - "\x86\xd8\xb5\x6f\x98\x5e\x8a\x66" - "\x4f\x1f\x78\xa1\xbb\x37\xf1\xbe", - .ilen = 32, - .result = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", - .rlen = 32, - }, { - .key = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .klen = 40, - .input = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" - "\x9e\xc6\x84\x0f\x17\x21\x07\xee" - "\xa2\xbc\x06\x98\xc6\x4b\xda\x75" - "\x2e\xaa\xbe\x58\xce\x01\x5b\xc7", - .ilen = 32, - .result = "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35" - "\x35\x35\x35\x35\x35\x35\x35\x35", - .rlen = 32, - }, -}; - -/* - * XETA test vectors - */ -#define XETA_ENC_TEST_VECTORS 4 -#define XETA_DEC_TEST_VECTORS 4 - -static struct cipher_testvec xeta_enc_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = zeroed_string, - .ilen = 8, - .result = "\xaa\x22\x96\xe5\x6c\x61\xf3\x45", - .rlen = 8, - }, { - .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" - "\x77\x5d\x0e\x26\x6c\x28\x78\x43", - .klen = 16, - .input = "\x74\x65\x73\x74\x20\x6d\x65\x2e", - .ilen = 8, - .result = "\x82\x3e\xeb\x35\xdc\xdd\xd9\xc3", - .rlen = 8, - }, { - .key = "\x09\x65\x43\x11\x66\x44\x39\x25" - "\x51\x3a\x16\x10\x0a\x08\x12\x6e", - .klen = 16, - .input = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" - "\x65\x73\x74\x5f\x76\x65\x63\x74", - .ilen = 16, - .result = "\xe2\x04\xdb\xf2\x89\x85\x9e\xea" - "\x61\x35\xaa\xed\xb5\xcb\x71\x2c", - .rlen = 16, - }, { - .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" - "\x5d\x04\x16\x36\x15\x72\x63\x2f", - .klen = 16, - .input = "\x54\x65\x61\x20\x69\x73\x20\x67" - "\x6f\x6f\x64\x20\x66\x6f\x72\x20" - "\x79\x6f\x75\x21\x21\x21\x20\x72" - "\x65\x61\x6c\x6c\x79\x21\x21\x21", - .ilen = 32, - .result = "\x0b\x03\xcd\x8a\xbe\x95\xfd\xb1" - "\xc1\x44\x91\x0b\xa5\xc9\x1b\xb4" - "\xa9\xda\x1e\x9e\xb1\x3e\x2a\x8f" - "\xea\xa5\x6a\x85\xd1\xf4\xa8\xa5", - .rlen = 32, - } -}; - -static struct cipher_testvec xeta_dec_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = "\xaa\x22\x96\xe5\x6c\x61\xf3\x45", - .ilen = 8, - .result = zeroed_string, - .rlen = 8, - }, { - .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" - "\x77\x5d\x0e\x26\x6c\x28\x78\x43", - .klen = 16, - .input = "\x82\x3e\xeb\x35\xdc\xdd\xd9\xc3", - .ilen = 8, - .result = "\x74\x65\x73\x74\x20\x6d\x65\x2e", - .rlen = 8, - }, { - .key = "\x09\x65\x43\x11\x66\x44\x39\x25" - "\x51\x3a\x16\x10\x0a\x08\x12\x6e", - .klen = 16, - .input = "\xe2\x04\xdb\xf2\x89\x85\x9e\xea" - "\x61\x35\xaa\xed\xb5\xcb\x71\x2c", - .ilen = 16, - .result = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" - "\x65\x73\x74\x5f\x76\x65\x63\x74", - .rlen = 16, - }, { - .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" - "\x5d\x04\x16\x36\x15\x72\x63\x2f", - .klen = 16, - .input = "\x0b\x03\xcd\x8a\xbe\x95\xfd\xb1" - "\xc1\x44\x91\x0b\xa5\xc9\x1b\xb4" - "\xa9\xda\x1e\x9e\xb1\x3e\x2a\x8f" - "\xea\xa5\x6a\x85\xd1\xf4\xa8\xa5", - .ilen = 32, - .result = "\x54\x65\x61\x20\x69\x73\x20\x67" - "\x6f\x6f\x64\x20\x66\x6f\x72\x20" - "\x79\x6f\x75\x21\x21\x21\x20\x72" - "\x65\x61\x6c\x6c\x79\x21\x21\x21", - .rlen = 32, - } -}; - -/* - * FCrypt test vectors - */ -#define FCRYPT_ENC_TEST_VECTORS ARRAY_SIZE(fcrypt_pcbc_enc_tv_template) -#define FCRYPT_DEC_TEST_VECTORS ARRAY_SIZE(fcrypt_pcbc_dec_tv_template) - -static struct cipher_testvec fcrypt_pcbc_enc_tv_template[] = { - { /* http://www.openafs.org/pipermail/openafs-devel/2000-December/005320.html */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 8, - .result = "\x0E\x09\x00\xC7\x3E\xF7\xED\x41", - .rlen = 8, - }, { - .key = "\x11\x44\x77\xAA\xDD\x00\x33\x66", - .klen = 8, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0", - .ilen = 8, - .result = "\xD8\xED\x78\x74\x77\xEC\x06\x80", - .rlen = 8, - }, { /* From Arla */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 8, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .input = "The quick brown fox jumps over the lazy dogs.\0\0", - .ilen = 48, - .result = "\x00\xf0\x0e\x11\x75\xe6\x23\x82" - "\xee\xac\x98\x62\x44\x51\xe4\x84" - "\xc3\x59\xd8\xaa\x64\x60\xae\xf7" - "\xd2\xd9\x13\x79\x72\xa3\x45\x03" - "\x23\xb5\x62\xd7\x0c\xf5\x27\xd1" - "\xf8\x91\x3c\xac\x44\x22\x92\xef", - .rlen = 48, - }, { - .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 8, - .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .input = "The quick brown fox jumps over the lazy dogs.\0\0", - .ilen = 48, - .result = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" - "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" - "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" - "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" - "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" - "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", - .rlen = 48, - }, { /* split-page version */ - .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 8, - .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .input = "The quick brown fox jumps over the lazy dogs.\0\0", - .ilen = 48, - .result = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" - "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" - "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" - "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" - "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" - "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", - .rlen = 48, - .np = 2, - .tap = { 20, 28 }, - } -}; - -static struct cipher_testvec fcrypt_pcbc_dec_tv_template[] = { - { /* http://www.openafs.org/pipermail/openafs-devel/2000-December/005320.html */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x0E\x09\x00\xC7\x3E\xF7\xED\x41", - .ilen = 8, - .result = "\x00\x00\x00\x00\x00\x00\x00\x00", - .rlen = 8, - }, { - .key = "\x11\x44\x77\xAA\xDD\x00\x33\x66", - .klen = 8, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\xD8\xED\x78\x74\x77\xEC\x06\x80", - .ilen = 8, - .result = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0", - .rlen = 8, - }, { /* From Arla */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 8, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .input = "\x00\xf0\x0e\x11\x75\xe6\x23\x82" - "\xee\xac\x98\x62\x44\x51\xe4\x84" - "\xc3\x59\xd8\xaa\x64\x60\xae\xf7" - "\xd2\xd9\x13\x79\x72\xa3\x45\x03" - "\x23\xb5\x62\xd7\x0c\xf5\x27\xd1" - "\xf8\x91\x3c\xac\x44\x22\x92\xef", - .ilen = 48, - .result = "The quick brown fox jumps over the lazy dogs.\0\0", - .rlen = 48, - }, { - .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 8, - .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .input = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" - "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" - "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" - "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" - "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" - "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", - .ilen = 48, - .result = "The quick brown fox jumps over the lazy dogs.\0\0", - .rlen = 48, - }, { /* split-page version */ - .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 8, - .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .input = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" - "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" - "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" - "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" - "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" - "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", - .ilen = 48, - .result = "The quick brown fox jumps over the lazy dogs.\0\0", - .rlen = 48, - .np = 2, - .tap = { 20, 28 }, - } -}; - -/* - * CAMELLIA test vectors. - */ -#define CAMELLIA_ENC_TEST_VECTORS 3 -#define CAMELLIA_DEC_TEST_VECTORS 3 -#define CAMELLIA_CBC_ENC_TEST_VECTORS 2 -#define CAMELLIA_CBC_DEC_TEST_VECTORS 2 - -static struct cipher_testvec camellia_enc_tv_template[] = { - { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 16, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 16, - .result = "\x67\x67\x31\x38\x54\x96\x69\x73" - "\x08\x57\x06\x56\x48\xea\xbe\x43", - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77", - .klen = 24, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 16, - .result = "\xb4\x99\x34\x01\xb3\xe9\x96\xf8" - "\x4e\xe5\xce\xe7\xd7\x9b\x09\xb9", - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .klen = 32, - .input = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ilen = 16, - .result = "\x9a\xcc\x23\x7d\xff\x16\xd7\x6c" - "\x20\xef\x7c\x91\x9e\x3a\x75\x09", - .rlen = 16, - }, -}; - -static struct cipher_testvec camellia_dec_tv_template[] = { - { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 16, - .input = "\x67\x67\x31\x38\x54\x96\x69\x73" - "\x08\x57\x06\x56\x48\xea\xbe\x43", - .ilen = 16, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x55\x55\x55\x55\x55\x55\x55\x55" "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77", .klen = 24, - .input = "\xb4\x99\x34\x01\xb3\xe9\x96\xf8" - "\x4e\xe5\xce\xe7\xd7\x9b\x09\xb9", - .ilen = 16, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 16, - }, { - .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", - .klen = 32, - .input = "\x9a\xcc\x23\x7d\xff\x16\xd7\x6c" - "\x20\xef\x7c\x91\x9e\x3a\x75\x09", - .ilen = 16, - .result = "\x01\x23\x45\x67\x89\xab\xcd\xef" - "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .rlen = 16, - }, -}; - -static struct cipher_testvec camellia_cbc_enc_tv_template[] = { - { - .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" - "\x51\x2e\x03\xd5\x34\x12\x00\x06", - .klen = 16, - .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" - "\xb4\x22\xda\x80\x2c\x9f\xac\x41", - .input = "Single block msg", - .ilen = 16, - .result = "\xea\x32\x12\x76\x3b\x50\x10\xe7" - "\x18\xf6\xfd\x5d\xf6\x8f\x13\x51", - .rlen = 16, - }, { - .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" - "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", - .klen = 16, - .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" - "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .ilen = 32, - .result = "\xa5\xdf\x6e\x50\xda\x70\x6c\x01" - "\x4a\xab\xf3\xf2\xd6\xfc\x6c\xfd" - "\x19\xb4\x3e\x57\x1c\x02\x5e\xa0" - "\x15\x78\xe0\x5e\xf2\xcb\x87\x16", - .rlen = 32, - }, -}; - -static struct cipher_testvec camellia_cbc_dec_tv_template[] = { - { - .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" - "\x51\x2e\x03\xd5\x34\x12\x00\x06", - .klen = 16, - .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" - "\xb4\x22\xda\x80\x2c\x9f\xac\x41", - .input = "\xea\x32\x12\x76\x3b\x50\x10\xe7" - "\x18\xf6\xfd\x5d\xf6\x8f\x13\x51", - .ilen = 16, - .result = "Single block msg", - .rlen = 16, - }, { - .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" - "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", - .klen = 16, - .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" - "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", - .input = "\xa5\xdf\x6e\x50\xda\x70\x6c\x01" - "\x4a\xab\xf3\xf2\xd6\xfc\x6c\xfd" - "\x19\xb4\x3e\x57\x1c\x02\x5e\xa0" - "\x15\x78\xe0\x5e\xf2\xcb\x87\x16", - .ilen = 32, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .rlen = 32, - }, -}; - -/* - * SEED test vectors - */ -#define SEED_ENC_TEST_VECTORS 4 -#define SEED_DEC_TEST_VECTORS 4 - -static struct cipher_testvec seed_enc_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .ilen = 16, - .result = "\x5e\xba\xc6\xe0\x05\x4e\x16\x68" - "\x19\xaf\xf1\xcc\x6d\x34\x6c\xdb", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = zeroed_string, - .ilen = 16, - .result = "\xc1\x1f\x22\xf2\x01\x40\x50\x50" - "\x84\x48\x35\x97\xe4\x37\x0f\x43", - .rlen = 16, - }, { - .key = "\x47\x06\x48\x08\x51\xe6\x1b\xe8" - "\x5d\x74\xbf\xb3\xfd\x95\x61\x85", - .klen = 16, - .input = "\x83\xa2\xf8\xa2\x88\x64\x1f\xb9" - "\xa4\xe9\xa5\xcc\x2f\x13\x1c\x7d", - .ilen = 16, - .result = "\xee\x54\xd1\x3e\xbc\xae\x70\x6d" - "\x22\x6b\xc3\x14\x2c\xd4\x0d\x4a", - .rlen = 16, - }, { - .key = "\x28\xdb\xc3\xbc\x49\xff\xd8\x7d" - "\xcf\xa5\x09\xb1\x1d\x42\x2b\xe7", - .klen = 16, - .input = "\xb4\x1e\x6b\xe2\xeb\xa8\x4a\x14" - "\x8e\x2e\xed\x84\x59\x3c\x5e\xc7", - .ilen = 16, - .result = "\x9b\x9b\x7b\xfc\xd1\x81\x3c\xb9" - "\x5d\x0b\x36\x18\xf4\x0f\x51\x22", - .rlen = 16, } }; -static struct cipher_testvec seed_dec_tv_template[] = { - { - .key = zeroed_string, - .klen = 16, - .input = "\x5e\xba\xc6\xe0\x05\x4e\x16\x68" - "\x19\xaf\xf1\xcc\x6d\x34\x6c\xdb", - .ilen = 16, - .result = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .rlen = 16, - }, { - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .klen = 16, - .input = "\xc1\x1f\x22\xf2\x01\x40\x50\x50" - "\x84\x48\x35\x97\xe4\x37\x0f\x43", - .ilen = 16, - .result = zeroed_string, - .rlen = 16, - }, { - .key = "\x47\x06\x48\x08\x51\xe6\x1b\xe8" - "\x5d\x74\xbf\xb3\xfd\x95\x61\x85", - .klen = 16, - .input = "\xee\x54\xd1\x3e\xbc\xae\x70\x6d" - "\x22\x6b\xc3\x14\x2c\xd4\x0d\x4a", - .ilen = 16, - .result = "\x83\xa2\xf8\xa2\x88\x64\x1f\xb9" - "\xa4\xe9\xa5\xcc\x2f\x13\x1c\x7d", - .rlen = 16, - }, { - .key = "\x28\xdb\xc3\xbc\x49\xff\xd8\x7d" - "\xcf\xa5\x09\xb1\x1d\x42\x2b\xe7", - .klen = 16, - .input = "\x9b\x9b\x7b\xfc\xd1\x81\x3c\xb9" - "\x5d\x0b\x36\x18\xf4\x0f\x51\x22", - .ilen = 16, - .result = "\xb4\x1e\x6b\xe2\xeb\xa8\x4a\x14" - "\x8e\x2e\xed\x84\x59\x3c\x5e\xc7", - .rlen = 16, - } -}; - -#define SALSA20_STREAM_ENC_TEST_VECTORS 5 -static struct cipher_testvec salsa20_stream_enc_tv_template[] = { - /* - * Testvectors from verified.test-vectors submitted to ECRYPT. - * They are truncated to size 39, 64, 111, 129 to test a variety - * of input length. - */ - { /* Set 3, vector 0 */ - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", - .klen = 16, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00", - .ilen = 39, - .result = "\x2D\xD5\xC3\xF7\xBA\x2B\x20\xF7" - "\x68\x02\x41\x0C\x68\x86\x88\x89" - "\x5A\xD8\xC1\xBD\x4E\xA6\xC9\xB1" - "\x40\xFB\x9B\x90\xE2\x10\x49\xBF" - "\x58\x3F\x52\x79\x70\xEB\xC1", - .rlen = 39, - }, { /* Set 5, vector 0 */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 16, - .iv = "\x80\x00\x00\x00\x00\x00\x00\x00", - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .ilen = 64, - .result = "\xB6\x6C\x1E\x44\x46\xDD\x95\x57" - "\xE5\x78\xE2\x23\xB0\xB7\x68\x01" - "\x7B\x23\xB2\x67\xBB\x02\x34\xAE" - "\x46\x26\xBF\x44\x3F\x21\x97\x76" - "\x43\x6F\xB1\x9F\xD0\xE8\x86\x6F" - "\xCD\x0D\xE9\xA9\x53\x8F\x4A\x09" - "\xCA\x9A\xC0\x73\x2E\x30\xBC\xF9" - "\x8E\x4F\x13\xE4\xB9\xE2\x01\xD9", - .rlen = 64, - }, { /* Set 3, vector 27 */ - .key = "\x1B\x1C\x1D\x1E\x1F\x20\x21\x22" - "\x23\x24\x25\x26\x27\x28\x29\x2A" - "\x2B\x2C\x2D\x2E\x2F\x30\x31\x32" - "\x33\x34\x35\x36\x37\x38\x39\x3A", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00", - .ilen = 111, - .result = "\xAE\x39\x50\x8E\xAC\x9A\xEC\xE7" - "\xBF\x97\xBB\x20\xB9\xDE\xE4\x1F" - "\x87\xD9\x47\xF8\x28\x91\x35\x98" - "\xDB\x72\xCC\x23\x29\x48\x56\x5E" - "\x83\x7E\x0B\xF3\x7D\x5D\x38\x7B" - "\x2D\x71\x02\xB4\x3B\xB5\xD8\x23" - "\xB0\x4A\xDF\x3C\xEC\xB6\xD9\x3B" - "\x9B\xA7\x52\xBE\xC5\xD4\x50\x59" - "\x15\x14\xB4\x0E\x40\xE6\x53\xD1" - "\x83\x9C\x5B\xA0\x92\x29\x6B\x5E" - "\x96\x5B\x1E\x2F\xD3\xAC\xC1\x92" - "\xB1\x41\x3F\x19\x2F\xC4\x3B\xC6" - "\x95\x46\x45\x54\xE9\x75\x03\x08" - "\x44\xAF\xE5\x8A\x81\x12\x09", - .rlen = 111, - }, { /* Set 5, vector 27 */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 32, - .iv = "\x00\x00\x00\x10\x00\x00\x00\x00", - .input = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00", - .ilen = 129, - .result = "\xD2\xDB\x1A\x5C\xF1\xC1\xAC\xDB" - "\xE8\x1A\x7A\x43\x40\xEF\x53\x43" - "\x5E\x7F\x4B\x1A\x50\x52\x3F\x8D" - "\x28\x3D\xCF\x85\x1D\x69\x6E\x60" - "\xF2\xDE\x74\x56\x18\x1B\x84\x10" - "\xD4\x62\xBA\x60\x50\xF0\x61\xF2" - "\x1C\x78\x7F\xC1\x24\x34\xAF\x58" - "\xBF\x2C\x59\xCA\x90\x77\xF3\xB0" - "\x5B\x4A\xDF\x89\xCE\x2C\x2F\xFC" - "\x67\xF0\xE3\x45\xE8\xB3\xB3\x75" - "\xA0\x95\x71\xA1\x29\x39\x94\xCA" - "\x45\x2F\xBD\xCB\x10\xB6\xBE\x9F" - "\x8E\xF9\xB2\x01\x0A\x5A\x0A\xB7" - "\x6B\x9D\x70\x8E\x4B\xD6\x2F\xCD" - "\x2E\x40\x48\x75\xE9\xE2\x21\x45" - "\x0B\xC9\xB6\xB5\x66\xBC\x9A\x59" - "\x5A", - .rlen = 129, - }, { /* large test vector generated using Crypto++ */ - .key = "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - .klen = 32, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .input = - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" - "\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" - "\x30\x31\x32\x33\x34\x35\x36\x37" - "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" - "\x40\x41\x42\x43\x44\x45\x46\x47" - "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" - "\x50\x51\x52\x53\x54\x55\x56\x57" - "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" - "\x60\x61\x62\x63\x64\x65\x66\x67" - "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" - "\x70\x71\x72\x73\x74\x75\x76\x77" - "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" - "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" - "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" - "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - "\x00\x03\x06\x09\x0c\x0f\x12\x15" - "\x18\x1b\x1e\x21\x24\x27\x2a\x2d" - "\x30\x33\x36\x39\x3c\x3f\x42\x45" - "\x48\x4b\x4e\x51\x54\x57\x5a\x5d" - "\x60\x63\x66\x69\x6c\x6f\x72\x75" - "\x78\x7b\x7e\x81\x84\x87\x8a\x8d" - "\x90\x93\x96\x99\x9c\x9f\xa2\xa5" - "\xa8\xab\xae\xb1\xb4\xb7\xba\xbd" - "\xc0\xc3\xc6\xc9\xcc\xcf\xd2\xd5" - "\xd8\xdb\xde\xe1\xe4\xe7\xea\xed" - "\xf0\xf3\xf6\xf9\xfc\xff\x02\x05" - "\x08\x0b\x0e\x11\x14\x17\x1a\x1d" - "\x20\x23\x26\x29\x2c\x2f\x32\x35" - "\x38\x3b\x3e\x41\x44\x47\x4a\x4d" - "\x50\x53\x56\x59\x5c\x5f\x62\x65" - "\x68\x6b\x6e\x71\x74\x77\x7a\x7d" - "\x80\x83\x86\x89\x8c\x8f\x92\x95" - "\x98\x9b\x9e\xa1\xa4\xa7\xaa\xad" - "\xb0\xb3\xb6\xb9\xbc\xbf\xc2\xc5" - "\xc8\xcb\xce\xd1\xd4\xd7\xda\xdd" - "\xe0\xe3\xe6\xe9\xec\xef\xf2\xf5" - "\xf8\xfb\xfe\x01\x04\x07\x0a\x0d" - "\x10\x13\x16\x19\x1c\x1f\x22\x25" - "\x28\x2b\x2e\x31\x34\x37\x3a\x3d" - "\x40\x43\x46\x49\x4c\x4f\x52\x55" - "\x58\x5b\x5e\x61\x64\x67\x6a\x6d" - "\x70\x73\x76\x79\x7c\x7f\x82\x85" - "\x88\x8b\x8e\x91\x94\x97\x9a\x9d" - "\xa0\xa3\xa6\xa9\xac\xaf\xb2\xb5" - "\xb8\xbb\xbe\xc1\xc4\xc7\xca\xcd" - "\xd0\xd3\xd6\xd9\xdc\xdf\xe2\xe5" - "\xe8\xeb\xee\xf1\xf4\xf7\xfa\xfd" - "\x00\x05\x0a\x0f\x14\x19\x1e\x23" - "\x28\x2d\x32\x37\x3c\x41\x46\x4b" - "\x50\x55\x5a\x5f\x64\x69\x6e\x73" - "\x78\x7d\x82\x87\x8c\x91\x96\x9b" - "\xa0\xa5\xaa\xaf\xb4\xb9\xbe\xc3" - "\xc8\xcd\xd2\xd7\xdc\xe1\xe6\xeb" - "\xf0\xf5\xfa\xff\x04\x09\x0e\x13" - "\x18\x1d\x22\x27\x2c\x31\x36\x3b" - "\x40\x45\x4a\x4f\x54\x59\x5e\x63" - "\x68\x6d\x72\x77\x7c\x81\x86\x8b" - "\x90\x95\x9a\x9f\xa4\xa9\xae\xb3" - "\xb8\xbd\xc2\xc7\xcc\xd1\xd6\xdb" - "\xe0\xe5\xea\xef\xf4\xf9\xfe\x03" - "\x08\x0d\x12\x17\x1c\x21\x26\x2b" - "\x30\x35\x3a\x3f\x44\x49\x4e\x53" - "\x58\x5d\x62\x67\x6c\x71\x76\x7b" - "\x80\x85\x8a\x8f\x94\x99\x9e\xa3" - "\xa8\xad\xb2\xb7\xbc\xc1\xc6\xcb" - "\xd0\xd5\xda\xdf\xe4\xe9\xee\xf3" - "\xf8\xfd\x02\x07\x0c\x11\x16\x1b" - "\x20\x25\x2a\x2f\x34\x39\x3e\x43" - "\x48\x4d\x52\x57\x5c\x61\x66\x6b" - "\x70\x75\x7a\x7f\x84\x89\x8e\x93" - "\x98\x9d\xa2\xa7\xac\xb1\xb6\xbb" - "\xc0\xc5\xca\xcf\xd4\xd9\xde\xe3" - "\xe8\xed\xf2\xf7\xfc\x01\x06\x0b" - "\x10\x15\x1a\x1f\x24\x29\x2e\x33" - "\x38\x3d\x42\x47\x4c\x51\x56\x5b" - "\x60\x65\x6a\x6f\x74\x79\x7e\x83" - "\x88\x8d\x92\x97\x9c\xa1\xa6\xab" - "\xb0\xb5\xba\xbf\xc4\xc9\xce\xd3" - "\xd8\xdd\xe2\xe7\xec\xf1\xf6\xfb" - "\x00\x07\x0e\x15\x1c\x23\x2a\x31" - "\x38\x3f\x46\x4d\x54\x5b\x62\x69" - "\x70\x77\x7e\x85\x8c\x93\x9a\xa1" - "\xa8\xaf\xb6\xbd\xc4\xcb\xd2\xd9" - "\xe0\xe7\xee\xf5\xfc\x03\x0a\x11" - "\x18\x1f\x26\x2d\x34\x3b\x42\x49" - "\x50\x57\x5e\x65\x6c\x73\x7a\x81" - "\x88\x8f\x96\x9d\xa4\xab\xb2\xb9" - "\xc0\xc7\xce\xd5\xdc\xe3\xea\xf1" - "\xf8\xff\x06\x0d\x14\x1b\x22\x29" - "\x30\x37\x3e\x45\x4c\x53\x5a\x61" - "\x68\x6f\x76\x7d\x84\x8b\x92\x99" - "\xa0\xa7\xae\xb5\xbc\xc3\xca\xd1" - "\xd8\xdf\xe6\xed\xf4\xfb\x02\x09" - "\x10\x17\x1e\x25\x2c\x33\x3a\x41" - "\x48\x4f\x56\x5d\x64\x6b\x72\x79" - "\x80\x87\x8e\x95\x9c\xa3\xaa\xb1" - "\xb8\xbf\xc6\xcd\xd4\xdb\xe2\xe9" - "\xf0\xf7\xfe\x05\x0c\x13\x1a\x21" - "\x28\x2f\x36\x3d\x44\x4b\x52\x59" - "\x60\x67\x6e\x75\x7c\x83\x8a\x91" - "\x98\x9f\xa6\xad\xb4\xbb\xc2\xc9" - "\xd0\xd7\xde\xe5\xec\xf3\xfa\x01" - "\x08\x0f\x16\x1d\x24\x2b\x32\x39" - "\x40\x47\x4e\x55\x5c\x63\x6a\x71" - "\x78\x7f\x86\x8d\x94\x9b\xa2\xa9" - "\xb0\xb7\xbe\xc5\xcc\xd3\xda\xe1" - "\xe8\xef\xf6\xfd\x04\x0b\x12\x19" - "\x20\x27\x2e\x35\x3c\x43\x4a\x51" - "\x58\x5f\x66\x6d\x74\x7b\x82\x89" - "\x90\x97\x9e\xa5\xac\xb3\xba\xc1" - "\xc8\xcf\xd6\xdd\xe4\xeb\xf2\xf9" - "\x00\x09\x12\x1b\x24\x2d\x36\x3f" - "\x48\x51\x5a\x63\x6c\x75\x7e\x87" - "\x90\x99\xa2\xab\xb4\xbd\xc6\xcf" - "\xd8\xe1\xea\xf3\xfc\x05\x0e\x17" - "\x20\x29\x32\x3b\x44\x4d\x56\x5f" - "\x68\x71\x7a\x83\x8c\x95\x9e\xa7" - "\xb0\xb9\xc2\xcb\xd4\xdd\xe6\xef" - "\xf8\x01\x0a\x13\x1c\x25\x2e\x37" - "\x40\x49\x52\x5b\x64\x6d\x76\x7f" - "\x88\x91\x9a\xa3\xac\xb5\xbe\xc7" - "\xd0\xd9\xe2\xeb\xf4\xfd\x06\x0f" - "\x18\x21\x2a\x33\x3c\x45\x4e\x57" - "\x60\x69\x72\x7b\x84\x8d\x96\x9f" - "\xa8\xb1\xba\xc3\xcc\xd5\xde\xe7" - "\xf0\xf9\x02\x0b\x14\x1d\x26\x2f" - "\x38\x41\x4a\x53\x5c\x65\x6e\x77" - "\x80\x89\x92\x9b\xa4\xad\xb6\xbf" - "\xc8\xd1\xda\xe3\xec\xf5\xfe\x07" - "\x10\x19\x22\x2b\x34\x3d\x46\x4f" - "\x58\x61\x6a\x73\x7c\x85\x8e\x97" - "\xa0\xa9\xb2\xbb\xc4\xcd\xd6\xdf" - "\xe8\xf1\xfa\x03\x0c\x15\x1e\x27" - "\x30\x39\x42\x4b\x54\x5d\x66\x6f" - "\x78\x81\x8a\x93\x9c\xa5\xae\xb7" - "\xc0\xc9\xd2\xdb\xe4\xed\xf6\xff" - "\x08\x11\x1a\x23\x2c\x35\x3e\x47" - "\x50\x59\x62\x6b\x74\x7d\x86\x8f" - "\x98\xa1\xaa\xb3\xbc\xc5\xce\xd7" - "\xe0\xe9\xf2\xfb\x04\x0d\x16\x1f" - "\x28\x31\x3a\x43\x4c\x55\x5e\x67" - "\x70\x79\x82\x8b\x94\x9d\xa6\xaf" - "\xb8\xc1\xca\xd3\xdc\xe5\xee\xf7" - "\x00\x0b\x16\x21\x2c\x37\x42\x4d" - "\x58\x63\x6e\x79\x84\x8f\x9a\xa5" - "\xb0\xbb\xc6\xd1\xdc\xe7\xf2\xfd" - "\x08\x13\x1e\x29\x34\x3f\x4a\x55" - "\x60\x6b\x76\x81\x8c\x97\xa2\xad" - "\xb8\xc3\xce\xd9\xe4\xef\xfa\x05" - "\x10\x1b\x26\x31\x3c\x47\x52\x5d" - "\x68\x73\x7e\x89\x94\x9f\xaa\xb5" - "\xc0\xcb\xd6\xe1\xec\xf7\x02\x0d" - "\x18\x23\x2e\x39\x44\x4f\x5a\x65" - "\x70\x7b\x86\x91\x9c\xa7\xb2\xbd" - "\xc8\xd3\xde\xe9\xf4\xff\x0a\x15" - "\x20\x2b\x36\x41\x4c\x57\x62\x6d" - "\x78\x83\x8e\x99\xa4\xaf\xba\xc5" - "\xd0\xdb\xe6\xf1\xfc\x07\x12\x1d" - "\x28\x33\x3e\x49\x54\x5f\x6a\x75" - "\x80\x8b\x96\xa1\xac\xb7\xc2\xcd" - "\xd8\xe3\xee\xf9\x04\x0f\x1a\x25" - "\x30\x3b\x46\x51\x5c\x67\x72\x7d" - "\x88\x93\x9e\xa9\xb4\xbf\xca\xd5" - "\xe0\xeb\xf6\x01\x0c\x17\x22\x2d" - "\x38\x43\x4e\x59\x64\x6f\x7a\x85" - "\x90\x9b\xa6\xb1\xbc\xc7\xd2\xdd" - "\xe8\xf3\xfe\x09\x14\x1f\x2a\x35" - "\x40\x4b\x56\x61\x6c\x77\x82\x8d" - "\x98\xa3\xae\xb9\xc4\xcf\xda\xe5" - "\xf0\xfb\x06\x11\x1c\x27\x32\x3d" - "\x48\x53\x5e\x69\x74\x7f\x8a\x95" - "\xa0\xab\xb6\xc1\xcc\xd7\xe2\xed" - "\xf8\x03\x0e\x19\x24\x2f\x3a\x45" - "\x50\x5b\x66\x71\x7c\x87\x92\x9d" - "\xa8\xb3\xbe\xc9\xd4\xdf\xea\xf5" - "\x00\x0d\x1a\x27\x34\x41\x4e\x5b" - "\x68\x75\x82\x8f\x9c\xa9\xb6\xc3" - "\xd0\xdd\xea\xf7\x04\x11\x1e\x2b" - "\x38\x45\x52\x5f\x6c\x79\x86\x93" - "\xa0\xad\xba\xc7\xd4\xe1\xee\xfb" - "\x08\x15\x22\x2f\x3c\x49\x56\x63" - "\x70\x7d\x8a\x97\xa4\xb1\xbe\xcb" - "\xd8\xe5\xf2\xff\x0c\x19\x26\x33" - "\x40\x4d\x5a\x67\x74\x81\x8e\x9b" - "\xa8\xb5\xc2\xcf\xdc\xe9\xf6\x03" - "\x10\x1d\x2a\x37\x44\x51\x5e\x6b" - "\x78\x85\x92\x9f\xac\xb9\xc6\xd3" - "\xe0\xed\xfa\x07\x14\x21\x2e\x3b" - "\x48\x55\x62\x6f\x7c\x89\x96\xa3" - "\xb0\xbd\xca\xd7\xe4\xf1\xfe\x0b" - "\x18\x25\x32\x3f\x4c\x59\x66\x73" - "\x80\x8d\x9a\xa7\xb4\xc1\xce\xdb" - "\xe8\xf5\x02\x0f\x1c\x29\x36\x43" - "\x50\x5d\x6a\x77\x84\x91\x9e\xab" - "\xb8\xc5\xd2\xdf\xec\xf9\x06\x13" - "\x20\x2d\x3a\x47\x54\x61\x6e\x7b" - "\x88\x95\xa2\xaf\xbc\xc9\xd6\xe3" - "\xf0\xfd\x0a\x17\x24\x31\x3e\x4b" - "\x58\x65\x72\x7f\x8c\x99\xa6\xb3" - "\xc0\xcd\xda\xe7\xf4\x01\x0e\x1b" - "\x28\x35\x42\x4f\x5c\x69\x76\x83" - "\x90\x9d\xaa\xb7\xc4\xd1\xde\xeb" - "\xf8\x05\x12\x1f\x2c\x39\x46\x53" - "\x60\x6d\x7a\x87\x94\xa1\xae\xbb" - "\xc8\xd5\xe2\xef\xfc\x09\x16\x23" - "\x30\x3d\x4a\x57\x64\x71\x7e\x8b" - "\x98\xa5\xb2\xbf\xcc\xd9\xe6\xf3" - "\x00\x0f\x1e\x2d\x3c\x4b\x5a\x69" - "\x78\x87\x96\xa5\xb4\xc3\xd2\xe1" - "\xf0\xff\x0e\x1d\x2c\x3b\x4a\x59" - "\x68\x77\x86\x95\xa4\xb3\xc2\xd1" - "\xe0\xef\xfe\x0d\x1c\x2b\x3a\x49" - "\x58\x67\x76\x85\x94\xa3\xb2\xc1" - "\xd0\xdf\xee\xfd\x0c\x1b\x2a\x39" - "\x48\x57\x66\x75\x84\x93\xa2\xb1" - "\xc0\xcf\xde\xed\xfc\x0b\x1a\x29" - "\x38\x47\x56\x65\x74\x83\x92\xa1" - "\xb0\xbf\xce\xdd\xec\xfb\x0a\x19" - "\x28\x37\x46\x55\x64\x73\x82\x91" - "\xa0\xaf\xbe\xcd\xdc\xeb\xfa\x09" - "\x18\x27\x36\x45\x54\x63\x72\x81" - "\x90\x9f\xae\xbd\xcc\xdb\xea\xf9" - "\x08\x17\x26\x35\x44\x53\x62\x71" - "\x80\x8f\x9e\xad\xbc\xcb\xda\xe9" - "\xf8\x07\x16\x25\x34\x43\x52\x61" - "\x70\x7f\x8e\x9d\xac\xbb\xca\xd9" - "\xe8\xf7\x06\x15\x24\x33\x42\x51" - "\x60\x6f\x7e\x8d\x9c\xab\xba\xc9" - "\xd8\xe7\xf6\x05\x14\x23\x32\x41" - "\x50\x5f\x6e\x7d\x8c\x9b\xaa\xb9" - "\xc8\xd7\xe6\xf5\x04\x13\x22\x31" - "\x40\x4f\x5e\x6d\x7c\x8b\x9a\xa9" - "\xb8\xc7\xd6\xe5\xf4\x03\x12\x21" - "\x30\x3f\x4e\x5d\x6c\x7b\x8a\x99" - "\xa8\xb7\xc6\xd5\xe4\xf3\x02\x11" - "\x20\x2f\x3e\x4d\x5c\x6b\x7a\x89" - "\x98\xa7\xb6\xc5\xd4\xe3\xf2\x01" - "\x10\x1f\x2e\x3d\x4c\x5b\x6a\x79" - "\x88\x97\xa6\xb5\xc4\xd3\xe2\xf1" - "\x00\x11\x22\x33\x44\x55\x66\x77" - "\x88\x99\xaa\xbb\xcc\xdd\xee\xff" - "\x10\x21\x32\x43\x54\x65\x76\x87" - "\x98\xa9\xba\xcb\xdc\xed\xfe\x0f" - "\x20\x31\x42\x53\x64\x75\x86\x97" - "\xa8\xb9\xca\xdb\xec\xfd\x0e\x1f" - "\x30\x41\x52\x63\x74\x85\x96\xa7" - "\xb8\xc9\xda\xeb\xfc\x0d\x1e\x2f" - "\x40\x51\x62\x73\x84\x95\xa6\xb7" - "\xc8\xd9\xea\xfb\x0c\x1d\x2e\x3f" - "\x50\x61\x72\x83\x94\xa5\xb6\xc7" - "\xd8\xe9\xfa\x0b\x1c\x2d\x3e\x4f" - "\x60\x71\x82\x93\xa4\xb5\xc6\xd7" - "\xe8\xf9\x0a\x1b\x2c\x3d\x4e\x5f" - "\x70\x81\x92\xa3\xb4\xc5\xd6\xe7" - "\xf8\x09\x1a\x2b\x3c\x4d\x5e\x6f" - "\x80\x91\xa2\xb3\xc4\xd5\xe6\xf7" - "\x08\x19\x2a\x3b\x4c\x5d\x6e\x7f" - "\x90\xa1\xb2\xc3\xd4\xe5\xf6\x07" - "\x18\x29\x3a\x4b\x5c\x6d\x7e\x8f" - "\xa0\xb1\xc2\xd3\xe4\xf5\x06\x17" - "\x28\x39\x4a\x5b\x6c\x7d\x8e\x9f" - "\xb0\xc1\xd2\xe3\xf4\x05\x16\x27" - "\x38\x49\x5a\x6b\x7c\x8d\x9e\xaf" - "\xc0\xd1\xe2\xf3\x04\x15\x26\x37" - "\x48\x59\x6a\x7b\x8c\x9d\xae\xbf" - "\xd0\xe1\xf2\x03\x14\x25\x36\x47" - "\x58\x69\x7a\x8b\x9c\xad\xbe\xcf" - "\xe0\xf1\x02\x13\x24\x35\x46\x57" - "\x68\x79\x8a\x9b\xac\xbd\xce\xdf" - "\xf0\x01\x12\x23\x34\x45\x56\x67" - "\x78\x89\x9a\xab\xbc\xcd\xde\xef" - "\x00\x13\x26\x39\x4c\x5f\x72\x85" - "\x98\xab\xbe\xd1\xe4\xf7\x0a\x1d" - "\x30\x43\x56\x69\x7c\x8f\xa2\xb5" - "\xc8\xdb\xee\x01\x14\x27\x3a\x4d" - "\x60\x73\x86\x99\xac\xbf\xd2\xe5" - "\xf8\x0b\x1e\x31\x44\x57\x6a\x7d" - "\x90\xa3\xb6\xc9\xdc\xef\x02\x15" - "\x28\x3b\x4e\x61\x74\x87\x9a\xad" - "\xc0\xd3\xe6\xf9\x0c\x1f\x32\x45" - "\x58\x6b\x7e\x91\xa4\xb7\xca\xdd" - "\xf0\x03\x16\x29\x3c\x4f\x62\x75" - "\x88\x9b\xae\xc1\xd4\xe7\xfa\x0d" - "\x20\x33\x46\x59\x6c\x7f\x92\xa5" - "\xb8\xcb\xde\xf1\x04\x17\x2a\x3d" - "\x50\x63\x76\x89\x9c\xaf\xc2\xd5" - "\xe8\xfb\x0e\x21\x34\x47\x5a\x6d" - "\x80\x93\xa6\xb9\xcc\xdf\xf2\x05" - "\x18\x2b\x3e\x51\x64\x77\x8a\x9d" - "\xb0\xc3\xd6\xe9\xfc\x0f\x22\x35" - "\x48\x5b\x6e\x81\x94\xa7\xba\xcd" - "\xe0\xf3\x06\x19\x2c\x3f\x52\x65" - "\x78\x8b\x9e\xb1\xc4\xd7\xea\xfd" - "\x10\x23\x36\x49\x5c\x6f\x82\x95" - "\xa8\xbb\xce\xe1\xf4\x07\x1a\x2d" - "\x40\x53\x66\x79\x8c\x9f\xb2\xc5" - "\xd8\xeb\xfe\x11\x24\x37\x4a\x5d" - "\x70\x83\x96\xa9\xbc\xcf\xe2\xf5" - "\x08\x1b\x2e\x41\x54\x67\x7a\x8d" - "\xa0\xb3\xc6\xd9\xec\xff\x12\x25" - "\x38\x4b\x5e\x71\x84\x97\xaa\xbd" - "\xd0\xe3\xf6\x09\x1c\x2f\x42\x55" - "\x68\x7b\x8e\xa1\xb4\xc7\xda\xed" - "\x00\x15\x2a\x3f\x54\x69\x7e\x93" - "\xa8\xbd\xd2\xe7\xfc\x11\x26\x3b" - "\x50\x65\x7a\x8f\xa4\xb9\xce\xe3" - "\xf8\x0d\x22\x37\x4c\x61\x76\x8b" - "\xa0\xb5\xca\xdf\xf4\x09\x1e\x33" - "\x48\x5d\x72\x87\x9c\xb1\xc6\xdb" - "\xf0\x05\x1a\x2f\x44\x59\x6e\x83" - "\x98\xad\xc2\xd7\xec\x01\x16\x2b" - "\x40\x55\x6a\x7f\x94\xa9\xbe\xd3" - "\xe8\xfd\x12\x27\x3c\x51\x66\x7b" - "\x90\xa5\xba\xcf\xe4\xf9\x0e\x23" - "\x38\x4d\x62\x77\x8c\xa1\xb6\xcb" - "\xe0\xf5\x0a\x1f\x34\x49\x5e\x73" - "\x88\x9d\xb2\xc7\xdc\xf1\x06\x1b" - "\x30\x45\x5a\x6f\x84\x99\xae\xc3" - "\xd8\xed\x02\x17\x2c\x41\x56\x6b" - "\x80\x95\xaa\xbf\xd4\xe9\xfe\x13" - "\x28\x3d\x52\x67\x7c\x91\xa6\xbb" - "\xd0\xe5\xfa\x0f\x24\x39\x4e\x63" - "\x78\x8d\xa2\xb7\xcc\xe1\xf6\x0b" - "\x20\x35\x4a\x5f\x74\x89\x9e\xb3" - "\xc8\xdd\xf2\x07\x1c\x31\x46\x5b" - "\x70\x85\x9a\xaf\xc4\xd9\xee\x03" - "\x18\x2d\x42\x57\x6c\x81\x96\xab" - "\xc0\xd5\xea\xff\x14\x29\x3e\x53" - "\x68\x7d\x92\xa7\xbc\xd1\xe6\xfb" - "\x10\x25\x3a\x4f\x64\x79\x8e\xa3" - "\xb8\xcd\xe2\xf7\x0c\x21\x36\x4b" - "\x60\x75\x8a\x9f\xb4\xc9\xde\xf3" - "\x08\x1d\x32\x47\x5c\x71\x86\x9b" - "\xb0\xc5\xda\xef\x04\x19\x2e\x43" - "\x58\x6d\x82\x97\xac\xc1\xd6\xeb" - "\x00\x17\x2e\x45\x5c\x73\x8a\xa1" - "\xb8\xcf\xe6\xfd\x14\x2b\x42\x59" - "\x70\x87\x9e\xb5\xcc\xe3\xfa\x11" - "\x28\x3f\x56\x6d\x84\x9b\xb2\xc9" - "\xe0\xf7\x0e\x25\x3c\x53\x6a\x81" - "\x98\xaf\xc6\xdd\xf4\x0b\x22\x39" - "\x50\x67\x7e\x95\xac\xc3\xda\xf1" - "\x08\x1f\x36\x4d\x64\x7b\x92\xa9" - "\xc0\xd7\xee\x05\x1c\x33\x4a\x61" - "\x78\x8f\xa6\xbd\xd4\xeb\x02\x19" - "\x30\x47\x5e\x75\x8c\xa3\xba\xd1" - "\xe8\xff\x16\x2d\x44\x5b\x72\x89" - "\xa0\xb7\xce\xe5\xfc\x13\x2a\x41" - "\x58\x6f\x86\x9d\xb4\xcb\xe2\xf9" - "\x10\x27\x3e\x55\x6c\x83\x9a\xb1" - "\xc8\xdf\xf6\x0d\x24\x3b\x52\x69" - "\x80\x97\xae\xc5\xdc\xf3\x0a\x21" - "\x38\x4f\x66\x7d\x94\xab\xc2\xd9" - "\xf0\x07\x1e\x35\x4c\x63\x7a\x91" - "\xa8\xbf\xd6\xed\x04\x1b\x32\x49" - "\x60\x77\x8e\xa5\xbc\xd3\xea\x01" - "\x18\x2f\x46\x5d\x74\x8b\xa2\xb9" - "\xd0\xe7\xfe\x15\x2c\x43\x5a\x71" - "\x88\x9f\xb6\xcd\xe4\xfb\x12\x29" - "\x40\x57\x6e\x85\x9c\xb3\xca\xe1" - "\xf8\x0f\x26\x3d\x54\x6b\x82\x99" - "\xb0\xc7\xde\xf5\x0c\x23\x3a\x51" - "\x68\x7f\x96\xad\xc4\xdb\xf2\x09" - "\x20\x37\x4e\x65\x7c\x93\xaa\xc1" - "\xd8\xef\x06\x1d\x34\x4b\x62\x79" - "\x90\xa7\xbe\xd5\xec\x03\x1a\x31" - "\x48\x5f\x76\x8d\xa4\xbb\xd2\xe9" - "\x00\x19\x32\x4b\x64\x7d\x96\xaf" - "\xc8\xe1\xfa\x13\x2c\x45\x5e\x77" - "\x90\xa9\xc2\xdb\xf4\x0d\x26\x3f" - "\x58\x71\x8a\xa3\xbc\xd5\xee\x07" - "\x20\x39\x52\x6b\x84\x9d\xb6\xcf" - "\xe8\x01\x1a\x33\x4c\x65\x7e\x97" - "\xb0\xc9\xe2\xfb\x14\x2d\x46\x5f" - "\x78\x91\xaa\xc3\xdc\xf5\x0e\x27" - "\x40\x59\x72\x8b\xa4\xbd\xd6\xef" - "\x08\x21\x3a\x53\x6c\x85\x9e\xb7" - "\xd0\xe9\x02\x1b\x34\x4d\x66\x7f" - "\x98\xb1\xca\xe3\xfc\x15\x2e\x47" - "\x60\x79\x92\xab\xc4\xdd\xf6\x0f" - "\x28\x41\x5a\x73\x8c\xa5\xbe\xd7" - "\xf0\x09\x22\x3b\x54\x6d\x86\x9f" - "\xb8\xd1\xea\x03\x1c\x35\x4e\x67" - "\x80\x99\xb2\xcb\xe4\xfd\x16\x2f" - "\x48\x61\x7a\x93\xac\xc5\xde\xf7" - "\x10\x29\x42\x5b\x74\x8d\xa6\xbf" - "\xd8\xf1\x0a\x23\x3c\x55\x6e\x87" - "\xa0\xb9\xd2\xeb\x04\x1d\x36\x4f" - "\x68\x81\x9a\xb3\xcc\xe5\xfe\x17" - "\x30\x49\x62\x7b\x94\xad\xc6\xdf" - "\xf8\x11\x2a\x43\x5c\x75\x8e\xa7" - "\xc0\xd9\xf2\x0b\x24\x3d\x56\x6f" - "\x88\xa1\xba\xd3\xec\x05\x1e\x37" - "\x50\x69\x82\x9b\xb4\xcd\xe6\xff" - "\x18\x31\x4a\x63\x7c\x95\xae\xc7" - "\xe0\xf9\x12\x2b\x44\x5d\x76\x8f" - "\xa8\xc1\xda\xf3\x0c\x25\x3e\x57" - "\x70\x89\xa2\xbb\xd4\xed\x06\x1f" - "\x38\x51\x6a\x83\x9c\xb5\xce\xe7" - "\x00\x1b\x36\x51\x6c\x87\xa2\xbd" - "\xd8\xf3\x0e\x29\x44\x5f\x7a\x95" - "\xb0\xcb\xe6\x01\x1c\x37\x52\x6d" - "\x88\xa3\xbe\xd9\xf4\x0f\x2a\x45" - "\x60\x7b\x96\xb1\xcc\xe7\x02\x1d" - "\x38\x53\x6e\x89\xa4\xbf\xda\xf5" - "\x10\x2b\x46\x61\x7c\x97\xb2\xcd" - "\xe8\x03\x1e\x39\x54\x6f\x8a\xa5" - "\xc0\xdb\xf6\x11\x2c\x47\x62\x7d" - "\x98\xb3\xce\xe9\x04\x1f\x3a\x55" - "\x70\x8b\xa6\xc1\xdc\xf7\x12\x2d" - "\x48\x63\x7e\x99\xb4\xcf\xea\x05" - "\x20\x3b\x56\x71\x8c\xa7\xc2\xdd" - "\xf8\x13\x2e\x49\x64\x7f\x9a\xb5" - "\xd0\xeb\x06\x21\x3c\x57\x72\x8d" - "\xa8\xc3\xde\xf9\x14\x2f\x4a\x65" - "\x80\x9b\xb6\xd1\xec\x07\x22\x3d" - "\x58\x73\x8e\xa9\xc4\xdf\xfa\x15" - "\x30\x4b\x66\x81\x9c\xb7\xd2\xed" - "\x08\x23\x3e\x59\x74\x8f\xaa\xc5" - "\xe0\xfb\x16\x31\x4c\x67\x82\x9d" - "\xb8\xd3\xee\x09\x24\x3f\x5a\x75" - "\x90\xab\xc6\xe1\xfc\x17\x32\x4d" - "\x68\x83\x9e\xb9\xd4\xef\x0a\x25" - "\x40\x5b\x76\x91\xac\xc7\xe2\xfd" - "\x18\x33\x4e\x69\x84\x9f\xba\xd5" - "\xf0\x0b\x26\x41\x5c\x77\x92\xad" - "\xc8\xe3\xfe\x19\x34\x4f\x6a\x85" - "\xa0\xbb\xd6\xf1\x0c\x27\x42\x5d" - "\x78\x93\xae\xc9\xe4\xff\x1a\x35" - "\x50\x6b\x86\xa1\xbc\xd7\xf2\x0d" - "\x28\x43\x5e\x79\x94\xaf\xca\xe5" - "\x00\x1d\x3a\x57\x74\x91\xae\xcb" - "\xe8\x05\x22\x3f\x5c\x79\x96\xb3" - "\xd0\xed\x0a\x27\x44\x61\x7e\x9b" - "\xb8\xd5\xf2\x0f\x2c\x49\x66\x83" - "\xa0\xbd\xda\xf7\x14\x31\x4e\x6b" - "\x88\xa5\xc2\xdf\xfc\x19\x36\x53" - "\x70\x8d\xaa\xc7\xe4\x01\x1e\x3b" - "\x58\x75\x92\xaf\xcc\xe9\x06\x23" - "\x40\x5d\x7a\x97\xb4\xd1\xee\x0b" - "\x28\x45\x62\x7f\x9c\xb9\xd6\xf3" - "\x10\x2d\x4a\x67\x84\xa1\xbe\xdb" - "\xf8\x15\x32\x4f\x6c\x89\xa6\xc3" - "\xe0\xfd\x1a\x37\x54\x71\x8e\xab" - "\xc8\xe5\x02\x1f\x3c\x59\x76\x93" - "\xb0\xcd\xea\x07\x24\x41\x5e\x7b" - "\x98\xb5\xd2\xef\x0c\x29\x46\x63" - "\x80\x9d\xba\xd7\xf4\x11\x2e\x4b" - "\x68\x85\xa2\xbf\xdc\xf9\x16\x33" - "\x50\x6d\x8a\xa7\xc4\xe1\xfe\x1b" - "\x38\x55\x72\x8f\xac\xc9\xe6\x03" - "\x20\x3d\x5a\x77\x94\xb1\xce\xeb" - "\x08\x25\x42\x5f\x7c\x99\xb6\xd3" - "\xf0\x0d\x2a\x47\x64\x81\x9e\xbb" - "\xd8\xf5\x12\x2f\x4c\x69\x86\xa3" - "\xc0\xdd\xfa\x17\x34\x51\x6e\x8b" - "\xa8\xc5\xe2\xff\x1c\x39\x56\x73" - "\x90\xad\xca\xe7\x04\x21\x3e\x5b" - "\x78\x95\xb2\xcf\xec\x09\x26\x43" - "\x60\x7d\x9a\xb7\xd4\xf1\x0e\x2b" - "\x48\x65\x82\x9f\xbc\xd9\xf6\x13" - "\x30\x4d\x6a\x87\xa4\xc1\xde\xfb" - "\x18\x35\x52\x6f\x8c\xa9\xc6\xe3" - "\x00\x1f\x3e\x5d\x7c\x9b\xba\xd9" - "\xf8\x17\x36\x55\x74\x93\xb2\xd1" - "\xf0\x0f\x2e\x4d\x6c\x8b\xaa\xc9" - "\xe8\x07\x26\x45\x64\x83\xa2\xc1" - "\xe0\xff\x1e\x3d\x5c\x7b\x9a\xb9" - "\xd8\xf7\x16\x35\x54\x73\x92\xb1" - "\xd0\xef\x0e\x2d\x4c\x6b\x8a\xa9" - "\xc8\xe7\x06\x25\x44\x63\x82\xa1" - "\xc0\xdf\xfe\x1d\x3c\x5b\x7a\x99" - "\xb8\xd7\xf6\x15\x34\x53\x72\x91" - "\xb0\xcf\xee\x0d\x2c\x4b\x6a\x89" - "\xa8\xc7\xe6\x05\x24\x43\x62\x81" - "\xa0\xbf\xde\xfd\x1c\x3b\x5a\x79" - "\x98\xb7\xd6\xf5\x14\x33\x52\x71" - "\x90\xaf\xce\xed\x0c\x2b\x4a\x69" - "\x88\xa7\xc6\xe5\x04\x23\x42\x61" - "\x80\x9f\xbe\xdd\xfc\x1b\x3a\x59" - "\x78\x97\xb6\xd5\xf4\x13\x32\x51" - "\x70\x8f\xae\xcd\xec\x0b\x2a\x49" - "\x68\x87\xa6\xc5\xe4\x03\x22\x41" - "\x60\x7f\x9e\xbd\xdc\xfb\x1a\x39" - "\x58\x77\x96\xb5\xd4\xf3\x12\x31" - "\x50\x6f\x8e\xad\xcc\xeb\x0a\x29" - "\x48\x67\x86\xa5\xc4\xe3\x02\x21" - "\x40\x5f\x7e\x9d\xbc\xdb\xfa\x19" - "\x38\x57\x76\x95\xb4\xd3\xf2\x11" - "\x30\x4f\x6e\x8d\xac\xcb\xea\x09" - "\x28\x47\x66\x85\xa4\xc3\xe2\x01" - "\x20\x3f\x5e\x7d\x9c\xbb\xda\xf9" - "\x18\x37\x56\x75\x94\xb3\xd2\xf1" - "\x10\x2f\x4e\x6d\x8c\xab\xca\xe9" - "\x08\x27\x46\x65\x84\xa3\xc2\xe1" - "\x00\x21\x42\x63", - .ilen = 4100, - .result = - "\xb5\x81\xf5\x64\x18\x73\xe3\xf0" - "\x4c\x13\xf2\x77\x18\x60\x65\x5e" - "\x29\x01\xce\x98\x55\x53\xf9\x0c" - "\x2a\x08\xd5\x09\xb3\x57\x55\x56" - "\xc5\xe9\x56\x90\xcb\x6a\xa3\xc0" - "\xff\xc4\x79\xb4\xd2\x97\x5d\xc4" - "\x43\xd1\xfe\x94\x7b\x88\x06\x5a" - "\xb2\x9e\x2c\xfc\x44\x03\xb7\x90" - "\xa0\xc1\xba\x6a\x33\xb8\xc7\xb2" - "\x9d\xe1\x12\x4f\xc0\x64\xd4\x01" - "\xfe\x8c\x7a\x66\xf7\xe6\x5a\x91" - "\xbb\xde\x56\x86\xab\x65\x21\x30" - "\x00\x84\x65\x24\xa5\x7d\x85\xb4" - "\xe3\x17\xed\x3a\xb7\x6f\xb4\x0b" - "\x0b\xaf\x15\xae\x5a\x8f\xf2\x0c" - "\x2f\x27\xf4\x09\xd8\xd2\x96\xb7" - "\x71\xf2\xc5\x99\x4d\x7e\x7f\x75" - "\x77\x89\x30\x8b\x59\xdb\xa2\xb2" - "\xa0\xf3\x19\x39\x2b\xc5\x7e\x3f" - "\x4f\xd9\xd3\x56\x28\x97\x44\xdc" - "\xc0\x8b\x77\x24\xd9\x52\xe7\xc5" - "\xaf\xf6\x7d\x59\xb2\x44\x05\x1d" - "\xb1\xb0\x11\xa5\x0f\xec\x33\xe1" - "\x6d\x1b\x4e\x1f\xff\x57\x91\xb4" - "\x5b\x9a\x96\xc5\x53\xbc\xae\x20" - "\x3c\xbb\x14\xe2\xe8\x22\x33\xc1" - "\x5e\x76\x9e\x46\x99\xf6\x2a\x15" - "\xc6\x97\x02\xa0\x66\x43\xd1\xa6" - "\x31\xa6\x9f\xfb\xf4\xd3\x69\xe5" - "\xcd\x76\x95\xb8\x7a\x82\x7f\x21" - "\x45\xff\x3f\xce\x55\xf6\x95\x10" - "\x08\x77\x10\x43\xc6\xf3\x09\xe5" - "\x68\xe7\x3c\xad\x00\x52\x45\x0d" - "\xfe\x2d\xc6\xc2\x94\x8c\x12\x1d" - "\xe6\x25\xae\x98\x12\x8e\x19\x9c" - "\x81\x68\xb1\x11\xf6\x69\xda\xe3" - "\x62\x08\x18\x7a\x25\x49\x28\xac" - "\xba\x71\x12\x0b\xe4\xa2\xe5\xc7" - "\x5d\x8e\xec\x49\x40\x21\xbf\x5a" - "\x98\xf3\x02\x68\x55\x03\x7f\x8a" - "\xe5\x94\x0c\x32\x5c\x07\x82\x63" - "\xaf\x6f\x91\x40\x84\x8e\x52\x25" - "\xd0\xb0\x29\x53\x05\xe2\x50\x7a" - "\x34\xeb\xc9\x46\x20\xa8\x3d\xde" - "\x7f\x16\x5f\x36\xc5\x2e\xdc\xd1" - "\x15\x47\xc7\x50\x40\x6d\x91\xc5" - "\xe7\x93\x95\x1a\xd3\x57\xbc\x52" - "\x33\xee\x14\x19\x22\x52\x89\xa7" - "\x4a\x25\x56\x77\x4b\xca\xcf\x0a" - "\xe1\xf5\x35\x85\x30\x7e\x59\x4a" - "\xbd\x14\x5b\xdf\xe3\x46\xcb\xac" - "\x1f\x6c\x96\x0e\xf4\x81\xd1\x99" - "\xca\x88\x63\x3d\x02\x58\x6b\xa9" - "\xe5\x9f\xb3\x00\xb2\x54\xc6\x74" - "\x1c\xbf\x46\xab\x97\xcc\xf8\x54" - "\x04\x07\x08\x52\xe6\xc0\xda\x93" - "\x74\x7d\x93\x99\x5d\x78\x68\xa6" - "\x2e\x6b\xd3\x6a\x69\xcc\x12\x6b" - "\xd4\xc7\xa5\xc6\xe7\xf6\x03\x04" - "\x5d\xcd\x61\x5e\x17\x40\xdc\xd1" - "\x5c\xf5\x08\xdf\x5c\x90\x85\xa4" - "\xaf\xf6\x78\xbb\x0d\xf1\xf4\xa4" - "\x54\x26\x72\x9e\x61\xfa\x86\xcf" - "\xe8\x9e\xa1\xe0\xc7\x48\x23\xae" - "\x5a\x90\xae\x75\x0a\x74\x18\x89" - "\x05\xb1\x92\xb2\x7f\xd0\x1b\xa6" - "\x62\x07\x25\x01\xc7\xc2\x4f\xf9" - "\xe8\xfe\x63\x95\x80\x07\xb4\x26" - "\xcc\xd1\x26\xb6\xc4\x3f\x9e\xcb" - "\x8e\x3b\x2e\x44\x16\xd3\x10\x9a" - "\x95\x08\xeb\xc8\xcb\xeb\xbf\x6f" - "\x0b\xcd\x1f\xc8\xca\x86\xaa\xec" - "\x33\xe6\x69\xf4\x45\x25\x86\x3a" - "\x22\x94\x4f\x00\x23\x6a\x44\xc2" - "\x49\x97\x33\xab\x36\x14\x0a\x70" - "\x24\xc3\xbe\x04\x3b\x79\xa0\xf9" - "\xb8\xe7\x76\x29\x22\x83\xd7\xf2" - "\x94\xf4\x41\x49\xba\x5f\x7b\x07" - "\xb5\xfb\xdb\x03\x1a\x9f\xb6\x4c" - "\xc2\x2e\x37\x40\x49\xc3\x38\x16" - "\xe2\x4f\x77\x82\xb0\x68\x4c\x71" - "\x1d\x57\x61\x9c\xd9\x4e\x54\x99" - "\x47\x13\x28\x73\x3c\xbb\x00\x90" - "\xf3\x4d\xc9\x0e\xfd\xe7\xb1\x71" - "\xd3\x15\x79\xbf\xcc\x26\x2f\xbd" - "\xad\x6c\x50\x69\x6c\x3e\x6d\x80" - "\x9a\xea\x78\xaf\x19\xb2\x0d\x4d" - "\xad\x04\x07\xae\x22\x90\x4a\x93" - "\x32\x0e\x36\x9b\x1b\x46\xba\x3b" - "\xb4\xac\xc6\xd1\xa2\x31\x53\x3b" - "\x2a\x3d\x45\xfe\x03\x61\x10\x85" - "\x17\x69\xa6\x78\xcc\x6c\x87\x49" - "\x53\xf9\x80\x10\xde\x80\xa2\x41" - "\x6a\xc3\x32\x02\xad\x6d\x3c\x56" - "\x00\x71\x51\x06\xa7\xbd\xfb\xef" - "\x3c\xb5\x9f\xfc\x48\x7d\x53\x7c" - "\x66\xb0\x49\x23\xc4\x47\x10\x0e" - "\xe5\x6c\x74\x13\xe6\xc5\x3f\xaa" - "\xde\xff\x07\x44\xdd\x56\x1b\xad" - "\x09\x77\xfb\x5b\x12\xb8\x0d\x38" - "\x17\x37\x35\x7b\x9b\xbc\xfe\xd4" - "\x7e\x8b\xda\x7e\x5b\x04\xa7\x22" - "\xa7\x31\xa1\x20\x86\xc7\x1b\x99" - "\xdb\xd1\x89\xf4\x94\xa3\x53\x69" - "\x8d\xe7\xe8\x74\x11\x8d\x74\xd6" - "\x07\x37\x91\x9f\xfd\x67\x50\x3a" - "\xc9\xe1\xf4\x36\xd5\xa0\x47\xd1" - "\xf9\xe5\x39\xa3\x31\xac\x07\x36" - "\x23\xf8\x66\x18\x14\x28\x34\x0f" - "\xb8\xd0\xe7\x29\xb3\x04\x4b\x55" - "\x01\x41\xb2\x75\x8d\xcb\x96\x85" - "\x3a\xfb\xab\x2b\x9e\xfa\x58\x20" - "\x44\x1f\xc0\x14\x22\x75\x61\xe8" - "\xaa\x19\xcf\xf1\x82\x56\xf4\xd7" - "\x78\x7b\x3d\x5f\xb3\x9e\x0b\x8a" - "\x57\x50\xdb\x17\x41\x65\x4d\xa3" - "\x02\xc9\x9c\x9c\x53\xfb\x39\x39" - "\x9b\x1d\x72\x24\xda\xb7\x39\xbe" - "\x13\x3b\xfa\x29\xda\x9e\x54\x64" - "\x6e\xba\xd8\xa1\xcb\xb3\x36\xfa" - "\xcb\x47\x85\xe9\x61\x38\xbc\xbe" - "\xc5\x00\x38\x2a\x54\xf7\xc4\xb9" - "\xb3\xd3\x7b\xa0\xa0\xf8\x72\x7f" - "\x8c\x8e\x82\x0e\xc6\x1c\x75\x9d" - "\xca\x8e\x61\x87\xde\xad\x80\xd2" - "\xf5\xf9\x80\xef\x15\x75\xaf\xf5" - "\x80\xfb\xff\x6d\x1e\x25\xb7\x40" - "\x61\x6a\x39\x5a\x6a\xb5\x31\xab" - "\x97\x8a\x19\x89\x44\x40\xc0\xa6" - "\xb4\x4e\x30\x32\x7b\x13\xe7\x67" - "\xa9\x8b\x57\x04\xc2\x01\xa6\xf4" - "\x28\x99\xad\x2c\x76\xa3\x78\xc2" - "\x4a\xe6\xca\x5c\x50\x6a\xc1\xb0" - "\x62\x4b\x10\x8e\x7c\x17\x43\xb3" - "\x17\x66\x1c\x3e\x8d\x69\xf0\x5a" - "\x71\xf5\x97\xdc\xd1\x45\xdd\x28" - "\xf3\x5d\xdf\x53\x7b\x11\xe5\xbc" - "\x4c\xdb\x1b\x51\x6b\xe9\xfb\x3d" - "\xc1\xc3\x2c\xb9\x71\xf5\xb6\xb2" - "\x13\x36\x79\x80\x53\xe8\xd3\xa6" - "\x0a\xaf\xfd\x56\x97\xf7\x40\x8e" - "\x45\xce\xf8\xb0\x9e\x5c\x33\x82" - "\xb0\x44\x56\xfc\x05\x09\xe9\x2a" - "\xac\x26\x80\x14\x1d\xc8\x3a\x35" - "\x4c\x82\x97\xfd\x76\xb7\xa9\x0a" - "\x35\x58\x79\x8e\x0f\x66\xea\xaf" - "\x51\x6c\x09\xa9\x6e\x9b\xcb\x9a" - "\x31\x47\xa0\x2f\x7c\x71\xb4\x4a" - "\x11\xaa\x8c\x66\xc5\x64\xe6\x3a" - "\x54\xda\x24\x6a\xc4\x41\x65\x46" - "\x82\xa0\x0a\x0f\x5f\xfb\x25\xd0" - "\x2c\x91\xa7\xee\xc4\x81\x07\x86" - "\x75\x5e\x33\x69\x97\xe4\x2c\xa8" - "\x9d\x9f\x0b\x6a\xbe\xad\x98\xda" - "\x6d\x94\x41\xda\x2c\x1e\x89\xc4" - "\xc2\xaf\x1e\x00\x05\x0b\x83\x60" - "\xbd\x43\xea\x15\x23\x7f\xb9\xac" - "\xee\x4f\x2c\xaf\x2a\xf3\xdf\xd0" - "\xf3\x19\x31\xbb\x4a\x74\x84\x17" - "\x52\x32\x2c\x7d\x61\xe4\xcb\xeb" - "\x80\x38\x15\x52\xcb\x6f\xea\xe5" - "\x73\x9c\xd9\x24\x69\xc6\x95\x32" - "\x21\xc8\x11\xe4\xdc\x36\xd7\x93" - "\x38\x66\xfb\xb2\x7f\x3a\xb9\xaf" - "\x31\xdd\x93\x75\x78\x8a\x2c\x94" - "\x87\x1a\x58\xec\x9e\x7d\x4d\xba" - "\xe1\xe5\x4d\xfc\xbc\xa4\x2a\x14" - "\xef\xcc\xa7\xec\xab\x43\x09\x18" - "\xd3\xab\x68\xd1\x07\x99\x44\x47" - "\xd6\x83\x85\x3b\x30\xea\xa9\x6b" - "\x63\xea\xc4\x07\xfb\x43\x2f\xa4" - "\xaa\xb0\xab\x03\x89\xce\x3f\x8c" - "\x02\x7c\x86\x54\xbc\x88\xaf\x75" - "\xd2\xdc\x63\x17\xd3\x26\xf6\x96" - "\xa9\x3c\xf1\x61\x8c\x11\x18\xcc" - "\xd6\xea\x5b\xe2\xcd\xf0\xf1\xb2" - "\xe5\x35\x90\x1f\x85\x4c\x76\x5b" - "\x66\xce\x44\xa4\x32\x9f\xe6\x7b" - "\x71\x6e\x9f\x58\x15\x67\x72\x87" - "\x64\x8e\x3a\x44\x45\xd4\x76\xfa" - "\xc2\xf6\xef\x85\x05\x18\x7a\x9b" - "\xba\x41\x54\xac\xf0\xfc\x59\x12" - "\x3f\xdf\xa0\xe5\x8a\x65\xfd\x3a" - "\x62\x8d\x83\x2c\x03\xbe\x05\x76" - "\x2e\x53\x49\x97\x94\x33\xae\x40" - "\x81\x15\xdb\x6e\xad\xaa\xf5\x4b" - "\xe3\x98\x70\xdf\xe0\x7c\xcd\xdb" - "\x02\xd4\x7d\x2f\xc1\xe6\xb4\xf3" - "\xd7\x0d\x7a\xd9\x23\x9e\x87\x2d" - "\xce\x87\xad\xcc\x72\x05\x00\x29" - "\xdc\x73\x7f\x64\xc1\x15\x0e\xc2" - "\xdf\xa7\x5f\xeb\x41\xa1\xcd\xef" - "\x5c\x50\x79\x2a\x56\x56\x71\x8c" - "\xac\xc0\x79\x50\x69\xca\x59\x32" - "\x65\xf2\x54\xe4\x52\x38\x76\xd1" - "\x5e\xde\x26\x9e\xfb\x75\x2e\x11" - "\xb5\x10\xf4\x17\x73\xf5\x89\xc7" - "\x4f\x43\x5c\x8e\x7c\xb9\x05\x52" - "\x24\x40\x99\xfe\x9b\x85\x0b\x6c" - "\x22\x3e\x8b\xae\x86\xa1\xd2\x79" - "\x05\x68\x6b\xab\xe3\x41\x49\xed" - "\x15\xa1\x8d\x40\x2d\x61\xdf\x1a" - "\x59\xc9\x26\x8b\xef\x30\x4c\x88" - "\x4b\x10\xf8\x8d\xa6\x92\x9f\x4b" - "\xf3\xc4\x53\x0b\x89\x5d\x28\x92" - "\xcf\x78\xb2\xc0\x5d\xed\x7e\xfc" - "\xc0\x12\x23\x5f\x5a\x78\x86\x43" - "\x6e\x27\xf7\x5a\xa7\x6a\xed\x19" - "\x04\xf0\xb3\x12\xd1\xbd\x0e\x89" - "\x6e\xbc\x96\xa8\xd8\x49\x39\x9f" - "\x7e\x67\xf0\x2e\x3e\x01\xa9\xba" - "\xec\x8b\x62\x8e\xcb\x4a\x70\x43" - "\xc7\xc2\xc4\xca\x82\x03\x73\xe9" - "\x11\xdf\xcf\x54\xea\xc9\xb0\x95" - "\x51\xc0\x13\x3d\x92\x05\xfa\xf4" - "\xa9\x34\xc8\xce\x6c\x3d\x54\xcc" - "\xc4\xaf\xf1\xdc\x11\x44\x26\xa2" - "\xaf\xf1\x85\x75\x7d\x03\x61\x68" - "\x4e\x78\xc6\x92\x7d\x86\x7d\x77" - "\xdc\x71\x72\xdb\xc6\xae\xa1\xcb" - "\x70\x9a\x0b\x19\xbe\x4a\x6c\x2a" - "\xe2\xba\x6c\x64\x9a\x13\x28\xdf" - "\x85\x75\xe6\x43\xf6\x87\x08\x68" - "\x6e\xba\x6e\x79\x9f\x04\xbc\x23" - "\x50\xf6\x33\x5c\x1f\x24\x25\xbe" - "\x33\x47\x80\x45\x56\xa3\xa7\xd7" - "\x7a\xb1\x34\x0b\x90\x3c\x9c\xad" - "\x44\x5f\x9e\x0e\x9d\xd4\xbd\x93" - "\x5e\xfa\x3c\xe0\xb0\xd9\xed\xf3" - "\xd6\x2e\xff\x24\xd8\x71\x6c\xed" - "\xaf\x55\xeb\x22\xac\x93\x68\x32" - "\x05\x5b\x47\xdd\xc6\x4a\xcb\xc7" - "\x10\xe1\x3c\x92\x1a\xf3\x23\x78" - "\x2b\xa1\xd2\x80\xf4\x12\xb1\x20" - "\x8f\xff\x26\x35\xdd\xfb\xc7\x4e" - "\x78\xf1\x2d\x50\x12\x77\xa8\x60" - "\x7c\x0f\xf5\x16\x2f\x63\x70\x2a" - "\xc0\x96\x80\x4e\x0a\xb4\x93\x35" - "\x5d\x1d\x3f\x56\xf7\x2f\xbb\x90" - "\x11\x16\x8f\xa2\xec\x47\xbe\xac" - "\x56\x01\x26\x56\xb1\x8c\xb2\x10" - "\xf9\x1a\xca\xf5\xd1\xb7\x39\x20" - "\x63\xf1\x69\x20\x4f\x13\x12\x1f" - "\x5b\x65\xfc\x98\xf7\xc4\x7a\xbe" - "\xf7\x26\x4d\x2b\x84\x7b\x42\xad" - "\xd8\x7a\x0a\xb4\xd8\x74\xbf\xc1" - "\xf0\x6e\xb4\x29\xa3\xbb\xca\x46" - "\x67\x70\x6a\x2d\xce\x0e\xa2\x8a" - "\xa9\x87\xbf\x05\xc4\xc1\x04\xa3" - "\xab\xd4\x45\x43\x8c\xb6\x02\xb0" - "\x41\xc8\xfc\x44\x3d\x59\xaa\x2e" - "\x44\x21\x2a\x8d\x88\x9d\x57\xf4" - "\xa0\x02\x77\xb8\xa6\xa0\xe6\x75" - "\x5c\x82\x65\x3e\x03\x5c\x29\x8f" - "\x38\x55\xab\x33\x26\xef\x9f\x43" - "\x52\xfd\x68\xaf\x36\xb4\xbb\x9a" - "\x58\x09\x09\x1b\xc3\x65\x46\x46" - "\x1d\xa7\x94\x18\x23\x50\x2c\xca" - "\x2c\x55\x19\x97\x01\x9d\x93\x3b" - "\x63\x86\xf2\x03\x67\x45\xd2\x72" - "\x28\x52\x6c\xf4\xe3\x1c\xb5\x11" - "\x13\xf1\xeb\x21\xc7\xd9\x56\x82" - "\x2b\x82\x39\xbd\x69\x54\xed\x62" - "\xc3\xe2\xde\x73\xd4\x6a\x12\xae" - "\x13\x21\x7f\x4b\x5b\xfc\xbf\xe8" - "\x2b\xbe\x56\xba\x68\x8b\x9a\xb1" - "\x6e\xfa\xbf\x7e\x5a\x4b\xf1\xac" - "\x98\x65\x85\xd1\x93\x53\xd3\x7b" - "\x09\xdd\x4b\x10\x6d\x84\xb0\x13" - "\x65\xbd\xcf\x52\x09\xc4\x85\xe2" - "\x84\x74\x15\x65\xb7\xf7\x51\xaf" - "\x55\xad\xa4\xd1\x22\x54\x70\x94" - "\xa0\x1c\x90\x41\xfd\x99\xd7\x5a" - "\x31\xef\xaa\x25\xd0\x7f\x4f\xea" - "\x1d\x55\x42\xe5\x49\xb0\xd0\x46" - "\x62\x36\x43\xb2\x82\x15\x75\x50" - "\xa4\x72\xeb\x54\x27\x1f\x8a\xe4" - "\x7d\xe9\x66\xc5\xf1\x53\xa4\xd1" - "\x0c\xeb\xb8\xf8\xbc\xd4\xe2\xe7" - "\xe1\xf8\x4b\xcb\xa9\xa1\xaf\x15" - "\x83\xcb\x72\xd0\x33\x79\x00\x2d" - "\x9f\xd7\xf1\x2e\x1e\x10\xe4\x45" - "\xc0\x75\x3a\x39\xea\x68\xf7\x5d" - "\x1b\x73\x8f\xe9\x8e\x0f\x72\x47" - "\xae\x35\x0a\x31\x7a\x14\x4d\x4a" - "\x6f\x47\xf7\x7e\x91\x6e\x74\x8b" - "\x26\x47\xf9\xc3\xf9\xde\x70\xf5" - "\x61\xab\xa9\x27\x9f\x82\xe4\x9c" - "\x89\x91\x3f\x2e\x6a\xfd\xb5\x49" - "\xe9\xfd\x59\x14\x36\x49\x40\x6d" - "\x32\xd8\x85\x42\xf3\xa5\xdf\x0c" - "\xa8\x27\xd7\x54\xe2\x63\x2f\xf2" - "\x7e\x8b\x8b\xe7\xf1\x9a\x95\x35" - "\x43\xdc\x3a\xe4\xb6\xf4\xd0\xdf" - "\x9c\xcb\x94\xf3\x21\xa0\x77\x50" - "\xe2\xc6\xc4\xc6\x5f\x09\x64\x5b" - "\x92\x90\xd8\xe1\xd1\xed\x4b\x42" - "\xd7\x37\xaf\x65\x3d\x11\x39\xb6" - "\x24\x8a\x60\xae\xd6\x1e\xbf\x0e" - "\x0d\xd7\xdc\x96\x0e\x65\x75\x4e" - "\x29\x06\x9d\xa4\x51\x3a\x10\x63" - "\x8f\x17\x07\xd5\x8e\x3c\xf4\x28" - "\x00\x5a\x5b\x05\x19\xd8\xc0\x6c" - "\xe5\x15\xe4\x9c\x9d\x71\x9d\x5e" - "\x94\x29\x1a\xa7\x80\xfa\x0e\x33" - "\x03\xdd\xb7\x3e\x9a\xa9\x26\x18" - "\x37\xa9\x64\x08\x4d\x94\x5a\x88" - "\xca\x35\xce\x81\x02\xe3\x1f\x1b" - "\x89\x1a\x77\x85\xe3\x41\x6d\x32" - "\x42\x19\x23\x7d\xc8\x73\xee\x25" - "\x85\x0d\xf8\x31\x25\x79\x1b\x6f" - "\x79\x25\xd2\xd8\xd4\x23\xfd\xf7" - "\x82\x36\x6a\x0c\x46\x22\x15\xe9" - "\xff\x72\x41\x91\x91\x7d\x3a\xb7" - "\xdd\x65\x99\x70\xf6\x8d\x84\xf8" - "\x67\x15\x20\x11\xd6\xb2\x55\x7b" - "\xdb\x87\xee\xef\x55\x89\x2a\x59" - "\x2b\x07\x8f\x43\x8a\x59\x3c\x01" - "\x8b\x65\x54\xa1\x66\xd5\x38\xbd" - "\xc6\x30\xa9\xcc\x49\xb6\xa8\x1b" - "\xb8\xc0\x0e\xe3\x45\x28\xe2\xff" - "\x41\x9f\x7e\x7c\xd1\xae\x9e\x25" - "\x3f\x4c\x7c\x7c\xf4\xa8\x26\x4d" - "\x5c\xfd\x4b\x27\x18\xf9\x61\x76" - "\x48\xba\x0c\x6b\xa9\x4d\xfc\xf5" - "\x3b\x35\x7e\x2f\x4a\xa9\xc2\x9a" - "\xae\xab\x86\x09\x89\xc9\xc2\x40" - "\x39\x2c\x81\xb3\xb8\x17\x67\xc2" - "\x0d\x32\x4a\x3a\x67\x81\xd7\x1a" - "\x34\x52\xc5\xdb\x0a\xf5\x63\x39" - "\xea\x1f\xe1\x7c\xa1\x9e\xc1\x35" - "\xe3\xb1\x18\x45\x67\xf9\x22\x38" - "\x95\xd9\x34\x34\x86\xc6\x41\x94" - "\x15\xf9\x5b\x41\xa6\x87\x8b\xf8" - "\xd5\xe1\x1b\xe2\x5b\xf3\x86\x10" - "\xff\xe6\xae\x69\x76\xbc\x0d\xb4" - "\x09\x90\x0c\xa2\x65\x0c\xad\x74" - "\xf5\xd7\xff\xda\xc1\xce\x85\xbe" - "\x00\xa7\xff\x4d\x2f\x65\xd3\x8c" - "\x86\x2d\x05\xe8\xed\x3e\x6b\x8b" - "\x0f\x3d\x83\x8c\xf1\x1d\x5b\x96" - "\x2e\xb1\x9c\xc2\x98\xe1\x70\xb9" - "\xba\x5c\x8a\x43\xd6\x34\xa7\x2d" - "\xc9\x92\xae\xf2\xa5\x7b\x05\x49" - "\xa7\x33\x34\x86\xca\xe4\x96\x23" - "\x76\x5b\xf2\xc6\xf1\x51\x28\x42" - "\x7b\xcc\x76\x8f\xfa\xa2\xad\x31" - "\xd4\xd6\x7a\x6d\x25\x25\x54\xe4" - "\x3f\x50\x59\xe1\x5c\x05\xb7\x27" - "\x48\xbf\x07\xec\x1b\x13\xbe\x2b" - "\xa1\x57\x2b\xd5\xab\xd7\xd0\x4c" - "\x1e\xcb\x71\x9b\xc5\x90\x85\xd3" - "\xde\x59\xec\x71\xeb\x89\xbb\xd0" - "\x09\x50\xe1\x16\x3f\xfd\x1c\x34" - "\xc3\x1c\xa1\x10\x77\x53\x98\xef" - "\xf2\xfd\xa5\x01\x59\xc2\x9b\x26" - "\xc7\x42\xd9\x49\xda\x58\x2b\x6e" - "\x9f\x53\x19\x76\x7e\xd9\xc9\x0e" - "\x68\xc8\x7f\x51\x22\x42\xef\x49" - "\xa4\x55\xb6\x36\xac\x09\xc7\x31" - "\x88\x15\x4b\x2e\x8f\x3a\x08\xf7" - "\xd8\xf7\xa8\xc5\xa9\x33\xa6\x45" - "\xe4\xc4\x94\x76\xf3\x0d\x8f\x7e" - "\xc8\xf6\xbc\x23\x0a\xb6\x4c\xd3" - "\x6a\xcd\x36\xc2\x90\x5c\x5c\x3c" - "\x65\x7b\xc2\xd6\xcc\xe6\x0d\x87" - "\x73\x2e\x71\x79\x16\x06\x63\x28" - "\x09\x15\xd8\x89\x38\x38\x3d\xb5" - "\x42\x1c\x08\x24\xf7\x2a\xd2\x9d" - "\xc8\xca\xef\xf9\x27\xd8\x07\x86" - "\xf7\x43\x0b\x55\x15\x3f\x9f\x83" - "\xef\xdc\x49\x9d\x2a\xc1\x54\x62" - "\xbd\x9b\x66\x55\x9f\xb7\x12\xf3" - "\x1b\x4d\x9d\x2a\x5c\xed\x87\x75" - "\x87\x26\xec\x61\x2c\xb4\x0f\x89" - "\xb0\xfb\x2e\x68\x5d\x15\xc7\x8d" - "\x2e\xc0\xd9\xec\xaf\x4f\xd2\x25" - "\x29\xe8\xd2\x26\x2b\x67\xe9\xfc" - "\x2b\xa8\x67\x96\x12\x1f\x5b\x96" - "\xc6\x14\x53\xaf\x44\xea\xd6\xe2" - "\x94\x98\xe4\x12\x93\x4c\x92\xe0" - "\x18\xa5\x8d\x2d\xe4\x71\x3c\x47" - "\x4c\xf7\xe6\x47\x9e\xc0\x68\xdf" - "\xd4\xf5\x5a\x74\xb1\x2b\x29\x03" - "\x19\x07\xaf\x90\x62\x5c\x68\x98" - "\x48\x16\x11\x02\x9d\xee\xb4\x9b" - "\xe5\x42\x7f\x08\xfd\x16\x32\x0b" - "\xd0\xb3\xfa\x2b\xb7\x99\xf9\x29" - "\xcd\x20\x45\x9f\xb3\x1a\x5d\xa2" - "\xaf\x4d\xe0\xbd\x42\x0d\xbc\x74" - "\x99\x9c\x8e\x53\x1a\xb4\x3e\xbd" - "\xa2\x9a\x2d\xf7\xf8\x39\x0f\x67" - "\x63\xfc\x6b\xc0\xaf\xb3\x4b\x4f" - "\x55\xc4\xcf\xa7\xc8\x04\x11\x3e" - "\x14\x32\xbb\x1b\x38\x77\xd6\x7f" - "\x54\x4c\xdf\x75\xf3\x07\x2d\x33" - "\x9b\xa8\x20\xe1\x7b\x12\xb5\xf3" - "\xef\x2f\xce\x72\xe5\x24\x60\xc1" - "\x30\xe2\xab\xa1\x8e\x11\x09\xa8" - "\x21\x33\x44\xfe\x7f\x35\x32\x93" - "\x39\xa7\xad\x8b\x79\x06\xb2\xcb" - "\x4e\xa9\x5f\xc7\xba\x74\x29\xec" - "\x93\xa0\x4e\x54\x93\xc0\xbc\x55" - "\x64\xf0\x48\xe5\x57\x99\xee\x75" - "\xd6\x79\x0f\x66\xb7\xc6\x57\x76" - "\xf7\xb7\xf3\x9c\xc5\x60\xe8\x7f" - "\x83\x76\xd6\x0e\xaa\xe6\x90\x39" - "\x1d\xa6\x32\x6a\x34\xe3\x55\xf8" - "\x58\xa0\x58\x7d\x33\xe0\x22\x39" - "\x44\x64\x87\x86\x5a\x2f\xa7\x7e" - "\x0f\x38\xea\xb0\x30\xcc\x61\xa5" - "\x6a\x32\xae\x1e\xf7\xe9\xd0\xa9" - "\x0c\x32\x4b\xb5\x49\x28\xab\x85" - "\x2f\x8e\x01\x36\x38\x52\xd0\xba" - "\xd6\x02\x78\xf8\x0e\x3e\x9c\x8b" - "\x6b\x45\x99\x3f\x5c\xfe\x58\xf1" - "\x5c\x94\x04\xe1\xf5\x18\x6d\x51" - "\xb2\x5d\x18\x20\xb6\xc2\x9a\x42" - "\x1d\xb3\xab\x3c\xb6\x3a\x13\x03" - "\xb2\x46\x82\x4f\xfc\x64\xbc\x4f" - "\xca\xfa\x9c\xc0\xd5\xa7\xbd\x11" - "\xb7\xe4\x5a\xf6\x6f\x4d\x4d\x54" - "\xea\xa4\x98\x66\xd4\x22\x3b\xd3" - "\x8f\x34\x47\xd9\x7c\xf4\x72\x3b" - "\x4d\x02\x77\xf6\xd6\xdd\x08\x0a" - "\x81\xe1\x86\x89\x3e\x56\x10\x3c" - "\xba\xd7\x81\x8c\x08\xbc\x8b\xe2" - "\x53\xec\xa7\x89\xee\xc8\x56\xb5" - "\x36\x2c\xb2\x03\xba\x99\xdd\x7c" - "\x48\xa0\xb0\xbc\x91\x33\xe9\xa8" - "\xcb\xcd\xcf\x59\x5f\x1f\x15\xe2" - "\x56\xf5\x4e\x01\x35\x27\x45\x77" - "\x47\xc8\xbc\xcb\x7e\x39\xc1\x97" - "\x28\xd3\x84\xfc\x2c\x3e\xc8\xad" - "\x9c\xf8\x8a\x61\x9c\x28\xaa\xc5" - "\x99\x20\x43\x85\x9d\xa5\xe2\x8b" - "\xb8\xae\xeb\xd0\x32\x0d\x52\x78" - "\x09\x56\x3f\xc7\xd8\x7e\x26\xfc" - "\x37\xfb\x6f\x04\xfc\xfa\x92\x10" - "\xac\xf8\x3e\x21\xdc\x8c\x21\x16" - "\x7d\x67\x6e\xf6\xcd\xda\xb6\x98" - "\x23\xab\x23\x3c\xb2\x10\xa0\x53" - "\x5a\x56\x9f\xc5\xd0\xff\xbb\xe4" - "\x98\x3c\x69\x1e\xdb\x38\x8f\x7e" - "\x0f\xd2\x98\x88\x81\x8b\x45\x67" - "\xea\x33\xf1\xeb\xe9\x97\x55\x2e" - "\xd9\xaa\xeb\x5a\xec\xda\xe1\x68" - "\xa8\x9d\x3c\x84\x7c\x05\x3d\x62" - "\x87\x8f\x03\x21\x28\x95\x0c\x89" - "\x25\x22\x4a\xb0\x93\xa9\x50\xa2" - "\x2f\x57\x6e\x18\x42\x19\x54\x0c" - "\x55\x67\xc6\x11\x49\xf4\x5c\xd2" - "\xe9\x3d\xdd\x8b\x48\x71\x21\x00" - "\xc3\x9a\x6c\x85\x74\x28\x83\x4a" - "\x1b\x31\x05\xe1\x06\x92\xe7\xda" - "\x85\x73\x78\x45\x20\x7f\xae\x13" - "\x7c\x33\x06\x22\xf4\x83\xf9\x35" - "\x3f\x6c\x71\xa8\x4e\x48\xbe\x9b" - "\xce\x8a\xba\xda\xbe\x28\x08\xf7" - "\xe2\x14\x8c\x71\xea\x72\xf9\x33" - "\xf2\x88\x3f\xd7\xbb\x69\x6c\x29" - "\x19\xdc\x84\xce\x1f\x12\x4f\xc8" - "\xaf\xa5\x04\xba\x5a\xab\xb0\xd9" - "\x14\x1f\x6c\x68\x98\x39\x89\x7a" - "\xd9\xd8\x2f\xdf\xa8\x47\x4a\x25" - "\xe2\xfb\x33\xf4\x59\x78\xe1\x68" - "\x85\xcf\xfe\x59\x20\xd4\x05\x1d" - "\x80\x99\xae\xbc\xca\xae\x0f\x2f" - "\x65\x43\x34\x8e\x7e\xac\xd3\x93" - "\x2f\xac\x6d\x14\x3d\x02\x07\x70" - "\x9d\xa4\xf3\x1b\x5c\x36\xfc\x01" - "\x73\x34\x85\x0c\x6c\xd6\xf1\xbd" - "\x3f\xdf\xee\xf5\xd9\xba\x56\xef" - "\xf4\x9b\x6b\xee\x9f\x5a\x78\x6d" - "\x32\x19\xf4\xf7\xf8\x4c\x69\x0b" - "\x4b\xbc\xbb\xb7\xf2\x85\xaf\x70" - "\x75\x24\x6c\x54\xa7\x0e\x4d\x1d" - "\x01\xbf\x08\xac\xcf\x7f\x2c\xe3" - "\x14\x89\x5e\x70\x5a\x99\x92\xcd" - "\x01\x84\xc8\xd2\xab\xe5\x4f\x58" - "\xe7\x0f\x2f\x0e\xff\x68\xea\xfd" - "\x15\xb3\x17\xe6\xb0\xe7\x85\xd8" - "\x23\x2e\x05\xc7\xc9\xc4\x46\x1f" - "\xe1\x9e\x49\x20\x23\x24\x4d\x7e" - "\x29\x65\xff\xf4\xb6\xfd\x1a\x85" - "\xc4\x16\xec\xfc\xea\x7b\xd6\x2c" - "\x43\xf8\xb7\xbf\x79\xc0\x85\xcd" - "\xef\xe1\x98\xd3\xa5\xf7\x90\x8c" - "\xe9\x7f\x80\x6b\xd2\xac\x4c\x30" - "\xa7\xc6\x61\x6c\xd2\xf9\x2c\xff" - "\x30\xbc\x22\x81\x7d\x93\x12\xe4" - "\x0a\xcd\xaf\xdd\xe8\xab\x0a\x1e" - "\x13\xa4\x27\xc3\x5f\xf7\x4b\xbb" - "\x37\x09\x4b\x91\x6f\x92\x4f\xaf" - "\x52\xee\xdf\xef\x09\x6f\xf7\x5c" - "\x6e\x12\x17\x72\x63\x57\xc7\xba" - "\x3b\x6b\x38\x32\x73\x1b\x9c\x80" - "\xc1\x7a\xc6\xcf\xcd\x35\xc0\x6b" - "\x31\x1a\x6b\xe9\xd8\x2c\x29\x3f" - "\x96\xfb\xb6\xcd\x13\x91\x3b\xc2" - "\xd2\xa3\x31\x8d\xa4\xcd\x57\xcd" - "\x13\x3d\x64\xfd\x06\xce\xe6\xdc" - "\x0c\x24\x43\x31\x40\x57\xf1\x72" - "\x17\xe3\x3a\x63\x6d\x35\xcf\x5d" - "\x97\x40\x59\xdd\xf7\x3c\x02\xf7" - "\x1c\x7e\x05\xbb\xa9\x0d\x01\xb1" - "\x8e\xc0\x30\xa9\x53\x24\xc9\x89" - "\x84\x6d\xaa\xd0\xcd\x91\xc2\x4d" - "\x91\xb0\x89\xe2\xbf\x83\x44\xaa" - "\x28\x72\x23\xa0\xc2\xad\xad\x1c" - "\xfc\x3f\x09\x7a\x0b\xdc\xc5\x1b" - "\x87\x13\xc6\x5b\x59\x8d\xf2\xc8" - "\xaf\xdf\x11\x95", - .rlen = 4100, - .np = 2, - .tap = { 4064, 36 }, - }, -}; - -/* - * CTS (Cipher Text Stealing) mode tests - */ -#define CTS_MODE_ENC_TEST_VECTORS 6 -#define CTS_MODE_DEC_TEST_VECTORS 6 -static struct cipher_testvec cts_mode_enc_tv_template[] = { - { /* from rfc3962 */ - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .ilen = 17, - .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20", - .rlen = 17, - .result = "\xc6\x35\x35\x68\xf2\xbf\x8c\xb4" - "\xd8\xa5\x80\x36\x2d\xa7\xff\x7f" - "\x97", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .ilen = 31, - .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20", - .rlen = 31, - .result = "\xfc\x00\x78\x3e\x0e\xfd\xb2\xc1" - "\xd4\x45\xd4\xc8\xef\xf7\xed\x22" - "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .ilen = 32, - .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43", - .rlen = 32, - .result = "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" - "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .ilen = 47, - .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43" - "\x68\x69\x63\x6b\x65\x6e\x2c\x20" - "\x70\x6c\x65\x61\x73\x65\x2c", - .rlen = 47, - .result = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" - "\xb3\xff\xfd\x94\x0c\x16\xa1\x8c" - "\x1b\x55\x49\xd2\xf8\x38\x02\x9e" - "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .ilen = 48, - .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43" - "\x68\x69\x63\x6b\x65\x6e\x2c\x20" - "\x70\x6c\x65\x61\x73\x65\x2c\x20", - .rlen = 48, - .result = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" - "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" - "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8" - "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .ilen = 64, - .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43" - "\x68\x69\x63\x6b\x65\x6e\x2c\x20" - "\x70\x6c\x65\x61\x73\x65\x2c\x20" - "\x61\x6e\x64\x20\x77\x6f\x6e\x74" - "\x6f\x6e\x20\x73\x6f\x75\x70\x2e", - .rlen = 64, - .result = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" - "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" - "\x48\x07\xef\xe8\x36\xee\x89\xa5" - "\x26\x73\x0d\xbc\x2f\x7b\xc8\x40" - "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" - "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8", - } -}; - -static struct cipher_testvec cts_mode_dec_tv_template[] = { - { /* from rfc3962 */ - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .rlen = 17, - .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20", - .ilen = 17, - .input = "\xc6\x35\x35\x68\xf2\xbf\x8c\xb4" - "\xd8\xa5\x80\x36\x2d\xa7\xff\x7f" - "\x97", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .rlen = 31, - .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20", - .ilen = 31, - .input = "\xfc\x00\x78\x3e\x0e\xfd\xb2\xc1" - "\xd4\x45\xd4\xc8\xef\xf7\xed\x22" - "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .rlen = 32, - .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43", - .ilen = 32, - .input = "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" - "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .rlen = 47, - .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43" - "\x68\x69\x63\x6b\x65\x6e\x2c\x20" - "\x70\x6c\x65\x61\x73\x65\x2c", - .ilen = 47, - .input = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" - "\xb3\xff\xfd\x94\x0c\x16\xa1\x8c" - "\x1b\x55\x49\xd2\xf8\x38\x02\x9e" - "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .rlen = 48, - .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43" - "\x68\x69\x63\x6b\x65\x6e\x2c\x20" - "\x70\x6c\x65\x61\x73\x65\x2c\x20", - .ilen = 48, - .input = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" - "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" - "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8" - "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8", - }, { - .klen = 16, - .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" - "\x74\x65\x72\x69\x79\x61\x6b\x69", - .rlen = 64, - .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" - "\x6c\x69\x6b\x65\x20\x74\x68\x65" - "\x20\x47\x65\x6e\x65\x72\x61\x6c" - "\x20\x47\x61\x75\x27\x73\x20\x43" - "\x68\x69\x63\x6b\x65\x6e\x2c\x20" - "\x70\x6c\x65\x61\x73\x65\x2c\x20" - "\x61\x6e\x64\x20\x77\x6f\x6e\x74" - "\x6f\x6e\x20\x73\x6f\x75\x70\x2e", - .ilen = 64, - .input = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" - "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" - "\x39\x31\x25\x23\xa7\x86\x62\xd5" - "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" - "\x48\x07\xef\xe8\x36\xee\x89\xa5" - "\x26\x73\x0d\xbc\x2f\x7b\xc8\x40" - "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" - "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8", - } -}; - -/* - * Compression stuff. - */ -#define COMP_BUF_SIZE 512 - -struct comp_testvec { - int inlen, outlen; - char input[COMP_BUF_SIZE]; - char output[COMP_BUF_SIZE]; -}; - -/* - * Deflate test vectors (null-terminated strings). - * Params: winbits=11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL. - */ -#define DEFLATE_COMP_TEST_VECTORS 2 -#define DEFLATE_DECOMP_TEST_VECTORS 2 - -static struct comp_testvec deflate_comp_tv_template[] = { - { - .inlen = 70, - .outlen = 38, - .input = "Join us now and share the software " - "Join us now and share the software ", - .output = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56" - "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51" - "\x28\xce\x48\x2c\x4a\x55\x28\xc9" - "\x48\x55\x28\xce\x4f\x2b\x29\x07" - "\x71\xbc\x08\x2b\x01\x00", - }, { - .inlen = 191, - .outlen = 122, - .input = "This document describes a compression method based on the DEFLATE" - "compression algorithm. This document defines the application of " - "the DEFLATE algorithm to the IP Payload Compression Protocol.", - .output = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04" - "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09" - "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8" - "\x24\xdb\x67\xd9\x47\xc1\xef\x49" - "\x68\x12\x51\xae\x76\x67\xd6\x27" - "\x19\x88\x1a\xde\x85\xab\x21\xf2" - "\x08\x5d\x16\x1e\x20\x04\x2d\xad" - "\xf3\x18\xa2\x15\x85\x2d\x69\xc4" - "\x42\x83\x23\xb6\x6c\x89\x71\x9b" - "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f" - "\xed\x62\xa9\x4c\x80\xff\x13\xaf" - "\x52\x37\xed\x0e\x52\x6b\x59\x02" - "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98" - "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a" - "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79" - "\xfa\x02", - }, -}; - -static struct comp_testvec deflate_decomp_tv_template[] = { - { - .inlen = 122, - .outlen = 191, - .input = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04" - "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09" - "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8" - "\x24\xdb\x67\xd9\x47\xc1\xef\x49" - "\x68\x12\x51\xae\x76\x67\xd6\x27" - "\x19\x88\x1a\xde\x85\xab\x21\xf2" - "\x08\x5d\x16\x1e\x20\x04\x2d\xad" - "\xf3\x18\xa2\x15\x85\x2d\x69\xc4" - "\x42\x83\x23\xb6\x6c\x89\x71\x9b" - "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f" - "\xed\x62\xa9\x4c\x80\xff\x13\xaf" - "\x52\x37\xed\x0e\x52\x6b\x59\x02" - "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98" - "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a" - "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79" - "\xfa\x02", - .output = "This document describes a compression method based on the DEFLATE" - "compression algorithm. This document defines the application of " - "the DEFLATE algorithm to the IP Payload Compression Protocol.", - }, { - .inlen = 38, - .outlen = 70, - .input = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56" - "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51" - "\x28\xce\x48\x2c\x4a\x55\x28\xc9" - "\x48\x55\x28\xce\x4f\x2b\x29\x07" - "\x71\xbc\x08\x2b\x01\x00", - .output = "Join us now and share the software " - "Join us now and share the software ", - }, -}; - -/* - * LZO test vectors (null-terminated strings). - */ -#define LZO_COMP_TEST_VECTORS 2 -#define LZO_DECOMP_TEST_VECTORS 2 - -static struct comp_testvec lzo_comp_tv_template[] = { - { - .inlen = 70, - .outlen = 46, - .input = "Join us now and share the software " - "Join us now and share the software ", - .output = "\x00\x0d\x4a\x6f\x69\x6e\x20\x75" - "\x73\x20\x6e\x6f\x77\x20\x61\x6e" - "\x64\x20\x73\x68\x61\x72\x65\x20" - "\x74\x68\x65\x20\x73\x6f\x66\x74" - "\x77\x70\x01\x01\x4a\x6f\x69\x6e" - "\x3d\x88\x00\x11\x00\x00", - }, { - .inlen = 159, - .outlen = 133, - .input = "This document describes a compression method based on the LZO " - "compression algorithm. This document defines the application of " - "the LZO algorithm used in UBIFS.", - .output = "\x00\x2b\x54\x68\x69\x73\x20\x64" - "\x6f\x63\x75\x6d\x65\x6e\x74\x20" - "\x64\x65\x73\x63\x72\x69\x62\x65" - "\x73\x20\x61\x20\x63\x6f\x6d\x70" - "\x72\x65\x73\x73\x69\x6f\x6e\x20" - "\x6d\x65\x74\x68\x6f\x64\x20\x62" - "\x61\x73\x65\x64\x20\x6f\x6e\x20" - "\x74\x68\x65\x20\x4c\x5a\x4f\x2b" - "\x8c\x00\x0d\x61\x6c\x67\x6f\x72" - "\x69\x74\x68\x6d\x2e\x20\x20\x54" - "\x68\x69\x73\x2a\x54\x01\x02\x66" - "\x69\x6e\x65\x73\x94\x06\x05\x61" - "\x70\x70\x6c\x69\x63\x61\x74\x76" - "\x0a\x6f\x66\x88\x02\x60\x09\x27" - "\xf0\x00\x0c\x20\x75\x73\x65\x64" - "\x20\x69\x6e\x20\x55\x42\x49\x46" - "\x53\x2e\x11\x00\x00", - }, -}; - -static struct comp_testvec lzo_decomp_tv_template[] = { - { - .inlen = 133, - .outlen = 159, - .input = "\x00\x2b\x54\x68\x69\x73\x20\x64" - "\x6f\x63\x75\x6d\x65\x6e\x74\x20" - "\x64\x65\x73\x63\x72\x69\x62\x65" - "\x73\x20\x61\x20\x63\x6f\x6d\x70" - "\x72\x65\x73\x73\x69\x6f\x6e\x20" - "\x6d\x65\x74\x68\x6f\x64\x20\x62" - "\x61\x73\x65\x64\x20\x6f\x6e\x20" - "\x74\x68\x65\x20\x4c\x5a\x4f\x2b" - "\x8c\x00\x0d\x61\x6c\x67\x6f\x72" - "\x69\x74\x68\x6d\x2e\x20\x20\x54" - "\x68\x69\x73\x2a\x54\x01\x02\x66" - "\x69\x6e\x65\x73\x94\x06\x05\x61" - "\x70\x70\x6c\x69\x63\x61\x74\x76" - "\x0a\x6f\x66\x88\x02\x60\x09\x27" - "\xf0\x00\x0c\x20\x75\x73\x65\x64" - "\x20\x69\x6e\x20\x55\x42\x49\x46" - "\x53\x2e\x11\x00\x00", - .output = "This document describes a compression method based on the LZO " - "compression algorithm. This document defines the application of " - "the LZO algorithm used in UBIFS.", - }, { - .inlen = 46, - .outlen = 70, - .input = "\x00\x0d\x4a\x6f\x69\x6e\x20\x75" - "\x73\x20\x6e\x6f\x77\x20\x61\x6e" - "\x64\x20\x73\x68\x61\x72\x65\x20" - "\x74\x68\x65\x20\x73\x6f\x66\x74" - "\x77\x70\x01\x01\x4a\x6f\x69\x6e" - "\x3d\x88\x00\x11\x00\x00", - .output = "Join us now and share the software " - "Join us now and share the software ", - }, -}; - -/* - * Michael MIC test vectors from IEEE 802.11i - */ -#define MICHAEL_MIC_TEST_VECTORS 6 - -static struct hash_testvec michael_mic_tv_template[] = { - { - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ksize = 8, - .plaintext = zeroed_string, - .psize = 0, - .digest = "\x82\x92\x5c\x1c\xa1\xd1\x30\xb8", - }, - { - .key = "\x82\x92\x5c\x1c\xa1\xd1\x30\xb8", - .ksize = 8, - .plaintext = "M", - .psize = 1, - .digest = "\x43\x47\x21\xca\x40\x63\x9b\x3f", - }, - { - .key = "\x43\x47\x21\xca\x40\x63\x9b\x3f", - .ksize = 8, - .plaintext = "Mi", - .psize = 2, - .digest = "\xe8\xf9\xbe\xca\xe9\x7e\x5d\x29", - }, - { - .key = "\xe8\xf9\xbe\xca\xe9\x7e\x5d\x29", - .ksize = 8, - .plaintext = "Mic", - .psize = 3, - .digest = "\x90\x03\x8f\xc6\xcf\x13\xc1\xdb", - }, - { - .key = "\x90\x03\x8f\xc6\xcf\x13\xc1\xdb", - .ksize = 8, - .plaintext = "Mich", - .psize = 4, - .digest = "\xd5\x5e\x10\x05\x10\x12\x89\x86", - }, - { - .key = "\xd5\x5e\x10\x05\x10\x12\x89\x86", - .ksize = 8, - .plaintext = "Michael", - .psize = 7, - .digest = "\x0a\x94\x2b\x12\x4e\xca\xa5\x46", - } -}; - -/* - * CRC32C test vectors - */ -#define CRC32C_TEST_VECTORS 14 - -static struct hash_testvec crc32c_tv_template[] = { - { - .psize = 0, - .digest = "\x00\x00\x00\x00", - }, - { - .key = "\x87\xa9\xcb\xed", - .ksize = 4, - .psize = 0, - .digest = "\x78\x56\x34\x12", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" - "\x21\x22\x23\x24\x25\x26\x27\x28", - .psize = 40, - .digest = "\x7f\x15\x2c\x0e", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" - "\x31\x32\x33\x34\x35\x36\x37\x38" - "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" - "\x41\x42\x43\x44\x45\x46\x47\x48" - "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50", - .psize = 40, - .digest = "\xf6\xeb\x80\xe9", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\x51\x52\x53\x54\x55\x56\x57\x58" - "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" - "\x61\x62\x63\x64\x65\x66\x67\x68" - "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" - "\x71\x72\x73\x74\x75\x76\x77\x78", - .psize = 40, - .digest = "\xed\xbd\x74\xde", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" - "\x81\x82\x83\x84\x85\x86\x87\x88" - "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" - "\x91\x92\x93\x94\x95\x96\x97\x98" - "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0", - .psize = 40, - .digest = "\x62\xc8\x79\xd5", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" - "\xa9\xaa\xab\xac\xad\xae\xaf\xb0" - "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8" - "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" - "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8", - .psize = 40, - .digest = "\xd0\x9a\x97\xba", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" - "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" - "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" - "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" - "\xe9\xea\xeb\xec\xed\xee\xef\xf0", - .psize = 40, - .digest = "\x13\xd9\x29\x2b", - }, - { - .key = "\x80\xea\xd3\xf1", - .ksize = 4, - .plaintext = "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" - "\x31\x32\x33\x34\x35\x36\x37\x38" - "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" - "\x41\x42\x43\x44\x45\x46\x47\x48" - "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50", - .psize = 40, - .digest = "\x0c\xb5\xe2\xa2", - }, - { - .key = "\xf3\x4a\x1d\x5d", - .ksize = 4, - .plaintext = "\x51\x52\x53\x54\x55\x56\x57\x58" - "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" - "\x61\x62\x63\x64\x65\x66\x67\x68" - "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" - "\x71\x72\x73\x74\x75\x76\x77\x78", - .psize = 40, - .digest = "\xd1\x7f\xfb\xa6", - }, - { - .key = "\x2e\x80\x04\x59", - .ksize = 4, - .plaintext = "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" - "\x81\x82\x83\x84\x85\x86\x87\x88" - "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" - "\x91\x92\x93\x94\x95\x96\x97\x98" - "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0", - .psize = 40, - .digest = "\x59\x33\xe6\x7a", - }, - { - .key = "\xa6\xcc\x19\x85", - .ksize = 4, - .plaintext = "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" - "\xa9\xaa\xab\xac\xad\xae\xaf\xb0" - "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8" - "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" - "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8", - .psize = 40, - .digest = "\xbe\x03\x01\xd2", - }, - { - .key = "\x41\xfc\xfe\x2d", - .ksize = 4, - .plaintext = "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" - "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" - "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" - "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" - "\xe9\xea\xeb\xec\xed\xee\xef\xf0", - .psize = 40, - .digest = "\x75\xd3\xc5\x24", - }, - { - .key = "\xff\xff\xff\xff", - .ksize = 4, - .plaintext = "\x01\x02\x03\x04\x05\x06\x07\x08" - "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" - "\x21\x22\x23\x24\x25\x26\x27\x28" - "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" - "\x31\x32\x33\x34\x35\x36\x37\x38" - "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" - "\x41\x42\x43\x44\x45\x46\x47\x48" - "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" - "\x51\x52\x53\x54\x55\x56\x57\x58" - "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" - "\x61\x62\x63\x64\x65\x66\x67\x68" - "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" - "\x71\x72\x73\x74\x75\x76\x77\x78" - "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" - "\x81\x82\x83\x84\x85\x86\x87\x88" - "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" - "\x91\x92\x93\x94\x95\x96\x97\x98" - "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0" - "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" - "\xa9\xaa\xab\xac\xad\xae\xaf\xb0" - "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8" - "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" - "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8" - "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" - "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" - "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" - "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" - "\xe9\xea\xeb\xec\xed\xee\xef\xf0", - .psize = 240, - .digest = "\x75\xd3\xc5\x24", - .np = 2, - .tap = { 31, 209 } - }, -}; - /* * Cipher speed tests */ diff --git a/crypto/testmgr.c b/crypto/testmgr.c new file mode 100644 index 000000000000..e8666b3ead67 --- /dev/null +++ b/crypto/testmgr.c @@ -0,0 +1,1746 @@ +/* + * Algorithm testing framework and tests. + * + * Copyright (c) 2002 James Morris + * Copyright (c) 2002 Jean-Francois Dive + * Copyright (c) 2007 Nokia Siemens Networks + * Copyright (c) 2008 Herbert Xu + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "testmgr.h" + +/* + * Need slab memory for testing (size in number of pages). + */ +#define XBUFSIZE 8 + +/* + * Indexes into the xbuf to simulate cross-page access. + */ +#define IDX1 32 +#define IDX2 32400 +#define IDX3 1 +#define IDX4 8193 +#define IDX5 22222 +#define IDX6 17101 +#define IDX7 27333 +#define IDX8 3000 + +/* +* Used by test_cipher() +*/ +#define ENCRYPT 1 +#define DECRYPT 0 + +struct tcrypt_result { + struct completion completion; + int err; +}; + +struct aead_test_suite { + struct { + struct aead_testvec *vecs; + unsigned int count; + } enc, dec; +}; + +struct cipher_test_suite { + struct { + struct cipher_testvec *vecs; + unsigned int count; + } enc, dec; +}; + +struct comp_test_suite { + struct { + struct comp_testvec *vecs; + unsigned int count; + } comp, decomp; +}; + +struct hash_test_suite { + struct hash_testvec *vecs; + unsigned int count; +}; + +struct alg_test_desc { + const char *alg; + int (*test)(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask); + + union { + struct aead_test_suite aead; + struct cipher_test_suite cipher; + struct comp_test_suite comp; + struct hash_test_suite hash; + } suite; +}; + +static unsigned int IDX[8] = { IDX1, IDX2, IDX3, IDX4, IDX5, IDX6, IDX7, IDX8 }; + +static char *xbuf[XBUFSIZE]; +static char *axbuf[XBUFSIZE]; + +static void hexdump(unsigned char *buf, unsigned int len) +{ + print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, + 16, 1, + buf, len, false); +} + +static void tcrypt_complete(struct crypto_async_request *req, int err) +{ + struct tcrypt_result *res = req->data; + + if (err == -EINPROGRESS) + return; + + res->err = err; + complete(&res->completion); +} + +static int test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, + unsigned int tcount) +{ + const char *algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm)); + unsigned int i, j, k, temp; + struct scatterlist sg[8]; + char result[64]; + struct ahash_request *req; + struct tcrypt_result tresult; + int ret; + void *hash_buff; + + init_completion(&tresult.completion); + + req = ahash_request_alloc(tfm, GFP_KERNEL); + if (!req) { + printk(KERN_ERR "alg: hash: Failed to allocate request for " + "%s\n", algo); + ret = -ENOMEM; + goto out_noreq; + } + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + tcrypt_complete, &tresult); + + for (i = 0; i < tcount; i++) { + memset(result, 0, 64); + + hash_buff = xbuf[0]; + + memcpy(hash_buff, template[i].plaintext, template[i].psize); + sg_init_one(&sg[0], hash_buff, template[i].psize); + + if (template[i].ksize) { + crypto_ahash_clear_flags(tfm, ~0); + ret = crypto_ahash_setkey(tfm, template[i].key, + template[i].ksize); + if (ret) { + printk(KERN_ERR "alg: hash: setkey failed on " + "test %d for %s: ret=%d\n", i + 1, algo, + -ret); + goto out; + } + } + + ahash_request_set_crypt(req, sg, result, template[i].psize); + ret = crypto_ahash_digest(req); + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &tresult.completion); + if (!ret && !(ret = tresult.err)) { + INIT_COMPLETION(tresult.completion); + break; + } + /* fall through */ + default: + printk(KERN_ERR "alg: hash: digest failed on test %d " + "for %s: ret=%d\n", i + 1, algo, -ret); + goto out; + } + + if (memcmp(result, template[i].digest, + crypto_ahash_digestsize(tfm))) { + printk(KERN_ERR "alg: hash: Test %d failed for %s\n", + i + 1, algo); + hexdump(result, crypto_ahash_digestsize(tfm)); + ret = -EINVAL; + goto out; + } + } + + j = 0; + for (i = 0; i < tcount; i++) { + if (template[i].np) { + j++; + memset(result, 0, 64); + + temp = 0; + sg_init_table(sg, template[i].np); + for (k = 0; k < template[i].np; k++) { + sg_set_buf(&sg[k], + memcpy(xbuf[IDX[k] >> PAGE_SHIFT] + + offset_in_page(IDX[k]), + template[i].plaintext + temp, + template[i].tap[k]), + template[i].tap[k]); + temp += template[i].tap[k]; + } + + if (template[i].ksize) { + crypto_ahash_clear_flags(tfm, ~0); + ret = crypto_ahash_setkey(tfm, template[i].key, + template[i].ksize); + + if (ret) { + printk(KERN_ERR "alg: hash: setkey " + "failed on chunking test %d " + "for %s: ret=%d\n", j, algo, + -ret); + goto out; + } + } + + ahash_request_set_crypt(req, sg, result, + template[i].psize); + ret = crypto_ahash_digest(req); + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &tresult.completion); + if (!ret && !(ret = tresult.err)) { + INIT_COMPLETION(tresult.completion); + break; + } + /* fall through */ + default: + printk(KERN_ERR "alg: hash: digest failed " + "on chunking test %d for %s: " + "ret=%d\n", j, algo, -ret); + goto out; + } + + if (memcmp(result, template[i].digest, + crypto_ahash_digestsize(tfm))) { + printk(KERN_ERR "alg: hash: Chunking test %d " + "failed for %s\n", j, algo); + hexdump(result, crypto_ahash_digestsize(tfm)); + ret = -EINVAL; + goto out; + } + } + } + + ret = 0; + +out: + ahash_request_free(req); +out_noreq: + return ret; +} + +static int test_aead(struct crypto_aead *tfm, int enc, + struct aead_testvec *template, unsigned int tcount) +{ + const char *algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)); + unsigned int i, j, k, n, temp; + int ret = 0; + char *q; + char *key; + struct aead_request *req; + struct scatterlist sg[8]; + struct scatterlist asg[8]; + const char *e; + struct tcrypt_result result; + unsigned int authsize; + void *input; + void *assoc; + char iv[MAX_IVLEN]; + + if (enc == ENCRYPT) + e = "encryption"; + else + e = "decryption"; + + init_completion(&result.completion); + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + printk(KERN_ERR "alg: aead: Failed to allocate request for " + "%s\n", algo); + ret = -ENOMEM; + goto out; + } + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + tcrypt_complete, &result); + + for (i = 0, j = 0; i < tcount; i++) { + if (!template[i].np) { + j++; + + /* some tepmplates have no input data but they will + * touch input + */ + input = xbuf[0]; + assoc = axbuf[0]; + + memcpy(input, template[i].input, template[i].ilen); + memcpy(assoc, template[i].assoc, template[i].alen); + if (template[i].iv) + memcpy(iv, template[i].iv, MAX_IVLEN); + else + memset(iv, 0, MAX_IVLEN); + + crypto_aead_clear_flags(tfm, ~0); + if (template[i].wk) + crypto_aead_set_flags( + tfm, CRYPTO_TFM_REQ_WEAK_KEY); + + key = template[i].key; + + ret = crypto_aead_setkey(tfm, key, + template[i].klen); + if (!ret == template[i].fail) { + printk(KERN_ERR "alg: aead: setkey failed on " + "test %d for %s: flags=%x\n", j, algo, + crypto_aead_get_flags(tfm)); + goto out; + } else if (ret) + continue; + + authsize = abs(template[i].rlen - template[i].ilen); + ret = crypto_aead_setauthsize(tfm, authsize); + if (ret) { + printk(KERN_ERR "alg: aead: Failed to set " + "authsize to %u on test %d for %s\n", + authsize, j, algo); + goto out; + } + + sg_init_one(&sg[0], input, + template[i].ilen + (enc ? authsize : 0)); + + sg_init_one(&asg[0], assoc, template[i].alen); + + aead_request_set_crypt(req, sg, sg, + template[i].ilen, iv); + + aead_request_set_assoc(req, asg, template[i].alen); + + ret = enc ? + crypto_aead_encrypt(req) : + crypto_aead_decrypt(req); + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !(ret = result.err)) { + INIT_COMPLETION(result.completion); + break; + } + /* fall through */ + default: + printk(KERN_ERR "alg: aead: %s failed on test " + "%d for %s: ret=%d\n", e, j, algo, -ret); + goto out; + } + + q = input; + if (memcmp(q, template[i].result, template[i].rlen)) { + printk(KERN_ERR "alg: aead: Test %d failed on " + "%s for %s\n", j, e, algo); + hexdump(q, template[i].rlen); + ret = -EINVAL; + goto out; + } + } + } + + for (i = 0, j = 0; i < tcount; i++) { + if (template[i].np) { + j++; + + if (template[i].iv) + memcpy(iv, template[i].iv, MAX_IVLEN); + else + memset(iv, 0, MAX_IVLEN); + + crypto_aead_clear_flags(tfm, ~0); + if (template[i].wk) + crypto_aead_set_flags( + tfm, CRYPTO_TFM_REQ_WEAK_KEY); + key = template[i].key; + + ret = crypto_aead_setkey(tfm, key, template[i].klen); + if (!ret == template[i].fail) { + printk(KERN_ERR "alg: aead: setkey failed on " + "chunk test %d for %s: flags=%x\n", j, + algo, crypto_aead_get_flags(tfm)); + goto out; + } else if (ret) + continue; + + authsize = abs(template[i].rlen - template[i].ilen); + + ret = -EINVAL; + sg_init_table(sg, template[i].np); + for (k = 0, temp = 0; k < template[i].np; k++) { + if (WARN_ON(offset_in_page(IDX[k]) + + template[i].tap[k] > PAGE_SIZE)) + goto out; + + q = xbuf[IDX[k] >> PAGE_SHIFT] + + offset_in_page(IDX[k]); + + memcpy(q, template[i].input + temp, + template[i].tap[k]); + + n = template[i].tap[k]; + if (k == template[i].np - 1 && enc) + n += authsize; + if (offset_in_page(q) + n < PAGE_SIZE) + q[n] = 0; + + sg_set_buf(&sg[k], q, template[i].tap[k]); + temp += template[i].tap[k]; + } + + ret = crypto_aead_setauthsize(tfm, authsize); + if (ret) { + printk(KERN_ERR "alg: aead: Failed to set " + "authsize to %u on chunk test %d for " + "%s\n", authsize, j, algo); + goto out; + } + + if (enc) { + if (WARN_ON(sg[k - 1].offset + + sg[k - 1].length + authsize > + PAGE_SIZE)) { + ret = -EINVAL; + goto out; + } + + sg[k - 1].length += authsize; + } + + sg_init_table(asg, template[i].anp); + for (k = 0, temp = 0; k < template[i].anp; k++) { + sg_set_buf(&asg[k], + memcpy(axbuf[IDX[k] >> PAGE_SHIFT] + + offset_in_page(IDX[k]), + template[i].assoc + temp, + template[i].atap[k]), + template[i].atap[k]); + temp += template[i].atap[k]; + } + + aead_request_set_crypt(req, sg, sg, + template[i].ilen, + iv); + + aead_request_set_assoc(req, asg, template[i].alen); + + ret = enc ? + crypto_aead_encrypt(req) : + crypto_aead_decrypt(req); + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !(ret = result.err)) { + INIT_COMPLETION(result.completion); + break; + } + /* fall through */ + default: + printk(KERN_ERR "alg: aead: %s failed on " + "chunk test %d for %s: ret=%d\n", e, j, + algo, -ret); + goto out; + } + + ret = -EINVAL; + for (k = 0, temp = 0; k < template[i].np; k++) { + q = xbuf[IDX[k] >> PAGE_SHIFT] + + offset_in_page(IDX[k]); + + n = template[i].tap[k]; + if (k == template[i].np - 1) + n += enc ? authsize : -authsize; + + if (memcmp(q, template[i].result + temp, n)) { + printk(KERN_ERR "alg: aead: Chunk " + "test %d failed on %s at page " + "%u for %s\n", j, e, k, algo); + hexdump(q, n); + goto out; + } + + q += n; + if (k == template[i].np - 1 && !enc) { + if (memcmp(q, template[i].input + + temp + n, authsize)) + n = authsize; + else + n = 0; + } else { + for (n = 0; offset_in_page(q + n) && + q[n]; n++) + ; + } + if (n) { + printk(KERN_ERR "alg: aead: Result " + "buffer corruption in chunk " + "test %d on %s at page %u for " + "%s: %u bytes:\n", j, e, k, + algo, n); + hexdump(q, n); + goto out; + } + + temp += template[i].tap[k]; + } + } + } + + ret = 0; + +out: + aead_request_free(req); + return ret; +} + +static int test_cipher(struct crypto_ablkcipher *tfm, int enc, + struct cipher_testvec *template, unsigned int tcount) +{ + const char *algo = + crypto_tfm_alg_driver_name(crypto_ablkcipher_tfm(tfm)); + unsigned int i, j, k, n, temp; + int ret; + char *q; + struct ablkcipher_request *req; + struct scatterlist sg[8]; + const char *e; + struct tcrypt_result result; + void *data; + char iv[MAX_IVLEN]; + + if (enc == ENCRYPT) + e = "encryption"; + else + e = "decryption"; + + init_completion(&result.completion); + + req = ablkcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + printk(KERN_ERR "alg: cipher: Failed to allocate request for " + "%s\n", algo); + ret = -ENOMEM; + goto out; + } + + ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + tcrypt_complete, &result); + + j = 0; + for (i = 0; i < tcount; i++) { + if (template[i].iv) + memcpy(iv, template[i].iv, MAX_IVLEN); + else + memset(iv, 0, MAX_IVLEN); + + if (!(template[i].np)) { + j++; + + data = xbuf[0]; + memcpy(data, template[i].input, template[i].ilen); + + crypto_ablkcipher_clear_flags(tfm, ~0); + if (template[i].wk) + crypto_ablkcipher_set_flags( + tfm, CRYPTO_TFM_REQ_WEAK_KEY); + + ret = crypto_ablkcipher_setkey(tfm, template[i].key, + template[i].klen); + if (!ret == template[i].fail) { + printk(KERN_ERR "alg: cipher: setkey failed " + "on test %d for %s: flags=%x\n", j, + algo, crypto_ablkcipher_get_flags(tfm)); + goto out; + } else if (ret) + continue; + + sg_init_one(&sg[0], data, template[i].ilen); + + ablkcipher_request_set_crypt(req, sg, sg, + template[i].ilen, iv); + ret = enc ? + crypto_ablkcipher_encrypt(req) : + crypto_ablkcipher_decrypt(req); + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !((ret = result.err))) { + INIT_COMPLETION(result.completion); + break; + } + /* fall through */ + default: + printk(KERN_ERR "alg: cipher: %s failed on " + "test %d for %s: ret=%d\n", e, j, algo, + -ret); + goto out; + } + + q = data; + if (memcmp(q, template[i].result, template[i].rlen)) { + printk(KERN_ERR "alg: cipher: Test %d failed " + "on %s for %s\n", j, e, algo); + hexdump(q, template[i].rlen); + ret = -EINVAL; + goto out; + } + } + } + + j = 0; + for (i = 0; i < tcount; i++) { + + if (template[i].iv) + memcpy(iv, template[i].iv, MAX_IVLEN); + else + memset(iv, 0, MAX_IVLEN); + + if (template[i].np) { + j++; + + crypto_ablkcipher_clear_flags(tfm, ~0); + if (template[i].wk) + crypto_ablkcipher_set_flags( + tfm, CRYPTO_TFM_REQ_WEAK_KEY); + + ret = crypto_ablkcipher_setkey(tfm, template[i].key, + template[i].klen); + if (!ret == template[i].fail) { + printk(KERN_ERR "alg: cipher: setkey failed " + "on chunk test %d for %s: flags=%x\n", + j, algo, + crypto_ablkcipher_get_flags(tfm)); + goto out; + } else if (ret) + continue; + + temp = 0; + ret = -EINVAL; + sg_init_table(sg, template[i].np); + for (k = 0; k < template[i].np; k++) { + if (WARN_ON(offset_in_page(IDX[k]) + + template[i].tap[k] > PAGE_SIZE)) + goto out; + + q = xbuf[IDX[k] >> PAGE_SHIFT] + + offset_in_page(IDX[k]); + + memcpy(q, template[i].input + temp, + template[i].tap[k]); + + if (offset_in_page(q) + template[i].tap[k] < + PAGE_SIZE) + q[template[i].tap[k]] = 0; + + sg_set_buf(&sg[k], q, template[i].tap[k]); + + temp += template[i].tap[k]; + } + + ablkcipher_request_set_crypt(req, sg, sg, + template[i].ilen, iv); + + ret = enc ? + crypto_ablkcipher_encrypt(req) : + crypto_ablkcipher_decrypt(req); + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !((ret = result.err))) { + INIT_COMPLETION(result.completion); + break; + } + /* fall through */ + default: + printk(KERN_ERR "alg: cipher: %s failed on " + "chunk test %d for %s: ret=%d\n", e, j, + algo, -ret); + goto out; + } + + temp = 0; + ret = -EINVAL; + for (k = 0; k < template[i].np; k++) { + q = xbuf[IDX[k] >> PAGE_SHIFT] + + offset_in_page(IDX[k]); + + if (memcmp(q, template[i].result + temp, + template[i].tap[k])) { + printk(KERN_ERR "alg: cipher: Chunk " + "test %d failed on %s at page " + "%u for %s\n", j, e, k, algo); + hexdump(q, template[i].tap[k]); + goto out; + } + + q += template[i].tap[k]; + for (n = 0; offset_in_page(q + n) && q[n]; n++) + ; + if (n) { + printk(KERN_ERR "alg: cipher: " + "Result buffer corruption in " + "chunk test %d on %s at page " + "%u for %s: %u bytes:\n", j, e, + k, algo, n); + hexdump(q, n); + goto out; + } + temp += template[i].tap[k]; + } + } + } + + ret = 0; + +out: + ablkcipher_request_free(req); + return ret; +} + +static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate, + struct comp_testvec *dtemplate, int ctcount, int dtcount) +{ + const char *algo = crypto_tfm_alg_driver_name(crypto_comp_tfm(tfm)); + unsigned int i; + char result[COMP_BUF_SIZE]; + int ret; + + for (i = 0; i < ctcount; i++) { + int ilen, dlen = COMP_BUF_SIZE; + + memset(result, 0, sizeof (result)); + + ilen = ctemplate[i].inlen; + ret = crypto_comp_compress(tfm, ctemplate[i].input, + ilen, result, &dlen); + if (ret) { + printk(KERN_ERR "alg: comp: compression failed " + "on test %d for %s: ret=%d\n", i + 1, algo, + -ret); + goto out; + } + + if (memcmp(result, ctemplate[i].output, dlen)) { + printk(KERN_ERR "alg: comp: Compression test %d " + "failed for %s\n", i + 1, algo); + hexdump(result, dlen); + ret = -EINVAL; + goto out; + } + } + + for (i = 0; i < dtcount; i++) { + int ilen, ret, dlen = COMP_BUF_SIZE; + + memset(result, 0, sizeof (result)); + + ilen = dtemplate[i].inlen; + ret = crypto_comp_decompress(tfm, dtemplate[i].input, + ilen, result, &dlen); + if (ret) { + printk(KERN_ERR "alg: comp: decompression failed " + "on test %d for %s: ret=%d\n", i + 1, algo, + -ret); + goto out; + } + + if (memcmp(result, dtemplate[i].output, dlen)) { + printk(KERN_ERR "alg: comp: Decompression test %d " + "failed for %s\n", i + 1, algo); + hexdump(result, dlen); + ret = -EINVAL; + goto out; + } + } + + ret = 0; + +out: + return ret; +} + +static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) +{ + struct crypto_aead *tfm; + int err = 0; + + tfm = crypto_alloc_aead(driver, type, mask); + if (IS_ERR(tfm)) { + printk(KERN_ERR "alg: aead: Failed to load transform for %s: " + "%ld\n", driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + if (desc->suite.aead.enc.vecs) { + err = test_aead(tfm, ENCRYPT, desc->suite.aead.enc.vecs, + desc->suite.aead.enc.count); + if (err) + goto out; + } + + if (!err && desc->suite.aead.dec.vecs) + err = test_aead(tfm, DECRYPT, desc->suite.aead.dec.vecs, + desc->suite.aead.dec.count); + +out: + crypto_free_aead(tfm); + return err; +} + +static int alg_test_cipher(const struct alg_test_desc *desc, + const char *driver, u32 type, u32 mask) +{ + struct crypto_ablkcipher *tfm; + int err = 0; + + tfm = crypto_alloc_ablkcipher(driver, type, mask); + if (IS_ERR(tfm)) { + printk(KERN_ERR "alg: cipher: Failed to load transform for " + "%s: %ld\n", driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + if (desc->suite.cipher.enc.vecs) { + err = test_cipher(tfm, ENCRYPT, desc->suite.cipher.enc.vecs, + desc->suite.cipher.enc.count); + if (err) + goto out; + } + + if (desc->suite.cipher.dec.vecs) + err = test_cipher(tfm, DECRYPT, desc->suite.cipher.dec.vecs, + desc->suite.cipher.dec.count); + +out: + crypto_free_ablkcipher(tfm); + return err; +} + +static int alg_test_comp(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) +{ + struct crypto_comp *tfm; + int err; + + tfm = crypto_alloc_comp(driver, type, mask); + if (IS_ERR(tfm)) { + printk(KERN_ERR "alg: comp: Failed to load transform for %s: " + "%ld\n", driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + err = test_comp(tfm, desc->suite.comp.comp.vecs, + desc->suite.comp.decomp.vecs, + desc->suite.comp.comp.count, + desc->suite.comp.decomp.count); + + crypto_free_comp(tfm); + return err; +} + +static int alg_test_hash(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) +{ + struct crypto_ahash *tfm; + int err; + + tfm = crypto_alloc_ahash(driver, type, mask); + if (IS_ERR(tfm)) { + printk(KERN_ERR "alg: hash: Failed to load transform for %s: " + "%ld\n", driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + err = test_hash(tfm, desc->suite.hash.vecs, desc->suite.hash.count); + + crypto_free_ahash(tfm); + return err; +} + +/* Please keep this list sorted by algorithm name. */ +static const struct alg_test_desc alg_test_descs[] = { + { + .alg = "cbc(aes)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_cbc_enc_tv_template, + .count = AES_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_cbc_dec_tv_template, + .count = AES_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "cbc(anubis)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = anubis_cbc_enc_tv_template, + .count = ANUBIS_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = anubis_cbc_dec_tv_template, + .count = ANUBIS_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "cbc(blowfish)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = bf_cbc_enc_tv_template, + .count = BF_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = bf_cbc_dec_tv_template, + .count = BF_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "cbc(camellia)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = camellia_cbc_enc_tv_template, + .count = CAMELLIA_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = camellia_cbc_dec_tv_template, + .count = CAMELLIA_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "cbc(des)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = des_cbc_enc_tv_template, + .count = DES_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = des_cbc_dec_tv_template, + .count = DES_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "cbc(des3_ede)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = des3_ede_cbc_enc_tv_template, + .count = DES3_EDE_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = des3_ede_cbc_dec_tv_template, + .count = DES3_EDE_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "cbc(twofish)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = tf_cbc_enc_tv_template, + .count = TF_CBC_ENC_TEST_VECTORS + }, + .dec = { + .vecs = tf_cbc_dec_tv_template, + .count = TF_CBC_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ccm(aes)", + .test = alg_test_aead, + .suite = { + .aead = { + .enc = { + .vecs = aes_ccm_enc_tv_template, + .count = AES_CCM_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_ccm_dec_tv_template, + .count = AES_CCM_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "crc32c", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = crc32c_tv_template, + .count = CRC32C_TEST_VECTORS + } + } + }, { + .alg = "cts(cbc(aes))", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = cts_mode_enc_tv_template, + .count = CTS_MODE_ENC_TEST_VECTORS + }, + .dec = { + .vecs = cts_mode_dec_tv_template, + .count = CTS_MODE_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "deflate", + .test = alg_test_comp, + .suite = { + .comp = { + .comp = { + .vecs = deflate_comp_tv_template, + .count = DEFLATE_COMP_TEST_VECTORS + }, + .decomp = { + .vecs = deflate_decomp_tv_template, + .count = DEFLATE_DECOMP_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(aes)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_enc_tv_template, + .count = AES_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_dec_tv_template, + .count = AES_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(anubis)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = anubis_enc_tv_template, + .count = ANUBIS_ENC_TEST_VECTORS + }, + .dec = { + .vecs = anubis_dec_tv_template, + .count = ANUBIS_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(arc4)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = arc4_enc_tv_template, + .count = ARC4_ENC_TEST_VECTORS + }, + .dec = { + .vecs = arc4_dec_tv_template, + .count = ARC4_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(blowfish)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = bf_enc_tv_template, + .count = BF_ENC_TEST_VECTORS + }, + .dec = { + .vecs = bf_dec_tv_template, + .count = BF_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(camellia)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = camellia_enc_tv_template, + .count = CAMELLIA_ENC_TEST_VECTORS + }, + .dec = { + .vecs = camellia_dec_tv_template, + .count = CAMELLIA_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(cast5)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = cast5_enc_tv_template, + .count = CAST5_ENC_TEST_VECTORS + }, + .dec = { + .vecs = cast5_dec_tv_template, + .count = CAST5_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(cast6)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = cast6_enc_tv_template, + .count = CAST6_ENC_TEST_VECTORS + }, + .dec = { + .vecs = cast6_dec_tv_template, + .count = CAST6_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(des)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = des_enc_tv_template, + .count = DES_ENC_TEST_VECTORS + }, + .dec = { + .vecs = des_dec_tv_template, + .count = DES_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(des3_ede)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = des3_ede_enc_tv_template, + .count = DES3_EDE_ENC_TEST_VECTORS + }, + .dec = { + .vecs = des3_ede_dec_tv_template, + .count = DES3_EDE_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(khazad)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = khazad_enc_tv_template, + .count = KHAZAD_ENC_TEST_VECTORS + }, + .dec = { + .vecs = khazad_dec_tv_template, + .count = KHAZAD_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(seed)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = seed_enc_tv_template, + .count = SEED_ENC_TEST_VECTORS + }, + .dec = { + .vecs = seed_dec_tv_template, + .count = SEED_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(serpent)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = serpent_enc_tv_template, + .count = SERPENT_ENC_TEST_VECTORS + }, + .dec = { + .vecs = serpent_dec_tv_template, + .count = SERPENT_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(tea)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = tea_enc_tv_template, + .count = TEA_ENC_TEST_VECTORS + }, + .dec = { + .vecs = tea_dec_tv_template, + .count = TEA_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(tnepres)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = tnepres_enc_tv_template, + .count = TNEPRES_ENC_TEST_VECTORS + }, + .dec = { + .vecs = tnepres_dec_tv_template, + .count = TNEPRES_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(twofish)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = tf_enc_tv_template, + .count = TF_ENC_TEST_VECTORS + }, + .dec = { + .vecs = tf_dec_tv_template, + .count = TF_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(xeta)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = xeta_enc_tv_template, + .count = XETA_ENC_TEST_VECTORS + }, + .dec = { + .vecs = xeta_dec_tv_template, + .count = XETA_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "ecb(xtea)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = xtea_enc_tv_template, + .count = XTEA_ENC_TEST_VECTORS + }, + .dec = { + .vecs = xtea_dec_tv_template, + .count = XTEA_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "gcm(aes)", + .test = alg_test_aead, + .suite = { + .aead = { + .enc = { + .vecs = aes_gcm_enc_tv_template, + .count = AES_GCM_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_gcm_dec_tv_template, + .count = AES_GCM_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "hmac(md5)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_md5_tv_template, + .count = HMAC_MD5_TEST_VECTORS + } + } + }, { + .alg = "hmac(rmd128)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_rmd128_tv_template, + .count = HMAC_RMD128_TEST_VECTORS + } + } + }, { + .alg = "hmac(rmd160)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_rmd160_tv_template, + .count = HMAC_RMD160_TEST_VECTORS + } + } + }, { + .alg = "hmac(sha1)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_sha1_tv_template, + .count = HMAC_SHA1_TEST_VECTORS + } + } + }, { + .alg = "hmac(sha224)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_sha224_tv_template, + .count = HMAC_SHA224_TEST_VECTORS + } + } + }, { + .alg = "hmac(sha256)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_sha256_tv_template, + .count = HMAC_SHA256_TEST_VECTORS + } + } + }, { + .alg = "hmac(sha384)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_sha384_tv_template, + .count = HMAC_SHA384_TEST_VECTORS + } + } + }, { + .alg = "hmac(sha512)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = hmac_sha512_tv_template, + .count = HMAC_SHA512_TEST_VECTORS + } + } + }, { + .alg = "lrw(aes)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_lrw_enc_tv_template, + .count = AES_LRW_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_lrw_dec_tv_template, + .count = AES_LRW_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "lzo", + .test = alg_test_comp, + .suite = { + .comp = { + .comp = { + .vecs = lzo_comp_tv_template, + .count = LZO_COMP_TEST_VECTORS + }, + .decomp = { + .vecs = lzo_decomp_tv_template, + .count = LZO_DECOMP_TEST_VECTORS + } + } + } + }, { + .alg = "md4", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = md4_tv_template, + .count = MD4_TEST_VECTORS + } + } + }, { + .alg = "md5", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = md5_tv_template, + .count = MD5_TEST_VECTORS + } + } + }, { + .alg = "michael_mic", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = michael_mic_tv_template, + .count = MICHAEL_MIC_TEST_VECTORS + } + } + }, { + .alg = "pcbc(fcrypt)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = fcrypt_pcbc_enc_tv_template, + .count = FCRYPT_ENC_TEST_VECTORS + }, + .dec = { + .vecs = fcrypt_pcbc_dec_tv_template, + .count = FCRYPT_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "rfc3686(ctr(aes))", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_ctr_enc_tv_template, + .count = AES_CTR_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_ctr_dec_tv_template, + .count = AES_CTR_DEC_TEST_VECTORS + } + } + } + }, { + .alg = "rmd128", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = rmd128_tv_template, + .count = RMD128_TEST_VECTORS + } + } + }, { + .alg = "rmd160", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = rmd160_tv_template, + .count = RMD160_TEST_VECTORS + } + } + }, { + .alg = "rmd256", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = rmd256_tv_template, + .count = RMD256_TEST_VECTORS + } + } + }, { + .alg = "rmd320", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = rmd320_tv_template, + .count = RMD320_TEST_VECTORS + } + } + }, { + .alg = "salsa20", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = salsa20_stream_enc_tv_template, + .count = SALSA20_STREAM_ENC_TEST_VECTORS + } + } + } + }, { + .alg = "sha1", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = sha1_tv_template, + .count = SHA1_TEST_VECTORS + } + } + }, { + .alg = "sha224", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = sha224_tv_template, + .count = SHA224_TEST_VECTORS + } + } + }, { + .alg = "sha256", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = sha256_tv_template, + .count = SHA256_TEST_VECTORS + } + } + }, { + .alg = "sha384", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = sha384_tv_template, + .count = SHA384_TEST_VECTORS + } + } + }, { + .alg = "sha512", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = sha512_tv_template, + .count = SHA512_TEST_VECTORS + } + } + }, { + .alg = "tgr128", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = tgr128_tv_template, + .count = TGR128_TEST_VECTORS + } + } + }, { + .alg = "tgr160", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = tgr160_tv_template, + .count = TGR160_TEST_VECTORS + } + } + }, { + .alg = "tgr192", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = tgr192_tv_template, + .count = TGR192_TEST_VECTORS + } + } + }, { + .alg = "wp256", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = wp256_tv_template, + .count = WP256_TEST_VECTORS + } + } + }, { + .alg = "wp384", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = wp384_tv_template, + .count = WP384_TEST_VECTORS + } + } + }, { + .alg = "wp512", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = wp512_tv_template, + .count = WP512_TEST_VECTORS + } + } + }, { + .alg = "xcbc(aes)", + .test = alg_test_hash, + .suite = { + .hash = { + .vecs = aes_xcbc128_tv_template, + .count = XCBC_AES_TEST_VECTORS + } + } + }, { + .alg = "xts(aes)", + .test = alg_test_cipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_xts_enc_tv_template, + .count = AES_XTS_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_xts_dec_tv_template, + .count = AES_XTS_DEC_TEST_VECTORS + } + } + } + } +}; + +int alg_test(const char *driver, const char *alg, u32 type, u32 mask) +{ + int start = 0; + int end = ARRAY_SIZE(alg_test_descs); + + while (start < end) { + int i = (start + end) / 2; + int diff = strcmp(alg_test_descs[i].alg, alg); + + if (diff > 0) { + end = i; + continue; + } + + if (diff < 0) { + start = i + 1; + continue; + } + + return alg_test_descs[i].test(alg_test_descs + i, driver, + type, mask); + } + + printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver); + return 0; +} +EXPORT_SYMBOL_GPL(alg_test); + +int __init testmgr_init(void) +{ + int i; + + for (i = 0; i < XBUFSIZE; i++) { + xbuf[i] = (void *)__get_free_page(GFP_KERNEL); + if (!xbuf[i]) + goto err_free_xbuf; + } + + for (i = 0; i < XBUFSIZE; i++) { + axbuf[i] = (void *)__get_free_page(GFP_KERNEL); + if (!axbuf[i]) + goto err_free_axbuf; + } + + return 0; + +err_free_axbuf: + for (i = 0; i < XBUFSIZE && axbuf[i]; i++) + free_page((unsigned long)axbuf[i]); +err_free_xbuf: + for (i = 0; i < XBUFSIZE && xbuf[i]; i++) + free_page((unsigned long)xbuf[i]); + + return -ENOMEM; +} + +void testmgr_exit(void) +{ + int i; + + for (i = 0; i < XBUFSIZE; i++) + free_page((unsigned long)axbuf[i]); + for (i = 0; i < XBUFSIZE; i++) + free_page((unsigned long)xbuf[i]); +} diff --git a/crypto/testmgr.h b/crypto/testmgr.h new file mode 100644 index 000000000000..dee94d9ecfba --- /dev/null +++ b/crypto/testmgr.h @@ -0,0 +1,8738 @@ +/* + * Algorithm testing framework and tests. + * + * Copyright (c) 2002 James Morris + * Copyright (c) 2002 Jean-Francois Dive + * Copyright (c) 2007 Nokia Siemens Networks + * Copyright (c) 2008 Herbert Xu + * + * 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. + * + */ +#ifndef _CRYPTO_TESTMGR_H +#define _CRYPTO_TESTMGR_H + +#define MAX_DIGEST_SIZE 64 +#define MAX_TAP 8 + +#define MAX_KEYLEN 56 +#define MAX_IVLEN 32 + +struct hash_testvec { + /* only used with keyed hash algorithms */ + char *key; + char *plaintext; + char *digest; + unsigned char tap[MAX_TAP]; + unsigned char psize; + unsigned char np; + unsigned char ksize; +}; + +struct cipher_testvec { + char *key; + char *iv; + char *input; + char *result; + unsigned short tap[MAX_TAP]; + int np; + unsigned char fail; + unsigned char wk; /* weak key flag */ + unsigned char klen; + unsigned short ilen; + unsigned short rlen; +}; + +struct aead_testvec { + char *key; + char *iv; + char *input; + char *assoc; + char *result; + unsigned char tap[MAX_TAP]; + unsigned char atap[MAX_TAP]; + int np; + int anp; + unsigned char fail; + unsigned char wk; /* weak key flag */ + unsigned char klen; + unsigned short ilen; + unsigned short alen; + unsigned short rlen; +}; + +static char zeroed_string[48]; + +/* + * MD4 test vectors from RFC1320 + */ +#define MD4_TEST_VECTORS 7 + +static struct hash_testvec md4_tv_template [] = { + { + .plaintext = "", + .digest = "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31" + "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0", + }, { + .plaintext = "a", + .psize = 1, + .digest = "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46" + "\x24\x5e\x05\xfb\xdb\xd6\xfb\x24", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xa4\x48\x01\x7a\xaf\x21\xd8\x52" + "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\xd9\x13\x0a\x81\x64\x54\x9f\xe8" + "\x18\x87\x48\x06\xe1\xc7\x01\x4b", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd" + "\xee\xa8\xed\x63\xdf\x41\x2d\xa9", + .np = 2, + .tap = { 13, 13 }, + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\x04\x3f\x85\x82\xf2\x41\xdb\x35" + "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4", + }, { + .plaintext = "123456789012345678901234567890123456789012345678901234567890123" + "45678901234567890", + .psize = 80, + .digest = "\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19" + "\x9c\x3e\x7b\x16\x4f\xcc\x05\x36", + }, +}; + +/* + * MD5 test vectors from RFC1321 + */ +#define MD5_TEST_VECTORS 7 + +static struct hash_testvec md5_tv_template[] = { + { + .digest = "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" + "\xe9\x80\x09\x98\xec\xf8\x42\x7e", + }, { + .plaintext = "a", + .psize = 1, + .digest = "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8" + "\x31\xc3\x99\xe2\x69\x77\x26\x61", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" + "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d" + "\x52\x5a\x2f\x31\xaa\xf1\x61\xd0", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00" + "\x7d\xfb\x49\x6c\xca\x67\xe1\x3b", + .np = 2, + .tap = {13, 13} + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" + "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", + }, { + .plaintext = "12345678901234567890123456789012345678901234567890123456789012" + "345678901234567890", + .psize = 80, + .digest = "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55" + "\xac\x49\xda\x2e\x21\x07\xb6\x7a", + } + +}; + +/* + * RIPEMD-128 test vectors from ISO/IEC 10118-3:2004(E) + */ +#define RMD128_TEST_VECTORS 10 + +static struct hash_testvec rmd128_tv_template[] = { + { + .digest = "\xcd\xf2\x62\x13\xa1\x50\xdc\x3e" + "\xcb\x61\x0f\x18\xf6\xb3\x8b\x46", + }, { + .plaintext = "a", + .psize = 1, + .digest = "\x86\xbe\x7a\xfa\x33\x9d\x0f\xc7" + "\xcf\xc7\x85\xe7\x2f\x57\x8d\x33", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xc1\x4a\x12\x19\x9c\x66\xe4\xba" + "\x84\x63\x6b\x0f\x69\x14\x4c\x77", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x9e\x32\x7b\x3d\x6e\x52\x30\x62" + "\xaf\xc1\x13\x2d\x7d\xf9\xd1\xb8", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xfd\x2a\xa6\x07\xf7\x1d\xc8\xf5" + "\x10\x71\x49\x22\xb3\x71\x83\x4e", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" + "fghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xd1\xe9\x59\xeb\x17\x9c\x91\x1f" + "\xae\xa4\x62\x4c\x60\xc5\xc7\x02", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x3f\x45\xef\x19\x47\x32\xc2\xdb" + "\xb2\xc4\xa2\xc7\x69\x79\x5f\xa3", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighij" + "hijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\xa1\xaa\x06\x89\xd0\xfa\xfa\x2d" + "\xdc\x22\xe8\x8b\x49\x13\x3a\x06", + .np = 2, + .tap = { 28, 28 }, + }, { + .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghi" + "jklmghijklmnhijklmnoijklmnopjklmnopqklmnopqr" + "lmnopqrsmnopqrstnopqrstu", + .psize = 112, + .digest = "\xd4\xec\xc9\x13\xe1\xdf\x77\x6b" + "\xf4\x8d\xe9\xd5\x5b\x1f\x25\x46", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijk", + .psize = 32, + .digest = "\x13\xfc\x13\xe8\xef\xff\x34\x7d" + "\xe1\x93\xff\x46\xdb\xac\xcf\xd4", + } +}; + +/* + * RIPEMD-160 test vectors from ISO/IEC 10118-3:2004(E) + */ +#define RMD160_TEST_VECTORS 10 + +static struct hash_testvec rmd160_tv_template[] = { + { + .digest = "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28" + "\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31", + }, { + .plaintext = "a", + .psize = 1, + .digest = "\x0b\xdc\x9d\x2d\x25\x6b\x3e\xe9\xda\xae" + "\x34\x7b\xe6\xf4\xdc\x83\x5a\x46\x7f\xfe", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\x8e\xb2\x08\xf7\xe0\x5d\x98\x7a\x9b\x04" + "\x4a\x8e\x98\xc6\xb0\x87\xf1\x5a\x0b\xfc", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x5d\x06\x89\xef\x49\xd2\xfa\xe5\x72\xb8" + "\x81\xb1\x23\xa8\x5f\xfa\x21\x59\x5f\x36", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xf7\x1c\x27\x10\x9c\x69\x2c\x1b\x56\xbb" + "\xdc\xeb\x5b\x9d\x28\x65\xb3\x70\x8d\xbc", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" + "fghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xb0\xe2\x0b\x6e\x31\x16\x64\x02\x86\xed" + "\x3a\x87\xa5\x71\x30\x79\xb2\x1f\x51\x89", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x9b\x75\x2e\x45\x57\x3d\x4b\x39\xf4\xdb" + "\xd3\x32\x3c\xab\x82\xbf\x63\x32\x6b\xfb", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighij" + "hijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x12\xa0\x53\x38\x4a\x9c\x0c\x88\xe4\x05" + "\xa0\x6c\x27\xdc\xf4\x9a\xda\x62\xeb\x2b", + .np = 2, + .tap = { 28, 28 }, + }, { + .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghi" + "jklmghijklmnhijklmnoijklmnopjklmnopqklmnopqr" + "lmnopqrsmnopqrstnopqrstu", + .psize = 112, + .digest = "\x6f\x3f\xa3\x9b\x6b\x50\x3c\x38\x4f\x91" + "\x9a\x49\xa7\xaa\x5c\x2c\x08\xbd\xfb\x45", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijk", + .psize = 32, + .digest = "\x94\xc2\x64\x11\x54\x04\xe6\x33\x79\x0d" + "\xfc\xc8\x7b\x58\x7d\x36\x77\x06\x7d\x9f", + } +}; + +/* + * RIPEMD-256 test vectors + */ +#define RMD256_TEST_VECTORS 8 + +static struct hash_testvec rmd256_tv_template[] = { + { + .digest = "\x02\xba\x4c\x4e\x5f\x8e\xcd\x18" + "\x77\xfc\x52\xd6\x4d\x30\xe3\x7a" + "\x2d\x97\x74\xfb\x1e\x5d\x02\x63" + "\x80\xae\x01\x68\xe3\xc5\x52\x2d", + }, { + .plaintext = "a", + .psize = 1, + .digest = "\xf9\x33\x3e\x45\xd8\x57\xf5\xd9" + "\x0a\x91\xba\xb7\x0a\x1e\xba\x0c" + "\xfb\x1b\xe4\xb0\x78\x3c\x9a\xcf" + "\xcd\x88\x3a\x91\x34\x69\x29\x25", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xaf\xbd\x6e\x22\x8b\x9d\x8c\xbb" + "\xce\xf5\xca\x2d\x03\xe6\xdb\xa1" + "\x0a\xc0\xbc\x7d\xcb\xe4\x68\x0e" + "\x1e\x42\xd2\xe9\x75\x45\x9b\x65", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x87\xe9\x71\x75\x9a\x1c\xe4\x7a" + "\x51\x4d\x5c\x91\x4c\x39\x2c\x90" + "\x18\xc7\xc4\x6b\xc1\x44\x65\x55" + "\x4a\xfc\xdf\x54\xa5\x07\x0c\x0e", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\x64\x9d\x30\x34\x75\x1e\xa2\x16" + "\x77\x6b\xf9\xa1\x8a\xcc\x81\xbc" + "\x78\x96\x11\x8a\x51\x97\x96\x87" + "\x82\xdd\x1f\xd9\x7d\x8d\x51\x33", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" + "fghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\x57\x40\xa4\x08\xac\x16\xb7\x20" + "\xb8\x44\x24\xae\x93\x1c\xbb\x1f" + "\xe3\x63\xd1\xd0\xbf\x40\x17\xf1" + "\xa8\x9f\x7e\xa6\xde\x77\xa0\xb8", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x06\xfd\xcc\x7a\x40\x95\x48\xaa" + "\xf9\x13\x68\xc0\x6a\x62\x75\xb5" + "\x53\xe3\xf0\x99\xbf\x0e\xa4\xed" + "\xfd\x67\x78\xdf\x89\xa8\x90\xdd", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighij" + "hijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x38\x43\x04\x55\x83\xaa\xc6\xc8" + "\xc8\xd9\x12\x85\x73\xe7\xa9\x80" + "\x9a\xfb\x2a\x0f\x34\xcc\xc3\x6e" + "\xa9\xe7\x2f\x16\xf6\x36\x8e\x3f", + .np = 2, + .tap = { 28, 28 }, + } +}; + +/* + * RIPEMD-320 test vectors + */ +#define RMD320_TEST_VECTORS 8 + +static struct hash_testvec rmd320_tv_template[] = { + { + .digest = "\x22\xd6\x5d\x56\x61\x53\x6c\xdc\x75\xc1" + "\xfd\xf5\xc6\xde\x7b\x41\xb9\xf2\x73\x25" + "\xeb\xc6\x1e\x85\x57\x17\x7d\x70\x5a\x0e" + "\xc8\x80\x15\x1c\x3a\x32\xa0\x08\x99\xb8", + }, { + .plaintext = "a", + .psize = 1, + .digest = "\xce\x78\x85\x06\x38\xf9\x26\x58\xa5\xa5" + "\x85\x09\x75\x79\x92\x6d\xda\x66\x7a\x57" + "\x16\x56\x2c\xfc\xf6\xfb\xe7\x7f\x63\x54" + "\x2f\x99\xb0\x47\x05\xd6\x97\x0d\xff\x5d", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xde\x4c\x01\xb3\x05\x4f\x89\x30\xa7\x9d" + "\x09\xae\x73\x8e\x92\x30\x1e\x5a\x17\x08" + "\x5b\xef\xfd\xc1\xb8\xd1\x16\x71\x3e\x74" + "\xf8\x2f\xa9\x42\xd6\x4c\xdb\xc4\x68\x2d", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x3a\x8e\x28\x50\x2e\xd4\x5d\x42\x2f\x68" + "\x84\x4f\x9d\xd3\x16\xe7\xb9\x85\x33\xfa" + "\x3f\x2a\x91\xd2\x9f\x84\xd4\x25\xc8\x8d" + "\x6b\x4e\xff\x72\x7d\xf6\x6a\x7c\x01\x97", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xca\xbd\xb1\x81\x0b\x92\x47\x0a\x20\x93" + "\xaa\x6b\xce\x05\x95\x2c\x28\x34\x8c\xf4" + "\x3f\xf6\x08\x41\x97\x51\x66\xbb\x40\xed" + "\x23\x40\x04\xb8\x82\x44\x63\xe6\xb0\x09", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" + "fghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xed\x54\x49\x40\xc8\x6d\x67\xf2\x50\xd2" + "\x32\xc3\x0b\x7b\x3e\x57\x70\xe0\xc6\x0c" + "\x8c\xb9\xa4\xca\xfe\x3b\x11\x38\x8a\xf9" + "\x92\x0e\x1b\x99\x23\x0b\x84\x3c\x86\xa4", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x55\x78\x88\xaf\x5f\x6d\x8e\xd6\x2a\xb6" + "\x69\x45\xc6\xd2\xa0\xa4\x7e\xcd\x53\x41" + "\xe9\x15\xeb\x8f\xea\x1d\x05\x24\x95\x5f" + "\x82\x5d\xc7\x17\xe4\xa0\x08\xab\x2d\x42", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighij" + "hijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\xd0\x34\xa7\x95\x0c\xf7\x22\x02\x1b\xa4" + "\xb8\x4d\xf7\x69\xa5\xde\x20\x60\xe2\x59" + "\xdf\x4c\x9b\xb4\xa4\x26\x8c\x0e\x93\x5b" + "\xbc\x74\x70\xa9\x69\xc9\xd0\x72\xa1\xac", + .np = 2, + .tap = { 28, 28 }, + } +}; + +/* + * SHA1 test vectors from from FIPS PUB 180-1 + */ +#define SHA1_TEST_VECTORS 2 + +static struct hash_testvec sha1_tv_template[] = { + { + .plaintext = "abc", + .psize = 3, + .digest = "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e" + "\x25\x71\x78\x50\xc2\x6c\x9c\xd0\xd8\x9d", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e\xba\xae" + "\x4a\xa1\xf9\x51\x29\xe5\xe5\x46\x70\xf1", + .np = 2, + .tap = { 28, 28 } + } +}; + + +/* + * SHA224 test vectors from from FIPS PUB 180-2 + */ +#define SHA224_TEST_VECTORS 2 + +static struct hash_testvec sha224_tv_template[] = { + { + .plaintext = "abc", + .psize = 3, + .digest = "\x23\x09\x7D\x22\x34\x05\xD8\x22" + "\x86\x42\xA4\x77\xBD\xA2\x55\xB3" + "\x2A\xAD\xBC\xE4\xBD\xA0\xB3\xF7" + "\xE3\x6C\x9D\xA7", + }, { + .plaintext = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x75\x38\x8B\x16\x51\x27\x76\xCC" + "\x5D\xBA\x5D\xA1\xFD\x89\x01\x50" + "\xB0\xC6\x45\x5C\xB4\xF5\x8B\x19" + "\x52\x52\x25\x25", + .np = 2, + .tap = { 28, 28 } + } +}; + +/* + * SHA256 test vectors from from NIST + */ +#define SHA256_TEST_VECTORS 2 + +static struct hash_testvec sha256_tv_template[] = { + { + .plaintext = "abc", + .psize = 3, + .digest = "\xba\x78\x16\xbf\x8f\x01\xcf\xea" + "\x41\x41\x40\xde\x5d\xae\x22\x23" + "\xb0\x03\x61\xa3\x96\x17\x7a\x9c" + "\xb4\x10\xff\x61\xf2\x00\x15\xad", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x24\x8d\x6a\x61\xd2\x06\x38\xb8" + "\xe5\xc0\x26\x93\x0c\x3e\x60\x39" + "\xa3\x3c\xe4\x59\x64\xff\x21\x67" + "\xf6\xec\xed\xd4\x19\xdb\x06\xc1", + .np = 2, + .tap = { 28, 28 } + }, +}; + +/* + * SHA384 test vectors from from NIST and kerneli + */ +#define SHA384_TEST_VECTORS 4 + +static struct hash_testvec sha384_tv_template[] = { + { + .plaintext= "abc", + .psize = 3, + .digest = "\xcb\x00\x75\x3f\x45\xa3\x5e\x8b" + "\xb5\xa0\x3d\x69\x9a\xc6\x50\x07" + "\x27\x2c\x32\xab\x0e\xde\xd1\x63" + "\x1a\x8b\x60\x5a\x43\xff\x5b\xed" + "\x80\x86\x07\x2b\xa1\xe7\xcc\x23" + "\x58\xba\xec\xa1\x34\xc8\x25\xa7", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x33\x91\xfd\xdd\xfc\x8d\xc7\x39" + "\x37\x07\xa6\x5b\x1b\x47\x09\x39" + "\x7c\xf8\xb1\xd1\x62\xaf\x05\xab" + "\xfe\x8f\x45\x0d\xe5\xf3\x6b\xc6" + "\xb0\x45\x5a\x85\x20\xbc\x4e\x6f" + "\x5f\xe9\x5b\x1f\xe3\xc8\x45\x2b", + }, { + .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .psize = 112, + .digest = "\x09\x33\x0c\x33\xf7\x11\x47\xe8" + "\x3d\x19\x2f\xc7\x82\xcd\x1b\x47" + "\x53\x11\x1b\x17\x3b\x3b\x05\xd2" + "\x2f\xa0\x80\x86\xe3\xb0\xf7\x12" + "\xfc\xc7\xc7\x1a\x55\x7e\x2d\xb9" + "\x66\xc3\xe9\xfa\x91\x74\x60\x39", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd" + "efghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", + .psize = 104, + .digest = "\x3d\x20\x89\x73\xab\x35\x08\xdb" + "\xbd\x7e\x2c\x28\x62\xba\x29\x0a" + "\xd3\x01\x0e\x49\x78\xc1\x98\xdc" + "\x4d\x8f\xd0\x14\xe5\x82\x82\x3a" + "\x89\xe1\x6f\x9b\x2a\x7b\xbc\x1a" + "\xc9\x38\xe2\xd1\x99\xe8\xbe\xa4", + .np = 4, + .tap = { 26, 26, 26, 26 } + }, +}; + +/* + * SHA512 test vectors from from NIST and kerneli + */ +#define SHA512_TEST_VECTORS 4 + +static struct hash_testvec sha512_tv_template[] = { + { + .plaintext = "abc", + .psize = 3, + .digest = "\xdd\xaf\x35\xa1\x93\x61\x7a\xba" + "\xcc\x41\x73\x49\xae\x20\x41\x31" + "\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2" + "\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a" + "\x21\x92\x99\x2a\x27\x4f\xc1\xa8" + "\x36\xba\x3c\x23\xa3\xfe\xeb\xbd" + "\x45\x4d\x44\x23\x64\x3c\xe8\x0e" + "\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a" + "\x0c\xed\x7b\xeb\x8e\x08\xa4\x16" + "\x57\xc1\x6e\xf4\x68\xb2\x28\xa8" + "\x27\x9b\xe3\x31\xa7\x03\xc3\x35" + "\x96\xfd\x15\xc1\x3b\x1b\x07\xf9" + "\xaa\x1d\x3b\xea\x57\x78\x9c\xa0" + "\x31\xad\x85\xc7\xa7\x1d\xd7\x03" + "\x54\xec\x63\x12\x38\xca\x34\x45", + }, { + .plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .psize = 112, + .digest = "\x8e\x95\x9b\x75\xda\xe3\x13\xda" + "\x8c\xf4\xf7\x28\x14\xfc\x14\x3f" + "\x8f\x77\x79\xc6\xeb\x9f\x7f\xa1" + "\x72\x99\xae\xad\xb6\x88\x90\x18" + "\x50\x1d\x28\x9e\x49\x00\xf7\xe4" + "\x33\x1b\x99\xde\xc4\xb5\x43\x3a" + "\xc7\xd3\x29\xee\xb6\xdd\x26\x54" + "\x5e\x96\xe5\x5b\x87\x4b\xe9\x09", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd" + "efghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", + .psize = 104, + .digest = "\x93\x0d\x0c\xef\xcb\x30\xff\x11" + "\x33\xb6\x89\x81\x21\xf1\xcf\x3d" + "\x27\x57\x8a\xfc\xaf\xe8\x67\x7c" + "\x52\x57\xcf\x06\x99\x11\xf7\x5d" + "\x8f\x58\x31\xb5\x6e\xbf\xda\x67" + "\xb2\x78\xe6\x6d\xff\x8b\x84\xfe" + "\x2b\x28\x70\xf7\x42\xa5\x80\xd8" + "\xed\xb4\x19\x87\x23\x28\x50\xc9", + .np = 4, + .tap = { 26, 26, 26, 26 } + }, +}; + + +/* + * WHIRLPOOL test vectors from Whirlpool package + * by Vincent Rijmen and Paulo S. L. M. Barreto as part of the NESSIE + * submission + */ +#define WP512_TEST_VECTORS 8 + +static struct hash_testvec wp512_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = "\x19\xFA\x61\xD7\x55\x22\xA4\x66" + "\x9B\x44\xE3\x9C\x1D\x2E\x17\x26" + "\xC5\x30\x23\x21\x30\xD4\x07\xF8" + "\x9A\xFE\xE0\x96\x49\x97\xF7\xA7" + "\x3E\x83\xBE\x69\x8B\x28\x8F\xEB" + "\xCF\x88\xE3\xE0\x3C\x4F\x07\x57" + "\xEA\x89\x64\xE5\x9B\x63\xD9\x37" + "\x08\xB1\x38\xCC\x42\xA6\x6E\xB3", + + + }, { + .plaintext = "a", + .psize = 1, + .digest = "\x8A\xCA\x26\x02\x79\x2A\xEC\x6F" + "\x11\xA6\x72\x06\x53\x1F\xB7\xD7" + "\xF0\xDF\xF5\x94\x13\x14\x5E\x69" + "\x73\xC4\x50\x01\xD0\x08\x7B\x42" + "\xD1\x1B\xC6\x45\x41\x3A\xEF\xF6" + "\x3A\x42\x39\x1A\x39\x14\x5A\x59" + "\x1A\x92\x20\x0D\x56\x01\x95\xE5" + "\x3B\x47\x85\x84\xFD\xAE\x23\x1A", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\x4E\x24\x48\xA4\xC6\xF4\x86\xBB" + "\x16\xB6\x56\x2C\x73\xB4\x02\x0B" + "\xF3\x04\x3E\x3A\x73\x1B\xCE\x72" + "\x1A\xE1\xB3\x03\xD9\x7E\x6D\x4C" + "\x71\x81\xEE\xBD\xB6\xC5\x7E\x27" + "\x7D\x0E\x34\x95\x71\x14\xCB\xD6" + "\xC7\x97\xFC\x9D\x95\xD8\xB5\x82" + "\xD2\x25\x29\x20\x76\xD4\xEE\xF5", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x37\x8C\x84\xA4\x12\x6E\x2D\xC6" + "\xE5\x6D\xCC\x74\x58\x37\x7A\xAC" + "\x83\x8D\x00\x03\x22\x30\xF5\x3C" + "\xE1\xF5\x70\x0C\x0F\xFB\x4D\x3B" + "\x84\x21\x55\x76\x59\xEF\x55\xC1" + "\x06\xB4\xB5\x2A\xC5\xA4\xAA\xA6" + "\x92\xED\x92\x00\x52\x83\x8F\x33" + "\x62\xE8\x6D\xBD\x37\xA8\x90\x3E", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xF1\xD7\x54\x66\x26\x36\xFF\xE9" + "\x2C\x82\xEB\xB9\x21\x2A\x48\x4A" + "\x8D\x38\x63\x1E\xAD\x42\x38\xF5" + "\x44\x2E\xE1\x3B\x80\x54\xE4\x1B" + "\x08\xBF\x2A\x92\x51\xC3\x0B\x6A" + "\x0B\x8A\xAE\x86\x17\x7A\xB4\xA6" + "\xF6\x8F\x67\x3E\x72\x07\x86\x5D" + "\x5D\x98\x19\xA3\xDB\xA4\xEB\x3B", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xDC\x37\xE0\x08\xCF\x9E\xE6\x9B" + "\xF1\x1F\x00\xED\x9A\xBA\x26\x90" + "\x1D\xD7\xC2\x8C\xDE\xC0\x66\xCC" + "\x6A\xF4\x2E\x40\xF8\x2F\x3A\x1E" + "\x08\xEB\xA2\x66\x29\x12\x9D\x8F" + "\xB7\xCB\x57\x21\x1B\x92\x81\xA6" + "\x55\x17\xCC\x87\x9D\x7B\x96\x21" + "\x42\xC6\x5F\x5A\x7A\xF0\x14\x67", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x46\x6E\xF1\x8B\xAB\xB0\x15\x4D" + "\x25\xB9\xD3\x8A\x64\x14\xF5\xC0" + "\x87\x84\x37\x2B\xCC\xB2\x04\xD6" + "\x54\x9C\x4A\xFA\xDB\x60\x14\x29" + "\x4D\x5B\xD8\xDF\x2A\x6C\x44\xE5" + "\x38\xCD\x04\x7B\x26\x81\xA5\x1A" + "\x2C\x60\x48\x1E\x88\xC5\xA2\x0B" + "\x2C\x2A\x80\xCF\x3A\x9A\x08\x3B", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijk", + .psize = 32, + .digest = "\x2A\x98\x7E\xA4\x0F\x91\x70\x61" + "\xF5\xD6\xF0\xA0\xE4\x64\x4F\x48" + "\x8A\x7A\x5A\x52\xDE\xEE\x65\x62" + "\x07\xC5\x62\xF9\x88\xE9\x5C\x69" + "\x16\xBD\xC8\x03\x1B\xC5\xBE\x1B" + "\x7B\x94\x76\x39\xFE\x05\x0B\x56" + "\x93\x9B\xAA\xA0\xAD\xFF\x9A\xE6" + "\x74\x5B\x7B\x18\x1C\x3B\xE3\xFD", + }, +}; + +#define WP384_TEST_VECTORS 8 + +static struct hash_testvec wp384_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = "\x19\xFA\x61\xD7\x55\x22\xA4\x66" + "\x9B\x44\xE3\x9C\x1D\x2E\x17\x26" + "\xC5\x30\x23\x21\x30\xD4\x07\xF8" + "\x9A\xFE\xE0\x96\x49\x97\xF7\xA7" + "\x3E\x83\xBE\x69\x8B\x28\x8F\xEB" + "\xCF\x88\xE3\xE0\x3C\x4F\x07\x57", + + + }, { + .plaintext = "a", + .psize = 1, + .digest = "\x8A\xCA\x26\x02\x79\x2A\xEC\x6F" + "\x11\xA6\x72\x06\x53\x1F\xB7\xD7" + "\xF0\xDF\xF5\x94\x13\x14\x5E\x69" + "\x73\xC4\x50\x01\xD0\x08\x7B\x42" + "\xD1\x1B\xC6\x45\x41\x3A\xEF\xF6" + "\x3A\x42\x39\x1A\x39\x14\x5A\x59", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\x4E\x24\x48\xA4\xC6\xF4\x86\xBB" + "\x16\xB6\x56\x2C\x73\xB4\x02\x0B" + "\xF3\x04\x3E\x3A\x73\x1B\xCE\x72" + "\x1A\xE1\xB3\x03\xD9\x7E\x6D\x4C" + "\x71\x81\xEE\xBD\xB6\xC5\x7E\x27" + "\x7D\x0E\x34\x95\x71\x14\xCB\xD6", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x37\x8C\x84\xA4\x12\x6E\x2D\xC6" + "\xE5\x6D\xCC\x74\x58\x37\x7A\xAC" + "\x83\x8D\x00\x03\x22\x30\xF5\x3C" + "\xE1\xF5\x70\x0C\x0F\xFB\x4D\x3B" + "\x84\x21\x55\x76\x59\xEF\x55\xC1" + "\x06\xB4\xB5\x2A\xC5\xA4\xAA\xA6", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xF1\xD7\x54\x66\x26\x36\xFF\xE9" + "\x2C\x82\xEB\xB9\x21\x2A\x48\x4A" + "\x8D\x38\x63\x1E\xAD\x42\x38\xF5" + "\x44\x2E\xE1\x3B\x80\x54\xE4\x1B" + "\x08\xBF\x2A\x92\x51\xC3\x0B\x6A" + "\x0B\x8A\xAE\x86\x17\x7A\xB4\xA6", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xDC\x37\xE0\x08\xCF\x9E\xE6\x9B" + "\xF1\x1F\x00\xED\x9A\xBA\x26\x90" + "\x1D\xD7\xC2\x8C\xDE\xC0\x66\xCC" + "\x6A\xF4\x2E\x40\xF8\x2F\x3A\x1E" + "\x08\xEB\xA2\x66\x29\x12\x9D\x8F" + "\xB7\xCB\x57\x21\x1B\x92\x81\xA6", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x46\x6E\xF1\x8B\xAB\xB0\x15\x4D" + "\x25\xB9\xD3\x8A\x64\x14\xF5\xC0" + "\x87\x84\x37\x2B\xCC\xB2\x04\xD6" + "\x54\x9C\x4A\xFA\xDB\x60\x14\x29" + "\x4D\x5B\xD8\xDF\x2A\x6C\x44\xE5" + "\x38\xCD\x04\x7B\x26\x81\xA5\x1A", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijk", + .psize = 32, + .digest = "\x2A\x98\x7E\xA4\x0F\x91\x70\x61" + "\xF5\xD6\xF0\xA0\xE4\x64\x4F\x48" + "\x8A\x7A\x5A\x52\xDE\xEE\x65\x62" + "\x07\xC5\x62\xF9\x88\xE9\x5C\x69" + "\x16\xBD\xC8\x03\x1B\xC5\xBE\x1B" + "\x7B\x94\x76\x39\xFE\x05\x0B\x56", + }, +}; + +#define WP256_TEST_VECTORS 8 + +static struct hash_testvec wp256_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = "\x19\xFA\x61\xD7\x55\x22\xA4\x66" + "\x9B\x44\xE3\x9C\x1D\x2E\x17\x26" + "\xC5\x30\x23\x21\x30\xD4\x07\xF8" + "\x9A\xFE\xE0\x96\x49\x97\xF7\xA7", + + + }, { + .plaintext = "a", + .psize = 1, + .digest = "\x8A\xCA\x26\x02\x79\x2A\xEC\x6F" + "\x11\xA6\x72\x06\x53\x1F\xB7\xD7" + "\xF0\xDF\xF5\x94\x13\x14\x5E\x69" + "\x73\xC4\x50\x01\xD0\x08\x7B\x42", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\x4E\x24\x48\xA4\xC6\xF4\x86\xBB" + "\x16\xB6\x56\x2C\x73\xB4\x02\x0B" + "\xF3\x04\x3E\x3A\x73\x1B\xCE\x72" + "\x1A\xE1\xB3\x03\xD9\x7E\x6D\x4C", + }, { + .plaintext = "message digest", + .psize = 14, + .digest = "\x37\x8C\x84\xA4\x12\x6E\x2D\xC6" + "\xE5\x6D\xCC\x74\x58\x37\x7A\xAC" + "\x83\x8D\x00\x03\x22\x30\xF5\x3C" + "\xE1\xF5\x70\x0C\x0F\xFB\x4D\x3B", + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = "\xF1\xD7\x54\x66\x26\x36\xFF\xE9" + "\x2C\x82\xEB\xB9\x21\x2A\x48\x4A" + "\x8D\x38\x63\x1E\xAD\x42\x38\xF5" + "\x44\x2E\xE1\x3B\x80\x54\xE4\x1B", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = "\xDC\x37\xE0\x08\xCF\x9E\xE6\x9B" + "\xF1\x1F\x00\xED\x9A\xBA\x26\x90" + "\x1D\xD7\xC2\x8C\xDE\xC0\x66\xCC" + "\x6A\xF4\x2E\x40\xF8\x2F\x3A\x1E", + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = "\x46\x6E\xF1\x8B\xAB\xB0\x15\x4D" + "\x25\xB9\xD3\x8A\x64\x14\xF5\xC0" + "\x87\x84\x37\x2B\xCC\xB2\x04\xD6" + "\x54\x9C\x4A\xFA\xDB\x60\x14\x29", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijk", + .psize = 32, + .digest = "\x2A\x98\x7E\xA4\x0F\x91\x70\x61" + "\xF5\xD6\xF0\xA0\xE4\x64\x4F\x48" + "\x8A\x7A\x5A\x52\xDE\xEE\x65\x62" + "\x07\xC5\x62\xF9\x88\xE9\x5C\x69", + }, +}; + +/* + * TIGER test vectors from Tiger website + */ +#define TGR192_TEST_VECTORS 6 + +static struct hash_testvec tgr192_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = "\x24\xf0\x13\x0c\x63\xac\x93\x32" + "\x16\x16\x6e\x76\xb1\xbb\x92\x5f" + "\xf3\x73\xde\x2d\x49\x58\x4e\x7a", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xf2\x58\xc1\xe8\x84\x14\xab\x2a" + "\x52\x7a\xb5\x41\xff\xc5\xb8\xbf" + "\x93\x5f\x7b\x95\x1c\x13\x29\x51", + }, { + .plaintext = "Tiger", + .psize = 5, + .digest = "\x9f\x00\xf5\x99\x07\x23\x00\xdd" + "\x27\x6a\xbb\x38\xc8\xeb\x6d\xec" + "\x37\x79\x0c\x11\x6f\x9d\x2b\xdf", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-", + .psize = 64, + .digest = "\x87\xfb\x2a\x90\x83\x85\x1c\xf7" + "\x47\x0d\x2c\xf8\x10\xe6\xdf\x9e" + "\xb5\x86\x44\x50\x34\xa5\xa3\x86", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789", + .psize = 64, + .digest = "\x46\x7d\xb8\x08\x63\xeb\xce\x48" + "\x8d\xf1\xcd\x12\x61\x65\x5d\xe9" + "\x57\x89\x65\x65\x97\x5f\x91\x97", + }, { + .plaintext = "Tiger - A Fast New Hash Function, " + "by Ross Anderson and Eli Biham, " + "proceedings of Fast Software Encryption 3, " + "Cambridge, 1996.", + .psize = 125, + .digest = "\x3d\x9a\xeb\x03\xd1\xbd\x1a\x63" + "\x57\xb2\x77\x4d\xfd\x6d\x5b\x24" + "\xdd\x68\x15\x1d\x50\x39\x74\xfc", + }, +}; + +#define TGR160_TEST_VECTORS 6 + +static struct hash_testvec tgr160_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = "\x24\xf0\x13\x0c\x63\xac\x93\x32" + "\x16\x16\x6e\x76\xb1\xbb\x92\x5f" + "\xf3\x73\xde\x2d", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xf2\x58\xc1\xe8\x84\x14\xab\x2a" + "\x52\x7a\xb5\x41\xff\xc5\xb8\xbf" + "\x93\x5f\x7b\x95", + }, { + .plaintext = "Tiger", + .psize = 5, + .digest = "\x9f\x00\xf5\x99\x07\x23\x00\xdd" + "\x27\x6a\xbb\x38\xc8\xeb\x6d\xec" + "\x37\x79\x0c\x11", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-", + .psize = 64, + .digest = "\x87\xfb\x2a\x90\x83\x85\x1c\xf7" + "\x47\x0d\x2c\xf8\x10\xe6\xdf\x9e" + "\xb5\x86\x44\x50", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789", + .psize = 64, + .digest = "\x46\x7d\xb8\x08\x63\xeb\xce\x48" + "\x8d\xf1\xcd\x12\x61\x65\x5d\xe9" + "\x57\x89\x65\x65", + }, { + .plaintext = "Tiger - A Fast New Hash Function, " + "by Ross Anderson and Eli Biham, " + "proceedings of Fast Software Encryption 3, " + "Cambridge, 1996.", + .psize = 125, + .digest = "\x3d\x9a\xeb\x03\xd1\xbd\x1a\x63" + "\x57\xb2\x77\x4d\xfd\x6d\x5b\x24" + "\xdd\x68\x15\x1d", + }, +}; + +#define TGR128_TEST_VECTORS 6 + +static struct hash_testvec tgr128_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = "\x24\xf0\x13\x0c\x63\xac\x93\x32" + "\x16\x16\x6e\x76\xb1\xbb\x92\x5f", + }, { + .plaintext = "abc", + .psize = 3, + .digest = "\xf2\x58\xc1\xe8\x84\x14\xab\x2a" + "\x52\x7a\xb5\x41\xff\xc5\xb8\xbf", + }, { + .plaintext = "Tiger", + .psize = 5, + .digest = "\x9f\x00\xf5\x99\x07\x23\x00\xdd" + "\x27\x6a\xbb\x38\xc8\xeb\x6d\xec", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-", + .psize = 64, + .digest = "\x87\xfb\x2a\x90\x83\x85\x1c\xf7" + "\x47\x0d\x2c\xf8\x10\xe6\xdf\x9e", + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789", + .psize = 64, + .digest = "\x46\x7d\xb8\x08\x63\xeb\xce\x48" + "\x8d\xf1\xcd\x12\x61\x65\x5d\xe9", + }, { + .plaintext = "Tiger - A Fast New Hash Function, " + "by Ross Anderson and Eli Biham, " + "proceedings of Fast Software Encryption 3, " + "Cambridge, 1996.", + .psize = 125, + .digest = "\x3d\x9a\xeb\x03\xd1\xbd\x1a\x63" + "\x57\xb2\x77\x4d\xfd\x6d\x5b\x24", + }, +}; + +/* + * HMAC-MD5 test vectors from RFC2202 + * (These need to be fixed to not use strlen). + */ +#define HMAC_MD5_TEST_VECTORS 7 + +static struct hash_testvec hmac_md5_tv_template[] = +{ + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + .ksize = 16, + .plaintext = "Hi There", + .psize = 8, + .digest = "\x92\x94\x72\x7a\x36\x38\xbb\x1c" + "\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03" + "\xea\xa8\x6e\x31\x0a\x5d\xb7\x38", + .np = 2, + .tap = {14, 14} + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + .ksize = 16, + .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + .psize = 50, + .digest = "\x56\xbe\x34\x52\x1d\x14\x4c\x88" + "\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", + .ksize = 25, + .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + .psize = 50, + .digest = "\x69\x7e\xaf\x0a\xca\x3a\x3a\xea" + "\x3a\x75\x16\x47\x46\xff\xaa\x79", + }, { + .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + .ksize = 16, + .plaintext = "Test With Truncation", + .psize = 20, + .digest = "\x56\x46\x1e\xf2\x34\x2e\xdc\x00" + "\xf9\xba\xb9\x95\x69\x0e\xfd\x4c", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", + .psize = 54, + .digest = "\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f" + "\x0b\x62\xe6\xce\x61\xb9\xd0\xcd", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + .psize = 73, + .digest = "\x6f\x63\x0f\xad\x67\xcd\xa0\xee" + "\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e", + }, +}; + +/* + * HMAC-RIPEMD128 test vectors from RFC2286 + */ +#define HMAC_RMD128_TEST_VECTORS 7 + +static struct hash_testvec hmac_rmd128_tv_template[] = { + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + .ksize = 16, + .plaintext = "Hi There", + .psize = 8, + .digest = "\xfb\xf6\x1f\x94\x92\xaa\x4b\xbf" + "\x81\xc1\x72\xe8\x4e\x07\x34\xdb", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\x87\x5f\x82\x88\x62\xb6\xb3\x34" + "\xb4\x27\xc5\x5f\x9f\x7f\xf0\x9b", + .np = 2, + .tap = { 14, 14 }, + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + .ksize = 16, + .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + .psize = 50, + .digest = "\x09\xf0\xb2\x84\x6d\x2f\x54\x3d" + "\xa3\x63\xcb\xec\x8d\x62\xa3\x8d", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", + .ksize = 25, + .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + .psize = 50, + .digest = "\xbd\xbb\xd7\xcf\x03\xe4\x4b\x5a" + "\xa6\x0a\xf8\x15\xbe\x4d\x22\x94", + }, { + .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + .ksize = 16, + .plaintext = "Test With Truncation", + .psize = 20, + .digest = "\xe7\x98\x08\xf2\x4b\x25\xfd\x03" + "\x1c\x15\x5f\x0d\x55\x1d\x9a\x3a", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", + .psize = 54, + .digest = "\xdc\x73\x29\x28\xde\x98\x10\x4a" + "\x1f\x59\xd3\x73\xc1\x50\xac\xbb", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + .psize = 73, + .digest = "\x5c\x6b\xec\x96\x79\x3e\x16\xd4" + "\x06\x90\xc2\x37\x63\x5f\x30\xc5", + }, +}; + +/* + * HMAC-RIPEMD160 test vectors from RFC2286 + */ +#define HMAC_RMD160_TEST_VECTORS 7 + +static struct hash_testvec hmac_rmd160_tv_template[] = { + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + .ksize = 20, + .plaintext = "Hi There", + .psize = 8, + .digest = "\x24\xcb\x4b\xd6\x7d\x20\xfc\x1a\x5d\x2e" + "\xd7\x73\x2d\xcc\x39\x37\x7f\x0a\x56\x68", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\xdd\xa6\xc0\x21\x3a\x48\x5a\x9e\x24\xf4" + "\x74\x20\x64\xa7\xf0\x33\xb4\x3c\x40\x69", + .np = 2, + .tap = { 14, 14 }, + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + .ksize = 20, + .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + .psize = 50, + .digest = "\xb0\xb1\x05\x36\x0d\xe7\x59\x96\x0a\xb4" + "\xf3\x52\x98\xe1\x16\xe2\x95\xd8\xe7\xc1", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", + .ksize = 25, + .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + .psize = 50, + .digest = "\xd5\xca\x86\x2f\x4d\x21\xd5\xe6\x10\xe1" + "\x8b\x4c\xf1\xbe\xb9\x7a\x43\x65\xec\xf4", + }, { + .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + .ksize = 20, + .plaintext = "Test With Truncation", + .psize = 20, + .digest = "\x76\x19\x69\x39\x78\xf9\x1d\x90\x53\x9a" + "\xe7\x86\x50\x0f\xf3\xd8\xe0\x51\x8e\x39", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", + .psize = 54, + .digest = "\x64\x66\xca\x07\xac\x5e\xac\x29\xe1\xbd" + "\x52\x3e\x5a\xda\x76\x05\xb7\x91\xfd\x8b", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + .psize = 73, + .digest = "\x69\xea\x60\x79\x8d\x71\x61\x6c\xce\x5f" + "\xd0\x87\x1e\x23\x75\x4c\xd7\x5d\x5a\x0a", + }, +}; + +/* + * HMAC-SHA1 test vectors from RFC2202 + */ +#define HMAC_SHA1_TEST_VECTORS 7 + +static struct hash_testvec hmac_sha1_tv_template[] = { + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + .ksize = 20, + .plaintext = "Hi There", + .psize = 8, + .digest = "\xb6\x17\x31\x86\x55\x05\x72\x64" + "\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1" + "\x46\xbe", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74" + "\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79", + .np = 2, + .tap = { 14, 14 } + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + .ksize = 20, + .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + .psize = 50, + .digest = "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3" + "\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", + .ksize = 25, + .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + .psize = 50, + .digest = "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84" + "\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda", + }, { + .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + .ksize = 20, + .plaintext = "Test With Truncation", + .psize = 20, + .digest = "\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2" + "\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", + .psize = 54, + .digest = "\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70" + "\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + .psize = 73, + .digest = "\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b" + "\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91", + }, +}; + + +/* + * SHA224 HMAC test vectors from RFC4231 + */ +#define HMAC_SHA224_TEST_VECTORS 4 + +static struct hash_testvec hmac_sha224_tv_template[] = { + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b", + .ksize = 20, + /* ("Hi There") */ + .plaintext = "\x48\x69\x20\x54\x68\x65\x72\x65", + .psize = 8, + .digest = "\x89\x6f\xb1\x12\x8a\xbb\xdf\x19" + "\x68\x32\x10\x7c\xd4\x9d\xf3\x3f" + "\x47\xb4\xb1\x16\x99\x12\xba\x4f" + "\x53\x68\x4b\x22", + }, { + .key = "Jefe", + .ksize = 4, + /* ("what do ya want for nothing?") */ + .plaintext = "\x77\x68\x61\x74\x20\x64\x6f\x20" + "\x79\x61\x20\x77\x61\x6e\x74\x20" + "\x66\x6f\x72\x20\x6e\x6f\x74\x68" + "\x69\x6e\x67\x3f", + .psize = 28, + .digest = "\xa3\x0e\x01\x09\x8b\xc6\xdb\xbf" + "\x45\x69\x0f\x3a\x7e\x9e\x6d\x0f" + "\x8b\xbe\xa2\xa3\x9e\x61\x48\x00" + "\x8f\xd0\x5e\x44", + .np = 4, + .tap = { 7, 7, 7, 7 } + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + .ksize = 131, + /* ("Test Using Larger Than Block-Size Key - Hash Key First") */ + .plaintext = "\x54\x65\x73\x74\x20\x55\x73\x69" + "\x6e\x67\x20\x4c\x61\x72\x67\x65" + "\x72\x20\x54\x68\x61\x6e\x20\x42" + "\x6c\x6f\x63\x6b\x2d\x53\x69\x7a" + "\x65\x20\x4b\x65\x79\x20\x2d\x20" + "\x48\x61\x73\x68\x20\x4b\x65\x79" + "\x20\x46\x69\x72\x73\x74", + .psize = 54, + .digest = "\x95\xe9\xa0\xdb\x96\x20\x95\xad" + "\xae\xbe\x9b\x2d\x6f\x0d\xbc\xe2" + "\xd4\x99\xf1\x12\xf2\xd2\xb7\x27" + "\x3f\xa6\x87\x0e", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + .ksize = 131, + /* ("This is a test using a larger than block-size key and a") + (" larger than block-size data. The key needs to be") + (" hashed before being used by the HMAC algorithm.") */ + .plaintext = "\x54\x68\x69\x73\x20\x69\x73\x20" + "\x61\x20\x74\x65\x73\x74\x20\x75" + "\x73\x69\x6e\x67\x20\x61\x20\x6c" + "\x61\x72\x67\x65\x72\x20\x74\x68" + "\x61\x6e\x20\x62\x6c\x6f\x63\x6b" + "\x2d\x73\x69\x7a\x65\x20\x6b\x65" + "\x79\x20\x61\x6e\x64\x20\x61\x20" + "\x6c\x61\x72\x67\x65\x72\x20\x74" + "\x68\x61\x6e\x20\x62\x6c\x6f\x63" + "\x6b\x2d\x73\x69\x7a\x65\x20\x64" + "\x61\x74\x61\x2e\x20\x54\x68\x65" + "\x20\x6b\x65\x79\x20\x6e\x65\x65" + "\x64\x73\x20\x74\x6f\x20\x62\x65" + "\x20\x68\x61\x73\x68\x65\x64\x20" + "\x62\x65\x66\x6f\x72\x65\x20\x62" + "\x65\x69\x6e\x67\x20\x75\x73\x65" + "\x64\x20\x62\x79\x20\x74\x68\x65" + "\x20\x48\x4d\x41\x43\x20\x61\x6c" + "\x67\x6f\x72\x69\x74\x68\x6d\x2e", + .psize = 152, + .digest = "\x3a\x85\x41\x66\xac\x5d\x9f\x02" + "\x3f\x54\xd5\x17\xd0\xb3\x9d\xbd" + "\x94\x67\x70\xdb\x9c\x2b\x95\xc9" + "\xf6\xf5\x65\xd1", + }, +}; + +/* + * HMAC-SHA256 test vectors from + * draft-ietf-ipsec-ciph-sha-256-01.txt + */ +#define HMAC_SHA256_TEST_VECTORS 10 + +static struct hash_testvec hmac_sha256_tv_template[] = { + { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + .ksize = 32, + .plaintext = "abc", + .psize = 3, + .digest = "\xa2\x1b\x1f\x5d\x4c\xf4\xf7\x3a" + "\x4d\xd9\x39\x75\x0f\x7a\x06\x6a" + "\x7f\x98\xcc\x13\x1c\xb1\x6a\x66" + "\x92\x75\x90\x21\xcf\xab\x81\x81", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + .ksize = 32, + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x10\x4f\xdc\x12\x57\x32\x8f\x08" + "\x18\x4b\xa7\x31\x31\xc5\x3c\xae" + "\xe6\x98\xe3\x61\x19\x42\x11\x49" + "\xea\x8c\x71\x24\x56\x69\x7d\x30", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + .ksize = 32, + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 112, + .digest = "\x47\x03\x05\xfc\x7e\x40\xfe\x34" + "\xd3\xee\xb3\xe7\x73\xd9\x5a\xab" + "\x73\xac\xf0\xfd\x06\x04\x47\xa5" + "\xeb\x45\x95\xbf\x33\xa9\xd1\xa3", + }, { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b", + .ksize = 32, + .plaintext = "Hi There", + .psize = 8, + .digest = "\x19\x8a\x60\x7e\xb4\x4b\xfb\xc6" + "\x99\x03\xa0\xf1\xcf\x2b\xbd\xc5" + "\xba\x0a\xa3\xf3\xd9\xae\x3c\x1c" + "\x7a\x3b\x16\x96\xa0\xb6\x8c\xf7", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e" + "\x6a\x04\x24\x26\x08\x95\x75\xc7" + "\x5a\x00\x3f\x08\x9d\x27\x39\x83" + "\x9d\xec\x58\xb9\x64\xec\x38\x43", + .np = 2, + .tap = { 14, 14 } + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + .ksize = 32, + .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + .psize = 50, + .digest = "\xcd\xcb\x12\x20\xd1\xec\xcc\xea" + "\x91\xe5\x3a\xba\x30\x92\xf9\x62" + "\xe5\x49\xfe\x6c\xe9\xed\x7f\xdc" + "\x43\x19\x1f\xbd\xe4\x5c\x30\xb0", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" + "\x21\x22\x23\x24\x25", + .ksize = 37, + .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + .psize = 50, + .digest = "\xd4\x63\x3c\x17\xf6\xfb\x8d\x74" + "\x4c\x66\xde\xe0\xf8\xf0\x74\x55" + "\x6e\xc4\xaf\x55\xef\x07\x99\x85" + "\x41\x46\x8e\xb4\x9b\xd2\xe9\x17", + }, { + .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c", + .ksize = 32, + .plaintext = "Test With Truncation", + .psize = 20, + .digest = "\x75\x46\xaf\x01\x84\x1f\xc0\x9b" + "\x1a\xb9\xc3\x74\x9a\x5f\x1c\x17" + "\xd4\xf5\x89\x66\x8a\x58\x7b\x27" + "\x00\xa9\xc9\x7c\x11\x93\xcf\x42", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", + .psize = 54, + .digest = "\x69\x53\x02\x5e\xd9\x6f\x0c\x09" + "\xf8\x0a\x96\xf7\x8e\x65\x38\xdb" + "\xe2\xe7\xb8\x20\xe3\xdd\x97\x0e" + "\x7d\xdd\x39\x09\x1b\x32\x35\x2f", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key and Larger Than " + "One Block-Size Data", + .psize = 73, + .digest = "\x63\x55\xac\x22\xe8\x90\xd0\xa3" + "\xc8\x48\x1a\x5c\xa4\x82\x5b\xc8" + "\x84\xd3\xe7\xa1\xff\x98\xa2\xfc" + "\x2a\xc7\xd8\xe0\x64\xc3\xb2\xe6", + }, +}; + +#define XCBC_AES_TEST_VECTORS 6 + +static struct hash_testvec aes_xcbc128_tv_template[] = { + { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .plaintext = zeroed_string, + .digest = "\x75\xf0\x25\x1d\x52\x8a\xc0\x1c" + "\x45\x73\xdf\xd5\x84\xd7\x9f\x29", + .psize = 0, + .ksize = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .plaintext = "\x00\x01\x02", + .digest = "\x5b\x37\x65\x80\xae\x2f\x19\xaf" + "\xe7\x21\x9c\xee\xf1\x72\x75\x6f", + .psize = 3, + .ksize = 16, + } , { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .digest = "\xd2\xa2\x46\xfa\x34\x9b\x68\xa7" + "\x99\x98\xa4\x39\x4f\xf7\xa2\x63", + .psize = 16, + .ksize = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13", + .digest = "\x47\xf5\x1b\x45\x64\x96\x62\x15" + "\xb8\x98\x5c\x63\x05\x5e\xd3\x08", + .tap = { 10, 10 }, + .psize = 20, + .np = 2, + .ksize = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .digest = "\xf5\x4f\x0e\xc8\xd2\xb9\xf3\xd3" + "\x68\x07\x73\x4b\xd5\x28\x3f\xd4", + .psize = 32, + .ksize = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .plaintext = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21", + .digest = "\xbe\xcb\xb3\xbc\xcd\xb5\x18\xa3" + "\x06\x77\xd5\x48\x1f\xb6\xb4\xd8", + .tap = { 17, 17 }, + .psize = 34, + .np = 2, + .ksize = 16, + } +}; + +/* + * SHA384 HMAC test vectors from RFC4231 + */ + +#define HMAC_SHA384_TEST_VECTORS 4 + +static struct hash_testvec hmac_sha384_tv_template[] = { + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b", + .ksize = 20, + .plaintext = "Hi There", + .psize = 8, + .digest = "\xaf\xd0\x39\x44\xd8\x48\x95\x62" + "\x6b\x08\x25\xf4\xab\x46\x90\x7f" + "\x15\xf9\xda\xdb\xe4\x10\x1e\xc6" + "\x82\xaa\x03\x4c\x7c\xeb\xc5\x9c" + "\xfa\xea\x9e\xa9\x07\x6e\xde\x7f" + "\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\xaf\x45\xd2\xe3\x76\x48\x40\x31" + "\x61\x7f\x78\xd2\xb5\x8a\x6b\x1b" + "\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47" + "\xe4\x2e\xc3\x73\x63\x22\x44\x5e" + "\x8e\x22\x40\xca\x5e\x69\xe2\xc7" + "\x8b\x32\x39\xec\xfa\xb2\x16\x49", + .np = 4, + .tap = { 7, 7, 7, 7 } + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + .ksize = 131, + .plaintext = "Test Using Larger Than Block-Siz" + "e Key - Hash Key First", + .psize = 54, + .digest = "\x4e\xce\x08\x44\x85\x81\x3e\x90" + "\x88\xd2\xc6\x3a\x04\x1b\xc5\xb4" + "\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f" + "\x3c\xd1\x1f\x05\x03\x3a\xc4\xc6" + "\x0c\x2e\xf6\xab\x40\x30\xfe\x82" + "\x96\x24\x8d\xf1\x63\xf4\x49\x52", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + .ksize = 131, + .plaintext = "This is a test u" + "sing a larger th" + "an block-size ke" + "y and a larger t" + "han block-size d" + "ata. The key nee" + "ds to be hashed " + "before being use" + "d by the HMAC al" + "gorithm.", + .psize = 152, + .digest = "\x66\x17\x17\x8e\x94\x1f\x02\x0d" + "\x35\x1e\x2f\x25\x4e\x8f\xd3\x2c" + "\x60\x24\x20\xfe\xb0\xb8\xfb\x9a" + "\xdc\xce\xbb\x82\x46\x1e\x99\xc5" + "\xa6\x78\xcc\x31\xe7\x99\x17\x6d" + "\x38\x60\xe6\x11\x0c\x46\x52\x3e", + }, +}; + +/* + * SHA512 HMAC test vectors from RFC4231 + */ + +#define HMAC_SHA512_TEST_VECTORS 4 + +static struct hash_testvec hmac_sha512_tv_template[] = { + { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b", + .ksize = 20, + .plaintext = "Hi There", + .psize = 8, + .digest = "\x87\xaa\x7c\xde\xa5\xef\x61\x9d" + "\x4f\xf0\xb4\x24\x1a\x1d\x6c\xb0" + "\x23\x79\xf4\xe2\xce\x4e\xc2\x78" + "\x7a\xd0\xb3\x05\x45\xe1\x7c\xde" + "\xda\xa8\x33\xb7\xd6\xb8\xa7\x02" + "\x03\x8b\x27\x4e\xae\xa3\xf4\xe4" + "\xbe\x9d\x91\x4e\xeb\x61\xf1\x70" + "\x2e\x69\x6c\x20\x3a\x12\x68\x54", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2" + "\xe3\x95\xfb\xe7\x3b\x56\xe0\xa3" + "\x87\xbd\x64\x22\x2e\x83\x1f\xd6" + "\x10\x27\x0c\xd7\xea\x25\x05\x54" + "\x97\x58\xbf\x75\xc0\x5a\x99\x4a" + "\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd" + "\xca\xea\xb1\xa3\x4d\x4a\x6b\x4b" + "\x63\x6e\x07\x0a\x38\xbc\xe7\x37", + .np = 4, + .tap = { 7, 7, 7, 7 } + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + .ksize = 131, + .plaintext = "Test Using Large" + "r Than Block-Siz" + "e Key - Hash Key" + " First", + .psize = 54, + .digest = "\x80\xb2\x42\x63\xc7\xc1\xa3\xeb" + "\xb7\x14\x93\xc1\xdd\x7b\xe8\xb4" + "\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1" + "\x12\x1b\x01\x37\x83\xf8\xf3\x52" + "\x6b\x56\xd0\x37\xe0\x5f\x25\x98" + "\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52" + "\x95\xe6\x4f\x73\xf6\x3f\x0a\xec" + "\x8b\x91\x5a\x98\x5d\x78\x65\x98", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + .ksize = 131, + .plaintext = + "This is a test u" + "sing a larger th" + "an block-size ke" + "y and a larger t" + "han block-size d" + "ata. The key nee" + "ds to be hashed " + "before being use" + "d by the HMAC al" + "gorithm.", + .psize = 152, + .digest = "\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba" + "\xa4\xdf\xa9\xf9\x6e\x5e\x3f\xfd" + "\xde\xbd\x71\xf8\x86\x72\x89\x86" + "\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44" + "\xb6\x02\x2c\xac\x3c\x49\x82\xb1" + "\x0d\x5e\xeb\x55\xc3\xe4\xde\x15" + "\x13\x46\x76\xfb\x6d\xe0\x44\x60" + "\x65\xc9\x74\x40\xfa\x8c\x6a\x58", + }, +}; + +/* + * DES test vectors. + */ +#define DES_ENC_TEST_VECTORS 10 +#define DES_DEC_TEST_VECTORS 4 +#define DES_CBC_ENC_TEST_VECTORS 5 +#define DES_CBC_DEC_TEST_VECTORS 4 +#define DES3_EDE_ENC_TEST_VECTORS 3 +#define DES3_EDE_DEC_TEST_VECTORS 3 +#define DES3_EDE_CBC_ENC_TEST_VECTORS 1 +#define DES3_EDE_CBC_DEC_TEST_VECTORS 1 + +static struct cipher_testvec des_enc_tv_template[] = { + { /* From Applied Cryptography */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7", + .ilen = 8, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", + .rlen = 8, + }, { /* Same key, different plaintext block */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x22\x33\x44\x55\x66\x77\x88\x99", + .ilen = 8, + .result = "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", + .rlen = 8, + }, { /* Sbox test from NBS */ + .key = "\x7c\xa1\x10\x45\x4a\x1a\x6e\x57", + .klen = 8, + .input = "\x01\xa1\xd6\xd0\x39\x77\x67\x42", + .ilen = 8, + .result = "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", + .rlen = 8, + }, { /* Three blocks */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\x22\x33\x44\x55\x66\x77\x88\x99" + "\xca\xfe\xba\xbe\xfe\xed\xbe\xef", + .ilen = 24, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b" + "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90", + .rlen = 24, + }, { /* Weak key */ + .fail = 1, + .wk = 1, + .key = "\x01\x01\x01\x01\x01\x01\x01\x01", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7", + .ilen = 8, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", + .rlen = 8, + }, { /* Two blocks -- for testing encryption across pages */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\x22\x33\x44\x55\x66\x77\x88\x99", + .ilen = 16, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", + .rlen = 16, + .np = 2, + .tap = { 8, 8 } + }, { /* Four blocks -- for testing encryption with chunking */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\x22\x33\x44\x55\x66\x77\x88\x99" + "\xca\xfe\xba\xbe\xfe\xed\xbe\xef" + "\x22\x33\x44\x55\x66\x77\x88\x99", + .ilen = 32, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b" + "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90" + "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", + .rlen = 32, + .np = 3, + .tap = { 14, 10, 8 } + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\x22\x33\x44\x55\x66\x77\x88\x99" + "\xca\xfe\xba\xbe\xfe\xed\xbe\xef", + .ilen = 24, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b" + "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90", + .rlen = 24, + .np = 4, + .tap = { 2, 1, 3, 18 } + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\x22\x33\x44\x55\x66\x77\x88\x99", + .ilen = 16, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\xf7\x9c\x89\x2a\x33\x8f\x4a\x8b", + .rlen = 16, + .np = 5, + .tap = { 2, 2, 2, 2, 8 } + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xe7", + .ilen = 8, + .result = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", + .rlen = 8, + .np = 8, + .tap = { 1, 1, 1, 1, 1, 1, 1, 1 } + }, +}; + +static struct cipher_testvec des_dec_tv_template[] = { + { /* From Applied Cryptography */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xe7", + .rlen = 8, + }, { /* Sbox test from NBS */ + .key = "\x7c\xa1\x10\x45\x4a\x1a\x6e\x57", + .klen = 8, + .input = "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", + .ilen = 8, + .result = "\x01\xa1\xd6\xd0\x39\x77\x67\x42", + .rlen = 8, + }, { /* Two blocks, for chunking test */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", + .ilen = 16, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\xa3\x99\x7b\xca\xaf\x69\xa0\xf5", + .rlen = 16, + .np = 2, + .tap = { 8, 8 } + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d" + "\x69\x0f\x5b\x0d\x9a\x26\x93\x9b", + .ilen = 16, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xe7" + "\xa3\x99\x7b\xca\xaf\x69\xa0\xf5", + .rlen = 16, + .np = 3, + .tap = { 3, 12, 1 } + }, +}; + +static struct cipher_testvec des_cbc_enc_tv_template[] = { + { /* From OpenSSL */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .input = "\x37\x36\x35\x34\x33\x32\x31\x20" + "\x4e\x6f\x77\x20\x69\x73\x20\x74" + "\x68\x65\x20\x74\x69\x6d\x65\x20", + .ilen = 24, + .result = "\xcc\xd1\x73\xff\xab\x20\x39\xf4" + "\xac\xd8\xae\xfd\xdf\xd8\xa1\xeb" + "\x46\x8e\x91\x15\x78\x88\xba\x68", + .rlen = 24, + }, { /* FIPS Pub 81 */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\x12\x34\x56\x78\x90\xab\xcd\xef", + .input = "\x4e\x6f\x77\x20\x69\x73\x20\x74", + .ilen = 8, + .result = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", + .input = "\x68\x65\x20\x74\x69\x6d\x65\x20", + .ilen = 8, + .result = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", + .input = "\x66\x6f\x72\x20\x61\x6c\x6c\x20", + .ilen = 8, + .result = "\x68\x37\x88\x49\x9a\x7c\x05\xf6", + .rlen = 8, + }, { /* Copy of openssl vector for chunk testing */ + /* From OpenSSL */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .input = "\x37\x36\x35\x34\x33\x32\x31\x20" + "\x4e\x6f\x77\x20\x69\x73\x20\x74" + "\x68\x65\x20\x74\x69\x6d\x65\x20", + .ilen = 24, + .result = "\xcc\xd1\x73\xff\xab\x20\x39\xf4" + "\xac\xd8\xae\xfd\xdf\xd8\xa1\xeb" + "\x46\x8e\x91\x15\x78\x88\xba\x68", + .rlen = 24, + .np = 2, + .tap = { 13, 11 } + }, +}; + +static struct cipher_testvec des_cbc_dec_tv_template[] = { + { /* FIPS Pub 81 */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\x12\x34\x56\x78\x90\xab\xcd\xef", + .input = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", + .ilen = 8, + .result = "\x4e\x6f\x77\x20\x69\x73\x20\x74", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\xe5\xc7\xcd\xde\x87\x2b\xf2\x7c", + .input = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", + .ilen = 8, + .result = "\x68\x65\x20\x74\x69\x6d\x65\x20", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", + .input = "\x68\x37\x88\x49\x9a\x7c\x05\xf6", + .ilen = 8, + .result = "\x66\x6f\x72\x20\x61\x6c\x6c\x20", + .rlen = 8, + }, { /* Copy of above, for chunk testing */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .iv = "\x43\xe9\x34\x00\x8c\x38\x9c\x0f", + .input = "\x68\x37\x88\x49\x9a\x7c\x05\xf6", + .ilen = 8, + .result = "\x66\x6f\x72\x20\x61\x6c\x6c\x20", + .rlen = 8, + .np = 2, + .tap = { 4, 4 } + }, +}; + +static struct cipher_testvec des3_ede_enc_tv_template[] = { + { /* These are from openssl */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x55\x55\x55\x55\x55\x55\x55\x55" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 24, + .input = "\x73\x6f\x6d\x65\x64\x61\x74\x61", + .ilen = 8, + .result = "\x18\xd7\x48\xe5\x63\x62\x05\x72", + .rlen = 8, + }, { + .key = "\x03\x52\x02\x07\x67\x20\x82\x17" + "\x86\x02\x87\x66\x59\x08\x21\x98" + "\x64\x05\x6a\xbd\xfe\xa9\x34\x57", + .klen = 24, + .input = "\x73\x71\x75\x69\x67\x67\x6c\x65", + .ilen = 8, + .result = "\xc0\x7d\x2a\x0f\xa5\x66\xfa\x30", + .rlen = 8, + }, { + .key = "\x10\x46\x10\x34\x89\x98\x80\x20" + "\x91\x07\xd0\x15\x89\x19\x01\x01" + "\x19\x07\x92\x10\x98\x1a\x01\x01", + .klen = 24, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 8, + .result = "\xe1\xef\x62\xc3\x32\xfe\x82\x5b", + .rlen = 8, + }, +}; + +static struct cipher_testvec des3_ede_dec_tv_template[] = { + { /* These are from openssl */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x55\x55\x55\x55\x55\x55\x55\x55" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 24, + .input = "\x18\xd7\x48\xe5\x63\x62\x05\x72", + .ilen = 8, + .result = "\x73\x6f\x6d\x65\x64\x61\x74\x61", + .rlen = 8, + }, { + .key = "\x03\x52\x02\x07\x67\x20\x82\x17" + "\x86\x02\x87\x66\x59\x08\x21\x98" + "\x64\x05\x6a\xbd\xfe\xa9\x34\x57", + .klen = 24, + .input = "\xc0\x7d\x2a\x0f\xa5\x66\xfa\x30", + .ilen = 8, + .result = "\x73\x71\x75\x69\x67\x67\x6c\x65", + .rlen = 8, + }, { + .key = "\x10\x46\x10\x34\x89\x98\x80\x20" + "\x91\x07\xd0\x15\x89\x19\x01\x01" + "\x19\x07\x92\x10\x98\x1a\x01\x01", + .klen = 24, + .input = "\xe1\xef\x62\xc3\x32\xfe\x82\x5b", + .ilen = 8, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 8, + }, +}; + +static struct cipher_testvec des3_ede_cbc_enc_tv_template[] = { + { /* Generated from openssl */ + .key = "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24" + "\x44\x4D\x99\x5A\x12\xD6\x40\xC0" + "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8", + .klen = 24, + .iv = "\x7D\x33\x88\x93\x0F\x93\xB2\x42", + .input = "\x6f\x54\x20\x6f\x61\x4d\x79\x6e" + "\x53\x20\x63\x65\x65\x72\x73\x74" + "\x54\x20\x6f\x6f\x4d\x20\x6e\x61" + "\x20\x79\x65\x53\x72\x63\x74\x65" + "\x20\x73\x6f\x54\x20\x6f\x61\x4d" + "\x79\x6e\x53\x20\x63\x65\x65\x72" + "\x73\x74\x54\x20\x6f\x6f\x4d\x20" + "\x6e\x61\x20\x79\x65\x53\x72\x63" + "\x74\x65\x20\x73\x6f\x54\x20\x6f" + "\x61\x4d\x79\x6e\x53\x20\x63\x65" + "\x65\x72\x73\x74\x54\x20\x6f\x6f" + "\x4d\x20\x6e\x61\x20\x79\x65\x53" + "\x72\x63\x74\x65\x20\x73\x6f\x54" + "\x20\x6f\x61\x4d\x79\x6e\x53\x20" + "\x63\x65\x65\x72\x73\x74\x54\x20" + "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79", + .ilen = 128, + .result = "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4" + "\x67\x17\x21\xc7\x6e\x8a\xd5\x49" + "\x74\xb3\x49\x05\xc5\x1c\xd0\xed" + "\x12\x56\x5c\x53\x96\xb6\x00\x7d" + "\x90\x48\xfc\xf5\x8d\x29\x39\xcc" + "\x8a\xd5\x35\x18\x36\x23\x4e\xd7" + "\x76\xd1\xda\x0c\x94\x67\xbb\x04" + "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea" + "\x22\x64\x47\xaa\x8f\x75\x13\xbf" + "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a" + "\x71\x63\x2e\x89\x7b\x1e\x12\xca" + "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a" + "\xd6\xf9\x21\x31\x62\x44\x45\xa6" + "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc" + "\x9d\xde\xa5\x70\xe9\x42\x45\x8a" + "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19", + .rlen = 128, + }, +}; + +static struct cipher_testvec des3_ede_cbc_dec_tv_template[] = { + { /* Generated from openssl */ + .key = "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24" + "\x44\x4D\x99\x5A\x12\xD6\x40\xC0" + "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8", + .klen = 24, + .iv = "\x7D\x33\x88\x93\x0F\x93\xB2\x42", + .input = "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4" + "\x67\x17\x21\xc7\x6e\x8a\xd5\x49" + "\x74\xb3\x49\x05\xc5\x1c\xd0\xed" + "\x12\x56\x5c\x53\x96\xb6\x00\x7d" + "\x90\x48\xfc\xf5\x8d\x29\x39\xcc" + "\x8a\xd5\x35\x18\x36\x23\x4e\xd7" + "\x76\xd1\xda\x0c\x94\x67\xbb\x04" + "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea" + "\x22\x64\x47\xaa\x8f\x75\x13\xbf" + "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a" + "\x71\x63\x2e\x89\x7b\x1e\x12\xca" + "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a" + "\xd6\xf9\x21\x31\x62\x44\x45\xa6" + "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc" + "\x9d\xde\xa5\x70\xe9\x42\x45\x8a" + "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19", + .ilen = 128, + .result = "\x6f\x54\x20\x6f\x61\x4d\x79\x6e" + "\x53\x20\x63\x65\x65\x72\x73\x74" + "\x54\x20\x6f\x6f\x4d\x20\x6e\x61" + "\x20\x79\x65\x53\x72\x63\x74\x65" + "\x20\x73\x6f\x54\x20\x6f\x61\x4d" + "\x79\x6e\x53\x20\x63\x65\x65\x72" + "\x73\x74\x54\x20\x6f\x6f\x4d\x20" + "\x6e\x61\x20\x79\x65\x53\x72\x63" + "\x74\x65\x20\x73\x6f\x54\x20\x6f" + "\x61\x4d\x79\x6e\x53\x20\x63\x65" + "\x65\x72\x73\x74\x54\x20\x6f\x6f" + "\x4d\x20\x6e\x61\x20\x79\x65\x53" + "\x72\x63\x74\x65\x20\x73\x6f\x54" + "\x20\x6f\x61\x4d\x79\x6e\x53\x20" + "\x63\x65\x65\x72\x73\x74\x54\x20" + "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79", + .rlen = 128, + }, +}; + +/* + * Blowfish test vectors. + */ +#define BF_ENC_TEST_VECTORS 6 +#define BF_DEC_TEST_VECTORS 6 +#define BF_CBC_ENC_TEST_VECTORS 1 +#define BF_CBC_DEC_TEST_VECTORS 1 + +static struct cipher_testvec bf_enc_tv_template[] = { + { /* DES test vectors from OpenSSL */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 8, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 8, + .result = "\x4e\xf9\x97\x45\x61\x98\xdd\x78", + .rlen = 8, + }, { + .key = "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ilen = 8, + .result = "\xa7\x90\x79\x51\x08\xea\x3c\xae", + .rlen = 8, + }, { + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .klen = 8, + .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 8, + .result = "\xe8\x7a\x24\x4e\x2c\xc8\x5e\x82", + .rlen = 8, + }, { /* Vary the keylength... */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" + "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f", + .klen = 16, + .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 8, + .result = "\x93\x14\x28\x87\xee\x3b\xe1\x5c", + .rlen = 8, + }, { + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" + "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" + "\x00\x11\x22\x33\x44", + .klen = 21, + .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 8, + .result = "\xe6\xf5\x1e\xd7\x9b\x9d\xb2\x1f", + .rlen = 8, + }, { /* Generated with bf488 */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" + "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x04\x68\x91\x04\xc2\xfd\x3b\x2f" + "\x58\x40\x23\x64\x1a\xba\x61\x76" + "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e" + "\xff\xff\xff\xff\xff\xff\xff\xff", + .klen = 56, + .input = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 8, + .result = "\xc0\x45\x04\x01\x2e\x4e\x1f\x53", + .rlen = 8, + }, +}; + +static struct cipher_testvec bf_dec_tv_template[] = { + { /* DES test vectors from OpenSSL */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 8, + .input = "\x4e\xf9\x97\x45\x61\x98\xdd\x78", + .ilen = 8, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 8, + }, { + .key = "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e", + .klen = 8, + .input = "\xa7\x90\x79\x51\x08\xea\x3c\xae", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .rlen = 8, + }, { + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .klen = 8, + .input = "\xe8\x7a\x24\x4e\x2c\xc8\x5e\x82", + .ilen = 8, + .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 8, + }, { /* Vary the keylength... */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" + "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f", + .klen = 16, + .input = "\x93\x14\x28\x87\xee\x3b\xe1\x5c", + .ilen = 8, + .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 8, + }, { + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" + "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" + "\x00\x11\x22\x33\x44", + .klen = 21, + .input = "\xe6\xf5\x1e\xd7\x9b\x9d\xb2\x1f", + .ilen = 8, + .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 8, + }, { /* Generated with bf488, using OpenSSL, Libgcrypt and Nettle */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87" + "\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x04\x68\x91\x04\xc2\xfd\x3b\x2f" + "\x58\x40\x23\x64\x1a\xba\x61\x76" + "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e" + "\xff\xff\xff\xff\xff\xff\xff\xff", + .klen = 56, + .input = "\xc0\x45\x04\x01\x2e\x4e\x1f\x53", + .ilen = 8, + .result = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 8, + }, +}; + +static struct cipher_testvec bf_cbc_enc_tv_template[] = { + { /* From OpenSSL */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .klen = 16, + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .input = "\x37\x36\x35\x34\x33\x32\x31\x20" + "\x4e\x6f\x77\x20\x69\x73\x20\x74" + "\x68\x65\x20\x74\x69\x6d\x65\x20" + "\x66\x6f\x72\x20\x00\x00\x00\x00", + .ilen = 32, + .result = "\x6b\x77\xb4\xd6\x30\x06\xde\xe6" + "\x05\xb1\x56\xe2\x74\x03\x97\x93" + "\x58\xde\xb9\xe7\x15\x46\x16\xd9" + "\x59\xf1\x65\x2b\xd5\xff\x92\xcc", + .rlen = 32, + }, +}; + +static struct cipher_testvec bf_cbc_dec_tv_template[] = { + { /* From OpenSSL */ + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .klen = 16, + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .input = "\x6b\x77\xb4\xd6\x30\x06\xde\xe6" + "\x05\xb1\x56\xe2\x74\x03\x97\x93" + "\x58\xde\xb9\xe7\x15\x46\x16\xd9" + "\x59\xf1\x65\x2b\xd5\xff\x92\xcc", + .ilen = 32, + .result = "\x37\x36\x35\x34\x33\x32\x31\x20" + "\x4e\x6f\x77\x20\x69\x73\x20\x74" + "\x68\x65\x20\x74\x69\x6d\x65\x20" + "\x66\x6f\x72\x20\x00\x00\x00\x00", + .rlen = 32, + }, +}; + +/* + * Twofish test vectors. + */ +#define TF_ENC_TEST_VECTORS 3 +#define TF_DEC_TEST_VECTORS 3 +#define TF_CBC_ENC_TEST_VECTORS 4 +#define TF_CBC_DEC_TEST_VECTORS 4 + +static struct cipher_testvec tf_enc_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = zeroed_string, + .ilen = 16, + .result = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77", + .klen = 24, + .input = zeroed_string, + .ilen = 16, + .result = "\xcf\xd1\xd2\xe5\xa9\xbe\x9c\xdf" + "\x50\x1f\x13\xb8\x92\xbd\x22\x48", + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .klen = 32, + .input = zeroed_string, + .ilen = 16, + .result = "\x37\x52\x7b\xe0\x05\x23\x34\xb8" + "\x9f\x0c\xfc\xca\xe8\x7c\xfa\x20", + .rlen = 16, + }, +}; + +static struct cipher_testvec tf_dec_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77", + .klen = 24, + .input = "\xcf\xd1\xd2\xe5\xa9\xbe\x9c\xdf" + "\x50\x1f\x13\xb8\x92\xbd\x22\x48", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .klen = 32, + .input = "\x37\x52\x7b\xe0\x05\x23\x34\xb8" + "\x9f\x0c\xfc\xca\xe8\x7c\xfa\x20", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, +}; + +static struct cipher_testvec tf_cbc_enc_tv_template[] = { + { /* Generated with Nettle */ + .key = zeroed_string, + .klen = 16, + .iv = zeroed_string, + .input = zeroed_string, + .ilen = 16, + .result = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .iv = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", + .input = zeroed_string, + .ilen = 16, + .result = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" + "\x86\xcb\x08\x6b\x78\x9f\x54\x19", + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .iv = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" + "\x86\xcb\x08\x6b\x78\x9f\x54\x19", + .input = zeroed_string, + .ilen = 16, + .result = "\x05\xef\x8c\x61\xa8\x11\x58\x26" + "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .iv = zeroed_string, + .input = zeroed_string, + .ilen = 48, + .result = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a" + "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" + "\x86\xcb\x08\x6b\x78\x9f\x54\x19" + "\x05\xef\x8c\x61\xa8\x11\x58\x26" + "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", + .rlen = 48, + }, +}; + +static struct cipher_testvec tf_cbc_dec_tv_template[] = { + { /* Reverse of the first four above */ + .key = zeroed_string, + .klen = 16, + .iv = zeroed_string, + .input = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .iv = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a", + .input = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" + "\x86\xcb\x08\x6b\x78\x9f\x54\x19", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .iv = "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" + "\x86\xcb\x08\x6b\x78\x9f\x54\x19", + .input = "\x05\xef\x8c\x61\xa8\x11\x58\x26" + "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .iv = zeroed_string, + .input = "\x9f\x58\x9f\x5c\xf6\x12\x2c\x32" + "\xb6\xbf\xec\x2f\x2a\xe8\xc3\x5a" + "\xd4\x91\xdb\x16\xe7\xb1\xc3\x9e" + "\x86\xcb\x08\x6b\x78\x9f\x54\x19" + "\x05\xef\x8c\x61\xa8\x11\x58\x26" + "\x34\xba\x5c\xb7\x10\x6a\xa6\x41", + .ilen = 48, + .result = zeroed_string, + .rlen = 48, + }, +}; + +/* + * Serpent test vectors. These are backwards because Serpent writes + * octet sequences in right-to-left mode. + */ +#define SERPENT_ENC_TEST_VECTORS 4 +#define SERPENT_DEC_TEST_VECTORS 4 + +#define TNEPRES_ENC_TEST_VECTORS 4 +#define TNEPRES_DEC_TEST_VECTORS 4 + +static struct cipher_testvec serpent_enc_tv_template[] = { + { + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .ilen = 16, + .result = "\x12\x07\xfc\xce\x9b\xd0\xd6\x47" + "\x6a\xe9\x8f\xbe\xd1\x43\xa0\xe2", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .ilen = 16, + .result = "\x4c\x7d\x8a\x32\x80\x72\xa2\x2c" + "\x82\x3e\x4a\x1f\x3a\xcd\xa1\x6d", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .ilen = 16, + .result = "\xde\x26\x9f\xf8\x33\xe4\x32\xb8" + "\x5b\x2e\x88\xd2\x70\x1c\xe7\x5c", + .rlen = 16, + }, { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", + .klen = 16, + .input = zeroed_string, + .ilen = 16, + .result = "\xdd\xd2\x6b\x98\xa5\xff\xd8\x2c" + "\x05\x34\x5a\x9d\xad\xbf\xaf\x49", + .rlen = 16, + }, +}; + +static struct cipher_testvec tnepres_enc_tv_template[] = { + { /* KeySize=128, PT=0, I=1 */ + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .key = "\x80\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 16, + .ilen = 16, + .result = "\x49\xaf\xbf\xad\x9d\x5a\x34\x05" + "\x2c\xd8\xff\xa5\x98\x6b\xd2\xdd", + .rlen = 16, + }, { /* KeySize=192, PT=0, I=1 */ + .key = "\x80\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 24, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 16, + .result = "\xe7\x8e\x54\x02\xc7\x19\x55\x68" + "\xac\x36\x78\xf7\xa3\xf6\x0c\x66", + .rlen = 16, + }, { /* KeySize=256, PT=0, I=1 */ + .key = "\x80\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 32, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 16, + .result = "\xab\xed\x96\xe7\x66\xbf\x28\xcb" + "\xc0\xeb\xd2\x1a\x82\xef\x08\x19", + .rlen = 16, + }, { /* KeySize=256, I=257 */ + .key = "\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18" + "\x17\x16\x15\x14\x13\x12\x11\x10" + "\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08" + "\x07\x06\x05\x04\x03\x02\x01\x00", + .klen = 32, + .input = "\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08" + "\x07\x06\x05\x04\x03\x02\x01\x00", + .ilen = 16, + .result = "\x5c\xe7\x1c\x70\xd2\x88\x2e\x5b" + "\xb8\x32\xe4\x33\xf8\x9f\x26\xde", + .rlen = 16, + }, +}; + + +static struct cipher_testvec serpent_dec_tv_template[] = { + { + .input = "\x12\x07\xfc\xce\x9b\xd0\xd6\x47" + "\x6a\xe9\x8f\xbe\xd1\x43\xa0\xe2", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\x4c\x7d\x8a\x32\x80\x72\xa2\x2c" + "\x82\x3e\x4a\x1f\x3a\xcd\xa1\x6d", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\xde\x26\x9f\xf8\x33\xe4\x32\xb8" + "\x5b\x2e\x88\xd2\x70\x1c\xe7\x5c", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", + .klen = 16, + .input = "\xdd\xd2\x6b\x98\xa5\xff\xd8\x2c" + "\x05\x34\x5a\x9d\xad\xbf\xaf\x49", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, +}; + +static struct cipher_testvec tnepres_dec_tv_template[] = { + { + .input = "\x41\xcc\x6b\x31\x59\x31\x45\x97" + "\x6d\x6f\xbb\x38\x4b\x37\x21\x28", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\xea\xf4\xd7\xfc\xd8\x01\x34\x47" + "\x81\x45\x0b\xfa\x0c\xd6\xad\x6e", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\x64\xa9\x1a\x37\xed\x9f\xe7\x49" + "\xa8\x4e\x76\xd6\xf5\x0d\x78\xee", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { /* KeySize=128, I=121 */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", + .klen = 16, + .input = "\x3d\xda\xbf\xc0\x06\xda\xab\x06" + "\x46\x2a\xf4\xef\x81\x54\x4e\x26", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, +}; + + +/* Cast6 test vectors from RFC 2612 */ +#define CAST6_ENC_TEST_VECTORS 3 +#define CAST6_DEC_TEST_VECTORS 3 + +static struct cipher_testvec cast6_enc_tv_template[] = { + { + .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" + "\x0a\xf7\x56\x47\xf2\x9f\x61\x5d", + .klen = 16, + .input = zeroed_string, + .ilen = 16, + .result = "\xc8\x42\xa0\x89\x72\xb4\x3d\x20" + "\x83\x6c\x91\xd1\xb7\x53\x0f\x6b", + .rlen = 16, + }, { + .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" + "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" + "\xba\xc7\x7a\x77\x17\x94\x28\x63", + .klen = 24, + .input = zeroed_string, + .ilen = 16, + .result = "\x1b\x38\x6c\x02\x10\xdc\xad\xcb" + "\xdd\x0e\x41\xaa\x08\xa7\xa7\xe8", + .rlen = 16, + }, { + .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" + "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" + "\x8d\x7c\x47\xce\x26\x49\x08\x46" + "\x1c\xc1\xb5\x13\x7a\xe6\xb6\x04", + .klen = 32, + .input = zeroed_string, + .ilen = 16, + .result = "\x4f\x6a\x20\x38\x28\x68\x97\xb9" + "\xc9\x87\x01\x36\x55\x33\x17\xfa", + .rlen = 16, + }, +}; + +static struct cipher_testvec cast6_dec_tv_template[] = { + { + .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" + "\x0a\xf7\x56\x47\xf2\x9f\x61\x5d", + .klen = 16, + .input = "\xc8\x42\xa0\x89\x72\xb4\x3d\x20" + "\x83\x6c\x91\xd1\xb7\x53\x0f\x6b", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" + "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" + "\xba\xc7\x7a\x77\x17\x94\x28\x63", + .klen = 24, + .input = "\x1b\x38\x6c\x02\x10\xdc\xad\xcb" + "\xdd\x0e\x41\xaa\x08\xa7\xa7\xe8", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\x23\x42\xbb\x9e\xfa\x38\x54\x2c" + "\xbe\xd0\xac\x83\x94\x0a\xc2\x98" + "\x8d\x7c\x47\xce\x26\x49\x08\x46" + "\x1c\xc1\xb5\x13\x7a\xe6\xb6\x04", + .klen = 32, + .input = "\x4f\x6a\x20\x38\x28\x68\x97\xb9" + "\xc9\x87\x01\x36\x55\x33\x17\xfa", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, +}; + + +/* + * AES test vectors. + */ +#define AES_ENC_TEST_VECTORS 3 +#define AES_DEC_TEST_VECTORS 3 +#define AES_CBC_ENC_TEST_VECTORS 4 +#define AES_CBC_DEC_TEST_VECTORS 4 +#define AES_LRW_ENC_TEST_VECTORS 8 +#define AES_LRW_DEC_TEST_VECTORS 8 +#define AES_XTS_ENC_TEST_VECTORS 4 +#define AES_XTS_DEC_TEST_VECTORS 4 +#define AES_CTR_ENC_TEST_VECTORS 7 +#define AES_CTR_DEC_TEST_VECTORS 6 +#define AES_GCM_ENC_TEST_VECTORS 9 +#define AES_GCM_DEC_TEST_VECTORS 8 +#define AES_CCM_ENC_TEST_VECTORS 7 +#define AES_CCM_DEC_TEST_VECTORS 7 + +static struct cipher_testvec aes_enc_tv_template[] = { + { /* From FIPS-197 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .ilen = 16, + .result = "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30" + "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17", + .klen = 24, + .input = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .ilen = 16, + .result = "\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0" + "\x6e\xaf\x70\xa0\xec\x0d\x71\x91", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .ilen = 16, + .result = "\x8e\xa2\xb7\xca\x51\x67\x45\xbf" + "\xea\xfc\x49\x90\x4b\x49\x60\x89", + .rlen = 16, + }, +}; + +static struct cipher_testvec aes_dec_tv_template[] = { + { /* From FIPS-197 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30" + "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a", + .ilen = 16, + .result = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17", + .klen = 24, + .input = "\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0" + "\x6e\xaf\x70\xa0\xec\x0d\x71\x91", + .ilen = 16, + .result = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\x8e\xa2\xb7\xca\x51\x67\x45\xbf" + "\xea\xfc\x49\x90\x4b\x49\x60\x89", + .ilen = 16, + .result = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .rlen = 16, + }, +}; + +static struct cipher_testvec aes_cbc_enc_tv_template[] = { + { /* From RFC 3602 */ + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .klen = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .input = "Single block msg", + .ilen = 16, + .result = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" + "\x27\x08\x94\x2d\xbe\x77\x18\x1a", + .rlen = 16, + }, { + .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" + "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", + .klen = 16, + .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" + "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .ilen = 32, + .result = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" + "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" + "\x75\x86\x60\x2d\x25\x3c\xff\xf9" + "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", + .rlen = 32, + }, { /* From NIST SP800-38A */ + .key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" + "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + .klen = 24, + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .ilen = 64, + .result = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" + "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" + "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" + "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" + "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" + "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" + "\x08\xb0\xe2\x79\x88\x59\x88\x81" + "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd", + .rlen = 64, + }, { + .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe" + "\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" + "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", + .klen = 32, + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .ilen = 64, + .result = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" + "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" + "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" + "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" + "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" + "\xa5\x30\xe2\x63\x04\x23\x14\x61" + "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" + "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", + .rlen = 64, + }, +}; + +static struct cipher_testvec aes_cbc_dec_tv_template[] = { + { /* From RFC 3602 */ + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .klen = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .input = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" + "\x27\x08\x94\x2d\xbe\x77\x18\x1a", + .ilen = 16, + .result = "Single block msg", + .rlen = 16, + }, { + .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" + "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", + .klen = 16, + .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" + "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", + .input = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" + "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" + "\x75\x86\x60\x2d\x25\x3c\xff\xf9" + "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", + .ilen = 32, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .rlen = 32, + }, { /* From NIST SP800-38A */ + .key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" + "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + .klen = 24, + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .input = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" + "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" + "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" + "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" + "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" + "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" + "\x08\xb0\xe2\x79\x88\x59\x88\x81" + "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd", + .ilen = 64, + .result = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .rlen = 64, + }, { + .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe" + "\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" + "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", + .klen = 32, + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .input = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" + "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" + "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" + "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" + "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" + "\xa5\x30\xe2\x63\x04\x23\x14\x61" + "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" + "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", + .ilen = 64, + .result = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .rlen = 64, + }, +}; + +static struct cipher_testvec aes_lrw_enc_tv_template[] = { + /* from http://grouper.ieee.org/groups/1619/email/pdf00017.pdf */ + { /* LRW-32-AES 1 */ + .key = "\x45\x62\xac\x25\xf8\x28\x17\x6d" + "\x4c\x26\x84\x14\xb5\x68\x01\x85" + "\x25\x8e\x2a\x05\xe7\x3e\x9d\x03" + "\xee\x5a\x83\x0c\xcc\x09\x4c\x87", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\xf1\xb2\x73\xcd\x65\xa3\xdf\x5f" + "\xe9\x5d\x48\x92\x54\x63\x4e\xb8", + .rlen = 16, + }, { /* LRW-32-AES 2 */ + .key = "\x59\x70\x47\x14\xf5\x57\x47\x8c" + "\xd7\x79\xe8\x0f\x54\x88\x79\x44" + "\x0d\x48\xf0\xb7\xb1\x5a\x53\xea" + "\x1c\xaa\x6b\x29\xc2\xca\xfb\xaf", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x02", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\x00\xc8\x2b\xae\x95\xbb\xcd\xe5" + "\x27\x4f\x07\x69\xb2\x60\xe1\x36", + .rlen = 16, + }, { /* LRW-32-AES 3 */ + .key = "\xd8\x2a\x91\x34\xb2\x6a\x56\x50" + "\x30\xfe\x69\xe2\x37\x7f\x98\x47" + "\xcd\xf9\x0b\x16\x0c\x64\x8f\xb6" + "\xb0\x0d\x0d\x1b\xae\x85\x87\x1f", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x02\x00\x00\x00\x00", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\x76\x32\x21\x83\xed\x8f\xf1\x82" + "\xf9\x59\x62\x03\x69\x0e\x5e\x01", + .rlen = 16, + }, { /* LRW-32-AES 4 */ + .key = "\x0f\x6a\xef\xf8\xd3\xd2\xbb\x15" + "\x25\x83\xf7\x3c\x1f\x01\x28\x74" + "\xca\xc6\xbc\x35\x4d\x4a\x65\x54" + "\x90\xae\x61\xcf\x7b\xae\xbd\xcc" + "\xad\xe4\x94\xc5\x4a\x29\xae\x70", + .klen = 40, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\x9c\x0f\x15\x2f\x55\xa2\xd8\xf0" + "\xd6\x7b\x8f\x9e\x28\x22\xbc\x41", + .rlen = 16, + }, { /* LRW-32-AES 5 */ + .key = "\x8a\xd4\xee\x10\x2f\xbd\x81\xff" + "\xf8\x86\xce\xac\x93\xc5\xad\xc6" + "\xa0\x19\x07\xc0\x9d\xf7\xbb\xdd" + "\x52\x13\xb2\xb7\xf0\xff\x11\xd8" + "\xd6\x08\xd0\xcd\x2e\xb1\x17\x6f", + .klen = 40, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x02\x00\x00\x00\x00", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\xd4\x27\x6a\x7f\x14\x91\x3d\x65" + "\xc8\x60\x48\x02\x87\xe3\x34\x06", + .rlen = 16, + }, { /* LRW-32-AES 6 */ + .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" + "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" + "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" + "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" + "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" + "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", + .klen = 48, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\xbd\x06\xb8\xe1\xdb\x98\x89\x9e" + "\xc4\x98\xe4\x91\xcf\x1c\x70\x2b", + .rlen = 16, + }, { /* LRW-32-AES 7 */ + .key = "\xfb\x76\x15\xb2\x3d\x80\x89\x1d" + "\xd4\x70\x98\x0b\xc7\x95\x84\xc8" + "\xb2\xfb\x64\xce\x60\x97\x87\x8d" + "\x17\xfc\xe4\x5a\x49\xe8\x30\xb7" + "\x6e\x78\x17\xe7\x2d\x5e\x12\xd4" + "\x60\x64\x04\x7a\xf1\x2f\x9e\x0c", + .klen = 48, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x02\x00\x00\x00\x00", + .input = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .ilen = 16, + .result = "\x5b\x90\x8e\xc1\xab\xdd\x67\x5f" + "\x3d\x69\x8a\x95\x53\xc8\x9c\xe5", + .rlen = 16, + }, { +/* http://www.mail-archive.com/stds-p1619@listserv.ieee.org/msg00173.html */ + .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" + "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" + "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" + "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" + "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" + "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", + .klen = 48, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\x05\x11\xb7\x18\xab\xc6\x2d\xac" + "\x70\x5d\xf6\x22\x94\xcd\xe5\x6c" + "\x17\x6b\xf6\x1c\xf0\xf3\x6e\xf8" + "\x50\x38\x1f\x71\x49\xb6\x57\xd6" + "\x8f\xcb\x8d\x6b\xe3\xa6\x29\x90" + "\xfe\x2a\x62\x82\xae\x6d\x8b\xf6" + "\xad\x1e\x9e\x20\x5f\x38\xbe\x04" + "\xda\x10\x8e\xed\xa2\xa4\x87\xab" + "\xda\x6b\xb4\x0c\x75\xba\xd3\x7c" + "\xc9\xac\x42\x31\x95\x7c\xc9\x04" + "\xeb\xd5\x6e\x32\x69\x8a\xdb\xa6" + "\x15\xd7\x3f\x4f\x2f\x66\x69\x03" + "\x9c\x1f\x54\x0f\xde\x1f\xf3\x65" + "\x4c\x96\x12\xed\x7c\x92\x03\x01" + "\x6f\xbc\x35\x93\xac\xf1\x27\xf1" + "\xb4\x96\x82\x5a\x5f\xb0\xa0\x50" + "\x89\xa4\x8e\x66\x44\x85\xcc\xfd" + "\x33\x14\x70\xe3\x96\xb2\xc3\xd3" + "\xbb\x54\x5a\x1a\xf9\x74\xa2\xc5" + "\x2d\x64\x75\xdd\xb4\x54\xe6\x74" + "\x8c\xd3\x9d\x9e\x86\xab\x51\x53" + "\xb7\x93\x3e\x6f\xd0\x4e\x2c\x40" + "\xf6\xa8\x2e\x3e\x9d\xf4\x66\xa5" + "\x76\x12\x73\x44\x1a\x56\xd7\x72" + "\x88\xcd\x21\x8c\x4c\x0f\xfe\xda" + "\x95\xe0\x3a\xa6\xa5\x84\x46\xcd" + "\xd5\x3e\x9d\x3a\xe2\x67\xe6\x60" + "\x1a\xe2\x70\x85\x58\xc2\x1b\x09" + "\xe1\xd7\x2c\xca\xad\xa8\x8f\xf9" + "\xac\xb3\x0e\xdb\xca\x2e\xe2\xb8" + "\x51\x71\xd9\x3c\x6c\xf1\x56\xf8" + "\xea\x9c\xf1\xfb\x0c\xe6\xb7\x10" + "\x1c\xf8\xa9\x7c\xe8\x53\x35\xc1" + "\x90\x3e\x76\x4a\x74\xa4\x21\x2c" + "\xf6\x2c\x4e\x0f\x94\x3a\x88\x2e" + "\x41\x09\x6a\x33\x7d\xf6\xdd\x3f" + "\x8d\x23\x31\x74\x84\xeb\x88\x6e" + "\xcc\xb9\xbc\x22\x83\x19\x07\x22" + "\xa5\x2d\xdf\xa5\xf3\x80\x85\x78" + "\x84\x39\x6a\x6d\x6a\x99\x4f\xa5" + "\x15\xfe\x46\xb0\xe4\x6c\xa5\x41" + "\x3c\xce\x8f\x42\x60\x71\xa7\x75" + "\x08\x40\x65\x8a\x82\xbf\xf5\x43" + "\x71\x96\xa9\x4d\x44\x8a\x20\xbe" + "\xfa\x4d\xbb\xc0\x7d\x31\x96\x65" + "\xe7\x75\xe5\x3e\xfd\x92\x3b\xc9" + "\x55\xbb\x16\x7e\xf7\xc2\x8c\xa4" + "\x40\x1d\xe5\xef\x0e\xdf\xe4\x9a" + "\x62\x73\x65\xfd\x46\x63\x25\x3d" + "\x2b\xaf\xe5\x64\xfe\xa5\x5c\xcf" + "\x24\xf3\xb4\xac\x64\xba\xdf\x4b" + "\xc6\x96\x7d\x81\x2d\x8d\x97\xf7" + "\xc5\x68\x77\x84\x32\x2b\xcc\x85" + "\x74\x96\xf0\x12\x77\x61\xb9\xeb" + "\x71\xaa\x82\xcb\x1c\xdb\x89\xc8" + "\xc6\xb5\xe3\x5c\x7d\x39\x07\x24" + "\xda\x39\x87\x45\xc0\x2b\xbb\x01" + "\xac\xbc\x2a\x5c\x7f\xfc\xe8\xce" + "\x6d\x9c\x6f\xed\xd3\xc1\xa1\xd6" + "\xc5\x55\xa9\x66\x2f\xe1\xc8\x32" + "\xa6\x5d\xa4\x3a\x98\x73\xe8\x45" + "\xa4\xc7\xa8\xb4\xf6\x13\x03\xf6" + "\xe9\x2e\xc4\x29\x0f\x84\xdb\xc4" + "\x21\xc4\xc2\x75\x67\x89\x37\x0a", + .ilen = 512, + .result = "\x1a\x1d\xa9\x30\xad\xf9\x2f\x9b" + "\xb6\x1d\xae\xef\xf0\x2f\xf8\x5a" + "\x39\x3c\xbf\x2a\xb2\x45\xb2\x23" + "\x1b\x63\x3c\xcf\xaa\xbe\xcf\x4e" + "\xfa\xe8\x29\xc2\x20\x68\x2b\x3c" + "\x2e\x8b\xf7\x6e\x25\xbd\xe3\x3d" + "\x66\x27\xd6\xaf\xd6\x64\x3e\xe3" + "\xe8\x58\x46\x97\x39\x51\x07\xde" + "\xcb\x37\xbc\xa9\xc0\x5f\x75\xc3" + "\x0e\x84\x23\x1d\x16\xd4\x1c\x59" + "\x9c\x1a\x02\x55\xab\x3a\x97\x1d" + "\xdf\xdd\xc7\x06\x51\xd7\x70\xae" + "\x23\xc6\x8c\xf5\x1e\xa0\xe5\x82" + "\xb8\xb2\xbf\x04\xa0\x32\x8e\x68" + "\xeb\xaf\x6e\x2d\x94\x22\x2f\xce" + "\x4c\xb5\x59\xe2\xa2\x2f\xa0\x98" + "\x1a\x97\xc6\xd4\xb5\x00\x59\xf2" + "\x84\x14\x72\xb1\x9a\x6e\xa3\x7f" + "\xea\x20\xe7\xcb\x65\x77\x3a\xdf" + "\xc8\x97\x67\x15\xc2\x2a\x27\xcc" + "\x18\x55\xa1\x24\x0b\x24\x24\xaf" + "\x5b\xec\x68\xb8\xc8\xf5\xba\x63" + "\xff\xed\x89\xce\xd5\x3d\x88\xf3" + "\x25\xef\x05\x7c\x3a\xef\xeb\xd8" + "\x7a\x32\x0d\xd1\x1e\x58\x59\x99" + "\x90\x25\xb5\x26\xb0\xe3\x2b\x6c" + "\x4c\xa9\x8b\x84\x4f\x5e\x01\x50" + "\x41\x30\x58\xc5\x62\x74\x52\x1d" + "\x45\x24\x6a\x42\x64\x4f\x97\x1c" + "\xa8\x66\xb5\x6d\x79\xd4\x0d\x48" + "\xc5\x5f\xf3\x90\x32\xdd\xdd\xe1" + "\xe4\xa9\x9f\xfc\xc3\x52\x5a\x46" + "\xe4\x81\x84\x95\x36\x59\x7a\x6b" + "\xaa\xb3\x60\xad\xce\x9f\x9f\x28" + "\xe0\x01\x75\x22\xc4\x4e\xa9\x62" + "\x5c\x62\x0d\x00\xcb\x13\xe8\x43" + "\x72\xd4\x2d\x53\x46\xb5\xd1\x16" + "\x22\x18\xdf\x34\x33\xf5\xd6\x1c" + "\xb8\x79\x78\x97\x94\xff\x72\x13" + "\x4c\x27\xfc\xcb\xbf\x01\x53\xa6" + "\xb4\x50\x6e\xde\xdf\xb5\x43\xa4" + "\x59\xdf\x52\xf9\x7c\xe0\x11\x6f" + "\x2d\x14\x8e\x24\x61\x2c\xe1\x17" + "\xcc\xce\x51\x0c\x19\x8a\x82\x30" + "\x94\xd5\x3d\x6a\x53\x06\x5e\xbd" + "\xb7\xeb\xfa\xfd\x27\x51\xde\x85" + "\x1e\x86\x53\x11\x53\x94\x00\xee" + "\x2b\x8c\x08\x2a\xbf\xdd\xae\x11" + "\xcb\x1e\xa2\x07\x9a\x80\xcf\x62" + "\x9b\x09\xdc\x95\x3c\x96\x8e\xb1" + "\x09\xbd\xe4\xeb\xdb\xca\x70\x7a" + "\x9e\xfa\x31\x18\x45\x3c\x21\x33" + "\xb0\xb3\x2b\xea\xf3\x71\x2d\xe1" + "\x03\xad\x1b\x48\xd4\x67\x27\xf0" + "\x62\xe4\x3d\xfb\x9b\x08\x76\xe7" + "\xdd\x2b\x01\x39\x04\x5a\x58\x7a" + "\xf7\x11\x90\xec\xbd\x51\x5c\x32" + "\x6b\xd7\x35\x39\x02\x6b\xf2\xa6" + "\xd0\x0d\x07\xe1\x06\xc4\x5b\x7d" + "\xe4\x6a\xd7\xee\x15\x1f\x83\xb4" + "\xa3\xa7\x5e\xc3\x90\xb7\xef\xd3" + "\xb7\x4f\xf8\x92\x4c\xb7\x3c\x29" + "\xcd\x7e\x2b\x5d\x43\xea\x42\xe7" + "\x74\x3f\x7d\x58\x88\x75\xde\x3e", + .rlen = 512, + } +}; + +static struct cipher_testvec aes_lrw_dec_tv_template[] = { + /* from http://grouper.ieee.org/groups/1619/email/pdf00017.pdf */ + /* same as enc vectors with input and result reversed */ + { /* LRW-32-AES 1 */ + .key = "\x45\x62\xac\x25\xf8\x28\x17\x6d" + "\x4c\x26\x84\x14\xb5\x68\x01\x85" + "\x25\x8e\x2a\x05\xe7\x3e\x9d\x03" + "\xee\x5a\x83\x0c\xcc\x09\x4c\x87", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\xf1\xb2\x73\xcd\x65\xa3\xdf\x5f" + "\xe9\x5d\x48\x92\x54\x63\x4e\xb8", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { /* LRW-32-AES 2 */ + .key = "\x59\x70\x47\x14\xf5\x57\x47\x8c" + "\xd7\x79\xe8\x0f\x54\x88\x79\x44" + "\x0d\x48\xf0\xb7\xb1\x5a\x53\xea" + "\x1c\xaa\x6b\x29\xc2\xca\xfb\xaf", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x02", + .input = "\x00\xc8\x2b\xae\x95\xbb\xcd\xe5" + "\x27\x4f\x07\x69\xb2\x60\xe1\x36", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { /* LRW-32-AES 3 */ + .key = "\xd8\x2a\x91\x34\xb2\x6a\x56\x50" + "\x30\xfe\x69\xe2\x37\x7f\x98\x47" + "\xcd\xf9\x0b\x16\x0c\x64\x8f\xb6" + "\xb0\x0d\x0d\x1b\xae\x85\x87\x1f", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x02\x00\x00\x00\x00", + .input = "\x76\x32\x21\x83\xed\x8f\xf1\x82" + "\xf9\x59\x62\x03\x69\x0e\x5e\x01", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { /* LRW-32-AES 4 */ + .key = "\x0f\x6a\xef\xf8\xd3\xd2\xbb\x15" + "\x25\x83\xf7\x3c\x1f\x01\x28\x74" + "\xca\xc6\xbc\x35\x4d\x4a\x65\x54" + "\x90\xae\x61\xcf\x7b\xae\xbd\xcc" + "\xad\xe4\x94\xc5\x4a\x29\xae\x70", + .klen = 40, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\x9c\x0f\x15\x2f\x55\xa2\xd8\xf0" + "\xd6\x7b\x8f\x9e\x28\x22\xbc\x41", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { /* LRW-32-AES 5 */ + .key = "\x8a\xd4\xee\x10\x2f\xbd\x81\xff" + "\xf8\x86\xce\xac\x93\xc5\xad\xc6" + "\xa0\x19\x07\xc0\x9d\xf7\xbb\xdd" + "\x52\x13\xb2\xb7\xf0\xff\x11\xd8" + "\xd6\x08\xd0\xcd\x2e\xb1\x17\x6f", + .klen = 40, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x02\x00\x00\x00\x00", + .input = "\xd4\x27\x6a\x7f\x14\x91\x3d\x65" + "\xc8\x60\x48\x02\x87\xe3\x34\x06", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { /* LRW-32-AES 6 */ + .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" + "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" + "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" + "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" + "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" + "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", + .klen = 48, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\xbd\x06\xb8\xe1\xdb\x98\x89\x9e" + "\xc4\x98\xe4\x91\xcf\x1c\x70\x2b", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { /* LRW-32-AES 7 */ + .key = "\xfb\x76\x15\xb2\x3d\x80\x89\x1d" + "\xd4\x70\x98\x0b\xc7\x95\x84\xc8" + "\xb2\xfb\x64\xce\x60\x97\x87\x8d" + "\x17\xfc\xe4\x5a\x49\xe8\x30\xb7" + "\x6e\x78\x17\xe7\x2d\x5e\x12\xd4" + "\x60\x64\x04\x7a\xf1\x2f\x9e\x0c", + .klen = 48, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x02\x00\x00\x00\x00", + .input = "\x5b\x90\x8e\xc1\xab\xdd\x67\x5f" + "\x3d\x69\x8a\x95\x53\xc8\x9c\xe5", + .ilen = 16, + .result = "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x41\x42\x43\x44\x45\x46", + .rlen = 16, + }, { +/* http://www.mail-archive.com/stds-p1619@listserv.ieee.org/msg00173.html */ + .key = "\xf8\xd4\x76\xff\xd6\x46\xee\x6c" + "\x23\x84\xcb\x1c\x77\xd6\x19\x5d" + "\xfe\xf1\xa9\xf3\x7b\xbc\x8d\x21" + "\xa7\x9c\x21\xf8\xcb\x90\x02\x89" + "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1" + "\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e", + .klen = 48, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01", + .input = "\x1a\x1d\xa9\x30\xad\xf9\x2f\x9b" + "\xb6\x1d\xae\xef\xf0\x2f\xf8\x5a" + "\x39\x3c\xbf\x2a\xb2\x45\xb2\x23" + "\x1b\x63\x3c\xcf\xaa\xbe\xcf\x4e" + "\xfa\xe8\x29\xc2\x20\x68\x2b\x3c" + "\x2e\x8b\xf7\x6e\x25\xbd\xe3\x3d" + "\x66\x27\xd6\xaf\xd6\x64\x3e\xe3" + "\xe8\x58\x46\x97\x39\x51\x07\xde" + "\xcb\x37\xbc\xa9\xc0\x5f\x75\xc3" + "\x0e\x84\x23\x1d\x16\xd4\x1c\x59" + "\x9c\x1a\x02\x55\xab\x3a\x97\x1d" + "\xdf\xdd\xc7\x06\x51\xd7\x70\xae" + "\x23\xc6\x8c\xf5\x1e\xa0\xe5\x82" + "\xb8\xb2\xbf\x04\xa0\x32\x8e\x68" + "\xeb\xaf\x6e\x2d\x94\x22\x2f\xce" + "\x4c\xb5\x59\xe2\xa2\x2f\xa0\x98" + "\x1a\x97\xc6\xd4\xb5\x00\x59\xf2" + "\x84\x14\x72\xb1\x9a\x6e\xa3\x7f" + "\xea\x20\xe7\xcb\x65\x77\x3a\xdf" + "\xc8\x97\x67\x15\xc2\x2a\x27\xcc" + "\x18\x55\xa1\x24\x0b\x24\x24\xaf" + "\x5b\xec\x68\xb8\xc8\xf5\xba\x63" + "\xff\xed\x89\xce\xd5\x3d\x88\xf3" + "\x25\xef\x05\x7c\x3a\xef\xeb\xd8" + "\x7a\x32\x0d\xd1\x1e\x58\x59\x99" + "\x90\x25\xb5\x26\xb0\xe3\x2b\x6c" + "\x4c\xa9\x8b\x84\x4f\x5e\x01\x50" + "\x41\x30\x58\xc5\x62\x74\x52\x1d" + "\x45\x24\x6a\x42\x64\x4f\x97\x1c" + "\xa8\x66\xb5\x6d\x79\xd4\x0d\x48" + "\xc5\x5f\xf3\x90\x32\xdd\xdd\xe1" + "\xe4\xa9\x9f\xfc\xc3\x52\x5a\x46" + "\xe4\x81\x84\x95\x36\x59\x7a\x6b" + "\xaa\xb3\x60\xad\xce\x9f\x9f\x28" + "\xe0\x01\x75\x22\xc4\x4e\xa9\x62" + "\x5c\x62\x0d\x00\xcb\x13\xe8\x43" + "\x72\xd4\x2d\x53\x46\xb5\xd1\x16" + "\x22\x18\xdf\x34\x33\xf5\xd6\x1c" + "\xb8\x79\x78\x97\x94\xff\x72\x13" + "\x4c\x27\xfc\xcb\xbf\x01\x53\xa6" + "\xb4\x50\x6e\xde\xdf\xb5\x43\xa4" + "\x59\xdf\x52\xf9\x7c\xe0\x11\x6f" + "\x2d\x14\x8e\x24\x61\x2c\xe1\x17" + "\xcc\xce\x51\x0c\x19\x8a\x82\x30" + "\x94\xd5\x3d\x6a\x53\x06\x5e\xbd" + "\xb7\xeb\xfa\xfd\x27\x51\xde\x85" + "\x1e\x86\x53\x11\x53\x94\x00\xee" + "\x2b\x8c\x08\x2a\xbf\xdd\xae\x11" + "\xcb\x1e\xa2\x07\x9a\x80\xcf\x62" + "\x9b\x09\xdc\x95\x3c\x96\x8e\xb1" + "\x09\xbd\xe4\xeb\xdb\xca\x70\x7a" + "\x9e\xfa\x31\x18\x45\x3c\x21\x33" + "\xb0\xb3\x2b\xea\xf3\x71\x2d\xe1" + "\x03\xad\x1b\x48\xd4\x67\x27\xf0" + "\x62\xe4\x3d\xfb\x9b\x08\x76\xe7" + "\xdd\x2b\x01\x39\x04\x5a\x58\x7a" + "\xf7\x11\x90\xec\xbd\x51\x5c\x32" + "\x6b\xd7\x35\x39\x02\x6b\xf2\xa6" + "\xd0\x0d\x07\xe1\x06\xc4\x5b\x7d" + "\xe4\x6a\xd7\xee\x15\x1f\x83\xb4" + "\xa3\xa7\x5e\xc3\x90\xb7\xef\xd3" + "\xb7\x4f\xf8\x92\x4c\xb7\x3c\x29" + "\xcd\x7e\x2b\x5d\x43\xea\x42\xe7" + "\x74\x3f\x7d\x58\x88\x75\xde\x3e", + .ilen = 512, + .result = "\x05\x11\xb7\x18\xab\xc6\x2d\xac" + "\x70\x5d\xf6\x22\x94\xcd\xe5\x6c" + "\x17\x6b\xf6\x1c\xf0\xf3\x6e\xf8" + "\x50\x38\x1f\x71\x49\xb6\x57\xd6" + "\x8f\xcb\x8d\x6b\xe3\xa6\x29\x90" + "\xfe\x2a\x62\x82\xae\x6d\x8b\xf6" + "\xad\x1e\x9e\x20\x5f\x38\xbe\x04" + "\xda\x10\x8e\xed\xa2\xa4\x87\xab" + "\xda\x6b\xb4\x0c\x75\xba\xd3\x7c" + "\xc9\xac\x42\x31\x95\x7c\xc9\x04" + "\xeb\xd5\x6e\x32\x69\x8a\xdb\xa6" + "\x15\xd7\x3f\x4f\x2f\x66\x69\x03" + "\x9c\x1f\x54\x0f\xde\x1f\xf3\x65" + "\x4c\x96\x12\xed\x7c\x92\x03\x01" + "\x6f\xbc\x35\x93\xac\xf1\x27\xf1" + "\xb4\x96\x82\x5a\x5f\xb0\xa0\x50" + "\x89\xa4\x8e\x66\x44\x85\xcc\xfd" + "\x33\x14\x70\xe3\x96\xb2\xc3\xd3" + "\xbb\x54\x5a\x1a\xf9\x74\xa2\xc5" + "\x2d\x64\x75\xdd\xb4\x54\xe6\x74" + "\x8c\xd3\x9d\x9e\x86\xab\x51\x53" + "\xb7\x93\x3e\x6f\xd0\x4e\x2c\x40" + "\xf6\xa8\x2e\x3e\x9d\xf4\x66\xa5" + "\x76\x12\x73\x44\x1a\x56\xd7\x72" + "\x88\xcd\x21\x8c\x4c\x0f\xfe\xda" + "\x95\xe0\x3a\xa6\xa5\x84\x46\xcd" + "\xd5\x3e\x9d\x3a\xe2\x67\xe6\x60" + "\x1a\xe2\x70\x85\x58\xc2\x1b\x09" + "\xe1\xd7\x2c\xca\xad\xa8\x8f\xf9" + "\xac\xb3\x0e\xdb\xca\x2e\xe2\xb8" + "\x51\x71\xd9\x3c\x6c\xf1\x56\xf8" + "\xea\x9c\xf1\xfb\x0c\xe6\xb7\x10" + "\x1c\xf8\xa9\x7c\xe8\x53\x35\xc1" + "\x90\x3e\x76\x4a\x74\xa4\x21\x2c" + "\xf6\x2c\x4e\x0f\x94\x3a\x88\x2e" + "\x41\x09\x6a\x33\x7d\xf6\xdd\x3f" + "\x8d\x23\x31\x74\x84\xeb\x88\x6e" + "\xcc\xb9\xbc\x22\x83\x19\x07\x22" + "\xa5\x2d\xdf\xa5\xf3\x80\x85\x78" + "\x84\x39\x6a\x6d\x6a\x99\x4f\xa5" + "\x15\xfe\x46\xb0\xe4\x6c\xa5\x41" + "\x3c\xce\x8f\x42\x60\x71\xa7\x75" + "\x08\x40\x65\x8a\x82\xbf\xf5\x43" + "\x71\x96\xa9\x4d\x44\x8a\x20\xbe" + "\xfa\x4d\xbb\xc0\x7d\x31\x96\x65" + "\xe7\x75\xe5\x3e\xfd\x92\x3b\xc9" + "\x55\xbb\x16\x7e\xf7\xc2\x8c\xa4" + "\x40\x1d\xe5\xef\x0e\xdf\xe4\x9a" + "\x62\x73\x65\xfd\x46\x63\x25\x3d" + "\x2b\xaf\xe5\x64\xfe\xa5\x5c\xcf" + "\x24\xf3\xb4\xac\x64\xba\xdf\x4b" + "\xc6\x96\x7d\x81\x2d\x8d\x97\xf7" + "\xc5\x68\x77\x84\x32\x2b\xcc\x85" + "\x74\x96\xf0\x12\x77\x61\xb9\xeb" + "\x71\xaa\x82\xcb\x1c\xdb\x89\xc8" + "\xc6\xb5\xe3\x5c\x7d\x39\x07\x24" + "\xda\x39\x87\x45\xc0\x2b\xbb\x01" + "\xac\xbc\x2a\x5c\x7f\xfc\xe8\xce" + "\x6d\x9c\x6f\xed\xd3\xc1\xa1\xd6" + "\xc5\x55\xa9\x66\x2f\xe1\xc8\x32" + "\xa6\x5d\xa4\x3a\x98\x73\xe8\x45" + "\xa4\xc7\xa8\xb4\xf6\x13\x03\xf6" + "\xe9\x2e\xc4\x29\x0f\x84\xdb\xc4" + "\x21\xc4\xc2\x75\x67\x89\x37\x0a", + .rlen = 512, + } +}; + +static struct cipher_testvec aes_xts_enc_tv_template[] = { + /* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf */ + { /* XTS-AES 1 */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 32, + .result = "\x91\x7c\xf6\x9e\xbd\x68\xb2\xec" + "\x9b\x9f\xe9\xa3\xea\xdd\xa6\x92" + "\xcd\x43\xd2\xf5\x95\x98\xed\x85" + "\x8c\x02\xc2\x65\x2f\xbf\x92\x2e", + .rlen = 32, + }, { /* XTS-AES 2 */ + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\xc4\x54\x18\x5e\x6a\x16\x93\x6e" + "\x39\x33\x40\x38\xac\xef\x83\x8b" + "\xfb\x18\x6f\xff\x74\x80\xad\xc4" + "\x28\x93\x82\xec\xd6\xd3\x94\xf0", + .rlen = 32, + }, { /* XTS-AES 3 */ + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\xaf\x85\x33\x6b\x59\x7a\xfc\x1a" + "\x90\x0b\x2e\xb2\x1e\xc9\x49\xd2" + "\x92\xdf\x4c\x04\x7e\x0b\x21\x53" + "\x21\x86\xa5\x97\x1a\x22\x7a\x89", + .rlen = 32, + }, { /* XTS-AES 4 */ + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\x27\xa7\x47\x9b\xef\xa1\xd4\x76" + "\x48\x9f\x30\x8c\xd4\xcf\xa6\xe2" + "\xa9\x6e\x4b\xbe\x32\x08\xff\x25" + "\x28\x7d\xd3\x81\x96\x16\xe8\x9c" + "\xc7\x8c\xf7\xf5\xe5\x43\x44\x5f" + "\x83\x33\xd8\xfa\x7f\x56\x00\x00" + "\x05\x27\x9f\xa5\xd8\xb5\xe4\xad" + "\x40\xe7\x36\xdd\xb4\xd3\x54\x12" + "\x32\x80\x63\xfd\x2a\xab\x53\xe5" + "\xea\x1e\x0a\x9f\x33\x25\x00\xa5" + "\xdf\x94\x87\xd0\x7a\x5c\x92\xcc" + "\x51\x2c\x88\x66\xc7\xe8\x60\xce" + "\x93\xfd\xf1\x66\xa2\x49\x12\xb4" + "\x22\x97\x61\x46\xae\x20\xce\x84" + "\x6b\xb7\xdc\x9b\xa9\x4a\x76\x7a" + "\xae\xf2\x0c\x0d\x61\xad\x02\x65" + "\x5e\xa9\x2d\xc4\xc4\xe4\x1a\x89" + "\x52\xc6\x51\xd3\x31\x74\xbe\x51" + "\xa1\x0c\x42\x11\x10\xe6\xd8\x15" + "\x88\xed\xe8\x21\x03\xa2\x52\xd8" + "\xa7\x50\xe8\x76\x8d\xef\xff\xed" + "\x91\x22\x81\x0a\xae\xb9\x9f\x91" + "\x72\xaf\x82\xb6\x04\xdc\x4b\x8e" + "\x51\xbc\xb0\x82\x35\xa6\xf4\x34" + "\x13\x32\xe4\xca\x60\x48\x2a\x4b" + "\xa1\xa0\x3b\x3e\x65\x00\x8f\xc5" + "\xda\x76\xb7\x0b\xf1\x69\x0d\xb4" + "\xea\xe2\x9c\x5f\x1b\xad\xd0\x3c" + "\x5c\xcf\x2a\x55\xd7\x05\xdd\xcd" + "\x86\xd4\x49\x51\x1c\xeb\x7e\xc3" + "\x0b\xf1\x2b\x1f\xa3\x5b\x91\x3f" + "\x9f\x74\x7a\x8a\xfd\x1b\x13\x0e" + "\x94\xbf\xf9\x4e\xff\xd0\x1a\x91" + "\x73\x5c\xa1\x72\x6a\xcd\x0b\x19" + "\x7c\x4e\x5b\x03\x39\x36\x97\xe1" + "\x26\x82\x6f\xb6\xbb\xde\x8e\xcc" + "\x1e\x08\x29\x85\x16\xe2\xc9\xed" + "\x03\xff\x3c\x1b\x78\x60\xf6\xde" + "\x76\xd4\xce\xcd\x94\xc8\x11\x98" + "\x55\xef\x52\x97\xca\x67\xe9\xf3" + "\xe7\xff\x72\xb1\xe9\x97\x85\xca" + "\x0a\x7e\x77\x20\xc5\xb3\x6d\xc6" + "\xd7\x2c\xac\x95\x74\xc8\xcb\xbc" + "\x2f\x80\x1e\x23\xe5\x6f\xd3\x44" + "\xb0\x7f\x22\x15\x4b\xeb\xa0\xf0" + "\x8c\xe8\x89\x1e\x64\x3e\xd9\x95" + "\xc9\x4d\x9a\x69\xc9\xf1\xb5\xf4" + "\x99\x02\x7a\x78\x57\x2a\xee\xbd" + "\x74\xd2\x0c\xc3\x98\x81\xc2\x13" + "\xee\x77\x0b\x10\x10\xe4\xbe\xa7" + "\x18\x84\x69\x77\xae\x11\x9f\x7a" + "\x02\x3a\xb5\x8c\xca\x0a\xd7\x52" + "\xaf\xe6\x56\xbb\x3c\x17\x25\x6a" + "\x9f\x6e\x9b\xf1\x9f\xdd\x5a\x38" + "\xfc\x82\xbb\xe8\x72\xc5\x53\x9e" + "\xdb\x60\x9e\xf4\xf7\x9c\x20\x3e" + "\xbb\x14\x0f\x2e\x58\x3c\xb2\xad" + "\x15\xb4\xaa\x5b\x65\x50\x16\xa8" + "\x44\x92\x77\xdb\xd4\x77\xef\x2c" + "\x8d\x6c\x01\x7d\xb7\x38\xb1\x8d" + "\xeb\x4a\x42\x7d\x19\x23\xce\x3f" + "\xf2\x62\x73\x57\x79\xa4\x18\xf2" + "\x0a\x28\x2d\xf9\x20\x14\x7b\xea" + "\xbe\x42\x1e\xe5\x31\x9d\x05\x68", + .rlen = 512, + } +}; + +static struct cipher_testvec aes_xts_dec_tv_template[] = { + /* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf */ + { /* XTS-AES 1 */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x91\x7c\xf6\x9e\xbd\x68\xb2\xec" + "\x9b\x9f\xe9\xa3\xea\xdd\xa6\x92" + "\xcd\x43\xd2\xf5\x95\x98\xed\x85" + "\x8c\x02\xc2\x65\x2f\xbf\x92\x2e", + .ilen = 32, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 32, + }, { /* XTS-AES 2 */ + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xc4\x54\x18\x5e\x6a\x16\x93\x6e" + "\x39\x33\x40\x38\xac\xef\x83\x8b" + "\xfb\x18\x6f\xff\x74\x80\xad\xc4" + "\x28\x93\x82\xec\xd6\xd3\x94\xf0", + .ilen = 32, + .result = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .rlen = 32, + }, { /* XTS-AES 3 */ + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xaf\x85\x33\x6b\x59\x7a\xfc\x1a" + "\x90\x0b\x2e\xb2\x1e\xc9\x49\xd2" + "\x92\xdf\x4c\x04\x7e\x0b\x21\x53" + "\x21\x86\xa5\x97\x1a\x22\x7a\x89", + .ilen = 32, + .result = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .rlen = 32, + }, { /* XTS-AES 4 */ + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x27\xa7\x47\x9b\xef\xa1\xd4\x76" + "\x48\x9f\x30\x8c\xd4\xcf\xa6\xe2" + "\xa9\x6e\x4b\xbe\x32\x08\xff\x25" + "\x28\x7d\xd3\x81\x96\x16\xe8\x9c" + "\xc7\x8c\xf7\xf5\xe5\x43\x44\x5f" + "\x83\x33\xd8\xfa\x7f\x56\x00\x00" + "\x05\x27\x9f\xa5\xd8\xb5\xe4\xad" + "\x40\xe7\x36\xdd\xb4\xd3\x54\x12" + "\x32\x80\x63\xfd\x2a\xab\x53\xe5" + "\xea\x1e\x0a\x9f\x33\x25\x00\xa5" + "\xdf\x94\x87\xd0\x7a\x5c\x92\xcc" + "\x51\x2c\x88\x66\xc7\xe8\x60\xce" + "\x93\xfd\xf1\x66\xa2\x49\x12\xb4" + "\x22\x97\x61\x46\xae\x20\xce\x84" + "\x6b\xb7\xdc\x9b\xa9\x4a\x76\x7a" + "\xae\xf2\x0c\x0d\x61\xad\x02\x65" + "\x5e\xa9\x2d\xc4\xc4\xe4\x1a\x89" + "\x52\xc6\x51\xd3\x31\x74\xbe\x51" + "\xa1\x0c\x42\x11\x10\xe6\xd8\x15" + "\x88\xed\xe8\x21\x03\xa2\x52\xd8" + "\xa7\x50\xe8\x76\x8d\xef\xff\xed" + "\x91\x22\x81\x0a\xae\xb9\x9f\x91" + "\x72\xaf\x82\xb6\x04\xdc\x4b\x8e" + "\x51\xbc\xb0\x82\x35\xa6\xf4\x34" + "\x13\x32\xe4\xca\x60\x48\x2a\x4b" + "\xa1\xa0\x3b\x3e\x65\x00\x8f\xc5" + "\xda\x76\xb7\x0b\xf1\x69\x0d\xb4" + "\xea\xe2\x9c\x5f\x1b\xad\xd0\x3c" + "\x5c\xcf\x2a\x55\xd7\x05\xdd\xcd" + "\x86\xd4\x49\x51\x1c\xeb\x7e\xc3" + "\x0b\xf1\x2b\x1f\xa3\x5b\x91\x3f" + "\x9f\x74\x7a\x8a\xfd\x1b\x13\x0e" + "\x94\xbf\xf9\x4e\xff\xd0\x1a\x91" + "\x73\x5c\xa1\x72\x6a\xcd\x0b\x19" + "\x7c\x4e\x5b\x03\x39\x36\x97\xe1" + "\x26\x82\x6f\xb6\xbb\xde\x8e\xcc" + "\x1e\x08\x29\x85\x16\xe2\xc9\xed" + "\x03\xff\x3c\x1b\x78\x60\xf6\xde" + "\x76\xd4\xce\xcd\x94\xc8\x11\x98" + "\x55\xef\x52\x97\xca\x67\xe9\xf3" + "\xe7\xff\x72\xb1\xe9\x97\x85\xca" + "\x0a\x7e\x77\x20\xc5\xb3\x6d\xc6" + "\xd7\x2c\xac\x95\x74\xc8\xcb\xbc" + "\x2f\x80\x1e\x23\xe5\x6f\xd3\x44" + "\xb0\x7f\x22\x15\x4b\xeb\xa0\xf0" + "\x8c\xe8\x89\x1e\x64\x3e\xd9\x95" + "\xc9\x4d\x9a\x69\xc9\xf1\xb5\xf4" + "\x99\x02\x7a\x78\x57\x2a\xee\xbd" + "\x74\xd2\x0c\xc3\x98\x81\xc2\x13" + "\xee\x77\x0b\x10\x10\xe4\xbe\xa7" + "\x18\x84\x69\x77\xae\x11\x9f\x7a" + "\x02\x3a\xb5\x8c\xca\x0a\xd7\x52" + "\xaf\xe6\x56\xbb\x3c\x17\x25\x6a" + "\x9f\x6e\x9b\xf1\x9f\xdd\x5a\x38" + "\xfc\x82\xbb\xe8\x72\xc5\x53\x9e" + "\xdb\x60\x9e\xf4\xf7\x9c\x20\x3e" + "\xbb\x14\x0f\x2e\x58\x3c\xb2\xad" + "\x15\xb4\xaa\x5b\x65\x50\x16\xa8" + "\x44\x92\x77\xdb\xd4\x77\xef\x2c" + "\x8d\x6c\x01\x7d\xb7\x38\xb1\x8d" + "\xeb\x4a\x42\x7d\x19\x23\xce\x3f" + "\xf2\x62\x73\x57\x79\xa4\x18\xf2" + "\x0a\x28\x2d\xf9\x20\x14\x7b\xea" + "\xbe\x42\x1e\xe5\x31\x9d\x05\x68", + .ilen = 512, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .rlen = 512, + } +}; + + +static struct cipher_testvec aes_ctr_enc_tv_template[] = { + { /* From RFC 3686 */ + .key = "\xae\x68\x52\xf8\x12\x10\x67\xcc" + "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" + "\x00\x00\x00\x30", + .klen = 20, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "Single block msg", + .ilen = 16, + .result = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" + "\x2d\x61\x75\xa3\x26\x13\x11\xb8", + .rlen = 16, + }, { + .key = "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" + "\x43\xd6\xce\x1f\x32\x53\x91\x63" + "\x00\x6c\xb6\xdb", + .klen = 20, + .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .ilen = 32, + .result = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" + "\x79\x0d\x41\xee\x8e\xda\xd3\x88" + "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" + "\xfc\xe6\x30\xdf\x91\x41\xbe\x28", + .rlen = 32, + }, { + .key = "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" + "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" + "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" + "\x00\x00\x00\x48", + .klen = 28, + .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", + .input = "Single block msg", + .ilen = 16, + .result = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" + "\x4e\x79\x35\xa0\x03\xcb\xe9\x28", + .rlen = 16, + }, { + .key = "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" + "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" + "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" + "\x00\x96\xb0\x3b", + .klen = 28, + .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .ilen = 32, + .result = "\x45\x32\x43\xfc\x60\x9b\x23\x32" + "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" + "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" + "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00", + .rlen = 32, + }, { + .key = "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" + "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" + "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" + "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" + "\x00\x00\x00\x60", + .klen = 36, + .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", + .input = "Single block msg", + .ilen = 16, + .result = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" + "\x56\x08\x63\xdc\x71\xe3\xe0\xc0", + .rlen = 16, + }, { + .key = "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" + "\x07\x96\x36\x58\x79\xef\xf8\x86" + "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" + "\x4b\x50\x59\x0c\x87\xa2\x38\x84" + "\x00\xfa\xac\x24", + .klen = 36, + .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .ilen = 32, + .result = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" + "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" + "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" + "\x55\x30\x83\x1d\x93\x44\xaf\x1c", + .rlen = 32, + }, { + // generated using Crypto++ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x00\x00\x00\x00", + .klen = 32 + 4, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x03\x06\x09\x0c\x0f\x12\x15" + "\x18\x1b\x1e\x21\x24\x27\x2a\x2d" + "\x30\x33\x36\x39\x3c\x3f\x42\x45" + "\x48\x4b\x4e\x51\x54\x57\x5a\x5d" + "\x60\x63\x66\x69\x6c\x6f\x72\x75" + "\x78\x7b\x7e\x81\x84\x87\x8a\x8d" + "\x90\x93\x96\x99\x9c\x9f\xa2\xa5" + "\xa8\xab\xae\xb1\xb4\xb7\xba\xbd" + "\xc0\xc3\xc6\xc9\xcc\xcf\xd2\xd5" + "\xd8\xdb\xde\xe1\xe4\xe7\xea\xed" + "\xf0\xf3\xf6\xf9\xfc\xff\x02\x05" + "\x08\x0b\x0e\x11\x14\x17\x1a\x1d" + "\x20\x23\x26\x29\x2c\x2f\x32\x35" + "\x38\x3b\x3e\x41\x44\x47\x4a\x4d" + "\x50\x53\x56\x59\x5c\x5f\x62\x65" + "\x68\x6b\x6e\x71\x74\x77\x7a\x7d" + "\x80\x83\x86\x89\x8c\x8f\x92\x95" + "\x98\x9b\x9e\xa1\xa4\xa7\xaa\xad" + "\xb0\xb3\xb6\xb9\xbc\xbf\xc2\xc5" + "\xc8\xcb\xce\xd1\xd4\xd7\xda\xdd" + "\xe0\xe3\xe6\xe9\xec\xef\xf2\xf5" + "\xf8\xfb\xfe\x01\x04\x07\x0a\x0d" + "\x10\x13\x16\x19\x1c\x1f\x22\x25" + "\x28\x2b\x2e\x31\x34\x37\x3a\x3d" + "\x40\x43\x46\x49\x4c\x4f\x52\x55" + "\x58\x5b\x5e\x61\x64\x67\x6a\x6d" + "\x70\x73\x76\x79\x7c\x7f\x82\x85" + "\x88\x8b\x8e\x91\x94\x97\x9a\x9d" + "\xa0\xa3\xa6\xa9\xac\xaf\xb2\xb5" + "\xb8\xbb\xbe\xc1\xc4\xc7\xca\xcd" + "\xd0\xd3\xd6\xd9\xdc\xdf\xe2\xe5" + "\xe8\xeb\xee\xf1\xf4\xf7\xfa\xfd" + "\x00\x05\x0a\x0f\x14\x19\x1e\x23" + "\x28\x2d\x32\x37\x3c\x41\x46\x4b" + "\x50\x55\x5a\x5f\x64\x69\x6e\x73" + "\x78\x7d\x82\x87\x8c\x91\x96\x9b" + "\xa0\xa5\xaa\xaf\xb4\xb9\xbe\xc3" + "\xc8\xcd\xd2\xd7\xdc\xe1\xe6\xeb" + "\xf0\xf5\xfa\xff\x04\x09\x0e\x13" + "\x18\x1d\x22\x27\x2c\x31\x36\x3b" + "\x40\x45\x4a\x4f\x54\x59\x5e\x63" + "\x68\x6d\x72\x77\x7c\x81\x86\x8b" + "\x90\x95\x9a\x9f\xa4\xa9\xae\xb3" + "\xb8\xbd\xc2\xc7\xcc\xd1\xd6\xdb" + "\xe0\xe5\xea\xef\xf4\xf9\xfe\x03" + "\x08\x0d\x12\x17\x1c\x21\x26\x2b" + "\x30\x35\x3a\x3f\x44\x49\x4e\x53" + "\x58\x5d\x62\x67\x6c\x71\x76\x7b" + "\x80\x85\x8a\x8f\x94\x99\x9e\xa3" + "\xa8\xad\xb2\xb7\xbc\xc1\xc6\xcb" + "\xd0\xd5\xda\xdf\xe4\xe9\xee\xf3" + "\xf8\xfd\x02\x07\x0c\x11\x16\x1b" + "\x20\x25\x2a\x2f\x34\x39\x3e\x43" + "\x48\x4d\x52\x57\x5c\x61\x66\x6b" + "\x70\x75\x7a\x7f\x84\x89\x8e\x93" + "\x98\x9d\xa2\xa7\xac\xb1\xb6\xbb" + "\xc0\xc5\xca\xcf\xd4\xd9\xde\xe3" + "\xe8\xed\xf2\xf7\xfc\x01\x06\x0b" + "\x10\x15\x1a\x1f\x24\x29\x2e\x33" + "\x38\x3d\x42\x47\x4c\x51\x56\x5b" + "\x60\x65\x6a\x6f\x74\x79\x7e\x83" + "\x88\x8d\x92\x97\x9c\xa1\xa6\xab" + "\xb0\xb5\xba\xbf\xc4\xc9\xce\xd3" + "\xd8\xdd\xe2\xe7\xec\xf1\xf6\xfb" + "\x00\x07\x0e\x15\x1c\x23\x2a\x31" + "\x38\x3f\x46\x4d\x54\x5b\x62\x69" + "\x70\x77\x7e\x85\x8c\x93\x9a\xa1" + "\xa8\xaf\xb6\xbd\xc4\xcb\xd2\xd9" + "\xe0\xe7\xee\xf5\xfc\x03\x0a\x11" + "\x18\x1f\x26\x2d\x34\x3b\x42\x49" + "\x50\x57\x5e\x65\x6c\x73\x7a\x81" + "\x88\x8f\x96\x9d\xa4\xab\xb2\xb9" + "\xc0\xc7\xce\xd5\xdc\xe3\xea\xf1" + "\xf8\xff\x06\x0d\x14\x1b\x22\x29" + "\x30\x37\x3e\x45\x4c\x53\x5a\x61" + "\x68\x6f\x76\x7d\x84\x8b\x92\x99" + "\xa0\xa7\xae\xb5\xbc\xc3\xca\xd1" + "\xd8\xdf\xe6\xed\xf4\xfb\x02\x09" + "\x10\x17\x1e\x25\x2c\x33\x3a\x41" + "\x48\x4f\x56\x5d\x64\x6b\x72\x79" + "\x80\x87\x8e\x95\x9c\xa3\xaa\xb1" + "\xb8\xbf\xc6\xcd\xd4\xdb\xe2\xe9" + "\xf0\xf7\xfe\x05\x0c\x13\x1a\x21" + "\x28\x2f\x36\x3d\x44\x4b\x52\x59" + "\x60\x67\x6e\x75\x7c\x83\x8a\x91" + "\x98\x9f\xa6\xad\xb4\xbb\xc2\xc9" + "\xd0\xd7\xde\xe5\xec\xf3\xfa\x01" + "\x08\x0f\x16\x1d\x24\x2b\x32\x39" + "\x40\x47\x4e\x55\x5c\x63\x6a\x71" + "\x78\x7f\x86\x8d\x94\x9b\xa2\xa9" + "\xb0\xb7\xbe\xc5\xcc\xd3\xda\xe1" + "\xe8\xef\xf6\xfd\x04\x0b\x12\x19" + "\x20\x27\x2e\x35\x3c\x43\x4a\x51" + "\x58\x5f\x66\x6d\x74\x7b\x82\x89" + "\x90\x97\x9e\xa5\xac\xb3\xba\xc1" + "\xc8\xcf\xd6\xdd\xe4\xeb\xf2\xf9" + "\x00\x09\x12\x1b\x24\x2d\x36\x3f" + "\x48\x51\x5a\x63\x6c\x75\x7e\x87" + "\x90\x99\xa2\xab\xb4\xbd\xc6\xcf" + "\xd8\xe1\xea\xf3\xfc\x05\x0e\x17" + "\x20\x29\x32\x3b\x44\x4d\x56\x5f" + "\x68\x71\x7a\x83\x8c\x95\x9e\xa7" + "\xb0\xb9\xc2\xcb\xd4\xdd\xe6\xef" + "\xf8\x01\x0a\x13\x1c\x25\x2e\x37" + "\x40\x49\x52\x5b\x64\x6d\x76\x7f" + "\x88\x91\x9a\xa3\xac\xb5\xbe\xc7" + "\xd0\xd9\xe2\xeb\xf4\xfd\x06\x0f" + "\x18\x21\x2a\x33\x3c\x45\x4e\x57" + "\x60\x69\x72\x7b\x84\x8d\x96\x9f" + "\xa8\xb1\xba\xc3\xcc\xd5\xde\xe7" + "\xf0\xf9\x02\x0b\x14\x1d\x26\x2f" + "\x38\x41\x4a\x53\x5c\x65\x6e\x77" + "\x80\x89\x92\x9b\xa4\xad\xb6\xbf" + "\xc8\xd1\xda\xe3\xec\xf5\xfe\x07" + "\x10\x19\x22\x2b\x34\x3d\x46\x4f" + "\x58\x61\x6a\x73\x7c\x85\x8e\x97" + "\xa0\xa9\xb2\xbb\xc4\xcd\xd6\xdf" + "\xe8\xf1\xfa\x03\x0c\x15\x1e\x27" + "\x30\x39\x42\x4b\x54\x5d\x66\x6f" + "\x78\x81\x8a\x93\x9c\xa5\xae\xb7" + "\xc0\xc9\xd2\xdb\xe4\xed\xf6\xff" + "\x08\x11\x1a\x23\x2c\x35\x3e\x47" + "\x50\x59\x62\x6b\x74\x7d\x86\x8f" + "\x98\xa1\xaa\xb3\xbc\xc5\xce\xd7" + "\xe0\xe9\xf2\xfb\x04\x0d\x16\x1f" + "\x28\x31\x3a\x43\x4c\x55\x5e\x67" + "\x70\x79\x82\x8b\x94\x9d\xa6\xaf" + "\xb8\xc1\xca\xd3\xdc\xe5\xee\xf7" + "\x00\x0b\x16\x21\x2c\x37\x42\x4d" + "\x58\x63\x6e\x79\x84\x8f\x9a\xa5" + "\xb0\xbb\xc6\xd1\xdc\xe7\xf2\xfd" + "\x08\x13\x1e\x29\x34\x3f\x4a\x55" + "\x60\x6b\x76\x81\x8c\x97\xa2\xad" + "\xb8\xc3\xce\xd9\xe4\xef\xfa\x05" + "\x10\x1b\x26\x31\x3c\x47\x52\x5d" + "\x68\x73\x7e\x89\x94\x9f\xaa\xb5" + "\xc0\xcb\xd6\xe1\xec\xf7\x02\x0d" + "\x18\x23\x2e\x39\x44\x4f\x5a\x65" + "\x70\x7b\x86\x91\x9c\xa7\xb2\xbd" + "\xc8\xd3\xde\xe9\xf4\xff\x0a\x15" + "\x20\x2b\x36\x41\x4c\x57\x62\x6d" + "\x78\x83\x8e\x99\xa4\xaf\xba\xc5" + "\xd0\xdb\xe6\xf1\xfc\x07\x12\x1d" + "\x28\x33\x3e\x49\x54\x5f\x6a\x75" + "\x80\x8b\x96\xa1\xac\xb7\xc2\xcd" + "\xd8\xe3\xee\xf9\x04\x0f\x1a\x25" + "\x30\x3b\x46\x51\x5c\x67\x72\x7d" + "\x88\x93\x9e\xa9\xb4\xbf\xca\xd5" + "\xe0\xeb\xf6\x01\x0c\x17\x22\x2d" + "\x38\x43\x4e\x59\x64\x6f\x7a\x85" + "\x90\x9b\xa6\xb1\xbc\xc7\xd2\xdd" + "\xe8\xf3\xfe\x09\x14\x1f\x2a\x35" + "\x40\x4b\x56\x61\x6c\x77\x82\x8d" + "\x98\xa3\xae\xb9\xc4\xcf\xda\xe5" + "\xf0\xfb\x06\x11\x1c\x27\x32\x3d" + "\x48\x53\x5e\x69\x74\x7f\x8a\x95" + "\xa0\xab\xb6\xc1\xcc\xd7\xe2\xed" + "\xf8\x03\x0e\x19\x24\x2f\x3a\x45" + "\x50\x5b\x66\x71\x7c\x87\x92\x9d" + "\xa8\xb3\xbe\xc9\xd4\xdf\xea\xf5" + "\x00\x0d\x1a\x27\x34\x41\x4e\x5b" + "\x68\x75\x82\x8f\x9c\xa9\xb6\xc3" + "\xd0\xdd\xea\xf7\x04\x11\x1e\x2b" + "\x38\x45\x52\x5f\x6c\x79\x86\x93" + "\xa0\xad\xba\xc7\xd4\xe1\xee\xfb" + "\x08\x15\x22\x2f\x3c\x49\x56\x63" + "\x70\x7d\x8a\x97\xa4\xb1\xbe\xcb" + "\xd8\xe5\xf2\xff\x0c\x19\x26\x33" + "\x40\x4d\x5a\x67\x74\x81\x8e\x9b" + "\xa8\xb5\xc2\xcf\xdc\xe9\xf6\x03" + "\x10\x1d\x2a\x37\x44\x51\x5e\x6b" + "\x78\x85\x92\x9f\xac\xb9\xc6\xd3" + "\xe0\xed\xfa\x07\x14\x21\x2e\x3b" + "\x48\x55\x62\x6f\x7c\x89\x96\xa3" + "\xb0\xbd\xca\xd7\xe4\xf1\xfe\x0b" + "\x18\x25\x32\x3f\x4c\x59\x66\x73" + "\x80\x8d\x9a\xa7\xb4\xc1\xce\xdb" + "\xe8\xf5\x02\x0f\x1c\x29\x36\x43" + "\x50\x5d\x6a\x77\x84\x91\x9e\xab" + "\xb8\xc5\xd2\xdf\xec\xf9\x06\x13" + "\x20\x2d\x3a\x47\x54\x61\x6e\x7b" + "\x88\x95\xa2\xaf\xbc\xc9\xd6\xe3" + "\xf0\xfd\x0a\x17\x24\x31\x3e\x4b" + "\x58\x65\x72\x7f\x8c\x99\xa6\xb3" + "\xc0\xcd\xda\xe7\xf4\x01\x0e\x1b" + "\x28\x35\x42\x4f\x5c\x69\x76\x83" + "\x90\x9d\xaa\xb7\xc4\xd1\xde\xeb" + "\xf8\x05\x12\x1f\x2c\x39\x46\x53" + "\x60\x6d\x7a\x87\x94\xa1\xae\xbb" + "\xc8\xd5\xe2\xef\xfc\x09\x16\x23" + "\x30\x3d\x4a\x57\x64\x71\x7e\x8b" + "\x98\xa5\xb2\xbf\xcc\xd9\xe6\xf3" + "\x00\x0f\x1e\x2d\x3c\x4b\x5a\x69" + "\x78\x87\x96\xa5\xb4\xc3\xd2\xe1" + "\xf0\xff\x0e\x1d\x2c\x3b\x4a\x59" + "\x68\x77\x86\x95\xa4\xb3\xc2\xd1" + "\xe0\xef\xfe\x0d\x1c\x2b\x3a\x49" + "\x58\x67\x76\x85\x94\xa3\xb2\xc1" + "\xd0\xdf\xee\xfd\x0c\x1b\x2a\x39" + "\x48\x57\x66\x75\x84\x93\xa2\xb1" + "\xc0\xcf\xde\xed\xfc\x0b\x1a\x29" + "\x38\x47\x56\x65\x74\x83\x92\xa1" + "\xb0\xbf\xce\xdd\xec\xfb\x0a\x19" + "\x28\x37\x46\x55\x64\x73\x82\x91" + "\xa0\xaf\xbe\xcd\xdc\xeb\xfa\x09" + "\x18\x27\x36\x45\x54\x63\x72\x81" + "\x90\x9f\xae\xbd\xcc\xdb\xea\xf9" + "\x08\x17\x26\x35\x44\x53\x62\x71" + "\x80\x8f\x9e\xad\xbc\xcb\xda\xe9" + "\xf8\x07\x16\x25\x34\x43\x52\x61" + "\x70\x7f\x8e\x9d\xac\xbb\xca\xd9" + "\xe8\xf7\x06\x15\x24\x33\x42\x51" + "\x60\x6f\x7e\x8d\x9c\xab\xba\xc9" + "\xd8\xe7\xf6\x05\x14\x23\x32\x41" + "\x50\x5f\x6e\x7d\x8c\x9b\xaa\xb9" + "\xc8\xd7\xe6\xf5\x04\x13\x22\x31" + "\x40\x4f\x5e\x6d\x7c\x8b\x9a\xa9" + "\xb8\xc7\xd6\xe5\xf4\x03\x12\x21" + "\x30\x3f\x4e\x5d\x6c\x7b\x8a\x99" + "\xa8\xb7\xc6\xd5\xe4\xf3\x02\x11" + "\x20\x2f\x3e\x4d\x5c\x6b\x7a\x89" + "\x98\xa7\xb6\xc5\xd4\xe3\xf2\x01" + "\x10\x1f\x2e\x3d\x4c\x5b\x6a\x79" + "\x88\x97\xa6\xb5\xc4\xd3\xe2\xf1" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff" + "\x10\x21\x32\x43\x54\x65\x76\x87" + "\x98\xa9\xba\xcb\xdc\xed\xfe\x0f" + "\x20\x31\x42\x53\x64\x75\x86\x97" + "\xa8\xb9\xca\xdb\xec\xfd\x0e\x1f" + "\x30\x41\x52\x63\x74\x85\x96\xa7" + "\xb8\xc9\xda\xeb\xfc\x0d\x1e\x2f" + "\x40\x51\x62\x73\x84\x95\xa6\xb7" + "\xc8\xd9\xea\xfb\x0c\x1d\x2e\x3f" + "\x50\x61\x72\x83\x94\xa5\xb6\xc7" + "\xd8\xe9\xfa\x0b\x1c\x2d\x3e\x4f" + "\x60\x71\x82\x93\xa4\xb5\xc6\xd7" + "\xe8\xf9\x0a\x1b\x2c\x3d\x4e\x5f" + "\x70\x81\x92\xa3\xb4\xc5\xd6\xe7" + "\xf8\x09\x1a\x2b\x3c\x4d\x5e\x6f" + "\x80\x91\xa2\xb3\xc4\xd5\xe6\xf7" + "\x08\x19\x2a\x3b\x4c\x5d\x6e\x7f" + "\x90\xa1\xb2\xc3\xd4\xe5\xf6\x07" + "\x18\x29\x3a\x4b\x5c\x6d\x7e\x8f" + "\xa0\xb1\xc2\xd3\xe4\xf5\x06\x17" + "\x28\x39\x4a\x5b\x6c\x7d\x8e\x9f" + "\xb0\xc1\xd2\xe3\xf4\x05\x16\x27" + "\x38\x49\x5a\x6b\x7c\x8d\x9e\xaf" + "\xc0\xd1\xe2\xf3\x04\x15\x26\x37" + "\x48\x59\x6a\x7b\x8c\x9d\xae\xbf" + "\xd0\xe1\xf2\x03\x14\x25\x36\x47" + "\x58\x69\x7a\x8b\x9c\xad\xbe\xcf" + "\xe0\xf1\x02\x13\x24\x35\x46\x57" + "\x68\x79\x8a\x9b\xac\xbd\xce\xdf" + "\xf0\x01\x12\x23\x34\x45\x56\x67" + "\x78\x89\x9a\xab\xbc\xcd\xde\xef" + "\x00\x13\x26\x39\x4c\x5f\x72\x85" + "\x98\xab\xbe\xd1\xe4\xf7\x0a\x1d" + "\x30\x43\x56\x69\x7c\x8f\xa2\xb5" + "\xc8\xdb\xee\x01\x14\x27\x3a\x4d" + "\x60\x73\x86\x99\xac\xbf\xd2\xe5" + "\xf8\x0b\x1e\x31\x44\x57\x6a\x7d" + "\x90\xa3\xb6\xc9\xdc\xef\x02\x15" + "\x28\x3b\x4e\x61\x74\x87\x9a\xad" + "\xc0\xd3\xe6\xf9\x0c\x1f\x32\x45" + "\x58\x6b\x7e\x91\xa4\xb7\xca\xdd" + "\xf0\x03\x16\x29\x3c\x4f\x62\x75" + "\x88\x9b\xae\xc1\xd4\xe7\xfa\x0d" + "\x20\x33\x46\x59\x6c\x7f\x92\xa5" + "\xb8\xcb\xde\xf1\x04\x17\x2a\x3d" + "\x50\x63\x76\x89\x9c\xaf\xc2\xd5" + "\xe8\xfb\x0e\x21\x34\x47\x5a\x6d" + "\x80\x93\xa6\xb9\xcc\xdf\xf2\x05" + "\x18\x2b\x3e\x51\x64\x77\x8a\x9d" + "\xb0\xc3\xd6\xe9\xfc\x0f\x22\x35" + "\x48\x5b\x6e\x81\x94\xa7\xba\xcd" + "\xe0\xf3\x06\x19\x2c\x3f\x52\x65" + "\x78\x8b\x9e\xb1\xc4\xd7\xea\xfd" + "\x10\x23\x36\x49\x5c\x6f\x82\x95" + "\xa8\xbb\xce\xe1\xf4\x07\x1a\x2d" + "\x40\x53\x66\x79\x8c\x9f\xb2\xc5" + "\xd8\xeb\xfe\x11\x24\x37\x4a\x5d" + "\x70\x83\x96\xa9\xbc\xcf\xe2\xf5" + "\x08\x1b\x2e\x41\x54\x67\x7a\x8d" + "\xa0\xb3\xc6\xd9\xec\xff\x12\x25" + "\x38\x4b\x5e\x71\x84\x97\xaa\xbd" + "\xd0\xe3\xf6\x09\x1c\x2f\x42\x55" + "\x68\x7b\x8e\xa1\xb4\xc7\xda\xed" + "\x00\x15\x2a\x3f\x54\x69\x7e\x93" + "\xa8\xbd\xd2\xe7\xfc\x11\x26\x3b" + "\x50\x65\x7a\x8f\xa4\xb9\xce\xe3" + "\xf8\x0d\x22\x37\x4c\x61\x76\x8b" + "\xa0\xb5\xca\xdf\xf4\x09\x1e\x33" + "\x48\x5d\x72\x87\x9c\xb1\xc6\xdb" + "\xf0\x05\x1a\x2f\x44\x59\x6e\x83" + "\x98\xad\xc2\xd7\xec\x01\x16\x2b" + "\x40\x55\x6a\x7f\x94\xa9\xbe\xd3" + "\xe8\xfd\x12\x27\x3c\x51\x66\x7b" + "\x90\xa5\xba\xcf\xe4\xf9\x0e\x23" + "\x38\x4d\x62\x77\x8c\xa1\xb6\xcb" + "\xe0\xf5\x0a\x1f\x34\x49\x5e\x73" + "\x88\x9d\xb2\xc7\xdc\xf1\x06\x1b" + "\x30\x45\x5a\x6f\x84\x99\xae\xc3" + "\xd8\xed\x02\x17\x2c\x41\x56\x6b" + "\x80\x95\xaa\xbf\xd4\xe9\xfe\x13" + "\x28\x3d\x52\x67\x7c\x91\xa6\xbb" + "\xd0\xe5\xfa\x0f\x24\x39\x4e\x63" + "\x78\x8d\xa2\xb7\xcc\xe1\xf6\x0b" + "\x20\x35\x4a\x5f\x74\x89\x9e\xb3" + "\xc8\xdd\xf2\x07\x1c\x31\x46\x5b" + "\x70\x85\x9a\xaf\xc4\xd9\xee\x03" + "\x18\x2d\x42\x57\x6c\x81\x96\xab" + "\xc0\xd5\xea\xff\x14\x29\x3e\x53" + "\x68\x7d\x92\xa7\xbc\xd1\xe6\xfb" + "\x10\x25\x3a\x4f\x64\x79\x8e\xa3" + "\xb8\xcd\xe2\xf7\x0c\x21\x36\x4b" + "\x60\x75\x8a\x9f\xb4\xc9\xde\xf3" + "\x08\x1d\x32\x47\x5c\x71\x86\x9b" + "\xb0\xc5\xda\xef\x04\x19\x2e\x43" + "\x58\x6d\x82\x97\xac\xc1\xd6\xeb" + "\x00\x17\x2e\x45\x5c\x73\x8a\xa1" + "\xb8\xcf\xe6\xfd\x14\x2b\x42\x59" + "\x70\x87\x9e\xb5\xcc\xe3\xfa\x11" + "\x28\x3f\x56\x6d\x84\x9b\xb2\xc9" + "\xe0\xf7\x0e\x25\x3c\x53\x6a\x81" + "\x98\xaf\xc6\xdd\xf4\x0b\x22\x39" + "\x50\x67\x7e\x95\xac\xc3\xda\xf1" + "\x08\x1f\x36\x4d\x64\x7b\x92\xa9" + "\xc0\xd7\xee\x05\x1c\x33\x4a\x61" + "\x78\x8f\xa6\xbd\xd4\xeb\x02\x19" + "\x30\x47\x5e\x75\x8c\xa3\xba\xd1" + "\xe8\xff\x16\x2d\x44\x5b\x72\x89" + "\xa0\xb7\xce\xe5\xfc\x13\x2a\x41" + "\x58\x6f\x86\x9d\xb4\xcb\xe2\xf9" + "\x10\x27\x3e\x55\x6c\x83\x9a\xb1" + "\xc8\xdf\xf6\x0d\x24\x3b\x52\x69" + "\x80\x97\xae\xc5\xdc\xf3\x0a\x21" + "\x38\x4f\x66\x7d\x94\xab\xc2\xd9" + "\xf0\x07\x1e\x35\x4c\x63\x7a\x91" + "\xa8\xbf\xd6\xed\x04\x1b\x32\x49" + "\x60\x77\x8e\xa5\xbc\xd3\xea\x01" + "\x18\x2f\x46\x5d\x74\x8b\xa2\xb9" + "\xd0\xe7\xfe\x15\x2c\x43\x5a\x71" + "\x88\x9f\xb6\xcd\xe4\xfb\x12\x29" + "\x40\x57\x6e\x85\x9c\xb3\xca\xe1" + "\xf8\x0f\x26\x3d\x54\x6b\x82\x99" + "\xb0\xc7\xde\xf5\x0c\x23\x3a\x51" + "\x68\x7f\x96\xad\xc4\xdb\xf2\x09" + "\x20\x37\x4e\x65\x7c\x93\xaa\xc1" + "\xd8\xef\x06\x1d\x34\x4b\x62\x79" + "\x90\xa7\xbe\xd5\xec\x03\x1a\x31" + "\x48\x5f\x76\x8d\xa4\xbb\xd2\xe9" + "\x00\x19\x32\x4b\x64\x7d\x96\xaf" + "\xc8\xe1\xfa\x13\x2c\x45\x5e\x77" + "\x90\xa9\xc2\xdb\xf4\x0d\x26\x3f" + "\x58\x71\x8a\xa3\xbc\xd5\xee\x07" + "\x20\x39\x52\x6b\x84\x9d\xb6\xcf" + "\xe8\x01\x1a\x33\x4c\x65\x7e\x97" + "\xb0\xc9\xe2\xfb\x14\x2d\x46\x5f" + "\x78\x91\xaa\xc3\xdc\xf5\x0e\x27" + "\x40\x59\x72\x8b\xa4\xbd\xd6\xef" + "\x08\x21\x3a\x53\x6c\x85\x9e\xb7" + "\xd0\xe9\x02\x1b\x34\x4d\x66\x7f" + "\x98\xb1\xca\xe3\xfc\x15\x2e\x47" + "\x60\x79\x92\xab\xc4\xdd\xf6\x0f" + "\x28\x41\x5a\x73\x8c\xa5\xbe\xd7" + "\xf0\x09\x22\x3b\x54\x6d\x86\x9f" + "\xb8\xd1\xea\x03\x1c\x35\x4e\x67" + "\x80\x99\xb2\xcb\xe4\xfd\x16\x2f" + "\x48\x61\x7a\x93\xac\xc5\xde\xf7" + "\x10\x29\x42\x5b\x74\x8d\xa6\xbf" + "\xd8\xf1\x0a\x23\x3c\x55\x6e\x87" + "\xa0\xb9\xd2\xeb\x04\x1d\x36\x4f" + "\x68\x81\x9a\xb3\xcc\xe5\xfe\x17" + "\x30\x49\x62\x7b\x94\xad\xc6\xdf" + "\xf8\x11\x2a\x43\x5c\x75\x8e\xa7" + "\xc0\xd9\xf2\x0b\x24\x3d\x56\x6f" + "\x88\xa1\xba\xd3\xec\x05\x1e\x37" + "\x50\x69\x82\x9b\xb4\xcd\xe6\xff" + "\x18\x31\x4a\x63\x7c\x95\xae\xc7" + "\xe0\xf9\x12\x2b\x44\x5d\x76\x8f" + "\xa8\xc1\xda\xf3\x0c\x25\x3e\x57" + "\x70\x89\xa2\xbb\xd4\xed\x06\x1f" + "\x38\x51\x6a\x83\x9c\xb5\xce\xe7" + "\x00\x1b\x36\x51\x6c\x87\xa2\xbd" + "\xd8\xf3\x0e\x29\x44\x5f\x7a\x95" + "\xb0\xcb\xe6\x01\x1c\x37\x52\x6d" + "\x88\xa3\xbe\xd9\xf4\x0f\x2a\x45" + "\x60\x7b\x96\xb1\xcc\xe7\x02\x1d" + "\x38\x53\x6e\x89\xa4\xbf\xda\xf5" + "\x10\x2b\x46\x61\x7c\x97\xb2\xcd" + "\xe8\x03\x1e\x39\x54\x6f\x8a\xa5" + "\xc0\xdb\xf6\x11\x2c\x47\x62\x7d" + "\x98\xb3\xce\xe9\x04\x1f\x3a\x55" + "\x70\x8b\xa6\xc1\xdc\xf7\x12\x2d" + "\x48\x63\x7e\x99\xb4\xcf\xea\x05" + "\x20\x3b\x56\x71\x8c\xa7\xc2\xdd" + "\xf8\x13\x2e\x49\x64\x7f\x9a\xb5" + "\xd0\xeb\x06\x21\x3c\x57\x72\x8d" + "\xa8\xc3\xde\xf9\x14\x2f\x4a\x65" + "\x80\x9b\xb6\xd1\xec\x07\x22\x3d" + "\x58\x73\x8e\xa9\xc4\xdf\xfa\x15" + "\x30\x4b\x66\x81\x9c\xb7\xd2\xed" + "\x08\x23\x3e\x59\x74\x8f\xaa\xc5" + "\xe0\xfb\x16\x31\x4c\x67\x82\x9d" + "\xb8\xd3\xee\x09\x24\x3f\x5a\x75" + "\x90\xab\xc6\xe1\xfc\x17\x32\x4d" + "\x68\x83\x9e\xb9\xd4\xef\x0a\x25" + "\x40\x5b\x76\x91\xac\xc7\xe2\xfd" + "\x18\x33\x4e\x69\x84\x9f\xba\xd5" + "\xf0\x0b\x26\x41\x5c\x77\x92\xad" + "\xc8\xe3\xfe\x19\x34\x4f\x6a\x85" + "\xa0\xbb\xd6\xf1\x0c\x27\x42\x5d" + "\x78\x93\xae\xc9\xe4\xff\x1a\x35" + "\x50\x6b\x86\xa1\xbc\xd7\xf2\x0d" + "\x28\x43\x5e\x79\x94\xaf\xca\xe5" + "\x00\x1d\x3a\x57\x74\x91\xae\xcb" + "\xe8\x05\x22\x3f\x5c\x79\x96\xb3" + "\xd0\xed\x0a\x27\x44\x61\x7e\x9b" + "\xb8\xd5\xf2\x0f\x2c\x49\x66\x83" + "\xa0\xbd\xda\xf7\x14\x31\x4e\x6b" + "\x88\xa5\xc2\xdf\xfc\x19\x36\x53" + "\x70\x8d\xaa\xc7\xe4\x01\x1e\x3b" + "\x58\x75\x92\xaf\xcc\xe9\x06\x23" + "\x40\x5d\x7a\x97\xb4\xd1\xee\x0b" + "\x28\x45\x62\x7f\x9c\xb9\xd6\xf3" + "\x10\x2d\x4a\x67\x84\xa1\xbe\xdb" + "\xf8\x15\x32\x4f\x6c\x89\xa6\xc3" + "\xe0\xfd\x1a\x37\x54\x71\x8e\xab" + "\xc8\xe5\x02\x1f\x3c\x59\x76\x93" + "\xb0\xcd\xea\x07\x24\x41\x5e\x7b" + "\x98\xb5\xd2\xef\x0c\x29\x46\x63" + "\x80\x9d\xba\xd7\xf4\x11\x2e\x4b" + "\x68\x85\xa2\xbf\xdc\xf9\x16\x33" + "\x50\x6d\x8a\xa7\xc4\xe1\xfe\x1b" + "\x38\x55\x72\x8f\xac\xc9\xe6\x03" + "\x20\x3d\x5a\x77\x94\xb1\xce\xeb" + "\x08\x25\x42\x5f\x7c\x99\xb6\xd3" + "\xf0\x0d\x2a\x47\x64\x81\x9e\xbb" + "\xd8\xf5\x12\x2f\x4c\x69\x86\xa3" + "\xc0\xdd\xfa\x17\x34\x51\x6e\x8b" + "\xa8\xc5\xe2\xff\x1c\x39\x56\x73" + "\x90\xad\xca\xe7\x04\x21\x3e\x5b" + "\x78\x95\xb2\xcf\xec\x09\x26\x43" + "\x60\x7d\x9a\xb7\xd4\xf1\x0e\x2b" + "\x48\x65\x82\x9f\xbc\xd9\xf6\x13" + "\x30\x4d\x6a\x87\xa4\xc1\xde\xfb" + "\x18\x35\x52\x6f\x8c\xa9\xc6\xe3" + "\x00\x1f\x3e\x5d\x7c\x9b\xba\xd9" + "\xf8\x17\x36\x55\x74\x93\xb2\xd1" + "\xf0\x0f\x2e\x4d\x6c\x8b\xaa\xc9" + "\xe8\x07\x26\x45\x64\x83\xa2\xc1" + "\xe0\xff\x1e\x3d\x5c\x7b\x9a\xb9" + "\xd8\xf7\x16\x35\x54\x73\x92\xb1" + "\xd0\xef\x0e\x2d\x4c\x6b\x8a\xa9" + "\xc8\xe7\x06\x25\x44\x63\x82\xa1" + "\xc0\xdf\xfe\x1d\x3c\x5b\x7a\x99" + "\xb8\xd7\xf6\x15\x34\x53\x72\x91" + "\xb0\xcf\xee\x0d\x2c\x4b\x6a\x89" + "\xa8\xc7\xe6\x05\x24\x43\x62\x81" + "\xa0\xbf\xde\xfd\x1c\x3b\x5a\x79" + "\x98\xb7\xd6\xf5\x14\x33\x52\x71" + "\x90\xaf\xce\xed\x0c\x2b\x4a\x69" + "\x88\xa7\xc6\xe5\x04\x23\x42\x61" + "\x80\x9f\xbe\xdd\xfc\x1b\x3a\x59" + "\x78\x97\xb6\xd5\xf4\x13\x32\x51" + "\x70\x8f\xae\xcd\xec\x0b\x2a\x49" + "\x68\x87\xa6\xc5\xe4\x03\x22\x41" + "\x60\x7f\x9e\xbd\xdc\xfb\x1a\x39" + "\x58\x77\x96\xb5\xd4\xf3\x12\x31" + "\x50\x6f\x8e\xad\xcc\xeb\x0a\x29" + "\x48\x67\x86\xa5\xc4\xe3\x02\x21" + "\x40\x5f\x7e\x9d\xbc\xdb\xfa\x19" + "\x38\x57\x76\x95\xb4\xd3\xf2\x11" + "\x30\x4f\x6e\x8d\xac\xcb\xea\x09" + "\x28\x47\x66\x85\xa4\xc3\xe2\x01" + "\x20\x3f\x5e\x7d\x9c\xbb\xda\xf9" + "\x18\x37\x56\x75\x94\xb3\xd2\xf1" + "\x10\x2f\x4e\x6d\x8c\xab\xca\xe9" + "\x08\x27\x46\x65\x84\xa3\xc2\xe1" + "\x00\x21\x42\x63", + .ilen = 4100, + .result = + "\xf0\x5c\x74\xad\x4e\xbc\x99\xe2" + "\xae\xff\x91\x3a\x44\xcf\x38\x32" + "\x1e\xad\xa7\xcd\xa1\x39\x95\xaa" + "\x10\xb1\xb3\x2e\x04\x31\x8f\x86" + "\xf2\x62\x74\x70\x0c\xa4\x46\x08" + "\xa8\xb7\x99\xa8\xe9\xd2\x73\x79" + "\x7e\x6e\xd4\x8f\x1e\xc7\x8e\x31" + "\x0b\xfa\x4b\xce\xfd\xf3\x57\x71" + "\xe9\x46\x03\xa5\x3d\x34\x00\xe2" + "\x18\xff\x75\x6d\x06\x2d\x00\xab" + "\xb9\x3e\x6c\x59\xc5\x84\x06\xb5" + "\x8b\xd0\x89\x9c\x4a\x79\x16\xc6" + "\x3d\x74\x54\xfa\x44\xcd\x23\x26" + "\x5c\xcf\x7e\x28\x92\x32\xbf\xdf" + "\xa7\x20\x3c\x74\x58\x2a\x9a\xde" + "\x61\x00\x1c\x4f\xff\x59\xc4\x22" + "\xac\x3c\xd0\xe8\x6c\xf9\x97\x1b" + "\x58\x9b\xad\x71\xe8\xa9\xb5\x0d" + "\xee\x2f\x04\x1f\x7f\xbc\x99\xee" + "\x84\xff\x42\x60\xdc\x3a\x18\xa5" + "\x81\xf9\xef\xdc\x7a\x0f\x65\x41" + "\x2f\xa3\xd3\xf9\xc2\xcb\xc0\x4d" + "\x8f\xd3\x76\x96\xad\x49\x6d\x38" + "\x3d\x39\x0b\x6c\x80\xb7\x54\x69" + "\xf0\x2c\x90\x02\x29\x0d\x1c\x12" + "\xad\x55\xc3\x8b\x68\xd9\xcc\xb3" + "\xb2\x64\x33\x90\x5e\xca\x4b\xe2" + "\xfb\x75\xdc\x63\xf7\x9f\x82\x74" + "\xf0\xc9\xaa\x7f\xe9\x2a\x9b\x33" + "\xbc\x88\x00\x7f\xca\xb2\x1f\x14" + "\xdb\xc5\x8e\x7b\x11\x3c\x3e\x08" + "\xf3\x83\xe8\xe0\x94\x86\x2e\x92" + "\x78\x6b\x01\xc9\xc7\x83\xba\x21" + "\x6a\x25\x15\x33\x4e\x45\x08\xec" + "\x35\xdb\xe0\x6e\x31\x51\x79\xa9" + "\x42\x44\x65\xc1\xa0\xf1\xf9\x2a" + "\x70\xd5\xb6\xc6\xc1\x8c\x39\xfc" + "\x25\xa6\x55\xd9\xdd\x2d\x4c\xec" + "\x49\xc6\xeb\x0e\xa8\x25\x2a\x16" + "\x1b\x66\x84\xda\xe2\x92\xe5\xc0" + "\xc8\x53\x07\xaf\x80\x84\xec\xfd" + "\xcd\xd1\x6e\xcd\x6f\x6a\xf5\x36" + "\xc5\x15\xe5\x25\x7d\x77\xd1\x1a" + "\x93\x36\xa9\xcf\x7c\xa4\x54\x4a" + "\x06\x51\x48\x4e\xf6\x59\x87\xd2" + "\x04\x02\xef\xd3\x44\xde\x76\x31" + "\xb3\x34\x17\x1b\x9d\x66\x11\x9f" + "\x1e\xcc\x17\xe9\xc7\x3c\x1b\xe7" + "\xcb\x50\x08\xfc\xdc\x2b\x24\xdb" + "\x65\x83\xd0\x3b\xe3\x30\xea\x94" + "\x6c\xe7\xe8\x35\x32\xc7\xdb\x64" + "\xb4\x01\xab\x36\x2c\x77\x13\xaf" + "\xf8\x2b\x88\x3f\x54\x39\xc4\x44" + "\xfe\xef\x6f\x68\x34\xbe\x0f\x05" + "\x16\x6d\xf6\x0a\x30\xe7\xe3\xed" + "\xc4\xde\x3c\x1b\x13\xd8\xdb\xfe" + "\x41\x62\xe5\x28\xd4\x8d\xa3\xc7" + "\x93\x97\xc6\x48\x45\x1d\x9f\x83" + "\xdf\x4b\x40\x3e\x42\x25\x87\x80" + "\x4c\x7d\xa8\xd4\x98\x23\x95\x75" + "\x41\x8c\xda\x41\x9b\xd4\xa7\x06" + "\xb5\xf1\x71\x09\x53\xbe\xca\xbf" + "\x32\x03\xed\xf0\x50\x1c\x56\x39" + "\x5b\xa4\x75\x18\xf7\x9b\x58\xef" + "\x53\xfc\x2a\x38\x23\x15\x75\xcd" + "\x45\xe5\x5a\x82\x55\xba\x21\xfa" + "\xd4\xbd\xc6\x94\x7c\xc5\x80\x12" + "\xf7\x4b\x32\xc4\x9a\x82\xd8\x28" + "\x8f\xd9\xc2\x0f\x60\x03\xbe\x5e" + "\x21\xd6\x5f\x58\xbf\x5c\xb1\x32" + "\x82\x8d\xa9\xe5\xf2\x66\x1a\xc0" + "\xa0\xbc\x58\x2f\x71\xf5\x2f\xed" + "\xd1\x26\xb9\xd8\x49\x5a\x07\x19" + "\x01\x7c\x59\xb0\xf8\xa4\xb7\xd3" + "\x7b\x1a\x8c\x38\xf4\x50\xa4\x59" + "\xb0\xcc\x41\x0b\x88\x7f\xe5\x31" + "\xb3\x42\xba\xa2\x7e\xd4\x32\x71" + "\x45\x87\x48\xa9\xc2\xf2\x89\xb3" + "\xe4\xa7\x7e\x52\x15\x61\xfa\xfe" + "\xc9\xdd\x81\xeb\x13\xab\xab\xc3" + "\x98\x59\xd8\x16\x3d\x14\x7a\x1c" + "\x3c\x41\x9a\x16\x16\x9b\xd2\xd2" + "\x69\x3a\x29\x23\xac\x86\x32\xa5" + "\x48\x9c\x9e\xf3\x47\x77\x81\x70" + "\x24\xe8\x85\xd2\xf5\xb5\xfa\xff" + "\x59\x6a\xd3\x50\x59\x43\x59\xde" + "\xd9\xf1\x55\xa5\x0c\xc3\x1a\x1a" + "\x18\x34\x0d\x1a\x63\x33\xed\x10" + "\xe0\x1d\x2a\x18\xd2\xc0\x54\xa8" + "\xca\xb5\x9a\xd3\xdd\xca\x45\x84" + "\x50\xe7\x0f\xfe\xa4\x99\x5a\xbe" + "\x43\x2d\x9a\xcb\x92\x3f\x5a\x1d" + "\x85\xd8\xc9\xdf\x68\xc9\x12\x80" + "\x56\x0c\xdc\x00\xdc\x3a\x7d\x9d" + "\xa3\xa2\xe8\x4d\xbf\xf9\x70\xa0" + "\xa4\x13\x4f\x6b\xaf\x0a\x89\x7f" + "\xda\xf0\xbf\x9b\xc8\x1d\xe5\xf8" + "\x2e\x8b\x07\xb5\x73\x1b\xcc\xa2" + "\xa6\xad\x30\xbc\x78\x3c\x5b\x10" + "\xfa\x5e\x62\x2d\x9e\x64\xb3\x33" + "\xce\xf9\x1f\x86\xe7\x8b\xa2\xb8" + "\xe8\x99\x57\x8c\x11\xed\x66\xd9" + "\x3c\x72\xb9\xc3\xe6\x4e\x17\x3a" + "\x6a\xcb\x42\x24\x06\xed\x3e\x4e" + "\xa3\xe8\x6a\x94\xda\x0d\x4e\xd5" + "\x14\x19\xcf\xb6\x26\xd8\x2e\xcc" + "\x64\x76\x38\x49\x4d\xfe\x30\x6d" + "\xe4\xc8\x8c\x7b\xc4\xe0\x35\xba" + "\x22\x6e\x76\xe1\x1a\xf2\x53\xc3" + "\x28\xa2\x82\x1f\x61\x69\xad\xc1" + "\x7b\x28\x4b\x1e\x6c\x85\x95\x9b" + "\x51\xb5\x17\x7f\x12\x69\x8c\x24" + "\xd5\xc7\x5a\x5a\x11\x54\xff\x5a" + "\xf7\x16\xc3\x91\xa6\xf0\xdc\x0a" + "\xb6\xa7\x4a\x0d\x7a\x58\xfe\xa5" + "\xf5\xcb\x8f\x7b\x0e\xea\x57\xe7" + "\xbd\x79\xd6\x1c\x88\x23\x6c\xf2" + "\x4d\x29\x77\x53\x35\x6a\x00\x8d" + "\xcd\xa3\x58\xbe\x77\x99\x18\xf8" + "\xe6\xe1\x8f\xe9\x37\x8f\xe3\xe2" + "\x5a\x8a\x93\x25\xaf\xf3\x78\x80" + "\xbe\xa6\x1b\xc6\xac\x8b\x1c\x91" + "\x58\xe1\x9f\x89\x35\x9d\x1d\x21" + "\x29\x9f\xf4\x99\x02\x27\x0f\xa8" + "\x4f\x79\x94\x2b\x33\x2c\xda\xa2" + "\x26\x39\x83\x94\xef\x27\xd8\x53" + "\x8f\x66\x0d\xe4\x41\x7d\x34\xcd" + "\x43\x7c\x95\x0a\x53\xef\x66\xda" + "\x7e\x9b\xf3\x93\xaf\xd0\x73\x71" + "\xba\x40\x9b\x74\xf8\xd7\xd7\x41" + "\x6d\xaf\x72\x9c\x8d\x21\x87\x3c" + "\xfd\x0a\x90\xa9\x47\x96\x9e\xd3" + "\x88\xee\x73\xcf\x66\x2f\x52\x56" + "\x6d\xa9\x80\x4c\xe2\x6f\x62\x88" + "\x3f\x0e\x54\x17\x48\x80\x5d\xd3" + "\xc3\xda\x25\x3d\xa1\xc8\xcb\x9f" + "\x9b\x70\xb3\xa1\xeb\x04\x52\xa1" + "\xf2\x22\x0f\xfc\xc8\x18\xfa\xf9" + "\x85\x9c\xf1\xac\xeb\x0c\x02\x46" + "\x75\xd2\xf5\x2c\xe3\xd2\x59\x94" + "\x12\xf3\x3c\xfc\xd7\x92\xfa\x36" + "\xba\x61\x34\x38\x7c\xda\x48\x3e" + "\x08\xc9\x39\x23\x5e\x02\x2c\x1a" + "\x18\x7e\xb4\xd9\xfd\x9e\x40\x02" + "\xb1\x33\x37\x32\xe7\xde\xd6\xd0" + "\x7c\x58\x65\x4b\xf8\x34\x27\x9c" + "\x44\xb4\xbd\xe9\xe9\x4c\x78\x7d" + "\x4b\x9f\xce\xb1\xcd\x47\xa5\x37" + "\xe5\x6d\xbd\xb9\x43\x94\x0a\xd4" + "\xd6\xf9\x04\x5f\xb5\x66\x6c\x1a" + "\x35\x12\xe3\x36\x28\x27\x36\x58" + "\x01\x2b\x79\xe4\xba\x6d\x10\x7d" + "\x65\xdf\x84\x95\xf4\xd5\xb6\x8f" + "\x2b\x9f\x96\x00\x86\x60\xf0\x21" + "\x76\xa8\x6a\x8c\x28\x1c\xb3\x6b" + "\x97\xd7\xb6\x53\x2a\xcc\xab\x40" + "\x9d\x62\x79\x58\x52\xe6\x65\xb7" + "\xab\x55\x67\x9c\x89\x7c\x03\xb0" + "\x73\x59\xc5\x81\xf5\x18\x17\x5c" + "\x89\xf3\x78\x35\x44\x62\x78\x72" + "\xd0\x96\xeb\x31\xe7\x87\x77\x14" + "\x99\x51\xf2\x59\x26\x9e\xb5\xa6" + "\x45\xfe\x6e\xbd\x07\x4c\x94\x5a" + "\xa5\x7d\xfc\xf1\x2b\x77\xe2\xfe" + "\x17\xd4\x84\xa0\xac\xb5\xc7\xda" + "\xa9\x1a\xb6\xf3\x74\x11\xb4\x9d" + "\xfb\x79\x2e\x04\x2d\x50\x28\x83" + "\xbf\xc6\x52\xd3\x34\xd6\xe8\x7a" + "\xb6\xea\xe7\xa8\x6c\x15\x1e\x2c" + "\x57\xbc\x48\x4e\x5f\x5c\xb6\x92" + "\xd2\x49\x77\x81\x6d\x90\x70\xae" + "\x98\xa1\x03\x0d\x6b\xb9\x77\x14" + "\xf1\x4e\x23\xd3\xf8\x68\xbd\xc2" + "\xfe\x04\xb7\x5c\xc5\x17\x60\x8f" + "\x65\x54\xa4\x7a\x42\xdc\x18\x0d" + "\xb5\xcf\x0f\xd3\xc7\x91\x66\x1b" + "\x45\x42\x27\x75\x50\xe5\xee\xb8" + "\x7f\x33\x2c\xba\x4a\x92\x4d\x2c" + "\x3c\xe3\x0d\x80\x01\xba\x0d\x29" + "\xd8\x3c\xe9\x13\x16\x57\xe6\xea" + "\x94\x52\xe7\x00\x4d\x30\xb0\x0f" + "\x35\xb8\xb8\xa7\xb1\xb5\x3b\x44" + "\xe1\x2f\xfd\x88\xed\x43\xe7\x52" + "\x10\x93\xb3\x8a\x30\x6b\x0a\xf7" + "\x23\xc6\x50\x9d\x4a\xb0\xde\xc3" + "\xdc\x9b\x2f\x01\x56\x36\x09\xc5" + "\x2f\x6b\xfe\xf1\xd8\x27\x45\x03" + "\x30\x5e\x5c\x5b\xb4\x62\x0e\x1a" + "\xa9\x21\x2b\x92\x94\x87\x62\x57" + "\x4c\x10\x74\x1a\xf1\x0a\xc5\x84" + "\x3b\x9e\x72\x02\xd7\xcc\x09\x56" + "\xbd\x54\xc1\xf0\xc3\xe3\xb3\xf8" + "\xd2\x0d\x61\xcb\xef\xce\x0d\x05" + "\xb0\x98\xd9\x8e\x4f\xf9\xbc\x93" + "\xa6\xea\xc8\xcf\x10\x53\x4b\xf1" + "\xec\xfc\x89\xf9\x64\xb0\x22\xbf" + "\x9e\x55\x46\x9f\x7c\x50\x8e\x84" + "\x54\x20\x98\xd7\x6c\x40\x1e\xdb" + "\x69\x34\x78\x61\x24\x21\x9c\x8a" + "\xb3\x62\x31\x8b\x6e\xf5\x2a\x35" + "\x86\x13\xb1\x6c\x64\x2e\x41\xa5" + "\x05\xf2\x42\xba\xd2\x3a\x0d\x8e" + "\x8a\x59\x94\x3c\xcf\x36\x27\x82" + "\xc2\x45\xee\x58\xcd\x88\xb4\xec" + "\xde\xb2\x96\x0a\xaf\x38\x6f\x88" + "\xd7\xd8\xe1\xdf\xb9\x96\xa9\x0a" + "\xb1\x95\x28\x86\x20\xe9\x17\x49" + "\xa2\x29\x38\xaa\xa5\xe9\x6e\xf1" + "\x19\x27\xc0\xd5\x2a\x22\xc3\x0b" + "\xdb\x7c\x73\x10\xb9\xba\x89\x76" + "\x54\xae\x7d\x71\xb3\x93\xf6\x32" + "\xe6\x47\x43\x55\xac\xa0\x0d\xc2" + "\x93\x27\x4a\x8e\x0e\x74\x15\xc7" + "\x0b\x85\xd9\x0c\xa9\x30\x7a\x3e" + "\xea\x8f\x85\x6d\x3a\x12\x4f\x72" + "\x69\x58\x7a\x80\xbb\xb5\x97\xf3" + "\xcf\x70\xd2\x5d\xdd\x4d\x21\x79" + "\x54\x4d\xe4\x05\xe8\xbd\xc2\x62" + "\xb1\x3b\x77\x1c\xd6\x5c\xf3\xa0" + "\x79\x00\xa8\x6c\x29\xd9\x18\x24" + "\x36\xa2\x46\xc0\x96\x65\x7f\xbd" + "\x2a\xed\x36\x16\x0c\xaa\x9f\xf4" + "\xc5\xb4\xe2\x12\xed\x69\xed\x4f" + "\x26\x2c\x39\x52\x89\x98\xe7\x2c" + "\x99\xa4\x9e\xa3\x9b\x99\x46\x7a" + "\x3a\xdc\xa8\x59\xa3\xdb\xc3\x3b" + "\x95\x0d\x3b\x09\x6e\xee\x83\x5d" + "\x32\x4d\xed\xab\xfa\x98\x14\x4e" + "\xc3\x15\x45\x53\x61\xc4\x93\xbd" + "\x90\xf4\x99\x95\x4c\xe6\x76\x92" + "\x29\x90\x46\x30\x92\x69\x7d\x13" + "\xf2\xa5\xcd\x69\x49\x44\xb2\x0f" + "\x63\x40\x36\x5f\x09\xe2\x78\xf8" + "\x91\xe3\xe2\xfa\x10\xf7\xc8\x24" + "\xa8\x89\x32\x5c\x37\x25\x1d\xb2" + "\xea\x17\x8a\x0a\xa9\x64\xc3\x7c" + "\x3c\x7c\xbd\xc6\x79\x34\xe7\xe2" + "\x85\x8e\xbf\xf8\xde\x92\xa0\xae" + "\x20\xc4\xf6\xbb\x1f\x38\x19\x0e" + "\xe8\x79\x9c\xa1\x23\xe9\x54\x7e" + "\x37\x2f\xe2\x94\x32\xaf\xa0\x23" + "\x49\xe4\xc0\xb3\xac\x00\x8f\x36" + "\x05\xc4\xa6\x96\xec\x05\x98\x4f" + "\x96\x67\x57\x1f\x20\x86\x1b\x2d" + "\x69\xe4\x29\x93\x66\x5f\xaf\x6b" + "\x88\x26\x2c\x67\x02\x4b\x52\xd0" + "\x83\x7a\x43\x1f\xc0\x71\x15\x25" + "\x77\x65\x08\x60\x11\x76\x4c\x8d" + "\xed\xa9\x27\xc6\xb1\x2a\x2c\x6a" + "\x4a\x97\xf5\xc6\xb7\x70\x42\xd3" + "\x03\xd1\x24\x95\xec\x6d\xab\x38" + "\x72\xce\xe2\x8b\x33\xd7\x51\x09" + "\xdc\x45\xe0\x09\x96\x32\xf3\xc4" + "\x84\xdc\x73\x73\x2d\x1b\x11\x98" + "\xc5\x0e\x69\x28\x94\xc7\xb5\x4d" + "\xc8\x8a\xd0\xaa\x13\x2e\x18\x74" + "\xdd\xd1\x1e\xf3\x90\xe8\xfc\x9a" + "\x72\x4a\x0e\xd1\xe4\xfb\x0d\x96" + "\xd1\x0c\x79\x85\x1b\x1c\xfe\xe1" + "\x62\x8f\x7a\x73\x32\xab\xc8\x18" + "\x69\xe3\x34\x30\xdf\x13\xa6\xe5" + "\xe8\x0e\x67\x7f\x81\x11\xb4\x60" + "\xc7\xbd\x79\x65\x50\xdc\xc4\x5b" + "\xde\x39\xa4\x01\x72\x63\xf3\xd1" + "\x64\x4e\xdf\xfc\x27\x92\x37\x0d" + "\x57\xcd\x11\x4f\x11\x04\x8e\x1d" + "\x16\xf7\xcd\x92\x9a\x99\x30\x14" + "\xf1\x7c\x67\x1b\x1f\x41\x0b\xe8" + "\x32\xe8\xb8\xc1\x4f\x54\x86\x4f" + "\xe5\x79\x81\x73\xcd\x43\x59\x68" + "\x73\x02\x3b\x78\x21\x72\x43\x00" + "\x49\x17\xf7\x00\xaf\x68\x24\x53" + "\x05\x0a\xc3\x33\xe0\x33\x3f\x69" + "\xd2\x84\x2f\x0b\xed\xde\x04\xf4" + "\x11\x94\x13\x69\x51\x09\x28\xde" + "\x57\x5c\xef\xdc\x9a\x49\x1c\x17" + "\x97\xf3\x96\xc1\x7f\x5d\x2e\x7d" + "\x55\xb8\xb3\x02\x09\xb3\x1f\xe7" + "\xc9\x8d\xa3\x36\x34\x8a\x77\x13" + "\x30\x63\x4c\xa5\xcd\xc3\xe0\x7e" + "\x05\xa1\x7b\x0c\xcb\x74\x47\x31" + "\x62\x03\x43\xf1\x87\xb4\xb0\x85" + "\x87\x8e\x4b\x25\xc7\xcf\xae\x4b" + "\x36\x46\x3e\x62\xbc\x6f\xeb\x5f" + "\x73\xac\xe6\x07\xee\xc1\xa1\xd6" + "\xc4\xab\xc9\xd6\x89\x45\xe1\xf1" + "\x04\x4e\x1a\x6f\xbb\x4f\x3a\xa3" + "\xa0\xcb\xa3\x0a\xd8\x71\x35\x55" + "\xe4\xbc\x2e\x04\x06\xe6\xff\x5b" + "\x1c\xc0\x11\x7c\xc5\x17\xf3\x38" + "\xcf\xe9\xba\x0f\x0e\xef\x02\xc2" + "\x8d\xc6\xbc\x4b\x67\x20\x95\xd7" + "\x2c\x45\x5b\x86\x44\x8c\x6f\x2e" + "\x7e\x9f\x1c\x77\xba\x6b\x0e\xa3" + "\x69\xdc\xab\x24\x57\x60\x47\xc1" + "\xd1\xa5\x9d\x23\xe6\xb1\x37\xfe" + "\x93\xd2\x4c\x46\xf9\x0c\xc6\xfb" + "\xd6\x9d\x99\x69\xab\x7a\x07\x0c" + "\x65\xe7\xc4\x08\x96\xe2\xa5\x01" + "\x3f\x46\x07\x05\x7e\xe8\x9a\x90" + "\x50\xdc\xe9\x7a\xea\xa1\x39\x6e" + "\x66\xe4\x6f\xa5\x5f\xb2\xd9\x5b" + "\xf5\xdb\x2a\x32\xf0\x11\x6f\x7c" + "\x26\x10\x8f\x3d\x80\xe9\x58\xf7" + "\xe0\xa8\x57\xf8\xdb\x0e\xce\x99" + "\x63\x19\x3d\xd5\xec\x1b\x77\x69" + "\x98\xf6\xe4\x5f\x67\x17\x4b\x09" + "\x85\x62\x82\x70\x18\xe2\x9a\x78" + "\xe2\x62\xbd\xb4\xf1\x42\xc6\xfb" + "\x08\xd0\xbd\xeb\x4e\x09\xf2\xc8" + "\x1e\xdc\x3d\x32\x21\x56\x9c\x4f" + "\x35\xf3\x61\x06\x72\x84\xc4\x32" + "\xf2\xf1\xfa\x0b\x2f\xc3\xdb\x02" + "\x04\xc2\xde\x57\x64\x60\x8d\xcf" + "\xcb\x86\x5d\x97\x3e\xb1\x9c\x01" + "\xd6\x28\x8f\x99\xbc\x46\xeb\x05" + "\xaf\x7e\xb8\x21\x2a\x56\x85\x1c" + "\xb3\x71\xa0\xde\xca\x96\xf1\x78" + "\x49\xa2\x99\x81\x80\x5c\x01\xf5" + "\xa0\xa2\x56\x63\xe2\x70\x07\xa5" + "\x95\xd6\x85\xeb\x36\x9e\xa9\x51" + "\x66\x56\x5f\x1d\x02\x19\xe2\xf6" + "\x4f\x73\x38\x09\x75\x64\x48\xe0" + "\xf1\x7e\x0e\xe8\x9d\xf9\xed\x94" + "\xfe\x16\x26\x62\x49\x74\xf4\xb0" + "\xd4\xa9\x6c\xb0\xfd\x53\xe9\x81" + "\xe0\x7a\xbf\xcf\xb5\xc4\x01\x81" + "\x79\x99\x77\x01\x3b\xe9\xa2\xb6" + "\xe6\x6a\x8a\x9e\x56\x1c\x8d\x1e" + "\x8f\x06\x55\x2c\x6c\xdc\x92\x87" + "\x64\x3b\x4b\x19\xa1\x13\x64\x1d" + "\x4a\xe9\xc0\x00\xb8\x95\xef\x6b" + "\x1a\x86\x6d\x37\x52\x02\xc2\xe0" + "\xc8\xbb\x42\x0c\x02\x21\x4a\xc9" + "\xef\xa0\x54\xe4\x5e\x16\x53\x81" + "\x70\x62\x10\xaf\xde\xb8\xb5\xd3" + "\xe8\x5e\x6c\xc3\x8a\x3e\x18\x07" + "\xf2\x2f\x7d\xa7\xe1\x3d\x4e\xb4" + "\x26\xa7\xa3\x93\x86\xb2\x04\x1e" + "\x53\x5d\x86\xd6\xde\x65\xca\xe3" + "\x4e\xc1\xcf\xef\xc8\x70\x1b\x83" + "\x13\xdd\x18\x8b\x0d\x76\xd2\xf6" + "\x37\x7a\x93\x7a\x50\x11\x9f\x96" + "\x86\x25\xfd\xac\xdc\xbe\x18\x93" + "\x19\x6b\xec\x58\x4f\xb9\x75\xa7" + "\xdd\x3f\x2f\xec\xc8\x5a\x84\xab" + "\xd5\xe4\x8a\x07\xf6\x4d\x23\xd6" + "\x03\xfb\x03\x6a\xea\x66\xbf\xd4" + "\xb1\x34\xfb\x78\xe9\x55\xdc\x7c" + "\x3d\x9c\xe5\x9a\xac\xc3\x7a\x80" + "\x24\x6d\xa0\xef\x25\x7c\xb7\xea" + "\xce\x4d\x5f\x18\x60\xce\x87\x22" + "\x66\x2f\xd5\xdd\xdd\x02\x21\x75" + "\x82\xa0\x1f\x58\xc6\xd3\x62\xf7" + "\x32\xd8\xaf\x1e\x07\x77\x51\x96" + "\xd5\x6b\x1e\x7e\x80\x02\xe8\x67" + "\xea\x17\x0b\x10\xd2\x3f\x28\x25" + "\x4f\x05\x77\x02\x14\x69\xf0\x2c" + "\xbe\x0c\xf1\x74\x30\xd1\xb9\x9b" + "\xfc\x8c\xbb\x04\x16\xd9\xba\xc3" + "\xbc\x91\x8a\xc4\x30\xa4\xb0\x12" + "\x4c\x21\x87\xcb\xc9\x1d\x16\x96" + "\x07\x6f\x23\x54\xb9\x6f\x79\xe5" + "\x64\xc0\x64\xda\xb1\xae\xdd\x60" + "\x6c\x1a\x9d\xd3\x04\x8e\x45\xb0" + "\x92\x61\xd0\x48\x81\xed\x5e\x1d" + "\xa0\xc9\xa4\x33\xc7\x13\x51\x5d" + "\x7f\x83\x73\xb6\x70\x18\x65\x3e" + "\x2f\x0e\x7a\x12\x39\x98\xab\xd8" + "\x7e\x6f\xa3\xd1\xba\x56\xad\xbd" + "\xf0\x03\x01\x1c\x85\x35\x9f\xeb" + "\x19\x63\xa1\xaf\xfe\x2d\x35\x50" + "\x39\xa0\x65\x7c\x95\x7e\x6b\xfe" + "\xc1\xac\x07\x7c\x98\x4f\xbe\x57" + "\xa7\x22\xec\xe2\x7e\x29\x09\x53" + "\xe8\xbf\xb4\x7e\x3f\x8f\xfc\x14" + "\xce\x54\xf9\x18\x58\xb5\xff\x44" + "\x05\x9d\xce\x1b\xb6\x82\x23\xc8" + "\x2e\xbc\x69\xbb\x4a\x29\x0f\x65" + "\x94\xf0\x63\x06\x0e\xef\x8c\xbd" + "\xff\xfd\xb0\x21\x6e\x57\x05\x75" + "\xda\xd5\xc4\xeb\x8d\x32\xf7\x50" + "\xd3\x6f\x22\xed\x5f\x8e\xa2\x5b" + "\x80\x8c\xc8\x78\x40\x24\x4b\x89" + "\x30\xce\x7a\x97\x0e\xc4\xaf\xef" + "\x9b\xb4\xcd\x66\x74\x14\x04\x2b" + "\xf7\xce\x0b\x1c\x6e\xc2\x78\x8c" + "\xca\xc5\xd0\x1c\x95\x4a\x91\x2d" + "\xa7\x20\xeb\x86\x52\xb7\x67\xd8" + "\x0c\xd6\x04\x14\xde\x51\x74\x75" + "\xe7\x11\xb4\x87\xa3\x3d\x2d\xad" + "\x4f\xef\xa0\x0f\x70\x00\x6d\x13" + "\x19\x1d\x41\x50\xe9\xd8\xf0\x32" + "\x71\xbc\xd3\x11\xf2\xac\xbe\xaf" + "\x75\x46\x65\x4e\x07\x34\x37\xa3" + "\x89\xfe\x75\xd4\x70\x4c\xc6\x3f" + "\x69\x24\x0e\x38\x67\x43\x8c\xde" + "\x06\xb5\xb8\xe7\xc4\xf0\x41\x8f" + "\xf0\xbd\x2f\x0b\xb9\x18\xf8\xde" + "\x64\xb1\xdb\xee\x00\x50\x77\xe1" + "\xc7\xff\xa6\xfa\xdd\x70\xf4\xe3" + "\x93\xe9\x77\x35\x3d\x4b\x2f\x2b" + "\x6d\x55\xf0\xfc\x88\x54\x4e\x89" + "\xc1\x8a\x23\x31\x2d\x14\x2a\xb8" + "\x1b\x15\xdd\x9e\x6e\x7b\xda\x05" + "\x91\x7d\x62\x64\x96\x72\xde\xfc" + "\xc1\xec\xf0\x23\x51\x6f\xdb\x5b" + "\x1d\x08\x57\xce\x09\xb8\xf6\xcd" + "\x8d\x95\xf2\x20\xbf\x0f\x20\x57" + "\x98\x81\x84\x4f\x15\x5c\x76\xe7" + "\x3e\x0a\x3a\x6c\xc4\x8a\xbe\x78" + "\x74\x77\xc3\x09\x4b\x5d\x48\xe4" + "\xc8\xcb\x0b\xea\x17\x28\xcf\xcf" + "\x31\x32\x44\xa4\xe5\x0e\x1a\x98" + "\x94\xc4\xf0\xff\xae\x3e\x44\xe8" + "\xa5\xb3\xb5\x37\x2f\xe8\xaf\x6f" + "\x28\xc1\x37\x5f\x31\xd2\xb9\x33" + "\xb1\xb2\x52\x94\x75\x2c\x29\x59" + "\x06\xc2\x25\xe8\x71\x65\x4e\xed" + "\xc0\x9c\xb1\xbb\x25\xdc\x6c\xe7" + "\x4b\xa5\x7a\x54\x7a\x60\xff\x7a" + "\xe0\x50\x40\x96\x35\x63\xe4\x0b" + "\x76\xbd\xa4\x65\x00\x1b\x57\x88" + "\xae\xed\x39\x88\x42\x11\x3c\xed" + "\x85\x67\x7d\xb9\x68\x82\xe9\x43" + "\x3c\x47\x53\xfa\xe8\xf8\x9f\x1f" + "\x9f\xef\x0f\xf7\x30\xd9\x30\x0e" + "\xb9\x9f\x69\x18\x2f\x7e\xf8\xf8" + "\xf8\x8c\x0f\xd4\x02\x4d\xea\xcd" + "\x0a\x9c\x6f\x71\x6d\x5a\x4c\x60" + "\xce\x20\x56\x32\xc6\xc5\x99\x1f" + "\x09\xe6\x4e\x18\x1a\x15\x13\xa8" + "\x7d\xb1\x6b\xc0\xb2\x6d\xf8\x26" + "\x66\xf8\x3d\x18\x74\x70\x66\x7a" + "\x34\x17\xde\xba\x47\xf1\x06\x18" + "\xcb\xaf\xeb\x4a\x1e\x8f\xa7\x77" + "\xe0\x3b\x78\x62\x66\xc9\x10\xea" + "\x1f\xb7\x29\x0a\x45\xa1\x1d\x1e" + "\x1d\xe2\x65\x61\x50\x9c\xd7\x05" + "\xf2\x0b\x5b\x12\x61\x02\xc8\xe5" + "\x63\x4f\x20\x0c\x07\x17\x33\x5e" + "\x03\x9a\x53\x0f\x2e\x55\xfe\x50" + "\x43\x7d\xd0\xb6\x7e\x5a\xda\xae" + "\x58\xef\x15\xa9\x83\xd9\x46\xb1" + "\x42\xaa\xf5\x02\x6c\xce\x92\x06" + "\x1b\xdb\x66\x45\x91\x79\xc2\x2d" + "\xe6\x53\xd3\x14\xfd\xbb\x44\x63" + "\xc6\xd7\x3d\x7a\x0c\x75\x78\x9d" + "\x5c\xa6\x39\xb3\xe5\x63\xca\x8b" + "\xfe\xd3\xef\x60\x83\xf6\x8e\x70" + "\xb6\x67\xc7\x77\xed\x23\xef\x4c" + "\xf0\xed\x2d\x07\x59\x6f\xc1\x01" + "\x34\x37\x08\xab\xd9\x1f\x09\xb1" + "\xce\x5b\x17\xff\x74\xf8\x9c\xd5" + "\x2c\x56\x39\x79\x0f\x69\x44\x75" + "\x58\x27\x01\xc4\xbf\xa7\xa1\x1d" + "\x90\x17\x77\x86\x5a\x3f\xd9\xd1" + "\x0e\xa0\x10\xf8\xec\x1e\xa5\x7f" + "\x5e\x36\xd1\xe3\x04\x2c\x70\xf7" + "\x8e\xc0\x98\x2f\x6c\x94\x2b\x41" + "\xb7\x60\x00\xb7\x2e\xb8\x02\x8d" + "\xb8\xb0\xd3\x86\xba\x1d\xd7\x90" + "\xd6\xb6\xe1\xfc\xd7\xd8\x28\x06" + "\x63\x9b\xce\x61\x24\x79\xc0\x70" + "\x52\xd0\xb6\xd4\x28\x95\x24\x87" + "\x03\x1f\xb7\x9a\xda\xa3\xfb\x52" + "\x5b\x68\xe7\x4c\x8c\x24\xe1\x42" + "\xf7\xd5\xfd\xad\x06\x32\x9f\xba" + "\xc1\xfc\xdd\xc6\xfc\xfc\xb3\x38" + "\x74\x56\x58\x40\x02\x37\x52\x2c" + "\x55\xcc\xb3\x9e\x7a\xe9\xd4\x38" + "\x41\x5e\x0c\x35\xe2\x11\xd1\x13" + "\xf8\xb7\x8d\x72\x6b\x22\x2a\xb0" + "\xdb\x08\xba\x35\xb9\x3f\xc8\xd3" + "\x24\x90\xec\x58\xd2\x09\xc7\x2d" + "\xed\x38\x80\x36\x72\x43\x27\x49" + "\x4a\x80\x8a\xa2\xe8\xd3\xda\x30" + "\x7d\xb6\x82\x37\x86\x92\x86\x3e" + "\x08\xb2\x28\x5a\x55\x44\x24\x7d" + "\x40\x48\x8a\xb6\x89\x58\x08\xa0" + "\xd6\x6d\x3a\x17\xbf\xf6\x54\xa2" + "\xf5\xd3\x8c\x0f\x78\x12\x57\x8b" + "\xd5\xc2\xfd\x58\x5b\x7f\x38\xe3" + "\xcc\xb7\x7c\x48\xb3\x20\xe8\x81" + "\x14\x32\x45\x05\xe0\xdb\x9f\x75" + "\x85\xb4\x6a\xfc\x95\xe3\x54\x22" + "\x12\xee\x30\xfe\xd8\x30\xef\x34" + "\x50\xab\x46\x30\x98\x2f\xb7\xc0" + "\x15\xa2\x83\xb6\xf2\x06\x21\xa2" + "\xc3\x26\x37\x14\xd1\x4d\xb5\x10" + "\x52\x76\x4d\x6a\xee\xb5\x2b\x15" + "\xb7\xf9\x51\xe8\x2a\xaf\xc7\xfa" + "\x77\xaf\xb0\x05\x4d\xd1\x68\x8e" + "\x74\x05\x9f\x9d\x93\xa5\x3e\x7f" + "\x4e\x5f\x9d\xcb\x09\xc7\x83\xe3" + "\x02\x9d\x27\x1f\xef\x85\x05\x8d" + "\xec\x55\x88\x0f\x0d\x7c\x4c\xe8" + "\xa1\x75\xa0\xd8\x06\x47\x14\xef" + "\xaa\x61\xcf\x26\x15\xad\xd8\xa3" + "\xaa\x75\xf2\x78\x4a\x5a\x61\xdf" + "\x8b\xc7\x04\xbc\xb2\x32\xd2\x7e" + "\x42\xee\xb4\x2f\x51\xff\x7b\x2e" + "\xd3\x02\xe8\xdc\x5d\x0d\x50\xdc" + "\xae\xb7\x46\xf9\xa8\xe6\xd0\x16" + "\xcc\xe6\x2c\x81\xc7\xad\xe9\xf0" + "\x05\x72\x6d\x3d\x0a\x7a\xa9\x02" + "\xac\x82\x93\x6e\xb6\x1c\x28\xfc" + "\x44\x12\xfb\x73\x77\xd4\x13\x39" + "\x29\x88\x8a\xf3\x5c\xa6\x36\xa0" + "\x2a\xed\x7e\xb1\x1d\xd6\x4c\x6b" + "\x41\x01\x18\x5d\x5d\x07\x97\xa6" + "\x4b\xef\x31\x18\xea\xac\xb1\x84" + "\x21\xed\xda\x86", + .rlen = 4100, + .np = 2, + .tap = { 4064, 36 }, + }, +}; + +static struct cipher_testvec aes_ctr_dec_tv_template[] = { + { /* From RFC 3686 */ + .key = "\xae\x68\x52\xf8\x12\x10\x67\xcc" + "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" + "\x00\x00\x00\x30", + .klen = 20, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" + "\x2d\x61\x75\xa3\x26\x13\x11\xb8", + .ilen = 16, + .result = "Single block msg", + .rlen = 16, + }, { + .key = "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" + "\x43\xd6\xce\x1f\x32\x53\x91\x63" + "\x00\x6c\xb6\xdb", + .klen = 20, + .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", + .input = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" + "\x79\x0d\x41\xee\x8e\xda\xd3\x88" + "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" + "\xfc\xe6\x30\xdf\x91\x41\xbe\x28", + .ilen = 32, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .rlen = 32, + }, { + .key = "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" + "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" + "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" + "\x00\x00\x00\x48", + .klen = 28, + .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", + .input = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" + "\x4e\x79\x35\xa0\x03\xcb\xe9\x28", + .ilen = 16, + .result = "Single block msg", + .rlen = 16, + }, { + .key = "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" + "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" + "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" + "\x00\x96\xb0\x3b", + .klen = 28, + .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", + .input = "\x45\x32\x43\xfc\x60\x9b\x23\x32" + "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" + "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" + "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00", + .ilen = 32, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .rlen = 32, + }, { + .key = "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" + "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" + "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" + "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" + "\x00\x00\x00\x60", + .klen = 36, + .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", + .input = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" + "\x56\x08\x63\xdc\x71\xe3\xe0\xc0", + .ilen = 16, + .result = "Single block msg", + .rlen = 16, + }, { + .key = "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" + "\x07\x96\x36\x58\x79\xef\xf8\x86" + "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" + "\x4b\x50\x59\x0c\x87\xa2\x38\x84" + "\x00\xfa\xac\x24", + .klen = 36, + .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", + .input = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" + "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" + "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" + "\x55\x30\x83\x1d\x93\x44\xaf\x1c", + .ilen = 32, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .rlen = 32, + }, +}; + +static struct aead_testvec aes_gcm_enc_tv_template[] = { + { /* From McGrew & Viega - http://citeseer.ist.psu.edu/656989.html */ + .key = zeroed_string, + .klen = 16, + .result = "\x58\xe2\xfc\xce\xfa\x7e\x30\x61" + "\x36\x7f\x1d\x57\xa4\xe7\x45\x5a", + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 16, + .input = zeroed_string, + .ilen = 16, + .result = "\x03\x88\xda\xce\x60\xb6\xa3\x92" + "\xf3\x28\xc2\xb9\x71\xb2\xfe\x78" + "\xab\x6e\x47\xd4\x2c\xec\x13\xbd" + "\xf5\x3a\x67\xb2\x12\x57\xbd\xdf", + .rlen = 32, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08", + .klen = 16, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", + .ilen = 64, + .result = "\x42\x83\x1e\xc2\x21\x77\x74\x24" + "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" + "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" + "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" + "\x21\xd5\x14\xb2\x54\x66\x93\x1c" + "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" + "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" + "\x3d\x58\xe0\x91\x47\x3f\x59\x85" + "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6" + "\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4", + .rlen = 80, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08", + .klen = 16, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39", + .ilen = 60, + .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", + .alen = 20, + .result = "\x42\x83\x1e\xc2\x21\x77\x74\x24" + "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" + "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" + "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" + "\x21\xd5\x14\xb2\x54\x66\x93\x1c" + "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" + "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" + "\x3d\x58\xe0\x91" + "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb" + "\x94\xfa\xe9\x5a\xe7\x12\x1a\x47", + .rlen = 76, + }, { + .key = zeroed_string, + .klen = 24, + .result = "\xcd\x33\xb2\x8a\xc7\x73\xf7\x4b" + "\xa0\x0e\xd1\xf3\x12\x57\x24\x35", + .rlen = 16, + }, { + .key = zeroed_string, + .klen = 24, + .input = zeroed_string, + .ilen = 16, + .result = "\x98\xe7\x24\x7c\x07\xf0\xfe\x41" + "\x1c\x26\x7e\x43\x84\xb0\xf6\x00" + "\x2f\xf5\x8d\x80\x03\x39\x27\xab" + "\x8e\xf4\xd4\x58\x75\x14\xf0\xfb", + .rlen = 32, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c", + .klen = 24, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", + .ilen = 64, + .result = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" + "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" + "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" + "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" + "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" + "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" + "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" + "\xcc\xda\x27\x10\xac\xad\xe2\x56" + "\x99\x24\xa7\xc8\x58\x73\x36\xbf" + "\xb1\x18\x02\x4d\xb8\x67\x4a\x14", + .rlen = 80, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c", + .klen = 24, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39", + .ilen = 60, + .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", + .alen = 20, + .result = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" + "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" + "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" + "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" + "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" + "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" + "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" + "\xcc\xda\x27\x10" + "\x25\x19\x49\x8e\x80\xf1\x47\x8f" + "\x37\xba\x55\xbd\x6d\x27\x61\x8c", + .rlen = 76, + .np = 2, + .tap = { 32, 28 }, + .anp = 2, + .atap = { 8, 12 } + }, { + .key = zeroed_string, + .klen = 32, + .result = "\x53\x0f\x8a\xfb\xc7\x45\x36\xb9" + "\xa9\x63\xb4\xf1\xc4\xcb\x73\x8b", + .rlen = 16, + } +}; + +static struct aead_testvec aes_gcm_dec_tv_template[] = { + { /* From McGrew & Viega - http://citeseer.ist.psu.edu/656989.html */ + .key = zeroed_string, + .klen = 32, + .input = "\xce\xa7\x40\x3d\x4d\x60\x6b\x6e" + "\x07\x4e\xc5\xd3\xba\xf3\x9d\x18" + "\xd0\xd1\xc8\xa7\x99\x99\x6b\xf0" + "\x26\x5b\x98\xb5\xd4\x8a\xb9\x19", + .ilen = 32, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08", + .klen = 32, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\x52\x2d\xc1\xf0\x99\x56\x7d\x07" + "\xf4\x7f\x37\xa3\x2a\x84\x42\x7d" + "\x64\x3a\x8c\xdc\xbf\xe5\xc0\xc9" + "\x75\x98\xa2\xbd\x25\x55\xd1\xaa" + "\x8c\xb0\x8e\x48\x59\x0d\xbb\x3d" + "\xa7\xb0\x8b\x10\x56\x82\x88\x38" + "\xc5\xf6\x1e\x63\x93\xba\x7a\x0a" + "\xbc\xc9\xf6\x62\x89\x80\x15\xad" + "\xb0\x94\xda\xc5\xd9\x34\x71\xbd" + "\xec\x1a\x50\x22\x70\xe3\xcc\x6c", + .ilen = 80, + .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", + .rlen = 64, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08", + .klen = 32, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\x52\x2d\xc1\xf0\x99\x56\x7d\x07" + "\xf4\x7f\x37\xa3\x2a\x84\x42\x7d" + "\x64\x3a\x8c\xdc\xbf\xe5\xc0\xc9" + "\x75\x98\xa2\xbd\x25\x55\xd1\xaa" + "\x8c\xb0\x8e\x48\x59\x0d\xbb\x3d" + "\xa7\xb0\x8b\x10\x56\x82\x88\x38" + "\xc5\xf6\x1e\x63\x93\xba\x7a\x0a" + "\xbc\xc9\xf6\x62" + "\x76\xfc\x6e\xce\x0f\x4e\x17\x68" + "\xcd\xdf\x88\x53\xbb\x2d\x55\x1b", + .ilen = 76, + .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", + .alen = 20, + .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39", + .rlen = 60, + .np = 2, + .tap = { 48, 28 }, + .anp = 3, + .atap = { 8, 8, 4 } + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08", + .klen = 16, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\x42\x83\x1e\xc2\x21\x77\x74\x24" + "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" + "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" + "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" + "\x21\xd5\x14\xb2\x54\x66\x93\x1c" + "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" + "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" + "\x3d\x58\xe0\x91\x47\x3f\x59\x85" + "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6" + "\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4", + .ilen = 80, + .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", + .rlen = 64, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08", + .klen = 16, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\x42\x83\x1e\xc2\x21\x77\x74\x24" + "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" + "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0" + "\x35\xc1\x7e\x23\x29\xac\xa1\x2e" + "\x21\xd5\x14\xb2\x54\x66\x93\x1c" + "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" + "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97" + "\x3d\x58\xe0\x91" + "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb" + "\x94\xfa\xe9\x5a\xe7\x12\x1a\x47", + .ilen = 76, + .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", + .alen = 20, + .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39", + .rlen = 60, + }, { + .key = zeroed_string, + .klen = 24, + .input = "\x98\xe7\x24\x7c\x07\xf0\xfe\x41" + "\x1c\x26\x7e\x43\x84\xb0\xf6\x00" + "\x2f\xf5\x8d\x80\x03\x39\x27\xab" + "\x8e\xf4\xd4\x58\x75\x14\xf0\xfb", + .ilen = 32, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c", + .klen = 24, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" + "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" + "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" + "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" + "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" + "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" + "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" + "\xcc\xda\x27\x10\xac\xad\xe2\x56" + "\x99\x24\xa7\xc8\x58\x73\x36\xbf" + "\xb1\x18\x02\x4d\xb8\x67\x4a\x14", + .ilen = 80, + .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39\x1a\xaf\xd2\x55", + .rlen = 64, + }, { + .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c" + "\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c", + .klen = 24, + .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad" + "\xde\xca\xf8\x88", + .input = "\x39\x80\xca\x0b\x3c\x00\xe8\x41" + "\xeb\x06\xfa\xc4\x87\x2a\x27\x57" + "\x85\x9e\x1c\xea\xa6\xef\xd9\x84" + "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" + "\x7d\x77\x3d\x00\xc1\x44\xc5\x25" + "\xac\x61\x9d\x18\xc8\x4a\x3f\x47" + "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9" + "\xcc\xda\x27\x10" + "\x25\x19\x49\x8e\x80\xf1\x47\x8f" + "\x37\xba\x55\xbd\x6d\x27\x61\x8c", + .ilen = 76, + .assoc = "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", + .alen = 20, + .result = "\xd9\x31\x32\x25\xf8\x84\x06\xe5" + "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda" + "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53" + "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57" + "\xba\x63\x7b\x39", + .rlen = 60, + } +}; + +static struct aead_testvec aes_ccm_enc_tv_template[] = { + { /* From RFC 3610 */ + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x03\x02\x01\x00" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", + .alen = 8, + .input = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e", + .ilen = 23, + .result = "\x58\x8c\x97\x9a\x61\xc6\x63\xd2" + "\xf0\x66\xd0\xc2\xc0\xf9\x89\x80" + "\x6d\x5f\x6b\x61\xda\xc3\x84\x17" + "\xe8\xd1\x2c\xfd\xf9\x26\xe0", + .rlen = 31, + }, { + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x07\x06\x05\x04" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b", + .alen = 12, + .input = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" + "\x14\x15\x16\x17\x18\x19\x1a\x1b" + "\x1c\x1d\x1e\x1f", + .ilen = 20, + .result = "\xdc\xf1\xfb\x7b\x5d\x9e\x23\xfb" + "\x9d\x4e\x13\x12\x53\x65\x8a\xd8" + "\x6e\xbd\xca\x3e\x51\xe8\x3f\x07" + "\x7d\x9c\x2d\x93", + .rlen = 28, + }, { + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x0b\x0a\x09\x08" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", + .alen = 8, + .input = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20", + .ilen = 25, + .result = "\x82\x53\x1a\x60\xcc\x24\x94\x5a" + "\x4b\x82\x79\x18\x1a\xb5\xc8\x4d" + "\xf2\x1c\xe7\xf9\xb7\x3f\x42\xe1" + "\x97\xea\x9c\x07\xe5\x6b\x5e\xb1" + "\x7e\x5f\x4e", + .rlen = 35, + }, { + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x0c\x0b\x0a\x09" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b", + .alen = 12, + .input = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" + "\x14\x15\x16\x17\x18\x19\x1a\x1b" + "\x1c\x1d\x1e", + .ilen = 19, + .result = "\x07\x34\x25\x94\x15\x77\x85\x15" + "\x2b\x07\x40\x98\x33\x0a\xbb\x14" + "\x1b\x94\x7b\x56\x6a\xa9\x40\x6b" + "\x4d\x99\x99\x88\xdd", + .rlen = 29, + }, { + .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" + "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", + .klen = 16, + .iv = "\x01\x00\x33\x56\x8e\xf7\xb2\x63" + "\x3c\x96\x96\x76\x6c\xfa\x00\x00", + .assoc = "\x63\x01\x8f\x76\xdc\x8a\x1b\xcb", + .alen = 8, + .input = "\x90\x20\xea\x6f\x91\xbd\xd8\x5a" + "\xfa\x00\x39\xba\x4b\xaf\xf9\xbf" + "\xb7\x9c\x70\x28\x94\x9c\xd0\xec", + .ilen = 24, + .result = "\x4c\xcb\x1e\x7c\xa9\x81\xbe\xfa" + "\xa0\x72\x6c\x55\xd3\x78\x06\x12" + "\x98\xc8\x5c\x92\x81\x4a\xbc\x33" + "\xc5\x2e\xe8\x1d\x7d\x77\xc0\x8a", + .rlen = 32, + }, { + .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" + "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", + .klen = 16, + .iv = "\x01\x00\xd5\x60\x91\x2d\x3f\x70" + "\x3c\x96\x96\x76\x6c\xfa\x00\x00", + .assoc = "\xcd\x90\x44\xd2\xb7\x1f\xdb\x81" + "\x20\xea\x60\xc0", + .alen = 12, + .input = "\x64\x35\xac\xba\xfb\x11\xa8\x2e" + "\x2f\x07\x1d\x7c\xa4\xa5\xeb\xd9" + "\x3a\x80\x3b\xa8\x7f", + .ilen = 21, + .result = "\x00\x97\x69\xec\xab\xdf\x48\x62" + "\x55\x94\xc5\x92\x51\xe6\x03\x57" + "\x22\x67\x5e\x04\xc8\x47\x09\x9e" + "\x5a\xe0\x70\x45\x51", + .rlen = 29, + }, { + .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" + "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", + .klen = 16, + .iv = "\x01\x00\x42\xff\xf8\xf1\x95\x1c" + "\x3c\x96\x96\x76\x6c\xfa\x00\x00", + .assoc = "\xd8\x5b\xc7\xe6\x9f\x94\x4f\xb8", + .alen = 8, + .input = "\x8a\x19\xb9\x50\xbc\xf7\x1a\x01" + "\x8e\x5e\x67\x01\xc9\x17\x87\x65" + "\x98\x09\xd6\x7d\xbe\xdd\x18", + .ilen = 23, + .result = "\xbc\x21\x8d\xaa\x94\x74\x27\xb6" + "\xdb\x38\x6a\x99\xac\x1a\xef\x23" + "\xad\xe0\xb5\x29\x39\xcb\x6a\x63" + "\x7c\xf9\xbe\xc2\x40\x88\x97\xc6" + "\xba", + .rlen = 33, + }, +}; + +static struct aead_testvec aes_ccm_dec_tv_template[] = { + { /* From RFC 3610 */ + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x03\x02\x01\x00" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", + .alen = 8, + .input = "\x58\x8c\x97\x9a\x61\xc6\x63\xd2" + "\xf0\x66\xd0\xc2\xc0\xf9\x89\x80" + "\x6d\x5f\x6b\x61\xda\xc3\x84\x17" + "\xe8\xd1\x2c\xfd\xf9\x26\xe0", + .ilen = 31, + .result = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e", + .rlen = 23, + }, { + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x07\x06\x05\x04" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b", + .alen = 12, + .input = "\xdc\xf1\xfb\x7b\x5d\x9e\x23\xfb" + "\x9d\x4e\x13\x12\x53\x65\x8a\xd8" + "\x6e\xbd\xca\x3e\x51\xe8\x3f\x07" + "\x7d\x9c\x2d\x93", + .ilen = 28, + .result = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" + "\x14\x15\x16\x17\x18\x19\x1a\x1b" + "\x1c\x1d\x1e\x1f", + .rlen = 20, + }, { + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x0b\x0a\x09\x08" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07", + .alen = 8, + .input = "\x82\x53\x1a\x60\xcc\x24\x94\x5a" + "\x4b\x82\x79\x18\x1a\xb5\xc8\x4d" + "\xf2\x1c\xe7\xf9\xb7\x3f\x42\xe1" + "\x97\xea\x9c\x07\xe5\x6b\x5e\xb1" + "\x7e\x5f\x4e", + .ilen = 35, + .result = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20", + .rlen = 25, + }, { + .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + .klen = 16, + .iv = "\x01\x00\x00\x00\x0c\x0b\x0a\x09" + "\xa0\xa1\xa2\xa3\xa4\xa5\x00\x00", + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b", + .alen = 12, + .input = "\x07\x34\x25\x94\x15\x77\x85\x15" + "\x2b\x07\x40\x98\x33\x0a\xbb\x14" + "\x1b\x94\x7b\x56\x6a\xa9\x40\x6b" + "\x4d\x99\x99\x88\xdd", + .ilen = 29, + .result = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13" + "\x14\x15\x16\x17\x18\x19\x1a\x1b" + "\x1c\x1d\x1e", + .rlen = 19, + }, { + .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" + "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", + .klen = 16, + .iv = "\x01\x00\x33\x56\x8e\xf7\xb2\x63" + "\x3c\x96\x96\x76\x6c\xfa\x00\x00", + .assoc = "\x63\x01\x8f\x76\xdc\x8a\x1b\xcb", + .alen = 8, + .input = "\x4c\xcb\x1e\x7c\xa9\x81\xbe\xfa" + "\xa0\x72\x6c\x55\xd3\x78\x06\x12" + "\x98\xc8\x5c\x92\x81\x4a\xbc\x33" + "\xc5\x2e\xe8\x1d\x7d\x77\xc0\x8a", + .ilen = 32, + .result = "\x90\x20\xea\x6f\x91\xbd\xd8\x5a" + "\xfa\x00\x39\xba\x4b\xaf\xf9\xbf" + "\xb7\x9c\x70\x28\x94\x9c\xd0\xec", + .rlen = 24, + }, { + .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" + "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", + .klen = 16, + .iv = "\x01\x00\xd5\x60\x91\x2d\x3f\x70" + "\x3c\x96\x96\x76\x6c\xfa\x00\x00", + .assoc = "\xcd\x90\x44\xd2\xb7\x1f\xdb\x81" + "\x20\xea\x60\xc0", + .alen = 12, + .input = "\x00\x97\x69\xec\xab\xdf\x48\x62" + "\x55\x94\xc5\x92\x51\xe6\x03\x57" + "\x22\x67\x5e\x04\xc8\x47\x09\x9e" + "\x5a\xe0\x70\x45\x51", + .ilen = 29, + .result = "\x64\x35\xac\xba\xfb\x11\xa8\x2e" + "\x2f\x07\x1d\x7c\xa4\xa5\xeb\xd9" + "\x3a\x80\x3b\xa8\x7f", + .rlen = 21, + }, { + .key = "\xd7\x82\x8d\x13\xb2\xb0\xbd\xc3" + "\x25\xa7\x62\x36\xdf\x93\xcc\x6b", + .klen = 16, + .iv = "\x01\x00\x42\xff\xf8\xf1\x95\x1c" + "\x3c\x96\x96\x76\x6c\xfa\x00\x00", + .assoc = "\xd8\x5b\xc7\xe6\x9f\x94\x4f\xb8", + .alen = 8, + .input = "\xbc\x21\x8d\xaa\x94\x74\x27\xb6" + "\xdb\x38\x6a\x99\xac\x1a\xef\x23" + "\xad\xe0\xb5\x29\x39\xcb\x6a\x63" + "\x7c\xf9\xbe\xc2\x40\x88\x97\xc6" + "\xba", + .ilen = 33, + .result = "\x8a\x19\xb9\x50\xbc\xf7\x1a\x01" + "\x8e\x5e\x67\x01\xc9\x17\x87\x65" + "\x98\x09\xd6\x7d\xbe\xdd\x18", + .rlen = 23, + }, +}; + +/* Cast5 test vectors from RFC 2144 */ +#define CAST5_ENC_TEST_VECTORS 3 +#define CAST5_DEC_TEST_VECTORS 3 + +static struct cipher_testvec cast5_enc_tv_template[] = { + { + .key = "\x01\x23\x45\x67\x12\x34\x56\x78" + "\x23\x45\x67\x89\x34\x56\x78\x9a", + .klen = 16, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ilen = 8, + .result = "\x23\x8b\x4f\xe5\x84\x7e\x44\xb2", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x12\x34\x56\x78" + "\x23\x45", + .klen = 10, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ilen = 8, + .result = "\xeb\x6a\x71\x1a\x2c\x02\x27\x1b", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x12", + .klen = 5, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ilen = 8, + .result = "\x7a\xc8\x16\xd1\x6e\x9b\x30\x2e", + .rlen = 8, + }, +}; + +static struct cipher_testvec cast5_dec_tv_template[] = { + { + .key = "\x01\x23\x45\x67\x12\x34\x56\x78" + "\x23\x45\x67\x89\x34\x56\x78\x9a", + .klen = 16, + .input = "\x23\x8b\x4f\xe5\x84\x7e\x44\xb2", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x12\x34\x56\x78" + "\x23\x45", + .klen = 10, + .input = "\xeb\x6a\x71\x1a\x2c\x02\x27\x1b", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x12", + .klen = 5, + .input = "\x7a\xc8\x16\xd1\x6e\x9b\x30\x2e", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .rlen = 8, + }, +}; + +/* + * ARC4 test vectors from OpenSSL + */ +#define ARC4_ENC_TEST_VECTORS 7 +#define ARC4_DEC_TEST_VECTORS 7 + +static struct cipher_testvec arc4_enc_tv_template[] = { + { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ilen = 8, + .result = "\x75\xb7\x87\x80\x99\xe0\xc5\x96", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 8, + .result = "\x74\x94\xc2\xe7\x10\x4b\x08\x79", + .rlen = 8, + }, { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 8, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 8, + .result = "\xde\x18\x89\x41\xa3\x37\x5d\x3a", + .rlen = 8, + }, { + .key = "\xef\x01\x23\x45", + .klen = 4, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00", + .ilen = 20, + .result = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" + "\xbd\x61\x5a\x11\x62\xe1\xc7\xba" + "\x36\xb6\x78\x58", + .rlen = 20, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + "\x12\x34\x56\x78", + .ilen = 28, + .result = "\x66\xa0\x94\x9f\x8a\xf7\xd6\x89" + "\x1f\x7f\x83\x2b\xa8\x33\xc0\x0c" + "\x89\x2e\xbe\x30\x14\x3c\xe2\x87" + "\x40\x01\x1e\xcf", + .rlen = 28, + }, { + .key = "\xef\x01\x23\x45", + .klen = 4, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00", + .ilen = 10, + .result = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" + "\xbd\x61", + .rlen = 10, + }, { + .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 16, + .input = "\x01\x23\x45\x67\x89\xAB\xCD\xEF", + .ilen = 8, + .result = "\x69\x72\x36\x59\x1B\x52\x42\xB1", + .rlen = 8, + }, +}; + +static struct cipher_testvec arc4_dec_tv_template[] = { + { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x75\xb7\x87\x80\x99\xe0\xc5\x96", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .rlen = 8, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x74\x94\xc2\xe7\x10\x4b\x08\x79", + .ilen = 8, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 8, + }, { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 8, + .input = "\xde\x18\x89\x41\xa3\x37\x5d\x3a", + .ilen = 8, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 8, + }, { + .key = "\xef\x01\x23\x45", + .klen = 4, + .input = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" + "\xbd\x61\x5a\x11\x62\xe1\xc7\xba" + "\x36\xb6\x78\x58", + .ilen = 20, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00", + .rlen = 20, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef", + .klen = 8, + .input = "\x66\xa0\x94\x9f\x8a\xf7\xd6\x89" + "\x1f\x7f\x83\x2b\xa8\x33\xc0\x0c" + "\x89\x2e\xbe\x30\x14\x3c\xe2\x87" + "\x40\x01\x1e\xcf", + .ilen = 28, + .result = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + "\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + "\x12\x34\x56\x78", + .rlen = 28, + }, { + .key = "\xef\x01\x23\x45", + .klen = 4, + .input = "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf" + "\xbd\x61", + .ilen = 10, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00", + .rlen = 10, + }, { + .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 16, + .input = "\x69\x72\x36\x59\x1B\x52\x42\xB1", + .ilen = 8, + .result = "\x01\x23\x45\x67\x89\xAB\xCD\xEF", + .rlen = 8, + }, +}; + +/* + * TEA test vectors + */ +#define TEA_ENC_TEST_VECTORS 4 +#define TEA_DEC_TEST_VECTORS 4 + +static struct cipher_testvec tea_enc_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = zeroed_string, + .ilen = 8, + .result = "\x0a\x3a\xea\x41\x40\xa9\xba\x94", + .rlen = 8, + }, { + .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" + "\x77\x5d\x0e\x26\x6c\x28\x78\x43", + .klen = 16, + .input = "\x74\x65\x73\x74\x20\x6d\x65\x2e", + .ilen = 8, + .result = "\x77\x5d\x2a\x6a\xf6\xce\x92\x09", + .rlen = 8, + }, { + .key = "\x09\x65\x43\x11\x66\x44\x39\x25" + "\x51\x3a\x16\x10\x0a\x08\x12\x6e", + .klen = 16, + .input = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" + "\x65\x73\x74\x5f\x76\x65\x63\x74", + .ilen = 16, + .result = "\xbe\x7a\xbb\x81\x95\x2d\x1f\x1e" + "\xdd\x89\xa1\x25\x04\x21\xdf\x95", + .rlen = 16, + }, { + .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" + "\x5d\x04\x16\x36\x15\x72\x63\x2f", + .klen = 16, + .input = "\x54\x65\x61\x20\x69\x73\x20\x67" + "\x6f\x6f\x64\x20\x66\x6f\x72\x20" + "\x79\x6f\x75\x21\x21\x21\x20\x72" + "\x65\x61\x6c\x6c\x79\x21\x21\x21", + .ilen = 32, + .result = "\xe0\x4d\x5d\x3c\xb7\x8c\x36\x47" + "\x94\x18\x95\x91\xa9\xfc\x49\xf8" + "\x44\xd1\x2d\xc2\x99\xb8\x08\x2a" + "\x07\x89\x73\xc2\x45\x92\xc6\x90", + .rlen = 32, + } +}; + +static struct cipher_testvec tea_dec_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = "\x0a\x3a\xea\x41\x40\xa9\xba\x94", + .ilen = 8, + .result = zeroed_string, + .rlen = 8, + }, { + .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" + "\x77\x5d\x0e\x26\x6c\x28\x78\x43", + .klen = 16, + .input = "\x77\x5d\x2a\x6a\xf6\xce\x92\x09", + .ilen = 8, + .result = "\x74\x65\x73\x74\x20\x6d\x65\x2e", + .rlen = 8, + }, { + .key = "\x09\x65\x43\x11\x66\x44\x39\x25" + "\x51\x3a\x16\x10\x0a\x08\x12\x6e", + .klen = 16, + .input = "\xbe\x7a\xbb\x81\x95\x2d\x1f\x1e" + "\xdd\x89\xa1\x25\x04\x21\xdf\x95", + .ilen = 16, + .result = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" + "\x65\x73\x74\x5f\x76\x65\x63\x74", + .rlen = 16, + }, { + .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" + "\x5d\x04\x16\x36\x15\x72\x63\x2f", + .klen = 16, + .input = "\xe0\x4d\x5d\x3c\xb7\x8c\x36\x47" + "\x94\x18\x95\x91\xa9\xfc\x49\xf8" + "\x44\xd1\x2d\xc2\x99\xb8\x08\x2a" + "\x07\x89\x73\xc2\x45\x92\xc6\x90", + .ilen = 32, + .result = "\x54\x65\x61\x20\x69\x73\x20\x67" + "\x6f\x6f\x64\x20\x66\x6f\x72\x20" + "\x79\x6f\x75\x21\x21\x21\x20\x72" + "\x65\x61\x6c\x6c\x79\x21\x21\x21", + .rlen = 32, + } +}; + +/* + * XTEA test vectors + */ +#define XTEA_ENC_TEST_VECTORS 4 +#define XTEA_DEC_TEST_VECTORS 4 + +static struct cipher_testvec xtea_enc_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = zeroed_string, + .ilen = 8, + .result = "\xd8\xd4\xe9\xde\xd9\x1e\x13\xf7", + .rlen = 8, + }, { + .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" + "\x77\x5d\x0e\x26\x6c\x28\x78\x43", + .klen = 16, + .input = "\x74\x65\x73\x74\x20\x6d\x65\x2e", + .ilen = 8, + .result = "\x94\xeb\xc8\x96\x84\x6a\x49\xa8", + .rlen = 8, + }, { + .key = "\x09\x65\x43\x11\x66\x44\x39\x25" + "\x51\x3a\x16\x10\x0a\x08\x12\x6e", + .klen = 16, + .input = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" + "\x65\x73\x74\x5f\x76\x65\x63\x74", + .ilen = 16, + .result = "\x3e\xce\xae\x22\x60\x56\xa8\x9d" + "\x77\x4d\xd4\xb4\x87\x24\xe3\x9a", + .rlen = 16, + }, { + .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" + "\x5d\x04\x16\x36\x15\x72\x63\x2f", + .klen = 16, + .input = "\x54\x65\x61\x20\x69\x73\x20\x67" + "\x6f\x6f\x64\x20\x66\x6f\x72\x20" + "\x79\x6f\x75\x21\x21\x21\x20\x72" + "\x65\x61\x6c\x6c\x79\x21\x21\x21", + .ilen = 32, + .result = "\x99\x81\x9f\x5d\x6f\x4b\x31\x3a" + "\x86\xff\x6f\xd0\xe3\x87\x70\x07" + "\x4d\xb8\xcf\xf3\x99\x50\xb3\xd4" + "\x73\xa2\xfa\xc9\x16\x59\x5d\x81", + .rlen = 32, + } +}; + +static struct cipher_testvec xtea_dec_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = "\xd8\xd4\xe9\xde\xd9\x1e\x13\xf7", + .ilen = 8, + .result = zeroed_string, + .rlen = 8, + }, { + .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" + "\x77\x5d\x0e\x26\x6c\x28\x78\x43", + .klen = 16, + .input = "\x94\xeb\xc8\x96\x84\x6a\x49\xa8", + .ilen = 8, + .result = "\x74\x65\x73\x74\x20\x6d\x65\x2e", + .rlen = 8, + }, { + .key = "\x09\x65\x43\x11\x66\x44\x39\x25" + "\x51\x3a\x16\x10\x0a\x08\x12\x6e", + .klen = 16, + .input = "\x3e\xce\xae\x22\x60\x56\xa8\x9d" + "\x77\x4d\xd4\xb4\x87\x24\xe3\x9a", + .ilen = 16, + .result = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" + "\x65\x73\x74\x5f\x76\x65\x63\x74", + .rlen = 16, + }, { + .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" + "\x5d\x04\x16\x36\x15\x72\x63\x2f", + .klen = 16, + .input = "\x99\x81\x9f\x5d\x6f\x4b\x31\x3a" + "\x86\xff\x6f\xd0\xe3\x87\x70\x07" + "\x4d\xb8\xcf\xf3\x99\x50\xb3\xd4" + "\x73\xa2\xfa\xc9\x16\x59\x5d\x81", + .ilen = 32, + .result = "\x54\x65\x61\x20\x69\x73\x20\x67" + "\x6f\x6f\x64\x20\x66\x6f\x72\x20" + "\x79\x6f\x75\x21\x21\x21\x20\x72" + "\x65\x61\x6c\x6c\x79\x21\x21\x21", + .rlen = 32, + } +}; + +/* + * KHAZAD test vectors. + */ +#define KHAZAD_ENC_TEST_VECTORS 5 +#define KHAZAD_DEC_TEST_VECTORS 5 + +static struct cipher_testvec khazad_enc_tv_template[] = { + { + .key = "\x80\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 16, + .input = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 8, + .result = "\x49\xa4\xce\x32\xac\x19\x0e\x3f", + .rlen = 8, + }, { + .key = "\x38\x38\x38\x38\x38\x38\x38\x38" + "\x38\x38\x38\x38\x38\x38\x38\x38", + .klen = 16, + .input = "\x38\x38\x38\x38\x38\x38\x38\x38", + .ilen = 8, + .result = "\x7e\x82\x12\xa1\xd9\x5b\xe4\xf9", + .rlen = 8, + }, { + .key = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2" + "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", + .klen = 16, + .input = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", + .ilen = 8, + .result = "\xaa\xbe\xc1\x95\xc5\x94\x1a\x9c", + .rlen = 8, + }, { + .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" + "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .klen = 16, + .input = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .ilen = 8, + .result = "\x04\x74\xf5\x70\x50\x16\xd3\xb8", + .rlen = 8, + }, { + .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" + "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .klen = 16, + .input = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" + "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .ilen = 16, + .result = "\x04\x74\xf5\x70\x50\x16\xd3\xb8" + "\x04\x74\xf5\x70\x50\x16\xd3\xb8", + .rlen = 16, + }, +}; + +static struct cipher_testvec khazad_dec_tv_template[] = { + { + .key = "\x80\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 16, + .input = "\x49\xa4\xce\x32\xac\x19\x0e\x3f", + .ilen = 8, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 8, + }, { + .key = "\x38\x38\x38\x38\x38\x38\x38\x38" + "\x38\x38\x38\x38\x38\x38\x38\x38", + .klen = 16, + .input = "\x7e\x82\x12\xa1\xd9\x5b\xe4\xf9", + .ilen = 8, + .result = "\x38\x38\x38\x38\x38\x38\x38\x38", + .rlen = 8, + }, { + .key = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2" + "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", + .klen = 16, + .input = "\xaa\xbe\xc1\x95\xc5\x94\x1a\x9c", + .ilen = 8, + .result = "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2", + .rlen = 8, + }, { + .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" + "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .klen = 16, + .input = "\x04\x74\xf5\x70\x50\x16\xd3\xb8", + .ilen = 8, + .result = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .rlen = 8, + }, { + .key = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" + "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .klen = 16, + .input = "\x04\x74\xf5\x70\x50\x16\xd3\xb8" + "\x04\x74\xf5\x70\x50\x16\xd3\xb8", + .ilen = 16, + .result = "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f" + "\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x2f", + .rlen = 16, + }, +}; + +/* + * Anubis test vectors. + */ + +#define ANUBIS_ENC_TEST_VECTORS 5 +#define ANUBIS_DEC_TEST_VECTORS 5 +#define ANUBIS_CBC_ENC_TEST_VECTORS 2 +#define ANUBIS_CBC_DEC_TEST_VECTORS 2 + +static struct cipher_testvec anubis_enc_tv_template[] = { + { + .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .klen = 16, + .input = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .ilen = 16, + .result = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" + "\x08\xb7\x52\x8e\x6e\x6e\x86\x90", + .rlen = 16, + }, { + + .key = "\x03\x03\x03\x03\x03\x03\x03\x03" + "\x03\x03\x03\x03\x03\x03\x03\x03" + "\x03\x03\x03\x03", + .klen = 20, + .input = "\x03\x03\x03\x03\x03\x03\x03\x03" + "\x03\x03\x03\x03\x03\x03\x03\x03", + .ilen = 16, + .result = "\xdb\xf1\x42\xf4\xd1\x8a\xc7\x49" + "\x87\x41\x6f\x82\x0a\x98\x64\xae", + .rlen = 16, + }, { + .key = "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24", + .klen = 28, + .input = "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24\x24\x24\x24\x24", + .ilen = 16, + .result = "\xfd\x1b\x4a\xe3\xbf\xf0\xad\x3d" + "\x06\xd3\x61\x27\xfd\x13\x9e\xde", + .rlen = 16, + }, { + .key = "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25", + .klen = 32, + .input = "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25", + .ilen = 16, + .result = "\x1a\x91\xfb\x2b\xb7\x78\x6b\xc4" + "\x17\xd9\xff\x40\x3b\x0e\xe5\xfe", + .rlen = 16, + }, { + .key = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .klen = 40, + .input = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .ilen = 16, + .result = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" + "\x9e\xc6\x84\x0f\x17\x21\x07\xee", + .rlen = 16, + }, +}; + +static struct cipher_testvec anubis_dec_tv_template[] = { + { + .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .klen = 16, + .input = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" + "\x08\xb7\x52\x8e\x6e\x6e\x86\x90", + .ilen = 16, + .result = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .rlen = 16, + }, { + + .key = "\x03\x03\x03\x03\x03\x03\x03\x03" + "\x03\x03\x03\x03\x03\x03\x03\x03" + "\x03\x03\x03\x03", + .klen = 20, + .input = "\xdb\xf1\x42\xf4\xd1\x8a\xc7\x49" + "\x87\x41\x6f\x82\x0a\x98\x64\xae", + .ilen = 16, + .result = "\x03\x03\x03\x03\x03\x03\x03\x03" + "\x03\x03\x03\x03\x03\x03\x03\x03", + .rlen = 16, + }, { + .key = "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24", + .klen = 28, + .input = "\xfd\x1b\x4a\xe3\xbf\xf0\xad\x3d" + "\x06\xd3\x61\x27\xfd\x13\x9e\xde", + .ilen = 16, + .result = "\x24\x24\x24\x24\x24\x24\x24\x24" + "\x24\x24\x24\x24\x24\x24\x24\x24", + .rlen = 16, + }, { + .key = "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25", + .klen = 32, + .input = "\x1a\x91\xfb\x2b\xb7\x78\x6b\xc4" + "\x17\xd9\xff\x40\x3b\x0e\xe5\xfe", + .ilen = 16, + .result = "\x25\x25\x25\x25\x25\x25\x25\x25" + "\x25\x25\x25\x25\x25\x25\x25\x25", + .rlen = 16, + }, { + .key = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .input = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" + "\x9e\xc6\x84\x0f\x17\x21\x07\xee", + .klen = 40, + .ilen = 16, + .result = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .rlen = 16, + }, +}; + +static struct cipher_testvec anubis_cbc_enc_tv_template[] = { + { + .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .klen = 16, + .input = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .ilen = 32, + .result = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" + "\x08\xb7\x52\x8e\x6e\x6e\x86\x90" + "\x86\xd8\xb5\x6f\x98\x5e\x8a\x66" + "\x4f\x1f\x78\xa1\xbb\x37\xf1\xbe", + .rlen = 32, + }, { + .key = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .klen = 40, + .input = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .ilen = 32, + .result = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" + "\x9e\xc6\x84\x0f\x17\x21\x07\xee" + "\xa2\xbc\x06\x98\xc6\x4b\xda\x75" + "\x2e\xaa\xbe\x58\xce\x01\x5b\xc7", + .rlen = 32, + }, +}; + +static struct cipher_testvec anubis_cbc_dec_tv_template[] = { + { + .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .klen = 16, + .input = "\x6d\xc5\xda\xa2\x26\x7d\x62\x6f" + "\x08\xb7\x52\x8e\x6e\x6e\x86\x90" + "\x86\xd8\xb5\x6f\x98\x5e\x8a\x66" + "\x4f\x1f\x78\xa1\xbb\x37\xf1\xbe", + .ilen = 32, + .result = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", + .rlen = 32, + }, { + .key = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .klen = 40, + .input = "\xa5\x2c\x85\x6f\x9c\xba\xa0\x97" + "\x9e\xc6\x84\x0f\x17\x21\x07\xee" + "\xa2\xbc\x06\x98\xc6\x4b\xda\x75" + "\x2e\xaa\xbe\x58\xce\x01\x5b\xc7", + .ilen = 32, + .result = "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35" + "\x35\x35\x35\x35\x35\x35\x35\x35", + .rlen = 32, + }, +}; + +/* + * XETA test vectors + */ +#define XETA_ENC_TEST_VECTORS 4 +#define XETA_DEC_TEST_VECTORS 4 + +static struct cipher_testvec xeta_enc_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = zeroed_string, + .ilen = 8, + .result = "\xaa\x22\x96\xe5\x6c\x61\xf3\x45", + .rlen = 8, + }, { + .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" + "\x77\x5d\x0e\x26\x6c\x28\x78\x43", + .klen = 16, + .input = "\x74\x65\x73\x74\x20\x6d\x65\x2e", + .ilen = 8, + .result = "\x82\x3e\xeb\x35\xdc\xdd\xd9\xc3", + .rlen = 8, + }, { + .key = "\x09\x65\x43\x11\x66\x44\x39\x25" + "\x51\x3a\x16\x10\x0a\x08\x12\x6e", + .klen = 16, + .input = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" + "\x65\x73\x74\x5f\x76\x65\x63\x74", + .ilen = 16, + .result = "\xe2\x04\xdb\xf2\x89\x85\x9e\xea" + "\x61\x35\xaa\xed\xb5\xcb\x71\x2c", + .rlen = 16, + }, { + .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" + "\x5d\x04\x16\x36\x15\x72\x63\x2f", + .klen = 16, + .input = "\x54\x65\x61\x20\x69\x73\x20\x67" + "\x6f\x6f\x64\x20\x66\x6f\x72\x20" + "\x79\x6f\x75\x21\x21\x21\x20\x72" + "\x65\x61\x6c\x6c\x79\x21\x21\x21", + .ilen = 32, + .result = "\x0b\x03\xcd\x8a\xbe\x95\xfd\xb1" + "\xc1\x44\x91\x0b\xa5\xc9\x1b\xb4" + "\xa9\xda\x1e\x9e\xb1\x3e\x2a\x8f" + "\xea\xa5\x6a\x85\xd1\xf4\xa8\xa5", + .rlen = 32, + } +}; + +static struct cipher_testvec xeta_dec_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = "\xaa\x22\x96\xe5\x6c\x61\xf3\x45", + .ilen = 8, + .result = zeroed_string, + .rlen = 8, + }, { + .key = "\x2b\x02\x05\x68\x06\x14\x49\x76" + "\x77\x5d\x0e\x26\x6c\x28\x78\x43", + .klen = 16, + .input = "\x82\x3e\xeb\x35\xdc\xdd\xd9\xc3", + .ilen = 8, + .result = "\x74\x65\x73\x74\x20\x6d\x65\x2e", + .rlen = 8, + }, { + .key = "\x09\x65\x43\x11\x66\x44\x39\x25" + "\x51\x3a\x16\x10\x0a\x08\x12\x6e", + .klen = 16, + .input = "\xe2\x04\xdb\xf2\x89\x85\x9e\xea" + "\x61\x35\xaa\xed\xb5\xcb\x71\x2c", + .ilen = 16, + .result = "\x6c\x6f\x6e\x67\x65\x72\x5f\x74" + "\x65\x73\x74\x5f\x76\x65\x63\x74", + .rlen = 16, + }, { + .key = "\x4d\x76\x32\x17\x05\x3f\x75\x2c" + "\x5d\x04\x16\x36\x15\x72\x63\x2f", + .klen = 16, + .input = "\x0b\x03\xcd\x8a\xbe\x95\xfd\xb1" + "\xc1\x44\x91\x0b\xa5\xc9\x1b\xb4" + "\xa9\xda\x1e\x9e\xb1\x3e\x2a\x8f" + "\xea\xa5\x6a\x85\xd1\xf4\xa8\xa5", + .ilen = 32, + .result = "\x54\x65\x61\x20\x69\x73\x20\x67" + "\x6f\x6f\x64\x20\x66\x6f\x72\x20" + "\x79\x6f\x75\x21\x21\x21\x20\x72" + "\x65\x61\x6c\x6c\x79\x21\x21\x21", + .rlen = 32, + } +}; + +/* + * FCrypt test vectors + */ +#define FCRYPT_ENC_TEST_VECTORS ARRAY_SIZE(fcrypt_pcbc_enc_tv_template) +#define FCRYPT_DEC_TEST_VECTORS ARRAY_SIZE(fcrypt_pcbc_dec_tv_template) + +static struct cipher_testvec fcrypt_pcbc_enc_tv_template[] = { + { /* http://www.openafs.org/pipermail/openafs-devel/2000-December/005320.html */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 8, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 8, + .result = "\x0E\x09\x00\xC7\x3E\xF7\xED\x41", + .rlen = 8, + }, { + .key = "\x11\x44\x77\xAA\xDD\x00\x33\x66", + .klen = 8, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0", + .ilen = 8, + .result = "\xD8\xED\x78\x74\x77\xEC\x06\x80", + .rlen = 8, + }, { /* From Arla */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .klen = 8, + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .input = "The quick brown fox jumps over the lazy dogs.\0\0", + .ilen = 48, + .result = "\x00\xf0\x0e\x11\x75\xe6\x23\x82" + "\xee\xac\x98\x62\x44\x51\xe4\x84" + "\xc3\x59\xd8\xaa\x64\x60\xae\xf7" + "\xd2\xd9\x13\x79\x72\xa3\x45\x03" + "\x23\xb5\x62\xd7\x0c\xf5\x27\xd1" + "\xf8\x91\x3c\xac\x44\x22\x92\xef", + .rlen = 48, + }, { + .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 8, + .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .input = "The quick brown fox jumps over the lazy dogs.\0\0", + .ilen = 48, + .result = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" + "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" + "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" + "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" + "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" + "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", + .rlen = 48, + }, { /* split-page version */ + .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 8, + .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .input = "The quick brown fox jumps over the lazy dogs.\0\0", + .ilen = 48, + .result = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" + "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" + "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" + "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" + "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" + "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", + .rlen = 48, + .np = 2, + .tap = { 20, 28 }, + } +}; + +static struct cipher_testvec fcrypt_pcbc_dec_tv_template[] = { + { /* http://www.openafs.org/pipermail/openafs-devel/2000-December/005320.html */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 8, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x0E\x09\x00\xC7\x3E\xF7\xED\x41", + .ilen = 8, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 8, + }, { + .key = "\x11\x44\x77\xAA\xDD\x00\x33\x66", + .klen = 8, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xD8\xED\x78\x74\x77\xEC\x06\x80", + .ilen = 8, + .result = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0", + .rlen = 8, + }, { /* From Arla */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .klen = 8, + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .input = "\x00\xf0\x0e\x11\x75\xe6\x23\x82" + "\xee\xac\x98\x62\x44\x51\xe4\x84" + "\xc3\x59\xd8\xaa\x64\x60\xae\xf7" + "\xd2\xd9\x13\x79\x72\xa3\x45\x03" + "\x23\xb5\x62\xd7\x0c\xf5\x27\xd1" + "\xf8\x91\x3c\xac\x44\x22\x92\xef", + .ilen = 48, + .result = "The quick brown fox jumps over the lazy dogs.\0\0", + .rlen = 48, + }, { + .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 8, + .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .input = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" + "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" + "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" + "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" + "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" + "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", + .ilen = 48, + .result = "The quick brown fox jumps over the lazy dogs.\0\0", + .rlen = 48, + }, { /* split-page version */ + .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 8, + .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .input = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" + "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" + "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" + "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" + "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" + "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", + .ilen = 48, + .result = "The quick brown fox jumps over the lazy dogs.\0\0", + .rlen = 48, + .np = 2, + .tap = { 20, 28 }, + } +}; + +/* + * CAMELLIA test vectors. + */ +#define CAMELLIA_ENC_TEST_VECTORS 3 +#define CAMELLIA_DEC_TEST_VECTORS 3 +#define CAMELLIA_CBC_ENC_TEST_VECTORS 2 +#define CAMELLIA_CBC_DEC_TEST_VECTORS 2 + +static struct cipher_testvec camellia_enc_tv_template[] = { + { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 16, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 16, + .result = "\x67\x67\x31\x38\x54\x96\x69\x73" + "\x08\x57\x06\x56\x48\xea\xbe\x43", + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77", + .klen = 24, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 16, + .result = "\xb4\x99\x34\x01\xb3\xe9\x96\xf8" + "\x4e\xe5\xce\xe7\xd7\x9b\x09\xb9", + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .klen = 32, + .input = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ilen = 16, + .result = "\x9a\xcc\x23\x7d\xff\x16\xd7\x6c" + "\x20\xef\x7c\x91\x9e\x3a\x75\x09", + .rlen = 16, + }, +}; + +static struct cipher_testvec camellia_dec_tv_template[] = { + { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .klen = 16, + .input = "\x67\x67\x31\x38\x54\x96\x69\x73" + "\x08\x57\x06\x56\x48\xea\xbe\x43", + .ilen = 16, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77", + .klen = 24, + .input = "\xb4\x99\x34\x01\xb3\xe9\x96\xf8" + "\x4e\xe5\xce\xe7\xd7\x9b\x09\xb9", + .ilen = 16, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 16, + }, { + .key = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .klen = 32, + .input = "\x9a\xcc\x23\x7d\xff\x16\xd7\x6c" + "\x20\xef\x7c\x91\x9e\x3a\x75\x09", + .ilen = 16, + .result = "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .rlen = 16, + }, +}; + +static struct cipher_testvec camellia_cbc_enc_tv_template[] = { + { + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .klen = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .input = "Single block msg", + .ilen = 16, + .result = "\xea\x32\x12\x76\x3b\x50\x10\xe7" + "\x18\xf6\xfd\x5d\xf6\x8f\x13\x51", + .rlen = 16, + }, { + .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" + "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", + .klen = 16, + .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" + "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .ilen = 32, + .result = "\xa5\xdf\x6e\x50\xda\x70\x6c\x01" + "\x4a\xab\xf3\xf2\xd6\xfc\x6c\xfd" + "\x19\xb4\x3e\x57\x1c\x02\x5e\xa0" + "\x15\x78\xe0\x5e\xf2\xcb\x87\x16", + .rlen = 32, + }, +}; + +static struct cipher_testvec camellia_cbc_dec_tv_template[] = { + { + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .klen = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .input = "\xea\x32\x12\x76\x3b\x50\x10\xe7" + "\x18\xf6\xfd\x5d\xf6\x8f\x13\x51", + .ilen = 16, + .result = "Single block msg", + .rlen = 16, + }, { + .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" + "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", + .klen = 16, + .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" + "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", + .input = "\xa5\xdf\x6e\x50\xda\x70\x6c\x01" + "\x4a\xab\xf3\xf2\xd6\xfc\x6c\xfd" + "\x19\xb4\x3e\x57\x1c\x02\x5e\xa0" + "\x15\x78\xe0\x5e\xf2\xcb\x87\x16", + .ilen = 32, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .rlen = 32, + }, +}; + +/* + * SEED test vectors + */ +#define SEED_ENC_TEST_VECTORS 4 +#define SEED_DEC_TEST_VECTORS 4 + +static struct cipher_testvec seed_enc_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .ilen = 16, + .result = "\x5e\xba\xc6\xe0\x05\x4e\x16\x68" + "\x19\xaf\xf1\xcc\x6d\x34\x6c\xdb", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = zeroed_string, + .ilen = 16, + .result = "\xc1\x1f\x22\xf2\x01\x40\x50\x50" + "\x84\x48\x35\x97\xe4\x37\x0f\x43", + .rlen = 16, + }, { + .key = "\x47\x06\x48\x08\x51\xe6\x1b\xe8" + "\x5d\x74\xbf\xb3\xfd\x95\x61\x85", + .klen = 16, + .input = "\x83\xa2\xf8\xa2\x88\x64\x1f\xb9" + "\xa4\xe9\xa5\xcc\x2f\x13\x1c\x7d", + .ilen = 16, + .result = "\xee\x54\xd1\x3e\xbc\xae\x70\x6d" + "\x22\x6b\xc3\x14\x2c\xd4\x0d\x4a", + .rlen = 16, + }, { + .key = "\x28\xdb\xc3\xbc\x49\xff\xd8\x7d" + "\xcf\xa5\x09\xb1\x1d\x42\x2b\xe7", + .klen = 16, + .input = "\xb4\x1e\x6b\xe2\xeb\xa8\x4a\x14" + "\x8e\x2e\xed\x84\x59\x3c\x5e\xc7", + .ilen = 16, + .result = "\x9b\x9b\x7b\xfc\xd1\x81\x3c\xb9" + "\x5d\x0b\x36\x18\xf4\x0f\x51\x22", + .rlen = 16, + } +}; + +static struct cipher_testvec seed_dec_tv_template[] = { + { + .key = zeroed_string, + .klen = 16, + .input = "\x5e\xba\xc6\xe0\x05\x4e\x16\x68" + "\x19\xaf\xf1\xcc\x6d\x34\x6c\xdb", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .rlen = 16, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\xc1\x1f\x22\xf2\x01\x40\x50\x50" + "\x84\x48\x35\x97\xe4\x37\x0f\x43", + .ilen = 16, + .result = zeroed_string, + .rlen = 16, + }, { + .key = "\x47\x06\x48\x08\x51\xe6\x1b\xe8" + "\x5d\x74\xbf\xb3\xfd\x95\x61\x85", + .klen = 16, + .input = "\xee\x54\xd1\x3e\xbc\xae\x70\x6d" + "\x22\x6b\xc3\x14\x2c\xd4\x0d\x4a", + .ilen = 16, + .result = "\x83\xa2\xf8\xa2\x88\x64\x1f\xb9" + "\xa4\xe9\xa5\xcc\x2f\x13\x1c\x7d", + .rlen = 16, + }, { + .key = "\x28\xdb\xc3\xbc\x49\xff\xd8\x7d" + "\xcf\xa5\x09\xb1\x1d\x42\x2b\xe7", + .klen = 16, + .input = "\x9b\x9b\x7b\xfc\xd1\x81\x3c\xb9" + "\x5d\x0b\x36\x18\xf4\x0f\x51\x22", + .ilen = 16, + .result = "\xb4\x1e\x6b\xe2\xeb\xa8\x4a\x14" + "\x8e\x2e\xed\x84\x59\x3c\x5e\xc7", + .rlen = 16, + } +}; + +#define SALSA20_STREAM_ENC_TEST_VECTORS 5 +static struct cipher_testvec salsa20_stream_enc_tv_template[] = { + /* + * Testvectors from verified.test-vectors submitted to ECRYPT. + * They are truncated to size 39, 64, 111, 129 to test a variety + * of input length. + */ + { /* Set 3, vector 0 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .ilen = 39, + .result = "\x2D\xD5\xC3\xF7\xBA\x2B\x20\xF7" + "\x68\x02\x41\x0C\x68\x86\x88\x89" + "\x5A\xD8\xC1\xBD\x4E\xA6\xC9\xB1" + "\x40\xFB\x9B\x90\xE2\x10\x49\xBF" + "\x58\x3F\x52\x79\x70\xEB\xC1", + .rlen = 39, + }, { /* Set 5, vector 0 */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 16, + .iv = "\x80\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 64, + .result = "\xB6\x6C\x1E\x44\x46\xDD\x95\x57" + "\xE5\x78\xE2\x23\xB0\xB7\x68\x01" + "\x7B\x23\xB2\x67\xBB\x02\x34\xAE" + "\x46\x26\xBF\x44\x3F\x21\x97\x76" + "\x43\x6F\xB1\x9F\xD0\xE8\x86\x6F" + "\xCD\x0D\xE9\xA9\x53\x8F\x4A\x09" + "\xCA\x9A\xC0\x73\x2E\x30\xBC\xF9" + "\x8E\x4F\x13\xE4\xB9\xE2\x01\xD9", + .rlen = 64, + }, { /* Set 3, vector 27 */ + .key = "\x1B\x1C\x1D\x1E\x1F\x20\x21\x22" + "\x23\x24\x25\x26\x27\x28\x29\x2A" + "\x2B\x2C\x2D\x2E\x2F\x30\x31\x32" + "\x33\x34\x35\x36\x37\x38\x39\x3A", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .ilen = 111, + .result = "\xAE\x39\x50\x8E\xAC\x9A\xEC\xE7" + "\xBF\x97\xBB\x20\xB9\xDE\xE4\x1F" + "\x87\xD9\x47\xF8\x28\x91\x35\x98" + "\xDB\x72\xCC\x23\x29\x48\x56\x5E" + "\x83\x7E\x0B\xF3\x7D\x5D\x38\x7B" + "\x2D\x71\x02\xB4\x3B\xB5\xD8\x23" + "\xB0\x4A\xDF\x3C\xEC\xB6\xD9\x3B" + "\x9B\xA7\x52\xBE\xC5\xD4\x50\x59" + "\x15\x14\xB4\x0E\x40\xE6\x53\xD1" + "\x83\x9C\x5B\xA0\x92\x29\x6B\x5E" + "\x96\x5B\x1E\x2F\xD3\xAC\xC1\x92" + "\xB1\x41\x3F\x19\x2F\xC4\x3B\xC6" + "\x95\x46\x45\x54\xE9\x75\x03\x08" + "\x44\xAF\xE5\x8A\x81\x12\x09", + .rlen = 111, + }, { /* Set 5, vector 27 */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 32, + .iv = "\x00\x00\x00\x10\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00", + .ilen = 129, + .result = "\xD2\xDB\x1A\x5C\xF1\xC1\xAC\xDB" + "\xE8\x1A\x7A\x43\x40\xEF\x53\x43" + "\x5E\x7F\x4B\x1A\x50\x52\x3F\x8D" + "\x28\x3D\xCF\x85\x1D\x69\x6E\x60" + "\xF2\xDE\x74\x56\x18\x1B\x84\x10" + "\xD4\x62\xBA\x60\x50\xF0\x61\xF2" + "\x1C\x78\x7F\xC1\x24\x34\xAF\x58" + "\xBF\x2C\x59\xCA\x90\x77\xF3\xB0" + "\x5B\x4A\xDF\x89\xCE\x2C\x2F\xFC" + "\x67\xF0\xE3\x45\xE8\xB3\xB3\x75" + "\xA0\x95\x71\xA1\x29\x39\x94\xCA" + "\x45\x2F\xBD\xCB\x10\xB6\xBE\x9F" + "\x8E\xF9\xB2\x01\x0A\x5A\x0A\xB7" + "\x6B\x9D\x70\x8E\x4B\xD6\x2F\xCD" + "\x2E\x40\x48\x75\xE9\xE2\x21\x45" + "\x0B\xC9\xB6\xB5\x66\xBC\x9A\x59" + "\x5A", + .rlen = 129, + }, { /* large test vector generated using Crypto++ */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x03\x06\x09\x0c\x0f\x12\x15" + "\x18\x1b\x1e\x21\x24\x27\x2a\x2d" + "\x30\x33\x36\x39\x3c\x3f\x42\x45" + "\x48\x4b\x4e\x51\x54\x57\x5a\x5d" + "\x60\x63\x66\x69\x6c\x6f\x72\x75" + "\x78\x7b\x7e\x81\x84\x87\x8a\x8d" + "\x90\x93\x96\x99\x9c\x9f\xa2\xa5" + "\xa8\xab\xae\xb1\xb4\xb7\xba\xbd" + "\xc0\xc3\xc6\xc9\xcc\xcf\xd2\xd5" + "\xd8\xdb\xde\xe1\xe4\xe7\xea\xed" + "\xf0\xf3\xf6\xf9\xfc\xff\x02\x05" + "\x08\x0b\x0e\x11\x14\x17\x1a\x1d" + "\x20\x23\x26\x29\x2c\x2f\x32\x35" + "\x38\x3b\x3e\x41\x44\x47\x4a\x4d" + "\x50\x53\x56\x59\x5c\x5f\x62\x65" + "\x68\x6b\x6e\x71\x74\x77\x7a\x7d" + "\x80\x83\x86\x89\x8c\x8f\x92\x95" + "\x98\x9b\x9e\xa1\xa4\xa7\xaa\xad" + "\xb0\xb3\xb6\xb9\xbc\xbf\xc2\xc5" + "\xc8\xcb\xce\xd1\xd4\xd7\xda\xdd" + "\xe0\xe3\xe6\xe9\xec\xef\xf2\xf5" + "\xf8\xfb\xfe\x01\x04\x07\x0a\x0d" + "\x10\x13\x16\x19\x1c\x1f\x22\x25" + "\x28\x2b\x2e\x31\x34\x37\x3a\x3d" + "\x40\x43\x46\x49\x4c\x4f\x52\x55" + "\x58\x5b\x5e\x61\x64\x67\x6a\x6d" + "\x70\x73\x76\x79\x7c\x7f\x82\x85" + "\x88\x8b\x8e\x91\x94\x97\x9a\x9d" + "\xa0\xa3\xa6\xa9\xac\xaf\xb2\xb5" + "\xb8\xbb\xbe\xc1\xc4\xc7\xca\xcd" + "\xd0\xd3\xd6\xd9\xdc\xdf\xe2\xe5" + "\xe8\xeb\xee\xf1\xf4\xf7\xfa\xfd" + "\x00\x05\x0a\x0f\x14\x19\x1e\x23" + "\x28\x2d\x32\x37\x3c\x41\x46\x4b" + "\x50\x55\x5a\x5f\x64\x69\x6e\x73" + "\x78\x7d\x82\x87\x8c\x91\x96\x9b" + "\xa0\xa5\xaa\xaf\xb4\xb9\xbe\xc3" + "\xc8\xcd\xd2\xd7\xdc\xe1\xe6\xeb" + "\xf0\xf5\xfa\xff\x04\x09\x0e\x13" + "\x18\x1d\x22\x27\x2c\x31\x36\x3b" + "\x40\x45\x4a\x4f\x54\x59\x5e\x63" + "\x68\x6d\x72\x77\x7c\x81\x86\x8b" + "\x90\x95\x9a\x9f\xa4\xa9\xae\xb3" + "\xb8\xbd\xc2\xc7\xcc\xd1\xd6\xdb" + "\xe0\xe5\xea\xef\xf4\xf9\xfe\x03" + "\x08\x0d\x12\x17\x1c\x21\x26\x2b" + "\x30\x35\x3a\x3f\x44\x49\x4e\x53" + "\x58\x5d\x62\x67\x6c\x71\x76\x7b" + "\x80\x85\x8a\x8f\x94\x99\x9e\xa3" + "\xa8\xad\xb2\xb7\xbc\xc1\xc6\xcb" + "\xd0\xd5\xda\xdf\xe4\xe9\xee\xf3" + "\xf8\xfd\x02\x07\x0c\x11\x16\x1b" + "\x20\x25\x2a\x2f\x34\x39\x3e\x43" + "\x48\x4d\x52\x57\x5c\x61\x66\x6b" + "\x70\x75\x7a\x7f\x84\x89\x8e\x93" + "\x98\x9d\xa2\xa7\xac\xb1\xb6\xbb" + "\xc0\xc5\xca\xcf\xd4\xd9\xde\xe3" + "\xe8\xed\xf2\xf7\xfc\x01\x06\x0b" + "\x10\x15\x1a\x1f\x24\x29\x2e\x33" + "\x38\x3d\x42\x47\x4c\x51\x56\x5b" + "\x60\x65\x6a\x6f\x74\x79\x7e\x83" + "\x88\x8d\x92\x97\x9c\xa1\xa6\xab" + "\xb0\xb5\xba\xbf\xc4\xc9\xce\xd3" + "\xd8\xdd\xe2\xe7\xec\xf1\xf6\xfb" + "\x00\x07\x0e\x15\x1c\x23\x2a\x31" + "\x38\x3f\x46\x4d\x54\x5b\x62\x69" + "\x70\x77\x7e\x85\x8c\x93\x9a\xa1" + "\xa8\xaf\xb6\xbd\xc4\xcb\xd2\xd9" + "\xe0\xe7\xee\xf5\xfc\x03\x0a\x11" + "\x18\x1f\x26\x2d\x34\x3b\x42\x49" + "\x50\x57\x5e\x65\x6c\x73\x7a\x81" + "\x88\x8f\x96\x9d\xa4\xab\xb2\xb9" + "\xc0\xc7\xce\xd5\xdc\xe3\xea\xf1" + "\xf8\xff\x06\x0d\x14\x1b\x22\x29" + "\x30\x37\x3e\x45\x4c\x53\x5a\x61" + "\x68\x6f\x76\x7d\x84\x8b\x92\x99" + "\xa0\xa7\xae\xb5\xbc\xc3\xca\xd1" + "\xd8\xdf\xe6\xed\xf4\xfb\x02\x09" + "\x10\x17\x1e\x25\x2c\x33\x3a\x41" + "\x48\x4f\x56\x5d\x64\x6b\x72\x79" + "\x80\x87\x8e\x95\x9c\xa3\xaa\xb1" + "\xb8\xbf\xc6\xcd\xd4\xdb\xe2\xe9" + "\xf0\xf7\xfe\x05\x0c\x13\x1a\x21" + "\x28\x2f\x36\x3d\x44\x4b\x52\x59" + "\x60\x67\x6e\x75\x7c\x83\x8a\x91" + "\x98\x9f\xa6\xad\xb4\xbb\xc2\xc9" + "\xd0\xd7\xde\xe5\xec\xf3\xfa\x01" + "\x08\x0f\x16\x1d\x24\x2b\x32\x39" + "\x40\x47\x4e\x55\x5c\x63\x6a\x71" + "\x78\x7f\x86\x8d\x94\x9b\xa2\xa9" + "\xb0\xb7\xbe\xc5\xcc\xd3\xda\xe1" + "\xe8\xef\xf6\xfd\x04\x0b\x12\x19" + "\x20\x27\x2e\x35\x3c\x43\x4a\x51" + "\x58\x5f\x66\x6d\x74\x7b\x82\x89" + "\x90\x97\x9e\xa5\xac\xb3\xba\xc1" + "\xc8\xcf\xd6\xdd\xe4\xeb\xf2\xf9" + "\x00\x09\x12\x1b\x24\x2d\x36\x3f" + "\x48\x51\x5a\x63\x6c\x75\x7e\x87" + "\x90\x99\xa2\xab\xb4\xbd\xc6\xcf" + "\xd8\xe1\xea\xf3\xfc\x05\x0e\x17" + "\x20\x29\x32\x3b\x44\x4d\x56\x5f" + "\x68\x71\x7a\x83\x8c\x95\x9e\xa7" + "\xb0\xb9\xc2\xcb\xd4\xdd\xe6\xef" + "\xf8\x01\x0a\x13\x1c\x25\x2e\x37" + "\x40\x49\x52\x5b\x64\x6d\x76\x7f" + "\x88\x91\x9a\xa3\xac\xb5\xbe\xc7" + "\xd0\xd9\xe2\xeb\xf4\xfd\x06\x0f" + "\x18\x21\x2a\x33\x3c\x45\x4e\x57" + "\x60\x69\x72\x7b\x84\x8d\x96\x9f" + "\xa8\xb1\xba\xc3\xcc\xd5\xde\xe7" + "\xf0\xf9\x02\x0b\x14\x1d\x26\x2f" + "\x38\x41\x4a\x53\x5c\x65\x6e\x77" + "\x80\x89\x92\x9b\xa4\xad\xb6\xbf" + "\xc8\xd1\xda\xe3\xec\xf5\xfe\x07" + "\x10\x19\x22\x2b\x34\x3d\x46\x4f" + "\x58\x61\x6a\x73\x7c\x85\x8e\x97" + "\xa0\xa9\xb2\xbb\xc4\xcd\xd6\xdf" + "\xe8\xf1\xfa\x03\x0c\x15\x1e\x27" + "\x30\x39\x42\x4b\x54\x5d\x66\x6f" + "\x78\x81\x8a\x93\x9c\xa5\xae\xb7" + "\xc0\xc9\xd2\xdb\xe4\xed\xf6\xff" + "\x08\x11\x1a\x23\x2c\x35\x3e\x47" + "\x50\x59\x62\x6b\x74\x7d\x86\x8f" + "\x98\xa1\xaa\xb3\xbc\xc5\xce\xd7" + "\xe0\xe9\xf2\xfb\x04\x0d\x16\x1f" + "\x28\x31\x3a\x43\x4c\x55\x5e\x67" + "\x70\x79\x82\x8b\x94\x9d\xa6\xaf" + "\xb8\xc1\xca\xd3\xdc\xe5\xee\xf7" + "\x00\x0b\x16\x21\x2c\x37\x42\x4d" + "\x58\x63\x6e\x79\x84\x8f\x9a\xa5" + "\xb0\xbb\xc6\xd1\xdc\xe7\xf2\xfd" + "\x08\x13\x1e\x29\x34\x3f\x4a\x55" + "\x60\x6b\x76\x81\x8c\x97\xa2\xad" + "\xb8\xc3\xce\xd9\xe4\xef\xfa\x05" + "\x10\x1b\x26\x31\x3c\x47\x52\x5d" + "\x68\x73\x7e\x89\x94\x9f\xaa\xb5" + "\xc0\xcb\xd6\xe1\xec\xf7\x02\x0d" + "\x18\x23\x2e\x39\x44\x4f\x5a\x65" + "\x70\x7b\x86\x91\x9c\xa7\xb2\xbd" + "\xc8\xd3\xde\xe9\xf4\xff\x0a\x15" + "\x20\x2b\x36\x41\x4c\x57\x62\x6d" + "\x78\x83\x8e\x99\xa4\xaf\xba\xc5" + "\xd0\xdb\xe6\xf1\xfc\x07\x12\x1d" + "\x28\x33\x3e\x49\x54\x5f\x6a\x75" + "\x80\x8b\x96\xa1\xac\xb7\xc2\xcd" + "\xd8\xe3\xee\xf9\x04\x0f\x1a\x25" + "\x30\x3b\x46\x51\x5c\x67\x72\x7d" + "\x88\x93\x9e\xa9\xb4\xbf\xca\xd5" + "\xe0\xeb\xf6\x01\x0c\x17\x22\x2d" + "\x38\x43\x4e\x59\x64\x6f\x7a\x85" + "\x90\x9b\xa6\xb1\xbc\xc7\xd2\xdd" + "\xe8\xf3\xfe\x09\x14\x1f\x2a\x35" + "\x40\x4b\x56\x61\x6c\x77\x82\x8d" + "\x98\xa3\xae\xb9\xc4\xcf\xda\xe5" + "\xf0\xfb\x06\x11\x1c\x27\x32\x3d" + "\x48\x53\x5e\x69\x74\x7f\x8a\x95" + "\xa0\xab\xb6\xc1\xcc\xd7\xe2\xed" + "\xf8\x03\x0e\x19\x24\x2f\x3a\x45" + "\x50\x5b\x66\x71\x7c\x87\x92\x9d" + "\xa8\xb3\xbe\xc9\xd4\xdf\xea\xf5" + "\x00\x0d\x1a\x27\x34\x41\x4e\x5b" + "\x68\x75\x82\x8f\x9c\xa9\xb6\xc3" + "\xd0\xdd\xea\xf7\x04\x11\x1e\x2b" + "\x38\x45\x52\x5f\x6c\x79\x86\x93" + "\xa0\xad\xba\xc7\xd4\xe1\xee\xfb" + "\x08\x15\x22\x2f\x3c\x49\x56\x63" + "\x70\x7d\x8a\x97\xa4\xb1\xbe\xcb" + "\xd8\xe5\xf2\xff\x0c\x19\x26\x33" + "\x40\x4d\x5a\x67\x74\x81\x8e\x9b" + "\xa8\xb5\xc2\xcf\xdc\xe9\xf6\x03" + "\x10\x1d\x2a\x37\x44\x51\x5e\x6b" + "\x78\x85\x92\x9f\xac\xb9\xc6\xd3" + "\xe0\xed\xfa\x07\x14\x21\x2e\x3b" + "\x48\x55\x62\x6f\x7c\x89\x96\xa3" + "\xb0\xbd\xca\xd7\xe4\xf1\xfe\x0b" + "\x18\x25\x32\x3f\x4c\x59\x66\x73" + "\x80\x8d\x9a\xa7\xb4\xc1\xce\xdb" + "\xe8\xf5\x02\x0f\x1c\x29\x36\x43" + "\x50\x5d\x6a\x77\x84\x91\x9e\xab" + "\xb8\xc5\xd2\xdf\xec\xf9\x06\x13" + "\x20\x2d\x3a\x47\x54\x61\x6e\x7b" + "\x88\x95\xa2\xaf\xbc\xc9\xd6\xe3" + "\xf0\xfd\x0a\x17\x24\x31\x3e\x4b" + "\x58\x65\x72\x7f\x8c\x99\xa6\xb3" + "\xc0\xcd\xda\xe7\xf4\x01\x0e\x1b" + "\x28\x35\x42\x4f\x5c\x69\x76\x83" + "\x90\x9d\xaa\xb7\xc4\xd1\xde\xeb" + "\xf8\x05\x12\x1f\x2c\x39\x46\x53" + "\x60\x6d\x7a\x87\x94\xa1\xae\xbb" + "\xc8\xd5\xe2\xef\xfc\x09\x16\x23" + "\x30\x3d\x4a\x57\x64\x71\x7e\x8b" + "\x98\xa5\xb2\xbf\xcc\xd9\xe6\xf3" + "\x00\x0f\x1e\x2d\x3c\x4b\x5a\x69" + "\x78\x87\x96\xa5\xb4\xc3\xd2\xe1" + "\xf0\xff\x0e\x1d\x2c\x3b\x4a\x59" + "\x68\x77\x86\x95\xa4\xb3\xc2\xd1" + "\xe0\xef\xfe\x0d\x1c\x2b\x3a\x49" + "\x58\x67\x76\x85\x94\xa3\xb2\xc1" + "\xd0\xdf\xee\xfd\x0c\x1b\x2a\x39" + "\x48\x57\x66\x75\x84\x93\xa2\xb1" + "\xc0\xcf\xde\xed\xfc\x0b\x1a\x29" + "\x38\x47\x56\x65\x74\x83\x92\xa1" + "\xb0\xbf\xce\xdd\xec\xfb\x0a\x19" + "\x28\x37\x46\x55\x64\x73\x82\x91" + "\xa0\xaf\xbe\xcd\xdc\xeb\xfa\x09" + "\x18\x27\x36\x45\x54\x63\x72\x81" + "\x90\x9f\xae\xbd\xcc\xdb\xea\xf9" + "\x08\x17\x26\x35\x44\x53\x62\x71" + "\x80\x8f\x9e\xad\xbc\xcb\xda\xe9" + "\xf8\x07\x16\x25\x34\x43\x52\x61" + "\x70\x7f\x8e\x9d\xac\xbb\xca\xd9" + "\xe8\xf7\x06\x15\x24\x33\x42\x51" + "\x60\x6f\x7e\x8d\x9c\xab\xba\xc9" + "\xd8\xe7\xf6\x05\x14\x23\x32\x41" + "\x50\x5f\x6e\x7d\x8c\x9b\xaa\xb9" + "\xc8\xd7\xe6\xf5\x04\x13\x22\x31" + "\x40\x4f\x5e\x6d\x7c\x8b\x9a\xa9" + "\xb8\xc7\xd6\xe5\xf4\x03\x12\x21" + "\x30\x3f\x4e\x5d\x6c\x7b\x8a\x99" + "\xa8\xb7\xc6\xd5\xe4\xf3\x02\x11" + "\x20\x2f\x3e\x4d\x5c\x6b\x7a\x89" + "\x98\xa7\xb6\xc5\xd4\xe3\xf2\x01" + "\x10\x1f\x2e\x3d\x4c\x5b\x6a\x79" + "\x88\x97\xa6\xb5\xc4\xd3\xe2\xf1" + "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff" + "\x10\x21\x32\x43\x54\x65\x76\x87" + "\x98\xa9\xba\xcb\xdc\xed\xfe\x0f" + "\x20\x31\x42\x53\x64\x75\x86\x97" + "\xa8\xb9\xca\xdb\xec\xfd\x0e\x1f" + "\x30\x41\x52\x63\x74\x85\x96\xa7" + "\xb8\xc9\xda\xeb\xfc\x0d\x1e\x2f" + "\x40\x51\x62\x73\x84\x95\xa6\xb7" + "\xc8\xd9\xea\xfb\x0c\x1d\x2e\x3f" + "\x50\x61\x72\x83\x94\xa5\xb6\xc7" + "\xd8\xe9\xfa\x0b\x1c\x2d\x3e\x4f" + "\x60\x71\x82\x93\xa4\xb5\xc6\xd7" + "\xe8\xf9\x0a\x1b\x2c\x3d\x4e\x5f" + "\x70\x81\x92\xa3\xb4\xc5\xd6\xe7" + "\xf8\x09\x1a\x2b\x3c\x4d\x5e\x6f" + "\x80\x91\xa2\xb3\xc4\xd5\xe6\xf7" + "\x08\x19\x2a\x3b\x4c\x5d\x6e\x7f" + "\x90\xa1\xb2\xc3\xd4\xe5\xf6\x07" + "\x18\x29\x3a\x4b\x5c\x6d\x7e\x8f" + "\xa0\xb1\xc2\xd3\xe4\xf5\x06\x17" + "\x28\x39\x4a\x5b\x6c\x7d\x8e\x9f" + "\xb0\xc1\xd2\xe3\xf4\x05\x16\x27" + "\x38\x49\x5a\x6b\x7c\x8d\x9e\xaf" + "\xc0\xd1\xe2\xf3\x04\x15\x26\x37" + "\x48\x59\x6a\x7b\x8c\x9d\xae\xbf" + "\xd0\xe1\xf2\x03\x14\x25\x36\x47" + "\x58\x69\x7a\x8b\x9c\xad\xbe\xcf" + "\xe0\xf1\x02\x13\x24\x35\x46\x57" + "\x68\x79\x8a\x9b\xac\xbd\xce\xdf" + "\xf0\x01\x12\x23\x34\x45\x56\x67" + "\x78\x89\x9a\xab\xbc\xcd\xde\xef" + "\x00\x13\x26\x39\x4c\x5f\x72\x85" + "\x98\xab\xbe\xd1\xe4\xf7\x0a\x1d" + "\x30\x43\x56\x69\x7c\x8f\xa2\xb5" + "\xc8\xdb\xee\x01\x14\x27\x3a\x4d" + "\x60\x73\x86\x99\xac\xbf\xd2\xe5" + "\xf8\x0b\x1e\x31\x44\x57\x6a\x7d" + "\x90\xa3\xb6\xc9\xdc\xef\x02\x15" + "\x28\x3b\x4e\x61\x74\x87\x9a\xad" + "\xc0\xd3\xe6\xf9\x0c\x1f\x32\x45" + "\x58\x6b\x7e\x91\xa4\xb7\xca\xdd" + "\xf0\x03\x16\x29\x3c\x4f\x62\x75" + "\x88\x9b\xae\xc1\xd4\xe7\xfa\x0d" + "\x20\x33\x46\x59\x6c\x7f\x92\xa5" + "\xb8\xcb\xde\xf1\x04\x17\x2a\x3d" + "\x50\x63\x76\x89\x9c\xaf\xc2\xd5" + "\xe8\xfb\x0e\x21\x34\x47\x5a\x6d" + "\x80\x93\xa6\xb9\xcc\xdf\xf2\x05" + "\x18\x2b\x3e\x51\x64\x77\x8a\x9d" + "\xb0\xc3\xd6\xe9\xfc\x0f\x22\x35" + "\x48\x5b\x6e\x81\x94\xa7\xba\xcd" + "\xe0\xf3\x06\x19\x2c\x3f\x52\x65" + "\x78\x8b\x9e\xb1\xc4\xd7\xea\xfd" + "\x10\x23\x36\x49\x5c\x6f\x82\x95" + "\xa8\xbb\xce\xe1\xf4\x07\x1a\x2d" + "\x40\x53\x66\x79\x8c\x9f\xb2\xc5" + "\xd8\xeb\xfe\x11\x24\x37\x4a\x5d" + "\x70\x83\x96\xa9\xbc\xcf\xe2\xf5" + "\x08\x1b\x2e\x41\x54\x67\x7a\x8d" + "\xa0\xb3\xc6\xd9\xec\xff\x12\x25" + "\x38\x4b\x5e\x71\x84\x97\xaa\xbd" + "\xd0\xe3\xf6\x09\x1c\x2f\x42\x55" + "\x68\x7b\x8e\xa1\xb4\xc7\xda\xed" + "\x00\x15\x2a\x3f\x54\x69\x7e\x93" + "\xa8\xbd\xd2\xe7\xfc\x11\x26\x3b" + "\x50\x65\x7a\x8f\xa4\xb9\xce\xe3" + "\xf8\x0d\x22\x37\x4c\x61\x76\x8b" + "\xa0\xb5\xca\xdf\xf4\x09\x1e\x33" + "\x48\x5d\x72\x87\x9c\xb1\xc6\xdb" + "\xf0\x05\x1a\x2f\x44\x59\x6e\x83" + "\x98\xad\xc2\xd7\xec\x01\x16\x2b" + "\x40\x55\x6a\x7f\x94\xa9\xbe\xd3" + "\xe8\xfd\x12\x27\x3c\x51\x66\x7b" + "\x90\xa5\xba\xcf\xe4\xf9\x0e\x23" + "\x38\x4d\x62\x77\x8c\xa1\xb6\xcb" + "\xe0\xf5\x0a\x1f\x34\x49\x5e\x73" + "\x88\x9d\xb2\xc7\xdc\xf1\x06\x1b" + "\x30\x45\x5a\x6f\x84\x99\xae\xc3" + "\xd8\xed\x02\x17\x2c\x41\x56\x6b" + "\x80\x95\xaa\xbf\xd4\xe9\xfe\x13" + "\x28\x3d\x52\x67\x7c\x91\xa6\xbb" + "\xd0\xe5\xfa\x0f\x24\x39\x4e\x63" + "\x78\x8d\xa2\xb7\xcc\xe1\xf6\x0b" + "\x20\x35\x4a\x5f\x74\x89\x9e\xb3" + "\xc8\xdd\xf2\x07\x1c\x31\x46\x5b" + "\x70\x85\x9a\xaf\xc4\xd9\xee\x03" + "\x18\x2d\x42\x57\x6c\x81\x96\xab" + "\xc0\xd5\xea\xff\x14\x29\x3e\x53" + "\x68\x7d\x92\xa7\xbc\xd1\xe6\xfb" + "\x10\x25\x3a\x4f\x64\x79\x8e\xa3" + "\xb8\xcd\xe2\xf7\x0c\x21\x36\x4b" + "\x60\x75\x8a\x9f\xb4\xc9\xde\xf3" + "\x08\x1d\x32\x47\x5c\x71\x86\x9b" + "\xb0\xc5\xda\xef\x04\x19\x2e\x43" + "\x58\x6d\x82\x97\xac\xc1\xd6\xeb" + "\x00\x17\x2e\x45\x5c\x73\x8a\xa1" + "\xb8\xcf\xe6\xfd\x14\x2b\x42\x59" + "\x70\x87\x9e\xb5\xcc\xe3\xfa\x11" + "\x28\x3f\x56\x6d\x84\x9b\xb2\xc9" + "\xe0\xf7\x0e\x25\x3c\x53\x6a\x81" + "\x98\xaf\xc6\xdd\xf4\x0b\x22\x39" + "\x50\x67\x7e\x95\xac\xc3\xda\xf1" + "\x08\x1f\x36\x4d\x64\x7b\x92\xa9" + "\xc0\xd7\xee\x05\x1c\x33\x4a\x61" + "\x78\x8f\xa6\xbd\xd4\xeb\x02\x19" + "\x30\x47\x5e\x75\x8c\xa3\xba\xd1" + "\xe8\xff\x16\x2d\x44\x5b\x72\x89" + "\xa0\xb7\xce\xe5\xfc\x13\x2a\x41" + "\x58\x6f\x86\x9d\xb4\xcb\xe2\xf9" + "\x10\x27\x3e\x55\x6c\x83\x9a\xb1" + "\xc8\xdf\xf6\x0d\x24\x3b\x52\x69" + "\x80\x97\xae\xc5\xdc\xf3\x0a\x21" + "\x38\x4f\x66\x7d\x94\xab\xc2\xd9" + "\xf0\x07\x1e\x35\x4c\x63\x7a\x91" + "\xa8\xbf\xd6\xed\x04\x1b\x32\x49" + "\x60\x77\x8e\xa5\xbc\xd3\xea\x01" + "\x18\x2f\x46\x5d\x74\x8b\xa2\xb9" + "\xd0\xe7\xfe\x15\x2c\x43\x5a\x71" + "\x88\x9f\xb6\xcd\xe4\xfb\x12\x29" + "\x40\x57\x6e\x85\x9c\xb3\xca\xe1" + "\xf8\x0f\x26\x3d\x54\x6b\x82\x99" + "\xb0\xc7\xde\xf5\x0c\x23\x3a\x51" + "\x68\x7f\x96\xad\xc4\xdb\xf2\x09" + "\x20\x37\x4e\x65\x7c\x93\xaa\xc1" + "\xd8\xef\x06\x1d\x34\x4b\x62\x79" + "\x90\xa7\xbe\xd5\xec\x03\x1a\x31" + "\x48\x5f\x76\x8d\xa4\xbb\xd2\xe9" + "\x00\x19\x32\x4b\x64\x7d\x96\xaf" + "\xc8\xe1\xfa\x13\x2c\x45\x5e\x77" + "\x90\xa9\xc2\xdb\xf4\x0d\x26\x3f" + "\x58\x71\x8a\xa3\xbc\xd5\xee\x07" + "\x20\x39\x52\x6b\x84\x9d\xb6\xcf" + "\xe8\x01\x1a\x33\x4c\x65\x7e\x97" + "\xb0\xc9\xe2\xfb\x14\x2d\x46\x5f" + "\x78\x91\xaa\xc3\xdc\xf5\x0e\x27" + "\x40\x59\x72\x8b\xa4\xbd\xd6\xef" + "\x08\x21\x3a\x53\x6c\x85\x9e\xb7" + "\xd0\xe9\x02\x1b\x34\x4d\x66\x7f" + "\x98\xb1\xca\xe3\xfc\x15\x2e\x47" + "\x60\x79\x92\xab\xc4\xdd\xf6\x0f" + "\x28\x41\x5a\x73\x8c\xa5\xbe\xd7" + "\xf0\x09\x22\x3b\x54\x6d\x86\x9f" + "\xb8\xd1\xea\x03\x1c\x35\x4e\x67" + "\x80\x99\xb2\xcb\xe4\xfd\x16\x2f" + "\x48\x61\x7a\x93\xac\xc5\xde\xf7" + "\x10\x29\x42\x5b\x74\x8d\xa6\xbf" + "\xd8\xf1\x0a\x23\x3c\x55\x6e\x87" + "\xa0\xb9\xd2\xeb\x04\x1d\x36\x4f" + "\x68\x81\x9a\xb3\xcc\xe5\xfe\x17" + "\x30\x49\x62\x7b\x94\xad\xc6\xdf" + "\xf8\x11\x2a\x43\x5c\x75\x8e\xa7" + "\xc0\xd9\xf2\x0b\x24\x3d\x56\x6f" + "\x88\xa1\xba\xd3\xec\x05\x1e\x37" + "\x50\x69\x82\x9b\xb4\xcd\xe6\xff" + "\x18\x31\x4a\x63\x7c\x95\xae\xc7" + "\xe0\xf9\x12\x2b\x44\x5d\x76\x8f" + "\xa8\xc1\xda\xf3\x0c\x25\x3e\x57" + "\x70\x89\xa2\xbb\xd4\xed\x06\x1f" + "\x38\x51\x6a\x83\x9c\xb5\xce\xe7" + "\x00\x1b\x36\x51\x6c\x87\xa2\xbd" + "\xd8\xf3\x0e\x29\x44\x5f\x7a\x95" + "\xb0\xcb\xe6\x01\x1c\x37\x52\x6d" + "\x88\xa3\xbe\xd9\xf4\x0f\x2a\x45" + "\x60\x7b\x96\xb1\xcc\xe7\x02\x1d" + "\x38\x53\x6e\x89\xa4\xbf\xda\xf5" + "\x10\x2b\x46\x61\x7c\x97\xb2\xcd" + "\xe8\x03\x1e\x39\x54\x6f\x8a\xa5" + "\xc0\xdb\xf6\x11\x2c\x47\x62\x7d" + "\x98\xb3\xce\xe9\x04\x1f\x3a\x55" + "\x70\x8b\xa6\xc1\xdc\xf7\x12\x2d" + "\x48\x63\x7e\x99\xb4\xcf\xea\x05" + "\x20\x3b\x56\x71\x8c\xa7\xc2\xdd" + "\xf8\x13\x2e\x49\x64\x7f\x9a\xb5" + "\xd0\xeb\x06\x21\x3c\x57\x72\x8d" + "\xa8\xc3\xde\xf9\x14\x2f\x4a\x65" + "\x80\x9b\xb6\xd1\xec\x07\x22\x3d" + "\x58\x73\x8e\xa9\xc4\xdf\xfa\x15" + "\x30\x4b\x66\x81\x9c\xb7\xd2\xed" + "\x08\x23\x3e\x59\x74\x8f\xaa\xc5" + "\xe0\xfb\x16\x31\x4c\x67\x82\x9d" + "\xb8\xd3\xee\x09\x24\x3f\x5a\x75" + "\x90\xab\xc6\xe1\xfc\x17\x32\x4d" + "\x68\x83\x9e\xb9\xd4\xef\x0a\x25" + "\x40\x5b\x76\x91\xac\xc7\xe2\xfd" + "\x18\x33\x4e\x69\x84\x9f\xba\xd5" + "\xf0\x0b\x26\x41\x5c\x77\x92\xad" + "\xc8\xe3\xfe\x19\x34\x4f\x6a\x85" + "\xa0\xbb\xd6\xf1\x0c\x27\x42\x5d" + "\x78\x93\xae\xc9\xe4\xff\x1a\x35" + "\x50\x6b\x86\xa1\xbc\xd7\xf2\x0d" + "\x28\x43\x5e\x79\x94\xaf\xca\xe5" + "\x00\x1d\x3a\x57\x74\x91\xae\xcb" + "\xe8\x05\x22\x3f\x5c\x79\x96\xb3" + "\xd0\xed\x0a\x27\x44\x61\x7e\x9b" + "\xb8\xd5\xf2\x0f\x2c\x49\x66\x83" + "\xa0\xbd\xda\xf7\x14\x31\x4e\x6b" + "\x88\xa5\xc2\xdf\xfc\x19\x36\x53" + "\x70\x8d\xaa\xc7\xe4\x01\x1e\x3b" + "\x58\x75\x92\xaf\xcc\xe9\x06\x23" + "\x40\x5d\x7a\x97\xb4\xd1\xee\x0b" + "\x28\x45\x62\x7f\x9c\xb9\xd6\xf3" + "\x10\x2d\x4a\x67\x84\xa1\xbe\xdb" + "\xf8\x15\x32\x4f\x6c\x89\xa6\xc3" + "\xe0\xfd\x1a\x37\x54\x71\x8e\xab" + "\xc8\xe5\x02\x1f\x3c\x59\x76\x93" + "\xb0\xcd\xea\x07\x24\x41\x5e\x7b" + "\x98\xb5\xd2\xef\x0c\x29\x46\x63" + "\x80\x9d\xba\xd7\xf4\x11\x2e\x4b" + "\x68\x85\xa2\xbf\xdc\xf9\x16\x33" + "\x50\x6d\x8a\xa7\xc4\xe1\xfe\x1b" + "\x38\x55\x72\x8f\xac\xc9\xe6\x03" + "\x20\x3d\x5a\x77\x94\xb1\xce\xeb" + "\x08\x25\x42\x5f\x7c\x99\xb6\xd3" + "\xf0\x0d\x2a\x47\x64\x81\x9e\xbb" + "\xd8\xf5\x12\x2f\x4c\x69\x86\xa3" + "\xc0\xdd\xfa\x17\x34\x51\x6e\x8b" + "\xa8\xc5\xe2\xff\x1c\x39\x56\x73" + "\x90\xad\xca\xe7\x04\x21\x3e\x5b" + "\x78\x95\xb2\xcf\xec\x09\x26\x43" + "\x60\x7d\x9a\xb7\xd4\xf1\x0e\x2b" + "\x48\x65\x82\x9f\xbc\xd9\xf6\x13" + "\x30\x4d\x6a\x87\xa4\xc1\xde\xfb" + "\x18\x35\x52\x6f\x8c\xa9\xc6\xe3" + "\x00\x1f\x3e\x5d\x7c\x9b\xba\xd9" + "\xf8\x17\x36\x55\x74\x93\xb2\xd1" + "\xf0\x0f\x2e\x4d\x6c\x8b\xaa\xc9" + "\xe8\x07\x26\x45\x64\x83\xa2\xc1" + "\xe0\xff\x1e\x3d\x5c\x7b\x9a\xb9" + "\xd8\xf7\x16\x35\x54\x73\x92\xb1" + "\xd0\xef\x0e\x2d\x4c\x6b\x8a\xa9" + "\xc8\xe7\x06\x25\x44\x63\x82\xa1" + "\xc0\xdf\xfe\x1d\x3c\x5b\x7a\x99" + "\xb8\xd7\xf6\x15\x34\x53\x72\x91" + "\xb0\xcf\xee\x0d\x2c\x4b\x6a\x89" + "\xa8\xc7\xe6\x05\x24\x43\x62\x81" + "\xa0\xbf\xde\xfd\x1c\x3b\x5a\x79" + "\x98\xb7\xd6\xf5\x14\x33\x52\x71" + "\x90\xaf\xce\xed\x0c\x2b\x4a\x69" + "\x88\xa7\xc6\xe5\x04\x23\x42\x61" + "\x80\x9f\xbe\xdd\xfc\x1b\x3a\x59" + "\x78\x97\xb6\xd5\xf4\x13\x32\x51" + "\x70\x8f\xae\xcd\xec\x0b\x2a\x49" + "\x68\x87\xa6\xc5\xe4\x03\x22\x41" + "\x60\x7f\x9e\xbd\xdc\xfb\x1a\x39" + "\x58\x77\x96\xb5\xd4\xf3\x12\x31" + "\x50\x6f\x8e\xad\xcc\xeb\x0a\x29" + "\x48\x67\x86\xa5\xc4\xe3\x02\x21" + "\x40\x5f\x7e\x9d\xbc\xdb\xfa\x19" + "\x38\x57\x76\x95\xb4\xd3\xf2\x11" + "\x30\x4f\x6e\x8d\xac\xcb\xea\x09" + "\x28\x47\x66\x85\xa4\xc3\xe2\x01" + "\x20\x3f\x5e\x7d\x9c\xbb\xda\xf9" + "\x18\x37\x56\x75\x94\xb3\xd2\xf1" + "\x10\x2f\x4e\x6d\x8c\xab\xca\xe9" + "\x08\x27\x46\x65\x84\xa3\xc2\xe1" + "\x00\x21\x42\x63", + .ilen = 4100, + .result = + "\xb5\x81\xf5\x64\x18\x73\xe3\xf0" + "\x4c\x13\xf2\x77\x18\x60\x65\x5e" + "\x29\x01\xce\x98\x55\x53\xf9\x0c" + "\x2a\x08\xd5\x09\xb3\x57\x55\x56" + "\xc5\xe9\x56\x90\xcb\x6a\xa3\xc0" + "\xff\xc4\x79\xb4\xd2\x97\x5d\xc4" + "\x43\xd1\xfe\x94\x7b\x88\x06\x5a" + "\xb2\x9e\x2c\xfc\x44\x03\xb7\x90" + "\xa0\xc1\xba\x6a\x33\xb8\xc7\xb2" + "\x9d\xe1\x12\x4f\xc0\x64\xd4\x01" + "\xfe\x8c\x7a\x66\xf7\xe6\x5a\x91" + "\xbb\xde\x56\x86\xab\x65\x21\x30" + "\x00\x84\x65\x24\xa5\x7d\x85\xb4" + "\xe3\x17\xed\x3a\xb7\x6f\xb4\x0b" + "\x0b\xaf\x15\xae\x5a\x8f\xf2\x0c" + "\x2f\x27\xf4\x09\xd8\xd2\x96\xb7" + "\x71\xf2\xc5\x99\x4d\x7e\x7f\x75" + "\x77\x89\x30\x8b\x59\xdb\xa2\xb2" + "\xa0\xf3\x19\x39\x2b\xc5\x7e\x3f" + "\x4f\xd9\xd3\x56\x28\x97\x44\xdc" + "\xc0\x8b\x77\x24\xd9\x52\xe7\xc5" + "\xaf\xf6\x7d\x59\xb2\x44\x05\x1d" + "\xb1\xb0\x11\xa5\x0f\xec\x33\xe1" + "\x6d\x1b\x4e\x1f\xff\x57\x91\xb4" + "\x5b\x9a\x96\xc5\x53\xbc\xae\x20" + "\x3c\xbb\x14\xe2\xe8\x22\x33\xc1" + "\x5e\x76\x9e\x46\x99\xf6\x2a\x15" + "\xc6\x97\x02\xa0\x66\x43\xd1\xa6" + "\x31\xa6\x9f\xfb\xf4\xd3\x69\xe5" + "\xcd\x76\x95\xb8\x7a\x82\x7f\x21" + "\x45\xff\x3f\xce\x55\xf6\x95\x10" + "\x08\x77\x10\x43\xc6\xf3\x09\xe5" + "\x68\xe7\x3c\xad\x00\x52\x45\x0d" + "\xfe\x2d\xc6\xc2\x94\x8c\x12\x1d" + "\xe6\x25\xae\x98\x12\x8e\x19\x9c" + "\x81\x68\xb1\x11\xf6\x69\xda\xe3" + "\x62\x08\x18\x7a\x25\x49\x28\xac" + "\xba\x71\x12\x0b\xe4\xa2\xe5\xc7" + "\x5d\x8e\xec\x49\x40\x21\xbf\x5a" + "\x98\xf3\x02\x68\x55\x03\x7f\x8a" + "\xe5\x94\x0c\x32\x5c\x07\x82\x63" + "\xaf\x6f\x91\x40\x84\x8e\x52\x25" + "\xd0\xb0\x29\x53\x05\xe2\x50\x7a" + "\x34\xeb\xc9\x46\x20\xa8\x3d\xde" + "\x7f\x16\x5f\x36\xc5\x2e\xdc\xd1" + "\x15\x47\xc7\x50\x40\x6d\x91\xc5" + "\xe7\x93\x95\x1a\xd3\x57\xbc\x52" + "\x33\xee\x14\x19\x22\x52\x89\xa7" + "\x4a\x25\x56\x77\x4b\xca\xcf\x0a" + "\xe1\xf5\x35\x85\x30\x7e\x59\x4a" + "\xbd\x14\x5b\xdf\xe3\x46\xcb\xac" + "\x1f\x6c\x96\x0e\xf4\x81\xd1\x99" + "\xca\x88\x63\x3d\x02\x58\x6b\xa9" + "\xe5\x9f\xb3\x00\xb2\x54\xc6\x74" + "\x1c\xbf\x46\xab\x97\xcc\xf8\x54" + "\x04\x07\x08\x52\xe6\xc0\xda\x93" + "\x74\x7d\x93\x99\x5d\x78\x68\xa6" + "\x2e\x6b\xd3\x6a\x69\xcc\x12\x6b" + "\xd4\xc7\xa5\xc6\xe7\xf6\x03\x04" + "\x5d\xcd\x61\x5e\x17\x40\xdc\xd1" + "\x5c\xf5\x08\xdf\x5c\x90\x85\xa4" + "\xaf\xf6\x78\xbb\x0d\xf1\xf4\xa4" + "\x54\x26\x72\x9e\x61\xfa\x86\xcf" + "\xe8\x9e\xa1\xe0\xc7\x48\x23\xae" + "\x5a\x90\xae\x75\x0a\x74\x18\x89" + "\x05\xb1\x92\xb2\x7f\xd0\x1b\xa6" + "\x62\x07\x25\x01\xc7\xc2\x4f\xf9" + "\xe8\xfe\x63\x95\x80\x07\xb4\x26" + "\xcc\xd1\x26\xb6\xc4\x3f\x9e\xcb" + "\x8e\x3b\x2e\x44\x16\xd3\x10\x9a" + "\x95\x08\xeb\xc8\xcb\xeb\xbf\x6f" + "\x0b\xcd\x1f\xc8\xca\x86\xaa\xec" + "\x33\xe6\x69\xf4\x45\x25\x86\x3a" + "\x22\x94\x4f\x00\x23\x6a\x44\xc2" + "\x49\x97\x33\xab\x36\x14\x0a\x70" + "\x24\xc3\xbe\x04\x3b\x79\xa0\xf9" + "\xb8\xe7\x76\x29\x22\x83\xd7\xf2" + "\x94\xf4\x41\x49\xba\x5f\x7b\x07" + "\xb5\xfb\xdb\x03\x1a\x9f\xb6\x4c" + "\xc2\x2e\x37\x40\x49\xc3\x38\x16" + "\xe2\x4f\x77\x82\xb0\x68\x4c\x71" + "\x1d\x57\x61\x9c\xd9\x4e\x54\x99" + "\x47\x13\x28\x73\x3c\xbb\x00\x90" + "\xf3\x4d\xc9\x0e\xfd\xe7\xb1\x71" + "\xd3\x15\x79\xbf\xcc\x26\x2f\xbd" + "\xad\x6c\x50\x69\x6c\x3e\x6d\x80" + "\x9a\xea\x78\xaf\x19\xb2\x0d\x4d" + "\xad\x04\x07\xae\x22\x90\x4a\x93" + "\x32\x0e\x36\x9b\x1b\x46\xba\x3b" + "\xb4\xac\xc6\xd1\xa2\x31\x53\x3b" + "\x2a\x3d\x45\xfe\x03\x61\x10\x85" + "\x17\x69\xa6\x78\xcc\x6c\x87\x49" + "\x53\xf9\x80\x10\xde\x80\xa2\x41" + "\x6a\xc3\x32\x02\xad\x6d\x3c\x56" + "\x00\x71\x51\x06\xa7\xbd\xfb\xef" + "\x3c\xb5\x9f\xfc\x48\x7d\x53\x7c" + "\x66\xb0\x49\x23\xc4\x47\x10\x0e" + "\xe5\x6c\x74\x13\xe6\xc5\x3f\xaa" + "\xde\xff\x07\x44\xdd\x56\x1b\xad" + "\x09\x77\xfb\x5b\x12\xb8\x0d\x38" + "\x17\x37\x35\x7b\x9b\xbc\xfe\xd4" + "\x7e\x8b\xda\x7e\x5b\x04\xa7\x22" + "\xa7\x31\xa1\x20\x86\xc7\x1b\x99" + "\xdb\xd1\x89\xf4\x94\xa3\x53\x69" + "\x8d\xe7\xe8\x74\x11\x8d\x74\xd6" + "\x07\x37\x91\x9f\xfd\x67\x50\x3a" + "\xc9\xe1\xf4\x36\xd5\xa0\x47\xd1" + "\xf9\xe5\x39\xa3\x31\xac\x07\x36" + "\x23\xf8\x66\x18\x14\x28\x34\x0f" + "\xb8\xd0\xe7\x29\xb3\x04\x4b\x55" + "\x01\x41\xb2\x75\x8d\xcb\x96\x85" + "\x3a\xfb\xab\x2b\x9e\xfa\x58\x20" + "\x44\x1f\xc0\x14\x22\x75\x61\xe8" + "\xaa\x19\xcf\xf1\x82\x56\xf4\xd7" + "\x78\x7b\x3d\x5f\xb3\x9e\x0b\x8a" + "\x57\x50\xdb\x17\x41\x65\x4d\xa3" + "\x02\xc9\x9c\x9c\x53\xfb\x39\x39" + "\x9b\x1d\x72\x24\xda\xb7\x39\xbe" + "\x13\x3b\xfa\x29\xda\x9e\x54\x64" + "\x6e\xba\xd8\xa1\xcb\xb3\x36\xfa" + "\xcb\x47\x85\xe9\x61\x38\xbc\xbe" + "\xc5\x00\x38\x2a\x54\xf7\xc4\xb9" + "\xb3\xd3\x7b\xa0\xa0\xf8\x72\x7f" + "\x8c\x8e\x82\x0e\xc6\x1c\x75\x9d" + "\xca\x8e\x61\x87\xde\xad\x80\xd2" + "\xf5\xf9\x80\xef\x15\x75\xaf\xf5" + "\x80\xfb\xff\x6d\x1e\x25\xb7\x40" + "\x61\x6a\x39\x5a\x6a\xb5\x31\xab" + "\x97\x8a\x19\x89\x44\x40\xc0\xa6" + "\xb4\x4e\x30\x32\x7b\x13\xe7\x67" + "\xa9\x8b\x57\x04\xc2\x01\xa6\xf4" + "\x28\x99\xad\x2c\x76\xa3\x78\xc2" + "\x4a\xe6\xca\x5c\x50\x6a\xc1\xb0" + "\x62\x4b\x10\x8e\x7c\x17\x43\xb3" + "\x17\x66\x1c\x3e\x8d\x69\xf0\x5a" + "\x71\xf5\x97\xdc\xd1\x45\xdd\x28" + "\xf3\x5d\xdf\x53\x7b\x11\xe5\xbc" + "\x4c\xdb\x1b\x51\x6b\xe9\xfb\x3d" + "\xc1\xc3\x2c\xb9\x71\xf5\xb6\xb2" + "\x13\x36\x79\x80\x53\xe8\xd3\xa6" + "\x0a\xaf\xfd\x56\x97\xf7\x40\x8e" + "\x45\xce\xf8\xb0\x9e\x5c\x33\x82" + "\xb0\x44\x56\xfc\x05\x09\xe9\x2a" + "\xac\x26\x80\x14\x1d\xc8\x3a\x35" + "\x4c\x82\x97\xfd\x76\xb7\xa9\x0a" + "\x35\x58\x79\x8e\x0f\x66\xea\xaf" + "\x51\x6c\x09\xa9\x6e\x9b\xcb\x9a" + "\x31\x47\xa0\x2f\x7c\x71\xb4\x4a" + "\x11\xaa\x8c\x66\xc5\x64\xe6\x3a" + "\x54\xda\x24\x6a\xc4\x41\x65\x46" + "\x82\xa0\x0a\x0f\x5f\xfb\x25\xd0" + "\x2c\x91\xa7\xee\xc4\x81\x07\x86" + "\x75\x5e\x33\x69\x97\xe4\x2c\xa8" + "\x9d\x9f\x0b\x6a\xbe\xad\x98\xda" + "\x6d\x94\x41\xda\x2c\x1e\x89\xc4" + "\xc2\xaf\x1e\x00\x05\x0b\x83\x60" + "\xbd\x43\xea\x15\x23\x7f\xb9\xac" + "\xee\x4f\x2c\xaf\x2a\xf3\xdf\xd0" + "\xf3\x19\x31\xbb\x4a\x74\x84\x17" + "\x52\x32\x2c\x7d\x61\xe4\xcb\xeb" + "\x80\x38\x15\x52\xcb\x6f\xea\xe5" + "\x73\x9c\xd9\x24\x69\xc6\x95\x32" + "\x21\xc8\x11\xe4\xdc\x36\xd7\x93" + "\x38\x66\xfb\xb2\x7f\x3a\xb9\xaf" + "\x31\xdd\x93\x75\x78\x8a\x2c\x94" + "\x87\x1a\x58\xec\x9e\x7d\x4d\xba" + "\xe1\xe5\x4d\xfc\xbc\xa4\x2a\x14" + "\xef\xcc\xa7\xec\xab\x43\x09\x18" + "\xd3\xab\x68\xd1\x07\x99\x44\x47" + "\xd6\x83\x85\x3b\x30\xea\xa9\x6b" + "\x63\xea\xc4\x07\xfb\x43\x2f\xa4" + "\xaa\xb0\xab\x03\x89\xce\x3f\x8c" + "\x02\x7c\x86\x54\xbc\x88\xaf\x75" + "\xd2\xdc\x63\x17\xd3\x26\xf6\x96" + "\xa9\x3c\xf1\x61\x8c\x11\x18\xcc" + "\xd6\xea\x5b\xe2\xcd\xf0\xf1\xb2" + "\xe5\x35\x90\x1f\x85\x4c\x76\x5b" + "\x66\xce\x44\xa4\x32\x9f\xe6\x7b" + "\x71\x6e\x9f\x58\x15\x67\x72\x87" + "\x64\x8e\x3a\x44\x45\xd4\x76\xfa" + "\xc2\xf6\xef\x85\x05\x18\x7a\x9b" + "\xba\x41\x54\xac\xf0\xfc\x59\x12" + "\x3f\xdf\xa0\xe5\x8a\x65\xfd\x3a" + "\x62\x8d\x83\x2c\x03\xbe\x05\x76" + "\x2e\x53\x49\x97\x94\x33\xae\x40" + "\x81\x15\xdb\x6e\xad\xaa\xf5\x4b" + "\xe3\x98\x70\xdf\xe0\x7c\xcd\xdb" + "\x02\xd4\x7d\x2f\xc1\xe6\xb4\xf3" + "\xd7\x0d\x7a\xd9\x23\x9e\x87\x2d" + "\xce\x87\xad\xcc\x72\x05\x00\x29" + "\xdc\x73\x7f\x64\xc1\x15\x0e\xc2" + "\xdf\xa7\x5f\xeb\x41\xa1\xcd\xef" + "\x5c\x50\x79\x2a\x56\x56\x71\x8c" + "\xac\xc0\x79\x50\x69\xca\x59\x32" + "\x65\xf2\x54\xe4\x52\x38\x76\xd1" + "\x5e\xde\x26\x9e\xfb\x75\x2e\x11" + "\xb5\x10\xf4\x17\x73\xf5\x89\xc7" + "\x4f\x43\x5c\x8e\x7c\xb9\x05\x52" + "\x24\x40\x99\xfe\x9b\x85\x0b\x6c" + "\x22\x3e\x8b\xae\x86\xa1\xd2\x79" + "\x05\x68\x6b\xab\xe3\x41\x49\xed" + "\x15\xa1\x8d\x40\x2d\x61\xdf\x1a" + "\x59\xc9\x26\x8b\xef\x30\x4c\x88" + "\x4b\x10\xf8\x8d\xa6\x92\x9f\x4b" + "\xf3\xc4\x53\x0b\x89\x5d\x28\x92" + "\xcf\x78\xb2\xc0\x5d\xed\x7e\xfc" + "\xc0\x12\x23\x5f\x5a\x78\x86\x43" + "\x6e\x27\xf7\x5a\xa7\x6a\xed\x19" + "\x04\xf0\xb3\x12\xd1\xbd\x0e\x89" + "\x6e\xbc\x96\xa8\xd8\x49\x39\x9f" + "\x7e\x67\xf0\x2e\x3e\x01\xa9\xba" + "\xec\x8b\x62\x8e\xcb\x4a\x70\x43" + "\xc7\xc2\xc4\xca\x82\x03\x73\xe9" + "\x11\xdf\xcf\x54\xea\xc9\xb0\x95" + "\x51\xc0\x13\x3d\x92\x05\xfa\xf4" + "\xa9\x34\xc8\xce\x6c\x3d\x54\xcc" + "\xc4\xaf\xf1\xdc\x11\x44\x26\xa2" + "\xaf\xf1\x85\x75\x7d\x03\x61\x68" + "\x4e\x78\xc6\x92\x7d\x86\x7d\x77" + "\xdc\x71\x72\xdb\xc6\xae\xa1\xcb" + "\x70\x9a\x0b\x19\xbe\x4a\x6c\x2a" + "\xe2\xba\x6c\x64\x9a\x13\x28\xdf" + "\x85\x75\xe6\x43\xf6\x87\x08\x68" + "\x6e\xba\x6e\x79\x9f\x04\xbc\x23" + "\x50\xf6\x33\x5c\x1f\x24\x25\xbe" + "\x33\x47\x80\x45\x56\xa3\xa7\xd7" + "\x7a\xb1\x34\x0b\x90\x3c\x9c\xad" + "\x44\x5f\x9e\x0e\x9d\xd4\xbd\x93" + "\x5e\xfa\x3c\xe0\xb0\xd9\xed\xf3" + "\xd6\x2e\xff\x24\xd8\x71\x6c\xed" + "\xaf\x55\xeb\x22\xac\x93\x68\x32" + "\x05\x5b\x47\xdd\xc6\x4a\xcb\xc7" + "\x10\xe1\x3c\x92\x1a\xf3\x23\x78" + "\x2b\xa1\xd2\x80\xf4\x12\xb1\x20" + "\x8f\xff\x26\x35\xdd\xfb\xc7\x4e" + "\x78\xf1\x2d\x50\x12\x77\xa8\x60" + "\x7c\x0f\xf5\x16\x2f\x63\x70\x2a" + "\xc0\x96\x80\x4e\x0a\xb4\x93\x35" + "\x5d\x1d\x3f\x56\xf7\x2f\xbb\x90" + "\x11\x16\x8f\xa2\xec\x47\xbe\xac" + "\x56\x01\x26\x56\xb1\x8c\xb2\x10" + "\xf9\x1a\xca\xf5\xd1\xb7\x39\x20" + "\x63\xf1\x69\x20\x4f\x13\x12\x1f" + "\x5b\x65\xfc\x98\xf7\xc4\x7a\xbe" + "\xf7\x26\x4d\x2b\x84\x7b\x42\xad" + "\xd8\x7a\x0a\xb4\xd8\x74\xbf\xc1" + "\xf0\x6e\xb4\x29\xa3\xbb\xca\x46" + "\x67\x70\x6a\x2d\xce\x0e\xa2\x8a" + "\xa9\x87\xbf\x05\xc4\xc1\x04\xa3" + "\xab\xd4\x45\x43\x8c\xb6\x02\xb0" + "\x41\xc8\xfc\x44\x3d\x59\xaa\x2e" + "\x44\x21\x2a\x8d\x88\x9d\x57\xf4" + "\xa0\x02\x77\xb8\xa6\xa0\xe6\x75" + "\x5c\x82\x65\x3e\x03\x5c\x29\x8f" + "\x38\x55\xab\x33\x26\xef\x9f\x43" + "\x52\xfd\x68\xaf\x36\xb4\xbb\x9a" + "\x58\x09\x09\x1b\xc3\x65\x46\x46" + "\x1d\xa7\x94\x18\x23\x50\x2c\xca" + "\x2c\x55\x19\x97\x01\x9d\x93\x3b" + "\x63\x86\xf2\x03\x67\x45\xd2\x72" + "\x28\x52\x6c\xf4\xe3\x1c\xb5\x11" + "\x13\xf1\xeb\x21\xc7\xd9\x56\x82" + "\x2b\x82\x39\xbd\x69\x54\xed\x62" + "\xc3\xe2\xde\x73\xd4\x6a\x12\xae" + "\x13\x21\x7f\x4b\x5b\xfc\xbf\xe8" + "\x2b\xbe\x56\xba\x68\x8b\x9a\xb1" + "\x6e\xfa\xbf\x7e\x5a\x4b\xf1\xac" + "\x98\x65\x85\xd1\x93\x53\xd3\x7b" + "\x09\xdd\x4b\x10\x6d\x84\xb0\x13" + "\x65\xbd\xcf\x52\x09\xc4\x85\xe2" + "\x84\x74\x15\x65\xb7\xf7\x51\xaf" + "\x55\xad\xa4\xd1\x22\x54\x70\x94" + "\xa0\x1c\x90\x41\xfd\x99\xd7\x5a" + "\x31\xef\xaa\x25\xd0\x7f\x4f\xea" + "\x1d\x55\x42\xe5\x49\xb0\xd0\x46" + "\x62\x36\x43\xb2\x82\x15\x75\x50" + "\xa4\x72\xeb\x54\x27\x1f\x8a\xe4" + "\x7d\xe9\x66\xc5\xf1\x53\xa4\xd1" + "\x0c\xeb\xb8\xf8\xbc\xd4\xe2\xe7" + "\xe1\xf8\x4b\xcb\xa9\xa1\xaf\x15" + "\x83\xcb\x72\xd0\x33\x79\x00\x2d" + "\x9f\xd7\xf1\x2e\x1e\x10\xe4\x45" + "\xc0\x75\x3a\x39\xea\x68\xf7\x5d" + "\x1b\x73\x8f\xe9\x8e\x0f\x72\x47" + "\xae\x35\x0a\x31\x7a\x14\x4d\x4a" + "\x6f\x47\xf7\x7e\x91\x6e\x74\x8b" + "\x26\x47\xf9\xc3\xf9\xde\x70\xf5" + "\x61\xab\xa9\x27\x9f\x82\xe4\x9c" + "\x89\x91\x3f\x2e\x6a\xfd\xb5\x49" + "\xe9\xfd\x59\x14\x36\x49\x40\x6d" + "\x32\xd8\x85\x42\xf3\xa5\xdf\x0c" + "\xa8\x27\xd7\x54\xe2\x63\x2f\xf2" + "\x7e\x8b\x8b\xe7\xf1\x9a\x95\x35" + "\x43\xdc\x3a\xe4\xb6\xf4\xd0\xdf" + "\x9c\xcb\x94\xf3\x21\xa0\x77\x50" + "\xe2\xc6\xc4\xc6\x5f\x09\x64\x5b" + "\x92\x90\xd8\xe1\xd1\xed\x4b\x42" + "\xd7\x37\xaf\x65\x3d\x11\x39\xb6" + "\x24\x8a\x60\xae\xd6\x1e\xbf\x0e" + "\x0d\xd7\xdc\x96\x0e\x65\x75\x4e" + "\x29\x06\x9d\xa4\x51\x3a\x10\x63" + "\x8f\x17\x07\xd5\x8e\x3c\xf4\x28" + "\x00\x5a\x5b\x05\x19\xd8\xc0\x6c" + "\xe5\x15\xe4\x9c\x9d\x71\x9d\x5e" + "\x94\x29\x1a\xa7\x80\xfa\x0e\x33" + "\x03\xdd\xb7\x3e\x9a\xa9\x26\x18" + "\x37\xa9\x64\x08\x4d\x94\x5a\x88" + "\xca\x35\xce\x81\x02\xe3\x1f\x1b" + "\x89\x1a\x77\x85\xe3\x41\x6d\x32" + "\x42\x19\x23\x7d\xc8\x73\xee\x25" + "\x85\x0d\xf8\x31\x25\x79\x1b\x6f" + "\x79\x25\xd2\xd8\xd4\x23\xfd\xf7" + "\x82\x36\x6a\x0c\x46\x22\x15\xe9" + "\xff\x72\x41\x91\x91\x7d\x3a\xb7" + "\xdd\x65\x99\x70\xf6\x8d\x84\xf8" + "\x67\x15\x20\x11\xd6\xb2\x55\x7b" + "\xdb\x87\xee\xef\x55\x89\x2a\x59" + "\x2b\x07\x8f\x43\x8a\x59\x3c\x01" + "\x8b\x65\x54\xa1\x66\xd5\x38\xbd" + "\xc6\x30\xa9\xcc\x49\xb6\xa8\x1b" + "\xb8\xc0\x0e\xe3\x45\x28\xe2\xff" + "\x41\x9f\x7e\x7c\xd1\xae\x9e\x25" + "\x3f\x4c\x7c\x7c\xf4\xa8\x26\x4d" + "\x5c\xfd\x4b\x27\x18\xf9\x61\x76" + "\x48\xba\x0c\x6b\xa9\x4d\xfc\xf5" + "\x3b\x35\x7e\x2f\x4a\xa9\xc2\x9a" + "\xae\xab\x86\x09\x89\xc9\xc2\x40" + "\x39\x2c\x81\xb3\xb8\x17\x67\xc2" + "\x0d\x32\x4a\x3a\x67\x81\xd7\x1a" + "\x34\x52\xc5\xdb\x0a\xf5\x63\x39" + "\xea\x1f\xe1\x7c\xa1\x9e\xc1\x35" + "\xe3\xb1\x18\x45\x67\xf9\x22\x38" + "\x95\xd9\x34\x34\x86\xc6\x41\x94" + "\x15\xf9\x5b\x41\xa6\x87\x8b\xf8" + "\xd5\xe1\x1b\xe2\x5b\xf3\x86\x10" + "\xff\xe6\xae\x69\x76\xbc\x0d\xb4" + "\x09\x90\x0c\xa2\x65\x0c\xad\x74" + "\xf5\xd7\xff\xda\xc1\xce\x85\xbe" + "\x00\xa7\xff\x4d\x2f\x65\xd3\x8c" + "\x86\x2d\x05\xe8\xed\x3e\x6b\x8b" + "\x0f\x3d\x83\x8c\xf1\x1d\x5b\x96" + "\x2e\xb1\x9c\xc2\x98\xe1\x70\xb9" + "\xba\x5c\x8a\x43\xd6\x34\xa7\x2d" + "\xc9\x92\xae\xf2\xa5\x7b\x05\x49" + "\xa7\x33\x34\x86\xca\xe4\x96\x23" + "\x76\x5b\xf2\xc6\xf1\x51\x28\x42" + "\x7b\xcc\x76\x8f\xfa\xa2\xad\x31" + "\xd4\xd6\x7a\x6d\x25\x25\x54\xe4" + "\x3f\x50\x59\xe1\x5c\x05\xb7\x27" + "\x48\xbf\x07\xec\x1b\x13\xbe\x2b" + "\xa1\x57\x2b\xd5\xab\xd7\xd0\x4c" + "\x1e\xcb\x71\x9b\xc5\x90\x85\xd3" + "\xde\x59\xec\x71\xeb\x89\xbb\xd0" + "\x09\x50\xe1\x16\x3f\xfd\x1c\x34" + "\xc3\x1c\xa1\x10\x77\x53\x98\xef" + "\xf2\xfd\xa5\x01\x59\xc2\x9b\x26" + "\xc7\x42\xd9\x49\xda\x58\x2b\x6e" + "\x9f\x53\x19\x76\x7e\xd9\xc9\x0e" + "\x68\xc8\x7f\x51\x22\x42\xef\x49" + "\xa4\x55\xb6\x36\xac\x09\xc7\x31" + "\x88\x15\x4b\x2e\x8f\x3a\x08\xf7" + "\xd8\xf7\xa8\xc5\xa9\x33\xa6\x45" + "\xe4\xc4\x94\x76\xf3\x0d\x8f\x7e" + "\xc8\xf6\xbc\x23\x0a\xb6\x4c\xd3" + "\x6a\xcd\x36\xc2\x90\x5c\x5c\x3c" + "\x65\x7b\xc2\xd6\xcc\xe6\x0d\x87" + "\x73\x2e\x71\x79\x16\x06\x63\x28" + "\x09\x15\xd8\x89\x38\x38\x3d\xb5" + "\x42\x1c\x08\x24\xf7\x2a\xd2\x9d" + "\xc8\xca\xef\xf9\x27\xd8\x07\x86" + "\xf7\x43\x0b\x55\x15\x3f\x9f\x83" + "\xef\xdc\x49\x9d\x2a\xc1\x54\x62" + "\xbd\x9b\x66\x55\x9f\xb7\x12\xf3" + "\x1b\x4d\x9d\x2a\x5c\xed\x87\x75" + "\x87\x26\xec\x61\x2c\xb4\x0f\x89" + "\xb0\xfb\x2e\x68\x5d\x15\xc7\x8d" + "\x2e\xc0\xd9\xec\xaf\x4f\xd2\x25" + "\x29\xe8\xd2\x26\x2b\x67\xe9\xfc" + "\x2b\xa8\x67\x96\x12\x1f\x5b\x96" + "\xc6\x14\x53\xaf\x44\xea\xd6\xe2" + "\x94\x98\xe4\x12\x93\x4c\x92\xe0" + "\x18\xa5\x8d\x2d\xe4\x71\x3c\x47" + "\x4c\xf7\xe6\x47\x9e\xc0\x68\xdf" + "\xd4\xf5\x5a\x74\xb1\x2b\x29\x03" + "\x19\x07\xaf\x90\x62\x5c\x68\x98" + "\x48\x16\x11\x02\x9d\xee\xb4\x9b" + "\xe5\x42\x7f\x08\xfd\x16\x32\x0b" + "\xd0\xb3\xfa\x2b\xb7\x99\xf9\x29" + "\xcd\x20\x45\x9f\xb3\x1a\x5d\xa2" + "\xaf\x4d\xe0\xbd\x42\x0d\xbc\x74" + "\x99\x9c\x8e\x53\x1a\xb4\x3e\xbd" + "\xa2\x9a\x2d\xf7\xf8\x39\x0f\x67" + "\x63\xfc\x6b\xc0\xaf\xb3\x4b\x4f" + "\x55\xc4\xcf\xa7\xc8\x04\x11\x3e" + "\x14\x32\xbb\x1b\x38\x77\xd6\x7f" + "\x54\x4c\xdf\x75\xf3\x07\x2d\x33" + "\x9b\xa8\x20\xe1\x7b\x12\xb5\xf3" + "\xef\x2f\xce\x72\xe5\x24\x60\xc1" + "\x30\xe2\xab\xa1\x8e\x11\x09\xa8" + "\x21\x33\x44\xfe\x7f\x35\x32\x93" + "\x39\xa7\xad\x8b\x79\x06\xb2\xcb" + "\x4e\xa9\x5f\xc7\xba\x74\x29\xec" + "\x93\xa0\x4e\x54\x93\xc0\xbc\x55" + "\x64\xf0\x48\xe5\x57\x99\xee\x75" + "\xd6\x79\x0f\x66\xb7\xc6\x57\x76" + "\xf7\xb7\xf3\x9c\xc5\x60\xe8\x7f" + "\x83\x76\xd6\x0e\xaa\xe6\x90\x39" + "\x1d\xa6\x32\x6a\x34\xe3\x55\xf8" + "\x58\xa0\x58\x7d\x33\xe0\x22\x39" + "\x44\x64\x87\x86\x5a\x2f\xa7\x7e" + "\x0f\x38\xea\xb0\x30\xcc\x61\xa5" + "\x6a\x32\xae\x1e\xf7\xe9\xd0\xa9" + "\x0c\x32\x4b\xb5\x49\x28\xab\x85" + "\x2f\x8e\x01\x36\x38\x52\xd0\xba" + "\xd6\x02\x78\xf8\x0e\x3e\x9c\x8b" + "\x6b\x45\x99\x3f\x5c\xfe\x58\xf1" + "\x5c\x94\x04\xe1\xf5\x18\x6d\x51" + "\xb2\x5d\x18\x20\xb6\xc2\x9a\x42" + "\x1d\xb3\xab\x3c\xb6\x3a\x13\x03" + "\xb2\x46\x82\x4f\xfc\x64\xbc\x4f" + "\xca\xfa\x9c\xc0\xd5\xa7\xbd\x11" + "\xb7\xe4\x5a\xf6\x6f\x4d\x4d\x54" + "\xea\xa4\x98\x66\xd4\x22\x3b\xd3" + "\x8f\x34\x47\xd9\x7c\xf4\x72\x3b" + "\x4d\x02\x77\xf6\xd6\xdd\x08\x0a" + "\x81\xe1\x86\x89\x3e\x56\x10\x3c" + "\xba\xd7\x81\x8c\x08\xbc\x8b\xe2" + "\x53\xec\xa7\x89\xee\xc8\x56\xb5" + "\x36\x2c\xb2\x03\xba\x99\xdd\x7c" + "\x48\xa0\xb0\xbc\x91\x33\xe9\xa8" + "\xcb\xcd\xcf\x59\x5f\x1f\x15\xe2" + "\x56\xf5\x4e\x01\x35\x27\x45\x77" + "\x47\xc8\xbc\xcb\x7e\x39\xc1\x97" + "\x28\xd3\x84\xfc\x2c\x3e\xc8\xad" + "\x9c\xf8\x8a\x61\x9c\x28\xaa\xc5" + "\x99\x20\x43\x85\x9d\xa5\xe2\x8b" + "\xb8\xae\xeb\xd0\x32\x0d\x52\x78" + "\x09\x56\x3f\xc7\xd8\x7e\x26\xfc" + "\x37\xfb\x6f\x04\xfc\xfa\x92\x10" + "\xac\xf8\x3e\x21\xdc\x8c\x21\x16" + "\x7d\x67\x6e\xf6\xcd\xda\xb6\x98" + "\x23\xab\x23\x3c\xb2\x10\xa0\x53" + "\x5a\x56\x9f\xc5\xd0\xff\xbb\xe4" + "\x98\x3c\x69\x1e\xdb\x38\x8f\x7e" + "\x0f\xd2\x98\x88\x81\x8b\x45\x67" + "\xea\x33\xf1\xeb\xe9\x97\x55\x2e" + "\xd9\xaa\xeb\x5a\xec\xda\xe1\x68" + "\xa8\x9d\x3c\x84\x7c\x05\x3d\x62" + "\x87\x8f\x03\x21\x28\x95\x0c\x89" + "\x25\x22\x4a\xb0\x93\xa9\x50\xa2" + "\x2f\x57\x6e\x18\x42\x19\x54\x0c" + "\x55\x67\xc6\x11\x49\xf4\x5c\xd2" + "\xe9\x3d\xdd\x8b\x48\x71\x21\x00" + "\xc3\x9a\x6c\x85\x74\x28\x83\x4a" + "\x1b\x31\x05\xe1\x06\x92\xe7\xda" + "\x85\x73\x78\x45\x20\x7f\xae\x13" + "\x7c\x33\x06\x22\xf4\x83\xf9\x35" + "\x3f\x6c\x71\xa8\x4e\x48\xbe\x9b" + "\xce\x8a\xba\xda\xbe\x28\x08\xf7" + "\xe2\x14\x8c\x71\xea\x72\xf9\x33" + "\xf2\x88\x3f\xd7\xbb\x69\x6c\x29" + "\x19\xdc\x84\xce\x1f\x12\x4f\xc8" + "\xaf\xa5\x04\xba\x5a\xab\xb0\xd9" + "\x14\x1f\x6c\x68\x98\x39\x89\x7a" + "\xd9\xd8\x2f\xdf\xa8\x47\x4a\x25" + "\xe2\xfb\x33\xf4\x59\x78\xe1\x68" + "\x85\xcf\xfe\x59\x20\xd4\x05\x1d" + "\x80\x99\xae\xbc\xca\xae\x0f\x2f" + "\x65\x43\x34\x8e\x7e\xac\xd3\x93" + "\x2f\xac\x6d\x14\x3d\x02\x07\x70" + "\x9d\xa4\xf3\x1b\x5c\x36\xfc\x01" + "\x73\x34\x85\x0c\x6c\xd6\xf1\xbd" + "\x3f\xdf\xee\xf5\xd9\xba\x56\xef" + "\xf4\x9b\x6b\xee\x9f\x5a\x78\x6d" + "\x32\x19\xf4\xf7\xf8\x4c\x69\x0b" + "\x4b\xbc\xbb\xb7\xf2\x85\xaf\x70" + "\x75\x24\x6c\x54\xa7\x0e\x4d\x1d" + "\x01\xbf\x08\xac\xcf\x7f\x2c\xe3" + "\x14\x89\x5e\x70\x5a\x99\x92\xcd" + "\x01\x84\xc8\xd2\xab\xe5\x4f\x58" + "\xe7\x0f\x2f\x0e\xff\x68\xea\xfd" + "\x15\xb3\x17\xe6\xb0\xe7\x85\xd8" + "\x23\x2e\x05\xc7\xc9\xc4\x46\x1f" + "\xe1\x9e\x49\x20\x23\x24\x4d\x7e" + "\x29\x65\xff\xf4\xb6\xfd\x1a\x85" + "\xc4\x16\xec\xfc\xea\x7b\xd6\x2c" + "\x43\xf8\xb7\xbf\x79\xc0\x85\xcd" + "\xef\xe1\x98\xd3\xa5\xf7\x90\x8c" + "\xe9\x7f\x80\x6b\xd2\xac\x4c\x30" + "\xa7\xc6\x61\x6c\xd2\xf9\x2c\xff" + "\x30\xbc\x22\x81\x7d\x93\x12\xe4" + "\x0a\xcd\xaf\xdd\xe8\xab\x0a\x1e" + "\x13\xa4\x27\xc3\x5f\xf7\x4b\xbb" + "\x37\x09\x4b\x91\x6f\x92\x4f\xaf" + "\x52\xee\xdf\xef\x09\x6f\xf7\x5c" + "\x6e\x12\x17\x72\x63\x57\xc7\xba" + "\x3b\x6b\x38\x32\x73\x1b\x9c\x80" + "\xc1\x7a\xc6\xcf\xcd\x35\xc0\x6b" + "\x31\x1a\x6b\xe9\xd8\x2c\x29\x3f" + "\x96\xfb\xb6\xcd\x13\x91\x3b\xc2" + "\xd2\xa3\x31\x8d\xa4\xcd\x57\xcd" + "\x13\x3d\x64\xfd\x06\xce\xe6\xdc" + "\x0c\x24\x43\x31\x40\x57\xf1\x72" + "\x17\xe3\x3a\x63\x6d\x35\xcf\x5d" + "\x97\x40\x59\xdd\xf7\x3c\x02\xf7" + "\x1c\x7e\x05\xbb\xa9\x0d\x01\xb1" + "\x8e\xc0\x30\xa9\x53\x24\xc9\x89" + "\x84\x6d\xaa\xd0\xcd\x91\xc2\x4d" + "\x91\xb0\x89\xe2\xbf\x83\x44\xaa" + "\x28\x72\x23\xa0\xc2\xad\xad\x1c" + "\xfc\x3f\x09\x7a\x0b\xdc\xc5\x1b" + "\x87\x13\xc6\x5b\x59\x8d\xf2\xc8" + "\xaf\xdf\x11\x95", + .rlen = 4100, + .np = 2, + .tap = { 4064, 36 }, + }, +}; + +/* + * CTS (Cipher Text Stealing) mode tests + */ +#define CTS_MODE_ENC_TEST_VECTORS 6 +#define CTS_MODE_DEC_TEST_VECTORS 6 +static struct cipher_testvec cts_mode_enc_tv_template[] = { + { /* from rfc3962 */ + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .ilen = 17, + .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20", + .rlen = 17, + .result = "\xc6\x35\x35\x68\xf2\xbf\x8c\xb4" + "\xd8\xa5\x80\x36\x2d\xa7\xff\x7f" + "\x97", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .ilen = 31, + .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20", + .rlen = 31, + .result = "\xfc\x00\x78\x3e\x0e\xfd\xb2\xc1" + "\xd4\x45\xd4\xc8\xef\xf7\xed\x22" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .ilen = 32, + .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43", + .rlen = 32, + .result = "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .ilen = 47, + .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20" + "\x70\x6c\x65\x61\x73\x65\x2c", + .rlen = 47, + .result = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\xb3\xff\xfd\x94\x0c\x16\xa1\x8c" + "\x1b\x55\x49\xd2\xf8\x38\x02\x9e" + "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .ilen = 48, + .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20" + "\x70\x6c\x65\x61\x73\x65\x2c\x20", + .rlen = 48, + .result = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" + "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8" + "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .ilen = 64, + .input = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20" + "\x70\x6c\x65\x61\x73\x65\x2c\x20" + "\x61\x6e\x64\x20\x77\x6f\x6e\x74" + "\x6f\x6e\x20\x73\x6f\x75\x70\x2e", + .rlen = 64, + .result = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" + "\x48\x07\xef\xe8\x36\xee\x89\xa5" + "\x26\x73\x0d\xbc\x2f\x7b\xc8\x40" + "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" + "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8", + } +}; + +static struct cipher_testvec cts_mode_dec_tv_template[] = { + { /* from rfc3962 */ + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .rlen = 17, + .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20", + .ilen = 17, + .input = "\xc6\x35\x35\x68\xf2\xbf\x8c\xb4" + "\xd8\xa5\x80\x36\x2d\xa7\xff\x7f" + "\x97", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .rlen = 31, + .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20", + .ilen = 31, + .input = "\xfc\x00\x78\x3e\x0e\xfd\xb2\xc1" + "\xd4\x45\xd4\xc8\xef\xf7\xed\x22" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .rlen = 32, + .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43", + .ilen = 32, + .input = "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .rlen = 47, + .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20" + "\x70\x6c\x65\x61\x73\x65\x2c", + .ilen = 47, + .input = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\xb3\xff\xfd\x94\x0c\x16\xa1\x8c" + "\x1b\x55\x49\xd2\xf8\x38\x02\x9e" + "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .rlen = 48, + .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20" + "\x70\x6c\x65\x61\x73\x65\x2c\x20", + .ilen = 48, + .input = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" + "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8" + "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8", + }, { + .klen = 16, + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .rlen = 64, + .result = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20" + "\x70\x6c\x65\x61\x73\x65\x2c\x20" + "\x61\x6e\x64\x20\x77\x6f\x6e\x74" + "\x6f\x6e\x20\x73\x6f\x75\x70\x2e", + .ilen = 64, + .input = "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\x39\x31\x25\x23\xa7\x86\x62\xd5" + "\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" + "\x48\x07\xef\xe8\x36\xee\x89\xa5" + "\x26\x73\x0d\xbc\x2f\x7b\xc8\x40" + "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0" + "\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8", + } +}; + +/* + * Compression stuff. + */ +#define COMP_BUF_SIZE 512 + +struct comp_testvec { + int inlen, outlen; + char input[COMP_BUF_SIZE]; + char output[COMP_BUF_SIZE]; +}; + +/* + * Deflate test vectors (null-terminated strings). + * Params: winbits=11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL. + */ +#define DEFLATE_COMP_TEST_VECTORS 2 +#define DEFLATE_DECOMP_TEST_VECTORS 2 + +static struct comp_testvec deflate_comp_tv_template[] = { + { + .inlen = 70, + .outlen = 38, + .input = "Join us now and share the software " + "Join us now and share the software ", + .output = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56" + "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51" + "\x28\xce\x48\x2c\x4a\x55\x28\xc9" + "\x48\x55\x28\xce\x4f\x2b\x29\x07" + "\x71\xbc\x08\x2b\x01\x00", + }, { + .inlen = 191, + .outlen = 122, + .input = "This document describes a compression method based on the DEFLATE" + "compression algorithm. This document defines the application of " + "the DEFLATE algorithm to the IP Payload Compression Protocol.", + .output = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04" + "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09" + "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8" + "\x24\xdb\x67\xd9\x47\xc1\xef\x49" + "\x68\x12\x51\xae\x76\x67\xd6\x27" + "\x19\x88\x1a\xde\x85\xab\x21\xf2" + "\x08\x5d\x16\x1e\x20\x04\x2d\xad" + "\xf3\x18\xa2\x15\x85\x2d\x69\xc4" + "\x42\x83\x23\xb6\x6c\x89\x71\x9b" + "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f" + "\xed\x62\xa9\x4c\x80\xff\x13\xaf" + "\x52\x37\xed\x0e\x52\x6b\x59\x02" + "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98" + "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a" + "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79" + "\xfa\x02", + }, +}; + +static struct comp_testvec deflate_decomp_tv_template[] = { + { + .inlen = 122, + .outlen = 191, + .input = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04" + "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09" + "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8" + "\x24\xdb\x67\xd9\x47\xc1\xef\x49" + "\x68\x12\x51\xae\x76\x67\xd6\x27" + "\x19\x88\x1a\xde\x85\xab\x21\xf2" + "\x08\x5d\x16\x1e\x20\x04\x2d\xad" + "\xf3\x18\xa2\x15\x85\x2d\x69\xc4" + "\x42\x83\x23\xb6\x6c\x89\x71\x9b" + "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f" + "\xed\x62\xa9\x4c\x80\xff\x13\xaf" + "\x52\x37\xed\x0e\x52\x6b\x59\x02" + "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98" + "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a" + "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79" + "\xfa\x02", + .output = "This document describes a compression method based on the DEFLATE" + "compression algorithm. This document defines the application of " + "the DEFLATE algorithm to the IP Payload Compression Protocol.", + }, { + .inlen = 38, + .outlen = 70, + .input = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56" + "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51" + "\x28\xce\x48\x2c\x4a\x55\x28\xc9" + "\x48\x55\x28\xce\x4f\x2b\x29\x07" + "\x71\xbc\x08\x2b\x01\x00", + .output = "Join us now and share the software " + "Join us now and share the software ", + }, +}; + +/* + * LZO test vectors (null-terminated strings). + */ +#define LZO_COMP_TEST_VECTORS 2 +#define LZO_DECOMP_TEST_VECTORS 2 + +static struct comp_testvec lzo_comp_tv_template[] = { + { + .inlen = 70, + .outlen = 46, + .input = "Join us now and share the software " + "Join us now and share the software ", + .output = "\x00\x0d\x4a\x6f\x69\x6e\x20\x75" + "\x73\x20\x6e\x6f\x77\x20\x61\x6e" + "\x64\x20\x73\x68\x61\x72\x65\x20" + "\x74\x68\x65\x20\x73\x6f\x66\x74" + "\x77\x70\x01\x01\x4a\x6f\x69\x6e" + "\x3d\x88\x00\x11\x00\x00", + }, { + .inlen = 159, + .outlen = 133, + .input = "This document describes a compression method based on the LZO " + "compression algorithm. This document defines the application of " + "the LZO algorithm used in UBIFS.", + .output = "\x00\x2b\x54\x68\x69\x73\x20\x64" + "\x6f\x63\x75\x6d\x65\x6e\x74\x20" + "\x64\x65\x73\x63\x72\x69\x62\x65" + "\x73\x20\x61\x20\x63\x6f\x6d\x70" + "\x72\x65\x73\x73\x69\x6f\x6e\x20" + "\x6d\x65\x74\x68\x6f\x64\x20\x62" + "\x61\x73\x65\x64\x20\x6f\x6e\x20" + "\x74\x68\x65\x20\x4c\x5a\x4f\x2b" + "\x8c\x00\x0d\x61\x6c\x67\x6f\x72" + "\x69\x74\x68\x6d\x2e\x20\x20\x54" + "\x68\x69\x73\x2a\x54\x01\x02\x66" + "\x69\x6e\x65\x73\x94\x06\x05\x61" + "\x70\x70\x6c\x69\x63\x61\x74\x76" + "\x0a\x6f\x66\x88\x02\x60\x09\x27" + "\xf0\x00\x0c\x20\x75\x73\x65\x64" + "\x20\x69\x6e\x20\x55\x42\x49\x46" + "\x53\x2e\x11\x00\x00", + }, +}; + +static struct comp_testvec lzo_decomp_tv_template[] = { + { + .inlen = 133, + .outlen = 159, + .input = "\x00\x2b\x54\x68\x69\x73\x20\x64" + "\x6f\x63\x75\x6d\x65\x6e\x74\x20" + "\x64\x65\x73\x63\x72\x69\x62\x65" + "\x73\x20\x61\x20\x63\x6f\x6d\x70" + "\x72\x65\x73\x73\x69\x6f\x6e\x20" + "\x6d\x65\x74\x68\x6f\x64\x20\x62" + "\x61\x73\x65\x64\x20\x6f\x6e\x20" + "\x74\x68\x65\x20\x4c\x5a\x4f\x2b" + "\x8c\x00\x0d\x61\x6c\x67\x6f\x72" + "\x69\x74\x68\x6d\x2e\x20\x20\x54" + "\x68\x69\x73\x2a\x54\x01\x02\x66" + "\x69\x6e\x65\x73\x94\x06\x05\x61" + "\x70\x70\x6c\x69\x63\x61\x74\x76" + "\x0a\x6f\x66\x88\x02\x60\x09\x27" + "\xf0\x00\x0c\x20\x75\x73\x65\x64" + "\x20\x69\x6e\x20\x55\x42\x49\x46" + "\x53\x2e\x11\x00\x00", + .output = "This document describes a compression method based on the LZO " + "compression algorithm. This document defines the application of " + "the LZO algorithm used in UBIFS.", + }, { + .inlen = 46, + .outlen = 70, + .input = "\x00\x0d\x4a\x6f\x69\x6e\x20\x75" + "\x73\x20\x6e\x6f\x77\x20\x61\x6e" + "\x64\x20\x73\x68\x61\x72\x65\x20" + "\x74\x68\x65\x20\x73\x6f\x66\x74" + "\x77\x70\x01\x01\x4a\x6f\x69\x6e" + "\x3d\x88\x00\x11\x00\x00", + .output = "Join us now and share the software " + "Join us now and share the software ", + }, +}; + +/* + * Michael MIC test vectors from IEEE 802.11i + */ +#define MICHAEL_MIC_TEST_VECTORS 6 + +static struct hash_testvec michael_mic_tv_template[] = { + { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ksize = 8, + .plaintext = zeroed_string, + .psize = 0, + .digest = "\x82\x92\x5c\x1c\xa1\xd1\x30\xb8", + }, + { + .key = "\x82\x92\x5c\x1c\xa1\xd1\x30\xb8", + .ksize = 8, + .plaintext = "M", + .psize = 1, + .digest = "\x43\x47\x21\xca\x40\x63\x9b\x3f", + }, + { + .key = "\x43\x47\x21\xca\x40\x63\x9b\x3f", + .ksize = 8, + .plaintext = "Mi", + .psize = 2, + .digest = "\xe8\xf9\xbe\xca\xe9\x7e\x5d\x29", + }, + { + .key = "\xe8\xf9\xbe\xca\xe9\x7e\x5d\x29", + .ksize = 8, + .plaintext = "Mic", + .psize = 3, + .digest = "\x90\x03\x8f\xc6\xcf\x13\xc1\xdb", + }, + { + .key = "\x90\x03\x8f\xc6\xcf\x13\xc1\xdb", + .ksize = 8, + .plaintext = "Mich", + .psize = 4, + .digest = "\xd5\x5e\x10\x05\x10\x12\x89\x86", + }, + { + .key = "\xd5\x5e\x10\x05\x10\x12\x89\x86", + .ksize = 8, + .plaintext = "Michael", + .psize = 7, + .digest = "\x0a\x94\x2b\x12\x4e\xca\xa5\x46", + } +}; + +/* + * CRC32C test vectors + */ +#define CRC32C_TEST_VECTORS 14 + +static struct hash_testvec crc32c_tv_template[] = { + { + .psize = 0, + .digest = "\x00\x00\x00\x00", + }, + { + .key = "\x87\xa9\xcb\xed", + .ksize = 4, + .psize = 0, + .digest = "\x78\x56\x34\x12", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" + "\x21\x22\x23\x24\x25\x26\x27\x28", + .psize = 40, + .digest = "\x7f\x15\x2c\x0e", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" + "\x31\x32\x33\x34\x35\x36\x37\x38" + "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" + "\x41\x42\x43\x44\x45\x46\x47\x48" + "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50", + .psize = 40, + .digest = "\xf6\xeb\x80\xe9", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\x51\x52\x53\x54\x55\x56\x57\x58" + "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" + "\x61\x62\x63\x64\x65\x66\x67\x68" + "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" + "\x71\x72\x73\x74\x75\x76\x77\x78", + .psize = 40, + .digest = "\xed\xbd\x74\xde", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" + "\x81\x82\x83\x84\x85\x86\x87\x88" + "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" + "\x91\x92\x93\x94\x95\x96\x97\x98" + "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0", + .psize = 40, + .digest = "\x62\xc8\x79\xd5", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" + "\xa9\xaa\xab\xac\xad\xae\xaf\xb0" + "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8" + "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" + "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8", + .psize = 40, + .digest = "\xd0\x9a\x97\xba", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" + "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" + "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" + "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" + "\xe9\xea\xeb\xec\xed\xee\xef\xf0", + .psize = 40, + .digest = "\x13\xd9\x29\x2b", + }, + { + .key = "\x80\xea\xd3\xf1", + .ksize = 4, + .plaintext = "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" + "\x31\x32\x33\x34\x35\x36\x37\x38" + "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" + "\x41\x42\x43\x44\x45\x46\x47\x48" + "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50", + .psize = 40, + .digest = "\x0c\xb5\xe2\xa2", + }, + { + .key = "\xf3\x4a\x1d\x5d", + .ksize = 4, + .plaintext = "\x51\x52\x53\x54\x55\x56\x57\x58" + "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" + "\x61\x62\x63\x64\x65\x66\x67\x68" + "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" + "\x71\x72\x73\x74\x75\x76\x77\x78", + .psize = 40, + .digest = "\xd1\x7f\xfb\xa6", + }, + { + .key = "\x2e\x80\x04\x59", + .ksize = 4, + .plaintext = "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" + "\x81\x82\x83\x84\x85\x86\x87\x88" + "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" + "\x91\x92\x93\x94\x95\x96\x97\x98" + "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0", + .psize = 40, + .digest = "\x59\x33\xe6\x7a", + }, + { + .key = "\xa6\xcc\x19\x85", + .ksize = 4, + .plaintext = "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" + "\xa9\xaa\xab\xac\xad\xae\xaf\xb0" + "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8" + "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" + "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8", + .psize = 40, + .digest = "\xbe\x03\x01\xd2", + }, + { + .key = "\x41\xfc\xfe\x2d", + .ksize = 4, + .plaintext = "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" + "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" + "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" + "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" + "\xe9\xea\xeb\xec\xed\xee\xef\xf0", + .psize = 40, + .digest = "\x75\xd3\xc5\x24", + }, + { + .key = "\xff\xff\xff\xff", + .ksize = 4, + .plaintext = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" + "\x21\x22\x23\x24\x25\x26\x27\x28" + "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" + "\x31\x32\x33\x34\x35\x36\x37\x38" + "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" + "\x41\x42\x43\x44\x45\x46\x47\x48" + "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" + "\x51\x52\x53\x54\x55\x56\x57\x58" + "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" + "\x61\x62\x63\x64\x65\x66\x67\x68" + "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" + "\x71\x72\x73\x74\x75\x76\x77\x78" + "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" + "\x81\x82\x83\x84\x85\x86\x87\x88" + "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" + "\x91\x92\x93\x94\x95\x96\x97\x98" + "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0" + "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" + "\xa9\xaa\xab\xac\xad\xae\xaf\xb0" + "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8" + "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" + "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8" + "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" + "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" + "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" + "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" + "\xe9\xea\xeb\xec\xed\xee\xef\xf0", + .psize = 240, + .digest = "\x75\xd3\xc5\x24", + .np = 2, + .tap = { 31, 209 } + }, +}; + +#endif /* _CRYPTO_TESTMGR_H */ diff --git a/include/linux/crypto.h b/include/linux/crypto.h index c43dc47fdf75..7ea0a4bc4ced 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -515,6 +515,8 @@ struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags); struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask); void crypto_free_tfm(struct crypto_tfm *tfm); +int alg_test(const char *driver, const char *alg, u32 type, u32 mask); + /* * Transform helpers which query the underlying algorithm. */ -- cgit v1.2.3 From 73d3864a4823abda19ebc4387b6ddcbf416e3a77 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 3 Aug 2008 21:15:23 +0800 Subject: crypto: api - Use test infrastructure This patch makes use of the new testing infrastructure by requiring algorithms to pass a run-time test before they're made available to users. Signed-off-by: Herbert Xu --- crypto/algapi.c | 143 ++++++++++++++++++++++++++++++++++++++++++------- crypto/algboss.c | 75 ++++++++++++++++++++++++-- crypto/api.c | 73 +++++++++++++++++++------ crypto/internal.h | 7 ++- crypto/proc.c | 3 ++ include/linux/crypto.h | 8 +++ 6 files changed, 267 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/crypto/algapi.c b/crypto/algapi.c index e9154c1347ca..7c41e7405c41 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -21,6 +21,8 @@ #include "internal.h" +static void crypto_remove_final(struct list_head *list); + static LIST_HEAD(crypto_template_list); void crypto_larval_error(const char *name, u32 type, u32 mask) @@ -126,23 +128,97 @@ static void crypto_remove_spawns(struct list_head *spawns, } } -static int __crypto_register_alg(struct crypto_alg *alg, - struct list_head *list) +static struct crypto_larval *__crypto_register_alg(struct crypto_alg *alg) { struct crypto_alg *q; + struct crypto_larval *larval; int ret = -EAGAIN; if (crypto_is_dead(alg)) - goto out; + goto err; INIT_LIST_HEAD(&alg->cra_users); + /* No cheating! */ + alg->cra_flags &= ~CRYPTO_ALG_TESTED; + ret = -EEXIST; atomic_set(&alg->cra_refcnt, 1); list_for_each_entry(q, &crypto_alg_list, cra_list) { if (q == alg) - goto out; + goto err; + + if (crypto_is_larval(q)) { + if (!strcmp(alg->cra_driver_name, q->cra_driver_name)) + goto err; + continue; + } + + if (!strcmp(q->cra_driver_name, alg->cra_name) || + !strcmp(q->cra_name, alg->cra_driver_name)) + goto err; + } + + larval = crypto_larval_alloc(alg->cra_name, + alg->cra_flags | CRYPTO_ALG_TESTED, 0); + if (IS_ERR(larval)) + goto out; + + ret = -ENOENT; + larval->adult = crypto_mod_get(alg); + if (!larval->adult) + goto free_larval; + + atomic_set(&larval->alg.cra_refcnt, 1); + memcpy(larval->alg.cra_driver_name, alg->cra_driver_name, + CRYPTO_MAX_ALG_NAME); + larval->alg.cra_priority = alg->cra_priority; + + list_add(&alg->cra_list, &crypto_alg_list); + list_add(&larval->alg.cra_list, &crypto_alg_list); + +out: + return larval; + +free_larval: + kfree(larval); +err: + larval = ERR_PTR(ret); + goto out; +} + +void crypto_alg_tested(const char *name, int err) +{ + struct crypto_larval *test; + struct crypto_alg *alg; + struct crypto_alg *q; + LIST_HEAD(list); + + down_write(&crypto_alg_sem); + list_for_each_entry(q, &crypto_alg_list, cra_list) { + if (!crypto_is_larval(q)) + continue; + + test = (struct crypto_larval *)q; + + if (!strcmp(q->cra_driver_name, name)) + goto found; + } + + printk(KERN_ERR "alg: Unexpected test result for %s: %d\n", name, err); + goto unlock; + +found: + alg = test->adult; + if (err || list_empty(&alg->cra_list)) + goto complete; + + alg->cra_flags |= CRYPTO_ALG_TESTED; + + list_for_each_entry(q, &crypto_alg_list, cra_list) { + if (q == alg) + continue; if (crypto_is_moribund(q)) continue; @@ -178,17 +254,18 @@ static int __crypto_register_alg(struct crypto_alg *alg, q->cra_priority > alg->cra_priority) continue; - crypto_remove_spawns(&q->cra_users, list, alg->cra_flags); + crypto_remove_spawns(&q->cra_users, &list, alg->cra_flags); } - - list_add(&alg->cra_list, &crypto_alg_list); - crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg); - ret = 0; +complete: + complete_all(&test->completion); -out: - return ret; +unlock: + up_write(&crypto_alg_sem); + + crypto_remove_final(&list); } +EXPORT_SYMBOL_GPL(crypto_alg_tested); static void crypto_remove_final(struct list_head *list) { @@ -201,9 +278,27 @@ static void crypto_remove_final(struct list_head *list) } } +static void crypto_wait_for_test(struct crypto_larval *larval) +{ + int err; + + err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult); + if (err != NOTIFY_STOP) { + if (WARN_ON(err != NOTIFY_DONE)) + goto out; + crypto_alg_tested(larval->alg.cra_driver_name, 0); + } + + err = wait_for_completion_interruptible(&larval->completion); + WARN_ON(err); + +out: + crypto_larval_kill(&larval->alg); +} + int crypto_register_alg(struct crypto_alg *alg) { - LIST_HEAD(list); + struct crypto_larval *larval; int err; err = crypto_check_alg(alg); @@ -211,11 +306,14 @@ int crypto_register_alg(struct crypto_alg *alg) return err; down_write(&crypto_alg_sem); - err = __crypto_register_alg(alg, &list); + larval = __crypto_register_alg(alg); up_write(&crypto_alg_sem); - crypto_remove_final(&list); - return err; + if (IS_ERR(larval)) + return PTR_ERR(larval); + + crypto_wait_for_test(larval); + return 0; } EXPORT_SYMBOL_GPL(crypto_register_alg); @@ -333,8 +431,8 @@ EXPORT_SYMBOL_GPL(crypto_lookup_template); int crypto_register_instance(struct crypto_template *tmpl, struct crypto_instance *inst) { - LIST_HEAD(list); - int err = -EINVAL; + struct crypto_larval *larval; + int err; err = crypto_check_alg(&inst->alg); if (err) @@ -344,8 +442,8 @@ int crypto_register_instance(struct crypto_template *tmpl, down_write(&crypto_alg_sem); - err = __crypto_register_alg(&inst->alg, &list); - if (err) + larval = __crypto_register_alg(&inst->alg); + if (IS_ERR(larval)) goto unlock; hlist_add_head(&inst->list, &tmpl->instances); @@ -354,7 +452,12 @@ int crypto_register_instance(struct crypto_template *tmpl, unlock: up_write(&crypto_alg_sem); - crypto_remove_final(&list); + err = PTR_ERR(larval); + if (IS_ERR(larval)) + goto err; + + crypto_wait_for_test(larval); + err = 0; err: return err; diff --git a/crypto/algboss.c b/crypto/algboss.c index 2662ac014841..ed9f663c82c6 100644 --- a/crypto/algboss.c +++ b/crypto/algboss.c @@ -45,6 +45,15 @@ struct cryptomgr_param { char larval[CRYPTO_MAX_ALG_NAME]; char template[CRYPTO_MAX_ALG_NAME]; + + u32 otype; + u32 omask; +}; + +struct crypto_test_param { + char driver[CRYPTO_MAX_ALG_NAME]; + char alg[CRYPTO_MAX_ALG_NAME]; + u32 type; }; static int cryptomgr_probe(void *data) @@ -76,8 +85,7 @@ out: module_put_and_exit(0); err: - crypto_larval_error(param->larval, param->type.data.type, - param->type.data.mask); + crypto_larval_error(param->larval, param->otype, param->omask); goto out; } @@ -169,13 +177,68 @@ static int cryptomgr_schedule_probe(struct crypto_larval *larval) param->type.attr.rta_len = sizeof(param->type); param->type.attr.rta_type = CRYPTOA_TYPE; - param->type.data.type = larval->alg.cra_flags; - param->type.data.mask = larval->mask; + param->type.data.type = larval->alg.cra_flags & ~CRYPTO_ALG_TESTED; + param->type.data.mask = larval->mask & ~CRYPTO_ALG_TESTED; param->tb[0] = ¶m->type.attr; + param->otype = larval->alg.cra_flags; + param->omask = larval->mask; + memcpy(param->larval, larval->alg.cra_name, CRYPTO_MAX_ALG_NAME); - thread = kthread_run(cryptomgr_probe, param, "cryptomgr"); + thread = kthread_run(cryptomgr_probe, param, "cryptomgr_probe"); + if (IS_ERR(thread)) + goto err_free_param; + + return NOTIFY_STOP; + +err_free_param: + kfree(param); +err_put_module: + module_put(THIS_MODULE); +err: + return NOTIFY_OK; +} + +static int cryptomgr_test(void *data) +{ + struct crypto_test_param *param = data; + u32 type = param->type; + int err = 0; + + if (!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) & + CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV)) + goto skiptest; + + if ((type & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_CIPHER) + goto skiptest; + + err = alg_test(param->driver, param->alg, 0, CRYPTO_ALG_TESTED); + +skiptest: + crypto_alg_tested(param->driver, err); + + kfree(param); + module_put_and_exit(0); +} + +static int cryptomgr_schedule_test(struct crypto_alg *alg) +{ + struct task_struct *thread; + struct crypto_test_param *param; + + if (!try_module_get(THIS_MODULE)) + goto err; + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) + goto err_put_module; + + memcpy(param->driver, alg->cra_driver_name, sizeof(param->driver)); + memcpy(param->alg, alg->cra_name, sizeof(param->alg)); + param->type = alg->cra_flags; + + thread = kthread_run(cryptomgr_test, param, "cryptomgr_test"); if (IS_ERR(thread)) goto err_free_param; @@ -195,6 +258,8 @@ static int cryptomgr_notify(struct notifier_block *this, unsigned long msg, switch (msg) { case CRYPTO_MSG_ALG_REQUEST: return cryptomgr_schedule_probe(data); + case CRYPTO_MSG_ALG_REGISTER: + return cryptomgr_schedule_test(data); } return NOTIFY_DONE; diff --git a/crypto/api.c b/crypto/api.c index 0906cedd4521..0444d242e985 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -55,6 +55,11 @@ void crypto_mod_put(struct crypto_alg *alg) } EXPORT_SYMBOL_GPL(crypto_mod_put); +static inline int crypto_is_test_larval(struct crypto_larval *larval) +{ + return larval->alg.cra_driver_name[0]; +} + static struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type, u32 mask) { @@ -71,6 +76,7 @@ static struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type, continue; if (crypto_is_larval(q) && + !crypto_is_test_larval((struct crypto_larval *)q) && ((struct crypto_larval *)q)->mask != mask) continue; @@ -104,10 +110,8 @@ static void crypto_larval_destroy(struct crypto_alg *alg) kfree(larval); } -static struct crypto_alg *crypto_larval_alloc(const char *name, u32 type, - u32 mask) +struct crypto_larval *crypto_larval_alloc(const char *name, u32 type, u32 mask) { - struct crypto_alg *alg; struct crypto_larval *larval; larval = kzalloc(sizeof(*larval), GFP_KERNEL); @@ -119,10 +123,25 @@ static struct crypto_alg *crypto_larval_alloc(const char *name, u32 type, larval->alg.cra_priority = -1; larval->alg.cra_destroy = crypto_larval_destroy; - atomic_set(&larval->alg.cra_refcnt, 2); strlcpy(larval->alg.cra_name, name, CRYPTO_MAX_ALG_NAME); init_completion(&larval->completion); + return larval; +} +EXPORT_SYMBOL_GPL(crypto_larval_alloc); + +static struct crypto_alg *crypto_larval_add(const char *name, u32 type, + u32 mask) +{ + struct crypto_alg *alg; + struct crypto_larval *larval; + + larval = crypto_larval_alloc(name, type, mask); + if (IS_ERR(larval)) + return ERR_CAST(larval); + + atomic_set(&larval->alg.cra_refcnt, 2); + down_write(&crypto_alg_sem); alg = __crypto_alg_lookup(name, type, mask); if (!alg) { @@ -152,14 +171,23 @@ EXPORT_SYMBOL_GPL(crypto_larval_kill); static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg) { struct crypto_larval *larval = (void *)alg; + long timeout; + + timeout = wait_for_completion_interruptible_timeout( + &larval->completion, 60 * HZ); - wait_for_completion_interruptible_timeout(&larval->completion, 60 * HZ); alg = larval->adult; - if (alg) { - if (!crypto_mod_get(alg)) - alg = ERR_PTR(-EAGAIN); - } else + if (timeout < 0) + alg = ERR_PTR(-EINTR); + else if (!timeout) + alg = ERR_PTR(-ETIMEDOUT); + else if (!alg) alg = ERR_PTR(-ENOENT); + else if (crypto_is_test_larval(larval) && + !(alg->cra_flags & CRYPTO_ALG_TESTED)) + alg = ERR_PTR(-EAGAIN); + else if (!crypto_mod_get(alg)) + alg = ERR_PTR(-EAGAIN); crypto_mod_put(&larval->alg); return alg; @@ -192,25 +220,40 @@ struct crypto_alg *crypto_larval_lookup(const char *name, u32 type, u32 mask) if (alg) return crypto_is_larval(alg) ? crypto_larval_wait(alg) : alg; - return crypto_larval_alloc(name, type, mask); + return crypto_larval_add(name, type, mask); } EXPORT_SYMBOL_GPL(crypto_larval_lookup); +int crypto_probing_notify(unsigned long val, void *v) +{ + int ok; + + ok = blocking_notifier_call_chain(&crypto_chain, val, v); + if (ok == NOTIFY_DONE) { + request_module("cryptomgr"); + ok = blocking_notifier_call_chain(&crypto_chain, val, v); + } + + return ok; +} +EXPORT_SYMBOL_GPL(crypto_probing_notify); + struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask) { struct crypto_alg *alg; struct crypto_alg *larval; int ok; + if (!(mask & CRYPTO_ALG_TESTED)) { + type |= CRYPTO_ALG_TESTED; + mask |= CRYPTO_ALG_TESTED; + } + larval = crypto_larval_lookup(name, type, mask); if (IS_ERR(larval) || !crypto_is_larval(larval)) return larval; - ok = crypto_notify(CRYPTO_MSG_ALG_REQUEST, larval); - if (ok == NOTIFY_DONE) { - request_module("cryptomgr"); - ok = crypto_notify(CRYPTO_MSG_ALG_REQUEST, larval); - } + ok = crypto_probing_notify(CRYPTO_MSG_ALG_REQUEST, larval); if (ok == NOTIFY_STOP) alg = crypto_larval_wait(larval); diff --git a/crypto/internal.h b/crypto/internal.h index 48cb70416d59..fc93743c5d3e 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -94,9 +94,11 @@ void crypto_exit_digest_ops(struct crypto_tfm *tfm); void crypto_exit_cipher_ops(struct crypto_tfm *tfm); void crypto_exit_compress_ops(struct crypto_tfm *tfm); +struct crypto_larval *crypto_larval_alloc(const char *name, u32 type, u32 mask); void crypto_larval_kill(struct crypto_alg *alg); struct crypto_alg *crypto_larval_lookup(const char *name, u32 type, u32 mask); void crypto_larval_error(const char *name, u32 type, u32 mask); +void crypto_alg_tested(const char *name, int err); void crypto_shoot_alg(struct crypto_alg *alg); struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, @@ -107,6 +109,7 @@ int crypto_register_instance(struct crypto_template *tmpl, int crypto_register_notifier(struct notifier_block *nb); int crypto_unregister_notifier(struct notifier_block *nb); +int crypto_probing_notify(unsigned long val, void *v); int __init testmgr_init(void); void testmgr_exit(void); @@ -142,9 +145,9 @@ static inline int crypto_is_moribund(struct crypto_alg *alg) return alg->cra_flags & (CRYPTO_ALG_DEAD | CRYPTO_ALG_DYING); } -static inline int crypto_notify(unsigned long val, void *v) +static inline void crypto_notify(unsigned long val, void *v) { - return blocking_notifier_call_chain(&crypto_chain, val, v); + blocking_notifier_call_chain(&crypto_chain, val, v); } #endif /* _CRYPTO_INTERNAL_H */ diff --git a/crypto/proc.c b/crypto/proc.c index c6ede1e9c875..1d616adead0d 100644 --- a/crypto/proc.c +++ b/crypto/proc.c @@ -46,6 +46,9 @@ static int c_show(struct seq_file *m, void *p) seq_printf(m, "module : %s\n", module_name(alg->cra_module)); seq_printf(m, "priority : %d\n", alg->cra_priority); seq_printf(m, "refcnt : %d\n", atomic_read(&alg->cra_refcnt)); + seq_printf(m, "selftest : %s\n", + (alg->cra_flags & CRYPTO_ALG_TESTED) ? + "passed" : "unknown"); switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) { case CRYPTO_ALG_TYPE_CIPHER: diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 7ea0a4bc4ced..81d994a3bdaf 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -60,6 +60,14 @@ */ #define CRYPTO_ALG_GENIV 0x00000200 +/* + * Set if the algorithm has passed automated run-time testing. Note that + * if there is no run-time testing for a given algorithm it is considered + * to have passed. + */ + +#define CRYPTO_ALG_TESTED 0x00000400 + /* * Transform masks and values (for crt_flags). */ -- cgit v1.2.3 From 17f0f4a47df9aea9ee26c939f8057c35e0be1847 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 14 Aug 2008 22:15:52 +1000 Subject: crypto: rng - RNG interface and implementation This patch adds a random number generator interface as well as a cryptographic pseudo-random number generator based on AES. It is meant to be used in cases where a deterministic CPRNG is required. One of the first applications will be as an input in the IPsec IV generation process. Signed-off-by: Neil Horman Signed-off-by: Herbert Xu --- crypto/Kconfig | 16 ++ crypto/Makefile | 4 +- crypto/ansi_cprng.c | 417 ++++++++++++++++++++++++++++++++++++++++++ crypto/krng.c | 66 +++++++ crypto/rng.c | 126 +++++++++++++ include/crypto/internal/rng.h | 26 +++ include/crypto/rng.h | 75 ++++++++ include/linux/crypto.h | 25 +++ 8 files changed, 754 insertions(+), 1 deletion(-) create mode 100644 crypto/ansi_cprng.c create mode 100644 crypto/krng.c create mode 100644 crypto/rng.c create mode 100644 include/crypto/internal/rng.h create mode 100644 include/crypto/rng.h (limited to 'include/linux') diff --git a/crypto/Kconfig b/crypto/Kconfig index a784c2dce57e..2274293e71e1 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -46,6 +46,10 @@ config CRYPTO_HASH tristate select CRYPTO_ALGAPI +config CRYPTO_RNG + tristate + select CRYPTO_ALGAPI + config CRYPTO_MANAGER tristate "Cryptographic algorithm manager" select CRYPTO_AEAD @@ -689,6 +693,18 @@ config CRYPTO_LZO help This is the LZO algorithm. +comment "Random Number Generation" + +config CRYPTO_ANSI_CPRNG + tristate "Pseudo Random Number Generation for Cryptographic modules" + select CRYPTO_AES + select CRYPTO_RNG + select CRYPTO_FIPS + help + This option enables the generic pseudo random number generator + for cryptographic modules. Uses the Algorithm specified in + ANSI X9.31 A.2.4 + source "drivers/crypto/Kconfig" endif # if CRYPTO diff --git a/crypto/Makefile b/crypto/Makefile index 8a27b834ea76..5862b807334e 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -73,7 +73,9 @@ obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o - +obj-$(CONFIG_CRYPTO_RNG) += rng.o +obj-$(CONFIG_CRYPTO_RNG) += krng.o +obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o # diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c new file mode 100644 index 000000000000..72db0fd763cc --- /dev/null +++ b/crypto/ansi_cprng.c @@ -0,0 +1,417 @@ +/* + * PRNG: Pseudo Random Number Generator + * Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using + * AES 128 cipher + * + * (C) Neil Horman + * + * 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 + * any later version. + * + * + */ + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define DEFAULT_PRNG_KEY "0123456789abcdef" +#define DEFAULT_PRNG_KSZ 16 +#define DEFAULT_BLK_SZ 16 +#define DEFAULT_V_SEED "zaybxcwdveuftgsh" + +/* + * Flags for the prng_context flags field + */ + +#define PRNG_FIXED_SIZE 0x1 +#define PRNG_NEED_RESET 0x2 + +/* + * Note: DT is our counter value + * I is our intermediate value + * V is our seed vector + * See http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf + * for implementation details + */ + + +struct prng_context { + spinlock_t prng_lock; + unsigned char rand_data[DEFAULT_BLK_SZ]; + unsigned char last_rand_data[DEFAULT_BLK_SZ]; + unsigned char DT[DEFAULT_BLK_SZ]; + unsigned char I[DEFAULT_BLK_SZ]; + unsigned char V[DEFAULT_BLK_SZ]; + u32 rand_data_valid; + struct crypto_cipher *tfm; + u32 flags; +}; + +static int dbg; + +static void hexdump(char *note, unsigned char *buf, unsigned int len) +{ + if (dbg) { + printk(KERN_CRIT "%s", note); + print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, + 16, 1, + buf, len, false); + } +} + +#define dbgprint(format, args...) do {\ +if (dbg)\ + printk(format, ##args);\ +} while (0) + +static void xor_vectors(unsigned char *in1, unsigned char *in2, + unsigned char *out, unsigned int size) +{ + int i; + + for (i = 0; i < size; i++) + out[i] = in1[i] ^ in2[i]; + +} +/* + * Returns DEFAULT_BLK_SZ bytes of random data per call + * returns 0 if generation succeded, <0 if something went wrong + */ +static int _get_more_prng_bytes(struct prng_context *ctx) +{ + int i; + unsigned char tmp[DEFAULT_BLK_SZ]; + unsigned char *output = NULL; + + + dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p\n", + ctx); + + hexdump("Input DT: ", ctx->DT, DEFAULT_BLK_SZ); + hexdump("Input I: ", ctx->I, DEFAULT_BLK_SZ); + hexdump("Input V: ", ctx->V, DEFAULT_BLK_SZ); + + /* + * This algorithm is a 3 stage state machine + */ + for (i = 0; i < 3; i++) { + + switch (i) { + case 0: + /* + * Start by encrypting the counter value + * This gives us an intermediate value I + */ + memcpy(tmp, ctx->DT, DEFAULT_BLK_SZ); + output = ctx->I; + hexdump("tmp stage 0: ", tmp, DEFAULT_BLK_SZ); + break; + case 1: + + /* + * Next xor I with our secret vector V + * encrypt that result to obtain our + * pseudo random data which we output + */ + xor_vectors(ctx->I, ctx->V, tmp, DEFAULT_BLK_SZ); + hexdump("tmp stage 1: ", tmp, DEFAULT_BLK_SZ); + output = ctx->rand_data; + break; + case 2: + /* + * First check that we didn't produce the same + * random data that we did last time around through this + */ + if (!memcmp(ctx->rand_data, ctx->last_rand_data, + DEFAULT_BLK_SZ)) { + printk(KERN_ERR + "ctx %p Failed repetition check!\n", + ctx); + ctx->flags |= PRNG_NEED_RESET; + return -EINVAL; + } + memcpy(ctx->last_rand_data, ctx->rand_data, + DEFAULT_BLK_SZ); + + /* + * Lastly xor the random data with I + * and encrypt that to obtain a new secret vector V + */ + xor_vectors(ctx->rand_data, ctx->I, tmp, + DEFAULT_BLK_SZ); + output = ctx->V; + hexdump("tmp stage 2: ", tmp, DEFAULT_BLK_SZ); + break; + } + + + /* do the encryption */ + crypto_cipher_encrypt_one(ctx->tfm, output, tmp); + + } + + /* + * Now update our DT value + */ + for (i = 0; i < DEFAULT_BLK_SZ; i++) { + ctx->DT[i] += 1; + if (ctx->DT[i] != 0) + break; + } + + dbgprint("Returning new block for context %p\n", ctx); + ctx->rand_data_valid = 0; + + hexdump("Output DT: ", ctx->DT, DEFAULT_BLK_SZ); + hexdump("Output I: ", ctx->I, DEFAULT_BLK_SZ); + hexdump("Output V: ", ctx->V, DEFAULT_BLK_SZ); + hexdump("New Random Data: ", ctx->rand_data, DEFAULT_BLK_SZ); + + return 0; +} + +/* Our exported functions */ +static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx) +{ + unsigned long flags; + unsigned char *ptr = buf; + unsigned int byte_count = (unsigned int)nbytes; + int err; + + + if (nbytes < 0) + return -EINVAL; + + spin_lock_irqsave(&ctx->prng_lock, flags); + + err = -EINVAL; + if (ctx->flags & PRNG_NEED_RESET) + goto done; + + /* + * If the FIXED_SIZE flag is on, only return whole blocks of + * pseudo random data + */ + err = -EINVAL; + if (ctx->flags & PRNG_FIXED_SIZE) { + if (nbytes < DEFAULT_BLK_SZ) + goto done; + byte_count = DEFAULT_BLK_SZ; + } + + err = byte_count; + + dbgprint(KERN_CRIT "getting %d random bytes for context %p\n", + byte_count, ctx); + + +remainder: + if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { + if (_get_more_prng_bytes(ctx) < 0) { + memset(buf, 0, nbytes); + err = -EINVAL; + goto done; + } + } + + /* + * Copy up to the next whole block size + */ + if (byte_count < DEFAULT_BLK_SZ) { + for (; ctx->rand_data_valid < DEFAULT_BLK_SZ; + ctx->rand_data_valid++) { + *ptr = ctx->rand_data[ctx->rand_data_valid]; + ptr++; + byte_count--; + if (byte_count == 0) + goto done; + } + } + + /* + * Now copy whole blocks + */ + for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) { + if (_get_more_prng_bytes(ctx) < 0) { + memset(buf, 0, nbytes); + err = -EINVAL; + goto done; + } + memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ); + ctx->rand_data_valid += DEFAULT_BLK_SZ; + ptr += DEFAULT_BLK_SZ; + } + + /* + * Now copy any extra partial data + */ + if (byte_count) + goto remainder; + +done: + spin_unlock_irqrestore(&ctx->prng_lock, flags); + dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p\n", + err, ctx); + return err; +} + +static void free_prng_context(struct prng_context *ctx) +{ + crypto_free_cipher(ctx->tfm); +} + +static int reset_prng_context(struct prng_context *ctx, + unsigned char *key, size_t klen, + unsigned char *V, unsigned char *DT) +{ + int ret; + int rc = -EINVAL; + unsigned char *prng_key; + + spin_lock(&ctx->prng_lock); + ctx->flags |= PRNG_NEED_RESET; + + prng_key = (key != NULL) ? key : (unsigned char *)DEFAULT_PRNG_KEY; + + if (!key) + klen = DEFAULT_PRNG_KSZ; + + if (V) + memcpy(ctx->V, V, DEFAULT_BLK_SZ); + else + memcpy(ctx->V, DEFAULT_V_SEED, DEFAULT_BLK_SZ); + + if (DT) + memcpy(ctx->DT, DT, DEFAULT_BLK_SZ); + else + memset(ctx->DT, 0, DEFAULT_BLK_SZ); + + memset(ctx->rand_data, 0, DEFAULT_BLK_SZ); + memset(ctx->last_rand_data, 0, DEFAULT_BLK_SZ); + + if (ctx->tfm) + crypto_free_cipher(ctx->tfm); + + ctx->tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(ctx->tfm)) { + dbgprint(KERN_CRIT "Failed to alloc tfm for context %p\n", + ctx); + ctx->tfm = NULL; + goto out; + } + + ctx->rand_data_valid = DEFAULT_BLK_SZ; + + ret = crypto_cipher_setkey(ctx->tfm, prng_key, klen); + if (ret) { + dbgprint(KERN_CRIT "PRNG: setkey() failed flags=%x\n", + crypto_cipher_get_flags(ctx->tfm)); + crypto_free_cipher(ctx->tfm); + goto out; + } + + rc = 0; + ctx->flags &= ~PRNG_NEED_RESET; +out: + spin_unlock(&ctx->prng_lock); + + return rc; + +} + +static int cprng_init(struct crypto_tfm *tfm) +{ + struct prng_context *ctx = crypto_tfm_ctx(tfm); + + spin_lock_init(&ctx->prng_lock); + + return reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL); +} + +static void cprng_exit(struct crypto_tfm *tfm) +{ + free_prng_context(crypto_tfm_ctx(tfm)); +} + +static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata, + unsigned int dlen) +{ + struct prng_context *prng = crypto_rng_ctx(tfm); + + return get_prng_bytes(rdata, dlen, prng); +} + +static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) +{ + struct prng_context *prng = crypto_rng_ctx(tfm); + u8 *key = seed + DEFAULT_PRNG_KSZ; + + if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ) + return -EINVAL; + + reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, NULL); + + if (prng->flags & PRNG_NEED_RESET) + return -EINVAL; + return 0; +} + +static struct crypto_alg rng_alg = { + .cra_name = "stdrng", + .cra_driver_name = "ansi_cprng", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_RNG, + .cra_ctxsize = sizeof(struct prng_context), + .cra_type = &crypto_rng_type, + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(rng_alg.cra_list), + .cra_init = cprng_init, + .cra_exit = cprng_exit, + .cra_u = { + .rng = { + .rng_make_random = cprng_get_random, + .rng_reset = cprng_reset, + .seedsize = DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ, + } + } +}; + + +/* Module initalization */ +static int __init prng_mod_init(void) +{ + int ret = 0; + + if (fips_enabled) + rng_alg.cra_priority += 200; + + ret = crypto_register_alg(&rng_alg); + + if (ret) + goto out; +out: + return 0; +} + +static void __exit prng_mod_fini(void) +{ + crypto_unregister_alg(&rng_alg); + return; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Software Pseudo Random Number Generator"); +MODULE_AUTHOR("Neil Horman "); +module_param(dbg, int, 0); +MODULE_PARM_DESC(dbg, "Boolean to enable debugging (0/1 == off/on)"); +module_init(prng_mod_init); +module_exit(prng_mod_fini); +MODULE_ALIAS("stdrng"); diff --git a/crypto/krng.c b/crypto/krng.c new file mode 100644 index 000000000000..4328bb3430ed --- /dev/null +++ b/crypto/krng.c @@ -0,0 +1,66 @@ +/* + * RNG implementation using standard kernel RNG. + * + * Copyright (c) 2008 Herbert Xu + * + * 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 + * any later version. + * + */ + +#include +#include +#include +#include +#include + +static int krng_get_random(struct crypto_rng *tfm, u8 *rdata, unsigned int dlen) +{ + get_random_bytes(rdata, dlen); + return 0; +} + +static int krng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) +{ + return 0; +} + +static struct crypto_alg krng_alg = { + .cra_name = "stdrng", + .cra_driver_name = "krng", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_TYPE_RNG, + .cra_ctxsize = 0, + .cra_type = &crypto_rng_type, + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(krng_alg.cra_list), + .cra_u = { + .rng = { + .rng_make_random = krng_get_random, + .rng_reset = krng_reset, + .seedsize = 0, + } + } +}; + + +/* Module initalization */ +static int __init krng_mod_init(void) +{ + return crypto_register_alg(&krng_alg); +} + +static void __exit krng_mod_fini(void) +{ + crypto_unregister_alg(&krng_alg); + return; +} + +module_init(krng_mod_init); +module_exit(krng_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Kernel Random Number Generator"); +MODULE_ALIAS("stdrng"); diff --git a/crypto/rng.c b/crypto/rng.c new file mode 100644 index 000000000000..6e94bc735578 --- /dev/null +++ b/crypto/rng.c @@ -0,0 +1,126 @@ +/* + * Cryptographic API. + * + * RNG operations. + * + * Copyright (c) 2008 Neil Horman + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(crypto_default_rng_lock); +struct crypto_rng *crypto_default_rng; +EXPORT_SYMBOL_GPL(crypto_default_rng); +static int crypto_default_rng_refcnt; + +static int rngapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) +{ + u8 *buf = NULL; + int err; + + if (!seed && slen) { + buf = kmalloc(slen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + get_random_bytes(buf, slen); + seed = buf; + } + + err = crypto_rng_alg(tfm)->rng_reset(tfm, seed, slen); + + kfree(buf); + return err; +} + +static int crypto_init_rng_ops(struct crypto_tfm *tfm, u32 type, u32 mask) +{ + struct rng_alg *alg = &tfm->__crt_alg->cra_rng; + struct rng_tfm *ops = &tfm->crt_rng; + + ops->rng_gen_random = alg->rng_make_random; + ops->rng_reset = rngapi_reset; + + return 0; +} + +static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg) + __attribute__ ((unused)); +static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg) +{ + seq_printf(m, "type : rng\n"); + seq_printf(m, "seedsize : %u\n", alg->cra_rng.seedsize); +} + +static unsigned int crypto_rng_ctxsize(struct crypto_alg *alg, u32 type, + u32 mask) +{ + return alg->cra_ctxsize; +} + +const struct crypto_type crypto_rng_type = { + .ctxsize = crypto_rng_ctxsize, + .init = crypto_init_rng_ops, +#ifdef CONFIG_PROC_FS + .show = crypto_rng_show, +#endif +}; +EXPORT_SYMBOL_GPL(crypto_rng_type); + +int crypto_get_default_rng(void) +{ + struct crypto_rng *rng; + int err; + + mutex_lock(&crypto_default_rng_lock); + if (!crypto_default_rng) { + rng = crypto_alloc_rng("stdrng", 0, 0); + err = PTR_ERR(rng); + if (IS_ERR(rng)) + goto unlock; + + err = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng)); + if (err) { + crypto_free_rng(rng); + goto unlock; + } + + crypto_default_rng = rng; + } + + crypto_default_rng_refcnt++; + err = 0; + +unlock: + mutex_unlock(&crypto_default_rng_lock); + + return err; +} +EXPORT_SYMBOL_GPL(crypto_get_default_rng); + +void crypto_put_default_rng(void) +{ + mutex_lock(&crypto_default_rng_lock); + if (!--crypto_default_rng_refcnt) { + crypto_free_rng(crypto_default_rng); + crypto_default_rng = NULL; + } + mutex_unlock(&crypto_default_rng_lock); +} +EXPORT_SYMBOL_GPL(crypto_put_default_rng); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Random Number Genertor"); diff --git a/include/crypto/internal/rng.h b/include/crypto/internal/rng.h new file mode 100644 index 000000000000..896973369573 --- /dev/null +++ b/include/crypto/internal/rng.h @@ -0,0 +1,26 @@ +/* + * RNG: Random Number Generator algorithms under the crypto API + * + * Copyright (c) 2008 Neil Horman + * + * 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. + * + */ + +#ifndef _CRYPTO_INTERNAL_RNG_H +#define _CRYPTO_INTERNAL_RNG_H + +#include +#include + +extern const struct crypto_type crypto_rng_type; + +static inline void *crypto_rng_ctx(struct crypto_rng *tfm) +{ + return crypto_tfm_ctx(&tfm->base); +} + +#endif diff --git a/include/crypto/rng.h b/include/crypto/rng.h new file mode 100644 index 000000000000..c93f9b917925 --- /dev/null +++ b/include/crypto/rng.h @@ -0,0 +1,75 @@ +/* + * RNG: Random Number Generator algorithms under the crypto API + * + * Copyright (c) 2008 Neil Horman + * + * 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. + * + */ + +#ifndef _CRYPTO_RNG_H +#define _CRYPTO_RNG_H + +#include + +extern struct crypto_rng *crypto_default_rng; + +int crypto_get_default_rng(void); +void crypto_put_default_rng(void); + +static inline struct crypto_rng *__crypto_rng_cast(struct crypto_tfm *tfm) +{ + return (struct crypto_rng *)tfm; +} + +static inline struct crypto_rng *crypto_alloc_rng(const char *alg_name, + u32 type, u32 mask) +{ + type &= ~CRYPTO_ALG_TYPE_MASK; + type |= CRYPTO_ALG_TYPE_RNG; + mask |= CRYPTO_ALG_TYPE_MASK; + + return __crypto_rng_cast(crypto_alloc_base(alg_name, type, mask)); +} + +static inline struct crypto_tfm *crypto_rng_tfm(struct crypto_rng *tfm) +{ + return &tfm->base; +} + +static inline struct rng_alg *crypto_rng_alg(struct crypto_rng *tfm) +{ + return &crypto_rng_tfm(tfm)->__crt_alg->cra_rng; +} + +static inline struct rng_tfm *crypto_rng_crt(struct crypto_rng *tfm) +{ + return &crypto_rng_tfm(tfm)->crt_rng; +} + +static inline void crypto_free_rng(struct crypto_rng *tfm) +{ + crypto_free_tfm(crypto_rng_tfm(tfm)); +} + +static inline int crypto_rng_get_bytes(struct crypto_rng *tfm, + u8 *rdata, unsigned int dlen) +{ + return crypto_rng_crt(tfm)->rng_gen_random(tfm, rdata, dlen); +} + +static inline int crypto_rng_reset(struct crypto_rng *tfm, + u8 *seed, unsigned int slen) +{ + return crypto_rng_crt(tfm)->rng_reset(tfm, seed, slen); +} + +static inline int crypto_rng_seedsize(struct crypto_rng *tfm) +{ + return crypto_rng_alg(tfm)->seedsize; +} + +#endif diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 81d994a3bdaf..3d2317e4af2e 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -38,6 +38,7 @@ #define CRYPTO_ALG_TYPE_DIGEST 0x00000008 #define CRYPTO_ALG_TYPE_HASH 0x00000009 #define CRYPTO_ALG_TYPE_AHASH 0x0000000a +#define CRYPTO_ALG_TYPE_RNG 0x0000000c #define CRYPTO_ALG_TYPE_HASH_MASK 0x0000000e #define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000c @@ -113,6 +114,7 @@ struct crypto_aead; struct crypto_blkcipher; struct crypto_hash; struct crypto_ahash; +struct crypto_rng; struct crypto_tfm; struct crypto_type; struct aead_givcrypt_request; @@ -298,6 +300,15 @@ struct compress_alg { unsigned int slen, u8 *dst, unsigned int *dlen); }; +struct rng_alg { + int (*rng_make_random)(struct crypto_rng *tfm, u8 *rdata, + unsigned int dlen); + int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen); + + unsigned int seedsize; +}; + + #define cra_ablkcipher cra_u.ablkcipher #define cra_aead cra_u.aead #define cra_blkcipher cra_u.blkcipher @@ -306,6 +317,7 @@ struct compress_alg { #define cra_hash cra_u.hash #define cra_ahash cra_u.ahash #define cra_compress cra_u.compress +#define cra_rng cra_u.rng struct crypto_alg { struct list_head cra_list; @@ -333,6 +345,7 @@ struct crypto_alg { struct hash_alg hash; struct ahash_alg ahash; struct compress_alg compress; + struct rng_alg rng; } cra_u; int (*cra_init)(struct crypto_tfm *tfm); @@ -438,6 +451,12 @@ struct compress_tfm { u8 *dst, unsigned int *dlen); }; +struct rng_tfm { + int (*rng_gen_random)(struct crypto_rng *tfm, u8 *rdata, + unsigned int dlen); + int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen); +}; + #define crt_ablkcipher crt_u.ablkcipher #define crt_aead crt_u.aead #define crt_blkcipher crt_u.blkcipher @@ -445,6 +464,7 @@ struct compress_tfm { #define crt_hash crt_u.hash #define crt_ahash crt_u.ahash #define crt_compress crt_u.compress +#define crt_rng crt_u.rng struct crypto_tfm { @@ -458,6 +478,7 @@ struct crypto_tfm { struct hash_tfm hash; struct ahash_tfm ahash; struct compress_tfm compress; + struct rng_tfm rng; } crt_u; struct crypto_alg *__crt_alg; @@ -489,6 +510,10 @@ struct crypto_hash { struct crypto_tfm base; }; +struct crypto_rng { + struct crypto_tfm base; +}; + enum { CRYPTOA_UNSPEC, CRYPTOA_ALG, -- cgit v1.2.3 From 9f1ba9062e032fb7b395cd27fc564754fe4e9867 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 7 Aug 2008 20:07:01 +0300 Subject: mac80211/cfg80211: Add BSS configuration options for AP mode This change adds a new cfg80211 command, NL80211_CMD_SET_BSS, to allow AP mode BSS parameters to be changed from user space (e.g., hostapd). The drivers using mac80211 are expected to be modified with separate changes to use the new BSS info parameter for short slot time in the bss_info_changed() handler. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 19 ++++++++++++++++++ include/net/cfg80211.h | 22 +++++++++++++++++++++ include/net/mac80211.h | 9 +++++++++ net/mac80211/cfg.c | 37 +++++++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 2be7c63bc0f2..447c02a5190e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -89,6 +89,8 @@ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC * or, if no MAC address given, all mesh paths, on the interface identified * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -127,6 +129,8 @@ enum nl80211_commands { NL80211_CMD_NEW_MPATH, NL80211_CMD_DEL_MPATH, + NL80211_CMD_SET_BSS, + /* add commands here */ /* used to define NL80211_CMD_MAX below */ @@ -134,6 +138,11 @@ enum nl80211_commands { NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 }; +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS /** * enum nl80211_attrs - nl80211 netlink attributes @@ -192,6 +201,12 @@ enum nl80211_commands { * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_mntr_flags. * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -235,6 +250,10 @@ enum nl80211_attrs { NL80211_ATTR_MPATH_NEXT_HOP, NL80211_ATTR_MPATH_INFO, + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e00750836ba5..7afef14d5c5b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -268,6 +268,23 @@ struct mpath_info { u8 flags; }; +/** + * struct bss_parameters - BSS parameters + * + * Used to change BSS parameters (mainly for AP mode). + * + * @use_cts_prot: Whether to use CTS protection + * (0 = no, 1 = yes, -1 = do not change) + * @use_short_preamble: Whether the use of short preambles is allowed + * (0 = no, 1 = yes, -1 = do not change) + * @use_short_slot_time: Whether the use of short slot time is allowed + * (0 = no, 1 = yes, -1 = do not change) + */ +struct bss_parameters { + int use_cts_prot; + int use_short_preamble; + int use_short_slot_time; +}; /* from net/wireless.h */ struct wiphy; @@ -318,6 +335,8 @@ struct wiphy; * @change_station: Modify a given station. * * @set_mesh_cfg: set mesh parameters (by now, just mesh id) + * + * @change_bss: Modify parameters for a given BSS. */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, @@ -370,6 +389,9 @@ struct cfg80211_ops { int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *dst, u8 *next_hop, struct mpath_info *pinfo); + + int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params); }; #endif /* __NET_CFG80211_H */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0fdc3dabc964..7c399a9c11da 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -158,12 +158,14 @@ struct ieee80211_low_level_stats { * also implies a change in the AID. * @BSS_CHANGED_ERP_CTS_PROT: CTS protection changed * @BSS_CHANGED_ERP_PREAMBLE: preamble changed + * @BSS_CHANGED_ERP_SLOT: slot timing changed * @BSS_CHANGED_HT: 802.11n parameters changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, BSS_CHANGED_ERP_CTS_PROT = 1<<1, BSS_CHANGED_ERP_PREAMBLE = 1<<2, + BSS_CHANGED_ERP_SLOT = 1<<3, BSS_CHANGED_HT = 1<<4, }; @@ -177,6 +179,7 @@ enum ieee80211_bss_change { * @aid: association ID number, valid only when @assoc is true * @use_cts_prot: use CTS protection * @use_short_preamble: use 802.11b short preamble + * @use_short_slot: use short slot time (only relevant for ERP) * @dtim_period: num of beacons before the next DTIM, for PSM * @timestamp: beacon timestamp * @beacon_int: beacon interval @@ -192,6 +195,7 @@ struct ieee80211_bss_conf { /* erp related data */ bool use_cts_prot; bool use_short_preamble; + bool use_short_slot; u8 dtim_period; u16 beacon_int; u16 assoc_capability; @@ -420,6 +424,11 @@ struct ieee80211_rx_status { * @IEEE80211_CONF_PS: Enable 802.11 power save mode */ enum ieee80211_conf_flags { + /* + * TODO: IEEE80211_CONF_SHORT_SLOT_TIME will be removed once drivers + * have been converted to use bss_info_changed() for slot time + * configuration + */ IEEE80211_CONF_SHORT_SLOT_TIME = (1<<0), IEEE80211_CONF_RADIOTAP = (1<<1), IEEE80211_CONF_SUPPORT_HT_MODE = (1<<2), diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6d2ad2bf3ab5..2b19532f4c8a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1010,6 +1010,42 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, } #endif +static int ieee80211_change_bss(struct wiphy *wiphy, + struct net_device *dev, + struct bss_parameters *params) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata; + u32 changed = 0; + + if (dev == local->mdev) + return -EOPNOTSUPP; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + + if (params->use_cts_prot >= 0) { + sdata->bss_conf.use_cts_prot = params->use_cts_prot; + changed |= BSS_CHANGED_ERP_CTS_PROT; + } + if (params->use_short_preamble >= 0) { + sdata->bss_conf.use_short_preamble = + params->use_short_preamble; + changed |= BSS_CHANGED_ERP_PREAMBLE; + } + if (params->use_short_slot_time >= 0) { + sdata->bss_conf.use_short_slot = + params->use_short_slot_time; + changed |= BSS_CHANGED_ERP_SLOT; + } + + ieee80211_bss_info_change_notify(sdata, changed); + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1033,4 +1069,5 @@ struct cfg80211_ops mac80211_config_ops = { .get_mpath = ieee80211_get_mpath, .dump_mpath = ieee80211_dump_mpath, #endif + .change_bss = ieee80211_change_bss, }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 59eb2cf42e5f..47542ee01c57 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -87,6 +87,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_MESH_ID_LEN }, [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, + + [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, + [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, + [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, }; /* message building helper */ @@ -1525,6 +1529,48 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + struct bss_parameters params; + + memset(¶ms, 0, sizeof(params)); + /* default to not changing parameters */ + params.use_cts_prot = -1; + params.use_short_preamble = -1; + params.use_short_slot_time = -1; + + if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) + params.use_cts_prot = + nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); + if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) + params.use_short_preamble = + nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); + if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) + params.use_short_slot_time = + nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + return err; + + if (!drv->ops->change_bss) { + err = -EOPNOTSUPP; + goto out; + } + + rtnl_lock(); + err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); + rtnl_unlock(); + + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -1656,6 +1702,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_BSS, + .doit = nl80211_set_bss, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; /* multicast groups */ -- cgit v1.2.3 From 18d7c65ba6628d2759bd650e7039d6307f2c99e9 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 14 Aug 2008 13:27:41 +0530 Subject: mac80211: Add an 802.11n definition This patch adds a HT Capability (DSSS/CCK Mode in 40MHz BSS) definition to ieee80211.h Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 7f4df7c7659d..be456450cd2e 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -714,6 +714,7 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_SGI_40 0x0040 #define IEEE80211_HT_CAP_DELAY_BA 0x0400 #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 /* 802.11n HT capability AMPDU settings */ #define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03 #define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C -- cgit v1.2.3 From 095f695cbb07281682462da0618fffabb499d0be Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 19 Aug 2008 12:50:31 -0500 Subject: ssb: Update for Rev. 5 SPROM Although a revision 5 SPROM has not been seen in the wild, the open-source portion of the MIPS driver 4.150.10.5 describes its layout, which is mostly inherited from revision 4. This patch implements the differences. Signed-off-by: Larry Finger Acked-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 50 +++++++++++++++++++++++++++++++------------- include/linux/ssb/ssb_regs.h | 15 +++++++++++++ 2 files changed, 50 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index f883dcfffe06..0de4b5e04505 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -399,14 +399,19 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) out->antenna_gain.ghz5.a3 = gain; } -static void sprom_extract_r4(struct ssb_sprom *out, const u16 *in) +static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) { int i; u16 v; + u16 il0mac_offset; + if (out->revision == 4) + il0mac_offset = SSB_SPROM4_IL0MAC; + else + il0mac_offset = SSB_SPROM5_IL0MAC; /* extract the equivalent of the r1 variables */ for (i = 0; i < 3; i++) { - v = in[SPOFF(SSB_SPROM4_IL0MAC) + i]; + v = in[SPOFF(il0mac_offset) + i]; *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); } for (i = 0; i < 3; i++) { @@ -420,9 +425,15 @@ static void sprom_extract_r4(struct ssb_sprom *out, const u16 *in) SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0); SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A, SSB_SPROM4_ETHPHY_ET1A_SHIFT); - SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0); - SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); - SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); + if (out->revision == 4) { + SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0); + SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); + SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); + } else { + SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0); + SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0); + SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0); + } SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A, SSB_SPROM4_ANTAVAIL_A_SHIFT); SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG, @@ -433,12 +444,21 @@ static void sprom_extract_r4(struct ssb_sprom *out, const u16 *in) SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0); SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A, SSB_SPROM4_ITSSI_A_SHIFT); - SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0); - SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1, - SSB_SPROM4_GPIOA_P1_SHIFT); - SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0); - SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3, - SSB_SPROM4_GPIOB_P3_SHIFT); + if (out->revision == 4) { + SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0); + SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1, + SSB_SPROM4_GPIOA_P1_SHIFT); + SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0); + SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3, + SSB_SPROM4_GPIOB_P3_SHIFT); + } else { + SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0); + SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1, + SSB_SPROM5_GPIOA_P1_SHIFT); + SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0); + SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3, + SSB_SPROM5_GPIOB_P3_SHIFT); + } /* Extract the antenna gain values. */ SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01, @@ -471,16 +491,16 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, } else if (bus->chip_id == 0x4321) { /* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */ out->revision = 4; - sprom_extract_r4(out, in); + sprom_extract_r45(out, in); } else { if (out->revision == 0) goto unsupported; if (out->revision >= 1 && out->revision <= 3) { sprom_extract_r123(out, in); } - if (out->revision == 4) - sprom_extract_r4(out, in); - if (out->revision >= 5) + if (out->revision == 4 || out->revision == 5) + sprom_extract_r45(out, in); + if (out->revision > 5) goto unsupported; } diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index ebad0bac9801..271bb4b6446e 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -316,6 +316,21 @@ #define SSB_SPROM4_PA1B1 0x1090 #define SSB_SPROM4_PA1B2 0x1092 +/* SPROM Revision 5 (inherits most data from rev 4) */ +#define SSB_SPROM5_BFLLO 0x104A /* Boardflags (low 16 bits) */ +#define SSB_SPROM5_BFLHI 0x104C /* Board Flags Hi */ +#define SSB_SPROM5_IL0MAC 0x1052 /* 6 byte MAC address for a/b/g/n */ +#define SSB_SPROM5_CCODE 0x1044 /* Country Code (2 bytes) */ +#define SSB_SPROM5_GPIOA 0x1076 /* Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM5_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM5_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM5_GPIOA_P1_SHIFT 8 +#define SSB_SPROM5_GPIOB 0x1078 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM5_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM5_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM5_GPIOB_P3_SHIFT 8 + + /* Values for SSB_SPROM1_BINF_CCODE */ enum { SSB_SPROM1CCODE_WORLD = 0, -- cgit v1.2.3 From 31ce12fb3ebf88b054deb99ad729e84888bf6125 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 20 Aug 2008 17:45:06 -0500 Subject: ssb: Clean up extraction of MAC addresses from SPROM Only rev 1 and 2 ssb SPROMs have fields named et0mac and et1mac; however, all of the extraction routines extract pseudo data for these fields from regions that are all 1's resulting in a hardware address of FF:FF:FF:FF:FF:FF. This patch forces such a fill at the beginning of the data extraction process, and only does the formal extraction if the SPROM rev is 1 or 2. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 34 ++++++++++++++-------------------- include/linux/ssb/ssb_regs.h | 4 ---- 2 files changed, 14 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 0de4b5e04505..d5cde051806b 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -327,11 +327,9 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) s8 gain; u16 loc[3]; - if (out->revision == 3) { /* rev 3 moved MAC */ + if (out->revision == 3) /* rev 3 moved MAC */ loc[0] = SSB_SPROM3_IL0MAC; - loc[1] = SSB_SPROM3_ET0MAC; - loc[2] = SSB_SPROM3_ET1MAC; - } else { + else { loc[0] = SSB_SPROM1_IL0MAC; loc[1] = SSB_SPROM1_ET0MAC; loc[2] = SSB_SPROM1_ET1MAC; @@ -340,13 +338,15 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) v = in[SPOFF(loc[0]) + i]; *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); } - for (i = 0; i < 3; i++) { - v = in[SPOFF(loc[1]) + i]; - *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); - } - for (i = 0; i < 3; i++) { - v = in[SPOFF(loc[2]) + i]; - *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); + if (out->revision < 3) { /* only rev 1-2 have et0, et1 */ + for (i = 0; i < 3; i++) { + v = in[SPOFF(loc[1]) + i]; + *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); + } + for (i = 0; i < 3; i++) { + v = in[SPOFF(loc[2]) + i]; + *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); + } } SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A, @@ -409,19 +409,11 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) il0mac_offset = SSB_SPROM4_IL0MAC; else il0mac_offset = SSB_SPROM5_IL0MAC; - /* extract the equivalent of the r1 variables */ + /* extract the MAC address */ for (i = 0; i < 3; i++) { v = in[SPOFF(il0mac_offset) + i]; *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); } - for (i = 0; i < 3; i++) { - v = in[SPOFF(SSB_SPROM4_ET0MAC) + i]; - *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); - } - for (i = 0; i < 3; i++) { - v = in[SPOFF(SSB_SPROM4_ET1MAC) + i]; - *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); - } SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0); SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A, SSB_SPROM4_ETHPHY_ET1A_SHIFT); @@ -482,6 +474,8 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, out->revision = in[size - 1] & 0x00FF; ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision); + memset(out->et0mac, 0xFF, 6); /* preset et0 and et1 mac */ + memset(out->et1mac, 0xFF, 6); if ((bus->chip_id & 0xFF00) == 0x4400) { /* Workaround: The BCM44XX chip has a stupid revision * number stored in the SPROM. diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 271bb4b6446e..99a0f991e850 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -245,8 +245,6 @@ /* SPROM Revision 3 (inherits most data from rev 2) */ #define SSB_SPROM3_IL0MAC 0x104A /* 6 bytes MAC address for 802.11b/g */ -#define SSB_SPROM3_ET0MAC 0x1050 /* 6 bytes MAC address for Ethernet ?? */ -#define SSB_SPROM3_ET1MAC 0x1050 /* 6 bytes MAC address for 802.11a ?? */ #define SSB_SPROM3_OFDMAPO 0x102C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_OFDMALPO 0x1030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_OFDMAHPO 0x1034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ @@ -267,8 +265,6 @@ /* SPROM Revision 4 */ #define SSB_SPROM4_IL0MAC 0x104C /* 6 byte MAC address for a/b/g/n */ -#define SSB_SPROM4_ET0MAC 0x1018 /* 6 bytes MAC address for Ethernet ?? */ -#define SSB_SPROM4_ET1MAC 0x1018 /* 6 bytes MAC address for 802.11a ?? */ #define SSB_SPROM4_ETHPHY 0x105A /* Ethernet PHY settings ?? */ #define SSB_SPROM4_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ #define SSB_SPROM4_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ -- cgit v1.2.3 From 36aedc903ea11a4188de0a118d26c9f20afdd272 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 25 Aug 2008 11:58:58 +0300 Subject: mac80211/cfg80211: HT capabilities for NEW_STA Allow userspace (e.g., hostapd) to set HT capabilities for associated STAs. This is based on a patch from Zhu Yi (only the NL80211_ATTR_HT_CAPABILITY for NEW_STA part is included here). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 12 ++++++++++++ include/net/cfg80211.h | 1 + net/mac80211/cfg.c | 5 +++++ net/wireless/nl80211.c | 10 ++++++++++ 4 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 447c02a5190e..0c1147de3ec7 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -207,6 +207,9 @@ enum nl80211_commands { * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled * (u8, 0 or 1) * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -254,16 +257,25 @@ enum nl80211_attrs { NL80211_ATTR_BSS_SHORT_PREAMBLE, NL80211_ATTR_BSS_SHORT_SLOT_TIME, + NL80211_ATTR_HT_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY + #define NL80211_MAX_SUPP_RATES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 /** * enum nl80211_iftype - (virtual) interface types diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7afef14d5c5b..0a72d1e3d3ab 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -152,6 +152,7 @@ struct station_parameters { u16 aid; u8 supported_rates_len; u8 plink_action; + struct ieee80211_ht_cap *ht_capa; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2b19532f4c8a..928813ce08e2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -674,6 +674,11 @@ static void sta_apply_parameters(struct ieee80211_local *local, sta->supp_rates[local->oper_channel->band] = rates; } + if (params->ht_capa) { + ieee80211_ht_cap_ie_to_ht_info(params->ht_capa, + &sta->ht_info); + } + if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { switch (params->plink_action) { case PLINK_ACTION_OPEN: diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 47542ee01c57..4d6c02afd6f5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -91,6 +91,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, + + [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, + .len = NL80211_HT_CAPABILITY_LEN }, }; /* message building helper */ @@ -1129,6 +1132,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) + params.ht_capa = + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], ¶ms.station_flags)) return -EINVAL; @@ -1192,6 +1199,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) + params.ht_capa = + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], ¶ms.station_flags)) -- cgit v1.2.3 From 7c19a3d280297d43ef5ff7c6b205dc208a16d3d1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 29 Aug 2008 14:37:23 -0700 Subject: net: Unbreak userspace usage of linux/mroute.h Nothing in linux/pim.h should be exported to userspace. This should fix the XORP build failure reported by Jose Calhariz, the debain package maintainer. Nothing originally in linux/mroute.h was exported to userspace ever, but some of this stuff started to be when it was moved into this new linux/pim.h, and that was wrong. If we didn't provide these definitions for 10 years we can reasonably expect that applications defined this stuff locally or used GLIBC headers providing the protocol definitions. And as such the only result of this can be conflict and userland build breakage. Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 - include/linux/mroute.h | 2 +- include/linux/mroute6.h | 1 + include/linux/pim.h | 18 ------------------ 4 files changed, 2 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 7d970678f940..59391250d51c 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -297,7 +297,6 @@ unifdef-y += parport.h unifdef-y += patchkey.h unifdef-y += pci.h unifdef-y += personality.h -unifdef-y += pim.h unifdef-y += pktcdvd.h unifdef-y += pmu.h unifdef-y += poll.h diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 07112ee9293a..8a455694d682 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -6,7 +6,6 @@ #ifdef __KERNEL__ #include #endif -#include /* * Based on the MROUTING 3.5 defines primarily to keep @@ -130,6 +129,7 @@ struct igmpmsg */ #ifdef __KERNEL__ +#include #include #ifdef CONFIG_IP_MROUTE diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 5cf50473a10f..6f4c180179e2 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -115,6 +115,7 @@ struct sioc_mif_req6 #ifdef __KERNEL__ +#include #include /* for struct sk_buff_head */ #ifdef CONFIG_IPV6_MROUTE diff --git a/include/linux/pim.h b/include/linux/pim.h index 236ffd317394..1ba0661561a4 100644 --- a/include/linux/pim.h +++ b/include/linux/pim.h @@ -3,22 +3,6 @@ #include -#ifndef __KERNEL__ -struct pim { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 pim_type:4, /* PIM message type */ - pim_ver:4; /* PIM version */ -#elif defined(__BIG_ENDIAN_BITFIELD) - __u8 pim_ver:4; /* PIM version */ - pim_type:4; /* PIM message type */ -#endif - __u8 pim_rsv; /* Reserved */ - __be16 pim_cksum; /* Checksum */ -}; - -#define PIM_MINLEN 8 -#endif - /* Message types - V1 */ #define PIM_V1_VERSION __constant_htonl(0x10000000) #define PIM_V1_REGISTER 1 @@ -27,7 +11,6 @@ struct pim { #define PIM_VERSION 2 #define PIM_REGISTER 1 -#if defined(__KERNEL__) #define PIM_NULL_REGISTER __constant_htonl(0x40000000) /* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */ @@ -42,4 +25,3 @@ struct pimreghdr struct sk_buff; extern int pim_rcv_v1(struct sk_buff *); #endif -#endif -- cgit v1.2.3 From bef69ea0dcce574a425feb0a5aa4c63dd108b9a6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 29 Aug 2008 20:18:31 -0700 Subject: Resource handling: add 'insert_resource_expand_to_fit()' function Not used anywhere yet, but this complements the existing plain 'insert_resource()' functionality with a version that can expand the resource we are adding in order to fix up any conflicts it has with existing resources. Signed-off-by: Linus Torvalds --- include/linux/ioport.h | 1 + kernel/resource.c | 88 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 64 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 22d2115458c6..8d3b7a9afd17 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -109,6 +109,7 @@ extern struct resource iomem_resource; extern int request_resource(struct resource *root, struct resource *new); extern int release_resource(struct resource *new); extern int insert_resource(struct resource *parent, struct resource *new); +extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new); extern int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, diff --git a/kernel/resource.c b/kernel/resource.c index f5b518eabefe..cf0a178c7513 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -362,35 +362,21 @@ int allocate_resource(struct resource *root, struct resource *new, EXPORT_SYMBOL(allocate_resource); -/** - * insert_resource - Inserts a resource in the resource tree - * @parent: parent of the new resource - * @new: new resource to insert - * - * Returns 0 on success, -EBUSY if the resource can't be inserted. - * - * This function is equivalent to request_resource when no conflict - * happens. If a conflict happens, and the conflicting resources - * entirely fit within the range of the new resource, then the new - * resource is inserted and the conflicting resources become children of - * the new resource. +/* + * Insert a resource into the resource tree. If successful, return NULL, + * otherwise return the conflicting resource (compare to __request_resource()) */ -int insert_resource(struct resource *parent, struct resource *new) +static struct resource * __insert_resource(struct resource *parent, struct resource *new) { - int result; struct resource *first, *next; - write_lock(&resource_lock); - for (;; parent = first) { - result = 0; first = __request_resource(parent, new); if (!first) - goto out; + return first; - result = -EBUSY; if (first == parent) - goto out; + return first; if ((first->start > new->start) || (first->end < new->end)) break; @@ -401,15 +387,13 @@ int insert_resource(struct resource *parent, struct resource *new) for (next = first; ; next = next->sibling) { /* Partial overlap? Bad, and unfixable */ if (next->start < new->start || next->end > new->end) - goto out; + return next; if (!next->sibling) break; if (next->sibling->start > new->end) break; } - result = 0; - new->parent = parent; new->sibling = next->sibling; new->child = first; @@ -426,10 +410,64 @@ int insert_resource(struct resource *parent, struct resource *new) next = next->sibling; next->sibling = new; } + return NULL; +} - out: +/** + * insert_resource - Inserts a resource in the resource tree + * @parent: parent of the new resource + * @new: new resource to insert + * + * Returns 0 on success, -EBUSY if the resource can't be inserted. + * + * This function is equivalent to request_resource when no conflict + * happens. If a conflict happens, and the conflicting resources + * entirely fit within the range of the new resource, then the new + * resource is inserted and the conflicting resources become children of + * the new resource. + */ +int insert_resource(struct resource *parent, struct resource *new) +{ + struct resource *conflict; + + write_lock(&resource_lock); + conflict = __insert_resource(parent, new); + write_unlock(&resource_lock); + return conflict ? -EBUSY : 0; +} + +/** + * insert_resource_expand_to_fit - Insert a resource into the resource tree + * @parent: parent of the new resource + * @new: new resource to insert + * + * Insert a resource into the resource tree, possibly expanding it in order + * to make it encompass any conflicting resources. + */ +void insert_resource_expand_to_fit(struct resource *root, struct resource *new) +{ + if (new->parent) + return; + + write_lock(&resource_lock); + for (;;) { + struct resource *conflict; + + conflict = __insert_resource(root, new); + if (!conflict) + break; + if (conflict == root) + break; + + /* Ok, expand resource to cover the conflict, then try again .. */ + if (conflict->start < new->start) + new->start = conflict->start; + if (conflict->end > new->end) + new->end = conflict->end; + + printk("Expanded resource %s due to conflict with %s\n", new->name, conflict->name); + } write_unlock(&resource_lock); - return result; } /** -- cgit v1.2.3 From 673d62cc5ea6fca046650f17f77985b112c62322 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Sun, 31 Aug 2008 23:39:21 +0200 Subject: debugobjects: fix lockdep warning Daniel J. Blueman reported: > ======================================================= > [ INFO: possible circular locking dependency detected ] > 2.6.27-rc4-224c #1 > ------------------------------------------------------- > hald/4680 is trying to acquire lock: > (&n->list_lock){++..}, at: [] add_partial+0x26/0x80 > > but task is already holding lock: > (&obj_hash[i].lock){++..}, at: [] > debug_object_free+0x5c/0x120 We fix it by moving the actual freeing to outside the lock (the lock now only protects the list). The pool lock is also promoted to irq-safe (suggested by Dan). It's necessary because free_pool is now called outside the irq disabled region. So we need to protect against an interrupt handler which calls debug_object_init(). [tglx@linutronix.de: added hlist_move_list helper to avoid looping through the list twice] Reported-by: Daniel J Blueman Signed-off-by: Vegard Nossum Signed-off-by: Thomas Gleixner --- include/linux/list.h | 13 +++++++++++++ lib/debugobjects.c | 31 +++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index db35ef02e745..969f6e92d089 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -619,6 +619,19 @@ static inline void hlist_add_after(struct hlist_node *n, next->next->pprev = &next->next; } +/* + * Move a list from one list head to another. Fixup the pprev + * reference of the first entry if it exists. + */ +static inline void hlist_move_list(struct hlist_head *old, + struct hlist_head *new) +{ + new->first = old->first; + if (new->first) + new->first->pprev = &new->first; + old->first = NULL; +} + #define hlist_entry(ptr, type, member) container_of(ptr,type,member) #define hlist_for_each(pos, head) \ diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 45a6bde762d1..e3ab374e1334 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -112,6 +112,7 @@ static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b) /* * Allocate a new object. If the pool is empty, switch off the debugger. + * Must be called with interrupts disabled. */ static struct debug_obj * alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr) @@ -148,17 +149,18 @@ alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr) static void free_object(struct debug_obj *obj) { unsigned long idx = (unsigned long)(obj - obj_static_pool); + unsigned long flags; if (obj_pool_free < ODEBUG_POOL_SIZE || idx < ODEBUG_POOL_SIZE) { - spin_lock(&pool_lock); + spin_lock_irqsave(&pool_lock, flags); hlist_add_head(&obj->node, &obj_pool); obj_pool_free++; obj_pool_used--; - spin_unlock(&pool_lock); + spin_unlock_irqrestore(&pool_lock, flags); } else { - spin_lock(&pool_lock); + spin_lock_irqsave(&pool_lock, flags); obj_pool_used--; - spin_unlock(&pool_lock); + spin_unlock_irqrestore(&pool_lock, flags); kmem_cache_free(obj_cache, obj); } } @@ -171,6 +173,7 @@ static void debug_objects_oom(void) { struct debug_bucket *db = obj_hash; struct hlist_node *node, *tmp; + HLIST_HEAD(freelist); struct debug_obj *obj; unsigned long flags; int i; @@ -179,11 +182,14 @@ static void debug_objects_oom(void) for (i = 0; i < ODEBUG_HASH_SIZE; i++, db++) { spin_lock_irqsave(&db->lock, flags); - hlist_for_each_entry_safe(obj, node, tmp, &db->list, node) { + hlist_move_list(&db->list, &freelist); + spin_unlock_irqrestore(&db->lock, flags); + + /* Now free them */ + hlist_for_each_entry_safe(obj, node, tmp, &freelist, node) { hlist_del(&obj->node); free_object(obj); } - spin_unlock_irqrestore(&db->lock, flags); } } @@ -498,8 +504,9 @@ void debug_object_free(void *addr, struct debug_obj_descr *descr) return; default: hlist_del(&obj->node); + spin_unlock_irqrestore(&db->lock, flags); free_object(obj); - break; + return; } out_unlock: spin_unlock_irqrestore(&db->lock, flags); @@ -510,6 +517,7 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) { unsigned long flags, oaddr, saddr, eaddr, paddr, chunks; struct hlist_node *node, *tmp; + HLIST_HEAD(freelist); struct debug_obj_descr *descr; enum debug_obj_state state; struct debug_bucket *db; @@ -545,11 +553,18 @@ repeat: goto repeat; default: hlist_del(&obj->node); - free_object(obj); + hlist_add_head(&obj->node, &freelist); break; } } spin_unlock_irqrestore(&db->lock, flags); + + /* Now free them */ + hlist_for_each_entry_safe(obj, node, tmp, &freelist, node) { + hlist_del(&obj->node); + free_object(obj); + } + if (cnt > debug_objects_maxchain) debug_objects_maxchain = cnt; } -- cgit v1.2.3 From 71fc9fcc70e6ad96215510c1dbcbade05cd95e41 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 2 Sep 2008 20:18:46 +0200 Subject: IDE: compile fix for sff_dma_ops The sff_dma_ops struct should be wrapped by BLK_DEV_IDEDMA_SFF instead of BLK_DEV_IDEDMA_PCI. Signed-off-by: Kevin Hilman Cc: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 87c12ed96954..fee4b157c533 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1111,7 +1111,6 @@ void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI int ide_pci_set_master(struct pci_dev *, const char *); unsigned long ide_pci_dma_base(ide_hwif_t *, const struct ide_port_info *); -extern const struct ide_dma_ops sff_dma_ops; int ide_pci_check_simplex(ide_hwif_t *, const struct ide_port_info *); int ide_hwif_setup_dma(ide_hwif_t *, const struct ide_port_info *); #else @@ -1275,6 +1274,7 @@ extern int __ide_dma_end(ide_drive_t *); int ide_dma_test_irq(ide_drive_t *); extern void ide_dma_lost_irq(ide_drive_t *); extern void ide_dma_timeout(ide_drive_t *); +extern const struct ide_dma_ops sff_dma_ops; #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ #else -- cgit v1.2.3 From 96f80219b738f84f90e449385bdede90f2910521 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 2 Sep 2008 20:18:47 +0200 Subject: ide: fix hwif_to_node() hwif_to_node() incorrectly assumes that hwif->dev always belongs to a PCI device. This results in ide-cs oopsing in init_irq() after commit c56c5648a3bd15ff14c50f284b261140cd5b5472 accidentally fixed device tree registration for ide-cs. Fix it by using dev_to_node(). Thanks to Martin Michlmayr and Larry Finger for help with debugging the issue. Reported-by: Martin Michlmayr Tested-by: Martin Michlmayr Cc: Larry Finger Cc: Dominik Brodowski Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index fee4b157c533..1524829f73f2 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1448,8 +1448,7 @@ static inline void ide_dump_identify(u8 *id) static inline int hwif_to_node(ide_hwif_t *hwif) { - struct pci_dev *dev = to_pci_dev(hwif->dev); - return hwif->dev ? pcibus_to_node(dev->bus) : -1; + return hwif->dev ? dev_to_node(hwif->dev) : -1; } static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive) -- cgit v1.2.3 From 4b8561521dbaa3d766b198496b220e984e3bf756 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Tue, 2 Sep 2008 14:35:53 -0700 Subject: mm: show quicklist usage in /proc/meminfo Quicklists can consume several GB of memory. We should provide a means of monitoring this. After this patch is applied, /proc/meminfo will output the following: % cat /proc/meminfo MemTotal: 7715392 kB MemFree: 5401600 kB Buffers: 80384 kB Cached: 300800 kB SwapCached: 0 kB Active: 235584 kB Inactive: 262656 kB SwapTotal: 2031488 kB SwapFree: 2031488 kB Dirty: 3520 kB Writeback: 0 kB AnonPages: 117696 kB Mapped: 38528 kB Slab: 1589952 kB SReclaimable: 23104 kB SUnreclaim: 1566848 kB PageTables: 14656 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 5889152 kB Committed_AS: 393152 kB VmallocTotal: 17592177655808 kB VmallocUsed: 29056 kB VmallocChunk: 17592177626432 kB Quicklists: 130944 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 262144 kB Signed-off-by: KOSAKI Motohiro Cc: Christoph Lameter Cc: Keiichiro Tokunaga Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/proc_misc.c | 7 +++++-- include/linux/quicklist.h | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index ded969862960..00f10a2dcf12 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -189,7 +190,8 @@ static int meminfo_read_proc(char *page, char **start, off_t off, "Committed_AS: %8lu kB\n" "VmallocTotal: %8lu kB\n" "VmallocUsed: %8lu kB\n" - "VmallocChunk: %8lu kB\n", + "VmallocChunk: %8lu kB\n" + "Quicklists: %8lu kB\n", K(i.totalram), K(i.freeram), K(i.bufferram), @@ -221,7 +223,8 @@ static int meminfo_read_proc(char *page, char **start, off_t off, K(committed), (unsigned long)VMALLOC_TOTAL >> 10, vmi.used >> 10, - vmi.largest_chunk >> 10 + vmi.largest_chunk >> 10, + K(quicklist_total_size()) ); len += hugetlb_report_meminfo(page + len); diff --git a/include/linux/quicklist.h b/include/linux/quicklist.h index 39b66713a0bb..bd466439c588 100644 --- a/include/linux/quicklist.h +++ b/include/linux/quicklist.h @@ -80,6 +80,13 @@ void quicklist_trim(int nr, void (*dtor)(void *), unsigned long quicklist_total_size(void); +#else + +static inline unsigned long quicklist_total_size(void) +{ + return 0; +} + #endif #endif /* LINUX_QUICKLIST_H */ -- cgit v1.2.3 From f75c4950bb6e677e89479192b2ecfbec0beab14e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Sep 2008 16:47:35 -0300 Subject: V4L/DVB (8675): gspca: Pixmap PJPG (Pixart 73xx JPEG) added, generated by pac7311. The JPEG frames generated by the Pixart 73xx have: - special markers 'ff ff ff xx' every 1024/512 bytes, - unused 8 bits at end of JPEG blocks, and then ask for a new pixel format. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 200 ++---------------------------------- include/linux/videodev2.h | 1 + 2 files changed, 10 insertions(+), 191 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 85d9ddbc5b7f..3c9f2b14ca8c 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -22,6 +22,7 @@ #define MODULE_NAME "pac7311" #include "gspca.h" +#include "jpeg.h" MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); @@ -40,7 +41,6 @@ struct sd { unsigned char colors; unsigned char autogain; - char ffnb; /* number of 'ff' in the previous frame */ char tosof; /* number of bytes before next start of frame */ signed char ag_cnt; #define AG_CNT_START 13 @@ -121,95 +121,23 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header) /* (594) */ - -static const __u8 pac7311_jpeg_header[] = { - 0xff, 0xd8, - 0xff, 0xe0, 0x00, 0x03, 0x20, - 0xff, 0xc0, 0x00, 0x11, 0x08, - 0x01, 0xe0, /* 12: height */ - 0x02, 0x80, /* 14: width */ - 0x03, /* 16 */ - 0x01, 0x21, 0x00, - 0x02, 0x11, 0x01, - 0x03, 0x11, 0x01, - 0xff, 0xdb, 0x00, 0x84, - 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, - 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, - 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, - 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, - 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, - 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, - 0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, - 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, - 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, - 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, - 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, - 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, - 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, - 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, - 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, - 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, - 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, - 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, - 0x11, 0x00, 0x3f, 0x00 -}; - /* pac 7302 */ static const __u8 probe_7302[] = { /* index,value */ @@ -566,7 +494,6 @@ static void sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sd->ffnb = 0; sd->tosof = 0; if (sd->sensor == SENSOR_PAC7302) @@ -699,34 +626,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) } } -/* output the jpeg header */ -static void put_jpeg_head(struct gspca_dev *gspca_dev, - struct gspca_frame *frame) -{ - struct sd *sd = (struct sd *) gspca_dev; - unsigned char tmpbuf[4]; - - if (sd->ag_cnt >= 0) { - if (--sd->ag_cnt < 0) { - sd->ag_cnt = AG_CNT_START; - atomic_set(&sd->avg_lum, sd->lum_sum / AG_CNT_START); - atomic_set(&sd->do_gain, 1); - } - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - (__u8 *) pac7311_jpeg_header, - 12); - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - tmpbuf, 4); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - (__u8 *) &pac7311_jpeg_header[16], - PAC7311_JPEG_HEADER_SIZE - 16); -} - /* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ @@ -739,24 +638,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, #define INTER_FRAME 0x53 /* eof + inter frame + sof */ #define LUM_OFFSET 0x1e /* reverse offset / start of frame */ -/*fixme:test+*/ -/* dump the packet */ - if (gspca_debug & 0x200) { - static char tmp[50]; - - PDEBUG(0x200, "pkt_scan"); - tmp[0] = 0; - for (i = 0; i < len; i++) { - if (i % 16 == 0 && i != 0) { - PDEBUG(0x200, "%s", tmp); - tmp[0] = 0; - } - sprintf(&tmp[(i % 16) * 3], "%02x ", data[i]); - } - if (tmp[0] != 0) - PDEBUG(0x200, "%s", tmp); - } -/*fixme:test-*/ /* * inside a frame, there may be: * escaped ff ('ff 00') @@ -767,47 +648,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * 0x33 bytes * one byte luminosity * 0x16 bytes - * ff ff 00 ff 96 62 44 start of frame header + * ff ff 00 ff 96 62 44 start of frame */ - if (sd->tosof == 0) { /* if inside a frame */ - - /* check for 'ff ff ff xx' at start and at end of packet */ - /* (len is always >= 3 and xx never ff) */ - switch (sd->ffnb) { - case 1: - if (data[0] != 0xff) { /* can be '00' only */ - __u8 ff; - - sd->ffnb = 0; - ff = 0xff; - gspca_frame_add(gspca_dev, INTER_PACKET, - frame, &ff, 1); - break; /* keep 'ff 00' */ - } - /* fall thru */ - case 2: - case 3: - data += 4 - sd->ffnb; - len -= 4 - sd->ffnb; - sd->ffnb = 0; - break; - } - if (data[len - 1] == 0xff) { - if (data[len - 2] == 0xff) { - if (data[len - 3] == 0xff) { - sd->ffnb = 3; - len -= 3; - } else { - sd->ffnb = 2; - len -= 2; - } - } else { - sd->ffnb = 1; - len--; - } - } - } else { /* outside a frame */ + if (sd->tosof != 0) { /* if outside a frame */ /* get the luminosity and go to the start of frame */ data += sd->tosof; @@ -815,14 +659,14 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sd->tosof > LUM_OFFSET) sd->lum_sum += data[-LUM_OFFSET]; sd->tosof = 0; - put_jpeg_head(gspca_dev, frame); + jpeg_put_header(gspca_dev, frame, 1, 0x21); } for (i = 0; i < len; i++) { if (data[i] != 0xff) continue; switch (data[i + 1]) { - case 0xd9: /* end of frame */ + case 0xd9: /* 'ff d9' end of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, i + 2); @@ -835,33 +679,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, sd->tosof = -len; break; } - put_jpeg_head(gspca_dev, frame); - break; - case 0xff: /* 'ff ff ff xx' */ -/*fixme:test+*/ -/* is there a start of frame ? */ - if (data[i + 2] == 0x00) { - static __u8 ffd9[2] = {0xff, 0xd9}; - - gspca_frame_add(gspca_dev, - INTER_PACKET, - frame, data, - i + 7 - INTER_FRAME); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, ffd9, 2); - data += i + 7; - len -= i + 7; - i = 0; - put_jpeg_head(gspca_dev, frame); - break; - } -/*fixme:test-*/ - gspca_frame_add(gspca_dev, INTER_PACKET, - frame, data, i); - data += i + 4; - len -= i + 4; - i = 0; + jpeg_put_header(gspca_dev, frame, 1, 0x21); break; } } diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e65a6bed4e3e..6c73516b74c4 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -334,6 +334,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From 89a44b8a690ff2d8639b72931d9c197a8db0c803 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 3 Sep 2008 16:48:16 -0300 Subject: V4L/DVB (8720): gspca: V4L2_CAP_SENSOR_UPSIDE_DOWN added as a cap for some webcams. This patch adds a V4L2_CAP_SENSOR_UPSIDE_DOWN flag to the capabilities flags, and sets this flag for the Philips SPC200NC cam (which has its sensor installed upside down). The same flag is also needed and added for the Philips SPC300NC. Together with a patch to libv4l which adds flipping the image in software this fixes the upside down display with the SPC200NC cam. Signed-of-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 ++ drivers/media/video/gspca/gspca.h | 4 ++++ drivers/media/video/gspca/zc3xx.c | 31 +++++++++++++++++++------------ include/linux/videodev2.h | 5 +++++ 4 files changed, 30 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 061e19129245..3461bc9e4739 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -851,6 +851,8 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (gspca_dev->flags & GSPCA_SENSOR_UPSIDE_DOWN_FLAG) + cap->capabilities |= V4L2_CAP_SENSOR_UPSIDE_DOWN; return 0; } diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 2596568e82fd..1920c99d6f4a 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -118,6 +118,9 @@ struct gspca_frame { struct v4l2_buffer v4l2_buf; }; +/* defines for the flags member */ +#define GSPCA_SENSOR_UPSIDE_DOWN_FLAG 0x01 + struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct file_operations fops; @@ -163,6 +166,7 @@ struct gspca_dev { char nurbs; /* number of allocated URBs */ char memory; /* memory type (V4L2_MEMORY_xxx) */ __u8 nbalt; /* number of USB alternate settings */ + __u8 flags; /* see GSPCA_XXX_FLAG defines */ }; int gspca_dev_probe(struct usb_interface *intf, diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 79436f27cd4e..f1d8c7a08ba2 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -70,6 +70,10 @@ struct sd { unsigned short chip_revision; }; +#define DRIVER_INFO(sensor, flags) .driver_info = ((sensor) << 8) | (flags) +#define DRIVER_INFO_GET_SENSOR(driver_info) ((driver_info) >> 8) +#define DRIVER_INFO_GET_FLAGS(driver_info) ((driver_info) & 0xff) + /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); @@ -7015,7 +7019,8 @@ static int sd_config(struct gspca_dev *gspca_dev, /* define some sensors from the vendor/product */ sd->sharpness = 2; - sd->sensor = id->driver_info; + sd->sensor = DRIVER_INFO_GET_SENSOR(id->driver_info); + gspca_dev->flags = DRIVER_INFO_GET_FLAGS(id->driver_info); sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) PDEBUG(D_PROBE, "probe sensor -> %02x", sensor); @@ -7505,19 +7510,19 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x041e)}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x041e, 0x4017)}, - {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x401c), DRIVER_INFO(SENSOR_PAS106, 0)}, {USB_DEVICE(0x041e, 0x401e)}, {USB_DEVICE(0x041e, 0x401f)}, #endif {USB_DEVICE(0x041e, 0x4029)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x4034), DRIVER_INFO(SENSOR_PAS106, 0)}, + {USB_DEVICE(0x041e, 0x4035), DRIVER_INFO(SENSOR_PAS106, 0)}, {USB_DEVICE(0x041e, 0x4036)}, {USB_DEVICE(0x041e, 0x403a)}, #endif - {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_TAS5130C_VF0250}, - {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_TAS5130C_VF0250}, + {USB_DEVICE(0x041e, 0x4051), DRIVER_INFO(SENSOR_TAS5130C_VF0250, 0)}, + {USB_DEVICE(0x041e, 0x4053), DRIVER_INFO(SENSOR_TAS5130C_VF0250, 0)}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0458, 0x7007)}, {USB_DEVICE(0x0458, 0x700c)}, @@ -7543,11 +7548,13 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08d9)}, {USB_DEVICE(0x046d, 0x08d8)}, {USB_DEVICE(0x046d, 0x08da)}, - {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, - {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x046d, 0x08dd), DRIVER_INFO(SENSOR_MC501CB, 0)}, + {USB_DEVICE(0x0471, 0x0325), DRIVER_INFO(SENSOR_PAS106, + GSPCA_SENSOR_UPSIDE_DOWN_FLAG)}, + {USB_DEVICE(0x0471, 0x0326), DRIVER_INFO(SENSOR_PAS106, + GSPCA_SENSOR_UPSIDE_DOWN_FLAG)}, + {USB_DEVICE(0x0471, 0x032d), DRIVER_INFO(SENSOR_PAS106, 0)}, + {USB_DEVICE(0x0471, 0x032e), DRIVER_INFO(SENSOR_PAS106, 0)}, {USB_DEVICE(0x055f, 0xc005)}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x055f, 0xd003)}, @@ -7559,7 +7566,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0ac8, 0x301b)}, {USB_DEVICE(0x0ac8, 0x303b)}, #endif - {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, + {USB_DEVICE(0x0ac8, 0x305b), DRIVER_INFO(SENSOR_TAS5130C_VF0250, 0)}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0ac8, 0x307b)}, {USB_DEVICE(0x10fd, 0x0128)}, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 6c73516b74c4..4c30655b293f 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -261,6 +261,11 @@ struct v4l2_capability { #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ +/* This flags gets set if the "sensor" is known to be upside down and this can + *not* be fixed using v4l2 flipx/y controls. Note that absence of this flag + is not a guarantee for the image not being upside down. */ +#define V4L2_CAP_SENSOR_UPSIDE_DOWN 0x10000000 + /* * V I D E O I M A G E F O R M A T */ -- cgit v1.2.3 From d6db35e89c420d867e9ffdf145ecf2cb1b91291b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 3 Sep 2008 17:12:13 -0300 Subject: V4L/DVB (8809): gspca: Revert commit 9a9335776548d01525141c6e8f0c12e86bbde982 the previous patch (sensor upside down). Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 -- drivers/media/video/gspca/gspca.h | 4 ---- drivers/media/video/gspca/zc3xx.c | 31 ++++++++++++------------------- include/linux/videodev2.h | 5 ----- 4 files changed, 12 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 0b292ca0c080..bffddf527759 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -851,8 +851,6 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - if (gspca_dev->flags & GSPCA_SENSOR_UPSIDE_DOWN_FLAG) - cap->capabilities |= V4L2_CAP_SENSOR_UPSIDE_DOWN; return 0; } diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 1920c99d6f4a..2596568e82fd 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -118,9 +118,6 @@ struct gspca_frame { struct v4l2_buffer v4l2_buf; }; -/* defines for the flags member */ -#define GSPCA_SENSOR_UPSIDE_DOWN_FLAG 0x01 - struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct file_operations fops; @@ -166,7 +163,6 @@ struct gspca_dev { char nurbs; /* number of allocated URBs */ char memory; /* memory type (V4L2_MEMORY_xxx) */ __u8 nbalt; /* number of USB alternate settings */ - __u8 flags; /* see GSPCA_XXX_FLAG defines */ }; int gspca_dev_probe(struct usb_interface *intf, diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index f1d8c7a08ba2..79436f27cd4e 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -70,10 +70,6 @@ struct sd { unsigned short chip_revision; }; -#define DRIVER_INFO(sensor, flags) .driver_info = ((sensor) << 8) | (flags) -#define DRIVER_INFO_GET_SENSOR(driver_info) ((driver_info) >> 8) -#define DRIVER_INFO_GET_FLAGS(driver_info) ((driver_info) & 0xff) - /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); @@ -7019,8 +7015,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* define some sensors from the vendor/product */ sd->sharpness = 2; - sd->sensor = DRIVER_INFO_GET_SENSOR(id->driver_info); - gspca_dev->flags = DRIVER_INFO_GET_FLAGS(id->driver_info); + sd->sensor = id->driver_info; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) PDEBUG(D_PROBE, "probe sensor -> %02x", sensor); @@ -7510,19 +7505,19 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x041e)}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x041e, 0x4017)}, - {USB_DEVICE(0x041e, 0x401c), DRIVER_INFO(SENSOR_PAS106, 0)}, + {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x041e, 0x401e)}, {USB_DEVICE(0x041e, 0x401f)}, #endif {USB_DEVICE(0x041e, 0x4029)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x041e, 0x4034), DRIVER_INFO(SENSOR_PAS106, 0)}, - {USB_DEVICE(0x041e, 0x4035), DRIVER_INFO(SENSOR_PAS106, 0)}, + {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x041e, 0x4036)}, {USB_DEVICE(0x041e, 0x403a)}, #endif - {USB_DEVICE(0x041e, 0x4051), DRIVER_INFO(SENSOR_TAS5130C_VF0250, 0)}, - {USB_DEVICE(0x041e, 0x4053), DRIVER_INFO(SENSOR_TAS5130C_VF0250, 0)}, + {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_TAS5130C_VF0250}, + {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_TAS5130C_VF0250}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0458, 0x7007)}, {USB_DEVICE(0x0458, 0x700c)}, @@ -7548,13 +7543,11 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08d9)}, {USB_DEVICE(0x046d, 0x08d8)}, {USB_DEVICE(0x046d, 0x08da)}, - {USB_DEVICE(0x046d, 0x08dd), DRIVER_INFO(SENSOR_MC501CB, 0)}, - {USB_DEVICE(0x0471, 0x0325), DRIVER_INFO(SENSOR_PAS106, - GSPCA_SENSOR_UPSIDE_DOWN_FLAG)}, - {USB_DEVICE(0x0471, 0x0326), DRIVER_INFO(SENSOR_PAS106, - GSPCA_SENSOR_UPSIDE_DOWN_FLAG)}, - {USB_DEVICE(0x0471, 0x032d), DRIVER_INFO(SENSOR_PAS106, 0)}, - {USB_DEVICE(0x0471, 0x032e), DRIVER_INFO(SENSOR_PAS106, 0)}, + {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, + {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x055f, 0xc005)}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x055f, 0xd003)}, @@ -7566,7 +7559,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0ac8, 0x301b)}, {USB_DEVICE(0x0ac8, 0x303b)}, #endif - {USB_DEVICE(0x0ac8, 0x305b), DRIVER_INFO(SENSOR_TAS5130C_VF0250, 0)}, + {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, #ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0ac8, 0x307b)}, {USB_DEVICE(0x10fd, 0x0128)}, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 4c30655b293f..6c73516b74c4 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -261,11 +261,6 @@ struct v4l2_capability { #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ -/* This flags gets set if the "sensor" is known to be upside down and this can - *not* be fixed using v4l2 flipx/y controls. Note that absence of this flag - is not a guarantee for the image not being upside down. */ -#define V4L2_CAP_SENSOR_UPSIDE_DOWN 0x10000000 - /* * V I D E O I M A G E F O R M A T */ -- cgit v1.2.3 From 20122542df420a31532c8e2b5d4107e9846bac6b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Sep 2008 17:12:20 -0300 Subject: V4L/DVB (8832): gspca: Bad pixelformat of vc0321 webcams. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/vc032x.c | 4 ++-- include/linux/videodev2.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index f8c3ff30e464..bd4c226c9a07 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -88,12 +88,12 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vc0321_mode[] = { - {320, 240, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 6c73516b74c4..303d93ffd6b2 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -335,6 +335,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ +#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From 94fe7424a4c21940b4569200faaf0a0a5efd2924 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Wed, 3 Sep 2008 15:12:34 -0700 Subject: rtc-m48t59: add support for M48T02 and M48T59 chips Add support for two compatible RTC: - M48T08 which does not have alarm part, - M48T08 which does not have alarm part and has only 2KB of NVRAM These types covers all Mostek's RTC used in Sun UltraSparc workstations. Tested on Sun Ultra60 with M48T59 RTC. Signed-off-by: Krzysztof Helt Signed-off-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- drivers/rtc/Kconfig | 7 +++++-- drivers/rtc/rtc-m48t59.c | 47 ++++++++++++++++++++++++++++++++++++++-------- include/linux/rtc/m48t59.h | 45 ++++++++++++++++++++++++-------------------- 3 files changed, 69 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index ecdff2002476..daf08fe980d0 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -406,10 +406,13 @@ config RTC_DRV_M48T86 will be called rtc-m48t86. config RTC_DRV_M48T59 - tristate "ST M48T59" + tristate "ST M48T59/M48T08/M48T02" help If you say Y here you will get support for the - ST M48T59 RTC chip. + ST M48T59 RTC chip and compatible ST M48T08 and M48T02. + + These chips are usually found in Sun SPARC and UltraSPARC + workstations. This driver can also be built as a module, if so, the module will be called "rtc-m48t59". diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index e085ab2c3dbe..50f9f10b32f0 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -24,8 +24,9 @@ #define NO_IRQ (-1) #endif -#define M48T59_READ(reg) pdata->read_byte(dev, reg) -#define M48T59_WRITE(val, reg) pdata->write_byte(dev, reg, val) +#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg)) +#define M48T59_WRITE(val, reg) \ + (pdata->write_byte(dev, pdata->offset + reg, val)) #define M48T59_SET_BITS(mask, reg) \ M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg)) @@ -309,6 +310,11 @@ static const struct rtc_class_ops m48t59_rtc_ops = { .proc = m48t59_rtc_proc, }; +static const struct rtc_class_ops m48t02_rtc_ops = { + .read_time = m48t59_rtc_read_time, + .set_time = m48t59_rtc_set_time, +}; + static ssize_t m48t59_nvram_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) @@ -320,7 +326,7 @@ static ssize_t m48t59_nvram_read(struct kobject *kobj, ssize_t cnt = 0; unsigned long flags; - for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + for (; size > 0 && pos < pdata->offset; cnt++, size--) { spin_lock_irqsave(&m48t59->lock, flags); *buf++ = M48T59_READ(cnt); spin_unlock_irqrestore(&m48t59->lock, flags); @@ -340,7 +346,7 @@ static ssize_t m48t59_nvram_write(struct kobject *kobj, ssize_t cnt = 0; unsigned long flags; - for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + for (; size > 0 && pos < pdata->offset; cnt++, size--) { spin_lock_irqsave(&m48t59->lock, flags); M48T59_WRITE(*buf++, cnt); spin_unlock_irqrestore(&m48t59->lock, flags); @@ -357,7 +363,6 @@ static struct bin_attribute m48t59_nvram_attr = { }, .read = m48t59_nvram_read, .write = m48t59_nvram_write, - .size = M48T59_NVRAM_SIZE, }; static int __devinit m48t59_rtc_probe(struct platform_device *pdev) @@ -366,6 +371,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) struct m48t59_private *m48t59 = NULL; struct resource *res; int ret = -ENOMEM; + char *name; + const struct rtc_class_ops *ops; /* This chip could be memory-mapped or I/O-mapped */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -390,6 +397,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) /* Ensure we only kmalloc platform data once */ pdev->dev.platform_data = pdata; } + if (!pdata->type) + pdata->type = M48T59RTC_TYPE_M48T59; /* Try to use the generic memory read/write ops */ if (!pdata->write_byte) @@ -419,14 +428,36 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (ret) goto out; } + switch (pdata->type) { + case M48T59RTC_TYPE_M48T59: + name = "m48t59"; + ops = &m48t59_rtc_ops; + pdata->offset = 0x1ff0; + break; + case M48T59RTC_TYPE_M48T02: + name = "m48t02"; + ops = &m48t02_rtc_ops; + pdata->offset = 0x7f0; + break; + case M48T59RTC_TYPE_M48T08: + name = "m48t08"; + ops = &m48t02_rtc_ops; + pdata->offset = 0x1ff0; + break; + default: + dev_err(&pdev->dev, "Unknown RTC type\n"); + ret = -ENODEV; + goto out; + } - m48t59->rtc = rtc_device_register("m48t59", &pdev->dev, - &m48t59_rtc_ops, THIS_MODULE); + m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE); if (IS_ERR(m48t59->rtc)) { ret = PTR_ERR(m48t59->rtc); goto out; } + m48t59_nvram_attr.size = pdata->offset; + ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); if (ret) goto out; @@ -489,5 +520,5 @@ module_init(m48t59_rtc_init); module_exit(m48t59_rtc_exit); MODULE_AUTHOR("Mark Zhan "); -MODULE_DESCRIPTION("M48T59 RTC driver"); +MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h index e8c7c21ceb1f..41798505d157 100644 --- a/include/linux/rtc/m48t59.h +++ b/include/linux/rtc/m48t59.h @@ -18,40 +18,45 @@ /* * M48T59 Register Offset */ -#define M48T59_YEAR 0x1fff -#define M48T59_MONTH 0x1ffe -#define M48T59_MDAY 0x1ffd /* Day of Month */ -#define M48T59_WDAY 0x1ffc /* Day of Week */ +#define M48T59_YEAR 0xf +#define M48T59_MONTH 0xe +#define M48T59_MDAY 0xd /* Day of Month */ +#define M48T59_WDAY 0xc /* Day of Week */ #define M48T59_WDAY_CB 0x20 /* Century Bit */ #define M48T59_WDAY_CEB 0x10 /* Century Enable Bit */ -#define M48T59_HOUR 0x1ffb -#define M48T59_MIN 0x1ffa -#define M48T59_SEC 0x1ff9 -#define M48T59_CNTL 0x1ff8 +#define M48T59_HOUR 0xb +#define M48T59_MIN 0xa +#define M48T59_SEC 0x9 +#define M48T59_CNTL 0x8 #define M48T59_CNTL_READ 0x40 #define M48T59_CNTL_WRITE 0x80 -#define M48T59_WATCHDOG 0x1ff7 -#define M48T59_INTR 0x1ff6 +#define M48T59_WATCHDOG 0x7 +#define M48T59_INTR 0x6 #define M48T59_INTR_AFE 0x80 /* Alarm Interrupt Enable */ #define M48T59_INTR_ABE 0x20 -#define M48T59_ALARM_DATE 0x1ff5 -#define M48T59_ALARM_HOUR 0x1ff4 -#define M48T59_ALARM_MIN 0x1ff3 -#define M48T59_ALARM_SEC 0x1ff2 -#define M48T59_UNUSED 0x1ff1 -#define M48T59_FLAGS 0x1ff0 +#define M48T59_ALARM_DATE 0x5 +#define M48T59_ALARM_HOUR 0x4 +#define M48T59_ALARM_MIN 0x3 +#define M48T59_ALARM_SEC 0x2 +#define M48T59_UNUSED 0x1 +#define M48T59_FLAGS 0x0 #define M48T59_FLAGS_WDT 0x80 /* watchdog timer expired */ #define M48T59_FLAGS_AF 0x40 /* alarm */ #define M48T59_FLAGS_BF 0x10 /* low battery */ -#define M48T59_NVRAM_SIZE 0x1ff0 +#define M48T59RTC_TYPE_M48T59 0 /* to keep compatibility */ +#define M48T59RTC_TYPE_M48T02 1 +#define M48T59RTC_TYPE_M48T08 2 struct m48t59_plat_data { - /* The method to access M48T59 registers, - * NOTE: The 'ofs' should be 0x00~0x1fff - */ + /* The method to access M48T59 registers */ void (*write_byte)(struct device *dev, u32 ofs, u8 val); unsigned char (*read_byte)(struct device *dev, u32 ofs); + + int type; /* RTC model */ + + /* offset to RTC registers, automatically set according to the type */ + unsigned int offset; }; #endif /* _LINUX_RTC_M48T59_H_ */ -- cgit v1.2.3 From 64151ad5b3a03e236390d6d5160805ee4f4e7c67 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Wed, 3 Sep 2008 15:41:57 -0700 Subject: rtc-m48t59: allow externally mapped ioaddr Add support for externally mapped ioaddr. This is required on sparc32 as the ioaddr must be mapped with of_ioremap(). Signed-off-by: Krzysztof Helt Signed-off-by: David S. Miller --- drivers/rtc/rtc-m48t59.c | 14 ++++++++++---- include/linux/rtc/m48t59.h | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 50f9f10b32f0..867c8a79a6a8 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -411,9 +411,14 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (!m48t59) return -ENOMEM; - m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1); - if (!m48t59->ioaddr) - goto out; + m48t59->ioaddr = pdata->ioaddr; + + if (!m48t59->ioaddr) { + /* ioaddr not mapped externally */ + m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1); + if (!m48t59->ioaddr) + goto out; + } /* Try to get irq number. We also can work in * the mode without IRQ. @@ -481,11 +486,12 @@ out: static int __devexit m48t59_rtc_remove(struct platform_device *pdev) { struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); if (!IS_ERR(m48t59->rtc)) rtc_device_unregister(m48t59->rtc); - if (m48t59->ioaddr) + if (m48t59->ioaddr && !pdata->ioaddr) iounmap(m48t59->ioaddr); if (m48t59->irq != NO_IRQ) free_irq(m48t59->irq, &pdev->dev); diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h index 41798505d157..6fc961459b4a 100644 --- a/include/linux/rtc/m48t59.h +++ b/include/linux/rtc/m48t59.h @@ -55,6 +55,8 @@ struct m48t59_plat_data { int type; /* RTC model */ + /* ioaddr mapped externally */ + void __iomem *ioaddr; /* offset to RTC registers, automatically set according to the type */ unsigned int offset; }; -- cgit v1.2.3 From b4eec206370b7154dc354dc30f0a3f02ea8468b2 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Implement lookup table for feature-negotiation information A lookup table for feature-negotiation information, extracted from RFC 4340/42, is provided by this patch. All currently known features can be found in this table, along with their feature location, their default value, and type. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- include/linux/dccp.h | 9 ++-- net/dccp/feat.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 6080449fbec9..3978aff197d9 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -176,19 +176,20 @@ enum { }; /* DCCP features (RFC 4340 section 6.4) */ -enum { +enum dccp_feature_numbers { DCCPF_RESERVED = 0, DCCPF_CCID = 1, - DCCPF_SHORT_SEQNOS = 2, /* XXX: not yet implemented */ + DCCPF_SHORT_SEQNOS = 2, DCCPF_SEQUENCE_WINDOW = 3, - DCCPF_ECN_INCAPABLE = 4, /* XXX: not yet implemented */ + DCCPF_ECN_INCAPABLE = 4, DCCPF_ACK_RATIO = 5, DCCPF_SEND_ACK_VECTOR = 6, DCCPF_SEND_NDP_COUNT = 7, DCCPF_MIN_CSUM_COVER = 8, - DCCPF_DATA_CHECKSUM = 9, /* XXX: not yet implemented */ + DCCPF_DATA_CHECKSUM = 9, /* 10-127 reserved */ DCCPF_MIN_CCID_SPECIFIC = 128, + DCCPF_SEND_LEV_RATE = 192, /* RFC 4342, sec. 8.4 */ DCCPF_MAX_CCID_SPECIFIC = 255, }; diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 94a81b8e5efb..d7468f70544d 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -23,6 +23,80 @@ #define DCCP_FEAT_SP_NOAGREE (-123) +static const struct { + u8 feat_num; /* DCCPF_xxx */ + enum dccp_feat_type rxtx; /* RX or TX */ + enum dccp_feat_type reconciliation; /* SP or NN */ + u8 default_value; /* as in 6.4 */ +/* + * Lookup table for location and type of features (from RFC 4340/4342) + * +--------------------------+----+-----+----+----+---------+-----------+ + * | Feature | Location | Reconc. | Initial | Section | + * | | RX | TX | SP | NN | Value | Reference | + * +--------------------------+----+-----+----+----+---------+-----------+ + * | DCCPF_CCID | | X | X | | 2 | 10 | + * | DCCPF_SHORT_SEQNOS | | X | X | | 0 | 7.6.1 | + * | DCCPF_SEQUENCE_WINDOW | | X | | X | 100 | 7.5.2 | + * | DCCPF_ECN_INCAPABLE | X | | X | | 0 | 12.1 | + * | DCCPF_ACK_RATIO | | X | | X | 2 | 11.3 | + * | DCCPF_SEND_ACK_VECTOR | X | | X | | 0 | 11.5 | + * | DCCPF_SEND_NDP_COUNT | | X | X | | 0 | 7.7.2 | + * | DCCPF_MIN_CSUM_COVER | X | | X | | 0 | 9.2.1 | + * | DCCPF_DATA_CHECKSUM | X | | X | | 0 | 9.3.1 | + * | DCCPF_SEND_LEV_RATE | X | | X | | 0 | 4342/8.4 | + * +--------------------------+----+-----+----+----+---------+-----------+ + */ +} dccp_feat_table[] = { + { DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2 }, + { DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0 }, + { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100 }, + { DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0 }, + { DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2 }, + { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0 }, + { DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0 }, + { DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0 }, + { DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0 }, + { DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0 }, +}; +#define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table) + +/** + * dccp_feat_index - Hash function to map feature number into array position + * Returns consecutive array index or -1 if the feature is not understood. + */ +static int dccp_feat_index(u8 feat_num) +{ + /* The first 9 entries are occupied by the types from RFC 4340, 6.4 */ + if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM) + return feat_num - 1; + + /* + * Other features: add cases for new feature types here after adding + * them to the above table. + */ + switch (feat_num) { + case DCCPF_SEND_LEV_RATE: + return DCCP_FEAT_SUPPORTED_MAX - 1; + } + return -1; +} + +static u8 dccp_feat_type(u8 feat_num) +{ + int idx = dccp_feat_index(feat_num); + + if (idx < 0) + return FEAT_UNKNOWN; + return dccp_feat_table[idx].reconciliation; +} + +static int dccp_feat_default_value(u8 feat_num) +{ + int idx = dccp_feat_index(feat_num); + + return idx < 0 ? : dccp_feat_table[idx].default_value; +} + /* copy constructor, fval must not already contain allocated memory */ static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) { @@ -37,6 +111,45 @@ static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) return 0; } +static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val) +{ + if (unlikely(val == NULL)) + return; + if (dccp_feat_type(feat_num) == FEAT_SP) + kfree(val->sp.vec); + memset(val, 0, sizeof(*val)); +} + +static struct dccp_feat_entry * + dccp_feat_clone_entry(struct dccp_feat_entry const *original) +{ + struct dccp_feat_entry *new; + u8 type = dccp_feat_type(original->feat_num); + + if (type == FEAT_UNKNOWN) + return NULL; + + new = kmemdup(original, sizeof(struct dccp_feat_entry), gfp_any()); + if (new == NULL) + return NULL; + + if (type == FEAT_SP && dccp_feat_clone_sp_val(&new->val, + original->val.sp.vec, + original->val.sp.len)) { + kfree(new); + return NULL; + } + return new; +} + +static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) +{ + if (entry != NULL) { + dccp_feat_val_destructor(entry->feat_num, &entry->val); + kfree(entry); + } +} + int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, u8 *val, u8 len, gfp_t gfp) { @@ -653,6 +766,8 @@ const char *dccp_feat_name(const u8 feat) if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC) return feature_names[DCCPF_RESERVED]; + if (feat == DCCPF_SEND_LEV_RATE) + return "Send Loss Event Rate"; if (feat >= DCCPF_MIN_CCID_SPECIFIC) return "CCID-specific"; -- cgit v1.2.3 From 828755cee087e4a34f45d6c9db661ccd0631cc6d Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Per-socket initialisation of feature negotiation This provides feature-negotiation initialisation for both DCCP sockets and DCCP request_sockets, to support feature negotiation during connection setup. It also resolves a FIXME regarding the congestion control initialisation. Thanks to Wei Yongjun for help with the IPv6 side of this patch. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- include/linux/dccp.h | 4 ++++ net/dccp/dccp.h | 3 ++- net/dccp/feat.c | 19 +++++++++++++++++++ net/dccp/feat.h | 1 + net/dccp/input.c | 2 -- net/dccp/ipv4.c | 3 ++- net/dccp/ipv6.c | 3 ++- net/dccp/minisocks.c | 7 ++++++- net/dccp/proto.c | 1 + 9 files changed, 37 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 3978aff197d9..484b8a1fb023 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -412,6 +412,7 @@ extern void dccp_minisock_init(struct dccp_minisock *dmsk); * @dreq_iss: initial sequence number sent on the Response (RFC 4340, 7.1) * @dreq_isr: initial sequence number received on the Request * @dreq_service: service code present on the Request (there is just one) + * @dreq_featneg: feature negotiation options for this connection * The following two fields are analogous to the ones in dccp_sock: * @dreq_timestamp_echo: last received timestamp to echo (13.1) * @dreq_timestamp_echo: the time of receiving the last @dreq_timestamp_echo @@ -421,6 +422,7 @@ struct dccp_request_sock { __u64 dreq_iss; __u64 dreq_isr; __be32 dreq_service; + struct list_head dreq_featneg; __u32 dreq_timestamp_echo; __u32 dreq_timestamp_time; }; @@ -498,6 +500,7 @@ struct dccp_ackvec; * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) * @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4) * @dccps_minisock - associated minisock (accessed via dccp_msk) + * @dccps_featneg - tracks feature-negotiation state (mostly during handshake) * @dccps_hc_rx_ackvec - rx half connection ack vector * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) * @dccps_hc_tx_ccid - CCID used for the sender (or sending half-connection) @@ -535,6 +538,7 @@ struct dccp_sock { __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; struct dccp_minisock dccps_minisock; + struct list_head dccps_featneg; struct dccp_ackvec *dccps_hc_rx_ackvec; struct ccid *dccps_hc_rx_ccid; struct ccid *dccps_hc_tx_ccid; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index b4bc6e095a0e..ab096c06bba0 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -252,7 +252,8 @@ extern const char *dccp_state_name(const int state); extern void dccp_set_state(struct sock *sk, const int state); extern void dccp_done(struct sock *sk); -extern void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb); +extern int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp, + struct sk_buff const *skb); extern int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb); diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 2ec2cd117699..faade82856fe 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -279,6 +279,25 @@ void dccp_feat_list_purge(struct list_head *fn_list) } EXPORT_SYMBOL_GPL(dccp_feat_list_purge); +/* generate @to as full clone of @from - @to must not contain any nodes */ +int dccp_feat_clone_list(struct list_head const *from, struct list_head *to) +{ + struct dccp_feat_entry *entry, *new; + + INIT_LIST_HEAD(to); + list_for_each_entry(entry, from, node) { + new = dccp_feat_clone_entry(entry); + if (new == NULL) + goto cloning_failed; + list_add_tail(&new->node, to); + } + return 0; + +cloning_failed: + dccp_feat_list_purge(to); + return -ENOMEM; +} + int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, u8 *val, u8 len, gfp_t gfp) { diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 94203c230c65..7e953fd0a79b 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -95,6 +95,7 @@ extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len); extern void dccp_feat_clean(struct dccp_minisock *dmsk); extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); +extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); extern int dccp_feat_init(struct dccp_minisock *dmsk); #endif /* _DCCP_FEAT_H */ diff --git a/net/dccp/input.c b/net/dccp/input.c index 779d0ed9ae94..3070015edc75 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -590,8 +590,6 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (inet_csk(sk)->icsk_af_ops->conn_request(sk, skb) < 0) return 1; - - /* FIXME: do congestion control initialization */ goto discard; } if (dh->dccph_type == DCCP_PKT_RESET) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 882c5c4de69e..0ce84ea89119 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -595,7 +595,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (req == NULL) goto drop; - dccp_reqsk_init(req, skb); + if (dccp_reqsk_init(req, dccp_sk(sk), skb)) + goto drop_and_free; dreq = dccp_rsk(req); if (dccp_parse_options(sk, dreq, skb)) diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 5e1ee0da2c40..33e8a1ea3041 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -424,7 +424,8 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (req == NULL) goto drop; - dccp_reqsk_init(req, skb); + if (dccp_reqsk_init(req, dccp_sk(sk), skb)) + goto drop_and_free; dreq = dccp_rsk(req); if (dccp_parse_options(sk, dreq, skb)) diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index b2804e2d1b8c..e487133ae079 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -125,6 +125,7 @@ struct sock *dccp_create_openreq_child(struct sock *sk, newdp->dccps_timestamp_time = dreq->dreq_timestamp_time; newicsk->icsk_rto = DCCP_TIMEOUT_INIT; + INIT_LIST_HEAD(&newdp->dccps_featneg); if (dccp_feat_clone(sk, newsk)) goto out_free; @@ -304,7 +305,8 @@ void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, EXPORT_SYMBOL_GPL(dccp_reqsk_send_ack); -void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb) +int dccp_reqsk_init(struct request_sock *req, + struct dccp_sock const *dp, struct sk_buff const *skb) { struct dccp_request_sock *dreq = dccp_rsk(req); @@ -312,6 +314,9 @@ void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb) inet_rsk(req)->acked = 0; req->rcv_wnd = sysctl_dccp_feat_sequence_window; dreq->dreq_timestamp_echo = 0; + + /* inherit feature negotiation options from listening socket */ + return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg); } EXPORT_SYMBOL_GPL(dccp_reqsk_init); diff --git a/net/dccp/proto.c b/net/dccp/proto.c index d0bd34819761..1cdf4ae99605 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -193,6 +193,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) dccp_init_xmit_timers(sk); + INIT_LIST_HEAD(&dp->dccps_featneg); /* * FIXME: We're hardcoding the CCID, and doing this at this point makes * the listening (master) sock get CCID control blocks, which is not -- cgit v1.2.3 From 71bb49596bbf4e5a3328e1704d18604e822ba181 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Query supported CCIDs This provides a data structure to record which CCIDs are locally supported and three accessor functions: - a test function for internal use which is used to validate CCID requests made by the user; - a copy function so that the list can be used for feature-negotiation; - documented getsockopt() support so that the user can query capabilities. The data structure is a table which is filled in at compile-time with the list of available CCIDs (which in turn depends on the Kconfig choices). Using the copy function for cloning the list of supported CCIDs is useful for feature negotiation, since the negotiation is now with the full list of available CCIDs (e.g. {2, 3}) instead of the default value {2}. This means negotiation will not fail if the peer requests to use CCID3 instead of CCID2. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- Documentation/networking/dccp.txt | 4 ++++ include/linux/dccp.h | 1 + net/dccp/ccid.c | 48 +++++++++++++++++++++++++++++++++++++++ net/dccp/ccid.h | 5 ++++ net/dccp/feat.c | 4 ++++ net/dccp/proto.c | 2 ++ 6 files changed, 64 insertions(+) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 39131a3c78f8..f0aeb20fa63b 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -57,6 +57,10 @@ can be set before calling bind(). DCCP_SOCKOPT_GET_CUR_MPS is read-only and retrieves the current maximum packet size (application payload size) in bytes, see RFC 4340, section 14. +DCCP_SOCKOPT_AVAILABLE_CCIDS is also read-only and returns the list of CCIDs +supported by the endpoint (see include/linux/dccp.h for symbolic constants). +The caller needs to provide a sufficiently large (> 2) array of type uint8_t. + DCCP_SOCKOPT_SERVER_TIMEWAIT enables the server (listening socket) to hold timewait state when closing the connection (RFC 4340, 8.3). The usual case is that the closing server sends a CloseReq, whereupon the client holds timewait diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 484b8a1fb023..d3ac1bde60b4 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -209,6 +209,7 @@ struct dccp_so_feat { #define DCCP_SOCKOPT_SERVER_TIMEWAIT 6 #define DCCP_SOCKOPT_SEND_CSCOV 10 #define DCCP_SOCKOPT_RECV_CSCOV 11 +#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 #define DCCP_SOCKOPT_CCID_RX_INFO 128 #define DCCP_SOCKOPT_CCID_TX_INFO 192 diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index 4809753d12ae..f72ca83df552 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c @@ -13,6 +13,13 @@ #include "ccid.h" +static u8 builtin_ccids[] = { + DCCPC_CCID2, /* CCID2 is supported by default */ +#if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) + DCCPC_CCID3, +#endif +}; + static struct ccid_operations *ccids[CCID_MAX]; #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) static atomic_t ccids_lockct = ATOMIC_INIT(0); @@ -86,6 +93,47 @@ static void ccid_kmem_cache_destroy(struct kmem_cache *slab) } } +/* check that up to @array_len members in @ccid_array are supported */ +bool ccid_support_check(u8 const *ccid_array, u8 array_len) +{ + u8 i, j, found; + + for (i = 0, found = 0; i < array_len; i++, found = 0) { + for (j = 0; !found && j < ARRAY_SIZE(builtin_ccids); j++) + found = (ccid_array[i] == builtin_ccids[j]); + if (!found) + return false; + } + return true; +} + +/** + * ccid_get_builtin_ccids - Provide copy of `builtin' CCID array + * @ccid_array: pointer to copy into + * @array_len: value to return length into + * This function allocates memory - caller must see that it is freed after use. + */ +int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) +{ + *ccid_array = kmemdup(builtin_ccids, sizeof(builtin_ccids), gfp_any()); + if (*ccid_array == NULL) + return -ENOBUFS; + *array_len = ARRAY_SIZE(builtin_ccids); + return 0; +} + +int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + if (len < sizeof(builtin_ccids)) + return -EINVAL; + + if (put_user(sizeof(builtin_ccids), optlen) || + copy_to_user(optval, builtin_ccids, sizeof(builtin_ccids))) + return -EFAULT; + return 0; +} + int ccid_register(struct ccid_operations *ccid_ops) { int err = -ENOBUFS; diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h index fdeae7b57319..259f5469d7d0 100644 --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -103,6 +103,11 @@ static inline void *ccid_priv(const struct ccid *ccid) return (void *)ccid->ccid_priv; } +extern bool ccid_support_check(u8 const *ccid_array, u8 array_len); +extern int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len); +extern int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, + char __user *, int __user *); + extern struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp); diff --git a/net/dccp/feat.c b/net/dccp/feat.c index b859722fba72..9399554878cc 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -383,6 +383,10 @@ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, !dccp_feat_sp_list_ok(feat, sp_val, sp_len)) return -EINVAL; + /* Avoid negotiating alien CCIDs by only advertising supported ones */ + if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len)) + return -EOPNOTSUPP; + if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len)) return -ENOMEM; diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 01332fe7a99a..b4b10cbd8880 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -649,6 +649,8 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, case DCCP_SOCKOPT_GET_CUR_MPS: val = dp->dccps_mss_cache; break; + case DCCP_SOCKOPT_AVAILABLE_CCIDS: + return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); case DCCP_SOCKOPT_SERVER_TIMEWAIT: val = dp->dccps_server_timewait; break; -- cgit v1.2.3 From 668144f7b41716a9efe1b398e15ead32a26cd101 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Deprecate old setsockopt framework The previous setsockopt interface, which passed socket options via struct dccp_so_feat, is complicated/difficult to use. Continuing to support it leads to ugly code since the old approach did not distinguish between NN and SP values. This patch removes the old setsockopt interface and replaces it with two new functions to register NN/SP values for feature negotiation. These are essentially wrappers around the internal __feat_register functions, with checking added to avoid * wrong usage (type); * changing values while the connection is in progress. Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 7 ----- net/dccp/feat.c | 72 ++++++++++++++++++++-------------------------------- net/dccp/feat.h | 5 ++-- net/dccp/proto.c | 53 ++------------------------------------ 4 files changed, 32 insertions(+), 105 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index d3ac1bde60b4..6eaaca9b037a 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -193,13 +193,6 @@ enum dccp_feature_numbers { DCCPF_MAX_CCID_SPECIFIC = 255, }; -/* this structure is argument to DCCP_SOCKOPT_CHANGE_X */ -struct dccp_so_feat { - __u8 dccpsf_feat; - __u8 __user *dccpsf_val; - __u8 dccpsf_len; -}; - /* DCCP socket options */ #define DCCP_SOCKOPT_PACKET_SIZE 1 /* XXX deprecated, without effect */ #define DCCP_SOCKOPT_SERVICE 2 diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 6852960bb0a9..44b10afd3fb6 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -393,53 +393,35 @@ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); } -int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, - u8 *val, u8 len, gfp_t gfp) -{ - struct dccp_opt_pend *opt; - - dccp_feat_debug(type, feature, *val); - - if (len > 3) { - DCCP_WARN("invalid length %d\n", len); +/** + * dccp_feat_register_sp - Register requests to change SP feature values + * @sk: client or listening socket + * @feat: one of %dccp_feature_numbers + * @is_local: whether the local (1) or remote (0) @feat is meant + * @list: array of preferred values, in descending order of preference + * @len: length of @list in bytes + */ +int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, + u8 const *list, u8 len) +{ /* any changes must be registered before establishing the connection */ + if (sk->sk_state != DCCP_CLOSED) + return -EISCONN; + if (dccp_feat_type(feat) != FEAT_SP) return -EINVAL; - } - /* XXX add further sanity checks */ - - /* check if that feature is already being negotiated */ - list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { - /* ok we found a negotiation for this option already */ - if (opt->dccpop_feat == feature && opt->dccpop_type == type) { - dccp_pr_debug("Replacing old\n"); - /* replace */ - BUG_ON(opt->dccpop_val == NULL); - kfree(opt->dccpop_val); - opt->dccpop_val = val; - opt->dccpop_len = len; - opt->dccpop_conf = 0; - return 0; - } - } - - /* negotiation for a new feature */ - opt = kmalloc(sizeof(*opt), gfp); - if (opt == NULL) - return -ENOMEM; - - opt->dccpop_type = type; - opt->dccpop_feat = feature; - opt->dccpop_len = len; - opt->dccpop_val = val; - opt->dccpop_conf = 0; - opt->dccpop_sc = NULL; - - BUG_ON(opt->dccpop_val == NULL); - - list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending); - return 0; + return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local, + 0, list, len); } -EXPORT_SYMBOL_GPL(dccp_feat_change); +/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */ +int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val) +{ + /* any changes must be registered before establishing the connection */ + if (sk->sk_state != DCCP_CLOSED) + return -EISCONN; + if (dccp_feat_type(feat) != FEAT_NN) + return -EINVAL; + return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val); +} /* * Tracking features whose value depend on the choice of CCID @@ -1137,7 +1119,7 @@ int dccp_feat_init(struct sock *sk) /* Ack ratio */ rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, - dmsk->dccpms_ack_ratio); + dp->dccps_l_ack_ratio); out: return rc; } diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 9eefdb43783e..2c92bd18e5f4 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -110,8 +110,9 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) #define dccp_feat_debug(type, feat, val) #endif /* CONFIG_IP_DCCP_DEBUG */ -extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, - u8 *val, u8 len, gfp_t gfp); +extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, + u8 const *list, u8 len); +extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len); extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 46cb3490d48e..108d56bd25c5 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -470,44 +470,6 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, return 0; } -/* byte 1 is feature. the rest is the preference list */ -static int dccp_setsockopt_change(struct sock *sk, int type, - struct dccp_so_feat __user *optval) -{ - struct dccp_so_feat opt; - u8 *val; - int rc; - - if (copy_from_user(&opt, optval, sizeof(opt))) - return -EFAULT; - /* - * rfc4340: 6.1. Change Options - */ - if (opt.dccpsf_len < 1) - return -EINVAL; - - val = kmalloc(opt.dccpsf_len, GFP_KERNEL); - if (!val) - return -ENOMEM; - - if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { - rc = -EFAULT; - goto out_free_val; - } - - rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat, - val, opt.dccpsf_len, GFP_KERNEL); - if (rc) - goto out_free_val; - -out: - return rc; - -out_free_val: - kfree(val); - goto out; -} - static int do_dccp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) { @@ -530,20 +492,9 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, err = 0; break; case DCCP_SOCKOPT_CHANGE_L: - if (optlen != sizeof(struct dccp_so_feat)) - err = -EINVAL; - else - err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L, - (struct dccp_so_feat __user *) - optval); - break; case DCCP_SOCKOPT_CHANGE_R: - if (optlen != sizeof(struct dccp_so_feat)) - err = -EINVAL; - else - err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R, - (struct dccp_so_feat __user *) - optval); + DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); + err = 0; break; case DCCP_SOCKOPT_SERVER_TIMEWAIT: if (dp->dccps_role != DCCP_ROLE_SERVER) -- cgit v1.2.3 From 20f41eee82864e308a5499308a1722dc3181cc3a Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Feature negotiation for minimum-checksum-coverage This provides feature negotiation for server minimum checksum coverage which so far has been missing. Since sender/receiver coverage values range only from 0...15, their type has also been reduced in size from u16 to u4. Feature-negotiation options are now generated for both sender and receiver coverage, i.e. when the peer has `forgotten' to enable partial coverage then feature negotiation will automatically enable (negotiate) the partial coverage value for this connection. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- include/linux/dccp.h | 4 ++-- net/dccp/proto.c | 53 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 6eaaca9b037a..5a5a89935dbc 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -527,8 +527,8 @@ struct dccp_sock { __u32 dccps_timestamp_time; __u16 dccps_l_ack_ratio; __u16 dccps_r_ack_ratio; - __u16 dccps_pcslen; - __u16 dccps_pcrlen; + __u8 dccps_pcslen:4; + __u8 dccps_pcrlen:4; __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; struct dccp_minisock dccps_minisock; diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 108d56bd25c5..47b137a3feaf 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -470,6 +470,42 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, return 0; } +static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) +{ + u8 *list, len; + int i, rc; + + if (cscov < 0 || cscov > 15) + return -EINVAL; + /* + * Populate a list of permissible values, in the range cscov...15. This + * is necessary since feature negotiation of single values only works if + * both sides incidentally choose the same value. Since the list starts + * lowest-value first, negotiation will pick the smallest shared value. + */ + if (cscov == 0) + return 0; + len = 16 - cscov; + + list = kmalloc(len, GFP_KERNEL); + if (list == NULL) + return -ENOBUFS; + + for (i = 0; i < len; i++) + list[i] = cscov++; + + rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); + + if (rc == 0) { + if (rx) + dccp_sk(sk)->dccps_pcrlen = cscov; + else + dccp_sk(sk)->dccps_pcslen = cscov; + } + kfree(list); + return rc; +} + static int do_dccp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) { @@ -502,20 +538,11 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, else dp->dccps_server_timewait = (val != 0); break; - case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */ - if (val < 0 || val > 15) - err = -EINVAL; - else - dp->dccps_pcslen = val; + case DCCP_SOCKOPT_SEND_CSCOV: + err = dccp_setsockopt_cscov(sk, val, false); break; - case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */ - if (val < 0 || val > 15) - err = -EINVAL; - else { - dp->dccps_pcrlen = val; - /* FIXME: add feature negotiation, - * ChangeL(MinimumChecksumCoverage, val) */ - } + case DCCP_SOCKOPT_RECV_CSCOV: + err = dccp_setsockopt_cscov(sk, val, true); break; default: err = -ENOPROTOOPT; -- cgit v1.2.3 From 17c30b40ed79e9f3955e884632c8f01e577b204a Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Deprecate Ack Ratio sysctl This patch deprecates the Ack Ratio sysctl, since * Ack Ratio is entirely ignored by CCID-3 and CCID-4, * Ack Ratio currently doesn't work in CCID-2 (i.e. is always set to 1); * even if it would work in CCID-2, there is no point for a user to change it: - Ack Ratio is constrained by cwnd (RFC 4341, 6.1.2), - if Ack Ratio > cwnd, the system resorts to spurious RTO timeouts (since waiting for Acks which will never arrive in this window), - cwnd is not a user-configurable value. The only reasonable place for Ack Ratio is to print it for debugging. It is planned to do this later on, as part of e.g. dccp_probe. With this patch Ack Ratio is now under full control of feature negotiation: * Ack Ratio is resolved as a dependency of the selected CCID; * if the chosen CCID supports it (i.e. CCID == CCID-2), Ack Ratio is set to the default of 2, following RFC 4340, 11.3 - "New connections start with Ack Ratio 2 for both endpoints"; * what happens then is part of another patch set, since it concerns the dynamic update of Ack Ratio while the connection is in full flight. Thanks to Tomasz Grobelny for discussion leading up to this patch. Signed-off-by: Gerrit Renker Acked-by: Arnaldo Carvalho de Melo --- Documentation/networking/dccp.txt | 3 --- include/linux/dccp.h | 2 -- net/dccp/dccp.h | 1 - net/dccp/minisocks.c | 1 - net/dccp/options.c | 1 - net/dccp/sysctl.c | 7 ------- 6 files changed, 15 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index f0aeb20fa63b..43df4487379b 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -125,9 +125,6 @@ send_ndp = 1 send_ackvec = 1 Whether or not to send Ack Vector options (sec. 11.5). -ack_ratio = 2 - The default Ack Ratio (sec. 11.3) to use. - tx_ccid = 2 Default CCID for the sender-receiver half-connection. diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 5a5a89935dbc..eda389ce04f4 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -368,7 +368,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) * @dccpms_ccid - Congestion Control Id (CCID) (section 10) * @dccpms_send_ack_vector - Send Ack Vector Feature (section 11.5) * @dccpms_send_ndp_count - Send NDP Count Feature (7.7.2) - * @dccpms_ack_ratio - Ack Ratio Feature (section 11.3) * @dccpms_pending - List of features being negotiated * @dccpms_conf - */ @@ -378,7 +377,6 @@ struct dccp_minisock { __u8 dccpms_tx_ccid; __u8 dccpms_send_ack_vector; __u8 dccpms_send_ndp_count; - __u8 dccpms_ack_ratio; struct list_head dccpms_pending; struct list_head dccpms_conf; }; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index e656dafb5d96..031ce350d3c1 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -98,7 +98,6 @@ extern int sysctl_dccp_retries2; extern int sysctl_dccp_feat_sequence_window; extern int sysctl_dccp_feat_rx_ccid; extern int sysctl_dccp_feat_tx_ccid; -extern int sysctl_dccp_feat_ack_ratio; extern int sysctl_dccp_feat_send_ack_vector; extern int sysctl_dccp_feat_send_ndp_count; extern int sysctl_dccp_tx_qlen; diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index e487133ae079..ee7f40f8ddfa 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -47,7 +47,6 @@ void dccp_minisock_init(struct dccp_minisock *dmsk) dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; dmsk->dccpms_rx_ccid = sysctl_dccp_feat_rx_ccid; dmsk->dccpms_tx_ccid = sysctl_dccp_feat_tx_ccid; - dmsk->dccpms_ack_ratio = sysctl_dccp_feat_ack_ratio; dmsk->dccpms_send_ack_vector = sysctl_dccp_feat_send_ack_vector; dmsk->dccpms_send_ndp_count = sysctl_dccp_feat_send_ndp_count; } diff --git a/net/dccp/options.c b/net/dccp/options.c index 67a171a1268c..515ad45013ad 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -26,7 +26,6 @@ int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; -int sysctl_dccp_feat_ack_ratio = DCCPF_INITIAL_ACK_RATIO; int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT; diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index 21295993fdb8..f6e54f433e29 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -40,13 +40,6 @@ static struct ctl_table dccp_default_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "ack_ratio", - .data = &sysctl_dccp_feat_ack_ratio, - .maxlen = sizeof(sysctl_dccp_feat_ack_ratio), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "send_ackvec", .data = &sysctl_dccp_feat_send_ack_vector, -- cgit v1.2.3 From fade756f18d42694e3acb00e3471ab43002cba16 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Set per-connection CCIDs via socket options With this patch, TX/RX CCIDs can now be changed on a per-connection basis, which overrides the defaults set by the global sysctl variables for TX/RX CCIDs. To make full use of this facility, the remaining patches of this patch set are needed, which track dependencies and activate negotiated feature values. Note on the maximum number of CCIDs that can be registered: ----------------------------------------------------------- The maximum number of CCIDs that can be registered on the socket is constrained by the space in a Confirm/Change feature negotiation option. The space in these in turn depends on the size of header options as defined in RFC 4340, 5.8. Since this is a recurring constant, it has been moved from ackvec.h into linux/dccp.h, clarifying its purpose. Relative to this size, the maximum number of CCID identifiers that can be present in a Confirm option (which always consumes 1 byte more than a Change option, cf. 6.1) is 2 bytes less than the maximum TLV size: one for the CCID-feature-type and one for the selected value. Signed-off-by: Gerrit Renker --- Documentation/networking/dccp.txt | 14 ++++++++++++++ include/linux/dccp.h | 5 +++++ net/dccp/ackvec.c | 9 ++++----- net/dccp/ackvec.h | 5 ++--- net/dccp/feat.h | 2 ++ net/dccp/proto.c | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 43df4487379b..610083ff73f6 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -61,6 +61,20 @@ DCCP_SOCKOPT_AVAILABLE_CCIDS is also read-only and returns the list of CCIDs supported by the endpoint (see include/linux/dccp.h for symbolic constants). The caller needs to provide a sufficiently large (> 2) array of type uint8_t. +DCCP_SOCKOPT_CCID is write-only and sets both the TX and RX CCIDs at the same +time, combining the operation of the next two socket options. This option is +preferrable over the latter two, since often applications will use the same +type of CCID for both directions; and mixed use of CCIDs is not currently well +understood. This socket option takes as argument at least one uint8_t value, or +an array of uint8_t values, which must match available CCIDS (see above). CCIDs +must be registered on the socket before calling connect() or listen(). + +DCCP_SOCKOPT_TX_CCID is read/write. It returns the current CCID (if set) or sets +the preference list for the TX CCID, using the same format as DCCP_SOCKOPT_CCID. +Please note that the getsockopt argument type here is `int', not uint8_t. + +DCCP_SOCKOPT_RX_CCID is analogous to DCCP_SOCKOPT_TX_CCID, but for the RX CCID. + DCCP_SOCKOPT_SERVER_TIMEWAIT enables the server (listening socket) to hold timewait state when closing the connection (RFC 4340, 8.3). The usual case is that the closing server sends a CloseReq, whereupon the client holds timewait diff --git a/include/linux/dccp.h b/include/linux/dccp.h index eda389ce04f4..6a72ff52a8a4 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -168,6 +168,8 @@ enum { DCCPO_MIN_CCID_SPECIFIC = 128, DCCPO_MAX_CCID_SPECIFIC = 255, }; +/* maximum size of a single TLV-encoded DCCP option (sans type/len bytes) */ +#define DCCP_SINGLE_OPT_MAXLEN 253 /* DCCP CCIDS */ enum { @@ -203,6 +205,9 @@ enum dccp_feature_numbers { #define DCCP_SOCKOPT_SEND_CSCOV 10 #define DCCP_SOCKOPT_RECV_CSCOV 11 #define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 +#define DCCP_SOCKOPT_CCID 13 +#define DCCP_SOCKOPT_TX_CCID 14 +#define DCCP_SOCKOPT_RX_CCID 15 #define DCCP_SOCKOPT_CCID_RX_INFO 128 #define DCCP_SOCKOPT_CCID_TX_INFO 192 diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c index 1e8be246ad15..01e4d39fa232 100644 --- a/net/dccp/ackvec.c +++ b/net/dccp/ackvec.c @@ -12,7 +12,6 @@ #include "ackvec.h" #include "dccp.h" -#include #include #include #include @@ -68,7 +67,7 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) struct dccp_sock *dp = dccp_sk(sk); struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; /* Figure out how many options do we need to represent the ackvec */ - const u16 nr_opts = DIV_ROUND_UP(av->av_vec_len, DCCP_MAX_ACKVEC_OPT_LEN); + const u8 nr_opts = DIV_ROUND_UP(av->av_vec_len, DCCP_SINGLE_OPT_MAXLEN); u16 len = av->av_vec_len + 2 * nr_opts, i; u32 elapsed_time; const unsigned char *tail, *from; @@ -100,8 +99,8 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) for (i = 0; i < nr_opts; ++i) { int copylen = len; - if (len > DCCP_MAX_ACKVEC_OPT_LEN) - copylen = DCCP_MAX_ACKVEC_OPT_LEN; + if (len > DCCP_SINGLE_OPT_MAXLEN) + copylen = DCCP_SINGLE_OPT_MAXLEN; *to++ = DCCPO_ACK_VECTOR_0; *to++ = copylen + 2; @@ -432,7 +431,7 @@ found: int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, u64 *ackno, const u8 opt, const u8 *value, const u8 len) { - if (len > DCCP_MAX_ACKVEC_OPT_LEN) + if (len > DCCP_SINGLE_OPT_MAXLEN) return -1; /* dccp_ackvector_print(DCCP_SKB_CB(skb)->dccpd_ack_seq, value, len); */ diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h index bcb64fb4acef..4ccee030524e 100644 --- a/net/dccp/ackvec.h +++ b/net/dccp/ackvec.h @@ -11,15 +11,14 @@ * published by the Free Software Foundation. */ +#include #include #include #include #include -/* Read about the ECN nonce to see why it is 253 */ -#define DCCP_MAX_ACKVEC_OPT_LEN 253 /* We can spread an ack vector across multiple options */ -#define DCCP_MAX_ACKVEC_LEN (DCCP_MAX_ACKVEC_OPT_LEN * 2) +#define DCCP_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * 2) #define DCCP_ACKVEC_STATE_RECEIVED 0 #define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6) diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 2c92bd18e5f4..b53b11717c40 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -22,6 +22,8 @@ /* Wmin=32 and Wmax=2^46-1 from 7.5.2 */ #define DCCPF_SEQ_WMIN 32 #define DCCPF_SEQ_WMAX 0x3FFFFFFFFFFFull +/* Maximum number of SP values that fit in a single (Confirm) option */ +#define DCCP_FEAT_MAX_SP_VALS (DCCP_SINGLE_OPT_MAXLEN - 2) enum dccp_feat_type { FEAT_AT_RX = 1, /* located at RX side of half-connection */ diff --git a/net/dccp/proto.c b/net/dccp/proto.c index e29bbf914057..2cd56df44d8e 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -506,6 +506,36 @@ static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) return rc; } +static int dccp_setsockopt_ccid(struct sock *sk, int type, + char __user *optval, int optlen) +{ + u8 *val; + int rc = 0; + + if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) + return -EINVAL; + + val = kmalloc(optlen, GFP_KERNEL); + if (val == NULL) + return -ENOMEM; + + if (copy_from_user(val, optval, optlen)) { + kfree(val); + return -EFAULT; + } + + lock_sock(sk); + if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID) + rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen); + + if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID)) + rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen); + release_sock(sk); + + kfree(val); + return rc; +} + static int do_dccp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) { @@ -520,6 +550,10 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, case DCCP_SOCKOPT_CHANGE_R: DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); return 0; + case DCCP_SOCKOPT_CCID: + case DCCP_SOCKOPT_RX_CCID: + case DCCP_SOCKOPT_TX_CCID: + return dccp_setsockopt_ccid(sk, optname, optval, optlen); } if (optlen < (int)sizeof(int)) -- cgit v1.2.3 From 78673e24df27c76ec75565f4024d45c2c74ef148 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Remove obsolete parts of the old CCID interface The TX/RX CCIDs of the minisock are now redundant: similar to the Ack Vector case, their value equals initially that of the sysctl, but at the end of feature negotiation may be something different. The old interface removed by this patch thus has been replaced by the newer interface to dynamically query the currently loaded CCIDs earlier in this patch set. Also removed the constructors for the TX CCID and the RX CCID, since the switch rx/non-rx is done by the handler in minisocks.c (and the handler is the only place in the code where CCIDs are loaded). Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- Documentation/networking/dccp.txt | 5 +++-- include/linux/dccp.h | 3 --- net/dccp/ccid.c | 14 -------------- net/dccp/ccid.h | 5 ----- net/dccp/feat.c | 12 ------------ net/dccp/minisocks.c | 2 -- 6 files changed, 3 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 610083ff73f6..a203d132dbef 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -140,10 +140,11 @@ send_ackvec = 1 Whether or not to send Ack Vector options (sec. 11.5). tx_ccid = 2 - Default CCID for the sender-receiver half-connection. + Default CCID for the sender-receiver half-connection. Depending on the + choice of CCID, the Send Ack Vector feature is enabled automatically. rx_ccid = 2 - Default CCID for the receiver-sender half-connection. + Default CCID for the receiver-sender half-connection; see tx_ccid. seq_window = 100 The initial sequence window (sec. 7.5.2). diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 6a72ff52a8a4..46daea312d92 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -370,7 +370,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) * Will be used to pass the state from dccp_request_sock to dccp_sock. * * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - * @dccpms_ccid - Congestion Control Id (CCID) (section 10) * @dccpms_send_ack_vector - Send Ack Vector Feature (section 11.5) * @dccpms_send_ndp_count - Send NDP Count Feature (7.7.2) * @dccpms_pending - List of features being negotiated @@ -378,8 +377,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) */ struct dccp_minisock { __u64 dccpms_sequence_window; - __u8 dccpms_rx_ccid; - __u8 dccpms_tx_ccid; __u8 dccpms_send_ack_vector; __u8 dccpms_send_ndp_count; struct list_head dccpms_pending; diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index f72ca83df552..330372a1a0b6 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c @@ -253,20 +253,6 @@ out_module_put: EXPORT_SYMBOL_GPL(ccid_new); -struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp) -{ - return ccid_new(id, sk, 1, gfp); -} - -EXPORT_SYMBOL_GPL(ccid_hc_rx_new); - -struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk, gfp_t gfp) -{ - return ccid_new(id, sk, 0, gfp); -} - -EXPORT_SYMBOL_GPL(ccid_hc_tx_new); - static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) { struct ccid_operations *ccid_ops; diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h index 803343aed004..18f69423a708 100644 --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -111,11 +111,6 @@ extern int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, extern struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp); -extern struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, - gfp_t gfp); -extern struct ccid *ccid_hc_tx_new(unsigned char id, struct sock *sk, - gfp_t gfp); - static inline int ccid_get_current_rx_ccid(struct dccp_sock *dp) { struct ccid *ccid = dp->dccps_hc_rx_ccid; diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 6c82dea409d9..cb2ddd2b894b 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -1119,18 +1119,6 @@ int dccp_feat_init(struct sock *sk) INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */ INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */ - /* CCID L */ - rc = __feat_register_sp(&dp->dccps_featneg, DCCPF_CCID, 1, 0, - &dmsk->dccpms_tx_ccid, 1); - if (rc) - goto out; - - /* CCID R */ - rc = __feat_register_sp(&dp->dccps_featneg, DCCPF_CCID, 0, 0, - &dmsk->dccpms_rx_ccid, 1); - if (rc) - goto out; - /* Ack ratio */ rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, dp->dccps_l_ack_ratio); diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 258195972bce..486d61df2604 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -45,8 +45,6 @@ EXPORT_SYMBOL_GPL(dccp_death_row); void dccp_minisock_init(struct dccp_minisock *dmsk) { dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; - dmsk->dccpms_rx_ccid = sysctl_dccp_feat_rx_ccid; - dmsk->dccpms_tx_ccid = sysctl_dccp_feat_tx_ccid; dmsk->dccpms_send_ack_vector = sysctl_dccp_feat_send_ack_vector; dmsk->dccpms_send_ndp_count = sysctl_dccp_feat_send_ndp_count; } -- cgit v1.2.3 From 68e074bfcef269bc61006c2740d7f89ccbbd93d7 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Remove manual influence on NDP Count feature Updating the NDP count feature is handled automatically now: * for CCID-2 it is disabled, since the code does not use NDP counts; * for CCID-3 it is enabled, as NDP counts are used to determine loss lengths. Allowing the user to change NDP values leads to unpredictable and failing behaviour, since it is then possible to disable NDP counts even when they are needed (e.g. in CCID-3). This means that only those user settings are sensible that agree with the values for Send NDP Count implied by the choice of CCID. But those settings are already activated by the feature negotiation (CCID dependency tracking), hence this form of support is redundant. At startup the initialisation of the NDP count feature is with the default value of 0, which is done implicitly by the zeroing-out of the socket when it is allocated. If the choice of CCID or feature negotiation enables NDP count, this will then be updated via the NDP activation handler. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- Documentation/networking/dccp.txt | 3 --- include/linux/dccp.h | 4 ++-- net/dccp/dccp.h | 1 - net/dccp/feat.c | 2 +- net/dccp/minisocks.c | 1 - net/dccp/options.c | 4 +--- net/dccp/sysctl.c | 7 ------- 7 files changed, 4 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index a203d132dbef..1403745ab406 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -133,9 +133,6 @@ retries2 importance for retransmitted acknowledgments and feature negotiation, data packets are never retransmitted. Analogue of tcp_retries2. -send_ndp = 1 - Whether or not to send NDP count options (sec. 7.7.2). - send_ackvec = 1 Whether or not to send Ack Vector options (sec. 11.5). diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 46daea312d92..60e94438eadd 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -371,14 +371,12 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) * * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) * @dccpms_send_ack_vector - Send Ack Vector Feature (section 11.5) - * @dccpms_send_ndp_count - Send NDP Count Feature (7.7.2) * @dccpms_pending - List of features being negotiated * @dccpms_conf - */ struct dccp_minisock { __u64 dccpms_sequence_window; __u8 dccpms_send_ack_vector; - __u8 dccpms_send_ndp_count; struct list_head dccpms_pending; struct list_head dccpms_conf; }; @@ -490,6 +488,7 @@ struct dccp_ackvec; * @dccps_r_ack_ratio - feature-remote Ack Ratio * @dccps_pcslen - sender partial checksum coverage (via sockopt) * @dccps_pcrlen - receiver partial checksum coverage (via sockopt) + * @dccps_send_ndp_count - local Send NDP Count feature (7.7.2) * @dccps_ndp_count - number of Non Data Packets since last data packet * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) * @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4) @@ -529,6 +528,7 @@ struct dccp_sock { __u16 dccps_r_ack_ratio; __u8 dccps_pcslen:4; __u8 dccps_pcrlen:4; + __u8 dccps_send_ndp_count:1; __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; struct dccp_minisock dccps_minisock; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 1baed789bcc2..51436c825655 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -99,7 +99,6 @@ extern int sysctl_dccp_feat_sequence_window; extern int sysctl_dccp_feat_rx_ccid; extern int sysctl_dccp_feat_tx_ccid; extern int sysctl_dccp_feat_send_ack_vector; -extern int sysctl_dccp_feat_send_ndp_count; extern int sysctl_dccp_tx_qlen; extern int sysctl_dccp_sync_ratelimit; diff --git a/net/dccp/feat.c b/net/dccp/feat.c index cb2ddd2b894b..35a57ab3bb1e 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -86,7 +86,7 @@ static int dccp_hdlr_ackvec(struct sock *sk, u64 enable, bool rx) static int dccp_hdlr_ndp(struct sock *sk, u64 enable, bool rx) { if (!rx) - dccp_msk(sk)->dccpms_send_ndp_count = (enable > 0); + dccp_sk(sk)->dccps_send_ndp_count = (enable > 0); return 0; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 486d61df2604..9e2232572662 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -46,7 +46,6 @@ void dccp_minisock_init(struct dccp_minisock *dmsk) { dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; dmsk->dccpms_send_ack_vector = sysctl_dccp_feat_send_ack_vector; - dmsk->dccpms_send_ndp_count = sysctl_dccp_feat_send_ndp_count; } void dccp_time_wait(struct sock *sk, int state, int timeo) diff --git a/net/dccp/options.c b/net/dccp/options.c index 3a9a22f0ac1a..6b0704497e83 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -27,7 +27,6 @@ int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; -int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT; u64 dccp_decode_value_var(const u8 *bf, const u8 len) { @@ -531,8 +530,7 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb) DCCP_SKB_CB(skb)->dccpd_opt_len = 0; - if (dmsk->dccpms_send_ndp_count && - dccp_insert_option_ndp(sk, skb)) + if (dp->dccps_send_ndp_count && dccp_insert_option_ndp(sk, skb)) return -1; if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA) { diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index f6e54f433e29..587c12f915c1 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -47,13 +47,6 @@ static struct ctl_table dccp_default_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "send_ndp", - .data = &sysctl_dccp_feat_send_ndp_count, - .maxlen = sizeof(sysctl_dccp_feat_send_ndp_count), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "request_retries", .data = &sysctl_dccp_request_retries, -- cgit v1.2.3 From b235dc4abbc1356284bd0dc730efa711f394e0e2 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp ccid-2: Phase out the use of boolean Ack Vector sysctl This removes the use of the sysctl and the minisock variable for the Send Ack Vector feature, which is now handled fully dynamically via feature negotiation; i.e. when CCID2 is enabled, Ack Vectors are automatically enabled (as per RFC 4341, 4.). Using a sysctl in parallel to this implementation would open the door to crashes, since much of the code relies on tests of the boolean minisock / sysctl variable. Thus, this patch replaces all tests of type if (dccp_msk(sk)->dccpms_send_ack_vector) /* ... */ with if (dp->dccps_hc_rx_ackvec != NULL) /* ... */ The dccps_hc_rx_ackvec is allocated by the dccp_hdlr_ackvec() when feature negotiation concluded that Ack Vectors are to be used on the half-connection. Otherwise, it is NULL (due to dccp_init_sock/dccp_create_openreq_child), so that the test is a valid one. The activation handler for Ack Vectors is called as soon as the feature negotiation has concluded at the * server when the Ack marking the transition RESPOND => OPEN arrives; * client after it has sent its ACK, marking the transition REQUEST => PARTOPEN. Adding the sequence number of the Response packet to the Ack Vector has been removed, since (a) connection establishment implies that the Response has been received; (b) the CCIDs only look at packets received in the (PART)OPEN state, i.e. this entry will always be ignored; (c) it can not be used for anything useful - to detect loss for instance, only packets received after the loss can serve as pseudo-dupacks. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- Documentation/networking/dccp.txt | 3 --- include/linux/dccp.h | 3 --- net/dccp/dccp.h | 3 +-- net/dccp/diag.c | 2 +- net/dccp/input.c | 12 +++--------- net/dccp/minisocks.c | 1 - net/dccp/options.c | 7 ++----- net/dccp/proto.c | 3 +-- net/dccp/sysctl.c | 7 ------- 9 files changed, 8 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 1403745ab406..7a3bb1abb830 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -133,9 +133,6 @@ retries2 importance for retransmitted acknowledgments and feature negotiation, data packets are never retransmitted. Analogue of tcp_retries2. -send_ackvec = 1 - Whether or not to send Ack Vector options (sec. 11.5). - tx_ccid = 2 Default CCID for the sender-receiver half-connection. Depending on the choice of CCID, the Send Ack Vector feature is enabled automatically. diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 60e94438eadd..61734e27abb7 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -360,7 +360,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) #define DCCPF_INITIAL_SEQUENCE_WINDOW 100 #define DCCPF_INITIAL_ACK_RATIO 2 #define DCCPF_INITIAL_CCID DCCPC_CCID2 -#define DCCPF_INITIAL_SEND_ACK_VECTOR 1 /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 @@ -370,13 +369,11 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) * Will be used to pass the state from dccp_request_sock to dccp_sock. * * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - * @dccpms_send_ack_vector - Send Ack Vector Feature (section 11.5) * @dccpms_pending - List of features being negotiated * @dccpms_conf - */ struct dccp_minisock { __u64 dccpms_sequence_window; - __u8 dccpms_send_ack_vector; struct list_head dccpms_pending; struct list_head dccpms_conf; }; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 51436c825655..3fd16e82c003 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -98,7 +98,6 @@ extern int sysctl_dccp_retries2; extern int sysctl_dccp_feat_sequence_window; extern int sysctl_dccp_feat_rx_ccid; extern int sysctl_dccp_feat_tx_ccid; -extern int sysctl_dccp_feat_send_ack_vector; extern int sysctl_dccp_tx_qlen; extern int sysctl_dccp_sync_ratelimit; @@ -434,7 +433,7 @@ static inline int dccp_ack_pending(const struct sock *sk) const struct dccp_sock *dp = dccp_sk(sk); return dp->dccps_timestamp_echo != 0 || #ifdef CONFIG_IP_DCCP_ACKVEC - (dccp_msk(sk)->dccpms_send_ack_vector && + (dp->dccps_hc_rx_ackvec != NULL && dccp_ackvec_pending(dp->dccps_hc_rx_ackvec)) || #endif inet_csk_ack_scheduled(sk); diff --git a/net/dccp/diag.c b/net/dccp/diag.c index d8a3509b26f6..93aae7c95550 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -29,7 +29,7 @@ static void dccp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_backoff = icsk->icsk_backoff; info->tcpi_pmtu = icsk->icsk_pmtu_cookie; - if (dccp_msk(sk)->dccpms_send_ack_vector) + if (dp->dccps_hc_rx_ackvec != NULL) info->tcpi_options |= TCPI_OPT_SACK; ccid_hc_rx_get_info(dp->dccps_hc_rx_ccid, sk, info); diff --git a/net/dccp/input.c b/net/dccp/input.c index 0672b7e1763e..5eb443f656c1 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -163,7 +163,7 @@ static void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); - if (dccp_msk(sk)->dccpms_send_ack_vector) + if (dp->dccps_hc_rx_ackvec != NULL) dccp_ackvec_check_rcv_ackno(dp->dccps_hc_rx_ackvec, sk, DCCP_SKB_CB(skb)->dccpd_ack_seq); } @@ -375,7 +375,7 @@ int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) dccp_event_ack_recv(sk, skb); - if (dccp_msk(sk)->dccpms_send_ack_vector && + if (dp->dccps_hc_rx_ackvec != NULL && dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, DCCP_SKB_CB(skb)->dccpd_seq, DCCP_ACKVEC_STATE_RECEIVED)) @@ -434,12 +434,6 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, dp->dccps_syn_rtt = dccp_sample_rtt(sk, 10 * (tstamp - dp->dccps_options_received.dccpor_timestamp_echo)); - if (dccp_msk(sk)->dccpms_send_ack_vector && - dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, - DCCP_SKB_CB(skb)->dccpd_seq, - DCCP_ACKVEC_STATE_RECEIVED)) - goto out_invalid_packet; /* FIXME: change error code */ - /* Stop the REQUEST timer */ inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); WARN_ON(sk->sk_send_head == NULL); @@ -637,7 +631,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) dccp_event_ack_recv(sk, skb); - if (dccp_msk(sk)->dccpms_send_ack_vector && + if (dp->dccps_hc_rx_ackvec != NULL && dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, DCCP_SKB_CB(skb)->dccpd_seq, DCCP_ACKVEC_STATE_RECEIVED)) diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 9e2232572662..0ebf8ebcf3de 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -45,7 +45,6 @@ EXPORT_SYMBOL_GPL(dccp_death_row); void dccp_minisock_init(struct dccp_minisock *dmsk) { dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; - dmsk->dccpms_send_ack_vector = sysctl_dccp_feat_send_ack_vector; } void dccp_time_wait(struct sock *sk, int state, int timeo) diff --git a/net/dccp/options.c b/net/dccp/options.c index 6b0704497e83..aca309e16632 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -26,7 +26,6 @@ int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; -int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; u64 dccp_decode_value_var(const u8 *bf, const u8 len) { @@ -145,8 +144,7 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, case DCCPO_ACK_VECTOR_1: if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */ break; - - if (dccp_msk(sk)->dccpms_send_ack_vector && + if (dp->dccps_hc_rx_ackvec != NULL && dccp_ackvec_parse(sk, skb, &ackno, opt, value, len)) goto out_invalid_option; break; @@ -526,7 +524,6 @@ static void dccp_insert_option_padding(struct sk_buff *skb) int dccp_insert_options(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); - struct dccp_minisock *dmsk = dccp_msk(sk); DCCP_SKB_CB(skb)->dccpd_opt_len = 0; @@ -547,7 +544,7 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb) if (dccp_insert_option_timestamp(sk, skb)) return -1; - } else if (dmsk->dccpms_send_ack_vector && + } else if (dp->dccps_hc_rx_ackvec != NULL && dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) && dccp_insert_option_ackvec(sk, skb)) { return -1; diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 0d420790b795..775eaa3d0c49 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -207,7 +207,6 @@ EXPORT_SYMBOL_GPL(dccp_init_sock); void dccp_destroy_sock(struct sock *sk) { struct dccp_sock *dp = dccp_sk(sk); - struct dccp_minisock *dmsk = dccp_msk(sk); /* * DCCP doesn't use sk_write_queue, just sk_send_head @@ -225,7 +224,7 @@ void dccp_destroy_sock(struct sock *sk) kfree(dp->dccps_service_list); dp->dccps_service_list = NULL; - if (dmsk->dccpms_send_ack_vector) { + if (dp->dccps_hc_rx_ackvec != NULL) { dccp_ackvec_free(dp->dccps_hc_rx_ackvec); dp->dccps_hc_rx_ackvec = NULL; } diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index 587c12f915c1..018e210875e1 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -40,13 +40,6 @@ static struct ctl_table dccp_default_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "send_ackvec", - .data = &sysctl_dccp_feat_send_ack_vector, - .maxlen = sizeof(sysctl_dccp_feat_send_ack_vector), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "request_retries", .data = &sysctl_dccp_request_retries, -- cgit v1.2.3 From 5d3dac267a7fd0811ec777e76a81f97f5cdcb395 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Initialisation framework for feature negotiation This initialises feature negotiation from two tables, which are initialised from sysctls. As a novel feature, specifics of the implementation (e.g. currently short seqnos and ECN are not supported) are advertised for robustness. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- include/linux/dccp.h | 19 --------------- net/dccp/feat.c | 66 ++++++++++++++++++++++++++++++++++++++++++++-------- net/dccp/feat.h | 2 +- 3 files changed, 57 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 61734e27abb7..990e97fa1f07 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -369,28 +369,9 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) * Will be used to pass the state from dccp_request_sock to dccp_sock. * * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - * @dccpms_pending - List of features being negotiated - * @dccpms_conf - */ struct dccp_minisock { __u64 dccpms_sequence_window; - struct list_head dccpms_pending; - struct list_head dccpms_conf; -}; - -struct dccp_opt_conf { - __u8 *dccpoc_val; - __u8 dccpoc_len; -}; - -struct dccp_opt_pend { - struct list_head dccpop_node; - __u8 dccpop_type; - __u8 dccpop_feat; - __u8 *dccpop_val; - __u8 dccpop_len; - int dccpop_conf; - struct dccp_opt_conf *dccpop_sc; }; extern void dccp_minisock_init(struct dccp_minisock *dmsk); diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 35a57ab3bb1e..a687740e4420 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -1110,24 +1110,70 @@ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq, return 0; /* ignore FN options in all other states */ } +/** + * dccp_feat_init - Seed feature negotiation with host-specific defaults + * This initialises global defaults, depending on the value of the sysctls. + * These can later be overridden by registering changes via setsockopt calls. + * The last link in the chain is finalise_settings, to make sure that between + * here and the start of actual feature negotiation no inconsistencies enter. + * + * All features not appearing below use either defaults or are otherwise + * later adjusted through dccp_feat_finalise_settings(). + */ int dccp_feat_init(struct sock *sk) { - struct dccp_sock *dp = dccp_sk(sk); - struct dccp_minisock *dmsk = dccp_msk(sk); + struct list_head *fn = &dccp_sk(sk)->dccps_featneg; + u8 on = 1, off = 0; int rc; + struct { + u8 *val; + u8 len; + } tx, rx; + + /* Non-negotiable (NN) features */ + rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0, + sysctl_dccp_feat_sequence_window); + if (rc) + return rc; + + /* Server-priority (SP) features */ + + /* Advertise that short seqnos are not supported (7.6.1) */ + rc = __feat_register_sp(fn, DCCPF_SHORT_SEQNOS, true, true, &off, 1); + if (rc) + return rc; - INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */ - INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */ + /* RFC 4340 12.1: "If a DCCP is not ECN capable, ..." */ + rc = __feat_register_sp(fn, DCCPF_ECN_INCAPABLE, true, true, &on, 1); + if (rc) + return rc; + + /* + * We advertise the available list of CCIDs and reorder according to + * preferences, to avoid failure resulting from negotiating different + * singleton values (which always leads to failure). + * These settings can still (later) be overridden via sockopts. + */ + if (ccid_get_builtin_ccids(&tx.val, &tx.len) || + ccid_get_builtin_ccids(&rx.val, &rx.len)) + return -ENOBUFS; - /* Ack ratio */ - rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, - dp->dccps_l_ack_ratio); -out: + if (!dccp_feat_prefer(sysctl_dccp_feat_tx_ccid, tx.val, tx.len) || + !dccp_feat_prefer(sysctl_dccp_feat_rx_ccid, rx.val, rx.len)) + goto free_ccid_lists; + + rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len); + if (rc) + goto free_ccid_lists; + + rc = __feat_register_sp(fn, DCCPF_CCID, false, false, rx.val, rx.len); + +free_ccid_lists: + kfree(tx.val); + kfree(rx.val); return rc; } -EXPORT_SYMBOL_GPL(dccp_feat_init); - int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list) { struct dccp_sock *dp = dccp_sk(sk); diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 177e9c39ea9f..f73b47abca93 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -112,13 +112,13 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) #define dccp_feat_debug(type, feat, val) #endif /* CONFIG_IP_DCCP_DEBUG */ +extern int dccp_feat_init(struct sock *sk); extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, u8 const *list, u8 len); extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, u8 mand, u8 opt, u8 feat, u8 *val, u8 len); extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); -extern int dccp_feat_init(struct sock *sk); /* * Encoding variable-length options and their maximum length. -- cgit v1.2.3 From 51c7d4fa2675c106a980ddcdbe308b54b5151945 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Implement both feature-local and feature-remote Sequence Window feature This adds full support for local/remote Sequence Window feature, from which the * sequence-number-validity (W) and * acknowledgment-number-validity (W') windows derive as specified in RFC 4340, 7.5.3. Specifically, the following changes are introduced: * integrated new socket fields into dccp_sk; * updated the update_gsr/gss routines with regard to these fields; * updated handler code: the Sequence Window feature is located at the TX side, so the local feature is meant if the handler-rx flag is false; * the initialisation of `rcv_wnd' in reqsk is removed, since - rcv_wnd is not used by the code anywhere; - sequence number checks are not done in the LISTEN state (cf. 7.5.3); - dccp_check_req checks the Ack number validity more rigorously; * the `struct dccp_minisock' became empty and is now removed. Until the handshake completes with activating negotiated values, the local/remote Sequence-Window values are undefined and thus can not reliably be estimated. This issue is addressed in a separate patch. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- Documentation/networking/dccp.txt | 3 ++- include/linux/dccp.h | 24 ++++-------------------- net/dccp/dccp.h | 16 +++++++--------- net/dccp/feat.c | 13 +++++++++++-- net/dccp/minisocks.c | 11 ----------- net/dccp/proto.c | 2 -- 6 files changed, 24 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 7a3bb1abb830..b132e4a3cf0f 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -141,7 +141,8 @@ rx_ccid = 2 Default CCID for the receiver-sender half-connection; see tx_ccid. seq_window = 100 - The initial sequence window (sec. 7.5.2). + The initial sequence window (sec. 7.5.2) of the sender. This influences + the local ackno validity and the remote seqno validity windows (7.5.1). tx_qlen = 5 The size of the transmit buffer in packets. A value of 0 corresponds diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 990e97fa1f07..7a0502ab383a 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -363,19 +363,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 -/** - * struct dccp_minisock - Minimal DCCP connection representation - * - * Will be used to pass the state from dccp_request_sock to dccp_sock. - * - * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - */ -struct dccp_minisock { - __u64 dccpms_sequence_window; -}; - -extern void dccp_minisock_init(struct dccp_minisock *dmsk); - /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from @@ -464,13 +451,14 @@ struct dccp_ackvec; * @dccps_timestamp_time - time of receiving latest @dccps_timestamp_echo * @dccps_l_ack_ratio - feature-local Ack Ratio * @dccps_r_ack_ratio - feature-remote Ack Ratio + * @dccps_l_seq_win - local Sequence Window (influences ack number validity) + * @dccps_r_seq_win - remote Sequence Window (influences seq number validity) * @dccps_pcslen - sender partial checksum coverage (via sockopt) * @dccps_pcrlen - receiver partial checksum coverage (via sockopt) * @dccps_send_ndp_count - local Send NDP Count feature (7.7.2) * @dccps_ndp_count - number of Non Data Packets since last data packet * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) * @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4) - * @dccps_minisock - associated minisock (accessed via dccp_msk) * @dccps_featneg - tracks feature-negotiation state (mostly during handshake) * @dccps_hc_rx_ackvec - rx half connection ack vector * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) @@ -504,12 +492,13 @@ struct dccp_sock { __u32 dccps_timestamp_time; __u16 dccps_l_ack_ratio; __u16 dccps_r_ack_ratio; + __u64 dccps_l_seq_win:48; + __u64 dccps_r_seq_win:48; __u8 dccps_pcslen:4; __u8 dccps_pcrlen:4; __u8 dccps_send_ndp_count:1; __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; - struct dccp_minisock dccps_minisock; struct list_head dccps_featneg; struct dccp_ackvec *dccps_hc_rx_ackvec; struct ccid *dccps_hc_rx_ccid; @@ -527,11 +516,6 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk) return (struct dccp_sock *)sk; } -static inline struct dccp_minisock *dccp_msk(const struct sock *sk) -{ - return (struct dccp_minisock *)&dccp_sk(sk)->dccps_minisock; -} - static inline const char *dccp_role(const struct sock *sk) { switch (dccp_sk(sk)->dccps_role) { diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 3fd16e82c003..6101ecdb85b6 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -409,23 +409,21 @@ static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack, static inline void dccp_update_gsr(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - const struct dccp_minisock *dmsk = dccp_msk(sk); dp->dccps_gsr = seq; - dccp_set_seqno(&dp->dccps_swl, - dp->dccps_gsr + 1 - (dmsk->dccpms_sequence_window / 4)); - dccp_set_seqno(&dp->dccps_swh, - dp->dccps_gsr + (3 * dmsk->dccpms_sequence_window) / 4); + /* Sequence validity window depends on remote Sequence Window (7.5.1) */ + dp->dccps_swl = SUB48(ADD48(dp->dccps_gsr, 1), dp->dccps_r_seq_win / 4); + dp->dccps_swh = ADD48(dp->dccps_gsr, (3 * dp->dccps_r_seq_win) / 4); } static inline void dccp_update_gss(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - dp->dccps_awh = dp->dccps_gss = seq; - dccp_set_seqno(&dp->dccps_awl, - (dp->dccps_gss - - dccp_msk(sk)->dccpms_sequence_window + 1)); + dp->dccps_gss = seq; + /* Ack validity window depends on local Sequence Window value (7.5.1) */ + dp->dccps_awl = SUB48(ADD48(dp->dccps_gss, 1), dp->dccps_l_seq_win); + dp->dccps_awh = dp->dccps_gss; } static inline int dccp_ack_pending(const struct sock *sk) diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 9a4938092783..843465999854 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -52,8 +52,17 @@ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) { - if (!rx) - dccp_msk(sk)->dccpms_sequence_window = seq_win; + struct dccp_sock *dp = dccp_sk(sk); + + if (rx) { + dp->dccps_r_seq_win = seq_win; + /* propagate changes to update SWL/SWH */ + dccp_update_gsr(sk, dp->dccps_gsr); + } else { + dp->dccps_l_seq_win = seq_win; + /* propagate changes to update AWL */ + dccp_update_gss(sk, dp->dccps_gss); + } return 0; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 0ebf8ebcf3de..0ecb19c5e8ce 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -42,11 +42,6 @@ struct inet_timewait_death_row dccp_death_row = { EXPORT_SYMBOL_GPL(dccp_death_row); -void dccp_minisock_init(struct dccp_minisock *dmsk) -{ - dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; -} - void dccp_time_wait(struct sock *sk, int state, int timeo) { struct inet_timewait_sock *tw = NULL; @@ -110,7 +105,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk, struct dccp_request_sock *dreq = dccp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct dccp_sock *newdp = dccp_sk(newsk); - struct dccp_minisock *newdmsk = dccp_msk(newsk); newdp->dccps_role = DCCP_ROLE_SERVER; newdp->dccps_hc_rx_ackvec = NULL; @@ -128,10 +122,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk, * Initialize S.GAR := S.ISS * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies */ - - /* See dccp_v4_conn_request */ - newdmsk->dccpms_sequence_window = req->rcv_wnd; - newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss; dccp_update_gss(newsk, dreq->dreq_iss); @@ -289,7 +279,6 @@ int dccp_reqsk_init(struct request_sock *req, inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport; inet_rsk(req)->acked = 0; - req->rcv_wnd = sysctl_dccp_feat_sequence_window; dreq->dreq_timestamp_echo = 0; /* inherit feature negotiation options from listening socket */ diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 775eaa3d0c49..392a5d822b33 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -180,8 +180,6 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) struct dccp_sock *dp = dccp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); - dccp_minisock_init(&dp->dccps_minisock); - icsk->icsk_rto = DCCP_TIMEOUT_INIT; icsk->icsk_syn_retries = sysctl_dccp_request_retries; sk->sk_state = DCCP_CLOSED; -- cgit v1.2.3 From 0a4822679d94e2b0117aeead06a19fad59533905 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Initialisation and type-checking of feature sysctls This patch takes care of initialising and type-checking sysctls related to feature negotiation. Type checking is important since some of the sysctls now directly act on the feature-negotiation process. The sysctls are initialised with the known default values for each feature. For the type-checking the value constraints from RFC 4340 are used: * Sequence Window uses the specified Wmin=32, the maximum is ulong (4 bytes), tested and confirmed that it works up to 4294967295 - for Gbps speed; * Ack Ratio is between 0 .. 0xffff (2-byte unsigned integer); * CCIDs are between 0 .. 255; * request_retries, retries1, retries2 also between 0..255 for good measure; * tx_qlen is checked to be non-negative; * sync_ratelimit remains as before. Further changes: ---------------- Performed s@sysctl_dccp_feat@sysctl_dccp@g since the sysctls are now in feat.c. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald --- include/linux/dccp.h | 8 -------- net/dccp/dccp.h | 3 --- net/dccp/feat.c | 11 ++++++++--- net/dccp/feat.h | 8 ++++++++ net/dccp/options.c | 4 ---- net/dccp/sysctl.c | 43 ++++++++++++++++++++++++++++++------------- 6 files changed, 46 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 7a0502ab383a..7434a8353e23 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -355,14 +355,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) return __dccp_hdr_len(dccp_hdr(skb)); } - -/* initial values for each feature */ -#define DCCPF_INITIAL_SEQUENCE_WINDOW 100 -#define DCCPF_INITIAL_ACK_RATIO 2 -#define DCCPF_INITIAL_CCID DCCPC_CCID2 -/* FIXME: for now we're default to 1 but it should really be 0 */ -#define DCCPF_INITIAL_SEND_NDP_COUNT 1 - /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 6101ecdb85b6..4f681f1a16cc 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -95,9 +95,6 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); extern int sysctl_dccp_request_retries; extern int sysctl_dccp_retries1; extern int sysctl_dccp_retries2; -extern int sysctl_dccp_feat_sequence_window; -extern int sysctl_dccp_feat_rx_ccid; -extern int sysctl_dccp_feat_tx_ccid; extern int sysctl_dccp_tx_qlen; extern int sysctl_dccp_sync_ratelimit; diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 843465999854..4c95cbdb0e01 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -26,6 +26,11 @@ #include "ccid.h" #include "feat.h" +/* feature-specific sysctls - initialised to the defaults from RFC 4340, 6.4 */ +unsigned long sysctl_dccp_sequence_window __read_mostly = 100; +int sysctl_dccp_rx_ccid __read_mostly = 2, + sysctl_dccp_tx_ccid __read_mostly = 2; + /* * Feature activation handlers. * @@ -1141,7 +1146,7 @@ int dccp_feat_init(struct sock *sk) /* Non-negotiable (NN) features */ rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0, - sysctl_dccp_feat_sequence_window); + sysctl_dccp_sequence_window); if (rc) return rc; @@ -1172,8 +1177,8 @@ int dccp_feat_init(struct sock *sk) if (ccid_request_modules(tx.val, tx.len)) goto free_ccid_lists; - if (!dccp_feat_prefer(sysctl_dccp_feat_tx_ccid, tx.val, tx.len) || - !dccp_feat_prefer(sysctl_dccp_feat_rx_ccid, rx.val, rx.len)) + if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) || + !dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len)) goto free_ccid_lists; rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len); diff --git a/net/dccp/feat.h b/net/dccp/feat.h index f73b47abca93..f8456bca4eeb 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -99,6 +99,13 @@ struct ccid_dependency { u8 val; }; +/* + * Sysctls to seed defaults for feature negotiation + */ +extern unsigned long sysctl_dccp_sequence_window; +extern int sysctl_dccp_rx_ccid; +extern int sysctl_dccp_tx_ccid; + #ifdef CONFIG_IP_DCCP_DEBUG extern const char *dccp_feat_typename(const u8 type); extern const char *dccp_feat_name(const u8 feat); @@ -113,6 +120,7 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) #endif /* CONFIG_IP_DCCP_DEBUG */ extern int dccp_feat_init(struct sock *sk); +extern void dccp_feat_initialise_sysctls(void); extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, u8 const *list, u8 len); extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); diff --git a/net/dccp/options.c b/net/dccp/options.c index aca309e16632..5906e96eedde 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -23,10 +23,6 @@ #include "dccp.h" #include "feat.h" -int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; -int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; -int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; - u64 dccp_decode_value_var(const u8 *bf, const u8 len) { u64 value = 0; diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index 018e210875e1..a5a1856234e7 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -18,55 +18,72 @@ #error This file should not be compiled without CONFIG_SYSCTL defined #endif +/* Boundary values */ +static int zero = 0, + u8_max = 0xFF; +static unsigned long seqw_min = 32; + static struct ctl_table dccp_default_table[] = { { .procname = "seq_window", - .data = &sysctl_dccp_feat_sequence_window, - .maxlen = sizeof(sysctl_dccp_feat_sequence_window), + .data = &sysctl_dccp_sequence_window, + .maxlen = sizeof(sysctl_dccp_sequence_window), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_doulongvec_minmax, + .extra1 = &seqw_min, /* RFC 4340, 7.5.2 */ }, { .procname = "rx_ccid", - .data = &sysctl_dccp_feat_rx_ccid, - .maxlen = sizeof(sysctl_dccp_feat_rx_ccid), + .data = &sysctl_dccp_rx_ccid, + .maxlen = sizeof(sysctl_dccp_rx_ccid), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, /* RFC 4340, 10. */ }, { .procname = "tx_ccid", - .data = &sysctl_dccp_feat_tx_ccid, - .maxlen = sizeof(sysctl_dccp_feat_tx_ccid), + .data = &sysctl_dccp_tx_ccid, + .maxlen = sizeof(sysctl_dccp_tx_ccid), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, /* RFC 4340, 10. */ }, { .procname = "request_retries", .data = &sysctl_dccp_request_retries, .maxlen = sizeof(sysctl_dccp_request_retries), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, }, { .procname = "retries1", .data = &sysctl_dccp_retries1, .maxlen = sizeof(sysctl_dccp_retries1), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, }, { .procname = "retries2", .data = &sysctl_dccp_retries2, .maxlen = sizeof(sysctl_dccp_retries2), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, }, { .procname = "tx_qlen", .data = &sysctl_dccp_tx_qlen, .maxlen = sizeof(sysctl_dccp_tx_qlen), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, }, { .procname = "sync_ratelimit", -- cgit v1.2.3 From f10ecaee6dc2c6d56783462b2a82e98bc81b55f4 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Replace magic CCID-specific numbers by symbolic constants The constants DCCPO_{MIN,MAX}_CCID_SPECIFIC are nowhere used in the code, but instead for the CCID-specific options numbers are used. This patch unifies the use of CCID-specific option numbers, by adding symbolic names reflecting the definitions in RFC 4340, 10.3. Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 6 ++++-- net/dccp/options.c | 13 +++---------- 2 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 7434a8353e23..7187bd8a75f6 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -165,8 +165,10 @@ enum { DCCPO_TIMESTAMP_ECHO = 42, DCCPO_ELAPSED_TIME = 43, DCCPO_MAX = 45, - DCCPO_MIN_CCID_SPECIFIC = 128, - DCCPO_MAX_CCID_SPECIFIC = 255, + DCCPO_MIN_RX_CCID_SPECIFIC = 128, /* from sender to receiver */ + DCCPO_MAX_RX_CCID_SPECIFIC = 191, + DCCPO_MIN_TX_CCID_SPECIFIC = 192, /* from receiver to sender */ + DCCPO_MAX_TX_CCID_SPECIFIC = 255, }; /* maximum size of a single TLV-encoded DCCP option (sans type/len bytes) */ #define DCCP_SINGLE_OPT_MAXLEN 253 diff --git a/net/dccp/options.c b/net/dccp/options.c index b102774694c6..e1c94e2b8be0 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -96,18 +96,11 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, } /* - * CCID-Specific Options (from RFC 4340, sec. 10.3): - * - * Option numbers 128 through 191 are for options sent from the - * HC-Sender to the HC-Receiver; option numbers 192 through 255 - * are for options sent from the HC-Receiver to the HC-Sender. - * * CCID-specific options are ignored during connection setup, as * negotiation may still be in progress (see RFC 4340, 10.3). * The same applies to Ack Vectors, as these depend on the CCID. - * */ - if (dreq != NULL && (opt >= 128 || + if (dreq != NULL && (opt >= DCCPO_MIN_RX_CCID_SPECIFIC || opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1)) goto ignore_option; @@ -226,12 +219,12 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n", dccp_role(sk), elapsed_time); break; - case 128 ... 191: + case DCCPO_MIN_RX_CCID_SPECIFIC ... DCCPO_MAX_RX_CCID_SPECIFIC: if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk, pkt_type, opt, value, len)) goto out_invalid_option; break; - case 192 ... 255: + case DCCPO_MIN_TX_CCID_SPECIFIC ... DCCPO_MAX_TX_CCID_SPECIFIC: if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk, pkt_type, opt, value, len)) goto out_invalid_option; -- cgit v1.2.3 From c2f42077bd06f300ae959204f3c007f820f5e769 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp ccid-2: Schedule Sync as out-of-band mechanism The problem with Ack Vectors is that i) their length is variable and can in principle grow quite large, ii) it is hard to predict exactly how large they will be. Due to the second point it seems not a good idea to reduce the MPS; in particular when on average there is enough room for the Ack Vector and an increase in length is momentarily due to some burst loss, after which the Ack Vector returns to its normal/average length. The solution taken by this patch is to subtract a minimum-expected Ack Vector length from the MPS (previous patch), and to defer any larger Ack Vectors onto a separate Sync - but only if indeed there is no space left on the skb. This patch provides the infrastructure to schedule Sync-packets for transporting (urgent) out-of-band data. Its signalling is quicker than scheduling an Ack, since it does not need to wait for new application data. It can thus serve other parts of the DCCP code as well. Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 2 ++ net/dccp/options.c | 24 ++++++++++++++++++++---- net/dccp/output.c | 8 ++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 7187bd8a75f6..83197b601a4f 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -462,6 +462,7 @@ struct dccp_ackvec; * @dccps_hc_rx_insert_options - receiver wants to add options when acking * @dccps_hc_tx_insert_options - sender wants to add options when sending * @dccps_server_timewait - server holds timewait state on close (RFC 4340, 8.3) + * @dccps_sync_scheduled - flag which signals "send out-of-band message soon" * @dccps_xmit_timer - timer for when CCID is not ready to send * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs) */ @@ -502,6 +503,7 @@ struct dccp_sock { __u8 dccps_hc_rx_insert_options:1; __u8 dccps_hc_tx_insert_options:1; __u8 dccps_server_timewait:1; + __u8 dccps_sync_scheduled:1; struct timer_list dccps_xmit_timer; }; diff --git a/net/dccp/options.c b/net/dccp/options.c index b11d7b7167f0..791e07853a79 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -430,6 +430,7 @@ static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; + struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); const u16 buflen = dccp_ackvec_buflen(av); /* Figure out how many options do we need to represent the ackvec */ const u8 nr_opts = DIV_ROUND_UP(buflen, DCCP_SINGLE_OPT_MAXLEN); @@ -438,10 +439,25 @@ static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) const unsigned char *tail, *from; unsigned char *to; - if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) + if (dcb->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) { + DCCP_WARN("Lacking space for %u bytes on %s packet\n", len, + dccp_packet_name(dcb->dccpd_type)); return -1; - - DCCP_SKB_CB(skb)->dccpd_opt_len += len; + } + /* + * Since Ack Vectors are variable-length, we can not always predict + * their size. To catch exception cases where the space is running out + * on the skb, a separate Sync is scheduled to carry the Ack Vector. + */ + if (len > DCCPAV_MIN_OPTLEN && + len + dcb->dccpd_opt_len + skb->len > dp->dccps_mss_cache) { + DCCP_WARN("No space left for Ack Vector (%u) on skb (%u+%u), " + "MPS=%u ==> reduce payload size?\n", len, skb->len, + dcb->dccpd_opt_len, dp->dccps_mss_cache); + dp->dccps_sync_scheduled = 1; + return 0; + } + dcb->dccpd_opt_len += len; to = skb_push(skb, len); len = buflen; @@ -482,7 +498,7 @@ static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) /* * Each sent Ack Vector is recorded in the list, as per A.2 of RFC 4340. */ - if (dccp_ackvec_update_records(av, DCCP_SKB_CB(skb)->dccpd_seq, nonce)) + if (dccp_ackvec_update_records(av, dcb->dccpd_seq, nonce)) return -ENOBUFS; return 0; } diff --git a/net/dccp/output.c b/net/dccp/output.c index 1b3168307586..bfda071559f4 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -305,6 +305,8 @@ void dccp_write_xmit(struct sock *sk, int block) if (err) DCCP_BUG("err=%d after ccid_hc_tx_packet_sent", err); + if (dp->dccps_sync_scheduled) + dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); } else { dccp_pr_debug("packet discarded due to err=%d\n", err); kfree_skb(skb); @@ -591,6 +593,12 @@ void dccp_send_sync(struct sock *sk, const u64 ackno, DCCP_SKB_CB(skb)->dccpd_type = pkt_type; DCCP_SKB_CB(skb)->dccpd_ack_seq = ackno; + /* + * Clear the flag in case the Sync was scheduled for out-of-band data, + * such as carrying a long Ack Vector. + */ + dccp_sk(sk)->dccps_sync_scheduled = 0; + dccp_transmit_skb(sk, skb); } -- cgit v1.2.3 From e7937772d7a2b0127cc4cbc67bc594e139fdaf63 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Extend CCID packet dequeueing interface This extends the packet dequeuing interface of dccp_write_xmit() to allow 1. CCIDs to take care of timing when the next packet may be sent; 2. delayed sending (as before, with an inter-packet gap up to 65.535 seconds). The main purpose is to take CCID2 out of its polling mode (when it is network- limited, it tries every millisecond to send, without interruption). The interface can also be used to support other CCIDs. The mode of operation for (2) is as follows: * new packet is enqueued via dccp_sendmsg() => dccp_write_xmit(), * ccid_hc_tx_send_packet() detects that it may not send (e.g. window full), * it signals this condition via `CCID_PACKET_WILL_DEQUEUE_LATER', * dccp_write_xmit() returns without further action; * after some time the wait-condition for CCID becomes true, * that CCID schedules the tasklet, * tasklet function calls ccid_hc_tx_send_packet() via dccp_write_xmit(), * since the wait-condition is now true, ccid_hc_tx_packet() returns "send now", * packet is sent, and possibly more (since dccp_write_xmit() loops). Code reuse: the taskled function calls dccp_write_xmit(), the timer function reduces to a wrapper around the same code. If the tasklet finds that the socket is locked, it re-schedules the tasklet function (not the tasklet) after one jiffy. Changed DCCP_BUG to dccp_pr_debug when transmit_skb returns an error (e.g. when a local qdisc is used, NET_XMIT_DROP=1 can be returned for many packets). Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 4 +- net/dccp/output.c | 129 ++++++++++++++++++++++++++++++++------------------- net/dccp/timer.c | 25 +++++----- 3 files changed, 98 insertions(+), 60 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 83197b601a4f..eed52bcd35d0 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -463,7 +463,8 @@ struct dccp_ackvec; * @dccps_hc_tx_insert_options - sender wants to add options when sending * @dccps_server_timewait - server holds timewait state on close (RFC 4340, 8.3) * @dccps_sync_scheduled - flag which signals "send out-of-band message soon" - * @dccps_xmit_timer - timer for when CCID is not ready to send + * @dccps_xmitlet - tasklet scheduled by the TX CCID to dequeue data packets + * @dccps_xmit_timer - used by the TX CCID to delay sending (rate-based pacing) * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs) */ struct dccp_sock { @@ -504,6 +505,7 @@ struct dccp_sock { __u8 dccps_hc_tx_insert_options:1; __u8 dccps_server_timewait:1; __u8 dccps_sync_scheduled:1; + struct tasklet_struct dccps_xmitlet; struct timer_list dccps_xmit_timer; }; diff --git a/net/dccp/output.c b/net/dccp/output.c index bfda071559f4..9afd58e39e23 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -251,65 +251,98 @@ do_interrupted: goto out; } +/** + * dccp_xmit_packet - Send data packet under control of CCID + * Transmits next-queued payload and informs CCID to account for the packet. + */ +static void dccp_xmit_packet(struct sock *sk) +{ + int err, len; + struct dccp_sock *dp = dccp_sk(sk); + struct sk_buff *skb = skb_dequeue(&sk->sk_write_queue); + + if (unlikely(skb == NULL)) + return; + len = skb->len; + + if (sk->sk_state == DCCP_PARTOPEN) { + const u32 cur_mps = dp->dccps_mss_cache - DCCP_FEATNEG_OVERHEAD; + /* + * See 8.1.5 - Handshake Completion. + * + * For robustness we resend Confirm options until the client has + * entered OPEN. During the initial feature negotiation, the MPS + * is smaller than usual, reduced by the Change/Confirm options. + */ + if (!list_empty(&dp->dccps_featneg) && len > cur_mps) { + DCCP_WARN("Payload too large (%d) for featneg.\n", len); + dccp_send_ack(sk); + dccp_feat_list_purge(&dp->dccps_featneg); + } + + inet_csk_schedule_ack(sk); + inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, + inet_csk(sk)->icsk_rto, + DCCP_RTO_MAX); + DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATAACK; + } else if (dccp_ack_pending(sk)) { + DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATAACK; + } else { + DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATA; + } + + err = dccp_transmit_skb(sk, skb); + if (err) + dccp_pr_debug("transmit_skb() returned err=%d\n", err); + /* + * Register this one as sent even if an error occurred. To the remote + * end a local packet drop is indistinguishable from network loss, i.e. + * any local drop will eventually be reported via receiver feedback. + */ + ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, len); + + /* + * If the CCID needs to transfer additional header options out-of-band + * (e.g. Ack Vectors or feature-negotiation options), it activates this + * flag to schedule a Sync. The Sync will automatically incorporate all + * currently pending header options, thus clearing the backlog. + */ + if (dp->dccps_sync_scheduled) + dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); +} + void dccp_write_xmit(struct sock *sk, int block) { struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; while ((skb = skb_peek(&sk->sk_write_queue))) { - int err = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); + int rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); - if (err > 0) { + switch (ccid_packet_dequeue_eval(rc)) { + case CCID_PACKET_WILL_DEQUEUE_LATER: + return; + case CCID_PACKET_DELAY: if (!block) { sk_reset_timer(sk, &dp->dccps_xmit_timer, - msecs_to_jiffies(err)+jiffies); + msecs_to_jiffies(rc)+jiffies); + return; + } + rc = dccp_wait_for_ccid(sk, skb, rc); + if (rc && rc != -EINTR) { + DCCP_BUG("err=%d after dccp_wait_for_ccid", rc); + skb_dequeue(&sk->sk_write_queue); + kfree_skb(skb); break; - } else - err = dccp_wait_for_ccid(sk, skb, err); - if (err && err != -EINTR) - DCCP_BUG("err=%d after dccp_wait_for_ccid", err); - } - - skb_dequeue(&sk->sk_write_queue); - if (err == 0) { - struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); - const int len = skb->len; - - if (sk->sk_state == DCCP_PARTOPEN) { - const u32 cur_mps = dp->dccps_mss_cache - DCCP_FEATNEG_OVERHEAD; - /* - * See 8.1.5 - Handshake Completion. - * - * For robustness we resend Confirm options until the client has - * entered OPEN. During the initial feature negotiation, the MPS - * is smaller than usual, reduced by the Change/Confirm options. - */ - if (!list_empty(&dp->dccps_featneg) && len > cur_mps) { - DCCP_WARN("Payload too large (%d) for featneg.\n", len); - dccp_send_ack(sk); - dccp_feat_list_purge(&dp->dccps_featneg); - } - - inet_csk_schedule_ack(sk); - inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, - inet_csk(sk)->icsk_rto, - DCCP_RTO_MAX); - dcb->dccpd_type = DCCP_PKT_DATAACK; - } else if (dccp_ack_pending(sk)) - dcb->dccpd_type = DCCP_PKT_DATAACK; - else - dcb->dccpd_type = DCCP_PKT_DATA; - - err = dccp_transmit_skb(sk, skb); - ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, len); - if (err) - DCCP_BUG("err=%d after ccid_hc_tx_packet_sent", - err); - if (dp->dccps_sync_scheduled) - dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); - } else { - dccp_pr_debug("packet discarded due to err=%d\n", err); + } + /* fall through */ + case CCID_PACKET_SEND_AT_ONCE: + dccp_xmit_packet(sk); + break; + case CCID_PACKET_ERR: + skb_dequeue(&sk->sk_write_queue); kfree_skb(skb); + dccp_pr_debug("packet discarded due to err=%d\n", rc); } } } diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 162d1e683c39..9369aca4b0e9 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -237,32 +237,35 @@ out: sock_put(sk); } -/* Transmit-delay timer: used by the CCIDs to delay actual send time */ -static void dccp_write_xmit_timer(unsigned long data) +/** + * dccp_write_xmitlet - Workhorse for CCID packet dequeueing interface + * See the comments above %ccid_dequeueing_decision for supported modes. + */ +static void dccp_write_xmitlet(unsigned long data) { struct sock *sk = (struct sock *)data; - struct dccp_sock *dp = dccp_sk(sk); bh_lock_sock(sk); if (sock_owned_by_user(sk)) - sk_reset_timer(sk, &dp->dccps_xmit_timer, jiffies+1); + sk_reset_timer(sk, &dccp_sk(sk)->dccps_xmit_timer, jiffies + 1); else dccp_write_xmit(sk, 0); bh_unlock_sock(sk); - sock_put(sk); } -static void dccp_init_write_xmit_timer(struct sock *sk) +static void dccp_write_xmit_timer(unsigned long data) { - struct dccp_sock *dp = dccp_sk(sk); - - setup_timer(&dp->dccps_xmit_timer, dccp_write_xmit_timer, - (unsigned long)sk); + dccp_write_xmitlet(data); + sock_put((struct sock *)data); } void dccp_init_xmit_timers(struct sock *sk) { - dccp_init_write_xmit_timer(sk); + struct dccp_sock *dp = dccp_sk(sk); + + tasklet_init(&dp->dccps_xmitlet, dccp_write_xmitlet, (unsigned long)sk); + setup_timer(&dp->dccps_xmit_timer, dccp_write_xmit_timer, + (unsigned long)sk); inet_csk_init_xmit_timers(sk, &dccp_write_timer, &dccp_delack_timer, &dccp_keepalive_timer); } -- cgit v1.2.3 From d6da3511d6b558d0b017777b61dc08b8fbc06ea4 Mon Sep 17 00:00:00 2001 From: Tomasz Grobelny Date: Thu, 4 Sep 2008 07:30:19 +0200 Subject: dccp: Policy-based packet dequeueing infrastructure This patch adds a generic infrastructure for policy-based dequeueing of TX packets and provides two policies: * a simple FIFO policy (which is the default) and * a priority based policy (set via socket options). Both policies honour the tx_qlen sysctl for the maximum size of the write queue (can be overridden via socket options). The priority policy uses skb->priority internally to assign an u32 priority identifier, using the same ranking as SO_PRIORITY. The skb->priority field is set to 0 when the packet leaves DCCP. The priority is supplied as ancillary data using cmsg(3), the patch also provides the requisite parsing routines. Signed-off-by: Tomasz Grobelny Signed-off-by: Gerrit Renker --- Documentation/networking/dccp.txt | 19 ++++++ include/linux/dccp.h | 21 +++++++ net/dccp/Makefile | 2 +- net/dccp/dccp.h | 12 ++++ net/dccp/output.c | 7 +-- net/dccp/proto.c | 67 +++++++++++++++++++- net/dccp/qpolicy.c | 126 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 246 insertions(+), 8 deletions(-) create mode 100644 net/dccp/qpolicy.c (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index b132e4a3cf0f..fcfc12534428 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -45,6 +45,25 @@ http://linux-net.osdl.org/index.php/DCCP_Testing#Experimental_DCCP_source_tree Socket options ============== +DCCP_SOCKOPT_QPOLICY_ID sets the dequeuing policy for outgoing packets. It takes +a policy ID as argument and can only be set before the connection (i.e. changes +during an established connection are not supported). Currently, two policies are +defined: the "simple" policy (DCCPQ_POLICY_SIMPLE), which does nothing special, +and a priority-based variant (DCCPQ_POLICY_PRIO). The latter allows to pass an +u32 priority value as ancillary data to sendmsg(), where higher numbers indicate +a higher packet priority (similar to SO_PRIORITY). This ancillary data needs to +be formatted using a cmsg(3) message header filled in as follows: + cmsg->cmsg_level = SOL_DCCP; + cmsg->cmsg_type = DCCP_SCM_PRIORITY; + cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); /* or CMSG_LEN(4) */ + +DCCP_SOCKOPT_QPOLICY_TXQLEN sets the maximum length of the output queue. A zero +value is always interpreted as unbounded queue length. If different from zero, +the interpretation of this parameter depends on the current dequeuing policy +(see above): the "simple" policy will enforce a fixed queue size by returning +EAGAIN, whereas the "prio" policy enforces a fixed queue length by dropping the +lowest-priority packet first. The default value for this parameter is +initialised from /proc/sys/net/dccp/default/tx_qlen. DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of service codes (RFC 4340, sec. 8.1.2); if this socket option is not set, diff --git a/include/linux/dccp.h b/include/linux/dccp.h index eed52bcd35d0..010e2d87ed75 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -197,6 +197,21 @@ enum dccp_feature_numbers { DCCPF_MAX_CCID_SPECIFIC = 255, }; +/* DCCP socket control message types for cmsg */ +enum dccp_cmsg_type { + DCCP_SCM_PRIORITY = 1, + DCCP_SCM_QPOLICY_MAX = 0xFFFF, + /* ^-- Up to here reserved exclusively for qpolicy parameters */ + DCCP_SCM_MAX +}; + +/* DCCP priorities for outgoing/queued packets */ +enum dccp_packet_dequeueing_policy { + DCCPQ_POLICY_SIMPLE, + DCCPQ_POLICY_PRIO, + DCCPQ_POLICY_MAX +}; + /* DCCP socket options */ #define DCCP_SOCKOPT_PACKET_SIZE 1 /* XXX deprecated, without effect */ #define DCCP_SOCKOPT_SERVICE 2 @@ -210,6 +225,8 @@ enum dccp_feature_numbers { #define DCCP_SOCKOPT_CCID 13 #define DCCP_SOCKOPT_TX_CCID 14 #define DCCP_SOCKOPT_RX_CCID 15 +#define DCCP_SOCKOPT_QPOLICY_ID 16 +#define DCCP_SOCKOPT_QPOLICY_TXQLEN 17 #define DCCP_SOCKOPT_CCID_RX_INFO 128 #define DCCP_SOCKOPT_CCID_TX_INFO 192 @@ -458,6 +475,8 @@ struct dccp_ackvec; * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) * @dccps_hc_tx_ccid - CCID used for the sender (or sending half-connection) * @dccps_options_received - parsed set of retrieved options + * @dccps_qpolicy - TX dequeueing policy, one of %dccp_packet_dequeueing_policy + * @dccps_tx_qlen - maximum length of the TX queue * @dccps_role - role of this sock, one of %dccp_role * @dccps_hc_rx_insert_options - receiver wants to add options when acking * @dccps_hc_tx_insert_options - sender wants to add options when sending @@ -500,6 +519,8 @@ struct dccp_sock { struct ccid *dccps_hc_rx_ccid; struct ccid *dccps_hc_tx_ccid; struct dccp_options_received dccps_options_received; + __u8 dccps_qpolicy; + __u32 dccps_tx_qlen; enum dccp_role dccps_role:2; __u8 dccps_hc_rx_insert_options:1; __u8 dccps_hc_tx_insert_options:1; diff --git a/net/dccp/Makefile b/net/dccp/Makefile index b68440bd7fa2..0c1c9af2bf7e 100644 --- a/net/dccp/Makefile +++ b/net/dccp/Makefile @@ -1,7 +1,7 @@ obj-$(CONFIG_IP_DCCP) += dccp.o dccp_ipv4.o dccp-y := ccid.o feat.o input.o minisocks.o options.o \ - output.o proto.o timer.o ackvec.o + qpolicy.o output.o proto.o timer.o ackvec.o dccp_ipv4-y := ipv4.o diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 74c90cd27677..ce2dd6f6f34d 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -234,6 +234,18 @@ extern void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, extern void dccp_send_sync(struct sock *sk, const u64 seq, const enum dccp_pkt_type pkt_type); +/* + * TX Packet Dequeueing Interface + */ +extern void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb); +extern bool dccp_qpolicy_full(struct sock *sk); +extern void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb); +extern struct sk_buff *dccp_qpolicy_top(struct sock *sk); +extern struct sk_buff *dccp_qpolicy_pop(struct sock *sk); + +/* + * TX Packet Output and TX Timers + */ extern void dccp_write_xmit(struct sock *sk); extern void dccp_write_space(struct sock *sk); extern void dccp_flush_write_queue(struct sock *sk, long *time_budget); diff --git a/net/dccp/output.c b/net/dccp/output.c index b1eaf7bcfb11..2532797a8009 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -241,7 +241,7 @@ static void dccp_xmit_packet(struct sock *sk) { int err, len; struct dccp_sock *dp = dccp_sk(sk); - struct sk_buff *skb = skb_dequeue(&sk->sk_write_queue); + struct sk_buff *skb = dccp_qpolicy_pop(sk); if (unlikely(skb == NULL)) return; @@ -344,7 +344,7 @@ void dccp_write_xmit(struct sock *sk) struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; - while ((skb = skb_peek(&sk->sk_write_queue))) { + while ((skb = dccp_qpolicy_top(sk))) { int rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); switch (ccid_packet_dequeue_eval(rc)) { @@ -358,8 +358,7 @@ void dccp_write_xmit(struct sock *sk) dccp_xmit_packet(sk); break; case CCID_PACKET_ERR: - skb_dequeue(&sk->sk_write_queue); - kfree_skb(skb); + dccp_qpolicy_drop(sk, skb); dccp_pr_debug("packet discarded due to err=%d\n", rc); } } diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 8c125ffab1c5..b56efdd2a421 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -189,6 +189,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) dp->dccps_rate_last = jiffies; dp->dccps_role = DCCP_ROLE_UNDEFINED; dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; + dp->dccps_tx_qlen = sysctl_dccp_tx_qlen; dccp_init_xmit_timers(sk); @@ -541,6 +542,20 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, case DCCP_SOCKOPT_RECV_CSCOV: err = dccp_setsockopt_cscov(sk, val, true); break; + case DCCP_SOCKOPT_QPOLICY_ID: + if (sk->sk_state != DCCP_CLOSED) + err = -EISCONN; + else if (val < 0 || val >= DCCPQ_POLICY_MAX) + err = -EINVAL; + else + dp->dccps_qpolicy = val; + break; + case DCCP_SOCKOPT_QPOLICY_TXQLEN: + if (val < 0) + err = -EINVAL; + else + dp->dccps_tx_qlen = val; + break; default: err = -ENOPROTOOPT; break; @@ -648,6 +663,12 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, case DCCP_SOCKOPT_RECV_CSCOV: val = dp->dccps_pcrlen; break; + case DCCP_SOCKOPT_QPOLICY_ID: + val = dp->dccps_qpolicy; + break; + case DCCP_SOCKOPT_QPOLICY_TXQLEN: + val = dp->dccps_tx_qlen; + break; case 128 ... 191: return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, len, (u32 __user *)optval, optlen); @@ -690,6 +711,43 @@ int compat_dccp_getsockopt(struct sock *sk, int level, int optname, EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); #endif +static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) +{ + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); + + /* + * Assign an (opaque) qpolicy priority value to skb->priority. + * + * We are overloading this skb field for use with the qpolicy subystem. + * The skb->priority is normally used for the SO_PRIORITY option, which + * is initialised from sk_priority. Since the assignment of sk_priority + * to skb->priority happens later (on layer 3), we overload this field + * for use with queueing priorities as long as the skb is on layer 4. + * The default priority value (if nothing is set) is 0. + */ + skb->priority = 0; + + for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + + if (!CMSG_OK(msg, cmsg)) + return -EINVAL; + + if (cmsg->cmsg_level != SOL_DCCP) + continue; + + switch (cmsg->cmsg_type) { + case DCCP_SCM_PRIORITY: + if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32))) + return -EINVAL; + skb->priority = *(__u32 *)CMSG_DATA(cmsg); + break; + default: + return -EINVAL; + } + } + return 0; +} + int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { @@ -705,8 +763,7 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, lock_sock(sk); - if (sysctl_dccp_tx_qlen && - (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) { + if (dccp_qpolicy_full(sk)) { rc = -EAGAIN; goto out_release; } @@ -734,7 +791,11 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (rc != 0) goto out_discard; - skb_queue_tail(&sk->sk_write_queue, skb); + rc = dccp_msghdr_parse(msg, skb); + if (rc != 0) + goto out_discard; + + dccp_qpolicy_push(sk, skb); dccp_write_xmit(sk); out_release: release_sock(sk); diff --git a/net/dccp/qpolicy.c b/net/dccp/qpolicy.c new file mode 100644 index 000000000000..414696b0d830 --- /dev/null +++ b/net/dccp/qpolicy.c @@ -0,0 +1,126 @@ +/* + * net/dccp/qpolicy.c + * + * Policy-based packet dequeueing interface for DCCP. + * + * Copyright (c) 2008 Tomasz Grobelny + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License v2 + * as published by the Free Software Foundation. + */ +#include "dccp.h" + +/* + * Simple Dequeueing Policy: + * If tx_qlen is different from 0, enqueue up to tx_qlen elements. + */ +static void qpolicy_simple_push(struct sock *sk, struct sk_buff *skb) +{ + skb_queue_tail(&sk->sk_write_queue, skb); +} + +static bool qpolicy_simple_full(struct sock *sk) +{ + return dccp_sk(sk)->dccps_tx_qlen && + sk->sk_write_queue.qlen >= dccp_sk(sk)->dccps_tx_qlen; +} + +static struct sk_buff *qpolicy_simple_top(struct sock *sk) +{ + return skb_peek(&sk->sk_write_queue); +} + +/* + * Priority-based Dequeueing Policy: + * If tx_qlen is different from 0 and the queue has reached its upper bound + * of tx_qlen elements, replace older packets lowest-priority-first. + */ +static struct sk_buff *qpolicy_prio_best_skb(struct sock *sk) +{ + struct sk_buff *skb, *best = NULL; + + skb_queue_walk(&sk->sk_write_queue, skb) + if (best == NULL || skb->priority > best->priority) + best = skb; + return best; +} + +static struct sk_buff *qpolicy_prio_worst_skb(struct sock *sk) +{ + struct sk_buff *skb, *worst = NULL; + + skb_queue_walk(&sk->sk_write_queue, skb) + if (worst == NULL || skb->priority < worst->priority) + worst = skb; + return worst; +} + +static bool qpolicy_prio_full(struct sock *sk) +{ + if (qpolicy_simple_full(sk)) + dccp_qpolicy_drop(sk, qpolicy_prio_worst_skb(sk)); + return false; +} + +/** + * struct dccp_qpolicy_operations - TX Packet Dequeueing Interface + * @push: add a new @skb to the write queue + * @full: indicates that no more packets will be admitted + * @top: peeks at whatever the queueing policy defines as its `top' + */ +static struct dccp_qpolicy_operations { + void (*push) (struct sock *sk, struct sk_buff *skb); + bool (*full) (struct sock *sk); + struct sk_buff* (*top) (struct sock *sk); + +} qpol_table[DCCPQ_POLICY_MAX] = { + [DCCPQ_POLICY_SIMPLE] = { + .push = qpolicy_simple_push, + .full = qpolicy_simple_full, + .top = qpolicy_simple_top, + }, + [DCCPQ_POLICY_PRIO] = { + .push = qpolicy_simple_push, + .full = qpolicy_prio_full, + .top = qpolicy_prio_best_skb, + }, +}; + +/* + * Externally visible interface + */ +void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb) +{ + qpol_table[dccp_sk(sk)->dccps_qpolicy].push(sk, skb); +} + +bool dccp_qpolicy_full(struct sock *sk) +{ + return qpol_table[dccp_sk(sk)->dccps_qpolicy].full(sk); +} + +void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb) +{ + if (skb != NULL) { + skb_unlink(skb, &sk->sk_write_queue); + kfree_skb(skb); + } +} + +struct sk_buff *dccp_qpolicy_top(struct sock *sk) +{ + return qpol_table[dccp_sk(sk)->dccps_qpolicy].top(sk); +} + +struct sk_buff *dccp_qpolicy_pop(struct sock *sk) +{ + struct sk_buff *skb = dccp_qpolicy_top(sk); + + /* Clear any skb fields that we used internally */ + skb->priority = 0; + + if (skb) + skb_unlink(skb, &sk->sk_write_queue); + return skb; +} -- cgit v1.2.3 From 8c5eb880585a6fa278aa49553dd53a25e1ac319d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 3 Sep 2008 09:45:57 +0100 Subject: Use PCI_DEVICE_ID_88ALP01 for CAFÉ chip, rather than PCI_DEVICE_ID_CAFE. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probably better to use the official designation. Signed-off-by: David Woodhouse Acked-by: Pierre Ossman --- drivers/mmc/host/sdhci-pci.c | 2 +- include/linux/pci_ids.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index fcb14c2346cc..0341cfbd6fc4 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -326,7 +326,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = { { .vendor = PCI_VENDOR_ID_MARVELL, - .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, + .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = (kernel_ulong_t)&sdhci_cafe, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index f1624b396754..ef6ef64beb53 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1521,7 +1521,7 @@ #define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 #define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 -#define PCI_DEVICE_ID_MARVELL_CAFE_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 #define PCI_VENDOR_ID_V3 0x11b0 #define PCI_DEVICE_ID_V3_V960 0x0001 -- cgit v1.2.3 From 514fca4373d28522c0709fcdd439fc9e1e257bb6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 3 Sep 2008 09:47:17 +0100 Subject: [MTD] [NAND] Define and use PCI_DEVICE_ID_MARVELL_88ALP01_NAND for CAFÉ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 6 +++++- include/linux/pci_ids.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 95345d051579..b8064bf3aee4 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -1,6 +1,9 @@ /* * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 * + * The data sheet for this device can be found at: + * http://www.marvell.com/products/pcconn/88ALP01.jsp + * * Copyright © 2006 Red Hat, Inc. * Copyright © 2006 David Woodhouse */ @@ -842,7 +845,8 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) } static struct pci_device_id cafe_nand_tbl[] = { - { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, + PCI_ANY_ID, PCI_ANY_ID }, { } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ef6ef64beb53..e6240b7cb404 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1521,6 +1521,7 @@ #define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 #define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 #define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 #define PCI_VENDOR_ID_V3 0x11b0 -- cgit v1.2.3 From aa7a7fb3990ffc74945494cbd2fc6e920825ee2c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 3 Sep 2008 09:49:20 +0100 Subject: Define and use PCI_DEVICE_ID_MARVELL_88ALP01_CCIC for CAFÉ camera driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, stop looking at the NAND controller (0x4100) and checking the device class. For a while during development, all three functions on the chip had the same ID. We made them fix that fairly promptly, and we can forget about it now. Signed-off-by: David Woodhouse Acked-by: Jonathan Corbet --- drivers/media/video/cafe_ccic.c | 13 +++---------- include/linux/pci_ids.h | 1 + 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index c149b7d712e5..ea0db819184c 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -2091,15 +2091,8 @@ static int cafe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; - u16 classword; struct cafe_camera *cam; - /* - * Make sure we have a camera here - we'll get calls for - * the other cafe devices as well. - */ - pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword); - if (classword != PCI_CLASS_MULTIMEDIA_VIDEO) - return -ENODEV; + /* * Start putting together one of our big camera structures. */ @@ -2287,8 +2280,8 @@ static int cafe_pci_resume(struct pci_dev *pdev) static struct pci_device_id cafe_ids[] = { - { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ - { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, + PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, { 0, } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e6240b7cb404..6cf53f49b625 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1523,6 +1523,7 @@ #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 #define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 #define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 #define PCI_VENDOR_ID_V3 0x11b0 #define PCI_DEVICE_ID_V3_V960 0x0001 -- cgit v1.2.3 From 268364a0f48aee2f851f9d1ef8a6cda0f3039ef1 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 4 Sep 2008 21:02:44 +0200 Subject: IO resources: add reserve_region_with_split() add reserve_region_with_split() to not lose e820 reserved entries if they overlap with existing IO regions: with test case by extend 0xe0000000 - 0xeffffff to 0xdd800000 - we get: e0000000-efffffff : PCI MMCONFIG 0 e0000000-efffffff : reserved and in /proc/iomem we get: found conflict for reserved [dd800000, efffffff], try to reserve with split __reserve_region_with_split: (PCI Bus #80) [dd000000, ddffffff], res: (reserved) [dd800000, efffffff] __reserve_region_with_split: (PCI Bus #00) [de000000, dfffffff], res: (reserved) [de000000, efffffff] initcall pci_subsys_init+0x0/0x121 returned 0 after 381 msecs in dmesg various fixes and improvements suggested by Linus. Signed-off-by: Yinghai Lu Signed-off-by: Ingo Molnar --- include/linux/ioport.h | 3 +++ kernel/resource.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 8d3b7a9afd17..fded376b94e3 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -108,6 +108,9 @@ extern struct resource iomem_resource; extern int request_resource(struct resource *root, struct resource *new); extern int release_resource(struct resource *new); +extern void reserve_region_with_split(struct resource *root, + resource_size_t start, resource_size_t end, + const char *name); extern int insert_resource(struct resource *parent, struct resource *new); extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new); extern int allocate_resource(struct resource *root, struct resource *new, diff --git a/kernel/resource.c b/kernel/resource.c index 03d796c1b2e9..414d6fc9131e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -516,6 +516,74 @@ int adjust_resource(struct resource *res, resource_size_t start, resource_size_t return result; } +static void __init __reserve_region_with_split(struct resource *root, + resource_size_t start, resource_size_t end, + const char *name) +{ + struct resource *parent = root; + struct resource *conflict; + struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); + + if (!res) + return; + + res->name = name; + res->start = start; + res->end = end; + res->flags = IORESOURCE_BUSY; + + for (;;) { + conflict = __request_resource(parent, res); + if (!conflict) + break; + if (conflict != parent) { + parent = conflict; + if (!(conflict->flags & IORESOURCE_BUSY)) + continue; + } + + /* Uhhuh, that didn't work out.. */ + kfree(res); + res = NULL; + break; + } + + if (!res) { + printk(KERN_DEBUG " __reserve_region_with_split: (%s) [%llx, %llx], res: (%s) [%llx, %llx]\n", + conflict->name, conflict->start, conflict->end, + name, start, end); + + /* failed, split and try again */ + + /* conflict coverred whole area */ + if (conflict->start <= start && conflict->end >= end) + return; + + if (conflict->start > start) + __reserve_region_with_split(root, start, conflict->start-1, name); + if (!(conflict->flags & IORESOURCE_BUSY)) { + resource_size_t common_start, common_end; + + common_start = max(conflict->start, start); + common_end = min(conflict->end, end); + if (common_start < common_end) + __reserve_region_with_split(root, common_start, common_end, name); + } + if (conflict->end < end) + __reserve_region_with_split(root, conflict->end+1, end, name); + } + +} + +void reserve_region_with_split(struct resource *root, + resource_size_t start, resource_size_t end, + const char *name) +{ + write_lock(&resource_lock); + __reserve_region_with_split(root, start, end, name); + write_unlock(&resource_lock); +} + EXPORT_SYMBOL(adjust_resource); /** -- cgit v1.2.3 From f7981c1c67b53abb4a7d8a501e68585b9826179a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 26 Aug 2008 10:23:22 +0200 Subject: mv643xx_eth: require contiguous receive and transmit queue numbering Simplify receive and transmit queue handling by requiring the set of queue numbers to be contiguous starting from zero. Signed-off-by: Lennert Buytenhek --- drivers/net/mv643xx_eth.c | 123 ++++++++++++++++---------------------------- include/linux/mv643xx_eth.h | 6 +-- 2 files changed, 47 insertions(+), 82 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 53cfd01b405d..c41541d8710f 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -360,8 +360,7 @@ struct mv643xx_eth_private { int default_rx_ring_size; unsigned long rx_desc_sram_addr; int rx_desc_sram_size; - u8 rxq_mask; - int rxq_primary; + int rxq_count; struct napi_struct napi; struct timer_list rx_oom; struct rx_queue rxq[8]; @@ -372,8 +371,7 @@ struct mv643xx_eth_private { int default_tx_ring_size; unsigned long tx_desc_sram_addr; int tx_desc_sram_size; - u8 txq_mask; - int txq_primary; + int txq_count; struct tx_queue txq[8]; #ifdef MV643XX_ETH_TX_FAST_REFILL int tx_clean_threshold; @@ -455,7 +453,7 @@ static void __txq_maybe_wake(struct tx_queue *txq) * netif_{stop,wake}_queue() flow control only applies to * the primary queue. */ - BUG_ON(txq->index != mp->txq_primary); + BUG_ON(txq->index != 0); if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1) netif_wake_queue(mp->dev); @@ -626,13 +624,12 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) #ifdef MV643XX_ETH_TX_FAST_REFILL if (++mp->tx_clean_threshold > 5) { mp->tx_clean_threshold = 0; - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_reclaim(mp->txq + i, 0); + for (i = 0; i < mp->txq_count; i++) + txq_reclaim(mp->txq + i, 0); if (netif_carrier_ok(mp->dev)) { spin_lock_irq(&mp->lock); - __txq_maybe_wake(mp->txq + mp->txq_primary); + __txq_maybe_wake(mp->txq); spin_unlock_irq(&mp->lock); } } @@ -640,13 +637,11 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) work_done = 0; oom = 0; - for (i = 7; work_done < budget && i >= 0; i--) { - if (mp->rxq_mask & (1 << i)) { - struct rx_queue *rxq = mp->rxq + i; + for (i = mp->rxq_count - 1; work_done < budget && i >= 0; i--) { + struct rx_queue *rxq = mp->rxq + i; - work_done += rxq_process(rxq, budget - work_done); - work_done += rxq_refill(rxq, budget - work_done, &oom); - } + work_done += rxq_process(rxq, budget - work_done); + work_done += rxq_refill(rxq, budget - work_done, &oom); } if (work_done < budget) { @@ -846,11 +841,11 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&mp->lock, flags); - txq = mp->txq + mp->txq_primary; + txq = mp->txq; if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { spin_unlock_irqrestore(&mp->lock, flags); - if (txq->index == mp->txq_primary && net_ratelimit()) + if (txq->index == 0 && net_ratelimit()) dev_printk(KERN_ERR, &dev->dev, "primary tx queue full?!\n"); kfree_skb(skb); @@ -862,7 +857,7 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_packets++; dev->trans_start = jiffies; - if (txq->index == mp->txq_primary) { + if (txq->index == 0) { int entries_left; entries_left = txq->tx_ring_size - txq->tx_desc_count; @@ -1517,7 +1512,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) size = rxq->rx_ring_size * sizeof(struct rx_desc); - if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) { + if (index == 0 && size <= mp->rx_desc_sram_size) { rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr, mp->rx_desc_sram_size); rxq->rx_desc_dma = mp->rx_desc_sram_addr; @@ -1559,7 +1554,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) out_free: - if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) + if (index == 0 && size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else dma_free_coherent(NULL, size, @@ -1590,7 +1585,7 @@ static void rxq_deinit(struct rx_queue *rxq) rxq->rx_desc_count); } - if (rxq->index == mp->rxq_primary && + if (rxq->index == 0 && rxq->rx_desc_area_size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else @@ -1617,7 +1612,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) size = txq->tx_ring_size * sizeof(struct tx_desc); - if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) { + if (index == 0 && size <= mp->tx_desc_sram_size) { txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr, mp->tx_desc_sram_size); txq->tx_desc_dma = mp->tx_desc_sram_addr; @@ -1661,7 +1656,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) out_free: - if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) + if (index == 0 && size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else dma_free_coherent(NULL, size, @@ -1738,7 +1733,7 @@ static void txq_deinit(struct tx_queue *txq) BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); - if (txq->index == mp->txq_primary && + if (txq->index == 0 && txq->tx_desc_area_size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else @@ -1768,13 +1763,11 @@ static void handle_link_event(struct mv643xx_eth_private *mp) netif_carrier_off(dev); netif_stop_queue(dev); - for (i = 0; i < 8; i++) { + for (i = 0; i < mp->txq_count; i++) { struct tx_queue *txq = mp->txq + i; - if (mp->txq_mask & (1 << i)) { - txq_reclaim(txq, 1); - txq_reset_hw_ptr(txq); - } + txq_reclaim(txq, 1); + txq_reset_hw_ptr(txq); } } return; @@ -1847,9 +1840,8 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) if (int_cause_ext & INT_EXT_TX) { int i; - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_reclaim(mp->txq + i, 0); + for (i = 0; i < mp->txq_count; i++) + txq_reclaim(mp->txq + i, 0); /* * Enough space again in the primary TX queue for a @@ -1857,7 +1849,7 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) */ if (netif_carrier_ok(dev)) { spin_lock(&mp->lock); - __txq_maybe_wake(mp->txq + mp->txq_primary); + __txq_maybe_wake(mp->txq); spin_unlock(&mp->lock); } } @@ -1945,12 +1937,9 @@ static void port_start(struct mv643xx_eth_private *mp) * Configure TX path and queues. */ tx_set_rate(mp, 1000000000, 16777216); - for (i = 0; i < 8; i++) { + for (i = 0; i < mp->txq_count; i++) { struct tx_queue *txq = mp->txq + i; - if ((mp->txq_mask & (1 << i)) == 0) - continue; - txq_reset_hw_ptr(txq); txq_set_rate(txq, 1000000000, 16777216); txq_set_fixed_prio_mode(txq); @@ -1975,14 +1964,11 @@ static void port_start(struct mv643xx_eth_private *mp) /* * Enable the receive queues. */ - for (i = 0; i < 8; i++) { + for (i = 0; i < mp->rxq_count; i++) { struct rx_queue *rxq = mp->rxq + i; int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i); u32 addr; - if ((mp->rxq_mask & (1 << i)) == 0) - continue; - addr = (u32)rxq->rx_desc_dma; addr += rxq->rx_curr_desc * sizeof(struct rx_desc); wrl(mp, off, addr); @@ -2044,15 +2030,11 @@ static int mv643xx_eth_open(struct net_device *dev) napi_enable(&mp->napi); oom = 0; - for (i = 0; i < 8; i++) { - if ((mp->rxq_mask & (1 << i)) == 0) - continue; - + for (i = 0; i < mp->rxq_count; i++) { err = rxq_init(mp, i); if (err) { while (--i >= 0) - if (mp->rxq_mask & (1 << i)) - rxq_deinit(mp->rxq + i); + rxq_deinit(mp->rxq + i); goto out; } @@ -2064,15 +2046,11 @@ static int mv643xx_eth_open(struct net_device *dev) add_timer(&mp->rx_oom); } - for (i = 0; i < 8; i++) { - if ((mp->txq_mask & (1 << i)) == 0) - continue; - + for (i = 0; i < mp->txq_count; i++) { err = txq_init(mp, i); if (err) { while (--i >= 0) - if (mp->txq_mask & (1 << i)) - txq_deinit(mp->txq + i); + txq_deinit(mp->txq + i); goto out_free; } } @@ -2094,9 +2072,8 @@ static int mv643xx_eth_open(struct net_device *dev) out_free: - for (i = 0; i < 8; i++) - if (mp->rxq_mask & (1 << i)) - rxq_deinit(mp->rxq + i); + for (i = 0; i < mp->rxq_count; i++) + rxq_deinit(mp->rxq + i); out: free_irq(dev->irq, dev); @@ -2108,12 +2085,10 @@ static void port_reset(struct mv643xx_eth_private *mp) unsigned int data; int i; - for (i = 0; i < 8; i++) { - if (mp->rxq_mask & (1 << i)) - rxq_disable(mp->rxq + i); - if (mp->txq_mask & (1 << i)) - txq_disable(mp->txq + i); - } + for (i = 0; i < mp->rxq_count; i++) + rxq_disable(mp->rxq + i); + for (i = 0; i < mp->txq_count; i++) + txq_disable(mp->txq + i); while (1) { u32 ps = rdl(mp, PORT_STATUS(mp->port_num)); @@ -2151,12 +2126,10 @@ static int mv643xx_eth_stop(struct net_device *dev) port_reset(mp); mib_counters_update(mp); - for (i = 0; i < 8; i++) { - if (mp->rxq_mask & (1 << i)) - rxq_deinit(mp->rxq + i); - if (mp->txq_mask & (1 << i)) - txq_deinit(mp->txq + i); - } + for (i = 0; i < mp->rxq_count; i++) + rxq_deinit(mp->rxq + i); + for (i = 0; i < mp->txq_count; i++) + txq_deinit(mp->txq + i); return 0; } @@ -2211,7 +2184,7 @@ static void tx_timeout_task(struct work_struct *ugly) port_reset(mp); port_start(mp); - __txq_maybe_wake(mp->txq + mp->txq_primary); + __txq_maybe_wake(mp->txq); } } @@ -2453,11 +2426,7 @@ static void set_params(struct mv643xx_eth_private *mp, mp->rx_desc_sram_addr = pd->rx_sram_addr; mp->rx_desc_sram_size = pd->rx_sram_size; - if (pd->rx_queue_mask) - mp->rxq_mask = pd->rx_queue_mask; - else - mp->rxq_mask = 0x01; - mp->rxq_primary = fls(mp->rxq_mask) - 1; + mp->rxq_count = pd->rx_queue_count ? : 1; mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; if (pd->tx_queue_size) @@ -2465,11 +2434,7 @@ static void set_params(struct mv643xx_eth_private *mp, mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_size = pd->tx_sram_size; - if (pd->tx_queue_mask) - mp->txq_mask = pd->tx_queue_mask; - else - mp->txq_mask = 0x01; - mp->txq_primary = fls(mp->txq_mask) - 1; + mp->txq_count = pd->tx_queue_count ? : 1; } static int phy_detect(struct mv643xx_eth_private *mp) diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 12078577aef6..eb78b00edcde 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -49,10 +49,10 @@ struct mv643xx_eth_platform_data { int duplex; /* - * Which RX/TX queues to use. + * How many RX/TX queues to use. */ - int rx_queue_mask; - int tx_queue_mask; + int rx_queue_count; + int tx_queue_count; /* * Override default RX/TX queue sizes if nonzero. -- cgit v1.2.3 From fc0eb9f226d8ecc8e3b563bf808bd6d61a6153a1 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 26 Aug 2008 12:56:56 +0200 Subject: mv643xx_eth: smi sharing is a per-unit property, not a per-port one Which top-level unit's SMI interface to use should be a property of the top-level unit, not of the individual ports. This patch moves the ->shared_smi pointer from the per-port platform data to the global platform data. Signed-off-by: Lennert Buytenhek --- arch/arm/mach-mv78xx0/common.c | 6 +++--- drivers/net/mv643xx_eth.c | 19 +++++++++++-------- include/linux/mv643xx_eth.h | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c index 953a26c469cb..5842d3bb02b2 100644 --- a/arch/arm/mach-mv78xx0/common.c +++ b/arch/arm/mach-mv78xx0/common.c @@ -330,6 +330,7 @@ void __init mv78xx0_ge00_init(struct mv643xx_eth_platform_data *eth_data) struct mv643xx_eth_shared_platform_data mv78xx0_ge01_shared_data = { .t_clk = 0, .dram = &mv78xx0_mbus_dram_info, + .shared_smi = &mv78xx0_ge00_shared, }; static struct resource mv78xx0_ge01_shared_resources[] = { @@ -370,7 +371,6 @@ static struct platform_device mv78xx0_ge01 = { void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data) { eth_data->shared = &mv78xx0_ge01_shared; - eth_data->shared_smi = &mv78xx0_ge00_shared; mv78xx0_ge01.dev.platform_data = eth_data; platform_device_register(&mv78xx0_ge01_shared); @@ -384,6 +384,7 @@ void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data) struct mv643xx_eth_shared_platform_data mv78xx0_ge10_shared_data = { .t_clk = 0, .dram = &mv78xx0_mbus_dram_info, + .shared_smi = &mv78xx0_ge00_shared, }; static struct resource mv78xx0_ge10_shared_resources[] = { @@ -424,7 +425,6 @@ static struct platform_device mv78xx0_ge10 = { void __init mv78xx0_ge10_init(struct mv643xx_eth_platform_data *eth_data) { eth_data->shared = &mv78xx0_ge10_shared; - eth_data->shared_smi = &mv78xx0_ge00_shared; mv78xx0_ge10.dev.platform_data = eth_data; platform_device_register(&mv78xx0_ge10_shared); @@ -438,6 +438,7 @@ void __init mv78xx0_ge10_init(struct mv643xx_eth_platform_data *eth_data) struct mv643xx_eth_shared_platform_data mv78xx0_ge11_shared_data = { .t_clk = 0, .dram = &mv78xx0_mbus_dram_info, + .shared_smi = &mv78xx0_ge00_shared, }; static struct resource mv78xx0_ge11_shared_resources[] = { @@ -478,7 +479,6 @@ static struct platform_device mv78xx0_ge11 = { void __init mv78xx0_ge11_init(struct mv643xx_eth_platform_data *eth_data) { eth_data->shared = &mv78xx0_ge11_shared; - eth_data->shared_smi = &mv78xx0_ge00_shared; mv78xx0_ge11.dev.platform_data = eth_data; platform_device_register(&mv78xx0_ge11_shared); diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index c41541d8710f..6d3da78b7ad5 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -247,6 +247,11 @@ struct mv643xx_eth_shared_private { */ void __iomem *base; + /* + * Points at the right SMI instance to use. + */ + struct mv643xx_eth_shared_private *smi; + /* * Protects access to SMI_REG, which is shared between ports. */ @@ -345,7 +350,6 @@ struct mv643xx_eth_private { struct net_device *dev; - struct mv643xx_eth_shared_private *shared_smi; int phy_addr; spinlock_t lock; @@ -1015,7 +1019,7 @@ static int smi_wait_ready(struct mv643xx_eth_shared_private *msp) static int smi_reg_read(struct mv643xx_eth_private *mp, unsigned int addr, unsigned int reg) { - struct mv643xx_eth_shared_private *msp = mp->shared_smi; + struct mv643xx_eth_shared_private *msp = mp->shared->smi; void __iomem *smi_reg = msp->base + SMI_REG; int ret; @@ -1053,7 +1057,7 @@ out: static int smi_reg_write(struct mv643xx_eth_private *mp, unsigned int addr, unsigned int reg, unsigned int value) { - struct mv643xx_eth_shared_private *msp = mp->shared_smi; + struct mv643xx_eth_shared_private *msp = mp->shared->smi; void __iomem *smi_reg = msp->base + SMI_REG; mutex_lock(&msp->phy_lock); @@ -2311,6 +2315,10 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (msp->base == NULL) goto out_free; + msp->smi = msp; + if (pd != NULL && pd->shared_smi != NULL) + msp->smi = platform_get_drvdata(pd->shared_smi); + mutex_init(&msp->phy_lock); msp->err_interrupt = NO_IRQ; @@ -2405,13 +2413,8 @@ static void set_params(struct mv643xx_eth_private *mp, uc_addr_get(mp, dev->dev_addr); if (pd->phy_addr == -1) { - mp->shared_smi = NULL; mp->phy_addr = -1; } else { - mp->shared_smi = mp->shared; - if (pd->shared_smi != NULL) - mp->shared_smi = platform_get_drvdata(pd->shared_smi); - if (pd->force_phy_addr || pd->phy_addr) { mp->phy_addr = pd->phy_addr & 0x3f; phy_addr_set(mp, mp->phy_addr); diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index eb78b00edcde..12339eb65704 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -17,6 +17,7 @@ struct mv643xx_eth_shared_platform_data { struct mbus_dram_target_info *dram; + struct platform_device *shared_smi; unsigned int t_clk; }; @@ -30,7 +31,6 @@ struct mv643xx_eth_platform_data { /* * Whether a PHY is present, and if yes, at which address. */ - struct platform_device *shared_smi; int force_phy_addr; int phy_addr; -- cgit v1.2.3 From ac840605f3b1d9b99e1e6629a54994f8e003ff91 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 26 Aug 2008 14:06:47 +0200 Subject: mv643xx_eth: remove force_phy_addr field Currently, there are two different fields in the mv643xx_eth_platform_data struct that together describe the PHY address -- one field (phy_addr) has the address of the PHY, but if that address is zero, a second field (force_phy_addr) needs to be set to distinguish the actual address zero from a zero due to not having filled in the PHY address explicitly (which should mean 'use the default PHY address'). If we are a bit smarter about the encoding of the phy_addr field, we can avoid the need for a second field -- this patch does that. Signed-off-by: Lennert Buytenhek --- arch/arm/mach-kirkwood/db88f6281-bp-setup.c | 2 +- arch/arm/mach-kirkwood/rd88f6192-nas-setup.c | 2 +- arch/arm/mach-kirkwood/rd88f6281-setup.c | 2 +- arch/arm/mach-loki/lb88rc8480-setup.c | 2 +- arch/arm/mach-mv78xx0/db78x00-bp-setup.c | 8 ++++---- arch/arm/mach-orion5x/db88f5281-setup.c | 2 +- arch/arm/mach-orion5x/dns323-setup.c | 2 +- arch/arm/mach-orion5x/kurobox_pro-setup.c | 2 +- arch/arm/mach-orion5x/mss2-setup.c | 2 +- arch/arm/mach-orion5x/mv2120-setup.c | 2 +- arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c | 2 +- arch/arm/mach-orion5x/rd88f5181l-ge-setup.c | 2 +- arch/arm/mach-orion5x/rd88f5182-setup.c | 2 +- arch/arm/mach-orion5x/ts78xx-setup.c | 3 +-- arch/arm/mach-orion5x/tsx09-common.c | 2 +- arch/arm/mach-orion5x/wnr854t-setup.c | 2 +- arch/arm/mach-orion5x/wrt350n-v2-setup.c | 2 +- arch/powerpc/sysdev/mv64x60_dev.c | 6 ++---- drivers/net/mv643xx_eth.c | 4 ++-- include/linux/mv643xx_eth.h | 5 ++++- 20 files changed, 28 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-kirkwood/db88f6281-bp-setup.c b/arch/arm/mach-kirkwood/db88f6281-bp-setup.c index 610fb24d8ae2..cd317795f355 100644 --- a/arch/arm/mach-kirkwood/db88f6281-bp-setup.c +++ b/arch/arm/mach-kirkwood/db88f6281-bp-setup.c @@ -25,7 +25,7 @@ #include "common.h" static struct mv643xx_eth_platform_data db88f6281_ge00_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; static struct mv_sata_platform_data db88f6281_sata_data = { diff --git a/arch/arm/mach-kirkwood/rd88f6192-nas-setup.c b/arch/arm/mach-kirkwood/rd88f6192-nas-setup.c index a3012d445971..b1d1a87a6821 100644 --- a/arch/arm/mach-kirkwood/rd88f6192-nas-setup.c +++ b/arch/arm/mach-kirkwood/rd88f6192-nas-setup.c @@ -30,7 +30,7 @@ #define RD88F6192_GPIO_USB_VBUS 10 static struct mv643xx_eth_platform_data rd88f6192_ge00_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; static struct mv_sata_platform_data rd88f6192_sata_data = { diff --git a/arch/arm/mach-kirkwood/rd88f6281-setup.c b/arch/arm/mach-kirkwood/rd88f6281-setup.c index d96487a0f18b..b6416615c0b9 100644 --- a/arch/arm/mach-kirkwood/rd88f6281-setup.c +++ b/arch/arm/mach-kirkwood/rd88f6281-setup.c @@ -69,7 +69,7 @@ static struct platform_device rd88f6281_nand_flash = { }; static struct mv643xx_eth_platform_data rd88f6281_ge00_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, .speed = SPEED_1000, .duplex = DUPLEX_FULL, }; diff --git a/arch/arm/mach-loki/lb88rc8480-setup.c b/arch/arm/mach-loki/lb88rc8480-setup.c index 2cc9ac9b488f..85f9c1296aa0 100644 --- a/arch/arm/mach-loki/lb88rc8480-setup.c +++ b/arch/arm/mach-loki/lb88rc8480-setup.c @@ -67,7 +67,7 @@ static struct platform_device lb88rc8480_boot_flash = { }; static struct mv643xx_eth_platform_data lb88rc8480_ge0_data = { - .phy_addr = 1, + .phy_addr = MV643XX_ETH_PHY_ADDR(1), .mac_addr = { 0x00, 0x50, 0x43, 0x11, 0x22, 0x33 }, }; diff --git a/arch/arm/mach-mv78xx0/db78x00-bp-setup.c b/arch/arm/mach-mv78xx0/db78x00-bp-setup.c index a2d0c9783604..49f434c39eb7 100644 --- a/arch/arm/mach-mv78xx0/db78x00-bp-setup.c +++ b/arch/arm/mach-mv78xx0/db78x00-bp-setup.c @@ -19,19 +19,19 @@ #include "common.h" static struct mv643xx_eth_platform_data db78x00_ge00_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; static struct mv643xx_eth_platform_data db78x00_ge01_data = { - .phy_addr = 9, + .phy_addr = MV643XX_ETH_PHY_ADDR(9), }; static struct mv643xx_eth_platform_data db78x00_ge10_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, }; static struct mv643xx_eth_platform_data db78x00_ge11_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, }; static struct mv_sata_platform_data db78x00_sata_data = { diff --git a/arch/arm/mach-orion5x/db88f5281-setup.c b/arch/arm/mach-orion5x/db88f5281-setup.c index ff13e9060b18..d318bea2af91 100644 --- a/arch/arm/mach-orion5x/db88f5281-setup.c +++ b/arch/arm/mach-orion5x/db88f5281-setup.c @@ -285,7 +285,7 @@ subsys_initcall(db88f5281_pci_init); * Ethernet ****************************************************************************/ static struct mv643xx_eth_platform_data db88f5281_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; /***************************************************************************** diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c index b38c65ccfb15..3e66098340a5 100644 --- a/arch/arm/mach-orion5x/dns323-setup.c +++ b/arch/arm/mach-orion5x/dns323-setup.c @@ -79,7 +79,7 @@ subsys_initcall(dns323_pci_init); */ static struct mv643xx_eth_platform_data dns323_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; /**************************************************************************** diff --git a/arch/arm/mach-orion5x/kurobox_pro-setup.c b/arch/arm/mach-orion5x/kurobox_pro-setup.c index e321ec331839..610f2a6297f8 100644 --- a/arch/arm/mach-orion5x/kurobox_pro-setup.c +++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c @@ -161,7 +161,7 @@ subsys_initcall(kurobox_pro_pci_init); ****************************************************************************/ static struct mv643xx_eth_platform_data kurobox_pro_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; /***************************************************************************** diff --git a/arch/arm/mach-orion5x/mss2-setup.c b/arch/arm/mach-orion5x/mss2-setup.c index 53ff1893b883..68acca98e638 100644 --- a/arch/arm/mach-orion5x/mss2-setup.c +++ b/arch/arm/mach-orion5x/mss2-setup.c @@ -109,7 +109,7 @@ subsys_initcall(mss2_pci_init); ****************************************************************************/ static struct mv643xx_eth_platform_data mss2_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; /***************************************************************************** diff --git a/arch/arm/mach-orion5x/mv2120-setup.c b/arch/arm/mach-orion5x/mv2120-setup.c index 978d4d599396..97c9ccb2ac60 100644 --- a/arch/arm/mach-orion5x/mv2120-setup.c +++ b/arch/arm/mach-orion5x/mv2120-setup.c @@ -39,7 +39,7 @@ * Ethernet ****************************************************************************/ static struct mv643xx_eth_platform_data mv2120_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; static struct mv_sata_platform_data mv2120_sata_data = { diff --git a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c index e72fe1e065e8..500cdadaf09c 100644 --- a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c +++ b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c @@ -88,7 +88,7 @@ static struct orion5x_mpp_mode rd88f5181l_fxo_mpp_modes[] __initdata = { }; static struct mv643xx_eth_platform_data rd88f5181l_fxo_eth_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, .speed = SPEED_1000, .duplex = DUPLEX_FULL, }; diff --git a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c index a1fe3257320d..ebde81416499 100644 --- a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c +++ b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c @@ -89,7 +89,7 @@ static struct orion5x_mpp_mode rd88f5181l_ge_mpp_modes[] __initdata = { }; static struct mv643xx_eth_platform_data rd88f5181l_ge_eth_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, .speed = SPEED_1000, .duplex = DUPLEX_FULL, }; diff --git a/arch/arm/mach-orion5x/rd88f5182-setup.c b/arch/arm/mach-orion5x/rd88f5182-setup.c index 4c3bcd76ac85..a04f9e4b633a 100644 --- a/arch/arm/mach-orion5x/rd88f5182-setup.c +++ b/arch/arm/mach-orion5x/rd88f5182-setup.c @@ -221,7 +221,7 @@ subsys_initcall(rd88f5182_pci_init); ****************************************************************************/ static struct mv643xx_eth_platform_data rd88f5182_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; /***************************************************************************** diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c index ae0a5dccd2a1..1368e9fd1a06 100644 --- a/arch/arm/mach-orion5x/ts78xx-setup.c +++ b/arch/arm/mach-orion5x/ts78xx-setup.c @@ -103,8 +103,7 @@ static struct platform_device ts78xx_nor_boot_flash = { * Ethernet ****************************************************************************/ static struct mv643xx_eth_platform_data ts78xx_eth_data = { - .phy_addr = 0, - .force_phy_addr = 1, + .phy_addr = MV643XX_ETH_PHY_ADDR(0), }; /***************************************************************************** diff --git a/arch/arm/mach-orion5x/tsx09-common.c b/arch/arm/mach-orion5x/tsx09-common.c index 83feac3147a6..19cde24fbfdf 100644 --- a/arch/arm/mach-orion5x/tsx09-common.c +++ b/arch/arm/mach-orion5x/tsx09-common.c @@ -48,7 +48,7 @@ void qnap_tsx09_power_off(void) ****************************************************************************/ struct mv643xx_eth_platform_data qnap_tsx09_eth_data = { - .phy_addr = 8, + .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; static int __init qnap_tsx09_parse_hex_nibble(char n) diff --git a/arch/arm/mach-orion5x/wnr854t-setup.c b/arch/arm/mach-orion5x/wnr854t-setup.c index b6bc43e07eed..7ddc22c2bb54 100644 --- a/arch/arm/mach-orion5x/wnr854t-setup.c +++ b/arch/arm/mach-orion5x/wnr854t-setup.c @@ -92,7 +92,7 @@ static struct platform_device wnr854t_nor_flash = { }; static struct mv643xx_eth_platform_data wnr854t_eth_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, .speed = SPEED_1000, .duplex = DUPLEX_FULL, }; diff --git a/arch/arm/mach-orion5x/wrt350n-v2-setup.c b/arch/arm/mach-orion5x/wrt350n-v2-setup.c index b10da17b3fbd..9a4fd5256462 100644 --- a/arch/arm/mach-orion5x/wrt350n-v2-setup.c +++ b/arch/arm/mach-orion5x/wrt350n-v2-setup.c @@ -100,7 +100,7 @@ static struct platform_device wrt350n_v2_nor_flash = { }; static struct mv643xx_eth_platform_data wrt350n_v2_eth_data = { - .phy_addr = -1, + .phy_addr = MV643XX_ETH_PHY_NONE, .speed = SPEED_1000, .duplex = DUPLEX_FULL, }; diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c index 32e0ad0ebea8..b6bd775d2e22 100644 --- a/arch/powerpc/sysdev/mv64x60_dev.c +++ b/arch/powerpc/sysdev/mv64x60_dev.c @@ -293,10 +293,8 @@ static int __init mv64x60_eth_device_setup(struct device_node *np, int id, return -ENODEV; prop = of_get_property(phy, "reg", NULL); - if (prop) { - pdata.force_phy_addr = 1; - pdata.phy_addr = *prop; - } + if (prop) + pdata.phy_addr = MV643XX_ETH_PHY_ADDR(*prop); of_node_put(phy); diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 6d3da78b7ad5..b4850cf2a8ce 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -2412,10 +2412,10 @@ static void set_params(struct mv643xx_eth_private *mp, else uc_addr_get(mp, dev->dev_addr); - if (pd->phy_addr == -1) { + if (pd->phy_addr == MV643XX_ETH_PHY_NONE) { mp->phy_addr = -1; } else { - if (pd->force_phy_addr || pd->phy_addr) { + if (pd->phy_addr != MV643XX_ETH_PHY_ADDR_DEFAULT) { mp->phy_addr = pd->phy_addr & 0x3f; phy_addr_set(mp, mp->phy_addr); } else { diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 12339eb65704..cbbbe9bfecad 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -21,6 +21,10 @@ struct mv643xx_eth_shared_platform_data { unsigned int t_clk; }; +#define MV643XX_ETH_PHY_ADDR_DEFAULT 0 +#define MV643XX_ETH_PHY_ADDR(x) (0x80 | (x)) +#define MV643XX_ETH_PHY_NONE 0xff + struct mv643xx_eth_platform_data { /* * Pointer back to our parent instance, and our port number. @@ -31,7 +35,6 @@ struct mv643xx_eth_platform_data { /* * Whether a PHY is present, and if yes, at which address. */ - int force_phy_addr; int phy_addr; /* -- cgit v1.2.3 From 7c1e76897492d92b6a1c2d6892494d39ded9680c Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Wed, 3 Sep 2008 21:36:50 +0000 Subject: clockevents: prevent clockevent event_handler ending up handler_noop There is a ordering related problem with clockevents code, due to which clockevents_register_device() called after tickless/highres switch will not work. The new clockevent ends up with clockevents_handle_noop as event handler, resulting in no timer activity. The problematic path seems to be * old device already has hrtimer_interrupt as the event_handler * new clockevent device registers with a higher rating * tick_check_new_device() is called * clockevents_exchange_device() gets called * old->event_handler is set to clockevents_handle_noop * tick_setup_device() is called for the new device * which sets new->event_handler using the old->event_handler which is noop. Change the ordering so that new device inherits the proper handler. This does not have any issue in normal case as most likely all the clockevent devices are setup before the highres switch. But, can potentially be affecting some corner case where HPET force detect happens after the highres switch. This was a problem with HPET in MSI mode code that we have been experimenting with. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Shaohua Li Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar --- include/linux/clockchips.h | 2 ++ kernel/time/clockevents.c | 3 +-- kernel/time/tick-common.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index c33b0dc28e4d..ed3a5d473e52 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -127,6 +127,8 @@ extern int clockevents_register_notifier(struct notifier_block *nb); extern int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, ktime_t now); +extern void clockevents_handle_noop(struct clock_event_device *dev); + #ifdef CONFIG_GENERIC_CLOCKEVENTS extern void clockevents_notify(unsigned long reason, void *arg); #else diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 3d1e3e1a1971..1876b526c778 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -177,7 +177,7 @@ void clockevents_register_device(struct clock_event_device *dev) /* * Noop handler when we shut down an event device */ -static void clockevents_handle_noop(struct clock_event_device *dev) +void clockevents_handle_noop(struct clock_event_device *dev) { } @@ -199,7 +199,6 @@ void clockevents_exchange_device(struct clock_event_device *old, * released list and do a notify add later. */ if (old) { - old->event_handler = clockevents_handle_noop; clockevents_set_mode(old, CLOCK_EVT_MODE_UNUSED); list_del(&old->list); list_add(&old->list, &clockevents_released); diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 80c4336f4188..c4777193d567 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -161,6 +161,7 @@ static void tick_setup_device(struct tick_device *td, } else { handler = td->evtdev->event_handler; next_event = td->evtdev->next_event; + td->evtdev->event_handler = clockevents_handle_noop; } td->evtdev = newdev; -- cgit v1.2.3 From afbc8d8e72daa5a5faf6a0242186bdfcc42b2427 Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Thu, 4 Sep 2008 23:11:01 -0700 Subject: Fix conditional export of kvh.h and a.out.h to userspace. Some architectures have moved the asm/ into arch/ and some have not. This patch checks for a.out.h and kvh.h in both places before exporting the corresponding file from linux/ [dwmw2: simplified a little] Signed-off-by: Khem Raj Signed-off-by: David Woodhouse --- include/asm-generic/Kbuild.asm | 6 ++++-- include/linux/Kbuild | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index 1170dc60e638..1870d5e05f1c 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -1,8 +1,10 @@ -ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/kvm.h),) +ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm.h \ + $(srctree)/include/asm-$(SRCARCH)/kvm.h),) header-y += kvm.h endif -ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) +ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/a.out.h \ + $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h endif unifdef-y += auxvec.h diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 59391250d51c..b68ec09399be 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -167,7 +167,8 @@ unifdef-y += acct.h unifdef-y += adb.h unifdef-y += adfs_fs.h unifdef-y += agpgart.h -ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) +ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/a.out.h \ + $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h endif unifdef-y += apm_bios.h @@ -258,7 +259,8 @@ unifdef-y += kd.h unifdef-y += kernelcapi.h unifdef-y += kernel.h unifdef-y += keyboard.h -ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/kvm.h),) +ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm.h \ + $(srctree)/include/asm-$(SRCARCH)/kvm.h),) unifdef-y += kvm.h endif unifdef-y += llc.h -- cgit v1.2.3 From 49048622eae698e5c4ae61f7e71200f265ccc529 Mon Sep 17 00:00:00 2001 From: Balbir Singh Date: Fri, 5 Sep 2008 18:12:23 +0200 Subject: sched: fix process time monotonicity Spencer reported a problem where utime and stime were going negative despite the fixes in commit b27f03d4bdc145a09fb7b0c0e004b29f1ee555fa. The suspected reason for the problem is that signal_struct maintains it's own utime and stime (of exited tasks), these are not updated using the new task_utime() routine, hence sig->utime can go backwards and cause the same problem to occur (sig->utime, adds tsk->utime and not task_utime()). This patch fixes the problem TODO: using max(task->prev_utime, derived utime) works for now, but a more generic solution is to implement cputime_max() and use the cputime_gt() function for comparison. Reported-by: spencer@bluehost.com Signed-off-by: Balbir Singh Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- fs/proc/array.c | 59 --------------------------------------------------- include/linux/sched.h | 4 ++++ kernel/exit.c | 6 +++--- kernel/sched.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/array.c b/fs/proc/array.c index 0d6eb33597c6..71c9be59c9c2 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -337,65 +337,6 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, return 0; } -/* - * Use precise platform statistics if available: - */ -#ifdef CONFIG_VIRT_CPU_ACCOUNTING -static cputime_t task_utime(struct task_struct *p) -{ - return p->utime; -} - -static cputime_t task_stime(struct task_struct *p) -{ - return p->stime; -} -#else -static cputime_t task_utime(struct task_struct *p) -{ - clock_t utime = cputime_to_clock_t(p->utime), - total = utime + cputime_to_clock_t(p->stime); - u64 temp; - - /* - * Use CFS's precise accounting: - */ - temp = (u64)nsec_to_clock_t(p->se.sum_exec_runtime); - - if (total) { - temp *= utime; - do_div(temp, total); - } - utime = (clock_t)temp; - - p->prev_utime = max(p->prev_utime, clock_t_to_cputime(utime)); - return p->prev_utime; -} - -static cputime_t task_stime(struct task_struct *p) -{ - clock_t stime; - - /* - * Use CFS's precise accounting. (we subtract utime from - * the total, to make sure the total observed by userspace - * grows monotonically - apps rely on that): - */ - stime = nsec_to_clock_t(p->se.sum_exec_runtime) - - cputime_to_clock_t(task_utime(p)); - - if (stime >= 0) - p->prev_stime = max(p->prev_stime, clock_t_to_cputime(stime)); - - return p->prev_stime; -} -#endif - -static cputime_t task_gtime(struct task_struct *p) -{ - return p->gtime; -} - static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task, int whole) { diff --git a/include/linux/sched.h b/include/linux/sched.h index cfb0d87b99fc..3d9120c5ad15 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1475,6 +1475,10 @@ static inline void put_task_struct(struct task_struct *t) __put_task_struct(t); } +extern cputime_t task_utime(struct task_struct *p); +extern cputime_t task_stime(struct task_struct *p); +extern cputime_t task_gtime(struct task_struct *p); + /* * Per process flags */ diff --git a/kernel/exit.c b/kernel/exit.c index 25ed2ad986df..16395644a98f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -112,9 +112,9 @@ static void __exit_signal(struct task_struct *tsk) * We won't ever get here for the group leader, since it * will have been the last reference on the signal_struct. */ - sig->utime = cputime_add(sig->utime, tsk->utime); - sig->stime = cputime_add(sig->stime, tsk->stime); - sig->gtime = cputime_add(sig->gtime, tsk->gtime); + sig->utime = cputime_add(sig->utime, task_utime(tsk)); + sig->stime = cputime_add(sig->stime, task_stime(tsk)); + sig->gtime = cputime_add(sig->gtime, task_gtime(tsk)); sig->min_flt += tsk->min_flt; sig->maj_flt += tsk->maj_flt; sig->nvcsw += tsk->nvcsw; diff --git a/kernel/sched.c b/kernel/sched.c index 9a1ddb84e26d..1a5f73c1fcdc 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4178,6 +4178,65 @@ void account_steal_time(struct task_struct *p, cputime_t steal) cpustat->steal = cputime64_add(cpustat->steal, tmp); } +/* + * Use precise platform statistics if available: + */ +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +cputime_t task_utime(struct task_struct *p) +{ + return p->utime; +} + +cputime_t task_stime(struct task_struct *p) +{ + return p->stime; +} +#else +cputime_t task_utime(struct task_struct *p) +{ + clock_t utime = cputime_to_clock_t(p->utime), + total = utime + cputime_to_clock_t(p->stime); + u64 temp; + + /* + * Use CFS's precise accounting: + */ + temp = (u64)nsec_to_clock_t(p->se.sum_exec_runtime); + + if (total) { + temp *= utime; + do_div(temp, total); + } + utime = (clock_t)temp; + + p->prev_utime = max(p->prev_utime, clock_t_to_cputime(utime)); + return p->prev_utime; +} + +cputime_t task_stime(struct task_struct *p) +{ + clock_t stime; + + /* + * Use CFS's precise accounting. (we subtract utime from + * the total, to make sure the total observed by userspace + * grows monotonically - apps rely on that): + */ + stime = nsec_to_clock_t(p->se.sum_exec_runtime) - + cputime_to_clock_t(task_utime(p)); + + if (stime >= 0) + p->prev_stime = max(p->prev_stime, clock_t_to_cputime(stime)); + + return p->prev_stime; +} +#endif + +inline cputime_t task_gtime(struct task_struct *p) +{ + return p->gtime; +} + /* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. -- cgit v1.2.3 From ca1af29a733629b9158a4a32a927d16ff9009a95 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 2 Sep 2008 13:13:39 +0200 Subject: x86, pci: add northbridge pci ids for fam 0x11 processors The PCI device ids for AMD family 0x11 processors are missing in pci_ids.h. This patch adds them. Signed-off-by: Joerg Roedel Signed-off-by: Ingo Molnar --- include/linux/pci_ids.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9ec2bcce8e83..0d0b314476c9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -497,6 +497,11 @@ #define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 #define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 #define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 +#define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 +#define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 +#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 +#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 #define PCI_DEVICE_ID_AMD_SCSI 0x2020 -- cgit v1.2.3 From f59ac0481660e66cec67f1d6b024e78b9dc715fe Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 29 Aug 2008 16:26:43 -0700 Subject: cfg80211: keep track of supported interface modes It is obviously good for userspace to know up front which interface modes a given piece of hardware might support (even if adding such an interface might fail later because of concurrency issues), so let's make cfg80211 aware of that. For good measure, disallow adding interfaces in all other modes so drivers don't forget to announce support for one mode when they add it. Signed-off-by: Johannes Berg Signed-off-by: Stephen Blackheath Signed-off-by: Ivo van Doorn Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/adm8211.c | 1 + drivers/net/wireless/ath5k/base.c | 6 ++++++ drivers/net/wireless/ath9k/main.c | 5 +++++ drivers/net/wireless/b43/main.c | 7 +++++++ drivers/net/wireless/b43legacy/main.c | 5 +++++ drivers/net/wireless/iwlwifi/iwl-core.c | 4 ++++ drivers/net/wireless/iwlwifi/iwl3945-base.c | 5 +++++ drivers/net/wireless/mac80211_hwsim.c | 3 +++ drivers/net/wireless/p54/p54common.c | 3 +++ drivers/net/wireless/rt2x00/rt2x00dev.c | 5 +++++ drivers/net/wireless/rtl8187_dev.c | 2 ++ drivers/net/wireless/zd1211rw/zd_mac.c | 5 +++++ include/linux/nl80211.h | 6 ++++++ include/net/wireless.h | 3 +++ net/mac80211/main.c | 7 +++++++ net/wireless/core.c | 9 ++++++++- net/wireless/nl80211.c | 22 ++++++++++++++++++++-- 17 files changed, 95 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 3333d4596b8d..c6a55cd12db9 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1884,6 +1884,7 @@ static int __devinit adm8211_probe(struct pci_dev *pdev, dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); /* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */ dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); dev->channel_change_time = 1000; dev->max_signal = 100; /* FIXME: find better value */ diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 7989ab5c2bba..85260c39aa2b 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -485,6 +485,12 @@ ath5k_pci_probe(struct pci_dev *pdev, hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + hw->extra_tx_headroom = 2; hw->channel_change_time = 5000; sc = hw->priv; diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index dc45eef3289a..39a4a70d0130 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1482,6 +1482,11 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 63bafc2f3f0a..2d915c1a82a1 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4569,6 +4569,13 @@ static int b43_wireless_init(struct ssb_device *dev) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); + hw->queues = b43_modparam_qos ? 4 : 1; SET_IEEE80211_DEV(hw, dev->dev); if (is_valid_ether_addr(sprom->et1mac)) diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 1cb77db5c292..68f63f5093af 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -3704,6 +3704,11 @@ static int b43legacy_wireless_init(struct ssb_device *dev) hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); hw->queues = 1; /* FIXME: hardware has more queues */ SET_IEEE80211_DEV(hw, dev->dev); if (is_valid_ether_addr(sprom->et1mac)) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index fbf75a62958d..0a511ef8e354 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -819,6 +819,10 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; /* queues to support 11n aggregation */ diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a622fc33590a..cee3045f1606 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -7888,6 +7888,11 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + /* 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 732429d49122..6ba50f087f7b 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -447,6 +447,9 @@ static int __init init_mac80211_hwsim(void) hw->channel_change_time = 1; hw->queues = 4; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP); hw->ampdu_queues = 1; memcpy(data->channels, hwsim_channels, sizeof(hwsim_channels)); diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 17e06bbc996a..6da98e6e6a9a 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -1072,6 +1072,9 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */ IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_UNSPEC; + + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + dev->channel_change_time = 1000; /* TODO: find actual value */ dev->max_signal = 127; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 369b0b2d8643..2f3bfc606880 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1052,6 +1052,11 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); + rt2x00dev->hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + /* * Let the driver probe the device to detect the capabilities. */ diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 060a26505358..8a42bfa6d4f0 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -1184,6 +1184,8 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, dev->max_signal = 65; } + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) printk(KERN_INFO "rtl8187: inconsistency between id with OEM" " info!\n"); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 4d7b98b05030..e019102b2285 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -937,6 +937,11 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DB; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + hw->max_signal = 100; hw->queues = 1; hw->extra_tx_headroom = sizeof(struct zd_ctrlset); diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 0c1147de3ec7..5e51f4e7600b 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -210,6 +210,10 @@ enum nl80211_commands { * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -259,6 +263,8 @@ enum nl80211_attrs { NL80211_ATTR_HT_CAPABILITY, + NL80211_ATTR_SUPPORTED_IFTYPES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/wireless.h b/include/net/wireless.h index 9324f8dd183e..1dc8ec3daa2f 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -185,6 +185,9 @@ struct wiphy { /* permanent MAC address */ u8 perm_addr[ETH_ALEN]; + /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ + u16 interface_modes; + /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 638b75f36e23..396cfb2d0f46 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1675,6 +1675,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } } + /* if low-level driver supports AP, we also support VLAN */ + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) + local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); + + /* mac80211 always supports monitor */ + local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + result = wiphy_register(local->hw.wiphy); if (result < 0) return result; diff --git a/net/wireless/core.c b/net/wireless/core.c index f1da0b93bc56..7e995ac06a0c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1,7 +1,7 @@ /* * This is the linux wireless configuration interface. * - * Copyright 2006, 2007 Johannes Berg + * Copyright 2006-2008 Johannes Berg */ #include @@ -259,6 +259,13 @@ int wiphy_register(struct wiphy *wiphy) struct ieee80211_supported_band *sband; bool have_band = false; int i; + u16 ifmodes = wiphy->interface_modes; + + /* sanity check ifmodes */ + WARN_ON(!ifmodes); + ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; + if (WARN_ON(ifmodes != wiphy->interface_modes)) + wiphy->interface_modes = ifmodes; /* sanity check supported bands/channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4d6c02afd6f5..77880ba8b619 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -113,10 +113,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_rates, *nl_rate; + struct nlattr *nl_modes; enum ieee80211_band band; struct ieee80211_channel *chan; struct ieee80211_rate *rate; int i; + u16 ifmodes = dev->wiphy.interface_modes; hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); if (!hdr) @@ -125,6 +127,20 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); + nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); + if (!nl_modes) + goto nla_put_failure; + + i = 0; + while (ifmodes) { + if (ifmodes & 1) + NLA_PUT_FLAG(msg, i); + ifmodes >>= 1; + i++; + } + + nla_nest_end(msg, nl_modes); + nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); if (!nl_bands) goto nla_put_failure; @@ -415,7 +431,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) ifindex = dev->ifindex; dev_put(dev); - if (!drv->ops->change_virtual_intf) { + if (!drv->ops->change_virtual_intf || + !(drv->wiphy.interface_modes & (1 << type))) { err = -EOPNOTSUPP; goto unlock; } @@ -462,7 +479,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(drv)) return PTR_ERR(drv); - if (!drv->ops->add_virtual_intf) { + if (!drv->ops->add_virtual_intf || + !(drv->wiphy.interface_modes & (1 << type))) { err = -EOPNOTSUPP; goto unlock; } -- cgit v1.2.3 From 11d55d2cba6e867be8955e5ae011c54c556b849f Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 5 Sep 2008 14:00:18 -0700 Subject: res_counter: fix off-by-one bug in setting limit I found we can no longer set limit to 0 with 2.6.27-rcX: # mount -t cgroup -omemory xxx /mnt # mkdir /mnt/0 # echo 0 > /mnt/0/memory.limit_in_bytes bash: echo: write error: Device or resource busy It turned out 'limit' can't be set to 'usage', which is wrong IMO. Signed-off-by: Li Zefan Acked-by: KAMEZAWA Hiroyuki Acked-by: Balbir Singh Acked-by: Pavel Emelyanov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index fdeadd9740dc..271c1c2c9f6f 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -166,7 +166,7 @@ static inline int res_counter_set_limit(struct res_counter *cnt, int ret = -EBUSY; spin_lock_irqsave(&cnt->lock, flags); - if (cnt->usage < limit) { + if (cnt->usage <= limit) { cnt->limit = limit; ret = 0; } -- cgit v1.2.3 From 22f30168d296dbb54a21ebad44c9d735bca6f67b Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 5 Sep 2008 14:00:23 -0700 Subject: tracehook: comment pasto fixes Fix some pasto's in comments in the new linux/tracehook.h and asm-generic/syscall.h files. Reported-by: Wenji Huang Signed-off-by: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/syscall.h | 2 +- include/linux/tracehook.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/syscall.h b/include/asm-generic/syscall.h index abcf34c2fdc7..ea8087b55ffc 100644 --- a/include/asm-generic/syscall.h +++ b/include/asm-generic/syscall.h @@ -126,7 +126,7 @@ void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, * @args: array of argument values to store * * Changes @n arguments to the system call starting with the @i'th argument. - * @n'th argument to @val. Argument @i gets value @args[0], and so on. + * Argument @i gets value @args[0], and so on. * An arch inline version is probably optimal when @i and @n are constants. * * It's only valid to call this when @task is stopped for tracing on diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index b48d81969574..6186a789d6c7 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -272,7 +272,7 @@ static inline void tracehook_finish_clone(struct task_struct *child, * tracehook_report_clone_complete(). This must prevent the child from * self-reaping if tracehook_report_clone_complete() uses the @child * pointer; otherwise it might have died and been released by the time - * tracehook_report_report_clone_complete() is called. + * tracehook_report_clone_complete() is called. * * Called with no locks held, but the child cannot run until this returns. */ -- cgit v1.2.3 From dfb512ec4834116124da61d6c1ee10fd0aa32bd6 Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Fri, 29 Aug 2008 13:11:41 -0700 Subject: sched: arch_reinit_sched_domains() must destroy domains to force rebuild What I realized recently is that calling rebuild_sched_domains() in arch_reinit_sched_domains() by itself is not enough when cpusets are enabled. partition_sched_domains() code is trying to avoid unnecessary domain rebuilds and will not actually rebuild anything if new domain masks match the old ones. What this means is that doing echo 1 > /sys/devices/system/cpu/sched_mc_power_savings on a system with cpusets enabled will not take affect untill something changes in the cpuset setup (ie new sets created or deleted). This patch fixes restore correct behaviour where domains must be rebuilt in order to enable MC powersaving flags. Test on quad-core Core2 box with both CONFIG_CPUSETS and !CONFIG_CPUSETS. Also tested on dual-core Core2 laptop. Lockdep is happy and things are working as expected. Signed-off-by: Max Krasnyansky Tested-by: Vaidyanathan Srinivasan Signed-off-by: Ingo Molnar --- include/linux/cpuset.h | 2 +- kernel/sched.c | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index e8f450c499b0..2691926fb506 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -160,7 +160,7 @@ static inline int current_cpuset_is_being_rebound(void) static inline void rebuild_sched_domains(void) { - partition_sched_domains(0, NULL, NULL); + partition_sched_domains(1, NULL, NULL); } #endif /* !CONFIG_CPUSETS */ diff --git a/kernel/sched.c b/kernel/sched.c index d601fb0406ca..d72ee9a0eacd 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7589,24 +7589,27 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur, * and partition_sched_domains() will fallback to the single partition * 'fallback_doms', it also forces the domains to be rebuilt. * + * If doms_new==NULL it will be replaced with cpu_online_map. + * ndoms_new==0 is a special case for destroying existing domains. + * It will not create the default domain. + * * Call with hotplug lock held */ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, struct sched_domain_attr *dattr_new) { - int i, j; + int i, j, n; mutex_lock(&sched_domains_mutex); /* always unregister in case we don't destroy any domains */ unregister_sched_domain_sysctl(); - if (doms_new == NULL) - ndoms_new = 0; + n = doms_new ? ndoms_new : 0; /* Destroy deleted domains */ for (i = 0; i < ndoms_cur; i++) { - for (j = 0; j < ndoms_new; j++) { + for (j = 0; j < n; j++) { if (cpus_equal(doms_cur[i], doms_new[j]) && dattrs_equal(dattr_cur, i, dattr_new, j)) goto match1; @@ -7619,7 +7622,6 @@ match1: if (doms_new == NULL) { ndoms_cur = 0; - ndoms_new = 1; doms_new = &fallback_doms; cpus_andnot(doms_new[0], cpu_online_map, cpu_isolated_map); dattr_new = NULL; @@ -7656,8 +7658,13 @@ match2: int arch_reinit_sched_domains(void) { get_online_cpus(); + + /* Destroy domains first to force the rebuild */ + partition_sched_domains(0, NULL, NULL); + rebuild_sched_domains(); put_online_cpus(); + return 0; } @@ -7741,7 +7748,7 @@ static int update_sched_domains(struct notifier_block *nfb, case CPU_ONLINE_FROZEN: case CPU_DEAD: case CPU_DEAD_FROZEN: - partition_sched_domains(0, NULL, NULL); + partition_sched_domains(1, NULL, NULL); return NOTIFY_OK; default: -- cgit v1.2.3 From 5394f80f92642c61fc2a95385be85f2fdcfb5adb Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 7 Sep 2008 01:51:32 -0700 Subject: x86: check for and defend against BIOS memory corruption Some BIOSes have been observed to corrupt memory in the low 64k. This change: - Reserves all memory which does not have to be in that area, to prevent it from being used as general memory by the kernel. Things like the SMP trampoline are still in the memory, however. - Clears the reserved memory so we can observe changes to it. - Adds a function check_for_bios_corruption() which checks and reports on memory becoming unexpectedly non-zero. Currently it's called in the x86 fault handler, and the powermanagement debug output. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ingo Molnar --- Documentation/kernel-parameters.txt | 5 +++ arch/x86/Kconfig | 3 ++ arch/x86/kernel/setup.c | 87 +++++++++++++++++++++++++++++++++++++ arch/x86/mm/fault.c | 2 + drivers/base/power/main.c | 1 + include/linux/kernel.h | 12 +++++ 6 files changed, 110 insertions(+) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1150444a21ab..df48af505d15 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -360,6 +360,11 @@ and is between 256 and 4096 characters. It is defined in the file Format: ,, See header of drivers/net/hamradio/baycom_ser_hdx.c. + bios_corruption_check=0/1 [X86] + Some BIOSes seem to corrupt the first 64k of memory + when doing things like suspend/resume. Setting this + option will scan the memory looking for corruption. + boot_delay= Milliseconds to delay each printk during boot. Values larger than 10 seconds (10000) are changed to no delay (0). diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ed92864d1325..1bb52e2ca02e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -201,6 +201,9 @@ config X86_TRAMPOLINE depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP) default y +config X86_CHECK_BIOS_CORRUPTION + def_bool y + config KTIME_SCALAR def_bool X86_32 source "init/Kconfig" diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 362d4e7f2d38..ee89ebc5aabc 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -578,6 +578,89 @@ static struct x86_quirks default_x86_quirks __initdata; struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; +/* + * Some BIOSes seem to corrupt the low 64k of memory during events + * like suspend/resume and unplugging an HDMI cable. Reserve all + * remaining free memory in that area and fill it with a distinct + * pattern. + */ +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION +#define MAX_SCAN_AREAS 8 +static struct e820entry scan_areas[MAX_SCAN_AREAS]; +static int num_scan_areas; + +static void __init setup_bios_corruption_check(void) +{ + u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */ + + while(addr < 0x10000 && num_scan_areas < MAX_SCAN_AREAS) { + u64 size; + addr = find_e820_area_size(addr, &size, PAGE_SIZE); + + if (addr == 0) + break; + + if ((addr + size) > 0x10000) + size = 0x10000 - addr; + + if (size == 0) + break; + + e820_update_range(addr, size, E820_RAM, E820_RESERVED); + scan_areas[num_scan_areas].addr = addr; + scan_areas[num_scan_areas].size = size; + num_scan_areas++; + + /* Assume we've already mapped this early memory */ + memset(__va(addr), 0, size); + + addr += size; + } + + printk(KERN_INFO "scanning %d areas for BIOS corruption\n", + num_scan_areas); + update_e820(); +} + +static int __read_mostly bios_corruption_check = 1; + +void check_for_bios_corruption(void) +{ + int i; + int corruption = 0; + + if (!bios_corruption_check) + return; + + for(i = 0; i < num_scan_areas; i++) { + unsigned long *addr = __va(scan_areas[i].addr); + unsigned long size = scan_areas[i].size; + + for(; size; addr++, size -= sizeof(unsigned long)) { + if (!*addr) + continue; + printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n", + addr, __pa(addr), *addr); + corruption = 1; + *addr = 0; + } + } + + if (corruption) + dump_stack(); +} + +static int set_bios_corruption_check(char *arg) +{ + char *end; + + bios_corruption_check = simple_strtol(arg, &end, 10); + + return (*end == 0) ? 0 : -EINVAL; +} +early_param("bios_corruption_check", set_bios_corruption_check); +#endif + /* * Determine if we were loaded by an EFI loader. If so, then we have also been * passed the efi memmap, systab, etc., so we should use these data structures @@ -750,6 +833,10 @@ void __init setup_arch(char **cmdline_p) high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; #endif +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION + setup_bios_corruption_check(); +#endif + /* max_pfn_mapped is updated here */ max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn< Date: Sun, 7 Sep 2008 01:51:33 -0700 Subject: x86: add periodic corruption check Perodically check for corruption in low phusical memory. Don't bother checking at fault time, since it won't show anything useful. Signed-off-by: Hugh Dickins Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ingo Molnar --- arch/x86/kernel/setup.c | 17 +++++++++++++++++ arch/x86/mm/fault.c | 2 -- arch/x86/mm/init_32.c | 2 ++ arch/x86/mm/init_64.c | 2 ++ include/linux/kernel.h | 1 + 5 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index ee89ebc5aabc..c239b3780973 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -623,6 +623,7 @@ static void __init setup_bios_corruption_check(void) } static int __read_mostly bios_corruption_check = 1; +static struct timer_list periodic_check_timer; void check_for_bios_corruption(void) { @@ -650,6 +651,22 @@ void check_for_bios_corruption(void) dump_stack(); } +static void periodic_check_for_corruption(unsigned long data) +{ + check_for_bios_corruption(); + mod_timer(&periodic_check_timer, jiffies + 60*HZ); +} + +void start_periodic_check_for_corruption(void) +{ + if (!bios_corruption_check) + return; + + init_timer(&periodic_check_timer); + periodic_check_timer.function = &periodic_check_for_corruption; + periodic_check_for_corruption(0); +} + static int set_bios_corruption_check(char *arg) { char *end; diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 5140bdf03020..455f3fe67b42 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -848,8 +848,6 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - check_for_bios_corruption(); - #ifdef CONFIG_X86_32 bust_spinlocks(1); #else diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index d37f29376b0c..657a16ad61ba 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -907,6 +907,8 @@ void __init mem_init(void) int codesize, reservedpages, datasize, initsize; int tmp; + start_periodic_check_for_corruption(); + #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); #endif diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index d3746efb060d..f4db5276fa21 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -769,6 +769,8 @@ void __init mem_init(void) { long codesize, reservedpages, datasize, initsize; + start_periodic_check_for_corruption(); + pci_iommu_alloc(); /* clear_bss() already clear the empty_zero_page */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 8017129e6b63..00bb251c6451 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -246,6 +246,7 @@ extern int root_mountflags; * able to scatter it around anywhere in the kernel. */ void check_for_bios_corruption(void); +void start_periodic_check_for_corruption(void); #else static inline void check_for_bios_corruption(void) { -- cgit v1.2.3 From b0dbcf511c4bd10350902e79a1bdd4f5dcca66b6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 4 Sep 2008 21:13:37 +0100 Subject: [NET] smc91x: provide configurable leds This patch provides a mechanism for platforms to be able to supply the LED configuration via platform data, rather than having to hard code it in smc91x.h. Acked-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 9 ++++++++- drivers/net/smc91x.h | 2 +- include/linux/smc91x.h | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 2040965d7724..ceed2f69216a 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1520,7 +1520,9 @@ smc_open(struct net_device *dev) /* Setup the default Register Modes */ lp->tcr_cur_mode = TCR_DEFAULT; lp->rcr_cur_mode = RCR_DEFAULT; - lp->rpc_cur_mode = RPC_DEFAULT; + lp->rpc_cur_mode = RPC_DEFAULT | + lp->cfg.leda << RPC_LSXA_SHFT | + lp->cfg.ledb << RPC_LSXB_SHFT; /* * If we are not using a MII interface, we need to @@ -2157,6 +2159,11 @@ static int smc_drv_probe(struct platform_device *pdev) lp->cfg.flags |= (nowait) ? SMC91X_NOWAIT : 0; } + if (!lp->cfg.leda && !lp->cfg.ledb) { + lp->cfg.leda = RPC_LSA_DEFAULT; + lp->cfg.ledb = RPC_LSB_DEFAULT; + } + ndev->dma = (unsigned char)-1; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 997e7f1d5c6e..8322e7f37af5 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -794,7 +794,7 @@ smc_pxa_dma_irq(int dma, void *dummy) #define RPC_LSB_DEFAULT RPC_LED_FD #endif -#define RPC_DEFAULT (RPC_ANEG | (RPC_LSA_DEFAULT << RPC_LSXA_SHFT) | (RPC_LSB_DEFAULT << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) +#define RPC_DEFAULT (RPC_ANEG | RPC_SPEED | RPC_DPLX) /* Bank 0 0x0C is reserved */ diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 3827b922ba1f..ed25483d25d9 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -18,6 +18,8 @@ struct smc91x_platdata { unsigned long flags; + unsigned char leda; + unsigned char ledb; }; #endif /* __SMC91X_H__ */ -- cgit v1.2.3 From 05496769e5da83ce22ed97345afd9c7b71d6bd24 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 16 Sep 2008 14:36:17 -0400 Subject: jbd2: clean up how the journal device name is printed Calculate the journal device name once and stash it away in the journal_s structure. This avoids needing to call bdevname() everywhere and reduces stack usage by not needing to allocate an on-stack buffer. In addition, we eliminate the '/' that can appear in device names (e.g. "cciss/c0d0p9" --- see kernel bugzilla #11321) that can cause problems when creating proc directory names, and include the inode number to support ocfs2 which creates multiple journals with different inode numbers. Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 12 +++--------- fs/jbd2/commit.c | 11 +++-------- fs/jbd2/journal.c | 48 ++++++++++++++++-------------------------------- include/linux/jbd2.h | 3 ++- 4 files changed, 24 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index f58cc0309dc9..64e1c21eb5d2 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1476,15 +1476,9 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, EXT4_INODES_PER_GROUP(sb), sbi->s_mount_opt); - if (EXT4_SB(sb)->s_journal->j_inode == NULL) { - char b[BDEVNAME_SIZE]; - - printk(KERN_INFO "EXT4 FS on %s, external journal on %s\n", - sb->s_id, bdevname(EXT4_SB(sb)->s_journal->j_dev, b)); - } else { - printk(KERN_INFO "EXT4 FS on %s, internal journal\n", - sb->s_id); - } + printk(KERN_INFO "EXT4 FS on %s, %s journal on %s\n", + sb->s_id, EXT4_SB(sb)->s_journal->j_inode ? "internal" : + "external", EXT4_SB(sb)->s_journal->j_devname); return res; } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index f2ad061e95ec..b091e5378fe0 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -147,12 +147,9 @@ static int journal_submit_commit_record(journal_t *journal, * to remember if we sent a barrier request */ if (ret == -EOPNOTSUPP && barrier_done) { - char b[BDEVNAME_SIZE]; - printk(KERN_WARNING - "JBD: barrier-based sync failed on %s - " - "disabling barriers\n", - bdevname(journal->j_dev, b)); + "JBD: barrier-based sync failed on %s - " + "disabling barriers\n", journal->j_devname); spin_lock(&journal->j_state_lock); journal->j_flags &= ~JBD2_BARRIER; spin_unlock(&journal->j_state_lock); @@ -681,11 +678,9 @@ start_journal_io: */ err = journal_finish_inode_data_buffers(journal, commit_transaction); if (err) { - char b[BDEVNAME_SIZE]; - printk(KERN_WARNING "JBD2: Detected IO errors while flushing file data " - "on %s\n", bdevname(journal->j_fs_dev, b)); + "on %s\n", journal->j_devname); err = 0; } diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 8207a01c4edb..81186a29742e 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -597,13 +597,9 @@ int jbd2_journal_bmap(journal_t *journal, unsigned long blocknr, if (ret) *retp = ret; else { - char b[BDEVNAME_SIZE]; - printk(KERN_ALERT "%s: journal block not found " "at offset %lu on %s\n", - __func__, - blocknr, - bdevname(journal->j_dev, b)); + __func__, blocknr, journal->j_devname); err = -EIO; __journal_abort_soft(journal, err); } @@ -901,10 +897,7 @@ static struct proc_dir_entry *proc_jbd2_stats; static void jbd2_stats_proc_init(journal_t *journal) { - char name[BDEVNAME_SIZE]; - - bdevname(journal->j_dev, name); - journal->j_proc_entry = proc_mkdir(name, proc_jbd2_stats); + journal->j_proc_entry = proc_mkdir(journal->j_devname, proc_jbd2_stats); if (journal->j_proc_entry) { proc_create_data("history", S_IRUGO, journal->j_proc_entry, &jbd2_seq_history_fops, journal); @@ -915,12 +908,9 @@ static void jbd2_stats_proc_init(journal_t *journal) static void jbd2_stats_proc_exit(journal_t *journal) { - char name[BDEVNAME_SIZE]; - - bdevname(journal->j_dev, name); remove_proc_entry("info", journal->j_proc_entry); remove_proc_entry("history", journal->j_proc_entry); - remove_proc_entry(name, proc_jbd2_stats); + remove_proc_entry(journal->j_devname, proc_jbd2_stats); } static void journal_init_stats(journal_t *journal) @@ -1018,6 +1008,7 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev, { journal_t *journal = journal_init_common(); struct buffer_head *bh; + char *p; int n; if (!journal) @@ -1039,6 +1030,10 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev, journal->j_fs_dev = fs_dev; journal->j_blk_offset = start; journal->j_maxlen = len; + bdevname(journal->j_dev, journal->j_devname); + p = journal->j_devname; + while ((p = strchr(p, '/'))) + *p = '!'; jbd2_stats_proc_init(journal); bh = __getblk(journal->j_dev, start, journal->j_blocksize); @@ -1061,6 +1056,7 @@ journal_t * jbd2_journal_init_inode (struct inode *inode) { struct buffer_head *bh; journal_t *journal = journal_init_common(); + char *p; int err; int n; unsigned long long blocknr; @@ -1070,6 +1066,12 @@ journal_t * jbd2_journal_init_inode (struct inode *inode) journal->j_dev = journal->j_fs_dev = inode->i_sb->s_bdev; journal->j_inode = inode; + bdevname(journal->j_dev, journal->j_devname); + p = journal->j_devname; + while ((p = strchr(p, '/'))) + *p = '!'; + p = journal->j_devname + strlen(journal->j_devname); + sprintf(p, ":%lu", journal->j_inode->i_ino); jbd_debug(1, "journal %p: inode %s/%ld, size %Ld, bits %d, blksize %ld\n", journal, inode->i_sb->s_id, inode->i_ino, @@ -1760,23 +1762,6 @@ int jbd2_journal_wipe(journal_t *journal, int write) return err; } -/* - * journal_dev_name: format a character string to describe on what - * device this journal is present. - */ - -static const char *journal_dev_name(journal_t *journal, char *buffer) -{ - struct block_device *bdev; - - if (journal->j_inode) - bdev = journal->j_inode->i_sb->s_bdev; - else - bdev = journal->j_dev; - - return bdevname(bdev, buffer); -} - /* * Journal abort has very specific semantics, which we describe * for journal abort. @@ -1793,13 +1778,12 @@ static const char *journal_dev_name(journal_t *journal, char *buffer) void __jbd2_journal_abort_hard(journal_t *journal) { transaction_t *transaction; - char b[BDEVNAME_SIZE]; if (journal->j_flags & JBD2_ABORT) return; printk(KERN_ERR "Aborting journal on device %s.\n", - journal_dev_name(journal, b)); + journal->j_devname); spin_lock(&journal->j_state_lock); journal->j_flags |= JBD2_ABORT; diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 3dd209007098..66c3499478b5 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -850,7 +850,8 @@ struct journal_s */ struct block_device *j_dev; int j_blocksize; - unsigned long long j_blk_offset; + unsigned long long j_blk_offset; + char j_devname[BDEVNAME_SIZE+24]; /* * Device which holds the client fs. For internal journal this will be -- cgit v1.2.3 From e545a6140b698b2494daf0b32107bdcc5e901390 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Sun, 7 Sep 2008 16:57:22 +0200 Subject: kernel/cpu.c: create a CPU_STARTING cpu_chain notifier Right now, there is no notifier that is called on a new cpu, before the new cpu begins processing interrupts/softirqs. Various kernel function would need that notification, e.g. kvm works around by calling smp_call_function_single(), rcu polls cpu_online_map. The patch adds a CPU_STARTING notification. It also adds a helper function that sends the message to all cpu_chain handlers. Tested on x86-64. All other archs are untested. Especially on sparc, I'm not sure if I got it right. Signed-off-by: Manfred Spraul Signed-off-by: Ingo Molnar --- arch/alpha/kernel/smp.c | 3 +++ arch/arm/kernel/smp.c | 1 + arch/cris/arch-v32/kernel/smp.c | 1 + arch/ia64/kernel/smpboot.c | 1 + arch/m32r/kernel/smpboot.c | 2 ++ arch/mips/kernel/smp.c | 2 ++ arch/powerpc/kernel/smp.c | 1 + arch/s390/kernel/smp.c | 2 ++ arch/sh/kernel/smp.c | 2 ++ arch/sparc/kernel/sun4d_smp.c | 1 + arch/sparc/kernel/sun4m_smp.c | 2 ++ arch/um/kernel/smp.c | 1 + arch/x86/kernel/smpboot.c | 1 + arch/x86/mach-voyager/voyager_smp.c | 2 ++ include/linux/cpu.h | 1 + include/linux/notifier.h | 10 +++++++++- kernel/cpu.c | 19 +++++++++++++++++++ 17 files changed, 51 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 83df541650fc..06b6fdab639f 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -149,6 +149,9 @@ smp_callin(void) atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; + /* inform the notifiers about the new cpu */ + notify_cpu_starting(cpuid); + /* Must have completely accurate bogos. */ local_irq_enable(); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index e9842f6767f9..e42a749a56dd 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -277,6 +277,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) /* * Enable local interrupts. */ + notify_cpu_starting(cpu); local_irq_enable(); local_fiq_enable(); diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c index 952a24b2f5a9..52e16c6436f9 100644 --- a/arch/cris/arch-v32/kernel/smp.c +++ b/arch/cris/arch-v32/kernel/smp.c @@ -178,6 +178,7 @@ void __init smp_callin(void) unmask_irq(IPI_INTR_VECT); unmask_irq(TIMER0_INTR_VECT); preempt_disable(); + notify_cpu_starting(cpu); local_irq_enable(); cpu_set(cpu, cpu_online_map); diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index bcea81e432fd..333b58f218d0 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -401,6 +401,7 @@ smp_callin (void) spin_lock(&vector_lock); /* Setup the per cpu irq handling data structures */ __setup_vector_irq(cpuid); + notify_cpu_starting(cpuid); cpu_set(cpuid, cpu_online_map); per_cpu(cpu_state, cpuid) = CPU_ONLINE; spin_unlock(&vector_lock); diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c index 2c03ac1d005f..fc2994811f15 100644 --- a/arch/m32r/kernel/smpboot.c +++ b/arch/m32r/kernel/smpboot.c @@ -498,6 +498,8 @@ static void __init smp_online(void) { int cpu_id = smp_processor_id(); + notify_cpu_starting(cpu_id); + local_irq_enable(); /* Get our bogomips. */ diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 4410f172b8ab..7b59cfb7e602 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -121,6 +121,8 @@ asmlinkage __cpuinit void start_secondary(void) cpu = smp_processor_id(); cpu_data[cpu].udelay_val = loops_per_jiffy; + notify_cpu_starting(cpu); + mp_ops->smp_finish(); set_cpu_sibling_map(cpu); diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 5337ca7bb649..c27b10a1bd79 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -453,6 +453,7 @@ int __devinit start_secondary(void *unused) secondary_cpu_time_init(); ipi_call_lock(); + notify_cpu_starting(cpu); cpu_set(cpu, cpu_online_map); /* Update sibling maps */ base = cpu_first_thread_in_core(cpu); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 00b9b4dec5eb..9e8b1f9b8f4d 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -585,6 +585,8 @@ int __cpuinit start_secondary(void *cpuvoid) /* Enable pfault pseudo page faults on this cpu. */ pfault_init(); + /* call cpu notifiers */ + notify_cpu_starting(smp_processor_id()); /* Mark this cpu as online */ spin_lock(&call_lock); cpu_set(smp_processor_id(), cpu_online_map); diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 60c50841143e..001778f9adaf 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -82,6 +82,8 @@ asmlinkage void __cpuinit start_secondary(void) preempt_disable(); + notify_cpu_starting(smp_processor_id()); + local_irq_enable(); calibrate_delay(); diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 69596402a500..446767e8f569 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -88,6 +88,7 @@ void __init smp4d_callin(void) local_flush_cache_all(); local_flush_tlb_all(); + notify_cpu_starting(cpuid); /* * Unblock the master CPU _only_ when the scheduler state * of all secondary CPUs will be up-to-date, so after diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index a14a76ac7f36..9964890dc1db 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -71,6 +71,8 @@ void __cpuinit smp4m_callin(void) local_flush_cache_all(); local_flush_tlb_all(); + notify_cpu_starting(cpuid); + /* Get our local ticker going. */ smp_setup_percpu_timer(); diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c index be2d50c3aa95..045772142844 100644 --- a/arch/um/kernel/smp.c +++ b/arch/um/kernel/smp.c @@ -85,6 +85,7 @@ static int idle_proc(void *cpup) while (!cpu_isset(cpu, smp_commenced_mask)) cpu_relax(); + notify_cpu_starting(cpu); cpu_set(cpu, cpu_online_map); default_idle(); return 0; diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 7985c5b3f916..0b8261c3cac2 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -257,6 +257,7 @@ static void __cpuinit smp_callin(void) end_local_APIC_setup(); map_cpu_to_logical_apicid(); + notify_cpu_starting(cpuid); /* * Get our bogomips. * diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index ee0fba092157..199a5f4a873c 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c @@ -448,6 +448,8 @@ static void __init start_secondary(void *unused) VDEBUG(("VOYAGER SMP: CPU%d, stack at about %p\n", cpuid, &cpuid)); + notify_cpu_starting(cpuid); + /* enable interrupts */ local_irq_enable(); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index d7faf8808497..c2747ac2ae43 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -69,6 +69,7 @@ static inline void unregister_cpu_notifier(struct notifier_block *nb) #endif int cpu_up(unsigned int cpu); +void notify_cpu_starting(unsigned int cpu); extern void cpu_hotplug_init(void); extern void cpu_maps_update_begin(void); extern void cpu_maps_update_done(void); diff --git a/include/linux/notifier.h b/include/linux/notifier.h index da2698b0fdd1..b86fa2ffca0c 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -213,9 +213,16 @@ static inline int notifier_to_errno(int ret) #define CPU_DOWN_FAILED 0x0006 /* CPU (unsigned)v NOT going down */ #define CPU_DEAD 0x0007 /* CPU (unsigned)v dead */ #define CPU_DYING 0x0008 /* CPU (unsigned)v not running any task, - * not handling interrupts, soon dead */ + * not handling interrupts, soon dead. + * Called on the dying cpu, interrupts + * are already disabled. Must not + * sleep, must not fail */ #define CPU_POST_DEAD 0x0009 /* CPU (unsigned)v dead, cpu_hotplug * lock is dropped */ +#define CPU_STARTING 0x000A /* CPU (unsigned)v soon running. + * Called on the new cpu, just before + * enabling interrupts. Must not sleep, + * must not fail */ /* Used for CPU hotplug events occuring while tasks are frozen due to a suspend * operation in progress @@ -229,6 +236,7 @@ static inline int notifier_to_errno(int ret) #define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN) #define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) #define CPU_DYING_FROZEN (CPU_DYING | CPU_TASKS_FROZEN) +#define CPU_STARTING_FROZEN (CPU_STARTING | CPU_TASKS_FROZEN) /* Hibernation and suspend events */ #define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */ diff --git a/kernel/cpu.c b/kernel/cpu.c index f17e9854c246..dc45f2459efb 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -453,6 +453,25 @@ out: } #endif /* CONFIG_PM_SLEEP_SMP */ +/** + * notify_cpu_starting(cpu) - call the CPU_STARTING notifiers + * @cpu: cpu that just started + * + * This function calls the cpu_chain notifiers with CPU_STARTING. + * It must be called by the arch code on the new cpu, before the new cpu + * enables interrupts and before the "boot" cpu returns from __cpu_up(). + */ +void notify_cpu_starting(unsigned int cpu) +{ + unsigned long val = CPU_STARTING; + +#ifdef CONFIG_PM_SLEEP_SMP + if (cpu_isset(cpu, frozen_cpus)) + val = CPU_STARTING_FROZEN; +#endif /* CONFIG_PM_SLEEP_SMP */ + raw_notifier_call_chain(&cpu_chain, val, (void *)(long)cpu); +} + #endif /* CONFIG_SMP */ /* -- cgit v1.2.3 From 9c3254ad42e7925c3a3907a072185273705bd7b2 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 7 Sep 2008 14:54:41 -0700 Subject: x86: fix compile error with corruption checking disabled Fix compile error: arch/x86/mm/init_32.c: In function 'mem_init': arch/x86/mm/init_32.c:908: error: implicit declaration of function 'start_periodic_check_for_corruption' Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ingo Molnar --- include/linux/kernel.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 00bb251c6451..50873b211788 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -251,6 +251,10 @@ void start_periodic_check_for_corruption(void); static inline void check_for_bios_corruption(void) { } + +static inline void start_periodic_check_for_corruption(void) +{ +} #endif /* Values used for system_state */ -- cgit v1.2.3 From 410e27a49bb98bc7fa3ff5fc05cc313817b9f253 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Tue, 9 Sep 2008 13:27:22 +0200 Subject: This reverts "Merge branch 'dccp' of git://eden-feed.erg.abdn.ac.uk/dccp_exp" as it accentally contained the wrong set of patches. These will be submitted separately. Signed-off-by: Gerrit Renker --- Documentation/networking/dccp.txt | 54 +- include/linux/dccp.h | 122 ++- include/net/tcp.h | 15 - net/dccp/Kconfig | 3 + net/dccp/Makefile | 5 +- net/dccp/ackvec.c | 619 ++++++------ net/dccp/ackvec.h | 204 ++-- net/dccp/ccid.c | 101 +- net/dccp/ccid.h | 113 +-- net/dccp/ccids/Kconfig | 30 +- net/dccp/ccids/ccid2.c | 622 +++++++----- net/dccp/ccids/ccid2.h | 63 +- net/dccp/ccids/ccid3.c | 762 +++++++++------ net/dccp/ccids/ccid3.h | 153 +-- net/dccp/ccids/lib/loss_interval.c | 30 +- net/dccp/ccids/lib/loss_interval.h | 4 +- net/dccp/ccids/lib/packet_history.c | 282 +++--- net/dccp/ccids/lib/packet_history.h | 78 +- net/dccp/ccids/lib/tfrc.h | 16 - net/dccp/ccids/lib/tfrc_equation.c | 29 +- net/dccp/dccp.h | 104 +- net/dccp/diag.c | 2 +- net/dccp/feat.c | 1805 +++++++++-------------------------- net/dccp/feat.h | 144 +-- net/dccp/input.c | 164 ++-- net/dccp/ipv4.c | 4 +- net/dccp/ipv6.c | 4 +- net/dccp/minisocks.c | 87 +- net/dccp/options.c | 341 ++++--- net/dccp/output.c | 279 ++---- net/dccp/probe.c | 75 +- net/dccp/proto.c | 281 ++---- net/dccp/qpolicy.c | 137 --- net/dccp/sysctl.c | 64 +- net/dccp/timer.c | 42 +- net/ipv4/tcp_input.c | 17 +- 36 files changed, 2884 insertions(+), 3971 deletions(-) delete mode 100644 net/dccp/qpolicy.c (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index fcfc12534428..39131a3c78f8 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -45,25 +45,6 @@ http://linux-net.osdl.org/index.php/DCCP_Testing#Experimental_DCCP_source_tree Socket options ============== -DCCP_SOCKOPT_QPOLICY_ID sets the dequeuing policy for outgoing packets. It takes -a policy ID as argument and can only be set before the connection (i.e. changes -during an established connection are not supported). Currently, two policies are -defined: the "simple" policy (DCCPQ_POLICY_SIMPLE), which does nothing special, -and a priority-based variant (DCCPQ_POLICY_PRIO). The latter allows to pass an -u32 priority value as ancillary data to sendmsg(), where higher numbers indicate -a higher packet priority (similar to SO_PRIORITY). This ancillary data needs to -be formatted using a cmsg(3) message header filled in as follows: - cmsg->cmsg_level = SOL_DCCP; - cmsg->cmsg_type = DCCP_SCM_PRIORITY; - cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); /* or CMSG_LEN(4) */ - -DCCP_SOCKOPT_QPOLICY_TXQLEN sets the maximum length of the output queue. A zero -value is always interpreted as unbounded queue length. If different from zero, -the interpretation of this parameter depends on the current dequeuing policy -(see above): the "simple" policy will enforce a fixed queue size by returning -EAGAIN, whereas the "prio" policy enforces a fixed queue length by dropping the -lowest-priority packet first. The default value for this parameter is -initialised from /proc/sys/net/dccp/default/tx_qlen. DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of service codes (RFC 4340, sec. 8.1.2); if this socket option is not set, @@ -76,24 +57,6 @@ can be set before calling bind(). DCCP_SOCKOPT_GET_CUR_MPS is read-only and retrieves the current maximum packet size (application payload size) in bytes, see RFC 4340, section 14. -DCCP_SOCKOPT_AVAILABLE_CCIDS is also read-only and returns the list of CCIDs -supported by the endpoint (see include/linux/dccp.h for symbolic constants). -The caller needs to provide a sufficiently large (> 2) array of type uint8_t. - -DCCP_SOCKOPT_CCID is write-only and sets both the TX and RX CCIDs at the same -time, combining the operation of the next two socket options. This option is -preferrable over the latter two, since often applications will use the same -type of CCID for both directions; and mixed use of CCIDs is not currently well -understood. This socket option takes as argument at least one uint8_t value, or -an array of uint8_t values, which must match available CCIDS (see above). CCIDs -must be registered on the socket before calling connect() or listen(). - -DCCP_SOCKOPT_TX_CCID is read/write. It returns the current CCID (if set) or sets -the preference list for the TX CCID, using the same format as DCCP_SOCKOPT_CCID. -Please note that the getsockopt argument type here is `int', not uint8_t. - -DCCP_SOCKOPT_RX_CCID is analogous to DCCP_SOCKOPT_TX_CCID, but for the RX CCID. - DCCP_SOCKOPT_SERVER_TIMEWAIT enables the server (listening socket) to hold timewait state when closing the connection (RFC 4340, 8.3). The usual case is that the closing server sends a CloseReq, whereupon the client holds timewait @@ -152,16 +115,23 @@ retries2 importance for retransmitted acknowledgments and feature negotiation, data packets are never retransmitted. Analogue of tcp_retries2. +send_ndp = 1 + Whether or not to send NDP count options (sec. 7.7.2). + +send_ackvec = 1 + Whether or not to send Ack Vector options (sec. 11.5). + +ack_ratio = 2 + The default Ack Ratio (sec. 11.3) to use. + tx_ccid = 2 - Default CCID for the sender-receiver half-connection. Depending on the - choice of CCID, the Send Ack Vector feature is enabled automatically. + Default CCID for the sender-receiver half-connection. rx_ccid = 2 - Default CCID for the receiver-sender half-connection; see tx_ccid. + Default CCID for the receiver-sender half-connection. seq_window = 100 - The initial sequence window (sec. 7.5.2) of the sender. This influences - the local ackno validity and the remote seqno validity windows (7.5.1). + The initial sequence window (sec. 7.5.2). tx_qlen = 5 The size of the transmit buffer in packets. A value of 0 corresponds diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 010e2d87ed75..6080449fbec9 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -165,13 +165,9 @@ enum { DCCPO_TIMESTAMP_ECHO = 42, DCCPO_ELAPSED_TIME = 43, DCCPO_MAX = 45, - DCCPO_MIN_RX_CCID_SPECIFIC = 128, /* from sender to receiver */ - DCCPO_MAX_RX_CCID_SPECIFIC = 191, - DCCPO_MIN_TX_CCID_SPECIFIC = 192, /* from receiver to sender */ - DCCPO_MAX_TX_CCID_SPECIFIC = 255, + DCCPO_MIN_CCID_SPECIFIC = 128, + DCCPO_MAX_CCID_SPECIFIC = 255, }; -/* maximum size of a single TLV-encoded DCCP option (sans type/len bytes) */ -#define DCCP_SINGLE_OPT_MAXLEN 253 /* DCCP CCIDS */ enum { @@ -180,36 +176,27 @@ enum { }; /* DCCP features (RFC 4340 section 6.4) */ -enum dccp_feature_numbers { +enum { DCCPF_RESERVED = 0, DCCPF_CCID = 1, - DCCPF_SHORT_SEQNOS = 2, + DCCPF_SHORT_SEQNOS = 2, /* XXX: not yet implemented */ DCCPF_SEQUENCE_WINDOW = 3, - DCCPF_ECN_INCAPABLE = 4, + DCCPF_ECN_INCAPABLE = 4, /* XXX: not yet implemented */ DCCPF_ACK_RATIO = 5, DCCPF_SEND_ACK_VECTOR = 6, DCCPF_SEND_NDP_COUNT = 7, DCCPF_MIN_CSUM_COVER = 8, - DCCPF_DATA_CHECKSUM = 9, + DCCPF_DATA_CHECKSUM = 9, /* XXX: not yet implemented */ /* 10-127 reserved */ DCCPF_MIN_CCID_SPECIFIC = 128, - DCCPF_SEND_LEV_RATE = 192, /* RFC 4342, sec. 8.4 */ DCCPF_MAX_CCID_SPECIFIC = 255, }; -/* DCCP socket control message types for cmsg */ -enum dccp_cmsg_type { - DCCP_SCM_PRIORITY = 1, - DCCP_SCM_QPOLICY_MAX = 0xFFFF, - /* ^-- Up to here reserved exclusively for qpolicy parameters */ - DCCP_SCM_MAX -}; - -/* DCCP priorities for outgoing/queued packets */ -enum dccp_packet_dequeueing_policy { - DCCPQ_POLICY_SIMPLE, - DCCPQ_POLICY_PRIO, - DCCPQ_POLICY_MAX +/* this structure is argument to DCCP_SOCKOPT_CHANGE_X */ +struct dccp_so_feat { + __u8 dccpsf_feat; + __u8 __user *dccpsf_val; + __u8 dccpsf_len; }; /* DCCP socket options */ @@ -221,12 +208,6 @@ enum dccp_packet_dequeueing_policy { #define DCCP_SOCKOPT_SERVER_TIMEWAIT 6 #define DCCP_SOCKOPT_SEND_CSCOV 10 #define DCCP_SOCKOPT_RECV_CSCOV 11 -#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 -#define DCCP_SOCKOPT_CCID 13 -#define DCCP_SOCKOPT_TX_CCID 14 -#define DCCP_SOCKOPT_RX_CCID 15 -#define DCCP_SOCKOPT_QPOLICY_ID 16 -#define DCCP_SOCKOPT_QPOLICY_TXQLEN 17 #define DCCP_SOCKOPT_CCID_RX_INFO 128 #define DCCP_SOCKOPT_CCID_TX_INFO 192 @@ -374,13 +355,62 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) return __dccp_hdr_len(dccp_hdr(skb)); } + +/* initial values for each feature */ +#define DCCPF_INITIAL_SEQUENCE_WINDOW 100 +#define DCCPF_INITIAL_ACK_RATIO 2 +#define DCCPF_INITIAL_CCID DCCPC_CCID2 +#define DCCPF_INITIAL_SEND_ACK_VECTOR 1 +/* FIXME: for now we're default to 1 but it should really be 0 */ +#define DCCPF_INITIAL_SEND_NDP_COUNT 1 + +/** + * struct dccp_minisock - Minimal DCCP connection representation + * + * Will be used to pass the state from dccp_request_sock to dccp_sock. + * + * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) + * @dccpms_ccid - Congestion Control Id (CCID) (section 10) + * @dccpms_send_ack_vector - Send Ack Vector Feature (section 11.5) + * @dccpms_send_ndp_count - Send NDP Count Feature (7.7.2) + * @dccpms_ack_ratio - Ack Ratio Feature (section 11.3) + * @dccpms_pending - List of features being negotiated + * @dccpms_conf - + */ +struct dccp_minisock { + __u64 dccpms_sequence_window; + __u8 dccpms_rx_ccid; + __u8 dccpms_tx_ccid; + __u8 dccpms_send_ack_vector; + __u8 dccpms_send_ndp_count; + __u8 dccpms_ack_ratio; + struct list_head dccpms_pending; + struct list_head dccpms_conf; +}; + +struct dccp_opt_conf { + __u8 *dccpoc_val; + __u8 dccpoc_len; +}; + +struct dccp_opt_pend { + struct list_head dccpop_node; + __u8 dccpop_type; + __u8 dccpop_feat; + __u8 *dccpop_val; + __u8 dccpop_len; + int dccpop_conf; + struct dccp_opt_conf *dccpop_sc; +}; + +extern void dccp_minisock_init(struct dccp_minisock *dmsk); + /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from * @dreq_iss: initial sequence number sent on the Response (RFC 4340, 7.1) * @dreq_isr: initial sequence number received on the Request * @dreq_service: service code present on the Request (there is just one) - * @dreq_featneg: feature negotiation options for this connection * The following two fields are analogous to the ones in dccp_sock: * @dreq_timestamp_echo: last received timestamp to echo (13.1) * @dreq_timestamp_echo: the time of receiving the last @dreq_timestamp_echo @@ -390,7 +420,6 @@ struct dccp_request_sock { __u64 dreq_iss; __u64 dreq_isr; __be32 dreq_service; - struct list_head dreq_featneg; __u32 dreq_timestamp_echo; __u32 dreq_timestamp_time; }; @@ -462,28 +491,21 @@ struct dccp_ackvec; * @dccps_timestamp_time - time of receiving latest @dccps_timestamp_echo * @dccps_l_ack_ratio - feature-local Ack Ratio * @dccps_r_ack_ratio - feature-remote Ack Ratio - * @dccps_l_seq_win - local Sequence Window (influences ack number validity) - * @dccps_r_seq_win - remote Sequence Window (influences seq number validity) * @dccps_pcslen - sender partial checksum coverage (via sockopt) * @dccps_pcrlen - receiver partial checksum coverage (via sockopt) - * @dccps_send_ndp_count - local Send NDP Count feature (7.7.2) * @dccps_ndp_count - number of Non Data Packets since last data packet * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) * @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4) - * @dccps_featneg - tracks feature-negotiation state (mostly during handshake) + * @dccps_minisock - associated minisock (accessed via dccp_msk) * @dccps_hc_rx_ackvec - rx half connection ack vector * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) * @dccps_hc_tx_ccid - CCID used for the sender (or sending half-connection) * @dccps_options_received - parsed set of retrieved options - * @dccps_qpolicy - TX dequeueing policy, one of %dccp_packet_dequeueing_policy - * @dccps_tx_qlen - maximum length of the TX queue * @dccps_role - role of this sock, one of %dccp_role * @dccps_hc_rx_insert_options - receiver wants to add options when acking * @dccps_hc_tx_insert_options - sender wants to add options when sending * @dccps_server_timewait - server holds timewait state on close (RFC 4340, 8.3) - * @dccps_sync_scheduled - flag which signals "send out-of-band message soon" - * @dccps_xmitlet - tasklet scheduled by the TX CCID to dequeue data packets - * @dccps_xmit_timer - used by the TX CCID to delay sending (rate-based pacing) + * @dccps_xmit_timer - timer for when CCID is not ready to send * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs) */ struct dccp_sock { @@ -507,26 +529,19 @@ struct dccp_sock { __u32 dccps_timestamp_time; __u16 dccps_l_ack_ratio; __u16 dccps_r_ack_ratio; - __u64 dccps_l_seq_win:48; - __u64 dccps_r_seq_win:48; - __u8 dccps_pcslen:4; - __u8 dccps_pcrlen:4; - __u8 dccps_send_ndp_count:1; + __u16 dccps_pcslen; + __u16 dccps_pcrlen; __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; - struct list_head dccps_featneg; + struct dccp_minisock dccps_minisock; struct dccp_ackvec *dccps_hc_rx_ackvec; struct ccid *dccps_hc_rx_ccid; struct ccid *dccps_hc_tx_ccid; struct dccp_options_received dccps_options_received; - __u8 dccps_qpolicy; - __u32 dccps_tx_qlen; enum dccp_role dccps_role:2; __u8 dccps_hc_rx_insert_options:1; __u8 dccps_hc_tx_insert_options:1; __u8 dccps_server_timewait:1; - __u8 dccps_sync_scheduled:1; - struct tasklet_struct dccps_xmitlet; struct timer_list dccps_xmit_timer; }; @@ -535,6 +550,11 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk) return (struct dccp_sock *)sk; } +static inline struct dccp_minisock *dccp_msk(const struct sock *sk) +{ + return (struct dccp_minisock *)&dccp_sk(sk)->dccps_minisock; +} + static inline const char *dccp_role(const struct sock *sk) { switch (dccp_sk(sk)->dccps_role) { diff --git a/include/net/tcp.h b/include/net/tcp.h index 6bc4b8148ca0..8983386356a5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -782,21 +782,6 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk) /* Use define here intentionally to get WARN_ON location shown at the caller */ #define tcp_verify_left_out(tp) WARN_ON(tcp_left_out(tp) > tp->packets_out) -/* - * Convert RFC3390 larger initial windows into an equivalent number of packets. - * - * John Heffner states: - * - * The RFC specifies a window of no more than 4380 bytes - * unless 2*MSS > 4380. Reading the pseudocode in the RFC - * is a bit misleading because they use a clamp at 4380 bytes - * rather than a multiplier in the relevant range. - */ -static inline u32 rfc3390_bytes_to_packets(const u32 bytes) -{ - return bytes <= 1095 ? 4 : (bytes > 1460 ? 2 : 3); -} - extern void tcp_enter_cwr(struct sock *sk, const int set_ssthresh); extern __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst); diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig index 206c16ad9c3c..7aa2a7acc7ec 100644 --- a/net/dccp/Kconfig +++ b/net/dccp/Kconfig @@ -25,6 +25,9 @@ config INET_DCCP_DIAG def_tristate y if (IP_DCCP = y && INET_DIAG = y) def_tristate m +config IP_DCCP_ACKVEC + bool + source "net/dccp/ccids/Kconfig" menu "DCCP Kernel Hacking" diff --git a/net/dccp/Makefile b/net/dccp/Makefile index 0c1c9af2bf7e..f4f8793aafff 100644 --- a/net/dccp/Makefile +++ b/net/dccp/Makefile @@ -1,7 +1,6 @@ obj-$(CONFIG_IP_DCCP) += dccp.o dccp_ipv4.o -dccp-y := ccid.o feat.o input.o minisocks.o options.o \ - qpolicy.o output.o proto.o timer.o ackvec.o +dccp-y := ccid.o feat.o input.o minisocks.o options.o output.o proto.o timer.o dccp_ipv4-y := ipv4.o @@ -9,6 +8,8 @@ dccp_ipv4-y := ipv4.o obj-$(subst y,$(CONFIG_IP_DCCP),$(CONFIG_IPV6)) += dccp_ipv6.o dccp_ipv6-y := ipv6.o +dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o + obj-$(CONFIG_INET_DCCP_DIAG) += dccp_diag.o obj-$(CONFIG_NET_DCCPPROBE) += dccp_probe.o diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c index 41819848bdda..1e8be246ad15 100644 --- a/net/dccp/ackvec.c +++ b/net/dccp/ackvec.c @@ -1,375 +1,445 @@ /* * net/dccp/ackvec.c * - * An implementation of Ack Vectors for the DCCP protocol - * Copyright (c) 2007 University of Aberdeen, Scotland, UK + * An implementation of the DCCP protocol * Copyright (c) 2005 Arnaldo Carvalho de Melo * * 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; version 2 of the License; */ + +#include "ackvec.h" #include "dccp.h" + +#include +#include +#include #include +#include #include +#include + static struct kmem_cache *dccp_ackvec_slab; static struct kmem_cache *dccp_ackvec_record_slab; -struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority) +static struct dccp_ackvec_record *dccp_ackvec_record_new(void) { - struct dccp_ackvec *av = kmem_cache_zalloc(dccp_ackvec_slab, priority); + struct dccp_ackvec_record *avr = + kmem_cache_alloc(dccp_ackvec_record_slab, GFP_ATOMIC); - if (av != NULL) { - av->av_buf_head = av->av_buf_tail = DCCPAV_MAX_ACKVEC_LEN - 1; - INIT_LIST_HEAD(&av->av_records); - } - return av; + if (avr != NULL) + INIT_LIST_HEAD(&avr->avr_node); + + return avr; } -static void dccp_ackvec_purge_records(struct dccp_ackvec *av) +static void dccp_ackvec_record_delete(struct dccp_ackvec_record *avr) { - struct dccp_ackvec_record *cur, *next; - - list_for_each_entry_safe(cur, next, &av->av_records, avr_node) - kmem_cache_free(dccp_ackvec_record_slab, cur); - INIT_LIST_HEAD(&av->av_records); + if (unlikely(avr == NULL)) + return; + /* Check if deleting a linked record */ + WARN_ON(!list_empty(&avr->avr_node)); + kmem_cache_free(dccp_ackvec_record_slab, avr); } -void dccp_ackvec_free(struct dccp_ackvec *av) +static void dccp_ackvec_insert_avr(struct dccp_ackvec *av, + struct dccp_ackvec_record *avr) { - if (likely(av != NULL)) { - dccp_ackvec_purge_records(av); - kmem_cache_free(dccp_ackvec_slab, av); + /* + * AVRs are sorted by seqno. Since we are sending them in order, we + * just add the AVR at the head of the list. + * -sorbo. + */ + if (!list_empty(&av->av_records)) { + const struct dccp_ackvec_record *head = + list_entry(av->av_records.next, + struct dccp_ackvec_record, + avr_node); + BUG_ON(before48(avr->avr_ack_seqno, head->avr_ack_seqno)); } + + list_add(&avr->avr_node, &av->av_records); } -/** - * dccp_ackvec_update_records - Record information about sent Ack Vectors - * @av: Ack Vector records to update - * @seqno: Sequence number of the packet carrying the Ack Vector just sent - * @nonce_sum: The sum of all buffer nonces contained in the Ack Vector - */ -int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seqno, u8 nonce_sum) +int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) { + struct dccp_sock *dp = dccp_sk(sk); + struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; + /* Figure out how many options do we need to represent the ackvec */ + const u16 nr_opts = DIV_ROUND_UP(av->av_vec_len, DCCP_MAX_ACKVEC_OPT_LEN); + u16 len = av->av_vec_len + 2 * nr_opts, i; + u32 elapsed_time; + const unsigned char *tail, *from; + unsigned char *to; struct dccp_ackvec_record *avr; + suseconds_t delta; + + if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) + return -1; + + delta = ktime_us_delta(ktime_get_real(), av->av_time); + elapsed_time = delta / 10; - avr = kmem_cache_alloc(dccp_ackvec_record_slab, GFP_ATOMIC); + if (elapsed_time != 0 && + dccp_insert_option_elapsed_time(sk, skb, elapsed_time)) + return -1; + + avr = dccp_ackvec_record_new(); if (avr == NULL) - return -ENOBUFS; + return -1; + + DCCP_SKB_CB(skb)->dccpd_opt_len += len; + + to = skb_push(skb, len); + len = av->av_vec_len; + from = av->av_buf + av->av_buf_head; + tail = av->av_buf + DCCP_MAX_ACKVEC_LEN; + + for (i = 0; i < nr_opts; ++i) { + int copylen = len; + + if (len > DCCP_MAX_ACKVEC_OPT_LEN) + copylen = DCCP_MAX_ACKVEC_OPT_LEN; + + *to++ = DCCPO_ACK_VECTOR_0; + *to++ = copylen + 2; + + /* Check if buf_head wraps */ + if (from + copylen > tail) { + const u16 tailsize = tail - from; + + memcpy(to, from, tailsize); + to += tailsize; + len -= tailsize; + copylen -= tailsize; + from = av->av_buf; + } + + memcpy(to, from, copylen); + from += copylen; + to += copylen; + len -= copylen; + } - avr->avr_ack_seqno = seqno; - avr->avr_ack_ptr = av->av_buf_head; - avr->avr_ack_ackno = av->av_buf_ackno; - avr->avr_ack_nonce = nonce_sum; - avr->avr_ack_runlen = dccp_ackvec_runlen(av->av_buf + av->av_buf_head); - /* - * When the buffer overflows, we keep no more than one record. This is - * the simplest way of disambiguating sender-Acks dating from before the - * overflow from sender-Acks which refer to after the overflow; a simple - * solution is preferable here since we are handling an exception. - */ - if (av->av_overflow) - dccp_ackvec_purge_records(av); /* - * Since GSS is incremented for each packet, the list is automatically - * arranged in descending order of @ack_seqno. + * From RFC 4340, A.2: + * + * For each acknowledgement it sends, the HC-Receiver will add an + * acknowledgement record. ack_seqno will equal the HC-Receiver + * sequence number it used for the ack packet; ack_ptr will equal + * buf_head; ack_ackno will equal buf_ackno; and ack_nonce will + * equal buf_nonce. */ - list_add(&avr->avr_node, &av->av_records); + avr->avr_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq; + avr->avr_ack_ptr = av->av_buf_head; + avr->avr_ack_ackno = av->av_buf_ackno; + avr->avr_ack_nonce = av->av_buf_nonce; + avr->avr_sent_len = av->av_vec_len; - dccp_pr_debug("Added Vector, ack_seqno=%llu, ack_ackno=%llu (rl=%u)\n", + dccp_ackvec_insert_avr(av, avr); + + dccp_pr_debug("%s ACK Vector 0, len=%d, ack_seqno=%llu, " + "ack_ackno=%llu\n", + dccp_role(sk), avr->avr_sent_len, (unsigned long long)avr->avr_ack_seqno, - (unsigned long long)avr->avr_ack_ackno, - avr->avr_ack_runlen); + (unsigned long long)avr->avr_ack_ackno); return 0; } -static struct dccp_ackvec_record *dccp_ackvec_lookup(struct list_head *av_list, - const u64 ackno) +struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority) { - struct dccp_ackvec_record *avr; - /* - * Exploit that records are inserted in descending order of sequence - * number, start with the oldest record first. If @ackno is `before' - * the earliest ack_ackno, the packet is too old to be considered. - */ - list_for_each_entry_reverse(avr, av_list, avr_node) { - if (avr->avr_ack_seqno == ackno) - return avr; - if (before48(ackno, avr->avr_ack_seqno)) - break; + struct dccp_ackvec *av = kmem_cache_alloc(dccp_ackvec_slab, priority); + + if (av != NULL) { + av->av_buf_head = DCCP_MAX_ACKVEC_LEN - 1; + av->av_buf_ackno = UINT48_MAX + 1; + av->av_buf_nonce = 0; + av->av_time = ktime_set(0, 0); + av->av_vec_len = 0; + INIT_LIST_HEAD(&av->av_records); } - return NULL; + + return av; } -/* - * Buffer index and length computation using modulo-buffersize arithmetic. - * Note that, as pointers move from right to left, head is `before' tail. - */ -static inline u16 __ackvec_idx_add(const u16 a, const u16 b) +void dccp_ackvec_free(struct dccp_ackvec *av) { - return (a + b) % DCCPAV_MAX_ACKVEC_LEN; + if (unlikely(av == NULL)) + return; + + if (!list_empty(&av->av_records)) { + struct dccp_ackvec_record *avr, *next; + + list_for_each_entry_safe(avr, next, &av->av_records, avr_node) { + list_del_init(&avr->avr_node); + dccp_ackvec_record_delete(avr); + } + } + + kmem_cache_free(dccp_ackvec_slab, av); } -static inline u16 __ackvec_idx_sub(const u16 a, const u16 b) +static inline u8 dccp_ackvec_state(const struct dccp_ackvec *av, + const u32 index) { - return __ackvec_idx_add(a, DCCPAV_MAX_ACKVEC_LEN - b); + return av->av_buf[index] & DCCP_ACKVEC_STATE_MASK; } -u16 dccp_ackvec_buflen(const struct dccp_ackvec *av) +static inline u8 dccp_ackvec_len(const struct dccp_ackvec *av, + const u32 index) { - if (unlikely(av->av_overflow)) - return DCCPAV_MAX_ACKVEC_LEN; - return __ackvec_idx_sub(av->av_buf_tail, av->av_buf_head); + return av->av_buf[index] & DCCP_ACKVEC_LEN_MASK; } -/** - * dccp_ackvec_update_old - Update previous state as per RFC 4340, 11.4.1 - * @av: non-empty buffer to update - * @distance: negative or zero distance of @seqno from buf_ackno downward - * @seqno: the (old) sequence number whose record is to be updated - * @state: state in which packet carrying @seqno was received +/* + * If several packets are missing, the HC-Receiver may prefer to enter multiple + * bytes with run length 0, rather than a single byte with a larger run length; + * this simplifies table updates if one of the missing packets arrives. */ -static void dccp_ackvec_update_old(struct dccp_ackvec *av, s64 distance, - u64 seqno, enum dccp_ackvec_states state) +static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av, + const unsigned int packets, + const unsigned char state) { - u16 ptr = av->av_buf_head; + unsigned int gap; + long new_head; - BUG_ON(distance > 0); - if (unlikely(dccp_ackvec_is_empty(av))) - return; + if (av->av_vec_len + packets > DCCP_MAX_ACKVEC_LEN) + return -ENOBUFS; - do { - u8 runlen = dccp_ackvec_runlen(av->av_buf + ptr); + gap = packets - 1; + new_head = av->av_buf_head - packets; - if (distance + runlen >= 0) { - /* - * Only update the state if packet has not been received - * yet. This is OK as per the second table in RFC 4340, - * 11.4.1; i.e. here we are using the following table: - * RECEIVED - * 0 1 3 - * S +---+---+---+ - * T 0 | 0 | 0 | 0 | - * O +---+---+---+ - * R 1 | 1 | 1 | 1 | - * E +---+---+---+ - * D 3 | 0 | 1 | 3 | - * +---+---+---+ - * The "Not Received" state was set by reserve_seats(). - */ - if (av->av_buf[ptr] == DCCPAV_NOT_RECEIVED) - av->av_buf[ptr] = state; - else - dccp_pr_debug("Not changing %llu state to %u\n", - (unsigned long long)seqno, state); - break; + if (new_head < 0) { + if (gap > 0) { + memset(av->av_buf, DCCP_ACKVEC_STATE_NOT_RECEIVED, + gap + new_head + 1); + gap = -new_head; } + new_head += DCCP_MAX_ACKVEC_LEN; + } - distance += runlen + 1; - ptr = __ackvec_idx_add(ptr, 1); + av->av_buf_head = new_head; - } while (ptr != av->av_buf_tail); -} + if (gap > 0) + memset(av->av_buf + av->av_buf_head + 1, + DCCP_ACKVEC_STATE_NOT_RECEIVED, gap); -/* Mark @num entries after buf_head as "Not yet received". */ -static void dccp_ackvec_reserve_seats(struct dccp_ackvec *av, u16 num) -{ - u16 start = __ackvec_idx_add(av->av_buf_head, 1), - len = DCCPAV_MAX_ACKVEC_LEN - start; - - /* check for buffer wrap-around */ - if (num > len) { - memset(av->av_buf + start, DCCPAV_NOT_RECEIVED, len); - start = 0; - num -= len; - } - if (num) - memset(av->av_buf + start, DCCPAV_NOT_RECEIVED, num); + av->av_buf[av->av_buf_head] = state; + av->av_vec_len += packets; + return 0; } -/** - * dccp_ackvec_add_new - Record one or more new entries in Ack Vector buffer - * @av: container of buffer to update (can be empty or non-empty) - * @num_packets: number of packets to register (must be >= 1) - * @seqno: sequence number of the first packet in @num_packets - * @state: state in which packet carrying @seqno was received +/* + * Implements the RFC 4340, Appendix A */ -static void dccp_ackvec_add_new(struct dccp_ackvec *av, u32 num_packets, - u64 seqno, enum dccp_ackvec_states state) +int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, + const u64 ackno, const u8 state) { - u32 num_cells = num_packets; + /* + * Check at the right places if the buffer is full, if it is, tell the + * caller to start dropping packets till the HC-Sender acks our ACK + * vectors, when we will free up space in av_buf. + * + * We may well decide to do buffer compression, etc, but for now lets + * just drop. + * + * From Appendix A.1.1 (`New Packets'): + * + * Of course, the circular buffer may overflow, either when the + * HC-Sender is sending data at a very high rate, when the + * HC-Receiver's acknowledgements are not reaching the HC-Sender, + * or when the HC-Sender is forgetting to acknowledge those acks + * (so the HC-Receiver is unable to clean up old state). In this + * case, the HC-Receiver should either compress the buffer (by + * increasing run lengths when possible), transfer its state to + * a larger buffer, or, as a last resort, drop all received + * packets, without processing them whatsoever, until its buffer + * shrinks again. + */ - if (num_packets > DCCPAV_BURST_THRESH) { - u32 lost_packets = num_packets - 1; + /* See if this is the first ackno being inserted */ + if (av->av_vec_len == 0) { + av->av_buf[av->av_buf_head] = state; + av->av_vec_len = 1; + } else if (after48(ackno, av->av_buf_ackno)) { + const u64 delta = dccp_delta_seqno(av->av_buf_ackno, ackno); - DCCP_WARN("Warning: large burst loss (%u)\n", lost_packets); /* - * We received 1 packet and have a loss of size "num_packets-1" - * which we squeeze into num_cells-1 rather than reserving an - * entire byte for each lost packet. - * The reason is that the vector grows in O(burst_length); when - * it grows too large there will no room left for the payload. - * This is a trade-off: if a few packets out of the burst show - * up later, their state will not be changed; it is simply too - * costly to reshuffle/reallocate/copy the buffer each time. - * Should such problems persist, we will need to switch to a - * different underlying data structure. + * Look if the state of this packet is the same as the + * previous ackno and if so if we can bump the head len. */ - for (num_packets = num_cells = 1; lost_packets; ++num_cells) { - u8 len = min(lost_packets, (u32)DCCPAV_MAX_RUNLEN); - - av->av_buf_head = __ackvec_idx_sub(av->av_buf_head, 1); - av->av_buf[av->av_buf_head] = DCCPAV_NOT_RECEIVED | len; + if (delta == 1 && + dccp_ackvec_state(av, av->av_buf_head) == state && + dccp_ackvec_len(av, av->av_buf_head) < DCCP_ACKVEC_LEN_MASK) + av->av_buf[av->av_buf_head]++; + else if (dccp_ackvec_set_buf_head_state(av, delta, state)) + return -ENOBUFS; + } else { + /* + * A.1.2. Old Packets + * + * When a packet with Sequence Number S <= buf_ackno + * arrives, the HC-Receiver will scan the table for + * the byte corresponding to S. (Indexing structures + * could reduce the complexity of this scan.) + */ + u64 delta = dccp_delta_seqno(ackno, av->av_buf_ackno); + u32 index = av->av_buf_head; - lost_packets -= len; + while (1) { + const u8 len = dccp_ackvec_len(av, index); + const u8 av_state = dccp_ackvec_state(av, index); + /* + * valid packets not yet in av_buf have a reserved + * entry, with a len equal to 0. + */ + if (av_state == DCCP_ACKVEC_STATE_NOT_RECEIVED && + len == 0 && delta == 0) { /* Found our + reserved seat! */ + dccp_pr_debug("Found %llu reserved seat!\n", + (unsigned long long)ackno); + av->av_buf[index] = state; + goto out; + } + /* len == 0 means one packet */ + if (delta < len + 1) + goto out_duplicate; + + delta -= len + 1; + if (++index == DCCP_MAX_ACKVEC_LEN) + index = 0; } } - if (num_cells + dccp_ackvec_buflen(av) >= DCCPAV_MAX_ACKVEC_LEN) { - DCCP_CRIT("Ack Vector buffer overflow: dropping old entries\n"); - av->av_overflow = true; - } - - av->av_buf_head = __ackvec_idx_sub(av->av_buf_head, num_packets); - if (av->av_overflow) - av->av_buf_tail = av->av_buf_head; - - av->av_buf[av->av_buf_head] = state; - av->av_buf_ackno = seqno; + av->av_buf_ackno = ackno; + av->av_time = ktime_get_real(); +out: + return 0; - if (num_packets > 1) - dccp_ackvec_reserve_seats(av, num_packets - 1); +out_duplicate: + /* Duplicate packet */ + dccp_pr_debug("Received a dup or already considered lost " + "packet: %llu\n", (unsigned long long)ackno); + return -EILSEQ; } -/** - * dccp_ackvec_input - Register incoming packet in the buffer - */ -void dccp_ackvec_input(struct dccp_ackvec *av, struct sk_buff *skb) +static void dccp_ackvec_throw_record(struct dccp_ackvec *av, + struct dccp_ackvec_record *avr) { - u64 seqno = DCCP_SKB_CB(skb)->dccpd_seq; - enum dccp_ackvec_states state = DCCPAV_RECEIVED; + struct dccp_ackvec_record *next; - if (dccp_ackvec_is_empty(av)) { - dccp_ackvec_add_new(av, 1, seqno, state); - av->av_tail_ackno = seqno; + /* sort out vector length */ + if (av->av_buf_head <= avr->avr_ack_ptr) + av->av_vec_len = avr->avr_ack_ptr - av->av_buf_head; + else + av->av_vec_len = DCCP_MAX_ACKVEC_LEN - 1 - + av->av_buf_head + avr->avr_ack_ptr; - } else { - s64 num_packets = dccp_delta_seqno(av->av_buf_ackno, seqno); - u8 *current_head = av->av_buf + av->av_buf_head; - - if (num_packets == 1 && - dccp_ackvec_state(current_head) == state && - dccp_ackvec_runlen(current_head) < DCCPAV_MAX_RUNLEN) { + /* free records */ + list_for_each_entry_safe_from(avr, next, &av->av_records, avr_node) { + list_del_init(&avr->avr_node); + dccp_ackvec_record_delete(avr); + } +} - *current_head += 1; - av->av_buf_ackno = seqno; +void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk, + const u64 ackno) +{ + struct dccp_ackvec_record *avr; - } else if (num_packets > 0) { - dccp_ackvec_add_new(av, num_packets, seqno, state); - } else { - dccp_ackvec_update_old(av, num_packets, seqno, state); - } + /* + * If we traverse backwards, it should be faster when we have large + * windows. We will be receiving ACKs for stuff we sent a while back + * -sorbo. + */ + list_for_each_entry_reverse(avr, &av->av_records, avr_node) { + if (ackno == avr->avr_ack_seqno) { + dccp_pr_debug("%s ACK packet 0, len=%d, ack_seqno=%llu, " + "ack_ackno=%llu, ACKED!\n", + dccp_role(sk), 1, + (unsigned long long)avr->avr_ack_seqno, + (unsigned long long)avr->avr_ack_ackno); + dccp_ackvec_throw_record(av, avr); + break; + } else if (avr->avr_ack_seqno > ackno) + break; /* old news */ } } -/** - * dccp_ackvec_clear_state - Perform house-keeping / garbage-collection - * This routine is called when the peer acknowledges the receipt of Ack Vectors - * up to and including @ackno. While based on on section A.3 of RFC 4340, here - * are additional precautions to prevent corrupted buffer state. In particular, - * we use tail_ackno to identify outdated records; it always marks the earliest - * packet of group (2) in 11.4.2. - */ -void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno) - { - struct dccp_ackvec_record *avr, *next; - u8 runlen_now, eff_runlen; - s64 delta; +static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, + struct sock *sk, u64 *ackno, + const unsigned char len, + const unsigned char *vector) +{ + unsigned char i; + struct dccp_ackvec_record *avr; - avr = dccp_ackvec_lookup(&av->av_records, ackno); - if (avr == NULL) + /* Check if we actually sent an ACK vector */ + if (list_empty(&av->av_records)) return; - /* - * Deal with outdated acknowledgments: this arises when e.g. there are - * several old records and the acks from the peer come in slowly. In - * that case we may still have records that pre-date tail_ackno. - */ - delta = dccp_delta_seqno(av->av_tail_ackno, avr->avr_ack_ackno); - if (delta < 0) - goto free_records; - /* - * Deal with overlapping Ack Vectors: don't subtract more than the - * number of packets between tail_ackno and ack_ackno. - */ - eff_runlen = delta < avr->avr_ack_runlen ? delta : avr->avr_ack_runlen; - runlen_now = dccp_ackvec_runlen(av->av_buf + avr->avr_ack_ptr); + i = len; /* - * The run length of Ack Vector cells does not decrease over time. If - * the run length is the same as at the time the Ack Vector was sent, we - * free the ack_ptr cell. That cell can however not be freed if the run - * length has increased: in this case we need to move the tail pointer - * backwards (towards higher indices), to its next-oldest neighbour. + * XXX + * I think it might be more efficient to work backwards. See comment on + * rcv_ackno. -sorbo. */ - if (runlen_now > eff_runlen) { + avr = list_entry(av->av_records.next, struct dccp_ackvec_record, avr_node); + while (i--) { + const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK; + u64 ackno_end_rl; - av->av_buf[avr->avr_ack_ptr] -= eff_runlen + 1; - av->av_buf_tail = __ackvec_idx_add(avr->avr_ack_ptr, 1); + dccp_set_seqno(&ackno_end_rl, *ackno - rl); - /* This move may not have cleared the overflow flag. */ - if (av->av_overflow) - av->av_overflow = (av->av_buf_head == av->av_buf_tail); - } else { - av->av_buf_tail = avr->avr_ack_ptr; /* - * We have made sure that avr points to a valid cell within the - * buffer. This cell is either older than head, or equals head - * (empty buffer): in both cases we no longer have any overflow. + * If our AVR sequence number is greater than the ack, go + * forward in the AVR list until it is not so. */ - av->av_overflow = 0; - } - - /* - * The peer has acknowledged up to and including ack_ackno. Hence the - * first packet in group (2) of 11.4.2 is the successor of ack_ackno. - */ - av->av_tail_ackno = ADD48(avr->avr_ack_ackno, 1); + list_for_each_entry_from(avr, &av->av_records, avr_node) { + if (!after48(avr->avr_ack_seqno, *ackno)) + goto found; + } + /* End of the av_records list, not found, exit */ + break; +found: + if (between48(avr->avr_ack_seqno, ackno_end_rl, *ackno)) { + const u8 state = *vector & DCCP_ACKVEC_STATE_MASK; + if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED) { + dccp_pr_debug("%s ACK vector 0, len=%d, " + "ack_seqno=%llu, ack_ackno=%llu, " + "ACKED!\n", + dccp_role(sk), len, + (unsigned long long) + avr->avr_ack_seqno, + (unsigned long long) + avr->avr_ack_ackno); + dccp_ackvec_throw_record(av, avr); + break; + } + /* + * If it wasn't received, continue scanning... we might + * find another one. + */ + } -free_records: - list_for_each_entry_safe_from(avr, next, &av->av_records, avr_node) { - list_del(&avr->avr_node); - kmem_cache_free(dccp_ackvec_record_slab, avr); + dccp_set_seqno(ackno, ackno_end_rl - 1); + ++vector; } } -/* - * Routines to keep track of Ack Vectors received in an skb - */ -int dccp_ackvec_parsed_add(struct list_head *head, u8 *vec, u8 len, u8 nonce) +int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, + u64 *ackno, const u8 opt, const u8 *value, const u8 len) { - struct dccp_ackvec_parsed *new = kmalloc(sizeof(*new), GFP_ATOMIC); - - if (new == NULL) - return -ENOBUFS; - new->vec = vec; - new->len = len; - new->nonce = nonce; + if (len > DCCP_MAX_ACKVEC_OPT_LEN) + return -1; - list_add_tail(&new->node, head); + /* dccp_ackvector_print(DCCP_SKB_CB(skb)->dccpd_ack_seq, value, len); */ + dccp_ackvec_check_rcv_ackvector(dccp_sk(sk)->dccps_hc_rx_ackvec, sk, + ackno, len, value); return 0; } -EXPORT_SYMBOL_GPL(dccp_ackvec_parsed_add); - -void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks) -{ - struct dccp_ackvec_parsed *cur, *next; - - list_for_each_entry_safe(cur, next, parsed_chunks, node) - kfree(cur); - INIT_LIST_HEAD(parsed_chunks); -} -EXPORT_SYMBOL_GPL(dccp_ackvec_parsed_cleanup); int __init dccp_ackvec_init(void) { @@ -379,9 +449,10 @@ int __init dccp_ackvec_init(void) if (dccp_ackvec_slab == NULL) goto out_err; - dccp_ackvec_record_slab = kmem_cache_create("dccp_ackvec_record", - sizeof(struct dccp_ackvec_record), - 0, SLAB_HWCACHE_ALIGN, NULL); + dccp_ackvec_record_slab = + kmem_cache_create("dccp_ackvec_record", + sizeof(struct dccp_ackvec_record), + 0, SLAB_HWCACHE_ALIGN, NULL); if (dccp_ackvec_record_slab == NULL) goto out_destroy_slab; diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h index 6cdca79a99f7..bcb64fb4acef 100644 --- a/net/dccp/ackvec.h +++ b/net/dccp/ackvec.h @@ -3,134 +3,156 @@ /* * net/dccp/ackvec.h * - * An implementation of Ack Vectors for the DCCP protocol - * Copyright (c) 2007 University of Aberdeen, Scotland, UK + * An implementation of the DCCP protocol * Copyright (c) 2005 Arnaldo Carvalho de Melo + * * 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 #include +#include #include #include -/* - * Ack Vector buffer space is static, in multiples of %DCCP_SINGLE_OPT_MAXLEN, - * the maximum size of a single Ack Vector. Setting %DCCPAV_NUM_ACKVECS to 1 - * will be sufficient for most cases of low Ack Ratios, using a value of 2 gives - * more headroom if Ack Ratio is higher or when the sender acknowledges slowly. - * The maximum value is bounded by the u16 types for indices and functions. - */ -#define DCCPAV_NUM_ACKVECS 2 -#define DCCPAV_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * DCCPAV_NUM_ACKVECS) - -/* Estimated minimum average Ack Vector length - used for updating MPS */ -#define DCCPAV_MIN_OPTLEN 16 - -/* Threshold for coping with large bursts of losses */ -#define DCCPAV_BURST_THRESH (DCCPAV_MAX_ACKVEC_LEN / 8) - -enum dccp_ackvec_states { - DCCPAV_RECEIVED = 0x00, - DCCPAV_ECN_MARKED = 0x40, - DCCPAV_RESERVED = 0x80, - DCCPAV_NOT_RECEIVED = 0xC0 -}; -#define DCCPAV_MAX_RUNLEN 0x3F +/* Read about the ECN nonce to see why it is 253 */ +#define DCCP_MAX_ACKVEC_OPT_LEN 253 +/* We can spread an ack vector across multiple options */ +#define DCCP_MAX_ACKVEC_LEN (DCCP_MAX_ACKVEC_OPT_LEN * 2) -static inline u8 dccp_ackvec_runlen(const u8 *cell) -{ - return *cell & DCCPAV_MAX_RUNLEN; -} +#define DCCP_ACKVEC_STATE_RECEIVED 0 +#define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6) +#define DCCP_ACKVEC_STATE_NOT_RECEIVED (3 << 6) -static inline u8 dccp_ackvec_state(const u8 *cell) -{ - return *cell & ~DCCPAV_MAX_RUNLEN; -} +#define DCCP_ACKVEC_STATE_MASK 0xC0 /* 11000000 */ +#define DCCP_ACKVEC_LEN_MASK 0x3F /* 00111111 */ -/** struct dccp_ackvec - Ack Vector main data structure +/** struct dccp_ackvec - ack vector + * + * This data structure is the one defined in RFC 4340, Appendix A. * - * This implements a fixed-size circular buffer within an array and is largely - * based on Appendix A of RFC 4340. + * @av_buf_head - circular buffer head + * @av_buf_tail - circular buffer tail + * @av_buf_ackno - ack # of the most recent packet acknowledgeable in the + * buffer (i.e. %av_buf_head) + * @av_buf_nonce - the one-bit sum of the ECN Nonces on all packets acked + * by the buffer with State 0 * - * @av_buf: circular buffer storage area - * @av_buf_head: head index; begin of live portion in @av_buf - * @av_buf_tail: tail index; first index _after_ the live portion in @av_buf - * @av_buf_ackno: highest seqno of acknowledgeable packet recorded in @av_buf - * @av_tail_ackno: lowest seqno of acknowledgeable packet recorded in @av_buf - * @av_buf_nonce: ECN nonce sums, each covering subsequent segments of up to - * %DCCP_SINGLE_OPT_MAXLEN cells in the live portion of @av_buf - * @av_overflow: if 1 then buf_head == buf_tail indicates buffer wraparound - * @av_records: list of %dccp_ackvec_record (Ack Vectors sent previously) + * Additionally, the HC-Receiver must keep some information about the + * Ack Vectors it has recently sent. For each packet sent carrying an + * Ack Vector, it remembers four variables: + * + * @av_records - list of dccp_ackvec_record + * @av_ack_nonce - the one-bit sum of the ECN Nonces for all State 0. + * + * @av_time - the time in usecs + * @av_buf - circular buffer of acknowledgeable packets */ struct dccp_ackvec { - u8 av_buf[DCCPAV_MAX_ACKVEC_LEN]; - u16 av_buf_head; - u16 av_buf_tail; - u64 av_buf_ackno:48; - u64 av_tail_ackno:48; - bool av_buf_nonce[DCCPAV_NUM_ACKVECS]; - u8 av_overflow:1; + u64 av_buf_ackno; struct list_head av_records; + ktime_t av_time; + u16 av_buf_head; + u16 av_vec_len; + u8 av_buf_nonce; + u8 av_ack_nonce; + u8 av_buf[DCCP_MAX_ACKVEC_LEN]; }; -/** struct dccp_ackvec_record - Records information about sent Ack Vectors +/** struct dccp_ackvec_record - ack vector record * - * These list entries define the additional information which the HC-Receiver - * keeps about recently-sent Ack Vectors; again refer to RFC 4340, Appendix A. + * ACK vector record as defined in Appendix A of spec. * - * @avr_node: the list node in @av_records - * @avr_ack_seqno: sequence number of the packet the Ack Vector was sent on - * @avr_ack_ackno: the Ack number that this record/Ack Vector refers to - * @avr_ack_ptr: pointer into @av_buf where this record starts - * @avr_ack_runlen: run length of @avr_ack_ptr at the time of sending - * @avr_ack_nonce: the sum of @av_buf_nonce's at the time this record was sent + * The list is sorted by avr_ack_seqno * - * The list as a whole is sorted in descending order by @avr_ack_seqno. + * @avr_node - node in av_records + * @avr_ack_seqno - sequence number of the packet this record was sent on + * @avr_ack_ackno - sequence number being acknowledged + * @avr_ack_ptr - pointer into av_buf where this record starts + * @avr_ack_nonce - av_ack_nonce at the time this record was sent + * @avr_sent_len - lenght of the record in av_buf */ struct dccp_ackvec_record { struct list_head avr_node; - u64 avr_ack_seqno:48; - u64 avr_ack_ackno:48; + u64 avr_ack_seqno; + u64 avr_ack_ackno; u16 avr_ack_ptr; - u8 avr_ack_runlen; - u8 avr_ack_nonce:1; + u16 avr_sent_len; + u8 avr_ack_nonce; }; -extern int dccp_ackvec_init(void); +struct sock; +struct sk_buff; + +#ifdef CONFIG_IP_DCCP_ACKVEC +extern int dccp_ackvec_init(void); extern void dccp_ackvec_exit(void); extern struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority); extern void dccp_ackvec_free(struct dccp_ackvec *av); -extern void dccp_ackvec_input(struct dccp_ackvec *av, struct sk_buff *skb); -extern int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8 sum); -extern void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno); -extern u16 dccp_ackvec_buflen(const struct dccp_ackvec *av); +extern int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, + const u64 ackno, const u8 state); + +extern void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, + struct sock *sk, const u64 ackno); +extern int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, + u64 *ackno, const u8 opt, + const u8 *value, const u8 len); -static inline bool dccp_ackvec_is_empty(const struct dccp_ackvec *av) +extern int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb); + +static inline int dccp_ackvec_pending(const struct dccp_ackvec *av) +{ + return av->av_vec_len; +} +#else /* CONFIG_IP_DCCP_ACKVEC */ +static inline int dccp_ackvec_init(void) { - return av->av_overflow == 0 && av->av_buf_head == av->av_buf_tail; + return 0; } -/** - * struct dccp_ackvec_parsed - Record offsets of Ack Vectors in skb - * @vec: start of vector (offset into skb) - * @len: length of @vec - * @nonce: whether @vec had an ECN nonce of 0 or 1 - * @node: FIFO - arranged in descending order of ack_ackno - * This structure is used by CCIDs to access Ack Vectors in a received skb. - */ -struct dccp_ackvec_parsed { - u8 *vec, - len, - nonce:1; - struct list_head node; -}; +static inline void dccp_ackvec_exit(void) +{ +} + +static inline struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority) +{ + return NULL; +} + +static inline void dccp_ackvec_free(struct dccp_ackvec *av) +{ +} + +static inline int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, + const u64 ackno, const u8 state) +{ + return -1; +} -extern int dccp_ackvec_parsed_add(struct list_head *head, - u8 *vec, u8 len, u8 nonce); -extern void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks); +static inline void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, + struct sock *sk, const u64 ackno) +{ +} + +static inline int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, + const u64 *ackno, const u8 opt, + const u8 *value, const u8 len) +{ + return -1; +} + +static inline int dccp_insert_option_ackvec(const struct sock *sk, + const struct sk_buff *skb) +{ + return -1; +} + +static inline int dccp_ackvec_pending(const struct dccp_ackvec *av) +{ + return 0; +} +#endif /* CONFIG_IP_DCCP_ACKVEC */ #endif /* _ACKVEC_H */ diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index e3fb52b4f5c6..4809753d12ae 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c @@ -13,13 +13,6 @@ #include "ccid.h" -static u8 builtin_ccids[] = { - DCCPC_CCID2, /* CCID2 is supported by default */ -#if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) - DCCPC_CCID3, -#endif -}; - static struct ccid_operations *ccids[CCID_MAX]; #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) static atomic_t ccids_lockct = ATOMIC_INIT(0); @@ -93,47 +86,6 @@ static void ccid_kmem_cache_destroy(struct kmem_cache *slab) } } -/* check that up to @array_len members in @ccid_array are supported */ -bool ccid_support_check(u8 const *ccid_array, u8 array_len) -{ - u8 i, j, found; - - for (i = 0, found = 0; i < array_len; i++, found = 0) { - for (j = 0; !found && j < ARRAY_SIZE(builtin_ccids); j++) - found = (ccid_array[i] == builtin_ccids[j]); - if (!found) - return false; - } - return true; -} - -/** - * ccid_get_builtin_ccids - Provide copy of `builtin' CCID array - * @ccid_array: pointer to copy into - * @array_len: value to return length into - * This function allocates memory - caller must see that it is freed after use. - */ -int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) -{ - *ccid_array = kmemdup(builtin_ccids, sizeof(builtin_ccids), gfp_any()); - if (*ccid_array == NULL) - return -ENOBUFS; - *array_len = ARRAY_SIZE(builtin_ccids); - return 0; -} - -int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, - char __user *optval, int __user *optlen) -{ - if (len < sizeof(builtin_ccids)) - return -EINVAL; - - if (put_user(sizeof(builtin_ccids), optlen) || - copy_to_user(optval, builtin_ccids, sizeof(builtin_ccids))) - return -EFAULT; - return 0; -} - int ccid_register(struct ccid_operations *ccid_ops) { int err = -ENOBUFS; @@ -196,41 +148,22 @@ int ccid_unregister(struct ccid_operations *ccid_ops) EXPORT_SYMBOL_GPL(ccid_unregister); -/** - * ccid_request_module - Pre-load CCID module for later use - * This should be called only from process context (e.g. during connection - * setup) and is necessary for later calls to ccid_new (typically in software - * interrupt), so that it has the modules available when they are needed. - */ -static int ccid_request_module(u8 id) -{ - if (!in_atomic()) { - ccids_read_lock(); - if (ccids[id] == NULL) { - ccids_read_unlock(); - return request_module("net-dccp-ccid-%d", id); - } - ccids_read_unlock(); - } - return 0; -} - -int ccid_request_modules(u8 const *ccid_array, u8 array_len) -{ -#ifdef CONFIG_KMOD - while (array_len--) - if (ccid_request_module(ccid_array[array_len])) - return -1; -#endif - return 0; -} - struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) { struct ccid_operations *ccid_ops; struct ccid *ccid = NULL; ccids_read_lock(); +#ifdef CONFIG_KMOD + if (ccids[id] == NULL) { + /* We only try to load if in process context */ + ccids_read_unlock(); + if (gfp & GFP_ATOMIC) + goto out; + request_module("net-dccp-ccid-%d", id); + ccids_read_lock(); + } +#endif ccid_ops = ccids[id]; if (ccid_ops == NULL) goto out_unlock; @@ -272,6 +205,20 @@ out_module_put: EXPORT_SYMBOL_GPL(ccid_new); +struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp) +{ + return ccid_new(id, sk, 1, gfp); +} + +EXPORT_SYMBOL_GPL(ccid_hc_rx_new); + +struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk, gfp_t gfp) +{ + return ccid_new(id, sk, 0, gfp); +} + +EXPORT_SYMBOL_GPL(ccid_hc_tx_new); + static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) { struct ccid_operations *ccid_ops; diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h index d27054ba2159..fdeae7b57319 100644 --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -60,18 +60,22 @@ struct ccid_operations { void (*ccid_hc_tx_exit)(struct sock *sk); void (*ccid_hc_rx_packet_recv)(struct sock *sk, struct sk_buff *skb); - int (*ccid_hc_rx_parse_options)(struct sock *sk, u8 pkt, - u8 opt, u8 *val, u8 len); + int (*ccid_hc_rx_parse_options)(struct sock *sk, + unsigned char option, + unsigned char len, u16 idx, + unsigned char* value); int (*ccid_hc_rx_insert_options)(struct sock *sk, struct sk_buff *skb); void (*ccid_hc_tx_packet_recv)(struct sock *sk, struct sk_buff *skb); - int (*ccid_hc_tx_parse_options)(struct sock *sk, u8 pkt, - u8 opt, u8 *val, u8 len); + int (*ccid_hc_tx_parse_options)(struct sock *sk, + unsigned char option, + unsigned char len, u16 idx, + unsigned char* value); int (*ccid_hc_tx_send_packet)(struct sock *sk, struct sk_buff *skb); void (*ccid_hc_tx_packet_sent)(struct sock *sk, - unsigned int len); + int more, unsigned int len); void (*ccid_hc_rx_get_info)(struct sock *sk, struct tcp_info *info); void (*ccid_hc_tx_get_info)(struct sock *sk, @@ -99,78 +103,31 @@ static inline void *ccid_priv(const struct ccid *ccid) return (void *)ccid->ccid_priv; } -extern bool ccid_support_check(u8 const *ccid_array, u8 array_len); -extern int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len); -extern int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, - char __user *, int __user *); - -extern int ccid_request_modules(u8 const *ccid_array, u8 array_len); extern struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp); -static inline int ccid_get_current_rx_ccid(struct dccp_sock *dp) -{ - struct ccid *ccid = dp->dccps_hc_rx_ccid; - - if (ccid == NULL || ccid->ccid_ops == NULL) - return -1; - return ccid->ccid_ops->ccid_id; -} - -static inline int ccid_get_current_tx_ccid(struct dccp_sock *dp) -{ - struct ccid *ccid = dp->dccps_hc_tx_ccid; - - if (ccid == NULL || ccid->ccid_ops == NULL) - return -1; - return ccid->ccid_ops->ccid_id; -} +extern struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, + gfp_t gfp); +extern struct ccid *ccid_hc_tx_new(unsigned char id, struct sock *sk, + gfp_t gfp); extern void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk); extern void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk); -/* - * Congestion control of queued data packets via CCID decision. - * - * The TX CCID performs its congestion-control by indicating whether and when a - * queued packet may be sent, using the return code of ccid_hc_tx_send_packet(). - * The following modes are supported via the symbolic constants below: - * - timer-based pacing (CCID returns a delay value in milliseconds); - * - autonomous dequeueing (CCID internally schedules dccps_xmitlet). - */ - -enum ccid_dequeueing_decision { - CCID_PACKET_SEND_AT_ONCE = 0x00000, /* "green light": no delay */ - CCID_PACKET_DELAY_MAX = 0x0FFFF, /* maximum delay in msecs */ - CCID_PACKET_DELAY = 0x10000, /* CCID msec-delay mode */ - CCID_PACKET_WILL_DEQUEUE_LATER = 0x20000, /* CCID autonomous mode */ - CCID_PACKET_ERR = 0xF0000, /* error condition */ -}; - -static inline int ccid_packet_dequeue_eval(const int return_code) -{ - if (return_code < 0) - return CCID_PACKET_ERR; - if (return_code == 0) - return CCID_PACKET_SEND_AT_ONCE; - if (return_code <= CCID_PACKET_DELAY_MAX) - return CCID_PACKET_DELAY; - return return_code; -} - static inline int ccid_hc_tx_send_packet(struct ccid *ccid, struct sock *sk, struct sk_buff *skb) { + int rc = 0; if (ccid->ccid_ops->ccid_hc_tx_send_packet != NULL) - return ccid->ccid_ops->ccid_hc_tx_send_packet(sk, skb); - return CCID_PACKET_SEND_AT_ONCE; + rc = ccid->ccid_ops->ccid_hc_tx_send_packet(sk, skb); + return rc; } static inline void ccid_hc_tx_packet_sent(struct ccid *ccid, struct sock *sk, - unsigned int len) + int more, unsigned int len) { if (ccid->ccid_ops->ccid_hc_tx_packet_sent != NULL) - ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, len); + ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, more, len); } static inline void ccid_hc_rx_packet_recv(struct ccid *ccid, struct sock *sk, @@ -187,31 +144,27 @@ static inline void ccid_hc_tx_packet_recv(struct ccid *ccid, struct sock *sk, ccid->ccid_ops->ccid_hc_tx_packet_recv(sk, skb); } -/** - * ccid_hc_tx_parse_options - Parse CCID-specific options sent by the receiver - * @pkt: type of packet that @opt appears on (RFC 4340, 5.1) - * @opt: the CCID-specific option type (RFC 4340, 5.8 and 10.3) - * @val: value of @opt - * @len: length of @val in bytes - */ static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk, - u8 pkt, u8 opt, u8 *val, u8 len) + unsigned char option, + unsigned char len, u16 idx, + unsigned char* value) { - if (ccid->ccid_ops->ccid_hc_tx_parse_options == NULL) - return 0; - return ccid->ccid_ops->ccid_hc_tx_parse_options(sk, pkt, opt, val, len); + int rc = 0; + if (ccid->ccid_ops->ccid_hc_tx_parse_options != NULL) + rc = ccid->ccid_ops->ccid_hc_tx_parse_options(sk, option, len, idx, + value); + return rc; } -/** - * ccid_hc_rx_parse_options - Parse CCID-specific options sent by the sender - * Arguments are analogous to ccid_hc_tx_parse_options() - */ static inline int ccid_hc_rx_parse_options(struct ccid *ccid, struct sock *sk, - u8 pkt, u8 opt, u8 *val, u8 len) + unsigned char option, + unsigned char len, u16 idx, + unsigned char* value) { - if (ccid->ccid_ops->ccid_hc_rx_parse_options == NULL) - return 0; - return ccid->ccid_ops->ccid_hc_rx_parse_options(sk, pkt, opt, val, len); + int rc = 0; + if (ccid->ccid_ops->ccid_hc_rx_parse_options != NULL) + rc = ccid->ccid_ops->ccid_hc_rx_parse_options(sk, option, len, idx, value); + return rc; } static inline int ccid_hc_rx_insert_options(struct ccid *ccid, struct sock *sk, diff --git a/net/dccp/ccids/Kconfig b/net/dccp/ccids/Kconfig index fb168be2cb43..12275943eab8 100644 --- a/net/dccp/ccids/Kconfig +++ b/net/dccp/ccids/Kconfig @@ -1,8 +1,10 @@ menu "DCCP CCIDs Configuration (EXPERIMENTAL)" + depends on EXPERIMENTAL config IP_DCCP_CCID2 - tristate "CCID2 (TCP-Like)" + tristate "CCID2 (TCP-Like) (EXPERIMENTAL)" def_tristate IP_DCCP + select IP_DCCP_ACKVEC ---help--- CCID 2, TCP-like Congestion Control, denotes Additive Increase, Multiplicative Decrease (AIMD) congestion control with behavior @@ -34,7 +36,7 @@ config IP_DCCP_CCID2_DEBUG If in doubt, say N. config IP_DCCP_CCID3 - tristate "CCID3 (TCP-Friendly)" + tristate "CCID3 (TCP-Friendly) (EXPERIMENTAL)" def_tristate IP_DCCP select IP_DCCP_TFRC_LIB ---help--- @@ -62,9 +64,9 @@ config IP_DCCP_CCID3 If in doubt, say M. -if IP_DCCP_CCID3 config IP_DCCP_CCID3_DEBUG bool "CCID3 debugging messages" + depends on IP_DCCP_CCID3 ---help--- Enable CCID3-specific debugging messages. @@ -74,29 +76,10 @@ config IP_DCCP_CCID3_DEBUG If in doubt, say N. -choice - prompt "Select method for measuring the packet size s" - default IP_DCCP_CCID3_MEASURE_S_AS_MPS - -config IP_DCCP_CCID3_MEASURE_S_AS_MPS - bool "Always use MPS in place of s" - ---help--- - This use is recommended as it is consistent with the initialisation - of X and suggested when s varies (rfc3448bis, (1) in section 4.1). -config IP_DCCP_CCID3_MEASURE_S_AS_AVG - bool "Use moving average" - ---help--- - An alternative way of tracking s, also supported by rfc3448bis. - This used to be the default for CCID-3 in previous kernels. -config IP_DCCP_CCID3_MEASURE_S_AS_MAX - bool "Track the maximum payload length" - ---help--- - An experimental method based on tracking the maximum packet size. -endchoice - config IP_DCCP_CCID3_RTO int "Use higher bound for nofeedback timer" default 100 + depends on IP_DCCP_CCID3 && EXPERIMENTAL ---help--- Use higher lower bound for nofeedback timer expiration. @@ -123,7 +106,6 @@ config IP_DCCP_CCID3_RTO The purpose of the nofeedback timer is to slow DCCP down when there is serious network congestion: experimenting with larger values should therefore not be performed on WANs. -endif # IP_DCCP_CCID3 config IP_DCCP_TFRC_LIB tristate diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index fa713227c66f..9a430734530c 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -25,7 +25,7 @@ /* * This implementation should follow RFC 4341 */ -#include "../feat.h" + #include "../ccid.h" #include "../dccp.h" #include "ccid2.h" @@ -34,8 +34,51 @@ #ifdef CONFIG_IP_DCCP_CCID2_DEBUG static int ccid2_debug; #define ccid2_pr_debug(format, a...) DCCP_PR_DEBUG(ccid2_debug, format, ##a) + +static void ccid2_hc_tx_check_sanity(const struct ccid2_hc_tx_sock *hctx) +{ + int len = 0; + int pipe = 0; + struct ccid2_seq *seqp = hctx->ccid2hctx_seqh; + + /* there is data in the chain */ + if (seqp != hctx->ccid2hctx_seqt) { + seqp = seqp->ccid2s_prev; + len++; + if (!seqp->ccid2s_acked) + pipe++; + + while (seqp != hctx->ccid2hctx_seqt) { + struct ccid2_seq *prev = seqp->ccid2s_prev; + + len++; + if (!prev->ccid2s_acked) + pipe++; + + /* packets are sent sequentially */ + BUG_ON(dccp_delta_seqno(seqp->ccid2s_seq, + prev->ccid2s_seq ) >= 0); + BUG_ON(time_before(seqp->ccid2s_sent, + prev->ccid2s_sent)); + + seqp = prev; + } + } + + BUG_ON(pipe != hctx->ccid2hctx_pipe); + ccid2_pr_debug("len of chain=%d\n", len); + + do { + seqp = seqp->ccid2s_prev; + len++; + } while (seqp != hctx->ccid2hctx_seqh); + + ccid2_pr_debug("total len=%d\n", len); + BUG_ON(len != hctx->ccid2hctx_seqbufc * CCID2_SEQBUF_LEN); +} #else #define ccid2_pr_debug(format, a...) +#define ccid2_hc_tx_check_sanity(hctx) #endif static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx) @@ -44,7 +87,8 @@ static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx) int i; /* check if we have space to preserve the pointer to the buffer */ - if (hctx->seqbufc >= sizeof(hctx->seqbuf) / sizeof(struct ccid2_seq *)) + if (hctx->ccid2hctx_seqbufc >= (sizeof(hctx->ccid2hctx_seqbuf) / + sizeof(struct ccid2_seq*))) return -ENOMEM; /* allocate buffer and initialize linked list */ @@ -60,35 +104,38 @@ static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx) seqp->ccid2s_prev = &seqp[CCID2_SEQBUF_LEN - 1]; /* This is the first allocation. Initiate the head and tail. */ - if (hctx->seqbufc == 0) - hctx->seqh = hctx->seqt = seqp; + if (hctx->ccid2hctx_seqbufc == 0) + hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqt = seqp; else { /* link the existing list with the one we just created */ - hctx->seqh->ccid2s_next = seqp; - seqp->ccid2s_prev = hctx->seqh; + hctx->ccid2hctx_seqh->ccid2s_next = seqp; + seqp->ccid2s_prev = hctx->ccid2hctx_seqh; - hctx->seqt->ccid2s_prev = &seqp[CCID2_SEQBUF_LEN - 1]; - seqp[CCID2_SEQBUF_LEN - 1].ccid2s_next = hctx->seqt; + hctx->ccid2hctx_seqt->ccid2s_prev = &seqp[CCID2_SEQBUF_LEN - 1]; + seqp[CCID2_SEQBUF_LEN - 1].ccid2s_next = hctx->ccid2hctx_seqt; } /* store the original pointer to the buffer so we can free it */ - hctx->seqbuf[hctx->seqbufc] = seqp; - hctx->seqbufc++; + hctx->ccid2hctx_seqbuf[hctx->ccid2hctx_seqbufc] = seqp; + hctx->ccid2hctx_seqbufc++; return 0; } static int ccid2_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) { - if (ccid2_cwnd_network_limited(ccid2_hc_tx_sk(sk))) - return CCID_PACKET_WILL_DEQUEUE_LATER; - return CCID_PACKET_SEND_AT_ONCE; + struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); + + if (hctx->ccid2hctx_pipe < hctx->ccid2hctx_cwnd) + return 0; + + return 1; /* XXX CCID should dequeue when ready instead of polling */ } static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val) { struct dccp_sock *dp = dccp_sk(sk); - u32 max_ratio = DIV_ROUND_UP(ccid2_hc_tx_sk(sk)->cwnd, 2); + u32 max_ratio = DIV_ROUND_UP(ccid2_hc_tx_sk(sk)->ccid2hctx_cwnd, 2); /* * Ensure that Ack Ratio does not exceed ceil(cwnd/2), which is (2) from @@ -100,8 +147,8 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val) DCCP_WARN("Limiting Ack Ratio (%u) to %u\n", val, max_ratio); val = max_ratio; } - if (val > DCCPF_ACK_RATIO_MAX) - val = DCCPF_ACK_RATIO_MAX; + if (val > 0xFFFF) /* RFC 4340, 11.3 */ + val = 0xFFFF; if (val == dp->dccps_l_ack_ratio) return; @@ -110,77 +157,99 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val) dp->dccps_l_ack_ratio = val; } +static void ccid2_change_srtt(struct ccid2_hc_tx_sock *hctx, long val) +{ + ccid2_pr_debug("change SRTT to %ld\n", val); + hctx->ccid2hctx_srtt = val; +} + +static void ccid2_start_rto_timer(struct sock *sk); + static void ccid2_hc_tx_rto_expire(unsigned long data) { struct sock *sk = (struct sock *)data; struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - const bool sender_was_blocked = ccid2_cwnd_network_limited(hctx); + long s; bh_lock_sock(sk); if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &hctx->rtotimer, jiffies + HZ / 5); + sk_reset_timer(sk, &hctx->ccid2hctx_rtotimer, + jiffies + HZ / 5); goto out; } ccid2_pr_debug("RTO_EXPIRE\n"); + ccid2_hc_tx_check_sanity(hctx); + /* back-off timer */ - hctx->rto <<= 1; - if (hctx->rto > DCCP_RTO_MAX) - hctx->rto = DCCP_RTO_MAX; + hctx->ccid2hctx_rto <<= 1; + + s = hctx->ccid2hctx_rto / HZ; + if (s > 60) + hctx->ccid2hctx_rto = 60 * HZ; + + ccid2_start_rto_timer(sk); /* adjust pipe, cwnd etc */ - hctx->ssthresh = hctx->cwnd / 2; - if (hctx->ssthresh < 2) - hctx->ssthresh = 2; - hctx->cwnd = 1; - hctx->pipe = 0; + hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd / 2; + if (hctx->ccid2hctx_ssthresh < 2) + hctx->ccid2hctx_ssthresh = 2; + hctx->ccid2hctx_cwnd = 1; + hctx->ccid2hctx_pipe = 0; /* clear state about stuff we sent */ - hctx->seqt = hctx->seqh; - hctx->packets_acked = 0; + hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh; + hctx->ccid2hctx_packets_acked = 0; /* clear ack ratio state. */ - hctx->rpseq = 0; - hctx->rpdupack = -1; + hctx->ccid2hctx_rpseq = 0; + hctx->ccid2hctx_rpdupack = -1; ccid2_change_l_ack_ratio(sk, 1); - - /* if we were blocked before, we may now send cwnd=1 packet */ - if (sender_was_blocked) - tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet); - /* restart backed-off timer */ - sk_reset_timer(sk, &hctx->rtotimer, jiffies + hctx->rto); + ccid2_hc_tx_check_sanity(hctx); out: bh_unlock_sock(sk); sock_put(sk); } -static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len) +static void ccid2_start_rto_timer(struct sock *sk) +{ + struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); + + ccid2_pr_debug("setting RTO timeout=%ld\n", hctx->ccid2hctx_rto); + + BUG_ON(timer_pending(&hctx->ccid2hctx_rtotimer)); + sk_reset_timer(sk, &hctx->ccid2hctx_rtotimer, + jiffies + hctx->ccid2hctx_rto); +} + +static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len) { struct dccp_sock *dp = dccp_sk(sk); struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); struct ccid2_seq *next; - hctx->pipe++; + hctx->ccid2hctx_pipe++; - hctx->seqh->ccid2s_seq = dp->dccps_gss; - hctx->seqh->ccid2s_acked = 0; - hctx->seqh->ccid2s_sent = jiffies; + hctx->ccid2hctx_seqh->ccid2s_seq = dp->dccps_gss; + hctx->ccid2hctx_seqh->ccid2s_acked = 0; + hctx->ccid2hctx_seqh->ccid2s_sent = jiffies; - next = hctx->seqh->ccid2s_next; + next = hctx->ccid2hctx_seqh->ccid2s_next; /* check if we need to alloc more space */ - if (next == hctx->seqt) { + if (next == hctx->ccid2hctx_seqt) { if (ccid2_hc_tx_alloc_seq(hctx)) { DCCP_CRIT("packet history - out of memory!"); /* FIXME: find a more graceful way to bail out */ return; } - next = hctx->seqh->ccid2s_next; - BUG_ON(next == hctx->seqt); + next = hctx->ccid2hctx_seqh->ccid2s_next; + BUG_ON(next == hctx->ccid2hctx_seqt); } - hctx->seqh = next; + hctx->ccid2hctx_seqh = next; - ccid2_pr_debug("cwnd=%d pipe=%d\n", hctx->cwnd, hctx->pipe); + ccid2_pr_debug("cwnd=%d pipe=%d\n", hctx->ccid2hctx_cwnd, + hctx->ccid2hctx_pipe); /* * FIXME: The code below is broken and the variables have been removed @@ -203,12 +272,12 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len) */ #if 0 /* Ack Ratio. Need to maintain a concept of how many windows we sent */ - hctx->arsent++; + hctx->ccid2hctx_arsent++; /* We had an ack loss in this window... */ - if (hctx->ackloss) { - if (hctx->arsent >= hctx->cwnd) { - hctx->arsent = 0; - hctx->ackloss = 0; + if (hctx->ccid2hctx_ackloss) { + if (hctx->ccid2hctx_arsent >= hctx->ccid2hctx_cwnd) { + hctx->ccid2hctx_arsent = 0; + hctx->ccid2hctx_ackloss = 0; } } else { /* No acks lost up to now... */ @@ -218,28 +287,28 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len) int denom = dp->dccps_l_ack_ratio * dp->dccps_l_ack_ratio - dp->dccps_l_ack_ratio; - denom = hctx->cwnd * hctx->cwnd / denom; + denom = hctx->ccid2hctx_cwnd * hctx->ccid2hctx_cwnd / denom; - if (hctx->arsent >= denom) { + if (hctx->ccid2hctx_arsent >= denom) { ccid2_change_l_ack_ratio(sk, dp->dccps_l_ack_ratio - 1); - hctx->arsent = 0; + hctx->ccid2hctx_arsent = 0; } } else { /* we can't increase ack ratio further [1] */ - hctx->arsent = 0; /* or maybe set it to cwnd*/ + hctx->ccid2hctx_arsent = 0; /* or maybe set it to cwnd*/ } } #endif /* setup RTO timer */ - if (!timer_pending(&hctx->rtotimer)) - sk_reset_timer(sk, &hctx->rtotimer, jiffies + hctx->rto); + if (!timer_pending(&hctx->ccid2hctx_rtotimer)) + ccid2_start_rto_timer(sk); #ifdef CONFIG_IP_DCCP_CCID2_DEBUG do { - struct ccid2_seq *seqp = hctx->seqt; + struct ccid2_seq *seqp = hctx->ccid2hctx_seqt; - while (seqp != hctx->seqh) { + while (seqp != hctx->ccid2hctx_seqh) { ccid2_pr_debug("out seq=%llu acked=%d time=%lu\n", (unsigned long long)seqp->ccid2s_seq, seqp->ccid2s_acked, seqp->ccid2s_sent); @@ -247,158 +316,205 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len) } } while (0); ccid2_pr_debug("=========\n"); + ccid2_hc_tx_check_sanity(hctx); #endif } -/** - * ccid2_rtt_estimator - Sample RTT and compute RTO using RFC2988 algorithm - * This code is almost identical with TCP's tcp_rtt_estimator(), since - * - it has a higher sampling frequency (recommended by RFC 1323), - * - the RTO does not collapse into RTT due to RTTVAR going towards zero, - * - it is simple (cf. more complex proposals such as Eifel timer or research - * which suggests that the gain should be set according to window size), - * - in tests it was found to work well with CCID2 [gerrit]. +/* XXX Lame code duplication! + * returns -1 if none was found. + * else returns the next offset to use in the function call. */ -static void ccid2_rtt_estimator(struct sock *sk, const long mrtt) +static int ccid2_ackvector(struct sock *sk, struct sk_buff *skb, int offset, + unsigned char **vec, unsigned char *veclen) { - struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - long m = mrtt ? : 1; - - if (hctx->srtt == 0) { - /* First measurement m */ - hctx->srtt = m << 3; - hctx->mdev = m << 1; - - hctx->mdev_max = max(TCP_RTO_MIN, hctx->mdev); - hctx->rttvar = hctx->mdev_max; - hctx->rtt_seq = dccp_sk(sk)->dccps_gss; - } else { - /* Update scaled SRTT as SRTT += 1/8 * (m - SRTT) */ - m -= (hctx->srtt >> 3); - hctx->srtt += m; - - /* Similarly, update scaled mdev with regard to |m| */ - if (m < 0) { - m = -m; - m -= (hctx->mdev >> 2); + const struct dccp_hdr *dh = dccp_hdr(skb); + unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb); + unsigned char *opt_ptr; + const unsigned char *opt_end = (unsigned char *)dh + + (dh->dccph_doff * 4); + unsigned char opt, len; + unsigned char *value; + + BUG_ON(offset < 0); + options += offset; + opt_ptr = options; + if (opt_ptr >= opt_end) + return -1; + + while (opt_ptr != opt_end) { + opt = *opt_ptr++; + len = 0; + value = NULL; + + /* Check if this isn't a single byte option */ + if (opt > DCCPO_MAX_RESERVED) { + if (opt_ptr == opt_end) + goto out_invalid_option; + + len = *opt_ptr++; + if (len < 3) + goto out_invalid_option; /* - * This neutralises RTO increase when RTT < SRTT - mdev - * (see P. Sarolahti, A. Kuznetsov,"Congestion Control - * in Linux TCP", USENIX 2002, pp. 49-62). + * Remove the type and len fields, leaving + * just the value size */ - if (m > 0) - m >>= 3; - } else { - m -= (hctx->mdev >> 2); - } - hctx->mdev += m; + len -= 2; + value = opt_ptr; + opt_ptr += len; - if (hctx->mdev > hctx->mdev_max) { - hctx->mdev_max = hctx->mdev; - if (hctx->mdev_max > hctx->rttvar) - hctx->rttvar = hctx->mdev_max; + if (opt_ptr > opt_end) + goto out_invalid_option; } - /* - * Decay RTTVAR at most once per flight, exploiting that - * 1) pipe <= cwnd <= Sequence_Window = W (RFC 4340, 7.5.2) - * 2) AWL = GSS-W+1 <= GAR <= GSS (RFC 4340, 7.5.1) - * GAR is a useful bound for FlightSize = pipe, AWL is probably - * too low as it over-estimates pipe. - */ - if (after48(dccp_sk(sk)->dccps_gar, hctx->rtt_seq)) { - if (hctx->mdev_max < hctx->rttvar) - hctx->rttvar -= (hctx->rttvar - - hctx->mdev_max) >> 2; - hctx->rtt_seq = dccp_sk(sk)->dccps_gss; - hctx->mdev_max = TCP_RTO_MIN; + switch (opt) { + case DCCPO_ACK_VECTOR_0: + case DCCPO_ACK_VECTOR_1: + *vec = value; + *veclen = len; + return offset + (opt_ptr - options); } } - /* - * Set RTO from SRTT and RTTVAR - * Clock granularity is ignored since the minimum error for RTTVAR is - * clamped to 50msec (corresponding to HZ=20). This leads to a minimum - * RTO of 200msec. This agrees with TCP and RFC 4341, 5.: "Because DCCP - * does not retransmit data, DCCP does not require TCP's recommended - * minimum timeout of one second". - */ - hctx->rto = (hctx->srtt >> 3) + hctx->rttvar; + return -1; - if (hctx->rto > DCCP_RTO_MAX) - hctx->rto = DCCP_RTO_MAX; +out_invalid_option: + DCCP_BUG("Invalid option - this should not happen (previous parsing)!"); + return -1; } -static void ccid2_new_ack(struct sock *sk, struct ccid2_seq *seqp, - unsigned int *maxincr) +static void ccid2_hc_tx_kill_rto_timer(struct sock *sk) { struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - if (hctx->cwnd < hctx->ssthresh) { - if (*maxincr > 0 && ++hctx->packets_acked == 2) { - hctx->cwnd += 1; - *maxincr -= 1; - hctx->packets_acked = 0; - } - } else if (++hctx->packets_acked >= hctx->cwnd) { - hctx->cwnd += 1; - hctx->packets_acked = 0; - } - /* - * FIXME: RTT is sampled several times per acknowledgment (for each - * entry in the Ack Vector), instead of once per Ack (as in TCP SACK). - * This causes the RTT to be over-estimated, since the older entries - * in the Ack Vector have earlier sending times. - * The cleanest solution is to not use the ccid2s_sent field at all - * and instead use DCCP timestamps - need to be resolved at some time. - */ - ccid2_rtt_estimator(sk, jiffies - seqp->ccid2s_sent); + sk_stop_timer(sk, &hctx->ccid2hctx_rtotimer); + ccid2_pr_debug("deleted RTO timer\n"); } -static void ccid2_congestion_event(struct sock *sk, struct ccid2_seq *seqp) +static inline void ccid2_new_ack(struct sock *sk, + struct ccid2_seq *seqp, + unsigned int *maxincr) { struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - if (time_before(seqp->ccid2s_sent, hctx->last_cong)) { - ccid2_pr_debug("Multiple losses in an RTT---treating as one\n"); - return; + if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) { + if (*maxincr > 0 && ++hctx->ccid2hctx_packets_acked == 2) { + hctx->ccid2hctx_cwnd += 1; + *maxincr -= 1; + hctx->ccid2hctx_packets_acked = 0; + } + } else if (++hctx->ccid2hctx_packets_acked >= hctx->ccid2hctx_cwnd) { + hctx->ccid2hctx_cwnd += 1; + hctx->ccid2hctx_packets_acked = 0; } - hctx->last_cong = jiffies; + /* update RTO */ + if (hctx->ccid2hctx_srtt == -1 || + time_after(jiffies, hctx->ccid2hctx_lastrtt + hctx->ccid2hctx_srtt)) { + unsigned long r = (long)jiffies - (long)seqp->ccid2s_sent; + int s; + + /* first measurement */ + if (hctx->ccid2hctx_srtt == -1) { + ccid2_pr_debug("R: %lu Time=%lu seq=%llu\n", + r, jiffies, + (unsigned long long)seqp->ccid2s_seq); + ccid2_change_srtt(hctx, r); + hctx->ccid2hctx_rttvar = r >> 1; + } else { + /* RTTVAR */ + long tmp = hctx->ccid2hctx_srtt - r; + long srtt; + + if (tmp < 0) + tmp *= -1; + + tmp >>= 2; + hctx->ccid2hctx_rttvar *= 3; + hctx->ccid2hctx_rttvar >>= 2; + hctx->ccid2hctx_rttvar += tmp; + + /* SRTT */ + srtt = hctx->ccid2hctx_srtt; + srtt *= 7; + srtt >>= 3; + tmp = r >> 3; + srtt += tmp; + ccid2_change_srtt(hctx, srtt); + } + s = hctx->ccid2hctx_rttvar << 2; + /* clock granularity is 1 when based on jiffies */ + if (!s) + s = 1; + hctx->ccid2hctx_rto = hctx->ccid2hctx_srtt + s; + + /* must be at least a second */ + s = hctx->ccid2hctx_rto / HZ; + /* DCCP doesn't require this [but I like it cuz my code sux] */ +#if 1 + if (s < 1) + hctx->ccid2hctx_rto = HZ; +#endif + /* max 60 seconds */ + if (s > 60) + hctx->ccid2hctx_rto = HZ * 60; - hctx->cwnd = hctx->cwnd / 2 ? : 1U; - hctx->ssthresh = max(hctx->cwnd, 2U); + hctx->ccid2hctx_lastrtt = jiffies; - /* Avoid spurious timeouts resulting from Ack Ratio > cwnd */ - if (dccp_sk(sk)->dccps_l_ack_ratio > hctx->cwnd) - ccid2_change_l_ack_ratio(sk, hctx->cwnd); + ccid2_pr_debug("srtt: %ld rttvar: %ld rto: %ld (HZ=%d) R=%lu\n", + hctx->ccid2hctx_srtt, hctx->ccid2hctx_rttvar, + hctx->ccid2hctx_rto, HZ, r); + } + + /* we got a new ack, so re-start RTO timer */ + ccid2_hc_tx_kill_rto_timer(sk); + ccid2_start_rto_timer(sk); } -static int ccid2_hc_tx_parse_options(struct sock *sk, u8 packet_type, - u8 option, u8 *optval, u8 optlen) +static void ccid2_hc_tx_dec_pipe(struct sock *sk) { struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - switch (option) { - case DCCPO_ACK_VECTOR_0: - case DCCPO_ACK_VECTOR_1: - return dccp_ackvec_parsed_add(&hctx->av_chunks, optval, optlen, - option - DCCPO_ACK_VECTOR_0); + if (hctx->ccid2hctx_pipe == 0) + DCCP_BUG("pipe == 0"); + else + hctx->ccid2hctx_pipe--; + + if (hctx->ccid2hctx_pipe == 0) + ccid2_hc_tx_kill_rto_timer(sk); +} + +static void ccid2_congestion_event(struct sock *sk, struct ccid2_seq *seqp) +{ + struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); + + if (time_before(seqp->ccid2s_sent, hctx->ccid2hctx_last_cong)) { + ccid2_pr_debug("Multiple losses in an RTT---treating as one\n"); + return; } - return 0; + + hctx->ccid2hctx_last_cong = jiffies; + + hctx->ccid2hctx_cwnd = hctx->ccid2hctx_cwnd / 2 ? : 1U; + hctx->ccid2hctx_ssthresh = max(hctx->ccid2hctx_cwnd, 2U); + + /* Avoid spurious timeouts resulting from Ack Ratio > cwnd */ + if (dccp_sk(sk)->dccps_l_ack_ratio > hctx->ccid2hctx_cwnd) + ccid2_change_l_ack_ratio(sk, hctx->ccid2hctx_cwnd); } static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - const bool sender_was_blocked = ccid2_cwnd_network_limited(hctx); - struct dccp_ackvec_parsed *avp; u64 ackno, seqno; struct ccid2_seq *seqp; + unsigned char *vector; + unsigned char veclen; + int offset = 0; int done = 0; unsigned int maxincr = 0; + ccid2_hc_tx_check_sanity(hctx); /* check reverse path congestion */ seqno = DCCP_SKB_CB(skb)->dccpd_seq; @@ -407,21 +523,21 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) * -sorbo. */ /* need to bootstrap */ - if (hctx->rpdupack == -1) { - hctx->rpdupack = 0; - hctx->rpseq = seqno; + if (hctx->ccid2hctx_rpdupack == -1) { + hctx->ccid2hctx_rpdupack = 0; + hctx->ccid2hctx_rpseq = seqno; } else { /* check if packet is consecutive */ - if (dccp_delta_seqno(hctx->rpseq, seqno) == 1) - hctx->rpseq = seqno; + if (dccp_delta_seqno(hctx->ccid2hctx_rpseq, seqno) == 1) + hctx->ccid2hctx_rpseq = seqno; /* it's a later packet */ - else if (after48(seqno, hctx->rpseq)) { - hctx->rpdupack++; + else if (after48(seqno, hctx->ccid2hctx_rpseq)) { + hctx->ccid2hctx_rpdupack++; /* check if we got enough dupacks */ - if (hctx->rpdupack >= NUMDUPACK) { - hctx->rpdupack = -1; /* XXX lame */ - hctx->rpseq = 0; + if (hctx->ccid2hctx_rpdupack >= NUMDUPACK) { + hctx->ccid2hctx_rpdupack = -1; /* XXX lame */ + hctx->ccid2hctx_rpseq = 0; ccid2_change_l_ack_ratio(sk, 2 * dp->dccps_l_ack_ratio); } @@ -429,22 +545,27 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) } /* check forward path congestion */ - if (dccp_packet_without_ack(skb)) + /* still didn't send out new data packets */ + if (hctx->ccid2hctx_seqh == hctx->ccid2hctx_seqt) return; - /* still didn't send out new data packets */ - if (hctx->seqh == hctx->seqt) - goto done; + switch (DCCP_SKB_CB(skb)->dccpd_type) { + case DCCP_PKT_ACK: + case DCCP_PKT_DATAACK: + break; + default: + return; + } ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq; - if (after48(ackno, hctx->high_ack)) - hctx->high_ack = ackno; + if (after48(ackno, hctx->ccid2hctx_high_ack)) + hctx->ccid2hctx_high_ack = ackno; - seqp = hctx->seqt; + seqp = hctx->ccid2hctx_seqt; while (before48(seqp->ccid2s_seq, ackno)) { seqp = seqp->ccid2s_next; - if (seqp == hctx->seqh) { - seqp = hctx->seqh->ccid2s_prev; + if (seqp == hctx->ccid2hctx_seqh) { + seqp = hctx->ccid2hctx_seqh->ccid2s_prev; break; } } @@ -454,26 +575,26 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) * packets per acknowledgement. Rounding up avoids that cwnd is not * advanced when Ack Ratio is 1 and gives a slight edge otherwise. */ - if (hctx->cwnd < hctx->ssthresh) + if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) maxincr = DIV_ROUND_UP(dp->dccps_l_ack_ratio, 2); /* go through all ack vectors */ - list_for_each_entry(avp, &hctx->av_chunks, node) { + while ((offset = ccid2_ackvector(sk, skb, offset, + &vector, &veclen)) != -1) { /* go through this ack vector */ - for (; avp->len--; avp->vec++) { - u64 ackno_end_rl = SUB48(ackno, - dccp_ackvec_runlen(avp->vec)); + while (veclen--) { + const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK; + u64 ackno_end_rl = SUB48(ackno, rl); - ccid2_pr_debug("ackvec %llu |%u,%u|\n", + ccid2_pr_debug("ackvec start:%llu end:%llu\n", (unsigned long long)ackno, - dccp_ackvec_state(avp->vec) >> 6, - dccp_ackvec_runlen(avp->vec)); + (unsigned long long)ackno_end_rl); /* if the seqno we are analyzing is larger than the * current ackno, then move towards the tail of our * seqnos. */ while (after48(seqp->ccid2s_seq, ackno)) { - if (seqp == hctx->seqt) { + if (seqp == hctx->ccid2hctx_seqt) { done = 1; break; } @@ -486,24 +607,26 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) * run length */ while (between48(seqp->ccid2s_seq,ackno_end_rl,ackno)) { - const u8 state = dccp_ackvec_state(avp->vec); + const u8 state = *vector & + DCCP_ACKVEC_STATE_MASK; /* new packet received or marked */ - if (state != DCCPAV_NOT_RECEIVED && + if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED && !seqp->ccid2s_acked) { - if (state == DCCPAV_ECN_MARKED) + if (state == + DCCP_ACKVEC_STATE_ECN_MARKED) { ccid2_congestion_event(sk, seqp); - else + } else ccid2_new_ack(sk, seqp, &maxincr); seqp->ccid2s_acked = 1; ccid2_pr_debug("Got ack for %llu\n", (unsigned long long)seqp->ccid2s_seq); - hctx->pipe--; + ccid2_hc_tx_dec_pipe(sk); } - if (seqp == hctx->seqt) { + if (seqp == hctx->ccid2hctx_seqt) { done = 1; break; } @@ -513,6 +636,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) break; ackno = SUB48(ackno_end_rl, 1); + vector++; } if (done) break; @@ -521,11 +645,11 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) /* The state about what is acked should be correct now * Check for NUMDUPACK */ - seqp = hctx->seqt; - while (before48(seqp->ccid2s_seq, hctx->high_ack)) { + seqp = hctx->ccid2hctx_seqt; + while (before48(seqp->ccid2s_seq, hctx->ccid2hctx_high_ack)) { seqp = seqp->ccid2s_next; - if (seqp == hctx->seqh) { - seqp = hctx->seqh->ccid2s_prev; + if (seqp == hctx->ccid2hctx_seqh) { + seqp = hctx->ccid2hctx_seqh->ccid2s_prev; break; } } @@ -536,7 +660,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) if (done == NUMDUPACK) break; } - if (seqp == hctx->seqt) + if (seqp == hctx->ccid2hctx_seqt) break; seqp = seqp->ccid2s_prev; } @@ -557,34 +681,25 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) * one ack vector. */ ccid2_congestion_event(sk, seqp); - hctx->pipe--; + ccid2_hc_tx_dec_pipe(sk); } - if (seqp == hctx->seqt) + if (seqp == hctx->ccid2hctx_seqt) break; seqp = seqp->ccid2s_prev; } - hctx->seqt = last_acked; + hctx->ccid2hctx_seqt = last_acked; } /* trim acked packets in tail */ - while (hctx->seqt != hctx->seqh) { - if (!hctx->seqt->ccid2s_acked) + while (hctx->ccid2hctx_seqt != hctx->ccid2hctx_seqh) { + if (!hctx->ccid2hctx_seqt->ccid2s_acked) break; - hctx->seqt = hctx->seqt->ccid2s_next; + hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqt->ccid2s_next; } - /* restart RTO timer if not all outstanding data has been acked */ - if (hctx->pipe == 0) - sk_stop_timer(sk, &hctx->rtotimer); - else - sk_reset_timer(sk, &hctx->rtotimer, jiffies + hctx->rto); -done: - /* check if incoming Acks allow pending packets to be sent */ - if (sender_was_blocked && !ccid2_cwnd_network_limited(hctx)) - tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet); - dccp_ackvec_parsed_cleanup(&hctx->av_chunks); + ccid2_hc_tx_check_sanity(hctx); } static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk) @@ -594,13 +709,17 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk) u32 max_ratio; /* RFC 4341, 5: initialise ssthresh to arbitrarily high (max) value */ - hctx->ssthresh = ~0U; + hctx->ccid2hctx_ssthresh = ~0U; - /* Use larger initial windows (RFC 3390, rfc2581bis) */ - hctx->cwnd = rfc3390_bytes_to_packets(dp->dccps_mss_cache); + /* + * RFC 4341, 5: "The cwnd parameter is initialized to at most four + * packets for new connections, following the rules from [RFC3390]". + * We need to convert the bytes of RFC3390 into the packets of RFC 4341. + */ + hctx->ccid2hctx_cwnd = clamp(4380U / dp->dccps_mss_cache, 2U, 4U); /* Make sure that Ack Ratio is enabled and within bounds. */ - max_ratio = DIV_ROUND_UP(hctx->cwnd, 2); + max_ratio = DIV_ROUND_UP(hctx->ccid2hctx_cwnd, 2); if (dp->dccps_l_ack_ratio == 0 || dp->dccps_l_ack_ratio > max_ratio) dp->dccps_l_ack_ratio = max_ratio; @@ -608,11 +727,15 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk) if (ccid2_hc_tx_alloc_seq(hctx)) return -ENOMEM; - hctx->rto = DCCP_TIMEOUT_INIT; - hctx->rpdupack = -1; - hctx->last_cong = jiffies; - setup_timer(&hctx->rtotimer, ccid2_hc_tx_rto_expire, (unsigned long)sk); - INIT_LIST_HEAD(&hctx->av_chunks); + hctx->ccid2hctx_rto = 3 * HZ; + ccid2_change_srtt(hctx, -1); + hctx->ccid2hctx_rttvar = -1; + hctx->ccid2hctx_rpdupack = -1; + hctx->ccid2hctx_last_cong = jiffies; + setup_timer(&hctx->ccid2hctx_rtotimer, ccid2_hc_tx_rto_expire, + (unsigned long)sk); + + ccid2_hc_tx_check_sanity(hctx); return 0; } @@ -621,11 +744,11 @@ static void ccid2_hc_tx_exit(struct sock *sk) struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); int i; - sk_stop_timer(sk, &hctx->rtotimer); + ccid2_hc_tx_kill_rto_timer(sk); - for (i = 0; i < hctx->seqbufc; i++) - kfree(hctx->seqbuf[i]); - hctx->seqbufc = 0; + for (i = 0; i < hctx->ccid2hctx_seqbufc; i++) + kfree(hctx->ccid2hctx_seqbuf[i]); + hctx->ccid2hctx_seqbufc = 0; } static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) @@ -636,28 +759,27 @@ static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) switch (DCCP_SKB_CB(skb)->dccpd_type) { case DCCP_PKT_DATA: case DCCP_PKT_DATAACK: - hcrx->data++; - if (hcrx->data >= dp->dccps_r_ack_ratio) { + hcrx->ccid2hcrx_data++; + if (hcrx->ccid2hcrx_data >= dp->dccps_r_ack_ratio) { dccp_send_ack(sk); - hcrx->data = 0; + hcrx->ccid2hcrx_data = 0; } break; } } static struct ccid_operations ccid2 = { - .ccid_id = DCCPC_CCID2, - .ccid_name = "TCP-like", - .ccid_owner = THIS_MODULE, - .ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock), - .ccid_hc_tx_init = ccid2_hc_tx_init, - .ccid_hc_tx_exit = ccid2_hc_tx_exit, - .ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet, - .ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent, - .ccid_hc_tx_parse_options = ccid2_hc_tx_parse_options, - .ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv, - .ccid_hc_rx_obj_size = sizeof(struct ccid2_hc_rx_sock), - .ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv, + .ccid_id = DCCPC_CCID2, + .ccid_name = "TCP-like", + .ccid_owner = THIS_MODULE, + .ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock), + .ccid_hc_tx_init = ccid2_hc_tx_init, + .ccid_hc_tx_exit = ccid2_hc_tx_exit, + .ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet, + .ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent, + .ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv, + .ccid_hc_rx_obj_size = sizeof(struct ccid2_hc_rx_sock), + .ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv, }; #ifdef CONFIG_IP_DCCP_CCID2_DEBUG diff --git a/net/dccp/ccids/ccid2.h b/net/dccp/ccids/ccid2.h index 8b7a2dee2f6d..2c94ca029010 100644 --- a/net/dccp/ccids/ccid2.h +++ b/net/dccp/ccids/ccid2.h @@ -42,49 +42,34 @@ struct ccid2_seq { /** struct ccid2_hc_tx_sock - CCID2 TX half connection * - * @{cwnd,ssthresh,pipe}: as per RFC 4341, section 5 - * @packets_acked: Ack counter for deriving cwnd growth (RFC 3465) - * @srtt: smoothed RTT estimate, scaled by 2^3 - * @mdev: smoothed RTT variation, scaled by 2^2 - * @mdev_max: maximum of @mdev during one flight - * @rttvar: moving average/maximum of @mdev_max - * @rto: RTO value deriving from SRTT and RTTVAR (RFC 2988) - * @rtt_seq: to decay RTTVAR at most once per flight - * @rpseq: last consecutive seqno - * @rpdupack: dupacks since rpseq - * @av_chunks: list of Ack Vectors received on current skb - */ + * @ccid2hctx_{cwnd,ssthresh,pipe}: as per RFC 4341, section 5 + * @ccid2hctx_packets_acked - Ack counter for deriving cwnd growth (RFC 3465) + * @ccid2hctx_lastrtt -time RTT was last measured + * @ccid2hctx_rpseq - last consecutive seqno + * @ccid2hctx_rpdupack - dupacks since rpseq +*/ struct ccid2_hc_tx_sock { - u32 cwnd; - u32 ssthresh; - u32 pipe; - u32 packets_acked; - struct ccid2_seq *seqbuf[CCID2_SEQBUF_MAX]; - int seqbufc; - struct ccid2_seq *seqh; - struct ccid2_seq *seqt; - /* RTT measurement: variables/principles are the same as in TCP */ - u32 srtt, - mdev, - mdev_max, - rttvar, - rto; - u64 rtt_seq:48; - struct timer_list rtotimer; - u64 rpseq; - int rpdupack; - unsigned long last_cong; - u64 high_ack; - struct list_head av_chunks; + u32 ccid2hctx_cwnd; + u32 ccid2hctx_ssthresh; + u32 ccid2hctx_pipe; + u32 ccid2hctx_packets_acked; + struct ccid2_seq *ccid2hctx_seqbuf[CCID2_SEQBUF_MAX]; + int ccid2hctx_seqbufc; + struct ccid2_seq *ccid2hctx_seqh; + struct ccid2_seq *ccid2hctx_seqt; + long ccid2hctx_rto; + long ccid2hctx_srtt; + long ccid2hctx_rttvar; + unsigned long ccid2hctx_lastrtt; + struct timer_list ccid2hctx_rtotimer; + u64 ccid2hctx_rpseq; + int ccid2hctx_rpdupack; + unsigned long ccid2hctx_last_cong; + u64 ccid2hctx_high_ack; }; -static inline bool ccid2_cwnd_network_limited(struct ccid2_hc_tx_sock *hctx) -{ - return (hctx->pipe >= hctx->cwnd); -} - struct ccid2_hc_rx_sock { - int data; + int ccid2hcrx_data; }; static inline struct ccid2_hc_tx_sock *ccid2_hc_tx_sk(const struct sock *sk) diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index 06cfdad84a6a..3b8bd7ca6761 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -49,41 +49,75 @@ static int ccid3_debug; /* * Transmitter Half-Connection Routines */ -/* Oscillation Prevention/Reduction: recommended by rfc3448bis, on by default */ -static int do_osc_prev = true; +#ifdef CONFIG_IP_DCCP_CCID3_DEBUG +static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state) +{ + static char *ccid3_state_names[] = { + [TFRC_SSTATE_NO_SENT] = "NO_SENT", + [TFRC_SSTATE_NO_FBACK] = "NO_FBACK", + [TFRC_SSTATE_FBACK] = "FBACK", + [TFRC_SSTATE_TERM] = "TERM", + }; + + return ccid3_state_names[state]; +} +#endif + +static void ccid3_hc_tx_set_state(struct sock *sk, + enum ccid3_hc_tx_states state) +{ + struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + enum ccid3_hc_tx_states oldstate = hctx->ccid3hctx_state; + + ccid3_pr_debug("%s(%p) %-8.8s -> %s\n", + dccp_role(sk), sk, ccid3_tx_state_name(oldstate), + ccid3_tx_state_name(state)); + WARN_ON(state == oldstate); + hctx->ccid3hctx_state = state; +} /* * Compute the initial sending rate X_init in the manner of RFC 3390: * - * X_init = min(4 * MPS, max(2 * MPS, 4380 bytes)) / RTT + * X_init = min(4 * s, max(2 * s, 4380 bytes)) / RTT * + * Note that RFC 3390 uses MSS, RFC 4342 refers to RFC 3390, and rfc3448bis + * (rev-02) clarifies the use of RFC 3390 with regard to the above formula. * For consistency with other parts of the code, X_init is scaled by 2^6. */ static inline u64 rfc3390_initial_rate(struct sock *sk) { - const u32 mps = dccp_sk(sk)->dccps_mss_cache, - w_init = clamp(4380U, 2 * mps, 4 * mps); + const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + const __u32 w_init = clamp_t(__u32, 4380U, + 2 * hctx->ccid3hctx_s, 4 * hctx->ccid3hctx_s); - return scaled_div(w_init << 6, ccid3_hc_tx_sk(sk)->rtt); + return scaled_div(w_init << 6, hctx->ccid3hctx_rtt); } -/** - * ccid3_update_send_interval - Calculate new t_ipi = s / X - * This respects the granularity of X (64 * bytes/second) and enforces the - * scaled minimum of s * 64 / t_mbi = `s' bytes/second as per RFC 3448/4342. +/* + * Recalculate t_ipi and delta (should be called whenever X changes) */ static void ccid3_update_send_interval(struct ccid3_hc_tx_sock *hctx) { - if (unlikely(hctx->x <= hctx->s)) - hctx->x = hctx->s; - hctx->t_ipi = scaled_div32(((u64)hctx->s) << 6, hctx->x); + /* Calculate new t_ipi = s / X_inst (X_inst is in 64 * bytes/second) */ + hctx->ccid3hctx_t_ipi = scaled_div32(((u64)hctx->ccid3hctx_s) << 6, + hctx->ccid3hctx_x); + + /* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */ + hctx->ccid3hctx_delta = min_t(u32, hctx->ccid3hctx_t_ipi / 2, + TFRC_OPSYS_HALF_TIME_GRAN); + + ccid3_pr_debug("t_ipi=%u, delta=%u, s=%u, X=%u\n", + hctx->ccid3hctx_t_ipi, hctx->ccid3hctx_delta, + hctx->ccid3hctx_s, (unsigned)(hctx->ccid3hctx_x >> 6)); + } static u32 ccid3_hc_tx_idle_rtt(struct ccid3_hc_tx_sock *hctx, ktime_t now) { - u32 delta = ktime_us_delta(now, hctx->t_last_win_count); + u32 delta = ktime_us_delta(now, hctx->ccid3hctx_t_last_win_count); - return delta / hctx->rtt; + return delta / hctx->ccid3hctx_rtt; } /** @@ -99,8 +133,8 @@ static u32 ccid3_hc_tx_idle_rtt(struct ccid3_hc_tx_sock *hctx, ktime_t now) static void ccid3_hc_tx_update_x(struct sock *sk, ktime_t *stamp) { struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - u64 min_rate = 2 * hctx->x_recv; - const u64 old_x = hctx->x; + __u64 min_rate = 2 * hctx->ccid3hctx_x_recv; + const __u64 old_x = hctx->ccid3hctx_x; ktime_t now = stamp ? *stamp : ktime_get_real(); /* @@ -111,44 +145,50 @@ static void ccid3_hc_tx_update_x(struct sock *sk, ktime_t *stamp) */ if (ccid3_hc_tx_idle_rtt(hctx, now) >= 2) { min_rate = rfc3390_initial_rate(sk); - min_rate = max(min_rate, 2 * hctx->x_recv); + min_rate = max(min_rate, 2 * hctx->ccid3hctx_x_recv); } - if (hctx->p > 0) { + if (hctx->ccid3hctx_p > 0) { - hctx->x = min(((u64)hctx->x_calc) << 6, min_rate); + hctx->ccid3hctx_x = min(((__u64)hctx->ccid3hctx_x_calc) << 6, + min_rate); + hctx->ccid3hctx_x = max(hctx->ccid3hctx_x, + (((__u64)hctx->ccid3hctx_s) << 6) / + TFRC_T_MBI); - } else if (ktime_us_delta(now, hctx->t_ld) - (s64)hctx->rtt >= 0) { + } else if (ktime_us_delta(now, hctx->ccid3hctx_t_ld) + - (s64)hctx->ccid3hctx_rtt >= 0) { - hctx->x = min(2 * hctx->x, min_rate); - hctx->x = max(hctx->x, - scaled_div(((u64)hctx->s) << 6, hctx->rtt)); - hctx->t_ld = now; + hctx->ccid3hctx_x = min(2 * hctx->ccid3hctx_x, min_rate); + hctx->ccid3hctx_x = max(hctx->ccid3hctx_x, + scaled_div(((__u64)hctx->ccid3hctx_s) << 6, + hctx->ccid3hctx_rtt)); + hctx->ccid3hctx_t_ld = now; } - if (hctx->x != old_x) { + if (hctx->ccid3hctx_x != old_x) { ccid3_pr_debug("X_prev=%u, X_now=%u, X_calc=%u, " "X_recv=%u\n", (unsigned)(old_x >> 6), - (unsigned)(hctx->x >> 6), hctx->x_calc, - (unsigned)(hctx->x_recv >> 6)); + (unsigned)(hctx->ccid3hctx_x >> 6), + hctx->ccid3hctx_x_calc, + (unsigned)(hctx->ccid3hctx_x_recv >> 6)); ccid3_update_send_interval(hctx); } } /* - * ccid3_hc_tx_measure_packet_size - Measuring the packet size `s' (sec 4.1) - * @new_len: DCCP payload size in bytes (not used by all methods) + * Track the mean packet size `s' (cf. RFC 4342, 5.3 and RFC 3448, 4.1) + * @len: DCCP packet payload size in bytes */ -static u32 ccid3_hc_tx_measure_packet_size(struct sock *sk, const u16 new_len) +static inline void ccid3_hc_tx_update_s(struct ccid3_hc_tx_sock *hctx, int len) { -#if defined(CONFIG_IP_DCCP_CCID3_MEASURE_S_AS_AVG) - return tfrc_ewma(ccid3_hc_tx_sk(sk)->s, new_len, 9); -#elif defined(CONFIG_IP_DCCP_CCID3_MEASURE_S_AS_MAX) - return max(ccid3_hc_tx_sk(sk)->s, new_len); -#else /* CONFIG_IP_DCCP_CCID3_MEASURE_S_AS_MPS */ - return dccp_sk(sk)->dccps_mss_cache; -#endif + const u16 old_s = hctx->ccid3hctx_s; + + hctx->ccid3hctx_s = tfrc_ewma(hctx->ccid3hctx_s, len, 9); + + if (hctx->ccid3hctx_s != old_s) + ccid3_update_send_interval(hctx); } /* @@ -158,13 +198,13 @@ static u32 ccid3_hc_tx_measure_packet_size(struct sock *sk, const u16 new_len) static inline void ccid3_hc_tx_update_win_count(struct ccid3_hc_tx_sock *hctx, ktime_t now) { - u32 delta = ktime_us_delta(now, hctx->t_last_win_count), - quarter_rtts = (4 * delta) / hctx->rtt; + u32 delta = ktime_us_delta(now, hctx->ccid3hctx_t_last_win_count), + quarter_rtts = (4 * delta) / hctx->ccid3hctx_rtt; if (quarter_rtts > 0) { - hctx->t_last_win_count = now; - hctx->last_win_count += min(quarter_rtts, 5U); - hctx->last_win_count &= 0xF; /* mod 16 */ + hctx->ccid3hctx_t_last_win_count = now; + hctx->ccid3hctx_last_win_count += min(quarter_rtts, 5U); + hctx->ccid3hctx_last_win_count &= 0xF; /* mod 16 */ } } @@ -181,26 +221,25 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data) goto restart_timer; } - ccid3_pr_debug("%s(%p) entry with%s feedback\n", dccp_role(sk), sk, - hctx->feedback ? "" : "out"); + ccid3_pr_debug("%s(%p, state=%s) - entry \n", dccp_role(sk), sk, + ccid3_tx_state_name(hctx->ccid3hctx_state)); - /* Ignore and do not restart after leaving the established state */ - if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN)) + if (hctx->ccid3hctx_state == TFRC_SSTATE_FBACK) + ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK); + else if (hctx->ccid3hctx_state != TFRC_SSTATE_NO_FBACK) goto out; - /* Reset feedback state to "no feedback received" */ - hctx->feedback = false; - /* * Determine new allowed sending rate X as per draft rfc3448bis-00, 4.4 - * RTO is 0 if and only if no feedback has been received yet. */ - if (hctx->t_rto == 0 || hctx->p == 0) { + if (hctx->ccid3hctx_t_rto == 0 || /* no feedback received yet */ + hctx->ccid3hctx_p == 0) { /* halve send rate directly */ - hctx->x /= 2; + hctx->ccid3hctx_x = max(hctx->ccid3hctx_x / 2, + (((__u64)hctx->ccid3hctx_s) << 6) / + TFRC_T_MBI); ccid3_update_send_interval(hctx); - } else { /* * Modify the cached value of X_recv @@ -212,41 +251,44 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data) * * Note that X_recv is scaled by 2^6 while X_calc is not */ - BUG_ON(hctx->p && !hctx->x_calc); + BUG_ON(hctx->ccid3hctx_p && !hctx->ccid3hctx_x_calc); - if (hctx->x_calc > (hctx->x_recv >> 5)) - hctx->x_recv /= 2; + if (hctx->ccid3hctx_x_calc > (hctx->ccid3hctx_x_recv >> 5)) + hctx->ccid3hctx_x_recv = + max(hctx->ccid3hctx_x_recv / 2, + (((__u64)hctx->ccid3hctx_s) << 6) / + (2 * TFRC_T_MBI)); else { - hctx->x_recv = hctx->x_calc; - hctx->x_recv <<= 4; + hctx->ccid3hctx_x_recv = hctx->ccid3hctx_x_calc; + hctx->ccid3hctx_x_recv <<= 4; } ccid3_hc_tx_update_x(sk, NULL); } ccid3_pr_debug("Reduced X to %llu/64 bytes/sec\n", - (unsigned long long)hctx->x); + (unsigned long long)hctx->ccid3hctx_x); /* * Set new timeout for the nofeedback timer. * See comments in packet_recv() regarding the value of t_RTO. */ - if (unlikely(hctx->t_rto == 0)) /* no feedback received yet */ + if (unlikely(hctx->ccid3hctx_t_rto == 0)) /* no feedback yet */ t_nfb = TFRC_INITIAL_TIMEOUT; else - t_nfb = max(hctx->t_rto, 2 * hctx->t_ipi); + t_nfb = max(hctx->ccid3hctx_t_rto, 2 * hctx->ccid3hctx_t_ipi); restart_timer: - sk_reset_timer(sk, &hctx->no_feedback_timer, + sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer, jiffies + usecs_to_jiffies(t_nfb)); out: bh_unlock_sock(sk); sock_put(sk); } -/** - * ccid3_hc_tx_send_packet - Delay-based dequeueing of TX packets - * @skb: next packet candidate to send on @sk - * This function uses the convention of ccid_packet_dequeue_eval() and - * returns a millisecond-delay value between 0 and t_mbi = 64000 msec. +/* + * returns + * > 0: delay (in msecs) that should pass before actually sending + * = 0: can send immediately + * < 0: error condition; do not send packet */ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) { @@ -263,14 +305,18 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) if (unlikely(skb->len == 0)) return -EBADMSG; - if (hctx->s == 0) { - sk_reset_timer(sk, &hctx->no_feedback_timer, (jiffies + + switch (hctx->ccid3hctx_state) { + case TFRC_SSTATE_NO_SENT: + sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer, + (jiffies + usecs_to_jiffies(TFRC_INITIAL_TIMEOUT))); - hctx->last_win_count = 0; - hctx->t_last_win_count = now; + hctx->ccid3hctx_last_win_count = 0; + hctx->ccid3hctx_t_last_win_count = now; /* Set t_0 for initial packet */ - hctx->t_nom = now; + hctx->ccid3hctx_t_nom = now; + + hctx->ccid3hctx_s = skb->len; /* * Use initial RTT sample when available: recommended by erratum @@ -279,9 +325,9 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) */ if (dp->dccps_syn_rtt) { ccid3_pr_debug("SYN RTT = %uus\n", dp->dccps_syn_rtt); - hctx->rtt = dp->dccps_syn_rtt; - hctx->x = rfc3390_initial_rate(sk); - hctx->t_ld = now; + hctx->ccid3hctx_rtt = dp->dccps_syn_rtt; + hctx->ccid3hctx_x = rfc3390_initial_rate(sk); + hctx->ccid3hctx_t_ld = now; } else { /* * Sender does not have RTT sample: @@ -289,20 +335,17 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) * is needed in several parts (e.g. window counter); * - set sending rate X_pps = 1pps as per RFC 3448, 4.2. */ - hctx->rtt = DCCP_FALLBACK_RTT; - hctx->x = dp->dccps_mss_cache; - hctx->x <<= 6; + hctx->ccid3hctx_rtt = DCCP_FALLBACK_RTT; + hctx->ccid3hctx_x = hctx->ccid3hctx_s; + hctx->ccid3hctx_x <<= 6; } - - /* Compute t_ipi = s / X */ - hctx->s = ccid3_hc_tx_measure_packet_size(sk, skb->len); ccid3_update_send_interval(hctx); - /* Seed value for Oscillation Prevention (sec. 4.5) */ - hctx->r_sqmean = tfrc_scaled_sqrt(hctx->rtt); - - } else { - delay = ktime_us_delta(hctx->t_nom, now); + ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK); + break; + case TFRC_SSTATE_NO_FBACK: + case TFRC_SSTATE_FBACK: + delay = ktime_us_delta(hctx->ccid3hctx_t_nom, now); ccid3_pr_debug("delay=%ld\n", (long)delay); /* * Scheduling of packet transmissions [RFC 3448, 4.6] @@ -312,80 +355,99 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) * else * // send the packet in (t_nom - t_now) milliseconds. */ - if (delay >= TFRC_T_DELTA) - return (u32)delay / USEC_PER_MSEC; + if (delay - (s64)hctx->ccid3hctx_delta >= 1000) + return (u32)delay / 1000L; ccid3_hc_tx_update_win_count(hctx, now); + break; + case TFRC_SSTATE_TERM: + DCCP_BUG("%s(%p) - Illegal state TERM", dccp_role(sk), sk); + return -EINVAL; } /* prepare to send now (add options etc.) */ dp->dccps_hc_tx_insert_options = 1; - DCCP_SKB_CB(skb)->dccpd_ccval = hctx->last_win_count; + DCCP_SKB_CB(skb)->dccpd_ccval = hctx->ccid3hctx_last_win_count; /* set the nominal send time for the next following packet */ - hctx->t_nom = ktime_add_us(hctx->t_nom, hctx->t_ipi); - return CCID_PACKET_SEND_AT_ONCE; + hctx->ccid3hctx_t_nom = ktime_add_us(hctx->ccid3hctx_t_nom, + hctx->ccid3hctx_t_ipi); + return 0; } -static void ccid3_hc_tx_packet_sent(struct sock *sk, unsigned int len) +static void ccid3_hc_tx_packet_sent(struct sock *sk, int more, + unsigned int len) { struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - /* Changes to s will become effective the next time X is computed */ - hctx->s = ccid3_hc_tx_measure_packet_size(sk, len); + ccid3_hc_tx_update_s(hctx, len); - if (tfrc_tx_hist_add(&hctx->hist, dccp_sk(sk)->dccps_gss)) + if (tfrc_tx_hist_add(&hctx->ccid3hctx_hist, dccp_sk(sk)->dccps_gss)) DCCP_CRIT("packet history - out of memory!"); } static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) { struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - struct tfrc_tx_hist_entry *acked; + struct ccid3_options_received *opt_recv; ktime_t now; unsigned long t_nfb; - u32 r_sample; + u32 pinv, r_sample; /* we are only interested in ACKs */ if (!(DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_ACK || DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_DATAACK)) return; - /* - * Locate the acknowledged packet in the TX history. - * - * Returning "entry not found" here can for instance happen when - * - the host has not sent out anything (e.g. a passive server), - * - the Ack is outdated (packet with higher Ack number was received), - * - it is a bogus Ack (for a packet not sent on this connection). - */ - acked = tfrc_tx_hist_find_entry(hctx->hist, dccp_hdr_ack_seq(skb)); - if (acked == NULL) + /* ... and only in the established state */ + if (hctx->ccid3hctx_state != TFRC_SSTATE_FBACK && + hctx->ccid3hctx_state != TFRC_SSTATE_NO_FBACK) + return; + + opt_recv = &hctx->ccid3hctx_options_received; + now = ktime_get_real(); + + /* Estimate RTT from history if ACK number is valid */ + r_sample = tfrc_tx_hist_rtt(hctx->ccid3hctx_hist, + DCCP_SKB_CB(skb)->dccpd_ack_seq, now); + if (r_sample == 0) { + DCCP_WARN("%s(%p): %s with bogus ACK-%llu\n", dccp_role(sk), sk, + dccp_packet_name(DCCP_SKB_CB(skb)->dccpd_type), + (unsigned long long)DCCP_SKB_CB(skb)->dccpd_ack_seq); return; - /* For the sake of RTT sampling, ignore/remove all older entries */ - tfrc_tx_hist_purge(&acked->next); + } - /* Update the moving average for the RTT estimate (RFC 3448, 4.3) */ - now = ktime_get_real(); - r_sample = dccp_sample_rtt(sk, ktime_us_delta(now, acked->stamp)); - hctx->rtt = tfrc_ewma(hctx->rtt, r_sample, 9); + /* Update receive rate in units of 64 * bytes/second */ + hctx->ccid3hctx_x_recv = opt_recv->ccid3or_receive_rate; + hctx->ccid3hctx_x_recv <<= 6; + /* Update loss event rate (which is scaled by 1e6) */ + pinv = opt_recv->ccid3or_loss_event_rate; + if (pinv == ~0U || pinv == 0) /* see RFC 4342, 8.5 */ + hctx->ccid3hctx_p = 0; + else /* can not exceed 100% */ + hctx->ccid3hctx_p = scaled_div(1, pinv); + /* + * Validate new RTT sample and update moving average + */ + r_sample = dccp_sample_rtt(sk, r_sample); + hctx->ccid3hctx_rtt = tfrc_ewma(hctx->ccid3hctx_rtt, r_sample, 9); /* * Update allowed sending rate X as per draft rfc3448bis-00, 4.2/3 */ - if (!hctx->feedback) { - hctx->feedback = true; + if (hctx->ccid3hctx_state == TFRC_SSTATE_NO_FBACK) { + ccid3_hc_tx_set_state(sk, TFRC_SSTATE_FBACK); - if (hctx->t_rto == 0) { + if (hctx->ccid3hctx_t_rto == 0) { /* * Initial feedback packet: Larger Initial Windows (4.2) */ - hctx->x = rfc3390_initial_rate(sk); - hctx->t_ld = now; + hctx->ccid3hctx_x = rfc3390_initial_rate(sk); + hctx->ccid3hctx_t_ld = now; ccid3_update_send_interval(hctx); goto done_computing_x; - } else if (hctx->p == 0) { + } else if (hctx->ccid3hctx_p == 0) { /* * First feedback after nofeedback timer expiry (4.3) */ @@ -394,52 +456,25 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) } /* Update sending rate (step 4 of [RFC 3448, 4.3]) */ - if (hctx->p > 0) - hctx->x_calc = tfrc_calc_x(hctx->s, hctx->rtt, hctx->p); + if (hctx->ccid3hctx_p > 0) + hctx->ccid3hctx_x_calc = + tfrc_calc_x(hctx->ccid3hctx_s, + hctx->ccid3hctx_rtt, + hctx->ccid3hctx_p); ccid3_hc_tx_update_x(sk, &now); done_computing_x: ccid3_pr_debug("%s(%p), RTT=%uus (sample=%uus), s=%u, " "p=%u, X_calc=%u, X_recv=%u, X=%u\n", - dccp_role(sk), sk, hctx->rtt, r_sample, - hctx->s, hctx->p, hctx->x_calc, - (unsigned)(hctx->x_recv >> 6), - (unsigned)(hctx->x >> 6)); - /* - * Oscillation Reduction (RFC 3448, 4.5) - modifying t_ipi according to - * RTT changes, multiplying by X/X_inst = sqrt(R_sample)/R_sqmean. This - * can be useful if few connections share a link, avoiding that buffer - * fill levels (RTT) oscillate as a result of frequent adjustments to X. - * A useful presentation with background information is in - * Joerg Widmer, "Equation-Based Congestion Control", - * MSc Thesis, University of Mannheim, Germany, 2000 - * (sec. 3.6.4), who calls this ISM ("Inter-packet Space Modulation"). - */ - if (do_osc_prev) { - r_sample = tfrc_scaled_sqrt(r_sample); - /* - * The modulation can work in both ways: increase/decrease t_ipi - * according to long-term increases/decreases of the RTT. The - * former is a useful measure, since it works against queue - * build-up. The latter temporarily increases the sending rate, - * so that buffers fill up more quickly. This in turn causes - * the RTT to increase, so that either later reduction becomes - * necessary or the RTT stays at a very high level. Decreasing - * t_ipi is therefore not supported. - * Furthermore, during the initial slow-start phase the RTT - * naturally increases, where using the algorithm would cause - * delays. Hence it is disabled during the initial slow-start. - */ - if (r_sample > hctx->r_sqmean && hctx->p > 0) - hctx->t_ipi = div_u64((u64)hctx->t_ipi * (u64)r_sample, - hctx->r_sqmean); - hctx->t_ipi = min_t(u32, hctx->t_ipi, TFRC_T_MBI); - /* update R_sqmean _after_ computing the modulation factor */ - hctx->r_sqmean = tfrc_ewma(hctx->r_sqmean, r_sample, 9); - } + dccp_role(sk), + sk, hctx->ccid3hctx_rtt, r_sample, + hctx->ccid3hctx_s, hctx->ccid3hctx_p, + hctx->ccid3hctx_x_calc, + (unsigned)(hctx->ccid3hctx_x_recv >> 6), + (unsigned)(hctx->ccid3hctx_x >> 6)); /* unschedule no feedback timer */ - sk_stop_timer(sk, &hctx->no_feedback_timer); + sk_stop_timer(sk, &hctx->ccid3hctx_no_feedback_timer); /* * As we have calculated new ipi, delta, t_nom it is possible @@ -453,66 +488,95 @@ done_computing_x: * This can help avoid triggering the nofeedback timer too * often ('spinning') on LANs with small RTTs. */ - hctx->t_rto = max_t(u32, 4 * hctx->rtt, (CONFIG_IP_DCCP_CCID3_RTO * - (USEC_PER_SEC / 1000))); + hctx->ccid3hctx_t_rto = max_t(u32, 4 * hctx->ccid3hctx_rtt, + (CONFIG_IP_DCCP_CCID3_RTO * + (USEC_PER_SEC / 1000))); /* * Schedule no feedback timer to expire in * max(t_RTO, 2 * s/X) = max(t_RTO, 2 * t_ipi) */ - t_nfb = max(hctx->t_rto, 2 * hctx->t_ipi); + t_nfb = max(hctx->ccid3hctx_t_rto, 2 * hctx->ccid3hctx_t_ipi); ccid3_pr_debug("%s(%p), Scheduled no feedback timer to " "expire in %lu jiffies (%luus)\n", - dccp_role(sk), sk, usecs_to_jiffies(t_nfb), t_nfb); + dccp_role(sk), + sk, usecs_to_jiffies(t_nfb), t_nfb); - sk_reset_timer(sk, &hctx->no_feedback_timer, + sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer, jiffies + usecs_to_jiffies(t_nfb)); } -static int ccid3_hc_tx_parse_options(struct sock *sk, u8 packet_type, - u8 option, u8 *optval, u8 optlen) +static int ccid3_hc_tx_parse_options(struct sock *sk, unsigned char option, + unsigned char len, u16 idx, + unsigned char *value) { + int rc = 0; + const struct dccp_sock *dp = dccp_sk(sk); struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + struct ccid3_options_received *opt_recv; __be32 opt_val; - switch (option) { - case TFRC_OPT_RECEIVE_RATE: - case TFRC_OPT_LOSS_EVENT_RATE: - /* Must be ignored on Data packets, cf. RFC 4342 8.3 and 8.5 */ - if (packet_type == DCCP_PKT_DATA) - break; - if (unlikely(optlen != 4)) { - DCCP_WARN("%s(%p), invalid len %d for %u\n", - dccp_role(sk), sk, optlen, option); - return -EINVAL; - } - opt_val = ntohl(get_unaligned((__be32 *)optval)); + opt_recv = &hctx->ccid3hctx_options_received; - if (option == TFRC_OPT_RECEIVE_RATE) { - /* Receive Rate is kept in units of 64 bytes/second */ - hctx->x_recv = opt_val; - hctx->x_recv <<= 6; + if (opt_recv->ccid3or_seqno != dp->dccps_gsr) { + opt_recv->ccid3or_seqno = dp->dccps_gsr; + opt_recv->ccid3or_loss_event_rate = ~0; + opt_recv->ccid3or_loss_intervals_idx = 0; + opt_recv->ccid3or_loss_intervals_len = 0; + opt_recv->ccid3or_receive_rate = 0; + } - ccid3_pr_debug("%s(%p), RECEIVE_RATE=%u\n", - dccp_role(sk), sk, opt_val); + switch (option) { + case TFRC_OPT_LOSS_EVENT_RATE: + if (unlikely(len != 4)) { + DCCP_WARN("%s(%p), invalid len %d " + "for TFRC_OPT_LOSS_EVENT_RATE\n", + dccp_role(sk), sk, len); + rc = -EINVAL; } else { - /* Update the fixpoint Loss Event Rate fraction */ - hctx->p = tfrc_invert_loss_event_rate(opt_val); - + opt_val = get_unaligned((__be32 *)value); + opt_recv->ccid3or_loss_event_rate = ntohl(opt_val); ccid3_pr_debug("%s(%p), LOSS_EVENT_RATE=%u\n", - dccp_role(sk), sk, opt_val); + dccp_role(sk), sk, + opt_recv->ccid3or_loss_event_rate); } + break; + case TFRC_OPT_LOSS_INTERVALS: + opt_recv->ccid3or_loss_intervals_idx = idx; + opt_recv->ccid3or_loss_intervals_len = len; + ccid3_pr_debug("%s(%p), LOSS_INTERVALS=(%u, %u)\n", + dccp_role(sk), sk, + opt_recv->ccid3or_loss_intervals_idx, + opt_recv->ccid3or_loss_intervals_len); + break; + case TFRC_OPT_RECEIVE_RATE: + if (unlikely(len != 4)) { + DCCP_WARN("%s(%p), invalid len %d " + "for TFRC_OPT_RECEIVE_RATE\n", + dccp_role(sk), sk, len); + rc = -EINVAL; + } else { + opt_val = get_unaligned((__be32 *)value); + opt_recv->ccid3or_receive_rate = ntohl(opt_val); + ccid3_pr_debug("%s(%p), RECEIVE_RATE=%u\n", + dccp_role(sk), sk, + opt_recv->ccid3or_receive_rate); + } + break; } - return 0; + + return rc; } static int ccid3_hc_tx_init(struct ccid *ccid, struct sock *sk) { struct ccid3_hc_tx_sock *hctx = ccid_priv(ccid); - hctx->hist = NULL; - setup_timer(&hctx->no_feedback_timer, - ccid3_hc_tx_no_feedback_timer, (unsigned long)sk); + hctx->ccid3hctx_state = TFRC_SSTATE_NO_SENT; + hctx->ccid3hctx_hist = NULL; + setup_timer(&hctx->ccid3hctx_no_feedback_timer, + ccid3_hc_tx_no_feedback_timer, (unsigned long)sk); + return 0; } @@ -520,36 +584,42 @@ static void ccid3_hc_tx_exit(struct sock *sk) { struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - sk_stop_timer(sk, &hctx->no_feedback_timer); - tfrc_tx_hist_purge(&hctx->hist); + ccid3_hc_tx_set_state(sk, TFRC_SSTATE_TERM); + sk_stop_timer(sk, &hctx->ccid3hctx_no_feedback_timer); + + tfrc_tx_hist_purge(&hctx->ccid3hctx_hist); } static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info) { - info->tcpi_rto = ccid3_hc_tx_sk(sk)->t_rto; - info->tcpi_rtt = ccid3_hc_tx_sk(sk)->rtt; + struct ccid3_hc_tx_sock *hctx; + + /* Listen socks doesn't have a private CCID block */ + if (sk->sk_state == DCCP_LISTEN) + return; + + hctx = ccid3_hc_tx_sk(sk); + info->tcpi_rto = hctx->ccid3hctx_t_rto; + info->tcpi_rtt = hctx->ccid3hctx_rtt; } static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len, u32 __user *optval, int __user *optlen) { - const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - struct tfrc_tx_info tfrc; + const struct ccid3_hc_tx_sock *hctx; const void *val; + /* Listen socks doesn't have a private CCID block */ + if (sk->sk_state == DCCP_LISTEN) + return -EINVAL; + + hctx = ccid3_hc_tx_sk(sk); switch (optname) { case DCCP_SOCKOPT_CCID_TX_INFO: - if (len < sizeof(tfrc)) + if (len < sizeof(hctx->ccid3hctx_tfrc)) return -EINVAL; - tfrc.tfrctx_x = hctx->x; - tfrc.tfrctx_x_recv = hctx->x_recv; - tfrc.tfrctx_x_calc = hctx->x_calc; - tfrc.tfrctx_rtt = hctx->rtt; - tfrc.tfrctx_p = hctx->p; - tfrc.tfrctx_rto = hctx->t_rto; - tfrc.tfrctx_ipi = hctx->t_ipi; - len = sizeof(tfrc); - val = &tfrc; + len = sizeof(hctx->ccid3hctx_tfrc); + val = &hctx->ccid3hctx_tfrc; break; default: return -ENOPROTOOPT; @@ -564,82 +634,112 @@ static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len, /* * Receiver Half-Connection Routines */ + +/* CCID3 feedback types */ +enum ccid3_fback_type { + CCID3_FBACK_NONE = 0, + CCID3_FBACK_INITIAL, + CCID3_FBACK_PERIODIC, + CCID3_FBACK_PARAM_CHANGE +}; + +#ifdef CONFIG_IP_DCCP_CCID3_DEBUG +static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state) +{ + static char *ccid3_rx_state_names[] = { + [TFRC_RSTATE_NO_DATA] = "NO_DATA", + [TFRC_RSTATE_DATA] = "DATA", + [TFRC_RSTATE_TERM] = "TERM", + }; + + return ccid3_rx_state_names[state]; +} +#endif + +static void ccid3_hc_rx_set_state(struct sock *sk, + enum ccid3_hc_rx_states state) +{ + struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); + enum ccid3_hc_rx_states oldstate = hcrx->ccid3hcrx_state; + + ccid3_pr_debug("%s(%p) %-8.8s -> %s\n", + dccp_role(sk), sk, ccid3_rx_state_name(oldstate), + ccid3_rx_state_name(state)); + WARN_ON(state == oldstate); + hcrx->ccid3hcrx_state = state; +} + static void ccid3_hc_rx_send_feedback(struct sock *sk, const struct sk_buff *skb, enum ccid3_fback_type fbtype) { struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); + struct dccp_sock *dp = dccp_sk(sk); + ktime_t now; + s64 delta = 0; + + if (unlikely(hcrx->ccid3hcrx_state == TFRC_RSTATE_TERM)) + return; + + now = ktime_get_real(); switch (fbtype) { case CCID3_FBACK_INITIAL: - hcrx->x_recv = 0; - hcrx->p_inverse = ~0U; /* see RFC 4342, 8.5 */ + hcrx->ccid3hcrx_x_recv = 0; + hcrx->ccid3hcrx_pinv = ~0U; /* see RFC 4342, 8.5 */ break; case CCID3_FBACK_PARAM_CHANGE: - if (unlikely(hcrx->feedback == CCID3_FBACK_NONE)) { - /* - * rfc3448bis-06, 6.3.1: First packet(s) lost or marked - * FIXME: in rfc3448bis the receiver returns X_recv=0 - * here as it normally would in the first feedback packet. - * However this is not possible yet, since the code still - * uses RFC 3448, i.e. - * If (p > 0) - * Calculate X_calc using the TCP throughput equation. - * X = max(min(X_calc, 2*X_recv), s/t_mbi); - * would bring X down to s/t_mbi. That is why we return - * X_recv according to rfc3448bis-06 for the moment. - */ - u32 s = tfrc_rx_hist_packet_size(&hcrx->hist), - rtt = tfrc_rx_hist_rtt(&hcrx->hist); - - hcrx->x_recv = scaled_div32(s, 2 * rtt); - break; - } /* * When parameters change (new loss or p > p_prev), we do not * have a reliable estimate for R_m of [RFC 3448, 6.2] and so - * always check whether at least RTT time units were covered. + * need to reuse the previous value of X_recv. However, when + * X_recv was 0 (due to early loss), this would kill X down to + * s/t_mbi (i.e. one packet in 64 seconds). + * To avoid such drastic reduction, we approximate X_recv as + * the number of bytes since last feedback. + * This is a safe fallback, since X is bounded above by X_calc. */ - hcrx->x_recv = tfrc_rx_hist_x_recv(&hcrx->hist, hcrx->x_recv); - break; + if (hcrx->ccid3hcrx_x_recv > 0) + break; + /* fall through */ case CCID3_FBACK_PERIODIC: - /* - * Step (2) of rfc3448bis-06, 6.2: - * - if no data packets have been received, just restart timer - * - if data packets have been received, re-compute X_recv - */ - if (hcrx->hist.bytes_recvd == 0) - goto prepare_for_next_time; - hcrx->x_recv = tfrc_rx_hist_x_recv(&hcrx->hist, hcrx->x_recv); + delta = ktime_us_delta(now, hcrx->ccid3hcrx_tstamp_last_feedback); + if (delta <= 0) + DCCP_BUG("delta (%ld) <= 0", (long)delta); + else + hcrx->ccid3hcrx_x_recv = + scaled_div32(hcrx->ccid3hcrx_bytes_recv, delta); break; default: return; } - ccid3_pr_debug("X_recv=%u, 1/p=%u\n", hcrx->x_recv, hcrx->p_inverse); + ccid3_pr_debug("Interval %ldusec, X_recv=%u, 1/p=%u\n", (long)delta, + hcrx->ccid3hcrx_x_recv, hcrx->ccid3hcrx_pinv); - dccp_sk(sk)->dccps_hc_rx_insert_options = 1; - dccp_send_ack(sk); + hcrx->ccid3hcrx_tstamp_last_feedback = now; + hcrx->ccid3hcrx_last_counter = dccp_hdr(skb)->dccph_ccval; + hcrx->ccid3hcrx_bytes_recv = 0; -prepare_for_next_time: - tfrc_rx_hist_restart_byte_counter(&hcrx->hist); - hcrx->last_counter = dccp_hdr(skb)->dccph_ccval; - hcrx->feedback = fbtype; + dp->dccps_hc_rx_insert_options = 1; + dccp_send_ack(sk); } static int ccid3_hc_rx_insert_options(struct sock *sk, struct sk_buff *skb) { - const struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); + const struct ccid3_hc_rx_sock *hcrx; __be32 x_recv, pinv; if (!(sk->sk_state == DCCP_OPEN || sk->sk_state == DCCP_PARTOPEN)) return 0; + hcrx = ccid3_hc_rx_sk(sk); + if (dccp_packet_without_ack(skb)) return 0; - x_recv = htonl(hcrx->x_recv); - pinv = htonl(hcrx->p_inverse); + x_recv = htonl(hcrx->ccid3hcrx_x_recv); + pinv = htonl(hcrx->ccid3hcrx_pinv); if (dccp_insert_option(sk, skb, TFRC_OPT_LOSS_EVENT_RATE, &pinv, sizeof(pinv)) || @@ -662,95 +762,171 @@ static int ccid3_hc_rx_insert_options(struct sock *sk, struct sk_buff *skb) static u32 ccid3_first_li(struct sock *sk) { struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); - u32 s = tfrc_rx_hist_packet_size(&hcrx->hist), - rtt = tfrc_rx_hist_rtt(&hcrx->hist), x_recv, p; + u32 x_recv, p, delta; u64 fval; - /* - * rfc3448bis-06, 6.3.1: First data packet(s) are marked or lost. Set p - * to give the equivalent of X_target = s/(2*R). Thus fval = 2 and so p - * is about 20.64%. This yields an interval length of 4.84 (rounded up). - */ - if (unlikely(hcrx->feedback == CCID3_FBACK_NONE)) - return 5; + if (hcrx->ccid3hcrx_rtt == 0) { + DCCP_WARN("No RTT estimate available, using fallback RTT\n"); + hcrx->ccid3hcrx_rtt = DCCP_FALLBACK_RTT; + } - x_recv = tfrc_rx_hist_x_recv(&hcrx->hist, hcrx->x_recv); - if (x_recv == 0) - goto failed; + delta = ktime_to_us(net_timedelta(hcrx->ccid3hcrx_tstamp_last_feedback)); + x_recv = scaled_div32(hcrx->ccid3hcrx_bytes_recv, delta); + if (x_recv == 0) { /* would also trigger divide-by-zero */ + DCCP_WARN("X_recv==0\n"); + if ((x_recv = hcrx->ccid3hcrx_x_recv) == 0) { + DCCP_BUG("stored value of X_recv is zero"); + return ~0U; + } + } - fval = scaled_div32(scaled_div(s, rtt), x_recv); + fval = scaled_div(hcrx->ccid3hcrx_s, hcrx->ccid3hcrx_rtt); + fval = scaled_div32(fval, x_recv); p = tfrc_calc_x_reverse_lookup(fval); ccid3_pr_debug("%s(%p), receive rate=%u bytes/s, implied " "loss rate=%u\n", dccp_role(sk), sk, x_recv, p); - if (p > 0) - return scaled_div(1, p); -failed: - return UINT_MAX; + return p == 0 ? ~0U : scaled_div(1, p); } static void ccid3_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) { struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); + enum ccid3_fback_type do_feedback = CCID3_FBACK_NONE; const u64 ndp = dccp_sk(sk)->dccps_options_received.dccpor_ndp; const bool is_data_packet = dccp_data_packet(skb); + if (unlikely(hcrx->ccid3hcrx_state == TFRC_RSTATE_NO_DATA)) { + if (is_data_packet) { + const u32 payload = skb->len - dccp_hdr(skb)->dccph_doff * 4; + do_feedback = CCID3_FBACK_INITIAL; + ccid3_hc_rx_set_state(sk, TFRC_RSTATE_DATA); + hcrx->ccid3hcrx_s = payload; + /* + * Not necessary to update ccid3hcrx_bytes_recv here, + * since X_recv = 0 for the first feedback packet (cf. + * RFC 3448, 6.3) -- gerrit + */ + } + goto update_records; + } + + if (tfrc_rx_hist_duplicate(&hcrx->ccid3hcrx_hist, skb)) + return; /* done receiving */ + + if (is_data_packet) { + const u32 payload = skb->len - dccp_hdr(skb)->dccph_doff * 4; + /* + * Update moving-average of s and the sum of received payload bytes + */ + hcrx->ccid3hcrx_s = tfrc_ewma(hcrx->ccid3hcrx_s, payload, 9); + hcrx->ccid3hcrx_bytes_recv += payload; + } + /* * Perform loss detection and handle pending losses */ - if (tfrc_rx_congestion_event(&hcrx->hist, &hcrx->li_hist, - skb, ndp, ccid3_first_li, sk)) - ccid3_hc_rx_send_feedback(sk, skb, CCID3_FBACK_PARAM_CHANGE); + if (tfrc_rx_handle_loss(&hcrx->ccid3hcrx_hist, &hcrx->ccid3hcrx_li_hist, + skb, ndp, ccid3_first_li, sk)) { + do_feedback = CCID3_FBACK_PARAM_CHANGE; + goto done_receiving; + } + + if (tfrc_rx_hist_loss_pending(&hcrx->ccid3hcrx_hist)) + return; /* done receiving */ + /* - * Feedback for first non-empty data packet (RFC 3448, 6.3) + * Handle data packets: RTT sampling and monitoring p */ - else if (unlikely(hcrx->feedback == CCID3_FBACK_NONE && is_data_packet)) - ccid3_hc_rx_send_feedback(sk, skb, CCID3_FBACK_INITIAL); + if (unlikely(!is_data_packet)) + goto update_records; + + if (!tfrc_lh_is_initialised(&hcrx->ccid3hcrx_li_hist)) { + const u32 sample = tfrc_rx_hist_sample_rtt(&hcrx->ccid3hcrx_hist, skb); + /* + * Empty loss history: no loss so far, hence p stays 0. + * Sample RTT values, since an RTT estimate is required for the + * computation of p when the first loss occurs; RFC 3448, 6.3.1. + */ + if (sample != 0) + hcrx->ccid3hcrx_rtt = tfrc_ewma(hcrx->ccid3hcrx_rtt, sample, 9); + + } else if (tfrc_lh_update_i_mean(&hcrx->ccid3hcrx_li_hist, skb)) { + /* + * Step (3) of [RFC 3448, 6.1]: Recompute I_mean and, if I_mean + * has decreased (resp. p has increased), send feedback now. + */ + do_feedback = CCID3_FBACK_PARAM_CHANGE; + } + /* * Check if the periodic once-per-RTT feedback is due; RFC 4342, 10.3 */ - else if (!tfrc_rx_hist_loss_pending(&hcrx->hist) && is_data_packet && - SUB16(dccp_hdr(skb)->dccph_ccval, hcrx->last_counter) > 3) - ccid3_hc_rx_send_feedback(sk, skb, CCID3_FBACK_PERIODIC); + if (SUB16(dccp_hdr(skb)->dccph_ccval, hcrx->ccid3hcrx_last_counter) > 3) + do_feedback = CCID3_FBACK_PERIODIC; + +update_records: + tfrc_rx_hist_add_packet(&hcrx->ccid3hcrx_hist, skb, ndp); + +done_receiving: + if (do_feedback) + ccid3_hc_rx_send_feedback(sk, skb, do_feedback); } static int ccid3_hc_rx_init(struct ccid *ccid, struct sock *sk) { struct ccid3_hc_rx_sock *hcrx = ccid_priv(ccid); - tfrc_lh_init(&hcrx->li_hist); - return tfrc_rx_hist_init(&hcrx->hist, sk); + hcrx->ccid3hcrx_state = TFRC_RSTATE_NO_DATA; + tfrc_lh_init(&hcrx->ccid3hcrx_li_hist); + return tfrc_rx_hist_alloc(&hcrx->ccid3hcrx_hist); } static void ccid3_hc_rx_exit(struct sock *sk) { struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); - tfrc_rx_hist_purge(&hcrx->hist); - tfrc_lh_cleanup(&hcrx->li_hist); + ccid3_hc_rx_set_state(sk, TFRC_RSTATE_TERM); + + tfrc_rx_hist_purge(&hcrx->ccid3hcrx_hist); + tfrc_lh_cleanup(&hcrx->ccid3hcrx_li_hist); } static void ccid3_hc_rx_get_info(struct sock *sk, struct tcp_info *info) { + const struct ccid3_hc_rx_sock *hcrx; + + /* Listen socks doesn't have a private CCID block */ + if (sk->sk_state == DCCP_LISTEN) + return; + + hcrx = ccid3_hc_rx_sk(sk); + info->tcpi_ca_state = hcrx->ccid3hcrx_state; info->tcpi_options |= TCPI_OPT_TIMESTAMPS; - info->tcpi_rcv_rtt = tfrc_rx_hist_rtt(&ccid3_hc_rx_sk(sk)->hist); + info->tcpi_rcv_rtt = hcrx->ccid3hcrx_rtt; } static int ccid3_hc_rx_getsockopt(struct sock *sk, const int optname, int len, u32 __user *optval, int __user *optlen) { - const struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); + const struct ccid3_hc_rx_sock *hcrx; struct tfrc_rx_info rx_info; const void *val; + /* Listen socks doesn't have a private CCID block */ + if (sk->sk_state == DCCP_LISTEN) + return -EINVAL; + + hcrx = ccid3_hc_rx_sk(sk); switch (optname) { case DCCP_SOCKOPT_CCID_RX_INFO: if (len < sizeof(rx_info)) return -EINVAL; - rx_info.tfrcrx_x_recv = hcrx->x_recv; - rx_info.tfrcrx_rtt = tfrc_rx_hist_rtt(&hcrx->hist); - rx_info.tfrcrx_p = tfrc_invert_loss_event_rate(hcrx->p_inverse); + rx_info.tfrcrx_x_recv = hcrx->ccid3hcrx_x_recv; + rx_info.tfrcrx_rtt = hcrx->ccid3hcrx_rtt; + rx_info.tfrcrx_p = hcrx->ccid3hcrx_pinv == 0 ? ~0U : + scaled_div(1, hcrx->ccid3hcrx_pinv); len = sizeof(rx_info); val = &rx_info; break; @@ -786,9 +962,6 @@ static struct ccid_operations ccid3 = { .ccid_hc_tx_getsockopt = ccid3_hc_tx_getsockopt, }; -module_param(do_osc_prev, bool, 0644); -MODULE_PARM_DESC(do_osc_prev, "Use Oscillation Prevention (RFC 3448, 4.5)"); - #ifdef CONFIG_IP_DCCP_CCID3_DEBUG module_param(ccid3_debug, bool, 0644); MODULE_PARM_DESC(ccid3_debug, "Enable debug messages"); @@ -796,19 +969,6 @@ MODULE_PARM_DESC(ccid3_debug, "Enable debug messages"); static __init int ccid3_module_init(void) { - struct timespec tp; - - /* - * Without a fine-grained clock resolution, RTTs/X_recv are not sampled - * correctly and feedback is sent either too early or too late. - */ - hrtimer_get_res(CLOCK_MONOTONIC, &tp); - if (tp.tv_sec || tp.tv_nsec > DCCP_TIME_RESOLUTION * NSEC_PER_USEC) { - printk(KERN_ERR "%s: Timer too coarse (%ld usec), need %u-usec" - " resolution - check your clocksource.\n", __func__, - tp.tv_nsec/NSEC_PER_USEC, DCCP_TIME_RESOLUTION); - return -ESOCKTNOSUPPORT; - } return ccid_register(&ccid3); } module_init(ccid3_module_init); diff --git a/net/dccp/ccids/ccid3.h b/net/dccp/ccids/ccid3.h index af6e1bf937d9..49ca32bd7e79 100644 --- a/net/dccp/ccids/ccid3.h +++ b/net/dccp/ccids/ccid3.h @@ -47,22 +47,11 @@ /* Two seconds as per RFC 3448 4.2 */ #define TFRC_INITIAL_TIMEOUT (2 * USEC_PER_SEC) -/* Maximum backoff interval t_mbi (RFC 3448, 4.3) */ -#define TFRC_T_MBI (64 * USEC_PER_SEC) +/* In usecs - half the scheduling granularity as per RFC3448 4.6 */ +#define TFRC_OPSYS_HALF_TIME_GRAN (USEC_PER_SEC / (2 * HZ)) -/* - * The t_delta parameter (RFC 3448, 4.6): delays of less than %USEC_PER_MSEC are - * rounded down to 0, since sk_reset_timer() here uses millisecond granularity. - * Hence we can use a constant t_delta = %USEC_PER_MSEC when HZ >= 500. A coarse - * resolution of HZ < 500 means that the error is below one timer tick (t_gran) - * when using the constant t_delta = t_gran / 2 = %USEC_PER_SEC / (2 * HZ). - */ -#if (HZ >= 500) -# define TFRC_T_DELTA USEC_PER_MSEC -#else -# define TFRC_T_DELTA (USEC_PER_SEC / (2 * HZ)) -#warning Coarse CONFIG_HZ resolution -- higher value recommended for TFRC. -#endif +/* Parameter t_mbi from [RFC 3448, 4.3]: backoff interval in seconds */ +#define TFRC_T_MBI 64 enum ccid3_options { TFRC_OPT_LOSS_EVENT_RATE = 192, @@ -70,43 +59,62 @@ enum ccid3_options { TFRC_OPT_RECEIVE_RATE = 194, }; +struct ccid3_options_received { + u64 ccid3or_seqno:48, + ccid3or_loss_intervals_idx:16; + u16 ccid3or_loss_intervals_len; + u32 ccid3or_loss_event_rate; + u32 ccid3or_receive_rate; +}; + +/* TFRC sender states */ +enum ccid3_hc_tx_states { + TFRC_SSTATE_NO_SENT = 1, + TFRC_SSTATE_NO_FBACK, + TFRC_SSTATE_FBACK, + TFRC_SSTATE_TERM, +}; + /** struct ccid3_hc_tx_sock - CCID3 sender half-connection socket * - * @x - Current sending rate in 64 * bytes per second - * @x_recv - Receive rate in 64 * bytes per second - * @x_calc - Calculated rate in bytes per second - * @rtt - Estimate of current round trip time in usecs - * @r_sqmean - Estimate of long-term RTT (RFC 3448, 4.5) - * @p - Current loss event rate (0-1) scaled by 1000000 - * @s - Packet size in bytes - * @t_rto - Nofeedback Timer setting in usecs - * @t_ipi - Interpacket (send) interval (RFC 3448, 4.6) in usecs - * @feedback - Whether feedback has been received or not - * @last_win_count - Last window counter sent - * @t_last_win_count - Timestamp of earliest packet with - * last_win_count value sent - * @no_feedback_timer - Handle to no feedback timer - * @t_ld - Time last doubled during slow start - * @t_nom - Nominal send time of next packet - * @hist - Packet history + * @ccid3hctx_x - Current sending rate in 64 * bytes per second + * @ccid3hctx_x_recv - Receive rate in 64 * bytes per second + * @ccid3hctx_x_calc - Calculated rate in bytes per second + * @ccid3hctx_rtt - Estimate of current round trip time in usecs + * @ccid3hctx_p - Current loss event rate (0-1) scaled by 1000000 + * @ccid3hctx_s - Packet size in bytes + * @ccid3hctx_t_rto - Nofeedback Timer setting in usecs + * @ccid3hctx_t_ipi - Interpacket (send) interval (RFC 3448, 4.6) in usecs + * @ccid3hctx_state - Sender state, one of %ccid3_hc_tx_states + * @ccid3hctx_last_win_count - Last window counter sent + * @ccid3hctx_t_last_win_count - Timestamp of earliest packet + * with last_win_count value sent + * @ccid3hctx_no_feedback_timer - Handle to no feedback timer + * @ccid3hctx_t_ld - Time last doubled during slow start + * @ccid3hctx_t_nom - Nominal send time of next packet + * @ccid3hctx_delta - Send timer delta (RFC 3448, 4.6) in usecs + * @ccid3hctx_hist - Packet history + * @ccid3hctx_options_received - Parsed set of retrieved options */ struct ccid3_hc_tx_sock { - u64 x; - u64 x_recv; - u32 x_calc; - u32 rtt; - u16 r_sqmean; - u32 p; - u32 t_rto; - u32 t_ipi; - u16 s; - bool feedback:1; - u8 last_win_count; - ktime_t t_last_win_count; - struct timer_list no_feedback_timer; - ktime_t t_ld; - ktime_t t_nom; - struct tfrc_tx_hist_entry *hist; + struct tfrc_tx_info ccid3hctx_tfrc; +#define ccid3hctx_x ccid3hctx_tfrc.tfrctx_x +#define ccid3hctx_x_recv ccid3hctx_tfrc.tfrctx_x_recv +#define ccid3hctx_x_calc ccid3hctx_tfrc.tfrctx_x_calc +#define ccid3hctx_rtt ccid3hctx_tfrc.tfrctx_rtt +#define ccid3hctx_p ccid3hctx_tfrc.tfrctx_p +#define ccid3hctx_t_rto ccid3hctx_tfrc.tfrctx_rto +#define ccid3hctx_t_ipi ccid3hctx_tfrc.tfrctx_ipi + u16 ccid3hctx_s; + enum ccid3_hc_tx_states ccid3hctx_state:8; + u8 ccid3hctx_last_win_count; + ktime_t ccid3hctx_t_last_win_count; + struct timer_list ccid3hctx_no_feedback_timer; + ktime_t ccid3hctx_t_ld; + ktime_t ccid3hctx_t_nom; + u32 ccid3hctx_delta; + struct tfrc_tx_hist_entry *ccid3hctx_hist; + struct ccid3_options_received ccid3hctx_options_received; }; static inline struct ccid3_hc_tx_sock *ccid3_hc_tx_sk(const struct sock *sk) @@ -116,32 +124,41 @@ static inline struct ccid3_hc_tx_sock *ccid3_hc_tx_sk(const struct sock *sk) return hctx; } - -enum ccid3_fback_type { - CCID3_FBACK_NONE = 0, - CCID3_FBACK_INITIAL, - CCID3_FBACK_PERIODIC, - CCID3_FBACK_PARAM_CHANGE +/* TFRC receiver states */ +enum ccid3_hc_rx_states { + TFRC_RSTATE_NO_DATA = 1, + TFRC_RSTATE_DATA, + TFRC_RSTATE_TERM = 127, }; /** struct ccid3_hc_rx_sock - CCID3 receiver half-connection socket * - * @last_counter - Tracks window counter (RFC 4342, 8.1) - * @feedback - The type of the feedback last sent - * @x_recv - Receiver estimate of send rate (RFC 3448, sec. 4.3) - * @tstamp_last_feedback - Time at which last feedback was sent - * @hist - Packet history (loss detection + RTT sampling) - * @li_hist - Loss Interval database - * @p_inverse - Inverse of Loss Event Rate (RFC 4342, sec. 8.5) + * @ccid3hcrx_x_recv - Receiver estimate of send rate (RFC 3448 4.3) + * @ccid3hcrx_rtt - Receiver estimate of rtt (non-standard) + * @ccid3hcrx_p - Current loss event rate (RFC 3448 5.4) + * @ccid3hcrx_last_counter - Tracks window counter (RFC 4342, 8.1) + * @ccid3hcrx_state - Receiver state, one of %ccid3_hc_rx_states + * @ccid3hcrx_bytes_recv - Total sum of DCCP payload bytes + * @ccid3hcrx_x_recv - Receiver estimate of send rate (RFC 3448, sec. 4.3) + * @ccid3hcrx_rtt - Receiver estimate of RTT + * @ccid3hcrx_tstamp_last_feedback - Time at which last feedback was sent + * @ccid3hcrx_tstamp_last_ack - Time at which last feedback was sent + * @ccid3hcrx_hist - Packet history (loss detection + RTT sampling) + * @ccid3hcrx_li_hist - Loss Interval database + * @ccid3hcrx_s - Received packet size in bytes + * @ccid3hcrx_pinv - Inverse of Loss Event Rate (RFC 4342, sec. 8.5) */ struct ccid3_hc_rx_sock { - u8 last_counter:4; - enum ccid3_fback_type feedback:4; - u32 x_recv; - ktime_t tstamp_last_feedback; - struct tfrc_rx_hist hist; - struct tfrc_loss_hist li_hist; -#define p_inverse li_hist.i_mean + u8 ccid3hcrx_last_counter:4; + enum ccid3_hc_rx_states ccid3hcrx_state:8; + u32 ccid3hcrx_bytes_recv; + u32 ccid3hcrx_x_recv; + u32 ccid3hcrx_rtt; + ktime_t ccid3hcrx_tstamp_last_feedback; + struct tfrc_rx_hist ccid3hcrx_hist; + struct tfrc_loss_hist ccid3hcrx_li_hist; + u16 ccid3hcrx_s; +#define ccid3hcrx_pinv ccid3hcrx_li_hist.i_mean }; static inline struct ccid3_hc_rx_sock *ccid3_hc_rx_sk(const struct sock *sk) diff --git a/net/dccp/ccids/lib/loss_interval.c b/net/dccp/ccids/lib/loss_interval.c index b1ae8f8259e5..5b3ce0688c5c 100644 --- a/net/dccp/ccids/lib/loss_interval.c +++ b/net/dccp/ccids/lib/loss_interval.c @@ -86,26 +86,21 @@ static void tfrc_lh_calc_i_mean(struct tfrc_loss_hist *lh) /** * tfrc_lh_update_i_mean - Update the `open' loss interval I_0 - * This updates I_mean as the sequence numbers increase. As a consequence, the - * open loss interval I_0 increases, hence p = W_tot/max(I_tot0, I_tot1) - * decreases, and thus there is no need to send renewed feedback. + * For recomputing p: returns `true' if p > p_prev <=> 1/p < 1/p_prev */ -void tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb) +u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb) { struct tfrc_loss_interval *cur = tfrc_lh_peek(lh); + u32 old_i_mean = lh->i_mean; s64 len; if (cur == NULL) /* not initialised */ - return; - - /* FIXME: should probably also count non-data packets (RFC 4342, 6.1) */ - if (!dccp_data_packet(skb)) - return; + return 0; len = dccp_delta_seqno(cur->li_seqno, DCCP_SKB_CB(skb)->dccpd_seq) + 1; if (len - (s64)cur->li_length <= 0) /* duplicate or reordered */ - return; + return 0; if (SUB16(dccp_hdr(skb)->dccph_ccval, cur->li_ccval) > 4) /* @@ -119,11 +114,14 @@ void tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb) cur->li_is_closed = 1; if (tfrc_lh_length(lh) == 1) /* due to RFC 3448, 6.3.1 */ - return; + return 0; cur->li_length = len; tfrc_lh_calc_i_mean(lh); + + return (lh->i_mean < old_i_mean); } +EXPORT_SYMBOL_GPL(tfrc_lh_update_i_mean); /* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */ static inline u8 tfrc_lh_is_new_loss(struct tfrc_loss_interval *cur, @@ -140,18 +138,18 @@ static inline u8 tfrc_lh_is_new_loss(struct tfrc_loss_interval *cur, * @sk: Used by @calc_first_li in caller-specific way (subtyping) * Updates I_mean and returns 1 if a new interval has in fact been added to @lh. */ -bool tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh, - u32 (*calc_first_li)(struct sock *), struct sock *sk) +int tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh, + u32 (*calc_first_li)(struct sock *), struct sock *sk) { struct tfrc_loss_interval *cur = tfrc_lh_peek(lh), *new; if (cur != NULL && !tfrc_lh_is_new_loss(cur, tfrc_rx_hist_loss_prev(rh))) - return false; + return 0; new = tfrc_lh_demand_next(lh); if (unlikely(new == NULL)) { DCCP_CRIT("Cannot allocate/add loss record."); - return false; + return 0; } new->li_seqno = tfrc_rx_hist_loss_prev(rh)->tfrchrx_seqno; @@ -169,7 +167,7 @@ bool tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh, tfrc_lh_calc_i_mean(lh); } - return true; + return 1; } EXPORT_SYMBOL_GPL(tfrc_lh_interval_add); diff --git a/net/dccp/ccids/lib/loss_interval.h b/net/dccp/ccids/lib/loss_interval.h index d08a226db43e..246018a3b269 100644 --- a/net/dccp/ccids/lib/loss_interval.h +++ b/net/dccp/ccids/lib/loss_interval.h @@ -67,9 +67,9 @@ static inline u8 tfrc_lh_length(struct tfrc_loss_hist *lh) struct tfrc_rx_hist; -extern bool tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *, +extern int tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *, u32 (*first_li)(struct sock *), struct sock *); -extern void tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *); +extern u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *); extern void tfrc_lh_cleanup(struct tfrc_loss_hist *lh); #endif /* _DCCP_LI_HIST_ */ diff --git a/net/dccp/ccids/lib/packet_history.c b/net/dccp/ccids/lib/packet_history.c index cce9f03bda3e..6cc108afdc3b 100644 --- a/net/dccp/ccids/lib/packet_history.c +++ b/net/dccp/ccids/lib/packet_history.c @@ -40,6 +40,18 @@ #include "packet_history.h" #include "../../dccp.h" +/** + * tfrc_tx_hist_entry - Simple singly-linked TX history list + * @next: next oldest entry (LIFO order) + * @seqno: sequence number of this entry + * @stamp: send time of packet with sequence number @seqno + */ +struct tfrc_tx_hist_entry { + struct tfrc_tx_hist_entry *next; + u64 seqno; + ktime_t stamp; +}; + /* * Transmitter History Routines */ @@ -61,6 +73,15 @@ void tfrc_tx_packet_history_exit(void) } } +static struct tfrc_tx_hist_entry * + tfrc_tx_hist_find_entry(struct tfrc_tx_hist_entry *head, u64 seqno) +{ + while (head != NULL && head->seqno != seqno) + head = head->next; + + return head; +} + int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno) { struct tfrc_tx_hist_entry *entry = kmem_cache_alloc(tfrc_tx_hist_slab, gfp_any()); @@ -90,6 +111,25 @@ void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp) } EXPORT_SYMBOL_GPL(tfrc_tx_hist_purge); +u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head, const u64 seqno, + const ktime_t now) +{ + u32 rtt = 0; + struct tfrc_tx_hist_entry *packet = tfrc_tx_hist_find_entry(head, seqno); + + if (packet != NULL) { + rtt = ktime_us_delta(now, packet->stamp); + /* + * Garbage-collect older (irrelevant) entries: + */ + tfrc_tx_hist_purge(&packet->next); + } + + return rtt; +} +EXPORT_SYMBOL_GPL(tfrc_tx_hist_rtt); + + /* * Receiver History Routines */ @@ -151,31 +191,14 @@ int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(tfrc_rx_hist_duplicate); - -static void __tfrc_rx_hist_swap(struct tfrc_rx_hist *h, const u8 a, const u8 b) -{ - struct tfrc_rx_hist_entry *tmp = h->ring[a]; - - h->ring[a] = h->ring[b]; - h->ring[b] = tmp; -} - static void tfrc_rx_hist_swap(struct tfrc_rx_hist *h, const u8 a, const u8 b) { - __tfrc_rx_hist_swap(h, tfrc_rx_hist_index(h, a), - tfrc_rx_hist_index(h, b)); -} + const u8 idx_a = tfrc_rx_hist_index(h, a), + idx_b = tfrc_rx_hist_index(h, b); + struct tfrc_rx_hist_entry *tmp = h->ring[idx_a]; -/** - * tfrc_rx_hist_resume_rtt_sampling - Prepare RX history for RTT sampling - * This is called after loss detection has finished, when the history entry - * with the index of `loss_count' holds the highest-received sequence number. - * RTT sampling requires this information at ring[0] (tfrc_rx_hist_sample_rtt). - */ -static inline void tfrc_rx_hist_resume_rtt_sampling(struct tfrc_rx_hist *h) -{ - __tfrc_rx_hist_swap(h, 0, tfrc_rx_hist_index(h, h->loss_count)); - h->loss_count = h->loss_start = 0; + h->ring[idx_a] = h->ring[idx_b]; + h->ring[idx_b] = tmp; } /* @@ -192,8 +215,10 @@ static void __do_track_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u64 n1) u64 s0 = tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno, s1 = DCCP_SKB_CB(skb)->dccpd_seq; - if (!dccp_loss_free(s0, s1, n1)) /* gap between S0 and S1 */ + if (!dccp_loss_free(s0, s1, n1)) { /* gap between S0 and S1 */ h->loss_count = 1; + tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_entry(h, 1), skb, n1); + } } static void __one_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n2) @@ -215,7 +240,8 @@ static void __one_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n2 if (dccp_loss_free(s2, s1, n1)) { /* hole is filled: S0, S2, and S1 are consecutive */ - tfrc_rx_hist_resume_rtt_sampling(h); + h->loss_count = 0; + h->loss_start = tfrc_rx_hist_index(h, 1); } else /* gap between S2 and S1: just update loss_prev */ tfrc_rx_hist_entry_from_skb(tfrc_rx_hist_loss_prev(h), skb, n2); @@ -268,7 +294,8 @@ static int __two_after_loss(struct tfrc_rx_hist *h, struct sk_buff *skb, u32 n3) if (dccp_loss_free(s1, s2, n2)) { /* entire hole filled by S0, S3, S1, S2 */ - tfrc_rx_hist_resume_rtt_sampling(h); + h->loss_start = tfrc_rx_hist_index(h, 2); + h->loss_count = 0; } else { /* gap remains between S1 and S2 */ h->loss_start = tfrc_rx_hist_index(h, 1); @@ -312,7 +339,8 @@ static void __three_after_loss(struct tfrc_rx_hist *h) if (dccp_loss_free(s2, s3, n3)) { /* no gap between S2 and S3: entire hole is filled */ - tfrc_rx_hist_resume_rtt_sampling(h); + h->loss_start = tfrc_rx_hist_index(h, 3); + h->loss_count = 0; } else { /* gap between S2 and S3 */ h->loss_start = tfrc_rx_hist_index(h, 2); @@ -326,13 +354,13 @@ static void __three_after_loss(struct tfrc_rx_hist *h) } /** - * tfrc_rx_congestion_event - Loss detection and further processing - * @h: The non-empty RX history object - * @lh: Loss Intervals database to update - * @skb: Currently received packet - * @ndp: The NDP count belonging to @skb - * @first_li: Caller-dependent computation of first loss interval in @lh - * @sk: Used by @calc_first_li (see tfrc_lh_interval_add) + * tfrc_rx_handle_loss - Loss detection and further processing + * @h: The non-empty RX history object + * @lh: Loss Intervals database to update + * @skb: Currently received packet + * @ndp: The NDP count belonging to @skb + * @calc_first_li: Caller-dependent computation of first loss interval in @lh + * @sk: Used by @calc_first_li (see tfrc_lh_interval_add) * Chooses action according to pending loss, updates LI database when a new * loss was detected, and does required post-processing. Returns 1 when caller * should send feedback, 0 otherwise. @@ -340,20 +368,15 @@ static void __three_after_loss(struct tfrc_rx_hist *h) * records accordingly, the caller should not perform any more RX history * operations when loss_count is greater than 0 after calling this function. */ -bool tfrc_rx_congestion_event(struct tfrc_rx_hist *h, - struct tfrc_loss_hist *lh, - struct sk_buff *skb, const u64 ndp, - u32 (*first_li)(struct sock *), struct sock *sk) +int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, + struct tfrc_loss_hist *lh, + struct sk_buff *skb, const u64 ndp, + u32 (*calc_first_li)(struct sock *), struct sock *sk) { - bool new_event = false; - - if (tfrc_rx_hist_duplicate(h, skb)) - return 0; + int is_new_loss = 0; if (h->loss_count == 0) { __do_track_loss(h, skb, ndp); - tfrc_rx_hist_sample_rtt(h, skb); - tfrc_rx_hist_add_packet(h, skb, ndp); } else if (h->loss_count == 1) { __one_after_loss(h, skb, ndp); } else if (h->loss_count != 2) { @@ -362,57 +385,34 @@ bool tfrc_rx_congestion_event(struct tfrc_rx_hist *h, /* * Update Loss Interval database and recycle RX records */ - new_event = tfrc_lh_interval_add(lh, h, first_li, sk); + is_new_loss = tfrc_lh_interval_add(lh, h, calc_first_li, sk); __three_after_loss(h); } - - /* - * Update moving-average of `s' and the sum of received payload bytes. - */ - if (dccp_data_packet(skb)) { - const u32 payload = skb->len - dccp_hdr(skb)->dccph_doff * 4; - - h->packet_size = tfrc_ewma(h->packet_size, payload, 9); - h->bytes_recvd += payload; - } - - /* RFC 3448, 6.1: update I_0, whose growth implies p <= p_prev */ - if (!new_event) - tfrc_lh_update_i_mean(lh, skb); - - return new_event; + return is_new_loss; } -EXPORT_SYMBOL_GPL(tfrc_rx_congestion_event); +EXPORT_SYMBOL_GPL(tfrc_rx_handle_loss); -/* Compute the sending rate X_recv measured between feedback intervals */ -u32 tfrc_rx_hist_x_recv(struct tfrc_rx_hist *h, const u32 last_x_recv) +int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) { - u64 bytes = h->bytes_recvd, last_rtt = h->rtt_estimate; - s64 delta = ktime_to_us(net_timedelta(h->bytes_start)); - - WARN_ON(delta <= 0); - /* - * Ensure that the sampling interval for X_recv is at least one RTT, - * by extending the sampling interval backwards in time, over the last - * R_(m-1) seconds, as per rfc3448bis-06, 6.2. - * To reduce noise (e.g. when the RTT changes often), this is only - * done when delta is smaller than RTT/2. - */ - if (last_x_recv > 0 && delta < last_rtt/2) { - tfrc_pr_debug("delta < RTT ==> %ld us < %u us\n", - (long)delta, (unsigned)last_rtt); + int i; - delta = (bytes ? delta : 0) + last_rtt; - bytes += div_u64((u64)last_x_recv * last_rtt, USEC_PER_SEC); + for (i = 0; i <= TFRC_NDUPACK; i++) { + h->ring[i] = kmem_cache_alloc(tfrc_rx_hist_slab, GFP_ATOMIC); + if (h->ring[i] == NULL) + goto out_free; } - if (unlikely(bytes == 0)) { - DCCP_WARN("X_recv == 0, using old value of %u\n", last_x_recv); - return last_x_recv; + h->loss_count = h->loss_start = 0; + return 0; + +out_free: + while (i-- != 0) { + kmem_cache_free(tfrc_rx_hist_slab, h->ring[i]); + h->ring[i] = NULL; } - return scaled_div32(bytes, delta); + return -ENOBUFS; } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_x_recv); +EXPORT_SYMBOL_GPL(tfrc_rx_hist_alloc); void tfrc_rx_hist_purge(struct tfrc_rx_hist *h) { @@ -426,81 +426,73 @@ void tfrc_rx_hist_purge(struct tfrc_rx_hist *h) } EXPORT_SYMBOL_GPL(tfrc_rx_hist_purge); -static int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) +/** + * tfrc_rx_hist_rtt_last_s - reference entry to compute RTT samples against + */ +static inline struct tfrc_rx_hist_entry * + tfrc_rx_hist_rtt_last_s(const struct tfrc_rx_hist *h) { - int i; - - memset(h, 0, sizeof(*h)); - - for (i = 0; i <= TFRC_NDUPACK; i++) { - h->ring[i] = kmem_cache_alloc(tfrc_rx_hist_slab, GFP_ATOMIC); - if (h->ring[i] == NULL) { - tfrc_rx_hist_purge(h); - return -ENOBUFS; - } - } - return 0; + return h->ring[0]; } -int tfrc_rx_hist_init(struct tfrc_rx_hist *h, struct sock *sk) +/** + * tfrc_rx_hist_rtt_prev_s: previously suitable (wrt rtt_last_s) RTT-sampling entry + */ +static inline struct tfrc_rx_hist_entry * + tfrc_rx_hist_rtt_prev_s(const struct tfrc_rx_hist *h) { - if (tfrc_rx_hist_alloc(h)) - return -ENOBUFS; - /* - * Initialise first entry with GSR to start loss detection as early as - * possible. Code using this must not use any other fields. The entry - * will be overwritten once the CCID updates its received packets. - */ - tfrc_rx_hist_loss_prev(h)->tfrchrx_seqno = dccp_sk(sk)->dccps_gsr; - return 0; + return h->ring[h->rtt_sample_prev]; } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_init); /** * tfrc_rx_hist_sample_rtt - Sample RTT from timestamp / CCVal - * Based on ideas presented in RFC 4342, 8.1. This function expects that no loss - * is pending and uses the following history entries (via rtt_sample_prev): - * - h->ring[0] contains the most recent history entry prior to @skb; - * - h->ring[1] is an unused `dummy' entry when the current difference is 0; + * Based on ideas presented in RFC 4342, 8.1. Returns 0 if it was not able + * to compute a sample with given data - calling function should check this. */ -void tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb) +u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb) { - struct tfrc_rx_hist_entry *last = h->ring[0]; - u32 sample, delta_v; - - /* - * When not to sample: - * - on non-data packets - * (RFC 4342, 8.1: CCVal only fully defined for data packets); - * - when no data packets have been received yet - * (FIXME: using sampled packet size as indicator here); - * - as long as there are gaps in the sequence space (pending loss). - */ - if (!dccp_data_packet(skb) || h->packet_size == 0 || - tfrc_rx_hist_loss_pending(h)) - return; + u32 sample = 0, + delta_v = SUB16(dccp_hdr(skb)->dccph_ccval, + tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); + + if (delta_v < 1 || delta_v > 4) { /* unsuitable CCVal delta */ + if (h->rtt_sample_prev == 2) { /* previous candidate stored */ + sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, + tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); + if (sample) + sample = 4 / sample * + ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp, + tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp); + else /* + * FIXME: This condition is in principle not + * possible but occurs when CCID is used for + * two-way data traffic. I have tried to trace + * it, but the cause does not seem to be here. + */ + DCCP_BUG("please report to dccp@vger.kernel.org" + " => prev = %u, last = %u", + tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, + tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); + } else if (delta_v < 1) { + h->rtt_sample_prev = 1; + goto keep_ref_for_next_time; + } - h->rtt_sample_prev = 0; /* reset previous candidate */ + } else if (delta_v == 4) /* optimal match */ + sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp)); + else { /* suboptimal match */ + h->rtt_sample_prev = 2; + goto keep_ref_for_next_time; + } - delta_v = SUB16(dccp_hdr(skb)->dccph_ccval, last->tfrchrx_ccval); - if (delta_v == 0) { /* less than RTT/4 difference */ - h->rtt_sample_prev = 1; - return; + if (unlikely(sample > DCCP_SANE_RTT_MAX)) { + DCCP_WARN("RTT sample %u too large, using max\n", sample); + sample = DCCP_SANE_RTT_MAX; } - sample = dccp_sane_rtt(ktime_to_us(net_timedelta(last->tfrchrx_tstamp))); - if (delta_v <= 4) /* between RTT/4 and RTT */ - sample *= 4 / delta_v; - else if (!(sample < h->rtt_estimate && sample > h->rtt_estimate/2)) - /* - * Optimisation: CCVal difference is greater than 1 RTT, yet the - * sample is less than the local RTT estimate; which means that - * the RTT estimate is too high. - * To avoid noise, it is not done if the sample is below RTT/2. - */ - return; + h->rtt_sample_prev = 0; /* use current entry as next reference */ +keep_ref_for_next_time: - /* Use a lower weight than usual to increase responsiveness */ - h->rtt_estimate = tfrc_ewma(h->rtt_estimate, sample, 5); + return sample; } EXPORT_SYMBOL_GPL(tfrc_rx_hist_sample_rtt); diff --git a/net/dccp/ccids/lib/packet_history.h b/net/dccp/ccids/lib/packet_history.h index 555e65cd73a0..461cc91cce88 100644 --- a/net/dccp/ccids/lib/packet_history.h +++ b/net/dccp/ccids/lib/packet_history.h @@ -40,28 +40,12 @@ #include #include "tfrc.h" -/** - * tfrc_tx_hist_entry - Simple singly-linked TX history list - * @next: next oldest entry (LIFO order) - * @seqno: sequence number of this entry - * @stamp: send time of packet with sequence number @seqno - */ -struct tfrc_tx_hist_entry { - struct tfrc_tx_hist_entry *next; - u64 seqno; - ktime_t stamp; -}; - -static inline struct tfrc_tx_hist_entry * - tfrc_tx_hist_find_entry(struct tfrc_tx_hist_entry *head, u64 seqno) -{ - while (head != NULL && head->seqno != seqno) - head = head->next; - return head; -} +struct tfrc_tx_hist_entry; extern int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno); extern void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp); +extern u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head, + const u64 seqno, const ktime_t now); /* Subtraction a-b modulo-16, respects circular wrap-around */ #define SUB16(a, b) (((a) + 16 - (b)) & 0xF) @@ -91,22 +75,12 @@ struct tfrc_rx_hist_entry { * @loss_count: Number of entries in circular history * @loss_start: Movable index (for loss detection) * @rtt_sample_prev: Used during RTT sampling, points to candidate entry - * @rtt_estimate: Receiver RTT estimate - * @packet_size: Packet size in bytes (as per RFC 3448, 3.1) - * @bytes_recvd: Number of bytes received since @bytes_start - * @bytes_start: Start time for counting @bytes_recvd */ struct tfrc_rx_hist { struct tfrc_rx_hist_entry *ring[TFRC_NDUPACK + 1]; u8 loss_count:2, loss_start:2; - /* Receiver RTT sampling */ #define rtt_sample_prev loss_start - u32 rtt_estimate; - /* Receiver sampling of application payload lengths */ - u32 packet_size, - bytes_recvd; - ktime_t bytes_start; }; /** @@ -150,50 +124,20 @@ static inline bool tfrc_rx_hist_loss_pending(const struct tfrc_rx_hist *h) return h->loss_count > 0; } -/* - * Accessor functions to retrieve parameters sampled by the RX history - */ -static inline u32 tfrc_rx_hist_packet_size(const struct tfrc_rx_hist *h) -{ - if (h->packet_size == 0) { - DCCP_WARN("No sample for s, using fallback\n"); - return TCP_MIN_RCVMSS; - } - return h->packet_size; - -} -static inline u32 tfrc_rx_hist_rtt(const struct tfrc_rx_hist *h) -{ - if (h->rtt_estimate == 0) { - DCCP_WARN("No RTT estimate available, using fallback RTT\n"); - return DCCP_FALLBACK_RTT; - } - return h->rtt_estimate; -} - -static inline void tfrc_rx_hist_restart_byte_counter(struct tfrc_rx_hist *h) -{ - h->bytes_recvd = 0; - h->bytes_start = ktime_get_real(); -} - -extern u32 tfrc_rx_hist_x_recv(struct tfrc_rx_hist *h, const u32 last_x_recv); - - extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, const struct sk_buff *skb, const u64 ndp); extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); struct tfrc_loss_hist; -extern bool tfrc_rx_congestion_event(struct tfrc_rx_hist *h, - struct tfrc_loss_hist *lh, - struct sk_buff *skb, const u64 ndp, - u32 (*first_li)(struct sock *sk), - struct sock *sk); -extern void tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, - const struct sk_buff *skb); -extern int tfrc_rx_hist_init(struct tfrc_rx_hist *h, struct sock *sk); +extern int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, + struct tfrc_loss_hist *lh, + struct sk_buff *skb, const u64 ndp, + u32 (*first_li)(struct sock *sk), + struct sock *sk); +extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, + const struct sk_buff *skb); +extern int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h); extern void tfrc_rx_hist_purge(struct tfrc_rx_hist *h); #endif /* _DCCP_PKT_HIST_ */ diff --git a/net/dccp/ccids/lib/tfrc.h b/net/dccp/ccids/lib/tfrc.h index ede12f53de5a..ed9857527acf 100644 --- a/net/dccp/ccids/lib/tfrc.h +++ b/net/dccp/ccids/lib/tfrc.h @@ -47,21 +47,6 @@ static inline u32 scaled_div32(u64 a, u64 b) return result; } -/** - * tfrc_scaled_sqrt - Compute scaled integer sqrt(x) for 0 < x < 2^22-1 - * Uses scaling to improve accuracy of the integer approximation of sqrt(). The - * scaling factor of 2^10 limits the maximum @sample to 4e6; this is okay for - * clamped RTT samples (dccp_sample_rtt). - * Should best be used for expressions of type sqrt(x)/sqrt(y), since then the - * scaling factor is neutralised. For this purpose, it avoids returning zero. - */ -static inline u16 tfrc_scaled_sqrt(const u32 sample) -{ - const unsigned long non_zero_sample = sample ? : 1; - - return int_sqrt(non_zero_sample << 10); -} - /** * tfrc_ewma - Exponentially weighted moving average * @weight: Weight to be used as damping factor, in units of 1/10 @@ -73,7 +58,6 @@ static inline u32 tfrc_ewma(const u32 avg, const u32 newval, const u8 weight) extern u32 tfrc_calc_x(u16 s, u32 R, u32 p); extern u32 tfrc_calc_x_reverse_lookup(u32 fvalue); -extern u32 tfrc_invert_loss_event_rate(u32 loss_event_rate); extern int tfrc_tx_packet_history_init(void); extern void tfrc_tx_packet_history_exit(void); diff --git a/net/dccp/ccids/lib/tfrc_equation.c b/net/dccp/ccids/lib/tfrc_equation.c index 38239c4d5e14..2f20a29cffe4 100644 --- a/net/dccp/ccids/lib/tfrc_equation.c +++ b/net/dccp/ccids/lib/tfrc_equation.c @@ -632,16 +632,8 @@ u32 tfrc_calc_x(u16 s, u32 R, u32 p) if (p <= TFRC_CALC_X_SPLIT) { /* 0.0000 < p <= 0.05 */ if (p < TFRC_SMALLEST_P) { /* 0.0000 < p < 0.0001 */ - /* - * In the congestion-avoidance phase p decays towards 0 - * when there are no further losses, so this case is - * natural. Truncating to p_min = 0.01% means that the - * maximum achievable throughput is limited to about - * X_calc_max = 122.4 * s/RTT (see RFC 3448, 3.1); e.g. - * with s=1500 bytes, RTT=0.01 s: X_calc_max = 147 Mbps. - */ - tfrc_pr_debug("Value of p (%d) below resolution. " - "Substituting %d\n", p, TFRC_SMALLEST_P); + DCCP_WARN("Value of p (%d) below resolution. " + "Substituting %d\n", p, TFRC_SMALLEST_P); index = 0; } else /* 0.0001 <= p <= 0.05 */ index = p/TFRC_SMALLEST_P - 1; @@ -666,6 +658,7 @@ u32 tfrc_calc_x(u16 s, u32 R, u32 p) result = scaled_div(s, R); return scaled_div32(result, f); } + EXPORT_SYMBOL_GPL(tfrc_calc_x); /** @@ -700,19 +693,5 @@ u32 tfrc_calc_x_reverse_lookup(u32 fvalue) index = tfrc_binsearch(fvalue, 0); return (index + 1) * 1000000 / TFRC_CALC_X_ARRSIZE; } -EXPORT_SYMBOL_GPL(tfrc_calc_x_reverse_lookup); -/** - * tfrc_invert_loss_event_rate - Compute p so that 10^6 corresponds to 100% - * When @loss_event_rate is large, there is a chance that p is truncated to 0. - * To avoid re-entering slow-start in that case, we set p = TFRC_SMALLEST_P > 0. - */ -u32 tfrc_invert_loss_event_rate(u32 loss_event_rate) -{ - if (loss_event_rate == UINT_MAX) /* see RFC 4342, 8.5 */ - return 0; - if (unlikely(loss_event_rate == 0)) /* map 1/0 into 100% */ - return 1000000; - return max_t(u32, scaled_div(1, loss_event_rate), TFRC_SMALLEST_P); -} -EXPORT_SYMBOL_GPL(tfrc_invert_loss_event_rate); +EXPORT_SYMBOL_GPL(tfrc_calc_x_reverse_lookup); diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 5281190aa19c..b4bc6e095a0e 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -42,11 +42,9 @@ extern int dccp_debug; #define dccp_pr_debug(format, a...) DCCP_PR_DEBUG(dccp_debug, format, ##a) #define dccp_pr_debug_cat(format, a...) DCCP_PRINTK(dccp_debug, format, ##a) -#define dccp_debug(fmt, a...) dccp_pr_debug_cat(KERN_DEBUG fmt, ##a) #else #define dccp_pr_debug(format, a...) #define dccp_pr_debug_cat(format, a...) -#define dccp_debug(format, a...) #endif extern struct inet_hashinfo dccp_hashinfo; @@ -63,14 +61,11 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); * - DCCP-Reset with ACK Subheader and 4 bytes of Reset Code fields * Hence a safe upper bound for the maximum option length is 1020-28 = 992 */ -#define MAX_DCCP_SPECIFIC_HEADER (255 * sizeof(uint32_t)) +#define MAX_DCCP_SPECIFIC_HEADER (255 * sizeof(int)) #define DCCP_MAX_PACKET_HDR 28 #define DCCP_MAX_OPT_LEN (MAX_DCCP_SPECIFIC_HEADER - DCCP_MAX_PACKET_HDR) #define MAX_DCCP_HEADER (MAX_DCCP_SPECIFIC_HEADER + MAX_HEADER) -/* Upper bound for initial feature-negotiation overhead (padded to 32 bits) */ -#define DCCP_FEATNEG_OVERHEAD (32 * sizeof(uint32_t)) - #define DCCP_TIMEWAIT_LEN (60 * HZ) /* how long to wait to destroy TIME-WAIT * state, about 60 seconds */ @@ -86,13 +81,10 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); */ #define DCCP_RTO_MAX ((unsigned)(64 * HZ)) -/* DCCP base time resolution - 10 microseconds (RFC 4340, 13.1 ... 13.3) */ -#define DCCP_TIME_RESOLUTION 10 - /* * RTT sampling: sanity bounds and fallback RTT value from RFC 4340, section 3.4 */ -#define DCCP_SANE_RTT_MIN (10 * DCCP_TIME_RESOLUTION) +#define DCCP_SANE_RTT_MIN 100 #define DCCP_FALLBACK_RTT (USEC_PER_SEC / 5) #define DCCP_SANE_RTT_MAX (3 * USEC_PER_SEC) @@ -103,6 +95,12 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); extern int sysctl_dccp_request_retries; extern int sysctl_dccp_retries1; extern int sysctl_dccp_retries2; +extern int sysctl_dccp_feat_sequence_window; +extern int sysctl_dccp_feat_rx_ccid; +extern int sysctl_dccp_feat_tx_ccid; +extern int sysctl_dccp_feat_ack_ratio; +extern int sysctl_dccp_feat_send_ack_vector; +extern int sysctl_dccp_feat_send_ndp_count; extern int sysctl_dccp_tx_qlen; extern int sysctl_dccp_sync_ratelimit; @@ -237,22 +235,8 @@ extern void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, extern void dccp_send_sync(struct sock *sk, const u64 seq, const enum dccp_pkt_type pkt_type); -/* - * TX Packet Dequeueing Interface - */ -extern void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb); -extern bool dccp_qpolicy_full(struct sock *sk); -extern void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb); -extern struct sk_buff *dccp_qpolicy_top(struct sock *sk); -extern struct sk_buff *dccp_qpolicy_pop(struct sock *sk); -extern bool dccp_qpolicy_param_ok(struct sock *sk, __be32 param); - -/* - * TX Packet Output and TX Timers - */ -extern void dccp_write_xmit(struct sock *sk); +extern void dccp_write_xmit(struct sock *sk, int block); extern void dccp_write_space(struct sock *sk); -extern void dccp_flush_write_queue(struct sock *sk, long *time_budget); extern void dccp_init_xmit_timers(struct sock *sk); static inline void dccp_clear_xmit_timers(struct sock *sk) @@ -268,8 +252,7 @@ extern const char *dccp_state_name(const int state); extern void dccp_set_state(struct sock *sk, const int state); extern void dccp_done(struct sock *sk); -extern int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp, - struct sk_buff const *skb); +extern void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb); extern int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb); @@ -334,14 +317,7 @@ extern struct sk_buff *dccp_ctl_make_reset(struct sock *sk, extern int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code); extern void dccp_send_close(struct sock *sk, const int active); extern int dccp_invalid_packet(struct sk_buff *skb); - -static inline u32 dccp_sane_rtt(long usec_sample) -{ - if (unlikely(usec_sample <= 0 || usec_sample > DCCP_SANE_RTT_MAX)) - DCCP_WARN("RTT sample %ld out of bounds!\n", usec_sample); - return clamp_val(usec_sample, DCCP_SANE_RTT_MIN, DCCP_SANE_RTT_MAX); -} -extern u32 dccp_sample_rtt(struct sock *sk, long delta); +extern u32 dccp_sample_rtt(struct sock *sk, long delta); static inline int dccp_bad_service_code(const struct sock *sk, const __be32 service) @@ -435,62 +411,36 @@ static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack, static inline void dccp_update_gsr(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); + const struct dccp_minisock *dmsk = dccp_msk(sk); dp->dccps_gsr = seq; - /* Sequence validity window depends on remote Sequence Window (7.5.1) */ - dp->dccps_swl = SUB48(ADD48(dp->dccps_gsr, 1), dp->dccps_r_seq_win / 4); - /* - * Adjust SWL so that it is not below ISR. In contrast to RFC 4340, - * 7.5.1 we perform this check beyond the initial handshake: W/W' are - * always > 32, so for the first W/W' packets in the lifetime of a - * connection we always have to adjust SWL. - * A second reason why we are doing this is that the window depends on - * the feature-remote value of Sequence Window: nothing stops the peer - * from updating this value while we are busy adjusting SWL for the - * first W packets (we would have to count from scratch again then). - * Therefore it is safer to always make sure that the Sequence Window - * is not artificially extended by a peer who grows SWL downwards by - * continually updating the feature-remote Sequence-Window. - * If sequence numbers wrap it is bad luck. But that will take a while - * (48 bit), and this measure prevents Sequence-number attacks. - */ - if (before48(dp->dccps_swl, dp->dccps_isr)) - dp->dccps_swl = dp->dccps_isr; - dp->dccps_swh = ADD48(dp->dccps_gsr, (3 * dp->dccps_r_seq_win) / 4); + dccp_set_seqno(&dp->dccps_swl, + dp->dccps_gsr + 1 - (dmsk->dccpms_sequence_window / 4)); + dccp_set_seqno(&dp->dccps_swh, + dp->dccps_gsr + (3 * dmsk->dccpms_sequence_window) / 4); } static inline void dccp_update_gss(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - dp->dccps_gss = seq; - /* Ack validity window depends on local Sequence Window value (7.5.1) */ - dp->dccps_awl = SUB48(ADD48(dp->dccps_gss, 1), dp->dccps_l_seq_win); - /* Adjust AWL so that it is not below ISS - see comment above for SWL */ - if (before48(dp->dccps_awl, dp->dccps_iss)) - dp->dccps_awl = dp->dccps_iss; - dp->dccps_awh = dp->dccps_gss; -} - -static inline int dccp_ackvec_pending(const struct sock *sk) -{ - return dccp_sk(sk)->dccps_hc_rx_ackvec != NULL && - !dccp_ackvec_is_empty(dccp_sk(sk)->dccps_hc_rx_ackvec); + dp->dccps_awh = dp->dccps_gss = seq; + dccp_set_seqno(&dp->dccps_awl, + (dp->dccps_gss - + dccp_msk(sk)->dccpms_sequence_window + 1)); } static inline int dccp_ack_pending(const struct sock *sk) { - return dccp_ackvec_pending(sk) || inet_csk_ack_scheduled(sk); + const struct dccp_sock *dp = dccp_sk(sk); + return dp->dccps_timestamp_echo != 0 || +#ifdef CONFIG_IP_DCCP_ACKVEC + (dccp_msk(sk)->dccpms_send_ack_vector && + dccp_ackvec_pending(dp->dccps_hc_rx_ackvec)) || +#endif + inet_csk_ack_scheduled(sk); } -extern int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val); -extern int dccp_feat_finalise_settings(struct dccp_sock *dp); -extern int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq); -extern int dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*, - struct sk_buff *skb); -extern int dccp_feat_activate_values(struct sock *sk, struct list_head *fn); -extern void dccp_feat_list_purge(struct list_head *fn_list); - extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb); extern int dccp_insert_options_rsk(struct dccp_request_sock*, struct sk_buff*); extern int dccp_insert_option_elapsed_time(struct sock *sk, diff --git a/net/dccp/diag.c b/net/dccp/diag.c index 93aae7c95550..d8a3509b26f6 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -29,7 +29,7 @@ static void dccp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_backoff = icsk->icsk_backoff; info->tcpi_pmtu = icsk->icsk_pmtu_cookie; - if (dp->dccps_hc_rx_ackvec != NULL) + if (dccp_msk(sk)->dccpms_send_ack_vector) info->tcpi_options |= TCPI_OPT_SACK; ccid_hc_rx_get_info(dp->dccps_hc_rx_ccid, sk, info); diff --git a/net/dccp/feat.c b/net/dccp/feat.c index f94c7c9d1a7f..933a0ecf8d46 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -1,19 +1,11 @@ /* * net/dccp/feat.c * - * Feature negotiation for the DCCP protocol (RFC 4340, section 6) - * - * Copyright (c) 2008 The University of Aberdeen, Scotland, UK - * Copyright (c) 2008 Gerrit Renker - * Rewrote from scratch, some bits from earlier code by - * Copyright (c) 2005 Andrea Bittau - * + * An implementation of the DCCP protocol + * Andrea Bittau * * ASSUMPTIONS * ----------- - * o Feature negotiation is coordinated with connection setup (as in TCP), wild - * changes of parameters of an established connection are not supported. - * o Changing NN values (Ack Ratio only) is supported in state OPEN/PARTOPEN. * o All currently known SP features have 1-byte quantities. If in the future * extensions of RFCs 4340..42 define features with item lengths larger than * one byte, a feature-specific extension of the code will be required. @@ -23,1510 +15,635 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + #include + #include "ccid.h" #include "feat.h" -/* feature-specific sysctls - initialised to the defaults from RFC 4340, 6.4 */ -unsigned long sysctl_dccp_sequence_window __read_mostly = 100; -int sysctl_dccp_rx_ccid __read_mostly = 2, - sysctl_dccp_tx_ccid __read_mostly = 2; +#define DCCP_FEAT_SP_NOAGREE (-123) -/* - * Feature activation handlers. - * - * These all use an u64 argument, to provide enough room for NN/SP features. At - * this stage the negotiated values have been checked to be within their range. - */ -static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) +int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, + u8 *val, u8 len, gfp_t gfp) { - struct dccp_sock *dp = dccp_sk(sk); - struct ccid *new_ccid = ccid_new(ccid, sk, rx, gfp_any()); + struct dccp_opt_pend *opt; - if (new_ccid == NULL) - return -ENOMEM; + dccp_feat_debug(type, feature, *val); - if (rx) { - ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); - dp->dccps_hc_rx_ccid = new_ccid; - } else { - ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); - dp->dccps_hc_tx_ccid = new_ccid; + if (len > 3) { + DCCP_WARN("invalid length %d\n", len); + return -EINVAL; + } + /* XXX add further sanity checks */ + + /* check if that feature is already being negotiated */ + list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { + /* ok we found a negotiation for this option already */ + if (opt->dccpop_feat == feature && opt->dccpop_type == type) { + dccp_pr_debug("Replacing old\n"); + /* replace */ + BUG_ON(opt->dccpop_val == NULL); + kfree(opt->dccpop_val); + opt->dccpop_val = val; + opt->dccpop_len = len; + opt->dccpop_conf = 0; + return 0; + } } - return 0; -} -static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) -{ - struct dccp_sock *dp = dccp_sk(sk); + /* negotiation for a new feature */ + opt = kmalloc(sizeof(*opt), gfp); + if (opt == NULL) + return -ENOMEM; - if (rx) { - dp->dccps_r_seq_win = seq_win; - /* propagate changes to update SWL/SWH */ - dccp_update_gsr(sk, dp->dccps_gsr); - } else { - dp->dccps_l_seq_win = seq_win; - /* propagate changes to update AWL */ - dccp_update_gss(sk, dp->dccps_gss); - } - return 0; -} + opt->dccpop_type = type; + opt->dccpop_feat = feature; + opt->dccpop_len = len; + opt->dccpop_val = val; + opt->dccpop_conf = 0; + opt->dccpop_sc = NULL; -static int dccp_hdlr_ack_ratio(struct sock *sk, u64 ratio, bool rx) -{ -#ifndef __CCID2_COPES_GRACEFULLY_WITH_DYNAMIC_ACK_RATIO_UPDATES__ - /* - * FIXME: This is required until several problems in the CCID-2 code are - * resolved. The CCID-2 code currently does not cope well; using dynamic - * Ack Ratios greater than 1 caused instabilities. These were manifest - * in hangups and long RTO timeouts (1...3 seconds). Until this has been - * stabilised, it is safer not to activate dynamic Ack Ratio changes. - */ - dccp_pr_debug("Not changing %s Ack Ratio from 1 to %u\n", - rx ? "RX" : "TX", (u16)ratio); - ratio = 1; -#endif - if (rx) - dccp_sk(sk)->dccps_r_ack_ratio = ratio; - else - dccp_sk(sk)->dccps_l_ack_ratio = ratio; + BUG_ON(opt->dccpop_val == NULL); + + list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending); return 0; } -static int dccp_hdlr_ackvec(struct sock *sk, u64 enable, bool rx) +EXPORT_SYMBOL_GPL(dccp_feat_change); + +static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) { struct dccp_sock *dp = dccp_sk(sk); + struct dccp_minisock *dmsk = dccp_msk(sk); + /* figure out if we are changing our CCID or the peer's */ + const int rx = type == DCCPO_CHANGE_R; + const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid; + struct ccid *new_ccid; + + /* Check if nothing is being changed. */ + if (ccid_nr == new_ccid_nr) + return 0; + + new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC); + if (new_ccid == NULL) + return -ENOMEM; if (rx) { - if (enable && dp->dccps_hc_rx_ackvec == NULL) { - dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(gfp_any()); - if (dp->dccps_hc_rx_ackvec == NULL) - return -ENOMEM; - } else if (!enable) { - dccp_ackvec_free(dp->dccps_hc_rx_ackvec); - dp->dccps_hc_rx_ackvec = NULL; - } + ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); + dp->dccps_hc_rx_ccid = new_ccid; + dmsk->dccpms_rx_ccid = new_ccid_nr; + } else { + ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); + dp->dccps_hc_tx_ccid = new_ccid; + dmsk->dccpms_tx_ccid = new_ccid_nr; } - return 0; -} -static int dccp_hdlr_ndp(struct sock *sk, u64 enable, bool rx) -{ - if (!rx) - dccp_sk(sk)->dccps_send_ndp_count = (enable > 0); return 0; } -/* - * Minimum Checksum Coverage is located at the RX side (9.2.1). This means that - * `rx' holds when the sending peer informs about his partial coverage via a - * ChangeR() option. In the other case, we are the sender and the receiver - * announces its coverage via ChangeL() options. The policy here is to honour - * such communication by enabling the corresponding partial coverage - but only - * if it has not been set manually before; the warning here means that all - * packets will be dropped. - */ -static int dccp_hdlr_min_cscov(struct sock *sk, u64 cscov, bool rx) +static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) { - struct dccp_sock *dp = dccp_sk(sk); + dccp_feat_debug(type, feat, val); - if (rx) - dp->dccps_pcrlen = cscov; - else { - if (dp->dccps_pcslen == 0) - dp->dccps_pcslen = cscov; - else if (cscov > dp->dccps_pcslen) - DCCP_WARN("CsCov %u too small, peer requires >= %u\n", - dp->dccps_pcslen, (u8)cscov); + switch (feat) { + case DCCPF_CCID: + return dccp_feat_update_ccid(sk, type, val); + default: + dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n", + dccp_feat_typename(type), feat); + break; } return 0; } -static const struct { - u8 feat_num; /* DCCPF_xxx */ - enum dccp_feat_type rxtx; /* RX or TX */ - enum dccp_feat_type reconciliation; /* SP or NN */ - u8 default_value; /* as in 6.4 */ - int (*activation_hdlr)(struct sock *sk, u64 val, bool rx); -/* - * Lookup table for location and type of features (from RFC 4340/4342) - * +--------------------------+----+-----+----+----+---------+-----------+ - * | Feature | Location | Reconc. | Initial | Section | - * | | RX | TX | SP | NN | Value | Reference | - * +--------------------------+----+-----+----+----+---------+-----------+ - * | DCCPF_CCID | | X | X | | 2 | 10 | - * | DCCPF_SHORT_SEQNOS | | X | X | | 0 | 7.6.1 | - * | DCCPF_SEQUENCE_WINDOW | | X | | X | 100 | 7.5.2 | - * | DCCPF_ECN_INCAPABLE | X | | X | | 0 | 12.1 | - * | DCCPF_ACK_RATIO | | X | | X | 2 | 11.3 | - * | DCCPF_SEND_ACK_VECTOR | X | | X | | 0 | 11.5 | - * | DCCPF_SEND_NDP_COUNT | | X | X | | 0 | 7.7.2 | - * | DCCPF_MIN_CSUM_COVER | X | | X | | 0 | 9.2.1 | - * | DCCPF_DATA_CHECKSUM | X | | X | | 0 | 9.3.1 | - * | DCCPF_SEND_LEV_RATE | X | | X | | 0 | 4342/8.4 | - * +--------------------------+----+-----+----+----+---------+-----------+ - */ -} dccp_feat_table[] = { - { DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2, dccp_hdlr_ccid }, - { DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0, NULL }, - { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100, dccp_hdlr_seq_win }, - { DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0, NULL }, - { DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2, dccp_hdlr_ack_ratio}, - { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_ackvec }, - { DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0, dccp_hdlr_ndp }, - { DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_min_cscov}, - { DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0, NULL }, - { DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0, NULL }, -}; -#define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table) - -/** - * dccp_feat_index - Hash function to map feature number into array position - * Returns consecutive array index or -1 if the feature is not understood. - */ -static int dccp_feat_index(u8 feat_num) +static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt, + u8 *rpref, u8 rlen) { - /* The first 9 entries are occupied by the types from RFC 4340, 6.4 */ - if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM) - return feat_num - 1; + struct dccp_sock *dp = dccp_sk(sk); + u8 *spref, slen, *res = NULL; + int i, j, rc, agree = 1; + BUG_ON(rpref == NULL); + + /* check if we are the black sheep */ + if (dp->dccps_role == DCCP_ROLE_CLIENT) { + spref = rpref; + slen = rlen; + rpref = opt->dccpop_val; + rlen = opt->dccpop_len; + } else { + spref = opt->dccpop_val; + slen = opt->dccpop_len; + } /* - * Other features: add cases for new feature types here after adding - * them to the above table. + * Now we have server preference list in spref and client preference in + * rpref */ - switch (feat_num) { - case DCCPF_SEND_LEV_RATE: - return DCCP_FEAT_SUPPORTED_MAX - 1; - } - return -1; -} - -static u8 dccp_feat_type(u8 feat_num) -{ - int idx = dccp_feat_index(feat_num); - - if (idx < 0) - return FEAT_UNKNOWN; - return dccp_feat_table[idx].reconciliation; -} + BUG_ON(spref == NULL); + BUG_ON(rpref == NULL); -static int dccp_feat_default_value(u8 feat_num) -{ - int idx = dccp_feat_index(feat_num); + /* FIXME sanity check vals */ - return idx < 0 ? : dccp_feat_table[idx].default_value; -} - -/* - * Debugging and verbose-printing section - */ -static const char *dccp_feat_fname(const u8 feat) -{ - static const char *feature_names[] = { - [DCCPF_RESERVED] = "Reserved", - [DCCPF_CCID] = "CCID", - [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", - [DCCPF_SEQUENCE_WINDOW] = "Sequence Window", - [DCCPF_ECN_INCAPABLE] = "ECN Incapable", - [DCCPF_ACK_RATIO] = "Ack Ratio", - [DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector", - [DCCPF_SEND_NDP_COUNT] = "Send NDP Count", - [DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage", - [DCCPF_DATA_CHECKSUM] = "Send Data Checksum", - }; - if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC) - return feature_names[DCCPF_RESERVED]; - - if (feat == DCCPF_SEND_LEV_RATE) - return "Send Loss Event Rate"; - if (feat >= DCCPF_MIN_CCID_SPECIFIC) - return "CCID-specific"; - - return feature_names[feat]; -} - -static const char *dccp_feat_sname[] = { "DEFAULT", "INITIALISING", "CHANGING", - "UNSTABLE", "STABLE" }; - -#ifdef CONFIG_IP_DCCP_DEBUG -static const char *dccp_feat_oname(const u8 opt) -{ - switch (opt) { - case DCCPO_CHANGE_L: return "Change_L"; - case DCCPO_CONFIRM_L: return "Confirm_L"; - case DCCPO_CHANGE_R: return "Change_R"; - case DCCPO_CONFIRM_R: return "Confirm_R"; + /* Are values in any order? XXX Lame "algorithm" here */ + for (i = 0; i < slen; i++) { + for (j = 0; j < rlen; j++) { + if (spref[i] == rpref[j]) { + res = &spref[i]; + break; + } + } + if (res) + break; } - return NULL; -} -static void dccp_feat_printval(u8 feat_num, dccp_feat_val const *val) -{ - u8 i, type = dccp_feat_type(feat_num); - - if (val == NULL || (type == FEAT_SP && val->sp.vec == NULL)) - dccp_pr_debug_cat("(NULL)"); - else if (type == FEAT_SP) - for (i = 0; i < val->sp.len; i++) - dccp_pr_debug_cat("%s%u", i ? " " : "", val->sp.vec[i]); - else if (type == FEAT_NN) - dccp_pr_debug_cat("%llu", (unsigned long long)val->nn); - else - dccp_pr_debug_cat("unknown type %u", type); -} - -static void dccp_feat_printvals(u8 feat_num, u8 *list, u8 len) -{ - u8 type = dccp_feat_type(feat_num); - dccp_feat_val fval = { .sp.vec = list, .sp.len = len }; - - if (type == FEAT_NN) - fval.nn = dccp_decode_value_var(list, len); - dccp_feat_printval(feat_num, &fval); -} + /* we didn't agree on anything */ + if (res == NULL) { + /* confirm previous value */ + switch (opt->dccpop_feat) { + case DCCPF_CCID: + /* XXX did i get this right? =P */ + if (opt->dccpop_type == DCCPO_CHANGE_L) + res = &dccp_msk(sk)->dccpms_tx_ccid; + else + res = &dccp_msk(sk)->dccpms_rx_ccid; + break; -static void dccp_feat_print_entry(struct dccp_feat_entry const *entry) -{ - dccp_debug(" * %s %s = ", entry->is_local ? "local" : "remote", - dccp_feat_fname(entry->feat_num)); - dccp_feat_printval(entry->feat_num, &entry->val); - dccp_pr_debug_cat(", state=%s %s\n", dccp_feat_sname[entry->state], - entry->needs_confirm ? "(Confirm pending)" : ""); -} + default: + DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat); + /* XXX implement res */ + return -EFAULT; + } -#define dccp_feat_print_opt(opt, feat, val, len, mandatory) do { \ - dccp_pr_debug("%s(%s, ", dccp_feat_oname(opt), dccp_feat_fname(feat));\ - dccp_feat_printvals(feat, val, len); \ - dccp_pr_debug_cat(") %s\n", mandatory ? "!" : ""); } while (0) - -#define dccp_feat_print_fnlist(fn_list) { \ - const struct dccp_feat_entry *___entry; \ - \ - dccp_pr_debug("List Dump:\n"); \ - list_for_each_entry(___entry, fn_list, node) \ - dccp_feat_print_entry(___entry); \ -} -#else /* ! CONFIG_IP_DCCP_DEBUG */ -#define dccp_feat_print_opt(opt, feat, val, len, mandatory) -#define dccp_feat_print_fnlist(fn_list) -#endif + dccp_pr_debug("Don't agree... reconfirming %d\n", *res); + agree = 0; /* this is used for mandatory options... */ + } -static int __dccp_feat_activate(struct sock *sk, const int idx, - const bool is_local, dccp_feat_val const *fval) -{ - bool rx; - u64 val; + /* need to put result and our preference list */ + rlen = 1 + opt->dccpop_len; + rpref = kmalloc(rlen, GFP_ATOMIC); + if (rpref == NULL) + return -ENOMEM; - if (idx < 0 || idx >= DCCP_FEAT_SUPPORTED_MAX) - return -1; - if (dccp_feat_table[idx].activation_hdlr == NULL) - return 0; + *rpref = *res; + memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len); - if (fval == NULL) { - val = dccp_feat_table[idx].default_value; - } else if (dccp_feat_table[idx].reconciliation == FEAT_SP) { - if (fval->sp.vec == NULL) { - /* - * This can happen when an empty Confirm is sent - * for an SP (i.e. known) feature. In this case - * we would be using the default anyway. - */ - DCCP_CRIT("Feature #%d undefined: using default", idx); - val = dccp_feat_table[idx].default_value; - } else { - val = fval->sp.vec[0]; + /* put it in the "confirm queue" */ + if (opt->dccpop_sc == NULL) { + opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC); + if (opt->dccpop_sc == NULL) { + kfree(rpref); + return -ENOMEM; } } else { - val = fval->nn; + /* recycle the confirm slot */ + BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); + kfree(opt->dccpop_sc->dccpoc_val); + dccp_pr_debug("recycling confirm slot\n"); + } + memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc)); + + opt->dccpop_sc->dccpoc_val = rpref; + opt->dccpop_sc->dccpoc_len = rlen; + + /* update the option on our side [we are about to send the confirm] */ + rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res); + if (rc) { + kfree(opt->dccpop_sc->dccpoc_val); + kfree(opt->dccpop_sc); + opt->dccpop_sc = NULL; + return rc; } - /* Location is RX if this is a local-RX or remote-TX feature */ - rx = (is_local == (dccp_feat_table[idx].rxtx == FEAT_AT_RX)); - - dccp_debug(" -> activating %s %s, %sval=%llu\n", rx ? "RX" : "TX", - dccp_feat_fname(dccp_feat_table[idx].feat_num), - fval ? "" : "default ", (unsigned long long)val); - - return dccp_feat_table[idx].activation_hdlr(sk, val, rx); -} - -/** - * dccp_feat_activate - Activate feature value on socket - * @sk: fully connected DCCP socket (after handshake is complete) - * @feat_num: feature to activate, one of %dccp_feature_numbers - * @local: whether local (1) or remote (0) @feat_num is meant - * @fval: the value (SP or NN) to activate, or NULL to use the default value - * For general use this function is preferable over __dccp_feat_activate(). - */ -static int dccp_feat_activate(struct sock *sk, u8 feat_num, bool local, - dccp_feat_val const *fval) -{ - return __dccp_feat_activate(sk, dccp_feat_index(feat_num), local, fval); -} - -/* Test for "Req'd" feature (RFC 4340, 6.4) */ -static inline int dccp_feat_must_be_understood(u8 feat_num) -{ - return feat_num == DCCPF_CCID || feat_num == DCCPF_SHORT_SEQNOS || - feat_num == DCCPF_SEQUENCE_WINDOW; -} + dccp_pr_debug("Will confirm %d\n", *rpref); -/* copy constructor, fval must not already contain allocated memory */ -static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) -{ - fval->sp.len = len; - if (fval->sp.len > 0) { - fval->sp.vec = kmemdup(val, len, gfp_any()); - if (fval->sp.vec == NULL) { - fval->sp.len = 0; - return -ENOBUFS; - } + /* say we want to change to X but we just got a confirm X, suppress our + * change + */ + if (!opt->dccpop_conf) { + if (*opt->dccpop_val == *res) + opt->dccpop_conf = 1; + dccp_pr_debug("won't ask for change of same feature\n"); } - return 0; -} -static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val) -{ - if (unlikely(val == NULL)) - return; - if (dccp_feat_type(feat_num) == FEAT_SP) - kfree(val->sp.vec); - memset(val, 0, sizeof(*val)); + return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */ } -static struct dccp_feat_entry * - dccp_feat_clone_entry(struct dccp_feat_entry const *original) +static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) { - struct dccp_feat_entry *new; - u8 type = dccp_feat_type(original->feat_num); - - if (type == FEAT_UNKNOWN) - return NULL; + struct dccp_minisock *dmsk = dccp_msk(sk); + struct dccp_opt_pend *opt; + int rc = 1; + u8 t; - new = kmemdup(original, sizeof(struct dccp_feat_entry), gfp_any()); - if (new == NULL) - return NULL; + /* + * We received a CHANGE. We gotta match it against our own preference + * list. If we got a CHANGE_R it means it's a change for us, so we need + * to compare our CHANGE_L list. + */ + if (type == DCCPO_CHANGE_L) + t = DCCPO_CHANGE_R; + else + t = DCCPO_CHANGE_L; - if (type == FEAT_SP && dccp_feat_clone_sp_val(&new->val, - original->val.sp.vec, - original->val.sp.len)) { - kfree(new); - return NULL; - } - return new; -} + /* find our preference list for this feature */ + list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { + if (opt->dccpop_type != t || opt->dccpop_feat != feature) + continue; -static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) -{ - if (entry != NULL) { - dccp_feat_val_destructor(entry->feat_num, &entry->val); - kfree(entry); + /* find the winner from the two preference lists */ + rc = dccp_feat_reconcile(sk, opt, val, len); + break; } -} -/* - * List management functions - * - * Feature negotiation lists rely on and maintain the following invariants: - * - each feat_num in the list is known, i.e. we know its type and default value - * - each feat_num/is_local combination is unique (old entries are overwritten) - * - SP values are always freshly allocated - * - list is sorted in increasing order of feature number (faster lookup) - */ -static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list, - u8 feat_num, bool is_local) -{ - struct dccp_feat_entry *entry; + /* We didn't deal with the change. This can happen if we have no + * preference list for the feature. In fact, it just shouldn't + * happen---if we understand a feature, we should have a preference list + * with at least the default value. + */ + BUG_ON(rc == 1); - list_for_each_entry(entry, fn_list, node) - if (entry->feat_num == feat_num && entry->is_local == is_local) - return entry; - else if (entry->feat_num > feat_num) - break; - return NULL; + return rc; } -/** - * dccp_feat_entry_new - Central list update routine (called by all others) - * @head: list to add to - * @feat: feature number - * @local: whether the local (1) or remote feature with number @feat is meant - * This is the only constructor and serves to ensure the above invariants. - */ -static struct dccp_feat_entry * - dccp_feat_entry_new(struct list_head *head, u8 feat, bool local) +static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) { - struct dccp_feat_entry *entry; - - list_for_each_entry(entry, head, node) - if (entry->feat_num == feat && entry->is_local == local) { - dccp_feat_val_destructor(entry->feat_num, &entry->val); - return entry; - } else if (entry->feat_num > feat) { - head = &entry->node; - break; - } + struct dccp_opt_pend *opt; + struct dccp_minisock *dmsk = dccp_msk(sk); + u8 *copy; + int rc; - entry = kmalloc(sizeof(*entry), gfp_any()); - if (entry != NULL) { - entry->feat_num = feat; - entry->is_local = local; - list_add_tail(&entry->node, head); + /* NN features must be Change L (sec. 6.3.2) */ + if (type != DCCPO_CHANGE_L) { + dccp_pr_debug("received %s for NN feature %d\n", + dccp_feat_typename(type), feature); + return -EFAULT; } - return entry; -} -/** - * dccp_feat_push_change - Add/overwrite a Change option in the list - * @fn_list: feature-negotiation list to update - * @feat: one of %dccp_feature_numbers - * @local: whether local (1) or remote (0) @feat_num is meant - * @needs_mandatory: whether to use Mandatory feature negotiation options - * @fval: pointer to NN/SP value to be inserted (will be copied) - */ -static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local, - u8 mandatory, dccp_feat_val *fval) -{ - struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local); + /* XXX sanity check opt val */ - if (new == NULL) + /* copy option so we can confirm it */ + opt = kzalloc(sizeof(*opt), GFP_ATOMIC); + if (opt == NULL) return -ENOMEM; - new->feat_num = feat; - new->is_local = local; - new->state = FEAT_INITIALISING; - new->needs_confirm = 0; - new->empty_confirm = 0; - new->val = *fval; - new->needs_mandatory = mandatory; + copy = kmemdup(val, len, GFP_ATOMIC); + if (copy == NULL) { + kfree(opt); + return -ENOMEM; + } - return 0; -} + opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ + opt->dccpop_feat = feature; + opt->dccpop_val = copy; + opt->dccpop_len = len; -/** - * dccp_feat_push_confirm - Add a Confirm entry to the FN list - * @fn_list: feature-negotiation list to add to - * @feat: one of %dccp_feature_numbers - * @local: whether local (1) or remote (0) @feat_num is being confirmed - * @fval: pointer to NN/SP value to be inserted or NULL - * Returns 0 on success, a Reset code for further processing otherwise. - */ -static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local, - dccp_feat_val *fval) -{ - struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local); + /* change feature */ + rc = dccp_feat_update(sk, type, feature, *val); + if (rc) { + kfree(opt->dccpop_val); + kfree(opt); + return rc; + } - if (new == NULL) - return DCCP_RESET_CODE_TOO_BUSY; + dccp_feat_debug(type, feature, *copy); - new->feat_num = feat; - new->is_local = local; - new->state = FEAT_STABLE; /* transition in 6.6.2 */ - new->needs_confirm = 1; - new->empty_confirm = (fval == NULL); - new->val.nn = 0; /* zeroes the whole structure */ - if (!new->empty_confirm) - new->val = *fval; - new->needs_mandatory = 0; + list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); return 0; } -static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local) +static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, + u8 type, u8 feature) { - return dccp_feat_push_confirm(fn_list, feat, local, NULL); -} + /* XXX check if other confirms for that are queued and recycle slot */ + struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC); -static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) -{ - list_del(&entry->node); - dccp_feat_entry_destructor(entry); -} - -void dccp_feat_list_purge(struct list_head *fn_list) -{ - struct dccp_feat_entry *entry, *next; - - list_for_each_entry_safe(entry, next, fn_list, node) - dccp_feat_entry_destructor(entry); - INIT_LIST_HEAD(fn_list); -} -EXPORT_SYMBOL_GPL(dccp_feat_list_purge); - -/* generate @to as full clone of @from - @to must not contain any nodes */ -int dccp_feat_clone_list(struct list_head const *from, struct list_head *to) -{ - struct dccp_feat_entry *entry, *new; - - INIT_LIST_HEAD(to); - list_for_each_entry(entry, from, node) { - new = dccp_feat_clone_entry(entry); - if (new == NULL) - goto cloning_failed; - list_add_tail(&new->node, to); + if (opt == NULL) { + /* XXX what do we do? Ignoring should be fine. It's a change + * after all =P + */ + return; } - return 0; -cloning_failed: - dccp_feat_list_purge(to); - return -ENOMEM; -} + switch (type) { + case DCCPO_CHANGE_L: + opt->dccpop_type = DCCPO_CONFIRM_R; + break; + case DCCPO_CHANGE_R: + opt->dccpop_type = DCCPO_CONFIRM_L; + break; + default: + DCCP_WARN("invalid type %d\n", type); + kfree(opt); + return; + } + opt->dccpop_feat = feature; + opt->dccpop_val = NULL; + opt->dccpop_len = 0; -/** - * dccp_feat_valid_nn_length - Enforce length constraints on NN options - * Length is between 0 and %DCCP_OPTVAL_MAXLEN. Used for outgoing packets only, - * incoming options are accepted as long as their values are valid. - */ -static u8 dccp_feat_valid_nn_length(u8 feat_num) -{ - if (feat_num == DCCPF_ACK_RATIO) /* RFC 4340, 11.3 and 6.6.8 */ - return 2; - if (feat_num == DCCPF_SEQUENCE_WINDOW) /* RFC 4340, 7.5.2 and 6.5 */ - return 6; - return 0; -} + /* change feature */ + dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature); -static u8 dccp_feat_is_valid_nn_val(u8 feat_num, u64 val) -{ - switch (feat_num) { - case DCCPF_ACK_RATIO: - return val <= DCCPF_ACK_RATIO_MAX; - case DCCPF_SEQUENCE_WINDOW: - return val >= DCCPF_SEQ_WMIN && val <= DCCPF_SEQ_WMAX; - } - return 0; /* feature unknown - so we can't tell */ + list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); } -/* check that SP values are within the ranges defined in RFC 4340 */ -static u8 dccp_feat_is_valid_sp_val(u8 feat_num, u8 val) +static void dccp_feat_flush_confirm(struct sock *sk) { - switch (feat_num) { - case DCCPF_CCID: - return val == DCCPC_CCID2 || val == DCCPC_CCID3; - /* Type-check Boolean feature values: */ - case DCCPF_SHORT_SEQNOS: - case DCCPF_ECN_INCAPABLE: - case DCCPF_SEND_ACK_VECTOR: - case DCCPF_SEND_NDP_COUNT: - case DCCPF_DATA_CHECKSUM: - case DCCPF_SEND_LEV_RATE: - return val < 2; - case DCCPF_MIN_CSUM_COVER: - return val < 16; - } - return 0; /* feature unknown */ -} + struct dccp_minisock *dmsk = dccp_msk(sk); + /* Check if there is anything to confirm in the first place */ + int yes = !list_empty(&dmsk->dccpms_conf); -static u8 dccp_feat_sp_list_ok(u8 feat_num, u8 const *sp_list, u8 sp_len) -{ - if (sp_list == NULL || sp_len < 1) - return 0; - while (sp_len--) - if (!dccp_feat_is_valid_sp_val(feat_num, *sp_list++)) - return 0; - return 1; -} + if (!yes) { + struct dccp_opt_pend *opt; -/** - * dccp_feat_insert_opts - Generate FN options from current list state - * @skb: next sk_buff to be sent to the peer - * @dp: for client during handshake and general negotiation - * @dreq: used by the server only (all Changes/Confirms in LISTEN/RESPOND) - */ -int dccp_feat_insert_opts(struct dccp_sock *dp, struct dccp_request_sock *dreq, - struct sk_buff *skb) -{ - struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg; - struct dccp_feat_entry *pos, *next; - u8 opt, type, len, *ptr, nn_in_nbo[DCCP_OPTVAL_MAXLEN]; - bool rpt; - - /* put entries into @skb in the order they appear in the list */ - list_for_each_entry_safe_reverse(pos, next, fn, node) { - opt = dccp_feat_genopt(pos); - type = dccp_feat_type(pos->feat_num); - rpt = false; - - if (pos->empty_confirm) { - len = 0; - ptr = NULL; - } else { - if (type == FEAT_SP) { - len = pos->val.sp.len; - ptr = pos->val.sp.vec; - rpt = pos->needs_confirm; - } else if (type == FEAT_NN) { - len = dccp_feat_valid_nn_length(pos->feat_num); - ptr = nn_in_nbo; - dccp_encode_value_var(pos->val.nn, ptr, len); - } else { - DCCP_BUG("unknown feature %u", pos->feat_num); - return -1; + list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { + if (opt->dccpop_conf) { + yes = 1; + break; } } - dccp_feat_print_opt(opt, pos->feat_num, ptr, len, 0); - - if (dccp_insert_fn_opt(skb, opt, pos->feat_num, ptr, len, rpt)) - return -1; - if (pos->needs_mandatory && dccp_insert_option_mandatory(skb)) - return -1; - /* - * Enter CHANGING after transmitting the Change option (6.6.2). - */ - if (pos->state == FEAT_INITIALISING) - pos->state = FEAT_CHANGING; } - return 0; -} - -/** - * __feat_register_nn - Register new NN value on socket - * @fn: feature-negotiation list to register with - * @feat: an NN feature from %dccp_feature_numbers - * @mandatory: use Mandatory option if 1 - * @nn_val: value to register (restricted to 4 bytes) - * Note that NN features are local by definition (RFC 4340, 6.3.2). - */ -static int __feat_register_nn(struct list_head *fn, u8 feat, - u8 mandatory, u64 nn_val) -{ - dccp_feat_val fval = { .nn = nn_val }; - - if (dccp_feat_type(feat) != FEAT_NN || - !dccp_feat_is_valid_nn_val(feat, nn_val)) - return -EINVAL; - - /* Don't bother with default values, they will be activated anyway. */ - if (nn_val - (u64)dccp_feat_default_value(feat) == 0) - return 0; - - return dccp_feat_push_change(fn, feat, 1, mandatory, &fval); -} - -/** - * __feat_register_sp - Register new SP value/list on socket - * @fn: feature-negotiation list to register with - * @feat: an SP feature from %dccp_feature_numbers - * @is_local: whether the local (1) or the remote (0) @feat is meant - * @mandatory: use Mandatory option if 1 - * @sp_val: SP value followed by optional preference list - * @sp_len: length of @sp_val in bytes - */ -static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, - u8 mandatory, u8 const *sp_val, u8 sp_len) -{ - dccp_feat_val fval; - if (dccp_feat_type(feat) != FEAT_SP || - !dccp_feat_sp_list_ok(feat, sp_val, sp_len)) - return -EINVAL; - - /* Avoid negotiating alien CCIDs by only advertising supported ones */ - if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len)) - return -EOPNOTSUPP; - - if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len)) - return -ENOMEM; + if (!yes) + return; - return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); + /* OK there is something to confirm... */ + /* XXX check if packet is in flight? Send delayed ack?? */ + if (sk->sk_state == DCCP_OPEN) + dccp_send_ack(sk); } -/** - * dccp_feat_register_sp - Register requests to change SP feature values - * @sk: client or listening socket - * @feat: one of %dccp_feature_numbers - * @is_local: whether the local (1) or remote (0) @feat is meant - * @list: array of preferred values, in descending order of preference - * @len: length of @list in bytes - */ -int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, - u8 const *list, u8 len) -{ /* any changes must be registered before establishing the connection */ - if (sk->sk_state != DCCP_CLOSED) - return -EISCONN; - if (dccp_feat_type(feat) != FEAT_SP) - return -EINVAL; - return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local, - 0, list, len); -} - -/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */ -int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val) +int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) { - /* any changes must be registered before establishing the connection */ - if (sk->sk_state != DCCP_CLOSED) - return -EISCONN; - if (dccp_feat_type(feat) != FEAT_NN) - return -EINVAL; - return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val); -} + int rc; -/** - * dccp_feat_signal_nn_change - Update NN values for an established connection - * @sk: DCCP socket of an established connection - * @feat: NN feature number from %dccp_feature_numbers - * @nn_val: the new value to use - * This function is used to communicate NN updates out-of-band. The difference - * to feature negotiation during connection setup is that values are activated - * immediately after validation, i.e. we don't wait for the Confirm: either the - * value is accepted by the peer (and then the waiting is futile), or it is not - * (Reset or empty Confirm). We don't accept empty Confirms - transmitted values - * are validated, and the peer "MUST accept any valid value" (RFC 4340, 6.3.2). - */ -int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val) -{ - struct list_head *fn = &dccp_sk(sk)->dccps_featneg; - dccp_feat_val fval = { .nn = nn_val }; - struct dccp_feat_entry *entry; + dccp_feat_debug(type, feature, *val); - if (sk->sk_state != DCCP_OPEN && sk->sk_state != DCCP_PARTOPEN) - return 0; + /* figure out if it's SP or NN feature */ + switch (feature) { + /* deal with SP features */ + case DCCPF_CCID: + rc = dccp_feat_sp(sk, type, feature, val, len); + break; - if (dccp_feat_type(feat) != FEAT_NN || - !dccp_feat_is_valid_nn_val(feat, nn_val)) - return -EINVAL; + /* deal with NN features */ + case DCCPF_ACK_RATIO: + rc = dccp_feat_nn(sk, type, feature, val, len); + break; - entry = dccp_feat_list_lookup(fn, feat, 1); - if (entry != NULL) { - dccp_pr_debug("Ignoring %llu, entry %llu exists in state %s\n", - (unsigned long long)nn_val, - (unsigned long long)entry->val.nn, - dccp_feat_sname[entry->state]); - return 0; + /* XXX implement other features */ + default: + dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n", + dccp_feat_typename(type), feature); + rc = -EFAULT; + break; } - if (dccp_feat_activate(sk, feat, 1, &fval)) - return -EADV; - - inet_csk_schedule_ack(sk); - return dccp_feat_push_change(fn, feat, 1, 0, &fval); -} -EXPORT_SYMBOL_GPL(dccp_feat_signal_nn_change); - -/* - * Tracking features whose value depend on the choice of CCID - * - * This is designed with an extension in mind so that a list walk could be done - * before activating any features. However, the existing framework was found to - * work satisfactorily up until now, the automatic verification is left open. - * When adding new CCIDs, add a corresponding dependency table here. - */ -static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local) -{ - static const struct ccid_dependency ccid2_dependencies[2][2] = { - /* - * CCID2 mandates Ack Vectors (RFC 4341, 4.): as CCID is a TX - * feature and Send Ack Vector is an RX feature, `is_local' - * needs to be reversed. + /* check if there were problems changing features */ + if (rc) { + /* If we don't agree on SP, we sent a confirm for old value. + * However we propagate rc to caller in case option was + * mandatory */ - { /* Dependencies of the receiver-side (remote) CCID2 */ - { - .dependent_feat = DCCPF_SEND_ACK_VECTOR, - .is_local = true, - .is_mandatory = true, - .val = 1 - }, - { 0, 0, 0, 0 } - }, - { /* Dependencies of the sender-side (local) CCID2 */ - { - .dependent_feat = DCCPF_SEND_ACK_VECTOR, - .is_local = false, - .is_mandatory = true, - .val = 1 - }, - { 0, 0, 0, 0 } - } - }; - static const struct ccid_dependency ccid3_dependencies[2][5] = { - { /* - * Dependencies of the receiver-side CCID3 - */ - { /* locally disable Ack Vectors */ - .dependent_feat = DCCPF_SEND_ACK_VECTOR, - .is_local = true, - .is_mandatory = false, - .val = 0 - }, - { /* see below why Send Loss Event Rate is on */ - .dependent_feat = DCCPF_SEND_LEV_RATE, - .is_local = true, - .is_mandatory = true, - .val = 1 - }, - { /* NDP Count is needed as per RFC 4342, 6.1.1 */ - .dependent_feat = DCCPF_SEND_NDP_COUNT, - .is_local = false, - .is_mandatory = true, - .val = 1 - }, - { 0, 0, 0, 0 }, - }, - { /* - * CCID3 at the TX side: we request that the HC-receiver - * will not send Ack Vectors (they will be ignored, so - * Mandatory is not set); we enable Send Loss Event Rate - * (Mandatory since the implementation does not support - * the Loss Intervals option of RFC 4342, 8.6). - * The last two options are for peer's information only. - */ - { - .dependent_feat = DCCPF_SEND_ACK_VECTOR, - .is_local = false, - .is_mandatory = false, - .val = 0 - }, - { - .dependent_feat = DCCPF_SEND_LEV_RATE, - .is_local = false, - .is_mandatory = true, - .val = 1 - }, - { /* this CCID does not support Ack Ratio */ - .dependent_feat = DCCPF_ACK_RATIO, - .is_local = true, - .is_mandatory = false, - .val = 0 - }, - { /* tell receiver we are sending NDP counts */ - .dependent_feat = DCCPF_SEND_NDP_COUNT, - .is_local = true, - .is_mandatory = false, - .val = 1 - }, - { 0, 0, 0, 0 } - } - }; - switch (ccid) { - case DCCPC_CCID2: - return ccid2_dependencies[is_local]; - case DCCPC_CCID3: - return ccid3_dependencies[is_local]; - default: - return NULL; + if (rc != DCCP_FEAT_SP_NOAGREE) + dccp_feat_empty_confirm(dccp_msk(sk), type, feature); } -} -/** - * dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID - * @fn: feature-negotiation list to update - * @id: CCID number to track - * @is_local: whether TX CCID (1) or RX CCID (0) is meant - * This function needs to be called after registering all other features. - */ -static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local) -{ - const struct ccid_dependency *table = dccp_feat_ccid_deps(id, is_local); - int i, rc = (table == NULL); - - for (i = 0; rc == 0 && table[i].dependent_feat != DCCPF_RESERVED; i++) - if (dccp_feat_type(table[i].dependent_feat) == FEAT_SP) - rc = __feat_register_sp(fn, table[i].dependent_feat, - table[i].is_local, - table[i].is_mandatory, - &table[i].val, 1); - else - rc = __feat_register_nn(fn, table[i].dependent_feat, - table[i].is_mandatory, - table[i].val); - return rc; -} - -/** - * dccp_feat_finalise_settings - Finalise settings before starting negotiation - * @dp: client or listening socket (settings will be inherited) - * This is called after all registrations (socket initialisation, sysctls, and - * sockopt calls), and before sending the first packet containing Change options - * (ie. client-Request or server-Response), to ensure internal consistency. - */ -int dccp_feat_finalise_settings(struct dccp_sock *dp) -{ - struct list_head *fn = &dp->dccps_featneg; - struct dccp_feat_entry *entry; - int i = 2, ccids[2] = { -1, -1 }; + /* generate the confirm [if required] */ + dccp_feat_flush_confirm(sk); - /* - * Propagating CCIDs: - * 1) not useful to propagate CCID settings if this host advertises more - * than one CCID: the choice of CCID may still change - if this is - * the client, or if this is the server and the client sends - * singleton CCID values. - * 2) since is that propagate_ccid changes the list, we defer changing - * the sorted list until after the traversal. - */ - list_for_each_entry(entry, fn, node) - if (entry->feat_num == DCCPF_CCID && entry->val.sp.len == 1) - ccids[entry->is_local] = entry->val.sp.vec[0]; - while (i--) - if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i)) - return -1; - dccp_feat_print_fnlist(fn); - return 0; + return rc; } -/** - * dccp_feat_server_ccid_dependencies - Resolve CCID-dependent features - * It is the server which resolves the dependencies once the CCID has been - * fully negotiated. If no CCID has been negotiated, it uses the default CCID. - */ -int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq) -{ - struct list_head *fn = &dreq->dreq_featneg; - struct dccp_feat_entry *entry; - u8 is_local, ccid; - - for (is_local = 0; is_local <= 1; is_local++) { - entry = dccp_feat_list_lookup(fn, DCCPF_CCID, is_local); - - if (entry != NULL && !entry->empty_confirm) - ccid = entry->val.sp.vec[0]; - else - ccid = dccp_feat_default_value(DCCPF_CCID); - - if (dccp_feat_propagate_ccid(fn, ccid, is_local)) - return -1; - } - return 0; -} +EXPORT_SYMBOL_GPL(dccp_feat_change_recv); -/* Select the first entry in @servlist that also occurs in @clilist (6.3.1) */ -static int dccp_feat_preflist_match(u8 *servlist, u8 slen, u8 *clilist, u8 clen) +int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, + u8 *val, u8 len) { - u8 c, s; + u8 t; + struct dccp_opt_pend *opt; + struct dccp_minisock *dmsk = dccp_msk(sk); + int found = 0; + int all_confirmed = 1; - for (s = 0; s < slen; s++) - for (c = 0; c < clen; c++) - if (servlist[s] == clilist[c]) - return servlist[s]; - return -1; -} + dccp_feat_debug(type, feature, *val); -/** - * dccp_feat_prefer - Move preferred entry to the start of array - * Reorder the @array_len elements in @array so that @preferred_value comes - * first. Returns >0 to indicate that @preferred_value does occur in @array. - */ -static u8 dccp_feat_prefer(u8 preferred_value, u8 *array, u8 array_len) -{ - u8 i, does_occur = 0; + /* locate our change request */ + switch (type) { + case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break; + case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break; + default: DCCP_WARN("invalid type %d\n", type); + return 1; - if (array != NULL) { - for (i = 0; i < array_len; i++) - if (array[i] == preferred_value) { - array[i] = array[0]; - does_occur++; - } - if (does_occur) - array[0] = preferred_value; } - return does_occur; -} + /* XXX sanity check feature value */ -/** - * dccp_feat_reconcile - Reconcile SP preference lists - * @fval: SP list to reconcile into - * @arr: received SP preference list - * @len: length of @arr in bytes - * @is_server: whether this side is the server (and @fv is the server's list) - * @reorder: whether to reorder the list in @fv after reconciling with @arr - * When successful, > 0 is returned and the reconciled list is in @fval. - * A value of 0 means that negotiation failed (no shared entry). - */ -static int dccp_feat_reconcile(dccp_feat_val *fv, u8 *arr, u8 len, - bool is_server, bool reorder) -{ - int rc; + list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { + if (!opt->dccpop_conf && opt->dccpop_type == t && + opt->dccpop_feat == feature) { + found = 1; + dccp_pr_debug("feature %d found\n", opt->dccpop_feat); - if (!fv->sp.vec || !arr) { - DCCP_CRIT("NULL feature value or array"); - return 0; - } + /* XXX do sanity check */ - if (is_server) - rc = dccp_feat_preflist_match(fv->sp.vec, fv->sp.len, arr, len); - else - rc = dccp_feat_preflist_match(arr, len, fv->sp.vec, fv->sp.len); - - if (!reorder) - return rc; - if (rc < 0) - return 0; + opt->dccpop_conf = 1; - /* - * Reorder list: used for activating features and in dccp_insert_fn_opt. - */ - return dccp_feat_prefer(rc, fv->sp.vec, fv->sp.len); -} + /* We got a confirmation---change the option */ + dccp_feat_update(sk, opt->dccpop_type, + opt->dccpop_feat, *val); -/** - * dccp_feat_change_recv - Process incoming ChangeL/R options - * @fn: feature-negotiation list to update - * @is_mandatory: whether the Change was preceded by a Mandatory option - * @opt: %DCCPO_CHANGE_L or %DCCPO_CHANGE_R - * @feat: one of %dccp_feature_numbers - * @val: NN value or SP value/preference list - * @len: length of @val in bytes - * @server: whether this node is the server (1) or the client (0) - */ -static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt, - u8 feat, u8 *val, u8 len, const bool server) -{ - u8 defval, type = dccp_feat_type(feat); - const bool local = (opt == DCCPO_CHANGE_R); - struct dccp_feat_entry *entry; - dccp_feat_val fval; - - if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */ - goto unknown_feature_or_value; - - dccp_feat_print_opt(opt, feat, val, len, is_mandatory); - - /* - * Negotiation of NN features: Change R is invalid, so there is no - * simultaneous negotiation; hence we do not look up in the list. - */ - if (type == FEAT_NN) { - if (local || len > sizeof(fval.nn)) - goto unknown_feature_or_value; - - /* 6.3.2: "The feature remote MUST accept any valid value..." */ - fval.nn = dccp_decode_value_var(val, len); - if (!dccp_feat_is_valid_nn_val(feat, fval.nn)) - goto unknown_feature_or_value; + /* XXX check the return value of dccp_feat_update */ + break; + } - return dccp_feat_push_confirm(fn, feat, local, &fval); + if (!opt->dccpop_conf) + all_confirmed = 0; } - /* - * Unidirectional/simultaneous negotiation of SP features (6.3.1) + /* fix re-transmit timer */ + /* XXX gotta make sure that no option negotiation occurs during + * connection shutdown. Consider that the CLOSEREQ is sent and timer is + * on. if all options are confirmed it might kill timer which should + * remain alive until close is received. */ - entry = dccp_feat_list_lookup(fn, feat, local); - if (entry == NULL) { - /* - * No particular preferences have been registered. We deal with - * this situation by assuming that all valid values are equally - * acceptable, and apply the following checks: - * - if the peer's list is a singleton, we accept a valid value; - * - if we are the server, we first try to see if the peer (the - * client) advertises the default value. If yes, we use it, - * otherwise we accept the preferred value; - * - else if we are the client, we use the first list element. - */ - if (dccp_feat_clone_sp_val(&fval, val, 1)) - return DCCP_RESET_CODE_TOO_BUSY; - - if (len > 1 && server) { - defval = dccp_feat_default_value(feat); - if (dccp_feat_preflist_match(&defval, 1, val, len) > -1) - fval.sp.vec[0] = defval; - } else if (!dccp_feat_is_valid_sp_val(feat, fval.sp.vec[0])) { - kfree(fval.sp.vec); - goto unknown_feature_or_value; - } - - /* Treat unsupported CCIDs like invalid values */ - if (feat == DCCPF_CCID && !ccid_support_check(fval.sp.vec, 1)) { - kfree(fval.sp.vec); - goto not_valid_or_not_known; - } - - return dccp_feat_push_confirm(fn, feat, local, &fval); - - } else if (entry->state == FEAT_UNSTABLE) { /* 6.6.2 */ - return 0; + if (all_confirmed) { + dccp_pr_debug("clear feat negotiation timer %p\n", sk); + inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); } - if (dccp_feat_reconcile(&entry->val, val, len, server, true)) { - entry->empty_confirm = 0; - } else if (is_mandatory) { - return DCCP_RESET_CODE_MANDATORY_ERROR; - } else if (entry->state == FEAT_INITIALISING) { - /* - * Failed simultaneous negotiation (server only): try to `save' - * the connection by checking whether entry contains the default - * value for @feat. If yes, send an empty Confirm to signal that - * the received Change was not understood - which implies using - * the default value. - * If this also fails, we use Reset as the last resort. - */ - WARN_ON(!server); - defval = dccp_feat_default_value(feat); - if (!dccp_feat_reconcile(&entry->val, &defval, 1, server, true)) - return DCCP_RESET_CODE_OPTION_ERROR; - entry->empty_confirm = 1; - } - entry->needs_confirm = 1; - entry->needs_mandatory = 0; - entry->state = FEAT_STABLE; + if (!found) + dccp_pr_debug("%s(%d, ...) never requested\n", + dccp_feat_typename(type), feature); return 0; - -unknown_feature_or_value: - if (!is_mandatory) - return dccp_push_empty_confirm(fn, feat, local); - -not_valid_or_not_known: - return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR - : DCCP_RESET_CODE_OPTION_ERROR; } -/** - * dccp_feat_confirm_recv - Process received Confirm options - * @fn: feature-negotiation list to update - * @is_mandatory: whether @opt was preceded by a Mandatory option - * @opt: %DCCPO_CONFIRM_L or %DCCPO_CONFIRM_R - * @feat: one of %dccp_feature_numbers - * @val: NN value or SP value/preference list - * @len: length of @val in bytes - * @server: whether this node is server (1) or client (0) - */ -static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt, - u8 feat, u8 *val, u8 len, const bool server) -{ - u8 *plist, plen, type = dccp_feat_type(feat); - const bool local = (opt == DCCPO_CONFIRM_R); - struct dccp_feat_entry *entry = dccp_feat_list_lookup(fn, feat, local); - - dccp_feat_print_opt(opt, feat, val, len, is_mandatory); - - if (entry == NULL) { /* nothing queued: ignore or handle error */ - if (is_mandatory && type == FEAT_UNKNOWN) - return DCCP_RESET_CODE_MANDATORY_ERROR; - - if (!local && type == FEAT_NN) /* 6.3.2 */ - goto confirmation_failed; - return 0; - } - - if (entry->state != FEAT_CHANGING) /* 6.6.2 */ - return 0; - - if (len == 0) { - if (dccp_feat_must_be_understood(feat)) /* 6.6.7 */ - goto confirmation_failed; - /* - * Empty Confirm during connection setup: this means reverting - * to the `old' value, which in this case is the default. Since - * we handle default values automatically when no other values - * have been set, we revert to the old value by removing this - * entry from the list. - */ - dccp_feat_list_pop(entry); - return 0; - } +EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); - if (type == FEAT_NN) { - if (len > sizeof(entry->val.nn)) - goto confirmation_failed; +void dccp_feat_clean(struct dccp_minisock *dmsk) +{ + struct dccp_opt_pend *opt, *next; - if (entry->val.nn == dccp_decode_value_var(val, len)) - goto confirmation_succeeded; + list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending, + dccpop_node) { + BUG_ON(opt->dccpop_val == NULL); + kfree(opt->dccpop_val); - DCCP_WARN("Bogus Confirm for non-existing value\n"); - goto confirmation_failed; - } + if (opt->dccpop_sc != NULL) { + BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); + kfree(opt->dccpop_sc->dccpoc_val); + kfree(opt->dccpop_sc); + } - /* - * Parsing SP Confirms: the first element of @val is the preferred - * SP value which the peer confirms, the remainder depends on @len. - * Note that only the confirmed value need to be a valid SP value. - */ - if (!dccp_feat_is_valid_sp_val(feat, *val)) - goto confirmation_failed; - - if (len == 1) { /* peer didn't supply a preference list */ - plist = val; - plen = len; - } else { /* preferred value + preference list */ - plist = val + 1; - plen = len - 1; + kfree(opt); } + INIT_LIST_HEAD(&dmsk->dccpms_pending); - /* Check whether the peer got the reconciliation right (6.6.8) */ - if (dccp_feat_reconcile(&entry->val, plist, plen, server, 0) != *val) { - DCCP_WARN("Confirm selected the wrong value %u\n", *val); - return DCCP_RESET_CODE_OPTION_ERROR; + list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) { + BUG_ON(opt == NULL); + if (opt->dccpop_val != NULL) + kfree(opt->dccpop_val); + kfree(opt); } - entry->val.sp.vec[0] = *val; - -confirmation_succeeded: - entry->state = FEAT_STABLE; - return 0; - -confirmation_failed: - DCCP_WARN("Confirmation failed\n"); - return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR - : DCCP_RESET_CODE_OPTION_ERROR; + INIT_LIST_HEAD(&dmsk->dccpms_conf); } -/** - * dccp_feat_handle_nn_established - Fast-path reception of NN options - * @sk: socket of an established DCCP connection - * @mandatory: whether @opt was preceded by a Mandatory option - * @opt: %DCCPO_CHANGE_L | %DCCPO_CONFIRM_R (NN only) - * @feat: NN number, one of %dccp_feature_numbers - * @val: NN value - * @len: length of @val in bytes - * This function combines the functionality of change_recv/confirm_recv, with - * the following differences (reset codes are the same): - * - cleanup after receiving the Confirm; - * - values are directly activated after successful parsing; - * - deliberately restricted to NN features. - * The restriction to NN features is essential since SP features can have non- - * predictable outcomes (depending on the remote configuration), and are inter- - * dependent (CCIDs for instance cause further dependencies). +EXPORT_SYMBOL_GPL(dccp_feat_clean); + +/* this is to be called only when a listening sock creates its child. It is + * assumed by the function---the confirm is not duplicated, but rather it is + * "passed on". */ -static u8 dccp_feat_handle_nn_established(struct sock *sk, u8 mandatory, u8 opt, - u8 feat, u8 *val, u8 len) +int dccp_feat_clone(struct sock *oldsk, struct sock *newsk) { - struct list_head *fn = &dccp_sk(sk)->dccps_featneg; - const bool local = (opt == DCCPO_CONFIRM_R); - struct dccp_feat_entry *entry; - u8 type = dccp_feat_type(feat); - dccp_feat_val fval; + struct dccp_minisock *olddmsk = dccp_msk(oldsk); + struct dccp_minisock *newdmsk = dccp_msk(newsk); + struct dccp_opt_pend *opt; + int rc = 0; - dccp_feat_print_opt(opt, feat, val, len, mandatory); + INIT_LIST_HEAD(&newdmsk->dccpms_pending); + INIT_LIST_HEAD(&newdmsk->dccpms_conf); - /* Ignore non-mandatory unknown and non-NN features */ - if (type == FEAT_UNKNOWN) { - if (local && !mandatory) - return 0; - goto fast_path_unknown; - } else if (type != FEAT_NN) { - return 0; - } - - /* - * We don't accept empty Confirms, since in fast-path feature - * negotiation the values are enabled immediately after sending - * the Change option. - * Empty Changes on the other hand are invalid (RFC 4340, 6.1). - */ - if (len == 0 || len > sizeof(fval.nn)) - goto fast_path_unknown; - - if (opt == DCCPO_CHANGE_L) { - fval.nn = dccp_decode_value_var(val, len); - if (!dccp_feat_is_valid_nn_val(feat, fval.nn)) - goto fast_path_unknown; + list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) { + struct dccp_opt_pend *newopt; + /* copy the value of the option */ + u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC); - if (dccp_feat_push_confirm(fn, feat, local, &fval) || - dccp_feat_activate(sk, feat, local, &fval)) - return DCCP_RESET_CODE_TOO_BUSY; + if (val == NULL) + goto out_clean; - /* set the `Ack Pending' flag to piggyback a Confirm */ - inet_csk_schedule_ack(sk); - - } else if (opt == DCCPO_CONFIRM_R) { - entry = dccp_feat_list_lookup(fn, feat, local); - if (entry == NULL || entry->state != FEAT_CHANGING) - return 0; - - fval.nn = dccp_decode_value_var(val, len); - if (fval.nn != entry->val.nn) { - DCCP_WARN("Bogus Confirm for non-existing value\n"); - goto fast_path_failed; + newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC); + if (newopt == NULL) { + kfree(val); + goto out_clean; } - /* It has been confirmed - so remove the entry */ - dccp_feat_list_pop(entry); + /* insert the option */ + newopt->dccpop_val = val; + list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending); - } else { - DCCP_WARN("Received illegal option %u\n", opt); - goto fast_path_failed; + /* XXX what happens with backlogs and multiple connections at + * once... + */ + /* the master socket no longer needs to worry about confirms */ + opt->dccpop_sc = NULL; /* it's not a memleak---new socket has it */ + + /* reset state for a new socket */ + opt->dccpop_conf = 0; } - return 0; -fast_path_unknown: - if (!mandatory) - return dccp_push_empty_confirm(fn, feat, local); + /* XXX not doing anything about the conf queue */ + +out: + return rc; -fast_path_failed: - return mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR - : DCCP_RESET_CODE_OPTION_ERROR; +out_clean: + dccp_feat_clean(newdmsk); + rc = -ENOMEM; + goto out; } -/** - * dccp_feat_parse_options - Process Feature-Negotiation Options - * @sk: for general use and used by the client during connection setup - * @dreq: used by the server during connection setup - * @mandatory: whether @opt was preceded by a Mandatory option - * @opt: %DCCPO_CHANGE_L | %DCCPO_CHANGE_R | %DCCPO_CONFIRM_L | %DCCPO_CONFIRM_R - * @feat: one of %dccp_feature_numbers - * @val: value contents of @opt - * @len: length of @val in bytes - * Returns 0 on success, a Reset code for ending the connection otherwise. - */ -int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq, - u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len) +EXPORT_SYMBOL_GPL(dccp_feat_clone); + +static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat, + u8 *val, u8 len) { - struct dccp_sock *dp = dccp_sk(sk); - struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg; - bool server = false; + int rc = -ENOMEM; + u8 *copy = kmemdup(val, len, GFP_KERNEL); - switch (sk->sk_state) { - /* - * Negotiation during connection setup - */ - case DCCP_LISTEN: - server = true; /* fall through */ - case DCCP_REQUESTING: - switch (opt) { - case DCCPO_CHANGE_L: - case DCCPO_CHANGE_R: - return dccp_feat_change_recv(fn, mandatory, opt, feat, - val, len, server); - case DCCPO_CONFIRM_R: - case DCCPO_CONFIRM_L: - return dccp_feat_confirm_recv(fn, mandatory, opt, feat, - val, len, server); - } - break; - /* - * Support for exchanging NN options on an established connection - * This is currently restricted to Ack Ratio (RFC 4341, 6.1.2) - */ - case DCCP_OPEN: - case DCCP_PARTOPEN: - return dccp_feat_handle_nn_established(sk, mandatory, opt, feat, - val, len); + if (copy != NULL) { + rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL); + if (rc) + kfree(copy); } - return 0; /* ignore FN options in all other states */ + return rc; } -/** - * dccp_feat_init - Seed feature negotiation with host-specific defaults - * This initialises global defaults, depending on the value of the sysctls. - * These can later be overridden by registering changes via setsockopt calls. - * The last link in the chain is finalise_settings, to make sure that between - * here and the start of actual feature negotiation no inconsistencies enter. - * - * All features not appearing below use either defaults or are otherwise - * later adjusted through dccp_feat_finalise_settings(). - */ -int dccp_feat_init(struct sock *sk) +int dccp_feat_init(struct dccp_minisock *dmsk) { - struct list_head *fn = &dccp_sk(sk)->dccps_featneg; - u8 on = 1, off = 0; int rc; - struct { - u8 *val; - u8 len; - } tx, rx; - - /* Non-negotiable (NN) features */ - rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0, - sysctl_dccp_sequence_window); - if (rc) - return rc; - /* Server-priority (SP) features */ - - /* Advertise that short seqnos are not supported (7.6.1) */ - rc = __feat_register_sp(fn, DCCPF_SHORT_SEQNOS, true, true, &off, 1); - if (rc) - return rc; + INIT_LIST_HEAD(&dmsk->dccpms_pending); + INIT_LIST_HEAD(&dmsk->dccpms_conf); - /* RFC 4340 12.1: "If a DCCP is not ECN capable, ..." */ - rc = __feat_register_sp(fn, DCCPF_ECN_INCAPABLE, true, true, &on, 1); + /* CCID L */ + rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID, + &dmsk->dccpms_tx_ccid, 1); if (rc) - return rc; - - /* - * We advertise the available list of CCIDs and reorder according to - * preferences, to avoid failure resulting from negotiating different - * singleton values (which always leads to failure). - * These settings can still (later) be overridden via sockopts. - */ - if (ccid_get_builtin_ccids(&tx.val, &tx.len) || - ccid_get_builtin_ccids(&rx.val, &rx.len)) - return -ENOBUFS; - - /* Pre-load all CCID modules that are going to be advertised */ - rc = -EUNATCH; - if (ccid_request_modules(tx.val, tx.len)) - goto free_ccid_lists; - - if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) || - !dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len)) - goto free_ccid_lists; + goto out; - rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len); + /* CCID R */ + rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID, + &dmsk->dccpms_rx_ccid, 1); if (rc) - goto free_ccid_lists; + goto out; - rc = __feat_register_sp(fn, DCCPF_CCID, false, false, rx.val, rx.len); - -free_ccid_lists: - kfree(tx.val); - kfree(rx.val); + /* Ack ratio */ + rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO, + &dmsk->dccpms_ack_ratio, 1); +out: return rc; } -int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list) -{ - struct dccp_sock *dp = dccp_sk(sk); - struct dccp_feat_entry *cur, *next; - int idx; - dccp_feat_val *fvals[DCCP_FEAT_SUPPORTED_MAX][2] = { - [0 ... DCCP_FEAT_SUPPORTED_MAX-1] = { NULL, NULL } - }; - - list_for_each_entry(cur, fn_list, node) { - /* - * An empty Confirm means that either an unknown feature type - * or an invalid value was present. In the first case there is - * nothing to activate, in the other the default value is used. - */ - if (cur->empty_confirm) - continue; +EXPORT_SYMBOL_GPL(dccp_feat_init); - idx = dccp_feat_index(cur->feat_num); - if (idx < 0) { - DCCP_BUG("Unknown feature %u", cur->feat_num); - goto activation_failed; - } - if (cur->state != FEAT_STABLE) { - DCCP_CRIT("Negotiation of %s %s failed in state %s", - cur->is_local ? "local" : "remote", - dccp_feat_fname(cur->feat_num), - dccp_feat_sname[cur->state]); - goto activation_failed; - } - fvals[idx][cur->is_local] = &cur->val; +#ifdef CONFIG_IP_DCCP_DEBUG +const char *dccp_feat_typename(const u8 type) +{ + switch(type) { + case DCCPO_CHANGE_L: return("ChangeL"); + case DCCPO_CONFIRM_L: return("ConfirmL"); + case DCCPO_CHANGE_R: return("ChangeR"); + case DCCPO_CONFIRM_R: return("ConfirmR"); + /* the following case must not appear in feature negotation */ + default: dccp_pr_debug("unknown type %d [BUG!]\n", type); } + return NULL; +} - /* - * Activate in decreasing order of index, so that the CCIDs are always - * activated as the last feature. This avoids the case where a CCID - * relies on the initialisation of one or more features that it depends - * on (e.g. Send NDP Count, Send Ack Vector, and Ack Ratio features). - */ - for (idx = DCCP_FEAT_SUPPORTED_MAX; --idx >= 0;) - if (__dccp_feat_activate(sk, idx, 0, fvals[idx][0]) || - __dccp_feat_activate(sk, idx, 1, fvals[idx][1])) { - DCCP_CRIT("Could not activate %d", idx); - goto activation_failed; - } +EXPORT_SYMBOL_GPL(dccp_feat_typename); - /* Clean up Change options which have been confirmed already */ - list_for_each_entry_safe(cur, next, fn_list, node) - if (!cur->needs_confirm) - dccp_feat_list_pop(cur); +const char *dccp_feat_name(const u8 feat) +{ + static const char *feature_names[] = { + [DCCPF_RESERVED] = "Reserved", + [DCCPF_CCID] = "CCID", + [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", + [DCCPF_SEQUENCE_WINDOW] = "Sequence Window", + [DCCPF_ECN_INCAPABLE] = "ECN Incapable", + [DCCPF_ACK_RATIO] = "Ack Ratio", + [DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector", + [DCCPF_SEND_NDP_COUNT] = "Send NDP Count", + [DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage", + [DCCPF_DATA_CHECKSUM] = "Send Data Checksum", + }; + if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC) + return feature_names[DCCPF_RESERVED]; - dccp_pr_debug("Activation OK\n"); - return 0; + if (feat >= DCCPF_MIN_CCID_SPECIFIC) + return "CCID-specific"; -activation_failed: - /* - * We clean up everything that may have been allocated, since - * it is difficult to track at which stage negotiation failed. - * This is ok, since all allocation functions below are robust - * against NULL arguments. - */ - ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); - ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); - dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; - dccp_ackvec_free(dp->dccps_hc_rx_ackvec); - dp->dccps_hc_rx_ackvec = NULL; - return -1; + return feature_names[feat]; } + +EXPORT_SYMBOL_GPL(dccp_feat_name); +#endif /* CONFIG_IP_DCCP_DEBUG */ diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 2217066e22d7..e272222c7ace 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -3,134 +3,38 @@ /* * net/dccp/feat.h * - * Feature negotiation for the DCCP protocol (RFC 4340, section 6) - * Copyright (c) 2008 Gerrit Renker + * An implementation of the DCCP protocol * Copyright (c) 2005 Andrea Bittau * - * 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. + * 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 #include "dccp.h" -/* - * Known limit values - */ -/* Ack Ratio takes 2-byte integer values (11.3) */ -#define DCCPF_ACK_RATIO_MAX 0xFFFF -/* Wmin=32 and Wmax=2^46-1 from 7.5.2 */ -#define DCCPF_SEQ_WMIN 32 -#define DCCPF_SEQ_WMAX 0x3FFFFFFFFFFFull -/* Maximum number of SP values that fit in a single (Confirm) option */ -#define DCCP_FEAT_MAX_SP_VALS (DCCP_SINGLE_OPT_MAXLEN - 2) - -enum dccp_feat_type { - FEAT_AT_RX = 1, /* located at RX side of half-connection */ - FEAT_AT_TX = 2, /* located at TX side of half-connection */ - FEAT_SP = 4, /* server-priority reconciliation (6.3.1) */ - FEAT_NN = 8, /* non-negotiable reconciliation (6.3.2) */ - FEAT_UNKNOWN = 0xFF /* not understood or invalid feature */ -}; - -enum dccp_feat_state { - FEAT_DEFAULT = 0, /* using default values from 6.4 */ - FEAT_INITIALISING, /* feature is being initialised */ - FEAT_CHANGING, /* Change sent but not confirmed yet */ - FEAT_UNSTABLE, /* local modification in state CHANGING */ - FEAT_STABLE /* both ends (think they) agree */ -}; +#ifdef CONFIG_IP_DCCP_DEBUG +extern const char *dccp_feat_typename(const u8 type); +extern const char *dccp_feat_name(const u8 feat); -/** - * dccp_feat_val - Container for SP or NN feature values - * @nn: single NN value - * @sp.vec: single SP value plus optional preference list - * @sp.len: length of @sp.vec in bytes - */ -typedef union { - u64 nn; - struct { - u8 *vec; - u8 len; - } sp; -} dccp_feat_val; - -/** - * struct feat_entry - Data structure to perform feature negotiation - * @feat_num: one of %dccp_feature_numbers - * @val: feature's current value (SP features may have preference list) - * @state: feature's current state - * @needs_mandatory: whether Mandatory options should be sent - * @needs_confirm: whether to send a Confirm instead of a Change - * @empty_confirm: whether to send an empty Confirm (depends on @needs_confirm) - * @is_local: feature location (1) or feature-remote (0) - * @node: list pointers, entries arranged in FIFO order - */ -struct dccp_feat_entry { - u8 feat_num; - dccp_feat_val val; - enum dccp_feat_state state:8; - bool needs_mandatory:1, - needs_confirm:1, - empty_confirm:1, - is_local:1; - - struct list_head node; -}; - -static inline u8 dccp_feat_genopt(struct dccp_feat_entry *entry) +static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) { - if (entry->needs_confirm) - return entry->is_local ? DCCPO_CONFIRM_L : DCCPO_CONFIRM_R; - return entry->is_local ? DCCPO_CHANGE_L : DCCPO_CHANGE_R; + dccp_pr_debug("%s(%s (%d), %d)\n", dccp_feat_typename(type), + dccp_feat_name(feat), feat, val); } +#else +#define dccp_feat_debug(type, feat, val) +#endif /* CONFIG_IP_DCCP_DEBUG */ + +extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, + u8 *val, u8 len, gfp_t gfp); +extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, + u8 *val, u8 len); +extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, + u8 *val, u8 len); +extern void dccp_feat_clean(struct dccp_minisock *dmsk); +extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); +extern int dccp_feat_init(struct dccp_minisock *dmsk); -/** - * struct ccid_dependency - Track changes resulting from choosing a CCID - * @dependent_feat: one of %dccp_feature_numbers - * @is_local: local (1) or remote (0) @dependent_feat - * @is_mandatory: whether presence of @dependent_feat is mission-critical or not - * @val: corresponding default value for @dependent_feat (u8 is sufficient here) - */ -struct ccid_dependency { - u8 dependent_feat; - bool is_local:1, - is_mandatory:1; - u8 val; -}; - -/* - * Sysctls to seed defaults for feature negotiation - */ -extern unsigned long sysctl_dccp_sequence_window; -extern int sysctl_dccp_rx_ccid; -extern int sysctl_dccp_tx_ccid; - -extern int dccp_feat_init(struct sock *sk); -extern void dccp_feat_initialise_sysctls(void); -extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, - u8 const *list, u8 len); -extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); -extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, - u8 mand, u8 opt, u8 feat, u8 *val, u8 len); -extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); - -/* - * Encoding variable-length options and their maximum length. - * - * This affects NN options (SP options are all u8) and other variable-length - * options (see table 3 in RFC 4340). The limit is currently given the Sequence - * Window NN value (sec. 7.5.2) and the NDP count (sec. 7.7) option, all other - * options consume less than 6 bytes (timestamps are 4 bytes). - * When updating this constant (e.g. due to new internet drafts / RFCs), make - * sure that you also update all code which refers to it. - */ -#define DCCP_OPTVAL_MAXLEN 6 - -extern void dccp_encode_value_var(const u64 value, u8 *to, const u8 len); -extern u64 dccp_decode_value_var(const u8 *bf, const u8 len); - -extern int dccp_insert_option_mandatory(struct sk_buff *skb); -extern int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat, - u8 *val, u8 len, bool repeat_first); #endif /* _DCCP_FEAT_H */ diff --git a/net/dccp/input.c b/net/dccp/input.c index df0e6714aa11..779d0ed9ae94 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -159,15 +159,13 @@ static void dccp_rcv_reset(struct sock *sk, struct sk_buff *skb) dccp_time_wait(sk, DCCP_TIME_WAIT, 0); } -static void dccp_handle_ackvec_processing(struct sock *sk, struct sk_buff *skb) +static void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb) { - struct dccp_ackvec *av = dccp_sk(sk)->dccps_hc_rx_ackvec; + struct dccp_sock *dp = dccp_sk(sk); - if (av == NULL) - return; - if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) - dccp_ackvec_clear_state(av, DCCP_SKB_CB(skb)->dccpd_ack_seq); - dccp_ackvec_input(av, skb); + if (dccp_msk(sk)->dccpms_send_ack_vector) + dccp_ackvec_check_rcv_ackno(dp->dccps_hc_rx_ackvec, sk, + DCCP_SKB_CB(skb)->dccpd_ack_seq); } static void dccp_deliver_input_to_ccids(struct sock *sk, struct sk_buff *skb) @@ -366,13 +364,22 @@ discard: int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, const struct dccp_hdr *dh, const unsigned len) { + struct dccp_sock *dp = dccp_sk(sk); + if (dccp_check_seqno(sk, skb)) goto discard; if (dccp_parse_options(sk, NULL, skb)) return 1; - dccp_handle_ackvec_processing(sk, skb); + if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) + dccp_event_ack_recv(sk, skb); + + if (dccp_msk(sk)->dccpms_send_ack_vector && + dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, + DCCP_SKB_CB(skb)->dccpd_seq, + DCCP_ACKVEC_STATE_RECEIVED)) + goto discard; dccp_deliver_input_to_ccids(sk, skb); return __dccp_rcv_established(sk, skb, dh, len); @@ -414,33 +421,40 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, goto out_invalid_packet; } - /* - * If option processing (Step 8) failed, return 1 here so that - * dccp_v4_do_rcv() sends a Reset. The Reset code depends on - * the option type and is set in dccp_parse_options(). - */ if (dccp_parse_options(sk, NULL, skb)) - return 1; + goto out_invalid_packet; /* Obtain usec RTT sample from SYN exchange (used by CCID 3) */ if (likely(dp->dccps_options_received.dccpor_timestamp_echo)) dp->dccps_syn_rtt = dccp_sample_rtt(sk, 10 * (tstamp - dp->dccps_options_received.dccpor_timestamp_echo)); + if (dccp_msk(sk)->dccpms_send_ack_vector && + dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, + DCCP_SKB_CB(skb)->dccpd_seq, + DCCP_ACKVEC_STATE_RECEIVED)) + goto out_invalid_packet; /* FIXME: change error code */ + /* Stop the REQUEST timer */ inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); WARN_ON(sk->sk_send_head == NULL); kfree_skb(sk->sk_send_head); sk->sk_send_head = NULL; + dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq; + dccp_update_gsr(sk, dp->dccps_isr); /* - * Set ISR, GSR from packet. ISS was set in dccp_v{4,6}_connect - * and GSS in dccp_transmit_skb(). Setting AWL/AWH and SWL/SWH - * is done as part of activating the feature values below, since - * these settings depend on the local/remote Sequence Window - * features, which were undefined or not confirmed until now. + * SWL and AWL are initially adjusted so that they are not less than + * the initial Sequence Numbers received and sent, respectively: + * SWL := max(GSR + 1 - floor(W/4), ISR), + * AWL := max(GSS - W' + 1, ISS). + * These adjustments MUST be applied only at the beginning of the + * connection. + * + * AWL was adjusted in dccp_v4_connect -acme */ - dp->dccps_gsr = dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq; + dccp_set_seqno(&dp->dccps_swl, + max48(dp->dccps_swl, dp->dccps_isr)); dccp_sync_mss(sk, icsk->icsk_pmtu_cookie); @@ -461,15 +475,6 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, */ dccp_set_state(sk, DCCP_PARTOPEN); - /* - * If feature negotiation was successful, activate features now; - * an activation failure means that this host could not activate - * one ore more features (e.g. insufficient memory), which would - * leave at least one feature in an undefined state. - */ - if (dccp_feat_activate_values(sk, &dp->dccps_featneg)) - goto unable_to_proceed; - /* Make sure socket is routed, for correct metrics. */ icsk->icsk_af_ops->rebuild_header(sk); @@ -504,16 +509,6 @@ out_invalid_packet: /* dccp_v4_do_rcv will send a reset */ DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR; return 1; - -unable_to_proceed: - DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_ABORTED; - /* - * We mark this socket as no longer usable, so that the loop in - * dccp_sendmsg() terminates and the application gets notified. - */ - dccp_set_state(sk, DCCP_CLOSED); - sk->sk_err = ECOMM; - return 1; } static int dccp_rcv_respond_partopen_state_process(struct sock *sk, @@ -595,6 +590,8 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (inet_csk(sk)->icsk_af_ops->conn_request(sk, skb) < 0) return 1; + + /* FIXME: do congestion control initialization */ goto discard; } if (dh->dccph_type == DCCP_PKT_RESET) @@ -603,35 +600,29 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, /* Caller (dccp_v4_do_rcv) will send Reset */ dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; return 1; - } else if (sk->sk_state == DCCP_CLOSED) { - dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; - return 1; } - /* Step 6: Check sequence numbers (omitted in LISTEN/REQUEST state) */ - if (sk->sk_state != DCCP_REQUESTING && dccp_check_seqno(sk, skb)) - goto discard; + if (sk->sk_state != DCCP_REQUESTING) { + if (dccp_check_seqno(sk, skb)) + goto discard; - /* - * Step 7: Check for unexpected packet types - * If (S.is_server and P.type == Response) - * or (S.is_client and P.type == Request) - * or (S.state == RESPOND and P.type == Data), - * Send Sync packet acknowledging P.seqno - * Drop packet and return - */ - if ((dp->dccps_role != DCCP_ROLE_CLIENT && - dh->dccph_type == DCCP_PKT_RESPONSE) || - (dp->dccps_role == DCCP_ROLE_CLIENT && - dh->dccph_type == DCCP_PKT_REQUEST) || - (sk->sk_state == DCCP_RESPOND && dh->dccph_type == DCCP_PKT_DATA)) { - dccp_send_sync(sk, dcb->dccpd_seq, DCCP_PKT_SYNC); - goto discard; - } + /* + * Step 8: Process options and mark acknowledgeable + */ + if (dccp_parse_options(sk, NULL, skb)) + return 1; - /* Step 8: Process options */ - if (dccp_parse_options(sk, NULL, skb)) - return 1; + if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) + dccp_event_ack_recv(sk, skb); + + if (dccp_msk(sk)->dccpms_send_ack_vector && + dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, + DCCP_SKB_CB(skb)->dccpd_seq, + DCCP_ACKVEC_STATE_RECEIVED)) + goto discard; + + dccp_deliver_input_to_ccids(sk, skb); + } /* * Step 9: Process Reset @@ -640,22 +631,44 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * S.state := TIMEWAIT * Set TIMEWAIT timer * Drop packet and return - */ + */ if (dh->dccph_type == DCCP_PKT_RESET) { dccp_rcv_reset(sk, skb); return 0; - } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { /* Step 13 */ + /* + * Step 7: Check for unexpected packet types + * If (S.is_server and P.type == Response) + * or (S.is_client and P.type == Request) + * or (S.state == RESPOND and P.type == Data), + * Send Sync packet acknowledging P.seqno + * Drop packet and return + */ + } else if ((dp->dccps_role != DCCP_ROLE_CLIENT && + dh->dccph_type == DCCP_PKT_RESPONSE) || + (dp->dccps_role == DCCP_ROLE_CLIENT && + dh->dccph_type == DCCP_PKT_REQUEST) || + (sk->sk_state == DCCP_RESPOND && + dh->dccph_type == DCCP_PKT_DATA)) { + dccp_send_sync(sk, dcb->dccpd_seq, DCCP_PKT_SYNC); + goto discard; + } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { if (dccp_rcv_closereq(sk, skb)) return 0; goto discard; - } else if (dh->dccph_type == DCCP_PKT_CLOSE) { /* Step 14 */ + } else if (dh->dccph_type == DCCP_PKT_CLOSE) { if (dccp_rcv_close(sk, skb)) return 0; goto discard; } switch (sk->sk_state) { + case DCCP_CLOSED: + dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; + return 1; + case DCCP_REQUESTING: + /* FIXME: do congestion control initialization */ + queued = dccp_rcv_request_sent_state_process(sk, skb, dh, len); if (queued >= 0) return queued; @@ -663,12 +676,8 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, __kfree_skb(skb); return 0; - case DCCP_PARTOPEN: - /* Step 8: if using Ack Vectors, mark packet acknowledgeable */ - dccp_handle_ackvec_processing(sk, skb); - dccp_deliver_input_to_ccids(sk, skb); - /* fall through */ case DCCP_RESPOND: + case DCCP_PARTOPEN: queued = dccp_rcv_respond_partopen_state_process(sk, skb, dh, len); break; @@ -707,7 +716,16 @@ u32 dccp_sample_rtt(struct sock *sk, long delta) /* dccpor_elapsed_time is either zeroed out or set and > 0 */ delta -= dccp_sk(sk)->dccps_options_received.dccpor_elapsed_time * 10; - return dccp_sane_rtt(delta); + if (unlikely(delta <= 0)) { + DCCP_WARN("unusable RTT sample %ld, using min\n", delta); + return DCCP_SANE_RTT_MIN; + } + if (unlikely(delta > DCCP_SANE_RTT_MAX)) { + DCCP_WARN("RTT sample %ld too large, using max\n", delta); + return DCCP_SANE_RTT_MAX; + } + + return delta; } EXPORT_SYMBOL_GPL(dccp_sample_rtt); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index b623f6b25482..882c5c4de69e 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -545,7 +545,6 @@ out: static void dccp_v4_reqsk_destructor(struct request_sock *req) { - dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); kfree(inet_rsk(req)->opt); } @@ -596,8 +595,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (req == NULL) goto drop; - if (dccp_reqsk_init(req, dccp_sk(sk), skb)) - goto drop_and_free; + dccp_reqsk_init(req, skb); dreq = dccp_rsk(req); if (dccp_parse_options(sk, dreq, skb)) diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index ad6212e00435..5e1ee0da2c40 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -302,7 +302,6 @@ done: static void dccp_v6_reqsk_destructor(struct request_sock *req) { - dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); if (inet6_rsk(req)->pktopts != NULL) kfree_skb(inet6_rsk(req)->pktopts); } @@ -425,8 +424,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (req == NULL) goto drop; - if (dccp_reqsk_init(req, dccp_sk(sk), skb)) - goto drop_and_free; + dccp_reqsk_init(req, skb); dreq = dccp_rsk(req); if (dccp_parse_options(sk, dreq, skb)) diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index f4d9c8f60ede..b2804e2d1b8c 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -42,6 +42,16 @@ struct inet_timewait_death_row dccp_death_row = { EXPORT_SYMBOL_GPL(dccp_death_row); +void dccp_minisock_init(struct dccp_minisock *dmsk) +{ + dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; + dmsk->dccpms_rx_ccid = sysctl_dccp_feat_rx_ccid; + dmsk->dccpms_tx_ccid = sysctl_dccp_feat_tx_ccid; + dmsk->dccpms_ack_ratio = sysctl_dccp_feat_ack_ratio; + dmsk->dccpms_send_ack_vector = sysctl_dccp_feat_send_ack_vector; + dmsk->dccpms_send_ndp_count = sysctl_dccp_feat_send_ndp_count; +} + void dccp_time_wait(struct sock *sk, int state, int timeo) { struct inet_timewait_sock *tw = NULL; @@ -102,9 +112,10 @@ struct sock *dccp_create_openreq_child(struct sock *sk, struct sock *newsk = inet_csk_clone(sk, req, GFP_ATOMIC); if (newsk != NULL) { - struct dccp_request_sock *dreq = dccp_rsk(req); + const struct dccp_request_sock *dreq = dccp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct dccp_sock *newdp = dccp_sk(newsk); + struct dccp_minisock *newdmsk = dccp_msk(newsk); newdp->dccps_role = DCCP_ROLE_SERVER; newdp->dccps_hc_rx_ackvec = NULL; @@ -114,32 +125,65 @@ struct sock *dccp_create_openreq_child(struct sock *sk, newdp->dccps_timestamp_time = dreq->dreq_timestamp_time; newicsk->icsk_rto = DCCP_TIMEOUT_INIT; - INIT_LIST_HEAD(&newdp->dccps_featneg); + if (dccp_feat_clone(sk, newsk)) + goto out_free; + + if (newdmsk->dccpms_send_ack_vector) { + newdp->dccps_hc_rx_ackvec = + dccp_ackvec_alloc(GFP_ATOMIC); + if (unlikely(newdp->dccps_hc_rx_ackvec == NULL)) + goto out_free; + } + + newdp->dccps_hc_rx_ccid = + ccid_hc_rx_new(newdmsk->dccpms_rx_ccid, + newsk, GFP_ATOMIC); + newdp->dccps_hc_tx_ccid = + ccid_hc_tx_new(newdmsk->dccpms_tx_ccid, + newsk, GFP_ATOMIC); + if (unlikely(newdp->dccps_hc_rx_ccid == NULL || + newdp->dccps_hc_tx_ccid == NULL)) { + dccp_ackvec_free(newdp->dccps_hc_rx_ackvec); + ccid_hc_rx_delete(newdp->dccps_hc_rx_ccid, newsk); + ccid_hc_tx_delete(newdp->dccps_hc_tx_ccid, newsk); +out_free: + /* It is still raw copy of parent, so invalidate + * destructor and make plain sk_free() */ + newsk->sk_destruct = NULL; + sk_free(newsk); + return NULL; + } + /* * Step 3: Process LISTEN state * * Choose S.ISS (initial seqno) or set from Init Cookies * Initialize S.GAR := S.ISS - * Set S.ISR, S.GSR from packet (or Init Cookies) - * - * Setting AWL/AWH and SWL/SWH happens as part of the feature - * activation below, as these windows all depend on the local - * and remote Sequence Window feature values (7.5.2). + * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies */ - newdp->dccps_gss = newdp->dccps_iss = dreq->dreq_iss; - newdp->dccps_gar = newdp->dccps_iss; - newdp->dccps_gsr = newdp->dccps_isr = dreq->dreq_isr; + + /* See dccp_v4_conn_request */ + newdmsk->dccpms_sequence_window = req->rcv_wnd; + + newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss; + dccp_update_gss(newsk, dreq->dreq_iss); + + newdp->dccps_isr = dreq->dreq_isr; + dccp_update_gsr(newsk, dreq->dreq_isr); /* - * Activate features: initialise CCIDs, sequence windows etc. + * SWL and AWL are initially adjusted so that they are not less than + * the initial Sequence Numbers received and sent, respectively: + * SWL := max(GSR + 1 - floor(W/4), ISR), + * AWL := max(GSS - W' + 1, ISS). + * These adjustments MUST be applied only at the beginning of the + * connection. */ - if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) { - /* It is still raw copy of parent, so invalidate - * destructor and make plain sk_free() */ - newsk->sk_destruct = NULL; - sk_free(newsk); - return NULL; - } + dccp_set_seqno(&newdp->dccps_swl, + max48(newdp->dccps_swl, newdp->dccps_isr)); + dccp_set_seqno(&newdp->dccps_awl, + max48(newdp->dccps_awl, newdp->dccps_iss)); + dccp_init_xmit_timers(newsk); DCCP_INC_STATS_BH(DCCP_MIB_PASSIVEOPENS); @@ -260,17 +304,14 @@ void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, EXPORT_SYMBOL_GPL(dccp_reqsk_send_ack); -int dccp_reqsk_init(struct request_sock *req, - struct dccp_sock const *dp, struct sk_buff const *skb) +void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb) { struct dccp_request_sock *dreq = dccp_rsk(req); inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport; inet_rsk(req)->acked = 0; + req->rcv_wnd = sysctl_dccp_feat_sequence_window; dreq->dreq_timestamp_echo = 0; - - /* inherit feature negotiation options from listening socket */ - return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg); } EXPORT_SYMBOL_GPL(dccp_reqsk_init); diff --git a/net/dccp/options.c b/net/dccp/options.c index e5a32979d7d7..0809b63cb055 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -23,20 +23,23 @@ #include "dccp.h" #include "feat.h" -u64 dccp_decode_value_var(const u8 *bf, const u8 len) +int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; +int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; +int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; +int sysctl_dccp_feat_ack_ratio = DCCPF_INITIAL_ACK_RATIO; +int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; +int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT; + +static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len) { - u64 value = 0; + u32 value = 0; - if (len >= DCCP_OPTVAL_MAXLEN) - value += ((u64)*bf++) << 40; - if (len > 4) - value += ((u64)*bf++) << 32; if (len > 3) - value += ((u64)*bf++) << 24; + value += *bf++ << 24; if (len > 2) - value += ((u64)*bf++) << 16; + value += *bf++ << 16; if (len > 1) - value += ((u64)*bf++) << 8; + value += *bf++ << 8; if (len > 0) value += *bf; @@ -54,6 +57,7 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, struct dccp_sock *dp = dccp_sk(sk); const struct dccp_hdr *dh = dccp_hdr(skb); const u8 pkt_type = DCCP_SKB_CB(skb)->dccpd_type; + u64 ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq; unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb); unsigned char *opt_ptr = options; const unsigned char *opt_end = (unsigned char *)dh + @@ -95,11 +99,18 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, } /* + * CCID-Specific Options (from RFC 4340, sec. 10.3): + * + * Option numbers 128 through 191 are for options sent from the + * HC-Sender to the HC-Receiver; option numbers 192 through 255 + * are for options sent from the HC-Receiver to the HC-Sender. + * * CCID-specific options are ignored during connection setup, as * negotiation may still be in progress (see RFC 4340, 10.3). * The same applies to Ack Vectors, as these depend on the CCID. + * */ - if (dreq != NULL && (opt >= DCCPO_MIN_RX_CCID_SPECIFIC || + if (dreq != NULL && (opt >= 128 || opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1)) goto ignore_option; @@ -120,13 +131,43 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk), (unsigned long long)opt_recv->dccpor_ndp); break; - case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R: - if (pkt_type == DCCP_PKT_DATA) /* RFC 4340, 6 */ + case DCCPO_CHANGE_L: + /* fall through */ + case DCCPO_CHANGE_R: + if (pkt_type == DCCP_PKT_DATA) break; - rc = dccp_feat_parse_options(sk, dreq, mandatory, opt, - *value, value + 1, len - 1); - if (rc) - goto out_featneg_failed; + if (len < 2) + goto out_invalid_option; + rc = dccp_feat_change_recv(sk, opt, *value, value + 1, + len - 1); + /* + * When there is a change error, change_recv is + * responsible for dealing with it. i.e. reply with an + * empty confirm. + * If the change was mandatory, then we need to die. + */ + if (rc && mandatory) + goto out_invalid_option; + break; + case DCCPO_CONFIRM_L: + /* fall through */ + case DCCPO_CONFIRM_R: + if (pkt_type == DCCP_PKT_DATA) + break; + if (len < 2) /* FIXME this disallows empty confirm */ + goto out_invalid_option; + if (dccp_feat_confirm_recv(sk, opt, *value, + value + 1, len - 1)) + goto out_invalid_option; + break; + case DCCPO_ACK_VECTOR_0: + case DCCPO_ACK_VECTOR_1: + if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */ + break; + + if (dccp_msk(sk)->dccpms_send_ack_vector && + dccp_ackvec_parse(sk, skb, &ackno, opt, value, len)) + goto out_invalid_option; break; case DCCPO_TIMESTAMP: if (len != 4) @@ -154,8 +195,6 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, dccp_role(sk), ntohl(opt_val), (unsigned long long) DCCP_SKB_CB(skb)->dccpd_ack_seq); - /* schedule an Ack in case this sender is quiescent */ - inet_csk_schedule_ack(sk); break; case DCCPO_TIMESTAMP_ECHO: if (len != 4 && len != 6 && len != 8) @@ -212,25 +251,23 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n", dccp_role(sk), elapsed_time); break; - case DCCPO_MIN_RX_CCID_SPECIFIC ... DCCPO_MAX_RX_CCID_SPECIFIC: + case 128 ... 191: { + const u16 idx = value - options; + if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk, - pkt_type, opt, value, len)) + opt, len, idx, + value) != 0) goto out_invalid_option; + } break; - case DCCPO_ACK_VECTOR_0: - case DCCPO_ACK_VECTOR_1: - if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */ - break; - /* - * Ack vectors are processed by the TX CCID if it is - * interested. The RX CCID need not parse Ack Vectors, - * since it is only interested in clearing old state. - * Fall through. - */ - case DCCPO_MIN_TX_CCID_SPECIFIC ... DCCPO_MAX_TX_CCID_SPECIFIC: + case 192 ... 255: { + const u16 idx = value - options; + if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk, - pkt_type, opt, value, len)) + opt, len, idx, + value) != 0) goto out_invalid_option; + } break; default: DCCP_CRIT("DCCP(%p): option %d(len=%d) not " @@ -252,10 +289,8 @@ out_nonsensical_length: out_invalid_option: DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT); - rc = DCCP_RESET_CODE_OPTION_ERROR; -out_featneg_failed: - DCCP_WARN("DCCP(%p): Option %d (len=%d) error=%u\n", sk, opt, len, rc); - DCCP_SKB_CB(skb)->dccpd_reset_code = rc; + DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR; + DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len); DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt; DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0; DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0; @@ -264,12 +299,9 @@ out_featneg_failed: EXPORT_SYMBOL_GPL(dccp_parse_options); -void dccp_encode_value_var(const u64 value, u8 *to, const u8 len) +static void dccp_encode_value_var(const u32 value, unsigned char *to, + const unsigned int len) { - if (len >= DCCP_OPTVAL_MAXLEN) - *to++ = (value & 0xFF0000000000ull) >> 40; - if (len > 4) - *to++ = (value & 0xFF00000000ull) >> 32; if (len > 3) *to++ = (value & 0xFF000000) >> 24; if (len > 2) @@ -429,140 +461,92 @@ static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp, return 0; } -static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) +static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat, + u8 *val, u8 len) { - struct dccp_sock *dp = dccp_sk(sk); - struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; - struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); - const u16 buflen = dccp_ackvec_buflen(av); - /* Figure out how many options do we need to represent the ackvec */ - const u8 nr_opts = DIV_ROUND_UP(buflen, DCCP_SINGLE_OPT_MAXLEN); - u16 len = buflen + 2 * nr_opts; - u8 i, nonce = 0; - const unsigned char *tail, *from; - unsigned char *to; + u8 *to; - if (dcb->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) { - DCCP_WARN("Lacking space for %u bytes on %s packet\n", len, - dccp_packet_name(dcb->dccpd_type)); + if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) { + DCCP_WARN("packet too small for feature %d option!\n", feat); return -1; } - /* - * Since Ack Vectors are variable-length, we can not always predict - * their size. To catch exception cases where the space is running out - * on the skb, a separate Sync is scheduled to carry the Ack Vector. - */ - if (len > DCCPAV_MIN_OPTLEN && - len + dcb->dccpd_opt_len + skb->len > dp->dccps_mss_cache) { - DCCP_WARN("No space left for Ack Vector (%u) on skb (%u+%u), " - "MPS=%u ==> reduce payload size?\n", len, skb->len, - dcb->dccpd_opt_len, dp->dccps_mss_cache); - dp->dccps_sync_scheduled = 1; - return 0; - } - dcb->dccpd_opt_len += len; - to = skb_push(skb, len); - len = buflen; - from = av->av_buf + av->av_buf_head; - tail = av->av_buf + DCCPAV_MAX_ACKVEC_LEN; + DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3; - for (i = 0; i < nr_opts; ++i) { - int copylen = len; - - if (len > DCCP_SINGLE_OPT_MAXLEN) - copylen = DCCP_SINGLE_OPT_MAXLEN; - - /* - * RFC 4340, 12.2: Encode the Nonce Echo for this Ack Vector via - * its type; ack_nonce is the sum of all individual buf_nonce's. - */ - nonce ^= av->av_buf_nonce[i]; - - *to++ = DCCPO_ACK_VECTOR_0 + av->av_buf_nonce[i]; - *to++ = copylen + 2; - - /* Check if buf_head wraps */ - if (from + copylen > tail) { - const u16 tailsize = tail - from; - - memcpy(to, from, tailsize); - to += tailsize; - len -= tailsize; - copylen -= tailsize; - from = av->av_buf; - } - - memcpy(to, from, copylen); - from += copylen; - to += copylen; - len -= copylen; - } - /* - * Each sent Ack Vector is recorded in the list, as per A.2 of RFC 4340. - */ - if (dccp_ackvec_update_records(av, dcb->dccpd_seq, nonce)) - return -ENOBUFS; - return 0; -} + to = skb_push(skb, len + 3); + *to++ = type; + *to++ = len + 3; + *to++ = feat; -/** - * dccp_insert_option_mandatory - Mandatory option (5.8.2) - * Note that since we are using skb_push, this function needs to be called - * _after_ inserting the option it is supposed to influence (stack order). - */ -int dccp_insert_option_mandatory(struct sk_buff *skb) -{ - if (DCCP_SKB_CB(skb)->dccpd_opt_len >= DCCP_MAX_OPT_LEN) - return -1; + if (len) + memcpy(to, val, len); - DCCP_SKB_CB(skb)->dccpd_opt_len++; - *skb_push(skb, 1) = DCCPO_MANDATORY; + dccp_pr_debug("%s(%s (%d), ...), length %d\n", + dccp_feat_typename(type), + dccp_feat_name(feat), feat, len); return 0; } -/** - * dccp_insert_fn_opt - Insert single Feature-Negotiation option into @skb - * @type: %DCCPO_CHANGE_L, %DCCPO_CHANGE_R, %DCCPO_CONFIRM_L, %DCCPO_CONFIRM_R - * @feat: one out of %dccp_feature_numbers - * @val: NN value or SP array (preferred element first) to copy - * @len: true length of @val in bytes (excluding first element repetition) - * @repeat_first: whether to copy the first element of @val twice - * The last argument is used to construct Confirm options, where the preferred - * value and the preference list appear separately (RFC 4340, 6.3.1). Preference - * lists are kept such that the preferred entry is always first, so we only need - * to copy twice, and avoid the overhead of cloning into a bigger array. - */ -int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat, - u8 *val, u8 len, bool repeat_first) +static int dccp_insert_options_feat(struct sock *sk, struct sk_buff *skb) { - u8 tot_len, *to; + struct dccp_sock *dp = dccp_sk(sk); + struct dccp_minisock *dmsk = dccp_msk(sk); + struct dccp_opt_pend *opt, *next; + int change = 0; + + /* confirm any options [NN opts] */ + list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) { + dccp_insert_feat_opt(skb, opt->dccpop_type, + opt->dccpop_feat, opt->dccpop_val, + opt->dccpop_len); + /* fear empty confirms */ + if (opt->dccpop_val) + kfree(opt->dccpop_val); + kfree(opt); + } + INIT_LIST_HEAD(&dmsk->dccpms_conf); + + /* see which features we need to send */ + list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { + /* see if we need to send any confirm */ + if (opt->dccpop_sc) { + dccp_insert_feat_opt(skb, opt->dccpop_type + 1, + opt->dccpop_feat, + opt->dccpop_sc->dccpoc_val, + opt->dccpop_sc->dccpoc_len); + + BUG_ON(!opt->dccpop_sc->dccpoc_val); + kfree(opt->dccpop_sc->dccpoc_val); + kfree(opt->dccpop_sc); + opt->dccpop_sc = NULL; + } - /* take the `Feature' field and possible repetition into account */ - if (len > (DCCP_SINGLE_OPT_MAXLEN - 2)) { - DCCP_WARN("length %u for feature %u too large\n", len, feat); - return -1; + /* any option not confirmed, re-send it */ + if (!opt->dccpop_conf) { + dccp_insert_feat_opt(skb, opt->dccpop_type, + opt->dccpop_feat, opt->dccpop_val, + opt->dccpop_len); + change++; + } } - if (unlikely(val == NULL || len == 0)) - len = repeat_first = 0; - tot_len = 3 + repeat_first + len; + /* Retransmit timer. + * If this is the master listening sock, we don't set a timer on it. It + * should be fine because if the dude doesn't receive our RESPONSE + * [which will contain the CHANGE] he will send another REQUEST which + * will "retrnasmit" the change. + */ + if (change && dp->dccps_role != DCCP_ROLE_LISTEN) { + dccp_pr_debug("reset feat negotiation timer %p\n", sk); - if (DCCP_SKB_CB(skb)->dccpd_opt_len + tot_len > DCCP_MAX_OPT_LEN) { - DCCP_WARN("packet too small for feature %d option!\n", feat); - return -1; + /* XXX don't reset the timer on re-transmissions. I.e. reset it + * only when sending new stuff i guess. Currently the timer + * never backs off because on re-transmission it just resets it! + */ + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + inet_csk(sk)->icsk_rto, DCCP_RTO_MAX); } - DCCP_SKB_CB(skb)->dccpd_opt_len += tot_len; - - to = skb_push(skb, tot_len); - *to++ = type; - *to++ = tot_len; - *to++ = feat; - if (repeat_first) - *to++ = *val; - if (len) - memcpy(to, val, len); return 0; } @@ -581,30 +565,19 @@ static void dccp_insert_option_padding(struct sk_buff *skb) int dccp_insert_options(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); + struct dccp_minisock *dmsk = dccp_msk(sk); DCCP_SKB_CB(skb)->dccpd_opt_len = 0; - if (dp->dccps_send_ndp_count && dccp_insert_option_ndp(sk, skb)) + if (dmsk->dccpms_send_ndp_count && + dccp_insert_option_ndp(sk, skb)) return -1; - if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA) { - - /* Feature Negotiation */ - if (dccp_feat_insert_opts(dp, NULL, skb)) + if (!dccp_packet_without_ack(skb)) { + if (dmsk->dccpms_send_ack_vector && + dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) && + dccp_insert_option_ackvec(sk, skb)) return -1; - - if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST) { - /* - * Obtain RTT sample from Request/Response exchange. - * This is currently used in CCID 3 initialisation. - */ - if (dccp_insert_option_timestamp(sk, skb)) - return -1; - - } else if (dccp_ackvec_pending(sk) && - dccp_insert_option_ackvec(sk, skb)) { - return -1; - } } if (dp->dccps_hc_rx_insert_options) { @@ -613,6 +586,21 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb) dp->dccps_hc_rx_insert_options = 0; } + /* Feature negotiation */ + /* Data packets can't do feat negotiation */ + if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA && + DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATAACK && + dccp_insert_options_feat(sk, skb)) + return -1; + + /* + * Obtain RTT sample from Request/Response exchange. + * This is currently used in CCID 3 initialisation. + */ + if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST && + dccp_insert_option_timestamp(sk, skb)) + return -1; + if (dp->dccps_timestamp_echo != 0 && dccp_insert_option_timestamp_echo(dp, NULL, skb)) return -1; @@ -625,9 +613,6 @@ int dccp_insert_options_rsk(struct dccp_request_sock *dreq, struct sk_buff *skb) { DCCP_SKB_CB(skb)->dccpd_opt_len = 0; - if (dccp_feat_insert_opts(NULL, dreq, skb)) - return -1; - if (dreq->dreq_timestamp_echo != 0 && dccp_insert_option_timestamp_echo(NULL, dreq, skb)) return -1; diff --git a/net/dccp/output.c b/net/dccp/output.c index 2532797a8009..d06945c7d3df 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -26,13 +26,11 @@ static inline void dccp_event_ack_sent(struct sock *sk) inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); } -/* enqueue @skb on sk_send_head for retransmission, return clone to send now */ -static struct sk_buff *dccp_skb_entail(struct sock *sk, struct sk_buff *skb) +static void dccp_skb_entail(struct sock *sk, struct sk_buff *skb) { skb_set_owner_w(skb, sk); WARN_ON(sk->sk_send_head); sk->sk_send_head = skb; - return skb_clone(sk->sk_send_head, gfp_any()); } /* @@ -163,27 +161,21 @@ unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu) struct inet_connection_sock *icsk = inet_csk(sk); struct dccp_sock *dp = dccp_sk(sk); u32 ccmps = dccp_determine_ccmps(dp); - u32 cur_mps = ccmps ? min(pmtu, ccmps) : pmtu; + int cur_mps = ccmps ? min(pmtu, ccmps) : pmtu; /* Account for header lengths and IPv4/v6 option overhead */ cur_mps -= (icsk->icsk_af_ops->net_header_len + icsk->icsk_ext_hdr_len + sizeof(struct dccp_hdr) + sizeof(struct dccp_hdr_ext)); /* - * Leave enough headroom for common DCCP header options. - * This only considers options which may appear on DCCP-Data packets, as - * per table 3 in RFC 4340, 5.8. When running out of space for other - * options (eg. Ack Vector which can take up to 255 bytes), it is better - * to schedule a separate Ack. Thus we leave headroom for the following: - * - 1 byte for Slow Receiver (11.6) - * - 6 bytes for Timestamp (13.1) - * - 10 bytes for Timestamp Echo (13.3) - * - 8 bytes for NDP count (7.7, when activated) - * - 6 bytes for Data Checksum (9.3) - * - %DCCPAV_MIN_OPTLEN bytes for Ack Vector size (11.4, when enabled) + * FIXME: this should come from the CCID infrastructure, where, say, + * TFRC will say it wants TIMESTAMPS, ELAPSED time, etc, for now lets + * put a rough estimate for NDP + TIMESTAMP + TIMESTAMP_ECHO + ELAPSED + * TIME + TFRC_OPT_LOSS_EVENT_RATE + TFRC_OPT_RECEIVE_RATE + padding to + * make it a multiple of 4 */ - cur_mps -= roundup(1 + 6 + 10 + dp->dccps_send_ndp_count * 8 + 6 + - (dp->dccps_hc_rx_ackvec ? DCCPAV_MIN_OPTLEN : 0), 4); + + cur_mps -= ((5 + 6 + 10 + 6 + 6 + 6 + 3) / 4) * 4; /* And store cached results */ icsk->icsk_pmtu_cookie = pmtu; @@ -208,158 +200,95 @@ void dccp_write_space(struct sock *sk) } /** - * dccp_wait_for_ccid - Await CCID send permission + * dccp_wait_for_ccid - Wait for ccid to tell us we can send a packet * @sk: socket to wait for - * @delay: timeout in jiffies - * This is used by CCIDs which need to delay the send time in process context. + * @skb: current skb to pass on for waiting + * @delay: sleep timeout in milliseconds (> 0) + * This function is called by default when the socket is closed, and + * when a non-zero linger time is set on the socket. For consistency */ -static int dccp_wait_for_ccid(struct sock *sk, unsigned long delay) +static int dccp_wait_for_ccid(struct sock *sk, struct sk_buff *skb, int delay) { + struct dccp_sock *dp = dccp_sk(sk); DEFINE_WAIT(wait); - long remaining; - - prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); - sk->sk_write_pending++; - release_sock(sk); + unsigned long jiffdelay; + int rc; - remaining = schedule_timeout(delay); - - lock_sock(sk); - sk->sk_write_pending--; - finish_wait(sk->sk_sleep, &wait); + do { + dccp_pr_debug("delayed send by %d msec\n", delay); + jiffdelay = msecs_to_jiffies(delay); - if (signal_pending(current) || sk->sk_err) - return -1; - return remaining; -} - -/** - * dccp_xmit_packet - Send data packet under control of CCID - * Transmits next-queued payload and informs CCID to account for the packet. - */ -static void dccp_xmit_packet(struct sock *sk) -{ - int err, len; - struct dccp_sock *dp = dccp_sk(sk); - struct sk_buff *skb = dccp_qpolicy_pop(sk); + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); - if (unlikely(skb == NULL)) - return; - len = skb->len; + sk->sk_write_pending++; + release_sock(sk); + schedule_timeout(jiffdelay); + lock_sock(sk); + sk->sk_write_pending--; - if (sk->sk_state == DCCP_PARTOPEN) { - const u32 cur_mps = dp->dccps_mss_cache - DCCP_FEATNEG_OVERHEAD; - /* - * See 8.1.5 - Handshake Completion. - * - * For robustness we resend Confirm options until the client has - * entered OPEN. During the initial feature negotiation, the MPS - * is smaller than usual, reduced by the Change/Confirm options. - */ - if (!list_empty(&dp->dccps_featneg) && len > cur_mps) { - DCCP_WARN("Payload too large (%d) for featneg.\n", len); - dccp_send_ack(sk); - dccp_feat_list_purge(&dp->dccps_featneg); - } + if (sk->sk_err) + goto do_error; + if (signal_pending(current)) + goto do_interrupted; - inet_csk_schedule_ack(sk); - inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, - inet_csk(sk)->icsk_rto, - DCCP_RTO_MAX); - DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATAACK; - } else if (dccp_ack_pending(sk)) { - DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATAACK; - } else { - DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATA; - } - - err = dccp_transmit_skb(sk, skb); - if (err) - dccp_pr_debug("transmit_skb() returned err=%d\n", err); - /* - * Register this one as sent even if an error occurred. To the remote - * end a local packet drop is indistinguishable from network loss, i.e. - * any local drop will eventually be reported via receiver feedback. - */ - ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, len); - - /* - * If the CCID needs to transfer additional header options out-of-band - * (e.g. Ack Vectors or feature-negotiation options), it activates this - * flag to schedule a Sync. The Sync will automatically incorporate all - * currently pending header options, thus clearing the backlog. - */ - if (dp->dccps_sync_scheduled) - dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); + rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); + } while ((delay = rc) > 0); +out: + finish_wait(sk->sk_sleep, &wait); + return rc; + +do_error: + rc = -EPIPE; + goto out; +do_interrupted: + rc = -EINTR; + goto out; } -/** - * dccp_flush_write_queue - Drain queue at end of connection - * Since dccp_sendmsg queues packets without waiting for them to be sent, it may - * happen that the TX queue is not empty at the end of a connection. We give the - * HC-sender CCID a grace period of up to @time_budget jiffies. If this function - * returns with a non-empty write queue, it will be purged later. - */ -void dccp_flush_write_queue(struct sock *sk, long *time_budget) +void dccp_write_xmit(struct sock *sk, int block) { struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; - long delay, rc; - - while (*time_budget > 0 && (skb = skb_peek(&sk->sk_write_queue))) { - rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); - switch (ccid_packet_dequeue_eval(rc)) { - case CCID_PACKET_WILL_DEQUEUE_LATER: - /* - * If the CCID determines when to send, the next sending - * time is unknown or the CCID may not even send again - * (e.g. remote host crashes or lost Ack packets). - */ - DCCP_WARN("CCID did not manage to send all packets\n"); - return; - case CCID_PACKET_DELAY: - delay = msecs_to_jiffies(rc); - if (delay > *time_budget) - return; - rc = dccp_wait_for_ccid(sk, delay); - if (rc < 0) - return; - *time_budget -= (delay - rc); - /* check again if we can send now */ - break; - case CCID_PACKET_SEND_AT_ONCE: - dccp_xmit_packet(sk); - break; - case CCID_PACKET_ERR: - skb_dequeue(&sk->sk_write_queue); - kfree_skb(skb); - dccp_pr_debug("packet discarded due to err=%ld\n", rc); + while ((skb = skb_peek(&sk->sk_write_queue))) { + int err = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); + + if (err > 0) { + if (!block) { + sk_reset_timer(sk, &dp->dccps_xmit_timer, + msecs_to_jiffies(err)+jiffies); + break; + } else + err = dccp_wait_for_ccid(sk, skb, err); + if (err && err != -EINTR) + DCCP_BUG("err=%d after dccp_wait_for_ccid", err); } - } -} -void dccp_write_xmit(struct sock *sk) -{ - struct dccp_sock *dp = dccp_sk(sk); - struct sk_buff *skb; + skb_dequeue(&sk->sk_write_queue); + if (err == 0) { + struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); + const int len = skb->len; - while ((skb = dccp_qpolicy_top(sk))) { - int rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); - - switch (ccid_packet_dequeue_eval(rc)) { - case CCID_PACKET_WILL_DEQUEUE_LATER: - return; - case CCID_PACKET_DELAY: - sk_reset_timer(sk, &dp->dccps_xmit_timer, - jiffies + msecs_to_jiffies(rc)); - return; - case CCID_PACKET_SEND_AT_ONCE: - dccp_xmit_packet(sk); - break; - case CCID_PACKET_ERR: - dccp_qpolicy_drop(sk, skb); - dccp_pr_debug("packet discarded due to err=%d\n", rc); + if (sk->sk_state == DCCP_PARTOPEN) { + /* See 8.1.5. Handshake Completion */ + inet_csk_schedule_ack(sk); + inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, + inet_csk(sk)->icsk_rto, + DCCP_RTO_MAX); + dcb->dccpd_type = DCCP_PKT_DATAACK; + } else if (dccp_ack_pending(sk)) + dcb->dccpd_type = DCCP_PKT_DATAACK; + else + dcb->dccpd_type = DCCP_PKT_DATA; + + err = dccp_transmit_skb(sk, skb); + ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len); + if (err) + DCCP_BUG("err=%d after ccid_hc_tx_packet_sent", + err); + } else { + dccp_pr_debug("packet discarded due to err=%d\n", err); + kfree_skb(skb); } } } @@ -410,12 +339,10 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE; DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss; - /* Resolve feature dependencies resulting from choice of CCID */ - if (dccp_feat_server_ccid_dependencies(dreq)) - goto response_failed; - - if (dccp_insert_options_rsk(dreq, skb)) - goto response_failed; + if (dccp_insert_options_rsk(dreq, skb)) { + kfree_skb(skb); + return NULL; + } /* Build and checksum header */ dh = dccp_zeroed_hdr(skb, dccp_header_size); @@ -436,9 +363,6 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, inet_rsk(req)->acked = 1; DCCP_INC_STATS(DCCP_MIB_OUTSEGS); return skb; -response_failed: - kfree_skb(skb); - return NULL; } EXPORT_SYMBOL_GPL(dccp_make_response); @@ -523,9 +447,8 @@ int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code) /* * Do all connect socket setups that can be done AF independent. */ -int dccp_connect(struct sock *sk) +static inline void dccp_connect_init(struct sock *sk) { - struct sk_buff *skb; struct dccp_sock *dp = dccp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); struct inet_connection_sock *icsk = inet_csk(sk); @@ -535,13 +458,19 @@ int dccp_connect(struct sock *sk) dccp_sync_mss(sk, dst_mtu(dst)); - /* do not connect if feature negotiation setup fails */ - if (dccp_feat_finalise_settings(dccp_sk(sk))) - return -EPROTO; - /* Initialise GAR as per 8.5; AWL/AWH are set in dccp_transmit_skb() */ dp->dccps_gar = dp->dccps_iss; + icsk->icsk_retransmits = 0; +} + +int dccp_connect(struct sock *sk) +{ + struct sk_buff *skb; + struct inet_connection_sock *icsk = inet_csk(sk); + + dccp_connect_init(sk); + skb = alloc_skb(sk->sk_prot->max_header, sk->sk_allocation); if (unlikely(skb == NULL)) return -ENOBUFS; @@ -551,11 +480,11 @@ int dccp_connect(struct sock *sk) DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST; - dccp_transmit_skb(sk, dccp_skb_entail(sk, skb)); + dccp_skb_entail(sk, skb); + dccp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL)); DCCP_INC_STATS(DCCP_MIB_ACTIVEOPENS); /* Timer for repeating the REQUEST until an answer. */ - icsk->icsk_retransmits = 0; inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, DCCP_RTO_MAX); return 0; @@ -642,12 +571,6 @@ void dccp_send_sync(struct sock *sk, const u64 ackno, DCCP_SKB_CB(skb)->dccpd_type = pkt_type; DCCP_SKB_CB(skb)->dccpd_ack_seq = ackno; - /* - * Clear the flag in case the Sync was scheduled for out-of-band data, - * such as carrying a long Ack Vector. - */ - dccp_sk(sk)->dccps_sync_scheduled = 0; - dccp_transmit_skb(sk, skb); } @@ -676,7 +599,9 @@ void dccp_send_close(struct sock *sk, const int active) DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_CLOSE; if (active) { - skb = dccp_skb_entail(sk, skb); + dccp_write_xmit(sk, 1); + dccp_skb_entail(sk, skb); + dccp_transmit_skb(sk, skb_clone(skb, prio)); /* * Retransmission timer for active-close: RFC 4340, 8.3 requires * to retransmit the Close/CloseReq until the CLOSING/CLOSEREQ @@ -689,6 +614,6 @@ void dccp_send_close(struct sock *sk, const int active) */ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, DCCP_TIMEOUT_INIT, DCCP_RTO_MAX); - } - dccp_transmit_skb(sk, skb); + } else + dccp_transmit_skb(sk, skb); } diff --git a/net/dccp/probe.c b/net/dccp/probe.c index eaa59d82ab0f..81368a7f5379 100644 --- a/net/dccp/probe.c +++ b/net/dccp/probe.c @@ -46,54 +46,75 @@ static struct { struct kfifo *fifo; spinlock_t lock; wait_queue_head_t wait; - ktime_t start; + struct timespec tstart; } dccpw; -static void jdccp_write_xmit(struct sock *sk) +static void printl(const char *fmt, ...) { - const struct inet_sock *inet = inet_sk(sk); - struct ccid3_hc_tx_sock *hctx = NULL; - struct timespec tv; - char buf[256]; - int len, ccid = ccid_get_current_tx_ccid(dccp_sk(sk)); + va_list args; + int len; + struct timespec now; + char tbuf[256]; - if (ccid == DCCPC_CCID3) - hctx = ccid3_hc_tx_sk(sk); + va_start(args, fmt); + getnstimeofday(&now); - if (!port || ntohs(inet->dport) == port || ntohs(inet->sport) == port) { + now = timespec_sub(now, dccpw.tstart); - tv = ktime_to_timespec(ktime_sub(ktime_get(), dccpw.start)); - len = sprintf(buf, "%lu.%09lu %d.%d.%d.%d:%u %d.%d.%d.%d:%u %d", - (unsigned long)tv.tv_sec, - (unsigned long)tv.tv_nsec, - NIPQUAD(inet->saddr), ntohs(inet->sport), - NIPQUAD(inet->daddr), ntohs(inet->dport), ccid); + len = sprintf(tbuf, "%lu.%06lu ", + (unsigned long) now.tv_sec, + (unsigned long) now.tv_nsec / NSEC_PER_USEC); + len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args); + va_end(args); + kfifo_put(dccpw.fifo, tbuf, len); + wake_up(&dccpw.wait); +} + +static int jdccp_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + const struct dccp_minisock *dmsk = dccp_msk(sk); + const struct inet_sock *inet = inet_sk(sk); + const struct ccid3_hc_tx_sock *hctx; + + if (dmsk->dccpms_tx_ccid == DCCPC_CCID3) + hctx = ccid3_hc_tx_sk(sk); + else + hctx = NULL; + + if (port == 0 || ntohs(inet->dport) == port || + ntohs(inet->sport) == port) { if (hctx) - len += sprintf(buf + len, " %d %d %d %u %u %u %d", - hctx->s, hctx->rtt, hctx->p, hctx->x_calc, - (unsigned)(hctx->x_recv >> 6), - (unsigned)(hctx->x >> 6), hctx->t_ipi); - - len += sprintf(buf + len, "\n"); - kfifo_put(dccpw.fifo, buf, len); - wake_up(&dccpw.wait); + printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %u " + "%llu %llu %d\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), size, + hctx->ccid3hctx_s, hctx->ccid3hctx_rtt, + hctx->ccid3hctx_p, hctx->ccid3hctx_x_calc, + hctx->ccid3hctx_x_recv >> 6, + hctx->ccid3hctx_x >> 6, hctx->ccid3hctx_t_ipi); + else + printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), size); } jprobe_return(); + return 0; } static struct jprobe dccp_send_probe = { .kp = { - .symbol_name = "dccp_write_xmit", + .symbol_name = "dccp_sendmsg", }, - .entry = jdccp_write_xmit, + .entry = jdccp_sendmsg, }; static int dccpprobe_open(struct inode *inode, struct file *file) { kfifo_reset(dccpw.fifo); - dccpw.start = ktime_get(); + getnstimeofday(&dccpw.tstart); return 0; } diff --git a/net/dccp/proto.c b/net/dccp/proto.c index ecf3be961e11..d0bd34819761 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -67,9 +67,6 @@ void dccp_set_state(struct sock *sk, const int state) case DCCP_OPEN: if (oldstate != DCCP_OPEN) DCCP_INC_STATS(DCCP_MIB_CURRESTAB); - /* Client retransmits all Confirm options until entering OPEN */ - if (oldstate == DCCP_PARTOPEN) - dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg); break; case DCCP_CLOSED: @@ -178,25 +175,63 @@ EXPORT_SYMBOL_GPL(dccp_state_name); int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) { struct dccp_sock *dp = dccp_sk(sk); + struct dccp_minisock *dmsk = dccp_msk(sk); struct inet_connection_sock *icsk = inet_csk(sk); + dccp_minisock_init(&dp->dccps_minisock); + icsk->icsk_rto = DCCP_TIMEOUT_INIT; icsk->icsk_syn_retries = sysctl_dccp_request_retries; sk->sk_state = DCCP_CLOSED; sk->sk_write_space = dccp_write_space; icsk->icsk_sync_mss = dccp_sync_mss; - dp->dccps_mss_cache = TCP_MIN_RCVMSS; + dp->dccps_mss_cache = 536; dp->dccps_rate_last = jiffies; dp->dccps_role = DCCP_ROLE_UNDEFINED; dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; - dp->dccps_tx_qlen = sysctl_dccp_tx_qlen; + dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1; dccp_init_xmit_timers(sk); - INIT_LIST_HEAD(&dp->dccps_featneg); - /* control socket doesn't need feat nego */ - if (likely(ctl_sock_initialized)) - return dccp_feat_init(sk); + /* + * FIXME: We're hardcoding the CCID, and doing this at this point makes + * the listening (master) sock get CCID control blocks, which is not + * necessary, but for now, to not mess with the test userspace apps, + * lets leave it here, later the real solution is to do this in a + * setsockopt(CCIDs-I-want/accept). -acme + */ + if (likely(ctl_sock_initialized)) { + int rc = dccp_feat_init(dmsk); + + if (rc) + return rc; + + if (dmsk->dccpms_send_ack_vector) { + dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL); + if (dp->dccps_hc_rx_ackvec == NULL) + return -ENOMEM; + } + dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid, + sk, GFP_KERNEL); + dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid, + sk, GFP_KERNEL); + if (unlikely(dp->dccps_hc_rx_ccid == NULL || + dp->dccps_hc_tx_ccid == NULL)) { + ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); + ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); + if (dmsk->dccpms_send_ack_vector) { + dccp_ackvec_free(dp->dccps_hc_rx_ackvec); + dp->dccps_hc_rx_ackvec = NULL; + } + dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; + return -ENOMEM; + } + } else { + /* control socket doesn't need feat nego */ + INIT_LIST_HEAD(&dmsk->dccpms_pending); + INIT_LIST_HEAD(&dmsk->dccpms_conf); + } + return 0; } @@ -205,6 +240,7 @@ EXPORT_SYMBOL_GPL(dccp_init_sock); void dccp_destroy_sock(struct sock *sk) { struct dccp_sock *dp = dccp_sk(sk); + struct dccp_minisock *dmsk = dccp_msk(sk); /* * DCCP doesn't use sk_write_queue, just sk_send_head @@ -222,7 +258,7 @@ void dccp_destroy_sock(struct sock *sk) kfree(dp->dccps_service_list); dp->dccps_service_list = NULL; - if (dp->dccps_hc_rx_ackvec != NULL) { + if (dmsk->dccpms_send_ack_vector) { dccp_ackvec_free(dp->dccps_hc_rx_ackvec); dp->dccps_hc_rx_ackvec = NULL; } @@ -231,7 +267,7 @@ void dccp_destroy_sock(struct sock *sk) dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; /* clean up feature negotiation state */ - dccp_feat_list_purge(&dp->dccps_featneg); + dccp_feat_clean(dmsk); } EXPORT_SYMBOL_GPL(dccp_destroy_sock); @@ -241,9 +277,6 @@ static inline int dccp_listen_start(struct sock *sk, int backlog) struct dccp_sock *dp = dccp_sk(sk); dp->dccps_role = DCCP_ROLE_LISTEN; - /* do not start to listen if feature negotiation setup fails */ - if (dccp_feat_finalise_settings(dp)) - return -EPROTO; return inet_csk_listen_start(sk, backlog); } @@ -433,70 +466,42 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, return 0; } -static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) +/* byte 1 is feature. the rest is the preference list */ +static int dccp_setsockopt_change(struct sock *sk, int type, + struct dccp_so_feat __user *optval) { - u8 *list, len; - int i, rc; + struct dccp_so_feat opt; + u8 *val; + int rc; - if (cscov < 0 || cscov > 15) - return -EINVAL; + if (copy_from_user(&opt, optval, sizeof(opt))) + return -EFAULT; /* - * Populate a list of permissible values, in the range cscov...15. This - * is necessary since feature negotiation of single values only works if - * both sides incidentally choose the same value. Since the list starts - * lowest-value first, negotiation will pick the smallest shared value. + * rfc4340: 6.1. Change Options */ - if (cscov == 0) - return 0; - len = 16 - cscov; - - list = kmalloc(len, GFP_KERNEL); - if (list == NULL) - return -ENOBUFS; - - for (i = 0; i < len; i++) - list[i] = cscov++; - - rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); - - if (rc == 0) { - if (rx) - dccp_sk(sk)->dccps_pcrlen = cscov; - else - dccp_sk(sk)->dccps_pcslen = cscov; - } - kfree(list); - return rc; -} - -static int dccp_setsockopt_ccid(struct sock *sk, int type, - char __user *optval, int optlen) -{ - u8 *val; - int rc = 0; - - if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) + if (opt.dccpsf_len < 1) return -EINVAL; - val = kmalloc(optlen, GFP_KERNEL); - if (val == NULL) + val = kmalloc(opt.dccpsf_len, GFP_KERNEL); + if (!val) return -ENOMEM; - if (copy_from_user(val, optval, optlen)) { - kfree(val); - return -EFAULT; + if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { + rc = -EFAULT; + goto out_free_val; } - lock_sock(sk); - if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID) - rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen); + rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat, + val, opt.dccpsf_len, GFP_KERNEL); + if (rc) + goto out_free_val; - if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID)) - rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen); - release_sock(sk); +out: + return rc; +out_free_val: kfree(val); - return rc; + goto out; } static int do_dccp_setsockopt(struct sock *sk, int level, int optname, @@ -505,21 +510,7 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, struct dccp_sock *dp = dccp_sk(sk); int val, err = 0; - switch (optname) { - case DCCP_SOCKOPT_PACKET_SIZE: - DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); - return 0; - case DCCP_SOCKOPT_CHANGE_L: - case DCCP_SOCKOPT_CHANGE_R: - DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); - return 0; - case DCCP_SOCKOPT_CCID: - case DCCP_SOCKOPT_RX_CCID: - case DCCP_SOCKOPT_TX_CCID: - return dccp_setsockopt_ccid(sk, optname, optval, optlen); - } - - if (optlen < (int)sizeof(int)) + if (optlen < sizeof(int)) return -EINVAL; if (get_user(val, (int __user *)optval)) @@ -530,38 +521,53 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, lock_sock(sk); switch (optname) { + case DCCP_SOCKOPT_PACKET_SIZE: + DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); + err = 0; + break; + case DCCP_SOCKOPT_CHANGE_L: + if (optlen != sizeof(struct dccp_so_feat)) + err = -EINVAL; + else + err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L, + (struct dccp_so_feat __user *) + optval); + break; + case DCCP_SOCKOPT_CHANGE_R: + if (optlen != sizeof(struct dccp_so_feat)) + err = -EINVAL; + else + err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R, + (struct dccp_so_feat __user *) + optval); + break; case DCCP_SOCKOPT_SERVER_TIMEWAIT: if (dp->dccps_role != DCCP_ROLE_SERVER) err = -EOPNOTSUPP; else dp->dccps_server_timewait = (val != 0); break; - case DCCP_SOCKOPT_SEND_CSCOV: - err = dccp_setsockopt_cscov(sk, val, false); - break; - case DCCP_SOCKOPT_RECV_CSCOV: - err = dccp_setsockopt_cscov(sk, val, true); - break; - case DCCP_SOCKOPT_QPOLICY_ID: - if (sk->sk_state != DCCP_CLOSED) - err = -EISCONN; - else if (val < 0 || val >= DCCPQ_POLICY_MAX) + case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */ + if (val < 0 || val > 15) err = -EINVAL; else - dp->dccps_qpolicy = val; + dp->dccps_pcslen = val; break; - case DCCP_SOCKOPT_QPOLICY_TXQLEN: - if (val < 0) + case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */ + if (val < 0 || val > 15) err = -EINVAL; - else - dp->dccps_tx_qlen = val; + else { + dp->dccps_pcrlen = val; + /* FIXME: add feature negotiation, + * ChangeL(MinimumChecksumCoverage, val) */ + } break; default: err = -ENOPROTOOPT; break; } - release_sock(sk); + release_sock(sk); return err; } @@ -642,18 +648,6 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, case DCCP_SOCKOPT_GET_CUR_MPS: val = dp->dccps_mss_cache; break; - case DCCP_SOCKOPT_AVAILABLE_CCIDS: - return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); - case DCCP_SOCKOPT_TX_CCID: - val = ccid_get_current_tx_ccid(dp); - if (val < 0) - return -ENOPROTOOPT; - break; - case DCCP_SOCKOPT_RX_CCID: - val = ccid_get_current_rx_ccid(dp); - if (val < 0) - return -ENOPROTOOPT; - break; case DCCP_SOCKOPT_SERVER_TIMEWAIT: val = dp->dccps_server_timewait; break; @@ -663,12 +657,6 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, case DCCP_SOCKOPT_RECV_CSCOV: val = dp->dccps_pcrlen; break; - case DCCP_SOCKOPT_QPOLICY_ID: - val = dp->dccps_qpolicy; - break; - case DCCP_SOCKOPT_QPOLICY_TXQLEN: - val = dp->dccps_tx_qlen; - break; case 128 ... 191: return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, len, (u32 __user *)optval, optlen); @@ -711,47 +699,6 @@ int compat_dccp_getsockopt(struct sock *sk, int level, int optname, EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); #endif -static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) -{ - struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); - - /* - * Assign an (opaque) qpolicy priority value to skb->priority. - * - * We are overloading this skb field for use with the qpolicy subystem. - * The skb->priority is normally used for the SO_PRIORITY option, which - * is initialised from sk_priority. Since the assignment of sk_priority - * to skb->priority happens later (on layer 3), we overload this field - * for use with queueing priorities as long as the skb is on layer 4. - * The default priority value (if nothing is set) is 0. - */ - skb->priority = 0; - - for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { - - if (!CMSG_OK(msg, cmsg)) - return -EINVAL; - - if (cmsg->cmsg_level != SOL_DCCP) - continue; - - if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX && - !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type)) - return -EINVAL; - - switch (cmsg->cmsg_type) { - case DCCP_SCM_PRIORITY: - if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32))) - return -EINVAL; - skb->priority = *(__u32 *)CMSG_DATA(cmsg); - break; - default: - return -EINVAL; - } - } - return 0; -} - int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { @@ -767,7 +714,8 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, lock_sock(sk); - if (dccp_qpolicy_full(sk)) { + if (sysctl_dccp_tx_qlen && + (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) { rc = -EAGAIN; goto out_release; } @@ -795,12 +743,8 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (rc != 0) goto out_discard; - rc = dccp_msghdr_parse(msg, skb); - if (rc != 0) - goto out_discard; - - dccp_qpolicy_push(sk, skb); - dccp_write_xmit(sk); + skb_queue_tail(&sk->sk_write_queue, skb); + dccp_write_xmit(sk,0); out_release: release_sock(sk); return rc ? : len; @@ -1023,22 +967,9 @@ void dccp_close(struct sock *sk, long timeout) /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); } else if (sk->sk_state != DCCP_CLOSED) { - /* - * Normal connection termination. May need to wait if there are - * still packets in the TX queue that are delayed by the CCID. - */ - dccp_flush_write_queue(sk, &timeout); dccp_terminate_connection(sk); } - /* - * Flush write queue. This may be necessary in several cases: - * - we have been closed by the peer but still have application data; - * - abortive termination (unread data or zero linger time), - * - normal termination but queue could not be flushed within time limit - */ - __skb_queue_purge(&sk->sk_write_queue); - sk_stream_wait_close(sk, timeout); adjudge_to_death: diff --git a/net/dccp/qpolicy.c b/net/dccp/qpolicy.c deleted file mode 100644 index 27383f88c75f..000000000000 --- a/net/dccp/qpolicy.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * net/dccp/qpolicy.c - * - * Policy-based packet dequeueing interface for DCCP. - * - * Copyright (c) 2008 Tomasz Grobelny - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License v2 - * as published by the Free Software Foundation. - */ -#include "dccp.h" - -/* - * Simple Dequeueing Policy: - * If tx_qlen is different from 0, enqueue up to tx_qlen elements. - */ -static void qpolicy_simple_push(struct sock *sk, struct sk_buff *skb) -{ - skb_queue_tail(&sk->sk_write_queue, skb); -} - -static bool qpolicy_simple_full(struct sock *sk) -{ - return dccp_sk(sk)->dccps_tx_qlen && - sk->sk_write_queue.qlen >= dccp_sk(sk)->dccps_tx_qlen; -} - -static struct sk_buff *qpolicy_simple_top(struct sock *sk) -{ - return skb_peek(&sk->sk_write_queue); -} - -/* - * Priority-based Dequeueing Policy: - * If tx_qlen is different from 0 and the queue has reached its upper bound - * of tx_qlen elements, replace older packets lowest-priority-first. - */ -static struct sk_buff *qpolicy_prio_best_skb(struct sock *sk) -{ - struct sk_buff *skb, *best = NULL; - - skb_queue_walk(&sk->sk_write_queue, skb) - if (best == NULL || skb->priority > best->priority) - best = skb; - return best; -} - -static struct sk_buff *qpolicy_prio_worst_skb(struct sock *sk) -{ - struct sk_buff *skb, *worst = NULL; - - skb_queue_walk(&sk->sk_write_queue, skb) - if (worst == NULL || skb->priority < worst->priority) - worst = skb; - return worst; -} - -static bool qpolicy_prio_full(struct sock *sk) -{ - if (qpolicy_simple_full(sk)) - dccp_qpolicy_drop(sk, qpolicy_prio_worst_skb(sk)); - return false; -} - -/** - * struct dccp_qpolicy_operations - TX Packet Dequeueing Interface - * @push: add a new @skb to the write queue - * @full: indicates that no more packets will be admitted - * @top: peeks at whatever the queueing policy defines as its `top' - */ -static struct dccp_qpolicy_operations { - void (*push) (struct sock *sk, struct sk_buff *skb); - bool (*full) (struct sock *sk); - struct sk_buff* (*top) (struct sock *sk); - __be32 params; - -} qpol_table[DCCPQ_POLICY_MAX] = { - [DCCPQ_POLICY_SIMPLE] = { - .push = qpolicy_simple_push, - .full = qpolicy_simple_full, - .top = qpolicy_simple_top, - .params = 0, - }, - [DCCPQ_POLICY_PRIO] = { - .push = qpolicy_simple_push, - .full = qpolicy_prio_full, - .top = qpolicy_prio_best_skb, - .params = DCCP_SCM_PRIORITY, - }, -}; - -/* - * Externally visible interface - */ -void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb) -{ - qpol_table[dccp_sk(sk)->dccps_qpolicy].push(sk, skb); -} - -bool dccp_qpolicy_full(struct sock *sk) -{ - return qpol_table[dccp_sk(sk)->dccps_qpolicy].full(sk); -} - -void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb) -{ - if (skb != NULL) { - skb_unlink(skb, &sk->sk_write_queue); - kfree_skb(skb); - } -} - -struct sk_buff *dccp_qpolicy_top(struct sock *sk) -{ - return qpol_table[dccp_sk(sk)->dccps_qpolicy].top(sk); -} - -struct sk_buff *dccp_qpolicy_pop(struct sock *sk) -{ - struct sk_buff *skb = dccp_qpolicy_top(sk); - - /* Clear any skb fields that we used internally */ - skb->priority = 0; - - if (skb) - skb_unlink(skb, &sk->sk_write_queue); - return skb; -} - -bool dccp_qpolicy_param_ok(struct sock *sk, __be32 param) -{ - /* check if exactly one bit is set */ - if (!param || (param & (param - 1))) - return false; - return (qpol_table[dccp_sk(sk)->dccps_qpolicy].params & param) == param; -} diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index a5a1856234e7..21295993fdb8 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -18,72 +18,76 @@ #error This file should not be compiled without CONFIG_SYSCTL defined #endif -/* Boundary values */ -static int zero = 0, - u8_max = 0xFF; -static unsigned long seqw_min = 32; - static struct ctl_table dccp_default_table[] = { { .procname = "seq_window", - .data = &sysctl_dccp_sequence_window, - .maxlen = sizeof(sysctl_dccp_sequence_window), + .data = &sysctl_dccp_feat_sequence_window, + .maxlen = sizeof(sysctl_dccp_feat_sequence_window), .mode = 0644, - .proc_handler = proc_doulongvec_minmax, - .extra1 = &seqw_min, /* RFC 4340, 7.5.2 */ + .proc_handler = proc_dointvec, }, { .procname = "rx_ccid", - .data = &sysctl_dccp_rx_ccid, - .maxlen = sizeof(sysctl_dccp_rx_ccid), + .data = &sysctl_dccp_feat_rx_ccid, + .maxlen = sizeof(sysctl_dccp_feat_rx_ccid), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &u8_max, /* RFC 4340, 10. */ + .proc_handler = proc_dointvec, }, { .procname = "tx_ccid", - .data = &sysctl_dccp_tx_ccid, - .maxlen = sizeof(sysctl_dccp_tx_ccid), + .data = &sysctl_dccp_feat_tx_ccid, + .maxlen = sizeof(sysctl_dccp_feat_tx_ccid), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "ack_ratio", + .data = &sysctl_dccp_feat_ack_ratio, + .maxlen = sizeof(sysctl_dccp_feat_ack_ratio), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "send_ackvec", + .data = &sysctl_dccp_feat_send_ack_vector, + .maxlen = sizeof(sysctl_dccp_feat_send_ack_vector), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "send_ndp", + .data = &sysctl_dccp_feat_send_ndp_count, + .maxlen = sizeof(sysctl_dccp_feat_send_ndp_count), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &u8_max, /* RFC 4340, 10. */ + .proc_handler = proc_dointvec, }, { .procname = "request_retries", .data = &sysctl_dccp_request_retries, .maxlen = sizeof(sysctl_dccp_request_retries), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &u8_max, + .proc_handler = proc_dointvec, }, { .procname = "retries1", .data = &sysctl_dccp_retries1, .maxlen = sizeof(sysctl_dccp_retries1), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &u8_max, + .proc_handler = proc_dointvec, }, { .procname = "retries2", .data = &sysctl_dccp_retries2, .maxlen = sizeof(sysctl_dccp_retries2), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &u8_max, + .proc_handler = proc_dointvec, }, { .procname = "tx_qlen", .data = &sysctl_dccp_tx_qlen, .maxlen = sizeof(sysctl_dccp_tx_qlen), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, + .proc_handler = proc_dointvec, }, { .procname = "sync_ratelimit", diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 16359e29e7f5..54b3c7e9e016 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -87,6 +87,17 @@ static void dccp_retransmit_timer(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); + /* retransmit timer is used for feature negotiation throughout + * connection. In this case, no packet is re-transmitted, but rather an + * ack is generated and pending changes are placed into its options. + */ + if (sk->sk_send_head == NULL) { + dccp_pr_debug("feat negotiation retransmit timeout %p\n", sk); + if (sk->sk_state == DCCP_OPEN) + dccp_send_ack(sk); + goto backoff; + } + /* * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was * sent, no need to retransmit, this sock is dead. @@ -115,6 +126,7 @@ static void dccp_retransmit_timer(struct sock *sk) return; } +backoff: icsk->icsk_backoff++; icsk->icsk_rto = min(icsk->icsk_rto << 1, DCCP_RTO_MAX); @@ -237,35 +249,32 @@ out: sock_put(sk); } -/** - * dccp_write_xmitlet - Workhorse for CCID packet dequeueing interface - * See the comments above %ccid_dequeueing_decision for supported modes. - */ -static void dccp_write_xmitlet(unsigned long data) +/* Transmit-delay timer: used by the CCIDs to delay actual send time */ +static void dccp_write_xmit_timer(unsigned long data) { struct sock *sk = (struct sock *)data; + struct dccp_sock *dp = dccp_sk(sk); bh_lock_sock(sk); if (sock_owned_by_user(sk)) - sk_reset_timer(sk, &dccp_sk(sk)->dccps_xmit_timer, jiffies + 1); + sk_reset_timer(sk, &dp->dccps_xmit_timer, jiffies+1); else - dccp_write_xmit(sk); + dccp_write_xmit(sk, 0); bh_unlock_sock(sk); + sock_put(sk); } -static void dccp_write_xmit_timer(unsigned long data) +static void dccp_init_write_xmit_timer(struct sock *sk) { - dccp_write_xmitlet(data); - sock_put((struct sock *)data); + struct dccp_sock *dp = dccp_sk(sk); + + setup_timer(&dp->dccps_xmit_timer, dccp_write_xmit_timer, + (unsigned long)sk); } void dccp_init_xmit_timers(struct sock *sk) { - struct dccp_sock *dp = dccp_sk(sk); - - tasklet_init(&dp->dccps_xmitlet, dccp_write_xmitlet, (unsigned long)sk); - setup_timer(&dp->dccps_xmit_timer, dccp_write_xmit_timer, - (unsigned long)sk); + dccp_init_write_xmit_timer(sk); inet_csk_init_xmit_timers(sk, &dccp_write_timer, &dccp_delack_timer, &dccp_keepalive_timer); } @@ -281,7 +290,8 @@ u32 dccp_timestamp(void) { s64 delta = ktime_us_delta(ktime_get_real(), dccp_timestamp_seed); - return div_u64(delta, DCCP_TIME_RESOLUTION); + do_div(delta, 10); + return delta; } EXPORT_SYMBOL_GPL(dccp_timestamp); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 9da9f19ece8a..f79a51607292 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -811,12 +811,25 @@ void tcp_update_metrics(struct sock *sk) } } +/* Numbers are taken from RFC3390. + * + * John Heffner states: + * + * The RFC specifies a window of no more than 4380 bytes + * unless 2*MSS > 4380. Reading the pseudocode in the RFC + * is a bit misleading because they use a clamp at 4380 bytes + * rather than use a multiplier in the relevant range. + */ __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst) { __u32 cwnd = (dst ? dst_metric(dst, RTAX_INITCWND) : 0); - if (!cwnd) - cwnd = rfc3390_bytes_to_packets(tp->mss_cache); + if (!cwnd) { + if (tp->mss_cache > 1460) + cwnd = 2; + else + cwnd = (tp->mss_cache > 1095) ? 3 : 4; + } return min_t(__u32, cwnd, tp->snd_cwnd_clamp); } -- cgit v1.2.3 From fb683f1627745e937ef199edd3428ac4b2ef1e08 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Aug 2008 16:36:28 +0200 Subject: Export smc91x led definitions Now that we can configure smc91x leds from its platform data, it seems rather useful to move the led definitions to the externally visible header file. Signed-off-by: Marc Zyngier --- drivers/net/smc91x.h | 8 -------- include/linux/smc91x.h | 9 +++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 8322e7f37af5..bcbd645d88ce 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -778,14 +778,6 @@ smc_pxa_dma_irq(int dma, void *dummy) #define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode #define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb #define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb -#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect -#define RPC_LED_RES (0x01) // LED = Reserved -#define RPC_LED_10 (0x02) // LED = 10Mbps link detect -#define RPC_LED_FD (0x03) // LED = Full Duplex Mode -#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred -#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect -#define RPC_LED_TX (0x06) // LED = TX packet occurred -#define RPC_LED_RX (0x07) // LED = RX packet occurred #ifndef RPC_LSA_DEFAULT #define RPC_LSA_DEFAULT RPC_LED_100 diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index ed25483d25d9..bc21db598c06 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -16,6 +16,15 @@ #define SMC91X_USE_DMA (1 << 6) +#define RPC_LED_100_10 (0x00) /* LED = 100Mbps OR's with 10Mbps link detect */ +#define RPC_LED_RES (0x01) /* LED = Reserved */ +#define RPC_LED_10 (0x02) /* LED = 10Mbps link detect */ +#define RPC_LED_FD (0x03) /* LED = Full Duplex Mode */ +#define RPC_LED_TX_RX (0x04) /* LED = TX or RX packet occurred */ +#define RPC_LED_100 (0x05) /* LED = 100Mbps link dectect */ +#define RPC_LED_TX (0x06) /* LED = TX packet occurred */ +#define RPC_LED_RX (0x07) /* LED = RX packet occurred */ + struct smc91x_platdata { unsigned long flags; unsigned char leda; -- cgit v1.2.3 From 636dc67cbf8c481a996faf6c23f0532d0f02ebad Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Wed, 10 Sep 2008 01:06:46 +0900 Subject: add is_buffer_dma_capable helper function is_buffer_dma_capable helper function is to see if a memory region is DMA-capable or not. The arugments are the dma_mask (or coherent_dma_mask) of a device and the address and size of a memory region. Signed-off-by: FUJITA Tomonori Acked-by: Joerg Roedel Signed-off-by: Ingo Molnar --- include/linux/dma-mapping.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 952e0f857ac9..6ed50c1642f1 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -48,6 +48,11 @@ static inline int is_device_dma_capable(struct device *dev) return dev->dma_mask != NULL && *dev->dma_mask != DMA_MASK_NONE; } +static inline int is_buffer_dma_capable(u64 mask, dma_addr_t addr, size_t size) +{ + return addr + size <= mask; +} + #ifdef CONFIG_HAS_DMA #include #else -- cgit v1.2.3 From f7d0b926ac8c8ec0c7a83ee69409bd2e6bb39f81 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 9 Sep 2008 15:43:22 -0700 Subject: mm: define USE_SPLIT_PTLOCKS rather than repeating expression Define USE_SPLIT_PTLOCKS as a constant expression rather than repeating "NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS" all over the place. Signed-off-by: Jeremy Fitzhardinge Acked-by: Hugh Dickins Signed-off-by: Ingo Molnar --- arch/x86/xen/mmu.c | 2 +- include/linux/mm.h | 6 +++--- include/linux/mm_types.h | 10 ++++++---- include/linux/sched.h | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index aa37469da696..2e1b64088490 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -646,7 +646,7 @@ static spinlock_t *lock_pte(struct page *page) { spinlock_t *ptl = NULL; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS ptl = __pte_lockptr(page); spin_lock(ptl); #endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 72a15dc26bbf..4194bf8e4f6c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -919,7 +919,7 @@ static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long a } #endif /* CONFIG_MMU && !__ARCH_HAS_4LEVEL_HACK */ -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS /* * We tuck a spinlock to guard each pagetable page into its struct page, * at page->private, with BUILD_BUG_ON to make sure that this will not @@ -932,14 +932,14 @@ static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long a } while (0) #define pte_lock_deinit(page) ((page)->mapping = NULL) #define pte_lockptr(mm, pmd) ({(void)(mm); __pte_lockptr(pmd_page(*(pmd)));}) -#else +#else /* !USE_SPLIT_PTLOCKS */ /* * We use mm->page_table_lock to guard all pagetable pages of the mm. */ #define pte_lock_init(page) do {} while (0) #define pte_lock_deinit(page) do {} while (0) #define pte_lockptr(mm, pmd) ({(void)(pmd); &(mm)->page_table_lock;}) -#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#endif /* USE_SPLIT_PTLOCKS */ static inline void pgtable_page_ctor(struct page *page) { diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index bf334138c7c1..9d49fa36bbef 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -21,11 +21,13 @@ struct address_space; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#define USE_SPLIT_PTLOCKS (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS) + +#if USE_SPLIT_PTLOCKS typedef atomic_long_t mm_counter_t; -#else /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#else /* !USE_SPLIT_PTLOCKS */ typedef unsigned long mm_counter_t; -#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#endif /* !USE_SPLIT_PTLOCKS */ /* * Each physical page in the system has a struct page associated with @@ -65,7 +67,7 @@ struct page { * see PAGE_MAPPING_ANON below. */ }; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS spinlock_t ptl; #endif struct kmem_cache *slab; /* SLUB: Pointer to slab */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 3d9120c5ad15..272c35309df2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -352,7 +352,7 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr, extern void arch_unmap_area(struct mm_struct *, unsigned long); extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS /* * The mm counters are not protected by its page_table_lock, * so must be incremented atomically. @@ -363,7 +363,7 @@ extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); #define inc_mm_counter(mm, member) atomic_long_inc(&(mm)->_##member) #define dec_mm_counter(mm, member) atomic_long_dec(&(mm)->_##member) -#else /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#else /* !USE_SPLIT_PTLOCKS */ /* * The mm counters are protected by its page_table_lock, * so can be incremented directly. @@ -374,7 +374,7 @@ extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); #define inc_mm_counter(mm, member) (mm)->_##member++ #define dec_mm_counter(mm, member) (mm)->_##member-- -#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#endif /* !USE_SPLIT_PTLOCKS */ #define get_mm_rss(mm) \ (get_mm_counter(mm, file_rss) + get_mm_counter(mm, anon_rss)) -- cgit v1.2.3 From 271bff7afbb2cbaa81e744006ad2fff1f3e10b1b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 11 Sep 2008 04:48:58 -0700 Subject: net: Add DMA mapping tokens to skb_shared_info. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 909923717830..4b2be23903c4 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -146,8 +146,14 @@ struct skb_shared_info { unsigned short gso_segs; unsigned short gso_type; __be32 ip6_frag_id; +#ifdef CONFIG_HAS_DMA + unsigned int num_dma_maps; +#endif struct sk_buff *frag_list; skb_frag_t frags[MAX_SKB_FRAGS]; +#ifdef CONFIG_HAS_DMA + dma_addr_t dma_maps[MAX_SKB_FRAGS + 1]; +#endif }; /* We divide dataref into two halves. The higher 16 bits hold references -- cgit v1.2.3 From a40c24a13366e324bc0ff8c3bb107db89312c984 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 11 Sep 2008 04:51:14 -0700 Subject: net: Add SKB DMA mapping helper functions. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 8 ++++++ net/core/Makefile | 1 + net/core/skb_dma_map.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 net/core/skb_dma_map.c (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4b2be23903c4..aa80ad9cbc88 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -359,6 +359,14 @@ struct sk_buff { #include +#ifdef CONFIG_HAS_DMA +#include +extern int skb_dma_map(struct device *dev, struct sk_buff *skb, + enum dma_data_direction dir); +extern void skb_dma_unmap(struct device *dev, struct sk_buff *skb, + enum dma_data_direction dir); +#endif + extern void kfree_skb(struct sk_buff *skb); extern void __kfree_skb(struct sk_buff *skb); extern struct sk_buff *__alloc_skb(unsigned int size, diff --git a/net/core/Makefile b/net/core/Makefile index b1332f6d0042..26a37cb31923 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -6,6 +6,7 @@ obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \ gen_stats.o gen_estimator.o net_namespace.o obj-$(CONFIG_SYSCTL) += sysctl_net_core.o +obj-$(CONFIG_HAS_DMA) += skb_dma_map.o obj-y += dev.o ethtool.o dev_mcast.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o diff --git a/net/core/skb_dma_map.c b/net/core/skb_dma_map.c new file mode 100644 index 000000000000..1f49afcd8e86 --- /dev/null +++ b/net/core/skb_dma_map.c @@ -0,0 +1,66 @@ +/* skb_dma_map.c: DMA mapping helpers for socket buffers. + * + * Copyright (C) David S. Miller + */ + +#include +#include +#include +#include + +int skb_dma_map(struct device *dev, struct sk_buff *skb, + enum dma_data_direction dir) +{ + struct skb_shared_info *sp = skb_shinfo(skb); + dma_addr_t map; + int i; + + map = dma_map_single(dev, skb->data, + skb_headlen(skb), dir); + if (dma_mapping_error(dev, map)) + goto out_err; + + sp->dma_maps[0] = map; + for (i = 0; i < sp->nr_frags; i++) { + skb_frag_t *fp = &sp->frags[i]; + + map = dma_map_page(dev, fp->page, fp->page_offset, + fp->size, dir); + if (dma_mapping_error(dev, map)) + goto unwind; + sp->dma_maps[i + 1] = map; + } + sp->num_dma_maps = i + 1; + + return 0; + +unwind: + while (i-- >= 0) { + skb_frag_t *fp = &sp->frags[i]; + + dma_unmap_page(dev, sp->dma_maps[i + 1], + fp->size, dir); + } + dma_unmap_single(dev, sp->dma_maps[0], + skb_headlen(skb), dir); +out_err: + return -ENOMEM; +} +EXPORT_SYMBOL(skb_dma_map); + +void skb_dma_unmap(struct device *dev, struct sk_buff *skb, + enum dma_data_direction dir) +{ + struct skb_shared_info *sp = skb_shinfo(skb); + int i; + + dma_unmap_single(dev, sp->dma_maps[0], + skb_headlen(skb), dir); + for (i = 0; i < sp->nr_frags; i++) { + skb_frag_t *fp = &sp->frags[i]; + + dma_unmap_page(dev, sp->dma_maps[i + 1], + fp->size, dir); + } +} +EXPORT_SYMBOL(skb_dma_unmap); -- cgit v1.2.3 From 2dc75d3c3b49c64fd26b4832a7efb75546cb3fc5 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 11 Sep 2008 14:20:23 +0200 Subject: block: disable sysfs parts of the disk command filter We still have life time issues with the sysfs command filter kobject, so disable it for 2.6.27 release. We can revisit this and make it work properly for 2.6.28, for 2.6.27 release it's too risky. Signed-off-by: Jens Axboe --- block/cmd-filter.c | 2 ++ drivers/ide/ide-cd.c | 2 -- drivers/scsi/sd.c | 2 -- drivers/scsi/sr.c | 2 -- include/linux/blkdev.h | 2 -- 5 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/block/cmd-filter.c b/block/cmd-filter.c index 228b6447e89f..79c14996ac11 100644 --- a/block/cmd-filter.c +++ b/block/cmd-filter.c @@ -49,6 +49,7 @@ int blk_verify_command(struct blk_cmd_filter *filter, } EXPORT_SYMBOL(blk_verify_command); +#if 0 /* and now, the sysfs stuff */ static ssize_t rcf_cmds_show(struct blk_cmd_filter *filter, char *page, int rw) @@ -233,3 +234,4 @@ void blk_unregister_filter(struct gendisk *disk) kobject_put(disk->holder_dir->parent); } EXPORT_SYMBOL(blk_unregister_filter); +#endif diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index f1489999cf91..49a8c589e346 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1933,7 +1933,6 @@ static void ide_cd_remove(ide_drive_t *drive) ide_proc_unregister_driver(drive, info->driver); - blk_unregister_filter(info->disk); del_gendisk(info->disk); ide_cd_put(info); @@ -2159,7 +2158,6 @@ static int ide_cd_probe(ide_drive_t *drive) g->fops = &idecd_ops; g->flags |= GENHD_FL_REMOVABLE; add_disk(g); - blk_register_filter(g); return 0; out_free_cd: diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 2a2bc89aba83..e5e7d7856454 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1875,7 +1875,6 @@ static int sd_probe(struct device *dev) dev_set_drvdata(dev, sdkp); add_disk(gd); - blk_register_filter(gd); sd_dif_config_host(sdkp); sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", @@ -1909,7 +1908,6 @@ static int sd_remove(struct device *dev) struct scsi_disk *sdkp = dev_get_drvdata(dev); device_del(&sdkp->dev); - blk_unregister_filter(sdkp->disk); del_gendisk(sdkp->disk); sd_shutdown(dev); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 3292965bfd84..27f5bfd1def3 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -656,7 +656,6 @@ static int sr_probe(struct device *dev) dev_set_drvdata(dev, cd); disk->flags |= GENHD_FL_REMOVABLE; add_disk(disk); - blk_register_filter(disk); sdev_printk(KERN_DEBUG, sdev, "Attached scsi CD-ROM %s\n", cd->cdi.name); @@ -895,7 +894,6 @@ static int sr_remove(struct device *dev) { struct scsi_cd *cd = dev_get_drvdata(dev); - blk_unregister_filter(cd->disk); del_gendisk(cd->disk); mutex_lock(&sr_ref_mutex); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 44710d7e7bff..53ea933cf60b 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -843,8 +843,6 @@ extern int blkdev_issue_flush(struct block_device *, sector_t *); */ extern int blk_verify_command(struct blk_cmd_filter *filter, unsigned char *cmd, int has_write_perm); -extern int blk_register_filter(struct gendisk *disk); -extern void blk_unregister_filter(struct gendisk *disk); extern void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter); #define MAX_PHYS_SEGMENTS 128 -- cgit v1.2.3 From 00c5ae2fa0f8191a1b204e71f0ee11359e3b2c06 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 3 Sep 2008 11:26:42 +0800 Subject: mac80211: change MIMO_PS to SM_PS This patch follows 11n spec naming more rigorously replacing MIMO_PS with SM_PS (Spatial Multiplexing Power Save). (Originally submitted as 4 patches, "mac80211: change MIMO_PS to SM_PS", "iwlwifi: change MIMO_PS to SM_PS", "ath9k: change MIMO_PS to SM_PS", and "iwlwifi: remove double definition of SM PS". -- JWL) Signed-off-by: Ron Rindjunsky Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +- drivers/net/wireless/iwlwifi/iwl-core.c | 16 ++++++++-------- drivers/net/wireless/iwlwifi/iwl-dev.h | 5 ----- drivers/net/wireless/iwlwifi/iwl-sta.c | 8 ++++---- include/linux/ieee80211.h | 12 ++++++------ net/mac80211/main.c | 4 ++-- 8 files changed, 24 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 245b7308a9ad..57d7cc87cb0f 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -224,7 +224,7 @@ static void setup_ht_cap(struct ieee80211_ht_info *ht_info) ht_info->ht_supported = 1; ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH - |(u16)IEEE80211_HT_CAP_MIMO_PS + |(u16)IEEE80211_HT_CAP_SM_PS |(u16)IEEE80211_HT_CAP_SGI_40 |(u16)IEEE80211_HT_CAP_DSSSCCK40; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 98f2c843b99e..4fc3a0f1d8f1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -1153,8 +1153,8 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, !sta->ht_info.ht_supported) return -1; - if (((sta->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS) >> 2) - == IWL_MIMO_PS_STATIC) + if (((sta->ht_info.cap & IEEE80211_HT_CAP_SM_PS) >> 2) + == WLAN_HT_CAP_SM_PS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 1547122e66fa..27ddf6cb1c6e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -564,7 +564,7 @@ static void iwl4965_ht_conf(struct iwl_priv *priv, if (!iwl_conf->is_ht) return; - priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); + priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2); if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20) iwl_conf->sgf |= HT_SHORT_GI_20MHZ; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index a0b86af25c83..789556db6842 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -399,8 +399,8 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, ht_info->cap |= (u16)IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_20; - ht_info->cap |= (u16)(IEEE80211_HT_CAP_MIMO_PS & - (IWL_MIMO_PS_NONE << 2)); + ht_info->cap |= (u16)(IEEE80211_HT_CAP_SM_PS & + (WLAN_HT_CAP_SM_PS_DISABLED << 2)); max_bit_rate = MAX_BIT_RATE_20_MHZ; if (priv->hw_params.fat_channel & BIT(band)) { @@ -709,7 +709,7 @@ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); /* # of Rx chains to use when expecting MIMO. */ - if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC))) + if (is_single || (!is_cam && (priv->ps_mode == WLAN_HT_CAP_SM_PS_STATIC))) return 2; else return 3; @@ -721,14 +721,14 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); /* # Rx chains when idling and maybe trying to save power */ switch (priv->ps_mode) { - case IWL_MIMO_PS_STATIC: - case IWL_MIMO_PS_DYNAMIC: + case WLAN_HT_CAP_SM_PS_STATIC: + case WLAN_HT_CAP_SM_PS_DYNAMIC: idle_cnt = (is_cam) ? 2 : 1; break; - case IWL_MIMO_PS_NONE: + case WLAN_HT_CAP_SM_PS_DISABLED: idle_cnt = (is_cam) ? active_cnt : 1; break; - case IWL_MIMO_PS_INVALID: + case WLAN_HT_CAP_SM_PS_INVALID: default: IWL_ERROR("invalide mimo ps mode %d\n", priv->ps_mode); WARN_ON(1); @@ -912,7 +912,7 @@ int iwl_init_drv(struct iwl_priv *priv) priv->iw_mode = IEEE80211_IF_TYPE_STA; priv->use_ant_b_for_management_frame = 1; /* start with ant B */ - priv->ps_mode = IWL_MIMO_PS_NONE; + priv->ps_mode = WLAN_HT_CAP_SM_PS_DISABLED; /* Choose which receivers/antennas to use */ iwl_set_rxon_chain(priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 640ceea913c7..f302e93b7798 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -644,11 +644,6 @@ struct iwl_kw { #define IWL_CHANNEL_WIDTH_20MHZ 0 #define IWL_CHANNEL_WIDTH_40MHZ 1 -#define IWL_MIMO_PS_STATIC 0 -#define IWL_MIMO_PS_NONE 3 -#define IWL_MIMO_PS_DYNAMIC 1 -#define IWL_MIMO_PS_INVALID 2 - #define IWL_OPERATION_MODE_AUTO 0 #define IWL_OPERATION_MODE_HT_ONLY 1 #define IWL_OPERATION_MODE_MIXED 2 diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 5b7b05c8773f..a72569f1acb5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -191,20 +191,20 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, if (!sta_ht_inf || !sta_ht_inf->ht_supported) goto done; - mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2; + mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; sta_flags = priv->stations[index].sta.station_flags; sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); switch (mimo_ps_mode) { - case WLAN_HT_CAP_MIMO_PS_STATIC: + case WLAN_HT_CAP_SM_PS_STATIC: sta_flags |= STA_FLG_MIMO_DIS_MSK; break; - case WLAN_HT_CAP_MIMO_PS_DYNAMIC: + case WLAN_HT_CAP_SM_PS_DYNAMIC: sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; break; - case WLAN_HT_CAP_MIMO_PS_DISABLED: + case WLAN_HT_CAP_SM_PS_DISABLED: break; default: IWL_WARNING("Invalid MIMO PS mode %d\n", mimo_ps_mode); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index be456450cd2e..333d3ae76883 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -708,7 +708,7 @@ struct ieee80211_ht_addt_info { /* 802.11n HT capabilities masks */ #define IEEE80211_HT_CAP_SUP_WIDTH 0x0002 -#define IEEE80211_HT_CAP_MIMO_PS 0x000C +#define IEEE80211_HT_CAP_SM_PS 0x000C #define IEEE80211_HT_CAP_GRN_FLD 0x0010 #define IEEE80211_HT_CAP_SGI_20 0x0020 #define IEEE80211_HT_CAP_SGI_40 0x0040 @@ -737,11 +737,11 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004 #define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010 -/* MIMO Power Save Modes */ -#define WLAN_HT_CAP_MIMO_PS_STATIC 0 -#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1 -#define WLAN_HT_CAP_MIMO_PS_INVALID 2 -#define WLAN_HT_CAP_MIMO_PS_DISABLED 3 +/* Spatial Multiplexing Power Save Modes */ +#define WLAN_HT_CAP_SM_PS_STATIC 0 +#define WLAN_HT_CAP_SM_PS_DYNAMIC 1 +#define WLAN_HT_CAP_SM_PS_INVALID 2 +#define WLAN_HT_CAP_SM_PS_DISABLED 3 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 396cfb2d0f46..7dc063197259 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1140,8 +1140,8 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht, ht_conf.ht_supported = 1; ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap; - ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); - ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; + ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS); + ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS; ht_bss_conf.primary_channel = req_bss_cap->primary_channel; ht_bss_conf.bss_cap = req_bss_cap->bss_cap; ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; -- cgit v1.2.3 From 44d414dbff9d5bf46fc09f2e68567b5848cbbfd3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 8 Sep 2008 17:44:28 +0200 Subject: mac80211: move some HT code out of mlme.c Some of the HT code in mlme.c is misplaced: * constants/definitions belong to the ieee80211.h header * code being used in other modes as well shouldn't be there Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 18 +++ net/mac80211/Makefile | 1 + net/mac80211/ht.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/mlme.c | 321 --------------------------------------------- 4 files changed, 347 insertions(+), 321 deletions(-) create mode 100644 net/mac80211/ht.c (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 333d3ae76883..abc1abc63bf0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -643,6 +643,9 @@ struct ieee80211_mgmt { } u; } __attribute__ ((packed)); +/* mgmt header + 1 byte category code */ +#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) + /* Control frames */ struct ieee80211_rts { @@ -737,6 +740,21 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004 #define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010 +/* block-ack parameters */ +#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 +#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C +#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 +#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 +#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 + +/* + * A-PMDU buffer sizes + * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) + */ +#define IEEE80211_MIN_AMPDU_BUF 0x8 +#define IEEE80211_MAX_AMPDU_BUF 0x40 + + /* Spatial Multiplexing Power Save Modes */ #define WLAN_HT_CAP_SM_PS_STATIC 0 #define WLAN_HT_CAP_SM_PS_DYNAMIC 1 diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 376280a420ac..1a7ac50910c3 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -8,6 +8,7 @@ mac80211-y := \ wep.o \ wpa.o \ scan.o \ + ht.o \ mlme.o \ iface.o \ rate.o \ diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c new file mode 100644 index 000000000000..5ccf1bc17466 --- /dev/null +++ b/net/mac80211/ht.c @@ -0,0 +1,328 @@ +/* + * HT handling + * + * Copyright 2003, Jouni Malinen + * Copyright 2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * Copyright 2007, Michael Wu + * Copyright 2007-2008, Intel Corporation + * + * 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 +#include +#include +#include "ieee80211_i.h" +#include "sta_info.h" + +int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, + struct ieee80211_ht_info *ht_info) +{ + + if (ht_info == NULL) + return -EINVAL; + + memset(ht_info, 0, sizeof(*ht_info)); + + if (ht_cap_ie) { + u8 ampdu_info = ht_cap_ie->ampdu_params_info; + + ht_info->ht_supported = 1; + ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info); + ht_info->ampdu_factor = + ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR; + ht_info->ampdu_density = + (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2; + memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16); + } else + ht_info->ht_supported = 0; + + return 0; +} + +int ieee80211_ht_addt_info_ie_to_ht_bss_info( + struct ieee80211_ht_addt_info *ht_add_info_ie, + struct ieee80211_ht_bss_info *bss_info) +{ + if (bss_info == NULL) + return -EINVAL; + + memset(bss_info, 0, sizeof(*bss_info)); + + if (ht_add_info_ie) { + u16 op_mode; + op_mode = le16_to_cpu(ht_add_info_ie->operation_mode); + + bss_info->primary_channel = ht_add_info_ie->control_chan; + bss_info->bss_cap = ht_add_info_ie->ht_param; + bss_info->bss_op_mode = (u8)(op_mode & 0xff); + } + + return 0; +} + +void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da, + u16 tid, u8 dialog_token, u16 start_seq_num, + u16 agg_size, u16 timeout) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u16 capab; + + skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); + + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer " + "for addba request frame\n", sdata->dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, da, ETH_ALEN); + memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) + memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN); + else + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req)); + + mgmt->u.action.category = WLAN_CATEGORY_BACK; + mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; + + mgmt->u.action.u.addba_req.dialog_token = dialog_token; + capab = (u16)(1 << 1); /* bit 1 aggregation policy */ + capab |= (u16)(tid << 2); /* bit 5:2 TID number */ + capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */ + + mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab); + + mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout); + mgmt->u.action.u.addba_req.start_seq_num = + cpu_to_le16(start_seq_num << 4); + + ieee80211_sta_tx(sdata, skb, 0); +} + +void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, + u16 initiator, u16 reason_code) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u16 params; + + skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); + + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer " + "for delba frame\n", sdata->dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, da, ETH_ALEN); + memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) + memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN); + else + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); + + mgmt->u.action.category = WLAN_CATEGORY_BACK; + mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; + params = (u16)(initiator << 11); /* bit 11 initiator */ + params |= (u16)(tid << 12); /* bit 15:12 TID number */ + + mgmt->u.action.u.delba.params = cpu_to_le16(params); + mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); + + ieee80211_sta_tx(sdata, skb, 0); +} + +void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn) +{ + struct ieee80211_local *local = sdata->local; + struct sk_buff *skb; + struct ieee80211_bar *bar; + u16 bar_control = 0; + + skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer for " + "bar frame\n", sdata->dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar)); + memset(bar, 0, sizeof(*bar)); + bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_BACK_REQ); + memcpy(bar->ra, ra, ETH_ALEN); + memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN); + bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; + bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; + bar_control |= (u16)(tid << 12); + bar->control = cpu_to_le16(bar_control); + bar->start_seq_num = cpu_to_le16(ssn); + + ieee80211_sta_tx(sdata, skb, 0); +} + +void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, + u16 initiator, u16 reason) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_hw *hw = &local->hw; + struct sta_info *sta; + int ret, i; + DECLARE_MAC_BUF(mac); + + rcu_read_lock(); + + sta = sta_info_get(local, ra); + if (!sta) { + rcu_read_unlock(); + return; + } + + /* check if TID is in operational state */ + spin_lock_bh(&sta->lock); + if (sta->ampdu_mlme.tid_state_rx[tid] + != HT_AGG_STATE_OPERATIONAL) { + spin_unlock_bh(&sta->lock); + rcu_read_unlock(); + return; + } + sta->ampdu_mlme.tid_state_rx[tid] = + HT_AGG_STATE_REQ_STOP_BA_MSK | + (initiator << HT_AGG_STATE_INITIATOR_SHIFT); + spin_unlock_bh(&sta->lock); + + /* stop HW Rx aggregation. ampdu_action existence + * already verified in session init so we add the BUG_ON */ + BUG_ON(!local->ops->ampdu_action); + +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n", + print_mac(mac, ra), tid); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + + ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP, + ra, tid, NULL); + if (ret) + printk(KERN_DEBUG "HW problem - can not stop rx " + "aggregation for tid %d\n", tid); + + /* shutdown timer has not expired */ + if (initiator != WLAN_BACK_TIMER) + del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer); + + /* check if this is a self generated aggregation halt */ + if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER) + ieee80211_send_delba(sdata, ra, tid, 0, reason); + + /* free the reordering buffer */ + for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) { + if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) { + /* release the reordered frames */ + dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]); + sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--; + sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL; + } + } + /* free resources */ + kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf); + kfree(sta->ampdu_mlme.tid_rx[tid]); + sta->ampdu_mlme.tid_rx[tid] = NULL; + sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE; + + rcu_read_unlock(); +} + + +/* + * After sending add Block Ack request we activated a timer until + * add Block Ack response will arrive from the recipient. + * If this timer expires sta_addba_resp_timer_expired will be executed. + */ +void sta_addba_resp_timer_expired(unsigned long data) +{ + /* not an elegant detour, but there is no choice as the timer passes + * only one argument, and both sta_info and TID are needed, so init + * flow in sta_info_create gives the TID as data, while the timer_to_id + * array gives the sta through container_of */ + u16 tid = *(u8 *)data; + struct sta_info *temp_sta = container_of((void *)data, + struct sta_info, timer_to_tid[tid]); + + struct ieee80211_local *local = temp_sta->local; + struct ieee80211_hw *hw = &local->hw; + struct sta_info *sta; + u8 *state; + + rcu_read_lock(); + + sta = sta_info_get(local, temp_sta->addr); + if (!sta) { + rcu_read_unlock(); + return; + } + + state = &sta->ampdu_mlme.tid_state_tx[tid]; + /* check if the TID waits for addBA response */ + spin_lock_bh(&sta->lock); + if (!(*state & HT_ADDBA_REQUESTED_MSK)) { + spin_unlock_bh(&sta->lock); + *state = HT_AGG_STATE_IDLE; +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "timer expired on tid %d but we are not " + "expecting addBA response there", tid); +#endif + goto timer_expired_exit; + } + +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid); +#endif + + /* go through the state check in stop_BA_session */ + *state = HT_AGG_STATE_OPERATIONAL; + spin_unlock_bh(&sta->lock); + ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid, + WLAN_BACK_INITIATOR); + +timer_expired_exit: + rcu_read_unlock(); +} + +void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr) +{ + struct ieee80211_local *local = sdata->local; + int i; + + for (i = 0; i < STA_TID_NUM; i++) { + ieee80211_stop_tx_ba_session(&local->hw, addr, i, + WLAN_BACK_INITIATOR); + ieee80211_sta_stop_rx_ba_session(sdata, addr, i, + WLAN_BACK_RECIPIENT, + WLAN_REASON_QSTA_LEAVE_QBSS); + } +} + diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index be3292bfabe3..f43ca7b88d57 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -48,20 +48,6 @@ #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 -/* mgmt header + 1 byte category code */ -#define IEEE80211_MIN_ACTION_SIZE (24 + 1) - -#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 -#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C -#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 -#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 -#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 - -/* next values represent the buffer size for A-MPDU frame. - * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) */ -#define IEEE80211_MIN_AMPDU_BUF 0x8 -#define IEEE80211_MAX_AMPDU_BUF 0x40 - /* utils */ static int ecw2cw(int ecw) { @@ -389,52 +375,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, return changed; } -int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, - struct ieee80211_ht_info *ht_info) -{ - - if (ht_info == NULL) - return -EINVAL; - - memset(ht_info, 0, sizeof(*ht_info)); - - if (ht_cap_ie) { - u8 ampdu_info = ht_cap_ie->ampdu_params_info; - - ht_info->ht_supported = 1; - ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info); - ht_info->ampdu_factor = - ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR; - ht_info->ampdu_density = - (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2; - memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16); - } else - ht_info->ht_supported = 0; - - return 0; -} - -int ieee80211_ht_addt_info_ie_to_ht_bss_info( - struct ieee80211_ht_addt_info *ht_add_info_ie, - struct ieee80211_ht_bss_info *bss_info) -{ - if (bss_info == NULL) - return -EINVAL; - - memset(bss_info, 0, sizeof(*bss_info)); - - if (ht_add_info_ie) { - u16 op_mode; - op_mode = le16_to_cpu(ht_add_info_ie->operation_mode); - - bss_info->primary_channel = ht_add_info_ie->control_chan; - bss_info->bss_cap = ht_add_info_ie->ht_param; - bss_info->bss_op_mode = (u8)(op_mode & 0xff); - } - - return 0; -} - static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_sta *ifsta) { @@ -1118,55 +1058,6 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d return; } -void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da, - u16 tid, u8 dialog_token, u16 start_seq_num, - u16 agg_size, u16 timeout) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_sta *ifsta = &sdata->u.sta; - struct sk_buff *skb; - struct ieee80211_mgmt *mgmt; - u16 capab; - - skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer " - "for addba request frame\n", sdata->dev->name); - return; - } - skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); - memset(mgmt, 0, 24); - memcpy(mgmt->da, da, ETH_ALEN); - memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); - if (sdata->vif.type == IEEE80211_IF_TYPE_AP) - memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN); - else - memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); - - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - - skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req)); - - mgmt->u.action.category = WLAN_CATEGORY_BACK; - mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; - - mgmt->u.action.u.addba_req.dialog_token = dialog_token; - capab = (u16)(1 << 1); /* bit 1 aggregation policy */ - capab |= (u16)(tid << 2); /* bit 5:2 TID number */ - capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */ - - mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab); - - mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout); - mgmt->u.action.u.addba_req.start_seq_num = - cpu_to_le16(start_seq_num << 4); - - ieee80211_sta_tx(sdata, skb, 0); -} - /* * After accepting the AddBA Request we activated a timer, * resetting it after each frame that arrives from the originator. @@ -1396,149 +1287,6 @@ addba_resp_exit: rcu_read_unlock(); } -void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, - u16 initiator, u16 reason_code) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_sta *ifsta = &sdata->u.sta; - struct sk_buff *skb; - struct ieee80211_mgmt *mgmt; - u16 params; - - skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer " - "for delba frame\n", sdata->dev->name); - return; - } - - skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); - memset(mgmt, 0, 24); - memcpy(mgmt->da, da, ETH_ALEN); - memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); - if (sdata->vif.type == IEEE80211_IF_TYPE_AP) - memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN); - else - memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - - skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); - - mgmt->u.action.category = WLAN_CATEGORY_BACK; - mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; - params = (u16)(initiator << 11); /* bit 11 initiator */ - params |= (u16)(tid << 12); /* bit 15:12 TID number */ - - mgmt->u.action.u.delba.params = cpu_to_le16(params); - mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); - - ieee80211_sta_tx(sdata, skb, 0); -} - -void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn) -{ - struct ieee80211_local *local = sdata->local; - struct sk_buff *skb; - struct ieee80211_bar *bar; - u16 bar_control = 0; - - skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer for " - "bar frame\n", sdata->dev->name); - return; - } - skb_reserve(skb, local->hw.extra_tx_headroom); - bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar)); - memset(bar, 0, sizeof(*bar)); - bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_BACK_REQ); - memcpy(bar->ra, ra, ETH_ALEN); - memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN); - bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; - bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; - bar_control |= (u16)(tid << 12); - bar->control = cpu_to_le16(bar_control); - bar->start_seq_num = cpu_to_le16(ssn); - - ieee80211_sta_tx(sdata, skb, 0); -} - -void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, - u16 initiator, u16 reason) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_hw *hw = &local->hw; - struct sta_info *sta; - int ret, i; - DECLARE_MAC_BUF(mac); - - rcu_read_lock(); - - sta = sta_info_get(local, ra); - if (!sta) { - rcu_read_unlock(); - return; - } - - /* check if TID is in operational state */ - spin_lock_bh(&sta->lock); - if (sta->ampdu_mlme.tid_state_rx[tid] - != HT_AGG_STATE_OPERATIONAL) { - spin_unlock_bh(&sta->lock); - rcu_read_unlock(); - return; - } - sta->ampdu_mlme.tid_state_rx[tid] = - HT_AGG_STATE_REQ_STOP_BA_MSK | - (initiator << HT_AGG_STATE_INITIATOR_SHIFT); - spin_unlock_bh(&sta->lock); - - /* stop HW Rx aggregation. ampdu_action existence - * already verified in session init so we add the BUG_ON */ - BUG_ON(!local->ops->ampdu_action); - -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n", - print_mac(mac, ra), tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - - ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP, - ra, tid, NULL); - if (ret) - printk(KERN_DEBUG "HW problem - can not stop rx " - "aggregation for tid %d\n", tid); - - /* shutdown timer has not expired */ - if (initiator != WLAN_BACK_TIMER) - del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer); - - /* check if this is a self generated aggregation halt */ - if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER) - ieee80211_send_delba(sdata, ra, tid, 0, reason); - - /* free the reordering buffer */ - for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) { - if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) { - /* release the reordered frames */ - dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]); - sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--; - sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL; - } - } - /* free resources */ - kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf); - kfree(sta->ampdu_mlme.tid_rx[tid]); - sta->ampdu_mlme.tid_rx[tid] = NULL; - sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE; - - rcu_read_unlock(); -} - - static void ieee80211_sta_process_delba(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { @@ -1582,75 +1330,6 @@ static void ieee80211_sta_process_delba(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); } -/* - * After sending add Block Ack request we activated a timer until - * add Block Ack response will arrive from the recipient. - * If this timer expires sta_addba_resp_timer_expired will be executed. - */ -void sta_addba_resp_timer_expired(unsigned long data) -{ - /* not an elegant detour, but there is no choice as the timer passes - * only one argument, and both sta_info and TID are needed, so init - * flow in sta_info_create gives the TID as data, while the timer_to_id - * array gives the sta through container_of */ - u16 tid = *(u8 *)data; - struct sta_info *temp_sta = container_of((void *)data, - struct sta_info, timer_to_tid[tid]); - - struct ieee80211_local *local = temp_sta->local; - struct ieee80211_hw *hw = &local->hw; - struct sta_info *sta; - u8 *state; - - rcu_read_lock(); - - sta = sta_info_get(local, temp_sta->addr); - if (!sta) { - rcu_read_unlock(); - return; - } - - state = &sta->ampdu_mlme.tid_state_tx[tid]; - /* check if the TID waits for addBA response */ - spin_lock_bh(&sta->lock); - if (!(*state & HT_ADDBA_REQUESTED_MSK)) { - spin_unlock_bh(&sta->lock); - *state = HT_AGG_STATE_IDLE; -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "timer expired on tid %d but we are not " - "expecting addBA response there", tid); -#endif - goto timer_expired_exit; - } - -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid); -#endif - - /* go through the state check in stop_BA_session */ - *state = HT_AGG_STATE_OPERATIONAL; - spin_unlock_bh(&sta->lock); - ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid, - WLAN_BACK_INITIATOR); - -timer_expired_exit: - rcu_read_unlock(); -} - -void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr) -{ - struct ieee80211_local *local = sdata->local; - int i; - - for (i = 0; i < STA_TID_NUM; i++) { - ieee80211_stop_tx_ba_session(&local->hw, addr, i, - WLAN_BACK_INITIATOR); - ieee80211_sta_stop_rx_ba_session(sdata, addr, i, - WLAN_BACK_RECIPIENT, - WLAN_REASON_QSTA_LEAVE_QBSS); - } -} - static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, struct ieee80211_msrment_ie *request_ie, const u8 *da, const u8 *bssid, -- cgit v1.2.3 From 92651940ab00dbe64722e908f70d816713d677b7 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 12 Sep 2008 16:29:34 -0700 Subject: pkt_sched: Add multiqueue scheduler support This patch is intended to add a qdisc to support the new tx multiqueue architecture by providing a band for each hardware queue. By doing this it is possible to support a different qdisc per physical hardware queue. This qdisc uses the skb->queue_mapping to select which band to place the traffic onto. It then uses a round robin w/ a check to see if the subqueue is stopped to determine which band to dequeue the packet from. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- Documentation/networking/multiqueue.txt | 47 +++- include/linux/pkt_sched.h | 7 + net/sched/Kconfig | 9 + net/sched/Makefile | 1 + net/sched/sch_multiq.c | 467 ++++++++++++++++++++++++++++++++ 5 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 net/sched/sch_multiq.c (limited to 'include/linux') diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt index d391ea631141..5787ee6eca4f 100644 --- a/Documentation/networking/multiqueue.txt +++ b/Documentation/networking/multiqueue.txt @@ -24,4 +24,49 @@ netif_{start|stop|wake}_subqueue() functions to manage each queue while the device is still operational. netdev->queue_lock is still used when the device comes online or when it's completely shut down (unregister_netdev(), etc.). -Author: Peter P. Waskiewicz Jr. + +Section 2: Qdisc support for multiqueue devices + +----------------------------------------------- + +Currently two qdiscs support multiqueue devices. The first is the default +pfifo_fast qdisc. This qdisc supports one qdisc per hardware queue. A new +round-robin qdisc, sch_multiq also supports multiple hardware queues. The +qdisc is responsible for classifying the skb's and then directing the skb's to +bands and queues based on the value in skb->queue_mapping. Use this field in +the base driver to determine which queue to send the skb to. + +sch_multiq has been added for hardware that wishes to avoid unnecessary +requeuing. It will cycle though the bands and verify that the hardware queue +associated with the band is not stopped prior to dequeuing a packet. + +On qdisc load, the number of bands is based on the number of queues on the +hardware. Once the association is made, any skb with skb->queue_mapping set, +will be queued to the band associated with the hardware queue. + + +Section 3: Brief howto using MULTIQ for multiqueue devices +--------------------------------------------------------------- + +The userspace command 'tc,' part of the iproute2 package, is used to configure +qdiscs. To add the MULTIQ qdisc to your network device, assuming the device +is called eth0, run the following command: + +# tc qdisc add dev eth0 root handle 1: multiq + +The qdisc will allocate the number of bands to equal the number of queues that +the device reports, and bring the qdisc online. Assuming eth0 has 4 Tx +queues, the band mapping would look like: + +band 0 => queue 0 +band 1 => queue 1 +band 2 => queue 2 +band 3 => queue 3 + +Traffic will begin flowing through each queue if your base device has either +the default simple_tx_hash or a custom netdev->select_queue() defined. + +The behavior of tc filters remains the same. + +Author: Alexander Duyck +Original Author: Peter P. Waskiewicz Jr. diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index e5de421ac7b4..5d921fa91a5b 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -123,6 +123,13 @@ struct tc_prio_qopt __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ }; +/* MULTIQ section */ + +struct tc_multiq_qopt { + __u16 bands; /* Number of bands */ + __u16 max_bands; /* Maximum number of queues */ +}; + /* TBF section */ struct tc_tbf_qopt diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 9437b27ff84d..efaa7a75e7f3 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -106,6 +106,15 @@ config NET_SCH_PRIO To compile this code as a module, choose M here: the module will be called sch_prio. +config NET_SCH_MULTIQ + tristate "Hardware Multiqueue-aware Multi Band Queuing (MULTIQ)" + ---help--- + Say Y here if you want to use an n-band queue packet scheduler + to support devices that have multiple hardware transmit queues. + + To compile this code as a module, choose M here: the + module will be called sch_multiq. + config NET_SCH_RED tristate "Random Early Detection (RED)" ---help--- diff --git a/net/sched/Makefile b/net/sched/Makefile index 1d2b0f7df848..3d9b953f7f62 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o +obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o obj-$(CONFIG_NET_CLS_U32) += cls_u32.o diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c new file mode 100644 index 000000000000..49a8b67ed3b8 --- /dev/null +++ b/net/sched/sch_multiq.c @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Author: Alexander Duyck + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct multiq_sched_data { + u16 bands; + u16 max_bands; + u16 curband; + struct tcf_proto *filter_list; + struct Qdisc **queues; +}; + + +static struct Qdisc * +multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + u32 band; + struct tcf_result res; + int err; + + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; + err = tc_classify(skb, q->filter_list, &res); +#ifdef CONFIG_NET_CLS_ACT + switch (err) { + case TC_ACT_STOLEN: + case TC_ACT_QUEUED: + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; + case TC_ACT_SHOT: + return NULL; + } +#endif + band = skb_get_queue_mapping(skb); + + if (band >= q->bands) + return q->queues[0]; + + return q->queues[band]; +} + +static int +multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch) +{ + struct Qdisc *qdisc; + int ret; + + qdisc = multiq_classify(skb, sch, &ret); +#ifdef CONFIG_NET_CLS_ACT + if (qdisc == NULL) { + + if (ret & __NET_XMIT_BYPASS) + sch->qstats.drops++; + kfree_skb(skb); + return ret; + } +#endif + + ret = qdisc_enqueue(skb, qdisc); + if (ret == NET_XMIT_SUCCESS) { + sch->bstats.bytes += qdisc_pkt_len(skb); + sch->bstats.packets++; + sch->q.qlen++; + return NET_XMIT_SUCCESS; + } + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; + return ret; +} + + +static int +multiq_requeue(struct sk_buff *skb, struct Qdisc *sch) +{ + struct Qdisc *qdisc; + int ret; + + qdisc = multiq_classify(skb, sch, &ret); +#ifdef CONFIG_NET_CLS_ACT + if (qdisc == NULL) { + if (ret & __NET_XMIT_BYPASS) + sch->qstats.drops++; + kfree_skb(skb); + return ret; + } +#endif + + ret = qdisc->ops->requeue(skb, qdisc); + if (ret == NET_XMIT_SUCCESS) { + sch->q.qlen++; + sch->qstats.requeues++; + return NET_XMIT_SUCCESS; + } + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; + return ret; +} + + +static struct sk_buff *multiq_dequeue(struct Qdisc *sch) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + struct Qdisc *qdisc; + struct sk_buff *skb; + int band; + + for (band = 0; band < q->bands; band++) { + /* cycle through bands to ensure fairness */ + q->curband++; + if (q->curband >= q->bands) + q->curband = 0; + + /* Check that target subqueue is available before + * pulling an skb to avoid excessive requeues + */ + if (!__netif_subqueue_stopped(qdisc_dev(sch), q->curband)) { + qdisc = q->queues[q->curband]; + skb = qdisc->dequeue(qdisc); + if (skb) { + sch->q.qlen--; + return skb; + } + } + } + return NULL; + +} + +static unsigned int multiq_drop(struct Qdisc *sch) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + int band; + unsigned int len; + struct Qdisc *qdisc; + + for (band = q->bands-1; band >= 0; band--) { + qdisc = q->queues[band]; + if (qdisc->ops->drop) { + len = qdisc->ops->drop(qdisc); + if (len != 0) { + sch->q.qlen--; + return len; + } + } + } + return 0; +} + + +static void +multiq_reset(struct Qdisc *sch) +{ + u16 band; + struct multiq_sched_data *q = qdisc_priv(sch); + + for (band = 0; band < q->bands; band++) + qdisc_reset(q->queues[band]); + sch->q.qlen = 0; + q->curband = 0; +} + +static void +multiq_destroy(struct Qdisc *sch) +{ + int band; + struct multiq_sched_data *q = qdisc_priv(sch); + + tcf_destroy_chain(&q->filter_list); + for (band = 0; band < q->bands; band++) + qdisc_destroy(q->queues[band]); + + kfree(q->queues); +} + +static int multiq_tune(struct Qdisc *sch, struct nlattr *opt) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + struct tc_multiq_qopt *qopt; + int i; + + if (!netif_is_multiqueue(qdisc_dev(sch))) + return -EINVAL; + if (nla_len(opt) < sizeof(*qopt)) + return -EINVAL; + + qopt = nla_data(opt); + + qopt->bands = qdisc_dev(sch)->real_num_tx_queues; + + sch_tree_lock(sch); + q->bands = qopt->bands; + for (i = q->bands; i < q->max_bands; i++) { + struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc); + if (child != &noop_qdisc) { + qdisc_tree_decrease_qlen(child, child->q.qlen); + qdisc_destroy(child); + } + } + + sch_tree_unlock(sch); + + for (i = 0; i < q->bands; i++) { + if (q->queues[i] == &noop_qdisc) { + struct Qdisc *child; + child = qdisc_create_dflt(qdisc_dev(sch), + sch->dev_queue, + &pfifo_qdisc_ops, + TC_H_MAKE(sch->handle, + i + 1)); + if (child) { + sch_tree_lock(sch); + child = xchg(&q->queues[i], child); + + if (child != &noop_qdisc) { + qdisc_tree_decrease_qlen(child, + child->q.qlen); + qdisc_destroy(child); + } + sch_tree_unlock(sch); + } + } + } + return 0; +} + +static int multiq_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + int i; + + q->queues = NULL; + + if (opt == NULL) + return -EINVAL; + + q->max_bands = qdisc_dev(sch)->num_tx_queues; + + q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL); + if (!q->queues) + return -ENOBUFS; + for (i = 0; i < q->max_bands; i++) + q->queues[i] = &noop_qdisc; + + return multiq_tune(sch, opt); +} + +static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + unsigned char *b = skb_tail_pointer(skb); + struct tc_multiq_qopt opt; + + opt.bands = q->bands; + opt.max_bands = q->max_bands; + + NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); + + return skb->len; + +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, + struct Qdisc **old) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + unsigned long band = arg - 1; + + if (band >= q->bands) + return -EINVAL; + + if (new == NULL) + new = &noop_qdisc; + + sch_tree_lock(sch); + *old = q->queues[band]; + q->queues[band] = new; + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); + qdisc_reset(*old); + sch_tree_unlock(sch); + + return 0; +} + +static struct Qdisc * +multiq_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + unsigned long band = arg - 1; + + if (band >= q->bands) + return NULL; + + return q->queues[band]; +} + +static unsigned long multiq_get(struct Qdisc *sch, u32 classid) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + unsigned long band = TC_H_MIN(classid); + + if (band - 1 >= q->bands) + return 0; + return band; +} + +static unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent, + u32 classid) +{ + return multiq_get(sch, classid); +} + + +static void multiq_put(struct Qdisc *q, unsigned long cl) +{ + return; +} + +static int multiq_change(struct Qdisc *sch, u32 handle, u32 parent, + struct nlattr **tca, unsigned long *arg) +{ + unsigned long cl = *arg; + struct multiq_sched_data *q = qdisc_priv(sch); + + if (cl - 1 > q->bands) + return -ENOENT; + return 0; +} + +static int multiq_delete(struct Qdisc *sch, unsigned long cl) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + if (cl - 1 > q->bands) + return -ENOENT; + return 0; +} + + +static int multiq_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + + if (cl - 1 > q->bands) + return -ENOENT; + tcm->tcm_handle |= TC_H_MIN(cl); + if (q->queues[cl-1]) + tcm->tcm_info = q->queues[cl-1]->handle; + return 0; +} + +static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl, + struct gnet_dump *d) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + struct Qdisc *cl_q; + + cl_q = q->queues[cl - 1]; + if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 || + gnet_stats_copy_queue(d, &cl_q->qstats) < 0) + return -1; + + return 0; +} + +static void multiq_walk(struct Qdisc *sch, struct qdisc_walker *arg) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + int band; + + if (arg->stop) + return; + + for (band = 0; band < q->bands; band++) { + if (arg->count < arg->skip) { + arg->count++; + continue; + } + if (arg->fn(sch, band+1, arg) < 0) { + arg->stop = 1; + break; + } + arg->count++; + } +} + +static struct tcf_proto **multiq_find_tcf(struct Qdisc *sch, unsigned long cl) +{ + struct multiq_sched_data *q = qdisc_priv(sch); + + if (cl) + return NULL; + return &q->filter_list; +} + +static const struct Qdisc_class_ops multiq_class_ops = { + .graft = multiq_graft, + .leaf = multiq_leaf, + .get = multiq_get, + .put = multiq_put, + .change = multiq_change, + .delete = multiq_delete, + .walk = multiq_walk, + .tcf_chain = multiq_find_tcf, + .bind_tcf = multiq_bind, + .unbind_tcf = multiq_put, + .dump = multiq_dump_class, + .dump_stats = multiq_dump_class_stats, +}; + +static struct Qdisc_ops multiq_qdisc_ops __read_mostly = { + .next = NULL, + .cl_ops = &multiq_class_ops, + .id = "multiq", + .priv_size = sizeof(struct multiq_sched_data), + .enqueue = multiq_enqueue, + .dequeue = multiq_dequeue, + .requeue = multiq_requeue, + .drop = multiq_drop, + .init = multiq_init, + .reset = multiq_reset, + .destroy = multiq_destroy, + .change = multiq_tune, + .dump = multiq_dump, + .owner = THIS_MODULE, +}; + +static int __init multiq_module_init(void) +{ + return register_qdisc(&multiq_qdisc_ops); +} + +static void __exit multiq_module_exit(void) +{ + unregister_qdisc(&multiq_qdisc_ops); +} + +module_init(multiq_module_init) +module_exit(multiq_module_exit) + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ca9b0e27e072be4cef2f5f0cbc0b0fd94eae3520 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 12 Sep 2008 16:30:20 -0700 Subject: pkt_action: add new action skbedit This new action will have the ability to change the priority and/or queue_mapping fields on an sk_buff. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- Documentation/networking/multiqueue.txt | 9 +- include/linux/tc_act/Kbuild | 1 + include/linux/tc_act/tc_skbedit.h | 44 +++++++ include/net/tc_act/tc_skbedit.h | 34 ++++++ net/sched/Kconfig | 11 ++ net/sched/Makefile | 1 + net/sched/act_skbedit.c | 203 ++++++++++++++++++++++++++++++++ 7 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 include/linux/tc_act/tc_skbedit.h create mode 100644 include/net/tc_act/tc_skbedit.h create mode 100644 net/sched/act_skbedit.c (limited to 'include/linux') diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt index 5787ee6eca4f..10113ffa8072 100644 --- a/Documentation/networking/multiqueue.txt +++ b/Documentation/networking/multiqueue.txt @@ -66,7 +66,14 @@ band 3 => queue 3 Traffic will begin flowing through each queue if your base device has either the default simple_tx_hash or a custom netdev->select_queue() defined. -The behavior of tc filters remains the same. +The behavior of tc filters remains the same. However a new tc action, +skbedit, has been added. Assuming you wanted to route all traffic to a +specific host, for example 192.168.0.3, though a specific queue you could use +this action and establish a filter such as: + +tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \ + match ip dst 192.168.0.3 \ + action skbedit queue_mapping 3 Author: Alexander Duyck Original Author: Peter P. Waskiewicz Jr. diff --git a/include/linux/tc_act/Kbuild b/include/linux/tc_act/Kbuild index 6dac0d7365cc..76990937f4c9 100644 --- a/include/linux/tc_act/Kbuild +++ b/include/linux/tc_act/Kbuild @@ -3,3 +3,4 @@ header-y += tc_ipt.h header-y += tc_mirred.h header-y += tc_pedit.h header-y += tc_nat.h +header-y += tc_skbedit.h diff --git a/include/linux/tc_act/tc_skbedit.h b/include/linux/tc_act/tc_skbedit.h new file mode 100644 index 000000000000..a14e461a7af7 --- /dev/null +++ b/include/linux/tc_act/tc_skbedit.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Author: Alexander Duyck + */ + +#ifndef __LINUX_TC_SKBEDIT_H +#define __LINUX_TC_SKBEDIT_H + +#include + +#define TCA_ACT_SKBEDIT 11 + +#define SKBEDIT_F_PRIORITY 0x1 +#define SKBEDIT_F_QUEUE_MAPPING 0x2 + +struct tc_skbedit { + tc_gen; +}; + +enum { + TCA_SKBEDIT_UNSPEC, + TCA_SKBEDIT_TM, + TCA_SKBEDIT_PARMS, + TCA_SKBEDIT_PRIORITY, + TCA_SKBEDIT_QUEUE_MAPPING, + __TCA_SKBEDIT_MAX +}; +#define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1) + +#endif diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h new file mode 100644 index 000000000000..6abb3ed3ebf7 --- /dev/null +++ b/include/net/tc_act/tc_skbedit.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Author: Alexander Duyck + */ + +#ifndef __NET_TC_SKBEDIT_H +#define __NET_TC_SKBEDIT_H + +#include + +struct tcf_skbedit { + struct tcf_common common; + u32 flags; + u32 priority; + u16 queue_mapping; +}; +#define to_skbedit(pc) \ + container_of(pc, struct tcf_skbedit, common) + +#endif /* __NET_TC_SKBEDIT_H */ diff --git a/net/sched/Kconfig b/net/sched/Kconfig index efaa7a75e7f3..6767e54155db 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -485,6 +485,17 @@ config NET_ACT_SIMP To compile this code as a module, choose M here: the module will be called simple. +config NET_ACT_SKBEDIT + tristate "SKB Editing" + depends on NET_CLS_ACT + ---help--- + Say Y here to change skb priority or queue_mapping settings. + + If unsure, say N. + + To compile this code as a module, choose M here: the + module will be called skbedit. + config NET_CLS_IND bool "Incoming device classification" depends on NET_CLS_U32 || NET_CLS_FW diff --git a/net/sched/Makefile b/net/sched/Makefile index 3d9b953f7f62..e60c9925b269 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_NET_ACT_IPT) += act_ipt.o obj-$(CONFIG_NET_ACT_NAT) += act_nat.o obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o +obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c new file mode 100644 index 000000000000..fe9777e77f35 --- /dev/null +++ b/net/sched/act_skbedit.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Author: Alexander Duyck + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SKBEDIT_TAB_MASK 15 +static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1]; +static u32 skbedit_idx_gen; +static DEFINE_RWLOCK(skbedit_lock); + +static struct tcf_hashinfo skbedit_hash_info = { + .htab = tcf_skbedit_ht, + .hmask = SKBEDIT_TAB_MASK, + .lock = &skbedit_lock, +}; + +static int tcf_skbedit(struct sk_buff *skb, struct tc_action *a, + struct tcf_result *res) +{ + struct tcf_skbedit *d = a->priv; + + spin_lock(&d->tcf_lock); + d->tcf_tm.lastuse = jiffies; + d->tcf_bstats.bytes += qdisc_pkt_len(skb); + d->tcf_bstats.packets++; + + if (d->flags & SKBEDIT_F_PRIORITY) + skb->priority = d->priority; + if (d->flags & SKBEDIT_F_QUEUE_MAPPING && + skb->dev->real_num_tx_queues > d->queue_mapping) + skb_set_queue_mapping(skb, d->queue_mapping); + + spin_unlock(&d->tcf_lock); + return d->tcf_action; +} + +static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { + [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, + [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, + [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, +}; + +static int tcf_skbedit_init(struct nlattr *nla, struct nlattr *est, + struct tc_action *a, int ovr, int bind) +{ + struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; + struct tc_skbedit *parm; + struct tcf_skbedit *d; + struct tcf_common *pc; + u32 flags = 0, *priority = NULL; + u16 *queue_mapping = NULL; + int ret = 0, err; + + if (nla == NULL) + return -EINVAL; + + err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); + if (err < 0) + return err; + + if (tb[TCA_SKBEDIT_PARMS] == NULL) + return -EINVAL; + + if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { + flags |= SKBEDIT_F_PRIORITY; + priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); + } + + if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { + flags |= SKBEDIT_F_QUEUE_MAPPING; + queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); + } + if (!flags) + return -EINVAL; + + parm = nla_data(tb[TCA_SKBEDIT_PARMS]); + + pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); + if (!pc) { + pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, + &skbedit_idx_gen, &skbedit_hash_info); + if (unlikely(!pc)) + return -ENOMEM; + + d = to_skbedit(pc); + ret = ACT_P_CREATED; + } else { + d = to_skbedit(pc); + if (!ovr) { + tcf_hash_release(pc, bind, &skbedit_hash_info); + return -EEXIST; + } + } + + spin_lock_bh(&d->tcf_lock); + + d->flags = flags; + if (flags & SKBEDIT_F_PRIORITY) + d->priority = *priority; + if (flags & SKBEDIT_F_QUEUE_MAPPING) + d->queue_mapping = *queue_mapping; + d->tcf_action = parm->action; + + spin_unlock_bh(&d->tcf_lock); + + if (ret == ACT_P_CREATED) + tcf_hash_insert(pc, &skbedit_hash_info); + return ret; +} + +static inline int tcf_skbedit_cleanup(struct tc_action *a, int bind) +{ + struct tcf_skbedit *d = a->priv; + + if (d) + return tcf_hash_release(&d->common, bind, &skbedit_hash_info); + return 0; +} + +static inline int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, + int bind, int ref) +{ + unsigned char *b = skb_tail_pointer(skb); + struct tcf_skbedit *d = a->priv; + struct tc_skbedit opt; + struct tcf_t t; + + opt.index = d->tcf_index; + opt.refcnt = d->tcf_refcnt - ref; + opt.bindcnt = d->tcf_bindcnt - bind; + opt.action = d->tcf_action; + NLA_PUT(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt); + if (d->flags & SKBEDIT_F_PRIORITY) + NLA_PUT(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority), + &d->priority); + if (d->flags & SKBEDIT_F_QUEUE_MAPPING) + NLA_PUT(skb, TCA_SKBEDIT_QUEUE_MAPPING, + sizeof(d->queue_mapping), &d->queue_mapping); + t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); + t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); + t.expires = jiffies_to_clock_t(d->tcf_tm.expires); + NLA_PUT(skb, TCA_SKBEDIT_TM, sizeof(t), &t); + return skb->len; + +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static struct tc_action_ops act_skbedit_ops = { + .kind = "skbedit", + .hinfo = &skbedit_hash_info, + .type = TCA_ACT_SKBEDIT, + .capab = TCA_CAP_NONE, + .owner = THIS_MODULE, + .act = tcf_skbedit, + .dump = tcf_skbedit_dump, + .cleanup = tcf_skbedit_cleanup, + .init = tcf_skbedit_init, + .walk = tcf_generic_walker, +}; + +MODULE_AUTHOR("Alexander Duyck, "); +MODULE_DESCRIPTION("SKB Editing"); +MODULE_LICENSE("GPL"); + +static int __init skbedit_init_module(void) +{ + return tcf_register_action(&act_skbedit_ops); +} + +static void __exit skbedit_cleanup_module(void) +{ + tcf_unregister_action(&act_skbedit_ops); +} + +module_init(skbedit_init_module); +module_exit(skbedit_cleanup_module); -- cgit v1.2.3 From 97b697a11b07e2ebfa69c488132596cc5eb24119 Mon Sep 17 00:00:00 2001 From: Taisuke Yamada Date: Sat, 13 Sep 2008 16:46:15 -0400 Subject: [libata] LBA28/LBA48 off-by-one bug in ata.h I recently bought 3 HGST P7K500-series 500GB SATA drives and had trouble accessing the block right on the LBA28-LBA48 border. Here's how it fails (same for all 3 drives): # dd if=/dev/sdc bs=512 count=1 skip=268435455 > /dev/null dd: reading `/dev/sdc': Input/output error 0+0 records in 0+0 records out 0 bytes (0 B) copied, 0.288033 seconds, 0.0 kB/s # dmesg ata1.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x0 ata1.00: BMDMA stat 0x25 ata1.00: cmd c8/00:08:f8:ff:ff/00:00:00:00:00/ef tag 0 dma 4096 in res 51/04:08:f8:ff:ff/00:00:00:00:00/ef Emask 0x1 (device error) ata1.00: status: { DRDY ERR } ata1.00: error: { ABRT } ata1.00: configured for UDMA/33 ata1: EH complete ... After some investigations, it turned out this seems to be caused by misinterpretation of the ATA specification on LBA28 access. Following part is the code in question: === include/linux/ata.h === static inline int lba_28_ok(u64 block, u32 n_block) { /* check the ending block number */ return ((block + n_block - 1) < ((u64)1 << 28)) && (n_block <= 256); } HGST drive (sometimes) fails with LBA28 access of {block = 0xfffffff, n_block = 1}, and this behavior seems to be comformant. Other drives, including other HGST drives are not that strict, through. >From the ATA specification: (http://www.t13.org/Documents/UploadedDocuments/project/d1410r3b-ATA-ATAPI-6.pdf) 8.15.29 Word (61:60): Total number of user addressable sectors This field contains a value that is one greater than the total number of user addressable sectors (see 6.2). The maximum value that shall be placed in this field is 0FFFFFFFh. So the driver shouldn't use the value of 0xfffffff for LBA28 request as this exceeds maximum user addressable sector. The logical maximum value for LBA28 is 0xffffffe. The obvious fix is to cut "- 1" part, and the patch attached just do that. I've been using the patched kernel for about a month now, and the same fix is also floating on the net for some time. So I believe this fix works reliably. Just FYI, many Windows/Intel platform users also seems to be struck by this, and HGST has issued a note pointing to Intel ICH8/9 driver. "28-bit LBA command is being used to access LBAs 29-bits in length" http://www.hitachigst.com/hddt/knowtree.nsf/cffe836ed7c12018862565b000530c74/b531b8bce8745fb78825740f00580e23 Also, *BSDs seems to have similar fix included sometime around ~2004, through I have not checked out exact portion of the code. Signed-off-by: Taisuke Yamada Signed-off-by: Jeff Garzik --- include/linux/ata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ata.h b/include/linux/ata.h index 1ce19c1ef0e9..8a12d718c169 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -745,7 +745,7 @@ static inline int ata_ok(u8 status) static inline int lba_28_ok(u64 block, u32 n_block) { /* check the ending block number */ - return ((block + n_block - 1) < ((u64)1 << 28)) && (n_block <= 256); + return ((block + n_block) < ((u64)1 << 28)) && (n_block <= 256); } static inline int lba_48_ok(u64 block, u32 n_block) -- cgit v1.2.3 From dea420ce0e2973e8ef1fd11fde6804c8d03a82ad Mon Sep 17 00:00:00 2001 From: Hiroshi DOYU Date: Sat, 13 Sep 2008 02:33:07 -0700 Subject: include/linux/ioport.h: add missing macro argument for devm_release_* family akpm: these have no callers at this time, but they shall soon, so let's get them right. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Hiroshi DOYU Cc: Tony Lindgren Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ioport.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 8d3b7a9afd17..350033e8f4e1 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -159,9 +159,9 @@ extern struct resource * __devm_request_region(struct device *dev, struct resource *parent, resource_size_t start, resource_size_t n, const char *name); -#define devm_release_region(start,n) \ +#define devm_release_region(dev, start, n) \ __devm_release_region(dev, &ioport_resource, (start), (n)) -#define devm_release_mem_region(start,n) \ +#define devm_release_mem_region(dev, start, n) \ __devm_release_region(dev, &iomem_resource, (start), (n)) extern void __devm_release_region(struct device *dev, struct resource *parent, -- cgit v1.2.3 From 5bead2a0680687b9576d57c177988e8aa082b922 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Sat, 13 Sep 2008 02:33:19 -0700 Subject: mm: mark the correct zone as full when scanning zonelists The iterator for_each_zone_zonelist() uses a struct zoneref *z cursor when scanning zonelists to keep track of where in the zonelist it is. The zoneref that is returned corresponds to the the next zone that is to be scanned, not the current one. It was intended to be treated as an opaque list. When the page allocator is scanning a zonelist, it marks elements in the zonelist corresponding to zones that are temporarily full. As the zonelist is being updated, it uses the cursor here; if (NUMA_BUILD) zlc_mark_zone_full(zonelist, z); This is intended to prevent rescanning in the near future but the zoneref cursor does not correspond to the zone that has been found to be full. This is an easy misunderstanding to make so this patch corrects the problem by changing zoneref cursor to be the current zone being scanned instead of the next one. Signed-off-by: Mel Gorman Cc: Andy Whitcroft Cc: KAMEZAWA Hiroyuki Cc: [2.6.26.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 12 ++++++------ mm/mmzone.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 443bc7cd8c62..428328a05fa1 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -751,8 +751,9 @@ static inline int zonelist_node_idx(struct zoneref *zoneref) * * This function returns the next zone at or below a given zone index that is * within the allowed nodemask using a cursor as the starting point for the - * search. The zoneref returned is a cursor that is used as the next starting - * point for future calls to next_zones_zonelist(). + * search. The zoneref returned is a cursor that represents the current zone + * being examined. It should be advanced by one before calling + * next_zones_zonelist again. */ struct zoneref *next_zones_zonelist(struct zoneref *z, enum zone_type highest_zoneidx, @@ -768,9 +769,8 @@ struct zoneref *next_zones_zonelist(struct zoneref *z, * * This function returns the first zone at or below a given zone index that is * within the allowed nodemask. The zoneref returned is a cursor that can be - * used to iterate the zonelist with next_zones_zonelist. The cursor should - * not be used by the caller as it does not match the value of the zone - * returned. + * used to iterate the zonelist with next_zones_zonelist by advancing it by + * one before calling. */ static inline struct zoneref *first_zones_zonelist(struct zonelist *zonelist, enum zone_type highest_zoneidx, @@ -795,7 +795,7 @@ static inline struct zoneref *first_zones_zonelist(struct zonelist *zonelist, #define for_each_zone_zonelist_nodemask(zone, z, zlist, highidx, nodemask) \ for (z = first_zones_zonelist(zlist, highidx, nodemask, &zone); \ zone; \ - z = next_zones_zonelist(z, highidx, nodemask, &zone)) \ + z = next_zones_zonelist(++z, highidx, nodemask, &zone)) \ /** * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index diff --git a/mm/mmzone.c b/mm/mmzone.c index 486ed595ee6f..16ce8b955dcf 100644 --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -69,6 +69,6 @@ struct zoneref *next_zones_zonelist(struct zoneref *z, (z->zone && !zref_in_nodemask(z, nodes))) z++; - *zone = zonelist_zone(z++); + *zone = zonelist_zone(z); return z; } -- cgit v1.2.3 From 8e82f8c34b1759ae0d80fe96101746ec51fb1ba4 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Sat, 13 Sep 2008 02:33:26 -0700 Subject: memstick: fix MSProHG 8-bit interface mode support - 8-bit interface mode never worked properly. The only adapter I have which supports the 8b mode (the Jmicron) had some problems with its clock wiring and they discovered it only now. We also discovered that ProHG media is more sensitive to the ordering of initialization commands. - Make the driver fall back to highest supported mode instead of always falling back to serial. The driver will attempt the switch to 8b mode for any new MSPro card, but not all of them support it. Previously, these new cards ended up in serial mode, which is not the best idea (they work fine with 4b, after all). - Edit some macros for better conformance to Sony documentation Signed-off-by: Alex Dubov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/core/memstick.c | 10 ++-- drivers/memstick/core/mspro_block.c | 33 ++++++++++--- drivers/memstick/host/jmb38x_ms.c | 39 ++++++++++----- include/linux/memstick.h | 97 +++++++++++++++++++------------------ 4 files changed, 106 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index a38005008a20..cea46906408e 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -185,7 +185,7 @@ static void memstick_free(struct device *dev) } static struct class memstick_host_class = { - .name = "memstick_host", + .name = "memstick_host", .dev_release = memstick_free }; @@ -264,7 +264,7 @@ EXPORT_SYMBOL(memstick_new_req); * @sg - TPC argument */ void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc, - struct scatterlist *sg) + const struct scatterlist *sg) { mrq->tpc = tpc; if (tpc & 8) @@ -294,7 +294,7 @@ EXPORT_SYMBOL(memstick_init_req_sg); * user supplied buffer. */ void memstick_init_req(struct memstick_request *mrq, unsigned char tpc, - void *buf, size_t length) + const void *buf, size_t length) { mrq->tpc = tpc; if (tpc & 8) @@ -439,7 +439,7 @@ static void memstick_check(struct work_struct *work) if (!host->card) { if (memstick_power_on(host)) goto out_power_off; - } else + } else if (host->card->stop) host->card->stop(host->card); card = memstick_alloc_card(host); @@ -458,7 +458,7 @@ static void memstick_check(struct work_struct *work) || !(host->card->check(host->card))) { device_unregister(&host->card->dev); host->card = NULL; - } else + } else if (host->card->start) host->card->start(host->card); } diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 44b1817f2f2f..d2d2318dafa4 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -30,6 +30,8 @@ module_param(major, int, 0644); #define MSPRO_BLOCK_SIGNATURE 0xa5c3 #define MSPRO_BLOCK_MAX_ATTRIBUTES 41 +#define MSPRO_BLOCK_PART_SHIFT 3 + enum { MSPRO_BLOCK_ID_SYSINFO = 0x10, MSPRO_BLOCK_ID_MODELNAME = 0x15, @@ -195,7 +197,7 @@ static int mspro_block_bd_open(struct inode *inode, struct file *filp) static int mspro_block_disk_release(struct gendisk *disk) { struct mspro_block_data *msb = disk->private_data; - int disk_id = disk->first_minor >> MEMSTICK_PART_SHIFT; + int disk_id = disk->first_minor >> MSPRO_BLOCK_PART_SHIFT; mutex_lock(&mspro_block_disk_lock); @@ -877,6 +879,7 @@ static int mspro_block_switch_interface(struct memstick_dev *card) struct mspro_block_data *msb = memstick_get_drvdata(card); int rc = 0; +try_again: if (msb->caps & MEMSTICK_CAP_PAR4) rc = mspro_block_set_interface(card, MEMSTICK_SYS_PAR4); else @@ -930,6 +933,18 @@ static int mspro_block_switch_interface(struct memstick_dev *card) rc = memstick_set_rw_addr(card); if (!rc) rc = mspro_block_set_interface(card, msb->system); + + if (!rc) { + msleep(150); + rc = mspro_block_wait_for_ced(card); + if (rc) + return rc; + + if (msb->caps & MEMSTICK_CAP_PAR8) { + msb->caps &= ~MEMSTICK_CAP_PAR8; + goto try_again; + } + } } return rc; } @@ -1117,14 +1132,16 @@ static int mspro_block_init_card(struct memstick_dev *card) return -EIO; msb->caps = host->caps; - rc = mspro_block_switch_interface(card); + + msleep(150); + rc = mspro_block_wait_for_ced(card); if (rc) return rc; - msleep(200); - rc = mspro_block_wait_for_ced(card); + rc = mspro_block_switch_interface(card); if (rc) return rc; + dev_dbg(&card->dev, "card activated\n"); if (msb->system != MEMSTICK_SYS_SERIAL) msb->caps |= MEMSTICK_CAP_AUTO_GET_INT; @@ -1192,12 +1209,12 @@ static int mspro_block_init_disk(struct memstick_dev *card) if (rc) return rc; - if ((disk_id << MEMSTICK_PART_SHIFT) > 255) { + if ((disk_id << MSPRO_BLOCK_PART_SHIFT) > 255) { rc = -ENOSPC; goto out_release_id; } - msb->disk = alloc_disk(1 << MEMSTICK_PART_SHIFT); + msb->disk = alloc_disk(1 << MSPRO_BLOCK_PART_SHIFT); if (!msb->disk) { rc = -ENOMEM; goto out_release_id; @@ -1220,7 +1237,7 @@ static int mspro_block_init_disk(struct memstick_dev *card) MSPRO_BLOCK_MAX_PAGES * msb->page_size); msb->disk->major = major; - msb->disk->first_minor = disk_id << MEMSTICK_PART_SHIFT; + msb->disk->first_minor = disk_id << MSPRO_BLOCK_PART_SHIFT; msb->disk->fops = &ms_block_bdops; msb->usage_count = 1; msb->disk->private_data = msb; @@ -1416,7 +1433,7 @@ out_unlock: static struct memstick_device_id mspro_block_id_tbl[] = { {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_PRO, MEMSTICK_CATEGORY_STORAGE_DUO, - MEMSTICK_CLASS_GENERIC_DUO}, + MEMSTICK_CLASS_DUO}, {} }; diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index 3485c63d20b0..2fb95a5b72eb 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -81,6 +81,8 @@ struct jmb38x_ms { #define TPC_CODE_SZ_MASK 0x00000700 #define TPC_DATA_SZ_MASK 0x00000007 +#define HOST_CONTROL_TDELAY_EN 0x00040000 +#define HOST_CONTROL_HW_OC_P 0x00010000 #define HOST_CONTROL_RESET_REQ 0x00008000 #define HOST_CONTROL_REI 0x00004000 #define HOST_CONTROL_LED 0x00000400 @@ -88,6 +90,7 @@ struct jmb38x_ms { #define HOST_CONTROL_RESET 0x00000100 #define HOST_CONTROL_POWER_EN 0x00000080 #define HOST_CONTROL_CLOCK_EN 0x00000040 +#define HOST_CONTROL_REO 0x00000008 #define HOST_CONTROL_IF_SHIFT 4 #define HOST_CONTROL_IF_SERIAL 0x0 @@ -133,11 +136,15 @@ struct jmb38x_ms { #define PAD_PU_PD_ON_MS_SOCK1 0x0f0f0000 #define CLOCK_CONTROL_40MHZ 0x00000001 -#define CLOCK_CONTROL_50MHZ 0x00000002 +#define CLOCK_CONTROL_50MHZ 0x0000000a #define CLOCK_CONTROL_60MHZ 0x00000008 #define CLOCK_CONTROL_62_5MHZ 0x0000000c #define CLOCK_CONTROL_OFF 0x00000000 +#define PCI_CTL_CLOCK_DLY_ADDR 0x000000b0 +#define PCI_CTL_CLOCK_DLY_MASK_A 0x00000f00 +#define PCI_CTL_CLOCK_DLY_MASK_B 0x0000f000 + enum { CMD_READY = 0x01, FIFO_READY = 0x02, @@ -367,8 +374,7 @@ static int jmb38x_ms_issue_cmd(struct memstick_host *msh) return host->req->error; } - dev_dbg(&msh->dev, "control %08x\n", - readl(host->addr + HOST_CONTROL)); + dev_dbg(&msh->dev, "control %08x\n", readl(host->addr + HOST_CONTROL)); dev_dbg(&msh->dev, "status %08x\n", readl(host->addr + INT_STATUS)); dev_dbg(&msh->dev, "hstatus %08x\n", readl(host->addr + STATUS)); @@ -637,7 +643,7 @@ static int jmb38x_ms_reset(struct jmb38x_ms_host *host) ndelay(20); } dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); - return -EIO; + /* return -EIO; */ reset_next: writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN @@ -680,7 +686,9 @@ static int jmb38x_ms_set_param(struct memstick_host *msh, host_ctl = 7; host_ctl |= HOST_CONTROL_POWER_EN - | HOST_CONTROL_CLOCK_EN; + | HOST_CONTROL_CLOCK_EN + | HOST_CONTROL_HW_OC_P + | HOST_CONTROL_TDELAY_EN; writel(host_ctl, host->addr + HOST_CONTROL); writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 @@ -704,33 +712,40 @@ static int jmb38x_ms_set_param(struct memstick_host *msh, break; case MEMSTICK_INTERFACE: host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); + pci_read_config_dword(host->chip->pdev, + PCI_CTL_CLOCK_DLY_ADDR, + &clock_delay); + clock_delay &= host->id ? ~PCI_CTL_CLOCK_DLY_MASK_B + : ~PCI_CTL_CLOCK_DLY_MASK_A; if (value == MEMSTICK_SERIAL) { host_ctl &= ~HOST_CONTROL_FAST_CLK; + host_ctl &= ~HOST_CONTROL_REO; host_ctl |= HOST_CONTROL_IF_SERIAL << HOST_CONTROL_IF_SHIFT; host_ctl |= HOST_CONTROL_REI; clock_ctl = CLOCK_CONTROL_40MHZ; - clock_delay = 0; } else if (value == MEMSTICK_PAR4) { - host_ctl |= HOST_CONTROL_FAST_CLK; + host_ctl |= HOST_CONTROL_FAST_CLK | HOST_CONTROL_REO; host_ctl |= HOST_CONTROL_IF_PAR4 << HOST_CONTROL_IF_SHIFT; host_ctl &= ~HOST_CONTROL_REI; clock_ctl = CLOCK_CONTROL_40MHZ; - clock_delay = 4; + clock_delay |= host->id ? (4 << 12) : (4 << 8); } else if (value == MEMSTICK_PAR8) { host_ctl |= HOST_CONTROL_FAST_CLK; host_ctl |= HOST_CONTROL_IF_PAR8 << HOST_CONTROL_IF_SHIFT; - host_ctl &= ~HOST_CONTROL_REI; - clock_ctl = CLOCK_CONTROL_60MHZ; - clock_delay = 0; + host_ctl &= ~(HOST_CONTROL_REI | HOST_CONTROL_REO); + clock_ctl = CLOCK_CONTROL_50MHZ; } else return -EINVAL; + writel(host_ctl, host->addr + HOST_CONTROL); writel(clock_ctl, host->addr + CLOCK_CONTROL); - writel(clock_delay, host->addr + CLOCK_DELAY); + pci_write_config_dword(host->chip->pdev, + PCI_CTL_CLOCK_DLY_ADDR, + clock_delay); break; }; return 0; diff --git a/include/linux/memstick.h b/include/linux/memstick.h index a9f998a3f48b..d0c37e682234 100644 --- a/include/linux/memstick.h +++ b/include/linux/memstick.h @@ -21,30 +21,30 @@ struct ms_status_register { unsigned char reserved; unsigned char interrupt; -#define MEMSTICK_INT_CMDNAK 0x0001 -#define MEMSTICK_INT_IOREQ 0x0008 -#define MEMSTICK_INT_IOBREQ 0x0010 -#define MEMSTICK_INT_BREQ 0x0020 -#define MEMSTICK_INT_ERR 0x0040 -#define MEMSTICK_INT_CED 0x0080 +#define MEMSTICK_INT_CMDNAK 0x01 +#define MEMSTICK_INT_IOREQ 0x08 +#define MEMSTICK_INT_IOBREQ 0x10 +#define MEMSTICK_INT_BREQ 0x20 +#define MEMSTICK_INT_ERR 0x40 +#define MEMSTICK_INT_CED 0x80 unsigned char status0; -#define MEMSTICK_STATUS0_WP 0x0001 -#define MEMSTICK_STATUS0_SL 0x0002 -#define MEMSTICK_STATUS0_BF 0x0010 -#define MEMSTICK_STATUS0_BE 0x0020 -#define MEMSTICK_STATUS0_FB0 0x0040 -#define MEMSTICK_STATUS0_MB 0x0080 +#define MEMSTICK_STATUS0_WP 0x01 +#define MEMSTICK_STATUS0_SL 0x02 +#define MEMSTICK_STATUS0_BF 0x10 +#define MEMSTICK_STATUS0_BE 0x20 +#define MEMSTICK_STATUS0_FB0 0x40 +#define MEMSTICK_STATUS0_MB 0x80 unsigned char status1; -#define MEMSTICK_STATUS1_UCFG 0x0001 -#define MEMSTICK_STATUS1_FGER 0x0002 -#define MEMSTICK_STATUS1_UCEX 0x0004 -#define MEMSTICK_STATUS1_EXER 0x0008 -#define MEMSTICK_STATUS1_UCDT 0x0010 -#define MEMSTICK_STATUS1_DTER 0x0020 -#define MEMSTICK_STATUS1_FBI 0x0040 -#define MEMSTICK_STATUS1_MB 0x0080 +#define MEMSTICK_STATUS1_UCFG 0x01 +#define MEMSTICK_STATUS1_FGER 0x02 +#define MEMSTICK_STATUS1_UCEX 0x04 +#define MEMSTICK_STATUS1_EXER 0x08 +#define MEMSTICK_STATUS1_UCDT 0x10 +#define MEMSTICK_STATUS1_DTER 0x20 +#define MEMSTICK_STATUS1_FB1 0x40 +#define MEMSTICK_STATUS1_MB 0x80 } __attribute__((packed)); struct ms_id_register { @@ -56,32 +56,32 @@ struct ms_id_register { struct ms_param_register { unsigned char system; -#define MEMSTICK_SYS_ATEN 0xc0 -#define MEMSTICK_SYS_BAMD 0x80 #define MEMSTICK_SYS_PAM 0x08 +#define MEMSTICK_SYS_BAMD 0x80 unsigned char block_address_msb; unsigned short block_address; unsigned char cp; -#define MEMSTICK_CP_BLOCK 0x0000 -#define MEMSTICK_CP_PAGE 0x0020 -#define MEMSTICK_CP_EXTRA 0x0040 -#define MEMSTICK_CP_OVERWRITE 0x0080 +#define MEMSTICK_CP_BLOCK 0x00 +#define MEMSTICK_CP_PAGE 0x20 +#define MEMSTICK_CP_EXTRA 0x40 +#define MEMSTICK_CP_OVERWRITE 0x80 unsigned char page_address; } __attribute__((packed)); struct ms_extra_data_register { unsigned char overwrite_flag; -#define MEMSTICK_OVERWRITE_UPDATA 0x0010 -#define MEMSTICK_OVERWRITE_PAGE 0x0060 -#define MEMSTICK_OVERWRITE_BLOCK 0x0080 +#define MEMSTICK_OVERWRITE_UDST 0x10 +#define MEMSTICK_OVERWRITE_PGST1 0x20 +#define MEMSTICK_OVERWRITE_PGST0 0x40 +#define MEMSTICK_OVERWRITE_BKST 0x80 unsigned char management_flag; -#define MEMSTICK_MANAGEMENT_SYSTEM 0x0004 -#define MEMSTICK_MANAGEMENT_TRANS_TABLE 0x0008 -#define MEMSTICK_MANAGEMENT_COPY 0x0010 -#define MEMSTICK_MANAGEMENT_ACCESS 0x0020 +#define MEMSTICK_MANAGEMENT_SYSFLG 0x04 +#define MEMSTICK_MANAGEMENT_ATFLG 0x08 +#define MEMSTICK_MANAGEMENT_SCMS1 0x10 +#define MEMSTICK_MANAGEMENT_SCMS0 0x20 unsigned short logical_address; } __attribute__((packed)); @@ -96,9 +96,9 @@ struct ms_register { struct mspro_param_register { unsigned char system; -#define MEMSTICK_SYS_SERIAL 0x80 #define MEMSTICK_SYS_PAR4 0x00 #define MEMSTICK_SYS_PAR8 0x40 +#define MEMSTICK_SYS_SERIAL 0x80 unsigned short data_count; unsigned int data_address; @@ -147,7 +147,7 @@ struct ms_register_addr { unsigned char w_length; } __attribute__((packed)); -enum { +enum memstick_tpc { MS_TPC_READ_MG_STATUS = 0x01, MS_TPC_READ_LONG_DATA = 0x02, MS_TPC_READ_SHORT_DATA = 0x03, @@ -167,7 +167,7 @@ enum { MS_TPC_SET_CMD = 0x0e }; -enum { +enum memstick_command { MS_CMD_BLOCK_END = 0x33, MS_CMD_RESET = 0x3c, MS_CMD_BLOCK_WRITE = 0x55, @@ -201,8 +201,6 @@ enum { /*** Driver structures and functions ***/ -#define MEMSTICK_PART_SHIFT 3 - enum memstick_param { MEMSTICK_POWER = 1, MEMSTICK_INTERFACE }; #define MEMSTICK_POWER_OFF 0 @@ -215,24 +213,27 @@ enum memstick_param { MEMSTICK_POWER = 1, MEMSTICK_INTERFACE }; struct memstick_host; struct memstick_driver; +struct memstick_device_id { + unsigned char match_flags; #define MEMSTICK_MATCH_ALL 0x01 + unsigned char type; #define MEMSTICK_TYPE_LEGACY 0xff #define MEMSTICK_TYPE_DUO 0x00 #define MEMSTICK_TYPE_PRO 0x01 + unsigned char category; #define MEMSTICK_CATEGORY_STORAGE 0xff #define MEMSTICK_CATEGORY_STORAGE_DUO 0x00 +#define MEMSTICK_CATEGORY_IO 0x01 +#define MEMSTICK_CATEGORY_IO_PRO 0x10 -#define MEMSTICK_CLASS_GENERIC 0xff -#define MEMSTICK_CLASS_GENERIC_DUO 0x00 - - -struct memstick_device_id { - unsigned char match_flags; - unsigned char type; - unsigned char category; unsigned char class; +#define MEMSTICK_CLASS_FLASH 0xff +#define MEMSTICK_CLASS_DUO 0x00 +#define MEMSTICK_CLASS_ROM 0x01 +#define MEMSTICK_CLASS_RO 0x02 +#define MEMSTICK_CLASS_WP 0x03 }; struct memstick_request { @@ -319,9 +320,9 @@ void memstick_suspend_host(struct memstick_host *host); void memstick_resume_host(struct memstick_host *host); void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc, - struct scatterlist *sg); + const struct scatterlist *sg); void memstick_init_req(struct memstick_request *mrq, unsigned char tpc, - void *buf, size_t length); + const void *buf, size_t length); int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq); void memstick_new_req(struct memstick_host *host); -- cgit v1.2.3 From eecfffc154ffbfe70686a9905c090b488778c28e Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 12 Sep 2008 19:42:33 +0900 Subject: iommu: add iommu_device_max_index IOMMU helper function This function helps IOMMUs to know the highest address that a device can access to. Signed-off-by: FUJITA Tomonori Signed-off-by: Ingo Molnar --- include/linux/iommu-helper.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h index c975caf75385..58f41107e4ae 100644 --- a/include/linux/iommu-helper.h +++ b/include/linux/iommu-helper.h @@ -1,3 +1,13 @@ +static inline unsigned long iommu_device_max_index(unsigned long size, + unsigned long offset, + u64 dma_mask) +{ + if (size + offset > dma_mask) + return dma_mask - offset + 1; + else + return size; +} + extern int iommu_is_span_boundary(unsigned int index, unsigned int nr, unsigned long shift, unsigned long boundary_size); -- cgit v1.2.3 From 589fc9a6e2102b498978f6350581ec7fa5aeb032 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 12 Sep 2008 19:42:34 +0900 Subject: iommu: add dma_get_mask helper function Several IOMMUs do the same thing to get the dma_mask of a device. This adds a helper function to do the same thing to sweep them. Signed-off-by: FUJITA Tomonori Signed-off-by: Ingo Molnar --- include/linux/dma-mapping.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 6ed50c1642f1..0dba7433af18 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -63,6 +63,13 @@ static inline int is_buffer_dma_capable(u64 mask, dma_addr_t addr, size_t size) #define dma_sync_single dma_sync_single_for_cpu #define dma_sync_sg dma_sync_sg_for_cpu +static inline u64 dma_get_mask(struct device *dev) +{ + if (dev->dma_mask && *dev->dma_mask) + return *dev->dma_mask; + return DMA_32BIT_MASK; +} + extern u64 dma_get_required_mask(struct device *dev); static inline unsigned int dma_get_max_seg_size(struct device *dev) -- cgit v1.2.3 From b2e1b30290539b344cbaff0d9da38012e03aa347 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 9 Sep 2008 23:19:48 -0700 Subject: cfg80211: Add new wireless regulatory infrastructure This adds the new wireless regulatory infrastructure. The main motiviation behind this was to centralize regulatory code as each driver was implementing their own regulatory solution, and to replace the initial centralized code we have where: * only 3 regulatory domains are supported: US, JP and EU * regulatory domains can only be changed through module parameter * all rules were built statically in the kernel We now have support for regulatory domains for many countries and regulatory domains are now queried through a userspace agent through udev allowing distributions to update regulatory rules without updating the kernel. Each driver can regulatory_hint() a regulatory domain based on either their EEPROM mapped regulatory domain value to a respective ISO/IEC 3166-1 country code or pass an internally built regulatory domain. We also add support to let the user set the regulatory domain through userspace in case of faulty EEPROMs to further help compliance. Support for world roaming will be added soon for cards capable of this. For more information see: http://wireless.kernel.org/en/developers/Regulatory/CRDA For now we leave an option to enable the old module parameter, ieee80211_regdom, and to build the 3 old regdomains statically (US, JP and EU). This option is CONFIG_WIRELESS_OLD_REGULATORY. These old static definitions and the module parameter is being scheduled for removal for 2.6.29. Note that if you use this you won't make use of a world regulatory domain as its pointless. If you leave this option enabled and if CRDA is present and you use US or JP we will try to ask CRDA to update us a regulatory domain for us. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 18 + Documentation/networking/regulatory.txt | 194 +++++++ include/linux/nl80211.h | 96 +++- include/net/cfg80211.h | 60 +++ include/net/mac80211.h | 2 + include/net/wireless.h | 58 +++ net/mac80211/cfg.c | 7 + net/wireless/Kconfig | 32 ++ net/wireless/core.c | 162 +++++- net/wireless/core.h | 2 +- net/wireless/nl80211.c | 151 ++++++ net/wireless/reg.c | 805 +++++++++++++++++++++++++---- net/wireless/reg.h | 44 ++ 13 files changed, 1513 insertions(+), 118 deletions(-) create mode 100644 Documentation/networking/regulatory.txt create mode 100644 net/wireless/reg.h (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index eb1a47b97427..c93fcdec246d 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,6 +6,24 @@ be removed from this file. --------------------------- +What: old static regulatory information and ieee80211_regdom module parameter +When: 2.6.29 +Why: The old regulatory infrastructure has been replaced with a new one + which does not require statically defined regulatory domains. We do + not want to keep static regulatory domains in the kernel due to the + the dynamic nature of regulatory law and localization. We kept around + the old static definitions for the regulatory domains of: + * US + * JP + * EU + and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was + set. We also kept around the ieee80211_regdom module parameter in case + some applications were relying on it. Changing regulatory domains + can now be done instead by using nl80211, as is done with iw. +Who: Luis R. Rodriguez + +--------------------------- + What: dev->power.power_state When: July 2007 Why: Broken design for runtime control over driver power states, confusing diff --git a/Documentation/networking/regulatory.txt b/Documentation/networking/regulatory.txt new file mode 100644 index 000000000000..a96989a8ff35 --- /dev/null +++ b/Documentation/networking/regulatory.txt @@ -0,0 +1,194 @@ +Linux wireless regulatory documentation +--------------------------------------- + +This document gives a brief review over how the Linux wireless +regulatory infrastructure works. + +More up to date information can be obtained at the project's web page: + +http://wireless.kernel.org/en/developers/Regulatory + +Keeping regulatory domains in userspace +--------------------------------------- + +Due to the dynamic nature of regulatory domains we keep them +in userspace and provide a framework for userspace to upload +to the kernel one regulatory domain to be used as the central +core regulatory domain all wireless devices should adhere to. + +How to get regulatory domains to the kernel +------------------------------------------- + +Userspace gets a regulatory domain in the kernel by having +a userspace agent build it and send it via nl80211. Only +expected regulatory domains will be respected by the kernel. + +A currently available userspace agent which can accomplish this +is CRDA - central regulatory domain agent. Its documented here: + +http://wireless.kernel.org/en/developers/Regulatory/CRDA + +Essentially the kernel will send a udev event when it knows +it needs a new regulatory domain. A udev rule can be put in place +to trigger crda to send the respective regulatory domain for a +specific ISO/IEC 3166 alpha2. + +Below is an example udev rule which can be used: + +# Example file, should be put in /etc/udev/rules.d/regulatory.rules +KERNEL=="regulatory*", ACTION=="change", SUBSYSTEM=="platform", RUN+="/sbin/crda" + +The alpha2 is passed as an environment variable under the variable COUNTRY. + +Who asks for regulatory domains? +-------------------------------- + +* Users + +Users can use iw: + +http://wireless.kernel.org/en/users/Documentation/iw + +An example: + + # set regulatory domain to "Costa Rica" + iw reg set CR + +This will request the kernel to set the regulatory domain to +the specificied alpha2. The kernel in turn will then ask userspace +to provide a regulatory domain for the alpha2 specified by the user +by sending a uevent. + +* Wireless subsystems for Country Information elements + +The kernel will send a uevent to inform userspace a new +regulatory domain is required. More on this to be added +as its integration is added. + +* Drivers + +If drivers determine they need a specific regulatory domain +set they can inform the wireless core using regulatory_hint(). +They have two options -- they either provide an alpha2 so that +crda can provide back a regulatory domain for that country or +they can build their own regulatory domain based on internal +custom knowledge so the wireless core can respect it. + +*Most* drivers will rely on the first mechanism of providing a +regulatory hint with an alpha2. For these drivers there is an additional +check that can be used to ensure compliance based on custom EEPROM +regulatory data. This additional check can be used by drivers by +registering on its struct wiphy a reg_notifier() callback. This notifier +is called when the core's regulatory domain has been changed. The driver +can use this to review the changes made and also review who made them +(driver, user, country IE) and determine what to allow based on its +internal EEPROM data. Devices drivers wishing to be capable of world +roaming should use this callback. More on world roaming will be +added to this document when its support is enabled. + +Device drivers who provide their own built regulatory domain +do not need a callback as the channels registered by them are +the only ones that will be allowed and therefore *additional* +cannels cannot be enabled. + +Example code - drivers hinting an alpha2: +------------------------------------------ + +This example comes from the zd1211rw device driver. You can start +by having a mapping of your device's EEPROM country/regulatory +domain value to to a specific alpha2 as follows: + +static struct zd_reg_alpha2_map reg_alpha2_map[] = { + { ZD_REGDOMAIN_FCC, "US" }, + { ZD_REGDOMAIN_IC, "CA" }, + { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */ + { ZD_REGDOMAIN_JAPAN, "JP" }, + { ZD_REGDOMAIN_JAPAN_ADD, "JP" }, + { ZD_REGDOMAIN_SPAIN, "ES" }, + { ZD_REGDOMAIN_FRANCE, "FR" }, + +Then you can define a routine to map your read EEPROM value to an alpha2, +as follows: + +static int zd_reg2alpha2(u8 regdomain, char *alpha2) +{ + unsigned int i; + struct zd_reg_alpha2_map *reg_map; + for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) { + reg_map = ®_alpha2_map[i]; + if (regdomain == reg_map->reg) { + alpha2[0] = reg_map->alpha2[0]; + alpha2[1] = reg_map->alpha2[1]; + return 0; + } + } + return 1; +} + +Lastly, you can then hint to the core of your discovered alpha2, if a match +was found. You need to do this after you have registered your wiphy. You +are expected to do this during initialization. + + r = zd_reg2alpha2(mac->regdomain, alpha2); + if (!r) + regulatory_hint(hw->wiphy, alpha2, NULL); + +Example code - drivers providing a built in regulatory domain: +-------------------------------------------------------------- + +If you have regulatory information you can obtain from your +driver and you *need* to use this we let you build a regulatory domain +structure and pass it to the wireless core. To do this you should +kmalloc() a structure big enough to hold your regulatory domain +structure and you should then fill it with your data. Finally you simply +call regulatory_hint() with the regulatory domain structure in it. + +Bellow is a simple example, with a regulatory domain cached using the stack. +Your implementation may vary (read EEPROM cache instead, for example). + +Example cache of some regulatory domain + +struct ieee80211_regdomain mydriver_jp_regdom = { + .n_reg_rules = 3, + .alpha2 = "JP", + //.alpha2 = "99", /* If I have no alpha2 to map it to */ + .reg_rules = { + /* IEEE 802.11b/g, channels 1..14 */ + REG_RULE(2412-20, 2484+20, 40, 6, 20, 0), + /* IEEE 802.11a, channels 34..48 */ + REG_RULE(5170-20, 5240+20, 40, 6, 20, + NL80211_RRF_PASSIVE_SCAN), + /* IEEE 802.11a, channels 52..64 */ + REG_RULE(5260-20, 5320+20, 40, 6, 20, + NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + } +}; + +Then in some part of your code after your wiphy has been registered: + + int r; + struct ieee80211_regdomain *rd; + int size_of_regd; + int num_rules = mydriver_jp_regdom.n_reg_rules; + unsigned int i; + + size_of_regd = sizeof(struct ieee80211_regdomain) + + (num_rules * sizeof(struct ieee80211_reg_rule)); + + rd = kzalloc(size_of_regd, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + memcpy(rd, &mydriver_jp_regdom, sizeof(struct ieee80211_regdomain)); + + for (i=0; i < num_rules; i++) { + memcpy(&rd->reg_rules[i], &mydriver_jp_regdom.reg_rules[i], + sizeof(struct ieee80211_reg_rule)); + } + r = regulatory_hint(hw->wiphy, NULL, rd); + if (r) { + kfree(rd); + return r; + } + diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 5e51f4e7600b..9bad65400fba 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -92,6 +92,20 @@ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by * %NL80211_ATTR_IFINDEX. * + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -131,7 +145,10 @@ enum nl80211_commands { NL80211_CMD_SET_BSS, - /* add commands here */ + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ __NL80211_CMD_AFTER_LAST, @@ -197,10 +214,21 @@ enum nl80211_commands { * info given for %NL80211_CMD_GET_MPATH, nested attribute described at * &enum nl80211_mpath_info. * - * * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_mntr_flags. * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled * (u8, 0 or 1) @@ -265,6 +293,9 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORTED_IFTYPES, + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -278,6 +309,7 @@ enum nl80211_attrs { #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY #define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 @@ -472,6 +504,66 @@ enum nl80211_bitrate_attr { NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 }; +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + /** * enum nl80211_mntr_flags - monitor configuration flags * diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0a72d1e3d3ab..9f40c4d417d7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -287,6 +287,66 @@ struct bss_parameters { int use_short_slot_time; }; +/** + * enum reg_set_by - Indicates who is trying to set the regulatory domain + * @REGDOM_SET_BY_INIT: regulatory domain was set by initialization. We will be + * using a static world regulatory domain by default. + * @REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world regulatory domain. + * @REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the wireless core + * it thinks its knows the regulatory domain we should be in. + * @REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an 802.11 country + * information element with regulatory information it thinks we + * should consider. + */ +enum reg_set_by { + REGDOM_SET_BY_INIT, + REGDOM_SET_BY_CORE, + REGDOM_SET_BY_USER, + REGDOM_SET_BY_DRIVER, + REGDOM_SET_BY_COUNTRY_IE, +}; + +struct ieee80211_freq_range { + u32 start_freq_khz; + u32 end_freq_khz; + u32 max_bandwidth_khz; +}; + +struct ieee80211_power_rule { + u32 max_antenna_gain; + u32 max_eirp; +}; + +struct ieee80211_reg_rule { + struct ieee80211_freq_range freq_range; + struct ieee80211_power_rule power_rule; + u32 flags; +}; + +struct ieee80211_regdomain { + u32 n_reg_rules; + char alpha2[2]; + struct ieee80211_reg_rule reg_rules[]; +}; + +#define MHZ_TO_KHZ(freq) (freq * 1000) +#define KHZ_TO_MHZ(freq) (freq / 1000) +#define DBI_TO_MBI(gain) (gain * 100) +#define MBI_TO_DBI(gain) (gain / 100) +#define DBM_TO_MBM(gain) (gain * 100) +#define MBM_TO_DBM(gain) (gain / 100) + +#define REG_RULE(start, end, bw, gain, eirp, reg_flags) { \ + .freq_range.start_freq_khz = (start) * 1000, \ + .freq_range.end_freq_khz = (end) * 1000, \ + .freq_range.max_bandwidth_khz = (bw) * 1000, \ + .power_rule.max_antenna_gain = (gain) * 100, \ + .power_rule.max_eirp = (eirp) * 100, \ + .flags = reg_flags, \ + } + /* from net/wireless.h */ struct wiphy; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index fb9e62211c34..f504e3eca7d3 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -833,6 +833,8 @@ struct ieee80211_hw { s8 max_signal; }; +struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy); + /** * SET_IEEE80211_DEV - set device for 802.11 hardware * diff --git a/include/net/wireless.h b/include/net/wireless.h index 1dc8ec3daa2f..e4378cc6bf8e 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -60,6 +60,7 @@ enum ieee80211_channel_flags { * with cfg80211. * * @center_freq: center frequency in MHz + * @max_bandwidth: maximum allowed bandwidth for this channel, in MHz * @hw_value: hardware-specific value for the channel * @flags: channel flags from &enum ieee80211_channel_flags. * @orig_flags: channel flags at registration time, used by regulatory @@ -73,6 +74,7 @@ enum ieee80211_channel_flags { struct ieee80211_channel { enum ieee80211_band band; u16 center_freq; + u8 max_bandwidth; u16 hw_value; u32 flags; int max_antenna_gain; @@ -178,6 +180,7 @@ struct ieee80211_supported_band { * struct wiphy - wireless hardware description * @idx: the wiphy index assigned to this item * @class_dev: the class device representing /sys/class/ieee80211/ + * @reg_notifier: the driver's regulatory notification callback */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -197,6 +200,9 @@ struct wiphy { struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; + /* Lets us get back the wiphy on the callback */ + int (*reg_notifier)(struct wiphy *wiphy, enum reg_set_by setby); + /* fields below are read-only, assigned by cfg80211 */ /* the item in /sys/class/ieee80211/ points to this, @@ -322,6 +328,58 @@ extern int ieee80211_frequency_to_channel(int freq); */ extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, int freq); +/** + * __regulatory_hint - hint to the wireless core a regulatory domain + * @wiphy: if a driver is providing the hint this is the driver's very + * own &struct wiphy + * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain + * should be in. If @rd is set this should be NULL + * @rd: a complete regulatory domain, if passed the caller need not worry + * about freeing it + * + * The Wireless subsystem can use this function to hint to the wireless core + * what it believes should be the current regulatory domain by + * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory + * domain should be in or by providing a completely build regulatory domain. + * + * Returns -EALREADY if *a regulatory domain* has already been set. Note that + * this could be by another driver. It is safe for drivers to continue if + * -EALREADY is returned, if drivers are not capable of world roaming they + * should not register more channels than they support. Right now we only + * support listening to the first driver hint. If the driver is capable + * of world roaming but wants to respect its own EEPROM mappings for + * specific regulatory domains it should register the @reg_notifier callback + * on the &struct wiphy. Returns 0 if the hint went through fine or through an + * intersection operation. Otherwise a standard error code is returned. + * + */ +extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, + const char *alpha2, struct ieee80211_regdomain *rd); +/** + * regulatory_hint - driver hint to the wireless core a regulatory domain + * @wiphy: the driver's very own &struct wiphy + * @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain + * should be in. If @rd is set this should be NULL. Note that if you + * set this to NULL you should still set rd->alpha2 to some accepted + * alpha2. + * @rd: a complete regulatory domain provided by the driver. If passed + * the driver does not need to worry about freeing it. + * + * Wireless drivers can use this function to hint to the wireless core + * what it believes should be the current regulatory domain by + * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory + * domain should be in or by providing a completely build regulatory domain. + * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried + * for a regulatory domain structure for the respective country. If + * a regulatory domain is build and passed you should set the alpha2 + * if possible, otherwise set it to the special value of "99" which tells + * the wireless core it is unknown. If you pass a built regulatory domain + * and we return non zero you are in charge of kfree()'ing the structure. + * + * See __regulatory_hint() documentation for possible return values. + */ +extern int regulatory_hint(struct wiphy *wiphy, + const char *alpha2, struct ieee80211_regdomain *rd); /** * ieee80211_get_channel - get channel struct from wiphy for specified frequency diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 928813ce08e2..5a3bdaad6c19 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -17,6 +17,13 @@ #include "rate.h" #include "mesh.h" +struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + return &local->hw; +} +EXPORT_SYMBOL(wiphy_to_hw); + static enum ieee80211_if_types nl80211_type_to_mac80211_type(enum nl80211_iftype type) { diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 833b024f8f66..b97bd9fe6b79 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -14,6 +14,38 @@ config NL80211 If unsure, say Y. +config WIRELESS_OLD_REGULATORY + bool "Old wireless static regulatory defintions" + default n + ---help--- + This option enables the old static regulatory information + and uses it within the new framework. This is available + temporarily as an option to help prevent immediate issues + due to the switch to the new regulatory framework which + does require a new userspace application which has the + database of regulatory information (CRDA) and another for + setting regulatory domains (iw). + + For more information see: + + http://wireless.kernel.org/en/developers/Regulatory/CRDA + http://wireless.kernel.org/en/users/Documentation/iw + + It is important to note though that if you *do* have CRDA present + and if this option is enabled CRDA *will* be called to update the + regulatory domain (for US and JP only). Support for letting the user + set the regulatory domain through iw is also supported. This option + mainly exists to leave around for a kernel release some old static + regulatory domains that were defined and to keep around the old + ieee80211_regdom module parameter. This is being phased out and you + should stop using them ASAP. + + Say N unless you cannot install a new userspace application + or have one currently depending on the ieee80211_regdom module + parameter and cannot port it to use the new userspace interfaces. + + This is scheduled for removal for 2.6.29. + config WIRELESS_EXT bool "Wireless extensions" default n diff --git a/net/wireless/core.c b/net/wireless/core.c index 7e995ac06a0c..a910cd2d0fd1 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -13,12 +13,14 @@ #include #include #include +#include #include #include #include #include "nl80211.h" #include "core.h" #include "sysfs.h" +#include "reg.h" /* name for sysfs, %d is appended */ #define PHY_NAME "phy" @@ -27,6 +29,107 @@ MODULE_AUTHOR("Johannes Berg"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("wireless configuration support"); +struct list_head regulatory_requests; + +/* Central wireless core regulatory domains, we only need two, + * the current one and a world regulatory domain in case we have no + * information to give us an alpha2 */ +struct ieee80211_regdomain *cfg80211_regdomain; + +/* We keep a static world regulatory domain in case of the absence of CRDA */ +const struct ieee80211_regdomain world_regdom = { + .n_reg_rules = 1, + .alpha2 = "00", + .reg_rules = { + REG_RULE(2402, 2472, 40, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS), + } +}; + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY +/* All this fucking static junk will be removed soon, so + * don't fucking count on it !@#$ */ + +static char *ieee80211_regdom = "US"; +module_param(ieee80211_regdom, charp, 0444); +MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); + +/* We assume 40 MHz bandwidth for the old regulatory work. + * We make emphasis we are using the exact same frequencies + * as before */ + +const struct ieee80211_regdomain us_regdom = { + .n_reg_rules = 6, + .alpha2 = "US", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-20, 2462+20, 40, 6, 27, 0), + /* IEEE 802.11a, channel 36 */ + REG_RULE(5180-20, 5180+20, 40, 6, 23, 0), + /* IEEE 802.11a, channel 40 */ + REG_RULE(5200-20, 5200+20, 40, 6, 23, 0), + /* IEEE 802.11a, channel 44 */ + REG_RULE(5220-20, 5220+20, 40, 6, 23, 0), + /* IEEE 802.11a, channels 48..64 */ + REG_RULE(5240-20, 5320+20, 40, 6, 23, 0), + /* IEEE 802.11a, channels 149..165, outdoor */ + REG_RULE(5745-20, 5825+20, 40, 6, 30, 0), + } +}; + +const struct ieee80211_regdomain jp_regdom = { + .n_reg_rules = 3, + .alpha2 = "JP", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..14 */ + REG_RULE(2412-20, 2484+20, 40, 6, 20, 0), + /* IEEE 802.11a, channels 34..48 */ + REG_RULE(5170-20, 5240+20, 40, 6, 20, + NL80211_RRF_PASSIVE_SCAN), + /* IEEE 802.11a, channels 52..64 */ + REG_RULE(5260-20, 5320+20, 40, 6, 20, + NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + } +}; + +const struct ieee80211_regdomain eu_regdom = { + .n_reg_rules = 6, + /* This alpha2 is bogus, we leave it here just for stupid + * backward compatibility */ + .alpha2 = "EU", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..13 */ + REG_RULE(2412-20, 2472+20, 40, 6, 20, 0), + /* IEEE 802.11a, channel 36 */ + REG_RULE(5180-20, 5180+20, 40, 6, 23, + NL80211_RRF_PASSIVE_SCAN), + /* IEEE 802.11a, channel 40 */ + REG_RULE(5200-20, 5200+20, 40, 6, 23, + NL80211_RRF_PASSIVE_SCAN), + /* IEEE 802.11a, channel 44 */ + REG_RULE(5220-20, 5220+20, 40, 6, 23, + NL80211_RRF_PASSIVE_SCAN), + /* IEEE 802.11a, channels 48..64 */ + REG_RULE(5240-20, 5320+20, 40, 6, 20, + NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + /* IEEE 802.11a, channels 100..140 */ + REG_RULE(5500-20, 5700+20, 40, 6, 30, + NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + } +}; + +#endif + +struct ieee80211_regdomain *cfg80211_world_regdom = + (struct ieee80211_regdomain *) &world_regdom; + +LIST_HEAD(regulatory_requests); +DEFINE_MUTEX(cfg80211_reg_mutex); + /* RCU might be appropriate here since we usually * only read the list, and that can happen quite * often because we need to do it for each command */ @@ -302,7 +405,9 @@ int wiphy_register(struct wiphy *wiphy) ieee80211_set_bitrate_flags(wiphy); /* set up regulatory info */ - wiphy_update_regulatory(wiphy); + mutex_lock(&cfg80211_reg_mutex); + wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE); + mutex_unlock(&cfg80211_reg_mutex); mutex_lock(&cfg80211_drv_mutex); @@ -409,9 +514,35 @@ static struct notifier_block cfg80211_netdev_notifier = { .notifier_call = cfg80211_netdev_notifier_call, }; +#ifdef CONFIG_WIRELESS_OLD_REGULATORY +const struct ieee80211_regdomain *static_regdom(char *alpha2) +{ + if (alpha2[0] == 'U' && alpha2[1] == 'S') + return &us_regdom; + if (alpha2[0] == 'J' && alpha2[1] == 'P') + return &jp_regdom; + if (alpha2[0] == 'E' && alpha2[1] == 'U') + return &eu_regdom; + /* Default, as per the old rules */ + return &us_regdom; +} +#endif + static int cfg80211_init(void) { - int err = wiphy_sysfs_init(); + int err; + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + cfg80211_regdomain = + (struct ieee80211_regdomain *) static_regdom(ieee80211_regdom); + /* Used during reset_regdomains_static() */ + cfg80211_world_regdom = cfg80211_regdomain; +#else + cfg80211_regdomain = + (struct ieee80211_regdomain *) cfg80211_world_regdom; +#endif + + err = wiphy_sysfs_init(); if (err) goto out_fail_sysfs; @@ -425,8 +556,33 @@ static int cfg80211_init(void) ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); + err = regulatory_init(); + if (err) + goto out_fail_reg; + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + printk(KERN_INFO "cfg80211: Using old static regulatory domain:\n"); + print_regdomain_info(cfg80211_regdomain); + /* The old code still requests for a new regdomain and if + * you have CRDA you get it updated, otherwise you get + * stuck with the static values. We ignore "EU" code as + * that is not a valid ISO / IEC 3166 alpha2 */ + if (ieee80211_regdom[0] != 'E' && + ieee80211_regdom[1] != 'U') + err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, + ieee80211_regdom, NULL); +#else + err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL); + if (err) + printk(KERN_ERR "cfg80211: calling CRDA failed - " + "unable to update world regulatory domain, " + "using static definition\n"); +#endif + return 0; +out_fail_reg: + debugfs_remove(ieee80211_debugfs_dir); out_fail_nl80211: unregister_netdevice_notifier(&cfg80211_netdev_notifier); out_fail_notifier: @@ -434,6 +590,7 @@ out_fail_notifier: out_fail_sysfs: return err; } + subsys_initcall(cfg80211_init); static void cfg80211_exit(void) @@ -442,5 +599,6 @@ static void cfg80211_exit(void) nl80211_exit(); unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); + regulatory_exit(); } module_exit(cfg80211_exit); diff --git a/net/wireless/core.h b/net/wireless/core.h index 7a02c356d63d..771cc5cc7658 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -79,6 +79,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); -void wiphy_update_regulatory(struct wiphy *wiphy); +void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 77880ba8b619..1221d726ed50 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -18,6 +18,7 @@ #include #include "core.h" #include "nl80211.h" +#include "reg.h" /* the netlink family */ static struct genl_family nl80211_fam = { @@ -88,6 +89,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = IEEE80211_MAX_MESH_ID_LEN }, [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, + [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, + [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, + [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, @@ -1599,6 +1603,141 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) return err; } +static const struct nla_policy + reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, +}; + +static int parse_reg_rule(struct nlattr *tb[], + struct ieee80211_reg_rule *reg_rule) +{ + struct ieee80211_freq_range *freq_range = ®_rule->freq_range; + struct ieee80211_power_rule *power_rule = ®_rule->power_rule; + + if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) + return -EINVAL; + if (!tb[NL80211_ATTR_FREQ_RANGE_START]) + return -EINVAL; + if (!tb[NL80211_ATTR_FREQ_RANGE_END]) + return -EINVAL; + if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) + return -EINVAL; + if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + return -EINVAL; + + reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); + + freq_range->start_freq_khz = + nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); + freq_range->end_freq_khz = + nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); + freq_range->max_bandwidth_khz = + nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); + + power_rule->max_eirp = + nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); + + if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) + power_rule->max_antenna_gain = + nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); + + return 0; +} + +static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) +{ + int r; + char *data = NULL; + + if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) + return -EINVAL; + + data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + /* We ignore world regdom requests with the old regdom setup */ + if (is_world_regdom(data)) + return -EINVAL; +#endif + mutex_lock(&cfg80211_drv_mutex); + r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL); + mutex_unlock(&cfg80211_drv_mutex); + return r; +} + +static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; + struct nlattr *nl_reg_rule; + char *alpha2 = NULL; + int rem_reg_rules = 0, r = 0; + u32 num_rules = 0, rule_idx = 0, size_of_regd; + struct ieee80211_regdomain *rd = NULL; + + if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_REG_RULES]) + return -EINVAL; + + alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); + + nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], + rem_reg_rules) { + num_rules++; + if (num_rules > NL80211_MAX_SUPP_REG_RULES) + goto bad_reg; + } + + if (!reg_is_valid_request(alpha2)) + return -EINVAL; + + size_of_regd = sizeof(struct ieee80211_regdomain) + + (num_rules * sizeof(struct ieee80211_reg_rule)); + + rd = kzalloc(size_of_regd, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + rd->n_reg_rules = num_rules; + rd->alpha2[0] = alpha2[0]; + rd->alpha2[1] = alpha2[1]; + + nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], + rem_reg_rules) { + nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, + nla_data(nl_reg_rule), nla_len(nl_reg_rule), + reg_rule_policy); + r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); + if (r) + goto bad_reg; + + rule_idx++; + + if (rule_idx > NL80211_MAX_SUPP_REG_RULES) + goto bad_reg; + } + + BUG_ON(rule_idx != num_rules); + + mutex_lock(&cfg80211_drv_mutex); + r = set_regdom(rd); + mutex_unlock(&cfg80211_drv_mutex); + if (r) + goto bad_reg; + + return r; + +bad_reg: + kfree(rd); + return -EINVAL; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -1736,6 +1875,18 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_REG, + .doit = nl80211_set_reg, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_REQ_SET_REG, + .doit = nl80211_req_set_reg, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; /* multicast groups */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 855bff4b3250..592b2e391d42 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2,179 +2,758 @@ * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2007 Johannes Berg + * Copyright 2008 Luis R. Rodriguez * * 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. */ -/* - * This regulatory domain control implementation is highly incomplete, it - * only exists for the purpose of not regressing mac80211. - * - * For now, drivers can restrict the set of allowed channels by either - * not registering those channels or setting the IEEE80211_CHAN_DISABLED - * flag; that flag will only be *set* by this code, never *cleared. +/** + * DOC: Wireless regulatory infrastructure * * The usual implementation is for a driver to read a device EEPROM to * determine which regulatory domain it should be operating under, then * looking up the allowable channels in a driver-local table and finally * registering those channels in the wiphy structure. * - * Alternatively, drivers that trust the regulatory domain control here - * will register a complete set of capabilities and the control code - * will restrict the set by setting the IEEE80211_CHAN_* flags. + * Another set of compliance enforcement is for drivers to use their + * own compliance limits which can be stored on the EEPROM. The host + * driver or firmware may ensure these are used. + * + * In addition to all this we provide an extra layer of regulatory + * conformance. For drivers which do not have any regulatory + * information CRDA provides the complete regulatory solution. + * For others it provides a community effort on further restrictions + * to enhance compliance. + * + * Note: When number of rules --> infinity we will not be able to + * index on alpha2 any more, instead we'll probably have to + * rely on some SHA1 checksum of the regdomain for example. + * */ #include +#include +#include +#include +#include #include +#include #include "core.h" +#include "reg.h" -static char *ieee80211_regdom = "US"; -module_param(ieee80211_regdom, charp, 0444); -MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); +/* To trigger userspace events */ +static struct platform_device *reg_pdev; -struct ieee80211_channel_range { - short start_freq; - short end_freq; - int max_power; - int max_antenna_gain; - u32 flags; +/* Keep the ordering from large to small */ +static u32 supported_bandwidths[] = { + MHZ_TO_KHZ(40), + MHZ_TO_KHZ(20), }; -struct ieee80211_regdomain { - const char *code; - const struct ieee80211_channel_range *ranges; - int n_ranges; -}; +bool is_world_regdom(char *alpha2) +{ + if (!alpha2) + return false; + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + return false; +} -#define RANGE_PWR(_start, _end, _pwr, _ag, _flags) \ - { _start, _end, _pwr, _ag, _flags } +static bool is_alpha2_set(char *alpha2) +{ + if (!alpha2) + return false; + if (alpha2[0] != 0 && alpha2[1] != 0) + return true; + return false; +} +static bool is_alpha_upper(char letter) +{ + /* ASCII A - Z */ + if (letter >= 65 && letter <= 90) + return true; + return false; +} -/* - * Ideally, in the future, these definitions will be loaded from a - * userspace table via some daemon. - */ -static const struct ieee80211_channel_range ieee80211_US_channels[] = { - /* IEEE 802.11b/g, channels 1..11 */ - RANGE_PWR(2412, 2462, 27, 6, 0), - /* IEEE 802.11a, channel 36*/ - RANGE_PWR(5180, 5180, 23, 6, 0), - /* IEEE 802.11a, channel 40*/ - RANGE_PWR(5200, 5200, 23, 6, 0), - /* IEEE 802.11a, channel 44*/ - RANGE_PWR(5220, 5220, 23, 6, 0), - /* IEEE 802.11a, channels 48..64 */ - RANGE_PWR(5240, 5320, 23, 6, 0), - /* IEEE 802.11a, channels 149..165, outdoor */ - RANGE_PWR(5745, 5825, 30, 6, 0), -}; +static bool is_unknown_alpha2(char *alpha2) +{ + if (!alpha2) + return false; + /* Special case where regulatory domain was built by driver + * but a specific alpha2 cannot be determined */ + if (alpha2[0] == '9' && alpha2[1] == '9') + return true; + return false; +} -static const struct ieee80211_channel_range ieee80211_JP_channels[] = { - /* IEEE 802.11b/g, channels 1..14 */ - RANGE_PWR(2412, 2484, 20, 6, 0), - /* IEEE 802.11a, channels 34..48 */ - RANGE_PWR(5170, 5240, 20, 6, IEEE80211_CHAN_PASSIVE_SCAN), - /* IEEE 802.11a, channels 52..64 */ - RANGE_PWR(5260, 5320, 20, 6, IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR), -}; +static bool is_an_alpha2(char *alpha2) +{ + if (!alpha2) + return false; + if (is_alpha_upper(alpha2[0]) && is_alpha_upper(alpha2[1])) + return true; + return false; +} -static const struct ieee80211_channel_range ieee80211_EU_channels[] = { - /* IEEE 802.11b/g, channels 1..13 */ - RANGE_PWR(2412, 2472, 20, 6, 0), - /* IEEE 802.11a, channel 36*/ - RANGE_PWR(5180, 5180, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), - /* IEEE 802.11a, channel 40*/ - RANGE_PWR(5200, 5200, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), - /* IEEE 802.11a, channel 44*/ - RANGE_PWR(5220, 5220, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), - /* IEEE 802.11a, channels 48..64 */ - RANGE_PWR(5240, 5320, 23, 6, IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR), - /* IEEE 802.11a, channels 100..140 */ - RANGE_PWR(5500, 5700, 30, 6, IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR), -}; +static bool alpha2_equal(char *alpha2_x, char *alpha2_y) +{ + if (!alpha2_x || !alpha2_y) + return false; + if (alpha2_x[0] == alpha2_y[0] && + alpha2_x[1] == alpha2_y[1]) + return true; + return false; +} + +static bool regdom_changed(char *alpha2) +{ + if (!cfg80211_regdomain) + return true; + if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) + return false; + return true; +} + +/* This lets us keep regulatory code which is updated on a regulatory + * basis in userspace. */ +static int call_crda(const char *alpha2) +{ + char country_env[9 + 2] = "COUNTRY="; + char *envp[] = { + country_env, + NULL + }; + + if (!is_world_regdom((char *) alpha2)) + printk(KERN_INFO "cfg80211: Calling CRDA for country: %c%c\n", + alpha2[0], alpha2[1]); + else +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + return -EINVAL; +#else + printk(KERN_INFO "cfg80211: Calling CRDA to update world " + "regulatory domain\n"); +#endif + + country_env[8] = alpha2[0]; + country_env[9] = alpha2[1]; + + return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); +} + +/* This has the logic which determines when a new request + * should be ignored. */ +static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, + char *alpha2, struct ieee80211_regdomain *rd) +{ + struct regulatory_request *last_request = NULL; -#define REGDOM(_code) \ - { \ - .code = __stringify(_code), \ - .ranges = ieee80211_ ##_code## _channels, \ - .n_ranges = ARRAY_SIZE(ieee80211_ ##_code## _channels), \ + /* All initial requests are respected */ + if (list_empty(®ulatory_requests)) + return 0; + + last_request = list_first_entry(®ulatory_requests, + struct regulatory_request, list); + + switch (set_by) { + case REGDOM_SET_BY_INIT: + return -EINVAL; + case REGDOM_SET_BY_CORE: + /* Always respect new wireless core hints, should only + * come in for updating the world regulatory domain at init + * anyway */ + return 0; + case REGDOM_SET_BY_COUNTRY_IE: + if (last_request->initiator == set_by) { + if (last_request->wiphy != wiphy) { + /* Two cards with two APs claiming different + * different Country IE alpha2s! + * You're special!! */ + if (!alpha2_equal(last_request->alpha2, + cfg80211_regdomain->alpha2)) { + /* XXX: Deal with conflict, consider + * building a new one out of the + * intersection */ + WARN_ON(1); + return -EOPNOTSUPP; + } + return -EALREADY; + } + /* Two consecutive Country IE hints on the same wiphy */ + if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) + return 0; + return -EALREADY; + } + if (WARN_ON(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2)), + "Invalid Country IE regulatory hint passed " + "to the wireless core\n") + return -EINVAL; + /* We ignore Country IE hints for now, as we haven't yet + * added the dot11MultiDomainCapabilityEnabled flag + * for wiphys */ + return 1; + case REGDOM_SET_BY_DRIVER: + BUG_ON(!wiphy); + if (last_request->initiator == set_by) { + /* Two separate drivers hinting different things, + * this is possible if you have two devices present + * on a system with different EEPROM regulatory + * readings. XXX: Do intersection, we support only + * the first regulatory hint for now */ + if (last_request->wiphy != wiphy) + return -EALREADY; + if (rd) + return -EALREADY; + /* Driver should not be trying to hint different + * regulatory domains! */ + BUG_ON(!alpha2_equal(alpha2, + cfg80211_regdomain->alpha2)); + return -EALREADY; + } + if (last_request->initiator == REGDOM_SET_BY_CORE) + return 0; + /* XXX: Handle intersection, and add the + * dot11MultiDomainCapabilityEnabled flag to wiphy. For now + * we assume the driver has this set to false, following the + * 802.11d dot11MultiDomainCapabilityEnabled documentation */ + if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) + return 0; + return 0; + case REGDOM_SET_BY_USER: + if (last_request->initiator == set_by || + last_request->initiator == REGDOM_SET_BY_CORE) + return 0; + /* Drivers can use their wiphy's reg_notifier() + * to override any information */ + if (last_request->initiator == REGDOM_SET_BY_DRIVER) + return 0; + /* XXX: Handle intersection */ + if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) + return -EOPNOTSUPP; + return 0; + default: + return -EINVAL; } +} -static const struct ieee80211_regdomain ieee80211_regdoms[] = { - REGDOM(US), - REGDOM(JP), - REGDOM(EU), -}; +static bool __reg_is_valid_request(char *alpha2, + struct regulatory_request **request) +{ + struct regulatory_request *req; + if (list_empty(®ulatory_requests)) + return false; + list_for_each_entry(req, ®ulatory_requests, list) { + if (alpha2_equal(req->alpha2, alpha2)) { + *request = req; + return true; + } + } + return false; +} +/* Used by nl80211 before kmalloc'ing our regulatory domain */ +bool reg_is_valid_request(char *alpha2) +{ + struct regulatory_request *request = NULL; + return __reg_is_valid_request(alpha2, &request); +} -static const struct ieee80211_regdomain *get_regdom(void) +/* Sanity check on a regulatory rule */ +static bool is_valid_reg_rule(struct ieee80211_reg_rule *rule) { - static const struct ieee80211_channel_range - ieee80211_world_channels[] = { - /* IEEE 802.11b/g, channels 1..11 */ - RANGE_PWR(2412, 2462, 27, 6, 0), - }; - static const struct ieee80211_regdomain regdom_world = REGDOM(world); - int i; + struct ieee80211_freq_range *freq_range = &rule->freq_range; + u32 freq_diff; + + if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0) + return false; + + if (freq_range->start_freq_khz > freq_range->end_freq_khz) + return false; + + freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; + + if (freq_range->max_bandwidth_khz > freq_diff) + return false; + + return true; +} + +static bool is_valid_rd(struct ieee80211_regdomain *rd) +{ + struct ieee80211_reg_rule *reg_rule = NULL; + unsigned int i; - for (i = 0; i < ARRAY_SIZE(ieee80211_regdoms); i++) - if (strcmp(ieee80211_regdom, ieee80211_regdoms[i].code) == 0) - return &ieee80211_regdoms[i]; + if (!rd->n_reg_rules) + return false; - return ®dom_world; + for (i = 0; i < rd->n_reg_rules; i++) { + reg_rule = &rd->reg_rules[i]; + if (!is_valid_reg_rule(reg_rule)) + return false; + } + + return true; } +/* Returns value in KHz */ +static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, + u32 freq) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) { + u32 start_freq_khz = freq - supported_bandwidths[i]/2; + u32 end_freq_khz = freq + supported_bandwidths[i]/2; + if (start_freq_khz >= freq_range->start_freq_khz && + end_freq_khz <= freq_range->end_freq_khz) + return supported_bandwidths[i]; + } + return 0; +} -static void handle_channel(struct ieee80211_channel *chan, - const struct ieee80211_regdomain *rd) +/* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may + * want to just have the channel structure use these */ +static u32 map_regdom_flags(u32 rd_flags) +{ + u32 channel_flags = 0; + if (rd_flags & NL80211_RRF_PASSIVE_SCAN) + channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; + if (rd_flags & NL80211_RRF_NO_IBSS) + channel_flags |= IEEE80211_CHAN_NO_IBSS; + if (rd_flags & NL80211_RRF_DFS) + channel_flags |= IEEE80211_CHAN_RADAR; + return channel_flags; +} + +/** + * freq_reg_info - get regulatory information for the given frequency + * @center_freq: Frequency in KHz for which we want regulatory information for + * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one + * you can set this to 0. If this frequency is allowed we then set + * this value to the maximum allowed bandwidth. + * @reg_rule: the regulatory rule which we have for this frequency + * + * Use this function to get the regulatory rule for a specific frequency. + */ +static int freq_reg_info(u32 center_freq, u32 *bandwidth, + const struct ieee80211_reg_rule **reg_rule) { int i; - u32 flags = chan->orig_flags; - const struct ieee80211_channel_range *rg = NULL; + u32 max_bandwidth = 0; - for (i = 0; i < rd->n_ranges; i++) { - if (rd->ranges[i].start_freq <= chan->center_freq && - chan->center_freq <= rd->ranges[i].end_freq) { - rg = &rd->ranges[i]; + if (!cfg80211_regdomain) + return -EINVAL; + + for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { + const struct ieee80211_reg_rule *rr; + const struct ieee80211_freq_range *fr = NULL; + const struct ieee80211_power_rule *pr = NULL; + + rr = &cfg80211_regdomain->reg_rules[i]; + fr = &rr->freq_range; + pr = &rr->power_rule; + max_bandwidth = freq_max_bandwidth(fr, center_freq); + if (max_bandwidth && *bandwidth <= max_bandwidth) { + *reg_rule = rr; + *bandwidth = max_bandwidth; break; } } - if (!rg) { - /* not found */ + return !max_bandwidth; +} + +static void handle_channel(struct ieee80211_channel *chan) +{ + int r; + u32 flags = chan->orig_flags; + u32 max_bandwidth = 0; + const struct ieee80211_reg_rule *reg_rule = NULL; + const struct ieee80211_power_rule *power_rule = NULL; + + r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq), + &max_bandwidth, ®_rule); + + if (r) { flags |= IEEE80211_CHAN_DISABLED; chan->flags = flags; return; } - chan->flags = flags; + power_rule = ®_rule->power_rule; + + chan->flags = flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = min(chan->orig_mag, - rg->max_antenna_gain); + (int) MBI_TO_DBI(power_rule->max_antenna_gain)); + chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); if (chan->orig_mpwr) - chan->max_power = min(chan->orig_mpwr, rg->max_power); + chan->max_power = min(chan->orig_mpwr, + (int) MBM_TO_DBM(power_rule->max_eirp)); else - chan->max_power = rg->max_power; + chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); } -static void handle_band(struct ieee80211_supported_band *sband, - const struct ieee80211_regdomain *rd) +static void handle_band(struct ieee80211_supported_band *sband) { int i; for (i = 0; i < sband->n_channels; i++) - handle_channel(&sband->channels[i], rd); + handle_channel(&sband->channels[i]); } -void wiphy_update_regulatory(struct wiphy *wiphy) +static void update_all_wiphy_regulatory(enum reg_set_by setby) { - enum ieee80211_band band; - const struct ieee80211_regdomain *rd = get_regdom(); + struct cfg80211_registered_device *drv; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) + list_for_each_entry(drv, &cfg80211_drv_list, list) + wiphy_update_regulatory(&drv->wiphy, setby); +} + +void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) +{ + enum ieee80211_band band; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) - handle_band(wiphy->bands[band], rd); + handle_band(wiphy->bands[band]); + if (wiphy->reg_notifier) + wiphy->reg_notifier(wiphy, setby); + } +} + +/* Caller must hold &cfg80211_drv_mutex */ +int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, + const char *alpha2, struct ieee80211_regdomain *rd) +{ + struct regulatory_request *request; + char *rd_alpha2; + int r = 0; + + r = ignore_request(wiphy, set_by, (char *) alpha2, rd); + if (r) + return r; + + if (rd) + rd_alpha2 = rd->alpha2; + else + rd_alpha2 = (char *) alpha2; + + switch (set_by) { + case REGDOM_SET_BY_CORE: + case REGDOM_SET_BY_COUNTRY_IE: + case REGDOM_SET_BY_DRIVER: + case REGDOM_SET_BY_USER: + request = kzalloc(sizeof(struct regulatory_request), + GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->alpha2[0] = rd_alpha2[0]; + request->alpha2[1] = rd_alpha2[1]; + request->initiator = set_by; + request->wiphy = wiphy; + + list_add_tail(&request->list, ®ulatory_requests); + if (rd) + break; + r = call_crda(alpha2); +#ifndef CONFIG_WIRELESS_OLD_REGULATORY + if (r) + printk(KERN_ERR "cfg80211: Failed calling CRDA\n"); +#endif + break; + default: + r = -ENOTSUPP; + break; + } + + return r; +} + +/* If rd is not NULL and if this call fails the caller must free it */ +int regulatory_hint(struct wiphy *wiphy, const char *alpha2, + struct ieee80211_regdomain *rd) +{ + int r; + BUG_ON(!rd && !alpha2); + + mutex_lock(&cfg80211_drv_mutex); + + r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd); + if (r || !rd) + goto unlock_and_exit; + + /* If the driver passed a regulatory domain we skipped asking + * userspace for one so we can now go ahead and set it */ + r = set_regdom(rd); + +unlock_and_exit: + mutex_unlock(&cfg80211_drv_mutex); + return r; +} +EXPORT_SYMBOL(regulatory_hint); + + +static void print_rd_rules(struct ieee80211_regdomain *rd) +{ + unsigned int i; + struct ieee80211_reg_rule *reg_rule = NULL; + struct ieee80211_freq_range *freq_range = NULL; + struct ieee80211_power_rule *power_rule = NULL; + + printk(KERN_INFO "\t(start_freq - end_freq @ bandwidth), " + "(max_antenna_gain, max_eirp)\n"); + + for (i = 0; i < rd->n_reg_rules; i++) { + reg_rule = &rd->reg_rules[i]; + freq_range = ®_rule->freq_range; + power_rule = ®_rule->power_rule; + + /* There may not be documentation for max antenna gain + * in certain regions */ + if (power_rule->max_antenna_gain) + printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), " + "(%d mBi, %d mBm)\n", + freq_range->start_freq_khz, + freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, + power_rule->max_antenna_gain, + power_rule->max_eirp); + else + printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), " + "(N/A, %d mBm)\n", + freq_range->start_freq_khz, + freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, + power_rule->max_eirp); + } +} + +static void print_regdomain(struct ieee80211_regdomain *rd) +{ + + if (is_world_regdom(rd->alpha2)) + printk(KERN_INFO "cfg80211: World regulatory " + "domain updated:\n"); + else { + if (is_unknown_alpha2(rd->alpha2)) + printk(KERN_INFO "cfg80211: Regulatory domain " + "changed to driver built-in settings " + "(unknown country)\n"); + else + printk(KERN_INFO "cfg80211: Regulatory domain " + "changed to country: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); + } + print_rd_rules(rd); +} + +void print_regdomain_info(struct ieee80211_regdomain *rd) +{ + printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); + print_rd_rules(rd); +} + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + +static bool is_old_static_regdom(struct ieee80211_regdomain *rd) +{ + if (rd == &us_regdom || rd == &jp_regdom || rd == &eu_regdom) + return true; + return false; +} + +/* The old crap never deals with a world regulatory domain, it only + * deals with the static regulatory domain passed and if possible + * an updated "US" or "JP" regulatory domain. We do however store the + * old static regulatory domain in cfg80211_world_regdom for convenience + * of use here */ +static void reset_regdomains_static(void) +{ + if (!is_old_static_regdom(cfg80211_regdomain)) + kfree(cfg80211_regdomain); + /* This is setting the regdom to the old static regdom */ + cfg80211_regdomain = + (struct ieee80211_regdomain *) cfg80211_world_regdom; +} +#else +static void reset_regdomains(void) +{ + if (cfg80211_world_regdom && cfg80211_world_regdom != &world_regdom) { + if (cfg80211_world_regdom == cfg80211_regdomain) { + kfree(cfg80211_regdomain); + } else { + kfree(cfg80211_world_regdom); + kfree(cfg80211_regdomain); + } + } else if (cfg80211_regdomain && cfg80211_regdomain != &world_regdom) + kfree(cfg80211_regdomain); + + cfg80211_world_regdom = (struct ieee80211_regdomain *) &world_regdom; + cfg80211_regdomain = NULL; +} + +/* Dynamic world regulatory domain requested by the wireless + * core upon initialization */ +static void update_world_regdomain(struct ieee80211_regdomain *rd) +{ + BUG_ON(list_empty(®ulatory_requests)); + + reset_regdomains(); + + cfg80211_world_regdom = rd; + cfg80211_regdomain = rd; +} +#endif + +static int __set_regdom(struct ieee80211_regdomain *rd) +{ + struct regulatory_request *request = NULL; + + /* Some basic sanity checks first */ + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + /* We ignore the world regdom with the old static regdomains setup + * as there is no point to it with satic regulatory definitions :( + * Don't worry this shit will be removed soon... */ + if (is_world_regdom(rd->alpha2)) + return -EINVAL; +#else + if (is_world_regdom(rd->alpha2)) { + if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) + return -EINVAL; + update_world_regdomain(rd); + return 0; + } +#endif + + if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && + !is_unknown_alpha2(rd->alpha2)) + return -EINVAL; + + if (list_empty(®ulatory_requests)) + return -EINVAL; + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + /* Static "US" and "JP" will be overridden, but just once */ + if (!is_old_static_regdom(cfg80211_regdomain) && + !regdom_changed(rd->alpha2)) + return -EINVAL; +#else + if (!regdom_changed(rd->alpha2)) + return -EINVAL; +#endif + + /* Now lets set the regulatory domain, update all driver channels + * and finally inform them of what we have done, in case they want + * to review or adjust their own settings based on their own + * internal EEPROM data */ + + if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) + return -EINVAL; + +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + reset_regdomains_static(); +#else + reset_regdomains(); +#endif + + /* Country IE parsing coming soon */ + switch (request->initiator) { + case REGDOM_SET_BY_CORE: + case REGDOM_SET_BY_DRIVER: + case REGDOM_SET_BY_USER: + if (!is_valid_rd(rd)) { + printk(KERN_ERR "cfg80211: Invalid " + "regulatory domain detected:\n"); + print_regdomain_info(rd); + return -EINVAL; + } + break; + case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */ + WARN_ON(1); + default: + return -EOPNOTSUPP; + } + + /* Tada! */ + cfg80211_regdomain = rd; + request->granted = 1; + + return 0; +} + + +/* Use this call to set the current regulatory domain. Conflicts with + * multiple drivers can be ironed out later. Caller must've already + * kmalloc'd the rd structure. If this calls fails you should kfree() + * the passed rd. Caller must hold cfg80211_drv_mutex */ +int set_regdom(struct ieee80211_regdomain *rd) +{ + struct regulatory_request *this_request = NULL, *prev_request = NULL; + int r; + + if (!list_empty(®ulatory_requests)) + prev_request = list_first_entry(®ulatory_requests, + struct regulatory_request, list); + + /* Note that this doesn't update the wiphys, this is done below */ + r = __set_regdom(rd); + if (r) + return r; + + BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request))); + + /* The initial standard core update of the world regulatory domain, no + * need to keep that request info around if it didn't fail. */ + if (is_world_regdom(rd->alpha2) && + this_request->initiator == REGDOM_SET_BY_CORE && + this_request->granted) { + list_del(&this_request->list); + kfree(this_request); + this_request = NULL; + } + + /* Remove old requests, we only leave behind the last one */ + if (prev_request) { + list_del(&prev_request->list); + kfree(prev_request); + prev_request = NULL; + } + + /* This would make this whole thing pointless */ + BUG_ON(rd != cfg80211_regdomain); + + /* update all wiphys now with the new established regulatory domain */ + update_all_wiphy_regulatory(this_request->initiator); + + print_regdomain(rd); + + return r; +} + +int regulatory_init(void) +{ + reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); + if (IS_ERR(reg_pdev)) + return PTR_ERR(reg_pdev); + return 0; +} + +void regulatory_exit(void) +{ + struct regulatory_request *req, *req_tmp; + mutex_lock(&cfg80211_drv_mutex); +#ifdef CONFIG_WIRELESS_OLD_REGULATORY + reset_regdomains_static(); +#else + reset_regdomains(); +#endif + list_for_each_entry_safe(req, req_tmp, ®ulatory_requests, list) { + list_del(&req->list); + kfree(req); + } + platform_device_unregister(reg_pdev); + mutex_unlock(&cfg80211_drv_mutex); } diff --git a/net/wireless/reg.h b/net/wireless/reg.h new file mode 100644 index 000000000000..d75fd0232972 --- /dev/null +++ b/net/wireless/reg.h @@ -0,0 +1,44 @@ +#ifndef __NET_WIRELESS_REG_H +#define __NET_WIRELESS_REG_H + +extern const struct ieee80211_regdomain world_regdom; +#ifdef CONFIG_WIRELESS_OLD_REGULATORY +extern const struct ieee80211_regdomain us_regdom; +extern const struct ieee80211_regdomain jp_regdom; +extern const struct ieee80211_regdomain eu_regdom; +#endif + +extern struct ieee80211_regdomain *cfg80211_regdomain; +extern struct ieee80211_regdomain *cfg80211_world_regdom; +extern struct list_head regulatory_requests; + +struct regdom_last_setby { + struct wiphy *wiphy; + u8 initiator; +}; + +/* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */ +struct regulatory_request { + struct list_head list; + struct wiphy *wiphy; + int granted; + enum reg_set_by initiator; + char alpha2[2]; +}; + +bool is_world_regdom(char *alpha2); +bool reg_is_valid_request(char *alpha2); + +int set_regdom(struct ieee80211_regdomain *rd); +int __regulatory_hint_alpha2(struct wiphy *wiphy, enum reg_set_by set_by, + const char *alpha2); + +int regulatory_init(void); +void regulatory_exit(void); + +void print_regdomain_info(struct ieee80211_regdomain *); + +/* If a char is A-Z */ +#define IS_ALPHA(letter) (letter >= 65 && letter <= 90) + +#endif /* __NET_WIRELESS_REG_H */ -- cgit v1.2.3 From 29bdc88384c2b24e37e5760df0dc898546083d6b Mon Sep 17 00:00:00 2001 From: Vladimir Sokolovsky Date: Mon, 15 Sep 2008 14:25:23 -0700 Subject: IB/mlx4: Fix up fast register page list format Byte swap the addresses in the page list for fast register work requests to big endian to match what the HCA expectx. Also, the addresses must have the "present" bit set so that the HCA knows it can access them. Otherwise the HCA will fault the first time it accesses the memory region. Signed-off-by: Vladimir Sokolovsky Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/qp.c | 6 ++++++ drivers/net/mlx4/mr.c | 2 -- include/linux/mlx4/device.h | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index f29dbb767e87..9559248f265b 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1342,6 +1342,12 @@ static __be32 convert_access(int acc) static void set_fmr_seg(struct mlx4_wqe_fmr_seg *fseg, struct ib_send_wr *wr) { struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(wr->wr.fast_reg.page_list); + int i; + + for (i = 0; i < wr->wr.fast_reg.page_list_len; ++i) + wr->wr.fast_reg.page_list->page_list[i] = + cpu_to_be64(wr->wr.fast_reg.page_list->page_list[i] | + MLX4_MTT_FLAG_PRESENT); fseg->flags = convert_access(wr->wr.fast_reg.access_flags); fseg->mem_key = cpu_to_be32(wr->wr.fast_reg.rkey); diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index 644adf074a35..d1dd5b48dbd1 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -71,8 +71,6 @@ struct mlx4_mpt_entry { #define MLX4_MPT_PD_FLAG_RAE (1 << 28) #define MLX4_MPT_PD_FLAG_EN_INV (3 << 24) -#define MLX4_MTT_FLAG_PRESENT 1 - #define MLX4_MPT_STATUS_SW 0xF0 #define MLX4_MPT_STATUS_HW 0x00 diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 655ea0d1ee14..b2f944468313 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -141,6 +141,10 @@ enum { MLX4_STAT_RATE_OFFSET = 5 }; +enum { + MLX4_MTT_FLAG_PRESENT = 1 +}; + static inline u64 mlx4_fw_ver(u64 major, u64 minor, u64 subminor) { return (major << 32) | (minor << 16) | subminor; -- cgit v1.2.3 From b08508c40adf3fd1330aabc4f37d3254179776c4 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Tue, 26 Aug 2008 08:20:34 -0700 Subject: PCI: fix compiler warnings in pci_get_subsys() pci_get_subsys() changed in 2.6.26 so that the from pointer is modified when the call is being invoked, so fix up the 'const' marking of it that the compiler is complaining about. Reported-by: Rufus & Azrael Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jesse Barnes --- drivers/pci/search.c | 6 +++--- include/linux/pci.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 3b3b5f178797..4edfc4731bd4 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -162,7 +162,7 @@ EXPORT_SYMBOL(pci_find_slot); * time. */ struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, - const struct pci_dev *from) + struct pci_dev *from) { struct pci_dev *pdev; @@ -263,7 +263,7 @@ static int match_pci_dev_by_id(struct device *dev, void *data) * this file. */ static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id, - const struct pci_dev *from) + struct pci_dev *from) { struct device *dev; struct device *dev_start = NULL; @@ -303,7 +303,7 @@ static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id, */ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, - const struct pci_dev *from) + struct pci_dev *from) { struct pci_dev *pdev; struct pci_device_id *id; diff --git a/include/linux/pci.h b/include/linux/pci.h index c0e14008a3c2..98dc6243a706 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -534,7 +534,7 @@ extern void pci_sort_breadthfirst(void); #ifdef CONFIG_PCI_LEGACY struct pci_dev __deprecated *pci_find_device(unsigned int vendor, unsigned int device, - const struct pci_dev *from); + struct pci_dev *from); struct pci_dev __deprecated *pci_find_slot(unsigned int bus, unsigned int devfn); #endif /* CONFIG_PCI_LEGACY */ @@ -550,7 +550,7 @@ struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from); struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, - const struct pci_dev *from); + struct pci_dev *from); struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn); struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn); struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from); @@ -816,7 +816,7 @@ _PCI_NOP_ALL(write,) static inline struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, - const struct pci_dev *from) + struct pci_dev *from) { return NULL; } @@ -838,7 +838,7 @@ static inline struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, - const struct pci_dev *from) + struct pci_dev *from) { return NULL; } -- cgit v1.2.3 From ef3d7714f6b75b51825ad0384b5ce48358427e50 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 16 Sep 2008 15:00:11 -0700 Subject: Fix PNP build failure, bugzilla #11276 This fill fix the following regression list entry: Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=11276 Subject : build error: CONFIG_OPTIMIZE_INLINING=y causes gcc 4.2 to do stupid things Submitter : Randy Dunlap Date : 2008-08-06 17:18 (38 days old) References : http://marc.info/?l=linux-kernel&m=121804329014332&w=4 http://lkml.org/lkml/2008/7/22/353 Handled-By : Bjorn Helgaas Patch : http://lkml.org/lkml/2008/7/22/364 with what I believe is a better fix than the one referenced in the regression entry above. These PNP header interfaces try to work in such a way that you can reference some of them even if PNP is not enabled, and the compiler was expected to optimize everything away. Which is mostly fine, except that there was one interface for which there was not provided an inline "NOP" implementation. Once we add that, all of these compile failures cannot handle any more. pnp: Provide NOP inline implementation of pnp_get_resource() when !PNP Fixes kernel bugzilla #11276. Signed-off-by: David S. Miller Signed-off-by: Linus Torvalds --- include/linux/pnp.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 1ce54b63085d..be764e514e35 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -21,7 +21,14 @@ struct pnp_dev; /* * Resource Management */ +#ifdef CONFIG_PNP struct resource *pnp_get_resource(struct pnp_dev *, unsigned int, unsigned int); +#else +static inline struct resource *pnp_get_resource(struct pnp_dev *dev, unsigned int type, unsigned int num) +{ + return NULL; +} +#endif static inline int pnp_resource_valid(struct resource *res) { -- cgit v1.2.3 From 6ae19b04ab41a4db0f0c48ec0b78950f6b028823 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Wed, 10 Sep 2008 12:06:15 -0400 Subject: Input: ads7846 - introduce .gpio_pendown to get pendown state The GPIO connected to ADS7846 nPENIRQ signal is usually used to get the pendown state as well. Introduce a .gpio_pendown, and use this to decide the pendown state if .get_pendown_state is NULL. Signed-off-by: Eric Miao Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 68 +++++++++++++++++++++++++++++-------- include/linux/spi/ads7846.h | 3 ++ 2 files changed, 57 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ce6f48c695f5..8583c766d565 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,7 @@ struct ads7846 { void *filter_data; void (*filter_cleanup)(void *data); int (*get_pendown_state)(void); + int gpio_pendown; }; /* leave chip selected when we're done, for quicker re-select? */ @@ -491,6 +493,14 @@ static struct attribute_group ads784x_attr_group = { /*--------------------------------------------------------------------------*/ +static int get_pendown_state(struct ads7846 *ts) +{ + if (ts->get_pendown_state) + return ts->get_pendown_state(); + + return !gpio_get_value(ts->gpio_pendown); +} + /* * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, * to retrieve touchscreen status. @@ -550,7 +560,7 @@ static void ads7846_rx(void *ads) */ if (ts->penirq_recheck_delay_usecs) { udelay(ts->penirq_recheck_delay_usecs); - if (!ts->get_pendown_state()) + if (!get_pendown_state(ts)) Rt = 0; } @@ -677,7 +687,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) spin_lock_irq(&ts->lock); - if (unlikely(!ts->get_pendown_state() || + if (unlikely(!get_pendown_state(ts) || device_suspended(&ts->spi->dev))) { if (ts->pendown) { struct input_dev *input = ts->input; @@ -716,7 +726,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle) unsigned long flags; spin_lock_irqsave(&ts->lock, flags); - if (likely(ts->get_pendown_state())) { + if (likely(get_pendown_state(ts))) { if (!ts->irq_disabled) { /* The ARM do_simple_IRQ() dispatcher doesn't act * like the other dispatchers: it will report IRQs @@ -806,6 +816,36 @@ static int ads7846_resume(struct spi_device *spi) return 0; } +static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) +{ + struct ads7846_platform_data *pdata = spi->dev.platform_data; + int err; + + /* REVISIT when the irq can be triggered active-low, or if for some + * reason the touchscreen isn't hooked up, we don't need to access + * the pendown state. + */ + if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) { + dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); + return -EINVAL; + } + + if (pdata->get_pendown_state) { + ts->get_pendown_state = pdata->get_pendown_state; + return 0; + } + + err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); + if (err) { + dev_err(&spi->dev, "failed to request pendown GPIO%d\n", + pdata->gpio_pendown); + return err; + } + + ts->gpio_pendown = pdata->gpio_pendown; + return 0; +} + static int __devinit ads7846_probe(struct spi_device *spi) { struct ads7846 *ts; @@ -833,15 +873,6 @@ static int __devinit ads7846_probe(struct spi_device *spi) return -EINVAL; } - /* REVISIT when the irq can be triggered active-low, or if for some - * reason the touchscreen isn't hooked up, we don't need to access - * the pendown state. - */ - if (pdata->get_pendown_state == NULL) { - dev_dbg(&spi->dev, "no get_pendown_state function?\n"); - return -EINVAL; - } - /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except * that even if the hardware can do that, the SPI controller driver * may not. So we stick to very-portable 8 bit words, both RX and TX. @@ -893,7 +924,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->filter_data = ts; } else ts->filter = ads7846_no_filter; - ts->get_pendown_state = pdata->get_pendown_state; + + err = setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; if (pdata->penirq_recheck_delay_usecs) ts->penirq_recheck_delay_usecs = @@ -1085,7 +1119,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi->dev.driver->name, ts)) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); err = -EBUSY; - goto err_cleanup_filter; + goto err_free_gpio; } err = ads784x_hwmon_register(spi, ts); @@ -1116,6 +1150,9 @@ static int __devinit ads7846_probe(struct spi_device *spi) ads784x_hwmon_unregister(spi, ts); err_free_irq: free_irq(spi->irq, ts); + err_free_gpio: + if (ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); err_cleanup_filter: if (ts->filter_cleanup) ts->filter_cleanup(ts->filter_data); @@ -1140,6 +1177,9 @@ static int __devexit ads7846_remove(struct spi_device *spi) /* suspend left the IRQ disabled */ enable_irq(ts->spi->irq); + if (ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); + if (ts->filter_cleanup) ts->filter_cleanup(ts->filter_data); diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index daf744017a31..05eab2f11e63 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -43,6 +43,9 @@ struct ads7846_platform_data { u16 debounce_tol; /* tolerance used for filtering */ u16 debounce_rep; /* additional consecutive good readings * required after the first two */ + int gpio_pendown; /* the GPIO used to decide the pendown + * state if get_pendown_state == NULL + */ int (*get_pendown_state)(void); int (*filter_init) (struct ads7846_platform_data *pdata, void **filter_data); -- cgit v1.2.3 From 452c1ce218a68b5dbd626397ecfc65ca89dd3cbb Mon Sep 17 00:00:00 2001 From: Chris Snook Date: Sun, 14 Sep 2008 19:56:10 -0500 Subject: atl2: add atl2 driver Driver for Atheros L2 10/100 network device. Includes necessary changes for Kconfig, Makefile, and pci_ids.h. Signed-off-by: Chris Snook Signed-off-by: Jay Cliburn Signed-off-by: Jeff Garzik --- drivers/net/Kconfig | 11 + drivers/net/Makefile | 1 + drivers/net/atlx/Makefile | 2 + drivers/net/atlx/atl2.c | 3127 +++++++++++++++++++++++++++++++++++++++++++++ drivers/net/atlx/atl2.h | 530 ++++++++ include/linux/pci_ids.h | 1 + 6 files changed, 3672 insertions(+) create mode 100644 drivers/net/atlx/atl2.c create mode 100644 drivers/net/atlx/atl2.h (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 4a11296a9514..81a3e959c6c3 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1840,6 +1840,17 @@ config NE_H8300 Say Y here if you want to use the NE2000 compatible controller on the Renesas H8/300 processor. +config ATL2 + tristate "Atheros L2 Fast Ethernet support" + depends on PCI + select CRC32 + select MII + help + This driver supports the Atheros L2 fast ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl2. + source "drivers/net/fs_enet/Kconfig" endif # NET_ETHERNET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f66b79bd3b89..9221346a515e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_EHEA) += ehea/ obj-$(CONFIG_CAN) += can/ obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_ATL1) += atlx/ +obj-$(CONFIG_ATL2) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_TEHUTI) += tehuti.o diff --git a/drivers/net/atlx/Makefile b/drivers/net/atlx/Makefile index ca45553a040d..e4f6022ca552 100644 --- a/drivers/net/atlx/Makefile +++ b/drivers/net/atlx/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_ATL1) += atl1.o +obj-$(CONFIG_ATL2) += atl2.o + diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c new file mode 100644 index 000000000000..d548a67da1e8 --- /dev/null +++ b/drivers/net/atlx/atl2.c @@ -0,0 +1,3127 @@ +/* + * Copyright(c) 2006 - 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2007 - 2008 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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. + * + * 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, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atl2.h" + +#define ATL2_DRV_VERSION "2.2.3" + +static char atl2_driver_name[] = "atl2"; +static const char atl2_driver_string[] = "Atheros(R) L2 Ethernet Driver"; +static char atl2_copyright[] = "Copyright (c) 2007 Atheros Corporation."; +static char atl2_driver_version[] = ATL2_DRV_VERSION; + +MODULE_AUTHOR("Atheros Corporation , Chris Snook "); +MODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ATL2_DRV_VERSION); + +/* + * atl2_pci_tbl - PCI Device ID Table + */ +static struct pci_device_id atl2_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)}, + /* required last entry */ + {0,} +}; +MODULE_DEVICE_TABLE(pci, atl2_pci_tbl); + +static void atl2_set_ethtool_ops(struct net_device *netdev); + +static void atl2_check_options(struct atl2_adapter *adapter); + +/* + * atl2_sw_init - Initialize general software structures (struct atl2_adapter) + * @adapter: board private structure to initialize + * + * atl2_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + */ +static int __devinit atl2_sw_init(struct atl2_adapter *adapter) +{ + struct atl2_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + + /* PCI config space info */ + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + hw->subsystem_vendor_id = pdev->subsystem_vendor; + hw->subsystem_id = pdev->subsystem_device; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id); + pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); + + adapter->wol = 0; + adapter->ict = 50000; /* ~100ms */ + adapter->link_speed = SPEED_0; /* hardware init */ + adapter->link_duplex = FULL_DUPLEX; + + hw->phy_configured = false; + hw->preamble_len = 7; + hw->ipgt = 0x60; + hw->min_ifg = 0x50; + hw->ipgr1 = 0x40; + hw->ipgr2 = 0x60; + hw->retry_buf = 2; + hw->max_retry = 0xf; + hw->lcol = 0x37; + hw->jam_ipg = 7; + hw->fc_rxd_hi = 0; + hw->fc_rxd_lo = 0; + hw->max_frame_size = adapter->netdev->mtu; + + spin_lock_init(&adapter->stats_lock); + spin_lock_init(&adapter->tx_lock); + + set_bit(__ATL2_DOWN, &adapter->flags); + + return 0; +} + +/* + * atl2_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + */ +static void atl2_set_multi(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + struct dev_mc_list *mc_ptr; + u32 rctl; + u32 hash_value; + + /* Check for Promiscuous and All Multicast modes */ + rctl = ATL2_READ_REG(hw, REG_MAC_CTRL); + + if (netdev->flags & IFF_PROMISC) { + rctl |= MAC_CTRL_PROMIS_EN; + } else if (netdev->flags & IFF_ALLMULTI) { + rctl |= MAC_CTRL_MC_ALL_EN; + rctl &= ~MAC_CTRL_PROMIS_EN; + } else + rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); + + ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl); + + /* clear the old settings from the multicast hash table */ + ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + /* comoute mc addresses' hash value ,and put it into hash table */ + for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = atl2_hash_mc_addr(hw, mc_ptr->dmi_addr); + atl2_hash_set(hw, hash_value); + } +} + +static void init_ring_ptrs(struct atl2_adapter *adapter) +{ + /* Read / Write Ptr Initialize: */ + adapter->txd_write_ptr = 0; + atomic_set(&adapter->txd_read_ptr, 0); + + adapter->rxd_read_ptr = 0; + adapter->rxd_write_ptr = 0; + + atomic_set(&adapter->txs_write_ptr, 0); + adapter->txs_next_clear = 0; +} + +/* + * atl2_configure - Configure Transmit&Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Tx /Rx unit of the MAC after a reset. + */ +static int atl2_configure(struct atl2_adapter *adapter) +{ + struct atl2_hw *hw = &adapter->hw; + u32 value; + + /* clear interrupt status */ + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff); + + /* set MAC Address */ + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8) | + (((u32)hw->mac_addr[5])); + ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value); + value = (((u32)hw->mac_addr[0]) << 8) | + (((u32)hw->mac_addr[1])); + ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value); + + /* HI base address */ + ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, + (u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32)); + + /* LO base address */ + ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO, + (u32)(adapter->txd_dma & 0x00000000ffffffffULL)); + ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO, + (u32)(adapter->txs_dma & 0x00000000ffffffffULL)); + ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO, + (u32)(adapter->rxd_dma & 0x00000000ffffffffULL)); + + /* element count */ + ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4)); + ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size); + ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM, (u16)adapter->rxd_ring_size); + + /* config Internal SRAM */ +/* + ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end); + ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end); +*/ + + /* config IPG/IFG */ + value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) << + MAC_IPG_IFG_IPGT_SHIFT) | + (((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) << + MAC_IPG_IFG_MIFG_SHIFT) | + (((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) << + MAC_IPG_IFG_IPGR1_SHIFT)| + (((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) << + MAC_IPG_IFG_IPGR2_SHIFT); + ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value); + + /* config Half-Duplex Control */ + value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | + (((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) << + MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | + MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | + (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | + (((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) << + MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); + ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value); + + /* set Interrupt Moderator Timer */ + ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt); + ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN); + + /* set Interrupt Clear Timer */ + ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict); + + /* set MTU */ + ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu + + ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE); + + /* 1590 */ + ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177); + + /* flow control */ + ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi); + ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo); + + /* Init mailbox */ + ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr); + ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr); + + /* enable DMA read/write */ + ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN); + ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN); + + value = ATL2_READ_REG(&adapter->hw, REG_ISR); + if ((value & ISR_PHY_LINKDOWN) != 0) + value = 1; /* config failed */ + else + value = 0; + + /* clear all interrupt status */ + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff); + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); + return value; +} + +/* + * atl2_setup_ring_resources - allocate Tx / RX descriptor resources + * @adapter: board private structure + * + * Return 0 on success, negative on failure + */ +static s32 atl2_setup_ring_resources(struct atl2_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int size; + u8 offset = 0; + + /* real ring DMA buffer */ + adapter->ring_size = size = + adapter->txd_ring_size * 1 + 7 + /* dword align */ + adapter->txs_ring_size * 4 + 7 + /* dword align */ + adapter->rxd_ring_size * 1536 + 127; /* 128bytes align */ + + adapter->ring_vir_addr = pci_alloc_consistent(pdev, size, + &adapter->ring_dma); + if (!adapter->ring_vir_addr) + return -ENOMEM; + memset(adapter->ring_vir_addr, 0, adapter->ring_size); + + /* Init TXD Ring */ + adapter->txd_dma = adapter->ring_dma ; + offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0; + adapter->txd_dma += offset; + adapter->txd_ring = (struct tx_pkt_header *) (adapter->ring_vir_addr + + offset); + + /* Init TXS Ring */ + adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size; + offset = (adapter->txs_dma & 0x7) ? (8 - (adapter->txs_dma & 0x7)) : 0; + adapter->txs_dma += offset; + adapter->txs_ring = (struct tx_pkt_status *) + (((u8 *)adapter->txd_ring) + (adapter->txd_ring_size + offset)); + + /* Init RXD Ring */ + adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size * 4; + offset = (adapter->rxd_dma & 127) ? + (128 - (adapter->rxd_dma & 127)) : 0; + if (offset > 7) + offset -= 8; + else + offset += (128 - 8); + + adapter->rxd_dma += offset; + adapter->rxd_ring = (struct rx_desc *) (((u8 *)adapter->txs_ring) + + (adapter->txs_ring_size * 4 + offset)); + +/* + * Read / Write Ptr Initialize: + * init_ring_ptrs(adapter); + */ + return 0; +} + +/* + * atl2_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + */ +static inline void atl2_irq_enable(struct atl2_adapter *adapter) +{ + ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); + ATL2_WRITE_FLUSH(&adapter->hw); +} + +/* + * atl2_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + */ +static inline void atl2_irq_disable(struct atl2_adapter *adapter) +{ + ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0); + ATL2_WRITE_FLUSH(&adapter->hw); + synchronize_irq(adapter->pdev->irq); +} + +#ifdef NETIF_F_HW_VLAN_TX +static void atl2_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + u32 ctrl; + + atl2_irq_disable(adapter); + adapter->vlgrp = grp; + + if (grp) { + /* enable VLAN tag insert/strip */ + ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL); + ctrl |= MAC_CTRL_RMV_VLAN; + ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl); + } else { + /* disable VLAN tag insert/strip */ + ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL); + ctrl &= ~MAC_CTRL_RMV_VLAN; + ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl); + } + + atl2_irq_enable(adapter); +} + +static void atl2_restore_vlan(struct atl2_adapter *adapter) +{ + atl2_vlan_rx_register(adapter->netdev, adapter->vlgrp); +} +#endif + +static void atl2_intr_rx(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct rx_desc *rxd; + struct sk_buff *skb; + + do { + rxd = adapter->rxd_ring+adapter->rxd_write_ptr; + if (!rxd->status.update) + break; /* end of tx */ + + /* clear this flag at once */ + rxd->status.update = 0; + + if (rxd->status.ok && rxd->status.pkt_size >= 60) { + int rx_size = (int)(rxd->status.pkt_size - 4); + /* alloc new buffer */ + skb = netdev_alloc_skb(netdev, rx_size + NET_IP_ALIGN); + if (NULL == skb) { + printk(KERN_WARNING + "%s: Mem squeeze, deferring packet.\n", + netdev->name); + /* + * Check that some rx space is free. If not, + * free one and mark stats->rx_dropped++. + */ + adapter->net_stats.rx_dropped++; + break; + } + skb_reserve(skb, NET_IP_ALIGN); + skb->dev = netdev; + memcpy(skb->data, rxd->packet, rx_size); + skb_put(skb, rx_size); + skb->protocol = eth_type_trans(skb, netdev); +#ifdef NETIF_F_HW_VLAN_TX + if (adapter->vlgrp && (rxd->status.vlan)) { + u16 vlan_tag = (rxd->status.vtag>>4) | + ((rxd->status.vtag&7) << 13) | + ((rxd->status.vtag&8) << 9); + vlan_hwaccel_rx(skb, adapter->vlgrp, vlan_tag); + } else +#endif + netif_rx(skb); + adapter->net_stats.rx_bytes += rx_size; + adapter->net_stats.rx_packets++; + netdev->last_rx = jiffies; + } else { + adapter->net_stats.rx_errors++; + + if (rxd->status.ok && rxd->status.pkt_size <= 60) + adapter->net_stats.rx_length_errors++; + if (rxd->status.mcast) + adapter->net_stats.multicast++; + if (rxd->status.crc) + adapter->net_stats.rx_crc_errors++; + if (rxd->status.align) + adapter->net_stats.rx_frame_errors++; + } + + /* advance write ptr */ + if (++adapter->rxd_write_ptr == adapter->rxd_ring_size) + adapter->rxd_write_ptr = 0; + } while (1); + + /* update mailbox? */ + adapter->rxd_read_ptr = adapter->rxd_write_ptr; + ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr); +} + +static void atl2_intr_tx(struct atl2_adapter *adapter) +{ + u32 txd_read_ptr; + u32 txs_write_ptr; + struct tx_pkt_status *txs; + struct tx_pkt_header *txph; + int free_hole = 0; + + do { + txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); + txs = adapter->txs_ring + txs_write_ptr; + if (!txs->update) + break; /* tx stop here */ + + free_hole = 1; + txs->update = 0; + + if (++txs_write_ptr == adapter->txs_ring_size) + txs_write_ptr = 0; + atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr); + + txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr); + txph = (struct tx_pkt_header *) + (((u8 *)adapter->txd_ring) + txd_read_ptr); + + if (txph->pkt_size != txs->pkt_size) { + struct tx_pkt_status *old_txs = txs; + printk(KERN_WARNING + "%s: txs packet size not consistent with txd" + " txd_:0x%08x, txs_:0x%08x!\n", + adapter->netdev->name, + *(u32 *)txph, *(u32 *)txs); + printk(KERN_WARNING + "txd read ptr: 0x%x\n", + txd_read_ptr); + txs = adapter->txs_ring + txs_write_ptr; + printk(KERN_WARNING + "txs-behind:0x%08x\n", + *(u32 *)txs); + if (txs_write_ptr < 2) { + txs = adapter->txs_ring + + (adapter->txs_ring_size + + txs_write_ptr - 2); + } else { + txs = adapter->txs_ring + (txs_write_ptr - 2); + } + printk(KERN_WARNING + "txs-before:0x%08x\n", + *(u32 *)txs); + txs = old_txs; + } + + /* 4for TPH */ + txd_read_ptr += (((u32)(txph->pkt_size) + 7) & ~3); + if (txd_read_ptr >= adapter->txd_ring_size) + txd_read_ptr -= adapter->txd_ring_size; + + atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr); + + /* tx statistics: */ + if (txs->ok) + adapter->net_stats.tx_packets++; + else + adapter->net_stats.tx_errors++; + + if (txs->defer) + adapter->net_stats.collisions++; + if (txs->abort_col) + adapter->net_stats.tx_aborted_errors++; + if (txs->late_col) + adapter->net_stats.tx_window_errors++; + if (txs->underun) + adapter->net_stats.tx_fifo_errors++; + } while (1); + + if (free_hole) { + if (netif_queue_stopped(adapter->netdev) && + netif_carrier_ok(adapter->netdev)) + netif_wake_queue(adapter->netdev); + } +} + +static void atl2_check_for_link(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + u16 phy_data = 0; + + spin_lock(&adapter->stats_lock); + atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + spin_unlock(&adapter->stats_lock); + + /* notify upper layer link down ASAP */ + if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */ + if (netif_carrier_ok(netdev)) { /* old link state: Up */ + printk(KERN_INFO "%s: %s NIC Link is Down\n", + atl2_driver_name, netdev->name); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } + schedule_work(&adapter->link_chg_task); +} + +static inline void atl2_clear_phy_int(struct atl2_adapter *adapter) +{ + u16 phy_data; + spin_lock(&adapter->stats_lock); + atl2_read_phy_reg(&adapter->hw, 19, &phy_data); + spin_unlock(&adapter->stats_lock); +} + +/* + * atl2_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + */ +static irqreturn_t atl2_intr(int irq, void *data) +{ + struct atl2_adapter *adapter = netdev_priv(data); + struct atl2_hw *hw = &adapter->hw; + u32 status; + + status = ATL2_READ_REG(hw, REG_ISR); + if (0 == status) + return IRQ_NONE; + + /* link event */ + if (status & ISR_PHY) + atl2_clear_phy_int(adapter); + + /* clear ISR status, and Enable CMB DMA/Disable Interrupt */ + ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); + + /* check if PCIE PHY Link down */ + if (status & ISR_PHY_LINKDOWN) { + if (netif_running(adapter->netdev)) { /* reset MAC */ + ATL2_WRITE_REG(hw, REG_ISR, 0); + ATL2_WRITE_REG(hw, REG_IMR, 0); + ATL2_WRITE_FLUSH(hw); + schedule_work(&adapter->reset_task); + return IRQ_HANDLED; + } + } + + /* check if DMA read/write error? */ + if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { + ATL2_WRITE_REG(hw, REG_ISR, 0); + ATL2_WRITE_REG(hw, REG_IMR, 0); + ATL2_WRITE_FLUSH(hw); + schedule_work(&adapter->reset_task); + return IRQ_HANDLED; + } + + /* link event */ + if (status & (ISR_PHY | ISR_MANUAL)) { + adapter->net_stats.tx_carrier_errors++; + atl2_check_for_link(adapter); + } + + /* transmit event */ + if (status & ISR_TX_EVENT) + atl2_intr_tx(adapter); + + /* rx exception */ + if (status & ISR_RX_EVENT) + atl2_intr_rx(adapter); + + /* re-enable Interrupt */ + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); + return IRQ_HANDLED; +} + +static int atl2_request_irq(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int flags, err = 0; + + flags = IRQF_SHARED; +#ifdef CONFIG_PCI_MSI + adapter->have_msi = true; + err = pci_enable_msi(adapter->pdev); + if (err) + adapter->have_msi = false; + + if (adapter->have_msi) + flags &= ~IRQF_SHARED; +#endif + + return request_irq(adapter->pdev->irq, &atl2_intr, flags, netdev->name, + netdev); +} + +/* + * atl2_free_ring_resources - Free Tx / RX descriptor Resources + * @adapter: board private structure + * + * Free all transmit software resources + */ +static void atl2_free_ring_resources(struct atl2_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr, + adapter->ring_dma); +} + +/* + * atl2_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + */ +static int atl2_open(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + int err; + u32 val; + + /* disallow open during test */ + if (test_bit(__ATL2_TESTING, &adapter->flags)) + return -EBUSY; + + /* allocate transmit descriptors */ + err = atl2_setup_ring_resources(adapter); + if (err) + return err; + + err = atl2_init_hw(&adapter->hw); + if (err) { + err = -EIO; + goto err_init_hw; + } + + /* hardware has been reset, we need to reload some things */ + atl2_set_multi(netdev); + init_ring_ptrs(adapter); + +#ifdef NETIF_F_HW_VLAN_TX + atl2_restore_vlan(adapter); +#endif + + if (atl2_configure(adapter)) { + err = -EIO; + goto err_config; + } + + err = atl2_request_irq(adapter); + if (err) + goto err_req_irq; + + clear_bit(__ATL2_DOWN, &adapter->flags); + + mod_timer(&adapter->watchdog_timer, jiffies + 4*HZ); + + val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); + ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, + val | MASTER_CTRL_MANUAL_INT); + + atl2_irq_enable(adapter); + + return 0; + +err_init_hw: +err_req_irq: +err_config: + atl2_free_ring_resources(adapter); + atl2_reset_hw(&adapter->hw); + + return err; +} + +static void atl2_down(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + /* signal that we're down so the interrupt handler does not + * reschedule our watchdog timer */ + set_bit(__ATL2_DOWN, &adapter->flags); + +#ifdef NETIF_F_LLTX + netif_stop_queue(netdev); +#else + netif_tx_disable(netdev); +#endif + + /* reset MAC to disable all RX/TX */ + atl2_reset_hw(&adapter->hw); + msleep(1); + + atl2_irq_disable(adapter); + + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_config_timer); + clear_bit(0, &adapter->cfg_phy); + + netif_carrier_off(netdev); + adapter->link_speed = SPEED_0; + adapter->link_duplex = -1; +} + +static void atl2_free_irq(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + free_irq(adapter->pdev->irq, netdev); + +#ifdef CONFIG_PCI_MSI + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); +#endif +} + +/* + * atl2_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + */ +static int atl2_close(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); + + atl2_down(adapter); + atl2_free_irq(adapter); + atl2_free_ring_resources(adapter); + + return 0; +} + +static inline int TxsFreeUnit(struct atl2_adapter *adapter) +{ + u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); + + return (adapter->txs_next_clear >= txs_write_ptr) ? + (int) (adapter->txs_ring_size - adapter->txs_next_clear + + txs_write_ptr - 1) : + (int) (txs_write_ptr - adapter->txs_next_clear - 1); +} + +static inline int TxdFreeBytes(struct atl2_adapter *adapter) +{ + u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr); + + return (adapter->txd_write_ptr >= txd_read_ptr) ? + (int) (adapter->txd_ring_size - adapter->txd_write_ptr + + txd_read_ptr - 1) : + (int) (txd_read_ptr - adapter->txd_write_ptr - 1); +} + +static int atl2_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + struct tx_pkt_header *txph; + u32 offset, copy_len; + int txs_unused; + int txbuf_unused; + + if (test_bit(__ATL2_DOWN, &adapter->flags)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (unlikely(skb->len <= 0)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + +#ifdef NETIF_F_LLTX + local_irq_save(flags); + if (!spin_trylock(&adapter->tx_lock)) { + /* Collision - tell upper layer to requeue */ + local_irq_restore(flags); + return NETDEV_TX_LOCKED; + } +#else + spin_lock_irqsave(&adapter->tx_lock, flags); +#endif + txs_unused = TxsFreeUnit(adapter); + txbuf_unused = TxdFreeBytes(adapter); + + if (skb->len + sizeof(struct tx_pkt_header) + 4 > txbuf_unused || + txs_unused < 1) { + /* not enough resources */ + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + offset = adapter->txd_write_ptr; + + txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + offset); + + *(u32 *)txph = 0; + txph->pkt_size = skb->len; + + offset += 4; + if (offset >= adapter->txd_ring_size) + offset -= adapter->txd_ring_size; + copy_len = adapter->txd_ring_size - offset; + if (copy_len >= skb->len) { + memcpy(((u8 *)adapter->txd_ring) + offset, skb->data, skb->len); + offset += ((u32)(skb->len + 3) & ~3); + } else { + memcpy(((u8 *)adapter->txd_ring)+offset, skb->data, copy_len); + memcpy((u8 *)adapter->txd_ring, skb->data+copy_len, + skb->len-copy_len); + offset = ((u32)(skb->len-copy_len + 3) & ~3); + } +#ifdef NETIF_F_HW_VLAN_TX + if (adapter->vlgrp && vlan_tx_tag_present(skb)) { + u16 vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = (vlan_tag << 4) | + (vlan_tag >> 13) | + ((vlan_tag >> 9) & 0x8); + txph->ins_vlan = 1; + txph->vlan = vlan_tag; + } +#endif + if (offset >= adapter->txd_ring_size) + offset -= adapter->txd_ring_size; + adapter->txd_write_ptr = offset; + + /* clear txs before send */ + adapter->txs_ring[adapter->txs_next_clear].update = 0; + if (++adapter->txs_next_clear == adapter->txs_ring_size) + adapter->txs_next_clear = 0; + + ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX, + (adapter->txd_write_ptr >> 2)); + + spin_unlock_irqrestore(&adapter->tx_lock, flags); + + netdev->trans_start = jiffies; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/* + * atl2_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + */ +static struct net_device_stats *atl2_get_stats(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + return &adapter->net_stats; +} + +/* + * atl2_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + */ +static int atl2_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + + if ((new_mtu < 40) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE))) + return -EINVAL; + + /* set MTU */ + if (hw->max_frame_size != new_mtu) { + netdev->mtu = new_mtu; + ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ENET_HEADER_SIZE + + VLAN_SIZE + ETHERNET_FCS_SIZE); + } + + return 0; +} + +/* + * atl2_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + */ +static int atl2_set_mac(struct net_device *netdev, void *p) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (netif_running(netdev)) + return -EBUSY; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + + atl2_set_mac_addr(&adapter->hw); + + return 0; +} + +/* + * atl2_mii_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct mii_ioctl_data *data = if_mii(ifr); + unsigned long flags; + + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_irqsave(&adapter->stats_lock, flags); + if (atl2_read_phy_reg(&adapter->hw, + data->reg_num & 0x1F, &data->val_out)) { + spin_unlock_irqrestore(&adapter->stats_lock, flags); + return -EIO; + } + spin_unlock_irqrestore(&adapter->stats_lock, flags); + break; + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (data->reg_num & ~(0x1F)) + return -EFAULT; + spin_lock_irqsave(&adapter->stats_lock, flags); + if (atl2_write_phy_reg(&adapter->hw, data->reg_num, + data->val_in)) { + spin_unlock_irqrestore(&adapter->stats_lock, flags); + return -EIO; + } + spin_unlock_irqrestore(&adapter->stats_lock, flags); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * atl2_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return atl2_mii_ioctl(netdev, ifr, cmd); +#ifdef ETHTOOL_OPS_COMPAT + case SIOCETHTOOL: + return ethtool_ioctl(ifr); +#endif + default: + return -EOPNOTSUPP; + } +} + +/* + * atl2_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + */ +static void atl2_tx_timeout(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); +} + +/* + * atl2_watchdog - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + */ +static void atl2_watchdog(unsigned long data) +{ + struct atl2_adapter *adapter = (struct atl2_adapter *) data; + u32 drop_rxd, drop_rxs; + unsigned long flags; + + if (!test_bit(__ATL2_DOWN, &adapter->flags)) { + spin_lock_irqsave(&adapter->stats_lock, flags); + drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV); + drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV); + adapter->net_stats.rx_over_errors += (drop_rxd+drop_rxs); + spin_unlock_irqrestore(&adapter->stats_lock, flags); + + /* Reset the timer */ + mod_timer(&adapter->watchdog_timer, jiffies + 4 * HZ); + } +} + +/* + * atl2_phy_config - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + */ +static void atl2_phy_config(unsigned long data) +{ + struct atl2_adapter *adapter = (struct atl2_adapter *) data; + struct atl2_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->stats_lock, flags); + atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); + atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + spin_unlock_irqrestore(&adapter->stats_lock, flags); + clear_bit(0, &adapter->cfg_phy); +} + +static int atl2_up(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int err = 0; + u32 val; + + /* hardware has been reset, we need to reload some things */ + + err = atl2_init_hw(&adapter->hw); + if (err) { + err = -EIO; + return err; + } + + atl2_set_multi(netdev); + init_ring_ptrs(adapter); + +#ifdef NETIF_F_HW_VLAN_TX + atl2_restore_vlan(adapter); +#endif + + if (atl2_configure(adapter)) { + err = -EIO; + goto err_up; + } + + clear_bit(__ATL2_DOWN, &adapter->flags); + + val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); + ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | + MASTER_CTRL_MANUAL_INT); + + atl2_irq_enable(adapter); + +err_up: + return err; +} + +static void atl2_reinit_locked(struct atl2_adapter *adapter) +{ + WARN_ON(in_interrupt()); + while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) + msleep(1); + atl2_down(adapter); + atl2_up(adapter); + clear_bit(__ATL2_RESETTING, &adapter->flags); +} + +static void atl2_reset_task(struct work_struct *work) +{ + struct atl2_adapter *adapter; + adapter = container_of(work, struct atl2_adapter, reset_task); + + atl2_reinit_locked(adapter); +} + +static void atl2_setup_mac_ctrl(struct atl2_adapter *adapter) +{ + u32 value; + struct atl2_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + + /* Config MAC CTRL Register */ + value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; + + /* duplex */ + if (FULL_DUPLEX == adapter->link_duplex) + value |= MAC_CTRL_DUPLX; + + /* flow control */ + value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); + + /* PAD & CRC */ + value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + + /* preamble length */ + value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + /* vlan */ + if (adapter->vlgrp) + value |= MAC_CTRL_RMV_VLAN; + + /* filter mode */ + value |= MAC_CTRL_BC_EN; + if (netdev->flags & IFF_PROMISC) + value |= MAC_CTRL_PROMIS_EN; + else if (netdev->flags & IFF_ALLMULTI) + value |= MAC_CTRL_MC_ALL_EN; + + /* half retry buffer */ + value |= (((u32)(adapter->hw.retry_buf & + MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT); + + ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); +} + +static int atl2_check_link(struct atl2_adapter *adapter) +{ + struct atl2_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + int ret_val; + u16 speed, duplex, phy_data; + int reconfig = 0; + + /* MII_BMSR must read twise */ + atl2_read_phy_reg(hw, MII_BMSR, &phy_data); + atl2_read_phy_reg(hw, MII_BMSR, &phy_data); + if (!(phy_data&BMSR_LSTATUS)) { /* link down */ + if (netif_carrier_ok(netdev)) { /* old link state: Up */ + u32 value; + /* disable rx */ + value = ATL2_READ_REG(hw, REG_MAC_CTRL); + value &= ~MAC_CTRL_RX_EN; + ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + return 0; + } + + /* Link Up */ + ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) + return ret_val; + switch (hw->MediaType) { + case MEDIA_TYPE_100M_FULL: + if (speed != SPEED_100 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_100M_HALF: + if (speed != SPEED_100 || duplex != HALF_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_10M_FULL: + if (speed != SPEED_10 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_10M_HALF: + if (speed != SPEED_10 || duplex != HALF_DUPLEX) + reconfig = 1; + break; + } + /* link result is our setting */ + if (reconfig == 0) { + if (adapter->link_speed != speed || + adapter->link_duplex != duplex) { + adapter->link_speed = speed; + adapter->link_duplex = duplex; + atl2_setup_mac_ctrl(adapter); + printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n", + atl2_driver_name, netdev->name, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex"); + } + + if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } + return 0; + } + + /* change original link status */ + if (netif_carrier_ok(netdev)) { + u32 value; + /* disable rx */ + value = ATL2_READ_REG(hw, REG_MAC_CTRL); + value &= ~MAC_CTRL_RX_EN; + ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); + + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + + /* auto-neg, insert timer to re-config phy + * (if interval smaller than 5 seconds, something strange) */ + if (!test_bit(__ATL2_DOWN, &adapter->flags)) { + if (!test_and_set_bit(0, &adapter->cfg_phy)) + mod_timer(&adapter->phy_config_timer, jiffies + 5 * HZ); + } + + return 0; +} + +/* + * atl2_link_chg_task - deal with link change event Out of interrupt context + * @netdev: network interface device structure + */ +static void atl2_link_chg_task(struct work_struct *work) +{ + struct atl2_adapter *adapter; + unsigned long flags; + + adapter = container_of(work, struct atl2_adapter, link_chg_task); + + spin_lock_irqsave(&adapter->stats_lock, flags); + atl2_check_link(adapter); + spin_unlock_irqrestore(&adapter->stats_lock, flags); +} + +static void atl2_setup_pcicmd(struct pci_dev *pdev) +{ + u16 cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + + if (cmd & PCI_COMMAND_INTX_DISABLE) + cmd &= ~PCI_COMMAND_INTX_DISABLE; + if (cmd & PCI_COMMAND_IO) + cmd &= ~PCI_COMMAND_IO; + if (0 == (cmd & PCI_COMMAND_MEMORY)) + cmd |= PCI_COMMAND_MEMORY; + if (0 == (cmd & PCI_COMMAND_MASTER)) + cmd |= PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + + /* + * some motherboards BIOS(PXE/EFI) driver may set PME + * while they transfer control to OS (Windows/Linux) + * so we should clear this bit before NIC work normally + */ + pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); +} + +/* + * atl2_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in atl2_pci_tbl + * + * Returns 0 on success, negative on failure + * + * atl2_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + */ +static int __devinit atl2_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct atl2_adapter *adapter; + static int cards_found; + unsigned long mmio_start; + int mmio_len; + int err; + + cards_found = 0; + + err = pci_enable_device(pdev); + if (err) + return err; + + /* + * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA + * until the kernel has the proper infrastructure to support 64-bit DMA + * on these devices. + */ + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) && + pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n"); + goto err_dma; + } + + /* Mark all PCI regions associated with PCI device + * pdev as being reserved by owner atl2_driver_name */ + err = pci_request_regions(pdev, atl2_driver_name); + if (err) + goto err_pci_reg; + + /* Enables bus-mastering on the device and calls + * pcibios_set_master to do the needed arch specific settings */ + pci_set_master(pdev); + + err = -ENOMEM; + netdev = alloc_etherdev(sizeof(struct atl2_adapter)); + if (!netdev) + goto err_alloc_etherdev; + + SET_NETDEV_DEV(netdev, &pdev->dev); + + pci_set_drvdata(pdev, netdev); + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.back = adapter; + + mmio_start = pci_resource_start(pdev, 0x0); + mmio_len = pci_resource_len(pdev, 0x0); + + adapter->hw.mem_rang = (u32)mmio_len; + adapter->hw.hw_addr = ioremap(mmio_start, mmio_len); + if (!adapter->hw.hw_addr) { + err = -EIO; + goto err_ioremap; + } + + atl2_setup_pcicmd(pdev); + + netdev->open = &atl2_open; + netdev->stop = &atl2_close; + netdev->hard_start_xmit = &atl2_xmit_frame; + netdev->get_stats = &atl2_get_stats; + netdev->set_multicast_list = &atl2_set_multi; + netdev->set_mac_address = &atl2_set_mac; + netdev->change_mtu = &atl2_change_mtu; + netdev->do_ioctl = &atl2_ioctl; + atl2_set_ethtool_ops(netdev); + +#ifdef HAVE_TX_TIMEOUT + netdev->tx_timeout = &atl2_tx_timeout; + netdev->watchdog_timeo = 5 * HZ; +#endif +#ifdef NETIF_F_HW_VLAN_TX + netdev->vlan_rx_register = atl2_vlan_rx_register; +#endif + strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); + + netdev->mem_start = mmio_start; + netdev->mem_end = mmio_start + mmio_len; + adapter->bd_number = cards_found; + adapter->pci_using_64 = false; + + /* setup the private structure */ + err = atl2_sw_init(adapter); + if (err) + goto err_sw_init; + + err = -EIO; + +#ifdef NETIF_F_HW_VLAN_TX + netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); +#endif + +#ifdef NETIF_F_LLTX + netdev->features |= NETIF_F_LLTX; +#endif + + /* Init PHY as early as possible due to power saving issue */ + atl2_phy_init(&adapter->hw); + + /* reset the controller to + * put the device in a known good starting state */ + + if (atl2_reset_hw(&adapter->hw)) { + err = -EIO; + goto err_reset; + } + + /* copy the MAC address out of the EEPROM */ + atl2_read_mac_addr(&adapter->hw); + memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); +/* FIXME: do we still need this? */ +#ifdef ETHTOOL_GPERMADDR + memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); + + if (!is_valid_ether_addr(netdev->perm_addr)) { +#else + if (!is_valid_ether_addr(netdev->dev_addr)) { +#endif + err = -EIO; + goto err_eeprom; + } + + atl2_check_options(adapter); + + init_timer(&adapter->watchdog_timer); + adapter->watchdog_timer.function = &atl2_watchdog; + adapter->watchdog_timer.data = (unsigned long) adapter; + + init_timer(&adapter->phy_config_timer); + adapter->phy_config_timer.function = &atl2_phy_config; + adapter->phy_config_timer.data = (unsigned long) adapter; + + INIT_WORK(&adapter->reset_task, atl2_reset_task); + INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task); + + strcpy(netdev->name, "eth%d"); /* ?? */ + err = register_netdev(netdev); + if (err) + goto err_register; + + /* assume we have no link for now */ + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + cards_found++; + + return 0; + +err_reset: +err_register: +err_sw_init: +err_eeprom: + iounmap(adapter->hw.hw_addr); +err_ioremap: + free_netdev(netdev); +err_alloc_etherdev: + pci_release_regions(pdev); +err_pci_reg: +err_dma: + pci_disable_device(pdev); + return err; +} + +/* + * atl2_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * atl2_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + */ +/* FIXME: write the original MAC address back in case it was changed from a + * BIOS-set value, as in atl1 -- CHS */ +static void __devexit atl2_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl2_adapter *adapter = netdev_priv(netdev); + + /* flush_scheduled work may reschedule our watchdog task, so + * explicitly disable watchdog tasks from being rescheduled */ + set_bit(__ATL2_DOWN, &adapter->flags); + + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_config_timer); + + flush_scheduled_work(); + + unregister_netdev(netdev); + + atl2_force_ps(&adapter->hw); + + iounmap(adapter->hw.hw_addr); + pci_release_regions(pdev); + + free_netdev(netdev); + + pci_disable_device(pdev); +} + +static int atl2_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u16 speed, duplex; + u32 ctrl = 0; + u32 wufc = adapter->wol; + +#ifdef CONFIG_PM + int retval = 0; +#endif + + netif_device_detach(netdev); + + if (netif_running(netdev)) { + WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); + atl2_down(adapter); + } + +#ifdef CONFIG_PM + retval = pci_save_state(pdev); + if (retval) + return retval; +#endif + + atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl); + atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl); + if (ctrl & BMSR_LSTATUS) + wufc &= ~ATLX_WUFC_LNKC; + + if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) { + u32 ret_val; + /* get current link speed & duplex */ + ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + printk(KERN_DEBUG + "%s: get speed&duplex error while suspend\n", + atl2_driver_name); + goto wol_dis; + } + + ctrl = 0; + + /* turn on magic packet wol */ + if (wufc & ATLX_WUFC_MAG) + ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN); + + /* ignore Link Chg event when Link is up */ + ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); + + /* Config MAC CTRL Register */ + ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; + if (FULL_DUPLEX == adapter->link_duplex) + ctrl |= MAC_CTRL_DUPLX; + ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + ctrl |= (((u32)adapter->hw.preamble_len & + MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); + ctrl |= (((u32)(adapter->hw.retry_buf & + MAC_CTRL_HALF_LEFT_BUF_MASK)) << + MAC_CTRL_HALF_LEFT_BUF_SHIFT); + if (wufc & ATLX_WUFC_MAG) { + /* magic packet maybe Broadcast&multicast&Unicast */ + ctrl |= MAC_CTRL_BC_EN; + } + + ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl); + + /* pcie patch */ + ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); + ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + goto suspend_exit; + } + + if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) { + /* link is down, so only LINK CHG WOL event enable */ + ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); + ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); + ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0); + + /* pcie patch */ + ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); + ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); + + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + + goto suspend_exit; + } + +wol_dis: + /* WOL disabled */ + ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0); + + /* pcie patch */ + ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); + ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); + + atl2_force_ps(hw); + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + +suspend_exit: + if (netif_running(netdev)) + atl2_free_irq(adapter); + + pci_disable_device(pdev); + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +#ifdef CONFIG_PM +static int atl2_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl2_adapter *adapter = netdev_priv(netdev); + u32 err; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR + "atl2: Cannot enable PCI device from suspend\n"); + return err; + } + + pci_set_master(pdev); + + ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); + + err = atl2_request_irq(adapter); + if (netif_running(netdev) && err) + return err; + + atl2_reset_hw(&adapter->hw); + + if (netif_running(netdev)) + atl2_up(adapter); + + netif_device_attach(netdev); + + return 0; +} +#endif + +static void atl2_shutdown(struct pci_dev *pdev) +{ + atl2_suspend(pdev, PMSG_SUSPEND); +} + +static struct pci_driver atl2_driver = { + .name = atl2_driver_name, + .id_table = atl2_pci_tbl, + .probe = atl2_probe, + .remove = __devexit_p(atl2_remove), + /* Power Managment Hooks */ + .suspend = atl2_suspend, +#ifdef CONFIG_PM + .resume = atl2_resume, +#endif + .shutdown = atl2_shutdown, +}; + +/* + * atl2_init_module - Driver Registration Routine + * + * atl2_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init atl2_init_module(void) +{ + printk(KERN_INFO "%s - version %s\n", atl2_driver_string, + atl2_driver_version); + printk(KERN_INFO "%s\n", atl2_copyright); + return pci_register_driver(&atl2_driver); +} +module_init(atl2_init_module); + +/* + * atl2_exit_module - Driver Exit Cleanup Routine + * + * atl2_exit_module is called just before the driver is removed + * from memory. + */ +static void __exit atl2_exit_module(void) +{ + pci_unregister_driver(&atl2_driver); +} +module_exit(atl2_exit_module); + +static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) +{ + struct atl2_adapter *adapter = hw->back; + pci_read_config_word(adapter->pdev, reg, value); +} + +static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) +{ + struct atl2_adapter *adapter = hw->back; + pci_write_config_word(adapter->pdev, reg, *value); +} + +static int atl2_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); + ecmd->advertising = ADVERTISED_TP; + + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->advertising |= hw->autoneg_advertised; + + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->transceiver = XCVR_INTERNAL; + + if (adapter->link_speed != SPEED_0) { + ecmd->speed = adapter->link_speed; + if (adapter->link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = AUTONEG_ENABLE; + return 0; +} + +static int atl2_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + + while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) + msleep(1); + + if (ecmd->autoneg == AUTONEG_ENABLE) { +#define MY_ADV_MASK (ADVERTISE_10_HALF | \ + ADVERTISE_10_FULL | \ + ADVERTISE_100_HALF| \ + ADVERTISE_100_FULL) + + if ((ecmd->advertising & MY_ADV_MASK) == MY_ADV_MASK) { + hw->MediaType = MEDIA_TYPE_AUTO_SENSOR; + hw->autoneg_advertised = MY_ADV_MASK; + } else if ((ecmd->advertising & MY_ADV_MASK) == + ADVERTISE_100_FULL) { + hw->MediaType = MEDIA_TYPE_100M_FULL; + hw->autoneg_advertised = ADVERTISE_100_FULL; + } else if ((ecmd->advertising & MY_ADV_MASK) == + ADVERTISE_100_HALF) { + hw->MediaType = MEDIA_TYPE_100M_HALF; + hw->autoneg_advertised = ADVERTISE_100_HALF; + } else if ((ecmd->advertising & MY_ADV_MASK) == + ADVERTISE_10_FULL) { + hw->MediaType = MEDIA_TYPE_10M_FULL; + hw->autoneg_advertised = ADVERTISE_10_FULL; + } else if ((ecmd->advertising & MY_ADV_MASK) == + ADVERTISE_10_HALF) { + hw->MediaType = MEDIA_TYPE_10M_HALF; + hw->autoneg_advertised = ADVERTISE_10_HALF; + } else { + clear_bit(__ATL2_RESETTING, &adapter->flags); + return -EINVAL; + } + ecmd->advertising = hw->autoneg_advertised | + ADVERTISED_TP | ADVERTISED_Autoneg; + } else { + clear_bit(__ATL2_RESETTING, &adapter->flags); + return -EINVAL; + } + + /* reset the link */ + if (netif_running(adapter->netdev)) { + atl2_down(adapter); + atl2_up(adapter); + } else + atl2_reset_hw(&adapter->hw); + + clear_bit(__ATL2_RESETTING, &adapter->flags); + return 0; +} + +static u32 atl2_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} + +static u32 atl2_get_msglevel(struct net_device *netdev) +{ + return 0; +} + +/* + * It's sane for this to be empty, but we might want to take advantage of this. + */ +static void atl2_set_msglevel(struct net_device *netdev, u32 data) +{ +} + +static int atl2_get_regs_len(struct net_device *netdev) +{ +#define ATL2_REGS_LEN 42 + return sizeof(u32) * ATL2_REGS_LEN; +} + +static void atl2_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u32 *regs_buff = p; + u16 phy_data; + + memset(p, 0, sizeof(u32) * ATL2_REGS_LEN); + + regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; + + regs_buff[0] = ATL2_READ_REG(hw, REG_VPD_CAP); + regs_buff[1] = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); + regs_buff[2] = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG); + regs_buff[3] = ATL2_READ_REG(hw, REG_TWSI_CTRL); + regs_buff[4] = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); + regs_buff[5] = ATL2_READ_REG(hw, REG_MASTER_CTRL); + regs_buff[6] = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT); + regs_buff[7] = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); + regs_buff[8] = ATL2_READ_REG(hw, REG_PHY_ENABLE); + regs_buff[9] = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER); + regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS); + regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL); + regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK); + regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL); + regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG); + regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); + regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4); + regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE); + regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4); + regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); + regs_buff[20] = ATL2_READ_REG(hw, REG_MTU); + regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL); + regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END); + regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI); + regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO); + regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE); + regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO); + regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE); + regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO); + regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM); + regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR); + regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH); + regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW); + regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH); + regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH); + regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX); + regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX); + regs_buff[38] = ATL2_READ_REG(hw, REG_ISR); + regs_buff[39] = ATL2_READ_REG(hw, REG_IMR); + + atl2_read_phy_reg(hw, MII_BMCR, &phy_data); + regs_buff[40] = (u32)phy_data; + atl2_read_phy_reg(hw, MII_BMSR, &phy_data); + regs_buff[41] = (u32)phy_data; +} + +static int atl2_get_eeprom_len(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + if (!atl2_check_eeprom_exist(&adapter->hw)) + return 512; + else + return 0; +} + +static int atl2_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u32 *eeprom_buff; + int first_dword, last_dword; + int ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (atl2_check_eeprom_exist(hw)) + return -EINVAL; + + eeprom->magic = hw->vendor_id | (hw->device_id << 16); + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32) * (last_dword - first_dword + 1), + GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + for (i = first_dword; i < last_dword; i++) { + if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) + return -EIO; + } + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; +} + +static int atl2_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u32 *eeprom_buff; + u32 *ptr; + int max_len, first_dword, last_dword, ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EOPNOTSUPP; + + if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) + return -EFAULT; + + max_len = 512; + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + eeprom_buff = kmalloc(max_len, GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + ptr = (u32 *)eeprom_buff; + + if (eeprom->offset & 3) { + /* need read/modify/write of first changed EEPROM word */ + /* only the second byte of the word is being modified */ + if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) + return -EIO; + ptr++; + } + if (((eeprom->offset + eeprom->len) & 3)) { + /* + * need read/modify/write of last changed EEPROM word + * only the first byte of the word is being modified + */ + if (!atl2_read_eeprom(hw, last_dword * 4, + &(eeprom_buff[last_dword - first_dword]))) + return -EIO; + } + + /* Device's eeprom is always little-endian, word addressable */ + memcpy(ptr, bytes, eeprom->len); + + for (i = 0; i < last_dword - first_dword + 1; i++) { + if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) + return -EIO; + } + + kfree(eeprom_buff); + return ret_val; +} + +static void atl2_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + strncpy(drvinfo->driver, atl2_driver_name, 32); + strncpy(drvinfo->version, atl2_driver_version, 32); + strncpy(drvinfo->fw_version, "L2", 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = atl2_get_regs_len(netdev); + drvinfo->eedump_len = atl2_get_eeprom_len(netdev); +} + +static void atl2_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + if (adapter->wol & ATLX_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if (adapter->wol & ATLX_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if (adapter->wol & ATLX_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if (adapter->wol & ATLX_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + if (adapter->wol & ATLX_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; +} + +static int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)) + return -EOPNOTSUPP; + + if (wol->wolopts & (WAKE_MCAST|WAKE_BCAST|WAKE_MCAST)) + return -EOPNOTSUPP; + + /* these settings will always override what we currently have */ + adapter->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + adapter->wol |= ATLX_WUFC_MAG; + if (wol->wolopts & WAKE_PHY) + adapter->wol |= ATLX_WUFC_LNKC; + + return 0; +} + +static int atl2_nway_reset(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + if (netif_running(netdev)) + atl2_reinit_locked(adapter); + return 0; +} + +static struct ethtool_ops atl2_ethtool_ops = { + .get_settings = atl2_get_settings, + .set_settings = atl2_set_settings, + .get_drvinfo = atl2_get_drvinfo, + .get_regs_len = atl2_get_regs_len, + .get_regs = atl2_get_regs, + .get_wol = atl2_get_wol, + .set_wol = atl2_set_wol, + .get_msglevel = atl2_get_msglevel, + .set_msglevel = atl2_set_msglevel, + .nway_reset = atl2_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = atl2_get_eeprom_len, + .get_eeprom = atl2_get_eeprom, + .set_eeprom = atl2_set_eeprom, + .get_tx_csum = atl2_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#ifdef NETIF_F_TSO + .get_tso = ethtool_op_get_tso, +#endif +}; + +static void atl2_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops); +} + +#define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \ + (((a) & 0xff00ff00) >> 8)) +#define LONGSWAP(a) ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16)) +#define SHORTSWAP(a) (((a) << 8) | ((a) >> 8)) + +/* + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + * return : 0 or idle status (if error) + */ +static s32 atl2_reset_hw(struct atl2_hw *hw) +{ + u32 icr; + u16 pci_cfg_cmd_word; + int i; + + /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ + atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); + if ((pci_cfg_cmd_word & + (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) != + (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) { + pci_cfg_cmd_word |= + (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER); + atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); + } + + /* Clear Interrupt mask to stop board from generating + * interrupts & Clear any pending interrupt events + */ + /* FIXME */ + /* ATL2_WRITE_REG(hw, REG_IMR, 0); */ + /* ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); */ + + /* Issue Soft Reset to the MAC. This will reset the chip's + * transmit, receive, DMA. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); + wmb(); + msleep(1); /* delay about 1ms */ + + /* Wait at least 10ms for All module to be Idle */ + for (i = 0; i < 10; i++) { + icr = ATL2_READ_REG(hw, REG_IDLE_STATUS); + if (!icr) + break; + msleep(1); /* delay 1 ms */ + cpu_relax(); + } + + if (icr) + return icr; + + return 0; +} + +#define CUSTOM_SPI_CS_SETUP 2 +#define CUSTOM_SPI_CLK_HI 2 +#define CUSTOM_SPI_CLK_LO 2 +#define CUSTOM_SPI_CS_HOLD 2 +#define CUSTOM_SPI_CS_HI 3 + +static struct atl2_spi_flash_dev flash_table[] = +{ +/* MFR WRSR READ PROGRAM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */ +{"Atmel", 0x0, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62 }, +{"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60 }, +{"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7 }, +}; + +static bool atl2_spi_read(struct atl2_hw *hw, u32 addr, u32 *buf) +{ + int i; + u32 value; + + ATL2_WRITE_REG(hw, REG_SPI_DATA, 0); + ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr); + + value = SPI_FLASH_CTRL_WAIT_READY | + (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << + SPI_FLASH_CTRL_CS_SETUP_SHIFT | + (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) << + SPI_FLASH_CTRL_CLK_HI_SHIFT | + (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) << + SPI_FLASH_CTRL_CLK_LO_SHIFT | + (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) << + SPI_FLASH_CTRL_CS_HOLD_SHIFT | + (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) << + SPI_FLASH_CTRL_CS_HI_SHIFT | + (0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT; + + ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + + value |= SPI_FLASH_CTRL_START; + + ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + + for (i = 0; i < 10; i++) { + msleep(1); + value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); + if (!(value & SPI_FLASH_CTRL_START)) + break; + } + + if (value & SPI_FLASH_CTRL_START) + return false; + + *buf = ATL2_READ_REG(hw, REG_SPI_DATA); + + return true; +} + +/* + * get_permanent_address + * return 0 if get valid mac address, + */ +static int get_permanent_address(struct atl2_hw *hw) +{ + u32 Addr[2]; + u32 i, Control; + u16 Register; + u8 EthAddr[NODE_ADDRESS_SIZE]; + bool KeyValid; + + if (is_valid_ether_addr(hw->perm_mac_addr)) + return 0; + + Addr[0] = 0; + Addr[1] = 0; + + if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */ + Register = 0; + KeyValid = false; + + /* Read out all EEPROM content */ + i = 0; + while (1) { + if (atl2_read_eeprom(hw, i + 0x100, &Control)) { + if (KeyValid) { + if (Register == REG_MAC_STA_ADDR) + Addr[0] = Control; + else if (Register == + (REG_MAC_STA_ADDR + 4)) + Addr[1] = Control; + KeyValid = false; + } else if ((Control & 0xff) == 0x5A) { + KeyValid = true; + Register = (u16) (Control >> 16); + } else { + /* assume data end while encount an invalid KEYWORD */ + break; + } + } else { + break; /* read error */ + } + i += 4; + } + + *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); + *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]); + + if (is_valid_ether_addr(EthAddr)) { + memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE); + return 0; + } + return 1; + } + + /* see if SPI flash exists? */ + Addr[0] = 0; + Addr[1] = 0; + Register = 0; + KeyValid = false; + i = 0; + while (1) { + if (atl2_spi_read(hw, i + 0x1f000, &Control)) { + if (KeyValid) { + if (Register == REG_MAC_STA_ADDR) + Addr[0] = Control; + else if (Register == (REG_MAC_STA_ADDR + 4)) + Addr[1] = Control; + KeyValid = false; + } else if ((Control & 0xff) == 0x5A) { + KeyValid = true; + Register = (u16) (Control >> 16); + } else { + break; /* data end */ + } + } else { + break; /* read error */ + } + i += 4; + } + + *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); + *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]); + if (is_valid_ether_addr(EthAddr)) { + memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE); + return 0; + } + /* maybe MAC-address is from BIOS */ + Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); + Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4); + *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); + *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]); + + if (is_valid_ether_addr(EthAddr)) { + memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE); + return 0; + } + + return 1; +} + +/* + * Reads the adapter's MAC address from the EEPROM + * + * hw - Struct containing variables accessed by shared code + */ +static s32 atl2_read_mac_addr(struct atl2_hw *hw) +{ + u16 i; + + if (get_permanent_address(hw)) { + /* for test */ + /* FIXME: shouldn't we use random_ether_addr() here? */ + hw->perm_mac_addr[0] = 0x00; + hw->perm_mac_addr[1] = 0x13; + hw->perm_mac_addr[2] = 0x74; + hw->perm_mac_addr[3] = 0x00; + hw->perm_mac_addr[4] = 0x5c; + hw->perm_mac_addr[5] = 0x38; + } + + for (i = 0; i < NODE_ADDRESS_SIZE; i++) + hw->mac_addr[i] = hw->perm_mac_addr[i]; + + return 0; +} + +/* + * Hashes an address to determine its location in the multicast table + * + * hw - Struct containing variables accessed by shared code + * mc_addr - the multicast address to hash + * + * atl2_hash_mc_addr + * purpose + * set hash value for a multicast address + * hash calcu processing : + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ +static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr) +{ + u32 crc32, value; + int i; + + value = 0; + crc32 = ether_crc_le(6, mc_addr); + + for (i = 0; i < 32; i++) + value |= (((crc32 >> i) & 1) << (31 - i)); + + return value; +} + +/* + * Sets the bit in the multicast table corresponding to the hash value. + * + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + */ +static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg; + u32 mta; + + /* The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 31) & 0x1; + hash_bit = (hash_value >> 26) & 0x1F; + + mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); + + mta |= (1 << hash_bit); + + ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); +} + +/* + * atl2_init_pcie - init PCIE module + */ +static void atl2_init_pcie(struct atl2_hw *hw) +{ + u32 value; + value = LTSSM_TEST_MODE_DEF; + ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value); + + value = PCIE_DLL_TX_CTRL1_DEF; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value); +} + +static void atl2_init_flash_opcode(struct atl2_hw *hw) +{ + if (hw->flash_vendor >= ARRAY_SIZE(flash_table)) + hw->flash_vendor = 0; /* ATMEL */ + + /* Init OP table */ + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM, + flash_table[hw->flash_vendor].cmdPROGRAM); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE, + flash_table[hw->flash_vendor].cmdSECTOR_ERASE); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE, + flash_table[hw->flash_vendor].cmdCHIP_ERASE); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID, + flash_table[hw->flash_vendor].cmdRDID); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN, + flash_table[hw->flash_vendor].cmdWREN); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR, + flash_table[hw->flash_vendor].cmdRDSR); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR, + flash_table[hw->flash_vendor].cmdWRSR); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ, + flash_table[hw->flash_vendor].cmdREAD); +} + +/******************************************************************** +* Performs basic configuration of the adapter. +* +* hw - Struct containing variables accessed by shared code +* Assumes that the controller has previously been reset and is in a +* post-reset uninitialized state. Initializes multicast table, +* and Calls routines to setup link +* Leaves the transmit and receive units disabled and uninitialized. +********************************************************************/ +static s32 atl2_init_hw(struct atl2_hw *hw) +{ + u32 ret_val = 0; + + atl2_init_pcie(hw); + + /* Zero out the Multicast HASH table */ + /* clear the old settings from the multicast hash table */ + ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + atl2_init_flash_opcode(hw); + + ret_val = atl2_phy_init(hw); + + return ret_val; +} + +/* + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + */ +static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, + u16 *duplex) +{ + s32 ret_val; + u16 phy_data; + + /* Read PHY Specific Status Register (17) */ + ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED)) + return ATLX_ERR_PHY_RES; + + switch (phy_data & MII_ATLX_PSSR_SPEED) { + case MII_ATLX_PSSR_100MBS: + *speed = SPEED_100; + break; + case MII_ATLX_PSSR_10MBS: + *speed = SPEED_10; + break; + default: + return ATLX_ERR_PHY_SPEED; + break; + } + + if (phy_data & MII_ATLX_PSSR_DPLX) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + return 0; +} + +/* + * Reads the value from a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to read + */ +static s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data) +{ + u32 val; + int i; + + val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | + MDIO_START | + MDIO_SUP_PREAMBLE | + MDIO_RW | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); + + wmb(); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + val = ATL2_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + wmb(); + } + if (!(val & (MDIO_START | MDIO_BUSY))) { + *phy_data = (u16)val; + return 0; + } + + return ATLX_ERR_PHY; +} + +/* + * Writes a value to a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to write + * data - data to write to the PHY + */ +static s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data) +{ + int i; + u32 val; + + val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | + (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | + MDIO_SUP_PREAMBLE | + MDIO_START | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); + + wmb(); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + val = ATL2_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + + wmb(); + } + + if (!(val & (MDIO_START | MDIO_BUSY))) + return 0; + + return ATLX_ERR_PHY; +} + +/* + * Configures PHY autoneg and flow control advertisement settings + * + * hw - Struct containing variables accessed by shared code + */ +static s32 atl2_phy_setup_autoneg_adv(struct atl2_hw *hw) +{ + s32 ret_val; + s16 mii_autoneg_adv_reg; + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; + + /* Need to parse autoneg_advertised and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). */ + mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; + + /* Need to parse MediaType and setup the + * appropriate PHY registers. */ + switch (hw->MediaType) { + case MEDIA_TYPE_AUTO_SENSOR: + mii_autoneg_adv_reg |= + (MII_AR_10T_HD_CAPS | + MII_AR_10T_FD_CAPS | + MII_AR_100TX_HD_CAPS| + MII_AR_100TX_FD_CAPS); + hw->autoneg_advertised = + ADVERTISE_10_HALF | + ADVERTISE_10_FULL | + ADVERTISE_100_HALF| + ADVERTISE_100_FULL; + break; + case MEDIA_TYPE_100M_FULL: + mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; + hw->autoneg_advertised = ADVERTISE_100_FULL; + break; + case MEDIA_TYPE_100M_HALF: + mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; + hw->autoneg_advertised = ADVERTISE_100_HALF; + break; + case MEDIA_TYPE_10M_FULL: + mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; + hw->autoneg_advertised = ADVERTISE_10_FULL; + break; + default: + mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; + hw->autoneg_advertised = ADVERTISE_10_HALF; + break; + } + + /* flow control fixed to enable all */ + mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); + + hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; + + ret_val = atl2_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); + + if (ret_val) + return ret_val; + + return 0; +} + +/* + * Resets the PHY and make all config validate + * + * hw - Struct containing variables accessed by shared code + * + * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) + */ +static s32 atl2_phy_commit(struct atl2_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG; + ret_val = atl2_write_phy_reg(hw, MII_BMCR, phy_data); + if (ret_val) { + u32 val; + int i; + /* pcie serdes link may be down ! */ + for (i = 0; i < 25; i++) { + msleep(1); + val = ATL2_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if (0 != (val & (MDIO_START | MDIO_BUSY))) { + printk(KERN_ERR "atl2: PCIe link down for at least 25ms !\n"); + return ret_val; + } + } + return 0; +} + +static s32 atl2_phy_init(struct atl2_hw *hw) +{ + s32 ret_val; + u16 phy_val; + + if (hw->phy_configured) + return 0; + + /* Enable PHY */ + ATL2_WRITE_REGW(hw, REG_PHY_ENABLE, 1); + ATL2_WRITE_FLUSH(hw); + msleep(1); + + /* check if the PHY is in powersaving mode */ + atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); + atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); + + /* 024E / 124E 0r 0274 / 1274 ? */ + if (phy_val & 0x1000) { + phy_val &= ~0x1000; + atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val); + } + + msleep(1); + + /*Enable PHY LinkChange Interrupt */ + ret_val = atl2_write_phy_reg(hw, 18, 0xC00); + if (ret_val) + return ret_val; + + /* setup AutoNeg parameters */ + ret_val = atl2_phy_setup_autoneg_adv(hw); + if (ret_val) + return ret_val; + + /* SW.Reset & En-Auto-Neg to restart Auto-Neg */ + ret_val = atl2_phy_commit(hw); + if (ret_val) + return ret_val; + + hw->phy_configured = true; + + return ret_val; +} + +static void atl2_set_mac_addr(struct atl2_hw *hw) +{ + u32 value; + /* 00-0B-6A-F6-00-DC + * 0: 6AF600DC 1: 000B + * low dword */ + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8) | + (((u32)hw->mac_addr[5])); + ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); + /* hight dword */ + value = (((u32)hw->mac_addr[0]) << 8) | + (((u32)hw->mac_addr[1])); + ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); +} + +/* + * check_eeprom_exist + * return 0 if eeprom exist + */ +static int atl2_check_eeprom_exist(struct atl2_hw *hw) +{ + u32 value; + + value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); + if (value & SPI_FLASH_CTRL_EN_VPD) { + value &= ~SPI_FLASH_CTRL_EN_VPD; + ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + } + value = ATL2_READ_REGW(hw, REG_PCIE_CAP_LIST); + return ((value & 0xFF00) == 0x6C00) ? 0 : 1; +} + +/* FIXME: This doesn't look right. -- CHS */ +static bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value) +{ + return true; +} + +static bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue) +{ + int i; + u32 Control; + + if (Offset & 0x3) + return false; /* address do not align */ + + ATL2_WRITE_REG(hw, REG_VPD_DATA, 0); + Control = (Offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; + ATL2_WRITE_REG(hw, REG_VPD_CAP, Control); + + for (i = 0; i < 10; i++) { + msleep(2); + Control = ATL2_READ_REG(hw, REG_VPD_CAP); + if (Control & VPD_CAP_VPD_FLAG) + break; + } + + if (Control & VPD_CAP_VPD_FLAG) { + *pValue = ATL2_READ_REG(hw, REG_VPD_DATA); + return true; + } + return false; /* timeout */ +} + +static void atl2_force_ps(struct atl2_hw *hw) +{ + u16 phy_val; + + atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); + atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); + atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val | 0x1000); + + atl2_write_phy_reg(hw, MII_DBG_ADDR, 2); + atl2_write_phy_reg(hw, MII_DBG_DATA, 0x3000); + atl2_write_phy_reg(hw, MII_DBG_ADDR, 3); + atl2_write_phy_reg(hw, MII_DBG_DATA, 0); +} + +/* This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. + */ +#define ATL2_MAX_NIC 4 + +#define OPTION_UNSET -1 +#define OPTION_DISABLED 0 +#define OPTION_ENABLED 1 + +/* All parameters are treated the same, as an integer array of values. + * This macro just reduces the need to repeat the same declaration code + * over and over (plus this helps to avoid typo bugs). + */ +#define ATL2_PARAM_INIT {[0 ... ATL2_MAX_NIC] = OPTION_UNSET} +#ifndef module_param_array +/* Module Parameters are always initialized to -1, so that the driver + * can tell the difference between no user specified value or the + * user asking for the default value. + * The true default values are loaded in when atl2_check_options is called. + * + * This is a GCC extension to ANSI C. + * See the item "Labeled Elements in Initializers" in the section + * "Extensions to the C Language Family" of the GCC documentation. + */ + +#define ATL2_PARAM(X, desc) \ + static const int __devinitdata X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \ + MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \ + MODULE_PARM_DESC(X, desc); +#else +#define ATL2_PARAM(X, desc) \ + static int __devinitdata X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \ + static int num_##X = 0; \ + module_param_array_named(X, X, int, &num_##X, 0); \ + MODULE_PARM_DESC(X, desc); +#endif + +/* + * Transmit Memory Size + * Valid Range: 64-2048 + * Default Value: 128 + */ +#define ATL2_MIN_TX_MEMSIZE 4 /* 4KB */ +#define ATL2_MAX_TX_MEMSIZE 64 /* 64KB */ +#define ATL2_DEFAULT_TX_MEMSIZE 8 /* 8KB */ +ATL2_PARAM(TxMemSize, "Bytes of Transmit Memory"); + +/* + * Receive Memory Block Count + * Valid Range: 16-512 + * Default Value: 128 + */ +#define ATL2_MIN_RXD_COUNT 16 +#define ATL2_MAX_RXD_COUNT 512 +#define ATL2_DEFAULT_RXD_COUNT 64 +ATL2_PARAM(RxMemBlock, "Number of receive memory block"); + +/* + * User Specified MediaType Override + * + * Valid Range: 0-5 + * - 0 - auto-negotiate at all supported speeds + * - 1 - only link at 1000Mbps Full Duplex + * - 2 - only link at 100Mbps Full Duplex + * - 3 - only link at 100Mbps Half Duplex + * - 4 - only link at 10Mbps Full Duplex + * - 5 - only link at 10Mbps Half Duplex + * Default Value: 0 + */ +ATL2_PARAM(MediaType, "MediaType Select"); + +/* + * Interrupt Moderate Timer in units of 2048 ns (~2 us) + * Valid Range: 10-65535 + * Default Value: 45000(90ms) + */ +#define INT_MOD_DEFAULT_CNT 100 /* 200us */ +#define INT_MOD_MAX_CNT 65000 +#define INT_MOD_MIN_CNT 50 +ATL2_PARAM(IntModTimer, "Interrupt Moderator Timer"); + +/* + * FlashVendor + * Valid Range: 0-2 + * 0 - Atmel + * 1 - SST + * 2 - ST + */ +ATL2_PARAM(FlashVendor, "SPI Flash Vendor"); + +#define AUTONEG_ADV_DEFAULT 0x2F +#define AUTONEG_ADV_MASK 0x2F +#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL + +#define FLASH_VENDOR_DEFAULT 0 +#define FLASH_VENDOR_MIN 0 +#define FLASH_VENDOR_MAX 2 + +struct atl2_option { + enum { enable_option, range_option, list_option } type; + char *name; + char *err; + int def; + union { + struct { /* range_option info */ + int min; + int max; + } r; + struct { /* list_option info */ + int nr; + struct atl2_opt_list { int i; char *str; } *p; + } l; + } arg; +}; + +static int __devinit atl2_validate_option(int *value, struct atl2_option *opt) +{ + int i; + struct atl2_opt_list *ent; + + if (*value == OPTION_UNSET) { + *value = opt->def; + return 0; + } + + switch (opt->type) { + case enable_option: + switch (*value) { + case OPTION_ENABLED: + printk(KERN_INFO "%s Enabled\n", opt->name); + return 0; + break; + case OPTION_DISABLED: + printk(KERN_INFO "%s Disabled\n", opt->name); + return 0; + break; + } + break; + case range_option: + if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { + printk(KERN_INFO "%s set to %i\n", opt->name, *value); + return 0; + } + break; + case list_option: + for (i = 0; i < opt->arg.l.nr; i++) { + ent = &opt->arg.l.p[i]; + if (*value == ent->i) { + if (ent->str[0] != '\0') + printk(KERN_INFO "%s\n", ent->str); + return 0; + } + } + break; + default: + BUG(); + } + + printk(KERN_INFO "Invalid %s specified (%i) %s\n", + opt->name, *value, opt->err); + *value = opt->def; + return -1; +} + +/* + * atl2_check_options - Range Checking for Command Line Parameters + * @adapter: board private structure + * + * This routine checks all command line parameters for valid user + * input. If an invalid value is given, or if no user specified + * value exists, a default value is used. The final value is stored + * in a variable in the adapter structure. + */ +static void __devinit atl2_check_options(struct atl2_adapter *adapter) +{ + int val; + struct atl2_option opt; + int bd = adapter->bd_number; + if (bd >= ATL2_MAX_NIC) { + printk(KERN_NOTICE "Warning: no configuration for board #%i\n", + bd); + printk(KERN_NOTICE "Using defaults for all values\n"); +#ifndef module_param_array + bd = ATL2_MAX_NIC; +#endif + } + + /* Bytes of Transmit Memory */ + opt.type = range_option; + opt.name = "Bytes of Transmit Memory"; + opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_TX_MEMSIZE); + opt.def = ATL2_DEFAULT_TX_MEMSIZE; + opt.arg.r.min = ATL2_MIN_TX_MEMSIZE; + opt.arg.r.max = ATL2_MAX_TX_MEMSIZE; +#ifdef module_param_array + if (num_TxMemSize > bd) { +#endif + val = TxMemSize[bd]; + atl2_validate_option(&val, &opt); + adapter->txd_ring_size = ((u32) val) * 1024; +#ifdef module_param_array + } else + adapter->txd_ring_size = ((u32)opt.def) * 1024; +#endif + /* txs ring size: */ + adapter->txs_ring_size = adapter->txd_ring_size / 128; + if (adapter->txs_ring_size > 160) + adapter->txs_ring_size = 160; + + /* Receive Memory Block Count */ + opt.type = range_option; + opt.name = "Number of receive memory block"; + opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_RXD_COUNT); + opt.def = ATL2_DEFAULT_RXD_COUNT; + opt.arg.r.min = ATL2_MIN_RXD_COUNT; + opt.arg.r.max = ATL2_MAX_RXD_COUNT; +#ifdef module_param_array + if (num_RxMemBlock > bd) { +#endif + val = RxMemBlock[bd]; + atl2_validate_option(&val, &opt); + adapter->rxd_ring_size = (u32)val; + /* FIXME */ + /* ((u16)val)&~1; */ /* even number */ +#ifdef module_param_array + } else + adapter->rxd_ring_size = (u32)opt.def; +#endif + /* init RXD Flow control value */ + adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size / 8) * 7; + adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT / 8) > + (adapter->rxd_ring_size / 12) ? (ATL2_MIN_RXD_COUNT / 8) : + (adapter->rxd_ring_size / 12); + + /* Interrupt Moderate Timer */ + opt.type = range_option; + opt.name = "Interrupt Moderate Timer"; + opt.err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT); + opt.def = INT_MOD_DEFAULT_CNT; + opt.arg.r.min = INT_MOD_MIN_CNT; + opt.arg.r.max = INT_MOD_MAX_CNT; +#ifdef module_param_array + if (num_IntModTimer > bd) { +#endif + val = IntModTimer[bd]; + atl2_validate_option(&val, &opt); + adapter->imt = (u16) val; +#ifdef module_param_array + } else + adapter->imt = (u16)(opt.def); +#endif + /* Flash Vendor */ + opt.type = range_option; + opt.name = "SPI Flash Vendor"; + opt.err = "using default of " __MODULE_STRING(FLASH_VENDOR_DEFAULT); + opt.def = FLASH_VENDOR_DEFAULT; + opt.arg.r.min = FLASH_VENDOR_MIN; + opt.arg.r.max = FLASH_VENDOR_MAX; +#ifdef module_param_array + if (num_FlashVendor > bd) { +#endif + val = FlashVendor[bd]; + atl2_validate_option(&val, &opt); + adapter->hw.flash_vendor = (u8) val; +#ifdef module_param_array + } else + adapter->hw.flash_vendor = (u8)(opt.def); +#endif + /* MediaType */ + opt.type = range_option; + opt.name = "Speed/Duplex Selection"; + opt.err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR); + opt.def = MEDIA_TYPE_AUTO_SENSOR; + opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR; + opt.arg.r.max = MEDIA_TYPE_10M_HALF; +#ifdef module_param_array + if (num_MediaType > bd) { +#endif + val = MediaType[bd]; + atl2_validate_option(&val, &opt); + adapter->hw.MediaType = (u16) val; +#ifdef module_param_array + } else + adapter->hw.MediaType = (u16)(opt.def); +#endif +} diff --git a/drivers/net/atlx/atl2.h b/drivers/net/atlx/atl2.h new file mode 100644 index 000000000000..6e1f28ff227b --- /dev/null +++ b/drivers/net/atlx/atl2.h @@ -0,0 +1,530 @@ +/* atl2.h -- atl2 driver definitions + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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. + * + * 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, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL2_H_ +#define _ATL2_H_ + +#include +#include + +#ifndef _ATL2_HW_H_ +#define _ATL2_HW_H_ + +#ifndef _ATL2_OSDEP_H_ +#define _ATL2_OSDEP_H_ + +#include +#include +#include +#include + +#include "atlx.h" + +#ifdef ETHTOOL_OPS_COMPAT +extern int ethtool_ioctl(struct ifreq *ifr); +#endif + +#define PCI_COMMAND_REGISTER PCI_COMMAND +#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE +#define ETH_ADDR_LEN ETH_ALEN + +#define ATL2_WRITE_REG(a, reg, value) (iowrite32((value), \ + ((a)->hw_addr + (reg)))) + +#define ATL2_WRITE_FLUSH(a) (ioread32((a)->hw_addr)) + +#define ATL2_READ_REG(a, reg) (ioread32((a)->hw_addr + (reg))) + +#define ATL2_WRITE_REGB(a, reg, value) (iowrite8((value), \ + ((a)->hw_addr + (reg)))) + +#define ATL2_READ_REGB(a, reg) (ioread8((a)->hw_addr + (reg))) + +#define ATL2_WRITE_REGW(a, reg, value) (iowrite16((value), \ + ((a)->hw_addr + (reg)))) + +#define ATL2_READ_REGW(a, reg) (ioread16((a)->hw_addr + (reg))) + +#define ATL2_WRITE_REG_ARRAY(a, reg, offset, value) \ + (iowrite32((value), (((a)->hw_addr + (reg)) + ((offset) << 2)))) + +#define ATL2_READ_REG_ARRAY(a, reg, offset) \ + (ioread32(((a)->hw_addr + (reg)) + ((offset) << 2))) + +#endif /* _ATL2_OSDEP_H_ */ + +struct atl2_adapter; +struct atl2_hw; + +/* function prototype */ +static s32 atl2_reset_hw(struct atl2_hw *hw); +static s32 atl2_read_mac_addr(struct atl2_hw *hw); +static s32 atl2_init_hw(struct atl2_hw *hw); +static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, + u16 *duplex); +static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr); +static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value); +static s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data); +static s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data); +static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value); +static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value); +static void atl2_set_mac_addr(struct atl2_hw *hw); +static bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue); +static bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value); +static s32 atl2_phy_init(struct atl2_hw *hw); +static int atl2_check_eeprom_exist(struct atl2_hw *hw); +static void atl2_force_ps(struct atl2_hw *hw); + +/* register definition */ + +/* Block IDLE Status Register */ +#define IDLE_STATUS_RXMAC 1 /* 1: RXMAC is non-IDLE */ +#define IDLE_STATUS_TXMAC 2 /* 1: TXMAC is non-IDLE */ +#define IDLE_STATUS_DMAR 8 /* 1: DMAR is non-IDLE */ +#define IDLE_STATUS_DMAW 4 /* 1: DMAW is non-IDLE */ + +/* MDIO Control Register */ +#define MDIO_WAIT_TIMES 10 + +/* MAC Control Register */ +#define MAC_CTRL_DBG_TX_BKPRESURE 0x100000 /* 1: TX max backoff */ +#define MAC_CTRL_MACLP_CLK_PHY 0x8000000 /* 1: 25MHz from phy */ +#define MAC_CTRL_HALF_LEFT_BUF_SHIFT 28 +#define MAC_CTRL_HALF_LEFT_BUF_MASK 0xF /* MAC retry buf x32B */ + +/* Internal SRAM Partition Register */ +#define REG_SRAM_TXRAM_END 0x1500 /* Internal tail address of TXRAM + * default: 2byte*1024 */ +#define REG_SRAM_RXRAM_END 0x1502 /* Internal tail address of RXRAM + * default: 2byte*1024 */ + +/* Descriptor Control register */ +#define REG_TXD_BASE_ADDR_LO 0x1544 /* The base address of the Transmit + * Data Mem low 32-bit(dword align) */ +#define REG_TXD_MEM_SIZE 0x1548 /* Transmit Data Memory size(by + * double word , max 256KB) */ +#define REG_TXS_BASE_ADDR_LO 0x154C /* The base address of the Transmit + * Status Memory low 32-bit(dword word + * align) */ +#define REG_TXS_MEM_SIZE 0x1550 /* double word unit, max 4*2047 + * bytes. */ +#define REG_RXD_BASE_ADDR_LO 0x1554 /* The base address of the Transmit + * Status Memory low 32-bit(unit 8 + * bytes) */ +#define REG_RXD_BUF_NUM 0x1558 /* Receive Data & Status Memory buffer + * number (unit 1536bytes, max + * 1536*2047) */ + +/* DMAR Control Register */ +#define REG_DMAR 0x1580 +#define DMAR_EN 0x1 /* 1: Enable DMAR */ + +/* TX Cur-Through (early tx threshold) Control Register */ +#define REG_TX_CUT_THRESH 0x1590 /* TxMac begin transmit packet + * threshold(unit word) */ + +/* DMAW Control Register */ +#define REG_DMAW 0x15A0 +#define DMAW_EN 0x1 + +/* Flow control register */ +#define REG_PAUSE_ON_TH 0x15A8 /* RXD high watermark of overflow + * threshold configuration register */ +#define REG_PAUSE_OFF_TH 0x15AA /* RXD lower watermark of overflow + * threshold configuration register */ + +/* Mailbox Register */ +#define REG_MB_TXD_WR_IDX 0x15f0 /* double word align */ +#define REG_MB_RXD_RD_IDX 0x15F4 /* RXD Read index (unit: 1536byets) */ + +/* Interrupt Status Register */ +#define ISR_TIMER 1 /* Interrupt when Timer counts down to zero */ +#define ISR_MANUAL 2 /* Software manual interrupt, for debug. Set + * when SW_MAN_INT_EN is set in Table 51 + * Selene Master Control Register + * (Offset 0x1400). */ +#define ISR_RXF_OV 4 /* RXF overflow interrupt */ +#define ISR_TXF_UR 8 /* TXF underrun interrupt */ +#define ISR_TXS_OV 0x10 /* Internal transmit status buffer full + * interrupt */ +#define ISR_RXS_OV 0x20 /* Internal receive status buffer full + * interrupt */ +#define ISR_LINK_CHG 0x40 /* Link Status Change Interrupt */ +#define ISR_HOST_TXD_UR 0x80 +#define ISR_HOST_RXD_OV 0x100 /* Host rx data memory full , one pulse */ +#define ISR_DMAR_TO_RST 0x200 /* DMAR op timeout interrupt. SW should + * do Reset */ +#define ISR_DMAW_TO_RST 0x400 +#define ISR_PHY 0x800 /* phy interrupt */ +#define ISR_TS_UPDATE 0x10000 /* interrupt after new tx pkt status written + * to host */ +#define ISR_RS_UPDATE 0x20000 /* interrupt ater new rx pkt status written + * to host. */ +#define ISR_TX_EARLY 0x40000 /* interrupt when txmac begin transmit one + * packet */ + +#define ISR_TX_EVENT (ISR_TXF_UR | ISR_TXS_OV | ISR_HOST_TXD_UR |\ + ISR_TS_UPDATE | ISR_TX_EARLY) +#define ISR_RX_EVENT (ISR_RXF_OV | ISR_RXS_OV | ISR_HOST_RXD_OV |\ + ISR_RS_UPDATE) + +#define IMR_NORMAL_MASK (\ + /*ISR_LINK_CHG |*/\ + ISR_MANUAL |\ + ISR_DMAR_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_PHY |\ + ISR_PHY_LINKDOWN |\ + ISR_TS_UPDATE |\ + ISR_RS_UPDATE) + +/* Receive MAC Statistics Registers */ +#define REG_STS_RX_PAUSE 0x1700 /* Num pause packets received */ +#define REG_STS_RXD_OV 0x1704 /* Num frames dropped due to RX + * FIFO overflow */ +#define REG_STS_RXS_OV 0x1708 /* Num frames dropped due to RX + * Status Buffer Overflow */ +#define REG_STS_RX_FILTER 0x170C /* Num packets dropped due to + * address filtering */ + +/* MII definitions */ + +/* PHY Common Register */ +#define MII_SMARTSPEED 0x14 +#define MII_DBG_ADDR 0x1D +#define MII_DBG_DATA 0x1E + +/* PCI Command Register Bit Definitions */ +#define PCI_REG_COMMAND 0x04 +#define CMD_IO_SPACE 0x0001 +#define CMD_MEMORY_SPACE 0x0002 +#define CMD_BUS_MASTER 0x0004 + +#define MEDIA_TYPE_100M_FULL 1 +#define MEDIA_TYPE_100M_HALF 2 +#define MEDIA_TYPE_10M_FULL 3 +#define MEDIA_TYPE_10M_HALF 4 + +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x000F /* Everything */ + +/* The size (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* with FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* with FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAX_JUMBO_FRAME_SIZE 0x2000 +#define VLAN_SIZE 4 + +struct tx_pkt_header { + unsigned pkt_size:11; + unsigned:4; /* reserved */ + unsigned ins_vlan:1; /* txmac should insert vlan */ + unsigned short vlan; /* vlan tag */ +}; +/* FIXME: replace above bitfields with MASK/SHIFT defines below */ +#define TX_PKT_HEADER_SIZE_MASK 0x7FF +#define TX_PKT_HEADER_SIZE_SHIFT 0 +#define TX_PKT_HEADER_INS_VLAN_MASK 0x1 +#define TX_PKT_HEADER_INS_VLAN_SHIFT 15 +#define TX_PKT_HEADER_VLAN_TAG_MASK 0xFFFF +#define TX_PKT_HEADER_VLAN_TAG_SHIFT 16 + +struct tx_pkt_status { + unsigned pkt_size:11; + unsigned:5; /* reserved */ + unsigned ok:1; /* current packet transmitted without error */ + unsigned bcast:1; /* broadcast packet */ + unsigned mcast:1; /* multicast packet */ + unsigned pause:1; /* transmiited a pause frame */ + unsigned ctrl:1; + unsigned defer:1; /* current packet is xmitted with defer */ + unsigned exc_defer:1; + unsigned single_col:1; + unsigned multi_col:1; + unsigned late_col:1; + unsigned abort_col:1; + unsigned underun:1; /* current packet is aborted + * due to txram underrun */ + unsigned:3; /* reserved */ + unsigned update:1; /* always 1'b1 in tx_status_buf */ +}; +/* FIXME: replace above bitfields with MASK/SHIFT defines below */ +#define TX_PKT_STATUS_SIZE_MASK 0x7FF +#define TX_PKT_STATUS_SIZE_SHIFT 0 +#define TX_PKT_STATUS_OK_MASK 0x1 +#define TX_PKT_STATUS_OK_SHIFT 16 +#define TX_PKT_STATUS_BCAST_MASK 0x1 +#define TX_PKT_STATUS_BCAST_SHIFT 17 +#define TX_PKT_STATUS_MCAST_MASK 0x1 +#define TX_PKT_STATUS_MCAST_SHIFT 18 +#define TX_PKT_STATUS_PAUSE_MASK 0x1 +#define TX_PKT_STATUS_PAUSE_SHIFT 19 +#define TX_PKT_STATUS_CTRL_MASK 0x1 +#define TX_PKT_STATUS_CTRL_SHIFT 20 +#define TX_PKT_STATUS_DEFER_MASK 0x1 +#define TX_PKT_STATUS_DEFER_SHIFT 21 +#define TX_PKT_STATUS_EXC_DEFER_MASK 0x1 +#define TX_PKT_STATUS_EXC_DEFER_SHIFT 22 +#define TX_PKT_STATUS_SINGLE_COL_MASK 0x1 +#define TX_PKT_STATUS_SINGLE_COL_SHIFT 23 +#define TX_PKT_STATUS_MULTI_COL_MASK 0x1 +#define TX_PKT_STATUS_MULTI_COL_SHIFT 24 +#define TX_PKT_STATUS_LATE_COL_MASK 0x1 +#define TX_PKT_STATUS_LATE_COL_SHIFT 25 +#define TX_PKT_STATUS_ABORT_COL_MASK 0x1 +#define TX_PKT_STATUS_ABORT_COL_SHIFT 26 +#define TX_PKT_STATUS_UNDERRUN_MASK 0x1 +#define TX_PKT_STATUS_UNDERRUN_SHIFT 27 +#define TX_PKT_STATUS_UPDATE_MASK 0x1 +#define TX_PKT_STATUS_UPDATE_SHIFT 31 + +struct rx_pkt_status { + unsigned pkt_size:11; /* packet size, max 2047 bytes */ + unsigned:5; /* reserved */ + unsigned ok:1; /* current packet received ok without error */ + unsigned bcast:1; /* current packet is broadcast */ + unsigned mcast:1; /* current packet is multicast */ + unsigned pause:1; + unsigned ctrl:1; + unsigned crc:1; /* received a packet with crc error */ + unsigned code:1; /* received a packet with code error */ + unsigned runt:1; /* received a packet less than 64 bytes + * with good crc */ + unsigned frag:1; /* received a packet less than 64 bytes + * with bad crc */ + unsigned trunc:1; /* current frame truncated due to rxram full */ + unsigned align:1; /* this packet is alignment error */ + unsigned vlan:1; /* this packet has vlan */ + unsigned:3; /* reserved */ + unsigned update:1; + unsigned short vtag; /* vlan tag */ + unsigned:16; +}; +/* FIXME: replace above bitfields with MASK/SHIFT defines below */ +#define RX_PKT_STATUS_SIZE_MASK 0x7FF +#define RX_PKT_STATUS_SIZE_SHIFT 0 +#define RX_PKT_STATUS_OK_MASK 0x1 +#define RX_PKT_STATUS_OK_SHIFT 16 +#define RX_PKT_STATUS_BCAST_MASK 0x1 +#define RX_PKT_STATUS_BCAST_SHIFT 17 +#define RX_PKT_STATUS_MCAST_MASK 0x1 +#define RX_PKT_STATUS_MCAST_SHIFT 18 +#define RX_PKT_STATUS_PAUSE_MASK 0x1 +#define RX_PKT_STATUS_PAUSE_SHIFT 19 +#define RX_PKT_STATUS_CTRL_MASK 0x1 +#define RX_PKT_STATUS_CTRL_SHIFT 20 +#define RX_PKT_STATUS_CRC_MASK 0x1 +#define RX_PKT_STATUS_CRC_SHIFT 21 +#define RX_PKT_STATUS_CODE_MASK 0x1 +#define RX_PKT_STATUS_CODE_SHIFT 22 +#define RX_PKT_STATUS_RUNT_MASK 0x1 +#define RX_PKT_STATUS_RUNT_SHIFT 23 +#define RX_PKT_STATUS_FRAG_MASK 0x1 +#define RX_PKT_STATUS_FRAG_SHIFT 24 +#define RX_PKT_STATUS_TRUNK_MASK 0x1 +#define RX_PKT_STATUS_TRUNK_SHIFT 25 +#define RX_PKT_STATUS_ALIGN_MASK 0x1 +#define RX_PKT_STATUS_ALIGN_SHIFT 26 +#define RX_PKT_STATUS_VLAN_MASK 0x1 +#define RX_PKT_STATUS_VLAN_SHIFT 27 +#define RX_PKT_STATUS_UPDATE_MASK 0x1 +#define RX_PKT_STATUS_UPDATE_SHIFT 31 +#define RX_PKT_STATUS_VLAN_TAG_MASK 0xFFFF +#define RX_PKT_STATUS_VLAN_TAG_SHIFT 32 + +struct rx_desc { + struct rx_pkt_status status; + unsigned char packet[1536-sizeof(struct rx_pkt_status)]; +}; + +enum atl2_speed_duplex { + atl2_10_half = 0, + atl2_10_full = 1, + atl2_100_half = 2, + atl2_100_full = 3 +}; + +struct atl2_spi_flash_dev { + const char *manu_name; /* manufacturer id */ + /* op-code */ + u8 cmdWRSR; + u8 cmdREAD; + u8 cmdPROGRAM; + u8 cmdWREN; + u8 cmdWRDI; + u8 cmdRDSR; + u8 cmdRDID; + u8 cmdSECTOR_ERASE; + u8 cmdCHIP_ERASE; +}; + +/* Structure containing variables used by the shared code (atl2_hw.c) */ +struct atl2_hw { + u8 __iomem *hw_addr; + void *back; + + u8 preamble_len; + u8 max_retry; /* Retransmission maximum, afterwards the + * packet will be discarded. */ + u8 jam_ipg; /* IPG to start JAM for collision based flow + * control in half-duplex mode. In unit of + * 8-bit time. */ + u8 ipgt; /* Desired back to back inter-packet gap. The + * default is 96-bit time. */ + u8 min_ifg; /* Minimum number of IFG to enforce in between + * RX frames. Frame gap below such IFP is + * dropped. */ + u8 ipgr1; /* 64bit Carrier-Sense window */ + u8 ipgr2; /* 96-bit IPG window */ + u8 retry_buf; /* When half-duplex mode, should hold some + * bytes for mac retry . (8*4bytes unit) */ + + u16 fc_rxd_hi; + u16 fc_rxd_lo; + u16 lcol; /* Collision Window */ + u16 max_frame_size; + + u16 MediaType; + u16 autoneg_advertised; + u16 pci_cmd_word; + + u16 mii_autoneg_adv_reg; + + u32 mem_rang; + u32 txcw; + u32 mc_filter_type; + u32 num_mc_addrs; + u32 collision_delta; + u32 tx_packet_delta; + u16 phy_spd_default; + + u16 device_id; + u16 vendor_id; + u16 subsystem_id; + u16 subsystem_vendor_id; + u8 revision_id; + + /* spi flash */ + u8 flash_vendor; + + u8 dma_fairness; + u8 mac_addr[NODE_ADDRESS_SIZE]; + u8 perm_mac_addr[NODE_ADDRESS_SIZE]; + + /* FIXME */ + /* bool phy_preamble_sup; */ + bool phy_configured; +}; + +#endif /* _ATL2_HW_H_ */ + +struct atl2_ring_header { + /* pointer to the descriptor ring memory */ + void *desc; + /* physical adress of the descriptor ring */ + dma_addr_t dma; + /* length of descriptor ring in bytes */ + unsigned int size; +}; + +/* board specific private data structure */ +struct atl2_adapter { + /* OS defined structs */ + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; +#ifdef NETIF_F_HW_VLAN_TX + struct vlan_group *vlgrp; +#endif + u32 wol; + u16 link_speed; + u16 link_duplex; + + spinlock_t stats_lock; + spinlock_t tx_lock; + + struct work_struct reset_task; + struct work_struct link_chg_task; + struct timer_list watchdog_timer; + struct timer_list phy_config_timer; + + unsigned long cfg_phy; + bool mac_disabled; + + /* All Descriptor memory */ + dma_addr_t ring_dma; + void *ring_vir_addr; + int ring_size; + + struct tx_pkt_header *txd_ring; + dma_addr_t txd_dma; + + struct tx_pkt_status *txs_ring; + dma_addr_t txs_dma; + + struct rx_desc *rxd_ring; + dma_addr_t rxd_dma; + + u32 txd_ring_size; /* bytes per unit */ + u32 txs_ring_size; /* dwords per unit */ + u32 rxd_ring_size; /* 1536 bytes per unit */ + + /* read /write ptr: */ + /* host */ + u32 txd_write_ptr; + u32 txs_next_clear; + u32 rxd_read_ptr; + + /* nic */ + atomic_t txd_read_ptr; + atomic_t txs_write_ptr; + u32 rxd_write_ptr; + + /* Interrupt Moderator timer ( 2us resolution) */ + u16 imt; + /* Interrupt Clear timer (2us resolution) */ + u16 ict; + + unsigned long flags; + /* structs defined in atl2_hw.h */ + u32 bd_number; /* board number */ + bool pci_using_64; + bool have_msi; + struct atl2_hw hw; + + u32 usr_cmd; + /* FIXME */ + /* u32 regs_buff[ATL2_REGS_LEN]; */ + u32 pci_state[16]; + + u32 *config_space; +}; + +enum atl2_state_t { + __ATL2_TESTING, + __ATL2_RESETTING, + __ATL2_DOWN +}; + +#endif /* _ATL2_H_ */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index f1624b396754..90a132ab84a6 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2213,6 +2213,7 @@ #define PCI_VENDOR_ID_ATTANSIC 0x1969 #define PCI_DEVICE_ID_ATTANSIC_L1 0x1048 +#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 #define PCI_VENDOR_ID_JMICRON 0x197B #define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 -- cgit v1.2.3 From 01f2e4ead2c51226ed1283ef6a8388ca6f4cff8f Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 15 Sep 2008 09:17:11 -0700 Subject: enic: add Cisco 10G Ethernet NIC driver Signed-off-by: Scott Feldman Signed-off-by: Jeff Garzik --- MAINTAINERS | 7 + drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/enic/Makefile | 5 + drivers/net/enic/cq_desc.h | 79 ++ drivers/net/enic/cq_enet_desc.h | 169 ++++ drivers/net/enic/enic.h | 115 +++ drivers/net/enic/enic_main.c | 1949 ++++++++++++++++++++++++++++++++++++++ drivers/net/enic/enic_res.c | 370 ++++++++ drivers/net/enic/enic_res.h | 151 +++ drivers/net/enic/rq_enet_desc.h | 60 ++ drivers/net/enic/vnic_cq.c | 89 ++ drivers/net/enic/vnic_cq.h | 113 +++ drivers/net/enic/vnic_dev.c | 674 +++++++++++++ drivers/net/enic/vnic_dev.h | 106 +++ drivers/net/enic/vnic_devcmd.h | 282 ++++++ drivers/net/enic/vnic_enet.h | 47 + drivers/net/enic/vnic_intr.c | 62 ++ drivers/net/enic/vnic_intr.h | 92 ++ drivers/net/enic/vnic_nic.h | 65 ++ drivers/net/enic/vnic_resource.h | 63 ++ drivers/net/enic/vnic_rq.c | 199 ++++ drivers/net/enic/vnic_rq.h | 204 ++++ drivers/net/enic/vnic_rss.h | 32 + drivers/net/enic/vnic_stats.h | 70 ++ drivers/net/enic/vnic_wq.c | 184 ++++ drivers/net/enic/vnic_wq.h | 154 +++ drivers/net/enic/wq_enet_desc.h | 98 ++ include/linux/pci_ids.h | 2 + 29 files changed, 5449 insertions(+) create mode 100644 drivers/net/enic/Makefile create mode 100644 drivers/net/enic/cq_desc.h create mode 100644 drivers/net/enic/cq_enet_desc.h create mode 100644 drivers/net/enic/enic.h create mode 100644 drivers/net/enic/enic_main.c create mode 100644 drivers/net/enic/enic_res.c create mode 100644 drivers/net/enic/enic_res.h create mode 100644 drivers/net/enic/rq_enet_desc.h create mode 100644 drivers/net/enic/vnic_cq.c create mode 100644 drivers/net/enic/vnic_cq.h create mode 100644 drivers/net/enic/vnic_dev.c create mode 100644 drivers/net/enic/vnic_dev.h create mode 100644 drivers/net/enic/vnic_devcmd.h create mode 100644 drivers/net/enic/vnic_enet.h create mode 100644 drivers/net/enic/vnic_intr.c create mode 100644 drivers/net/enic/vnic_intr.h create mode 100644 drivers/net/enic/vnic_nic.h create mode 100644 drivers/net/enic/vnic_resource.h create mode 100644 drivers/net/enic/vnic_rq.c create mode 100644 drivers/net/enic/vnic_rq.h create mode 100644 drivers/net/enic/vnic_rss.h create mode 100644 drivers/net/enic/vnic_stats.h create mode 100644 drivers/net/enic/vnic_wq.c create mode 100644 drivers/net/enic/vnic_wq.h create mode 100644 drivers/net/enic/wq_enet_desc.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index b3e92fbe336c..467f994b1fa0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1046,6 +1046,13 @@ L: cbe-oss-dev@ozlabs.org W: http://www.ibm.com/developerworks/power/cell/ S: Supported +CISCO 10G ETHERNET DRIVER +P: Scott Feldman +M: scofeldm@cisco.com +P: Joe Eykholt +M: jeykholt@cisco.com +S: Supported + CFAG12864B LCD DRIVER P: Miguel Ojeda Sandonis M: miguel.ojeda.sandonis@gmail.com diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 81a3e959c6c3..5c012cd8fe3d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2388,6 +2388,13 @@ config EHEA To compile the driver as a module, choose M here. The module will be called ehea. +config ENIC + tristate "E, the Cisco 10G Ethernet NIC" + depends on PCI && INET + select INET_LRO + help + This enables the support for the Cisco 10G Ethernet card. + config IXGBE tristate "Intel(R) 10GbE PCI Express adapters support" depends on PCI && INET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 9221346a515e..d4ec6ba7f073 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ATL2) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_TEHUTI) += tehuti.o +obj-$(CONFIG_ENIC) += enic/ gianfar_driver-objs := gianfar.o \ gianfar_ethtool.o \ diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile new file mode 100644 index 000000000000..391c3bce5b79 --- /dev/null +++ b/drivers/net/enic/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_ENIC) := enic.o + +enic-y := enic_main.o vnic_cq.o vnic_intr.o vnic_wq.o \ + enic_res.o vnic_dev.o vnic_rq.o + diff --git a/drivers/net/enic/cq_desc.h b/drivers/net/enic/cq_desc.h new file mode 100644 index 000000000000..c036a8bfd043 --- /dev/null +++ b/drivers/net/enic/cq_desc.h @@ -0,0 +1,79 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _CQ_DESC_H_ +#define _CQ_DESC_H_ + +/* + * Completion queue descriptor types + */ +enum cq_desc_types { + CQ_DESC_TYPE_WQ_ENET = 0, + CQ_DESC_TYPE_DESC_COPY = 1, + CQ_DESC_TYPE_WQ_EXCH = 2, + CQ_DESC_TYPE_RQ_ENET = 3, + CQ_DESC_TYPE_RQ_FCP = 4, +}; + +/* Completion queue descriptor: 16B + * + * All completion queues have this basic layout. The + * type_specfic area is unique for each completion + * queue type. + */ +struct cq_desc { + __le16 completed_index; + __le16 q_number; + u8 type_specfic[11]; + u8 type_color; +}; + +#define CQ_DESC_TYPE_BITS 7 +#define CQ_DESC_TYPE_MASK ((1 << CQ_DESC_TYPE_BITS) - 1) +#define CQ_DESC_COLOR_MASK 1 +#define CQ_DESC_Q_NUM_BITS 10 +#define CQ_DESC_Q_NUM_MASK ((1 << CQ_DESC_Q_NUM_BITS) - 1) +#define CQ_DESC_COMP_NDX_BITS 12 +#define CQ_DESC_COMP_NDX_MASK ((1 << CQ_DESC_COMP_NDX_BITS) - 1) + +static inline void cq_desc_dec(const struct cq_desc *desc_arg, + u8 *type, u8 *color, u16 *q_number, u16 *completed_index) +{ + const struct cq_desc *desc = desc_arg; + const u8 type_color = desc->type_color; + + *color = (type_color >> CQ_DESC_TYPE_BITS) & CQ_DESC_COLOR_MASK; + + /* + * Make sure color bit is read from desc *before* other fields + * are read from desc. Hardware guarantees color bit is last + * bit (byte) written. Adding the rmb() prevents the compiler + * and/or CPU from reordering the reads which would potentially + * result in reading stale values. + */ + + rmb(); + + *type = type_color & CQ_DESC_TYPE_MASK; + *q_number = le16_to_cpu(desc->q_number) & CQ_DESC_Q_NUM_MASK; + *completed_index = le16_to_cpu(desc->completed_index) & + CQ_DESC_COMP_NDX_MASK; +} + +#endif /* _CQ_DESC_H_ */ diff --git a/drivers/net/enic/cq_enet_desc.h b/drivers/net/enic/cq_enet_desc.h new file mode 100644 index 000000000000..03dce9ed612c --- /dev/null +++ b/drivers/net/enic/cq_enet_desc.h @@ -0,0 +1,169 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _CQ_ENET_DESC_H_ +#define _CQ_ENET_DESC_H_ + +#include "cq_desc.h" + +/* Ethernet completion queue descriptor: 16B */ +struct cq_enet_wq_desc { + __le16 completed_index; + __le16 q_number; + u8 reserved[11]; + u8 type_color; +}; + +static inline void cq_enet_wq_desc_dec(struct cq_enet_wq_desc *desc, + u8 *type, u8 *color, u16 *q_number, u16 *completed_index) +{ + cq_desc_dec((struct cq_desc *)desc, type, + color, q_number, completed_index); +} + +/* Completion queue descriptor: Ethernet receive queue, 16B */ +struct cq_enet_rq_desc { + __le16 completed_index_flags; + __le16 q_number_rss_type_flags; + __le32 rss_hash; + __le16 bytes_written_flags; + __le16 vlan; + __le16 checksum_fcoe; + u8 flags; + u8 type_color; +}; + +#define CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT (0x1 << 12) +#define CQ_ENET_RQ_DESC_FLAGS_FCOE (0x1 << 13) +#define CQ_ENET_RQ_DESC_FLAGS_EOP (0x1 << 14) +#define CQ_ENET_RQ_DESC_FLAGS_SOP (0x1 << 15) + +#define CQ_ENET_RQ_DESC_RSS_TYPE_BITS 4 +#define CQ_ENET_RQ_DESC_RSS_TYPE_MASK \ + ((1 << CQ_ENET_RQ_DESC_RSS_TYPE_BITS) - 1) +#define CQ_ENET_RQ_DESC_RSS_TYPE_NONE 0 +#define CQ_ENET_RQ_DESC_RSS_TYPE_IPv4 1 +#define CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv4 2 +#define CQ_ENET_RQ_DESC_RSS_TYPE_IPv6 3 +#define CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6 4 +#define CQ_ENET_RQ_DESC_RSS_TYPE_IPv6_EX 5 +#define CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6_EX 6 + +#define CQ_ENET_RQ_DESC_FLAGS_CSUM_NOT_CALC (0x1 << 14) + +#define CQ_ENET_RQ_DESC_BYTES_WRITTEN_BITS 14 +#define CQ_ENET_RQ_DESC_BYTES_WRITTEN_MASK \ + ((1 << CQ_ENET_RQ_DESC_BYTES_WRITTEN_BITS) - 1) +#define CQ_ENET_RQ_DESC_FLAGS_TRUNCATED (0x1 << 14) +#define CQ_ENET_RQ_DESC_FLAGS_VLAN_STRIPPED (0x1 << 15) + +#define CQ_ENET_RQ_DESC_FCOE_SOF_BITS 4 +#define CQ_ENET_RQ_DESC_FCOE_SOF_MASK \ + ((1 << CQ_ENET_RQ_DESC_FCOE_SOF_BITS) - 1) +#define CQ_ENET_RQ_DESC_FCOE_EOF_BITS 8 +#define CQ_ENET_RQ_DESC_FCOE_EOF_MASK \ + ((1 << CQ_ENET_RQ_DESC_FCOE_EOF_BITS) - 1) +#define CQ_ENET_RQ_DESC_FCOE_EOF_SHIFT 8 + +#define CQ_ENET_RQ_DESC_FLAGS_TCP_UDP_CSUM_OK (0x1 << 0) +#define CQ_ENET_RQ_DESC_FCOE_FC_CRC_OK (0x1 << 0) +#define CQ_ENET_RQ_DESC_FLAGS_UDP (0x1 << 1) +#define CQ_ENET_RQ_DESC_FCOE_ENC_ERROR (0x1 << 1) +#define CQ_ENET_RQ_DESC_FLAGS_TCP (0x1 << 2) +#define CQ_ENET_RQ_DESC_FLAGS_IPV4_CSUM_OK (0x1 << 3) +#define CQ_ENET_RQ_DESC_FLAGS_IPV6 (0x1 << 4) +#define CQ_ENET_RQ_DESC_FLAGS_IPV4 (0x1 << 5) +#define CQ_ENET_RQ_DESC_FLAGS_IPV4_FRAGMENT (0x1 << 6) +#define CQ_ENET_RQ_DESC_FLAGS_FCS_OK (0x1 << 7) + +static inline void cq_enet_rq_desc_dec(struct cq_enet_rq_desc *desc, + u8 *type, u8 *color, u16 *q_number, u16 *completed_index, + u8 *ingress_port, u8 *fcoe, u8 *eop, u8 *sop, u8 *rss_type, + u8 *csum_not_calc, u32 *rss_hash, u16 *bytes_written, u8 *packet_error, + u8 *vlan_stripped, u16 *vlan, u16 *checksum, u8 *fcoe_sof, + u8 *fcoe_fc_crc_ok, u8 *fcoe_enc_error, u8 *fcoe_eof, + u8 *tcp_udp_csum_ok, u8 *udp, u8 *tcp, u8 *ipv4_csum_ok, + u8 *ipv6, u8 *ipv4, u8 *ipv4_fragment, u8 *fcs_ok) +{ + u16 completed_index_flags = le16_to_cpu(desc->completed_index_flags); + u16 q_number_rss_type_flags = + le16_to_cpu(desc->q_number_rss_type_flags); + u16 bytes_written_flags = le16_to_cpu(desc->bytes_written_flags); + + cq_desc_dec((struct cq_desc *)desc, type, + color, q_number, completed_index); + + *ingress_port = (completed_index_flags & + CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT) ? 1 : 0; + *fcoe = (completed_index_flags & CQ_ENET_RQ_DESC_FLAGS_FCOE) ? + 1 : 0; + *eop = (completed_index_flags & CQ_ENET_RQ_DESC_FLAGS_EOP) ? + 1 : 0; + *sop = (completed_index_flags & CQ_ENET_RQ_DESC_FLAGS_SOP) ? + 1 : 0; + + *rss_type = (u8)((q_number_rss_type_flags >> CQ_DESC_Q_NUM_BITS) & + CQ_ENET_RQ_DESC_RSS_TYPE_MASK); + *csum_not_calc = (q_number_rss_type_flags & + CQ_ENET_RQ_DESC_FLAGS_CSUM_NOT_CALC) ? 1 : 0; + + *rss_hash = le32_to_cpu(desc->rss_hash); + + *bytes_written = bytes_written_flags & + CQ_ENET_RQ_DESC_BYTES_WRITTEN_MASK; + *packet_error = (bytes_written_flags & + CQ_ENET_RQ_DESC_FLAGS_TRUNCATED) ? 1 : 0; + *vlan_stripped = (bytes_written_flags & + CQ_ENET_RQ_DESC_FLAGS_VLAN_STRIPPED) ? 1 : 0; + + *vlan = le16_to_cpu(desc->vlan); + + if (*fcoe) { + *fcoe_sof = (u8)(le16_to_cpu(desc->checksum_fcoe) & + CQ_ENET_RQ_DESC_FCOE_SOF_MASK); + *fcoe_fc_crc_ok = (desc->flags & + CQ_ENET_RQ_DESC_FCOE_FC_CRC_OK) ? 1 : 0; + *fcoe_enc_error = (desc->flags & + CQ_ENET_RQ_DESC_FCOE_ENC_ERROR) ? 1 : 0; + *fcoe_eof = (u8)((desc->checksum_fcoe >> + CQ_ENET_RQ_DESC_FCOE_EOF_SHIFT) & + CQ_ENET_RQ_DESC_FCOE_EOF_MASK); + *checksum = 0; + } else { + *fcoe_sof = 0; + *fcoe_fc_crc_ok = 0; + *fcoe_enc_error = 0; + *fcoe_eof = 0; + *checksum = le16_to_cpu(desc->checksum_fcoe); + } + + *tcp_udp_csum_ok = + (desc->flags & CQ_ENET_RQ_DESC_FLAGS_TCP_UDP_CSUM_OK) ? 1 : 0; + *udp = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_UDP) ? 1 : 0; + *tcp = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_TCP) ? 1 : 0; + *ipv4_csum_ok = + (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV4_CSUM_OK) ? 1 : 0; + *ipv6 = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV6) ? 1 : 0; + *ipv4 = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV4) ? 1 : 0; + *ipv4_fragment = + (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV4_FRAGMENT) ? 1 : 0; + *fcs_ok = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_FCS_OK) ? 1 : 0; +} + +#endif /* _CQ_ENET_DESC_H_ */ diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h new file mode 100644 index 000000000000..fb83c926da58 --- /dev/null +++ b/drivers/net/enic/enic.h @@ -0,0 +1,115 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _ENIC_H_ +#define _ENIC_H_ + +#include + +#include "vnic_enet.h" +#include "vnic_dev.h" +#include "vnic_wq.h" +#include "vnic_rq.h" +#include "vnic_cq.h" +#include "vnic_intr.h" +#include "vnic_stats.h" +#include "vnic_rss.h" + +#define DRV_NAME "enic" +#define DRV_DESCRIPTION "Cisco 10G Ethernet Driver" +#define DRV_VERSION "0.0.1.18163.472" +#define DRV_COPYRIGHT "Copyright 2008 Cisco Systems, Inc" +#define PFX DRV_NAME ": " + +#define ENIC_LRO_MAX_DESC 8 +#define ENIC_LRO_MAX_AGGR 64 + +enum enic_cq_index { + ENIC_CQ_RQ, + ENIC_CQ_WQ, + ENIC_CQ_MAX, +}; + +enum enic_intx_intr_index { + ENIC_INTX_WQ_RQ, + ENIC_INTX_ERR, + ENIC_INTX_NOTIFY, + ENIC_INTX_MAX, +}; + +enum enic_msix_intr_index { + ENIC_MSIX_RQ, + ENIC_MSIX_WQ, + ENIC_MSIX_ERR, + ENIC_MSIX_NOTIFY, + ENIC_MSIX_MAX, +}; + +struct enic_msix_entry { + int requested; + char devname[IFNAMSIZ]; + irqreturn_t (*isr)(int, void *); + void *devid; +}; + +/* Per-instance private data structure */ +struct enic { + struct net_device *netdev; + struct pci_dev *pdev; + struct vnic_enet_config config; + struct vnic_dev_bar bar0; + struct vnic_dev *vdev; + struct net_device_stats net_stats; + struct timer_list notify_timer; + struct work_struct reset; + struct msix_entry msix_entry[ENIC_MSIX_MAX]; + struct enic_msix_entry msix[ENIC_MSIX_MAX]; + u32 msg_enable; + spinlock_t devcmd_lock; + u8 mac_addr[ETH_ALEN]; + u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN]; + unsigned int mc_count; + int csum_rx_enabled; + u32 port_mtu; + + /* work queue cache line section */ + ____cacheline_aligned struct vnic_wq wq[1]; + spinlock_t wq_lock[1]; + unsigned int wq_count; + struct vlan_group *vlan_group; + + /* receive queue cache line section */ + ____cacheline_aligned struct vnic_rq rq[1]; + unsigned int rq_count; + int (*rq_alloc_buf)(struct vnic_rq *rq); + struct napi_struct napi; + struct net_lro_mgr lro_mgr; + struct net_lro_desc lro_desc[ENIC_LRO_MAX_DESC]; + + /* interrupt resource cache line section */ + ____cacheline_aligned struct vnic_intr intr[ENIC_MSIX_MAX]; + unsigned int intr_count; + u32 __iomem *legacy_pba; /* memory-mapped */ + + /* completion queue cache line section */ + ____cacheline_aligned struct vnic_cq cq[ENIC_CQ_MAX]; + unsigned int cq_count; +}; + +#endif /* _ENIC_H_ */ diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c new file mode 100644 index 000000000000..4cf5ec76c993 --- /dev/null +++ b/drivers/net/enic/enic_main.c @@ -0,0 +1,1949 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cq_enet_desc.h" +#include "vnic_dev.h" +#include "vnic_intr.h" +#include "vnic_stats.h" +#include "enic_res.h" +#include "enic.h" + +#define ENIC_NOTIFY_TIMER_PERIOD (2 * HZ) +#define ENIC_JUMBO_FIRST_BUF_SIZE 256 + +/* Supported devices */ +static struct pci_device_id enic_id_table[] = { + { PCI_VDEVICE(CISCO, 0x0043) }, + { 0, } /* end of table */ +}; + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR("Scott Feldman "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, enic_id_table); + +struct enic_stat { + char name[ETH_GSTRING_LEN]; + unsigned int offset; +}; + +#define ENIC_TX_STAT(stat) \ + { .name = #stat, .offset = offsetof(struct vnic_tx_stats, stat) / 8 } +#define ENIC_RX_STAT(stat) \ + { .name = #stat, .offset = offsetof(struct vnic_rx_stats, stat) / 8 } + +static const struct enic_stat enic_tx_stats[] = { + ENIC_TX_STAT(tx_frames_ok), + ENIC_TX_STAT(tx_unicast_frames_ok), + ENIC_TX_STAT(tx_multicast_frames_ok), + ENIC_TX_STAT(tx_broadcast_frames_ok), + ENIC_TX_STAT(tx_bytes_ok), + ENIC_TX_STAT(tx_unicast_bytes_ok), + ENIC_TX_STAT(tx_multicast_bytes_ok), + ENIC_TX_STAT(tx_broadcast_bytes_ok), + ENIC_TX_STAT(tx_drops), + ENIC_TX_STAT(tx_errors), + ENIC_TX_STAT(tx_tso), +}; + +static const struct enic_stat enic_rx_stats[] = { + ENIC_RX_STAT(rx_frames_ok), + ENIC_RX_STAT(rx_frames_total), + ENIC_RX_STAT(rx_unicast_frames_ok), + ENIC_RX_STAT(rx_multicast_frames_ok), + ENIC_RX_STAT(rx_broadcast_frames_ok), + ENIC_RX_STAT(rx_bytes_ok), + ENIC_RX_STAT(rx_unicast_bytes_ok), + ENIC_RX_STAT(rx_multicast_bytes_ok), + ENIC_RX_STAT(rx_broadcast_bytes_ok), + ENIC_RX_STAT(rx_drop), + ENIC_RX_STAT(rx_no_bufs), + ENIC_RX_STAT(rx_errors), + ENIC_RX_STAT(rx_rss), + ENIC_RX_STAT(rx_crc_errors), + ENIC_RX_STAT(rx_frames_64), + ENIC_RX_STAT(rx_frames_127), + ENIC_RX_STAT(rx_frames_255), + ENIC_RX_STAT(rx_frames_511), + ENIC_RX_STAT(rx_frames_1023), + ENIC_RX_STAT(rx_frames_1518), + ENIC_RX_STAT(rx_frames_to_max), +}; + +static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); +static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); + +static int enic_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct enic *enic = netdev_priv(netdev); + + ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); + ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); + ecmd->port = PORT_FIBRE; + ecmd->transceiver = XCVR_EXTERNAL; + + if (netif_carrier_ok(netdev)) { + ecmd->speed = vnic_dev_port_speed(enic->vdev); + ecmd->duplex = DUPLEX_FULL; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = AUTONEG_DISABLE; + + return 0; +} + +static void enic_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct enic *enic = netdev_priv(netdev); + struct vnic_devcmd_fw_info *fw_info; + + spin_lock(&enic->devcmd_lock); + vnic_dev_fw_info(enic->vdev, &fw_info); + spin_unlock(&enic->devcmd_lock); + + strncpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strncpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); + strncpy(drvinfo->fw_version, fw_info->fw_version, + sizeof(drvinfo->fw_version)); + strncpy(drvinfo->bus_info, pci_name(enic->pdev), + sizeof(drvinfo->bus_info)); +} + +static void enic_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + unsigned int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < enic_n_tx_stats; i++) { + memcpy(data, enic_tx_stats[i].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + for (i = 0; i < enic_n_rx_stats; i++) { + memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + break; + } +} + +static int enic_get_stats_count(struct net_device *netdev) +{ + return enic_n_tx_stats + enic_n_rx_stats; +} + +static void enic_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct enic *enic = netdev_priv(netdev); + struct vnic_stats *vstats; + unsigned int i; + + spin_lock(&enic->devcmd_lock); + vnic_dev_stats_dump(enic->vdev, &vstats); + spin_unlock(&enic->devcmd_lock); + + for (i = 0; i < enic_n_tx_stats; i++) + *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].offset]; + for (i = 0; i < enic_n_rx_stats; i++) + *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].offset]; +} + +static u32 enic_get_rx_csum(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + return enic->csum_rx_enabled; +} + +static int enic_set_rx_csum(struct net_device *netdev, u32 data) +{ + struct enic *enic = netdev_priv(netdev); + + enic->csum_rx_enabled = + (data && ENIC_SETTING(enic, RXCSUM)) ? 1 : 0; + + return 0; +} + +static int enic_set_tx_csum(struct net_device *netdev, u32 data) +{ + struct enic *enic = netdev_priv(netdev); + + if (data && ENIC_SETTING(enic, TXCSUM)) + netdev->features |= NETIF_F_HW_CSUM; + else + netdev->features &= ~NETIF_F_HW_CSUM; + + return 0; +} + +static int enic_set_tso(struct net_device *netdev, u32 data) +{ + struct enic *enic = netdev_priv(netdev); + + if (data && ENIC_SETTING(enic, TSO)) + netdev->features |= + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN; + else + netdev->features &= + ~(NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN); + + return 0; +} + +static u32 enic_get_msglevel(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + return enic->msg_enable; +} + +static void enic_set_msglevel(struct net_device *netdev, u32 value) +{ + struct enic *enic = netdev_priv(netdev); + enic->msg_enable = value; +} + +static struct ethtool_ops enic_ethtool_ops = { + .get_settings = enic_get_settings, + .get_drvinfo = enic_get_drvinfo, + .get_msglevel = enic_get_msglevel, + .set_msglevel = enic_set_msglevel, + .get_link = ethtool_op_get_link, + .get_strings = enic_get_strings, + .get_stats_count = enic_get_stats_count, + .get_ethtool_stats = enic_get_ethtool_stats, + .get_rx_csum = enic_get_rx_csum, + .set_rx_csum = enic_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = enic_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_tso = ethtool_op_get_tso, + .set_tso = enic_set_tso, +}; + +static void enic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) +{ + struct enic *enic = vnic_dev_priv(wq->vdev); + + if (buf->sop) + pci_unmap_single(enic->pdev, buf->dma_addr, + buf->len, PCI_DMA_TODEVICE); + else + pci_unmap_page(enic->pdev, buf->dma_addr, + buf->len, PCI_DMA_TODEVICE); + + if (buf->os_buf) + dev_kfree_skb_any(buf->os_buf); +} + +static void enic_wq_free_buf(struct vnic_wq *wq, + struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque) +{ + enic_free_wq_buf(wq, buf); +} + +static int enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, + u8 type, u16 q_number, u16 completed_index, void *opaque) +{ + struct enic *enic = vnic_dev_priv(vdev); + + spin_lock(&enic->wq_lock[q_number]); + + vnic_wq_service(&enic->wq[q_number], cq_desc, + completed_index, enic_wq_free_buf, + opaque); + + if (netif_queue_stopped(enic->netdev) && + vnic_wq_desc_avail(&enic->wq[q_number]) >= MAX_SKB_FRAGS + 1) + netif_wake_queue(enic->netdev); + + spin_unlock(&enic->wq_lock[q_number]); + + return 0; +} + +static void enic_log_q_error(struct enic *enic) +{ + unsigned int i; + u32 error_status; + + for (i = 0; i < enic->wq_count; i++) { + error_status = vnic_wq_error_status(&enic->wq[i]); + if (error_status) + printk(KERN_ERR PFX "%s: WQ[%d] error_status %d\n", + enic->netdev->name, i, error_status); + } + + for (i = 0; i < enic->rq_count; i++) { + error_status = vnic_rq_error_status(&enic->rq[i]); + if (error_status) + printk(KERN_ERR PFX "%s: RQ[%d] error_status %d\n", + enic->netdev->name, i, error_status); + } +} + +static void enic_link_check(struct enic *enic) +{ + int link_status = vnic_dev_link_status(enic->vdev); + int carrier_ok = netif_carrier_ok(enic->netdev); + + if (link_status && !carrier_ok) { + printk(KERN_INFO PFX "%s: Link UP\n", enic->netdev->name); + netif_carrier_on(enic->netdev); + } else if (!link_status && carrier_ok) { + printk(KERN_INFO PFX "%s: Link DOWN\n", enic->netdev->name); + netif_carrier_off(enic->netdev); + } +} + +static void enic_mtu_check(struct enic *enic) +{ + u32 mtu = vnic_dev_mtu(enic->vdev); + + if (mtu != enic->port_mtu) { + if (mtu < enic->netdev->mtu) + printk(KERN_WARNING PFX + "%s: interface MTU (%d) set higher " + "than switch port MTU (%d)\n", + enic->netdev->name, enic->netdev->mtu, mtu); + enic->port_mtu = mtu; + } +} + +static void enic_msglvl_check(struct enic *enic) +{ + u32 msg_enable = vnic_dev_msg_lvl(enic->vdev); + + if (msg_enable != enic->msg_enable) { + printk(KERN_INFO PFX "%s: msg lvl changed from 0x%x to 0x%x\n", + enic->netdev->name, enic->msg_enable, msg_enable); + enic->msg_enable = msg_enable; + } +} + +static void enic_notify_check(struct enic *enic) +{ + enic_msglvl_check(enic); + enic_mtu_check(enic); + enic_link_check(enic); +} + +#define ENIC_TEST_INTR(pba, i) (pba & (1 << i)) + +static irqreturn_t enic_isr_legacy(int irq, void *data) +{ + struct net_device *netdev = data; + struct enic *enic = netdev_priv(netdev); + u32 pba; + + vnic_intr_mask(&enic->intr[ENIC_INTX_WQ_RQ]); + + pba = vnic_intr_legacy_pba(enic->legacy_pba); + if (!pba) { + vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]); + return IRQ_NONE; /* not our interrupt */ + } + + if (ENIC_TEST_INTR(pba, ENIC_INTX_NOTIFY)) + enic_notify_check(enic); + + if (ENIC_TEST_INTR(pba, ENIC_INTX_ERR)) { + enic_log_q_error(enic); + /* schedule recovery from WQ/RQ error */ + schedule_work(&enic->reset); + return IRQ_HANDLED; + } + + if (ENIC_TEST_INTR(pba, ENIC_INTX_WQ_RQ)) { + if (netif_rx_schedule_prep(netdev, &enic->napi)) + __netif_rx_schedule(netdev, &enic->napi); + } else { + vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]); + } + + return IRQ_HANDLED; +} + +static irqreturn_t enic_isr_msi(int irq, void *data) +{ + struct enic *enic = data; + + /* With MSI, there is no sharing of interrupts, so this is + * our interrupt and there is no need to ack it. The device + * is not providing per-vector masking, so the OS will not + * write to PCI config space to mask/unmask the interrupt. + * We're using mask_on_assertion for MSI, so the device + * automatically masks the interrupt when the interrupt is + * generated. Later, when exiting polling, the interrupt + * will be unmasked (see enic_poll). + * + * Also, the device uses the same PCIe Traffic Class (TC) + * for Memory Write data and MSI, so there are no ordering + * issues; the MSI will always arrive at the Root Complex + * _after_ corresponding Memory Writes (i.e. descriptor + * writes). + */ + + netif_rx_schedule(enic->netdev, &enic->napi); + + return IRQ_HANDLED; +} + +static irqreturn_t enic_isr_msix_rq(int irq, void *data) +{ + struct enic *enic = data; + + /* schedule NAPI polling for RQ cleanup */ + netif_rx_schedule(enic->netdev, &enic->napi); + + return IRQ_HANDLED; +} + +static irqreturn_t enic_isr_msix_wq(int irq, void *data) +{ + struct enic *enic = data; + unsigned int wq_work_to_do = -1; /* no limit */ + unsigned int wq_work_done; + + wq_work_done = vnic_cq_service(&enic->cq[ENIC_CQ_WQ], + wq_work_to_do, enic_wq_service, NULL); + + vnic_intr_return_credits(&enic->intr[ENIC_MSIX_WQ], + wq_work_done, + 1 /* unmask intr */, + 1 /* reset intr timer */); + + return IRQ_HANDLED; +} + +static irqreturn_t enic_isr_msix_err(int irq, void *data) +{ + struct enic *enic = data; + + enic_log_q_error(enic); + + /* schedule recovery from WQ/RQ error */ + schedule_work(&enic->reset); + + return IRQ_HANDLED; +} + +static irqreturn_t enic_isr_msix_notify(int irq, void *data) +{ + struct enic *enic = data; + + enic_notify_check(enic); + vnic_intr_unmask(&enic->intr[ENIC_MSIX_NOTIFY]); + + return IRQ_HANDLED; +} + +static inline void enic_queue_wq_skb_cont(struct enic *enic, + struct vnic_wq *wq, struct sk_buff *skb, + unsigned int len_left) +{ + skb_frag_t *frag; + + /* Queue additional data fragments */ + for (frag = skb_shinfo(skb)->frags; len_left; frag++) { + len_left -= frag->size; + enic_queue_wq_desc_cont(wq, skb, + pci_map_page(enic->pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE), + frag->size, + (len_left == 0)); /* EOP? */ + } +} + +static inline void enic_queue_wq_skb_vlan(struct enic *enic, + struct vnic_wq *wq, struct sk_buff *skb, + int vlan_tag_insert, unsigned int vlan_tag) +{ + unsigned int head_len = skb_headlen(skb); + unsigned int len_left = skb->len - head_len; + int eop = (len_left == 0); + + /* Queue the main skb fragment */ + enic_queue_wq_desc(wq, skb, + pci_map_single(enic->pdev, skb->data, + head_len, PCI_DMA_TODEVICE), + head_len, + vlan_tag_insert, vlan_tag, + eop); + + if (!eop) + enic_queue_wq_skb_cont(enic, wq, skb, len_left); +} + +static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, + struct vnic_wq *wq, struct sk_buff *skb, + int vlan_tag_insert, unsigned int vlan_tag) +{ + unsigned int head_len = skb_headlen(skb); + unsigned int len_left = skb->len - head_len; + unsigned int hdr_len = skb_transport_offset(skb); + unsigned int csum_offset = hdr_len + skb->csum_offset; + int eop = (len_left == 0); + + /* Queue the main skb fragment */ + enic_queue_wq_desc_csum_l4(wq, skb, + pci_map_single(enic->pdev, skb->data, + head_len, PCI_DMA_TODEVICE), + head_len, + csum_offset, + hdr_len, + vlan_tag_insert, vlan_tag, + eop); + + if (!eop) + enic_queue_wq_skb_cont(enic, wq, skb, len_left); +} + +static inline void enic_queue_wq_skb_tso(struct enic *enic, + struct vnic_wq *wq, struct sk_buff *skb, unsigned int mss, + int vlan_tag_insert, unsigned int vlan_tag) +{ + unsigned int head_len = skb_headlen(skb); + unsigned int len_left = skb->len - head_len; + unsigned int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int eop = (len_left == 0); + + /* Preload TCP csum field with IP pseudo hdr calculated + * with IP length set to zero. HW will later add in length + * to each TCP segment resulting from the TSO. + */ + + if (skb->protocol == __constant_htons(ETH_P_IP)) { + ip_hdr(skb)->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); + } else if (skb->protocol == __constant_htons(ETH_P_IPV6)) { + tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); + } + + /* Queue the main skb fragment */ + enic_queue_wq_desc_tso(wq, skb, + pci_map_single(enic->pdev, skb->data, + head_len, PCI_DMA_TODEVICE), + head_len, + mss, hdr_len, + vlan_tag_insert, vlan_tag, + eop); + + if (!eop) + enic_queue_wq_skb_cont(enic, wq, skb, len_left); +} + +static inline void enic_queue_wq_skb(struct enic *enic, + struct vnic_wq *wq, struct sk_buff *skb) +{ + unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int vlan_tag = 0; + int vlan_tag_insert = 0; + + if (enic->vlan_group && vlan_tx_tag_present(skb)) { + /* VLAN tag from trunking driver */ + vlan_tag_insert = 1; + vlan_tag = vlan_tx_tag_get(skb); + } + + if (mss) + enic_queue_wq_skb_tso(enic, wq, skb, mss, + vlan_tag_insert, vlan_tag); + else if (skb->ip_summed == CHECKSUM_PARTIAL) + enic_queue_wq_skb_csum_l4(enic, wq, skb, + vlan_tag_insert, vlan_tag); + else + enic_queue_wq_skb_vlan(enic, wq, skb, + vlan_tag_insert, vlan_tag); +} + +/* netif_tx_lock held, process context with BHs disabled */ +static int enic_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + struct vnic_wq *wq = &enic->wq[0]; + unsigned long flags; + + if (skb->len <= 0) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + /* Non-TSO sends must fit within ENIC_NON_TSO_MAX_DESC descs, + * which is very likely. In the off chance it's going to take + * more than * ENIC_NON_TSO_MAX_DESC, linearize the skb. + */ + + if (skb_shinfo(skb)->gso_size == 0 && + skb_shinfo(skb)->nr_frags + 1 > ENIC_NON_TSO_MAX_DESC && + skb_linearize(skb)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&enic->wq_lock[0], flags); + + if (vnic_wq_desc_avail(wq) < skb_shinfo(skb)->nr_frags + 1) { + netif_stop_queue(netdev); + /* This is a hard error, log it */ + printk(KERN_ERR PFX "%s: BUG! Tx ring full when " + "queue awake!\n", netdev->name); + spin_unlock_irqrestore(&enic->wq_lock[0], flags); + return NETDEV_TX_BUSY; + } + + enic_queue_wq_skb(enic, wq, skb); + + if (vnic_wq_desc_avail(wq) < MAX_SKB_FRAGS + 1) + netif_stop_queue(netdev); + + netdev->trans_start = jiffies; + + spin_unlock_irqrestore(&enic->wq_lock[0], flags); + + return NETDEV_TX_OK; +} + +/* dev_base_lock rwlock held, nominally process context */ +static struct net_device_stats *enic_get_stats(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + struct vnic_stats *stats; + + spin_lock(&enic->devcmd_lock); + vnic_dev_stats_dump(enic->vdev, &stats); + spin_unlock(&enic->devcmd_lock); + + enic->net_stats.tx_packets = stats->tx.tx_frames_ok; + enic->net_stats.tx_bytes = stats->tx.tx_bytes_ok; + enic->net_stats.tx_errors = stats->tx.tx_errors; + enic->net_stats.tx_dropped = stats->tx.tx_drops; + + enic->net_stats.rx_packets = stats->rx.rx_frames_ok; + enic->net_stats.rx_bytes = stats->rx.rx_bytes_ok; + enic->net_stats.rx_errors = stats->rx.rx_errors; + enic->net_stats.multicast = stats->rx.rx_multicast_frames_ok; + enic->net_stats.rx_crc_errors = stats->rx.rx_crc_errors; + enic->net_stats.rx_dropped = stats->rx.rx_no_bufs; + + return &enic->net_stats; +} + +static void enic_reset_mcaddrs(struct enic *enic) +{ + enic->mc_count = 0; +} + +static int enic_set_mac_addr(struct net_device *netdev, char *addr) +{ + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + memcpy(netdev->dev_addr, addr, netdev->addr_len); + + return 0; +} + +/* netif_tx_lock held, BHs disabled */ +static void enic_set_multicast_list(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + struct dev_mc_list *list = netdev->mc_list; + int directed = 1; + int multicast = (netdev->flags & IFF_MULTICAST) ? 1 : 0; + int broadcast = (netdev->flags & IFF_BROADCAST) ? 1 : 0; + int promisc = (netdev->flags & IFF_PROMISC) ? 1 : 0; + int allmulti = (netdev->flags & IFF_ALLMULTI) || + (netdev->mc_count > ENIC_MULTICAST_PERFECT_FILTERS); + u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN]; + unsigned int mc_count = netdev->mc_count; + unsigned int i, j; + + if (mc_count > ENIC_MULTICAST_PERFECT_FILTERS) + mc_count = ENIC_MULTICAST_PERFECT_FILTERS; + + spin_lock(&enic->devcmd_lock); + + vnic_dev_packet_filter(enic->vdev, directed, + multicast, broadcast, promisc, allmulti); + + /* Is there an easier way? Trying to minimize to + * calls to add/del multicast addrs. We keep the + * addrs from the last call in enic->mc_addr and + * look for changes to add/del. + */ + + for (i = 0; list && i < mc_count; i++) { + memcpy(mc_addr[i], list->dmi_addr, ETH_ALEN); + list = list->next; + } + + for (i = 0; i < enic->mc_count; i++) { + for (j = 0; j < mc_count; j++) + if (compare_ether_addr(enic->mc_addr[i], + mc_addr[j]) == 0) + break; + if (j == mc_count) + enic_del_multicast_addr(enic, enic->mc_addr[i]); + } + + for (i = 0; i < mc_count; i++) { + for (j = 0; j < enic->mc_count; j++) + if (compare_ether_addr(mc_addr[i], + enic->mc_addr[j]) == 0) + break; + if (j == enic->mc_count) + enic_add_multicast_addr(enic, mc_addr[i]); + } + + /* Save the list to compare against next time + */ + + for (i = 0; i < mc_count; i++) + memcpy(enic->mc_addr[i], mc_addr[i], ETH_ALEN); + + enic->mc_count = mc_count; + + spin_unlock(&enic->devcmd_lock); +} + +/* rtnl lock is held */ +static void enic_vlan_rx_register(struct net_device *netdev, + struct vlan_group *vlan_group) +{ + struct enic *enic = netdev_priv(netdev); + enic->vlan_group = vlan_group; +} + +/* rtnl lock is held */ +static void enic_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +{ + struct enic *enic = netdev_priv(netdev); + + spin_lock(&enic->devcmd_lock); + enic_add_vlan(enic, vid); + spin_unlock(&enic->devcmd_lock); +} + +/* rtnl lock is held */ +static void enic_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +{ + struct enic *enic = netdev_priv(netdev); + + spin_lock(&enic->devcmd_lock); + enic_del_vlan(enic, vid); + spin_unlock(&enic->devcmd_lock); +} + +/* netif_tx_lock held, BHs disabled */ +static void enic_tx_timeout(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + schedule_work(&enic->reset); +} + +static void enic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) +{ + struct enic *enic = vnic_dev_priv(rq->vdev); + + if (!buf->os_buf) + return; + + pci_unmap_single(enic->pdev, buf->dma_addr, + buf->len, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(buf->os_buf); +} + +static inline struct sk_buff *enic_rq_alloc_skb(unsigned int size) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(size + NET_IP_ALIGN); + + if (skb) + skb_reserve(skb, NET_IP_ALIGN); + + return skb; +} + +static int enic_rq_alloc_buf(struct vnic_rq *rq) +{ + struct enic *enic = vnic_dev_priv(rq->vdev); + struct sk_buff *skb; + unsigned int len = enic->netdev->mtu + ETH_HLEN; + unsigned int os_buf_index = 0; + dma_addr_t dma_addr; + + skb = enic_rq_alloc_skb(len); + if (!skb) + return -ENOMEM; + + dma_addr = pci_map_single(enic->pdev, skb->data, + len, PCI_DMA_FROMDEVICE); + + enic_queue_rq_desc(rq, skb, os_buf_index, + dma_addr, len); + + return 0; +} + +static int enic_get_skb_header(struct sk_buff *skb, void **iphdr, + void **tcph, u64 *hdr_flags, void *priv) +{ + struct cq_enet_rq_desc *cq_desc = priv; + unsigned int ip_len; + struct iphdr *iph; + + u8 type, color, eop, sop, ingress_port, vlan_stripped; + u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof; + u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; + u8 ipv6, ipv4, ipv4_fragment, fcs_ok, rss_type, csum_not_calc; + u8 packet_error; + u16 q_number, completed_index, bytes_written, vlan, checksum; + u32 rss_hash; + + cq_enet_rq_desc_dec(cq_desc, + &type, &color, &q_number, &completed_index, + &ingress_port, &fcoe, &eop, &sop, &rss_type, + &csum_not_calc, &rss_hash, &bytes_written, + &packet_error, &vlan_stripped, &vlan, &checksum, + &fcoe_sof, &fcoe_fc_crc_ok, &fcoe_enc_error, + &fcoe_eof, &tcp_udp_csum_ok, &udp, &tcp, + &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, + &fcs_ok); + + if (!(ipv4 && tcp && !ipv4_fragment)) + return -1; + + skb_reset_network_header(skb); + iph = ip_hdr(skb); + + ip_len = ip_hdrlen(skb); + skb_set_transport_header(skb, ip_len); + + /* check if ip header and tcp header are complete */ + if (ntohs(iph->tot_len) < ip_len + tcp_hdrlen(skb)) + return -1; + + *hdr_flags = LRO_IPV4 | LRO_TCP; + *tcph = tcp_hdr(skb); + *iphdr = iph; + + return 0; +} + +static void enic_rq_indicate_buf(struct vnic_rq *rq, + struct cq_desc *cq_desc, struct vnic_rq_buf *buf, + int skipped, void *opaque) +{ + struct enic *enic = vnic_dev_priv(rq->vdev); + struct sk_buff *skb; + + u8 type, color, eop, sop, ingress_port, vlan_stripped; + u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof; + u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; + u8 ipv6, ipv4, ipv4_fragment, fcs_ok, rss_type, csum_not_calc; + u8 packet_error; + u16 q_number, completed_index, bytes_written, vlan, checksum; + u32 rss_hash; + + if (skipped) + return; + + skb = buf->os_buf; + prefetch(skb->data - NET_IP_ALIGN); + pci_unmap_single(enic->pdev, buf->dma_addr, + buf->len, PCI_DMA_FROMDEVICE); + + cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc, + &type, &color, &q_number, &completed_index, + &ingress_port, &fcoe, &eop, &sop, &rss_type, + &csum_not_calc, &rss_hash, &bytes_written, + &packet_error, &vlan_stripped, &vlan, &checksum, + &fcoe_sof, &fcoe_fc_crc_ok, &fcoe_enc_error, + &fcoe_eof, &tcp_udp_csum_ok, &udp, &tcp, + &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, + &fcs_ok); + + if (packet_error) { + + if (bytes_written > 0 && !fcs_ok) { + if (net_ratelimit()) + printk(KERN_ERR PFX + "%s: packet error: bad FCS\n", + enic->netdev->name); + } + + dev_kfree_skb_any(skb); + + return; + } + + if (eop && bytes_written > 0) { + + /* Good receive + */ + + skb_put(skb, bytes_written); + skb->protocol = eth_type_trans(skb, enic->netdev); + + if (enic->csum_rx_enabled && !csum_not_calc) { + skb->csum = htons(checksum); + skb->ip_summed = CHECKSUM_COMPLETE; + } + + skb->dev = enic->netdev; + enic->netdev->last_rx = jiffies; + + if (enic->vlan_group && vlan_stripped) { + + if (ENIC_SETTING(enic, LRO)) + lro_vlan_hwaccel_receive_skb(&enic->lro_mgr, + skb, enic->vlan_group, + vlan, cq_desc); + else + vlan_hwaccel_receive_skb(skb, + enic->vlan_group, vlan); + + } else { + + if (ENIC_SETTING(enic, LRO)) + lro_receive_skb(&enic->lro_mgr, skb, cq_desc); + else + netif_receive_skb(skb); + + } + + } else { + + /* Buffer overflow + */ + + dev_kfree_skb_any(skb); + } +} + +static int enic_rq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, + u8 type, u16 q_number, u16 completed_index, void *opaque) +{ + struct enic *enic = vnic_dev_priv(vdev); + + vnic_rq_service(&enic->rq[q_number], cq_desc, + completed_index, VNIC_RQ_RETURN_DESC, + enic_rq_indicate_buf, opaque); + + return 0; +} + +static void enic_rq_drop_buf(struct vnic_rq *rq, + struct cq_desc *cq_desc, struct vnic_rq_buf *buf, + int skipped, void *opaque) +{ + struct enic *enic = vnic_dev_priv(rq->vdev); + struct sk_buff *skb = buf->os_buf; + + if (skipped) + return; + + pci_unmap_single(enic->pdev, buf->dma_addr, + buf->len, PCI_DMA_FROMDEVICE); + + dev_kfree_skb_any(skb); +} + +static int enic_rq_service_drop(struct vnic_dev *vdev, struct cq_desc *cq_desc, + u8 type, u16 q_number, u16 completed_index, void *opaque) +{ + struct enic *enic = vnic_dev_priv(vdev); + + vnic_rq_service(&enic->rq[q_number], cq_desc, + completed_index, VNIC_RQ_RETURN_DESC, + enic_rq_drop_buf, opaque); + + return 0; +} + +static int enic_poll(struct napi_struct *napi, int budget) +{ + struct enic *enic = container_of(napi, struct enic, napi); + struct net_device *netdev = enic->netdev; + unsigned int rq_work_to_do = budget; + unsigned int wq_work_to_do = -1; /* no limit */ + unsigned int work_done, rq_work_done, wq_work_done; + + /* Service RQ (first) and WQ + */ + + rq_work_done = vnic_cq_service(&enic->cq[ENIC_CQ_RQ], + rq_work_to_do, enic_rq_service, NULL); + + wq_work_done = vnic_cq_service(&enic->cq[ENIC_CQ_WQ], + wq_work_to_do, enic_wq_service, NULL); + + /* Accumulate intr event credits for this polling + * cycle. An intr event is the completion of a + * a WQ or RQ packet. + */ + + work_done = rq_work_done + wq_work_done; + + if (work_done > 0) + vnic_intr_return_credits(&enic->intr[ENIC_INTX_WQ_RQ], + work_done, + 0 /* don't unmask intr */, + 0 /* don't reset intr timer */); + + if (rq_work_done > 0) { + + /* Replenish RQ + */ + + vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); + + } else { + + /* If no work done, flush all LROs and exit polling + */ + + if (ENIC_SETTING(enic, LRO)) + lro_flush_all(&enic->lro_mgr); + + netif_rx_complete(netdev, napi); + vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]); + } + + return rq_work_done; +} + +static int enic_poll_msix(struct napi_struct *napi, int budget) +{ + struct enic *enic = container_of(napi, struct enic, napi); + struct net_device *netdev = enic->netdev; + unsigned int work_to_do = budget; + unsigned int work_done; + + /* Service RQ + */ + + work_done = vnic_cq_service(&enic->cq[ENIC_CQ_RQ], + work_to_do, enic_rq_service, NULL); + + if (work_done > 0) { + + /* Replenish RQ + */ + + vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); + + /* Accumulate intr event credits for this polling + * cycle. An intr event is the completion of a + * a WQ or RQ packet. + */ + + vnic_intr_return_credits(&enic->intr[ENIC_MSIX_RQ], + work_done, + 0 /* don't unmask intr */, + 0 /* don't reset intr timer */); + } else { + + /* If no work done, flush all LROs and exit polling + */ + + if (ENIC_SETTING(enic, LRO)) + lro_flush_all(&enic->lro_mgr); + + netif_rx_complete(netdev, napi); + vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]); + } + + return work_done; +} + +static void enic_notify_timer(unsigned long data) +{ + struct enic *enic = (struct enic *)data; + + enic_notify_check(enic); + + mod_timer(&enic->notify_timer, round_jiffies(ENIC_NOTIFY_TIMER_PERIOD)); +} + +static void enic_free_intr(struct enic *enic) +{ + struct net_device *netdev = enic->netdev; + unsigned int i; + + switch (vnic_dev_get_intr_mode(enic->vdev)) { + case VNIC_DEV_INTR_MODE_INTX: + case VNIC_DEV_INTR_MODE_MSI: + free_irq(enic->pdev->irq, netdev); + break; + case VNIC_DEV_INTR_MODE_MSIX: + for (i = 0; i < ARRAY_SIZE(enic->msix); i++) + if (enic->msix[i].requested) + free_irq(enic->msix_entry[i].vector, + enic->msix[i].devid); + break; + default: + break; + } +} + +static int enic_request_intr(struct enic *enic) +{ + struct net_device *netdev = enic->netdev; + unsigned int i; + int err = 0; + + switch (vnic_dev_get_intr_mode(enic->vdev)) { + + case VNIC_DEV_INTR_MODE_INTX: + + err = request_irq(enic->pdev->irq, enic_isr_legacy, + IRQF_SHARED, netdev->name, netdev); + break; + + case VNIC_DEV_INTR_MODE_MSI: + + err = request_irq(enic->pdev->irq, enic_isr_msi, + 0, netdev->name, enic); + break; + + case VNIC_DEV_INTR_MODE_MSIX: + + sprintf(enic->msix[ENIC_MSIX_RQ].devname, + "%.11s-rx", netdev->name); + enic->msix[ENIC_MSIX_RQ].isr = enic_isr_msix_rq; + enic->msix[ENIC_MSIX_RQ].devid = enic; + + sprintf(enic->msix[ENIC_MSIX_WQ].devname, + "%.11s-tx", netdev->name); + enic->msix[ENIC_MSIX_WQ].isr = enic_isr_msix_wq; + enic->msix[ENIC_MSIX_WQ].devid = enic; + + sprintf(enic->msix[ENIC_MSIX_ERR].devname, + "%.11s-err", netdev->name); + enic->msix[ENIC_MSIX_ERR].isr = enic_isr_msix_err; + enic->msix[ENIC_MSIX_ERR].devid = enic; + + sprintf(enic->msix[ENIC_MSIX_NOTIFY].devname, + "%.11s-notify", netdev->name); + enic->msix[ENIC_MSIX_NOTIFY].isr = enic_isr_msix_notify; + enic->msix[ENIC_MSIX_NOTIFY].devid = enic; + + for (i = 0; i < ARRAY_SIZE(enic->msix); i++) { + err = request_irq(enic->msix_entry[i].vector, + enic->msix[i].isr, 0, + enic->msix[i].devname, + enic->msix[i].devid); + if (err) { + enic_free_intr(enic); + break; + } + enic->msix[i].requested = 1; + } + + break; + + default: + break; + } + + return err; +} + +static int enic_notify_set(struct enic *enic) +{ + int err; + + switch (vnic_dev_get_intr_mode(enic->vdev)) { + case VNIC_DEV_INTR_MODE_INTX: + err = vnic_dev_notify_set(enic->vdev, ENIC_INTX_NOTIFY); + break; + case VNIC_DEV_INTR_MODE_MSIX: + err = vnic_dev_notify_set(enic->vdev, ENIC_MSIX_NOTIFY); + break; + default: + err = vnic_dev_notify_set(enic->vdev, -1 /* no intr */); + break; + } + + return err; +} + +static void enic_notify_timer_start(struct enic *enic) +{ + switch (vnic_dev_get_intr_mode(enic->vdev)) { + case VNIC_DEV_INTR_MODE_MSI: + mod_timer(&enic->notify_timer, jiffies); + break; + default: + /* Using intr for notification for INTx/MSI-X */ + break; + }; +} + +/* rtnl lock is held, process context */ +static int enic_open(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + unsigned int i; + int err; + + for (i = 0; i < enic->rq_count; i++) { + err = vnic_rq_fill(&enic->rq[i], enic_rq_alloc_buf); + if (err) { + printk(KERN_ERR PFX + "%s: Unable to alloc receive buffers.\n", + netdev->name); + return err; + } + } + + for (i = 0; i < enic->wq_count; i++) + vnic_wq_enable(&enic->wq[i]); + for (i = 0; i < enic->rq_count; i++) + vnic_rq_enable(&enic->rq[i]); + + enic_add_station_addr(enic); + enic_set_multicast_list(netdev); + + netif_wake_queue(netdev); + napi_enable(&enic->napi); + vnic_dev_enable(enic->vdev); + + for (i = 0; i < enic->intr_count; i++) + vnic_intr_unmask(&enic->intr[i]); + + enic_notify_timer_start(enic); + + return 0; +} + +/* rtnl lock is held, process context */ +static int enic_stop(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + unsigned int i; + int err; + + del_timer_sync(&enic->notify_timer); + + vnic_dev_disable(enic->vdev); + napi_disable(&enic->napi); + netif_stop_queue(netdev); + + for (i = 0; i < enic->intr_count; i++) + vnic_intr_mask(&enic->intr[i]); + + for (i = 0; i < enic->wq_count; i++) { + err = vnic_wq_disable(&enic->wq[i]); + if (err) + return err; + } + for (i = 0; i < enic->rq_count; i++) { + err = vnic_rq_disable(&enic->rq[i]); + if (err) + return err; + } + + (void)vnic_cq_service(&enic->cq[ENIC_CQ_RQ], + -1, enic_rq_service_drop, NULL); + (void)vnic_cq_service(&enic->cq[ENIC_CQ_WQ], + -1, enic_wq_service, NULL); + + for (i = 0; i < enic->wq_count; i++) + vnic_wq_clean(&enic->wq[i], enic_free_wq_buf); + for (i = 0; i < enic->rq_count; i++) + vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); + for (i = 0; i < enic->cq_count; i++) + vnic_cq_clean(&enic->cq[i]); + for (i = 0; i < enic->intr_count; i++) + vnic_intr_clean(&enic->intr[i]); + + return 0; +} + +static int enic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct enic *enic = netdev_priv(netdev); + int running = netif_running(netdev); + + if (running) + enic_stop(netdev); + + if (new_mtu < ENIC_MIN_MTU) + new_mtu = ENIC_MIN_MTU; + if (new_mtu > ENIC_MAX_MTU) + new_mtu = ENIC_MAX_MTU; + + netdev->mtu = new_mtu; + + if (netdev->mtu > enic->port_mtu) + printk(KERN_WARNING PFX + "%s: interface MTU (%d) set higher " + "than port MTU (%d)\n", + netdev->name, netdev->mtu, enic->port_mtu); + + if (running) + enic_open(netdev); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void enic_poll_controller(struct net_device *netdev) +{ + struct enic *enic = netdev_priv(netdev); + struct vnic_dev *vdev = enic->vdev; + + switch (vnic_dev_get_intr_mode(vdev)) { + case VNIC_DEV_INTR_MODE_MSIX: + enic_isr_msix_rq(enic->pdev->irq, enic); + enic_isr_msix_wq(enic->pdev->irq, enic); + break; + case VNIC_DEV_INTR_MODE_MSI: + enic_isr_msi(enic->pdev->irq, enic); + break; + case VNIC_DEV_INTR_MODE_INTX: + enic_isr_legacy(enic->pdev->irq, netdev); + break; + default: + break; + } +} +#endif + +static int enic_dev_wait(struct vnic_dev *vdev, + int (*start)(struct vnic_dev *, int), + int (*finished)(struct vnic_dev *, int *), + int arg) +{ + unsigned long time; + int done; + int err; + + BUG_ON(in_interrupt()); + + err = start(vdev, arg); + if (err) + return err; + + /* Wait for func to complete...2 seconds max + */ + + time = jiffies + (HZ * 2); + do { + + err = finished(vdev, &done); + if (err) + return err; + + if (done) + return 0; + + schedule_timeout_uninterruptible(HZ / 10); + + } while (time_after(time, jiffies)); + + return -ETIMEDOUT; +} + +static int enic_dev_open(struct enic *enic) +{ + int err; + + err = enic_dev_wait(enic->vdev, vnic_dev_open, + vnic_dev_open_done, 0); + if (err) + printk(KERN_ERR PFX + "vNIC device open failed, err %d.\n", err); + + return err; +} + +static int enic_dev_soft_reset(struct enic *enic) +{ + int err; + + err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset, + vnic_dev_soft_reset_done, 0); + if (err) + printk(KERN_ERR PFX + "vNIC soft reset failed, err %d.\n", err); + + return err; +} + +static void enic_reset(struct work_struct *work) +{ + struct enic *enic = container_of(work, struct enic, reset); + + if (!netif_running(enic->netdev)) + return; + + rtnl_lock(); + + spin_lock(&enic->devcmd_lock); + vnic_dev_hang_notify(enic->vdev); + spin_unlock(&enic->devcmd_lock); + + enic_stop(enic->netdev); + enic_dev_soft_reset(enic); + enic_reset_mcaddrs(enic); + enic_init_vnic_resources(enic); + enic_open(enic->netdev); + + rtnl_unlock(); +} + +static int enic_set_intr_mode(struct enic *enic) +{ + unsigned int n = ARRAY_SIZE(enic->rq); + unsigned int m = ARRAY_SIZE(enic->wq); + unsigned int i; + + /* Set interrupt mode (INTx, MSI, MSI-X) depending + * system capabilities. + * + * Try MSI-X first + * + * We need n RQs, m WQs, n+m CQs, and n+m+2 INTRs + * (the second to last INTR is used for WQ/RQ errors) + * (the last INTR is used for notifications) + */ + + BUG_ON(ARRAY_SIZE(enic->msix_entry) < n + m + 2); + for (i = 0; i < n + m + 2; i++) + enic->msix_entry[i].entry = i; + + if (enic->config.intr_mode < 1 && + enic->rq_count >= n && + enic->wq_count >= m && + enic->cq_count >= n + m && + enic->intr_count >= n + m + 2 && + !pci_enable_msix(enic->pdev, enic->msix_entry, n + m + 2)) { + + enic->rq_count = n; + enic->wq_count = m; + enic->cq_count = n + m; + enic->intr_count = n + m + 2; + + vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_MSIX); + + return 0; + } + + /* Next try MSI + * + * We need 1 RQ, 1 WQ, 2 CQs, and 1 INTR + */ + + if (enic->config.intr_mode < 2 && + enic->rq_count >= 1 && + enic->wq_count >= 1 && + enic->cq_count >= 2 && + enic->intr_count >= 1 && + !pci_enable_msi(enic->pdev)) { + + enic->rq_count = 1; + enic->wq_count = 1; + enic->cq_count = 2; + enic->intr_count = 1; + + vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_MSI); + + return 0; + } + + /* Next try INTx + * + * We need 1 RQ, 1 WQ, 2 CQs, and 3 INTRs + * (the first INTR is used for WQ/RQ) + * (the second INTR is used for WQ/RQ errors) + * (the last INTR is used for notifications) + */ + + if (enic->config.intr_mode < 3 && + enic->rq_count >= 1 && + enic->wq_count >= 1 && + enic->cq_count >= 2 && + enic->intr_count >= 3) { + + enic->rq_count = 1; + enic->wq_count = 1; + enic->cq_count = 2; + enic->intr_count = 3; + + vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_INTX); + + return 0; + } + + vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); + + return -EINVAL; +} + +static void enic_clear_intr_mode(struct enic *enic) +{ + switch (vnic_dev_get_intr_mode(enic->vdev)) { + case VNIC_DEV_INTR_MODE_MSIX: + pci_disable_msix(enic->pdev); + break; + case VNIC_DEV_INTR_MODE_MSI: + pci_disable_msi(enic->pdev); + break; + default: + break; + } + + vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); +} + +static void enic_iounmap(struct enic *enic) +{ + if (enic->bar0.vaddr) + iounmap(enic->bar0.vaddr); +} + +static int __devinit enic_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct enic *enic; + int using_dac = 0; + unsigned int i; + int err; + + const u8 rss_default_cpu = 0; + const u8 rss_hash_type = 0; + const u8 rss_hash_bits = 0; + const u8 rss_base_cpu = 0; + const u8 rss_enable = 0; + const u8 tso_ipid_split_en = 0; + const u8 ig_vlan_strip_en = 1; + + /* Allocate net device structure and initialize. Private + * instance data is initialized to zero. + */ + + netdev = alloc_etherdev(sizeof(struct enic)); + if (!netdev) { + printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + return -ENOMEM; + } + + /* Set the netdev name early so intr vectors are properly + * named and any error msgs can include netdev->name + */ + + rtnl_lock(); + err = dev_alloc_name(netdev, netdev->name); + rtnl_unlock(); + if (err < 0) { + printk(KERN_ERR PFX "Unable to allocate netdev name.\n"); + goto err_out_free_netdev; + } + + pci_set_drvdata(pdev, netdev); + + SET_NETDEV_DEV(netdev, &pdev->dev); + + enic = netdev_priv(netdev); + enic->netdev = netdev; + enic->pdev = pdev; + + /* Setup PCI resources + */ + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX + "%s: Cannot enable PCI device, aborting.\n", + netdev->name); + goto err_out_free_netdev; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + printk(KERN_ERR PFX + "%s: Cannot request PCI regions, aborting.\n", + netdev->name); + goto err_out_disable_device; + } + + pci_set_master(pdev); + + /* Query PCI controller on system for DMA addressing + * limitation for the device. Try 40-bit first, and + * fail to 32-bit. + */ + + err = pci_set_dma_mask(pdev, DMA_40BIT_MASK); + if (err) { + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + printk(KERN_ERR PFX + "%s: No usable DMA configuration, aborting.\n", + netdev->name); + goto err_out_release_regions; + } + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + printk(KERN_ERR PFX + "%s: Unable to obtain 32-bit DMA " + "for consistent allocations, aborting.\n", + netdev->name); + goto err_out_release_regions; + } + } else { + err = pci_set_consistent_dma_mask(pdev, DMA_40BIT_MASK); + if (err) { + printk(KERN_ERR PFX + "%s: Unable to obtain 40-bit DMA " + "for consistent allocations, aborting.\n", + netdev->name); + goto err_out_release_regions; + } + using_dac = 1; + } + + /* Map vNIC resources from BAR0 + */ + + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { + printk(KERN_ERR PFX + "%s: BAR0 not memory-map'able, aborting.\n", + netdev->name); + err = -ENODEV; + goto err_out_release_regions; + } + + enic->bar0.vaddr = pci_iomap(pdev, 0, enic->bar0.len); + enic->bar0.bus_addr = pci_resource_start(pdev, 0); + enic->bar0.len = pci_resource_len(pdev, 0); + + if (!enic->bar0.vaddr) { + printk(KERN_ERR PFX + "%s: Cannot memory-map BAR0 res hdr, aborting.\n", + netdev->name); + err = -ENODEV; + goto err_out_release_regions; + } + + /* Register vNIC device + */ + + enic->vdev = vnic_dev_register(NULL, enic, pdev, &enic->bar0); + if (!enic->vdev) { + printk(KERN_ERR PFX + "%s: vNIC registration failed, aborting.\n", + netdev->name); + err = -ENODEV; + goto err_out_iounmap; + } + + /* Issue device open to get device in known state + */ + + err = enic_dev_open(enic); + if (err) { + printk(KERN_ERR PFX + "%s: vNIC dev open failed, aborting.\n", + netdev->name); + goto err_out_vnic_unregister; + } + + /* Issue device init to initialize the vnic-to-switch link. + * We'll start with carrier off and wait for link UP + * notification later to turn on carrier. We don't need + * to wait here for the vnic-to-switch link initialization + * to complete; link UP notification is the indication that + * the process is complete. + */ + + netif_carrier_off(netdev); + + err = vnic_dev_init(enic->vdev, 0); + if (err) { + printk(KERN_ERR PFX + "%s: vNIC dev init failed, aborting.\n", + netdev->name); + goto err_out_dev_close; + } + + /* Get vNIC configuration + */ + + err = enic_get_vnic_config(enic); + if (err) { + printk(KERN_ERR PFX + "%s: Get vNIC configuration failed, aborting.\n", + netdev->name); + goto err_out_dev_close; + } + + /* Get available resource counts + */ + + enic_get_res_counts(enic); + + /* Set interrupt mode based on resource counts and system + * capabilities + */ + + err = enic_set_intr_mode(enic); + if (err) { + printk(KERN_ERR PFX + "%s: Failed to set intr mode, aborting.\n", + netdev->name); + goto err_out_dev_close; + } + + /* Request interrupt vector(s) + */ + + err = enic_request_intr(enic); + if (err) { + printk(KERN_ERR PFX "%s: Unable to request irq.\n", + netdev->name); + goto err_out_dev_close; + } + + /* Allocate and configure vNIC resources + */ + + err = enic_alloc_vnic_resources(enic); + if (err) { + printk(KERN_ERR PFX + "%s: Failed to alloc vNIC resources, aborting.\n", + netdev->name); + goto err_out_free_vnic_resources; + } + + enic_init_vnic_resources(enic); + + /* Enable VLAN tag stripping. RSS not enabled (yet). + */ + + err = enic_set_nic_cfg(enic, + rss_default_cpu, rss_hash_type, + rss_hash_bits, rss_base_cpu, + rss_enable, tso_ipid_split_en, + ig_vlan_strip_en); + if (err) { + printk(KERN_ERR PFX + "%s: Failed to config nic, aborting.\n", + netdev->name); + goto err_out_free_vnic_resources; + } + + /* Setup notification buffer area + */ + + err = enic_notify_set(enic); + if (err) { + printk(KERN_ERR PFX + "%s: Failed to alloc notify buffer, aborting.\n", + netdev->name); + goto err_out_free_vnic_resources; + } + + /* Setup notification timer, HW reset task, and locks + */ + + init_timer(&enic->notify_timer); + enic->notify_timer.function = enic_notify_timer; + enic->notify_timer.data = (unsigned long)enic; + + INIT_WORK(&enic->reset, enic_reset); + + for (i = 0; i < enic->wq_count; i++) + spin_lock_init(&enic->wq_lock[i]); + + spin_lock_init(&enic->devcmd_lock); + + /* Register net device + */ + + enic->port_mtu = enic->config.mtu; + (void)enic_change_mtu(netdev, enic->port_mtu); + + err = enic_set_mac_addr(netdev, enic->mac_addr); + if (err) { + printk(KERN_ERR PFX + "%s: Invalid MAC address, aborting.\n", + netdev->name); + goto err_out_notify_unset; + } + + netdev->open = enic_open; + netdev->stop = enic_stop; + netdev->hard_start_xmit = enic_hard_start_xmit; + netdev->get_stats = enic_get_stats; + netdev->set_multicast_list = enic_set_multicast_list; + netdev->change_mtu = enic_change_mtu; + netdev->vlan_rx_register = enic_vlan_rx_register; + netdev->vlan_rx_add_vid = enic_vlan_rx_add_vid; + netdev->vlan_rx_kill_vid = enic_vlan_rx_kill_vid; + netdev->tx_timeout = enic_tx_timeout; + netdev->watchdog_timeo = 2 * HZ; + netdev->ethtool_ops = &enic_ethtool_ops; +#ifdef CONFIG_NET_POLL_CONTROLLER + netdev->poll_controller = enic_poll_controller; +#endif + + switch (vnic_dev_get_intr_mode(enic->vdev)) { + default: + netif_napi_add(netdev, &enic->napi, enic_poll, 64); + break; + case VNIC_DEV_INTR_MODE_MSIX: + netif_napi_add(netdev, &enic->napi, enic_poll_msix, 64); + break; + } + + netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + if (ENIC_SETTING(enic, TXCSUM)) + netdev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + if (ENIC_SETTING(enic, TSO)) + netdev->features |= NETIF_F_TSO | + NETIF_F_TSO6 | NETIF_F_TSO_ECN; + if (using_dac) + netdev->features |= NETIF_F_HIGHDMA; + + + enic->csum_rx_enabled = ENIC_SETTING(enic, RXCSUM); + + if (ENIC_SETTING(enic, LRO)) { + enic->lro_mgr.max_aggr = ENIC_LRO_MAX_AGGR; + enic->lro_mgr.max_desc = ENIC_LRO_MAX_DESC; + enic->lro_mgr.lro_arr = enic->lro_desc; + enic->lro_mgr.get_skb_header = enic_get_skb_header; + enic->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID; + enic->lro_mgr.dev = netdev; + enic->lro_mgr.ip_summed = CHECKSUM_COMPLETE; + enic->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY; + } + + err = register_netdev(netdev); + if (err) { + printk(KERN_ERR PFX + "%s: Cannot register net device, aborting.\n", + netdev->name); + goto err_out_notify_unset; + } + + return 0; + +err_out_notify_unset: + vnic_dev_notify_unset(enic->vdev); +err_out_free_vnic_resources: + enic_free_vnic_resources(enic); + enic_free_intr(enic); +err_out_dev_close: + vnic_dev_close(enic->vdev); +err_out_vnic_unregister: + enic_clear_intr_mode(enic); + vnic_dev_unregister(enic->vdev); +err_out_iounmap: + enic_iounmap(enic); +err_out_release_regions: + pci_release_regions(pdev); +err_out_disable_device: + pci_disable_device(pdev); +err_out_free_netdev: + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + + return err; +} + +static void __devexit enic_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + + if (netdev) { + struct enic *enic = netdev_priv(netdev); + + flush_scheduled_work(); + unregister_netdev(netdev); + vnic_dev_notify_unset(enic->vdev); + enic_free_vnic_resources(enic); + enic_free_intr(enic); + vnic_dev_close(enic->vdev); + enic_clear_intr_mode(enic); + vnic_dev_unregister(enic->vdev); + enic_iounmap(enic); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + } +} + +static struct pci_driver enic_driver = { + .name = DRV_NAME, + .id_table = enic_id_table, + .probe = enic_probe, + .remove = __devexit_p(enic_remove), +}; + +static int __init enic_init_module(void) +{ + printk(KERN_INFO PFX "%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION); + + return pci_register_driver(&enic_driver); +} + +static void __exit enic_cleanup_module(void) +{ + pci_unregister_driver(&enic_driver); +} + +module_init(enic_init_module); +module_exit(enic_cleanup_module); diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c new file mode 100644 index 000000000000..95184b9108ef --- /dev/null +++ b/drivers/net/enic/enic_res.c @@ -0,0 +1,370 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "wq_enet_desc.h" +#include "rq_enet_desc.h" +#include "cq_enet_desc.h" +#include "vnic_resource.h" +#include "vnic_enet.h" +#include "vnic_dev.h" +#include "vnic_wq.h" +#include "vnic_rq.h" +#include "vnic_cq.h" +#include "vnic_intr.h" +#include "vnic_stats.h" +#include "vnic_nic.h" +#include "vnic_rss.h" +#include "enic_res.h" +#include "enic.h" + +int enic_get_vnic_config(struct enic *enic) +{ + struct vnic_enet_config *c = &enic->config; + int err; + + err = vnic_dev_mac_addr(enic->vdev, enic->mac_addr); + if (err) { + printk(KERN_ERR PFX "Error getting MAC addr, %d\n", err); + return err; + } + +#define GET_CONFIG(m) \ + do { \ + err = vnic_dev_spec(enic->vdev, \ + offsetof(struct vnic_enet_config, m), \ + sizeof(c->m), &c->m); \ + if (err) { \ + printk(KERN_ERR PFX \ + "Error getting %s, %d\n", #m, err); \ + return err; \ + } \ + } while (0) + + GET_CONFIG(flags); + GET_CONFIG(wq_desc_count); + GET_CONFIG(rq_desc_count); + GET_CONFIG(mtu); + GET_CONFIG(intr_timer); + GET_CONFIG(intr_timer_type); + GET_CONFIG(intr_mode); + + c->wq_desc_count = + min_t(u32, ENIC_MAX_WQ_DESCS, + max_t(u32, ENIC_MIN_WQ_DESCS, + c->wq_desc_count)); + c->wq_desc_count &= 0xfffffff0; /* must be aligned to groups of 16 */ + + c->rq_desc_count = + min_t(u32, ENIC_MAX_RQ_DESCS, + max_t(u32, ENIC_MIN_RQ_DESCS, + c->rq_desc_count)); + c->rq_desc_count &= 0xfffffff0; /* must be aligned to groups of 16 */ + + if (c->mtu == 0) + c->mtu = 1500; + c->mtu = min_t(u16, ENIC_MAX_MTU, + max_t(u16, ENIC_MIN_MTU, + c->mtu)); + + c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer); + + printk(KERN_INFO PFX "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x " + "wq/rq %d/%d\n", + enic->mac_addr[0], enic->mac_addr[1], enic->mac_addr[2], + enic->mac_addr[3], enic->mac_addr[4], enic->mac_addr[5], + c->wq_desc_count, c->rq_desc_count); + printk(KERN_INFO PFX "vNIC mtu %d csum tx/rx %d/%d tso/lro %d/%d " + "intr timer %d\n", + c->mtu, ENIC_SETTING(enic, TXCSUM), + ENIC_SETTING(enic, RXCSUM), ENIC_SETTING(enic, TSO), + ENIC_SETTING(enic, LRO), c->intr_timer); + + return 0; +} + +void enic_add_station_addr(struct enic *enic) +{ + vnic_dev_add_addr(enic->vdev, enic->mac_addr); +} + +void enic_add_multicast_addr(struct enic *enic, u8 *addr) +{ + vnic_dev_add_addr(enic->vdev, addr); +} + +void enic_del_multicast_addr(struct enic *enic, u8 *addr) +{ + vnic_dev_del_addr(enic->vdev, addr); +} + +void enic_add_vlan(struct enic *enic, u16 vlanid) +{ + u64 a0 = vlanid, a1 = 0; + int wait = 1000; + int err; + + err = vnic_dev_cmd(enic->vdev, CMD_VLAN_ADD, &a0, &a1, wait); + if (err) + printk(KERN_ERR PFX "Can't add vlan id, %d\n", err); +} + +void enic_del_vlan(struct enic *enic, u16 vlanid) +{ + u64 a0 = vlanid, a1 = 0; + int wait = 1000; + int err; + + err = vnic_dev_cmd(enic->vdev, CMD_VLAN_DEL, &a0, &a1, wait); + if (err) + printk(KERN_ERR PFX "Can't delete vlan id, %d\n", err); +} + +int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type, + u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en, + u8 ig_vlan_strip_en) +{ + u64 a0, a1; + u32 nic_cfg; + int wait = 1000; + + vnic_set_nic_cfg(&nic_cfg, rss_default_cpu, + rss_hash_type, rss_hash_bits, rss_base_cpu, + rss_enable, tso_ipid_split_en, ig_vlan_strip_en); + + a0 = nic_cfg; + a1 = 0; + + return vnic_dev_cmd(enic->vdev, CMD_NIC_CFG, &a0, &a1, wait); +} + +void enic_free_vnic_resources(struct enic *enic) +{ + unsigned int i; + + for (i = 0; i < enic->wq_count; i++) + vnic_wq_free(&enic->wq[i]); + for (i = 0; i < enic->rq_count; i++) + vnic_rq_free(&enic->rq[i]); + for (i = 0; i < enic->cq_count; i++) + vnic_cq_free(&enic->cq[i]); + for (i = 0; i < enic->intr_count; i++) + vnic_intr_free(&enic->intr[i]); +} + +void enic_get_res_counts(struct enic *enic) +{ + enic->wq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ); + enic->rq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_RQ); + enic->cq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_CQ); + enic->intr_count = vnic_dev_get_res_count(enic->vdev, + RES_TYPE_INTR_CTRL); + + printk(KERN_INFO PFX "vNIC resources avail: " + "wq %d rq %d cq %d intr %d\n", + enic->wq_count, enic->rq_count, + enic->cq_count, enic->intr_count); +} + +void enic_init_vnic_resources(struct enic *enic) +{ + enum vnic_dev_intr_mode intr_mode; + unsigned int mask_on_assertion; + unsigned int interrupt_offset; + unsigned int error_interrupt_enable; + unsigned int error_interrupt_offset; + unsigned int cq_index; + unsigned int i; + + intr_mode = vnic_dev_get_intr_mode(enic->vdev); + + /* Init RQ/WQ resources. + * + * RQ[0 - n-1] point to CQ[0 - n-1] + * WQ[0 - m-1] point to CQ[n - n+m-1] + * + * Error interrupt is not enabled for MSI. + */ + + switch (intr_mode) { + case VNIC_DEV_INTR_MODE_INTX: + case VNIC_DEV_INTR_MODE_MSIX: + error_interrupt_enable = 1; + error_interrupt_offset = enic->intr_count - 2; + break; + default: + error_interrupt_enable = 0; + error_interrupt_offset = 0; + break; + } + + for (i = 0; i < enic->rq_count; i++) { + cq_index = i; + vnic_rq_init(&enic->rq[i], + cq_index, + error_interrupt_enable, + error_interrupt_offset); + } + + for (i = 0; i < enic->wq_count; i++) { + cq_index = enic->rq_count + i; + vnic_wq_init(&enic->wq[i], + cq_index, + error_interrupt_enable, + error_interrupt_offset); + } + + /* Init CQ resources + * + * CQ[0 - n+m-1] point to INTR[0] for INTx, MSI + * CQ[0 - n+m-1] point to INTR[0 - n+m-1] for MSI-X + */ + + for (i = 0; i < enic->cq_count; i++) { + + switch (intr_mode) { + case VNIC_DEV_INTR_MODE_MSIX: + interrupt_offset = i; + break; + default: + interrupt_offset = 0; + break; + } + + vnic_cq_init(&enic->cq[i], + 0 /* flow_control_enable */, + 1 /* color_enable */, + 0 /* cq_head */, + 0 /* cq_tail */, + 1 /* cq_tail_color */, + 1 /* interrupt_enable */, + 1 /* cq_entry_enable */, + 0 /* cq_message_enable */, + interrupt_offset, + 0 /* cq_message_addr */); + } + + /* Init INTR resources + * + * mask_on_assertion is not used for INTx due to the level- + * triggered nature of INTx + */ + + switch (intr_mode) { + case VNIC_DEV_INTR_MODE_MSI: + case VNIC_DEV_INTR_MODE_MSIX: + mask_on_assertion = 1; + break; + default: + mask_on_assertion = 0; + break; + } + + for (i = 0; i < enic->intr_count; i++) { + vnic_intr_init(&enic->intr[i], + enic->config.intr_timer, + enic->config.intr_timer_type, + mask_on_assertion); + } + + /* Clear LIF stats + */ + + vnic_dev_stats_clear(enic->vdev); +} + +int enic_alloc_vnic_resources(struct enic *enic) +{ + enum vnic_dev_intr_mode intr_mode; + unsigned int i; + int err; + + intr_mode = vnic_dev_get_intr_mode(enic->vdev); + + printk(KERN_INFO PFX "vNIC resources used: " + "wq %d rq %d cq %d intr %d intr mode %s\n", + enic->wq_count, enic->rq_count, + enic->cq_count, enic->intr_count, + intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" : + intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" : + intr_mode == VNIC_DEV_INTR_MODE_MSIX ? "MSI-X" : + "unknown" + ); + + /* Allocate queue resources + */ + + for (i = 0; i < enic->wq_count; i++) { + err = vnic_wq_alloc(enic->vdev, &enic->wq[i], i, + enic->config.wq_desc_count, + sizeof(struct wq_enet_desc)); + if (err) + goto err_out_cleanup; + } + + for (i = 0; i < enic->rq_count; i++) { + err = vnic_rq_alloc(enic->vdev, &enic->rq[i], i, + enic->config.rq_desc_count, + sizeof(struct rq_enet_desc)); + if (err) + goto err_out_cleanup; + } + + for (i = 0; i < enic->cq_count; i++) { + if (i < enic->rq_count) + err = vnic_cq_alloc(enic->vdev, &enic->cq[i], i, + enic->config.rq_desc_count, + sizeof(struct cq_enet_rq_desc)); + else + err = vnic_cq_alloc(enic->vdev, &enic->cq[i], i, + enic->config.wq_desc_count, + sizeof(struct cq_enet_wq_desc)); + if (err) + goto err_out_cleanup; + } + + for (i = 0; i < enic->intr_count; i++) { + err = vnic_intr_alloc(enic->vdev, &enic->intr[i], i); + if (err) + goto err_out_cleanup; + } + + /* Hook remaining resource + */ + + enic->legacy_pba = vnic_dev_get_res(enic->vdev, + RES_TYPE_INTR_PBA_LEGACY, 0); + if (!enic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) { + printk(KERN_ERR PFX "Failed to hook legacy pba resource\n"); + err = -ENODEV; + goto err_out_cleanup; + } + + return 0; + +err_out_cleanup: + enic_free_vnic_resources(enic); + + return err; +} diff --git a/drivers/net/enic/enic_res.h b/drivers/net/enic/enic_res.h new file mode 100644 index 000000000000..68534a29b7ac --- /dev/null +++ b/drivers/net/enic/enic_res.h @@ -0,0 +1,151 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _ENIC_RES_H_ +#define _ENIC_RES_H_ + +#include "wq_enet_desc.h" +#include "rq_enet_desc.h" +#include "vnic_wq.h" +#include "vnic_rq.h" + +#define ENIC_MIN_WQ_DESCS 64 +#define ENIC_MAX_WQ_DESCS 4096 +#define ENIC_MIN_RQ_DESCS 64 +#define ENIC_MAX_RQ_DESCS 4096 + +#define ENIC_MIN_MTU 576 /* minimum for IPv4 */ +#define ENIC_MAX_MTU 9000 + +#define ENIC_MULTICAST_PERFECT_FILTERS 32 + +#define ENIC_NON_TSO_MAX_DESC 16 + +#define ENIC_SETTING(enic, f) ((enic->config.flags & VENETF_##f) ? 1 : 0) + +static inline void enic_queue_wq_desc_ex(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, unsigned int len, + unsigned int mss_or_csum_offset, unsigned int hdr_len, + int vlan_tag_insert, unsigned int vlan_tag, + int offload_mode, int cq_entry, int sop, int eop) +{ + struct wq_enet_desc *desc = vnic_wq_next_desc(wq); + + wq_enet_desc_enc(desc, + (u64)dma_addr | VNIC_PADDR_TARGET, + (u16)len, + (u16)mss_or_csum_offset, + (u16)hdr_len, (u8)offload_mode, + (u8)eop, (u8)cq_entry, + 0, /* fcoe_encap */ + (u8)vlan_tag_insert, + (u16)vlan_tag, + 0 /* loopback */); + + wmb(); + + vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop); +} + +static inline void enic_queue_wq_desc_cont(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, unsigned int len, int eop) +{ + enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, + 0, 0, 0, 0, 0, + eop, 0 /* !SOP */, eop); +} + +static inline void enic_queue_wq_desc(struct vnic_wq *wq, void *os_buf, + dma_addr_t dma_addr, unsigned int len, int vlan_tag_insert, + unsigned int vlan_tag, int eop) +{ + enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, + 0, 0, vlan_tag_insert, vlan_tag, + WQ_ENET_OFFLOAD_MODE_CSUM, + eop, 1 /* SOP */, eop); +} + +static inline void enic_queue_wq_desc_csum(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, unsigned int len, + int ip_csum, int tcpudp_csum, int vlan_tag_insert, + unsigned int vlan_tag, int eop) +{ + enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, + (ip_csum ? 1 : 0) + (tcpudp_csum ? 2 : 0), + 0, vlan_tag_insert, vlan_tag, + WQ_ENET_OFFLOAD_MODE_CSUM, + eop, 1 /* SOP */, eop); +} + +static inline void enic_queue_wq_desc_csum_l4(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, unsigned int len, + unsigned int csum_offset, unsigned int hdr_len, + int vlan_tag_insert, unsigned int vlan_tag, int eop) +{ + enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, + csum_offset, hdr_len, vlan_tag_insert, vlan_tag, + WQ_ENET_OFFLOAD_MODE_CSUM_L4, + eop, 1 /* SOP */, eop); +} + +static inline void enic_queue_wq_desc_tso(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, unsigned int len, + unsigned int mss, unsigned int hdr_len, int vlan_tag_insert, + unsigned int vlan_tag, int eop) +{ + enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, + mss, hdr_len, vlan_tag_insert, vlan_tag, + WQ_ENET_OFFLOAD_MODE_TSO, + eop, 1 /* SOP */, eop); +} + +static inline void enic_queue_rq_desc(struct vnic_rq *rq, + void *os_buf, unsigned int os_buf_index, + dma_addr_t dma_addr, unsigned int len) +{ + struct rq_enet_desc *desc = vnic_rq_next_desc(rq); + u8 type = os_buf_index ? + RQ_ENET_TYPE_NOT_SOP : RQ_ENET_TYPE_ONLY_SOP; + + rq_enet_desc_enc(desc, + (u64)dma_addr | VNIC_PADDR_TARGET, + type, (u16)len); + + wmb(); + + vnic_rq_post(rq, os_buf, os_buf_index, dma_addr, len); +} + +struct enic; + +int enic_get_vnic_config(struct enic *); +void enic_add_station_addr(struct enic *enic); +void enic_add_multicast_addr(struct enic *enic, u8 *addr); +void enic_del_multicast_addr(struct enic *enic, u8 *addr); +void enic_add_vlan(struct enic *enic, u16 vlanid); +void enic_del_vlan(struct enic *enic, u16 vlanid); +int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type, + u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en, + u8 ig_vlan_strip_en); +void enic_get_res_counts(struct enic *enic); +void enic_init_vnic_resources(struct enic *enic); +int enic_alloc_vnic_resources(struct enic *); +void enic_free_vnic_resources(struct enic *); + +#endif /* _ENIC_RES_H_ */ diff --git a/drivers/net/enic/rq_enet_desc.h b/drivers/net/enic/rq_enet_desc.h new file mode 100644 index 000000000000..a06e649010ce --- /dev/null +++ b/drivers/net/enic/rq_enet_desc.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _RQ_ENET_DESC_H_ +#define _RQ_ENET_DESC_H_ + +/* Ethernet receive queue descriptor: 16B */ +struct rq_enet_desc { + __le64 address; + __le16 length_type; + u8 reserved[6]; +}; + +enum rq_enet_type_types { + RQ_ENET_TYPE_ONLY_SOP = 0, + RQ_ENET_TYPE_NOT_SOP = 1, + RQ_ENET_TYPE_RESV2 = 2, + RQ_ENET_TYPE_RESV3 = 3, +}; + +#define RQ_ENET_ADDR_BITS 64 +#define RQ_ENET_LEN_BITS 14 +#define RQ_ENET_LEN_MASK ((1 << RQ_ENET_LEN_BITS) - 1) +#define RQ_ENET_TYPE_BITS 2 +#define RQ_ENET_TYPE_MASK ((1 << RQ_ENET_TYPE_BITS) - 1) + +static inline void rq_enet_desc_enc(struct rq_enet_desc *desc, + u64 address, u8 type, u16 length) +{ + desc->address = cpu_to_le64(address); + desc->length_type = cpu_to_le16((length & RQ_ENET_LEN_MASK) | + ((type & RQ_ENET_TYPE_MASK) << RQ_ENET_LEN_BITS)); +} + +static inline void rq_enet_desc_dec(struct rq_enet_desc *desc, + u64 *address, u8 *type, u16 *length) +{ + *address = le64_to_cpu(desc->address); + *length = le16_to_cpu(desc->length_type) & RQ_ENET_LEN_MASK; + *type = (u8)((le16_to_cpu(desc->length_type) >> RQ_ENET_LEN_BITS) & + RQ_ENET_TYPE_MASK); +} + +#endif /* _RQ_ENET_DESC_H_ */ diff --git a/drivers/net/enic/vnic_cq.c b/drivers/net/enic/vnic_cq.c new file mode 100644 index 000000000000..020ae6c3f3d9 --- /dev/null +++ b/drivers/net/enic/vnic_cq.c @@ -0,0 +1,89 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include + +#include "vnic_dev.h" +#include "vnic_cq.h" + +void vnic_cq_free(struct vnic_cq *cq) +{ + vnic_dev_free_desc_ring(cq->vdev, &cq->ring); + + cq->ctrl = NULL; +} + +int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index, + unsigned int desc_count, unsigned int desc_size) +{ + int err; + + cq->index = index; + cq->vdev = vdev; + + cq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_CQ, index); + if (!cq->ctrl) { + printk(KERN_ERR "Failed to hook CQ[%d] resource\n", index); + return -EINVAL; + } + + err = vnic_dev_alloc_desc_ring(vdev, &cq->ring, desc_count, desc_size); + if (err) + return err; + + return 0; +} + +void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable, + unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail, + unsigned int cq_tail_color, unsigned int interrupt_enable, + unsigned int cq_entry_enable, unsigned int cq_message_enable, + unsigned int interrupt_offset, u64 cq_message_addr) +{ + u64 paddr; + + paddr = (u64)cq->ring.base_addr | VNIC_PADDR_TARGET; + writeq(paddr, &cq->ctrl->ring_base); + iowrite32(cq->ring.desc_count, &cq->ctrl->ring_size); + iowrite32(flow_control_enable, &cq->ctrl->flow_control_enable); + iowrite32(color_enable, &cq->ctrl->color_enable); + iowrite32(cq_head, &cq->ctrl->cq_head); + iowrite32(cq_tail, &cq->ctrl->cq_tail); + iowrite32(cq_tail_color, &cq->ctrl->cq_tail_color); + iowrite32(interrupt_enable, &cq->ctrl->interrupt_enable); + iowrite32(cq_entry_enable, &cq->ctrl->cq_entry_enable); + iowrite32(cq_message_enable, &cq->ctrl->cq_message_enable); + iowrite32(interrupt_offset, &cq->ctrl->interrupt_offset); + writeq(cq_message_addr, &cq->ctrl->cq_message_addr); +} + +void vnic_cq_clean(struct vnic_cq *cq) +{ + cq->to_clean = 0; + cq->last_color = 0; + + iowrite32(0, &cq->ctrl->cq_head); + iowrite32(0, &cq->ctrl->cq_tail); + iowrite32(1, &cq->ctrl->cq_tail_color); + + vnic_dev_clear_desc_ring(&cq->ring); +} diff --git a/drivers/net/enic/vnic_cq.h b/drivers/net/enic/vnic_cq.h new file mode 100644 index 000000000000..114763cbc2f8 --- /dev/null +++ b/drivers/net/enic/vnic_cq.h @@ -0,0 +1,113 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_CQ_H_ +#define _VNIC_CQ_H_ + +#include "cq_desc.h" +#include "vnic_dev.h" + +/* Completion queue control */ +struct vnic_cq_ctrl { + u64 ring_base; /* 0x00 */ + u32 ring_size; /* 0x08 */ + u32 pad0; + u32 flow_control_enable; /* 0x10 */ + u32 pad1; + u32 color_enable; /* 0x18 */ + u32 pad2; + u32 cq_head; /* 0x20 */ + u32 pad3; + u32 cq_tail; /* 0x28 */ + u32 pad4; + u32 cq_tail_color; /* 0x30 */ + u32 pad5; + u32 interrupt_enable; /* 0x38 */ + u32 pad6; + u32 cq_entry_enable; /* 0x40 */ + u32 pad7; + u32 cq_message_enable; /* 0x48 */ + u32 pad8; + u32 interrupt_offset; /* 0x50 */ + u32 pad9; + u64 cq_message_addr; /* 0x58 */ + u32 pad10; +}; + +struct vnic_cq { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_cq_ctrl __iomem *ctrl; /* memory-mapped */ + struct vnic_dev_ring ring; + unsigned int to_clean; + unsigned int last_color; +}; + +static inline unsigned int vnic_cq_service(struct vnic_cq *cq, + unsigned int work_to_do, + int (*q_service)(struct vnic_dev *vdev, struct cq_desc *cq_desc, + u8 type, u16 q_number, u16 completed_index, void *opaque), + void *opaque) +{ + struct cq_desc *cq_desc; + unsigned int work_done = 0; + u16 q_number, completed_index; + u8 type, color; + + cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs + + cq->ring.desc_size * cq->to_clean); + cq_desc_dec(cq_desc, &type, &color, + &q_number, &completed_index); + + while (color != cq->last_color) { + + if ((*q_service)(cq->vdev, cq_desc, type, + q_number, completed_index, opaque)) + break; + + cq->to_clean++; + if (cq->to_clean == cq->ring.desc_count) { + cq->to_clean = 0; + cq->last_color = cq->last_color ? 0 : 1; + } + + cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs + + cq->ring.desc_size * cq->to_clean); + cq_desc_dec(cq_desc, &type, &color, + &q_number, &completed_index); + + work_done++; + if (work_done >= work_to_do) + break; + } + + return work_done; +} + +void vnic_cq_free(struct vnic_cq *cq); +int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index, + unsigned int desc_count, unsigned int desc_size); +void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable, + unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail, + unsigned int cq_tail_color, unsigned int interrupt_enable, + unsigned int cq_entry_enable, unsigned int message_enable, + unsigned int interrupt_offset, u64 message_addr); +void vnic_cq_clean(struct vnic_cq *cq); + +#endif /* _VNIC_CQ_H_ */ diff --git a/drivers/net/enic/vnic_dev.c b/drivers/net/enic/vnic_dev.c new file mode 100644 index 000000000000..4d104f5c30f9 --- /dev/null +++ b/drivers/net/enic/vnic_dev.c @@ -0,0 +1,674 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "vnic_resource.h" +#include "vnic_devcmd.h" +#include "vnic_dev.h" +#include "vnic_stats.h" + +struct vnic_res { + void __iomem *vaddr; + unsigned int count; +}; + +struct vnic_dev { + void *priv; + struct pci_dev *pdev; + struct vnic_res res[RES_TYPE_MAX]; + enum vnic_dev_intr_mode intr_mode; + struct vnic_devcmd __iomem *devcmd; + struct vnic_devcmd_notify *notify; + struct vnic_devcmd_notify notify_copy; + dma_addr_t notify_pa; + u32 *linkstatus; + dma_addr_t linkstatus_pa; + struct vnic_stats *stats; + dma_addr_t stats_pa; + struct vnic_devcmd_fw_info *fw_info; + dma_addr_t fw_info_pa; +}; + +#define VNIC_MAX_RES_HDR_SIZE \ + (sizeof(struct vnic_resource_header) + \ + sizeof(struct vnic_resource) * RES_TYPE_MAX) +#define VNIC_RES_STRIDE 128 + +void *vnic_dev_priv(struct vnic_dev *vdev) +{ + return vdev->priv; +} + +static int vnic_dev_discover_res(struct vnic_dev *vdev, + struct vnic_dev_bar *bar) +{ + struct vnic_resource_header __iomem *rh; + struct vnic_resource __iomem *r; + u8 type; + + if (bar->len < VNIC_MAX_RES_HDR_SIZE) { + printk(KERN_ERR "vNIC BAR0 res hdr length error\n"); + return -EINVAL; + } + + rh = bar->vaddr; + if (!rh) { + printk(KERN_ERR "vNIC BAR0 res hdr not mem-mapped\n"); + return -EINVAL; + } + + if (ioread32(&rh->magic) != VNIC_RES_MAGIC || + ioread32(&rh->version) != VNIC_RES_VERSION) { + printk(KERN_ERR "vNIC BAR0 res magic/version error " + "exp (%lx/%lx) curr (%x/%x)\n", + VNIC_RES_MAGIC, VNIC_RES_VERSION, + ioread32(&rh->magic), ioread32(&rh->version)); + return -EINVAL; + } + + r = (struct vnic_resource __iomem *)(rh + 1); + + while ((type = ioread8(&r->type)) != RES_TYPE_EOL) { + + u8 bar_num = ioread8(&r->bar); + u32 bar_offset = ioread32(&r->bar_offset); + u32 count = ioread32(&r->count); + u32 len; + + r++; + + if (bar_num != 0) /* only mapping in BAR0 resources */ + continue; + + switch (type) { + case RES_TYPE_WQ: + case RES_TYPE_RQ: + case RES_TYPE_CQ: + case RES_TYPE_INTR_CTRL: + /* each count is stride bytes long */ + len = count * VNIC_RES_STRIDE; + if (len + bar_offset > bar->len) { + printk(KERN_ERR "vNIC BAR0 resource %d " + "out-of-bounds, offset 0x%x + " + "size 0x%x > bar len 0x%lx\n", + type, bar_offset, + len, + bar->len); + return -EINVAL; + } + break; + case RES_TYPE_INTR_PBA_LEGACY: + case RES_TYPE_DEVCMD: + len = count; + break; + default: + continue; + } + + vdev->res[type].count = count; + vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset; + } + + return 0; +} + +unsigned int vnic_dev_get_res_count(struct vnic_dev *vdev, + enum vnic_res_type type) +{ + return vdev->res[type].count; +} + +void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, + unsigned int index) +{ + if (!vdev->res[type].vaddr) + return NULL; + + switch (type) { + case RES_TYPE_WQ: + case RES_TYPE_RQ: + case RES_TYPE_CQ: + case RES_TYPE_INTR_CTRL: + return (char __iomem *)vdev->res[type].vaddr + + index * VNIC_RES_STRIDE; + default: + return (char __iomem *)vdev->res[type].vaddr; + } +} + +unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring, + unsigned int desc_count, unsigned int desc_size) +{ + /* The base address of the desc rings must be 512 byte aligned. + * Descriptor count is aligned to groups of 32 descriptors. A + * count of 0 means the maximum 4096 descriptors. Descriptor + * size is aligned to 16 bytes. + */ + + unsigned int count_align = 32; + unsigned int desc_align = 16; + + ring->base_align = 512; + + if (desc_count == 0) + desc_count = 4096; + + ring->desc_count = ALIGN(desc_count, count_align); + + ring->desc_size = ALIGN(desc_size, desc_align); + + ring->size = ring->desc_count * ring->desc_size; + ring->size_unaligned = ring->size + ring->base_align; + + return ring->size_unaligned; +} + +void vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring) +{ + memset(ring->descs, 0, ring->size); +} + +int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, + unsigned int desc_count, unsigned int desc_size) +{ + vnic_dev_desc_ring_size(ring, desc_count, desc_size); + + ring->descs_unaligned = pci_alloc_consistent(vdev->pdev, + ring->size_unaligned, + &ring->base_addr_unaligned); + + if (!ring->descs_unaligned) { + printk(KERN_ERR + "Failed to allocate ring (size=%d), aborting\n", + (int)ring->size); + return -ENOMEM; + } + + ring->base_addr = ALIGN(ring->base_addr_unaligned, + ring->base_align); + ring->descs = (u8 *)ring->descs_unaligned + + (ring->base_addr - ring->base_addr_unaligned); + + vnic_dev_clear_desc_ring(ring); + + ring->desc_avail = ring->desc_count - 1; + + return 0; +} + +void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring) +{ + if (ring->descs) { + pci_free_consistent(vdev->pdev, + ring->size_unaligned, + ring->descs_unaligned, + ring->base_addr_unaligned); + ring->descs = NULL; + } +} + +int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + u64 *a0, u64 *a1, int wait) +{ + struct vnic_devcmd __iomem *devcmd = vdev->devcmd; + int delay; + u32 status; + int dev_cmd_err[] = { + /* convert from fw's version of error.h to host's version */ + 0, /* ERR_SUCCESS */ + EINVAL, /* ERR_EINVAL */ + EFAULT, /* ERR_EFAULT */ + EPERM, /* ERR_EPERM */ + EBUSY, /* ERR_EBUSY */ + }; + int err; + + status = ioread32(&devcmd->status); + if (status & STAT_BUSY) { + printk(KERN_ERR "Busy devcmd %d\n", _CMD_N(cmd)); + return -EBUSY; + } + + if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) { + writeq(*a0, &devcmd->args[0]); + writeq(*a1, &devcmd->args[1]); + wmb(); + } + + iowrite32(cmd, &devcmd->cmd); + + if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT)) + return 0; + + for (delay = 0; delay < wait; delay++) { + + udelay(100); + + status = ioread32(&devcmd->status); + if (!(status & STAT_BUSY)) { + + if (status & STAT_ERROR) { + err = dev_cmd_err[(int)readq(&devcmd->args[0])]; + printk(KERN_ERR "Error %d devcmd %d\n", + err, _CMD_N(cmd)); + return -err; + } + + if (_CMD_DIR(cmd) & _CMD_DIR_READ) { + rmb(); + *a0 = readq(&devcmd->args[0]); + *a1 = readq(&devcmd->args[1]); + } + + return 0; + } + } + + printk(KERN_ERR "Timedout devcmd %d\n", _CMD_N(cmd)); + return -ETIMEDOUT; +} + +int vnic_dev_fw_info(struct vnic_dev *vdev, + struct vnic_devcmd_fw_info **fw_info) +{ + u64 a0, a1 = 0; + int wait = 1000; + int err = 0; + + if (!vdev->fw_info) { + vdev->fw_info = pci_alloc_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_fw_info), + &vdev->fw_info_pa); + if (!vdev->fw_info) + return -ENOMEM; + + a0 = vdev->fw_info_pa; + + /* only get fw_info once and cache it */ + err = vnic_dev_cmd(vdev, CMD_MCPU_FW_INFO, &a0, &a1, wait); + } + + *fw_info = vdev->fw_info; + + return err; +} + +int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, + void *value) +{ + u64 a0, a1; + int wait = 1000; + int err; + + a0 = offset; + a1 = size; + + err = vnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait); + + switch (size) { + case 1: *(u8 *)value = (u8)a0; break; + case 2: *(u16 *)value = (u16)a0; break; + case 4: *(u32 *)value = (u32)a0; break; + case 8: *(u64 *)value = a0; break; + default: BUG(); break; + } + + return err; +} + +int vnic_dev_stats_clear(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait); +} + +int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats) +{ + u64 a0, a1; + int wait = 1000; + + if (!vdev->stats) { + vdev->stats = pci_alloc_consistent(vdev->pdev, + sizeof(struct vnic_stats), &vdev->stats_pa); + if (!vdev->stats) + return -ENOMEM; + } + + *stats = vdev->stats; + a0 = vdev->stats_pa; + a1 = sizeof(struct vnic_stats); + + return vnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait); +} + +int vnic_dev_close(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait); +} + +int vnic_dev_enable(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait); +} + +int vnic_dev_disable(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait); +} + +int vnic_dev_open(struct vnic_dev *vdev, int arg) +{ + u64 a0 = (u32)arg, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait); +} + +int vnic_dev_open_done(struct vnic_dev *vdev, int *done) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + int err; + + *done = 0; + + err = vnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait); + if (err) + return err; + + *done = (a0 == 0); + + return 0; +} + +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) +{ + u64 a0 = (u32)arg, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait); +} + +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + int err; + + *done = 0; + + err = vnic_dev_cmd(vdev, CMD_SOFT_RESET_STATUS, &a0, &a1, wait); + if (err) + return err; + + *done = (a0 == 0); + + return 0; +} + +int vnic_dev_hang_notify(struct vnic_dev *vdev) +{ + u64 a0, a1; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_HANG_NOTIFY, &a0, &a1, wait); +} + +int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr) +{ + u64 a0, a1; + int wait = 1000; + int err, i; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = 0; + + err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait); + if (err) + return err; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = ((u8 *)&a0)[i]; + + return 0; +} + +void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, + int broadcast, int promisc, int allmulti) +{ + u64 a0, a1 = 0; + int wait = 1000; + int err; + + a0 = (directed ? CMD_PFILTER_DIRECTED : 0) | + (multicast ? CMD_PFILTER_MULTICAST : 0) | + (broadcast ? CMD_PFILTER_BROADCAST : 0) | + (promisc ? CMD_PFILTER_PROMISCUOUS : 0) | + (allmulti ? CMD_PFILTER_ALL_MULTICAST : 0); + + err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait); + if (err) + printk(KERN_ERR "Can't set packet filter\n"); +} + +void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + int err; + int i; + + for (i = 0; i < ETH_ALEN; i++) + ((u8 *)&a0)[i] = addr[i]; + + err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait); + if (err) + printk(KERN_ERR + "Can't add addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], + err); +} + +void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + int err; + int i; + + for (i = 0; i < ETH_ALEN; i++) + ((u8 *)&a0)[i] = addr[i]; + + err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait); + if (err) + printk(KERN_ERR + "Can't del addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], + err); +} + +int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr) +{ + u64 a0, a1; + int wait = 1000; + + if (!vdev->notify) { + vdev->notify = pci_alloc_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_notify), + &vdev->notify_pa); + if (!vdev->notify) + return -ENOMEM; + } + + a0 = vdev->notify_pa; + a1 = ((u64)intr << 32) & 0x0000ffff00000000ULL; + a1 += sizeof(struct vnic_devcmd_notify); + + return vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); +} + +void vnic_dev_notify_unset(struct vnic_dev *vdev) +{ + u64 a0, a1; + int wait = 1000; + + a0 = 0; /* paddr = 0 to unset notify buffer */ + a1 = 0x0000ffff00000000ULL; /* intr num = -1 to unreg for intr */ + a1 += sizeof(struct vnic_devcmd_notify); + + vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); +} + +static int vnic_dev_notify_ready(struct vnic_dev *vdev) +{ + u32 *words; + unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4; + unsigned int i; + u32 csum; + + if (!vdev->notify) + return 0; + + do { + csum = 0; + memcpy(&vdev->notify_copy, vdev->notify, + sizeof(struct vnic_devcmd_notify)); + words = (u32 *)&vdev->notify_copy; + for (i = 1; i < nwords; i++) + csum += words[i]; + } while (csum != words[0]); + + return 1; +} + +int vnic_dev_init(struct vnic_dev *vdev, int arg) +{ + u64 a0 = (u32)arg, a1 = 0; + int wait = 1000; + return vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait); +} + +int vnic_dev_link_status(struct vnic_dev *vdev) +{ + if (vdev->linkstatus) + return *vdev->linkstatus; + + if (!vnic_dev_notify_ready(vdev)) + return 0; + + return vdev->notify_copy.link_state; +} + +u32 vnic_dev_port_speed(struct vnic_dev *vdev) +{ + if (!vnic_dev_notify_ready(vdev)) + return 0; + + return vdev->notify_copy.port_speed; +} + +u32 vnic_dev_msg_lvl(struct vnic_dev *vdev) +{ + if (!vnic_dev_notify_ready(vdev)) + return 0; + + return vdev->notify_copy.msglvl; +} + +u32 vnic_dev_mtu(struct vnic_dev *vdev) +{ + if (!vnic_dev_notify_ready(vdev)) + return 0; + + return vdev->notify_copy.mtu; +} + +void vnic_dev_set_intr_mode(struct vnic_dev *vdev, + enum vnic_dev_intr_mode intr_mode) +{ + vdev->intr_mode = intr_mode; +} + +enum vnic_dev_intr_mode vnic_dev_get_intr_mode( + struct vnic_dev *vdev) +{ + return vdev->intr_mode; +} + +void vnic_dev_unregister(struct vnic_dev *vdev) +{ + if (vdev) { + if (vdev->notify) + pci_free_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_notify), + vdev->notify, + vdev->notify_pa); + if (vdev->linkstatus) + pci_free_consistent(vdev->pdev, + sizeof(u32), + vdev->linkstatus, + vdev->linkstatus_pa); + if (vdev->stats) + pci_free_consistent(vdev->pdev, + sizeof(struct vnic_dev), + vdev->stats, vdev->stats_pa); + if (vdev->fw_info) + pci_free_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_fw_info), + vdev->fw_info, vdev->fw_info_pa); + kfree(vdev); + } +} + +struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev, + void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar) +{ + if (!vdev) { + vdev = kzalloc(sizeof(struct vnic_dev), GFP_ATOMIC); + if (!vdev) + return NULL; + } + + vdev->priv = priv; + vdev->pdev = pdev; + + if (vnic_dev_discover_res(vdev, bar)) + goto err_out; + + vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0); + if (!vdev->devcmd) + goto err_out; + + return vdev; + +err_out: + vnic_dev_unregister(vdev); + return NULL; +} + diff --git a/drivers/net/enic/vnic_dev.h b/drivers/net/enic/vnic_dev.h new file mode 100644 index 000000000000..2dcffd3a24bd --- /dev/null +++ b/drivers/net/enic/vnic_dev.h @@ -0,0 +1,106 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_DEV_H_ +#define _VNIC_DEV_H_ + +#include "vnic_resource.h" +#include "vnic_devcmd.h" + +#ifndef VNIC_PADDR_TARGET +#define VNIC_PADDR_TARGET 0x0000000000000000ULL +#endif + +enum vnic_dev_intr_mode { + VNIC_DEV_INTR_MODE_UNKNOWN, + VNIC_DEV_INTR_MODE_INTX, + VNIC_DEV_INTR_MODE_MSI, + VNIC_DEV_INTR_MODE_MSIX, +}; + +struct vnic_dev_bar { + void __iomem *vaddr; + dma_addr_t bus_addr; + unsigned long len; +}; + +struct vnic_dev_ring { + void *descs; + size_t size; + dma_addr_t base_addr; + size_t base_align; + void *descs_unaligned; + size_t size_unaligned; + dma_addr_t base_addr_unaligned; + unsigned int desc_size; + unsigned int desc_count; + unsigned int desc_avail; +}; + +struct vnic_dev; +struct vnic_stats; + +void *vnic_dev_priv(struct vnic_dev *vdev); +unsigned int vnic_dev_get_res_count(struct vnic_dev *vdev, + enum vnic_res_type type); +void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, + unsigned int index); +unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring, + unsigned int desc_count, unsigned int desc_size); +void vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring); +int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, + unsigned int desc_count, unsigned int desc_size); +void vnic_dev_free_desc_ring(struct vnic_dev *vdev, + struct vnic_dev_ring *ring); +int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + u64 *a0, u64 *a1, int wait); +int vnic_dev_fw_info(struct vnic_dev *vdev, + struct vnic_devcmd_fw_info **fw_info); +int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, + void *value); +int vnic_dev_stats_clear(struct vnic_dev *vdev); +int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats); +int vnic_dev_hang_notify(struct vnic_dev *vdev); +void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, + int broadcast, int promisc, int allmulti); +void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr); +void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr); +int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr); +int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr); +void vnic_dev_notify_unset(struct vnic_dev *vdev); +int vnic_dev_link_status(struct vnic_dev *vdev); +u32 vnic_dev_port_speed(struct vnic_dev *vdev); +u32 vnic_dev_msg_lvl(struct vnic_dev *vdev); +u32 vnic_dev_mtu(struct vnic_dev *vdev); +int vnic_dev_close(struct vnic_dev *vdev); +int vnic_dev_enable(struct vnic_dev *vdev); +int vnic_dev_disable(struct vnic_dev *vdev); +int vnic_dev_open(struct vnic_dev *vdev, int arg); +int vnic_dev_open_done(struct vnic_dev *vdev, int *done); +int vnic_dev_init(struct vnic_dev *vdev, int arg); +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg); +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done); +void vnic_dev_set_intr_mode(struct vnic_dev *vdev, + enum vnic_dev_intr_mode intr_mode); +enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev); +void vnic_dev_unregister(struct vnic_dev *vdev); +struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev, + void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar); + +#endif /* _VNIC_DEV_H_ */ diff --git a/drivers/net/enic/vnic_devcmd.h b/drivers/net/enic/vnic_devcmd.h new file mode 100644 index 000000000000..d8617a3373b1 --- /dev/null +++ b/drivers/net/enic/vnic_devcmd.h @@ -0,0 +1,282 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_DEVCMD_H_ +#define _VNIC_DEVCMD_H_ + +#define _CMD_NBITS 14 +#define _CMD_VTYPEBITS 10 +#define _CMD_FLAGSBITS 6 +#define _CMD_DIRBITS 2 + +#define _CMD_NMASK ((1 << _CMD_NBITS)-1) +#define _CMD_VTYPEMASK ((1 << _CMD_VTYPEBITS)-1) +#define _CMD_FLAGSMASK ((1 << _CMD_FLAGSBITS)-1) +#define _CMD_DIRMASK ((1 << _CMD_DIRBITS)-1) + +#define _CMD_NSHIFT 0 +#define _CMD_VTYPESHIFT (_CMD_NSHIFT+_CMD_NBITS) +#define _CMD_FLAGSSHIFT (_CMD_VTYPESHIFT+_CMD_VTYPEBITS) +#define _CMD_DIRSHIFT (_CMD_FLAGSSHIFT+_CMD_FLAGSBITS) + +/* + * Direction bits (from host perspective). + */ +#define _CMD_DIR_NONE 0U +#define _CMD_DIR_WRITE 1U +#define _CMD_DIR_READ 2U +#define _CMD_DIR_RW (_CMD_DIR_WRITE | _CMD_DIR_READ) + +/* + * Flag bits. + */ +#define _CMD_FLAGS_NONE 0U +#define _CMD_FLAGS_NOWAIT 1U + +/* + * vNIC type bits. + */ +#define _CMD_VTYPE_NONE 0U +#define _CMD_VTYPE_ENET 1U +#define _CMD_VTYPE_FC 2U +#define _CMD_VTYPE_SCSI 4U +#define _CMD_VTYPE_ALL (_CMD_VTYPE_ENET | _CMD_VTYPE_FC | _CMD_VTYPE_SCSI) + +/* + * Used to create cmds.. +*/ +#define _CMDCF(dir, flags, vtype, nr) \ + (((dir) << _CMD_DIRSHIFT) | \ + ((flags) << _CMD_FLAGSSHIFT) | \ + ((vtype) << _CMD_VTYPESHIFT) | \ + ((nr) << _CMD_NSHIFT)) +#define _CMDC(dir, vtype, nr) _CMDCF(dir, 0, vtype, nr) +#define _CMDCNW(dir, vtype, nr) _CMDCF(dir, _CMD_FLAGS_NOWAIT, vtype, nr) + +/* + * Used to decode cmds.. +*/ +#define _CMD_DIR(cmd) (((cmd) >> _CMD_DIRSHIFT) & _CMD_DIRMASK) +#define _CMD_FLAGS(cmd) (((cmd) >> _CMD_FLAGSSHIFT) & _CMD_FLAGSMASK) +#define _CMD_VTYPE(cmd) (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK) +#define _CMD_N(cmd) (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK) + +enum vnic_devcmd_cmd { + CMD_NONE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0), + + /* mcpu fw info in mem: (u64)a0=paddr to struct vnic_devcmd_fw_info */ + CMD_MCPU_FW_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 1), + + /* dev-specific block member: + * in: (u16)a0=offset,(u8)a1=size + * out: a0=value */ + CMD_DEV_SPEC = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 2), + + /* stats clear */ + CMD_STATS_CLEAR = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 3), + + /* stats dump in mem: (u64)a0=paddr to stats area, + * (u16)a1=sizeof stats area */ + CMD_STATS_DUMP = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 4), + + /* set Rx packet filter: (u32)a0=filters (see CMD_PFILTER_*) */ + CMD_PACKET_FILTER = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 7), + + /* hang detection notification */ + CMD_HANG_NOTIFY = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 8), + + /* MAC address in (u48)a0 */ + CMD_MAC_ADDR = _CMDC(_CMD_DIR_READ, + _CMD_VTYPE_ENET | _CMD_VTYPE_FC, 9), + + /* disable/enable promisc mode: (u8)a0=0/1 */ +/***** XXX DEPRECATED *****/ + CMD_PROMISC_MODE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 10), + + /* disable/enable all-multi mode: (u8)a0=0/1 */ +/***** XXX DEPRECATED *****/ + CMD_ALLMULTI_MODE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 11), + + /* add addr from (u48)a0 */ + CMD_ADDR_ADD = _CMDCNW(_CMD_DIR_WRITE, + _CMD_VTYPE_ENET | _CMD_VTYPE_FC, 12), + + /* del addr from (u48)a0 */ + CMD_ADDR_DEL = _CMDCNW(_CMD_DIR_WRITE, + _CMD_VTYPE_ENET | _CMD_VTYPE_FC, 13), + + /* add VLAN id in (u16)a0 */ + CMD_VLAN_ADD = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 14), + + /* del VLAN id in (u16)a0 */ + CMD_VLAN_DEL = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 15), + + /* nic_cfg in (u32)a0 */ + CMD_NIC_CFG = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 16), + + /* union vnic_rss_key in mem: (u64)a0=paddr, (u16)a1=len */ + CMD_RSS_KEY = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 17), + + /* union vnic_rss_cpu in mem: (u64)a0=paddr, (u16)a1=len */ + CMD_RSS_CPU = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 18), + + /* initiate softreset */ + CMD_SOFT_RESET = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 19), + + /* softreset status: + * out: a0=0 reset complete, a0=1 reset in progress */ + CMD_SOFT_RESET_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 20), + + /* set struct vnic_devcmd_notify buffer in mem: + * in: + * (u64)a0=paddr to notify (set paddr=0 to unset) + * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify) + * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr) + * out: + * (u32)a1 = effective size + */ + CMD_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 21), + + /* UNDI API: (u64)a0=paddr to s_PXENV_UNDI_ struct, + * (u8)a1=PXENV_UNDI_xxx */ + CMD_UNDI = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 22), + + /* initiate open sequence (u32)a0=flags (see CMD_OPENF_*) */ + CMD_OPEN = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 23), + + /* open status: + * out: a0=0 open complete, a0=1 open in progress */ + CMD_OPEN_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 24), + + /* close vnic */ + CMD_CLOSE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 25), + + /* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */ + CMD_INIT = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26), + + /* variant of CMD_INIT, with provisioning info + * (u64)a0=paddr of vnic_devcmd_provinfo + * (u32)a1=sizeof provision info */ + CMD_INIT_PROV_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 27), + + /* enable virtual link */ + CMD_ENABLE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28), + + /* disable virtual link */ + CMD_DISABLE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 29), + + /* stats dump all vnics on uplink in mem: (u64)a0=paddr (u32)a1=uif */ + CMD_STATS_DUMP_ALL = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 30), + + /* init status: + * out: a0=0 init complete, a0=1 init in progress + * if a0=0, a1=errno */ + CMD_INIT_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 31), + + /* INT13 API: (u64)a0=paddr to vnic_int13_params struct + * (u8)a1=INT13_CMD_xxx */ + CMD_INT13 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_FC, 32), + + /* logical uplink enable/disable: (u64)a0: 0/1=disable/enable */ + CMD_LOGICAL_UPLINK = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 33), + + /* undo initialize of virtual link */ + CMD_DEINIT = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34), +}; + +/* flags for CMD_OPEN */ +#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */ + +/* flags for CMD_INIT */ +#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */ + +/* flags for CMD_PACKET_FILTER */ +#define CMD_PFILTER_DIRECTED 0x01 +#define CMD_PFILTER_MULTICAST 0x02 +#define CMD_PFILTER_BROADCAST 0x04 +#define CMD_PFILTER_PROMISCUOUS 0x08 +#define CMD_PFILTER_ALL_MULTICAST 0x10 + +enum vnic_devcmd_status { + STAT_NONE = 0, + STAT_BUSY = 1 << 0, /* cmd in progress */ + STAT_ERROR = 1 << 1, /* last cmd caused error (code in a0) */ +}; + +enum vnic_devcmd_error { + ERR_SUCCESS = 0, + ERR_EINVAL = 1, + ERR_EFAULT = 2, + ERR_EPERM = 3, + ERR_EBUSY = 4, + ERR_ECMDUNKNOWN = 5, + ERR_EBADSTATE = 6, + ERR_ENOMEM = 7, + ERR_ETIMEDOUT = 8, + ERR_ELINKDOWN = 9, +}; + +struct vnic_devcmd_fw_info { + char fw_version[32]; + char fw_build[32]; + char hw_version[32]; + char hw_serial_number[32]; +}; + +struct vnic_devcmd_notify { + u32 csum; /* checksum over following words */ + + u32 link_state; /* link up == 1 */ + u32 port_speed; /* effective port speed (rate limit) */ + u32 mtu; /* MTU */ + u32 msglvl; /* requested driver msg lvl */ + u32 uif; /* uplink interface */ + u32 status; /* status bits (see VNIC_STF_*) */ + u32 error; /* error code (see ERR_*) for first ERR */ +}; +#define VNIC_STF_FATAL_ERR 0x0001 /* fatal fw error */ + +struct vnic_devcmd_provinfo { + u8 oui[3]; + u8 type; + u8 data[0]; +}; + +/* + * Writing cmd register causes STAT_BUSY to get set in status register. + * When cmd completes, STAT_BUSY will be cleared. + * + * If cmd completed successfully STAT_ERROR will be clear + * and args registers contain cmd-specific results. + * + * If cmd error, STAT_ERROR will be set and args[0] contains error code. + * + * status register is read-only. While STAT_BUSY is set, + * all other register contents are read-only. + */ + +/* Make sizeof(vnic_devcmd) a power-of-2 for I/O BAR. */ +#define VNIC_DEVCMD_NARGS 15 +struct vnic_devcmd { + u32 status; /* RO */ + u32 cmd; /* RW */ + u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */ +}; + +#endif /* _VNIC_DEVCMD_H_ */ diff --git a/drivers/net/enic/vnic_enet.h b/drivers/net/enic/vnic_enet.h new file mode 100644 index 000000000000..6332ac9391b8 --- /dev/null +++ b/drivers/net/enic/vnic_enet.h @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_ENIC_H_ +#define _VNIC_ENIC_H_ + +/* Device-specific region: enet configuration */ +struct vnic_enet_config { + u32 flags; + u32 wq_desc_count; + u32 rq_desc_count; + u16 mtu; + u16 intr_timer; + u8 intr_timer_type; + u8 intr_mode; + char devname[16]; +}; + +#define VENETF_TSO 0x1 /* TSO enabled */ +#define VENETF_LRO 0x2 /* LRO enabled */ +#define VENETF_RXCSUM 0x4 /* RX csum enabled */ +#define VENETF_TXCSUM 0x8 /* TX csum enabled */ +#define VENETF_RSS 0x10 /* RSS enabled */ +#define VENETF_RSSHASH_IPV4 0x20 /* Hash on IPv4 fields */ +#define VENETF_RSSHASH_TCPIPV4 0x40 /* Hash on TCP + IPv4 fields */ +#define VENETF_RSSHASH_IPV6 0x80 /* Hash on IPv6 fields */ +#define VENETF_RSSHASH_TCPIPV6 0x100 /* Hash on TCP + IPv6 fields */ +#define VENETF_RSSHASH_IPV6_EX 0x200 /* Hash on IPv6 extended fields */ +#define VENETF_RSSHASH_TCPIPV6_EX 0x400 /* Hash on TCP + IPv6 ext. fields */ + +#endif /* _VNIC_ENIC_H_ */ diff --git a/drivers/net/enic/vnic_intr.c b/drivers/net/enic/vnic_intr.c new file mode 100644 index 000000000000..ddc38f8f4656 --- /dev/null +++ b/drivers/net/enic/vnic_intr.c @@ -0,0 +1,62 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "vnic_dev.h" +#include "vnic_intr.h" + +void vnic_intr_free(struct vnic_intr *intr) +{ + intr->ctrl = NULL; +} + +int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr, + unsigned int index) +{ + intr->index = index; + intr->vdev = vdev; + + intr->ctrl = vnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index); + if (!intr->ctrl) { + printk(KERN_ERR "Failed to hook INTR[%d].ctrl resource\n", + index); + return -EINVAL; + } + + return 0; +} + +void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer, + unsigned int coalescing_type, unsigned int mask_on_assertion) +{ + iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer); + iowrite32(coalescing_type, &intr->ctrl->coalescing_type); + iowrite32(mask_on_assertion, &intr->ctrl->mask_on_assertion); + iowrite32(0, &intr->ctrl->int_credits); +} + +void vnic_intr_clean(struct vnic_intr *intr) +{ + iowrite32(0, &intr->ctrl->int_credits); +} diff --git a/drivers/net/enic/vnic_intr.h b/drivers/net/enic/vnic_intr.h new file mode 100644 index 000000000000..ccc408116af8 --- /dev/null +++ b/drivers/net/enic/vnic_intr.h @@ -0,0 +1,92 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_INTR_H_ +#define _VNIC_INTR_H_ + +#include + +#include "vnic_dev.h" + +#define VNIC_INTR_TIMER_MAX 0xffff + +#define VNIC_INTR_TIMER_TYPE_ABS 0 +#define VNIC_INTR_TIMER_TYPE_QUIET 1 + +/* Interrupt control */ +struct vnic_intr_ctrl { + u32 coalescing_timer; /* 0x00 */ + u32 pad0; + u32 coalescing_value; /* 0x08 */ + u32 pad1; + u32 coalescing_type; /* 0x10 */ + u32 pad2; + u32 mask_on_assertion; /* 0x18 */ + u32 pad3; + u32 mask; /* 0x20 */ + u32 pad4; + u32 int_credits; /* 0x28 */ + u32 pad5; + u32 int_credit_return; /* 0x30 */ + u32 pad6; +}; + +struct vnic_intr { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_intr_ctrl __iomem *ctrl; /* memory-mapped */ +}; + +static inline void vnic_intr_unmask(struct vnic_intr *intr) +{ + iowrite32(0, &intr->ctrl->mask); +} + +static inline void vnic_intr_mask(struct vnic_intr *intr) +{ + iowrite32(1, &intr->ctrl->mask); +} + +static inline void vnic_intr_return_credits(struct vnic_intr *intr, + unsigned int credits, int unmask, int reset_timer) +{ +#define VNIC_INTR_UNMASK_SHIFT 16 +#define VNIC_INTR_RESET_TIMER_SHIFT 17 + + u32 int_credit_return = (credits & 0xffff) | + (unmask ? (1 << VNIC_INTR_UNMASK_SHIFT) : 0) | + (reset_timer ? (1 << VNIC_INTR_RESET_TIMER_SHIFT) : 0); + + iowrite32(int_credit_return, &intr->ctrl->int_credit_return); +} + +static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba) +{ + /* get and ack interrupt in one read (clear-and-ack-on-read) */ + return ioread32(legacy_pba); +} + +void vnic_intr_free(struct vnic_intr *intr); +int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr, + unsigned int index); +void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer, + unsigned int coalescing_type, unsigned int mask_on_assertion); +void vnic_intr_clean(struct vnic_intr *intr); + +#endif /* _VNIC_INTR_H_ */ diff --git a/drivers/net/enic/vnic_nic.h b/drivers/net/enic/vnic_nic.h new file mode 100644 index 000000000000..dadf26fae69a --- /dev/null +++ b/drivers/net/enic/vnic_nic.h @@ -0,0 +1,65 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_NIC_H_ +#define _VNIC_NIC_H_ + +#define NIC_CFG_RSS_DEFAULT_CPU_MASK_FIELD 0xffUL +#define NIC_CFG_RSS_DEFAULT_CPU_SHIFT 0 +#define NIC_CFG_RSS_HASH_TYPE (0xffUL << 8) +#define NIC_CFG_RSS_HASH_TYPE_MASK_FIELD 0xffUL +#define NIC_CFG_RSS_HASH_TYPE_SHIFT 8 +#define NIC_CFG_RSS_HASH_BITS (7UL << 16) +#define NIC_CFG_RSS_HASH_BITS_MASK_FIELD 7UL +#define NIC_CFG_RSS_HASH_BITS_SHIFT 16 +#define NIC_CFG_RSS_BASE_CPU (7UL << 19) +#define NIC_CFG_RSS_BASE_CPU_MASK_FIELD 7UL +#define NIC_CFG_RSS_BASE_CPU_SHIFT 19 +#define NIC_CFG_RSS_ENABLE (1UL << 22) +#define NIC_CFG_RSS_ENABLE_MASK_FIELD 1UL +#define NIC_CFG_RSS_ENABLE_SHIFT 22 +#define NIC_CFG_TSO_IPID_SPLIT_EN (1UL << 23) +#define NIC_CFG_TSO_IPID_SPLIT_EN_MASK_FIELD 1UL +#define NIC_CFG_TSO_IPID_SPLIT_EN_SHIFT 23 +#define NIC_CFG_IG_VLAN_STRIP_EN (1UL << 24) +#define NIC_CFG_IG_VLAN_STRIP_EN_MASK_FIELD 1UL +#define NIC_CFG_IG_VLAN_STRIP_EN_SHIFT 24 + +static inline void vnic_set_nic_cfg(u32 *nic_cfg, + u8 rss_default_cpu, u8 rss_hash_type, + u8 rss_hash_bits, u8 rss_base_cpu, + u8 rss_enable, u8 tso_ipid_split_en, + u8 ig_vlan_strip_en) +{ + *nic_cfg = (rss_default_cpu & NIC_CFG_RSS_DEFAULT_CPU_MASK_FIELD) | + ((rss_hash_type & NIC_CFG_RSS_HASH_TYPE_MASK_FIELD) + << NIC_CFG_RSS_HASH_TYPE_SHIFT) | + ((rss_hash_bits & NIC_CFG_RSS_HASH_BITS_MASK_FIELD) + << NIC_CFG_RSS_HASH_BITS_SHIFT) | + ((rss_base_cpu & NIC_CFG_RSS_BASE_CPU_MASK_FIELD) + << NIC_CFG_RSS_BASE_CPU_SHIFT) | + ((rss_enable & NIC_CFG_RSS_ENABLE_MASK_FIELD) + << NIC_CFG_RSS_ENABLE_SHIFT) | + ((tso_ipid_split_en & NIC_CFG_TSO_IPID_SPLIT_EN_MASK_FIELD) + << NIC_CFG_TSO_IPID_SPLIT_EN_SHIFT) | + ((ig_vlan_strip_en & NIC_CFG_IG_VLAN_STRIP_EN_MASK_FIELD) + << NIC_CFG_IG_VLAN_STRIP_EN_SHIFT); +} + +#endif /* _VNIC_NIC_H_ */ diff --git a/drivers/net/enic/vnic_resource.h b/drivers/net/enic/vnic_resource.h new file mode 100644 index 000000000000..144d2812f081 --- /dev/null +++ b/drivers/net/enic/vnic_resource.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_RESOURCE_H_ +#define _VNIC_RESOURCE_H_ + +#define VNIC_RES_MAGIC 0x766E6963L /* 'vnic' */ +#define VNIC_RES_VERSION 0x00000000L + +/* vNIC resource types */ +enum vnic_res_type { + RES_TYPE_EOL, /* End-of-list */ + RES_TYPE_WQ, /* Work queues */ + RES_TYPE_RQ, /* Receive queues */ + RES_TYPE_CQ, /* Completion queues */ + RES_TYPE_RSVD1, + RES_TYPE_NIC_CFG, /* Enet NIC config registers */ + RES_TYPE_RSVD2, + RES_TYPE_RSVD3, + RES_TYPE_RSVD4, + RES_TYPE_RSVD5, + RES_TYPE_INTR_CTRL, /* Interrupt ctrl table */ + RES_TYPE_INTR_TABLE, /* MSI/MSI-X Interrupt table */ + RES_TYPE_INTR_PBA, /* MSI/MSI-X PBA table */ + RES_TYPE_INTR_PBA_LEGACY, /* Legacy intr status, r2c */ + RES_TYPE_RSVD6, + RES_TYPE_RSVD7, + RES_TYPE_DEVCMD, /* Device command region */ + RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */ + + RES_TYPE_MAX, /* Count of resource types */ +}; + +struct vnic_resource_header { + u32 magic; + u32 version; +}; + +struct vnic_resource { + u8 type; + u8 bar; + u8 pad[2]; + u32 bar_offset; + u32 count; +}; + +#endif /* _VNIC_RESOURCE_H_ */ diff --git a/drivers/net/enic/vnic_rq.c b/drivers/net/enic/vnic_rq.c new file mode 100644 index 000000000000..9365e63e821a --- /dev/null +++ b/drivers/net/enic/vnic_rq.c @@ -0,0 +1,199 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "vnic_dev.h" +#include "vnic_rq.h" + +static int vnic_rq_alloc_bufs(struct vnic_rq *rq) +{ + struct vnic_rq_buf *buf; + struct vnic_dev *vdev; + unsigned int i, j, count = rq->ring.desc_count; + unsigned int blks = VNIC_RQ_BUF_BLKS_NEEDED(count); + + vdev = rq->vdev; + + for (i = 0; i < blks; i++) { + rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ, GFP_ATOMIC); + if (!rq->bufs[i]) { + printk(KERN_ERR "Failed to alloc rq_bufs\n"); + return -ENOMEM; + } + } + + for (i = 0; i < blks; i++) { + buf = rq->bufs[i]; + for (j = 0; j < VNIC_RQ_BUF_BLK_ENTRIES; j++) { + buf->index = i * VNIC_RQ_BUF_BLK_ENTRIES + j; + buf->desc = (u8 *)rq->ring.descs + + rq->ring.desc_size * buf->index; + if (buf->index + 1 == count) { + buf->next = rq->bufs[0]; + break; + } else if (j + 1 == VNIC_RQ_BUF_BLK_ENTRIES) { + buf->next = rq->bufs[i + 1]; + } else { + buf->next = buf + 1; + buf++; + } + } + } + + rq->to_use = rq->to_clean = rq->bufs[0]; + rq->buf_index = 0; + + return 0; +} + +void vnic_rq_free(struct vnic_rq *rq) +{ + struct vnic_dev *vdev; + unsigned int i; + + vdev = rq->vdev; + + vnic_dev_free_desc_ring(vdev, &rq->ring); + + for (i = 0; i < VNIC_RQ_BUF_BLKS_MAX; i++) { + kfree(rq->bufs[i]); + rq->bufs[i] = NULL; + } + + rq->ctrl = NULL; +} + +int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index, + unsigned int desc_count, unsigned int desc_size) +{ + int err; + + rq->index = index; + rq->vdev = vdev; + + rq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_RQ, index); + if (!rq->ctrl) { + printk(KERN_ERR "Failed to hook RQ[%d] resource\n", index); + return -EINVAL; + } + + vnic_rq_disable(rq); + + err = vnic_dev_alloc_desc_ring(vdev, &rq->ring, desc_count, desc_size); + if (err) + return err; + + err = vnic_rq_alloc_bufs(rq); + if (err) { + vnic_rq_free(rq); + return err; + } + + return 0; +} + +void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset) +{ + u64 paddr; + u32 fetch_index; + + paddr = (u64)rq->ring.base_addr | VNIC_PADDR_TARGET; + writeq(paddr, &rq->ctrl->ring_base); + iowrite32(rq->ring.desc_count, &rq->ctrl->ring_size); + iowrite32(cq_index, &rq->ctrl->cq_index); + iowrite32(error_interrupt_enable, &rq->ctrl->error_interrupt_enable); + iowrite32(error_interrupt_offset, &rq->ctrl->error_interrupt_offset); + iowrite32(0, &rq->ctrl->dropped_packet_count); + iowrite32(0, &rq->ctrl->error_status); + + /* Use current fetch_index as the ring starting point */ + fetch_index = ioread32(&rq->ctrl->fetch_index); + rq->to_use = rq->to_clean = + &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES] + [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES]; + iowrite32(fetch_index, &rq->ctrl->posted_index); + + rq->buf_index = 0; +} + +unsigned int vnic_rq_error_status(struct vnic_rq *rq) +{ + return ioread32(&rq->ctrl->error_status); +} + +void vnic_rq_enable(struct vnic_rq *rq) +{ + iowrite32(1, &rq->ctrl->enable); +} + +int vnic_rq_disable(struct vnic_rq *rq) +{ + unsigned int wait; + + iowrite32(0, &rq->ctrl->enable); + + /* Wait for HW to ACK disable request */ + for (wait = 0; wait < 100; wait++) { + if (!(ioread32(&rq->ctrl->running))) + return 0; + udelay(1); + } + + printk(KERN_ERR "Failed to disable RQ[%d]\n", rq->index); + + return -ETIMEDOUT; +} + +void vnic_rq_clean(struct vnic_rq *rq, + void (*buf_clean)(struct vnic_rq *rq, struct vnic_rq_buf *buf)) +{ + struct vnic_rq_buf *buf; + u32 fetch_index; + + BUG_ON(ioread32(&rq->ctrl->enable)); + + buf = rq->to_clean; + + while (vnic_rq_desc_used(rq) > 0) { + + (*buf_clean)(rq, buf); + + buf = rq->to_clean = buf->next; + rq->ring.desc_avail++; + } + + /* Use current fetch_index as the ring starting point */ + fetch_index = ioread32(&rq->ctrl->fetch_index); + rq->to_use = rq->to_clean = + &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES] + [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES]; + iowrite32(fetch_index, &rq->ctrl->posted_index); + + rq->buf_index = 0; + + vnic_dev_clear_desc_ring(&rq->ring); +} + diff --git a/drivers/net/enic/vnic_rq.h b/drivers/net/enic/vnic_rq.h new file mode 100644 index 000000000000..82bfca67cc4d --- /dev/null +++ b/drivers/net/enic/vnic_rq.h @@ -0,0 +1,204 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_RQ_H_ +#define _VNIC_RQ_H_ + +#include + +#include "vnic_dev.h" +#include "vnic_cq.h" + +/* Receive queue control */ +struct vnic_rq_ctrl { + u64 ring_base; /* 0x00 */ + u32 ring_size; /* 0x08 */ + u32 pad0; + u32 posted_index; /* 0x10 */ + u32 pad1; + u32 cq_index; /* 0x18 */ + u32 pad2; + u32 enable; /* 0x20 */ + u32 pad3; + u32 running; /* 0x28 */ + u32 pad4; + u32 fetch_index; /* 0x30 */ + u32 pad5; + u32 error_interrupt_enable; /* 0x38 */ + u32 pad6; + u32 error_interrupt_offset; /* 0x40 */ + u32 pad7; + u32 error_status; /* 0x48 */ + u32 pad8; + u32 dropped_packet_count; /* 0x50 */ + u32 pad9; + u32 dropped_packet_count_rc; /* 0x58 */ + u32 pad10; +}; + +/* Break the vnic_rq_buf allocations into blocks of 64 entries */ +#define VNIC_RQ_BUF_BLK_ENTRIES 64 +#define VNIC_RQ_BUF_BLK_SZ \ + (VNIC_RQ_BUF_BLK_ENTRIES * sizeof(struct vnic_rq_buf)) +#define VNIC_RQ_BUF_BLKS_NEEDED(entries) \ + DIV_ROUND_UP(entries, VNIC_RQ_BUF_BLK_ENTRIES) +#define VNIC_RQ_BUF_BLKS_MAX VNIC_RQ_BUF_BLKS_NEEDED(4096) + +struct vnic_rq_buf { + struct vnic_rq_buf *next; + dma_addr_t dma_addr; + void *os_buf; + unsigned int os_buf_index; + unsigned int len; + unsigned int index; + void *desc; +}; + +struct vnic_rq { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_rq_ctrl __iomem *ctrl; /* memory-mapped */ + struct vnic_dev_ring ring; + struct vnic_rq_buf *bufs[VNIC_RQ_BUF_BLKS_MAX]; + struct vnic_rq_buf *to_use; + struct vnic_rq_buf *to_clean; + void *os_buf_head; + unsigned int buf_index; + unsigned int pkts_outstanding; +}; + +static inline unsigned int vnic_rq_desc_avail(struct vnic_rq *rq) +{ + /* how many does SW own? */ + return rq->ring.desc_avail; +} + +static inline unsigned int vnic_rq_desc_used(struct vnic_rq *rq) +{ + /* how many does HW own? */ + return rq->ring.desc_count - rq->ring.desc_avail - 1; +} + +static inline void *vnic_rq_next_desc(struct vnic_rq *rq) +{ + return rq->to_use->desc; +} + +static inline unsigned int vnic_rq_next_index(struct vnic_rq *rq) +{ + return rq->to_use->index; +} + +static inline unsigned int vnic_rq_next_buf_index(struct vnic_rq *rq) +{ + return rq->buf_index++; +} + +static inline void vnic_rq_post(struct vnic_rq *rq, + void *os_buf, unsigned int os_buf_index, + dma_addr_t dma_addr, unsigned int len) +{ + struct vnic_rq_buf *buf = rq->to_use; + + buf->os_buf = os_buf; + buf->os_buf_index = os_buf_index; + buf->dma_addr = dma_addr; + buf->len = len; + + buf = buf->next; + rq->to_use = buf; + rq->ring.desc_avail--; + + /* Move the posted_index every nth descriptor + */ + +#ifndef VNIC_RQ_RETURN_RATE +#define VNIC_RQ_RETURN_RATE 0xf /* keep 2^n - 1 */ +#endif + + if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) + iowrite32(buf->index, &rq->ctrl->posted_index); +} + +static inline void vnic_rq_return_descs(struct vnic_rq *rq, unsigned int count) +{ + rq->ring.desc_avail += count; +} + +enum desc_return_options { + VNIC_RQ_RETURN_DESC, + VNIC_RQ_DEFER_RETURN_DESC, +}; + +static inline void vnic_rq_service(struct vnic_rq *rq, + struct cq_desc *cq_desc, u16 completed_index, + int desc_return, void (*buf_service)(struct vnic_rq *rq, + struct cq_desc *cq_desc, struct vnic_rq_buf *buf, + int skipped, void *opaque), void *opaque) +{ + struct vnic_rq_buf *buf; + int skipped; + + buf = rq->to_clean; + while (1) { + + skipped = (buf->index != completed_index); + + (*buf_service)(rq, cq_desc, buf, skipped, opaque); + + if (desc_return == VNIC_RQ_RETURN_DESC) + rq->ring.desc_avail++; + + rq->to_clean = buf->next; + + if (!skipped) + break; + + buf = rq->to_clean; + } +} + +static inline int vnic_rq_fill(struct vnic_rq *rq, + int (*buf_fill)(struct vnic_rq *rq)) +{ + int err; + + while (vnic_rq_desc_avail(rq) > 1) { + + err = (*buf_fill)(rq); + if (err) + return err; + } + + return 0; +} + +void vnic_rq_free(struct vnic_rq *rq); +int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index, + unsigned int desc_count, unsigned int desc_size); +void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset); +unsigned int vnic_rq_error_status(struct vnic_rq *rq); +void vnic_rq_enable(struct vnic_rq *rq); +int vnic_rq_disable(struct vnic_rq *rq); +void vnic_rq_clean(struct vnic_rq *rq, + void (*buf_clean)(struct vnic_rq *rq, struct vnic_rq_buf *buf)); + +#endif /* _VNIC_RQ_H_ */ diff --git a/drivers/net/enic/vnic_rss.h b/drivers/net/enic/vnic_rss.h new file mode 100644 index 000000000000..e325d65d7c34 --- /dev/null +++ b/drivers/net/enic/vnic_rss.h @@ -0,0 +1,32 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + */ + +#ifndef _VNIC_RSS_H_ +#define _VNIC_RSS_H_ + +/* RSS key array */ +union vnic_rss_key { + struct { + u8 b[10]; + u8 b_pad[6]; + } key[4]; + u64 raw[8]; +}; + +/* RSS cpu array */ +union vnic_rss_cpu { + struct { + u8 b[4] ; + u8 b_pad[4]; + } cpu[32]; + u64 raw[32]; +}; + +void vnic_set_rss_key(union vnic_rss_key *rss_key, u8 *key); +void vnic_set_rss_cpu(union vnic_rss_cpu *rss_cpu, u8 *cpu); +void vnic_get_rss_key(union vnic_rss_key *rss_key, u8 *key); +void vnic_get_rss_cpu(union vnic_rss_cpu *rss_cpu, u8 *cpu); + +#endif /* _VNIC_RSS_H_ */ diff --git a/drivers/net/enic/vnic_stats.h b/drivers/net/enic/vnic_stats.h new file mode 100644 index 000000000000..9ff9614d89b1 --- /dev/null +++ b/drivers/net/enic/vnic_stats.h @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_STATS_H_ +#define _VNIC_STATS_H_ + +/* Tx statistics */ +struct vnic_tx_stats { + u64 tx_frames_ok; + u64 tx_unicast_frames_ok; + u64 tx_multicast_frames_ok; + u64 tx_broadcast_frames_ok; + u64 tx_bytes_ok; + u64 tx_unicast_bytes_ok; + u64 tx_multicast_bytes_ok; + u64 tx_broadcast_bytes_ok; + u64 tx_drops; + u64 tx_errors; + u64 tx_tso; + u64 rsvd[16]; +}; + +/* Rx statistics */ +struct vnic_rx_stats { + u64 rx_frames_ok; + u64 rx_frames_total; + u64 rx_unicast_frames_ok; + u64 rx_multicast_frames_ok; + u64 rx_broadcast_frames_ok; + u64 rx_bytes_ok; + u64 rx_unicast_bytes_ok; + u64 rx_multicast_bytes_ok; + u64 rx_broadcast_bytes_ok; + u64 rx_drop; + u64 rx_no_bufs; + u64 rx_errors; + u64 rx_rss; + u64 rx_crc_errors; + u64 rx_frames_64; + u64 rx_frames_127; + u64 rx_frames_255; + u64 rx_frames_511; + u64 rx_frames_1023; + u64 rx_frames_1518; + u64 rx_frames_to_max; + u64 rsvd[16]; +}; + +struct vnic_stats { + struct vnic_tx_stats tx; + struct vnic_rx_stats rx; +}; + +#endif /* _VNIC_STATS_H_ */ diff --git a/drivers/net/enic/vnic_wq.c b/drivers/net/enic/vnic_wq.c new file mode 100644 index 000000000000..a576d04708ef --- /dev/null +++ b/drivers/net/enic/vnic_wq.c @@ -0,0 +1,184 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "vnic_dev.h" +#include "vnic_wq.h" + +static int vnic_wq_alloc_bufs(struct vnic_wq *wq) +{ + struct vnic_wq_buf *buf; + struct vnic_dev *vdev; + unsigned int i, j, count = wq->ring.desc_count; + unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count); + + vdev = wq->vdev; + + for (i = 0; i < blks; i++) { + wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC); + if (!wq->bufs[i]) { + printk(KERN_ERR "Failed to alloc wq_bufs\n"); + return -ENOMEM; + } + } + + for (i = 0; i < blks; i++) { + buf = wq->bufs[i]; + for (j = 0; j < VNIC_WQ_BUF_BLK_ENTRIES; j++) { + buf->index = i * VNIC_WQ_BUF_BLK_ENTRIES + j; + buf->desc = (u8 *)wq->ring.descs + + wq->ring.desc_size * buf->index; + if (buf->index + 1 == count) { + buf->next = wq->bufs[0]; + break; + } else if (j + 1 == VNIC_WQ_BUF_BLK_ENTRIES) { + buf->next = wq->bufs[i + 1]; + } else { + buf->next = buf + 1; + buf++; + } + } + } + + wq->to_use = wq->to_clean = wq->bufs[0]; + + return 0; +} + +void vnic_wq_free(struct vnic_wq *wq) +{ + struct vnic_dev *vdev; + unsigned int i; + + vdev = wq->vdev; + + vnic_dev_free_desc_ring(vdev, &wq->ring); + + for (i = 0; i < VNIC_WQ_BUF_BLKS_MAX; i++) { + kfree(wq->bufs[i]); + wq->bufs[i] = NULL; + } + + wq->ctrl = NULL; +} + +int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, + unsigned int desc_count, unsigned int desc_size) +{ + int err; + + wq->index = index; + wq->vdev = vdev; + + wq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_WQ, index); + if (!wq->ctrl) { + printk(KERN_ERR "Failed to hook WQ[%d] resource\n", index); + return -EINVAL; + } + + vnic_wq_disable(wq); + + err = vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size); + if (err) + return err; + + err = vnic_wq_alloc_bufs(wq); + if (err) { + vnic_wq_free(wq); + return err; + } + + return 0; +} + +void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset) +{ + u64 paddr; + + paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET; + writeq(paddr, &wq->ctrl->ring_base); + iowrite32(wq->ring.desc_count, &wq->ctrl->ring_size); + iowrite32(0, &wq->ctrl->fetch_index); + iowrite32(0, &wq->ctrl->posted_index); + iowrite32(cq_index, &wq->ctrl->cq_index); + iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable); + iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset); + iowrite32(0, &wq->ctrl->error_status); +} + +unsigned int vnic_wq_error_status(struct vnic_wq *wq) +{ + return ioread32(&wq->ctrl->error_status); +} + +void vnic_wq_enable(struct vnic_wq *wq) +{ + iowrite32(1, &wq->ctrl->enable); +} + +int vnic_wq_disable(struct vnic_wq *wq) +{ + unsigned int wait; + + iowrite32(0, &wq->ctrl->enable); + + /* Wait for HW to ACK disable request */ + for (wait = 0; wait < 100; wait++) { + if (!(ioread32(&wq->ctrl->running))) + return 0; + udelay(1); + } + + printk(KERN_ERR "Failed to disable WQ[%d]\n", wq->index); + + return -ETIMEDOUT; +} + +void vnic_wq_clean(struct vnic_wq *wq, + void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf)) +{ + struct vnic_wq_buf *buf; + + BUG_ON(ioread32(&wq->ctrl->enable)); + + buf = wq->to_clean; + + while (vnic_wq_desc_used(wq) > 0) { + + (*buf_clean)(wq, buf); + + buf = wq->to_clean = buf->next; + wq->ring.desc_avail++; + } + + wq->to_use = wq->to_clean = wq->bufs[0]; + + iowrite32(0, &wq->ctrl->fetch_index); + iowrite32(0, &wq->ctrl->posted_index); + iowrite32(0, &wq->ctrl->error_status); + + vnic_dev_clear_desc_ring(&wq->ring); +} diff --git a/drivers/net/enic/vnic_wq.h b/drivers/net/enic/vnic_wq.h new file mode 100644 index 000000000000..7081828d8a42 --- /dev/null +++ b/drivers/net/enic/vnic_wq.h @@ -0,0 +1,154 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _VNIC_WQ_H_ +#define _VNIC_WQ_H_ + +#include + +#include "vnic_dev.h" +#include "vnic_cq.h" + +/* Work queue control */ +struct vnic_wq_ctrl { + u64 ring_base; /* 0x00 */ + u32 ring_size; /* 0x08 */ + u32 pad0; + u32 posted_index; /* 0x10 */ + u32 pad1; + u32 cq_index; /* 0x18 */ + u32 pad2; + u32 enable; /* 0x20 */ + u32 pad3; + u32 running; /* 0x28 */ + u32 pad4; + u32 fetch_index; /* 0x30 */ + u32 pad5; + u32 dca_value; /* 0x38 */ + u32 pad6; + u32 error_interrupt_enable; /* 0x40 */ + u32 pad7; + u32 error_interrupt_offset; /* 0x48 */ + u32 pad8; + u32 error_status; /* 0x50 */ + u32 pad9; +}; + +struct vnic_wq_buf { + struct vnic_wq_buf *next; + dma_addr_t dma_addr; + void *os_buf; + unsigned int len; + unsigned int index; + int sop; + void *desc; +}; + +/* Break the vnic_wq_buf allocations into blocks of 64 entries */ +#define VNIC_WQ_BUF_BLK_ENTRIES 64 +#define VNIC_WQ_BUF_BLK_SZ \ + (VNIC_WQ_BUF_BLK_ENTRIES * sizeof(struct vnic_wq_buf)) +#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \ + DIV_ROUND_UP(entries, VNIC_WQ_BUF_BLK_ENTRIES) +#define VNIC_WQ_BUF_BLKS_MAX VNIC_WQ_BUF_BLKS_NEEDED(4096) + +struct vnic_wq { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_wq_ctrl __iomem *ctrl; /* memory-mapped */ + struct vnic_dev_ring ring; + struct vnic_wq_buf *bufs[VNIC_WQ_BUF_BLKS_MAX]; + struct vnic_wq_buf *to_use; + struct vnic_wq_buf *to_clean; + unsigned int pkts_outstanding; +}; + +static inline unsigned int vnic_wq_desc_avail(struct vnic_wq *wq) +{ + /* how many does SW own? */ + return wq->ring.desc_avail; +} + +static inline unsigned int vnic_wq_desc_used(struct vnic_wq *wq) +{ + /* how many does HW own? */ + return wq->ring.desc_count - wq->ring.desc_avail - 1; +} + +static inline void *vnic_wq_next_desc(struct vnic_wq *wq) +{ + return wq->to_use->desc; +} + +static inline void vnic_wq_post(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, + unsigned int len, int sop, int eop) +{ + struct vnic_wq_buf *buf = wq->to_use; + + buf->sop = sop; + buf->os_buf = eop ? os_buf : NULL; + buf->dma_addr = dma_addr; + buf->len = len; + + buf = buf->next; + if (eop) + iowrite32(buf->index, &wq->ctrl->posted_index); + wq->to_use = buf; + + wq->ring.desc_avail--; +} + +static inline void vnic_wq_service(struct vnic_wq *wq, + struct cq_desc *cq_desc, u16 completed_index, + void (*buf_service)(struct vnic_wq *wq, + struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque), + void *opaque) +{ + struct vnic_wq_buf *buf; + + buf = wq->to_clean; + while (1) { + + (*buf_service)(wq, cq_desc, buf, opaque); + + wq->ring.desc_avail++; + + wq->to_clean = buf->next; + + if (buf->index == completed_index) + break; + + buf = wq->to_clean; + } +} + +void vnic_wq_free(struct vnic_wq *wq); +int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, + unsigned int desc_count, unsigned int desc_size); +void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset); +unsigned int vnic_wq_error_status(struct vnic_wq *wq); +void vnic_wq_enable(struct vnic_wq *wq); +int vnic_wq_disable(struct vnic_wq *wq); +void vnic_wq_clean(struct vnic_wq *wq, + void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf)); + +#endif /* _VNIC_WQ_H_ */ diff --git a/drivers/net/enic/wq_enet_desc.h b/drivers/net/enic/wq_enet_desc.h new file mode 100644 index 000000000000..483596c2d8bf --- /dev/null +++ b/drivers/net/enic/wq_enet_desc.h @@ -0,0 +1,98 @@ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _WQ_ENET_DESC_H_ +#define _WQ_ENET_DESC_H_ + +/* Ethernet work queue descriptor: 16B */ +struct wq_enet_desc { + __le64 address; + __le16 length; + __le16 mss_loopback; + __le16 header_length_flags; + __le16 vlan_tag; +}; + +#define WQ_ENET_ADDR_BITS 64 +#define WQ_ENET_LEN_BITS 14 +#define WQ_ENET_LEN_MASK ((1 << WQ_ENET_LEN_BITS) - 1) +#define WQ_ENET_MSS_BITS 14 +#define WQ_ENET_MSS_MASK ((1 << WQ_ENET_MSS_BITS) - 1) +#define WQ_ENET_MSS_SHIFT 2 +#define WQ_ENET_LOOPBACK_SHIFT 1 +#define WQ_ENET_HDRLEN_BITS 10 +#define WQ_ENET_HDRLEN_MASK ((1 << WQ_ENET_HDRLEN_BITS) - 1) +#define WQ_ENET_FLAGS_OM_BITS 2 +#define WQ_ENET_FLAGS_OM_MASK ((1 << WQ_ENET_FLAGS_OM_BITS) - 1) +#define WQ_ENET_FLAGS_EOP_SHIFT 12 +#define WQ_ENET_FLAGS_CQ_ENTRY_SHIFT 13 +#define WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT 14 +#define WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT 15 + +#define WQ_ENET_OFFLOAD_MODE_CSUM 0 +#define WQ_ENET_OFFLOAD_MODE_RESERVED 1 +#define WQ_ENET_OFFLOAD_MODE_CSUM_L4 2 +#define WQ_ENET_OFFLOAD_MODE_TSO 3 + +static inline void wq_enet_desc_enc(struct wq_enet_desc *desc, + u64 address, u16 length, u16 mss, u16 header_length, + u8 offload_mode, u8 eop, u8 cq_entry, u8 fcoe_encap, + u8 vlan_tag_insert, u16 vlan_tag, u8 loopback) +{ + desc->address = cpu_to_le64(address); + desc->length = cpu_to_le16(length & WQ_ENET_LEN_MASK); + desc->mss_loopback = cpu_to_le16((mss & WQ_ENET_MSS_MASK) << + WQ_ENET_MSS_SHIFT | (loopback & 1) << WQ_ENET_LOOPBACK_SHIFT); + desc->header_length_flags = cpu_to_le16( + (header_length & WQ_ENET_HDRLEN_MASK) | + (offload_mode & WQ_ENET_FLAGS_OM_MASK) << WQ_ENET_HDRLEN_BITS | + (eop & 1) << WQ_ENET_FLAGS_EOP_SHIFT | + (cq_entry & 1) << WQ_ENET_FLAGS_CQ_ENTRY_SHIFT | + (fcoe_encap & 1) << WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT | + (vlan_tag_insert & 1) << WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT); + desc->vlan_tag = cpu_to_le16(vlan_tag); +} + +static inline void wq_enet_desc_dec(struct wq_enet_desc *desc, + u64 *address, u16 *length, u16 *mss, u16 *header_length, + u8 *offload_mode, u8 *eop, u8 *cq_entry, u8 *fcoe_encap, + u8 *vlan_tag_insert, u16 *vlan_tag, u8 *loopback) +{ + *address = le64_to_cpu(desc->address); + *length = le16_to_cpu(desc->length) & WQ_ENET_LEN_MASK; + *mss = (le16_to_cpu(desc->mss_loopback) >> WQ_ENET_MSS_SHIFT) & + WQ_ENET_MSS_MASK; + *loopback = (u8)((le16_to_cpu(desc->mss_loopback) >> + WQ_ENET_LOOPBACK_SHIFT) & 1); + *header_length = le16_to_cpu(desc->header_length_flags) & + WQ_ENET_HDRLEN_MASK; + *offload_mode = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_HDRLEN_BITS) & WQ_ENET_FLAGS_OM_MASK); + *eop = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_EOP_SHIFT) & 1); + *cq_entry = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_CQ_ENTRY_SHIFT) & 1); + *fcoe_encap = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT) & 1); + *vlan_tag_insert = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT) & 1); + *vlan_tag = le16_to_cpu(desc->vlan_tag); +} + +#endif /* _WQ_ENET_DESC_H_ */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 90a132ab84a6..6f4276d461c0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1411,6 +1411,8 @@ #define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 #define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014 +#define PCI_VENDOR_ID_CISCO 0x1137 + #define PCI_VENDOR_ID_ZIATECH 0x1138 #define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550 -- cgit v1.2.3 From 4fd5f812c23c7deee6425f4a318e85c317cd1d6c Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 26 Aug 2008 13:08:46 +0200 Subject: phylib: allow incremental scanning of an mii bus This patch splits the bus scanning code in mdiobus_register() off into a separate function, and makes this function available for calling from external code. This allows incrementally scanning an mii bus, e.g. as information about which addresses are 'safe' to scan becomes available. Signed-off-by: Lennert Buytenhek Acked-by: Andy Fleming --- drivers/net/phy/mdio_bus.c | 89 +++++++++++++++++++++++++--------------------- include/linux/phy.h | 2 ++ 2 files changed, 50 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 94e0b7ed76f1..e7508c10887c 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -60,49 +60,14 @@ int mdiobus_register(struct mii_bus *bus) bus->reset(bus); for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *phydev; + bus->phy_map[i] = NULL; + if ((bus->phy_mask & (1 << i)) == 0) { + struct phy_device *phydev; - if (bus->phy_mask & (1 << i)) { - bus->phy_map[i] = NULL; - continue; + phydev = mdiobus_scan(bus, i); + if (IS_ERR(phydev)) + err = PTR_ERR(phydev); } - - phydev = get_phy_device(bus, i); - - if (IS_ERR(phydev)) - return PTR_ERR(phydev); - - /* There's a PHY at this address - * We need to set: - * 1) IRQ - * 2) bus_id - * 3) parent - * 4) bus - * 5) mii_bus - * And, we need to register it */ - if (phydev) { - phydev->irq = bus->irq[i]; - - phydev->dev.parent = bus->dev; - phydev->dev.bus = &mdio_bus_type; - snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i); - - phydev->bus = bus; - - /* Run all of the fixups for this PHY */ - phy_scan_fixups(phydev); - - err = device_register(&phydev->dev); - - if (err) { - printk(KERN_ERR "phy %d failed to register\n", - i); - phy_device_free(phydev); - phydev = NULL; - } - } - - bus->phy_map[i] = phydev; } pr_info("%s: probed\n", bus->name); @@ -122,6 +87,48 @@ void mdiobus_unregister(struct mii_bus *bus) } EXPORT_SYMBOL(mdiobus_unregister); +struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) +{ + struct phy_device *phydev; + int err; + + phydev = get_phy_device(bus, addr); + if (IS_ERR(phydev) || phydev == NULL) + return phydev; + + /* There's a PHY at this address + * We need to set: + * 1) IRQ + * 2) bus_id + * 3) parent + * 4) bus + * 5) mii_bus + * And, we need to register it */ + + phydev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; + + phydev->dev.parent = bus->dev; + phydev->dev.bus = &mdio_bus_type; + snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, addr); + + phydev->bus = bus; + + /* Run all of the fixups for this PHY */ + phy_scan_fixups(phydev); + + err = device_register(&phydev->dev); + if (err) { + printk(KERN_ERR "phy %d failed to register\n", addr); + phy_device_free(phydev); + phydev = NULL; + } + + bus->phy_map[addr] = phydev; + + return phydev; +} +EXPORT_SYMBOL(mdiobus_scan); + /** * mdio_bus_match - determine if given PHY driver supports the given PHY device * @dev: target PHY device diff --git a/include/linux/phy.h b/include/linux/phy.h index 7224c4099a28..5f170f5b1a30 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -410,6 +410,8 @@ int phy_start_aneg(struct phy_device *phydev); int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); +struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); + void phy_sanitize_settings(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev); int phy_enable_interrupts(struct phy_device *phydev); -- cgit v1.2.3 From 07a2c01a0c2a0cb4581a67d50d4f17cb4d2457c4 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 19 Sep 2008 02:02:05 +0900 Subject: convert swiotlb to use dma_get_mask swiotlb can use dma_get_mask() instead of the homegrown function. Signed-off-by: FUJITA Tomonori Cc: tony.luck@intel.com Signed-off-by: Ingo Molnar --- include/linux/dma-mapping.h | 2 +- lib/swiotlb.c | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 0dba7433af18..ba9114ec5d3a 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -65,7 +65,7 @@ static inline int is_buffer_dma_capable(u64 mask, dma_addr_t addr, size_t size) static inline u64 dma_get_mask(struct device *dev) { - if (dev->dma_mask && *dev->dma_mask) + if (dev && dev->dma_mask && *dev->dma_mask) return *dev->dma_mask; return DMA_32BIT_MASK; } diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 240a67c2c979..f8eebd489149 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -276,11 +276,7 @@ cleanup1: static int address_needs_mapping(struct device *hwdev, dma_addr_t addr, size_t size) { - dma_addr_t mask = 0xffffffff; - /* If the device has a mask, use it, otherwise default to 32 bits */ - if (hwdev && hwdev->dma_mask) - mask = *hwdev->dma_mask; - return !is_buffer_dma_capable(mask, addr, size); + return !is_buffer_dma_capable(dma_get_mask(hwdev), addr, size); } static int is_swiotlb_buffer(char *addr) -- cgit v1.2.3 From 006f582c73f4eda35e06fd323193c3df43fb3459 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 20 Sep 2008 21:20:20 -0700 Subject: tcp: convert retransmit_cnt_hint to seqno MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Main benefit in this is that we can then freely point the retransmit_skb_hint to anywhere we want to because there's no longer need to know what would be the count changes involve, and since this is really used only as a terminator, unnecessary work is one time walk at most, and if some retransmissions are necessary after that point later on, the walk is not full waste of time anyway. Since retransmit_high must be kept valid, all lost markers must ensure that. Now I also have learned how those "holes" in the rexmittable skbs can appear, mtu probe does them. So I removed the misleading comment as well. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/tcp.h | 2 +- include/net/tcp.h | 2 ++ net/ipv4/tcp_input.c | 34 ++++++++++++++++++++-------------- net/ipv4/tcp_output.c | 25 +++++++------------------ 4 files changed, 30 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 2e2557388e36..d7637c4b2840 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -358,7 +358,7 @@ struct tcp_sock { */ int lost_cnt_hint; - int retransmit_cnt_hint; + u32 retransmit_high; /* L-bits may be on up to this seqno */ u32 lost_retrans_low; /* Sent seq after any rxmit (lowest) */ diff --git a/include/net/tcp.h b/include/net/tcp.h index b71676326950..d0e90c50722b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -472,6 +472,8 @@ extern void tcp_send_delayed_ack(struct sock *sk); /* tcp_input.c */ extern void tcp_cwnd_application_limited(struct sock *sk); +extern void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, + struct sk_buff *skb); /* tcp_timer.c */ extern void tcp_init_xmit_timers(struct sock *); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 12512336dbd8..d271cc825005 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -979,17 +979,17 @@ static void tcp_update_reordering(struct sock *sk, const int metric, } } -/* RFC: This is from the original, I doubt that this is necessary at all: - * clear xmit_retrans hint if seq of this skb is beyond hint. How could we - * retransmitted past LOST markings in the first place? I'm not fully sure - * about undo and end of connection cases, which can cause R without L? - */ +/* This must be called before lost_out is incremented */ static void tcp_verify_retransmit_hint(struct tcp_sock *tp, struct sk_buff *skb) { - if ((tp->retransmit_skb_hint != NULL) && + if ((tp->retransmit_skb_hint == NULL) || before(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(tp->retransmit_skb_hint)->seq)) - tp->retransmit_skb_hint = NULL; + tp->retransmit_skb_hint = skb; + + if (!tp->lost_out || + after(TCP_SKB_CB(skb)->end_seq, tp->retransmit_high)) + tp->retransmit_high = TCP_SKB_CB(skb)->end_seq; } static void tcp_skb_mark_lost(struct tcp_sock *tp, struct sk_buff *skb) @@ -1002,6 +1002,16 @@ static void tcp_skb_mark_lost(struct tcp_sock *tp, struct sk_buff *skb) } } +void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, struct sk_buff *skb) +{ + tcp_verify_retransmit_hint(tp, skb); + + if (!(TCP_SKB_CB(skb)->sacked & (TCPCB_LOST|TCPCB_SACKED_ACKED))) { + tp->lost_out += tcp_skb_pcount(skb); + TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; + } +} + /* This procedure tags the retransmission queue when SACKs arrive. * * We have three tag bits: SACKED(S), RETRANS(R) and LOST(L). @@ -1178,13 +1188,7 @@ static void tcp_mark_lost_retrans(struct sock *sk) TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; tp->retrans_out -= tcp_skb_pcount(skb); - /* clear lost hint */ - tp->retransmit_skb_hint = NULL; - - if (!(TCP_SKB_CB(skb)->sacked & (TCPCB_LOST|TCPCB_SACKED_ACKED))) { - tp->lost_out += tcp_skb_pcount(skb); - TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; - } + tcp_skb_mark_lost_uncond_verify(tp, skb); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSTRETRANSMIT); } else { if (before(ack_seq, new_low_seq)) @@ -1890,6 +1894,7 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag) if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) { TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; tp->lost_out += tcp_skb_pcount(skb); + tp->retransmit_high = TCP_SKB_CB(skb)->end_seq; } } tcp_verify_left_out(tp); @@ -1974,6 +1979,7 @@ void tcp_enter_loss(struct sock *sk, int how) TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED; TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; tp->lost_out += tcp_skb_pcount(skb); + tp->retransmit_high = TCP_SKB_CB(skb)->end_seq; } } tcp_verify_left_out(tp); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 11490958a096..cfae61b40c44 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1838,7 +1838,7 @@ void tcp_simple_retransmit(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; unsigned int mss = tcp_current_mss(sk, 0); - int lost = 0; + u32 prior_lost = tp->lost_out; tcp_for_write_queue(skb, sk) { if (skb == tcp_send_head(sk)) @@ -1849,17 +1849,13 @@ void tcp_simple_retransmit(struct sock *sk) TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; tp->retrans_out -= tcp_skb_pcount(skb); } - if (!(TCP_SKB_CB(skb)->sacked & TCPCB_LOST)) { - TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; - tp->lost_out += tcp_skb_pcount(skb); - lost = 1; - } + tcp_skb_mark_lost_uncond_verify(tp, skb); } } tcp_clear_all_retrans_hints(tp); - if (!lost) + if (prior_lost == tp->lost_out) return; if (tcp_is_reno(tp)) @@ -2009,15 +2005,11 @@ void tcp_xmit_retransmit_queue(struct sock *sk) const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - int packet_cnt; - if (tp->retransmit_skb_hint) { + if (tp->retransmit_skb_hint) skb = tp->retransmit_skb_hint; - packet_cnt = tp->retransmit_cnt_hint; - } else { + else skb = tcp_write_queue_head(sk); - packet_cnt = 0; - } /* First pass: retransmit lost packets. */ if (tp->lost_out) { @@ -2028,7 +2020,6 @@ void tcp_xmit_retransmit_queue(struct sock *sk) break; /* we could do better than to assign each time */ tp->retransmit_skb_hint = skb; - tp->retransmit_cnt_hint = packet_cnt; /* Assume this retransmit will generate * only one packet for congestion window @@ -2039,6 +2030,8 @@ void tcp_xmit_retransmit_queue(struct sock *sk) */ if (tcp_packets_in_flight(tp) >= tp->snd_cwnd) return; + if (!before(TCP_SKB_CB(skb)->seq, tp->retransmit_high)) + break; if (sacked & TCPCB_LOST) { if (!(sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS))) { @@ -2059,10 +2052,6 @@ void tcp_xmit_retransmit_queue(struct sock *sk) inet_csk(sk)->icsk_rto, TCP_RTO_MAX); } - - packet_cnt += tcp_skb_pcount(skb); - if (packet_cnt >= tp->lost_out) - break; } } } -- cgit v1.2.3 From 0e1c54c2a405494281e0639aacc90db03b50ae77 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 20 Sep 2008 21:24:21 -0700 Subject: tcp: reorganize retransmit code loops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both loops are quite similar, so they can be combined with little effort. As a result, forward_skb_hint becomes obsolete as well. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 1 - net/ipv4/tcp_output.c | 79 +++++++++++++++++++++------------------------------ 3 files changed, 33 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index d7637c4b2840..767290628292 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -342,7 +342,6 @@ struct tcp_sock { struct sk_buff* lost_skb_hint; struct sk_buff *scoreboard_skb_hint; struct sk_buff *retransmit_skb_hint; - struct sk_buff *forward_skb_hint; struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ diff --git a/include/net/tcp.h b/include/net/tcp.h index d0e90c50722b..220f54cf42ec 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1042,7 +1042,6 @@ static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp) tp->lost_skb_hint = NULL; tp->scoreboard_skb_hint = NULL; tp->retransmit_skb_hint = NULL; - tp->forward_skb_hint = NULL; } /* MD5 Signature */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9f44be633ef6..b5b4ddcdda41 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2032,7 +2032,9 @@ void tcp_xmit_retransmit_queue(struct sock *sk) const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; + struct sk_buff *hole = NULL; int mib_idx; + int fwd_rexmitting = 0; if (!tp->lost_out) tp->retransmit_high = tp->snd_una; @@ -2049,7 +2051,8 @@ void tcp_xmit_retransmit_queue(struct sock *sk) if (skb == tcp_send_head(sk)) break; /* we could do better than to assign each time */ - tp->retransmit_skb_hint = skb; + if (hole == NULL) + tp->retransmit_skb_hint = skb; /* Assume this retransmit will generate * only one packet for congestion window @@ -2060,65 +2063,49 @@ void tcp_xmit_retransmit_queue(struct sock *sk) */ if (tcp_packets_in_flight(tp) >= tp->snd_cwnd) return; - if (!before(TCP_SKB_CB(skb)->seq, tp->retransmit_high)) - break; - if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS)) - continue; - - if (!(sacked & TCPCB_LOST)) - continue; - - if (tcp_retransmit_skb(sk, skb)) { - tp->retransmit_skb_hint = NULL; - return; - } - if (icsk->icsk_ca_state != TCP_CA_Loss) - mib_idx = LINUX_MIB_TCPFASTRETRANS; - else - mib_idx = LINUX_MIB_TCPSLOWSTARTRETRANS; - NET_INC_STATS_BH(sock_net(sk), mib_idx); - - if (skb == tcp_write_queue_head(sk)) - inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, - inet_csk(sk)->icsk_rto, - TCP_RTO_MAX); - } - - /* OK, demanded retransmission is finished. */ - if (!tcp_can_forward_retransmit(sk)) - return; - if (tp->forward_skb_hint) - skb = tp->forward_skb_hint; - else - skb = tcp_write_queue_head(sk); + if (fwd_rexmitting) { +begin_fwd: + if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) + break; + mib_idx = LINUX_MIB_TCPFORWARDRETRANS; - tcp_for_write_queue_from(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - tp->forward_skb_hint = skb; + } else if (!before(TCP_SKB_CB(skb)->seq, tp->retransmit_high)) { + if (!tcp_can_forward_retransmit(sk)) + break; + /* Backtrack if necessary to non-L'ed skb */ + if (hole != NULL) { + skb = hole; + hole = NULL; + } + fwd_rexmitting = 1; + goto begin_fwd; - if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) - break; + } else if (!(sacked & TCPCB_LOST)) { + if (hole == NULL && !(sacked & TCPCB_SACKED_RETRANS)) + hole = skb; + continue; - if (tcp_packets_in_flight(tp) >= tp->snd_cwnd) - break; + } else { + if (icsk->icsk_ca_state != TCP_CA_Loss) + mib_idx = LINUX_MIB_TCPFASTRETRANS; + else + mib_idx = LINUX_MIB_TCPSLOWSTARTRETRANS; + } - if (TCP_SKB_CB(skb)->sacked & TCPCB_TAGBITS) + if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS)) continue; - /* Ok, retransmit it. */ if (tcp_retransmit_skb(sk, skb)) { - tp->forward_skb_hint = NULL; - break; + tp->retransmit_skb_hint = NULL; + return; } + NET_INC_STATS_BH(sock_net(sk), mib_idx); if (skb == tcp_write_queue_head(sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, TCP_RTO_MAX); - - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFORWARDRETRANS); } } -- cgit v1.2.3 From 67fed45930fa31e92c11beb3a3dbf83a1a92a58d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 21 Sep 2008 22:36:24 -0700 Subject: net: Add new interfaces for SKB list light-weight init and splicing. This will be used by subsequent changesets. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index aa80ad9cbc88..027b06170b40 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -660,6 +660,22 @@ static inline __u32 skb_queue_len(const struct sk_buff_head *list_) return list_->qlen; } +/** + * __skb_queue_head_init - initialize non-spinlock portions of sk_buff_head + * @list: queue to initialize + * + * This initializes only the list and queue length aspects of + * an sk_buff_head object. This allows to initialize the list + * aspects of an sk_buff_head without reinitializing things like + * the spinlock. It can also be used for on-stack sk_buff_head + * objects where the spinlock is known to not be used. + */ +static inline void __skb_queue_head_init(struct sk_buff_head *list) +{ + list->prev = list->next = (struct sk_buff *)list; + list->qlen = 0; +} + /* * This function creates a split out lock class for each invocation; * this is needed for now since a whole lot of users of the skb-queue @@ -671,8 +687,7 @@ static inline __u32 skb_queue_len(const struct sk_buff_head *list_) static inline void skb_queue_head_init(struct sk_buff_head *list) { spin_lock_init(&list->lock); - list->prev = list->next = (struct sk_buff *)list; - list->qlen = 0; + __skb_queue_head_init(list); } static inline void skb_queue_head_init_class(struct sk_buff_head *list, @@ -699,6 +714,83 @@ static inline void __skb_insert(struct sk_buff *newsk, list->qlen++; } +static inline void __skb_queue_splice(const struct sk_buff_head *list, + struct sk_buff *prev, + struct sk_buff *next) +{ + struct sk_buff *first = list->next; + struct sk_buff *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * skb_queue_splice - join two skb lists, this is designed for stacks + * @list: the new list to add + * @head: the place to add it in the first list + */ +static inline void skb_queue_splice(const struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, (struct sk_buff *) head, head->next); + head->qlen = list->qlen; + } +} + +/** + * skb_queue_splice - join two skb lists and reinitialise the emptied list + * @list: the new list to add + * @head: the place to add it in the first list + * + * The list at @list is reinitialised + */ +static inline void skb_queue_splice_init(struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, (struct sk_buff *) head, head->next); + head->qlen = list->qlen; + __skb_queue_head_init(list); + } +} + +/** + * skb_queue_splice_tail - join two skb lists, each list being a queue + * @list: the new list to add + * @head: the place to add it in the first list + */ +static inline void skb_queue_splice_tail(const struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, head->prev, (struct sk_buff *) head); + head->qlen = list->qlen; + } +} + +/** + * skb_queue_splice_tail - join two skb lists and reinitialise the emptied list + * @list: the new list to add + * @head: the place to add it in the first list + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void skb_queue_splice_tail_init(struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, head->prev, (struct sk_buff *) head); + head->qlen = list->qlen; + __skb_queue_head_init(list); + } +} + /** * __skb_queue_after - queue a buffer at the list head * @list: list to use -- cgit v1.2.3 From 6d80c39f9155e289fe8037a8b6352931ff916ceb Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 22 Sep 2008 07:29:31 +0100 Subject: GFS2: Add UUID to GFS2 sb This patch adds a UUID to the GFS2 sb structure. This field is not actually referenced from kernel space at all, but is added for completeness and due to the userland tools which get their on-disk structure information from the gfs2_ondisk.h header file. Since we have to be backwards compatible, we will assume that any GFS2 sb for which the UUID is all 0 does not have a UUID as such. We should then be (after some userland changes) able to support the -U mount option. This addresses Fedora bugzilla #242689 Signed-off-by: Steven Whitehouse --- include/linux/gfs2_ondisk.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index c3c19f926e6f..14d0df0b5749 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -118,7 +118,11 @@ struct gfs2_sb { char sb_lockproto[GFS2_LOCKNAME_LEN]; char sb_locktable[GFS2_LOCKNAME_LEN]; - /* In gfs1, quota and license dinodes followed */ + + struct gfs2_inum __pad3; /* Was quota inode in gfs1 */ + struct gfs2_inum __pad4; /* Was licence inode in gfs1 */ +#define GFS2_HAS_UUID 1 + __u8 sb_uuid[16]; /* The UUID, maybe 0 for backwards compat */ }; /* -- cgit v1.2.3 From 38783e671399b5405f1fd177d602c400a9577ae6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 22 Sep 2008 01:15:02 -0700 Subject: isdn: isdn_ppp: Use SKB list facilities instead of home-grown implementation. Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_ppp.c | 352 ++++++++++++++++++++++++-------------------- include/linux/isdn_ppp.h | 2 +- 2 files changed, 190 insertions(+), 164 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 127cfdad68e7..77c280ef2eb6 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -1533,8 +1533,10 @@ static int isdn_ppp_mp_bundle_array_init(void) int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle); if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL ) return -ENOMEM; - for( i = 0; i < ISDN_MAX_CHANNELS; i++ ) + for (i = 0; i < ISDN_MAX_CHANNELS; i++) { spin_lock_init(&isdn_ppp_bundle_arr[i].lock); + skb_queue_head_init(&isdn_ppp_bundle_arr[i].frags); + } return 0; } @@ -1567,7 +1569,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL) return -ENOMEM; lp->next = lp->last = lp; /* nobody else in a queue */ - lp->netdev->pb->frags = NULL; + skb_queue_head_init(&lp->netdev->pb->frags); lp->netdev->pb->frames = 0; lp->netdev->pb->seq = UINT_MAX; } @@ -1579,28 +1581,29 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) static u32 isdn_ppp_mp_get_seq( int short_seq, struct sk_buff * skb, u32 last_seq ); -static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, - struct sk_buff * from, struct sk_buff * to ); -static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, - struct sk_buff * from, struct sk_buff * to ); -static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb ); +static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from, + struct sk_buff *to); +static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, + struct sk_buff *from, struct sk_buff *to, + u32 lastseq); +static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb); static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb ); static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, - struct sk_buff *skb) + struct sk_buff *skb) { - struct ippp_struct *is; - isdn_net_local * lpq; - ippp_bundle * mp; - isdn_mppp_stats * stats; - struct sk_buff * newfrag, * frag, * start, *nextf; + struct sk_buff *newfrag, *frag, *start, *nextf; u32 newseq, minseq, thisseq; + isdn_mppp_stats *stats; + struct ippp_struct *is; unsigned long flags; + isdn_net_local *lpq; + ippp_bundle *mp; int slot; spin_lock_irqsave(&net_dev->pb->lock, flags); - mp = net_dev->pb; - stats = &mp->stats; + mp = net_dev->pb; + stats = &mp->stats; slot = lp->ppp_slot; if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: lp->ppp_slot(%d)\n", @@ -1611,20 +1614,19 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, return; } is = ippp_table[slot]; - if( ++mp->frames > stats->max_queue_len ) + if (++mp->frames > stats->max_queue_len) stats->max_queue_len = mp->frames; - + if (is->debug & 0x8) isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb); - newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, - skb, is->last_link_seqno); - + newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, + skb, is->last_link_seqno); /* if this packet seq # is less than last already processed one, * toss it right away, but check for sequence start case first */ - if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) { + if (mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT)) { mp->seq = newseq; /* the first packet: required for * rfc1990 non-compliant clients -- * prevents constant packet toss */ @@ -1634,7 +1636,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, spin_unlock_irqrestore(&mp->lock, flags); return; } - + /* find the minimum received sequence number over all links */ is->last_link_seqno = minseq = newseq; for (lpq = net_dev->queue;;) { @@ -1655,22 +1657,31 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * packets */ newfrag = skb; - /* if this new fragment is before the first one, then enqueue it now. */ - if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) { - newfrag->next = frag; - mp->frags = frag = newfrag; - newfrag = NULL; - } + /* Insert new fragment into the proper sequence slot. */ + skb_queue_walk(&mp->frags, frag) { + if (MP_SEQ(frag) == newseq) { + isdn_ppp_mp_free_skb(mp, newfrag); + newfrag = NULL; + break; + } + if (MP_LT(newseq, MP_SEQ(frag))) { + __skb_queue_before(&mp->frags, frag, newfrag); + newfrag = NULL; + break; + } + } + if (newfrag) + __skb_queue_tail(&mp->frags, newfrag); - start = MP_FLAGS(frag) & MP_BEGIN_FRAG && - MP_SEQ(frag) == mp->seq ? frag : NULL; + frag = skb_peek(&mp->frags); + start = ((MP_FLAGS(frag) & MP_BEGIN_FRAG) && + (MP_SEQ(frag) == mp->seq)) ? frag : NULL; + if (!start) + goto check_overflow; - /* - * main fragment traversing loop + /* main fragment traversing loop * * try to accomplish several tasks: - * - insert new fragment into the proper sequence slot (once that's done - * newfrag will be set to NULL) * - reassemble any complete fragment sequence (non-null 'start' * indicates there is a continguous sequence present) * - discard any incomplete sequences that are below minseq -- due @@ -1679,71 +1690,46 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * come to complete such sequence and it should be discarded * * loop completes when we accomplished the following tasks: - * - new fragment is inserted in the proper sequence ('newfrag' is - * set to NULL) * - we hit a gap in the sequence, so no reassembly/processing is * possible ('start' would be set to NULL) * * algorithm for this code is derived from code in the book * 'PPP Design And Debugging' by James Carlson (Addison-Wesley) */ - while (start != NULL || newfrag != NULL) { - - thisseq = MP_SEQ(frag); - nextf = frag->next; - - /* drop any duplicate fragments */ - if (newfrag != NULL && thisseq == newseq) { - isdn_ppp_mp_free_skb(mp, newfrag); - newfrag = NULL; - } - - /* insert new fragment before next element if possible. */ - if (newfrag != NULL && (nextf == NULL || - MP_LT(newseq, MP_SEQ(nextf)))) { - newfrag->next = nextf; - frag->next = nextf = newfrag; - newfrag = NULL; - } - - if (start != NULL) { - /* check for misplaced start */ - if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { - printk(KERN_WARNING"isdn_mppp(seq %d): new " - "BEGIN flag with no prior END", thisseq); - stats->seqerrs++; - stats->frame_drops++; - start = isdn_ppp_mp_discard(mp, start,frag); - nextf = frag->next; - } - } else if (MP_LE(thisseq, minseq)) { - if (MP_FLAGS(frag) & MP_BEGIN_FRAG) + skb_queue_walk_safe(&mp->frags, frag, nextf) { + thisseq = MP_SEQ(frag); + + /* check for misplaced start */ + if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { + printk(KERN_WARNING"isdn_mppp(seq %d): new " + "BEGIN flag with no prior END", thisseq); + stats->seqerrs++; + stats->frame_drops++; + isdn_ppp_mp_discard(mp, start, frag); + start = frag; + } else if (MP_LE(thisseq, minseq)) { + if (MP_FLAGS(frag) & MP_BEGIN_FRAG) start = frag; - else { + else { if (MP_FLAGS(frag) & MP_END_FRAG) - stats->frame_drops++; - if( mp->frags == frag ) - mp->frags = nextf; + stats->frame_drops++; + __skb_unlink(skb, &mp->frags); isdn_ppp_mp_free_skb(mp, frag); - frag = nextf; continue; - } + } } - - /* if start is non-null and we have end fragment, then - * we have full reassembly sequence -- reassemble - * and process packet now + + /* if we have end fragment, then we have full reassembly + * sequence -- reassemble and process packet now */ - if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) { - minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK; - /* Reassemble the packet then dispatch it */ - isdn_ppp_mp_reassembly(net_dev, lp, start, nextf); - - start = NULL; - frag = NULL; + if (MP_FLAGS(frag) & MP_END_FRAG) { + minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK; + /* Reassemble the packet then dispatch it */ + isdn_ppp_mp_reassembly(net_dev, lp, start, frag, thisseq); - mp->frags = nextf; - } + start = NULL; + frag = NULL; + } /* check if need to update start pointer: if we just * reassembled the packet and sequence is contiguous @@ -1754,26 +1740,25 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * below low watermark and set start to the next frag or * clear start ptr. */ - if (nextf != NULL && + if (nextf != (struct sk_buff *)&mp->frags && ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) { - /* if we just reassembled and the next one is here, - * then start another reassembly. */ - - if (frag == NULL) { + /* if we just reassembled and the next one is here, + * then start another reassembly. + */ + if (frag == NULL) { if (MP_FLAGS(nextf) & MP_BEGIN_FRAG) - start = nextf; - else - { - printk(KERN_WARNING"isdn_mppp(seq %d):" - " END flag with no following " - "BEGIN", thisseq); + start = nextf; + else { + printk(KERN_WARNING"isdn_mppp(seq %d):" + " END flag with no following " + "BEGIN", thisseq); stats->seqerrs++; } } - - } else { - if ( nextf != NULL && frag != NULL && - MP_LT(thisseq, minseq)) { + } else { + if (nextf != (struct sk_buff *)&mp->frags && + frag != NULL && + MP_LT(thisseq, minseq)) { /* we've got a break in the sequence * and we not at the end yet * and we did not just reassembled @@ -1782,41 +1767,39 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * discard all the frames below low watermark * and start over */ stats->frame_drops++; - mp->frags = isdn_ppp_mp_discard(mp,start,nextf); + isdn_ppp_mp_discard(mp, start, nextf); } /* break in the sequence, no reassembly */ - start = NULL; - } - - frag = nextf; - } /* while -- main loop */ - - if (mp->frags == NULL) - mp->frags = frag; - + start = NULL; + } + if (!start) + break; + } + +check_overflow: /* rather straighforward way to deal with (not very) possible - * queue overflow */ + * queue overflow + */ if (mp->frames > MP_MAX_QUEUE_LEN) { stats->overflows++; - while (mp->frames > MP_MAX_QUEUE_LEN) { - frag = mp->frags->next; - isdn_ppp_mp_free_skb(mp, mp->frags); - mp->frags = frag; + skb_queue_walk_safe(&mp->frags, frag, nextf) { + if (mp->frames <= MP_MAX_QUEUE_LEN) + break; + __skb_unlink(frag, &mp->frags); + isdn_ppp_mp_free_skb(mp, frag); } } spin_unlock_irqrestore(&mp->lock, flags); } -static void isdn_ppp_mp_cleanup( isdn_net_local * lp ) +static void isdn_ppp_mp_cleanup(isdn_net_local *lp) { - struct sk_buff * frag = lp->netdev->pb->frags; - struct sk_buff * nextfrag; - while( frag ) { - nextfrag = frag->next; - isdn_ppp_mp_free_skb(lp->netdev->pb, frag); - frag = nextfrag; - } - lp->netdev->pb->frags = NULL; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&lp->netdev->pb->frags, skb, tmp) { + __skb_unlink(skb, &lp->netdev->pb->frags); + isdn_ppp_mp_free_skb(lp->netdev->pb, skb); + } } static u32 isdn_ppp_mp_get_seq( int short_seq, @@ -1853,72 +1836,115 @@ static u32 isdn_ppp_mp_get_seq( int short_seq, return seq; } -struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, - struct sk_buff * from, struct sk_buff * to ) +static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from, + struct sk_buff *to) { - if( from ) - while (from != to) { - struct sk_buff * next = from->next; - isdn_ppp_mp_free_skb(mp, from); - from = next; + if (from) { + struct sk_buff *skb, *tmp; + int freeing = 0; + + skb_queue_walk_safe(&mp->frags, skb, tmp) { + if (skb == to) + break; + if (skb == from) + freeing = 1; + if (!freeing) + continue; + __skb_unlink(skb, &mp->frags); + isdn_ppp_mp_free_skb(mp, skb); } - return from; + } } -void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, - struct sk_buff * from, struct sk_buff * to ) +static unsigned int calc_tot_len(struct sk_buff_head *queue, + struct sk_buff *from, struct sk_buff *to) { - ippp_bundle * mp = net_dev->pb; - int proto; - struct sk_buff * skb; + unsigned int tot_len = 0; + struct sk_buff *skb; + int found_start = 0; + + skb_queue_walk(queue, skb) { + if (skb == from) + found_start = 1; + if (!found_start) + continue; + tot_len += skb->len - MP_HEADER_LEN; + if (skb == to) + break; + } + return tot_len; +} + +/* Reassemble packet using fragments in the reassembly queue from + * 'from' until 'to', inclusive. + */ +static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, + struct sk_buff *from, struct sk_buff *to, + u32 lastseq) +{ + ippp_bundle *mp = net_dev->pb; unsigned int tot_len; + struct sk_buff *skb; + int proto; if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", __func__, lp->ppp_slot); return; } - if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) { - if( ippp_table[lp->ppp_slot]->debug & 0x40 ) + + tot_len = calc_tot_len(&mp->frags, from, to); + + if (MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG)) { + if (ippp_table[lp->ppp_slot]->debug & 0x40) printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, " - "len %d\n", MP_SEQ(from), from->len ); + "len %d\n", MP_SEQ(from), from->len); skb = from; skb_pull(skb, MP_HEADER_LEN); + __skb_unlink(skb, &mp->frags); mp->frames--; } else { - struct sk_buff * frag; - int n; + struct sk_buff *walk, *tmp; + int found_start = 0; - for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++) - tot_len += frag->len - MP_HEADER_LEN; - - if( ippp_table[lp->ppp_slot]->debug & 0x40 ) + if (ippp_table[lp->ppp_slot]->debug & 0x40) printk(KERN_DEBUG"isdn_mppp: reassembling frames %d " - "to %d, len %d\n", MP_SEQ(from), - (MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len ); - if( (skb = dev_alloc_skb(tot_len)) == NULL ) { + "to %d, len %d\n", MP_SEQ(from), lastseq, + tot_len); + + skb = dev_alloc_skb(tot_len); + if (!skb) printk(KERN_ERR "isdn_mppp: cannot allocate sk buff " - "of size %d\n", tot_len); - isdn_ppp_mp_discard(mp, from, to); - return; - } + "of size %d\n", tot_len); + + found_start = 0; + skb_queue_walk_safe(&mp->frags, walk, tmp) { + if (walk == from) + found_start = 1; + if (!found_start) + continue; - while( from != to ) { - unsigned int len = from->len - MP_HEADER_LEN; + if (skb) { + unsigned int len = walk->len - MP_HEADER_LEN; + skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN, + skb_put(skb, len), + len); + } + __skb_unlink(walk, &mp->frags); + isdn_ppp_mp_free_skb(mp, walk); - skb_copy_from_linear_data_offset(from, MP_HEADER_LEN, - skb_put(skb,len), - len); - frag = from->next; - isdn_ppp_mp_free_skb(mp, from); - from = frag; + if (walk == to) + break; } } + if (!skb) + return; + proto = isdn_ppp_strip_proto(skb); isdn_ppp_push_higher(net_dev, lp, skb, proto); } -static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb) +static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb) { dev_kfree_skb(skb); mp->frames--; diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index 8687a7dc0632..4c218ee7587a 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -157,7 +157,7 @@ typedef struct { typedef struct { int mp_mrru; /* unused */ - struct sk_buff * frags; /* fragments sl list -- use skb->next */ + struct sk_buff_head frags; /* fragments sl list */ long frames; /* number of frames in the frame list */ unsigned int seq; /* last processed packet seq #: any packets * with smaller seq # will be dropped -- cgit v1.2.3 From 15afe09bf496ae10c989e1a375a6b5da7bd3e16e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 20 Sep 2008 23:38:02 +0200 Subject: sched: wakeup preempt when small overlap Lin Ming reported a 10% OLTP regression against 2.6.27-rc4. The difference seems to come from different preemption agressiveness, which affects the cache footprint of the workload and its effective cache trashing. Aggresively preempt a task if its avg overlap is very small, this should avoid the task going to sleep and find it still running when we schedule back to it - saving a wakeup. Reported-by: Lin Ming Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 +- kernel/sched.c | 12 ++++++------ kernel/sched_fair.c | 13 ++++++++++--- kernel/sched_features.h | 1 + kernel/sched_idletask.c | 6 +++--- kernel/sched_rt.c | 2 +- 6 files changed, 22 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index b3b7a8f32477..d8e699b55858 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -897,7 +897,7 @@ struct sched_class { void (*yield_task) (struct rq *rq); int (*select_task_rq)(struct task_struct *p, int sync); - void (*check_preempt_curr) (struct rq *rq, struct task_struct *p); + void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int sync); struct task_struct * (*pick_next_task) (struct rq *rq); void (*put_prev_task) (struct rq *rq, struct task_struct *p); diff --git a/kernel/sched.c b/kernel/sched.c index 0d8905a1b8ca..ad9d39b021f8 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -604,9 +604,9 @@ struct rq { static DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues); -static inline void check_preempt_curr(struct rq *rq, struct task_struct *p) +static inline void check_preempt_curr(struct rq *rq, struct task_struct *p, int sync) { - rq->curr->sched_class->check_preempt_curr(rq, p); + rq->curr->sched_class->check_preempt_curr(rq, p, sync); } static inline int cpu_of(struct rq *rq) @@ -2282,7 +2282,7 @@ out_running: trace_mark(kernel_sched_wakeup, "pid %d state %ld ## rq %p task %p rq->curr %p", p->pid, p->state, rq, p, rq->curr); - check_preempt_curr(rq, p); + check_preempt_curr(rq, p, sync); p->state = TASK_RUNNING; #ifdef CONFIG_SMP @@ -2417,7 +2417,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) trace_mark(kernel_sched_wakeup_new, "pid %d state %ld ## rq %p task %p rq->curr %p", p->pid, p->state, rq, p, rq->curr); - check_preempt_curr(rq, p); + check_preempt_curr(rq, p, 0); #ifdef CONFIG_SMP if (p->sched_class->task_wake_up) p->sched_class->task_wake_up(rq, p); @@ -2877,7 +2877,7 @@ static void pull_task(struct rq *src_rq, struct task_struct *p, * Note that idle threads have a prio of MAX_PRIO, for this test * to be always true for them. */ - check_preempt_curr(this_rq, p); + check_preempt_curr(this_rq, p, 0); } /* @@ -6007,7 +6007,7 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) set_task_cpu(p, dest_cpu); if (on_rq) { activate_task(rq_dest, p, 0); - check_preempt_curr(rq_dest, p); + check_preempt_curr(rq_dest, p, 0); } done: ret = 1; diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index a10ac0bcee64..7328383690f1 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1331,7 +1331,7 @@ static inline int depth_se(struct sched_entity *se) /* * Preempt the current task with a newly woken task if needed: */ -static void check_preempt_wakeup(struct rq *rq, struct task_struct *p) +static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync) { struct task_struct *curr = rq->curr; struct cfs_rq *cfs_rq = task_cfs_rq(curr); @@ -1367,6 +1367,13 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p) if (!sched_feat(WAKEUP_PREEMPT)) return; + if (sched_feat(WAKEUP_OVERLAP) && sync && + se->avg_overlap < sysctl_sched_migration_cost && + pse->avg_overlap < sysctl_sched_migration_cost) { + resched_task(curr); + return; + } + /* * preemption test can be made between sibling entities who are in the * same cfs_rq i.e who have a common parent. Walk up the hierarchy of @@ -1649,7 +1656,7 @@ static void prio_changed_fair(struct rq *rq, struct task_struct *p, if (p->prio > oldprio) resched_task(rq->curr); } else - check_preempt_curr(rq, p); + check_preempt_curr(rq, p, 0); } /* @@ -1666,7 +1673,7 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p, if (running) resched_task(rq->curr); else - check_preempt_curr(rq, p); + check_preempt_curr(rq, p, 0); } /* Account for a task changing its policy or group. diff --git a/kernel/sched_features.h b/kernel/sched_features.h index 9353ca78154e..bf027a7accf8 100644 --- a/kernel/sched_features.h +++ b/kernel/sched_features.h @@ -11,3 +11,4 @@ SCHED_FEAT(ASYM_GRAN, 1) SCHED_FEAT(LB_BIAS, 1) SCHED_FEAT(LB_WAKEUP_UPDATE, 1) SCHED_FEAT(ASYM_EFF_LOAD, 1) +SCHED_FEAT(WAKEUP_OVERLAP, 1) diff --git a/kernel/sched_idletask.c b/kernel/sched_idletask.c index 3a4f92dbbe66..dec4ccabe2f5 100644 --- a/kernel/sched_idletask.c +++ b/kernel/sched_idletask.c @@ -14,7 +14,7 @@ static int select_task_rq_idle(struct task_struct *p, int sync) /* * Idle tasks are unconditionally rescheduled: */ -static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p) +static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int sync) { resched_task(rq->idle); } @@ -76,7 +76,7 @@ static void switched_to_idle(struct rq *rq, struct task_struct *p, if (running) resched_task(rq->curr); else - check_preempt_curr(rq, p); + check_preempt_curr(rq, p, 0); } static void prio_changed_idle(struct rq *rq, struct task_struct *p, @@ -93,7 +93,7 @@ static void prio_changed_idle(struct rq *rq, struct task_struct *p, if (p->prio > oldprio) resched_task(rq->curr); } else - check_preempt_curr(rq, p); + check_preempt_curr(rq, p, 0); } /* diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index 552310798dad..6d2d0a5d030b 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -783,7 +783,7 @@ static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p) /* * Preempt the current task with a newly woken task if needed: */ -static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p) +static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int sync) { if (p->prio < rq->curr->prio) { resched_task(rq->curr); -- cgit v1.2.3 From d26dbc5cf94b0a28acc947285c3b54814a73cb2e Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 22 Sep 2008 22:35:07 +0900 Subject: iommu: export iommu_area_reserve helper function x86 has set_bit_string() that does the exact same thing that set_bit_area() in lib/iommu-helper.c does. This patch exports set_bit_area() in lib/iommu-helper.c as iommu_area_reserve(), converts GART, Calgary, and AMD IOMMU to use it. Signed-off-by: FUJITA Tomonori Acked-by: Joerg Roedel Signed-off-by: Ingo Molnar --- arch/x86/kernel/amd_iommu.c | 2 +- arch/x86/kernel/pci-calgary_64.c | 2 +- arch/x86/kernel/pci-gart_64.c | 2 +- include/linux/iommu-helper.h | 1 + lib/iommu-helper.c | 5 ++--- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c index 6f7b97445738..70537d117a96 100644 --- a/arch/x86/kernel/amd_iommu.c +++ b/arch/x86/kernel/amd_iommu.c @@ -572,7 +572,7 @@ static void dma_ops_reserve_addresses(struct dma_ops_domain *dom, if (start_page + pages > last_page) pages = last_page - start_page; - set_bit_string(dom->bitmap, start_page, pages); + iommu_area_reserve(dom->bitmap, start_page, pages); } static void dma_ops_free_pagetable(struct dma_ops_domain *dma_dom) diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index fe7695e4caae..080d1d27f37a 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -261,7 +261,7 @@ static void iommu_range_reserve(struct iommu_table *tbl, badbit, tbl, start_addr, npages); } - set_bit_string(tbl->it_map, index, npages); + iommu_area_reserve(tbl->it_map, index, npages); spin_unlock_irqrestore(&tbl->it_lock, flags); } diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c index 508ef470b27f..3dcb1ad86e38 100644 --- a/arch/x86/kernel/pci-gart_64.c +++ b/arch/x86/kernel/pci-gart_64.c @@ -827,7 +827,7 @@ void __init gart_iommu_init(void) * Out of IOMMU space handling. * Reserve some invalid pages at the beginning of the GART. */ - set_bit_string(iommu_gart_bitmap, 0, EMERGENCY_PAGES); + iommu_area_reserve(iommu_gart_bitmap, 0, EMERGENCY_PAGES); agp_memory_reserved = iommu_size; printk(KERN_INFO diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h index 58f41107e4ae..786539e432d7 100644 --- a/include/linux/iommu-helper.h +++ b/include/linux/iommu-helper.h @@ -11,6 +11,7 @@ static inline unsigned long iommu_device_max_index(unsigned long size, extern int iommu_is_span_boundary(unsigned int index, unsigned int nr, unsigned long shift, unsigned long boundary_size); +extern void iommu_area_reserve(unsigned long *map, unsigned long i, int len); extern unsigned long iommu_area_alloc(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long shift, diff --git a/lib/iommu-helper.c b/lib/iommu-helper.c index a3b8d4c3f77a..5d90074dca75 100644 --- a/lib/iommu-helper.c +++ b/lib/iommu-helper.c @@ -30,8 +30,7 @@ again: return index; } -static inline void set_bit_area(unsigned long *map, unsigned long i, - int len) +void iommu_area_reserve(unsigned long *map, unsigned long i, int len) { unsigned long end = i + len; while (i < end) { @@ -64,7 +63,7 @@ again: start = index + 1; goto again; } - set_bit_area(map, index, nr); + iommu_area_reserve(map, index, nr); } return index; } -- cgit v1.2.3 From 5c1824587f0797373c95719a196f6098f7c6d20c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 22 Sep 2008 19:48:19 -0700 Subject: ipsec: Fix xfrm_state_walk race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As discovered by Timo Teräs, the currently xfrm_state_walk scheme is racy because if a second dump finishes before the first, we may free xfrm states that the first dump would walk over later. This patch fixes this by storing the dumps in a list in order to calculate the correct completion counter which cures this problem. I've expanded netlink_cb in order to accomodate the extra state related to this. It shouldn't be a big deal since netlink_cb is kmalloced for each dump and we're just increasing it by 4 or 8 bytes. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netlink.h | 2 +- include/net/xfrm.h | 10 +++------- net/xfrm/xfrm_state.c | 39 ++++++++++++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 9ff1b54908f3..cbba7760545b 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -220,7 +220,7 @@ struct netlink_callback int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); int family; - long args[6]; + long args[7]; }; struct netlink_notify diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 4bb94992b5fa..48630b266593 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1246,6 +1246,8 @@ struct xfrm6_tunnel { }; struct xfrm_state_walk { + struct list_head list; + unsigned long genid; struct xfrm_state *state; int count; u8 proto; @@ -1281,13 +1283,7 @@ static inline void xfrm6_fini(void) extern int xfrm_proc_init(void); #endif -static inline void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) -{ - walk->proto = proto; - walk->state = NULL; - walk->count = 0; -} - +extern void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto); extern int xfrm_state_walk(struct xfrm_state_walk *walk, int (*func)(struct xfrm_state *, int, void*), void *); extern void xfrm_state_walk_done(struct xfrm_state_walk *walk); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index abbe2702c400..053970e8765d 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -64,6 +64,9 @@ static unsigned long xfrm_state_walk_ongoing; /* Counter indicating walk completion, protected by xfrm_cfg_mutex. */ static unsigned long xfrm_state_walk_completed; +/* List of outstanding state walks used to set the completed counter. */ +static LIST_HEAD(xfrm_state_walks); + static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); @@ -1584,7 +1587,6 @@ int xfrm_state_walk(struct xfrm_state_walk *walk, if (err) { xfrm_state_hold(last); walk->state = last; - xfrm_state_walk_ongoing++; goto out; } } @@ -1599,25 +1601,44 @@ int xfrm_state_walk(struct xfrm_state_walk *walk, err = func(last, 0, data); out: spin_unlock_bh(&xfrm_state_lock); - if (old != NULL) { + if (old != NULL) xfrm_state_put(old); - xfrm_state_walk_completed++; - if (!list_empty(&xfrm_state_gc_leftovers)) - schedule_work(&xfrm_state_gc_work); - } return err; } EXPORT_SYMBOL(xfrm_state_walk); +void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) +{ + walk->proto = proto; + walk->state = NULL; + walk->count = 0; + list_add_tail(&walk->list, &xfrm_state_walks); + walk->genid = ++xfrm_state_walk_ongoing; +} +EXPORT_SYMBOL(xfrm_state_walk_init); + void xfrm_state_walk_done(struct xfrm_state_walk *walk) { + struct list_head *prev; + if (walk->state != NULL) { xfrm_state_put(walk->state); walk->state = NULL; - xfrm_state_walk_completed++; - if (!list_empty(&xfrm_state_gc_leftovers)) - schedule_work(&xfrm_state_gc_work); } + + prev = walk->list.prev; + list_del(&walk->list); + + if (prev != &xfrm_state_walks) { + list_entry(prev, struct xfrm_state_walk, list)->genid = + walk->genid; + return; + } + + xfrm_state_walk_completed = walk->genid; + + if (!list_empty(&xfrm_state_gc_leftovers)) + schedule_work(&xfrm_state_gc_work); } EXPORT_SYMBOL(xfrm_state_walk_done); -- cgit v1.2.3 From bce7b15426cac3000bf6a9cf59d9356ef0be2dec Mon Sep 17 00:00:00 2001 From: Remi Denis-Courmont Date: Mon, 22 Sep 2008 19:51:15 -0700 Subject: Phonet: global definitions Signed-off-by: Remi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/if_ether.h | 1 + include/linux/if_phonet.h | 14 ++++++ include/linux/phonet.h | 125 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/rtnetlink.h | 4 ++ include/linux/socket.h | 4 +- net/core/sock.c | 9 ++-- 6 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 include/linux/if_phonet.h create mode 100644 include/linux/phonet.h (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 5028e0b6082b..723a1c5fbc6c 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -100,6 +100,7 @@ #define ETH_P_ECONET 0x0018 /* Acorn Econet */ #define ETH_P_HDLC 0x0019 /* HDLC frames */ #define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ +#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ /* * This is an Ethernet frame header. diff --git a/include/linux/if_phonet.h b/include/linux/if_phonet.h new file mode 100644 index 000000000000..22df25fbc4e2 --- /dev/null +++ b/include/linux/if_phonet.h @@ -0,0 +1,14 @@ +/* + * File: if_phonet.h + * + * Phonet interface kernel definitions + * + * Copyright (C) 2008 Nokia Corporation. All rights reserved. + */ + +#define PHONET_HEADER_LEN 8 /* Phonet header length */ + +#define PHONET_MIN_MTU 6 +/* 6 bytes header + 65535 bytes payload */ +#define PHONET_MAX_MTU 65541 +#define PHONET_DEV_MTU PHONET_MAX_MTU diff --git a/include/linux/phonet.h b/include/linux/phonet.h new file mode 100644 index 000000000000..6a764f8584a4 --- /dev/null +++ b/include/linux/phonet.h @@ -0,0 +1,125 @@ +/** + * file phonet.h + * + * Phonet sockets kernel interface + * + * Copyright (C) 2008 Nokia Corporation. All rights reserved. + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef LINUX_PHONET_H +#define LINUX_PHONET_H + +/* Automatic protocol selection */ +#define PN_PROTO_TRANSPORT 0 +/* Phonet datagram socket */ +#define PN_PROTO_PHONET 1 +#define PHONET_NPROTO 2 + +#define PNADDR_ANY 0 +#define PNPORT_RESOURCE_ROUTING 0 + +/* Phonet protocol header */ +struct phonethdr { + __u8 pn_rdev; + __u8 pn_sdev; + __u8 pn_res; + __be16 pn_length; + __u8 pn_robj; + __u8 pn_sobj; +} __attribute__((packed)); + +/* Phonet socket address structure */ +struct sockaddr_pn { + sa_family_t spn_family; + __u8 spn_obj; + __u8 spn_dev; + __u8 spn_resource; + __u8 spn_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - 3]; +} __attribute__ ((packed)); + +static inline __u16 pn_object(__u8 addr, __u16 port) +{ + return (addr << 8) | (port & 0x3ff); +} + +static inline __u8 pn_obj(__u16 handle) +{ + return handle & 0xff; +} + +static inline __u8 pn_dev(__u16 handle) +{ + return handle >> 8; +} + +static inline __u16 pn_port(__u16 handle) +{ + return handle & 0x3ff; +} + +static inline __u8 pn_addr(__u16 handle) +{ + return (handle >> 8) & 0xfc; +} + +static inline void pn_sockaddr_set_addr(struct sockaddr_pn *spn, __u8 addr) +{ + spn->spn_dev &= 0x03; + spn->spn_dev |= addr & 0xfc; +} + +static inline void pn_sockaddr_set_port(struct sockaddr_pn *spn, __u16 port) +{ + spn->spn_dev &= 0xfc; + spn->spn_dev |= (port >> 8) & 0x03; + spn->spn_obj = port & 0xff; +} + +static inline void pn_sockaddr_set_object(struct sockaddr_pn *spn, + __u16 handle) +{ + spn->spn_dev = pn_dev(handle); + spn->spn_obj = pn_obj(handle); +} + +static inline void pn_sockaddr_set_resource(struct sockaddr_pn *spn, + __u8 resource) +{ + spn->spn_resource = resource; +} + +static inline __u8 pn_sockaddr_get_addr(const struct sockaddr_pn *spn) +{ + return spn->spn_dev & 0xfc; +} + +static inline __u16 pn_sockaddr_get_port(const struct sockaddr_pn *spn) +{ + return ((spn->spn_dev & 0x03) << 8) | spn->spn_obj; +} + +static inline __u16 pn_sockaddr_get_object(const struct sockaddr_pn *spn) +{ + return pn_object(spn->spn_dev, spn->spn_obj); +} + +static inline __u8 pn_sockaddr_get_resource(const struct sockaddr_pn *spn) +{ + return spn->spn_resource; +} + +#endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index ca643b13b026..2b3d51c6ec9c 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -582,6 +582,10 @@ enum rtnetlink_groups { #define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE RTNLGRP_ND_USEROPT, #define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) diff --git a/include/linux/socket.h b/include/linux/socket.h index dc5086fe7736..818ca33bf79f 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -190,7 +190,8 @@ struct ucred { #define AF_IUCV 32 /* IUCV sockets */ #define AF_RXRPC 33 /* RxRPC sockets */ #define AF_ISDN 34 /* mISDN sockets */ -#define AF_MAX 35 /* For now.. */ +#define AF_PHONET 35 /* Phonet sockets */ +#define AF_MAX 36 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -227,6 +228,7 @@ struct ucred { #define PF_IUCV AF_IUCV #define PF_RXRPC AF_RXRPC #define PF_ISDN AF_ISDN +#define PF_PHONET AF_PHONET #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ diff --git a/net/core/sock.c b/net/core/sock.c index 23b8b9da36b3..2d358dd8a03e 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -154,7 +154,8 @@ static const char *af_family_key_strings[AF_MAX+1] = { "sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" , "sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" , "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" , - "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_MAX" + "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" , + "sk_lock-AF_MAX" }; static const char *af_family_slock_key_strings[AF_MAX+1] = { "slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" , @@ -168,7 +169,8 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = { "slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" , "slock-27" , "slock-28" , "slock-AF_CAN" , "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" , - "slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_MAX" + "slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" , + "slock-AF_MAX" }; static const char *af_family_clock_key_strings[AF_MAX+1] = { "clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" , @@ -182,7 +184,8 @@ static const char *af_family_clock_key_strings[AF_MAX+1] = { "clock-AF_PPPOX" , "clock-AF_WANPIPE" , "clock-AF_LLC" , "clock-27" , "clock-28" , "clock-AF_CAN" , "clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" , - "clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_MAX" + "clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" , + "clock-AF_MAX" }; #endif -- cgit v1.2.3 From ba113a94b7503ee23ffe819e7045134b0c1d31de Mon Sep 17 00:00:00 2001 From: Remi Denis-Courmont Date: Mon, 22 Sep 2008 20:05:19 -0700 Subject: Phonet: common socket glue This provides the socket API for the Phonet protocols family. Signed-off-by: Remi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/phonet.h | 3 + include/net/phonet/phonet.h | 23 ++++ net/phonet/Makefile | 1 + net/phonet/af_phonet.c | 28 +++- net/phonet/socket.c | 311 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 net/phonet/socket.c (limited to 'include/linux') diff --git a/include/linux/phonet.h b/include/linux/phonet.h index 6a764f8584a4..001c0e679099 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -32,6 +32,9 @@ #define PNADDR_ANY 0 #define PNPORT_RESOURCE_ROUTING 0 +/* ioctls */ +#define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0) + /* Phonet protocol header */ struct phonethdr { __u8 pn_rdev; diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h index 8b777943d201..2ae5cbb59b60 100644 --- a/include/net/phonet/phonet.h +++ b/include/net/phonet/phonet.h @@ -29,6 +29,28 @@ */ #define MAX_PHONET_HEADER 8 +/* + * Every Phonet* socket has this structure first in its + * protocol-specific structure under name c. + */ +struct pn_sock { + struct sock sk; + u16 sobject; + u8 resource; +}; + +static inline struct pn_sock *pn_sk(struct sock *sk) +{ + return (struct pn_sock *)sk; +} + +extern const struct proto_ops phonet_dgram_ops; + +struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *sa); +void pn_sock_hash(struct sock *sk); +void pn_sock_unhash(struct sock *sk); +int pn_sock_get_port(struct sock *sk, unsigned short sport); + static inline struct phonethdr *pn_hdr(struct sk_buff *skb) { return (struct phonethdr *)skb_network_header(skb); @@ -64,6 +86,7 @@ void pn_skb_get_dst_sockaddr(struct sk_buff *skb, struct sockaddr_pn *sa) /* Protocols in Phonet protocol family. */ struct phonet_protocol { + const struct proto_ops *ops; struct proto *prot; int sock_type; }; diff --git a/net/phonet/Makefile b/net/phonet/Makefile index 4143c3e1dfdc..c1d671de7835 100644 --- a/net/phonet/Makefile +++ b/net/phonet/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_PHONET) += phonet.o phonet-objs := \ pn_dev.o \ pn_netlink.o \ + socket.o \ af_phonet.o diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 5c729ba56939..ba54d53020ff 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -41,6 +41,8 @@ static inline void phonet_proto_put(struct phonet_protocol *pp); static int pn_socket_create(struct net *net, struct socket *sock, int protocol) { + struct sock *sk; + struct pn_sock *pn; struct phonet_protocol *pnp; int err; @@ -69,8 +71,22 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol) goto out; } - /* TODO: create and init the struct sock */ - err = -EPROTONOSUPPORT; + sk = sk_alloc(net, PF_PHONET, GFP_KERNEL, pnp->prot); + if (sk == NULL) { + err = -ENOMEM; + goto out; + } + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = pnp->ops; + sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; + sk->sk_protocol = protocol; + pn = pn_sk(sk); + pn->sobject = 0; + pn->resource = 0; + sk->sk_prot->init(sk); + err = 0; out: phonet_proto_put(pnp); @@ -94,6 +110,7 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, struct net_device *orig_dev) { struct phonethdr *ph; + struct sock *sk; struct sockaddr_pn sa; u16 len; @@ -118,7 +135,12 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, if (pn_sockaddr_get_addr(&sa) == 0) goto out; /* currently, we cannot be device 0 */ - /* TODO: put packets to sockets backlog */ + sk = pn_find_sock_by_sa(&sa); + if (sk == NULL) + goto out; + + /* Push data to the socket (or other sockets connected to it). */ + return sk_receive_skb(sk, skb, 0); out: kfree_skb(skb); diff --git a/net/phonet/socket.c b/net/phonet/socket.c new file mode 100644 index 000000000000..99a4945d565d --- /dev/null +++ b/net/phonet/socket.c @@ -0,0 +1,311 @@ +/* + * File: socket.c + * + * Phonet sockets + * + * Copyright (C) 2008 Nokia Corporation. + * + * Contact: Remi Denis-Courmont + * Original author: Sakari Ailus + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include + +#include +#include +#include + +static int pn_socket_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (sk) { + sock->sk = NULL; + sk->sk_prot->close(sk, 0); + } + return 0; +} + +static struct { + struct hlist_head hlist; + spinlock_t lock; +} pnsocks = { + .hlist = HLIST_HEAD_INIT, + .lock = __SPIN_LOCK_UNLOCKED(pnsocks.lock), +}; + +/* + * Find address based on socket address, match only certain fields. + * Also grab sock if it was found. Remember to sock_put it later. + */ +struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn) +{ + struct hlist_node *node; + struct sock *sknode; + struct sock *rval = NULL; + u16 obj = pn_sockaddr_get_object(spn); + u8 res = spn->spn_resource; + + spin_lock_bh(&pnsocks.lock); + + sk_for_each(sknode, node, &pnsocks.hlist) { + struct pn_sock *pn = pn_sk(sknode); + BUG_ON(!pn->sobject); /* unbound socket */ + + if (pn_port(obj)) { + /* Look up socket by port */ + if (pn_port(pn->sobject) != pn_port(obj)) + continue; + } else { + /* If port is zero, look up by resource */ + if (pn->resource != res) + continue; + } + if (pn_addr(pn->sobject) + && pn_addr(pn->sobject) != pn_addr(obj)) + continue; + + rval = sknode; + sock_hold(sknode); + break; + } + + spin_unlock_bh(&pnsocks.lock); + + return rval; + +} + +void pn_sock_hash(struct sock *sk) +{ + spin_lock_bh(&pnsocks.lock); + sk_add_node(sk, &pnsocks.hlist); + spin_unlock_bh(&pnsocks.lock); +} +EXPORT_SYMBOL(pn_sock_hash); + +void pn_sock_unhash(struct sock *sk) +{ + spin_lock_bh(&pnsocks.lock); + sk_del_node_init(sk); + spin_unlock_bh(&pnsocks.lock); +} +EXPORT_SYMBOL(pn_sock_unhash); + +static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len) +{ + struct sock *sk = sock->sk; + struct pn_sock *pn = pn_sk(sk); + struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; + int err; + u16 handle; + u8 saddr; + + if (sk->sk_prot->bind) + return sk->sk_prot->bind(sk, addr, len); + + if (len < sizeof(struct sockaddr_pn)) + return -EINVAL; + if (spn->spn_family != AF_PHONET) + return -EAFNOSUPPORT; + + handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr); + saddr = pn_addr(handle); + if (saddr && phonet_address_lookup(saddr)) + return -EADDRNOTAVAIL; + + lock_sock(sk); + if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) { + err = -EINVAL; /* attempt to rebind */ + goto out; + } + err = sk->sk_prot->get_port(sk, pn_port(handle)); + if (err) + goto out; + + /* get_port() sets the port, bind() sets the address if applicable */ + pn->sobject = pn_object(saddr, pn_port(pn->sobject)); + pn->resource = spn->spn_resource; + + /* Enable RX on the socket */ + sk->sk_prot->hash(sk); +out: + release_sock(sk); + return err; +} + +static int pn_socket_autobind(struct socket *sock) +{ + struct sockaddr_pn sa; + int err; + + memset(&sa, 0, sizeof(sa)); + sa.spn_family = AF_PHONET; + err = pn_socket_bind(sock, (struct sockaddr *)&sa, + sizeof(struct sockaddr_pn)); + if (err != -EINVAL) + return err; + BUG_ON(!pn_port(pn_sk(sock->sk)->sobject)); + return 0; /* socket was already bound */ +} + +static int pn_socket_getname(struct socket *sock, struct sockaddr *addr, + int *sockaddr_len, int peer) +{ + struct sock *sk = sock->sk; + struct pn_sock *pn = pn_sk(sk); + + memset(addr, 0, sizeof(struct sockaddr_pn)); + addr->sa_family = AF_PHONET; + if (!peer) /* Race with bind() here is userland's problem. */ + pn_sockaddr_set_object((struct sockaddr_pn *)addr, + pn->sobject); + + *sockaddr_len = sizeof(struct sockaddr_pn); + return 0; +} + +static int pn_socket_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + struct pn_sock *pn = pn_sk(sk); + + if (cmd == SIOCPNGETOBJECT) { + struct net_device *dev; + u16 handle; + u8 saddr; + + if (get_user(handle, (__u16 __user *)arg)) + return -EFAULT; + + lock_sock(sk); + if (sk->sk_bound_dev_if) + dev = dev_get_by_index(sock_net(sk), + sk->sk_bound_dev_if); + else + dev = phonet_device_get(sock_net(sk)); + if (dev && (dev->flags & IFF_UP)) + saddr = phonet_address_get(dev, pn_addr(handle)); + else + saddr = PN_NO_ADDR; + release_sock(sk); + + if (dev) + dev_put(dev); + if (saddr == PN_NO_ADDR) + return -EHOSTUNREACH; + + handle = pn_object(saddr, pn_port(pn->sobject)); + return put_user(handle, (__u16 __user *)arg); + } + + return sk->sk_prot->ioctl(sk, cmd, arg); +} + +static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t total_len) +{ + struct sock *sk = sock->sk; + + if (pn_socket_autobind(sock)) + return -EAGAIN; + + return sk->sk_prot->sendmsg(iocb, sk, m, total_len); +} + +const struct proto_ops phonet_dgram_ops = { + .family = AF_PHONET, + .owner = THIS_MODULE, + .release = pn_socket_release, + .bind = pn_socket_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = pn_socket_getname, + .poll = datagram_poll, + .ioctl = pn_socket_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = sock_no_setsockopt, + .compat_getsockopt = sock_no_getsockopt, +#endif + .sendmsg = pn_socket_sendmsg, + .recvmsg = sock_common_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static DEFINE_MUTEX(port_mutex); + +/* allocate port for a socket */ +int pn_sock_get_port(struct sock *sk, unsigned short sport) +{ + static int port_cur; + struct pn_sock *pn = pn_sk(sk); + struct sockaddr_pn try_sa; + struct sock *tmpsk; + + memset(&try_sa, 0, sizeof(struct sockaddr_pn)); + try_sa.spn_family = AF_PHONET; + + mutex_lock(&port_mutex); + + if (!sport) { + /* search free port */ + int port, pmin = 0x40, pmax = 0x7f; + + for (port = pmin; port <= pmax; port++) { + port_cur++; + if (port_cur < pmin || port_cur > pmax) + port_cur = pmin; + + pn_sockaddr_set_port(&try_sa, port_cur); + tmpsk = pn_find_sock_by_sa(&try_sa); + if (tmpsk == NULL) { + sport = port_cur; + goto found; + } else + sock_put(tmpsk); + } + } else { + /* try to find specific port */ + pn_sockaddr_set_port(&try_sa, sport); + tmpsk = pn_find_sock_by_sa(&try_sa); + if (tmpsk == NULL) + /* No sock there! We can use that port... */ + goto found; + else + sock_put(tmpsk); + } + mutex_unlock(&port_mutex); + + /* the port must be in use already */ + return -EADDRINUSE; + +found: + mutex_unlock(&port_mutex); + pn->sobject = pn_object(pn_addr(pn->sobject), sport); + return 0; +} +EXPORT_SYMBOL(pn_sock_get_port); -- cgit v1.2.3 From 5f77076d75d35c9f5619e1f9d7e7428a627f65e6 Mon Sep 17 00:00:00 2001 From: Remi Denis-Courmont Date: Mon, 22 Sep 2008 20:08:04 -0700 Subject: Phonet: provide MAC header operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/if_phonet.h | 4 ++++ net/phonet/af_phonet.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include/linux') diff --git a/include/linux/if_phonet.h b/include/linux/if_phonet.h index 22df25fbc4e2..7e989216ec17 100644 --- a/include/linux/if_phonet.h +++ b/include/linux/if_phonet.h @@ -12,3 +12,7 @@ /* 6 bytes header + 65535 bytes payload */ #define PHONET_MAX_MTU 65541 #define PHONET_DEV_MTU PHONET_MAX_MTU + +#ifdef __KERNEL__ +extern struct header_ops phonet_header_ops; +#endif diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index e6771d3961cf..51397ff308bd 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -99,6 +99,35 @@ static struct net_proto_family phonet_proto_family = { .owner = THIS_MODULE, }; +/* Phonet device header operations */ +static int pn_header_create(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned len) +{ + u8 *media = skb_push(skb, 1); + + if (type != ETH_P_PHONET) + return -1; + + if (!saddr) + saddr = dev->dev_addr; + *media = *(const u8 *)saddr; + return 1; +} + +static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + const u8 *media = skb_mac_header(skb); + *haddr = *media; + return 1; +} + +struct header_ops phonet_header_ops = { + .create = pn_header_create, + .parse = pn_header_parse, +}; +EXPORT_SYMBOL(phonet_header_ops); + /* * Prepends an ISI header and sends a datagram. */ -- cgit v1.2.3 From be0c52bfed7f7828494fa00060efd5d758e92580 Mon Sep 17 00:00:00 2001 From: Remi Denis-Courmont Date: Mon, 22 Sep 2008 20:09:13 -0700 Subject: Phonet: emit errors when a packet cannot be delivered locally When there is no listener socket for a received packet, send an error back to the sender. Signed-off-by: Remi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/phonet.h | 32 +++++++++++++++ include/net/phonet/phonet.h | 5 +++ net/phonet/af_phonet.c | 96 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/phonet.h b/include/linux/phonet.h index 001c0e679099..3a027f588a4a 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -45,6 +45,38 @@ struct phonethdr { __u8 pn_sobj; } __attribute__((packed)); +/* Common Phonet payload header */ +struct phonetmsg { + __u8 pn_trans_id; /* transaction ID */ + __u8 pn_msg_id; /* message type */ + union { + struct { + __u8 pn_submsg_id; /* message subtype */ + __u8 pn_data[5]; + } base; + struct { + __u16 pn_e_res_id; /* extended resource ID */ + __u8 pn_e_submsg_id; /* message subtype */ + __u8 pn_e_data[3]; + } ext; + } pn_msg_u; +}; +#define PN_COMMON_MESSAGE 0xF0 +#define PN_PREFIX 0xE0 /* resource for extended messages */ +#define pn_submsg_id pn_msg_u.base.pn_submsg_id +#define pn_e_submsg_id pn_msg_u.ext.pn_e_submsg_id +#define pn_e_res_id pn_msg_u.ext.pn_e_res_id +#define pn_data pn_msg_u.base.pn_data +#define pn_e_data pn_msg_u.ext.pn_e_data + +/* data for unreachable errors */ +#define PN_COMM_SERVICE_NOT_IDENTIFIED_RESP 0x01 +#define PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP 0x14 +#define pn_orig_msg_id pn_data[0] +#define pn_status pn_data[1] +#define pn_e_orig_msg_id pn_e_data[0] +#define pn_e_status pn_e_data[1] + /* Phonet socket address structure */ struct sockaddr_pn { sa_family_t spn_family; diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h index 1c6f7e7d5fea..d4e72508e145 100644 --- a/include/net/phonet/phonet.h +++ b/include/net/phonet/phonet.h @@ -60,6 +60,11 @@ static inline struct phonethdr *pn_hdr(struct sk_buff *skb) return (struct phonethdr *)skb_network_header(skb); } +static inline struct phonetmsg *pn_msg(struct sk_buff *skb) +{ + return (struct phonetmsg *)skb_transport_header(skb); +} + /* * Get the other party's sockaddr from received skb. The skb begins * with a Phonet header. diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 50dc258d5aa2..1d8df6b7e3df 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -132,7 +132,7 @@ EXPORT_SYMBOL(phonet_header_ops); * Prepends an ISI header and sends a datagram. */ static int pn_send(struct sk_buff *skb, struct net_device *dev, - u16 dst, u16 src, u8 res) + u16 dst, u16 src, u8 res, u8 irq) { struct phonethdr *ph; int err; @@ -163,7 +163,10 @@ static int pn_send(struct sk_buff *skb, struct net_device *dev, skb_reset_mac_header(skb); skb->pkt_type = PACKET_LOOPBACK; skb_orphan(skb); - netif_rx_ni(skb); + if (irq) + netif_rx(skb); + else + netif_rx_ni(skb); err = 0; } else { err = dev_hard_header(skb, dev, ntohs(skb->protocol), @@ -181,6 +184,19 @@ drop: return err; } +static int pn_raw_send(const void *data, int len, struct net_device *dev, + u16 dst, u16 src, u8 res) +{ + struct sk_buff *skb = alloc_skb(MAX_PHONET_HEADER + len, GFP_ATOMIC); + if (skb == NULL) + return -ENOMEM; + + skb_reserve(skb, MAX_PHONET_HEADER); + __skb_put(skb, len); + skb_copy_to_linear_data(skb, data, len); + return pn_send(skb, dev, dst, src, res, 1); +} + /* * Create a Phonet header for the skb and send it out. Returns * non-zero error code if failed. The skb is freed then. @@ -211,7 +227,7 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb, src = pn_object(saddr, pn_obj(src)); err = pn_send(skb, dev, pn_sockaddr_get_object(target), - src, pn_sockaddr_get_resource(target)); + src, pn_sockaddr_get_resource(target), 0); dev_put(dev); return err; @@ -223,6 +239,73 @@ drop: } EXPORT_SYMBOL(pn_skb_send); +/* Do not send an error message in response to an error message */ +static inline int can_respond(struct sk_buff *skb) +{ + const struct phonethdr *ph; + const struct phonetmsg *pm; + u8 submsg_id; + + if (!pskb_may_pull(skb, 3)) + return 0; + + ph = pn_hdr(skb); + if (phonet_address_get(skb->dev, ph->pn_rdev) != ph->pn_rdev) + return 0; /* we are not the destination */ + if (ph->pn_res == PN_PREFIX && !pskb_may_pull(skb, 5)) + return 0; + + ph = pn_hdr(skb); /* re-acquires the pointer */ + pm = pn_msg(skb); + if (pm->pn_msg_id != PN_COMMON_MESSAGE) + return 1; + submsg_id = (ph->pn_res == PN_PREFIX) + ? pm->pn_e_submsg_id : pm->pn_submsg_id; + if (submsg_id != PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP && + pm->pn_e_submsg_id != PN_COMM_SERVICE_NOT_IDENTIFIED_RESP) + return 1; + return 0; +} + +static int send_obj_unreachable(struct sk_buff *rskb) +{ + const struct phonethdr *oph = pn_hdr(rskb); + const struct phonetmsg *opm = pn_msg(rskb); + struct phonetmsg resp; + + memset(&resp, 0, sizeof(resp)); + resp.pn_trans_id = opm->pn_trans_id; + resp.pn_msg_id = PN_COMMON_MESSAGE; + if (oph->pn_res == PN_PREFIX) { + resp.pn_e_res_id = opm->pn_e_res_id; + resp.pn_e_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP; + resp.pn_e_orig_msg_id = opm->pn_msg_id; + resp.pn_e_status = 0; + } else { + resp.pn_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP; + resp.pn_orig_msg_id = opm->pn_msg_id; + resp.pn_status = 0; + } + return pn_raw_send(&resp, sizeof(resp), rskb->dev, + pn_object(oph->pn_sdev, oph->pn_sobj), + pn_object(oph->pn_rdev, oph->pn_robj), + oph->pn_res); +} + +static int send_reset_indications(struct sk_buff *rskb) +{ + struct phonethdr *oph = pn_hdr(rskb); + static const u8 data[4] = { + 0x00 /* trans ID */, 0x10 /* subscribe msg */, + 0x00 /* subscription count */, 0x00 /* dummy */ + }; + + return pn_raw_send(data, sizeof(data), rskb->dev, + pn_object(oph->pn_sdev, 0x00), + pn_object(oph->pn_rdev, oph->pn_robj), 0x10); +} + + /* packet type functions */ /* @@ -260,8 +343,13 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, goto out; /* currently, we cannot be device 0 */ sk = pn_find_sock_by_sa(&sa); - if (sk == NULL) + if (sk == NULL) { + if (can_respond(skb)) { + send_obj_unreachable(skb); + send_reset_indications(skb); + } goto out; + } /* Push data to the socket (or other sockets connected to it). */ return sk_receive_skb(sk, skb, 0); -- cgit v1.2.3 From 0b815a1a6d43ab498674b8430c8c35ab08487a16 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 22 Sep 2008 21:28:11 -0700 Subject: net: network device name ifalias support This patch add support for keeping an additional character alias associated with an network interface. This is useful for maintaining the SNMP ifAlias value which is a user defined value. Routers use this to hold information like which circuit or line it is connected to. It is just an arbitrary text label on the network device. There are two exposed interfaces with this patch, the value can be read/written either via netlink or sysfs. This could be maintained just by the snmp daemon, but it is more generally useful for other management tools, and the kernel is good place to act as an agreed upon interface to store it. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/if.h | 1 + include/linux/if_link.h | 1 + include/linux/netdevice.h | 3 +++ net/core/dev.c | 23 +++++++++++++++++++++++ net/core/net-sysfs.c | 36 ++++++++++++++++++++++++++++++++++++ net/core/rtnetlink.c | 13 +++++++++++++ 6 files changed, 77 insertions(+) (limited to 'include/linux') diff --git a/include/linux/if.h b/include/linux/if.h index 5c9d1fa93fef..65246846c844 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -24,6 +24,7 @@ #include /* for "__user" et al */ #define IFNAMSIZ 16 +#define IFALIASZ 256 #include /* Standard interface flags (netdevice->flags). */ diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 84c3492ae5cb..f9032c88716a 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -79,6 +79,7 @@ enum IFLA_LINKINFO, #define IFLA_LINKINFO IFLA_LINKINFO IFLA_NET_NS_PID, + IFLA_IFALIAS, __IFLA_MAX }; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 488c56e649b5..d675df08b946 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -471,6 +471,8 @@ struct net_device char name[IFNAMSIZ]; /* device name hash chain */ struct hlist_node name_hlist; + /* snmp alias */ + char *ifalias; /* * I/O specific fields @@ -1224,6 +1226,7 @@ extern int dev_ethtool(struct net *net, struct ifreq *); extern unsigned dev_get_flags(const struct net_device *); extern int dev_change_flags(struct net_device *, unsigned); extern int dev_change_name(struct net_device *, char *); +extern int dev_set_alias(struct net_device *, const char *, size_t); extern int dev_change_net_namespace(struct net_device *, struct net *, const char *); extern int dev_set_mtu(struct net_device *, int); diff --git a/net/core/dev.c b/net/core/dev.c index fdfc4b6a6448..e91390533999 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -953,6 +953,29 @@ rollback: return err; } +/** + * dev_set_alias - change ifalias of a device + * @dev: device + * @alias: name up to IFALIASZ + * + * Set ifalias for a device, + */ +int dev_set_alias(struct net_device *dev, const char *alias, size_t len) +{ + ASSERT_RTNL(); + + if (len >= IFALIASZ) + return -EINVAL; + + dev->ifalias = krealloc(dev->ifalias, len+1, GFP_KERNEL); + if (!dev->ifalias) + return -ENOMEM; + + strlcpy(dev->ifalias, alias, len+1); + return len; +} + + /** * netdev_features_change - device changes features * @dev: device to cause notification diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index c1f4e0d428c0..92d6b9467314 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -209,9 +209,44 @@ static ssize_t store_tx_queue_len(struct device *dev, return netdev_store(dev, attr, buf, len, change_tx_queue_len); } +static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct net_device *netdev = to_net_dev(dev); + size_t count = len; + ssize_t ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* ignore trailing newline */ + if (len > 0 && buf[len - 1] == '\n') + --count; + + rtnl_lock(); + ret = dev_set_alias(netdev, buf, count); + rtnl_unlock(); + + return ret < 0 ? ret : len; +} + +static ssize_t show_ifalias(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct net_device *netdev = to_net_dev(dev); + ssize_t ret = 0; + + rtnl_lock(); + if (netdev->ifalias) + ret = sprintf(buf, "%s\n", netdev->ifalias); + rtnl_unlock(); + return ret; +} + static struct device_attribute net_class_attributes[] = { __ATTR(addr_len, S_IRUGO, show_addr_len, NULL), __ATTR(dev_id, S_IRUGO, show_dev_id, NULL), + __ATTR(ifalias, S_IRUGO | S_IWUSR, show_ifalias, store_ifalias), __ATTR(iflink, S_IRUGO, show_iflink, NULL), __ATTR(ifindex, S_IRUGO, show_ifindex, NULL), __ATTR(features, S_IRUGO, show_features, NULL), @@ -418,6 +453,7 @@ static void netdev_release(struct device *d) BUG_ON(dev->reg_state != NETREG_RELEASED); + kfree(dev->ifalias); kfree((char *)dev - dev->padded); } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 71edb8b36341..8862498fd4a6 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -586,6 +586,7 @@ static inline size_t if_nlmsg_size(const struct net_device *dev) { return NLMSG_ALIGN(sizeof(struct ifinfomsg)) + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ + + nla_total_size(IFALIASZ) /* IFLA_IFALIAS */ + nla_total_size(IFNAMSIZ) /* IFLA_QDISC */ + nla_total_size(sizeof(struct rtnl_link_ifmap)) + nla_total_size(sizeof(struct rtnl_link_stats)) @@ -640,6 +641,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (txq->qdisc_sleeping) NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); + if (dev->ifalias) + NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias); + if (1) { struct rtnl_link_ifmap map = { .mem_start = dev->mem_start, @@ -713,6 +717,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINKMODE] = { .type = NLA_U8 }, [IFLA_LINKINFO] = { .type = NLA_NESTED }, [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .len = IFALIASZ-1 }, }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -853,6 +858,14 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, modified = 1; } + if (tb[IFLA_IFALIAS]) { + err = dev_set_alias(dev, nla_data(tb[IFLA_IFALIAS]), + nla_len(tb[IFLA_IFALIAS])); + if (err < 0) + goto errout; + modified = 1; + } + if (tb[IFLA_BROADCAST]) { nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); send_addr_notify = 1; -- cgit v1.2.3 From 1d4a31dde95af56edac4dee268249a29a21fa7c0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 22 Sep 2008 21:57:21 -0700 Subject: net: Fix bus in SKB queue splicing interfaces. Handle the case of head being non-empty, by adding list->qlen to head->qlen instead of using direct assignment. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 027b06170b40..4a144e8d0538 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -738,7 +738,7 @@ static inline void skb_queue_splice(const struct sk_buff_head *list, { if (!skb_queue_empty(list)) { __skb_queue_splice(list, (struct sk_buff *) head, head->next); - head->qlen = list->qlen; + head->qlen += list->qlen; } } @@ -754,7 +754,7 @@ static inline void skb_queue_splice_init(struct sk_buff_head *list, { if (!skb_queue_empty(list)) { __skb_queue_splice(list, (struct sk_buff *) head, head->next); - head->qlen = list->qlen; + head->qlen += list->qlen; __skb_queue_head_init(list); } } @@ -769,7 +769,7 @@ static inline void skb_queue_splice_tail(const struct sk_buff_head *list, { if (!skb_queue_empty(list)) { __skb_queue_splice(list, head->prev, (struct sk_buff *) head); - head->qlen = list->qlen; + head->qlen += list->qlen; } } @@ -786,7 +786,7 @@ static inline void skb_queue_splice_tail_init(struct sk_buff_head *list, { if (!skb_queue_empty(list)) { __skb_queue_splice(list, head->prev, (struct sk_buff *) head); - head->qlen = list->qlen; + head->qlen += list->qlen; __skb_queue_head_init(list); } } -- cgit v1.2.3 From fc7ebb212d3e51d1188948d975aa93dbb0f58b25 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 23 Sep 2008 00:34:07 -0700 Subject: net: Add skb_queue_is_last(). Several bits of code want to know "is this the last SKB in a queue", and all of them implement this by hand. Provide an common interface to make this check. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4a144e8d0538..3a5838da160e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -472,6 +472,19 @@ static inline int skb_queue_empty(const struct sk_buff_head *list) return list->next == (struct sk_buff *)list; } +/** + * skb_queue_is_last - check if skb is the last entry in the queue + * @list: queue head + * @skb: buffer + * + * Returns true if @skb is the last buffer on the list. + */ +static inline bool skb_queue_is_last(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + return (skb->next == (struct sk_buff *) list); +} + /** * skb_get - reference buffer * @skb: buffer to reference -- cgit v1.2.3 From 249c8b42c7e5e6f33d0ff983041f08278b137e53 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 23 Sep 2008 00:44:42 -0700 Subject: net: Add skb_queue_next(). A lot of code wants to iterate over an SKB queue at the top level using it's own control structure and iterator scheme. Provide skb_queue_next(), which is only valid to invoke if skb_queue_is_last() returns false. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 3a5838da160e..d2f1778877d7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -485,6 +485,24 @@ static inline bool skb_queue_is_last(const struct sk_buff_head *list, return (skb->next == (struct sk_buff *) list); } +/** + * skb_queue_next - return the next packet in the queue + * @list: queue head + * @skb: current buffer + * + * Return the next packet in @list after @skb. It is only valid to + * call this if skb_queue_is_last() evaluates to false. + */ +static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + /* This BUG_ON may seem severe, but if we just return then we + * are going to dereference garbage. + */ + BUG_ON(skb_queue_is_last(list, skb)); + return skb->next; +} + /** * skb_get - reference buffer * @skb: buffer to reference -- cgit v1.2.3 From 1164f52a244204830c7625b3c22812781996d7b4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 23 Sep 2008 00:49:44 -0700 Subject: net: Add skb_queue_walk_from() and skb_queue_walk_from_safe(). These will be used by TCP write queue handling and elsewhere. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d2f1778877d7..a19ea43fea02 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1571,6 +1571,15 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len) skb != (struct sk_buff *)(queue); \ skb = tmp, tmp = skb->next) +#define skb_queue_walk_from(queue, skb) \ + for (; prefetch(skb->next), (skb != (struct sk_buff *)(queue)); \ + skb = skb->next) + +#define skb_queue_walk_from_safe(queue, skb, tmp) \ + for (tmp = skb->next; \ + skb != (struct sk_buff *)(queue); \ + skb = tmp, tmp = skb->next) + #define skb_queue_reverse_walk(queue, skb) \ for (skb = (queue)->prev; \ prefetch(skb->prev), (skb != (struct sk_buff *)(queue)); \ -- cgit v1.2.3 From c32a162fd420fe8dfb049db941b2438061047fcc Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 22 Sep 2008 13:57:43 -0700 Subject: smb.h: do not include linux/time.h in userspace linux/time.h conflicts with time.h from glibc It breaks building smbmount from samba. It's regression introduced by commit 76308da (" smb.h: uses struct timespec but didn't include linux/time.h"). Signed-off-by: Kirill A. Shutemov Cc: [2.6.26.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/smb.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/smb.h b/include/linux/smb.h index caa43b2370cb..82fefddc5987 100644 --- a/include/linux/smb.h +++ b/include/linux/smb.h @@ -11,7 +11,9 @@ #include #include +#ifdef __KERNEL__ #include +#endif enum smb_protocol { SMB_PROTOCOL_NONE, -- cgit v1.2.3 From faa312da9cd0b044bdc84483162c6ee10b9c83c0 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 04:18:43 +0800 Subject: lcd: allow lcd device to handle mode change events Some LCD panels are capable of different resolutions, and is allowed to change at run-time, so to make "struct lcd_device" to be able to handle mode change events here. Signed-off-by: Eric Miao Acked-by: Krzysztof Helt Signed-off-by: Russell King --- drivers/video/backlight/lcd.c | 18 +++++++++++++++--- drivers/video/fbmem.c | 1 + include/linux/lcd.h | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index b15b2b84a6f7..8e1731d3b228 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -27,14 +27,26 @@ static int fb_notifier_callback(struct notifier_block *self, struct fb_event *evdata = data; /* If we aren't interested in this event, skip it immediately ... */ - if (event != FB_EVENT_BLANK) + switch (event) { + case FB_EVENT_BLANK: + case FB_EVENT_MODE_CHANGE: + case FB_EVENT_MODE_CHANGE_ALL: + break; + default: return 0; + } ld = container_of(self, struct lcd_device, fb_notif); + if (!ld->ops) + return 0; + mutex_lock(&ld->ops_lock); - if (ld->ops) - if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) + if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { + if (event == FB_EVENT_BLANK) ld->ops->set_power(ld, *(int *)evdata->data); + else + ld->ops->set_mode(ld, evdata->data); + } mutex_unlock(&ld->ops_lock); return 0; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 98843c2ecf73..0737570030f5 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -979,6 +979,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; + event.data = &mode; fb_notifier_call_chain(evnt, &event); } } diff --git a/include/linux/lcd.h b/include/linux/lcd.h index 173febac6656..c67fecafff90 100644 --- a/include/linux/lcd.h +++ b/include/linux/lcd.h @@ -11,6 +11,7 @@ #include #include #include +#include /* Notes on locking: * @@ -45,6 +46,8 @@ struct lcd_ops { int (*get_contrast)(struct lcd_device *); /* Set LCD panel contrast */ int (*set_contrast)(struct lcd_device *, int contrast); + /* Set LCD panel mode (resolutions ...) */ + int (*set_mode)(struct lcd_device *, struct fb_videomode *); /* Check if given framebuffer device is the one LCD is bound to; return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */ int (*check_fb)(struct lcd_device *, struct fb_info *); -- cgit v1.2.3 From b18250a8f66050bd2a52287cd543fb93100e8ee0 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 04:21:44 +0800 Subject: lcd: add SPI-based LCD and backlight driver for SHARP corgi/spitz The driver is based on different source files including corgi_ssp.c, corgi_lcd.c and corgi_bl.c, previously authored by Richard Purdie and many others. The LCD and Backlight device actually share the same SPI device, so they are made into this single driver. Signed-off-by: Eric Miao Cc: Richard Purdie Signed-off-by: Russell King --- drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/corgi_lcd.c | 541 ++++++++++++++++++++++++++++++++++++ include/linux/spi/corgi_lcd.h | 16 ++ 4 files changed, 565 insertions(+) create mode 100644 drivers/video/backlight/corgi_lcd.c create mode 100644 include/linux/spi/corgi_lcd.h (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 452b770d8cc9..f5406cd78e28 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -24,6 +24,13 @@ config LCD_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +config LCD_CORGI + tristate "LCD Panel support for SHARP corgi/spitz model" + depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL + help + Say y here to support the LCD panels usually found on SHARP + corgi (C7x0) and spitz (Cxx00) models. + config LCD_LTV350QV tristate "Samsung LTV350QV LCD Panel" depends on LCD_CLASS_DEVICE && SPI_MASTER diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b405aace803f..cf12d58392e0 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,6 +1,7 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c new file mode 100644 index 000000000000..bf69e50d262e --- /dev/null +++ b/drivers/video/backlight/corgi_lcd.c @@ -0,0 +1,541 @@ +/* + * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models) + * + * Copyright (c) 2004-2006 Richard Purdie + * + * Based on Sharp's 2.4 Backlight Driver + * + * Copyright (c) 2008 Marvell International Ltd. + * Converted to SPI device based LCD/Backlight device driver + * by Eric Miao + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +/* Register Addresses */ +#define RESCTL_ADRS 0x00 +#define PHACTRL_ADRS 0x01 +#define DUTYCTRL_ADRS 0x02 +#define POWERREG0_ADRS 0x03 +#define POWERREG1_ADRS 0x04 +#define GPOR3_ADRS 0x05 +#define PICTRL_ADRS 0x06 +#define POLCTRL_ADRS 0x07 + +/* Register Bit Definitions */ +#define RESCTL_QVGA 0x01 +#define RESCTL_VGA 0x00 + +#define POWER1_VW_ON 0x01 /* VW Supply FET ON */ +#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */ +#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */ + +#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */ +#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */ +#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */ + +#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */ +#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */ +#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */ +#define POWER0_COM_ON 0x08 /* COM Power Supply ON */ +#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */ + +#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */ +#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */ +#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */ + +#define PICTRL_INIT_STATE 0x01 +#define PICTRL_INIOFF 0x02 +#define PICTRL_POWER_DOWN 0x04 +#define PICTRL_COM_SIGNAL_OFF 0x08 +#define PICTRL_DAC_SIGNAL_OFF 0x10 + +#define POLCTRL_SYNC_POL_FALL 0x01 +#define POLCTRL_EN_POL_FALL 0x02 +#define POLCTRL_DATA_POL_FALL 0x04 +#define POLCTRL_SYNC_ACT_H 0x08 +#define POLCTRL_EN_ACT_L 0x10 + +#define POLCTRL_SYNC_POL_RISE 0x00 +#define POLCTRL_EN_POL_RISE 0x00 +#define POLCTRL_DATA_POL_RISE 0x00 +#define POLCTRL_SYNC_ACT_L 0x00 +#define POLCTRL_EN_ACT_H 0x00 + +#define PHACTRL_PHASE_MANUAL 0x01 +#define DEFAULT_PHAD_QVGA (9) +#define DEFAULT_COMADJ (125) + +struct corgi_lcd { + struct spi_device *spi_dev; + struct lcd_device *lcd_dev; + struct backlight_device *bl_dev; + + int intensity; + int power; + int mode; + char buf[2]; + + void (*notify)(int intensity); + void (*kick_battery)(void); +}; + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); + +/* + * This is only a psuedo I2C interface. We can't use the standard kernel + * routines as the interface is write only. We just assume the data is acked... + */ +static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data) +{ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data); + udelay(10); +} + +static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data) +{ + lcdtg_ssp_i2c_send(lcd, data); + lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, data); +} + +static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base); +} + +static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); +} + +static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd, + uint8_t base, uint8_t data) +{ + int i; + for (i = 0; i < 8; i++) { + if (data & 0x80) + lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT); + else + lcdtg_i2c_send_bit(lcd, base); + data <<= 1; + } +} + +static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_i2c_send_bit(lcd, base); +} + +static void lcdtg_set_common_voltage(struct corgi_lcd *lcd, + uint8_t base_data, uint8_t data) +{ + /* Set Common Voltage to M62332FP via I2C */ + lcdtg_i2c_send_start(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x9c); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x00); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, data); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_stop(lcd, base_data); +} + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data) +{ + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .cs_change = 1, + .tx_buf = lcd->buf, + }; + + lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi_dev, &msg); +} + +/* Set Phase Adjust */ +static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode) +{ + int adj; + + switch(mode) { + case CORGI_LCD_MODE_VGA: + /* Setting for VGA */ + adj = sharpsl_param.phadadj; + adj = (adj < 0) ? PHACTRL_PHASE_MANUAL : + PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1); + break; + case CORGI_LCD_MODE_QVGA: + default: + /* Setting for QVGA */ + adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL; + break; + } + + corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj); +} + +static void corgi_lcd_power_on(struct corgi_lcd *lcd) +{ + int comadj; + + /* Initialize Internal Logic & Port */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_POWER_DOWN | PICTRL_INIOFF | + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF | + PICTRL_DAC_SIGNAL_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); + + /* VDD(+8V), SVSS(-4V) ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + mdelay(3); + + /* DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* INIB = H, INI = L */ + /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF); + + /* Set Common Voltage */ + comadj = sharpsl_param.comadj; + if (comadj < 0) + comadj = DEFAULT_COMADJ; + + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_OFF, comadj); + + /* VCC5 ON, DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_ON); + + /* GVSS(-8V) ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + mdelay(2); + + /* COM SIGNAL ON (PICTL[3] = L) */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE); + + /* COM ON, DAC ON, VCC5_ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_ON | POWER0_VCC5_ON); + + /* VW ON, GVSS ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* Signals output enable */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0); + + /* Set Phase Adjust */ + lcdtg_set_phadadj(lcd, lcd->mode); + + /* Initialize for Input Signals from ATI */ + corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS, + POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE | + POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L | + POLCTRL_EN_ACT_H); + udelay(1000); + + switch (lcd->mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } +} + +static void corgi_lcd_power_off(struct corgi_lcd *lcd) +{ + /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */ + msleep(34); + + /* (1)VW OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* (2)COM OFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF); + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON); + + /* (3)Set Common Voltage Bias 0V */ + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_ON, 0); + + /* (4)GVSS OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + + /* (5)VCC5 OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (6)Set PDWN, INIOFF, DACOFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF | + PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF); + + /* (7)DAC OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (8)VDD OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); +} + +static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + int mode = CORGI_LCD_MODE_QVGA; + + if (m->xres == 640 || m->xres == 480) + mode = CORGI_LCD_MODE_VGA; + + if (lcd->mode == mode) + return 0; + + lcdtg_set_phadadj(lcd, mode); + + switch (mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } + + lcd->mode = mode; + return 0; +} + +static int corgi_lcd_set_power(struct lcd_device *ld, int power) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + corgi_lcd_power_on(lcd); + + if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + corgi_lcd_power_off(lcd); + + lcd->power = power; + return 0; +} + +static int corgi_lcd_get_power(struct lcd_device *ld) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + return lcd->power; +} + +static struct lcd_ops corgi_lcd_ops = { + .get_power = corgi_lcd_get_power, + .set_power = corgi_lcd_set_power, + .set_mode = corgi_lcd_set_mode, +}; + +static int corgi_bl_get_intensity(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + + return lcd->intensity; +} + +static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) +{ + if (intensity > 0x10) + intensity += 0x10; + + corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity); + lcd->intensity = intensity; + + if (lcd->notify) + lcd->notify(intensity); + + if (lcd->kick_battery) + lcd->kick_battery(); + + return 0; +} + +static int corgi_bl_update_status(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + return corgi_bl_set_intensity(lcd, intensity); +} + +static struct backlight_ops corgi_bl_ops = { + .get_brightness = corgi_bl_get_intensity, + .update_status = corgi_bl_update_status, +}; + +#ifdef CONFIG_PM +static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgi_bl_set_intensity(lcd, 0); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + return 0; +} + +static int corgi_lcd_resume(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + return 0; +} +#else +#define corgi_lcd_suspend NULL +#define corgi_lcd_resume NULL +#endif + +static int __devinit corgi_lcd_probe(struct spi_device *spi) +{ + struct corgi_lcd_platform_data *pdata = spi->dev.platform_data; + struct corgi_lcd *lcd; + int ret = 0; + + if (pdata == NULL) { + dev_err(&spi->dev, "platform data not available\n"); + return -EINVAL; + } + + lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL); + if (!lcd) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + lcd->spi_dev = spi; + + lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev, + lcd, &corgi_lcd_ops); + if (IS_ERR(lcd->lcd_dev)) { + ret = PTR_ERR(lcd->lcd_dev); + goto err_free_lcd; + } + lcd->power = FB_BLANK_POWERDOWN; + lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA; + + lcd->bl_dev = backlight_device_register("corgi_bl", &spi->dev, + lcd, &corgi_bl_ops); + if (IS_ERR(lcd->bl_dev)) { + ret = PTR_ERR(lcd->bl_dev); + goto err_unregister_lcd; + } + lcd->bl_dev->props.max_brightness = pdata->max_intensity; + lcd->bl_dev->props.brightness = pdata->default_intensity; + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + + lcd->notify = pdata->notify; + lcd->kick_battery = pdata->kick_battery; + + dev_set_drvdata(&spi->dev, lcd); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + return 0; + +err_unregister_lcd: + lcd_device_unregister(lcd->lcd_dev); +err_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit corgi_lcd_remove(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + lcd->bl_dev->props.brightness = 0; + backlight_update_status(lcd->bl_dev); + backlight_device_unregister(lcd->bl_dev); + + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->lcd_dev); + kfree(lcd); + + return 0; +} + +static struct spi_driver corgi_lcd_driver = { + .driver = { + .name = "corgi-lcd", + .owner = THIS_MODULE, + }, + .probe = corgi_lcd_probe, + .remove = __devexit_p(corgi_lcd_remove), + .suspend = corgi_lcd_suspend, + .resume = corgi_lcd_resume, +}; + +static int __init corgi_lcd_init(void) +{ + return spi_register_driver(&corgi_lcd_driver); +} +module_init(corgi_lcd_init); + +static void __exit corgi_lcd_exit(void) +{ + spi_unregister_driver(&corgi_lcd_driver); +} +module_exit(corgi_lcd_exit); + +MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); +MODULE_AUTHOR("Eric Miao "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h new file mode 100644 index 000000000000..3c53ac26c8d1 --- /dev/null +++ b/include/linux/spi/corgi_lcd.h @@ -0,0 +1,16 @@ +#ifndef __LINUX_SPI_CORGI_LCD_H +#define __LINUX_SPI_CORGI_LCD_H + +#define CORGI_LCD_MODE_QVGA 1 +#define CORGI_LCD_MODE_VGA 2 + +struct corgi_lcd_platform_data { + int init_mode; + int max_intensity; + int default_intensity; + + void (*notify)(int intensity); + void (*kick_battery)(void); +}; + +#endif /* __LINUX_SPI_CORGI_LCD_H */ -- cgit v1.2.3 From bfdcaa3b6899bbfc6ba633aff3f5f2422486c8c1 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 05:57:20 +0800 Subject: lcd: add corgibl_limit_intensity() to corgi_lcd This is not generic enough, added here for backward compatibility. And make this an individual commit so future revert will be a bit easier. Signed-off-by: Eric Miao Cc: Richard Purdie Signed-off-by: Russell King --- drivers/video/backlight/corgi_lcd.c | 27 +++++++++++++++++++++++++++ include/linux/spi/corgi_lcd.h | 1 + 2 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index bf69e50d262e..068f14864099 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -86,6 +86,7 @@ struct corgi_lcd { struct lcd_device *lcd_dev; struct backlight_device *bl_dev; + int limit_mask; int intensity; int power; int mode; @@ -97,6 +98,11 @@ struct corgi_lcd { static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); +static struct corgi_lcd *the_corgi_lcd; +static unsigned long corgibl_flags; +#define CORGIBL_SUSPENDED 0x01 +#define CORGIBL_BATTLOW 0x02 + /* * This is only a psuedo I2C interface. We can't use the standard kernel * routines as the interface is write only. We just assume the data is acked... @@ -413,9 +419,25 @@ static int corgi_bl_update_status(struct backlight_device *bd) if (bd->props.fb_blank != FB_BLANK_UNBLANK) intensity = 0; + if (corgibl_flags & CORGIBL_SUSPENDED) + intensity = 0; + if (corgibl_flags & CORGIBL_BATTLOW) + intensity &= lcd->limit_mask; + return corgi_bl_set_intensity(lcd, intensity); } +void corgibl_limit_intensity(int limit) +{ + if (limit) + corgibl_flags |= CORGIBL_BATTLOW; + else + corgibl_flags &= ~CORGIBL_BATTLOW; + + backlight_update_status(the_corgi_lcd->bl_dev); +} +EXPORT_SYMBOL(corgibl_limit_intensity); + static struct backlight_ops corgi_bl_ops = { .get_brightness = corgi_bl_get_intensity, .update_status = corgi_bl_update_status, @@ -426,6 +448,7 @@ static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) { struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + corgibl_flags |= CORGIBL_SUSPENDED; corgi_bl_set_intensity(lcd, 0); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); return 0; @@ -435,6 +458,7 @@ static int corgi_lcd_resume(struct spi_device *spi) { struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + corgibl_flags &= ~CORGIBL_SUSPENDED; corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); backlight_update_status(lcd->bl_dev); return 0; @@ -488,6 +512,9 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, lcd); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); backlight_update_status(lcd->bl_dev); + + lcd->limit_mask = pdata->limit_mask; + the_corgi_lcd = lcd; return 0; err_unregister_lcd: diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h index 3c53ac26c8d1..b6161aae2752 100644 --- a/include/linux/spi/corgi_lcd.h +++ b/include/linux/spi/corgi_lcd.h @@ -8,6 +8,7 @@ struct corgi_lcd_platform_data { int init_mode; int max_intensity; int default_intensity; + int limit_mask; void (*notify)(int intensity); void (*kick_battery)(void); -- cgit v1.2.3 From 79617deeebb9cf089e2bc2aad19743b1209043f6 Mon Sep 17 00:00:00 2001 From: YanBo Date: Mon, 22 Sep 2008 13:30:32 +0800 Subject: mac80211: mesh portal functionality support Currently the mesh code doesn't support bridging mesh point interfaces with wired ethernet or AP to construct an MPP or MAP. This patch adds code to support the "6 address frame format packet" functionality to mesh point interfaces. Now the mesh network can be used as backhaul for end to end communication. Signed-off-by: Li YanBo Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 5 ++ net/mac80211/mesh.h | 4 ++ net/mac80211/mesh_pathtbl.c | 127 +++++++++++++++++++++++++++++++++++++++++++- net/mac80211/rx.c | 32 +++++++++-- net/mac80211/tx.c | 44 ++++++++++++--- 5 files changed, 201 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index abc1abc63bf0..14126bc36641 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -471,6 +471,11 @@ struct ieee80211s_hdr { u8 eaddr3[6]; } __attribute__ ((packed)); +/* Mesh flags */ +#define MESH_FLAGS_AE_A4 0x1 +#define MESH_FLAGS_AE_A5_A6 0x2 +#define MESH_FLAGS_PS_DEEP 0x4 + /** * struct ieee80211_quiet_ie * diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8ee414a0447c..e10471c6ba42 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -71,6 +71,7 @@ enum mesh_path_flags { */ struct mesh_path { u8 dst[ETH_ALEN]; + u8 mpp[ETH_ALEN]; /* used for MPP or MAP */ struct ieee80211_sub_if_data *sdata; struct sta_info *next_hop; struct timer_list timer; @@ -226,6 +227,9 @@ int mesh_nexthop_lookup(struct sk_buff *skb, void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata); +struct mesh_path *mpp_path_lookup(u8 *dst, + struct ieee80211_sub_if_data *sdata); +int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata); struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata); void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index e4fa2905fadc..3c72557df45a 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -36,6 +36,7 @@ struct mpath_node { }; static struct mesh_table *mesh_paths; +static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ /* This lock will have the grow table function as writer and add / delete nodes * as readers. When reading the table (i.e. doing lookups) we are well protected @@ -94,6 +95,34 @@ struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) return NULL; } +struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +{ + struct mesh_path *mpath; + struct hlist_node *n; + struct hlist_head *bucket; + struct mesh_table *tbl; + struct mpath_node *node; + + tbl = rcu_dereference(mpp_paths); + + bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; + hlist_for_each_entry_rcu(node, n, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && + memcmp(dst, mpath->dst, ETH_ALEN) == 0) { + if (MPATH_EXPIRED(mpath)) { + spin_lock_bh(&mpath->state_lock); + if (MPATH_EXPIRED(mpath)) + mpath->flags &= ~MESH_PATH_ACTIVE; + spin_unlock_bh(&mpath->state_lock); + } + return mpath; + } + } + return NULL; +} + + /** * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index * @idx: index @@ -226,6 +255,91 @@ err_path_alloc: } +int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) +{ + struct mesh_path *mpath, *new_mpath; + struct mpath_node *node, *new_node; + struct hlist_head *bucket; + struct hlist_node *n; + int grow = 0; + int err = 0; + u32 hash_idx; + + + if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0) + /* never add ourselves as neighbours */ + return -ENOTSUPP; + + if (is_multicast_ether_addr(dst)) + return -ENOTSUPP; + + err = -ENOMEM; + new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); + if (!new_mpath) + goto err_path_alloc; + + new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); + if (!new_node) + goto err_node_alloc; + + read_lock(&pathtbl_resize_lock); + memcpy(new_mpath->dst, dst, ETH_ALEN); + memcpy(new_mpath->mpp, mpp, ETH_ALEN); + new_mpath->sdata = sdata; + new_mpath->flags = 0; + skb_queue_head_init(&new_mpath->frame_queue); + new_node->mpath = new_mpath; + new_mpath->exp_time = jiffies; + spin_lock_init(&new_mpath->state_lock); + + hash_idx = mesh_table_hash(dst, sdata, mpp_paths); + bucket = &mpp_paths->hash_buckets[hash_idx]; + + spin_lock(&mpp_paths->hashwlock[hash_idx]); + + err = -EEXIST; + hlist_for_each_entry(node, n, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) + goto err_exists; + } + + hlist_add_head_rcu(&new_node->list, bucket); + if (atomic_inc_return(&mpp_paths->entries) >= + mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) + grow = 1; + + spin_unlock(&mpp_paths->hashwlock[hash_idx]); + read_unlock(&pathtbl_resize_lock); + if (grow) { + struct mesh_table *oldtbl, *newtbl; + + write_lock(&pathtbl_resize_lock); + oldtbl = mpp_paths; + newtbl = mesh_table_grow(mpp_paths); + if (!newtbl) { + write_unlock(&pathtbl_resize_lock); + return 0; + } + rcu_assign_pointer(mpp_paths, newtbl); + write_unlock(&pathtbl_resize_lock); + + synchronize_rcu(); + mesh_table_free(oldtbl, false); + } + return 0; + +err_exists: + spin_unlock(&mpp_paths->hashwlock[hash_idx]); + read_unlock(&pathtbl_resize_lock); + kfree(new_node); +err_node_alloc: + kfree(new_mpath); +err_path_alloc: + return err; +} + + /** * mesh_plink_broken - deactivates paths and sends perr when a link breaks * @@ -475,11 +589,21 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) int mesh_pathtbl_init(void) { mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); + if (!mesh_paths) + return -ENOMEM; mesh_paths->free_node = &mesh_path_node_free; mesh_paths->copy_node = &mesh_path_node_copy; mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; - if (!mesh_paths) + + mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); + if (!mpp_paths) { + mesh_table_free(mesh_paths, true); return -ENOMEM; + } + mpp_paths->free_node = &mesh_path_node_free; + mpp_paths->copy_node = &mesh_path_node_copy; + mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; + return 0; } @@ -511,4 +635,5 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) void mesh_pathtbl_unregister(void) { mesh_table_free(mesh_paths, true); + mesh_table_free(mpp_paths, true); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3ab9670f1809..2efa4dd47b5d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1107,10 +1107,6 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx) hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (ieee80211_vif_is_mesh(&sdata->vif)) - hdrlen += ieee80211_get_mesh_hdrlen( - (struct ieee80211s_hdr *) (skb->data + hdrlen)); - /* convert IEEE 802.11 header + possible LLC headers into Ethernet * header * IEEE 802.11 address fields: @@ -1134,6 +1130,15 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx) if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS && sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) return -1; + if (ieee80211_vif_is_mesh(&sdata->vif)) { + struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *) + (skb->data + hdrlen); + hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { + memcpy(dst, meshdr->eaddr1, ETH_ALEN); + memcpy(src, meshdr->eaddr2, ETH_ALEN); + } + } break; case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): if (sdata->vif.type != NL80211_IFTYPE_STATION || @@ -1393,6 +1398,25 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) /* illegal frame */ return RX_DROP_MONITOR; + if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){ + struct ieee80211_sub_if_data *sdata; + struct mesh_path *mppath; + + sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); + rcu_read_lock(); + mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata); + if (!mppath) { + mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata); + } else { + spin_lock_bh(&mppath->state_lock); + mppath->exp_time = jiffies; + if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0) + memcpy(mppath->mpp, hdr->addr4, ETH_ALEN); + spin_unlock_bh(&mppath->state_lock); + } + rcu_read_unlock(); + } + if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) return RX_CONTINUE; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 00d798cc9e04..00d96e63dce9 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1498,18 +1498,50 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); - /* RA TA DA SA */ - memset(hdr.addr1, 0, ETH_ALEN); - memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; ret = 0; goto fail; } - meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata); + memset(&mesh_hdr, 0, sizeof(mesh_hdr)); + + if (compare_ether_addr(dev->dev_addr, + skb->data + ETH_ALEN) == 0) { + /* RA TA DA SA */ + memset(hdr.addr1, 0, ETH_ALEN); + memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(hdr.addr3, skb->data, ETH_ALEN); + memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata); + } else { + /* packet from other interface */ + struct mesh_path *mppath; + + memset(hdr.addr1, 0, ETH_ALEN); + memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN); + + if (is_multicast_ether_addr(skb->data)) + memcpy(hdr.addr3, skb->data, ETH_ALEN); + else { + rcu_read_lock(); + mppath = mpp_path_lookup(skb->data, sdata); + if (mppath) + memcpy(hdr.addr3, mppath->mpp, ETH_ALEN); + else + memset(hdr.addr3, 0xff, ETH_ALEN); + rcu_read_unlock(); + } + + mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6; + mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; + put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum); + memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN); + memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN); + sdata->u.mesh.mesh_seqnum++; + meshhdrlen = 18; + } hdrlen = 30; break; #endif -- cgit v1.2.3 From 040dec3b37e4b9ec15b359bf5744f1ceba39fe3e Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Fri, 12 Sep 2008 06:55:14 -0700 Subject: netxen: add pci ids Define old and new pci vendor and device ids. Signed-off-by: Dhananjay Phadke Signed-off-by: Jeff Garzik --- drivers/net/netxen/netxen_nic_main.c | 18 +++++++++--------- include/linux/pci_ids.h | 10 ++++++++++ 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 008fd6618a5f..0f8ea2c7728b 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -77,18 +77,18 @@ static irqreturn_t netxen_msi_intr(int irq, void *data); /* PCI Device ID Table */ #define ENTRY(device) \ - {PCI_DEVICE(0x4040, (device)), \ + {PCI_DEVICE(PCI_VENDOR_ID_NETXEN, (device)), \ .class = PCI_CLASS_NETWORK_ETHERNET << 8, .class_mask = ~0} static struct pci_device_id netxen_pci_tbl[] __devinitdata = { - ENTRY(0x0001), - ENTRY(0x0002), - ENTRY(0x0003), - ENTRY(0x0004), - ENTRY(0x0005), - ENTRY(0x0024), - ENTRY(0x0025), - ENTRY(0x0100), + ENTRY(PCI_DEVICE_ID_NX2031_10GXSR), + ENTRY(PCI_DEVICE_ID_NX2031_10GCX4), + ENTRY(PCI_DEVICE_ID_NX2031_4GCU), + ENTRY(PCI_DEVICE_ID_NX2031_IMEZ), + ENTRY(PCI_DEVICE_ID_NX2031_HMEZ), + ENTRY(PCI_DEVICE_ID_NX2031_XG_MGMT), + ENTRY(PCI_DEVICE_ID_NX2031_XG_MGMT2), + ENTRY(PCI_DEVICE_ID_NX3031), {0,} }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 6f4276d461c0..a65b082a888a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2247,6 +2247,16 @@ #define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007 #define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009 +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_DEVICE_ID_NX2031_10GXSR 0x0001 +#define PCI_DEVICE_ID_NX2031_10GCX4 0x0002 +#define PCI_DEVICE_ID_NX2031_4GCU 0x0003 +#define PCI_DEVICE_ID_NX2031_IMEZ 0x0004 +#define PCI_DEVICE_ID_NX2031_HMEZ 0x0005 +#define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024 +#define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025 +#define PCI_DEVICE_ID_NX3031 0x0100 + #define PCI_VENDOR_ID_AKS 0x416c #define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100 -- cgit v1.2.3 From b4f151ff899362fec952c45d166252c9912c041f Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Sep 2008 17:48:26 +0100 Subject: MN10300: Move asm-arm/cnt32_to_63.h to include/linux/ Move asm-arm/cnt32_to_63.h to include/linux/ so that MN10300 can make use of it too. Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- arch/arm/mach-pxa/time.c | 2 +- arch/arm/mach-sa1100/generic.c | 2 +- arch/arm/mach-versatile/core.c | 2 +- include/linux/cnt32_to_63.h | 80 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 include/linux/cnt32_to_63.h (limited to 'include/linux') diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index 67e18509d7bf..b0d6b32654cf 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 1362994c78aa..b422526f6d8b 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -18,9 +18,9 @@ #include #include /* just for sched_clock() - funny that */ #include +#include #include -#include #include #include #include diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index d75e795c893e..b638f10411e8 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -28,8 +28,8 @@ #include #include #include +#include -#include #include #include #include diff --git a/include/linux/cnt32_to_63.h b/include/linux/cnt32_to_63.h new file mode 100644 index 000000000000..8c0f9505b48c --- /dev/null +++ b/include/linux/cnt32_to_63.h @@ -0,0 +1,80 @@ +/* + * Extend a 32-bit counter to 63 bits + * + * Author: Nicolas Pitre + * Created: December 3, 2006 + * Copyright: MontaVista Software, Inc. + * + * 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 __LINUX_CNT32_TO_63_H__ +#define __LINUX_CNT32_TO_63_H__ + +#include +#include +#include + +/* this is used only to give gcc a clue about good code generation */ +union cnt32_to_63 { + struct { +#if defined(__LITTLE_ENDIAN) + u32 lo, hi; +#elif defined(__BIG_ENDIAN) + u32 hi, lo; +#endif + }; + u64 val; +}; + + +/** + * cnt32_to_63 - Expand a 32-bit counter to a 63-bit counter + * @cnt_lo: The low part of the counter + * + * Many hardware clock counters are only 32 bits wide and therefore have + * a relatively short period making wrap-arounds rather frequent. This + * is a problem when implementing sched_clock() for example, where a 64-bit + * non-wrapping monotonic value is expected to be returned. + * + * To overcome that limitation, let's extend a 32-bit counter to 63 bits + * in a completely lock free fashion. Bits 0 to 31 of the clock are provided + * by the hardware while bits 32 to 62 are stored in memory. The top bit in + * memory is used to synchronize with the hardware clock half-period. When + * the top bit of both counters (hardware and in memory) differ then the + * memory is updated with a new value, incrementing it when the hardware + * counter wraps around. + * + * Because a word store in memory is atomic then the incremented value will + * always be in synch with the top bit indicating to any potential concurrent + * reader if the value in memory is up to date or not with regards to the + * needed increment. And any race in updating the value in memory is harmless + * as the same value would simply be stored more than once. + * + * The only restriction for the algorithm to work properly is that this + * code must be executed at least once per each half period of the 32-bit + * counter to properly update the state bit in memory. This is usually not a + * problem in practice, but if it is then a kernel timer could be scheduled + * to manage for this code to be executed often enough. + * + * Note that the top bit (bit 63) in the returned value should be considered + * as garbage. It is not cleared here because callers are likely to use a + * multiplier on the returned value which can get rid of the top bit + * implicitly by making the multiplier even, therefore saving on a runtime + * clear-bit instruction. Otherwise caller must remember to clear the top + * bit explicitly. + */ +#define cnt32_to_63(cnt_lo) \ +({ \ + static volatile u32 __m_cnt_hi; \ + union cnt32_to_63 __x; \ + __x.hi = __m_cnt_hi; \ + __x.lo = (cnt_lo); \ + if (unlikely((s32)(__x.hi ^ __x.lo) < 0)) \ + __m_cnt_hi = __x.hi = (__x.hi ^ 0x80000000) + (__x.hi >> 31); \ + __x.val; \ +}) + +#endif -- cgit v1.2.3 From ff7a4c7130c0ad97d55f7ab3f0a35fbc1f41b376 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Sun, 7 Sep 2008 11:30:06 +0800 Subject: [ARM] corgi_lcd: use GPIO API for BACKLIGHT_ON and BACKLIGHT_CONT Signed-off-by: Eric Miao Signed-off-by: Russell King --- arch/arm/mach-pxa/corgi.c | 9 +--- arch/arm/mach-pxa/spitz.c | 23 ++++------ drivers/video/backlight/corgi_lcd.c | 83 ++++++++++++++++++++++++++++++++++--- include/linux/spi/corgi_lcd.h | 3 ++ 4 files changed, 90 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index f8fd1d872157..5c08c4e9cd22 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c @@ -444,12 +444,6 @@ static struct pxa2xx_spi_chip corgi_ads7846_chip = { .cs_control = corgi_ads7846_cs, }; -static void corgi_notify_intensity(int intensity) -{ - /* Bit 5 is via SCOOP */ - gpio_set_value(CORGI_GPIO_BACKLIGHT_CONT, !!(intensity & 0x0020)); -} - static void corgi_bl_kick_battery(void) { void (*kick_batt)(void); @@ -466,7 +460,8 @@ static struct corgi_lcd_platform_data corgi_lcdcon_info = { .max_intensity = 0x2f, .default_intensity = 0x1f, .limit_mask = 0x0b, - .notify = corgi_notify_intensity, + .gpio_backlight_cont = CORGI_GPIO_BACKLIGHT_CONT, + .gpio_backlight_on = -1, .kick_battery = corgi_bl_kick_battery, }; diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 1d8654d2fb96..245890d2b6b5 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -305,21 +305,6 @@ static struct pxa2xx_spi_chip spitz_ads7846_chip = { .cs_control = spitz_ads7846_cs, }; -static void spitz_notify_intensity(int intensity) -{ - if (machine_is_spitz() || machine_is_borzoi()) { - gpio_set_value(SPITZ_GPIO_BACKLIGHT_CONT, !(intensity & 0x20)); - gpio_set_value(SPITZ_GPIO_BACKLIGHT_ON, intensity); - return; - } - - if (machine_is_akita()) { - gpio_set_value(AKITA_GPIO_BACKLIGHT_CONT, !(intensity & 0x20)); - gpio_set_value(AKITA_GPIO_BACKLIGHT_ON, intensity); - return; - } -} - static void spitz_bl_kick_battery(void) { void (*kick_batt)(void); @@ -336,7 +321,8 @@ static struct corgi_lcd_platform_data spitz_lcdcon_info = { .max_intensity = 0x2f, .default_intensity = 0x1f, .limit_mask = 0x0b, - .notify = spitz_notify_intensity, + .gpio_backlight_cont = SPITZ_GPIO_BACKLIGHT_CONT, + .gpio_backlight_on = SPITZ_GPIO_BACKLIGHT_ON, .kick_battery = spitz_bl_kick_battery, }; @@ -399,6 +385,11 @@ static void __init spitz_init_spi(void) if (err) goto err_free_2; + if (machine_is_akita()) { + spitz_lcdcon_info.gpio_backlight_cont = AKITA_GPIO_BACKLIGHT_CONT; + spitz_lcdcon_info.gpio_backlight_on = AKITA_GPIO_BACKLIGHT_ON; + } + pxa2xx_set_spi_info(2, &spitz_spi_info); spi_register_board_info(ARRAY_AND_SIZE(spitz_spi_devices)); return; diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index 068f14864099..2afd47eefe74 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -92,7 +93,10 @@ struct corgi_lcd { int mode; char buf[2]; - void (*notify)(int intensity); + int gpio_backlight_on; + int gpio_backlight_cont; + int gpio_backlight_cont_inverted; + void (*kick_battery)(void); }; @@ -393,18 +397,26 @@ static int corgi_bl_get_intensity(struct backlight_device *bd) static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) { + int cont; + if (intensity > 0x10) intensity += 0x10; corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity); - lcd->intensity = intensity; - if (lcd->notify) - lcd->notify(intensity); + /* Bit 5 via GPIO_BACKLIGHT_CONT */ + cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted; + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_set_value(lcd->gpio_backlight_cont, cont); + + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_set_value(lcd->gpio_backlight_on, intensity); if (lcd->kick_battery) lcd->kick_battery(); + lcd->intensity = intensity; return 0; } @@ -468,6 +480,56 @@ static int corgi_lcd_resume(struct spi_device *spi) #define corgi_lcd_resume NULL #endif +static int setup_gpio_backlight(struct corgi_lcd *lcd, + struct corgi_lcd_platform_data *pdata) +{ + struct spi_device *spi = lcd->spi_dev; + int err; + + lcd->gpio_backlight_on = -1; + lcd->gpio_backlight_cont = -1; + + if (gpio_is_valid(pdata->gpio_backlight_on)) { + err = gpio_request(pdata->gpio_backlight_on, "BL_ON"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_on\n", pdata->gpio_backlight_on); + return err; + } + + lcd->gpio_backlight_on = pdata->gpio_backlight_on; + gpio_direction_output(lcd->gpio_backlight_on, 0); + } + + if (gpio_is_valid(pdata->gpio_backlight_cont)) { + err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_cont\n", pdata->gpio_backlight_cont); + goto err_free_backlight_on; + } + + lcd->gpio_backlight_cont = pdata->gpio_backlight_cont; + + /* spitz and akita use both GPIOs for backlight, and + * have inverted polarity of GPIO_BACKLIGHT_CONT + */ + if (gpio_is_valid(lcd->gpio_backlight_on)) { + lcd->gpio_backlight_cont_inverted = 1; + gpio_direction_output(lcd->gpio_backlight_cont, 1); + } else { + lcd->gpio_backlight_cont_inverted = 0; + gpio_direction_output(lcd->gpio_backlight_cont, 0); + } + } + return 0; + +err_free_backlight_on: + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + return err; +} + static int __devinit corgi_lcd_probe(struct spi_device *spi) { struct corgi_lcd_platform_data *pdata = spi->dev.platform_data; @@ -506,7 +568,10 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi) lcd->bl_dev->props.brightness = pdata->default_intensity; lcd->bl_dev->props.power = FB_BLANK_UNBLANK; - lcd->notify = pdata->notify; + ret = setup_gpio_backlight(lcd, pdata); + if (ret) + goto err_unregister_bl; + lcd->kick_battery = pdata->kick_battery; dev_set_drvdata(&spi->dev, lcd); @@ -517,6 +582,8 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi) the_corgi_lcd = lcd; return 0; +err_unregister_bl: + backlight_device_unregister(lcd->bl_dev); err_unregister_lcd: lcd_device_unregister(lcd->lcd_dev); err_free_lcd: @@ -533,6 +600,12 @@ static int __devexit corgi_lcd_remove(struct spi_device *spi) backlight_update_status(lcd->bl_dev); backlight_device_unregister(lcd->bl_dev); + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_free(lcd->gpio_backlight_cont); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->lcd_dev); kfree(lcd); diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h index b6161aae2752..6692b3418ccf 100644 --- a/include/linux/spi/corgi_lcd.h +++ b/include/linux/spi/corgi_lcd.h @@ -10,6 +10,9 @@ struct corgi_lcd_platform_data { int default_intensity; int limit_mask; + int gpio_backlight_on; /* -1 if n/a */ + int gpio_backlight_cont; /* -1 if n/a */ + void (*notify)(int intensity); void (*kick_battery)(void); }; -- cgit v1.2.3 From 82ef04fb4c82542b3eda81cca461f0594ce9cd0b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 31 Jul 2008 17:02:40 +0900 Subject: libata: make SCR access ops per-link Logically, SCR access ops should take @link; however, there was no compelling reason to convert all SCR access ops when adding @link abstraction as there's one-to-one mapping between a port and a non-PMP link. However, that assumption won't hold anymore with the scheduled addition of slave link. Make SCR access ops per-link. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 24 ++++++++++++------------ drivers/ata/ata_piix.c | 15 +++++++++++---- drivers/ata/libata-core.c | 13 ++++--------- drivers/ata/sata_fsl.c | 26 +++++++++++++------------- drivers/ata/sata_inic162x.c | 8 ++++---- drivers/ata/sata_mv.c | 28 ++++++++++++++-------------- drivers/ata/sata_nv.c | 16 ++++++++-------- drivers/ata/sata_promise.c | 16 +++++++++------- drivers/ata/sata_qstor.c | 12 ++++++------ drivers/ata/sata_sil.c | 16 ++++++++-------- drivers/ata/sata_sil24.c | 12 ++++++------ drivers/ata/sata_sis.c | 28 ++++++++++++++++------------ drivers/ata/sata_svw.c | 10 ++++++---- drivers/ata/sata_uli.c | 24 ++++++++++++------------ drivers/ata/sata_via.c | 24 ++++++++++++------------ drivers/ata/sata_vsc.c | 10 ++++++---- drivers/scsi/libsas/sas_ata.c | 8 ++++---- include/linux/libata.h | 4 ++-- 18 files changed, 153 insertions(+), 141 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2e1a7cb2ed5f..6acea41eb7ca 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -267,8 +267,8 @@ struct ahci_port_priv { * per PM slot */ }; -static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); @@ -820,10 +820,10 @@ static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) return 0; } -static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { - void __iomem *port_mmio = ahci_port_base(ap); - int offset = ahci_scr_offset(ap, sc_reg); + void __iomem *port_mmio = ahci_port_base(link->ap); + int offset = ahci_scr_offset(link->ap, sc_reg); if (offset) { *val = readl(port_mmio + offset); @@ -832,10 +832,10 @@ static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return -EINVAL; } -static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { - void __iomem *port_mmio = ahci_port_base(ap); - int offset = ahci_scr_offset(ap, sc_reg); + void __iomem *port_mmio = ahci_port_base(link->ap); + int offset = ahci_scr_offset(link->ap, sc_reg); if (offset) { writel(val, port_mmio + offset); @@ -973,7 +973,7 @@ static void ahci_disable_alpm(struct ata_port *ap) writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT); /* go ahead and clean out PhyRdy Change from Serror too */ - ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); + ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); /* * Clear flag to indicate that we should ignore all PhyRdy @@ -1937,8 +1937,8 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat); /* AHCI needs SError cleared; otherwise, it might lock up */ - ahci_scr_read(ap, SCR_ERROR, &serror); - ahci_scr_write(ap, SCR_ERROR, serror); + ahci_scr_read(&ap->link, SCR_ERROR, &serror); + ahci_scr_write(&ap->link, SCR_ERROR, serror); host_ehi->serror |= serror; /* some controllers set IRQ_IF_ERR on device errors, ignore it */ @@ -2027,7 +2027,7 @@ static void ahci_port_intr(struct ata_port *ap) if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) && (status & PORT_IRQ_PHYRDY)) { status &= ~PORT_IRQ_PHYRDY; - ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); + ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); } if (unlikely(status & PORT_IRQ_ERROR)) { diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index e6b4606e36b6..81387ff48937 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -165,8 +165,10 @@ static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev); static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev); static int ich_pata_cable_detect(struct ata_port *ap); static u8 piix_vmw_bmdma_status(struct ata_port *ap); -static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val); -static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val); +static int piix_sidpr_scr_read(struct ata_link *link, + unsigned int reg, u32 *val); +static int piix_sidpr_scr_write(struct ata_link *link, + unsigned int reg, u32 val); #ifdef CONFIG_PM static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int piix_pci_device_resume(struct pci_dev *pdev); @@ -971,8 +973,10 @@ static u32 piix_merge_scr(u32 val0, u32 val1, const int * const *merge_tbl) return val; } -static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val) +static int piix_sidpr_scr_read(struct ata_link *link, + unsigned int reg, u32 *val) { + struct ata_port *ap = link->ap; const int * const sstatus_merge_tbl[] = { /* DET */ (const int []){ 1, 3, 0, 4, 3, -1 }, /* SPD */ (const int []){ 2, 1, 0, -1 }, @@ -1013,8 +1017,11 @@ static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val) return 0; } -static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val) +static int piix_sidpr_scr_write(struct ata_link *link, + unsigned int reg, u32 val) { + struct ata_port *ap = link->ap; + if (reg >= ARRAY_SIZE(piix_sidx_map)) return -EINVAL; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 79e3a8e7a84a..825461a33abe 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4868,10 +4868,8 @@ int sata_scr_valid(struct ata_link *link) int sata_scr_read(struct ata_link *link, int reg, u32 *val) { if (ata_is_host_link(link)) { - struct ata_port *ap = link->ap; - if (sata_scr_valid(link)) - return ap->ops->scr_read(ap, reg, val); + return link->ap->ops->scr_read(link, reg, val); return -EOPNOTSUPP; } @@ -4897,10 +4895,8 @@ int sata_scr_read(struct ata_link *link, int reg, u32 *val) int sata_scr_write(struct ata_link *link, int reg, u32 val) { if (ata_is_host_link(link)) { - struct ata_port *ap = link->ap; - if (sata_scr_valid(link)) - return ap->ops->scr_write(ap, reg, val); + return link->ap->ops->scr_write(link, reg, val); return -EOPNOTSUPP; } @@ -4925,13 +4921,12 @@ int sata_scr_write(struct ata_link *link, int reg, u32 val) int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) { if (ata_is_host_link(link)) { - struct ata_port *ap = link->ap; int rc; if (sata_scr_valid(link)) { - rc = ap->ops->scr_write(ap, reg, val); + rc = link->ap->ops->scr_write(link, reg, val); if (rc == 0) - rc = ap->ops->scr_read(ap, reg, &val); + rc = link->ap->ops->scr_read(link, reg, &val); return rc; } return -EOPNOTSUPP; diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 3924e7209a44..1a56db92ff7a 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -469,10 +469,10 @@ static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static int sata_fsl_scr_write(struct ata_port *ap, unsigned int sc_reg_in, - u32 val) +static int sata_fsl_scr_write(struct ata_link *link, + unsigned int sc_reg_in, u32 val) { - struct sata_fsl_host_priv *host_priv = ap->host->private_data; + struct sata_fsl_host_priv *host_priv = link->ap->host->private_data; void __iomem *ssr_base = host_priv->ssr_base; unsigned int sc_reg; @@ -493,10 +493,10 @@ static int sata_fsl_scr_write(struct ata_port *ap, unsigned int sc_reg_in, return 0; } -static int sata_fsl_scr_read(struct ata_port *ap, unsigned int sc_reg_in, - u32 *val) +static int sata_fsl_scr_read(struct ata_link *link, + unsigned int sc_reg_in, u32 *val) { - struct sata_fsl_host_priv *host_priv = ap->host->private_data; + struct sata_fsl_host_priv *host_priv = link->ap->host->private_data; void __iomem *ssr_base = host_priv->ssr_base; unsigned int sc_reg; @@ -645,12 +645,12 @@ static int sata_fsl_port_start(struct ata_port *ap) * Workaround for 8315DS board 3gbps link-up issue, * currently limit SATA port to GEN1 speed */ - sata_fsl_scr_read(ap, SCR_CONTROL, &temp); + sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp); temp &= ~(0xF << 4); temp |= (0x1 << 4); - sata_fsl_scr_write(ap, SCR_CONTROL, temp); + sata_fsl_scr_write(&ap->link, SCR_CONTROL, temp); - sata_fsl_scr_read(ap, SCR_CONTROL, &temp); + sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp); dev_printk(KERN_WARNING, dev, "scr_control, speed limited to %x\n", temp); #endif @@ -868,7 +868,7 @@ issue_srst: ioread32(CQ + hcr_base), ioread32(CA + hcr_base), ioread32(CC + hcr_base)); - sata_fsl_scr_read(ap, SCR_ERROR, &Serror); + sata_fsl_scr_read(&ap->link, SCR_ERROR, &Serror); DPRINTK("HStatus = 0x%x\n", ioread32(hcr_base + HSTATUS)); DPRINTK("HControl = 0x%x\n", ioread32(hcr_base + HCONTROL)); @@ -972,9 +972,9 @@ static void sata_fsl_error_intr(struct ata_port *ap) * Handle & Clear SError */ - sata_fsl_scr_read(ap, SCR_ERROR, &SError); + sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError); if (unlikely(SError & 0xFFFF0000)) { - sata_fsl_scr_write(ap, SCR_ERROR, SError); + sata_fsl_scr_write(&ap->link, SCR_ERROR, SError); } DPRINTK("error_intr,hStat=0x%x,CE=0x%x,DE =0x%x,SErr=0x%x\n", @@ -1091,7 +1091,7 @@ static void sata_fsl_host_intr(struct ata_port *ap) hstatus = ioread32(hcr_base + HSTATUS); - sata_fsl_scr_read(ap, SCR_ERROR, &SError); + sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError); if (unlikely(SError & 0xFFFF0000)) { DPRINTK("serror @host_intr : 0x%x\n", SError); diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 5032c32fa505..fbbd87c96f10 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -269,9 +269,9 @@ static void inic_reset_port(void __iomem *port_base) writeb(0xff, port_base + PORT_IRQ_STAT); } -static int inic_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) +static int inic_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) { - void __iomem *scr_addr = inic_port_base(ap) + PORT_SCR; + void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; void __iomem *addr; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) @@ -286,9 +286,9 @@ static int inic_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) return 0; } -static int inic_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +static int inic_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) { - void __iomem *scr_addr = inic_port_base(ap) + PORT_SCR; + void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) return -EINVAL; diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index c815f8ecf6e6..2b24ae58b52e 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -493,10 +493,10 @@ struct mv_hw_ops { void (*reset_bus)(struct ata_host *host, void __iomem *mmio); }; -static int mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val); -static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); -static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val); -static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); +static int mv_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val); +static int mv_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val); +static int mv5_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val); +static int mv5_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val); static int mv_port_start(struct ata_port *ap); static void mv_port_stop(struct ata_port *ap); static int mv_qc_defer(struct ata_queued_cmd *qc); @@ -1070,23 +1070,23 @@ static unsigned int mv_scr_offset(unsigned int sc_reg_in) return ofs; } -static int mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) +static int mv_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val) { unsigned int ofs = mv_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { - *val = readl(mv_ap_base(ap) + ofs); + *val = readl(mv_ap_base(link->ap) + ofs); return 0; } else return -EINVAL; } -static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int mv_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) { unsigned int ofs = mv_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { - writelfl(val, mv_ap_base(ap) + ofs); + writelfl(val, mv_ap_base(link->ap) + ofs); return 0; } else return -EINVAL; @@ -2251,11 +2251,11 @@ static unsigned int mv5_scr_offset(unsigned int sc_reg_in) return ofs; } -static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) +static int mv5_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val) { - struct mv_host_priv *hpriv = ap->host->private_data; + struct mv_host_priv *hpriv = link->ap->host->private_data; void __iomem *mmio = hpriv->base; - void __iomem *addr = mv5_phy_base(mmio, ap->port_no); + void __iomem *addr = mv5_phy_base(mmio, link->ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { @@ -2265,11 +2265,11 @@ static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) return -EINVAL; } -static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int mv5_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) { - struct mv_host_priv *hpriv = ap->host->private_data; + struct mv_host_priv *hpriv = link->ap->host->private_data; void __iomem *mmio = hpriv->base; - void __iomem *addr = mv5_phy_base(mmio, ap->port_no); + void __iomem *addr = mv5_phy_base(mmio, link->ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 1e1f3f3757ae..88fd4aeacde0 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -302,8 +302,8 @@ static void nv_ck804_host_stop(struct ata_host *host); static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance); static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance); static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance); -static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static void nv_nf2_freeze(struct ata_port *ap); static void nv_nf2_thaw(struct ata_port *ap); @@ -1492,21 +1492,21 @@ static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance) return ret; } -static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = ioread32(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + iowrite32(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } @@ -2184,9 +2184,9 @@ static void nv_swncq_host_interrupt(struct ata_port *ap, u16 fis) if (!pp->qc_active) return; - if (ap->ops->scr_read(ap, SCR_ERROR, &serror)) + if (ap->ops->scr_read(&ap->link, SCR_ERROR, &serror)) return; - ap->ops->scr_write(ap, SCR_ERROR, serror); + ap->ops->scr_write(&ap->link, SCR_ERROR, serror); if (ata_stat & ATA_ERR) { ata_ehi_clear_desc(ehi); diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 030665ba76b7..750d8cdc00cd 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -137,8 +137,8 @@ struct pdc_port_priv { dma_addr_t pkt_dma; }; -static int pdc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int pdc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int pdc_sata_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int pdc_sata_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int pdc_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int pdc_common_port_start(struct ata_port *ap); static int pdc_sata_port_start(struct ata_port *ap); @@ -386,19 +386,21 @@ static int pdc_sata_cable_detect(struct ata_port *ap) return ATA_CBL_SATA; } -static int pdc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int pdc_sata_scr_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int pdc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int pdc_sata_scr_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } @@ -731,7 +733,7 @@ static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc, if (sata_scr_valid(&ap->link)) { u32 serror; - pdc_sata_scr_read(ap, SCR_ERROR, &serror); + pdc_sata_scr_read(&ap->link, SCR_ERROR, &serror); ehi->serror |= serror; } diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 1600107047cf..a000c86ac859 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -111,8 +111,8 @@ struct qs_port_priv { qs_state_t state; }; -static int qs_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int qs_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int qs_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int qs_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int qs_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int qs_port_start(struct ata_port *ap); static void qs_host_stop(struct ata_host *host); @@ -242,11 +242,11 @@ static int qs_prereset(struct ata_link *link, unsigned long deadline) return ata_sff_prereset(link, deadline); } -static int qs_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int qs_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 8)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 8)); return 0; } @@ -256,11 +256,11 @@ static void qs_error_handler(struct ata_port *ap) ata_std_error_handler(ap); } -static int qs_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int qs_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 8)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 8)); return 0; } diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 88bf4212590f..031d7b7dee34 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -115,8 +115,8 @@ static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int sil_pci_device_resume(struct pci_dev *pdev); #endif static void sil_dev_config(struct ata_device *dev); -static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int sil_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int sil_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int sil_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed); static void sil_freeze(struct ata_port *ap); static void sil_thaw(struct ata_port *ap); @@ -317,9 +317,9 @@ static inline void __iomem *sil_scr_addr(struct ata_port *ap, return NULL; } -static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int sil_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { - void __iomem *mmio = sil_scr_addr(ap, sc_reg); + void __iomem *mmio = sil_scr_addr(link->ap, sc_reg); if (mmio) { *val = readl(mmio); @@ -328,9 +328,9 @@ static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return -EINVAL; } -static int sil_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sil_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { - void __iomem *mmio = sil_scr_addr(ap, sc_reg); + void __iomem *mmio = sil_scr_addr(link->ap, sc_reg); if (mmio) { writel(val, mmio); @@ -352,8 +352,8 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2) * controllers continue to assert IRQ as long as * SError bits are pending. Clear SError immediately. */ - sil_scr_read(ap, SCR_ERROR, &serror); - sil_scr_write(ap, SCR_ERROR, serror); + sil_scr_read(&ap->link, SCR_ERROR, &serror); + sil_scr_write(&ap->link, SCR_ERROR, serror); /* Sometimes spurious interrupts occur, double check * it's PHYRDY CHG. diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 84ffcc26a74b..4621807a1a6a 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -340,8 +340,8 @@ struct sil24_port_priv { }; static void sil24_dev_config(struct ata_device *dev); -static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val); -static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); +static int sil24_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val); +static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val); static int sil24_qc_defer(struct ata_queued_cmd *qc); static void sil24_qc_prep(struct ata_queued_cmd *qc); static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); @@ -504,9 +504,9 @@ static int sil24_scr_map[] = { [SCR_ACTIVE] = 3, }; -static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) +static int sil24_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) { - void __iomem *scr_addr = sil24_port_base(ap) + PORT_SCONTROL; + void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; @@ -517,9 +517,9 @@ static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) return -EINVAL; } -static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) { - void __iomem *scr_addr = sil24_port_base(ap) + PORT_SCONTROL; + void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 1010b3069bd5..9c43b4e7c4a6 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -64,8 +64,8 @@ enum { }; static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int sis_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static const struct pci_device_id sis_pci_tbl[] = { { PCI_VDEVICE(SI, 0x0180), sis_180 }, /* SiS 964/180 */ @@ -134,10 +134,11 @@ static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) return addr; } -static u32 sis_scr_cfg_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static u32 sis_scr_cfg_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); u32 val2 = 0; u8 pmr; @@ -158,10 +159,11 @@ static u32 sis_scr_cfg_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return 0; } -static int sis_scr_cfg_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sis_scr_cfg_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); u8 pmr; if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */ @@ -178,8 +180,9 @@ static int sis_scr_cfg_write(struct ata_port *ap, unsigned int sc_reg, u32 val) return 0; } -static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int sis_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { + struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 pmr; @@ -187,7 +190,7 @@ static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return -EINVAL; if (ap->flags & SIS_FLAG_CFGSCR) - return sis_scr_cfg_read(ap, sc_reg, val); + return sis_scr_cfg_read(link, sc_reg, val); pci_read_config_byte(pdev, SIS_PMR, &pmr); @@ -202,8 +205,9 @@ static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return 0; } -static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { + struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 pmr; @@ -213,7 +217,7 @@ static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) pci_read_config_byte(pdev, SIS_PMR, &pmr); if (ap->flags & SIS_FLAG_CFGSCR) - return sis_scr_cfg_write(ap, sc_reg, val); + return sis_scr_cfg_write(link, sc_reg, val); else { iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4)); if ((pdev->device == 0x0182) || (pdev->device == 0x0183) || diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index fb13b82aacba..609d147813ae 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -123,20 +123,22 @@ static int k2_sata_check_atapi_dma(struct ata_queued_cmd *qc) } } -static int k2_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int k2_sata_scr_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int k2_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int k2_sata_scr_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index db529b849948..019575bb3e08 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -57,8 +57,8 @@ struct uli_priv { }; static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int uli_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int uli_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static const struct pci_device_id uli_pci_tbl[] = { { PCI_VDEVICE(AL, 0x5289), uli_5289 }, @@ -107,39 +107,39 @@ static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) return hpriv->scr_cfg_addr[ap->port_no] + (4 * sc_reg); } -static u32 uli_scr_cfg_read(struct ata_port *ap, unsigned int sc_reg) +static u32 uli_scr_cfg_read(struct ata_link *link, unsigned int sc_reg) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); u32 val; pci_read_config_dword(pdev, cfg_addr, &val); return val; } -static void uli_scr_cfg_write(struct ata_port *ap, unsigned int scr, u32 val) +static void uli_scr_cfg_write(struct ata_link *link, unsigned int scr, u32 val) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, scr); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, scr); pci_write_config_dword(pdev, cfg_addr, val); } -static int uli_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = uli_scr_cfg_read(ap, sc_reg); + *val = uli_scr_cfg_read(link, sc_reg); return 0; } -static int uli_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 return -EINVAL; - uli_scr_cfg_write(ap, sc_reg, val); + uli_scr_cfg_write(link, sc_reg, val); return 0; } diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 96deeb354e16..1cfa74535d91 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -68,8 +68,8 @@ enum { }; static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int svia_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int svia_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static void svia_noop_freeze(struct ata_port *ap); static int vt6420_prereset(struct ata_link *link, unsigned long deadline); static int vt6421_pata_cable_detect(struct ata_port *ap); @@ -152,19 +152,19 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, svia_pci_tbl); MODULE_VERSION(DRV_VERSION); -static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int svia_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = ioread32(ap->ioaddr.scr_addr + (4 * sc_reg)); + *val = ioread32(link->ap->ioaddr.scr_addr + (4 * sc_reg)); return 0; } -static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int svia_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - iowrite32(val, ap->ioaddr.scr_addr + (4 * sc_reg)); + iowrite32(val, link->ap->ioaddr.scr_addr + (4 * sc_reg)); return 0; } @@ -210,20 +210,20 @@ static int vt6420_prereset(struct ata_link *link, unsigned long deadline) goto skip_scr; /* Resume phy. This is the old SATA resume sequence */ - svia_scr_write(ap, SCR_CONTROL, 0x300); - svia_scr_read(ap, SCR_CONTROL, &scontrol); /* flush */ + svia_scr_write(link, SCR_CONTROL, 0x300); + svia_scr_read(link, SCR_CONTROL, &scontrol); /* flush */ /* wait for phy to become ready, if necessary */ do { msleep(200); - svia_scr_read(ap, SCR_STATUS, &sstatus); + svia_scr_read(link, SCR_STATUS, &sstatus); if ((sstatus & 0xf) != 1) break; } while (time_before(jiffies, timeout)); /* open code sata_print_link_status() */ - svia_scr_read(ap, SCR_STATUS, &sstatus); - svia_scr_read(ap, SCR_CONTROL, &scontrol); + svia_scr_read(link, SCR_STATUS, &sstatus); + svia_scr_read(link, SCR_CONTROL, &scontrol); online = (sstatus & 0xf) == 0x3; @@ -232,7 +232,7 @@ static int vt6420_prereset(struct ata_link *link, unsigned long deadline) online ? "up" : "down", sstatus, scontrol); /* SStatus is read one more time */ - svia_scr_read(ap, SCR_STATUS, &sstatus); + svia_scr_read(link, SCR_STATUS, &sstatus); if (!online) { /* tell EH to bail */ diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index f3d635c0a2e9..c57cdff9e6bd 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -98,20 +98,22 @@ enum { VSC_SATA_INT_PHY_CHANGE), }; -static int vsc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int vsc_sata_scr_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int vsc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int vsc_sata_scr_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48ee8c7f5bdd..e1872989710a 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -294,10 +294,10 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) } } -static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, +static int sas_ata_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) { - struct domain_device *dev = ap->private_data; + struct domain_device *dev = link->ap->private_data; SAS_DPRINTK("STUB %s\n", __func__); switch (sc_reg_in) { @@ -319,10 +319,10 @@ static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, return 0; } -static int sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in, +static int sas_ata_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val) { - struct domain_device *dev = ap->private_data; + struct domain_device *dev = link->ap->private_data; SAS_DPRINTK("STUB %s\n", __func__); switch (sc_reg_in) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 225bfc5bd9ec..ffd622fa319c 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -772,8 +772,8 @@ struct ata_port_operations { /* * Optional features */ - int (*scr_read)(struct ata_port *ap, unsigned int sc_reg, u32 *val); - int (*scr_write)(struct ata_port *ap, unsigned int sc_reg, u32 val); + int (*scr_read)(struct ata_link *link, unsigned int sc_reg, u32 *val); + int (*scr_write)(struct ata_link *link, unsigned int sc_reg, u32 val); void (*pmp_attach)(struct ata_port *ap); void (*pmp_detach)(struct ata_port *ap); int (*enable_pm)(struct ata_port *ap, enum link_pm policy); -- cgit v1.2.3 From aadffb682cc5572f48cc24883681db65530bd284 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 31 Jul 2008 17:02:41 +0900 Subject: libata: reimplement link iterator Implement __ata_port_next_link() and reimplement __ata_port_for_each_link() and ata_port_for_each_link() using it. This removes relatively large inlined code and makes iteration easier to extend. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 30 ++++++++++++++++++++++++++++++ include/linux/libata.h | 33 ++++++++------------------------- 2 files changed, 38 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 825461a33abe..d156616f45f5 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -163,6 +163,35 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +/* + * Iterator helpers. Don't use directly. + * + * LOCKING: + * Host lock or EH context. + */ +struct ata_link *__ata_port_next_link(struct ata_port *ap, + struct ata_link *link, bool dev_only) +{ + /* NULL link indicates start of iteration */ + if (!link) { + if (dev_only && sata_pmp_attached(ap)) + return ap->pmp_link; + return &ap->link; + } + + /* we just iterated over the host link, what's next? */ + if (ata_is_host_link(link)) { + if (!sata_pmp_attached(ap)) + return NULL; + return ap->pmp_link; + } + + /* iterate to the next PMP link */ + if (++link < ap->pmp_link + ap->nr_pmp_links) + return link; + return NULL; +} + /** * ata_force_cbl - force cable type according to libata.force * @ap: ATA port of interest @@ -6255,6 +6284,7 @@ EXPORT_SYMBOL_GPL(ata_base_port_ops); EXPORT_SYMBOL_GPL(sata_port_ops); EXPORT_SYMBOL_GPL(ata_dummy_port_ops); EXPORT_SYMBOL_GPL(ata_dummy_port_info); +EXPORT_SYMBOL_GPL(__ata_port_next_link); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_host_init); EXPORT_SYMBOL_GPL(ata_host_alloc); diff --git a/include/linux/libata.h b/include/linux/libata.h index ffd622fa319c..3eaca347ce29 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1265,34 +1265,17 @@ static inline int ata_link_active(struct ata_link *link) return ata_tag_valid(link->active_tag) || link->sactive; } -static inline struct ata_link *ata_port_first_link(struct ata_port *ap) -{ - if (sata_pmp_attached(ap)) - return ap->pmp_link; - return &ap->link; -} - -static inline struct ata_link *ata_port_next_link(struct ata_link *link) -{ - struct ata_port *ap = link->ap; - - if (ata_is_host_link(link)) { - if (!sata_pmp_attached(ap)) - return NULL; - return ap->pmp_link; - } - - if (++link < ap->nr_pmp_links + ap->pmp_link) - return link; - return NULL; -} +extern struct ata_link *__ata_port_next_link(struct ata_port *ap, + struct ata_link *link, + bool dev_only); -#define __ata_port_for_each_link(lk, ap) \ - for ((lk) = &(ap)->link; (lk); (lk) = ata_port_next_link(lk)) +#define __ata_port_for_each_link(link, ap) \ + for ((link) = __ata_port_next_link((ap), NULL, false); (link); \ + (link) = __ata_port_next_link((ap), (link), false)) #define ata_port_for_each_link(link, ap) \ - for ((link) = ata_port_first_link(ap); (link); \ - (link) = ata_port_next_link(link)) + for ((link) = __ata_port_next_link((ap), NULL, true); (link); \ + (link) = __ata_port_next_link((ap), (link), true)) #define ata_link_for_each_dev(dev, link) \ for ((dev) = (link)->device; \ -- cgit v1.2.3 From b5b3fa386b8f96c7fa92e507e5deddc2637924b4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 31 Jul 2008 17:02:42 +0900 Subject: libata: misc updates to prepare for slave link * Add ATA_EH_ALL_ACTIONS. * Make sata_link_{on|off}_line() return bool instead of int. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 16 ++++++++-------- include/linux/libata.h | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d156616f45f5..71024e94c576 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4976,16 +4976,16 @@ int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) * None. * * RETURNS: - * 1 if the port online status is available and online. + * True if the port online status is available and online. */ -int ata_link_online(struct ata_link *link) +bool ata_link_online(struct ata_link *link) { u32 sstatus; if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0 && (sstatus & 0xf) == 0x3) - return 1; - return 0; + return true; + return false; } /** @@ -5000,16 +5000,16 @@ int ata_link_online(struct ata_link *link) * None. * * RETURNS: - * 1 if the port offline status is available and offline. + * True if the port offline status is available and offline. */ -int ata_link_offline(struct ata_link *link) +bool ata_link_offline(struct ata_link *link) { u32 sstatus; if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0 && (sstatus & 0xf) != 0x3) - return 1; - return 0; + return true; + return false; } #ifdef CONFIG_PM diff --git a/include/linux/libata.h b/include/linux/libata.h index 3eaca347ce29..0c7e6f3c28eb 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -321,6 +321,8 @@ enum { ATA_EH_LPM = (1 << 4), /* link power management action */ ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE, + ATA_EH_ALL_ACTIONS = ATA_EH_REVALIDATE | ATA_EH_RESET | + ATA_EH_ENABLE_LINK | ATA_EH_LPM, /* ata_eh_info->flags */ ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */ @@ -920,8 +922,8 @@ extern int sata_scr_valid(struct ata_link *link); extern int sata_scr_read(struct ata_link *link, int reg, u32 *val); extern int sata_scr_write(struct ata_link *link, int reg, u32 val); extern int sata_scr_write_flush(struct ata_link *link, int reg, u32 val); -extern int ata_link_online(struct ata_link *link); -extern int ata_link_offline(struct ata_link *link); +extern bool ata_link_online(struct ata_link *link); +extern bool ata_link_offline(struct ata_link *link); #ifdef CONFIG_PM extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg); extern void ata_host_resume(struct ata_host *host); -- cgit v1.2.3 From b1c72916abbdd0a55015c87358536ca0ebaf6735 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 31 Jul 2008 17:02:43 +0900 Subject: libata: implement slave_link Explanation taken from the comment of ata_slave_link_init(). In libata, a port contains links and a link contains devices. There is single host link but if a PMP is attached to it, there can be multiple fan-out links. On SATA, there's usually a single device connected to a link but PATA and SATA controllers emulating TF based interface can have two - master and slave. However, there are a few controllers which don't fit into this abstraction too well - SATA controllers which emulate TF interface with both master and slave devices but also have separate SCR register sets for each device. These controllers need separate links for physical link handling (e.g. onlineness, link speed) but should be treated like a traditional M/S controller for everything else (e.g. command issue, softreset). slave_link is libata's way of handling this class of controllers without impacting core layer too much. For anything other than physical link handling, the default host link is used for both master and slave. For physical link handling, separate @ap->slave_link is used. All dirty details are implemented inside libata core layer. From LLD's POV, the only difference is that prereset, hardreset and postreset are called once more for the slave link, so the reset sequence looks like the following. prereset(M) -> prereset(S) -> hardreset(M) -> hardreset(S) -> softreset(M) -> postreset(M) -> postreset(S) Note that softreset is called only for the master. Softreset resets both M/S by definition, so SRST on master should handle both (the standard method will work just fine). As slave_link excludes PMP support and only code paths which deal with the attributes of physical link are affected, all the changes are localized to libata.h, libata-core.c and libata-eh.c. * ata_is_host_link() updated so that slave_link is considered as host link too. * iterator extended to iterate over the slave_link when using the underbarred version. * force param handling updated such that devno 16 is mapped to the slave link/device. * ata_link_on/offline() updated to return the combined result from master and slave link. ata_phys_link_on/offline() are the direct versions. * EH autopsy and report are performed separately for master slave links. Reset is udpated to implement the above described reset sequence. Except for reset update, most changes are minor, many of them just modifying dev->link to ata_dev_phys_link(dev) or using phys online test instead. After this update, LLDs can take full advantage of per-dev SCR registers by simply turning on slave link. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 202 ++++++++++++++++++++++++++++++++++++++++------ drivers/ata/libata-eh.c | 142 ++++++++++++++++++++++++++------ drivers/ata/libata.h | 3 + include/linux/libata.h | 8 +- 4 files changed, 303 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 71024e94c576..6eed58e35e12 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -179,19 +179,51 @@ struct ata_link *__ata_port_next_link(struct ata_port *ap, return &ap->link; } - /* we just iterated over the host link, what's next? */ - if (ata_is_host_link(link)) { - if (!sata_pmp_attached(ap)) + /* we just iterated over the host master link, what's next? */ + if (link == &ap->link) { + if (!sata_pmp_attached(ap)) { + if (unlikely(ap->slave_link) && !dev_only) + return ap->slave_link; return NULL; + } return ap->pmp_link; } + /* slave_link excludes PMP */ + if (unlikely(link == ap->slave_link)) + return NULL; + /* iterate to the next PMP link */ if (++link < ap->pmp_link + ap->nr_pmp_links) return link; return NULL; } +/** + * ata_dev_phys_link - find physical link for a device + * @dev: ATA device to look up physical link for + * + * Look up physical link which @dev is attached to. Note that + * this is different from @dev->link only when @dev is on slave + * link. For all other cases, it's the same as @dev->link. + * + * LOCKING: + * Don't care. + * + * RETURNS: + * Pointer to the found physical link. + */ +struct ata_link *ata_dev_phys_link(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + + if (!ap->slave_link) + return dev->link; + if (!dev->devno) + return &ap->link; + return ap->slave_link; +} + /** * ata_force_cbl - force cable type according to libata.force * @ap: ATA port of interest @@ -235,7 +267,8 @@ void ata_force_cbl(struct ata_port *ap) * the host link and all fan-out ports connected via PMP. If the * device part is specified as 0 (e.g. 1.00:), it specifies the * first fan-out link not the host link. Device number 15 always - * points to the host link whether PMP is attached or not. + * points to the host link whether PMP is attached or not. If the + * controller has slave link, device number 16 points to it. * * LOCKING: * EH context. @@ -243,12 +276,11 @@ void ata_force_cbl(struct ata_port *ap) static void ata_force_link_limits(struct ata_link *link) { bool did_spd = false; - int linkno, i; + int linkno = link->pmp; + int i; if (ata_is_host_link(link)) - linkno = 15; - else - linkno = link->pmp; + linkno += 15; for (i = ata_force_tbl_size - 1; i >= 0; i--) { const struct ata_force_ent *fe = &ata_force_tbl[i]; @@ -295,9 +327,9 @@ static void ata_force_xfermask(struct ata_device *dev) int alt_devno = devno; int i; - /* allow n.15 for the first device attached to host port */ - if (ata_is_host_link(dev->link) && devno == 0) - alt_devno = 15; + /* allow n.15/16 for devices attached to host port */ + if (ata_is_host_link(dev->link)) + alt_devno += 15; for (i = ata_force_tbl_size - 1; i >= 0; i--) { const struct ata_force_ent *fe = &ata_force_tbl[i]; @@ -349,9 +381,9 @@ static void ata_force_horkage(struct ata_device *dev) int alt_devno = devno; int i; - /* allow n.15 for the first device attached to host port */ - if (ata_is_host_link(dev->link) && devno == 0) - alt_devno = 15; + /* allow n.15/16 for devices attached to host port */ + if (ata_is_host_link(dev->link)) + alt_devno += 15; for (i = 0; i < ata_force_tbl_size; i++) { const struct ata_force_ent *fe = &ata_force_tbl[i]; @@ -2710,7 +2742,7 @@ static void sata_print_link_status(struct ata_link *link) return; sata_scr_read(link, SCR_CONTROL, &scontrol); - if (ata_link_online(link)) { + if (ata_phys_link_online(link)) { tmp = (sstatus >> 4) & 0xf; ata_link_printk(link, KERN_INFO, "SATA link up %s (SStatus %X SControl %X)\n", @@ -3401,6 +3433,12 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline, unsigned long nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT); int warned = 0; + /* Slave readiness can't be tested separately from master. On + * M/S emulation configuration, this function should be called + * only on the master and it will handle both master and slave. + */ + WARN_ON(link == link->ap->slave_link); + if (time_after(nodev_deadline, deadline)) nodev_deadline = deadline; @@ -3622,7 +3660,7 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline) } /* no point in trying softreset on offline link */ - if (ata_link_offline(link)) + if (ata_phys_link_offline(link)) ehc->i.action &= ~ATA_EH_SOFTRESET; return 0; @@ -3700,7 +3738,7 @@ int sata_link_hardreset(struct ata_link *link, const unsigned long *timing, if (rc) goto out; /* if link is offline nothing more to do */ - if (ata_link_offline(link)) + if (ata_phys_link_offline(link)) goto out; /* Link is online. From this point, -ENODEV too is an error. */ @@ -4965,7 +5003,7 @@ int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) } /** - * ata_link_online - test whether the given link is online + * ata_phys_link_online - test whether the given link is online * @link: ATA link to test * * Test whether @link is online. Note that this function returns @@ -4978,7 +5016,7 @@ int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) * RETURNS: * True if the port online status is available and online. */ -bool ata_link_online(struct ata_link *link) +bool ata_phys_link_online(struct ata_link *link) { u32 sstatus; @@ -4989,7 +5027,7 @@ bool ata_link_online(struct ata_link *link) } /** - * ata_link_offline - test whether the given link is offline + * ata_phys_link_offline - test whether the given link is offline * @link: ATA link to test * * Test whether @link is offline. Note that this function @@ -5002,7 +5040,7 @@ bool ata_link_online(struct ata_link *link) * RETURNS: * True if the port offline status is available and offline. */ -bool ata_link_offline(struct ata_link *link) +bool ata_phys_link_offline(struct ata_link *link) { u32 sstatus; @@ -5012,6 +5050,58 @@ bool ata_link_offline(struct ata_link *link) return false; } +/** + * ata_link_online - test whether the given link is online + * @link: ATA link to test + * + * Test whether @link is online. This is identical to + * ata_phys_link_online() when there's no slave link. When + * there's a slave link, this function should only be called on + * the master link and will return true if any of M/S links is + * online. + * + * LOCKING: + * None. + * + * RETURNS: + * True if the port online status is available and online. + */ +bool ata_link_online(struct ata_link *link) +{ + struct ata_link *slave = link->ap->slave_link; + + WARN_ON(link == slave); /* shouldn't be called on slave link */ + + return ata_phys_link_online(link) || + (slave && ata_phys_link_online(slave)); +} + +/** + * ata_link_offline - test whether the given link is offline + * @link: ATA link to test + * + * Test whether @link is offline. This is identical to + * ata_phys_link_offline() when there's no slave link. When + * there's a slave link, this function should only be called on + * the master link and will return true if both M/S links are + * offline. + * + * LOCKING: + * None. + * + * RETURNS: + * True if the port offline status is available and offline. + */ +bool ata_link_offline(struct ata_link *link) +{ + struct ata_link *slave = link->ap->slave_link; + + WARN_ON(link == slave); /* shouldn't be called on slave link */ + + return ata_phys_link_offline(link) && + (!slave || ata_phys_link_offline(slave)); +} + #ifdef CONFIG_PM static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg, unsigned int action, unsigned int ehi_flags, @@ -5151,11 +5241,11 @@ int ata_port_start(struct ata_port *ap) */ void ata_dev_init(struct ata_device *dev) { - struct ata_link *link = dev->link; + struct ata_link *link = ata_dev_phys_link(dev); struct ata_port *ap = link->ap; unsigned long flags; - /* SATA spd limit is bound to the first device */ + /* SATA spd limit is bound to the attached device, reset together */ link->sata_spd_limit = link->hw_sata_spd_limit; link->sata_spd = 0; @@ -5318,6 +5408,7 @@ static void ata_host_release(struct device *gendev, void *res) scsi_host_put(ap->scsi_host); kfree(ap->pmp_link); + kfree(ap->slave_link); kfree(ap); host->ports[i] = NULL; } @@ -5438,6 +5529,68 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev, return host; } +/** + * ata_slave_link_init - initialize slave link + * @ap: port to initialize slave link for + * + * Create and initialize slave link for @ap. This enables slave + * link handling on the port. + * + * In libata, a port contains links and a link contains devices. + * There is single host link but if a PMP is attached to it, + * there can be multiple fan-out links. On SATA, there's usually + * a single device connected to a link but PATA and SATA + * controllers emulating TF based interface can have two - master + * and slave. + * + * However, there are a few controllers which don't fit into this + * abstraction too well - SATA controllers which emulate TF + * interface with both master and slave devices but also have + * separate SCR register sets for each device. These controllers + * need separate links for physical link handling + * (e.g. onlineness, link speed) but should be treated like a + * traditional M/S controller for everything else (e.g. command + * issue, softreset). + * + * slave_link is libata's way of handling this class of + * controllers without impacting core layer too much. For + * anything other than physical link handling, the default host + * link is used for both master and slave. For physical link + * handling, separate @ap->slave_link is used. All dirty details + * are implemented inside libata core layer. From LLD's POV, the + * only difference is that prereset, hardreset and postreset are + * called once more for the slave link, so the reset sequence + * looks like the following. + * + * prereset(M) -> prereset(S) -> hardreset(M) -> hardreset(S) -> + * softreset(M) -> postreset(M) -> postreset(S) + * + * Note that softreset is called only for the master. Softreset + * resets both M/S by definition, so SRST on master should handle + * both (the standard method will work just fine). + * + * LOCKING: + * Should be called before host is registered. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +int ata_slave_link_init(struct ata_port *ap) +{ + struct ata_link *link; + + WARN_ON(ap->slave_link); + WARN_ON(ap->flags & ATA_FLAG_PMP); + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + ata_link_init(ap, link, 1); + ap->slave_link = link; + return 0; +} + static void ata_host_stop(struct device *gendev, void *res) { struct ata_host *host = dev_get_drvdata(gendev); @@ -5664,6 +5817,8 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) /* init sata_spd_limit to the current value */ sata_link_init_spd(&ap->link); + if (ap->slave_link) + sata_link_init_spd(ap->slave_link); /* print per-port info to dmesg */ xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, @@ -6289,6 +6444,7 @@ EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_host_init); EXPORT_SYMBOL_GPL(ata_host_alloc); EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo); +EXPORT_SYMBOL_GPL(ata_slave_link_init); EXPORT_SYMBOL_GPL(ata_host_start); EXPORT_SYMBOL_GPL(ata_host_register); EXPORT_SYMBOL_GPL(ata_host_activate); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index c1db2f234d2e..99037a4860d9 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1756,7 +1756,7 @@ static unsigned int ata_eh_speed_down_verdict(struct ata_device *dev) static unsigned int ata_eh_speed_down(struct ata_device *dev, unsigned int eflags, unsigned int err_mask) { - struct ata_link *link = dev->link; + struct ata_link *link = ata_dev_phys_link(dev); int xfer_ok = 0; unsigned int verdict; unsigned int action = 0; @@ -1880,7 +1880,8 @@ static void ata_eh_link_autopsy(struct ata_link *link) for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - if (!(qc->flags & ATA_QCFLAG_FAILED) || qc->dev->link != link) + if (!(qc->flags & ATA_QCFLAG_FAILED) || + ata_dev_phys_link(qc->dev) != link) continue; /* inherit upper level err_mask */ @@ -1967,6 +1968,23 @@ void ata_eh_autopsy(struct ata_port *ap) ata_port_for_each_link(link, ap) ata_eh_link_autopsy(link); + /* Handle the frigging slave link. Autopsy is done similarly + * but actions and flags are transferred over to the master + * link and handled from there. + */ + if (ap->slave_link) { + struct ata_eh_context *mehc = &ap->link.eh_context; + struct ata_eh_context *sehc = &ap->slave_link->eh_context; + + ata_eh_link_autopsy(ap->slave_link); + + ata_eh_about_to_do(ap->slave_link, NULL, ATA_EH_ALL_ACTIONS); + mehc->i.action |= sehc->i.action; + mehc->i.dev_action[1] |= sehc->i.dev_action[1]; + mehc->i.flags |= sehc->i.flags; + ata_eh_done(ap->slave_link, NULL, ATA_EH_ALL_ACTIONS); + } + /* Autopsy of fanout ports can affect host link autopsy. * Perform host link autopsy last. */ @@ -2001,7 +2019,8 @@ static void ata_eh_link_report(struct ata_link *link) for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - if (!(qc->flags & ATA_QCFLAG_FAILED) || qc->dev->link != link || + if (!(qc->flags & ATA_QCFLAG_FAILED) || + ata_dev_phys_link(qc->dev) != link || ((qc->flags & ATA_QCFLAG_QUIET) && qc->err_mask == AC_ERR_DEV)) continue; @@ -2068,7 +2087,7 @@ static void ata_eh_link_report(struct ata_link *link) char cdb_buf[70] = ""; if (!(qc->flags & ATA_QCFLAG_FAILED) || - qc->dev->link != link || !qc->err_mask) + ata_dev_phys_link(qc->dev) != link || !qc->err_mask) continue; if (qc->dma_dir != DMA_NONE) { @@ -2160,12 +2179,14 @@ void ata_eh_report(struct ata_port *ap) } static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset, - unsigned int *classes, unsigned long deadline) + unsigned int *classes, unsigned long deadline, + bool clear_classes) { struct ata_device *dev; - ata_link_for_each_dev(dev, link) - classes[dev->devno] = ATA_DEV_UNKNOWN; + if (clear_classes) + ata_link_for_each_dev(dev, link) + classes[dev->devno] = ATA_DEV_UNKNOWN; return reset(link, classes, deadline); } @@ -2187,17 +2208,20 @@ int ata_eh_reset(struct ata_link *link, int classify, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) { struct ata_port *ap = link->ap; + struct ata_link *slave = ap->slave_link; struct ata_eh_context *ehc = &link->eh_context; + struct ata_eh_context *sehc = &slave->eh_context; unsigned int *classes = ehc->classes; unsigned int lflags = link->flags; int verbose = !(ehc->i.flags & ATA_EHI_QUIET); int max_tries = 0, try = 0; + struct ata_link *failed_link; struct ata_device *dev; unsigned long deadline, now; ata_reset_fn_t reset; unsigned long flags; u32 sstatus; - int nr_known, rc; + int nr_unknown, rc; /* * Prepare to reset @@ -2252,8 +2276,30 @@ int ata_eh_reset(struct ata_link *link, int classify, } if (prereset) { - rc = prereset(link, - ata_deadline(jiffies, ATA_EH_PRERESET_TIMEOUT)); + unsigned long deadline = ata_deadline(jiffies, + ATA_EH_PRERESET_TIMEOUT); + + if (slave) { + sehc->i.action &= ~ATA_EH_RESET; + sehc->i.action |= ehc->i.action; + } + + rc = prereset(link, deadline); + + /* If present, do prereset on slave link too. Reset + * is skipped iff both master and slave links report + * -ENOENT or clear ATA_EH_RESET. + */ + if (slave && (rc == 0 || rc == -ENOENT)) { + int tmp; + + tmp = prereset(slave, deadline); + if (tmp != -ENOENT) + rc = tmp; + + ehc->i.action |= sehc->i.action; + } + if (rc) { if (rc == -ENOENT) { ata_link_printk(link, KERN_DEBUG, @@ -2302,25 +2348,51 @@ int ata_eh_reset(struct ata_link *link, int classify, else ehc->i.flags |= ATA_EHI_DID_SOFTRESET; - rc = ata_do_reset(link, reset, classes, deadline); - if (rc && rc != -EAGAIN) + rc = ata_do_reset(link, reset, classes, deadline, true); + if (rc && rc != -EAGAIN) { + failed_link = link; goto fail; + } + + /* hardreset slave link if existent */ + if (slave && reset == hardreset) { + int tmp; + + if (verbose) + ata_link_printk(slave, KERN_INFO, + "hard resetting link\n"); + ata_eh_about_to_do(slave, NULL, ATA_EH_RESET); + tmp = ata_do_reset(slave, reset, classes, deadline, + false); + switch (tmp) { + case -EAGAIN: + rc = -EAGAIN; + case 0: + break; + default: + failed_link = slave; + rc = tmp; + goto fail; + } + } + + /* perform follow-up SRST if necessary */ if (reset == hardreset && ata_eh_followup_srst_needed(link, rc, classes)) { - /* okay, let's do follow-up softreset */ reset = softreset; if (!reset) { ata_link_printk(link, KERN_ERR, "follow-up softreset required " "but no softreset avaliable\n"); + failed_link = link; rc = -EINVAL; goto fail; } ata_eh_about_to_do(link, NULL, ATA_EH_RESET); - rc = ata_do_reset(link, reset, classes, deadline); + rc = ata_do_reset(link, reset, classes, deadline, true); } } else { if (verbose) @@ -2341,7 +2413,7 @@ int ata_eh_reset(struct ata_link *link, int classify, dev->pio_mode = XFER_PIO_0; dev->flags &= ~ATA_DFLAG_SLEEPING; - if (ata_link_offline(link)) + if (ata_phys_link_offline(ata_dev_phys_link(dev))) continue; /* apply class override */ @@ -2354,6 +2426,8 @@ int ata_eh_reset(struct ata_link *link, int classify, /* record current link speed */ if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0) link->sata_spd = (sstatus >> 4) & 0xf; + if (slave && sata_scr_read(slave, SCR_STATUS, &sstatus) == 0) + slave->sata_spd = (sstatus >> 4) & 0xf; /* thaw the port */ if (ata_is_host_link(link)) @@ -2366,12 +2440,17 @@ int ata_eh_reset(struct ata_link *link, int classify, * reset and here. This race is mediated by cross checking * link onlineness and classification result later. */ - if (postreset) + if (postreset) { postreset(link, classes); + if (slave) + postreset(slave, classes); + } /* clear cached SError */ spin_lock_irqsave(link->ap->lock, flags); link->eh_info.serror = 0; + if (slave) + slave->eh_info.serror = 0; spin_unlock_irqrestore(link->ap->lock, flags); /* Make sure onlineness and classification result correspond. @@ -2381,19 +2460,21 @@ int ata_eh_reset(struct ata_link *link, int classify, * link onlineness and classification result, those conditions * can be reliably detected and retried. */ - nr_known = 0; + nr_unknown = 0; ata_link_for_each_dev(dev, link) { /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */ - if (classes[dev->devno] == ATA_DEV_UNKNOWN) + if (classes[dev->devno] == ATA_DEV_UNKNOWN) { classes[dev->devno] = ATA_DEV_NONE; - else - nr_known++; + if (ata_phys_link_online(ata_dev_phys_link(dev))) + nr_unknown++; + } } - if (classify && !nr_known && ata_link_online(link)) { + if (classify && nr_unknown) { if (try < max_tries) { ata_link_printk(link, KERN_WARNING, "link online but " "device misclassified, retrying\n"); + failed_link = link; rc = -EAGAIN; goto fail; } @@ -2404,6 +2485,8 @@ int ata_eh_reset(struct ata_link *link, int classify, /* reset successful, schedule revalidation */ ata_eh_done(link, NULL, ATA_EH_RESET); + if (slave) + ata_eh_done(slave, NULL, ATA_EH_RESET); ehc->last_reset = jiffies; ehc->i.action |= ATA_EH_REVALIDATE; @@ -2411,6 +2494,8 @@ int ata_eh_reset(struct ata_link *link, int classify, out: /* clear hotplug flag */ ehc->i.flags &= ~ATA_EHI_HOTPLUGGED; + if (slave) + sehc->i.flags &= ~ATA_EHI_HOTPLUGGED; spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~ATA_PFLAG_RESETTING; @@ -2431,7 +2516,7 @@ int ata_eh_reset(struct ata_link *link, int classify, if (time_before(now, deadline)) { unsigned long delta = deadline - now; - ata_link_printk(link, KERN_WARNING, + ata_link_printk(failed_link, KERN_WARNING, "reset failed (errno=%d), retrying in %u secs\n", rc, DIV_ROUND_UP(jiffies_to_msecs(delta), 1000)); @@ -2439,8 +2524,13 @@ int ata_eh_reset(struct ata_link *link, int classify, delta = schedule_timeout_uninterruptible(delta); } - if (rc == -EPIPE || try == max_tries - 1) + if (try == max_tries - 1) { sata_down_spd_limit(link); + if (slave) + sata_down_spd_limit(slave); + } else if (rc == -EPIPE) + sata_down_spd_limit(failed_link); + if (hardreset) reset = hardreset; goto retry; @@ -2472,7 +2562,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) { WARN_ON(dev->class == ATA_DEV_PMP); - if (ata_link_offline(link)) { + if (ata_phys_link_offline(ata_dev_phys_link(dev))) { rc = -EIO; goto err; } @@ -2697,7 +2787,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err) /* This is the last chance, better to slow * down than lose it. */ - sata_down_spd_limit(dev->link); + sata_down_spd_limit(ata_dev_phys_link(dev)); ata_down_xfermask_limit(dev, ATA_DNXFER_PIO); } } @@ -2707,7 +2797,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err) ata_dev_disable(dev); /* detach if offline */ - if (ata_link_offline(dev->link)) + if (ata_phys_link_offline(ata_dev_phys_link(dev))) ata_eh_detach_dev(dev); /* schedule probe if necessary */ diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index ade5c75b6144..e96de96e3020 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -70,6 +70,7 @@ extern int atapi_passthru16; extern int libata_fua; extern int libata_noacpi; extern int libata_allow_tpm; +extern struct ata_link *ata_dev_phys_link(struct ata_device *dev); extern void ata_force_cbl(struct ata_port *ap); extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf); @@ -107,6 +108,8 @@ extern void ata_qc_issue(struct ata_queued_cmd *qc); extern void __ata_qc_complete(struct ata_queued_cmd *qc); extern int atapi_check_dma(struct ata_queued_cmd *qc); extern void swap_buf_le16(u16 *buf, unsigned int buf_words); +extern bool ata_phys_link_online(struct ata_link *link); +extern bool ata_phys_link_offline(struct ata_link *link); extern void ata_dev_init(struct ata_device *dev); extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp); extern int sata_link_init_spd(struct ata_link *link); diff --git a/include/linux/libata.h b/include/linux/libata.h index 0c7e6f3c28eb..244ff601559a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -690,7 +690,8 @@ struct ata_port { unsigned int qc_active; int nr_active_links; /* #links with active qcs */ - struct ata_link link; /* host default link */ + struct ata_link link; /* host default link */ + struct ata_link *slave_link; /* see ata_slave_link_init() */ int nr_pmp_links; /* nr of available PMP links */ struct ata_link *pmp_link; /* array of PMP links */ @@ -897,6 +898,7 @@ extern void ata_port_disable(struct ata_port *); extern struct ata_host *ata_host_alloc(struct device *dev, int max_ports); extern struct ata_host *ata_host_alloc_pinfo(struct device *dev, const struct ata_port_info * const * ppi, int n_ports); +extern int ata_slave_link_init(struct ata_port *ap); extern int ata_host_start(struct ata_host *host); extern int ata_host_register(struct ata_host *host, struct scsi_host_template *sht); @@ -1136,7 +1138,7 @@ static inline bool sata_pmp_attached(struct ata_port *ap) static inline int ata_is_host_link(const struct ata_link *link) { - return link == &link->ap->link; + return link == &link->ap->link || link == link->ap->slave_link; } #else /* CONFIG_SATA_PMP */ static inline bool sata_pmp_supported(struct ata_port *ap) @@ -1169,7 +1171,7 @@ static inline int sata_srst_pmp(struct ata_link *link) printk("%sata%u: "fmt, lv, (ap)->print_id , ##args) #define ata_link_printk(link, lv, fmt, args...) do { \ - if (sata_pmp_attached((link)->ap)) \ + if (sata_pmp_attached((link)->ap) || (link)->ap->slave_link) \ printk("%sata%u.%02u: "fmt, lv, (link)->ap->print_id, \ (link)->pmp , ##args); \ else \ -- cgit v1.2.3 From ea6ce53cd5d005455ec0a3cc1d45d3af0cb90919 Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Fri, 19 Sep 2008 23:46:01 +0200 Subject: [libata] Introduce ata_id_has_unload() Add a function to check an ATA device's id for head unload support as specified in ATA-7. Signed-off-by: Elias Oltmanns Signed-off-by: Jeff Garzik --- include/linux/ata.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ata.h b/include/linux/ata.h index 8a12d718c169..a26ebd25bac1 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -667,6 +667,15 @@ static inline int ata_id_has_dword_io(const u16 *id) return 0; } +static inline int ata_id_has_unload(const u16 *id) +{ + if (ata_id_major_version(id) >= 7 && + (id[ATA_ID_CFSSE] & 0xC000) == 0x4000 && + id[ATA_ID_CFSSE] & (1 << 13)) + return 1; + return 0; +} + static inline int ata_id_current_chs_valid(const u16 *id) { /* For ATA-1 devices, if the INITIALIZE DEVICE PARAMETERS command -- cgit v1.2.3 From 45fabbb77bd95adff7a80bde1c7a0ace1075fde6 Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Sun, 21 Sep 2008 11:54:08 +0200 Subject: libata: Implement disk shock protection support On user request (through sysfs), the IDLE IMMEDIATE command with UNLOAD FEATURE as specified in ATA-7 is issued to the device and processing of the request queue is stopped thereafter until the specified timeout expires or user space asks to resume normal operation. This is supposed to prevent the heads of a hard drive from accidentally crashing onto the platter when a heavy shock is anticipated (like a falling laptop expected to hit the floor). In fact, the whole port stops processing commands until the timeout has expired in order to avoid any resets due to failed commands on another device. Signed-off-by: Elias Oltmanns Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 1 + drivers/ata/libata-core.c | 1 + drivers/ata/libata-eh.c | 126 +++++++++++++++++++++++++++++++++++++++++++++- drivers/ata/libata-scsi.c | 108 +++++++++++++++++++++++++++++++++++++++ include/linux/libata.h | 13 ++++- 5 files changed, 246 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 6acea41eb7ca..aeadd00411a1 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -316,6 +316,7 @@ static struct device_attribute *ahci_shost_attrs[] = { static struct device_attribute *ahci_sdev_attrs[] = { &dev_attr_sw_activity, + &dev_attr_unload_heads, NULL }; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 6eed58e35e12..1ee9499bd343 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5378,6 +5378,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); + init_completion(&ap->park_req_pending); init_timer_deferrable(&ap->fastdrain_timer); ap->fastdrain_timer.function = ata_eh_fastdrain_timerfn; ap->fastdrain_timer.data = (unsigned long)ap; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 99037a4860d9..33ac5ea4f531 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2536,6 +2536,80 @@ int ata_eh_reset(struct ata_link *link, int classify, goto retry; } +static inline void ata_eh_pull_park_action(struct ata_port *ap) +{ + struct ata_link *link; + struct ata_device *dev; + unsigned long flags; + + /* + * This function can be thought of as an extended version of + * ata_eh_about_to_do() specially crafted to accommodate the + * requirements of ATA_EH_PARK handling. Since the EH thread + * does not leave the do {} while () loop in ata_eh_recover as + * long as the timeout for a park request to *one* device on + * the port has not expired, and since we still want to pick + * up park requests to other devices on the same port or + * timeout updates for the same device, we have to pull + * ATA_EH_PARK actions from eh_info into eh_context.i + * ourselves at the beginning of each pass over the loop. + * + * Additionally, all write accesses to &ap->park_req_pending + * through INIT_COMPLETION() (see below) or complete_all() + * (see ata_scsi_park_store()) are protected by the host lock. + * As a result we have that park_req_pending.done is zero on + * exit from this function, i.e. when ATA_EH_PARK actions for + * *all* devices on port ap have been pulled into the + * respective eh_context structs. If, and only if, + * park_req_pending.done is non-zero by the time we reach + * wait_for_completion_timeout(), another ATA_EH_PARK action + * has been scheduled for at least one of the devices on port + * ap and we have to cycle over the do {} while () loop in + * ata_eh_recover() again. + */ + + spin_lock_irqsave(ap->lock, flags); + INIT_COMPLETION(ap->park_req_pending); + ata_port_for_each_link(link, ap) { + ata_link_for_each_dev(dev, link) { + struct ata_eh_info *ehi = &link->eh_info; + + link->eh_context.i.dev_action[dev->devno] |= + ehi->dev_action[dev->devno] & ATA_EH_PARK; + ata_eh_clear_action(link, dev, ehi, ATA_EH_PARK); + } + } + spin_unlock_irqrestore(ap->lock, flags); +} + +static void ata_eh_park_issue_cmd(struct ata_device *dev, int park) +{ + struct ata_eh_context *ehc = &dev->link->eh_context; + struct ata_taskfile tf; + unsigned int err_mask; + + ata_tf_init(dev, &tf); + if (park) { + ehc->unloaded_mask |= 1 << dev->devno; + tf.command = ATA_CMD_IDLEIMMEDIATE; + tf.feature = 0x44; + tf.lbal = 0x4c; + tf.lbam = 0x4e; + tf.lbah = 0x55; + } else { + ehc->unloaded_mask &= ~(1 << dev->devno); + tf.command = ATA_CMD_CHK_POWER; + } + + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + if (park && (err_mask || tf.lbal != 0xc4)) { + ata_dev_printk(dev, KERN_ERR, "head unload failed!\n"); + ehc->unloaded_mask &= ~(1 << dev->devno); + } +} + static int ata_eh_revalidate_and_attach(struct ata_link *link, struct ata_device **r_failed_dev) { @@ -2845,7 +2919,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, struct ata_device *dev; int nr_failed_devs; int rc; - unsigned long flags; + unsigned long flags, deadline; DPRINTK("ENTER\n"); @@ -2919,6 +2993,56 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, } } + do { + unsigned long now; + + /* + * clears ATA_EH_PARK in eh_info and resets + * ap->park_req_pending + */ + ata_eh_pull_park_action(ap); + + deadline = jiffies; + ata_port_for_each_link(link, ap) { + ata_link_for_each_dev(dev, link) { + struct ata_eh_context *ehc = &link->eh_context; + unsigned long tmp; + + if (dev->class != ATA_DEV_ATA) + continue; + if (!(ehc->i.dev_action[dev->devno] & + ATA_EH_PARK)) + continue; + tmp = dev->unpark_deadline; + if (time_before(deadline, tmp)) + deadline = tmp; + else if (time_before_eq(tmp, jiffies)) + continue; + if (ehc->unloaded_mask & (1 << dev->devno)) + continue; + + ata_eh_park_issue_cmd(dev, 1); + } + } + + now = jiffies; + if (time_before_eq(deadline, now)) + break; + + deadline = wait_for_completion_timeout(&ap->park_req_pending, + deadline - now); + } while (deadline); + ata_port_for_each_link(link, ap) { + ata_link_for_each_dev(dev, link) { + if (!(link->eh_context.unloaded_mask & + (1 << dev->devno))) + continue; + + ata_eh_park_issue_cmd(dev, 0); + ata_eh_done(link, dev, ATA_EH_PARK); + } + } + /* the rest */ ata_port_for_each_link(link, ap) { struct ata_eh_context *ehc = &link->eh_context; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index b9d3ba423cb2..fccd5e496c62 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -183,6 +183,105 @@ DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR, ata_scsi_lpm_show, ata_scsi_lpm_put); EXPORT_SYMBOL_GPL(dev_attr_link_power_management_policy); +static ssize_t ata_scsi_park_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct ata_port *ap; + struct ata_link *link; + struct ata_device *dev; + unsigned long flags; + unsigned int uninitialized_var(msecs); + int rc = 0; + + ap = ata_shost_to_port(sdev->host); + + spin_lock_irqsave(ap->lock, flags); + dev = ata_scsi_find_dev(ap, sdev); + if (!dev) { + rc = -ENODEV; + goto unlock; + } + if (dev->flags & ATA_DFLAG_NO_UNLOAD) { + rc = -EOPNOTSUPP; + goto unlock; + } + + link = dev->link; + if (ap->pflags & ATA_PFLAG_EH_IN_PROGRESS && + link->eh_context.unloaded_mask & (1 << dev->devno) && + time_after(dev->unpark_deadline, jiffies)) + msecs = jiffies_to_msecs(dev->unpark_deadline - jiffies); + else + msecs = 0; + +unlock: + spin_unlock_irq(ap->lock); + + return rc ? rc : snprintf(buf, 20, "%u\n", msecs); +} + +static ssize_t ata_scsi_park_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct ata_port *ap; + struct ata_device *dev; + long int input; + unsigned long flags; + int rc; + + rc = strict_strtol(buf, 10, &input); + if (rc || input < -2) + return -EINVAL; + if (input > ATA_TMOUT_MAX_PARK) { + rc = -EOVERFLOW; + input = ATA_TMOUT_MAX_PARK; + } + + ap = ata_shost_to_port(sdev->host); + + spin_lock_irqsave(ap->lock, flags); + dev = ata_scsi_find_dev(ap, sdev); + if (unlikely(!dev)) { + rc = -ENODEV; + goto unlock; + } + if (dev->class != ATA_DEV_ATA) { + rc = -EOPNOTSUPP; + goto unlock; + } + + if (input >= 0) { + if (dev->flags & ATA_DFLAG_NO_UNLOAD) { + rc = -EOPNOTSUPP; + goto unlock; + } + + dev->unpark_deadline = ata_deadline(jiffies, input); + dev->link->eh_info.dev_action[dev->devno] |= ATA_EH_PARK; + ata_port_schedule_eh(ap); + complete(&ap->park_req_pending); + } else { + switch (input) { + case -1: + dev->flags &= ~ATA_DFLAG_NO_UNLOAD; + break; + case -2: + dev->flags |= ATA_DFLAG_NO_UNLOAD; + break; + } + } +unlock: + spin_unlock_irqrestore(ap->lock, flags); + + return rc ? rc : len; +} +DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, + ata_scsi_park_show, ata_scsi_park_store); +EXPORT_SYMBOL_GPL(dev_attr_unload_heads); + static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) { cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; @@ -269,6 +368,12 @@ DEVICE_ATTR(sw_activity, S_IWUGO | S_IRUGO, ata_scsi_activity_show, ata_scsi_activity_store); EXPORT_SYMBOL_GPL(dev_attr_sw_activity); +struct device_attribute *ata_common_sdev_attrs[] = { + &dev_attr_unload_heads, + NULL +}; +EXPORT_SYMBOL_GPL(ata_common_sdev_attrs); + static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) { @@ -954,6 +1059,9 @@ static int atapi_drain_needed(struct request *rq) static int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev) { + if (!ata_id_has_unload(dev->id)) + dev->flags |= ATA_DFLAG_NO_UNLOAD; + /* configure max sectors */ blk_queue_max_sectors(sdev->request_queue, dev->max_sectors); diff --git a/include/linux/libata.h b/include/linux/libata.h index 244ff601559a..1f44cfb847e1 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -146,6 +146,7 @@ enum { ATA_DFLAG_SPUNDOWN = (1 << 14), /* XXX: for spindown_compat */ ATA_DFLAG_SLEEPING = (1 << 15), /* device is sleeping */ ATA_DFLAG_DUBIOUS_XFER = (1 << 16), /* data transfer not verified */ + ATA_DFLAG_NO_UNLOAD = (1 << 17), /* device doesn't support unload */ ATA_DFLAG_INIT_MASK = (1 << 24) - 1, ATA_DFLAG_DETACH = (1 << 24), @@ -244,6 +245,7 @@ enum { ATA_TMOUT_BOOT = 30000, /* heuristic */ ATA_TMOUT_BOOT_QUICK = 7000, /* heuristic */ ATA_TMOUT_INTERNAL_QUICK = 5000, + ATA_TMOUT_MAX_PARK = 30000, /* FIXME: GoVault needs 2s but we can't afford that without * parallel probing. 800ms is enough for iVDR disk @@ -319,8 +321,9 @@ enum { ATA_EH_RESET = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, ATA_EH_ENABLE_LINK = (1 << 3), ATA_EH_LPM = (1 << 4), /* link power management action */ + ATA_EH_PARK = (1 << 5), /* unload heads and stop I/O */ - ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE, + ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE | ATA_EH_PARK, ATA_EH_ALL_ACTIONS = ATA_EH_REVALIDATE | ATA_EH_RESET | ATA_EH_ENABLE_LINK | ATA_EH_LPM, @@ -454,6 +457,7 @@ enum link_pm { MEDIUM_POWER, }; extern struct device_attribute dev_attr_link_power_management_policy; +extern struct device_attribute dev_attr_unload_heads; extern struct device_attribute dev_attr_em_message_type; extern struct device_attribute dev_attr_em_message; extern struct device_attribute dev_attr_sw_activity; @@ -566,6 +570,7 @@ struct ata_device { /* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */ u64 n_sectors; /* size of device, if ATA */ unsigned int class; /* ATA_DEV_xxx */ + unsigned long unpark_deadline; u8 pio_mode; u8 dma_mode; @@ -623,6 +628,7 @@ struct ata_eh_context { [ATA_EH_CMD_TIMEOUT_TABLE_SIZE]; unsigned int classes[ATA_MAX_DEVICES]; unsigned int did_probe_mask; + unsigned int unloaded_mask; unsigned int saved_ncq_enabled; u8 saved_xfer_mode[ATA_MAX_DEVICES]; /* timestamp for the last reset attempt or success */ @@ -712,6 +718,7 @@ struct ata_port { struct list_head eh_done_q; wait_queue_head_t eh_wait_q; int eh_tries; + struct completion park_req_pending; pm_message_t pm_mesg; int *pm_result; @@ -1102,6 +1109,7 @@ extern void ata_std_error_handler(struct ata_port *ap); */ extern const struct ata_port_operations ata_base_port_ops; extern const struct ata_port_operations sata_port_ops; +extern struct device_attribute *ata_common_sdev_attrs[]; #define ATA_BASE_SHT(drv_name) \ .module = THIS_MODULE, \ @@ -1116,7 +1124,8 @@ extern const struct ata_port_operations sata_port_ops; .proc_name = drv_name, \ .slave_configure = ata_scsi_slave_config, \ .slave_destroy = ata_scsi_slave_destroy, \ - .bios_param = ata_std_bios_param + .bios_param = ata_std_bios_param, \ + .sdev_attrs = ata_common_sdev_attrs #define ATA_NCQ_SHT(drv_name) \ ATA_BASE_SHT(drv_name), \ -- cgit v1.2.3 From 6866e7bc83f13a1bc6de59099930e9db1ab0042f Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Mon, 22 Sep 2008 14:47:13 -0700 Subject: libata: reorder ata_device to remove 8 bytes of padding on 64 bits reduce size by 8 bytes from 1160 to 1152 allowing it to fit in 1 fewer cachelines. Signed-off-by: Richard Kennedy Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- include/linux/libata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/libata.h b/include/linux/libata.h index 1f44cfb847e1..947cf84e555d 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -560,8 +560,8 @@ struct ata_ering { struct ata_device { struct ata_link *link; unsigned int devno; /* 0 or 1 */ - unsigned long flags; /* ATA_DFLAG_xxx */ unsigned int horkage; /* List of broken features */ + unsigned long flags; /* ATA_DFLAG_xxx */ struct scsi_device *sdev; /* attached SCSI device */ #ifdef CONFIG_ATA_ACPI acpi_handle acpi_handle; -- cgit v1.2.3 From b00c1a99e7758f794923c61e5cd55268d61c9469 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 29 Sep 2008 15:44:46 +0200 Subject: hrtimer: mark migration state Impact: during migration active hrtimers can be seen as inactive The migration code removes the hrtimers from the queues of the dead CPU and sets the state temporary to INACTIVE. The enqueue code sets it to ACTIVE/PENDING again. Prevent that the wrong state can be seen by using a separate migration state bit. Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 4 +++- kernel/hrtimer.c | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 6d93dce61cbb..bdd88df1b4e5 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -67,9 +67,10 @@ enum hrtimer_cb_mode { * 0x02 callback function running * 0x04 callback pending (high resolution mode) * - * Special case: + * Special cases: * 0x03 callback function running and enqueued * (was requeued on another CPU) + * 0x09 timer was migrated on CPU hotunplug * The "callback function running and enqueued" status is only possible on * SMP. It happens for example when a posix timer expired and the callback * queued a signal. Between dropping the lock which protects the posix timer @@ -87,6 +88,7 @@ enum hrtimer_cb_mode { #define HRTIMER_STATE_ENQUEUED 0x01 #define HRTIMER_STATE_CALLBACK 0x02 #define HRTIMER_STATE_PENDING 0x04 +#define HRTIMER_STATE_MIGRATE 0x08 /** * struct hrtimer - the basic hrtimer structure diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index ac2f6d6d4868..ace723dd1e52 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1602,7 +1602,13 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base, timer = rb_entry(node, struct hrtimer, node); BUG_ON(hrtimer_callback_running(timer)); debug_hrtimer_deactivate(timer); - __remove_hrtimer(timer, old_base, HRTIMER_STATE_INACTIVE, 0); + + /* + * Mark it as STATE_MIGRATE not INACTIVE otherwise the + * timer could be seen as !active and just vanish away + * under us on another CPU + */ + __remove_hrtimer(timer, old_base, HRTIMER_STATE_MIGRATE, 0); timer->base = new_base; /* * Enqueue the timer. Allow reprogramming of the event device @@ -1620,13 +1626,15 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base, * state, we need to do that otherwise we end up with * a stale timer. */ - if (timer->state == HRTIMER_STATE_INACTIVE) { + if (timer->state == HRTIMER_STATE_MIGRATE) { timer->state = HRTIMER_STATE_PENDING; list_add_tail(&timer->cb_entry, &new_base->cpu_base->cb_pending); raise = 1; } #endif + /* Clear the migration state bit */ + timer->state &= ~HRTIMER_STATE_MIGRATE; } return raise; } -- cgit v1.2.3 From ccc7dadf736639da86f3e0c86832c11a66fc8221 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 29 Sep 2008 15:47:42 +0200 Subject: hrtimer: prevent migration of per CPU hrtimers Impact: per CPU hrtimers can be migrated from a dead CPU The hrtimer code has no knowledge about per CPU timers, but we need to prevent the migration of such timers and warn when such a timer is active at migration time. Explicitely mark the timers as per CPU and use a more understandable mode descriptor for the interrupts safe unlocked callback mode, which is used by hrtimer_sleeper and the scheduler code. Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 14 +++++++++++--- kernel/hrtimer.c | 37 +++++++++++++++++++++++++------------ kernel/sched.c | 4 ++-- kernel/time/tick-sched.c | 2 +- kernel/trace/trace_sysprof.c | 2 +- 5 files changed, 40 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index bdd88df1b4e5..2f245fe63bda 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -47,14 +47,22 @@ enum hrtimer_restart { * HRTIMER_CB_IRQSAFE: Callback may run in hardirq context * HRTIMER_CB_IRQSAFE_NO_RESTART: Callback may run in hardirq context and * does not restart the timer - * HRTIMER_CB_IRQSAFE_NO_SOFTIRQ: Callback must run in hardirq context - * Special mode for tick emultation + * HRTIMER_CB_IRQSAFE_PERCPU: Callback must run in hardirq context + * Special mode for tick emulation and + * scheduler timer. Such timers are per + * cpu and not allowed to be migrated on + * cpu unplug. + * HRTIMER_CB_IRQSAFE_UNLOCKED: Callback should run in hardirq context + * with timer->base lock unlocked + * used for timers which call wakeup to + * avoid lock order problems with rq->lock */ enum hrtimer_cb_mode { HRTIMER_CB_SOFTIRQ, HRTIMER_CB_IRQSAFE, HRTIMER_CB_IRQSAFE_NO_RESTART, - HRTIMER_CB_IRQSAFE_NO_SOFTIRQ, + HRTIMER_CB_IRQSAFE_PERCPU, + HRTIMER_CB_IRQSAFE_UNLOCKED, }; /* diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index ace723dd1e52..cdec83e722fa 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -672,13 +672,14 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, */ BUG_ON(timer->function(timer) != HRTIMER_NORESTART); return 1; - case HRTIMER_CB_IRQSAFE_NO_SOFTIRQ: + case HRTIMER_CB_IRQSAFE_PERCPU: + case HRTIMER_CB_IRQSAFE_UNLOCKED: /* * This is solely for the sched tick emulation with * dynamic tick support to ensure that we do not * restart the tick right on the edge and end up with * the tick timer in the softirq ! The calling site - * takes care of this. + * takes care of this. Also used for hrtimer sleeper ! */ debug_hrtimer_deactivate(timer); return 1; @@ -1245,7 +1246,8 @@ static void __run_hrtimer(struct hrtimer *timer) timer_stats_account_hrtimer(timer); fn = timer->function; - if (timer->cb_mode == HRTIMER_CB_IRQSAFE_NO_SOFTIRQ) { + if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU || + timer->cb_mode == HRTIMER_CB_IRQSAFE_UNLOCKED) { /* * Used for scheduler timers, avoid lock inversion with * rq->lock and tasklist_lock. @@ -1452,7 +1454,7 @@ void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) sl->timer.function = hrtimer_wakeup; sl->task = task; #ifdef CONFIG_HIGH_RES_TIMERS - sl->timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ; + sl->timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED; #endif } @@ -1592,7 +1594,7 @@ static void __cpuinit init_hrtimers_cpu(int cpu) #ifdef CONFIG_HOTPLUG_CPU static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base, - struct hrtimer_clock_base *new_base) + struct hrtimer_clock_base *new_base, int dcpu) { struct hrtimer *timer; struct rb_node *node; @@ -1603,6 +1605,18 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base, BUG_ON(hrtimer_callback_running(timer)); debug_hrtimer_deactivate(timer); + /* + * Should not happen. Per CPU timers should be + * canceled _before_ the migration code is called + */ + if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU) { + __remove_hrtimer(timer, old_base, + HRTIMER_STATE_INACTIVE, 0); + WARN(1, "hrtimer (%p %p)active but cpu %d dead\n", + timer, timer->function, dcpu); + continue; + } + /* * Mark it as STATE_MIGRATE not INACTIVE otherwise the * timer could be seen as !active and just vanish away @@ -1619,12 +1633,11 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base, /* * Happens with high res enabled when the timer was * already expired and the callback mode is - * HRTIMER_CB_IRQSAFE_NO_SOFTIRQ - * (hrtimer_sleeper). The enqueue code does not move - * them to the soft irq pending list for - * performance/latency reasons, but in the migration - * state, we need to do that otherwise we end up with - * a stale timer. + * HRTIMER_CB_IRQSAFE_UNLOCKED (hrtimer_sleeper). The + * enqueue code does not move them to the soft irq + * pending list for performance/latency reasons, but + * in the migration state, we need to do that + * otherwise we end up with a stale timer. */ if (timer->state == HRTIMER_STATE_MIGRATE) { timer->state = HRTIMER_STATE_PENDING; @@ -1682,7 +1695,7 @@ static void migrate_hrtimers(int cpu) for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { if (migrate_hrtimer_list(&old_base->clock_base[i], - &new_base->clock_base[i])) + &new_base->clock_base[i], cpu)) raise = 1; } diff --git a/kernel/sched.c b/kernel/sched.c index 13dd2db9fb2d..ad1962dc0aa2 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -201,7 +201,7 @@ void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime) hrtimer_init(&rt_b->rt_period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); rt_b->rt_period_timer.function = sched_rt_period_timer; - rt_b->rt_period_timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ; + rt_b->rt_period_timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED; } static void start_rt_bandwidth(struct rt_bandwidth *rt_b) @@ -1119,7 +1119,7 @@ static void init_rq_hrtick(struct rq *rq) hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); rq->hrtick_timer.function = hrtick; - rq->hrtick_timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ; + rq->hrtick_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU; } #else static inline void hrtick_clear(struct rq *rq) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 39019b3f7621..cb02324bdb88 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -625,7 +625,7 @@ void tick_setup_sched_timer(void) */ hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ts->sched_timer.function = tick_sched_timer; - ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ; + ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU; /* Get the next period (per cpu) */ ts->sched_timer.expires = tick_init_jiffy_update(); diff --git a/kernel/trace/trace_sysprof.c b/kernel/trace/trace_sysprof.c index bb948e52ce20..db58fb66a135 100644 --- a/kernel/trace/trace_sysprof.c +++ b/kernel/trace/trace_sysprof.c @@ -202,7 +202,7 @@ static void start_stack_timer(int cpu) hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer->function = stack_trace_timer_fn; - hrtimer->cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ; + hrtimer->cb_mode = HRTIMER_CB_IRQSAFE_PERCPU; hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL); } -- cgit v1.2.3 From e851db5b05408b89b9a9429a66814b79fabee2a1 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 30 Jun 2008 18:45:30 -0400 Subject: SUNRPC: Add address family field to svc_serv data structure Introduce and initialize an address family field in the svc_serv structure. This field will determine what family to use for the service's listener sockets and what families are advertised via the local rpcbind daemon. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 2 +- fs/nfs/callback.c | 3 ++- fs/nfsd/nfssvc.c | 1 + include/linux/sunrpc/svc.h | 9 +++++---- net/sunrpc/svc.c | 11 ++++++----- 5 files changed, 15 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 5bd9bf0fa9df..1553fecc567d 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -266,7 +266,7 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ "lockd_up: no pid, %d users??\n", nlmsvc_users); error = -ENOMEM; - serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL); + serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, AF_INET, NULL); if (!serv) { printk(KERN_WARNING "lockd_up: create service failed\n"); goto out; diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index f447f4b4476c..6a09760c5960 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -105,7 +105,8 @@ int nfs_callback_up(void) mutex_lock(&nfs_callback_mutex); if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) goto out; - serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); + serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, + AF_INET, NULL); ret = -ENOMEM; if (!serv) goto out_err; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 80292ff5e924..7f3d76a7839d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -229,6 +229,7 @@ int nfsd_create_serv(void) atomic_set(&nfsd_busy, 0); nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, + AF_INET, nfsd_last_thread, nfsd, THIS_MODULE); if (nfsd_serv == NULL) err = -ENOMEM; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index dc69068d94c7..23143f38b121 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -66,6 +66,7 @@ struct svc_serv { struct list_head sv_tempsocks; /* all temporary sockets */ int sv_tmpcnt; /* count of temporary sockets */ struct timer_list sv_temptimer; /* timer for aging temporary sockets */ + sa_family_t sv_family; /* listener's address family */ char * sv_name; /* service name */ @@ -381,14 +382,14 @@ struct svc_procedure { /* * Function prototypes. */ -struct svc_serv * svc_create(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv*)); +struct svc_serv *svc_create(struct svc_program *, unsigned int, sa_family_t, + void (*shutdown)(struct svc_serv *)); struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv*), svc_thread_fn, - struct module *); + sa_family_t, void (*shutdown)(struct svc_serv *), + svc_thread_fn, struct module *); int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); void svc_destroy(struct svc_serv *); int svc_process(struct svc_rqst *); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 5a32cb7c4bb4..9ba17044109d 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -357,7 +357,7 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu) */ static struct svc_serv * __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, - void (*shutdown)(struct svc_serv *serv)) + sa_family_t family, void (*shutdown)(struct svc_serv *serv)) { struct svc_serv *serv; unsigned int vers; @@ -366,6 +366,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL))) return NULL; + serv->sv_family = family; serv->sv_name = prog->pg_name; serv->sv_program = prog; serv->sv_nrthreads = 1; @@ -425,21 +426,21 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, struct svc_serv * svc_create(struct svc_program *prog, unsigned int bufsize, - void (*shutdown)(struct svc_serv *serv)) + sa_family_t family, void (*shutdown)(struct svc_serv *serv)) { - return __svc_create(prog, bufsize, /*npools*/1, shutdown); + return __svc_create(prog, bufsize, /*npools*/1, family, shutdown); } EXPORT_SYMBOL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, - void (*shutdown)(struct svc_serv *serv), + sa_family_t family, void (*shutdown)(struct svc_serv *serv), svc_thread_fn func, struct module *mod) { struct svc_serv *serv; unsigned int npools = svc_pool_map_get(); - serv = __svc_create(prog, bufsize, npools, shutdown); + serv = __svc_create(prog, bufsize, npools, family, shutdown); if (serv != NULL) { serv->sv_function = func; -- cgit v1.2.3 From 04716e6621ff4abb422d64ba7b48718f52716a3e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 7 Aug 2008 13:00:20 -0400 Subject: nfsd: permit unauthenticated stat of export root RFC 2623 section 2.3.2 permits the server to bypass gss authentication checks for certain operations that a client may perform when mounting. In the case of a client that doesn't have some form of credentials available to it on boot, this allows it to perform the mount unattended. (Presumably real file access won't be needed until a user with credentials logs in.) Being slightly more lenient allows lots of old clients to access krb5-only exports, with the only loss being a small amount of information leaked about the root directory of the export. This affects only v2 and v3; v4 still requires authentication for all access. Thanks to Peter Staubach testing against a Solaris client, which suggesting addition of v3 getattr, to the list, and to Trond for noting that doing so exposes no additional information. Signed-off-by: J. Bruce Fields Cc: Peter Staubach Cc: Trond Myklebust --- fs/nfsd/nfs3proc.c | 8 +++++--- fs/nfsd/nfsfh.c | 30 ++++++++++++++++++++---------- fs/nfsd/nfsproc.c | 6 ++++-- fs/nfsd/vfs.c | 4 ++-- include/linux/nfsd/nfsd.h | 3 ++- 5 files changed, 33 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 4d617ea28cfc..9dbd2eb91281 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -63,7 +63,8 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); if (nfserr) RETURN_STATUS(nfserr); @@ -530,7 +531,7 @@ nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: FSSTAT(3) %s\n", SVCFH_fmt(&argp->fh)); - nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); + nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0); fh_put(&argp->fh); RETURN_STATUS(nfserr); } @@ -558,7 +559,8 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, resp->f_maxfilesize = ~(u32) 0; resp->f_properties = NFS3_FSF_DEFAULT; - nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); + nfserr = fh_verify(rqstp, &argp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); /* Check special features of the file system. May request * different read/write sizes for file systems known to have diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index ea37c96f0445..cd25d91895a1 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -302,17 +302,27 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (error) goto out; - if (!(access & NFSD_MAY_LOCK)) { - /* - * pseudoflavor restrictions are not enforced on NLM, - * which clients virtually always use auth_sys for, - * even while using RPCSEC_GSS for NFS. - */ - error = check_nfsd_access(exp, rqstp); - if (error) - goto out; - } + /* + * pseudoflavor restrictions are not enforced on NLM, + * which clients virtually always use auth_sys for, + * even while using RPCSEC_GSS for NFS. + */ + if (access & NFSD_MAY_LOCK) + goto skip_pseudoflavor_check; + /* + * Clients may expect to be able to use auth_sys during mount, + * even if they use gss for everything else; see section 2.3.2 + * of rfc 2623. + */ + if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT + && exp->ex_path.dentry == dentry) + goto skip_pseudoflavor_check; + + error = check_nfsd_access(exp, rqstp); + if (error) + goto out; +skip_pseudoflavor_check: /* Finally, check access permissions. */ error = nfsd_permission(rqstp, exp, dentry, access); diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 0766f95d236a..5cffeca7acef 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -65,7 +65,8 @@ nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); return nfsd_return_attrs(nfserr, resp); } @@ -521,7 +522,8 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: STATFS %s\n", SVCFH_fmt(&argp->fh)); - nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); + nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, + NFSD_MAY_BYPASS_GSS_ON_ROOT); fh_put(&argp->fh); return nfserr; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 18060bed5267..1319e8027d55 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1866,9 +1866,9 @@ out: * N.B. After this call fhp needs an fh_put */ __be32 -nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) +nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, int access) { - __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); + __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access); if (!err && vfs_statfs(fhp->fh_dentry,stat)) err = nfserr_io; return err; diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 108f47e5fd95..21269405ffe2 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -38,6 +38,7 @@ #define NFSD_MAY_LOCK 32 #define NFSD_MAY_OWNER_OVERRIDE 64 #define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ +#define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) @@ -125,7 +126,7 @@ int nfsd_truncate(struct svc_rqst *, struct svc_fh *, __be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *, loff_t *, struct readdir_cd *, filldir_t); __be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *, - struct kstatfs *); + struct kstatfs *, int access); int nfsd_notify_change(struct inode *, struct iattr *); __be32 nfsd_permission(struct svc_rqst *, struct svc_export *, -- cgit v1.2.3 From bfcd17a6c5529bc37234cfa720a047cf9397bcfc Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 6 Aug 2008 15:12:22 +0200 Subject: Configure out file locking features This patch adds the CONFIG_FILE_LOCKING option which allows to remove support for advisory locks. With this patch enabled, the flock() system call, the F_GETLK, F_SETLK and F_SETLKW operations of fcntl() and NFS support are disabled. These features are not necessarly needed on embedded systems. It allows to save ~11 Kb of kernel code and data: text data bss dec hex filename 1125436 118764 212992 1457192 163c28 vmlinux.old 1114299 118564 212992 1445855 160fdf vmlinux -11137 -200 0 -11337 -2C49 +/- This patch has originally been written by Matt Mackall , and is part of the Linux Tiny project. Signed-off-by: Thomas Petazzoni Signed-off-by: Matt Mackall Cc: matthew@wil.cx Cc: linux-fsdevel@vger.kernel.org Cc: mpm@selenic.com Cc: akpm@linux-foundation.org Signed-off-by: J. Bruce Fields --- fs/Kconfig | 8 ++++++++ fs/Makefile | 3 ++- fs/proc/proc_misc.c | 4 ++++ include/linux/fs.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++------- kernel/sys_ni.c | 1 + kernel/sysctl.c | 6 +++++- 6 files changed, 70 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/Kconfig b/fs/Kconfig index abccb5dab9a8..c6ae4d4842eb 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -419,6 +419,14 @@ config FS_POSIX_ACL bool default n +config FILE_LOCKING + bool "Enable POSIX file locking API" if EMBEDDED + default y + help + This option enables standard file locking support, required + for filesystems like NFS and for the flock() system + call. Disabling this option saves about 11k. + source "fs/xfs/Kconfig" source "fs/gfs2/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index a1482a5eff15..4b86d433baaf 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -7,7 +7,7 @@ obj-y := open.o read_write.o file_table.o super.o \ char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ - ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ + ioctl.o readdir.o select.o fifo.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o drop_caches.o splice.o sync.o utimes.o \ @@ -27,6 +27,7 @@ obj-$(CONFIG_ANON_INODES) += anon_inodes.o obj-$(CONFIG_SIGNALFD) += signalfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o +obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o nfsd-$(CONFIG_NFSD) := nfsctl.o diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 29e20c6b1f7f..1aabbe2592e1 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -684,6 +684,7 @@ static int cmdline_read_proc(char *page, char **start, off_t off, return proc_calc_metrics(page, start, off, count, eof, len); } +#ifdef CONFIG_FILE_LOCKING static int locks_open(struct inode *inode, struct file *filp) { return seq_open(filp, &locks_seq_operations); @@ -695,6 +696,7 @@ static const struct file_operations proc_locks_operations = { .llseek = seq_lseek, .release = seq_release, }; +#endif /* CONFIG_FILE_LOCKING */ static int execdomains_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -888,7 +890,9 @@ void __init proc_misc_init(void) #ifdef CONFIG_PRINTK proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations); #endif +#ifdef CONFIG_FILE_LOCKING proc_create("locks", 0, NULL, &proc_locks_operations); +#endif proc_create("devices", 0, NULL, &proc_devinfo_operations); proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations); #ifdef CONFIG_BLOCK diff --git a/include/linux/fs.h b/include/linux/fs.h index 580b513668fe..9f540165a078 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -983,6 +983,13 @@ struct file_lock { #include +extern void send_sigio(struct fown_struct *fown, int fd, int band); + +/* fs/sync.c */ +extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset, + loff_t endbyte, unsigned int flags); + +#ifdef CONFIG_FILE_LOCKING extern int fcntl_getlk(struct file *, struct flock __user *); extern int fcntl_setlk(unsigned int, struct file *, unsigned int, struct flock __user *); @@ -993,14 +1000,9 @@ extern int fcntl_setlk64(unsigned int, struct file *, unsigned int, struct flock64 __user *); #endif -extern void send_sigio(struct fown_struct *fown, int fd, int band); extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg); extern int fcntl_getlease(struct file *filp); -/* fs/sync.c */ -extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset, - loff_t endbyte, unsigned int flags); - /* fs/locks.c */ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); @@ -1023,6 +1025,37 @@ extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); extern struct seq_operations locks_seq_operations; +#else /* !CONFIG_FILE_LOCKING */ +#define fcntl_getlk(a, b) ({ -EINVAL; }) +#define fcntl_setlk(a, b, c, d) ({ -EACCES; }) +#if BITS_PER_LONG == 32 +#define fcntl_getlk64(a, b) ({ -EINVAL; }) +#define fcntl_setlk64(a, b, c, d) ({ -EACCES; }) +#endif +#define fcntl_setlease(a, b, c) ({ 0; }) +#define fcntl_getlease(a) ({ 0; }) +#define locks_init_lock(a) ({ }) +#define __locks_copy_lock(a, b) ({ }) +#define locks_copy_lock(a, b) ({ }) +#define locks_remove_posix(a, b) ({ }) +#define locks_remove_flock(a) ({ }) +#define posix_test_lock(a, b) ({ 0; }) +#define posix_lock_file(a, b, c) ({ -ENOLCK; }) +#define posix_lock_file_wait(a, b) ({ -ENOLCK; }) +#define posix_unblock_lock(a, b) (-ENOENT) +#define vfs_test_lock(a, b) ({ 0; }) +#define vfs_lock_file(a, b, c, d) (-ENOLCK) +#define vfs_cancel_lock(a, b) ({ 0; }) +#define flock_lock_file_wait(a, b) ({ -ENOLCK; }) +#define __break_lease(a, b) ({ 0; }) +#define lease_get_mtime(a, b) ({ }) +#define generic_setlease(a, b, c) ({ -EINVAL; }) +#define vfs_setlease(a, b, c) ({ -EINVAL; }) +#define lease_modify(a, b) ({ -EINVAL; }) +#define lock_may_read(a, b, c) ({ 1; }) +#define lock_may_write(a, b, c) ({ 1; }) +#endif /* !CONFIG_FILE_LOCKING */ + struct fasync_struct { int magic; @@ -1554,9 +1587,12 @@ extern int vfs_statfs(struct dentry *, struct kstatfs *); /* /sys/fs */ extern struct kobject *fs_kobj; +extern int rw_verify_area(int, struct file *, loff_t *, size_t); + #define FLOCK_VERIFY_READ 1 #define FLOCK_VERIFY_WRITE 2 +#ifdef CONFIG_FILE_LOCKING extern int locks_mandatory_locked(struct inode *); extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t); @@ -1587,8 +1623,6 @@ static inline int locks_verify_locked(struct inode *inode) return 0; } -extern int rw_verify_area(int, struct file *, loff_t *, size_t); - static inline int locks_verify_truncate(struct inode *inode, struct file *filp, loff_t size) @@ -1609,6 +1643,15 @@ static inline int break_lease(struct inode *inode, unsigned int mode) return __break_lease(inode, mode); return 0; } +#else /* !CONFIG_FILE_LOCKING */ +#define locks_mandatory_locked(a) ({ 0; }) +#define locks_mandatory_area(a, b, c, d, e) ({ 0; }) +#define __mandatory_lock(a) ({ 0; }) +#define mandatory_lock(a) ({ 0; }) +#define locks_verify_locked(a) ({ 0; }) +#define locks_verify_truncate(a, b, c) ({ 0; }) +#define break_lease(a, b) ({ 0; }) +#endif /* CONFIG_FILE_LOCKING */ /* fs/open.c */ diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 08d6e1bb99ac..503d8d4eb80a 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -125,6 +125,7 @@ cond_syscall(sys_vm86old); cond_syscall(sys_vm86); cond_syscall(compat_sys_ipc); cond_syscall(compat_sys_sysctl); +cond_syscall(sys_flock); /* arch-specific weak syscall entries */ cond_syscall(sys_pciconfig_read); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 50ec0886fa3d..4588b2cf2ecb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -97,7 +97,7 @@ static int sixty = 60; static int neg_one = -1; #endif -#ifdef CONFIG_MMU +#if defined(CONFIG_MMU) && defined(CONFIG_FILE_LOCKING) static int two = 2; #endif @@ -1261,6 +1261,7 @@ static struct ctl_table fs_table[] = { .extra1 = &minolduid, .extra2 = &maxolduid, }, +#ifdef CONFIG_FILE_LOCKING { .ctl_name = FS_LEASES, .procname = "leases-enable", @@ -1269,6 +1270,7 @@ static struct ctl_table fs_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, +#endif #ifdef CONFIG_DNOTIFY { .ctl_name = FS_DIR_NOTIFY, @@ -1280,6 +1282,7 @@ static struct ctl_table fs_table[] = { }, #endif #ifdef CONFIG_MMU +#ifdef CONFIG_FILE_LOCKING { .ctl_name = FS_LEASE_TIME, .procname = "lease-break-time", @@ -1291,6 +1294,7 @@ static struct ctl_table fs_table[] = { .extra1 = &zero, .extra2 = &two, }, +#endif { .procname = "aio-nr", .data = &aio_nr, -- cgit v1.2.3 From 14aeb2118d6e9fd9ee988324c740a00c80979093 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 18 Aug 2008 19:34:00 -0400 Subject: SUNRPC: Simplify rpcb_register() API Bruce suggested there's no need to expose the difference between an error sending the PMAP_SET request and an error reply from the portmapper to rpcb_register's callers. The user space equivalent of rpcb_register() is pmap_set(3), which returns a bool_t : either the PMAP set worked, or it didn't. Simple. So let's remove the "*okay" argument from rpcb_register() and rpcb_v4_register(), and simply return an error if any part of the call didn't work. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/clnt.h | 4 +-- net/sunrpc/rpcb_clnt.c | 65 +++++++++++++++++++-------------------------- net/sunrpc/svc.c | 8 ++---- 3 files changed, 32 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index e5bfe01ee305..8ac8e75243a7 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -124,10 +124,10 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); void rpc_shutdown_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); -int rpcb_register(u32, u32, int, unsigned short, int *); +int rpcb_register(u32, u32, int, unsigned short); int rpcb_v4_register(const u32 program, const u32 version, const struct sockaddr *address, - const char *netid, int *result); + const char *netid); int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int); void rpcb_getport_async(struct rpc_task *); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 24db2b4d12d3..cc7250d4659b 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -176,13 +176,12 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, } static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, - u32 version, struct rpc_message *msg, - int *result) + u32 version, struct rpc_message *msg) { struct rpc_clnt *rpcb_clnt; - int error = 0; + int result, error = 0; - *result = 0; + msg->rpc_resp = &result; rpcb_clnt = rpcb_create_local(addr, addrlen, version); if (!IS_ERR(rpcb_clnt)) { @@ -191,12 +190,19 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, } else error = PTR_ERR(rpcb_clnt); - if (error < 0) + if (error < 0) { printk(KERN_WARNING "RPC: failed to contact local rpcbind " "server (errno %d).\n", -error); - dprintk("RPC: registration status %d/%d\n", error, *result); + return error; + } + + if (!result) { + dprintk("RPC: registration failed\n"); + return -EACCES; + } - return error; + dprintk("RPC: registration succeeded\n"); + return 0; } /** @@ -205,7 +211,11 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * @vers: RPC version number to bind * @prot: transport protocol to register * @port: port value to register - * @okay: OUT: result code + * + * Returns zero if the registration request was dispatched successfully + * and the rpcbind daemon returned success. Otherwise, returns an errno + * value that reflects the nature of the error (request could not be + * dispatched, timed out, or rpcbind returned an error). * * RPC services invoke this function to advertise their contact * information via the system's rpcbind daemon. RPC services @@ -217,15 +227,6 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * all registered transports for [program, version] from the local * rpcbind database. * - * Returns zero if the registration request was dispatched - * successfully and a reply was received. The rpcbind daemon's - * boolean result code is stored in *okay. - * - * Returns an errno value and sets *result to zero if there was - * some problem that prevented the rpcbind request from being - * dispatched, or if the rpcbind daemon did not respond within - * the timeout. - * * This function uses rpcbind protocol version 2 to contact the * local rpcbind daemon. * @@ -236,7 +237,7 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6 * addresses). */ -int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) +int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port) { struct rpcbind_args map = { .r_prog = prog, @@ -246,7 +247,6 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) }; struct rpc_message msg = { .rpc_argp = &map, - .rpc_resp = okay, }; dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " @@ -259,7 +259,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, sizeof(rpcb_inaddr_loopback), - RPCBVERS_2, &msg, okay); + RPCBVERS_2, &msg); } /* @@ -290,7 +290,7 @@ static int rpcb_register_netid4(struct sockaddr_in *address_to_register, return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, sizeof(rpcb_inaddr_loopback), - RPCBVERS_4, msg, msg->rpc_resp); + RPCBVERS_4, msg); } /* @@ -321,7 +321,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback, sizeof(rpcb_in6addr_loopback), - RPCBVERS_4, msg, msg->rpc_resp); + RPCBVERS_4, msg); } /** @@ -330,7 +330,11 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, * @version: RPC version number of service to (un)register * @address: address family, IP address, and port to (un)register * @netid: netid of transport protocol to (un)register - * @result: result code from rpcbind RPC call + * + * Returns zero if the registration request was dispatched successfully + * and the rpcbind daemon returned success. Otherwise, returns an errno + * value that reflects the nature of the error (request could not be + * dispatched, timed out, or rpcbind returned an error). * * RPC services invoke this function to advertise their contact * information via the system's rpcbind daemon. RPC services @@ -342,15 +346,6 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, * to zero. Callers pass a netid of "" to unregister all * transport netids associated with [program, version, address]. * - * Returns zero if the registration request was dispatched - * successfully and a reply was received. The rpcbind daemon's - * result code is stored in *result. - * - * Returns an errno value and sets *result to zero if there was - * some problem that prevented the rpcbind request from being - * dispatched, or if the rpcbind daemon did not respond within - * the timeout. - * * This function uses rpcbind protocol version 4 to contact the * local rpcbind daemon. The local rpcbind daemon must support * version 4 of the rpcbind protocol in order for these functions @@ -372,8 +367,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, * advertises the service on all IPv4 and IPv6 addresses. */ int rpcb_v4_register(const u32 program, const u32 version, - const struct sockaddr *address, const char *netid, - int *result) + const struct sockaddr *address, const char *netid) { struct rpcbind_args map = { .r_prog = program, @@ -383,11 +377,8 @@ int rpcb_v4_register(const u32 program, const u32 version, }; struct rpc_message msg = { .rpc_argp = &map, - .rpc_resp = result, }; - *result = 0; - switch (address->sa_family) { case AF_INET: return rpcb_register_netid4((struct sockaddr_in *)address, diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 9ba17044109d..9805143d0660 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -730,7 +730,7 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) struct svc_program *progp; unsigned long flags; unsigned int i; - int error = 0, dummy; + int error = 0; if (!port) clear_thread_flag(TIF_SIGPENDING); @@ -751,13 +751,9 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) if (progp->pg_vers[i]->vs_hidden) continue; - error = rpcb_register(progp->pg_prog, i, proto, port, &dummy); + error = rpcb_register(progp->pg_prog, i, proto, port); if (error < 0) break; - if (port && !dummy) { - error = -EACCES; - break; - } } } -- cgit v1.2.3 From a26cfad6e0a308a2c68df1f1ef50aabd48b17e6d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 18 Aug 2008 19:34:16 -0400 Subject: SUNRPC: Support IPv6 when registering kernel RPC services In order to advertise NFS-related services on IPv6 interfaces via rpcbind, the kernel RPC server implementation must use rpcb_v4_register() instead of rpcb_register(). A new kernel build option allows distributions to use the legacy v2 call until they integrate an appropriate user-space rpcbind daemon that can support IPv6 RPC services. I tried adding some automatic logic to fall back if registering with a v4 protocol request failed, but there are too many corner cases. So I just made it a compile-time switch that distributions can throw when they've replaced portmapper with rpcbind. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/Kconfig | 22 +++++++++++ include/linux/sunrpc/svc.h | 4 +- net/sunrpc/svc.c | 95 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 113 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/Kconfig b/fs/Kconfig index c6ae4d4842eb..ed57a5a37250 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1773,6 +1773,28 @@ config SUNRPC_XPRT_RDMA If unsure, say N. +config SUNRPC_REGISTER_V4 + bool "Register local RPC services via rpcbind v4 (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL + default n + help + Sun added support for registering RPC services at an IPv6 + address by creating two new versions of the rpcbind protocol + (RFC 1833). + + This option enables support in the kernel RPC server for + registering kernel RPC services via version 4 of the rpcbind + protocol. If you enable this option, you must run a portmapper + daemon that supports rpcbind protocol version 4. + + Serving NFS over IPv6 from knfsd (the kernel's NFS server) + requires that you enable this option and use a portmapper that + supports rpcbind version 4. + + If unsure, say N to get traditional behavior (register kernel + RPC services using only rpcbind version 2). Distributions + using the legacy Linux portmapper daemon must say N here. + config RPCSEC_GSS_KRB5 tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" depends on SUNRPC && EXPERIMENTAL diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 23143f38b121..54a79e1ad634 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -393,7 +393,9 @@ struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); void svc_destroy(struct svc_serv *); int svc_process(struct svc_rqst *); -int svc_register(struct svc_serv *, int, unsigned short); +int svc_register(const struct svc_serv *, const unsigned short, + const unsigned short); + void svc_wake_up(struct svc_serv *); void svc_reserve(struct svc_rqst *rqstp, int space); struct svc_pool * svc_pool_for_cpu(struct svc_serv *serv, int cpu); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 9eb78a771da5..c43ccb628052 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -719,13 +719,92 @@ svc_exit_thread(struct svc_rqst *rqstp) } EXPORT_SYMBOL(svc_exit_thread); +#ifdef CONFIG_SUNRPC_REGISTER_V4 /* - * Register an RPC service with the local portmapper. - * To unregister a service, call this routine with - * proto and port == 0. + * Registering kernel RPC services with rpcbind version 2 will work + * over either IPv4 or IPv6, since the Linux kernel always registers + * services for the "any" address. + * + * However, the local rpcbind daemon listens on either only AF_INET + * or AF_INET6 (never both). When it listens on AF_INET6, an rpcbind + * version 2 registration will result in registering the service at + * IN6ADDR_ANY, even if the RPC service being registered is not + * IPv6-enabled. + * + * Rpcbind version 4 allows us to be a little more specific. Kernel + * RPC services that don't yet support AF_INET6 can register + * themselves as IPv4-only with the local rpcbind daemon, even if the + * daemon is listening only on AF_INET6. + * + * And, registering IPv6-enabled kernel RPC services via AF_INET6 + * verifies that the local user space rpcbind daemon is properly + * configured to support remote AF_INET6 rpcbind requests. + * + * An AF_INET6 registration request will fail if the local rpcbind + * daemon is not set up to listen on AF_INET6. Likewise, we fail + * AF_INET6 registration requests if svc_register() is configured to + * support only rpcbind version 2. */ -int -svc_register(struct svc_serv *serv, int proto, unsigned short port) +static int __svc_register(const u32 program, const u32 version, + const sa_family_t family, + const unsigned short protocol, + const unsigned short port) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_port = htons(port), + }; + struct sockaddr *sap; + char *netid; + + switch (family) { + case AF_INET: + sap = (struct sockaddr *)&sin; + netid = RPCBIND_NETID_TCP; + if (protocol == IPPROTO_UDP) + netid = RPCBIND_NETID_UDP; + break; + case AF_INET6: + sap = (struct sockaddr *)&sin6; + netid = RPCBIND_NETID_TCP6; + if (protocol == IPPROTO_UDP) + netid = RPCBIND_NETID_UDP6; + break; + default: + return -EAFNOSUPPORT; + } + + return rpcb_v4_register(program, version, sap, netid); +} +#else +static int __svc_register(const u32 program, const u32 version, + sa_family_t family, + const unsigned short protocol, + const unsigned short port) +{ + if (family != AF_INET) + return -EAFNOSUPPORT; + + return rpcb_register(program, version, protocol, port); +} +#endif + +/** + * svc_register - register an RPC service with the local portmapper + * @serv: svc_serv struct for the service to register + * @proto: transport protocol number to advertise + * @port: port to advertise + * + * Service is registered for any address in serv's address family + */ +int svc_register(const struct svc_serv *serv, const unsigned short proto, + const unsigned short port) { struct svc_program *progp; unsigned int i; @@ -738,8 +817,9 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) if (progp->pg_vers[i] == NULL) continue; - dprintk("svc: svc_register(%s, %s, %d, %d)%s\n", + dprintk("svc: svc_register(%s, %u, %s, %u, %d)%s\n", progp->pg_name, + serv->sv_family, proto == IPPROTO_UDP? "udp" : "tcp", port, i, @@ -749,7 +829,8 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) if (progp->pg_vers[i]->vs_hidden) continue; - error = rpcb_register(progp->pg_prog, i, proto, port); + error = __svc_register(progp->pg_prog, i, + serv->sv_family, proto, port); if (error < 0) break; } -- cgit v1.2.3 From 1b333c54a15a746ff6b04a684b0845a66daacef2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 27 Aug 2008 16:57:23 -0400 Subject: lockd: address-family independent printable addresses Knowing which source address is used for communicating with remote NLM services can be helpful for debugging configuration problems on hosts with multiple addresses. Keep the dprintk debugging here, but adapt it so it displays AF_INET6 addresses properly. There are also a couple of dprintk clean-ups as well. At some point we will aggregate the helpers that display presentation format addresses into a single set of shared helpers. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/host.c | 78 +++++++++++++++++++++++++++++++++++---------- include/linux/lockd/lockd.h | 4 +++ 2 files changed, 65 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/host.c b/fs/lockd/host.c index cb26e3d952a2..e5dcfa57e099 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include +#include #define NLMDBG_FACILITY NLMDBG_HOSTCACHE #define NLM_HOST_NRHASH 32 @@ -38,6 +40,32 @@ static struct nsm_handle * nsm_find(const struct sockaddr_in *sin, const char *hostname, unsigned int hostname_len); +static void nlm_display_address(const struct sockaddr *sap, + char *buf, const size_t len) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + + switch (sap->sa_family) { + case AF_UNSPEC: + snprintf(buf, len, "unspecified"); + break; + case AF_INET: + snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr)); + break; + case AF_INET6: + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) + snprintf(buf, len, NIPQUAD_FMT, + NIPQUAD(sin6->sin6_addr.s6_addr32[3])); + else + snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr)); + break; + default: + snprintf(buf, len, "unsupported address family"); + break; + } +} + /* * Common host lookup routine for server & client */ @@ -54,14 +82,10 @@ static struct nlm_host *nlm_lookup_host(int server, struct nsm_handle *nsm = NULL; int hash; - dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT - ", p=%d, v=%u, my role=%s, name=%.*s)\n", - NIPQUAD(ssin->sin_addr.s_addr), - NIPQUAD(sin->sin_addr.s_addr), proto, version, - server? "server" : "client", - hostname_len, - hostname? hostname : ""); - + dprintk("lockd: nlm_lookup_host(proto=%d, vers=%u," + " my role is %s, hostname=%.*s)\n", + proto, version, server ? "server" : "client", + hostname_len, hostname ? hostname : ""); hash = NLM_ADDRHASH(sin->sin_addr.s_addr); @@ -101,6 +125,8 @@ static struct nlm_host *nlm_lookup_host(int server, hlist_add_head(&host->h_hash, chain); nlm_get_host(host); + dprintk("lockd: nlm_lookup_host found host %s (%s)\n", + host->h_name, host->h_addrbuf); goto out; } @@ -113,13 +139,17 @@ static struct nlm_host *nlm_lookup_host(int server, else { host = NULL; nsm = nsm_find(sin, hostname, hostname_len); - if (!nsm) + if (!nsm) { + dprintk("lockd: nlm_lookup_host failed; " + "no nsm handle\n"); goto out; + } } host = kzalloc(sizeof(*host), GFP_KERNEL); if (!host) { nsm_release(nsm); + dprintk("lockd: nlm_lookup_host failed; no memory\n"); goto out; } host->h_name = nsm->sm_name; @@ -146,6 +176,15 @@ static struct nlm_host *nlm_lookup_host(int server, INIT_LIST_HEAD(&host->h_reclaim); nrhosts++; + + nlm_display_address((struct sockaddr *)&host->h_addr, + host->h_addrbuf, sizeof(host->h_addrbuf)); + nlm_display_address((struct sockaddr *)&host->h_saddr, + host->h_saddrbuf, sizeof(host->h_saddrbuf)); + + dprintk("lockd: nlm_lookup_host created host %s\n", + host->h_name); + out: mutex_unlock(&nlm_host_mutex); return host; @@ -210,9 +249,8 @@ nlm_bind_host(struct nlm_host *host) { struct rpc_clnt *clnt; - dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n", - NIPQUAD(host->h_saddr.sin_addr), - NIPQUAD(host->h_addr.sin_addr)); + dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n", + host->h_name, host->h_addrbuf, host->h_saddrbuf); /* Lock host handle */ mutex_lock(&host->h_mutex); @@ -224,7 +262,7 @@ nlm_bind_host(struct nlm_host *host) if (time_after_eq(jiffies, host->h_nextrebind)) { rpc_force_rebind(clnt); host->h_nextrebind = jiffies + NLM_HOST_REBIND; - dprintk("lockd: next rebind in %ld jiffies\n", + dprintk("lockd: next rebind in %lu jiffies\n", host->h_nextrebind - jiffies); } } else { @@ -327,12 +365,16 @@ void nlm_host_rebooted(const struct sockaddr_in *sin, struct nsm_handle *nsm; struct nlm_host *host; - dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n", - hostname, NIPQUAD(sin->sin_addr)); - /* Find the NSM handle for this peer */ - if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0))) + nsm = __nsm_find(sin, hostname, hostname_len, 0); + if (nsm == NULL) { + dprintk("lockd: never saw rebooted peer '%.*s' before\n", + hostname_len, hostname); return; + } + + dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n", + hostname_len, hostname, nsm->sm_addrbuf); /* When reclaiming locks on this peer, make sure that * we set up a new notification */ @@ -516,6 +558,8 @@ retry: nsm->sm_name = (char *) (nsm + 1); memcpy(nsm->sm_name, hostname, hostname_len); nsm->sm_name[hostname_len] = '\0'; + nlm_display_address((struct sockaddr *)&nsm->sm_addr, + nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf)); atomic_set(&nsm->sm_count, 1); goto retry; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index dbb87ab282e8..0691efbd0b34 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -61,6 +61,9 @@ struct nlm_host { struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ struct nsm_handle * h_nsmhandle; /* NSM status handle */ + + char h_addrbuf[48], /* address eyecatchers */ + h_saddrbuf[48]; }; struct nsm_handle { @@ -70,6 +73,7 @@ struct nsm_handle { struct sockaddr_in sm_addr; unsigned int sm_monitored : 1, sm_sticky : 1; /* don't unmonitor */ + char sm_addrbuf[48]; /* address eyecatcher */ }; /* -- cgit v1.2.3 From 5344b12d4f97d4a9a62d806425977a6ff64b6baf Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 27 Aug 2008 16:57:46 -0400 Subject: SUNRPC: Make svc_addr's argument a constant Clean up: Add extra type safety and squelch a few compiler complaints in upcoming patches. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 54a79e1ad634..3afe7fb403b2 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -266,17 +266,17 @@ struct svc_rqst { /* * Rigorous type checking on sockaddr type conversions */ -static inline struct sockaddr_in *svc_addr_in(struct svc_rqst *rqst) +static inline struct sockaddr_in *svc_addr_in(const struct svc_rqst *rqst) { return (struct sockaddr_in *) &rqst->rq_addr; } -static inline struct sockaddr_in6 *svc_addr_in6(struct svc_rqst *rqst) +static inline struct sockaddr_in6 *svc_addr_in6(const struct svc_rqst *rqst) { return (struct sockaddr_in6 *) &rqst->rq_addr; } -static inline struct sockaddr *svc_addr(struct svc_rqst *rqst) +static inline struct sockaddr *svc_addr(const struct svc_rqst *rqst) { return (struct sockaddr *) &rqst->rq_addr; } -- cgit v1.2.3 From b4ed58fd34d4def88bda59f9cc566ec9fca6a096 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 3 Sep 2008 14:35:39 -0400 Subject: lockd: Use sockaddr_storage + length for h_addr field To store larger addresses in the nlm_host structure, make h_addr a sockaddr_storage, and add an address length field. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 2 +- fs/lockd/host.c | 11 ++++++----- include/linux/lockd/lockd.h | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 0b45fd3a4bfd..0df5587f804e 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -166,7 +166,7 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock */ if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) continue; - if (!nlm_cmp_addr(&block->b_host->h_addr, addr)) + if (!nlm_cmp_addr(nlm_addr_in(block->b_host), addr)) continue; if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0) continue; diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 008e4026f540..8c7022eeae65 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -116,7 +116,7 @@ static struct nlm_host *nlm_lookup_host(int server, */ chain = &nlm_hosts[hash]; hlist_for_each_entry(host, pos, chain, h_hash) { - if (!nlm_cmp_addr(&host->h_addr, sin)) + if (!nlm_cmp_addr(nlm_addr_in(host), sin)) continue; /* See if we have an NSM handle for this client */ @@ -165,8 +165,9 @@ static struct nlm_host *nlm_lookup_host(int server, goto out; } host->h_name = nsm->sm_name; - host->h_addr = *sin; - nlm_clear_port((struct sockaddr *)&host->h_addr); + memcpy(nlm_addr(host), sin, sizeof(*sin)); + host->h_addrlen = sizeof(*sin); + nlm_clear_port(nlm_addr(host)); host->h_saddr = *ssin; host->h_version = version; host->h_proto = proto; @@ -291,8 +292,8 @@ nlm_bind_host(struct nlm_host *host) }; struct rpc_create_args args = { .protocol = host->h_proto, - .address = (struct sockaddr *)&host->h_addr, - .addrsize = sizeof(host->h_addr), + .address = nlm_addr(host), + .addrsize = host->h_addrlen, .saddress = (struct sockaddr *)&host->h_saddr, .timeout = &timeparms, .servername = host->h_name, diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 0691efbd0b34..41d7a8e61cea 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -38,7 +38,8 @@ */ struct nlm_host { struct hlist_node h_hash; /* doubly linked list */ - struct sockaddr_in h_addr; /* peer address */ + struct sockaddr_storage h_addr; /* peer address */ + size_t h_addrlen; struct sockaddr_in h_saddr; /* our address (optional) */ struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ char * h_name; /* remote hostname */ @@ -76,6 +77,19 @@ struct nsm_handle { char sm_addrbuf[48]; /* address eyecatcher */ }; +/* + * Rigorous type checking on sockaddr type conversions + */ +static inline struct sockaddr_in *nlm_addr_in(const struct nlm_host *host) +{ + return (struct sockaddr_in *)&host->h_addr; +} + +static inline struct sockaddr *nlm_addr(const struct nlm_host *host) +{ + return (struct sockaddr *)&host->h_addr; +} + /* * Map an fl_owner_t into a unique 32-bit "pid" */ -- cgit v1.2.3 From 90151e6e4d00a3150d03d52170c246734b274622 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 3 Sep 2008 14:35:46 -0400 Subject: lockd: Use sockaddr_storage for h_saddr field To store larger addresses in the nlm_host structure, make h_saddr a sockaddr_storage. And let's call it something more self-explanatory: "saddr" could easily be mistaken for "server address". Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/host.c | 12 ++++++------ fs/lockd/svcsubs.c | 2 +- include/linux/lockd/lockd.h | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 8c7022eeae65..3ce2702d0368 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -129,7 +129,7 @@ static struct nlm_host *nlm_lookup_host(int server, continue; if (host->h_server != server) continue; - if (!nlm_cmp_addr(&host->h_saddr, ssin)) + if (!nlm_cmp_addr(nlm_srcaddr_in(host), ssin)) continue; /* Move to head of hash chain. */ @@ -168,7 +168,7 @@ static struct nlm_host *nlm_lookup_host(int server, memcpy(nlm_addr(host), sin, sizeof(*sin)); host->h_addrlen = sizeof(*sin); nlm_clear_port(nlm_addr(host)); - host->h_saddr = *ssin; + memcpy(nlm_srcaddr(host), ssin, sizeof(*ssin)); host->h_version = version; host->h_proto = proto; host->h_rpcclnt = NULL; @@ -192,8 +192,8 @@ static struct nlm_host *nlm_lookup_host(int server, nlm_display_address((struct sockaddr *)&host->h_addr, host->h_addrbuf, sizeof(host->h_addrbuf)); - nlm_display_address((struct sockaddr *)&host->h_saddr, - host->h_saddrbuf, sizeof(host->h_saddrbuf)); + nlm_display_address((struct sockaddr *)&host->h_srcaddr, + host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf)); dprintk("lockd: nlm_lookup_host created host %s\n", host->h_name); @@ -267,7 +267,7 @@ nlm_bind_host(struct nlm_host *host) struct rpc_clnt *clnt; dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n", - host->h_name, host->h_addrbuf, host->h_saddrbuf); + host->h_name, host->h_addrbuf, host->h_srcaddrbuf); /* Lock host handle */ mutex_lock(&host->h_mutex); @@ -294,7 +294,7 @@ nlm_bind_host(struct nlm_host *host) .protocol = host->h_proto, .address = nlm_addr(host), .addrsize = host->h_addrlen, - .saddress = (struct sockaddr *)&host->h_saddr, + .saddress = nlm_srcaddr(host), .timeout = &timeparms, .servername = host->h_name, .program = &nlm_program, diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 198b4e55b373..d3d1330d7c27 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -418,7 +418,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) { - return nlm_cmp_addr(&host->h_saddr, datap); + return nlm_cmp_addr(nlm_srcaddr_in(host), datap); } /** diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 41d7a8e61cea..964e6c93830f 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -40,7 +40,7 @@ struct nlm_host { struct hlist_node h_hash; /* doubly linked list */ struct sockaddr_storage h_addr; /* peer address */ size_t h_addrlen; - struct sockaddr_in h_saddr; /* our address (optional) */ + struct sockaddr_storage h_srcaddr; /* our address (optional) */ struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ char * h_name; /* remote hostname */ u32 h_version; /* interface version */ @@ -64,7 +64,7 @@ struct nlm_host { struct nsm_handle * h_nsmhandle; /* NSM status handle */ char h_addrbuf[48], /* address eyecatchers */ - h_saddrbuf[48]; + h_srcaddrbuf[48]; }; struct nsm_handle { @@ -90,6 +90,16 @@ static inline struct sockaddr *nlm_addr(const struct nlm_host *host) return (struct sockaddr *)&host->h_addr; } +static inline struct sockaddr_in *nlm_srcaddr_in(const struct nlm_host *host) +{ + return (struct sockaddr_in *)&host->h_srcaddr; +} + +static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host) +{ + return (struct sockaddr *)&host->h_srcaddr; +} + /* * Map an fl_owner_t into a unique 32-bit "pid" */ -- cgit v1.2.3 From 7e9d7746bfd40121438b155023793796499497d8 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 3 Sep 2008 14:35:54 -0400 Subject: NSM: Use sockaddr_storage for sm_addr field To store larger addresses in the nsm_handle structure, make sm_addr a sockaddr_storage. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/host.c | 5 +++-- fs/lockd/mon.c | 2 +- include/linux/lockd/lockd.h | 13 ++++++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 3ce2702d0368..510ebcf485f0 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -551,7 +551,7 @@ retry: if (strlen(pos->sm_name) != hostname_len || memcmp(pos->sm_name, hostname, hostname_len)) continue; - } else if (!nlm_cmp_addr(&pos->sm_addr, sin)) + } else if (!nlm_cmp_addr(nsm_addr_in(pos), sin)) continue; atomic_inc(&pos->sm_count); kfree(nsm); @@ -571,7 +571,8 @@ retry: if (nsm == NULL) return NULL; - nsm->sm_addr = *sin; + memcpy(nsm_addr(nsm), sin, sizeof(*sin)); + nsm->sm_addrlen = sizeof(*sin); nsm->sm_name = (char *) (nsm + 1); memcpy(nsm->sm_name, hostname, hostname_len); nsm->sm_name[hostname_len] = '\0'; diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index e4d563543b11..4e7e958e8f67 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -51,7 +51,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) memset(&args, 0, sizeof(args)); args.mon_name = nsm->sm_name; - args.addr = nsm->sm_addr.sin_addr.s_addr; + args.addr = nsm_addr_in(nsm)->sin_addr.s_addr; args.prog = NLM_PROGRAM; args.vers = 3; args.proc = NLMPROC_NSM_NOTIFY; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 964e6c93830f..b1dfa0b1d1bc 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -71,7 +71,8 @@ struct nsm_handle { struct list_head sm_link; atomic_t sm_count; char * sm_name; - struct sockaddr_in sm_addr; + struct sockaddr_storage sm_addr; + size_t sm_addrlen; unsigned int sm_monitored : 1, sm_sticky : 1; /* don't unmonitor */ char sm_addrbuf[48]; /* address eyecatcher */ @@ -100,6 +101,16 @@ static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host) return (struct sockaddr *)&host->h_srcaddr; } +static inline struct sockaddr_in *nsm_addr_in(const struct nsm_handle *handle) +{ + return (struct sockaddr_in *)&handle->sm_addr; +} + +static inline struct sockaddr *nsm_addr(const struct nsm_handle *handle) +{ + return (struct sockaddr *)&handle->sm_addr; +} + /* * Map an fl_owner_t into a unique 32-bit "pid" */ -- cgit v1.2.3 From 781b61a6f4ff94cb8c14cf598b547f5d5c490969 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 3 Sep 2008 14:36:01 -0400 Subject: lockd: Teach nlm_cmp_addr() to support AF_INET6 addresses Update the nlm_cmp_addr() helper to support AF_INET6 as well as AF_INET addresses. New version takes two "struct sockaddr *" arguments instead of "struct sockaddr_in *" arguments. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 3 ++- fs/lockd/host.c | 6 +++--- fs/lockd/svcsubs.c | 2 +- include/linux/lockd/lockd.h | 36 ++++++++++++++++++++++++++++++++---- 4 files changed, 38 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 0df5587f804e..237224a3c420 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -166,7 +166,8 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock */ if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) continue; - if (!nlm_cmp_addr(nlm_addr_in(block->b_host), addr)) + if (!nlm_cmp_addr(nlm_addr(block->b_host), + (struct sockaddr *)addr)) continue; if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0) continue; diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 510ebcf485f0..dbf3fe620a0c 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -116,7 +116,7 @@ static struct nlm_host *nlm_lookup_host(int server, */ chain = &nlm_hosts[hash]; hlist_for_each_entry(host, pos, chain, h_hash) { - if (!nlm_cmp_addr(nlm_addr_in(host), sin)) + if (!nlm_cmp_addr(nlm_addr(host), (struct sockaddr *)sin)) continue; /* See if we have an NSM handle for this client */ @@ -129,7 +129,7 @@ static struct nlm_host *nlm_lookup_host(int server, continue; if (host->h_server != server) continue; - if (!nlm_cmp_addr(nlm_srcaddr_in(host), ssin)) + if (!nlm_cmp_addr(nlm_srcaddr(host), (struct sockaddr *)ssin)) continue; /* Move to head of hash chain. */ @@ -551,7 +551,7 @@ retry: if (strlen(pos->sm_name) != hostname_len || memcmp(pos->sm_name, hostname, hostname_len)) continue; - } else if (!nlm_cmp_addr(nsm_addr_in(pos), sin)) + } else if (!nlm_cmp_addr(nsm_addr(pos), (struct sockaddr *)sin)) continue; atomic_inc(&pos->sm_count); kfree(nsm); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index d3d1330d7c27..34c2766e27c7 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -418,7 +418,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) { - return nlm_cmp_addr(nlm_srcaddr_in(host), datap); + return nlm_cmp_addr(nlm_srcaddr(host), datap); } /** diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index b1dfa0b1d1bc..ec8af115843d 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -12,6 +12,8 @@ #ifdef __KERNEL__ #include +#include +#include #include #include #include @@ -272,13 +274,39 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) return file->f_file->f_path.dentry->d_inode; } +static inline int __nlm_cmp_addr4(const struct sockaddr *sap1, + const struct sockaddr *sap2) +{ + const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sap1; + const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sap2; + return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; +} + +static inline int __nlm_cmp_addr6(const struct sockaddr *sap1, + const struct sockaddr *sap2) +{ + const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1; + const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2; + return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); +} + /* - * Compare two host addresses (needs modifying for ipv6) + * Compare two host addresses + * + * Return TRUE if the addresses are the same; otherwise FALSE. */ -static inline int nlm_cmp_addr(const struct sockaddr_in *sin1, - const struct sockaddr_in *sin2) +static inline int nlm_cmp_addr(const struct sockaddr *sap1, + const struct sockaddr *sap2) { - return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; + if (sap1->sa_family == sap2->sa_family) { + switch (sap1->sa_family) { + case AF_INET: + return __nlm_cmp_addr4(sap1, sap2); + case AF_INET6: + return __nlm_cmp_addr6(sap1, sap2); + } + } + return 0; } /* -- cgit v1.2.3 From d5b337b4877f7c4e1d761434ee04d045b0201e03 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Sun, 28 Sep 2008 09:21:26 +0300 Subject: nfsd: use nfs client rpc callback program since commit ff7d9756b501744540be65e172d27ee321d86103 "nfsd: use static memory for callback program and stats" do_probe_callback uses a static callback program (NFS4_CALLBACK) rather than the one set in clp->cl_callback.cb_prog as passed in by the client in setclientid (4.0) or create_session (4.1). This patches introduces rpc_create_args.prognumber that allows overriding program->number when creating rpc_clnt. Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 1 + include/linux/sunrpc/clnt.h | 1 + net/sunrpc/clnt.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index f7c793a5b803..094747a1227c 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -380,6 +380,7 @@ static int do_probe_callback(void *data) .addrsize = sizeof(addr), .timeout = &timeparms, .program = &cb_program, + .prognumber = cb->cb_prog, .version = nfs_cb_version[1]->number, .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */ .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 8ac8e75243a7..6f0ee1b84a4f 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -104,6 +104,7 @@ struct rpc_create_args { const struct rpc_timeout *timeout; char *servername; struct rpc_program *program; + u32 prognumber; /* overrides program->number */ u32 version; rpc_authflavor_t authflavor; unsigned long flags; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 76739e928d0d..da0789fa1b88 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -174,7 +174,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; clnt->cl_protname = program->name; - clnt->cl_prog = program->number; + clnt->cl_prog = args->prognumber ? : program->number; clnt->cl_vers = version->number; clnt->cl_stats = program->stats; clnt->cl_metrics = rpc_alloc_iostats(clnt); -- cgit v1.2.3 From cf04a4c764cd3e651a64b3e667bb6a673ead99e1 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 30 Sep 2008 02:22:14 -0700 Subject: netdev: use const for some name functions dev_change_name and netdev_drivername should use const char on parameters that are read-only input values. The strcpy to newname is not needed since newname is not used later in function. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d675df08b946..9cfd20be8b7f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1225,7 +1225,7 @@ extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); extern int dev_ethtool(struct net *net, struct ifreq *); extern unsigned dev_get_flags(const struct net_device *); extern int dev_change_flags(struct net_device *, unsigned); -extern int dev_change_name(struct net_device *, char *); +extern int dev_change_name(struct net_device *, const char *); extern int dev_set_alias(struct net_device *, const char *, size_t); extern int dev_change_net_namespace(struct net_device *, struct net *, const char *); @@ -1670,7 +1670,7 @@ extern void dev_seq_stop(struct seq_file *seq, void *v); extern int netdev_class_create_file(struct class_attribute *class_attr); extern void netdev_class_remove_file(struct class_attribute *class_attr); -extern char *netdev_drivername(struct net_device *dev, char *buffer, int len); +extern char *netdev_drivername(const struct net_device *dev, char *buffer, int len); extern void linkwatch_run_queue(void); diff --git a/net/core/dev.c b/net/core/dev.c index a90737fe2472..64f0d5b7cdfc 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -890,7 +890,7 @@ int dev_alloc_name(struct net_device *dev, const char *name) * Change name of a device, can pass format strings "eth%d". * for wildcarding. */ -int dev_change_name(struct net_device *dev, char *newname) +int dev_change_name(struct net_device *dev, const char *newname) { char oldname[IFNAMSIZ]; int err = 0; @@ -916,7 +916,6 @@ int dev_change_name(struct net_device *dev, char *newname) err = dev_alloc_name(dev, newname); if (err < 0) return err; - strcpy(newname, dev->name); } else if (__dev_get_by_name(net, newname)) return -EEXIST; @@ -4754,10 +4753,10 @@ err_name: return -ENOMEM; } -char *netdev_drivername(struct net_device *dev, char *buffer, int len) +char *netdev_drivername(const struct net_device *dev, char *buffer, int len) { - struct device_driver *driver; - struct device *parent; + const struct device_driver *driver; + const struct device *parent; if (len <= 0 || !buffer) return buffer; -- cgit v1.2.3 From a57334e95e4fb132acca05bdc0efa2f9dda194af Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Tue, 30 Sep 2008 02:53:18 -0700 Subject: Phonet: declare headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/Kbuild | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index b68ec09399be..f431e40725d6 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -126,6 +126,7 @@ header-y += pci_regs.h header-y += pfkeyv2.h header-y += pg.h header-y += phantom.h +header-y += phonet.h header-y += pkt_cls.h header-y += pkt_sched.h header-y += posix_types.h @@ -232,6 +233,7 @@ unifdef-y += if_fddi.h unifdef-y += if_frad.h unifdef-y += if_ltalk.h unifdef-y += if_link.h +unifdef-y += if_phonet.h unifdef-y += if_pppol2tp.h unifdef-y += if_pppox.h unifdef-y += if_tr.h -- cgit v1.2.3 From 1c50b728c3e734150b8a4a8310ce3e01bc5c70be Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 29 Sep 2008 11:06:46 -0400 Subject: rcu: add rcu_read_lock_sched() / rcu_read_unlock_sched() Add rcu_read_lock_sched() and rcu_read_unlock_sched() to rcupdate.h to match the recently added write-side call_rcu_sched() and rcu_barrier_sched(). They also match the no-so-recently-added synchronize_sched(). It will help following matching use of the update/read lock primitives. Those new read lock will replace preempt_disable()/enable() used in pair with RCU-classic synchronization. Signed-off-by: Mathieu Desnoyers Acked-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/rcupdate.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index e8b4039cfb2f..86f1f5e43e33 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -132,6 +132,26 @@ struct rcu_head { */ #define rcu_read_unlock_bh() __rcu_read_unlock_bh() +/** + * rcu_read_lock_sched - mark the beginning of a RCU-classic critical section + * + * Should be used with either + * - synchronize_sched() + * or + * - call_rcu_sched() and rcu_barrier_sched() + * on the write-side to insure proper synchronization. + */ +#define rcu_read_lock_sched() preempt_disable() + +/* + * rcu_read_unlock_sched - marks the end of a RCU-classic critical section + * + * See rcu_read_lock_sched for more information. + */ +#define rcu_read_unlock_sched() preempt_enable() + + + /** * rcu_dereference - fetch an RCU-protected pointer in an * RCU read-side critical section. This pointer may later -- cgit v1.2.3 From 24268245d8ba9270152b2281666099ddc8ca389d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 1 Oct 2008 08:59:40 +0200 Subject: x86: add PCI IDs for AMD Barcelona PCI devices Signed-off-by: Robert Richter Cc: oprofile-list Cc: Barry Kasindorf Signed-off-by: Ingo Molnar --- include/linux/pci_ids.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 2886b0eb53ec..c114103af987 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -497,6 +497,11 @@ #define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 #define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 #define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 #define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 #define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 #define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 -- cgit v1.2.3 From 6e50e8a2136f1a90de251c653226ded447c5c915 Mon Sep 17 00:00:00 2001 From: Remi Denis-Courmont Date: Wed, 1 Oct 2008 01:30:19 -0700 Subject: phonet: Protect if_phonet.h against multiple inclusions. From: Remi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/if_phonet.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_phonet.h b/include/linux/if_phonet.h index 7e989216ec17..d70034bcec05 100644 --- a/include/linux/if_phonet.h +++ b/include/linux/if_phonet.h @@ -5,14 +5,15 @@ * * Copyright (C) 2008 Nokia Corporation. All rights reserved. */ +#ifndef LINUX_IF_PHONET_H +#define LINUX_IF_PHONET_H -#define PHONET_HEADER_LEN 8 /* Phonet header length */ - -#define PHONET_MIN_MTU 6 -/* 6 bytes header + 65535 bytes payload */ -#define PHONET_MAX_MTU 65541 +#define PHONET_MIN_MTU 6 /* pn_length = 0 */ +#define PHONET_MAX_MTU 65541 /* pn_length = 0xffff */ #define PHONET_DEV_MTU PHONET_MAX_MTU #ifdef __KERNEL__ extern struct header_ops phonet_header_ops; #endif + +#endif -- cgit v1.2.3 From 04a4bb55bcf35b63d40fd2725e58599ff8310dd7 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Wed, 1 Oct 2008 02:33:12 -0700 Subject: net: add skb_recycle_check() to enable netdriver skb recycling This patch adds skb_recycle_check(), which can be used by a network driver after transmitting an skb to check whether this skb can be recycled as a receive buffer. skb_recycle_check() checks that the skb is not shared or cloned, and that it is linear and its head portion large enough (as determined by the driver) to be recycled as a receive buffer. If these conditions are met, it does any necessary reference count dropping and cleans up the skbuff as if it just came from __alloc_skb(). Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 ++ net/core/skbuff.c | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a19ea43fea02..720b688c22b6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -383,6 +383,8 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size, return __alloc_skb(size, priority, 1, -1); } +extern int skb_recycle_check(struct sk_buff *skb, int skb_size); + extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); extern struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ca1ccdf1ef76..2c218a0808b4 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -363,8 +363,7 @@ static void kfree_skbmem(struct sk_buff *skb) } } -/* Free everything but the sk_buff shell. */ -static void skb_release_all(struct sk_buff *skb) +static void skb_release_head_state(struct sk_buff *skb) { dst_release(skb->dst); #ifdef CONFIG_XFRM @@ -388,6 +387,12 @@ static void skb_release_all(struct sk_buff *skb) skb->tc_verd = 0; #endif #endif +} + +/* Free everything but the sk_buff shell. */ +static void skb_release_all(struct sk_buff *skb) +{ + skb_release_head_state(skb); skb_release_data(skb); } @@ -424,6 +429,38 @@ void kfree_skb(struct sk_buff *skb) __kfree_skb(skb); } +int skb_recycle_check(struct sk_buff *skb, int skb_size) +{ + struct skb_shared_info *shinfo; + + if (skb_is_nonlinear(skb) || skb->fclone != SKB_FCLONE_UNAVAILABLE) + return 0; + + skb_size = SKB_DATA_ALIGN(skb_size + NET_SKB_PAD); + if (skb_end_pointer(skb) - skb->head < skb_size) + return 0; + + if (skb_shared(skb) || skb_cloned(skb)) + return 0; + + skb_release_head_state(skb); + shinfo = skb_shinfo(skb); + atomic_set(&shinfo->dataref, 1); + shinfo->nr_frags = 0; + shinfo->gso_size = 0; + shinfo->gso_segs = 0; + shinfo->gso_type = 0; + shinfo->ip6_frag_id = 0; + shinfo->frag_list = NULL; + + memset(skb, 0, offsetof(struct sk_buff, tail)); + skb_reset_tail_pointer(skb); + skb->data = skb->head + NET_SKB_PAD; + + return 1; +} +EXPORT_SYMBOL(skb_recycle_check); + static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { new->tstamp = old->tstamp; -- cgit v1.2.3 From 12a169e7d8f4b1c95252d8b04ed0f1033ed7cfe2 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 1 Oct 2008 07:03:24 -0700 Subject: ipsec: Put dumpers on the dump list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Herbert Xu came up with the idea and the original patch to make xfrm_state dump list contain also dumpers: As it is we go to extraordinary lengths to ensure that states don't go away while dumpers go to sleep. It's much easier if we just put the dumpers themselves on the list since they can't go away while they're going. I've also changed the order of addition on new states to prevent a never-ending dump. Timo Teräs improved the patch to apply cleanly to latest tree, modified iteration code to be more readable by using a common struct for entries in the list, implemented the same idea for xfrm_policy dumping and moved the af_key specific "last" entry caching to af_key. Signed-off-by: Herbert Xu Signed-off-by: Timo Teras Signed-off-by: David S. Miller --- include/linux/netlink.h | 2 +- include/net/xfrm.h | 70 ++++++++++++------------------ net/key/af_key.c | 38 ++++++++++++++--- net/xfrm/xfrm_policy.c | 111 +++++++++++++++++++++++++----------------------- net/xfrm/xfrm_state.c | 109 ++++++++++++++++------------------------------- net/xfrm/xfrm_user.c | 4 +- 6 files changed, 159 insertions(+), 175 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index cbba7760545b..9ff1b54908f3 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -220,7 +220,7 @@ struct netlink_callback int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); int family; - long args[7]; + long args[6]; }; struct netlink_notify diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 48630b266593..b98d2056f27f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -117,12 +117,21 @@ extern struct mutex xfrm_cfg_mutex; metrics. Plus, it will be made via sk->sk_dst_cache. Solved. */ +struct xfrm_state_walk { + struct list_head all; + u8 state; + union { + u8 dying; + u8 proto; + }; + u32 seq; +}; + /* Full description of state of transformer. */ struct xfrm_state { - struct list_head all; union { - struct list_head gclist; + struct hlist_node gclist; struct hlist_node bydst; }; struct hlist_node bysrc; @@ -136,12 +145,8 @@ struct xfrm_state u32 genid; - /* Key manger bits */ - struct { - u8 state; - u8 dying; - u32 seq; - } km; + /* Key manager bits */ + struct xfrm_state_walk km; /* Parameters of this state. */ struct { @@ -449,10 +454,20 @@ struct xfrm_tmpl #define XFRM_MAX_DEPTH 6 +struct xfrm_policy_walk_entry { + struct list_head all; + u8 dead; +}; + +struct xfrm_policy_walk { + struct xfrm_policy_walk_entry walk; + u8 type; + u32 seq; +}; + struct xfrm_policy { struct xfrm_policy *next; - struct list_head bytype; struct hlist_node bydst; struct hlist_node byidx; @@ -467,13 +482,12 @@ struct xfrm_policy struct xfrm_lifetime_cfg lft; struct xfrm_lifetime_cur curlft; struct dst_entry *bundles; - u16 family; + struct xfrm_policy_walk_entry walk; u8 type; u8 action; u8 flags; - u8 dead; u8 xfrm_nr; - /* XXX 1 byte hole, try to pack */ + u16 family; struct xfrm_sec_ctx *security; struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH]; }; @@ -1245,20 +1259,6 @@ struct xfrm6_tunnel { int priority; }; -struct xfrm_state_walk { - struct list_head list; - unsigned long genid; - struct xfrm_state *state; - int count; - u8 proto; -}; - -struct xfrm_policy_walk { - struct xfrm_policy *policy; - int count; - u8 type, cur_type; -}; - extern void xfrm_init(void); extern void xfrm4_init(void); extern void xfrm_state_init(void); @@ -1410,24 +1410,10 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp); -static inline void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type) -{ - walk->cur_type = XFRM_POLICY_TYPE_MAIN; - walk->type = type; - walk->policy = NULL; - walk->count = 0; -} - -static inline void xfrm_policy_walk_done(struct xfrm_policy_walk *walk) -{ - if (walk->policy != NULL) { - xfrm_pol_put(walk->policy); - walk->policy = NULL; - } -} - +extern void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type); extern int xfrm_policy_walk(struct xfrm_policy_walk *walk, int (*func)(struct xfrm_policy *, int, int, void*), void *); +extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk); int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl); struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir, struct xfrm_selector *sel, diff --git a/net/key/af_key.c b/net/key/af_key.c index b7f5a1c353ee..7ae641df70bd 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -58,6 +58,7 @@ struct pfkey_sock { struct xfrm_policy_walk policy; struct xfrm_state_walk state; } u; + struct sk_buff *skb; } dump; }; @@ -76,6 +77,10 @@ static int pfkey_can_dump(struct sock *sk) static void pfkey_terminate_dump(struct pfkey_sock *pfk) { if (pfk->dump.dump) { + if (pfk->dump.skb) { + kfree_skb(pfk->dump.skb); + pfk->dump.skb = NULL; + } pfk->dump.done(pfk); pfk->dump.dump = NULL; pfk->dump.done = NULL; @@ -308,12 +313,25 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, static int pfkey_do_dump(struct pfkey_sock *pfk) { + struct sadb_msg *hdr; int rc; rc = pfk->dump.dump(pfk); if (rc == -ENOBUFS) return 0; + if (pfk->dump.skb) { + if (!pfkey_can_dump(&pfk->sk)) + return 0; + + hdr = (struct sadb_msg *) pfk->dump.skb->data; + hdr->sadb_msg_seq = 0; + hdr->sadb_msg_errno = rc; + pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + &pfk->sk); + pfk->dump.skb = NULL; + } + pfkey_terminate_dump(pfk); return rc; } @@ -1744,9 +1762,14 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr) out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto); out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_reserved = 0; - out_hdr->sadb_msg_seq = count; + out_hdr->sadb_msg_seq = count + 1; out_hdr->sadb_msg_pid = pfk->dump.msg_pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk); + + if (pfk->dump.skb) + pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + &pfk->sk); + pfk->dump.skb = out_skb; + return 0; } @@ -2245,7 +2268,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h return 0; out: - xp->dead = 1; + xp->walk.dead = 1; xfrm_policy_destroy(xp); return err; } @@ -2583,9 +2606,14 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr) out_hdr->sadb_msg_type = SADB_X_SPDDUMP; out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC; out_hdr->sadb_msg_errno = 0; - out_hdr->sadb_msg_seq = count; + out_hdr->sadb_msg_seq = count + 1; out_hdr->sadb_msg_pid = pfk->dump.msg_pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk); + + if (pfk->dump.skb) + pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + &pfk->sk); + pfk->dump.skb = out_skb; + return 0; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ef9ccbc38752..b7ec08025ffb 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -46,7 +46,7 @@ EXPORT_SYMBOL(xfrm_cfg_mutex); static DEFINE_RWLOCK(xfrm_policy_lock); -static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX]; +static struct list_head xfrm_policy_all; unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2]; EXPORT_SYMBOL(xfrm_policy_count); @@ -164,7 +164,7 @@ static void xfrm_policy_timer(unsigned long data) read_lock(&xp->lock); - if (xp->dead) + if (xp->walk.dead) goto out; dir = xfrm_policy_id2dir(xp->index); @@ -236,7 +236,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp) policy = kzalloc(sizeof(struct xfrm_policy), gfp); if (policy) { - INIT_LIST_HEAD(&policy->bytype); + INIT_LIST_HEAD(&policy->walk.all); INIT_HLIST_NODE(&policy->bydst); INIT_HLIST_NODE(&policy->byidx); rwlock_init(&policy->lock); @@ -252,17 +252,13 @@ EXPORT_SYMBOL(xfrm_policy_alloc); void xfrm_policy_destroy(struct xfrm_policy *policy) { - BUG_ON(!policy->dead); + BUG_ON(!policy->walk.dead); BUG_ON(policy->bundles); if (del_timer(&policy->timer)) BUG(); - write_lock_bh(&xfrm_policy_lock); - list_del(&policy->bytype); - write_unlock_bh(&xfrm_policy_lock); - security_xfrm_policy_free(policy->security); kfree(policy); } @@ -310,8 +306,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy) int dead; write_lock_bh(&policy->lock); - dead = policy->dead; - policy->dead = 1; + dead = policy->walk.dead; + policy->walk.dead = 1; write_unlock_bh(&policy->lock); if (unlikely(dead)) { @@ -609,6 +605,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) if (delpol) { hlist_del(&delpol->bydst); hlist_del(&delpol->byidx); + list_del(&delpol->walk.all); xfrm_policy_count[dir]--; } policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir); @@ -617,7 +614,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) policy->curlft.use_time = 0; if (!mod_timer(&policy->timer, jiffies + HZ)) xfrm_pol_hold(policy); - list_add_tail(&policy->bytype, &xfrm_policy_bytype[policy->type]); + list_add(&policy->walk.all, &xfrm_policy_all); write_unlock_bh(&xfrm_policy_lock); if (delpol) @@ -684,6 +681,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir, } hlist_del(&pol->bydst); hlist_del(&pol->byidx); + list_del(&pol->walk.all); xfrm_policy_count[dir]--; } ret = pol; @@ -727,6 +725,7 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete, } hlist_del(&pol->bydst); hlist_del(&pol->byidx); + list_del(&pol->walk.all); xfrm_policy_count[dir]--; } ret = pol; @@ -840,6 +839,7 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info) continue; hlist_del(&pol->bydst); hlist_del(&pol->byidx); + list_del(&pol->walk.all); write_unlock_bh(&xfrm_policy_lock); xfrm_audit_policy_delete(pol, 1, @@ -867,60 +867,68 @@ int xfrm_policy_walk(struct xfrm_policy_walk *walk, int (*func)(struct xfrm_policy *, int, int, void*), void *data) { - struct xfrm_policy *old, *pol, *last = NULL; + struct xfrm_policy *pol; + struct xfrm_policy_walk_entry *x; int error = 0; if (walk->type >= XFRM_POLICY_TYPE_MAX && walk->type != XFRM_POLICY_TYPE_ANY) return -EINVAL; - if (walk->policy == NULL && walk->count != 0) + if (list_empty(&walk->walk.all) && walk->seq != 0) return 0; - old = pol = walk->policy; - walk->policy = NULL; - read_lock_bh(&xfrm_policy_lock); - - for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) { - if (walk->type != walk->cur_type && - walk->type != XFRM_POLICY_TYPE_ANY) + write_lock_bh(&xfrm_policy_lock); + if (list_empty(&walk->walk.all)) + x = list_first_entry(&xfrm_policy_all, struct xfrm_policy_walk_entry, all); + else + x = list_entry(&walk->walk.all, struct xfrm_policy_walk_entry, all); + list_for_each_entry_from(x, &xfrm_policy_all, all) { + if (x->dead) continue; - - if (pol == NULL) { - pol = list_first_entry(&xfrm_policy_bytype[walk->cur_type], - struct xfrm_policy, bytype); - } - list_for_each_entry_from(pol, &xfrm_policy_bytype[walk->cur_type], bytype) { - if (pol->dead) - continue; - if (last) { - error = func(last, xfrm_policy_id2dir(last->index), - walk->count, data); - if (error) { - xfrm_pol_hold(last); - walk->policy = last; - goto out; - } - } - last = pol; - walk->count++; + pol = container_of(x, struct xfrm_policy, walk); + if (walk->type != XFRM_POLICY_TYPE_ANY && + walk->type != pol->type) + continue; + error = func(pol, xfrm_policy_id2dir(pol->index), + walk->seq, data); + if (error) { + list_move_tail(&walk->walk.all, &x->all); + goto out; } - pol = NULL; + walk->seq++; } - if (walk->count == 0) { + if (walk->seq == 0) { error = -ENOENT; goto out; } - if (last) - error = func(last, xfrm_policy_id2dir(last->index), 0, data); + list_del_init(&walk->walk.all); out: - read_unlock_bh(&xfrm_policy_lock); - if (old != NULL) - xfrm_pol_put(old); + write_unlock_bh(&xfrm_policy_lock); return error; } EXPORT_SYMBOL(xfrm_policy_walk); +void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type) +{ + INIT_LIST_HEAD(&walk->walk.all); + walk->walk.dead = 1; + walk->type = type; + walk->seq = 0; +} +EXPORT_SYMBOL(xfrm_policy_walk_init); + +void xfrm_policy_walk_done(struct xfrm_policy_walk *walk) +{ + if (list_empty(&walk->walk.all)) + return; + + write_lock_bh(&xfrm_policy_lock); + list_del(&walk->walk.all); + write_unlock_bh(&xfrm_policy_lock); +} +EXPORT_SYMBOL(xfrm_policy_walk_done); + /* * Find policy to apply to this flow. * @@ -1077,7 +1085,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir) struct hlist_head *chain = policy_hash_bysel(&pol->selector, pol->family, dir); - list_add_tail(&pol->bytype, &xfrm_policy_bytype[pol->type]); + list_add(&pol->walk.all, &xfrm_policy_all); hlist_add_head(&pol->bydst, chain); hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index)); xfrm_policy_count[dir]++; @@ -1095,6 +1103,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, hlist_del(&pol->bydst); hlist_del(&pol->byidx); + list_del(&pol->walk.all); xfrm_policy_count[dir]--; return pol; @@ -1720,7 +1729,7 @@ restart: for (pi = 0; pi < npols; pi++) { read_lock_bh(&pols[pi]->lock); - pol_dead |= pols[pi]->dead; + pol_dead |= pols[pi]->walk.dead; read_unlock_bh(&pols[pi]->lock); } @@ -2415,9 +2424,7 @@ static void __init xfrm_policy_init(void) panic("XFRM: failed to allocate bydst hash\n"); } - for (dir = 0; dir < XFRM_POLICY_TYPE_MAX; dir++) - INIT_LIST_HEAD(&xfrm_policy_bytype[dir]); - + INIT_LIST_HEAD(&xfrm_policy_all); INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task); register_netdevice_notifier(&xfrm_dev_notifier); } @@ -2601,7 +2608,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol, int i, j, n = 0; write_lock_bh(&pol->lock); - if (unlikely(pol->dead)) { + if (unlikely(pol->walk.dead)) { /* target policy has been deleted */ write_unlock_bh(&pol->lock); return -ENOENT; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 053970e8765d..747fd8c291a7 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -59,14 +59,6 @@ static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; static unsigned int xfrm_state_num; static unsigned int xfrm_state_genid; -/* Counter indicating ongoing walk, protected by xfrm_state_lock. */ -static unsigned long xfrm_state_walk_ongoing; -/* Counter indicating walk completion, protected by xfrm_cfg_mutex. */ -static unsigned long xfrm_state_walk_completed; - -/* List of outstanding state walks used to set the completed counter. */ -static LIST_HEAD(xfrm_state_walks); - static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); @@ -199,8 +191,7 @@ static DEFINE_RWLOCK(xfrm_state_afinfo_lock); static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; static struct work_struct xfrm_state_gc_work; -static LIST_HEAD(xfrm_state_gc_leftovers); -static LIST_HEAD(xfrm_state_gc_list); +static HLIST_HEAD(xfrm_state_gc_list); static DEFINE_SPINLOCK(xfrm_state_gc_lock); int __xfrm_state_delete(struct xfrm_state *x); @@ -412,23 +403,16 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x) static void xfrm_state_gc_task(struct work_struct *data) { - struct xfrm_state *x, *tmp; - unsigned long completed; + struct xfrm_state *x; + struct hlist_node *entry, *tmp; + struct hlist_head gc_list; - mutex_lock(&xfrm_cfg_mutex); spin_lock_bh(&xfrm_state_gc_lock); - list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers); + hlist_move_list(&xfrm_state_gc_list, &gc_list); spin_unlock_bh(&xfrm_state_gc_lock); - completed = xfrm_state_walk_completed; - mutex_unlock(&xfrm_cfg_mutex); - - list_for_each_entry_safe(x, tmp, &xfrm_state_gc_leftovers, gclist) { - if ((long)(x->lastused - completed) > 0) - break; - list_del(&x->gclist); + hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist) xfrm_state_gc_destroy(x); - } wake_up(&km_waitq); } @@ -529,7 +513,7 @@ struct xfrm_state *xfrm_state_alloc(void) if (x) { atomic_set(&x->refcnt, 1); atomic_set(&x->tunnel_users, 0); - INIT_LIST_HEAD(&x->all); + INIT_LIST_HEAD(&x->km.all); INIT_HLIST_NODE(&x->bydst); INIT_HLIST_NODE(&x->bysrc); INIT_HLIST_NODE(&x->byspi); @@ -556,7 +540,7 @@ void __xfrm_state_destroy(struct xfrm_state *x) WARN_ON(x->km.state != XFRM_STATE_DEAD); spin_lock_bh(&xfrm_state_gc_lock); - list_add_tail(&x->gclist, &xfrm_state_gc_list); + hlist_add_head(&x->gclist, &xfrm_state_gc_list); spin_unlock_bh(&xfrm_state_gc_lock); schedule_work(&xfrm_state_gc_work); } @@ -569,8 +553,7 @@ int __xfrm_state_delete(struct xfrm_state *x) if (x->km.state != XFRM_STATE_DEAD) { x->km.state = XFRM_STATE_DEAD; spin_lock(&xfrm_state_lock); - x->lastused = xfrm_state_walk_ongoing; - list_del_rcu(&x->all); + list_del(&x->km.all); hlist_del(&x->bydst); hlist_del(&x->bysrc); if (x->id.spi) @@ -871,7 +854,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, if (km_query(x, tmpl, pol) == 0) { x->km.state = XFRM_STATE_ACQ; - list_add_tail(&x->all, &xfrm_state_all); + list_add(&x->km.all, &xfrm_state_all); hlist_add_head(&x->bydst, xfrm_state_bydst+h); h = xfrm_src_hash(daddr, saddr, family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); @@ -940,7 +923,7 @@ static void __xfrm_state_insert(struct xfrm_state *x) x->genid = ++xfrm_state_genid; - list_add_tail(&x->all, &xfrm_state_all); + list_add(&x->km.all, &xfrm_state_all); h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, x->props.reqid, x->props.family); @@ -1069,7 +1052,7 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re xfrm_state_hold(x); x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ; add_timer(&x->timer); - list_add_tail(&x->all, &xfrm_state_all); + list_add(&x->km.all, &xfrm_state_all); hlist_add_head(&x->bydst, xfrm_state_bydst+h); h = xfrm_src_hash(daddr, saddr, family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); @@ -1566,79 +1549,59 @@ int xfrm_state_walk(struct xfrm_state_walk *walk, int (*func)(struct xfrm_state *, int, void*), void *data) { - struct xfrm_state *old, *x, *last = NULL; + struct xfrm_state *state; + struct xfrm_state_walk *x; int err = 0; - if (walk->state == NULL && walk->count != 0) + if (walk->seq != 0 && list_empty(&walk->all)) return 0; - old = x = walk->state; - walk->state = NULL; spin_lock_bh(&xfrm_state_lock); - if (x == NULL) - x = list_first_entry(&xfrm_state_all, struct xfrm_state, all); + if (list_empty(&walk->all)) + x = list_first_entry(&xfrm_state_all, struct xfrm_state_walk, all); + else + x = list_entry(&walk->all, struct xfrm_state_walk, all); list_for_each_entry_from(x, &xfrm_state_all, all) { - if (x->km.state == XFRM_STATE_DEAD) + if (x->state == XFRM_STATE_DEAD) continue; - if (!xfrm_id_proto_match(x->id.proto, walk->proto)) + state = container_of(x, struct xfrm_state, km); + if (!xfrm_id_proto_match(state->id.proto, walk->proto)) continue; - if (last) { - err = func(last, walk->count, data); - if (err) { - xfrm_state_hold(last); - walk->state = last; - goto out; - } + err = func(state, walk->seq, data); + if (err) { + list_move_tail(&walk->all, &x->all); + goto out; } - last = x; - walk->count++; + walk->seq++; } - if (walk->count == 0) { + if (walk->seq == 0) { err = -ENOENT; goto out; } - if (last) - err = func(last, 0, data); + list_del_init(&walk->all); out: spin_unlock_bh(&xfrm_state_lock); - if (old != NULL) - xfrm_state_put(old); return err; } EXPORT_SYMBOL(xfrm_state_walk); void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) { + INIT_LIST_HEAD(&walk->all); walk->proto = proto; - walk->state = NULL; - walk->count = 0; - list_add_tail(&walk->list, &xfrm_state_walks); - walk->genid = ++xfrm_state_walk_ongoing; + walk->state = XFRM_STATE_DEAD; + walk->seq = 0; } EXPORT_SYMBOL(xfrm_state_walk_init); void xfrm_state_walk_done(struct xfrm_state_walk *walk) { - struct list_head *prev; - - if (walk->state != NULL) { - xfrm_state_put(walk->state); - walk->state = NULL; - } - - prev = walk->list.prev; - list_del(&walk->list); - - if (prev != &xfrm_state_walks) { - list_entry(prev, struct xfrm_state_walk, list)->genid = - walk->genid; + if (list_empty(&walk->all)) return; - } - - xfrm_state_walk_completed = walk->genid; - if (!list_empty(&xfrm_state_gc_leftovers)) - schedule_work(&xfrm_state_gc_work); + spin_lock_bh(&xfrm_state_lock); + list_del(&walk->all); + spin_lock_bh(&xfrm_state_lock); } EXPORT_SYMBOL(xfrm_state_walk_done); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 04c41504f84c..76f75df21e15 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1102,7 +1102,7 @@ static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, return xp; error: *errp = err; - xp->dead = 1; + xp->walk.dead = 1; xfrm_policy_destroy(xp); return NULL; } @@ -1595,7 +1595,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, return -ENOENT; read_lock(&xp->lock); - if (xp->dead) { + if (xp->walk.dead) { read_unlock(&xp->lock); goto out; } -- cgit v1.2.3 From f5715aea4564f233767ea1d944b2637a5fd7cd2e Mon Sep 17 00:00:00 2001 From: KOVACS Krisztian Date: Wed, 1 Oct 2008 07:30:02 -0700 Subject: ipv4: Implement IP_TRANSPARENT socket option This patch introduces the IP_TRANSPARENT socket option: enabling that will make the IPv4 routing omit the non-local source address check on output. Setting IP_TRANSPARENT requires NET_ADMIN capability. Signed-off-by: KOVACS Krisztian Signed-off-by: David S. Miller --- include/linux/in.h | 1 + include/net/inet_sock.h | 3 ++- include/net/inet_timewait_sock.h | 3 ++- net/ipv4/inet_timewait_sock.c | 1 + net/ipv4/ip_sockglue.c | 15 ++++++++++++++- 5 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/in.h b/include/linux/in.h index 4065313cd7ee..db458beef19d 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -75,6 +75,7 @@ struct in_addr { #define IP_IPSEC_POLICY 16 #define IP_XFRM_POLICY 17 #define IP_PASSSEC 18 +#define IP_TRANSPARENT 19 /* BSD compatibility */ #define IP_RECVRETOPTS IP_RETOPTS diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 643e26be058e..e97b66e2a9d0 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -129,7 +129,8 @@ struct inet_sock { is_icsk:1, freebind:1, hdrincl:1, - mc_loop:1; + mc_loop:1, + transparent:1; int mc_index; __be32 mc_addr; struct ip_mc_socklist *mc_list; diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 91324908fccd..80e4977631b8 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -128,7 +128,8 @@ struct inet_timewait_sock { __be16 tw_dport; __u16 tw_num; /* And these are ours. */ - __u8 tw_ipv6only:1; + __u8 tw_ipv6only:1, + tw_transparent:1; /* 15 bits hole, try to pack */ __u16 tw_ipv6_offset; unsigned long tw_ttd; diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 743f011b9a84..1c5fd38f8824 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -126,6 +126,7 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int stat tw->tw_reuse = sk->sk_reuse; tw->tw_hash = sk->sk_hash; tw->tw_ipv6only = 0; + tw->tw_transparent = inet->transparent; tw->tw_prot = sk->sk_prot_creator; twsk_net_set(tw, hold_net(sock_net(sk))); atomic_set(&tw->tw_refcnt, 1); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 105d92a039b9..465abf0a9869 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -419,7 +419,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, (1<= sizeof(int)) { @@ -878,6 +878,16 @@ static int do_ip_setsockopt(struct sock *sk, int level, err = xfrm_user_policy(sk, optname, optval, optlen); break; + case IP_TRANSPARENT: + if (!capable(CAP_NET_ADMIN)) { + err = -EPERM; + break; + } + if (optlen < 1) + goto e_inval; + inet->transparent = !!val; + break; + default: err = -ENOPROTOOPT; break; @@ -1130,6 +1140,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_FREEBIND: val = inet->freebind; break; + case IP_TRANSPARENT: + val = inet->transparent; + break; default: release_sock(sk); return -ENOPROTOOPT; -- cgit v1.2.3 From 4e9687d9c843dc34d368358a36f5f1610e4fbab3 Mon Sep 17 00:00:00 2001 From: Marek Vašut Date: Thu, 11 Sep 2008 19:37:32 +0100 Subject: [ARM] 5248/1: wm97xx generic battery driver This patch adds generic battery driver for wm97xx chips. Signed-off-by: Marek Vasut Acked-by: Anton Vorontsov Acked-by: Mark Brown Signed-off-by: Russell King --- arch/arm/mach-pxa/palmtx.c | 20 +++ drivers/power/Kconfig | 8 +- drivers/power/Makefile | 2 +- drivers/power/palmtx_battery.c | 198 ------------------------------ drivers/power/wm97xx_battery.c | 272 +++++++++++++++++++++++++++++++++++++++++ include/linux/wm97xx_batt.h | 26 ++++ 6 files changed, 323 insertions(+), 203 deletions(-) delete mode 100644 drivers/power/palmtx_battery.c create mode 100644 drivers/power/wm97xx_battery.c create mode 100644 include/linux/wm97xx_batt.h (limited to 'include/linux') diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c index fe924a23debe..4447711c9fc6 100644 --- a/arch/arm/mach-pxa/palmtx.c +++ b/arch/arm/mach-pxa/palmtx.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -339,6 +341,23 @@ static struct platform_device power_supply = { }, }; +/****************************************************************************** + * WM97xx battery + ******************************************************************************/ +static struct wm97xx_batt_info wm97xx_batt_pdata = { + .batt_aux = WM97XX_AUX_ID3, + .temp_aux = WM97XX_AUX_ID2, + .charge_gpio = -1, + .max_voltage = PALMTX_BAT_MAX_VOLTAGE, + .min_voltage = PALMTX_BAT_MIN_VOLTAGE, + .batt_mult = 1000, + .batt_div = 414, + .temp_mult = 1, + .temp_div = 1, + .batt_tech = POWER_SUPPLY_TECHNOLOGY_LIPO, + .batt_name = "main-batt", +}; + /****************************************************************************** * Framebuffer ******************************************************************************/ @@ -401,6 +420,7 @@ static void __init palmtx_init(void) pxa_set_ac97_info(NULL); pxa_set_ficp_info(&palmtx_ficp_platform_data); pxa_set_keypad_info(&palmtx_keypad_platform_data); + wm97xx_bat_set_pdata(&wm97xx_batt_pdata); platform_add_devices(devices, ARRAY_SIZE(devices)); } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 9ce55850271a..1982f8b42782 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -56,10 +56,10 @@ config BATTERY_TOSA Say Y to enable support for the battery on the Sharp Zaurus SL-6000 (tosa) models. -config BATTERY_PALMTX - tristate "Palm T|X battery" - depends on MACH_PALMTX +config BATTERY_WM97XX + bool "WM97xx generic battery driver" + depends on TOUCHSCREEN_WM97XX help - Say Y to enable support for the battery in Palm T|X. + Say Y to enable support for battery measured by WM97xx aux port. endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 4706bf8ff459..4e20026cc45a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -21,4 +21,4 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o -obj-$(CONFIG_BATTERY_PALMTX) += palmtx_battery.o +obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o \ No newline at end of file diff --git a/drivers/power/palmtx_battery.c b/drivers/power/palmtx_battery.c deleted file mode 100644 index 7035bfa41c62..000000000000 --- a/drivers/power/palmtx_battery.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * linux/drivers/power/palmtx_battery.c - * - * Battery measurement code for Palm T|X Handheld computer - * - * based on tosa_battery.c - * - * Copyright (C) 2008 Marek Vasut - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static DEFINE_MUTEX(bat_lock); -static struct work_struct bat_work; -struct mutex work_lock; -int bat_status = POWER_SUPPLY_STATUS_DISCHARGING; - -static unsigned long palmtx_read_bat(struct power_supply *bat_ps) -{ - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, - WM97XX_AUX_ID3) * 1000 / 414; -} - -static unsigned long palmtx_read_temp(struct power_supply *bat_ps) -{ - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, - WM97XX_AUX_ID2); -} - -static int palmtx_bat_get_property(struct power_supply *bat_ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = bat_status; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = palmtx_read_bat(bat_ps); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = PALMTX_BAT_MAX_VOLTAGE; - break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = PALMTX_BAT_MIN_VOLTAGE; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = palmtx_read_temp(bat_ps); - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - default: - return -EINVAL; - } - return 0; -} - -static void palmtx_bat_external_power_changed(struct power_supply *bat_ps) -{ - schedule_work(&bat_work); -} - -static char *status_text[] = { - [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", - [POWER_SUPPLY_STATUS_CHARGING] = "Charging", - [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", -}; - -static void palmtx_bat_update(struct power_supply *bat_ps) -{ - int old_status = bat_status; - - mutex_lock(&work_lock); - - bat_status = gpio_get_value(GPIO_NR_PALMTX_POWER_DETECT) ? - POWER_SUPPLY_STATUS_CHARGING : - POWER_SUPPLY_STATUS_DISCHARGING; - - if (old_status != bat_status) { - pr_debug("%s %s -> %s\n", bat_ps->name, - status_text[old_status], - status_text[bat_status]); - power_supply_changed(bat_ps); - } - - mutex_unlock(&work_lock); -} - -static enum power_supply_property palmtx_bat_main_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_PRESENT, -}; - -struct power_supply bat_ps = { - .name = "main-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = palmtx_bat_main_props, - .num_properties = ARRAY_SIZE(palmtx_bat_main_props), - .get_property = palmtx_bat_get_property, - .external_power_changed = palmtx_bat_external_power_changed, - .use_for_apm = 1, -}; - -static void palmtx_bat_work(struct work_struct *work) -{ - palmtx_bat_update(&bat_ps); -} - -#ifdef CONFIG_PM -static int palmtx_bat_suspend(struct platform_device *dev, pm_message_t state) -{ - flush_scheduled_work(); - return 0; -} - -static int palmtx_bat_resume(struct platform_device *dev) -{ - schedule_work(&bat_work); - return 0; -} -#else -#define palmtx_bat_suspend NULL -#define palmtx_bat_resume NULL -#endif - -static int __devinit palmtx_bat_probe(struct platform_device *dev) -{ - int ret = 0; - - if (!machine_is_palmtx()) - return -ENODEV; - - mutex_init(&work_lock); - - INIT_WORK(&bat_work, palmtx_bat_work); - - ret = power_supply_register(&dev->dev, &bat_ps); - if (!ret) - schedule_work(&bat_work); - - return ret; -} - -static int __devexit palmtx_bat_remove(struct platform_device *dev) -{ - power_supply_unregister(&bat_ps); - return 0; -} - -static struct platform_driver palmtx_bat_driver = { - .driver.name = "wm97xx-battery", - .driver.owner = THIS_MODULE, - .probe = palmtx_bat_probe, - .remove = __devexit_p(palmtx_bat_remove), - .suspend = palmtx_bat_suspend, - .resume = palmtx_bat_resume, -}; - -static int __init palmtx_bat_init(void) -{ - return platform_driver_register(&palmtx_bat_driver); -} - -static void __exit palmtx_bat_exit(void) -{ - platform_driver_unregister(&palmtx_bat_driver); -} - -module_init(palmtx_bat_init); -module_exit(palmtx_bat_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marek Vasut "); -MODULE_DESCRIPTION("Palm T|X battery driver"); diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c new file mode 100644 index 000000000000..8bde92126d34 --- /dev/null +++ b/drivers/power/wm97xx_battery.c @@ -0,0 +1,272 @@ +/* + * linux/drivers/power/wm97xx_battery.c + * + * Battery measurement code for WM97xx + * + * based on tosa_battery.c + * + * Copyright (C) 2008 Marek Vasut + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(bat_lock); +static struct work_struct bat_work; +struct mutex work_lock; +static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; +static struct wm97xx_batt_info *pdata; +static enum power_supply_property *prop; + +static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + pdata->batt_aux) * pdata->batt_mult / + pdata->batt_div; +} + +static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + pdata->temp_aux) * pdata->temp_mult / + pdata->temp_div; +} + +static int wm97xx_bat_get_property(struct power_supply *bat_ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat_status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = pdata->batt_tech; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (pdata->batt_aux >= 0) + val->intval = wm97xx_read_bat(bat_ps); + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_TEMP: + if (pdata->temp_aux >= 0) + val->intval = wm97xx_read_temp(bat_ps); + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + if (pdata->max_voltage >= 0) + val->intval = pdata->max_voltage; + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + if (pdata->min_voltage >= 0) + val->intval = pdata->min_voltage; + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) +{ + schedule_work(&bat_work); +} + +static void wm97xx_bat_update(struct power_supply *bat_ps) +{ + int old_status = bat_status; + + mutex_lock(&work_lock); + + bat_status = (pdata->charge_gpio >= 0) ? + (gpio_get_value(pdata->charge_gpio) ? + POWER_SUPPLY_STATUS_DISCHARGING : + POWER_SUPPLY_STATUS_CHARGING) : + POWER_SUPPLY_STATUS_UNKNOWN; + + if (old_status != bat_status) { + pr_debug("%s: %i -> %i\n", bat_ps->name, old_status, + bat_status); + power_supply_changed(bat_ps); + } + + mutex_unlock(&work_lock); +} + +static struct power_supply bat_ps = { + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = wm97xx_bat_get_property, + .external_power_changed = wm97xx_bat_external_power_changed, + .use_for_apm = 1, +}; + +static void wm97xx_bat_work(struct work_struct *work) +{ + wm97xx_bat_update(&bat_ps); +} + +#ifdef CONFIG_PM +static int wm97xx_bat_suspend(struct platform_device *dev, pm_message_t state) +{ + flush_scheduled_work(); + return 0; +} + +static int wm97xx_bat_resume(struct platform_device *dev) +{ + schedule_work(&bat_work); + return 0; +} +#else +#define wm97xx_bat_suspend NULL +#define wm97xx_bat_resume NULL +#endif + +static int __devinit wm97xx_bat_probe(struct platform_device *dev) +{ + int ret = 0; + int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ + int i = 0; + + if (dev->id != -1) + return -EINVAL; + + mutex_init(&work_lock); + + if (!pdata) { + dev_err(&dev->dev, "Please use wm97xx_bat_set_pdata\n"); + return -EINVAL; + } + + if (pdata->charge_gpio >= 0 && gpio_is_valid(pdata->charge_gpio)) { + ret = gpio_request(pdata->charge_gpio, "BATT CHRG"); + if (ret) + goto err; + ret = gpio_direction_input(pdata->charge_gpio); + if (ret) + goto err2; + props++; /* POWER_SUPPLY_PROP_STATUS */ + } + + if (pdata->batt_tech >= 0) + props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ + if (pdata->temp_aux >= 0) + props++; /* POWER_SUPPLY_PROP_TEMP */ + if (pdata->batt_aux >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ + if (pdata->max_voltage >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ + if (pdata->min_voltage >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ + + prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); + if (!prop) + goto err2; + + prop[i++] = POWER_SUPPLY_PROP_PRESENT; + if (pdata->charge_gpio >= 0) + prop[i++] = POWER_SUPPLY_PROP_STATUS; + if (pdata->batt_tech >= 0) + prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; + if (pdata->temp_aux >= 0) + prop[i++] = POWER_SUPPLY_PROP_TEMP; + if (pdata->batt_aux >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; + if (pdata->max_voltage >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; + if (pdata->min_voltage >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; + + INIT_WORK(&bat_work, wm97xx_bat_work); + + if (!pdata->batt_name) { + dev_info(&dev->dev, "Please consider setting proper battery " + "name in platform definition file, falling " + "back to name \"wm97xx-batt\"\n"); + bat_ps.name = "wm97xx-batt"; + } else + bat_ps.name = pdata->batt_name; + + bat_ps.properties = prop; + bat_ps.num_properties = props; + + ret = power_supply_register(&dev->dev, &bat_ps); + if (!ret) + schedule_work(&bat_work); + else + goto err3; + + return 0; +err3: + kfree(prop); +err2: + gpio_free(pdata->charge_gpio); +err: + return ret; +} + +static int __devexit wm97xx_bat_remove(struct platform_device *dev) +{ + if (pdata && pdata->charge_gpio && pdata->charge_gpio >= 0) + gpio_free(pdata->charge_gpio); + flush_scheduled_work(); + power_supply_unregister(&bat_ps); + kfree(prop); + return 0; +} + +static struct platform_driver wm97xx_bat_driver = { + .driver = { + .name = "wm97xx-battery", + .owner = THIS_MODULE, + }, + .probe = wm97xx_bat_probe, + .remove = __devexit_p(wm97xx_bat_remove), + .suspend = wm97xx_bat_suspend, + .resume = wm97xx_bat_resume, +}; + +static int __init wm97xx_bat_init(void) +{ + return platform_driver_register(&wm97xx_bat_driver); +} + +static void __exit wm97xx_bat_exit(void) +{ + platform_driver_unregister(&wm97xx_bat_driver); +} + +void __init wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) +{ + pdata = data; +} +EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata); + +module_init(wm97xx_bat_init); +module_exit(wm97xx_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("WM97xx battery driver"); diff --git a/include/linux/wm97xx_batt.h b/include/linux/wm97xx_batt.h new file mode 100644 index 000000000000..9681d1ab0e4f --- /dev/null +++ b/include/linux/wm97xx_batt.h @@ -0,0 +1,26 @@ +#ifndef _LINUX_WM97XX_BAT_H +#define _LINUX_WM97XX_BAT_H + +#include + +struct wm97xx_batt_info { + int batt_aux; + int temp_aux; + int charge_gpio; + int min_voltage; + int max_voltage; + int batt_div; + int batt_mult; + int temp_div; + int temp_mult; + int batt_tech; + char *batt_name; +}; + +#ifdef CONFIG_BATTERY_WM97XX +void __init wm97xx_bat_set_pdata(struct wm97xx_batt_info *data); +#else +static inline void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) {} +#endif + +#endif -- cgit v1.2.3 From 4b19de6d1cb07c8bcb6778e771f9cfd5bcfdfd3e Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Thu, 2 Oct 2008 14:50:16 -0700 Subject: mm: tiny-shmem nommu fix The previous patch db203d53d474aa068984e409d807628f5841da1b ("mm: tiny-shmem fix lock ordering: mmap_sem vs i_mutex") to fix the lock ordering in tiny-shmem breaks shared anonymous and IPC memory on NOMMU architectures because it was using the expanding truncate to signal ramfs to allocate a physically contiguous RAM backing the inode (otherwise it is unusable for "memory mapping" it to userspace). However do_truncate is what caused the lock ordering error, due to it taking i_mutex. In this case, we can actually just call ramfs directly to allocate memory for the mapping, rather than go via truncate. Acked-by: David Howells Acked-by: Hugh Dickins Signed-off-by: Nick Piggin Cc: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ramfs/file-nommu.c | 2 +- include/linux/ramfs.h | 1 + mm/tiny-shmem.c | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 52312ec93ff4..5145cb9125af 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -58,7 +58,7 @@ const struct inode_operations ramfs_file_inode_operations = { * size 0 on the assumption that it's going to be used for an mmap of shared * memory */ -static int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) +int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) { struct pagevec lru_pvec; unsigned long npages, xpages, loop, limit; diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index b160fb18e8d6..37aaf2b39863 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -6,6 +6,7 @@ extern int ramfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt); #ifndef CONFIG_MMU +extern int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize); extern unsigned long ramfs_nommu_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, diff --git a/mm/tiny-shmem.c b/mm/tiny-shmem.c index d17cb6f6ab10..8d7a27a6335c 100644 --- a/mm/tiny-shmem.c +++ b/mm/tiny-shmem.c @@ -80,6 +80,12 @@ struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags) inode->i_nlink = 0; /* It is unlinked */ init_file(file, shm_mnt, dentry, FMODE_WRITE | FMODE_READ, &ramfs_file_operations); + +#ifndef CONFIG_MMU + error = ramfs_nommu_expand_for_mapping(inode, size); + if (error) + goto close_file; +#endif return file; close_file: -- cgit v1.2.3 From 2133b5d7ff531bc15a923db4a6a50bf96c561be9 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 2 Oct 2008 16:06:39 -0700 Subject: rcu: RCU-based detection of stalled CPUs for Classic RCU This patch adds stalled-CPU detection to Classic RCU. This capability is enabled by a new config variable CONFIG_RCU_CPU_STALL_DETECTOR, which defaults disabled. This is a debugging feature to detect infinite loops in kernel code, not something that non-kernel-hackers would be expected to care about. This feature can detect looping CPUs in !PREEMPT builds and looping CPUs with preemption disabled in PREEMPT builds. This is essentially a port of this functionality from the treercu patch, replacing the stall debug patch that is already in tip/core/rcu (commit 67182ae1c4). The changes from the patch in tip/core/rcu include making the config variable name match that in treercu, changing from seconds to jiffies to avoid spurious warnings, and printing a boot message when this feature is enabled. Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar --- include/linux/rcuclassic.h | 12 +++- kernel/rcuclassic.c | 166 +++++++++++++++++++++++---------------------- lib/Kconfig.debug | 2 +- 3 files changed, 96 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index 29bf528c7dcc..5f89b62e6983 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -40,15 +40,21 @@ #include #include +#ifdef CONFIG_RCU_CPU_STALL_DETECTOR +#define RCU_SECONDS_TILL_STALL_CHECK ( 3 * HZ) /* for rcp->jiffies_stall */ +#define RCU_SECONDS_TILL_STALL_RECHECK (30 * HZ) /* for rcp->jiffies_stall */ +#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */ /* Global control variables for rcupdate callback mechanism. */ struct rcu_ctrlblk { long cur; /* Current batch number. */ long completed; /* Number of the last completed batch */ long pending; /* Number of the last pending batch */ -#ifdef CONFIG_DEBUG_RCU_STALL - unsigned long gp_check; /* Time grace period should end, in seconds. */ -#endif /* #ifdef CONFIG_DEBUG_RCU_STALL */ +#ifdef CONFIG_RCU_CPU_STALL_DETECTOR + unsigned long gp_start; /* Time at which GP started in jiffies. */ + unsigned long jiffies_stall; + /* Time at which to check for CPU stalls. */ +#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */ int signaled; diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index ed15128ca2c9..0d07e6e51578 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -164,6 +164,87 @@ static void __call_rcu(struct rcu_head *head, struct rcu_ctrlblk *rcp, } } +#ifdef CONFIG_RCU_CPU_STALL_DETECTOR + +static void record_gp_stall_check_time(struct rcu_ctrlblk *rcp) +{ + rcp->gp_start = jiffies; + rcp->jiffies_stall = jiffies + RCU_SECONDS_TILL_STALL_CHECK; +} + +static void print_other_cpu_stall(struct rcu_ctrlblk *rcp) +{ + int cpu; + long delta; + unsigned long flags; + + /* Only let one CPU complain about others per time interval. */ + + spin_lock_irqsave(&rcp->lock, flags); + delta = jiffies - rcp->jiffies_stall; + if (delta < 2 || rcp->cur != rcp->completed) { + spin_unlock_irqrestore(&rcp->lock, flags); + return; + } + rcp->jiffies_stall = jiffies + RCU_SECONDS_TILL_STALL_RECHECK; + spin_unlock_irqrestore(&rcp->lock, flags); + + /* OK, time to rat on our buddy... */ + + printk(KERN_ERR "RCU detected CPU stalls:"); + for_each_possible_cpu(cpu) { + if (cpu_isset(cpu, rcp->cpumask)) + printk(" %d", cpu); + } + printk(" (detected by %d, t=%ld jiffies)\n", + smp_processor_id(), (long)(jiffies - rcp->gp_start)); +} + +static void print_cpu_stall(struct rcu_ctrlblk *rcp) +{ + unsigned long flags; + + printk(KERN_ERR "RCU detected CPU %d stall (t=%lu/%lu jiffies)\n", + smp_processor_id(), jiffies, + jiffies - rcp->gp_start); + dump_stack(); + spin_lock_irqsave(&rcp->lock, flags); + if ((long)(jiffies - rcp->jiffies_stall) >= 0) + rcp->jiffies_stall = + jiffies + RCU_SECONDS_TILL_STALL_RECHECK; + spin_unlock_irqrestore(&rcp->lock, flags); + set_need_resched(); /* kick ourselves to get things going. */ +} + +static void check_cpu_stall(struct rcu_ctrlblk *rcp) +{ + long delta; + + delta = jiffies - rcp->jiffies_stall; + if (cpu_isset(smp_processor_id(), rcp->cpumask) && delta >= 0) { + + /* We haven't checked in, so go dump stack. */ + print_cpu_stall(rcp); + + } else if (rcp->cur != rcp->completed && delta >= 2) { + + /* They had two seconds to dump stack, so complain. */ + print_other_cpu_stall(rcp); + } +} + +#else /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */ + +static void record_gp_stall_check_time(struct rcu_ctrlblk *rcp) +{ +} + +static void check_cpu_stall(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) +{ +} + +#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */ + /** * call_rcu - Queue an RCU callback for invocation after a grace period. * @head: structure to be used for queueing the RCU updates. @@ -293,84 +374,6 @@ static void rcu_do_batch(struct rcu_data *rdp) * period (if necessary). */ -#ifdef CONFIG_DEBUG_RCU_STALL - -static inline void record_gp_check_time(struct rcu_ctrlblk *rcp) -{ - rcp->gp_check = get_seconds() + 3; -} - -static void print_other_cpu_stall(struct rcu_ctrlblk *rcp) -{ - int cpu; - long delta; - unsigned long flags; - - /* Only let one CPU complain about others per time interval. */ - - spin_lock_irqsave(&rcp->lock, flags); - delta = get_seconds() - rcp->gp_check; - if (delta < 2L || cpus_empty(rcp->cpumask)) { - spin_unlock(&rcp->lock); - return; - } - rcp->gp_check = get_seconds() + 30; - spin_unlock_irqrestore(&rcp->lock, flags); - - /* OK, time to rat on our buddy... */ - - printk(KERN_ERR "RCU detected CPU stalls:"); - for_each_cpu_mask(cpu, rcp->cpumask) - printk(" %d", cpu); - printk(" (detected by %d, t=%lu/%lu)\n", - smp_processor_id(), get_seconds(), rcp->gp_check); -} - -static void print_cpu_stall(struct rcu_ctrlblk *rcp) -{ - unsigned long flags; - - printk(KERN_ERR "RCU detected CPU %d stall (t=%lu/%lu)\n", - smp_processor_id(), get_seconds(), rcp->gp_check); - dump_stack(); - spin_lock_irqsave(&rcp->lock, flags); - if ((long)(get_seconds() - rcp->gp_check) >= 0L) - rcp->gp_check = get_seconds() + 30; - spin_unlock_irqrestore(&rcp->lock, flags); -} - -static void check_cpu_stall(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) -{ - long delta; - - delta = get_seconds() - rcp->gp_check; - if (cpu_isset(smp_processor_id(), rcp->cpumask) && delta >= 0L) { - - /* We haven't checked in, so go dump stack. */ - - print_cpu_stall(rcp); - - } else { - if (!cpus_empty(rcp->cpumask) && delta >= 2L) { - /* They had two seconds to dump stack, so complain. */ - print_other_cpu_stall(rcp); - } - } -} - -#else /* #ifdef CONFIG_DEBUG_RCU_STALL */ - -static inline void record_gp_check_time(struct rcu_ctrlblk *rcp) -{ -} - -static inline void -check_cpu_stall(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) -{ -} - -#endif /* #else #ifdef CONFIG_DEBUG_RCU_STALL */ - /* * Register a new batch of callbacks, and start it up if there is currently no * active batch and the batch to be registered has not already occurred. @@ -381,7 +384,7 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) if (rcp->cur != rcp->pending && rcp->completed == rcp->cur) { rcp->cur++; - record_gp_check_time(rcp); + record_gp_stall_check_time(rcp); /* * Accessing nohz_cpu_mask before incrementing rcp->cur needs a @@ -603,7 +606,7 @@ static void rcu_process_callbacks(struct softirq_action *unused) static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) { /* Check for CPU stalls, if enabled. */ - check_cpu_stall(rcp, rdp); + check_cpu_stall(rcp); if (rdp->nxtlist) { long completed_snap = ACCESS_ONCE(rcp->completed); @@ -769,6 +772,9 @@ static struct notifier_block __cpuinitdata rcu_nb = { */ void __init __rcu_init(void) { +#ifdef CONFIG_RCU_CPU_STALL_DETECTOR + printk(KERN_INFO "RCU-based detection of stalled CPUs is enabled.\n"); +#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */ rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE, (void *)(long)smp_processor_id()); /* Register notifier for non-boot CPUs */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ccede1aeab38..9fee969dd60e 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -597,7 +597,7 @@ config RCU_TORTURE_TEST_RUNNABLE Say N here if you want the RCU torture tests to start only after being manually enabled via /proc. -config RCU_CPU_STALL +config RCU_CPU_STALL_DETECTOR bool "Check for stalled CPUs delaying RCU grace periods" depends on CLASSIC_RCU default n -- cgit v1.2.3 From 3c9f3681d0b4af09c1cbf04f92fdfb72bd81ad7b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 31 Aug 2008 10:13:54 -0500 Subject: [SCSI] lib: add generic helper to print sizes rounded to the correct SI range This patch adds the ability to print sizes in either units of 10^3 (SI) or 2^10 (Binary) units. It rounds up to three significant figures and can be used for either memory or storage capacities. Oh, and I'm fully aware that 64 bits is only 16EiB ... the Zetta and Yotta units are added for future proofing against the day we have 128 bit computers ... [fujita.tomonori@lab.ntt.co.jp: fix missed unsigned long long cast] Signed-off-by: James Bottomley --- include/linux/string_helpers.h | 16 +++++++++++ lib/Makefile | 3 +- lib/string_helpers.c | 64 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 include/linux/string_helpers.h create mode 100644 lib/string_helpers.c (limited to 'include/linux') diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h new file mode 100644 index 000000000000..a3eb2f65b656 --- /dev/null +++ b/include/linux/string_helpers.h @@ -0,0 +1,16 @@ +#ifndef _LINUX_STRING_HELPERS_H_ +#define _LINUX_STRING_HELPERS_H_ + +#include + +/* Descriptions of the types of units to + * print in */ +enum string_size_units { + STRING_UNITS_10, /* use powers of 10^3 (standard SI) */ + STRING_UNITS_2, /* use binary powers of 2^10 */ +}; + +int string_get_size(u64 size, enum string_size_units units, + char *buf, int len); + +#endif diff --git a/lib/Makefile b/lib/Makefile index 3b1f94bbe9de..44001af76a7d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,7 +19,8 @@ lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o kref.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ - bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o + bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ + string_helpers.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/string_helpers.c b/lib/string_helpers.c new file mode 100644 index 000000000000..8347925030ff --- /dev/null +++ b/lib/string_helpers.c @@ -0,0 +1,64 @@ +/* + * Helpers for formatting and printing strings + * + * Copyright 31 August 2008 James Bottomley + */ +#include +#include +#include +#include + +/** + * string_get_size - get the size in the specified units + * @size: The size to be converted + * @units: units to use (powers of 1000 or 1024) + * @buf: buffer to format to + * @len: length of buffer + * + * This function returns a string formatted to 3 significant figures + * giving the size in the required units. Returns 0 on success or + * error on failure. @buf is always zero terminated. + * + */ +int string_get_size(u64 size, const enum string_size_units units, + char *buf, int len) +{ + const char *units_10[] = { "B", "KB", "MB", "GB", "TB", "PB", + "EB", "ZB", "YB", NULL}; + const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", + "EiB", "ZiB", "YiB", NULL }; + const char **units_str[] = { + [STRING_UNITS_10] = units_10, + [STRING_UNITS_2] = units_2, + }; + const int divisor[] = { + [STRING_UNITS_10] = 1000, + [STRING_UNITS_2] = 1024, + }; + int i, j; + u64 remainder = 0, sf_cap; + char tmp[8]; + + tmp[0] = '\0'; + + for (i = 0; size > divisor[units] && units_str[units][i]; i++) + remainder = do_div(size, divisor[units]); + + sf_cap = size; + for (j = 0; sf_cap*10 < 1000; j++) + sf_cap *= 10; + + if (j) { + remainder *= 1000; + do_div(remainder, divisor[units]); + snprintf(tmp, sizeof(tmp), ".%03lld", + (unsigned long long)remainder); + tmp[j+1] = '\0'; + } + + snprintf(buf, len, "%lld%s%s", (unsigned long long)size, + tmp, units_str[units][i]); + + return 0; +} +EXPORT_SYMBOL(string_get_size); -- cgit v1.2.3 From af558e33bedab672f5cfd3260bce7445e353fe21 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 6 Sep 2007 12:34:25 -0400 Subject: nfsd: common grace period control Rewrite grace period code to unify management of grace period across lockd and nfsd. The current code has lockd and nfsd cooperate to compute a grace period which is satisfactory to them both, and then individually enforce it. This creates a slight race condition, since the enforcement is not coordinated. It's also more complicated than necessary. Here instead we have lockd and nfsd each inform common code when they enter the grace period, and when they're ready to leave the grace period, and allow normal locking only after both of them are ready to leave. We also expect the locks_start_grace()/locks_end_grace() interface here to be simpler to build on for future cluster/high-availability work, which may require (for example) putting individual filesystems into grace, or enforcing grace periods across multiple cluster nodes. Signed-off-by: J. Bruce Fields --- fs/lockd/Makefile | 2 +- fs/lockd/grace.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ fs/lockd/svc.c | 20 ++++------------ fs/lockd/svc4proc.c | 12 +++++----- fs/lockd/svcproc.c | 12 +++++----- fs/nfsd/lockd.c | 1 - fs/nfsd/nfs4proc.c | 8 +++---- fs/nfsd/nfs4state.c | 34 ++++++++++++-------------- include/linux/fs.h | 8 +++++++ include/linux/lockd/bind.h | 9 ------- 10 files changed, 104 insertions(+), 61 deletions(-) create mode 100644 fs/lockd/grace.c (limited to 'include/linux') diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile index 7725a0a9a555..97f6073ab339 100644 --- a/fs/lockd/Makefile +++ b/fs/lockd/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_LOCKD) += lockd.o lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ - svcproc.o svcsubs.o mon.o xdr.o + svcproc.o svcsubs.o mon.o xdr.o grace.o lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o lockd-objs := $(lockd-objs-y) diff --git a/fs/lockd/grace.c b/fs/lockd/grace.c new file mode 100644 index 000000000000..183cc1f0af1c --- /dev/null +++ b/fs/lockd/grace.c @@ -0,0 +1,59 @@ +/* + * Common code for control of lockd and nfsv4 grace periods. + */ + +#include +#include + +static LIST_HEAD(grace_list); +static DEFINE_SPINLOCK(grace_lock); + +/** + * locks_start_grace + * @lm: who this grace period is for + * + * A grace period is a period during which locks should not be given + * out. Currently grace periods are only enforced by the two lock + * managers (lockd and nfsd), using the locks_in_grace() function to + * check when they are in a grace period. + * + * This function is called to start a grace period. + */ +void locks_start_grace(struct lock_manager *lm) +{ + spin_lock(&grace_lock); + list_add(&lm->list, &grace_list); + spin_unlock(&grace_lock); +} +EXPORT_SYMBOL_GPL(locks_start_grace); + +/** + * locks_end_grace + * @lm: who this grace period is for + * + * Call this function to state that the given lock manager is ready to + * resume regular locking. The grace period will not end until all lock + * managers that called locks_start_grace() also call locks_end_grace(). + * Note that callers count on it being safe to call this more than once, + * and the second call should be a no-op. + */ +void locks_end_grace(struct lock_manager *lm) +{ + spin_lock(&grace_lock); + list_del_init(&lm->list); + spin_unlock(&grace_lock); +} +EXPORT_SYMBOL_GPL(locks_end_grace); + +/** + * locks_in_grace + * + * Lock managers call this function to determine when it is OK for them + * to answer ordinary lock requests, and when they should accept only + * lock reclaims. + */ +int locks_in_grace(void) +{ + return !list_empty(&grace_list); +} +EXPORT_SYMBOL_GPL(locks_in_grace); diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index f345ef7fb8ae..f013aed11533 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -51,7 +51,6 @@ static DEFINE_MUTEX(nlmsvc_mutex); static unsigned int nlmsvc_users; static struct task_struct *nlmsvc_task; static struct svc_rqst *nlmsvc_rqst; -int nlmsvc_grace_period; unsigned long nlmsvc_timeout; /* @@ -85,30 +84,21 @@ static unsigned long get_lockd_grace_period(void) return nlm_timeout * 5 * HZ; } -unsigned long get_nfs_grace_period(void) -{ - unsigned long lockdgrace = get_lockd_grace_period(); - unsigned long nfsdgrace = 0; - - if (nlmsvc_ops) - nfsdgrace = nlmsvc_ops->get_grace_period(); - - return max(lockdgrace, nfsdgrace); -} -EXPORT_SYMBOL(get_nfs_grace_period); +static struct lock_manager lockd_manager = { +}; static void grace_ender(struct work_struct *not_used) { - nlmsvc_grace_period = 0; + locks_end_grace(&lockd_manager); } static DECLARE_DELAYED_WORK(grace_period_end, grace_ender); static void set_grace_period(void) { - unsigned long grace_period = get_nfs_grace_period() + jiffies; + unsigned long grace_period = get_lockd_grace_period(); - nlmsvc_grace_period = 1; + locks_start_grace(&lockd_manager); cancel_delayed_work_sync(&grace_period_end); schedule_delayed_work(&grace_period_end, grace_period); } diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 4a714f64515b..7ca617367b3e 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -89,7 +89,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept test requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rc; } @@ -123,7 +123,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { + if (locks_in_grace() && !argp->reclaim) { resp->status = nlm_lck_denied_grace_period; return rc; } @@ -169,7 +169,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -202,7 +202,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -341,7 +341,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { + if (locks_in_grace() && !argp->reclaim) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -374,7 +374,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 76262c1986f2..1b013e198804 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -118,7 +118,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept test requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rc; } @@ -153,7 +153,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { + if (locks_in_grace() && !argp->reclaim) { resp->status = nlm_lck_denied_grace_period; return rc; } @@ -199,7 +199,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -232,7 +232,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -373,7 +373,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { + if (locks_in_grace() && !argp->reclaim) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -406,7 +406,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 15c6faeec77c..b2786a5f9afe 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -70,7 +70,6 @@ nlm_fclose(struct file *filp) static struct nlmsvc_binding nfsd_nlm_ops = { .fopen = nlm_fopen, /* open file for locking */ .fclose = nlm_fclose, /* close file */ - .get_grace_period = get_nfs4_grace_period, }; void diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index e5b51ffafc6c..669461e291ae 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -201,10 +201,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* Openowner is now set, so sequence id will get bumped. Now we need * these checks before we do any creates: */ status = nfserr_grace; - if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) + if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) goto out; status = nfserr_no_grace; - if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) + if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) goto out; switch (open->op_claim_type) { @@ -575,7 +575,7 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; - if (nfs4_in_grace()) + if (locks_in_grace()) return nfserr_grace; status = nfsd_unlink(rqstp, &cstate->current_fh, 0, remove->rm_name, remove->rm_namelen); @@ -596,7 +596,7 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!cstate->save_fh.fh_dentry) return status; - if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags + if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags & NFSEXP_NOSUBTREECHECK)) return nfserr_grace; status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1578d7a2667e..0cc7ff5d5ab5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -61,7 +61,6 @@ static time_t lease_time = 90; /* default lease time */ static time_t user_lease_time = 90; static time_t boot_time; -static int in_grace = 1; static u32 current_ownerid = 1; static u32 current_fileid = 1; static u32 current_delegid = 1; @@ -1640,7 +1639,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta case NFS4_OPEN_CLAIM_NULL: /* Let's not give out any delegations till everyone's * had the chance to reclaim theirs.... */ - if (nfs4_in_grace()) + if (locks_in_grace()) goto out; if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) goto out; @@ -1816,12 +1815,15 @@ out: return status; } +struct lock_manager nfsd4_manager = { +}; + static void -end_grace(void) +nfsd4_end_grace(void) { dprintk("NFSD: end of grace period\n"); nfsd4_recdir_purge_old(); - in_grace = 0; + locks_end_grace(&nfsd4_manager); } static time_t @@ -1838,8 +1840,8 @@ nfs4_laundromat(void) nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); - if (in_grace) - end_grace(); + if (locks_in_grace()) + nfsd4_end_grace(); list_for_each_safe(pos, next, &client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { @@ -1974,7 +1976,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) return nfserr_bad_stateid; else if (ONE_STATEID(stateid) && (flags & RD_STATE)) return nfs_ok; - else if (nfs4_in_grace()) { + else if (locks_in_grace()) { /* Answer in remaining cases depends on existance of * conflicting state; so we must wait out the grace period. */ return nfserr_grace; @@ -1993,7 +1995,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) static inline int io_during_grace_disallowed(struct inode *inode, int flags) { - return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE)) + return locks_in_grace() && (flags & (RD_STATE | WR_STATE)) && mandatory_lock(inode); } @@ -2693,10 +2695,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, filp = lock_stp->st_vfs_file; status = nfserr_grace; - if (nfs4_in_grace() && !lock->lk_reclaim) + if (locks_in_grace() && !lock->lk_reclaim) goto out; status = nfserr_no_grace; - if (!nfs4_in_grace() && lock->lk_reclaim) + if (!locks_in_grace() && lock->lk_reclaim) goto out; locks_init_lock(&file_lock); @@ -2779,7 +2781,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, int error; __be32 status; - if (nfs4_in_grace()) + if (locks_in_grace()) return nfserr_grace; if (check_lock_length(lockt->lt_offset, lockt->lt_length)) @@ -3192,9 +3194,9 @@ __nfs4_state_start(void) unsigned long grace_time; boot_time = get_seconds(); - grace_time = get_nfs_grace_period(); + grace_time = get_nfs4_grace_period(); lease_time = user_lease_time; - in_grace = 1; + locks_start_grace(&nfsd4_manager); printk(KERN_INFO "NFSD: starting %ld-second grace period\n", grace_time/HZ); laundry_wq = create_singlethread_workqueue("nfsd4"); @@ -3213,12 +3215,6 @@ nfs4_state_start(void) return; } -int -nfs4_in_grace(void) -{ - return in_grace; -} - time_t nfs4_lease_time(void) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 9f540165a078..27cfa723b92a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -942,6 +942,14 @@ struct lock_manager_operations { int (*fl_change)(struct file_lock **, int); }; +struct lock_manager { + struct list_head list; +}; + +void locks_start_grace(struct lock_manager *); +void locks_end_grace(struct lock_manager *); +int locks_in_grace(void); + /* that will die - we need it for nfs_lock_info */ #include diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index 3d25bcd139d1..1f0465c374dc 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -27,7 +27,6 @@ struct nlmsvc_binding { struct nfs_fh *, struct file **); void (*fclose)(struct file *); - unsigned long (*get_grace_period)(void); }; extern struct nlmsvc_binding * nlmsvc_ops; @@ -56,12 +55,4 @@ extern int nlmclnt_proc(struct nlm_host *host, int cmd, extern int lockd_up(int proto); extern void lockd_down(void); -unsigned long get_nfs_grace_period(void); - -#ifdef CONFIG_NFSD_V4 -unsigned long get_nfs4_grace_period(void); -#else -static inline unsigned long get_nfs4_grace_period(void) {return 0;} -#endif - #endif /* LINUX_LOCKD_BIND_H */ -- cgit v1.2.3 From b2b5028905226f85075a408b1118857c9aa48bb3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 6 Feb 2008 13:59:23 -0500 Subject: lockd: move grace period checks to common code Do all the grace period checks in svclock.c. This simplifies the code a bit, and will ease some later changes. Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 15 ++------------- fs/lockd/svclock.c | 14 +++++++++++++- fs/lockd/svcproc.c | 15 ++------------- include/linux/lockd/lockd.h | 2 +- 4 files changed, 18 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 7ca617367b3e..f6f18fa5cf8b 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -88,12 +88,6 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, dprintk("lockd: TEST4 called\n"); resp->cookie = argp->cookie; - /* Don't accept test requests during grace period */ - if (locks_in_grace()) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -122,12 +116,6 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; - /* Don't accept new lock requests during grace period */ - if (locks_in_grace() && !argp->reclaim) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -146,7 +134,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now try to lock the file */ resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, - argp->block, &argp->cookie); + argp->block, &argp->cookie, + argp->reclaim); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index cf0d5c2c318d..808d246ada4d 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -360,7 +360,7 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) __be32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_host *host, struct nlm_lock *lock, int wait, - struct nlm_cookie *cookie) + struct nlm_cookie *cookie, int reclaim) { struct nlm_block *block = NULL; int error; @@ -406,6 +406,11 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } + if (locks_in_grace() && !reclaim) { + ret = nlm_lck_denied_grace_period; + goto out; + } + if (!wait) lock->fl.fl_flags &= ~FL_SLEEP; error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); @@ -502,6 +507,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } + if (locks_in_grace()) { + ret = nlm_lck_denied_grace_period; + goto out; + } error = vfs_test_lock(file->f_file, &lock->fl); if (error == FILE_LOCK_DEFERRED) { ret = nlmsvc_defer_lock_rqst(rqstp, block); @@ -582,6 +591,9 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); + if (locks_in_grace()) + return nlm_lck_denied_grace_period; + mutex_lock(&file->f_mutex); block = nlmsvc_lookup_block(file, lock); mutex_unlock(&file->f_mutex); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 1b013e198804..a587b81338b1 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -117,12 +117,6 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, dprintk("lockd: TEST called\n"); resp->cookie = argp->cookie; - /* Don't accept test requests during grace period */ - if (locks_in_grace()) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -152,12 +146,6 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; - /* Don't accept new lock requests during grace period */ - if (locks_in_grace() && !argp->reclaim) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -176,7 +164,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now try to lock the file */ resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, - argp->block, &argp->cookie)); + argp->block, &argp->cookie, + argp->reclaim)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index ec8af115843d..973ab1d6e862 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -242,7 +242,7 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); */ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, int, - struct nlm_cookie *); + struct nlm_cookie *, int); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, -- cgit v1.2.3 From 0d3ebb9ae9f9c887518fd4c81a68084111d154d7 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Tue, 30 Sep 2008 13:06:13 -0500 Subject: svcrdma: Add Fast Reg MR Data Types Add data types to track Fast Reg Memory Regions. The core data type is svc_rdma_fastreg_mr that associates a device MR with a host kva and page list. A field is added to the WR context to keep track of the FRMR used to map the local memory for an RPC. An FRMR list and spin lock are added to the transport instance to keep track of all FRMR allocated for the transport. Also added are device capability flags to indicate what the memory registration capabilities are for the underlying device and whether or not fast memory registration is supported. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index dc05b54bd3a3..49e458d98945 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -72,6 +72,7 @@ extern atomic_t rdma_stat_sq_prod; */ struct svc_rdma_op_ctxt { struct svc_rdma_op_ctxt *read_hdr; + struct svc_rdma_fastreg_mr *frmr; int hdr_count; struct xdr_buf arg; struct list_head dto_q; @@ -103,16 +104,30 @@ struct svc_rdma_chunk_sge { int start; /* sge no for this chunk */ int count; /* sge count for this chunk */ }; +struct svc_rdma_fastreg_mr { + struct ib_mr *mr; + void *kva; + struct ib_fast_reg_page_list *page_list; + int page_list_len; + unsigned long access_flags; + unsigned long map_len; + enum dma_data_direction direction; + struct list_head frmr_list; +}; struct svc_rdma_req_map { + struct svc_rdma_fastreg_mr *frmr; unsigned long count; union { struct kvec sge[RPCSVC_MAXPAGES]; struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES]; }; }; - +#define RDMACTXT_F_FAST_UNREG 1 #define RDMACTXT_F_LAST_CTXT 2 +#define SVCRDMA_DEVCAP_FAST_REG 1 /* fast mr registration */ +#define SVCRDMA_DEVCAP_READ_W_INV 2 /* read w/ invalidate */ + struct svcxprt_rdma { struct svc_xprt sc_xprt; /* SVC transport structure */ struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ @@ -136,6 +151,11 @@ struct svcxprt_rdma { struct ib_cq *sc_rq_cq; struct ib_cq *sc_sq_cq; struct ib_mr *sc_phys_mr; /* MR for server memory */ + u32 sc_dev_caps; /* distilled device caps */ + u32 sc_dma_lkey; /* local dma key */ + unsigned int sc_frmr_pg_list_len; + struct list_head sc_frmr_q; + spinlock_t sc_frmr_q_lock; spinlock_t sc_lock; /* transport lock */ -- cgit v1.2.3 From d7d204403b31beb83b1aefef7bd76f5209369555 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 12:50:21 -0400 Subject: lockd: Adjust nlmclnt_lookup_host() signature to accomodate non-AF_INET Pass a struct sockaddr * and a length to nlmclnt_lookup_host() to accomodate non-AF_INET family addresses. As a side benefit, eliminate the hostname_len argument, as the hostname is always NUL-terminated. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 5 ++--- fs/lockd/host.c | 32 +++++++++++++++++++++----------- include/linux/lockd/lockd.h | 9 +++++---- 3 files changed, 28 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 237224a3c420..9eaf306d15fa 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -58,10 +58,9 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) if (status < 0) return ERR_PTR(status); - host = nlmclnt_lookup_host((struct sockaddr_in *)nlm_init->address, + host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, nlm_init->protocol, nlm_version, - nlm_init->hostname, - strlen(nlm_init->hostname)); + nlm_init->hostname); if (host == NULL) { lockd_down(); return ERR_PTR(-ENOLCK); diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 5876b0e4c0be..cbd2398e594c 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -264,32 +264,42 @@ nlm_destroy_host(struct nlm_host *host) kfree(host); } -/* - * Find an NLM server handle in the cache. If there is none, create it. +/** + * nlmclnt_lookup_host - Find an NLM host handle matching a remote server + * @sap: network address of server + * @salen: length of server address + * @protocol: transport protocol to use + * @version: NLM protocol version + * @hostname: '\0'-terminated hostname of server + * + * Returns an nlm_host structure that matches the passed-in + * [server address, transport protocol, NLM version, server hostname]. + * If one doesn't already exist in the host cache, a new handle is + * created and returned. */ -struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin, - int proto, u32 version, - const char *hostname, - unsigned int hostname_len) +struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, + const size_t salen, + const unsigned short protocol, + const u32 version, const char *hostname) { const struct sockaddr source = { .sa_family = AF_UNSPEC, }; struct nlm_lookup_host_info ni = { .server = 0, - .sap = (struct sockaddr *)sin, - .salen = sizeof(*sin), - .protocol = proto, + .sap = sap, + .salen = salen, + .protocol = protocol, .version = version, .hostname = hostname, - .hostname_len = hostname_len, + .hostname_len = strlen(hostname), .src_sap = &source, .src_len = sizeof(source), }; dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__, (hostname ? hostname : ""), version, - (proto == IPPROTO_UDP ? "udp" : "tcp")); + (protocol == IPPROTO_UDP ? "udp" : "tcp")); return nlm_lookup_host(&ni); } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 973ab1d6e862..90a996d2f005 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -215,10 +215,11 @@ void nlmclnt_next_cookie(struct nlm_cookie *); /* * Host cache */ -struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin, - int proto, u32 version, - const char *hostname, - unsigned int hostname_len); +struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, + const size_t salen, + const unsigned short protocol, + const u32 version, + const char *hostname); struct nlm_host *nlmsvc_lookup_host(struct svc_rqst *, const char *, unsigned int); struct rpc_clnt * nlm_bind_host(struct nlm_host *); -- cgit v1.2.3 From 6bfbe8af4674458e6d88aef8f0136bd1b8855b11 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 12:50:29 -0400 Subject: lockd: Adjust nlmsvc_lookup_host() to accomodate AF_INET6 addresses Fix up nlmsvc_lookup_host() to pass AF_INET6 source addresses to nlm_lookup_host(). Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/host.c | 47 ++++++++++++++++++++++++++++++++++++--------- include/linux/lockd/lockd.h | 5 +++-- 2 files changed, 41 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/host.c b/fs/lockd/host.c index cbd2398e594c..9fd8889097b7 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -304,16 +304,33 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, return nlm_lookup_host(&ni); } -/* - * Find an NLM client handle in the cache. If there is none, create it. +/** + * nlmsvc_lookup_host - Find an NLM host handle matching a remote client + * @rqstp: incoming NLM request + * @hostname: name of client host + * @hostname_len: length of client hostname + * + * Returns an nlm_host structure that matches the [client address, + * transport protocol, NLM version, client hostname] of the passed-in + * NLM request. If one doesn't already exist in the host cache, a + * new handle is created and returned. + * + * Before possibly creating a new nlm_host, construct a sockaddr + * for a specific source address in case the local system has + * multiple network addresses. The family of the address in + * rq_daddr is guaranteed to be the same as the family of the + * address in rq_addr, so it's safe to use the same family for + * the source address. */ -struct nlm_host * -nlmsvc_lookup_host(struct svc_rqst *rqstp, - const char *hostname, unsigned int hostname_len) +struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, + const char *hostname, + const size_t hostname_len) { - const struct sockaddr_in source = { + struct sockaddr_in sin = { .sin_family = AF_INET, - .sin_addr = rqstp->rq_daddr.addr, + }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, }; struct nlm_lookup_host_info ni = { .server = 1, @@ -323,14 +340,26 @@ nlmsvc_lookup_host(struct svc_rqst *rqstp, .version = rqstp->rq_vers, .hostname = hostname, .hostname_len = hostname_len, - .src_sap = (struct sockaddr *)&source, - .src_len = sizeof(source), + .src_len = rqstp->rq_addrlen, }; dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__, (int)hostname_len, hostname, rqstp->rq_vers, (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp")); + switch (ni.sap->sa_family) { + case AF_INET: + sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr; + ni.src_sap = (struct sockaddr *)&sin; + break; + case AF_INET6: + ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6); + ni.src_sap = (struct sockaddr *)&sin6; + break; + default: + return NULL; + } + return nlm_lookup_host(&ni); } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 90a996d2f005..16ff2e88f05d 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -220,8 +220,9 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, const unsigned short protocol, const u32 version, const char *hostname); -struct nlm_host *nlmsvc_lookup_host(struct svc_rqst *, const char *, - unsigned int); +struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, + const char *hostname, + const size_t hostname_len); struct rpc_clnt * nlm_bind_host(struct nlm_host *); void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *); -- cgit v1.2.3 From dcff09f124f71d1d4fe61eb63c79e52f488ac22e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 12:50:36 -0400 Subject: lockd: change nlmclnt_grant() to take a "struct sockaddr *" Adjust the signature and callers of nlmclnt_grant() to pass a "struct sockaddr *" instead of a "struct sockaddr_in *" in order to support IPv6 addresses. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 5 ++--- fs/lockd/svc4proc.c | 2 +- fs/lockd/svcproc.c | 2 +- include/linux/lockd/lockd.h | 3 ++- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 9eaf306d15fa..2976bf0f4147 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -141,7 +141,7 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout) /* * The server lockd has called us back to tell us the lock was granted */ -__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) +__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) { const struct file_lock *fl = &lock->fl; const struct nfs_fh *fh = &lock->fh; @@ -165,8 +165,7 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock */ if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) continue; - if (!nlm_cmp_addr(nlm_addr(block->b_host), - (struct sockaddr *)addr)) + if (!nlm_cmp_addr(nlm_addr(block->b_host), addr)) continue; if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0) continue; diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index f6f18fa5cf8b..50ee8eb139ab 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -220,7 +220,7 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; dprintk("lockd: GRANTED called\n"); - resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock); + resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); return rpc_success; } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index a587b81338b1..935ce967a6a1 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -250,7 +250,7 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; dprintk("lockd: GRANTED called\n"); - resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock); + resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); return rpc_success; } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 16ff2e88f05d..e6b070979287 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -207,7 +207,8 @@ int nlm_async_reply(struct nlm_rqst *, u32, const struct rpc_call_ops *); struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl); void nlmclnt_finish_block(struct nlm_wait *block); int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); -__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *); +__be32 nlmclnt_grant(const struct sockaddr *addr, + const struct nlm_lock *lock); void nlmclnt_recovery(struct nlm_host *); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); void nlmclnt_next_cookie(struct nlm_cookie *); -- cgit v1.2.3 From b85e4676344fc4d7ec5e0f62c3d3712e48bbe223 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 12:50:44 -0400 Subject: lockd: Add helper to sanity check incoming NOTIFY requests lockd accepts SM_NOTIFY calls only from a privileged process on the local system. If lockd uses an AF_INET6 listener, the sender's address (ie the local rpc.statd) will be the IPv6 loopback address, not the IPv4 loopback address. Make sure the privilege test in nlmsvc_proc_sm_notify() and nlm4svc_proc_sm_notify() works for both AF_INET and AF_INET6 family addresses by refactoring the test into a helper and adding support for IPv6 addresses. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 6 ++---- fs/lockd/svcproc.c | 6 ++---- include/linux/lockd/lockd.h | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 50ee8eb139ab..014f6ce48172 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -421,11 +421,9 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, { struct sockaddr_in saddr; - memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr)); - dprintk("lockd: SM_NOTIFY called\n"); - if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) - || ntohs(saddr.sin_port) >= 1024) { + + if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", svc_print_addr(rqstp, buf, sizeof(buf))); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 935ce967a6a1..548b0bb2b84d 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -453,11 +453,9 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, { struct sockaddr_in saddr; - memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr)); - dprintk("lockd: SM_NOTIFY called\n"); - if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) - || ntohs(saddr.sin_port) >= 1024) { + + if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", svc_print_addr(rqstp, buf, sizeof(buf))); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index e6b070979287..b56d5aa9b194 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -277,6 +277,47 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) return file->f_file->f_path.dentry->d_inode; } +static inline int __nlm_privileged_request4(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) && + (ntohs(sin->sin_port) < 1024); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static inline int __nlm_privileged_request6(const struct sockaddr *sap) +{ + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) && + (ntohs(sin6->sin6_port) < 1024); +} +#else /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +static inline int __nlm_privileged_request6(const struct sockaddr *sap) +{ + return 0; +} +#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + +/* + * Ensure incoming requests are from local privileged callers. + * + * Return TRUE if sender is local and is connecting via a privileged port; + * otherwise return FALSE. + */ +static inline int nlm_privileged_requester(const struct svc_rqst *rqstp) +{ + const struct sockaddr *sap = svc_addr(rqstp); + + switch (sap->sa_family) { + case AF_INET: + return __nlm_privileged_request4(sap); + case AF_INET6: + return __nlm_privileged_request6(sap); + default: + return 0; + } +} + static inline int __nlm_cmp_addr4(const struct sockaddr *sap1, const struct sockaddr *sap2) { -- cgit v1.2.3 From 9a38a83880c224c6a3fd973ac9ae30a043487f0f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 12:50:51 -0400 Subject: lockd: Remove unused fields in the nlm_reboot structure The nlm_reboot structure is used to store information provided by the NSM_NOTIFY procedure. This procedure is not specified by the NLM or NSM protocols, other than to say that the procedure can be used to transmit information private to a particular NLM/NSM implementation. For Linux, the callback arguments include the name of the monitored host, the new NSM state of the host, and a 16-byte private opaque. As a clean up, remove the unused fields and the server-side XDR logic that decodes them. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/xdr.c | 2 -- fs/lockd/xdr4.c | 2 -- include/linux/lockd/xdr.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 3e459e18cc31..1f226290c67c 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -351,8 +351,6 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp) argp->state = ntohl(*p++); /* Preserve the address in network byte order */ argp->addr = *p++; - argp->vers = *p++; - argp->proto = *p++; return xdr_argsize_check(rqstp, p); } diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 43ff9397e6c6..50c493a8ad8e 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -358,8 +358,6 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp argp->state = ntohl(*p++); /* Preserve the address in network byte order */ argp->addr = *p++; - argp->vers = *p++; - argp->proto = *p++; return xdr_argsize_check(rqstp, p); } diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index df18fa053bcd..d6b3a802c046 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -81,8 +81,6 @@ struct nlm_reboot { unsigned int len; u32 state; __be32 addr; - __be32 vers; - __be32 proto; }; /* -- cgit v1.2.3 From c4b929b85bdb64afacbbf6453b1f2bf7e14c9e89 Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Wed, 8 Oct 2008 19:44:18 -0400 Subject: vfs: vfs-level fiemap interface Basic vfs-level fiemap infrastructure, which sets up a new ->fiemap inode operation. Userspace can get extent information on a file via fiemap ioctl. As input, the fiemap ioctl takes a struct fiemap which includes an array of struct fiemap_extent (fm_extents). Size of the extent array is passed as fm_extent_count and number of extents returned will be written into fm_mapped_extents. Offset and length fields on the fiemap structure (fm_start, fm_length) describe a logical range which will be searched for extents. All extents returned will at least partially contain this range. The actual extent offsets and ranges returned will be unmodified from their offset and range on-disk. The fiemap ioctl returns '0' on success. On error, -1 is returned and errno is set. If errno is equal to EBADR, then fm_flags will contain those flags which were passed in which the kernel did not understand. On all other errors, the contents of fm_extents is undefined. As fiemap evolved, there have been many authors of the vfs patch. As far as I can tell, the list includes: Kalpak Shah Andreas Dilger Eric Sandeen Mark Fasheh Signed-off-by: Mark Fasheh Signed-off-by: "Theodore Ts'o" Cc: Michael Kerrisk Cc: linux-api@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org --- Documentation/filesystems/fiemap.txt | 228 +++++++++++++++++++++++++++++++++++ fs/ioctl.c | 155 ++++++++++++++++++++++++ include/linux/fiemap.h | 64 ++++++++++ include/linux/fs.h | 18 +++ 4 files changed, 465 insertions(+) create mode 100644 Documentation/filesystems/fiemap.txt create mode 100644 include/linux/fiemap.h (limited to 'include/linux') diff --git a/Documentation/filesystems/fiemap.txt b/Documentation/filesystems/fiemap.txt new file mode 100644 index 000000000000..1e3defcfe50b --- /dev/null +++ b/Documentation/filesystems/fiemap.txt @@ -0,0 +1,228 @@ +============ +Fiemap Ioctl +============ + +The fiemap ioctl is an efficient method for userspace to get file +extent mappings. Instead of block-by-block mapping (such as bmap), fiemap +returns a list of extents. + + +Request Basics +-------------- + +A fiemap request is encoded within struct fiemap: + +struct fiemap { + __u64 fm_start; /* logical offset (inclusive) at + * which to start mapping (in) */ + __u64 fm_length; /* logical length of mapping which + * userspace cares about (in) */ + __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ + __u32 fm_mapped_extents; /* number of extents that were + * mapped (out) */ + __u32 fm_extent_count; /* size of fm_extents array (in) */ + __u32 fm_reserved; + struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ +}; + + +fm_start, and fm_length specify the logical range within the file +which the process would like mappings for. Extents returned mirror +those on disk - that is, the logical offset of the 1st returned extent +may start before fm_start, and the range covered by the last returned +extent may end after fm_length. All offsets and lengths are in bytes. + +Certain flags to modify the way in which mappings are looked up can be +set in fm_flags. If the kernel doesn't understand some particular +flags, it will return EBADR and the contents of fm_flags will contain +the set of flags which caused the error. If the kernel is compatible +with all flags passed, the contents of fm_flags will be unmodified. +It is up to userspace to determine whether rejection of a particular +flag is fatal to it's operation. This scheme is intended to allow the +fiemap interface to grow in the future but without losing +compatibility with old software. + +fm_extent_count specifies the number of elements in the fm_extents[] array +that can be used to return extents. If fm_extent_count is zero, then the +fm_extents[] array is ignored (no extents will be returned), and the +fm_mapped_extents count will hold the number of extents needed in +fm_extents[] to hold the file's current mapping. Note that there is +nothing to prevent the file from changing between calls to FIEMAP. + +The following flags can be set in fm_flags: + +* FIEMAP_FLAG_SYNC +If this flag is set, the kernel will sync the file before mapping extents. + +* FIEMAP_FLAG_XATTR +If this flag is set, the extents returned will describe the inodes +extended attribute lookup tree, instead of it's data tree. + + +Extent Mapping +-------------- + +Extent information is returned within the embedded fm_extents array +which userspace must allocate along with the fiemap structure. The +number of elements in the fiemap_extents[] array should be passed via +fm_extent_count. The number of extents mapped by kernel will be +returned via fm_mapped_extents. If the number of fiemap_extents +allocated is less than would be required to map the requested range, +the maximum number of extents that can be mapped in the fm_extent[] +array will be returned and fm_mapped_extents will be equal to +fm_extent_count. In that case, the last extent in the array will not +complete the requested range and will not have the FIEMAP_EXTENT_LAST +flag set (see the next section on extent flags). + +Each extent is described by a single fiemap_extent structure as +returned in fm_extents. + +struct fiemap_extent { + __u64 fe_logical; /* logical offset in bytes for the start of + * the extent */ + __u64 fe_physical; /* physical offset in bytes for the start + * of the extent */ + __u64 fe_length; /* length in bytes for the extent */ + __u64 fe_reserved64[2]; + __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ + __u32 fe_reserved[3]; +}; + +All offsets and lengths are in bytes and mirror those on disk. It is valid +for an extents logical offset to start before the request or it's logical +length to extend past the request. Unless FIEMAP_EXTENT_NOT_ALIGNED is +returned, fe_logical, fe_physical, and fe_length will be aligned to the +block size of the file system. With the exception of extents flagged as +FIEMAP_EXTENT_MERGED, adjacent extents will not be merged. + +The fe_flags field contains flags which describe the extent returned. +A special flag, FIEMAP_EXTENT_LAST is always set on the last extent in +the file so that the process making fiemap calls can determine when no +more extents are available, without having to call the ioctl again. + +Some flags are intentionally vague and will always be set in the +presence of other more specific flags. This way a program looking for +a general property does not have to know all existing and future flags +which imply that property. + +For example, if FIEMAP_EXTENT_DATA_INLINE or FIEMAP_EXTENT_DATA_TAIL +are set, FIEMAP_EXTENT_NOT_ALIGNED will also be set. A program looking +for inline or tail-packed data can key on the specific flag. Software +which simply cares not to try operating on non-aligned extents +however, can just key on FIEMAP_EXTENT_NOT_ALIGNED, and not have to +worry about all present and future flags which might imply unaligned +data. Note that the opposite is not true - it would be valid for +FIEMAP_EXTENT_NOT_ALIGNED to appear alone. + +* FIEMAP_EXTENT_LAST +This is the last extent in the file. A mapping attempt past this +extent will return nothing. + +* FIEMAP_EXTENT_UNKNOWN +The location of this extent is currently unknown. This may indicate +the data is stored on an inaccessible volume or that no storage has +been allocated for the file yet. + +* FIEMAP_EXTENT_DELALLOC + - This will also set FIEMAP_EXTENT_UNKNOWN. +Delayed allocation - while there is data for this extent, it's +physical location has not been allocated yet. + +* FIEMAP_EXTENT_ENCODED +This extent does not consist of plain filesystem blocks but is +encoded (e.g. encrypted or compressed). Reading the data in this +extent via I/O to the block device will have undefined results. + +Note that it is *always* undefined to try to update the data +in-place by writing to the indicated location without the +assistance of the filesystem, or to access the data using the +information returned by the FIEMAP interface while the filesystem +is mounted. In other words, user applications may only read the +extent data via I/O to the block device while the filesystem is +unmounted, and then only if the FIEMAP_EXTENT_ENCODED flag is +clear; user applications must not try reading or writing to the +filesystem via the block device under any other circumstances. + +* FIEMAP_EXTENT_DATA_ENCRYPTED + - This will also set FIEMAP_EXTENT_ENCODED +The data in this extent has been encrypted by the file system. + +* FIEMAP_EXTENT_NOT_ALIGNED +Extent offsets and length are not guaranteed to be block aligned. + +* FIEMAP_EXTENT_DATA_INLINE + This will also set FIEMAP_EXTENT_NOT_ALIGNED +Data is located within a meta data block. + +* FIEMAP_EXTENT_DATA_TAIL + This will also set FIEMAP_EXTENT_NOT_ALIGNED +Data is packed into a block with data from other files. + +* FIEMAP_EXTENT_UNWRITTEN +Unwritten extent - the extent is allocated but it's data has not been +initialized. This indicates the extent's data will be all zero if read +through the filesystem but the contents are undefined if read directly from +the device. + +* FIEMAP_EXTENT_MERGED +This will be set when a file does not support extents, i.e., it uses a block +based addressing scheme. Since returning an extent for each block back to +userspace would be highly inefficient, the kernel will try to merge most +adjacent blocks into 'extents'. + + +VFS -> File System Implementation +--------------------------------- + +File systems wishing to support fiemap must implement a ->fiemap callback on +their inode_operations structure. The fs ->fiemap call is responsible for +defining it's set of supported fiemap flags, and calling a helper function on +each discovered extent: + +struct inode_operations { + ... + + int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); + +->fiemap is passed struct fiemap_extent_info which describes the +fiemap request: + +struct fiemap_extent_info { + unsigned int fi_flags; /* Flags as passed from user */ + unsigned int fi_extents_mapped; /* Number of mapped extents */ + unsigned int fi_extents_max; /* Size of fiemap_extent array */ + struct fiemap_extent *fi_extents_start; /* Start of fiemap_extent array */ +}; + +It is intended that the file system should not need to access any of this +structure directly. + + +Flag checking should be done at the beginning of the ->fiemap callback via the +fiemap_check_flags() helper: + +int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); + +The struct fieinfo should be passed in as recieved from ioctl_fiemap(). The +set of fiemap flags which the fs understands should be passed via fs_flags. If +fiemap_check_flags finds invalid user flags, it will place the bad values in +fieinfo->fi_flags and return -EBADR. If the file system gets -EBADR, from +fiemap_check_flags(), it should immediately exit, returning that error back to +ioctl_fiemap(). + + +For each extent in the request range, the file system should call +the helper function, fiemap_fill_next_extent(): + +int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical, + u64 phys, u64 len, u32 flags, u32 dev); + +fiemap_fill_next_extent() will use the passed values to populate the +next free extent in the fm_extents array. 'General' extent flags will +automatically be set from specific flags on behalf of the calling file +system so that the userspace API is not broken. + +fiemap_fill_next_extent() returns 0 on success, and 1 when the +user-supplied fm_extents array is full. If an error is encountered +while copying the extent to user memory, -EFAULT will be returned. diff --git a/fs/ioctl.c b/fs/ioctl.c index 7db32b3382d3..045d9601fbbd 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -16,6 +16,9 @@ #include +/* So that the fiemap access checks can't overflow on 32 bit machines. */ +#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent)) + /** * vfs_ioctl - call filesystem specific ioctl methods * @filp: open file to invoke ioctl method on @@ -71,6 +74,156 @@ static int ioctl_fibmap(struct file *filp, int __user *p) return put_user(res, p); } +/** + * fiemap_fill_next_extent - Fiemap helper function + * @fieinfo: Fiemap context passed into ->fiemap + * @logical: Extent logical start offset, in bytes + * @phys: Extent physical start offset, in bytes + * @len: Extent length, in bytes + * @flags: FIEMAP_EXTENT flags that describe this extent + * + * Called from file system ->fiemap callback. Will populate extent + * info as passed in via arguments and copy to user memory. On + * success, extent count on fieinfo is incremented. + * + * Returns 0 on success, -errno on error, 1 if this was the last + * extent that will fit in user array. + */ +#define SET_UNKNOWN_FLAGS (FIEMAP_EXTENT_DELALLOC) +#define SET_NO_UNMOUNTED_IO_FLAGS (FIEMAP_EXTENT_DATA_ENCRYPTED) +#define SET_NOT_ALIGNED_FLAGS (FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE) +int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical, + u64 phys, u64 len, u32 flags) +{ + struct fiemap_extent extent; + struct fiemap_extent *dest = fieinfo->fi_extents_start; + + /* only count the extents */ + if (fieinfo->fi_extents_max == 0) { + fieinfo->fi_extents_mapped++; + return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; + } + + if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max) + return 1; + + if (flags & SET_UNKNOWN_FLAGS) + flags |= FIEMAP_EXTENT_UNKNOWN; + if (flags & SET_NO_UNMOUNTED_IO_FLAGS) + flags |= FIEMAP_EXTENT_ENCODED; + if (flags & SET_NOT_ALIGNED_FLAGS) + flags |= FIEMAP_EXTENT_NOT_ALIGNED; + + memset(&extent, 0, sizeof(extent)); + extent.fe_logical = logical; + extent.fe_physical = phys; + extent.fe_length = len; + extent.fe_flags = flags; + + dest += fieinfo->fi_extents_mapped; + if (copy_to_user(dest, &extent, sizeof(extent))) + return -EFAULT; + + fieinfo->fi_extents_mapped++; + if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max) + return 1; + return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; +} +EXPORT_SYMBOL(fiemap_fill_next_extent); + +/** + * fiemap_check_flags - check validity of requested flags for fiemap + * @fieinfo: Fiemap context passed into ->fiemap + * @fs_flags: Set of fiemap flags that the file system understands + * + * Called from file system ->fiemap callback. This will compute the + * intersection of valid fiemap flags and those that the fs supports. That + * value is then compared against the user supplied flags. In case of bad user + * flags, the invalid values will be written into the fieinfo structure, and + * -EBADR is returned, which tells ioctl_fiemap() to return those values to + * userspace. For this reason, a return code of -EBADR should be preserved. + * + * Returns 0 on success, -EBADR on bad flags. + */ +int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags) +{ + u32 incompat_flags; + + incompat_flags = fieinfo->fi_flags & ~(FIEMAP_FLAGS_COMPAT & fs_flags); + if (incompat_flags) { + fieinfo->fi_flags = incompat_flags; + return -EBADR; + } + return 0; +} +EXPORT_SYMBOL(fiemap_check_flags); + +static int fiemap_check_ranges(struct super_block *sb, + u64 start, u64 len, u64 *new_len) +{ + *new_len = len; + + if (len == 0) + return -EINVAL; + + if (start > sb->s_maxbytes) + return -EFBIG; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if ((len > sb->s_maxbytes) || + (sb->s_maxbytes - len) < start) + *new_len = sb->s_maxbytes - start; + + return 0; +} + +static int ioctl_fiemap(struct file *filp, unsigned long arg) +{ + struct fiemap fiemap; + struct fiemap_extent_info fieinfo = { 0, }; + struct inode *inode = filp->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + u64 len; + int error; + + if (!inode->i_op->fiemap) + return -EOPNOTSUPP; + + if (copy_from_user(&fiemap, (struct fiemap __user *)arg, + sizeof(struct fiemap))) + return -EFAULT; + + if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) + return -EINVAL; + + error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length, + &len); + if (error) + return error; + + fieinfo.fi_flags = fiemap.fm_flags; + fieinfo.fi_extents_max = fiemap.fm_extent_count; + fieinfo.fi_extents_start = (struct fiemap_extent *)(arg + sizeof(fiemap)); + + if (fiemap.fm_extent_count != 0 && + !access_ok(VERIFY_WRITE, fieinfo.fi_extents_start, + fieinfo.fi_extents_max * sizeof(struct fiemap_extent))) + return -EFAULT; + + if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC) + filemap_write_and_wait(inode->i_mapping); + + error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len); + fiemap.fm_flags = fieinfo.fi_flags; + fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; + if (copy_to_user((char *)arg, &fiemap, sizeof(fiemap))) + error = -EFAULT; + + return error; +} + static int file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -80,6 +233,8 @@ static int file_ioctl(struct file *filp, unsigned int cmd, switch (cmd) { case FIBMAP: return ioctl_fibmap(filp, p); + case FS_IOC_FIEMAP: + return ioctl_fiemap(filp, arg); case FIGETBSZ: return put_user(inode->i_sb->s_blocksize, p); case FIONREAD: diff --git a/include/linux/fiemap.h b/include/linux/fiemap.h new file mode 100644 index 000000000000..671decbd2aeb --- /dev/null +++ b/include/linux/fiemap.h @@ -0,0 +1,64 @@ +/* + * FS_IOC_FIEMAP ioctl infrastructure. + * + * Some portions copyright (C) 2007 Cluster File Systems, Inc + * + * Authors: Mark Fasheh + * Kalpak Shah + * Andreas Dilger + */ + +#ifndef _LINUX_FIEMAP_H +#define _LINUX_FIEMAP_H + +struct fiemap_extent { + __u64 fe_logical; /* logical offset in bytes for the start of + * the extent from the beginning of the file */ + __u64 fe_physical; /* physical offset in bytes for the start + * of the extent from the beginning of the disk */ + __u64 fe_length; /* length in bytes for this extent */ + __u64 fe_reserved64[2]; + __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ + __u32 fe_reserved[3]; +}; + +struct fiemap { + __u64 fm_start; /* logical offset (inclusive) at + * which to start mapping (in) */ + __u64 fm_length; /* logical length of mapping which + * userspace wants (in) */ + __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ + __u32 fm_mapped_extents;/* number of extents that were mapped (out) */ + __u32 fm_extent_count; /* size of fm_extents array (in) */ + __u32 fm_reserved; + struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ +}; + +#define FIEMAP_MAX_OFFSET (~0ULL) + +#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ +#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ + +#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) + +#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ +#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ +#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending. + * Sets EXTENT_UNKNOWN. */ +#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read + * while fs is unmounted */ +#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs. + * Sets EXTENT_NO_BYPASS. */ +#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be + * block aligned. */ +#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata. + * Sets EXTENT_NOT_ALIGNED.*/ +#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block. + * Sets EXTENT_NOT_ALIGNED.*/ +#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but + * no data (i.e. zero). */ +#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively + * support extents. Result + * merged for efficiency. */ + +#endif /* _LINUX_FIEMAP_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 580b513668fe..194fb237a307 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -231,6 +231,7 @@ extern int dir_notify_enable; #define FS_IOC_SETFLAGS _IOW('f', 2, long) #define FS_IOC_GETVERSION _IOR('v', 1, long) #define FS_IOC_SETVERSION _IOW('v', 2, long) +#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) #define FS_IOC32_GETFLAGS _IOR('f', 1, int) #define FS_IOC32_SETFLAGS _IOW('f', 2, int) #define FS_IOC32_GETVERSION _IOR('v', 1, int) @@ -291,6 +292,7 @@ extern int dir_notify_enable; #include #include #include +#include #include #include @@ -1178,6 +1180,20 @@ extern void dentry_unhash(struct dentry *dentry); */ extern int file_permission(struct file *, int); +/* + * VFS FS_IOC_FIEMAP helper definitions. + */ +struct fiemap_extent_info { + unsigned int fi_flags; /* Flags as passed from user */ + unsigned int fi_extents_mapped; /* Number of mapped extents */ + unsigned int fi_extents_max; /* Size of fiemap_extent array */ + struct fiemap_extent *fi_extents_start; /* Start of fiemap_extent + * array */ +}; +int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical, + u64 phys, u64 len, u32 flags); +int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); + /* * File types * @@ -1287,6 +1303,8 @@ struct inode_operations { void (*truncate_range)(struct inode *, loff_t, loff_t); long (*fallocate)(struct inode *inode, int mode, loff_t offset, loff_t len); + int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); }; struct seq_file; -- cgit v1.2.3 From 68c9d702bb72f367f3b148963ec6cf5e07ff7f65 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 3 Oct 2008 17:32:43 -0400 Subject: generic block based fiemap implementation Any block based fs (this patch includes ext3) just has to declare its own fiemap() function and then call this generic function with its own get_block_t. This works well for block based filesystems that will map multiple contiguous blocks at one time, but will work for filesystems that only map one block at a time, you will just end up with an "extent" for each block. One gotcha is this will not play nicely where there is hole+data after the EOF. This function will assume its hit the end of the data as soon as it hits a hole after the EOF, so if there is any data past that it will not pick that up. AFAIK no block based fs does this anyway, but its in the comments of the function anyway just in case. Signed-off-by: Josef Bacik Signed-off-by: Mark Fasheh Signed-off-by: "Theodore Ts'o" Cc: linux-fsdevel@vger.kernel.org --- fs/ext2/ext2.h | 2 + fs/ext2/file.c | 1 + fs/ext2/inode.c | 8 ++++ fs/ext3/file.c | 1 + fs/ext3/inode.c | 8 ++++ fs/ioctl.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/ext3_fs.h | 2 + include/linux/fs.h | 3 ++ 8 files changed, 143 insertions(+) (limited to 'include/linux') diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 47d88da2d33b..bae998c1e44e 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -133,6 +133,8 @@ extern void ext2_truncate (struct inode *); extern int ext2_setattr (struct dentry *, struct iattr *); extern void ext2_set_inode_flags(struct inode *inode); extern void ext2_get_inode_flags(struct ext2_inode_info *); +extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len); int __ext2_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata); diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 5f2fa9c36293..45ed07122182 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -86,4 +86,5 @@ const struct inode_operations ext2_file_inode_operations = { #endif .setattr = ext2_setattr, .permission = ext2_permission, + .fiemap = ext2_fiemap, }; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 991d6dfeb51f..7658b33e2653 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "ext2.h" #include "acl.h" #include "xip.h" @@ -704,6 +705,13 @@ int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_ } +int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len) +{ + return generic_block_fiemap(inode, fieinfo, start, len, + ext2_get_block); +} + static int ext2_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, ext2_get_block, wbc); diff --git a/fs/ext3/file.c b/fs/ext3/file.c index acc4913d3019..3be1e0689c9a 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -134,5 +134,6 @@ const struct inode_operations ext3_file_inode_operations = { .removexattr = generic_removexattr, #endif .permission = ext3_permission, + .fiemap = ext3_fiemap, }; diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 507d8689b111..ebfec4d0148e 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "xattr.h" #include "acl.h" @@ -981,6 +982,13 @@ out: return ret; } +int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len) +{ + return generic_block_fiemap(inode, fieinfo, start, len, + ext3_get_block); +} + /* * `handle' can be NULL if create is zero */ diff --git a/fs/ioctl.c b/fs/ioctl.c index 045d9601fbbd..33a6b7ecb8b8 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include @@ -224,6 +226,122 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg) return error; } +#define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits) +#define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits); + +/* + * @inode - the inode to map + * @arg - the pointer to userspace where we copy everything to + * @get_block - the fs's get_block function + * + * This does FIEMAP for block based inodes. Basically it will just loop + * through get_block until we hit the number of extents we want to map, or we + * go past the end of the file and hit a hole. + * + * If it is possible to have data blocks beyond a hole past @inode->i_size, then + * please do not use this function, it will stop at the first unmapped block + * beyond i_size + */ +int generic_block_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, u64 start, + u64 len, get_block_t *get_block) +{ + struct buffer_head tmp; + unsigned int start_blk; + long long length = 0, map_len = 0; + u64 logical = 0, phys = 0, size = 0; + u32 flags = FIEMAP_EXTENT_MERGED; + int ret = 0; + + if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC))) + return ret; + + start_blk = logical_to_blk(inode, start); + + /* guard against change */ + mutex_lock(&inode->i_mutex); + + length = (long long)min_t(u64, len, i_size_read(inode)); + map_len = length; + + do { + /* + * we set b_size to the total size we want so it will map as + * many contiguous blocks as possible at once + */ + memset(&tmp, 0, sizeof(struct buffer_head)); + tmp.b_size = map_len; + + ret = get_block(inode, start_blk, &tmp, 0); + if (ret) + break; + + /* HOLE */ + if (!buffer_mapped(&tmp)) { + /* + * first hole after going past the EOF, this is our + * last extent + */ + if (length <= 0) { + flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST; + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, + flags); + break; + } + + length -= blk_to_logical(inode, 1); + + /* if we have holes up to/past EOF then we're done */ + if (length <= 0) + break; + + start_blk++; + } else { + if (length <= 0 && size) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, + flags); + if (ret) + break; + } + + logical = blk_to_logical(inode, start_blk); + phys = blk_to_logical(inode, tmp.b_blocknr); + size = tmp.b_size; + flags = FIEMAP_EXTENT_MERGED; + + length -= tmp.b_size; + start_blk += logical_to_blk(inode, size); + + /* + * if we are past the EOF we need to loop again to see + * if there is a hole so we can mark this extent as the + * last one, and if not keep mapping things until we + * find a hole, or we run out of slots in the extent + * array + */ + if (length <= 0) + continue; + + ret = fiemap_fill_next_extent(fieinfo, logical, phys, + size, flags); + if (ret) + break; + } + cond_resched(); + } while (1); + + mutex_unlock(&inode->i_mutex); + + /* if ret is 1 then we just hit the end of the extent array */ + if (ret == 1) + ret = 0; + + return ret; +} +EXPORT_SYMBOL(generic_block_fiemap); + static int file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 80171ee89a22..8120fa1bc235 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -837,6 +837,8 @@ extern void ext3_truncate (struct inode *); extern void ext3_set_inode_flags(struct inode *); extern void ext3_get_inode_flags(struct ext3_inode_info *); extern void ext3_set_aops(struct inode *inode); +extern int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len); /* ioctl.c */ extern int ext3_ioctl (struct inode *, struct file *, unsigned int, diff --git a/include/linux/fs.h b/include/linux/fs.h index 194fb237a307..385c9a197df1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1998,6 +1998,9 @@ extern int vfs_fstat(unsigned int, struct kstat *); extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg); +extern int generic_block_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, u64 start, + u64 len, get_block_t *get_block); extern void get_filesystem(struct file_system_type *fs); extern void put_filesystem(struct file_system_type *fs); -- cgit v1.2.3 From 897312bd240357c88ce906633703c324c6f0a5cd Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 3 Oct 2008 15:23:41 -0700 Subject: include/linux/stacktrace.h: declare struct task_struct include/linux/stacktrace.h:13: warning: 'struct task_struct' declared inside parameter list (This might be a hard error on sparc64, which uses this header and has -Werror) Reported-by: "Randy.Dunlap" Acked-by: Ingo Molnar Cc: Peter Zijlstra Cc: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/stacktrace.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index 5da9794b2d78..b106fd8e0d5c 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h @@ -1,6 +1,8 @@ #ifndef __LINUX_STACKTRACE_H #define __LINUX_STACKTRACE_H +struct task_struct; + #ifdef CONFIG_STACKTRACE struct stack_trace { unsigned int nr_entries, max_entries; -- cgit v1.2.3 From 26a414092353590ceaa5955bcb53f863d6ea7549 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 17:15:30 -0400 Subject: NLM: Remove "proto" argument from lockd_up() Clean up: Now that lockd_up() starts listeners for both transports, the "proto" argument is no longer needed. Signed-off-by: Chuck Lever Cc: Neil Brown Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 4 ++-- fs/lockd/svc.c | 3 +-- fs/nfsd/nfsctl.c | 5 ++--- fs/nfsd/nfssvc.c | 19 +++++++------------ include/linux/lockd/bind.h | 2 +- 5 files changed, 13 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 2976bf0f4147..8307dd64bf46 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -54,7 +54,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; int status; - status = lockd_up(nlm_init->protocol); + status = lockd_up(); if (status < 0) return ERR_PTR(status); @@ -215,7 +215,7 @@ reclaimer(void *ptr) /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ lock_kernel(); - lockd_up(0); /* note: this cannot fail as lockd is already running */ + lockd_up(); /* note: this cannot fail as lockd is already running */ dprintk("lockd: reclaiming locks for host %s\n", host->h_name); diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 36396fc058c5..c631a83931ce 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -230,8 +230,7 @@ static int make_socks(struct svc_serv *serv) /* * Bring up the lockd process if it's not already up. */ -int -lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ +int lockd_up(void) { struct svc_serv *serv; int error = 0; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index c53e65f8f3a2..862dff5247f7 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -614,10 +614,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) return -EINVAL; err = nfsd_create_serv(); if (!err) { - int proto = 0; - err = svc_addsock(nfsd_serv, fd, buf, &proto); + err = svc_addsock(nfsd_serv, fd, buf, NULL); if (err >= 0) { - err = lockd_up(proto); + err = lockd_up(); if (err < 0) svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 7f3d76a7839d..59eeb46f82c5 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -244,25 +244,20 @@ static int nfsd_init_socks(int port) if (!list_empty(&nfsd_serv->sv_permsocks)) return 0; - error = lockd_up(IPPROTO_UDP); - if (error >= 0) { - error = svc_create_xprt(nfsd_serv, "udp", port, + error = svc_create_xprt(nfsd_serv, "udp", port, SVC_SOCK_DEFAULTS); - if (error < 0) - lockd_down(); - } if (error < 0) return error; - error = lockd_up(IPPROTO_TCP); - if (error >= 0) { - error = svc_create_xprt(nfsd_serv, "tcp", port, + error = svc_create_xprt(nfsd_serv, "tcp", port, SVC_SOCK_DEFAULTS); - if (error < 0) - lockd_down(); - } if (error < 0) return error; + + error = lockd_up(); + if (error < 0) + return error; + return 0; } diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index 1f0465c374dc..e5872dc994c0 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -52,7 +52,7 @@ extern void nlmclnt_done(struct nlm_host *host); extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl); -extern int lockd_up(int proto); +extern int lockd_up(void); extern void lockd_down(void); #endif /* LINUX_LOCKD_BIND_H */ -- cgit v1.2.3 From 2937391385807b3da9cd7a39345259caf550b032 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Oct 2008 17:15:38 -0400 Subject: NLM: Remove unused argument from svc_addsock() function Clean up: The svc_addsock() function no longer uses its "proto" argument, so remove it. Signed-off-by: Chuck Lever Cc: Neil Brown Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 2 +- include/linux/sunrpc/svcsock.h | 5 +---- net/sunrpc/svcsock.c | 4 +--- 3 files changed, 3 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 862dff5247f7..97543df58242 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -614,7 +614,7 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) return -EINVAL; err = nfsd_create_serv(); if (!err) { - err = svc_addsock(nfsd_serv, fd, buf, NULL); + err = svc_addsock(nfsd_serv, fd, buf); if (err >= 0) { err = lockd_up(); if (err < 0) diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 8cff696dedf5..483e10380aae 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -39,10 +39,7 @@ int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); void svc_sock_update_bufs(struct svc_serv *serv); int svc_sock_names(char *buf, struct svc_serv *serv, char *toclose); -int svc_addsock(struct svc_serv *serv, - int fd, - char *name_return, - int *proto); +int svc_addsock(struct svc_serv *serv, int fd, char *name_return); void svc_init_xprt_sock(void); void svc_cleanup_xprt_sock(void); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index f91377c14951..95293f549e9c 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1167,8 +1167,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, int svc_addsock(struct svc_serv *serv, int fd, - char *name_return, - int *proto) + char *name_return) { int err = 0; struct socket *so = sockfd_lookup(fd, &err); @@ -1203,7 +1202,6 @@ int svc_addsock(struct svc_serv *serv, sockfd_put(so); return err; } - if (proto) *proto = so->sk->sk_protocol; return one_sock_name(name_return, svsk); } EXPORT_SYMBOL_GPL(svc_addsock); -- cgit v1.2.3 From f20f258603ebc5da91e76884cf0c0d7ac9804b1c Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sun, 5 Oct 2008 18:23:27 +0200 Subject: ide-cd: temporary tray close fix This one fixes http://bugzilla.kernel.org/show_bug.cgi?id=11602. A more generic fix for drives which cannot autoclose tray will follow. Signed-off-by: Borislav Petkov Cc: Jens Axboe [bart: add an extra parentheses for consistency with the rest of kernel code] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 5 ++++- include/linux/ide.h | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 49a8c589e346..e1cd7d520fc0 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1661,7 +1661,9 @@ static int ide_cdrom_probe_capabilities(ide_drive_t *drive) cdi->mask &= ~CDC_PLAY_AUDIO; mechtype = buf[8 + 6] >> 5; - if (mechtype == mechtype_caddy || mechtype == mechtype_popup) + if (mechtype == mechtype_caddy || + mechtype == mechtype_popup || + (drive->atapi_flags & IDE_AFLAG_NO_AUTOCLOSE)) cdi->mask |= CDC_CLOSE_TRAY; if (cdi->sanyo_slot > 0) { @@ -1859,6 +1861,7 @@ static const struct cd_list_entry ide_cd_quirks_list[] = { { "MATSHITADVD-ROM SR-8176", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "MATSHITADVD-ROM SR-8174", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "Optiarc DVD RW AD-5200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "Optiarc DVD RW AD-7543A", NULL, IDE_AFLAG_NO_AUTOCLOSE }, { NULL, NULL, 0 } }; diff --git a/include/linux/ide.h b/include/linux/ide.h index 1524829f73f2..6514db8fd2e4 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -366,7 +366,9 @@ enum { /* Currently on a filemark */ IDE_AFLAG_FILEMARK = (1 << 25), /* 0 = no tape is loaded, so we don't rewind after ejecting */ - IDE_AFLAG_MEDIUM_PRESENT = (1 << 26) + IDE_AFLAG_MEDIUM_PRESENT = (1 << 26), + + IDE_AFLAG_NO_AUTOCLOSE = (1 << 27), }; struct ide_drive_s { -- cgit v1.2.3 From 9641458d3ec42def729fde64669abf07f3220cd5 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Sun, 5 Oct 2008 11:15:13 -0700 Subject: Phonet: Pipe End Point for Phonet Pipes protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This protocol provides some connection handling and negotiated congestion control. Nokia cellular modems use it for bulk transfers. It provides packet boundaries (hence SOCK_SEQPACKET). Congestion control is per packet rather per byte, so we do not re-use the generic socket memory accounting. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/phonet.h | 4 +- include/net/phonet/pep.h | 114 ++++++ net/phonet/Makefile | 4 +- net/phonet/af_phonet.c | 3 + net/phonet/pep.c | 908 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1031 insertions(+), 2 deletions(-) create mode 100644 net/phonet/pep.c (limited to 'include/linux') diff --git a/include/linux/phonet.h b/include/linux/phonet.h index 3a027f588a4a..f92185242078 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -27,7 +27,9 @@ #define PN_PROTO_TRANSPORT 0 /* Phonet datagram socket */ #define PN_PROTO_PHONET 1 -#define PHONET_NPROTO 2 +/* Phonet pipe */ +#define PN_PROTO_PIPE 2 +#define PHONET_NPROTO 3 #define PNADDR_ANY 0 #define PNPORT_RESOURCE_ROUTING 0 diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index b2f8c54c5333..fb024e186860 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -26,11 +26,21 @@ struct pep_sock { struct pn_sock pn_sk; + /* XXX: union-ify listening vs connected stuff ? */ /* Listening socket stuff: */ struct hlist_head ackq; + struct hlist_head hlist; /* Connected socket stuff: */ + struct sock *listener; + u16 peer_type; /* peer type/subtype */ + u8 pipe_handle; + + u8 rx_credits; u8 tx_credits; + u8 rx_fc; /* RX flow control */ + u8 tx_fc; /* TX flow control */ + u8 init_enable; /* auto-enable at creation */ }; static inline struct pep_sock *pep_sk(struct sock *sk) @@ -40,4 +50,108 @@ static inline struct pep_sock *pep_sk(struct sock *sk) extern const struct proto_ops phonet_stream_ops; +/* Pipe protocol definitions */ +struct pnpipehdr { + u8 utid; /* transaction ID */ + u8 message_id; + u8 pipe_handle; + union { + u8 state_after_connect; /* connect request */ + u8 state_after_reset; /* reset request */ + u8 error_code; /* any response */ + u8 pep_type; /* status indication */ + u8 data[1]; + }; +}; +#define other_pep_type data[1] + +static inline struct pnpipehdr *pnp_hdr(struct sk_buff *skb) +{ + return (struct pnpipehdr *)skb_transport_header(skb); +} + +#define MAX_PNPIPE_HEADER (MAX_PHONET_HEADER + 4) + +enum { + PNS_PIPE_DATA = 0x20, + + PNS_PEP_CONNECT_REQ = 0x40, + PNS_PEP_CONNECT_RESP, + PNS_PEP_DISCONNECT_REQ, + PNS_PEP_DISCONNECT_RESP, + PNS_PEP_RESET_REQ, + PNS_PEP_RESET_RESP, + PNS_PEP_ENABLE_REQ, + PNS_PEP_ENABLE_RESP, + PNS_PEP_CTRL_REQ, + PNS_PEP_CTRL_RESP, + PNS_PEP_DISABLE_REQ = 0x4C, + PNS_PEP_DISABLE_RESP, + + PNS_PEP_STATUS_IND = 0x60, + PNS_PIPE_CREATED_IND, + PNS_PIPE_RESET_IND = 0x63, + PNS_PIPE_ENABLED_IND, + PNS_PIPE_REDIRECTED_IND, + PNS_PIPE_DISABLED_IND = 0x66, +}; + +#define PN_PIPE_INVALID_HANDLE 0xff +#define PN_PEP_TYPE_COMMON 0x00 + +/* Phonet pipe status indication */ +enum { + PN_PEP_IND_FLOW_CONTROL, + PN_PEP_IND_ID_MCFC_GRANT_CREDITS, +}; + +/* Phonet pipe error codes */ +enum { + PN_PIPE_NO_ERROR, + PN_PIPE_ERR_INVALID_PARAM, + PN_PIPE_ERR_INVALID_HANDLE, + PN_PIPE_ERR_INVALID_CTRL_ID, + PN_PIPE_ERR_NOT_ALLOWED, + PN_PIPE_ERR_PEP_IN_USE, + PN_PIPE_ERR_OVERLOAD, + PN_PIPE_ERR_DEV_DISCONNECTED, + PN_PIPE_ERR_TIMEOUT, + PN_PIPE_ERR_ALL_PIPES_IN_USE, + PN_PIPE_ERR_GENERAL, + PN_PIPE_ERR_NOT_SUPPORTED, +}; + +/* Phonet pipe states */ +enum { + PN_PIPE_DISABLE, + PN_PIPE_ENABLE, +}; + +/* Phonet pipe sub-block types */ +enum { + PN_PIPE_SB_CREATE_REQ_PEP_SUB_TYPE, + PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE, + PN_PIPE_SB_REDIRECT_REQ_PEP_SUB_TYPE, + PN_PIPE_SB_NEGOTIATED_FC, + PN_PIPE_SB_REQUIRED_FC_TX, + PN_PIPE_SB_PREFERRED_FC_RX, +}; + +/* Phonet pipe flow control models */ +enum { + PN_NO_FLOW_CONTROL, + PN_LEGACY_FLOW_CONTROL, + PN_ONE_CREDIT_FLOW_CONTROL, + PN_MULTI_CREDIT_FLOW_CONTROL, +}; + +#define pn_flow_safe(fc) ((fc) >> 1) + +/* Phonet pipe flow control states */ +enum { + PEP_IND_EMPTY, + PEP_IND_BUSY, + PEP_IND_READY, +}; + #endif diff --git a/net/phonet/Makefile b/net/phonet/Makefile index ae9c3ed5be83..505df2a04a85 100644 --- a/net/phonet/Makefile +++ b/net/phonet/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_PHONET) += phonet.o +obj-$(CONFIG_PHONET) += phonet.o pn_pep.o phonet-objs := \ pn_dev.o \ @@ -7,3 +7,5 @@ phonet-objs := \ datagram.o \ sysctl.o \ af_phonet.o + +pn_pep-objs := pep.o diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 0a74aeaf5adf..9e9c6fce11aa 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -58,6 +58,9 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol) case SOCK_DGRAM: protocol = PN_PROTO_PHONET; break; + case SOCK_SEQPACKET: + protocol = PN_PROTO_PIPE; + break; default: return -EPROTONOSUPPORT; } diff --git a/net/phonet/pep.c b/net/phonet/pep.c new file mode 100644 index 000000000000..c5dfecb207d2 --- /dev/null +++ b/net/phonet/pep.c @@ -0,0 +1,908 @@ +/* + * File: pep.c + * + * Phonet pipe protocol end point socket + * + * Copyright (C) 2008 Nokia Corporation. + * + * Author: Rémi Denis-Courmont + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* sk_state values: + * TCP_CLOSE sock not in use yet + * TCP_CLOSE_WAIT disconnected pipe + * TCP_LISTEN listening pipe endpoint + * TCP_SYN_RECV connected pipe in disabled state + * TCP_ESTABLISHED connected pipe in enabled state + * + * pep_sock locking: + * - sk_state, ackq, hlist: sock lock needed + * - listener: read only + * - pipe_handle: read only + */ + +#define CREDITS_MAX 10 +#define CREDITS_THR 7 + +static const struct sockaddr_pn pipe_srv = { + .spn_family = AF_PHONET, + .spn_resource = 0xD9, /* pipe service */ +}; + +#define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */ + +/* Get the next TLV sub-block. */ +static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, + void *buf) +{ + void *data = NULL; + struct { + u8 sb_type; + u8 sb_len; + } *ph, h; + int buflen = *plen; + + ph = skb_header_pointer(skb, 0, 2, &h); + if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) + return NULL; + ph->sb_len -= 2; + *ptype = ph->sb_type; + *plen = ph->sb_len; + + if (buflen > ph->sb_len) + buflen = ph->sb_len; + data = skb_header_pointer(skb, 2, buflen, buf); + __skb_pull(skb, 2 + ph->sb_len); + return data; +} + +static int pep_reply(struct sock *sk, struct sk_buff *oskb, + u8 code, const void *data, int len, gfp_t priority) +{ + const struct pnpipehdr *oph = pnp_hdr(oskb); + struct pnpipehdr *ph; + struct sk_buff *skb; + + skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); + if (!skb) + return -ENOMEM; + skb_set_owner_w(skb, sk); + + skb_reserve(skb, MAX_PNPIPE_HEADER); + __skb_put(skb, len); + skb_copy_to_linear_data(skb, data, len); + __skb_push(skb, sizeof(*ph)); + skb_reset_transport_header(skb); + ph = pnp_hdr(skb); + ph->utid = oph->utid; + ph->message_id = oph->message_id + 1; /* REQ -> RESP */ + ph->pipe_handle = oph->pipe_handle; + ph->error_code = code; + + return pn_skb_send(sk, skb, &pipe_srv); +} + +#define PAD 0x00 +static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) +{ + static const u8 data[20] = { + PAD, PAD, PAD, 2 /* sub-blocks */, + PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD, + PN_MULTI_CREDIT_FLOW_CONTROL, + PN_ONE_CREDIT_FLOW_CONTROL, + PN_LEGACY_FLOW_CONTROL, + PAD, + PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD, + PN_MULTI_CREDIT_FLOW_CONTROL, + PN_ONE_CREDIT_FLOW_CONTROL, + PN_LEGACY_FLOW_CONTROL, + PAD, + }; + + might_sleep(); + return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data), + GFP_KERNEL); +} + +static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code) +{ + static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; + WARN_ON(code == PN_PIPE_NO_ERROR); + return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC); +} + +/* Control requests are not sent by the pipe service and have a specific + * message format. */ +static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code) +{ + const struct pnpipehdr *oph = pnp_hdr(oskb); + struct sk_buff *skb; + struct pnpipehdr *ph; + struct sockaddr_pn dst; + + skb = alloc_skb(MAX_PNPIPE_HEADER + 4, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + skb_set_owner_w(skb, sk); + + skb_reserve(skb, MAX_PHONET_HEADER); + ph = (struct pnpipehdr *)skb_put(skb, sizeof(*ph) + 4); + + ph->utid = oph->utid; + ph->message_id = PNS_PEP_CTRL_RESP; + ph->pipe_handle = oph->pipe_handle; + ph->data[0] = oph->data[1]; /* CTRL id */ + ph->data[1] = oph->data[0]; /* PEP type */ + ph->data[2] = code; /* error code, at an usual offset */ + ph->data[3] = PAD; + ph->data[4] = PAD; + + pn_skb_get_src_sockaddr(oskb, &dst); + return pn_skb_send(sk, skb, &dst); +} + +static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) +{ + struct pep_sock *pn = pep_sk(sk); + struct pnpipehdr *ph; + struct sk_buff *skb; + + skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); + if (!skb) + return -ENOMEM; + skb_set_owner_w(skb, sk); + + skb_reserve(skb, MAX_PNPIPE_HEADER + 4); + __skb_push(skb, sizeof(*ph) + 4); + skb_reset_transport_header(skb); + ph = pnp_hdr(skb); + ph->utid = 0; + ph->message_id = PNS_PEP_STATUS_IND; + ph->pipe_handle = pn->pipe_handle; + ph->pep_type = PN_PEP_TYPE_COMMON; + ph->data[1] = type; + ph->data[2] = PAD; + ph->data[3] = PAD; + ph->data[4] = status; + + return pn_skb_send(sk, skb, &pipe_srv); +} + +/* Send our RX flow control information to the sender. + * Socket must be locked. */ +static void pipe_grant_credits(struct sock *sk) +{ + struct pep_sock *pn = pep_sk(sk); + + BUG_ON(sk->sk_state != TCP_ESTABLISHED); + + switch (pn->rx_fc) { + case PN_LEGACY_FLOW_CONTROL: /* TODO */ + break; + case PN_ONE_CREDIT_FLOW_CONTROL: + pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, + PEP_IND_READY, GFP_ATOMIC); + pn->rx_credits = 1; + break; + case PN_MULTI_CREDIT_FLOW_CONTROL: + if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX) + break; + if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS, + CREDITS_MAX - pn->rx_credits, + GFP_ATOMIC) == 0) + pn->rx_credits = CREDITS_MAX; + break; + } +} + +static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) +{ + struct pep_sock *pn = pep_sk(sk); + struct pnpipehdr *hdr = pnp_hdr(skb); + + if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) + return -EINVAL; + + if (hdr->data[0] != PN_PEP_TYPE_COMMON) { + LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n", + (unsigned)hdr->data[0]); + return -EOPNOTSUPP; + } + + switch (hdr->data[1]) { + case PN_PEP_IND_FLOW_CONTROL: + switch (pn->tx_fc) { + case PN_LEGACY_FLOW_CONTROL: + switch (hdr->data[4]) { + case PEP_IND_BUSY: + pn->tx_credits = 0; + break; + case PEP_IND_READY: + pn->tx_credits = 1; + break; + } + break; + case PN_ONE_CREDIT_FLOW_CONTROL: + if (hdr->data[4] == PEP_IND_READY) + pn->tx_credits = 1; + break; + } + break; + + case PN_PEP_IND_ID_MCFC_GRANT_CREDITS: + if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL) + break; + if (pn->tx_credits + hdr->data[4] > 0xff) + pn->tx_credits = 0xff; + else + pn->tx_credits += hdr->data[4]; + break; + + default: + LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n", + (unsigned)hdr->data[1]); + return -EOPNOTSUPP; + } + if (pn->tx_credits) + sk->sk_write_space(sk); + return 0; +} + +static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb) +{ + struct pep_sock *pn = pep_sk(sk); + struct pnpipehdr *hdr = pnp_hdr(skb); + u8 n_sb = hdr->data[0]; + + pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; + __skb_pull(skb, sizeof(*hdr)); + while (n_sb > 0) { + u8 type, buf[2], len = sizeof(buf); + u8 *data = pep_get_sb(skb, &type, &len, buf); + + if (data == NULL) + return -EINVAL; + switch (type) { + case PN_PIPE_SB_NEGOTIATED_FC: + if (len < 2 || (data[0] | data[1]) > 3) + break; + pn->tx_fc = data[0] & 3; + pn->rx_fc = data[1] & 3; + break; + } + n_sb--; + } + return 0; +} + +/* Queue an skb to a connected sock. + * Socket lock must be held. */ +static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) +{ + struct pep_sock *pn = pep_sk(sk); + struct pnpipehdr *hdr = pnp_hdr(skb); + int err = 0; + + BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); + + switch (hdr->message_id) { + case PNS_PEP_CONNECT_REQ: + pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); + break; + + case PNS_PEP_DISCONNECT_REQ: + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); + sk->sk_state = TCP_CLOSE_WAIT; + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_state_change(sk); + break; + + case PNS_PEP_ENABLE_REQ: + /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); + break; + + case PNS_PEP_RESET_REQ: + switch (hdr->state_after_reset) { + case PN_PIPE_DISABLE: + pn->init_enable = 0; + break; + case PN_PIPE_ENABLE: + pn->init_enable = 1; + break; + default: /* not allowed to send an error here!? */ + err = -EINVAL; + goto out; + } + /* fall through */ + case PNS_PEP_DISABLE_REQ: + pn->tx_credits = 0; + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); + break; + + case PNS_PEP_CTRL_REQ: + /* TODO */ + pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR); + break; + + case PNS_PIPE_DATA: + __skb_pull(skb, 3); /* Pipe data header */ + if (!pn_flow_safe(pn->rx_fc)) { + err = sock_queue_rcv_skb(sk, skb); + if (!err) + return 0; + break; + } + + if (pn->rx_credits == 0) { + err = -ENOBUFS; + break; + } + pn->rx_credits--; + skb->dev = NULL; + skb_set_owner_r(skb, sk); + err = skb->len; + skb_queue_tail(&sk->sk_receive_queue, skb); + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, err); + return 0; + + case PNS_PEP_STATUS_IND: + pipe_rcv_status(sk, skb); + break; + + case PNS_PIPE_REDIRECTED_IND: + err = pipe_rcv_created(sk, skb); + break; + + case PNS_PIPE_CREATED_IND: + err = pipe_rcv_created(sk, skb); + if (err) + break; + /* fall through */ + case PNS_PIPE_RESET_IND: + if (!pn->init_enable) + break; + /* fall through */ + case PNS_PIPE_ENABLED_IND: + if (!pn_flow_safe(pn->tx_fc)) { + pn->tx_credits = 1; + sk->sk_write_space(sk); + } + if (sk->sk_state == TCP_ESTABLISHED) + break; /* Nothing to do */ + sk->sk_state = TCP_ESTABLISHED; + pipe_grant_credits(sk); + break; + + case PNS_PIPE_DISABLED_IND: + sk->sk_state = TCP_SYN_RECV; + pn->rx_credits = 0; + break; + + default: + LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u\n", + hdr->message_id); + err = -EINVAL; + } +out: + kfree_skb(skb); + return err; +} + +/* Destroy connected sock. */ +static void pipe_destruct(struct sock *sk) +{ + skb_queue_purge(&sk->sk_receive_queue); +} + +static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) +{ + struct sock *newsk; + struct pep_sock *newpn, *pn = pep_sk(sk); + struct pnpipehdr *hdr; + struct sockaddr_pn dst; + u16 peer_type; + u8 pipe_handle, enabled, n_sb; + + if (!pskb_pull(skb, sizeof(*hdr) + 4)) + return -EINVAL; + + hdr = pnp_hdr(skb); + pipe_handle = hdr->pipe_handle; + switch (hdr->state_after_connect) { + case PN_PIPE_DISABLE: + enabled = 0; + break; + case PN_PIPE_ENABLE: + enabled = 1; + break; + default: + pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM); + return -EINVAL; + } + peer_type = hdr->other_pep_type << 8; + + if (unlikely(sk->sk_state != TCP_LISTEN) || sk_acceptq_is_full(sk)) { + pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); + return -ENOBUFS; + } + + /* Parse sub-blocks (options) */ + n_sb = hdr->data[4]; + while (n_sb > 0) { + u8 type, buf[1], len = sizeof(buf); + const u8 *data = pep_get_sb(skb, &type, &len, buf); + + if (data == NULL) + return -EINVAL; + switch (type) { + case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: + if (len < 1) + return -EINVAL; + peer_type = (peer_type & 0xff00) | data[0]; + break; + } + n_sb--; + } + + skb = skb_clone(skb, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + /* Create a new to-be-accepted sock */ + newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot); + if (!newsk) { + kfree_skb(skb); + return -ENOMEM; + } + sock_init_data(NULL, newsk); + newsk->sk_state = TCP_SYN_RECV; + newsk->sk_backlog_rcv = pipe_do_rcv; + newsk->sk_protocol = sk->sk_protocol; + newsk->sk_destruct = pipe_destruct; + + newpn = pep_sk(newsk); + pn_skb_get_dst_sockaddr(skb, &dst); + newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); + newpn->pn_sk.resource = pn->pn_sk.resource; + newpn->pipe_handle = pipe_handle; + newpn->peer_type = peer_type; + newpn->rx_credits = newpn->tx_credits = 0; + newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; + newpn->init_enable = enabled; + + BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); + skb_queue_head(&newsk->sk_receive_queue, skb); + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, 0); + + sk_acceptq_added(sk); + sk_add_node(newsk, &pn->ackq); + return 0; +} + +/* Listening sock must be locked */ +static struct sock *pep_find_pipe(const struct hlist_head *hlist, + const struct sockaddr_pn *dst, + u8 pipe_handle) +{ + struct hlist_node *node; + struct sock *sknode; + u16 dobj = pn_sockaddr_get_object(dst); + + sk_for_each(sknode, node, hlist) { + struct pep_sock *pnnode = pep_sk(sknode); + + /* Ports match, but addresses might not: */ + if (pnnode->pn_sk.sobject != dobj) + continue; + if (pnnode->pipe_handle != pipe_handle) + continue; + if (sknode->sk_state == TCP_CLOSE_WAIT) + continue; + + sock_hold(sknode); + return sknode; + } + return NULL; +} + +/* + * Deliver an skb to a listening sock. + * Socket lock must be held. + * We then queue the skb to the right connected sock (if any). + */ +static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) +{ + struct pep_sock *pn = pep_sk(sk); + struct sock *sknode; + struct pnpipehdr *hdr = pnp_hdr(skb); + struct sockaddr_pn dst; + int err = NET_RX_SUCCESS; + u8 pipe_handle; + + if (!pskb_may_pull(skb, sizeof(*hdr))) + goto drop; + + hdr = pnp_hdr(skb); + pipe_handle = hdr->pipe_handle; + if (pipe_handle == PN_PIPE_INVALID_HANDLE) + goto drop; + + pn_skb_get_dst_sockaddr(skb, &dst); + + /* Look for an existing pipe handle */ + sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle); + if (sknode) + return sk_receive_skb(sknode, skb, 1); + + /* Look for a pipe handle pending accept */ + sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle); + if (sknode) { + sock_put(sknode); + if (net_ratelimit()) + printk(KERN_WARNING"Phonet unconnected PEP ignored"); + err = NET_RX_DROP; + goto drop; + } + + switch (hdr->message_id) { + case PNS_PEP_CONNECT_REQ: + err = pep_connreq_rcv(sk, skb); + break; + + case PNS_PEP_DISCONNECT_REQ: + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); + break; + + case PNS_PEP_CTRL_REQ: + pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE); + break; + + case PNS_PEP_RESET_REQ: + case PNS_PEP_ENABLE_REQ: + case PNS_PEP_DISABLE_REQ: + /* invalid handle is not even allowed here! */ + default: + err = NET_RX_DROP; + } +drop: + kfree_skb(skb); + return err; +} + +/* associated socket ceases to exist */ +static void pep_sock_close(struct sock *sk, long timeout) +{ + struct pep_sock *pn = pep_sk(sk); + + sk_common_release(sk); + + lock_sock(sk); + if (sk->sk_state == TCP_LISTEN) { + /* Destroy the listen queue */ + struct sock *sknode; + struct hlist_node *p, *n; + + sk_for_each_safe(sknode, p, n, &pn->ackq) + sk_del_node_init(sknode); + sk->sk_state = TCP_CLOSE; + } + release_sock(sk); +} + +static int pep_wait_connreq(struct sock *sk, int noblock) +{ + struct task_struct *tsk = current; + struct pep_sock *pn = pep_sk(sk); + long timeo = sock_rcvtimeo(sk, noblock); + + for (;;) { + DEFINE_WAIT(wait); + + if (sk->sk_state != TCP_LISTEN) + return -EINVAL; + if (!hlist_empty(&pn->ackq)) + break; + if (!timeo) + return -EWOULDBLOCK; + if (signal_pending(tsk)) + return sock_intr_errno(timeo); + + prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait, + TASK_INTERRUPTIBLE); + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + finish_wait(&sk->sk_socket->wait, &wait); + } + + return 0; +} + +static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) +{ + struct pep_sock *pn = pep_sk(sk); + struct sock *newsk = NULL; + struct sk_buff *oskb; + int err; + + lock_sock(sk); + err = pep_wait_connreq(sk, flags & O_NONBLOCK); + if (err) + goto out; + + newsk = __sk_head(&pn->ackq); + + oskb = skb_dequeue(&newsk->sk_receive_queue); + err = pep_accept_conn(newsk, oskb); + if (err) { + skb_queue_head(&newsk->sk_receive_queue, oskb); + newsk = NULL; + goto out; + } + + sock_hold(sk); + pep_sk(newsk)->listener = sk; + + sock_hold(newsk); + sk_del_node_init(newsk); + sk_acceptq_removed(sk); + sk_add_node(newsk, &pn->hlist); + __sock_put(newsk); + +out: + release_sock(sk); + *errp = err; + return newsk; +} + +static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + int answ; + + switch (cmd) { + case SIOCINQ: + if (sk->sk_state == TCP_LISTEN) + return -EINVAL; + + lock_sock(sk); + if (!skb_queue_empty(&sk->sk_receive_queue)) + answ = skb_peek(&sk->sk_receive_queue)->len; + else + answ = 0; + release_sock(sk); + return put_user(answ, (int __user *)arg); + } + + return -ENOIOCTLCMD; +} + +static int pep_init(struct sock *sk) +{ + struct pep_sock *pn = pep_sk(sk); + + INIT_HLIST_HEAD(&pn->ackq); + INIT_HLIST_HEAD(&pn->hlist); + pn->pipe_handle = PN_PIPE_INVALID_HANDLE; + return 0; +} + +static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t len) +{ + struct pep_sock *pn = pep_sk(sk); + struct sk_buff *skb = NULL; + struct pnpipehdr *ph; + long timeo; + int flags = msg->msg_flags; + int err, done; + + if (msg->msg_flags & MSG_OOB || !(msg->msg_flags & MSG_EOR)) + return -EOPNOTSUPP; + + lock_sock(sk); + timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); + if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) { + err = -ENOTCONN; + goto out; + } + if (sk->sk_state != TCP_ESTABLISHED) { + /* Wait until the pipe gets to enabled state */ +disabled: + err = sk_stream_wait_connect(sk, &timeo); + if (err) + goto out; + + if (sk->sk_state == TCP_CLOSE_WAIT) { + err = -ECONNRESET; + goto out; + } + } + BUG_ON(sk->sk_state != TCP_ESTABLISHED); + + /* Wait until flow control allows TX */ + done = pn->tx_credits > 0; + while (!done) { + DEFINE_WAIT(wait); + + if (!timeo) { + err = -EAGAIN; + goto out; + } + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto out; + } + + prepare_to_wait(&sk->sk_socket->wait, &wait, + TASK_INTERRUPTIBLE); + done = sk_wait_event(sk, &timeo, pn->tx_credits > 0); + finish_wait(&sk->sk_socket->wait, &wait); + + if (sk->sk_state != TCP_ESTABLISHED) + goto disabled; + } + + if (!skb) { + skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, + flags & MSG_DONTWAIT, &err); + if (skb == NULL) + goto out; + skb_reserve(skb, MAX_PHONET_HEADER + 3); + + if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits) + goto disabled; /* sock_alloc_send_skb might sleep */ + } + + err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + if (err < 0) + goto out; + + __skb_push(skb, 3); + skb_reset_transport_header(skb); + ph = pnp_hdr(skb); + ph->utid = 0; + ph->message_id = PNS_PIPE_DATA; + ph->pipe_handle = pn->pipe_handle; + if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */ + pn->tx_credits--; + + err = pn_skb_send(sk, skb, &pipe_srv); + if (err >= 0) + err = len; /* success! */ + skb = NULL; +out: + release_sock(sk); + kfree_skb(skb); + return err; +} + +static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len) +{ + struct sk_buff *skb; + int err; + + if (unlikely(flags & MSG_OOB)) + return -EOPNOTSUPP; + if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE))) + return -ENOTCONN; + + skb = skb_recv_datagram(sk, flags, noblock, &err); + lock_sock(sk); + if (skb == NULL) { + if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT) + err = -ECONNRESET; + release_sock(sk); + return err; + } + + if (sk->sk_state == TCP_ESTABLISHED) + pipe_grant_credits(sk); + release_sock(sk); + + msg->msg_flags |= MSG_EOR; + + if (skb->len > len) + msg->msg_flags |= MSG_TRUNC; + else + len = skb->len; + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); + if (!err) + err = (flags & MSG_TRUNC) ? skb->len : len; + + skb_free_datagram(sk, skb); + return err; +} + +static void pep_sock_unhash(struct sock *sk) +{ + struct pep_sock *pn = pep_sk(sk); + struct sock *skparent = NULL; + + lock_sock(sk); + if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) { + skparent = pn->listener; + sk_del_node_init(sk); + release_sock(sk); + + sk = skparent; + pn = pep_sk(skparent); + lock_sock(sk); + } + /* Unhash a listening sock only when it is closed + * and all of its active connected pipes are closed. */ + if (hlist_empty(&pn->hlist)) + pn_sock_unhash(&pn->pn_sk.sk); + release_sock(sk); + + if (skparent) + sock_put(skparent); +} + +static struct proto pep_proto = { + .close = pep_sock_close, + .accept = pep_sock_accept, + .ioctl = pep_ioctl, + .init = pep_init, + .sendmsg = pep_sendmsg, + .recvmsg = pep_recvmsg, + .backlog_rcv = pep_do_rcv, + .hash = pn_sock_hash, + .unhash = pep_sock_unhash, + .get_port = pn_sock_get_port, + .obj_size = sizeof(struct pep_sock), + .owner = THIS_MODULE, + .name = "PNPIPE", +}; + +static struct phonet_protocol pep_pn_proto = { + .ops = &phonet_stream_ops, + .prot = &pep_proto, + .sock_type = SOCK_SEQPACKET, +}; + +static int __init pep_register(void) +{ + return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto); +} + +static void __exit pep_unregister(void) +{ + phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto); +} + +module_init(pep_register); +module_exit(pep_unregister); +MODULE_AUTHOR("Remi Denis-Courmont, Nokia"); +MODULE_DESCRIPTION("Phonet pipe protocol"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE); -- cgit v1.2.3 From 02a47617cdce440f60c71a51f3a93f9f5fcc5a7a Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Sun, 5 Oct 2008 11:16:16 -0700 Subject: Phonet: implement GPRS virtual interface over PEP socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/linux/phonet.h | 8 ++ include/linux/socket.h | 1 + include/net/phonet/gprs.h | 38 +++++ include/net/phonet/pep.h | 1 + net/phonet/Makefile | 2 +- net/phonet/pep-gprs.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++ net/phonet/pep.c | 161 +++++++++++++++++++-- net/phonet/socket.c | 8 +- 8 files changed, 550 insertions(+), 16 deletions(-) create mode 100644 include/net/phonet/gprs.h create mode 100644 net/phonet/pep-gprs.c (limited to 'include/linux') diff --git a/include/linux/phonet.h b/include/linux/phonet.h index f92185242078..c9609f9aedac 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -31,9 +31,17 @@ #define PN_PROTO_PIPE 2 #define PHONET_NPROTO 3 +/* Socket options for SOL_PNPIPE level */ +#define PNPIPE_ENCAP 1 +#define PNPIPE_IFINDEX 2 + #define PNADDR_ANY 0 #define PNPORT_RESOURCE_ROUTING 0 +/* Values for PNPIPE_ENCAP option */ +#define PNPIPE_ENCAP_NONE 0 +#define PNPIPE_ENCAP_IP 1 + /* ioctls */ #define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0) diff --git a/include/linux/socket.h b/include/linux/socket.h index 818ca33bf79f..20fc4bbfca42 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -297,6 +297,7 @@ struct ucred { #define SOL_RXRPC 272 #define SOL_PPPOL2TP 273 #define SOL_BLUETOOTH 274 +#define SOL_PNPIPE 275 /* IPX options */ #define IPX_TYPE 1 diff --git a/include/net/phonet/gprs.h b/include/net/phonet/gprs.h new file mode 100644 index 000000000000..928daf595beb --- /dev/null +++ b/include/net/phonet/gprs.h @@ -0,0 +1,38 @@ +/* + * File: pep_gprs.h + * + * GPRS over Phonet pipe end point socket + * + * Copyright (C) 2008 Nokia Corporation. + * + * Author: Rémi Denis-Courmont + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef NET_PHONET_GPRS_H +#define NET_PHONET_GPRS_H + +struct sock; +struct sk_buff; + +int pep_writeable(struct sock *sk); +int pep_write(struct sock *sk, struct sk_buff *skb); +struct sk_buff *pep_read(struct sock *sk); + +int gprs_attach(struct sock *sk); +void gprs_detach(struct sock *sk); + +#endif diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index 4d79564850ae..fcd793030e4d 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -35,6 +35,7 @@ struct pep_sock { struct sock *listener; struct sk_buff_head ctrlreq_queue; #define PNPIPE_CTRLREQ_MAX 10 + int ifindex; u16 peer_type; /* peer type/subtype */ u8 pipe_handle; diff --git a/net/phonet/Makefile b/net/phonet/Makefile index 505df2a04a85..d62bbba649b3 100644 --- a/net/phonet/Makefile +++ b/net/phonet/Makefile @@ -8,4 +8,4 @@ phonet-objs := \ sysctl.o \ af_phonet.o -pn_pep-objs := pep.o +pn_pep-objs := pep.o pep-gprs.o diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c new file mode 100644 index 000000000000..9978afbd9f2a --- /dev/null +++ b/net/phonet/pep-gprs.c @@ -0,0 +1,347 @@ +/* + * File: pep-gprs.c + * + * GPRS over Phonet pipe end point socket + * + * Copyright (C) 2008 Nokia Corporation. + * + * Author: Rémi Denis-Courmont + * + * 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. + * + * 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define GPRS_DEFAULT_MTU 1400 + +struct gprs_dev { + struct sock *sk; + void (*old_state_change)(struct sock *); + void (*old_data_ready)(struct sock *, int); + void (*old_write_space)(struct sock *); + + struct net_device *net; + struct net_device_stats stats; + + struct sk_buff_head tx_queue; + struct work_struct tx_work; + spinlock_t tx_lock; + unsigned tx_max; +}; + +static int gprs_type_trans(struct sk_buff *skb) +{ + const u8 *pvfc; + u8 buf; + + pvfc = skb_header_pointer(skb, 0, 1, &buf); + if (!pvfc) + return 0; + /* Look at IP version field */ + switch (*pvfc >> 4) { + case 4: + return htons(ETH_P_IP); + case 6: + return htons(ETH_P_IPV6); + } + return 0; +} + +/* + * Socket callbacks + */ + +static void gprs_state_change(struct sock *sk) +{ + struct gprs_dev *dev = sk->sk_user_data; + + if (sk->sk_state == TCP_CLOSE_WAIT) { + netif_stop_queue(dev->net); + netif_carrier_off(dev->net); + } +} + +static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb) +{ + int err = 0; + u16 protocol = gprs_type_trans(skb); + + if (!protocol) { + err = -EINVAL; + goto drop; + } + + if (likely(skb_headroom(skb) & 3)) { + struct sk_buff *rskb, *fs; + int flen = 0; + + /* Phonet Pipe data header is misaligned (3 bytes), + * so wrap the IP packet as a single fragment of an head-less + * socket buffer. The network stack will pull what it needs, + * but at least, the whole IP payload is not memcpy'd. */ + rskb = netdev_alloc_skb(dev->net, 0); + if (!rskb) { + err = -ENOBUFS; + goto drop; + } + skb_shinfo(rskb)->frag_list = skb; + rskb->len += skb->len; + rskb->data_len += rskb->len; + rskb->truesize += rskb->len; + + /* Avoid nested fragments */ + for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) + flen += fs->len; + skb->next = skb_shinfo(skb)->frag_list; + skb_shinfo(skb)->frag_list = NULL; + skb->len -= flen; + skb->data_len -= flen; + skb->truesize -= flen; + + skb = rskb; + } + + skb->protocol = protocol; + skb_reset_mac_header(skb); + skb->dev = dev->net; + + if (likely(dev->net->flags & IFF_UP)) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + netif_rx(skb); + skb = NULL; + } else + err = -ENODEV; + +drop: + if (skb) { + dev_kfree_skb(skb); + dev->stats.rx_dropped++; + } + return err; +} + +static void gprs_data_ready(struct sock *sk, int len) +{ + struct gprs_dev *dev = sk->sk_user_data; + struct sk_buff *skb; + + while ((skb = pep_read(sk)) != NULL) { + skb_orphan(skb); + gprs_recv(dev, skb); + } +} + +static void gprs_write_space(struct sock *sk) +{ + struct gprs_dev *dev = sk->sk_user_data; + unsigned credits = pep_writeable(sk); + + spin_lock_bh(&dev->tx_lock); + dev->tx_max = credits; + if (credits > skb_queue_len(&dev->tx_queue)) + netif_wake_queue(dev->net); + spin_unlock_bh(&dev->tx_lock); +} + +/* + * Network device callbacks + */ + +static int gprs_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct gprs_dev *dev = netdev_priv(net); + + switch (skb->protocol) { + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + break; + default: + dev_kfree_skb(skb); + return 0; + } + + spin_lock(&dev->tx_lock); + if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) { + skb_queue_tail(&dev->tx_queue, skb); + skb = NULL; + } + if (skb_queue_len(&dev->tx_queue) >= dev->tx_max) + netif_stop_queue(net); + spin_unlock(&dev->tx_lock); + + schedule_work(&dev->tx_work); + if (unlikely(skb)) + dev_kfree_skb(skb); + return 0; +} + +static void gprs_tx(struct work_struct *work) +{ + struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work); + struct sock *sk = dev->sk; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) { + int err; + + dev->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; + + skb_orphan(skb); + skb_set_owner_w(skb, sk); + + lock_sock(sk); + err = pep_write(sk, skb); + if (err) { + LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n", + dev->net->name, err); + dev->stats.tx_aborted_errors++; + dev->stats.tx_errors++; + } + release_sock(sk); + } + + lock_sock(sk); + gprs_write_space(sk); + release_sock(sk); +} + +static int gprs_set_mtu(struct net_device *net, int new_mtu) +{ + if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11))) + return -EINVAL; + + net->mtu = new_mtu; + return 0; +} + +static struct net_device_stats *gprs_get_stats(struct net_device *net) +{ + struct gprs_dev *dev = netdev_priv(net); + + return &dev->stats; +} + +static void gprs_setup(struct net_device *net) +{ + net->features = NETIF_F_FRAGLIST; + net->type = ARPHRD_NONE; + net->flags = IFF_POINTOPOINT | IFF_NOARP; + net->mtu = GPRS_DEFAULT_MTU; + net->hard_header_len = 0; + net->addr_len = 0; + net->tx_queue_len = 10; + + net->destructor = free_netdev; + net->hard_start_xmit = gprs_xmit; /* mandatory */ + net->change_mtu = gprs_set_mtu; + net->get_stats = gprs_get_stats; +} + +/* + * External interface + */ + +/* + * Attach a GPRS interface to a datagram socket. + * Returns the interface index on success, negative error code on error. + */ +int gprs_attach(struct sock *sk) +{ + static const char ifname[] = "gprs%d"; + struct gprs_dev *dev; + struct net_device *net; + int err; + + if (unlikely(sk->sk_type == SOCK_STREAM)) + return -EINVAL; /* need packet boundaries */ + + /* Create net device */ + net = alloc_netdev(sizeof(*dev), ifname, gprs_setup); + if (!net) + return -ENOMEM; + dev = netdev_priv(net); + dev->net = net; + dev->tx_max = 0; + spin_lock_init(&dev->tx_lock); + skb_queue_head_init(&dev->tx_queue); + INIT_WORK(&dev->tx_work, gprs_tx); + + netif_stop_queue(net); + err = register_netdev(net); + if (err) { + free_netdev(net); + return err; + } + + lock_sock(sk); + if (unlikely(sk->sk_user_data)) { + err = -EBUSY; + goto out_rel; + } + if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) || + sock_flag(sk, SOCK_DEAD))) { + err = -EINVAL; + goto out_rel; + } + sk->sk_user_data = dev; + dev->old_state_change = sk->sk_state_change; + dev->old_data_ready = sk->sk_data_ready; + dev->old_write_space = sk->sk_write_space; + sk->sk_state_change = gprs_state_change; + sk->sk_data_ready = gprs_data_ready; + sk->sk_write_space = gprs_write_space; + release_sock(sk); + + sock_hold(sk); + dev->sk = sk; + + printk(KERN_DEBUG"%s: attached\n", net->name); + gprs_write_space(sk); /* kick off TX */ + return net->ifindex; + +out_rel: + release_sock(sk); + unregister_netdev(net); + return err; +} + +void gprs_detach(struct sock *sk) +{ + struct gprs_dev *dev = sk->sk_user_data; + struct net_device *net = dev->net; + + lock_sock(sk); + sk->sk_user_data = NULL; + sk->sk_state_change = dev->old_state_change; + sk->sk_data_ready = dev->old_data_ready; + sk->sk_write_space = dev->old_write_space; + release_sock(sk); + + printk(KERN_DEBUG"%s: detached\n", net->name); + unregister_netdev(net); + flush_scheduled_work(); + sock_put(sk); + skb_queue_purge(&dev->tx_queue); +} diff --git a/net/phonet/pep.c b/net/phonet/pep.c index d564d07cb797..bc6d50f83249 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -31,6 +31,7 @@ #include #include #include +#include /* sk_state values: * TCP_CLOSE sock not in use yet @@ -612,6 +613,7 @@ drop: static void pep_sock_close(struct sock *sk, long timeout) { struct pep_sock *pn = pep_sk(sk); + int ifindex = 0; sk_common_release(sk); @@ -625,7 +627,12 @@ static void pep_sock_close(struct sock *sk, long timeout) sk_del_node_init(sknode); sk->sk_state = TCP_CLOSE; } + ifindex = pn->ifindex; + pn->ifindex = 0; release_sock(sk); + + if (ifindex) + gprs_detach(sk); } static int pep_wait_connreq(struct sock *sk, int noblock) @@ -730,12 +737,107 @@ static int pep_init(struct sock *sk) return 0; } +static int pep_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) +{ + struct pep_sock *pn = pep_sk(sk); + int val = 0, err = 0; + + if (level != SOL_PNPIPE) + return -ENOPROTOOPT; + if (optlen >= sizeof(int)) { + if (get_user(val, (int __user *) optval)) + return -EFAULT; + } + + lock_sock(sk); + switch (optname) { + case PNPIPE_ENCAP: + if (val && val != PNPIPE_ENCAP_IP) { + err = -EINVAL; + break; + } + if (!pn->ifindex == !val) + break; /* Nothing to do! */ + if (!capable(CAP_NET_ADMIN)) { + err = -EPERM; + break; + } + if (val) { + release_sock(sk); + err = gprs_attach(sk); + if (err > 0) { + pn->ifindex = err; + err = 0; + } + } else { + pn->ifindex = 0; + release_sock(sk); + gprs_detach(sk); + err = 0; + } + goto out_norel; + default: + err = -ENOPROTOOPT; + } + release_sock(sk); + +out_norel: + return err; +} + +static int pep_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct pep_sock *pn = pep_sk(sk); + int len, val; + + if (level != SOL_PNPIPE) + return -ENOPROTOOPT; + if (get_user(len, optlen)) + return -EFAULT; + + switch (optname) { + case PNPIPE_ENCAP: + val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; + break; + case PNPIPE_IFINDEX: + val = pn->ifindex; + break; + default: + return -ENOPROTOOPT; + } + + len = min_t(unsigned int, sizeof(int), len); + if (put_user(len, optlen)) + return -EFAULT; + if (put_user(val, (int __user *) optval)) + return -EFAULT; + return 0; +} + +static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) +{ + struct pep_sock *pn = pep_sk(sk); + struct pnpipehdr *ph; + + skb_push(skb, 3); + skb_reset_transport_header(skb); + ph = pnp_hdr(skb); + ph->utid = 0; + ph->message_id = PNS_PIPE_DATA; + ph->pipe_handle = pn->pipe_handle; + if (pn_flow_safe(pn->tx_fc) && pn->tx_credits) + pn->tx_credits--; + + return pn_skb_send(sk, skb, &pipe_srv); +} + static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { struct pep_sock *pn = pep_sk(sk); struct sk_buff *skb = NULL; - struct pnpipehdr *ph; long timeo; int flags = msg->msg_flags; int err, done; @@ -801,16 +903,7 @@ disabled: if (err < 0) goto out; - __skb_push(skb, 3); - skb_reset_transport_header(skb); - ph = pnp_hdr(skb); - ph->utid = 0; - ph->message_id = PNS_PIPE_DATA; - ph->pipe_handle = pn->pipe_handle; - if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */ - pn->tx_credits--; - - err = pn_skb_send(sk, skb, &pipe_srv); + err = pipe_skb_send(sk, skb); if (err >= 0) err = len; /* success! */ skb = NULL; @@ -820,6 +913,50 @@ out: return err; } +int pep_writeable(struct sock *sk) +{ + struct pep_sock *pn = pep_sk(sk); + + return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0; +} + +int pep_write(struct sock *sk, struct sk_buff *skb) +{ + struct sk_buff *rskb, *fs; + int flen = 0; + + rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); + if (!rskb) { + kfree_skb(skb); + return -ENOMEM; + } + skb_shinfo(rskb)->frag_list = skb; + rskb->len += skb->len; + rskb->data_len += rskb->len; + rskb->truesize += rskb->len; + + /* Avoid nested fragments */ + for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) + flen += fs->len; + skb->next = skb_shinfo(skb)->frag_list; + skb_shinfo(skb)->frag_list = NULL; + skb->len -= flen; + skb->data_len -= flen; + skb->truesize -= flen; + + skb_reserve(rskb, MAX_PHONET_HEADER + 3); + return pipe_skb_send(sk, rskb); +} + +struct sk_buff *pep_read(struct sock *sk) +{ + struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); + + if (sk->sk_state == TCP_ESTABLISHED) + pipe_grant_credits(sk); + return skb; +} + static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len) @@ -902,6 +1039,8 @@ static struct proto pep_proto = { .accept = pep_sock_accept, .ioctl = pep_ioctl, .init = pep_init, + .setsockopt = pep_setsockopt, + .getsockopt = pep_getsockopt, .sendmsg = pep_sendmsg, .recvmsg = pep_recvmsg, .backlog_rcv = pep_do_rcv, diff --git a/net/phonet/socket.c b/net/phonet/socket.c index a9c3d1f2e9db..d81740187fb4 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -342,11 +342,11 @@ const struct proto_ops phonet_stream_ops = { .ioctl = pn_socket_ioctl, .listen = pn_socket_listen, .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, #ifdef CONFIG_COMPAT - .compat_setsockopt = sock_no_setsockopt, - .compat_getsockopt = compat_sock_no_getsockopt, + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, #endif .sendmsg = pn_socket_sendmsg, .recvmsg = sock_common_recvmsg, -- cgit v1.2.3 From 13c1d18931ebb5cf407cb348ef2cd6284d68902d Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Sun, 5 Oct 2008 13:33:42 -0700 Subject: xfrm: MIGRATE enhancements (draft-ebalard-mext-pfkey-enhanced-migrate) Provides implementation of the enhancements of XFRM/PF_KEY MIGRATE mechanism specified in draft-ebalard-mext-pfkey-enhanced-migrate-00. Defines associated PF_KEY SADB_X_EXT_KMADDRESS extension and XFRM/netlink XFRMA_KMADDRESS attribute. Signed-off-by: Arnaud Ebalard Signed-off-by: David S. Miller --- include/linux/pfkeyv2.h | 13 +++++++- include/linux/xfrm.h | 10 ++++++ include/net/xfrm.h | 15 +++++++-- net/key/af_key.c | 86 +++++++++++++++++++++++++++++++++++++++---------- net/xfrm/xfrm_policy.c | 5 +-- net/xfrm/xfrm_state.c | 5 +-- net/xfrm/xfrm_user.c | 57 +++++++++++++++++++++++++------- 7 files changed, 154 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h index 700725ddcaae..01b262959f2e 100644 --- a/include/linux/pfkeyv2.h +++ b/include/linux/pfkeyv2.h @@ -226,6 +226,15 @@ struct sadb_x_sec_ctx { } __attribute__((packed)); /* sizeof(struct sadb_sec_ctx) = 8 */ +/* Used by MIGRATE to pass addresses IKE will use to perform + * negotiation with the peer */ +struct sadb_x_kmaddress { + uint16_t sadb_x_kmaddress_len; + uint16_t sadb_x_kmaddress_exttype; + uint32_t sadb_x_kmaddress_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_kmaddress) == 8 */ + /* Message types */ #define SADB_RESERVED 0 #define SADB_GETSPI 1 @@ -346,7 +355,9 @@ struct sadb_x_sec_ctx { #define SADB_X_EXT_NAT_T_DPORT 22 #define SADB_X_EXT_NAT_T_OA 23 #define SADB_X_EXT_SEC_CTX 24 -#define SADB_EXT_MAX 24 +/* Used with MIGRATE to pass @ to IKE for negotiation */ +#define SADB_X_EXT_KMADDRESS 25 +#define SADB_EXT_MAX 25 /* Identity Extension values */ #define SADB_IDENTTYPE_RESERVED 0 diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index fb0c215a3051..4bc1e6b86cb2 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -279,6 +279,7 @@ enum xfrm_attr_type_t { XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */ XFRMA_MIGRATE, XFRMA_ALG_AEAD, /* struct xfrm_algo_aead */ + XFRMA_KMADDRESS, /* struct xfrm_user_kmaddress */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) @@ -415,6 +416,15 @@ struct xfrm_user_report { struct xfrm_selector sel; }; +/* Used by MIGRATE to pass addresses IKE should use to perform + * SA negotiation with the peer */ +struct xfrm_user_kmaddress { + xfrm_address_t local; + xfrm_address_t remote; + __u32 reserved; + __u16 family; +}; + struct xfrm_user_migrate { xfrm_address_t old_daddr; xfrm_address_t old_saddr; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b98d2056f27f..11c890ad8ebb 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -492,6 +492,13 @@ struct xfrm_policy struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH]; }; +struct xfrm_kmaddress { + xfrm_address_t local; + xfrm_address_t remote; + u32 reserved; + u16 family; +}; + struct xfrm_migrate { xfrm_address_t old_daddr; xfrm_address_t old_saddr; @@ -531,7 +538,7 @@ struct xfrm_mgr int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); int (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c); int (*report)(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr); - int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles); + int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, struct xfrm_kmaddress *k); }; extern int xfrm_register_km(struct xfrm_mgr *km); @@ -1432,12 +1439,14 @@ extern int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *xdst, #ifdef CONFIG_XFRM_MIGRATE extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles); + struct xfrm_migrate *m, int num_bundles, + struct xfrm_kmaddress *k); extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m); extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, struct xfrm_migrate *m); extern int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles); + struct xfrm_migrate *m, int num_bundles, + struct xfrm_kmaddress *k); #endif extern wait_queue_head_t km_waitq; diff --git a/net/key/af_key.c b/net/key/af_key.c index 7ae641df70bd..362fe317e1f3 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -398,6 +398,7 @@ static u8 sadb_ext_min_len[] = { [SADB_X_EXT_NAT_T_DPORT] = (u8) sizeof(struct sadb_x_nat_t_port), [SADB_X_EXT_NAT_T_OA] = (u8) sizeof(struct sadb_address), [SADB_X_EXT_SEC_CTX] = (u8) sizeof(struct sadb_x_sec_ctx), + [SADB_X_EXT_KMADDRESS] = (u8) sizeof(struct sadb_x_kmaddress), }; /* Verify sadb_address_{len,prefixlen} against sa_family. */ @@ -2384,24 +2385,21 @@ static int pfkey_sockaddr_pair_size(sa_family_t family) return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2); } -static int parse_sockaddr_pair(struct sadb_x_ipsecrequest *rq, +static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len, xfrm_address_t *saddr, xfrm_address_t *daddr, u16 *family) { - u8 *sa = (u8 *) (rq + 1); int af, socklen; - if (rq->sadb_x_ipsecrequest_len < - pfkey_sockaddr_pair_size(((struct sockaddr *)sa)->sa_family)) + if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family)) return -EINVAL; - af = pfkey_sockaddr_extract((struct sockaddr *) sa, - saddr); + af = pfkey_sockaddr_extract(sa, saddr); if (!af) return -EINVAL; socklen = pfkey_sockaddr_len(af); - if (pfkey_sockaddr_extract((struct sockaddr *) (sa + socklen), + if (pfkey_sockaddr_extract((struct sockaddr *) (((u8 *)sa) + socklen), daddr) != af) return -EINVAL; @@ -2421,7 +2419,9 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, return -EINVAL; /* old endoints */ - err = parse_sockaddr_pair(rq1, &m->old_saddr, &m->old_daddr, + err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1), + rq1->sadb_x_ipsecrequest_len, + &m->old_saddr, &m->old_daddr, &m->old_family); if (err) return err; @@ -2434,7 +2434,9 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, return -EINVAL; /* new endpoints */ - err = parse_sockaddr_pair(rq2, &m->new_saddr, &m->new_daddr, + err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1), + rq2->sadb_x_ipsecrequest_len, + &m->new_saddr, &m->new_daddr, &m->new_family); if (err) return err; @@ -2460,29 +2462,40 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, int i, len, ret, err = -EINVAL; u8 dir; struct sadb_address *sa; + struct sadb_x_kmaddress *kma; struct sadb_x_policy *pol; struct sadb_x_ipsecrequest *rq; struct xfrm_selector sel; struct xfrm_migrate m[XFRM_MAX_DEPTH]; + struct xfrm_kmaddress k; if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1], - ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) || + ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) || !ext_hdrs[SADB_X_EXT_POLICY - 1]) { err = -EINVAL; goto out; } + kma = ext_hdrs[SADB_X_EXT_KMADDRESS - 1]; pol = ext_hdrs[SADB_X_EXT_POLICY - 1]; - if (!pol) { - err = -EINVAL; - goto out; - } if (pol->sadb_x_policy_dir >= IPSEC_DIR_MAX) { err = -EINVAL; goto out; } + if (kma) { + /* convert sadb_x_kmaddress to xfrm_kmaddress */ + k.reserved = kma->sadb_x_kmaddress_reserved; + ret = parse_sockaddr_pair((struct sockaddr *)(kma + 1), + 8*(kma->sadb_x_kmaddress_len) - sizeof(*kma), + &k.local, &k.remote, &k.family); + if (ret < 0) { + err = ret; + goto out; + } + } + dir = pol->sadb_x_policy_dir - 1; memset(&sel, 0, sizeof(sel)); @@ -2527,7 +2540,8 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, goto out; } - return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i); + return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i, + kma ? &k : NULL); out: return err; @@ -3319,6 +3333,32 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type, return 0; } + +static int set_sadb_kmaddress(struct sk_buff *skb, struct xfrm_kmaddress *k) +{ + struct sadb_x_kmaddress *kma; + u8 *sa; + int family = k->family; + int socklen = pfkey_sockaddr_len(family); + int size_req; + + size_req = (sizeof(struct sadb_x_kmaddress) + + pfkey_sockaddr_pair_size(family)); + + kma = (struct sadb_x_kmaddress *)skb_put(skb, size_req); + memset(kma, 0, size_req); + kma->sadb_x_kmaddress_len = size_req / 8; + kma->sadb_x_kmaddress_exttype = SADB_X_EXT_KMADDRESS; + kma->sadb_x_kmaddress_reserved = k->reserved; + + sa = (u8 *)(kma + 1); + if (!pfkey_sockaddr_fill(&k->local, 0, (struct sockaddr *)sa, family) || + !pfkey_sockaddr_fill(&k->remote, 0, (struct sockaddr *)(sa+socklen), family)) + return -EINVAL; + + return 0; +} + static int set_ipsecrequest(struct sk_buff *skb, uint8_t proto, uint8_t mode, int level, uint32_t reqid, uint8_t family, @@ -3351,7 +3391,8 @@ static int set_ipsecrequest(struct sk_buff *skb, #ifdef CONFIG_NET_KEY_MIGRATE static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles) + struct xfrm_migrate *m, int num_bundles, + struct xfrm_kmaddress *k) { int i; int sasize_sel; @@ -3368,6 +3409,12 @@ static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, if (num_bundles <= 0 || num_bundles > XFRM_MAX_DEPTH) return -EINVAL; + if (k != NULL) { + /* addresses for KM */ + size += PFKEY_ALIGN8(sizeof(struct sadb_x_kmaddress) + + pfkey_sockaddr_pair_size(k->family)); + } + /* selector */ sasize_sel = pfkey_sockaddr_size(sel->family); if (!sasize_sel) @@ -3404,6 +3451,10 @@ static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, hdr->sadb_msg_seq = 0; hdr->sadb_msg_pid = 0; + /* Addresses to be used by KM for negotiation, if ext is available */ + if (k != NULL && (set_sadb_kmaddress(skb, k) < 0)) + return -EINVAL; + /* selector src */ set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel); @@ -3449,7 +3500,8 @@ err: } #else static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles) + struct xfrm_migrate *m, int num_bundles, + struct xfrm_kmaddress *k) { return -ENOPROTOOPT; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b7ec08025ffb..832b47c1de80 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2679,7 +2679,8 @@ static int xfrm_migrate_check(struct xfrm_migrate *m, int num_migrate) } int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate) + struct xfrm_migrate *m, int num_migrate, + struct xfrm_kmaddress *k) { int i, err, nx_cur = 0, nx_new = 0; struct xfrm_policy *pol = NULL; @@ -2723,7 +2724,7 @@ int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, } /* Stage 5 - announce */ - km_migrate(sel, dir, type, m, num_migrate); + km_migrate(sel, dir, type, m, num_migrate, k); xfrm_pol_put(pol); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 747fd8c291a7..508337f97249 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1814,7 +1814,8 @@ EXPORT_SYMBOL(km_policy_expired); #ifdef CONFIG_XFRM_MIGRATE int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate) + struct xfrm_migrate *m, int num_migrate, + struct xfrm_kmaddress *k) { int err = -EINVAL; int ret; @@ -1823,7 +1824,7 @@ int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, read_lock(&xfrm_km_lock); list_for_each_entry(km, &xfrm_km_list, list) { if (km->migrate) { - ret = km->migrate(sel, dir, type, m, num_migrate); + ret = km->migrate(sel, dir, type, m, num_migrate, k); if (!ret) err = ret; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 76f75df21e15..4a8a1abb59ee 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1710,12 +1710,23 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, #ifdef CONFIG_XFRM_MIGRATE static int copy_from_user_migrate(struct xfrm_migrate *ma, + struct xfrm_kmaddress *k, struct nlattr **attrs, int *num) { struct nlattr *rt = attrs[XFRMA_MIGRATE]; struct xfrm_user_migrate *um; int i, num_migrate; + if (k != NULL) { + struct xfrm_user_kmaddress *uk; + + uk = nla_data(attrs[XFRMA_KMADDRESS]); + memcpy(&k->local, &uk->local, sizeof(k->local)); + memcpy(&k->remote, &uk->remote, sizeof(k->remote)); + k->family = uk->family; + k->reserved = uk->reserved; + } + um = nla_data(rt); num_migrate = nla_len(rt) / sizeof(*um); @@ -1745,6 +1756,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, { struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); struct xfrm_migrate m[XFRM_MAX_DEPTH]; + struct xfrm_kmaddress km, *kmp; u8 type; int err; int n = 0; @@ -1752,19 +1764,20 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, if (attrs[XFRMA_MIGRATE] == NULL) return -EINVAL; + kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; + err = copy_from_user_policy_type(&type, attrs); if (err) return err; - err = copy_from_user_migrate((struct xfrm_migrate *)m, - attrs, &n); + err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); if (err) return err; if (!n) return 0; - xfrm_migrate(&pi->sel, pi->dir, type, m, n); + xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); return 0; } @@ -1795,16 +1808,30 @@ static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); } -static inline size_t xfrm_migrate_msgsize(int num_migrate) +static int copy_to_user_kmaddress(struct xfrm_kmaddress *k, struct sk_buff *skb) +{ + struct xfrm_user_kmaddress uk; + + memset(&uk, 0, sizeof(uk)); + uk.family = k->family; + uk.reserved = k->reserved; + memcpy(&uk.local, &k->local, sizeof(uk.local)); + memcpy(&uk.remote, &k->local, sizeof(uk.remote)); + + return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); +} + +static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) { return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) - + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) - + userpolicy_type_attrsize(); + + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) + + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) + + userpolicy_type_attrsize(); } static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, - int num_migrate, struct xfrm_selector *sel, - u8 dir, u8 type) + int num_migrate, struct xfrm_kmaddress *k, + struct xfrm_selector *sel, u8 dir, u8 type) { struct xfrm_migrate *mp; struct xfrm_userpolicy_id *pol_id; @@ -1821,6 +1848,9 @@ static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); pol_id->dir = dir; + if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0)) + goto nlmsg_failure; + if (copy_to_user_policy_type(type, skb) < 0) goto nlmsg_failure; @@ -1836,23 +1866,25 @@ nlmsg_failure: } static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate) + struct xfrm_migrate *m, int num_migrate, + struct xfrm_kmaddress *k) { struct sk_buff *skb; - skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate), GFP_ATOMIC); + skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); if (skb == NULL) return -ENOMEM; /* build migrate */ - if (build_migrate(skb, m, num_migrate, sel, dir, type) < 0) + if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) BUG(); return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); } #else static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate) + struct xfrm_migrate *m, int num_migrate, + struct xfrm_kmaddress *k) { return -ENOPROTOOPT; } @@ -1901,6 +1933,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, + [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, }; static struct xfrm_link { -- cgit v1.2.3 From 1e19b16a30c34c042f1eaa23db4c99bfad1dac0e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 6 Oct 2008 14:15:24 +0200 Subject: AMD IOMMU: use iommu_device_max_index, fix include/linux/iommu-helper.h has no header guards, which breaks sparc64 build. Add them. Signed-off-by: Thomas Gleixner Cc: Joerg Roedel Cc: FUJITA Tomonori Signed-off-by: Ingo Molnar --- include/linux/iommu-helper.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h index 786539e432d7..a6d0586e2bf7 100644 --- a/include/linux/iommu-helper.h +++ b/include/linux/iommu-helper.h @@ -1,3 +1,6 @@ +#ifndef _LINUX_IOMMU_HELPER_H +#define _LINUX_IOMMU_HELPER_H + static inline unsigned long iommu_device_max_index(unsigned long size, unsigned long offset, u64 dma_mask) @@ -19,3 +22,5 @@ extern unsigned long iommu_area_alloc(unsigned long *map, unsigned long size, unsigned long align_mask); extern void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr); + +#endif -- cgit v1.2.3 From 64be8608c163bd480cf5ec4b34366f11e0f3c87f Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Mon, 6 Oct 2008 14:45:18 -0500 Subject: svcrdma: Add FRMR get/put services Add services for the allocating, freeing, and unmapping Fast Reg MR. These services will be used by the transport connection setup, send and receive routines. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 3 + net/sunrpc/xprtrdma/svc_rdma_transport.c | 116 +++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 49e458d98945..34252683671c 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -214,6 +214,9 @@ extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); extern struct svc_rdma_req_map *svc_rdma_get_req_map(void); extern void svc_rdma_put_req_map(struct svc_rdma_req_map *); +extern struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *); +extern void svc_rdma_put_frmr(struct svcxprt_rdma *, + struct svc_rdma_fastreg_mr *); extern void svc_sq_reap(struct svcxprt_rdma *); extern void svc_rq_reap(struct svcxprt_rdma *); extern struct svc_xprt_class svc_rdma_class; diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 900cb69728c6..f0b5c5f2f629 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -100,6 +100,7 @@ struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) ctxt->xprt = xprt; INIT_LIST_HEAD(&ctxt->dto_q); ctxt->count = 0; + ctxt->frmr = NULL; atomic_inc(&xprt->sc_ctxt_used); return ctxt; } @@ -109,11 +110,19 @@ static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) struct svcxprt_rdma *xprt = ctxt->xprt; int i; for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) { - atomic_dec(&xprt->sc_dma_used); - ib_dma_unmap_single(xprt->sc_cm_id->device, - ctxt->sge[i].addr, - ctxt->sge[i].length, - ctxt->direction); + /* + * Unmap the DMA addr in the SGE if the lkey matches + * the sc_dma_lkey, otherwise, ignore it since it is + * an FRMR lkey and will be unmapped later when the + * last WR that uses it completes. + */ + if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) { + atomic_dec(&xprt->sc_dma_used); + ib_dma_unmap_single(xprt->sc_cm_id->device, + ctxt->sge[i].addr, + ctxt->sge[i].length, + ctxt->direction); + } } } @@ -150,6 +159,7 @@ struct svc_rdma_req_map *svc_rdma_get_req_map(void) schedule_timeout_uninterruptible(msecs_to_jiffies(500)); } map->count = 0; + map->frmr = NULL; return map; } @@ -425,10 +435,12 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, INIT_LIST_HEAD(&cma_xprt->sc_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_read_complete_q); + INIT_LIST_HEAD(&cma_xprt->sc_frmr_q); init_waitqueue_head(&cma_xprt->sc_send_wait); spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); + spin_lock_init(&cma_xprt->sc_frmr_q_lock); cma_xprt->sc_ord = svcrdma_ord; @@ -686,6 +698,97 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, return ERR_PTR(ret); } +static struct svc_rdma_fastreg_mr *rdma_alloc_frmr(struct svcxprt_rdma *xprt) +{ + struct ib_mr *mr; + struct ib_fast_reg_page_list *pl; + struct svc_rdma_fastreg_mr *frmr; + + frmr = kmalloc(sizeof(*frmr), GFP_KERNEL); + if (!frmr) + goto err; + + mr = ib_alloc_fast_reg_mr(xprt->sc_pd, RPCSVC_MAXPAGES); + if (!mr) + goto err_free_frmr; + + pl = ib_alloc_fast_reg_page_list(xprt->sc_cm_id->device, + RPCSVC_MAXPAGES); + if (!pl) + goto err_free_mr; + + frmr->mr = mr; + frmr->page_list = pl; + INIT_LIST_HEAD(&frmr->frmr_list); + return frmr; + + err_free_mr: + ib_dereg_mr(mr); + err_free_frmr: + kfree(frmr); + err: + return ERR_PTR(-ENOMEM); +} + +static void rdma_dealloc_frmr_q(struct svcxprt_rdma *xprt) +{ + struct svc_rdma_fastreg_mr *frmr; + + while (!list_empty(&xprt->sc_frmr_q)) { + frmr = list_entry(xprt->sc_frmr_q.next, + struct svc_rdma_fastreg_mr, frmr_list); + list_del_init(&frmr->frmr_list); + ib_dereg_mr(frmr->mr); + ib_free_fast_reg_page_list(frmr->page_list); + kfree(frmr); + } +} + +struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_fastreg_mr *frmr = NULL; + + spin_lock_bh(&rdma->sc_frmr_q_lock); + if (!list_empty(&rdma->sc_frmr_q)) { + frmr = list_entry(rdma->sc_frmr_q.next, + struct svc_rdma_fastreg_mr, frmr_list); + list_del_init(&frmr->frmr_list); + frmr->map_len = 0; + frmr->page_list_len = 0; + } + spin_unlock_bh(&rdma->sc_frmr_q_lock); + if (frmr) + return frmr; + + return rdma_alloc_frmr(rdma); +} + +static void frmr_unmap_dma(struct svcxprt_rdma *xprt, + struct svc_rdma_fastreg_mr *frmr) +{ + int page_no; + for (page_no = 0; page_no < frmr->page_list_len; page_no++) { + dma_addr_t addr = frmr->page_list->page_list[page_no]; + if (ib_dma_mapping_error(frmr->mr->device, addr)) + continue; + atomic_dec(&xprt->sc_dma_used); + ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE, + frmr->direction); + } +} + +void svc_rdma_put_frmr(struct svcxprt_rdma *rdma, + struct svc_rdma_fastreg_mr *frmr) +{ + if (frmr) { + frmr_unmap_dma(rdma, frmr); + spin_lock_bh(&rdma->sc_frmr_q_lock); + BUG_ON(!list_empty(&frmr->frmr_list)); + list_add(&frmr->frmr_list, &rdma->sc_frmr_q); + spin_unlock_bh(&rdma->sc_frmr_q_lock); + } +} + /* * This is the xpo_recvfrom function for listening endpoints. Its * purpose is to accept incoming connections. The CMA callback handler @@ -961,6 +1064,9 @@ static void __svc_rdma_free(struct work_struct *work) WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0); WARN_ON(atomic_read(&rdma->sc_dma_used) != 0); + /* De-allocate fastreg mr */ + rdma_dealloc_frmr_q(rdma); + /* Destroy the QP if present (not a listener) */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) ib_destroy_qp(rdma->sc_qp); -- cgit v1.2.3 From e1183210625cc8e02ce13eec78fb7a246567fc59 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Fri, 3 Oct 2008 15:22:18 -0500 Subject: svcrdma: Add a service to register a Fast Reg MR with the device Fast Reg MR introduces a new WR type. Add a service to register the region with the adapter and update the completion handling to support completions with a NULL WR context. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_transport.c | 111 +++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 34252683671c..1402d193b393 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -214,6 +214,7 @@ extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); extern struct svc_rdma_req_map *svc_rdma_get_req_map(void); extern void svc_rdma_put_req_map(struct svc_rdma_req_map *); +extern int svc_rdma_fastreg(struct svcxprt_rdma *, struct svc_rdma_fastreg_mr *); extern struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *); extern void svc_rdma_put_frmr(struct svcxprt_rdma *, struct svc_rdma_fastreg_mr *); diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index a8ec4b1eec58..c3e8db0eeb3b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -325,6 +325,45 @@ static void rq_cq_reap(struct svcxprt_rdma *xprt) svc_xprt_enqueue(&xprt->sc_xprt); } +/* + * Processs a completion context + */ +static void process_context(struct svcxprt_rdma *xprt, + struct svc_rdma_op_ctxt *ctxt) +{ + svc_rdma_unmap_dma(ctxt); + + switch (ctxt->wr_op) { + case IB_WR_SEND: + svc_rdma_put_context(ctxt, 1); + break; + + case IB_WR_RDMA_WRITE: + svc_rdma_put_context(ctxt, 0); + break; + + case IB_WR_RDMA_READ: + if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { + struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; + BUG_ON(!read_hdr); + spin_lock_bh(&xprt->sc_rq_dto_lock); + set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); + list_add_tail(&read_hdr->dto_q, + &xprt->sc_read_complete_q); + spin_unlock_bh(&xprt->sc_rq_dto_lock); + svc_xprt_enqueue(&xprt->sc_xprt); + } + svc_rdma_put_context(ctxt, 0); + break; + + default: + printk(KERN_ERR "svcrdma: unexpected completion type, " + "opcode=%d\n", + ctxt->wr_op); + break; + } +} + /* * Send Queue Completion Handler - potentially called on interrupt context. * @@ -337,17 +376,12 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt) struct ib_cq *cq = xprt->sc_sq_cq; int ret; - if (!test_and_clear_bit(RDMAXPRT_SQ_PENDING, &xprt->sc_flags)) return; ib_req_notify_cq(xprt->sc_sq_cq, IB_CQ_NEXT_COMP); atomic_inc(&rdma_stat_sq_poll); while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) { - ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id; - xprt = ctxt->xprt; - - svc_rdma_unmap_dma(ctxt); if (wc.status != IB_WC_SUCCESS) /* Close the transport */ set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags); @@ -356,35 +390,10 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt) atomic_dec(&xprt->sc_sq_count); wake_up(&xprt->sc_send_wait); - switch (ctxt->wr_op) { - case IB_WR_SEND: - svc_rdma_put_context(ctxt, 1); - break; - - case IB_WR_RDMA_WRITE: - svc_rdma_put_context(ctxt, 0); - break; - - case IB_WR_RDMA_READ: - if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { - struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; - BUG_ON(!read_hdr); - spin_lock_bh(&xprt->sc_rq_dto_lock); - set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); - list_add_tail(&read_hdr->dto_q, - &xprt->sc_read_complete_q); - spin_unlock_bh(&xprt->sc_rq_dto_lock); - svc_xprt_enqueue(&xprt->sc_xprt); - } - svc_rdma_put_context(ctxt, 0); - break; + ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id; + if (ctxt) + process_context(xprt, ctxt); - default: - printk(KERN_ERR "svcrdma: unexpected completion type, " - "opcode=%d, status=%d\n", - wc.opcode, wc.status); - break; - } svc_xprt_put(&xprt->sc_xprt); } @@ -1184,6 +1193,40 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt) return 1; } +/* + * Attempt to register the kvec representing the RPC memory with the + * device. + * + * Returns: + * NULL : The device does not support fastreg or there were no more + * fastreg mr. + * frmr : The kvec register request was successfully posted. + * <0 : An error was encountered attempting to register the kvec. + */ +int svc_rdma_fastreg(struct svcxprt_rdma *xprt, + struct svc_rdma_fastreg_mr *frmr) +{ + struct ib_send_wr fastreg_wr; + u8 key; + + /* Bump the key */ + key = (u8)(frmr->mr->lkey & 0x000000FF); + ib_update_fast_reg_key(frmr->mr, ++key); + + /* Prepare FASTREG WR */ + memset(&fastreg_wr, 0, sizeof fastreg_wr); + fastreg_wr.opcode = IB_WR_FAST_REG_MR; + fastreg_wr.send_flags = IB_SEND_SIGNALED; + fastreg_wr.wr.fast_reg.iova_start = (unsigned long)frmr->kva; + fastreg_wr.wr.fast_reg.page_list = frmr->page_list; + fastreg_wr.wr.fast_reg.page_list_len = frmr->page_list_len; + fastreg_wr.wr.fast_reg.page_shift = PAGE_SHIFT; + fastreg_wr.wr.fast_reg.length = frmr->map_len; + fastreg_wr.wr.fast_reg.access_flags = frmr->access_flags; + fastreg_wr.wr.fast_reg.rkey = frmr->mr->lkey; + return svc_rdma_send(xprt, &fastreg_wr); +} + int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr) { struct ib_send_wr *bad_wr; @@ -1193,8 +1236,6 @@ int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr) return -ENOTCONN; BUG_ON(wr->send_flags != IB_SEND_SIGNALED); - BUG_ON(((struct svc_rdma_op_ctxt *)(unsigned long)wr->wr_id)->wr_op != - wr->opcode); /* If the SQ is full, wait until an SQ entry is available */ while (1) { spin_lock_bh(&xprt->sc_lock); -- cgit v1.2.3 From 146b6df6a537939570c5772ebd7db826fdbd5d82 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Tue, 12 Aug 2008 15:12:10 -0500 Subject: svcrdma: Modify the RPC recv path to use FRMR when available RPCRDMA requests that specify a read-list are fetched with RDMA_READ. Using an FRMR to map the data sink improves NFSRDMA security on transports that place the RDMA_READ data sink LKEY on the wire because the valid lifetime of the MR is only the duration of the RDMA_READ. The LKEY is invalidated when the last RDMA_READ WR completes. Mapping the data sink also allows for very large amounts to data to be fetched with a single WR, so if the client is also using FRMR, the entire RPC read-list can be fetched with a single WR. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 187 +++++++++++++++++++++++++++---- net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 +- 3 files changed, 171 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 1402d193b393..c14fe86dac59 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -212,6 +212,7 @@ extern int svc_rdma_post_recv(struct svcxprt_rdma *); extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *); extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); +extern void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt); extern struct svc_rdma_req_map *svc_rdma_get_req_map(void); extern void svc_rdma_put_req_map(struct svc_rdma_req_map *); extern int svc_rdma_fastreg(struct svcxprt_rdma *, struct svc_rdma_fastreg_mr *); diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 74de31a06616..a4756576d687 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -116,7 +116,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, * * Assumptions: * - chunk[0]->position points to pages[0] at an offset of 0 - * - pages[] is not physically or virtually contigous and consists of + * - pages[] is not physically or virtually contiguous and consists of * PAGE_SIZE elements. * * Output: @@ -125,7 +125,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, * chunk in the read list * */ -static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, +static int map_read_chunks(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, struct svc_rdma_op_ctxt *head, struct rpcrdma_msg *rmsgp, @@ -211,26 +211,128 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, return sge_no; } -static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, - struct svc_rdma_op_ctxt *ctxt, - struct kvec *vec, - u64 *sgl_offset, - int count) +/* Map a read-chunk-list to an XDR and fast register the page-list. + * + * Assumptions: + * - chunk[0] position points to pages[0] at an offset of 0 + * - pages[] will be made physically contiguous by creating a one-off memory + * region using the fastreg verb. + * - byte_count is # of bytes in read-chunk-list + * - ch_count is # of chunks in read-chunk-list + * + * Output: + * - sge array pointing into pages[] array. + * - chunk_sge array specifying sge index and count for each + * chunk in the read list + */ +static int fast_reg_read_chunks(struct svcxprt_rdma *xprt, + struct svc_rqst *rqstp, + struct svc_rdma_op_ctxt *head, + struct rpcrdma_msg *rmsgp, + struct svc_rdma_req_map *rpl_map, + struct svc_rdma_req_map *chl_map, + int ch_count, + int byte_count) +{ + int page_no; + int ch_no; + u32 offset; + struct rpcrdma_read_chunk *ch; + struct svc_rdma_fastreg_mr *frmr; + int ret = 0; + + frmr = svc_rdma_get_frmr(xprt); + if (IS_ERR(frmr)) + return -ENOMEM; + + head->frmr = frmr; + head->arg.head[0] = rqstp->rq_arg.head[0]; + head->arg.tail[0] = rqstp->rq_arg.tail[0]; + head->arg.pages = &head->pages[head->count]; + head->hdr_count = head->count; /* save count of hdr pages */ + head->arg.page_base = 0; + head->arg.page_len = byte_count; + head->arg.len = rqstp->rq_arg.len + byte_count; + head->arg.buflen = rqstp->rq_arg.buflen + byte_count; + + /* Fast register the page list */ + frmr->kva = page_address(rqstp->rq_arg.pages[0]); + frmr->direction = DMA_FROM_DEVICE; + frmr->access_flags = (IB_ACCESS_LOCAL_WRITE|IB_ACCESS_REMOTE_WRITE); + frmr->map_len = byte_count; + frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT; + for (page_no = 0; page_no < frmr->page_list_len; page_no++) { + frmr->page_list->page_list[page_no] = + ib_dma_map_single(xprt->sc_cm_id->device, + page_address(rqstp->rq_arg.pages[page_no]), + PAGE_SIZE, DMA_TO_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + frmr->page_list->page_list[page_no])) + goto fatal_err; + atomic_inc(&xprt->sc_dma_used); + head->arg.pages[page_no] = rqstp->rq_arg.pages[page_no]; + } + head->count += page_no; + + /* rq_respages points one past arg pages */ + rqstp->rq_respages = &rqstp->rq_arg.pages[page_no]; + + /* Create the reply and chunk maps */ + offset = 0; + ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0]; + for (ch_no = 0; ch_no < ch_count; ch_no++) { + rpl_map->sge[ch_no].iov_base = frmr->kva + offset; + rpl_map->sge[ch_no].iov_len = ch->rc_target.rs_length; + chl_map->ch[ch_no].count = 1; + chl_map->ch[ch_no].start = ch_no; + offset += ch->rc_target.rs_length; + ch++; + } + + ret = svc_rdma_fastreg(xprt, frmr); + if (ret) + goto fatal_err; + + return ch_no; + + fatal_err: + printk("svcrdma: error fast registering xdr for xprt %p", xprt); + svc_rdma_put_frmr(xprt, frmr); + return -EIO; +} + +static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, + struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_fastreg_mr *frmr, + struct kvec *vec, + u64 *sgl_offset, + int count) { int i; ctxt->count = count; ctxt->direction = DMA_FROM_DEVICE; for (i = 0; i < count; i++) { - atomic_inc(&xprt->sc_dma_used); - ctxt->sge[i].addr = - ib_dma_map_single(xprt->sc_cm_id->device, - vec[i].iov_base, vec[i].iov_len, - DMA_FROM_DEVICE); + ctxt->sge[i].length = 0; /* in case map fails */ + if (!frmr) { + ctxt->sge[i].addr = + ib_dma_map_single(xprt->sc_cm_id->device, + vec[i].iov_base, + vec[i].iov_len, + DMA_FROM_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + ctxt->sge[i].addr)) + return -EINVAL; + ctxt->sge[i].lkey = xprt->sc_dma_lkey; + atomic_inc(&xprt->sc_dma_used); + } else { + ctxt->sge[i].addr = (unsigned long)vec[i].iov_base; + ctxt->sge[i].lkey = frmr->mr->lkey; + } ctxt->sge[i].length = vec[i].iov_len; - ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey; *sgl_offset = *sgl_offset + vec[i].iov_len; } + return 0; } static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count) @@ -278,6 +380,7 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, struct svc_rdma_op_ctxt *hdr_ctxt) { struct ib_send_wr read_wr; + struct ib_send_wr inv_wr; int err = 0; int ch_no; int ch_count; @@ -301,9 +404,20 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count); if (ch_count > RPCSVC_MAXPAGES) return -EINVAL; - sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp, - rpl_map, chl_map, - ch_count, byte_count); + + if (!xprt->sc_frmr_pg_list_len) + sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, + rpl_map, chl_map, ch_count, + byte_count); + else + sge_count = fast_reg_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, + rpl_map, chl_map, ch_count, + byte_count); + if (sge_count < 0) { + err = -EIO; + goto out; + } + sgl_offset = 0; ch_no = 0; @@ -312,13 +426,16 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, next_sge: ctxt = svc_rdma_get_context(xprt); ctxt->direction = DMA_FROM_DEVICE; + ctxt->frmr = hdr_ctxt->frmr; + ctxt->read_hdr = NULL; clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); + clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags); /* Prepare READ WR */ memset(&read_wr, 0, sizeof read_wr); - ctxt->wr_op = IB_WR_RDMA_READ; read_wr.wr_id = (unsigned long)ctxt; read_wr.opcode = IB_WR_RDMA_READ; + ctxt->wr_op = read_wr.opcode; read_wr.send_flags = IB_SEND_SIGNALED; read_wr.wr.rdma.rkey = ch->rc_target.rs_handle; read_wr.wr.rdma.remote_addr = @@ -327,10 +444,15 @@ next_sge: read_wr.sg_list = ctxt->sge; read_wr.num_sge = rdma_read_max_sge(xprt, chl_map->ch[ch_no].count); - rdma_set_ctxt_sge(xprt, ctxt, - &rpl_map->sge[chl_map->ch[ch_no].start], - &sgl_offset, - read_wr.num_sge); + err = rdma_set_ctxt_sge(xprt, ctxt, hdr_ctxt->frmr, + &rpl_map->sge[chl_map->ch[ch_no].start], + &sgl_offset, + read_wr.num_sge); + if (err) { + svc_rdma_unmap_dma(ctxt); + svc_rdma_put_context(ctxt, 0); + goto out; + } if (((ch+1)->rc_discrim == 0) && (read_wr.num_sge == chl_map->ch[ch_no].count)) { /* @@ -339,6 +461,29 @@ next_sge: * the client and the RPC needs to be enqueued. */ set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); + if (hdr_ctxt->frmr) { + set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags); + /* + * Invalidate the local MR used to map the data + * sink. + */ + if (xprt->sc_dev_caps & + SVCRDMA_DEVCAP_READ_W_INV) { + read_wr.opcode = + IB_WR_RDMA_READ_WITH_INV; + ctxt->wr_op = read_wr.opcode; + read_wr.ex.invalidate_rkey = + ctxt->frmr->mr->lkey; + } else { + /* Prepare INVALIDATE WR */ + memset(&inv_wr, 0, sizeof inv_wr); + inv_wr.opcode = IB_WR_LOCAL_INV; + inv_wr.send_flags = IB_SEND_SIGNALED; + inv_wr.ex.invalidate_rkey = + hdr_ctxt->frmr->mr->lkey; + read_wr.next = &inv_wr; + } + } ctxt->read_hdr = hdr_ctxt; } /* Post the read */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index f22f58767661..fb0dff5e53ea 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -105,7 +105,7 @@ struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) return ctxt; } -static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) +void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) { struct svcxprt_rdma *xprt = ctxt->xprt; int i; @@ -343,9 +343,12 @@ static void process_context(struct svcxprt_rdma *xprt, break; case IB_WR_RDMA_READ: + case IB_WR_RDMA_READ_WITH_INV: if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; BUG_ON(!read_hdr); + if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags)) + svc_rdma_put_frmr(xprt, ctxt->frmr); spin_lock_bh(&xprt->sc_rq_dto_lock); set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); list_add_tail(&read_hdr->dto_q, -- cgit v1.2.3 From 654bed16cf86a9ef94495d9e6131b7ff7840a3dd Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 7 Oct 2008 14:22:33 -0700 Subject: net: packet split receive api Add some packet-split receive hooks. For one this allows to do NUMA node affine page allocs. Later on these hooks will be extended to do emergency reserve allocations for fragments. Signed-off-by: Peter Zijlstra Signed-off-by: David S. Miller --- include/linux/skbuff.h | 23 +++++++++++++++++++++++ net/core/skbuff.c | 20 ++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 720b688c22b6..2725f4e5a9bf 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -968,6 +968,9 @@ static inline void skb_fill_page_desc(struct sk_buff *skb, int i, skb_shinfo(skb)->nr_frags = i + 1; } +extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, + int off, int size); + #define SKB_PAGE_ASSERT(skb) BUG_ON(skb_shinfo(skb)->nr_frags) #define SKB_FRAG_ASSERT(skb) BUG_ON(skb_shinfo(skb)->frag_list) #define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb)) @@ -1382,6 +1385,26 @@ static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, return __netdev_alloc_skb(dev, length, GFP_ATOMIC); } +extern struct page *__netdev_alloc_page(struct net_device *dev, gfp_t gfp_mask); + +/** + * netdev_alloc_page - allocate a page for ps-rx on a specific device + * @dev: network device to receive on + * + * Allocate a new page node local to the specified device. + * + * %NULL is returned if there is no free memory. + */ +static inline struct page *netdev_alloc_page(struct net_device *dev) +{ + return __netdev_alloc_page(dev, GFP_ATOMIC); +} + +static inline void netdev_free_page(struct net_device *dev, struct page *page) +{ + __free_page(page); +} + /** * skb_clone_writable - is the header of a clone writable * @skb: buffer to check diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 8bd248a64879..7f7bb1a636d9 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -263,6 +263,26 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, return skb; } +struct page *__netdev_alloc_page(struct net_device *dev, gfp_t gfp_mask) +{ + int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1; + struct page *page; + + page = alloc_pages_node(node, gfp_mask, 0); + return page; +} +EXPORT_SYMBOL(__netdev_alloc_page); + +void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, + int size) +{ + skb_fill_page_desc(skb, i, page, off, size); + skb->len += size; + skb->data_len += size; + skb->truesize += size; +} +EXPORT_SYMBOL(skb_add_rx_frag); + /** * dev_alloc_skb - allocate an skbuff for receiving * @length: length to allocate -- cgit v1.2.3 From 33f5f57eeb0c6386fdd85f9c690dc8d700ba7928 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 7 Oct 2008 14:43:06 -0700 Subject: tcp: kill pointless urg_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It all started from me noticing that this urgent check in tcp_clean_rtx_queue is unnecessarily inside the loop. Then I took a longer look to it and found out that the users of urg_mode can trivially do without, well almost, there was one gotcha. Bonus: those funny people who use urg with >= 2^31 write_seq - snd_una could now rejoice too (that's the only purpose for the between being there, otherwise a simple compare would have done the thing). Not that I assume that the rest of the tcp code happily lives with such mind-boggling numbers :-). Alas, it turned out to be impossible to set wmem to such numbers anyway, yes I really tried a big sendfile after setting some wmem but nothing happened :-). ...Tcp_wmem is int and so is sk_sndbuf... So I hacked a bit variable to long and found out that it seems to work... :-) Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/tcp.h | 9 ++++----- net/ipv4/tcp.c | 4 +--- net/ipv4/tcp_input.c | 11 ++++++----- net/ipv4/tcp_minisocks.c | 1 + net/ipv4/tcp_output.c | 18 ++++++++++++------ 5 files changed, 24 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 767290628292..fe77e1499ab7 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -312,8 +312,11 @@ struct tcp_sock { u32 retrans_out; /* Retransmitted packets out */ u16 urg_data; /* Saved octet of OOB data and control flags */ - u8 urg_mode; /* In urgent mode */ u8 ecn_flags; /* ECN status bits. */ + u8 reordering; /* Packet reordering metric. */ + u32 snd_up; /* Urgent pointer */ + + u8 keepalive_probes; /* num of allowed keep alive probes */ /* * Options received (usually on last packet, some only on SYN packets). */ @@ -361,8 +364,6 @@ struct tcp_sock { u32 lost_retrans_low; /* Sent seq after any rxmit (lowest) */ - u8 reordering; /* Packet reordering metric. */ - u8 keepalive_probes; /* num of allowed keep alive probes */ u32 prior_ssthresh; /* ssthresh saved at recovery start */ u32 high_seq; /* snd_nxt at onset of congestion */ @@ -374,8 +375,6 @@ struct tcp_sock { u32 total_retrans; /* Total retransmits for entire connection */ u32 urg_seq; /* Seq of received urgent pointer */ - u32 snd_up; /* Urgent pointer */ - unsigned int keepalive_time; /* time before keep alive takes place */ unsigned int keepalive_intvl; /* time interval between keep alive probes */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 7d3fe571d15f..eccb7165a80c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -497,10 +497,8 @@ static inline void skb_entail(struct sock *sk, struct sk_buff *skb) static inline void tcp_mark_urg(struct tcp_sock *tp, int flags, struct sk_buff *skb) { - if (flags & MSG_OOB) { - tp->urg_mode = 1; + if (flags & MSG_OOB) tp->snd_up = tp->write_seq; - } } static inline void tcp_push(struct sock *sk, int flags, int mss_now, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 3b76bce769dd..c19f429dc443 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2836,7 +2836,8 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) * is before the ack sequence we can discard it as it's confirmed to have * arrived at the other end. */ -static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets) +static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, + u32 prior_snd_una) { struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); @@ -2903,9 +2904,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets) if (sacked & TCPCB_LOST) tp->lost_out -= acked_pcount; - if (unlikely(tp->urg_mode && !before(end_seq, tp->snd_up))) - tp->urg_mode = 0; - tp->packets_out -= acked_pcount; pkts_acked += acked_pcount; @@ -2935,6 +2933,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets) tp->lost_skb_hint = NULL; } + if (likely(between(tp->snd_up, prior_snd_una, tp->snd_una))) + tp->snd_up = tp->snd_una; + if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) flag |= FLAG_SACK_RENEGING; @@ -3311,7 +3312,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) goto no_queue; /* See if we can take anything off of the retransmit queue. */ - flag |= tcp_clean_rtx_queue(sk, prior_fackets); + flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una); if (tp->frto_counter) frto_cwnd = tcp_process_frto(sk, flag); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f976fc57892c..779f2e9d0689 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -395,6 +395,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->pred_flags = 0; newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1; newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = treq->snt_isn + 1; + newtp->snd_up = treq->snt_isn + 1; tcp_prequeue_init(newtp); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 493553c71d32..990a58493235 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -345,6 +345,11 @@ static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) TCP_SKB_CB(skb)->end_seq = seq; } +static inline int tcp_urg_mode(const struct tcp_sock *tp) +{ + return tp->snd_una != tp->snd_up; +} + #define OPTION_SACK_ADVERTISE (1 << 0) #define OPTION_TS (1 << 1) #define OPTION_MD5 (1 << 2) @@ -646,7 +651,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, th->check = 0; th->urg_ptr = 0; - if (unlikely(tp->urg_mode && + /* The urg_mode check is necessary during a below snd_una win probe */ + if (unlikely(tcp_urg_mode(tp) && between(tp->snd_up, tcb->seq + 1, tcb->seq + 0xFFFF))) { th->urg_ptr = htons(tp->snd_up - tcb->seq); th->urg = 1; @@ -1012,7 +1018,7 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu) /* Compute the current effective MSS, taking SACKs and IP options, * and even PMTU discovery events into account. * - * LARGESEND note: !urg_mode is overkill, only frames up to snd_up + * LARGESEND note: !tcp_urg_mode is overkill, only frames up to snd_up * cannot be large. However, taking into account rare use of URG, this * is not a big flaw. */ @@ -1029,7 +1035,7 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed) mss_now = tp->mss_cache; - if (large_allowed && sk_can_gso(sk) && !tp->urg_mode) + if (large_allowed && sk_can_gso(sk) && !tcp_urg_mode(tp)) doing_tso = 1; if (dst) { @@ -1193,7 +1199,7 @@ static inline int tcp_nagle_test(struct tcp_sock *tp, struct sk_buff *skb, /* Don't use the nagle rule for urgent data (or for the final FIN). * Nagle can be ignored during F-RTO too (see RFC4138). */ - if (tp->urg_mode || (tp->frto_counter == 2) || + if (tcp_urg_mode(tp) || (tp->frto_counter == 2) || (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) return 1; @@ -2358,6 +2364,7 @@ static void tcp_connect_init(struct sock *sk) tcp_init_wl(tp, tp->write_seq, 0); tp->snd_una = tp->write_seq; tp->snd_sml = tp->write_seq; + tp->snd_up = tp->write_seq; tp->rcv_nxt = 0; tp->rcv_wup = 0; tp->copied_seq = 0; @@ -2567,8 +2574,7 @@ int tcp_write_wakeup(struct sock *sk) tcp_event_new_data_sent(sk, skb); return err; } else { - if (tp->urg_mode && - between(tp->snd_up, tp->snd_una + 1, tp->snd_una + 0xFFFF)) + if (between(tp->snd_up, tp->snd_una + 1, tp->snd_una + 0xFFFF)) tcp_xmit_probe_skb(sk, 1); return tcp_xmit_probe_skb(sk, 0); } -- cgit v1.2.3 From b8bae41ed6a53cce56c50811a91cd963e3187d1c Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Tue, 7 Oct 2008 15:34:37 -0700 Subject: ipv4: add mc_count to in_device. This patch add mc_count to struct in_device and updates increment/decrement/initilaize of this field in IPv4 and in IPv6. - Also printing the vfs /proc entry (/proc/net/igmp) is adjusted to use the new mc_count. Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 1 + net/ipv4/igmp.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index c6f51ad52d5b..06fcdb45106b 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -25,6 +25,7 @@ struct in_device struct in_ifaddr *ifa_list; /* IP ifaddr chain */ rwlock_t mc_list_lock; struct ip_mc_list *mc_list; /* IP multicast filter chain */ + int mc_count; /* Number of installed mcasts */ spinlock_t mc_tomb_lock; struct ip_mc_list *mc_tomb; unsigned long mr_v1_seen; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index f70fac612596..7f9e337e3908 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1234,6 +1234,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) write_lock_bh(&in_dev->mc_list_lock); im->next=in_dev->mc_list; in_dev->mc_list=im; + in_dev->mc_count++; write_unlock_bh(&in_dev->mc_list_lock); #ifdef CONFIG_IP_MULTICAST igmpv3_del_delrec(in_dev, im->multiaddr); @@ -1282,6 +1283,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) if (--i->users == 0) { write_lock_bh(&in_dev->mc_list_lock); *ip = i->next; + in_dev->mc_count--; write_unlock_bh(&in_dev->mc_list_lock); igmp_group_dropped(i); @@ -1330,6 +1332,7 @@ void ip_mc_init_dev(struct in_device *in_dev) setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire, (unsigned long)in_dev); in_dev->mr_ifc_count = 0; + in_dev->mc_count = 0; setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, (unsigned long)in_dev); in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; @@ -1369,8 +1372,8 @@ void ip_mc_destroy_dev(struct in_device *in_dev) write_lock_bh(&in_dev->mc_list_lock); while ((i = in_dev->mc_list) != NULL) { in_dev->mc_list = i->next; + in_dev->mc_count--; write_unlock_bh(&in_dev->mc_list_lock); - igmp_group_dropped(i); ip_ma_put(i); @@ -2383,7 +2386,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) if (state->in_dev->mc_list == im) { seq_printf(seq, "%d\t%-10s: %5d %7s\n", - state->dev->ifindex, state->dev->name, state->dev->mc_count, querier); + state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); } seq_printf(seq, -- cgit v1.2.3 From 76108cea065cda58366d16a7eb6ca90d717a1396 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:00 +0200 Subject: netfilter: Use unsigned types for hooknum and pf vars and (try to) consistently use u_int8_t for the L3 family. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 30 ++++++++-------- include/linux/netfilter/x_tables.h | 28 +++++++-------- include/net/netfilter/nf_conntrack_core.h | 2 +- include/net/netfilter/nf_conntrack_expect.h | 2 +- include/net/netfilter/nf_conntrack_l4proto.h | 4 +-- include/net/netfilter/nf_log.h | 8 ++--- include/net/netfilter/nf_queue.h | 6 ++-- net/bridge/br_netfilter.c | 4 +-- net/bridge/netfilter/ebt_log.c | 2 +- net/bridge/netfilter/ebt_ulog.c | 2 +- net/ipv4/netfilter/ipt_LOG.c | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 2 +- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 4 +-- net/ipv6/netfilter/ip6t_LOG.c | 2 +- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 4 +-- net/netfilter/core.c | 4 +-- net/netfilter/nf_conntrack_core.c | 6 ++-- net/netfilter/nf_conntrack_expect.c | 2 +- net/netfilter/nf_conntrack_h323_main.c | 3 +- net/netfilter/nf_conntrack_proto_dccp.c | 4 +-- net/netfilter/nf_conntrack_proto_generic.c | 2 +- net/netfilter/nf_conntrack_proto_gre.c | 2 +- net/netfilter/nf_conntrack_proto_sctp.c | 2 +- net/netfilter/nf_conntrack_proto_tcp.c | 6 ++-- net/netfilter/nf_conntrack_proto_udp.c | 4 +-- net/netfilter/nf_conntrack_proto_udplite.c | 4 +-- net/netfilter/nf_internals.h | 4 +-- net/netfilter/nf_log.c | 6 ++-- net/netfilter/nf_queue.c | 10 +++--- net/netfilter/nf_sockopt.c | 15 ++++---- net/netfilter/nfnetlink_log.c | 4 +-- net/netfilter/x_tables.c | 47 ++++++++++++++------------ net/netfilter/xt_connlimit.c | 2 +- net/netfilter/xt_conntrack.c | 8 ++--- net/netfilter/xt_hashlimit.c | 11 +++--- 35 files changed, 127 insertions(+), 121 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 0c5eb7ed8b3f..8c83d2e23bde 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -92,8 +92,8 @@ struct nf_hook_ops /* User fills in from here down. */ nf_hookfn *hook; struct module *owner; - int pf; - int hooknum; + u_int8_t pf; + unsigned int hooknum; /* Hooks are ordered in ascending priority. */ int priority; }; @@ -102,7 +102,7 @@ struct nf_sockopt_ops { struct list_head list; - int pf; + u_int8_t pf; /* Non-inclusive ranges: use 0/0/NULL to never get called. */ int set_optmin; @@ -140,7 +140,7 @@ extern struct ctl_path nf_net_ipv4_netfilter_sysctl_path[]; extern struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]; -int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, +int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), int thresh); @@ -151,7 +151,7 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, * okfn must be invoked by the caller in this case. Any other return * value indicates the packet has been consumed by the hook. */ -static inline int nf_hook_thresh(int pf, unsigned int hook, +static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, @@ -167,7 +167,7 @@ static inline int nf_hook_thresh(int pf, unsigned int hook, return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh); } -static inline int nf_hook(int pf, unsigned int hook, struct sk_buff *skb, +static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *)) { @@ -212,14 +212,14 @@ __ret;}) NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN) /* Call setsockopt() */ -int nf_setsockopt(struct sock *sk, int pf, int optval, char __user *opt, +int nf_setsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt, int len); -int nf_getsockopt(struct sock *sk, int pf, int optval, char __user *opt, +int nf_getsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt, int *len); -int compat_nf_setsockopt(struct sock *sk, int pf, int optval, +int compat_nf_setsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt, int len); -int compat_nf_getsockopt(struct sock *sk, int pf, int optval, +int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt, int *len); /* Call this before modifying an existing packet: ensures it is @@ -292,7 +292,7 @@ extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); extern void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *); static inline void -nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family) +nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) { #ifdef CONFIG_NF_NAT_NEEDED void (*decodefn)(struct sk_buff *, struct flowi *); @@ -315,7 +315,7 @@ extern struct proc_dir_entry *proc_net_netfilter; #else /* !CONFIG_NETFILTER */ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) #define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb) -static inline int nf_hook_thresh(int pf, unsigned int hook, +static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, @@ -324,7 +324,7 @@ static inline int nf_hook_thresh(int pf, unsigned int hook, { return okfn(skb); } -static inline int nf_hook(int pf, unsigned int hook, struct sk_buff *skb, +static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *)) { @@ -332,7 +332,9 @@ static inline int nf_hook(int pf, unsigned int hook, struct sk_buff *skb, } struct flowi; static inline void -nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family) {} +nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) +{ +} #endif /*CONFIG_NETFILTER*/ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 2326296b6f25..6989b22716e6 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -292,7 +292,7 @@ struct xt_table /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; - int af; /* address/protocol family */ + u_int8_t af; /* address/protocol family */ }; #include @@ -346,19 +346,19 @@ extern struct xt_table_info *xt_replace_table(struct xt_table *table, struct xt_table_info *newinfo, int *error); -extern struct xt_match *xt_find_match(int af, const char *name, u8 revision); -extern struct xt_target *xt_find_target(int af, const char *name, u8 revision); -extern struct xt_target *xt_request_find_target(int af, const char *name, +extern struct xt_match *xt_find_match(u8 af, const char *name, u8 revision); +extern struct xt_target *xt_find_target(u8 af, const char *name, u8 revision); +extern struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision); -extern int xt_find_revision(int af, const char *name, u8 revision, int target, - int *err); +extern int xt_find_revision(u8 af, const char *name, u8 revision, + int target, int *err); -extern struct xt_table *xt_find_table_lock(struct net *net, int af, +extern struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, const char *name); extern void xt_table_unlock(struct xt_table *t); -extern int xt_proto_init(struct net *net, int af); -extern void xt_proto_fini(struct net *net, int af); +extern int xt_proto_init(struct net *net, u_int8_t af); +extern void xt_proto_fini(struct net *net, u_int8_t af); extern struct xt_table_info *xt_alloc_table_info(unsigned int size); extern void xt_free_table_info(struct xt_table_info *info); @@ -423,12 +423,12 @@ struct compat_xt_counters_info #define COMPAT_XT_ALIGN(s) (((s) + (__alignof__(struct compat_xt_counters)-1)) \ & ~(__alignof__(struct compat_xt_counters)-1)) -extern void xt_compat_lock(int af); -extern void xt_compat_unlock(int af); +extern void xt_compat_lock(u_int8_t af); +extern void xt_compat_unlock(u_int8_t af); -extern int xt_compat_add_offset(int af, unsigned int offset, short delta); -extern void xt_compat_flush_offsets(int af); -extern short xt_compat_calc_jump(int af, unsigned int offset); +extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, short delta); +extern void xt_compat_flush_offsets(u_int8_t af); +extern short xt_compat_calc_jump(u_int8_t af, unsigned int offset); extern int xt_compat_match_offset(const struct xt_match *match); extern int xt_compat_match_from_user(struct xt_entry_match *m, diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index a81771210934..05760d6a706e 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -20,7 +20,7 @@ /* This header is used to share core functionality between the standalone connection tracking module, and the compatibility layer's use of connection tracking. */ -extern unsigned int nf_conntrack_in(int pf, +extern unsigned int nf_conntrack_in(u_int8_t pf, unsigned int hooknum, struct sk_buff *skb); diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index dfdf4b459475..4c4d894cb9b5 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -86,7 +86,7 @@ void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); /* Allocate space for an expectation: this is mandatory before calling nf_ct_expect_related. You will have to call put afterwards. */ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me); -void nf_ct_expect_init(struct nf_conntrack_expect *, unsigned int, int, +void nf_ct_expect_init(struct nf_conntrack_expect *, unsigned int, u_int8_t, const union nf_inet_addr *, const union nf_inet_addr *, u_int8_t, const __be16 *, const __be16 *); diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 723df9d1cc35..d4376e97bae8 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -39,7 +39,7 @@ struct nf_conntrack_l4proto const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum); /* Called when a new connection for this protocol found; @@ -52,7 +52,7 @@ struct nf_conntrack_l4proto int (*error)(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, - int pf, unsigned int hooknum); + u_int8_t pf, unsigned int hooknum); /* Print out the per-protocol part of the tuple. Return like seq_* */ int (*print_tuple)(struct seq_file *s, diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index 8c6b5ae45534..7182c06974f4 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -28,7 +28,7 @@ struct nf_loginfo { } u; }; -typedef void nf_logfn(unsigned int pf, +typedef void nf_logfn(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, @@ -43,12 +43,12 @@ struct nf_logger { }; /* Function to register/unregister log function. */ -int nf_log_register(int pf, const struct nf_logger *logger); +int nf_log_register(u_int8_t pf, const struct nf_logger *logger); void nf_log_unregister(const struct nf_logger *logger); -void nf_log_unregister_pf(int pf); +void nf_log_unregister_pf(u_int8_t pf); /* Calls the registered backend logging function */ -void nf_log_packet(int pf, +void nf_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index d030044e9235..252fd1010b77 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -8,7 +8,7 @@ struct nf_queue_entry { unsigned int id; struct nf_hook_ops *elem; - int pf; + u_int8_t pf; unsigned int hook; struct net_device *indev; struct net_device *outdev; @@ -24,9 +24,9 @@ struct nf_queue_handler { char *name; }; -extern int nf_register_queue_handler(int pf, +extern int nf_register_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh); -extern int nf_unregister_queue_handler(int pf, +extern int nf_unregister_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh); extern void nf_unregister_queue_handlers(const struct nf_queue_handler *qh); extern void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 6a9a6cd74b1e..a4abed5b4c44 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -657,7 +657,7 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, { struct nf_bridge_info *nf_bridge; struct net_device *parent; - int pf; + u_int8_t pf; if (!skb->nf_bridge) return NF_ACCEPT; @@ -791,7 +791,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb, { struct nf_bridge_info *nf_bridge = skb->nf_bridge; struct net_device *realoutdev = bridge_parent(skb->dev); - int pf; + u_int8_t pf; #ifdef CONFIG_NETFILTER_DEBUG /* Be very paranoid. This probably won't happen anymore, but let's diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 2f430d4ae911..3770cd8a7b3a 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -84,7 +84,7 @@ print_ports(const struct sk_buff *skb, uint8_t protocol, int offset) #define myNIPQUAD(a) a[0], a[1], a[2], a[3] static void -ebt_log_packet(unsigned int pf, unsigned int hooknum, +ebt_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_loginfo *loginfo, const char *prefix) diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 2d4c9ef909fc..c84bda61afba 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -223,7 +223,7 @@ alloc_failure: } /* this function is registered with the netfilter core */ -static void ebt_log_packet(unsigned int pf, unsigned int hooknum, +static void ebt_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_loginfo *li, const char *prefix) diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index 0af14137137b..9330ba3577e1 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -375,7 +375,7 @@ static struct nf_loginfo default_loginfo = { }; static void -ipt_log_packet(unsigned int pf, +ipt_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index b192756c6d0d..d8241e6b077b 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -292,7 +292,7 @@ ulog_tg(struct sk_buff *skb, const struct net_device *in, return XT_CONTINUE; } -static void ipt_logfn(unsigned int pf, +static void ipt_logfn(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 97791048fa9b..da8edcdaef32 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -79,7 +79,7 @@ static int icmp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { /* Try to delete connection immediately after all replies: @@ -173,7 +173,7 @@ icmp_error_message(struct sk_buff *skb, /* Small and modified version of icmp_rcv */ static int icmp_error(struct sk_buff *skb, unsigned int dataoff, - enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum) + enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum) { const struct icmphdr *icmph; struct icmphdr _ih; diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index 3a2316974f83..0716f8afa0bc 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -385,7 +385,7 @@ static struct nf_loginfo default_loginfo = { }; static void -ip6t_log_packet(unsigned int pf, +ip6t_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 14d47d833545..5756f30ebc68 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -81,7 +81,7 @@ static int icmpv6_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { /* Try to delete connection immediately after all replies: @@ -173,7 +173,7 @@ icmpv6_error_message(struct sk_buff *skb, static int icmpv6_error(struct sk_buff *skb, unsigned int dataoff, - enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum) + enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum) { const struct icmp6hdr *icmp6h; struct icmp6hdr _ih; diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 292fa28146fb..26b8f489d7a2 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -113,7 +113,7 @@ EXPORT_SYMBOL(nf_unregister_hooks); unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, - int hook, + unsigned int hook, const struct net_device *indev, const struct net_device *outdev, struct list_head **i, @@ -155,7 +155,7 @@ unsigned int nf_iterate(struct list_head *head, /* Returns 1 if okfn() needs to be executed by the caller, * -EPERM for NF_DROP, 0 otherwise. */ -int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, +int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 9d1830da8e84..6aaf64b5dede 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -665,7 +665,7 @@ resolve_normal_ct(struct sk_buff *skb, } unsigned int -nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff *skb) +nf_conntrack_in(u_int8_t pf, unsigned int hooknum, struct sk_buff *skb) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; @@ -683,7 +683,7 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff *skb) } /* rcu_read_lock()ed by nf_hook_slow */ - l3proto = __nf_ct_l3proto_find((u_int16_t)pf); + l3proto = __nf_ct_l3proto_find(pf); ret = l3proto->get_l4proto(skb, skb_network_offset(skb), &dataoff, &protonum); if (ret <= 0) { @@ -693,7 +693,7 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff *skb) return -ret; } - l4proto = __nf_ct_l4proto_find((u_int16_t)pf, protonum); + l4proto = __nf_ct_l4proto_find(pf, protonum); /* It may be an special packet, error, unclean... * inverse of the return code tells to the netfilter diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index e8f0dead267f..990fa12f2ee5 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -241,7 +241,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) EXPORT_SYMBOL_GPL(nf_ct_expect_alloc); void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, - int family, + u_int8_t family, const union nf_inet_addr *saddr, const union nf_inet_addr *daddr, u_int8_t proto, const __be16 *src, const __be16 *dst) diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 2f83c158934d..5dc0478108ae 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -709,7 +709,8 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, /* If the calling party is on the same side of the forward-to party, * we don't need to track the second call */ static int callforward_do_filter(const union nf_inet_addr *src, - const union nf_inet_addr *dst, int family) + const union nf_inet_addr *dst, + u_int8_t family) { const struct nf_afinfo *afinfo; struct flowi fl1, fl2; diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index e7866dd3cde6..edc30358dc19 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -461,7 +461,7 @@ static u64 dccp_ack_seq(const struct dccp_hdr *dh) static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, unsigned int hooknum) + u_int8_t pf, unsigned int hooknum) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); struct dccp_hdr _dh, *dh; @@ -546,7 +546,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, } static int dccp_error(struct sk_buff *skb, unsigned int dataoff, - enum ip_conntrack_info *ctinfo, int pf, + enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum) { struct dccp_hdr _dh, *dh; diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index e31b0e7bd0b1..dbe680af85d2 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -45,7 +45,7 @@ static int packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_generic_timeout); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 9bd03967fea4..c5a78220fa38 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -219,7 +219,7 @@ static int gre_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { /* If we've seen traffic both ways, this is a GRE connection. diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 30aa5b94a771..b5a90596d3f4 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -287,7 +287,7 @@ static int sctp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { enum sctp_conntrack new_state, old_state; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 6f61261888ef..539a8202025c 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -486,7 +486,7 @@ static bool tcp_in_window(const struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, const struct tcphdr *tcph, - int pf) + u_int8_t pf) { struct ip_ct_tcp_state *sender = &state->seen[dir]; struct ip_ct_tcp_state *receiver = &state->seen[!dir]; @@ -749,7 +749,7 @@ static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG) + 1] = static int tcp_error(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { const struct tcphdr *th; @@ -804,7 +804,7 @@ static int tcp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { struct nf_conntrack_tuple *tuple; diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index 8b21762e65de..2a965c4a0eac 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -66,7 +66,7 @@ static int udp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { /* If we've seen traffic both ways, this is some kind of UDP @@ -91,7 +91,7 @@ static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb, static int udp_error(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { unsigned int udplen = skb->len - dataoff; diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 1fa62f3c24f1..4fb6c8d83a84 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -65,7 +65,7 @@ static int udplite_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { /* If we've seen traffic both ways, this is some kind of UDP @@ -91,7 +91,7 @@ static bool udplite_new(struct nf_conn *ct, const struct sk_buff *skb, static int udplite_error(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, - int pf, + u_int8_t pf, unsigned int hooknum) { unsigned int udplen = skb->len - dataoff; diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 196269c1e586..bf6609978af7 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -15,7 +15,7 @@ /* core.c */ extern unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, - int hook, + unsigned int hook, const struct net_device *indev, const struct net_device *outdev, struct list_head **i, @@ -25,7 +25,7 @@ extern unsigned int nf_iterate(struct list_head *head, /* nf_queue.c */ extern int nf_queue(struct sk_buff *skb, struct list_head *elem, - int pf, unsigned int hook, + u_int8_t pf, unsigned int hook, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 9fda6ee95a31..5c2f73320154 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -20,7 +20,7 @@ static DEFINE_MUTEX(nf_log_mutex); /* return EBUSY if somebody else is registered, EEXIST if the same logger * is registred, 0 on success. */ -int nf_log_register(int pf, const struct nf_logger *logger) +int nf_log_register(u_int8_t pf, const struct nf_logger *logger) { int ret; @@ -45,7 +45,7 @@ int nf_log_register(int pf, const struct nf_logger *logger) } EXPORT_SYMBOL(nf_log_register); -void nf_log_unregister_pf(int pf) +void nf_log_unregister_pf(u_int8_t pf) { if (pf >= NPROTO) return; @@ -73,7 +73,7 @@ void nf_log_unregister(const struct nf_logger *logger) } EXPORT_SYMBOL(nf_log_unregister); -void nf_log_packet(int pf, +void nf_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 582ec3efc8a5..f285086f6292 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -22,7 +22,7 @@ static DEFINE_MUTEX(queue_handler_mutex); /* return EBUSY when somebody else is registered, return EEXIST if the * same handler is registered, return 0 in case of success. */ -int nf_register_queue_handler(int pf, const struct nf_queue_handler *qh) +int nf_register_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) { int ret; @@ -45,7 +45,7 @@ int nf_register_queue_handler(int pf, const struct nf_queue_handler *qh) EXPORT_SYMBOL(nf_register_queue_handler); /* The caller must flush their queue before this */ -int nf_unregister_queue_handler(int pf, const struct nf_queue_handler *qh) +int nf_unregister_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) { if (pf >= NPROTO) return -EINVAL; @@ -67,7 +67,7 @@ EXPORT_SYMBOL(nf_unregister_queue_handler); void nf_unregister_queue_handlers(const struct nf_queue_handler *qh) { - int pf; + u_int8_t pf; mutex_lock(&queue_handler_mutex); for (pf = 0; pf < NPROTO; pf++) { @@ -107,7 +107,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) */ static int __nf_queue(struct sk_buff *skb, struct list_head *elem, - int pf, unsigned int hook, + u_int8_t pf, unsigned int hook, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), @@ -191,7 +191,7 @@ err: int nf_queue(struct sk_buff *skb, struct list_head *elem, - int pf, unsigned int hook, + u_int8_t pf, unsigned int hook, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), diff --git a/net/netfilter/nf_sockopt.c b/net/netfilter/nf_sockopt.c index 01489681fa96..f9b46de6a3db 100644 --- a/net/netfilter/nf_sockopt.c +++ b/net/netfilter/nf_sockopt.c @@ -60,7 +60,7 @@ void nf_unregister_sockopt(struct nf_sockopt_ops *reg) } EXPORT_SYMBOL(nf_unregister_sockopt); -static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, int pf, +static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf, int val, int get) { struct nf_sockopt_ops *ops; @@ -96,7 +96,7 @@ out: } /* Call get/setsockopt() */ -static int nf_sockopt(struct sock *sk, int pf, int val, +static int nf_sockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, int *len, int get) { struct nf_sockopt_ops *ops; @@ -115,21 +115,22 @@ static int nf_sockopt(struct sock *sk, int pf, int val, return ret; } -int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt, +int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, int len) { return nf_sockopt(sk, pf, val, opt, &len, 0); } EXPORT_SYMBOL(nf_setsockopt); -int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len) +int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, + int *len) { return nf_sockopt(sk, pf, val, opt, len, 1); } EXPORT_SYMBOL(nf_getsockopt); #ifdef CONFIG_COMPAT -static int compat_nf_sockopt(struct sock *sk, int pf, int val, +static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, int *len, int get) { struct nf_sockopt_ops *ops; @@ -155,14 +156,14 @@ static int compat_nf_sockopt(struct sock *sk, int pf, int val, return ret; } -int compat_nf_setsockopt(struct sock *sk, int pf, +int compat_nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, int len) { return compat_nf_sockopt(sk, pf, val, opt, &len, 0); } EXPORT_SYMBOL(compat_nf_setsockopt); -int compat_nf_getsockopt(struct sock *sk, int pf, +int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, int *len) { return compat_nf_sockopt(sk, pf, val, opt, len, 1); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 9a35b57ab76d..41e0105d3828 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -359,7 +359,7 @@ static inline int __build_packet_message(struct nfulnl_instance *inst, const struct sk_buff *skb, unsigned int data_len, - unsigned int pf, + u_int8_t pf, unsigned int hooknum, const struct net_device *indev, const struct net_device *outdev, @@ -534,7 +534,7 @@ static struct nf_loginfo default_loginfo = { /* log handler for internal netfilter logging api */ static void -nfulnl_log_packet(unsigned int pf, +nfulnl_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 5d75cd86ebb3..cf2f3e90cef9 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -68,7 +68,8 @@ static const char *const xt_prefix[NPROTO] = { int xt_register_target(struct xt_target *target) { - int ret, af = target->family; + u_int8_t af = target->family; + int ret; ret = mutex_lock_interruptible(&xt[af].mutex); if (ret != 0) @@ -82,7 +83,7 @@ EXPORT_SYMBOL(xt_register_target); void xt_unregister_target(struct xt_target *target) { - int af = target->family; + u_int8_t af = target->family; mutex_lock(&xt[af].mutex); list_del(&target->list); @@ -123,7 +124,8 @@ EXPORT_SYMBOL(xt_unregister_targets); int xt_register_match(struct xt_match *match) { - int ret, af = match->family; + u_int8_t af = match->family; + int ret; ret = mutex_lock_interruptible(&xt[af].mutex); if (ret != 0) @@ -139,7 +141,7 @@ EXPORT_SYMBOL(xt_register_match); void xt_unregister_match(struct xt_match *match) { - int af = match->family; + u_int8_t af = match->family; mutex_lock(&xt[af].mutex); list_del(&match->list); @@ -185,7 +187,7 @@ EXPORT_SYMBOL(xt_unregister_matches); */ /* Find match, grabs ref. Returns ERR_PTR() on error. */ -struct xt_match *xt_find_match(int af, const char *name, u8 revision) +struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) { struct xt_match *m; int err = 0; @@ -210,7 +212,7 @@ struct xt_match *xt_find_match(int af, const char *name, u8 revision) EXPORT_SYMBOL(xt_find_match); /* Find target, grabs ref. Returns ERR_PTR() on error. */ -struct xt_target *xt_find_target(int af, const char *name, u8 revision) +struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) { struct xt_target *t; int err = 0; @@ -234,7 +236,7 @@ struct xt_target *xt_find_target(int af, const char *name, u8 revision) } EXPORT_SYMBOL(xt_find_target); -struct xt_target *xt_request_find_target(int af, const char *name, u8 revision) +struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision) { struct xt_target *target; @@ -246,7 +248,7 @@ struct xt_target *xt_request_find_target(int af, const char *name, u8 revision) } EXPORT_SYMBOL_GPL(xt_request_find_target); -static int match_revfn(int af, const char *name, u8 revision, int *bestp) +static int match_revfn(u8 af, const char *name, u8 revision, int *bestp) { const struct xt_match *m; int have_rev = 0; @@ -262,7 +264,7 @@ static int match_revfn(int af, const char *name, u8 revision, int *bestp) return have_rev; } -static int target_revfn(int af, const char *name, u8 revision, int *bestp) +static int target_revfn(u8 af, const char *name, u8 revision, int *bestp) { const struct xt_target *t; int have_rev = 0; @@ -279,7 +281,7 @@ static int target_revfn(int af, const char *name, u8 revision, int *bestp) } /* Returns true or false (if no such extension at all) */ -int xt_find_revision(int af, const char *name, u8 revision, int target, +int xt_find_revision(u8 af, const char *name, u8 revision, int target, int *err) { int have_rev, best = -1; @@ -337,7 +339,7 @@ int xt_check_match(const struct xt_match *match, unsigned short family, EXPORT_SYMBOL_GPL(xt_check_match); #ifdef CONFIG_COMPAT -int xt_compat_add_offset(int af, unsigned int offset, short delta) +int xt_compat_add_offset(u_int8_t af, unsigned int offset, short delta) { struct compat_delta *tmp; @@ -359,7 +361,7 @@ int xt_compat_add_offset(int af, unsigned int offset, short delta) } EXPORT_SYMBOL_GPL(xt_compat_add_offset); -void xt_compat_flush_offsets(int af) +void xt_compat_flush_offsets(u_int8_t af) { struct compat_delta *tmp, *next; @@ -373,7 +375,7 @@ void xt_compat_flush_offsets(int af) } EXPORT_SYMBOL_GPL(xt_compat_flush_offsets); -short xt_compat_calc_jump(int af, unsigned int offset) +short xt_compat_calc_jump(u_int8_t af, unsigned int offset) { struct compat_delta *tmp; short delta; @@ -590,7 +592,8 @@ void xt_free_table_info(struct xt_table_info *info) EXPORT_SYMBOL(xt_free_table_info); /* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ -struct xt_table *xt_find_table_lock(struct net *net, int af, const char *name) +struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, + const char *name) { struct xt_table *t; @@ -612,13 +615,13 @@ void xt_table_unlock(struct xt_table *table) EXPORT_SYMBOL_GPL(xt_table_unlock); #ifdef CONFIG_COMPAT -void xt_compat_lock(int af) +void xt_compat_lock(u_int8_t af) { mutex_lock(&xt[af].compat_mutex); } EXPORT_SYMBOL_GPL(xt_compat_lock); -void xt_compat_unlock(int af) +void xt_compat_unlock(u_int8_t af) { mutex_unlock(&xt[af].compat_mutex); } @@ -722,13 +725,13 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); #ifdef CONFIG_PROC_FS struct xt_names_priv { struct seq_net_private p; - int af; + u_int8_t af; }; static void *xt_table_seq_start(struct seq_file *seq, loff_t *pos) { struct xt_names_priv *priv = seq->private; struct net *net = seq_file_net(seq); - int af = priv->af; + u_int8_t af = priv->af; mutex_lock(&xt[af].mutex); return seq_list_start(&net->xt.tables[af], *pos); @@ -738,7 +741,7 @@ static void *xt_table_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct xt_names_priv *priv = seq->private; struct net *net = seq_file_net(seq); - int af = priv->af; + u_int8_t af = priv->af; return seq_list_next(v, &net->xt.tables[af], pos); } @@ -746,7 +749,7 @@ static void *xt_table_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void xt_table_seq_stop(struct seq_file *seq, void *v) { struct xt_names_priv *priv = seq->private; - int af = priv->af; + u_int8_t af = priv->af; mutex_unlock(&xt[af].mutex); } @@ -922,7 +925,7 @@ static const struct file_operations xt_target_ops = { #endif /* CONFIG_PROC_FS */ -int xt_proto_init(struct net *net, int af) +int xt_proto_init(struct net *net, u_int8_t af) { #ifdef CONFIG_PROC_FS char buf[XT_FUNCTION_MAXNAMELEN]; @@ -974,7 +977,7 @@ out: } EXPORT_SYMBOL_GPL(xt_proto_init); -void xt_proto_fini(struct net *net, int af) +void xt_proto_fini(struct net *net, u_int8_t af) { #ifdef CONFIG_PROC_FS char buf[XT_FUNCTION_MAXNAMELEN]; diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 70907f6baac3..1655e2cf25c4 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -82,7 +82,7 @@ static inline bool already_closed(const struct nf_conn *conn) static inline unsigned int same_source_net(const union nf_inet_addr *addr, const union nf_inet_addr *mask, - const union nf_inet_addr *u3, unsigned int family) + const union nf_inet_addr *u3, u_int8_t family) { if (family == AF_INET) { return (addr->ip & mask->ip) == (u3->ip & mask->ip); diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index d61412f58ef7..28a42a3fbff7 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -133,7 +133,7 @@ conntrack_addrcmp(const union nf_inet_addr *kaddr, static inline bool conntrack_mt_origsrc(const struct nf_conn *ct, const struct xt_conntrack_mtinfo1 *info, - unsigned int family) + u_int8_t family) { return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, &info->origsrc_addr, &info->origsrc_mask, family); @@ -142,7 +142,7 @@ conntrack_mt_origsrc(const struct nf_conn *ct, static inline bool conntrack_mt_origdst(const struct nf_conn *ct, const struct xt_conntrack_mtinfo1 *info, - unsigned int family) + u_int8_t family) { return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3, &info->origdst_addr, &info->origdst_mask, family); @@ -151,7 +151,7 @@ conntrack_mt_origdst(const struct nf_conn *ct, static inline bool conntrack_mt_replsrc(const struct nf_conn *ct, const struct xt_conntrack_mtinfo1 *info, - unsigned int family) + u_int8_t family) { return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3, &info->replsrc_addr, &info->replsrc_mask, family); @@ -160,7 +160,7 @@ conntrack_mt_replsrc(const struct nf_conn *ct, static inline bool conntrack_mt_repldst(const struct nf_conn *ct, const struct xt_conntrack_mtinfo1 *info, - unsigned int family) + u_int8_t family) { return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3, &info->repldst_addr, &info->repldst_mask, family); diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index d9418a267812..0c9268fd2e10 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -80,7 +80,7 @@ struct dsthash_ent { struct xt_hashlimit_htable { struct hlist_node node; /* global list of all htables */ atomic_t use; - int family; + u_int8_t family; struct hashlimit_cfg1 cfg; /* config */ @@ -185,7 +185,7 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent) } static void htable_gc(unsigned long htlong); -static int htable_create_v0(struct xt_hashlimit_info *minfo, int family) +static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family) { struct xt_hashlimit_htable *hinfo; unsigned int size; @@ -258,8 +258,7 @@ static int htable_create_v0(struct xt_hashlimit_info *minfo, int family) return 0; } -static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, - unsigned int family) +static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) { struct xt_hashlimit_htable *hinfo; unsigned int size; @@ -378,7 +377,7 @@ static void htable_destroy(struct xt_hashlimit_htable *hinfo) } static struct xt_hashlimit_htable *htable_find_get(const char *name, - int family) + u_int8_t family) { struct xt_hashlimit_htable *hinfo; struct hlist_node *pos; @@ -901,7 +900,7 @@ static void dl_seq_stop(struct seq_file *s, void *v) spin_unlock_bh(&htable->lock); } -static int dl_seq_real_show(struct dsthash_ent *ent, int family, +static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, struct seq_file *s) { /* recalculate to show accurate numbers */ -- cgit v1.2.3 From e948b20a71a06a740c925d6ea22b59b4e17cfa0c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:00 +0200 Subject: netfilter: rename ipt_recent to xt_recent Like with other modules (such as ipt_state), ipt_recent.h is changed to forward definitions to (IOW include) xt_recent.h, and xt_recent.c is changed to use the new constant names. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_recent.h | 26 ++ include/linux/netfilter_ipv4/ipt_recent.h | 28 +- net/ipv4/netfilter/Kconfig | 13 - net/ipv4/netfilter/Makefile | 1 - net/ipv4/netfilter/ipt_recent.c | 501 ----------------------------- net/netfilter/Kconfig | 11 + net/netfilter/Makefile | 1 + net/netfilter/xt_recent.c | 502 ++++++++++++++++++++++++++++++ 9 files changed, 552 insertions(+), 532 deletions(-) create mode 100644 include/linux/netfilter/xt_recent.h delete mode 100644 net/ipv4/netfilter/ipt_recent.c create mode 100644 net/netfilter/xt_recent.c (limited to 'include/linux') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 3aff513d12c8..5a8af875bce2 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -32,6 +32,7 @@ header-y += xt_owner.h header-y += xt_pkttype.h header-y += xt_rateest.h header-y += xt_realm.h +header-y += xt_recent.h header-y += xt_sctp.h header-y += xt_state.h header-y += xt_statistic.h diff --git a/include/linux/netfilter/xt_recent.h b/include/linux/netfilter/xt_recent.h new file mode 100644 index 000000000000..5cfeb81c6794 --- /dev/null +++ b/include/linux/netfilter/xt_recent.h @@ -0,0 +1,26 @@ +#ifndef _LINUX_NETFILTER_XT_RECENT_H +#define _LINUX_NETFILTER_XT_RECENT_H 1 + +enum { + XT_RECENT_CHECK = 1 << 0, + XT_RECENT_SET = 1 << 1, + XT_RECENT_UPDATE = 1 << 2, + XT_RECENT_REMOVE = 1 << 3, + XT_RECENT_TTL = 1 << 4, + + XT_RECENT_SOURCE = 0, + XT_RECENT_DEST = 1, + + XT_RECENT_NAME_LEN = 200, +}; + +struct xt_recent_mtinfo { + u_int32_t seconds; + u_int32_t hit_count; + u_int8_t check_set; + u_int8_t invert; + char name[XT_RECENT_NAME_LEN]; + u_int8_t side; +}; + +#endif /* _LINUX_NETFILTER_XT_RECENT_H */ diff --git a/include/linux/netfilter_ipv4/ipt_recent.h b/include/linux/netfilter_ipv4/ipt_recent.h index 6508a4592651..d636cca133c2 100644 --- a/include/linux/netfilter_ipv4/ipt_recent.h +++ b/include/linux/netfilter_ipv4/ipt_recent.h @@ -1,27 +1,21 @@ #ifndef _IPT_RECENT_H #define _IPT_RECENT_H -#define RECENT_NAME "ipt_recent" -#define RECENT_VER "v0.3.1" +#include -#define IPT_RECENT_CHECK 1 -#define IPT_RECENT_SET 2 -#define IPT_RECENT_UPDATE 4 -#define IPT_RECENT_REMOVE 8 -#define IPT_RECENT_TTL 16 +#define ipt_recent_info xt_recent_mtinfo -#define IPT_RECENT_SOURCE 0 -#define IPT_RECENT_DEST 1 +enum { + IPT_RECENT_CHECK = XT_RECENT_CHECK, + IPT_RECENT_SET = XT_RECENT_SET, + IPT_RECENT_UPDATE = XT_RECENT_UPDATE, + IPT_RECENT_REMOVE = XT_RECENT_REMOVE, + IPT_RECENT_TTL = XT_RECENT_TTL, -#define IPT_RECENT_NAME_LEN 200 + IPT_RECENT_SOURCE = XT_RECENT_SOURCE, + IPT_RECENT_DEST = XT_RECENT_DEST, -struct ipt_recent_info { - u_int32_t seconds; - u_int32_t hit_count; - u_int8_t check_set; - u_int8_t invert; - char name[IPT_RECENT_NAME_LEN]; - u_int8_t side; + IPT_RECENT_NAME_LEN = XT_RECENT_NAME_LEN, }; #endif /*_IPT_RECENT_H*/ diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 90eb7cb47e77..4e842d566428 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -57,19 +57,6 @@ config IP_NF_IPTABLES To compile it as a module, choose M here. If unsure, say N. # The matches. -config IP_NF_MATCH_RECENT - tristate '"recent" match support' - depends on IP_NF_IPTABLES - depends on NETFILTER_ADVANCED - help - This match is used for creating one or many lists of recently - used addresses and then matching against that/those list(s). - - Short options are available by using 'iptables -m recent -h' - Official Website: - - To compile it as a module, choose M here. If unsure, say N. - config IP_NF_MATCH_ECN tristate '"ecn" match support' depends on IP_NF_IPTABLES diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 3f31291f37ce..1107edbe478f 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -48,7 +48,6 @@ obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o -obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o # targets diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c deleted file mode 100644 index 3974d7cae5c0..000000000000 --- a/net/ipv4/netfilter/ipt_recent.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright (c) 2006 Patrick McHardy - * - * 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. - * - * This is a replacement of the old ipt_recent module, which carried the - * following copyright notice: - * - * Author: Stephen Frost - * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -MODULE_AUTHOR("Patrick McHardy "); -MODULE_DESCRIPTION("Xtables: \"recently-seen\" host matching for IPv4"); -MODULE_LICENSE("GPL"); - -static unsigned int ip_list_tot = 100; -static unsigned int ip_pkt_list_tot = 20; -static unsigned int ip_list_hash_size = 0; -static unsigned int ip_list_perms = 0644; -static unsigned int ip_list_uid = 0; -static unsigned int ip_list_gid = 0; -module_param(ip_list_tot, uint, 0400); -module_param(ip_pkt_list_tot, uint, 0400); -module_param(ip_list_hash_size, uint, 0400); -module_param(ip_list_perms, uint, 0400); -module_param(ip_list_uid, uint, 0400); -module_param(ip_list_gid, uint, 0400); -MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list"); -MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember (max. 255)"); -MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs"); -MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files"); -MODULE_PARM_DESC(ip_list_uid,"owner of /proc/net/ipt_recent/* files"); -MODULE_PARM_DESC(ip_list_gid,"owning group of /proc/net/ipt_recent/* files"); - -struct recent_entry { - struct list_head list; - struct list_head lru_list; - __be32 addr; - u_int8_t ttl; - u_int8_t index; - u_int16_t nstamps; - unsigned long stamps[0]; -}; - -struct recent_table { - struct list_head list; - char name[IPT_RECENT_NAME_LEN]; -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *proc; -#endif - unsigned int refcnt; - unsigned int entries; - struct list_head lru_list; - struct list_head iphash[0]; -}; - -static LIST_HEAD(tables); -static DEFINE_SPINLOCK(recent_lock); -static DEFINE_MUTEX(recent_mutex); - -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_dir; -static const struct file_operations recent_fops; -#endif - -static u_int32_t hash_rnd; -static int hash_rnd_initted; - -static unsigned int recent_entry_hash(__be32 addr) -{ - if (!hash_rnd_initted) { - get_random_bytes(&hash_rnd, 4); - hash_rnd_initted = 1; - } - return jhash_1word((__force u32)addr, hash_rnd) & (ip_list_hash_size - 1); -} - -static struct recent_entry * -recent_entry_lookup(const struct recent_table *table, __be32 addr, u_int8_t ttl) -{ - struct recent_entry *e; - unsigned int h; - - h = recent_entry_hash(addr); - list_for_each_entry(e, &table->iphash[h], list) - if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl)) - return e; - return NULL; -} - -static void recent_entry_remove(struct recent_table *t, struct recent_entry *e) -{ - list_del(&e->list); - list_del(&e->lru_list); - kfree(e); - t->entries--; -} - -static struct recent_entry * -recent_entry_init(struct recent_table *t, __be32 addr, u_int8_t ttl) -{ - struct recent_entry *e; - - if (t->entries >= ip_list_tot) { - e = list_entry(t->lru_list.next, struct recent_entry, lru_list); - recent_entry_remove(t, e); - } - e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot, - GFP_ATOMIC); - if (e == NULL) - return NULL; - e->addr = addr; - e->ttl = ttl; - e->stamps[0] = jiffies; - e->nstamps = 1; - e->index = 1; - list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]); - list_add_tail(&e->lru_list, &t->lru_list); - t->entries++; - return e; -} - -static void recent_entry_update(struct recent_table *t, struct recent_entry *e) -{ - e->stamps[e->index++] = jiffies; - if (e->index > e->nstamps) - e->nstamps = e->index; - e->index %= ip_pkt_list_tot; - list_move_tail(&e->lru_list, &t->lru_list); -} - -static struct recent_table *recent_table_lookup(const char *name) -{ - struct recent_table *t; - - list_for_each_entry(t, &tables, list) - if (!strcmp(t->name, name)) - return t; - return NULL; -} - -static void recent_table_flush(struct recent_table *t) -{ - struct recent_entry *e, *next; - unsigned int i; - - for (i = 0; i < ip_list_hash_size; i++) - list_for_each_entry_safe(e, next, &t->iphash[i], list) - recent_entry_remove(t, e); -} - -static bool -recent_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) -{ - const struct ipt_recent_info *info = matchinfo; - struct recent_table *t; - struct recent_entry *e; - __be32 addr; - u_int8_t ttl; - bool ret = info->invert; - - if (info->side == IPT_RECENT_DEST) - addr = ip_hdr(skb)->daddr; - else - addr = ip_hdr(skb)->saddr; - - ttl = ip_hdr(skb)->ttl; - /* use TTL as seen before forwarding */ - if (out && !skb->sk) - ttl++; - - spin_lock_bh(&recent_lock); - t = recent_table_lookup(info->name); - e = recent_entry_lookup(t, addr, - info->check_set & IPT_RECENT_TTL ? ttl : 0); - if (e == NULL) { - if (!(info->check_set & IPT_RECENT_SET)) - goto out; - e = recent_entry_init(t, addr, ttl); - if (e == NULL) - *hotdrop = true; - ret = !ret; - goto out; - } - - if (info->check_set & IPT_RECENT_SET) - ret = !ret; - else if (info->check_set & IPT_RECENT_REMOVE) { - recent_entry_remove(t, e); - ret = !ret; - } else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) { - unsigned long time = jiffies - info->seconds * HZ; - unsigned int i, hits = 0; - - for (i = 0; i < e->nstamps; i++) { - if (info->seconds && time_after(time, e->stamps[i])) - continue; - if (++hits >= info->hit_count) { - ret = !ret; - break; - } - } - } - - if (info->check_set & IPT_RECENT_SET || - (info->check_set & IPT_RECENT_UPDATE && ret)) { - recent_entry_update(t, e); - e->ttl = ttl; - } -out: - spin_unlock_bh(&recent_lock); - return ret; -} - -static bool -recent_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) -{ - const struct ipt_recent_info *info = matchinfo; - struct recent_table *t; - unsigned i; - bool ret = false; - - if (hweight8(info->check_set & - (IPT_RECENT_SET | IPT_RECENT_REMOVE | - IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1) - return false; - if ((info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE)) && - (info->seconds || info->hit_count)) - return false; - if (info->hit_count > ip_pkt_list_tot) - return false; - if (info->name[0] == '\0' || - strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN) - return false; - - mutex_lock(&recent_mutex); - t = recent_table_lookup(info->name); - if (t != NULL) { - t->refcnt++; - ret = true; - goto out; - } - - t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size, - GFP_KERNEL); - if (t == NULL) - goto out; - t->refcnt = 1; - strcpy(t->name, info->name); - INIT_LIST_HEAD(&t->lru_list); - for (i = 0; i < ip_list_hash_size; i++) - INIT_LIST_HEAD(&t->iphash[i]); -#ifdef CONFIG_PROC_FS - t->proc = proc_create(t->name, ip_list_perms, proc_dir, &recent_fops); - if (t->proc == NULL) { - kfree(t); - goto out; - } - t->proc->uid = ip_list_uid; - t->proc->gid = ip_list_gid; - t->proc->data = t; -#endif - spin_lock_bh(&recent_lock); - list_add_tail(&t->list, &tables); - spin_unlock_bh(&recent_lock); - ret = true; -out: - mutex_unlock(&recent_mutex); - return ret; -} - -static void recent_mt_destroy(const struct xt_match *match, void *matchinfo) -{ - const struct ipt_recent_info *info = matchinfo; - struct recent_table *t; - - mutex_lock(&recent_mutex); - t = recent_table_lookup(info->name); - if (--t->refcnt == 0) { - spin_lock_bh(&recent_lock); - list_del(&t->list); - spin_unlock_bh(&recent_lock); -#ifdef CONFIG_PROC_FS - remove_proc_entry(t->name, proc_dir); -#endif - recent_table_flush(t); - kfree(t); - } - mutex_unlock(&recent_mutex); -} - -#ifdef CONFIG_PROC_FS -struct recent_iter_state { - struct recent_table *table; - unsigned int bucket; -}; - -static void *recent_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(recent_lock) -{ - struct recent_iter_state *st = seq->private; - const struct recent_table *t = st->table; - struct recent_entry *e; - loff_t p = *pos; - - spin_lock_bh(&recent_lock); - - for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) - list_for_each_entry(e, &t->iphash[st->bucket], list) - if (p-- == 0) - return e; - return NULL; -} - -static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct recent_iter_state *st = seq->private; - const struct recent_table *t = st->table; - struct recent_entry *e = v; - struct list_head *head = e->list.next; - - while (head == &t->iphash[st->bucket]) { - if (++st->bucket >= ip_list_hash_size) - return NULL; - head = t->iphash[st->bucket].next; - } - (*pos)++; - return list_entry(head, struct recent_entry, list); -} - -static void recent_seq_stop(struct seq_file *s, void *v) - __releases(recent_lock) -{ - spin_unlock_bh(&recent_lock); -} - -static int recent_seq_show(struct seq_file *seq, void *v) -{ - const struct recent_entry *e = v; - unsigned int i; - - i = (e->index - 1) % ip_pkt_list_tot; - seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u", - NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index); - for (i = 0; i < e->nstamps; i++) - seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]); - seq_printf(seq, "\n"); - return 0; -} - -static const struct seq_operations recent_seq_ops = { - .start = recent_seq_start, - .next = recent_seq_next, - .stop = recent_seq_stop, - .show = recent_seq_show, -}; - -static int recent_seq_open(struct inode *inode, struct file *file) -{ - struct proc_dir_entry *pde = PDE(inode); - struct recent_iter_state *st; - - st = __seq_open_private(file, &recent_seq_ops, sizeof(*st)); - if (st == NULL) - return -ENOMEM; - - st->table = pde->data; - return 0; -} - -static ssize_t recent_proc_write(struct file *file, const char __user *input, - size_t size, loff_t *loff) -{ - const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); - struct recent_table *t = pde->data; - struct recent_entry *e; - char buf[sizeof("+255.255.255.255")], *c = buf; - __be32 addr; - int add; - - if (size > sizeof(buf)) - size = sizeof(buf); - if (copy_from_user(buf, input, size)) - return -EFAULT; - while (isspace(*c)) - c++; - - if (size - (c - buf) < 5) - return c - buf; - if (!strncmp(c, "clear", 5)) { - c += 5; - spin_lock_bh(&recent_lock); - recent_table_flush(t); - spin_unlock_bh(&recent_lock); - return c - buf; - } - - switch (*c) { - case '-': - add = 0; - c++; - break; - case '+': - c++; - default: - add = 1; - break; - } - addr = in_aton(c); - - spin_lock_bh(&recent_lock); - e = recent_entry_lookup(t, addr, 0); - if (e == NULL) { - if (add) - recent_entry_init(t, addr, 0); - } else { - if (add) - recent_entry_update(t, e); - else - recent_entry_remove(t, e); - } - spin_unlock_bh(&recent_lock); - return size; -} - -static const struct file_operations recent_fops = { - .open = recent_seq_open, - .read = seq_read, - .write = recent_proc_write, - .release = seq_release_private, - .owner = THIS_MODULE, -}; -#endif /* CONFIG_PROC_FS */ - -static struct xt_match recent_mt_reg __read_mostly = { - .name = "recent", - .family = AF_INET, - .match = recent_mt, - .matchsize = sizeof(struct ipt_recent_info), - .checkentry = recent_mt_check, - .destroy = recent_mt_destroy, - .me = THIS_MODULE, -}; - -static int __init recent_mt_init(void) -{ - int err; - - if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255) - return -EINVAL; - ip_list_hash_size = 1 << fls(ip_list_tot); - - err = xt_register_match(&recent_mt_reg); -#ifdef CONFIG_PROC_FS - if (err) - return err; - proc_dir = proc_mkdir("ipt_recent", init_net.proc_net); - if (proc_dir == NULL) { - xt_unregister_match(&recent_mt_reg); - err = -ENOMEM; - } -#endif - return err; -} - -static void __exit recent_mt_exit(void) -{ - BUG_ON(!list_empty(&tables)); - xt_unregister_match(&recent_mt_reg); -#ifdef CONFIG_PROC_FS - remove_proc_entry("ipt_recent", init_net.proc_net); -#endif -} - -module_init(recent_mt_init); -module_exit(recent_mt_exit); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ee898e74808d..ccc78b07a1a4 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -732,6 +732,17 @@ config NETFILTER_XT_MATCH_REALM If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_RECENT + tristate '"recent" match support' + depends on NETFILTER_XTABLES + depends on NETFILTER_ADVANCED + ---help--- + This match is used for creating one or many lists of recently + used addresses and then matching against that/those list(s). + + Short options are available by using 'iptables -m recent -h' + Official Website: + config NETFILTER_XT_MATCH_SCTP tristate '"sctp" protocol match support (EXPERIMENTAL)' depends on NETFILTER_XTABLES && EXPERIMENTAL diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 3bd2cc556aea..f101cf61e6f8 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o +obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c new file mode 100644 index 000000000000..422c0e4d66b7 --- /dev/null +++ b/net/netfilter/xt_recent.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2006 Patrick McHardy + * + * 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. + * + * This is a replacement of the old ipt_recent module, which carried the + * following copyright notice: + * + * Author: Stephen Frost + * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_DESCRIPTION("Xtables: \"recently-seen\" host matching for IPv4"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_recent"); + +static unsigned int ip_list_tot = 100; +static unsigned int ip_pkt_list_tot = 20; +static unsigned int ip_list_hash_size = 0; +static unsigned int ip_list_perms = 0644; +static unsigned int ip_list_uid = 0; +static unsigned int ip_list_gid = 0; +module_param(ip_list_tot, uint, 0400); +module_param(ip_pkt_list_tot, uint, 0400); +module_param(ip_list_hash_size, uint, 0400); +module_param(ip_list_perms, uint, 0400); +module_param(ip_list_uid, uint, 0400); +module_param(ip_list_gid, uint, 0400); +MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list"); +MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember (max. 255)"); +MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs"); +MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files"); +MODULE_PARM_DESC(ip_list_uid,"owner of /proc/net/ipt_recent/* files"); +MODULE_PARM_DESC(ip_list_gid,"owning group of /proc/net/ipt_recent/* files"); + +struct recent_entry { + struct list_head list; + struct list_head lru_list; + __be32 addr; + u_int8_t ttl; + u_int8_t index; + u_int16_t nstamps; + unsigned long stamps[0]; +}; + +struct recent_table { + struct list_head list; + char name[XT_RECENT_NAME_LEN]; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc; +#endif + unsigned int refcnt; + unsigned int entries; + struct list_head lru_list; + struct list_head iphash[0]; +}; + +static LIST_HEAD(tables); +static DEFINE_SPINLOCK(recent_lock); +static DEFINE_MUTEX(recent_mutex); + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_dir; +static const struct file_operations recent_fops; +#endif + +static u_int32_t hash_rnd; +static int hash_rnd_initted; + +static unsigned int recent_entry_hash(__be32 addr) +{ + if (!hash_rnd_initted) { + get_random_bytes(&hash_rnd, 4); + hash_rnd_initted = 1; + } + return jhash_1word((__force u32)addr, hash_rnd) & (ip_list_hash_size - 1); +} + +static struct recent_entry * +recent_entry_lookup(const struct recent_table *table, __be32 addr, u_int8_t ttl) +{ + struct recent_entry *e; + unsigned int h; + + h = recent_entry_hash(addr); + list_for_each_entry(e, &table->iphash[h], list) + if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl)) + return e; + return NULL; +} + +static void recent_entry_remove(struct recent_table *t, struct recent_entry *e) +{ + list_del(&e->list); + list_del(&e->lru_list); + kfree(e); + t->entries--; +} + +static struct recent_entry * +recent_entry_init(struct recent_table *t, __be32 addr, u_int8_t ttl) +{ + struct recent_entry *e; + + if (t->entries >= ip_list_tot) { + e = list_entry(t->lru_list.next, struct recent_entry, lru_list); + recent_entry_remove(t, e); + } + e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot, + GFP_ATOMIC); + if (e == NULL) + return NULL; + e->addr = addr; + e->ttl = ttl; + e->stamps[0] = jiffies; + e->nstamps = 1; + e->index = 1; + list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]); + list_add_tail(&e->lru_list, &t->lru_list); + t->entries++; + return e; +} + +static void recent_entry_update(struct recent_table *t, struct recent_entry *e) +{ + e->stamps[e->index++] = jiffies; + if (e->index > e->nstamps) + e->nstamps = e->index; + e->index %= ip_pkt_list_tot; + list_move_tail(&e->lru_list, &t->lru_list); +} + +static struct recent_table *recent_table_lookup(const char *name) +{ + struct recent_table *t; + + list_for_each_entry(t, &tables, list) + if (!strcmp(t->name, name)) + return t; + return NULL; +} + +static void recent_table_flush(struct recent_table *t) +{ + struct recent_entry *e, *next; + unsigned int i; + + for (i = 0; i < ip_list_hash_size; i++) + list_for_each_entry_safe(e, next, &t->iphash[i], list) + recent_entry_remove(t, e); +} + +static bool +recent_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, unsigned int protoff, + bool *hotdrop) +{ + const struct xt_recent_mtinfo *info = matchinfo; + struct recent_table *t; + struct recent_entry *e; + __be32 addr; + u_int8_t ttl; + bool ret = info->invert; + + if (info->side == XT_RECENT_DEST) + addr = ip_hdr(skb)->daddr; + else + addr = ip_hdr(skb)->saddr; + + ttl = ip_hdr(skb)->ttl; + /* use TTL as seen before forwarding */ + if (out && !skb->sk) + ttl++; + + spin_lock_bh(&recent_lock); + t = recent_table_lookup(info->name); + e = recent_entry_lookup(t, addr, + info->check_set & XT_RECENT_TTL ? ttl : 0); + if (e == NULL) { + if (!(info->check_set & XT_RECENT_SET)) + goto out; + e = recent_entry_init(t, addr, ttl); + if (e == NULL) + *hotdrop = true; + ret = !ret; + goto out; + } + + if (info->check_set & XT_RECENT_SET) + ret = !ret; + else if (info->check_set & XT_RECENT_REMOVE) { + recent_entry_remove(t, e); + ret = !ret; + } else if (info->check_set & (XT_RECENT_CHECK | XT_RECENT_UPDATE)) { + unsigned long time = jiffies - info->seconds * HZ; + unsigned int i, hits = 0; + + for (i = 0; i < e->nstamps; i++) { + if (info->seconds && time_after(time, e->stamps[i])) + continue; + if (++hits >= info->hit_count) { + ret = !ret; + break; + } + } + } + + if (info->check_set & XT_RECENT_SET || + (info->check_set & XT_RECENT_UPDATE && ret)) { + recent_entry_update(t, e); + e->ttl = ttl; + } +out: + spin_unlock_bh(&recent_lock); + return ret; +} + +static bool +recent_mt_check(const char *tablename, const void *ip, + const struct xt_match *match, void *matchinfo, + unsigned int hook_mask) +{ + const struct xt_recent_mtinfo *info = matchinfo; + struct recent_table *t; + unsigned i; + bool ret = false; + + if (hweight8(info->check_set & + (XT_RECENT_SET | XT_RECENT_REMOVE | + XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1) + return false; + if ((info->check_set & (XT_RECENT_SET | XT_RECENT_REMOVE)) && + (info->seconds || info->hit_count)) + return false; + if (info->hit_count > ip_pkt_list_tot) + return false; + if (info->name[0] == '\0' || + strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN) + return false; + + mutex_lock(&recent_mutex); + t = recent_table_lookup(info->name); + if (t != NULL) { + t->refcnt++; + ret = true; + goto out; + } + + t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size, + GFP_KERNEL); + if (t == NULL) + goto out; + t->refcnt = 1; + strcpy(t->name, info->name); + INIT_LIST_HEAD(&t->lru_list); + for (i = 0; i < ip_list_hash_size; i++) + INIT_LIST_HEAD(&t->iphash[i]); +#ifdef CONFIG_PROC_FS + t->proc = proc_create(t->name, ip_list_perms, proc_dir, &recent_fops); + if (t->proc == NULL) { + kfree(t); + goto out; + } + t->proc->uid = ip_list_uid; + t->proc->gid = ip_list_gid; + t->proc->data = t; +#endif + spin_lock_bh(&recent_lock); + list_add_tail(&t->list, &tables); + spin_unlock_bh(&recent_lock); + ret = true; +out: + mutex_unlock(&recent_mutex); + return ret; +} + +static void recent_mt_destroy(const struct xt_match *match, void *matchinfo) +{ + const struct xt_recent_mtinfo *info = matchinfo; + struct recent_table *t; + + mutex_lock(&recent_mutex); + t = recent_table_lookup(info->name); + if (--t->refcnt == 0) { + spin_lock_bh(&recent_lock); + list_del(&t->list); + spin_unlock_bh(&recent_lock); +#ifdef CONFIG_PROC_FS + remove_proc_entry(t->name, proc_dir); +#endif + recent_table_flush(t); + kfree(t); + } + mutex_unlock(&recent_mutex); +} + +#ifdef CONFIG_PROC_FS +struct recent_iter_state { + struct recent_table *table; + unsigned int bucket; +}; + +static void *recent_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(recent_lock) +{ + struct recent_iter_state *st = seq->private; + const struct recent_table *t = st->table; + struct recent_entry *e; + loff_t p = *pos; + + spin_lock_bh(&recent_lock); + + for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) + list_for_each_entry(e, &t->iphash[st->bucket], list) + if (p-- == 0) + return e; + return NULL; +} + +static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct recent_iter_state *st = seq->private; + const struct recent_table *t = st->table; + struct recent_entry *e = v; + struct list_head *head = e->list.next; + + while (head == &t->iphash[st->bucket]) { + if (++st->bucket >= ip_list_hash_size) + return NULL; + head = t->iphash[st->bucket].next; + } + (*pos)++; + return list_entry(head, struct recent_entry, list); +} + +static void recent_seq_stop(struct seq_file *s, void *v) + __releases(recent_lock) +{ + spin_unlock_bh(&recent_lock); +} + +static int recent_seq_show(struct seq_file *seq, void *v) +{ + const struct recent_entry *e = v; + unsigned int i; + + i = (e->index - 1) % ip_pkt_list_tot; + seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u", + NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index); + for (i = 0; i < e->nstamps; i++) + seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]); + seq_printf(seq, "\n"); + return 0; +} + +static const struct seq_operations recent_seq_ops = { + .start = recent_seq_start, + .next = recent_seq_next, + .stop = recent_seq_stop, + .show = recent_seq_show, +}; + +static int recent_seq_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + struct recent_iter_state *st; + + st = __seq_open_private(file, &recent_seq_ops, sizeof(*st)); + if (st == NULL) + return -ENOMEM; + + st->table = pde->data; + return 0; +} + +static ssize_t recent_proc_write(struct file *file, const char __user *input, + size_t size, loff_t *loff) +{ + const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + struct recent_table *t = pde->data; + struct recent_entry *e; + char buf[sizeof("+255.255.255.255")], *c = buf; + __be32 addr; + int add; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size)) + return -EFAULT; + while (isspace(*c)) + c++; + + if (size - (c - buf) < 5) + return c - buf; + if (!strncmp(c, "clear", 5)) { + c += 5; + spin_lock_bh(&recent_lock); + recent_table_flush(t); + spin_unlock_bh(&recent_lock); + return c - buf; + } + + switch (*c) { + case '-': + add = 0; + c++; + break; + case '+': + c++; + default: + add = 1; + break; + } + addr = in_aton(c); + + spin_lock_bh(&recent_lock); + e = recent_entry_lookup(t, addr, 0); + if (e == NULL) { + if (add) + recent_entry_init(t, addr, 0); + } else { + if (add) + recent_entry_update(t, e); + else + recent_entry_remove(t, e); + } + spin_unlock_bh(&recent_lock); + return size; +} + +static const struct file_operations recent_fops = { + .open = recent_seq_open, + .read = seq_read, + .write = recent_proc_write, + .release = seq_release_private, + .owner = THIS_MODULE, +}; +#endif /* CONFIG_PROC_FS */ + +static struct xt_match recent_mt_reg __read_mostly = { + .name = "recent", + .family = AF_INET, + .match = recent_mt, + .matchsize = sizeof(struct xt_recent_mtinfo), + .checkentry = recent_mt_check, + .destroy = recent_mt_destroy, + .me = THIS_MODULE, +}; + +static int __init recent_mt_init(void) +{ + int err; + + if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255) + return -EINVAL; + ip_list_hash_size = 1 << fls(ip_list_tot); + + err = xt_register_match(&recent_mt_reg); +#ifdef CONFIG_PROC_FS + if (err) + return err; + proc_dir = proc_mkdir("ipt_recent", init_net.proc_net); + if (proc_dir == NULL) { + xt_unregister_match(&recent_mt_reg); + err = -ENOMEM; + } +#endif + return err; +} + +static void __exit recent_mt_exit(void) +{ + BUG_ON(!list_empty(&tables)); + xt_unregister_match(&recent_mt_reg); +#ifdef CONFIG_PROC_FS + remove_proc_entry("ipt_recent", init_net.proc_net); +#endif +} + +module_init(recent_mt_init); +module_exit(recent_mt_exit); -- cgit v1.2.3 From 7e9c6eeb136a46dfd941852803b3a9dd78939b69 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:00 +0200 Subject: netfilter: Introduce NFPROTO_* constants The netfilter subsystem only supports a handful of protocols (much less than PF_*) and even non-PF protocols like ARP and pseudo-protocols like PF_BRIDGE. By creating NFPROTO_*, we can earn a few memory savings on arrays that previously were always PF_MAX-sized and keep the pseudo-protocols to ourselves. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 14 ++++++++++++-- net/netfilter/core.c | 6 +++--- net/netfilter/nf_log.c | 12 ++++++------ net/netfilter/nf_queue.c | 12 ++++++------ net/netfilter/x_tables.c | 18 ++++++++++-------- 5 files changed, 37 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 8c83d2e23bde..bf3afb0844f7 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -52,6 +52,16 @@ enum nf_inet_hooks { NF_INET_NUMHOOKS }; +enum { + NFPROTO_UNSPEC = 0, + NFPROTO_IPV4 = 2, + NFPROTO_ARP = 3, + NFPROTO_BRIDGE = 7, + NFPROTO_IPV6 = 10, + NFPROTO_DECNET = 12, + NFPROTO_NUMPROTO, +}; + union nf_inet_addr { __u32 all[4]; __be32 ip; @@ -138,7 +148,7 @@ extern struct ctl_path nf_net_netfilter_sysctl_path[]; extern struct ctl_path nf_net_ipv4_netfilter_sysctl_path[]; #endif /* CONFIG_SYSCTL */ -extern struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]; +extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, @@ -247,7 +257,7 @@ struct nf_afinfo { int route_key_size; }; -extern const struct nf_afinfo *nf_afinfo[NPROTO]; +extern const struct nf_afinfo *nf_afinfo[NFPROTO_NUMPROTO]; static inline const struct nf_afinfo *nf_get_afinfo(unsigned short family) { return rcu_dereference(nf_afinfo[family]); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 26b8f489d7a2..b16cd79951c6 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -26,7 +26,7 @@ static DEFINE_MUTEX(afinfo_mutex); -const struct nf_afinfo *nf_afinfo[NPROTO] __read_mostly; +const struct nf_afinfo *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly; EXPORT_SYMBOL(nf_afinfo); int nf_register_afinfo(const struct nf_afinfo *afinfo) @@ -51,7 +51,7 @@ void nf_unregister_afinfo(const struct nf_afinfo *afinfo) } EXPORT_SYMBOL_GPL(nf_unregister_afinfo); -struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS] __read_mostly; +struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly; EXPORT_SYMBOL(nf_hooks); static DEFINE_MUTEX(nf_hook_mutex); @@ -264,7 +264,7 @@ EXPORT_SYMBOL(proc_net_netfilter); void __init netfilter_init(void) { int i, h; - for (i = 0; i < NPROTO; i++) { + for (i = 0; i < ARRAY_SIZE(nf_hooks); i++) { for (h = 0; h < NF_MAX_HOOKS; h++) INIT_LIST_HEAD(&nf_hooks[i][h]); } diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 5c2f73320154..fa8ae5d2659c 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -15,7 +15,7 @@ #define NF_LOG_PREFIXLEN 128 -static const struct nf_logger *nf_loggers[NPROTO] __read_mostly; +static const struct nf_logger *nf_loggers[NFPROTO_NUMPROTO] __read_mostly; static DEFINE_MUTEX(nf_log_mutex); /* return EBUSY if somebody else is registered, EEXIST if the same logger @@ -24,7 +24,7 @@ int nf_log_register(u_int8_t pf, const struct nf_logger *logger) { int ret; - if (pf >= NPROTO) + if (pf >= ARRAY_SIZE(nf_loggers)) return -EINVAL; /* Any setup of logging members must be done before @@ -47,7 +47,7 @@ EXPORT_SYMBOL(nf_log_register); void nf_log_unregister_pf(u_int8_t pf) { - if (pf >= NPROTO) + if (pf >= ARRAY_SIZE(nf_loggers)) return; mutex_lock(&nf_log_mutex); rcu_assign_pointer(nf_loggers[pf], NULL); @@ -63,7 +63,7 @@ void nf_log_unregister(const struct nf_logger *logger) int i; mutex_lock(&nf_log_mutex); - for (i = 0; i < NPROTO; i++) { + for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) { if (nf_loggers[i] == logger) rcu_assign_pointer(nf_loggers[i], NULL); } @@ -103,7 +103,7 @@ static void *seq_start(struct seq_file *seq, loff_t *pos) { rcu_read_lock(); - if (*pos >= NPROTO) + if (*pos >= ARRAY_SIZE(nf_loggers)) return NULL; return pos; @@ -113,7 +113,7 @@ static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; - if (*pos >= NPROTO) + if (*pos >= ARRAY_SIZE(nf_loggers)) return NULL; return pos; diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index f285086f6292..4f2310c93e01 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -16,7 +16,7 @@ * long term mutex. The handler must provide an an outfn() to accept packets * for queueing and must reinject all packets it receives, no matter what. */ -static const struct nf_queue_handler *queue_handler[NPROTO]; +static const struct nf_queue_handler *queue_handler[NFPROTO_NUMPROTO] __read_mostly; static DEFINE_MUTEX(queue_handler_mutex); @@ -26,7 +26,7 @@ int nf_register_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) { int ret; - if (pf >= NPROTO) + if (pf >= ARRAY_SIZE(queue_handler)) return -EINVAL; mutex_lock(&queue_handler_mutex); @@ -47,7 +47,7 @@ EXPORT_SYMBOL(nf_register_queue_handler); /* The caller must flush their queue before this */ int nf_unregister_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) { - if (pf >= NPROTO) + if (pf >= ARRAY_SIZE(queue_handler)) return -EINVAL; mutex_lock(&queue_handler_mutex); @@ -70,7 +70,7 @@ void nf_unregister_queue_handlers(const struct nf_queue_handler *qh) u_int8_t pf; mutex_lock(&queue_handler_mutex); - for (pf = 0; pf < NPROTO; pf++) { + for (pf = 0; pf < ARRAY_SIZE(queue_handler); pf++) { if (queue_handler[pf] == qh) rcu_assign_pointer(queue_handler[pf], NULL); } @@ -285,7 +285,7 @@ EXPORT_SYMBOL(nf_reinject); #ifdef CONFIG_PROC_FS static void *seq_start(struct seq_file *seq, loff_t *pos) { - if (*pos >= NPROTO) + if (*pos >= ARRAY_SIZE(queue_handler)) return NULL; return pos; @@ -295,7 +295,7 @@ static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; - if (*pos >= NPROTO) + if (*pos >= ARRAY_SIZE(queue_handler)) return NULL; return pos; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index cf2f3e90cef9..2a7eb1da5d03 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -58,10 +58,12 @@ static struct xt_af *xt; #define duprintf(format, args...) #endif -static const char *const xt_prefix[NPROTO] = { - [AF_INET] = "ip", - [AF_INET6] = "ip6", - [NF_ARP] = "arp", +static const char *const xt_prefix[NFPROTO_NUMPROTO] = { + [NFPROTO_UNSPEC] = "x", + [NFPROTO_IPV4] = "ip", + [NFPROTO_ARP] = "arp", + [NFPROTO_BRIDGE] = "eb", + [NFPROTO_IPV6] = "ip6", }; /* Registration hooks for targets. */ @@ -932,7 +934,7 @@ int xt_proto_init(struct net *net, u_int8_t af) struct proc_dir_entry *proc; #endif - if (af >= NPROTO) + if (af >= ARRAY_SIZE(xt_prefix)) return -EINVAL; @@ -1001,7 +1003,7 @@ static int __net_init xt_net_init(struct net *net) { int i; - for (i = 0; i < NPROTO; i++) + for (i = 0; i < NFPROTO_NUMPROTO; i++) INIT_LIST_HEAD(&net->xt.tables[i]); return 0; } @@ -1014,11 +1016,11 @@ static int __init xt_init(void) { int i, rv; - xt = kmalloc(sizeof(struct xt_af) * NPROTO, GFP_KERNEL); + xt = kmalloc(sizeof(struct xt_af) * NFPROTO_NUMPROTO, GFP_KERNEL); if (!xt) return -ENOMEM; - for (i = 0; i < NPROTO; i++) { + for (i = 0; i < NFPROTO_NUMPROTO; i++) { mutex_init(&xt[i].mutex); #ifdef CONFIG_COMPAT mutex_init(&xt[i].compat_mutex); -- cgit v1.2.3 From 48dc7865aa3db9404aedc8677d9daf8f8f469ab0 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 8 Oct 2008 11:35:01 +0200 Subject: netfilter: netns: remove nf_*_net() wrappers Now that dev_net() exists, the usefullness of them is even less. Also they're a big problem in resolving circular header dependencies necessary for NOTRACK-in-netns patch. See below. Signed-off-by: Alexey Dobriyan Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 53 ---------------------------------- net/ipv4/netfilter/iptable_filter.c | 6 ++-- net/ipv4/netfilter/iptable_mangle.c | 10 +++---- net/ipv4/netfilter/iptable_raw.c | 4 +-- net/ipv4/netfilter/iptable_security.c | 6 ++-- net/ipv6/netfilter/ip6table_filter.c | 6 ++-- net/ipv6/netfilter/ip6table_security.c | 6 ++-- 7 files changed, 19 insertions(+), 72 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index bf3afb0844f7..48cfe51bfddc 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -5,13 +5,11 @@ #include #include #include -#include #include #include #include #include #include -#include #endif #include #include @@ -355,56 +353,5 @@ extern void (*nf_ct_destroy)(struct nf_conntrack *); static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif -static inline struct net *nf_pre_routing_net(const struct net_device *in, - const struct net_device *out) -{ -#ifdef CONFIG_NET_NS - return in->nd_net; -#else - return &init_net; -#endif -} - -static inline struct net *nf_local_in_net(const struct net_device *in, - const struct net_device *out) -{ -#ifdef CONFIG_NET_NS - return in->nd_net; -#else - return &init_net; -#endif -} - -static inline struct net *nf_forward_net(const struct net_device *in, - const struct net_device *out) -{ -#ifdef CONFIG_NET_NS - BUG_ON(in->nd_net != out->nd_net); - return in->nd_net; -#else - return &init_net; -#endif -} - -static inline struct net *nf_local_out_net(const struct net_device *in, - const struct net_device *out) -{ -#ifdef CONFIG_NET_NS - return out->nd_net; -#else - return &init_net; -#endif -} - -static inline struct net *nf_post_routing_net(const struct net_device *in, - const struct net_device *out) -{ -#ifdef CONFIG_NET_NS - return out->nd_net; -#else - return &init_net; -#endif -} - #endif /*__KERNEL__*/ #endif /*__LINUX_NETFILTER_H*/ diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 1ea677dcf845..c9224310ebae 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -70,7 +70,7 @@ ipt_local_in_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_local_in_net(in, out)->ipv4.iptable_filter); + dev_net(in)->ipv4.iptable_filter); } static unsigned int @@ -81,7 +81,7 @@ ipt_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_forward_net(in, out)->ipv4.iptable_filter); + dev_net(in)->ipv4.iptable_filter); } static unsigned int @@ -101,7 +101,7 @@ ipt_local_out_hook(unsigned int hook, } return ipt_do_table(skb, hook, in, out, - nf_local_out_net(in, out)->ipv4.iptable_filter); + dev_net(out)->ipv4.iptable_filter); } static struct nf_hook_ops ipt_ops[] __read_mostly = { diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index da59182f2226..69f2c4287146 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -81,7 +81,7 @@ ipt_pre_routing_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_pre_routing_net(in, out)->ipv4.iptable_mangle); + dev_net(in)->ipv4.iptable_mangle); } static unsigned int @@ -92,7 +92,7 @@ ipt_post_routing_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_post_routing_net(in, out)->ipv4.iptable_mangle); + dev_net(out)->ipv4.iptable_mangle); } static unsigned int @@ -103,7 +103,7 @@ ipt_local_in_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_local_in_net(in, out)->ipv4.iptable_mangle); + dev_net(in)->ipv4.iptable_mangle); } static unsigned int @@ -114,7 +114,7 @@ ipt_forward_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_forward_net(in, out)->ipv4.iptable_mangle); + dev_net(in)->ipv4.iptable_mangle); } static unsigned int @@ -147,7 +147,7 @@ ipt_local_hook(unsigned int hook, tos = iph->tos; ret = ipt_do_table(skb, hook, in, out, - nf_local_out_net(in, out)->ipv4.iptable_mangle); + dev_net(out)->ipv4.iptable_mangle); /* Reroute for ANY change. */ if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE) { iph = ip_hdr(skb); diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index fddce7754b72..8faebfe638f1 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -53,7 +53,7 @@ ipt_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_pre_routing_net(in, out)->ipv4.iptable_raw); + dev_net(in)->ipv4.iptable_raw); } static unsigned int @@ -72,7 +72,7 @@ ipt_local_hook(unsigned int hook, return NF_ACCEPT; } return ipt_do_table(skb, hook, in, out, - nf_local_out_net(in, out)->ipv4.iptable_raw); + dev_net(out)->ipv4.iptable_raw); } /* 'raw' is the very first table. */ diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index db6d312128e1..36f3be3cc428 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -73,7 +73,7 @@ ipt_local_in_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_local_in_net(in, out)->ipv4.iptable_security); + dev_net(in)->ipv4.iptable_security); } static unsigned int @@ -84,7 +84,7 @@ ipt_forward_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ipt_do_table(skb, hook, in, out, - nf_forward_net(in, out)->ipv4.iptable_security); + dev_net(in)->ipv4.iptable_security); } static unsigned int @@ -103,7 +103,7 @@ ipt_local_out_hook(unsigned int hook, return NF_ACCEPT; } return ipt_do_table(skb, hook, in, out, - nf_local_out_net(in, out)->ipv4.iptable_security); + dev_net(out)->ipv4.iptable_security); } static struct nf_hook_ops ipt_ops[] __read_mostly = { diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index 55a2c290bad4..b110a8a85a14 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -68,7 +68,7 @@ ip6t_local_in_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ip6t_do_table(skb, hook, in, out, - nf_local_in_net(in, out)->ipv6.ip6table_filter); + dev_net(in)->ipv6.ip6table_filter); } static unsigned int @@ -79,7 +79,7 @@ ip6t_forward_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ip6t_do_table(skb, hook, in, out, - nf_forward_net(in, out)->ipv6.ip6table_filter); + dev_net(in)->ipv6.ip6table_filter); } static unsigned int @@ -100,7 +100,7 @@ ip6t_local_out_hook(unsigned int hook, #endif return ip6t_do_table(skb, hook, in, out, - nf_local_out_net(in, out)->ipv6.ip6table_filter); + dev_net(out)->ipv6.ip6table_filter); } static struct nf_hook_ops ip6t_ops[] __read_mostly = { diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index 6e7131036bc6..20bc52f13e43 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -72,7 +72,7 @@ ip6t_local_in_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ip6t_do_table(skb, hook, in, out, - nf_local_in_net(in, out)->ipv6.ip6table_security); + dev_net(in)->ipv6.ip6table_security); } static unsigned int @@ -83,7 +83,7 @@ ip6t_forward_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { return ip6t_do_table(skb, hook, in, out, - nf_forward_net(in, out)->ipv6.ip6table_security); + dev_net(in)->ipv6.ip6table_security); } static unsigned int @@ -95,7 +95,7 @@ ip6t_local_out_hook(unsigned int hook, { /* TBD: handle short packets via raw socket */ return ip6t_do_table(skb, hook, in, out, - nf_local_out_net(in, out)->ipv6.ip6table_security); + dev_net(out)->ipv6.ip6table_security); } static struct nf_hook_ops ip6t_ops[] __read_mostly = { -- cgit v1.2.3 From 3bb0d1c00f86b13bb184193a8f0189ddd6f0459f Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 8 Oct 2008 11:35:10 +0200 Subject: netfilter: netns nf_conntrack: GRE conntracking in netns * make keymap list per-netns * per-netns keymal lock (not strictly necessary) * flush keymap at netns stop and module unload. Signed-off-by: Alexey Dobriyan Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_conntrack_proto_gre.h | 2 +- net/netfilter/nf_conntrack_pptp.c | 2 +- net/netfilter/nf_conntrack_proto_gre.c | 97 ++++++++++++++++++------ 3 files changed, 76 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nf_conntrack_proto_gre.h b/include/linux/netfilter/nf_conntrack_proto_gre.h index 535e4219d2bb..2a10efda17fb 100644 --- a/include/linux/netfilter/nf_conntrack_proto_gre.h +++ b/include/linux/netfilter/nf_conntrack_proto_gre.h @@ -87,7 +87,7 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, /* delete keymap entries */ void nf_ct_gre_keymap_destroy(struct nf_conn *ct); -extern void nf_ct_gre_keymap_flush(void); +extern void nf_ct_gre_keymap_flush(struct net *net); extern void nf_nat_need_gre(void); #endif /* __KERNEL__ */ diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 5db7df5d19b7..e47d5de41cc2 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -602,7 +602,7 @@ static int __init nf_conntrack_pptp_init(void) static void __exit nf_conntrack_pptp_fini(void) { nf_conntrack_helper_unregister(&pptp); - nf_ct_gre_keymap_flush(); + nf_ct_gre_keymap_flush(&init_net); } module_init(nf_conntrack_pptp_init); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 5b1273a01fe3..a2cdbcbf64c4 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -29,8 +29,11 @@ #include #include #include +#include #include - +#include +#include +#include #include #include #include @@ -40,19 +43,23 @@ #define GRE_TIMEOUT (30 * HZ) #define GRE_STREAM_TIMEOUT (180 * HZ) -static DEFINE_RWLOCK(nf_ct_gre_lock); -static LIST_HEAD(gre_keymap_list); +static int proto_gre_net_id; +struct netns_proto_gre { + rwlock_t keymap_lock; + struct list_head keymap_list; +}; -void nf_ct_gre_keymap_flush(void) +void nf_ct_gre_keymap_flush(struct net *net) { + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); struct nf_ct_gre_keymap *km, *tmp; - write_lock_bh(&nf_ct_gre_lock); - list_for_each_entry_safe(km, tmp, &gre_keymap_list, list) { + write_lock_bh(&net_gre->keymap_lock); + list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) { list_del(&km->list); kfree(km); } - write_unlock_bh(&nf_ct_gre_lock); + write_unlock_bh(&net_gre->keymap_lock); } EXPORT_SYMBOL(nf_ct_gre_keymap_flush); @@ -67,19 +74,20 @@ static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km, } /* look up the source key for a given tuple */ -static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t) +static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t) { + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); struct nf_ct_gre_keymap *km; __be16 key = 0; - read_lock_bh(&nf_ct_gre_lock); - list_for_each_entry(km, &gre_keymap_list, list) { + read_lock_bh(&net_gre->keymap_lock); + list_for_each_entry(km, &net_gre->keymap_list, list) { if (gre_key_cmpfn(km, t)) { key = km->tuple.src.u.gre.key; break; } } - read_unlock_bh(&nf_ct_gre_lock); + read_unlock_bh(&net_gre->keymap_lock); pr_debug("lookup src key 0x%x for ", key); nf_ct_dump_tuple(t); @@ -91,20 +99,22 @@ static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t) int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, struct nf_conntrack_tuple *t) { + struct net *net = nf_ct_net(ct); + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); struct nf_conn_help *help = nfct_help(ct); struct nf_ct_gre_keymap **kmp, *km; kmp = &help->help.ct_pptp_info.keymap[dir]; if (*kmp) { /* check whether it's a retransmission */ - read_lock_bh(&nf_ct_gre_lock); - list_for_each_entry(km, &gre_keymap_list, list) { + read_lock_bh(&net_gre->keymap_lock); + list_for_each_entry(km, &net_gre->keymap_list, list) { if (gre_key_cmpfn(km, t) && km == *kmp) { - read_unlock_bh(&nf_ct_gre_lock); + read_unlock_bh(&net_gre->keymap_lock); return 0; } } - read_unlock_bh(&nf_ct_gre_lock); + read_unlock_bh(&net_gre->keymap_lock); pr_debug("trying to override keymap_%s for ct %p\n", dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct); return -EEXIST; @@ -119,9 +129,9 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, pr_debug("adding new entry %p: ", km); nf_ct_dump_tuple(&km->tuple); - write_lock_bh(&nf_ct_gre_lock); - list_add_tail(&km->list, &gre_keymap_list); - write_unlock_bh(&nf_ct_gre_lock); + write_lock_bh(&net_gre->keymap_lock); + list_add_tail(&km->list, &net_gre->keymap_list); + write_unlock_bh(&net_gre->keymap_lock); return 0; } @@ -130,12 +140,14 @@ EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add); /* destroy the keymap entries associated with specified master ct */ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) { + struct net *net = nf_ct_net(ct); + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); struct nf_conn_help *help = nfct_help(ct); enum ip_conntrack_dir dir; pr_debug("entering for ct %p\n", ct); - write_lock_bh(&nf_ct_gre_lock); + write_lock_bh(&net_gre->keymap_lock); for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { if (help->help.ct_pptp_info.keymap[dir]) { pr_debug("removing %p from list\n", @@ -145,7 +157,7 @@ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) help->help.ct_pptp_info.keymap[dir] = NULL; } } - write_unlock_bh(&nf_ct_gre_lock); + write_unlock_bh(&net_gre->keymap_lock); } EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy); @@ -164,6 +176,7 @@ static bool gre_invert_tuple(struct nf_conntrack_tuple *tuple, static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) { + struct net *net = dev_net(skb->dev ? skb->dev : skb->dst->dev); const struct gre_hdr_pptp *pgrehdr; struct gre_hdr_pptp _pgrehdr; __be16 srckey; @@ -190,7 +203,7 @@ static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, } tuple->dst.u.gre.key = pgrehdr->call_id; - srckey = gre_keymap_lookup(tuple); + srckey = gre_keymap_lookup(net, tuple); tuple->src.u.gre.key = srckey; return true; @@ -285,15 +298,53 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { #endif }; +static int proto_gre_net_init(struct net *net) +{ + struct netns_proto_gre *net_gre; + int rv; + + net_gre = kmalloc(sizeof(struct netns_proto_gre), GFP_KERNEL); + if (!net_gre) + return -ENOMEM; + rwlock_init(&net_gre->keymap_lock); + INIT_LIST_HEAD(&net_gre->keymap_list); + + rv = net_assign_generic(net, proto_gre_net_id, net_gre); + if (rv < 0) + kfree(net_gre); + return rv; +} + +static void proto_gre_net_exit(struct net *net) +{ + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); + + nf_ct_gre_keymap_flush(net); + kfree(net_gre); +} + +static struct pernet_operations proto_gre_net_ops = { + .init = proto_gre_net_init, + .exit = proto_gre_net_exit, +}; + static int __init nf_ct_proto_gre_init(void) { - return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); + int rv; + + rv = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); + if (rv < 0) + return rv; + rv = register_pernet_gen_device(&proto_gre_net_id, &proto_gre_net_ops); + if (rv < 0) + nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); + return rv; } static void nf_ct_proto_gre_fini(void) { nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); - nf_ct_gre_keymap_flush(); + unregister_pernet_gen_device(proto_gre_net_id, &proto_gre_net_ops); } module_init(nf_ct_proto_gre_init); -- cgit v1.2.3 From e84392707e10301b93121e1b74e2823db50cdf9e Mon Sep 17 00:00:00 2001 From: KOVACS Krisztian Date: Wed, 8 Oct 2008 11:35:12 +0200 Subject: netfilter: iptables TPROXY target The TPROXY target implements redirection of non-local TCP/UDP traffic to local sockets. Additionally, it's possible to manipulate the packet mark if and only if a socket has been found. (We need this because we cannot use multiple targets in the same iptables rule.) Signed-off-by: KOVACS Krisztian Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_TPROXY.h | 14 +++++ net/netfilter/Kconfig | 15 +++++ net/netfilter/Makefile | 1 + net/netfilter/xt_TPROXY.c | 112 ++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 include/linux/netfilter/xt_TPROXY.h create mode 100644 net/netfilter/xt_TPROXY.c (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h new file mode 100644 index 000000000000..152e8f97132b --- /dev/null +++ b/include/linux/netfilter/xt_TPROXY.h @@ -0,0 +1,14 @@ +#ifndef _XT_TPROXY_H_target +#define _XT_TPROXY_H_target + +/* TPROXY target is capable of marking the packet to perform + * redirection. We can get rid of that whenever we get support for + * mutliple targets in the same rule. */ +struct xt_tproxy_target_info { + u_int32_t mark_mask; + u_int32_t mark_value; + __be32 laddr; + __be16 lport; +}; + +#endif /* _XT_TPROXY_H_target */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f6c807299487..de18bba619f2 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -421,6 +421,21 @@ config NETFILTER_XT_TARGET_RATEEST To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_TARGET_TPROXY + tristate '"TPROXY" target support (EXPERIMENTAL)' + depends on EXPERIMENTAL + depends on NETFILTER_TPROXY + depends on NETFILTER_XTABLES + depends on NETFILTER_ADVANCED + select NF_DEFRAG_IPV4 + help + This option adds a `TPROXY' target, which is somewhat similar to + REDIRECT. It can only be used in the mangle table and is useful + to redirect traffic to a transparent proxy. It does _not_ depend + on Netfilter connection tracking and NAT, unlike REDIRECT. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_TARGET_TRACE tristate '"TRACE" target support' depends on NETFILTER_XTABLES diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 1cdc3a13d01f..8ce67665882d 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o obj-$(CONFIG_NETFILTER_XT_TARGET_RATEEST) += xt_RATEEST.o obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o +obj-$(CONFIG_NETFILTER_XT_TARGET_TPROXY) += xt_TPROXY.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c new file mode 100644 index 000000000000..183f251d2f06 --- /dev/null +++ b/net/netfilter/xt_TPROXY.c @@ -0,0 +1,112 @@ +/* + * Transparent proxy support for Linux/iptables + * + * Copyright (c) 2006-2007 BalaBit IT Ltd. + * Author: Balazs Scheidler, Krisztian Kovacs + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static unsigned int +tproxy_tg(struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const struct xt_target *target, + const void *targinfo) +{ + const struct iphdr *iph = ip_hdr(skb); + const struct xt_tproxy_target_info *tgi = targinfo; + struct udphdr _hdr, *hp; + struct sock *sk; + + hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); + if (hp == NULL) + return NF_DROP; + + sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, + iph->saddr, tgi->laddr ? tgi->laddr : iph->daddr, + hp->source, tgi->lport ? tgi->lport : hp->dest, + in, true); + + /* NOTE: assign_sock consumes our sk reference */ + if (sk && nf_tproxy_assign_sock(skb, sk)) { + /* This should be in a separate target, but we don't do multiple + targets on the same rule yet */ + skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; + + pr_debug("redirecting: proto %u %08x:%u -> %08x:%u, mark: %x\n", + iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), + ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark); + return NF_ACCEPT; + } + + pr_debug("no socket, dropping: proto %u %08x:%u -> %08x:%u, mark: %x\n", + iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), + ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark); + return NF_DROP; +} + +static bool +tproxy_tg_check(const char *tablename, + const void *entry, + const struct xt_target *target, + void *targetinfo, + unsigned int hook_mask) +{ + const struct ipt_ip *i = entry; + + if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) + && !(i->invflags & IPT_INV_PROTO)) + return true; + + pr_info("xt_TPROXY: Can be used only in combination with " + "either -p tcp or -p udp\n"); + return false; +} + +static struct xt_target tproxy_tg_reg __read_mostly = { + .name = "TPROXY", + .family = AF_INET, + .table = "mangle", + .target = tproxy_tg, + .targetsize = sizeof(struct xt_tproxy_target_info), + .checkentry = tproxy_tg_check, + .hooks = 1 << NF_INET_PRE_ROUTING, + .me = THIS_MODULE, +}; + +static int __init tproxy_tg_init(void) +{ + nf_defrag_ipv4_enable(); + return xt_register_target(&tproxy_tg_reg); +} + +static void __exit tproxy_tg_exit(void) +{ + xt_unregister_target(&tproxy_tg_reg); +} + +module_init(tproxy_tg_init); +module_exit(tproxy_tg_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Krisztian Kovacs"); +MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); +MODULE_ALIAS("ipt_TPROXY"); -- cgit v1.2.3 From 18219d3f7d6a5bc43825a41e0763158efbdb80d3 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:13 +0200 Subject: netfilter: ebtables: do centralized size checking Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 3 +++ net/bridge/netfilter/ebt_802_3.c | 7 +++-- net/bridge/netfilter/ebt_among.c | 1 + net/bridge/netfilter/ebt_arp.c | 9 +++---- net/bridge/netfilter/ebt_arpreply.c | 9 +++---- net/bridge/netfilter/ebt_dnat.c | 9 +++---- net/bridge/netfilter/ebt_ip.c | 9 +++---- net/bridge/netfilter/ebt_ip6.c | 9 +++---- net/bridge/netfilter/ebt_limit.c | 11 +++----- net/bridge/netfilter/ebt_log.c | 11 ++++---- net/bridge/netfilter/ebt_mark.c | 6 ++--- net/bridge/netfilter/ebt_mark_m.c | 7 +++-- net/bridge/netfilter/ebt_nflog.c | 4 +-- net/bridge/netfilter/ebt_pkttype.c | 7 +++-- net/bridge/netfilter/ebt_redirect.c | 11 ++++---- net/bridge/netfilter/ebt_snat.c | 11 ++++---- net/bridge/netfilter/ebt_stp.c | 10 +++---- net/bridge/netfilter/ebt_ulog.c | 5 ++-- net/bridge/netfilter/ebt_vlan.c | 10 ++----- net/bridge/netfilter/ebtables.c | 43 +++++++++++++++++++++++++++---- 20 files changed, 104 insertions(+), 88 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index 892f5b7771c7..fd085af8962d 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -215,6 +215,7 @@ struct ebt_match int (*check)(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *matchdata, unsigned int datalen); void (*destroy)(void *matchdata, unsigned int datalen); + unsigned int matchsize; struct module *me; }; @@ -229,6 +230,7 @@ struct ebt_watcher int (*check)(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *watcherdata, unsigned int datalen); void (*destroy)(void *watcherdata, unsigned int datalen); + unsigned int targetsize; struct module *me; }; @@ -244,6 +246,7 @@ struct ebt_target int (*check)(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *targetdata, unsigned int datalen); void (*destroy)(void *targetdata, unsigned int datalen); + unsigned int targetsize; struct module *me; }; diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 98534025360f..ccecfbd2a25d 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -7,10 +7,10 @@ * May 2003 * */ - +#include +#include #include #include -#include static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) @@ -42,8 +42,6 @@ static int ebt_802_3_check(const char *tablename, unsigned int hookmask, { const struct ebt_802_3_info *info = data; - if (datalen < sizeof(struct ebt_802_3_info)) - return -EINVAL; if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK) return -EINVAL; @@ -54,6 +52,7 @@ static struct ebt_match filter_802_3 __read_mostly = { .name = EBT_802_3_MATCH, .match = ebt_filter_802_3, .check = ebt_802_3_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_802_3_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 70b6dca5ea75..b0acb13a390c 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -216,6 +216,7 @@ static struct ebt_match filter_among __read_mostly = { .name = EBT_AMONG_MATCH, .match = ebt_filter_among, .check = ebt_among_check, + .matchsize = -1, /* special case */ .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 7c535be75665..385f9cb85bce 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -8,12 +8,12 @@ * April, 2002 * */ - -#include -#include #include #include #include +#include +#include +#include static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) @@ -105,8 +105,6 @@ static int ebt_arp_check(const char *tablename, unsigned int hookmask, { const struct ebt_arp_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info))) - return -EINVAL; if ((e->ethproto != htons(ETH_P_ARP) && e->ethproto != htons(ETH_P_RARP)) || e->invflags & EBT_IPROTO) @@ -120,6 +118,7 @@ static struct ebt_match filter_arp __read_mostly = { .name = EBT_ARP_MATCH, .match = ebt_filter_arp, .check = ebt_arp_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_arp_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index 0c4279590fc7..a860ea6da46a 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -8,12 +8,12 @@ * August, 2003 * */ - -#include -#include #include #include #include +#include +#include +#include static int ebt_target_reply(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, @@ -63,8 +63,6 @@ static int ebt_target_reply_check(const char *tablename, unsigned int hookmask, { const struct ebt_arpreply_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info))) - return -EINVAL; if (BASE_CHAIN && info->target == EBT_RETURN) return -EINVAL; if (e->ethproto != htons(ETH_P_ARP) || @@ -80,6 +78,7 @@ static struct ebt_target reply_target __read_mostly = { .name = EBT_ARPREPLY_TARGET, .target = ebt_target_reply, .check = ebt_target_reply_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_arpreply_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index ca64c1cc1b47..c2be41e8bb99 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -7,12 +7,12 @@ * June, 2002 * */ - +#include +#include #include +#include #include #include -#include -#include static int ebt_target_dnat(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, @@ -39,8 +39,6 @@ static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask, (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) && (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) return -EINVAL; - if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info))) - return -EINVAL; if (INVALID_TARGET) return -EINVAL; return 0; @@ -50,6 +48,7 @@ static struct ebt_target dnat __read_mostly = { .name = EBT_DNAT_TARGET, .target = ebt_target_dnat, .check = ebt_target_dnat_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_nat_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index 65caa00dcf2a..c1ae2547e3d0 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -11,13 +11,13 @@ * Innominate Security Technologies AG * September, 2002 */ - -#include -#include #include #include #include #include +#include +#include +#include struct tcpudphdr { __be16 src; @@ -83,8 +83,6 @@ static int ebt_ip_check(const char *tablename, unsigned int hookmask, { const struct ebt_ip_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info))) - return -EINVAL; if (e->ethproto != htons(ETH_P_IP) || e->invflags & EBT_IPROTO) return -EINVAL; @@ -111,6 +109,7 @@ static struct ebt_match filter_ip __read_mostly = { .name = EBT_IP_MATCH, .match = ebt_filter_ip, .check = ebt_ip_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_ip_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 36efb3a75249..554dd68637c8 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -13,14 +13,14 @@ * * Jan, 2008 */ - -#include -#include #include #include #include #include #include +#include +#include +#include struct tcpudphdr { __be16 src; @@ -97,8 +97,6 @@ static int ebt_ip6_check(const char *tablename, unsigned int hookmask, { struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_ip6_info))) - return -EINVAL; if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) return -EINVAL; if (info->bitmask & ~EBT_IP6_MASK || info->invflags & ~EBT_IP6_MASK) @@ -125,6 +123,7 @@ static struct ebt_match filter_ip6 = .name = EBT_IP6_MATCH, .match = ebt_filter_ip6, .check = ebt_ip6_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_ip6_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 8cbdc01c253e..3d71f3510ffa 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -10,13 +10,12 @@ * September, 2003 * */ - -#include -#include #include - #include #include +#include +#include +#include static DEFINE_SPINLOCK(limit_lock); @@ -71,9 +70,6 @@ static int ebt_limit_check(const char *tablename, unsigned int hookmask, { struct ebt_limit_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info))) - return -EINVAL; - /* Check for overflow. */ if (info->burst == 0 || user2credits(info->avg * info->burst) < user2credits(info->avg)) { @@ -94,6 +90,7 @@ static struct ebt_match ebt_limit_reg __read_mostly = { .name = EBT_LIMIT_MATCH, .match = ebt_limit_match, .check = ebt_limit_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_limit_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 8b17c64bcd75..d9596f114a37 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -8,10 +8,6 @@ * April, 2002 * */ - -#include -#include -#include #include #include #include @@ -21,6 +17,10 @@ #include #include #include +#include +#include +#include +#include static DEFINE_SPINLOCK(ebt_log_lock); @@ -29,8 +29,6 @@ static int ebt_log_check(const char *tablename, unsigned int hookmask, { struct ebt_log_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info))) - return -EINVAL; if (info->bitmask & ~EBT_LOG_MASK) return -EINVAL; if (info->loglevel >= 8) @@ -218,6 +216,7 @@ static struct ebt_watcher log = .name = EBT_LOG_WATCHER, .watcher = ebt_log, .check = ebt_log_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_log_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index 36723f47db0a..bb02412786c8 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -13,9 +13,10 @@ * Marking a frame doesn't really change anything in the frame anyway. */ +#include +#include #include #include -#include static int ebt_target_mark(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, @@ -42,8 +43,6 @@ static int ebt_target_mark_check(const char *tablename, unsigned int hookmask, const struct ebt_mark_t_info *info = data; int tmp; - if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info))) - return -EINVAL; tmp = info->target | ~EBT_VERDICT_BITS; if (BASE_CHAIN && tmp == EBT_RETURN) return -EINVAL; @@ -61,6 +60,7 @@ static struct ebt_target mark_target __read_mostly = { .name = EBT_MARK_TARGET, .target = ebt_target_mark, .check = ebt_target_mark_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_mark_t_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index 9b0a4543861f..b8ce9eb71709 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -7,10 +7,10 @@ * July, 2002 * */ - +#include +#include #include #include -#include static int ebt_filter_mark(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, @@ -28,8 +28,6 @@ static int ebt_mark_check(const char *tablename, unsigned int hookmask, { const struct ebt_mark_m_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info))) - return -EINVAL; if (info->bitmask & ~EBT_MARK_MASK) return -EINVAL; if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND)) @@ -43,6 +41,7 @@ static struct ebt_match filter_mark __read_mostly = { .name = EBT_MARK_MATCH, .match = ebt_filter_mark, .check = ebt_mark_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_mark_m_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 8e799aa9e560..88ceb5eb8496 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -42,8 +43,6 @@ static int ebt_nflog_check(const char *tablename, { struct ebt_nflog_info *info = (struct ebt_nflog_info *)data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_nflog_info))) - return -EINVAL; if (info->flags & ~EBT_NFLOG_MASK) return -EINVAL; info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0'; @@ -54,6 +53,7 @@ static struct ebt_watcher nflog __read_mostly = { .name = EBT_NFLOG_WATCHER, .watcher = ebt_nflog, .check = ebt_nflog_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_nflog_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 676db32df3d1..019026177f8b 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -7,10 +7,10 @@ * April, 2003 * */ - +#include +#include #include #include -#include static int ebt_filter_pkttype(const struct sk_buff *skb, const struct net_device *in, @@ -28,8 +28,6 @@ static int ebt_pkttype_check(const char *tablename, unsigned int hookmask, { const struct ebt_pkttype_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info))) - return -EINVAL; if (info->invert != 0 && info->invert != 1) return -EINVAL; /* Allow any pkt_type value */ @@ -40,6 +38,7 @@ static struct ebt_match filter_pkttype __read_mostly = { .name = EBT_PKTTYPE_MATCH, .match = ebt_filter_pkttype, .check = ebt_pkttype_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_pkttype_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index b8afe850cf1e..040532683862 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -7,13 +7,13 @@ * April, 2002 * */ - -#include -#include -#include #include #include #include "../br_private.h" +#include +#include +#include +#include static int ebt_target_redirect(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, @@ -38,8 +38,6 @@ static int ebt_target_redirect_check(const char *tablename, unsigned int hookmas { const struct ebt_redirect_info *info = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info))) - return -EINVAL; if (BASE_CHAIN && info->target == EBT_RETURN) return -EINVAL; CLEAR_BASE_CHAIN_BIT; @@ -55,6 +53,7 @@ static struct ebt_target redirect_target __read_mostly = { .name = EBT_REDIRECT_TARGET, .target = ebt_target_redirect, .check = ebt_target_redirect_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_redirect_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 5425333dda03..abfbc6c95024 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -7,14 +7,14 @@ * June, 2002 * */ - -#include -#include -#include #include #include #include #include +#include +#include +#include +#include static int ebt_target_snat(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, @@ -49,8 +49,6 @@ static int ebt_target_snat_check(const char *tablename, unsigned int hookmask, const struct ebt_nat_info *info = data; int tmp; - if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info))) - return -EINVAL; tmp = info->target | ~EBT_VERDICT_BITS; if (BASE_CHAIN && tmp == EBT_RETURN) return -EINVAL; @@ -72,6 +70,7 @@ static struct ebt_target snat __read_mostly = { .name = EBT_SNAT_TARGET, .target = ebt_target_snat, .check = ebt_target_snat_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_nat_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 40f36d37607d..c7a0a00dac7c 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -7,11 +7,11 @@ * * July, 2003 */ - -#include -#include #include #include +#include +#include +#include #define BPDU_TYPE_CONFIG 0 #define BPDU_TYPE_TCN 0x80 @@ -157,15 +157,12 @@ static int ebt_stp_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_stp_info *info = data; - const unsigned int len = EBT_ALIGN(sizeof(struct ebt_stp_info)); const uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; const uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK || !(info->bitmask & EBT_STP_MASK)) return -EINVAL; - if (datalen != len) - return -EINVAL; /* Make sure the match only receives stp frames */ if (compare_ether_addr(e->destmac, bridge_ula) || compare_ether_addr(e->destmsk, msk) || !(e->bitmask & EBT_DESTMAC)) @@ -178,6 +175,7 @@ static struct ebt_match filter_stp __read_mostly = { .name = EBT_STP_MATCH, .match = ebt_filter_stp, .check = ebt_stp_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_stp_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 3b1678cd66f1..bdd8a27bba9c 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -260,8 +261,7 @@ static int ebt_ulog_check(const char *tablename, unsigned int hookmask, { struct ebt_ulog_info *uloginfo = data; - if (datalen != EBT_ALIGN(sizeof(struct ebt_ulog_info)) || - uloginfo->nlgroup > 31) + if (uloginfo->nlgroup > 31) return -EINVAL; uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0'; @@ -276,6 +276,7 @@ static struct ebt_watcher ulog __read_mostly = { .name = EBT_ULOG_WATCHER, .watcher = ebt_ulog, .check = ebt_ulog_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_ulog_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index ab60b0dade80..4dba47aefc8a 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -93,14 +94,6 @@ ebt_check_vlan(const char *tablename, { struct ebt_vlan_info *info = data; - /* Parameters buffer overflow check */ - if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) { - DEBUG_MSG - ("passed size %d is not eq to ebt_vlan_info (%Zd)\n", - datalen, sizeof(struct ebt_vlan_info)); - return -EINVAL; - } - /* Is it 802.1Q frame checked? */ if (e->ethproto != htons(ETH_P_8021Q)) { DEBUG_MSG @@ -173,6 +166,7 @@ static struct ebt_match filter_vlan __read_mostly = { .name = EBT_VLAN_MATCH, .match = ebt_filter_vlan, .check = ebt_check_vlan, + .matchsize = XT_ALIGN(sizeof(struct ebt_vlan_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 32afff859e4a..b04e288d20f2 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -59,8 +60,9 @@ static LIST_HEAD(ebt_targets); static LIST_HEAD(ebt_matches); static LIST_HEAD(ebt_watchers); -static struct ebt_target ebt_standard_target = -{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL}; +static struct ebt_target ebt_standard_target = { + .name = "standard", +}; static inline int ebt_do_watcher (struct ebt_entry_watcher *w, const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, @@ -350,6 +352,18 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, return -ENOENT; } mutex_unlock(&ebt_mutex); + if (XT_ALIGN(match->matchsize) != m->match_size && + match->matchsize != -1) { + /* + * ebt_among is exempt from centralized matchsize checking + * because it uses a dynamic-size data set. + */ + printk(KERN_WARNING "ebtables: %s match: " + "invalid size %Zu != %u\n", + match->name, XT_ALIGN(match->matchsize), m->match_size); + module_put(match->me); + return -EINVAL; + } if (match->check && match->check(name, hookmask, e, m->data, m->match_size) != 0) { BUGPRINT("match->check failed\n"); @@ -380,6 +394,14 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, return -ENOENT; } mutex_unlock(&ebt_mutex); + if (XT_ALIGN(watcher->targetsize) != w->watcher_size) { + printk(KERN_WARNING "ebtables: %s watcher: " + "invalid size %Zu != %u\n", + watcher->name, XT_ALIGN(watcher->targetsize), + w->watcher_size); + module_put(watcher->me); + return -EINVAL; + } if (watcher->check && watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) { BUGPRINT("watcher->check failed\n"); @@ -681,9 +703,20 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, ret = -EFAULT; goto cleanup_watchers; } - } else if (t->target_size > gap - sizeof(struct ebt_entry_target) || - (t->u.target->check && - t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){ + } else if (t->target_size > gap - sizeof(struct ebt_entry_target)) { + module_put(t->u.target->me); + ret = -EFAULT; + goto cleanup_watchers; + } else if (XT_ALIGN(target->targetsize) != t->target_size) { + printk(KERN_WARNING "ebtables: %s target: " + "invalid size %Zu != %u\n", + target->name, XT_ALIGN(target->targetsize), + t->target_size); + module_put(t->u.target->me); + ret = -EINVAL; + goto cleanup_watchers; + } else if (t->u.target->check && + t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0) { module_put(t->u.target->me); ret = -EFAULT; goto cleanup_watchers; -- cgit v1.2.3 From 19eda879a136889110c692dec4c2ab59e0e43cef Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:13 +0200 Subject: netfilter: change return types of check functions for Ebtables extensions Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 9 +++------ net/bridge/netfilter/ebt_802_3.c | 6 +++--- net/bridge/netfilter/ebt_among.c | 15 ++++++++------- net/bridge/netfilter/ebt_arp.c | 8 ++++---- net/bridge/netfilter/ebt_arpreply.c | 10 +++++----- net/bridge/netfilter/ebt_dnat.c | 10 +++++----- net/bridge/netfilter/ebt_ip.c | 16 ++++++++-------- net/bridge/netfilter/ebt_ip6.c | 16 ++++++++-------- net/bridge/netfilter/ebt_limit.c | 6 +++--- net/bridge/netfilter/ebt_log.c | 8 ++++---- net/bridge/netfilter/ebt_mark.c | 10 +++++----- net/bridge/netfilter/ebt_mark_m.c | 10 +++++----- net/bridge/netfilter/ebt_nflog.c | 12 ++++++------ net/bridge/netfilter/ebt_pkttype.c | 6 +++--- net/bridge/netfilter/ebt_redirect.c | 10 +++++----- net/bridge/netfilter/ebt_snat.c | 14 +++++++------- net/bridge/netfilter/ebt_stp.c | 8 ++++---- net/bridge/netfilter/ebt_ulog.c | 21 ++++++++++++--------- net/bridge/netfilter/ebt_vlan.c | 16 ++++++++-------- net/bridge/netfilter/ebtables.c | 6 +++--- 20 files changed, 109 insertions(+), 108 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index fd085af8962d..5f71719b7a27 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -211,8 +211,7 @@ struct ebt_match int (*match)(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchdata, unsigned int datalen); - /* 0 == let it in */ - int (*check)(const char *tablename, unsigned int hookmask, + bool (*check)(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *matchdata, unsigned int datalen); void (*destroy)(void *matchdata, unsigned int datalen); unsigned int matchsize; @@ -226,8 +225,7 @@ struct ebt_watcher void (*watcher)(const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *watcherdata, unsigned int datalen); - /* 0 == let it in */ - int (*check)(const char *tablename, unsigned int hookmask, + bool (*check)(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *watcherdata, unsigned int datalen); void (*destroy)(void *watcherdata, unsigned int datalen); unsigned int targetsize; @@ -242,8 +240,7 @@ struct ebt_target int (*target)(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *targetdata, unsigned int datalen); - /* 0 == let it in */ - int (*check)(const char *tablename, unsigned int hookmask, + bool (*check)(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *targetdata, unsigned int datalen); void (*destroy)(void *targetdata, unsigned int datalen); unsigned int targetsize; diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index ccecfbd2a25d..868df9c1e42b 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -37,15 +37,15 @@ static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device * } static struct ebt_match filter_802_3; -static int ebt_802_3_check(const char *tablename, unsigned int hookmask, +static bool ebt_802_3_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_802_3_info *info = data; if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK) - return -EINVAL; + return false; - return 0; + return true; } static struct ebt_match filter_802_3 __read_mostly = { diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index b0acb13a390c..95e2e70ac90a 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -177,9 +177,10 @@ static int ebt_filter_among(const struct sk_buff *skb, return EBT_MATCH; } -static int ebt_among_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, - unsigned int datalen) +static bool +ebt_among_check(const char *tablename, unsigned int hookmask, + const struct ebt_entry *e, void *data, + unsigned int datalen) { const struct ebt_among_info *info = data; int expected_length = sizeof(struct ebt_among_info); @@ -197,19 +198,19 @@ static int ebt_among_check(const char *tablename, unsigned int hookmask, "against expected %d, rounded to %Zd\n", datalen, expected_length, EBT_ALIGN(expected_length)); - return -EINVAL; + return false; } if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) { printk(KERN_WARNING "ebtables: among: dst integrity fail: %x\n", -err); - return -EINVAL; + return false; } if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) { printk(KERN_WARNING "ebtables: among: src integrity fail: %x\n", -err); - return -EINVAL; + return false; } - return 0; + return true; } static struct ebt_match filter_among __read_mostly = { diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 385f9cb85bce..cb33672380d0 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -100,7 +100,7 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in return EBT_MATCH; } -static int ebt_arp_check(const char *tablename, unsigned int hookmask, +static bool ebt_arp_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_arp_info *info = data; @@ -108,10 +108,10 @@ static int ebt_arp_check(const char *tablename, unsigned int hookmask, if ((e->ethproto != htons(ETH_P_ARP) && e->ethproto != htons(ETH_P_RARP)) || e->invflags & EBT_IPROTO) - return -EINVAL; + return false; if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_match filter_arp __read_mostly = { diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index a860ea6da46a..c298d3deffa4 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -58,20 +58,20 @@ static int ebt_target_reply(struct sk_buff *skb, unsigned int hooknr, return info->target; } -static int ebt_target_reply_check(const char *tablename, unsigned int hookmask, +static bool ebt_target_reply_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_arpreply_info *info = data; if (BASE_CHAIN && info->target == EBT_RETURN) - return -EINVAL; + return false; if (e->ethproto != htons(ETH_P_ARP) || e->invflags & EBT_IPROTO) - return -EINVAL; + return false; CLEAR_BASE_CHAIN_BIT; if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_target reply_target __read_mostly = { diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index c2be41e8bb99..6ddea2184e95 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -27,21 +27,21 @@ static int ebt_target_dnat(struct sk_buff *skb, unsigned int hooknr, return info->target; } -static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask, +static bool ebt_target_dnat_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_nat_info *info = data; if (BASE_CHAIN && info->target == EBT_RETURN) - return -EINVAL; + return false; CLEAR_BASE_CHAIN_BIT; if ( (strcmp(tablename, "nat") || (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) && (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) - return -EINVAL; + return false; if (INVALID_TARGET) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_target dnat __read_mostly = { diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index c1ae2547e3d0..cbf0918ec166 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -78,31 +78,31 @@ static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in, return EBT_MATCH; } -static int ebt_ip_check(const char *tablename, unsigned int hookmask, +static bool ebt_ip_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_ip_info *info = data; if (e->ethproto != htons(ETH_P_IP) || e->invflags & EBT_IPROTO) - return -EINVAL; + return false; if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK) - return -EINVAL; + return false; if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) { if (info->invflags & EBT_IP_PROTO) - return -EINVAL; + return false; if (info->protocol != IPPROTO_TCP && info->protocol != IPPROTO_UDP && info->protocol != IPPROTO_UDPLITE && info->protocol != IPPROTO_SCTP && info->protocol != IPPROTO_DCCP) - return -EINVAL; + return false; } if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1]) - return -EINVAL; + return false; if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1]) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_match filter_ip __read_mostly = { diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 554dd68637c8..1230c9ee394a 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -92,30 +92,30 @@ static int ebt_filter_ip6(const struct sk_buff *skb, return EBT_MATCH; } -static int ebt_ip6_check(const char *tablename, unsigned int hookmask, +static bool ebt_ip6_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) - return -EINVAL; + return false; if (info->bitmask & ~EBT_IP6_MASK || info->invflags & ~EBT_IP6_MASK) - return -EINVAL; + return false; if (info->bitmask & (EBT_IP6_DPORT | EBT_IP6_SPORT)) { if (info->invflags & EBT_IP6_PROTO) - return -EINVAL; + return false; if (info->protocol != IPPROTO_TCP && info->protocol != IPPROTO_UDP && info->protocol != IPPROTO_UDPLITE && info->protocol != IPPROTO_SCTP && info->protocol != IPPROTO_DCCP) - return -EINVAL; + return false; } if (info->bitmask & EBT_IP6_DPORT && info->dport[0] > info->dport[1]) - return -EINVAL; + return false; if (info->bitmask & EBT_IP6_SPORT && info->sport[0] > info->sport[1]) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_match filter_ip6 = diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 3d71f3510ffa..9b04f2be94e9 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -65,7 +65,7 @@ user2credits(u_int32_t user) return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE; } -static int ebt_limit_check(const char *tablename, unsigned int hookmask, +static bool ebt_limit_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { struct ebt_limit_info *info = data; @@ -75,7 +75,7 @@ static int ebt_limit_check(const char *tablename, unsigned int hookmask, user2credits(info->avg * info->burst) < user2credits(info->avg)) { printk("Overflow in ebt_limit, try lower: %u/%u\n", info->avg, info->burst); - return -EINVAL; + return false; } /* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */ @@ -83,7 +83,7 @@ static int ebt_limit_check(const char *tablename, unsigned int hookmask, info->credit = user2credits(info->avg * info->burst); info->credit_cap = user2credits(info->avg * info->burst); info->cost = user2credits(info->avg); - return 0; + return true; } static struct ebt_match ebt_limit_reg __read_mostly = { diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index d9596f114a37..f3d6d5ec2dc6 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -24,17 +24,17 @@ static DEFINE_SPINLOCK(ebt_log_lock); -static int ebt_log_check(const char *tablename, unsigned int hookmask, +static bool ebt_log_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { struct ebt_log_info *info = data; if (info->bitmask & ~EBT_LOG_MASK) - return -EINVAL; + return false; if (info->loglevel >= 8) - return -EINVAL; + return false; info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; - return 0; + return true; } struct tcpudphdr diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index bb02412786c8..b85c73895aeb 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -37,7 +37,7 @@ static int ebt_target_mark(struct sk_buff *skb, unsigned int hooknr, return info->target | ~EBT_VERDICT_BITS; } -static int ebt_target_mark_check(const char *tablename, unsigned int hookmask, +static bool ebt_target_mark_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_mark_t_info *info = data; @@ -45,15 +45,15 @@ static int ebt_target_mark_check(const char *tablename, unsigned int hookmask, tmp = info->target | ~EBT_VERDICT_BITS; if (BASE_CHAIN && tmp == EBT_RETURN) - return -EINVAL; + return false; CLEAR_BASE_CHAIN_BIT; if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0) - return -EINVAL; + return false; tmp = info->target & ~EBT_VERDICT_BITS; if (tmp != MARK_SET_VALUE && tmp != MARK_OR_VALUE && tmp != MARK_AND_VALUE && tmp != MARK_XOR_VALUE) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_target mark_target __read_mostly = { diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index b8ce9eb71709..b2707d772c90 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -23,18 +23,18 @@ static int ebt_filter_mark(const struct sk_buff *skb, return !(((skb->mark & info->mask) == info->mark) ^ info->invert); } -static int ebt_mark_check(const char *tablename, unsigned int hookmask, +static bool ebt_mark_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_mark_m_info *info = data; if (info->bitmask & ~EBT_MARK_MASK) - return -EINVAL; + return false; if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND)) - return -EINVAL; + return false; if (!info->bitmask) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_match filter_mark __read_mostly = { diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 88ceb5eb8496..a6954eb3f58a 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -36,17 +36,17 @@ static void ebt_nflog(const struct sk_buff *skb, nf_log_packet(PF_BRIDGE, hooknr, skb, in, out, &li, "%s", info->prefix); } -static int ebt_nflog_check(const char *tablename, - unsigned int hookmask, - const struct ebt_entry *e, - void *data, unsigned int datalen) +static bool ebt_nflog_check(const char *tablename, + unsigned int hookmask, + const struct ebt_entry *e, + void *data, unsigned int datalen) { struct ebt_nflog_info *info = (struct ebt_nflog_info *)data; if (info->flags & ~EBT_NFLOG_MASK) - return -EINVAL; + return false; info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0'; - return 0; + return true; } static struct ebt_watcher nflog __read_mostly = { diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 019026177f8b..4dcd3b86cff6 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -23,15 +23,15 @@ static int ebt_filter_pkttype(const struct sk_buff *skb, return (skb->pkt_type != info->pkt_type) ^ info->invert; } -static int ebt_pkttype_check(const char *tablename, unsigned int hookmask, +static bool ebt_pkttype_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_pkttype_info *info = data; if (info->invert != 0 && info->invert != 1) - return -EINVAL; + return false; /* Allow any pkt_type value */ - return 0; + return true; } static struct ebt_match filter_pkttype __read_mostly = { diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 040532683862..d2076f4227cd 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -33,20 +33,20 @@ static int ebt_target_redirect(struct sk_buff *skb, unsigned int hooknr, return info->target; } -static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask, +static bool ebt_target_redirect_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_redirect_info *info = data; if (BASE_CHAIN && info->target == EBT_RETURN) - return -EINVAL; + return false; CLEAR_BASE_CHAIN_BIT; if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) && (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) - return -EINVAL; + return false; if (INVALID_TARGET) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_target redirect_target __read_mostly = { diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index abfbc6c95024..5a5a16acca00 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -43,7 +43,7 @@ out: return info->target | ~EBT_VERDICT_BITS; } -static int ebt_target_snat_check(const char *tablename, unsigned int hookmask, +static bool ebt_target_snat_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_nat_info *info = data; @@ -51,19 +51,19 @@ static int ebt_target_snat_check(const char *tablename, unsigned int hookmask, tmp = info->target | ~EBT_VERDICT_BITS; if (BASE_CHAIN && tmp == EBT_RETURN) - return -EINVAL; + return false; CLEAR_BASE_CHAIN_BIT; if (strcmp(tablename, "nat")) - return -EINVAL; + return false; if (hookmask & ~(1 << NF_BR_POST_ROUTING)) - return -EINVAL; + return false; if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0) - return -EINVAL; + return false; tmp = info->target | EBT_VERDICT_BITS; if ((tmp & ~NAT_ARP_BIT) != ~NAT_ARP_BIT) - return -EINVAL; - return 0; + return false; + return true; } static struct ebt_target snat __read_mostly = { diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index c7a0a00dac7c..37d9480a00c6 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -153,7 +153,7 @@ static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in return EBT_MATCH; } -static int ebt_stp_check(const char *tablename, unsigned int hookmask, +static bool ebt_stp_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { const struct ebt_stp_info *info = data; @@ -162,13 +162,13 @@ static int ebt_stp_check(const char *tablename, unsigned int hookmask, if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK || !(info->bitmask & EBT_STP_MASK)) - return -EINVAL; + return false; /* Make sure the match only receives stp frames */ if (compare_ether_addr(e->destmac, bridge_ula) || compare_ether_addr(e->destmsk, msk) || !(e->bitmask & EBT_DESTMAC)) - return -EINVAL; + return false; - return 0; + return true; } static struct ebt_match filter_stp __read_mostly = { diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index bdd8a27bba9c..e13a005f58ad 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -255,14 +255,13 @@ static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr, ebt_ulog_packet(hooknr, skb, in, out, uloginfo, NULL); } - -static int ebt_ulog_check(const char *tablename, unsigned int hookmask, +static bool ebt_ulog_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { struct ebt_ulog_info *uloginfo = data; if (uloginfo->nlgroup > 31) - return -EINVAL; + return false; uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0'; @@ -288,12 +287,13 @@ static const struct nf_logger ebt_ulog_logger = { static int __init ebt_ulog_init(void) { - int i, ret = 0; + bool ret = true; + int i; if (nlbufsiz >= 128*1024) { printk(KERN_NOTICE "ebt_ulog: Netlink buffer has to be <= 128kB," " please try a smaller nlbufsiz parameter.\n"); - return -EINVAL; + return false; } /* initialize ulog_buffers */ @@ -305,12 +305,15 @@ static int __init ebt_ulog_init(void) ebtulognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, EBT_ULOG_MAXNLGROUPS, NULL, NULL, THIS_MODULE); - if (!ebtulognl) - ret = -ENOMEM; - else if ((ret = ebt_register_watcher(&ulog))) + if (!ebtulognl) { + printk(KERN_WARNING KBUILD_MODNAME ": out of memory trying to " + "call netlink_kernel_create\n"); + ret = false; + } else if (ebt_register_watcher(&ulog) != 0) { netlink_kernel_release(ebtulognl); + } - if (ret == 0) + if (ret) nf_log_register(NFPROTO_BRIDGE, &ebt_ulog_logger); return ret; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 4dba47aefc8a..fc88d5d59e04 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -87,7 +87,7 @@ ebt_filter_vlan(const struct sk_buff *skb, return EBT_MATCH; } -static int +static bool ebt_check_vlan(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen) @@ -99,7 +99,7 @@ ebt_check_vlan(const char *tablename, DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n", (unsigned short) ntohs(e->ethproto)); - return -EINVAL; + return false; } /* Check for bitmask range @@ -107,14 +107,14 @@ ebt_check_vlan(const char *tablename, if (info->bitmask & ~EBT_VLAN_MASK) { DEBUG_MSG("bitmask %2X is out of mask (%2X)\n", info->bitmask, EBT_VLAN_MASK); - return -EINVAL; + return false; } /* Check for inversion flags range */ if (info->invflags & ~EBT_VLAN_MASK) { DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n", info->invflags, EBT_VLAN_MASK); - return -EINVAL; + return false; } /* Reserved VLAN ID (VID) values @@ -129,7 +129,7 @@ ebt_check_vlan(const char *tablename, DEBUG_MSG ("id %d is out of range (1-4096)\n", info->id); - return -EINVAL; + return false; } /* Note: This is valid VLAN-tagged frame point. * Any value of user_priority are acceptable, @@ -144,7 +144,7 @@ ebt_check_vlan(const char *tablename, if ((unsigned char) info->prio > 7) { DEBUG_MSG("prio %d is out of range (0-7)\n", info->prio); - return -EINVAL; + return false; } } /* Check for encapsulated proto range - it is possible to be @@ -155,11 +155,11 @@ ebt_check_vlan(const char *tablename, DEBUG_MSG ("encap frame length %d is less than minimal\n", ntohs(info->encap)); - return -EINVAL; + return false; } } - return 0; + return true; } static struct ebt_match filter_vlan __read_mostly = { diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index b04e288d20f2..fe4995277296 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -365,7 +365,7 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, return -EINVAL; } if (match->check && - match->check(name, hookmask, e, m->data, m->match_size) != 0) { + !match->check(name, hookmask, e, m->data, m->match_size)) { BUGPRINT("match->check failed\n"); module_put(match->me); return -EINVAL; @@ -403,7 +403,7 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, return -EINVAL; } if (watcher->check && - watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) { + !watcher->check(name, hookmask, e, w->data, w->watcher_size)) { BUGPRINT("watcher->check failed\n"); module_put(watcher->me); return -EINVAL; @@ -716,7 +716,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, ret = -EINVAL; goto cleanup_watchers; } else if (t->u.target->check && - t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0) { + !t->u.target->check(name, hookmask, e, t->data, t->target_size)) { module_put(t->u.target->me); ret = -EFAULT; goto cleanup_watchers; -- cgit v1.2.3 From 8cc784eec6676b58e7f60419c88179aaa97bf71c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:13 +0200 Subject: netfilter: change return types of match functions for ebtables extensions Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 3 +-- net/bridge/netfilter/ebt_802_3.c | 13 ++++----- net/bridge/netfilter/ebt_among.c | 44 ++++++++++++++----------------- net/bridge/netfilter/ebt_arp.c | 35 ++++++++++++------------ net/bridge/netfilter/ebt_ip.c | 25 +++++++++--------- net/bridge/netfilter/ebt_ip6.c | 26 +++++++++--------- net/bridge/netfilter/ebt_limit.c | 6 ++--- net/bridge/netfilter/ebt_mark_m.c | 6 ++--- net/bridge/netfilter/ebt_pkttype.c | 4 +-- net/bridge/netfilter/ebt_stp.c | 39 ++++++++++++++------------- net/bridge/netfilter/ebt_vlan.c | 8 +++--- 11 files changed, 104 insertions(+), 105 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index 5f71719b7a27..f9fda2c442a0 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -207,8 +207,7 @@ struct ebt_match { struct list_head list; const char name[EBT_FUNCTION_MAXNAMELEN]; - /* 0 == it matches */ - int (*match)(const struct sk_buff *skb, const struct net_device *in, + bool (*match)(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchdata, unsigned int datalen); bool (*check)(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 868df9c1e42b..8ebe62b9bcc1 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -12,7 +12,8 @@ #include #include -static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in, +static bool ebt_filter_802_3(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { const struct ebt_802_3_info *info = data; @@ -21,19 +22,19 @@ static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device * if (info->bitmask & EBT_802_3_SAP) { if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) - return EBT_NOMATCH; + return false; if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_802_3_TYPE) { if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE)) - return EBT_NOMATCH; + return false; if (FWINV(info->type != type, EBT_802_3_TYPE)) - return EBT_NOMATCH; + return false; } - return EBT_MATCH; + return true; } static struct ebt_match filter_802_3; diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 95e2e70ac90a..bfdc67bcbfaf 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -14,8 +14,8 @@ #include #include -static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, - const char *mac, __be32 ip) +static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, + const char *mac, __be32 ip) { /* You may be puzzled as to how this code works. * Some tricks were used, refer to @@ -33,23 +33,19 @@ static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, if (ip) { for (i = start; i < limit; i++) { p = &wh->pool[i]; - if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) { - if (p->ip == 0 || p->ip == ip) { - return 1; - } - } + if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) + if (p->ip == 0 || p->ip == ip) + return true; } } else { for (i = start; i < limit; i++) { p = &wh->pool[i]; - if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) { - if (p->ip == 0) { - return 1; - } - } + if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) + if (p->ip == 0) + return true; } } - return 0; + return false; } static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash @@ -131,10 +127,10 @@ static int get_ip_src(const struct sk_buff *skb, __be32 *addr) return 0; } -static int ebt_filter_among(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, - unsigned int datalen) +static bool ebt_filter_among(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, const void *data, + unsigned int datalen) { const struct ebt_among_info *info = data; const char *dmac, *smac; @@ -147,34 +143,34 @@ static int ebt_filter_among(const struct sk_buff *skb, if (wh_src) { smac = eth_hdr(skb)->h_source; if (get_ip_src(skb, &sip)) - return EBT_NOMATCH; + return false; if (!(info->bitmask & EBT_AMONG_SRC_NEG)) { /* we match only if it contains */ if (!ebt_mac_wormhash_contains(wh_src, smac, sip)) - return EBT_NOMATCH; + return false; } else { /* we match only if it DOES NOT contain */ if (ebt_mac_wormhash_contains(wh_src, smac, sip)) - return EBT_NOMATCH; + return false; } } if (wh_dst) { dmac = eth_hdr(skb)->h_dest; if (get_ip_dst(skb, &dip)) - return EBT_NOMATCH; + return false; if (!(info->bitmask & EBT_AMONG_DST_NEG)) { /* we match only if it contains */ if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip)) - return EBT_NOMATCH; + return false; } else { /* we match only if it DOES NOT contain */ if (ebt_mac_wormhash_contains(wh_dst, dmac, dip)) - return EBT_NOMATCH; + return false; } } - return EBT_MATCH; + return true; } static bool diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index cb33672380d0..f1f0bcf5524a 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -15,7 +15,8 @@ #include #include -static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in, +static bool ebt_filter_arp(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { const struct ebt_arp_info *info = data; @@ -24,42 +25,42 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); if (ah == NULL) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode != ah->ar_op, EBT_ARP_OPCODE)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype != ah->ar_hrd, EBT_ARP_HTYPE)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype != ah->ar_pro, EBT_ARP_PTYPE)) - return EBT_NOMATCH; + return false; if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_GRAT)) { const __be32 *sap, *dap; __be32 saddr, daddr; if (ah->ar_pln != sizeof(__be32) || ah->ar_pro != htons(ETH_P_IP)) - return EBT_NOMATCH; + return false; sap = skb_header_pointer(skb, sizeof(struct arphdr) + ah->ar_hln, sizeof(saddr), &saddr); if (sap == NULL) - return EBT_NOMATCH; + return false; dap = skb_header_pointer(skb, sizeof(struct arphdr) + 2*ah->ar_hln+sizeof(saddr), sizeof(daddr), &daddr); if (dap == NULL) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_SRC_IP && FWINV(info->saddr != (*sap & info->smsk), EBT_ARP_SRC_IP)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_DST_IP && FWINV(info->daddr != (*dap & info->dmsk), EBT_ARP_DST_IP)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_GRAT && FWINV(*dap != *sap, EBT_ARP_GRAT)) - return EBT_NOMATCH; + return false; } if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) { @@ -68,18 +69,18 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in uint8_t verdict, i; if (ah->ar_hln != ETH_ALEN || ah->ar_hrd != htons(ARPHRD_ETHER)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_ARP_SRC_MAC) { mp = skb_header_pointer(skb, sizeof(struct arphdr), sizeof(_mac), &_mac); if (mp == NULL) - return EBT_NOMATCH; + return false; verdict = 0; for (i = 0; i < 6; i++) verdict |= (mp[i] ^ info->smaddr[i]) & info->smmsk[i]; if (FWINV(verdict != 0, EBT_ARP_SRC_MAC)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_ARP_DST_MAC) { @@ -87,17 +88,17 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in ah->ar_hln + ah->ar_pln, sizeof(_mac), &_mac); if (mp == NULL) - return EBT_NOMATCH; + return false; verdict = 0; for (i = 0; i < 6; i++) verdict |= (mp[i] ^ info->dmaddr[i]) & info->dmmsk[i]; if (FWINV(verdict != 0, EBT_ARP_DST_MAC)) - return EBT_NOMATCH; + return false; } } - return EBT_MATCH; + return true; } static bool ebt_arp_check(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index cbf0918ec166..018782f044c4 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -24,7 +24,8 @@ struct tcpudphdr { __be16 dst; }; -static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in, +static bool ebt_filter_ip(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { @@ -36,46 +37,46 @@ static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in, ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); if (ih == NULL) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP_TOS && FWINV(info->tos != ih->tos, EBT_IP_TOS)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP_SOURCE && FWINV((ih->saddr & info->smsk) != info->saddr, EBT_IP_SOURCE)) - return EBT_NOMATCH; + return false; if ((info->bitmask & EBT_IP_DEST) && FWINV((ih->daddr & info->dmsk) != info->daddr, EBT_IP_DEST)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP_PROTO) { if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO)) - return EBT_NOMATCH; + return false; if (!(info->bitmask & EBT_IP_DPORT) && !(info->bitmask & EBT_IP_SPORT)) - return EBT_MATCH; + return true; if (ntohs(ih->frag_off) & IP_OFFSET) - return EBT_NOMATCH; + return false; pptr = skb_header_pointer(skb, ih->ihl*4, sizeof(_ports), &_ports); if (pptr == NULL) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP_DPORT) { u32 dst = ntohs(pptr->dst); if (FWINV(dst < info->dport[0] || dst > info->dport[1], EBT_IP_DPORT)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_IP_SPORT) { u32 src = ntohs(pptr->src); if (FWINV(src < info->sport[0] || src > info->sport[1], EBT_IP_SPORT)) - return EBT_NOMATCH; + return false; } } - return EBT_MATCH; + return true; } static bool ebt_ip_check(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 1230c9ee394a..7fc3928e3fb5 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -27,7 +27,7 @@ struct tcpudphdr { __be16 dst; }; -static int ebt_filter_ip6(const struct sk_buff *skb, +static bool ebt_filter_ip6(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) @@ -42,54 +42,54 @@ static int ebt_filter_ip6(const struct sk_buff *skb, ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h); if (ih6 == NULL) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP6_TCLASS && FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS)) - return EBT_NOMATCH; + return false; for (i = 0; i < 4; i++) tmp_addr.in6_u.u6_addr32[i] = ih6->saddr.in6_u.u6_addr32[i] & info->smsk.in6_u.u6_addr32[i]; if (info->bitmask & EBT_IP6_SOURCE && FWINV((ipv6_addr_cmp(&tmp_addr, &info->saddr) != 0), EBT_IP6_SOURCE)) - return EBT_NOMATCH; + return false; for (i = 0; i < 4; i++) tmp_addr.in6_u.u6_addr32[i] = ih6->daddr.in6_u.u6_addr32[i] & info->dmsk.in6_u.u6_addr32[i]; if (info->bitmask & EBT_IP6_DEST && FWINV((ipv6_addr_cmp(&tmp_addr, &info->daddr) != 0), EBT_IP6_DEST)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP6_PROTO) { uint8_t nexthdr = ih6->nexthdr; int offset_ph; offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr); if (offset_ph == -1) - return EBT_NOMATCH; + return false; if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO)) - return EBT_NOMATCH; + return false; if (!(info->bitmask & EBT_IP6_DPORT) && !(info->bitmask & EBT_IP6_SPORT)) - return EBT_MATCH; + return true; pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports), &_ports); if (pptr == NULL) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_IP6_DPORT) { u32 dst = ntohs(pptr->dst); if (FWINV(dst < info->dport[0] || dst > info->dport[1], EBT_IP6_DPORT)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_IP6_SPORT) { u32 src = ntohs(pptr->src); if (FWINV(src < info->sport[0] || src > info->sport[1], EBT_IP6_SPORT)) - return EBT_NOMATCH; + return false; } - return EBT_MATCH; + return true; } - return EBT_MATCH; + return true; } static bool ebt_ip6_check(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 9b04f2be94e9..925065a22a65 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -30,7 +30,7 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) -static int ebt_limit_match(const struct sk_buff *skb, +static bool ebt_limit_match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { @@ -46,11 +46,11 @@ static int ebt_limit_match(const struct sk_buff *skb, /* We're not limited. */ info->credit -= info->cost; spin_unlock_bh(&limit_lock); - return EBT_MATCH; + return true; } spin_unlock_bh(&limit_lock); - return EBT_NOMATCH; + return false; } /* Precision saver. */ diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index b2707d772c90..ec16c0e2868a 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -12,15 +12,15 @@ #include #include -static int ebt_filter_mark(const struct sk_buff *skb, +static bool ebt_filter_mark(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { const struct ebt_mark_m_info *info = data; if (info->bitmask & EBT_MARK_OR) - return !(!!(skb->mark & info->mask) ^ info->invert); - return !(((skb->mark & info->mask) == info->mark) ^ info->invert); + return !!(skb->mark & info->mask) ^ info->invert; + return ((skb->mark & info->mask) == info->mark) ^ info->invert; } static bool ebt_mark_check(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 4dcd3b86cff6..74b443284366 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -12,7 +12,7 @@ #include #include -static int ebt_filter_pkttype(const struct sk_buff *skb, +static bool ebt_filter_pkttype(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *data, @@ -20,7 +20,7 @@ static int ebt_filter_pkttype(const struct sk_buff *skb, { const struct ebt_pkttype_info *info = data; - return (skb->pkt_type != info->pkt_type) ^ info->invert; + return (skb->pkt_type == info->pkt_type) ^ info->invert; } static bool ebt_pkttype_check(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 37d9480a00c6..7618206639ed 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -40,7 +40,7 @@ struct stp_config_pdu { #define NR16(p) (p[0] << 8 | p[1]) #define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) -static int ebt_filter_config(const struct ebt_stp_info *info, +static bool ebt_filter_config(const struct ebt_stp_info *info, const struct stp_config_pdu *stpc) { const struct ebt_stp_config_info *c; @@ -51,12 +51,12 @@ static int ebt_filter_config(const struct ebt_stp_info *info, c = &info->config; if ((info->bitmask & EBT_STP_FLAGS) && FWINV(c->flags != stpc->flags, EBT_STP_FLAGS)) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_STP_ROOTPRIO) { v16 = NR16(stpc->root); if (FWINV(v16 < c->root_priol || v16 > c->root_priou, EBT_STP_ROOTPRIO)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_ROOTADDR) { verdict = 0; @@ -64,19 +64,19 @@ static int ebt_filter_config(const struct ebt_stp_info *info, verdict |= (stpc->root[2+i] ^ c->root_addr[i]) & c->root_addrmsk[i]; if (FWINV(verdict != 0, EBT_STP_ROOTADDR)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_ROOTCOST) { v32 = NR32(stpc->root_cost); if (FWINV(v32 < c->root_costl || v32 > c->root_costu, EBT_STP_ROOTCOST)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_SENDERPRIO) { v16 = NR16(stpc->sender); if (FWINV(v16 < c->sender_priol || v16 > c->sender_priou, EBT_STP_SENDERPRIO)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_SENDERADDR) { verdict = 0; @@ -84,42 +84,43 @@ static int ebt_filter_config(const struct ebt_stp_info *info, verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) & c->sender_addrmsk[i]; if (FWINV(verdict != 0, EBT_STP_SENDERADDR)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_PORT) { v16 = NR16(stpc->port); if (FWINV(v16 < c->portl || v16 > c->portu, EBT_STP_PORT)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_MSGAGE) { v16 = NR16(stpc->msg_age); if (FWINV(v16 < c->msg_agel || v16 > c->msg_ageu, EBT_STP_MSGAGE)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_MAXAGE) { v16 = NR16(stpc->max_age); if (FWINV(v16 < c->max_agel || v16 > c->max_ageu, EBT_STP_MAXAGE)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_HELLOTIME) { v16 = NR16(stpc->hello_time); if (FWINV(v16 < c->hello_timel || v16 > c->hello_timeu, EBT_STP_HELLOTIME)) - return EBT_NOMATCH; + return false; } if (info->bitmask & EBT_STP_FWDD) { v16 = NR16(stpc->forward_delay); if (FWINV(v16 < c->forward_delayl || v16 > c->forward_delayu, EBT_STP_FWDD)) - return EBT_NOMATCH; + return false; } - return EBT_MATCH; + return true; } -static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in, +static bool ebt_filter_stp(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { const struct ebt_stp_info *info = data; @@ -129,15 +130,15 @@ static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in sp = skb_header_pointer(skb, 0, sizeof(_stph), &_stph); if (sp == NULL) - return EBT_NOMATCH; + return false; /* The stp code only considers these */ if (memcmp(sp, header, sizeof(header))) - return EBT_NOMATCH; + return false; if (info->bitmask & EBT_STP_TYPE && FWINV(info->type != sp->type, EBT_STP_TYPE)) - return EBT_NOMATCH; + return false; if (sp->type == BPDU_TYPE_CONFIG && info->bitmask & EBT_STP_CONFIG_MASK) { @@ -147,10 +148,10 @@ static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in st = skb_header_pointer(skb, sizeof(_stph), sizeof(_stpc), &_stpc); if (st == NULL) - return EBT_NOMATCH; + return false; return ebt_filter_config(info, st); } - return EBT_MATCH; + return true; } static bool ebt_stp_check(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index fc88d5d59e04..8cc4257a1ade 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -38,9 +38,9 @@ MODULE_LICENSE("GPL"); #define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args) #define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_ -#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return EBT_NOMATCH;} +#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return false; } -static int +static bool ebt_filter_vlan(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -58,7 +58,7 @@ ebt_filter_vlan(const struct sk_buff *skb, fp = skb_header_pointer(skb, 0, sizeof(_frame), &_frame); if (fp == NULL) - return EBT_NOMATCH; + return false; /* Tag Control Information (TCI) consists of the following elements: * - User_priority. The user_priority field is three bits in length, @@ -84,7 +84,7 @@ ebt_filter_vlan(const struct sk_buff *skb, if (GET_BITMASK(EBT_VLAN_ENCAP)) EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP); - return EBT_MATCH; + return true; } static bool -- cgit v1.2.3 From 0ac6ab1f7915fc820ca0cf8f597290dbb249edcc Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:13 +0200 Subject: netfilter: Change return types of targets/watchers for Ebtables extensions Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 6 +++--- net/bridge/netfilter/ebt_arpreply.c | 2 +- net/bridge/netfilter/ebt_dnat.c | 2 +- net/bridge/netfilter/ebt_log.c | 3 ++- net/bridge/netfilter/ebt_mark.c | 2 +- net/bridge/netfilter/ebt_nflog.c | 11 ++++++----- net/bridge/netfilter/ebt_redirect.c | 3 ++- net/bridge/netfilter/ebt_snat.c | 2 +- net/bridge/netfilter/ebt_ulog.c | 3 ++- 9 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index f9fda2c442a0..097432b94c55 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -221,7 +221,7 @@ struct ebt_watcher { struct list_head list; const char name[EBT_FUNCTION_MAXNAMELEN]; - void (*watcher)(const struct sk_buff *skb, unsigned int hooknr, + unsigned int (*watcher)(const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *watcherdata, unsigned int datalen); bool (*check)(const char *tablename, unsigned int hookmask, @@ -235,8 +235,8 @@ struct ebt_target { struct list_head list; const char name[EBT_FUNCTION_MAXNAMELEN]; - /* returns one of the standard verdicts */ - int (*target)(struct sk_buff *skb, unsigned int hooknr, + /* returns one of the standard EBT_* verdicts */ + unsigned int (*target)(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *targetdata, unsigned int datalen); bool (*check)(const char *tablename, unsigned int hookmask, diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index c298d3deffa4..b444cf835f1e 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -15,7 +15,7 @@ #include #include -static int ebt_target_reply(struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_target_reply(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index 6ddea2184e95..d58b9e32338e 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -14,7 +14,7 @@ #include #include -static int ebt_target_dnat(struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_target_dnat(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index f3d6d5ec2dc6..2705d7a2a9b5 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -192,7 +192,7 @@ out: } -static void ebt_log(const struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_log(const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { @@ -209,6 +209,7 @@ static void ebt_log(const struct sk_buff *skb, unsigned int hooknr, else ebt_log_packet(NFPROTO_BRIDGE, hooknr, skb, in, out, &li, info->prefix); + return EBT_CONTINUE; } static struct ebt_watcher log = diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index b85c73895aeb..e4b91d8e2c6c 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -18,7 +18,7 @@ #include #include -static int ebt_target_mark(struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_target_mark(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index a6954eb3f58a..2c75023b3260 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -19,11 +19,11 @@ #include #include -static void ebt_nflog(const struct sk_buff *skb, - unsigned int hooknr, - const struct net_device *in, - const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int ebt_nflog(const struct sk_buff *skb, + unsigned int hooknr, + const struct net_device *in, + const struct net_device *out, + const void *data, unsigned int datalen) { struct ebt_nflog_info *info = (struct ebt_nflog_info *)data; struct nf_loginfo li; @@ -34,6 +34,7 @@ static void ebt_nflog(const struct sk_buff *skb, li.u.ulog.qthreshold = info->threshold; nf_log_packet(PF_BRIDGE, hooknr, skb, in, out, &li, "%s", info->prefix); + return EBT_CONTINUE; } static bool ebt_nflog_check(const char *tablename, diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index d2076f4227cd..7bf1390ad97b 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -15,7 +15,8 @@ #include #include -static int ebt_target_redirect(struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_target_redirect(struct sk_buff *skb, + unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 5a5a16acca00..d13f05d2620e 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -16,7 +16,7 @@ #include #include -static int ebt_target_snat(struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_target_snat(struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index e13a005f58ad..5f86f555f6d1 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -246,13 +246,14 @@ static void ebt_log_packet(u_int8_t pf, unsigned int hooknum, ebt_ulog_packet(hooknum, skb, in, out, &loginfo, prefix); } -static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr, +static unsigned int ebt_ulog(const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { const struct ebt_ulog_info *uloginfo = data; ebt_ulog_packet(hooknr, skb, in, out, uloginfo, NULL); + return EBT_CONTINUE; } static bool ebt_ulog_check(const char *tablename, unsigned int hookmask, -- cgit v1.2.3 From 001a18d369f4813ed792629ff4a9a6ade2a4a031 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:14 +0200 Subject: netfilter: add dummy members to Ebtables code to ease transition to Xtables Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 6 ++++ net/bridge/netfilter/ebt_802_3.c | 2 ++ net/bridge/netfilter/ebt_among.c | 2 ++ net/bridge/netfilter/ebt_arp.c | 2 ++ net/bridge/netfilter/ebt_arpreply.c | 2 ++ net/bridge/netfilter/ebt_dnat.c | 2 ++ net/bridge/netfilter/ebt_ip.c | 2 ++ net/bridge/netfilter/ebt_ip6.c | 2 ++ net/bridge/netfilter/ebt_limit.c | 2 ++ net/bridge/netfilter/ebt_log.c | 2 ++ net/bridge/netfilter/ebt_mark.c | 2 ++ net/bridge/netfilter/ebt_mark_m.c | 2 ++ net/bridge/netfilter/ebt_nflog.c | 2 ++ net/bridge/netfilter/ebt_pkttype.c | 2 ++ net/bridge/netfilter/ebt_redirect.c | 2 ++ net/bridge/netfilter/ebt_snat.c | 2 ++ net/bridge/netfilter/ebt_stp.c | 2 ++ net/bridge/netfilter/ebt_ulog.c | 2 ++ net/bridge/netfilter/ebt_vlan.c | 2 ++ net/bridge/netfilter/ebtables.c | 58 ++++++++++++++++++++++++++----- 20 files changed, 91 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index 097432b94c55..82f854bf37e7 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -214,6 +214,8 @@ struct ebt_match const struct ebt_entry *e, void *matchdata, unsigned int datalen); void (*destroy)(void *matchdata, unsigned int datalen); unsigned int matchsize; + u_int8_t revision; + u_int8_t family; struct module *me; }; @@ -228,6 +230,8 @@ struct ebt_watcher const struct ebt_entry *e, void *watcherdata, unsigned int datalen); void (*destroy)(void *watcherdata, unsigned int datalen); unsigned int targetsize; + u_int8_t revision; + u_int8_t family; struct module *me; }; @@ -243,6 +247,8 @@ struct ebt_target const struct ebt_entry *e, void *targetdata, unsigned int datalen); void (*destroy)(void *targetdata, unsigned int datalen); unsigned int targetsize; + u_int8_t revision; + u_int8_t family; struct module *me; }; diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 8ebe62b9bcc1..f9876f227574 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -51,6 +51,8 @@ static bool ebt_802_3_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_802_3 __read_mostly = { .name = EBT_802_3_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_802_3, .check = ebt_802_3_check, .matchsize = XT_ALIGN(sizeof(struct ebt_802_3_info)), diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index bfdc67bcbfaf..568c890887b5 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -211,6 +211,8 @@ ebt_among_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_among __read_mostly = { .name = EBT_AMONG_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_among, .check = ebt_among_check, .matchsize = -1, /* special case */ diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index f1f0bcf5524a..4a5226cbab89 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -117,6 +117,8 @@ static bool ebt_arp_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_arp __read_mostly = { .name = EBT_ARP_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_arp, .check = ebt_arp_check, .matchsize = XT_ALIGN(sizeof(struct ebt_arp_info)), diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index b444cf835f1e..7ab16556800e 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -76,6 +76,8 @@ static bool ebt_target_reply_check(const char *tablename, unsigned int hookmask, static struct ebt_target reply_target __read_mostly = { .name = EBT_ARPREPLY_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, .target = ebt_target_reply, .check = ebt_target_reply_check, .targetsize = XT_ALIGN(sizeof(struct ebt_arpreply_info)), diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index d58b9e32338e..64838e2835a0 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -46,6 +46,8 @@ static bool ebt_target_dnat_check(const char *tablename, unsigned int hookmask, static struct ebt_target dnat __read_mostly = { .name = EBT_DNAT_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, .target = ebt_target_dnat, .check = ebt_target_dnat_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nat_info)), diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index 018782f044c4..0bef6f7bc83f 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -108,6 +108,8 @@ static bool ebt_ip_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_ip __read_mostly = { .name = EBT_IP_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_ip, .check = ebt_ip_check, .matchsize = XT_ALIGN(sizeof(struct ebt_ip_info)), diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 7fc3928e3fb5..afcabe205b8f 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -121,6 +121,8 @@ static bool ebt_ip6_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_ip6 = { .name = EBT_IP6_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_ip6, .check = ebt_ip6_check, .matchsize = XT_ALIGN(sizeof(struct ebt_ip6_info)), diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 925065a22a65..9ca0a2564c8d 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -88,6 +88,8 @@ static bool ebt_limit_check(const char *tablename, unsigned int hookmask, static struct ebt_match ebt_limit_reg __read_mostly = { .name = EBT_LIMIT_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_limit_match, .check = ebt_limit_check, .matchsize = XT_ALIGN(sizeof(struct ebt_limit_info)), diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 2705d7a2a9b5..c2e1c357025e 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -215,6 +215,8 @@ static unsigned int ebt_log(const struct sk_buff *skb, unsigned int hooknr, static struct ebt_watcher log = { .name = EBT_LOG_WATCHER, + .revision = 0, + .family = NFPROTO_BRIDGE, .watcher = ebt_log, .check = ebt_log_check, .targetsize = XT_ALIGN(sizeof(struct ebt_log_info)), diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index e4b91d8e2c6c..910721a12673 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -58,6 +58,8 @@ static bool ebt_target_mark_check(const char *tablename, unsigned int hookmask, static struct ebt_target mark_target __read_mostly = { .name = EBT_MARK_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, .target = ebt_target_mark, .check = ebt_target_mark_check, .targetsize = XT_ALIGN(sizeof(struct ebt_mark_t_info)), diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index ec16c0e2868a..6512ad9b4097 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -39,6 +39,8 @@ static bool ebt_mark_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_mark __read_mostly = { .name = EBT_MARK_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_mark, .check = ebt_mark_check, .matchsize = XT_ALIGN(sizeof(struct ebt_mark_m_info)), diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 2c75023b3260..aa0410c69a60 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -52,6 +52,8 @@ static bool ebt_nflog_check(const char *tablename, static struct ebt_watcher nflog __read_mostly = { .name = EBT_NFLOG_WATCHER, + .revision = 0, + .family = NFPROTO_BRIDGE, .watcher = ebt_nflog, .check = ebt_nflog_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nflog_info)), diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 74b443284366..a9acecc88e9e 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -36,6 +36,8 @@ static bool ebt_pkttype_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_pkttype __read_mostly = { .name = EBT_PKTTYPE_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_pkttype, .check = ebt_pkttype_check, .matchsize = XT_ALIGN(sizeof(struct ebt_pkttype_info)), diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 7bf1390ad97b..4c628108bcda 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -52,6 +52,8 @@ static bool ebt_target_redirect_check(const char *tablename, unsigned int hookma static struct ebt_target redirect_target __read_mostly = { .name = EBT_REDIRECT_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, .target = ebt_target_redirect, .check = ebt_target_redirect_check, .targetsize = XT_ALIGN(sizeof(struct ebt_redirect_info)), diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index d13f05d2620e..0e83de781c0c 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -68,6 +68,8 @@ static bool ebt_target_snat_check(const char *tablename, unsigned int hookmask, static struct ebt_target snat __read_mostly = { .name = EBT_SNAT_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, .target = ebt_target_snat, .check = ebt_target_snat_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nat_info)), diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 7618206639ed..e6d8f0c140a4 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -174,6 +174,8 @@ static bool ebt_stp_check(const char *tablename, unsigned int hookmask, static struct ebt_match filter_stp __read_mostly = { .name = EBT_STP_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_stp, .check = ebt_stp_check, .matchsize = XT_ALIGN(sizeof(struct ebt_stp_info)), diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 5f86f555f6d1..076b44590f16 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -274,6 +274,8 @@ static bool ebt_ulog_check(const char *tablename, unsigned int hookmask, static struct ebt_watcher ulog __read_mostly = { .name = EBT_ULOG_WATCHER, + .revision = 0, + .family = NFPROTO_BRIDGE, .watcher = ebt_ulog, .check = ebt_ulog_check, .targetsize = XT_ALIGN(sizeof(struct ebt_ulog_info)), diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 8cc4257a1ade..9e3a39ae4660 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -164,6 +164,8 @@ ebt_check_vlan(const char *tablename, static struct ebt_match filter_vlan __read_mostly = { .name = EBT_VLAN_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, .match = ebt_filter_vlan, .check = ebt_check_vlan, .matchsize = XT_ALIGN(sizeof(struct ebt_vlan_info)), diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index fe4995277296..bc4b3f4f37c4 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -61,7 +61,9 @@ static LIST_HEAD(ebt_matches); static LIST_HEAD(ebt_watchers); static struct ebt_target ebt_standard_target = { - .name = "standard", + .name = "standard", + .revision = 0, + .family = NFPROTO_BRIDGE, }; static inline int ebt_do_watcher (struct ebt_entry_watcher *w, @@ -352,6 +354,17 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, return -ENOENT; } mutex_unlock(&ebt_mutex); + if (match->family != NFPROTO_BRIDGE) { + printk(KERN_WARNING "ebtables: %s match: not for ebtables?\n", + match->name); + goto out; + } + if (match->revision != 0) { + printk(KERN_WARNING "ebtables: %s match: ebtables is not " + "supporting revisions at this time\n", + match->name); + goto out; + } if (XT_ALIGN(match->matchsize) != m->match_size && match->matchsize != -1) { /* @@ -361,17 +374,18 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, printk(KERN_WARNING "ebtables: %s match: " "invalid size %Zu != %u\n", match->name, XT_ALIGN(match->matchsize), m->match_size); - module_put(match->me); - return -EINVAL; + goto out; } if (match->check && !match->check(name, hookmask, e, m->data, m->match_size)) { BUGPRINT("match->check failed\n"); - module_put(match->me); - return -EINVAL; + goto out; } (*cnt)++; return 0; + out: + module_put(match->me); + return -EINVAL; } static inline int @@ -394,22 +408,34 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, return -ENOENT; } mutex_unlock(&ebt_mutex); + if (watcher->family != NFPROTO_BRIDGE) { + printk(KERN_WARNING "ebtables: %s watcher: not for ebtables?\n", + watcher->name); + goto out; + } + if (watcher->revision != 0) { + printk(KERN_WARNING "ebtables: %s watcher: ebtables is not " + "supporting revisions at this time\n", + watcher->name); + goto out; + } if (XT_ALIGN(watcher->targetsize) != w->watcher_size) { printk(KERN_WARNING "ebtables: %s watcher: " "invalid size %Zu != %u\n", watcher->name, XT_ALIGN(watcher->targetsize), w->watcher_size); - module_put(watcher->me); - return -EINVAL; + goto out; } if (watcher->check && !watcher->check(name, hookmask, e, w->data, w->watcher_size)) { BUGPRINT("watcher->check failed\n"); - module_put(watcher->me); - return -EINVAL; + goto out; } (*cnt)++; return 0; + out: + module_put(watcher->me); + return -EINVAL; } static int ebt_verify_pointers(struct ebt_replace *repl, @@ -690,6 +716,20 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, } mutex_unlock(&ebt_mutex); + if (target->family != NFPROTO_BRIDGE) { + printk(KERN_WARNING "ebtables: %s target: not for ebtables?\n", + target->name); + ret = -EINVAL; + goto cleanup_watchers; + } + if (target->revision != 0) { + printk(KERN_WARNING "ebtables: %s target: ebtables is not " + "supporting revisions at this time\n", + target->name); + ret = -EINVAL; + goto cleanup_watchers; + } + t->u.target = target; if (t->u.target == &ebt_standard_target) { if (gap < sizeof(struct ebt_standard_target)) { -- cgit v1.2.3 From 2d06d4a5cc107046508d860a0b47dbc43b829b79 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:15 +0200 Subject: netfilter: change Ebtables function signatures to match Xtables's Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 43 +++++++++++++++++++------------ net/bridge/netfilter/ebt_802_3.c | 18 +++++++------ net/bridge/netfilter/ebt_among.c | 18 ++++++------- net/bridge/netfilter/ebt_arp.c | 18 ++++++++----- net/bridge/netfilter/ebt_arpreply.c | 18 ++++++++----- net/bridge/netfilter/ebt_dnat.c | 17 +++++++----- net/bridge/netfilter/ebt_ip.c | 19 ++++++++------ net/bridge/netfilter/ebt_ip6.c | 19 ++++++++------ net/bridge/netfilter/ebt_limit.c | 17 +++++++----- net/bridge/netfilter/ebt_log.c | 17 +++++++----- net/bridge/netfilter/ebt_mark.c | 17 +++++++----- net/bridge/netfilter/ebt_mark_m.c | 17 +++++++----- net/bridge/netfilter/ebt_nflog.c | 21 +++++++-------- net/bridge/netfilter/ebt_pkttype.c | 20 +++++++------- net/bridge/netfilter/ebt_redirect.c | 18 +++++++------ net/bridge/netfilter/ebt_snat.c | 17 +++++++----- net/bridge/netfilter/ebt_stp.c | 18 ++++++++----- net/bridge/netfilter/ebt_ulog.c | 17 +++++++----- net/bridge/netfilter/ebt_vlan.c | 18 ++++++------- net/bridge/netfilter/ebtables.c | 30 ++++++++++----------- 20 files changed, 224 insertions(+), 173 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index 82f854bf37e7..f20a57da7a25 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -31,6 +31,9 @@ * The 4 lsb are more than enough to store the verdict. */ #define EBT_VERDICT_BITS 0x0000000F +struct xt_match; +struct xt_target; + struct ebt_counter { uint64_t pcnt; @@ -208,11 +211,13 @@ struct ebt_match struct list_head list; const char name[EBT_FUNCTION_MAXNAMELEN]; bool (*match)(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const void *matchdata, - unsigned int datalen); - bool (*check)(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *matchdata, unsigned int datalen); - void (*destroy)(void *matchdata, unsigned int datalen); + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, unsigned int protoff, + bool *hotdrop); + bool (*checkentry)(const char *table, const void *entry, + const struct xt_match *match, void *matchinfo, + unsigned int hook_mask); + void (*destroy)(const struct xt_match *match, void *matchinfo); unsigned int matchsize; u_int8_t revision; u_int8_t family; @@ -223,12 +228,14 @@ struct ebt_watcher { struct list_head list; const char name[EBT_FUNCTION_MAXNAMELEN]; - unsigned int (*watcher)(const struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *watcherdata, unsigned int datalen); - bool (*check)(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *watcherdata, unsigned int datalen); - void (*destroy)(void *watcherdata, unsigned int datalen); + unsigned int (*target)(struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + unsigned int hook_num, const struct xt_target *target, + const void *targinfo); + bool (*checkentry)(const char *table, const void *entry, + const struct xt_target *target, void *targinfo, + unsigned int hook_mask); + void (*destroy)(const struct xt_target *target, void *targinfo); unsigned int targetsize; u_int8_t revision; u_int8_t family; @@ -240,12 +247,14 @@ struct ebt_target struct list_head list; const char name[EBT_FUNCTION_MAXNAMELEN]; /* returns one of the standard EBT_* verdicts */ - unsigned int (*target)(struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *targetdata, unsigned int datalen); - bool (*check)(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *targetdata, unsigned int datalen); - void (*destroy)(void *targetdata, unsigned int datalen); + unsigned int (*target)(struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + unsigned int hook_num, const struct xt_target *target, + const void *targinfo); + bool (*checkentry)(const char *table, const void *entry, + const struct xt_target *target, void *targinfo, + unsigned int hook_mask); + void (*destroy)(const struct xt_target *target, void *targinfo); unsigned int targetsize; u_int8_t revision; u_int8_t family; diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index f9876f227574..6f1a69c28ed9 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -12,9 +12,10 @@ #include #include -static bool ebt_filter_802_3(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, unsigned int datalen) +static bool +ebt_802_3_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_802_3_info *info = data; const struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb); @@ -37,9 +38,10 @@ static bool ebt_filter_802_3(const struct sk_buff *skb, return true; } -static struct ebt_match filter_802_3; -static bool ebt_802_3_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_802_3_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_802_3_info *info = data; @@ -53,8 +55,8 @@ static struct ebt_match filter_802_3 __read_mostly = { .name = EBT_802_3_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_802_3, - .check = ebt_802_3_check, + .match = ebt_802_3_mt, + .checkentry = ebt_802_3_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_802_3_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 88b5c9118a75..84a306f085b5 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -127,10 +127,10 @@ static int get_ip_src(const struct sk_buff *skb, __be32 *addr) return 0; } -static bool ebt_filter_among(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, - unsigned int datalen) +static bool +ebt_among_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_among_info *info = data; const char *dmac, *smac; @@ -174,9 +174,9 @@ static bool ebt_filter_among(const struct sk_buff *skb, } static bool -ebt_among_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, - unsigned int datalen) +ebt_among_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_entry_match *em = container_of(data, const struct ebt_entry_match, data); @@ -215,8 +215,8 @@ static struct ebt_match filter_among __read_mostly = { .name = EBT_AMONG_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_among, - .check = ebt_among_check, + .match = ebt_among_mt, + .checkentry = ebt_among_mt_check, .matchsize = -1, /* special case */ .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 4a5226cbab89..6e7cd2f5ad74 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -15,9 +15,10 @@ #include #include -static bool ebt_filter_arp(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, unsigned int datalen) +static bool +ebt_arp_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_arp_info *info = data; const struct arphdr *ah; @@ -101,10 +102,13 @@ static bool ebt_filter_arp(const struct sk_buff *skb, return true; } -static bool ebt_arp_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_arp_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_arp_info *info = data; + const struct ebt_entry *e = entry; if ((e->ethproto != htons(ETH_P_ARP) && e->ethproto != htons(ETH_P_RARP)) || @@ -119,8 +123,8 @@ static struct ebt_match filter_arp __read_mostly = { .name = EBT_ARP_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_arp, - .check = ebt_arp_check, + .match = ebt_arp_mt, + .checkentry = ebt_arp_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_arp_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index 7ab16556800e..6f2f65897770 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -15,9 +15,10 @@ #include #include -static unsigned int ebt_target_reply(struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_arpreply_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hook_nr, + const struct xt_target *target, const void *data) { struct ebt_arpreply_info *info = (void *)data; const __be32 *siptr, *diptr; @@ -58,10 +59,13 @@ static unsigned int ebt_target_reply(struct sk_buff *skb, unsigned int hooknr, return info->target; } -static bool ebt_target_reply_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_arpreply_tg_check(const char *tablename, const void *entry, + const struct xt_target *target, void *data, + unsigned int hookmask) { const struct ebt_arpreply_info *info = data; + const struct ebt_entry *e = entry; if (BASE_CHAIN && info->target == EBT_RETURN) return false; @@ -78,8 +82,8 @@ static struct ebt_target reply_target __read_mostly = { .name = EBT_ARPREPLY_TARGET, .revision = 0, .family = NFPROTO_BRIDGE, - .target = ebt_target_reply, - .check = ebt_target_reply_check, + .target = ebt_arpreply_tg, + .checkentry = ebt_arpreply_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_arpreply_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index 64838e2835a0..b7cc013bd377 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -14,9 +14,10 @@ #include #include -static unsigned int ebt_target_dnat(struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_dnat_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hook_nr, + const struct xt_target *target, const void *data) { const struct ebt_nat_info *info = data; @@ -27,8 +28,10 @@ static unsigned int ebt_target_dnat(struct sk_buff *skb, unsigned int hooknr, return info->target; } -static bool ebt_target_dnat_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_dnat_tg_check(const char *tablename, const void *entry, + const struct xt_target *target, void *data, + unsigned int hookmask) { const struct ebt_nat_info *info = data; @@ -48,8 +51,8 @@ static struct ebt_target dnat __read_mostly = { .name = EBT_DNAT_TARGET, .revision = 0, .family = NFPROTO_BRIDGE, - .target = ebt_target_dnat, - .check = ebt_target_dnat_check, + .target = ebt_dnat_tg, + .checkentry = ebt_dnat_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nat_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index 0bef6f7bc83f..e7f3b1776b02 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -24,10 +24,10 @@ struct tcpudphdr { __be16 dst; }; -static bool ebt_filter_ip(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, - unsigned int datalen) +static bool +ebt_ip_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_ip_info *info = data; const struct iphdr *ih; @@ -79,10 +79,13 @@ static bool ebt_filter_ip(const struct sk_buff *skb, return true; } -static bool ebt_ip_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_ip_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_ip_info *info = data; + const struct ebt_entry *e = entry; if (e->ethproto != htons(ETH_P_IP) || e->invflags & EBT_IPROTO) @@ -110,8 +113,8 @@ static struct ebt_match filter_ip __read_mostly = { .name = EBT_IP_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_ip, - .check = ebt_ip_check, + .match = ebt_ip_mt, + .checkentry = ebt_ip_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_ip_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index afcabe205b8f..807685da2934 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -27,10 +27,10 @@ struct tcpudphdr { __be16 dst; }; -static bool ebt_filter_ip6(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, - unsigned int datalen) +static bool +ebt_ip6_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; const struct ipv6hdr *ih6; @@ -92,9 +92,12 @@ static bool ebt_filter_ip6(const struct sk_buff *skb, return true; } -static bool ebt_ip6_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_ip6_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { + const struct ebt_entry *e = entry; struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) @@ -123,8 +126,8 @@ static struct ebt_match filter_ip6 = .name = EBT_IP6_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_ip6, - .check = ebt_ip6_check, + .match = ebt_ip6_mt, + .checkentry = ebt_ip6_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_ip6_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 9ca0a2564c8d..d3372739227e 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -30,9 +30,10 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) -static bool ebt_limit_match(const struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static bool +ebt_limit_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { struct ebt_limit_info *info = (struct ebt_limit_info *)data; unsigned long now = jiffies; @@ -65,8 +66,10 @@ user2credits(u_int32_t user) return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE; } -static bool ebt_limit_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_limit_mt_check(const char *table, const void *e, + const struct xt_match *match, void *data, + unsigned int hook_mask) { struct ebt_limit_info *info = data; @@ -90,8 +93,8 @@ static struct ebt_match ebt_limit_reg __read_mostly = { .name = EBT_LIMIT_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_limit_match, - .check = ebt_limit_check, + .match = ebt_limit_mt, + .checkentry = ebt_limit_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_limit_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index c2e1c357025e..424dfdf7f27e 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -24,8 +24,10 @@ static DEFINE_SPINLOCK(ebt_log_lock); -static bool ebt_log_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_log_tg_check(const char *table, const void *entry, + const struct xt_target *target, void *data, + unsigned int hook_mask) { struct ebt_log_info *info = data; @@ -192,9 +194,10 @@ out: } -static unsigned int ebt_log(const struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_log_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknr, + const struct xt_target *target, const void *data) { const struct ebt_log_info *info = data; struct nf_loginfo li; @@ -217,8 +220,8 @@ static struct ebt_watcher log = .name = EBT_LOG_WATCHER, .revision = 0, .family = NFPROTO_BRIDGE, - .watcher = ebt_log, - .check = ebt_log_check, + .target = ebt_log_tg, + .checkentry = ebt_log_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_log_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index 910721a12673..92c67271bd8d 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -18,9 +18,10 @@ #include #include -static unsigned int ebt_target_mark(struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_mark_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hook_nr, + const struct xt_target *target, const void *data) { const struct ebt_mark_t_info *info = data; int action = info->target & -16; @@ -37,8 +38,10 @@ static unsigned int ebt_target_mark(struct sk_buff *skb, unsigned int hooknr, return info->target | ~EBT_VERDICT_BITS; } -static bool ebt_target_mark_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_mark_tg_check(const char *table, const void *e, + const struct xt_target *target, void *data, + unsigned int hookmask) { const struct ebt_mark_t_info *info = data; int tmp; @@ -60,8 +63,8 @@ static struct ebt_target mark_target __read_mostly = { .name = EBT_MARK_TARGET, .revision = 0, .family = NFPROTO_BRIDGE, - .target = ebt_target_mark, - .check = ebt_target_mark_check, + .target = ebt_mark_tg, + .checkentry = ebt_mark_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_mark_t_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index 6512ad9b4097..db64a0de4f74 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -12,9 +12,10 @@ #include #include -static bool ebt_filter_mark(const struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, const void *data, - unsigned int datalen) +static bool +ebt_mark_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_mark_m_info *info = data; @@ -23,8 +24,10 @@ static bool ebt_filter_mark(const struct sk_buff *skb, return ((skb->mark & info->mask) == info->mark) ^ info->invert; } -static bool ebt_mark_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_mark_mt_check(const char *table, const void *e, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_mark_m_info *info = data; @@ -41,8 +44,8 @@ static struct ebt_match filter_mark __read_mostly = { .name = EBT_MARK_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_mark, - .check = ebt_mark_check, + .match = ebt_mark_mt, + .checkentry = ebt_mark_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_mark_m_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index aa0410c69a60..b415f8871883 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -19,11 +19,10 @@ #include #include -static unsigned int ebt_nflog(const struct sk_buff *skb, - unsigned int hooknr, - const struct net_device *in, - const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_nflog_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknr, + const struct xt_target *target, const void *data) { struct ebt_nflog_info *info = (struct ebt_nflog_info *)data; struct nf_loginfo li; @@ -37,10 +36,10 @@ static unsigned int ebt_nflog(const struct sk_buff *skb, return EBT_CONTINUE; } -static bool ebt_nflog_check(const char *tablename, - unsigned int hookmask, - const struct ebt_entry *e, - void *data, unsigned int datalen) +static bool +ebt_nflog_tg_check(const char *table, const void *e, + const struct xt_target *target, void *data, + unsigned int hookmask) { struct ebt_nflog_info *info = (struct ebt_nflog_info *)data; @@ -54,8 +53,8 @@ static struct ebt_watcher nflog __read_mostly = { .name = EBT_NFLOG_WATCHER, .revision = 0, .family = NFPROTO_BRIDGE, - .watcher = ebt_nflog, - .check = ebt_nflog_check, + .target = ebt_nflog_tg, + .checkentry = ebt_nflog_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nflog_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index a9acecc88e9e..06393452ef91 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -12,19 +12,21 @@ #include #include -static bool ebt_filter_pkttype(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const void *data, - unsigned int datalen) +static bool +ebt_pkttype_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, + bool *hotdrop) { const struct ebt_pkttype_info *info = data; return (skb->pkt_type == info->pkt_type) ^ info->invert; } -static bool ebt_pkttype_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_pkttype_mt_check(const char *table, const void *e, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_pkttype_info *info = data; @@ -38,8 +40,8 @@ static struct ebt_match filter_pkttype __read_mostly = { .name = EBT_PKTTYPE_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_pkttype, - .check = ebt_pkttype_check, + .match = ebt_pkttype_mt, + .checkentry = ebt_pkttype_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_pkttype_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 4c628108bcda..e9540cf4f6d6 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -15,10 +15,10 @@ #include #include -static unsigned int ebt_target_redirect(struct sk_buff *skb, - unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_redirect_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknr, + const struct xt_target *target, const void *data) { const struct ebt_redirect_info *info = data; @@ -34,8 +34,10 @@ static unsigned int ebt_target_redirect(struct sk_buff *skb, return info->target; } -static bool ebt_target_redirect_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_redirect_tg_check(const char *tablename, const void *e, + const struct xt_target *target, void *data, + unsigned int hookmask) { const struct ebt_redirect_info *info = data; @@ -54,8 +56,8 @@ static struct ebt_target redirect_target __read_mostly = { .name = EBT_REDIRECT_TARGET, .revision = 0, .family = NFPROTO_BRIDGE, - .target = ebt_target_redirect, - .check = ebt_target_redirect_check, + .target = ebt_redirect_tg, + .checkentry = ebt_redirect_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_redirect_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 0e83de781c0c..363d0051e04b 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -16,9 +16,10 @@ #include #include -static unsigned int ebt_target_snat(struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_snat_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hook_nr, + const struct xt_target *target, const void *data) { const struct ebt_nat_info *info = data; @@ -43,8 +44,10 @@ out: return info->target | ~EBT_VERDICT_BITS; } -static bool ebt_target_snat_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_snat_tg_check(const char *tablename, const void *e, + const struct xt_target *target, void *data, + unsigned int hookmask) { const struct ebt_nat_info *info = data; int tmp; @@ -70,8 +73,8 @@ static struct ebt_target snat __read_mostly = { .name = EBT_SNAT_TARGET, .revision = 0, .family = NFPROTO_BRIDGE, - .target = ebt_target_snat, - .check = ebt_target_snat_check, + .target = ebt_snat_tg, + .checkentry = ebt_snat_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nat_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index e6d8f0c140a4..7576d1d62a49 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -119,9 +119,10 @@ static bool ebt_filter_config(const struct ebt_stp_info *info, return true; } -static bool ebt_filter_stp(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, const void *data, unsigned int datalen) +static bool +ebt_stp_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_stp_info *info = data; const struct stp_header *sp; @@ -154,12 +155,15 @@ static bool ebt_filter_stp(const struct sk_buff *skb, return true; } -static bool ebt_stp_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_stp_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { const struct ebt_stp_info *info = data; const uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; const uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const struct ebt_entry *e = entry; if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK || !(info->bitmask & EBT_STP_MASK)) @@ -176,8 +180,8 @@ static struct ebt_match filter_stp __read_mostly = { .name = EBT_STP_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_stp, - .check = ebt_stp_check, + .match = ebt_stp_mt, + .checkentry = ebt_stp_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_stp_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 076b44590f16..77ff9c46b268 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -246,9 +246,10 @@ static void ebt_log_packet(u_int8_t pf, unsigned int hooknum, ebt_ulog_packet(hooknum, skb, in, out, &loginfo, prefix); } -static unsigned int ebt_ulog(const struct sk_buff *skb, unsigned int hooknr, - const struct net_device *in, const struct net_device *out, - const void *data, unsigned int datalen) +static unsigned int +ebt_ulog_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknr, + const struct xt_target *target, const void *data) { const struct ebt_ulog_info *uloginfo = data; @@ -256,8 +257,10 @@ static unsigned int ebt_ulog(const struct sk_buff *skb, unsigned int hooknr, return EBT_CONTINUE; } -static bool ebt_ulog_check(const char *tablename, unsigned int hookmask, - const struct ebt_entry *e, void *data, unsigned int datalen) +static bool +ebt_ulog_tg_check(const char *table, const void *entry, + const struct xt_target *target, void *data, + unsigned int hookmask) { struct ebt_ulog_info *uloginfo = data; @@ -276,8 +279,8 @@ static struct ebt_watcher ulog __read_mostly = { .name = EBT_ULOG_WATCHER, .revision = 0, .family = NFPROTO_BRIDGE, - .watcher = ebt_ulog, - .check = ebt_ulog_check, + .target = ebt_ulog_tg, + .checkentry = ebt_ulog_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_ulog_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 9e3a39ae4660..3af688b0fc37 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -41,10 +41,9 @@ MODULE_LICENSE("GPL"); #define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return false; } static bool -ebt_filter_vlan(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const void *data, unsigned int datalen) +ebt_vlan_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *data, int offset, unsigned int protoff, bool *hotdrop) { const struct ebt_vlan_info *info = data; const struct vlan_hdr *fp; @@ -88,11 +87,12 @@ ebt_filter_vlan(const struct sk_buff *skb, } static bool -ebt_check_vlan(const char *tablename, - unsigned int hooknr, - const struct ebt_entry *e, void *data, unsigned int datalen) +ebt_vlan_mt_check(const char *table, const void *entry, + const struct xt_match *match, void *data, + unsigned int hook_mask) { struct ebt_vlan_info *info = data; + const struct ebt_entry *e = entry; /* Is it 802.1Q frame checked? */ if (e->ethproto != htons(ETH_P_8021Q)) { @@ -166,8 +166,8 @@ static struct ebt_match filter_vlan __read_mostly = { .name = EBT_VLAN_MATCH, .revision = 0, .family = NFPROTO_BRIDGE, - .match = ebt_filter_vlan, - .check = ebt_check_vlan, + .match = ebt_vlan_mt, + .checkentry = ebt_vlan_mt_check, .matchsize = XT_ALIGN(sizeof(struct ebt_vlan_info)), .me = THIS_MODULE, }; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index bc4b3f4f37c4..340e1c6bdcb1 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -67,11 +67,10 @@ static struct ebt_target ebt_standard_target = { }; static inline int ebt_do_watcher (struct ebt_entry_watcher *w, - const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, + struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out) { - w->u.watcher->watcher(skb, hooknr, in, out, w->data, - w->watcher_size); + w->u.watcher->target(skb, in, out, hooknr, NULL, w->data); /* watchers don't give a verdict */ return 0; } @@ -80,8 +79,7 @@ static inline int ebt_do_match (struct ebt_entry_match *m, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out) { - return m->u.match->match(skb, in, out, m->data, - m->match_size); + return m->u.match->match(skb, in, out, NULL, m->data, 0, 0, NULL); } static inline int ebt_dev_check(char *entry, const struct net_device *device) @@ -195,8 +193,8 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, if (!t->u.target->target) verdict = ((struct ebt_standard_target *)t)->verdict; else - verdict = t->u.target->target(skb, hook, - in, out, t->data, t->target_size); + verdict = t->u.target->target(skb, in, out, hook, + NULL, t->data); if (verdict == EBT_ACCEPT) { read_unlock_bh(&table->lock); return NF_ACCEPT; @@ -376,8 +374,8 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, match->name, XT_ALIGN(match->matchsize), m->match_size); goto out; } - if (match->check && - !match->check(name, hookmask, e, m->data, m->match_size)) { + if (match->checkentry && + !match->checkentry(name, e, NULL, m->data, hookmask)) { BUGPRINT("match->check failed\n"); goto out; } @@ -426,8 +424,8 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, w->watcher_size); goto out; } - if (watcher->check && - !watcher->check(name, hookmask, e, w->data, w->watcher_size)) { + if (watcher->checkentry && + !watcher->checkentry(name, e, NULL, w->data, hookmask)) { BUGPRINT("watcher->check failed\n"); goto out; } @@ -609,7 +607,7 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i) if (i && (*i)-- == 0) return 1; if (m->u.match->destroy) - m->u.match->destroy(m->data, m->match_size); + m->u.match->destroy(NULL, m->data); module_put(m->u.match->me); return 0; @@ -621,7 +619,7 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i) if (i && (*i)-- == 0) return 1; if (w->u.watcher->destroy) - w->u.watcher->destroy(w->data, w->watcher_size); + w->u.watcher->destroy(NULL, w->data); module_put(w->u.watcher->me); return 0; @@ -641,7 +639,7 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL); t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); if (t->u.target->destroy) - t->u.target->destroy(t->data, t->target_size); + t->u.target->destroy(NULL, t->data); module_put(t->u.target->me); return 0; @@ -755,8 +753,8 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, module_put(t->u.target->me); ret = -EINVAL; goto cleanup_watchers; - } else if (t->u.target->check && - !t->u.target->check(name, hookmask, e, t->data, t->target_size)) { + } else if (t->u.target->checkentry && + !t->u.target->checkentry(name, e, NULL, t->data, hookmask)) { module_put(t->u.target->me); ret = -EFAULT; goto cleanup_watchers; -- cgit v1.2.3 From 043ef46c7690bfdbd5b012e15812a14a19ca5604 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:15 +0200 Subject: netfilter: move Ebtables to use Xtables Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 6 +- net/bridge/netfilter/Kconfig | 1 + net/bridge/netfilter/ebt_802_3.c | 8 +- net/bridge/netfilter/ebt_among.c | 14 +-- net/bridge/netfilter/ebt_arp.c | 8 +- net/bridge/netfilter/ebt_arpreply.c | 8 +- net/bridge/netfilter/ebt_dnat.c | 8 +- net/bridge/netfilter/ebt_ip.c | 8 +- net/bridge/netfilter/ebt_ip6.c | 9 +- net/bridge/netfilter/ebt_limit.c | 8 +- net/bridge/netfilter/ebt_log.c | 9 +- net/bridge/netfilter/ebt_mark.c | 8 +- net/bridge/netfilter/ebt_mark_m.c | 8 +- net/bridge/netfilter/ebt_nflog.c | 16 +-- net/bridge/netfilter/ebt_pkttype.c | 8 +- net/bridge/netfilter/ebt_redirect.c | 8 +- net/bridge/netfilter/ebt_snat.c | 8 +- net/bridge/netfilter/ebt_stp.c | 8 +- net/bridge/netfilter/ebt_ulog.c | 10 +- net/bridge/netfilter/ebt_vlan.c | 8 +- net/bridge/netfilter/ebtables.c | 173 ++++++++++++------------------ net/netfilter/x_tables.c | 9 +- 22 files changed, 158 insertions(+), 193 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index f20a57da7a25..d3f9243b9d9b 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -124,7 +124,7 @@ struct ebt_entry_match { union { char name[EBT_FUNCTION_MAXNAMELEN]; - struct ebt_match *match; + struct xt_match *match; } u; /* size of data */ unsigned int match_size; @@ -135,7 +135,7 @@ struct ebt_entry_watcher { union { char name[EBT_FUNCTION_MAXNAMELEN]; - struct ebt_watcher *watcher; + struct xt_target *watcher; } u; /* size of data */ unsigned int watcher_size; @@ -146,7 +146,7 @@ struct ebt_entry_target { union { char name[EBT_FUNCTION_MAXNAMELEN]; - struct ebt_target *target; + struct xt_target *target; } u; /* size of data */ unsigned int target_size; diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 909479794999..e7c197ffb2f4 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -7,6 +7,7 @@ menu "Bridge: Netfilter Configuration" config BRIDGE_NF_EBTABLES tristate "Ethernet Bridge tables (ebtables) support" + select NETFILTER_XTABLES help ebtables is a general, extensible frame/packet identification framework. Say 'Y' or 'M' here if you want to do Ethernet diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 6f1a69c28ed9..6fc2a59e09a1 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -51,8 +51,8 @@ ebt_802_3_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_802_3 __read_mostly = { - .name = EBT_802_3_MATCH, +static struct xt_match ebt_802_3_mt_reg __read_mostly = { + .name = "802_3", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_802_3_mt, @@ -63,12 +63,12 @@ static struct ebt_match filter_802_3 __read_mostly = { static int __init ebt_802_3_init(void) { - return ebt_register_match(&filter_802_3); + return xt_register_match(&ebt_802_3_mt_reg); } static void __exit ebt_802_3_fini(void) { - ebt_unregister_match(&filter_802_3); + xt_unregister_match(&ebt_802_3_mt_reg); } module_init(ebt_802_3_init); diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 84a306f085b5..084559e1840f 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -7,12 +7,12 @@ * August, 2003 * */ - -#include -#include #include #include #include +#include +#include +#include static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, const char *mac, __be32 ip) @@ -211,8 +211,8 @@ ebt_among_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_among __read_mostly = { - .name = EBT_AMONG_MATCH, +static struct xt_match ebt_among_mt_reg __read_mostly = { + .name = "among", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_among_mt, @@ -223,12 +223,12 @@ static struct ebt_match filter_among __read_mostly = { static int __init ebt_among_init(void) { - return ebt_register_match(&filter_among); + return xt_register_match(&ebt_among_mt_reg); } static void __exit ebt_among_fini(void) { - ebt_unregister_match(&filter_among); + xt_unregister_match(&ebt_among_mt_reg); } module_init(ebt_among_init); diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 6e7cd2f5ad74..a073dffe7a11 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -119,8 +119,8 @@ ebt_arp_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_arp __read_mostly = { - .name = EBT_ARP_MATCH, +static struct xt_match ebt_arp_mt_reg __read_mostly = { + .name = "arp", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_arp_mt, @@ -131,12 +131,12 @@ static struct ebt_match filter_arp __read_mostly = { static int __init ebt_arp_init(void) { - return ebt_register_match(&filter_arp); + return xt_register_match(&ebt_arp_mt_reg); } static void __exit ebt_arp_fini(void) { - ebt_unregister_match(&filter_arp); + xt_unregister_match(&ebt_arp_mt_reg); } module_init(ebt_arp_init); diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index 6f2f65897770..8071b64af46f 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -78,8 +78,8 @@ ebt_arpreply_tg_check(const char *tablename, const void *entry, return true; } -static struct ebt_target reply_target __read_mostly = { - .name = EBT_ARPREPLY_TARGET, +static struct xt_target ebt_arpreply_tg_reg __read_mostly = { + .name = "arpreply", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_arpreply_tg, @@ -90,12 +90,12 @@ static struct ebt_target reply_target __read_mostly = { static int __init ebt_arpreply_init(void) { - return ebt_register_target(&reply_target); + return xt_register_target(&ebt_arpreply_tg_reg); } static void __exit ebt_arpreply_fini(void) { - ebt_unregister_target(&reply_target); + xt_unregister_target(&ebt_arpreply_tg_reg); } module_init(ebt_arpreply_init); diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index b7cc013bd377..d2211c4a477e 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -47,8 +47,8 @@ ebt_dnat_tg_check(const char *tablename, const void *entry, return true; } -static struct ebt_target dnat __read_mostly = { - .name = EBT_DNAT_TARGET, +static struct xt_target ebt_dnat_tg_reg __read_mostly = { + .name = "dnat", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_dnat_tg, @@ -59,12 +59,12 @@ static struct ebt_target dnat __read_mostly = { static int __init ebt_dnat_init(void) { - return ebt_register_target(&dnat); + return xt_register_target(&ebt_dnat_tg_reg); } static void __exit ebt_dnat_fini(void) { - ebt_unregister_target(&dnat); + xt_unregister_target(&ebt_dnat_tg_reg); } module_init(ebt_dnat_init); diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index e7f3b1776b02..b42c7ce799b3 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -109,8 +109,8 @@ ebt_ip_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_ip __read_mostly = { - .name = EBT_IP_MATCH, +static struct xt_match ebt_ip_mt_reg __read_mostly = { + .name = "ip", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_ip_mt, @@ -121,12 +121,12 @@ static struct ebt_match filter_ip __read_mostly = { static int __init ebt_ip_init(void) { - return ebt_register_match(&filter_ip); + return xt_register_match(&ebt_ip_mt_reg); } static void __exit ebt_ip_fini(void) { - ebt_unregister_match(&filter_ip); + xt_unregister_match(&ebt_ip_mt_reg); } module_init(ebt_ip_init); diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 807685da2934..317e624ae59f 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -121,9 +121,8 @@ ebt_ip6_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_ip6 = -{ - .name = EBT_IP6_MATCH, +static struct xt_match ebt_ip6_mt_reg __read_mostly = { + .name = "ip6", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_ip6_mt, @@ -134,12 +133,12 @@ static struct ebt_match filter_ip6 = static int __init ebt_ip6_init(void) { - return ebt_register_match(&filter_ip6); + return xt_register_match(&ebt_ip6_mt_reg); } static void __exit ebt_ip6_fini(void) { - ebt_unregister_match(&filter_ip6); + xt_unregister_match(&ebt_ip6_mt_reg); } module_init(ebt_ip6_init); diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index d3372739227e..43d9a5003633 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -89,8 +89,8 @@ ebt_limit_mt_check(const char *table, const void *e, return true; } -static struct ebt_match ebt_limit_reg __read_mostly = { - .name = EBT_LIMIT_MATCH, +static struct xt_match ebt_limit_mt_reg __read_mostly = { + .name = "limit", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_limit_mt, @@ -101,12 +101,12 @@ static struct ebt_match ebt_limit_reg __read_mostly = { static int __init ebt_limit_init(void) { - return ebt_register_match(&ebt_limit_reg); + return xt_register_match(&ebt_limit_mt_reg); } static void __exit ebt_limit_fini(void) { - ebt_unregister_match(&ebt_limit_reg); + xt_unregister_match(&ebt_limit_mt_reg); } module_init(ebt_limit_init); diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 424dfdf7f27e..b40f9ed4c343 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -215,9 +215,8 @@ ebt_log_tg(struct sk_buff *skb, const struct net_device *in, return EBT_CONTINUE; } -static struct ebt_watcher log = -{ - .name = EBT_LOG_WATCHER, +static struct xt_target ebt_log_tg_reg __read_mostly = { + .name = "log", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_log_tg, @@ -236,7 +235,7 @@ static int __init ebt_log_init(void) { int ret; - ret = ebt_register_watcher(&log); + ret = xt_register_target(&ebt_log_tg_reg); if (ret < 0) return ret; nf_log_register(NFPROTO_BRIDGE, &ebt_log_logger); @@ -246,7 +245,7 @@ static int __init ebt_log_init(void) static void __exit ebt_log_fini(void) { nf_log_unregister(&ebt_log_logger); - ebt_unregister_watcher(&log); + xt_unregister_target(&ebt_log_tg_reg); } module_init(ebt_log_init); diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index 92c67271bd8d..dff19fc91cf5 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -59,8 +59,8 @@ ebt_mark_tg_check(const char *table, const void *e, return true; } -static struct ebt_target mark_target __read_mostly = { - .name = EBT_MARK_TARGET, +static struct xt_target ebt_mark_tg_reg __read_mostly = { + .name = "mark", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_mark_tg, @@ -71,12 +71,12 @@ static struct ebt_target mark_target __read_mostly = { static int __init ebt_mark_init(void) { - return ebt_register_target(&mark_target); + return xt_register_target(&ebt_mark_tg_reg); } static void __exit ebt_mark_fini(void) { - ebt_unregister_target(&mark_target); + xt_unregister_target(&ebt_mark_tg_reg); } module_init(ebt_mark_init); diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index db64a0de4f74..aa6781c7f98b 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -40,8 +40,8 @@ ebt_mark_mt_check(const char *table, const void *e, return true; } -static struct ebt_match filter_mark __read_mostly = { - .name = EBT_MARK_MATCH, +static struct xt_match ebt_mark_mt_reg __read_mostly = { + .name = "mark_m", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_mark_mt, @@ -52,12 +52,12 @@ static struct ebt_match filter_mark __read_mostly = { static int __init ebt_mark_m_init(void) { - return ebt_register_match(&filter_mark); + return xt_register_match(&ebt_mark_mt_reg); } static void __exit ebt_mark_m_fini(void) { - ebt_unregister_match(&filter_mark); + xt_unregister_match(&ebt_mark_mt_reg); } module_init(ebt_mark_m_init); diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index b415f8871883..917ac3600791 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -49,24 +49,24 @@ ebt_nflog_tg_check(const char *table, const void *e, return true; } -static struct ebt_watcher nflog __read_mostly = { - .name = EBT_NFLOG_WATCHER, - .revision = 0, - .family = NFPROTO_BRIDGE, - .target = ebt_nflog_tg, +static struct xt_target ebt_nflog_tg_reg __read_mostly = { + .name = "nflog", + .revision = 0, + .family = NFPROTO_BRIDGE, + .target = ebt_nflog_tg, .checkentry = ebt_nflog_tg_check, .targetsize = XT_ALIGN(sizeof(struct ebt_nflog_info)), - .me = THIS_MODULE, + .me = THIS_MODULE, }; static int __init ebt_nflog_init(void) { - return ebt_register_watcher(&nflog); + return xt_register_target(&ebt_nflog_tg_reg); } static void __exit ebt_nflog_fini(void) { - ebt_unregister_watcher(&nflog); + xt_unregister_target(&ebt_nflog_tg_reg); } module_init(ebt_nflog_init); diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 06393452ef91..1c04ce5a52c7 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -36,8 +36,8 @@ ebt_pkttype_mt_check(const char *table, const void *e, return true; } -static struct ebt_match filter_pkttype __read_mostly = { - .name = EBT_PKTTYPE_MATCH, +static struct xt_match ebt_pkttype_mt_reg __read_mostly = { + .name = "pkttype", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_pkttype_mt, @@ -48,12 +48,12 @@ static struct ebt_match filter_pkttype __read_mostly = { static int __init ebt_pkttype_init(void) { - return ebt_register_match(&filter_pkttype); + return xt_register_match(&ebt_pkttype_mt_reg); } static void __exit ebt_pkttype_fini(void) { - ebt_unregister_match(&filter_pkttype); + xt_unregister_match(&ebt_pkttype_mt_reg); } module_init(ebt_pkttype_init); diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index e9540cf4f6d6..1b7684ffe404 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -52,8 +52,8 @@ ebt_redirect_tg_check(const char *tablename, const void *e, return true; } -static struct ebt_target redirect_target __read_mostly = { - .name = EBT_REDIRECT_TARGET, +static struct xt_target ebt_redirect_tg_reg __read_mostly = { + .name = "redirect", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_redirect_tg, @@ -64,12 +64,12 @@ static struct ebt_target redirect_target __read_mostly = { static int __init ebt_redirect_init(void) { - return ebt_register_target(&redirect_target); + return xt_register_target(&ebt_redirect_tg_reg); } static void __exit ebt_redirect_fini(void) { - ebt_unregister_target(&redirect_target); + xt_unregister_target(&ebt_redirect_tg_reg); } module_init(ebt_redirect_init); diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 363d0051e04b..c90217a4f9e1 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -69,8 +69,8 @@ ebt_snat_tg_check(const char *tablename, const void *e, return true; } -static struct ebt_target snat __read_mostly = { - .name = EBT_SNAT_TARGET, +static struct xt_target ebt_snat_tg_reg __read_mostly = { + .name = "snat", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_snat_tg, @@ -81,12 +81,12 @@ static struct ebt_target snat __read_mostly = { static int __init ebt_snat_init(void) { - return ebt_register_target(&snat); + return xt_register_target(&ebt_snat_tg_reg); } static void __exit ebt_snat_fini(void) { - ebt_unregister_target(&snat); + xt_unregister_target(&ebt_snat_tg_reg); } module_init(ebt_snat_init); diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 7576d1d62a49..28bb48b67a80 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -176,8 +176,8 @@ ebt_stp_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_stp __read_mostly = { - .name = EBT_STP_MATCH, +static struct xt_match ebt_stp_mt_reg __read_mostly = { + .name = "stp", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_stp_mt, @@ -188,12 +188,12 @@ static struct ebt_match filter_stp __read_mostly = { static int __init ebt_stp_init(void) { - return ebt_register_match(&filter_stp); + return xt_register_match(&ebt_stp_mt_reg); } static void __exit ebt_stp_fini(void) { - ebt_unregister_match(&filter_stp); + xt_unregister_match(&ebt_stp_mt_reg); } module_init(ebt_stp_init); diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 77ff9c46b268..25ca6467349e 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -275,8 +275,8 @@ ebt_ulog_tg_check(const char *table, const void *entry, return 0; } -static struct ebt_watcher ulog __read_mostly = { - .name = EBT_ULOG_WATCHER, +static struct xt_target ebt_ulog_tg_reg __read_mostly = { + .name = "ulog", .revision = 0, .family = NFPROTO_BRIDGE, .target = ebt_ulog_tg, @@ -286,7 +286,7 @@ static struct ebt_watcher ulog __read_mostly = { }; static const struct nf_logger ebt_ulog_logger = { - .name = EBT_ULOG_WATCHER, + .name = "ulog", .logfn = &ebt_log_packet, .me = THIS_MODULE, }; @@ -315,7 +315,7 @@ static int __init ebt_ulog_init(void) printk(KERN_WARNING KBUILD_MODNAME ": out of memory trying to " "call netlink_kernel_create\n"); ret = false; - } else if (ebt_register_watcher(&ulog) != 0) { + } else if (xt_register_target(&ebt_ulog_tg_reg) != 0) { netlink_kernel_release(ebtulognl); } @@ -331,7 +331,7 @@ static void __exit ebt_ulog_fini(void) int i; nf_log_unregister(&ebt_ulog_logger); - ebt_unregister_watcher(&ulog); + xt_unregister_target(&ebt_ulog_tg_reg); for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { ub = &ulog_buffers[i]; if (timer_pending(&ub->timer)) diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 3af688b0fc37..5addef6d62f0 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -162,8 +162,8 @@ ebt_vlan_mt_check(const char *table, const void *entry, return true; } -static struct ebt_match filter_vlan __read_mostly = { - .name = EBT_VLAN_MATCH, +static struct xt_match ebt_vlan_mt_reg __read_mostly = { + .name = "vlan", .revision = 0, .family = NFPROTO_BRIDGE, .match = ebt_vlan_mt, @@ -177,12 +177,12 @@ static int __init ebt_vlan_init(void) DEBUG_MSG("ebtables 802.1Q extension module v" MODULE_VERS "\n"); DEBUG_MSG("module debug=%d\n", !!debug); - return ebt_register_match(&filter_vlan); + return xt_register_match(&ebt_vlan_mt_reg); } static void __exit ebt_vlan_fini(void) { - ebt_unregister_match(&filter_vlan); + xt_unregister_match(&ebt_vlan_mt_reg); } module_init(ebt_vlan_init); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 340e1c6bdcb1..c4f7a2e8ed39 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -60,17 +60,18 @@ static LIST_HEAD(ebt_targets); static LIST_HEAD(ebt_matches); static LIST_HEAD(ebt_watchers); -static struct ebt_target ebt_standard_target = { +static struct xt_target ebt_standard_target = { .name = "standard", .revision = 0, .family = NFPROTO_BRIDGE, + .targetsize = sizeof(int), }; static inline int ebt_do_watcher (struct ebt_entry_watcher *w, struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, const struct net_device *out) { - w->u.watcher->target(skb, in, out, hooknr, NULL, w->data); + w->u.watcher->target(skb, in, out, hooknr, w->u.watcher, w->data); /* watchers don't give a verdict */ return 0; } @@ -79,7 +80,7 @@ static inline int ebt_do_match (struct ebt_entry_match *m, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out) { - return m->u.match->match(skb, in, out, NULL, m->data, 0, 0, NULL); + return m->u.match->match(skb, in, out, m->u.match, m->data, 0, 0, NULL); } static inline int ebt_dev_check(char *entry, const struct net_device *device) @@ -194,7 +195,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, verdict = ((struct ebt_standard_target *)t)->verdict; else verdict = t->u.target->target(skb, in, out, hook, - NULL, t->data); + t->u.target, t->data); if (verdict == EBT_ACCEPT) { read_unlock_bh(&table->lock); return NF_ACCEPT; @@ -336,104 +337,73 @@ static inline int ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, const char *name, unsigned int hookmask, unsigned int *cnt) { - struct ebt_match *match; + struct xt_match *match; size_t left = ((char *)e + e->watchers_offset) - (char *)m; int ret; if (left < sizeof(struct ebt_entry_match) || left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; - match = find_match_lock(m->u.name, &ret, &ebt_mutex); - if (!match) - return ret; - m->u.match = match; - if (!try_module_get(match->me)) { - mutex_unlock(&ebt_mutex); + + match = try_then_request_module(xt_find_match(NFPROTO_BRIDGE, + m->u.name, 0), "ebt_%s", m->u.name); + if (IS_ERR(match)) + return PTR_ERR(match); + if (match == NULL) return -ENOENT; - } - mutex_unlock(&ebt_mutex); - if (match->family != NFPROTO_BRIDGE) { - printk(KERN_WARNING "ebtables: %s match: not for ebtables?\n", - match->name); - goto out; - } - if (match->revision != 0) { - printk(KERN_WARNING "ebtables: %s match: ebtables is not " - "supporting revisions at this time\n", - match->name); - goto out; - } - if (XT_ALIGN(match->matchsize) != m->match_size && - match->matchsize != -1) { - /* - * ebt_among is exempt from centralized matchsize checking - * because it uses a dynamic-size data set. - */ - printk(KERN_WARNING "ebtables: %s match: " - "invalid size %Zu != %u\n", - match->name, XT_ALIGN(match->matchsize), m->match_size); - goto out; - } - if (match->checkentry && + m->u.match = match; + + ret = xt_check_match(match, NFPROTO_BRIDGE, m->match_size, + name, hookmask, e->ethproto, e->invflags & EBT_IPROTO); + if (ret < 0) { + module_put(match->me); + return ret; + } else if (match->checkentry != NULL && !match->checkentry(name, e, NULL, m->data, hookmask)) { + module_put(match->me); BUGPRINT("match->check failed\n"); - goto out; + return -EINVAL; } + (*cnt)++; return 0; - out: - module_put(match->me); - return -EINVAL; } static inline int ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, const char *name, unsigned int hookmask, unsigned int *cnt) { - struct ebt_watcher *watcher; + struct xt_target *watcher; size_t left = ((char *)e + e->target_offset) - (char *)w; int ret; if (left < sizeof(struct ebt_entry_watcher) || left - sizeof(struct ebt_entry_watcher) < w->watcher_size) return -EINVAL; - watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex); - if (!watcher) - return ret; - w->u.watcher = watcher; - if (!try_module_get(watcher->me)) { - mutex_unlock(&ebt_mutex); + + watcher = try_then_request_module( + xt_find_target(NFPROTO_BRIDGE, w->u.name, 0), + "ebt_%s", w->u.name); + if (IS_ERR(watcher)) + return PTR_ERR(watcher); + if (watcher == NULL) return -ENOENT; - } - mutex_unlock(&ebt_mutex); - if (watcher->family != NFPROTO_BRIDGE) { - printk(KERN_WARNING "ebtables: %s watcher: not for ebtables?\n", - watcher->name); - goto out; - } - if (watcher->revision != 0) { - printk(KERN_WARNING "ebtables: %s watcher: ebtables is not " - "supporting revisions at this time\n", - watcher->name); - goto out; - } - if (XT_ALIGN(watcher->targetsize) != w->watcher_size) { - printk(KERN_WARNING "ebtables: %s watcher: " - "invalid size %Zu != %u\n", - watcher->name, XT_ALIGN(watcher->targetsize), - w->watcher_size); - goto out; - } - if (watcher->checkentry && + w->u.watcher = watcher; + + ret = xt_check_target(watcher, NFPROTO_BRIDGE, w->watcher_size, + name, hookmask, e->ethproto, e->invflags & EBT_IPROTO); + if (ret < 0) { + module_put(watcher->me); + return ret; + } else if (watcher->checkentry != NULL && !watcher->checkentry(name, e, NULL, w->data, hookmask)) { + module_put(watcher->me); BUGPRINT("watcher->check failed\n"); - goto out; + return -EINVAL; } + (*cnt)++; return 0; - out: - module_put(watcher->me); - return -EINVAL; } static int ebt_verify_pointers(struct ebt_replace *repl, @@ -607,7 +577,7 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i) if (i && (*i)-- == 0) return 1; if (m->u.match->destroy) - m->u.match->destroy(NULL, m->data); + m->u.match->destroy(m->u.match, m->data); module_put(m->u.match->me); return 0; @@ -619,7 +589,7 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i) if (i && (*i)-- == 0) return 1; if (w->u.watcher->destroy) - w->u.watcher->destroy(NULL, w->data); + w->u.watcher->destroy(w->u.watcher, w->data); module_put(w->u.watcher->me); return 0; @@ -639,7 +609,7 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL); t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); if (t->u.target->destroy) - t->u.target->destroy(NULL, t->data); + t->u.target->destroy(t->u.target, t->data); module_put(t->u.target->me); return 0; @@ -651,7 +621,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, struct ebt_cl_stack *cl_s, unsigned int udc_cnt) { struct ebt_entry_target *t; - struct ebt_target *target; + struct xt_target *target; unsigned int i, j, hook = 0, hookmask = 0; size_t gap; int ret; @@ -704,27 +674,15 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, goto cleanup_watchers; t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); gap = e->next_offset - e->target_offset; - target = find_target_lock(t->u.name, &ret, &ebt_mutex); - if (!target) - goto cleanup_watchers; - if (!try_module_get(target->me)) { - mutex_unlock(&ebt_mutex); - ret = -ENOENT; - goto cleanup_watchers; - } - mutex_unlock(&ebt_mutex); - if (target->family != NFPROTO_BRIDGE) { - printk(KERN_WARNING "ebtables: %s target: not for ebtables?\n", - target->name); - ret = -EINVAL; + target = try_then_request_module( + xt_find_target(NFPROTO_BRIDGE, t->u.name, 0), + "ebt_%s", t->u.name); + if (IS_ERR(target)) { + ret = PTR_ERR(target); goto cleanup_watchers; - } - if (target->revision != 0) { - printk(KERN_WARNING "ebtables: %s target: ebtables is not " - "supporting revisions at this time\n", - target->name); - ret = -EINVAL; + } else if (target == NULL) { + ret = -ENOENT; goto cleanup_watchers; } @@ -745,13 +703,12 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, module_put(t->u.target->me); ret = -EFAULT; goto cleanup_watchers; - } else if (XT_ALIGN(target->targetsize) != t->target_size) { - printk(KERN_WARNING "ebtables: %s target: " - "invalid size %Zu != %u\n", - target->name, XT_ALIGN(target->targetsize), - t->target_size); - module_put(t->u.target->me); - ret = -EINVAL; + } + + ret = xt_check_target(target, NFPROTO_BRIDGE, t->target_size, + name, hookmask, e->ethproto, e->invflags & EBT_IPROTO); + if (ret < 0) { + module_put(target->me); goto cleanup_watchers; } else if (t->u.target->checkentry && !t->u.target->checkentry(name, e, NULL, t->data, hookmask)) { @@ -1589,11 +1546,14 @@ static int __init ebtables_init(void) { int ret; - mutex_lock(&ebt_mutex); - list_add(&ebt_standard_target.list, &ebt_targets); - mutex_unlock(&ebt_mutex); - if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0) + ret = xt_register_target(&ebt_standard_target); + if (ret < 0) return ret; + ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { + xt_unregister_target(&ebt_standard_target); + return ret; + } printk(KERN_INFO "Ebtables v2.0 registered\n"); return 0; @@ -1602,6 +1562,7 @@ static int __init ebtables_init(void) static void __exit ebtables_fini(void) { nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); printk(KERN_INFO "Ebtables v2.0 unregistered\n"); } diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index aece6c2d134b..0e23f42e3411 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -30,7 +30,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("[ip,ip6,arp]_tables backend module"); +MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module"); #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) @@ -325,7 +325,12 @@ int xt_check_match(const struct xt_match *match, unsigned short family, unsigned int size, const char *table, unsigned int hook_mask, unsigned short proto, int inv_proto) { - if (XT_ALIGN(match->matchsize) != size) { + if (XT_ALIGN(match->matchsize) != size && + match->matchsize != -1) { + /* + * ebt_among is exempt from centralized matchsize checking + * because it uses a dynamic-size data set. + */ printk("%s_tables: %s match: invalid size %Zu != %u\n", xt_prefix[family], match->name, XT_ALIGN(match->matchsize), size); -- cgit v1.2.3 From 66bff35b722956cc2423f55fcf1b69cefa24ef8b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:16 +0200 Subject: netfilter: remove unused Ebtables functions Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge/ebtables.h | 6 -- net/bridge/netfilter/ebtables.c | 108 ------------------------------ 2 files changed, 114 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index d3f9243b9d9b..568a690f6a62 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -302,12 +302,6 @@ struct ebt_table ~(__alignof__(struct ebt_replace)-1)) extern int ebt_register_table(struct ebt_table *table); extern void ebt_unregister_table(struct ebt_table *table); -extern int ebt_register_match(struct ebt_match *match); -extern void ebt_unregister_match(struct ebt_match *match); -extern int ebt_register_watcher(struct ebt_watcher *watcher); -extern void ebt_unregister_watcher(struct ebt_watcher *watcher); -extern int ebt_register_target(struct ebt_target *target); -extern void ebt_unregister_target(struct ebt_target *target); extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, struct ebt_table *table); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 7964d3f03886..b489ed262fa5 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -56,9 +56,6 @@ static DEFINE_MUTEX(ebt_mutex); static LIST_HEAD(ebt_tables); -static LIST_HEAD(ebt_targets); -static LIST_HEAD(ebt_matches); -static LIST_HEAD(ebt_watchers); static struct xt_target ebt_standard_target = { .name = "standard", @@ -322,24 +319,6 @@ find_table_lock(const char *name, int *error, struct mutex *mutex) return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex); } -static inline struct ebt_match * -find_match_lock(const char *name, int *error, struct mutex *mutex) -{ - return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex); -} - -static inline struct ebt_watcher * -find_watcher_lock(const char *name, int *error, struct mutex *mutex) -{ - return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex); -} - -static inline struct ebt_target * -find_target_lock(const char *name, int *error, struct mutex *mutex) -{ - return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex); -} - static inline int ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, const char *name, unsigned int hookmask, unsigned int *cnt) @@ -1103,87 +1082,6 @@ free_newinfo: return ret; } -int ebt_register_target(struct ebt_target *target) -{ - struct ebt_target *t; - int ret; - - ret = mutex_lock_interruptible(&ebt_mutex); - if (ret != 0) - return ret; - list_for_each_entry(t, &ebt_targets, list) { - if (strcmp(t->name, target->name) == 0) { - mutex_unlock(&ebt_mutex); - return -EEXIST; - } - } - list_add(&target->list, &ebt_targets); - mutex_unlock(&ebt_mutex); - - return 0; -} - -void ebt_unregister_target(struct ebt_target *target) -{ - mutex_lock(&ebt_mutex); - list_del(&target->list); - mutex_unlock(&ebt_mutex); -} - -int ebt_register_match(struct ebt_match *match) -{ - struct ebt_match *m; - int ret; - - ret = mutex_lock_interruptible(&ebt_mutex); - if (ret != 0) - return ret; - list_for_each_entry(m, &ebt_matches, list) { - if (strcmp(m->name, match->name) == 0) { - mutex_unlock(&ebt_mutex); - return -EEXIST; - } - } - list_add(&match->list, &ebt_matches); - mutex_unlock(&ebt_mutex); - - return 0; -} - -void ebt_unregister_match(struct ebt_match *match) -{ - mutex_lock(&ebt_mutex); - list_del(&match->list); - mutex_unlock(&ebt_mutex); -} - -int ebt_register_watcher(struct ebt_watcher *watcher) -{ - struct ebt_watcher *w; - int ret; - - ret = mutex_lock_interruptible(&ebt_mutex); - if (ret != 0) - return ret; - list_for_each_entry(w, &ebt_watchers, list) { - if (strcmp(w->name, watcher->name) == 0) { - mutex_unlock(&ebt_mutex); - return -EEXIST; - } - } - list_add(&watcher->list, &ebt_watchers); - mutex_unlock(&ebt_mutex); - - return 0; -} - -void ebt_unregister_watcher(struct ebt_watcher *watcher) -{ - mutex_lock(&ebt_mutex); - list_del(&watcher->list); - mutex_unlock(&ebt_mutex); -} - int ebt_register_table(struct ebt_table *table) { struct ebt_table_info *newinfo; @@ -1575,12 +1473,6 @@ static void __exit ebtables_fini(void) EXPORT_SYMBOL(ebt_register_table); EXPORT_SYMBOL(ebt_unregister_table); -EXPORT_SYMBOL(ebt_register_match); -EXPORT_SYMBOL(ebt_unregister_match); -EXPORT_SYMBOL(ebt_register_watcher); -EXPORT_SYMBOL(ebt_unregister_watcher); -EXPORT_SYMBOL(ebt_register_target); -EXPORT_SYMBOL(ebt_unregister_target); EXPORT_SYMBOL(ebt_do_table); module_init(ebtables_init); module_exit(ebtables_fini); -- cgit v1.2.3 From 367c679007fa4f990eb7ee381326ec59d8148b0e Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:17 +0200 Subject: netfilter: xtables: do centralized checkentry call (1/2) It used to be that {ip,ip6,etc}_tables called extension->checkentry themselves, but this can be moved into the xtables core. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 6 ++++-- net/bridge/netfilter/ebtables.c | 24 ++++++------------------ net/ipv4/netfilter/arp_tables.c | 10 ++++------ net/ipv4/netfilter/ip_tables.c | 23 +++++++++-------------- net/ipv6/netfilter/ip6_tables.c | 23 +++++++++-------------- net/netfilter/x_tables.c | 12 ++++++++++-- net/sched/act_ipt.c | 14 +++----------- 7 files changed, 45 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 6989b22716e6..85aa42785a5e 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -330,10 +330,12 @@ extern void xt_unregister_matches(struct xt_match *match, unsigned int n); extern int xt_check_match(const struct xt_match *match, unsigned short family, unsigned int size, const char *table, unsigned int hook, - unsigned short proto, int inv_proto); + unsigned short proto, int inv_proto, + const void *entry, void *matchinfo); extern int xt_check_target(const struct xt_target *target, unsigned short family, unsigned int size, const char *table, unsigned int hook, - unsigned short proto, int inv_proto); + unsigned short proto, int inv_proto, + const void *entry, void *targinfo); extern struct xt_table *xt_register_table(struct net *net, struct xt_table *table, diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 7d8ead52d25f..7ee72b71d3cb 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -340,15 +340,11 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, m->u.match = match; ret = xt_check_match(match, NFPROTO_BRIDGE, m->match_size, - name, hookmask, e->ethproto, e->invflags & EBT_IPROTO); + name, hookmask, e->ethproto, e->invflags & EBT_IPROTO, + e, m->data); if (ret < 0) { module_put(match->me); return ret; - } else if (match->checkentry != NULL && - !match->checkentry(name, e, NULL, m->data, hookmask)) { - module_put(match->me); - BUGPRINT("match->check failed\n"); - return -EINVAL; } (*cnt)++; @@ -377,15 +373,11 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, w->u.watcher = watcher; ret = xt_check_target(watcher, NFPROTO_BRIDGE, w->watcher_size, - name, hookmask, e->ethproto, e->invflags & EBT_IPROTO); + name, hookmask, e->ethproto, e->invflags & EBT_IPROTO, + e, w->data); if (ret < 0) { module_put(watcher->me); return ret; - } else if (watcher->checkentry != NULL && - !watcher->checkentry(name, e, NULL, w->data, hookmask)) { - module_put(watcher->me); - BUGPRINT("watcher->check failed\n"); - return -EINVAL; } (*cnt)++; @@ -692,15 +684,11 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, } ret = xt_check_target(target, NFPROTO_BRIDGE, t->target_size, - name, hookmask, e->ethproto, e->invflags & EBT_IPROTO); + name, hookmask, e->ethproto, e->invflags & EBT_IPROTO, + e, t->data); if (ret < 0) { module_put(target->me); goto cleanup_watchers; - } else if (t->u.target->checkentry && - !t->u.target->checkentry(name, e, NULL, t->data, hookmask)) { - module_put(t->u.target->me); - ret = -EINVAL; - goto cleanup_watchers; } (*cnt)++; return 0; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index b4a9a1799c94..ae525a9afbec 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -465,15 +465,13 @@ static inline int check_target(struct arpt_entry *e, const char *name) ret = xt_check_target(target, NFPROTO_ARP, t->u.target_size - sizeof(*t), - name, e->comefrom, 0, 0); - if (!ret && t->u.kernel.target->checkentry - && !t->u.kernel.target->checkentry(name, e, target, t->data, - e->comefrom)) { + name, e->comefrom, 0, 0, e, t->data); + if (ret < 0) { duprintf("arp_tables: check failed for `%s'.\n", t->u.kernel.target->name); - ret = -EINVAL; + return ret; } - return ret; + return 0; } static inline int diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 4e7c719445c2..b4c74a7a807c 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -616,17 +616,14 @@ check_match(struct ipt_entry_match *m, const char *name, match = m->u.kernel.match; ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m), name, hookmask, ip->proto, - ip->invflags & IPT_INV_PROTO); - if (!ret && m->u.kernel.match->checkentry - && !m->u.kernel.match->checkentry(name, ip, match, m->data, - hookmask)) { + ip->invflags & IPT_INV_PROTO, ip, m->data); + if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", m->u.kernel.match->name); - ret = -EINVAL; + return ret; } - if (!ret) - (*i)++; - return ret; + ++*i; + return 0; } static int @@ -668,15 +665,13 @@ static int check_target(struct ipt_entry *e, const char *name) target = t->u.kernel.target; ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), name, e->comefrom, e->ip.proto, - e->ip.invflags & IPT_INV_PROTO); - if (!ret && t->u.kernel.target->checkentry - && !t->u.kernel.target->checkentry(name, e, target, t->data, - e->comefrom)) { + e->ip.invflags & IPT_INV_PROTO, e, t->data); + if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", t->u.kernel.target->name); - ret = -EINVAL; + return ret; } - return ret; + return 0; } static int diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 0b4557e03431..12c41b8d365b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -642,17 +642,14 @@ static int check_match(struct ip6t_entry_match *m, const char *name, match = m->u.kernel.match; ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m), name, hookmask, ipv6->proto, - ipv6->invflags & IP6T_INV_PROTO); - if (!ret && m->u.kernel.match->checkentry - && !m->u.kernel.match->checkentry(name, ipv6, match, m->data, - hookmask)) { + ipv6->invflags & IP6T_INV_PROTO, ipv6, m->data); + if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", m->u.kernel.match->name); - ret = -EINVAL; + return ret; } - if (!ret) - (*i)++; - return ret; + ++*i; + return 0; } static int @@ -694,15 +691,13 @@ static int check_target(struct ip6t_entry *e, const char *name) target = t->u.kernel.target; ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t), name, e->comefrom, e->ipv6.proto, - e->ipv6.invflags & IP6T_INV_PROTO); - if (!ret && t->u.kernel.target->checkentry - && !t->u.kernel.target->checkentry(name, e, target, t->data, - e->comefrom)) { + e->ipv6.invflags & IP6T_INV_PROTO, e, t->data); + if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", t->u.kernel.target->name); - ret = -EINVAL; + return ret; } - return ret; + return 0; } static int diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 3b1fc40cc274..d1f2fb3e8f2d 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -323,7 +323,8 @@ EXPORT_SYMBOL_GPL(xt_find_revision); int xt_check_match(const struct xt_match *match, unsigned short family, unsigned int size, const char *table, unsigned int hook_mask, - unsigned short proto, int inv_proto) + unsigned short proto, int inv_proto, const void *entry, + void *matchinfo) { if (XT_ALIGN(match->matchsize) != size && match->matchsize != -1) { @@ -351,6 +352,9 @@ int xt_check_match(const struct xt_match *match, unsigned short family, xt_prefix[family], match->name, match->proto); return -EINVAL; } + if (match->checkentry != NULL && + !match->checkentry(table, entry, match, matchinfo, hook_mask)) + return -EINVAL; return 0; } EXPORT_SYMBOL_GPL(xt_check_match); @@ -469,7 +473,8 @@ EXPORT_SYMBOL_GPL(xt_compat_match_to_user); int xt_check_target(const struct xt_target *target, unsigned short family, unsigned int size, const char *table, unsigned int hook_mask, - unsigned short proto, int inv_proto) + unsigned short proto, int inv_proto, const void *entry, + void *targinfo) { if (XT_ALIGN(target->targetsize) != size) { printk("%s_tables: %s target: invalid size %Zu != %u\n", @@ -493,6 +498,9 @@ int xt_check_target(const struct xt_target *target, unsigned short family, xt_prefix[family], target->name, target->proto); return -EINVAL; } + if (target->checkentry != NULL && + !target->checkentry(table, entry, target, targinfo, hook_mask)) + return -EINVAL; return 0; } EXPORT_SYMBOL_GPL(xt_check_target); diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index d1263b3c96c3..79ea19375caf 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -51,20 +51,12 @@ static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int t->u.kernel.target = target; ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), - table, hook, 0, 0); - if (ret) { + table, hook, 0, 0, NULL, t->data); + if (ret < 0) { module_put(t->u.kernel.target->me); return ret; } - if (t->u.kernel.target->checkentry - && !t->u.kernel.target->checkentry(table, NULL, - t->u.kernel.target, t->data, - hook)) { - module_put(t->u.kernel.target->me); - ret = -EINVAL; - } - - return ret; + return 0; } static void ipt_destroy_target(struct ipt_entry_target *t) -- cgit v1.2.3 From f7108a20dee44e5bb037f9e48f6a207b42e6ae1c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:18 +0200 Subject: netfilter: xtables: move extension arguments into compound structure (1/6) The function signatures for Xtables extensions have grown over time. It involves a lot of typing/replication, and also a bit of stack space even if they are not used. Realize an NFWS2008 idea and pack them into structs. The skb remains outside of the struct so gcc can continue to apply its optimizations. This patch does this for match extensions' match functions. A few ambiguities have also been addressed. The "offset" parameter for example has been renamed to "fragoff" (there are so many different offsets already) and "protoff" to "thoff" (there is more than just one protocol here, so clarify). Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 28 ++++++++++++++++------ net/bridge/netfilter/ebt_802_3.c | 6 ++--- net/bridge/netfilter/ebt_among.c | 6 ++--- net/bridge/netfilter/ebt_arp.c | 6 ++--- net/bridge/netfilter/ebt_ip.c | 6 ++--- net/bridge/netfilter/ebt_ip6.c | 6 ++--- net/bridge/netfilter/ebt_limit.c | 6 ++--- net/bridge/netfilter/ebt_mark_m.c | 6 ++--- net/bridge/netfilter/ebt_pkttype.c | 7 ++---- net/bridge/netfilter/ebt_stp.c | 6 ++--- net/bridge/netfilter/ebt_vlan.c | 6 ++--- net/bridge/netfilter/ebtables.c | 16 ++++++++----- net/ipv4/netfilter/ip_tables.c | 46 ++++++++++++++++-------------------- net/ipv4/netfilter/ipt_addrtype.c | 18 +++++--------- net/ipv4/netfilter/ipt_ah.c | 14 ++++------- net/ipv4/netfilter/ipt_ecn.c | 9 +++---- net/ipv4/netfilter/ipt_ttl.c | 7 ++---- net/ipv6/netfilter/ip6_tables.c | 44 +++++++++++++--------------------- net/ipv6/netfilter/ip6t_ah.c | 11 ++++----- net/ipv6/netfilter/ip6t_eui64.c | 9 +++---- net/ipv6/netfilter/ip6t_frag.c | 11 ++++----- net/ipv6/netfilter/ip6t_hbh.c | 13 ++++------ net/ipv6/netfilter/ip6t_hl.c | 7 ++---- net/ipv6/netfilter/ip6t_ipv6header.c | 7 ++---- net/ipv6/netfilter/ip6t_mh.c | 15 +++++------- net/ipv6/netfilter/ip6t_rt.c | 11 ++++----- net/netfilter/xt_comment.c | 5 +--- net/netfilter/xt_connbytes.c | 7 ++---- net/netfilter/xt_connlimit.c | 17 ++++++------- net/netfilter/xt_connmark.c | 14 ++++------- net/netfilter/xt_conntrack.c | 22 +++++++---------- net/netfilter/xt_dccp.c | 16 ++++++------- net/netfilter/xt_dscp.c | 30 ++++++++--------------- net/netfilter/xt_esp.c | 13 ++++------ net/netfilter/xt_hashlimit.c | 22 +++++++---------- net/netfilter/xt_helper.c | 7 ++---- net/netfilter/xt_iprange.c | 21 +++++----------- net/netfilter/xt_length.c | 14 ++++------- net/netfilter/xt_limit.c | 7 ++---- net/netfilter/xt_mac.c | 7 ++---- net/netfilter/xt_mark.c | 13 ++++------ net/netfilter/xt_multiport.c | 26 ++++++++------------ net/netfilter/xt_owner.c | 21 +++++----------- net/netfilter/xt_physdev.c | 7 ++---- net/netfilter/xt_pkttype.c | 11 ++++----- net/netfilter/xt_policy.c | 11 ++++----- net/netfilter/xt_quota.c | 7 ++---- net/netfilter/xt_rateest.c | 12 +++------- net/netfilter/xt_realm.c | 7 ++---- net/netfilter/xt_recent.c | 17 ++++++------- net/netfilter/xt_sctp.c | 16 ++++++------- net/netfilter/xt_socket.c | 11 ++------- net/netfilter/xt_state.c | 7 ++---- net/netfilter/xt_statistic.c | 7 ++---- net/netfilter/xt_string.c | 9 +++---- net/netfilter/xt_tcpmss.c | 13 ++++------ net/netfilter/xt_tcpudp.c | 36 ++++++++++++---------------- net/netfilter/xt_time.c | 6 ++--- net/netfilter/xt_u32.c | 7 ++---- 59 files changed, 286 insertions(+), 487 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 85aa42785a5e..bcd40ec83257 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -173,6 +173,26 @@ struct xt_counters_info #include +/** + * struct xt_match_param - parameters for match extensions' match functions + * + * @in: input netdevice + * @out: output netdevice + * @match: struct xt_match through which this function was invoked + * @matchinfo: per-match data + * @fragoff: packet is a fragment, this is the data offset + * @thoff: position of transport header relative to skb->data + * @hotdrop: drop packet if we had inspection problems + */ +struct xt_match_param { + const struct net_device *in, *out; + const struct xt_match *match; + const void *matchinfo; + int fragoff; + unsigned int thoff; + bool *hotdrop; +}; + struct xt_match { struct list_head list; @@ -185,13 +205,7 @@ struct xt_match non-linear skb, using skb_header_pointer and skb_ip_make_writable. */ bool (*match)(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop); + const struct xt_match_param *); /* Called when user tries to insert an entry of this type. */ /* Should return true or false. */ diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 6fc2a59e09a1..c9e1bc149513 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -13,11 +13,9 @@ #include static bool -ebt_802_3_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_802_3_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_802_3_info *info = data; + const struct ebt_802_3_info *info = par->matchinfo; const struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb); __be16 type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type; diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 084559e1840f..0ad0db3e815d 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -128,11 +128,9 @@ static int get_ip_src(const struct sk_buff *skb, __be32 *addr) } static bool -ebt_among_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_among_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_among_info *info = data; + const struct ebt_among_info *info = par->matchinfo; const char *dmac, *smac; const struct ebt_mac_wormhash *wh_dst, *wh_src; __be32 dip = 0, sip = 0; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index a073dffe7a11..1ff8fa3a9e7b 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -16,11 +16,9 @@ #include static bool -ebt_arp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_arp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_arp_info *info = data; + const struct ebt_arp_info *info = par->matchinfo; const struct arphdr *ah; struct arphdr _arph; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index b42c7ce799b3..c70ea39840b7 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -25,11 +25,9 @@ struct tcpudphdr { }; static bool -ebt_ip_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_ip_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_ip_info *info = data; + const struct ebt_ip_info *info = par->matchinfo; const struct iphdr *ih; struct iphdr _iph; const struct tcpudphdr *pptr; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 7bd983129674..5acee02de723 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -28,11 +28,9 @@ struct tcpudphdr { }; static bool -ebt_ip6_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_ip6_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_ip6_info *info = data; + const struct ebt_ip6_info *info = par->matchinfo; const struct ipv6hdr *ih6; struct ipv6hdr _ip6h; const struct tcpudphdr *pptr; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 58aaaa149068..9a3ec8cadaa4 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -31,11 +31,9 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) static bool -ebt_limit_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - struct ebt_limit_info *info = (void *)data; + struct ebt_limit_info *info = (void *)par->matchinfo; unsigned long now = jiffies; spin_lock_bh(&limit_lock); diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index aa6781c7f98b..5b22ef96127c 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -13,11 +13,9 @@ #include static bool -ebt_mark_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_mark_m_info *info = data; + const struct ebt_mark_m_info *info = par->matchinfo; if (info->bitmask & EBT_MARK_OR) return !!(skb->mark & info->mask) ^ info->invert; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 1c04ce5a52c7..b756f88fb10f 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -13,12 +13,9 @@ #include static bool -ebt_pkttype_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, - bool *hotdrop) +ebt_pkttype_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_pkttype_info *info = data; + const struct ebt_pkttype_info *info = par->matchinfo; return (skb->pkt_type == info->pkt_type) ^ info->invert; } diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 28bb48b67a80..06d777c62c32 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -120,11 +120,9 @@ static bool ebt_filter_config(const struct ebt_stp_info *info, } static bool -ebt_stp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_stp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_stp_info *info = data; + const struct ebt_stp_info *info = par->matchinfo; const struct stp_header *sp; struct stp_header _stph; const uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 5addef6d62f0..b05b4a818341 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -41,11 +41,9 @@ MODULE_LICENSE("GPL"); #define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return false; } static bool -ebt_vlan_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *data, int offset, unsigned int protoff, bool *hotdrop) +ebt_vlan_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ebt_vlan_info *info = data; + const struct ebt_vlan_info *info = par->matchinfo; const struct vlan_hdr *fp; struct vlan_hdr _frame; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 7ee72b71d3cb..f8e1822f38d4 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -74,11 +74,11 @@ static inline int ebt_do_watcher (struct ebt_entry_watcher *w, } static inline int ebt_do_match (struct ebt_entry_match *m, - const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, bool *hotdrop) + const struct sk_buff *skb, struct xt_match_param *par) { - return m->u.match->match(skb, in, out, m->u.match, - m->data, 0, 0, hotdrop); + par->match = m->u.match; + par->matchinfo = m->data; + return m->u.match->match(skb, par); } static inline int ebt_dev_check(char *entry, const struct net_device *device) @@ -155,6 +155,11 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, char *base; struct ebt_table_info *private; bool hotdrop = false; + struct xt_match_param mtpar; + + mtpar.in = in; + mtpar.out = out; + mtpar.hotdrop = &hotdrop; read_lock_bh(&table->lock); private = table->private; @@ -175,8 +180,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, if (ebt_basic_match(point, eth_hdr(skb), in, out)) goto letscontinue; - if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, - in, out, &hotdrop) != 0) + if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &mtpar) != 0) goto letscontinue; if (hotdrop) { read_unlock_bh(&table->lock); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index b4c74a7a807c..99fdb59454fd 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -186,16 +186,14 @@ ipt_error(struct sk_buff *skb, /* Performance critical - called for every packet */ static inline bool -do_match(struct ipt_entry_match *m, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int offset, - bool *hotdrop) +do_match(struct ipt_entry_match *m, const struct sk_buff *skb, + struct xt_match_param *par) { + par->match = m->u.kernel.match; + par->matchinfo = m->data; + /* Stop iteration if it doesn't match */ - if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data, - offset, ip_hdrlen(skb), hotdrop)) + if (!m->u.kernel.match->match(skb, par)) return true; else return false; @@ -326,7 +324,6 @@ ipt_do_table(struct sk_buff *skb, struct xt_table *table) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); - u_int16_t offset; const struct iphdr *ip; u_int16_t datalen; bool hotdrop = false; @@ -336,6 +333,7 @@ ipt_do_table(struct sk_buff *skb, void *table_base; struct ipt_entry *e, *back; struct xt_table_info *private; + struct xt_match_param mtpar; /* Initialization */ ip = ip_hdr(skb); @@ -348,7 +346,11 @@ ipt_do_table(struct sk_buff *skb, * things we don't know, ie. tcp syn flag or ports). If the * rule is also a fragment-specific rule, non-fragments won't * match it. */ - offset = ntohs(ip->frag_off) & IP_OFFSET; + mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; + mtpar.thoff = ip_hdrlen(skb); + mtpar.hotdrop = &hotdrop; + mtpar.in = in; + mtpar.out = out; read_lock_bh(&table->lock); IP_NF_ASSERT(table->valid_hooks & (1 << hook)); @@ -362,12 +364,11 @@ ipt_do_table(struct sk_buff *skb, do { IP_NF_ASSERT(e); IP_NF_ASSERT(back); - if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) { + if (ip_packet_match(ip, indev, outdev, + &e->ip, mtpar.fragoff)) { struct ipt_entry_target *t; - if (IPT_MATCH_ITERATE(e, do_match, - skb, in, out, - offset, &hotdrop) != 0) + if (IPT_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) goto no_match; ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1); @@ -2116,30 +2117,23 @@ icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, } static bool -icmp_match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop) +icmp_match(const struct sk_buff *skb, const struct xt_match_param *par) { const struct icmphdr *ic; struct icmphdr _icmph; - const struct ipt_icmp *icmpinfo = matchinfo; + const struct ipt_icmp *icmpinfo = par->matchinfo; /* Must not be a fragment. */ - if (offset) + if (par->fragoff != 0) return false; - ic = skb_header_pointer(skb, protoff, sizeof(_icmph), &_icmph); + ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph); if (ic == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("Dropping evil ICMP tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c index 2c9d88a6c838..e60995e4c20c 100644 --- a/net/ipv4/netfilter/ipt_addrtype.c +++ b/net/ipv4/netfilter/ipt_addrtype.c @@ -30,12 +30,9 @@ static inline bool match_type(const struct net_device *dev, __be32 addr, } static bool -addrtype_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +addrtype_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_addrtype_info *info = matchinfo; + const struct ipt_addrtype_info *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); bool ret = true; @@ -50,20 +47,17 @@ addrtype_mt_v0(const struct sk_buff *skb, const struct net_device *in, } static bool -addrtype_mt_v1(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +addrtype_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_addrtype_info_v1 *info = matchinfo; + const struct ipt_addrtype_info_v1 *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); const struct net_device *dev = NULL; bool ret = true; if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN) - dev = in; + dev = par->in; else if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) - dev = out; + dev = par->out; if (info->source) ret &= match_type(dev, iph->saddr, info->source) ^ diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c index e2e993edd665..2fce19ef4f3f 100644 --- a/net/ipv4/netfilter/ipt_ah.c +++ b/net/ipv4/netfilter/ipt_ah.c @@ -36,27 +36,23 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool -ah_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool ah_mt(const struct sk_buff *skb, const struct xt_match_param *par) { struct ip_auth_hdr _ahdr; const struct ip_auth_hdr *ah; - const struct ipt_ah *ahinfo = matchinfo; + const struct ipt_ah *ahinfo = par->matchinfo; /* Must not be a fragment. */ - if (offset) + if (par->fragoff != 0) return false; - ah = skb_header_pointer(skb, protoff, - sizeof(_ahdr), &_ahdr); + ah = skb_header_pointer(skb, par->thoff, sizeof(_ahdr), &_ahdr); if (ah == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("Dropping evil AH tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return 0; } diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index 2c45b4be7c3c..069154631508 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -67,12 +67,9 @@ static inline bool match_tcp(const struct sk_buff *skb, return true; } -static bool -ecn_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool ecn_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_ecn_info *info = matchinfo; + const struct ipt_ecn_info *info = par->matchinfo; if (info->operation & IPT_ECN_OP_MATCH_IP) if (!match_ip(skb, info)) @@ -81,7 +78,7 @@ ecn_mt(const struct sk_buff *skb, const struct net_device *in, if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { if (ip_hdr(skb)->protocol != IPPROTO_TCP) return false; - if (!match_tcp(skb, info, hotdrop)) + if (!match_tcp(skb, info, par->hotdrop)) return false; } diff --git a/net/ipv4/netfilter/ipt_ttl.c b/net/ipv4/netfilter/ipt_ttl.c index d4c3fdc2a79f..297f1cbf4ff5 100644 --- a/net/ipv4/netfilter/ipt_ttl.c +++ b/net/ipv4/netfilter/ipt_ttl.c @@ -18,12 +18,9 @@ MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("Xtables: IPv4 TTL field match"); MODULE_LICENSE("GPL"); -static bool -ttl_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool ttl_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_ttl_info *info = matchinfo; + const struct ipt_ttl_info *info = par->matchinfo; const u8 ttl = ip_hdr(skb)->ttl; switch (info->mode) { diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 12c41b8d365b..cf2c5370a4e8 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -215,17 +215,14 @@ ip6t_error(struct sk_buff *skb, /* Performance critical - called for every packet */ static inline bool -do_match(struct ip6t_entry_match *m, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int offset, - unsigned int protoff, - bool *hotdrop) +do_match(struct ip6t_entry_match *m, const struct sk_buff *skb, + struct xt_match_param *par) { + par->match = m->u.kernel.match; + par->matchinfo = m->data; + /* Stop iteration if it doesn't match */ - if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data, - offset, protoff, hotdrop)) + if (!m->u.kernel.match->match(skb, par)) return true; else return false; @@ -355,8 +352,6 @@ ip6t_do_table(struct sk_buff *skb, struct xt_table *table) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); - int offset = 0; - unsigned int protoff = 0; bool hotdrop = false; /* Initializing verdict to NF_DROP keeps gcc happy. */ unsigned int verdict = NF_DROP; @@ -364,6 +359,7 @@ ip6t_do_table(struct sk_buff *skb, void *table_base; struct ip6t_entry *e, *back; struct xt_table_info *private; + struct xt_match_param mtpar; /* Initialization */ indev = in ? in->name : nulldevname; @@ -374,6 +370,9 @@ ip6t_do_table(struct sk_buff *skb, * things we don't know, ie. tcp syn flag or ports). If the * rule is also a fragment-specific rule, non-fragments won't * match it. */ + mtpar.hotdrop = &hotdrop; + mtpar.in = in; + mtpar.out = out; read_lock_bh(&table->lock); IP_NF_ASSERT(table->valid_hooks & (1 << hook)); @@ -388,12 +387,10 @@ ip6t_do_table(struct sk_buff *skb, IP_NF_ASSERT(e); IP_NF_ASSERT(back); if (ip6_packet_match(skb, indev, outdev, &e->ipv6, - &protoff, &offset, &hotdrop)) { + &mtpar.thoff, &mtpar.fragoff, &hotdrop)) { struct ip6t_entry_target *t; - if (IP6T_MATCH_ITERATE(e, do_match, - skb, in, out, - offset, protoff, &hotdrop) != 0) + if (IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) goto no_match; ADD_COUNTER(e->counters, @@ -2141,30 +2138,23 @@ icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, } static bool -icmp6_match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop) +icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par) { const struct icmp6hdr *ic; struct icmp6hdr _icmph; - const struct ip6t_icmp *icmpinfo = matchinfo; + const struct ip6t_icmp *icmpinfo = par->matchinfo; /* Must not be a fragment. */ - if (offset) + if (par->fragoff != 0) return false; - ic = skb_header_pointer(skb, protoff, sizeof(_icmph), &_icmph); + ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph); if (ic == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("Dropping evil ICMP tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 061f89beeb67..a04f2b8396e9 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -36,14 +36,11 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool -ah_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool ah_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { struct ip_auth_hdr _ah; const struct ip_auth_hdr *ah; - const struct ip6t_ah *ahinfo = matchinfo; + const struct ip6t_ah *ahinfo = par->matchinfo; unsigned int ptr; unsigned int hdrlen = 0; int err; @@ -51,13 +48,13 @@ ah_mt6(const struct sk_buff *skb, const struct net_device *in, err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL); if (err < 0) { if (err != -ENOENT) - *hotdrop = true; + *par->hotdrop = true; return false; } ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah); if (ah == NULL) { - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index ba38df1116f0..db610bacbcce 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -20,18 +20,15 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andras Kis-Szabo "); static bool -eui64_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +eui64_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { unsigned char eui64[8]; int i = 0; if (!(skb_mac_header(skb) >= skb->head && skb_mac_header(skb) + ETH_HLEN <= skb->data) && - offset != 0) { - *hotdrop = true; + par->fragoff != 0) { + *par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index 972f699af22c..6951d0dacf45 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -35,27 +35,24 @@ id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) } static bool -frag_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +frag_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { struct frag_hdr _frag; const struct frag_hdr *fh; - const struct ip6t_frag *fraginfo = matchinfo; + const struct ip6t_frag *fraginfo = par->matchinfo; unsigned int ptr; int err; err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL); if (err < 0) { if (err != -ENOENT) - *hotdrop = true; + *par->hotdrop = true; return false; } fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); if (fh == NULL) { - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index d5edb51a595a..d3351978819a 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -42,14 +42,11 @@ MODULE_ALIAS("ip6t_dst"); */ static bool -hbh_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +hbh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { struct ipv6_opt_hdr _optsh; const struct ipv6_opt_hdr *oh; - const struct ip6t_opts *optinfo = matchinfo; + const struct ip6t_opts *optinfo = par->matchinfo; unsigned int temp; unsigned int ptr; unsigned int hdrlen = 0; @@ -61,16 +58,16 @@ hbh_mt6(const struct sk_buff *skb, const struct net_device *in, unsigned int optlen; int err; - err = ipv6_find_hdr(skb, &ptr, match->data, NULL); + err = ipv6_find_hdr(skb, &ptr, par->match->data, NULL); if (err < 0) { if (err != -ENOENT) - *hotdrop = true; + *par->hotdrop = true; return false; } oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); if (oh == NULL) { - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_hl.c b/net/ipv6/netfilter/ip6t_hl.c index 25c1eb92fac3..c964dca1132d 100644 --- a/net/ipv6/netfilter/ip6t_hl.c +++ b/net/ipv6/netfilter/ip6t_hl.c @@ -19,12 +19,9 @@ MODULE_AUTHOR("Maciej Soltysiak "); MODULE_DESCRIPTION("Xtables: IPv6 Hop Limit field match"); MODULE_LICENSE("GPL"); -static bool -hl_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool hl_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ip6t_hl_info *info = matchinfo; + const struct ip6t_hl_info *info = par->matchinfo; const struct ipv6hdr *ip6h = ipv6_hdr(skb); switch (info->mode) { diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index ef0661aacea7..6aaca511d473 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -27,12 +27,9 @@ MODULE_DESCRIPTION("Xtables: IPv6 header types match"); MODULE_AUTHOR("Andras Kis-Szabo "); static bool -ipv6header_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +ipv6header_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ip6t_ipv6header_info *info = matchinfo; + const struct ip6t_ipv6header_info *info = par->matchinfo; unsigned int temp; int len; u8 nexthdr; diff --git a/net/ipv6/netfilter/ip6t_mh.c b/net/ipv6/netfilter/ip6t_mh.c index dd876274ff6e..2803258b6d07 100644 --- a/net/ipv6/netfilter/ip6t_mh.c +++ b/net/ipv6/netfilter/ip6t_mh.c @@ -37,32 +37,29 @@ type_match(u_int8_t min, u_int8_t max, u_int8_t type, bool invert) return (type >= min && type <= max) ^ invert; } -static bool -mh_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool mh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { struct ip6_mh _mh; const struct ip6_mh *mh; - const struct ip6t_mh *mhinfo = matchinfo; + const struct ip6t_mh *mhinfo = par->matchinfo; /* Must not be a fragment. */ - if (offset) + if (par->fragoff != 0) return false; - mh = skb_header_pointer(skb, protoff, sizeof(_mh), &_mh); + mh = skb_header_pointer(skb, par->thoff, sizeof(_mh), &_mh); if (mh == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ duprintf("Dropping evil MH tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } if (mh->ip6mh_proto != IPPROTO_NONE) { duprintf("Dropping invalid MH Payload Proto: %u\n", mh->ip6mh_proto); - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 7c544ae591d8..9cf4b8a37af7 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -36,14 +36,11 @@ segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) return r; } -static bool -rt_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool rt_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { struct ipv6_rt_hdr _route; const struct ipv6_rt_hdr *rh; - const struct ip6t_rt *rtinfo = matchinfo; + const struct ip6t_rt *rtinfo = par->matchinfo; unsigned int temp; unsigned int ptr; unsigned int hdrlen = 0; @@ -55,13 +52,13 @@ rt_mt6(const struct sk_buff *skb, const struct net_device *in, err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL); if (err < 0) { if (err != -ENOENT) - *hotdrop = true; + *par->hotdrop = true; return false; } rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route); if (rh == NULL) { - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_comment.c b/net/netfilter/xt_comment.c index fa211b2ab874..bd7aa57af428 100644 --- a/net/netfilter/xt_comment.c +++ b/net/netfilter/xt_comment.c @@ -16,10 +16,7 @@ MODULE_ALIAS("ipt_comment"); MODULE_ALIAS("ip6t_comment"); static bool -comment_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protooff, - bool *hotdrop) +comment_mt(const struct sk_buff *skb, const struct xt_match_param *par) { /* We always match */ return true; diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index d2cd22a49c96..30c19b5fe908 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -17,12 +17,9 @@ MODULE_ALIAS("ipt_connbytes"); MODULE_ALIAS("ip6t_connbytes"); static bool -connbytes_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +connbytes_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_connbytes_info *sinfo = matchinfo; + const struct xt_connbytes_info *sinfo = par->matchinfo; const struct nf_conn *ct; enum ip_conntrack_info ctinfo; u_int64_t what = 0; /* initialize to make gcc happy */ diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index bd00830ff697..8b8f70e76646 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -178,12 +178,9 @@ static int count_them(struct xt_connlimit_data *data, } static bool -connlimit_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_connlimit_info *info = matchinfo; + const struct xt_connlimit_info *info = par->matchinfo; union nf_inet_addr addr; struct nf_conntrack_tuple tuple; const struct nf_conntrack_tuple *tuple_ptr = &tuple; @@ -195,10 +192,10 @@ connlimit_mt(const struct sk_buff *skb, const struct net_device *in, if (ct != NULL) tuple_ptr = &ct->tuplehash[0].tuple; else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), - match->family, &tuple)) + par->match->family, &tuple)) goto hotdrop; - if (match->family == NFPROTO_IPV6) { + if (par->match->family == NFPROTO_IPV6) { const struct ipv6hdr *iph = ipv6_hdr(skb); memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr)); } else { @@ -208,19 +205,19 @@ connlimit_mt(const struct sk_buff *skb, const struct net_device *in, spin_lock_bh(&info->data->lock); connections = count_them(info->data, tuple_ptr, &addr, - &info->mask, match); + &info->mask, par->match); spin_unlock_bh(&info->data->lock); if (connections < 0) { /* kmalloc failed, drop it entirely */ - *hotdrop = true; + *par->hotdrop = true; return false; } return (connections > info->limit) ^ info->inverse; hotdrop: - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 0577b8ff4e1e..df4f4a865a5e 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -34,12 +34,9 @@ MODULE_ALIAS("ipt_connmark"); MODULE_ALIAS("ip6t_connmark"); static bool -connmark_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +connmark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_connmark_mtinfo1 *info = matchinfo; + const struct xt_connmark_mtinfo1 *info = par->matchinfo; enum ip_conntrack_info ctinfo; const struct nf_conn *ct; @@ -51,12 +48,9 @@ connmark_mt(const struct sk_buff *skb, const struct net_device *in, } static bool -connmark_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +connmark_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_connmark_info *info = matchinfo; + const struct xt_connmark_info *info = par->matchinfo; const struct nf_conn *ct; enum ip_conntrack_info ctinfo; diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 392b457f9c22..13a7e4eacdfd 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -25,12 +25,9 @@ MODULE_ALIAS("ipt_conntrack"); MODULE_ALIAS("ip6t_conntrack"); static bool -conntrack_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +conntrack_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_conntrack_info *sinfo = matchinfo; + const struct xt_conntrack_info *sinfo = par->matchinfo; const struct nf_conn *ct; enum ip_conntrack_info ctinfo; unsigned int statebit; @@ -205,12 +202,9 @@ ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info, } static bool -conntrack_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_conntrack_mtinfo1 *info = matchinfo; + const struct xt_conntrack_mtinfo1 *info = par->matchinfo; enum ip_conntrack_info ctinfo; const struct nf_conn *ct; unsigned int statebit; @@ -244,22 +238,22 @@ conntrack_mt(const struct sk_buff *skb, const struct net_device *in, return false; if (info->match_flags & XT_CONNTRACK_ORIGSRC) - if (conntrack_mt_origsrc(ct, info, match->family) ^ + if (conntrack_mt_origsrc(ct, info, par->match->family) ^ !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) return false; if (info->match_flags & XT_CONNTRACK_ORIGDST) - if (conntrack_mt_origdst(ct, info, match->family) ^ + if (conntrack_mt_origdst(ct, info, par->match->family) ^ !(info->invert_flags & XT_CONNTRACK_ORIGDST)) return false; if (info->match_flags & XT_CONNTRACK_REPLSRC) - if (conntrack_mt_replsrc(ct, info, match->family) ^ + if (conntrack_mt_replsrc(ct, info, par->match->family) ^ !(info->invert_flags & XT_CONNTRACK_REPLSRC)) return false; if (info->match_flags & XT_CONNTRACK_REPLDST) - if (conntrack_mt_repldst(ct, info, match->family) ^ + if (conntrack_mt_repldst(ct, info, par->match->family) ^ !(info->invert_flags & XT_CONNTRACK_REPLDST)) return false; diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 87971f47132d..7aa30bb91050 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -93,20 +93,18 @@ match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff, } static bool -dccp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +dccp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_dccp_info *info = matchinfo; + const struct xt_dccp_info *info = par->matchinfo; const struct dccp_hdr *dh; struct dccp_hdr _dh; - if (offset) + if (par->fragoff != 0) return false; - dh = skb_header_pointer(skb, protoff, sizeof(_dh), &_dh); + dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh); if (dh == NULL) { - *hotdrop = true; + *par->hotdrop = true; return false; } @@ -118,8 +116,8 @@ dccp_mt(const struct sk_buff *skb, const struct net_device *in, XT_DCCP_DEST_PORTS, info->flags, info->invflags) && DCCHECK(match_types(dh, info->typemask), XT_DCCP_TYPE, info->flags, info->invflags) - && DCCHECK(match_option(info->option, skb, protoff, dh, - hotdrop), + && DCCHECK(match_option(info->option, skb, par->thoff, dh, + par->hotdrop), XT_DCCP_OPTION, info->flags, info->invflags); } diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 7f03aa13a955..57d612061358 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -26,23 +26,18 @@ MODULE_ALIAS("ipt_tos"); MODULE_ALIAS("ip6t_tos"); static bool -dscp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +dscp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_dscp_info *info = matchinfo; + const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; return (dscp == info->dscp) ^ !!info->invert; } static bool -dscp_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +dscp_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_dscp_info *info = matchinfo; + const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; return (dscp == info->dscp) ^ !!info->invert; @@ -63,24 +58,19 @@ dscp_mt_check(const char *tablename, const void *info, return true; } -static bool tos_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, const void *matchinfo, - int offset, unsigned int protoff, bool *hotdrop) +static bool +tos_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_tos_info *info = matchinfo; + const struct ipt_tos_info *info = par->matchinfo; return (ip_hdr(skb)->tos == info->tos) ^ info->invert; } -static bool tos_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +static bool tos_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_tos_match_info *info = matchinfo; + const struct xt_tos_match_info *info = par->matchinfo; - if (match->family == NFPROTO_IPV4) + if (par->match->family == NFPROTO_IPV4) return ((ip_hdr(skb)->tos & info->tos_mask) == info->tos_value) ^ !!info->invert; else diff --git a/net/netfilter/xt_esp.c b/net/netfilter/xt_esp.c index 045c4deecafc..6d59f2e7c1c1 100644 --- a/net/netfilter/xt_esp.c +++ b/net/netfilter/xt_esp.c @@ -42,26 +42,23 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool -esp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool esp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct ip_esp_hdr *eh; struct ip_esp_hdr _esp; - const struct xt_esp *espinfo = matchinfo; + const struct xt_esp *espinfo = par->matchinfo; /* Must not be a fragment. */ - if (offset) + if (par->fragoff != 0) return false; - eh = skb_header_pointer(skb, protoff, sizeof(_esp), &_esp); + eh = skb_header_pointer(skb, par->thoff, sizeof(_esp), &_esp); if (eh == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("Dropping evil ESP tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 7bae369603d7..22a60a728cf1 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -563,19 +563,16 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, } static bool -hashlimit_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +hashlimit_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { const struct xt_hashlimit_info *r = - ((const struct xt_hashlimit_info *)matchinfo)->u.master; + ((const struct xt_hashlimit_info *)par->matchinfo)->u.master; struct xt_hashlimit_htable *hinfo = r->hinfo; unsigned long now = jiffies; struct dsthash_ent *dh; struct dsthash_dst dst; - if (hashlimit_init_dst(hinfo, &dst, skb, protoff) < 0) + if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0) goto hotdrop; spin_lock_bh(&hinfo->lock); @@ -613,23 +610,20 @@ hashlimit_mt_v0(const struct sk_buff *skb, const struct net_device *in, return false; hotdrop: - *hotdrop = true; + *par->hotdrop = true; return false; } static bool -hashlimit_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_hashlimit_mtinfo1 *info = matchinfo; + const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; struct xt_hashlimit_htable *hinfo = info->hinfo; unsigned long now = jiffies; struct dsthash_ent *dh; struct dsthash_dst dst; - if (hashlimit_init_dst(hinfo, &dst, skb, protoff) < 0) + if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0) goto hotdrop; spin_lock_bh(&hinfo->lock); @@ -666,7 +660,7 @@ hashlimit_mt(const struct sk_buff *skb, const struct net_device *in, return info->cfg.mode & XT_HASHLIMIT_INVERT; hotdrop: - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index 134d94324eb9..73bdc3ba13fc 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -24,12 +24,9 @@ MODULE_ALIAS("ip6t_helper"); static bool -helper_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +helper_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_helper_info *info = matchinfo; + const struct xt_helper_info *info = par->matchinfo; const struct nf_conn *ct; const struct nf_conn_help *master_help; const struct nf_conntrack_helper *helper; diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c index a7498cc48dca..6f62c36948d9 100644 --- a/net/netfilter/xt_iprange.c +++ b/net/netfilter/xt_iprange.c @@ -17,12 +17,9 @@ #include static bool -iprange_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +iprange_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_iprange_info *info = matchinfo; + const struct ipt_iprange_info *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); if (info->flags & IPRANGE_SRC) { @@ -55,12 +52,9 @@ iprange_mt_v0(const struct sk_buff *skb, const struct net_device *in, } static bool -iprange_mt4(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +iprange_mt4(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_iprange_mtinfo *info = matchinfo; + const struct xt_iprange_mtinfo *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); bool m; @@ -111,12 +105,9 @@ iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b) } static bool -iprange_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +iprange_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_iprange_mtinfo *info = matchinfo; + const struct xt_iprange_mtinfo *info = par->matchinfo; const struct ipv6hdr *iph = ipv6_hdr(skb); bool m; diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index b8612d1914b7..c4871ca6c86d 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -21,24 +21,18 @@ MODULE_ALIAS("ipt_length"); MODULE_ALIAS("ip6t_length"); static bool -length_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +length_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_length_info *info = matchinfo; + const struct xt_length_info *info = par->matchinfo; u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } static bool -length_mt6(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +length_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_length_info *info = matchinfo; + const struct xt_length_info *info = par->matchinfo; const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr); diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index 00247bd1095e..c475eac5dbec 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -58,13 +58,10 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) static bool -limit_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) { struct xt_rateinfo *r = - ((const struct xt_rateinfo *)matchinfo)->master; + ((const struct xt_rateinfo *)par->matchinfo)->master; unsigned long now = jiffies; spin_lock_bh(&limit_lock); diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c index 60db240098ac..269f9d8aef5f 100644 --- a/net/netfilter/xt_mac.c +++ b/net/netfilter/xt_mac.c @@ -24,12 +24,9 @@ MODULE_DESCRIPTION("Xtables: MAC address match"); MODULE_ALIAS("ipt_mac"); MODULE_ALIAS("ip6t_mac"); -static bool -mac_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool mac_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_mac_info *info = matchinfo; + const struct xt_mac_info *info = par->matchinfo; /* Is mac pointer valid? */ return skb_mac_header(skb) >= skb->head && diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 96dd2b63b6b6..885476146531 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -23,22 +23,17 @@ MODULE_ALIAS("ipt_mark"); MODULE_ALIAS("ip6t_mark"); static bool -mark_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +mark_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_mark_info *info = matchinfo; + const struct xt_mark_info *info = par->matchinfo; return ((skb->mark & info->mask) == info->mark) ^ info->invert; } static bool -mark_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_mark_mtinfo1 *info = matchinfo; + const struct xt_mark_mtinfo1 *info = par->matchinfo; return ((skb->mark & info->mask) == info->mark) ^ info->invert; } diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index f6fe008ab8c3..7087e291528d 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -95,25 +95,22 @@ ports_match_v1(const struct xt_multiport_v1 *minfo, } static bool -multiport_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +multiport_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { const __be16 *pptr; __be16 _ports[2]; - const struct xt_multiport *multiinfo = matchinfo; + const struct xt_multiport *multiinfo = par->matchinfo; - if (offset) + if (par->fragoff != 0) return false; - pptr = skb_header_pointer(skb, protoff, sizeof(_ports), _ports); + pptr = skb_header_pointer(skb, par->thoff, sizeof(_ports), _ports); if (pptr == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("xt_multiport: Dropping evil offset=0 tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } @@ -122,25 +119,22 @@ multiport_mt_v0(const struct sk_buff *skb, const struct net_device *in, } static bool -multiport_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +multiport_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const __be16 *pptr; __be16 _ports[2]; - const struct xt_multiport_v1 *multiinfo = matchinfo; + const struct xt_multiport_v1 *multiinfo = par->matchinfo; - if (offset) + if (par->fragoff != 0) return false; - pptr = skb_header_pointer(skb, protoff, sizeof(_ports), _ports); + pptr = skb_header_pointer(skb, par->thoff, sizeof(_ports), _ports); if (pptr == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("xt_multiport: Dropping evil offset=0 tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index d1c3b7ae9b49..493b5eb8d148 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -21,12 +21,9 @@ #include static bool -owner_mt_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +owner_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_owner_info *info = matchinfo; + const struct ipt_owner_info *info = par->matchinfo; const struct file *filp; if (skb->sk == NULL || skb->sk->sk_socket == NULL) @@ -50,12 +47,9 @@ owner_mt_v0(const struct sk_buff *skb, const struct net_device *in, } static bool -owner_mt6_v0(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +owner_mt6_v0(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ip6t_owner_info *info = matchinfo; + const struct ip6t_owner_info *info = par->matchinfo; const struct file *filp; if (skb->sk == NULL || skb->sk->sk_socket == NULL) @@ -79,12 +73,9 @@ owner_mt6_v0(const struct sk_buff *skb, const struct net_device *in, } static bool -owner_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +owner_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_owner_match_info *info = matchinfo; + const struct xt_owner_match_info *info = par->matchinfo; const struct file *filp; if (skb->sk == NULL || skb->sk->sk_socket == NULL) diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index 72a0bdd53fa8..e980e179d4f1 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -21,14 +21,11 @@ MODULE_ALIAS("ipt_physdev"); MODULE_ALIAS("ip6t_physdev"); static bool -physdev_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par) { int i; static const char nulldevname[IFNAMSIZ]; - const struct xt_physdev_info *info = matchinfo; + const struct xt_physdev_info *info = par->matchinfo; bool ret; const char *indev, *outdev; const struct nf_bridge_info *nf_bridge; diff --git a/net/netfilter/xt_pkttype.c b/net/netfilter/xt_pkttype.c index 81e86d319a8f..37753a377603 100644 --- a/net/netfilter/xt_pkttype.c +++ b/net/netfilter/xt_pkttype.c @@ -23,20 +23,17 @@ MODULE_ALIAS("ipt_pkttype"); MODULE_ALIAS("ip6t_pkttype"); static bool -pkttype_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +pkttype_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_pkttype_info *info = matchinfo; + const struct xt_pkttype_info *info = par->matchinfo; u_int8_t type; if (skb->pkt_type != PACKET_LOOPBACK) type = skb->pkt_type; - else if (match->family == NFPROTO_IPV4 && + else if (par->match->family == NFPROTO_IPV4 && ipv4_is_multicast(ip_hdr(skb)->daddr)) type = PACKET_MULTICAST; - else if (match->family == NFPROTO_IPV6 && + else if (par->match->family == NFPROTO_IPV6 && ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF) type = PACKET_MULTICAST; else diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index f1d514e9d0a2..b0a00fb0511b 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -110,18 +110,15 @@ match_policy_out(const struct sk_buff *skb, const struct xt_policy_info *info, } static bool -policy_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +policy_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_policy_info *info = matchinfo; + const struct xt_policy_info *info = par->matchinfo; int ret; if (info->flags & XT_POLICY_MATCH_IN) - ret = match_policy_in(skb, info, match->family); + ret = match_policy_in(skb, info, par->match->family); else - ret = match_policy_out(skb, info, match->family); + ret = match_policy_out(skb, info, par->match->family); if (ret < 0) ret = info->flags & XT_POLICY_MATCH_NONE ? true : false; diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index a3c8798f0cc7..3ab92666c149 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -18,13 +18,10 @@ MODULE_ALIAS("ip6t_quota"); static DEFINE_SPINLOCK(quota_lock); static bool -quota_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) { struct xt_quota_info *q = - ((const struct xt_quota_info *)matchinfo)->master; + ((const struct xt_quota_info *)par->matchinfo)->master; bool ret = q->flags & XT_QUOTA_INVERT; spin_lock_bh("a_lock); diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 4dcfd7353dba..e9f64ef45655 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -14,16 +14,10 @@ #include -static bool xt_rateest_mt(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop) +static bool +xt_rateest_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_rateest_match_info *info = matchinfo; + const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est *r; u_int32_t bps1, bps2, pps1, pps2; bool ret = true; diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c index ef65756d4894..b25942110ed7 100644 --- a/net/netfilter/xt_realm.c +++ b/net/netfilter/xt_realm.c @@ -22,12 +22,9 @@ MODULE_DESCRIPTION("Xtables: Routing realm match"); MODULE_ALIAS("ipt_realm"); static bool -realm_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +realm_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_realm_info *info = matchinfo; + const struct xt_realm_info *info = par->matchinfo; const struct dst_entry *dst = skb->dst; return (info->id == (dst->tclassid & info->mask)) ^ info->invert; diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 4a916e2624d3..baeb90a56231 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -204,19 +204,16 @@ static void recent_table_flush(struct recent_table *t) } static bool -recent_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_recent_mtinfo *info = matchinfo; + const struct xt_recent_mtinfo *info = par->matchinfo; struct recent_table *t; struct recent_entry *e; union nf_inet_addr addr = {}; u_int8_t ttl; bool ret = info->invert; - if (match->family == NFPROTO_IPV4) { + if (par->match->family == NFPROTO_IPV4) { const struct iphdr *iph = ip_hdr(skb); if (info->side == XT_RECENT_DEST) @@ -237,19 +234,19 @@ recent_mt(const struct sk_buff *skb, const struct net_device *in, } /* use TTL as seen before forwarding */ - if (out && !skb->sk) + if (par->out != NULL && skb->sk == NULL) ttl++; spin_lock_bh(&recent_lock); t = recent_table_lookup(info->name); - e = recent_entry_lookup(t, &addr, match->family, + e = recent_entry_lookup(t, &addr, par->match->family, (info->check_set & XT_RECENT_TTL) ? ttl : 0); if (e == NULL) { if (!(info->check_set & XT_RECENT_SET)) goto out; - e = recent_entry_init(t, &addr, match->family, ttl); + e = recent_entry_init(t, &addr, par->match->family, ttl); if (e == NULL) - *hotdrop = true; + *par->hotdrop = true; ret = !ret; goto out; } diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index ab67aca4d8fe..b0014ab65da7 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -117,23 +117,21 @@ match_packet(const struct sk_buff *skb, } static bool -sctp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +sctp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_sctp_info *info = matchinfo; + const struct xt_sctp_info *info = par->matchinfo; const sctp_sctphdr_t *sh; sctp_sctphdr_t _sh; - if (offset) { + if (par->fragoff != 0) { duprintf("Dropping non-first fragment.. FIXME\n"); return false; } - sh = skb_header_pointer(skb, protoff, sizeof(_sh), &_sh); + sh = skb_header_pointer(skb, par->thoff, sizeof(_sh), &_sh); if (sh == NULL) { duprintf("Dropping evil TCP offset=0 tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } duprintf("spt: %d\tdpt: %d\n", ntohs(sh->source), ntohs(sh->dest)); @@ -144,8 +142,8 @@ sctp_mt(const struct sk_buff *skb, const struct net_device *in, && SCCHECK(ntohs(sh->dest) >= info->dpts[0] && ntohs(sh->dest) <= info->dpts[1], XT_SCTP_DEST_PORTS, info->flags, info->invflags) - && SCCHECK(match_packet(skb, protoff + sizeof (sctp_sctphdr_t), - info, hotdrop), + && SCCHECK(match_packet(skb, par->thoff + sizeof(sctp_sctphdr_t), + info, par->hotdrop), XT_SCTP_CHUNK_TYPES, info->flags, info->invflags); } diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index ac9db17c7b9c..02a8fed21082 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -86,14 +86,7 @@ extract_icmp_fields(const struct sk_buff *skb, static bool -socket_mt(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop) +socket_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct iphdr *iph = ip_hdr(skb); struct udphdr _hdr, *hp = NULL; @@ -146,7 +139,7 @@ socket_mt(const struct sk_buff *skb, #endif sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, - saddr, daddr, sport, dport, in, false); + saddr, daddr, sport, dport, par->in, false); if (sk != NULL) { bool wildcard = (inet_sk(sk)->rcv_saddr == 0); diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index f92f8bcc1e38..29f5a8a1b024 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -21,12 +21,9 @@ MODULE_ALIAS("ipt_state"); MODULE_ALIAS("ip6t_state"); static bool -state_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +state_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_state_info *sinfo = matchinfo; + const struct xt_state_info *sinfo = par->matchinfo; enum ip_conntrack_info ctinfo; unsigned int statebit; diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index f41a92322e6e..dcadc491db21 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -25,12 +25,9 @@ MODULE_ALIAS("ip6t_statistic"); static DEFINE_SPINLOCK(nth_lock); static bool -statistic_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - struct xt_statistic_info *info = (struct xt_statistic_info *)matchinfo; + struct xt_statistic_info *info = (void *)par->matchinfo; bool ret = info->flags & XT_STATISTIC_INVERT; switch (info->mode) { diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index 18d8884e7370..33f2d29ca4f7 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -22,18 +22,15 @@ MODULE_ALIAS("ipt_string"); MODULE_ALIAS("ip6t_string"); static bool -string_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +string_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_string_info *conf = matchinfo; + const struct xt_string_info *conf = par->matchinfo; struct ts_state state; int invert; memset(&state, 0, sizeof(struct ts_state)); - invert = (match->revision == 0 ? conf->u.v0.invert : + invert = (par->match->revision == 0 ? conf->u.v0.invert : conf->u.v1.flags & XT_STRING_FLAG_INVERT); return (skb_find_text((struct sk_buff *)skb, conf->from_offset, diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c index 4791c7cbe5a9..4809b34b10f8 100644 --- a/net/netfilter/xt_tcpmss.c +++ b/net/netfilter/xt_tcpmss.c @@ -25,12 +25,9 @@ MODULE_ALIAS("ipt_tcpmss"); MODULE_ALIAS("ip6t_tcpmss"); static bool -tcpmss_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, - bool *hotdrop) +tcpmss_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_tcpmss_match_info *info = matchinfo; + const struct xt_tcpmss_match_info *info = par->matchinfo; const struct tcphdr *th; struct tcphdr _tcph; /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ @@ -39,7 +36,7 @@ tcpmss_mt(const struct sk_buff *skb, const struct net_device *in, unsigned int i, optlen; /* If we don't have the whole header, drop packet. */ - th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); if (th == NULL) goto dropit; @@ -52,7 +49,7 @@ tcpmss_mt(const struct sk_buff *skb, const struct net_device *in, goto out; /* Truncated options. */ - op = skb_header_pointer(skb, protoff + sizeof(*th), optlen, _opt); + op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt); if (op == NULL) goto dropit; @@ -76,7 +73,7 @@ out: return info->invert; dropit: - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index 5a6268cbb9f8..66cf71b1d59c 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -68,25 +68,22 @@ tcp_find_option(u_int8_t option, return invert; } -static bool -tcp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool tcp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct tcphdr *th; struct tcphdr _tcph; - const struct xt_tcp *tcpinfo = matchinfo; + const struct xt_tcp *tcpinfo = par->matchinfo; - if (offset) { + if (par->fragoff != 0) { /* To quote Alan: Don't allow a fragment of TCP 8 bytes in. Nobody normal causes this. Its a cracker trying to break in by doing a flag overwrite to pass the direction checks. */ - if (offset == 1) { + if (par->fragoff == 1) { duprintf("Dropping evil TCP offset=1 frag.\n"); - *hotdrop = true; + *par->hotdrop = true; } /* Must not be a fragment. */ return false; @@ -94,12 +91,12 @@ tcp_mt(const struct sk_buff *skb, const struct net_device *in, #define FWINVTCP(bool, invflg) ((bool) ^ !!(tcpinfo->invflags & (invflg))) - th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); if (th == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ duprintf("Dropping evil TCP offset=0 tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } @@ -117,13 +114,13 @@ tcp_mt(const struct sk_buff *skb, const struct net_device *in, return false; if (tcpinfo->option) { if (th->doff * 4 < sizeof(_tcph)) { - *hotdrop = true; + *par->hotdrop = true; return false; } - if (!tcp_find_option(tcpinfo->option, skb, protoff, + if (!tcp_find_option(tcpinfo->option, skb, par->thoff, th->doff*4 - sizeof(_tcph), tcpinfo->invflags & XT_TCP_INV_OPTION, - hotdrop)) + par->hotdrop)) return false; } return true; @@ -141,25 +138,22 @@ tcp_mt_check(const char *tablename, const void *info, return !(tcpinfo->invflags & ~XT_TCP_INV_MASK); } -static bool -udp_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool udp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct udphdr *uh; struct udphdr _udph; - const struct xt_udp *udpinfo = matchinfo; + const struct xt_udp *udpinfo = par->matchinfo; /* Must not be a fragment. */ - if (offset) + if (par->fragoff != 0) return false; - uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); + uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph); if (uh == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ duprintf("Dropping evil UDP tinygram.\n"); - *hotdrop = true; + *par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c index 32d4c769caa4..28599d3979c4 100644 --- a/net/netfilter/xt_time.c +++ b/net/netfilter/xt_time.c @@ -153,11 +153,9 @@ static void localtime_3(struct xtm *r, time_t time) } static bool -time_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +time_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_time_info *info = matchinfo; + const struct xt_time_info *info = par->matchinfo; unsigned int packet_time; struct xtm current_time; s64 stamp; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index a6b971dc5d38..24a527624500 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -87,12 +87,9 @@ static bool u32_match_it(const struct xt_u32 *data, return true; } -static bool -u32_mt(const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct xt_match *match, - const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) +static bool u32_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_u32 *data = matchinfo; + const struct xt_u32 *data = par->matchinfo; bool ret; ret = u32_match_it(data, skb); -- cgit v1.2.3 From 9b4fce7a3508a9776534188b6065b206a9608ccf Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:18 +0200 Subject: netfilter: xtables: move extension arguments into compound structure (2/6) This patch does this for match extensions' checkentry functions. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 32 +++++++++++++++-------- net/bridge/netfilter/ebt_802_3.c | 7 ++---- net/bridge/netfilter/ebt_among.c | 9 +++---- net/bridge/netfilter/ebt_arp.c | 9 +++---- net/bridge/netfilter/ebt_ip.c | 9 +++---- net/bridge/netfilter/ebt_ip6.c | 9 +++---- net/bridge/netfilter/ebt_limit.c | 7 ++---- net/bridge/netfilter/ebt_mark_m.c | 7 ++---- net/bridge/netfilter/ebt_pkttype.c | 7 ++---- net/bridge/netfilter/ebt_stp.c | 9 +++---- net/bridge/netfilter/ebt_vlan.c | 9 +++---- net/bridge/netfilter/ebtables.c | 19 +++++++++----- net/ipv4/netfilter/ip_tables.c | 49 +++++++++++++++++------------------- net/ipv4/netfilter/ipt_addrtype.c | 13 +++++----- net/ipv4/netfilter/ipt_ah.c | 8 ++---- net/ipv4/netfilter/ipt_ecn.c | 9 +++---- net/ipv6/netfilter/ip6_tables.c | 48 +++++++++++++++++------------------ net/ipv6/netfilter/ip6t_ah.c | 8 ++---- net/ipv6/netfilter/ip6t_frag.c | 8 ++---- net/ipv6/netfilter/ip6t_hbh.c | 8 ++---- net/ipv6/netfilter/ip6t_ipv6header.c | 7 ++---- net/ipv6/netfilter/ip6t_mh.c | 8 ++---- net/ipv6/netfilter/ip6t_rt.c | 8 ++---- net/netfilter/x_tables.c | 32 +++++++++++------------ net/netfilter/xt_connbytes.c | 14 ++++------- net/netfilter/xt_connlimit.c | 13 ++++------ net/netfilter/xt_connmark.c | 20 ++++++--------- net/netfilter/xt_conntrack.c | 9 +++---- net/netfilter/xt_dccp.c | 7 ++---- net/netfilter/xt_dscp.c | 11 +++----- net/netfilter/xt_esp.c | 8 ++---- net/netfilter/xt_hashlimit.c | 24 +++++++----------- net/netfilter/xt_helper.c | 11 +++----- net/netfilter/xt_limit.c | 7 ++---- net/netfilter/xt_mark.c | 7 ++---- net/netfilter/xt_multiport.c | 37 +++++++++------------------ net/netfilter/xt_owner.c | 14 +++-------- net/netfilter/xt_physdev.c | 13 ++++------ net/netfilter/xt_policy.c | 15 +++++------ net/netfilter/xt_quota.c | 7 ++---- net/netfilter/xt_rateest.c | 8 ++---- net/netfilter/xt_recent.c | 7 ++---- net/netfilter/xt_sctp.c | 7 ++---- net/netfilter/xt_state.c | 9 +++---- net/netfilter/xt_statistic.c | 7 ++---- net/netfilter/xt_string.c | 9 +++---- net/netfilter/xt_tcpudp.c | 16 +++--------- net/netfilter/xt_time.c | 7 ++---- 48 files changed, 240 insertions(+), 386 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index bcd40ec83257..763a704ce83f 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -193,6 +193,25 @@ struct xt_match_param { bool *hotdrop; }; +/** + * struct xt_mtchk_param - parameters for match extensions' + * checkentry functions + * + * @table: table the rule is tried to be inserted into + * @entryinfo: the family-specific rule data + * (struct ipt_ip, ip6t_ip, ebt_entry) + * @match: struct xt_match through which this function was invoked + * @matchinfo: per-match data + * @hook_mask: via which hooks the new rule is reachable + */ +struct xt_mtchk_param { + const char *table; + const void *entryinfo; + const struct xt_match *match; + void *matchinfo; + unsigned int hook_mask; +}; + struct xt_match { struct list_head list; @@ -208,12 +227,7 @@ struct xt_match const struct xt_match_param *); /* Called when user tries to insert an entry of this type. */ - /* Should return true or false. */ - bool (*checkentry)(const char *tablename, - const void *ip, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mask); + bool (*checkentry)(const struct xt_mtchk_param *); /* Called when entry of this type deleted. */ void (*destroy)(const struct xt_match *match, void *matchinfo); @@ -342,10 +356,8 @@ extern void xt_unregister_match(struct xt_match *target); extern int xt_register_matches(struct xt_match *match, unsigned int n); extern void xt_unregister_matches(struct xt_match *match, unsigned int n); -extern int xt_check_match(const struct xt_match *match, unsigned short family, - unsigned int size, const char *table, unsigned int hook, - unsigned short proto, int inv_proto, - const void *entry, void *matchinfo); +extern int xt_check_match(struct xt_mtchk_param *, u_int8_t family, + unsigned int size, u_int8_t proto, bool inv_proto); extern int xt_check_target(const struct xt_target *target, unsigned short family, unsigned int size, const char *table, unsigned int hook, unsigned short proto, int inv_proto, diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index c9e1bc149513..bd91dc58d49b 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -36,12 +36,9 @@ ebt_802_3_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_802_3_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_802_3_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_802_3_info *info = data; + const struct ebt_802_3_info *info = par->matchinfo; if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK) return false; diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 0ad0db3e815d..b595f091f35b 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -171,14 +171,11 @@ ebt_among_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_among_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_among_mt_check(const struct xt_mtchk_param *par) { + const struct ebt_among_info *info = par->matchinfo; const struct ebt_entry_match *em = - container_of(data, const struct ebt_entry_match, data); - const struct ebt_among_info *info = data; + container_of(par->matchinfo, const struct ebt_entry_match, data); int expected_length = sizeof(struct ebt_among_info); const struct ebt_mac_wormhash *wh_dst, *wh_src; int err; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 1ff8fa3a9e7b..b7ad60419f9a 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -100,13 +100,10 @@ ebt_arp_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_arp_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_arp_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_arp_info *info = data; - const struct ebt_entry *e = entry; + const struct ebt_arp_info *info = par->matchinfo; + const struct ebt_entry *e = par->entryinfo; if ((e->ethproto != htons(ETH_P_ARP) && e->ethproto != htons(ETH_P_RARP)) || diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index c70ea39840b7..d771bbfbcbe6 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -77,13 +77,10 @@ ebt_ip_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_ip_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_ip_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_ip_info *info = data; - const struct ebt_entry *e = entry; + const struct ebt_ip_info *info = par->matchinfo; + const struct ebt_entry *e = par->entryinfo; if (e->ethproto != htons(ETH_P_IP) || e->invflags & EBT_IPROTO) diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 5acee02de723..784a6573876c 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -90,13 +90,10 @@ ebt_ip6_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_ip6_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_ip6_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_entry *e = entry; - struct ebt_ip6_info *info = data; + const struct ebt_entry *e = par->entryinfo; + struct ebt_ip6_info *info = par->matchinfo; if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) return false; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 9a3ec8cadaa4..f7bd9192ff0c 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -64,12 +64,9 @@ user2credits(u_int32_t user) return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE; } -static bool -ebt_limit_mt_check(const char *table, const void *e, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_limit_mt_check(const struct xt_mtchk_param *par) { - struct ebt_limit_info *info = data; + struct ebt_limit_info *info = par->matchinfo; /* Check for overflow. */ if (info->burst == 0 || diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index 5b22ef96127c..ea570f214b1d 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -22,12 +22,9 @@ ebt_mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((skb->mark & info->mask) == info->mark) ^ info->invert; } -static bool -ebt_mark_mt_check(const char *table, const void *e, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_mark_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_mark_m_info *info = data; + const struct ebt_mark_m_info *info = par->matchinfo; if (info->bitmask & ~EBT_MARK_MASK) return false; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index b756f88fb10f..883e96e2a542 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -20,12 +20,9 @@ ebt_pkttype_mt(const struct sk_buff *skb, const struct xt_match_param *par) return (skb->pkt_type == info->pkt_type) ^ info->invert; } -static bool -ebt_pkttype_mt_check(const char *table, const void *e, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_pkttype_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_pkttype_info *info = data; + const struct ebt_pkttype_info *info = par->matchinfo; if (info->invert != 0 && info->invert != 1) return false; diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 06d777c62c32..48527e621626 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -153,15 +153,12 @@ ebt_stp_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_stp_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_stp_mt_check(const struct xt_mtchk_param *par) { - const struct ebt_stp_info *info = data; + const struct ebt_stp_info *info = par->matchinfo; const uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; const uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - const struct ebt_entry *e = entry; + const struct ebt_entry *e = par->entryinfo; if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK || !(info->bitmask & EBT_STP_MASK)) diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index b05b4a818341..3dddd489328e 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -84,13 +84,10 @@ ebt_vlan_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ebt_vlan_mt_check(const char *table, const void *entry, - const struct xt_match *match, void *data, - unsigned int hook_mask) +static bool ebt_vlan_mt_check(const struct xt_mtchk_param *par) { - struct ebt_vlan_info *info = data; - const struct ebt_entry *e = entry; + struct ebt_vlan_info *info = par->matchinfo; + const struct ebt_entry *e = par->entryinfo; /* Is it 802.1Q frame checked? */ if (e->ethproto != htons(ETH_P_8021Q)) { diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f8e1822f38d4..5ce37b2f5b84 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -324,9 +324,10 @@ find_table_lock(const char *name, int *error, struct mutex *mutex) } static inline int -ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, - const char *name, unsigned int hookmask, unsigned int *cnt) +ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, + unsigned int *cnt) { + const struct ebt_entry *e = par->entryinfo; struct xt_match *match; size_t left = ((char *)e + e->watchers_offset) - (char *)m; int ret; @@ -343,9 +344,10 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, return -ENOENT; m->u.match = match; - ret = xt_check_match(match, NFPROTO_BRIDGE, m->match_size, - name, hookmask, e->ethproto, e->invflags & EBT_IPROTO, - e, m->data); + par->match = match; + par->matchinfo = m->data; + ret = xt_check_match(par, NFPROTO_BRIDGE, m->match_size, + e->ethproto, e->invflags & EBT_IPROTO); if (ret < 0) { module_put(match->me); return ret; @@ -607,6 +609,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, unsigned int i, j, hook = 0, hookmask = 0; size_t gap; int ret; + struct xt_mtchk_param par; /* don't mess with the struct ebt_entries */ if (e->bitmask == 0) @@ -647,7 +650,11 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, hookmask = cl_s[i - 1].hookmask; } i = 0; - ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i); + + par.table = name; + par.entryinfo = e; + par.hook_mask = hookmask; + ret = EBT_MATCH_ITERATE(e, ebt_check_match, &par, &i); if (ret != 0) goto cleanup_matches; j = 0; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 99fdb59454fd..4147298a6a81 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -607,20 +607,20 @@ check_entry(struct ipt_entry *e, const char *name) } static int -check_match(struct ipt_entry_match *m, const char *name, - const struct ipt_ip *ip, - unsigned int hookmask, unsigned int *i) +check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par, + unsigned int *i) { - struct xt_match *match; + const struct ipt_ip *ip = par->entryinfo; int ret; - match = m->u.kernel.match; - ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m), - name, hookmask, ip->proto, - ip->invflags & IPT_INV_PROTO, ip, m->data); + par->match = m->u.kernel.match; + par->matchinfo = m->data; + + ret = xt_check_match(par, NFPROTO_IPV4, m->u.match_size - sizeof(*m), + ip->proto, ip->invflags & IPT_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", - m->u.kernel.match->name); + par.match->name); return ret; } ++*i; @@ -628,10 +628,7 @@ check_match(struct ipt_entry_match *m, const char *name, } static int -find_check_match(struct ipt_entry_match *m, - const char *name, - const struct ipt_ip *ip, - unsigned int hookmask, +find_check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par, unsigned int *i) { struct xt_match *match; @@ -646,7 +643,7 @@ find_check_match(struct ipt_entry_match *m, } m->u.kernel.match = match; - ret = check_match(m, name, ip, hookmask, i); + ret = check_match(m, par, i); if (ret) goto err; @@ -683,14 +680,17 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size, struct xt_target *target; int ret; unsigned int j; + struct xt_mtchk_param mtpar; ret = check_entry(e, name); if (ret) return ret; j = 0; - ret = IPT_MATCH_ITERATE(e, find_check_match, name, &e->ip, - e->comefrom, &j); + mtpar.table = name; + mtpar.entryinfo = &e->ip; + mtpar.hook_mask = e->comefrom; + ret = IPT_MATCH_ITERATE(e, find_check_match, &mtpar, &j); if (ret != 0) goto cleanup_matches; @@ -1644,12 +1644,15 @@ static int compat_check_entry(struct ipt_entry *e, const char *name, unsigned int *i) { + struct xt_mtchk_param mtpar; unsigned int j; int ret; j = 0; - ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, - e->comefrom, &j); + mtpar.table = name; + mtpar.entryinfo = &e->ip; + mtpar.hook_mask = e->comefrom; + ret = IPT_MATCH_ITERATE(e, check_match, &mtpar, &j); if (ret) goto cleanup_matches; @@ -2144,15 +2147,9 @@ icmp_match(const struct sk_buff *skb, const struct xt_match_param *par) !!(icmpinfo->invflags&IPT_ICMP_INV)); } -/* Called when user tries to insert an entry of this type. */ -static bool -icmp_checkentry(const char *tablename, - const void *entry, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mask) +static bool icmp_checkentry(const struct xt_mtchk_param *par) { - const struct ipt_icmp *icmpinfo = matchinfo; + const struct ipt_icmp *icmpinfo = par->matchinfo; /* Must specify no unknown invflags */ return !(icmpinfo->invflags & ~IPT_ICMP_INV); diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c index e60995e4c20c..88762f02779d 100644 --- a/net/ipv4/netfilter/ipt_addrtype.c +++ b/net/ipv4/netfilter/ipt_addrtype.c @@ -68,12 +68,9 @@ addrtype_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool -addrtype_mt_checkentry_v1(const char *tablename, const void *ip_void, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) { - struct ipt_addrtype_info_v1 *info = matchinfo; + struct ipt_addrtype_info_v1 *info = par->matchinfo; if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN && info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) { @@ -82,14 +79,16 @@ addrtype_mt_checkentry_v1(const char *tablename, const void *ip_void, return false; } - if (hook_mask & (1 << NF_INET_PRE_ROUTING | 1 << NF_INET_LOCAL_IN) && + if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN)) && info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) { printk(KERN_ERR "ipt_addrtype: output interface limitation " "not valid in PRE_ROUTING and INPUT\n"); return false; } - if (hook_mask & (1 << NF_INET_POST_ROUTING | 1 << NF_INET_LOCAL_OUT) && + if (par->hook_mask & ((1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT)) && info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN) { printk(KERN_ERR "ipt_addrtype: input interface limitation " "not valid in POST_ROUTING and OUTPUT\n"); diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c index 2fce19ef4f3f..0104c0b399de 100644 --- a/net/ipv4/netfilter/ipt_ah.c +++ b/net/ipv4/netfilter/ipt_ah.c @@ -61,13 +61,9 @@ static bool ah_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(ahinfo->invflags & IPT_AH_INV_SPI)); } -/* Called when user tries to insert an entry of this type. */ -static bool -ah_mt_check(const char *tablename, const void *ip_void, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool ah_mt_check(const struct xt_mtchk_param *par) { - const struct ipt_ah *ahinfo = matchinfo; + const struct ipt_ah *ahinfo = par->matchinfo; /* Must specify no unknown invflags */ if (ahinfo->invflags & ~IPT_AH_INV_MASK) { diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index 069154631508..6289b64144c6 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -85,13 +85,10 @@ static bool ecn_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -ecn_mt_check(const char *tablename, const void *ip_void, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool ecn_mt_check(const struct xt_mtchk_param *par) { - const struct ipt_ecn_info *info = matchinfo; - const struct ipt_ip *ip = ip_void; + const struct ipt_ecn_info *info = par->matchinfo; + const struct ipt_ip *ip = par->entryinfo; if (info->operation & IPT_ECN_OP_MATCH_MASK) return false; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index cf2c5370a4e8..9c843e3777bc 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -629,20 +629,20 @@ check_entry(struct ip6t_entry *e, const char *name) return 0; } -static int check_match(struct ip6t_entry_match *m, const char *name, - const struct ip6t_ip6 *ipv6, - unsigned int hookmask, unsigned int *i) +static int check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par, + unsigned int *i) { - struct xt_match *match; + const struct ip6t_ip6 *ipv6 = par->entryinfo; int ret; - match = m->u.kernel.match; - ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m), - name, hookmask, ipv6->proto, - ipv6->invflags & IP6T_INV_PROTO, ipv6, m->data); + par->match = m->u.kernel.match; + par->matchinfo = m->data; + + ret = xt_check_match(par, NFPROTO_IPV6, m->u.match_size - sizeof(*m), + ipv6->proto, ipv6->invflags & IP6T_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", - m->u.kernel.match->name); + par.match->name); return ret; } ++*i; @@ -650,10 +650,7 @@ static int check_match(struct ip6t_entry_match *m, const char *name, } static int -find_check_match(struct ip6t_entry_match *m, - const char *name, - const struct ip6t_ip6 *ipv6, - unsigned int hookmask, +find_check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par, unsigned int *i) { struct xt_match *match; @@ -668,7 +665,7 @@ find_check_match(struct ip6t_entry_match *m, } m->u.kernel.match = match; - ret = check_match(m, name, ipv6, hookmask, i); + ret = check_match(m, par, i); if (ret) goto err; @@ -705,14 +702,17 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size, struct xt_target *target; int ret; unsigned int j; + struct xt_mtchk_param mtpar; ret = check_entry(e, name); if (ret) return ret; j = 0; - ret = IP6T_MATCH_ITERATE(e, find_check_match, name, &e->ipv6, - e->comefrom, &j); + mtpar.table = name; + mtpar.entryinfo = &e->ipv6; + mtpar.hook_mask = e->comefrom; + ret = IP6T_MATCH_ITERATE(e, find_check_match, &mtpar, &j); if (ret != 0) goto cleanup_matches; @@ -1669,10 +1669,13 @@ static int compat_check_entry(struct ip6t_entry *e, const char *name, { unsigned int j; int ret; + struct xt_mtchk_param mtpar; j = 0; - ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, - e->comefrom, &j); + mtpar.table = name; + mtpar.entryinfo = &e->ipv6; + mtpar.hook_mask = e->comefrom; + ret = IP6T_MATCH_ITERATE(e, check_match, &mtpar, &j); if (ret) goto cleanup_matches; @@ -2166,14 +2169,9 @@ icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par) } /* Called when user tries to insert an entry of this type. */ -static bool -icmp6_checkentry(const char *tablename, - const void *entry, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mask) +static bool icmp6_checkentry(const struct xt_mtchk_param *par) { - const struct ip6t_icmp *icmpinfo = matchinfo; + const struct ip6t_icmp *icmpinfo = par->matchinfo; /* Must specify no unknown invflags */ return !(icmpinfo->invflags & ~IP6T_ICMP_INV); diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index a04f2b8396e9..3a82f24746b9 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -90,13 +90,9 @@ static bool ah_mt6(const struct sk_buff *skb, const struct xt_match_param *par) !(ahinfo->hdrres && ah->reserved); } -/* Called when user tries to insert an entry of this type. */ -static bool -ah_mt6_check(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool ah_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_ah *ahinfo = matchinfo; + const struct ip6t_ah *ahinfo = par->matchinfo; if (ahinfo->invflags & ~IP6T_AH_INV_MASK) { pr_debug("ip6t_ah: unknown flags %X\n", ahinfo->invflags); diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index 6951d0dacf45..673aa0a5084e 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -107,13 +107,9 @@ frag_mt6(const struct sk_buff *skb, const struct xt_match_param *par) && (ntohs(fh->frag_off) & IP6_MF)); } -/* Called when user tries to insert an entry of this type. */ -static bool -frag_mt6_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool frag_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_frag *fraginfo = matchinfo; + const struct ip6t_frag *fraginfo = par->matchinfo; if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) { pr_debug("ip6t_frag: unknown flags %X\n", fraginfo->invflags); diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index d3351978819a..cbe8dec9744b 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -160,13 +160,9 @@ hbh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -/* Called when user tries to insert an entry of this type. */ -static bool -hbh_mt6_check(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool hbh_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_opts *optsinfo = matchinfo; + const struct ip6t_opts *optsinfo = par->matchinfo; if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) { pr_debug("ip6t_opts: unknown flags %X\n", optsinfo->invflags); diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index 6aaca511d473..14e6724d5672 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -118,12 +118,9 @@ ipv6header_mt6(const struct sk_buff *skb, const struct xt_match_param *par) } } -static bool -ipv6header_mt6_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool ipv6header_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_ipv6header_info *info = matchinfo; + const struct ip6t_ipv6header_info *info = par->matchinfo; /* invflags is 0 or 0xff in hard mode */ if ((!info->modeflag) && info->invflags != 0x00 && diff --git a/net/ipv6/netfilter/ip6t_mh.c b/net/ipv6/netfilter/ip6t_mh.c index 2803258b6d07..aafe4e66577b 100644 --- a/net/ipv6/netfilter/ip6t_mh.c +++ b/net/ipv6/netfilter/ip6t_mh.c @@ -67,13 +67,9 @@ static bool mh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) !!(mhinfo->invflags & IP6T_MH_INV_TYPE)); } -/* Called when user tries to insert an entry of this type. */ -static bool -mh_mt6_check(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool mh_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_mh *mhinfo = matchinfo; + const struct ip6t_mh *mhinfo = par->matchinfo; /* Must specify no unknown invflags */ return !(mhinfo->invflags & ~IP6T_MH_INV_MASK); diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 9cf4b8a37af7..356b8d6f6baa 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -186,13 +186,9 @@ static bool rt_mt6(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -/* Called when user tries to insert an entry of this type. */ -static bool -rt_mt6_check(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool rt_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_rt *rtinfo = matchinfo; + const struct ip6t_rt *rtinfo = par->matchinfo; if (rtinfo->invflags & ~IP6T_RT_INV_MASK) { pr_debug("ip6t_rt: unknown flags %X\n", rtinfo->invflags); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index d1f2fb3e8f2d..817ab14f7cd6 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -321,39 +321,39 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target, } EXPORT_SYMBOL_GPL(xt_find_revision); -int xt_check_match(const struct xt_match *match, unsigned short family, - unsigned int size, const char *table, unsigned int hook_mask, - unsigned short proto, int inv_proto, const void *entry, - void *matchinfo) +int xt_check_match(struct xt_mtchk_param *par, u_int8_t family, + unsigned int size, u_int8_t proto, bool inv_proto) { - if (XT_ALIGN(match->matchsize) != size && - match->matchsize != -1) { + if (XT_ALIGN(par->match->matchsize) != size && + par->match->matchsize != -1) { /* * ebt_among is exempt from centralized matchsize checking * because it uses a dynamic-size data set. */ printk("%s_tables: %s match: invalid size %Zu != %u\n", - xt_prefix[family], match->name, - XT_ALIGN(match->matchsize), size); + xt_prefix[family], par->match->name, + XT_ALIGN(par->match->matchsize), size); return -EINVAL; } - if (match->table && strcmp(match->table, table)) { + if (par->match->table != NULL && + strcmp(par->match->table, par->table) != 0) { printk("%s_tables: %s match: only valid in %s table, not %s\n", - xt_prefix[family], match->name, match->table, table); + xt_prefix[family], par->match->name, + par->match->table, par->table); return -EINVAL; } - if (match->hooks && (hook_mask & ~match->hooks) != 0) { + if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) { printk("%s_tables: %s match: bad hook_mask %#x/%#x\n", - xt_prefix[family], match->name, hook_mask, match->hooks); + xt_prefix[family], par->match->name, + par->hook_mask, par->match->hooks); return -EINVAL; } - if (match->proto && (match->proto != proto || inv_proto)) { + if (par->match->proto && (par->match->proto != proto || inv_proto)) { printk("%s_tables: %s match: only valid for protocol %u\n", - xt_prefix[family], match->name, match->proto); + xt_prefix[family], par->match->name, par->match->proto); return -EINVAL; } - if (match->checkentry != NULL && - !match->checkentry(table, entry, match, matchinfo, hook_mask)) + if (par->match->checkentry != NULL && !par->match->checkentry(par)) return -EINVAL; return 0; } diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index 30c19b5fe908..43a36c728e56 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -92,12 +92,9 @@ connbytes_mt(const struct sk_buff *skb, const struct xt_match_param *par) return what >= sinfo->count.from; } -static bool -connbytes_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool connbytes_mt_check(const struct xt_mtchk_param *par) { - const struct xt_connbytes_info *sinfo = matchinfo; + const struct xt_connbytes_info *sinfo = par->matchinfo; if (sinfo->what != XT_CONNBYTES_PKTS && sinfo->what != XT_CONNBYTES_BYTES && @@ -109,17 +106,16 @@ connbytes_mt_check(const char *tablename, const void *ip, sinfo->direction != XT_CONNBYTES_DIR_BOTH) return false; - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", match->family); + "proto=%u\n", par->match->family); return false; } return true; } -static void -connbytes_mt_destroy(const struct xt_match *match, void *matchinfo) +static void connbytes_mt_destroy(const struct xt_match *match, void *matchinfo) { nf_ct_l3proto_module_put(match->family); } diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 8b8f70e76646..1361e9919cf2 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -221,24 +221,21 @@ connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool -connlimit_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool connlimit_mt_check(const struct xt_mtchk_param *par) { - struct xt_connlimit_info *info = matchinfo; + struct xt_connlimit_info *info = par->matchinfo; unsigned int i; - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "cannot load conntrack support for " - "address family %u\n", match->family); + "address family %u\n", par->match->family); return false; } /* init private data */ info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL); if (info->data == NULL) { - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); return false; } diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index df4f4a865a5e..b935b7888a90 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -61,33 +61,27 @@ connmark_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) return ((ct->mark & info->mask) == info->mark) ^ info->invert; } -static bool -connmark_mt_check_v0(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool connmark_mt_check_v0(const struct xt_mtchk_param *par) { - const struct xt_connmark_info *cm = matchinfo; + const struct xt_connmark_info *cm = par->matchinfo; if (cm->mark > 0xffffffff || cm->mask > 0xffffffff) { printk(KERN_WARNING "connmark: only support 32bit mark\n"); return false; } - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", match->family); + "proto=%u\n", par->match->family); return false; } return true; } -static bool -connmark_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool connmark_mt_check(const struct xt_mtchk_param *par) { - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "cannot load conntrack support for " - "proto=%u\n", match->family); + "proto=%u\n", par->match->family); return false; } return true; diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 13a7e4eacdfd..f04c46a02ce0 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -278,14 +278,11 @@ conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -conntrack_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool conntrack_mt_check(const struct xt_mtchk_param *par) { - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", match->family); + "proto=%u\n", par->match->family); return false; } return true; diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 7aa30bb91050..e5d3e8673287 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -121,12 +121,9 @@ dccp_mt(const struct sk_buff *skb, const struct xt_match_param *par) XT_DCCP_OPTION, info->flags, info->invflags); } -static bool -dccp_mt_check(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool dccp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_dccp_info *info = matchinfo; + const struct xt_dccp_info *info = par->matchinfo; return !(info->flags & ~XT_DCCP_VALID_FLAGS) && !(info->invflags & ~XT_DCCP_VALID_FLAGS) diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 57d612061358..c3f8085460d7 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -43,15 +43,12 @@ dscp_mt6(const struct sk_buff *skb, const struct xt_match_param *par) return (dscp == info->dscp) ^ !!info->invert; } -static bool -dscp_mt_check(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool dscp_mt_check(const struct xt_mtchk_param *par) { - const u_int8_t dscp = ((struct xt_dscp_info *)matchinfo)->dscp; + const struct xt_dscp_info *info = par->matchinfo; - if (dscp > XT_DSCP_MAX) { - printk(KERN_ERR "xt_dscp: dscp %x out of range\n", dscp); + if (info->dscp > XT_DSCP_MAX) { + printk(KERN_ERR "xt_dscp: dscp %x out of range\n", info->dscp); return false; } diff --git a/net/netfilter/xt_esp.c b/net/netfilter/xt_esp.c index 6d59f2e7c1c1..609439967c2c 100644 --- a/net/netfilter/xt_esp.c +++ b/net/netfilter/xt_esp.c @@ -66,13 +66,9 @@ static bool esp_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(espinfo->invflags & XT_ESP_INV_SPI)); } -/* Called when user tries to insert an entry of this type. */ -static bool -esp_mt_check(const char *tablename, const void *ip_void, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool esp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_esp *espinfo = matchinfo; + const struct xt_esp *espinfo = par->matchinfo; if (espinfo->invflags & ~XT_ESP_INV_MASK) { duprintf("xt_esp: unknown flags %X\n", espinfo->invflags); diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 22a60a728cf1..2f73820e46d7 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -664,12 +664,9 @@ hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool -hashlimit_mt_check_v0(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) { - struct xt_hashlimit_info *r = matchinfo; + struct xt_hashlimit_info *r = par->matchinfo; /* Check for overflow. */ if (r->cfg.burst == 0 || @@ -698,8 +695,8 @@ hashlimit_mt_check_v0(const char *tablename, const void *inf, * the list of htable's in htable_create(), since then we would * create duplicate proc files. -HW */ mutex_lock(&hlimit_mutex); - r->hinfo = htable_find_get(r->name, match->family); - if (!r->hinfo && htable_create_v0(r, match->family) != 0) { + r->hinfo = htable_find_get(r->name, par->match->family); + if (!r->hinfo && htable_create_v0(r, par->match->family) != 0) { mutex_unlock(&hlimit_mutex); return false; } @@ -710,12 +707,9 @@ hashlimit_mt_check_v0(const char *tablename, const void *inf, return true; } -static bool -hashlimit_mt_check(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool hashlimit_mt_check(const struct xt_mtchk_param *par) { - struct xt_hashlimit_mtinfo1 *info = matchinfo; + struct xt_hashlimit_mtinfo1 *info = par->matchinfo; /* Check for overflow. */ if (info->cfg.burst == 0 || @@ -729,7 +723,7 @@ hashlimit_mt_check(const char *tablename, const void *inf, return false; if (info->name[sizeof(info->name)-1] != '\0') return false; - if (match->family == NFPROTO_IPV4) { + if (par->match->family == NFPROTO_IPV4) { if (info->cfg.srcmask > 32 || info->cfg.dstmask > 32) return false; } else { @@ -744,8 +738,8 @@ hashlimit_mt_check(const char *tablename, const void *inf, * the list of htable's in htable_create(), since then we would * create duplicate proc files. -HW */ mutex_lock(&hlimit_mutex); - info->hinfo = htable_find_get(info->name, match->family); - if (!info->hinfo && htable_create(info, match->family) != 0) { + info->hinfo = htable_find_get(info->name, par->match->family); + if (!info->hinfo && htable_create(info, par->match->family) != 0) { mutex_unlock(&hlimit_mutex); return false; } diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index 73bdc3ba13fc..86d3c332fcb8 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -54,16 +54,13 @@ helper_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool -helper_mt_check(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool helper_mt_check(const struct xt_mtchk_param *par) { - struct xt_helper_info *info = matchinfo; + struct xt_helper_info *info = par->matchinfo; - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", match->family); + "proto=%u\n", par->match->family); return false; } info->name[29] = '\0'; diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index c475eac5dbec..c908d69a5595 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -92,12 +92,9 @@ user2credits(u_int32_t user) return (user * HZ * CREDITS_PER_JIFFY) / XT_LIMIT_SCALE; } -static bool -limit_mt_check(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool limit_mt_check(const struct xt_mtchk_param *par) { - struct xt_rateinfo *r = matchinfo; + struct xt_rateinfo *r = par->matchinfo; /* Check for overflow. */ if (r->burst == 0 diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 885476146531..10b9e34bbc5b 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -38,12 +38,9 @@ mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((skb->mark & info->mask) == info->mark) ^ info->invert; } -static bool -mark_mt_check_v0(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool mark_mt_check_v0(const struct xt_mtchk_param *par) { - const struct xt_mark_info *minfo = matchinfo; + const struct xt_mark_info *minfo = par->matchinfo; if (minfo->mark > 0xffffffff || minfo->mask > 0xffffffff) { printk(KERN_WARNING "mark: only supports 32bit mark\n"); diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index 7087e291528d..d06bb2dd3900 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -158,50 +158,37 @@ check(u_int16_t proto, && count <= XT_MULTI_PORTS; } -/* Called when user tries to insert an entry of this type. */ -static bool -multiport_mt_check_v0(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool multiport_mt_check_v0(const struct xt_mtchk_param *par) { - const struct ipt_ip *ip = info; - const struct xt_multiport *multiinfo = matchinfo; + const struct ipt_ip *ip = par->entryinfo; + const struct xt_multiport *multiinfo = par->matchinfo; return check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count); } -static bool -multiport_mt_check(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool multiport_mt_check(const struct xt_mtchk_param *par) { - const struct ipt_ip *ip = info; - const struct xt_multiport_v1 *multiinfo = matchinfo; + const struct ipt_ip *ip = par->entryinfo; + const struct xt_multiport_v1 *multiinfo = par->matchinfo; return check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count); } -static bool -multiport_mt6_check_v0(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool multiport_mt6_check_v0(const struct xt_mtchk_param *par) { - const struct ip6t_ip6 *ip = info; - const struct xt_multiport *multiinfo = matchinfo; + const struct ip6t_ip6 *ip = par->entryinfo; + const struct xt_multiport *multiinfo = par->matchinfo; return check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count); } -static bool -multiport_mt6_check(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool multiport_mt6_check(const struct xt_mtchk_param *par) { - const struct ip6t_ip6 *ip = info; - const struct xt_multiport_v1 *multiinfo = matchinfo; + const struct ip6t_ip6 *ip = par->entryinfo; + const struct xt_multiport_v1 *multiinfo = par->matchinfo; return check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count); diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index 493b5eb8d148..32f84e84d9e6 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -107,12 +107,9 @@ owner_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -owner_mt_check_v0(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool owner_mt_check_v0(const struct xt_mtchk_param *par) { - const struct ipt_owner_info *info = matchinfo; + const struct ipt_owner_info *info = par->matchinfo; if (info->match & (IPT_OWNER_PID | IPT_OWNER_SID | IPT_OWNER_COMM)) { printk(KERN_WARNING KBUILD_MODNAME @@ -124,12 +121,9 @@ owner_mt_check_v0(const char *tablename, const void *ip, return true; } -static bool -owner_mt6_check_v0(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool owner_mt6_check_v0(const struct xt_mtchk_param *par) { - const struct ip6t_owner_info *info = matchinfo; + const struct ip6t_owner_info *info = par->matchinfo; if (info->match & (IP6T_OWNER_PID | IP6T_OWNER_SID)) { printk(KERN_WARNING KBUILD_MODNAME diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index e980e179d4f1..b01786d2dd91 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -91,12 +91,9 @@ match_outdev: return ret ^ !(info->invert & XT_PHYSDEV_OP_OUT); } -static bool -physdev_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool physdev_mt_check(const struct xt_mtchk_param *par) { - const struct xt_physdev_info *info = matchinfo; + const struct xt_physdev_info *info = par->matchinfo; if (!(info->bitmask & XT_PHYSDEV_OP_MASK) || info->bitmask & ~XT_PHYSDEV_OP_MASK) @@ -104,12 +101,12 @@ physdev_mt_check(const char *tablename, const void *ip, if (info->bitmask & XT_PHYSDEV_OP_OUT && (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) || info->invert & XT_PHYSDEV_OP_BRIDGED) && - hook_mask & ((1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD) | - (1 << NF_INET_POST_ROUTING))) { + par->hook_mask & ((1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_FORWARD) | (1 << NF_INET_POST_ROUTING))) { printk(KERN_WARNING "physdev match: using --physdev-out in the " "OUTPUT, FORWARD and POSTROUTING chains for non-bridged " "traffic is not supported anymore.\n"); - if (hook_mask & (1 << NF_INET_LOCAL_OUT)) + if (par->hook_mask & (1 << NF_INET_LOCAL_OUT)) return false; } return true; diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index b0a00fb0511b..328bd20ddd25 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -128,26 +128,23 @@ policy_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool -policy_mt_check(const char *tablename, const void *ip_void, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool policy_mt_check(const struct xt_mtchk_param *par) { - const struct xt_policy_info *info = matchinfo; + const struct xt_policy_info *info = par->matchinfo; if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT))) { printk(KERN_ERR "xt_policy: neither incoming nor " "outgoing policy selected\n"); return false; } - if (hook_mask & (1 << NF_INET_PRE_ROUTING | 1 << NF_INET_LOCAL_IN) - && info->flags & XT_POLICY_MATCH_OUT) { + if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN)) && info->flags & XT_POLICY_MATCH_OUT) { printk(KERN_ERR "xt_policy: output policy not valid in " "PRE_ROUTING and INPUT\n"); return false; } - if (hook_mask & (1 << NF_INET_POST_ROUTING | 1 << NF_INET_LOCAL_OUT) - && info->flags & XT_POLICY_MATCH_IN) { + if (par->hook_mask & ((1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT)) && info->flags & XT_POLICY_MATCH_IN) { printk(KERN_ERR "xt_policy: input policy not valid in " "POST_ROUTING and OUTPUT\n"); return false; diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index 3ab92666c149..c84fce5e0f3e 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -37,12 +37,9 @@ quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool -quota_mt_check(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool quota_mt_check(const struct xt_mtchk_param *par) { - struct xt_quota_info *q = matchinfo; + struct xt_quota_info *q = par->matchinfo; if (q->flags & ~XT_QUOTA_MASK) return false; diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index e9f64ef45655..4b05ce168a78 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -74,13 +74,9 @@ xt_rateest_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool xt_rateest_mt_checkentry(const char *tablename, - const void *ip, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mask) +static bool xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) { - struct xt_rateest_match_info *info = matchinfo; + struct xt_rateest_match_info *info = par->matchinfo; struct xt_rateest *est1, *est2; if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index baeb90a56231..a512b49f3fe4 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -280,12 +280,9 @@ out: return ret; } -static bool -recent_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool recent_mt_check(const struct xt_mtchk_param *par) { - const struct xt_recent_mtinfo *info = matchinfo; + const struct xt_recent_mtinfo *info = par->matchinfo; struct recent_table *t; unsigned i; bool ret = false; diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index b0014ab65da7..e223cb43ae8e 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -147,12 +147,9 @@ sctp_mt(const struct sk_buff *skb, const struct xt_match_param *par) XT_SCTP_CHUNK_TYPES, info->flags, info->invflags); } -static bool -sctp_mt_check(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool sctp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_sctp_info *info = matchinfo; + const struct xt_sctp_info *info = par->matchinfo; return !(info->flags & ~XT_SCTP_VALID_FLAGS) && !(info->invflags & ~XT_SCTP_VALID_FLAGS) diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index 29f5a8a1b024..88b1235519d7 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -37,14 +37,11 @@ state_mt(const struct sk_buff *skb, const struct xt_match_param *par) return (sinfo->statemask & statebit); } -static bool -state_mt_check(const char *tablename, const void *inf, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool state_mt_check(const struct xt_mtchk_param *par) { - if (nf_ct_l3proto_try_module_get(match->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", match->family); + "proto=%u\n", par->match->family); return false; } return true; diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index dcadc491db21..0d75141139d5 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -49,12 +49,9 @@ statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool -statistic_mt_check(const char *tablename, const void *entry, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool statistic_mt_check(const struct xt_mtchk_param *par) { - struct xt_statistic_info *info = matchinfo; + struct xt_statistic_info *info = par->matchinfo; if (info->mode > XT_STATISTIC_MODE_MAX || info->flags & ~XT_STATISTIC_MASK) diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index 33f2d29ca4f7..c9407aa78f73 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -40,12 +40,9 @@ string_mt(const struct sk_buff *skb, const struct xt_match_param *par) #define STRING_TEXT_PRIV(m) ((struct xt_string_info *)(m)) -static bool -string_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool string_mt_check(const struct xt_mtchk_param *par) { - struct xt_string_info *conf = matchinfo; + struct xt_string_info *conf = par->matchinfo; struct ts_config *ts_conf; int flags = TS_AUTOLOAD; @@ -56,7 +53,7 @@ string_mt_check(const char *tablename, const void *ip, return false; if (conf->patlen > XT_STRING_MAX_PATTERN_SIZE) return false; - if (match->revision == 1) { + if (par->match->revision == 1) { if (conf->u.v1.flags & ~(XT_STRING_FLAG_IGNORECASE | XT_STRING_FLAG_INVERT)) return false; diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index 66cf71b1d59c..1ebdc4934eed 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -126,13 +126,9 @@ static bool tcp_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -/* Called when user tries to insert an entry of this type. */ -static bool -tcp_mt_check(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool tcp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_tcp *tcpinfo = matchinfo; + const struct xt_tcp *tcpinfo = par->matchinfo; /* Must specify no unknown invflags */ return !(tcpinfo->invflags & ~XT_TCP_INV_MASK); @@ -165,13 +161,9 @@ static bool udp_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); } -/* Called when user tries to insert an entry of this type. */ -static bool -udp_mt_check(const char *tablename, const void *info, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool udp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_udp *udpinfo = matchinfo; + const struct xt_udp *udpinfo = par->matchinfo; /* Must specify no unknown invflags */ return !(udpinfo->invflags & ~XT_UDP_INV_MASK); diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c index 28599d3979c4..29375ba8db73 100644 --- a/net/netfilter/xt_time.c +++ b/net/netfilter/xt_time.c @@ -218,12 +218,9 @@ time_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool -time_mt_check(const char *tablename, const void *ip, - const struct xt_match *match, void *matchinfo, - unsigned int hook_mask) +static bool time_mt_check(const struct xt_mtchk_param *par) { - const struct xt_time_info *info = matchinfo; + const struct xt_time_info *info = par->matchinfo; if (info->daytime_start > XT_TIME_MAX_DAYTIME || info->daytime_stop > XT_TIME_MAX_DAYTIME) { -- cgit v1.2.3 From 6be3d8598e883fb632edf059ba2f8d1b9f4da138 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:19 +0200 Subject: netfilter: xtables: move extension arguments into compound structure (3/6) This patch does this for match extensions' destroy functions. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 8 +++++++- net/bridge/netfilter/ebtables.c | 20 ++++++++++++-------- net/ipv4/netfilter/ip_tables.c | 10 +++++++--- net/ipv6/netfilter/ip6_tables.c | 10 +++++++--- net/netfilter/xt_connbytes.c | 4 ++-- net/netfilter/xt_connlimit.c | 7 +++---- net/netfilter/xt_connmark.c | 5 ++--- net/netfilter/xt_conntrack.c | 5 ++--- net/netfilter/xt_hashlimit.c | 9 ++++----- net/netfilter/xt_helper.c | 4 ++-- net/netfilter/xt_rateest.c | 5 ++--- net/netfilter/xt_recent.c | 4 ++-- net/netfilter/xt_state.c | 4 ++-- net/netfilter/xt_string.c | 4 ++-- 14 files changed, 56 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 763a704ce83f..c79c88380149 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -212,6 +212,12 @@ struct xt_mtchk_param { unsigned int hook_mask; }; +/* Match destructor parameters */ +struct xt_mtdtor_param { + const struct xt_match *match; + void *matchinfo; +}; + struct xt_match { struct list_head list; @@ -230,7 +236,7 @@ struct xt_match bool (*checkentry)(const struct xt_mtchk_param *); /* Called when entry of this type deleted. */ - void (*destroy)(const struct xt_match *match, void *matchinfo); + void (*destroy)(const struct xt_mtdtor_param *); /* Called when userspace align differs from kernel space one */ void (*compat_from_user)(void *dst, void *src); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 5ce37b2f5b84..0320b5203624 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -558,12 +558,16 @@ ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo, static inline int ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i) { + struct xt_mtdtor_param par; + if (i && (*i)-- == 0) return 1; - if (m->u.match->destroy) - m->u.match->destroy(m->u.match, m->data); - module_put(m->u.match->me); + par.match = m->u.match; + par.matchinfo = m->data; + if (par.match->destroy != NULL) + par.match->destroy(&par); + module_put(par.match->me); return 0; } @@ -609,7 +613,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, unsigned int i, j, hook = 0, hookmask = 0; size_t gap; int ret; - struct xt_mtchk_param par; + struct xt_mtchk_param mtpar; /* don't mess with the struct ebt_entries */ if (e->bitmask == 0) @@ -651,10 +655,10 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, } i = 0; - par.table = name; - par.entryinfo = e; - par.hook_mask = hookmask; - ret = EBT_MATCH_ITERATE(e, ebt_check_match, &par, &i); + mtpar.table = name; + mtpar.entryinfo = e; + mtpar.hook_mask = hookmask; + ret = EBT_MATCH_ITERATE(e, ebt_check_match, &mtpar, &i); if (ret != 0) goto cleanup_matches; j = 0; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 4147298a6a81..12ad4d5c55d6 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -576,12 +576,16 @@ mark_source_chains(struct xt_table_info *newinfo, static int cleanup_match(struct ipt_entry_match *m, unsigned int *i) { + struct xt_mtdtor_param par; + if (i && (*i)-- == 0) return 1; - if (m->u.kernel.match->destroy) - m->u.kernel.match->destroy(m->u.kernel.match, m->data); - module_put(m->u.kernel.match->me); + par.match = m->u.kernel.match; + par.matchinfo = m->data; + if (par.match->destroy != NULL) + par.match->destroy(&par); + module_put(par.match->me); return 0; } diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 9c843e3777bc..891358e89a2b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -599,12 +599,16 @@ mark_source_chains(struct xt_table_info *newinfo, static int cleanup_match(struct ip6t_entry_match *m, unsigned int *i) { + struct xt_mtdtor_param par; + if (i && (*i)-- == 0) return 1; - if (m->u.kernel.match->destroy) - m->u.kernel.match->destroy(m->u.kernel.match, m->data); - module_put(m->u.kernel.match->me); + par.match = m->u.kernel.match; + par.matchinfo = m->data; + if (par.match->destroy != NULL) + par.match->destroy(&par); + module_put(par.match->me); return 0; } diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index 43a36c728e56..5bf4aa08b0fd 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -115,9 +115,9 @@ static bool connbytes_mt_check(const struct xt_mtchk_param *par) return true; } -static void connbytes_mt_destroy(const struct xt_match *match, void *matchinfo) +static void connbytes_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); } static struct xt_match connbytes_mt_reg[] __read_mostly = { diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 1361e9919cf2..bfb3ee6c7129 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -246,16 +246,15 @@ static bool connlimit_mt_check(const struct xt_mtchk_param *par) return true; } -static void -connlimit_mt_destroy(const struct xt_match *match, void *matchinfo) +static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) { - const struct xt_connlimit_info *info = matchinfo; + const struct xt_connlimit_info *info = par->matchinfo; struct xt_connlimit_conn *conn; struct xt_connlimit_conn *tmp; struct list_head *hash = info->data->iphash; unsigned int i; - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) { list_for_each_entry_safe(conn, tmp, &hash[i], list) { diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index b935b7888a90..c708577ea1bf 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -87,10 +87,9 @@ static bool connmark_mt_check(const struct xt_mtchk_param *par) return true; } -static void -connmark_mt_destroy(const struct xt_match *match, void *matchinfo) +static void connmark_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); } #ifdef CONFIG_COMPAT diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index f04c46a02ce0..5cd58d7fcb1c 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -288,10 +288,9 @@ static bool conntrack_mt_check(const struct xt_mtchk_param *par) return true; } -static void -conntrack_mt_destroy(const struct xt_match *match, void *matchinfo) +static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); } #ifdef CONFIG_COMPAT diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 2f73820e46d7..6fc4292d46e6 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -748,17 +748,16 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par) } static void -hashlimit_mt_destroy_v0(const struct xt_match *match, void *matchinfo) +hashlimit_mt_destroy_v0(const struct xt_mtdtor_param *par) { - const struct xt_hashlimit_info *r = matchinfo; + const struct xt_hashlimit_info *r = par->matchinfo; htable_put(r->hinfo); } -static void -hashlimit_mt_destroy(const struct xt_match *match, void *matchinfo) +static void hashlimit_mt_destroy(const struct xt_mtdtor_param *par) { - const struct xt_hashlimit_mtinfo1 *info = matchinfo; + const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; htable_put(info->hinfo); } diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index 86d3c332fcb8..280c984349f3 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -67,9 +67,9 @@ static bool helper_mt_check(const struct xt_mtchk_param *par) return true; } -static void helper_mt_destroy(const struct xt_match *match, void *matchinfo) +static void helper_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); } static struct xt_match helper_mt_reg[] __read_mostly = { diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 4b05ce168a78..220a1d588ee0 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -117,10 +117,9 @@ err1: return false; } -static void xt_rateest_mt_destroy(const struct xt_match *match, - void *matchinfo) +static void xt_rateest_mt_destroy(const struct xt_mtdtor_param *par) { - struct xt_rateest_match_info *info = matchinfo; + struct xt_rateest_match_info *info = par->matchinfo; xt_rateest_put(info->est1); if (info->est2) diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index a512b49f3fe4..4ebd4ca9a991 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -349,9 +349,9 @@ out: return ret; } -static void recent_mt_destroy(const struct xt_match *match, void *matchinfo) +static void recent_mt_destroy(const struct xt_mtdtor_param *par) { - const struct xt_recent_mtinfo *info = matchinfo; + const struct xt_recent_mtinfo *info = par->matchinfo; struct recent_table *t; mutex_lock(&recent_mutex); diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index 88b1235519d7..4c946cbd731f 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -47,9 +47,9 @@ static bool state_mt_check(const struct xt_mtchk_param *par) return true; } -static void state_mt_destroy(const struct xt_match *match, void *matchinfo) +static void state_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(match->family); + nf_ct_l3proto_module_put(par->match->family); } static struct xt_match state_mt_reg[] __read_mostly = { diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index c9407aa78f73..b4d774111311 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -70,9 +70,9 @@ static bool string_mt_check(const struct xt_mtchk_param *par) return true; } -static void string_mt_destroy(const struct xt_match *match, void *matchinfo) +static void string_mt_destroy(const struct xt_mtdtor_param *par) { - textsearch_destroy(STRING_TEXT_PRIV(matchinfo)->config); + textsearch_destroy(STRING_TEXT_PRIV(par->matchinfo)->config); } static struct xt_match xt_string_mt_reg[] __read_mostly = { -- cgit v1.2.3 From 7eb3558655aaa87a3e71a0c065dfaddda521fa6d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:19 +0200 Subject: netfilter: xtables: move extension arguments into compound structure (4/6) This patch does this for target extensions' target functions. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 22 +++++++++++++++++----- net/bridge/netfilter/ebt_arpreply.c | 8 +++----- net/bridge/netfilter/ebt_dnat.c | 6 ++---- net/bridge/netfilter/ebt_log.c | 14 ++++++-------- net/bridge/netfilter/ebt_mark.c | 6 ++---- net/bridge/netfilter/ebt_nflog.c | 9 ++++----- net/bridge/netfilter/ebt_redirect.c | 12 +++++------- net/bridge/netfilter/ebt_snat.c | 6 ++---- net/bridge/netfilter/ebt_ulog.c | 9 +++------ net/bridge/netfilter/ebtables.c | 27 ++++++++++++++++----------- net/ipv4/netfilter/arp_tables.c | 23 ++++++++++++----------- net/ipv4/netfilter/arpt_mangle.c | 7 ++----- net/ipv4/netfilter/ip_tables.c | 24 ++++++++++-------------- net/ipv4/netfilter/ipt_CLUSTERIP.c | 6 ++---- net/ipv4/netfilter/ipt_ECN.c | 6 ++---- net/ipv4/netfilter/ipt_LOG.c | 8 +++----- net/ipv4/netfilter/ipt_MASQUERADE.c | 14 ++++++-------- net/ipv4/netfilter/ipt_NETMAP.c | 17 ++++++++--------- net/ipv4/netfilter/ipt_REDIRECT.c | 12 +++++------- net/ipv4/netfilter/ipt_REJECT.c | 8 +++----- net/ipv4/netfilter/ipt_TTL.c | 6 ++---- net/ipv4/netfilter/ipt_ULOG.c | 10 +++------- net/ipv4/netfilter/nf_nat_rule.c | 32 ++++++++++++-------------------- net/ipv6/netfilter/ip6_tables.c | 24 +++++++++++------------- net/ipv6/netfilter/ip6t_HL.c | 6 ++---- net/ipv6/netfilter/ip6t_LOG.c | 8 +++----- net/ipv6/netfilter/ip6t_REJECT.c | 18 ++++++++---------- net/netfilter/xt_CLASSIFY.c | 6 ++---- net/netfilter/xt_CONNMARK.c | 12 ++++-------- net/netfilter/xt_CONNSECMARK.c | 6 ++---- net/netfilter/xt_DSCP.c | 30 ++++++++++-------------------- net/netfilter/xt_MARK.c | 18 ++++++------------ net/netfilter/xt_NFLOG.c | 10 ++++------ net/netfilter/xt_NFQUEUE.c | 6 ++---- net/netfilter/xt_NOTRACK.c | 4 +--- net/netfilter/xt_RATEEST.c | 9 ++------- net/netfilter/xt_SECMARK.c | 6 ++---- net/netfilter/xt_TCPMSS.c | 12 ++++-------- net/netfilter/xt_TCPOPTSTRIP.c | 12 ++++-------- net/netfilter/xt_TPROXY.c | 11 +++-------- net/netfilter/xt_TRACE.c | 4 +--- net/sched/act_ipt.c | 12 ++++++++---- 42 files changed, 209 insertions(+), 297 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index c79c88380149..46d0cb1ad340 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -218,6 +218,22 @@ struct xt_mtdtor_param { void *matchinfo; }; +/** + * struct xt_target_param - parameters for target extensions' target functions + * + * @hooknum: hook through which this target was invoked + * @target: struct xt_target through which this function was invoked + * @targinfo: per-target data + * + * Other fields see above. + */ +struct xt_target_param { + const struct net_device *in, *out; + unsigned int hooknum; + const struct xt_target *target; + const void *targinfo; +}; + struct xt_match { struct list_head list; @@ -269,11 +285,7 @@ struct xt_target must now handle non-linear skbs, using skb_copy_bits and skb_ip_make_writable. */ unsigned int (*target)(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo); + const struct xt_target_param *); /* Called when user tries to insert an entry of this type: hook_mask is a bitmask of hooks from which it can be diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index baf5510d044c..fc94699f719e 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -16,11 +16,9 @@ #include static unsigned int -ebt_arpreply_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hook_nr, - const struct xt_target *target, const void *data) +ebt_arpreply_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_arpreply_info *info = data; + const struct ebt_arpreply_info *info = par->targinfo; const __be32 *siptr, *diptr; __be32 _sip, _dip; const struct arphdr *ap; @@ -53,7 +51,7 @@ ebt_arpreply_tg(struct sk_buff *skb, const struct net_device *in, if (diptr == NULL) return EBT_DROP; - arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)in, + arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)par->in, *diptr, shp, info->mac, shp); return info->target; diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index cb80101e412c..bb5d79e0beea 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -15,11 +15,9 @@ #include static unsigned int -ebt_dnat_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hook_nr, - const struct xt_target *target, const void *data) +ebt_dnat_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_nat_info *info = data; + const struct ebt_nat_info *info = par->targinfo; if (!skb_make_writable(skb, 0)) return EBT_DROP; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index b40f9ed4c343..87de5fccb2f1 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -195,11 +195,9 @@ out: } static unsigned int -ebt_log_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknr, - const struct xt_target *target, const void *data) +ebt_log_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_log_info *info = data; + const struct ebt_log_info *info = par->targinfo; struct nf_loginfo li; li.type = NF_LOG_TYPE_LOG; @@ -207,11 +205,11 @@ ebt_log_tg(struct sk_buff *skb, const struct net_device *in, li.u.log.logflags = info->bitmask; if (info->bitmask & EBT_LOG_NFLOG) - nf_log_packet(NFPROTO_BRIDGE, hooknr, skb, in, out, &li, - "%s", info->prefix); + nf_log_packet(NFPROTO_BRIDGE, par->hooknum, skb, par->in, + par->out, &li, "%s", info->prefix); else - ebt_log_packet(NFPROTO_BRIDGE, hooknr, skb, in, out, &li, - info->prefix); + ebt_log_packet(NFPROTO_BRIDGE, par->hooknum, skb, par->in, + par->out, &li, info->prefix); return EBT_CONTINUE; } diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index dff19fc91cf5..aafc456c3c3b 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -19,11 +19,9 @@ #include static unsigned int -ebt_mark_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hook_nr, - const struct xt_target *target, const void *data) +ebt_mark_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_mark_t_info *info = data; + const struct ebt_mark_t_info *info = par->targinfo; int action = info->target & -16; if (action == MARK_SET_VALUE) diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 74b4fa0aabc1..6a28d994cf7d 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -20,11 +20,9 @@ #include static unsigned int -ebt_nflog_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknr, - const struct xt_target *target, const void *data) +ebt_nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_nflog_info *info = data; + const struct ebt_nflog_info *info = par->targinfo; struct nf_loginfo li; li.type = NF_LOG_TYPE_ULOG; @@ -32,7 +30,8 @@ ebt_nflog_tg(struct sk_buff *skb, const struct net_device *in, li.u.ulog.group = info->group; li.u.ulog.qthreshold = info->threshold; - nf_log_packet(PF_BRIDGE, hooknr, skb, in, out, &li, "%s", info->prefix); + nf_log_packet(PF_BRIDGE, par->hooknum, skb, par->in, par->out, + &li, "%s", info->prefix); return EBT_CONTINUE; } diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index a50ffbe0e4fb..0cfe2fad9404 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -16,20 +16,18 @@ #include static unsigned int -ebt_redirect_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknr, - const struct xt_target *target, const void *data) +ebt_redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_redirect_info *info = data; + const struct ebt_redirect_info *info = par->targinfo; if (!skb_make_writable(skb, 0)) return EBT_DROP; - if (hooknr != NF_BR_BROUTING) + if (par->hooknum != NF_BR_BROUTING) memcpy(eth_hdr(skb)->h_dest, - in->br_port->br->dev->dev_addr, ETH_ALEN); + par->in->br_port->br->dev->dev_addr, ETH_ALEN); else - memcpy(eth_hdr(skb)->h_dest, in->dev_addr, ETH_ALEN); + memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN); skb->pkt_type = PACKET_HOST; return info->target; } diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 8a55c7d49b55..f55960eee996 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -17,11 +17,9 @@ #include static unsigned int -ebt_snat_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hook_nr, - const struct xt_target *target, const void *data) +ebt_snat_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_nat_info *info = data; + const struct ebt_nat_info *info = par->targinfo; if (!skb_make_writable(skb, 0)) return EBT_DROP; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 25ca6467349e..bfedf12cbf41 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -247,13 +247,10 @@ static void ebt_log_packet(u_int8_t pf, unsigned int hooknum, } static unsigned int -ebt_ulog_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknr, - const struct xt_target *target, const void *data) +ebt_ulog_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ebt_ulog_info *uloginfo = data; - - ebt_ulog_packet(hooknr, skb, in, out, uloginfo, NULL); + ebt_ulog_packet(par->hooknum, skb, par->in, par->out, + par->targinfo, NULL); return EBT_CONTINUE; } diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 0320b5203624..a1156bab4a03 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -64,11 +64,13 @@ static struct xt_target ebt_standard_target = { .targetsize = sizeof(int), }; -static inline int ebt_do_watcher (struct ebt_entry_watcher *w, - struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, - const struct net_device *out) +static inline int +ebt_do_watcher(const struct ebt_entry_watcher *w, struct sk_buff *skb, + struct xt_target_param *par) { - w->u.watcher->target(skb, in, out, hooknr, w->u.watcher, w->data); + par->target = w->u.watcher; + par->targinfo = w->data; + w->u.watcher->target(skb, par); /* watchers don't give a verdict */ return 0; } @@ -156,10 +158,12 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, struct ebt_table_info *private; bool hotdrop = false; struct xt_match_param mtpar; + struct xt_target_param tgpar; - mtpar.in = in; - mtpar.out = out; + mtpar.in = tgpar.in = in; + mtpar.out = tgpar.out = out; mtpar.hotdrop = &hotdrop; + tgpar.hooknum = hook; read_lock_bh(&table->lock); private = table->private; @@ -193,17 +197,18 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, /* these should only watch: not modify, nor tell us what to do with the packet */ - EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, hook, in, - out); + EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &tgpar); t = (struct ebt_entry_target *) (((char *)point) + point->target_offset); /* standard target */ if (!t->u.target->target) verdict = ((struct ebt_standard_target *)t)->verdict; - else - verdict = t->u.target->target(skb, in, out, hook, - t->u.target, t->data); + else { + tgpar.target = t->u.target; + tgpar.targinfo = t->data; + verdict = t->u.target->target(skb, &tgpar); + } if (verdict == EBT_ACCEPT) { read_unlock_bh(&table->lock); return NF_ACCEPT; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index ae525a9afbec..5b631ad74b5f 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -200,15 +200,12 @@ static inline int arp_checkentry(const struct arpt_arp *arp) return 1; } -static unsigned int arpt_error(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +static unsigned int +arpt_error(struct sk_buff *skb, const struct xt_target_param *par) { if (net_ratelimit()) - printk("arp_tables: error: '%s'\n", (char *)targinfo); + printk("arp_tables: error: '%s'\n", + (const char *)par->targinfo); return NF_DROP; } @@ -232,6 +229,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, const char *indev, *outdev; void *table_base; const struct xt_table_info *private; + struct xt_target_param tgpar; if (!pskb_may_pull(skb, arp_hdr_len(skb->dev))) return NF_DROP; @@ -245,6 +243,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, e = get_entry(table_base, private->hook_entry[hook]); back = get_entry(table_base, private->underflow[hook]); + tgpar.in = in; + tgpar.out = out; + tgpar.hooknum = hook; + arp = arp_hdr(skb); do { if (arp_packet_match(arp, skb->dev, indev, outdev, &e->arp)) { @@ -290,11 +292,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, /* Targets which reenter must return * abs. verdicts */ + tgpar.target = t->u.kernel.target; + tgpar.targinfo = t->data; verdict = t->u.kernel.target->target(skb, - in, out, - hook, - t->u.kernel.target, - t->data); + &tgpar); /* Target might have changed stuff. */ arp = arp_hdr(skb); diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index 3f9e4ccd6168..0bf81b353694 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -9,12 +9,9 @@ MODULE_AUTHOR("Bart De Schuymer "); MODULE_DESCRIPTION("arptables arp payload mangle target"); static unsigned int -target(struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - unsigned int hooknum, const struct xt_target *target, - const void *targinfo) +target(struct sk_buff *skb, const struct xt_target_param *par) { - const struct arpt_mangle *mangle = targinfo; + const struct arpt_mangle *mangle = par->targinfo; const struct arphdr *arp; unsigned char *arpptr; int pln, hln; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 12ad4d5c55d6..0f8ecf390229 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -171,15 +171,11 @@ ip_checkentry(const struct ipt_ip *ip) } static unsigned int -ipt_error(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +ipt_error(struct sk_buff *skb, const struct xt_target_param *par) { if (net_ratelimit()) - printk("ip_tables: error: `%s'\n", (char *)targinfo); + printk("ip_tables: error: `%s'\n", + (const char *)par->targinfo); return NF_DROP; } @@ -334,6 +330,7 @@ ipt_do_table(struct sk_buff *skb, struct ipt_entry *e, *back; struct xt_table_info *private; struct xt_match_param mtpar; + struct xt_target_param tgpar; /* Initialization */ ip = ip_hdr(skb); @@ -349,8 +346,9 @@ ipt_do_table(struct sk_buff *skb, mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; mtpar.thoff = ip_hdrlen(skb); mtpar.hotdrop = &hotdrop; - mtpar.in = in; - mtpar.out = out; + mtpar.in = tgpar.in = in; + mtpar.out = tgpar.out = out; + tgpar.hooknum = hook; read_lock_bh(&table->lock); IP_NF_ASSERT(table->valid_hooks & (1 << hook)); @@ -414,16 +412,14 @@ ipt_do_table(struct sk_buff *skb, } else { /* Targets which reenter must return abs. verdicts */ + tgpar.target = t->u.kernel.target; + tgpar.targinfo = t->data; #ifdef CONFIG_NETFILTER_DEBUG ((struct ipt_entry *)table_base)->comefrom = 0xeeeeeeec; #endif verdict = t->u.kernel.target->target(skb, - in, out, - hook, - t->u.kernel.target, - t->data); - + &tgpar); #ifdef CONFIG_NETFILTER_DEBUG if (((struct ipt_entry *)table_base)->comefrom != 0xeeeeeeec diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 63faddc18a1c..67e8aa8f34f2 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -281,11 +281,9 @@ clusterip_responsible(const struct clusterip_config *config, u_int32_t hash) ***********************************************************************/ static unsigned int -clusterip_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +clusterip_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ipt_clusterip_tgt_info *cipinfo = targinfo; + const struct ipt_clusterip_tgt_info *cipinfo = par->targinfo; struct nf_conn *ct; enum ip_conntrack_info ctinfo; u_int32_t hash; diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index aee2364afffd..e37f181e8298 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -77,11 +77,9 @@ set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo) } static unsigned int -ecn_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +ecn_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ipt_ECN_info *einfo = targinfo; + const struct ipt_ECN_info *einfo = par->targinfo; if (einfo->operation & IPT_ECN_OP_SET_IP) if (!set_ect_ip(skb, einfo)) diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index 1c9785df4df7..e9942aed35ae 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -426,18 +426,16 @@ ipt_log_packet(u_int8_t pf, } static unsigned int -log_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +log_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ipt_log_info *loginfo = targinfo; + const struct ipt_log_info *loginfo = par->targinfo; struct nf_loginfo li; li.type = NF_LOG_TYPE_LOG; li.u.log.level = loginfo->level; li.u.log.logflags = loginfo->logflags; - ipt_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, &li, + ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, par->out, &li, loginfo->prefix); return XT_CONTINUE; } diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 65c811b27b7b..e0d9d49b79ee 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -50,9 +50,7 @@ masquerade_tg_check(const char *tablename, const void *e, } static unsigned int -masquerade_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +masquerade_tg(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; struct nf_conn_nat *nat; @@ -62,7 +60,7 @@ masquerade_tg(struct sk_buff *skb, const struct net_device *in, const struct rtable *rt; __be32 newsrc; - NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING); + NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); ct = nf_ct_get(skb, &ctinfo); nat = nfct_nat(ct); @@ -76,16 +74,16 @@ masquerade_tg(struct sk_buff *skb, const struct net_device *in, if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) return NF_ACCEPT; - mr = targinfo; + mr = par->targinfo; rt = skb->rtable; - newsrc = inet_select_addr(out, rt->rt_gateway, RT_SCOPE_UNIVERSE); + newsrc = inet_select_addr(par->out, rt->rt_gateway, RT_SCOPE_UNIVERSE); if (!newsrc) { - printk("MASQUERADE: %s ate my IP address\n", out->name); + printk("MASQUERADE: %s ate my IP address\n", par->out->name); return NF_DROP; } write_lock_bh(&masq_lock); - nat->masq_index = out->ifindex; + nat->masq_index = par->out->ifindex; write_unlock_bh(&masq_lock); /* Transfer from original range. */ diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index f281500bd7fa..cf18f23b3460 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -41,24 +41,23 @@ netmap_tg_check(const char *tablename, const void *e, } static unsigned int -netmap_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +netmap_tg(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; __be32 new_ip, netmask; - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; struct nf_nat_range newrange; - NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING - || hooknum == NF_INET_POST_ROUTING - || hooknum == NF_INET_LOCAL_OUT); + NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_POST_ROUTING || + par->hooknum == NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); - if (hooknum == NF_INET_PRE_ROUTING || hooknum == NF_INET_LOCAL_OUT) + if (par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_OUT) new_ip = ip_hdr(skb)->daddr & ~netmask; else new_ip = ip_hdr(skb)->saddr & ~netmask; @@ -70,7 +69,7 @@ netmap_tg(struct sk_buff *skb, const struct net_device *in, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(hooknum)); + return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); } static struct xt_target netmap_tg_reg __read_mostly = { diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index ef496105eae1..23adb09ddfb4 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -45,24 +45,22 @@ redirect_tg_check(const char *tablename, const void *e, } static unsigned int -redirect_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; __be32 newdst; - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; struct nf_nat_range newrange; - NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING - || hooknum == NF_INET_LOCAL_OUT); + NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); /* Local packets: make them go to loopback */ - if (hooknum == NF_INET_LOCAL_OUT) + if (par->hooknum == NF_INET_LOCAL_OUT) newdst = htonl(0x7F000001); else { struct in_device *indev; diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 9f5da0c2cae8..b36071bb1077 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -136,11 +136,9 @@ static inline void send_unreach(struct sk_buff *skb_in, int code) } static unsigned int -reject_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +reject_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ipt_reject_info *reject = targinfo; + const struct ipt_reject_info *reject = par->targinfo; /* WARNING: This code causes reentry within iptables. This means that the iptables jump stack is now crap. We @@ -168,7 +166,7 @@ reject_tg(struct sk_buff *skb, const struct net_device *in, send_unreach(skb, ICMP_PKT_FILTERED); break; case IPT_TCP_RESET: - send_reset(skb, hooknum); + send_reset(skb, par->hooknum); case IPT_ICMP_ECHOREPLY: /* Doesn't happen. */ break; diff --git a/net/ipv4/netfilter/ipt_TTL.c b/net/ipv4/netfilter/ipt_TTL.c index 7d01d424a71a..05cbfd2f7470 100644 --- a/net/ipv4/netfilter/ipt_TTL.c +++ b/net/ipv4/netfilter/ipt_TTL.c @@ -20,12 +20,10 @@ MODULE_DESCRIPTION("Xtables: IPv4 TTL field modification target"); MODULE_LICENSE("GPL"); static unsigned int -ttl_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +ttl_tg(struct sk_buff *skb, const struct xt_target_param *par) { struct iphdr *iph; - const struct ipt_TTL_info *info = targinfo; + const struct ipt_TTL_info *info = par->targinfo; int new_ttl; if (!skb_make_writable(skb, skb->len)) diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 9065e4a34fbc..46c0df0dc2dc 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -281,14 +281,10 @@ alloc_failure: } static unsigned int -ulog_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +ulog_tg(struct sk_buff *skb, const struct xt_target_param *par) { - struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo; - - ipt_ulog_packet(hooknum, skb, in, out, loginfo, NULL); - + ipt_ulog_packet(par->hooknum, skb, par->in, par->out, + par->targinfo, NULL); return XT_CONTINUE; } diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index f929352ec0ee..83170ff131f9 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -67,25 +67,21 @@ static struct xt_table nat_table = { }; /* Source NAT */ -static unsigned int ipt_snat_target(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +static unsigned int +ipt_snat_target(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; - NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING); + NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); ct = nf_ct_get(skb, &ctinfo); /* Connection must be valid and new. */ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); - NF_CT_ASSERT(out); + NF_CT_ASSERT(par->out != NULL); return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC); } @@ -109,28 +105,24 @@ static void warn_if_extra_mangle(struct net *net, __be32 dstip, __be32 srcip) ip_rt_put(rt); } -static unsigned int ipt_dnat_target(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +static unsigned int +ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; - NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING || - hooknum == NF_INET_LOCAL_OUT); + NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); /* Connection must be valid and new. */ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); - if (hooknum == NF_INET_LOCAL_OUT && + if (par->hooknum == NF_INET_LOCAL_OUT && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) - warn_if_extra_mangle(dev_net(out), ip_hdr(skb)->daddr, + warn_if_extra_mangle(dev_net(par->out), ip_hdr(skb)->daddr, mr->range[0].min_ip); return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 891358e89a2b..ee0986cdbd66 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -200,15 +200,11 @@ ip6_checkentry(const struct ip6t_ip6 *ipv6) } static unsigned int -ip6t_error(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +ip6t_error(struct sk_buff *skb, const struct xt_target_param *par) { if (net_ratelimit()) - printk("ip6_tables: error: `%s'\n", (char *)targinfo); + printk("ip6_tables: error: `%s'\n", + (const char *)par->targinfo); return NF_DROP; } @@ -360,6 +356,7 @@ ip6t_do_table(struct sk_buff *skb, struct ip6t_entry *e, *back; struct xt_table_info *private; struct xt_match_param mtpar; + struct xt_target_param tgpar; /* Initialization */ indev = in ? in->name : nulldevname; @@ -371,8 +368,9 @@ ip6t_do_table(struct sk_buff *skb, * rule is also a fragment-specific rule, non-fragments won't * match it. */ mtpar.hotdrop = &hotdrop; - mtpar.in = in; - mtpar.out = out; + mtpar.in = tgpar.in = in; + mtpar.out = tgpar.out = out; + tgpar.hooknum = hook; read_lock_bh(&table->lock); IP_NF_ASSERT(table->valid_hooks & (1 << hook)); @@ -438,15 +436,15 @@ ip6t_do_table(struct sk_buff *skb, } else { /* Targets which reenter must return abs. verdicts */ + tgpar.target = t->u.kernel.target; + tgpar.targinfo = t->data; + #ifdef CONFIG_NETFILTER_DEBUG ((struct ip6t_entry *)table_base)->comefrom = 0xeeeeeeec; #endif verdict = t->u.kernel.target->target(skb, - in, out, - hook, - t->u.kernel.target, - t->data); + &tgpar); #ifdef CONFIG_NETFILTER_DEBUG if (((struct ip6t_entry *)table_base)->comefrom diff --git a/net/ipv6/netfilter/ip6t_HL.c b/net/ipv6/netfilter/ip6t_HL.c index 7eebd3509166..ac759a54f2c6 100644 --- a/net/ipv6/netfilter/ip6t_HL.c +++ b/net/ipv6/netfilter/ip6t_HL.c @@ -19,12 +19,10 @@ MODULE_DESCRIPTION("Xtables: IPv6 Hop Limit field modification target"); MODULE_LICENSE("GPL"); static unsigned int -hl_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +hl_tg6(struct sk_buff *skb, const struct xt_target_param *par) { struct ipv6hdr *ip6h; - const struct ip6t_HL_info *info = targinfo; + const struct ip6t_HL_info *info = par->targinfo; int new_hl; if (!skb_make_writable(skb, skb->len)) diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index fd148f3d842f..a31d3ecd1fc9 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -438,18 +438,16 @@ ip6t_log_packet(u_int8_t pf, } static unsigned int -log_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +log_tg6(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ip6t_log_info *loginfo = targinfo; + const struct ip6t_log_info *loginfo = par->targinfo; struct nf_loginfo li; li.type = NF_LOG_TYPE_LOG; li.u.log.level = loginfo->level; li.u.log.logflags = loginfo->logflags; - ip6t_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, + ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, par->out, &li, loginfo->prefix); return XT_CONTINUE; } diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index f1a9fce1ec95..1d5f3a70ed09 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -173,12 +173,10 @@ send_unreach(struct net *net, struct sk_buff *skb_in, unsigned char code, } static unsigned int -reject_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +reject_tg6(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ip6t_reject_info *reject = targinfo; - struct net *net = dev_net(in ? in : out); + const struct ip6t_reject_info *reject = par->targinfo; + struct net *net = dev_net((par->in != NULL) ? par->in : par->out); pr_debug("%s: medium point\n", __func__); /* WARNING: This code causes reentry within ip6tables. @@ -186,19 +184,19 @@ reject_tg6(struct sk_buff *skb, const struct net_device *in, must return an absolute verdict. --RR */ switch (reject->with) { case IP6T_ICMP6_NO_ROUTE: - send_unreach(net, skb, ICMPV6_NOROUTE, hooknum); + send_unreach(net, skb, ICMPV6_NOROUTE, par->hooknum); break; case IP6T_ICMP6_ADM_PROHIBITED: - send_unreach(net, skb, ICMPV6_ADM_PROHIBITED, hooknum); + send_unreach(net, skb, ICMPV6_ADM_PROHIBITED, par->hooknum); break; case IP6T_ICMP6_NOT_NEIGHBOUR: - send_unreach(net, skb, ICMPV6_NOT_NEIGHBOUR, hooknum); + send_unreach(net, skb, ICMPV6_NOT_NEIGHBOUR, par->hooknum); break; case IP6T_ICMP6_ADDR_UNREACH: - send_unreach(net, skb, ICMPV6_ADDR_UNREACH, hooknum); + send_unreach(net, skb, ICMPV6_ADDR_UNREACH, par->hooknum); break; case IP6T_ICMP6_PORT_UNREACH: - send_unreach(net, skb, ICMPV6_PORT_UNREACH, hooknum); + send_unreach(net, skb, ICMPV6_PORT_UNREACH, par->hooknum); break; case IP6T_ICMP6_ECHOREPLY: /* Do nothing */ diff --git a/net/netfilter/xt_CLASSIFY.c b/net/netfilter/xt_CLASSIFY.c index 8cffa295dd37..011bc80dd2a1 100644 --- a/net/netfilter/xt_CLASSIFY.c +++ b/net/netfilter/xt_CLASSIFY.c @@ -27,11 +27,9 @@ MODULE_ALIAS("ipt_CLASSIFY"); MODULE_ALIAS("ip6t_CLASSIFY"); static unsigned int -classify_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +classify_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_classify_target_info *clinfo = targinfo; + const struct xt_classify_target_info *clinfo = par->targinfo; skb->priority = clinfo->priority; return XT_CONTINUE; diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c index e1415c3f5c91..95ed267328a7 100644 --- a/net/netfilter/xt_CONNMARK.c +++ b/net/netfilter/xt_CONNMARK.c @@ -36,11 +36,9 @@ MODULE_ALIAS("ip6t_CONNMARK"); #include static unsigned int -connmark_tg_v0(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +connmark_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_connmark_target_info *markinfo = targinfo; + const struct xt_connmark_target_info *markinfo = par->targinfo; struct nf_conn *ct; enum ip_conntrack_info ctinfo; u_int32_t diff; @@ -77,11 +75,9 @@ connmark_tg_v0(struct sk_buff *skb, const struct net_device *in, } static unsigned int -connmark_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_connmark_tginfo1 *info = targinfo; + const struct xt_connmark_tginfo1 *info = par->targinfo; enum ip_conntrack_info ctinfo; struct nf_conn *ct; u_int32_t newmark; diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 5f221c3bd35c..2211a2cef280 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -65,11 +65,9 @@ static void secmark_restore(struct sk_buff *skb) } static unsigned int -connsecmark_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +connsecmark_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_connsecmark_target_info *info = targinfo; + const struct xt_connsecmark_target_info *info = par->targinfo; switch (info->mode) { case CONNSECMARK_SAVE: diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index f0b4958528e0..c78e80afdf3d 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -29,11 +29,9 @@ MODULE_ALIAS("ipt_TOS"); MODULE_ALIAS("ip6t_TOS"); static unsigned int -dscp_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +dscp_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_DSCP_info *dinfo = targinfo; + const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; if (dscp != dinfo->dscp) { @@ -48,11 +46,9 @@ dscp_tg(struct sk_buff *skb, const struct net_device *in, } static unsigned int -dscp_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +dscp_tg6(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_DSCP_info *dinfo = targinfo; + const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; if (dscp != dinfo->dscp) { @@ -80,11 +76,9 @@ dscp_tg_check(const char *tablename, const void *e_void, } static unsigned int -tos_tg_v0(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tos_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) { - const struct ipt_tos_target_info *info = targinfo; + const struct ipt_tos_target_info *info = par->targinfo; struct iphdr *iph = ip_hdr(skb); u_int8_t oldtos; @@ -119,11 +113,9 @@ tos_tg_check_v0(const char *tablename, const void *e_void, } static unsigned int -tos_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tos_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_tos_target_info *info = targinfo; + const struct xt_tos_target_info *info = par->targinfo; struct iphdr *iph = ip_hdr(skb); u_int8_t orig, nv; @@ -141,11 +133,9 @@ tos_tg(struct sk_buff *skb, const struct net_device *in, } static unsigned int -tos_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tos_tg6(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_tos_target_info *info = targinfo; + const struct xt_tos_target_info *info = par->targinfo; struct ipv6hdr *iph = ipv6_hdr(skb); u_int8_t orig, nv; diff --git a/net/netfilter/xt_MARK.c b/net/netfilter/xt_MARK.c index c8ea7a809707..27d03f396117 100644 --- a/net/netfilter/xt_MARK.c +++ b/net/netfilter/xt_MARK.c @@ -25,22 +25,18 @@ MODULE_ALIAS("ipt_MARK"); MODULE_ALIAS("ip6t_MARK"); static unsigned int -mark_tg_v0(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +mark_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_mark_target_info *markinfo = targinfo; + const struct xt_mark_target_info *markinfo = par->targinfo; skb->mark = markinfo->mark; return XT_CONTINUE; } static unsigned int -mark_tg_v1(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +mark_tg_v1(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_mark_target_info_v1 *markinfo = targinfo; + const struct xt_mark_target_info_v1 *markinfo = par->targinfo; int mark = 0; switch (markinfo->mode) { @@ -62,11 +58,9 @@ mark_tg_v1(struct sk_buff *skb, const struct net_device *in, } static unsigned int -mark_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +mark_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_mark_tginfo2 *info = targinfo; + const struct xt_mark_tginfo2 *info = par->targinfo; skb->mark = (skb->mark & ~info->mask) ^ info->mark; return XT_CONTINUE; diff --git a/net/netfilter/xt_NFLOG.c b/net/netfilter/xt_NFLOG.c index 9b0955201762..3218ad63bd1d 100644 --- a/net/netfilter/xt_NFLOG.c +++ b/net/netfilter/xt_NFLOG.c @@ -21,11 +21,9 @@ MODULE_ALIAS("ipt_NFLOG"); MODULE_ALIAS("ip6t_NFLOG"); static unsigned int -nflog_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_nflog_info *info = targinfo; + const struct xt_nflog_info *info = par->targinfo; struct nf_loginfo li; li.type = NF_LOG_TYPE_ULOG; @@ -33,8 +31,8 @@ nflog_tg(struct sk_buff *skb, const struct net_device *in, li.u.ulog.group = info->group; li.u.ulog.qthreshold = info->threshold; - nf_log_packet(target->family, hooknum, skb, in, out, &li, - "%s", info->prefix); + nf_log_packet(par->target->family, par->hooknum, skb, par->in, + par->out, &li, "%s", info->prefix); return XT_CONTINUE; } diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index c03c2e8d06fd..2cc1fff49307 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -24,11 +24,9 @@ MODULE_ALIAS("ip6t_NFQUEUE"); MODULE_ALIAS("arpt_NFQUEUE"); static unsigned int -nfqueue_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_NFQ_info *tinfo = targinfo; + const struct xt_NFQ_info *tinfo = par->targinfo; return NF_QUEUE_NR(tinfo->queuenum); } diff --git a/net/netfilter/xt_NOTRACK.c b/net/netfilter/xt_NOTRACK.c index b9ee268b37c3..cc50295cd11e 100644 --- a/net/netfilter/xt_NOTRACK.c +++ b/net/netfilter/xt_NOTRACK.c @@ -13,9 +13,7 @@ MODULE_ALIAS("ipt_NOTRACK"); MODULE_ALIAS("ip6t_NOTRACK"); static unsigned int -notrack_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +notrack_tg(struct sk_buff *skb, const struct xt_target_param *par) { /* Previously seen (loopback)? Ignore. */ if (skb->nfct != NULL) diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index da7946e6ecb2..92e33524f784 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -71,14 +71,9 @@ void xt_rateest_put(struct xt_rateest *est) EXPORT_SYMBOL_GPL(xt_rateest_put); static unsigned int -xt_rateest_tg(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par) { - const struct xt_rateest_target_info *info = targinfo; + const struct xt_rateest_target_info *info = par->targinfo; struct gnet_stats_basic *stats = &info->est->bstats; spin_lock_bh(&info->est->lock); diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index 2a2ab8334817..ad05214e3809 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -29,12 +29,10 @@ MODULE_ALIAS("ip6t_SECMARK"); static u8 mode; static unsigned int -secmark_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +secmark_tg(struct sk_buff *skb, const struct xt_target_param *par) { u32 secmark = 0; - const struct xt_secmark_target_info *info = targinfo; + const struct xt_secmark_target_info *info = par->targinfo; BUG_ON(info->mode != mode); diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index b868f9952398..e08762d9b0ff 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -174,15 +174,13 @@ static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, } static unsigned int -tcpmss_tg4(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tcpmss_tg4(struct sk_buff *skb, const struct xt_target_param *par) { struct iphdr *iph = ip_hdr(skb); __be16 newlen; int ret; - ret = tcpmss_mangle_packet(skb, targinfo, + ret = tcpmss_mangle_packet(skb, par->targinfo, tcpmss_reverse_mtu(skb, PF_INET), iph->ihl * 4, sizeof(*iph) + sizeof(struct tcphdr)); @@ -199,9 +197,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct net_device *in, #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) static unsigned int -tcpmss_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tcpmss_tg6(struct sk_buff *skb, const struct xt_target_param *par) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); u8 nexthdr; @@ -212,7 +208,7 @@ tcpmss_tg6(struct sk_buff *skb, const struct net_device *in, tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); if (tcphoff < 0) return NF_DROP; - ret = tcpmss_mangle_packet(skb, targinfo, + ret = tcpmss_mangle_packet(skb, par->targinfo, tcpmss_reverse_mtu(skb, PF_INET6), tcphoff, sizeof(*ipv6h) + sizeof(struct tcphdr)); diff --git a/net/netfilter/xt_TCPOPTSTRIP.c b/net/netfilter/xt_TCPOPTSTRIP.c index 2e0ae6cc5d95..9dd8c8ef63eb 100644 --- a/net/netfilter/xt_TCPOPTSTRIP.c +++ b/net/netfilter/xt_TCPOPTSTRIP.c @@ -75,19 +75,15 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb, } static unsigned int -tcpoptstrip_tg4(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_target_param *par) { - return tcpoptstrip_mangle_packet(skb, targinfo, ip_hdrlen(skb), + return tcpoptstrip_mangle_packet(skb, par->targinfo, ip_hdrlen(skb), sizeof(struct iphdr) + sizeof(struct tcphdr)); } #if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) static unsigned int -tcpoptstrip_tg6(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_target_param *par) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); int tcphoff; @@ -98,7 +94,7 @@ tcpoptstrip_tg6(struct sk_buff *skb, const struct net_device *in, if (tcphoff < 0) return NF_DROP; - return tcpoptstrip_mangle_packet(skb, targinfo, tcphoff, + return tcpoptstrip_mangle_packet(skb, par->targinfo, tcphoff, sizeof(*ipv6h) + sizeof(struct tcphdr)); } #endif diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 183f251d2f06..f08c49ea4bdc 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -25,15 +25,10 @@ #include static unsigned int -tproxy_tg(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) +tproxy_tg(struct sk_buff *skb, const struct xt_target_param *par) { const struct iphdr *iph = ip_hdr(skb); - const struct xt_tproxy_target_info *tgi = targinfo; + const struct xt_tproxy_target_info *tgi = par->targinfo; struct udphdr _hdr, *hp; struct sock *sk; @@ -44,7 +39,7 @@ tproxy_tg(struct sk_buff *skb, sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, iph->saddr, tgi->laddr ? tgi->laddr : iph->daddr, hp->source, tgi->lport ? tgi->lport : hp->dest, - in, true); + par->in, true); /* NOTE: assign_sock consumes our sk reference */ if (sk && nf_tproxy_assign_sock(skb, sk)) { diff --git a/net/netfilter/xt_TRACE.c b/net/netfilter/xt_TRACE.c index da35f9f1cd7b..fbb04b86c46b 100644 --- a/net/netfilter/xt_TRACE.c +++ b/net/netfilter/xt_TRACE.c @@ -11,9 +11,7 @@ MODULE_ALIAS("ipt_TRACE"); MODULE_ALIAS("ip6t_TRACE"); static unsigned int -trace_tg(struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, unsigned int hooknum, - const struct xt_target *target, const void *targinfo) +trace_tg(struct sk_buff *skb, const struct xt_target_param *par) { skb->nf_trace = 1; return XT_CONTINUE; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 79ea19375caf..89791a56429a 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -188,6 +188,7 @@ static int tcf_ipt(struct sk_buff *skb, struct tc_action *a, { int ret = 0, result = 0; struct tcf_ipt *ipt = a->priv; + struct xt_target_param par; if (skb_cloned(skb)) { if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) @@ -203,10 +204,13 @@ static int tcf_ipt(struct sk_buff *skb, struct tc_action *a, /* yes, we have to worry about both in and out dev worry later - danger - this API seems to have changed from earlier kernels */ - ret = ipt->tcfi_t->u.kernel.target->target(skb, skb->dev, NULL, - ipt->tcfi_hook, - ipt->tcfi_t->u.kernel.target, - ipt->tcfi_t->data); + par.in = skb->dev; + par.out = NULL; + par.hooknum = ipt->tcfi_hook; + par.target = ipt->tcfi_t->u.kernel.target; + par.targinfo = ipt->tcfi_t->data; + ret = par.target->target(skb, &par); + switch (ret) { case NF_ACCEPT: result = TC_ACT_OK; -- cgit v1.2.3 From af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37f Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:19 +0200 Subject: netfilter: xtables: move extension arguments into compound structure (5/6) This patch does this for target extensions' checkentry functions. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 29 +++++++++++++++++++--------- include/linux/netfilter_bridge/ebtables.h | 4 ++-- net/bridge/netfilter/ebt_arpreply.c | 10 +++------- net/bridge/netfilter/ebt_dnat.c | 19 +++++++++--------- net/bridge/netfilter/ebt_log.c | 7 ++----- net/bridge/netfilter/ebt_mark.c | 8 ++------ net/bridge/netfilter/ebt_nflog.c | 7 ++----- net/bridge/netfilter/ebt_redirect.c | 17 ++++++++-------- net/bridge/netfilter/ebt_snat.c | 8 ++------ net/bridge/netfilter/ebt_ulog.c | 7 ++----- net/bridge/netfilter/ebtables.c | 28 +++++++++++++++------------ net/ipv4/netfilter/arp_tables.c | 20 ++++++++++--------- net/ipv4/netfilter/arpt_mangle.c | 6 ++---- net/ipv4/netfilter/ip_tables.c | 17 +++++++++------- net/ipv4/netfilter/ipt_CLUSTERIP.c | 13 +++++-------- net/ipv4/netfilter/ipt_ECN.c | 9 +++------ net/ipv4/netfilter/ipt_LOG.c | 7 ++----- net/ipv4/netfilter/ipt_MASQUERADE.c | 7 ++----- net/ipv4/netfilter/ipt_NETMAP.c | 7 ++----- net/ipv4/netfilter/ipt_REDIRECT.c | 7 ++----- net/ipv4/netfilter/ipt_REJECT.c | 9 +++------ net/ipv4/netfilter/ipt_TTL.c | 7 ++----- net/ipv4/netfilter/ipt_ULOG.c | 7 ++----- net/ipv4/netfilter/nf_nat_rule.c | 16 ++++------------ net/ipv6/netfilter/ip6_tables.c | 16 ++++++++++------ net/ipv6/netfilter/ip6t_HL.c | 7 ++----- net/ipv6/netfilter/ip6t_LOG.c | 7 ++----- net/ipv6/netfilter/ip6t_REJECT.c | 9 +++------ net/netfilter/x_tables.c | 32 +++++++++++++++---------------- net/netfilter/xt_CONNMARK.c | 24 +++++++++-------------- net/netfilter/xt_CONNSECMARK.c | 16 +++++++--------- net/netfilter/xt_DSCP.c | 19 +++++++----------- net/netfilter/xt_MARK.c | 14 ++++---------- net/netfilter/xt_NFLOG.c | 7 ++----- net/netfilter/xt_RATEEST.c | 9 ++------- net/netfilter/xt_SECMARK.c | 12 +++++------- net/netfilter/xt_TCPMSS.c | 22 ++++++++------------- net/netfilter/xt_TPROXY.c | 9 ++------- net/sched/act_ipt.c | 12 +++++++++--- 39 files changed, 208 insertions(+), 283 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 46d0cb1ad340..8daeb496ba7a 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -234,6 +234,23 @@ struct xt_target_param { const void *targinfo; }; +/** + * struct xt_tgchk_param - parameters for target extensions' + * checkentry functions + * + * @entryinfo: the family-specific rule data + * (struct ipt_entry, ip6t_entry, arpt_entry, ebt_entry) + * + * Other fields see above. + */ +struct xt_tgchk_param { + const char *table; + void *entryinfo; + const struct xt_target *target; + void *targinfo; + unsigned int hook_mask; +}; + struct xt_match { struct list_head list; @@ -291,11 +308,7 @@ struct xt_target hook_mask is a bitmask of hooks from which it can be called. */ /* Should return true or false. */ - bool (*checkentry)(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask); + bool (*checkentry)(const struct xt_tgchk_param *); /* Called when entry of this type deleted. */ void (*destroy)(const struct xt_target *target, void *targinfo); @@ -376,10 +389,8 @@ extern void xt_unregister_matches(struct xt_match *match, unsigned int n); extern int xt_check_match(struct xt_mtchk_param *, u_int8_t family, unsigned int size, u_int8_t proto, bool inv_proto); -extern int xt_check_target(const struct xt_target *target, unsigned short family, - unsigned int size, const char *table, unsigned int hook, - unsigned short proto, int inv_proto, - const void *entry, void *targinfo); +extern int xt_check_target(struct xt_tgchk_param *, u_int8_t family, + unsigned int size, u_int8_t proto, bool inv_proto); extern struct xt_table *xt_register_table(struct net *net, struct xt_table *table, diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index 568a690f6a62..d45e29cd1cfb 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -310,9 +310,9 @@ extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff *skb, #define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg)) /* True if the hook mask denotes that the rule is in a base chain, * used in the check() functions */ -#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS)) +#define BASE_CHAIN (par->hook_mask & (1 << NF_BR_NUMHOOKS)) /* Clear the bit in the hook mask that tells if the rule is on a base chain */ -#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS)) +#define CLEAR_BASE_CHAIN_BIT (par->hook_mask &= ~(1 << NF_BR_NUMHOOKS)) /* True if the target is not a standard target */ #define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0) diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index fc94699f719e..76584cd72e57 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -57,20 +57,16 @@ ebt_arpreply_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target; } -static bool -ebt_arpreply_tg_check(const char *tablename, const void *entry, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_arpreply_tg_check(const struct xt_tgchk_param *par) { - const struct ebt_arpreply_info *info = data; - const struct ebt_entry *e = entry; + const struct ebt_arpreply_info *info = par->targinfo; + const struct ebt_entry *e = par->entryinfo; if (BASE_CHAIN && info->target == EBT_RETURN) return false; if (e->ethproto != htons(ETH_P_ARP) || e->invflags & EBT_IPROTO) return false; - CLEAR_BASE_CHAIN_BIT; return true; } diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index bb5d79e0beea..6b49ea9e31fb 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -26,19 +26,20 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target; } -static bool -ebt_dnat_tg_check(const char *tablename, const void *entry, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_dnat_tg_check(const struct xt_tgchk_param *par) { - const struct ebt_nat_info *info = data; + const struct ebt_nat_info *info = par->targinfo; + unsigned int hook_mask; if (BASE_CHAIN && info->target == EBT_RETURN) return false; - CLEAR_BASE_CHAIN_BIT; - if ( (strcmp(tablename, "nat") || - (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) && - (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) + + hook_mask = par->hook_mask & ~(1 << NF_BR_NUMHOOKS); + if ((strcmp(par->table, "nat") != 0 || + (hook_mask & ~((1 << NF_BR_PRE_ROUTING) | + (1 << NF_BR_LOCAL_OUT)))) && + (strcmp(par->table, "broute") != 0 || + hook_mask & ~(1 << NF_BR_BROUTING))) return false; if (INVALID_TARGET) return false; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 87de5fccb2f1..3d33c608906a 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -24,12 +24,9 @@ static DEFINE_SPINLOCK(ebt_log_lock); -static bool -ebt_log_tg_check(const char *table, const void *entry, - const struct xt_target *target, void *data, - unsigned int hook_mask) +static bool ebt_log_tg_check(const struct xt_tgchk_param *par) { - struct ebt_log_info *info = data; + struct ebt_log_info *info = par->targinfo; if (info->bitmask & ~EBT_LOG_MASK) return false; diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index aafc456c3c3b..2fee7e8e2e93 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -36,18 +36,14 @@ ebt_mark_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target | ~EBT_VERDICT_BITS; } -static bool -ebt_mark_tg_check(const char *table, const void *e, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_mark_tg_check(const struct xt_tgchk_param *par) { - const struct ebt_mark_t_info *info = data; + const struct ebt_mark_t_info *info = par->targinfo; int tmp; tmp = info->target | ~EBT_VERDICT_BITS; if (BASE_CHAIN && tmp == EBT_RETURN) return false; - CLEAR_BASE_CHAIN_BIT; if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0) return false; tmp = info->target & ~EBT_VERDICT_BITS; diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 6a28d994cf7d..2a63d996dd4e 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -35,12 +35,9 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) return EBT_CONTINUE; } -static bool -ebt_nflog_tg_check(const char *table, const void *e, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_nflog_tg_check(const struct xt_tgchk_param *par) { - struct ebt_nflog_info *info = data; + struct ebt_nflog_info *info = par->targinfo; if (info->flags & ~EBT_NFLOG_MASK) return false; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 0cfe2fad9404..c8a49f7a57ba 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -32,18 +32,19 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target; } -static bool -ebt_redirect_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_redirect_tg_check(const struct xt_tgchk_param *par) { - const struct ebt_redirect_info *info = data; + const struct ebt_redirect_info *info = par->targinfo; + unsigned int hook_mask; if (BASE_CHAIN && info->target == EBT_RETURN) return false; - CLEAR_BASE_CHAIN_BIT; - if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) && - (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) + + hook_mask = par->hook_mask & ~(1 << NF_BR_NUMHOOKS); + if ((strcmp(par->table, "nat") != 0 || + hook_mask & ~(1 << NF_BR_PRE_ROUTING)) && + (strcmp(par->table, "broute") != 0 || + hook_mask & ~(1 << NF_BR_BROUTING))) return false; if (INVALID_TARGET) return false; diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index f55960eee996..8d04d4c302bd 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -42,18 +42,14 @@ out: return info->target | ~EBT_VERDICT_BITS; } -static bool -ebt_snat_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_snat_tg_check(const struct xt_tgchk_param *par) { - const struct ebt_nat_info *info = data; + const struct ebt_nat_info *info = par->targinfo; int tmp; tmp = info->target | ~EBT_VERDICT_BITS; if (BASE_CHAIN && tmp == EBT_RETURN) return false; - CLEAR_BASE_CHAIN_BIT; if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0) return false; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index bfedf12cbf41..2c6d6823e703 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -254,12 +254,9 @@ ebt_ulog_tg(struct sk_buff *skb, const struct xt_target_param *par) return EBT_CONTINUE; } -static bool -ebt_ulog_tg_check(const char *table, const void *entry, - const struct xt_target *target, void *data, - unsigned int hookmask) +static bool ebt_ulog_tg_check(const struct xt_tgchk_param *par) { - struct ebt_ulog_info *uloginfo = data; + struct ebt_ulog_info *uloginfo = par->targinfo; if (uloginfo->nlgroup > 31) return false; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index a1156bab4a03..cf823c21c166 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -363,9 +363,10 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, } static inline int -ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, - const char *name, unsigned int hookmask, unsigned int *cnt) +ebt_check_watcher(struct ebt_entry_watcher *w, struct xt_tgchk_param *par, + unsigned int *cnt) { + const struct ebt_entry *e = par->entryinfo; struct xt_target *watcher; size_t left = ((char *)e + e->target_offset) - (char *)w; int ret; @@ -383,9 +384,10 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, return -ENOENT; w->u.watcher = watcher; - ret = xt_check_target(watcher, NFPROTO_BRIDGE, w->watcher_size, - name, hookmask, e->ethproto, e->invflags & EBT_IPROTO, - e, w->data); + par->target = watcher; + par->targinfo = w->data; + ret = xt_check_target(par, NFPROTO_BRIDGE, w->watcher_size, + e->ethproto, e->invflags & EBT_IPROTO); if (ret < 0) { module_put(watcher->me); return ret; @@ -619,6 +621,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, size_t gap; int ret; struct xt_mtchk_param mtpar; + struct xt_tgchk_param tgpar; /* don't mess with the struct ebt_entries */ if (e->bitmask == 0) @@ -660,14 +663,14 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, } i = 0; - mtpar.table = name; - mtpar.entryinfo = e; - mtpar.hook_mask = hookmask; + mtpar.table = tgpar.table = name; + mtpar.entryinfo = tgpar.entryinfo = e; + mtpar.hook_mask = tgpar.hook_mask = hookmask; ret = EBT_MATCH_ITERATE(e, ebt_check_match, &mtpar, &i); if (ret != 0) goto cleanup_matches; j = 0; - ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j); + ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, &tgpar, &j); if (ret != 0) goto cleanup_watchers; t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); @@ -703,9 +706,10 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, goto cleanup_watchers; } - ret = xt_check_target(target, NFPROTO_BRIDGE, t->target_size, - name, hookmask, e->ethproto, e->invflags & EBT_IPROTO, - e, t->data); + tgpar.target = target; + tgpar.targinfo = t->data; + ret = xt_check_target(&tgpar, NFPROTO_BRIDGE, t->target_size, + e->ethproto, e->invflags & EBT_IPROTO); if (ret < 0) { module_put(target->me); goto cleanup_watchers; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 5b631ad74b5f..b3238d0101cc 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -457,16 +457,18 @@ static inline int check_entry(struct arpt_entry *e, const char *name) static inline int check_target(struct arpt_entry *e, const char *name) { - struct arpt_entry_target *t; - struct xt_target *target; + struct arpt_entry_target *t = arpt_get_target(e); int ret; - - t = arpt_get_target(e); - target = t->u.kernel.target; - - ret = xt_check_target(target, NFPROTO_ARP, - t->u.target_size - sizeof(*t), - name, e->comefrom, 0, 0, e, t->data); + struct xt_tgchk_param par = { + .table = name, + .entryinfo = e, + .target = t->u.kernel.target, + .targinfo = t->data, + .hook_mask = e->comefrom, + }; + + ret = xt_check_target(&par, NFPROTO_ARP, + t->u.target_size - sizeof(*t), 0, false); if (ret < 0) { duprintf("arp_tables: check failed for `%s'.\n", t->u.kernel.target->name); diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index 0bf81b353694..b0d5b1d0a769 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -54,11 +54,9 @@ target(struct sk_buff *skb, const struct xt_target_param *par) return mangle->target; } -static bool -checkentry(const char *tablename, const void *e, const struct xt_target *target, - void *targinfo, unsigned int hook_mask) +static bool checkentry(const struct xt_tgchk_param *par) { - const struct arpt_mangle *mangle = targinfo; + const struct arpt_mangle *mangle = par->targinfo; if (mangle->flags & ~ARPT_MANGLE_MASK || !(mangle->flags & ARPT_MANGLE_MASK)) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 0f8ecf390229..e592c54d4992 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -655,15 +655,18 @@ err: static int check_target(struct ipt_entry *e, const char *name) { - struct ipt_entry_target *t; - struct xt_target *target; + struct ipt_entry_target *t = ipt_get_target(e); + struct xt_tgchk_param par = { + .table = name, + .entryinfo = e, + .target = t->u.kernel.target, + .targinfo = t->data, + .hook_mask = e->comefrom, + }; int ret; - t = ipt_get_target(e); - target = t->u.kernel.target; - ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), - name, e->comefrom, e->ip.proto, - e->ip.invflags & IPT_INV_PROTO, e, t->data); + ret = xt_check_target(&par, NFPROTO_IPV4, t->u.target_size - sizeof(*t), + e->ip.proto, e->ip.invflags & IPT_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", t->u.kernel.target->name); diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 67e8aa8f34f2..6c7254e02561 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -347,13 +347,10 @@ clusterip_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -clusterip_tg_check(const char *tablename, const void *e_void, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool clusterip_tg_check(const struct xt_tgchk_param *par) { - struct ipt_clusterip_tgt_info *cipinfo = targinfo; - const struct ipt_entry *e = e_void; + struct ipt_clusterip_tgt_info *cipinfo = par->targinfo; + const struct ipt_entry *e = par->entryinfo; struct clusterip_config *config; @@ -404,9 +401,9 @@ clusterip_tg_check(const char *tablename, const void *e_void, } cipinfo->config = config; - if (nf_ct_l3proto_try_module_get(target->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->target->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", target->family); + "proto=%u\n", par->target->family); return false; } diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index e37f181e8298..f7e2fa0974dc 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -93,13 +93,10 @@ ecn_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -ecn_tg_check(const char *tablename, const void *e_void, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool ecn_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_ECN_info *einfo = targinfo; - const struct ipt_entry *e = e_void; + const struct ipt_ECN_info *einfo = par->targinfo; + const struct ipt_entry *e = par->entryinfo; if (einfo->operation & IPT_ECN_OP_MASK) { printk(KERN_WARNING "ECN: unsupported ECN operation %x\n", diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index e9942aed35ae..fc6ce04a3e35 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -440,12 +440,9 @@ log_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -log_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool log_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_log_info *loginfo = targinfo; + const struct ipt_log_info *loginfo = par->targinfo; if (loginfo->level >= 8) { pr_debug("LOG: level %u >= 8\n", loginfo->level); diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index e0d9d49b79ee..f389f60cb105 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -31,12 +31,9 @@ MODULE_DESCRIPTION("Xtables: automatic-address SNAT"); static DEFINE_RWLOCK(masq_lock); /* FIXME: Multiple targets. --RR */ -static bool -masquerade_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool masquerade_tg_check(const struct xt_tgchk_param *par) { - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { pr_debug("masquerade_check: bad MAP_IPS.\n"); diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index cf18f23b3460..7c29582d4ec8 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -22,12 +22,9 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Svenning Soerensen "); MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of IPv4 subnets"); -static bool -netmap_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool netmap_tg_check(const struct xt_tgchk_param *par) { - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { pr_debug("NETMAP:check: bad MAP_IPS.\n"); diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index 23adb09ddfb4..698e5e78685b 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -26,12 +26,9 @@ MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Xtables: Connection redirection to localhost"); /* FIXME: Take multiple ranges --RR */ -static bool -redirect_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool redirect_tg_check(const struct xt_tgchk_param *par) { - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { pr_debug("redirect_check: bad MAP_IPS.\n"); diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index b36071bb1077..0b4b6e0ff2b9 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -175,13 +175,10 @@ reject_tg(struct sk_buff *skb, const struct xt_target_param *par) return NF_DROP; } -static bool -reject_tg_check(const char *tablename, const void *e_void, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool reject_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_reject_info *rejinfo = targinfo; - const struct ipt_entry *e = e_void; + const struct ipt_reject_info *rejinfo = par->targinfo; + const struct ipt_entry *e = par->entryinfo; if (rejinfo->with == IPT_ICMP_ECHOREPLY) { printk("ipt_REJECT: ECHOREPLY no longer supported.\n"); diff --git a/net/ipv4/netfilter/ipt_TTL.c b/net/ipv4/netfilter/ipt_TTL.c index 05cbfd2f7470..6d76aae90cc0 100644 --- a/net/ipv4/netfilter/ipt_TTL.c +++ b/net/ipv4/netfilter/ipt_TTL.c @@ -59,12 +59,9 @@ ttl_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -ttl_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool ttl_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_TTL_info *info = targinfo; + const struct ipt_TTL_info *info = par->targinfo; if (info->mode > IPT_TTL_MAXMODE) { printk(KERN_WARNING "ipt_TTL: invalid or unknown Mode %u\n", diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 46c0df0dc2dc..18a2826b57c6 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -313,12 +313,9 @@ static void ipt_logfn(u_int8_t pf, ipt_ulog_packet(hooknum, skb, in, out, &loginfo, prefix); } -static bool -ulog_tg_check(const char *tablename, const void *e, - const struct xt_target *target, void *targinfo, - unsigned int hookmask) +static bool ulog_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_ulog_info *loginfo = targinfo; + const struct ipt_ulog_info *loginfo = par->targinfo; if (loginfo->prefix[sizeof(loginfo->prefix) - 1] != '\0') { pr_debug("ipt_ULOG: prefix term %i\n", diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 83170ff131f9..bea54a685109 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -128,13 +128,9 @@ ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par) return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST); } -static bool ipt_snat_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) +static bool ipt_snat_checkentry(const struct xt_tgchk_param *par) { - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; /* Must be a valid range */ if (mr->rangesize != 1) { @@ -144,13 +140,9 @@ static bool ipt_snat_checkentry(const char *tablename, return true; } -static bool ipt_dnat_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) +static bool ipt_dnat_checkentry(const struct xt_tgchk_param *par) { - const struct nf_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = par->targinfo; /* Must be a valid range */ if (mr->rangesize != 1) { diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index ee0986cdbd66..ca14fb8bd362 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -679,15 +679,19 @@ err: static int check_target(struct ip6t_entry *e, const char *name) { - struct ip6t_entry_target *t; - struct xt_target *target; + struct ip6t_entry_target *t = ip6t_get_target(e); + struct xt_tgchk_param par = { + .table = name, + .entryinfo = e, + .target = t->u.kernel.target, + .targinfo = t->data, + .hook_mask = e->comefrom, + }; int ret; t = ip6t_get_target(e); - target = t->u.kernel.target; - ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t), - name, e->comefrom, e->ipv6.proto, - e->ipv6.invflags & IP6T_INV_PROTO, e, t->data); + ret = xt_check_target(&par, NFPROTO_IPV6, t->u.target_size - sizeof(*t), + e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", t->u.kernel.target->name); diff --git a/net/ipv6/netfilter/ip6t_HL.c b/net/ipv6/netfilter/ip6t_HL.c index ac759a54f2c6..27b5adf670a2 100644 --- a/net/ipv6/netfilter/ip6t_HL.c +++ b/net/ipv6/netfilter/ip6t_HL.c @@ -54,12 +54,9 @@ hl_tg6(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -hl_tg6_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool hl_tg6_check(const struct xt_tgchk_param *par) { - const struct ip6t_HL_info *info = targinfo; + const struct ip6t_HL_info *info = par->targinfo; if (info->mode > IP6T_HL_MAXMODE) { printk(KERN_WARNING "ip6t_HL: invalid or unknown Mode %u\n", diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index a31d3ecd1fc9..caa441d09567 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -453,12 +453,9 @@ log_tg6(struct sk_buff *skb, const struct xt_target_param *par) } -static bool -log_tg6_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool log_tg6_check(const struct xt_tgchk_param *par) { - const struct ip6t_log_info *loginfo = targinfo; + const struct ip6t_log_info *loginfo = par->targinfo; if (loginfo->level >= 8) { pr_debug("LOG: level %u >= 8\n", loginfo->level); diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index 1d5f3a70ed09..0981b4ccb8b1 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -213,13 +213,10 @@ reject_tg6(struct sk_buff *skb, const struct xt_target_param *par) return NF_DROP; } -static bool -reject_tg6_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool reject_tg6_check(const struct xt_tgchk_param *par) { - const struct ip6t_reject_info *rejinfo = targinfo; - const struct ip6t_entry *e = entry; + const struct ip6t_reject_info *rejinfo = par->targinfo; + const struct ip6t_entry *e = par->entryinfo; if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) { printk("ip6t_REJECT: ECHOREPLY is not supported.\n"); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 817ab14f7cd6..f29513cd1399 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -471,35 +471,35 @@ int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr, EXPORT_SYMBOL_GPL(xt_compat_match_to_user); #endif /* CONFIG_COMPAT */ -int xt_check_target(const struct xt_target *target, unsigned short family, - unsigned int size, const char *table, unsigned int hook_mask, - unsigned short proto, int inv_proto, const void *entry, - void *targinfo) +int xt_check_target(struct xt_tgchk_param *par, u_int8_t family, + unsigned int size, u_int8_t proto, bool inv_proto) { - if (XT_ALIGN(target->targetsize) != size) { + if (XT_ALIGN(par->target->targetsize) != size) { printk("%s_tables: %s target: invalid size %Zu != %u\n", - xt_prefix[family], target->name, - XT_ALIGN(target->targetsize), size); + xt_prefix[family], par->target->name, + XT_ALIGN(par->target->targetsize), size); return -EINVAL; } - if (target->table && strcmp(target->table, table)) { + if (par->target->table != NULL && + strcmp(par->target->table, par->table) != 0) { printk("%s_tables: %s target: only valid in %s table, not %s\n", - xt_prefix[family], target->name, target->table, table); + xt_prefix[family], par->target->name, + par->target->table, par->table); return -EINVAL; } - if (target->hooks && (hook_mask & ~target->hooks) != 0) { + if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) { printk("%s_tables: %s target: bad hook_mask %#x/%#x\n", - xt_prefix[family], target->name, hook_mask, - target->hooks); + xt_prefix[family], par->target->name, par->hook_mask, + par->target->hooks); return -EINVAL; } - if (target->proto && (target->proto != proto || inv_proto)) { + if (par->target->proto && (par->target->proto != proto || inv_proto)) { printk("%s_tables: %s target: only valid for protocol %u\n", - xt_prefix[family], target->name, target->proto); + xt_prefix[family], par->target->name, + par->target->proto); return -EINVAL; } - if (target->checkentry != NULL && - !target->checkentry(table, entry, target, targinfo, hook_mask)) + if (par->target->checkentry != NULL && !par->target->checkentry(par)) return -EINVAL; return 0; } diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c index 95ed267328a7..8fc9f35e67df 100644 --- a/net/netfilter/xt_CONNMARK.c +++ b/net/netfilter/xt_CONNMARK.c @@ -112,18 +112,15 @@ connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -connmark_tg_check_v0(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool connmark_tg_check_v0(const struct xt_tgchk_param *par) { - const struct xt_connmark_target_info *matchinfo = targinfo; + const struct xt_connmark_target_info *matchinfo = par->targinfo; if (matchinfo->mode == XT_CONNMARK_RESTORE) { - if (strcmp(tablename, "mangle") != 0) { + if (strcmp(par->table, "mangle") != 0) { printk(KERN_WARNING "CONNMARK: restore can only be " "called from \"mangle\" table, not \"%s\"\n", - tablename); + par->table); return false; } } @@ -131,22 +128,19 @@ connmark_tg_check_v0(const char *tablename, const void *entry, printk(KERN_WARNING "CONNMARK: Only supports 32bit mark\n"); return false; } - if (nf_ct_l3proto_try_module_get(target->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->target->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", target->family); + "proto=%u\n", par->target->family); return false; } return true; } -static bool -connmark_tg_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool connmark_tg_check(const struct xt_tgchk_param *par) { - if (nf_ct_l3proto_try_module_get(target->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->target->family) < 0) { printk(KERN_WARNING "cannot load conntrack support for " - "proto=%u\n", target->family); + "proto=%u\n", par->target->family); return false; } return true; diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 2211a2cef280..2041a3d4b4d8 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -85,16 +85,14 @@ connsecmark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -connsecmark_tg_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool connsecmark_tg_check(const struct xt_tgchk_param *par) { - const struct xt_connsecmark_target_info *info = targinfo; + const struct xt_connsecmark_target_info *info = par->targinfo; - if (strcmp(tablename, "mangle") && strcmp(tablename, "security")) { + if (strcmp(par->table, "mangle") != 0 && + strcmp(par->table, "security") != 0) { printk(KERN_INFO PFX "target only valid in the \'mangle\' " - "or \'security\' tables, not \'%s\'.\n", tablename); + "or \'security\' tables, not \'%s\'.\n", par->table); return false; } @@ -108,9 +106,9 @@ connsecmark_tg_check(const char *tablename, const void *entry, return false; } - if (nf_ct_l3proto_try_module_get(target->family) < 0) { + if (nf_ct_l3proto_try_module_get(par->target->family) < 0) { printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", target->family); + "proto=%u\n", par->target->family); return false; } return true; diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index c78e80afdf3d..6a347e768f86 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -61,15 +61,12 @@ dscp_tg6(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -dscp_tg_check(const char *tablename, const void *e_void, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool dscp_tg_check(const struct xt_tgchk_param *par) { - const u_int8_t dscp = ((struct xt_DSCP_info *)targinfo)->dscp; + const struct xt_DSCP_info *info = par->targinfo; - if (dscp > XT_DSCP_MAX) { - printk(KERN_WARNING "DSCP: dscp %x out of range\n", dscp); + if (info->dscp > XT_DSCP_MAX) { + printk(KERN_WARNING "DSCP: dscp %x out of range\n", info->dscp); return false; } return true; @@ -95,12 +92,10 @@ tos_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -tos_tg_check_v0(const char *tablename, const void *e_void, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool tos_tg_check_v0(const struct xt_tgchk_param *par) { - const u_int8_t tos = ((struct ipt_tos_target_info *)targinfo)->tos; + const struct ipt_tos_target_info *info = par->targinfo; + const uint8_t tos = info->tos; if (tos != IPTOS_LOWDELAY && tos != IPTOS_THROUGHPUT && tos != IPTOS_RELIABILITY && tos != IPTOS_MINCOST && diff --git a/net/netfilter/xt_MARK.c b/net/netfilter/xt_MARK.c index 27d03f396117..123ee0ba78c6 100644 --- a/net/netfilter/xt_MARK.c +++ b/net/netfilter/xt_MARK.c @@ -66,12 +66,9 @@ mark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -mark_tg_check_v0(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool mark_tg_check_v0(const struct xt_tgchk_param *par) { - const struct xt_mark_target_info *markinfo = targinfo; + const struct xt_mark_target_info *markinfo = par->targinfo; if (markinfo->mark > 0xffffffff) { printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n"); @@ -80,12 +77,9 @@ mark_tg_check_v0(const char *tablename, const void *entry, return true; } -static bool -mark_tg_check_v1(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool mark_tg_check_v1(const struct xt_tgchk_param *par) { - const struct xt_mark_target_info_v1 *markinfo = targinfo; + const struct xt_mark_target_info_v1 *markinfo = par->targinfo; if (markinfo->mode != XT_MARK_SET && markinfo->mode != XT_MARK_AND diff --git a/net/netfilter/xt_NFLOG.c b/net/netfilter/xt_NFLOG.c index 3218ad63bd1d..56ee4f118b59 100644 --- a/net/netfilter/xt_NFLOG.c +++ b/net/netfilter/xt_NFLOG.c @@ -36,12 +36,9 @@ nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -nflog_tg_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targetinfo, - unsigned int hookmask) +static bool nflog_tg_check(const struct xt_tgchk_param *par) { - const struct xt_nflog_info *info = targetinfo; + const struct xt_nflog_info *info = par->targinfo; if (info->flags & ~XT_NFLOG_MASK) return false; diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index 92e33524f784..edf4ab1f30ff 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -84,14 +84,9 @@ xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool -xt_rateest_tg_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) +static bool xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) { - struct xt_rateest_target_info *info = targinfo; + struct xt_rateest_target_info *info = par->targinfo; struct xt_rateest *est; struct { struct nlattr opt; diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index ad05214e3809..e5777227192c 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -80,16 +80,14 @@ static bool checkentry_selinux(struct xt_secmark_target_info *info) return true; } -static bool -secmark_tg_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool secmark_tg_check(const struct xt_tgchk_param *par) { - struct xt_secmark_target_info *info = targinfo; + struct xt_secmark_target_info *info = par->targinfo; - if (strcmp(tablename, "mangle") && strcmp(tablename, "security")) { + if (strcmp(par->table, "mangle") != 0 && + strcmp(par->table, "security") != 0) { printk(KERN_INFO PFX "target only valid in the \'mangle\' " - "or \'security\' tables, not \'%s\'.\n", tablename); + "or \'security\' tables, not \'%s\'.\n", par->table); return false; } diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index e08762d9b0ff..4f3b1f808795 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -237,16 +237,13 @@ static inline bool find_syn_match(const struct xt_entry_match *m) return false; } -static bool -tcpmss_tg4_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool tcpmss_tg4_check(const struct xt_tgchk_param *par) { - const struct xt_tcpmss_info *info = targinfo; - const struct ipt_entry *e = entry; + const struct xt_tcpmss_info *info = par->targinfo; + const struct ipt_entry *e = par->entryinfo; if (info->mss == XT_TCPMSS_CLAMP_PMTU && - (hook_mask & ~((1 << NF_INET_FORWARD) | + (par->hook_mask & ~((1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_POST_ROUTING))) != 0) { printk("xt_TCPMSS: path-MTU clamping only supported in " @@ -260,16 +257,13 @@ tcpmss_tg4_check(const char *tablename, const void *entry, } #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) -static bool -tcpmss_tg6_check(const char *tablename, const void *entry, - const struct xt_target *target, void *targinfo, - unsigned int hook_mask) +static bool tcpmss_tg6_check(const struct xt_tgchk_param *par) { - const struct xt_tcpmss_info *info = targinfo; - const struct ip6t_entry *e = entry; + const struct xt_tcpmss_info *info = par->targinfo; + const struct ip6t_entry *e = par->entryinfo; if (info->mss == XT_TCPMSS_CLAMP_PMTU && - (hook_mask & ~((1 << NF_INET_FORWARD) | + (par->hook_mask & ~((1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_POST_ROUTING))) != 0) { printk("xt_TCPMSS: path-MTU clamping only supported in " diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index f08c49ea4bdc..1340c2fa3621 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -59,14 +59,9 @@ tproxy_tg(struct sk_buff *skb, const struct xt_target_param *par) return NF_DROP; } -static bool -tproxy_tg_check(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targetinfo, - unsigned int hook_mask) +static bool tproxy_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_ip *i = entry; + const struct ipt_ip *i = par->entryinfo; if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) && !(i->invflags & IPT_INV_PROTO)) diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 89791a56429a..a54dc3f8234f 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -40,6 +40,7 @@ static struct tcf_hashinfo ipt_hash_info = { static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook) { + struct xt_tgchk_param par; struct xt_target *target; int ret = 0; @@ -49,9 +50,14 @@ static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int return -ENOENT; t->u.kernel.target = target; - - ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), - table, hook, 0, 0, NULL, t->data); + par.table = table; + par.entryinfo = NULL; + par.target = target; + par.targinfo = t->data; + par.hook_mask = hook; + + ret = xt_check_target(&par, NFPROTO_IPV4, + t->u.target_size - sizeof(*t), 0, false); if (ret < 0) { module_put(t->u.kernel.target->me); return ret; -- cgit v1.2.3 From a2df1648ba615dd5908e9a1fa7b2f133fa302487 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:19 +0200 Subject: netfilter: xtables: move extension arguments into compound structure (6/6) This patch does this for target extensions' destroy functions. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 8 +++++++- net/bridge/netfilter/ebtables.c | 19 +++++++++++++------ net/ipv4/netfilter/arp_tables.c | 9 ++++++--- net/ipv4/netfilter/ip_tables.c | 10 +++++++--- net/ipv4/netfilter/ipt_CLUSTERIP.c | 6 +++--- net/ipv6/netfilter/ip6_tables.c | 10 +++++++--- net/netfilter/xt_CONNMARK.c | 5 ++--- net/netfilter/xt_CONNSECMARK.c | 5 ++--- net/netfilter/xt_RATEEST.c | 5 ++--- net/netfilter/xt_SECMARK.c | 2 +- net/sched/act_ipt.c | 10 +++++++--- 11 files changed, 57 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 8daeb496ba7a..e3b3b669a143 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -251,6 +251,12 @@ struct xt_tgchk_param { unsigned int hook_mask; }; +/* Target destructor parameters */ +struct xt_tgdtor_param { + const struct xt_target *target; + void *targinfo; +}; + struct xt_match { struct list_head list; @@ -311,7 +317,7 @@ struct xt_target bool (*checkentry)(const struct xt_tgchk_param *); /* Called when entry of this type deleted. */ - void (*destroy)(const struct xt_target *target, void *targinfo); + void (*destroy)(const struct xt_tgdtor_param *); /* Called when userspace align differs from kernel space one */ void (*compat_from_user)(void *dst, void *src); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index cf823c21c166..29d8061fa153 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -581,18 +581,23 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i) static inline int ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i) { + struct xt_tgdtor_param par; + if (i && (*i)-- == 0) return 1; - if (w->u.watcher->destroy) - w->u.watcher->destroy(w->u.watcher, w->data); - module_put(w->u.watcher->me); + par.target = w->u.watcher; + par.targinfo = w->data; + if (par.target->destroy != NULL) + par.target->destroy(&par); + module_put(par.target->me); return 0; } static inline int ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) { + struct xt_tgdtor_param par; struct ebt_entry_target *t; if (e->bitmask == 0) @@ -603,10 +608,12 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL); EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL); t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); - if (t->u.target->destroy) - t->u.target->destroy(t->u.target, t->data); - module_put(t->u.target->me); + par.target = t->u.target; + par.targinfo = t->data; + if (par.target->destroy != NULL) + par.target->destroy(&par); + module_put(par.target->me); return 0; } diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index b3238d0101cc..3bab78330cf8 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -557,15 +557,18 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, static inline int cleanup_entry(struct arpt_entry *e, unsigned int *i) { + struct xt_tgdtor_param par; struct arpt_entry_target *t; if (i && (*i)-- == 0) return 1; t = arpt_get_target(e); - if (t->u.kernel.target->destroy) - t->u.kernel.target->destroy(t->u.kernel.target, t->data); - module_put(t->u.kernel.target->me); + par.target = t->u.kernel.target; + par.targinfo = t->data; + if (par.target->destroy != NULL) + par.target->destroy(&par); + module_put(par.target->me); return 0; } diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index e592c54d4992..50b9a6c34c38 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -768,6 +768,7 @@ check_entry_size_and_hooks(struct ipt_entry *e, static int cleanup_entry(struct ipt_entry *e, unsigned int *i) { + struct xt_tgdtor_param par; struct ipt_entry_target *t; if (i && (*i)-- == 0) @@ -776,9 +777,12 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i) /* Cleanup all matches */ IPT_MATCH_ITERATE(e, cleanup_match, NULL); t = ipt_get_target(e); - if (t->u.kernel.target->destroy) - t->u.kernel.target->destroy(t->u.kernel.target, t->data); - module_put(t->u.kernel.target->me); + + par.target = t->u.kernel.target; + par.targinfo = t->data; + if (par.target->destroy != NULL) + par.target->destroy(&par); + module_put(par.target->me); return 0; } diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 6c7254e02561..7ac1677419a9 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -411,9 +411,9 @@ static bool clusterip_tg_check(const struct xt_tgchk_param *par) } /* drop reference count of cluster config when rule is deleted */ -static void clusterip_tg_destroy(const struct xt_target *target, void *targinfo) +static void clusterip_tg_destroy(const struct xt_tgdtor_param *par) { - const struct ipt_clusterip_tgt_info *cipinfo = targinfo; + const struct ipt_clusterip_tgt_info *cipinfo = par->targinfo; /* if no more entries are referencing the config, remove it * from the list and destroy the proc entry */ @@ -421,7 +421,7 @@ static void clusterip_tg_destroy(const struct xt_target *target, void *targinfo) clusterip_config_put(cipinfo->config); - nf_ct_l3proto_module_put(target->family); + nf_ct_l3proto_module_put(par->target->family); } #ifdef CONFIG_COMPAT diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index ca14fb8bd362..d934a6994632 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -793,6 +793,7 @@ check_entry_size_and_hooks(struct ip6t_entry *e, static int cleanup_entry(struct ip6t_entry *e, unsigned int *i) { + struct xt_tgdtor_param par; struct ip6t_entry_target *t; if (i && (*i)-- == 0) @@ -801,9 +802,12 @@ cleanup_entry(struct ip6t_entry *e, unsigned int *i) /* Cleanup all matches */ IP6T_MATCH_ITERATE(e, cleanup_match, NULL); t = ip6t_get_target(e); - if (t->u.kernel.target->destroy) - t->u.kernel.target->destroy(t->u.kernel.target, t->data); - module_put(t->u.kernel.target->me); + + par.target = t->u.kernel.target; + par.targinfo = t->data; + if (par.target->destroy != NULL) + par.target->destroy(&par); + module_put(par.target->me); return 0; } diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c index 8fc9f35e67df..c5a5072e005d 100644 --- a/net/netfilter/xt_CONNMARK.c +++ b/net/netfilter/xt_CONNMARK.c @@ -146,10 +146,9 @@ static bool connmark_tg_check(const struct xt_tgchk_param *par) return true; } -static void -connmark_tg_destroy(const struct xt_target *target, void *targinfo) +static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { - nf_ct_l3proto_module_put(target->family); + nf_ct_l3proto_module_put(par->target->family); } #ifdef CONFIG_COMPAT diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 2041a3d4b4d8..b6e3f3f125fd 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -114,10 +114,9 @@ static bool connsecmark_tg_check(const struct xt_tgchk_param *par) return true; } -static void -connsecmark_tg_destroy(const struct xt_target *target, void *targinfo) +static void connsecmark_tg_destroy(const struct xt_tgdtor_param *par) { - nf_ct_l3proto_module_put(target->family); + nf_ct_l3proto_module_put(par->target->family); } static struct xt_target connsecmark_tg_reg[] __read_mostly = { diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index edf4ab1f30ff..43f5676b1af4 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -139,10 +139,9 @@ err1: return false; } -static void xt_rateest_tg_destroy(const struct xt_target *target, - void *targinfo) +static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) { - struct xt_rateest_target_info *info = targinfo; + struct xt_rateest_target_info *info = par->targinfo; xt_rateest_put(info->est); } diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index e5777227192c..7a6f9e6f5dfa 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -113,7 +113,7 @@ static bool secmark_tg_check(const struct xt_tgchk_param *par) return true; } -static void secmark_tg_destroy(const struct xt_target *target, void *targinfo) +static void secmark_tg_destroy(const struct xt_tgdtor_param *par) { switch (mode) { case SECMARK_MODE_SEL: diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index a54dc3f8234f..b951d422db9b 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -67,9 +67,13 @@ static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int static void ipt_destroy_target(struct ipt_entry_target *t) { - if (t->u.kernel.target->destroy) - t->u.kernel.target->destroy(t->u.kernel.target, t->data); - module_put(t->u.kernel.target->me); + struct xt_tgdtor_param par = { + .target = t->u.kernel.target, + .targinfo = t->data, + }; + if (par.target->destroy != NULL) + par.target->destroy(&par); + module_put(par.target->me); } static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) -- cgit v1.2.3 From 916a917dfec18535ff9e2afdafba82e6279eb4f4 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Oct 2008 11:35:20 +0200 Subject: netfilter: xtables: provide invoked family value to extensions By passing in the family through which extensions were invoked, a bit of data space can be reclaimed. The "family" member will be added to the parameter structures and the check functions be adjusted. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 12 ++++++++++-- net/bridge/netfilter/ebtables.c | 11 ++++++++--- net/ipv4/netfilter/arp_tables.c | 6 ++++-- net/ipv4/netfilter/ip_tables.c | 10 ++++++++-- net/ipv6/netfilter/ip6_tables.c | 10 ++++++++-- net/netfilter/x_tables.c | 23 ++++++++++++----------- net/sched/act_ipt.c | 4 ++-- 7 files changed, 52 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index e3b3b669a143..be41b609c88f 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -183,6 +183,8 @@ struct xt_counters_info * @fragoff: packet is a fragment, this is the data offset * @thoff: position of transport header relative to skb->data * @hotdrop: drop packet if we had inspection problems + * @family: Actual NFPROTO_* through which the function is invoked + * (helpful when match->family == NFPROTO_UNSPEC) */ struct xt_match_param { const struct net_device *in, *out; @@ -191,6 +193,7 @@ struct xt_match_param { int fragoff; unsigned int thoff; bool *hotdrop; + u_int8_t family; }; /** @@ -210,12 +213,14 @@ struct xt_mtchk_param { const struct xt_match *match; void *matchinfo; unsigned int hook_mask; + u_int8_t family; }; /* Match destructor parameters */ struct xt_mtdtor_param { const struct xt_match *match; void *matchinfo; + u_int8_t family; }; /** @@ -232,6 +237,7 @@ struct xt_target_param { unsigned int hooknum; const struct xt_target *target; const void *targinfo; + u_int8_t family; }; /** @@ -249,12 +255,14 @@ struct xt_tgchk_param { const struct xt_target *target; void *targinfo; unsigned int hook_mask; + u_int8_t family; }; /* Target destructor parameters */ struct xt_tgdtor_param { const struct xt_target *target; void *targinfo; + u_int8_t family; }; struct xt_match @@ -393,9 +401,9 @@ extern void xt_unregister_match(struct xt_match *target); extern int xt_register_matches(struct xt_match *match, unsigned int n); extern void xt_unregister_matches(struct xt_match *match, unsigned int n); -extern int xt_check_match(struct xt_mtchk_param *, u_int8_t family, +extern int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto, bool inv_proto); -extern int xt_check_target(struct xt_tgchk_param *, u_int8_t family, +extern int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto, bool inv_proto); extern struct xt_table *xt_register_table(struct net *net, diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 29d8061fa153..5bb88eb0aad4 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -160,6 +160,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, struct xt_match_param mtpar; struct xt_target_param tgpar; + mtpar.family = tgpar.family = NFPROTO_BRIDGE; mtpar.in = tgpar.in = in; mtpar.out = tgpar.out = out; mtpar.hotdrop = &hotdrop; @@ -351,7 +352,7 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, par->match = match; par->matchinfo = m->data; - ret = xt_check_match(par, NFPROTO_BRIDGE, m->match_size, + ret = xt_check_match(par, m->match_size, e->ethproto, e->invflags & EBT_IPROTO); if (ret < 0) { module_put(match->me); @@ -386,7 +387,7 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct xt_tgchk_param *par, par->target = watcher; par->targinfo = w->data; - ret = xt_check_target(par, NFPROTO_BRIDGE, w->watcher_size, + ret = xt_check_target(par, w->watcher_size, e->ethproto, e->invflags & EBT_IPROTO); if (ret < 0) { module_put(watcher->me); @@ -572,6 +573,7 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i) par.match = m->u.match; par.matchinfo = m->data; + par.family = NFPROTO_BRIDGE; if (par.match->destroy != NULL) par.match->destroy(&par); module_put(par.match->me); @@ -588,6 +590,7 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i) par.target = w->u.watcher; par.targinfo = w->data; + par.family = NFPROTO_BRIDGE; if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); @@ -611,6 +614,7 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) par.target = t->u.target; par.targinfo = t->data; + par.family = NFPROTO_BRIDGE; if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); @@ -673,6 +677,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, mtpar.table = tgpar.table = name; mtpar.entryinfo = tgpar.entryinfo = e; mtpar.hook_mask = tgpar.hook_mask = hookmask; + mtpar.family = tgpar.family = NFPROTO_BRIDGE; ret = EBT_MATCH_ITERATE(e, ebt_check_match, &mtpar, &i); if (ret != 0) goto cleanup_matches; @@ -715,7 +720,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, tgpar.target = target; tgpar.targinfo = t->data; - ret = xt_check_target(&tgpar, NFPROTO_BRIDGE, t->target_size, + ret = xt_check_target(&tgpar, t->target_size, e->ethproto, e->invflags & EBT_IPROTO); if (ret < 0) { module_put(target->me); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 3bab78330cf8..8d70d29f1ccf 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -246,6 +246,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, tgpar.in = in; tgpar.out = out; tgpar.hooknum = hook; + tgpar.family = NFPROTO_ARP; arp = arp_hdr(skb); do { @@ -465,10 +466,10 @@ static inline int check_target(struct arpt_entry *e, const char *name) .target = t->u.kernel.target, .targinfo = t->data, .hook_mask = e->comefrom, + .family = NFPROTO_ARP, }; - ret = xt_check_target(&par, NFPROTO_ARP, - t->u.target_size - sizeof(*t), 0, false); + ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); if (ret < 0) { duprintf("arp_tables: check failed for `%s'.\n", t->u.kernel.target->name); @@ -566,6 +567,7 @@ static inline int cleanup_entry(struct arpt_entry *e, unsigned int *i) t = arpt_get_target(e); par.target = t->u.kernel.target; par.targinfo = t->data; + par.family = NFPROTO_ARP; if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 50b9a6c34c38..213fb27debc1 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -348,6 +348,7 @@ ipt_do_table(struct sk_buff *skb, mtpar.hotdrop = &hotdrop; mtpar.in = tgpar.in = in; mtpar.out = tgpar.out = out; + mtpar.family = tgpar.family = NFPROTO_IPV4; tgpar.hooknum = hook; read_lock_bh(&table->lock); @@ -579,6 +580,7 @@ cleanup_match(struct ipt_entry_match *m, unsigned int *i) par.match = m->u.kernel.match; par.matchinfo = m->data; + par.family = NFPROTO_IPV4; if (par.match->destroy != NULL) par.match->destroy(&par); module_put(par.match->me); @@ -616,7 +618,7 @@ check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par, par->match = m->u.kernel.match; par->matchinfo = m->data; - ret = xt_check_match(par, NFPROTO_IPV4, m->u.match_size - sizeof(*m), + ret = xt_check_match(par, m->u.match_size - sizeof(*m), ip->proto, ip->invflags & IPT_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", @@ -662,10 +664,11 @@ static int check_target(struct ipt_entry *e, const char *name) .target = t->u.kernel.target, .targinfo = t->data, .hook_mask = e->comefrom, + .family = NFPROTO_IPV4, }; int ret; - ret = xt_check_target(&par, NFPROTO_IPV4, t->u.target_size - sizeof(*t), + ret = xt_check_target(&par, t->u.target_size - sizeof(*t), e->ip.proto, e->ip.invflags & IPT_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", @@ -693,6 +696,7 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size, mtpar.table = name; mtpar.entryinfo = &e->ip; mtpar.hook_mask = e->comefrom; + mtpar.family = NFPROTO_IPV4; ret = IPT_MATCH_ITERATE(e, find_check_match, &mtpar, &j); if (ret != 0) goto cleanup_matches; @@ -780,6 +784,7 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i) par.target = t->u.kernel.target; par.targinfo = t->data; + par.family = NFPROTO_IPV4; if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); @@ -1659,6 +1664,7 @@ compat_check_entry(struct ipt_entry *e, const char *name, mtpar.table = name; mtpar.entryinfo = &e->ip; mtpar.hook_mask = e->comefrom; + mtpar.family = NFPROTO_IPV4; ret = IPT_MATCH_ITERATE(e, check_match, &mtpar, &j); if (ret) goto cleanup_matches; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d934a6994632..a33485dc81cb 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -370,6 +370,7 @@ ip6t_do_table(struct sk_buff *skb, mtpar.hotdrop = &hotdrop; mtpar.in = tgpar.in = in; mtpar.out = tgpar.out = out; + mtpar.family = tgpar.family = NFPROTO_IPV6; tgpar.hooknum = hook; read_lock_bh(&table->lock); @@ -604,6 +605,7 @@ cleanup_match(struct ip6t_entry_match *m, unsigned int *i) par.match = m->u.kernel.match; par.matchinfo = m->data; + par.family = NFPROTO_IPV6; if (par.match->destroy != NULL) par.match->destroy(&par); module_put(par.match->me); @@ -640,7 +642,7 @@ static int check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par, par->match = m->u.kernel.match; par->matchinfo = m->data; - ret = xt_check_match(par, NFPROTO_IPV6, m->u.match_size - sizeof(*m), + ret = xt_check_match(par, m->u.match_size - sizeof(*m), ipv6->proto, ipv6->invflags & IP6T_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", @@ -686,11 +688,12 @@ static int check_target(struct ip6t_entry *e, const char *name) .target = t->u.kernel.target, .targinfo = t->data, .hook_mask = e->comefrom, + .family = NFPROTO_IPV6, }; int ret; t = ip6t_get_target(e); - ret = xt_check_target(&par, NFPROTO_IPV6, t->u.target_size - sizeof(*t), + ret = xt_check_target(&par, t->u.target_size - sizeof(*t), e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO); if (ret < 0) { duprintf("ip_tables: check failed for `%s'.\n", @@ -718,6 +721,7 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size, mtpar.table = name; mtpar.entryinfo = &e->ipv6; mtpar.hook_mask = e->comefrom; + mtpar.family = NFPROTO_IPV6; ret = IP6T_MATCH_ITERATE(e, find_check_match, &mtpar, &j); if (ret != 0) goto cleanup_matches; @@ -805,6 +809,7 @@ cleanup_entry(struct ip6t_entry *e, unsigned int *i) par.target = t->u.kernel.target; par.targinfo = t->data; + par.family = NFPROTO_IPV6; if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); @@ -1685,6 +1690,7 @@ static int compat_check_entry(struct ip6t_entry *e, const char *name, mtpar.table = name; mtpar.entryinfo = &e->ipv6; mtpar.hook_mask = e->comefrom; + mtpar.family = NFPROTO_IPV6; ret = IP6T_MATCH_ITERATE(e, check_match, &mtpar, &j); if (ret) goto cleanup_matches; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index f29513cd1399..89837a4eef76 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -321,7 +321,7 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target, } EXPORT_SYMBOL_GPL(xt_find_revision); -int xt_check_match(struct xt_mtchk_param *par, u_int8_t family, +int xt_check_match(struct xt_mtchk_param *par, unsigned int size, u_int8_t proto, bool inv_proto) { if (XT_ALIGN(par->match->matchsize) != size && @@ -331,26 +331,27 @@ int xt_check_match(struct xt_mtchk_param *par, u_int8_t family, * because it uses a dynamic-size data set. */ printk("%s_tables: %s match: invalid size %Zu != %u\n", - xt_prefix[family], par->match->name, + xt_prefix[par->family], par->match->name, XT_ALIGN(par->match->matchsize), size); return -EINVAL; } if (par->match->table != NULL && strcmp(par->match->table, par->table) != 0) { printk("%s_tables: %s match: only valid in %s table, not %s\n", - xt_prefix[family], par->match->name, + xt_prefix[par->family], par->match->name, par->match->table, par->table); return -EINVAL; } if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) { printk("%s_tables: %s match: bad hook_mask %#x/%#x\n", - xt_prefix[family], par->match->name, + xt_prefix[par->family], par->match->name, par->hook_mask, par->match->hooks); return -EINVAL; } if (par->match->proto && (par->match->proto != proto || inv_proto)) { printk("%s_tables: %s match: only valid for protocol %u\n", - xt_prefix[family], par->match->name, par->match->proto); + xt_prefix[par->family], par->match->name, + par->match->proto); return -EINVAL; } if (par->match->checkentry != NULL && !par->match->checkentry(par)) @@ -471,31 +472,31 @@ int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr, EXPORT_SYMBOL_GPL(xt_compat_match_to_user); #endif /* CONFIG_COMPAT */ -int xt_check_target(struct xt_tgchk_param *par, u_int8_t family, +int xt_check_target(struct xt_tgchk_param *par, unsigned int size, u_int8_t proto, bool inv_proto) { if (XT_ALIGN(par->target->targetsize) != size) { printk("%s_tables: %s target: invalid size %Zu != %u\n", - xt_prefix[family], par->target->name, + xt_prefix[par->family], par->target->name, XT_ALIGN(par->target->targetsize), size); return -EINVAL; } if (par->target->table != NULL && strcmp(par->target->table, par->table) != 0) { printk("%s_tables: %s target: only valid in %s table, not %s\n", - xt_prefix[family], par->target->name, + xt_prefix[par->family], par->target->name, par->target->table, par->table); return -EINVAL; } if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) { printk("%s_tables: %s target: bad hook_mask %#x/%#x\n", - xt_prefix[family], par->target->name, par->hook_mask, - par->target->hooks); + xt_prefix[par->family], par->target->name, + par->hook_mask, par->target->hooks); return -EINVAL; } if (par->target->proto && (par->target->proto != proto || inv_proto)) { printk("%s_tables: %s target: only valid for protocol %u\n", - xt_prefix[family], par->target->name, + xt_prefix[par->family], par->target->name, par->target->proto); return -EINVAL; } diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index b951d422db9b..0453d79ebf57 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -55,9 +55,9 @@ static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int par.target = target; par.targinfo = t->data; par.hook_mask = hook; + par.family = NFPROTO_IPV4; - ret = xt_check_target(&par, NFPROTO_IPV4, - t->u.target_size - sizeof(*t), 0, false); + ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); if (ret < 0) { module_put(t->u.kernel.target->me); return ret; -- cgit v1.2.3 From 18ee49ddb0d242ed1d0e273038d5e4f6de7379d3 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Wed, 1 Oct 2008 15:41:33 +0000 Subject: phylib: rename mii_bus::dev to mii_bus::parent In preparation of giving mii_bus objects a device tree presence of their own, rename struct mii_bus's ->dev argument to ->parent, since having a 'struct device *dev' that points to our parent device conflicts with introducing a 'struct device dev' representing our own device. Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller Acked-by: Andy Fleming --- arch/powerpc/platforms/82xx/ep8248e.c | 2 +- arch/powerpc/platforms/pasemi/gpio_mdio.c | 2 +- drivers/net/fec_mpc52xx_phy.c | 2 +- drivers/net/fs_enet/mii-bitbang.c | 2 +- drivers/net/fs_enet/mii-fec.c | 2 +- drivers/net/gianfar_mii.c | 2 +- drivers/net/macb.c | 2 +- drivers/net/mv643xx_eth.c | 2 +- drivers/net/phy/fixed.c | 2 +- drivers/net/phy/mdio-ofgpio.c | 2 +- drivers/net/phy/mdio_bus.c | 2 +- drivers/net/sb1250-mac.c | 2 +- drivers/net/sh_eth.c | 2 +- drivers/net/tc35815.c | 2 +- drivers/net/tg3.c | 2 +- drivers/net/ucc_geth_mii.c | 2 +- include/linux/phy.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/platforms/82xx/ep8248e.c b/arch/powerpc/platforms/82xx/ep8248e.c index d5770fdf7f09..0eb6d7f62241 100644 --- a/arch/powerpc/platforms/82xx/ep8248e.c +++ b/arch/powerpc/platforms/82xx/ep8248e.c @@ -137,7 +137,7 @@ static int __devinit ep8248e_mdio_probe(struct of_device *ofdev, bus->irq[i] = -1; bus->name = "ep8248e-mdio-bitbang"; - bus->dev = &ofdev->dev; + bus->parent = &ofdev->dev; snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); return mdiobus_register(bus); diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c index ab6955412ba4..798c7abe1604 100644 --- a/arch/powerpc/platforms/pasemi/gpio_mdio.c +++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c @@ -272,7 +272,7 @@ static int __devinit gpio_mdio_probe(struct of_device *ofdev, prop = of_get_property(np, "mdio-pin", NULL); priv->mdio_pin = *prop; - new_bus->dev = dev; + new_bus->parent = dev; dev_set_drvdata(dev, new_bus); err = mdiobus_register(new_bus); diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c index f5634447276d..2a523308c77e 100644 --- a/drivers/net/fec_mpc52xx_phy.c +++ b/drivers/net/fec_mpc52xx_phy.c @@ -127,7 +127,7 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_i snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); bus->priv = priv; - bus->dev = dev; + bus->parent = dev; dev_set_drvdata(dev, bus); /* set MII speed */ diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index be4b72f4f49a..2774252c2352 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -203,7 +203,7 @@ static int __devinit fs_enet_mdio_probe(struct of_device *ofdev, if (!strcmp(np->type, "ethernet-phy")) add_phy(new_bus, np); - new_bus->dev = &ofdev->dev; + new_bus->parent = &ofdev->dev; dev_set_drvdata(&ofdev->dev, new_bus); ret = mdiobus_register(new_bus); diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index 695f74cc4398..4d89a22317ac 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -172,7 +172,7 @@ static int __devinit fs_enet_mdio_probe(struct of_device *ofdev, if (!strcmp(np->type, "ethernet-phy")) add_phy(new_bus, np); - new_bus->dev = &ofdev->dev; + new_bus->parent = &ofdev->dev; dev_set_drvdata(&ofdev->dev, new_bus); ret = mdiobus_register(new_bus); diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index 678f48c69119..38895b0e376d 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c @@ -196,7 +196,7 @@ static int gfar_mdio_probe(struct device *dev) new_bus->irq = pdata->irq; - new_bus->dev = dev; + new_bus->parent = dev; dev_set_drvdata(dev, new_bus); /* diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 84c77f1f9a5c..045361fe3d6a 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -250,7 +250,7 @@ static int macb_mii_init(struct macb *bp) bp->mii_bus.reset = &macb_mdio_reset; snprintf(bp->mii_bus.id, MII_BUS_ID_SIZE, "%x", bp->pdev->id); bp->mii_bus.priv = bp; - bp->mii_bus.dev = &bp->dev->dev; + bp->mii_bus.parent = &bp->dev->dev; pdata = bp->pdev->dev.platform_data; if (pdata) diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 372811ade9f5..634008154480 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -2368,7 +2368,7 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) msp->smi_bus.read = smi_bus_read; msp->smi_bus.write = smi_bus_write, snprintf(msp->smi_bus.id, MII_BUS_ID_SIZE, "%d", pdev->id); - msp->smi_bus.dev = &pdev->dev; + msp->smi_bus.parent = &pdev->dev; msp->smi_bus.phy_mask = 0xffffffff; if (mdiobus_register(&msp->smi_bus) < 0) goto out_unmap; diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 4e07956a483b..1cec8967a089 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -215,7 +215,7 @@ static int __init fixed_mdio_bus_init(void) snprintf(fmb->mii_bus.id, MII_BUS_ID_SIZE, "0"); fmb->mii_bus.name = "Fixed MDIO Bus"; - fmb->mii_bus.dev = &pdev->dev; + fmb->mii_bus.parent = &pdev->dev; fmb->mii_bus.read = &fixed_mdio_read; fmb->mii_bus.write = &fixed_mdio_write; fmb->mii_bus.irq = fmb->irqs; diff --git a/drivers/net/phy/mdio-ofgpio.c b/drivers/net/phy/mdio-ofgpio.c index 7edfc0c34835..27bd4539d089 100644 --- a/drivers/net/phy/mdio-ofgpio.c +++ b/drivers/net/phy/mdio-ofgpio.c @@ -142,7 +142,7 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, if (!strcmp(np->type, "ethernet-phy")) add_phy(new_bus, np); - new_bus->dev = &ofdev->dev; + new_bus->parent = &ofdev->dev; dev_set_drvdata(&ofdev->dev, new_bus); ret = mdiobus_register(new_bus); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index e7508c10887c..a7211a32bb4a 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -107,7 +107,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) phydev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; - phydev->dev.parent = bus->dev; + phydev->dev.parent = bus->parent; phydev->dev.bus = &mdio_bus_type; snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, addr); diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index ce10cfa1ee53..d0ee735590bb 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2378,7 +2378,7 @@ static int sbmac_init(struct platform_device *pldev, long long base) for (i = 0; i < PHY_MAX_ADDR; ++i) sc->mii_bus.irq[i] = SBMAC_PHY_INT; - sc->mii_bus.dev = &pldev->dev; + sc->mii_bus.parent = &pldev->dev; dev_set_drvdata(&pldev->dev, &sc->mii_bus); return 0; diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 1c370e6aa641..1b3b7b66ffd8 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -1140,7 +1140,7 @@ static int sh_mdio_init(struct net_device *ndev, int id) /* Hook up MII support for ethtool */ mdp->mii_bus->name = "sh_mii"; - mdp->mii_bus->dev = &ndev->dev; + mdp->mii_bus->parent = &ndev->dev; mdp->mii_bus->id[0] = id; /* PHY IRQ */ diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 8487ace9d2e3..1f2b1f2d73ae 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -768,7 +768,7 @@ static int tc_mii_init(struct net_device *dev) snprintf(lp->mii_bus.id, MII_BUS_ID_SIZE, "%x", (lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn); lp->mii_bus.priv = dev; - lp->mii_bus.dev = &lp->pci_dev->dev; + lp->mii_bus.parent = &lp->pci_dev->dev; lp->mii_bus.irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); if (!lp->mii_bus.irq) { err = -ENOMEM; diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 123920759efd..4b8b75bcbde0 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -961,7 +961,7 @@ static int tg3_mdio_init(struct tg3 *tp) snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%x", (tp->pdev->bus->number << 8) | tp->pdev->devfn); mdio_bus->priv = tp; - mdio_bus->dev = &tp->pdev->dev; + mdio_bus->parent = &tp->pdev->dev; mdio_bus->read = &tg3_mdio_read; mdio_bus->write = &tg3_mdio_write; mdio_bus->reset = &tg3_mdio_reset; diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c index 6d9e7ad9fda9..75b72fe1f23c 100644 --- a/drivers/net/ucc_geth_mii.c +++ b/drivers/net/ucc_geth_mii.c @@ -187,7 +187,7 @@ static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *ma new_bus->priv = (void __force *)regs; - new_bus->dev = device; + new_bus->parent = device; dev_set_drvdata(device, new_bus); /* Read MII management master from device tree */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 5f170f5b1a30..87499bdedc6f 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -99,7 +99,7 @@ struct mii_bus { */ struct mutex mdio_lock; - struct device *dev; + struct device *parent; /* list of all PHYs on bus */ struct phy_device *phy_map[PHY_MAX_ADDR]; -- cgit v1.2.3 From 298cf9beb9679522de995e249eccbd82f7c51999 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Wed, 8 Oct 2008 16:29:57 -0700 Subject: phylib: move to dynamic allocation of struct mii_bus This patch introduces mdiobus_alloc() and mdiobus_free(), and makes all mdio bus drivers use these functions to allocate their struct mii_bus'es dynamically. Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller Acked-by: Andy Fleming --- drivers/net/au1000_eth.c | 43 +++++++++++++++---------- drivers/net/au1000_eth.h | 2 +- drivers/net/bfin_mac.c | 31 +++++++++++------- drivers/net/bfin_mac.h | 2 +- drivers/net/cpmac.c | 51 +++++++++++++++++------------- drivers/net/fec_mpc52xx_phy.c | 6 ++-- drivers/net/fs_enet/mii-bitbang.c | 7 ++--- drivers/net/fs_enet/mii-fec.c | 6 ++-- drivers/net/gianfar_mii.c | 7 ++--- drivers/net/macb.c | 49 +++++++++++++++++------------ drivers/net/macb.h | 2 +- drivers/net/mv643xx_eth.c | 32 ++++++++++++------- drivers/net/phy/fixed.c | 29 +++++++++++------ drivers/net/phy/mdio-bitbang.c | 4 +-- drivers/net/phy/mdio-ofgpio.c | 9 +++--- drivers/net/phy/mdio_bus.c | 24 ++++++++++++++ drivers/net/sb1250-mac.c | 36 ++++++++++++--------- drivers/net/sh_eth.c | 2 +- drivers/net/tc35815.c | 45 +++++++++++++++----------- drivers/net/tg3.c | 66 ++++++++++++++++++++------------------- drivers/net/tg3.h | 2 +- drivers/net/ucc_geth_mii.c | 7 ++--- include/linux/phy.h | 2 ++ 23 files changed, 279 insertions(+), 185 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 92c16c37ff23..7b92201a7b50 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -290,7 +290,7 @@ static int mii_probe (struct net_device *dev) if(aup->mac_id == 0) { /* get PHY0 */ # if defined(AU1XXX_PHY0_ADDR) - phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus.phy_map[AU1XXX_PHY0_ADDR]; + phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus->phy_map[AU1XXX_PHY0_ADDR]; # else printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n", dev->name); @@ -298,7 +298,7 @@ static int mii_probe (struct net_device *dev) # endif /* defined(AU1XXX_PHY0_ADDR) */ } else if (aup->mac_id == 1) { /* get PHY1 */ # if defined(AU1XXX_PHY1_ADDR) - phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus.phy_map[AU1XXX_PHY1_ADDR]; + phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus->phy_map[AU1XXX_PHY1_ADDR]; # else printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n", dev->name); @@ -311,8 +311,8 @@ static int mii_probe (struct net_device *dev) /* find the first (lowest address) PHY on the current MAC's MII bus */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) - if (aup->mii_bus.phy_map[phy_addr]) { - phydev = aup->mii_bus.phy_map[phy_addr]; + if (aup->mii_bus->phy_map[phy_addr]) { + phydev = aup->mii_bus->phy_map[phy_addr]; # if !defined(AU1XXX_PHY_SEARCH_HIGHEST_ADDR) break; /* break out with first one found */ # endif @@ -331,7 +331,7 @@ static int mii_probe (struct net_device *dev) * the MAC0 MII bus */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { struct phy_device *const tmp_phydev = - au_macs[0]->mii_bus.phy_map[phy_addr]; + au_macs[0]->mii_bus->phy_map[phy_addr]; if (!tmp_phydev) continue; /* no PHY here... */ @@ -698,28 +698,32 @@ static struct net_device * au1000_probe(int port_num) *aup->enable = 0; aup->mac_enabled = 0; - aup->mii_bus.priv = dev; - aup->mii_bus.read = mdiobus_read; - aup->mii_bus.write = mdiobus_write; - aup->mii_bus.reset = mdiobus_reset; - aup->mii_bus.name = "au1000_eth_mii"; - snprintf(aup->mii_bus.id, MII_BUS_ID_SIZE, "%x", aup->mac_id); - aup->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + aup->mii_bus = mdiobus_alloc(); + if (aup->mii_bus == NULL) + goto err_out; + + aup->mii_bus->priv = dev; + aup->mii_bus->read = mdiobus_read; + aup->mii_bus->write = mdiobus_write; + aup->mii_bus->reset = mdiobus_reset; + aup->mii_bus->name = "au1000_eth_mii"; + snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%x", aup->mac_id); + aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); for(i = 0; i < PHY_MAX_ADDR; ++i) - aup->mii_bus.irq[i] = PHY_POLL; + aup->mii_bus->irq[i] = PHY_POLL; /* if known, set corresponding PHY IRQs */ #if defined(AU1XXX_PHY_STATIC_CONFIG) # if defined(AU1XXX_PHY0_IRQ) if (AU1XXX_PHY0_BUSID == aup->mac_id) - aup->mii_bus.irq[AU1XXX_PHY0_ADDR] = AU1XXX_PHY0_IRQ; + aup->mii_bus->irq[AU1XXX_PHY0_ADDR] = AU1XXX_PHY0_IRQ; # endif # if defined(AU1XXX_PHY1_IRQ) if (AU1XXX_PHY1_BUSID == aup->mac_id) - aup->mii_bus.irq[AU1XXX_PHY1_ADDR] = AU1XXX_PHY1_IRQ; + aup->mii_bus->irq[AU1XXX_PHY1_ADDR] = AU1XXX_PHY1_IRQ; # endif #endif - mdiobus_register(&aup->mii_bus); + mdiobus_register(aup->mii_bus); if (mii_probe(dev) != 0) { goto err_out; @@ -775,6 +779,11 @@ static struct net_device * au1000_probe(int port_num) return dev; err_out: + if (aup->mii_bus != NULL) { + mdiobus_unregister(aup->mii_bus); + mdiobus_free(aup->mii_bus); + } + /* here we should have a valid dev plus aup-> register addresses * so we can reset the mac properly.*/ reset_mac(dev); @@ -1005,6 +1014,8 @@ static void __exit au1000_cleanup_module(void) if (dev) { aup = (struct au1000_private *) dev->priv; unregister_netdev(dev); + mdiobus_unregister(aup->mii_bus); + mdiobus_free(aup->mii_bus); for (j = 0; j < NUM_RX_DMA; j++) if (aup->rx_db_inuse[j]) ReleaseDB(aup, aup->rx_db_inuse[j]); diff --git a/drivers/net/au1000_eth.h b/drivers/net/au1000_eth.h index f3baeaa12854..824ecd5ff3a8 100644 --- a/drivers/net/au1000_eth.h +++ b/drivers/net/au1000_eth.h @@ -106,7 +106,7 @@ struct au1000_private { int old_duplex; struct phy_device *phy_dev; - struct mii_bus mii_bus; + struct mii_bus *mii_bus; /* These variables are just for quick access to certain regs addresses. */ volatile mac_reg_t *mac; /* mac registers */ diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index df896e23e2c5..a0d41c5d97d8 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -398,7 +398,7 @@ static int mii_probe(struct net_device *dev) /* search for connect PHY device */ for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *const tmp_phydev = lp->mii_bus.phy_map[i]; + struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i]; if (!tmp_phydev) continue; /* no PHY here... */ @@ -1058,17 +1058,21 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) setup_mac_addr(ndev->dev_addr); /* MDIO bus initial */ - lp->mii_bus.priv = ndev; - lp->mii_bus.read = mdiobus_read; - lp->mii_bus.write = mdiobus_write; - lp->mii_bus.reset = mdiobus_reset; - lp->mii_bus.name = "bfin_mac_mdio"; - snprintf(lp->mii_bus.id, MII_BUS_ID_SIZE, "0"); - lp->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + lp->mii_bus = mdiobus_alloc(); + if (lp->mii_bus == NULL) + goto out_err_mdiobus_alloc; + + lp->mii_bus->priv = ndev; + lp->mii_bus->read = mdiobus_read; + lp->mii_bus->write = mdiobus_write; + lp->mii_bus->reset = mdiobus_reset; + lp->mii_bus->name = "bfin_mac_mdio"; + snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "0"); + lp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); for (i = 0; i < PHY_MAX_ADDR; ++i) - lp->mii_bus.irq[i] = PHY_POLL; + lp->mii_bus->irq[i] = PHY_POLL; - rc = mdiobus_register(&lp->mii_bus); + rc = mdiobus_register(lp->mii_bus); if (rc) { dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); goto out_err_mdiobus_register; @@ -1121,8 +1125,10 @@ out_err_reg_ndev: free_irq(IRQ_MAC_RX, ndev); out_err_request_irq: out_err_mii_probe: - mdiobus_unregister(&lp->mii_bus); + mdiobus_unregister(lp->mii_bus); out_err_mdiobus_register: + mdiobus_free(lp->mii_bus); +out_err_mdiobus_alloc: peripheral_free_list(pin_req); out_err_setup_pin_mux: out_err_probe_mac: @@ -1139,7 +1145,8 @@ static int __devexit bfin_mac_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - mdiobus_unregister(&lp->mii_bus); + mdiobus_unregister(lp->mii_bus); + mdiobus_free(lp->mii_bus); unregister_netdev(ndev); diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h index beff51064ff4..052b5dce3e3c 100644 --- a/drivers/net/bfin_mac.h +++ b/drivers/net/bfin_mac.h @@ -66,7 +66,7 @@ struct bfin_mac_local { int old_duplex; struct phy_device *phydev; - struct mii_bus mii_bus; + struct mii_bus *mii_bus; }; extern void bfin_get_ether_addr(char *addr); diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index ec6b0af3d46b..017a5361b980 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -302,13 +302,7 @@ static int cpmac_mdio_reset(struct mii_bus *bus) static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, }; -static struct mii_bus cpmac_mii = { - .name = "cpmac-mii", - .read = cpmac_mdio_read, - .write = cpmac_mdio_write, - .reset = cpmac_mdio_reset, - .irq = mii_irqs, -}; +static struct mii_bus *cpmac_mii; static int cpmac_config(struct net_device *dev, struct ifmap *map) { @@ -1116,7 +1110,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { if (!(pdata->phy_mask & (1 << phy_id))) continue; - if (!cpmac_mii.phy_map[phy_id]) + if (!cpmac_mii->phy_map[phy_id]) continue; break; } @@ -1168,7 +1162,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); - priv->phy = phy_connect(dev, cpmac_mii.phy_map[phy_id]->dev.bus_id, + priv->phy = phy_connect(dev, cpmac_mii->phy_map[phy_id]->dev.bus_id, &cpmac_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { if (netif_msg_drv(priv)) @@ -1216,11 +1210,22 @@ int __devinit cpmac_init(void) u32 mask; int i, res; - cpmac_mii.priv = ioremap(AR7_REGS_MDIO, 256); + cpmac_mii = mdiobus_alloc(); + if (cpmac_mii == NULL) + return -ENOMEM; + + cpmac_mii->name = "cpmac-mii"; + cpmac_mii->read = cpmac_mdio_read; + cpmac_mii->write = cpmac_mdio_write; + cpmac_mii->reset = cpmac_mdio_reset; + cpmac_mii->irq = mii_irqs; + + cpmac_mii->priv = ioremap(AR7_REGS_MDIO, 256); - if (!cpmac_mii.priv) { + if (!cpmac_mii->priv) { printk(KERN_ERR "Can't ioremap mdio registers\n"); - return -ENXIO; + res = -ENXIO; + goto fail_alloc; } #warning FIXME: unhardcode gpio&reset bits @@ -1230,10 +1235,10 @@ int __devinit cpmac_init(void) ar7_device_reset(AR7_RESET_BIT_CPMAC_HI); ar7_device_reset(AR7_RESET_BIT_EPHY); - cpmac_mii.reset(&cpmac_mii); + cpmac_mii->reset(cpmac_mii); for (i = 0; i < 300000; i++) - if ((mask = cpmac_read(cpmac_mii.priv, CPMAC_MDIO_ALIVE))) + if ((mask = cpmac_read(cpmac_mii->priv, CPMAC_MDIO_ALIVE))) break; else cpu_relax(); @@ -1244,10 +1249,10 @@ int __devinit cpmac_init(void) mask = 0; } - cpmac_mii.phy_mask = ~(mask | 0x80000000); - snprintf(cpmac_mii.id, MII_BUS_ID_SIZE, "0"); + cpmac_mii->phy_mask = ~(mask | 0x80000000); + snprintf(cpmac_mii->id, MII_BUS_ID_SIZE, "0"); - res = mdiobus_register(&cpmac_mii); + res = mdiobus_register(cpmac_mii); if (res) goto fail_mii; @@ -1258,10 +1263,13 @@ int __devinit cpmac_init(void) return 0; fail_cpmac: - mdiobus_unregister(&cpmac_mii); + mdiobus_unregister(cpmac_mii); fail_mii: - iounmap(cpmac_mii.priv); + iounmap(cpmac_mii->priv); + +fail_alloc: + mdiobus_free(cpmac_mii); return res; } @@ -1269,8 +1277,9 @@ fail_mii: void __devexit cpmac_exit(void) { platform_driver_unregister(&cpmac_driver); - mdiobus_unregister(&cpmac_mii); - iounmap(cpmac_mii.priv); + mdiobus_unregister(cpmac_mii); + mdiobus_free(cpmac_mii); + iounmap(cpmac_mii->priv); } module_init(cpmac_init); diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c index 2a523308c77e..08e18bcb970f 100644 --- a/drivers/net/fec_mpc52xx_phy.c +++ b/drivers/net/fec_mpc52xx_phy.c @@ -83,7 +83,7 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_i int err; int i; - bus = kzalloc(sizeof(*bus), GFP_KERNEL); + bus = mdiobus_alloc(); if (bus == NULL) return -ENOMEM; priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -150,7 +150,7 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_i irq_dispose_mapping(bus->irq[i]); kfree(bus->irq); kfree(priv); - kfree(bus); + mdiobus_free(bus); return err; } @@ -171,7 +171,7 @@ static int mpc52xx_fec_mdio_remove(struct of_device *of) irq_dispose_mapping(bus->irq[i]); kfree(priv); kfree(bus->irq); - kfree(bus); + mdiobus_free(bus); return 0; } diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index 2774252c2352..49b6645d7e0c 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -218,9 +218,9 @@ out_free_irqs: out_unmap_regs: iounmap(bitbang->dir); out_free_bus: - kfree(new_bus); -out_free_priv: free_mdio_bitbang(new_bus); +out_free_priv: + kfree(bitbang); out: return ret; } @@ -231,12 +231,11 @@ static int fs_enet_mdio_remove(struct of_device *ofdev) struct bb_info *bitbang = bus->priv; mdiobus_unregister(bus); - free_mdio_bitbang(bus); dev_set_drvdata(&ofdev->dev, NULL); kfree(bus->irq); + free_mdio_bitbang(bus); iounmap(bitbang->dir); kfree(bitbang); - kfree(bus); return 0; } diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index 4d89a22317ac..28077cc1b949 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -128,7 +128,7 @@ static int __devinit fs_enet_mdio_probe(struct of_device *ofdev, struct fec_info *fec; int ret = -ENOMEM, i; - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + new_bus = mdiobus_alloc(); if (!new_bus) goto out; @@ -190,7 +190,7 @@ out_res: out_fec: kfree(fec); out_mii: - kfree(new_bus); + mdiobus_free(new_bus); out: return ret; } @@ -205,7 +205,7 @@ static int fs_enet_mdio_remove(struct of_device *ofdev) kfree(bus->irq); iounmap(fec->fecp); kfree(fec); - kfree(bus); + mdiobus_free(bus); return 0; } diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index 38895b0e376d..bf73eea98010 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c @@ -164,8 +164,7 @@ static int gfar_mdio_probe(struct device *dev) if (NULL == dev) return -EINVAL; - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); - + new_bus = mdiobus_alloc(); if (NULL == new_bus) return -ENOMEM; @@ -242,7 +241,7 @@ static int gfar_mdio_probe(struct device *dev) bus_register_fail: iounmap(regs); reg_map_fail: - kfree(new_bus); + mdiobus_free(new_bus); return err; } @@ -258,7 +257,7 @@ static int gfar_mdio_remove(struct device *dev) iounmap((void __iomem *)bus->priv); bus->priv = NULL; - kfree(bus); + mdiobus_free(bus); return 0; } diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 045361fe3d6a..01f7a31bac76 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -195,8 +195,8 @@ static int macb_mii_probe(struct net_device *dev) /* find the first phy */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (bp->mii_bus.phy_map[phy_addr]) { - phydev = bp->mii_bus.phy_map[phy_addr]; + if (bp->mii_bus->phy_map[phy_addr]) { + phydev = bp->mii_bus->phy_map[phy_addr]; break; } } @@ -244,30 +244,36 @@ static int macb_mii_init(struct macb *bp) /* Enable managment port */ macb_writel(bp, NCR, MACB_BIT(MPE)); - bp->mii_bus.name = "MACB_mii_bus"; - bp->mii_bus.read = &macb_mdio_read; - bp->mii_bus.write = &macb_mdio_write; - bp->mii_bus.reset = &macb_mdio_reset; - snprintf(bp->mii_bus.id, MII_BUS_ID_SIZE, "%x", bp->pdev->id); - bp->mii_bus.priv = bp; - bp->mii_bus.parent = &bp->dev->dev; + bp->mii_bus = mdiobus_alloc(); + if (bp->mii_bus == NULL) { + err = -ENOMEM; + goto err_out; + } + + bp->mii_bus->name = "MACB_mii_bus"; + bp->mii_bus->read = &macb_mdio_read; + bp->mii_bus->write = &macb_mdio_write; + bp->mii_bus->reset = &macb_mdio_reset; + snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%x", bp->pdev->id); + bp->mii_bus->priv = bp; + bp->mii_bus->parent = &bp->dev->dev; pdata = bp->pdev->dev.platform_data; if (pdata) - bp->mii_bus.phy_mask = pdata->phy_mask; + bp->mii_bus->phy_mask = pdata->phy_mask; - bp->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); - if (!bp->mii_bus.irq) { + bp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + if (!bp->mii_bus->irq) { err = -ENOMEM; - goto err_out; + goto err_out_free_mdiobus; } for (i = 0; i < PHY_MAX_ADDR; i++) - bp->mii_bus.irq[i] = PHY_POLL; + bp->mii_bus->irq[i] = PHY_POLL; - platform_set_drvdata(bp->dev, &bp->mii_bus); + platform_set_drvdata(bp->dev, bp->mii_bus); - if (mdiobus_register(&bp->mii_bus)) + if (mdiobus_register(bp->mii_bus)) goto err_out_free_mdio_irq; if (macb_mii_probe(bp->dev) != 0) { @@ -277,9 +283,11 @@ static int macb_mii_init(struct macb *bp) return 0; err_out_unregister_bus: - mdiobus_unregister(&bp->mii_bus); + mdiobus_unregister(bp->mii_bus); err_out_free_mdio_irq: - kfree(bp->mii_bus.irq); + kfree(bp->mii_bus->irq); +err_out_free_mdiobus: + mdiobus_free(bp->mii_bus); err_out: return err; } @@ -1261,8 +1269,9 @@ static int __exit macb_remove(struct platform_device *pdev) bp = netdev_priv(dev); if (bp->phy_dev) phy_disconnect(bp->phy_dev); - mdiobus_unregister(&bp->mii_bus); - kfree(bp->mii_bus.irq); + mdiobus_unregister(bp->mii_bus); + kfree(bp->mii_bus->irq); + mdiobus_free(bp->mii_bus); unregister_netdev(dev); free_irq(dev->irq, dev); iounmap(bp->regs); diff --git a/drivers/net/macb.h b/drivers/net/macb.h index 57b85acf0d16..d3212f6db703 100644 --- a/drivers/net/macb.h +++ b/drivers/net/macb.h @@ -384,7 +384,7 @@ struct macb { unsigned int rx_pending, tx_pending; - struct mii_bus mii_bus; + struct mii_bus *mii_bus; struct phy_device *phy_dev; unsigned int link; unsigned int speed; diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 634008154480..d25a30251139 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -250,7 +250,7 @@ struct mv643xx_eth_shared_private { /* * Provides access to local SMI interface. */ - struct mii_bus smi_bus; + struct mii_bus *smi_bus; /* * If we have access to the error interrupt pin (which is @@ -2363,15 +2363,19 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) * Set up and register SMI bus. */ if (pd == NULL || pd->shared_smi == NULL) { - msp->smi_bus.priv = msp; - msp->smi_bus.name = "mv643xx_eth smi"; - msp->smi_bus.read = smi_bus_read; - msp->smi_bus.write = smi_bus_write, - snprintf(msp->smi_bus.id, MII_BUS_ID_SIZE, "%d", pdev->id); - msp->smi_bus.parent = &pdev->dev; - msp->smi_bus.phy_mask = 0xffffffff; - if (mdiobus_register(&msp->smi_bus) < 0) + msp->smi_bus = mdiobus_alloc(); + if (msp->smi_bus == NULL) goto out_unmap; + + msp->smi_bus->priv = msp; + msp->smi_bus->name = "mv643xx_eth smi"; + msp->smi_bus->read = smi_bus_read; + msp->smi_bus->write = smi_bus_write, + snprintf(msp->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id); + msp->smi_bus->parent = &pdev->dev; + msp->smi_bus->phy_mask = 0xffffffff; + if (mdiobus_register(msp->smi_bus) < 0) + goto out_free_mii_bus; msp->smi = msp; } else { msp->smi = platform_get_drvdata(pd->shared_smi); @@ -2411,6 +2415,8 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) return 0; +out_free_mii_bus: + mdiobus_free(msp->smi_bus); out_unmap: iounmap(msp->base); out_free: @@ -2424,8 +2430,10 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; - if (pd == NULL || pd->shared_smi == NULL) - mdiobus_unregister(&msp->smi_bus); + if (pd == NULL || pd->shared_smi == NULL) { + mdiobus_free(msp->smi_bus); + mdiobus_unregister(msp->smi_bus); + } if (msp->err_interrupt != NO_IRQ) free_irq(msp->err_interrupt, msp); iounmap(msp->base); @@ -2493,7 +2501,7 @@ static void set_params(struct mv643xx_eth_private *mp, static struct phy_device *phy_scan(struct mv643xx_eth_private *mp, int phy_addr) { - struct mii_bus *bus = &mp->shared->smi->smi_bus; + struct mii_bus *bus = mp->shared->smi->smi_bus; struct phy_device *phydev; int start; int num; diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 1cec8967a089..b5e13f8d5e31 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -24,7 +24,7 @@ struct fixed_mdio_bus { int irqs[PHY_MAX_ADDR]; - struct mii_bus mii_bus; + struct mii_bus *mii_bus; struct list_head phys; }; @@ -213,19 +213,27 @@ static int __init fixed_mdio_bus_init(void) goto err_pdev; } - snprintf(fmb->mii_bus.id, MII_BUS_ID_SIZE, "0"); - fmb->mii_bus.name = "Fixed MDIO Bus"; - fmb->mii_bus.parent = &pdev->dev; - fmb->mii_bus.read = &fixed_mdio_read; - fmb->mii_bus.write = &fixed_mdio_write; - fmb->mii_bus.irq = fmb->irqs; + fmb->mii_bus = mdiobus_alloc(); + if (fmb->mii_bus == NULL) { + ret = -ENOMEM; + goto err_mdiobus_reg; + } - ret = mdiobus_register(&fmb->mii_bus); + snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "0"); + fmb->mii_bus->name = "Fixed MDIO Bus"; + fmb->mii_bus->parent = &pdev->dev; + fmb->mii_bus->read = &fixed_mdio_read; + fmb->mii_bus->write = &fixed_mdio_write; + fmb->mii_bus->irq = fmb->irqs; + + ret = mdiobus_register(fmb->mii_bus); if (ret) - goto err_mdiobus_reg; + goto err_mdiobus_alloc; return 0; +err_mdiobus_alloc: + mdiobus_free(fmb->mii_bus); err_mdiobus_reg: platform_device_unregister(pdev); err_pdev: @@ -238,7 +246,8 @@ static void __exit fixed_mdio_bus_exit(void) struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp, *tmp; - mdiobus_unregister(&fmb->mii_bus); + mdiobus_unregister(fmb->mii_bus); + mdiobus_free(fmb->mii_bus); platform_device_unregister(pdev); list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index c01b78013ddc..2576055b350b 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -165,7 +165,7 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) { struct mii_bus *bus; - bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + bus = mdiobus_alloc(); if (!bus) return NULL; @@ -184,7 +184,7 @@ void free_mdio_bitbang(struct mii_bus *bus) struct mdiobb_ctrl *ctrl = bus->priv; module_put(ctrl->ops->owner); - kfree(bus); + mdiobus_free(bus); } EXPORT_SYMBOL(free_mdio_bitbang); diff --git a/drivers/net/phy/mdio-ofgpio.c b/drivers/net/phy/mdio-ofgpio.c index 27bd4539d089..2ff97754e574 100644 --- a/drivers/net/phy/mdio-ofgpio.c +++ b/drivers/net/phy/mdio-ofgpio.c @@ -122,7 +122,7 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, new_bus = alloc_mdio_bitbang(&bitbang->ctrl); if (!new_bus) - goto out_free_priv; + goto out_free_bitbang; new_bus->name = "GPIO Bitbanged MII", @@ -155,9 +155,9 @@ out_free_irqs: dev_set_drvdata(&ofdev->dev, NULL); kfree(new_bus->irq); out_free_bus: - kfree(new_bus); -out_free_priv: free_mdio_bitbang(new_bus); +out_free_bitbang: + kfree(bitbang); out: return ret; } @@ -168,11 +168,10 @@ static int mdio_ofgpio_remove(struct of_device *ofdev) struct mdio_gpio_info *bitbang = bus->priv; mdiobus_unregister(bus); + kfree(bus->irq); free_mdio_bitbang(bus); dev_set_drvdata(&ofdev->dev, NULL); - kfree(bus->irq); kfree(bitbang); - kfree(bus); return 0; } diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index a7211a32bb4a..d206691f15d8 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -35,6 +35,18 @@ #include #include +/** + * mdiobus_alloc - allocate a mii_bus structure + * + * Description: called by a bus driver to allocate an mii_bus + * structure to fill in. + */ +struct mii_bus *mdiobus_alloc(void) +{ + return kzalloc(sizeof(struct mii_bus), GFP_KERNEL); +} +EXPORT_SYMBOL(mdiobus_alloc); + /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -87,6 +99,18 @@ void mdiobus_unregister(struct mii_bus *bus) } EXPORT_SYMBOL(mdiobus_unregister); +/** + * mdiobus_free - free a struct mii_bus + * @bus: mii_bus to free + * + * This function frees the mii_bus. + */ +void mdiobus_free(struct mii_bus *bus) +{ + kfree(bus); +} +EXPORT_SYMBOL(mdiobus_free); + struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) { struct phy_device *phydev; diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index d0ee735590bb..2615d46e6e50 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -256,7 +256,7 @@ struct sbmac_softc { struct net_device *sbm_dev; /* pointer to linux device */ struct napi_struct napi; struct phy_device *phy_dev; /* the associated PHY device */ - struct mii_bus mii_bus; /* the MII bus */ + struct mii_bus *mii_bus; /* the MII bus */ int phy_irq[PHY_MAX_ADDR]; spinlock_t sbm_lock; /* spin lock */ int sbm_devflags; /* current device flags */ @@ -2348,10 +2348,17 @@ static int sbmac_init(struct platform_device *pldev, long long base) /* This is needed for PASS2 for Rx H/W checksum feature */ sbmac_set_iphdr_offset(sc); + sc->mii_bus = mdiobus_alloc(); + if (sc->mii_bus == NULL) { + sbmac_uninitctx(sc); + return -ENOMEM; + } + err = register_netdev(dev); if (err) { printk(KERN_ERR "%s.%d: unable to register netdev\n", sbmac_string, idx); + mdiobus_free(sc->mii_bus); sbmac_uninitctx(sc); return err; } @@ -2369,17 +2376,17 @@ static int sbmac_init(struct platform_device *pldev, long long base) pr_info("%s: SiByte Ethernet at 0x%08Lx, address: %s\n", dev->name, base, print_mac(mac, eaddr)); - sc->mii_bus.name = sbmac_mdio_string; - snprintf(sc->mii_bus.id, MII_BUS_ID_SIZE, "%x", idx); - sc->mii_bus.priv = sc; - sc->mii_bus.read = sbmac_mii_read; - sc->mii_bus.write = sbmac_mii_write; - sc->mii_bus.irq = sc->phy_irq; + sc->mii_bus->name = sbmac_mdio_string; + snprintf(sc->mii_bus->id, MII_BUS_ID_SIZE, "%x", idx); + sc->mii_bus->priv = sc; + sc->mii_bus->read = sbmac_mii_read; + sc->mii_bus->write = sbmac_mii_write; + sc->mii_bus->irq = sc->phy_irq; for (i = 0; i < PHY_MAX_ADDR; ++i) - sc->mii_bus.irq[i] = SBMAC_PHY_INT; + sc->mii_bus->irq[i] = SBMAC_PHY_INT; - sc->mii_bus.parent = &pldev->dev; - dev_set_drvdata(&pldev->dev, &sc->mii_bus); + sc->mii_bus->parent = &pldev->dev; + dev_set_drvdata(&pldev->dev, sc->mii_bus); return 0; } @@ -2410,7 +2417,7 @@ static int sbmac_open(struct net_device *dev) /* * Probe PHY address */ - err = mdiobus_register(&sc->mii_bus); + err = mdiobus_register(sc->mii_bus); if (err) { printk(KERN_ERR "%s: unable to register MDIO bus\n", dev->name); @@ -2447,7 +2454,7 @@ static int sbmac_open(struct net_device *dev) return 0; out_unregister: - mdiobus_unregister(&sc->mii_bus); + mdiobus_unregister(sc->mii_bus); out_unirq: free_irq(dev->irq, dev); @@ -2463,7 +2470,7 @@ static int sbmac_mii_probe(struct net_device *dev) int i; for (i = 0; i < PHY_MAX_ADDR; i++) { - phy_dev = sc->mii_bus.phy_map[i]; + phy_dev = sc->mii_bus->phy_map[i]; if (phy_dev) break; } @@ -2641,7 +2648,7 @@ static int sbmac_close(struct net_device *dev) phy_disconnect(sc->phy_dev); sc->phy_dev = NULL; - mdiobus_unregister(&sc->mii_bus); + mdiobus_unregister(sc->mii_bus); free_irq(dev->irq, dev); @@ -2750,6 +2757,7 @@ static int __exit sbmac_remove(struct platform_device *pldev) unregister_netdev(dev); sbmac_uninitctx(sc); + mdiobus_free(sc->mii_bus); iounmap(sc->sbm_base); free_netdev(dev); diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 1b3b7b66ffd8..b39d1cc1ef04 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -1166,7 +1166,7 @@ out_free_irq: kfree(mdp->mii_bus->irq); out_free_bus: - kfree(mdp->mii_bus); + free_mdio_bitbang(mdp->mii_bus); out_free_bitbang: kfree(bitbang); diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 1f2b1f2d73ae..4980b12b6219 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -424,7 +424,7 @@ struct tc35815_local { */ spinlock_t lock; - struct mii_bus mii_bus; + struct mii_bus *mii_bus; struct phy_device *phy_dev; int duplex; int speed; @@ -704,13 +704,13 @@ static int tc_mii_probe(struct net_device *dev) /* find the first phy */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (lp->mii_bus.phy_map[phy_addr]) { + if (lp->mii_bus->phy_map[phy_addr]) { if (phydev) { printk(KERN_ERR "%s: multiple PHYs found\n", dev->name); return -EINVAL; } - phydev = lp->mii_bus.phy_map[phy_addr]; + phydev = lp->mii_bus->phy_map[phy_addr]; break; } } @@ -762,23 +762,29 @@ static int tc_mii_init(struct net_device *dev) int err; int i; - lp->mii_bus.name = "tc35815_mii_bus"; - lp->mii_bus.read = tc_mdio_read; - lp->mii_bus.write = tc_mdio_write; - snprintf(lp->mii_bus.id, MII_BUS_ID_SIZE, "%x", - (lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn); - lp->mii_bus.priv = dev; - lp->mii_bus.parent = &lp->pci_dev->dev; - lp->mii_bus.irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!lp->mii_bus.irq) { + lp->mii_bus = mdiobus_alloc(); + if (lp->mii_bus == NULL) { err = -ENOMEM; goto err_out; } + lp->mii_bus->name = "tc35815_mii_bus"; + lp->mii_bus->read = tc_mdio_read; + lp->mii_bus->write = tc_mdio_write; + snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%x", + (lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn); + lp->mii_bus->priv = dev; + lp->mii_bus->parent = &lp->pci_dev->dev; + lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!lp->mii_bus->irq) { + err = -ENOMEM; + goto err_out_free_mii_bus; + } + for (i = 0; i < PHY_MAX_ADDR; i++) - lp->mii_bus.irq[i] = PHY_POLL; + lp->mii_bus->irq[i] = PHY_POLL; - err = mdiobus_register(&lp->mii_bus); + err = mdiobus_register(lp->mii_bus); if (err) goto err_out_free_mdio_irq; err = tc_mii_probe(dev); @@ -787,9 +793,11 @@ static int tc_mii_init(struct net_device *dev) return 0; err_out_unregister_bus: - mdiobus_unregister(&lp->mii_bus); + mdiobus_unregister(lp->mii_bus); err_out_free_mdio_irq: - kfree(lp->mii_bus.irq); + kfree(lp->mii_bus->irq); +err_out_free_mii_bus; + mdiobus_free(lp->mii_bus); err_out: return err; } @@ -961,8 +969,9 @@ static void __devexit tc35815_remove_one(struct pci_dev *pdev) struct tc35815_local *lp = netdev_priv(dev); phy_disconnect(lp->phy_dev); - mdiobus_unregister(&lp->mii_bus); - kfree(lp->mii_bus.irq); + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); unregister_netdev(dev); free_netdev(dev); pci_set_drvdata(pdev, NULL); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 4b8b75bcbde0..eb9f8f3638e1 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -876,7 +876,7 @@ static void tg3_mdio_config(struct tg3 *tp) { u32 val; - if (tp->mdio_bus.phy_map[PHY_ADDR]->interface != + if (tp->mdio_bus->phy_map[PHY_ADDR]->interface != PHY_INTERFACE_MODE_RGMII) return; @@ -920,9 +920,9 @@ static void tg3_mdio_config(struct tg3 *tp) static void tg3_mdio_start(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { - mutex_lock(&tp->mdio_bus.mdio_lock); + mutex_lock(&tp->mdio_bus->mdio_lock); tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_PAUSED; - mutex_unlock(&tp->mdio_bus.mdio_lock); + mutex_unlock(&tp->mdio_bus->mdio_lock); } tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL; @@ -936,9 +936,9 @@ static void tg3_mdio_start(struct tg3 *tp) static void tg3_mdio_stop(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { - mutex_lock(&tp->mdio_bus.mdio_lock); + mutex_lock(&tp->mdio_bus->mdio_lock); tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_PAUSED; - mutex_unlock(&tp->mdio_bus.mdio_lock); + mutex_unlock(&tp->mdio_bus->mdio_lock); } } @@ -947,7 +947,6 @@ static int tg3_mdio_init(struct tg3 *tp) int i; u32 reg; struct phy_device *phydev; - struct mii_bus *mdio_bus = &tp->mdio_bus; tg3_mdio_start(tp); @@ -955,21 +954,23 @@ static int tg3_mdio_init(struct tg3 *tp) (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED)) return 0; - memset(mdio_bus, 0, sizeof(*mdio_bus)); + tp->mdio_bus = mdiobus_alloc(); + if (tp->mdio_bus == NULL) + return -ENOMEM; - mdio_bus->name = "tg3 mdio bus"; - snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%x", + tp->mdio_bus->name = "tg3 mdio bus"; + snprintf(tp->mdio_bus->id, MII_BUS_ID_SIZE, "%x", (tp->pdev->bus->number << 8) | tp->pdev->devfn); - mdio_bus->priv = tp; - mdio_bus->parent = &tp->pdev->dev; - mdio_bus->read = &tg3_mdio_read; - mdio_bus->write = &tg3_mdio_write; - mdio_bus->reset = &tg3_mdio_reset; - mdio_bus->phy_mask = ~(1 << PHY_ADDR); - mdio_bus->irq = &tp->mdio_irq[0]; + tp->mdio_bus->priv = tp; + tp->mdio_bus->parent = &tp->pdev->dev; + tp->mdio_bus->read = &tg3_mdio_read; + tp->mdio_bus->write = &tg3_mdio_write; + tp->mdio_bus->reset = &tg3_mdio_reset; + tp->mdio_bus->phy_mask = ~(1 << PHY_ADDR); + tp->mdio_bus->irq = &tp->mdio_irq[0]; for (i = 0; i < PHY_MAX_ADDR; i++) - mdio_bus->irq[i] = PHY_POLL; + tp->mdio_bus->irq[i] = PHY_POLL; /* The bus registration will look for all the PHYs on the mdio bus. * Unfortunately, it does not ensure the PHY is powered up before @@ -979,7 +980,7 @@ static int tg3_mdio_init(struct tg3 *tp) if (tg3_readphy(tp, MII_BMCR, ®) || (reg & BMCR_PDOWN)) tg3_bmcr_reset(tp); - i = mdiobus_register(mdio_bus); + i = mdiobus_register(tp->mdio_bus); if (i) { printk(KERN_WARNING "%s: mdiobus_reg failed (0x%x)\n", tp->dev->name, i); @@ -988,7 +989,7 @@ static int tg3_mdio_init(struct tg3 *tp) tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED; - phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[PHY_ADDR]; switch (phydev->phy_id) { case TG3_PHY_ID_BCM50610: @@ -1014,7 +1015,8 @@ static void tg3_mdio_fini(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_INITED; - mdiobus_unregister(&tp->mdio_bus); + mdiobus_unregister(tp->mdio_bus); + mdiobus_free(tp->mdio_bus); tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_PAUSED; } } @@ -1220,7 +1222,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv) u32 old_tx_mode = tp->tx_mode; if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) - autoneg = tp->mdio_bus.phy_map[PHY_ADDR]->autoneg; + autoneg = tp->mdio_bus->phy_map[PHY_ADDR]->autoneg; else autoneg = tp->link_config.autoneg; @@ -1257,7 +1259,7 @@ static void tg3_adjust_link(struct net_device *dev) u8 oldflowctrl, linkmesg = 0; u32 mac_mode, lcl_adv, rmt_adv; struct tg3 *tp = netdev_priv(dev); - struct phy_device *phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + struct phy_device *phydev = tp->mdio_bus->phy_map[PHY_ADDR]; spin_lock(&tp->lock); @@ -1334,7 +1336,7 @@ static int tg3_phy_init(struct tg3 *tp) /* Bring the PHY back to a known state. */ tg3_bmcr_reset(tp); - phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[PHY_ADDR]; /* Attach the MAC to the PHY. */ phydev = phy_connect(tp->dev, phydev->dev.bus_id, tg3_adjust_link, @@ -1367,7 +1369,7 @@ static void tg3_phy_start(struct tg3 *tp) if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return; - phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[PHY_ADDR]; if (tp->link_config.phy_is_low_power) { tp->link_config.phy_is_low_power = 0; @@ -1387,13 +1389,13 @@ static void tg3_phy_stop(struct tg3 *tp) if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return; - phy_stop(tp->mdio_bus.phy_map[PHY_ADDR]); + phy_stop(tp->mdio_bus->phy_map[PHY_ADDR]); } static void tg3_phy_fini(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) { - phy_disconnect(tp->mdio_bus.phy_map[PHY_ADDR]); + phy_disconnect(tp->mdio_bus->phy_map[PHY_ADDR]); tp->tg3_flags3 &= ~TG3_FLG3_PHY_CONNECTED; } } @@ -2049,7 +2051,7 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) struct phy_device *phydev; u32 advertising; - phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[PHY_ADDR]; tp->link_config.phy_is_low_power = 1; @@ -8954,7 +8956,7 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - return phy_ethtool_gset(tp->mdio_bus.phy_map[PHY_ADDR], cmd); + return phy_ethtool_gset(tp->mdio_bus->phy_map[PHY_ADDR], cmd); } cmd->supported = (SUPPORTED_Autoneg); @@ -8995,7 +8997,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - return phy_ethtool_sset(tp->mdio_bus.phy_map[PHY_ADDR], cmd); + return phy_ethtool_sset(tp->mdio_bus->phy_map[PHY_ADDR], cmd); } if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) { @@ -9143,7 +9145,7 @@ static int tg3_nway_reset(struct net_device *dev) if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - r = phy_start_aneg(tp->mdio_bus.phy_map[PHY_ADDR]); + r = phy_start_aneg(tp->mdio_bus->phy_map[PHY_ADDR]); } else { u32 bmcr; @@ -9260,7 +9262,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam u32 newadv; struct phy_device *phydev; - phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[PHY_ADDR]; if (epause->rx_pause) { if (epause->tx_pause) @@ -10242,7 +10244,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - return phy_mii_ioctl(tp->mdio_bus.phy_map[PHY_ADDR], data, cmd); + return phy_mii_ioctl(tp->mdio_bus->phy_map[PHY_ADDR], data, cmd); } switch(cmd) { diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 6c7b5e303dbb..be252abe8985 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2556,7 +2556,7 @@ struct tg3 { int msi_cap; int pcix_cap; - struct mii_bus mdio_bus; + struct mii_bus *mdio_bus; int mdio_irq[PHY_MAX_ADDR]; /* PHY info */ diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c index 75b72fe1f23c..c001d261366b 100644 --- a/drivers/net/ucc_geth_mii.c +++ b/drivers/net/ucc_geth_mii.c @@ -141,8 +141,7 @@ static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *ma struct resource res; int k, err = 0; - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); - + new_bus = mdiobus_alloc(); if (NULL == new_bus) return -ENOMEM; @@ -235,7 +234,7 @@ bus_register_fail: ioremap_fail: kfree(new_bus->irq); reg_map_fail: - kfree(new_bus); + mdiobus_free(new_bus); return err; } @@ -251,7 +250,7 @@ static int uec_mdio_remove(struct of_device *ofdev) iounmap((void __iomem *)bus->priv); bus->priv = NULL; - kfree(bus); + mdiobus_free(bus); return 0; } diff --git a/include/linux/phy.h b/include/linux/phy.h index 87499bdedc6f..ca4a83c4c537 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -408,8 +408,10 @@ void phy_start(struct phy_device *phydev); void phy_stop(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); +struct mii_bus *mdiobus_alloc(void); int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); +void mdiobus_free(struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); void phy_sanitize_settings(struct phy_device *phydev); -- cgit v1.2.3 From 46abc02175b3c246dd5141d878f565a8725060c9 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Wed, 8 Oct 2008 16:33:40 -0700 Subject: phylib: give mdio buses a device tree presence Introduce the mdio_bus class, and give each 'struct mii_bus' its own 'struct device', so that mii_bus objects are represented in the device tree and can be found by querying the device tree. Signed-off-by: Lennert Buytenhek Acked-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 75 +++++++++++++++++++++++++++++++++++++++++++--- include/linux/phy.h | 8 +++++ 2 files changed, 79 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index d206691f15d8..fb4c0965b152 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -43,10 +43,34 @@ */ struct mii_bus *mdiobus_alloc(void) { - return kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + struct mii_bus *bus; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (bus != NULL) + bus->state = MDIOBUS_ALLOCATED; + + return bus; } EXPORT_SYMBOL(mdiobus_alloc); +/** + * mdiobus_release - mii_bus device release callback + * + * Description: called when the last reference to an mii_bus is + * dropped, to free the underlying memory. + */ +static void mdiobus_release(struct device *d) +{ + struct mii_bus *bus = to_mii_bus(d); + BUG_ON(bus->state != MDIOBUS_RELEASED); + kfree(bus); +} + +static struct class mdio_bus_class = { + .name = "mdio_bus", + .dev_release = mdiobus_release, +}; + /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -66,6 +90,22 @@ int mdiobus_register(struct mii_bus *bus) NULL == bus->write) return -EINVAL; + BUG_ON(bus->state != MDIOBUS_ALLOCATED && + bus->state != MDIOBUS_UNREGISTERED); + + bus->dev.parent = bus->parent; + bus->dev.class = &mdio_bus_class; + bus->dev.groups = NULL; + memcpy(bus->dev.bus_id, bus->id, MII_BUS_ID_SIZE); + + err = device_register(&bus->dev); + if (err) { + printk(KERN_ERR "mii_bus %s failed to register\n", bus->id); + return -EINVAL; + } + + bus->state = MDIOBUS_REGISTERED; + mutex_init(&bus->mdio_lock); if (bus->reset) @@ -92,6 +132,10 @@ void mdiobus_unregister(struct mii_bus *bus) { int i; + BUG_ON(bus->state != MDIOBUS_REGISTERED); + bus->state = MDIOBUS_UNREGISTERED; + + device_unregister(&bus->dev); for (i = 0; i < PHY_MAX_ADDR; i++) { if (bus->phy_map[i]) device_unregister(&bus->phy_map[i]->dev); @@ -103,11 +147,24 @@ EXPORT_SYMBOL(mdiobus_unregister); * mdiobus_free - free a struct mii_bus * @bus: mii_bus to free * - * This function frees the mii_bus. + * This function releases the reference to the underlying device + * object in the mii_bus. If this is the last reference, the mii_bus + * will be freed. */ void mdiobus_free(struct mii_bus *bus) { - kfree(bus); + /* + * For compatibility with error handling in drivers. + */ + if (bus->state == MDIOBUS_ALLOCATED) { + kfree(bus); + return; + } + + BUG_ON(bus->state != MDIOBUS_UNREGISTERED); + bus->state = MDIOBUS_RELEASED; + + put_device(&bus->dev); } EXPORT_SYMBOL(mdiobus_free); @@ -205,10 +262,20 @@ EXPORT_SYMBOL(mdio_bus_type); int __init mdio_bus_init(void) { - return bus_register(&mdio_bus_type); + int ret; + + ret = class_register(&mdio_bus_class); + if (!ret) { + ret = bus_register(&mdio_bus_type); + if (ret) + class_unregister(&mdio_bus_class); + } + + return ret; } void mdio_bus_exit(void) { + class_unregister(&mdio_bus_class); bus_unregister(&mdio_bus_type); } diff --git a/include/linux/phy.h b/include/linux/phy.h index ca4a83c4c537..891f27f9137e 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -100,6 +100,13 @@ struct mii_bus { struct mutex mdio_lock; struct device *parent; + enum { + MDIOBUS_ALLOCATED = 1, + MDIOBUS_REGISTERED, + MDIOBUS_UNREGISTERED, + MDIOBUS_RELEASED, + } state; + struct device dev; /* list of all PHYs on bus */ struct phy_device *phy_map[PHY_MAX_ADDR]; @@ -113,6 +120,7 @@ struct mii_bus { */ int *irq; }; +#define to_mii_bus(d) container_of(d, struct mii_bus, dev) #define PHY_INTERRUPT_DISABLED 0x0 #define PHY_INTERRUPT_ENABLED 0x80000000 -- cgit v1.2.3 From 2e888103295f47b8fcbf7e9bb8c5da97dd2ecd76 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 29 Sep 2008 17:12:35 +0000 Subject: phylib: add mdiobus_{read,write} Add mdiobus_{read,write} routines to allow direct reading/writing of registers on an mii bus without having to go through the PHY abstraction, and make phy_{read,write} use these primitives. Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 49 ---------------------------------------------- include/linux/phy.h | 46 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 87 insertions(+), 57 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index fb4c0965b152..6671e2da0d57 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -210,6 +210,55 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) } EXPORT_SYMBOL(mdiobus_scan); +/** + * mdiobus_read - Convenience function for reading a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to read + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum) +{ + int retval; + + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); + retval = bus->read(bus, addr, regnum); + mutex_unlock(&bus->mdio_lock); + + return retval; +} +EXPORT_SYMBOL(mdiobus_read); + +/** + * mdiobus_write - Convenience function for writing a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val) +{ + int err; + + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); + err = bus->write(bus, addr, regnum, val); + mutex_unlock(&bus->mdio_lock); + + return err; +} +EXPORT_SYMBOL(mdiobus_write); + /** * mdio_bus_match - determine if given PHY driver supports the given PHY device * @dev: target PHY device diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 20cc82c78137..df4e6257d4a7 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -57,55 +57,6 @@ void phy_print_status(struct phy_device *phydev) EXPORT_SYMBOL(phy_print_status); -/** - * phy_read - Convenience function for reading a given PHY register - * @phydev: the phy_device struct - * @regnum: register number to read - * - * NOTE: MUST NOT be called from interrupt context, - * because the bus read/write functions may wait for an interrupt - * to conclude the operation. - */ -int phy_read(struct phy_device *phydev, u16 regnum) -{ - int retval; - struct mii_bus *bus = phydev->bus; - - BUG_ON(in_interrupt()); - - mutex_lock(&bus->mdio_lock); - retval = bus->read(bus, phydev->addr, regnum); - mutex_unlock(&bus->mdio_lock); - - return retval; -} -EXPORT_SYMBOL(phy_read); - -/** - * phy_write - Convenience function for writing a given PHY register - * @phydev: the phy_device struct - * @regnum: register number to write - * @val: value to write to @regnum - * - * NOTE: MUST NOT be called from interrupt context, - * because the bus read/write functions may wait for an interrupt - * to conclude the operation. - */ -int phy_write(struct phy_device *phydev, u16 regnum, u16 val) -{ - int err; - struct mii_bus *bus = phydev->bus; - - BUG_ON(in_interrupt()); - - mutex_lock(&bus->mdio_lock); - err = bus->write(bus, phydev->addr, regnum, val); - mutex_unlock(&bus->mdio_lock); - - return err; -} -EXPORT_SYMBOL(phy_write); - /** * phy_clear_interrupt - Ack the phy device's interrupt * @phydev: the phy_device struct diff --git a/include/linux/phy.h b/include/linux/phy.h index 891f27f9137e..77c4ed60b982 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -122,6 +122,15 @@ struct mii_bus { }; #define to_mii_bus(d) container_of(d, struct mii_bus, dev) +struct mii_bus *mdiobus_alloc(void); +int mdiobus_register(struct mii_bus *bus); +void mdiobus_unregister(struct mii_bus *bus); +void mdiobus_free(struct mii_bus *bus); +struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); +int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum); +int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val); + + #define PHY_INTERRUPT_DISABLED 0x0 #define PHY_INTERRUPT_ENABLED 0x80000000 @@ -399,8 +408,35 @@ struct phy_fixup { int (*run)(struct phy_device *phydev); }; -int phy_read(struct phy_device *phydev, u16 regnum); -int phy_write(struct phy_device *phydev, u16 regnum, u16 val); +/** + * phy_read - Convenience function for reading a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to read + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +static inline int phy_read(struct phy_device *phydev, u16 regnum) +{ + return mdiobus_read(phydev->bus, phydev->addr, regnum); +} + +/** + * phy_write - Convenience function for writing a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +static inline int phy_write(struct phy_device *phydev, u16 regnum, u16 val) +{ + return mdiobus_write(phydev->bus, phydev->addr, regnum, val); +} + int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id); struct phy_device* get_phy_device(struct mii_bus *bus, int addr); int phy_clear_interrupt(struct phy_device *phydev); @@ -416,12 +452,6 @@ void phy_start(struct phy_device *phydev); void phy_stop(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); -struct mii_bus *mdiobus_alloc(void); -int mdiobus_register(struct mii_bus *bus); -void mdiobus_unregister(struct mii_bus *bus); -void mdiobus_free(struct mii_bus *bus); -struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); - void phy_sanitize_settings(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev); int phy_enable_interrupts(struct phy_device *phydev); -- cgit v1.2.3 From 91da11f870f00a3322b81c73042291d7f0be5a17 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 7 Oct 2008 13:44:02 +0000 Subject: net: Distributed Switch Architecture protocol support Distributed Switch Architecture is a protocol for managing hardware switch chips. It consists of a set of MII management registers and commands to configure the switch, and an ethernet header format to signal which of the ports of the switch a packet was received from or is intended to be sent to. The switches that this driver supports are typically embedded in access points and routers, and a typical setup with a DSA switch looks something like this: +-----------+ +-----------+ | | RGMII | | | +-------+ +------ 1000baseT MDI ("WAN") | | | 6-port +------ 1000baseT MDI ("LAN1") | CPU | | ethernet +------ 1000baseT MDI ("LAN2") | |MIImgmt| switch +------ 1000baseT MDI ("LAN3") | +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4") | | | | +-----------+ +-----------+ The switch driver presents each port on the switch as a separate network interface to Linux, polls the switch to maintain software link state of those ports, forwards MII management interface accesses to those network interfaces (e.g. as done by ethtool) to the switch, and exposes the switch's hardware statistics counters via the appropriate Linux kernel interfaces. This initial patch supports the MII management interface register layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and supports the "Ethertype DSA" packet tagging format. (There is no officially registered ethertype for the Ethertype DSA packet format, so we just grab a random one. The ethertype to use is programmed into the switch, and the switch driver uses the value of ETH_P_EDSA for this, so this define can be changed at any time in the future if the one we chose is allocated to another protocol or if Ethertype DSA gets its own officially registered ethertype, and everything will continue to work.) Signed-off-by: Lennert Buytenhek Tested-by: Nicolas Pitre Tested-by: Byron Bradley Tested-by: Tim Ellis Tested-by: Peter van Valderen Tested-by: Dirk Teurlings Signed-off-by: David S. Miller --- include/linux/if_ether.h | 1 + include/linux/netdevice.h | 3 + include/net/dsa.h | 34 ++++ net/Kconfig | 1 + net/Makefile | 1 + net/dsa/Kconfig | 31 ++++ net/dsa/Makefile | 9 + net/dsa/dsa.c | 369 ++++++++++++++++++++++++++++++++++++++++ net/dsa/dsa_priv.h | 110 ++++++++++++ net/dsa/mv88e6123_61_65.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++ net/dsa/mv88e6xxx.c | 377 +++++++++++++++++++++++++++++++++++++++++ net/dsa/mv88e6xxx.h | 77 +++++++++ net/dsa/slave.c | 288 ++++++++++++++++++++++++++++++++ net/dsa/tag_edsa.c | 213 +++++++++++++++++++++++ 14 files changed, 1931 insertions(+) create mode 100644 include/net/dsa.h create mode 100644 net/dsa/Kconfig create mode 100644 net/dsa/Makefile create mode 100644 net/dsa/dsa.c create mode 100644 net/dsa/dsa_priv.h create mode 100644 net/dsa/mv88e6123_61_65.c create mode 100644 net/dsa/mv88e6xxx.c create mode 100644 net/dsa/mv88e6xxx.h create mode 100644 net/dsa/slave.c create mode 100644 net/dsa/tag_edsa.c (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 723a1c5fbc6c..2140aacb6338 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -77,6 +77,7 @@ #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ #define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ /* * Non DIX types. Won't clash for 1500 types. diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9cfd20be8b7f..794eeb4b3462 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -607,6 +607,9 @@ struct net_device /* Protocol specific pointers */ +#ifdef CONFIG_NET_DSA + void *dsa_ptr; /* dsa specific data */ +#endif void *atalk_ptr; /* AppleTalk link */ void *ip_ptr; /* IPv4 specific data */ void *dn_ptr; /* DECnet specific data */ diff --git a/include/net/dsa.h b/include/net/dsa.h new file mode 100644 index 000000000000..dc4784f54520 --- /dev/null +++ b/include/net/dsa.h @@ -0,0 +1,34 @@ +/* + * include/net/dsa.h - Driver for Distributed Switch Architecture switch chips + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#ifndef __LINUX_NET_DSA_H +#define __LINUX_NET_DSA_H + +#define DSA_MAX_PORTS 12 + +struct dsa_platform_data { + /* + * Reference to a Linux network interface that connects + * to the switch chip. + */ + struct device *netdev; + + /* + * How to access the switch configuration registers, and + * the names of the switch ports (use "cpu" to designate + * the switch port that the cpu is connected to). + */ + struct device *mii_bus; + int sw_addr; + char *port_names[DSA_MAX_PORTS]; +}; + + +#endif diff --git a/net/Kconfig b/net/Kconfig index 9103a16a77be..d789d79551ae 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -180,6 +180,7 @@ source "net/tipc/Kconfig" source "net/atm/Kconfig" source "net/802/Kconfig" source "net/bridge/Kconfig" +source "net/dsa/Kconfig" source "net/8021q/Kconfig" source "net/decnet/Kconfig" source "net/llc/Kconfig" diff --git a/net/Makefile b/net/Makefile index acaf819f24aa..27d1f10dc0e0 100644 --- a/net/Makefile +++ b/net/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_NET_SCHED) += sched/ obj-$(CONFIG_BRIDGE) += bridge/ +obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ obj-$(CONFIG_ATALK) += appletalk/ obj-$(CONFIG_WAN_ROUTER) += wanrouter/ diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig new file mode 100644 index 000000000000..7cf55e5eb39f --- /dev/null +++ b/net/dsa/Kconfig @@ -0,0 +1,31 @@ +menuconfig NET_DSA + bool "Distributed Switch Architecture support" + default n + depends on EXPERIMENTAL + ---help--- + This allows you to use hardware switch chips that use + the Distributed Switch Architecture. + + +if NET_DSA + +# tagging formats +config NET_DSA_TAG_EDSA + bool + default n + + +# switch drivers +config NET_DSA_MV88E6XXX + bool + default n + +config NET_DSA_MV88E6123_61_65 + bool "Marvell 88E6123/6161/6165 ethernet switch chip support" + select NET_DSA_MV88E6XXX + select NET_DSA_TAG_EDSA + ---help--- + This enables support for the Marvell 88E6123/6161/6165 + ethernet switch chips. + +endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile new file mode 100644 index 000000000000..b59a6f6bcf56 --- /dev/null +++ b/net/dsa/Makefile @@ -0,0 +1,9 @@ +# tagging formats +obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o + +# switch drivers +obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o +obj-$(CONFIG_NET_DSA_MV88E6123_61_65) += mv88e6123_61_65.o + +# the core +obj-$(CONFIG_NET_DSA) += dsa.o slave.o diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c new file mode 100644 index 000000000000..6cc5be2ec7f1 --- /dev/null +++ b/net/dsa/dsa.c @@ -0,0 +1,369 @@ +/* + * net/dsa/dsa.c - Hardware switch handling + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include +#include "dsa_priv.h" + +char dsa_driver_version[] = "0.1"; + + +/* switch driver registration ***********************************************/ +static DEFINE_MUTEX(dsa_switch_drivers_mutex); +static LIST_HEAD(dsa_switch_drivers); + +void register_switch_driver(struct dsa_switch_driver *drv) +{ + mutex_lock(&dsa_switch_drivers_mutex); + list_add_tail(&drv->list, &dsa_switch_drivers); + mutex_unlock(&dsa_switch_drivers_mutex); +} + +void unregister_switch_driver(struct dsa_switch_driver *drv) +{ + mutex_lock(&dsa_switch_drivers_mutex); + list_del_init(&drv->list); + mutex_unlock(&dsa_switch_drivers_mutex); +} + +static struct dsa_switch_driver * +dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name) +{ + struct dsa_switch_driver *ret; + struct list_head *list; + char *name; + + ret = NULL; + name = NULL; + + mutex_lock(&dsa_switch_drivers_mutex); + list_for_each(list, &dsa_switch_drivers) { + struct dsa_switch_driver *drv; + + drv = list_entry(list, struct dsa_switch_driver, list); + + name = drv->probe(bus, sw_addr); + if (name != NULL) { + ret = drv; + break; + } + } + mutex_unlock(&dsa_switch_drivers_mutex); + + *_name = name; + + return ret; +} + + +/* basic switch operations **************************************************/ +static struct dsa_switch * +dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, + struct mii_bus *bus, struct net_device *dev) +{ + struct dsa_switch *ds; + int ret; + struct dsa_switch_driver *drv; + char *name; + int i; + + /* + * Probe for switch model. + */ + drv = dsa_switch_probe(bus, pd->sw_addr, &name); + if (drv == NULL) { + printk(KERN_ERR "%s: could not detect attached switch\n", + dev->name); + return ERR_PTR(-EINVAL); + } + printk(KERN_INFO "%s: detected a %s switch\n", dev->name, name); + + + /* + * Allocate and initialise switch state. + */ + ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL); + if (ds == NULL) + return ERR_PTR(-ENOMEM); + + ds->pd = pd; + ds->master_netdev = dev; + ds->master_mii_bus = bus; + + ds->drv = drv; + ds->tag_protocol = drv->tag_protocol; + + + /* + * Validate supplied switch configuration. + */ + ds->cpu_port = -1; + for (i = 0; i < DSA_MAX_PORTS; i++) { + char *name; + + name = pd->port_names[i]; + if (name == NULL) + continue; + + if (!strcmp(name, "cpu")) { + if (ds->cpu_port != -1) { + printk(KERN_ERR "multiple cpu ports?!\n"); + ret = -EINVAL; + goto out; + } + ds->cpu_port = i; + } else { + ds->valid_port_mask |= 1 << i; + } + } + + if (ds->cpu_port == -1) { + printk(KERN_ERR "no cpu port?!\n"); + ret = -EINVAL; + goto out; + } + + + /* + * If we use a tagging format that doesn't have an ethertype + * field, make sure that all packets from this point on get + * sent to the tag format's receive function. (Which will + * discard received packets until we set ds->ports[] below.) + */ + wmb(); + dev->dsa_ptr = (void *)ds; + + + /* + * Do basic register setup. + */ + ret = drv->setup(ds); + if (ret < 0) + goto out; + + ret = drv->set_addr(ds, dev->dev_addr); + if (ret < 0) + goto out; + + ds->slave_mii_bus = mdiobus_alloc(); + if (ds->slave_mii_bus == NULL) { + ret = -ENOMEM; + goto out; + } + dsa_slave_mii_bus_init(ds); + + ret = mdiobus_register(ds->slave_mii_bus); + if (ret < 0) + goto out_free; + + + /* + * Create network devices for physical switch ports. + */ + wmb(); + for (i = 0; i < DSA_MAX_PORTS; i++) { + struct net_device *slave_dev; + + if (!(ds->valid_port_mask & (1 << i))) + continue; + + slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]); + if (slave_dev == NULL) { + printk(KERN_ERR "%s: can't create dsa slave " + "device for port %d(%s)\n", + dev->name, i, pd->port_names[i]); + continue; + } + + ds->ports[i] = slave_dev; + } + + return ds; + +out_free: + mdiobus_free(ds->slave_mii_bus); +out: + dev->dsa_ptr = NULL; + kfree(ds); + return ERR_PTR(ret); +} + +static void dsa_switch_destroy(struct dsa_switch *ds) +{ +} + + +/* link polling *************************************************************/ +static void dsa_link_poll_work(struct work_struct *ugly) +{ + struct dsa_switch *ds; + + ds = container_of(ugly, struct dsa_switch, link_poll_work); + + ds->drv->poll_link(ds); + mod_timer(&ds->link_poll_timer, round_jiffies(jiffies + HZ)); +} + +static void dsa_link_poll_timer(unsigned long _ds) +{ + struct dsa_switch *ds = (void *)_ds; + + schedule_work(&ds->link_poll_work); +} + + +/* platform driver init and cleanup *****************************************/ +static int dev_is_class(struct device *dev, void *class) +{ + if (dev->class != NULL && !strcmp(dev->class->name, class)) + return 1; + + return 0; +} + +static struct device *dev_find_class(struct device *parent, char *class) +{ + if (dev_is_class(parent, class)) { + get_device(parent); + return parent; + } + + return device_find_child(parent, class, dev_is_class); +} + +static struct mii_bus *dev_to_mii_bus(struct device *dev) +{ + struct device *d; + + d = dev_find_class(dev, "mdio_bus"); + if (d != NULL) { + struct mii_bus *bus; + + bus = to_mii_bus(d); + put_device(d); + + return bus; + } + + return NULL; +} + +static struct net_device *dev_to_net_device(struct device *dev) +{ + struct device *d; + + d = dev_find_class(dev, "net"); + if (d != NULL) { + struct net_device *nd; + + nd = to_net_dev(d); + dev_hold(nd); + put_device(d); + + return nd; + } + + return NULL; +} + +static int dsa_probe(struct platform_device *pdev) +{ + static int dsa_version_printed; + struct dsa_platform_data *pd = pdev->dev.platform_data; + struct net_device *dev; + struct mii_bus *bus; + struct dsa_switch *ds; + + if (!dsa_version_printed++) + printk(KERN_NOTICE "Distributed Switch Architecture " + "driver version %s\n", dsa_driver_version); + + if (pd == NULL || pd->mii_bus == NULL || pd->netdev == NULL) + return -EINVAL; + + bus = dev_to_mii_bus(pd->mii_bus); + if (bus == NULL) + return -EINVAL; + + dev = dev_to_net_device(pd->netdev); + if (dev == NULL) + return -EINVAL; + + if (dev->dsa_ptr != NULL) { + dev_put(dev); + return -EEXIST; + } + + ds = dsa_switch_setup(&pdev->dev, pd, bus, dev); + if (IS_ERR(ds)) { + dev_put(dev); + return PTR_ERR(ds); + } + + if (ds->drv->poll_link != NULL) { + INIT_WORK(&ds->link_poll_work, dsa_link_poll_work); + init_timer(&ds->link_poll_timer); + ds->link_poll_timer.data = (unsigned long)ds; + ds->link_poll_timer.function = dsa_link_poll_timer; + ds->link_poll_timer.expires = round_jiffies(jiffies + HZ); + add_timer(&ds->link_poll_timer); + } + + platform_set_drvdata(pdev, ds); + + return 0; +} + +static int dsa_remove(struct platform_device *pdev) +{ + struct dsa_switch *ds = platform_get_drvdata(pdev); + + if (ds->drv->poll_link != NULL) + del_timer_sync(&ds->link_poll_timer); + + flush_scheduled_work(); + + dsa_switch_destroy(ds); + + return 0; +} + +static void dsa_shutdown(struct platform_device *pdev) +{ +} + +static struct platform_driver dsa_driver = { + .probe = dsa_probe, + .remove = dsa_remove, + .shutdown = dsa_shutdown, + .driver = { + .name = "dsa", + .owner = THIS_MODULE, + }, +}; + +static int __init dsa_init_module(void) +{ + return platform_driver_register(&dsa_driver); +} +module_init(dsa_init_module); + +static void __exit dsa_cleanup_module(void) +{ + platform_driver_unregister(&dsa_driver); +} +module_exit(dsa_cleanup_module); + +MODULE_AUTHOR("Lennert Buytenhek ") +MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dsa"); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h new file mode 100644 index 000000000000..21ee9052079a --- /dev/null +++ b/net/dsa/dsa_priv.h @@ -0,0 +1,110 @@ +/* + * net/dsa/dsa_priv.h - Hardware switch handling + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#ifndef __DSA_PRIV_H +#define __DSA_PRIV_H + +#include +#include +#include +#include +#include + +struct dsa_switch { + /* + * Configuration data for the platform device that owns + * this dsa switch instance. + */ + struct dsa_platform_data *pd; + + /* + * References to network device and mii bus to use. + */ + struct net_device *master_netdev; + struct mii_bus *master_mii_bus; + + /* + * The used switch driver and frame tagging type. + */ + struct dsa_switch_driver *drv; + __be16 tag_protocol; + + /* + * Slave mii_bus and devices for the individual ports. + */ + int cpu_port; + u32 valid_port_mask; + struct mii_bus *slave_mii_bus; + struct net_device *ports[DSA_MAX_PORTS]; + + /* + * Link state polling. + */ + struct work_struct link_poll_work; + struct timer_list link_poll_timer; +}; + +struct dsa_slave_priv { + struct net_device *dev; + struct dsa_switch *parent; + int port; + struct phy_device *phy; +}; + +struct dsa_switch_driver { + struct list_head list; + + __be16 tag_protocol; + int priv_size; + + /* + * Probing and setup. + */ + char *(*probe)(struct mii_bus *bus, int sw_addr); + int (*setup)(struct dsa_switch *ds); + int (*set_addr)(struct dsa_switch *ds, u8 *addr); + + /* + * Access to the switch's PHY registers. + */ + int (*phy_read)(struct dsa_switch *ds, int port, int regnum); + int (*phy_write)(struct dsa_switch *ds, int port, + int regnum, u16 val); + + /* + * Link state polling and IRQ handling. + */ + void (*poll_link)(struct dsa_switch *ds); + + /* + * ethtool hardware statistics. + */ + void (*get_strings)(struct dsa_switch *ds, int port, uint8_t *data); + void (*get_ethtool_stats)(struct dsa_switch *ds, + int port, uint64_t *data); + int (*get_sset_count)(struct dsa_switch *ds); +}; + +/* dsa.c */ +extern char dsa_driver_version[]; +void register_switch_driver(struct dsa_switch_driver *type); +void unregister_switch_driver(struct dsa_switch_driver *type); + +/* slave.c */ +void dsa_slave_mii_bus_init(struct dsa_switch *ds); +struct net_device *dsa_slave_create(struct dsa_switch *ds, + struct device *parent, + int port, char *name); + +/* tag_edsa.c */ +int edsa_xmit(struct sk_buff *skb, struct net_device *dev); + + +#endif diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c new file mode 100644 index 000000000000..147818cc706e --- /dev/null +++ b/net/dsa/mv88e6123_61_65.c @@ -0,0 +1,417 @@ +/* + * net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include "dsa_priv.h" +#include "mv88e6xxx.h" + +static char *mv88e6123_61_65_probe(struct mii_bus *bus, int sw_addr) +{ + int ret; + + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + if (ret >= 0) { + ret &= 0xfff0; + if (ret == 0x1210) + return "Marvell 88E6123"; + if (ret == 0x1610) + return "Marvell 88E6161"; + if (ret == 0x1650) + return "Marvell 88E6165"; + } + + return NULL; +} + +static int mv88e6123_61_65_switch_reset(struct dsa_switch *ds) +{ + int i; + int ret; + + /* + * Set all ports to the disabled state. + */ + for (i = 0; i < 8; i++) { + ret = REG_READ(REG_PORT(i), 0x04); + REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); + } + + /* + * Wait for transmit queues to drain. + */ + msleep(2); + + /* + * Reset the switch. + */ + REG_WRITE(REG_GLOBAL, 0x04, 0xc400); + + /* + * Wait up to one second for reset to complete. + */ + for (i = 0; i < 1000; i++) { + ret = REG_READ(REG_GLOBAL, 0x00); + if ((ret & 0xc800) == 0xc800) + break; + + msleep(1); + } + if (i == 1000) + return -ETIMEDOUT; + + return 0; +} + +static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) +{ + int ret; + int i; + + /* + * Disable the PHY polling unit (since there won't be any + * external PHYs to poll), don't discard packets with + * excessive collisions, and mask all interrupt sources. + */ + REG_WRITE(REG_GLOBAL, 0x04, 0x0000); + + /* + * Set the default address aging time to 5 minutes, and + * enable address learn messages to be sent to all message + * ports. + */ + REG_WRITE(REG_GLOBAL, 0x0a, 0x0148); + + /* + * Configure the priority mapping registers. + */ + ret = mv88e6xxx_config_prio(ds); + if (ret < 0) + return ret; + + /* + * Configure the cpu port, and configure the cpu port as the + * port to which ingress and egress monitor frames are to be + * sent. + */ + REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1110)); + + /* + * Disable remote management for now, and set the switch's + * DSA device number to zero. + */ + REG_WRITE(REG_GLOBAL, 0x1c, 0x0000); + + /* + * Send all frames with destination addresses matching + * 01:80:c2:00:00:2x to the CPU port. + */ + REG_WRITE(REG_GLOBAL2, 0x02, 0xffff); + + /* + * Send all frames with destination addresses matching + * 01:80:c2:00:00:0x to the CPU port. + */ + REG_WRITE(REG_GLOBAL2, 0x03, 0xffff); + + /* + * Disable the loopback filter, disable flow control + * messages, disable flood broadcast override, disable + * removing of provider tags, disable ATU age violation + * interrupts, disable tag flow control, force flow + * control priority to the highest, and send all special + * multicast frames to the CPU at the highest priority. + */ + REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff); + + /* + * Map all DSA device IDs to the CPU port. + */ + for (i = 0; i < 32; i++) + REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port); + + /* + * Clear all trunk masks. + */ + for (i = 0; i < 8; i++) + REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff); + + /* + * Clear all trunk mappings. + */ + for (i = 0; i < 16; i++) + REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11)); + + /* + * Disable ingress rate limiting by resetting all ingress + * rate limit registers to their initial state. + */ + for (i = 0; i < 6; i++) + REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8)); + + /* + * Initialise cross-chip port VLAN table to reset defaults. + */ + REG_WRITE(REG_GLOBAL2, 0x0b, 0x9000); + + /* + * Clear the priority override table. + */ + for (i = 0; i < 16; i++) + REG_WRITE(REG_GLOBAL2, 0x0f, 0x8000 | (i << 8)); + + /* @@@ initialise AVB (22/23) watchdog (27) sdet (29) registers */ + + return 0; +} + +static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) +{ + int addr = REG_PORT(p); + + /* + * MAC Forcing register: don't force link, speed, duplex + * or flow control state to any particular values. + */ + REG_WRITE(addr, 0x01, 0x0003); + + /* + * Do not limit the period of time that this port can be + * paused for by the remote end or the period of time that + * this port can pause the remote end. + */ + REG_WRITE(addr, 0x02, 0x0000); + + /* + * Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, + * configure the EDSA tagging mode if this is the CPU port, + * disable Header mode, enable IGMP/MLD snooping, disable VLAN + * tunneling, determine priority by looking at 802.1p and IP + * priority fields (IP prio has precedence), and set STP state + * to Forwarding. Finally, if this is the CPU port, additionally + * enable forwarding of unknown unicast and multicast addresses. + */ + REG_WRITE(addr, 0x04, + (p == ds->cpu_port) ? 0x373f : 0x0433); + + /* + * Port Control 1: disable trunking. Also, if this is the + * CPU port, enable learn messages to be sent to this port. + */ + REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000); + + /* + * Port based VLAN map: give each port its own address + * database, allow the CPU port to talk to each of the 'real' + * ports, and allow each of the 'real' ports to only talk to + * the CPU port. + */ + REG_WRITE(addr, 0x06, + ((p & 0xf) << 12) | + ((p == ds->cpu_port) ? + ds->valid_port_mask : + (1 << ds->cpu_port))); + + /* + * Default VLAN ID and priority: don't set a default VLAN + * ID, and set the default packet priority to zero. + */ + REG_WRITE(addr, 0x07, 0x0000); + + /* + * Port Control 2: don't force a good FCS, set the maximum + * frame size to 10240 bytes, don't let the switch add or + * strip 802.1q tags, don't discard tagged or untagged frames + * on this port, do a destination address lookup on all + * received packets as usual, disable ARP mirroring and don't + * send a copy of all transmitted/received frames on this port + * to the CPU. + */ + REG_WRITE(addr, 0x08, 0x2080); + + /* + * Egress rate control: disable egress rate control. + */ + REG_WRITE(addr, 0x09, 0x0001); + + /* + * Egress rate control 2: disable egress rate control. + */ + REG_WRITE(addr, 0x0a, 0x0000); + + /* + * Port Association Vector: when learning source addresses + * of packets, add the address to the address database using + * a port bitmap that has only the bit for this port set and + * the other bits clear. + */ + REG_WRITE(addr, 0x0b, 1 << p); + + /* + * Port ATU control: disable limiting the number of address + * database entries that this port is allowed to use. + */ + REG_WRITE(addr, 0x0c, 0x0000); + + /* + * Priorit Override: disable DA, SA and VTU priority override. + */ + REG_WRITE(addr, 0x0d, 0x0000); + + /* + * Port Ethertype: use the Ethertype DSA Ethertype value. + */ + REG_WRITE(addr, 0x0f, ETH_P_EDSA); + + /* + * Tag Remap: use an identity 802.1p prio -> switch prio + * mapping. + */ + REG_WRITE(addr, 0x18, 0x3210); + + /* + * Tag Remap 2: use an identity 802.1p prio -> switch prio + * mapping. + */ + REG_WRITE(addr, 0x19, 0x7654); + + return 0; +} + +static int mv88e6123_61_65_setup(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); + int i; + int ret; + + mutex_init(&ps->smi_mutex); + mutex_init(&ps->stats_mutex); + + ret = mv88e6123_61_65_switch_reset(ds); + if (ret < 0) + return ret; + + /* @@@ initialise vtu and atu */ + + ret = mv88e6123_61_65_setup_global(ds); + if (ret < 0) + return ret; + + for (i = 0; i < 6; i++) { + ret = mv88e6123_61_65_setup_port(ds, i); + if (ret < 0) + return ret; + } + + return 0; +} + +static int mv88e6123_61_65_port_to_phy_addr(int port) +{ + if (port >= 0 && port <= 4) + return port; + return -1; +} + +static int +mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + int addr = mv88e6123_61_65_port_to_phy_addr(port); + return mv88e6xxx_phy_read(ds, addr, regnum); +} + +static int +mv88e6123_61_65_phy_write(struct dsa_switch *ds, + int port, int regnum, u16 val) +{ + int addr = mv88e6123_61_65_port_to_phy_addr(port); + return mv88e6xxx_phy_write(ds, addr, regnum, val); +} + +static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = { + { "in_good_octets", 8, 0x00, }, + { "in_bad_octets", 4, 0x02, }, + { "in_unicast", 4, 0x04, }, + { "in_broadcasts", 4, 0x06, }, + { "in_multicasts", 4, 0x07, }, + { "in_pause", 4, 0x16, }, + { "in_undersize", 4, 0x18, }, + { "in_fragments", 4, 0x19, }, + { "in_oversize", 4, 0x1a, }, + { "in_jabber", 4, 0x1b, }, + { "in_rx_error", 4, 0x1c, }, + { "in_fcs_error", 4, 0x1d, }, + { "out_octets", 8, 0x0e, }, + { "out_unicast", 4, 0x10, }, + { "out_broadcasts", 4, 0x13, }, + { "out_multicasts", 4, 0x12, }, + { "out_pause", 4, 0x15, }, + { "excessive", 4, 0x11, }, + { "collisions", 4, 0x1e, }, + { "deferred", 4, 0x05, }, + { "single", 4, 0x14, }, + { "multiple", 4, 0x17, }, + { "out_fcs_error", 4, 0x03, }, + { "late", 4, 0x1f, }, + { "hist_64bytes", 4, 0x08, }, + { "hist_65_127bytes", 4, 0x09, }, + { "hist_128_255bytes", 4, 0x0a, }, + { "hist_256_511bytes", 4, 0x0b, }, + { "hist_512_1023bytes", 4, 0x0c, }, + { "hist_1024_max_bytes", 4, 0x0d, }, +}; + +static void +mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +{ + mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats), + mv88e6123_61_65_hw_stats, port, data); +} + +static void +mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds, + int port, uint64_t *data) +{ + mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats), + mv88e6123_61_65_hw_stats, port, data); +} + +static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds) +{ + return ARRAY_SIZE(mv88e6123_61_65_hw_stats); +} + +static struct dsa_switch_driver mv88e6123_61_65_switch_driver = { + .tag_protocol = __constant_htons(ETH_P_EDSA), + .priv_size = sizeof(struct mv88e6xxx_priv_state), + .probe = mv88e6123_61_65_probe, + .setup = mv88e6123_61_65_setup, + .set_addr = mv88e6xxx_set_addr_indirect, + .phy_read = mv88e6123_61_65_phy_read, + .phy_write = mv88e6123_61_65_phy_write, + .poll_link = mv88e6xxx_poll_link, + .get_strings = mv88e6123_61_65_get_strings, + .get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats, + .get_sset_count = mv88e6123_61_65_get_sset_count, +}; + +int __init mv88e6123_61_65_init(void) +{ + register_switch_driver(&mv88e6123_61_65_switch_driver); + return 0; +} +module_init(mv88e6123_61_65_init); + +void __exit mv88e6123_61_65_cleanup(void) +{ + unregister_switch_driver(&mv88e6123_61_65_switch_driver); +} +module_exit(mv88e6123_61_65_cleanup); diff --git a/net/dsa/mv88e6xxx.c b/net/dsa/mv88e6xxx.c new file mode 100644 index 000000000000..13d2328a2406 --- /dev/null +++ b/net/dsa/mv88e6xxx.c @@ -0,0 +1,377 @@ +/* + * net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include "dsa_priv.h" +#include "mv88e6xxx.h" + +/* + * If the switch's ADDR[4:0] strap pins are strapped to zero, it will + * use all 32 SMI bus addresses on its SMI bus, and all switch registers + * will be directly accessible on some {device address,register address} + * pair. If the ADDR[4:0] pins are not strapped to zero, the switch + * will only respond to SMI transactions to that specific address, and + * an indirect addressing mechanism needs to be used to access its + * registers. + */ +static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr) +{ + int ret; + int i; + + for (i = 0; i < 16; i++) { + ret = mdiobus_read(bus, sw_addr, 0); + if (ret < 0) + return ret; + + if ((ret & 0x8000) == 0) + return 0; + } + + return -ETIMEDOUT; +} + +int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) +{ + int ret; + + if (sw_addr == 0) + return mdiobus_read(bus, addr, reg); + + /* + * Wait for the bus to become free. + */ + ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); + if (ret < 0) + return ret; + + /* + * Transmit the read command. + */ + ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg); + if (ret < 0) + return ret; + + /* + * Wait for the read command to complete. + */ + ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); + if (ret < 0) + return ret; + + /* + * Read the data. + */ + ret = mdiobus_read(bus, sw_addr, 1); + if (ret < 0) + return ret; + + return ret & 0xffff; +} + +int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) +{ + struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); + int ret; + + mutex_lock(&ps->smi_mutex); + ret = __mv88e6xxx_reg_read(ds->master_mii_bus, + ds->pd->sw_addr, addr, reg); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, + int reg, u16 val) +{ + int ret; + + if (sw_addr == 0) + return mdiobus_write(bus, addr, reg, val); + + /* + * Wait for the bus to become free. + */ + ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); + if (ret < 0) + return ret; + + /* + * Transmit the data to write. + */ + ret = mdiobus_write(bus, sw_addr, 1, val); + if (ret < 0) + return ret; + + /* + * Transmit the write command. + */ + ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg); + if (ret < 0) + return ret; + + /* + * Wait for the write command to complete. + */ + ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); + if (ret < 0) + return ret; + + return 0; +} + +int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) +{ + struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); + int ret; + + mutex_lock(&ps->smi_mutex); + ret = __mv88e6xxx_reg_write(ds->master_mii_bus, + ds->pd->sw_addr, addr, reg, val); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +int mv88e6xxx_config_prio(struct dsa_switch *ds) +{ + /* + * Configure the IP ToS mapping registers. + */ + REG_WRITE(REG_GLOBAL, 0x10, 0x0000); + REG_WRITE(REG_GLOBAL, 0x11, 0x0000); + REG_WRITE(REG_GLOBAL, 0x12, 0x5555); + REG_WRITE(REG_GLOBAL, 0x13, 0x5555); + REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa); + REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa); + REG_WRITE(REG_GLOBAL, 0x16, 0xffff); + REG_WRITE(REG_GLOBAL, 0x17, 0xffff); + + /* + * Configure the IEEE 802.1p priority mapping register. + */ + REG_WRITE(REG_GLOBAL, 0x18, 0xfa41); + + return 0; +} + +int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) +{ + int i; + int ret; + + for (i = 0; i < 6; i++) { + int j; + + /* + * Write the MAC address byte. + */ + REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]); + + /* + * Wait for the write to complete. + */ + for (j = 0; j < 16; j++) { + ret = REG_READ(REG_GLOBAL2, 0x0d); + if ((ret & 0x8000) == 0) + break; + } + if (j == 16) + return -ETIMEDOUT; + } + + return 0; +} + +int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) +{ + if (addr >= 0) + return mv88e6xxx_reg_read(ds, addr, regnum); + return 0xffff; +} + +int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val) +{ + if (addr >= 0) + return mv88e6xxx_reg_write(ds, addr, regnum, val); + return 0; +} + +void mv88e6xxx_poll_link(struct dsa_switch *ds) +{ + int i; + + for (i = 0; i < DSA_MAX_PORTS; i++) { + struct net_device *dev; + int port_status; + int link; + int speed; + int duplex; + int fc; + + dev = ds->ports[i]; + if (dev == NULL) + continue; + + link = 0; + if (dev->flags & IFF_UP) { + port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00); + if (port_status < 0) + continue; + + link = !!(port_status & 0x0800); + } + + if (!link) { + if (netif_carrier_ok(dev)) { + printk(KERN_INFO "%s: link down\n", dev->name); + netif_carrier_off(dev); + } + continue; + } + + switch (port_status & 0x0300) { + case 0x0000: + speed = 10; + break; + case 0x0100: + speed = 100; + break; + case 0x0200: + speed = 1000; + break; + default: + speed = -1; + break; + } + duplex = (port_status & 0x0400) ? 1 : 0; + fc = (port_status & 0x8000) ? 1 : 0; + + if (!netif_carrier_ok(dev)) { + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, " + "flow control %sabled\n", dev->name, + speed, duplex ? "full" : "half", + fc ? "en" : "dis"); + netif_carrier_on(dev); + } + } +} + +static int mv88e6xxx_stats_wait(struct dsa_switch *ds) +{ + int ret; + int i; + + for (i = 0; i < 10; i++) { + ret = REG_READ(REG_GLOBAL2, 0x1d); + if ((ret & 0x8000) == 0) + return 0; + } + + return -ETIMEDOUT; +} + +static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port) +{ + int ret; + + /* + * Snapshot the hardware statistics counters for this port. + */ + REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port); + + /* + * Wait for the snapshotting to complete. + */ + ret = mv88e6xxx_stats_wait(ds); + if (ret < 0) + return ret; + + return 0; +} + +static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) +{ + u32 _val; + int ret; + + *val = 0; + + ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat); + if (ret < 0) + return; + + ret = mv88e6xxx_stats_wait(ds); + if (ret < 0) + return; + + ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e); + if (ret < 0) + return; + + _val = ret << 16; + + ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f); + if (ret < 0) + return; + + *val = _val | ret; +} + +void mv88e6xxx_get_strings(struct dsa_switch *ds, + int nr_stats, struct mv88e6xxx_hw_stat *stats, + int port, uint8_t *data) +{ + int i; + + for (i = 0; i < nr_stats; i++) { + memcpy(data + i * ETH_GSTRING_LEN, + stats[i].string, ETH_GSTRING_LEN); + } +} + +void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, + int nr_stats, struct mv88e6xxx_hw_stat *stats, + int port, uint64_t *data) +{ + struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); + int ret; + int i; + + mutex_lock(&ps->stats_mutex); + + ret = mv88e6xxx_stats_snapshot(ds, port); + if (ret < 0) { + mutex_unlock(&ps->stats_mutex); + return; + } + + /* + * Read each of the counters. + */ + for (i = 0; i < nr_stats; i++) { + struct mv88e6xxx_hw_stat *s = stats + i; + u32 low; + u32 high; + + mv88e6xxx_stats_read(ds, s->reg, &low); + if (s->sizeof_stat == 8) + mv88e6xxx_stats_read(ds, s->reg + 1, &high); + else + high = 0; + + data[i] = (((u64)high) << 32) | low; + } + + mutex_unlock(&ps->stats_mutex); +} diff --git a/net/dsa/mv88e6xxx.h b/net/dsa/mv88e6xxx.h new file mode 100644 index 000000000000..a004d4d02081 --- /dev/null +++ b/net/dsa/mv88e6xxx.h @@ -0,0 +1,77 @@ +/* + * net/dsa/mv88e6xxx.h - Marvell 88e6xxx switch chip support + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#ifndef __MV88E6XXX_H +#define __MV88E6XXX_H + +#define REG_PORT(p) (0x10 + (p)) +#define REG_GLOBAL 0x1b +#define REG_GLOBAL2 0x1c + +struct mv88e6xxx_priv_state { + /* + * When using multi-chip addressing, this mutex protects + * access to the indirect access registers. (In single-chip + * mode, this mutex is effectively useless.) + */ + struct mutex smi_mutex; + + /* + * This mutex serialises access to the statistics unit. + * Hold this mutex over snapshot + dump sequences. + */ + struct mutex stats_mutex; +}; + +struct mv88e6xxx_hw_stat { + char string[ETH_GSTRING_LEN]; + int sizeof_stat; + int reg; +}; + +int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg); +int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg); +int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, + int reg, u16 val); +int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val); +int mv88e6xxx_config_prio(struct dsa_switch *ds); +int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr); +int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum); +int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val); +void mv88e6xxx_poll_link(struct dsa_switch *ds); +void mv88e6xxx_get_strings(struct dsa_switch *ds, + int nr_stats, struct mv88e6xxx_hw_stat *stats, + int port, uint8_t *data); +void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, + int nr_stats, struct mv88e6xxx_hw_stat *stats, + int port, uint64_t *data); + +#define REG_READ(addr, reg) \ + ({ \ + int __ret; \ + \ + __ret = mv88e6xxx_reg_read(ds, addr, reg); \ + if (__ret < 0) \ + return __ret; \ + __ret; \ + }) + +#define REG_WRITE(addr, reg, val) \ + ({ \ + int __ret; \ + \ + __ret = mv88e6xxx_reg_write(ds, addr, reg, val); \ + if (__ret < 0) \ + return __ret; \ + }) + + + +#endif diff --git a/net/dsa/slave.c b/net/dsa/slave.c new file mode 100644 index 000000000000..3cb331e98b89 --- /dev/null +++ b/net/dsa/slave.c @@ -0,0 +1,288 @@ +/* + * net/dsa/slave.c - Slave device handling + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include "dsa_priv.h" + +/* slave mii_bus handling ***************************************************/ +static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) +{ + struct dsa_switch *ds = bus->priv; + + if (ds->valid_port_mask & (1 << addr)) + return ds->drv->phy_read(ds, addr, reg); + + return 0xffff; +} + +static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct dsa_switch *ds = bus->priv; + + if (ds->valid_port_mask & (1 << addr)) + return ds->drv->phy_write(ds, addr, reg, val); + + return 0; +} + +void dsa_slave_mii_bus_init(struct dsa_switch *ds) +{ + ds->slave_mii_bus->priv = (void *)ds; + ds->slave_mii_bus->name = "dsa slave smi"; + ds->slave_mii_bus->read = dsa_slave_phy_read; + ds->slave_mii_bus->write = dsa_slave_phy_write; + snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x", + ds->master_mii_bus->id, ds->pd->sw_addr); + ds->slave_mii_bus->parent = &(ds->master_mii_bus->dev); +} + + +/* slave device handling ****************************************************/ +static int dsa_slave_open(struct net_device *dev) +{ + return 0; +} + +static int dsa_slave_close(struct net_device *dev) +{ + return 0; +} + +static void dsa_slave_change_rx_flags(struct net_device *dev, int change) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct net_device *master = p->parent->master_netdev; + + if (change & IFF_ALLMULTI) + dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1); + if (change & IFF_PROMISC) + dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1); +} + +static void dsa_slave_set_rx_mode(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct net_device *master = p->parent->master_netdev; + + dev_mc_sync(master, dev); + dev_unicast_sync(master, dev); +} + +static int dsa_slave_set_mac_address(struct net_device *dev, void *addr) +{ + memcpy(dev->dev_addr, addr + 2, 6); + + return 0; +} + +static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct mii_ioctl_data *mii_data = if_mii(ifr); + + if (p->phy != NULL) + return phy_mii_ioctl(p->phy, mii_data, cmd); + + return -EOPNOTSUPP; +} + + +/* ethtool operations *******************************************************/ +static int +dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + int err; + + err = -EOPNOTSUPP; + if (p->phy != NULL) { + err = phy_read_status(p->phy); + if (err == 0) + err = phy_ethtool_gset(p->phy, cmd); + } + + return err; +} + +static int +dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + + if (p->phy != NULL) + return phy_ethtool_sset(p->phy, cmd); + + return -EOPNOTSUPP; +} + +static void dsa_slave_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strncpy(drvinfo->driver, "dsa", 32); + strncpy(drvinfo->version, dsa_driver_version, 32); + strncpy(drvinfo->fw_version, "N/A", 32); + strncpy(drvinfo->bus_info, "platform", 32); +} + +static int dsa_slave_nway_reset(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + + if (p->phy != NULL) + return genphy_restart_aneg(p->phy); + + return -EOPNOTSUPP; +} + +static u32 dsa_slave_get_link(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + + if (p->phy != NULL) { + genphy_update_link(p->phy); + return p->phy->link; + } + + return -EOPNOTSUPP; +} + +static void dsa_slave_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *data) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (stringset == ETH_SS_STATS) { + int len = ETH_GSTRING_LEN; + + strncpy(data, "tx_packets", len); + strncpy(data + len, "tx_bytes", len); + strncpy(data + 2 * len, "rx_packets", len); + strncpy(data + 3 * len, "rx_bytes", len); + if (ds->drv->get_strings != NULL) + ds->drv->get_strings(ds, p->port, data + 4 * len); + } +} + +static void dsa_slave_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, + uint64_t *data) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + data[0] = p->dev->stats.tx_packets; + data[1] = p->dev->stats.tx_bytes; + data[2] = p->dev->stats.rx_packets; + data[3] = p->dev->stats.rx_bytes; + if (ds->drv->get_ethtool_stats != NULL) + ds->drv->get_ethtool_stats(ds, p->port, data + 4); +} + +static int dsa_slave_get_sset_count(struct net_device *dev, int sset) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (sset == ETH_SS_STATS) { + int count; + + count = 4; + if (ds->drv->get_sset_count != NULL) + count += ds->drv->get_sset_count(ds); + + return count; + } + + return -EOPNOTSUPP; +} + +static const struct ethtool_ops dsa_slave_ethtool_ops = { + .get_settings = dsa_slave_get_settings, + .set_settings = dsa_slave_set_settings, + .get_drvinfo = dsa_slave_get_drvinfo, + .nway_reset = dsa_slave_nway_reset, + .get_link = dsa_slave_get_link, + .set_sg = ethtool_op_set_sg, + .get_strings = dsa_slave_get_strings, + .get_ethtool_stats = dsa_slave_get_ethtool_stats, + .get_sset_count = dsa_slave_get_sset_count, +}; + + +/* slave device setup *******************************************************/ +struct net_device * +dsa_slave_create(struct dsa_switch *ds, struct device *parent, + int port, char *name) +{ + struct net_device *master = ds->master_netdev; + struct net_device *slave_dev; + struct dsa_slave_priv *p; + int ret; + + slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), + name, ether_setup); + if (slave_dev == NULL) + return slave_dev; + + slave_dev->features = master->vlan_features; + SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops); + memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN); + slave_dev->tx_queue_len = 0; + switch (ds->tag_protocol) { +#ifdef CONFIG_NET_DSA_TAG_EDSA + case htons(ETH_P_EDSA): + slave_dev->hard_start_xmit = edsa_xmit; + break; +#endif + default: + BUG(); + } + slave_dev->open = dsa_slave_open; + slave_dev->stop = dsa_slave_close; + slave_dev->change_rx_flags = dsa_slave_change_rx_flags; + slave_dev->set_rx_mode = dsa_slave_set_rx_mode; + slave_dev->set_multicast_list = dsa_slave_set_rx_mode; + slave_dev->set_mac_address = dsa_slave_set_mac_address; + slave_dev->do_ioctl = dsa_slave_ioctl; + SET_NETDEV_DEV(slave_dev, parent); + slave_dev->vlan_features = master->vlan_features; + + p = netdev_priv(slave_dev); + p->dev = slave_dev; + p->parent = ds; + p->port = port; + p->phy = ds->slave_mii_bus->phy_map[port]; + + ret = register_netdev(slave_dev); + if (ret) { + printk(KERN_ERR "%s: error %d registering interface %s\n", + master->name, ret, slave_dev->name); + free_netdev(slave_dev); + return NULL; + } + + netif_carrier_off(slave_dev); + + if (p->phy != NULL) { + phy_attach(slave_dev, p->phy->dev.bus_id, + 0, PHY_INTERFACE_MODE_GMII); + + p->phy->autoneg = AUTONEG_ENABLE; + p->phy->speed = 0; + p->phy->duplex = 0; + p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg; + phy_start_aneg(p->phy); + } + + return slave_dev; +} diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c new file mode 100644 index 000000000000..f985ea993843 --- /dev/null +++ b/net/dsa/tag_edsa.c @@ -0,0 +1,213 @@ +/* + * net/dsa/tag_edsa.c - Ethertype DSA tagging + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include "dsa_priv.h" + +#define DSA_HLEN 4 +#define EDSA_HLEN 8 + +int edsa_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + u8 *edsa_header; + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* + * Convert the outermost 802.1q tag to a DSA tag and prepend + * a DSA ethertype field is the packet is tagged, or insert + * a DSA ethertype plus DSA tag between the addresses and the + * current ethertype field if the packet is untagged. + */ + if (skb->protocol == htons(ETH_P_8021Q)) { + if (skb_cow_head(skb, DSA_HLEN) < 0) + goto out_free; + skb_push(skb, DSA_HLEN); + + memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN); + + /* + * Construct tagged FROM_CPU DSA tag from 802.1q tag. + */ + edsa_header = skb->data + 2 * ETH_ALEN; + edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; + edsa_header[1] = ETH_P_EDSA & 0xff; + edsa_header[2] = 0x00; + edsa_header[3] = 0x00; + edsa_header[4] = 0x60; + edsa_header[5] = p->port << 3; + + /* + * Move CFI field from byte 6 to byte 5. + */ + if (edsa_header[6] & 0x10) { + edsa_header[5] |= 0x01; + edsa_header[6] &= ~0x10; + } + } else { + if (skb_cow_head(skb, EDSA_HLEN) < 0) + goto out_free; + skb_push(skb, EDSA_HLEN); + + memmove(skb->data, skb->data + EDSA_HLEN, 2 * ETH_ALEN); + + /* + * Construct untagged FROM_CPU DSA tag. + */ + edsa_header = skb->data + 2 * ETH_ALEN; + edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; + edsa_header[1] = ETH_P_EDSA & 0xff; + edsa_header[2] = 0x00; + edsa_header[3] = 0x00; + edsa_header[4] = 0x40; + edsa_header[5] = p->port << 3; + edsa_header[6] = 0x00; + edsa_header[7] = 0x00; + } + + skb->protocol = htons(ETH_P_EDSA); + + skb->dev = p->parent->master_netdev; + dev_queue_xmit(skb); + + return NETDEV_TX_OK; + +out_free: + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct dsa_switch *ds = dev->dsa_ptr; + u8 *edsa_header; + int source_port; + + if (unlikely(ds == NULL)) + goto out_drop; + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) + goto out; + + if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) + goto out_drop; + + /* + * Skip the two null bytes after the ethertype. + */ + edsa_header = skb->data + 2; + + /* + * Check that frame type is either TO_CPU or FORWARD, and + * that the source device is zero. + */ + if ((edsa_header[0] & 0xdf) != 0x00 && (edsa_header[0] & 0xdf) != 0xc0) + goto out_drop; + + /* + * Check that the source port is a registered DSA port. + */ + source_port = (edsa_header[1] >> 3) & 0x1f; + if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) + goto out_drop; + + /* + * If the 'tagged' bit is set, convert the DSA tag to a 802.1q + * tag and delete the ethertype part. If the 'tagged' bit is + * clear, delete the ethertype and the DSA tag parts. + */ + if (edsa_header[0] & 0x20) { + u8 new_header[4]; + + /* + * Insert 802.1q ethertype and copy the VLAN-related + * fields, but clear the bit that will hold CFI (since + * DSA uses that bit location for another purpose). + */ + new_header[0] = (ETH_P_8021Q >> 8) & 0xff; + new_header[1] = ETH_P_8021Q & 0xff; + new_header[2] = edsa_header[2] & ~0x10; + new_header[3] = edsa_header[3]; + + /* + * Move CFI bit from its place in the DSA header to + * its 802.1q-designated place. + */ + if (edsa_header[1] & 0x01) + new_header[2] |= 0x10; + + skb_pull_rcsum(skb, DSA_HLEN); + + /* + * Update packet checksum if skb is CHECKSUM_COMPLETE. + */ + if (skb->ip_summed == CHECKSUM_COMPLETE) { + __wsum c = skb->csum; + c = csum_add(c, csum_partial(new_header + 2, 2, 0)); + c = csum_sub(c, csum_partial(edsa_header + 2, 2, 0)); + skb->csum = c; + } + + memcpy(edsa_header, new_header, DSA_HLEN); + + memmove(skb->data - ETH_HLEN, + skb->data - ETH_HLEN - DSA_HLEN, + 2 * ETH_ALEN); + } else { + /* + * Remove DSA tag and update checksum. + */ + skb_pull_rcsum(skb, EDSA_HLEN); + memmove(skb->data - ETH_HLEN, + skb->data - ETH_HLEN - EDSA_HLEN, + 2 * ETH_ALEN); + } + + skb->dev = ds->ports[source_port]; + skb_push(skb, ETH_HLEN); + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->last_rx = jiffies; + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += skb->len; + + netif_receive_skb(skb); + + return 0; + +out_drop: + kfree_skb(skb); +out: + return 0; +} + +static struct packet_type edsa_packet_type = { + .type = __constant_htons(ETH_P_EDSA), + .func = edsa_rcv, +}; + +static int __init edsa_init_module(void) +{ + dev_add_pack(&edsa_packet_type); + return 0; +} +module_init(edsa_init_module); + +static void __exit edsa_cleanup_module(void) +{ + dev_remove_pack(&edsa_packet_type); +} +module_exit(edsa_cleanup_module); -- cgit v1.2.3 From cf85d08fdf4548ee46657ccfb7f9949a85145db5 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 7 Oct 2008 13:45:02 +0000 Subject: dsa: add support for original DSA tagging format Most of the DSA switches currently in the field do not support the Ethertype DSA tagging format that one of the previous patches added support for, but only the original DSA tagging format. The original DSA tagging format carries the same information as the Ethertype DSA tagging format, but with the difference that it does not have an ethertype field. In other words, when receiving a packet that is tagged with an original DSA tag, there is no way of telling in eth_type_trans() that this packet is in fact a DSA-tagged packet. This patch adds a hook into eth_type_trans() which is only compiled in if support for a switch chip that doesn't support Ethertype DSA is selected, and which checks whether there is a DSA switch driver instance attached to this network device which uses the old tag format. If so, it sets the protocol field to ETH_P_DSA without looking at the packet, so that the packet ends up in the right place. Signed-off-by: Lennert Buytenhek Tested-by: Nicolas Pitre Tested-by: Peter van Valderen Tested-by: Dirk Teurlings Signed-off-by: David S. Miller --- include/linux/if_ether.h | 1 + include/linux/netdevice.h | 11 +++ include/net/dsa.h | 2 + net/dsa/Kconfig | 4 + net/dsa/Makefile | 1 + net/dsa/dsa.c | 16 ++++ net/dsa/dsa_priv.h | 3 + net/dsa/mv88e6123_61_65.c | 18 +++-- net/dsa/slave.c | 5 ++ net/dsa/tag_dsa.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++ net/ethernet/eth.c | 10 +++ 11 files changed, 258 insertions(+), 7 deletions(-) create mode 100644 net/dsa/tag_dsa.c (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 2140aacb6338..32b9dcda68c7 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -101,6 +101,7 @@ #define ETH_P_ECONET 0x0018 /* Acorn Econet */ #define ETH_P_HDLC 0x0019 /* HDLC frames */ #define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ +#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */ #define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ /* diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 794eeb4b3462..97f0c64c152a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -42,6 +42,7 @@ #include #include +#include struct vlan_group; struct ethtool_ops; @@ -801,6 +802,16 @@ void dev_net_set(struct net_device *dev, struct net *net) #endif } +static inline bool netdev_uses_dsa_tags(struct net_device *dev) +{ +#ifdef CONFIG_NET_DSA_TAG_DSA + if (dev->dsa_ptr != NULL) + return dsa_uses_dsa_tags(dev->dsa_ptr); +#endif + + return 0; +} + /** * netdev_priv - access network device private data * @dev: network device diff --git a/include/net/dsa.h b/include/net/dsa.h index dc4784f54520..72e509b6a12e 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -30,5 +30,7 @@ struct dsa_platform_data { char *port_names[DSA_MAX_PORTS]; }; +extern bool dsa_uses_dsa_tags(void *dsa_ptr); + #endif diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 7cf55e5eb39f..6b68016827da 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -10,6 +10,10 @@ menuconfig NET_DSA if NET_DSA # tagging formats +config NET_DSA_TAG_DSA + bool + default n + config NET_DSA_TAG_EDSA bool default n diff --git a/net/dsa/Makefile b/net/dsa/Makefile index b59a6f6bcf56..8b92123315b8 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -1,4 +1,5 @@ # tagging formats +obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o # switch drivers diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 6cc5be2ec7f1..f8c549281c30 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -202,6 +202,22 @@ static void dsa_switch_destroy(struct dsa_switch *ds) } +/* hooks for ethertype-less tagging formats *********************************/ +/* + * The original DSA tag format and some other tag formats have no + * ethertype, which means that we need to add a little hack to the + * networking receive path to make sure that received frames get + * the right ->protocol assigned to them when one of those tag + * formats is in use. + */ +bool dsa_uses_dsa_tags(void *dsa_ptr) +{ + struct dsa_switch *ds = dsa_ptr; + + return !!(ds->tag_protocol == htons(ETH_P_DSA)); +} + + /* link polling *************************************************************/ static void dsa_link_poll_work(struct work_struct *ugly) { diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 21ee9052079a..2f1d68c495e8 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -103,6 +103,9 @@ struct net_device *dsa_slave_create(struct dsa_switch *ds, struct device *parent, int port, char *name); +/* tag_dsa.c */ +int dsa_xmit(struct sk_buff *skb, struct net_device *dev); + /* tag_edsa.c */ int edsa_xmit(struct sk_buff *skb, struct net_device *dev); diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c index 147818cc706e..555b164082fc 100644 --- a/net/dsa/mv88e6123_61_65.c +++ b/net/dsa/mv88e6123_61_65.c @@ -192,15 +192,19 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) /* * Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, - * configure the EDSA tagging mode if this is the CPU port, - * disable Header mode, enable IGMP/MLD snooping, disable VLAN - * tunneling, determine priority by looking at 802.1p and IP - * priority fields (IP prio has precedence), and set STP state - * to Forwarding. Finally, if this is the CPU port, additionally - * enable forwarding of unknown unicast and multicast addresses. + * configure the requested (DSA/EDSA) tagging mode if this is + * the CPU port, disable Header mode, enable IGMP/MLD snooping, + * disable VLAN tunneling, determine priority by looking at + * 802.1p and IP priority fields (IP prio has precedence), and + * set STP state to Forwarding. Finally, if this is the CPU + * port, additionally enable forwarding of unknown unicast and + * multicast addresses. */ REG_WRITE(addr, 0x04, - (p == ds->cpu_port) ? 0x373f : 0x0433); + (p == ds->cpu_port) ? + (ds->tag_protocol == htons(ETH_P_DSA)) ? + 0x053f : 0x373f : + 0x0433); /* * Port Control 1: disable trunking. Also, if this is the diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 3cb331e98b89..8f8868dd4302 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -239,6 +239,11 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN); slave_dev->tx_queue_len = 0; switch (ds->tag_protocol) { +#ifdef CONFIG_NET_DSA_TAG_DSA + case htons(ETH_P_DSA): + slave_dev->hard_start_xmit = dsa_xmit; + break; +#endif #ifdef CONFIG_NET_DSA_TAG_EDSA case htons(ETH_P_EDSA): slave_dev->hard_start_xmit = edsa_xmit; diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c new file mode 100644 index 000000000000..bdc0510b53b7 --- /dev/null +++ b/net/dsa/tag_dsa.c @@ -0,0 +1,194 @@ +/* + * net/dsa/tag_dsa.c - (Non-ethertype) DSA tagging + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include "dsa_priv.h" + +#define DSA_HLEN 4 + +int dsa_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + u8 *dsa_header; + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* + * Convert the outermost 802.1q tag to a DSA tag for tagged + * packets, or insert a DSA tag between the addresses and + * the ethertype field for untagged packets. + */ + if (skb->protocol == htons(ETH_P_8021Q)) { + if (skb_cow_head(skb, 0) < 0) + goto out_free; + + /* + * Construct tagged FROM_CPU DSA tag from 802.1q tag. + */ + dsa_header = skb->data + 2 * ETH_ALEN; + dsa_header[0] = 0x60; + dsa_header[1] = p->port << 3; + + /* + * Move CFI field from byte 2 to byte 1. + */ + if (dsa_header[2] & 0x10) { + dsa_header[1] |= 0x01; + dsa_header[2] &= ~0x10; + } + } else { + if (skb_cow_head(skb, DSA_HLEN) < 0) + goto out_free; + skb_push(skb, DSA_HLEN); + + memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN); + + /* + * Construct untagged FROM_CPU DSA tag. + */ + dsa_header = skb->data + 2 * ETH_ALEN; + dsa_header[0] = 0x40; + dsa_header[1] = p->port << 3; + dsa_header[2] = 0x00; + dsa_header[3] = 0x00; + } + + skb->protocol = htons(ETH_P_DSA); + + skb->dev = p->parent->master_netdev; + dev_queue_xmit(skb); + + return NETDEV_TX_OK; + +out_free: + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct dsa_switch *ds = dev->dsa_ptr; + u8 *dsa_header; + int source_port; + + if (unlikely(ds == NULL)) + goto out_drop; + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) + goto out; + + if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) + goto out_drop; + + /* + * The ethertype field is part of the DSA header. + */ + dsa_header = skb->data - 2; + + /* + * Check that frame type is either TO_CPU or FORWARD, and + * that the source device is zero. + */ + if ((dsa_header[0] & 0xdf) != 0x00 && (dsa_header[0] & 0xdf) != 0xc0) + goto out_drop; + + /* + * Check that the source port is a registered DSA port. + */ + source_port = (dsa_header[1] >> 3) & 0x1f; + if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) + goto out_drop; + + /* + * Convert the DSA header to an 802.1q header if the 'tagged' + * bit in the DSA header is set. If the 'tagged' bit is clear, + * delete the DSA header entirely. + */ + if (dsa_header[0] & 0x20) { + u8 new_header[4]; + + /* + * Insert 802.1q ethertype and copy the VLAN-related + * fields, but clear the bit that will hold CFI (since + * DSA uses that bit location for another purpose). + */ + new_header[0] = (ETH_P_8021Q >> 8) & 0xff; + new_header[1] = ETH_P_8021Q & 0xff; + new_header[2] = dsa_header[2] & ~0x10; + new_header[3] = dsa_header[3]; + + /* + * Move CFI bit from its place in the DSA header to + * its 802.1q-designated place. + */ + if (dsa_header[1] & 0x01) + new_header[2] |= 0x10; + + /* + * Update packet checksum if skb is CHECKSUM_COMPLETE. + */ + if (skb->ip_summed == CHECKSUM_COMPLETE) { + __wsum c = skb->csum; + c = csum_add(c, csum_partial(new_header + 2, 2, 0)); + c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0)); + skb->csum = c; + } + + memcpy(dsa_header, new_header, DSA_HLEN); + } else { + /* + * Remove DSA tag and update checksum. + */ + skb_pull_rcsum(skb, DSA_HLEN); + memmove(skb->data - ETH_HLEN, + skb->data - ETH_HLEN - DSA_HLEN, + 2 * ETH_ALEN); + } + + skb->dev = ds->ports[source_port]; + skb_push(skb, ETH_HLEN); + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->last_rx = jiffies; + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += skb->len; + + netif_receive_skb(skb); + + return 0; + +out_drop: + kfree_skb(skb); +out: + return 0; +} + +static struct packet_type dsa_packet_type = { + .type = __constant_htons(ETH_P_DSA), + .func = dsa_rcv, +}; + +static int __init dsa_init_module(void) +{ + dev_add_pack(&dsa_packet_type); + return 0; +} +module_init(dsa_init_module); + +static void __exit dsa_cleanup_module(void) +{ + dev_remove_pack(&dsa_packet_type); +} +module_exit(dsa_cleanup_module); diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 647a9edee375..dae47e7a44d8 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -184,6 +185,15 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) skb->pkt_type = PACKET_OTHERHOST; } + /* + * Some variants of DSA tagging don't have an ethertype field + * at all, so we check here whether one of those tagging + * variants has been configured on the receiving interface, + * and if so, set skb->protocol without looking at the packet. + */ + if (netdev_uses_dsa_tags(dev)) + return htons(ETH_P_DSA); + if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; -- cgit v1.2.3 From 396138f03f4521c55ecc3a5dd75d4c56e6323244 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 7 Oct 2008 13:46:07 +0000 Subject: dsa: add support for Trailer tagging format This adds support for the Trailer switch tagging format. This is another tagging that doesn't explicitly mark tagged packets with a distinct ethertype, so that we need to add a similar hack in the receive path as for the Original DSA tagging format. Signed-off-by: Lennert Buytenhek Tested-by: Byron Bradley Tested-by: Tim Ellis Signed-off-by: David S. Miller --- include/linux/if_ether.h | 1 + include/linux/netdevice.h | 10 ++++ include/net/dsa.h | 1 + net/dsa/Kconfig | 4 ++ net/dsa/Makefile | 1 + net/dsa/dsa.c | 7 +++ net/dsa/dsa_priv.h | 3 ++ net/dsa/slave.c | 5 ++ net/dsa/tag_trailer.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++ net/ethernet/eth.c | 2 + 10 files changed, 164 insertions(+) create mode 100644 net/dsa/tag_trailer.c (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 32b9dcda68c7..a0099e98b5c4 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -102,6 +102,7 @@ #define ETH_P_HDLC 0x0019 /* HDLC frames */ #define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ #define ETH_P_DSA 0x001B /* Distributed Switch Arch. */ +#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */ #define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ /* diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 97f0c64c152a..d3ea3de70a8a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -812,6 +812,16 @@ static inline bool netdev_uses_dsa_tags(struct net_device *dev) return 0; } +static inline bool netdev_uses_trailer_tags(struct net_device *dev) +{ +#ifdef CONFIG_NET_DSA_TAG_TRAILER + if (dev->dsa_ptr != NULL) + return dsa_uses_trailer_tags(dev->dsa_ptr); +#endif + + return 0; +} + /** * netdev_priv - access network device private data * @dev: network device diff --git a/include/net/dsa.h b/include/net/dsa.h index 72e509b6a12e..52e97bfca5a1 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -31,6 +31,7 @@ struct dsa_platform_data { }; extern bool dsa_uses_dsa_tags(void *dsa_ptr); +extern bool dsa_uses_trailer_tags(void *dsa_ptr); #endif diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 79bcd76d3f10..505aa14e67fc 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -18,6 +18,10 @@ config NET_DSA_TAG_EDSA bool default n +config NET_DSA_TAG_TRAILER + bool + default n + # switch drivers config NET_DSA_MV88E6XXX diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 7fb6f85a69ed..63d3c44908b0 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -1,6 +1,7 @@ # tagging formats obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o +obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o # switch drivers obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index f8c549281c30..33e99462023a 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -217,6 +217,13 @@ bool dsa_uses_dsa_tags(void *dsa_ptr) return !!(ds->tag_protocol == htons(ETH_P_DSA)); } +bool dsa_uses_trailer_tags(void *dsa_ptr) +{ + struct dsa_switch *ds = dsa_ptr; + + return !!(ds->tag_protocol == htons(ETH_P_TRAILER)); +} + /* link polling *************************************************************/ static void dsa_link_poll_work(struct work_struct *ugly) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 2f1d68c495e8..7063378a1ebf 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -109,5 +109,8 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev); /* tag_edsa.c */ int edsa_xmit(struct sk_buff *skb, struct net_device *dev); +/* tag_trailer.c */ +int trailer_xmit(struct sk_buff *skb, struct net_device *dev); + #endif diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 8f8868dd4302..37616884b8a9 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -248,6 +248,11 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, case htons(ETH_P_EDSA): slave_dev->hard_start_xmit = edsa_xmit; break; +#endif +#ifdef CONFIG_NET_DSA_TAG_TRAILER + case htons(ETH_P_TRAILER): + slave_dev->hard_start_xmit = trailer_xmit; + break; #endif default: BUG(); diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c new file mode 100644 index 000000000000..d3117764b2c2 --- /dev/null +++ b/net/dsa/tag_trailer.c @@ -0,0 +1,130 @@ +/* + * net/dsa/tag_trailer.c - Trailer tag format handling + * Copyright (c) 2008 Marvell Semiconductor + * + * 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. + */ + +#include +#include +#include +#include "dsa_priv.h" + +int trailer_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct sk_buff *nskb; + int padlen; + u8 *trailer; + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* + * We have to make sure that the trailer ends up as the very + * last 4 bytes of the packet. This means that we have to pad + * the packet to the minimum ethernet frame size, if necessary, + * before adding the trailer. + */ + padlen = 0; + if (skb->len < 60) + padlen = 60 - skb->len; + + nskb = alloc_skb(NET_IP_ALIGN + skb->len + padlen + 4, GFP_ATOMIC); + if (nskb == NULL) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + skb_reserve(nskb, NET_IP_ALIGN); + + skb_reset_mac_header(nskb); + skb_set_network_header(nskb, skb_network_header(skb) - skb->head); + skb_set_transport_header(nskb, skb_transport_header(skb) - skb->head); + skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len)); + kfree_skb(skb); + + if (padlen) { + u8 *pad = skb_put(nskb, padlen); + memset(pad, 0, padlen); + } + + trailer = skb_put(nskb, 4); + trailer[0] = 0x80; + trailer[1] = 1 << p->port; + trailer[2] = 0x10; + trailer[3] = 0x00; + + nskb->protocol = htons(ETH_P_TRAILER); + + nskb->dev = p->parent->master_netdev; + dev_queue_xmit(nskb); + + return NETDEV_TX_OK; +} + +static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct dsa_switch *ds = dev->dsa_ptr; + u8 *trailer; + int source_port; + + if (unlikely(ds == NULL)) + goto out_drop; + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) + goto out; + + if (skb_linearize(skb)) + goto out_drop; + + trailer = skb_tail_pointer(skb) - 4; + if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || + (trailer[3] & 0xef) != 0x00 || trailer[3] != 0x00) + goto out_drop; + + source_port = trailer[1] & 7; + if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) + goto out_drop; + + pskb_trim_rcsum(skb, skb->len - 4); + + skb->dev = ds->ports[source_port]; + skb_push(skb, ETH_HLEN); + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->last_rx = jiffies; + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += skb->len; + + netif_receive_skb(skb); + + return 0; + +out_drop: + kfree_skb(skb); +out: + return 0; +} + +static struct packet_type trailer_packet_type = { + .type = __constant_htons(ETH_P_TRAILER), + .func = trailer_rcv, +}; + +static int __init trailer_init_module(void) +{ + dev_add_pack(&trailer_packet_type); + return 0; +} +module_init(trailer_init_module); + +static void __exit trailer_cleanup_module(void) +{ + dev_remove_pack(&trailer_packet_type); +} +module_exit(trailer_cleanup_module); diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index dae47e7a44d8..b9d85af2dd31 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -193,6 +193,8 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) */ if (netdev_uses_dsa_tags(dev)) return htons(ETH_P_DSA); + if (netdev_uses_trailer_tags(dev)) + return htons(ETH_P_TRAILER); if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; -- cgit v1.2.3 From 7a67f63b3233ff28e753854fe27891c44f8588ae Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 8 Aug 2008 11:17:12 +0200 Subject: block: add bio_has_data() to detect whether a bio carries data or not Signed-off-by: Jens Axboe --- include/linux/bio.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bio.h b/include/linux/bio.h index 0933a14e6414..9e93c9299479 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -445,6 +445,14 @@ static inline char *__bio_kmap_irq(struct bio *bio, unsigned short idx, __bio_kmap_irq((bio), (bio)->bi_idx, (flags)) #define bio_kunmap_irq(buf,flags) __bio_kunmap_irq(buf, flags) +/* + * Check whether this bio carries any data or not. A NULL bio is allowed. + */ +static inline int bio_has_data(struct bio *bio) +{ + return bio && bio->bi_io_vec != NULL; +} + #if defined(CONFIG_BLK_DEV_INTEGRITY) #define bip_vec_idx(bip, idx) (&(bip->bip_vec[(idx)])) -- cgit v1.2.3 From a9c701e594669dd49fed448c27c64f20cfacc8a7 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 8 Aug 2008 11:04:44 +0200 Subject: block: use bio_has_data() to check for data carrying bio Signed-off-by: Jens Axboe --- block/blk-core.c | 5 +---- include/linux/bio.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 2cba5ef97b2b..54e442ba44aa 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1490,10 +1490,7 @@ void submit_bio(int rw, struct bio *bio) * If it's a regular read/write or a barrier with data attached, * go through the normal accounting stuff before submission. */ - if (!bio_empty_barrier(bio)) { - - BIO_BUG_ON(!bio->bi_size); - BIO_BUG_ON(!bio->bi_io_vec); + if (bio_has_data(bio)) { if (rw & WRITE) { count_vm_events(PGPGOUT, count); diff --git a/include/linux/bio.h b/include/linux/bio.h index 9e93c9299479..dbeb66f813ab 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -185,7 +185,7 @@ struct bio { #define bio_failfast(bio) ((bio)->bi_rw & (1 << BIO_RW_FAILFAST)) #define bio_rw_ahead(bio) ((bio)->bi_rw & (1 << BIO_RW_AHEAD)) #define bio_rw_meta(bio) ((bio)->bi_rw & (1 << BIO_RW_META)) -#define bio_empty_barrier(bio) (bio_barrier(bio) && !(bio)->bi_size) +#define bio_empty_barrier(bio) (bio_barrier(bio) && !bio_has_data(bio)) static inline unsigned int bio_cur_sectors(struct bio *bio) { -- cgit v1.2.3 From d628eaef310533767ce68664873869c2d7f78f09 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 9 Aug 2008 16:22:17 +0100 Subject: Fix up comments about matching flags between bio and rq Signed-off-by: David Woodhouse Signed-off-by: Jens Axboe --- block/blk-core.c | 7 ++----- include/linux/bio.h | 4 ++-- include/linux/blkdev.h | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index b5776c1fd52a..a496727df7ef 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -624,10 +624,6 @@ blk_alloc_request(struct request_queue *q, int rw, int priv, gfp_t gfp_mask) blk_rq_init(q, rq); - /* - * first three bits are identical in rq->cmd_flags and bio->bi_rw, - * see bio.h and blkdev.h - */ rq->cmd_flags = rw | REQ_ALLOCED; if (priv) { @@ -2012,7 +2008,8 @@ EXPORT_SYMBOL_GPL(blk_end_request_callback); void blk_rq_bio_prep(struct request_queue *q, struct request *rq, struct bio *bio) { - /* first two bits are identical in rq->cmd_flags and bio->bi_rw */ + /* Bit 0 (R/W) is identical in rq->cmd_flags and bio->bi_rw, and + we want BIO_RW_AHEAD (bit 1) to imply REQ_FAILFAST (bit 1). */ rq->cmd_flags |= (bio->bi_rw & 3); rq->nr_phys_segments = bio_phys_segments(q, bio); diff --git a/include/linux/bio.h b/include/linux/bio.h index dbeb66f813ab..17f1fbdb31bf 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -150,8 +150,8 @@ struct bio { * bit 3 -- fail fast, don't want low level driver retries * bit 4 -- synchronous I/O hint: the block layer will unplug immediately */ -#define BIO_RW 0 -#define BIO_RW_AHEAD 1 +#define BIO_RW 0 /* Must match RW in req flags (blkdev.h) */ +#define BIO_RW_AHEAD 1 /* Must match FAILFAST in req flags */ #define BIO_RW_BARRIER 2 #define BIO_RW_FAILFAST 3 #define BIO_RW_SYNC 4 diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 53ea933cf60b..e0ba018f5e88 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -84,7 +84,7 @@ enum { }; /* - * request type modified bits. first three bits match BIO_RW* bits, important + * request type modified bits. first two bits match BIO_RW* bits, important */ enum rq_flag_bits { __REQ_RW, /* not set, read. set, write */ -- cgit v1.2.3 From fb2dce862d9f9a68e6b9374579056ec9eca02a63 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 5 Aug 2008 18:01:53 +0100 Subject: Add 'discard' request handling Some block devices benefit from a hint that they can forget the contents of certain sectors. Add basic support for this to the block core, along with a 'blkdev_issue_discard()' helper function which issues such requests. The caller doesn't get to provide an end_io functio, since blkdev_issue_discard() will automatically split the request up into multiple bios if appropriate. Neither does the function wait for completion -- it's expected that callers won't care about when, or even _if_, the request completes. It's only a hint to the device anyway. By definition, the file system doesn't _care_ about these sectors any more. [With feedback from OGAWA Hirofumi and Jens Axboe Signed-off-by: Jens Axboe --- block/blk-barrier.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ block/blk-core.c | 28 ++++++++++++++------ block/blk-settings.c | 17 +++++++++++++ include/linux/bio.h | 8 ++++-- include/linux/blkdev.h | 16 ++++++++++++ include/linux/fs.h | 3 ++- 6 files changed, 130 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/block/blk-barrier.c b/block/blk-barrier.c index a09ead19f9c5..273121c0eb80 100644 --- a/block/blk-barrier.c +++ b/block/blk-barrier.c @@ -315,3 +315,72 @@ int blkdev_issue_flush(struct block_device *bdev, sector_t *error_sector) return ret; } EXPORT_SYMBOL(blkdev_issue_flush); + +static void blkdev_discard_end_io(struct bio *bio, int err) +{ + if (err) { + if (err == -EOPNOTSUPP) + set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); + clear_bit(BIO_UPTODATE, &bio->bi_flags); + } + + bio_put(bio); +} + +/** + * blkdev_issue_discard - queue a discard + * @bdev: blockdev to issue discard for + * @sector: start sector + * @nr_sects: number of sectors to discard + * + * Description: + * Issue a discard request for the sectors in question. Does not wait. + */ +int blkdev_issue_discard(struct block_device *bdev, sector_t sector, + unsigned nr_sects) +{ + struct request_queue *q; + struct bio *bio; + int ret = 0; + + if (bdev->bd_disk == NULL) + return -ENXIO; + + q = bdev_get_queue(bdev); + if (!q) + return -ENXIO; + + if (!q->prepare_discard_fn) + return -EOPNOTSUPP; + + while (nr_sects && !ret) { + bio = bio_alloc(GFP_KERNEL, 0); + if (!bio) + return -ENOMEM; + + bio->bi_end_io = blkdev_discard_end_io; + bio->bi_bdev = bdev; + + bio->bi_sector = sector; + + if (nr_sects > q->max_hw_sectors) { + bio->bi_size = q->max_hw_sectors << 9; + nr_sects -= q->max_hw_sectors; + sector += q->max_hw_sectors; + } else { + bio->bi_size = nr_sects << 9; + nr_sects = 0; + } + bio_get(bio); + submit_bio(WRITE_DISCARD, bio); + + /* Check if it failed immediately */ + if (bio_flagged(bio, BIO_EOPNOTSUPP)) + ret = -EOPNOTSUPP; + else if (!bio_flagged(bio, BIO_UPTODATE)) + ret = -EIO; + bio_put(bio); + } + return ret; +} +EXPORT_SYMBOL(blkdev_issue_discard); diff --git a/block/blk-core.c b/block/blk-core.c index a496727df7ef..1e143c4f9d34 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1079,6 +1079,10 @@ void init_request_from_bio(struct request *req, struct bio *bio) */ if (unlikely(bio_barrier(bio))) req->cmd_flags |= (REQ_HARDBARRIER | REQ_NOMERGE); + if (unlikely(bio_discard(bio))) { + req->cmd_flags |= (REQ_SOFTBARRIER | REQ_DISCARD); + req->q->prepare_discard_fn(req->q, req); + } if (bio_sync(bio)) req->cmd_flags |= REQ_RW_SYNC; @@ -1095,7 +1099,7 @@ void init_request_from_bio(struct request *req, struct bio *bio) static int __make_request(struct request_queue *q, struct bio *bio) { struct request *req; - int el_ret, nr_sectors, barrier, err; + int el_ret, nr_sectors, barrier, discard, err; const unsigned short prio = bio_prio(bio); const int sync = bio_sync(bio); int rw_flags; @@ -1115,6 +1119,12 @@ static int __make_request(struct request_queue *q, struct bio *bio) goto end_io; } + discard = bio_discard(bio); + if (unlikely(discard) && !q->prepare_discard_fn) { + err = -EOPNOTSUPP; + goto end_io; + } + spin_lock_irq(q->queue_lock); if (unlikely(barrier) || elv_queue_empty(q)) @@ -1405,7 +1415,8 @@ end_io: if (bio_check_eod(bio, nr_sectors)) goto end_io; - if (bio_empty_barrier(bio) && !q->prepare_flush_fn) { + if ((bio_empty_barrier(bio) && !q->prepare_flush_fn) || + (bio_discard(bio) && !q->prepare_discard_fn)) { err = -EOPNOTSUPP; goto end_io; } @@ -1487,7 +1498,6 @@ void submit_bio(int rw, struct bio *bio) * go through the normal accounting stuff before submission. */ if (bio_has_data(bio)) { - if (rw & WRITE) { count_vm_events(PGPGOUT, count); } else { @@ -1881,7 +1891,7 @@ static int blk_end_io(struct request *rq, int error, unsigned int nr_bytes, struct request_queue *q = rq->q; unsigned long flags = 0UL; - if (bio_has_data(rq->bio)) { + if (bio_has_data(rq->bio) || blk_discard_rq(rq)) { if (__end_that_request_first(rq, error, nr_bytes)) return 1; @@ -1939,7 +1949,7 @@ EXPORT_SYMBOL_GPL(blk_end_request); **/ int __blk_end_request(struct request *rq, int error, unsigned int nr_bytes) { - if (bio_has_data(rq->bio) && + if ((bio_has_data(rq->bio) || blk_discard_rq(rq)) && __end_that_request_first(rq, error, nr_bytes)) return 1; @@ -2012,12 +2022,14 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, we want BIO_RW_AHEAD (bit 1) to imply REQ_FAILFAST (bit 1). */ rq->cmd_flags |= (bio->bi_rw & 3); - rq->nr_phys_segments = bio_phys_segments(q, bio); - rq->nr_hw_segments = bio_hw_segments(q, bio); + if (bio_has_data(bio)) { + rq->nr_phys_segments = bio_phys_segments(q, bio); + rq->nr_hw_segments = bio_hw_segments(q, bio); + rq->buffer = bio_data(bio); + } rq->current_nr_sectors = bio_cur_sectors(bio); rq->hard_cur_sectors = rq->current_nr_sectors; rq->hard_nr_sectors = rq->nr_sectors = bio_sectors(bio); - rq->buffer = bio_data(bio); rq->data_len = bio->bi_size; rq->bio = rq->biotail = bio; diff --git a/block/blk-settings.c b/block/blk-settings.c index dfc77012843f..539d873c820d 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -32,6 +32,23 @@ void blk_queue_prep_rq(struct request_queue *q, prep_rq_fn *pfn) } EXPORT_SYMBOL(blk_queue_prep_rq); +/** + * blk_queue_set_discard - set a discard_sectors function for queue + * @q: queue + * @dfn: prepare_discard function + * + * It's possible for a queue to register a discard callback which is used + * to transform a discard request into the appropriate type for the + * hardware. If none is registered, then discard requests are failed + * with %EOPNOTSUPP. + * + */ +void blk_queue_set_discard(struct request_queue *q, prepare_discard_fn *dfn) +{ + q->prepare_discard_fn = dfn; +} +EXPORT_SYMBOL(blk_queue_set_discard); + /** * blk_queue_merge_bvec - set a merge_bvec function for queue * @q: queue diff --git a/include/linux/bio.h b/include/linux/bio.h index 17f1fbdb31bf..1fdfc5621c83 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -149,6 +149,8 @@ struct bio { * bit 2 -- barrier * bit 3 -- fail fast, don't want low level driver retries * bit 4 -- synchronous I/O hint: the block layer will unplug immediately + * bit 5 -- metadata request + * bit 6 -- discard sectors */ #define BIO_RW 0 /* Must match RW in req flags (blkdev.h) */ #define BIO_RW_AHEAD 1 /* Must match FAILFAST in req flags */ @@ -156,6 +158,7 @@ struct bio { #define BIO_RW_FAILFAST 3 #define BIO_RW_SYNC 4 #define BIO_RW_META 5 +#define BIO_RW_DISCARD 6 /* * upper 16 bits of bi_rw define the io priority of this bio @@ -186,13 +189,14 @@ struct bio { #define bio_rw_ahead(bio) ((bio)->bi_rw & (1 << BIO_RW_AHEAD)) #define bio_rw_meta(bio) ((bio)->bi_rw & (1 << BIO_RW_META)) #define bio_empty_barrier(bio) (bio_barrier(bio) && !bio_has_data(bio)) +#define bio_discard(bio) ((bio)->bi_rw & (1 << BIO_RW_DISCARD)) static inline unsigned int bio_cur_sectors(struct bio *bio) { if (bio->bi_vcnt) return bio_iovec(bio)->bv_len >> 9; - - return 0; + else /* dataless requests such as discard */ + return bio->bi_size >> 9; } static inline void *bio_data(struct bio *bio) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e0ba018f5e88..26ececbbebe2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -89,6 +89,7 @@ enum { enum rq_flag_bits { __REQ_RW, /* not set, read. set, write */ __REQ_FAILFAST, /* no low level driver retries */ + __REQ_DISCARD, /* request to discard sectors */ __REQ_SORTED, /* elevator knows about this request */ __REQ_SOFTBARRIER, /* may not be passed by ioscheduler */ __REQ_HARDBARRIER, /* may not be passed by drive either */ @@ -111,6 +112,7 @@ enum rq_flag_bits { }; #define REQ_RW (1 << __REQ_RW) +#define REQ_DISCARD (1 << __REQ_DISCARD) #define REQ_FAILFAST (1 << __REQ_FAILFAST) #define REQ_SORTED (1 << __REQ_SORTED) #define REQ_SOFTBARRIER (1 << __REQ_SOFTBARRIER) @@ -252,6 +254,7 @@ typedef void (request_fn_proc) (struct request_queue *q); typedef int (make_request_fn) (struct request_queue *q, struct bio *bio); typedef int (prep_rq_fn) (struct request_queue *, struct request *); typedef void (unplug_fn) (struct request_queue *); +typedef int (prepare_discard_fn) (struct request_queue *, struct request *); struct bio_vec; struct bvec_merge_data { @@ -307,6 +310,7 @@ struct request_queue make_request_fn *make_request_fn; prep_rq_fn *prep_rq_fn; unplug_fn *unplug_fn; + prepare_discard_fn *prepare_discard_fn; merge_bvec_fn *merge_bvec_fn; prepare_flush_fn *prepare_flush_fn; softirq_done_fn *softirq_done_fn; @@ -546,6 +550,7 @@ enum { #define blk_sorted_rq(rq) ((rq)->cmd_flags & REQ_SORTED) #define blk_barrier_rq(rq) ((rq)->cmd_flags & REQ_HARDBARRIER) #define blk_fua_rq(rq) ((rq)->cmd_flags & REQ_FUA) +#define blk_discard_rq(rq) ((rq)->cmd_flags & REQ_DISCARD) #define blk_bidi_rq(rq) ((rq)->next_rq != NULL) #define blk_empty_barrier(rq) (blk_barrier_rq(rq) && blk_fs_request(rq) && !(rq)->hard_nr_sectors) /* rq->queuelist of dequeued request must be list_empty() */ @@ -796,6 +801,7 @@ extern void blk_queue_merge_bvec(struct request_queue *, merge_bvec_fn *); extern void blk_queue_dma_alignment(struct request_queue *, int); extern void blk_queue_update_dma_alignment(struct request_queue *, int); extern void blk_queue_softirq_done(struct request_queue *, softirq_done_fn *); +extern void blk_queue_set_discard(struct request_queue *, prepare_discard_fn *); extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev); extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *); extern int blk_do_ordered(struct request_queue *, struct request **); @@ -837,6 +843,16 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt, } extern int blkdev_issue_flush(struct block_device *, sector_t *); +extern int blkdev_issue_discard(struct block_device *, sector_t sector, + unsigned nr_sects); + +static inline int sb_issue_discard(struct super_block *sb, + sector_t block, unsigned nr_blocks) +{ + block <<= (sb->s_blocksize_bits - 9); + nr_blocks <<= (sb->s_blocksize_bits - 9); + return blkdev_issue_discard(sb->s_bdev, block, nr_blocks); +} /* * command filter functions diff --git a/include/linux/fs.h b/include/linux/fs.h index 580b513668fe..eb0131319134 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -86,7 +86,8 @@ extern int dir_notify_enable; #define READ_META (READ | (1 << BIO_RW_META)) #define WRITE_SYNC (WRITE | (1 << BIO_RW_SYNC)) #define SWRITE_SYNC (SWRITE | (1 << BIO_RW_SYNC)) -#define WRITE_BARRIER ((1 << BIO_RW) | (1 << BIO_RW_BARRIER)) +#define WRITE_BARRIER (WRITE | (1 << BIO_RW_BARRIER)) +#define WRITE_DISCARD (WRITE | (1 << BIO_RW_DISCARD)) #define SEL_IN 1 #define SEL_OUT 2 -- cgit v1.2.3 From eae9acd13a8d14b50c00a961fa959606f34bbd92 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 5 Aug 2008 18:08:25 +0100 Subject: Support 'discard sectors' operation in translation layer support core Signed-off-by: David Woodhouse Signed-off-by: Jens Axboe --- drivers/mtd/mtd_blkdevs.c | 16 ++++++++++++++++ include/linux/blkdev.h | 1 + include/linux/mtd/blktrans.h | 2 ++ 3 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 9ff007c4962c..681d5aca2af4 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -32,6 +32,14 @@ struct mtd_blkcore_priv { spinlock_t queue_lock; }; +static int blktrans_discard_request(struct request_queue *q, + struct request *req) +{ + req->cmd_type = REQ_TYPE_LINUX_BLOCK; + req->cmd[0] = REQ_LB_OP_DISCARD; + return 0; +} + static int do_blktrans_request(struct mtd_blktrans_ops *tr, struct mtd_blktrans_dev *dev, struct request *req) @@ -44,6 +52,10 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, buf = req->buffer; + if (req->cmd_type == REQ_TYPE_LINUX_BLOCK && + req->cmd[0] == REQ_LB_OP_DISCARD) + return !tr->discard(dev, block, nsect); + if (!blk_fs_request(req)) return 0; @@ -367,6 +379,10 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) tr->blkcore_priv->rq->queuedata = tr; blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); + if (tr->discard) + blk_queue_set_discard(tr->blkcore_priv->rq, + blktrans_discard_request); + tr->blkshift = ffs(tr->blksize) - 1; tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 26ececbbebe2..727886d25c4e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -81,6 +81,7 @@ enum { */ REQ_LB_OP_EJECT = 0x40, /* eject request */ REQ_LB_OP_FLUSH = 0x41, /* flush device */ + REQ_LB_OP_DISCARD = 0x42, /* discard sectors */ }; /* diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index 310e61606415..8b4aa0523db7 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -41,6 +41,8 @@ struct mtd_blktrans_ops { unsigned long block, char *buffer); int (*writesect)(struct mtd_blktrans_dev *dev, unsigned long block, char *buffer); + int (*discard)(struct mtd_blktrans_dev *dev, + unsigned long block, unsigned nr_blocks); /* Block layer ioctls */ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo); -- cgit v1.2.3 From 27b29e86bf3d4b3cf6641a0efd78ed11a9b633b2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 10 Aug 2008 11:21:57 +0100 Subject: blktrace: support discard requests Signed-off-by: David Woodhouse Signed-off-by: Jens Axboe --- block/blktrace.c | 11 ++++++++++- include/linux/blktrace_api.h | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/block/blktrace.c b/block/blktrace.c index eb9651ccb241..7495a84353e4 100644 --- a/block/blktrace.c +++ b/block/blktrace.c @@ -114,7 +114,13 @@ static u32 ddir_act[2] __read_mostly = { BLK_TC_ACT(BLK_TC_READ), BLK_TC_ACT(BLK /* * Bio action bits of interest */ -static u32 bio_act[9] __read_mostly = { 0, BLK_TC_ACT(BLK_TC_BARRIER), BLK_TC_ACT(BLK_TC_SYNC), 0, BLK_TC_ACT(BLK_TC_AHEAD), 0, 0, 0, BLK_TC_ACT(BLK_TC_META) }; +static u32 bio_act[17] __read_mostly = { + [1] = BLK_TC_ACT(BLK_TC_BARRIER), + [2] = BLK_TC_ACT(BLK_TC_SYNC), + [4] = BLK_TC_ACT(BLK_TC_AHEAD), + [8] = BLK_TC_ACT(BLK_TC_META), + [16] = BLK_TC_ACT(BLK_TC_DISCARD) +}; /* * More could be added as needed, taking care to increment the decrementer @@ -128,6 +134,8 @@ static u32 bio_act[9] __read_mostly = { 0, BLK_TC_ACT(BLK_TC_BARRIER), BLK_TC_AC (((rw) & (1 << BIO_RW_AHEAD)) << (2 - BIO_RW_AHEAD)) #define trace_meta_bit(rw) \ (((rw) & (1 << BIO_RW_META)) >> (BIO_RW_META - 3)) +#define trace_discard_bit(rw) \ + (((rw) & (1 << BIO_RW_DISCARD)) >> (BIO_RW_DISCARD - 4)) /* * The worker for the various blk_add_trace*() types. Fills out a @@ -151,6 +159,7 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, what |= bio_act[trace_sync_bit(rw)]; what |= bio_act[trace_ahead_bit(rw)]; what |= bio_act[trace_meta_bit(rw)]; + what |= bio_act[trace_discard_bit(rw)]; pid = tsk->pid; if (unlikely(act_log_check(bt, what, sector, pid))) diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index d084b8d227a5..27da2cc682ee 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -21,6 +21,7 @@ enum blktrace_cat { BLK_TC_NOTIFY = 1 << 10, /* special message */ BLK_TC_AHEAD = 1 << 11, /* readahead */ BLK_TC_META = 1 << 12, /* metadata */ + BLK_TC_DISCARD = 1 << 13, /* discard requests */ BLK_TC_END = 1 << 15, /* only 16-bits, reminder */ }; @@ -195,6 +196,9 @@ static inline void blk_add_trace_rq(struct request_queue *q, struct request *rq, if (likely(!bt)) return; + if (blk_discard_rq(rq)) + rw |= (1 << BIO_RW_DISCARD); + if (blk_pc_request(rq)) { what |= BLK_TC_ACT(BLK_TC_PC); __blk_add_trace(bt, 0, rq->data_len, rw, what, rq->errors, sizeof(rq->cmd), rq->cmd); -- cgit v1.2.3 From d30a2605be9d5132d95944916e8f578fcfe4f976 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 11 Aug 2008 15:58:42 +0100 Subject: Add BLKDISCARD ioctl to allow userspace to discard sectors We may well want mkfs tools to use this to mark the whole device as unwanted before they format it, for example. The ioctl takes a pair of uint64_ts, which are start offset and length in _bytes_. Although at the moment it might make sense for them both to be in 512-byte sectors, I don't want to limit the ABI to that. Signed-off-by: David Woodhouse Signed-off-by: Jens Axboe --- block/compat_ioctl.c | 1 + block/ioctl.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + 3 files changed, 78 insertions(+) (limited to 'include/linux') diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index c23177e4623f..1e559fba7bdf 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -788,6 +788,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) return compat_hdio_getgeo(disk, bdev, compat_ptr(arg)); case BLKFLSBUF: case BLKROSET: + case BLKDISCARD: /* * the ones below are implemented in blkdev_locked_ioctl, * but we call blkdev_ioctl, which gets the lock for us diff --git a/block/ioctl.c b/block/ioctl.c index 77185e5c026a..342298bb6080 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -111,6 +111,69 @@ static int blkdev_reread_part(struct block_device *bdev) return res; } +static void blk_ioc_discard_endio(struct bio *bio, int err) +{ + if (err) { + if (err == -EOPNOTSUPP) + set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); + clear_bit(BIO_UPTODATE, &bio->bi_flags); + } + complete(bio->bi_private); +} + +static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, + uint64_t len) +{ + struct request_queue *q = bdev_get_queue(bdev); + int ret = 0; + + if (start & 511) + return -EINVAL; + if (len & 511) + return -EINVAL; + start >>= 9; + len >>= 9; + + if (start + len > (bdev->bd_inode->i_size >> 9)) + return -EINVAL; + + if (!q->prepare_discard_fn) + return -EOPNOTSUPP; + + while (len && !ret) { + DECLARE_COMPLETION_ONSTACK(wait); + struct bio *bio; + + bio = bio_alloc(GFP_KERNEL, 0); + if (!bio) + return -ENOMEM; + + bio->bi_end_io = blk_ioc_discard_endio; + bio->bi_bdev = bdev; + bio->bi_private = &wait; + bio->bi_sector = start; + + if (len > q->max_hw_sectors) { + bio->bi_size = q->max_hw_sectors << 9; + len -= q->max_hw_sectors; + start += q->max_hw_sectors; + } else { + bio->bi_size = len << 9; + len = 0; + } + submit_bio(WRITE_DISCARD, bio); + + wait_for_completion(&wait); + + if (bio_flagged(bio, BIO_EOPNOTSUPP)) + ret = -EOPNOTSUPP; + else if (!bio_flagged(bio, BIO_UPTODATE)) + ret = -EIO; + bio_put(bio); + } + return ret; +} + static int put_ushort(unsigned long arg, unsigned short val) { return put_user(val, (unsigned short __user *)arg); @@ -258,6 +321,19 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, set_device_ro(bdev, n); unlock_kernel(); return 0; + + case BLKDISCARD: { + uint64_t range[2]; + + if (!(file->f_mode & FMODE_WRITE)) + return -EBADF; + + if (copy_from_user(range, (void __user *)arg, sizeof(range))) + return -EFAULT; + + return blk_ioctl_discard(bdev, range[0], range[1]); + } + case HDIO_GETGEO: { struct hd_geometry geo; diff --git a/include/linux/fs.h b/include/linux/fs.h index eb0131319134..88358ca6af25 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -223,6 +223,7 @@ extern int dir_notify_enable; #define BLKTRACESTART _IO(0x12,116) #define BLKTRACESTOP _IO(0x12,117) #define BLKTRACETEARDOWN _IO(0x12,118) +#define BLKDISCARD _IO(0x12,119) #define BMAP_IOCTL 1 /* obsolete - kept for compatibility */ #define FIBMAP _IO(0x00,1) /* bmap access */ -- cgit v1.2.3 From e17fc0a1ccf88f6d4dcb363729f3141b0958c325 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 9 Aug 2008 16:42:20 +0100 Subject: Allow elevators to sort/merge discard requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit But blkdev_issue_discard() still emits requests which are interpreted as soft barriers, because naïve callers might otherwise issue subsequent writes to those same sectors, which might cross on the queue (if they're reallocated quickly enough). Callers still _can_ issue non-barrier discard requests, but they have to take care of queue ordering for themselves. Signed-off-by: David Woodhouse Signed-off-by: Jens Axboe --- block/blk-barrier.c | 2 +- block/blk-core.c | 12 +++++++----- block/blk-merge.c | 27 +++++++++++++++++---------- block/elevator.c | 12 ++++++++++-- block/ioctl.c | 2 +- include/linux/bio.h | 2 +- include/linux/blkdev.h | 5 +++-- include/linux/fs.h | 3 ++- 8 files changed, 42 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/block/blk-barrier.c b/block/blk-barrier.c index e5448131d4f1..988b63479b2f 100644 --- a/block/blk-barrier.c +++ b/block/blk-barrier.c @@ -372,7 +372,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, nr_sects = 0; } bio_get(bio); - submit_bio(WRITE_DISCARD, bio); + submit_bio(DISCARD_BARRIER, bio); /* Check if it failed immediately */ if (bio_flagged(bio, BIO_EOPNOTSUPP)) diff --git a/block/blk-core.c b/block/blk-core.c index 1e143c4f9d34..1261516dd42a 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1077,12 +1077,13 @@ void init_request_from_bio(struct request *req, struct bio *bio) /* * REQ_BARRIER implies no merging, but lets make it explicit */ - if (unlikely(bio_barrier(bio))) - req->cmd_flags |= (REQ_HARDBARRIER | REQ_NOMERGE); if (unlikely(bio_discard(bio))) { - req->cmd_flags |= (REQ_SOFTBARRIER | REQ_DISCARD); + req->cmd_flags |= REQ_DISCARD; + if (bio_barrier(bio)) + req->cmd_flags |= REQ_SOFTBARRIER; req->q->prepare_discard_fn(req->q, req); - } + } else if (unlikely(bio_barrier(bio))) + req->cmd_flags |= (REQ_HARDBARRIER | REQ_NOMERGE); if (bio_sync(bio)) req->cmd_flags |= REQ_RW_SYNC; @@ -1114,7 +1115,8 @@ static int __make_request(struct request_queue *q, struct bio *bio) blk_queue_bounce(q, &bio); barrier = bio_barrier(bio); - if (unlikely(barrier) && (q->next_ordered == QUEUE_ORDERED_NONE)) { + if (unlikely(barrier) && bio_has_data(bio) && + (q->next_ordered == QUEUE_ORDERED_NONE)) { err = -EOPNOTSUPP; goto end_io; } diff --git a/block/blk-merge.c b/block/blk-merge.c index 5efc9e7a68b7..6cf8f0c70a51 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -11,7 +11,7 @@ void blk_recalc_rq_sectors(struct request *rq, int nsect) { - if (blk_fs_request(rq)) { + if (blk_fs_request(rq) || blk_discard_rq(rq)) { rq->hard_sector += nsect; rq->hard_nr_sectors -= nsect; @@ -131,13 +131,17 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio, if (!test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags)) return 0; - if (!BIOVEC_PHYS_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt))) - return 0; if (bio->bi_size + nxt->bi_size > q->max_segment_size) return 0; + if (!bio_has_data(bio)) + return 1; + + if (!BIOVEC_PHYS_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt))) + return 0; + /* - * bio and nxt are contigous in memory, check if the queue allows + * bio and nxt are contiguous in memory; check if the queue allows * these two to be merged into one */ if (BIO_SEG_BOUNDARY(q, bio, nxt)) @@ -153,8 +157,9 @@ static int blk_hw_contig_segment(struct request_queue *q, struct bio *bio, blk_recount_segments(q, bio); if (!bio_flagged(nxt, BIO_SEG_VALID)) blk_recount_segments(q, nxt); - if (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) || - BIOVEC_VIRT_OVERSIZE(bio->bi_hw_back_size + nxt->bi_hw_front_size)) + if (bio_has_data(bio) && + (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) || + BIOVEC_VIRT_OVERSIZE(bio->bi_hw_back_size + nxt->bi_hw_front_size))) return 0; if (bio->bi_hw_back_size + nxt->bi_hw_front_size > q->max_segment_size) return 0; @@ -317,8 +322,9 @@ int ll_back_merge_fn(struct request_queue *q, struct request *req, if (!bio_flagged(bio, BIO_SEG_VALID)) blk_recount_segments(q, bio); len = req->biotail->bi_hw_back_size + bio->bi_hw_front_size; - if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio)) - && !BIOVEC_VIRT_OVERSIZE(len)) { + if (!bio_has_data(bio) || + (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio)) + && !BIOVEC_VIRT_OVERSIZE(len))) { int mergeable = ll_new_mergeable(q, req, bio); if (mergeable) { @@ -356,8 +362,9 @@ int ll_front_merge_fn(struct request_queue *q, struct request *req, blk_recount_segments(q, bio); if (!bio_flagged(req->bio, BIO_SEG_VALID)) blk_recount_segments(q, req->bio); - if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) && - !BIOVEC_VIRT_OVERSIZE(len)) { + if (!bio_has_data(bio) || + (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) && + !BIOVEC_VIRT_OVERSIZE(len))) { int mergeable = ll_new_mergeable(q, req, bio); if (mergeable) { diff --git a/block/elevator.c b/block/elevator.c index ed6f8f32d27e..4f5127054e3f 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -74,6 +74,12 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio) if (!rq_mergeable(rq)) return 0; + /* + * Don't merge file system requests and discard requests + */ + if (bio_discard(bio) != bio_discard(rq->bio)) + return 0; + /* * different data direction or already started, don't merge */ @@ -438,6 +444,8 @@ void elv_dispatch_sort(struct request_queue *q, struct request *rq) list_for_each_prev(entry, &q->queue_head) { struct request *pos = list_entry_rq(entry); + if (blk_discard_rq(rq) != blk_discard_rq(pos)) + break; if (rq_data_dir(rq) != rq_data_dir(pos)) break; if (pos->cmd_flags & stop_flags) @@ -607,7 +615,7 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) break; case ELEVATOR_INSERT_SORT: - BUG_ON(!blk_fs_request(rq)); + BUG_ON(!blk_fs_request(rq) && !blk_discard_rq(rq)); rq->cmd_flags |= REQ_SORTED; q->nr_sorted++; if (rq_mergeable(rq)) { @@ -692,7 +700,7 @@ void __elv_add_request(struct request_queue *q, struct request *rq, int where, * this request is scheduling boundary, update * end_sector */ - if (blk_fs_request(rq)) { + if (blk_fs_request(rq) || blk_discard_rq(rq)) { q->end_sector = rq_end_sector(rq); q->boundary_rq = rq; } diff --git a/block/ioctl.c b/block/ioctl.c index 342298bb6080..375c57922b00 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -161,7 +161,7 @@ static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, bio->bi_size = len << 9; len = 0; } - submit_bio(WRITE_DISCARD, bio); + submit_bio(DISCARD_NOBARRIER, bio); wait_for_completion(&wait); diff --git a/include/linux/bio.h b/include/linux/bio.h index 1fdfc5621c83..33c3947d61e9 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -188,8 +188,8 @@ struct bio { #define bio_failfast(bio) ((bio)->bi_rw & (1 << BIO_RW_FAILFAST)) #define bio_rw_ahead(bio) ((bio)->bi_rw & (1 << BIO_RW_AHEAD)) #define bio_rw_meta(bio) ((bio)->bi_rw & (1 << BIO_RW_META)) -#define bio_empty_barrier(bio) (bio_barrier(bio) && !bio_has_data(bio)) #define bio_discard(bio) ((bio)->bi_rw & (1 << BIO_RW_DISCARD)) +#define bio_empty_barrier(bio) (bio_barrier(bio) && !bio_has_data(bio) && !bio_discard(bio)) static inline unsigned int bio_cur_sectors(struct bio *bio) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 727886d25c4e..e9eb35c9bf26 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -541,7 +541,7 @@ enum { #define blk_noretry_request(rq) ((rq)->cmd_flags & REQ_FAILFAST) #define blk_rq_started(rq) ((rq)->cmd_flags & REQ_STARTED) -#define blk_account_rq(rq) (blk_rq_started(rq) && blk_fs_request(rq)) +#define blk_account_rq(rq) (blk_rq_started(rq) && (blk_fs_request(rq) || blk_discard_rq(rq))) #define blk_pm_suspend_request(rq) ((rq)->cmd_type == REQ_TYPE_PM_SUSPEND) #define blk_pm_resume_request(rq) ((rq)->cmd_type == REQ_TYPE_PM_RESUME) @@ -598,7 +598,8 @@ static inline void blk_clear_queue_full(struct request_queue *q, int rw) #define RQ_NOMERGE_FLAGS \ (REQ_NOMERGE | REQ_STARTED | REQ_HARDBARRIER | REQ_SOFTBARRIER) #define rq_mergeable(rq) \ - (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && blk_fs_request((rq))) + (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && \ + (blk_discard_rq(rq) || blk_fs_request((rq)))) /* * q->prep_rq_fn return values diff --git a/include/linux/fs.h b/include/linux/fs.h index 88358ca6af25..860689f541b1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -87,7 +87,8 @@ extern int dir_notify_enable; #define WRITE_SYNC (WRITE | (1 << BIO_RW_SYNC)) #define SWRITE_SYNC (SWRITE | (1 << BIO_RW_SYNC)) #define WRITE_BARRIER (WRITE | (1 << BIO_RW_BARRIER)) -#define WRITE_DISCARD (WRITE | (1 << BIO_RW_DISCARD)) +#define DISCARD_NOBARRIER (1 << BIO_RW_DISCARD) +#define DISCARD_BARRIER ((1 << BIO_RW_DISCARD) | (1 << BIO_RW_BARRIER)) #define SEL_IN 1 #define SEL_OUT 2 -- cgit v1.2.3 From 1a8e2bddd5c29008f311613e75925fecbf522c5b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 13 Aug 2008 12:35:09 +0100 Subject: Kill REQ_TYPE_FLUSH It was only used by ps3disk, and it should probably have been REQ_TYPE_LINUX_BLOCK + REQ_LB_OP_FLUSH. Signed-off-by: David Woodhouse Signed-off-by: Jens Axboe --- drivers/block/ps3disk.c | 9 ++++++--- include/linux/blkdev.h | 6 +----- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c index d797e209951d..4b0d6c7f4c66 100644 --- a/drivers/block/ps3disk.c +++ b/drivers/block/ps3disk.c @@ -199,7 +199,8 @@ static void ps3disk_do_request(struct ps3_storage_device *dev, if (blk_fs_request(req)) { if (ps3disk_submit_request_sg(dev, req)) break; - } else if (req->cmd_type == REQ_TYPE_FLUSH) { + } else if (req->cmd_type == REQ_TYPE_LINUX_BLOCK && + req->cmd[0] == REQ_LB_OP_FLUSH) { if (ps3disk_submit_flush_request(dev, req)) break; } else { @@ -257,7 +258,8 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data) return IRQ_HANDLED; } - if (req->cmd_type == REQ_TYPE_FLUSH) { + if (req->cmd_type == REQ_TYPE_LINUX_BLOCK && + req->cmd[0] == REQ_LB_OP_FLUSH) { read = 0; num_sectors = req->hard_cur_sectors; op = "flush"; @@ -405,7 +407,8 @@ static void ps3disk_prepare_flush(struct request_queue *q, struct request *req) dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); - req->cmd_type = REQ_TYPE_FLUSH; + req->cmd_type = REQ_TYPE_LINUX_BLOCK; + req->cmd[0] = REQ_LB_OP_FLUSH; } static unsigned long ps3disk_mask; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e9eb35c9bf26..f131776f029e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -54,7 +54,6 @@ enum rq_cmd_type_bits { REQ_TYPE_PM_SUSPEND, /* suspend request */ REQ_TYPE_PM_RESUME, /* resume request */ REQ_TYPE_PM_SHUTDOWN, /* shutdown request */ - REQ_TYPE_FLUSH, /* flush request */ REQ_TYPE_SPECIAL, /* driver defined type */ REQ_TYPE_LINUX_BLOCK, /* generic block layer message */ /* @@ -76,11 +75,8 @@ enum rq_cmd_type_bits { * */ enum { - /* - * just examples for now - */ REQ_LB_OP_EJECT = 0x40, /* eject request */ - REQ_LB_OP_FLUSH = 0x41, /* flush device */ + REQ_LB_OP_FLUSH = 0x41, /* flush request */ REQ_LB_OP_DISCARD = 0x42, /* discard sectors */ }; -- cgit v1.2.3 From 766ca4428d1239a970926856c447310c9c191af2 Mon Sep 17 00:00:00 2001 From: Fernando Luis Vázquez Cao Date: Thu, 14 Aug 2008 09:59:13 +0200 Subject: virtio_blk: use a wrapper function to access io context information of IO requests struct request has an ioprio member but it is never updated because currently bios do not hold io context information. The implication of this is that virtio_blk ends up passing useless information to the backend driver. That said, some IO schedulers such as CFQ do store io context information in struct request, but use private members for that, which means that that information cannot be directly accessed in a IO scheduler-independent way. This patch adds a function to obtain the ioprio of a request. We should avoid accessing ioprio directly and use this function instead, so that its users do not have to care about future changes in block layer structures or what the currently active IO controller is. This patch does not introduce any functional changes but paves the way for future clean-ups and enhancements. Signed-off-by: Fernando Luis Vazquez Cao Acked-by: Rusty Russell Signed-off-by: Jens Axboe --- drivers/block/virtio_blk.c | 4 ++-- include/linux/blkdev.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 42251095134f..879506a2c234 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -84,11 +84,11 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, if (blk_fs_request(vbr->req)) { vbr->out_hdr.type = 0; vbr->out_hdr.sector = vbr->req->sector; - vbr->out_hdr.ioprio = vbr->req->ioprio; + vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); } else if (blk_pc_request(vbr->req)) { vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD; vbr->out_hdr.sector = 0; - vbr->out_hdr.ioprio = vbr->req->ioprio; + vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); } else { /* We don't put anything else in the queue. */ BUG(); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f131776f029e..490ce458b031 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -232,6 +232,11 @@ struct request { struct request *next_rq; }; +static inline unsigned short req_get_ioprio(struct request *req) +{ + return req->ioprio; +} + /* * State information carried for REQ_TYPE_PM_SUSPEND and REQ_TYPE_PM_RESUME * requests. Some step values could eventually be made generic. -- cgit v1.2.3 From b8b3e16cfe6435d961f6aaebcfd52a1ff2a988c5 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 15 Aug 2008 10:15:19 +0200 Subject: block: drop virtual merging accounting Remove virtual merge accounting. Signed-off-by: Mikulas Patocka Signed-off-by: Jens Axboe --- block/blk-merge.c | 79 ++++------------------------------------------------- fs/bio.c | 6 ++-- include/linux/bio.h | 15 ---------- 3 files changed, 8 insertions(+), 92 deletions(-) (limited to 'include/linux') diff --git a/block/blk-merge.c b/block/blk-merge.c index 6cf8f0c70a51..2c2a2ee716ec 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -66,7 +66,7 @@ void blk_recalc_rq_segments(struct request *rq) */ high = page_to_pfn(bv->bv_page) > q->bounce_pfn; if (high || highprv) - goto new_hw_segment; + goto new_segment; if (cluster) { if (seg_size + bv->bv_len > q->max_segment_size) goto new_segment; @@ -74,8 +74,6 @@ void blk_recalc_rq_segments(struct request *rq) goto new_segment; if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bv)) goto new_segment; - if (BIOVEC_VIRT_OVERSIZE(hw_seg_size + bv->bv_len)) - goto new_hw_segment; seg_size += bv->bv_len; hw_seg_size += bv->bv_len; @@ -83,17 +81,11 @@ void blk_recalc_rq_segments(struct request *rq) continue; } new_segment: - if (BIOVEC_VIRT_MERGEABLE(bvprv, bv) && - !BIOVEC_VIRT_OVERSIZE(hw_seg_size + bv->bv_len)) - hw_seg_size += bv->bv_len; - else { -new_hw_segment: - if (nr_hw_segs == 1 && - hw_seg_size > rq->bio->bi_hw_front_size) - rq->bio->bi_hw_front_size = hw_seg_size; - hw_seg_size = BIOVEC_VIRT_START_SIZE(bv) + bv->bv_len; - nr_hw_segs++; - } + if (nr_hw_segs == 1 && + hw_seg_size > rq->bio->bi_hw_front_size) + rq->bio->bi_hw_front_size = hw_seg_size; + hw_seg_size = bv->bv_len; + nr_hw_segs++; nr_phys_segs++; bvprv = bv; @@ -150,23 +142,6 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio, return 0; } -static int blk_hw_contig_segment(struct request_queue *q, struct bio *bio, - struct bio *nxt) -{ - if (!bio_flagged(bio, BIO_SEG_VALID)) - blk_recount_segments(q, bio); - if (!bio_flagged(nxt, BIO_SEG_VALID)) - blk_recount_segments(q, nxt); - if (bio_has_data(bio) && - (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) || - BIOVEC_VIRT_OVERSIZE(bio->bi_hw_back_size + nxt->bi_hw_front_size))) - return 0; - if (bio->bi_hw_back_size + nxt->bi_hw_front_size > q->max_segment_size) - return 0; - - return 1; -} - /* * map a request to scatterlist, return number of sg entries setup. Caller * must make sure sg can hold rq->nr_phys_segments entries @@ -304,7 +279,6 @@ int ll_back_merge_fn(struct request_queue *q, struct request *req, struct bio *bio) { unsigned short max_sectors; - int len; if (unlikely(blk_pc_request(req))) max_sectors = q->max_hw_sectors; @@ -321,20 +295,6 @@ int ll_back_merge_fn(struct request_queue *q, struct request *req, blk_recount_segments(q, req->biotail); if (!bio_flagged(bio, BIO_SEG_VALID)) blk_recount_segments(q, bio); - len = req->biotail->bi_hw_back_size + bio->bi_hw_front_size; - if (!bio_has_data(bio) || - (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio)) - && !BIOVEC_VIRT_OVERSIZE(len))) { - int mergeable = ll_new_mergeable(q, req, bio); - - if (mergeable) { - if (req->nr_hw_segments == 1) - req->bio->bi_hw_front_size = len; - if (bio->bi_hw_segments == 1) - bio->bi_hw_back_size = len; - } - return mergeable; - } return ll_new_hw_segment(q, req, bio); } @@ -343,7 +303,6 @@ int ll_front_merge_fn(struct request_queue *q, struct request *req, struct bio *bio) { unsigned short max_sectors; - int len; if (unlikely(blk_pc_request(req))) max_sectors = q->max_hw_sectors; @@ -357,24 +316,10 @@ int ll_front_merge_fn(struct request_queue *q, struct request *req, q->last_merge = NULL; return 0; } - len = bio->bi_hw_back_size + req->bio->bi_hw_front_size; if (!bio_flagged(bio, BIO_SEG_VALID)) blk_recount_segments(q, bio); if (!bio_flagged(req->bio, BIO_SEG_VALID)) blk_recount_segments(q, req->bio); - if (!bio_has_data(bio) || - (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) && - !BIOVEC_VIRT_OVERSIZE(len))) { - int mergeable = ll_new_mergeable(q, req, bio); - - if (mergeable) { - if (bio->bi_hw_segments == 1) - bio->bi_hw_front_size = len; - if (req->nr_hw_segments == 1) - req->biotail->bi_hw_back_size = len; - } - return mergeable; - } return ll_new_hw_segment(q, req, bio); } @@ -406,18 +351,6 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, return 0; total_hw_segments = req->nr_hw_segments + next->nr_hw_segments; - if (blk_hw_contig_segment(q, req->biotail, next->bio)) { - int len = req->biotail->bi_hw_back_size + - next->bio->bi_hw_front_size; - /* - * propagate the combined length to the end of the requests - */ - if (req->nr_hw_segments == 1) - req->bio->bi_hw_front_size = len; - if (next->nr_hw_segments == 1) - next->biotail->bi_hw_back_size = len; - total_hw_segments--; - } if (total_hw_segments > q->max_hw_segments) return 0; diff --git a/fs/bio.c b/fs/bio.c index 3cba7ae34d75..4ac7c59d1c6d 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -350,8 +350,7 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page */ while (bio->bi_phys_segments >= q->max_phys_segments - || bio->bi_hw_segments >= q->max_hw_segments - || BIOVEC_VIRT_OVERSIZE(bio->bi_size)) { + || bio->bi_hw_segments >= q->max_hw_segments) { if (retried_segments) return 0; @@ -395,8 +394,7 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page } /* If we may be able to merge these biovecs, force a recount */ - if (bio->bi_vcnt && (BIOVEC_PHYS_MERGEABLE(bvec-1, bvec) || - BIOVEC_VIRT_MERGEABLE(bvec-1, bvec))) + if (bio->bi_vcnt && (BIOVEC_PHYS_MERGEABLE(bvec-1, bvec))) bio->bi_flags &= ~(1 << BIO_SEG_VALID); bio->bi_vcnt++; diff --git a/include/linux/bio.h b/include/linux/bio.h index 33c3947d61e9..894d16ce0020 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -26,21 +26,8 @@ #ifdef CONFIG_BLOCK -/* Platforms may set this to teach the BIO layer about IOMMU hardware. */ #include -#if defined(BIO_VMERGE_MAX_SIZE) && defined(BIO_VMERGE_BOUNDARY) -#define BIOVEC_VIRT_START_SIZE(x) (bvec_to_phys(x) & (BIO_VMERGE_BOUNDARY - 1)) -#define BIOVEC_VIRT_OVERSIZE(x) ((x) > BIO_VMERGE_MAX_SIZE) -#else -#define BIOVEC_VIRT_START_SIZE(x) 0 -#define BIOVEC_VIRT_OVERSIZE(x) 0 -#endif - -#ifndef BIO_VMERGE_BOUNDARY -#define BIO_VMERGE_BOUNDARY 0 -#endif - #define BIO_DEBUG #ifdef BIO_DEBUG @@ -240,8 +227,6 @@ static inline void *bio_data(struct bio *bio) ((bvec_to_phys((vec1)) + (vec1)->bv_len) == bvec_to_phys((vec2))) #endif -#define BIOVEC_VIRT_MERGEABLE(vec1, vec2) \ - ((((bvec_to_phys((vec1)) + (vec1)->bv_len) | bvec_to_phys((vec2))) & (BIO_VMERGE_BOUNDARY - 1)) == 0) #define __BIO_SEG_BOUNDARY(addr1, addr2, mask) \ (((addr1) | (mask)) == (((addr2) - 1) | (mask))) #define BIOVEC_SEG_BOUNDARY(q, b1, b2) \ -- cgit v1.2.3 From 5df97b91b5d7ed426034fcc84cb6e7cf682b8838 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 15 Aug 2008 10:20:02 +0200 Subject: drop vmerge accounting Remove hw_segments field from struct bio and struct request. Without virtual merge accounting they have no purpose. Signed-off-by: Mikulas Patocka Signed-off-by: Jens Axboe --- block/blk-core.c | 1 - block/blk-merge.c | 31 ++++--------------------------- block/elevator.c | 2 -- drivers/md/raid1.c | 3 --- drivers/md/raid10.c | 3 --- fs/bio.c | 12 +----------- include/linux/bio.h | 16 +--------------- include/linux/blkdev.h | 7 ------- 8 files changed, 6 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 1261516dd42a..2616cdd049a8 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2026,7 +2026,6 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, if (bio_has_data(bio)) { rq->nr_phys_segments = bio_phys_segments(q, bio); - rq->nr_hw_segments = bio_hw_segments(q, bio); rq->buffer = bio_data(bio); } rq->current_nr_sectors = bio_cur_sectors(bio); diff --git a/block/blk-merge.c b/block/blk-merge.c index 2c2a2ee716ec..d81d91419ff5 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -41,12 +41,9 @@ void blk_recalc_rq_sectors(struct request *rq, int nsect) void blk_recalc_rq_segments(struct request *rq) { int nr_phys_segs; - int nr_hw_segs; unsigned int phys_size; - unsigned int hw_size; struct bio_vec *bv, *bvprv = NULL; int seg_size; - int hw_seg_size; int cluster; struct req_iterator iter; int high, highprv = 1; @@ -56,8 +53,8 @@ void blk_recalc_rq_segments(struct request *rq) return; cluster = test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags); - hw_seg_size = seg_size = 0; - phys_size = hw_size = nr_phys_segs = nr_hw_segs = 0; + seg_size = 0; + phys_size = nr_phys_segs = 0; rq_for_each_segment(bv, rq, iter) { /* * the trick here is making sure that a high page is never @@ -76,30 +73,17 @@ void blk_recalc_rq_segments(struct request *rq) goto new_segment; seg_size += bv->bv_len; - hw_seg_size += bv->bv_len; bvprv = bv; continue; } new_segment: - if (nr_hw_segs == 1 && - hw_seg_size > rq->bio->bi_hw_front_size) - rq->bio->bi_hw_front_size = hw_seg_size; - hw_seg_size = bv->bv_len; - nr_hw_segs++; - nr_phys_segs++; bvprv = bv; seg_size = bv->bv_len; highprv = high; } - if (nr_hw_segs == 1 && - hw_seg_size > rq->bio->bi_hw_front_size) - rq->bio->bi_hw_front_size = hw_seg_size; - if (hw_seg_size > rq->biotail->bi_hw_back_size) - rq->biotail->bi_hw_back_size = hw_seg_size; rq->nr_phys_segments = nr_phys_segs; - rq->nr_hw_segments = nr_hw_segs; } void blk_recount_segments(struct request_queue *q, struct bio *bio) @@ -112,7 +96,6 @@ void blk_recount_segments(struct request_queue *q, struct bio *bio) blk_recalc_rq_segments(&rq); bio->bi_next = nxt; bio->bi_phys_segments = rq.nr_phys_segments; - bio->bi_hw_segments = rq.nr_hw_segments; bio->bi_flags |= (1 << BIO_SEG_VALID); } EXPORT_SYMBOL(blk_recount_segments); @@ -255,10 +238,9 @@ static inline int ll_new_hw_segment(struct request_queue *q, struct request *req, struct bio *bio) { - int nr_hw_segs = bio_hw_segments(q, bio); int nr_phys_segs = bio_phys_segments(q, bio); - if (req->nr_hw_segments + nr_hw_segs > q->max_hw_segments + if (req->nr_phys_segments + nr_phys_segs > q->max_hw_segments || req->nr_phys_segments + nr_phys_segs > q->max_phys_segments) { req->cmd_flags |= REQ_NOMERGE; if (req == q->last_merge) @@ -270,7 +252,6 @@ static inline int ll_new_hw_segment(struct request_queue *q, * This will form the start of a new hw segment. Bump both * counters. */ - req->nr_hw_segments += nr_hw_segs; req->nr_phys_segments += nr_phys_segs; return 1; } @@ -328,7 +309,6 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, struct request *next) { int total_phys_segments; - int total_hw_segments; /* * First check if the either of the requests are re-queued @@ -350,14 +330,11 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, if (total_phys_segments > q->max_phys_segments) return 0; - total_hw_segments = req->nr_hw_segments + next->nr_hw_segments; - - if (total_hw_segments > q->max_hw_segments) + if (total_phys_segments > q->max_hw_segments) return 0; /* Merge is OK... */ req->nr_phys_segments = total_phys_segments; - req->nr_hw_segments = total_hw_segments; return 1; } diff --git a/block/elevator.c b/block/elevator.c index 4f5127054e3f..269615e6dbf5 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -790,7 +790,6 @@ struct request *elv_next_request(struct request_queue *q) * device can handle */ rq->nr_phys_segments++; - rq->nr_hw_segments++; } if (!q->prep_rq_fn) @@ -813,7 +812,6 @@ struct request *elv_next_request(struct request_queue *q) * so that we don't add it again */ --rq->nr_phys_segments; - --rq->nr_hw_segments; } rq = NULL; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 03a5ab705c20..28a3869dcfd2 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1302,9 +1302,6 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio) sbio->bi_size = r1_bio->sectors << 9; sbio->bi_idx = 0; sbio->bi_phys_segments = 0; - sbio->bi_hw_segments = 0; - sbio->bi_hw_front_size = 0; - sbio->bi_hw_back_size = 0; sbio->bi_flags &= ~(BIO_POOL_MASK - 1); sbio->bi_flags |= 1 << BIO_UPTODATE; sbio->bi_next = NULL; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index e34cd0e62473..0f40688503e7 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1345,9 +1345,6 @@ static void sync_request_write(mddev_t *mddev, r10bio_t *r10_bio) tbio->bi_size = r10_bio->sectors << 9; tbio->bi_idx = 0; tbio->bi_phys_segments = 0; - tbio->bi_hw_segments = 0; - tbio->bi_hw_front_size = 0; - tbio->bi_hw_back_size = 0; tbio->bi_flags &= ~(BIO_POOL_MASK - 1); tbio->bi_flags |= 1 << BIO_UPTODATE; tbio->bi_next = NULL; diff --git a/fs/bio.c b/fs/bio.c index 4ac7c59d1c6d..bee4deca774a 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -208,14 +208,6 @@ inline int bio_phys_segments(struct request_queue *q, struct bio *bio) return bio->bi_phys_segments; } -inline int bio_hw_segments(struct request_queue *q, struct bio *bio) -{ - if (unlikely(!bio_flagged(bio, BIO_SEG_VALID))) - blk_recount_segments(q, bio); - - return bio->bi_hw_segments; -} - /** * __bio_clone - clone a bio * @bio: destination bio @@ -350,7 +342,7 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page */ while (bio->bi_phys_segments >= q->max_phys_segments - || bio->bi_hw_segments >= q->max_hw_segments) { + || bio->bi_phys_segments >= q->max_hw_segments) { if (retried_segments) return 0; @@ -399,7 +391,6 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page bio->bi_vcnt++; bio->bi_phys_segments++; - bio->bi_hw_segments++; done: bio->bi_size += len; return len; @@ -1381,7 +1372,6 @@ EXPORT_SYMBOL(bio_init); EXPORT_SYMBOL(__bio_clone); EXPORT_SYMBOL(bio_clone); EXPORT_SYMBOL(bio_phys_segments); -EXPORT_SYMBOL(bio_hw_segments); EXPORT_SYMBOL(bio_add_page); EXPORT_SYMBOL(bio_add_pc_page); EXPORT_SYMBOL(bio_get_nr_vecs); diff --git a/include/linux/bio.h b/include/linux/bio.h index 894d16ce0020..dfc3556d311c 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -77,21 +77,8 @@ struct bio { */ unsigned short bi_phys_segments; - /* Number of segments after physical and DMA remapping - * hardware coalescing is performed. - */ - unsigned short bi_hw_segments; - unsigned int bi_size; /* residual I/O count */ - /* - * To keep track of the max hw size, we account for the - * sizes of the first and last virtually mergeable segments - * in this bio - */ - unsigned int bi_hw_front_size; - unsigned int bi_hw_back_size; - unsigned int bi_max_vecs; /* max bvl_vecs we can hold */ struct bio_vec *bi_io_vec; /* the actual vec list */ @@ -113,7 +100,7 @@ struct bio { #define BIO_UPTODATE 0 /* ok after I/O completion */ #define BIO_RW_BLOCK 1 /* RW_AHEAD set, and read/write would block */ #define BIO_EOF 2 /* out-out-bounds error */ -#define BIO_SEG_VALID 3 /* nr_hw_seg valid */ +#define BIO_SEG_VALID 3 /* bi_phys_segments valid */ #define BIO_CLONED 4 /* doesn't own data */ #define BIO_BOUNCED 5 /* bio is a bounce bio */ #define BIO_USER_MAPPED 6 /* contains user pages */ @@ -324,7 +311,6 @@ extern void bio_free(struct bio *, struct bio_set *); extern void bio_endio(struct bio *, int); struct request_queue; extern int bio_phys_segments(struct request_queue *, struct bio *); -extern int bio_hw_segments(struct request_queue *, struct bio *); extern void __bio_clone(struct bio *, struct bio *); extern struct bio *bio_clone(struct bio *, gfp_t); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 490ce458b031..1adb03827bd3 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -189,13 +189,6 @@ struct request { */ unsigned short nr_phys_segments; - /* Number of scatter-gather addr+len pairs after - * physical and DMA remapping hardware coalescing is performed. - * This is the number of scatter-gather entries the driver - * will actually have to deal with after DMA mapping is done. - */ - unsigned short nr_hw_segments; - unsigned short ioprio; void *special; -- cgit v1.2.3 From 5b99c2ffa980528a197f26c7d876cceeccce8dd5 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 15 Aug 2008 10:56:11 +0200 Subject: block: make bi_phys_segments an unsigned int instead of short raid5 can overflow with more than 255 stripes, and we can increase it to an int for free on both 32 and 64-bit archs due to the padding. Signed-off-by: Jens Axboe --- drivers/md/raid5.c | 12 ++++++------ include/linux/bio.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 05b22925cce4..37e546528f9c 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -102,17 +102,17 @@ const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256))); #endif /* - * We maintain a biased count of active stripes in the bottom 8 bits of - * bi_phys_segments, and a count of processed stripes in the upper 8 bits + * We maintain a biased count of active stripes in the bottom 16 bits of + * bi_phys_segments, and a count of processed stripes in the upper 16 bits */ static inline int raid5_bi_phys_segments(struct bio *bio) { - return bio->bi_phys_segments & 0xff; + return bio->bi_phys_segments & 0xffff; } static inline int raid5_bi_hw_segments(struct bio *bio) { - return (bio->bi_phys_segments >> 8) & 0xff; + return (bio->bi_phys_segments >> 16) & 0xffff; } static inline int raid5_dec_bi_phys_segments(struct bio *bio) @@ -126,13 +126,13 @@ static inline int raid5_dec_bi_hw_segments(struct bio *bio) unsigned short val = raid5_bi_hw_segments(bio); --val; - bio->bi_phys_segments = (val << 8) | raid5_bi_phys_segments(bio); + bio->bi_phys_segments = (val << 16) | raid5_bi_phys_segments(bio); return val; } static inline void raid5_set_bi_hw_segments(struct bio *bio, unsigned int cnt) { - bio->bi_phys_segments = raid5_bi_phys_segments(bio) || (cnt << 8); + bio->bi_phys_segments = raid5_bi_phys_segments(bio) || (cnt << 16); } static inline int raid6_next_disk(int disk, int raid_disks) diff --git a/include/linux/bio.h b/include/linux/bio.h index dfc3556d311c..2c0c09034fd2 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -75,7 +75,7 @@ struct bio { /* Number of segments in this BIO after * physical address coalescing is performed. */ - unsigned short bi_phys_segments; + unsigned int bi_phys_segments; unsigned int bi_size; /* residual I/O count */ -- cgit v1.2.3 From a1ed5b0cffe4b16a93a6a3390e8cee0fbef94f86 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:50:16 +0200 Subject: klist: don't iterate over deleted entries A klist entry is kept on the list till all its current iterations are finished; however, a new iteration after deletion also iterates over deleted entries as long as their reference count stays above zero. This causes problems for cases where there are users which iterate over the list while synchronized against list manipulations and natuarally expect already deleted entries to not show up during iteration. This patch implements dead flag which gets set on deletion so that iteration can skip already deleted entries. The dead flag piggy backs on the lowest bit of knode->n_klist and only visible to klist implementation proper. While at it, drop klist_iter->i_head as it's redundant and doesn't offer anything in semantics or performance wise as klist_iter->i_klist is dereferenced on every iteration anyway. Signed-off-by: Tejun Heo Cc: Greg Kroah-Hartman Cc: Alan Stern Cc: Jens Axboe Signed-off-by: Jens Axboe --- include/linux/klist.h | 3 +- lib/klist.c | 96 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/include/linux/klist.h b/include/linux/klist.h index 06c338ef7f1b..8ea98db223e5 100644 --- a/include/linux/klist.h +++ b/include/linux/klist.h @@ -38,7 +38,7 @@ extern void klist_init(struct klist *k, void (*get)(struct klist_node *), void (*put)(struct klist_node *)); struct klist_node { - struct klist *n_klist; + void *n_klist; /* never access directly */ struct list_head n_node; struct kref n_ref; struct completion n_removed; @@ -57,7 +57,6 @@ extern int klist_node_attached(struct klist_node *n); struct klist_iter { struct klist *i_klist; - struct list_head *i_head; struct klist_node *i_cur; }; diff --git a/lib/klist.c b/lib/klist.c index cca37f96faa2..bbdd3015c2c7 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -37,6 +37,37 @@ #include #include +/* + * Use the lowest bit of n_klist to mark deleted nodes and exclude + * dead ones from iteration. + */ +#define KNODE_DEAD 1LU +#define KNODE_KLIST_MASK ~KNODE_DEAD + +static struct klist *knode_klist(struct klist_node *knode) +{ + return (struct klist *) + ((unsigned long)knode->n_klist & KNODE_KLIST_MASK); +} + +static bool knode_dead(struct klist_node *knode) +{ + return (unsigned long)knode->n_klist & KNODE_DEAD; +} + +static void knode_set_klist(struct klist_node *knode, struct klist *klist) +{ + knode->n_klist = klist; + /* no knode deserves to start its life dead */ + WARN_ON(knode_dead(knode)); +} + +static void knode_kill(struct klist_node *knode) +{ + /* and no knode should die twice ever either, see we're very humane */ + WARN_ON(knode_dead(knode)); + *(unsigned long *)&knode->n_klist |= KNODE_DEAD; +} /** * klist_init - Initialize a klist structure. @@ -79,7 +110,7 @@ static void klist_node_init(struct klist *k, struct klist_node *n) INIT_LIST_HEAD(&n->n_node); init_completion(&n->n_removed); kref_init(&n->n_ref); - n->n_klist = k; + knode_set_klist(n, k); if (k->get) k->get(n); } @@ -115,7 +146,7 @@ EXPORT_SYMBOL_GPL(klist_add_tail); */ void klist_add_after(struct klist_node *n, struct klist_node *pos) { - struct klist *k = pos->n_klist; + struct klist *k = knode_klist(pos); klist_node_init(k, n); spin_lock(&k->k_lock); @@ -131,7 +162,7 @@ EXPORT_SYMBOL_GPL(klist_add_after); */ void klist_add_before(struct klist_node *n, struct klist_node *pos) { - struct klist *k = pos->n_klist; + struct klist *k = knode_klist(pos); klist_node_init(k, n); spin_lock(&k->k_lock); @@ -144,9 +175,10 @@ static void klist_release(struct kref *kref) { struct klist_node *n = container_of(kref, struct klist_node, n_ref); + WARN_ON(!knode_dead(n)); list_del(&n->n_node); complete(&n->n_removed); - n->n_klist = NULL; + knode_set_klist(n, NULL); } static int klist_dec_and_del(struct klist_node *n) @@ -154,22 +186,29 @@ static int klist_dec_and_del(struct klist_node *n) return kref_put(&n->n_ref, klist_release); } -/** - * klist_del - Decrement the reference count of node and try to remove. - * @n: node we're deleting. - */ -void klist_del(struct klist_node *n) +static void klist_put(struct klist_node *n, bool kill) { - struct klist *k = n->n_klist; + struct klist *k = knode_klist(n); void (*put)(struct klist_node *) = k->put; spin_lock(&k->k_lock); + if (kill) + knode_kill(n); if (!klist_dec_and_del(n)) put = NULL; spin_unlock(&k->k_lock); if (put) put(n); } + +/** + * klist_del - Decrement the reference count of node and try to remove. + * @n: node we're deleting. + */ +void klist_del(struct klist_node *n) +{ + klist_put(n, true); +} EXPORT_SYMBOL_GPL(klist_del); /** @@ -206,7 +245,6 @@ void klist_iter_init_node(struct klist *k, struct klist_iter *i, struct klist_node *n) { i->i_klist = k; - i->i_head = &k->k_list; i->i_cur = n; if (n) kref_get(&n->n_ref); @@ -237,7 +275,7 @@ EXPORT_SYMBOL_GPL(klist_iter_init); void klist_iter_exit(struct klist_iter *i) { if (i->i_cur) { - klist_del(i->i_cur); + klist_put(i->i_cur, false); i->i_cur = NULL; } } @@ -258,27 +296,33 @@ static struct klist_node *to_klist_node(struct list_head *n) */ struct klist_node *klist_next(struct klist_iter *i) { - struct list_head *next; - struct klist_node *lnode = i->i_cur; - struct klist_node *knode = NULL; void (*put)(struct klist_node *) = i->i_klist->put; + struct klist_node *last = i->i_cur; + struct klist_node *next; spin_lock(&i->i_klist->k_lock); - if (lnode) { - next = lnode->n_node.next; - if (!klist_dec_and_del(lnode)) + + if (last) { + next = to_klist_node(last->n_node.next); + if (!klist_dec_and_del(last)) put = NULL; } else - next = i->i_head->next; + next = to_klist_node(i->i_klist->k_list.next); - if (next != i->i_head) { - knode = to_klist_node(next); - kref_get(&knode->n_ref); + i->i_cur = NULL; + while (next != to_klist_node(&i->i_klist->k_list)) { + if (likely(!knode_dead(next))) { + kref_get(&next->n_ref); + i->i_cur = next; + break; + } + next = to_klist_node(next->n_node.next); } - i->i_cur = knode; + spin_unlock(&i->i_klist->k_lock); - if (put && lnode) - put(lnode); - return knode; + + if (put && last) + put(last); + return i->i_cur; } EXPORT_SYMBOL_GPL(klist_next); -- cgit v1.2.3 From 5a3ceb861663040f9ef0176df4aaa494bba5e352 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:50:19 +0200 Subject: driver-core: use klist for class device list and implement iterator Iterating over entries using callback usually isn't too fun especially when the entry being iterated over can't be manipulated freely. This patch converts class->p->class_devices to klist and implements class device iterator so that the users can freely build their own control structure. The users are also free to call back into class code without worrying about locking. class_for_each_device() and class_find_device() are converted to use the new iterators, so their users don't have to worry about locking anymore either. Note: This depends on klist-dont-iterate-over-deleted-entries patch because class_intf->add/remove_dev() depends on proper synchronization with device removal. Signed-off-by: Tejun Heo Cc: Greg Kroah-Hartman Cc: Jens Axboe Signed-off-by: Jens Axboe --- drivers/base/base.h | 2 +- drivers/base/class.c | 136 +++++++++++++++++++++++++++++++++++++------------ drivers/base/core.c | 6 +-- include/linux/device.h | 14 ++++- 4 files changed, 120 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 31dc0cd84afa..0a5f055dffba 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -54,7 +54,7 @@ struct driver_private { */ struct class_private { struct kset class_subsys; - struct list_head class_devices; + struct klist class_devices; struct list_head class_interfaces; struct kset class_dirs; struct mutex class_mutex; diff --git a/drivers/base/class.c b/drivers/base/class.c index cc5e28c8885c..eb85e4312301 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -135,6 +135,20 @@ static void remove_class_attrs(struct class *cls) } } +static void klist_class_dev_get(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_class); + + get_device(dev); +} + +static void klist_class_dev_put(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_class); + + put_device(dev); +} + int __class_register(struct class *cls, struct lock_class_key *key) { struct class_private *cp; @@ -145,7 +159,7 @@ int __class_register(struct class *cls, struct lock_class_key *key) cp = kzalloc(sizeof(*cp), GFP_KERNEL); if (!cp) return -ENOMEM; - INIT_LIST_HEAD(&cp->class_devices); + klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put); INIT_LIST_HEAD(&cp->class_interfaces); kset_init(&cp->class_dirs); __mutex_init(&cp->class_mutex, "struct class mutex", key); @@ -268,6 +282,71 @@ char *make_class_name(const char *name, struct kobject *kobj) } #endif +/** + * class_dev_iter_init - initialize class device iterator + * @iter: class iterator to initialize + * @class: the class we wanna iterate over + * @start: the device to start iterating from, if any + * @type: device_type of the devices to iterate over, NULL for all + * + * Initialize class iterator @iter such that it iterates over devices + * of @class. If @start is set, the list iteration will start there, + * otherwise if it is NULL, the iteration starts at the beginning of + * the list. + */ +void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, + struct device *start, const struct device_type *type) +{ + struct klist_node *start_knode = NULL; + + if (start) + start_knode = &start->knode_class; + klist_iter_init_node(&class->p->class_devices, &iter->ki, start_knode); + iter->type = type; +} +EXPORT_SYMBOL_GPL(class_dev_iter_init); + +/** + * class_dev_iter_next - iterate to the next device + * @iter: class iterator to proceed + * + * Proceed @iter to the next device and return it. Returns NULL if + * iteration is complete. + * + * The returned device is referenced and won't be released till + * iterator is proceed to the next device or exited. The caller is + * free to do whatever it wants to do with the device including + * calling back into class code. + */ +struct device *class_dev_iter_next(struct class_dev_iter *iter) +{ + struct klist_node *knode; + struct device *dev; + + while (1) { + knode = klist_next(&iter->ki); + if (!knode) + return NULL; + dev = container_of(knode, struct device, knode_class); + if (!iter->type || iter->type == dev->type) + return dev; + } +} +EXPORT_SYMBOL_GPL(class_dev_iter_next); + +/** + * class_dev_iter_exit - finish iteration + * @iter: class iterator to finish + * + * Finish an iteration. Always call this function after iteration is + * complete whether the iteration ran till the end or not. + */ +void class_dev_iter_exit(struct class_dev_iter *iter) +{ + klist_iter_exit(&iter->ki); +} +EXPORT_SYMBOL_GPL(class_dev_iter_exit); + /** * class_for_each_device - device iterator * @class: the class we're iterating @@ -283,13 +362,13 @@ char *make_class_name(const char *name, struct kobject *kobj) * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. * - * Note, we hold class->class_mutex in this function, so it can not be - * re-acquired in @fn, otherwise it will self-deadlocking. For - * example, calls to add or remove class members would be verboten. + * @fn is allowed to do anything including calling back into class + * code. There's no locking restriction. */ int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *, void *)) { + struct class_dev_iter iter; struct device *dev; int error = 0; @@ -301,20 +380,13 @@ int class_for_each_device(struct class *class, struct device *start, return -EINVAL; } - mutex_lock(&class->p->class_mutex); - list_for_each_entry(dev, &class->p->class_devices, node) { - if (start) { - if (start == dev) - start = NULL; - continue; - } - dev = get_device(dev); + class_dev_iter_init(&iter, class, start, NULL); + while ((dev = class_dev_iter_next(&iter))) { error = fn(dev, data); - put_device(dev); if (error) break; } - mutex_unlock(&class->p->class_mutex); + class_dev_iter_exit(&iter); return error; } @@ -337,16 +409,15 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * * Note, you will need to drop the reference with put_device() after use. * - * We hold class->class_mutex in this function, so it can not be - * re-acquired in @match, otherwise it will self-deadlocking. For - * example, calls to add or remove class members would be verboten. + * @fn is allowed to do anything including calling back into class + * code. There's no locking restriction. */ struct device *class_find_device(struct class *class, struct device *start, void *data, int (*match)(struct device *, void *)) { + struct class_dev_iter iter; struct device *dev; - int found = 0; if (!class) return NULL; @@ -356,29 +427,23 @@ struct device *class_find_device(struct class *class, struct device *start, return NULL; } - mutex_lock(&class->p->class_mutex); - list_for_each_entry(dev, &class->p->class_devices, node) { - if (start) { - if (start == dev) - start = NULL; - continue; - } - dev = get_device(dev); + class_dev_iter_init(&iter, class, start, NULL); + while ((dev = class_dev_iter_next(&iter))) { if (match(dev, data)) { - found = 1; + get_device(dev); break; - } else - put_device(dev); + } } - mutex_unlock(&class->p->class_mutex); + class_dev_iter_exit(&iter); - return found ? dev : NULL; + return dev; } EXPORT_SYMBOL_GPL(class_find_device); int class_interface_register(struct class_interface *class_intf) { struct class *parent; + struct class_dev_iter iter; struct device *dev; if (!class_intf || !class_intf->class) @@ -391,8 +456,10 @@ int class_interface_register(struct class_interface *class_intf) mutex_lock(&parent->p->class_mutex); list_add_tail(&class_intf->node, &parent->p->class_interfaces); if (class_intf->add_dev) { - list_for_each_entry(dev, &parent->p->class_devices, node) + class_dev_iter_init(&iter, parent, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) class_intf->add_dev(dev, class_intf); + class_dev_iter_exit(&iter); } mutex_unlock(&parent->p->class_mutex); @@ -402,6 +469,7 @@ int class_interface_register(struct class_interface *class_intf) void class_interface_unregister(struct class_interface *class_intf) { struct class *parent = class_intf->class; + struct class_dev_iter iter; struct device *dev; if (!parent) @@ -410,8 +478,10 @@ void class_interface_unregister(struct class_interface *class_intf) mutex_lock(&parent->p->class_mutex); list_del_init(&class_intf->node); if (class_intf->remove_dev) { - list_for_each_entry(dev, &parent->p->class_devices, node) + class_dev_iter_init(&iter, parent, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) class_intf->remove_dev(dev, class_intf); + class_dev_iter_exit(&iter); } mutex_unlock(&parent->p->class_mutex); diff --git a/drivers/base/core.c b/drivers/base/core.c index d021c98605b3..b98cb1416a2d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -536,7 +536,6 @@ void device_initialize(struct device *dev) klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); - INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); @@ -916,7 +915,8 @@ int device_add(struct device *dev) if (dev->class) { mutex_lock(&dev->class->p->class_mutex); /* tie the class to the device */ - list_add_tail(&dev->node, &dev->class->p->class_devices); + klist_add_tail(&dev->knode_class, + &dev->class->p->class_devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, @@ -1032,7 +1032,7 @@ void device_del(struct device *dev) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ - list_del_init(&dev->node); + klist_del(&dev->knode_class); mutex_unlock(&dev->class->p->class_mutex); } device_remove_file(dev, &uevent_attr); diff --git a/include/linux/device.h b/include/linux/device.h index 4d8372d135df..246937c9cbc7 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -199,6 +199,11 @@ struct class { struct class_private *p; }; +struct class_dev_iter { + struct klist_iter ki; + const struct device_type *type; +}; + extern struct kobject *sysfs_dev_block_kobj; extern struct kobject *sysfs_dev_char_kobj; extern int __must_check __class_register(struct class *class, @@ -213,6 +218,13 @@ extern void class_unregister(struct class *class); __class_register(class, &__key); \ }) +extern void class_dev_iter_init(struct class_dev_iter *iter, + struct class *class, + struct device *start, + const struct device_type *type); +extern struct device *class_dev_iter_next(struct class_dev_iter *iter); +extern void class_dev_iter_exit(struct class_dev_iter *iter); + extern int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); @@ -396,7 +408,7 @@ struct device { spinlock_t devres_lock; struct list_head devres_head; - struct list_head node; + struct klist_node knode_class; struct class *class; dev_t devt; /* dev_t, creates the sysfs "dev" */ struct attribute_group **groups; /* optional groups */ -- cgit v1.2.3 From 310a2c1012934f590192377f65940cad4aa72b15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:47:17 +0900 Subject: block: misc updates This patch makes the following misc updates in preparation for disk->part dereference fix and extended block devt support. * implment part_to_disk() * fix comment about gendisk->part indexing * rename get_part() to disk_map_sector() * don't use n which is always zero while printing disk information in diskstats_show() Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 7 ++++--- block/blk-merge.c | 4 ++-- block/genhd.c | 4 ++-- drivers/block/aoe/aoecmd.c | 2 +- include/linux/genhd.h | 13 ++++++++++--- 5 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 86d22e7d65c5..a0dc2e72fcbb 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -60,7 +60,7 @@ static void drive_stat_acct(struct request *rq, int new_io) if (!blk_fs_request(rq) || !rq->rq_disk) return; - part = get_part(rq->rq_disk, rq->sector); + part = disk_map_sector(rq->rq_disk, rq->sector); if (!new_io) __all_stat_inc(rq->rq_disk, part, merges[rw], rq->sector); else { @@ -1557,7 +1557,8 @@ static int __end_that_request_first(struct request *req, int error, } if (blk_fs_request(req) && req->rq_disk) { - struct hd_struct *part = get_part(req->rq_disk, req->sector); + struct hd_struct *part = + disk_map_sector(req->rq_disk, req->sector); const int rw = rq_data_dir(req); all_stat_add(req->rq_disk, part, sectors[rw], @@ -1745,7 +1746,7 @@ static void end_that_request_last(struct request *req, int error) if (disk && blk_fs_request(req) && req != &req->q->bar_rq) { unsigned long duration = jiffies - req->start_time; const int rw = rq_data_dir(req); - struct hd_struct *part = get_part(disk, req->sector); + struct hd_struct *part = disk_map_sector(disk, req->sector); __all_stat_inc(disk, part, ios[rw], req->sector); __all_stat_add(disk, part, ticks[rw], duration, req->sector); diff --git a/block/blk-merge.c b/block/blk-merge.c index d81d91419ff5..9b17da698d7c 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -387,8 +387,8 @@ static int attempt_merge(struct request_queue *q, struct request *req, elv_merge_requests(q, req, next); if (req->rq_disk) { - struct hd_struct *part - = get_part(req->rq_disk, req->sector); + struct hd_struct *part = + disk_map_sector(req->rq_disk, req->sector); disk_round_stats(req->rq_disk); req->rq_disk->in_flight--; if (part) { diff --git a/block/genhd.c b/block/genhd.c index 8b9a9ff1a842..11038fbc75ed 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -568,7 +568,7 @@ static int diskstats_show(struct seq_file *s, void *v) { struct gendisk *gp = v; char buf[BDEVNAME_SIZE]; - int n = 0; + int n; /* if (&gp->dev.kobj.entry == block_class.devices.next) @@ -582,7 +582,7 @@ static int diskstats_show(struct seq_file *s, void *v) disk_round_stats(gp); preempt_enable(); seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", - gp->major, n + gp->first_minor, disk_name(gp, n, buf), + gp->major, gp->first_minor, disk_name(gp, 0, buf), disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), (unsigned long long)disk_stat_read(gp, sectors[0]), jiffies_to_msecs(disk_stat_read(gp, ticks[0])), diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 2f1746295d06..885d1409521f 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -757,7 +757,7 @@ diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector const int rw = bio_data_dir(bio); struct hd_struct *part; - part = get_part(disk, sector); + part = disk_map_sector(disk, sector); all_stat_inc(disk, part, ios[rw], sector); all_stat_add(disk, part, ticks[rw], duration, sector); all_stat_add(disk, part, sectors[rw], n_sect, sector); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index be4f5e5bfe06..c64e659c9843 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -116,7 +116,7 @@ struct gendisk { int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ char disk_name[32]; /* name of major driver */ - struct hd_struct **part; /* [indexed by minor] */ + struct hd_struct **part; /* [indexed by minor - 1] */ struct block_device_operations *fops; struct request_queue *queue; void *private_data; @@ -145,14 +145,21 @@ struct gendisk { #endif }; +static inline struct gendisk *part_to_disk(struct hd_struct *part) +{ + if (likely(part)) + return dev_to_disk((part)->dev.parent); + return NULL; +} + /* * Macros to operate on percpu disk statistics: * * The __ variants should only be called in critical sections. The full * variants disable/enable preemption. */ -static inline struct hd_struct *get_part(struct gendisk *gendiskp, - sector_t sector) +static inline struct hd_struct *disk_map_sector(struct gendisk *gendiskp, + sector_t sector) { struct hd_struct *part; int i; -- cgit v1.2.3 From cf771cb5a7b716f3f9e532fd42a1e3a0a75adec5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Sep 2008 09:01:09 +0200 Subject: block: make variable and argument names more consistent In hd_struct, @partno is used to denote partition number and a number of other places use @part to denote hd_struct. Functions use @part and @index instead. This causes confusion and makes it difficult to use consistent variable names for hd_struct. Always use @partno if a variable represents partition number. Also, print out functions use @f or @part for seq_file argument. Use @seqf uniformly instead. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 54 ++++++++++++++++++++++++--------------------------- block/ioctl.c | 15 +++++++------- fs/block_dev.c | 8 ++++---- fs/partitions/check.c | 33 ++++++++++++++++--------------- include/linux/genhd.h | 12 ++++++------ 5 files changed, 60 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index 11038fbc75ed..dc9ad4c171e2 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -43,14 +43,14 @@ static inline int major_to_index(int major) } #ifdef CONFIG_PROC_FS -void blkdev_show(struct seq_file *f, off_t offset) +void blkdev_show(struct seq_file *seqf, off_t offset) { struct blk_major_name *dp; if (offset < BLKDEV_MAJOR_HASH_SIZE) { mutex_lock(&block_class_lock); for (dp = major_names[offset]; dp; dp = dp->next) - seq_printf(f, "%3d %s\n", dp->major, dp->name); + seq_printf(seqf, "%3d %s\n", dp->major, dp->name); mutex_unlock(&block_class_lock); } } @@ -157,7 +157,7 @@ void blk_unregister_region(dev_t devt, unsigned long range) EXPORT_SYMBOL(blk_unregister_region); -static struct kobject *exact_match(dev_t devt, int *part, void *data) +static struct kobject *exact_match(dev_t devt, int *partno, void *data) { struct gendisk *p = data; @@ -217,9 +217,9 @@ void unlink_gendisk(struct gendisk *disk) * This function gets the structure containing partitioning * information for the given device @devt. */ -struct gendisk *get_gendisk(dev_t devt, int *part) +struct gendisk *get_gendisk(dev_t devt, int *partno) { - struct kobject *kobj = kobj_lookup(bdev_map, devt, part); + struct kobject *kobj = kobj_lookup(bdev_map, devt, partno); struct device *dev = kobj_to_dev(kobj); return kobj ? dev_to_disk(dev) : NULL; @@ -336,23 +336,12 @@ static void *show_partition_start(struct seq_file *seqf, loff_t *pos) return p; } -static int show_partition(struct seq_file *part, void *v) +static int show_partition(struct seq_file *seqf, void *v) { struct gendisk *sgp = v; int n; char buf[BDEVNAME_SIZE]; - /* - * Print header if start told us to do. This is to preserve - * the original behavior of not printing header if no - * partition exists. This hackery will be removed later with - * class iteration clean up. - */ - if (part->private) { - seq_puts(part, "major minor #blocks name\n\n"); - part->private = NULL; - } - /* Don't show non-partitionable removeable devices or empty devices */ if (!get_capacity(sgp) || (sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE))) @@ -361,7 +350,7 @@ static int show_partition(struct seq_file *part, void *v) return 0; /* show the full disk and all non-0 size partitions of it */ - seq_printf(part, "%4d %4d %10llu %s\n", + seq_printf(seqf, "%4d %4d %10llu %s\n", sgp->major, sgp->first_minor, (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); @@ -370,7 +359,7 @@ static int show_partition(struct seq_file *part, void *v) continue; if (sgp->part[n]->nr_sects == 0) continue; - seq_printf(part, "%4d %4d %10llu %s\n", + seq_printf(seqf, "%4d %4d %10llu %s\n", sgp->major, n + 1 + sgp->first_minor, (unsigned long long)sgp->part[n]->nr_sects >> 1 , disk_name(sgp, n + 1, buf)); @@ -388,7 +377,7 @@ const struct seq_operations partitions_op = { #endif -static struct kobject *base_probe(dev_t devt, int *part, void *data) +static struct kobject *base_probe(dev_t devt, int *partno, void *data) { if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0) /* Make old-style 2.4 aliases work */ @@ -564,7 +553,14 @@ static struct device_type disk_type = { }; #ifdef CONFIG_PROC_FS -static int diskstats_show(struct seq_file *s, void *v) +/* + * aggregate disk stat collector. Uses the same stats that the sysfs + * entries do, above, but makes them available through one seq_file. + * + * The output looks suspiciously like /proc/partitions with a bunch of + * extra fields. + */ +static int diskstats_show(struct seq_file *seqf, void *v) { struct gendisk *gp = v; char buf[BDEVNAME_SIZE]; @@ -572,7 +568,7 @@ static int diskstats_show(struct seq_file *s, void *v) /* if (&gp->dev.kobj.entry == block_class.devices.next) - seq_puts(s, "major minor name" + seq_puts(seqf, "major minor name" " rio rmerge rsect ruse wio wmerge " "wsect wuse running use aveq" "\n\n"); @@ -581,7 +577,7 @@ static int diskstats_show(struct seq_file *s, void *v) preempt_disable(); disk_round_stats(gp); preempt_enable(); - seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", + seq_printf(seqf, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", gp->major, gp->first_minor, disk_name(gp, 0, buf), disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), (unsigned long long)disk_stat_read(gp, sectors[0]), @@ -603,7 +599,7 @@ static int diskstats_show(struct seq_file *s, void *v) preempt_disable(); part_round_stats(hd); preempt_enable(); - seq_printf(s, "%4d %4d %s %lu %lu %llu " + seq_printf(seqf, "%4d %4d %s %lu %lu %llu " "%u %lu %lu %llu %u %u %u %u\n", gp->major, n + gp->first_minor + 1, disk_name(gp, n + 1, buf), @@ -655,7 +651,7 @@ void genhd_media_change_notify(struct gendisk *disk) EXPORT_SYMBOL_GPL(genhd_media_change_notify); #endif /* 0 */ -dev_t blk_lookup_devt(const char *name, int part) +dev_t blk_lookup_devt(const char *name, int partno) { dev_t devt = MKDEV(0, 0); struct class_dev_iter iter; @@ -665,9 +661,9 @@ dev_t blk_lookup_devt(const char *name, int part) while ((dev = class_dev_iter_next(&iter))) { struct gendisk *disk = dev_to_disk(dev); - if (!strcmp(dev->bus_id, name) && part < disk->minors) { + if (!strcmp(dev->bus_id, name) && partno < disk->minors) { devt = MKDEV(MAJOR(dev->devt), - MINOR(dev->devt) + part); + MINOR(dev->devt) + partno); break; } } @@ -777,10 +773,10 @@ int bdev_read_only(struct block_device *bdev) EXPORT_SYMBOL(bdev_read_only); -int invalidate_partition(struct gendisk *disk, int index) +int invalidate_partition(struct gendisk *disk, int partno) { int res = 0; - struct block_device *bdev = bdget_disk(disk, index); + struct block_device *bdev = bdget_disk(disk, partno); if (bdev) { fsync_bdev(bdev); res = __invalidate_device(bdev); diff --git a/block/ioctl.c b/block/ioctl.c index eb046aeede8a..d77f5e280a6e 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -15,7 +15,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user struct blkpg_ioctl_arg a; struct blkpg_partition p; long long start, length; - int part; + int partno; int i; int err; @@ -28,8 +28,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user disk = bdev->bd_disk; if (bdev != bdev->bd_contains) return -EINVAL; - part = p.pno; - if (part <= 0 || part >= disk->minors) + partno = p.pno; + if (partno <= 0 || partno >= disk->minors) return -EINVAL; switch (a.op) { case BLKPG_ADD_PARTITION: @@ -59,13 +59,14 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user } } /* all seems OK */ - err = add_partition(disk, part, start, length, ADDPART_FLAG_NONE); + err = add_partition(disk, partno, start, length, + ADDPART_FLAG_NONE); mutex_unlock(&bdev->bd_mutex); return err; case BLKPG_DEL_PARTITION: - if (!disk->part[part-1]) + if (!disk->part[partno - 1]) return -ENXIO; - bdevp = bdget_disk(disk, part); + bdevp = bdget_disk(disk, partno); if (!bdevp) return -ENOMEM; mutex_lock(&bdevp->bd_mutex); @@ -79,7 +80,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user invalidate_bdev(bdevp); mutex_lock_nested(&bdev->bd_mutex, 1); - delete_partition(disk, part); + delete_partition(disk, partno); mutex_unlock(&bdev->bd_mutex); mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); diff --git a/fs/block_dev.c b/fs/block_dev.c index aff54219e049..de0776cd7215 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -930,7 +930,7 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) struct module *owner = NULL; struct gendisk *disk; int ret; - int part; + int partno; int perm = 0; if (file->f_mode & FMODE_READ) @@ -949,7 +949,7 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) ret = -ENXIO; file->f_mapping = bdev->bd_inode->i_mapping; lock_kernel(); - disk = get_gendisk(bdev->bd_dev, &part); + disk = get_gendisk(bdev->bd_dev, &partno); if (!disk) { unlock_kernel(); bdput(bdev); @@ -961,7 +961,7 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) if (!bdev->bd_openers) { bdev->bd_disk = disk; bdev->bd_contains = bdev; - if (!part) { + if (!partno) { struct backing_dev_info *bdi; if (disk->fops->open) { ret = disk->fops->open(bdev->bd_inode, file); @@ -989,7 +989,7 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) if (ret) goto out_first; bdev->bd_contains = whole; - p = disk->part[part - 1]; + p = disk->part[partno - 1]; bdev->bd_inode->i_data.backing_dev_info = whole->bd_inode->i_data.backing_dev_info; if (!(disk->flags & GENHD_FL_UP) || !p || !p->nr_sects) { diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 16f98d824608..b86aab1b0df6 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -120,22 +120,22 @@ static int (*check_part[])(struct parsed_partitions *, struct block_device *) = * a pointer to that same buffer (for convenience). */ -char *disk_name(struct gendisk *hd, int part, char *buf) +char *disk_name(struct gendisk *hd, int partno, char *buf) { - if (!part) + if (!partno) snprintf(buf, BDEVNAME_SIZE, "%s", hd->disk_name); else if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) - snprintf(buf, BDEVNAME_SIZE, "%sp%d", hd->disk_name, part); + snprintf(buf, BDEVNAME_SIZE, "%sp%d", hd->disk_name, partno); else - snprintf(buf, BDEVNAME_SIZE, "%s%d", hd->disk_name, part); + snprintf(buf, BDEVNAME_SIZE, "%s%d", hd->disk_name, partno); return buf; } const char *bdevname(struct block_device *bdev, char *buf) { - int part = MINOR(bdev->bd_dev) - bdev->bd_disk->first_minor; - return disk_name(bdev->bd_disk, part, buf); + int partno = MINOR(bdev->bd_dev) - bdev->bd_disk->first_minor; + return disk_name(bdev->bd_disk, partno, buf); } EXPORT_SYMBOL(bdevname); @@ -310,13 +310,13 @@ static inline void disk_sysfs_add_subdirs(struct gendisk *disk) kobject_put(k); } -void delete_partition(struct gendisk *disk, int part) +void delete_partition(struct gendisk *disk, int partno) { - struct hd_struct *p = disk->part[part-1]; + struct hd_struct *p = disk->part[partno - 1]; if (!p) return; - disk->part[part-1] = NULL; + disk->part[partno - 1] = NULL; p->start_sect = 0; p->nr_sects = 0; part_stat_set_all(p, 0); @@ -333,12 +333,13 @@ static ssize_t whole_disk_show(struct device *dev, static DEVICE_ATTR(whole_disk, S_IRUSR | S_IRGRP | S_IROTH, whole_disk_show, NULL); -int add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags) +int add_partition(struct gendisk *disk, int partno, + sector_t start, sector_t len, int flags) { struct hd_struct *p; int err; - if (disk->part[part - 1]) + if (disk->part[partno - 1]) return -EBUSY; p = kzalloc(sizeof(*p), GFP_KERNEL); @@ -351,18 +352,18 @@ int add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, } p->start_sect = start; p->nr_sects = len; - p->partno = part; + p->partno = partno; p->policy = disk->policy; if (isdigit(disk->dev.bus_id[strlen(disk->dev.bus_id)-1])) snprintf(p->dev.bus_id, BUS_ID_SIZE, - "%sp%d", disk->dev.bus_id, part); + "%sp%d", disk->dev.bus_id, partno); else snprintf(p->dev.bus_id, BUS_ID_SIZE, - "%s%d", disk->dev.bus_id, part); + "%s%d", disk->dev.bus_id, partno); device_initialize(&p->dev); - p->dev.devt = MKDEV(disk->major, disk->first_minor + part); + p->dev.devt = MKDEV(disk->major, disk->first_minor + partno); p->dev.class = &block_class; p->dev.type = &part_type; p->dev.parent = &disk->dev; @@ -386,7 +387,7 @@ int add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, } /* everything is up and running, commence */ - disk->part[part - 1] = p; + disk->part[partno - 1] = p; /* suppress uevent if the disk supresses it */ if (!disk->dev.uevent_suppress) diff --git a/include/linux/genhd.h b/include/linux/genhd.h index c64e659c9843..d1723c0a8600 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -365,7 +365,7 @@ extern int get_blkdev_list(char *, int); extern void add_disk(struct gendisk *disk); extern void del_gendisk(struct gendisk *gp); extern void unlink_gendisk(struct gendisk *gp); -extern struct gendisk *get_gendisk(dev_t dev, int *part); +extern struct gendisk *get_gendisk(dev_t dev, int *partno); extern void set_device_ro(struct block_device *bdev, int flag); extern void set_disk_ro(struct gendisk *disk, int flag); @@ -534,8 +534,8 @@ struct unixware_disklabel { #define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_WHOLEDISK 2 -extern dev_t blk_lookup_devt(const char *name, int part); -extern char *disk_name (struct gendisk *hd, int part, char *buf); +extern dev_t blk_lookup_devt(const char *name, int partno); +extern char *disk_name (struct gendisk *hd, int partno, char *buf); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); extern int __must_check add_partition(struct gendisk *, int, sector_t, sector_t, int); @@ -553,16 +553,16 @@ extern void blk_register_region(dev_t devt, unsigned long range, void *data); extern void blk_unregister_region(dev_t devt, unsigned long range); -static inline struct block_device *bdget_disk(struct gendisk *disk, int index) +static inline struct block_device *bdget_disk(struct gendisk *disk, int partno) { - return bdget(MKDEV(disk->major, disk->first_minor) + index); + return bdget(MKDEV(disk->major, disk->first_minor) + partno); } #else /* CONFIG_BLOCK */ static inline void printk_all_partitions(void) { } -static inline dev_t blk_lookup_devt(const char *name, int part) +static inline dev_t blk_lookup_devt(const char *name, int partno) { dev_t devt = MKDEV(0, 0); return devt; -- cgit v1.2.3 From f331c0296f2a9fee0d396a70598b954062603015 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Sep 2008 09:01:48 +0200 Subject: block: don't depend on consecutive minor space * Implement disk_devt() and part_devt() and use them to directly access devt instead of computing it from ->major and ->first_minor. Note that all references to ->major and ->first_minor outside of block layer is used to determine devt of the disk (the part0) and as ->major and ->first_minor will continue to represent devt for the disk, converting these users aren't strictly necessary. However, convert them for consistency. * Implement disk_max_parts() to avoid directly deferencing genhd->minors. * Update bdget_disk() such that it doesn't assume consecutive minor space. * Move devt computation from register_disk() to add_disk() and make it the only one (all other usages use the initially determined value). These changes clean up the code and will help disk->part dereference fix and extended block device numbers. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 107 +++++++++++++++++++++++++----------- block/ioctl.c | 6 +- drivers/block/pktcdvd.c | 2 +- drivers/block/ps3disk.c | 2 +- drivers/char/random.c | 6 +- drivers/md/dm-ioctl.c | 4 +- drivers/md/dm-stripe.c | 4 +- drivers/md/dm.c | 7 ++- drivers/memstick/core/mspro_block.c | 2 +- drivers/mmc/card/block.c | 2 +- drivers/s390/block/dasd_proc.c | 3 +- drivers/s390/block/dcssblk.c | 4 +- drivers/scsi/sr.c | 2 +- fs/block_dev.c | 2 +- fs/partitions/check.c | 19 ++++--- include/linux/genhd.h | 27 +++++++-- 16 files changed, 132 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index dc9ad4c171e2..fa32d09fda24 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -186,13 +186,14 @@ void add_disk(struct gendisk *disk) int retval; disk->flags |= GENHD_FL_UP; - blk_register_region(MKDEV(disk->major, disk->first_minor), - disk->minors, NULL, exact_match, exact_lock, disk); + disk->dev.devt = MKDEV(disk->major, disk->first_minor); + blk_register_region(disk_devt(disk), disk->minors, NULL, + exact_match, exact_lock, disk); register_disk(disk); blk_register_queue(disk); bdi = &disk->queue->backing_dev_info; - bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); + bdi_register_dev(bdi, disk_devt(disk)); retval = sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi"); WARN_ON(retval); } @@ -205,8 +206,7 @@ void unlink_gendisk(struct gendisk *disk) sysfs_remove_link(&disk->dev.kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); - blk_unregister_region(MKDEV(disk->major, disk->first_minor), - disk->minors); + blk_unregister_region(disk_devt(disk), disk->minors); } /** @@ -225,6 +225,38 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) return kobj ? dev_to_disk(dev) : NULL; } +/** + * bdget_disk - do bdget() by gendisk and partition number + * @disk: gendisk of interest + * @partno: partition number + * + * Find partition @partno from @disk, do bdget() on it. + * + * CONTEXT: + * Don't care. + * + * RETURNS: + * Resulting block_device on success, NULL on failure. + */ +extern struct block_device *bdget_disk(struct gendisk *disk, int partno) +{ + dev_t devt = MKDEV(0, 0); + + if (partno == 0) + devt = disk_devt(disk); + else { + struct hd_struct *part = disk->part[partno - 1]; + + if (part && part->nr_sects) + devt = part_devt(part); + } + + if (likely(devt != MKDEV(0, 0))) + return bdget(devt); + return NULL; +} +EXPORT_SYMBOL(bdget_disk); + /* * print a full list of all partitions - intended for places where the root * filesystem can't be mounted and thus to give the victim some idea of what @@ -255,7 +287,7 @@ void __init printk_all_partitions(void) * option takes. */ printk("%02x%02x %10llu %s", - disk->major, disk->first_minor, + MAJOR(disk_devt(disk)), MINOR(disk_devt(disk)), (unsigned long long)get_capacity(disk) >> 1, disk_name(disk, 0, buf)); if (disk->driverfs_dev != NULL && @@ -266,15 +298,15 @@ void __init printk_all_partitions(void) printk(" (driver?)\n"); /* now show the partitions */ - for (n = 0; n < disk->minors - 1; ++n) { - if (disk->part[n] == NULL) - continue; - if (disk->part[n]->nr_sects == 0) + for (n = 0; n < disk_max_parts(disk); ++n) { + struct hd_struct *part = disk->part[n]; + + if (!part || !part->nr_sects) continue; printk(" %02x%02x %10llu %s\n", - disk->major, n + 1 + disk->first_minor, - (unsigned long long)disk->part[n]->nr_sects >> 1, - disk_name(disk, n + 1, buf)); + MAJOR(part_devt(part)), MINOR(part_devt(part)), + (unsigned long long)part->nr_sects >> 1, + disk_name(disk, part->partno, buf)); } } class_dev_iter_exit(&iter); @@ -343,26 +375,27 @@ static int show_partition(struct seq_file *seqf, void *v) char buf[BDEVNAME_SIZE]; /* Don't show non-partitionable removeable devices or empty devices */ - if (!get_capacity(sgp) || - (sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE))) + if (!get_capacity(sgp) || (!disk_max_parts(sgp) && + (sgp->flags & GENHD_FL_REMOVABLE))) return 0; if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) return 0; /* show the full disk and all non-0 size partitions of it */ seq_printf(seqf, "%4d %4d %10llu %s\n", - sgp->major, sgp->first_minor, + MAJOR(disk_devt(sgp)), MINOR(disk_devt(sgp)), (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); - for (n = 0; n < sgp->minors - 1; n++) { - if (!sgp->part[n]) + for (n = 0; n < disk_max_parts(sgp); n++) { + struct hd_struct *part = sgp->part[n]; + if (!part) continue; - if (sgp->part[n]->nr_sects == 0) + if (part->nr_sects == 0) continue; seq_printf(seqf, "%4d %4d %10llu %s\n", - sgp->major, n + 1 + sgp->first_minor, - (unsigned long long)sgp->part[n]->nr_sects >> 1 , - disk_name(sgp, n + 1, buf)); + MAJOR(part_devt(part)), MINOR(part_devt(part)), + (unsigned long long)part->nr_sects >> 1, + disk_name(sgp, part->partno, buf)); } return 0; @@ -578,7 +611,8 @@ static int diskstats_show(struct seq_file *seqf, void *v) disk_round_stats(gp); preempt_enable(); seq_printf(seqf, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", - gp->major, gp->first_minor, disk_name(gp, 0, buf), + MAJOR(disk_devt(gp)), MINOR(disk_devt(gp)), + disk_name(gp, 0, buf), disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), (unsigned long long)disk_stat_read(gp, sectors[0]), jiffies_to_msecs(disk_stat_read(gp, ticks[0])), @@ -590,7 +624,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) jiffies_to_msecs(disk_stat_read(gp, time_in_queue))); /* now show all non-0 size partitions of it */ - for (n = 0; n < gp->minors - 1; n++) { + for (n = 0; n < disk_max_parts(gp); n++) { struct hd_struct *hd = gp->part[n]; if (!hd || !hd->nr_sects) @@ -601,8 +635,8 @@ static int diskstats_show(struct seq_file *seqf, void *v) preempt_enable(); seq_printf(seqf, "%4d %4d %s %lu %lu %llu " "%u %lu %lu %llu %u %u %u %u\n", - gp->major, n + gp->first_minor + 1, - disk_name(gp, n + 1, buf), + MAJOR(part_devt(hd)), MINOR(part_devt(hd)), + disk_name(gp, hd->partno, buf), part_stat_read(hd, ios[0]), part_stat_read(hd, merges[0]), (unsigned long long)part_stat_read(hd, sectors[0]), @@ -661,11 +695,22 @@ dev_t blk_lookup_devt(const char *name, int partno) while ((dev = class_dev_iter_next(&iter))) { struct gendisk *disk = dev_to_disk(dev); - if (!strcmp(dev->bus_id, name) && partno < disk->minors) { - devt = MKDEV(MAJOR(dev->devt), - MINOR(dev->devt) + partno); - break; + if (strcmp(dev->bus_id, name)) + continue; + if (partno < 0 || partno > disk_max_parts(disk)) + continue; + + if (partno == 0) + devt = disk_devt(disk); + else { + struct hd_struct *part = disk->part[partno - 1]; + + if (!part || !part->nr_sects) + continue; + + devt = part_devt(part); } + break; } class_dev_iter_exit(&iter); return devt; @@ -755,7 +800,7 @@ void set_disk_ro(struct gendisk *disk, int flag) { int i; disk->policy = flag; - for (i = 0; i < disk->minors - 1; i++) + for (i = 0; i < disk_max_parts(disk); i++) if (disk->part[i]) disk->part[i]->policy = flag; } diff --git a/block/ioctl.c b/block/ioctl.c index d77f5e280a6e..403f7d7e0c28 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -29,7 +29,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user if (bdev != bdev->bd_contains) return -EINVAL; partno = p.pno; - if (partno <= 0 || partno >= disk->minors) + if (partno <= 0 || partno > disk_max_parts(disk)) return -EINVAL; switch (a.op) { case BLKPG_ADD_PARTITION: @@ -47,7 +47,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user mutex_lock(&bdev->bd_mutex); /* overlap? */ - for (i = 0; i < disk->minors - 1; i++) { + for (i = 0; i < disk_max_parts(disk); i++) { struct hd_struct *s = disk->part[i]; if (!s) @@ -96,7 +96,7 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = bdev->bd_disk; int res; - if (disk->minors == 1 || bdev != bdev->bd_contains) + if (!disk_max_parts(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 29b7a648cc6e..e1a90bbb4747 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2911,7 +2911,7 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) if (!disk->queue) goto out_mem2; - pd->pkt_dev = MKDEV(disk->major, disk->first_minor); + pd->pkt_dev = MKDEV(pktdev_major, idx); ret = pkt_new_dev(pd, dev); if (ret) goto out_new_dev; diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c index 4b0d6c7f4c66..936466f62afd 100644 --- a/drivers/block/ps3disk.c +++ b/drivers/block/ps3disk.c @@ -541,7 +541,7 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev) struct ps3disk_private *priv = dev->sbd.core.driver_data; mutex_lock(&ps3disk_mask_mutex); - __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS, + __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS, &ps3disk_mask); mutex_unlock(&ps3disk_mask_mutex); del_gendisk(priv->gendisk); diff --git a/drivers/char/random.c b/drivers/char/random.c index 7ce1ac4baa6d..6af435b89867 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -661,10 +661,10 @@ void add_disk_randomness(struct gendisk *disk) if (!disk || !disk->random) return; /* first major is 1, so we get >= 0x200 here */ - DEBUG_ENT("disk event %d:%d\n", disk->major, disk->first_minor); + DEBUG_ENT("disk event %d:%d\n", + MAJOR(disk_devt(disk)), MINOR(disk_devt(disk))); - add_timer_randomness(disk->random, - 0x100 + MKDEV(disk->major, disk->first_minor)); + add_timer_randomness(disk->random, 0x100 + disk_devt(disk)); } #endif diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index b262c0042de3..c3de311117a1 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -426,7 +426,7 @@ static int list_devices(struct dm_ioctl *param, size_t param_size) old_nl->next = (uint32_t) ((void *) nl - (void *) old_nl); disk = dm_disk(hc->md); - nl->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); + nl->dev = huge_encode_dev(disk_devt(disk)); nl->next = 0; strcpy(nl->name, hc->name); @@ -539,7 +539,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) if (dm_suspended(md)) param->flags |= DM_SUSPEND_FLAG; - param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); + param->dev = huge_encode_dev(disk_devt(disk)); /* * Yes, this will be out of date by the time it gets back diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 4de90ab3968b..b745d8ac625b 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -284,8 +284,8 @@ static int stripe_end_io(struct dm_target *ti, struct bio *bio, memset(major_minor, 0, sizeof(major_minor)); sprintf(major_minor, "%d:%d", - bio->bi_bdev->bd_disk->major, - bio->bi_bdev->bd_disk->first_minor); + MAJOR(disk_devt(bio->bi_bdev->bd_disk)), + MINOR(disk_devt(bio->bi_bdev->bd_disk))); /* * Test to see which stripe drive triggered the event diff --git a/drivers/md/dm.c b/drivers/md/dm.c index ace998ce59f6..a78caad29996 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1146,7 +1146,7 @@ static void unlock_fs(struct mapped_device *md); static void free_dev(struct mapped_device *md) { - int minor = md->disk->first_minor; + int minor = MINOR(disk_devt(md->disk)); if (md->suspended_bdev) { unlock_fs(md); @@ -1267,7 +1267,7 @@ static struct mapped_device *dm_find_md(dev_t dev) md = idr_find(&_minor_idr, minor); if (md && (md == MINOR_ALLOCED || - (dm_disk(md)->first_minor != minor) || + (MINOR(disk_devt(dm_disk(md))) != minor) || test_bit(DMF_FREEING, &md->flags))) { md = NULL; goto out; @@ -1318,7 +1318,8 @@ void dm_put(struct mapped_device *md) if (atomic_dec_and_lock(&md->holders, &_minor_lock)) { map = dm_get_table(md); - idr_replace(&_minor_idr, MINOR_ALLOCED, dm_disk(md)->first_minor); + idr_replace(&_minor_idr, MINOR_ALLOCED, + MINOR(disk_devt(dm_disk(md)))); set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); if (!dm_suspended(md)) { diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index d2d2318dafa4..82bf649ef138 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -197,7 +197,7 @@ static int mspro_block_bd_open(struct inode *inode, struct file *filp) static int mspro_block_disk_release(struct gendisk *disk) { struct mspro_block_data *msb = disk->private_data; - int disk_id = disk->first_minor >> MSPRO_BLOCK_PART_SHIFT; + int disk_id = MINOR(disk_devt(disk)) >> MSPRO_BLOCK_PART_SHIFT; mutex_lock(&mspro_block_disk_lock); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index ebc8b9d77613..97156b689e82 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -83,7 +83,7 @@ static void mmc_blk_put(struct mmc_blk_data *md) mutex_lock(&open_lock); md->usage--; if (md->usage == 0) { - int devidx = md->disk->first_minor >> MMC_SHIFT; + int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT; __clear_bit(devidx, dev_use); put_disk(md->disk); diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 03c0e40a92ff..e3b5c4d3036e 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -76,7 +76,8 @@ dasd_devices_show(struct seq_file *m, void *v) /* Print kdev. */ if (block->gdp) seq_printf(m, " at (%3d:%6d)", - block->gdp->major, block->gdp->first_minor); + MAJOR(disk_devt(block->gdp)), + MINOR(disk_devt(block->gdp))); else seq_printf(m, " at (???:??????)"); /* Print device name. */ diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 711b3004b3e6..9481e4a3f76e 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -114,7 +114,7 @@ dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info) found = 0; // test if minor available list_for_each_entry(entry, &dcssblk_devices, lh) - if (minor == entry->gd->first_minor) + if (minor == MINOR(disk_devt(entry->gd))) found++; if (!found) break; // got unused minor } @@ -397,7 +397,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char goto unload_seg; } sprintf(dev_info->gd->disk_name, "dcssblk%d", - dev_info->gd->first_minor); + MINOR(disk_devt(dev_info->gd))); list_add_tail(&dev_info->lh, &dcssblk_devices); if (!try_module_get(THIS_MODULE)) { diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 27f5bfd1def3..8dbe3798d5fd 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -878,7 +878,7 @@ static void sr_kref_release(struct kref *kref) struct gendisk *disk = cd->disk; spin_lock(&sr_index_lock); - clear_bit(disk->first_minor, sr_index_bits); + clear_bit(MINOR(disk_devt(disk)), sr_index_bits); spin_unlock(&sr_index_lock); unregister_cdrom(&cd->cdi); diff --git a/fs/block_dev.c b/fs/block_dev.c index de0776cd7215..72e0a2887cb7 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -892,7 +892,7 @@ int check_disk_change(struct block_device *bdev) if (bdops->revalidate_disk) bdops->revalidate_disk(bdev->bd_disk); - if (bdev->bd_disk->minors > 1) + if (disk_max_parts(bdev->bd_disk)) bdev->bd_invalidated = 1; return 1; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index b86aab1b0df6..e77fa144a07d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -134,7 +134,11 @@ char *disk_name(struct gendisk *hd, int partno, char *buf) const char *bdevname(struct block_device *bdev, char *buf) { - int partno = MINOR(bdev->bd_dev) - bdev->bd_disk->first_minor; + int partno = 0; + + if (bdev->bd_part) + partno = bdev->bd_part->partno; + return disk_name(bdev->bd_disk, partno, buf); } @@ -169,7 +173,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev) if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); - state->limit = hd->minors; + state->limit = disk_max_parts(hd) + 1; i = res = err = 0; while (!res && check_part[i]) { memset(&state->parts, 0, sizeof(state->parts)); @@ -416,7 +420,6 @@ void register_disk(struct gendisk *disk) int err; disk->dev.parent = disk->driverfs_dev; - disk->dev.devt = MKDEV(disk->major, disk->first_minor); strlcpy(disk->dev.bus_id, disk->disk_name, BUS_ID_SIZE); /* ewww... some of these buggers have / in the name... */ @@ -440,7 +443,7 @@ void register_disk(struct gendisk *disk) disk_sysfs_add_subdirs(disk); /* No minors to use for partitions */ - if (disk->minors == 1) + if (!disk_max_parts(disk)) goto exit; /* No such device (e.g., media were just removed) */ @@ -463,8 +466,8 @@ exit: kobject_uevent(&disk->dev.kobj, KOBJ_ADD); /* announce possible partitions */ - for (i = 1; i < disk->minors; i++) { - p = disk->part[i-1]; + for (i = 0; i < disk_max_parts(disk); i++) { + p = disk->part[i]; if (!p || !p->nr_sects) continue; kobject_uevent(&p->dev.kobj, KOBJ_ADD); @@ -482,7 +485,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) if (res) return res; bdev->bd_invalidated = 0; - for (p = 1; p < disk->minors; p++) + for (p = 1; p <= disk_max_parts(disk); p++) delete_partition(disk, p); if (disk->fops->revalidate_disk) disk->fops->revalidate_disk(disk); @@ -545,7 +548,7 @@ void del_gendisk(struct gendisk *disk) int p; /* invalidate stuff */ - for (p = disk->minors - 1; p > 0; p--) { + for (p = disk_max_parts(disk); p > 0; p--) { invalidate_partition(disk, p); delete_partition(disk, p); } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index d1723c0a8600..0ff75329199c 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -111,10 +111,14 @@ struct hd_struct { #define GENHD_FL_FAIL 64 struct gendisk { + /* major, first_minor and minors are input parameters only, + * don't use directly. Use disk_devt() and disk_max_parts(). + */ int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ + char disk_name[32]; /* name of major driver */ struct hd_struct **part; /* [indexed by minor - 1] */ struct block_device_operations *fops; @@ -152,6 +156,21 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part) return NULL; } +static inline int disk_max_parts(struct gendisk *disk) +{ + return disk->minors - 1; +} + +static inline dev_t disk_devt(struct gendisk *disk) +{ + return disk->dev.devt; +} + +static inline dev_t part_devt(struct hd_struct *part) +{ + return part->dev.devt; +} + /* * Macros to operate on percpu disk statistics: * @@ -163,7 +182,7 @@ static inline struct hd_struct *disk_map_sector(struct gendisk *gendiskp, { struct hd_struct *part; int i; - for (i = 0; i < gendiskp->minors - 1; i++) { + for (i = 0; i < disk_max_parts(gendiskp); i++) { part = gendiskp->part[i]; if (part && part->start_sect <= sector && sector < part->start_sect + part->nr_sects) @@ -366,6 +385,7 @@ extern void add_disk(struct gendisk *disk); extern void del_gendisk(struct gendisk *gp); extern void unlink_gendisk(struct gendisk *gp); extern struct gendisk *get_gendisk(dev_t dev, int *partno); +extern struct block_device *bdget_disk(struct gendisk *disk, int partno); extern void set_device_ro(struct block_device *bdev, int flag); extern void set_disk_ro(struct gendisk *disk, int flag); @@ -553,11 +573,6 @@ extern void blk_register_region(dev_t devt, unsigned long range, void *data); extern void blk_unregister_region(dev_t devt, unsigned long range); -static inline struct block_device *bdget_disk(struct gendisk *disk, int partno) -{ - return bdget(MKDEV(disk->major, disk->first_minor) + partno); -} - #else /* CONFIG_BLOCK */ static inline void printk_all_partitions(void) { } -- cgit v1.2.3 From e71bf0d0ee89e51b92776391c5634938236977d5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Sep 2008 09:03:02 +0200 Subject: block: fix disk->part[] dereferencing race disk->part[] is protected by its matching bdev's lock. However, non-critical accesses like collecting stats and printing out sysfs and proc information used to be performed without any locking. As partitions can come and go dynamically, partitions can go away underneath those non-critical accesses. As some of those accesses are writes, this theoretically can lead to silent corruption. This patch fixes the race by using RCU for the partition array and dev reference counter to hold partitions. * Rename disk->part[] to disk->__part[] to make sure no one outside genhd layer proper accesses it directly. * Use RCU for disk->__part[] dereferencing. * Implement disk_{get|put}_part() which can be used to get and put partitions from gendisk respectively. * Iterators are implemented to help iterate through all partitions safely. * Functions which require RCU readlock are marked with _rcu suffix. * Use disk_put_part() in __blkdev_put() instead of directly putting the contained kobject. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 20 ++++- block/blk-merge.c | 9 +- block/genhd.c | 218 ++++++++++++++++++++++++++++++++++++++------- block/ioctl.c | 26 +++--- drivers/block/aoe/aoecmd.c | 6 +- fs/block_dev.c | 15 ++-- fs/partitions/check.c | 70 +++++++++------ include/linux/genhd.h | 53 ++++++++--- 8 files changed, 323 insertions(+), 94 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index a0dc2e72fcbb..d6128d9ad601 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -60,7 +60,9 @@ static void drive_stat_acct(struct request *rq, int new_io) if (!blk_fs_request(rq) || !rq->rq_disk) return; - part = disk_map_sector(rq->rq_disk, rq->sector); + rcu_read_lock(); + + part = disk_map_sector_rcu(rq->rq_disk, rq->sector); if (!new_io) __all_stat_inc(rq->rq_disk, part, merges[rw], rq->sector); else { @@ -71,6 +73,8 @@ static void drive_stat_acct(struct request *rq, int new_io) part->in_flight++; } } + + rcu_read_unlock(); } void blk_queue_congestion_threshold(struct request_queue *q) @@ -1557,12 +1561,14 @@ static int __end_that_request_first(struct request *req, int error, } if (blk_fs_request(req) && req->rq_disk) { - struct hd_struct *part = - disk_map_sector(req->rq_disk, req->sector); const int rw = rq_data_dir(req); + struct hd_struct *part; + rcu_read_lock(); + part = disk_map_sector_rcu(req->rq_disk, req->sector); all_stat_add(req->rq_disk, part, sectors[rw], nr_bytes >> 9, req->sector); + rcu_read_unlock(); } total_bytes = bio_nbytes = 0; @@ -1746,7 +1752,11 @@ static void end_that_request_last(struct request *req, int error) if (disk && blk_fs_request(req) && req != &req->q->bar_rq) { unsigned long duration = jiffies - req->start_time; const int rw = rq_data_dir(req); - struct hd_struct *part = disk_map_sector(disk, req->sector); + struct hd_struct *part; + + rcu_read_lock(); + + part = disk_map_sector_rcu(disk, req->sector); __all_stat_inc(disk, part, ios[rw], req->sector); __all_stat_add(disk, part, ticks[rw], duration, req->sector); @@ -1756,6 +1766,8 @@ static void end_that_request_last(struct request *req, int error) part_round_stats(part); part->in_flight--; } + + rcu_read_unlock(); } if (req->end_io) diff --git a/block/blk-merge.c b/block/blk-merge.c index 9b17da698d7c..eb2a3ca58303 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -387,14 +387,19 @@ static int attempt_merge(struct request_queue *q, struct request *req, elv_merge_requests(q, req, next); if (req->rq_disk) { - struct hd_struct *part = - disk_map_sector(req->rq_disk, req->sector); + struct hd_struct *part; + + rcu_read_lock(); + + part = disk_map_sector_rcu(req->rq_disk, req->sector); disk_round_stats(req->rq_disk); req->rq_disk->in_flight--; if (part) { part_round_stats(part); part->in_flight--; } + + rcu_read_unlock(); } req->ioprio = ioprio_best(req->ioprio, next->ioprio); diff --git a/block/genhd.c b/block/genhd.c index fa32d09fda24..b431d6543942 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -26,6 +26,158 @@ struct kobject *block_depr; static struct device_type disk_type; +/** + * disk_get_part - get partition + * @disk: disk to look partition from + * @partno: partition number + * + * Look for partition @partno from @disk. If found, increment + * reference count and return it. + * + * CONTEXT: + * Don't care. + * + * RETURNS: + * Pointer to the found partition on success, NULL if not found. + */ +struct hd_struct *disk_get_part(struct gendisk *disk, int partno) +{ + struct hd_struct *part; + + if (unlikely(partno < 1 || partno > disk_max_parts(disk))) + return NULL; + rcu_read_lock(); + part = rcu_dereference(disk->__part[partno - 1]); + if (part) + get_device(&part->dev); + rcu_read_unlock(); + + return part; +} +EXPORT_SYMBOL_GPL(disk_get_part); + +/** + * disk_part_iter_init - initialize partition iterator + * @piter: iterator to initialize + * @disk: disk to iterate over + * @flags: DISK_PITER_* flags + * + * Initialize @piter so that it iterates over partitions of @disk. + * + * CONTEXT: + * Don't care. + */ +void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, + unsigned int flags) +{ + piter->disk = disk; + piter->part = NULL; + + if (flags & DISK_PITER_REVERSE) + piter->idx = disk_max_parts(piter->disk) - 1; + else + piter->idx = 0; + + piter->flags = flags; +} +EXPORT_SYMBOL_GPL(disk_part_iter_init); + +/** + * disk_part_iter_next - proceed iterator to the next partition and return it + * @piter: iterator of interest + * + * Proceed @piter to the next partition and return it. + * + * CONTEXT: + * Don't care. + */ +struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) +{ + int inc, end; + + /* put the last partition */ + disk_put_part(piter->part); + piter->part = NULL; + + rcu_read_lock(); + + /* determine iteration parameters */ + if (piter->flags & DISK_PITER_REVERSE) { + inc = -1; + end = -1; + } else { + inc = 1; + end = disk_max_parts(piter->disk); + } + + /* iterate to the next partition */ + for (; piter->idx != end; piter->idx += inc) { + struct hd_struct *part; + + part = rcu_dereference(piter->disk->__part[piter->idx]); + if (!part) + continue; + if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects) + continue; + + get_device(&part->dev); + piter->part = part; + piter->idx += inc; + break; + } + + rcu_read_unlock(); + + return piter->part; +} +EXPORT_SYMBOL_GPL(disk_part_iter_next); + +/** + * disk_part_iter_exit - finish up partition iteration + * @piter: iter of interest + * + * Called when iteration is over. Cleans up @piter. + * + * CONTEXT: + * Don't care. + */ +void disk_part_iter_exit(struct disk_part_iter *piter) +{ + disk_put_part(piter->part); + piter->part = NULL; +} +EXPORT_SYMBOL_GPL(disk_part_iter_exit); + +/** + * disk_map_sector_rcu - map sector to partition + * @disk: gendisk of interest + * @sector: sector to map + * + * Find out which partition @sector maps to on @disk. This is + * primarily used for stats accounting. + * + * CONTEXT: + * RCU read locked. The returned partition pointer is valid only + * while preemption is disabled. + * + * RETURNS: + * Found partition on success, NULL if there's no matching partition. + */ +struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) +{ + int i; + + for (i = 0; i < disk_max_parts(disk); i++) { + struct hd_struct *part = rcu_dereference(disk->__part[i]); + + if (part && part->start_sect <= sector && + sector < part->start_sect + part->nr_sects) + return part; + } + return NULL; +} +EXPORT_SYMBOL_GPL(disk_map_sector_rcu); + /* * Can be deleted altogether. Later. * @@ -245,10 +397,12 @@ extern struct block_device *bdget_disk(struct gendisk *disk, int partno) if (partno == 0) devt = disk_devt(disk); else { - struct hd_struct *part = disk->part[partno - 1]; + struct hd_struct *part; + part = disk_get_part(disk, partno); if (part && part->nr_sects) devt = part_devt(part); + disk_put_part(part); } if (likely(devt != MKDEV(0, 0))) @@ -270,8 +424,9 @@ void __init printk_all_partitions(void) class_dev_iter_init(&iter, &block_class, NULL, &disk_type); while ((dev = class_dev_iter_next(&iter))) { struct gendisk *disk = dev_to_disk(dev); + struct disk_part_iter piter; + struct hd_struct *part; char buf[BDEVNAME_SIZE]; - int n; /* * Don't show empty devices or things that have been @@ -298,16 +453,13 @@ void __init printk_all_partitions(void) printk(" (driver?)\n"); /* now show the partitions */ - for (n = 0; n < disk_max_parts(disk); ++n) { - struct hd_struct *part = disk->part[n]; - - if (!part || !part->nr_sects) - continue; + disk_part_iter_init(&piter, disk, 0); + while ((part = disk_part_iter_next(&piter))) printk(" %02x%02x %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), (unsigned long long)part->nr_sects >> 1, disk_name(disk, part->partno, buf)); - } + disk_part_iter_exit(&piter); } class_dev_iter_exit(&iter); } @@ -371,7 +523,8 @@ static void *show_partition_start(struct seq_file *seqf, loff_t *pos) static int show_partition(struct seq_file *seqf, void *v) { struct gendisk *sgp = v; - int n; + struct disk_part_iter piter; + struct hd_struct *part; char buf[BDEVNAME_SIZE]; /* Don't show non-partitionable removeable devices or empty devices */ @@ -386,17 +539,14 @@ static int show_partition(struct seq_file *seqf, void *v) MAJOR(disk_devt(sgp)), MINOR(disk_devt(sgp)), (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); - for (n = 0; n < disk_max_parts(sgp); n++) { - struct hd_struct *part = sgp->part[n]; - if (!part) - continue; - if (part->nr_sects == 0) - continue; + + disk_part_iter_init(&piter, sgp, 0); + while ((part = disk_part_iter_next(&piter))) seq_printf(seqf, "%4d %4d %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), (unsigned long long)part->nr_sects >> 1, disk_name(sgp, part->partno, buf)); - } + disk_part_iter_exit(&piter); return 0; } @@ -571,7 +721,7 @@ static void disk_release(struct device *dev) struct gendisk *disk = dev_to_disk(dev); kfree(disk->random); - kfree(disk->part); + kfree(disk->__part); free_disk_stats(disk); kfree(disk); } @@ -596,8 +746,9 @@ static struct device_type disk_type = { static int diskstats_show(struct seq_file *seqf, void *v) { struct gendisk *gp = v; + struct disk_part_iter piter; + struct hd_struct *hd; char buf[BDEVNAME_SIZE]; - int n; /* if (&gp->dev.kobj.entry == block_class.devices.next) @@ -624,12 +775,8 @@ static int diskstats_show(struct seq_file *seqf, void *v) jiffies_to_msecs(disk_stat_read(gp, time_in_queue))); /* now show all non-0 size partitions of it */ - for (n = 0; n < disk_max_parts(gp); n++) { - struct hd_struct *hd = gp->part[n]; - - if (!hd || !hd->nr_sects) - continue; - + disk_part_iter_init(&piter, gp, 0); + while ((hd = disk_part_iter_next(&piter))) { preempt_disable(); part_round_stats(hd); preempt_enable(); @@ -650,6 +797,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) jiffies_to_msecs(part_stat_read(hd, time_in_queue)) ); } + disk_part_iter_exit(&piter); return 0; } @@ -703,12 +851,16 @@ dev_t blk_lookup_devt(const char *name, int partno) if (partno == 0) devt = disk_devt(disk); else { - struct hd_struct *part = disk->part[partno - 1]; + struct hd_struct *part; - if (!part || !part->nr_sects) + part = disk_get_part(disk, partno); + if (!part || !part->nr_sects) { + disk_put_part(part); continue; + } devt = part_devt(part); + disk_put_part(part); } break; } @@ -735,9 +887,9 @@ struct gendisk *alloc_disk_node(int minors, int node_id) } if (minors > 1) { int size = (minors - 1) * sizeof(struct hd_struct *); - disk->part = kmalloc_node(size, + disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, node_id); - if (!disk->part) { + if (!disk->__part) { free_disk_stats(disk); kfree(disk); return NULL; @@ -798,10 +950,14 @@ EXPORT_SYMBOL(set_device_ro); void set_disk_ro(struct gendisk *disk, int flag) { - int i; + struct disk_part_iter piter; + struct hd_struct *part; + disk->policy = flag; - for (i = 0; i < disk_max_parts(disk); i++) - if (disk->part[i]) disk->part[i]->policy = flag; + disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY); + while ((part = disk_part_iter_next(&piter))) + part->policy = flag; + disk_part_iter_exit(&piter); } EXPORT_SYMBOL(set_disk_ro); diff --git a/block/ioctl.c b/block/ioctl.c index 403f7d7e0c28..a5f672ad55f6 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -12,11 +12,12 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user { struct block_device *bdevp; struct gendisk *disk; + struct hd_struct *part; struct blkpg_ioctl_arg a; struct blkpg_partition p; + struct disk_part_iter piter; long long start, length; int partno; - int i; int err; if (!capable(CAP_SYS_ADMIN)) @@ -47,28 +48,33 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user mutex_lock(&bdev->bd_mutex); /* overlap? */ - for (i = 0; i < disk_max_parts(disk); i++) { - struct hd_struct *s = disk->part[i]; - - if (!s) - continue; - if (!(start+length <= s->start_sect || - start >= s->start_sect + s->nr_sects)) { + disk_part_iter_init(&piter, disk, + DISK_PITER_INCL_EMPTY); + while ((part = disk_part_iter_next(&piter))) { + if (!(start + length <= part->start_sect || + start >= part->start_sect + part->nr_sects)) { + disk_part_iter_exit(&piter); mutex_unlock(&bdev->bd_mutex); return -EBUSY; } } + disk_part_iter_exit(&piter); + /* all seems OK */ err = add_partition(disk, partno, start, length, ADDPART_FLAG_NONE); mutex_unlock(&bdev->bd_mutex); return err; case BLKPG_DEL_PARTITION: - if (!disk->part[partno - 1]) + part = disk_get_part(disk, partno); + if (!part) return -ENXIO; - bdevp = bdget_disk(disk, partno); + + bdevp = bdget(part_devt(part)); + disk_put_part(part); if (!bdevp) return -ENOMEM; + mutex_lock(&bdevp->bd_mutex); if (bdevp->bd_openers) { mutex_unlock(&bdevp->bd_mutex); diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 885d1409521f..84c03d65dcc5 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -757,11 +757,15 @@ diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector const int rw = bio_data_dir(bio); struct hd_struct *part; - part = disk_map_sector(disk, sector); + rcu_read_lock(); + + part = disk_map_sector_rcu(disk, sector); all_stat_inc(disk, part, ios[rw], sector); all_stat_add(disk, part, ticks[rw], duration, sector); all_stat_add(disk, part, sectors[rw], n_sect, sector); all_stat_add(disk, part, io_ticks, duration, sector); + + rcu_read_unlock(); } void diff --git a/fs/block_dev.c b/fs/block_dev.c index 72e0a2887cb7..2f2873b9a041 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -929,6 +929,7 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) { struct module *owner = NULL; struct gendisk *disk; + struct hd_struct *part = NULL; int ret; int partno; int perm = 0; @@ -978,7 +979,6 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) if (bdev->bd_invalidated) rescan_partitions(disk, bdev); } else { - struct hd_struct *p; struct block_device *whole; whole = bdget_disk(disk, 0); ret = -ENOMEM; @@ -989,16 +989,16 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) if (ret) goto out_first; bdev->bd_contains = whole; - p = disk->part[partno - 1]; + part = disk_get_part(disk, partno); bdev->bd_inode->i_data.backing_dev_info = whole->bd_inode->i_data.backing_dev_info; - if (!(disk->flags & GENHD_FL_UP) || !p || !p->nr_sects) { + if (!(disk->flags & GENHD_FL_UP) || + !part || !part->nr_sects) { ret = -ENXIO; goto out_first; } - kobject_get(&p->dev.kobj); - bdev->bd_part = p; - bd_set_size(bdev, (loff_t) p->nr_sects << 9); + bdev->bd_part = part; + bd_set_size(bdev, (loff_t)part->nr_sects << 9); } } else { put_disk(disk); @@ -1027,6 +1027,7 @@ out_first: __blkdev_put(bdev->bd_contains, 1); bdev->bd_contains = NULL; put_disk(disk); + disk_put_part(part); module_put(owner); out: mutex_unlock(&bdev->bd_mutex); @@ -1119,7 +1120,7 @@ static int __blkdev_put(struct block_device *bdev, int for_part) module_put(owner); if (bdev->bd_contains != bdev) { - kobject_put(&bdev->bd_part->dev.kobj); + disk_put_part(bdev->bd_part); bdev->bd_part = NULL; } bdev->bd_disk = NULL; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index e77fa144a07d..96c8bf41e455 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -314,19 +314,29 @@ static inline void disk_sysfs_add_subdirs(struct gendisk *disk) kobject_put(k); } +static void delete_partition_rcu_cb(struct rcu_head *head) +{ + struct hd_struct *part = container_of(head, struct hd_struct, rcu_head); + + part->start_sect = 0; + part->nr_sects = 0; + part_stat_set_all(part, 0); + put_device(&part->dev); +} + void delete_partition(struct gendisk *disk, int partno) { - struct hd_struct *p = disk->part[partno - 1]; + struct hd_struct *part; - if (!p) + part = disk->__part[partno-1]; + if (!part) return; - disk->part[partno - 1] = NULL; - p->start_sect = 0; - p->nr_sects = 0; - part_stat_set_all(p, 0); - kobject_put(p->holder_dir); - device_del(&p->dev); - put_device(&p->dev); + + rcu_assign_pointer(disk->__part[partno-1], NULL); + kobject_put(part->holder_dir); + device_del(&part->dev); + + call_rcu(&part->rcu_head, delete_partition_rcu_cb); } static ssize_t whole_disk_show(struct device *dev, @@ -343,7 +353,7 @@ int add_partition(struct gendisk *disk, int partno, struct hd_struct *p; int err; - if (disk->part[partno - 1]) + if (disk->__part[partno - 1]) return -EBUSY; p = kzalloc(sizeof(*p), GFP_KERNEL); @@ -391,7 +401,8 @@ int add_partition(struct gendisk *disk, int partno, } /* everything is up and running, commence */ - disk->part[partno - 1] = p; + INIT_RCU_HEAD(&p->rcu_head); + rcu_assign_pointer(disk->__part[partno - 1], p); /* suppress uevent if the disk supresses it */ if (!disk->dev.uevent_suppress) @@ -414,9 +425,9 @@ out_put: void register_disk(struct gendisk *disk) { struct block_device *bdev; + struct disk_part_iter piter; + struct hd_struct *part; char *s; - int i; - struct hd_struct *p; int err; disk->dev.parent = disk->driverfs_dev; @@ -466,16 +477,16 @@ exit: kobject_uevent(&disk->dev.kobj, KOBJ_ADD); /* announce possible partitions */ - for (i = 0; i < disk_max_parts(disk); i++) { - p = disk->part[i]; - if (!p || !p->nr_sects) - continue; - kobject_uevent(&p->dev.kobj, KOBJ_ADD); - } + disk_part_iter_init(&piter, disk, 0); + while ((part = disk_part_iter_next(&piter))) + kobject_uevent(&part->dev.kobj, KOBJ_ADD); + disk_part_iter_exit(&piter); } int rescan_partitions(struct gendisk *disk, struct block_device *bdev) { + struct disk_part_iter piter; + struct hd_struct *part; struct parsed_partitions *state; int p, res; @@ -485,8 +496,12 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) if (res) return res; bdev->bd_invalidated = 0; - for (p = 1; p <= disk_max_parts(disk); p++) - delete_partition(disk, p); + + disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY); + while ((part = disk_part_iter_next(&piter))) + delete_partition(disk, part->partno); + disk_part_iter_exit(&piter); + if (disk->fops->revalidate_disk) disk->fops->revalidate_disk(disk); if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) @@ -545,13 +560,18 @@ EXPORT_SYMBOL(read_dev_sector); void del_gendisk(struct gendisk *disk) { - int p; + struct disk_part_iter piter; + struct hd_struct *part; /* invalidate stuff */ - for (p = disk_max_parts(disk); p > 0; p--) { - invalidate_partition(disk, p); - delete_partition(disk, p); + disk_part_iter_init(&piter, disk, + DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); + while ((part = disk_part_iter_next(&piter))) { + invalidate_partition(disk, part->partno); + delete_partition(disk, part->partno); } + disk_part_iter_exit(&piter); + invalidate_partition(disk, 0); disk->capacity = 0; disk->flags &= ~GENHD_FL_UP; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 0ff75329199c..7fbba19e076b 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -11,6 +11,7 @@ #include #include +#include #ifdef CONFIG_BLOCK @@ -100,6 +101,7 @@ struct hd_struct { #else struct disk_stats dkstats; #endif + struct rcu_head rcu_head; }; #define GENHD_FL_REMOVABLE 1 @@ -120,7 +122,14 @@ struct gendisk { * disks that can't be partitioned. */ char disk_name[32]; /* name of major driver */ - struct hd_struct **part; /* [indexed by minor - 1] */ + + /* Array of pointers to partitions indexed by partno - 1. + * Protected with matching bdev lock but stat and other + * non-critical accesses use RCU. Always access through + * helpers. + */ + struct hd_struct **__part; + struct block_device_operations *fops; struct request_queue *queue; void *private_data; @@ -171,25 +180,41 @@ static inline dev_t part_devt(struct hd_struct *part) return part->dev.devt; } +extern struct hd_struct *disk_get_part(struct gendisk *disk, int partno); + +static inline void disk_put_part(struct hd_struct *part) +{ + if (likely(part)) + put_device(&part->dev); +} + +/* + * Smarter partition iterator without context limits. + */ +#define DISK_PITER_REVERSE (1 << 0) /* iterate in the reverse direction */ +#define DISK_PITER_INCL_EMPTY (1 << 1) /* include 0-sized parts */ + +struct disk_part_iter { + struct gendisk *disk; + struct hd_struct *part; + int idx; + unsigned int flags; +}; + +extern void disk_part_iter_init(struct disk_part_iter *piter, + struct gendisk *disk, unsigned int flags); +extern struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter); +extern void disk_part_iter_exit(struct disk_part_iter *piter); + +extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, + sector_t sector); + /* * Macros to operate on percpu disk statistics: * * The __ variants should only be called in critical sections. The full * variants disable/enable preemption. */ -static inline struct hd_struct *disk_map_sector(struct gendisk *gendiskp, - sector_t sector) -{ - struct hd_struct *part; - int i; - for (i = 0; i < disk_max_parts(gendiskp); i++) { - part = gendiskp->part[i]; - if (part && part->start_sect <= sector - && sector < part->start_sect + part->nr_sects) - return part; - } - return NULL; -} #ifdef CONFIG_SMP #define __disk_stat_add(gendiskp, field, addnd) \ -- cgit v1.2.3 From c9959059161ddd7bf4670cf47367033d6b2f79c4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:47:21 +0900 Subject: block: fix diskstats access There are two variants of stat functions - ones prefixed with double underbars which don't care about preemption and ones without which disable preemption before manipulating per-cpu counters. It's unclear whether the underbarred ones assume that preemtion is disabled on entry as some callers don't do that. This patch unifies diskstats access by implementing disk_stat_lock() and disk_stat_unlock() which take care of both RCU (for partition access) and preemption (for per-cpu counter access). diskstats access should always be enclosed between the two functions. As such, there's no need for the versions which disables preemption. They're removed and double underbars ones are renamed to drop the underbars. As an extra argument is added, there's no danger of using the old version unconverted. disk_stat_lock() uses get_cpu() and returns the cpu index and all diskstat functions which access per-cpu counters now has @cpu argument to help RT. This change adds RCU or preemption operations at some places but also collapses several preemption ops into one at others. Overall, the performance difference should be negligible as all involved ops are very lightweight per-cpu ones. Signed-off-by: Tejun Heo Cc: Peter Zijlstra Signed-off-by: Jens Axboe --- block/blk-core.c | 52 +++++++++-------- block/blk-merge.c | 11 ++-- block/genhd.c | 20 ++++--- drivers/block/aoe/aoecmd.c | 15 ++--- drivers/md/dm.c | 26 +++++---- drivers/md/linear.c | 7 ++- drivers/md/multipath.c | 7 ++- drivers/md/raid0.c | 7 ++- drivers/md/raid1.c | 8 ++- drivers/md/raid10.c | 7 ++- drivers/md/raid5.c | 8 ++- fs/partitions/check.c | 7 ++- include/linux/genhd.h | 139 +++++++++++++++++++-------------------------- 13 files changed, 158 insertions(+), 156 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index d6128d9ad601..e0a5ee36849c 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -56,25 +56,26 @@ static void drive_stat_acct(struct request *rq, int new_io) { struct hd_struct *part; int rw = rq_data_dir(rq); + int cpu; if (!blk_fs_request(rq) || !rq->rq_disk) return; - rcu_read_lock(); - + cpu = disk_stat_lock(); part = disk_map_sector_rcu(rq->rq_disk, rq->sector); + if (!new_io) - __all_stat_inc(rq->rq_disk, part, merges[rw], rq->sector); + all_stat_inc(cpu, rq->rq_disk, part, merges[rw], rq->sector); else { - disk_round_stats(rq->rq_disk); + disk_round_stats(cpu, rq->rq_disk); rq->rq_disk->in_flight++; if (part) { - part_round_stats(part); + part_round_stats(cpu, part); part->in_flight++; } } - rcu_read_unlock(); + disk_stat_unlock(); } void blk_queue_congestion_threshold(struct request_queue *q) @@ -997,7 +998,7 @@ static inline void add_request(struct request_queue *q, struct request *req) * /proc/diskstats. This accounts immediately for all queue usage up to * the current jiffies and restarts the counters again. */ -void disk_round_stats(struct gendisk *disk) +void disk_round_stats(int cpu, struct gendisk *disk) { unsigned long now = jiffies; @@ -1005,15 +1006,15 @@ void disk_round_stats(struct gendisk *disk) return; if (disk->in_flight) { - __disk_stat_add(disk, time_in_queue, - disk->in_flight * (now - disk->stamp)); - __disk_stat_add(disk, io_ticks, (now - disk->stamp)); + disk_stat_add(cpu, disk, time_in_queue, + disk->in_flight * (now - disk->stamp)); + disk_stat_add(cpu, disk, io_ticks, (now - disk->stamp)); } disk->stamp = now; } EXPORT_SYMBOL_GPL(disk_round_stats); -void part_round_stats(struct hd_struct *part) +void part_round_stats(int cpu, struct hd_struct *part) { unsigned long now = jiffies; @@ -1021,9 +1022,9 @@ void part_round_stats(struct hd_struct *part) return; if (part->in_flight) { - __part_stat_add(part, time_in_queue, - part->in_flight * (now - part->stamp)); - __part_stat_add(part, io_ticks, (now - part->stamp)); + part_stat_add(cpu, part, time_in_queue, + part->in_flight * (now - part->stamp)); + part_stat_add(cpu, part, io_ticks, (now - part->stamp)); } part->stamp = now; } @@ -1563,12 +1564,13 @@ static int __end_that_request_first(struct request *req, int error, if (blk_fs_request(req) && req->rq_disk) { const int rw = rq_data_dir(req); struct hd_struct *part; + int cpu; - rcu_read_lock(); + cpu = disk_stat_lock(); part = disk_map_sector_rcu(req->rq_disk, req->sector); - all_stat_add(req->rq_disk, part, sectors[rw], - nr_bytes >> 9, req->sector); - rcu_read_unlock(); + all_stat_add(cpu, req->rq_disk, part, sectors[rw], + nr_bytes >> 9, req->sector); + disk_stat_unlock(); } total_bytes = bio_nbytes = 0; @@ -1753,21 +1755,21 @@ static void end_that_request_last(struct request *req, int error) unsigned long duration = jiffies - req->start_time; const int rw = rq_data_dir(req); struct hd_struct *part; + int cpu; - rcu_read_lock(); - + cpu = disk_stat_lock(); part = disk_map_sector_rcu(disk, req->sector); - __all_stat_inc(disk, part, ios[rw], req->sector); - __all_stat_add(disk, part, ticks[rw], duration, req->sector); - disk_round_stats(disk); + all_stat_inc(cpu, disk, part, ios[rw], req->sector); + all_stat_add(cpu, disk, part, ticks[rw], duration, req->sector); + disk_round_stats(cpu, disk); disk->in_flight--; if (part) { - part_round_stats(part); + part_round_stats(cpu, part); part->in_flight--; } - rcu_read_unlock(); + disk_stat_unlock(); } if (req->end_io) diff --git a/block/blk-merge.c b/block/blk-merge.c index eb2a3ca58303..d926a24bf1fd 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -388,18 +388,19 @@ static int attempt_merge(struct request_queue *q, struct request *req, if (req->rq_disk) { struct hd_struct *part; + int cpu; - rcu_read_lock(); - + cpu = disk_stat_lock(); part = disk_map_sector_rcu(req->rq_disk, req->sector); - disk_round_stats(req->rq_disk); + + disk_round_stats(cpu, req->rq_disk); req->rq_disk->in_flight--; if (part) { - part_round_stats(part); + part_round_stats(cpu, part); part->in_flight--; } - rcu_read_unlock(); + disk_stat_unlock(); } req->ioprio = ioprio_best(req->ioprio, next->ioprio); diff --git a/block/genhd.c b/block/genhd.c index b431d6543942..430626e440f0 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -633,10 +633,11 @@ static ssize_t disk_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); + int cpu; - preempt_disable(); - disk_round_stats(disk); - preempt_enable(); + cpu = disk_stat_lock(); + disk_round_stats(cpu, disk); + disk_stat_unlock(); return sprintf(buf, "%8lu %8lu %8llu %8u " "%8lu %8lu %8llu %8u " @@ -749,6 +750,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) struct disk_part_iter piter; struct hd_struct *hd; char buf[BDEVNAME_SIZE]; + int cpu; /* if (&gp->dev.kobj.entry == block_class.devices.next) @@ -758,9 +760,9 @@ static int diskstats_show(struct seq_file *seqf, void *v) "\n\n"); */ - preempt_disable(); - disk_round_stats(gp); - preempt_enable(); + cpu = disk_stat_lock(); + disk_round_stats(cpu, gp); + disk_stat_unlock(); seq_printf(seqf, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", MAJOR(disk_devt(gp)), MINOR(disk_devt(gp)), disk_name(gp, 0, buf), @@ -777,9 +779,9 @@ static int diskstats_show(struct seq_file *seqf, void *v) /* now show all non-0 size partitions of it */ disk_part_iter_init(&piter, gp, 0); while ((hd = disk_part_iter_next(&piter))) { - preempt_disable(); - part_round_stats(hd); - preempt_enable(); + cpu = disk_stat_lock(); + part_round_stats(cpu, hd); + disk_stat_unlock(); seq_printf(seqf, "%4d %4d %s %lu %lu %llu " "%u %lu %lu %llu %u %u %u %u\n", MAJOR(part_devt(hd)), MINOR(part_devt(hd)), diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 84c03d65dcc5..17eed8c025d0 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -756,16 +756,17 @@ diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector unsigned long n_sect = bio->bi_size >> 9; const int rw = bio_data_dir(bio); struct hd_struct *part; + int cpu; - rcu_read_lock(); - + cpu = disk_stat_lock(); part = disk_map_sector_rcu(disk, sector); - all_stat_inc(disk, part, ios[rw], sector); - all_stat_add(disk, part, ticks[rw], duration, sector); - all_stat_add(disk, part, sectors[rw], n_sect, sector); - all_stat_add(disk, part, io_ticks, duration, sector); - rcu_read_unlock(); + all_stat_inc(cpu, disk, part, ios[rw], sector); + all_stat_add(cpu, disk, part, ticks[rw], duration, sector); + all_stat_add(cpu, disk, part, sectors[rw], n_sect, sector); + all_stat_add(cpu, disk, part, io_ticks, duration, sector); + + disk_stat_unlock(); } void diff --git a/drivers/md/dm.c b/drivers/md/dm.c index a78caad29996..653624792eaf 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -377,12 +377,13 @@ static void free_tio(struct mapped_device *md, struct dm_target_io *tio) static void start_io_acct(struct dm_io *io) { struct mapped_device *md = io->md; + int cpu; io->start_time = jiffies; - preempt_disable(); - disk_round_stats(dm_disk(md)); - preempt_enable(); + cpu = disk_stat_lock(); + disk_round_stats(cpu, dm_disk(md)); + disk_stat_unlock(); dm_disk(md)->in_flight = atomic_inc_return(&md->pending); } @@ -391,15 +392,15 @@ static int end_io_acct(struct dm_io *io) struct mapped_device *md = io->md; struct bio *bio = io->bio; unsigned long duration = jiffies - io->start_time; - int pending; + int pending, cpu; int rw = bio_data_dir(bio); - preempt_disable(); - disk_round_stats(dm_disk(md)); - preempt_enable(); - dm_disk(md)->in_flight = pending = atomic_dec_return(&md->pending); + cpu = disk_stat_lock(); + disk_round_stats(cpu, dm_disk(md)); + disk_stat_add(cpu, dm_disk(md), ticks[rw], duration); + disk_stat_unlock(); - disk_stat_add(dm_disk(md), ticks[rw], duration); + dm_disk(md)->in_flight = pending = atomic_dec_return(&md->pending); return !pending; } @@ -885,6 +886,7 @@ static int dm_request(struct request_queue *q, struct bio *bio) int r = -EIO; int rw = bio_data_dir(bio); struct mapped_device *md = q->queuedata; + int cpu; /* * There is no use in forwarding any barrier request since we can't @@ -897,8 +899,10 @@ static int dm_request(struct request_queue *q, struct bio *bio) down_read(&md->io_lock); - disk_stat_inc(dm_disk(md), ios[rw]); - disk_stat_add(dm_disk(md), sectors[rw], bio_sectors(bio)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, dm_disk(md), ios[rw]); + disk_stat_add(cpu, dm_disk(md), sectors[rw], bio_sectors(bio)); + disk_stat_unlock(); /* * If we're suspended we have to queue diff --git a/drivers/md/linear.c b/drivers/md/linear.c index b1eebf88c209..00cbc8e47294 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -318,14 +318,17 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) mddev_t *mddev = q->queuedata; dev_info_t *tmp_dev; sector_t block; + int cpu; if (unlikely(bio_barrier(bio))) { bio_endio(bio, -EOPNOTSUPP); return 0; } - disk_stat_inc(mddev->gendisk, ios[rw]); - disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bio)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, mddev->gendisk, ios[rw]); + disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); + disk_stat_unlock(); tmp_dev = which_dev(mddev, bio->bi_sector); block = bio->bi_sector >> 1; diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index c4779ccba1c3..182f5a94cdc5 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -147,6 +147,7 @@ static int multipath_make_request (struct request_queue *q, struct bio * bio) struct multipath_bh * mp_bh; struct multipath_info *multipath; const int rw = bio_data_dir(bio); + int cpu; if (unlikely(bio_barrier(bio))) { bio_endio(bio, -EOPNOTSUPP); @@ -158,8 +159,10 @@ static int multipath_make_request (struct request_queue *q, struct bio * bio) mp_bh->master_bio = bio; mp_bh->mddev = mddev; - disk_stat_inc(mddev->gendisk, ios[rw]); - disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bio)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, mddev->gendisk, ios[rw]); + disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); + disk_stat_unlock(); mp_bh->path = multipath_map(conf); if (mp_bh->path < 0) { diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 183610635661..e26030fa59ab 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -399,14 +399,17 @@ static int raid0_make_request (struct request_queue *q, struct bio *bio) sector_t chunk; sector_t block, rsect; const int rw = bio_data_dir(bio); + int cpu; if (unlikely(bio_barrier(bio))) { bio_endio(bio, -EOPNOTSUPP); return 0; } - disk_stat_inc(mddev->gendisk, ios[rw]); - disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bio)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, mddev->gendisk, ios[rw]); + disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); + disk_stat_unlock(); chunk_size = mddev->chunk_size >> 10; chunk_sects = mddev->chunk_size >> 9; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 0b82030c265d..babb13036f93 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -779,7 +779,7 @@ static int make_request(struct request_queue *q, struct bio * bio) struct page **behind_pages = NULL; const int rw = bio_data_dir(bio); const int do_sync = bio_sync(bio); - int do_barriers; + int cpu, do_barriers; mdk_rdev_t *blocked_rdev; /* @@ -804,8 +804,10 @@ static int make_request(struct request_queue *q, struct bio * bio) bitmap = mddev->bitmap; - disk_stat_inc(mddev->gendisk, ios[rw]); - disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bio)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, mddev->gendisk, ios[rw]); + disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); + disk_stat_unlock(); /* * make_request() can abort the operation when READA is being diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index d3b9aa096285..5ec80da0a9d7 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -789,6 +789,7 @@ static int make_request(struct request_queue *q, struct bio * bio) mirror_info_t *mirror; r10bio_t *r10_bio; struct bio *read_bio; + int cpu; int i; int chunk_sects = conf->chunk_mask + 1; const int rw = bio_data_dir(bio); @@ -843,8 +844,10 @@ static int make_request(struct request_queue *q, struct bio * bio) */ wait_barrier(conf); - disk_stat_inc(mddev->gendisk, ios[rw]); - disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bio)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, mddev->gendisk, ios[rw]); + disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); + disk_stat_unlock(); r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 37e546528f9c..5899f211515f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3387,7 +3387,7 @@ static int make_request(struct request_queue *q, struct bio * bi) sector_t logical_sector, last_sector; struct stripe_head *sh; const int rw = bio_data_dir(bi); - int remaining; + int cpu, remaining; if (unlikely(bio_barrier(bi))) { bio_endio(bi, -EOPNOTSUPP); @@ -3396,8 +3396,10 @@ static int make_request(struct request_queue *q, struct bio * bi) md_write_start(mddev, bi); - disk_stat_inc(mddev->gendisk, ios[rw]); - disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bi)); + cpu = disk_stat_lock(); + disk_stat_inc(cpu, mddev->gendisk, ios[rw]); + disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bi)); + disk_stat_unlock(); if (rw == READ && mddev->reshape_position == MaxSector && diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 96c8bf41e455..c442f0aadac3 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -219,10 +219,11 @@ static ssize_t part_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); + int cpu; - preempt_disable(); - part_round_stats(p); - preempt_enable(); + cpu = disk_stat_lock(); + part_round_stats(cpu, p); + disk_stat_unlock(); return sprintf(buf, "%8lu %8lu %8llu %8u " "%8lu %8lu %8llu %8u " diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 7fbba19e076b..ac8a901f2002 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -209,16 +209,24 @@ extern void disk_part_iter_exit(struct disk_part_iter *piter); extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector); -/* +/* * Macros to operate on percpu disk statistics: * - * The __ variants should only be called in critical sections. The full - * variants disable/enable preemption. + * {disk|part|all}_stat_{add|sub|inc|dec}() modify the stat counters + * and should be called between disk_stat_lock() and + * disk_stat_unlock(). + * + * part_stat_read() can be called at any time. + * + * part_stat_{add|set_all}() and {init|free}_part_stats are for + * internal use only. */ - #ifdef CONFIG_SMP -#define __disk_stat_add(gendiskp, field, addnd) \ - (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd) +#define disk_stat_lock() ({ rcu_read_lock(); get_cpu(); }) +#define disk_stat_unlock() do { put_cpu(); rcu_read_unlock(); } while (0) + +#define disk_stat_add(cpu, gendiskp, field, addnd) \ + (per_cpu_ptr(gendiskp->dkstats, cpu)->field += addnd) #define disk_stat_read(gendiskp, field) \ ({ \ @@ -229,7 +237,8 @@ extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, res; \ }) -static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { +static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) +{ int i; for_each_possible_cpu(i) @@ -237,14 +246,14 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { sizeof(struct disk_stats)); } -#define __part_stat_add(part, field, addnd) \ - (per_cpu_ptr(part->dkstats, smp_processor_id())->field += addnd) +#define part_stat_add(cpu, part, field, addnd) \ + (per_cpu_ptr(part->dkstats, cpu)->field += addnd) -#define __all_stat_add(gendiskp, part, field, addnd, sector) \ -({ \ - if (part) \ - __part_stat_add(part, field, addnd); \ - __disk_stat_add(gendiskp, field, addnd); \ +#define all_stat_add(cpu, gendiskp, part, field, addnd, sector) \ +({ \ + if (part) \ + part_stat_add(cpu, part, field, addnd); \ + disk_stat_add(cpu, gendiskp, field, addnd); \ }) #define part_stat_read(part, field) \ @@ -264,10 +273,13 @@ static inline void part_stat_set_all(struct hd_struct *part, int value) memset(per_cpu_ptr(part->dkstats, i), value, sizeof(struct disk_stats)); } - + #else /* !CONFIG_SMP */ -#define __disk_stat_add(gendiskp, field, addnd) \ - (gendiskp->dkstats.field += addnd) +#define disk_stat_lock() ({ rcu_read_lock(); 0; }) +#define disk_stat_unlock() rcu_read_unlock() + +#define disk_stat_add(cpu, gendiskp, field, addnd) \ + (gendiskp->dkstats.field += addnd) #define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) @@ -275,14 +287,14 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) memset(&gendiskp->dkstats, value, sizeof (struct disk_stats)); } -#define __part_stat_add(part, field, addnd) \ +#define part_stat_add(cpu, part, field, addnd) \ (part->dkstats.field += addnd) -#define __all_stat_add(gendiskp, part, field, addnd, sector) \ -({ \ - if (part) \ - part->dkstats.field += addnd; \ - __disk_stat_add(gendiskp, field, addnd); \ +#define all_stat_add(cpu, gendiskp, part, field, addnd, sector) \ +({ \ + if (part) \ + part_stat_add(cpu, part, field, addnd); \ + disk_stat_add(cpu, gendiskp, field, addnd); \ }) #define part_stat_read(part, field) (part->dkstats.field) @@ -294,63 +306,26 @@ static inline void part_stat_set_all(struct hd_struct *part, int value) #endif /* CONFIG_SMP */ -#define disk_stat_add(gendiskp, field, addnd) \ - do { \ - preempt_disable(); \ - __disk_stat_add(gendiskp, field, addnd); \ - preempt_enable(); \ - } while (0) - -#define __disk_stat_dec(gendiskp, field) __disk_stat_add(gendiskp, field, -1) -#define disk_stat_dec(gendiskp, field) disk_stat_add(gendiskp, field, -1) - -#define __disk_stat_inc(gendiskp, field) __disk_stat_add(gendiskp, field, 1) -#define disk_stat_inc(gendiskp, field) disk_stat_add(gendiskp, field, 1) - -#define __disk_stat_sub(gendiskp, field, subnd) \ - __disk_stat_add(gendiskp, field, -subnd) -#define disk_stat_sub(gendiskp, field, subnd) \ - disk_stat_add(gendiskp, field, -subnd) - -#define part_stat_add(gendiskp, field, addnd) \ - do { \ - preempt_disable(); \ - __part_stat_add(gendiskp, field, addnd);\ - preempt_enable(); \ - } while (0) - -#define __part_stat_dec(gendiskp, field) __part_stat_add(gendiskp, field, -1) -#define part_stat_dec(gendiskp, field) part_stat_add(gendiskp, field, -1) - -#define __part_stat_inc(gendiskp, field) __part_stat_add(gendiskp, field, 1) -#define part_stat_inc(gendiskp, field) part_stat_add(gendiskp, field, 1) - -#define __part_stat_sub(gendiskp, field, subnd) \ - __part_stat_add(gendiskp, field, -subnd) -#define part_stat_sub(gendiskp, field, subnd) \ - part_stat_add(gendiskp, field, -subnd) - -#define all_stat_add(gendiskp, part, field, addnd, sector) \ - do { \ - preempt_disable(); \ - __all_stat_add(gendiskp, part, field, addnd, sector); \ - preempt_enable(); \ - } while (0) - -#define __all_stat_dec(gendiskp, field, sector) \ - __all_stat_add(gendiskp, field, -1, sector) -#define all_stat_dec(gendiskp, field, sector) \ - all_stat_add(gendiskp, field, -1, sector) - -#define __all_stat_inc(gendiskp, part, field, sector) \ - __all_stat_add(gendiskp, part, field, 1, sector) -#define all_stat_inc(gendiskp, part, field, sector) \ - all_stat_add(gendiskp, part, field, 1, sector) - -#define __all_stat_sub(gendiskp, part, field, subnd, sector) \ - __all_stat_add(gendiskp, part, field, -subnd, sector) -#define all_stat_sub(gendiskp, part, field, subnd, sector) \ - all_stat_add(gendiskp, part, field, -subnd, sector) +#define disk_stat_dec(cpu, gendiskp, field) \ + disk_stat_add(cpu, gendiskp, field, -1) +#define disk_stat_inc(cpu, gendiskp, field) \ + disk_stat_add(cpu, gendiskp, field, 1) +#define disk_stat_sub(cpu, gendiskp, field, subnd) \ + disk_stat_add(cpu, gendiskp, field, -subnd) + +#define part_stat_dec(cpu, gendiskp, field) \ + part_stat_add(cpu, gendiskp, field, -1) +#define part_stat_inc(cpu, gendiskp, field) \ + part_stat_add(cpu, gendiskp, field, 1) +#define part_stat_sub(cpu, gendiskp, field, subnd) \ + part_stat_add(cpu, gendiskp, field, -subnd) + +#define all_stat_dec(cpu, gendiskp, field, sector) \ + all_stat_add(cpu, gendiskp, field, -1, sector) +#define all_stat_inc(cpu, gendiskp, part, field, sector) \ + all_stat_add(cpu, gendiskp, part, field, 1, sector) +#define all_stat_sub(cpu, gendiskp, part, field, subnd, sector) \ + all_stat_add(cpu, gendiskp, part, field, -subnd, sector) /* Inlines to alloc and free disk stats in struct gendisk */ #ifdef CONFIG_SMP @@ -401,8 +376,8 @@ static inline void free_part_stats(struct hd_struct *part) #endif /* CONFIG_SMP */ /* drivers/block/ll_rw_blk.c */ -extern void disk_round_stats(struct gendisk *disk); -extern void part_round_stats(struct hd_struct *part); +extern void disk_round_stats(int cpu, struct gendisk *disk); +extern void part_round_stats(int cpu, struct hd_struct *part); /* drivers/block/genhd.c */ extern int get_blkdev_list(char *, int); -- cgit v1.2.3 From bcce3de1be61e424deef35d1e86e86a35c4b6e65 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:47:22 +0900 Subject: block: implement extended dev numbers Implement extended device numbers. A block driver can tell block layer that it wants to use extended device numbers. After the usual minor space is used up, block layer automatically allocates devt's from EXT_BLOCK_MAJOR. Currently only one major number is allocated for this but as the allocation is strictly on-demand, ~1mil minor space under it should suffice unless the system actually has more than ~1mil partitions and if that ever happens adding more majors to the extended devt area is easy. Due to internal implementation issues, the first partition can't be allocated on the extended area. In other words, genhd->minors should at least be 1. This limitation will be lifted by later changes. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++--- fs/partitions/check.c | 9 +++- include/linux/genhd.h | 13 ++++-- include/linux/major.h | 2 + 4 files changed, 135 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index 430626e440f0..7bbfed05cecb 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "blk.h" @@ -24,6 +25,15 @@ static DEFINE_MUTEX(block_class_lock); struct kobject *block_depr; #endif +/* for extended dynamic devt allocation, currently only one major is used */ +#define MAX_EXT_DEVT (1 << MINORBITS) + +/* For extended devt allocation. ext_devt_mutex prevents look up + * results from going away underneath its user. + */ +static DEFINE_MUTEX(ext_devt_mutex); +static DEFINE_IDR(ext_devt_idr); + static struct device_type disk_type; /** @@ -288,6 +298,74 @@ EXPORT_SYMBOL(unregister_blkdev); static struct kobj_map *bdev_map; +/** + * blk_alloc_devt - allocate a dev_t for a partition + * @part: partition to allocate dev_t for + * @gfp_mask: memory allocation flag + * @devt: out parameter for resulting dev_t + * + * Allocate a dev_t for block device. + * + * RETURNS: + * 0 on success, allocated dev_t is returned in *@devt. -errno on + * failure. + * + * CONTEXT: + * Might sleep. + */ +int blk_alloc_devt(struct hd_struct *part, dev_t *devt) +{ + struct gendisk *disk = part_to_disk(part); + int idx, rc; + + /* in consecutive minor range? */ + if (part->partno < disk->minors) { + *devt = MKDEV(disk->major, disk->first_minor + part->partno); + return 0; + } + + /* allocate ext devt */ + do { + if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL)) + return -ENOMEM; + rc = idr_get_new(&ext_devt_idr, part, &idx); + } while (rc == -EAGAIN); + + if (rc) + return rc; + + if (idx > MAX_EXT_DEVT) { + idr_remove(&ext_devt_idr, idx); + return -EBUSY; + } + + *devt = MKDEV(BLOCK_EXT_MAJOR, idx); + return 0; +} + +/** + * blk_free_devt - free a dev_t + * @devt: dev_t to free + * + * Free @devt which was allocated using blk_alloc_devt(). + * + * CONTEXT: + * Might sleep. + */ +void blk_free_devt(dev_t devt) +{ + might_sleep(); + + if (devt == MKDEV(0, 0)) + return; + + if (MAJOR(devt) == BLOCK_EXT_MAJOR) { + mutex_lock(&ext_devt_mutex); + idr_remove(&ext_devt_idr, MINOR(devt)); + mutex_unlock(&ext_devt_mutex); + } +} + /* * Register device numbers dev..(dev+range-1) * range must be nonzero @@ -371,10 +449,27 @@ void unlink_gendisk(struct gendisk *disk) */ struct gendisk *get_gendisk(dev_t devt, int *partno) { - struct kobject *kobj = kobj_lookup(bdev_map, devt, partno); - struct device *dev = kobj_to_dev(kobj); + struct gendisk *disk = NULL; + + if (MAJOR(devt) != BLOCK_EXT_MAJOR) { + struct kobject *kobj; + + kobj = kobj_lookup(bdev_map, devt, partno); + if (kobj) + disk = dev_to_disk(kobj_to_dev(kobj)); + } else { + struct hd_struct *part; + + mutex_lock(&ext_devt_mutex); + part = idr_find(&ext_devt_idr, MINOR(devt)); + if (part && get_disk(part_to_disk(part))) { + *partno = part->partno; + disk = part_to_disk(part); + } + mutex_unlock(&ext_devt_mutex); + } - return kobj ? dev_to_disk(dev) : NULL; + return disk; } /** @@ -877,18 +972,30 @@ struct gendisk *alloc_disk(int minors) } struct gendisk *alloc_disk_node(int minors, int node_id) +{ + return alloc_disk_ext_node(minors, 0, node_id); +} + +struct gendisk *alloc_disk_ext(int minors, int ext_minors) +{ + return alloc_disk_ext_node(minors, ext_minors, -1); +} + +struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) { struct gendisk *disk; disk = kmalloc_node(sizeof(struct gendisk), GFP_KERNEL | __GFP_ZERO, node_id); if (disk) { + int tot_minors = minors + ext_minors; + if (!init_disk_stats(disk)) { kfree(disk); return NULL; } - if (minors > 1) { - int size = (minors - 1) * sizeof(struct hd_struct *); + if (tot_minors > 1) { + int size = (tot_minors - 1) * sizeof(struct hd_struct *); disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, node_id); if (!disk->__part) { @@ -898,6 +1005,7 @@ struct gendisk *alloc_disk_node(int minors, int node_id) } } disk->minors = minors; + disk->ext_minors = ext_minors; rand_initialize_disk(disk); disk->dev.class = &block_class; disk->dev.type = &disk_type; @@ -910,6 +1018,8 @@ struct gendisk *alloc_disk_node(int minors, int node_id) EXPORT_SYMBOL(alloc_disk); EXPORT_SYMBOL(alloc_disk_node); +EXPORT_SYMBOL(alloc_disk_ext); +EXPORT_SYMBOL(alloc_disk_ext_node); struct kobject *get_disk(struct gendisk *disk) { diff --git a/fs/partitions/check.c b/fs/partitions/check.c index c442f0aadac3..0d4b7f28f13f 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -333,6 +333,7 @@ void delete_partition(struct gendisk *disk, int partno) if (!part) return; + blk_free_devt(part_devt(part)); rcu_assign_pointer(disk->__part[partno-1], NULL); kobject_put(part->holder_dir); device_del(&part->dev); @@ -352,6 +353,7 @@ int add_partition(struct gendisk *disk, int partno, sector_t start, sector_t len, int flags) { struct hd_struct *p; + dev_t devt = MKDEV(0, 0); int err; if (disk->__part[partno - 1]) @@ -378,11 +380,15 @@ int add_partition(struct gendisk *disk, int partno, "%s%d", disk->dev.bus_id, partno); device_initialize(&p->dev); - p->dev.devt = MKDEV(disk->major, disk->first_minor + partno); p->dev.class = &block_class; p->dev.type = &part_type; p->dev.parent = &disk->dev; + err = blk_alloc_devt(p, &devt); + if (err) + goto out_put; + p->dev.devt = devt; + /* delay uevent until 'holders' subdir is created */ p->dev.uevent_suppress = 1; err = device_add(&p->dev); @@ -419,6 +425,7 @@ out_del: device_del(&p->dev); out_put: put_device(&p->dev); + blk_free_devt(devt); return err; } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ac8a901f2002..6fc532424062 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -113,13 +113,15 @@ struct hd_struct { #define GENHD_FL_FAIL 64 struct gendisk { - /* major, first_minor and minors are input parameters only, - * don't use directly. Use disk_devt() and disk_max_parts(). + /* major, first_minor, minors and ext_minors are input + * parameters only, don't use directly. Use disk_devt() and + * disk_max_parts(). */ int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ + int ext_minors; /* number of extended dynamic minors */ char disk_name[32]; /* name of major driver */ @@ -167,7 +169,7 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part) static inline int disk_max_parts(struct gendisk *disk) { - return disk->minors - 1; + return disk->minors + disk->ext_minors - 1; } static inline dev_t disk_devt(struct gendisk *disk) @@ -554,6 +556,8 @@ struct unixware_disklabel { #define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_WHOLEDISK 2 +extern int blk_alloc_devt(struct hd_struct *part, dev_t *devt); +extern void blk_free_devt(dev_t devt); extern dev_t blk_lookup_devt(const char *name, int partno); extern char *disk_name (struct gendisk *hd, int partno, char *buf); @@ -564,6 +568,9 @@ extern void printk_all_partitions(void); extern struct gendisk *alloc_disk_node(int minors, int node_id); extern struct gendisk *alloc_disk(int minors); +extern struct gendisk *alloc_disk_ext_node(int minors, int ext_minrs, + int node_id); +extern struct gendisk *alloc_disk_ext(int minors, int ext_minors); extern struct kobject *get_disk(struct gendisk *disk); extern void put_disk(struct gendisk *disk); extern void blk_register_region(dev_t devt, unsigned long range, diff --git a/include/linux/major.h b/include/linux/major.h index 53d5fafd85c3..88249452b935 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -170,4 +170,6 @@ #define VIOTAPE_MAJOR 230 +#define BLOCK_EXT_MAJOR 259 + #endif -- cgit v1.2.3 From 1f0142905d4812966831613847db38a66da29eb8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:47:23 +0900 Subject: block: adjust formatting for large minors and add ext_range sysfs attr With extended minors and the soon-to-follow debug feature, large minor numbers for block devices will be common. This patch does the followings to make printouts pretty. * Adapt print formats such that large minors don't break the formatting. * For extended MAJ:MIN, %02x%02x for MAJ:MIN used in printk_all_partitions() doesn't cut it anymore. Update it such that %03x:%05x is used if either MAJ or MIN doesn't fit in %02x. * Implement ext_range sysfs attribute which shows total minors the device can use including both conventional minor space and the extended one. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 45 ++++++++++++++++++++++++++++++++++----------- include/linux/fs.h | 1 + 2 files changed, 35 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index 7bbfed05cecb..ee4b13520e59 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -366,6 +366,18 @@ void blk_free_devt(dev_t devt) } } +static char *bdevt_str(dev_t devt, char *buf) +{ + if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) { + char tbuf[BDEVT_SIZE]; + snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt)); + snprintf(buf, BDEVT_SIZE, "%-9s", tbuf); + } else + snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt)); + + return buf; +} + /* * Register device numbers dev..(dev+range-1) * range must be nonzero @@ -521,7 +533,8 @@ void __init printk_all_partitions(void) struct gendisk *disk = dev_to_disk(dev); struct disk_part_iter piter; struct hd_struct *part; - char buf[BDEVNAME_SIZE]; + char name_buf[BDEVNAME_SIZE]; + char devt_buf[BDEVT_SIZE]; /* * Don't show empty devices or things that have been @@ -536,10 +549,10 @@ void __init printk_all_partitions(void) * numbers in hex - the same format as the root= * option takes. */ - printk("%02x%02x %10llu %s", - MAJOR(disk_devt(disk)), MINOR(disk_devt(disk)), + printk("%s %10llu %s", + bdevt_str(disk_devt(disk), devt_buf), (unsigned long long)get_capacity(disk) >> 1, - disk_name(disk, 0, buf)); + disk_name(disk, 0, name_buf)); if (disk->driverfs_dev != NULL && disk->driverfs_dev->driver != NULL) printk(" driver: %s\n", @@ -550,10 +563,10 @@ void __init printk_all_partitions(void) /* now show the partitions */ disk_part_iter_init(&piter, disk, 0); while ((part = disk_part_iter_next(&piter))) - printk(" %02x%02x %10llu %s\n", - MAJOR(part_devt(part)), MINOR(part_devt(part)), + printk(" %s %10llu %s\n", + bdevt_str(part_devt(part), devt_buf), (unsigned long long)part->nr_sects >> 1, - disk_name(disk, part->partno, buf)); + disk_name(disk, part->partno, name_buf)); disk_part_iter_exit(&piter); } class_dev_iter_exit(&iter); @@ -630,14 +643,14 @@ static int show_partition(struct seq_file *seqf, void *v) return 0; /* show the full disk and all non-0 size partitions of it */ - seq_printf(seqf, "%4d %4d %10llu %s\n", + seq_printf(seqf, "%4d %7d %10llu %s\n", MAJOR(disk_devt(sgp)), MINOR(disk_devt(sgp)), (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); disk_part_iter_init(&piter, sgp, 0); while ((part = disk_part_iter_next(&piter))) - seq_printf(seqf, "%4d %4d %10llu %s\n", + seq_printf(seqf, "%4d %7d %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), (unsigned long long)part->nr_sects >> 1, disk_name(sgp, part->partno, buf)); @@ -691,6 +704,14 @@ static ssize_t disk_range_show(struct device *dev, return sprintf(buf, "%d\n", disk->minors); } +static ssize_t disk_ext_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + + return sprintf(buf, "%d\n", disk_max_parts(disk) + 1); +} + static ssize_t disk_removable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -780,6 +801,7 @@ static ssize_t disk_fail_store(struct device *dev, #endif static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); +static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL); @@ -792,6 +814,7 @@ static struct device_attribute dev_attr_fail = static struct attribute *disk_attrs[] = { &dev_attr_range.attr, + &dev_attr_ext_range.attr, &dev_attr_removable.attr, &dev_attr_ro.attr, &dev_attr_size.attr, @@ -858,7 +881,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) cpu = disk_stat_lock(); disk_round_stats(cpu, gp); disk_stat_unlock(); - seq_printf(seqf, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", + seq_printf(seqf, "%4d %7d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", MAJOR(disk_devt(gp)), MINOR(disk_devt(gp)), disk_name(gp, 0, buf), disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), @@ -877,7 +900,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) cpu = disk_stat_lock(); part_round_stats(cpu, hd); disk_stat_unlock(); - seq_printf(seqf, "%4d %4d %s %lu %lu %llu " + seq_printf(seqf, "%4d %7d %s %lu %lu %llu " "%u %lu %lu %llu %u %u %u %u\n", MAJOR(part_devt(hd)), MINOR(part_devt(hd)), disk_name(gp, hd->partno, buf), diff --git a/include/linux/fs.h b/include/linux/fs.h index 860689f541b1..02a9fb5a830c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1685,6 +1685,7 @@ extern void chrdev_show(struct seq_file *,off_t); /* fs/block_dev.c */ #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ +#define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */ #ifdef CONFIG_BLOCK #define BLKDEV_MAJOR_HASH_SIZE 255 -- cgit v1.2.3 From ed9e1982347b36573cd622ee5f4e2a7ccd79b3fd Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:05 +0900 Subject: block: implement and use {disk|part}_to_dev() Implement {disk|part}_to_dev() and use them to access generic device instead of directly dereferencing {disk|part}->dev. To make sure no user is left behind, rename generic devices fields to __dev. This is in preparation of unifying partition 0 handling with other partitions. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-integrity.c | 5 +-- block/blk-sysfs.c | 4 +-- block/genhd.c | 27 ++++++++-------- drivers/block/aoe/aoeblk.c | 4 +-- drivers/block/nbd.c | 4 +-- drivers/ide/ide-probe.c | 2 +- drivers/md/dm.c | 4 +-- drivers/md/md.c | 10 +++--- fs/block_dev.c | 4 +-- fs/partitions/check.c | 79 ++++++++++++++++++++++++---------------------- include/linux/genhd.h | 20 ++++++------ 11 files changed, 86 insertions(+), 77 deletions(-) (limited to 'include/linux') diff --git a/block/blk-integrity.c b/block/blk-integrity.c index d87606eaca1d..69023da63151 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -331,7 +331,8 @@ int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template) return -1; if (kobject_init_and_add(&bi->kobj, &integrity_ktype, - &disk->dev.kobj, "%s", "integrity")) { + &disk_to_dev(disk)->kobj, + "%s", "integrity")) { kmem_cache_free(integrity_cachep, bi); return -1; } @@ -375,7 +376,7 @@ void blk_integrity_unregister(struct gendisk *disk) kobject_uevent(&bi->kobj, KOBJ_REMOVE); kobject_del(&bi->kobj); - kobject_put(&disk->dev.kobj); + kobject_put(&disk_to_dev(disk)->kobj); kmem_cache_free(integrity_cachep, bi); } EXPORT_SYMBOL(blk_integrity_unregister); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 304ec73ab821..b9a6ed166649 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -310,7 +310,7 @@ int blk_register_queue(struct gendisk *disk) if (!q->request_fn) return 0; - ret = kobject_add(&q->kobj, kobject_get(&disk->dev.kobj), + ret = kobject_add(&q->kobj, kobject_get(&disk_to_dev(disk)->kobj), "%s", "queue"); if (ret < 0) return ret; @@ -339,6 +339,6 @@ void blk_unregister_queue(struct gendisk *disk) kobject_uevent(&q->kobj, KOBJ_REMOVE); kobject_del(&q->kobj); - kobject_put(&disk->dev.kobj); + kobject_put(&disk_to_dev(disk)->kobj); } } diff --git a/block/genhd.c b/block/genhd.c index 67e5a59ced2a..0a2f16bd54b7 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -59,7 +59,7 @@ struct hd_struct *disk_get_part(struct gendisk *disk, int partno) rcu_read_lock(); part = rcu_dereference(disk->__part[partno - 1]); if (part) - get_device(&part->dev); + get_device(part_to_dev(part)); rcu_read_unlock(); return part; @@ -130,7 +130,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects) continue; - get_device(&part->dev); + get_device(part_to_dev(part)); piter->part = part; piter->idx += inc; break; @@ -435,7 +435,7 @@ static struct kobject *exact_match(dev_t devt, int *partno, void *data) { struct gendisk *p = data; - return &p->dev.kobj; + return &disk_to_dev(p)->kobj; } static int exact_lock(dev_t devt, void *data) @@ -460,7 +460,7 @@ void add_disk(struct gendisk *disk) int retval; disk->flags |= GENHD_FL_UP; - disk->dev.devt = MKDEV(disk->major, disk->first_minor); + disk_to_dev(disk)->devt = MKDEV(disk->major, disk->first_minor); blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); @@ -468,7 +468,8 @@ void add_disk(struct gendisk *disk) bdi = &disk->queue->backing_dev_info; bdi_register_dev(bdi, disk_devt(disk)); - retval = sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi"); + retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, + "bdi"); WARN_ON(retval); } @@ -477,7 +478,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ void unlink_gendisk(struct gendisk *disk) { - sysfs_remove_link(&disk->dev.kobj, "bdi"); + sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); blk_unregister_region(disk_devt(disk), disk->minors); @@ -903,7 +904,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) int cpu; /* - if (&gp->dev.kobj.entry == block_class.devices.next) + if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next) seq_puts(seqf, "major minor name" " rio rmerge rsect ruse wio wmerge " "wsect wuse running use aveq" @@ -972,7 +973,7 @@ static void media_change_notify_thread(struct work_struct *work) * set enviroment vars to indicate which event this is for * so that user space will know to go check the media status. */ - kobject_uevent_env(&gd->dev.kobj, KOBJ_CHANGE, envp); + kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); put_device(gd->driverfs_dev); } @@ -1062,9 +1063,9 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) disk->minors = minors; disk->ext_minors = ext_minors; rand_initialize_disk(disk); - disk->dev.class = &block_class; - disk->dev.type = &disk_type; - device_initialize(&disk->dev); + disk_to_dev(disk)->class = &block_class; + disk_to_dev(disk)->type = &disk_type; + device_initialize(disk_to_dev(disk)); INIT_WORK(&disk->async_notify, media_change_notify_thread); } @@ -1086,7 +1087,7 @@ struct kobject *get_disk(struct gendisk *disk) owner = disk->fops->owner; if (owner && !try_module_get(owner)) return NULL; - kobj = kobject_get(&disk->dev.kobj); + kobj = kobject_get(&disk_to_dev(disk)->kobj); if (kobj == NULL) { module_put(owner); return NULL; @@ -1100,7 +1101,7 @@ EXPORT_SYMBOL(get_disk); void put_disk(struct gendisk *disk) { if (disk) - kobject_put(&disk->dev.kobj); + kobject_put(&disk_to_dev(disk)->kobj); } EXPORT_SYMBOL(put_disk); diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 0c39782b2660..3edb6cb7d68f 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -109,12 +109,12 @@ static const struct attribute_group attr_group = { static int aoedisk_add_sysfs(struct aoedev *d) { - return sysfs_create_group(&d->gd->dev.kobj, &attr_group); + return sysfs_create_group(&disk_to_dev(d->gd)->kobj, &attr_group); } void aoedisk_rm_sysfs(struct aoedev *d) { - sysfs_remove_group(&d->gd->dev.kobj, &attr_group); + sysfs_remove_group(&disk_to_dev(d->gd)->kobj, &attr_group); } static int diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 1778e4a2c672..7b3351260d56 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -403,7 +403,7 @@ static int nbd_do_it(struct nbd_device *lo) BUG_ON(lo->magic != LO_MAGIC); lo->pid = current->pid; - ret = sysfs_create_file(&lo->disk->dev.kobj, &pid_attr.attr); + ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); if (ret) { printk(KERN_ERR "nbd: sysfs_create_file failed!"); return ret; @@ -412,7 +412,7 @@ static int nbd_do_it(struct nbd_device *lo) while ((req = nbd_read_stat(lo)) != NULL) nbd_end_request(req); - sysfs_remove_file(&lo->disk->dev.kobj, &pid_attr.attr); + sysfs_remove_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); return 0; } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index a51a30e9eab3..70aa86c8807e 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1188,7 +1188,7 @@ static struct kobject *exact_match(dev_t dev, int *part, void *data) { struct gendisk *p = data; *part &= (1 << PARTN_BITS) - 1; - return &p->dev.kobj; + return &disk_to_dev(p)->kobj; } static int exact_lock(dev_t dev, void *data) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 653624792eaf..637806695bb9 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1186,7 +1186,7 @@ static void event_callback(void *context) list_splice_init(&md->uevent_list, &uevents); spin_unlock_irqrestore(&md->uevent_lock, flags); - dm_send_uevents(&uevents, &md->disk->dev.kobj); + dm_send_uevents(&uevents, &disk_to_dev(md->disk)->kobj); atomic_inc(&md->event_nr); wake_up(&md->eventq); @@ -1643,7 +1643,7 @@ out: *---------------------------------------------------------------*/ void dm_kobject_uevent(struct mapped_device *md) { - kobject_uevent(&md->disk->dev.kobj, KOBJ_CHANGE); + kobject_uevent(&disk_to_dev(md->disk)->kobj, KOBJ_CHANGE); } uint32_t dm_next_uevent_seq(struct mapped_device *md) diff --git a/drivers/md/md.c b/drivers/md/md.c index deeac4b44173..96e9fccd2eab 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1465,9 +1465,9 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) goto fail; if (rdev->bdev->bd_part) - ko = &rdev->bdev->bd_part->dev.kobj; + ko = &part_to_dev(rdev->bdev->bd_part)->kobj; else - ko = &rdev->bdev->bd_disk->dev.kobj; + ko = &disk_to_dev(rdev->bdev->bd_disk)->kobj; if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) { kobject_del(&rdev->kobj); goto fail; @@ -3470,8 +3470,8 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) disk->queue = mddev->queue; add_disk(disk); mddev->gendisk = disk; - error = kobject_init_and_add(&mddev->kobj, &md_ktype, &disk->dev.kobj, - "%s", "md"); + error = kobject_init_and_add(&mddev->kobj, &md_ktype, + &disk_to_dev(disk)->kobj, "%s", "md"); mutex_unlock(&disks_mutex); if (error) printk(KERN_WARNING "md: cannot register %s/md - name in use\n", @@ -3761,7 +3761,7 @@ static int do_md_run(mddev_t * mddev) sysfs_notify(&mddev->kobj, NULL, "array_state"); sysfs_notify(&mddev->kobj, NULL, "sync_action"); sysfs_notify(&mddev->kobj, NULL, "degraded"); - kobject_uevent(&mddev->gendisk->dev.kobj, KOBJ_CHANGE); + kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); return 0; } diff --git a/fs/block_dev.c b/fs/block_dev.c index 2f2873b9a041..a02df22f37c3 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -543,9 +543,9 @@ EXPORT_SYMBOL(bd_release); static struct kobject *bdev_get_kobj(struct block_device *bdev) { if (bdev->bd_contains != bdev) - return kobject_get(&bdev->bd_part->dev.kobj); + return kobject_get(&part_to_dev(bdev->bd_part)->kobj); else - return kobject_get(&bdev->bd_disk->dev.kobj); + return kobject_get(&disk_to_dev(bdev->bd_disk)->kobj); } static struct kobject *bdev_get_holder(struct block_device *bdev) diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 0d4b7f28f13f..ac0df3acdcda 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -309,7 +309,7 @@ static inline void disk_sysfs_add_subdirs(struct gendisk *disk) { struct kobject *k; - k = kobject_get(&disk->dev.kobj); + k = kobject_get(&disk_to_dev(disk)->kobj); disk->holder_dir = kobject_create_and_add("holders", k); disk->slave_dir = kobject_create_and_add("slaves", k); kobject_put(k); @@ -322,7 +322,7 @@ static void delete_partition_rcu_cb(struct rcu_head *head) part->start_sect = 0; part->nr_sects = 0; part_stat_set_all(part, 0); - put_device(&part->dev); + put_device(part_to_dev(part)); } void delete_partition(struct gendisk *disk, int partno) @@ -336,7 +336,7 @@ void delete_partition(struct gendisk *disk, int partno) blk_free_devt(part_devt(part)); rcu_assign_pointer(disk->__part[partno-1], NULL); kobject_put(part->holder_dir); - device_del(&part->dev); + device_del(part_to_dev(part)); call_rcu(&part->rcu_head, delete_partition_rcu_cb); } @@ -354,6 +354,9 @@ int add_partition(struct gendisk *disk, int partno, { struct hd_struct *p; dev_t devt = MKDEV(0, 0); + struct device *ddev = disk_to_dev(disk); + struct device *pdev; + const char *dname; int err; if (disk->__part[partno - 1]) @@ -367,42 +370,43 @@ int add_partition(struct gendisk *disk, int partno, err = -ENOMEM; goto out_free; } + pdev = part_to_dev(p); + p->start_sect = start; p->nr_sects = len; p->partno = partno; p->policy = disk->policy; - if (isdigit(disk->dev.bus_id[strlen(disk->dev.bus_id)-1])) - snprintf(p->dev.bus_id, BUS_ID_SIZE, - "%sp%d", disk->dev.bus_id, partno); + dname = dev_name(ddev); + if (isdigit(dname[strlen(dname) - 1])) + snprintf(pdev->bus_id, BUS_ID_SIZE, "%sp%d", dname, partno); else - snprintf(p->dev.bus_id, BUS_ID_SIZE, - "%s%d", disk->dev.bus_id, partno); + snprintf(pdev->bus_id, BUS_ID_SIZE, "%s%d", dname, partno); - device_initialize(&p->dev); - p->dev.class = &block_class; - p->dev.type = &part_type; - p->dev.parent = &disk->dev; + device_initialize(pdev); + pdev->class = &block_class; + pdev->type = &part_type; + pdev->parent = ddev; err = blk_alloc_devt(p, &devt); if (err) - goto out_put; - p->dev.devt = devt; + goto out_free; + pdev->devt = devt; /* delay uevent until 'holders' subdir is created */ - p->dev.uevent_suppress = 1; - err = device_add(&p->dev); + pdev->uevent_suppress = 1; + err = device_add(pdev); if (err) goto out_put; err = -ENOMEM; - p->holder_dir = kobject_create_and_add("holders", &p->dev.kobj); + p->holder_dir = kobject_create_and_add("holders", &pdev->kobj); if (!p->holder_dir) goto out_del; - p->dev.uevent_suppress = 0; + pdev->uevent_suppress = 0; if (flags & ADDPART_FLAG_WHOLEDISK) { - err = device_create_file(&p->dev, &dev_attr_whole_disk); + err = device_create_file(pdev, &dev_attr_whole_disk); if (err) goto out_del; } @@ -412,8 +416,8 @@ int add_partition(struct gendisk *disk, int partno, rcu_assign_pointer(disk->__part[partno - 1], p); /* suppress uevent if the disk supresses it */ - if (!disk->dev.uevent_suppress) - kobject_uevent(&p->dev.kobj, KOBJ_ADD); + if (!ddev->uevent_suppress) + kobject_uevent(&pdev->kobj, KOBJ_ADD); return 0; @@ -422,9 +426,9 @@ out_free: return err; out_del: kobject_put(p->holder_dir); - device_del(&p->dev); + device_del(pdev); out_put: - put_device(&p->dev); + put_device(pdev); blk_free_devt(devt); return err; } @@ -432,30 +436,31 @@ out_put: /* Not exported, helper to add_disk(). */ void register_disk(struct gendisk *disk) { + struct device *ddev = disk_to_dev(disk); struct block_device *bdev; struct disk_part_iter piter; struct hd_struct *part; char *s; int err; - disk->dev.parent = disk->driverfs_dev; + ddev->parent = disk->driverfs_dev; - strlcpy(disk->dev.bus_id, disk->disk_name, BUS_ID_SIZE); + strlcpy(ddev->bus_id, disk->disk_name, BUS_ID_SIZE); /* ewww... some of these buggers have / in the name... */ - s = strchr(disk->dev.bus_id, '/'); + s = strchr(ddev->bus_id, '/'); if (s) *s = '!'; /* delay uevents, until we scanned partition table */ - disk->dev.uevent_suppress = 1; + ddev->uevent_suppress = 1; - if (device_add(&disk->dev)) + if (device_add(ddev)) return; #ifndef CONFIG_SYSFS_DEPRECATED - err = sysfs_create_link(block_depr, &disk->dev.kobj, - kobject_name(&disk->dev.kobj)); + err = sysfs_create_link(block_depr, &ddev->kobj, + kobject_name(&ddev->kobj)); if (err) { - device_del(&disk->dev); + device_del(ddev); return; } #endif @@ -481,13 +486,13 @@ void register_disk(struct gendisk *disk) exit: /* announce disk after possible partitions are created */ - disk->dev.uevent_suppress = 0; - kobject_uevent(&disk->dev.kobj, KOBJ_ADD); + ddev->uevent_suppress = 0; + kobject_uevent(&ddev->kobj, KOBJ_ADD); /* announce possible partitions */ disk_part_iter_init(&piter, disk, 0); while ((part = disk_part_iter_next(&piter))) - kobject_uevent(&part->dev.kobj, KOBJ_ADD); + kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD); disk_part_iter_exit(&piter); } @@ -518,7 +523,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) return -EIO; /* tell userspace that the media / partition table may have changed */ - kobject_uevent(&disk->dev.kobj, KOBJ_CHANGE); + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); for (p = 1; p < state->limit; p++) { sector_t size = state->parts[p].size; @@ -591,7 +596,7 @@ void del_gendisk(struct gendisk *disk) kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; #ifndef CONFIG_SYSFS_DEPRECATED - sysfs_remove_link(block_depr, disk->dev.bus_id); + sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); #endif - device_del(&disk->dev); + device_del(disk_to_dev(disk)); } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 6fc532424062..e4e18c509ac5 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -15,9 +15,11 @@ #ifdef CONFIG_BLOCK -#define kobj_to_dev(k) container_of(k, struct device, kobj) -#define dev_to_disk(device) container_of(device, struct gendisk, dev) -#define dev_to_part(device) container_of(device, struct hd_struct, dev) +#define kobj_to_dev(k) container_of((k), struct device, kobj) +#define dev_to_disk(device) container_of((device), struct gendisk, __dev) +#define dev_to_part(device) container_of((device), struct hd_struct, __dev) +#define disk_to_dev(disk) (&((disk)->__dev)) +#define part_to_dev(part) (&((part)->__dev)) extern struct device_type part_type; extern struct kobject *block_depr; @@ -88,7 +90,7 @@ struct disk_stats { struct hd_struct { sector_t start_sect; sector_t nr_sects; - struct device dev; + struct device __dev; struct kobject *holder_dir; int policy, partno; #ifdef CONFIG_FAIL_MAKE_REQUEST @@ -139,7 +141,7 @@ struct gendisk { int flags; struct device *driverfs_dev; // FIXME: remove - struct device dev; + struct device __dev; struct kobject *holder_dir; struct kobject *slave_dir; @@ -163,7 +165,7 @@ struct gendisk { static inline struct gendisk *part_to_disk(struct hd_struct *part) { if (likely(part)) - return dev_to_disk((part)->dev.parent); + return dev_to_disk(part_to_dev(part)->parent); return NULL; } @@ -174,12 +176,12 @@ static inline int disk_max_parts(struct gendisk *disk) static inline dev_t disk_devt(struct gendisk *disk) { - return disk->dev.devt; + return disk_to_dev(disk)->devt; } static inline dev_t part_devt(struct hd_struct *part) { - return part->dev.devt; + return part_to_dev(part)->devt; } extern struct hd_struct *disk_get_part(struct gendisk *disk, int partno); @@ -187,7 +189,7 @@ extern struct hd_struct *disk_get_part(struct gendisk *disk, int partno); static inline void disk_put_part(struct hd_struct *part) { if (likely(part)) - put_device(&part->dev); + put_device(part_to_dev(part)); } /* -- cgit v1.2.3 From b5d0b9df0ba5d9a044f3a21e7544f53d90bd1465 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Sep 2008 09:06:42 +0200 Subject: block: introduce partition 0 genhd and partition code handled disk and partitions separately. All information about the whole disk was in struct genhd and partitions in struct hd_struct. However, the whole disk (part0) and other partitions have a lot in common and the data structures end up having good number of common fields and thus separate code paths doing the same thing. Also, the partition array was indexed by partno - 1 which gets pretty confusing at times. This patch introduces partition 0 and makes the partition array indexed by partno. Following patches will unify the handling of disk and parts piece-by-piece. This patch also implements disk_partitionable() which tests whether a disk is partitionable. With coming dynamic partition array change, the most common usage of disk_max_parts() will be testing whether a disk is partitionable and the number of max partitions will become much less important. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 40 +++++++++++++++++++++++----------------- block/ioctl.c | 4 ++-- fs/block_dev.c | 2 +- fs/partitions/check.c | 12 ++++++------ include/linux/genhd.h | 11 +++++++++-- 5 files changed, 41 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index 0a2f16bd54b7..65b7386c26d8 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -54,10 +54,10 @@ struct hd_struct *disk_get_part(struct gendisk *disk, int partno) { struct hd_struct *part; - if (unlikely(partno < 1 || partno > disk_max_parts(disk))) + if (unlikely(partno < 0 || partno >= disk_max_parts(disk))) return NULL; rcu_read_lock(); - part = rcu_dereference(disk->__part[partno - 1]); + part = rcu_dereference(disk->__part[partno]); if (part) get_device(part_to_dev(part)); rcu_read_unlock(); @@ -85,8 +85,10 @@ void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, if (flags & DISK_PITER_REVERSE) piter->idx = disk_max_parts(piter->disk) - 1; - else + else if (flags & DISK_PITER_INCL_PART0) piter->idx = 0; + else + piter->idx = 1; piter->flags = flags; } @@ -114,7 +116,10 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) /* determine iteration parameters */ if (piter->flags & DISK_PITER_REVERSE) { inc = -1; - end = -1; + if (piter->flags & DISK_PITER_INCL_PART0) + end = -1; + else + end = 0; } else { inc = 1; end = disk_max_parts(piter->disk); @@ -177,7 +182,7 @@ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) { int i; - for (i = 0; i < disk_max_parts(disk); i++) { + for (i = 1; i < disk_max_parts(disk); i++) { struct hd_struct *part = rcu_dereference(disk->__part[i]); if (part && part->start_sect <= sector && @@ -669,7 +674,7 @@ static int show_partition(struct seq_file *seqf, void *v) char buf[BDEVNAME_SIZE]; /* Don't show non-partitionable removeable devices or empty devices */ - if (!get_capacity(sgp) || (!disk_max_parts(sgp) && + if (!get_capacity(sgp) || (!disk_partitionable(sgp) && (sgp->flags & GENHD_FL_REMOVABLE))) return 0; if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) @@ -742,7 +747,7 @@ static ssize_t disk_ext_range_show(struct device *dev, { struct gendisk *disk = dev_to_disk(dev); - return sprintf(buf, "%d\n", disk_max_parts(disk) + 1); + return sprintf(buf, "%d\n", disk_max_parts(disk)); } static ssize_t disk_removable_show(struct device *dev, @@ -998,7 +1003,7 @@ dev_t blk_lookup_devt(const char *name, int partno) if (strcmp(dev->bus_id, name)) continue; - if (partno < 0 || partno > disk_max_parts(disk)) + if (partno < 0 || partno >= disk_max_parts(disk)) continue; if (partno == 0) @@ -1045,21 +1050,22 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) GFP_KERNEL | __GFP_ZERO, node_id); if (disk) { int tot_minors = minors + ext_minors; + int size = tot_minors * sizeof(struct hd_struct *); if (!init_disk_stats(disk)) { kfree(disk); return NULL; } - if (tot_minors > 1) { - int size = (tot_minors - 1) * sizeof(struct hd_struct *); - disk->__part = kmalloc_node(size, - GFP_KERNEL | __GFP_ZERO, node_id); - if (!disk->__part) { - free_disk_stats(disk); - kfree(disk); - return NULL; - } + + disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, + node_id); + if (!disk->__part) { + free_disk_stats(disk); + kfree(disk); + return NULL; } + disk->__part[0] = &disk->part0; + disk->minors = minors; disk->ext_minors = ext_minors; rand_initialize_disk(disk); diff --git a/block/ioctl.c b/block/ioctl.c index a5f672ad55f6..64e7c67a64b0 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -30,7 +30,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user if (bdev != bdev->bd_contains) return -EINVAL; partno = p.pno; - if (partno <= 0 || partno > disk_max_parts(disk)) + if (partno <= 0 || partno >= disk_max_parts(disk)) return -EINVAL; switch (a.op) { case BLKPG_ADD_PARTITION: @@ -102,7 +102,7 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = bdev->bd_disk; int res; - if (!disk_max_parts(disk) || bdev != bdev->bd_contains) + if (!disk_partitionable(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/fs/block_dev.c b/fs/block_dev.c index a02df22f37c3..c982a9107979 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -892,7 +892,7 @@ int check_disk_change(struct block_device *bdev) if (bdops->revalidate_disk) bdops->revalidate_disk(bdev->bd_disk); - if (disk_max_parts(bdev->bd_disk)) + if (disk_partitionable(bdev->bd_disk)) bdev->bd_invalidated = 1; return 1; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index ac0df3acdcda..b60699c271ac 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -173,7 +173,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev) if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); - state->limit = disk_max_parts(hd) + 1; + state->limit = disk_max_parts(hd); i = res = err = 0; while (!res && check_part[i]) { memset(&state->parts, 0, sizeof(state->parts)); @@ -329,12 +329,12 @@ void delete_partition(struct gendisk *disk, int partno) { struct hd_struct *part; - part = disk->__part[partno-1]; + part = disk->__part[partno]; if (!part) return; blk_free_devt(part_devt(part)); - rcu_assign_pointer(disk->__part[partno-1], NULL); + rcu_assign_pointer(disk->__part[partno], NULL); kobject_put(part->holder_dir); device_del(part_to_dev(part)); @@ -359,7 +359,7 @@ int add_partition(struct gendisk *disk, int partno, const char *dname; int err; - if (disk->__part[partno - 1]) + if (disk->__part[partno]) return -EBUSY; p = kzalloc(sizeof(*p), GFP_KERNEL); @@ -413,7 +413,7 @@ int add_partition(struct gendisk *disk, int partno, /* everything is up and running, commence */ INIT_RCU_HEAD(&p->rcu_head); - rcu_assign_pointer(disk->__part[partno - 1], p); + rcu_assign_pointer(disk->__part[partno], p); /* suppress uevent if the disk supresses it */ if (!ddev->uevent_suppress) @@ -467,7 +467,7 @@ void register_disk(struct gendisk *disk) disk_sysfs_add_subdirs(disk); /* No minors to use for partitions */ - if (!disk_max_parts(disk)) + if (!disk_partitionable(disk)) goto exit; /* No such device (e.g., media were just removed) */ diff --git a/include/linux/genhd.h b/include/linux/genhd.h index e4e18c509ac5..9e866a2aee50 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -127,12 +127,13 @@ struct gendisk { char disk_name[32]; /* name of major driver */ - /* Array of pointers to partitions indexed by partno - 1. + /* Array of pointers to partitions indexed by partno. * Protected with matching bdev lock but stat and other * non-critical accesses use RCU. Always access through * helpers. */ struct hd_struct **__part; + struct hd_struct part0; struct block_device_operations *fops; struct request_queue *queue; @@ -171,7 +172,12 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part) static inline int disk_max_parts(struct gendisk *disk) { - return disk->minors + disk->ext_minors - 1; + return disk->minors + disk->ext_minors; +} + +static inline bool disk_partitionable(struct gendisk *disk) +{ + return disk_max_parts(disk) > 1; } static inline dev_t disk_devt(struct gendisk *disk) @@ -197,6 +203,7 @@ static inline void disk_put_part(struct hd_struct *part) */ #define DISK_PITER_REVERSE (1 << 0) /* iterate in the reverse direction */ #define DISK_PITER_INCL_EMPTY (1 << 1) /* include 0-sized parts */ +#define DISK_PITER_INCL_PART0 (1 << 2) /* include partition 0 */ struct disk_part_iter { struct gendisk *disk; -- cgit v1.2.3 From 80795aefb76d10c5d698e60c7e7750b5330787da Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:07 +0900 Subject: block: move capacity from disk to part0 Move disk->capacity to part0->nr_sects and convert all users who directly accessed the field to use {get|set}_capacity(). This is done early to allow the __dev field to be moved. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/block/aoe/aoeblk.c | 2 +- drivers/block/aoe/aoecmd.c | 4 ++-- drivers/block/aoe/aoedev.c | 2 +- fs/partitions/check.c | 2 +- include/linux/genhd.h | 5 ++--- 5 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 3edb6cb7d68f..aa69556c3485 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -276,7 +276,7 @@ aoeblk_gdalloc(void *vp) gd->first_minor = d->sysminor * AOE_PARTITIONS; gd->fops = &aoe_bdops; gd->private_data = d; - gd->capacity = d->ssize; + set_capacity(gd, d->ssize); snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d", d->aoemajor, d->aoeminor); diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 17eed8c025d0..934800f979c9 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -645,7 +645,7 @@ aoecmd_sleepwork(struct work_struct *work) unsigned long flags; u64 ssize; - ssize = d->gd->capacity; + ssize = get_capacity(d->gd); bd = bdget_disk(d->gd, 0); if (bd) { @@ -707,7 +707,7 @@ ataid_complete(struct aoedev *d, struct aoetgt *t, unsigned char *id) if (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE)) return; if (d->gd != NULL) { - d->gd->capacity = ssize; + set_capacity(d->gd, ssize); d->flags |= DEVFL_NEWSIZE; } else d->flags |= DEVFL_GDALLOC; diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index a1d813ab0d6b..6a8038d115b5 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -91,7 +91,7 @@ aoedev_downdev(struct aoedev *d) } if (d->gd) - d->gd->capacity = 0; + set_capacity(d->gd, 0); d->flags &= ~DEVFL_UP; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index b60699c271ac..902b95f1f9d5 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -586,7 +586,7 @@ void del_gendisk(struct gendisk *disk) disk_part_iter_exit(&piter); invalidate_partition(disk, 0); - disk->capacity = 0; + set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); disk_stat_set_all(disk, 0); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 9e866a2aee50..1cf828148ec6 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -138,7 +138,6 @@ struct gendisk { struct block_device_operations *fops; struct request_queue *queue; void *private_data; - sector_t capacity; int flags; struct device *driverfs_dev; // FIXME: remove @@ -411,11 +410,11 @@ static inline sector_t get_start_sect(struct block_device *bdev) } static inline sector_t get_capacity(struct gendisk *disk) { - return disk->capacity; + return disk->part0.nr_sects; } static inline void set_capacity(struct gendisk *disk, sector_t size) { - disk->capacity = size; + disk->part0.nr_sects = size; } #ifdef CONFIG_SOLARIS_X86_PARTITION -- cgit v1.2.3 From 548b10eb2959c96cef6fc29fc96e0931eeb53bc5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Aug 2008 09:01:47 +0200 Subject: block: move __dev from disk to part0 Move disk->__dev to part0->__dev. This simplifies bdget_disk() and lookup_devt() and allows common sysfs attributes to be unified. part_to_disk() is updated to handle part0 -> disk. Updated to include a fix from Bartlomiej Zolnierkiewicz , he writes: "part0 is a "special" partition and doesn't need to have capacity set - this fixes regression caused by "block: move __dev from disk to part0" commit." Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 40 ++++++++++++---------------------------- include/linux/genhd.h | 13 ++++++++----- 2 files changed, 20 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index 65b7386c26d8..36b9f1bdd91f 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -537,22 +537,15 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) */ extern struct block_device *bdget_disk(struct gendisk *disk, int partno) { - dev_t devt = MKDEV(0, 0); + struct hd_struct *part; + struct block_device *bdev = NULL; - if (partno == 0) - devt = disk_devt(disk); - else { - struct hd_struct *part; + part = disk_get_part(disk, partno); + if (part && (part->nr_sects || partno == 0)) + bdev = bdget(part_devt(part)); + disk_put_part(part); - part = disk_get_part(disk, partno); - if (part && part->nr_sects) - devt = part_devt(part); - disk_put_part(part); - } - - if (likely(devt != MKDEV(0, 0))) - return bdget(devt); - return NULL; + return bdev; } EXPORT_SYMBOL(bdget_disk); @@ -1000,27 +993,18 @@ dev_t blk_lookup_devt(const char *name, int partno) class_dev_iter_init(&iter, &block_class, NULL, &disk_type); while ((dev = class_dev_iter_next(&iter))) { struct gendisk *disk = dev_to_disk(dev); + struct hd_struct *part; if (strcmp(dev->bus_id, name)) continue; - if (partno < 0 || partno >= disk_max_parts(disk)) - continue; - - if (partno == 0) - devt = disk_devt(disk); - else { - struct hd_struct *part; - - part = disk_get_part(disk, partno); - if (!part || !part->nr_sects) { - disk_put_part(part); - continue; - } + part = disk_get_part(disk, partno); + if (part && (part->nr_sects || partno == 0)) { devt = part_devt(part); disk_put_part(part); + break; } - break; + disk_put_part(part); } class_dev_iter_exit(&iter); return devt; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 1cf828148ec6..ff293ec8b3f7 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -16,9 +16,9 @@ #ifdef CONFIG_BLOCK #define kobj_to_dev(k) container_of((k), struct device, kobj) -#define dev_to_disk(device) container_of((device), struct gendisk, __dev) +#define dev_to_disk(device) container_of((device), struct gendisk, part0.__dev) #define dev_to_part(device) container_of((device), struct hd_struct, __dev) -#define disk_to_dev(disk) (&((disk)->__dev)) +#define disk_to_dev(disk) (&(disk)->part0.__dev) #define part_to_dev(part) (&((part)->__dev)) extern struct device_type part_type; @@ -141,7 +141,6 @@ struct gendisk { int flags; struct device *driverfs_dev; // FIXME: remove - struct device __dev; struct kobject *holder_dir; struct kobject *slave_dir; @@ -164,8 +163,12 @@ struct gendisk { static inline struct gendisk *part_to_disk(struct hd_struct *part) { - if (likely(part)) - return dev_to_disk(part_to_dev(part)->parent); + if (likely(part)) { + if (part->partno) + return dev_to_disk(part_to_dev(part)->parent); + else + return dev_to_disk(part_to_dev(part)); + } return NULL; } -- cgit v1.2.3 From e56105214943ce5f0901d20e972a7cfd0d1d0656 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:09 +0900 Subject: block: unify sysfs size node handling Now that capacity and __dev are moved to part0, part0 and others can share the same method. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 10 +--------- fs/partitions/check.c | 4 ++-- include/linux/genhd.h | 3 +++ 3 files changed, 6 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index 36b9f1bdd91f..c70db35076a0 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -760,14 +760,6 @@ static ssize_t disk_ro_show(struct device *dev, return sprintf(buf, "%d\n", disk->policy ? 1 : 0); } -static ssize_t disk_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gendisk *disk = dev_to_disk(dev); - - return sprintf(buf, "%llu\n", (unsigned long long)get_capacity(disk)); -} - static ssize_t disk_capability_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -835,7 +827,7 @@ static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); -static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL); +static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); #ifdef CONFIG_FAIL_MAKE_REQUEST diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 902b95f1f9d5..24d2c56d7d2d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -208,8 +208,8 @@ static ssize_t part_start_show(struct device *dev, return sprintf(buf, "%llu\n",(unsigned long long)p->start_sect); } -static ssize_t part_size_show(struct device *dev, - struct device_attribute *attr, char *buf) +ssize_t part_size_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ff293ec8b3f7..9cb8380cf0eb 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -591,6 +591,9 @@ extern void blk_register_region(dev_t devt, unsigned long range, void *data); extern void blk_unregister_region(dev_t devt, unsigned long range); +extern ssize_t part_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + #else /* CONFIG_BLOCK */ static inline void printk_all_partitions(void) { } -- cgit v1.2.3 From b7db9956e57c8151b930d5e5fe5c766e6aad3ff7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:10 +0900 Subject: block: move policy from disk to part0 Move disk->policy to part0->policy. Implement and use get_disk_ro(). Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 16 +++++----------- drivers/ide/ide-cd.c | 2 +- drivers/md/dm-ioctl.c | 2 +- fs/partitions/check.c | 2 +- include/linux/genhd.h | 6 +++++- 5 files changed, 13 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index c70db35076a0..70358f3c7423 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -757,7 +757,7 @@ static ssize_t disk_ro_show(struct device *dev, { struct gendisk *disk = dev_to_disk(dev); - return sprintf(buf, "%d\n", disk->policy ? 1 : 0); + return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0); } static ssize_t disk_capability_show(struct device *dev, @@ -1090,10 +1090,7 @@ EXPORT_SYMBOL(put_disk); void set_device_ro(struct block_device *bdev, int flag) { - if (bdev->bd_contains != bdev) - bdev->bd_part->policy = flag; - else - bdev->bd_disk->policy = flag; + bdev->bd_part->policy = flag; } EXPORT_SYMBOL(set_device_ro); @@ -1103,8 +1100,8 @@ void set_disk_ro(struct gendisk *disk, int flag) struct disk_part_iter piter; struct hd_struct *part; - disk->policy = flag; - disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY); + disk_part_iter_init(&piter, disk, + DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0); while ((part = disk_part_iter_next(&piter))) part->policy = flag; disk_part_iter_exit(&piter); @@ -1116,10 +1113,7 @@ int bdev_read_only(struct block_device *bdev) { if (!bdev) return 0; - else if (bdev->bd_contains != bdev) - return bdev->bd_part->policy; - else - return bdev->bd_disk->policy; + return bdev->bd_part->policy; } EXPORT_SYMBOL(bdev_read_only); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index f16bb4667238..03c2cb6a58bc 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1113,7 +1113,7 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) if (write) { /* disk has become write protected */ - if (cd->disk->policy) { + if (get_disk_ro(cd->disk)) { cdrom_end_request(drive, 0); return ide_stopped; } diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index c3de311117a1..5b919159f084 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -548,7 +548,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) */ param->open_count = dm_open_count(md); - if (disk->policy) + if (get_disk_ro(disk)) param->flags |= DM_READONLY_FLAG; param->event_nr = dm_get_event_nr(md); diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 24d2c56d7d2d..ace6d03602c7 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -375,7 +375,7 @@ int add_partition(struct gendisk *disk, int partno, p->start_sect = start; p->nr_sects = len; p->partno = partno; - p->policy = disk->policy; + p->policy = get_disk_ro(disk); dname = dev_name(ddev); if (isdigit(dname[strlen(dname) - 1])) diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 9cb8380cf0eb..4411bdd671dd 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -145,7 +145,6 @@ struct gendisk { struct kobject *slave_dir; struct timer_rand_state *random; - int policy; atomic_t sync_io; /* RAID */ unsigned long stamp; @@ -403,6 +402,11 @@ extern struct block_device *bdget_disk(struct gendisk *disk, int partno); extern void set_device_ro(struct block_device *bdev, int flag); extern void set_disk_ro(struct gendisk *disk, int flag); +static inline int get_disk_ro(struct gendisk *disk) +{ + return disk->part0.policy; +} + /* drivers/char/random.c */ extern void add_disk_randomness(struct gendisk *disk); extern void rand_initialize_disk(struct gendisk *disk); -- cgit v1.2.3 From 4c46501d1659475dc6c89554af6ce7fe6ecf615c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:11 +0900 Subject: block: move holder_dir from disk to part0 Move disk->holder_dir to part0->holder_dir. Kill now mostly superflous bdev_get_holder(). While at it, kill superflous kobject_get/put() around holder_dir, slave_dir and cmd_filter creation and collapse disk_sysfs_add_subdirs() into register_disk(). These serve no purpose but obfuscating the code. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/cmd-filter.c | 9 ++------- fs/block_dev.c | 10 +--------- fs/partitions/check.c | 15 +++------------ include/linux/genhd.h | 1 - 4 files changed, 6 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/block/cmd-filter.c b/block/cmd-filter.c index 79c14996ac11..e669aed4c6bc 100644 --- a/block/cmd-filter.c +++ b/block/cmd-filter.c @@ -211,14 +211,10 @@ int blk_register_filter(struct gendisk *disk) { int ret; struct blk_cmd_filter *filter = &disk->queue->cmd_filter; - struct kobject *parent = kobject_get(disk->holder_dir->parent); - if (!parent) - return -ENODEV; - - ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, parent, + ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, + &disk_to_dev(disk)->kobj, "%s", "cmd_filter"); - if (ret < 0) return ret; @@ -231,7 +227,6 @@ void blk_unregister_filter(struct gendisk *disk) struct blk_cmd_filter *filter = &disk->queue->cmd_filter; kobject_put(&filter->kobj); - kobject_put(disk->holder_dir->parent); } EXPORT_SYMBOL(blk_unregister_filter); #endif diff --git a/fs/block_dev.c b/fs/block_dev.c index c982a9107979..57d572642854 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -548,14 +548,6 @@ static struct kobject *bdev_get_kobj(struct block_device *bdev) return kobject_get(&disk_to_dev(bdev->bd_disk)->kobj); } -static struct kobject *bdev_get_holder(struct block_device *bdev) -{ - if (bdev->bd_contains != bdev) - return kobject_get(bdev->bd_part->holder_dir); - else - return kobject_get(bdev->bd_disk->holder_dir); -} - static int add_symlink(struct kobject *from, struct kobject *to) { if (!from || !to) @@ -608,7 +600,7 @@ static int bd_holder_grab_dirs(struct block_device *bdev, if (!bo->sdev) goto fail_put_hdev; - bo->hdir = bdev_get_holder(bdev); + bo->hdir = kobject_get(bdev->bd_part->holder_dir); if (!bo->hdir) goto fail_put_sdev; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index ace6d03602c7..f0f604950ff4 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -305,16 +305,6 @@ struct device_type part_type = { .release = part_release, }; -static inline void disk_sysfs_add_subdirs(struct gendisk *disk) -{ - struct kobject *k; - - k = kobject_get(&disk_to_dev(disk)->kobj); - disk->holder_dir = kobject_create_and_add("holders", k); - disk->slave_dir = kobject_create_and_add("slaves", k); - kobject_put(k); -} - static void delete_partition_rcu_cb(struct rcu_head *head) { struct hd_struct *part = container_of(head, struct hd_struct, rcu_head); @@ -464,7 +454,8 @@ void register_disk(struct gendisk *disk) return; } #endif - disk_sysfs_add_subdirs(disk); + disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); + disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); /* No minors to use for partitions */ if (!disk_partitionable(disk)) @@ -592,7 +583,7 @@ void del_gendisk(struct gendisk *disk) disk_stat_set_all(disk, 0); disk->stamp = 0; - kobject_put(disk->holder_dir); + kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; #ifndef CONFIG_SYSFS_DEPRECATED diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 4411bdd671dd..2c0e1b597ab4 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -141,7 +141,6 @@ struct gendisk { int flags; struct device *driverfs_dev; // FIXME: remove - struct kobject *holder_dir; struct kobject *slave_dir; struct timer_rand_state *random; -- cgit v1.2.3 From 0762b8bde9729f10f8e6249809660ff2ec3ad735 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:12 +0900 Subject: block: always set bdev->bd_part Till now, bdev->bd_part is set only if the bdev was for parts other than part0. This patch makes bdev->bd_part always set so that code paths don't have to differenciate common handling. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 2 +- drivers/md/md.c | 5 +--- fs/block_dev.c | 67 ++++++++++++++++++++++++--------------------------- fs/partitions/check.c | 7 +----- include/linux/genhd.h | 2 +- 5 files changed, 35 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index e0a5ee36849c..a4a7c08d2f20 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1274,7 +1274,7 @@ __setup("fail_make_request=", setup_fail_make_request); static int should_fail_request(struct bio *bio) { if ((bio->bi_bdev->bd_disk->flags & GENHD_FL_FAIL) || - (bio->bi_bdev->bd_part && bio->bi_bdev->bd_part->make_it_fail)) + bio->bi_bdev->bd_part->make_it_fail) return should_fail(&fail_make_request, bio->bi_size); return 0; diff --git a/drivers/md/md.c b/drivers/md/md.c index 96e9fccd2eab..2bd9cf416123 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1464,10 +1464,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b))) goto fail; - if (rdev->bdev->bd_part) - ko = &part_to_dev(rdev->bdev->bd_part)->kobj; - else - ko = &disk_to_dev(rdev->bdev->bd_disk)->kobj; + ko = &part_to_dev(rdev->bdev->bd_part)->kobj; if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) { kobject_del(&rdev->kobj); goto fail; diff --git a/fs/block_dev.c b/fs/block_dev.c index 57d572642854..c3fa19bd64df 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -540,14 +540,6 @@ EXPORT_SYMBOL(bd_release); * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 */ -static struct kobject *bdev_get_kobj(struct block_device *bdev) -{ - if (bdev->bd_contains != bdev) - return kobject_get(&part_to_dev(bdev->bd_part)->kobj); - else - return kobject_get(&disk_to_dev(bdev->bd_disk)->kobj); -} - static int add_symlink(struct kobject *from, struct kobject *to) { if (!from || !to) @@ -596,7 +588,7 @@ static int bd_holder_grab_dirs(struct block_device *bdev, if (!bo->hdev) goto fail_put_sdir; - bo->sdev = bdev_get_kobj(bdev); + bo->sdev = kobject_get(&part_to_dev(bdev->bd_part)->kobj); if (!bo->sdev) goto fail_put_hdev; @@ -919,7 +911,6 @@ static int __blkdev_put(struct block_device *bdev, int for_part); static int do_open(struct block_device *bdev, struct file *file, int for_part) { - struct module *owner = NULL; struct gendisk *disk; struct hd_struct *part = NULL; int ret; @@ -941,25 +932,27 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) ret = -ENXIO; file->f_mapping = bdev->bd_inode->i_mapping; + lock_kernel(); + disk = get_gendisk(bdev->bd_dev, &partno); - if (!disk) { - unlock_kernel(); - bdput(bdev); - return ret; - } - owner = disk->fops->owner; + if (!disk) + goto out_unlock_kernel; + part = disk_get_part(disk, partno); + if (!part) + goto out_unlock_kernel; mutex_lock_nested(&bdev->bd_mutex, for_part); if (!bdev->bd_openers) { bdev->bd_disk = disk; + bdev->bd_part = part; bdev->bd_contains = bdev; if (!partno) { struct backing_dev_info *bdi; if (disk->fops->open) { ret = disk->fops->open(bdev->bd_inode, file); if (ret) - goto out_first; + goto out_clear; } if (!bdev->bd_openers) { bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); @@ -975,31 +968,32 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) whole = bdget_disk(disk, 0); ret = -ENOMEM; if (!whole) - goto out_first; + goto out_clear; BUG_ON(for_part); ret = __blkdev_get(whole, file->f_mode, file->f_flags, 1); if (ret) - goto out_first; + goto out_clear; bdev->bd_contains = whole; - part = disk_get_part(disk, partno); bdev->bd_inode->i_data.backing_dev_info = whole->bd_inode->i_data.backing_dev_info; if (!(disk->flags & GENHD_FL_UP) || !part || !part->nr_sects) { ret = -ENXIO; - goto out_first; + goto out_clear; } - bdev->bd_part = part; bd_set_size(bdev, (loff_t)part->nr_sects << 9); } } else { + disk_put_part(part); put_disk(disk); - module_put(owner); + module_put(disk->fops->owner); + part = NULL; + disk = NULL; if (bdev->bd_contains == bdev) { if (bdev->bd_disk->fops->open) { ret = bdev->bd_disk->fops->open(bdev->bd_inode, file); if (ret) - goto out; + goto out_unlock_bdev; } if (bdev->bd_invalidated) rescan_partitions(bdev->bd_disk, bdev); @@ -1012,20 +1006,24 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) unlock_kernel(); return 0; -out_first: + out_clear: bdev->bd_disk = NULL; + bdev->bd_part = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) __blkdev_put(bdev->bd_contains, 1); bdev->bd_contains = NULL; - put_disk(disk); - disk_put_part(part); - module_put(owner); -out: + out_unlock_bdev: mutex_unlock(&bdev->bd_mutex); + out_unlock_kernel: unlock_kernel(); - if (ret) - bdput(bdev); + + disk_put_part(part); + if (disk) + module_put(disk->fops->owner); + put_disk(disk); + bdput(bdev); + return ret; } @@ -1110,11 +1108,8 @@ static int __blkdev_put(struct block_device *bdev, int for_part) put_disk(disk); module_put(owner); - - if (bdev->bd_contains != bdev) { - disk_put_part(bdev->bd_part); - bdev->bd_part = NULL; - } + disk_put_part(bdev->bd_part); + bdev->bd_part = NULL; bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) diff --git a/fs/partitions/check.c b/fs/partitions/check.c index f0f604950ff4..87298c0fc8ce 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -134,12 +134,7 @@ char *disk_name(struct gendisk *hd, int partno, char *buf) const char *bdevname(struct block_device *bdev, char *buf) { - int partno = 0; - - if (bdev->bd_part) - partno = bdev->bd_part->partno; - - return disk_name(bdev->bd_disk, partno, buf); + return disk_name(bdev->bd_disk, bdev->bd_part->partno, buf); } EXPORT_SYMBOL(bdevname); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 2c0e1b597ab4..45a3682b5d87 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -412,7 +412,7 @@ extern void rand_initialize_disk(struct gendisk *disk); static inline sector_t get_start_sect(struct block_device *bdev) { - return bdev->bd_contains == bdev ? 0 : bdev->bd_part->start_sect; + return bdev->bd_part->start_sect; } static inline sector_t get_capacity(struct gendisk *disk) { -- cgit v1.2.3 From eddb2e26b5ee3c5da68ba4bf1921ba20e2097bff Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:13 +0900 Subject: block: kill GENHD_FL_FAIL and use part0->make_it_fail GENHD_FL_FAIL for disk is what make_it_fail is for parts. Kill it and use part0->make_it_fail. Sysfs node handling is unified too. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 5 +++-- block/genhd.c | 30 +----------------------------- fs/partitions/check.c | 10 +++++----- include/linux/genhd.h | 8 +++++++- 4 files changed, 16 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index a4a7c08d2f20..505ec61067df 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1273,8 +1273,9 @@ __setup("fail_make_request=", setup_fail_make_request); static int should_fail_request(struct bio *bio) { - if ((bio->bi_bdev->bd_disk->flags & GENHD_FL_FAIL) || - bio->bi_bdev->bd_part->make_it_fail) + struct hd_struct *part = bio->bi_bdev->bd_part; + + if (part_to_disk(part)->part0.make_it_fail || part->make_it_fail) return should_fail(&fail_make_request, bio->bi_size); return 0; diff --git a/block/genhd.c b/block/genhd.c index 70358f3c7423..06a252f2b967 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -795,34 +795,6 @@ static ssize_t disk_stat_show(struct device *dev, jiffies_to_msecs(disk_stat_read(disk, time_in_queue))); } -#ifdef CONFIG_FAIL_MAKE_REQUEST -static ssize_t disk_fail_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gendisk *disk = dev_to_disk(dev); - - return sprintf(buf, "%d\n", disk->flags & GENHD_FL_FAIL ? 1 : 0); -} - -static ssize_t disk_fail_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct gendisk *disk = dev_to_disk(dev); - int i; - - if (count > 0 && sscanf(buf, "%d", &i) > 0) { - if (i == 0) - disk->flags &= ~GENHD_FL_FAIL; - else - disk->flags |= GENHD_FL_FAIL; - } - - return count; -} - -#endif - static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); @@ -832,7 +804,7 @@ static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); #ifdef CONFIG_FAIL_MAKE_REQUEST static struct device_attribute dev_attr_fail = - __ATTR(make-it-fail, S_IRUGO|S_IWUSR, disk_fail_show, disk_fail_store); + __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); #endif static struct attribute *disk_attrs[] = { diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 87298c0fc8ce..60592d9f43b6 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -238,17 +238,17 @@ static ssize_t part_stat_show(struct device *dev, } #ifdef CONFIG_FAIL_MAKE_REQUEST -static ssize_t part_fail_show(struct device *dev, - struct device_attribute *attr, char *buf) +ssize_t part_fail_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); return sprintf(buf, "%d\n", p->make_it_fail); } -static ssize_t part_fail_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +ssize_t part_fail_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct hd_struct *p = dev_to_part(dev); int i; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 45a3682b5d87..3d15b42dc352 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -112,7 +112,6 @@ struct hd_struct { #define GENHD_FL_CD 8 #define GENHD_FL_UP 16 #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 -#define GENHD_FL_FAIL 64 struct gendisk { /* major, first_minor, minors and ext_minors are input @@ -596,6 +595,13 @@ extern void blk_unregister_region(dev_t devt, unsigned long range); extern ssize_t part_size_show(struct device *dev, struct device_attribute *attr, char *buf); +#ifdef CONFIG_FAIL_MAKE_REQUEST +extern ssize_t part_fail_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t part_fail_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +#endif /* CONFIG_FAIL_MAKE_REQUEST */ #else /* CONFIG_BLOCK */ -- cgit v1.2.3 From 074a7aca7afa6f230104e8e65eba3420263714a5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:14 +0900 Subject: block: move stats from disk to part0 Move stats related fields - stamp, in_flight, dkstats - from disk to part0 and unify stat handling such that... * part_stat_*() now updates part0 together if the specified partition is not part0. ie. part_stat_*() are now essentially all_stat_*(). * {disk|all}_stat_*() are gone. * part_round_stats() is updated similary. It handles part0 stats automatically and disk_round_stats() is killed. * part_{inc|dec}_in_fligh() is implemented which automatically updates part0 stats for parts other than part0. * disk_map_sector_rcu() is updated to return part0 if no part matches. Combined with the above changes, this makes NULL special case handling in callers unnecessary. * Separate stats show code paths for disk are collapsed into part stats show code paths. * Rename disk_stat_lock/unlock() to part_stat_lock/unlock() While at it, reposition stat handling macros a bit and add missing parentheses around macro parameters. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 84 ++++++++++-------------- block/blk-merge.c | 12 ++-- block/genhd.c | 97 +++++++-------------------- drivers/block/aoe/aoecmd.c | 12 ++-- drivers/md/dm.c | 27 ++++---- drivers/md/linear.c | 9 +-- drivers/md/md.c | 4 +- drivers/md/multipath.c | 9 +-- drivers/md/raid0.c | 9 +-- drivers/md/raid1.c | 9 +-- drivers/md/raid10.c | 9 +-- drivers/md/raid5.c | 9 +-- fs/partitions/check.c | 12 ++-- include/linux/genhd.h | 159 +++++++++++++-------------------------------- 14 files changed, 165 insertions(+), 296 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 505ec61067df..98138f002524 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -61,21 +61,17 @@ static void drive_stat_acct(struct request *rq, int new_io) if (!blk_fs_request(rq) || !rq->rq_disk) return; - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part = disk_map_sector_rcu(rq->rq_disk, rq->sector); if (!new_io) - all_stat_inc(cpu, rq->rq_disk, part, merges[rw], rq->sector); + part_stat_inc(cpu, part, merges[rw]); else { - disk_round_stats(cpu, rq->rq_disk); - rq->rq_disk->in_flight++; - if (part) { - part_round_stats(cpu, part); - part->in_flight++; - } + part_round_stats(cpu, part); + part_inc_in_flight(part); } - disk_stat_unlock(); + part_stat_unlock(); } void blk_queue_congestion_threshold(struct request_queue *q) @@ -983,8 +979,22 @@ static inline void add_request(struct request_queue *q, struct request *req) __elv_add_request(q, req, ELEVATOR_INSERT_SORT, 0); } -/* - * disk_round_stats() - Round off the performance stats on a struct +static void part_round_stats_single(int cpu, struct hd_struct *part, + unsigned long now) +{ + if (now == part->stamp) + return; + + if (part->in_flight) { + __part_stat_add(cpu, part, time_in_queue, + part->in_flight * (now - part->stamp)); + __part_stat_add(cpu, part, io_ticks, (now - part->stamp)); + } + part->stamp = now; +} + +/** + * part_round_stats() - Round off the performance stats on a struct * disk_stats. * * The average IO queue length and utilisation statistics are maintained @@ -998,36 +1008,15 @@ static inline void add_request(struct request_queue *q, struct request *req) * /proc/diskstats. This accounts immediately for all queue usage up to * the current jiffies and restarts the counters again. */ -void disk_round_stats(int cpu, struct gendisk *disk) -{ - unsigned long now = jiffies; - - if (now == disk->stamp) - return; - - if (disk->in_flight) { - disk_stat_add(cpu, disk, time_in_queue, - disk->in_flight * (now - disk->stamp)); - disk_stat_add(cpu, disk, io_ticks, (now - disk->stamp)); - } - disk->stamp = now; -} -EXPORT_SYMBOL_GPL(disk_round_stats); - void part_round_stats(int cpu, struct hd_struct *part) { unsigned long now = jiffies; - if (now == part->stamp) - return; - - if (part->in_flight) { - part_stat_add(cpu, part, time_in_queue, - part->in_flight * (now - part->stamp)); - part_stat_add(cpu, part, io_ticks, (now - part->stamp)); - } - part->stamp = now; + if (part->partno) + part_round_stats_single(cpu, &part_to_disk(part)->part0, now); + part_round_stats_single(cpu, part, now); } +EXPORT_SYMBOL_GPL(part_round_stats); /* * queue lock must be held @@ -1567,11 +1556,10 @@ static int __end_that_request_first(struct request *req, int error, struct hd_struct *part; int cpu; - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part = disk_map_sector_rcu(req->rq_disk, req->sector); - all_stat_add(cpu, req->rq_disk, part, sectors[rw], - nr_bytes >> 9, req->sector); - disk_stat_unlock(); + part_stat_add(cpu, part, sectors[rw], nr_bytes >> 9); + part_stat_unlock(); } total_bytes = bio_nbytes = 0; @@ -1758,19 +1746,15 @@ static void end_that_request_last(struct request *req, int error) struct hd_struct *part; int cpu; - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part = disk_map_sector_rcu(disk, req->sector); - all_stat_inc(cpu, disk, part, ios[rw], req->sector); - all_stat_add(cpu, disk, part, ticks[rw], duration, req->sector); - disk_round_stats(cpu, disk); - disk->in_flight--; - if (part) { - part_round_stats(cpu, part); - part->in_flight--; - } + part_stat_inc(cpu, part, ios[rw]); + part_stat_add(cpu, part, ticks[rw], duration); + part_round_stats(cpu, part); + part_dec_in_flight(part); - disk_stat_unlock(); + part_stat_unlock(); } if (req->end_io) diff --git a/block/blk-merge.c b/block/blk-merge.c index d926a24bf1fd..c77196d55899 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -390,17 +390,13 @@ static int attempt_merge(struct request_queue *q, struct request *req, struct hd_struct *part; int cpu; - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part = disk_map_sector_rcu(req->rq_disk, req->sector); - disk_round_stats(cpu, req->rq_disk); - req->rq_disk->in_flight--; - if (part) { - part_round_stats(cpu, part); - part->in_flight--; - } + part_round_stats(cpu, part); + part_dec_in_flight(part); - disk_stat_unlock(); + part_stat_unlock(); } req->ioprio = ioprio_best(req->ioprio, next->ioprio); diff --git a/block/genhd.c b/block/genhd.c index 06a252f2b967..e1cb96fb883e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -176,7 +176,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); * while preemption is disabled. * * RETURNS: - * Found partition on success, NULL if there's no matching partition. + * Found partition on success, part0 is returned if no partition matches */ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) { @@ -189,7 +189,7 @@ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) sector < part->start_sect + part->nr_sects) return part; } - return NULL; + return &disk->part0; } EXPORT_SYMBOL_GPL(disk_map_sector_rcu); @@ -580,24 +580,24 @@ void __init printk_all_partitions(void) * numbers in hex - the same format as the root= * option takes. */ - printk("%s %10llu %s", - bdevt_str(disk_devt(disk), devt_buf), - (unsigned long long)get_capacity(disk) >> 1, - disk_name(disk, 0, name_buf)); - if (disk->driverfs_dev != NULL && - disk->driverfs_dev->driver != NULL) - printk(" driver: %s\n", - disk->driverfs_dev->driver->name); - else - printk(" (driver?)\n"); + disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); + while ((part = disk_part_iter_next(&piter))) { + bool is_part0 = part == &disk->part0; - /* now show the partitions */ - disk_part_iter_init(&piter, disk, 0); - while ((part = disk_part_iter_next(&piter))) - printk(" %s %10llu %s\n", + printk("%s%s %10llu %s", is_part0 ? "" : " ", bdevt_str(part_devt(part), devt_buf), (unsigned long long)part->nr_sects >> 1, disk_name(disk, part->partno, name_buf)); + if (is_part0) { + if (disk->driverfs_dev != NULL && + disk->driverfs_dev->driver != NULL) + printk(" driver: %s\n", + disk->driverfs_dev->driver->name); + else + printk(" (driver?)\n"); + } else + printk("\n"); + } disk_part_iter_exit(&piter); } class_dev_iter_exit(&iter); @@ -674,12 +674,7 @@ static int show_partition(struct seq_file *seqf, void *v) return 0; /* show the full disk and all non-0 size partitions of it */ - seq_printf(seqf, "%4d %7d %10llu %s\n", - MAJOR(disk_devt(sgp)), MINOR(disk_devt(sgp)), - (unsigned long long)get_capacity(sgp) >> 1, - disk_name(sgp, 0, buf)); - - disk_part_iter_init(&piter, sgp, 0); + disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0); while ((part = disk_part_iter_next(&piter))) seq_printf(seqf, "%4d %7d %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), @@ -768,40 +763,13 @@ static ssize_t disk_capability_show(struct device *dev, return sprintf(buf, "%x\n", disk->flags); } -static ssize_t disk_stat_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gendisk *disk = dev_to_disk(dev); - int cpu; - - cpu = disk_stat_lock(); - disk_round_stats(cpu, disk); - disk_stat_unlock(); - return sprintf(buf, - "%8lu %8lu %8llu %8u " - "%8lu %8lu %8llu %8u " - "%8u %8u %8u" - "\n", - disk_stat_read(disk, ios[READ]), - disk_stat_read(disk, merges[READ]), - (unsigned long long)disk_stat_read(disk, sectors[READ]), - jiffies_to_msecs(disk_stat_read(disk, ticks[READ])), - disk_stat_read(disk, ios[WRITE]), - disk_stat_read(disk, merges[WRITE]), - (unsigned long long)disk_stat_read(disk, sectors[WRITE]), - jiffies_to_msecs(disk_stat_read(disk, ticks[WRITE])), - disk->in_flight, - jiffies_to_msecs(disk_stat_read(disk, io_ticks)), - jiffies_to_msecs(disk_stat_read(disk, time_in_queue))); -} - static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); -static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); +static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); #ifdef CONFIG_FAIL_MAKE_REQUEST static struct device_attribute dev_attr_fail = __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); @@ -836,7 +804,7 @@ static void disk_release(struct device *dev) kfree(disk->random); kfree(disk->__part); - free_disk_stats(disk); + free_part_stats(&disk->part0); kfree(disk); } struct class block_class = { @@ -873,28 +841,11 @@ static int diskstats_show(struct seq_file *seqf, void *v) "\n\n"); */ - cpu = disk_stat_lock(); - disk_round_stats(cpu, gp); - disk_stat_unlock(); - seq_printf(seqf, "%4d %7d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", - MAJOR(disk_devt(gp)), MINOR(disk_devt(gp)), - disk_name(gp, 0, buf), - disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), - (unsigned long long)disk_stat_read(gp, sectors[0]), - jiffies_to_msecs(disk_stat_read(gp, ticks[0])), - disk_stat_read(gp, ios[1]), disk_stat_read(gp, merges[1]), - (unsigned long long)disk_stat_read(gp, sectors[1]), - jiffies_to_msecs(disk_stat_read(gp, ticks[1])), - gp->in_flight, - jiffies_to_msecs(disk_stat_read(gp, io_ticks)), - jiffies_to_msecs(disk_stat_read(gp, time_in_queue))); - - /* now show all non-0 size partitions of it */ - disk_part_iter_init(&piter, gp, 0); + disk_part_iter_init(&piter, gp, DISK_PITER_INCL_PART0); while ((hd = disk_part_iter_next(&piter))) { - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part_round_stats(cpu, hd); - disk_stat_unlock(); + part_stat_unlock(); seq_printf(seqf, "%4d %7d %s %lu %lu %llu " "%u %lu %lu %llu %u %u %u %u\n", MAJOR(part_devt(hd)), MINOR(part_devt(hd)), @@ -1000,7 +951,7 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) int tot_minors = minors + ext_minors; int size = tot_minors * sizeof(struct hd_struct *); - if (!init_disk_stats(disk)) { + if (!init_part_stats(&disk->part0)) { kfree(disk); return NULL; } @@ -1008,7 +959,7 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, node_id); if (!disk->__part) { - free_disk_stats(disk); + free_part_stats(&disk->part0); kfree(disk); return NULL; } diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 934800f979c9..961d29a53cab 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -758,15 +758,15 @@ diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector struct hd_struct *part; int cpu; - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part = disk_map_sector_rcu(disk, sector); - all_stat_inc(cpu, disk, part, ios[rw], sector); - all_stat_add(cpu, disk, part, ticks[rw], duration, sector); - all_stat_add(cpu, disk, part, sectors[rw], n_sect, sector); - all_stat_add(cpu, disk, part, io_ticks, duration, sector); + part_stat_inc(cpu, part, ios[rw]); + part_stat_add(cpu, part, ticks[rw], duration); + part_stat_add(cpu, part, sectors[rw], n_sect); + part_stat_add(cpu, part, io_ticks, duration); - disk_stat_unlock(); + part_stat_unlock(); } void diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 637806695bb9..327de03a5bdf 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -381,10 +381,10 @@ static void start_io_acct(struct dm_io *io) io->start_time = jiffies; - cpu = disk_stat_lock(); - disk_round_stats(cpu, dm_disk(md)); - disk_stat_unlock(); - dm_disk(md)->in_flight = atomic_inc_return(&md->pending); + cpu = part_stat_lock(); + part_round_stats(cpu, &dm_disk(md)->part0); + part_stat_unlock(); + dm_disk(md)->part0.in_flight = atomic_inc_return(&md->pending); } static int end_io_acct(struct dm_io *io) @@ -395,12 +395,13 @@ static int end_io_acct(struct dm_io *io) int pending, cpu; int rw = bio_data_dir(bio); - cpu = disk_stat_lock(); - disk_round_stats(cpu, dm_disk(md)); - disk_stat_add(cpu, dm_disk(md), ticks[rw], duration); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_round_stats(cpu, &dm_disk(md)->part0); + part_stat_add(cpu, &dm_disk(md)->part0, ticks[rw], duration); + part_stat_unlock(); - dm_disk(md)->in_flight = pending = atomic_dec_return(&md->pending); + dm_disk(md)->part0.in_flight = pending = + atomic_dec_return(&md->pending); return !pending; } @@ -899,10 +900,10 @@ static int dm_request(struct request_queue *q, struct bio *bio) down_read(&md->io_lock); - cpu = disk_stat_lock(); - disk_stat_inc(cpu, dm_disk(md), ios[rw]); - disk_stat_add(cpu, dm_disk(md), sectors[rw], bio_sectors(bio)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &dm_disk(md)->part0, ios[rw]); + part_stat_add(cpu, &dm_disk(md)->part0, sectors[rw], bio_sectors(bio)); + part_stat_unlock(); /* * If we're suspended we have to queue diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 00cbc8e47294..c80ea90593d3 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -325,10 +325,11 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) return 0; } - cpu = disk_stat_lock(); - disk_stat_inc(cpu, mddev->gendisk, ios[rw]); - disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], + bio_sectors(bio)); + part_stat_unlock(); tmp_dev = which_dev(mddev, bio->bi_sector); block = bio->bi_sector >> 1; diff --git a/drivers/md/md.c b/drivers/md/md.c index 2bd9cf416123..0a3a4bdcd4af 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5546,8 +5546,8 @@ static int is_mddev_idle(mddev_t *mddev) rcu_read_lock(); rdev_for_each_rcu(rdev, mddev) { struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; - curr_events = disk_stat_read(disk, sectors[0]) + - disk_stat_read(disk, sectors[1]) - + curr_events = part_stat_read(&disk->part0, sectors[0]) + + part_stat_read(&disk->part0, sectors[1]) - atomic_read(&disk->sync_io); /* sync IO will cause sync_io to increase before the disk_stats * as sync_io is counted when a request starts, and diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 182f5a94cdc5..8bb8794129b3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -159,10 +159,11 @@ static int multipath_make_request (struct request_queue *q, struct bio * bio) mp_bh->master_bio = bio; mp_bh->mddev = mddev; - cpu = disk_stat_lock(); - disk_stat_inc(cpu, mddev->gendisk, ios[rw]); - disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], + bio_sectors(bio)); + part_stat_unlock(); mp_bh->path = multipath_map(conf); if (mp_bh->path < 0) { diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index e26030fa59ab..f52f442a735f 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -406,10 +406,11 @@ static int raid0_make_request (struct request_queue *q, struct bio *bio) return 0; } - cpu = disk_stat_lock(); - disk_stat_inc(cpu, mddev->gendisk, ios[rw]); - disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], + bio_sectors(bio)); + part_stat_unlock(); chunk_size = mddev->chunk_size >> 10; chunk_sects = mddev->chunk_size >> 9; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index babb13036f93..b9764429d856 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -804,10 +804,11 @@ static int make_request(struct request_queue *q, struct bio * bio) bitmap = mddev->bitmap; - cpu = disk_stat_lock(); - disk_stat_inc(cpu, mddev->gendisk, ios[rw]); - disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], + bio_sectors(bio)); + part_stat_unlock(); /* * make_request() can abort the operation when READA is being diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5ec80da0a9d7..5f990133f5ef 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -844,10 +844,11 @@ static int make_request(struct request_queue *q, struct bio * bio) */ wait_barrier(conf); - cpu = disk_stat_lock(); - disk_stat_inc(cpu, mddev->gendisk, ios[rw]); - disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bio)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], + bio_sectors(bio)); + part_stat_unlock(); r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 5899f211515f..ae16794bef20 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3396,10 +3396,11 @@ static int make_request(struct request_queue *q, struct bio * bi) md_write_start(mddev, bi); - cpu = disk_stat_lock(); - disk_stat_inc(cpu, mddev->gendisk, ios[rw]); - disk_stat_add(cpu, mddev->gendisk, sectors[rw], bio_sectors(bi)); - disk_stat_unlock(); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], + bio_sectors(bi)); + part_stat_unlock(); if (rw == READ && mddev->reshape_position == MaxSector && diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 60592d9f43b6..f517869e8d10 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -210,15 +210,15 @@ ssize_t part_size_show(struct device *dev, return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects); } -static ssize_t part_stat_show(struct device *dev, - struct device_attribute *attr, char *buf) +ssize_t part_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); int cpu; - cpu = disk_stat_lock(); + cpu = part_stat_lock(); part_round_stats(cpu, p); - disk_stat_unlock(); + part_stat_unlock(); return sprintf(buf, "%8lu %8lu %8llu %8u " "%8lu %8lu %8llu %8u " @@ -575,8 +575,8 @@ void del_gendisk(struct gendisk *disk) set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); - disk_stat_set_all(disk, 0); - disk->stamp = 0; + part_stat_set_all(&disk->part0, 0); + disk->part0.stamp = 0; kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 3d15b42dc352..c90e1b4fbe5a 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -145,13 +145,6 @@ struct gendisk { struct timer_rand_state *random; atomic_t sync_io; /* RAID */ - unsigned long stamp; - int in_flight; -#ifdef CONFIG_SMP - struct disk_stats *dkstats; -#else - struct disk_stats dkstats; -#endif struct work_struct async_notify; #ifdef CONFIG_BLK_DEV_INTEGRITY struct blk_integrity *integrity; @@ -232,46 +225,18 @@ extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, * internal use only. */ #ifdef CONFIG_SMP -#define disk_stat_lock() ({ rcu_read_lock(); get_cpu(); }) -#define disk_stat_unlock() do { put_cpu(); rcu_read_unlock(); } while (0) - -#define disk_stat_add(cpu, gendiskp, field, addnd) \ - (per_cpu_ptr(gendiskp->dkstats, cpu)->field += addnd) - -#define disk_stat_read(gendiskp, field) \ -({ \ - typeof(gendiskp->dkstats->field) res = 0; \ - int i; \ - for_each_possible_cpu(i) \ - res += per_cpu_ptr(gendiskp->dkstats, i)->field; \ - res; \ -}) - -static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) -{ - int i; - - for_each_possible_cpu(i) - memset(per_cpu_ptr(gendiskp->dkstats, i), value, - sizeof(struct disk_stats)); -} +#define part_stat_lock() ({ rcu_read_lock(); get_cpu(); }) +#define part_stat_unlock() do { put_cpu(); rcu_read_unlock(); } while (0) -#define part_stat_add(cpu, part, field, addnd) \ - (per_cpu_ptr(part->dkstats, cpu)->field += addnd) - -#define all_stat_add(cpu, gendiskp, part, field, addnd, sector) \ -({ \ - if (part) \ - part_stat_add(cpu, part, field, addnd); \ - disk_stat_add(cpu, gendiskp, field, addnd); \ -}) +#define __part_stat_add(cpu, part, field, addnd) \ + (per_cpu_ptr((part)->dkstats, (cpu))->field += (addnd)) #define part_stat_read(part, field) \ ({ \ - typeof(part->dkstats->field) res = 0; \ + typeof((part)->dkstats->field) res = 0; \ int i; \ for_each_possible_cpu(i) \ - res += per_cpu_ptr(part->dkstats, i)->field; \ + res += per_cpu_ptr((part)->dkstats, i)->field; \ res; \ }) @@ -284,109 +249,73 @@ static inline void part_stat_set_all(struct hd_struct *part, int value) sizeof(struct disk_stats)); } -#else /* !CONFIG_SMP */ -#define disk_stat_lock() ({ rcu_read_lock(); 0; }) -#define disk_stat_unlock() rcu_read_unlock() - -#define disk_stat_add(cpu, gendiskp, field, addnd) \ - (gendiskp->dkstats.field += addnd) -#define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) - -static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) +static inline int init_part_stats(struct hd_struct *part) { - memset(&gendiskp->dkstats, value, sizeof (struct disk_stats)); + part->dkstats = alloc_percpu(struct disk_stats); + if (!part->dkstats) + return 0; + return 1; } -#define part_stat_add(cpu, part, field, addnd) \ - (part->dkstats.field += addnd) - -#define all_stat_add(cpu, gendiskp, part, field, addnd, sector) \ -({ \ - if (part) \ - part_stat_add(cpu, part, field, addnd); \ - disk_stat_add(cpu, gendiskp, field, addnd); \ -}) - -#define part_stat_read(part, field) (part->dkstats.field) - -static inline void part_stat_set_all(struct hd_struct *part, int value) +static inline void free_part_stats(struct hd_struct *part) { - memset(&part->dkstats, value, sizeof(struct disk_stats)); + free_percpu(part->dkstats); } -#endif /* CONFIG_SMP */ - -#define disk_stat_dec(cpu, gendiskp, field) \ - disk_stat_add(cpu, gendiskp, field, -1) -#define disk_stat_inc(cpu, gendiskp, field) \ - disk_stat_add(cpu, gendiskp, field, 1) -#define disk_stat_sub(cpu, gendiskp, field, subnd) \ - disk_stat_add(cpu, gendiskp, field, -subnd) - -#define part_stat_dec(cpu, gendiskp, field) \ - part_stat_add(cpu, gendiskp, field, -1) -#define part_stat_inc(cpu, gendiskp, field) \ - part_stat_add(cpu, gendiskp, field, 1) -#define part_stat_sub(cpu, gendiskp, field, subnd) \ - part_stat_add(cpu, gendiskp, field, -subnd) +#else /* !CONFIG_SMP */ +#define part_stat_lock() ({ rcu_read_lock(); 0; }) +#define part_stat_unlock() rcu_read_unlock() -#define all_stat_dec(cpu, gendiskp, field, sector) \ - all_stat_add(cpu, gendiskp, field, -1, sector) -#define all_stat_inc(cpu, gendiskp, part, field, sector) \ - all_stat_add(cpu, gendiskp, part, field, 1, sector) -#define all_stat_sub(cpu, gendiskp, part, field, subnd, sector) \ - all_stat_add(cpu, gendiskp, part, field, -subnd, sector) +#define __part_stat_add(cpu, part, field, addnd) \ + ((part)->dkstats.field += addnd) -/* Inlines to alloc and free disk stats in struct gendisk */ -#ifdef CONFIG_SMP -static inline int init_disk_stats(struct gendisk *disk) -{ - disk->dkstats = alloc_percpu(struct disk_stats); - if (!disk->dkstats) - return 0; - return 1; -} +#define part_stat_read(part, field) ((part)->dkstats.field) -static inline void free_disk_stats(struct gendisk *disk) +static inline void part_stat_set_all(struct hd_struct *part, int value) { - free_percpu(disk->dkstats); + memset(&part->dkstats, value, sizeof(struct disk_stats)); } static inline int init_part_stats(struct hd_struct *part) { - part->dkstats = alloc_percpu(struct disk_stats); - if (!part->dkstats) - return 0; return 1; } static inline void free_part_stats(struct hd_struct *part) { - free_percpu(part->dkstats); } -#else /* CONFIG_SMP */ -static inline int init_disk_stats(struct gendisk *disk) -{ - return 1; -} +#endif /* CONFIG_SMP */ -static inline void free_disk_stats(struct gendisk *disk) -{ -} +#define part_stat_add(cpu, part, field, addnd) do { \ + __part_stat_add((cpu), (part), field, addnd); \ + if ((part)->partno) \ + __part_stat_add((cpu), &part_to_disk((part))->part0, \ + field, addnd); \ +} while (0) -static inline int init_part_stats(struct hd_struct *part) +#define part_stat_dec(cpu, gendiskp, field) \ + part_stat_add(cpu, gendiskp, field, -1) +#define part_stat_inc(cpu, gendiskp, field) \ + part_stat_add(cpu, gendiskp, field, 1) +#define part_stat_sub(cpu, gendiskp, field, subnd) \ + part_stat_add(cpu, gendiskp, field, -subnd) + +static inline void part_inc_in_flight(struct hd_struct *part) { - return 1; + part->in_flight++; + if (part->partno) + part_to_disk(part)->part0.in_flight++; } -static inline void free_part_stats(struct hd_struct *part) +static inline void part_dec_in_flight(struct hd_struct *part) { + part->in_flight--; + if (part->partno) + part_to_disk(part)->part0.in_flight--; } -#endif /* CONFIG_SMP */ /* drivers/block/ll_rw_blk.c */ -extern void disk_round_stats(int cpu, struct gendisk *disk); extern void part_round_stats(int cpu, struct hd_struct *part); /* drivers/block/genhd.c */ @@ -595,6 +524,8 @@ extern void blk_unregister_region(dev_t devt, unsigned long range); extern ssize_t part_size_show(struct device *dev, struct device_attribute *attr, char *buf); +extern ssize_t part_stat_show(struct device *dev, + struct device_attribute *attr, char *buf); #ifdef CONFIG_FAIL_MAKE_REQUEST extern ssize_t part_fail_show(struct device *dev, struct device_attribute *attr, char *buf); -- cgit v1.2.3 From 540eed5637b766bb1e881ef744c42617760b4815 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:15 +0900 Subject: block: make partition array dynamic disk->__part used to be statically allocated to the maximum possible number of partitions. This patch makes partition array allocation dynamic. The added overhead is minimal as only real change is one memory dereference changed to RCU one. This saves both a bit of memory and cpu cycles iterating through unoccupied slots and makes increasing partition limit easier. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 129 ++++++++++++++++++++++++++++++++++++++++++-------- block/ioctl.c | 2 +- fs/partitions/check.c | 31 ++++++++++-- include/linux/genhd.h | 19 +++++++- 4 files changed, 154 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index e1cb96fb883e..c2b14aa69d58 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -52,14 +52,21 @@ static struct device_type disk_type; */ struct hd_struct *disk_get_part(struct gendisk *disk, int partno) { - struct hd_struct *part; + struct hd_struct *part = NULL; + struct disk_part_tbl *ptbl; - if (unlikely(partno < 0 || partno >= disk_max_parts(disk))) + if (unlikely(partno < 0)) return NULL; + rcu_read_lock(); - part = rcu_dereference(disk->__part[partno]); - if (part) - get_device(part_to_dev(part)); + + ptbl = rcu_dereference(disk->part_tbl); + if (likely(partno < ptbl->len)) { + part = rcu_dereference(ptbl->part[partno]); + if (part) + get_device(part_to_dev(part)); + } + rcu_read_unlock(); return part; @@ -80,17 +87,24 @@ EXPORT_SYMBOL_GPL(disk_get_part); void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, unsigned int flags) { + struct disk_part_tbl *ptbl; + + rcu_read_lock(); + ptbl = rcu_dereference(disk->part_tbl); + piter->disk = disk; piter->part = NULL; if (flags & DISK_PITER_REVERSE) - piter->idx = disk_max_parts(piter->disk) - 1; + piter->idx = ptbl->len - 1; else if (flags & DISK_PITER_INCL_PART0) piter->idx = 0; else piter->idx = 1; piter->flags = flags; + + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(disk_part_iter_init); @@ -105,13 +119,16 @@ EXPORT_SYMBOL_GPL(disk_part_iter_init); */ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) { + struct disk_part_tbl *ptbl; int inc, end; /* put the last partition */ disk_put_part(piter->part); piter->part = NULL; + /* get part_tbl */ rcu_read_lock(); + ptbl = rcu_dereference(piter->disk->part_tbl); /* determine iteration parameters */ if (piter->flags & DISK_PITER_REVERSE) { @@ -122,14 +139,14 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) end = 0; } else { inc = 1; - end = disk_max_parts(piter->disk); + end = ptbl->len; } /* iterate to the next partition */ for (; piter->idx != end; piter->idx += inc) { struct hd_struct *part; - part = rcu_dereference(piter->disk->__part[piter->idx]); + part = rcu_dereference(ptbl->part[piter->idx]); if (!part) continue; if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects) @@ -180,10 +197,13 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); */ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) { + struct disk_part_tbl *ptbl; int i; - for (i = 1; i < disk_max_parts(disk); i++) { - struct hd_struct *part = rcu_dereference(disk->__part[i]); + ptbl = rcu_dereference(disk->part_tbl); + + for (i = 1; i < ptbl->len; i++) { + struct hd_struct *part = rcu_dereference(ptbl->part[i]); if (part && part->start_sect <= sector && sector < part->start_sect + part->nr_sects) @@ -798,12 +818,86 @@ static struct attribute_group *disk_attr_groups[] = { NULL }; +static void disk_free_ptbl_rcu_cb(struct rcu_head *head) +{ + struct disk_part_tbl *ptbl = + container_of(head, struct disk_part_tbl, rcu_head); + + kfree(ptbl); +} + +/** + * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way + * @disk: disk to replace part_tbl for + * @new_ptbl: new part_tbl to install + * + * Replace disk->part_tbl with @new_ptbl in RCU-safe way. The + * original ptbl is freed using RCU callback. + * + * LOCKING: + * Matching bd_mutx locked. + */ +static void disk_replace_part_tbl(struct gendisk *disk, + struct disk_part_tbl *new_ptbl) +{ + struct disk_part_tbl *old_ptbl = disk->part_tbl; + + rcu_assign_pointer(disk->part_tbl, new_ptbl); + if (old_ptbl) + call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb); +} + +/** + * disk_expand_part_tbl - expand disk->part_tbl + * @disk: disk to expand part_tbl for + * @partno: expand such that this partno can fit in + * + * Expand disk->part_tbl such that @partno can fit in. disk->part_tbl + * uses RCU to allow unlocked dereferencing for stats and other stuff. + * + * LOCKING: + * Matching bd_mutex locked, might sleep. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +int disk_expand_part_tbl(struct gendisk *disk, int partno) +{ + struct disk_part_tbl *old_ptbl = disk->part_tbl; + struct disk_part_tbl *new_ptbl; + int len = old_ptbl ? old_ptbl->len : 0; + int target = partno + 1; + size_t size; + int i; + + /* disk_max_parts() is zero during initialization, ignore if so */ + if (disk_max_parts(disk) && target > disk_max_parts(disk)) + return -EINVAL; + + if (target <= len) + return 0; + + size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]); + new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id); + if (!new_ptbl) + return -ENOMEM; + + INIT_RCU_HEAD(&new_ptbl->rcu_head); + new_ptbl->len = target; + + for (i = 0; i < len; i++) + rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]); + + disk_replace_part_tbl(disk, new_ptbl); + return 0; +} + static void disk_release(struct device *dev) { struct gendisk *disk = dev_to_disk(dev); kfree(disk->random); - kfree(disk->__part); + disk_replace_part_tbl(disk, NULL); free_part_stats(&disk->part0); kfree(disk); } @@ -948,22 +1042,16 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) disk = kmalloc_node(sizeof(struct gendisk), GFP_KERNEL | __GFP_ZERO, node_id); if (disk) { - int tot_minors = minors + ext_minors; - int size = tot_minors * sizeof(struct hd_struct *); - if (!init_part_stats(&disk->part0)) { kfree(disk); return NULL; } - - disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, - node_id); - if (!disk->__part) { - free_part_stats(&disk->part0); + if (disk_expand_part_tbl(disk, 0)) { + free_part_stats(&disk->part0); kfree(disk); return NULL; } - disk->__part[0] = &disk->part0; + disk->part_tbl->part[0] = &disk->part0; disk->minors = minors; disk->ext_minors = ext_minors; @@ -973,6 +1061,7 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) device_initialize(disk_to_dev(disk)); INIT_WORK(&disk->async_notify, media_change_notify_thread); + disk->node_id = node_id; } return disk; } diff --git a/block/ioctl.c b/block/ioctl.c index 64e7c67a64b0..38bee321e1fa 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -30,7 +30,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user if (bdev != bdev->bd_contains) return -EINVAL; partno = p.pno; - if (partno <= 0 || partno >= disk_max_parts(disk)) + if (partno <= 0) return -EINVAL; switch (a.op) { case BLKPG_ADD_PARTITION: diff --git a/fs/partitions/check.c b/fs/partitions/check.c index f517869e8d10..772b2ed8d239 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -312,14 +312,18 @@ static void delete_partition_rcu_cb(struct rcu_head *head) void delete_partition(struct gendisk *disk, int partno) { + struct disk_part_tbl *ptbl = disk->part_tbl; struct hd_struct *part; - part = disk->__part[partno]; + if (partno >= ptbl->len) + return; + + part = ptbl->part[partno]; if (!part) return; blk_free_devt(part_devt(part)); - rcu_assign_pointer(disk->__part[partno], NULL); + rcu_assign_pointer(ptbl->part[partno], NULL); kobject_put(part->holder_dir); device_del(part_to_dev(part)); @@ -341,10 +345,16 @@ int add_partition(struct gendisk *disk, int partno, dev_t devt = MKDEV(0, 0); struct device *ddev = disk_to_dev(disk); struct device *pdev; + struct disk_part_tbl *ptbl; const char *dname; int err; - if (disk->__part[partno]) + err = disk_expand_part_tbl(disk, partno); + if (err) + return err; + ptbl = disk->part_tbl; + + if (ptbl->part[partno]) return -EBUSY; p = kzalloc(sizeof(*p), GFP_KERNEL); @@ -398,7 +408,7 @@ int add_partition(struct gendisk *disk, int partno, /* everything is up and running, commence */ INIT_RCU_HEAD(&p->rcu_head); - rcu_assign_pointer(disk->__part[partno], p); + rcu_assign_pointer(ptbl->part[partno], p); /* suppress uevent if the disk supresses it */ if (!ddev->uevent_suppress) @@ -487,7 +497,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) struct disk_part_iter piter; struct hd_struct *part; struct parsed_partitions *state; - int p, res; + int p, highest, res; if (bdev->bd_part_count) return -EBUSY; @@ -511,6 +521,17 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) /* tell userspace that the media / partition table may have changed */ kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); + /* Detect the highest partition number and preallocate + * disk->part_tbl. This is an optimization and not strictly + * necessary. + */ + for (p = 1, highest = 0; p < state->limit; p++) + if (state->parts[p].size) + highest = p; + + disk_expand_part_tbl(disk, highest); + + /* add partitions */ for (p = 1; p < state->limit; p++) { sector_t size = state->parts[p].size; sector_t from = state->parts[p].from; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index c90e1b4fbe5a..ecf649c3deed 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -113,6 +113,21 @@ struct hd_struct { #define GENHD_FL_UP 16 #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 +#define BLK_SCSI_MAX_CMDS (256) +#define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) + +struct blk_scsi_cmd_filter { + unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; + unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; + struct kobject kobj; +}; + +struct disk_part_tbl { + struct rcu_head rcu_head; + int len; + struct hd_struct *part[]; +}; + struct gendisk { /* major, first_minor, minors and ext_minors are input * parameters only, don't use directly. Use disk_devt() and @@ -131,7 +146,7 @@ struct gendisk { * non-critical accesses use RCU. Always access through * helpers. */ - struct hd_struct **__part; + struct disk_part_tbl *part_tbl; struct hd_struct part0; struct block_device_operations *fops; @@ -149,6 +164,7 @@ struct gendisk { #ifdef CONFIG_BLK_DEV_INTEGRITY struct blk_integrity *integrity; #endif + int node_id; }; static inline struct gendisk *part_to_disk(struct hd_struct *part) @@ -503,6 +519,7 @@ extern void blk_free_devt(dev_t devt); extern dev_t blk_lookup_devt(const char *name, int partno); extern char *disk_name (struct gendisk *hd, int partno, char *buf); +extern int disk_expand_part_tbl(struct gendisk *disk, int target); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); extern int __must_check add_partition(struct gendisk *, int, sector_t, sector_t, int); extern void delete_partition(struct gendisk *, int); -- cgit v1.2.3 From 689d6fac40b41c7bf154f362deaf442548e4dc81 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:16 +0900 Subject: block: replace @ext_minors with GENHD_FL_EXT_DEVT With previous changes, it's meaningless to limit the number of partitions. Replace @ext_minors with GENHD_FL_EXT_DEVT such that setting the flag allows the disk to have maximum number of allowed partitions (only limited by the number of entries in parsed_partitions as determined by MAX_PART constant). This kills not-too-pretty alloc_disk_ext[_node]() functions and makes @minors parameter to alloc_disk[_node]() unnecessary. The parameter is left alone to avoid disturbing the users. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 16 +--------------- drivers/ide/ide-disk.c | 14 +++++--------- drivers/scsi/sd.c | 9 ++------- fs/partitions/check.h | 4 +--- include/linux/genhd.h | 16 ++++++++-------- 5 files changed, 17 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index c2b14aa69d58..eedab5b4685b 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1024,18 +1024,9 @@ struct gendisk *alloc_disk(int minors) { return alloc_disk_node(minors, -1); } +EXPORT_SYMBOL(alloc_disk); struct gendisk *alloc_disk_node(int minors, int node_id) -{ - return alloc_disk_ext_node(minors, 0, node_id); -} - -struct gendisk *alloc_disk_ext(int minors, int ext_minors) -{ - return alloc_disk_ext_node(minors, ext_minors, -1); -} - -struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) { struct gendisk *disk; @@ -1054,7 +1045,6 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) disk->part_tbl->part[0] = &disk->part0; disk->minors = minors; - disk->ext_minors = ext_minors; rand_initialize_disk(disk); disk_to_dev(disk)->class = &block_class; disk_to_dev(disk)->type = &disk_type; @@ -1065,11 +1055,7 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) } return disk; } - -EXPORT_SYMBOL(alloc_disk); EXPORT_SYMBOL(alloc_disk_node); -EXPORT_SYMBOL(alloc_disk_ext); -EXPORT_SYMBOL(alloc_disk_ext_node); struct kobject *get_disk(struct gendisk *disk) { diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index a072df5053ae..29c8ae752683 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -41,16 +41,12 @@ #include #include -#define IDE_DISK_PARTS (1 << PARTN_BITS) - #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) -#define IDE_DISK_MINORS IDE_DISK_PARTS +#define IDE_DISK_MINORS (1 << PARTN_BITS) #else #define IDE_DISK_MINORS 1 #endif -#define IDE_DISK_EXT_MINORS (IDE_DISK_PARTS - IDE_DISK_MINORS) - struct ide_disk_obj { ide_drive_t *drive; ide_driver_t *driver; @@ -1161,8 +1157,7 @@ static int ide_disk_probe(ide_drive_t *drive) if (!idkp) goto failed; - g = alloc_disk_ext_node(IDE_DISK_MINORS, IDE_DISK_EXT_MINORS, - hwif_to_node(drive->hwif)); + g = alloc_disk_node(IDE_DISK_MINORS, hwif_to_node(drive->hwif)); if (!g) goto out_free_idkp; @@ -1189,9 +1184,10 @@ static int ide_disk_probe(ide_drive_t *drive) drive->attach = 1; g->minors = IDE_DISK_MINORS; - g->ext_minors = IDE_DISK_EXT_MINORS; g->driverfs_dev = &drive->gendev; - g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; + g->flags |= GENHD_FL_EXT_DEVT; + if (drive->removable) + g->flags |= GENHD_FL_REMOVABLE; set_capacity(g, idedisk_capacity(drive)); g->fops = &idedisk_ops; add_disk(g); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 280d231a86ed..6598024531dd 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -86,16 +86,12 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK); MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD); MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); -#define SD_PARTS 64 - #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define SD_MINORS 16 #else #define SD_MINORS 1 #endif -#define SD_EXT_MINORS (SD_PARTS - SD_MINORS) - static int sd_revalidate_disk(struct gendisk *); static int sd_probe(struct device *); static int sd_remove(struct device *); @@ -1811,7 +1807,7 @@ static int sd_probe(struct device *dev) if (!sdkp) goto out; - gd = alloc_disk_ext(SD_MINORS, SD_EXT_MINORS); + gd = alloc_disk(SD_MINORS); if (!gd) goto out_free; @@ -1856,7 +1852,6 @@ static int sd_probe(struct device *dev) gd->major = sd_major((index & 0xf0) >> 4); gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); gd->minors = SD_MINORS; - gd->ext_minors = SD_EXT_MINORS; gd->fops = &sd_fops; if (index < 26) { @@ -1880,7 +1875,7 @@ static int sd_probe(struct device *dev) blk_queue_prep_rq(sdp->request_queue, sd_prep_fn); gd->driverfs_dev = &sdp->sdev_gendev; - gd->flags = GENHD_FL_DRIVERFS; + gd->flags = GENHD_FL_EXT_DEVT | GENHD_FL_DRIVERFS; if (sdp->removable) gd->flags |= GENHD_FL_REMOVABLE; diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 17ae8ecd9e8b..98dbe1a84528 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -5,15 +5,13 @@ * add_gd_partition adds a partitions details to the devices partition * description. */ -enum { MAX_PART = 256 }; - struct parsed_partitions { char name[BDEVNAME_SIZE]; struct { sector_t from; sector_t size; int flags; - } parts[MAX_PART]; + } parts[DISK_MAX_PARTS]; int next; int limit; }; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ecf649c3deed..04524c213de1 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -58,6 +58,8 @@ enum { UNIXWARE_PARTITION = 0x63, /* Same as GNU_HURD and SCO Unix */ }; +#define DISK_MAX_PARTS 256 + #include #include #include @@ -112,6 +114,7 @@ struct hd_struct { #define GENHD_FL_CD 8 #define GENHD_FL_UP 16 #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 +#define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ #define BLK_SCSI_MAX_CMDS (256) #define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) @@ -129,15 +132,13 @@ struct disk_part_tbl { }; struct gendisk { - /* major, first_minor, minors and ext_minors are input - * parameters only, don't use directly. Use disk_devt() and - * disk_max_parts(). + /* major, first_minor and minors are input parameters only, + * don't use directly. Use disk_devt() and disk_max_parts(). */ int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ - int ext_minors; /* number of extended dynamic minors */ char disk_name[32]; /* name of major driver */ @@ -180,7 +181,9 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part) static inline int disk_max_parts(struct gendisk *disk) { - return disk->minors + disk->ext_minors; + if (disk->flags & GENHD_FL_EXT_DEVT) + return DISK_MAX_PARTS; + return disk->minors; } static inline bool disk_partitionable(struct gendisk *disk) @@ -527,9 +530,6 @@ extern void printk_all_partitions(void); extern struct gendisk *alloc_disk_node(int minors, int node_id); extern struct gendisk *alloc_disk(int minors); -extern struct gendisk *alloc_disk_ext_node(int minors, int ext_minrs, - int node_id); -extern struct gendisk *alloc_disk_ext(int minors, int ext_minors); extern struct kobject *get_disk(struct gendisk *disk); extern void put_disk(struct gendisk *disk); extern void blk_register_region(dev_t devt, unsigned long range, -- cgit v1.2.3 From 3e1a7ff8a0a7b948f2684930166954f9e8e776fe Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:17 +0900 Subject: block: allow disk to have extended device number Now that disk and partition handlings are mostly unified, it's easy to allow disk to have extended device number. This patch makes add_disk() use extended device number if disk->minors is zero. Both sd and ide-disk are updated to use this. * sd_format_disk_name() is implemented which can generically determine the drive name. This removes disk number restriction stemming from limited device names. * If sd index goes over SD_MAX_DISKS (which can be increased now BTW), sd simply doesn't initialize minors letting block layer choose extended device number. * If CONFIG_DEBUG_EXT_DEVT is set, both sd and ide-disk always set minors to 0 and use extended device numbers. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 25 ++++++++++++++++- drivers/ide/ide-disk.c | 2 +- drivers/scsi/sd.c | 74 ++++++++++++++++++++++++++++++++++++-------------- fs/partitions/check.c | 1 + include/linux/genhd.h | 3 +- 5 files changed, 82 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/block/genhd.c b/block/genhd.c index eedab5b4685b..d9de3e482d1e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -478,14 +478,37 @@ static int exact_lock(dev_t devt, void *data) * * This function registers the partitioning information in @disk * with the kernel. + * + * FIXME: error handling */ void add_disk(struct gendisk *disk) { struct backing_dev_info *bdi; + dev_t devt; int retval; + /* minors == 0 indicates to use ext devt from part0 and should + * be accompanied with EXT_DEVT flag. Make sure all + * parameters make sense. + */ + WARN_ON(disk->minors && !(disk->major || disk->first_minor)); + WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT)); + disk->flags |= GENHD_FL_UP; - disk_to_dev(disk)->devt = MKDEV(disk->major, disk->first_minor); + + retval = blk_alloc_devt(&disk->part0, &devt); + if (retval) { + WARN_ON(1); + return; + } + disk_to_dev(disk)->devt = devt; + + /* ->major and ->first_minor aren't supposed to be + * dereferenced from here on, but set them just in case. + */ + disk->major = MAJOR(devt); + disk->first_minor = MINOR(devt); + blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 29c8ae752683..33ea8c048717 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -44,7 +44,7 @@ #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define IDE_DISK_MINORS (1 << PARTN_BITS) #else -#define IDE_DISK_MINORS 1 +#define IDE_DISK_MINORS 0 #endif struct ide_disk_obj { diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 6598024531dd..bcb04b2a7676 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -89,7 +89,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define SD_MINORS 16 #else -#define SD_MINORS 1 +#define SD_MINORS 0 #endif static int sd_revalidate_disk(struct gendisk *); @@ -1769,6 +1769,52 @@ static int sd_revalidate_disk(struct gendisk *disk) return 0; } +/** + * sd_format_disk_name - format disk name + * @prefix: name prefix - ie. "sd" for SCSI disks + * @index: index of the disk to format name for + * @buf: output buffer + * @buflen: length of the output buffer + * + * SCSI disk names starts at sda. The 26th device is sdz and the + * 27th is sdaa. The last one for two lettered suffix is sdzz + * which is followed by sdaaa. + * + * This is basically 26 base counting with one extra 'nil' entry + * at the beggining from the second digit on and can be + * determined using similar method as 26 base conversion with the + * index shifted -1 after each digit is computed. + * + * CONTEXT: + * Don't care. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen) +{ + const int base = 'z' - 'a' + 1; + char *begin = buf + strlen(prefix); + char *end = buf + buflen; + char *p; + int unit; + + p = end - 1; + *p = '\0'; + unit = base; + do { + if (p == begin) + return -EINVAL; + *--p = 'a' + (index % unit); + index = (index / unit) - 1; + } while (index >= 0); + + memmove(begin, p, end - p); + memcpy(buf, prefix, strlen(prefix)); + + return 0; +} + /** * sd_probe - called during driver initialization and whenever a * new scsi device is attached to the system. It is called once @@ -1821,8 +1867,8 @@ static int sd_probe(struct device *dev) if (error) goto out_put; - error = -EBUSY; - if (index >= SD_MAX_DISKS) + error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN); + if (error) goto out_free_index; sdkp->device = sdp; @@ -1849,24 +1895,12 @@ static int sd_probe(struct device *dev) get_device(&sdp->sdev_gendev); - gd->major = sd_major((index & 0xf0) >> 4); - gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); - gd->minors = SD_MINORS; - gd->fops = &sd_fops; - - if (index < 26) { - sprintf(gd->disk_name, "sd%c", 'a' + index % 26); - } else if (index < (26 + 1) * 26) { - sprintf(gd->disk_name, "sd%c%c", - 'a' + index / 26 - 1,'a' + index % 26); - } else { - const unsigned int m1 = (index / 26 - 1) / 26 - 1; - const unsigned int m2 = (index / 26 - 1) % 26; - const unsigned int m3 = index % 26; - sprintf(gd->disk_name, "sd%c%c%c", - 'a' + m1, 'a' + m2, 'a' + m3); + if (index < SD_MAX_DISKS) { + gd->major = sd_major((index & 0xf0) >> 4); + gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); + gd->minors = SD_MINORS; } - + gd->fops = &sd_fops; gd->private_data = &sdkp->driver; gd->queue = sdkp->device->request_queue; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 772b2ed8d239..0e411603fdf5 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -593,6 +593,7 @@ void del_gendisk(struct gendisk *disk) disk_part_iter_exit(&piter); invalidate_partition(disk, 0); + blk_free_devt(disk_to_dev(disk)->devt); set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 04524c213de1..206cdf96c3a7 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -59,6 +59,7 @@ enum { }; #define DISK_MAX_PARTS 256 +#define DISK_NAME_LEN 32 #include #include @@ -140,7 +141,7 @@ struct gendisk { int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ - char disk_name[32]; /* name of major driver */ + char disk_name[DISK_NAME_LEN]; /* name of major driver */ /* Array of pointers to partitions indexed by partno. * Protected with matching bdev lock but stat and other -- cgit v1.2.3 From 18887ad910e56066233a07fd3cfb2fa11338b782 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 28 Jul 2008 13:08:45 +0200 Subject: block: make kblockd_schedule_work() take the queue as parameter Preparatory patch for checking queuing affinity. Signed-off-by: Jens Axboe --- block/as-iosched.c | 6 +++--- block/blk-core.c | 8 ++++---- block/cfq-iosched.c | 2 +- include/linux/blkdev.h | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/block/as-iosched.c b/block/as-iosched.c index cf4eb0eefbbf..80af9257e64a 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -462,7 +462,7 @@ static void as_antic_stop(struct as_data *ad) del_timer(&ad->antic_timer); ad->antic_status = ANTIC_FINISHED; /* see as_work_handler */ - kblockd_schedule_work(&ad->antic_work); + kblockd_schedule_work(ad->q, &ad->antic_work); } } @@ -483,7 +483,7 @@ static void as_antic_timeout(unsigned long data) aic = ad->io_context->aic; ad->antic_status = ANTIC_FINISHED; - kblockd_schedule_work(&ad->antic_work); + kblockd_schedule_work(q, &ad->antic_work); if (aic->ttime_samples == 0) { /* process anticipated on has exited or timed out*/ @@ -844,7 +844,7 @@ static void as_completed_request(struct request_queue *q, struct request *rq) if (ad->changed_batch && ad->nr_dispatched == 1) { ad->current_batch_expires = jiffies + ad->batch_expire[ad->batch_data_dir]; - kblockd_schedule_work(&ad->antic_work); + kblockd_schedule_work(q, &ad->antic_work); ad->changed_batch = 0; if (ad->batch_data_dir == REQ_SYNC) diff --git a/block/blk-core.c b/block/blk-core.c index 527b3382a610..9c6f818d0c33 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -305,7 +305,7 @@ void blk_unplug_timeout(unsigned long data) blk_add_trace_pdu_int(q, BLK_TA_UNPLUG_TIMER, NULL, q->rq.count[READ] + q->rq.count[WRITE]); - kblockd_schedule_work(&q->unplug_work); + kblockd_schedule_work(q, &q->unplug_work); } void blk_unplug(struct request_queue *q) @@ -346,7 +346,7 @@ void blk_start_queue(struct request_queue *q) queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { blk_plug_device(q); - kblockd_schedule_work(&q->unplug_work); + kblockd_schedule_work(q, &q->unplug_work); } } EXPORT_SYMBOL(blk_start_queue); @@ -411,7 +411,7 @@ void __blk_run_queue(struct request_queue *q) queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { blk_plug_device(q); - kblockd_schedule_work(&q->unplug_work); + kblockd_schedule_work(q, &q->unplug_work); } } } @@ -1959,7 +1959,7 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, rq->rq_disk = bio->bi_bdev->bd_disk; } -int kblockd_schedule_work(struct work_struct *work) +int kblockd_schedule_work(struct request_queue *q, struct work_struct *work) { return queue_work(kblockd_workqueue, work); } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 1e2aff812ee2..5f6fd287c185 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -244,7 +244,7 @@ static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) { if (cfqd->busy_queues) { cfq_log(cfqd, "schedule dispatch"); - kblockd_schedule_work(&cfqd->unplug_work); + kblockd_schedule_work(cfqd->queue, &cfqd->unplug_work); } } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 1adb03827bd3..10aa46c8f170 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -912,7 +912,7 @@ static inline void put_dev_sector(Sector p) } struct work_struct; -int kblockd_schedule_work(struct work_struct *work); +int kblockd_schedule_work(struct request_queue *q, struct work_struct *work); void kblockd_flush_work(struct work_struct *work); #define MODULE_ALIAS_BLOCKDEV(major,minor) \ -- cgit v1.2.3 From c7c22e4d5c1fdebfac4dba76de7d0338c2b0d832 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 13 Sep 2008 20:26:01 +0200 Subject: block: add support for IO CPU affinity This patch adds support for controlling the IO completion CPU of either all requests on a queue, or on a per-request basis. We export a sysfs variable (rq_affinity) which, if set, migrates completions of requests to the CPU that originally submitted it. A bio helper (bio_set_completion_cpu()) is also added, so that queuers can ask for completion on that specific CPU. In testing, this has been show to cut the system time by as much as 20-40% on synthetic workloads where CPU affinity is desired. This requires a little help from the architecture, so it'll only work as designed for archs that are using the new generic smp helper infrastructure. Signed-off-by: Jens Axboe --- block/blk-core.c | 46 ++++++++--------- block/blk-settings.c | 2 +- block/blk-softirq.c | 126 +++++++++++++++++++++++++++++++++++------------ block/blk-sysfs.c | 31 ++++++++++++ block/blk.h | 12 +++++ fs/bio.c | 1 + include/linux/bio.h | 11 +++++ include/linux/blkdev.h | 5 +- include/linux/elevator.h | 8 +-- 9 files changed, 182 insertions(+), 60 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 9c6f818d0c33..5484838f46e7 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -110,7 +110,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq) memset(rq, 0, sizeof(*rq)); INIT_LIST_HEAD(&rq->queuelist); - INIT_LIST_HEAD(&rq->donelist); + rq->cpu = -1; rq->q = q; rq->sector = rq->hard_sector = (sector_t) -1; INIT_HLIST_NODE(&rq->hash); @@ -322,6 +322,21 @@ void blk_unplug(struct request_queue *q) } EXPORT_SYMBOL(blk_unplug); +static void blk_invoke_request_fn(struct request_queue *q) +{ + /* + * one level of recursion is ok and is much faster than kicking + * the unplug handling + */ + if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { + q->request_fn(q); + queue_flag_clear(QUEUE_FLAG_REENTER, q); + } else { + queue_flag_set(QUEUE_FLAG_PLUGGED, q); + kblockd_schedule_work(q, &q->unplug_work); + } +} + /** * blk_start_queue - restart a previously stopped queue * @q: The &struct request_queue in question @@ -336,18 +351,7 @@ void blk_start_queue(struct request_queue *q) WARN_ON(!irqs_disabled()); queue_flag_clear(QUEUE_FLAG_STOPPED, q); - - /* - * one level of recursion is ok and is much faster than kicking - * the unplug handling - */ - if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { - q->request_fn(q); - queue_flag_clear(QUEUE_FLAG_REENTER, q); - } else { - blk_plug_device(q); - kblockd_schedule_work(q, &q->unplug_work); - } + blk_invoke_request_fn(q); } EXPORT_SYMBOL(blk_start_queue); @@ -405,15 +409,8 @@ void __blk_run_queue(struct request_queue *q) * Only recurse once to avoid overrunning the stack, let the unplug * handling reinvoke the handler shortly if we already got there. */ - if (!elv_queue_empty(q)) { - if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { - q->request_fn(q); - queue_flag_clear(QUEUE_FLAG_REENTER, q); - } else { - blk_plug_device(q); - kblockd_schedule_work(q, &q->unplug_work); - } - } + if (!elv_queue_empty(q)) + blk_invoke_request_fn(q); } EXPORT_SYMBOL(__blk_run_queue); @@ -1056,6 +1053,7 @@ EXPORT_SYMBOL(blk_put_request); void init_request_from_bio(struct request *req, struct bio *bio) { + req->cpu = bio->bi_comp_cpu; req->cmd_type = REQ_TYPE_FS; /* @@ -1198,13 +1196,15 @@ get_rq: init_request_from_bio(req, bio); spin_lock_irq(q->queue_lock); + if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) || + bio_flagged(bio, BIO_CPU_AFFINE)) + req->cpu = blk_cpu_to_group(smp_processor_id()); if (elv_queue_empty(q)) blk_plug_device(q); add_request(q, req); out: if (sync) __generic_unplug_device(q); - spin_unlock_irq(q->queue_lock); return 0; diff --git a/block/blk-settings.c b/block/blk-settings.c index d70692badcdb..a60e959a12c4 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -443,7 +443,7 @@ void blk_queue_update_dma_alignment(struct request_queue *q, int mask) } EXPORT_SYMBOL(blk_queue_update_dma_alignment); -static int __init blk_settings_init(void) +int __init blk_settings_init(void) { blk_max_low_pfn = max_low_pfn - 1; blk_max_pfn = max_pfn - 1; diff --git a/block/blk-softirq.c b/block/blk-softirq.c index 9e1c43bff662..3a1af551191e 100644 --- a/block/blk-softirq.c +++ b/block/blk-softirq.c @@ -13,6 +13,70 @@ static DEFINE_PER_CPU(struct list_head, blk_cpu_done); +/* + * Softirq action handler - move entries to local list and loop over them + * while passing them to the queue registered handler. + */ +static void blk_done_softirq(struct softirq_action *h) +{ + struct list_head *cpu_list, local_list; + + local_irq_disable(); + cpu_list = &__get_cpu_var(blk_cpu_done); + list_replace_init(cpu_list, &local_list); + local_irq_enable(); + + while (!list_empty(&local_list)) { + struct request *rq; + + rq = list_entry(local_list.next, struct request, csd.list); + list_del_init(&rq->csd.list); + rq->q->softirq_done_fn(rq); + } +} + +#if defined(CONFIG_SMP) && defined(CONFIG_USE_GENERIC_SMP_HELPERS) +static void trigger_softirq(void *data) +{ + struct request *rq = data; + unsigned long flags; + struct list_head *list; + + local_irq_save(flags); + list = &__get_cpu_var(blk_cpu_done); + list_add_tail(&rq->csd.list, list); + + if (list->next == &rq->csd.list) + raise_softirq_irqoff(BLOCK_SOFTIRQ); + + local_irq_restore(flags); +} + +/* + * Setup and invoke a run of 'trigger_softirq' on the given cpu. + */ +static int raise_blk_irq(int cpu, struct request *rq) +{ + if (cpu_online(cpu)) { + struct call_single_data *data = &rq->csd; + + data->func = trigger_softirq; + data->info = rq; + data->flags = 0; + + __smp_call_function_single(cpu, data); + return 0; + } + + return 1; +} +#else /* CONFIG_SMP && CONFIG_USE_GENERIC_SMP_HELPERS */ +static int raise_blk_irq(int cpu, struct request *rq) +{ + return 1; +} +#endif + static int __cpuinit blk_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { @@ -33,33 +97,10 @@ static int __cpuinit blk_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } - -static struct notifier_block blk_cpu_notifier __cpuinitdata = { +static struct notifier_block __cpuinitdata blk_cpu_notifier = { .notifier_call = blk_cpu_notify, }; -/* - * splice the completion data to a local structure and hand off to - * process_completion_queue() to complete the requests - */ -static void blk_done_softirq(struct softirq_action *h) -{ - struct list_head *cpu_list, local_list; - - local_irq_disable(); - cpu_list = &__get_cpu_var(blk_cpu_done); - list_replace_init(cpu_list, &local_list); - local_irq_enable(); - - while (!list_empty(&local_list)) { - struct request *rq; - - rq = list_entry(local_list.next, struct request, donelist); - list_del_init(&rq->donelist); - rq->q->softirq_done_fn(rq); - } -} - /** * blk_complete_request - end I/O on a request * @req: the request being processed @@ -71,25 +112,48 @@ static void blk_done_softirq(struct softirq_action *h) * through a softirq handler. The user must have registered a completion * callback through blk_queue_softirq_done(). **/ - void blk_complete_request(struct request *req) { - struct list_head *cpu_list; + struct request_queue *q = req->q; unsigned long flags; + int ccpu, cpu, group_cpu; - BUG_ON(!req->q->softirq_done_fn); + BUG_ON(!q->softirq_done_fn); local_irq_save(flags); + cpu = smp_processor_id(); + group_cpu = blk_cpu_to_group(cpu); - cpu_list = &__get_cpu_var(blk_cpu_done); - list_add_tail(&req->donelist, cpu_list); - raise_softirq_irqoff(BLOCK_SOFTIRQ); + /* + * Select completion CPU + */ + if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) && req->cpu != -1) + ccpu = req->cpu; + else + ccpu = cpu; + + if (ccpu == cpu || ccpu == group_cpu) { + struct list_head *list; +do_local: + list = &__get_cpu_var(blk_cpu_done); + list_add_tail(&req->csd.list, list); + + /* + * if the list only contains our just added request, + * signal a raise of the softirq. If there are already + * entries there, someone already raised the irq but it + * hasn't run yet. + */ + if (list->next == &req->csd.list) + raise_softirq_irqoff(BLOCK_SOFTIRQ); + } else if (raise_blk_irq(ccpu, req)) + goto do_local; local_irq_restore(flags); } EXPORT_SYMBOL(blk_complete_request); -int __init blk_softirq_init(void) +__init int blk_softirq_init(void) { int i; diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index b9a6ed166649..21e275d7eed9 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -156,6 +156,30 @@ static ssize_t queue_nomerges_store(struct request_queue *q, const char *page, return ret; } +static ssize_t queue_rq_affinity_show(struct request_queue *q, char *page) +{ + unsigned int set = test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags); + + return queue_var_show(set != 0, page); +} + +static ssize_t +queue_rq_affinity_store(struct request_queue *q, const char *page, size_t count) +{ + ssize_t ret = -EINVAL; +#if defined(CONFIG_USE_GENERIC_SMP_HELPERS) + unsigned long val; + + ret = queue_var_store(&val, page, count); + spin_lock_irq(q->queue_lock); + if (val) + queue_flag_set(QUEUE_FLAG_SAME_COMP, q); + else + queue_flag_clear(QUEUE_FLAG_SAME_COMP, q); + spin_unlock_irq(q->queue_lock); +#endif + return ret; +} static struct queue_sysfs_entry queue_requests_entry = { .attr = {.name = "nr_requests", .mode = S_IRUGO | S_IWUSR }, @@ -197,6 +221,12 @@ static struct queue_sysfs_entry queue_nomerges_entry = { .store = queue_nomerges_store, }; +static struct queue_sysfs_entry queue_rq_affinity_entry = { + .attr = {.name = "rq_affinity", .mode = S_IRUGO | S_IWUSR }, + .show = queue_rq_affinity_show, + .store = queue_rq_affinity_store, +}; + static struct attribute *default_attrs[] = { &queue_requests_entry.attr, &queue_ra_entry.attr, @@ -205,6 +235,7 @@ static struct attribute *default_attrs[] = { &queue_iosched_entry.attr, &queue_hw_sector_size_entry.attr, &queue_nomerges_entry.attr, + &queue_rq_affinity_entry.attr, NULL, }; diff --git a/block/blk.h b/block/blk.h index c79f30e1df52..de74254cb916 100644 --- a/block/blk.h +++ b/block/blk.h @@ -59,4 +59,16 @@ static inline int queue_congestion_off_threshold(struct request_queue *q) #endif /* BLK_DEV_INTEGRITY */ +static inline int blk_cpu_to_group(int cpu) +{ +#ifdef CONFIG_SCHED_MC + cpumask_t mask = cpu_coregroup_map(cpu); + return first_cpu(mask); +#elif defined(CONFIG_SCHED_SMT) + return first_cpu(per_cpu(cpu_sibling_map, cpu)); +#else + return cpu; +#endif +} + #endif diff --git a/fs/bio.c b/fs/bio.c index bee4deca774a..6a637b5c24b5 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -111,6 +111,7 @@ void bio_init(struct bio *bio) { memset(bio, 0, sizeof(*bio)); bio->bi_flags = 1 << BIO_UPTODATE; + bio->bi_comp_cpu = -1; atomic_set(&bio->bi_cnt, 1); } diff --git a/include/linux/bio.h b/include/linux/bio.h index 2c0c09034fd2..13aba20edb2d 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -81,6 +81,8 @@ struct bio { unsigned int bi_max_vecs; /* max bvl_vecs we can hold */ + unsigned int bi_comp_cpu; /* completion CPU */ + struct bio_vec *bi_io_vec; /* the actual vec list */ bio_end_io_t *bi_end_io; @@ -105,6 +107,7 @@ struct bio { #define BIO_BOUNCED 5 /* bio is a bounce bio */ #define BIO_USER_MAPPED 6 /* contains user pages */ #define BIO_EOPNOTSUPP 7 /* not supported */ +#define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */ #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) /* @@ -342,6 +345,14 @@ void zero_fill_bio(struct bio *bio); extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); extern unsigned int bvec_nr_vecs(unsigned short idx); +/* + * Allow queuer to specify a completion CPU for this bio + */ +static inline void bio_set_completion_cpu(struct bio *bio, unsigned int cpu) +{ + bio->bi_comp_cpu = cpu; +} + /* * bio_set is used to allow other portions of the IO system to * allocate their own private memory pools for bio and iovec structures. diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 10aa46c8f170..93204bf7b297 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -139,7 +140,8 @@ enum rq_flag_bits { */ struct request { struct list_head queuelist; - struct list_head donelist; + struct call_single_data csd; + int cpu; struct request_queue *q; @@ -420,6 +422,7 @@ struct request_queue #define QUEUE_FLAG_ELVSWITCH 8 /* don't use elevator, just do FIFO */ #define QUEUE_FLAG_BIDI 9 /* queue supports bidi requests */ #define QUEUE_FLAG_NOMERGES 10 /* disable merge attempts */ +#define QUEUE_FLAG_SAME_COMP 11 /* force complete on same CPU */ static inline int queue_is_locked(struct request_queue *q) { diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 639624b55fbe..bb791c311a56 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -173,15 +173,15 @@ enum { #define rb_entry_rq(node) rb_entry((node), struct request, rb_node) /* - * Hack to reuse the donelist list_head as the fifo time holder while + * Hack to reuse the csd.list list_head as the fifo time holder while * the request is in the io scheduler. Saves an unsigned long in rq. */ -#define rq_fifo_time(rq) ((unsigned long) (rq)->donelist.next) -#define rq_set_fifo_time(rq,exp) ((rq)->donelist.next = (void *) (exp)) +#define rq_fifo_time(rq) ((unsigned long) (rq)->csd.list.next) +#define rq_set_fifo_time(rq,exp) ((rq)->csd.list.next = (void *) (exp)) #define rq_entry_fifo(ptr) list_entry((ptr), struct request, queuelist) #define rq_fifo_clear(rq) do { \ list_del_init(&(rq)->queuelist); \ - INIT_LIST_HEAD(&(rq)->donelist); \ + INIT_LIST_HEAD(&(rq)->csd.list); \ } while (0) /* -- cgit v1.2.3 From ab780f1ece0dc8d5e8e8e85435acc5e4747ccda3 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 26 Aug 2008 10:25:02 +0200 Subject: block: inherit CPU completion on bio->rq and rq->rq merges Somewhat incomplete, as we do allow merges of requests and bios that have different completion CPUs given. This is done on the assumption that a larger IO is still more beneficial than CPU locality. Signed-off-by: Jens Axboe --- block/blk-core.c | 4 ++++ block/blk-merge.c | 2 ++ include/linux/blkdev.h | 1 + 3 files changed, 7 insertions(+) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 5484838f46e7..b9a252cae4df 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1134,6 +1134,8 @@ static int __make_request(struct request_queue *q, struct bio *bio) req->biotail = bio; req->nr_sectors = req->hard_nr_sectors += nr_sectors; req->ioprio = ioprio_best(req->ioprio, prio); + if (!blk_rq_cpu_valid(req)) + req->cpu = bio->bi_comp_cpu; drive_stat_acct(req, 0); if (!attempt_back_merge(q, req)) elv_merged_request(q, req, el_ret); @@ -1161,6 +1163,8 @@ static int __make_request(struct request_queue *q, struct bio *bio) req->sector = req->hard_sector = bio->bi_sector; req->nr_sectors = req->hard_nr_sectors += nr_sectors; req->ioprio = ioprio_best(req->ioprio, prio); + if (!blk_rq_cpu_valid(req)) + req->cpu = bio->bi_comp_cpu; drive_stat_acct(req, 0); if (!attempt_front_merge(q, req)) elv_merged_request(q, req, el_ret); diff --git a/block/blk-merge.c b/block/blk-merge.c index c77196d55899..908d3e11ac52 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -400,6 +400,8 @@ static int attempt_merge(struct request_queue *q, struct request *req, } req->ioprio = ioprio_best(req->ioprio, next->ioprio); + if (blk_rq_cpu_valid(next)) + req->cpu = next->cpu; __blk_put_request(q, next); return 1; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 93204bf7b297..12df8efeef19 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -545,6 +545,7 @@ enum { #define blk_pm_request(rq) \ (blk_pm_suspend_request(rq) || blk_pm_resume_request(rq)) +#define blk_rq_cpu_valid(rq) ((rq)->cpu != -1) #define blk_sorted_rq(rq) ((rq)->cmd_flags & REQ_SORTED) #define blk_barrier_rq(rq) ((rq)->cmd_flags & REQ_HARDBARRIER) #define blk_fua_rq(rq) ((rq)->cmd_flags & REQ_FUA) -- cgit v1.2.3 From a3bce90edd8f6cafe3f63b1a943800792e830178 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 16:17:05 +0900 Subject: block: add gfp_mask argument to blk_rq_map_user and blk_rq_map_user_iov Currently, blk_rq_map_user and blk_rq_map_user_iov always do GFP_KERNEL allocation. This adds gfp_mask argument to blk_rq_map_user and blk_rq_map_user_iov so sg can use it (sg always does GFP_ATOMIC allocation). Signed-off-by: FUJITA Tomonori Signed-off-by: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- block/blk-map.c | 20 ++++++++++++-------- block/bsg.c | 5 +++-- block/scsi_ioctl.c | 5 +++-- drivers/cdrom/cdrom.c | 2 +- drivers/scsi/scsi_tgt_lib.c | 2 +- fs/bio.c | 33 +++++++++++++++++++-------------- include/linux/bio.h | 9 +++++---- include/linux/blkdev.h | 5 +++-- 8 files changed, 47 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/block/blk-map.c b/block/blk-map.c index ea1bf53929e4..ac21b7397e15 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -41,7 +41,8 @@ static int __blk_rq_unmap_user(struct bio *bio) } static int __blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned int len) + void __user *ubuf, unsigned int len, + gfp_t gfp_mask) { unsigned long uaddr; unsigned int alignment; @@ -57,9 +58,9 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, uaddr = (unsigned long) ubuf; alignment = queue_dma_alignment(q) | q->dma_pad_mask; if (!(uaddr & alignment) && !(len & alignment)) - bio = bio_map_user(q, NULL, uaddr, len, reading); + bio = bio_map_user(q, NULL, uaddr, len, reading, gfp_mask); else - bio = bio_copy_user(q, uaddr, len, reading); + bio = bio_copy_user(q, uaddr, len, reading, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); @@ -90,6 +91,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * @rq: request structure to fill * @ubuf: the user buffer * @len: length of user data + * @gfp_mask: memory allocation flags * * Description: * Data will be mapped directly for zero copy I/O, if possible. Otherwise @@ -105,7 +107,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * unmapping. */ int blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned long len) + void __user *ubuf, unsigned long len, gfp_t gfp_mask) { unsigned long bytes_read = 0; struct bio *bio = NULL; @@ -132,7 +134,7 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, if (end - start > BIO_MAX_PAGES) map_len -= PAGE_SIZE; - ret = __blk_rq_map_user(q, rq, ubuf, map_len); + ret = __blk_rq_map_user(q, rq, ubuf, map_len, gfp_mask); if (ret < 0) goto unmap_rq; if (!bio) @@ -160,6 +162,7 @@ EXPORT_SYMBOL(blk_rq_map_user); * @iov: pointer to the iovec * @iov_count: number of elements in the iovec * @len: I/O byte count + * @gfp_mask: memory allocation flags * * Description: * Data will be mapped directly for zero copy I/O, if possible. Otherwise @@ -175,7 +178,8 @@ EXPORT_SYMBOL(blk_rq_map_user); * unmapping. */ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, - struct sg_iovec *iov, int iov_count, unsigned int len) + struct sg_iovec *iov, int iov_count, unsigned int len, + gfp_t gfp_mask) { struct bio *bio; int i, read = rq_data_dir(rq) == READ; @@ -194,9 +198,9 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, } if (unaligned || (q->dma_pad_mask & len)) - bio = bio_copy_user_iov(q, iov, iov_count, read); + bio = bio_copy_user_iov(q, iov, iov_count, read, gfp_mask); else - bio = bio_map_user_iov(q, NULL, iov, iov_count, read); + bio = bio_map_user_iov(q, NULL, iov, iov_count, read, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); diff --git a/block/bsg.c b/block/bsg.c index 0aae8d7ba99c..e7a142e9916c 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -283,7 +283,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) next_rq->cmd_type = rq->cmd_type; dxferp = (void*)(unsigned long)hdr->din_xferp; - ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len); + ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len, + GFP_KERNEL); if (ret) goto out; } @@ -298,7 +299,7 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) dxfer_len = 0; if (dxfer_len) { - ret = blk_rq_map_user(q, rq, dxferp, dxfer_len); + ret = blk_rq_map_user(q, rq, dxferp, dxfer_len, GFP_KERNEL); if (ret) goto out; } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 3aab80a4c484..f49d6a11a69e 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -315,10 +315,11 @@ static int sg_io(struct file *file, struct request_queue *q, } ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count, - hdr->dxfer_len); + hdr->dxfer_len, GFP_KERNEL); kfree(iov); } else if (hdr->dxfer_len) - ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); + ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len, + GFP_KERNEL); if (ret) goto out; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 74031de517e6..e861d24a6d32 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2097,7 +2097,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, len = nr * CD_FRAMESIZE_RAW; - ret = blk_rq_map_user(q, rq, ubuf, len); + ret = blk_rq_map_user(q, rq, ubuf, len, GFP_KERNEL); if (ret) break; diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 257e097c39af..2a4fd820d616 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -362,7 +362,7 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, int err; dprintk("%lx %u\n", uaddr, len); - err = blk_rq_map_user(q, rq, (void *)uaddr, len); + err = blk_rq_map_user(q, rq, (void *)uaddr, len, GFP_KERNEL); if (err) { /* * TODO: need to fixup sg_tablesize, max_segment_size, diff --git a/fs/bio.c b/fs/bio.c index 6a637b5c24b5..3d2e9ad24728 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -558,13 +558,14 @@ int bio_uncopy_user(struct bio *bio) * @iov: the iovec. * @iov_count: number of elements in the iovec * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Prepares and returns a bio for indirect user io, bouncing data * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, - int iov_count, int write_to_vm) + int iov_count, int write_to_vm, gfp_t gfp_mask) { struct bio_map_data *bmd; struct bio_vec *bvec; @@ -587,12 +588,12 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, len += iov[i].iov_len; } - bmd = bio_alloc_map_data(nr_pages, iov_count, GFP_KERNEL); + bmd = bio_alloc_map_data(nr_pages, iov_count, gfp_mask); if (!bmd) return ERR_PTR(-ENOMEM); ret = -ENOMEM; - bio = bio_alloc(GFP_KERNEL, nr_pages); + bio = bio_alloc(gfp_mask, nr_pages); if (!bio) goto out_bmd; @@ -605,7 +606,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, if (bytes > len) bytes = len; - page = alloc_page(q->bounce_gfp | GFP_KERNEL); + page = alloc_page(q->bounce_gfp | gfp_mask); if (!page) { ret = -ENOMEM; break; @@ -647,26 +648,27 @@ out_bmd: * @uaddr: start of user address * @len: length in bytes * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Prepares and returns a bio for indirect user io, bouncing data * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr, - unsigned int len, int write_to_vm) + unsigned int len, int write_to_vm, gfp_t gfp_mask) { struct sg_iovec iov; iov.iov_base = (void __user *)uaddr; iov.iov_len = len; - return bio_copy_user_iov(q, &iov, 1, write_to_vm); + return bio_copy_user_iov(q, &iov, 1, write_to_vm, gfp_mask); } static struct bio *__bio_map_user_iov(struct request_queue *q, struct block_device *bdev, struct sg_iovec *iov, int iov_count, - int write_to_vm) + int write_to_vm, gfp_t gfp_mask) { int i, j; int nr_pages = 0; @@ -692,12 +694,12 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, if (!nr_pages) return ERR_PTR(-EINVAL); - bio = bio_alloc(GFP_KERNEL, nr_pages); + bio = bio_alloc(gfp_mask, nr_pages); if (!bio) return ERR_PTR(-ENOMEM); ret = -ENOMEM; - pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); + pages = kcalloc(nr_pages, sizeof(struct page *), gfp_mask); if (!pages) goto out; @@ -776,19 +778,21 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, * @uaddr: start of user address * @len: length in bytes * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Map the user space address into a bio suitable for io to a block * device. Returns an error pointer in case of error. */ struct bio *bio_map_user(struct request_queue *q, struct block_device *bdev, - unsigned long uaddr, unsigned int len, int write_to_vm) + unsigned long uaddr, unsigned int len, int write_to_vm, + gfp_t gfp_mask) { struct sg_iovec iov; iov.iov_base = (void __user *)uaddr; iov.iov_len = len; - return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm); + return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm, gfp_mask); } /** @@ -798,18 +802,19 @@ struct bio *bio_map_user(struct request_queue *q, struct block_device *bdev, * @iov: the iovec. * @iov_count: number of elements in the iovec * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Map the user space address into a bio suitable for io to a block * device. Returns an error pointer in case of error. */ struct bio *bio_map_user_iov(struct request_queue *q, struct block_device *bdev, struct sg_iovec *iov, int iov_count, - int write_to_vm) + int write_to_vm, gfp_t gfp_mask) { struct bio *bio; - bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm); - + bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm, + gfp_mask); if (IS_ERR(bio)) return bio; diff --git a/include/linux/bio.h b/include/linux/bio.h index 13aba20edb2d..200b185c3e83 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -325,11 +325,11 @@ extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *, unsigned int, unsigned int); extern int bio_get_nr_vecs(struct block_device *); extern struct bio *bio_map_user(struct request_queue *, struct block_device *, - unsigned long, unsigned int, int); + unsigned long, unsigned int, int, gfp_t); struct sg_iovec; extern struct bio *bio_map_user_iov(struct request_queue *, struct block_device *, - struct sg_iovec *, int, int); + struct sg_iovec *, int, int, gfp_t); extern void bio_unmap_user(struct bio *); extern struct bio *bio_map_kern(struct request_queue *, void *, unsigned int, gfp_t); @@ -337,9 +337,10 @@ extern struct bio *bio_copy_kern(struct request_queue *, void *, unsigned int, gfp_t, int); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -extern struct bio *bio_copy_user(struct request_queue *, unsigned long, unsigned int, int); +extern struct bio *bio_copy_user(struct request_queue *, unsigned long, + unsigned int, int, gfp_t); extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, - int, int); + int, int, gfp_t); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 12df8efeef19..00e388d0e221 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -710,11 +710,12 @@ extern void __blk_stop_queue(struct request_queue *q); extern void __blk_run_queue(struct request_queue *); extern void blk_run_queue(struct request_queue *); extern void blk_start_queueing(struct request_queue *); -extern int blk_rq_map_user(struct request_queue *, struct request *, void __user *, unsigned long); +extern int blk_rq_map_user(struct request_queue *, struct request *, + void __user *, unsigned long, gfp_t); extern int blk_rq_unmap_user(struct bio *); extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, unsigned int, gfp_t); extern int blk_rq_map_user_iov(struct request_queue *, struct request *, - struct sg_iovec *, int, unsigned int); + struct sg_iovec *, int, unsigned int, gfp_t); extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, -- cgit v1.2.3 From 152e283fdfea0cd11e297d982378b55937842dde Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 16:17:06 +0900 Subject: block: introduce struct rq_map_data to use reserved pages This patch introduces struct rq_map_data to enable bio_copy_use_iov() use reserved pages. Currently, bio_copy_user_iov allocates bounce pages but drivers/scsi/sg.c wants to allocate pages by itself and use them. struct rq_map_data can be used to pass allocated pages to bio_copy_user_iov. The current users of bio_copy_user_iov simply passes NULL (they don't want to use pre-allocated pages). Signed-off-by: FUJITA Tomonori Cc: Jens Axboe Cc: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- block/blk-map.c | 26 ++++++++++++-------- block/bsg.c | 7 +++--- block/scsi_ioctl.c | 4 ++-- drivers/cdrom/cdrom.c | 2 +- drivers/scsi/scsi_tgt_lib.c | 2 +- fs/bio.c | 58 ++++++++++++++++++++++++++++++++------------- include/linux/bio.h | 8 ++++--- include/linux/blkdev.h | 12 ++++++++-- 8 files changed, 80 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/block/blk-map.c b/block/blk-map.c index ac21b7397e15..dad6a2907835 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -41,8 +41,8 @@ static int __blk_rq_unmap_user(struct bio *bio) } static int __blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned int len, - gfp_t gfp_mask) + struct rq_map_data *map_data, void __user *ubuf, + unsigned int len, gfp_t gfp_mask) { unsigned long uaddr; unsigned int alignment; @@ -57,10 +57,10 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, */ uaddr = (unsigned long) ubuf; alignment = queue_dma_alignment(q) | q->dma_pad_mask; - if (!(uaddr & alignment) && !(len & alignment)) + if (!(uaddr & alignment) && !(len & alignment) && !map_data) bio = bio_map_user(q, NULL, uaddr, len, reading, gfp_mask); else - bio = bio_copy_user(q, uaddr, len, reading, gfp_mask); + bio = bio_copy_user(q, map_data, uaddr, len, reading, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); @@ -89,6 +89,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * blk_rq_map_user - map user data to a request, for REQ_TYPE_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request structure to fill + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @ubuf: the user buffer * @len: length of user data * @gfp_mask: memory allocation flags @@ -107,7 +108,8 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * unmapping. */ int blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned long len, gfp_t gfp_mask) + struct rq_map_data *map_data, void __user *ubuf, + unsigned long len, gfp_t gfp_mask) { unsigned long bytes_read = 0; struct bio *bio = NULL; @@ -134,7 +136,8 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, if (end - start > BIO_MAX_PAGES) map_len -= PAGE_SIZE; - ret = __blk_rq_map_user(q, rq, ubuf, map_len, gfp_mask); + ret = __blk_rq_map_user(q, rq, map_data, ubuf, map_len, + gfp_mask); if (ret < 0) goto unmap_rq; if (!bio) @@ -159,6 +162,7 @@ EXPORT_SYMBOL(blk_rq_map_user); * blk_rq_map_user_iov - map user data to a request, for REQ_TYPE_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request to map data to + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @iov: pointer to the iovec * @iov_count: number of elements in the iovec * @len: I/O byte count @@ -178,8 +182,8 @@ EXPORT_SYMBOL(blk_rq_map_user); * unmapping. */ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, - struct sg_iovec *iov, int iov_count, unsigned int len, - gfp_t gfp_mask) + struct rq_map_data *map_data, struct sg_iovec *iov, + int iov_count, unsigned int len, gfp_t gfp_mask) { struct bio *bio; int i, read = rq_data_dir(rq) == READ; @@ -197,8 +201,9 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, } } - if (unaligned || (q->dma_pad_mask & len)) - bio = bio_copy_user_iov(q, iov, iov_count, read, gfp_mask); + if (unaligned || (q->dma_pad_mask & len) || map_data) + bio = bio_copy_user_iov(q, map_data, iov, iov_count, read, + gfp_mask); else bio = bio_map_user_iov(q, NULL, iov, iov_count, read, gfp_mask); @@ -220,6 +225,7 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, rq->buffer = rq->data = NULL; return 0; } +EXPORT_SYMBOL(blk_rq_map_user_iov); /** * blk_rq_unmap_user - unmap a request with user data diff --git a/block/bsg.c b/block/bsg.c index e7a142e9916c..56cb343c76d8 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -283,8 +283,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) next_rq->cmd_type = rq->cmd_type; dxferp = (void*)(unsigned long)hdr->din_xferp; - ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len, - GFP_KERNEL); + ret = blk_rq_map_user(q, next_rq, NULL, dxferp, + hdr->din_xfer_len, GFP_KERNEL); if (ret) goto out; } @@ -299,7 +299,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) dxfer_len = 0; if (dxfer_len) { - ret = blk_rq_map_user(q, rq, dxferp, dxfer_len, GFP_KERNEL); + ret = blk_rq_map_user(q, rq, NULL, dxferp, dxfer_len, + GFP_KERNEL); if (ret) goto out; } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index f49d6a11a69e..c34272a348fe 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -314,11 +314,11 @@ static int sg_io(struct file *file, struct request_queue *q, goto out; } - ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count, + ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count, hdr->dxfer_len, GFP_KERNEL); kfree(iov); } else if (hdr->dxfer_len) - ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len, + ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, GFP_KERNEL); if (ret) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e861d24a6d32..d47f2f80accd 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2097,7 +2097,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, len = nr * CD_FRAMESIZE_RAW; - ret = blk_rq_map_user(q, rq, ubuf, len, GFP_KERNEL); + ret = blk_rq_map_user(q, rq, NULL, ubuf, len, GFP_KERNEL); if (ret) break; diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 2a4fd820d616..3117bb106b5d 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -362,7 +362,7 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, int err; dprintk("%lx %u\n", uaddr, len); - err = blk_rq_map_user(q, rq, (void *)uaddr, len, GFP_KERNEL); + err = blk_rq_map_user(q, rq, NULL, (void *)uaddr, len, GFP_KERNEL); if (err) { /* * TODO: need to fixup sg_tablesize, max_segment_size, diff --git a/fs/bio.c b/fs/bio.c index 3d2e9ad24728..a2f072647cdf 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -439,16 +439,19 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len, struct bio_map_data { struct bio_vec *iovecs; - int nr_sgvecs; struct sg_iovec *sgvecs; + int nr_sgvecs; + int is_our_pages; }; static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio, - struct sg_iovec *iov, int iov_count) + struct sg_iovec *iov, int iov_count, + int is_our_pages) { memcpy(bmd->iovecs, bio->bi_io_vec, sizeof(struct bio_vec) * bio->bi_vcnt); memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count); bmd->nr_sgvecs = iov_count; + bmd->is_our_pages = is_our_pages; bio->bi_private = bmd; } @@ -483,7 +486,8 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs, int iov_count, } static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, - struct sg_iovec *iov, int iov_count, int uncopy) + struct sg_iovec *iov, int iov_count, int uncopy, + int do_free_page) { int ret = 0, i; struct bio_vec *bvec; @@ -526,7 +530,7 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, } } - if (uncopy) + if (do_free_page) __free_page(bvec->bv_page); } @@ -545,7 +549,8 @@ int bio_uncopy_user(struct bio *bio) struct bio_map_data *bmd = bio->bi_private; int ret; - ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, bmd->nr_sgvecs, 1); + ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, bmd->nr_sgvecs, 1, + bmd->is_our_pages); bio_free_map_data(bmd); bio_put(bio); @@ -555,6 +560,7 @@ int bio_uncopy_user(struct bio *bio) /** * bio_copy_user_iov - copy user data to bio * @q: destination block queue + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @iov: the iovec. * @iov_count: number of elements in the iovec * @write_to_vm: bool indicating writing to pages or not @@ -564,8 +570,10 @@ int bio_uncopy_user(struct bio *bio) * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ -struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, - int iov_count, int write_to_vm, gfp_t gfp_mask) +struct bio *bio_copy_user_iov(struct request_queue *q, + struct rq_map_data *map_data, + struct sg_iovec *iov, int iov_count, + int write_to_vm, gfp_t gfp_mask) { struct bio_map_data *bmd; struct bio_vec *bvec; @@ -600,13 +608,26 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, bio->bi_rw |= (!write_to_vm << BIO_RW); ret = 0; + i = 0; while (len) { - unsigned int bytes = PAGE_SIZE; + unsigned int bytes; + + if (map_data) + bytes = 1U << (PAGE_SHIFT + map_data->page_order); + else + bytes = PAGE_SIZE; if (bytes > len) bytes = len; - page = alloc_page(q->bounce_gfp | gfp_mask); + if (map_data) { + if (i == map_data->nr_entries) { + ret = -ENOMEM; + break; + } + page = map_data->pages[i++]; + } else + page = alloc_page(q->bounce_gfp | gfp_mask); if (!page) { ret = -ENOMEM; break; @@ -625,16 +646,17 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, * success */ if (!write_to_vm) { - ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0); + ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 0); if (ret) goto cleanup; } - bio_set_map_data(bmd, bio, iov, iov_count); + bio_set_map_data(bmd, bio, iov, iov_count, map_data ? 0 : 1); return bio; cleanup: - bio_for_each_segment(bvec, bio, i) - __free_page(bvec->bv_page); + if (!map_data) + bio_for_each_segment(bvec, bio, i) + __free_page(bvec->bv_page); bio_put(bio); out_bmd: @@ -645,6 +667,7 @@ out_bmd: /** * bio_copy_user - copy user data to bio * @q: destination block queue + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @uaddr: start of user address * @len: length in bytes * @write_to_vm: bool indicating writing to pages or not @@ -654,15 +677,16 @@ out_bmd: * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ -struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr, - unsigned int len, int write_to_vm, gfp_t gfp_mask) +struct bio *bio_copy_user(struct request_queue *q, struct rq_map_data *map_data, + unsigned long uaddr, unsigned int len, + int write_to_vm, gfp_t gfp_mask) { struct sg_iovec iov; iov.iov_base = (void __user *)uaddr; iov.iov_len = len; - return bio_copy_user_iov(q, &iov, 1, write_to_vm, gfp_mask); + return bio_copy_user_iov(q, map_data, &iov, 1, write_to_vm, gfp_mask); } static struct bio *__bio_map_user_iov(struct request_queue *q, @@ -1028,7 +1052,7 @@ struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, bio->bi_private = bmd; bio->bi_end_io = bio_copy_kern_endio; - bio_set_map_data(bmd, bio, &iov, 1); + bio_set_map_data(bmd, bio, &iov, 1, 1); return bio; cleanup: bio_for_each_segment(bvec, bio, i) diff --git a/include/linux/bio.h b/include/linux/bio.h index 200b185c3e83..bc386cd5e996 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -327,6 +327,7 @@ extern int bio_get_nr_vecs(struct block_device *); extern struct bio *bio_map_user(struct request_queue *, struct block_device *, unsigned long, unsigned int, int, gfp_t); struct sg_iovec; +struct rq_map_data; extern struct bio *bio_map_user_iov(struct request_queue *, struct block_device *, struct sg_iovec *, int, int, gfp_t); @@ -337,9 +338,10 @@ extern struct bio *bio_copy_kern(struct request_queue *, void *, unsigned int, gfp_t, int); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -extern struct bio *bio_copy_user(struct request_queue *, unsigned long, - unsigned int, int, gfp_t); -extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, +extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *, + unsigned long, unsigned int, int, gfp_t); +extern struct bio *bio_copy_user_iov(struct request_queue *, + struct rq_map_data *, struct sg_iovec *, int, int, gfp_t); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 00e388d0e221..358ac423ed2f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -642,6 +642,12 @@ static inline void blk_queue_bounce(struct request_queue *q, struct bio **bio) } #endif /* CONFIG_MMU */ +struct rq_map_data { + struct page **pages; + int page_order; + int nr_entries; +}; + struct req_iterator { int i; struct bio *bio; @@ -711,11 +717,13 @@ extern void __blk_run_queue(struct request_queue *); extern void blk_run_queue(struct request_queue *); extern void blk_start_queueing(struct request_queue *); extern int blk_rq_map_user(struct request_queue *, struct request *, - void __user *, unsigned long, gfp_t); + struct rq_map_data *, void __user *, unsigned long, + gfp_t); extern int blk_rq_unmap_user(struct bio *); extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, unsigned int, gfp_t); extern int blk_rq_map_user_iov(struct request_queue *, struct request *, - struct sg_iovec *, int, unsigned int, gfp_t); + struct rq_map_data *, struct sg_iovec *, int, + unsigned int, gfp_t); extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, -- cgit v1.2.3 From 879040742cf09f2360a9ac41846288707e4e567c Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 15:05:58 +0900 Subject: block: add blk_rq_aligned helper function This adds blk_rq_aligned helper function to see if alignment and padding requirement is satisfied for DMA transfer. This also converts blk_rq_map_kern and __blk_rq_map_user to use the helper function. Signed-off-by: FUJITA Tomonori Cc: Jens Axboe Signed-off-by: Jens Axboe --- block/blk-map.c | 12 ++---------- include/linux/blkdev.h | 7 +++++++ 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/block/blk-map.c b/block/blk-map.c index dad6a2907835..572140cda5ff 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -45,7 +45,6 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, unsigned int len, gfp_t gfp_mask) { unsigned long uaddr; - unsigned int alignment; struct bio *bio, *orig_bio; int reading, ret; @@ -56,8 +55,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * direct dma. else, set up kernel bounce buffers */ uaddr = (unsigned long) ubuf; - alignment = queue_dma_alignment(q) | q->dma_pad_mask; - if (!(uaddr & alignment) && !(len & alignment) && !map_data) + if (blk_rq_aligned(q, ubuf, len) && !map_data) bio = bio_map_user(q, NULL, uaddr, len, reading, gfp_mask); else bio = bio_copy_user(q, map_data, uaddr, len, reading, gfp_mask); @@ -274,8 +272,6 @@ EXPORT_SYMBOL(blk_rq_unmap_user); int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, unsigned int len, gfp_t gfp_mask) { - unsigned long kaddr; - unsigned int alignment; int reading = rq_data_dir(rq) == READ; int do_copy = 0; struct bio *bio; @@ -285,11 +281,7 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, if (!len || !kbuf) return -EINVAL; - kaddr = (unsigned long)kbuf; - alignment = queue_dma_alignment(q) | q->dma_pad_mask; - do_copy = ((kaddr & alignment) || (len & alignment) || - object_is_on_stack(kbuf)); - + do_copy = !blk_rq_aligned(q, kbuf, len) || object_is_on_stack(kbuf); if (do_copy) bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading); else diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 358ac423ed2f..9c2549260427 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -899,6 +899,13 @@ static inline int queue_dma_alignment(struct request_queue *q) return q ? q->dma_alignment : 511; } +static inline int blk_rq_aligned(struct request_queue *q, void *addr, + unsigned int len) +{ + unsigned int alignment = queue_dma_alignment(q) | q->dma_pad_mask; + return !((unsigned long)addr & alignment) && !(len & alignment); +} + /* assumes size > 256 */ static inline unsigned int blksize_bits(unsigned int size) { -- cgit v1.2.3 From 818827669d85b84241696ffef2de485db46b0b5e Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 16:20:19 +0900 Subject: block: make blk_rq_map_user take a NULL user-space buffer This patch changes blk_rq_map_user to accept a NULL user-space buffer with a READ command if rq_map_data is not NULL. Thus a caller can pass page frames to lk_rq_map_user to just set up a request and bios with page frames propely. bio_uncopy_user (called via blk_rq_unmap_user) doesn't copy data to user space with such request. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- block/blk-map.c | 16 ++++++++++++---- fs/bio.c | 8 ++++---- include/linux/bio.h | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/block/blk-map.c b/block/blk-map.c index 572140cda5ff..4849fa36161e 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -42,7 +42,7 @@ static int __blk_rq_unmap_user(struct bio *bio) static int __blk_rq_map_user(struct request_queue *q, struct request *rq, struct rq_map_data *map_data, void __user *ubuf, - unsigned int len, gfp_t gfp_mask) + unsigned int len, int null_mapped, gfp_t gfp_mask) { unsigned long uaddr; struct bio *bio, *orig_bio; @@ -63,6 +63,9 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, if (IS_ERR(bio)) return PTR_ERR(bio); + if (null_mapped) + bio->bi_flags |= (1 << BIO_NULL_MAPPED); + orig_bio = bio; blk_queue_bounce(q, &bio); @@ -111,12 +114,17 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, { unsigned long bytes_read = 0; struct bio *bio = NULL; - int ret; + int ret, null_mapped = 0; if (len > (q->max_hw_sectors << 9)) return -EINVAL; - if (!len || !ubuf) + if (!len) return -EINVAL; + if (!ubuf) { + if (!map_data || rq_data_dir(rq) != READ) + return -EINVAL; + null_mapped = 1; + } while (bytes_read != len) { unsigned long map_len, end, start; @@ -135,7 +143,7 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, map_len -= PAGE_SIZE; ret = __blk_rq_map_user(q, rq, map_data, ubuf, map_len, - gfp_mask); + null_mapped, gfp_mask); if (ret < 0) goto unmap_rq; if (!bio) diff --git a/fs/bio.c b/fs/bio.c index 9d68ddb89b71..355302985e22 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -547,11 +547,11 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, int bio_uncopy_user(struct bio *bio) { struct bio_map_data *bmd = bio->bi_private; - int ret; - - ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, bmd->nr_sgvecs, 1, - bmd->is_our_pages); + int ret = 0; + if (!bio_flagged(bio, BIO_NULL_MAPPED)) + ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, + bmd->nr_sgvecs, 1, bmd->is_our_pages); bio_free_map_data(bmd); bio_put(bio); return ret; diff --git a/include/linux/bio.h b/include/linux/bio.h index bc386cd5e996..7af373f253dc 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -108,6 +108,7 @@ struct bio { #define BIO_USER_MAPPED 6 /* contains user pages */ #define BIO_EOPNOTSUPP 7 /* not supported */ #define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */ +#define BIO_NULL_MAPPED 9 /* contains invalid user pages */ #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) /* -- cgit v1.2.3 From 0c002c2f74e10baa9021d3ecc50585c6eafea568 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 4 Sep 2008 14:27:20 -0600 Subject: Wrapper for lower-level revalidate_disk routines. This is a wrapper for the lower-level revalidate_disk call-backs such as sd_revalidate_disk(). It allows us to perform pre and post operations when calling them. We will use this wrapper in a later patch to adjust block device sizes after an online resize (a _post_ operation). Signed-off-by: Andrew Patterson Signed-off-by: Jens Axboe --- fs/block_dev.c | 21 +++++++++++++++++++++ include/linux/fs.h | 1 + 2 files changed, 22 insertions(+) (limited to 'include/linux') diff --git a/fs/block_dev.c b/fs/block_dev.c index c3fa19bd64df..4eeb69a88734 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -852,6 +852,27 @@ struct block_device *open_by_devnum(dev_t dev, unsigned mode) EXPORT_SYMBOL(open_by_devnum); +/** + * revalidate_disk - wrapper for lower-level driver's revalidate_disk + * call-back + * + * @disk: struct gendisk to be revalidated + * + * This routine is a wrapper for lower-level driver's revalidate_disk + * call-backs. It is used to do common pre and post operations needed + * for all revalidate_disk operations. + */ +int revalidate_disk(struct gendisk *disk) +{ + int ret = 0; + + if (disk->fops->revalidate_disk) + ret = disk->fops->revalidate_disk(disk); + + return ret; +} +EXPORT_SYMBOL(revalidate_disk); + /* * This routine checks whether a removable media has been changed, * and invalidates all buffer-cache-entries in that case. This diff --git a/include/linux/fs.h b/include/linux/fs.h index 02a9fb5a830c..d63461f97983 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1722,6 +1722,7 @@ extern int fs_may_remount_ro(struct super_block *); */ #define bio_data_dir(bio) ((bio)->bi_rw & 1) +extern int revalidate_disk(struct gendisk *); extern int check_disk_change(struct block_device *); extern int __invalidate_device(struct block_device *); extern int invalidate_partition(struct gendisk *, int); -- cgit v1.2.3 From c3279d1454cdfed02a557d789d8a6d08ab4cbe70 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 4 Sep 2008 14:27:25 -0600 Subject: Adjust block device size after an online resize of a disk. The revalidate_disk routine now checks if a disk has been resized by comparing the gendisk capacity to the bdev inode size. If they are different (usually because the disk has been resized underneath the kernel) the bdev inode size is adjusted to match the capacity. Signed-off-by: Andrew Patterson Signed-off-by: Jens Axboe --- fs/block_dev.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 39 insertions(+) (limited to 'include/linux') diff --git a/fs/block_dev.c b/fs/block_dev.c index 4eeb69a88734..b721955d382e 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -852,6 +852,34 @@ struct block_device *open_by_devnum(dev_t dev, unsigned mode) EXPORT_SYMBOL(open_by_devnum); +/** + * check_disk_size_change - checks for disk size change and adjusts + * bdev size. + * + * @disk: struct gendisk to check + * @bdev: struct bdev to adjust. + * + * This routine checks to see if the bdev size does not match the disk size + * and adjusts it if it differs. + */ +void check_disk_size_change(struct gendisk *disk, struct block_device *bdev) +{ + loff_t disk_size, bdev_size; + + disk_size = (loff_t)get_capacity(disk) << 9; + bdev_size = i_size_read(bdev->bd_inode); + if (disk_size != bdev_size) { + char name[BDEVNAME_SIZE]; + + disk_name(disk, 0, name); + printk(KERN_INFO + "%s: detected capacity change from %lld to %lld\n", + name, bdev_size, disk_size); + i_size_write(bdev->bd_inode, disk_size); + } +} +EXPORT_SYMBOL(check_disk_size_change); + /** * revalidate_disk - wrapper for lower-level driver's revalidate_disk * call-back @@ -864,11 +892,20 @@ EXPORT_SYMBOL(open_by_devnum); */ int revalidate_disk(struct gendisk *disk) { + struct block_device *bdev; int ret = 0; if (disk->fops->revalidate_disk) ret = disk->fops->revalidate_disk(disk); + bdev = bdget_disk(disk, 0); + if (!bdev) + return ret; + + mutex_lock(&bdev->bd_mutex); + check_disk_size_change(disk, bdev); + mutex_unlock(&bdev->bd_mutex); + bdput(bdev); return ret; } EXPORT_SYMBOL(revalidate_disk); diff --git a/include/linux/fs.h b/include/linux/fs.h index d63461f97983..32477e8872d5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1722,6 +1722,8 @@ extern int fs_may_remount_ro(struct super_block *); */ #define bio_data_dir(bio) ((bio)->bi_rw & 1) +extern void check_disk_size_change(struct gendisk *disk, + struct block_device *bdev); extern int revalidate_disk(struct gendisk *); extern int check_disk_change(struct block_device *); extern int __invalidate_device(struct block_device *); -- cgit v1.2.3 From 242f9dcb8ba6f68fcd217a119a7648a4f69290e9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 14 Sep 2008 05:55:09 -0700 Subject: block: unify request timeout handling Right now SCSI and others do their own command timeout handling. Move those bits to the block layer. Instead of having a timer per command, we try to be a bit more clever and simply have one per-queue. This avoids the overhead of having to tear down and setup a timer for each command, so it will result in a lot less timer fiddling. Signed-off-by: Mike Anderson Signed-off-by: Jens Axboe --- block/Makefile | 4 +- block/blk-core.c | 7 ++ block/blk-settings.c | 12 +++ block/blk-softirq.c | 30 ++++--- block/blk-timeout.c | 155 +++++++++++++++++++++++++++++++++++ block/blk.h | 24 ++++++ block/elevator.c | 8 ++ drivers/ata/libata-eh.c | 13 +-- drivers/ata/libata.h | 2 +- drivers/scsi/aacraid/aachba.c | 2 +- drivers/scsi/gdth.c | 60 +++++++++----- drivers/scsi/gdth.h | 2 +- drivers/scsi/gdth_proc.c | 66 --------------- drivers/scsi/gdth_proc.h | 3 - drivers/scsi/ibmvscsi/ibmvscsi.c | 2 +- drivers/scsi/ide-scsi.c | 2 +- drivers/scsi/ipr.c | 3 +- drivers/scsi/ips.c | 2 +- drivers/scsi/libiscsi.c | 17 ++-- drivers/scsi/libsas/sas_ata.c | 2 +- drivers/scsi/libsas/sas_internal.h | 2 +- drivers/scsi/libsas/sas_scsi_host.c | 30 +++---- drivers/scsi/megaraid/megaraid_sas.c | 6 +- drivers/scsi/ncr53c8xx.c | 4 +- drivers/scsi/qla1280.c | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 4 +- drivers/scsi/scsi.c | 92 ++++----------------- drivers/scsi/scsi_error.c | 90 +++----------------- drivers/scsi/scsi_lib.c | 17 +++- drivers/scsi/scsi_priv.h | 7 +- drivers/scsi/scsi_sysfs.c | 7 +- drivers/scsi/scsi_transport_fc.c | 6 +- drivers/scsi/sd.c | 9 +- drivers/scsi/sr.c | 5 +- drivers/scsi/sym53c8xx_2/sym_glue.c | 4 +- include/linux/blkdev.h | 20 +++++ include/scsi/scsi_cmnd.h | 3 - include/scsi/scsi_host.h | 9 +- include/scsi/scsi_transport.h | 3 +- 39 files changed, 399 insertions(+), 339 deletions(-) create mode 100644 block/blk-timeout.c (limited to 'include/linux') diff --git a/block/Makefile b/block/Makefile index 0da976ce67dd..bfe73049f939 100644 --- a/block/Makefile +++ b/block/Makefile @@ -4,8 +4,8 @@ obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \ blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \ - blk-exec.o blk-merge.o blk-softirq.o ioctl.o genhd.o \ - scsi_ioctl.o cmd-filter.o + blk-exec.o blk-merge.o blk-softirq.o blk-timeout.o \ + ioctl.o genhd.o scsi_ioctl.o cmd-filter.o obj-$(CONFIG_BLK_DEV_BSG) += bsg.o obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o diff --git a/block/blk-core.c b/block/blk-core.c index f25eb9786d94..d768a8ddc173 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -110,6 +110,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq) memset(rq, 0, sizeof(*rq)); INIT_LIST_HEAD(&rq->queuelist); + INIT_LIST_HEAD(&rq->timeout_list); rq->cpu = -1; rq->q = q; rq->sector = rq->hard_sector = (sector_t) -1; @@ -490,6 +491,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) } init_timer(&q->unplug_timer); + setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q); + INIT_LIST_HEAD(&q->timeout_list); kobject_init(&q->kobj, &blk_queue_ktype); @@ -897,6 +900,8 @@ EXPORT_SYMBOL(blk_start_queueing); */ void blk_requeue_request(struct request_queue *q, struct request *rq) { + blk_delete_timer(rq); + blk_clear_rq_complete(rq); blk_add_trace_rq(q, rq, BLK_TA_REQUEUE); if (blk_rq_tagged(rq)) @@ -1650,6 +1655,8 @@ static void end_that_request_last(struct request *req, int error) { struct gendisk *disk = req->rq_disk; + blk_delete_timer(req); + if (blk_rq_tagged(req)) blk_queue_end_tag(req->q, req); diff --git a/block/blk-settings.c b/block/blk-settings.c index d70692badcdb..1d0330d0b40a 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -77,6 +77,18 @@ void blk_queue_softirq_done(struct request_queue *q, softirq_done_fn *fn) } EXPORT_SYMBOL(blk_queue_softirq_done); +void blk_queue_rq_timeout(struct request_queue *q, unsigned int timeout) +{ + q->rq_timeout = timeout; +} +EXPORT_SYMBOL_GPL(blk_queue_rq_timeout); + +void blk_queue_rq_timed_out(struct request_queue *q, rq_timed_out_fn *fn) +{ + q->rq_timed_out_fn = fn; +} +EXPORT_SYMBOL_GPL(blk_queue_rq_timed_out); + /** * blk_queue_make_request - define an alternate make_request function for a device * @q: the request queue for the device to be affected diff --git a/block/blk-softirq.c b/block/blk-softirq.c index 3a1af551191e..7ab344afb16f 100644 --- a/block/blk-softirq.c +++ b/block/blk-softirq.c @@ -101,18 +101,7 @@ static struct notifier_block __cpuinitdata blk_cpu_notifier = { .notifier_call = blk_cpu_notify, }; -/** - * blk_complete_request - end I/O on a request - * @req: the request being processed - * - * Description: - * Ends all I/O on a request. It does not handle partial completions, - * unless the driver actually implements this in its completion callback - * through requeueing. The actual completion happens out-of-order, - * through a softirq handler. The user must have registered a completion - * callback through blk_queue_softirq_done(). - **/ -void blk_complete_request(struct request *req) +void __blk_complete_request(struct request *req) { struct request_queue *q = req->q; unsigned long flags; @@ -151,6 +140,23 @@ do_local: local_irq_restore(flags); } + +/** + * blk_complete_request - end I/O on a request + * @req: the request being processed + * + * Description: + * Ends all I/O on a request. It does not handle partial completions, + * unless the driver actually implements this in its completion callback + * through requeueing. The actual completion happens out-of-order, + * through a softirq handler. The user must have registered a completion + * callback through blk_queue_softirq_done(). + **/ +void blk_complete_request(struct request *req) +{ + if (!blk_mark_rq_complete(req)) + __blk_complete_request(req); +} EXPORT_SYMBOL(blk_complete_request); __init int blk_softirq_init(void) diff --git a/block/blk-timeout.c b/block/blk-timeout.c new file mode 100644 index 000000000000..b36d07bf0afb --- /dev/null +++ b/block/blk-timeout.c @@ -0,0 +1,155 @@ +/* + * Functions related to generic timeout handling of requests. + */ +#include +#include +#include + +#include "blk.h" + +/* + * blk_delete_timer - Delete/cancel timer for a given function. + * @req: request that we are canceling timer for + * + */ +void blk_delete_timer(struct request *req) +{ + struct request_queue *q = req->q; + + /* + * Nothing to detach + */ + if (!q->rq_timed_out_fn || !req->deadline) + return; + + list_del_init(&req->timeout_list); + + if (list_empty(&q->timeout_list)) + del_timer(&q->timeout); +} + +static void blk_rq_timed_out(struct request *req) +{ + struct request_queue *q = req->q; + enum blk_eh_timer_return ret; + + ret = q->rq_timed_out_fn(req); + switch (ret) { + case BLK_EH_HANDLED: + __blk_complete_request(req); + break; + case BLK_EH_RESET_TIMER: + blk_clear_rq_complete(req); + blk_add_timer(req); + break; + case BLK_EH_NOT_HANDLED: + /* + * LLD handles this for now but in the future + * we can send a request msg to abort the command + * and we can move more of the generic scsi eh code to + * the blk layer. + */ + break; + default: + printk(KERN_ERR "block: bad eh return: %d\n", ret); + break; + } +} + +void blk_rq_timed_out_timer(unsigned long data) +{ + struct request_queue *q = (struct request_queue *) data; + unsigned long flags, uninitialized_var(next), next_set = 0; + struct request *rq, *tmp; + + spin_lock_irqsave(q->queue_lock, flags); + + list_for_each_entry_safe(rq, tmp, &q->timeout_list, timeout_list) { + if (time_after_eq(jiffies, rq->deadline)) { + list_del_init(&rq->timeout_list); + + /* + * Check if we raced with end io completion + */ + if (blk_mark_rq_complete(rq)) + continue; + blk_rq_timed_out(rq); + } + if (!next_set) { + next = rq->deadline; + next_set = 1; + } else if (time_after(next, rq->deadline)) + next = rq->deadline; + } + + if (next_set && !list_empty(&q->timeout_list)) + mod_timer(&q->timeout, round_jiffies(next)); + + spin_unlock_irqrestore(q->queue_lock, flags); +} + +/** + * blk_abort_request -- Request request recovery for the specified command + * @req: pointer to the request of interest + * + * This function requests that the block layer start recovery for the + * request by deleting the timer and calling the q's timeout function. + * LLDDs who implement their own error recovery MAY ignore the timeout + * event if they generated blk_abort_req. Must hold queue lock. + */ +void blk_abort_request(struct request *req) +{ + blk_delete_timer(req); + blk_rq_timed_out(req); +} +EXPORT_SYMBOL_GPL(blk_abort_request); + +/** + * blk_add_timer - Start timeout timer for a single request + * @req: request that is about to start running. + * + * Notes: + * Each request has its own timer, and as it is added to the queue, we + * set up the timer. When the request completes, we cancel the timer. + */ +void blk_add_timer(struct request *req) +{ + struct request_queue *q = req->q; + unsigned long expiry; + + if (!q->rq_timed_out_fn) + return; + + BUG_ON(!list_empty(&req->timeout_list)); + BUG_ON(test_bit(REQ_ATOM_COMPLETE, &req->atomic_flags)); + + if (req->timeout) + req->deadline = jiffies + req->timeout; + else { + req->deadline = jiffies + q->rq_timeout; + /* + * Some LLDs, like scsi, peek at the timeout to prevent + * a command from being retried forever. + */ + req->timeout = q->rq_timeout; + } + list_add_tail(&req->timeout_list, &q->timeout_list); + + /* + * If the timer isn't already pending or this timeout is earlier + * than an existing one, modify the timer. Round to next nearest + * second. + */ + expiry = round_jiffies(req->deadline); + + /* + * We use ->deadline == 0 to detect whether a timer was added or + * not, so just increase to next jiffy for that specific case + */ + if (unlikely(!req->deadline)) + req->deadline = 1; + + if (!timer_pending(&q->timeout) || + time_before(expiry, q->timeout.expires)) + mod_timer(&q->timeout, expiry); +} diff --git a/block/blk.h b/block/blk.h index de74254cb916..a4f4a50aefaa 100644 --- a/block/blk.h +++ b/block/blk.h @@ -17,6 +17,30 @@ void __blk_queue_free_tags(struct request_queue *q); void blk_unplug_work(struct work_struct *work); void blk_unplug_timeout(unsigned long data); +void blk_rq_timed_out_timer(unsigned long data); +void blk_delete_timer(struct request *); +void blk_add_timer(struct request *); + +/* + * Internal atomic flags for request handling + */ +enum rq_atomic_flags { + REQ_ATOM_COMPLETE = 0, +}; + +/* + * EH timer and IO completion will both attempt to 'grab' the request, make + * sure that only one of them suceeds + */ +static inline int blk_mark_rq_complete(struct request *rq) +{ + return test_and_set_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags); +} + +static inline void blk_clear_rq_complete(struct request *rq) +{ + clear_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags); +} struct io_context *current_io_context(gfp_t gfp_flags, int node); diff --git a/block/elevator.c b/block/elevator.c index 8e3fc3afc77b..a91fc59edd01 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -36,6 +36,8 @@ #include #include +#include "blk.h" + static DEFINE_SPINLOCK(elv_list_lock); static LIST_HEAD(elv_list); @@ -771,6 +773,12 @@ struct request *elv_next_request(struct request_queue *q) */ rq->cmd_flags |= REQ_STARTED; blk_add_trace_rq(q, rq, BLK_TA_ISSUE); + + /* + * We are now handing the request to the hardware, + * add the timeout handler + */ + blk_add_timer(rq); } if (!q->boundary_rq || q->boundary_rq == rq) { diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index c1db2f234d2e..bd0b2bc76f10 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -33,6 +33,7 @@ */ #include +#include #include #include #include @@ -457,29 +458,29 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev, * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ -enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) +enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) { struct Scsi_Host *host = cmd->device->host; struct ata_port *ap = ata_shost_to_port(host); unsigned long flags; struct ata_queued_cmd *qc; - enum scsi_eh_timer_return ret; + enum blk_eh_timer_return ret; DPRINTK("ENTER\n"); if (ap->ops->error_handler) { - ret = EH_NOT_HANDLED; + ret = BLK_EH_NOT_HANDLED; goto out; } - ret = EH_HANDLED; + ret = BLK_EH_HANDLED; spin_lock_irqsave(ap->lock, flags); qc = ata_qc_from_tag(ap, ap->link.active_tag); if (qc) { WARN_ON(qc->scsicmd != cmd); qc->flags |= ATA_QCFLAG_EH_SCHEDULED; qc->err_mask |= AC_ERR_TIMEOUT; - ret = EH_NOT_HANDLED; + ret = BLK_EH_NOT_HANDLED; } spin_unlock_irqrestore(ap->lock, flags); @@ -831,7 +832,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) * Note that ATA_QCFLAG_FAILED is unconditionally set after * this function completes. */ - scsi_req_abort_cmd(qc->scsicmd); + blk_abort_request(qc->scsicmd->request); } /** diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index ade5c75b6144..24f5005478b0 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -152,7 +152,7 @@ extern int ata_bus_probe(struct ata_port *ap); /* libata-eh.c */ extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); extern void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd); -extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); +extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); extern void ata_port_wait_eh(struct ata_port *ap); extern void ata_eh_fastdrain_timerfn(unsigned long arg); diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index aa4e77c25273..8abfd06b5a72 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -1139,7 +1139,7 @@ static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd srbcmd->id = cpu_to_le32(scmd_id(cmd)); srbcmd->lun = cpu_to_le32(cmd->device->lun); srbcmd->flags = cpu_to_le32(flag); - timeout = cmd->timeout_per_command/HZ; + timeout = cmd->request->timeout/HZ; if (timeout == 0) timeout = 1; srbcmd->timeout = cpu_to_le32(timeout); // timeout in seconds diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 822d5214692b..c387c15a2128 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -464,7 +464,6 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd, /* use request field to save the ptr. to completion struct. */ scp->request = (struct request *)&wait; - scp->timeout_per_command = timeout*HZ; scp->cmd_len = 12; scp->cmnd = cmnd; cmndinfo.priority = IOCTL_PRI; @@ -1995,23 +1994,12 @@ static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar priority) register Scsi_Cmnd *pscp; register Scsi_Cmnd *nscp; ulong flags; - unchar b, t; TRACE(("gdth_putq() priority %d\n",priority)); spin_lock_irqsave(&ha->smp_lock, flags); - if (!cmndinfo->internal_command) { + if (!cmndinfo->internal_command) cmndinfo->priority = priority; - b = scp->device->channel; - t = scp->device->id; - if (priority >= DEFAULT_PRI) { - if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) || - (b==ha->virt_bus && thdr[t].lock)) { - TRACE2(("gdth_putq(): locked IO ->update_timeout()\n")); - cmndinfo->timeout = gdth_update_timeout(scp, 0); - } - } - } if (ha->req_first==NULL) { ha->req_first = scp; /* queue was empty */ @@ -3899,6 +3887,39 @@ static const char *gdth_info(struct Scsi_Host *shp) return ((const char *)ha->binfo.type_string); } +static enum blk_eh_timer_return gdth_timed_out(struct scsi_cmnd *scp) +{ + gdth_ha_str *ha = shost_priv(scp->device->host); + struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); + unchar b, t; + ulong flags; + enum blk_eh_timer_return retval = BLK_EH_NOT_HANDLED; + + TRACE(("%s() cmd 0x%x\n", scp->cmnd[0], __func__)); + b = scp->device->channel; + t = scp->device->id; + + /* + * We don't really honor the command timeout, but we try to + * honor 6 times of the actual command timeout! So reset the + * timer if this is less than 6th timeout on this command! + */ + if (++cmndinfo->timeout_count < 6) + retval = BLK_EH_RESET_TIMER; + + /* Reset the timeout if it is locked IO */ + spin_lock_irqsave(&ha->smp_lock, flags); + if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha, b)].lock) || + (b == ha->virt_bus && t < MAX_HDRIVES && ha->hdr[t].lock)) { + TRACE2(("%s(): locked IO, reset timeout\n", __func__)); + retval = BLK_EH_RESET_TIMER; + } + spin_unlock_irqrestore(&ha->smp_lock, flags); + + return retval; +} + + static int gdth_eh_bus_reset(Scsi_Cmnd *scp) { gdth_ha_str *ha = shost_priv(scp->device->host); @@ -3992,7 +4013,7 @@ static int gdth_queuecommand(struct scsi_cmnd *scp, BUG_ON(!cmndinfo); scp->scsi_done = done; - gdth_update_timeout(scp, scp->timeout_per_command * 6); + cmndinfo->timeout_count = 0; cmndinfo->priority = DEFAULT_PRI; return __gdth_queuecommand(ha, scp, cmndinfo); @@ -4096,12 +4117,10 @@ static int ioc_lockdrv(void __user *arg) ha->hdr[j].lock = 1; spin_unlock_irqrestore(&ha->smp_lock, flags); gdth_wait_completion(ha, ha->bus_cnt, j); - gdth_stop_timeout(ha, ha->bus_cnt, j); } else { spin_lock_irqsave(&ha->smp_lock, flags); ha->hdr[j].lock = 0; spin_unlock_irqrestore(&ha->smp_lock, flags); - gdth_start_timeout(ha, ha->bus_cnt, j); gdth_next(ha); } } @@ -4539,18 +4558,14 @@ static int gdth_ioctl(struct inode *inode, struct file *filep, spin_lock_irqsave(&ha->smp_lock, flags); ha->raw[i].lock = 1; spin_unlock_irqrestore(&ha->smp_lock, flags); - for (j = 0; j < ha->tid_cnt; ++j) { + for (j = 0; j < ha->tid_cnt; ++j) gdth_wait_completion(ha, i, j); - gdth_stop_timeout(ha, i, j); - } } else { spin_lock_irqsave(&ha->smp_lock, flags); ha->raw[i].lock = 0; spin_unlock_irqrestore(&ha->smp_lock, flags); - for (j = 0; j < ha->tid_cnt; ++j) { - gdth_start_timeout(ha, i, j); + for (j = 0; j < ha->tid_cnt; ++j) gdth_next(ha); - } } } break; @@ -4644,6 +4659,7 @@ static struct scsi_host_template gdth_template = { .slave_configure = gdth_slave_configure, .bios_param = gdth_bios_param, .proc_info = gdth_proc_info, + .eh_timed_out = gdth_timed_out, .proc_name = "gdth", .can_queue = GDTH_MAXCMDS, .this_id = -1, diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index ca92476727cf..1646444e9bd5 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -916,7 +916,7 @@ typedef struct { gdth_cmd_str *internal_cmd_str; /* crier for internal messages*/ dma_addr_t sense_paddr; /* sense dma-addr */ unchar priority; - int timeout; + int timeout_count; /* # of timeout calls */ volatile int wait_for_completion; ushort status; ulong32 info; diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index ce0228e26aec..59349a316e13 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -748,69 +748,3 @@ static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id) } spin_unlock_irqrestore(&ha->smp_lock, flags); } - -static void gdth_stop_timeout(gdth_ha_str *ha, int busnum, int id) -{ - ulong flags; - Scsi_Cmnd *scp; - unchar b, t; - - spin_lock_irqsave(&ha->smp_lock, flags); - - for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { - struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); - if (!cmndinfo->internal_command) { - b = scp->device->channel; - t = scp->device->id; - if (t == (unchar)id && b == (unchar)busnum) { - TRACE2(("gdth_stop_timeout(): update_timeout()\n")); - cmndinfo->timeout = gdth_update_timeout(scp, 0); - } - } - } - spin_unlock_irqrestore(&ha->smp_lock, flags); -} - -static void gdth_start_timeout(gdth_ha_str *ha, int busnum, int id) -{ - ulong flags; - Scsi_Cmnd *scp; - unchar b, t; - - spin_lock_irqsave(&ha->smp_lock, flags); - - for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { - struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); - if (!cmndinfo->internal_command) { - b = scp->device->channel; - t = scp->device->id; - if (t == (unchar)id && b == (unchar)busnum) { - TRACE2(("gdth_start_timeout(): update_timeout()\n")); - gdth_update_timeout(scp, cmndinfo->timeout); - } - } - } - spin_unlock_irqrestore(&ha->smp_lock, flags); -} - -static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout) -{ - int oldto; - - oldto = scp->timeout_per_command; - scp->timeout_per_command = timeout; - - if (timeout == 0) { - del_timer(&scp->eh_timeout); - scp->eh_timeout.data = (unsigned long) NULL; - scp->eh_timeout.expires = 0; - } else { - if (scp->eh_timeout.data != (unsigned long) NULL) - del_timer(&scp->eh_timeout); - scp->eh_timeout.data = (unsigned long) scp; - scp->eh_timeout.expires = jiffies + timeout; - add_timer(&scp->eh_timeout); - } - - return oldto; -} diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h index 45e6fdacf36e..9b900cc9ebe8 100644 --- a/drivers/scsi/gdth_proc.h +++ b/drivers/scsi/gdth_proc.h @@ -20,9 +20,6 @@ static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch, ulong64 *paddr); static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr); static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id); -static void gdth_stop_timeout(gdth_ha_str *ha, int busnum, int id); -static void gdth_start_timeout(gdth_ha_str *ha, int busnum, int id); -static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout); #endif diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 7b1502c0ab6e..87e09f35d3d4 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -756,7 +756,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, init_event_struct(evt_struct, handle_cmd_rsp, VIOSRP_SRP_FORMAT, - cmnd->timeout_per_command/HZ); + cmnd->request->timeout/HZ); evt_struct->cmnd = cmnd; evt_struct->cmnd_done = done; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 461331d3dc45..81c16cba5417 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -612,7 +612,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, pc->req_xfer = pc->buf_size = scsi_bufflen(cmd); pc->scsi_cmd = cmd; pc->done = done; - pc->timeout = jiffies + cmd->timeout_per_command; + pc->timeout = jiffies + cmd->request->timeout; if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) { printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e7a3a6554425..d30eb7ba018e 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3670,7 +3670,8 @@ static int ipr_slave_configure(struct scsi_device *sdev) sdev->no_uld_attach = 1; } if (ipr_is_vset_device(res)) { - sdev->timeout = IPR_VSET_RW_TIMEOUT; + blk_queue_rq_timeout(sdev->request_queue, + IPR_VSET_RW_TIMEOUT); blk_queue_max_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS); } if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res)) diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index bc9e6ddf41df..ef683f0d2b5a 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -3818,7 +3818,7 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb) scb->cmd.dcdb.segment_4G = 0; scb->cmd.dcdb.enhanced_sg = 0; - TimeOut = scb->scsi_cmd->timeout_per_command; + TimeOut = scb->scsi_cmd->request->timeout; if (ha->subsys->param[4] & 0x00100000) { /* If NEW Tape DCDB is Supported */ if (!scb->sg_len) { diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 299e075a7b34..1eca82420aab 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1476,12 +1476,12 @@ static void iscsi_start_tx(struct iscsi_conn *conn) scsi_queue_work(conn->session->host, &conn->xmitwork); } -static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) +static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) { struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct iscsi_conn *conn; - enum scsi_eh_timer_return rc = EH_NOT_HANDLED; + enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; cls_session = starget_to_session(scsi_target(scmd->device)); session = cls_session->dd_data; @@ -1494,14 +1494,14 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) * We are probably in the middle of iscsi recovery so let * that complete and handle the error. */ - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; goto done; } conn = session->leadconn; if (!conn) { /* In the middle of shuting down */ - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; goto done; } @@ -1513,20 +1513,21 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) */ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) + (conn->ping_timeout * HZ), jiffies)) - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; /* * if we are about to check the transport then give the command * more time */ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ), jiffies)) - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; /* if in the middle of checking the transport then give us more time */ if (conn->ping_task) - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; done: spin_unlock(&session->lock); - debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh"); + debug_scsi("return %s\n", rc == BLK_EH_RESET_TIMER ? + "timer reset" : "nh"); return rc; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48ee8c7f5bdd..837b095ba90d 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -398,7 +398,7 @@ void sas_ata_task_abort(struct sas_task *task) /* Bounce SCSI-initiated commands to the SCSI EH */ if (qc->scsicmd) { - scsi_req_abort_cmd(qc->scsicmd); + blk_abort_request(qc->scsicmd->request); scsi_schedule_eh(qc->scsicmd->device->host); return; } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index b4f9368f116a..0001374bd6b2 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -55,7 +55,7 @@ void sas_unregister_phys(struct sas_ha_struct *sas_ha); int sas_register_ports(struct sas_ha_struct *sas_ha); void sas_unregister_ports(struct sas_ha_struct *sas_ha); -enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); int sas_init_queue(struct sas_ha_struct *sas_ha); int sas_init_events(struct sas_ha_struct *sas_ha); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index a8e3ef309070..744838780ada 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -673,43 +673,43 @@ out: return; } -enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { struct sas_task *task = TO_SAS_TASK(cmd); unsigned long flags; if (!task) { - cmd->timeout_per_command /= 2; + cmd->request->timeout /= 2; SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n", - cmd, task, (cmd->timeout_per_command ? - "EH_RESET_TIMER" : "EH_NOT_HANDLED")); - if (!cmd->timeout_per_command) - return EH_NOT_HANDLED; - return EH_RESET_TIMER; + cmd, task, (cmd->request->timeout ? + "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED")); + if (!cmd->request->timeout) + return BLK_EH_NOT_HANDLED; + return BLK_EH_RESET_TIMER; } spin_lock_irqsave(&task->task_state_lock, flags); BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", - cmd, task); - return EH_HANDLED; + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: " + "BLK_EH_HANDLED\n", cmd, task); + return BLK_EH_HANDLED; } if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) { spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: " - "EH_RESET_TIMER\n", + "BLK_EH_RESET_TIMER\n", cmd, task); - return EH_RESET_TIMER; + return BLK_EH_RESET_TIMER; } task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n", + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n", cmd, task); - return EH_NOT_HANDLED; + return BLK_EH_NOT_HANDLED; } int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) @@ -1039,7 +1039,7 @@ void sas_task_abort(struct sas_task *task) return; } - scsi_req_abort_cmd(sc); + blk_abort_request(sc->request); scsi_schedule_eh(sc->device->host); } diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 97b763378e7d..afe1de998763 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -1167,7 +1167,7 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd) * cmd has not been completed within the timeout period. */ static enum -scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) +blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) { struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr; struct megasas_instance *instance; @@ -1175,7 +1175,7 @@ scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) if (time_after(jiffies, scmd->jiffies_at_alloc + (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) { - return EH_NOT_HANDLED; + return BLK_EH_NOT_HANDLED; } instance = cmd->instance; @@ -1189,7 +1189,7 @@ scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) spin_unlock_irqrestore(instance->host->host_lock, flags); } - return EH_RESET_TIMER; + return BLK_EH_RESET_TIMER; } /** diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index c57c94c0ffd2..3b7240e40819 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -4170,8 +4170,8 @@ static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd) ** **---------------------------------------------------- */ - if (np->settle_time && cmd->timeout_per_command >= HZ) { - u_long tlimit = jiffies + cmd->timeout_per_command - HZ; + if (np->settle_time && cmd->request->timeout >= HZ) { + u_long tlimit = jiffies + cmd->request->timeout - HZ; if (time_after(np->settle_time, tlimit)) np->settle_time = tlimit; } diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 37f9ba0cd798..b6cd12b2e996 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -2845,7 +2845,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8)); /* Set ISP command timeout. */ - pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ); + pkt->timeout = cpu_to_le16(cmd->request->timeout/HZ); /* Set device target ID and LUN */ pkt->lun = SCSI_LUN_32(cmd); @@ -3114,7 +3114,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8)); /* Set ISP command timeout. */ - pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ); + pkt->timeout = cpu_to_le16(cmd->request->timeout/HZ); /* Set device target ID and LUN */ pkt->lun = SCSI_LUN_32(cmd); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 88bebb13bc52..de8279ad7d89 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1542,7 +1542,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) DEBUG2(printk(KERN_INFO "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x," "dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no, - cmd, jiffies, cmd->timeout_per_command / HZ, + cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); /* FIXME: wait for hba to go online */ @@ -1598,7 +1598,7 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) DEBUG2(printk(KERN_INFO "scsi%ld: TARGET_DEVICE_RESET cmd=%p jiffies = 0x%lx, " "to=%x,dpc_flags=%lx, status=%x allowed=%d\n", - ha->host_no, cmd, jiffies, cmd->timeout_per_command / HZ, + ha->host_no, cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); stat = qla4xxx_reset_target(ha, ddb_entry); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ee6be596503d..dbeb86cafc0d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -291,7 +291,6 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) unsigned long flags; cmd->device = dev; - init_timer(&cmd->eh_timeout); INIT_LIST_HEAD(&cmd->list); spin_lock_irqsave(&dev->list_lock, flags); list_add_tail(&cmd->list, &dev->cmd_list); @@ -652,14 +651,19 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) unsigned long timeout; int rtn = 0; + /* + * We will use a queued command if possible, otherwise we will + * emulate the queuing and calling of completion function ourselves. + */ + atomic_inc(&cmd->device->iorequest_cnt); + /* check if the device is still usable */ if (unlikely(cmd->device->sdev_state == SDEV_DEL)) { /* in SDEV_DEL we error all commands. DID_NO_CONNECT * returns an immediate error upwards, and signals * that the device is no longer present */ cmd->result = DID_NO_CONNECT << 16; - atomic_inc(&cmd->device->iorequest_cnt); - __scsi_done(cmd); + scsi_done(cmd); /* return 0 (because the command has been processed) */ goto out; } @@ -672,6 +676,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) * future requests should not occur until the device * transitions out of the suspend state. */ + scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n")); @@ -714,20 +719,8 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) host->resetting = 0; } - /* - * AK: unlikely race here: for some reason the timer could - * expire before the serial number is set up below. - */ - scsi_add_timer(cmd, cmd->timeout_per_command, scsi_times_out); - scsi_log_send(cmd); - /* - * We will use a queued command if possible, otherwise we will - * emulate the queuing and calling of completion function ourselves. - */ - atomic_inc(&cmd->device->iorequest_cnt); - /* * Before we queue this command, check if the command * length exceeds what the host adapter can handle. @@ -744,6 +737,12 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) } spin_lock_irqsave(host->host_lock, flags); + /* + * AK: unlikely race here: for some reason the timer could + * expire before the serial number is set up below. + * + * TODO: kill serial or move to blk layer + */ scsi_cmd_get_serial(host, cmd); if (unlikely(host->shost_state == SHOST_DEL)) { @@ -754,12 +753,8 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) } spin_unlock_irqrestore(host->host_lock, flags); if (rtn) { - if (scsi_delete_timer(cmd)) { - atomic_inc(&cmd->device->iodone_cnt); - scsi_queue_insert(cmd, - (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ? - rtn : SCSI_MLQUEUE_HOST_BUSY); - } + scsi_queue_insert(cmd, (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ? + rtn : SCSI_MLQUEUE_HOST_BUSY); SCSI_LOG_MLQUEUE(3, printk("queuecommand : request rejected\n")); } @@ -769,24 +764,6 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) return rtn; } -/** - * scsi_req_abort_cmd -- Request command recovery for the specified command - * @cmd: pointer to the SCSI command of interest - * - * This function requests that SCSI Core start recovery for the - * command by deleting the timer and adding the command to the eh - * queue. It can be called by either LLDDs or SCSI Core. LLDDs who - * implement their own error recovery MAY ignore the timeout event if - * they generated scsi_req_abort_cmd. - */ -void scsi_req_abort_cmd(struct scsi_cmnd *cmd) -{ - if (!scsi_delete_timer(cmd)) - return; - scsi_times_out(cmd); -} -EXPORT_SYMBOL(scsi_req_abort_cmd); - /** * scsi_done - Enqueue the finished SCSI command into the done queue. * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives @@ -802,42 +779,7 @@ EXPORT_SYMBOL(scsi_req_abort_cmd); */ static void scsi_done(struct scsi_cmnd *cmd) { - /* - * We don't have to worry about this one timing out anymore. - * If we are unable to remove the timer, then the command - * has already timed out. In which case, we have no choice but to - * let the timeout function run, as we have no idea where in fact - * that function could really be. It might be on another processor, - * etc, etc. - */ - if (!scsi_delete_timer(cmd)) - return; - __scsi_done(cmd); -} - -/* Private entry to scsi_done() to complete a command when the timer - * isn't running --- used by scsi_times_out */ -void __scsi_done(struct scsi_cmnd *cmd) -{ - struct request *rq = cmd->request; - - /* - * Set the serial numbers back to zero - */ - cmd->serial_number = 0; - - atomic_inc(&cmd->device->iodone_cnt); - if (cmd->result) - atomic_inc(&cmd->device->ioerr_cnt); - - BUG_ON(!rq); - - /* - * The uptodate/nbytes values don't matter, as we allow partial - * completes and thus will check this in the softirq callback - */ - rq->completion_data = cmd; - blk_complete_request(rq); + blk_complete_request(cmd->request); } /* Move this to a header if it becomes more generally useful */ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 39ce3aba1dac..fecefa05cb62 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -111,70 +111,9 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) return ret; } -/** - * scsi_add_timer - Start timeout timer for a single scsi command. - * @scmd: scsi command that is about to start running. - * @timeout: amount of time to allow this command to run. - * @complete: timeout function to call if timer isn't canceled. - * - * Notes: - * This should be turned into an inline function. Each scsi command - * has its own timer, and as it is added to the queue, we set up the - * timer. When the command completes, we cancel the timer. - */ -void scsi_add_timer(struct scsi_cmnd *scmd, int timeout, - void (*complete)(struct scsi_cmnd *)) -{ - - /* - * If the clock was already running for this command, then - * first delete the timer. The timer handling code gets rather - * confused if we don't do this. - */ - if (scmd->eh_timeout.function) - del_timer(&scmd->eh_timeout); - - scmd->eh_timeout.data = (unsigned long)scmd; - scmd->eh_timeout.expires = jiffies + timeout; - scmd->eh_timeout.function = (void (*)(unsigned long)) complete; - - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:" - " %d, (%p)\n", __func__, - scmd, timeout, complete)); - - add_timer(&scmd->eh_timeout); -} - -/** - * scsi_delete_timer - Delete/cancel timer for a given function. - * @scmd: Cmd that we are canceling timer for - * - * Notes: - * This should be turned into an inline function. - * - * Return value: - * 1 if we were able to detach the timer. 0 if we blew it, and the - * timer function has already started to run. - */ -int scsi_delete_timer(struct scsi_cmnd *scmd) -{ - int rtn; - - rtn = del_timer(&scmd->eh_timeout); - - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p," - " rtn: %d\n", __func__, - scmd, rtn)); - - scmd->eh_timeout.data = (unsigned long)NULL; - scmd->eh_timeout.function = NULL; - - return rtn; -} - /** * scsi_times_out - Timeout function for normal scsi commands. - * @scmd: Cmd that is timing out. + * @req: request that is timing out. * * Notes: * We do not need to lock this. There is the potential for a race @@ -182,9 +121,11 @@ int scsi_delete_timer(struct scsi_cmnd *scmd) * normal completion function determines that the timer has already * fired, then it mustn't do anything. */ -void scsi_times_out(struct scsi_cmnd *scmd) +enum blk_eh_timer_return scsi_times_out(struct request *req) { - enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); + struct scsi_cmnd *scmd = req->special; + enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); + enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED; scsi_log_completion(scmd, TIMEOUT_ERROR); @@ -196,22 +137,20 @@ void scsi_times_out(struct scsi_cmnd *scmd) eh_timed_out = NULL; if (eh_timed_out) - switch (eh_timed_out(scmd)) { - case EH_HANDLED: - __scsi_done(scmd); - return; - case EH_RESET_TIMER: - scsi_add_timer(scmd, scmd->timeout_per_command, - scsi_times_out); - return; - case EH_NOT_HANDLED: + rtn = eh_timed_out(scmd); + switch (rtn) { + case BLK_EH_NOT_HANDLED: break; + default: + return rtn; } if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { scmd->result |= DID_TIME_OUT << 16; - __scsi_done(scmd); + return BLK_EH_HANDLED; } + + return BLK_EH_NOT_HANDLED; } /** @@ -1793,7 +1732,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) blk_rq_init(NULL, &req); scmd->request = &req; - memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout)); scmd->cmnd = req.cmd; @@ -1804,8 +1742,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) scmd->sc_data_direction = DMA_BIDIRECTIONAL; - init_timer(&scmd->eh_timeout); - spin_lock_irqsave(shost->host_lock, flags); shost->tmf_in_progress = 1; spin_unlock_irqrestore(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 62307bd794a9..e7686500e9dd 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1181,7 +1181,6 @@ int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) cmd->transfersize = req->data_len; cmd->allowed = req->retries; - cmd->timeout_per_command = req->timeout; return BLKPREP_OK; } EXPORT_SYMBOL(scsi_setup_blk_pc_cmnd); @@ -1416,17 +1415,26 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) spin_unlock(shost->host_lock); spin_lock(sdev->request_queue->queue_lock); - __scsi_done(cmd); + blk_complete_request(req); } static void scsi_softirq_done(struct request *rq) { - struct scsi_cmnd *cmd = rq->completion_data; - unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command; + struct scsi_cmnd *cmd = rq->special; + unsigned long wait_for = (cmd->allowed + 1) * rq->timeout; int disposition; INIT_LIST_HEAD(&cmd->eh_entry); + /* + * Set the serial numbers back to zero + */ + cmd->serial_number = 0; + + atomic_inc(&cmd->device->iodone_cnt); + if (cmd->result) + atomic_inc(&cmd->device->ioerr_cnt); + disposition = scsi_decide_disposition(cmd); if (disposition != SUCCESS && time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) { @@ -1675,6 +1683,7 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) blk_queue_prep_rq(q, scsi_prep_fn); blk_queue_softirq_done(q, scsi_softirq_done); + blk_queue_rq_timed_out(q, scsi_times_out); return q; } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 79f0f7511204..6cddd5dd323c 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -4,6 +4,7 @@ #include struct request_queue; +struct request; struct scsi_cmnd; struct scsi_device; struct scsi_host_template; @@ -27,7 +28,6 @@ extern void scsi_exit_hosts(void); extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd); extern int scsi_setup_command_freelist(struct Scsi_Host *shost); extern void scsi_destroy_command_freelist(struct Scsi_Host *shost); -extern void __scsi_done(struct scsi_cmnd *cmd); #ifdef CONFIG_SCSI_LOGGING void scsi_log_send(struct scsi_cmnd *cmd); void scsi_log_completion(struct scsi_cmnd *cmd, int disposition); @@ -49,10 +49,7 @@ extern int __init scsi_init_devinfo(void); extern void scsi_exit_devinfo(void); /* scsi_error.c */ -extern void scsi_add_timer(struct scsi_cmnd *, int, - void (*)(struct scsi_cmnd *)); -extern int scsi_delete_timer(struct scsi_cmnd *); -extern void scsi_times_out(struct scsi_cmnd *cmd); +extern enum blk_eh_timer_return scsi_times_out(struct request *req); extern int scsi_error_handler(void *host); extern int scsi_decide_disposition(struct scsi_cmnd *cmd); extern void scsi_eh_wakeup(struct Scsi_Host *shost); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ab3c71869be5..7f618ee5ecea 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -560,12 +560,15 @@ sdev_rd_attr (vendor, "%.8s\n"); sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); +/* + * TODO: can we make these symlinks to the block layer ones? + */ static ssize_t sdev_show_timeout (struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev; sdev = to_scsi_device(dev); - return snprintf (buf, 20, "%d\n", sdev->timeout / HZ); + return snprintf(buf, 20, "%d\n", sdev->request_queue->rq_timeout / HZ); } static ssize_t @@ -576,7 +579,7 @@ sdev_store_timeout (struct device *dev, struct device_attribute *attr, int timeout; sdev = to_scsi_device(dev); sscanf (buf, "%d\n", &timeout); - sdev->timeout = timeout * HZ; + blk_queue_rq_timeout(sdev->request_queue, timeout * HZ); return count; } static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 56823fd1fb84..9168883d0dfe 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -1950,15 +1950,15 @@ static int fc_vport_match(struct attribute_container *cont, * Notes: * This routine assumes no locks are held on entry. */ -static enum scsi_eh_timer_return +static enum blk_eh_timer_return fc_timed_out(struct scsi_cmnd *scmd) { struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device)); if (rport->port_state == FC_PORTSTATE_BLOCKED) - return EH_RESET_TIMER; + return BLK_EH_RESET_TIMER; - return EH_NOT_HANDLED; + return BLK_EH_NOT_HANDLED; } /* diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index cb115d1bf228..c0cf4acda7de 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -383,7 +383,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) sector_t block = rq->sector; sector_t threshold; unsigned int this_count = rq->nr_sectors; - unsigned int timeout = sdp->timeout; int ret; if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { @@ -584,7 +583,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) SCpnt->transfersize = sdp->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = SD_MAX_RETRIES; - SCpnt->timeout_per_command = timeout; /* * This indicates that the command is ready from our end to be @@ -1878,11 +1876,12 @@ static int sd_probe(struct device *dev) sdkp->openers = 0; sdkp->previous_state = 1; - if (!sdp->timeout) { + if (!sdp->request_queue->rq_timeout) { if (sdp->type != TYPE_MOD) - sdp->timeout = SD_TIMEOUT; + blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT); else - sdp->timeout = SD_MOD_TIMEOUT; + blk_queue_rq_timeout(sdp->request_queue, + SD_MOD_TIMEOUT); } device_initialize(&sdkp->dev); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 8dbe3798d5fd..0f17009c99d2 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -331,7 +331,7 @@ static int sr_done(struct scsi_cmnd *SCpnt) static int sr_prep_fn(struct request_queue *q, struct request *rq) { - int block=0, this_count, s_size, timeout = SR_TIMEOUT; + int block = 0, this_count, s_size; struct scsi_cd *cd; struct scsi_cmnd *SCpnt; struct scsi_device *sdp = q->queuedata; @@ -461,7 +461,6 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq) SCpnt->transfersize = cd->device->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = MAX_RETRIES; - SCpnt->timeout_per_command = timeout; /* * This indicates that the command is ready from our end to be @@ -620,6 +619,8 @@ static int sr_probe(struct device *dev) disk->fops = &sr_bdops; disk->flags = GENHD_FL_CD; + blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT); + cd->device = sdev; cd->disk = disk; cd->driver = &sr_template; diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index d39107b7669b..f4e6cde1fd0d 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -519,8 +519,8 @@ static int sym53c8xx_queue_command(struct scsi_cmnd *cmd, * Shorten our settle_time if needed for * this command not to time out. */ - if (np->s.settle_time_valid && cmd->timeout_per_command) { - unsigned long tlimit = jiffies + cmd->timeout_per_command; + if (np->s.settle_time_valid && cmd->request->timeout) { + unsigned long tlimit = jiffies + cmd->request->timeout; tlimit -= SYM_CONF_TIMER_INTERVAL*2; if (time_after(np->s.settle_time, tlimit)) { np->s.settle_time = tlimit; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9c2549260427..067f28b80072 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -147,6 +147,7 @@ struct request { unsigned int cmd_flags; enum rq_cmd_type_bits cmd_type; + unsigned long atomic_flags; /* Maintain bio traversal state for part by part I/O submission. * hard_* are block layer internals, no driver should touch them! @@ -214,6 +215,8 @@ struct request { void *data; void *sense; + unsigned long deadline; + struct list_head timeout_list; unsigned int timeout; int retries; @@ -266,6 +269,14 @@ typedef void (prepare_flush_fn) (struct request_queue *, struct request *); typedef void (softirq_done_fn)(struct request *); typedef int (dma_drain_needed_fn)(struct request *); +enum blk_eh_timer_return { + BLK_EH_NOT_HANDLED, + BLK_EH_HANDLED, + BLK_EH_RESET_TIMER, +}; + +typedef enum blk_eh_timer_return (rq_timed_out_fn)(struct request *); + enum blk_queue_state { Queue_down, Queue_up, @@ -311,6 +322,7 @@ struct request_queue merge_bvec_fn *merge_bvec_fn; prepare_flush_fn *prepare_flush_fn; softirq_done_fn *softirq_done_fn; + rq_timed_out_fn *rq_timed_out_fn; dma_drain_needed_fn *dma_drain_needed; /* @@ -386,6 +398,10 @@ struct request_queue unsigned int nr_sorted; unsigned int in_flight; + unsigned int rq_timeout; + struct timer_list timeout; + struct list_head timeout_list; + /* * sg stuff */ @@ -770,6 +786,8 @@ extern int blk_end_request_callback(struct request *rq, int error, unsigned int nr_bytes, int (drv_callback)(struct request *)); extern void blk_complete_request(struct request *); +extern void __blk_complete_request(struct request *); +extern void blk_abort_request(struct request *); /* * blk_end_request() takes bytes instead of sectors as a complete size. @@ -811,6 +829,8 @@ extern void blk_queue_dma_alignment(struct request_queue *, int); extern void blk_queue_update_dma_alignment(struct request_queue *, int); extern void blk_queue_softirq_done(struct request_queue *, softirq_done_fn *); extern void blk_queue_set_discard(struct request_queue *, prepare_discard_fn *); +extern void blk_queue_rq_timed_out(struct request_queue *, rq_timed_out_fn *); +extern void blk_queue_rq_timeout(struct request_queue *, unsigned int); extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev); extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *); extern int blk_do_ordered(struct request_queue *, struct request **); diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index f9f6e793575c..855bf95963e7 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -75,7 +75,6 @@ struct scsi_cmnd { int retries; int allowed; - int timeout_per_command; unsigned char prot_op; unsigned char prot_type; @@ -86,7 +85,6 @@ struct scsi_cmnd { /* These elements define the operation we are about to perform */ unsigned char *cmnd; - struct timer_list eh_timeout; /* Used to time out the command. */ /* These elements define the operation we ultimately want to perform */ struct scsi_data_buffer sdb; @@ -139,7 +137,6 @@ extern void scsi_put_command(struct scsi_cmnd *); extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *, struct device *); extern void scsi_finish_command(struct scsi_cmnd *cmd); -extern void scsi_req_abort_cmd(struct scsi_cmnd *cmd); extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, size_t *offset, size_t *len); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 44a55d1bf530..d123ca84e732 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -43,13 +43,6 @@ struct blk_queue_tags; #define DISABLE_CLUSTERING 0 #define ENABLE_CLUSTERING 1 -enum scsi_eh_timer_return { - EH_NOT_HANDLED, - EH_HANDLED, - EH_RESET_TIMER, -}; - - struct scsi_host_template { struct module *module; const char *name; @@ -347,7 +340,7 @@ struct scsi_host_template { * * Status: OPTIONAL */ - enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); + enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); /* * Name of proc directory diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index 490bd13a634c..0de32cd4e8a7 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h @@ -21,6 +21,7 @@ #define SCSI_TRANSPORT_H #include +#include #include #include @@ -64,7 +65,7 @@ struct scsi_transport_template { * begin counting again * EH_NOT_HANDLED Begin normal error recovery */ - enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); + enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); /* * Used as callback for the completion of i_t_nexus request -- cgit v1.2.3 From 11914a53d2ec2974a565311af327b8983d8c820d Mon Sep 17 00:00:00 2001 From: Mike Anderson Date: Sat, 13 Sep 2008 20:31:27 +0200 Subject: block: Add interface to abort queued requests Signed-off-by: Mike Anderson Signed-off-by: Jens Axboe --- block/blk-timeout.c | 22 ++++++++++++++++++++++ block/elevator.c | 13 +++++++++++++ include/linux/blkdev.h | 1 + include/linux/blktrace_api.h | 2 ++ include/linux/elevator.h | 1 + 5 files changed, 39 insertions(+) (limited to 'include/linux') diff --git a/block/blk-timeout.c b/block/blk-timeout.c index b36d07bf0afb..6e5c781c5af1 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -153,3 +153,25 @@ void blk_add_timer(struct request *req) time_before(expiry, q->timeout.expires)) mod_timer(&q->timeout, expiry); } + +/** + * blk_abort_queue -- Abort all request on given queue + * @queue: pointer to queue + * + */ +void blk_abort_queue(struct request_queue *q) +{ + unsigned long flags; + struct request *rq, *tmp; + + spin_lock_irqsave(q->queue_lock, flags); + + elv_abort_queue(q); + + list_for_each_entry_safe(rq, tmp, &q->timeout_list, timeout_list) + blk_abort_request(rq); + + spin_unlock_irqrestore(q->queue_lock, flags); + +} +EXPORT_SYMBOL_GPL(blk_abort_queue); diff --git a/block/elevator.c b/block/elevator.c index a91fc59edd01..8a74eedc3530 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -914,6 +914,19 @@ int elv_may_queue(struct request_queue *q, int rw) return ELV_MQUEUE_MAY; } +void elv_abort_queue(struct request_queue *q) +{ + struct request *rq; + + while (!list_empty(&q->queue_head)) { + rq = list_entry_rq(q->queue_head.next); + rq->cmd_flags |= REQ_QUIET; + blk_add_trace_rq(q, rq, BLK_TA_ABORT); + end_queued_request(rq, 0); + } +} +EXPORT_SYMBOL(elv_abort_queue); + void elv_completed_request(struct request_queue *q, struct request *rq) { elevator_t *e = q->elevator; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 067f28b80072..37781d6fe045 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -788,6 +788,7 @@ extern int blk_end_request_callback(struct request *rq, int error, extern void blk_complete_request(struct request *); extern void __blk_complete_request(struct request *); extern void blk_abort_request(struct request *); +extern void blk_abort_queue(struct request_queue *); /* * blk_end_request() takes bytes instead of sectors as a complete size. diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 27da2cc682ee..dcaf2452ed1f 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -48,6 +48,7 @@ enum blktrace_act { __BLK_TA_SPLIT, /* bio was split */ __BLK_TA_BOUNCE, /* bio was bounced */ __BLK_TA_REMAP, /* bio was remapped */ + __BLK_TA_ABORT, /* request aborted */ }; /* @@ -78,6 +79,7 @@ enum blktrace_notify { #define BLK_TA_SPLIT (__BLK_TA_SPLIT) #define BLK_TA_BOUNCE (__BLK_TA_BOUNCE) #define BLK_TA_REMAP (__BLK_TA_REMAP | BLK_TC_ACT(BLK_TC_QUEUE)) +#define BLK_TA_ABORT (__BLK_TA_ABORT | BLK_TC_ACT(BLK_TC_QUEUE)) #define BLK_TN_PROCESS (__BLK_TN_PROCESS | BLK_TC_ACT(BLK_TC_NOTIFY)) #define BLK_TN_TIMESTAMP (__BLK_TN_TIMESTAMP | BLK_TC_ACT(BLK_TC_NOTIFY)) diff --git a/include/linux/elevator.h b/include/linux/elevator.h index bb791c311a56..92f6f634e3e6 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -112,6 +112,7 @@ extern struct request *elv_latter_request(struct request_queue *, struct request extern int elv_register_queue(struct request_queue *q); extern void elv_unregister_queue(struct request_queue *q); extern int elv_may_queue(struct request_queue *, int); +extern void elv_abort_queue(struct request_queue *); extern void elv_completed_request(struct request_queue *, struct request *); extern int elv_set_request(struct request_queue *, struct request *, gfp_t); extern void elv_put_request(struct request_queue *, struct request *); -- cgit v1.2.3 From 3e6053d76dcbd92b2f9f4ad5ece9bce83149523e Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 11 Sep 2008 10:57:55 +0200 Subject: block: adjust blkdev_issue_discard for swap Two mods to blkdev_issue_discard(), thinking ahead to its use on swap: 1. Add gfp_mask argument, so swap allocation can use it where GFP_KERNEL might deadlock but GFP_NOIO is safe. 2. Enlarge nr_sects argument from unsigned to sector_t: unsigned long is enough to cover a whole swap area, but sector_t suits any partition. Change sb_issue_discard()'s nr_blocks to sector_t too; but no need seen for a gfp_mask there, just pass GFP_KERNEL down to blkdev_issue_discard(). Signed-off-by: Hugh Dickins Signed-off-by: Jens Axboe --- block/blk-barrier.c | 7 ++++--- include/linux/blkdev.h | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/block/blk-barrier.c b/block/blk-barrier.c index 988b63479b2f..5c99ff8d2db8 100644 --- a/block/blk-barrier.c +++ b/block/blk-barrier.c @@ -332,12 +332,13 @@ static void blkdev_discard_end_io(struct bio *bio, int err) * @bdev: blockdev to issue discard for * @sector: start sector * @nr_sects: number of sectors to discard + * @gfp_mask: memory allocation flags (for bio_alloc) * * Description: * Issue a discard request for the sectors in question. Does not wait. */ -int blkdev_issue_discard(struct block_device *bdev, sector_t sector, - unsigned nr_sects) +int blkdev_issue_discard(struct block_device *bdev, + sector_t sector, sector_t nr_sects, gfp_t gfp_mask) { struct request_queue *q; struct bio *bio; @@ -354,7 +355,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, return -EOPNOTSUPP; while (nr_sects && !ret) { - bio = bio_alloc(GFP_KERNEL, 0); + bio = bio_alloc(gfp_mask, 0); if (!bio) return -ENOMEM; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 37781d6fe045..b47767c72ce3 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -873,15 +874,15 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt, } extern int blkdev_issue_flush(struct block_device *, sector_t *); -extern int blkdev_issue_discard(struct block_device *, sector_t sector, - unsigned nr_sects); +extern int blkdev_issue_discard(struct block_device *, + sector_t sector, sector_t nr_sects, gfp_t); static inline int sb_issue_discard(struct super_block *sb, - sector_t block, unsigned nr_blocks) + sector_t block, sector_t nr_blocks) { block <<= (sb->s_blocksize_bits - 9); nr_blocks <<= (sb->s_blocksize_bits - 9); - return blkdev_issue_discard(sb->s_bdev, block, nr_blocks); + return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_KERNEL); } /* -- cgit v1.2.3 From 0a0d96b03a1f3bfd6bc3ea08008699e8e59fccd9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 11 Sep 2008 13:17:37 +0200 Subject: block: add bio_kmalloc() Not all callers need (or want!) the mempool backing guarentee, it essentially means that you can only use bio_alloc() for short allocations and not for preallocating some bio's at setup or init time. So add bio_kmalloc() which does the same thing as bio_alloc(), except it just uses kmalloc() as the backing instead of the bio mempools. Signed-off-by: Jens Axboe --- fs/bio.c | 96 +++++++++++++++++++++++++++++++++++++++++------------ include/linux/bio.h | 1 + 2 files changed, 76 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/bio.c b/fs/bio.c index 355302985e22..e56e7685af9c 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -60,25 +60,46 @@ struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct struct bio_vec *bvl; /* - * see comment near bvec_array define! + * If 'bs' is given, lookup the pool and do the mempool alloc. + * If not, this is a bio_kmalloc() allocation and just do a + * kzalloc() for the exact number of vecs right away. */ - switch (nr) { - case 1 : *idx = 0; break; - case 2 ... 4: *idx = 1; break; - case 5 ... 16: *idx = 2; break; - case 17 ... 64: *idx = 3; break; - case 65 ... 128: *idx = 4; break; - case 129 ... BIO_MAX_PAGES: *idx = 5; break; + if (bs) { + /* + * see comment near bvec_array define! + */ + switch (nr) { + case 1: + *idx = 0; + break; + case 2 ... 4: + *idx = 1; + break; + case 5 ... 16: + *idx = 2; + break; + case 17 ... 64: + *idx = 3; + break; + case 65 ... 128: + *idx = 4; + break; + case 129 ... BIO_MAX_PAGES: + *idx = 5; + break; default: return NULL; - } - /* - * idx now points to the pool we want to allocate from - */ + } - bvl = mempool_alloc(bs->bvec_pools[*idx], gfp_mask); - if (bvl) - memset(bvl, 0, bvec_nr_vecs(*idx) * sizeof(struct bio_vec)); + /* + * idx now points to the pool we want to allocate from + */ + bvl = mempool_alloc(bs->bvec_pools[*idx], gfp_mask); + if (bvl) + memset(bvl, 0, + bvec_nr_vecs(*idx) * sizeof(struct bio_vec)); + } else + bvl = kzalloc(nr * sizeof(struct bio_vec), gfp_mask); return bvl; } @@ -107,6 +128,12 @@ static void bio_fs_destructor(struct bio *bio) bio_free(bio, fs_bio_set); } +static void bio_kmalloc_destructor(struct bio *bio) +{ + kfree(bio->bi_io_vec); + kfree(bio); +} + void bio_init(struct bio *bio) { memset(bio, 0, sizeof(*bio)); @@ -119,19 +146,25 @@ void bio_init(struct bio *bio) * bio_alloc_bioset - allocate a bio for I/O * @gfp_mask: the GFP_ mask given to the slab allocator * @nr_iovecs: number of iovecs to pre-allocate - * @bs: the bio_set to allocate from + * @bs: the bio_set to allocate from. If %NULL, just use kmalloc * * Description: - * bio_alloc_bioset will first try it's on mempool to satisfy the allocation. + * bio_alloc_bioset will first try its own mempool to satisfy the allocation. * If %__GFP_WAIT is set then we will block on the internal pool waiting - * for a &struct bio to become free. + * for a &struct bio to become free. If a %NULL @bs is passed in, we will + * fall back to just using @kmalloc to allocate the required memory. * * allocate bio and iovecs from the memory pools specified by the - * bio_set structure. + * bio_set structure, or @kmalloc if none given. **/ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs) { - struct bio *bio = mempool_alloc(bs->bio_pool, gfp_mask); + struct bio *bio; + + if (bs) + bio = mempool_alloc(bs->bio_pool, gfp_mask); + else + bio = kmalloc(sizeof(*bio), gfp_mask); if (likely(bio)) { struct bio_vec *bvl = NULL; @@ -142,7 +175,10 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs) bvl = bvec_alloc_bs(gfp_mask, nr_iovecs, &idx, bs); if (unlikely(!bvl)) { - mempool_free(bio, bs->bio_pool); + if (bs) + mempool_free(bio, bs->bio_pool); + else + kfree(bio); bio = NULL; goto out; } @@ -165,6 +201,23 @@ struct bio *bio_alloc(gfp_t gfp_mask, int nr_iovecs) return bio; } +/* + * Like bio_alloc(), but doesn't use a mempool backing. This means that + * it CAN fail, but while bio_alloc() can only be used for allocations + * that have a short (finite) life span, bio_kmalloc() should be used + * for more permanent bio allocations (like allocating some bio's for + * initalization or setup purposes). + */ +struct bio *bio_kmalloc(gfp_t gfp_mask, int nr_iovecs) +{ + struct bio *bio = bio_alloc_bioset(gfp_mask, nr_iovecs, NULL); + + if (bio) + bio->bi_destructor = bio_kmalloc_destructor; + + return bio; +} + void zero_fill_bio(struct bio *bio) { unsigned long flags; @@ -1349,6 +1402,7 @@ static int __init init_bio(void) subsys_initcall(init_bio); EXPORT_SYMBOL(bio_alloc); +EXPORT_SYMBOL(bio_kmalloc); EXPORT_SYMBOL(bio_put); EXPORT_SYMBOL(bio_free); EXPORT_SYMBOL(bio_endio); diff --git a/include/linux/bio.h b/include/linux/bio.h index 7af373f253dc..6520ee1a3f6d 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -308,6 +308,7 @@ extern struct bio_set *bioset_create(int, int); extern void bioset_free(struct bio_set *); extern struct bio *bio_alloc(gfp_t, int); +extern struct bio *bio_kmalloc(gfp_t, int); extern struct bio *bio_alloc_bioset(gfp_t, int, struct bio_set *); extern void bio_put(struct bio *); extern void bio_free(struct bio *, struct bio_set *); -- cgit v1.2.3 From 581d4e28d9195aa8b2231383dbabc288988d615e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 14 Sep 2008 05:56:33 -0700 Subject: block: add fault injection mechanism for faking request timeouts Only works for the generic request timer handling. Allows one to sporadically ignore request completions, thus exercising the timeout handling. Signed-off-by: Jens Axboe --- block/blk-softirq.c | 2 ++ block/blk-timeout.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ block/blk.h | 12 ++++++++++ block/genhd.c | 8 +++++++ include/linux/blkdev.h | 1 + lib/Kconfig.debug | 13 ++++++++++- 6 files changed, 94 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/block/blk-softirq.c b/block/blk-softirq.c index 7ab344afb16f..e660d26ca656 100644 --- a/block/blk-softirq.c +++ b/block/blk-softirq.c @@ -154,6 +154,8 @@ do_local: **/ void blk_complete_request(struct request *req) { + if (unlikely(blk_should_fake_timeout(req->q))) + return; if (!blk_mark_rq_complete(req)) __blk_complete_request(req); } diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 6e5c781c5af1..9b4ad138bb33 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -4,9 +4,68 @@ #include #include #include +#include #include "blk.h" +#ifdef CONFIG_FAIL_IO_TIMEOUT + +static DECLARE_FAULT_ATTR(fail_io_timeout); + +static int __init setup_fail_io_timeout(char *str) +{ + return setup_fault_attr(&fail_io_timeout, str); +} +__setup("fail_io_timeout=", setup_fail_io_timeout); + +int blk_should_fake_timeout(struct request_queue *q) +{ + if (!test_bit(QUEUE_FLAG_FAIL_IO, &q->queue_flags)) + return 0; + + return should_fail(&fail_io_timeout, 1); +} + +static int __init fail_io_timeout_debugfs(void) +{ + return init_fault_attr_dentries(&fail_io_timeout, "fail_io_timeout"); +} + +late_initcall(fail_io_timeout_debugfs); + +ssize_t part_timeout_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + int set = test_bit(QUEUE_FLAG_FAIL_IO, &disk->queue->queue_flags); + + return sprintf(buf, "%d\n", set != 0); +} + +ssize_t part_timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gendisk *disk = dev_to_disk(dev); + int val; + + if (count) { + struct request_queue *q = disk->queue; + char *p = (char *) buf; + + val = simple_strtoul(p, &p, 10); + spin_lock_irq(q->queue_lock); + if (val) + queue_flag_set(QUEUE_FLAG_FAIL_IO, q); + else + queue_flag_clear(QUEUE_FLAG_FAIL_IO, q); + spin_unlock_irq(q->queue_lock); + } + + return count; +} + +#endif /* CONFIG_FAIL_IO_TIMEOUT */ + /* * blk_delete_timer - Delete/cancel timer for a given function. * @req: request that we are canceling timer for diff --git a/block/blk.h b/block/blk.h index a4f4a50aefaa..e5c579769963 100644 --- a/block/blk.h +++ b/block/blk.h @@ -42,6 +42,18 @@ static inline void blk_clear_rq_complete(struct request *rq) clear_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags); } +#ifdef CONFIG_FAIL_IO_TIMEOUT +int blk_should_fake_timeout(struct request_queue *); +ssize_t part_timeout_show(struct device *, struct device_attribute *, char *); +ssize_t part_timeout_store(struct device *, struct device_attribute *, + const char *, size_t); +#else +static inline int blk_should_fake_timeout(struct request_queue *q) +{ + return 0; +} +#endif + struct io_context *current_io_context(gfp_t gfp_flags, int node); int ll_back_merge_fn(struct request_queue *q, struct request *req, diff --git a/block/genhd.c b/block/genhd.c index 8acaff0154e3..4cd3433c99ac 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -817,6 +817,11 @@ static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); static struct device_attribute dev_attr_fail = __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); #endif +#ifdef CONFIG_FAIL_IO_TIMEOUT +static struct device_attribute dev_attr_fail_timeout = + __ATTR(io-timeout-fail, S_IRUGO|S_IWUSR, part_timeout_show, + part_timeout_store); +#endif static struct attribute *disk_attrs[] = { &dev_attr_range.attr, @@ -828,6 +833,9 @@ static struct attribute *disk_attrs[] = { &dev_attr_stat.attr, #ifdef CONFIG_FAIL_MAKE_REQUEST &dev_attr_fail.attr, +#endif +#ifdef CONFIG_FAIL_IO_TIMEOUT + &dev_attr_fail_timeout.attr, #endif NULL }; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b47767c72ce3..e34999d14c16 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -440,6 +440,7 @@ struct request_queue #define QUEUE_FLAG_BIDI 9 /* queue supports bidi requests */ #define QUEUE_FLAG_NOMERGES 10 /* disable merge attempts */ #define QUEUE_FLAG_SAME_COMP 11 /* force complete on same CPU */ +#define QUEUE_FLAG_FAIL_IO 12 /* fake timeout */ static inline int queue_is_locked(struct request_queue *q) { diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index c556896abe57..7d7a31d0ddeb 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -683,10 +683,21 @@ config FAIL_PAGE_ALLOC config FAIL_MAKE_REQUEST bool "Fault-injection capability for disk IO" - depends on FAULT_INJECTION + depends on FAULT_INJECTION && BLOCK help Provide fault-injection capability for disk IO. +config FAIL_IO_TIMEOUT + bool "Faul-injection capability for faking disk interrupts" + depends on FAULT_INJECTION && BLOCK + help + Provide fault-injection capability on end IO handling. This + will make the block layer "forget" an interrupt as configured, + thus exercising the error handling. + + Only works with drivers that use the generic timeout handling, + for others it wont do anything. + config FAULT_INJECTION_DEBUG_FS bool "Debugfs entries for fault-injection capabilities" depends on FAULT_INJECTION && SYSFS && DEBUG_FS -- cgit v1.2.3 From 9c02f2b02e29a2244e36c6e1f246080d8afc6cff Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 18 Sep 2008 09:31:53 -0700 Subject: block: cleanup some of the integrity stuff in blkdev.h Don't put functions that are only used in fs/bio-integrity.c in blkdev.h, it's much cleaner to just keep it in there. Also kill completely unused bdev_get_tag_size() Signed-off-by: Jens Axboe --- fs/bio-integrity.c | 31 +++++++++++++++++++++++++++++++ include/linux/blkdev.h | 43 ------------------------------------------- 2 files changed, 31 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c index c3e174b35fe6..ba4ada08564a 100644 --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -150,6 +150,29 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, } EXPORT_SYMBOL(bio_integrity_add_page); +static struct blk_integrity *bdev_get_integrity(struct block_device *bdev) +{ + return bdev->bd_disk->integrity; +} + +static int bdev_integrity_enabled(struct block_device *bdev, int rw) +{ + struct blk_integrity *bi = bdev_get_integrity(bdev); + + if (bi == NULL) + return 0; + + if (rw == READ && bi->verify_fn != NULL && + (bi->flags & INTEGRITY_FLAG_READ)) + return 1; + + if (rw == WRITE && bi->generate_fn != NULL && + (bi->flags & INTEGRITY_FLAG_WRITE)) + return 1; + + return 0; +} + /** * bio_integrity_enabled - Check whether integrity can be passed * @bio: bio to check @@ -313,6 +336,14 @@ static void bio_integrity_generate(struct bio *bio) } } +static inline unsigned short blk_integrity_tuple_size(struct blk_integrity *bi) +{ + if (bi) + return bi->tuple_size; + + return 0; +} + /** * bio_integrity_prep - Prepare bio for integrity I/O * @bio: bio to prepare diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e34999d14c16..e23b838825bd 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1004,47 +1004,6 @@ extern int blk_integrity_compare(struct block_device *, struct block_device *); extern int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); extern int blk_rq_count_integrity_sg(struct request *); -static inline unsigned short blk_integrity_tuple_size(struct blk_integrity *bi) -{ - if (bi) - return bi->tuple_size; - - return 0; -} - -static inline struct blk_integrity *bdev_get_integrity(struct block_device *bdev) -{ - return bdev->bd_disk->integrity; -} - -static inline unsigned int bdev_get_tag_size(struct block_device *bdev) -{ - struct blk_integrity *bi = bdev_get_integrity(bdev); - - if (bi) - return bi->tag_size; - - return 0; -} - -static inline int bdev_integrity_enabled(struct block_device *bdev, int rw) -{ - struct blk_integrity *bi = bdev_get_integrity(bdev); - - if (bi == NULL) - return 0; - - if (rw == READ && bi->verify_fn != NULL && - (bi->flags & INTEGRITY_FLAG_READ)) - return 1; - - if (rw == WRITE && bi->generate_fn != NULL && - (bi->flags & INTEGRITY_FLAG_WRITE)) - return 1; - - return 0; -} - static inline int blk_integrity_rq(struct request *rq) { if (rq->bio == NULL) @@ -1058,8 +1017,6 @@ static inline int blk_integrity_rq(struct request *rq) #define blk_integrity_rq(rq) (0) #define blk_rq_count_integrity_sg(a) (0) #define blk_rq_map_integrity_sg(a, b) (0) -#define bdev_get_integrity(a) (0) -#define bdev_get_tag_size(a) (0) #define blk_integrity_compare(a, b) (0) #define blk_integrity_register(a, b) (0) #define blk_integrity_unregister(a) do { } while (0); -- cgit v1.2.3 From 32fab448e5e86694beade415e750363538ea5f49 Mon Sep 17 00:00:00 2001 From: Kiyoshi Ueda Date: Thu, 18 Sep 2008 10:45:09 -0400 Subject: block: add request update interface This patch adds blk_update_request(), which updates struct request with completing its data part, but doesn't complete the struct request itself. Though it looks like end_that_request_first() of older kernels, blk_update_request() should be used only by request stacking drivers. Request-based dm will use it in bio->bi_end_io callback to update the original request when a data part of a cloned request completes. Followings are additional background information of why request-based dm needs this interface. - Request stacking drivers can't use blk_end_request() directly from the lower driver's completion context (bio->bi_end_io or rq->end_io), because some device drivers (e.g. ide) may try to complete their request with queue lock held, and it may cause deadlock. See below for detailed description of possible deadlock: - To solve that, request-based dm offloads the completion of cloned struct request to softirq context (i.e. using blk_complete_request() from rq->end_io). - Though it is possible to use the same solution from bio->bi_end_io, it will delay the notification of bio completion to the original submitter. Also, it will cause inefficient partial completion, because the lower driver can't perform the cloned request anymore and request-based dm needs to requeue and redispatch it to the lower driver again later. That's not good. - So request-based dm needs blk_update_request() to perform the bio completion in the lower driver's completion context, which is more efficient. Signed-off-by: Kiyoshi Ueda Signed-off-by: Jun'ichi Nomura Signed-off-by: Jens Axboe --- block/blk-core.c | 57 ++++++++++++++++++++++++++++++++++++++++++-------- include/linux/blkdev.h | 2 ++ 2 files changed, 50 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 37fba001bdcf..527d43e982bb 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1806,6 +1806,22 @@ void end_request(struct request *req, int uptodate) } EXPORT_SYMBOL(end_request); +static int end_that_request_data(struct request *rq, int error, + unsigned int nr_bytes, unsigned int bidi_bytes) +{ + if (rq->bio) { + if (__end_that_request_first(rq, error, nr_bytes)) + return 1; + + /* Bidi request must be completed as a whole */ + if (blk_bidi_rq(rq) && + __end_that_request_first(rq->next_rq, error, bidi_bytes)) + return 1; + } + + return 0; +} + /** * blk_end_io - Generic end_io function to complete a request. * @rq: the request being processed @@ -1832,15 +1848,8 @@ static int blk_end_io(struct request *rq, int error, unsigned int nr_bytes, struct request_queue *q = rq->q; unsigned long flags = 0UL; - if (rq->bio) { - if (__end_that_request_first(rq, error, nr_bytes)) - return 1; - - /* Bidi request must be completed as a whole */ - if (blk_bidi_rq(rq) && - __end_that_request_first(rq->next_rq, error, bidi_bytes)) - return 1; - } + if (end_that_request_data(rq, error, nr_bytes, bidi_bytes)) + return 1; /* Special feature for tricky drivers */ if (drv_callback && drv_callback(rq)) @@ -1922,6 +1931,36 @@ int blk_end_bidi_request(struct request *rq, int error, unsigned int nr_bytes, } EXPORT_SYMBOL_GPL(blk_end_bidi_request); +/** + * blk_update_request - Special helper function for request stacking drivers + * @rq: the request being processed + * @error: %0 for success, < %0 for error + * @nr_bytes: number of bytes to complete @rq + * + * Description: + * Ends I/O on a number of bytes attached to @rq, but doesn't complete + * the request structure even if @rq doesn't have leftover. + * If @rq has leftover, sets it up for the next range of segments. + * + * This special helper function is only for request stacking drivers + * (e.g. request-based dm) so that they can handle partial completion. + * Actual device drivers should use blk_end_request instead. + */ +void blk_update_request(struct request *rq, int error, unsigned int nr_bytes) +{ + if (!end_that_request_data(rq, error, nr_bytes, 0)) { + /* + * These members are not updated in end_that_request_data() + * when all bios are completed. + * Update them so that the request stacking driver can find + * how many bytes remain in the request later. + */ + rq->nr_sectors = rq->hard_nr_sectors = 0; + rq->current_nr_sectors = rq->hard_cur_sectors = 0; + } +} +EXPORT_SYMBOL_GPL(blk_update_request); + /** * blk_end_request_callback - Special helper function for tricky drivers * @rq: the request being processed diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e23b838825bd..e82a84c9f37a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -791,6 +791,8 @@ extern void blk_complete_request(struct request *); extern void __blk_complete_request(struct request *); extern void blk_abort_request(struct request *); extern void blk_abort_queue(struct request_queue *); +extern void blk_update_request(struct request *rq, int error, + unsigned int nr_bytes); /* * blk_end_request() takes bytes instead of sectors as a complete size. -- cgit v1.2.3 From 82124d60354846623a4b94af335717a5e142a074 Mon Sep 17 00:00:00 2001 From: Kiyoshi Ueda Date: Thu, 18 Sep 2008 10:45:38 -0400 Subject: block: add request submission interface This patch adds blk_insert_cloned_request(), a generic request submission interface for request stacking drivers. Request-based dm will use it to submit their clones to underlying devices. blk_rq_check_limits() is also added because it is possible that the lower queue has stronger limitations than the upper queue if multiple drivers are stacking at request-level. Not only for blk_insert_cloned_request()'s internal use, the function will be used by request-based dm when the queue limitation is modified (e.g. by replacing dm's table). Signed-off-by: Kiyoshi Ueda Signed-off-by: Jun'ichi Nomura Signed-off-by: Jens Axboe --- block/blk-core.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 3 ++ 2 files changed, 84 insertions(+) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index 527d43e982bb..b8ffbfe85ca4 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1529,6 +1529,87 @@ void submit_bio(int rw, struct bio *bio) } EXPORT_SYMBOL(submit_bio); +/** + * blk_rq_check_limits - Helper function to check a request for the queue limit + * @q: the queue + * @rq: the request being checked + * + * Description: + * @rq may have been made based on weaker limitations of upper-level queues + * in request stacking drivers, and it may violate the limitation of @q. + * Since the block layer and the underlying device driver trust @rq + * after it is inserted to @q, it should be checked against @q before + * the insertion using this generic function. + * + * This function should also be useful for request stacking drivers + * in some cases below, so export this fuction. + * Request stacking drivers like request-based dm may change the queue + * limits while requests are in the queue (e.g. dm's table swapping). + * Such request stacking drivers should check those requests agaist + * the new queue limits again when they dispatch those requests, + * although such checkings are also done against the old queue limits + * when submitting requests. + */ +int blk_rq_check_limits(struct request_queue *q, struct request *rq) +{ + if (rq->nr_sectors > q->max_sectors || + rq->data_len > q->max_hw_sectors << 9) { + printk(KERN_ERR "%s: over max size limit.\n", __func__); + return -EIO; + } + + /* + * queue's settings related to segment counting like q->bounce_pfn + * may differ from that of other stacking queues. + * Recalculate it to check the request correctly on this queue's + * limitation. + */ + blk_recalc_rq_segments(rq); + if (rq->nr_phys_segments > q->max_phys_segments || + rq->nr_phys_segments > q->max_hw_segments) { + printk(KERN_ERR "%s: over max segments limit.\n", __func__); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(blk_rq_check_limits); + +/** + * blk_insert_cloned_request - Helper for stacking drivers to submit a request + * @q: the queue to submit the request + * @rq: the request being queued + */ +int blk_insert_cloned_request(struct request_queue *q, struct request *rq) +{ + unsigned long flags; + + if (blk_rq_check_limits(q, rq)) + return -EIO; + +#ifdef CONFIG_FAIL_MAKE_REQUEST + if (rq->rq_disk && rq->rq_disk->part0.make_it_fail && + should_fail(&fail_make_request, blk_rq_bytes(rq))) + return -EIO; +#endif + + spin_lock_irqsave(q->queue_lock, flags); + + /* + * Submitting request must be dequeued before calling this function + * because it will be linked to another request_queue + */ + BUG_ON(blk_queued_rq(rq)); + + drive_stat_acct(rq, 1); + __elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 0); + + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(blk_insert_cloned_request); + /** * __end_that_request_first - end I/O on a request * @req: the request being processed diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e82a84c9f37a..964c246bc271 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -693,6 +693,9 @@ extern void __blk_put_request(struct request_queue *, struct request *); extern struct request *blk_get_request(struct request_queue *, int, gfp_t); extern void blk_insert_request(struct request_queue *, struct request *, int, void *); extern void blk_requeue_request(struct request_queue *, struct request *); +extern int blk_rq_check_limits(struct request_queue *q, struct request *rq); +extern int blk_insert_cloned_request(struct request_queue *q, + struct request *rq); extern void blk_plug_device(struct request_queue *); extern void blk_plug_device_unlocked(struct request_queue *); extern int blk_remove_plug(struct request_queue *); -- cgit v1.2.3 From 4ee5eaf4516a60f8ef64d3c246c64c6be0cf8c3a Mon Sep 17 00:00:00 2001 From: Kiyoshi Ueda Date: Thu, 18 Sep 2008 10:46:13 -0400 Subject: block: add a queue flag for request stacking support This patch adds a queue flag to indicate the block device can be used for request stacking. Request stacking drivers need to stack their devices on top of only devices of which q->request_fn is functional. Since bio stacking drivers (e.g. md, loop) basically initialize their queue using blk_alloc_queue() and don't set q->request_fn, the check of (q->request_fn == NULL) looks enough for that purpose. However, dm will become both types of stacking driver (bio-based and request-based). And dm will always set q->request_fn even if the dm device is bio-based of which q->request_fn is not functional actually. So we need something else to distinguish the type of the device. Adding a queue flag is a solution for that. The reason why dm always sets q->request_fn is to keep the compatibility of dm user-space tools. Currently, all dm user-space tools are using bio-based dm without specifying the type of the dm device they use. To use request-based dm without changing such tools, the kernel must decide the type of the dm device automatically. The automatic type decision can't be done at the device creation time and needs to be deferred until such tools load a mapping table, since the actual type is decided by dm target type included in the mapping table. So a dm device has to be initialized using blk_init_queue() so that we can load either type of table. Then, all queue stuffs are set (e.g. q->request_fn) and we have no element to distinguish that it is bio-based or request-based, even after a table is loaded and the type of the device is decided. By the way, some stuffs of the queue (e.g. request_list, elevator) are needless when the dm device is used as bio-based. But the memory size is not so large (about 20[KB] per queue on ia64), so I hope the memory loss can be acceptable for bio-based dm users. Signed-off-by: Kiyoshi Ueda Signed-off-by: Jun'ichi Nomura Signed-off-by: Jens Axboe --- block/blk-core.c | 3 ++- include/linux/blkdev.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index b8ffbfe85ca4..fa212348c4c9 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -574,7 +574,8 @@ blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id) q->request_fn = rfn; q->prep_rq_fn = NULL; q->unplug_fn = generic_unplug_device; - q->queue_flags = (1 << QUEUE_FLAG_CLUSTER); + q->queue_flags = (1 << QUEUE_FLAG_CLUSTER | + 1 << QUEUE_FLAG_STACKABLE); q->queue_lock = lock; blk_queue_segment_boundary(q, 0xffffffff); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 964c246bc271..86f77ef127f4 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -441,6 +441,7 @@ struct request_queue #define QUEUE_FLAG_NOMERGES 10 /* disable merge attempts */ #define QUEUE_FLAG_SAME_COMP 11 /* force complete on same CPU */ #define QUEUE_FLAG_FAIL_IO 12 /* fake timeout */ +#define QUEUE_FLAG_STACKABLE 13 /* supports request stacking */ static inline int queue_is_locked(struct request_queue *q) { @@ -547,6 +548,8 @@ enum { #define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags) #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags) #define blk_queue_flushing(q) ((q)->ordseq) +#define blk_queue_stackable(q) \ + test_bit(QUEUE_FLAG_STACKABLE, &(q)->queue_flags) #define blk_fs_request(rq) ((rq)->cmd_type == REQ_TYPE_FS) #define blk_pc_request(rq) ((rq)->cmd_type == REQ_TYPE_BLOCK_PC) -- cgit v1.2.3 From 9e49184c82e9ec3ab4d45f9ea5a17ccaf43869f0 Mon Sep 17 00:00:00 2001 From: Keith Wansbrough Date: Mon, 22 Sep 2008 14:57:17 -0700 Subject: floppy: support arbitrary first-sector numbers The current floppy_struct allows floppies to number sectors starting from 0 or 1. This patch allows arbitrary first-sector numbers - for example, 0xC1 for Amstrad CPC disks. This extends the existing 1-bit field (FD_ZEROBASED, bit 2 of stretch) to 8 bits (FD_SECTMASK, bits 2 to 9). Currently 0x00 denotes a first sector number of 1, and 0x01 denotes a first sector number of 0. We extend this by interpreting FD_SECTMASK as the first sector number with the LSB flipped. Signed-off-by: Keith Wansbrough Cc: Alain Knaff Cc: Michael Kerrisk Cc: Karel Zak Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/floppy.c | 23 +++++++++++++++-------- include/linux/fd.h | 8 +++++++- 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 9c0b494f5e87..cf64ddf5d839 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -423,8 +423,15 @@ static struct floppy_raw_cmd *raw_cmd, default_raw_cmd; * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical * side 0 is on physical side 0 (but with the misnamed sector IDs). * 'stretch' should probably be renamed to something more general, like - * 'options'. Other parameters should be self-explanatory (see also - * setfdprm(8)). + * 'options'. + * + * Bits 2 through 9 of 'stretch' tell the number of the first sector. + * The LSB (bit 2) is flipped. For most disks, the first sector + * is 1 (represented by 0x00<<2). For some CP/M and music sampler + * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2). + * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2). + * + * Other parameters should be self-explanatory (see also setfdprm(8)). */ /* Size @@ -2236,9 +2243,9 @@ static void setup_format_params(int track) } } } - if (_floppy->stretch & FD_ZEROBASED) { + if (_floppy->stretch & FD_SECTBASEMASK) { for (count = 0; count < F_SECT_PER_TRACK; count++) - here[count].sect--; + here[count].sect += FD_SECTBASE(_floppy) - 1; } } @@ -2649,7 +2656,7 @@ static int make_raw_rw_request(void) } HEAD = fsector_t / _floppy->sect; - if (((_floppy->stretch & (FD_SWAPSIDES | FD_ZEROBASED)) || + if (((_floppy->stretch & (FD_SWAPSIDES | FD_SECTBASEMASK)) || TESTF(FD_NEED_TWADDLE)) && fsector_t < _floppy->sect) max_sector = _floppy->sect; @@ -2679,7 +2686,7 @@ static int make_raw_rw_request(void) CODE2SIZE; SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE; SECTOR = ((fsector_t % _floppy->sect) << 2 >> SIZECODE) + - ((_floppy->stretch & FD_ZEROBASED) ? 0 : 1); + FD_SECTBASE(_floppy); /* tracksize describes the size which can be filled up with sectors * of size ssize. @@ -3311,7 +3318,7 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g, g->head <= 0 || g->track <= 0 || g->track > UDP->tracks >> STRETCH(g) || /* check if reserved bits are set */ - (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_ZEROBASED)) != 0) + (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0) return -EINVAL; if (type) { if (!capable(CAP_SYS_ADMIN)) @@ -3356,7 +3363,7 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g, if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack || ((user_params[drive].sect ^ oldStretch) & - (FD_SWAPSIDES | FD_ZEROBASED))) + (FD_SWAPSIDES | FD_SECTBASEMASK))) invalidate_drive(bdev); else process_fd_request(); diff --git a/include/linux/fd.h b/include/linux/fd.h index b6bd41d2b460..f5d194af07a8 100644 --- a/include/linux/fd.h +++ b/include/linux/fd.h @@ -15,10 +15,16 @@ struct floppy_struct { sect, /* sectors per track */ head, /* nr of heads */ track, /* nr of tracks */ - stretch; /* !=0 means double track steps */ + stretch; /* bit 0 !=0 means double track steps */ + /* bit 1 != 0 means swap sides */ + /* bits 2..9 give the first sector */ + /* number (the LSB is flipped) */ #define FD_STRETCH 1 #define FD_SWAPSIDES 2 #define FD_ZEROBASED 4 +#define FD_SECTBASEMASK 0x3FC +#define FD_MKSECTBASE(s) (((s) ^ 1) << 2) +#define FD_SECTBASE(floppy) ((((floppy)->stretch & FD_SECTBASEMASK) >> 2) ^ 1) unsigned char gap, /* gap1 size */ -- cgit v1.2.3 From a68bbddba486020c9c74825ce90c4c1ec463e0e8 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 24 Sep 2008 13:03:33 +0200 Subject: block: add queue flag for SSD/non-rotational devices We don't want to idle in AS/CFQ if the device doesn't have a seek penalty. So add a QUEUE_FLAG_NONROT to indicate a non-rotational device, low level drivers should set this flag upon discovery of an SSD or similar device type. Signed-off-by: Jens Axboe --- block/as-iosched.c | 6 ++++++ block/cfq-iosched.c | 6 ++++++ include/linux/blkdev.h | 2 ++ 3 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/block/as-iosched.c b/block/as-iosched.c index 80af9257e64a..4c6fafbba933 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -745,6 +745,12 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq) */ static int as_can_anticipate(struct as_data *ad, struct request *rq) { + /* + * SSD device without seek penalty, disable idling + */ + if (blk_queue_nonrot(ad->q)) + return 0; + if (!ad->io_context) /* * Last request submitted was a write diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 494b6fdcb183..03a5953bb5df 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -878,6 +878,12 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) struct cfq_io_context *cic; unsigned long sl; + /* + * SSD device without seek penalty, disable idling + */ + if (blk_queue_nonrot(cfqd->queue)) + return; + WARN_ON(!RB_EMPTY_ROOT(&cfqq->sort_list)); WARN_ON(cfq_cfqq_slice_new(cfqq)); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 86f77ef127f4..0cf3e619fb21 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -442,6 +442,7 @@ struct request_queue #define QUEUE_FLAG_SAME_COMP 11 /* force complete on same CPU */ #define QUEUE_FLAG_FAIL_IO 12 /* fake timeout */ #define QUEUE_FLAG_STACKABLE 13 /* supports request stacking */ +#define QUEUE_FLAG_NONROT 14 /* non-rotational device (SSD) */ static inline int queue_is_locked(struct request_queue *q) { @@ -547,6 +548,7 @@ enum { #define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags) #define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags) #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags) +#define blk_queue_nonrot(q) test_bit(QUEUE_FLAG_NONROT, &(q)->queue_flags) #define blk_queue_flushing(q) ((q)->ordseq) #define blk_queue_stackable(q) \ test_bit(QUEUE_FLAG_STACKABLE, &(q)->queue_flags) -- cgit v1.2.3 From 8bff7c6b0f63c7ee9c5e3a076338d74125b8debb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 24 Sep 2008 13:05:10 +0200 Subject: libata: set queue SSD flag for SSD devices SSD devices should give an RPM setting of 1 in word 217 of the ID page. If we see such a device, tell the block layer about it. Signed-off-by: Jens Axboe --- drivers/ata/libata-scsi.c | 4 ++++ include/linux/ata.h | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index b9d3ba423cb2..054370700abf 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -977,6 +977,10 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, blk_queue_dma_drain(q, atapi_drain_needed, buf, ATAPI_MAX_DRAIN); } else { + if (ata_id_is_ssd(dev->id)) + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, + sdev->request_queue); + /* ATA devices must be sector aligned */ blk_queue_update_dma_alignment(sdev->request_queue, ATA_SECT_SIZE - 1); diff --git a/include/linux/ata.h b/include/linux/ata.h index 8a12d718c169..c1c8b4a4ba26 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -88,6 +88,7 @@ enum { ATA_ID_DLF = 128, ATA_ID_CSFO = 129, ATA_ID_CFA_POWER = 160, + ATA_ID_ROT_SPEED = 217, ATA_ID_PIO4 = (1 << 1), ATA_ID_SERNO_LEN = 20, @@ -691,6 +692,11 @@ static inline int ata_id_is_cfa(const u16 *id) return 0; } +static inline int ata_id_is_ssd(const u16 *id) +{ + return id[ATA_ID_ROT_SPEED] == 0x01; +} + static inline int ata_drive_40wire(const u16 *dev_id) { if (ata_id_is_sata(dev_id)) -- cgit v1.2.3 From c0ddffa84a7d12da9943a94d04dadbfb1883b904 Mon Sep 17 00:00:00 2001 From: Sven Schuetz Date: Fri, 26 Sep 2008 10:58:02 +0200 Subject: include blktrace_api.h in headers_install This header file is of interest for user space programming, i.e. for tools that process blktrace data. We would like to use it for a tool on-top of blktrace which processes data provided by blktrace. For this purpose, it would be helpful if the blktrace API would make it to /usr/include/linux. The git tree for the blktrace tools comes with its own copy of this header file. I didn't manage to replace that copy with the file generated by the patch below yet. A few more cleanups would be needed. For example, the blktrace ioctl numbers, which are currently defined in usr/include/fs.h, might need to be moved. Should be feasible, though. Signed-off-by: Sven Schuetz Signed-off-by: Martin Peschke Signed-off-by: Jens Axboe --- include/linux/Kbuild | 1 + include/linux/blktrace_api.h | 58 ++++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index b68ec09399be..31474e89c59a 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -180,6 +180,7 @@ unifdef-y += audit.h unifdef-y += auto_fs.h unifdef-y += auxvec.h unifdef-y += binfmts.h +unifdef-y += blktrace_api.h unifdef-y += capability.h unifdef-y += capi.h unifdef-y += cciss_ioctl.h diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index dcaf2452ed1f..a2a7d0ca2758 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -1,8 +1,10 @@ #ifndef BLKTRACE_H #define BLKTRACE_H +#ifdef __KERNEL__ #include #include +#endif /* * Trace categories @@ -92,17 +94,17 @@ enum blktrace_notify { * The trace itself */ struct blk_io_trace { - u32 magic; /* MAGIC << 8 | version */ - u32 sequence; /* event number */ - u64 time; /* in microseconds */ - u64 sector; /* disk offset */ - u32 bytes; /* transfer length */ - u32 action; /* what happened */ - u32 pid; /* who did it */ - u32 device; /* device number */ - u32 cpu; /* on what cpu did it happen */ - u16 error; /* completion error */ - u16 pdu_len; /* length of data after this trace */ + __u32 magic; /* MAGIC << 8 | version */ + __u32 sequence; /* event number */ + __u64 time; /* in microseconds */ + __u64 sector; /* disk offset */ + __u32 bytes; /* transfer length */ + __u32 action; /* what happened */ + __u32 pid; /* who did it */ + __u32 device; /* device number */ + __u32 cpu; /* on what cpu did it happen */ + __u16 error; /* completion error */ + __u16 pdu_len; /* length of data after this trace */ }; /* @@ -120,6 +122,25 @@ enum { Blktrace_stopped, }; +/* + * User setup structure passed with BLKTRACESTART + */ +struct blk_user_trace_setup { +#ifdef __KERNEL__ + char name[BDEVNAME_SIZE]; /* output */ +#else + char name[32]; /* output */ +#endif + __u16 act_mask; /* input */ + __u32 buf_size; /* input */ + __u32 buf_nr; /* input */ + __u64 start_lba; + __u64 end_lba; + __u32 pid; +}; + +#ifdef __KERNEL__ +#if defined(CONFIG_BLK_DEV_IO_TRACE) struct blk_trace { int trace_state; struct rchan *rchan; @@ -136,21 +157,6 @@ struct blk_trace { atomic_t dropped; }; -/* - * User setup structure passed with BLKTRACESTART - */ -struct blk_user_trace_setup { - char name[BDEVNAME_SIZE]; /* output */ - u16 act_mask; /* input */ - u32 buf_size; /* input */ - u32 buf_nr; /* input */ - u64 start_lba; - u64 end_lba; - u32 pid; -}; - -#ifdef __KERNEL__ -#if defined(CONFIG_BLK_DEV_IO_TRACE) extern int blk_trace_ioctl(struct block_device *, unsigned, char __user *); extern void blk_trace_shutdown(struct request_queue *); extern void __blk_add_trace(struct blk_trace *, sector_t, int, int, u32, int, int, void *); -- cgit v1.2.3 From ef9e3facdf1fe1228721a7c295a76d1b7a0e57ec Mon Sep 17 00:00:00 2001 From: Kiyoshi Ueda Date: Wed, 1 Oct 2008 16:12:15 +0200 Subject: block: add lld busy state exporting interface This patch adds an new interface, blk_lld_busy(), to check lld's busy state from the block layer. blk_lld_busy() calls down into low-level drivers for the checking if the drivers set q->lld_busy_fn() using blk_queue_lld_busy(). This resolves a performance problem on request stacking devices below. Some drivers like scsi mid layer stop dispatching request when they detect busy state on its low-level device like host/target/device. It allows other requests to stay in the I/O scheduler's queue for a chance of merging. Request stacking drivers like request-based dm should follow the same logic. However, there is no generic interface for the stacked device to check if the underlying device(s) are busy. If the request stacking driver dispatches and submits requests to the busy underlying device, the requests will stay in the underlying device's queue without a chance of merging. This causes performance problem on burst I/O load. With this patch, busy state of the underlying device is exported via q->lld_busy_fn(). So the request stacking driver can check it and stop dispatching requests if busy. The underlying device driver must return the busy state appropriately: 1: when the device driver can't process requests immediately. 0: when the device driver can process requests immediately, including abnormal situations where the device driver needs to kill all requests. Signed-off-by: Kiyoshi Ueda Signed-off-by: Jun'ichi Nomura Cc: Andrew Morton Signed-off-by: Jens Axboe --- block/blk-core.c | 28 ++++++++++++++++++++++++++++ block/blk-settings.c | 6 ++++++ include/linux/blkdev.h | 4 ++++ 3 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index c66333d8e48d..b2d0ac8b760e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2100,6 +2100,34 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, rq->rq_disk = bio->bi_bdev->bd_disk; } +/** + * blk_lld_busy - Check if underlying low-level drivers of a device are busy + * @q : the queue of the device being checked + * + * Description: + * Check if underlying low-level drivers of a device are busy. + * If the drivers want to export their busy state, they must set own + * exporting function using blk_queue_lld_busy() first. + * + * Basically, this function is used only by request stacking drivers + * to stop dispatching requests to underlying devices when underlying + * devices are busy. This behavior helps more I/O merging on the queue + * of the request stacking driver and prevents I/O throughput regression + * on burst I/O load. + * + * Return: + * 0 - Not busy (The request stacking driver should dispatch request) + * 1 - Busy (The request stacking driver should stop dispatching request) + */ +int blk_lld_busy(struct request_queue *q) +{ + if (q->lld_busy_fn) + return q->lld_busy_fn(q); + + return 0; +} +EXPORT_SYMBOL_GPL(blk_lld_busy); + int kblockd_schedule_work(struct request_queue *q, struct work_struct *work) { return queue_work(kblockd_workqueue, work); diff --git a/block/blk-settings.c b/block/blk-settings.c index 1d0330d0b40a..b21dcdb64151 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -89,6 +89,12 @@ void blk_queue_rq_timed_out(struct request_queue *q, rq_timed_out_fn *fn) } EXPORT_SYMBOL_GPL(blk_queue_rq_timed_out); +void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn) +{ + q->lld_busy_fn = fn; +} +EXPORT_SYMBOL_GPL(blk_queue_lld_busy); + /** * blk_queue_make_request - define an alternate make_request function for a device * @q: the request queue for the device to be affected diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 0cf3e619fb21..9e0ee1a8254e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -269,6 +269,7 @@ typedef int (merge_bvec_fn) (struct request_queue *, struct bvec_merge_data *, typedef void (prepare_flush_fn) (struct request_queue *, struct request *); typedef void (softirq_done_fn)(struct request *); typedef int (dma_drain_needed_fn)(struct request *); +typedef int (lld_busy_fn) (struct request_queue *q); enum blk_eh_timer_return { BLK_EH_NOT_HANDLED, @@ -325,6 +326,7 @@ struct request_queue softirq_done_fn *softirq_done_fn; rq_timed_out_fn *rq_timed_out_fn; dma_drain_needed_fn *dma_drain_needed; + lld_busy_fn *lld_busy_fn; /* * Dispatch queue sorting @@ -699,6 +701,7 @@ extern struct request *blk_get_request(struct request_queue *, int, gfp_t); extern void blk_insert_request(struct request_queue *, struct request *, int, void *); extern void blk_requeue_request(struct request_queue *, struct request *); extern int blk_rq_check_limits(struct request_queue *q, struct request *rq); +extern int blk_lld_busy(struct request_queue *q); extern int blk_insert_cloned_request(struct request_queue *q, struct request *rq); extern void blk_plug_device(struct request_queue *); @@ -835,6 +838,7 @@ extern void blk_queue_update_dma_pad(struct request_queue *, unsigned int); extern int blk_queue_dma_drain(struct request_queue *q, dma_drain_needed_fn *dma_drain_needed, void *buf, unsigned int size); +extern void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn); extern void blk_queue_segment_boundary(struct request_queue *, unsigned long); extern void blk_queue_prep_rq(struct request_queue *, prep_rq_fn *pfn); extern void blk_queue_merge_bvec(struct request_queue *, merge_bvec_fn *); -- cgit v1.2.3 From 0497b345e7d067109e0dd9bf9f4978a6847ee13b Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 1 Oct 2008 16:16:25 +0200 Subject: blktrace: use BLKTRACE_BDEV_SIZE as the name size for setup structure Define as 32, which is is what BDEVNAME_SIZE is/was as well. This keeps the user interface the same and gets rid of the difference between kernel and user api here. Signed-off-by: Jens Axboe --- block/blktrace.c | 3 ++- include/linux/blktrace_api.h | 8 +++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/block/blktrace.c b/block/blktrace.c index 9e0212c90b29..85049a7e7a17 100644 --- a/block/blktrace.c +++ b/block/blktrace.c @@ -369,7 +369,8 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, if (!buts->buf_size || !buts->buf_nr) return -EINVAL; - strcpy(buts->name, name); + strncpy(buts->name, name, BLKTRACE_BDEV_SIZE); + buts->name[BLKTRACE_BDEV_SIZE - 1] = '\0'; /* * some device names have larger paths - convert the slashes diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index a2a7d0ca2758..3a31eb506164 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -122,15 +122,13 @@ enum { Blktrace_stopped, }; +#define BLKTRACE_BDEV_SIZE 32 + /* * User setup structure passed with BLKTRACESTART */ struct blk_user_trace_setup { -#ifdef __KERNEL__ - char name[BDEVNAME_SIZE]; /* output */ -#else - char name[32]; /* output */ -#endif + char name[BLKTRACE_BDEV_SIZE]; /* output */ __u16 act_mask; /* input */ __u32 buf_size; /* input */ __u32 buf_nr; /* input */ -- cgit v1.2.3 From d00e29fd99dd63d1c51917604e35dee824ed567f Mon Sep 17 00:00:00 2001 From: Kiyoshi Ueda Date: Wed, 1 Oct 2008 10:14:46 -0400 Subject: block: remove end_{queued|dequeued}_request() This patch removes end_queued_request() and end_dequeued_request(), which are no longer used. As a results, users of __end_request() became only end_request(). So the actual code in __end_request() is moved to end_request() and __end_request() is removed. Signed-off-by: Kiyoshi Ueda Signed-off-by: Jun'ichi Nomura Signed-off-by: Jens Axboe --- block/blk-core.c | 58 ++++++-------------------------------------------- include/linux/blkdev.h | 2 -- 2 files changed, 7 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index b2d0ac8b760e..2d053b584410 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1790,17 +1790,6 @@ static void end_that_request_last(struct request *req, int error) } } -static inline void __end_request(struct request *rq, int uptodate, - unsigned int nr_bytes) -{ - int error = 0; - - if (uptodate <= 0) - error = uptodate ? uptodate : -EIO; - - __blk_end_request(rq, error, nr_bytes); -} - /** * blk_rq_bytes - Returns bytes left to complete in the entire request * @rq: the request being processed @@ -1830,41 +1819,6 @@ unsigned int blk_rq_cur_bytes(struct request *rq) } EXPORT_SYMBOL_GPL(blk_rq_cur_bytes); -/** - * end_queued_request - end all I/O on a queued request - * @rq: the request being processed - * @uptodate: error value or %0/%1 uptodate flag - * - * Description: - * Ends all I/O on a request, and removes it from the block layer queues. - * Not suitable for normal I/O completion, unless the driver still has - * the request attached to the block layer. - * - **/ -void end_queued_request(struct request *rq, int uptodate) -{ - __end_request(rq, uptodate, blk_rq_bytes(rq)); -} -EXPORT_SYMBOL(end_queued_request); - -/** - * end_dequeued_request - end all I/O on a dequeued request - * @rq: the request being processed - * @uptodate: error value or %0/%1 uptodate flag - * - * Description: - * Ends all I/O on a request. The request must already have been - * dequeued using blkdev_dequeue_request(), as is normally the case - * for most drivers. - * - **/ -void end_dequeued_request(struct request *rq, int uptodate) -{ - __end_request(rq, uptodate, blk_rq_bytes(rq)); -} -EXPORT_SYMBOL(end_dequeued_request); - - /** * end_request - end I/O on the current segment of the request * @req: the request being processed @@ -1879,14 +1833,16 @@ EXPORT_SYMBOL(end_dequeued_request); * they have a residual value to account for. For that case this function * isn't really useful, unless the residual just happens to be the * full current segment. In other words, don't use this function in new - * code. Use blk_end_request() or __blk_end_request() to end partial parts - * of a request, or end_dequeued_request() and end_queued_request() to - * completely end IO on a dequeued/queued request. - * + * code. Use blk_end_request() or __blk_end_request() to end a request. **/ void end_request(struct request *req, int uptodate) { - __end_request(req, uptodate, req->hard_cur_sectors << 9); + int error = 0; + + if (uptodate <= 0) + error = uptodate ? uptodate : -EIO; + + __blk_end_request(req, error, req->hard_cur_sectors << 9); } EXPORT_SYMBOL(end_request); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9e0ee1a8254e..bfc18e497c7f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -793,8 +793,6 @@ extern int __blk_end_request(struct request *rq, int error, extern int blk_end_bidi_request(struct request *rq, int error, unsigned int nr_bytes, unsigned int bidi_bytes); extern void end_request(struct request *, int); -extern void end_queued_request(struct request *, int); -extern void end_dequeued_request(struct request *, int); extern int blk_end_request_callback(struct request *rq, int error, unsigned int nr_bytes, int (drv_callback)(struct request *)); -- cgit v1.2.3 From 8deaf7210728c453295dc1cb2a5b66c68183ac85 Mon Sep 17 00:00:00 2001 From: Alberto Bertogli Date: Thu, 2 Oct 2008 12:46:53 +0200 Subject: bio.h: Remove unused conditional code The whole bio_integrity() definition is inside an #ifdef CONFIG_BLK_DEV_INTEGRITY, there's no need for the conditional code. Signed-off-by: Alberto Bertogli Signed-off-by: Jens Axboe --- include/linux/bio.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bio.h b/include/linux/bio.h index 6520ee1a3f6d..98c2d0570657 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -457,14 +457,7 @@ static inline int bio_has_data(struct bio *bio) #define bip_for_each_vec(bvl, bip, i) \ __bip_for_each_vec(bvl, bip, i, (bip)->bip_idx) -static inline int bio_integrity(struct bio *bio) -{ -#if defined(CONFIG_BLK_DEV_INTEGRITY) - return bio->bi_integrity != NULL; -#else - return 0; -#endif -} +#define bio_integrity(bio) (bio->bi_integrity != NULL) extern struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *, gfp_t, unsigned int, struct bio_set *); extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int); -- cgit v1.2.3 From b04accc425d52ca59699290661e0dfd09b0feeeb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 2 Oct 2008 12:53:22 +0200 Subject: block: revert part of d7533ad0e132f92e75c1b2eb7c26387b25a583c1 We need bdev_get_integrity() to support the pending md/dm patches. Signed-off-by: Jens Axboe --- fs/bio-integrity.c | 5 ----- include/linux/blkdev.h | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c index ba4ada08564a..6e28dcdd23a2 100644 --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -150,11 +150,6 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, } EXPORT_SYMBOL(bio_integrity_add_page); -static struct blk_integrity *bdev_get_integrity(struct block_device *bdev) -{ - return bdev->bd_disk->integrity; -} - static int bdev_integrity_enabled(struct block_device *bdev, int rw) { struct blk_integrity *bi = bdev_get_integrity(bdev); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index bfc18e497c7f..bc693f5c3886 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1016,6 +1016,12 @@ extern int blk_integrity_compare(struct block_device *, struct block_device *); extern int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); extern int blk_rq_count_integrity_sg(struct request *); +static inline +struct blk_integrity *bdev_get_integrity(struct block_device *bdev) +{ + return bdev->bd_disk->integrity; +} + static inline int blk_integrity_rq(struct request *rq) { if (rq->bio == NULL) @@ -1029,6 +1035,7 @@ static inline int blk_integrity_rq(struct request *rq) #define blk_integrity_rq(rq) (0) #define blk_rq_count_integrity_sg(a) (0) #define blk_rq_map_integrity_sg(a, b) (0) +#define bdev_get_integrity(a) (0) #define blk_integrity_compare(a, b) (0) #define blk_integrity_register(a, b) (0) #define blk_integrity_unregister(a) do { } while (0); -- cgit v1.2.3 From 74aa8c2cc010035a7eef2b4ca4d6430e0dae206a Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 1 Oct 2008 03:38:37 -0400 Subject: block: Introduce integrity data ownership flag A filesystem might supply its own integrity metadata. Introduce a flag that indicates whether the filesystem or the block layer owns the integrity buffer. Signed-off-by: Martin K. Petersen Signed-off-by: Jens Axboe --- fs/bio-integrity.c | 3 ++- include/linux/bio.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c index 6e28dcdd23a2..19caf7c962ac 100644 --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -107,7 +107,8 @@ void bio_integrity_free(struct bio *bio, struct bio_set *bs) BUG_ON(bip == NULL); /* A cloned bio doesn't own the integrity metadata */ - if (!bio_flagged(bio, BIO_CLONED) && bip->bip_buf != NULL) + if (!bio_flagged(bio, BIO_CLONED) && !bio_flagged(bio, BIO_FS_INTEGRITY) + && bip->bip_buf != NULL) kfree(bip->bip_buf); mempool_free(bip->bip_vec, bs->bvec_pools[bip->bip_pool]); diff --git a/include/linux/bio.h b/include/linux/bio.h index 98c2d0570657..d86d39d490e6 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -109,6 +109,7 @@ struct bio { #define BIO_EOPNOTSUPP 7 /* not supported */ #define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */ #define BIO_NULL_MAPPED 9 /* contains invalid user pages */ +#define BIO_FS_INTEGRITY 10 /* fs owns integrity data, not block layer */ #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) /* -- cgit v1.2.3 From ad7fce93147d32ae53d25d9ea1a8ba31a239deee Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 1 Oct 2008 03:38:39 -0400 Subject: block: Switch blk_integrity_compare from bdev to gendisk The DM and MD integrity support now depends on being able to use gendisks instead of block_devices when comparing integrity profiles. Change function parameters accordingly. Also update comparison logic so that two NULL profiles are a valid configuration. Signed-off-by: Martin K. Petersen Signed-off-by: Jens Axboe --- block/blk-integrity.c | 28 ++++++++++++++-------------- include/linux/blkdev.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/block/blk-integrity.c b/block/blk-integrity.c index e3817a016a12..61a8e2f8fdd0 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -108,51 +108,51 @@ new_segment: EXPORT_SYMBOL(blk_rq_map_integrity_sg); /** - * blk_integrity_compare - Compare integrity profile of two block devices - * @bd1: Device to compare - * @bd2: Device to compare + * blk_integrity_compare - Compare integrity profile of two disks + * @gd1: Disk to compare + * @gd2: Disk to compare * * Description: Meta-devices like DM and MD need to verify that all * sub-devices use the same integrity format before advertising to * upper layers that they can send/receive integrity metadata. This - * function can be used to check whether two block devices have + * function can be used to check whether two gendisk devices have * compatible integrity formats. */ -int blk_integrity_compare(struct block_device *bd1, struct block_device *bd2) +int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2) { - struct blk_integrity *b1 = bd1->bd_disk->integrity; - struct blk_integrity *b2 = bd2->bd_disk->integrity; + struct blk_integrity *b1 = gd1->integrity; + struct blk_integrity *b2 = gd2->integrity; - BUG_ON(bd1->bd_disk == NULL); - BUG_ON(bd2->bd_disk == NULL); + if (!b1 && !b2) + return 0; if (!b1 || !b2) - return 0; + return -1; if (b1->sector_size != b2->sector_size) { printk(KERN_ERR "%s: %s/%s sector sz %u != %u\n", __func__, - bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + gd1->disk_name, gd2->disk_name, b1->sector_size, b2->sector_size); return -1; } if (b1->tuple_size != b2->tuple_size) { printk(KERN_ERR "%s: %s/%s tuple sz %u != %u\n", __func__, - bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + gd1->disk_name, gd2->disk_name, b1->tuple_size, b2->tuple_size); return -1; } if (b1->tag_size && b2->tag_size && (b1->tag_size != b2->tag_size)) { printk(KERN_ERR "%s: %s/%s tag sz %u != %u\n", __func__, - bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + gd1->disk_name, gd2->disk_name, b1->tag_size, b2->tag_size); return -1; } if (strcmp(b1->name, b2->name)) { printk(KERN_ERR "%s: %s/%s type %s != %s\n", __func__, - bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + gd1->disk_name, gd2->disk_name, b1->name, b2->name); return -1; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index bc693f5c3886..00d340b0f758 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1012,7 +1012,7 @@ struct blk_integrity { extern int blk_integrity_register(struct gendisk *, struct blk_integrity *); extern void blk_integrity_unregister(struct gendisk *); -extern int blk_integrity_compare(struct block_device *, struct block_device *); +extern int blk_integrity_compare(struct gendisk *, struct gendisk *); extern int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); extern int blk_rq_count_integrity_sg(struct request *); -- cgit v1.2.3 From b02739b01c5309d74a59859f2ce92c931d1f1955 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Thu, 2 Oct 2008 18:47:49 +0200 Subject: block: gendisk integrity wrapper This is a wrapper for accessing a gendisk's integrity bits. It allows the integrity support in MD to be compiled with BLK_DEV_INTEGRITY off. Signed-off-by: Martin K. Petersen Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 00d340b0f758..a92d9e4ea96e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1022,6 +1022,11 @@ struct blk_integrity *bdev_get_integrity(struct block_device *bdev) return bdev->bd_disk->integrity; } +static inline struct blk_integrity *blk_get_integrity(struct gendisk *disk) +{ + return disk->integrity; +} + static inline int blk_integrity_rq(struct request *rq) { if (rq->bio == NULL) @@ -1036,6 +1041,7 @@ static inline int blk_integrity_rq(struct request *rq) #define blk_rq_count_integrity_sg(a) (0) #define blk_rq_map_integrity_sg(a, b) (0) #define bdev_get_integrity(a) (0) +#define blk_get_integrity(a) (0) #define blk_integrity_compare(a, b) (0) #define blk_integrity_register(a, b) (0) #define blk_integrity_unregister(a) do { } while (0); -- cgit v1.2.3 From ad3316bf4eeb53c89164f759767f911072b56203 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 1 Oct 2008 22:42:53 -0400 Subject: block: Find bio sector offset given idx and offset Helper function to find the sector offset in a bio given bvec index and page offset. Signed-off-by: Martin K. Petersen Signed-off-by: Jens Axboe --- fs/bio.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/bio.h | 1 + 2 files changed, 37 insertions(+) (limited to 'include/linux') diff --git a/fs/bio.c b/fs/bio.c index e56e7685af9c..a5af5809f566 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1300,6 +1300,42 @@ struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors) return bp; } +/** + * bio_sector_offset - Find hardware sector offset in bio + * @bio: bio to inspect + * @index: bio_vec index + * @offset: offset in bv_page + * + * Return the number of hardware sectors between beginning of bio + * and an end point indicated by a bio_vec index and an offset + * within that vector's page. + */ +sector_t bio_sector_offset(struct bio *bio, unsigned short index, + unsigned int offset) +{ + unsigned int sector_sz = queue_hardsect_size(bio->bi_bdev->bd_disk->queue); + struct bio_vec *bv; + sector_t sectors; + int i; + + sectors = 0; + + if (index >= bio->bi_idx) + index = bio->bi_vcnt - 1; + + __bio_for_each_segment(bv, bio, i, 0) { + if (i == index) { + if (offset > bv->bv_offset) + sectors += (offset - bv->bv_offset) / sector_sz; + break; + } + + sectors += bv->bv_len / sector_sz; + } + + return sectors; +} +EXPORT_SYMBOL(bio_sector_offset); /* * create memory pools for biovec's in a bio_set. diff --git a/include/linux/bio.h b/include/linux/bio.h index d86d39d490e6..fe12d0f9ebaa 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -327,6 +327,7 @@ extern int bio_add_page(struct bio *, struct page *, unsigned int,unsigned int); extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *, unsigned int, unsigned int); extern int bio_get_nr_vecs(struct block_device *); +extern sector_t bio_sector_offset(struct bio *, unsigned short, unsigned int); extern struct bio *bio_map_user(struct request_queue *, struct block_device *, unsigned long, unsigned int, int, gfp_t); struct sg_iovec; -- cgit v1.2.3 From 6feef531f55cf4a20fd9eb39f5352e5745203603 Mon Sep 17 00:00:00 2001 From: Denis ChengRq Date: Thu, 9 Oct 2008 08:57:05 +0200 Subject: block: mark bio_split_pool static Since all bio_split calls refer the same single bio_split_pool, the bio_split function can use bio_split_pool directly instead of the mempool_t parameter; then the mempool_t parameter can be removed from bio_split param list, and bio_split_pool is only referred in fs/bio.c file, can be marked static. Signed-off-by: Denis ChengRq Signed-off-by: Jens Axboe --- drivers/block/pktcdvd.c | 2 +- drivers/md/linear.c | 2 +- drivers/md/raid0.c | 2 +- drivers/md/raid10.c | 2 +- fs/bio.c | 9 ++++----- include/linux/bio.h | 4 +--- 6 files changed, 9 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index e1a90bbb4747..0e077150568b 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2544,7 +2544,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio) if (last_zone != zone) { BUG_ON(last_zone != zone + pd->settings.size); first_sectors = last_zone - bio->bi_sector; - bp = bio_split(bio, bio_split_pool, first_sectors); + bp = bio_split(bio, first_sectors); BUG_ON(!bp); pkt_make_request(q, &bp->bio1); pkt_make_request(q, &bp->bio2); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index c80ea90593d3..b9cbee688fae 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -353,7 +353,7 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) * split it. */ struct bio_pair *bp; - bp = bio_split(bio, bio_split_pool, + bp = bio_split(bio, ((tmp_dev->offset + tmp_dev->size)<<1) - bio->bi_sector); if (linear_make_request(q, &bp->bio1)) generic_make_request(&bp->bio1); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index f52f442a735f..53508a8a981d 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -427,7 +427,7 @@ static int raid0_make_request (struct request_queue *q, struct bio *bio) /* This is a one page bio that upper layers * refuse to split for us, so we need to split it. */ - bp = bio_split(bio, bio_split_pool, chunk_sects - (bio->bi_sector & (chunk_sects - 1)) ); + bp = bio_split(bio, chunk_sects - (bio->bi_sector & (chunk_sects - 1))); if (raid0_make_request(q, &bp->bio1)) generic_make_request(&bp->bio1); if (raid0_make_request(q, &bp->bio2)) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5f990133f5ef..8bdc9bfc2887 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -817,7 +817,7 @@ static int make_request(struct request_queue *q, struct bio * bio) /* This is a one page bio that upper layers * refuse to split for us, so we need to split it. */ - bp = bio_split(bio, bio_split_pool, + bp = bio_split(bio, chunk_sects - (bio->bi_sector & (chunk_sects - 1)) ); if (make_request(q, &bp->bio1)) generic_make_request(&bp->bio1); diff --git a/fs/bio.c b/fs/bio.c index a5af5809f566..77a55bcceedb 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -30,7 +30,7 @@ static struct kmem_cache *bio_slab __read_mostly; -mempool_t *bio_split_pool __read_mostly; +static mempool_t *bio_split_pool __read_mostly; /* * if you change this list, also change bvec_alloc or things will @@ -1256,9 +1256,9 @@ static void bio_pair_end_2(struct bio *bi, int err) * split a bio - only worry about a bio with a single page * in it's iovec */ -struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors) +struct bio_pair *bio_split(struct bio *bi, int first_sectors) { - struct bio_pair *bp = mempool_alloc(pool, GFP_NOIO); + struct bio_pair *bp = mempool_alloc(bio_split_pool, GFP_NOIO); if (!bp) return bp; @@ -1292,7 +1292,7 @@ struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors) bp->bio2.bi_end_io = bio_pair_end_2; bp->bio1.bi_private = bi; - bp->bio2.bi_private = pool; + bp->bio2.bi_private = bio_split_pool; if (bio_integrity(bi)) bio_integrity_split(bi, bp, first_sectors); @@ -1455,7 +1455,6 @@ EXPORT_SYMBOL(bio_map_kern); EXPORT_SYMBOL(bio_copy_kern); EXPORT_SYMBOL(bio_pair_release); EXPORT_SYMBOL(bio_split); -EXPORT_SYMBOL(bio_split_pool); EXPORT_SYMBOL(bio_copy_user); EXPORT_SYMBOL(bio_uncopy_user); EXPORT_SYMBOL(bioset_create); diff --git a/include/linux/bio.h b/include/linux/bio.h index fe12d0f9ebaa..fb97221d7c30 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -300,9 +300,7 @@ struct bio_pair { atomic_t cnt; int error; }; -extern struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, - int first_sectors); -extern mempool_t *bio_split_pool; +extern struct bio_pair *bio_split(struct bio *bi, int first_sectors); extern void bio_pair_release(struct bio_pair *dbio); extern struct bio_set *bioset_create(int, int); -- cgit v1.2.3 From af5639424008ffe96f89b059bea1aec15e0115a9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 9 Oct 2008 09:01:10 +0200 Subject: block: add some comments around the bio read-write flags Signed-off-by: Jens Axboe --- include/linux/bio.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/bio.h b/include/linux/bio.h index fb97221d7c30..ff5b4cf9e2da 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -123,13 +123,23 @@ struct bio { /* * bio bi_rw flags * - * bit 0 -- read (not set) or write (set) + * bit 0 -- data direction + * If not set, bio is a read from device. If set, it's a write to device. * bit 1 -- rw-ahead when set * bit 2 -- barrier + * Insert a serialization point in the IO queue, forcing previously + * submitted IO to be completed before this oen is issued. * bit 3 -- fail fast, don't want low level driver retries * bit 4 -- synchronous I/O hint: the block layer will unplug immediately + * Note that this does NOT indicate that the IO itself is sync, just + * that the block layer will not postpone issue of this IO by plugging. * bit 5 -- metadata request + * Used for tracing to differentiate metadata and data IO. May also + * get some preferential treatment in the IO scheduler * bit 6 -- discard sectors + * Informs the lower level device that this range of sectors is no longer + * used by the file system and may thus be freed by the device. Used + * for flash based storage. */ #define BIO_RW 0 /* Must match RW in req flags (blkdev.h) */ #define BIO_RW_AHEAD 1 /* Must match FAILFAST in req flags */ -- cgit v1.2.3 From a5d8c3483a6e19aca95ef6a2c5890e33bfa5b293 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 9 Oct 2008 11:35:51 +0200 Subject: sched debug: add name to sched_domain sysctl entries add /proc/sys/kernel/sched_domain/cpu0/domain0/name, to make it easier to see which specific scheduler domain remained at that entry. Since we process the scheduler domain tree and simplify it, it's not always immediately clear during debugging which domain came from where. depends on CONFIG_SCHED_DEBUG=y. Signed-off-by: Ingo Molnar --- include/linux/sched.h | 3 +++ kernel/sched.c | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index d8e699b55858..5d0819ee442a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -824,6 +824,9 @@ struct sched_domain { unsigned int ttwu_move_affine; unsigned int ttwu_move_balance; #endif +#ifdef CONFIG_SCHED_DEBUG + char *name; +#endif }; extern void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, diff --git a/kernel/sched.c b/kernel/sched.c index 9715f4ce6cfe..6f230596bd0c 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -6351,7 +6351,7 @@ set_table_entry(struct ctl_table *entry, static struct ctl_table * sd_alloc_ctl_domain_table(struct sched_domain *sd) { - struct ctl_table *table = sd_alloc_ctl_entry(12); + struct ctl_table *table = sd_alloc_ctl_entry(13); if (table == NULL) return NULL; @@ -6379,7 +6379,9 @@ sd_alloc_ctl_domain_table(struct sched_domain *sd) sizeof(int), 0644, proc_dointvec_minmax); set_table_entry(&table[10], "flags", &sd->flags, sizeof(int), 0644, proc_dointvec_minmax); - /* &table[11] is terminator */ + set_table_entry(&table[11], "name", sd->name, + CORENAME_MAX_SIZE, 0444, proc_dostring); + /* &table[12] is terminator */ return table; } @@ -7263,13 +7265,21 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd) * Non-inlined to reduce accumulated stack pressure in build_sched_domains() */ +#ifdef CONFIG_SCHED_DEBUG +# define SD_INIT_NAME(sd, type) sd->name = #type +#else +# define SD_INIT_NAME(sd, type) do { } while (0) +#endif + #define SD_INIT(sd, type) sd_init_##type(sd) + #define SD_INIT_FUNC(type) \ static noinline void sd_init_##type(struct sched_domain *sd) \ { \ memset(sd, 0, sizeof(*sd)); \ *sd = SD_##type##_INIT; \ sd->level = SD_LV_##type; \ + SD_INIT_NAME(sd, type); \ } SD_INIT_FUNC(CPU) -- cgit v1.2.3 From bf0b90e357c883e8efd72954432efe652de74c76 Mon Sep 17 00:00:00 2001 From: "venkatesh.pallipadi@intel.com" Date: Mon, 4 Aug 2008 11:59:07 -0700 Subject: [CPUFREQ][1/6] cpufreq: Add cpu number parameter to __cpufreq_driver_getavg() Add a cpu parameter to __cpufreq_driver_getavg(). This is needed for software cpufreq coordination where policy->cpu may not be same as the CPU on which we want to getavg frequency. A follow-on patch will use this parameter to getavg freq from all cpus in policy->cpus. Change since last patch. Fix the offline/online and suspend/resume oops reported by Youquan Song Signed-off-by: Venkatesh Pallipadi Signed-off-by: Dave Jones --- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 5 +++-- drivers/cpufreq/cpufreq.c | 6 +++--- drivers/cpufreq/cpufreq_ondemand.c | 2 +- include/linux/cpufreq.h | 7 +++++-- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index 1def5b06fa4a..c24c4a487b7c 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -256,7 +256,8 @@ static u32 get_cur_val(const cpumask_t *mask) * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and * no meaning should be associated with absolute values of these MSRs. */ -static unsigned int get_measured_perf(unsigned int cpu) +static unsigned int get_measured_perf(struct cpufreq_policy *policy, + unsigned int cpu) { union { struct { @@ -326,7 +327,7 @@ static unsigned int get_measured_perf(unsigned int cpu) #endif - retval = per_cpu(drv_data, cpu)->max_freq * perf_percent / 100; + retval = per_cpu(drv_data, policy->cpu)->max_freq * perf_percent / 100; put_cpu(); set_cpus_allowed_ptr(current, &saved_mask); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 9bbdc258624c..31d6f535a79d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1487,7 +1487,7 @@ no_policy: } EXPORT_SYMBOL_GPL(cpufreq_driver_target); -int __cpufreq_driver_getavg(struct cpufreq_policy *policy) +int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu) { int ret = 0; @@ -1495,8 +1495,8 @@ int __cpufreq_driver_getavg(struct cpufreq_policy *policy) if (!policy) return -EINVAL; - if (cpu_online(policy->cpu) && cpufreq_driver->getavg) - ret = cpufreq_driver->getavg(policy->cpu); + if (cpu_online(cpu) && cpufreq_driver->getavg) + ret = cpufreq_driver->getavg(policy, cpu); cpufreq_cpu_put(policy); return ret; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 67c9d4f9edc0..f56debd9a8d7 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -415,7 +415,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) if (load < (dbs_tuners_ins.up_threshold - 10)) { unsigned int freq_next, freq_cur; - freq_cur = __cpufreq_driver_getavg(policy); + freq_cur = __cpufreq_driver_getavg(policy, policy->cpu); if (!freq_cur) freq_cur = policy->cur; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 6fd5668aa572..1ee608fd7b77 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -187,7 +187,8 @@ extern int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int relation); -extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy); +extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy, + unsigned int cpu); int cpufreq_register_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor); @@ -226,7 +227,9 @@ struct cpufreq_driver { unsigned int (*get) (unsigned int cpu); /* optional */ - unsigned int (*getavg) (unsigned int cpu); + unsigned int (*getavg) (struct cpufreq_policy *policy, + unsigned int cpu); + int (*exit) (struct cpufreq_policy *policy); int (*suspend) (struct cpufreq_policy *policy, pm_message_t pmsg); int (*resume) (struct cpufreq_policy *policy); -- cgit v1.2.3 From 8083e4ad970e4eb567e31037060cdd4ba346f0c0 Mon Sep 17 00:00:00 2001 From: "venkatesh.pallipadi@intel.com" Date: Mon, 4 Aug 2008 11:59:11 -0700 Subject: [CPUFREQ][5/6] cpufreq: Changes to get_cpu_idle_time_us(), used by ondemand governor export get_cpu_idle_time_us() for it to be used in ondemand governor. Last update time can be current time when the CPU is currently non-idle, accounting for the busy time since last idle. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Dave Jones --- include/linux/tick.h | 2 +- kernel/time/tick-sched.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tick.h b/include/linux/tick.h index 8cf8cfe2cc97..98921a3e1aa8 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -126,7 +126,7 @@ static inline ktime_t tick_nohz_get_sleep_length(void) return len; } static inline void tick_nohz_stop_idle(int cpu) { } -static inline u64 get_cpu_idle_time_us(int cpu, u64 *unused) { return 0; } +static inline u64 get_cpu_idle_time_us(int cpu, u64 *unused) { return -1; } # endif /* !NO_HZ */ #endif diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index cb02324bdb88..a4d219398167 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -190,9 +191,17 @@ u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time) { struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); - *last_update_time = ktime_to_us(ts->idle_lastupdate); + if (!tick_nohz_enabled) + return -1; + + if (ts->idle_active) + *last_update_time = ktime_to_us(ts->idle_lastupdate); + else + *last_update_time = ktime_to_us(ktime_get()); + return ktime_to_us(ts->idle_sleeptime); } +EXPORT_SYMBOL_GPL(get_cpu_idle_time_us); /** * tick_nohz_stop_sched_tick - stop the idle tick from the idle task -- cgit v1.2.3 From c19e654ddbe3831252f61e76a74d661e1a755530 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 9 Oct 2008 11:59:55 -0700 Subject: gre: Add netlink interface This patch adds a netlink interface that will eventually displace the existing ioctl interface. It utilises the elegant rtnl_link_ops mechanism. This also means that user-space no longer needs to rely on the tunnel interface being of type GRE to identify GRE tunnels. The identification can now occur using rtnl_link_ops. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/if_tunnel.h | 19 ++++ net/ipv4/ip_gre.c | 247 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 262 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h index d4efe4014705..aeab2cb32a9c 100644 --- a/include/linux/if_tunnel.h +++ b/include/linux/if_tunnel.h @@ -2,6 +2,7 @@ #define _IF_TUNNEL_H_ #include +#include #define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0) #define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1) @@ -47,4 +48,22 @@ struct ip_tunnel_prl { /* PRL flags */ #define PRL_DEFAULT 0x0001 +enum +{ + IFLA_GRE_UNSPEC, + IFLA_GRE_LINK, + IFLA_GRE_IFLAGS, + IFLA_GRE_OFLAGS, + IFLA_GRE_IKEY, + IFLA_GRE_OKEY, + IFLA_GRE_LOCAL, + IFLA_GRE_REMOTE, + IFLA_GRE_TTL, + IFLA_GRE_TOS, + IFLA_GRE_PMTUDISC, + __IFLA_GRE_MAX, +}; + +#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) + #endif /* _IF_TUNNEL_H_ */ diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 80622dd6fb3f..25d2c77a7f38 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef CONFIG_IPV6 #include @@ -117,6 +118,7 @@ Alexey Kuznetsov. */ +static struct rtnl_link_ops ipgre_link_ops __read_mostly; static int ipgre_tunnel_init(struct net_device *dev); static void ipgre_tunnel_setup(struct net_device *dev); static int ipgre_tunnel_bind_dev(struct net_device *dev); @@ -286,9 +288,9 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct net *net, goto failed_free; } - dev->init = ipgre_tunnel_init; nt = netdev_priv(dev); nt->parms = *parms; + dev->rtnl_link_ops = &ipgre_link_ops; dev->mtu = ipgre_tunnel_bind_dev(dev); @@ -1087,6 +1089,7 @@ static int ipgre_close(struct net_device *dev) static void ipgre_tunnel_setup(struct net_device *dev) { + dev->init = ipgre_tunnel_init; dev->uninit = ipgre_tunnel_uninit; dev->destructor = free_netdev; dev->hard_start_xmit = ipgre_tunnel_xmit; @@ -1196,6 +1199,7 @@ static int ipgre_init_net(struct net *net) ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init; dev_net_set(ign->fb_tunnel_dev, net); + ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops; if ((err = register_netdev(ign->fb_tunnel_dev))) goto err_reg_dev; @@ -1228,6 +1232,229 @@ static struct pernet_operations ipgre_net_ops = { .exit = ipgre_exit_net, }; +static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + __be16 flags; + + if (!data) + return 0; + + flags = 0; + if (data[IFLA_GRE_IFLAGS]) + flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); + if (data[IFLA_GRE_OFLAGS]) + flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); + if (flags & (GRE_VERSION|GRE_ROUTING)) + return -EINVAL; + + return 0; +} + +static void ipgre_netlink_parms(struct nlattr *data[], + struct ip_tunnel_parm *parms) +{ + memset(parms, 0, sizeof(parms)); + + parms->iph.protocol = IPPROTO_GRE; + + if (!data) + return; + + if (data[IFLA_GRE_LINK]) + parms->link = nla_get_u32(data[IFLA_GRE_LINK]); + + if (data[IFLA_GRE_IFLAGS]) + parms->i_flags = nla_get_be16(data[IFLA_GRE_IFLAGS]); + + if (data[IFLA_GRE_OFLAGS]) + parms->o_flags = nla_get_be16(data[IFLA_GRE_OFLAGS]); + + if (data[IFLA_GRE_IKEY]) + parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]); + + if (data[IFLA_GRE_OKEY]) + parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]); + + if (data[IFLA_GRE_LOCAL]) + memcpy(&parms->iph.saddr, nla_data(data[IFLA_GRE_LOCAL]), 4); + + if (data[IFLA_GRE_REMOTE]) + memcpy(&parms->iph.daddr, nla_data(data[IFLA_GRE_REMOTE]), 4); + + if (data[IFLA_GRE_TTL]) + parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]); + + if (data[IFLA_GRE_TOS]) + parms->iph.tos = nla_get_u8(data[IFLA_GRE_TOS]); + + if (!data[IFLA_GRE_PMTUDISC] || nla_get_u8(data[IFLA_GRE_PMTUDISC])) + parms->iph.frag_off = htons(IP_DF); +} + +static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip_tunnel *nt; + struct net *net = dev_net(dev); + struct ipgre_net *ign = net_generic(net, ipgre_net_id); + int mtu; + int err; + + nt = netdev_priv(dev); + ipgre_netlink_parms(data, &nt->parms); + + if (ipgre_tunnel_locate(net, &nt->parms, 0)) + return -EEXIST; + + mtu = ipgre_tunnel_bind_dev(dev); + if (!tb[IFLA_MTU]) + dev->mtu = mtu; + + err = register_netdevice(dev); + if (err) + goto out; + + dev_hold(dev); + ipgre_tunnel_link(ign, nt); + +out: + return err; +} + +static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip_tunnel *t, *nt; + struct net *net = dev_net(dev); + struct ipgre_net *ign = net_generic(net, ipgre_net_id); + struct ip_tunnel_parm p; + int mtu; + + if (dev == ign->fb_tunnel_dev) + return -EINVAL; + + nt = netdev_priv(dev); + ipgre_netlink_parms(data, &p); + + t = ipgre_tunnel_locate(net, &p, 0); + + if (t) { + if (t->dev != dev) + return -EEXIST; + } else { + unsigned nflags = 0; + + t = nt; + + if (ipv4_is_multicast(p.iph.daddr)) + nflags = IFF_BROADCAST; + else if (p.iph.daddr) + nflags = IFF_POINTOPOINT; + + if ((dev->flags ^ nflags) & + (IFF_POINTOPOINT | IFF_BROADCAST)) + return -EINVAL; + + ipgre_tunnel_unlink(ign, t); + t->parms.iph.saddr = p.iph.saddr; + t->parms.iph.daddr = p.iph.daddr; + t->parms.i_key = p.i_key; + memcpy(dev->dev_addr, &p.iph.saddr, 4); + memcpy(dev->broadcast, &p.iph.daddr, 4); + ipgre_tunnel_link(ign, t); + netdev_state_change(dev); + } + + t->parms.o_key = p.o_key; + t->parms.iph.ttl = p.iph.ttl; + t->parms.iph.tos = p.iph.tos; + t->parms.iph.frag_off = p.iph.frag_off; + + if (t->parms.link != p.link) { + t->parms.link = p.link; + mtu = ipgre_tunnel_bind_dev(dev); + if (!tb[IFLA_MTU]) + dev->mtu = mtu; + netdev_state_change(dev); + } + + return 0; +} + +static size_t ipgre_get_size(const struct net_device *dev) +{ + return + /* IFLA_GRE_LINK */ + nla_total_size(4) + + /* IFLA_GRE_IFLAGS */ + nla_total_size(2) + + /* IFLA_GRE_OFLAGS */ + nla_total_size(2) + + /* IFLA_GRE_IKEY */ + nla_total_size(4) + + /* IFLA_GRE_OKEY */ + nla_total_size(4) + + /* IFLA_GRE_LOCAL */ + nla_total_size(4) + + /* IFLA_GRE_REMOTE */ + nla_total_size(4) + + /* IFLA_GRE_TTL */ + nla_total_size(1) + + /* IFLA_GRE_TOS */ + nla_total_size(1) + + /* IFLA_GRE_PMTUDISC */ + nla_total_size(1) + + 0; +} + +static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct ip_tunnel *t = netdev_priv(dev); + struct ip_tunnel_parm *p = &t->parms; + + NLA_PUT_U32(skb, IFLA_GRE_LINK, p->link); + NLA_PUT_BE16(skb, IFLA_GRE_IFLAGS, p->i_flags); + NLA_PUT_BE16(skb, IFLA_GRE_OFLAGS, p->o_flags); + NLA_PUT_BE32(skb, IFLA_GRE_IFLAGS, p->i_flags); + NLA_PUT_BE32(skb, IFLA_GRE_OFLAGS, p->o_flags); + NLA_PUT(skb, IFLA_GRE_LOCAL, 4, &p->iph.saddr); + NLA_PUT(skb, IFLA_GRE_REMOTE, 4, &p->iph.daddr); + NLA_PUT_U8(skb, IFLA_GRE_TTL, p->iph.ttl); + NLA_PUT_U8(skb, IFLA_GRE_TOS, p->iph.tos); + NLA_PUT_U8(skb, IFLA_GRE_PMTUDISC, !!(p->iph.frag_off & htons(IP_DF))); + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .len = 4 }, + [IFLA_GRE_REMOTE] = { .len = 4 }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_TOS] = { .type = NLA_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, +}; + +static struct rtnl_link_ops ipgre_link_ops __read_mostly = { + .kind = "gre", + .maxtype = IFLA_GRE_MAX, + .policy = ipgre_policy, + .priv_size = sizeof(struct ip_tunnel), + .setup = ipgre_tunnel_setup, + .validate = ipgre_tunnel_validate, + .newlink = ipgre_newlink, + .changelink = ipgre_changelink, + .get_size = ipgre_get_size, + .fill_info = ipgre_fill_info, +}; + /* * And now the modules code and kernel interface. */ @@ -1245,19 +1472,31 @@ static int __init ipgre_init(void) err = register_pernet_gen_device(&ipgre_net_id, &ipgre_net_ops); if (err < 0) - inet_del_protocol(&ipgre_protocol, IPPROTO_GRE); + goto gen_device_failed; + err = rtnl_link_register(&ipgre_link_ops); + if (err < 0) + goto rtnl_link_failed; + +out: return err; + +rtnl_link_failed: + unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); +gen_device_failed: + inet_del_protocol(&ipgre_protocol, IPPROTO_GRE); + goto out; } static void __exit ipgre_fini(void) { + rtnl_link_unregister(&ipgre_link_ops); + unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) printk(KERN_INFO "ipgre close: can't remove protocol\n"); - - unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); } module_init(ipgre_init); module_exit(ipgre_fini); MODULE_LICENSE("GPL"); +MODULE_ALIAS("rtnl-link-gre"); -- cgit v1.2.3 From e1a8000228e16212c93b23cfbed4d622e2ec7a6b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 9 Oct 2008 12:00:17 -0700 Subject: gre: Add Transparent Ethernet Bridging This patch adds support for Ethernet over GRE encapsulation. This is exposed to user-space with a new link type of "gretap" instead of "gre". It will create an ARPHRD_ETHER device in lieu of the usual ARPHRD_IPGRE. Note that to preserver backwards compatibility all Transparent Ethernet Bridging packets are passed to an ARPHRD_IPGRE tunnel if its key matches and there is no ARPHRD_ETHER device whose key matches more closely. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/if_ether.h | 1 + net/ipv4/ip_gre.c | 206 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 175 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index a0099e98b5c4..bf1a53b2682e 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -56,6 +56,7 @@ #define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ #define ETH_P_CUST 0x6006 /* DEC Customer use */ #define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ #define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ #define ETH_P_ATALK 0x809B /* Appletalk DDP */ #define ETH_P_AARP 0x80F3 /* Appletalk AARP */ diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 25d2c77a7f38..44ed9487fa15 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -166,38 +167,64 @@ static DEFINE_RWLOCK(ipgre_lock); /* Given src, dst and key, find appropriate for input tunnel. */ static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net, - __be32 remote, __be32 local, __be32 key) + __be32 remote, __be32 local, + __be32 key, __be16 gre_proto) { unsigned h0 = HASH(remote); unsigned h1 = HASH(key); struct ip_tunnel *t; + struct ip_tunnel *t2 = NULL; struct ipgre_net *ign = net_generic(net, ipgre_net_id); + int dev_type = (gre_proto == htons(ETH_P_TEB)) ? + ARPHRD_ETHER : ARPHRD_IPGRE; for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) { - if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) - return t; + if (t->parms.i_key == key && t->dev->flags & IFF_UP) { + if (t->dev->type == dev_type) + return t; + if (t->dev->type == ARPHRD_IPGRE && !t2) + t2 = t; + } } } + for (t = ign->tunnels_r[h0^h1]; t; t = t->next) { if (remote == t->parms.iph.daddr) { - if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) - return t; + if (t->parms.i_key == key && t->dev->flags & IFF_UP) { + if (t->dev->type == dev_type) + return t; + if (t->dev->type == ARPHRD_IPGRE && !t2) + t2 = t; + } } } + for (t = ign->tunnels_l[h1]; t; t = t->next) { if (local == t->parms.iph.saddr || (local == t->parms.iph.daddr && ipv4_is_multicast(local))) { - if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) - return t; + if (t->parms.i_key == key && t->dev->flags & IFF_UP) { + if (t->dev->type == dev_type) + return t; + if (t->dev->type == ARPHRD_IPGRE && !t2) + t2 = t; + } } } + for (t = ign->tunnels_wc[h1]; t; t = t->next) { - if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) - return t; + if (t->parms.i_key == key && t->dev->flags & IFF_UP) { + if (t->dev->type == dev_type) + return t; + if (t->dev->type == ARPHRD_IPGRE && !t2) + t2 = t; + } } + if (t2) + return t2; + if (ign->fb_tunnel_dev->flags&IFF_UP) return netdev_priv(ign->fb_tunnel_dev); return NULL; @@ -252,25 +279,37 @@ static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t) } } -static struct ip_tunnel * ipgre_tunnel_locate(struct net *net, - struct ip_tunnel_parm *parms, int create) +static struct ip_tunnel *ipgre_tunnel_find(struct net *net, + struct ip_tunnel_parm *parms, + int type) { __be32 remote = parms->iph.daddr; __be32 local = parms->iph.saddr; __be32 key = parms->i_key; - struct ip_tunnel *t, **tp, *nt; + struct ip_tunnel *t, **tp; + struct ipgre_net *ign = net_generic(net, ipgre_net_id); + + for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next) + if (local == t->parms.iph.saddr && + remote == t->parms.iph.daddr && + key == t->parms.i_key && + type == t->dev->type) + break; + + return t; +} + +static struct ip_tunnel * ipgre_tunnel_locate(struct net *net, + struct ip_tunnel_parm *parms, int create) +{ + struct ip_tunnel *t, *nt; struct net_device *dev; char name[IFNAMSIZ]; struct ipgre_net *ign = net_generic(net, ipgre_net_id); - for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next) { - if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) { - if (key == t->parms.i_key) - return t; - } - } - if (!create) - return NULL; + t = ipgre_tunnel_find(net, parms, ARPHRD_IPGRE); + if (t || !create) + return t; if (parms->name[0]) strlcpy(name, parms->name, IFNAMSIZ); @@ -385,8 +424,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info) read_lock(&ipgre_lock); t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr, - (flags&GRE_KEY) ? - *(((__be32*)p) + (grehlen>>2) - 1) : 0); + flags & GRE_KEY ? + *(((__be32 *)p) + (grehlen / 4) - 1) : 0, + p[1]); if (t == NULL || t->parms.iph.daddr == 0 || ipv4_is_multicast(t->parms.iph.daddr)) goto out; @@ -436,6 +476,7 @@ static int ipgre_rcv(struct sk_buff *skb) u32 seqno = 0; struct ip_tunnel *tunnel; int offset = 4; + __be16 gre_proto; if (!pskb_may_pull(skb, 16)) goto drop_nolock; @@ -475,20 +516,22 @@ static int ipgre_rcv(struct sk_buff *skb) } } + gre_proto = *(__be16 *)(h + 2); + read_lock(&ipgre_lock); if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev), - iph->saddr, iph->daddr, key)) != NULL) { + iph->saddr, iph->daddr, key, + gre_proto))) { struct net_device_stats *stats = &tunnel->dev->stats; secpath_reset(skb); - skb->protocol = *(__be16*)(h + 2); + skb->protocol = gre_proto; /* WCCP version 1 and 2 protocol decoding. * - Change protocol to IP * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header */ - if (flags == 0 && - skb->protocol == htons(ETH_P_WCCP)) { + if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) { skb->protocol = htons(ETH_P_IP); if ((*(h + offset) & 0xF0) != 0x40) offset += 4; @@ -496,7 +539,6 @@ static int ipgre_rcv(struct sk_buff *skb) skb->mac_header = skb->network_header; __pskb_pull(skb, offset); - skb_reset_network_header(skb); skb_postpull_rcsum(skb, skb_transport_header(skb), offset); skb->pkt_type = PACKET_HOST; #ifdef CONFIG_NET_IPGRE_BROADCAST @@ -524,13 +566,30 @@ static int ipgre_rcv(struct sk_buff *skb) } tunnel->i_seqno = seqno + 1; } + + /* Warning: All skb pointers will be invalidated! */ + if (tunnel->dev->type == ARPHRD_ETHER) { + if (!pskb_may_pull(skb, ETH_HLEN)) { + stats->rx_length_errors++; + stats->rx_errors++; + goto drop; + } + + iph = ip_hdr(skb); + skb->protocol = eth_type_trans(skb, tunnel->dev); + skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); + } + stats->rx_packets++; stats->rx_bytes += skb->len; skb->dev = tunnel->dev; dst_release(skb->dst); skb->dst = NULL; nf_reset(skb); + + skb_reset_network_header(skb); ipgre_ecn_decapsulate(iph, skb); + netif_rx(skb); read_unlock(&ipgre_lock); return(0); @@ -565,7 +624,10 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_error; } - if (dev->header_ops) { + if (dev->type == ARPHRD_ETHER) + IPCB(skb)->flags = 0; + + if (dev->header_ops && dev->type == ARPHRD_IPGRE) { gre_hlen = 0; tiph = (struct iphdr*)skb->data; } else { @@ -741,8 +803,9 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT); } - ((__be16*)(iph+1))[0] = tunnel->parms.o_flags; - ((__be16*)(iph+1))[1] = skb->protocol; + ((__be16 *)(iph + 1))[0] = tunnel->parms.o_flags; + ((__be16 *)(iph + 1))[1] = (dev->type == ARPHRD_ETHER) ? + htons(ETH_P_TEB) : skb->protocol; if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) { __be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4); @@ -804,7 +867,9 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) tdev = rt->u.dst.dev; ip_rt_put(rt); } - dev->flags |= IFF_POINTOPOINT; + + if (dev->type != ARPHRD_ETHER) + dev->flags |= IFF_POINTOPOINT; } if (!tdev && tunnel->parms.link) @@ -1250,6 +1315,30 @@ static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } +static int ipgre_tap_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + __be32 daddr; + + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + + if (!data) + goto out; + + if (data[IFLA_GRE_REMOTE]) { + memcpy(&daddr, nla_data(data[IFLA_GRE_REMOTE]), 4); + if (!daddr) + return -EINVAL; + } + +out: + return ipgre_tunnel_validate(tb, data); +} + static void ipgre_netlink_parms(struct nlattr *data[], struct ip_tunnel_parm *parms) { @@ -1291,6 +1380,35 @@ static void ipgre_netlink_parms(struct nlattr *data[], parms->iph.frag_off = htons(IP_DF); } +static int ipgre_tap_init(struct net_device *dev) +{ + struct ip_tunnel *tunnel; + + tunnel = netdev_priv(dev); + + tunnel->dev = dev; + strcpy(tunnel->parms.name, dev->name); + + ipgre_tunnel_bind_dev(dev); + + return 0; +} + +static void ipgre_tap_setup(struct net_device *dev) +{ + + ether_setup(dev); + + dev->init = ipgre_tap_init; + dev->uninit = ipgre_tunnel_uninit; + dev->destructor = free_netdev; + dev->hard_start_xmit = ipgre_tunnel_xmit; + dev->change_mtu = ipgre_tunnel_change_mtu; + + dev->iflink = 0; + dev->features |= NETIF_F_NETNS_LOCAL; +} + static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -1303,9 +1421,12 @@ static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], nt = netdev_priv(dev); ipgre_netlink_parms(data, &nt->parms); - if (ipgre_tunnel_locate(net, &nt->parms, 0)) + if (ipgre_tunnel_find(net, &nt->parms, dev->type)) return -EEXIST; + if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) + random_ether_addr(dev->dev_addr); + mtu = ipgre_tunnel_bind_dev(dev); if (!tb[IFLA_MTU]) dev->mtu = mtu; @@ -1455,6 +1576,19 @@ static struct rtnl_link_ops ipgre_link_ops __read_mostly = { .fill_info = ipgre_fill_info, }; +static struct rtnl_link_ops ipgre_tap_ops __read_mostly = { + .kind = "gretap", + .maxtype = IFLA_GRE_MAX, + .policy = ipgre_policy, + .priv_size = sizeof(struct ip_tunnel), + .setup = ipgre_tap_setup, + .validate = ipgre_tap_validate, + .newlink = ipgre_newlink, + .changelink = ipgre_changelink, + .get_size = ipgre_get_size, + .fill_info = ipgre_fill_info, +}; + /* * And now the modules code and kernel interface. */ @@ -1478,9 +1612,15 @@ static int __init ipgre_init(void) if (err < 0) goto rtnl_link_failed; + err = rtnl_link_register(&ipgre_tap_ops); + if (err < 0) + goto tap_ops_failed; + out: return err; +tap_ops_failed: + rtnl_link_unregister(&ipgre_link_ops); rtnl_link_failed: unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); gen_device_failed: @@ -1490,6 +1630,7 @@ gen_device_failed: static void __exit ipgre_fini(void) { + rtnl_link_unregister(&ipgre_tap_ops); rtnl_link_unregister(&ipgre_link_ops); unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) @@ -1500,3 +1641,4 @@ module_init(ipgre_init); module_exit(ipgre_fini); MODULE_LICENSE("GPL"); MODULE_ALIAS("rtnl-link-gre"); +MODULE_ALIAS("rtnl-link-gretap"); -- cgit v1.2.3 From 82b1519b345d61dcfae526e3fcb08128f39f9bcc Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Oct 2008 13:37:09 +0100 Subject: dm: export struct dm_dev Split struct dm_dev in two and publish the part that other targets need in include/linux/device-mapper.h. Signed-off-by: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- drivers/md/dm-ioctl.c | 4 +-- drivers/md/dm-table.c | 71 ++++++++++++++++++++++++------------------- drivers/md/dm.h | 7 ++--- include/linux/device-mapper.h | 7 ++++- 4 files changed, 49 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index b262c0042de3..90e19f13f269 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1131,7 +1131,7 @@ static void retrieve_deps(struct dm_table *table, unsigned int count = 0; struct list_head *tmp; size_t len, needed; - struct dm_dev *dd; + struct dm_dev_internal *dd; struct dm_target_deps *deps; deps = get_result_buffer(param, param_size, &len); @@ -1157,7 +1157,7 @@ static void retrieve_deps(struct dm_table *table, deps->count = count; count = 0; list_for_each_entry (dd, dm_table_get_devices(table), list) - deps->dev[count++] = huge_encode_dev(dd->bdev->bd_dev); + deps->dev[count++] = huge_encode_dev(dd->dm_dev.bdev->bd_dev); param->data_size = param->data_start + needed; } diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 61f441409234..5dd32b8a57ac 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -250,7 +250,8 @@ static void free_devices(struct list_head *devices) struct list_head *tmp, *next; list_for_each_safe(tmp, next, devices) { - struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + struct dm_dev_internal *dd = + list_entry(tmp, struct dm_dev_internal, list); kfree(dd); } } @@ -327,12 +328,12 @@ static int lookup_device(const char *path, dev_t *dev) /* * See if we've already got a device in the list. */ -static struct dm_dev *find_device(struct list_head *l, dev_t dev) +static struct dm_dev_internal *find_device(struct list_head *l, dev_t dev) { - struct dm_dev *dd; + struct dm_dev_internal *dd; list_for_each_entry (dd, l, list) - if (dd->bdev->bd_dev == dev) + if (dd->dm_dev.bdev->bd_dev == dev) return dd; return NULL; @@ -341,45 +342,47 @@ static struct dm_dev *find_device(struct list_head *l, dev_t dev) /* * Open a device so we can use it as a map destination. */ -static int open_dev(struct dm_dev *d, dev_t dev, struct mapped_device *md) +static int open_dev(struct dm_dev_internal *d, dev_t dev, + struct mapped_device *md) { static char *_claim_ptr = "I belong to device-mapper"; struct block_device *bdev; int r; - BUG_ON(d->bdev); + BUG_ON(d->dm_dev.bdev); - bdev = open_by_devnum(dev, d->mode); + bdev = open_by_devnum(dev, d->dm_dev.mode); if (IS_ERR(bdev)) return PTR_ERR(bdev); r = bd_claim_by_disk(bdev, _claim_ptr, dm_disk(md)); if (r) blkdev_put(bdev); else - d->bdev = bdev; + d->dm_dev.bdev = bdev; return r; } /* * Close a device that we've been using. */ -static void close_dev(struct dm_dev *d, struct mapped_device *md) +static void close_dev(struct dm_dev_internal *d, struct mapped_device *md) { - if (!d->bdev) + if (!d->dm_dev.bdev) return; - bd_release_from_disk(d->bdev, dm_disk(md)); - blkdev_put(d->bdev); - d->bdev = NULL; + bd_release_from_disk(d->dm_dev.bdev, dm_disk(md)); + blkdev_put(d->dm_dev.bdev); + d->dm_dev.bdev = NULL; } /* * If possible, this checks an area of a destination device is valid. */ -static int check_device_area(struct dm_dev *dd, sector_t start, sector_t len) +static int check_device_area(struct dm_dev_internal *dd, sector_t start, + sector_t len) { - sector_t dev_size = dd->bdev->bd_inode->i_size >> SECTOR_SHIFT; + sector_t dev_size = dd->dm_dev.bdev->bd_inode->i_size >> SECTOR_SHIFT; if (!dev_size) return 1; @@ -392,16 +395,17 @@ static int check_device_area(struct dm_dev *dd, sector_t start, sector_t len) * careful to leave things as they were if we fail to reopen the * device. */ -static int upgrade_mode(struct dm_dev *dd, int new_mode, struct mapped_device *md) +static int upgrade_mode(struct dm_dev_internal *dd, int new_mode, + struct mapped_device *md) { int r; - struct dm_dev dd_copy; - dev_t dev = dd->bdev->bd_dev; + struct dm_dev_internal dd_copy; + dev_t dev = dd->dm_dev.bdev->bd_dev; dd_copy = *dd; - dd->mode |= new_mode; - dd->bdev = NULL; + dd->dm_dev.mode |= new_mode; + dd->dm_dev.bdev = NULL; r = open_dev(dd, dev, md); if (!r) close_dev(&dd_copy, md); @@ -421,7 +425,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, { int r; dev_t uninitialized_var(dev); - struct dm_dev *dd; + struct dm_dev_internal *dd; unsigned int major, minor; BUG_ON(!t); @@ -443,20 +447,20 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, if (!dd) return -ENOMEM; - dd->mode = mode; - dd->bdev = NULL; + dd->dm_dev.mode = mode; + dd->dm_dev.bdev = NULL; if ((r = open_dev(dd, dev, t->md))) { kfree(dd); return r; } - format_dev_t(dd->name, dev); + format_dev_t(dd->dm_dev.name, dev); atomic_set(&dd->count, 0); list_add(&dd->list, &t->devices); - } else if (dd->mode != (mode | dd->mode)) { + } else if (dd->dm_dev.mode != (mode | dd->dm_dev.mode)) { r = upgrade_mode(dd, mode, t->md); if (r) return r; @@ -465,11 +469,11 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, if (!check_device_area(dd, start, len)) { DMWARN("device %s too small for target", path); - dm_put_device(ti, dd); + dm_put_device(ti, &dd->dm_dev); return -EINVAL; } - *result = dd; + *result = &dd->dm_dev; return 0; } @@ -540,8 +544,11 @@ int dm_get_device(struct dm_target *ti, const char *path, sector_t start, /* * Decrement a devices use count and remove it if necessary. */ -void dm_put_device(struct dm_target *ti, struct dm_dev *dd) +void dm_put_device(struct dm_target *ti, struct dm_dev *d) { + struct dm_dev_internal *dd = container_of(d, struct dm_dev_internal, + dm_dev); + if (atomic_dec_and_test(&dd->count)) { close_dev(dd, ti->table->md); list_del(&dd->list); @@ -937,12 +944,12 @@ int dm_table_resume_targets(struct dm_table *t) int dm_table_any_congested(struct dm_table *t, int bdi_bits) { - struct dm_dev *dd; + struct dm_dev_internal *dd; struct list_head *devices = dm_table_get_devices(t); int r = 0; list_for_each_entry(dd, devices, list) { - struct request_queue *q = bdev_get_queue(dd->bdev); + struct request_queue *q = bdev_get_queue(dd->dm_dev.bdev); r |= bdi_congested(&q->backing_dev_info, bdi_bits); } @@ -951,11 +958,11 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits) void dm_table_unplug_all(struct dm_table *t) { - struct dm_dev *dd; + struct dm_dev_internal *dd; struct list_head *devices = dm_table_get_devices(t); list_for_each_entry(dd, devices, list) { - struct request_queue *q = bdev_get_queue(dd->bdev); + struct request_queue *q = bdev_get_queue(dd->dm_dev.bdev); blk_unplug(q); } diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 1e59a0b0a78a..65f2f562220e 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -25,13 +25,10 @@ /* * List of devices that a metadevice uses and should open/close. */ -struct dm_dev { +struct dm_dev_internal { struct list_head list; - atomic_t count; - int mode; - struct block_device *bdev; - char name[16]; + struct dm_dev dm_dev; }; struct dm_table; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index a90222e3297d..178390de195e 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -13,7 +13,6 @@ struct dm_target; struct dm_table; -struct dm_dev; struct mapped_device; struct bio_vec; @@ -84,6 +83,12 @@ void dm_error(const char *message); */ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev); +struct dm_dev { + struct block_device *bdev; + int mode; + char name[16]; +}; + /* * Constructors should call these functions to ensure destination devices * are opened/closed correctly. -- cgit v1.2.3 From 89343da077ad564ed130c46e5ea6a79388410fa5 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Oct 2008 13:37:10 +0100 Subject: dm: publish dm_get_mapinfo Publish dm_get_mapinfo in include/linux/device-mapper.h because this function is used by targets. Signed-off-by: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- drivers/md/dm.h | 1 - include/linux/device-mapper.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 65f2f562220e..911d434361dd 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -91,7 +91,6 @@ int dm_stripe_init(void); void dm_stripe_exit(void); void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); -union map_info *dm_get_mapinfo(struct bio *bio); int dm_open_count(struct mapped_device *md); int dm_lock_for_deletion(struct mapped_device *md); diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 178390de195e..6b80961650f0 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -207,6 +207,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid); struct gendisk *dm_disk(struct mapped_device *md); int dm_suspended(struct mapped_device *md); int dm_noflush_suspending(struct dm_target *ti); +union map_info *dm_get_mapinfo(struct bio *bio); /* * Geometry functions. -- cgit v1.2.3 From ea0ec640940c2ae3a8d71af3249fccf06a9997a3 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Oct 2008 13:37:11 +0100 Subject: dm: publish dm_table_unplug_all Publish dm_table_unplug_all in include/linux/device-mapper.h because this function is used by targets. Signed-off-by: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- drivers/md/dm.h | 1 - include/linux/device-mapper.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 911d434361dd..8812208978e3 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -46,7 +46,6 @@ void dm_table_presuspend_targets(struct dm_table *t); void dm_table_postsuspend_targets(struct dm_table *t); int dm_table_resume_targets(struct dm_table *t); int dm_table_any_congested(struct dm_table *t, int bdi_bits); -void dm_table_unplug_all(struct dm_table *t); /* * To check the return value from dm_table_find_target(). diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 6b80961650f0..e462c7f3b391 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -237,6 +237,11 @@ int dm_table_add_target(struct dm_table *t, const char *type, */ int dm_table_complete(struct dm_table *t); +/* + * Unplug all devices in a table. + */ +void dm_table_unplug_all(struct dm_table *t); + /* * Table reference counting. */ -- cgit v1.2.3 From 54160904260fa764ba6e2dc738770be30fdf9553 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Oct 2008 13:37:12 +0100 Subject: dm: publish dm_vcalloc Publish dm_vcalloc in include/linux/device-mapper.h because this function is used by targets. Signed-off-by: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- drivers/md/dm.h | 1 - include/linux/device-mapper.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 8812208978e3..cd189da2b2fa 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -89,7 +89,6 @@ void dm_linear_exit(void); int dm_stripe_init(void); void dm_stripe_exit(void); -void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); int dm_open_count(struct mapped_device *md); int dm_lock_for_deletion(struct mapped_device *md); diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index e462c7f3b391..08d783592b73 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -267,6 +267,11 @@ void dm_table_event(struct dm_table *t); */ int dm_swap_table(struct mapped_device *md, struct dm_table *t); +/* + * A wrapper around vmalloc. + */ +void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); + /*----------------------------------------------------------------- * Macros. *---------------------------------------------------------------*/ -- cgit v1.2.3 From 4dde4492d850a4c9bcaa92e5bd7f4eebe3e2f5ab Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:19 +0200 Subject: ide: make drive->id an union (take 2) Make drive->id an unnamed union so id can be accessed either by using 'u16 *id' or 'struct hd_driveid *driveid'. Then convert all existing drive->id users accordingly (using 'u16 *id' when possible). This is an intermediate step to make ide 'struct hd_driveid'-free. While at it: - Add missing KERN_CONTs in it821x.c. - Use ATA_ID_WORDS and ATA_ID_*_LEN defines. - Remove unnecessary checks for drive->id. - s/drive_table/table/ in ide_in_drive_list(). - Cleanup ide_config_drive_speed() a bit. - s/drive1/dev1/ & s/drive0/dev0/ in ide_undecoded_slave(). v2: Fix typo in drivers/ide/ppc/pmac.c. (From Stephen Rothwell) There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 4 +- drivers/ide/arm/palm_bk3710.c | 3 +- drivers/ide/ide-acpi.c | 6 +-- drivers/ide/ide-cd.c | 15 +++--- drivers/ide/ide-disk.c | 63 +++++++++++----------- drivers/ide/ide-dma.c | 50 ++++++++++-------- drivers/ide/ide-floppy.c | 14 ++--- drivers/ide/ide-iops.c | 80 +++++++++++++--------------- drivers/ide/ide-lib.c | 24 +++++---- drivers/ide/ide-probe.c | 117 ++++++++++++++++++++++------------------- drivers/ide/ide-proc.c | 5 +- drivers/ide/ide-tape.c | 7 +-- drivers/ide/ide-taskfile.c | 6 ++- drivers/ide/ide-timings.c | 21 ++++---- drivers/ide/ide.c | 8 +-- drivers/ide/legacy/qd65xx.c | 22 ++++---- drivers/ide/pci/alim15x3.c | 4 +- drivers/ide/pci/cs5530.c | 10 ++-- drivers/ide/pci/hpt366.c | 8 +-- drivers/ide/pci/it821x.c | 51 +++++++++--------- drivers/ide/pci/pdc202xx_new.c | 4 +- drivers/ide/pci/pdc202xx_old.c | 6 +-- drivers/ide/pci/sc1200.c | 10 ++-- drivers/ide/pci/serverworks.c | 4 +- drivers/ide/pci/siimage.c | 8 +-- drivers/ide/ppc/pmac.c | 6 +-- drivers/scsi/ide-scsi.c | 13 ++--- include/linux/ide.h | 23 ++++---- 28 files changed, 312 insertions(+), 280 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index df4af4083954..ca9e8ea32ee2 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -265,8 +265,8 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode) * If we're going to be doing MW_DMA_1 or MW_DMA_2, we should * take care to note the values in the ID... */ - if (use_dma_info && drive->id->eide_dma_time > cycle_time) - cycle_time = drive->id->eide_dma_time; + if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time) + cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME]; drive->drive_data = cycle_time; diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 4fd91dcf1dc2..6cea984776fd 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -213,7 +213,8 @@ static void palm_bk3710_set_dma_mode(ide_drive_t *drive, u8 xferspeed) palm_bk3710_setudmamode(base, is_slave, xferspeed - XFER_UDMA_0); } else { - palm_bk3710_setdmamode(base, is_slave, drive->id->eide_dma_min, + palm_bk3710_setdmamode(base, is_slave, + drive->id[ATA_ID_EIDE_DMA_MIN], xferspeed); } } diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index 6f704628c27d..2427c380b3dc 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -584,7 +584,7 @@ void ide_acpi_get_timing(ide_hwif_t *hwif) * This function executes the _STM ACPI method for the target channel. * * _STM requires Identify Drive data, which has to passed as an argument. - * Unfortunately hd_driveid is a mangled version which we can't readily + * Unfortunately drive->id is a mangled version which we can't readily * use; hence we'll get the information afresh. */ void ide_acpi_push_timing(ide_hwif_t *hwif) @@ -614,10 +614,10 @@ void ide_acpi_push_timing(ide_hwif_t *hwif) in_params[0].buffer.length = sizeof(struct GTM_buffer); in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm; in_params[1].type = ACPI_TYPE_BUFFER; - in_params[1].buffer.length = sizeof(struct hd_driveid); + in_params[1].buffer.length = sizeof(ATA_ID_WORDS * 2); in_params[1].buffer.pointer = (u8 *)&master->idbuff; in_params[2].type = ACPI_TYPE_BUFFER; - in_params[2].buffer.length = sizeof(struct hd_driveid); + in_params[2].buffer.length = sizeof(ATA_ID_WORDS * 2); in_params[2].buffer.pointer = (u8 *)&slave->idbuff; /* Output buffer: _STM has no output */ diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 03c2cb6a58bc..46f9720f1ec9 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1866,14 +1866,14 @@ static const struct cd_list_entry ide_cd_quirks_list[] = { { NULL, NULL, 0 } }; -static unsigned int ide_cd_flags(struct hd_driveid *id) +static unsigned int ide_cd_flags(u16 *id) { const struct cd_list_entry *cle = ide_cd_quirks_list; while (cle->id_model) { - if (strcmp(cle->id_model, id->model) == 0 && + if (strcmp(cle->id_model, (char *)&id[ATA_ID_PROD]) == 0 && (cle->id_firmware == NULL || - strstr(id->fw_rev, cle->id_firmware))) + strstr((char *)&id[ATA_ID_FW_REV], cle->id_firmware))) return cle->cd_flags; cle++; } @@ -1885,7 +1885,8 @@ static int ide_cdrom_setup(ide_drive_t *drive) { struct cdrom_info *cd = drive->driver_data; struct cdrom_device_info *cdi = &cd->devinfo; - struct hd_driveid *id = drive->id; + u16 *id = drive->id; + char *fw_rev = (char *)&id[ATA_ID_FW_REV]; int nslots; blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn); @@ -1900,15 +1901,15 @@ static int ide_cdrom_setup(ide_drive_t *drive) drive->atapi_flags = IDE_AFLAG_MEDIA_CHANGED | IDE_AFLAG_NO_EJECT | ide_cd_flags(id); - if ((id->config & 0x0060) == 0x20) + if ((id[ATA_ID_CONFIG] & 0x0060) == 0x20) drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; if ((drive->atapi_flags & IDE_AFLAG_VERTOS_300_SSD) && - id->fw_rev[4] == '1' && id->fw_rev[6] <= '2') + fw_rev[4] == '1' && fw_rev[6] <= '2') drive->atapi_flags |= (IDE_AFLAG_TOCTRACKS_AS_BCD | IDE_AFLAG_TOCADDR_AS_BCD); else if ((drive->atapi_flags & IDE_AFLAG_VERTOS_600_ESD) && - id->fw_rev[4] == '1' && id->fw_rev[6] <= '2') + fw_rev[4] == '1' && fw_rev[6] <= '2') drive->atapi_flags |= IDE_AFLAG_TOCTRACKS_AS_BCD; else if (drive->atapi_flags & IDE_AFLAG_SANYO_3CD) /* 3 => use CD in slot 0 */ diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 33ea8c048717..f1669bca3cab 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -99,12 +99,13 @@ static void ide_disk_put(struct ide_disk_obj *idkp) * * It is called only once for each drive. */ -static int lba_capacity_is_ok(struct hd_driveid *id) +static int lba_capacity_is_ok(u16 *id) { + struct hd_driveid *driveid = (struct hd_driveid *)id; unsigned long lba_sects, chs_sects, head, tail; /* No non-LBA info .. so valid! */ - if (id->cyls == 0) + if (id[ATA_ID_CYLS] == 0) return 1; /* @@ -113,15 +114,15 @@ static int lba_capacity_is_ok(struct hd_driveid *id) * Some drives can be jumpered to use 15 heads instead of 16. * Some drives can be jumpered to use 4092 cyls instead of 16383. */ - if ((id->cyls == 16383 - || (id->cyls == 4092 && id->cur_cyls == 16383)) && - id->sectors == 63 && - (id->heads == 15 || id->heads == 16) && - (id->lba_capacity >= 16383*63*id->heads)) + if ((id[ATA_ID_CYLS] == 16383 || + (id[ATA_ID_CYLS] == 4092 && id[ATA_ID_CUR_CYLS] == 16383)) && + id[ATA_ID_SECTORS] == 63 && + (id[ATA_ID_HEADS] == 15 || id[ATA_ID_HEADS] == 16) && + (driveid->lba_capacity >= 16383 * 63 * id[ATA_ID_HEADS])) return 1; - lba_sects = id->lba_capacity; - chs_sects = id->cyls * id->heads * id->sectors; + lba_sects = driveid->lba_capacity; + chs_sects = id[ATA_ID_CYLS] * id[ATA_ID_HEADS] * id[ATA_ID_SECTORS]; /* perform a rough sanity check on lba_sects: within 10% is OK */ if ((lba_sects - chs_sects) < chs_sects/10) @@ -132,7 +133,7 @@ static int lba_capacity_is_ok(struct hd_driveid *id) tail = (lba_sects & 0xffff); lba_sects = (head | (tail << 16)); if ((lba_sects - chs_sects) < chs_sects/10) { - id->lba_capacity = lba_sects; + driveid->lba_capacity = lba_sects; return 1; /* lba_capacity is (now) good */ } @@ -389,18 +390,20 @@ static unsigned long long sectors_to_MB(unsigned long long n) * so on non-buggy drives we need test only one. * However, we should also check whether these fields are valid. */ -static inline int idedisk_supports_hpa(const struct hd_driveid *id) +static inline int idedisk_supports_hpa(const u16 *id) { - return (id->command_set_1 & 0x0400) && (id->cfs_enable_1 & 0x0400); + return (id[ATA_ID_COMMAND_SET_1] & 0x0400) && + (id[ATA_ID_CFS_ENABLE_1] & 0x0400); } /* * The same here. */ -static inline int idedisk_supports_lba48(const struct hd_driveid *id) +static inline int idedisk_supports_lba48(const u16 *id) { - return (id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400) - && id->lba_capacity_2; + return (id[ATA_ID_COMMAND_SET_2] & 0x0400) && + (id[ATA_ID_CFS_ENABLE_2] & 0x0400) && + ((struct hd_driveid *)id)->lba_capacity_2; } /* @@ -453,7 +456,8 @@ static void idedisk_check_hpa(ide_drive_t *drive) static void init_idedisk_capacity(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + struct hd_driveid *driveid = drive->driveid; + u16 *id = drive->id; /* * If this drive supports the Host Protected Area feature set, * then we may need to change our opinion about the drive's capacity. @@ -463,13 +467,13 @@ static void init_idedisk_capacity(ide_drive_t *drive) if (idedisk_supports_lba48(id)) { /* drive speaks 48-bit LBA */ drive->select.b.lba = 1; - drive->capacity64 = id->lba_capacity_2; + drive->capacity64 = driveid->lba_capacity_2; if (hpa) idedisk_check_hpa(drive); - } else if ((id->capability & 2) && lba_capacity_is_ok(id)) { + } else if ((driveid->capability & 2) && lba_capacity_is_ok(id)) { /* drive speaks 28-bit LBA */ drive->select.b.lba = 1; - drive->capacity64 = id->lba_capacity; + drive->capacity64 = driveid->lba_capacity; if (hpa) idedisk_check_hpa(drive); } else { @@ -523,7 +527,7 @@ static int proc_idedisk_read_cache int len; if (drive->id_read) - len = sprintf(out, "%i\n", drive->id->buf_size / 2); + len = sprintf(out, "%i\n", drive->id[ATA_ID_BUF_SIZE] / 2); else len = sprintf(out, "(none)\n"); @@ -618,7 +622,7 @@ static int set_multcount(ide_drive_t *drive, int arg) struct request *rq; int error; - if (arg < 0 || arg > drive->id->max_multsect) + if (arg < 0 || arg > drive->driveid->max_multsect) return -EINVAL; if (drive->special.b.set_multmode) @@ -650,7 +654,7 @@ static int set_nowerr(ide_drive_t *drive, int arg) static void update_ordered(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; unsigned ordered = QUEUE_ORDERED_NONE; prepare_flush_fn *prep_fn = NULL; @@ -762,8 +766,6 @@ static int set_lba_addressing(ide_drive_t *drive, int arg) #ifdef CONFIG_IDE_PROC_FS static void idedisk_add_settings(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; - ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, @@ -773,7 +775,7 @@ static void idedisk_add_settings(ide_drive_t *drive) ide_add_setting(drive, "address", SETTING_RW, TYPE_BYTE, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0, - id->max_multsect, 1, 1, &drive->mult_count, + drive->driveid->max_multsect, 1, 1, &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); @@ -795,7 +797,8 @@ static inline void idedisk_add_settings(ide_drive_t *drive) { ; } static void idedisk_setup(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct hd_driveid *id = drive->id; + u16 *id = drive->id; + char *m = (char *)&id[ATA_ID_PROD]; unsigned long long capacity; idedisk_add_settings(drive); @@ -807,7 +810,7 @@ static void idedisk_setup(ide_drive_t *drive) /* * Removable disks (eg. SYQUEST); ignore 'WD' drives */ - if (id->model[0] != 'W' || id->model[1] != 'D') + if (m[0] != 'W' || m[1] != 'D') drive->doorlocking = 1; } @@ -880,14 +883,14 @@ static void idedisk_setup(ide_drive_t *drive) drive->name, capacity, sectors_to_MB(capacity)); /* Only print cache size when it was specified */ - if (id->buf_size) - printk(KERN_CONT " w/%dKiB Cache", id->buf_size / 2); + if (id[ATA_ID_BUF_SIZE]) + printk(KERN_CONT " w/%dKiB Cache", id[ATA_ID_BUF_SIZE] / 2); printk(KERN_CONT ", CHS=%d/%d/%d\n", drive->bios_cyl, drive->bios_head, drive->bios_sect); /* write cache enabled? */ - if ((id->csfo & 1) || (id->cfs_enable_1 & (1 << 5))) + if ((id[ATA_ID_CSFO] & 1) || (id[ATA_ID_CFS_ENABLE_1] & (1 << 5))) drive->wcache = 1; write_cache(drive, 1); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 3fa07c0aeaa4..abab26de7687 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -288,7 +288,7 @@ EXPORT_SYMBOL_GPL(ide_destroy_dmatable); static int config_drive_for_dma (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct hd_driveid *id = drive->id; + u16 *id = drive->id; if (drive->media != ide_disk) { if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA) @@ -299,16 +299,17 @@ static int config_drive_for_dma (ide_drive_t *drive) * Enable DMA on any drive that has * UltraDMA (mode 0/1/2/3/4/5/6) enabled */ - if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f)) + if ((id[ATA_ID_FIELD_VALID] & 4) && + ((id[ATA_ID_UDMA_MODES] >> 8) & 0x7f)) return 1; /* * Enable DMA on any drive that has mode2 DMA * (multi or single) enabled */ - if (id->field_valid & 2) /* regular DMA */ - if ((id->dma_mword & 0x404) == 0x404 || - (id->dma_1word & 0x404) == 0x404) + if (id[ATA_ID_FIELD_VALID] & 2) /* regular DMA */ + if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 || + (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404) return 1; /* Consult the list of known "good" drives */ @@ -591,12 +592,12 @@ static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; } int __ide_dma_bad_drive (ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; int blacklist = ide_in_drive_list(id, drive_blacklist); if (blacklist) { printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n", - drive->name, id->model); + drive->name, (char *)&id[ATA_ID_PROD]); return blacklist; } return 0; @@ -612,21 +613,21 @@ static const u8 xfer_mode_bases[] = { static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; ide_hwif_t *hwif = drive->hwif; const struct ide_port_ops *port_ops = hwif->port_ops; unsigned int mask = 0; switch(base) { case XFER_UDMA_0: - if ((id->field_valid & 4) == 0) + if ((id[ATA_ID_FIELD_VALID] & 4) == 0) break; if (port_ops && port_ops->udma_filter) mask = port_ops->udma_filter(drive); else mask = hwif->ultra_mask; - mask &= id->dma_ultra; + mask &= id[ATA_ID_UDMA_MODES]; /* * avoid false cable warning from eighty_ninty_three() @@ -637,19 +638,19 @@ static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) } break; case XFER_MW_DMA_0: - if ((id->field_valid & 2) == 0) + if ((id[ATA_ID_FIELD_VALID] & 2) == 0) break; if (port_ops && port_ops->mdma_filter) mask = port_ops->mdma_filter(drive); else mask = hwif->mwdma_mask; - mask &= id->dma_mword; + mask &= id[ATA_ID_MWDMA_MODES]; break; case XFER_SW_DMA_0: - if (id->field_valid & 2) { - mask = id->dma_1word & hwif->swdma_mask; - } else if (id->tDMA) { - u8 mode = id->tDMA; + if (id[ATA_ID_FIELD_VALID] & 2) { + mask = id[ATA_ID_SWDMA_MODES] & hwif->swdma_mask; + } else if (drive->driveid->tDMA) { + u8 mode = drive->driveid->tDMA; /* * if the mode is valid convert it to the mask @@ -706,7 +707,8 @@ u8 ide_find_dma_mode(ide_drive_t *drive, u8 req_mode) /* * is this correct? */ - if (ide_dma_good_drive(drive) && drive->id->eide_dma_time < 150) + if (ide_dma_good_drive(drive) && + drive->id[ATA_ID_EIDE_DMA_TIME] < 150) mode = XFER_MW_DMA_1; } @@ -725,7 +727,7 @@ static int ide_tune_dma(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 speed; - if (drive->nodma || (drive->id->capability & 1) == 0) + if (drive->nodma || (drive->driveid->capability & 1) == 0) return 0; /* consult the list of known "bad" drives */ @@ -767,13 +769,15 @@ static int ide_dma_check(ide_drive_t *drive) int ide_id_dma_bug(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; - if (id->field_valid & 4) { - if ((id->dma_ultra >> 8) && (id->dma_mword >> 8)) + if (id[ATA_ID_FIELD_VALID] & 4) { + if ((id[ATA_ID_UDMA_MODES] >> 8) && + (id[ATA_ID_MWDMA_MODES] >> 8)) goto err_out; - } else if (id->field_valid & 2) { - if ((id->dma_mword >> 8) && (id->dma_1word >> 8)) + } else if (id[ATA_ID_FIELD_VALID] & 2) { + if ((id[ATA_ID_MWDMA_MODES] >> 8) && + (id[ATA_ID_SWDMA_MODES] >> 8)) goto err_out; } return 0; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index e9034c0125f3..67f93a46f510 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -965,12 +965,12 @@ static sector_t idefloppy_capacity(ide_drive_t *drive) * Check whether we can support a drive, based on the ATAPI IDENTIFY command * results. */ -static int idefloppy_identify_device(ide_drive_t *drive, struct hd_driveid *id) +static int idefloppy_identify_device(ide_drive_t *drive, u16 *id) { u8 gcw[2]; u8 device_type, protocol, removable, drq_type, packet_size; - *((u16 *) &gcw) = id->config; + *((u16 *)&gcw) = id[ATA_ID_CONFIG]; device_type = gcw[1] & 0x1F; removable = (gcw[0] & 0x80) >> 7; @@ -981,7 +981,8 @@ static int idefloppy_identify_device(ide_drive_t *drive, struct hd_driveid *id) #ifdef CONFIG_PPC /* kludge for Apple PowerBook internal zip */ if (device_type == 5 && - !strstr(id->model, "CD-ROM") && strstr(id->model, "ZIP")) + !strstr((char *)&id[ATA_ID_PROD], "CD-ROM") && + strstr((char *)&id[ATA_ID_PROD], "ZIP")) device_type = 0; #endif @@ -1024,9 +1025,10 @@ static inline void idefloppy_add_settings(ide_drive_t *drive) { ; } static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) { + u16 *id = drive->id; u8 gcw[2]; - *((u16 *) &gcw) = drive->id->config; + *((u16 *)&gcw) = id[ATA_ID_CONFIG]; floppy->pc = floppy->pc_stack; drive->pc_callback = ide_floppy_callback; @@ -1041,7 +1043,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) * it. It should be fixed as of version 1.9, but to be on the safe side * we'll leave the limitation below for the 2.2.x tree. */ - if (!strncmp(drive->id->model, "IOMEGA ZIP 100 ATAPI", 20)) { + if (!strncmp((char *)&id[ATA_ID_PROD], "IOMEGA ZIP 100 ATAPI", 20)) { drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE; /* This value will be visible in the /proc/ide/hdx/settings */ floppy->ticks = IDEFLOPPY_TICKS_DELAY; @@ -1052,7 +1054,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) * Guess what? The IOMEGA Clik! drive also needs the above fix. It makes * nasty clicking noises without it, so please don't remove this. */ - if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) { + if (strncmp((char *)&id[ATA_ID_PROD], "IOMEGA Clik!", 11) == 0) { blk_queue_max_sectors(drive->queue, 64); drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 68d655e0fa47..01b1943b315e 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -400,11 +400,11 @@ const struct ide_tp_ops default_tp_ops = { .output_data = ide_output_data, }; -void ide_fix_driveid(struct hd_driveid *driveid) +void ide_fix_driveid(u16 *id) { #ifndef __LITTLE_ENDIAN # ifdef __BIG_ENDIAN - u16 *id = (u16 *)driveid; + struct hd_driveid *driveid = (struct hd_driveid *)id; int i; for (i = 0; i < 256; i++) { @@ -593,18 +593,18 @@ EXPORT_SYMBOL(ide_wait_stat); /** * ide_in_drive_list - look for drive in black/white list * @id: drive identifier - * @drive_table: list to inspect + * @table: list to inspect * * Look for a drive in the blacklist and the whitelist tables * Returns 1 if the drive is found in the table. */ -int ide_in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table) +int ide_in_drive_list(u16 *id, const struct drive_list_entry *table) { - for ( ; drive_table->id_model; drive_table++) - if ((!strcmp(drive_table->id_model, id->model)) && - (!drive_table->id_firmware || - strstr(id->fw_rev, drive_table->id_firmware))) + for ( ; table->id_model; table++) + if ((!strcmp(table->id_model, (char *)&id[ATA_ID_PROD])) && + (!table->id_firmware || + strstr((char *)&id[ATA_ID_FW_REV], table->id_firmware))) return 1; return 0; } @@ -635,7 +635,7 @@ static const struct drive_list_entry ivb_list[] = { u8 eighty_ninty_three (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct hd_driveid *id = drive->id; + u16 *id = drive->id; int ivb = ide_in_drive_list(id, ivb_list); if (hwif->cbl == ATA_CBL_PATA40_SHORT) @@ -657,7 +657,8 @@ u8 eighty_ninty_three (ide_drive_t *drive) * - force bit13 (80c cable present) check also for !ivb devices * (unless the slave device is pre-ATA3) */ - if ((id->hw_config & 0x4000) || (ivb && (id->hw_config & 0x2000))) + if ((id[ATA_ID_HW_CONFIG] & 0x4000) || + (ivb && (id[ATA_ID_HW_CONFIG] & 0x2000))) return 1; no_80w: @@ -678,7 +679,7 @@ int ide_driveid_update(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; const struct ide_tp_ops *tp_ops = hwif->tp_ops; - struct hd_driveid *id; + u16 *id; unsigned long timeout, flags; u8 stat; @@ -722,16 +723,16 @@ int ide_driveid_update(ide_drive_t *drive) local_irq_enable(); local_irq_restore(flags); ide_fix_driveid(id); - if (id) { - drive->id->dma_ultra = id->dma_ultra; - drive->id->dma_mword = id->dma_mword; - drive->id->dma_1word = id->dma_1word; - /* anything more ? */ - kfree(id); - - if (drive->using_dma && ide_id_dma_bug(drive)) - ide_dma_off(drive); - } + + drive->id[ATA_ID_UDMA_MODES] = id[ATA_ID_UDMA_MODES]; + drive->id[ATA_ID_MWDMA_MODES] = id[ATA_ID_MWDMA_MODES]; + drive->id[ATA_ID_SWDMA_MODES] = id[ATA_ID_SWDMA_MODES]; + /* anything more ? */ + + kfree(id); + + if (drive->using_dma && ide_id_dma_bug(drive)) + ide_dma_off(drive); return 1; } @@ -740,6 +741,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) { ide_hwif_t *hwif = drive->hwif; const struct ide_tp_ops *tp_ops = hwif->tp_ops; + u16 *id = drive->id, i; int error = 0; u8 stat; ide_task_t task; @@ -750,7 +752,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) #endif /* Skip setting PIO flow-control modes on pre-EIDE drives */ - if ((speed & 0xf8) == XFER_PIO_0 && !(drive->id->capability & 0x08)) + if ((speed & 0xf8) == XFER_PIO_0 && !(drive->driveid->capability & 8)) goto skip; /* @@ -802,9 +804,9 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) return error; } - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; + id[ATA_ID_UDMA_MODES] &= ~0xFF00; + id[ATA_ID_MWDMA_MODES] &= ~0x0F00; + id[ATA_ID_SWDMA_MODES] &= ~0x0F00; skip: #ifdef CONFIG_BLK_DEV_IDEDMA @@ -814,23 +816,17 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) ide_dma_off_quietly(drive); #endif - switch(speed) { - case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break; - case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break; - case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break; - case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break; - case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break; - case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break; - case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break; - case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break; - case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break; - case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break; - case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break; - case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break; - case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break; - case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break; - default: break; + if (speed >= XFER_UDMA_0) { + i = 1 << (speed - XFER_UDMA_0); + id[ATA_ID_UDMA_MODES] |= (i << 8 | i); + } else if (speed >= XFER_MW_DMA_0) { + i = 1 << (speed - XFER_MW_DMA_0); + id[ATA_ID_MWDMA_MODES] |= (i << 8 | i); + } else if (speed >= XFER_SW_DMA_0) { + i = 1 << (speed - XFER_SW_DMA_0); + id[ATA_ID_SWDMA_MODES] |= (i << 8 | i); } + if (!drive->init_speed) drive->init_speed = speed; drive->current_speed = speed; @@ -1035,7 +1031,7 @@ out: static void ide_disk_pre_reset(ide_drive_t *drive) { - int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1; + int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1; drive->special.all = 0; drive->special.b.set_geometry = legacy; diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 97fefabea8b8..3066d7e75c73 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -90,29 +90,31 @@ static u8 ide_rate_filter(ide_drive_t *drive, u8 speed) u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode) { - int pio_mode; - struct hd_driveid* id = drive->id; - int overridden = 0; + u16 *id = drive->id; + int pio_mode = -1, overridden = 0; if (mode_wanted != 255) return min_t(u8, mode_wanted, max_mode); - if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0 && - (pio_mode = ide_scan_pio_blacklist(id->model)) != -1) { + if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0) + pio_mode = ide_scan_pio_blacklist((char *)&id[ATA_ID_PROD]); + + if (pio_mode != -1) { printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name); } else { - pio_mode = id->tPIO; + pio_mode = drive->driveid->tPIO; if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */ pio_mode = 2; overridden = 1; } - if (id->field_valid & 2) { /* drive implements ATA2? */ - if (id->capability & 8) { /* IORDY supported? */ - if (id->eide_pio_modes & 7) { + + if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */ + if (drive->driveid->capability & 8) { /* IORDY sup? */ + if (id[ATA_ID_PIO_MODES] & 7) { overridden = 0; - if (id->eide_pio_modes & 4) + if (id[ATA_ID_PIO_MODES] & 4) pio_mode = 5; - else if (id->eide_pio_modes & 2) + else if (id[ATA_ID_PIO_MODES] & 2) pio_mode = 4; else pio_mode = 3; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index a776a6d73c54..b4f8ca106639 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -50,44 +50,44 @@ static void generic_id(ide_drive_t *drive) { - drive->id->cyls = drive->cyl; - drive->id->heads = drive->head; - drive->id->sectors = drive->sect; - drive->id->cur_cyls = drive->cyl; - drive->id->cur_heads = drive->head; - drive->id->cur_sectors = drive->sect; + u16 *id = drive->id; + + id[ATA_ID_CUR_CYLS] = id[ATA_ID_CYLS] = drive->cyl; + id[ATA_ID_CUR_HEADS] = id[ATA_ID_HEADS] = drive->head; + id[ATA_ID_CUR_SECTORS] = id[ATA_ID_SECTORS] = drive->sect; } static void ide_disk_init_chs(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; /* Extract geometry if we did not already have one for the drive */ if (!drive->cyl || !drive->head || !drive->sect) { - drive->cyl = drive->bios_cyl = id->cyls; - drive->head = drive->bios_head = id->heads; - drive->sect = drive->bios_sect = id->sectors; + drive->cyl = drive->bios_cyl = id[ATA_ID_CYLS]; + drive->head = drive->bios_head = id[ATA_ID_HEADS]; + drive->sect = drive->bios_sect = id[ATA_ID_SECTORS]; } /* Handle logical geometry translation by the drive */ - if ((id->field_valid & 1) && id->cur_cyls && - id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) { - drive->cyl = id->cur_cyls; - drive->head = id->cur_heads; - drive->sect = id->cur_sectors; + if ((id[ATA_ID_FIELD_VALID] & 1) && id[ATA_ID_CUR_CYLS] && + id[ATA_ID_CUR_HEADS] && id[ATA_ID_CUR_HEADS] <= 16 && + id[ATA_ID_CUR_SECTORS]) { + drive->cyl = id[ATA_ID_CUR_CYLS]; + drive->head = id[ATA_ID_CUR_HEADS]; + drive->sect = id[ATA_ID_CUR_SECTORS]; } /* Use physical geometry if what we have still makes no sense */ - if (drive->head > 16 && id->heads && id->heads <= 16) { - drive->cyl = id->cyls; - drive->head = id->heads; - drive->sect = id->sectors; + if (drive->head > 16 && id[ATA_ID_HEADS] && id[ATA_ID_HEADS] <= 16) { + drive->cyl = id[ATA_ID_CYLS]; + drive->head = id[ATA_ID_HEADS]; + drive->sect = id[ATA_ID_SECTORS]; } } static void ide_disk_init_mult_count(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + struct hd_driveid *id = drive->driveid; if (id->max_multsect) { #ifdef CONFIG_IDEDISK_MULTI_MODE @@ -118,10 +118,10 @@ static void ide_disk_init_mult_count(ide_drive_t *drive) static inline void do_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); + u16 *id = drive->id; + char *m = (char *)&id[ATA_ID_PROD]; int bswap = 1; - struct hd_driveid *id; - id = drive->id; /* read 512 bytes of id info */ hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); @@ -138,23 +138,24 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) * WIN_PIDENTIFY *usually* returns little-endian info. */ if (cmd == WIN_PIDENTIFY) { - if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */ - || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */ - || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */ + if ((m[0] == 'N' && m[1] == 'E') || /* NEC */ + (m[0] == 'F' && m[1] == 'X') || /* Mitsumi */ + (m[0] == 'P' && m[1] == 'i')) /* Pioneer */ /* Vertos drives may still be weird */ - bswap ^= 1; + bswap ^= 1; } - ide_fixstring(id->model, sizeof(id->model), bswap); - ide_fixstring(id->fw_rev, sizeof(id->fw_rev), bswap); - ide_fixstring(id->serial_no, sizeof(id->serial_no), bswap); + + ide_fixstring(m, ATA_ID_PROD_LEN, bswap); + ide_fixstring((char *)&id[ATA_ID_FW_REV], ATA_ID_FW_REV_LEN, bswap); + ide_fixstring((char *)&id[ATA_ID_SERNO], ATA_ID_SERNO_LEN, bswap); /* we depend on this a lot! */ - id->model[sizeof(id->model)-1] = '\0'; + m[ATA_ID_PROD_LEN - 1] = '\0'; - if (strstr(id->model, "E X A B Y T E N E S T")) + if (strstr(m, "E X A B Y T E N E S T")) goto err_misc; - printk(KERN_INFO "%s: %s, ", drive->name, id->model); + printk(KERN_INFO "%s: %s, ", drive->name, m); drive->present = 1; drive->dead = 0; @@ -163,15 +164,15 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) * Check for an ATAPI device */ if (cmd == WIN_PIDENTIFY) { - u8 type = (id->config >> 8) & 0x1f; + u8 type = (id[ATA_ID_CONFIG] >> 8) & 0x1f; printk(KERN_CONT "ATAPI "); switch (type) { case ide_floppy: - if (!strstr(id->model, "CD-ROM")) { - if (!strstr(id->model, "oppy") && - !strstr(id->model, "poyp") && - !strstr(id->model, "ZIP")) + if (!strstr(m, "CD-ROM")) { + if (!strstr(m, "oppy") && + !strstr(m, "poyp") && + !strstr(m, "ZIP")) printk(KERN_CONT "cdrom or floppy?, assuming "); if (drive->media != ide_cdrom) { printk(KERN_CONT "FLOPPY"); @@ -185,8 +186,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) drive->removable = 1; #ifdef CONFIG_PPC /* kludge for Apple PowerBook internal zip */ - if (!strstr(id->model, "CD-ROM") && - strstr(id->model, "ZIP")) { + if (!strstr(m, "CD-ROM") && strstr(m, "ZIP")) { printk(KERN_CONT "FLOPPY"); type = ide_floppy; break; @@ -220,14 +220,13 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) * 0x848a = CompactFlash device * These are *not* removable in Linux definition of the term */ - - if ((id->config != 0x848a) && (id->config & (1<<7))) + if (id[ATA_ID_CONFIG] != 0x848a && (id[ATA_ID_CONFIG] & (1 << 7))) drive->removable = 1; drive->media = ide_disk; printk(KERN_CONT "%s DISK drive\n", - (id->config == 0x848a) ? "CFA" : "ATA"); + (id[ATA_ID_CONFIG] == 0x848a) ? "CFA" : "ATA"); return; @@ -525,7 +524,8 @@ static void enable_nest (ide_drive_t *drive) const struct ide_tp_ops *tp_ops = hwif->tp_ops; u8 stat; - printk(KERN_INFO "%s: enabling %s -- ", hwif->name, drive->id->model); + printk(KERN_INFO "%s: enabling %s -- ", + hwif->name, (char *)&drive->id[ATA_ID_PROD]); SELECT_DRIVE(drive); msleep(50); @@ -566,6 +566,8 @@ static void enable_nest (ide_drive_t *drive) static inline u8 probe_for_drive (ide_drive_t *drive) { + char *m; + /* * In order to keep things simple we have an id * block for all drives at all times. If the device @@ -582,8 +584,10 @@ static inline u8 probe_for_drive (ide_drive_t *drive) printk(KERN_ERR "ide: out of memory for id data.\n"); return 0; } - strcpy(drive->id->model, "UNKNOWN"); - + + m = (char *)&drive->id[ATA_ID_PROD]; + strcpy(m, "UNKNOWN"); + /* skip probing? */ if (!drive->noprobe) { @@ -595,7 +599,8 @@ static inline u8 probe_for_drive (ide_drive_t *drive) if (!drive->present) /* drive not found */ return 0; - if (strstr(drive->id->model, "E X A B Y T E N E S T")) + + if (strstr(m, "E X A B Y T E N E S T")) enable_nest(drive); /* identification failed? */ @@ -739,36 +744,38 @@ out: /** * ide_undecoded_slave - look for bad CF adapters - * @drive1: drive + * @dev1: slave device * * Analyse the drives on the interface and attempt to decide if we * have the same drive viewed twice. This occurs with crap CF adapters * and PCMCIA sometimes. */ -void ide_undecoded_slave(ide_drive_t *drive1) +void ide_undecoded_slave(ide_drive_t *dev1) { - ide_drive_t *drive0 = &drive1->hwif->drives[0]; + ide_drive_t *dev0 = &dev1->hwif->drives[0]; - if ((drive1->dn & 1) == 0 || drive0->present == 0) + if ((dev1->dn & 1) == 0 || dev0->present == 0) return; /* If the models don't match they are not the same product */ - if (strcmp(drive0->id->model, drive1->id->model)) + if (strcmp((char *)&dev0->id[ATA_ID_PROD], + (char *)&dev1->id[ATA_ID_PROD])) return; /* Serial numbers do not match */ - if (strncmp(drive0->id->serial_no, drive1->id->serial_no, 20)) + if (strncmp((char *)&dev0->id[ATA_ID_SERNO], + (char *)&dev1->id[ATA_ID_SERNO], ATA_ID_SERNO_LEN)) return; /* No serial number, thankfully very rare for CF */ - if (drive0->id->serial_no[0] == 0) + if (*(char *)&dev0->id[ATA_ID_SERNO] == 0) return; /* Appears to be an IDE flash adapter with decode bugs */ printk(KERN_WARNING "ide-probe: ignoring undecoded slave\n"); - drive1->present = 0; + dev1->present = 0; } EXPORT_SYMBOL_GPL(ide_undecoded_slave); @@ -852,7 +859,7 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) if (hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) drive->no_io_32bit = 1; else - drive->no_io_32bit = drive->id->dword_io ? 1 : 0; + drive->no_io_32bit = drive->id[ATA_ID_DWORD_IO] ? 1 : 0; } } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index f66c9c3f6fc6..0bdbb9bf90ba 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -561,11 +561,10 @@ static int proc_ide_read_dmodel (char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = (ide_drive_t *) data; - struct hd_driveid *id = drive->id; + char *m = (char *)&drive->id[ATA_ID_PROD]; int len; - len = sprintf(page, "%.40s\n", - (id && id->model[0]) ? (char *)id->model : "(none)"); + len = sprintf(page, "%.40s\n", m[0] ? m : "(none)"); PROC_IDE_READ_RETURN(page, start, off, count, eof, len); } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index a373cc4142b7..2c4c6674db61 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2311,7 +2311,7 @@ static int idetape_identify_device(ide_drive_t *drive) if (drive->id_read == 0) return 1; - *((unsigned short *) &gcw) = drive->id->config; + *((u16 *)&gcw) = drive->id[ATA_ID_CONFIG]; protocol = (gcw[1] & 0xC0) >> 6; device_type = gcw[1] & 0x1F; @@ -2463,7 +2463,7 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) drive->dsc_overlap = 0; } /* Seagate Travan drives do not support DSC overlap. */ - if (strstr(drive->id->model, "Seagate STT3401")) + if (strstr((char *)&drive->id[ATA_ID_PROD], "Seagate STT3401")) drive->dsc_overlap = 0; tape->minor = minor; tape->name[0] = 'h'; @@ -2471,7 +2471,8 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) tape->name[2] = '0' + minor; tape->chrdev_dir = IDETAPE_DIR_NONE; tape->pc = tape->pc_stack; - *((unsigned short *) &gcw) = drive->id->config; + + *((u16 *)&gcw) = drive->id[ATA_ID_CONFIG]; /* Command packet DRQ type */ if (((gcw[0] & 0x60) >> 5) == 1) diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 7fb6f1c86272..f889373d7f95 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -729,7 +729,7 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) u8 args[4], xfer_rate = 0; ide_task_t tfargs; struct ide_taskfile *tf = &tfargs.tf; - struct hd_driveid *id = drive->id; + u16 *id = drive->id; if (NULL == (void *) arg) { struct request *rq; @@ -772,7 +772,9 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) if (tf->command == WIN_SETFEATURES && tf->feature == SETFEATURES_XFER && tf->nsect >= XFER_SW_DMA_0 && - (id->dma_ultra || id->dma_mword || id->dma_1word)) { + (id[ATA_ID_UDMA_MODES] || + id[ATA_ID_MWDMA_MODES] || + id[ATA_ID_SWDMA_MODES])) { xfer_rate = args[1]; if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) { printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot " diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c index 8c2f8327f487..d64f345f2fc0 100644 --- a/drivers/ide/ide-timings.c +++ b/drivers/ide/ide-timings.c @@ -78,15 +78,15 @@ EXPORT_SYMBOL_GPL(ide_timing_find_mode); u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); u16 cycle = 0; - if (id->field_valid & 2) { - if (id->capability & 8) - cycle = id->eide_pio_iordy; + if (id[ATA_ID_FIELD_VALID] & 2) { + if (drive->driveid->capability & 8) + cycle = id[ATA_ID_EIDE_PIO_IORDY]; else - cycle = id->eide_pio; + cycle = id[ATA_ID_EIDE_PIO]; /* conservative "downgrade" for all pre-ATA2 drives */ if (pio < 3 && cycle < t->cycle) @@ -138,7 +138,7 @@ EXPORT_SYMBOL_GPL(ide_timing_merge); int ide_timing_compute(ide_drive_t *drive, u8 speed, struct ide_timing *t, int T, int UT) { - struct hd_driveid *id = drive->id; + u16 *id = drive->id; struct ide_timing *s, p; /* @@ -157,16 +157,15 @@ int ide_timing_compute(ide_drive_t *drive, u8 speed, * If the drive is an EIDE drive, it can tell us it needs extended * PIO/MWDMA cycle timing. */ - if (id && id->field_valid & 2) { /* EIDE drive */ - + if (id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */ memset(&p, 0, sizeof(p)); if (speed <= XFER_PIO_2) - p.cycle = p.cyc8b = id->eide_pio; + p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO]; else if (speed <= XFER_PIO_5) - p.cycle = p.cyc8b = id->eide_pio_iordy; + p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY]; else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) - p.cycle = id->eide_dma_min; + p.cycle = id[ATA_ID_EIDE_DMA_MIN]; ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 772451600e4d..8d3fab33a3c7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -328,7 +328,7 @@ int set_using_dma(ide_drive_t *drive, int arg) if (arg < 0 || arg > 1) return -EINVAL; - if (!drive->id || !(drive->id->capability & 1)) + if ((drive->driveid->capability & 1) == 0) goto out; if (hwif->dma_ops == NULL) @@ -710,21 +710,21 @@ static ssize_t model_show(struct device *dev, struct device_attribute *attr, char *buf) { ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", drive->id->model); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_PROD]); } static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, char *buf) { ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", drive->id->fw_rev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_FW_REV]); } static ssize_t serial_show(struct device *dev, struct device_attribute *attr, char *buf) { ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", drive->id->serial_no); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_SERNO]); } static struct device_attribute ide_dev_attrs[] = { diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 2338f344ea24..ef4e84053a81 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -151,12 +151,14 @@ static int qd_find_disk_type (ide_drive_t *drive, int *active_time, int *recovery_time) { struct qd65xx_timing_s *p; - char model[40]; + char *m = (char *)&drive->id[ATA_ID_PROD]; + char model[ATA_ID_PROD_LEN]; - if (!*drive->id->model) return 0; + if (*m == 0) + return 0; - strncpy(model,drive->id->model,40); - ide_fixstring(model,40,1); /* byte-swap */ + strncpy(model, m, ATA_ID_PROD_LEN); + ide_fixstring(model, ATA_ID_PROD_LEN, 1); /* byte-swap */ for (p = qd65xx_timing ; p->offset != -1 ; p++) { if (!strncmp(p->model, model+p->offset, 4)) { @@ -185,20 +187,20 @@ static void qd_set_timing (ide_drive_t *drive, u8 timing) static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio) { + u16 *id = drive->id; int active_time = 175; int recovery_time = 415; /* worst case values from the dos driver */ /* * FIXME: use "pio" value */ - if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time) - && drive->id->tPIO && (drive->id->field_valid & 0x02) - && drive->id->eide_pio >= 240) { - + if (!qd_find_disk_type(drive, &active_time, &recovery_time) && + drive->driveid->tPIO && (id[ATA_ID_FIELD_VALID] & 2) && + id[ATA_ID_EIDE_PIO] >= 240) { printk(KERN_INFO "%s: PIO mode%d\n", drive->name, - drive->id->tPIO); + drive->driveid->tPIO); active_time = 110; - recovery_time = drive->id->eide_pio - 120; + recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120; } qd_set_timing(drive, qd6500_compute_timing(HWIF(drive), active_time, recovery_time)); diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index d647526af557..fcc701b3c0a2 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -134,8 +134,8 @@ static u8 ali_udma_filter(ide_drive_t *drive) if (m5229_revision > 0x20 && m5229_revision < 0xC2) { if (drive->media != ide_disk) return 0; - if (chip_is_1543c_e && strstr(drive->id->model, "WDC ") && - wdc_udma == 0) + if (wdc_udma == 0 && chip_is_1543c_e && + strstr((char *)&drive->id[ATA_ID_PROD], "WDC ")) return 0; } diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index f235db8c678b..774ff58603a2 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -82,16 +82,18 @@ static u8 cs5530_udma_filter(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1]; - struct hd_driveid *mateid = mate->id; + u16 *mateid = mate->id; u8 mask = hwif->ultra_mask; if (mate->present == 0) goto out; - if ((mateid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { - if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7)) + if ((mate->driveid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { + if ((mateid[ATA_ID_FIELD_VALID] & 4) && + (mateid[ATA_ID_UDMA_MODES] & 7)) goto out; - if ((mateid->field_valid & 2) && (mateid->dma_mword & 7)) + if ((mateid[ATA_ID_FIELD_VALID] & 2) && + (mateid[ATA_ID_MWDMA_MODES] & 7)) mask = 0; } out: diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index c37ab1743819..b7f77fd3cb6e 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -605,10 +605,10 @@ static const struct hpt_info hpt371n __devinitdata = { static int check_in_drive_list(ide_drive_t *drive, const char **list) { - struct hd_driveid *id = drive->id; + char *m = (char *)&drive->id[ATA_ID_PROD]; while (*list) - if (!strcmp(*list++,id->model)) + if (!strcmp(*list++, m)) return 1; return 0; } @@ -731,11 +731,11 @@ static void hpt3xx_set_pio_mode(ide_drive_t *drive, const u8 pio) static void hpt3xx_quirkproc(ide_drive_t *drive) { - struct hd_driveid *id = drive->id; + char *m = (char *)&drive->id[ATA_ID_PROD]; const char **list = quirk_drives; while (*list) - if (strstr(id->model, *list++)) { + if (strstr(m, *list++)) { drive->quirk_list = 1; return; } diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 4a1508a707cc..31d4e6aef1b8 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -446,8 +446,7 @@ static u8 it821x_cable_detect(ide_hwif_t *hwif) static void it821x_quirkproc(ide_drive_t *drive) { struct it821x_dev *itdev = ide_get_hwifdata(drive->hwif); - struct hd_driveid *id = drive->id; - u16 *idbits = (u16 *)drive->id; + u16 *id = drive->id; if (!itdev->smart) { /* @@ -466,36 +465,36 @@ static void it821x_quirkproc(ide_drive_t *drive) */ /* Check for RAID v native */ - if(strstr(id->model, "Integrated Technology Express")) { + if (strstr((char *)&id[ATA_ID_PROD], + "Integrated Technology Express")) { /* In raid mode the ident block is slightly buggy We need to set the bits so that the IDE layer knows LBA28. LBA48 and DMA ar valid */ - id->capability |= 3; /* LBA28, DMA */ - id->command_set_2 |= 0x0400; /* LBA48 valid */ - id->cfs_enable_2 |= 0x0400; /* LBA48 on */ + drive->driveid->capability |= 3; /* LBA28, DMA */ + id[ATA_ID_COMMAND_SET_2] |= 0x0400; /* LBA48 valid */ + id[ATA_ID_CFS_ENABLE_2] |= 0x0400; /* LBA48 on */ /* Reporting logic */ printk(KERN_INFO "%s: IT8212 %sRAID %d volume", - drive->name, - idbits[147] ? "Bootable ":"", - idbits[129]); - if(idbits[129] != 1) - printk("(%dK stripe)", idbits[146]); - printk(".\n"); + drive->name, id[147] ? "Bootable " : "", + id[ATA_ID_CSFO]); + if (id[ATA_ID_CSFO] != 1) + printk(KERN_CONT "(%dK stripe)", id[146]); + printk(KERN_CONT ".\n"); } else { /* Non RAID volume. Fixups to stop the core code doing unsupported things */ - id->field_valid &= 3; - id->queue_depth = 0; - id->command_set_1 = 0; - id->command_set_2 &= 0xC400; - id->cfsse &= 0xC000; - id->cfs_enable_1 = 0; - id->cfs_enable_2 &= 0xC400; - id->csf_default &= 0xC000; - id->word127 = 0; - id->dlf = 0; - id->csfo = 0; - id->cfa_power = 0; + id[ATA_ID_FIELD_VALID] &= 3; + id[ATA_ID_QUEUE_DEPTH] = 0; + id[ATA_ID_COMMAND_SET_1] = 0; + id[ATA_ID_COMMAND_SET_2] &= 0xC400; + id[ATA_ID_CFSSE] &= 0xC000; + id[ATA_ID_CFS_ENABLE_1] = 0; + id[ATA_ID_CFS_ENABLE_2] &= 0xC400; + id[ATA_ID_CSF_DEFAULT] &= 0xC000; + id[127] = 0; + id[ATA_ID_DLF] = 0; + id[ATA_ID_CSFO] = 0; + id[ATA_ID_CFA_POWER] = 0; printk(KERN_INFO "%s: Performing identify fixups.\n", drive->name); } @@ -505,8 +504,8 @@ static void it821x_quirkproc(ide_drive_t *drive) * IDE core that DMA is supported (it821x hardware * takes care of DMA mode programming). */ - if (id->capability & 1) { - id->dma_mword |= 0x0101; + if (drive->driveid->capability & 1) { + id[ATA_ID_MWDMA_MODES] |= 0x0101; drive->current_speed = XFER_MW_DMA_0; } } diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index d477da6b5858..7ecfcd06f47e 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -203,10 +203,10 @@ static u8 pdcnew_cable_detect(ide_hwif_t *hwif) static void pdcnew_quirkproc(ide_drive_t *drive) { - const char **list, *model = drive->id->model; + const char **list, *m = (char *)&drive->id[ATA_ID_PROD]; for (list = pdc_quirk_drives; *list != NULL; list++) - if (strstr(model, *list) != NULL) { + if (strstr(m, *list) != NULL) { drive->quirk_list = 2; return; } diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index de9a27400462..23e861b177ce 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -86,7 +86,7 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed) * Prefetch_EN / IORDY_EN / PA[3:0] bits of register A */ AP &= ~0x3f; - if (drive->id->capability & 4) + if (drive->driveid->capability & 4) AP |= 0x20; /* set IORDY_EN bit */ if (drive->media == ide_disk) AP |= 0x10; /* set Prefetch_EN bit */ @@ -154,10 +154,10 @@ static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif) static void pdc202xx_quirkproc(ide_drive_t *drive) { - const char **list, *model = drive->id->model; + const char **list, *m = (char *)&drive->id[ATA_ID_PROD]; for (list = pdc_quirk_drives; *list != NULL; list++) - if (strstr(model, *list) != NULL) { + if (strstr(m, *list) != NULL) { drive->quirk_list = 2; return; } diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 8efaed16fea3..5c8367df14e4 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -105,16 +105,18 @@ static u8 sc1200_udma_filter(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1]; - struct hd_driveid *mateid = mate->id; + u16 *mateid = mate->id; u8 mask = hwif->ultra_mask; if (mate->present == 0) goto out; - if ((mateid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { - if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7)) + if ((mate->driveid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { + if ((mateid[ATA_ID_FIELD_VALID] & 4) && + (mateid[ATA_ID_UDMA_MODES] & 7)) goto out; - if ((mateid->field_valid & 2) && (mateid->dma_mword & 7)) + if ((mateid[ATA_ID_FIELD_VALID] & 2) && + (mateid[ATA_ID_MWDMA_MODES] & 7)) mask = 0; } out: diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index c3bdc6e51a48..ded6a13fd406 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -57,8 +57,10 @@ static struct pci_dev *isa_dev; static int check_in_drive_lists (ide_drive_t *drive, const char **list) { + char *m = (char *)&drive->id[ATA_ID_PROD]; + while (*list) - if (!strcmp(*list++, drive->id->model)) + if (!strcmp(*list++, m)) return 1; return 0; } diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index db2b88a369ab..7b5bd8729f64 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -223,7 +223,9 @@ static u8 sil_pata_udma_filter(ide_drive_t *drive) static u8 sil_sata_udma_filter(ide_drive_t *drive) { - return strstr(drive->id->model, "Maxtor") ? ATA_UDMA5 : ATA_UDMA6; + char *m = (char *)&drive->id[ATA_ID_PROD]; + + return strstr(m, "Maxtor") ? ATA_UDMA5 : ATA_UDMA6; } /** @@ -616,8 +618,8 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) static int is_dev_seagate_sata(ide_drive_t *drive) { - const char *s = &drive->id->model[0]; - unsigned len = strnlen(s, sizeof(drive->id->model)); + const char *s = (const char *)&drive->id[ATA_ID_PROD]; + unsigned len = strnlen(s, ATA_ID_PROD_LEN); if ((len > 4) && (!memcmp(s, "ST", 2))) if ((!memcmp(s + len - 2, "AS", 2)) || diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index fa2be26272d5..c3432da78d52 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -669,9 +669,9 @@ static void set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2, u8 speed) { + u16 *id = drive->id; int cycleTime, accessTime = 0, recTime = 0; unsigned accessTicks, recTicks; - struct hd_driveid *id = drive->id; struct mdma_timings_t* tm = NULL; int i; @@ -686,8 +686,8 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2, } /* Check if drive provides explicit DMA cycle time */ - if ((id->field_valid & 2) && id->eide_dma_time) - cycleTime = max_t(int, id->eide_dma_time, cycleTime); + if ((id[ATA_ID_FIELD_VALID] & 2) && id[ATA_ID_EIDE_DMA_TIME]) + cycleTime = max_t(int, id[ATA_ID_EIDE_DMA_TIME], cycleTime); /* OHare limits according to some old Apple sources */ if ((intf_type == controller_ohare) && (cycleTime < 150)) diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 81c16cba5417..b1b506f31be8 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -452,7 +452,7 @@ static inline void idescsi_add_settings(ide_drive_t *drive) { ; } */ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi) { - if (drive->id && (drive->id->config & 0x0060) == 0x20) + if ((drive->id[ATA_ID_CONFIG] & 0x0060) == 0x20) set_bit(IDE_AFLAG_DRQ_INTERRUPT, &drive->atapi_flags); clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); #if IDESCSI_DEBUG_LOG @@ -811,6 +811,7 @@ static int ide_scsi_probe(ide_drive_t *drive) struct gendisk *g; static int warned; int err = -ENOMEM; + u16 last_lun; if (!warned && drive->media == ide_cdrom) { printk(KERN_WARNING "ide-scsi is deprecated for cd burning! Use ide-cd and give dev=/dev/hdX as device\n"); @@ -836,12 +837,12 @@ static int ide_scsi_probe(ide_drive_t *drive) host->max_id = 1; - if (drive->id->last_lun) - debug_log("%s: id->last_lun=%u\n", drive->name, - drive->id->last_lun); + last_lun = drive->id[ATA_ID_LAST_LUN]; + if (last_lun) + debug_log("%s: last_lun=%u\n", drive->name, last_lun); - if ((drive->id->last_lun & 0x7) != 7) - host->max_lun = (drive->id->last_lun & 0x7) + 1; + if ((last_lun & 7) != 7) + host->max_lun = (last_lun & 7) + 1; else host->max_lun = 1; diff --git a/include/linux/ide.h b/include/linux/ide.h index 6514db8fd2e4..0c85aff3edf1 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -380,7 +380,11 @@ struct ide_drive_s { struct request *rq; /* current request */ struct ide_drive_s *next; /* circular list of hwgroup drives */ void *driver_data; /* extra driver data */ - struct hd_driveid *id; /* drive model identification info */ + union { + /* identification info */ + struct hd_driveid *driveid; + u16 *id; + }; #ifdef CONFIG_IDE_PROC_FS struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ struct ide_settings_s *settings;/* /proc/ide/ drive settings */ @@ -920,7 +924,7 @@ ide_startstop_t __ide_error(ide_drive_t *, struct request *, u8, u8); ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat); -extern void ide_fix_driveid(struct hd_driveid *); +void ide_fix_driveid(u16 *); extern void ide_fixstring(u8 *, const int, const int); @@ -1240,7 +1244,7 @@ struct drive_list_entry { const char *id_firmware; }; -int ide_in_drive_list(struct hd_driveid *, const struct drive_list_entry *); +int ide_in_drive_list(u16 *, const struct drive_list_entry *); #ifdef CONFIG_BLK_DEV_IDEDMA int __ide_dma_bad_drive(ide_drive_t *); @@ -1347,12 +1351,13 @@ const char *ide_xfer_verbose(u8 mode); extern void ide_toggle_bounce(ide_drive_t *drive, int on); extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate); -static inline int ide_dev_has_iordy(struct hd_driveid *id) +static inline int ide_dev_has_iordy(u16 *id) { - return ((id->field_valid & 2) && (id->capability & 8)) ? 1 : 0; + return ((id[ATA_ID_FIELD_VALID] & 2) && + (((struct hd_driveid *)id)->capability & 8)) ? 1 : 0; } -static inline int ide_dev_is_sata(struct hd_driveid *id) +static inline int ide_dev_is_sata(u16 *id) { /* * See if word 93 is 0 AND drive is at least ATA-5 compatible @@ -1360,7 +1365,7 @@ static inline int ide_dev_is_sata(struct hd_driveid *id) * this trick allows us to filter out the reserved values of * 0x0000 and 0xffff along with the earlier ATA revisions... */ - if (id->hw_config == 0 && (short)id->major_rev_num >= 0x0020) + if (id[ATA_ID_HW_CONFIG] == 0 && (short)id[ATA_ID_MAJOR_VER] >= 0x0020) return 1; return 0; } @@ -1437,11 +1442,11 @@ extern struct bus_type ide_bus_type; extern struct class *ide_port_class; /* check if CACHE FLUSH (EXT) command is supported (bits defined in ATA-6) */ -#define ide_id_has_flush_cache(id) ((id)->cfs_enable_2 & 0x3000) +#define ide_id_has_flush_cache(id) ((id)[ATA_ID_CFS_ENABLE_2] & 0x3000) /* some Maxtor disks have bit 13 defined incorrectly so check bit 10 too */ #define ide_id_has_flush_cache_ext(id) \ - (((id)->cfs_enable_2 & 0x2400) == 0x2400) + (((id)[ATA_ID_CFS_ENABLE_2] & 0x2400) == 0x2400) static inline void ide_dump_identify(u8 *id) { -- cgit v1.2.3 From 48fb2688aa67baba373531cc4ed2d9e695983c3f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:19 +0200 Subject: ide: remove drive->driveid * Factor out HDIO_[OBSOLETE,GET]_IDENTITY ioctls handling to ide_get_identity_ioctl(). * Use temporary buffer in ide_get_identity_ioctl() instead of accessing drive->id directly. * Add ide_id_to_hd_driveid() inline to convert raw id into struct hd_driveid format (needed on big-endian). * Use ide_id_to_hd_driveid() in ide_get_identity_ioctl(), cleanup ide_fix_driveid() and switch ide to use use raw id. * Remove no longer needed drive->driveid. This leaves us with 3 users of struct hd_driveid in tree: - arch/um/drivers/ubd_kern.c - drivers/block/xsysace.c - drivers/usb/storage/isd200.c While at it: * Use ata_id_u{32,64}() and ata_id_has_{dma,lba,iordy}() macros. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 23 ++++++++-------- drivers/ide/ide-dma.c | 6 ++--- drivers/ide/ide-iops.c | 20 ++------------ drivers/ide/ide-lib.c | 4 +-- drivers/ide/ide-probe.c | 22 ++++++++-------- drivers/ide/ide-timings.c | 2 +- drivers/ide/ide.c | 59 ++++++++++++++++++++++++++++++++++++------ drivers/ide/legacy/qd65xx.c | 4 +-- drivers/ide/pci/cs5530.c | 2 +- drivers/ide/pci/it821x.c | 4 +-- drivers/ide/pci/pdc202xx_old.c | 2 +- drivers/ide/pci/sc1200.c | 2 +- include/linux/ide.h | 9 ++----- 13 files changed, 90 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index f1669bca3cab..8f1ec037309a 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -101,13 +101,14 @@ static void ide_disk_put(struct ide_disk_obj *idkp) */ static int lba_capacity_is_ok(u16 *id) { - struct hd_driveid *driveid = (struct hd_driveid *)id; unsigned long lba_sects, chs_sects, head, tail; /* No non-LBA info .. so valid! */ if (id[ATA_ID_CYLS] == 0) return 1; + lba_sects = ata_id_u32(id, ATA_ID_LBA_CAPACITY); + /* * The ATA spec tells large drives to return * C/H/S = 16383/16/63 independent of their size. @@ -118,10 +119,9 @@ static int lba_capacity_is_ok(u16 *id) (id[ATA_ID_CYLS] == 4092 && id[ATA_ID_CUR_CYLS] == 16383)) && id[ATA_ID_SECTORS] == 63 && (id[ATA_ID_HEADS] == 15 || id[ATA_ID_HEADS] == 16) && - (driveid->lba_capacity >= 16383 * 63 * id[ATA_ID_HEADS])) + (lba_sects >= 16383 * 63 * id[ATA_ID_HEADS])) return 1; - lba_sects = driveid->lba_capacity; chs_sects = id[ATA_ID_CYLS] * id[ATA_ID_HEADS] * id[ATA_ID_SECTORS]; /* perform a rough sanity check on lba_sects: within 10% is OK */ @@ -133,7 +133,7 @@ static int lba_capacity_is_ok(u16 *id) tail = (lba_sects & 0xffff); lba_sects = (head | (tail << 16)); if ((lba_sects - chs_sects) < chs_sects/10) { - driveid->lba_capacity = lba_sects; + *(__le32 *)&id[ATA_ID_LBA_CAPACITY] = __cpu_to_le32(lba_sects); return 1; /* lba_capacity is (now) good */ } @@ -403,7 +403,7 @@ static inline int idedisk_supports_lba48(const u16 *id) { return (id[ATA_ID_COMMAND_SET_2] & 0x0400) && (id[ATA_ID_CFS_ENABLE_2] & 0x0400) && - ((struct hd_driveid *)id)->lba_capacity_2; + ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); } /* @@ -456,7 +456,6 @@ static void idedisk_check_hpa(ide_drive_t *drive) static void init_idedisk_capacity(ide_drive_t *drive) { - struct hd_driveid *driveid = drive->driveid; u16 *id = drive->id; /* * If this drive supports the Host Protected Area feature set, @@ -467,13 +466,13 @@ static void init_idedisk_capacity(ide_drive_t *drive) if (idedisk_supports_lba48(id)) { /* drive speaks 48-bit LBA */ drive->select.b.lba = 1; - drive->capacity64 = driveid->lba_capacity_2; + drive->capacity64 = ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); if (hpa) idedisk_check_hpa(drive); - } else if ((driveid->capability & 2) && lba_capacity_is_ok(id)) { + } else if (ata_id_has_lba(id) && lba_capacity_is_ok(id)) { /* drive speaks 28-bit LBA */ drive->select.b.lba = 1; - drive->capacity64 = driveid->lba_capacity; + drive->capacity64 = ata_id_u32(id, ATA_ID_LBA_CAPACITY); if (hpa) idedisk_check_hpa(drive); } else { @@ -622,7 +621,7 @@ static int set_multcount(ide_drive_t *drive, int arg) struct request *rq; int error; - if (arg < 0 || arg > drive->driveid->max_multsect) + if (arg < 0 || arg > (drive->id[ATA_ID_MAX_MULTSECT] & 0xff)) return -EINVAL; if (drive->special.b.set_multmode) @@ -775,8 +774,8 @@ static void idedisk_add_settings(ide_drive_t *drive) ide_add_setting(drive, "address", SETTING_RW, TYPE_BYTE, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0, - drive->driveid->max_multsect, 1, 1, &drive->mult_count, - set_multcount); + drive->id[ATA_ID_MAX_MULTSECT] & 0xff, 1, 1, + &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); ide_add_setting(drive, "lun", SETTING_RW, TYPE_INT, 0, 7, 1, 1, diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index abab26de7687..15e608f52eba 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -649,8 +649,8 @@ static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) case XFER_SW_DMA_0: if (id[ATA_ID_FIELD_VALID] & 2) { mask = id[ATA_ID_SWDMA_MODES] & hwif->swdma_mask; - } else if (drive->driveid->tDMA) { - u8 mode = drive->driveid->tDMA; + } else if (id[ATA_ID_OLD_DMA_MODES] >> 8) { + u8 mode = id[ATA_ID_OLD_DMA_MODES] >> 8; /* * if the mode is valid convert it to the mask @@ -727,7 +727,7 @@ static int ide_tune_dma(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 speed; - if (drive->nodma || (drive->driveid->capability & 1) == 0) + if (drive->nodma || ata_id_has_dma(drive->id) == 0) return 0; /* consult the list of known "bad" drives */ diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 01b1943b315e..95495e4219ff 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -404,26 +404,10 @@ void ide_fix_driveid(u16 *id) { #ifndef __LITTLE_ENDIAN # ifdef __BIG_ENDIAN - struct hd_driveid *driveid = (struct hd_driveid *)id; int i; - for (i = 0; i < 256; i++) { - /* these words are accessed as two 8-bit values */ - if (i == 47 || i == 49 || i == 51 || i == 52 || i == 59) - continue; - if (i == 60 || i == 61) /* ->lba_capacity is 32-bit */ - continue; - if (i == 98 || i == 99) /* ->spg is 32-bit */ - continue; - if (i > 99 && i < 104) /* ->lba_capacity_2 is 64-bit */ - continue; - + for (i = 0; i < 256; i++) id[i] = __le16_to_cpu(id[i]); - } - - driveid->lba_capacity = __le32_to_cpu(driveid->lba_capacity); - driveid->spg = __le32_to_cpu(driveid->spg); - driveid->lba_capacity_2 = __le64_to_cpu(driveid->lba_capacity_2); # else # error "Please fix " # endif @@ -752,7 +736,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) #endif /* Skip setting PIO flow-control modes on pre-EIDE drives */ - if ((speed & 0xf8) == XFER_PIO_0 && !(drive->driveid->capability & 8)) + if ((speed & 0xf8) == XFER_PIO_0 && ata_id_has_iordy(drive->id) == 0) goto skip; /* diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 3066d7e75c73..738c007a04d3 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -102,14 +102,14 @@ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode) if (pio_mode != -1) { printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name); } else { - pio_mode = drive->driveid->tPIO; + pio_mode = id[ATA_ID_OLD_PIO_MODES] >> 8; if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */ pio_mode = 2; overridden = 1; } if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */ - if (drive->driveid->capability & 8) { /* IORDY sup? */ + if (ata_id_has_iordy(id)) { if (id[ATA_ID_PIO_MODES] & 7) { overridden = 0; if (id[ATA_ID_PIO_MODES] & 4) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index b4f8ca106639..1bb4b2c0e2f4 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -87,20 +87,20 @@ static void ide_disk_init_chs(ide_drive_t *drive) static void ide_disk_init_mult_count(ide_drive_t *drive) { - struct hd_driveid *id = drive->driveid; + u16 *id = drive->id; + u8 max_multsect = id[ATA_ID_MAX_MULTSECT] & 0xff; - if (id->max_multsect) { + if (max_multsect) { #ifdef CONFIG_IDEDISK_MULTI_MODE - if ((id->max_multsect / 2) > 1) { - id->multsect = id->max_multsect; - id->multsect_valid = 1; - } else { - id->multsect = 0; - id->multsect_valid = 0; - } - drive->mult_req = id->multsect; + if ((max_multsect / 2) > 1) + id[ATA_ID_MULTSECT] = max_multsect | 0x100; + else + id[ATA_ID_MULTSECT] &= ~0x1ff; + + drive->mult_req = id[ATA_ID_MULTSECT] & 0xff; #endif - if ((id->multsect_valid & 1) && id->multsect) + if ((id[ATA_ID_MULTSECT] & 0x100) && + (id[ATA_ID_MULTSECT] & 0xff)) drive->special.b.set_multmode = 1; } } diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c index d64f345f2fc0..96e3d467a74c 100644 --- a/drivers/ide/ide-timings.c +++ b/drivers/ide/ide-timings.c @@ -83,7 +83,7 @@ u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio) u16 cycle = 0; if (id[ATA_ID_FIELD_VALID] & 2) { - if (drive->driveid->capability & 8) + if (ata_id_has_iordy(drive->id)) cycle = id[ATA_ID_EIDE_PIO_IORDY]; else cycle = id[ATA_ID_EIDE_PIO]; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 8d3fab33a3c7..21b3a767e7d7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -328,7 +328,7 @@ int set_using_dma(ide_drive_t *drive, int arg) if (arg < 0 || arg > 1) return -EINVAL; - if ((drive->driveid->capability & 1) == 0) + if (ata_id_has_dma(drive->id) == 0) goto out; if (hwif->dma_ops == NULL) @@ -502,12 +502,60 @@ static int generic_drive_reset(ide_drive_t *drive) return ret; } +static inline void ide_id_to_hd_driveid(u16 *id) +{ +#ifdef __BIG_ENDIAN + /* accessed in struct hd_driveid as 8-bit values */ + id[ATA_ID_MAX_MULTSECT] = __cpu_to_le16(id[ATA_ID_MAX_MULTSECT]); + id[ATA_ID_CAPABILITY] = __cpu_to_le16(id[ATA_ID_CAPABILITY]); + id[ATA_ID_OLD_PIO_MODES] = __cpu_to_le16(id[ATA_ID_OLD_PIO_MODES]); + id[ATA_ID_OLD_DMA_MODES] = __cpu_to_le16(id[ATA_ID_OLD_DMA_MODES]); + id[ATA_ID_MULTSECT] = __cpu_to_le16(id[ATA_ID_MULTSECT]); + + /* as 32-bit values */ + *(u32 *)&id[ATA_ID_LBA_CAPACITY] = ata_id_u32(id, ATA_ID_LBA_CAPACITY); + *(u32 *)&id[ATA_ID_SPG] = ata_id_u32(id, ATA_ID_SPG); + + /* as 64-bit value */ + *(u64 *)&id[ATA_ID_LBA_CAPACITY_2] = + ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); +#endif +} + +static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, + unsigned long arg) +{ + u16 *id = NULL; + int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142; + int rc = 0; + + if (drive->id_read == 0) { + rc = -ENOMSG; + goto out; + } + + id = kmalloc(size, GFP_KERNEL); + if (id == NULL) { + rc = -ENOMEM; + goto out; + } + + memcpy(id, drive->id, size); + ide_id_to_hd_driveid(id); + + if (copy_to_user((void __user *)arg, id, size)) + rc = -EFAULT; + + kfree(id); +out: + return rc; +} + int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device *bdev, unsigned int cmd, unsigned long arg) { unsigned long flags; ide_driver_t *drv; - void __user *p = (void __user *)arg; int err = 0, (*setfunc)(ide_drive_t *, int); u8 *val; @@ -528,12 +576,7 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device case HDIO_GET_IDENTITY: if (bdev != bdev->bd_contains) return -EINVAL; - if (drive->id_read == 0) - return -ENOMSG; - if (copy_to_user(p, drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142)) - return -EFAULT; - return 0; - + return ide_get_identity_ioctl(drive, cmd, arg); case HDIO_GET_NICE: return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP | drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP | diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index ef4e84053a81..6d7f548655e4 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -195,10 +195,10 @@ static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio) * FIXME: use "pio" value */ if (!qd_find_disk_type(drive, &active_time, &recovery_time) && - drive->driveid->tPIO && (id[ATA_ID_FIELD_VALID] & 2) && + (id[ATA_ID_OLD_PIO_MODES] & 0xff) && (id[ATA_ID_FIELD_VALID] & 2) && id[ATA_ID_EIDE_PIO] >= 240) { printk(KERN_INFO "%s: PIO mode%d\n", drive->name, - drive->driveid->tPIO); + id[ATA_ID_OLD_PIO_MODES] & 0xff); active_time = 110; recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120; } diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index 774ff58603a2..ef91e9d7c54f 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -88,7 +88,7 @@ static u8 cs5530_udma_filter(ide_drive_t *drive) if (mate->present == 0) goto out; - if ((mate->driveid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { + if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) { if ((mateid[ATA_ID_FIELD_VALID] & 4) && (mateid[ATA_ID_UDMA_MODES] & 7)) goto out; diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 31d4e6aef1b8..ed24065f74e8 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -470,7 +470,7 @@ static void it821x_quirkproc(ide_drive_t *drive) /* In raid mode the ident block is slightly buggy We need to set the bits so that the IDE layer knows LBA28. LBA48 and DMA ar valid */ - drive->driveid->capability |= 3; /* LBA28, DMA */ + id[ATA_ID_CAPABILITY] |= (3 << 8); /* LBA28, DMA */ id[ATA_ID_COMMAND_SET_2] |= 0x0400; /* LBA48 valid */ id[ATA_ID_CFS_ENABLE_2] |= 0x0400; /* LBA48 on */ /* Reporting logic */ @@ -504,7 +504,7 @@ static void it821x_quirkproc(ide_drive_t *drive) * IDE core that DMA is supported (it821x hardware * takes care of DMA mode programming). */ - if (drive->driveid->capability & 1) { + if (ata_id_has_dma(id)) { id[ATA_ID_MWDMA_MODES] |= 0x0101; drive->current_speed = XFER_MW_DMA_0; } diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 23e861b177ce..5d4436f3efd4 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -86,7 +86,7 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed) * Prefetch_EN / IORDY_EN / PA[3:0] bits of register A */ AP &= ~0x3f; - if (drive->driveid->capability & 4) + if (ata_id_iordy_disable(drive->id)) AP |= 0x20; /* set IORDY_EN bit */ if (drive->media == ide_disk) AP |= 0x10; /* set Prefetch_EN bit */ diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 5c8367df14e4..695cc9742048 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -111,7 +111,7 @@ static u8 sc1200_udma_filter(ide_drive_t *drive) if (mate->present == 0) goto out; - if ((mate->driveid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { + if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) { if ((mateid[ATA_ID_FIELD_VALID] & 4) && (mateid[ATA_ID_UDMA_MODES] & 7)) goto out; diff --git a/include/linux/ide.h b/include/linux/ide.h index 0c85aff3edf1..e887927e00e6 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -380,11 +380,7 @@ struct ide_drive_s { struct request *rq; /* current request */ struct ide_drive_s *next; /* circular list of hwgroup drives */ void *driver_data; /* extra driver data */ - union { - /* identification info */ - struct hd_driveid *driveid; - u16 *id; - }; + u16 *id; /* identification info */ #ifdef CONFIG_IDE_PROC_FS struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ struct ide_settings_s *settings;/* /proc/ide/ drive settings */ @@ -1353,8 +1349,7 @@ extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate); static inline int ide_dev_has_iordy(u16 *id) { - return ((id[ATA_ID_FIELD_VALID] & 2) && - (((struct hd_driveid *)id)->capability & 8)) ? 1 : 0; + return ((id[ATA_ID_FIELD_VALID] & 2) && ata_id_has_iordy(id)) ? 1 : 0; } static inline int ide_dev_is_sata(u16 *id) -- cgit v1.2.3 From 3a7d24841ad794ae64c90d7d00d62a83741912aa Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:21 +0200 Subject: ide: use ATA_* defines instead of *_STAT and *_ERR ones * ERR_STAT -> ATA_ERR * INDEX_STAT -> ATA_IDX * ECC_STAT -> ATA_CORR * DRQ_STAT -> ATA_DRQ * SEEK_STAT -> ATA_DSC * WRERR_STAT -> ATA_DF * READY_STAT -> ATA_DRDY * BUSY_STAT -> ATA_BUSY * MARK_ERR -> ATA_AMNF * TRK0_ERR -> ATA_TRK0NF * ABRT_ERR -> ATA_ABORTED * MCR_ERR -> ATA_MCR * ID_ERR -> ATA_IDNF * MC_ERR -> ATA_MC * ECC_ERR -> ATA_UNC * ICRC_ERR -> ATA_ICRC * BBD_ERR -> ATA_BBK Also: * ILI_ERR -> ATAPI_ILI * EOM_ERR -> ATAPI_EOM * LFS_ERR -> ATAPI_LFS * CD -> ATAPI_COD * IO -> ATAPI_IO Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 32 ++++++++++++++++--------------- drivers/ide/ide-cd.c | 16 ++++++++-------- drivers/ide/ide-dma.c | 2 +- drivers/ide/ide-floppy.c | 2 +- drivers/ide/ide-io.c | 25 ++++++++++++------------ drivers/ide/ide-iops.c | 22 ++++++++++----------- drivers/ide/ide-lib.c | 48 +++++++++++++++++++++++----------------------- drivers/ide/ide-probe.c | 16 ++++++++-------- drivers/ide/ide-tape.c | 6 +++--- drivers/ide/ide-taskfile.c | 26 ++++++++++++------------- drivers/ide/ide.c | 8 ++++---- drivers/ide/pci/ns87415.c | 6 +++--- drivers/ide/pci/scc_pata.c | 4 ++-- drivers/scsi/ide-scsi.c | 2 +- include/linux/ide.h | 15 ++++++++------- 15 files changed, 117 insertions(+), 113 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2433fce6c111..12674e6519e6 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -41,7 +41,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || - (drive->media == ide_tape && !scsi && (stat & ERR_STAT))) { + (drive->media == ide_tape && !scsi && (stat & ATA_ERR))) { if (drive->media == ide_floppy && !scsi) printk(KERN_ERR "%s: DMA %s error\n", drive->name, rq_data_dir(pc->rq) @@ -56,7 +56,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* No more interrupts */ - if ((stat & DRQ_STAT) == 0) { + if ((stat & ATA_DRQ) == 0) { debug_log("Packet command completed, %d bytes transferred\n", pc->xferred); @@ -65,10 +65,10 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, local_irq_enable_in_hardirq(); if (drive->media == ide_tape && !scsi && - (stat & ERR_STAT) && rq->cmd[0] == REQUEST_SENSE) - stat &= ~ERR_STAT; + (stat & ATA_ERR) && rq->cmd[0] == REQUEST_SENSE) + stat &= ~ATA_ERR; - if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) { + if ((stat & ATA_ERR) || (pc->flags & PC_FLAG_DMA_ERROR)) { /* Error detected */ debug_log("%s: I/O error\n", drive->name); @@ -95,7 +95,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, cmd_finished: pc->error = 0; if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && - (stat & SEEK_STAT) == 0) { + (stat & ATA_DSC) == 0) { dsc_handle(drive); return ide_stopped; } @@ -117,17 +117,18 @@ cmd_finished: /* Get the number of bytes to transfer on this interrupt. */ ide_read_bcount_and_ireason(drive, &bcount, &ireason); - if (ireason & CD) { + if (ireason & ATAPI_COD) { printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); return ide_do_reset(drive); } - if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) { + if (((ireason & ATAPI_IO) == ATAPI_IO) == + !!(pc->flags & PC_FLAG_WRITING)) { /* Hopefully, we will never get here */ printk(KERN_ERR "%s: We wanted to %s, but the device wants us " "to %s!\n", drive->name, - (ireason & IO) ? "Write" : "Read", - (ireason & IO) ? "Read" : "Write"); + (ireason & ATAPI_IO) ? "Write" : "Read", + (ireason & ATAPI_IO) ? "Read" : "Write"); return ide_do_reset(drive); } @@ -205,7 +206,8 @@ static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) { int retries = 100; - while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) { + while (retries-- && ((ireason & ATAPI_COD) == 0 || + (ireason & ATAPI_IO))) { printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " "a packet command, retrying\n", drive->name); udelay(100); @@ -214,8 +216,8 @@ static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " "a packet command, ignoring\n", drive->name); - ireason |= CD; - ireason &= ~IO; + ireason |= ATAPI_COD; + ireason &= ~ATAPI_IO; } } @@ -231,7 +233,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_startstop_t startstop; u8 ireason; - if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) { + if (ide_wait_stat(&startstop, drive, ATA_DRQ, ATA_BUSY, WAIT_READY)) { printk(KERN_ERR "%s: Strange, packet command initiated yet " "DRQ isn't asserted\n", drive->name); return startstop; @@ -241,7 +243,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, if (drive->media == ide_tape && !drive->scsi) ireason = ide_wait_ireason(drive, ireason); - if ((ireason & CD) == 0 || (ireason & IO)) { + if ((ireason & ATAPI_COD) == 0 || (ireason & ATAPI_IO)) { printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " "a packet command\n", drive->name); return ide_do_reset(drive); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 77e0f9ab7698..239557f8920e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -436,7 +436,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) ide_dump_status_no_sense(drive, "media error (blank)", stat); do_end_request = 1; - } else if ((err & ~ABRT_ERR) != 0) { + } else if ((err & ~ATA_ABORTED) != 0) { /* go to the default handler for other errors */ ide_error(drive, "cdrom_decode_status", stat); return 1; @@ -457,7 +457,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) * If we got a CHECK_CONDITION status, queue * a request sense command. */ - if (stat & ERR_STAT) + if (stat & ATA_ERR) cdrom_queue_request_sense(drive, NULL, NULL); } else { blk_dump_rq_flags(rq, "ide-cd: bad rq"); @@ -468,7 +468,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) return 1; end_request: - if (stat & ERR_STAT) { + if (stat & ATA_ERR) { unsigned long flags; spin_lock_irqsave(&ide_lock, flags); @@ -574,7 +574,7 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, */ /* check for errors */ - if (cdrom_decode_status(drive, DRQ_STAT, NULL)) + if (cdrom_decode_status(drive, ATA_DRQ, NULL)) return ide_stopped; /* ok, next interrupt will be DMA interrupt */ @@ -582,8 +582,8 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, drive->waiting_for_dma = 1; } else { /* otherwise, we must wait for DRQ to get set */ - if (ide_wait_stat(&startstop, drive, DRQ_STAT, - BUSY_STAT, WAIT_READY)) + if (ide_wait_stat(&startstop, drive, ATA_DRQ, + ATA_BUSY, WAIT_READY)) return startstop; } @@ -938,7 +938,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) thislen = len; /* If DRQ is clear, the command has completed. */ - if ((stat & DRQ_STAT) == 0) { + if ((stat & ATA_DRQ) == 0) { if (blk_fs_request(rq)) { /* * If we're not done reading/writing, complain. @@ -1206,7 +1206,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, unsigned long elapsed = jiffies - info->start_seek; int stat = hwif->tp_ops->read_status(hwif); - if ((stat & SEEK_STAT) != SEEK_STAT) { + if ((stat & ATA_DSC) != ATA_DSC) { if (elapsed < IDECD_SEEK_TIMEOUT) { ide_stall_queue(drive, IDECD_SEEK_TIMER); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 15e608f52eba..ef2f1504c0d5 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -106,7 +106,7 @@ ide_startstop_t ide_dma_intr (ide_drive_t *drive) dma_stat = hwif->dma_ops->dma_end(drive); stat = hwif->tp_ops->read_status(hwif); - if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) { if (!dma_stat) { struct request *rq = HWGROUP(drive)->rq; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 67f93a46f510..59baa9643f8b 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -945,7 +945,7 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) stat = hwif->tp_ops->read_status(hwif); local_irq_restore(flags); - progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000; + progress_indication = ((stat & ATA_DSC) == 0) ? 0 : 0x10000; } if (put_user(progress_indication, arg)) return (-EFAULT); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ce9ecd138836..8dd7b46b41b7 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -322,7 +322,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) ide_task_t *task = (ide_task_t *)rq->special; if (rq->errors == 0) - rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT); + rq->errors = !OK_STAT(stat, ATA_DRDY, BAD_STAT); if (task) { struct ide_taskfile *tf = &task->tf; @@ -373,12 +373,12 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 { ide_hwif_t *hwif = drive->hwif; - if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { + if ((stat & ATA_BUSY) || ((stat & ATA_DF) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; - } else if (stat & ERR_STAT) { + } else if (stat & ATA_ERR) { /* err has different meaning on cdrom and tape */ - if (err == ABRT_ERR) { + if (err == ATA_ABORTED) { if (drive->select.b.lba && /* some newer drives don't support ATA_CMD_INIT_DEV_PARAMS */ hwif->tp_ops->read_status(hwif) == ATA_CMD_INIT_DEV_PARAMS) @@ -386,16 +386,16 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 } else if ((err & BAD_CRC) == BAD_CRC) { /* UDMA crc error, just retry the operation */ drive->crc_count++; - } else if (err & (BBD_ERR | ECC_ERR)) { + } else if (err & (ATA_BBK | ATA_UNC)) { /* retries won't help these */ rq->errors = ERROR_MAX; - } else if (err & TRK0_ERR) { + } else if (err & ATA_TRK0NF) { /* help it find track zero */ rq->errors |= ERROR_RECAL; } } - if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ && + if ((stat & ATA_DRQ) && rq_data_dir(rq) == READ && (hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0) { int nsect = drive->mult_count ? drive->mult_count : 1; @@ -407,7 +407,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 return ide_stopped; } - if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ)) rq->errors |= ERROR_RESET; if ((rq->errors & ERROR_RESET) == ERROR_RESET) { @@ -427,14 +427,14 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u { ide_hwif_t *hwif = drive->hwif; - if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { + if ((stat & ATA_BUSY) || ((stat & ATA_DF) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else { /* add decoding error stuff */ } - if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ)) /* force an abort */ hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE); @@ -804,7 +804,8 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) ide_check_pm_state(drive, rq); SELECT_DRIVE(drive); - if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { + if (ide_wait_stat(&startstop, drive, drive->ready_stat, + ATA_BUSY | ATA_DRQ, WAIT_READY)) { printk(KERN_ERR "%s: drive not ready for command\n", drive->name); return startstop; } @@ -1324,7 +1325,7 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) if (hwif->irq == irq) { stat = hwif->tp_ops->read_status(hwif); - if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { /* Try to not flood the console with msgs */ static unsigned long last_msgtime, count; ++count; diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 21647a2eaff9..8cfa6125c7a4 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -473,7 +473,7 @@ int drive_is_ready (ide_drive_t *drive) /* Note: this may clear a pending IRQ!! */ stat = hwif->tp_ops->read_status(hwif); - if (stat & BUSY_STAT) + if (stat & ATA_BUSY) /* drive busy: definitely not interrupting */ return 0; @@ -505,10 +505,10 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti udelay(1); /* spec allows drive 400ns to assert "BUSY" */ stat = tp_ops->read_status(hwif); - if (stat & BUSY_STAT) { + if (stat & ATA_BUSY) { local_irq_set(flags); timeout += jiffies; - while ((stat = tp_ops->read_status(hwif)) & BUSY_STAT) { + while ((stat = tp_ops->read_status(hwif)) & ATA_BUSY) { if (time_after(jiffies, timeout)) { /* * One last read after the timeout in case @@ -516,7 +516,7 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti * progress during the timeout.. */ stat = tp_ops->read_status(hwif); - if (!(stat & BUSY_STAT)) + if ((stat & ATA_BUSY) == 0) break; local_irq_restore(flags); @@ -685,12 +685,12 @@ int ide_driveid_update(ide_drive_t *drive) msleep(50); /* give drive a breather */ stat = tp_ops->read_altstatus(hwif); - } while (stat & BUSY_STAT); + } while (stat & ATA_BUSY); - msleep(50); /* wait for IRQ and DRQ_STAT */ + msleep(50); /* wait for IRQ and ATA_DRQ */ stat = tp_ops->read_status(hwif); - if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { + if (!OK_STAT(stat, ATA_DRQ, BAD_R_STAT)) { SELECT_MASK(drive, 0); printk("%s: CHECK for good STATUS\n", drive->name); return 0; @@ -776,7 +776,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) tp_ops->set_irq(hwif, 1); error = __ide_wait_stat(drive, drive->ready_stat, - BUSY_STAT|DRQ_STAT|ERR_STAT, + ATA_BUSY | ATA_DRQ | ATA_ERR, WAIT_CMD, &stat); SELECT_MASK(drive, 0); @@ -923,7 +923,7 @@ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) udelay (10); stat = hwif->tp_ops->read_status(hwif); - if (OK_STAT(stat, 0, BUSY_STAT)) + if (OK_STAT(stat, 0, ATA_BUSY)) printk("%s: ATAPI reset complete\n", drive->name); else { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -969,7 +969,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) tmp = hwif->tp_ops->read_status(hwif); - if (!OK_STAT(tmp, 0, BUSY_STAT)) { + if (!OK_STAT(tmp, 0, ATA_BUSY)) { if (time_before(jiffies, hwgroup->poll_timeout)) { ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL); /* continue polling */ @@ -1183,7 +1183,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) */ mdelay(1); stat = hwif->tp_ops->read_status(hwif); - if ((stat & BUSY_STAT) == 0) + if ((stat & ATA_BUSY) == 0) return 0; /* * Assume a value of 0xff means nothing is connected to diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 738c007a04d3..c5c37bfd8b09 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -340,16 +340,16 @@ static void ide_dump_sector(ide_drive_t *drive) static void ide_dump_ata_error(ide_drive_t *drive, u8 err) { printk("{ "); - if (err & ABRT_ERR) printk("DriveStatusError "); - if (err & ICRC_ERR) - printk((err & ABRT_ERR) ? "BadCRC " : "BadSector "); - if (err & ECC_ERR) printk("UncorrectableError "); - if (err & ID_ERR) printk("SectorIdNotFound "); - if (err & TRK0_ERR) printk("TrackZeroNotFound "); - if (err & MARK_ERR) printk("AddrMarkNotFound "); + if (err & ATA_ABORTED) printk("DriveStatusError "); + if (err & ATA_ICRC) + printk((err & ATA_ABORTED) ? "BadCRC " : "BadSector "); + if (err & ATA_UNC) printk("UncorrectableError "); + if (err & ATA_IDNF) printk("SectorIdNotFound "); + if (err & ATA_TRK0NF) printk("TrackZeroNotFound "); + if (err & ATA_AMNF) printk("AddrMarkNotFound "); printk("}"); - if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || - (err & (ECC_ERR|ID_ERR|MARK_ERR))) { + if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK || + (err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) { ide_dump_sector(drive); if (HWGROUP(drive) && HWGROUP(drive)->rq) printk(", sector=%llu", @@ -361,12 +361,12 @@ static void ide_dump_ata_error(ide_drive_t *drive, u8 err) static void ide_dump_atapi_error(ide_drive_t *drive, u8 err) { printk("{ "); - if (err & ILI_ERR) printk("IllegalLengthIndication "); - if (err & EOM_ERR) printk("EndOfMedia "); - if (err & ABRT_ERR) printk("AbortedCommand "); - if (err & MCR_ERR) printk("MediaChangeRequested "); - if (err & LFS_ERR) printk("LastFailedSense=0x%02x ", - (err & LFS_ERR) >> 4); + if (err & ATAPI_ILI) printk("IllegalLengthIndication "); + if (err & ATAPI_EOM) printk("EndOfMedia "); + if (err & ATA_ABORTED) printk("AbortedCommand "); + if (err & ATA_MCR) printk("MediaChangeRequested "); + if (err & ATAPI_LFS) printk("LastFailedSense=0x%02x ", + (err & ATAPI_LFS) >> 4); printk("}\n"); } @@ -388,19 +388,19 @@ u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat) local_irq_save(flags); printk("%s: %s: status=0x%02x { ", drive->name, msg, stat); - if (stat & BUSY_STAT) + if (stat & ATA_BUSY) printk("Busy "); else { - if (stat & READY_STAT) printk("DriveReady "); - if (stat & WRERR_STAT) printk("DeviceFault "); - if (stat & SEEK_STAT) printk("SeekComplete "); - if (stat & DRQ_STAT) printk("DataRequest "); - if (stat & ECC_STAT) printk("CorrectedError "); - if (stat & INDEX_STAT) printk("Index "); - if (stat & ERR_STAT) printk("Error "); + if (stat & ATA_DRDY) printk("DriveReady "); + if (stat & ATA_DF) printk("DeviceFault "); + if (stat & ATA_DSC) printk("SeekComplete "); + if (stat & ATA_DRQ) printk("DataRequest "); + if (stat & ATA_CORR) printk("CorrectedError "); + if (stat & ATA_IDX) printk("Index "); + if (stat & ATA_ERR) printk("Error "); } printk("}\n"); - if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + if ((stat & (ATA_BUSY | ATA_ERR)) == ATA_ERR) { err = ide_read_error(drive); printk("%s: %s: error=0x%02x ", drive->name, msg, err); if (drive->media == ide_disk) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 4829daacb342..7578ad48080f 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -264,7 +264,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) if (io_ports->ctl_addr) { a = tp_ops->read_altstatus(hwif); s = tp_ops->read_status(hwif); - if ((a ^ s) & ~INDEX_STAT) + if ((a ^ s) & ~ATA_IDX) /* ancient Seagate drives, broken interfaces */ printk(KERN_INFO "%s: probing with STATUS(0x%02x) " "instead of ALTSTATUS(0x%02x)\n", @@ -301,13 +301,13 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) msleep(50); s = use_altstatus ? tp_ops->read_altstatus(hwif) : tp_ops->read_status(hwif); - } while (s & BUSY_STAT); + } while (s & ATA_BUSY); - /* wait for IRQ and DRQ_STAT */ + /* wait for IRQ and ATA_DRQ */ msleep(50); s = tp_ops->read_status(hwif); - if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) { + if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) { unsigned long flags; /* local CPU only; some systems need this */ @@ -391,7 +391,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) do { msleep(50); stat = hwif->tp_ops->read_status(hwif); - if ((stat & BUSY_STAT) == 0) + if ((stat & ATA_BUSY) == 0) return 0; } while (time_before(jiffies, timeout)); @@ -460,7 +460,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) if (drive->select.b.unit != 0) { /* exit with drive0 selected */ SELECT_DRIVE(&hwif->drives[0]); - /* allow BUSY_STAT to assert & clear */ + /* allow ATA_BUSY to assert & clear */ msleep(50); } /* no i/f present: mmm.. this should be a 4 -ml */ @@ -469,7 +469,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) stat = tp_ops->read_status(hwif); - if (OK_STAT(stat, READY_STAT, BUSY_STAT) || + if (OK_STAT(stat, ATA_DRDY, ATA_BUSY) || drive->present || cmd == ATA_CMD_ID_ATAPI) { /* send cmd and wait */ if ((rc = try_to_identify(drive, cmd))) { @@ -479,7 +479,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) stat = tp_ops->read_status(hwif); - if (stat == (BUSY_STAT | READY_STAT)) + if (stat == (ATA_BUSY | ATA_DRDY)) return 4; if (rc == 1 && cmd == ATA_CMD_ID_ATAPI) { diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 2c4c6674db61..2745e5d26848 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -920,8 +920,8 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) stat = hwif->tp_ops->read_status(hwif); - if (stat & SEEK_STAT) { - if (stat & ERR_STAT) { + if (stat & ATA_DSC) { + if (stat & ATA_ERR) { /* Error detected */ if (pc->c[0] != TEST_UNIT_READY) printk(KERN_ERR "ide-tape: %s: I/O error, ", @@ -1022,7 +1022,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, } if (!test_and_clear_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags) && - (stat & SEEK_STAT) == 0) { + (stat & ATA_DSC) == 0) { if (postponed_rq == NULL) { tape->dsc_polling_start = jiffies; tape->dsc_poll_freq = tape->best_dsc_rw_freq; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 9224f6797186..b1fb815dbf68 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -136,7 +136,7 @@ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) local_irq_enable_in_hardirq(); stat = hwif->tp_ops->read_status(hwif); - if (OK_STAT(stat, READY_STAT, BAD_STAT)) + if (OK_STAT(stat, ATA_DRDY, BAD_STAT)) drive->mult_count = drive->mult_req; else { drive->mult_req = drive->mult_count = 0; @@ -159,15 +159,15 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) while (1) { stat = hwif->tp_ops->read_status(hwif); - if ((stat & BUSY_STAT) == 0 || retries-- == 0) + if ((stat & ATA_BUSY) == 0 || retries-- == 0) break; udelay(10); }; - if (OK_STAT(stat, READY_STAT, BAD_STAT)) + if (OK_STAT(stat, ATA_DRDY, BAD_STAT)) return ide_stopped; - if (stat & (ERR_STAT|DRQ_STAT)) + if (stat & (ATA_ERR | ATA_DRQ)) return ide_error(drive, "set_geometry_intr", stat); ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL); @@ -185,7 +185,7 @@ static ide_startstop_t recal_intr(ide_drive_t *drive) local_irq_enable_in_hardirq(); stat = hwif->tp_ops->read_status(hwif); - if (!OK_STAT(stat, READY_STAT, BAD_STAT)) + if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) return ide_error(drive, "recal_intr", stat); return ide_stopped; } @@ -202,7 +202,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) local_irq_enable_in_hardirq(); stat = hwif->tp_ops->read_status(hwif); - if (!OK_STAT(stat, READY_STAT, BAD_STAT)) + if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); /* calls ide_end_drive_cmd */ @@ -225,13 +225,13 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) for (retries = 0; retries < 1000; retries++) { stat = hwif->tp_ops->read_status(hwif); - if (stat & BUSY_STAT) + if (stat & ATA_BUSY) udelay(10); else break; } - if (stat & BUSY_STAT) + if (stat & ATA_BUSY) printk(KERN_ERR "%s: drive still BUSY!\n", drive->name); return stat; @@ -390,7 +390,7 @@ void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq, u8 stat) { /* Command all done? */ - if (OK_STAT(stat, READY_STAT, BUSY_STAT)) { + if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) { task_end_request(drive, rq, stat); return ide_stopped; } @@ -410,11 +410,11 @@ static ide_startstop_t task_in_intr(ide_drive_t *drive) u8 stat = hwif->tp_ops->read_status(hwif); /* Error? */ - if (stat & ERR_STAT) + if (stat & ATA_ERR) return task_error(drive, rq, __func__, stat); /* Didn't want any data? Odd. */ - if (!(stat & DRQ_STAT)) + if ((stat & ATA_DRQ) == 0) return task_in_unexpected(drive, rq, stat); ide_pio_datablock(drive, rq, 0); @@ -447,7 +447,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive) return task_error(drive, rq, __func__, stat); /* Deal with unexpected ATA data phase. */ - if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft) + if (((stat & ATA_DRQ) == 0) ^ !hwif->nleft) return task_error(drive, rq, __func__, stat); if (!hwif->nleft) { @@ -466,7 +466,7 @@ static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq) { ide_startstop_t startstop; - if (ide_wait_stat(&startstop, drive, DRQ_STAT, + if (ide_wait_stat(&startstop, drive, ATA_DRQ, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n", drive->name, diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 21b3a767e7d7..7fddfe161795 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -119,7 +119,7 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif) drive->media = ide_disk; drive->select.all = (unit<<4)|0xa0; drive->hwif = hwif; - drive->ready_stat = READY_STAT; + drive->ready_stat = ATA_DRDY; drive->bad_wstat = BAD_W_STAT; drive->special.b.recalibrate = 1; drive->special.b.set_geometry = 1; @@ -884,7 +884,7 @@ MODULE_PARM_DESC(noprobe, "skip probing for a device"); static unsigned int ide_nowerr; module_param_call(nowerr, ide_set_dev_param_mask, NULL, &ide_nowerr, 0); -MODULE_PARM_DESC(nowerr, "ignore the WRERR_STAT bit for a device"); +MODULE_PARM_DESC(nowerr, "ignore the ATA_DF bit for a device"); static unsigned int ide_cdroms; @@ -949,7 +949,7 @@ static void ide_dev_apply_params(ide_drive_t *drive) drive->noprobe = 1; } if (ide_nowerr & (1 << i)) { - printk(KERN_INFO "ide: ignoring the WRERR_STAT bit for %s\n", + printk(KERN_INFO "ide: ignoring the ATA_DF bit for %s\n", drive->name); drive->bad_wstat = BAD_R_STAT; } @@ -970,7 +970,7 @@ static void ide_dev_apply_params(ide_drive_t *drive) drive->cyl, drive->head, drive->sect); drive->present = 1; drive->media = ide_disk; - drive->ready_stat = READY_STAT; + drive->ready_stat = ATA_DRDY; } } diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index ffefcd15196c..17685f0ade13 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -274,9 +274,9 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) do { udelay(50); stat = hwif->tp_ops->read_status(hwif); - if (stat == 0xff) - break; - } while ((stat & BUSY_STAT) && --timeout); + if (stat == 0xff) + break; + } while ((stat & ATA_BUSY) && --timeout); #endif } diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 44cccd1e086a..192a6a70a631 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -400,7 +400,7 @@ static int scc_dma_end(ide_drive_t *drive) /* errata A308 workaround: Step5 (check data loss) */ /* We don't check non ide_disk because it is limited to UDMA4 */ if (!(in_be32((void __iomem *)hwif->io_ports.ctl_addr) - & ERR_STAT) && + & ATA_ERR) && drive->media == ide_disk && drive->current_speed > XFER_UDMA_4) { reg = in_be32((void __iomem *)intsts_port); if (!(reg & INTSTS_ACTEINT)) { @@ -504,7 +504,7 @@ static int scc_dma_test_irq(ide_drive_t *drive) /* SCC errata A252,A308 workaround: Step4 */ if ((in_be32((void __iomem *)hwif->io_ports.ctl_addr) - & ERR_STAT) && + & ATA_ERR) && (int_stat & INTSTS_INTRQ)) return 1; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 167c3b625218..daa5f3115ddc 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -244,7 +244,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { ide_hwif_t *hwif = drive->hwif; - if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ)) /* force an abort */ hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE); diff --git a/include/linux/ide.h b/include/linux/ide.h index e887927e00e6..d963c1929c84 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -87,12 +87,13 @@ struct ide_io_ports { }; #define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good)) -#define BAD_R_STAT (BUSY_STAT | ERR_STAT) -#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT) -#define BAD_STAT (BAD_R_STAT | DRQ_STAT) -#define DRIVE_READY (READY_STAT | SEEK_STAT) -#define BAD_CRC (ABRT_ERR | ICRC_ERR) +#define BAD_R_STAT (ATA_BUSY | ATA_ERR) +#define BAD_W_STAT (BAD_R_STAT | ATA_DF) +#define BAD_STAT (BAD_R_STAT | ATA_DRQ) +#define DRIVE_READY (ATA_DRDY | ATA_DSC) + +#define BAD_CRC (ATA_ABORTED | ATA_ICRC) #define SATA_NR_PORTS (3) /* 16 possible ?? */ @@ -438,8 +439,8 @@ struct ide_drive_s { u8 mult_req; /* requested multiple sector setting */ u8 tune_req; /* requested drive tuning setting */ u8 io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ - u8 bad_wstat; /* used for ignoring WRERR_STAT */ - u8 nowerr; /* used for ignoring WRERR_STAT */ + u8 bad_wstat; /* used for ignoring ATA_DF */ + u8 nowerr; /* used for ignoring ATA_DF */ u8 sect0; /* offset of first sector for DM6:DDO */ u8 head; /* "real" number of heads */ u8 sect; /* "real" sectors per track */ -- cgit v1.2.3 From 3c619ffd48d7fdb3b17f0df67c4eb4b0bd80e253 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:22 +0200 Subject: ide: remove no longer needed ide_drive_t fields Remove ->remap_0_to_1 and ->sect0 (they are always zero now). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 2 +- drivers/ide/ide-io.c | 18 +++--------------- include/linux/ide.h | 2 -- 3 files changed, 4 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 83da9a98dabc..d4d730252669 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -472,7 +472,7 @@ static void init_idedisk_capacity(ide_drive_t *drive) static sector_t idedisk_capacity(ide_drive_t *drive) { - return drive->capacity64 - drive->sect0; + return drive->capacity64; } #ifdef CONFIG_IDE_PROC_FS diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 8dd7b46b41b7..96975e24a0cc 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -765,9 +765,7 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) * start_request - start of I/O and command issuing for IDE * * start_request() initiates handling of a new I/O request. It - * accepts commands and I/O (read/write) requests. It also does - * the final remapping for weird stuff like EZDrive. Once - * device mapper can work sector level the EZDrive stuff can go away + * accepts commands and I/O (read/write) requests. * * FIXME: this function needs a rename */ @@ -775,7 +773,6 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) { ide_startstop_t startstop; - sector_t block; BUG_ON(!blk_rq_started(rq)); @@ -790,16 +787,6 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) goto kill_rq; } - block = rq->sector; - if (blk_fs_request(rq) && - (drive->media == ide_disk || drive->media == ide_floppy)) { - block += drive->sect0; - } - /* Yecch - this will shift the entire interval, - possibly killing some innocent following sector */ - if (block == 0 && drive->remap_0_to_1 == 1) - block = 1; /* redirect MBR access to EZ-Drive partn table */ - if (blk_pm_request(rq)) ide_check_pm_state(drive, rq); @@ -844,7 +831,8 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) return ide_special_rq(drive, rq); drv = *(ide_driver_t **)rq->rq_disk->private_data; - return drv->do_request(drive, rq, block); + + return drv->do_request(drive, rq, rq->sector); } return do_special(drive); kill_rq: diff --git a/include/linux/ide.h b/include/linux/ide.h index d963c1929c84..ca0b132de1ae 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -418,7 +418,6 @@ struct ide_drive_s { unsigned atapi_overlap : 1; /* ATAPI overlap (not supported) */ unsigned doorlocking : 1; /* for removable only: door lock/unlock works */ unsigned nodma : 1; /* disallow DMA */ - unsigned remap_0_to_1 : 1; /* 0=noremap, 1=remap 0->1 (for EZDrive) */ unsigned blocked : 1; /* 1=powermanagment told us not to do anything, so sleep nicely */ unsigned scsi : 1; /* 0=default, 1=ide-scsi emulation */ unsigned sleeping : 1; /* 1=sleeping & sleep field valid */ @@ -441,7 +440,6 @@ struct ide_drive_s { u8 io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ u8 bad_wstat; /* used for ignoring ATA_DF */ u8 nowerr; /* used for ignoring ATA_DF */ - u8 sect0; /* offset of first sector for DM6:DDO */ u8 head; /* "real" number of heads */ u8 sect; /* "real" sectors per track */ u8 bios_head; /* BIOS/fdisk/LILO number of heads */ -- cgit v1.2.3 From b163f46d5ecf48d883ce156e5e5a21a1a9a125c7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:23 +0200 Subject: ide: enhance ide_busy_sleep() * Make ide_busy_sleep() take timeout value as a parameter and also allow use of AltStatus Register if requested with altstatus parameter. Update existing users accordingly. * Convert ide_driveid_update() and actual_try_to_identify() to use ide_busy_sleep(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-iops.c | 15 +++++---------- drivers/ide/ide-probe.c | 30 ++++++++++++------------------ include/linux/ide.h | 2 ++ 3 files changed, 19 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 004803030f64..a940d127aae3 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -666,7 +666,7 @@ int ide_driveid_update(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; const struct ide_tp_ops *tp_ops = hwif->tp_ops; u16 *id; - unsigned long timeout, flags; + unsigned long flags; u8 stat; /* @@ -678,16 +678,11 @@ int ide_driveid_update(ide_drive_t *drive) tp_ops->set_irq(hwif, 0); msleep(50); tp_ops->exec_command(hwif, ATA_CMD_ID_ATA); - timeout = jiffies + WAIT_WORSTCASE; - do { - if (time_after(jiffies, timeout)) { - SELECT_MASK(drive, 0); - return 0; /* drive timed-out */ - } - msleep(50); /* give drive a breather */ - stat = tp_ops->read_altstatus(hwif); - } while (stat & ATA_BUSY); + if (ide_busy_sleep(hwif, WAIT_WORSTCASE, 1)) { + SELECT_MASK(drive, 0); + return 0; + } msleep(50); /* wait for IRQ and ATA_DRQ */ stat = tp_ops->read_status(hwif); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index e78cfde5bd6d..ef773384aaa9 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -291,17 +291,9 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) tp_ops->exec_command(hwif, cmd); timeout = ((cmd == ATA_CMD_ID_ATA) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; - timeout += jiffies; - do { - if (time_after(jiffies, timeout)) { - /* drive timed-out */ - return 1; - } - /* give drive a breather */ - msleep(50); - s = use_altstatus ? tp_ops->read_altstatus(hwif) - : tp_ops->read_status(hwif); - } while (s & ATA_BUSY); + + if (ide_busy_sleep(hwif, timeout, use_altstatus)) + return 1; /* wait for IRQ and ATA_DRQ */ msleep(50); @@ -383,19 +375,21 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) return retval; } -static int ide_busy_sleep(ide_hwif_t *hwif) +int ide_busy_sleep(ide_hwif_t *hwif, unsigned long timeout, int altstatus) { - unsigned long timeout = jiffies + WAIT_WORSTCASE; u8 stat; + timeout += jiffies; + do { - msleep(50); - stat = hwif->tp_ops->read_status(hwif); + msleep(50); /* give drive a breather */ + stat = altstatus ? hwif->tp_ops->read_altstatus(hwif) + : hwif->tp_ops->read_status(hwif); if ((stat & ATA_BUSY) == 0) return 0; } while (time_before(jiffies, timeout)); - return 1; + return 1; /* drive timed-out */ } static u8 ide_read_device(ide_drive_t *drive) @@ -489,7 +483,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(drive); msleep(50); tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET); - (void)ide_busy_sleep(hwif); + (void)ide_busy_sleep(hwif, WAIT_WORSTCASE, 0); rc = try_to_identify(drive, cmd); } @@ -529,7 +523,7 @@ static void enable_nest (ide_drive_t *drive) msleep(50); tp_ops->exec_command(hwif, ATA_EXABYTE_ENABLE_NEST); - if (ide_busy_sleep(hwif)) { + if (ide_busy_sleep(hwif, WAIT_WORSTCASE, 0)) { printk(KERN_CONT "failed (timeout)\n"); return; } diff --git a/include/linux/ide.h b/include/linux/ide.h index ca0b132de1ae..2ad8548135d2 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -923,6 +923,8 @@ void ide_fix_driveid(u16 *); extern void ide_fixstring(u8 *, const int, const int); +int ide_busy_sleep(ide_hwif_t *, unsigned long, int); + int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long); extern ide_startstop_t ide_do_reset (ide_drive_t *); -- cgit v1.2.3 From a2cdee5a9a93360165d0576bbc7e9ccb3127afee Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:24 +0200 Subject: ide: remove IDE_CHIPSET_* macros They just obfuscate the code. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 3 ++- include/linux/ide.h | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index a27c0398b153..8cfce50c71e4 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1105,7 +1105,8 @@ static int init_irq (ide_hwif_t *hwif) sa = IRQF_SHARED; #endif /* __mc68000__ */ - if (IDE_CHIPSET_IS_PCI(hwif->chipset)) + if (hwif->chipset == ide_pci || hwif->chipset == ide_cmd646 || + hwif->chipset == ide_ali14xx) sa = IRQF_SHARED; if (io_ports->ctl_addr) diff --git a/include/linux/ide.h b/include/linux/ide.h index 2ad8548135d2..a9206c463d7d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -473,10 +473,6 @@ typedef struct ide_drive_s ide_drive_t; #define to_ide_device(dev)container_of(dev, ide_drive_t, gendev) -#define IDE_CHIPSET_PCI_MASK \ - ((1<> (c)) & 1) - struct ide_task_s; struct ide_port_info; -- cgit v1.2.3 From 7e59ea21aab1a91ca31bc64c7d3035ebdbd336d1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:26 +0200 Subject: ide: check drive->present in ide_get_paired_drive() * Change ide_get_paired_drive() to return NULL if peer device is not present and update all users accordingly. While at it: * ide_get_paired_drive() -> ide_get_pair_dev() * Use ide_get_pair_dev() in cs5530.c, sc1200.c and via82cxxx.c. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/palm_bk3710.c | 4 ++-- drivers/ide/pci/cs5530.c | 4 ++-- drivers/ide/pci/cs5535.c | 4 ++-- drivers/ide/pci/opti621.c | 4 ++-- drivers/ide/pci/sc1200.c | 4 ++-- drivers/ide/pci/siimage.c | 4 ++-- drivers/ide/pci/via82cxxx.c | 4 ++-- include/linux/ide.h | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 6cea984776fd..320971c78cb7 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -180,7 +180,7 @@ static void palm_bk3710_setpiomode(void __iomem *base, ide_drive_t *mate, val32 |= (t2i << (dev ? 8 : 0)); writel(val32, base + BK3710_DATRCVR); - if (mate && mate->present) { + if (mate) { u8 mode2 = ide_get_best_pio_mode(mate, 255, 4); if (mode2 < mode) @@ -230,7 +230,7 @@ static void palm_bk3710_set_pio_mode(ide_drive_t *drive, u8 pio) * Obtain the drive PIO data for tuning the Palm Chip registers */ cycle_time = ide_pio_cycle_time(drive, pio); - mate = ide_get_paired_drive(drive); + mate = ide_get_pair_dev(drive); palm_bk3710_setpiomode(base, mate, is_slave, cycle_time, pio); } diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index ef91e9d7c54f..804ecabbd65f 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -81,11 +81,11 @@ static void cs5530_set_pio_mode(ide_drive_t *drive, const u8 pio) static u8 cs5530_udma_filter(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1]; + ide_drive_t *mate = ide_get_pair_dev(drive); u16 *mateid = mate->id; u8 mask = hwif->ultra_mask; - if (mate->present == 0) + if (mate == NULL) goto out; if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) { diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index dd3dc23af995..707d2e182552 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -80,12 +80,12 @@ static void cs5535_set_speed(ide_drive_t *drive, const u8 speed) /* Set the PIO timings */ if (speed < XFER_SW_DMA_0) { - ide_drive_t *pair = ide_get_paired_drive(drive); + ide_drive_t *pair = ide_get_pair_dev(drive); u8 cmd, pioa; cmd = pioa = speed - XFER_PIO_0; - if (pair->present) { + if (pair) { u8 piob = ide_get_best_pio_mode(pair, 255, 4); if (piob < cmd) diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index e28e672ddafc..e913da479808 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -137,7 +137,7 @@ static u8 read_reg(int reg) static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio) { ide_hwif_t *hwif = drive->hwif; - ide_drive_t *pair = ide_get_paired_drive(drive); + ide_drive_t *pair = ide_get_pair_dev(drive); unsigned long flags; u8 tim, misc, addr_pio = pio, clk; @@ -153,7 +153,7 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->drive_data = XFER_PIO_0 + pio; - if (pair->present) { + if (pair) { if (pair->drive_data && pair->drive_data < drive->drive_data) addr_pio = pair->drive_data - XFER_PIO_0; } diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 695cc9742048..37326d2205b8 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -104,11 +104,11 @@ static void sc1200_tunepio(ide_drive_t *drive, u8 pio) static u8 sc1200_udma_filter(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1]; + ide_drive_t *mate = ide_get_pair_dev(drive); u16 *mateid = mate->id; u8 mask = hwif->ultra_mask; - if (mate->present == 0) + if (mate == NULL) goto out; if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) { diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 7b5bd8729f64..83c36e6035fa 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -245,7 +245,7 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio) ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); - ide_drive_t *pair = ide_get_paired_drive(drive); + ide_drive_t *pair = ide_get_pair_dev(drive); u32 speedt = 0; u16 speedp = 0; unsigned long addr = siimage_seldev(drive, 0x04); @@ -259,7 +259,7 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio) u8 unit = drive->select.b.unit; /* trim *taskfile* PIO to the slowest of the master/slave */ - if (pair->present) { + if (pair) { u8 pair_pio = ide_get_best_pio_mode(pair, 255, 4); if (pair_pio < tf_pio) diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 94fb9ab3223f..9cb531dc905a 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -154,7 +154,7 @@ static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing) static void via_set_drive(ide_drive_t *drive, const u8 speed) { ide_hwif_t *hwif = drive->hwif; - ide_drive_t *peer = hwif->drives + (~drive->dn & 1); + ide_drive_t *peer = ide_get_pair_dev(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); struct ide_host *host = pci_get_drvdata(dev); struct via82cxxx_dev *vdev = host->host_priv; @@ -173,7 +173,7 @@ static void via_set_drive(ide_drive_t *drive, const u8 speed) ide_timing_compute(drive, speed, &t, T, UT); - if (peer->present) { + if (peer) { ide_timing_compute(peer, peer->current_speed, &p, T, UT); ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); } diff --git a/include/linux/ide.h b/include/linux/ide.h index a9206c463d7d..1d9716a95dc4 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1450,10 +1450,10 @@ static inline int hwif_to_node(ide_hwif_t *hwif) return hwif->dev ? dev_to_node(hwif->dev) : -1; } -static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive) +static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_drive_t *peer = &drive->hwif->drives[(drive->dn ^ 1) & 1]; - return &hwif->drives[(drive->dn ^ 1) & 1]; + return peer->present ? peer : NULL; } #endif /* _IDE_H */ -- cgit v1.2.3 From 3ceca727fe3a38dd8d7a3adf938fefda83eee8af Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:27 +0200 Subject: ide: include only when needed * Include directly in instead of through . * Include only when needed. Cc: Christoph Hellwig Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 1 - drivers/ide/arm/palm_bk3710.c | 1 - drivers/ide/ide-disk.c | 1 + drivers/ide/ide-floppy.c | 1 + drivers/ide/ide-io.c | 1 + drivers/ide/ide-iops.c | 1 - drivers/ide/ide-lib.c | 1 - drivers/ide/ide-proc.c | 1 - drivers/ide/ide-timings.c | 1 - drivers/ide/ide.c | 1 + drivers/ide/legacy/ali14xx.c | 1 - drivers/ide/legacy/buddha.c | 1 - drivers/ide/legacy/dtc2278.c | 1 - drivers/ide/legacy/falconide.c | 1 - drivers/ide/legacy/gayle.c | 1 - drivers/ide/legacy/ht6560b.c | 1 - drivers/ide/legacy/ide-cs.c | 1 - drivers/ide/legacy/macide.c | 1 - drivers/ide/legacy/q40ide.c | 2 -- drivers/ide/legacy/qd65xx.c | 1 - drivers/ide/legacy/umc8672.c | 1 - drivers/ide/pci/aec62xx.c | 1 - drivers/ide/pci/alim15x3.c | 1 - drivers/ide/pci/atiixp.c | 1 - drivers/ide/pci/cmd640.c | 1 - drivers/ide/pci/cmd64x.c | 1 - drivers/ide/pci/cs5520.c | 1 - drivers/ide/pci/cs5530.c | 1 - drivers/ide/pci/delkin_cb.c | 1 - drivers/ide/pci/generic.c | 1 - drivers/ide/pci/hpt34x.c | 1 - drivers/ide/pci/hpt366.c | 1 - drivers/ide/pci/it8213.c | 1 - drivers/ide/pci/it821x.c | 1 - drivers/ide/pci/jmicron.c | 1 - drivers/ide/pci/ns87415.c | 1 - drivers/ide/pci/opti621.c | 1 - drivers/ide/pci/pdc202xx_new.c | 1 - drivers/ide/pci/pdc202xx_old.c | 1 - drivers/ide/pci/piix.c | 1 - drivers/ide/pci/rz1000.c | 1 - drivers/ide/pci/sc1200.c | 1 - drivers/ide/pci/scc_pata.c | 1 - drivers/ide/pci/serverworks.c | 1 - drivers/ide/pci/sgiioc4.c | 1 - drivers/ide/pci/siimage.c | 1 - drivers/ide/pci/sis5513.c | 1 - drivers/ide/pci/sl82c105.c | 1 - drivers/ide/pci/slc90e66.c | 1 - drivers/ide/pci/triflex.c | 1 - drivers/ide/pci/trm290.c | 1 - drivers/scsi/ide-scsi.c | 1 - include/linux/ide.h | 2 +- 53 files changed, 5 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index ca9e8ea32ee2..70f5b164828b 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 320971c78cb7..122ed3c072fd 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index eeb2c3b22e97..6e1a25e61df5 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 327960380908..b0f4d8459a9d 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 96975e24a0cc..99e0bbca3ace 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index a940d127aae3..ee44878e40d0 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index c5c37bfd8b09..ed426dd0fdd8 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 22f41fa4e5b6..cbe5a7efbbad 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c index 96e3d467a74c..81f527af8fae 100644 --- a/drivers/ide/ide-timings.c +++ b/drivers/ide/ide-timings.c @@ -22,7 +22,6 @@ */ #include -#include #include #include diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 27d5d6a1625f..080314e3d6b7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c index 4ec19737f3c5..7276c96aaa2a 100644 --- a/drivers/ide/legacy/ali14xx.c +++ b/drivers/ide/legacy/ali14xx.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 7c2afa97f417..c5a3c9ef6a5d 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c index af791a02a120..689b2e493413 100644 --- a/drivers/ide/legacy/dtc2278.c +++ b/drivers/ide/legacy/dtc2278.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 724f95073d80..39d500d84b07 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 51ba085d7aa8..691506886561 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index 98f7c95e39ed..5123ea291d07 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 21bfac137844..ee6fc30d5e2b 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index a0bb167980e7..43f97cc1d30e 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 4abd8fc78197..4af4a8ce4cdf 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -14,8 +14,6 @@ #include #include #include -#include - #include /* diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 6d7f548655e4..ec408b3a7100 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c index b54a14a57755..1da076e0c917 100644 --- a/drivers/ide/legacy/umc8672.c +++ b/drivers/ide/legacy/umc8672.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index 3187215e8f89..f65828da65d0 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index fcc701b3c0a2..ef41e2677a56 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 41f6cb6c163a..86e3120cb7c1 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index e6c62006ca1a..5b756757e049 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -103,7 +103,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index e064398e03b4..13dfeab1d19f 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index 151844fcbb07..27163147896b 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index 804ecabbd65f..d60806bd7dba 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index f84bfb4f600f..83b63b365e51 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index b07d4f4273b3..bdc539868701 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 6009b0b9655d..4f624899f44a 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index b7f77fd3cb6e..d706eb6b8acd 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -122,7 +122,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c index 652e47dd7e89..a0e058a2abfe 100644 --- a/drivers/ide/pci/it8213.c +++ b/drivers/ide/pci/it8213.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index ed24065f74e8..0fdea7e91a7a 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c index bb9d09d8f196..4010b4a8dfbb 100644 --- a/drivers/ide/pci/jmicron.c +++ b/drivers/ide/pci/jmicron.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 17685f0ade13..a482ade8e455 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index e913da479808..fefac2c174b6 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -85,7 +85,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 7ecfcd06f47e..73bd264fbf9f 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 5d4436f3efd4..8f0acb956c6b 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 30cfc815fe31..13136dddb2b4 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c index 8d11ee838a2a..c117a068761b 100644 --- a/drivers/ide/pci/rz1000.c +++ b/drivers/ide/pci/rz1000.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 37326d2205b8..bdc1fed41260 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 192a6a70a631..e92a874b31df 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index ded6a13fd406..5f79d284ff82 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 681306c9d79b..1017fb4f6317 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 83c36e6035fa..874c8ca40ed6 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 5efe21d6ef97..56bfb245f1fa 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 73905bcc08fb..61a006cb4746 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c index 866d6c65e3a0..a31c6911442d 100644 --- a/drivers/ide/pci/slc90e66.c +++ b/drivers/ide/pci/slc90e66.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c index b77ec35151b3..c980a7f39052 100644 --- a/drivers/ide/pci/triflex.c +++ b/drivers/ide/pci/triflex.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index fd28b49977fd..4dfbc6a68b5b 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -135,7 +135,6 @@ #include #include #include -#include #include #include diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 6a661a4454ee..d9a2cc09f5c6 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/ide.h b/include/linux/ide.h index 1d9716a95dc4..a7f980d2fe5b 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 263138a0ad6e38de7f6526b7de037ed4511308ef Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:27 +0200 Subject: ide: preparations for /proc/ide/hd*/settings rework After rework settings will be no longer created dynamically for each device so we need to make some fixups first. * Use set_[ksettings,unmaskirq]() as a set function for ["keepsettings","unmaskirq"] setting. * Allow writes to ["io_32bit","unmaskirq"] settings also when drive->no_[io_32bit,unmask] is set (this is checked later inside set_[io_32bit,unmaskirq]() anywyay and keeps consistency with the corresponding HDIO_SET_[32BIT,UNMASKINTR] ioctls). * Use max possible multi sectors value (16) as an allowed max for "multcount" setting. set_multcount() set function checks against device's max possbile value anyway and it makes the proc setting consistent with the corresponding HDIO_SET_MULTCOUNT ioctl. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 3 +-- drivers/ide/ide-proc.c | 6 +++--- drivers/ide/ide.c | 4 ++-- include/linux/ide.h | 2 ++ 4 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 5fc4213437a5..7a15907dce1d 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -760,8 +760,7 @@ static void idedisk_add_settings(ide_drive_t *drive) &drive->bios_sect, NULL); ide_add_setting(drive, "address", SETTING_RW, TYPE_BYTE, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); - ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0, - drive->id[ATA_ID_MAX_MULTSECT] & 0xff, 1, 1, + ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0, 16, 1, 1, &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index cbe5a7efbbad..7a64aedfa648 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -368,11 +368,11 @@ void ide_add_generic_settings (ide_drive_t *drive) /* * drive setting name read/write access data type min max mul_factor div_factor data pointer set function */ - __ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit, 0); - __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL, 0); + __ide_add_setting(drive, "io_32bit", SETTING_RW, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit, 0); + __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, set_ksettings, 0); __ide_add_setting(drive, "nice1", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0); __ide_add_setting(drive, "pio_mode", SETTING_WRITE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0); - __ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL, 0); + __ide_add_setting(drive, "unmaskirq", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, set_unmaskirq, 0); __ide_add_setting(drive, "using_dma", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma, 0); __ide_add_setting(drive, "init_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0); __ide_add_setting(drive, "current_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 080314e3d6b7..8e0c9f27ae4a 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -305,7 +305,7 @@ int set_io_32bit(ide_drive_t *drive, int arg) return 0; } -static int set_ksettings(ide_drive_t *drive, int arg) +int set_ksettings(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 1) return -EINVAL; @@ -394,7 +394,7 @@ int set_pio_mode(ide_drive_t *drive, int arg) return 0; } -static int set_unmaskirq(ide_drive_t *drive, int arg) +int set_unmaskirq(ide_drive_t *drive, int arg) { if (drive->no_unmask) return -EPERM; diff --git a/include/linux/ide.h b/include/linux/ide.h index a7f980d2fe5b..ad09e7c81ae9 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -688,7 +688,9 @@ typedef struct ide_driver_s ide_driver_t; extern struct mutex ide_setting_mtx; int set_io_32bit(ide_drive_t *, int); +int set_ksettings(ide_drive_t *, int); int set_pio_mode(ide_drive_t *, int); +int set_unmaskirq(ide_drive_t *, int); int set_using_dma(ide_drive_t *, int); /* ATAPI packet command flags */ -- cgit v1.2.3 From 8185d5aa93e0a5c111adc4952a5b87193a68ae5b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:28 +0200 Subject: ide: /proc/ide/hd*/settings rework * Add struct ide_devset, S_* flags, *DEVSET() & ide*_devset_*() macros. * Add 'const struct ide_devset **settings' to ide_driver_t. * Use 'const struct ide_devset **settings' in ide_drive_t instead of 'struct ide_settings_s *settings'. Then convert core code and device drivers to use struct ide_devset and co.: - device settings are no longer allocated dynamically for each device but instead there is an unique struct ide_devset instance per setting - device driver keeps the pointer to the table of pointers to its settings in ide_driver_t.settings - generic settings are kept in ide_generic_setting[] - ide_proc_[un]register_driver(), ide_find_setting_by_name(), ide_{read,write}_setting() and proc_ide_{read,write}_settings() are updated accordingly - ide*_add_settings() are removed * Remove no longer used __ide_add_setting(), ide_add_setting(), __ide_remove_setting() and auto_remove_settings(). * Remove no longer used TYPE_*, SETTING_*, ide_procset_t and ide_settings_t. * ->keep_settings, ->using_dma, ->unmask, ->noflush, ->dsc_overlap, ->nice1, ->addressing, ->wcache and ->nowerr ide_drive_t fields can now be bitfield flags. While at it: * Rename ide_find_setting_by_name() to ide_find_setting(). * Rename write_wcache() to set_wcache(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 15 ++- drivers/ide/ide-disk.c | 87 ++++++++------- drivers/ide/ide-floppy.c | 35 ++++-- drivers/ide/ide-probe.c | 2 - drivers/ide/ide-proc.c | 279 +++++++++++++++-------------------------------- drivers/ide/ide-tape.c | 74 +++++++++---- drivers/ide/ide.c | 21 ++-- drivers/scsi/ide-scsi.c | 52 ++++++--- include/linux/ide.h | 110 +++++++++++++------ 9 files changed, 340 insertions(+), 335 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 34a1aeaa15dd..1f5652326489 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1809,13 +1809,12 @@ static ide_proc_entry_t idecd_proc[] = { { NULL, 0, NULL, NULL } }; -static void ide_cdrom_add_settings(ide_drive_t *drive) -{ - ide_add_setting(drive, "dsc_overlap", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, - &drive->dsc_overlap, NULL); -} -#else -static inline void ide_cdrom_add_settings(ide_drive_t *drive) { ; } +ide_devset_rw(dsc_overlap, 0, 1, dsc_overlap); + +static const struct ide_devset *idecd_settings[] = { + &ide_devset_dsc_overlap, + NULL +}; #endif static const struct cd_list_entry ide_cd_quirks_list[] = { @@ -1926,7 +1925,6 @@ static int ide_cdrom_setup(ide_drive_t *drive) } ide_proc_register_driver(drive, cd->driver); - ide_cdrom_add_settings(drive); return 0; } @@ -1977,6 +1975,7 @@ static ide_driver_t ide_cdrom_driver = { .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc = idecd_proc, + .settings = idecd_settings, #endif }; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 7a15907dce1d..2e43ae15fb1b 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -599,6 +599,8 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq) rq->special = task; } +ide_devset_get(multcount, mult_count); + /* * This is tightly woven into the driver->do_special can not touch. * DON'T do it again until a total personality rewrite is committed. @@ -625,6 +627,8 @@ static int set_multcount(ide_drive_t *drive, int arg) return (drive->mult_count == arg) ? 0 : -EIO; } +ide_devset_get(nowerr, nowerr); + static int set_nowerr(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 1) @@ -673,7 +677,9 @@ static void update_ordered(ide_drive_t *drive) blk_queue_ordered(drive->queue, ordered, prep_fn); } -static int write_cache(ide_drive_t *drive, int arg) +ide_devset_get(wcache, wcache); + +static int set_wcache(ide_drive_t *drive, int arg) { ide_task_t args; int err = 1; @@ -710,6 +716,8 @@ static int do_idedisk_flushcache(ide_drive_t *drive) return ide_no_data_taskfile(drive, &args); } +ide_devset_get(acoustic, acoustic); + static int set_acoustic(ide_drive_t *drive, int arg) { ide_task_t args; @@ -727,6 +735,8 @@ static int set_acoustic(ide_drive_t *drive, int arg) return 0; } +ide_devset_get(lba_addressing, addressing); + /* * drive->addressing: * 0: 28-bit @@ -750,33 +760,33 @@ static int set_lba_addressing(ide_drive_t *drive, int arg) } #ifdef CONFIG_IDE_PROC_FS -static void idedisk_add_settings(ide_drive_t *drive) -{ - ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 65535, 1, 1, - &drive->bios_cyl, NULL); - ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, - &drive->bios_head, NULL); - ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1, - &drive->bios_sect, NULL); - ide_add_setting(drive, "address", SETTING_RW, TYPE_BYTE, 0, 2, 1, 1, - &drive->addressing, set_lba_addressing); - ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0, 16, 1, 1, - &drive->mult_count, set_multcount); - ide_add_setting(drive, "nowerr", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, - &drive->nowerr, set_nowerr); - ide_add_setting(drive, "lun", SETTING_RW, TYPE_INT, 0, 7, 1, 1, - &drive->lun, NULL); - ide_add_setting(drive, "wcache", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, - &drive->wcache, write_cache); - ide_add_setting(drive, "acoustic", SETTING_RW, TYPE_BYTE, 0, 254, 1, 1, - &drive->acoustic, set_acoustic); - ide_add_setting(drive, "failures", SETTING_RW, TYPE_INT, 0, 65535, 1, 1, - &drive->failures, NULL); - ide_add_setting(drive, "max_failures", SETTING_RW, TYPE_INT, 0, 65535, - 1, 1, &drive->max_failures, NULL); -} -#else -static inline void idedisk_add_settings(ide_drive_t *drive) { ; } +ide_devset_rw_nolock(acoustic, 0, 254, acoustic); +ide_devset_rw_nolock(address, 0, 2, lba_addressing); +ide_devset_rw_nolock(multcount, 0, 16, multcount); +ide_devset_rw_nolock(nowerr, 0, 1, nowerr); +ide_devset_rw_nolock(wcache, 0, 1, wcache); + +ide_devset_rw(bios_cyl, 0, 65535, bios_cyl); +ide_devset_rw(bios_head, 0, 255, bios_head); +ide_devset_rw(bios_sect, 0, 63, bios_sect); +ide_devset_rw(failures, 0, 65535, failures); +ide_devset_rw(lun, 0, 7, lun); +ide_devset_rw(max_failures, 0, 65535, max_failures); + +static const struct ide_devset *idedisk_settings[] = { + &ide_devset_acoustic, + &ide_devset_address, + &ide_devset_bios_cyl, + &ide_devset_bios_head, + &ide_devset_bios_sect, + &ide_devset_failures, + &ide_devset_lun, + &ide_devset_max_failures, + &ide_devset_multcount, + &ide_devset_nowerr, + &ide_devset_wcache, + NULL +}; #endif static void idedisk_setup(ide_drive_t *drive) @@ -788,7 +798,6 @@ static void idedisk_setup(ide_drive_t *drive) unsigned long long capacity; ide_proc_register_driver(drive, idkp->driver); - idedisk_add_settings(drive); if (drive->id_read == 0) return; @@ -880,7 +889,7 @@ static void idedisk_setup(ide_drive_t *drive) if ((id[ATA_ID_CSFO] & 1) || ata_id_wcache_enabled(id)) drive->wcache = 1; - write_cache(drive, 1); + set_wcache(drive, 1); } static void ide_cacheflush_p(ide_drive_t *drive) @@ -976,6 +985,7 @@ static ide_driver_t idedisk_driver = { .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc = idedisk_proc, + .settings = idedisk_settings, #endif }; @@ -1056,19 +1066,18 @@ static int idedisk_ioctl(struct inode *inode, struct file *file, struct block_device *bdev = inode->i_bdev; struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk); ide_drive_t *drive = idkp->drive; - int err, (*setfunc)(ide_drive_t *, int); - u8 *val; + int err, (*getfunc)(ide_drive_t *), (*setfunc)(ide_drive_t *, int); switch (cmd) { - case HDIO_GET_ADDRESS: val = &drive->addressing; goto read_val; - case HDIO_GET_MULTCOUNT: val = &drive->mult_count; goto read_val; - case HDIO_GET_NOWERR: val = &drive->nowerr; goto read_val; - case HDIO_GET_WCACHE: val = &drive->wcache; goto read_val; - case HDIO_GET_ACOUSTIC: val = &drive->acoustic; goto read_val; + case HDIO_GET_ADDRESS: getfunc = get_lba_addressing; goto read_val; + case HDIO_GET_MULTCOUNT: getfunc = get_multcount; goto read_val; + case HDIO_GET_NOWERR: getfunc = get_nowerr; goto read_val; + case HDIO_GET_WCACHE: getfunc = get_wcache; goto read_val; + case HDIO_GET_ACOUSTIC: getfunc = get_acoustic; goto read_val; case HDIO_SET_ADDRESS: setfunc = set_lba_addressing; goto set_val; case HDIO_SET_MULTCOUNT: setfunc = set_multcount; goto set_val; case HDIO_SET_NOWERR: setfunc = set_nowerr; goto set_val; - case HDIO_SET_WCACHE: setfunc = write_cache; goto set_val; + case HDIO_SET_WCACHE: setfunc = set_wcache; goto set_val; case HDIO_SET_ACOUSTIC: setfunc = set_acoustic; goto set_val; } @@ -1077,7 +1086,7 @@ static int idedisk_ioctl(struct inode *inode, struct file *file, read_val: mutex_lock(&ide_setting_mtx); spin_lock_irqsave(&ide_lock, flags); - err = *val; + err = getfunc(drive); spin_unlock_irqrestore(&ide_lock, flags); mutex_unlock(&ide_setting_mtx); return err >= 0 ? put_user(err, (long __user *)arg) : err; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 597459c81d5f..673644fdb6f2 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -1007,21 +1007,32 @@ static int idefloppy_identify_device(ide_drive_t *drive, u16 *id) } #ifdef CONFIG_IDE_PROC_FS -static void idefloppy_add_settings(ide_drive_t *drive) +ide_devset_rw(bios_cyl, 0, 1023, bios_cyl); +ide_devset_rw(bios_head, 0, 255, bios_head); +ide_devset_rw(bios_sect, 0, 63, bios_sect); + +static int get_ticks(ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; + return floppy->ticks; +} - ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 1023, 1, 1, - &drive->bios_cyl, NULL); - ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, - &drive->bios_head, NULL); - ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1, - &drive->bios_sect, NULL); - ide_add_setting(drive, "ticks", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, - &floppy->ticks, NULL); +static int set_ticks(ide_drive_t *drive, int arg) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + floppy->ticks = arg; + return 0; } -#else -static inline void idefloppy_add_settings(ide_drive_t *drive) { ; } + +IDE_DEVSET(ticks, S_RW, 0, 255, get_ticks, set_ticks); + +static const struct ide_devset *idefloppy_settings[] = { + &ide_devset_bios_cyl, + &ide_devset_bios_head, + &ide_devset_bios_sect, + &ide_devset_ticks, + NULL +}; #endif static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) @@ -1063,7 +1074,6 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) (void) ide_floppy_get_capacity(drive); ide_proc_register_driver(drive, floppy->driver); - idefloppy_add_settings(drive); } static void ide_floppy_remove(ide_drive_t *drive) @@ -1126,6 +1136,7 @@ static ide_driver_t idefloppy_driver = { .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc = idefloppy_proc, + .settings = idefloppy_settings, #endif }; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9926e12783bb..62f7e1ef10c1 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1332,8 +1332,6 @@ static void hwif_register_devices(ide_hwif_t *hwif) if (!drive->present) continue; - ide_add_generic_settings(drive); - snprintf(dev->bus_id, BUS_ID_SIZE, "%u.%u", hwif->index, i); dev->parent = &hwif->gendev; dev->bus = &ide_bus_type; diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 7a64aedfa648..5634b3971d21 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -114,140 +114,24 @@ static int proc_ide_read_identify } /** - * __ide_add_setting - add an ide setting option - * @drive: drive to use + * ide_find_setting - find a specific setting + * @st: setting table pointer * @name: setting name - * @rw: true if the function is read write - * @data_type: type of data - * @min: range minimum - * @max: range maximum - * @mul_factor: multiplication scale - * @div_factor: divison scale - * @data: private data field - * @set: setting - * @auto_remove: setting auto removal flag * - * Removes the setting named from the device if it is present. - * The function takes the settings_lock to protect against - * parallel changes. This function must not be called from IRQ - * context. Returns 0 on success or -1 on failure. - * - * BUGS: This code is seriously over-engineered. There is also - * magic about how the driver specific features are setup. If - * a driver is attached we assume the driver settings are auto - * remove. - */ - -static int __ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set, int auto_remove) -{ - ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; - - mutex_lock(&ide_setting_mtx); - while ((*p) && strcmp((*p)->name, name) < 0) - p = &((*p)->next); - if ((setting = kzalloc(sizeof(*setting), GFP_KERNEL)) == NULL) - goto abort; - if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL) - goto abort; - strcpy(setting->name, name); - setting->rw = rw; - setting->data_type = data_type; - setting->min = min; - setting->max = max; - setting->mul_factor = mul_factor; - setting->div_factor = div_factor; - setting->data = data; - setting->set = set; - - setting->next = *p; - if (auto_remove) - setting->auto_remove = 1; - *p = setting; - mutex_unlock(&ide_setting_mtx); - return 0; -abort: - mutex_unlock(&ide_setting_mtx); - kfree(setting); - return -1; -} - -int ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set) -{ - return __ide_add_setting(drive, name, rw, data_type, min, max, mul_factor, div_factor, data, set, 1); -} - -EXPORT_SYMBOL(ide_add_setting); - -/** - * __ide_remove_setting - remove an ide setting option - * @drive: drive to use - * @name: setting name - * - * Removes the setting named from the device if it is present. - * The caller must hold the setting semaphore. - */ - -static void __ide_remove_setting(ide_drive_t *drive, char *name) -{ - ide_settings_t **p, *setting; - - p = (ide_settings_t **) &drive->settings; - - while ((*p) && strcmp((*p)->name, name)) - p = &((*p)->next); - setting = (*p); - if (setting == NULL) - return; - - (*p) = setting->next; - - kfree(setting->name); - kfree(setting); -} - -/** - * auto_remove_settings - remove driver specific settings - * @drive: drive - * - * Automatically remove all the driver specific settings for this - * drive. This function may not be called from IRQ context. The - * caller must hold ide_setting_mtx. - */ - -static void auto_remove_settings(ide_drive_t *drive) -{ - ide_settings_t *setting; -repeat: - setting = drive->settings; - while (setting) { - if (setting->auto_remove) { - __ide_remove_setting(drive, setting->name); - goto repeat; - } - setting = setting->next; - } -} - -/** - * ide_find_setting_by_name - find a drive specific setting - * @drive: drive to scan - * @name: setting name - * - * Scan's the device setting table for a matching entry and returns + * Scan's the setting table for a matching entry and returns * this or NULL if no entry is found. The caller must hold the * setting semaphore */ -static ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name) +static const struct ide_devset *ide_find_setting(const struct ide_devset **st, + char *name) { - ide_settings_t *setting = drive->settings; - - while (setting) { - if (strcmp(setting->name, name) == 0) + while (*st) { + if (strcmp((*st)->name, name) == 0) break; - setting = setting->next; + st++; } - return setting; + return *st; } /** @@ -263,26 +147,19 @@ static ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name) * be told apart */ -static int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting) +static int ide_read_setting(ide_drive_t *drive, + const struct ide_devset *setting) { - int val = -EINVAL; - unsigned long flags; + int val = -EINVAL; + + if ((setting->flags & S_READ)) { + unsigned long flags; - if ((setting->rw & SETTING_READ)) { spin_lock_irqsave(&ide_lock, flags); - switch (setting->data_type) { - case TYPE_BYTE: - val = *((u8 *) setting->data); - break; - case TYPE_SHORT: - val = *((u16 *) setting->data); - break; - case TYPE_INT: - val = *((u32 *) setting->data); - break; - } + val = setting->get(drive); spin_unlock_irqrestore(&ide_lock, flags); } + return val; } @@ -304,33 +181,26 @@ static int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting) * The current scheme of polling is kludgy, though safe enough. */ -static int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val) +static int ide_write_setting(ide_drive_t *drive, + const struct ide_devset *setting, int val) { if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (setting->set) + if (setting->set && (setting->flags & S_NOLOCK)) return setting->set(drive, val); - if (!(setting->rw & SETTING_WRITE)) + if (!(setting->flags & S_WRITE)) return -EPERM; if (val < setting->min || val > setting->max) return -EINVAL; if (ide_spin_wait_hwgroup(drive)) return -EBUSY; - switch (setting->data_type) { - case TYPE_BYTE: - *((u8 *) setting->data) = val; - break; - case TYPE_SHORT: - *((u16 *) setting->data) = val; - break; - case TYPE_INT: - *((u32 *) setting->data) = val; - break; - } + setting->set(drive, val); spin_unlock_irq(&ide_lock); return 0; } +static ide_devset_get(xfer_rate, current_speed); + static int set_xfer_rate (ide_drive_t *drive, int arg) { ide_task_t task; @@ -355,29 +225,30 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) return err; } -/** - * ide_add_generic_settings - generic ide settings - * @drive: drive being configured - * - * Add the generic parts of the system settings to the /proc files. - * The caller must not be holding the ide_setting_mtx. - */ - -void ide_add_generic_settings (ide_drive_t *drive) -{ -/* - * drive setting name read/write access data type min max mul_factor div_factor data pointer set function - */ - __ide_add_setting(drive, "io_32bit", SETTING_RW, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit, 0); - __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, set_ksettings, 0); - __ide_add_setting(drive, "nice1", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0); - __ide_add_setting(drive, "pio_mode", SETTING_WRITE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0); - __ide_add_setting(drive, "unmaskirq", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, set_unmaskirq, 0); - __ide_add_setting(drive, "using_dma", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma, 0); - __ide_add_setting(drive, "init_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0); - __ide_add_setting(drive, "current_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0); - __ide_add_setting(drive, "number", SETTING_RW, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL, 0); -} +ide_devset_rw_nolock(current_speed, 0, 70, xfer_rate); +ide_devset_rw_nolock(io_32bit, 0, 1 + (SUPPORT_VLB_SYNC << 1), io_32bit); +ide_devset_rw_nolock(keepsettings, 0, 1, ksettings); +ide_devset_rw_nolock(unmaskirq, 0, 1, unmaskirq); +ide_devset_rw_nolock(using_dma, 0, 1, using_dma); + +ide_devset_w_nolock(pio_mode, 0, 255, pio_mode); + +ide_devset_rw(init_speed, 0, 70, init_speed); +ide_devset_rw(nice1, 0, 1, nice1); +ide_devset_rw(number, 0, 3, dn); + +static const struct ide_devset *ide_generic_settings[] = { + &ide_devset_current_speed, + &ide_devset_init_speed, + &ide_devset_io_32bit, + &ide_devset_keepsettings, + &ide_devset_nice1, + &ide_devset_number, + &ide_devset_pio_mode, + &ide_devset_unmaskirq, + &ide_devset_using_dma, + NULL +}; static void proc_ide_settings_warn(void) { @@ -394,19 +265,31 @@ static void proc_ide_settings_warn(void) static int proc_ide_read_settings (char *page, char **start, off_t off, int count, int *eof, void *data) { + const struct ide_devset *setting, **g, **d; ide_drive_t *drive = (ide_drive_t *) data; - ide_settings_t *setting = (ide_settings_t *) drive->settings; char *out = page; int len, rc, mul_factor, div_factor; proc_ide_settings_warn(); mutex_lock(&ide_setting_mtx); + g = ide_generic_settings; + d = drive->settings; out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n"); out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n"); - while (setting) { - mul_factor = setting->mul_factor; - div_factor = setting->div_factor; + while (*g || (d && *d)) { + /* read settings in the alphabetical order */ + if (*g && d && *d) { + if (strcmp((*d)->name, (*g)->name) < 0) + setting = *d++; + else + setting = *g++; + } else if (d && *d) { + setting = *d++; + } else + setting = *g++; + mul_factor = setting->mulf ? setting->mulf(drive) : 1; + div_factor = setting->divf ? setting->divf(drive) : 1; out += sprintf(out, "%-24s", setting->name); rc = ide_read_setting(drive, setting); if (rc >= 0) @@ -414,12 +297,11 @@ static int proc_ide_read_settings else out += sprintf(out, "%-16s", "write-only"); out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor); - if (setting->rw & SETTING_READ) + if (setting->flags & S_READ) out += sprintf(out, "r"); - if (setting->rw & SETTING_WRITE) + if (setting->flags & S_WRITE) out += sprintf(out, "w"); out += sprintf(out, "\n"); - setting = setting->next; } len = out - page; mutex_unlock(&ide_setting_mtx); @@ -433,9 +315,10 @@ static int proc_ide_write_settings(struct file *file, const char __user *buffer, { ide_drive_t *drive = (ide_drive_t *) data; char name[MAX_LEN + 1]; - int for_real = 0; + int for_real = 0, mul_factor, div_factor; unsigned long n; - ide_settings_t *setting; + + const struct ide_devset *setting; char *buf, *s; if (!capable(CAP_SYS_ADMIN)) @@ -503,13 +386,21 @@ static int proc_ide_write_settings(struct file *file, const char __user *buffer, } mutex_lock(&ide_setting_mtx); - setting = ide_find_setting_by_name(drive, name); + /* generic settings first, then driver specific ones */ + setting = ide_find_setting(ide_generic_settings, name); if (!setting) { - mutex_unlock(&ide_setting_mtx); - goto parse_error; + if (drive->settings) + setting = ide_find_setting(drive->settings, name); + if (!setting) { + mutex_unlock(&ide_setting_mtx); + goto parse_error; + } + } + if (for_real) { + mul_factor = setting->mulf ? setting->mulf(drive) : 1; + div_factor = setting->divf ? setting->divf(drive) : 1; + ide_write_setting(drive, setting, val * div_factor / mul_factor); } - if (for_real) - ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor); mutex_unlock(&ide_setting_mtx); } } while (!for_real++); @@ -680,6 +571,10 @@ static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) { + mutex_lock(&ide_setting_mtx); + drive->settings = driver->settings; + mutex_unlock(&ide_setting_mtx); + ide_add_proc_entries(drive->proc, driver->proc, drive); } @@ -716,7 +611,7 @@ void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) * OTOH both ide_{read,write}_setting are only ever used under * ide_setting_mtx. */ - auto_remove_settings(drive); + drive->settings = NULL; spin_unlock_irqrestore(&ide_lock, flags); mutex_unlock(&ide_setting_mtx); } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index f41983e4a4e4..7037accb0589 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2410,28 +2410,56 @@ static void idetape_get_mode_sense_results(ide_drive_t *drive) } #ifdef CONFIG_IDE_PROC_FS -static void idetape_add_settings(ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - - ide_add_setting(drive, "buffer", SETTING_READ, TYPE_SHORT, 0, 0xffff, - 1, 2, (u16 *)&tape->caps[16], NULL); - ide_add_setting(drive, "speed", SETTING_READ, TYPE_SHORT, 0, 0xffff, - 1, 1, (u16 *)&tape->caps[14], NULL); - ide_add_setting(drive, "buffer_size", SETTING_READ, TYPE_INT, 0, 0xffff, - 1, 1024, &tape->buffer_size, NULL); - ide_add_setting(drive, "tdsc", SETTING_RW, TYPE_INT, IDETAPE_DSC_RW_MIN, - IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_freq, - NULL); - ide_add_setting(drive, "dsc_overlap", SETTING_RW, TYPE_BYTE, 0, 1, 1, - 1, &drive->dsc_overlap, NULL); - ide_add_setting(drive, "avg_speed", SETTING_READ, TYPE_INT, 0, 0xffff, - 1, 1, &tape->avg_speed, NULL); - ide_add_setting(drive, "debug_mask", SETTING_RW, TYPE_INT, 0, 0xffff, 1, - 1, &tape->debug_mask, NULL); -} -#else -static inline void idetape_add_settings(ide_drive_t *drive) { ; } +#define ide_tape_devset_get(name, field) \ +static int get_##name(ide_drive_t *drive) \ +{ \ + idetape_tape_t *tape = drive->driver_data; \ + return tape->field; \ +} + +#define ide_tape_devset_set(name, field) \ +static int set_##name(ide_drive_t *drive, int arg) \ +{ \ + idetape_tape_t *tape = drive->driver_data; \ + tape->field = arg; \ + return 0; \ +} + +#define ide_tape_devset_rw(_name, _min, _max, _field, _mulf, _divf) \ +ide_tape_devset_get(_name, _field) \ +ide_tape_devset_set(_name, _field) \ +__IDE_DEVSET(_name, S_RW, _min, _max, get_##_name, set_##_name, _mulf, _divf) + +#define ide_tape_devset_r(_name, _min, _max, _field, _mulf, _divf) \ +ide_tape_devset_get(_name, _field) \ +__IDE_DEVSET(_name, S_READ, _min, _max, get_##_name, NULL, _mulf, _divf) + +static int mulf_tdsc(ide_drive_t *drive) { return 1000; } +static int divf_tdsc(ide_drive_t *drive) { return HZ; } +static int divf_buffer(ide_drive_t *drive) { return 2; } +static int divf_buffer_size(ide_drive_t *drive) { return 1024; } + +ide_devset_rw(dsc_overlap, 0, 1, dsc_overlap); + +ide_tape_devset_rw(debug_mask, 0, 0xffff, debug_mask, NULL, NULL); +ide_tape_devset_rw(tdsc, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, + best_dsc_rw_freq, mulf_tdsc, divf_tdsc); + +ide_tape_devset_r(avg_speed, 0, 0xffff, avg_speed, NULL, NULL); +ide_tape_devset_r(speed, 0, 0xffff, caps[14], NULL, NULL); +ide_tape_devset_r(buffer, 0, 0xffff, caps[16], NULL, divf_buffer); +ide_tape_devset_r(buffer_size, 0, 0xffff, buffer_size, NULL, divf_buffer_size); + +static const struct ide_devset *idetape_settings[] = { + &ide_devset_avg_speed, + &ide_devset_buffer, + &ide_devset_buffer_size, + &ide_devset_debug_mask, + &ide_devset_dsc_overlap, + &ide_devset_speed, + &ide_devset_tdsc, + NULL +}; #endif /* @@ -2515,7 +2543,6 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) drive->using_dma ? ", DMA":""); ide_proc_register_driver(drive, tape->driver); - idetape_add_settings(drive); } static void ide_tape_remove(ide_drive_t *drive) @@ -2586,6 +2613,7 @@ static ide_driver_t idetape_driver = { .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc = idetape_proc, + .settings = idetape_settings, #endif }; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 8e0c9f27ae4a..eb64e942f58b 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -287,6 +287,8 @@ int ide_spin_wait_hwgroup (ide_drive_t *drive) EXPORT_SYMBOL(ide_spin_wait_hwgroup); +ide_devset_get(io_32bit, io_32bit); + int set_io_32bit(ide_drive_t *drive, int arg) { if (drive->no_io_32bit) @@ -305,6 +307,8 @@ int set_io_32bit(ide_drive_t *drive, int arg) return 0; } +ide_devset_get(ksettings, keep_settings); + int set_ksettings(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 1) @@ -318,6 +322,8 @@ int set_ksettings(ide_drive_t *drive, int arg) return 0; } +ide_devset_get(using_dma, using_dma); + int set_using_dma(ide_drive_t *drive, int arg) { #ifdef CONFIG_BLK_DEV_IDEDMA @@ -394,6 +400,8 @@ int set_pio_mode(ide_drive_t *drive, int arg) return 0; } +ide_devset_get(unmaskirq, unmask); + int set_unmaskirq(ide_drive_t *drive, int arg) { if (drive->no_unmask) @@ -555,14 +563,13 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device { unsigned long flags; ide_driver_t *drv; - int err = 0, (*setfunc)(ide_drive_t *, int); - u8 *val; + int err = 0, (*getfunc)(ide_drive_t *), (*setfunc)(ide_drive_t *, int); switch (cmd) { - case HDIO_GET_32BIT: val = &drive->io_32bit; goto read_val; - case HDIO_GET_KEEPSETTINGS: val = &drive->keep_settings; goto read_val; - case HDIO_GET_UNMASKINTR: val = &drive->unmask; goto read_val; - case HDIO_GET_DMA: val = &drive->using_dma; goto read_val; + case HDIO_GET_32BIT: getfunc = get_io_32bit; goto read_val; + case HDIO_GET_KEEPSETTINGS: getfunc = get_ksettings; goto read_val; + case HDIO_GET_UNMASKINTR: getfunc = get_unmaskirq; goto read_val; + case HDIO_GET_DMA: getfunc = get_using_dma; goto read_val; case HDIO_SET_32BIT: setfunc = set_io_32bit; goto set_val; case HDIO_SET_KEEPSETTINGS: setfunc = set_ksettings; goto set_val; case HDIO_SET_PIO_MODE: setfunc = set_pio_mode; goto set_val; @@ -638,7 +645,7 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device read_val: mutex_lock(&ide_setting_mtx); spin_lock_irqsave(&ide_lock, flags); - err = *val; + err = getfunc(drive); spin_unlock_irqrestore(&ide_lock, flags); mutex_unlock(&ide_setting_mtx); return err >= 0 ? put_user(err, (long __user *)arg) : err; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 02bd5c487d1f..65cf84b222c5 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -429,21 +429,41 @@ static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *r } #ifdef CONFIG_IDE_PROC_FS -static void idescsi_add_settings(ide_drive_t *drive) -{ - idescsi_scsi_t *scsi = drive_to_idescsi(drive); - -/* - * drive setting name read/write data type min max mul_factor div_factor data pointer set function - */ - ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); - ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); - ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); - ide_add_setting(drive, "transform", SETTING_RW, TYPE_INT, 0, 3, 1, 1, &scsi->transform, NULL); - ide_add_setting(drive, "log", SETTING_RW, TYPE_INT, 0, 1, 1, 1, &scsi->log, NULL); -} -#else -static inline void idescsi_add_settings(ide_drive_t *drive) { ; } +#define ide_scsi_devset_get(name, field) \ +static int get_##name(ide_drive_t *drive) \ +{ \ + idescsi_scsi_t *scsi = drive_to_idescsi(drive); \ + return scsi->field; \ +} + +#define ide_scsi_devset_set(name, field) \ +static int set_##name(ide_drive_t *drive, int arg) \ +{ \ + idescsi_scsi_t *scsi = drive_to_idescsi(drive); \ + scsi->field = arg; \ + return 0; \ +} + +#define ide_scsi_devset_rw(_name, _min, _max, _field) \ +ide_scsi_devset_get(_name, _field); \ +ide_scsi_devset_set(_name, _field); \ +IDE_DEVSET(_name, S_RW, _min, _max, get_##_name, set_##_name) + +ide_devset_rw(bios_cyl, 0, 1023, bios_cyl); +ide_devset_rw(bios_head, 0, 255, bios_head); +ide_devset_rw(bios_sect, 0, 63, bios_sect); + +ide_scsi_devset_rw(transform, 0, 3, transform); +ide_scsi_devset_rw(log, 0, 1, log); + +static const struct ide_devset *idescsi_settings[] = { + &ide_devset_bios_cyl, + &ide_devset_bios_head, + &ide_devset_bios_sect, + &ide_devset_log, + &ide_devset_transform, + NULL +}; #endif /* @@ -461,7 +481,6 @@ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi) drive->pc_callback = ide_scsi_callback; ide_proc_register_driver(drive, scsi->driver); - idescsi_add_settings(drive); } static void ide_scsi_remove(ide_drive_t *drive) @@ -509,6 +528,7 @@ static ide_driver_t idescsi_driver = { .error = idescsi_atapi_error, #ifdef CONFIG_IDE_PROC_FS .proc = idescsi_proc, + .settings = idescsi_settings, #endif }; diff --git a/include/linux/ide.h b/include/linux/ide.h index ad09e7c81ae9..4667ec8aeebb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -304,8 +304,8 @@ typedef enum { ide_started, /* a drive operation was started, handler was set */ } ide_startstop_t; +struct ide_devset; struct ide_driver_s; -struct ide_settings_s; #ifdef CONFIG_BLK_DEV_IDEACPI struct ide_acpi_drive_link; @@ -384,7 +384,7 @@ struct ide_drive_s { u16 *id; /* identification info */ #ifdef CONFIG_IDE_PROC_FS struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ - struct ide_settings_s *settings;/* /proc/ide/ drive settings */ + const struct ide_devset **settings; /* /proc/ide/ drive settings */ #endif struct hwif_s *hwif; /* actually (ide_hwif_t *) */ @@ -396,16 +396,16 @@ struct ide_drive_s { special_t special; /* special action flags */ select_t select; /* basic drive/head select reg value */ - u8 keep_settings; /* restore settings after drive reset */ - u8 using_dma; /* disk is using dma for read/write */ u8 retry_pio; /* retrying dma capable host in pio */ u8 state; /* retry state */ u8 waiting_for_dma; /* dma currently in progress */ - u8 unmask; /* okay to unmask other irqs */ - u8 noflush; /* don't attempt flushes */ - u8 dsc_overlap; /* DSC overlap */ - u8 nice1; /* give potential excess bandwidth */ + unsigned keep_settings : 1; /* restore settings after drive reset */ + unsigned using_dma : 1; /* disk is using dma for read/write */ + unsigned unmask : 1; /* okay to unmask other irqs */ + unsigned noflush : 1; /* don't attempt flushes */ + unsigned dsc_overlap : 1; /* DSC overlap */ + unsigned nice1 : 1; /* give potential excess bandwidth */ unsigned present : 1; /* drive is physically present */ unsigned dead : 1; /* device ejected hint */ unsigned id_read : 1; /* 1=id read from disk 0 = synthetic */ @@ -423,14 +423,15 @@ struct ide_drive_s { unsigned sleeping : 1; /* 1=sleeping & sleep field valid */ unsigned post_reset : 1; unsigned udma33_warned : 1; + unsigned addressing : 2; /* 0=28-bit, 1=48-bit, 2=48-bit doing 28-bit */ + unsigned wcache : 1; /* status of write cache */ + unsigned nowerr : 1; /* used for ignoring ATA_DF */ - u8 addressing; /* 0=28-bit, 1=48-bit, 2=48-bit doing 28-bit */ u8 quirk_list; /* considered quirky, set for a specific host */ u8 init_speed; /* transfer rate set at boot */ u8 current_speed; /* current transfer rate set */ u8 desired_speed; /* desired transfer rate set */ u8 dn; /* now wide spread use */ - u8 wcache; /* status of write cache */ u8 acoustic; /* acoustic management */ u8 media; /* disk, cdrom, tape, floppy, ... */ u8 ready_stat; /* min status value for drive ready */ @@ -439,7 +440,6 @@ struct ide_drive_s { u8 tune_req; /* requested drive tuning setting */ u8 io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ u8 bad_wstat; /* used for ignoring ATA_DF */ - u8 nowerr; /* used for ignoring ATA_DF */ u8 head; /* "real" number of heads */ u8 sect; /* "real" sectors per track */ u8 bios_head; /* BIOS/fdisk/LILO number of heads */ @@ -687,12 +687,29 @@ typedef struct ide_driver_s ide_driver_t; extern struct mutex ide_setting_mtx; +int get_io_32bit(ide_drive_t *); int set_io_32bit(ide_drive_t *, int); +int get_ksettings(ide_drive_t *); int set_ksettings(ide_drive_t *, int); int set_pio_mode(ide_drive_t *, int); +int get_unmaskirq(ide_drive_t *); int set_unmaskirq(ide_drive_t *, int); +int get_using_dma(ide_drive_t *); int set_using_dma(ide_drive_t *, int); +#define ide_devset_get(name, field) \ +int get_##name(ide_drive_t *drive) \ +{ \ + return drive->field; \ +} + +#define ide_devset_set(name, field) \ +int set_##name(ide_drive_t *drive, int arg) \ +{ \ + drive->field = arg; \ + return 0; \ +} + /* ATAPI packet command flags */ enum { /* set when an error is considered normal - no retry (ide-tape) */ @@ -757,30 +774,53 @@ struct ide_atapi_pc { * configurable drive settings */ -#define TYPE_INT 0 -#define TYPE_BYTE 1 -#define TYPE_SHORT 2 +#define S_READ (1 << 0) +#define S_WRITE (1 << 1) +#define S_RW (S_READ | S_WRITE) +#define S_NOLOCK (1 << 2) -#define SETTING_READ (1 << 0) -#define SETTING_WRITE (1 << 1) -#define SETTING_RW (SETTING_READ | SETTING_WRITE) +struct ide_devset { + const char *name; + unsigned int flags; + int min, max; + int (*get)(ide_drive_t *); + int (*set)(ide_drive_t *, int); + int (*mulf)(ide_drive_t *); + int (*divf)(ide_drive_t *); +}; -typedef int (ide_procset_t)(ide_drive_t *, int); -typedef struct ide_settings_s { - char *name; - int rw; - int data_type; - int min; - int max; - int mul_factor; - int div_factor; - void *data; - ide_procset_t *set; - int auto_remove; - struct ide_settings_s *next; -} ide_settings_t; - -int ide_add_setting(ide_drive_t *, const char *, int, int, int, int, int, int, void *, ide_procset_t *set); +#define __DEVSET(_name, _flags, _min, _max, _get, _set, _mulf, _divf) { \ + .name = __stringify(_name), \ + .flags = _flags, \ + .min = _min, \ + .max = _max, \ + .get = _get, \ + .set = _set, \ + .mulf = _mulf, \ + .divf = _divf, \ +} + +#define __IDE_DEVSET(_name, _flags, _min, _max, _get, _set, _mulf, _divf) \ +static const struct ide_devset ide_devset_##_name = \ + __DEVSET(_name, _flags, _min, _max, _get, _set, _mulf, _divf) + +#define IDE_DEVSET(_name, _flags, _min, _max, _get, _set) \ +__IDE_DEVSET(_name, _flags, _min, _max, _get, _set, NULL, NULL) + +#define ide_devset_rw_nolock(_name, _min, _max, _func) \ +IDE_DEVSET(_name, S_RW | S_NOLOCK, _min, _max, get_##_func, set_##_func) + +#define ide_devset_w_nolock(_name, _min, _max, _func) \ +IDE_DEVSET(_name, S_WRITE | S_NOLOCK, _min, _max, NULL, set_##_func) + +#define ide_devset_rw(_name, _min, _max, _field) \ +static ide_devset_get(_name, _field); \ +static ide_devset_set(_name, _field); \ +IDE_DEVSET(_name, S_RW, _min, _max, get_##_name, set_##_name) + +#define ide_devset_r(_name, _min, _max, _field) \ +ide_devset_get(_name, _field) \ +IDE_DEVSET(_name, S_READ, _min, _max, get_##_name, NULL) /* * /proc/ide interface @@ -801,8 +841,6 @@ void ide_proc_unregister_port(ide_hwif_t *); void ide_proc_register_driver(ide_drive_t *, ide_driver_t *); void ide_proc_unregister_driver(ide_drive_t *, ide_driver_t *); -void ide_add_generic_settings(ide_drive_t *); - read_proc_t proc_ide_read_capacity; read_proc_t proc_ide_read_geometry; @@ -830,7 +868,6 @@ static inline void ide_proc_unregister_device(ide_drive_t *drive) { ; } static inline void ide_proc_unregister_port(ide_hwif_t *hwif) { ; } static inline void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) { ; } static inline void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) { ; } -static inline void ide_add_generic_settings(ide_drive_t *drive) { ; } #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0; #endif @@ -887,6 +924,7 @@ struct ide_driver_s { void (*shutdown)(ide_drive_t *); #ifdef CONFIG_IDE_PROC_FS ide_proc_entry_t *proc; + const struct ide_devset **settings; #endif }; -- cgit v1.2.3 From 151a670186a0f8441798f90c8701647adb7a1589 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:28 +0200 Subject: ide: remove SECTOR_WORDS define Just use SECTOR_SIZE instead. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 5 +++-- drivers/ide/ide-iops.c | 2 +- drivers/ide/ide-probe.c | 2 +- drivers/ide/ide-proc.c | 5 +++-- drivers/ide/ide-taskfile.c | 2 +- include/linux/ide.h | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 2e43ae15fb1b..474070c1ddb6 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -539,13 +539,14 @@ static int proc_idedisk_read_smart(char *page, char **start, off_t off, if (get_smart_data(drive, page, sub_cmd) == 0) { unsigned short *val = (unsigned short *) page; - char *out = ((char *)val) + (SECTOR_WORDS * 4); + char *out = (char *)val + SECTOR_SIZE; + page = out; do { out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); val += 1; - } while (i < (SECTOR_WORDS * 2)); + } while (i < SECTOR_SIZE / 2); len = out - page; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index ee44878e40d0..6256c2df62cc 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -693,7 +693,7 @@ int ide_driveid_update(ide_drive_t *drive) } local_irq_save(flags); SELECT_MASK(drive, 0); - id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); + id = kmalloc(SECTOR_SIZE, GFP_ATOMIC); if (!id) { local_irq_restore(flags); return 0; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 62f7e1ef10c1..be121ffcc1dc 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -561,7 +561,7 @@ static inline u8 probe_for_drive (ide_drive_t *drive) * Also note that 0 everywhere means "can't do X" */ - drive->id = kzalloc(SECTOR_WORDS *4, GFP_KERNEL); + drive->id = kzalloc(SECTOR_SIZE, GFP_KERNEL); drive->id_read = 0; if(drive->id == NULL) { diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 5634b3971d21..6489c647be82 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -100,13 +100,14 @@ static int proc_ide_read_identify err = taskfile_lib_get_identify(drive, page); if (!err) { - char *out = ((char *)page) + (SECTOR_WORDS * 4); + char *out = (char *)page + SECTOR_SIZE; + page = out; do { out += sprintf(out, "%04x%c", le16_to_cpup(val), (++i & 7) ? ' ' : '\n'); val += 1; - } while (i < (SECTOR_WORDS * 2)); + } while (i < SECTOR_SIZE / 2); len = out - page; } } diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index b1fb815dbf68..7ffe9004a4d6 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -768,7 +768,7 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) if (args[3]) { tfargs.tf_flags |= IDE_TFLAG_IO_16BIT; - bufsize = SECTOR_WORDS * 4 * args[3]; + bufsize = SECTOR_SIZE * args[3]; buf = kzalloc(bufsize, GFP_KERNEL); if (buf == NULL) return -ENOMEM; diff --git a/include/linux/ide.h b/include/linux/ide.h index 4667ec8aeebb..4444b0884e5d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -126,7 +126,7 @@ struct ide_io_ports { #define PARTN_BITS 6 /* number of minor dev bits for partitions */ #define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */ #define SECTOR_SIZE 512 -#define SECTOR_WORDS (SECTOR_SIZE / 4) /* number of 32bit words per sector */ + #define IDE_LARGE_SEEK(b1,b2,t) (((b1) > (b2) + (t)) || ((b2) > (b1) + (t))) /* -- cgit v1.2.3 From ebc6be520673f65aef188abde43972f9cd2162e9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:29 +0200 Subject: ide: remove read-only ->atapi_overlap field from ide_drive_t Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide.c | 1 - include/linux/ide.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index eb64e942f58b..5b3bfc78dcdd 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -585,7 +585,6 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device return ide_get_identity_ioctl(drive, cmd, arg); case HDIO_GET_NICE: return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP | - drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP | drive->nice1 << IDE_NICE_1, (long __user *) arg); #ifdef CONFIG_IDE_TASK_IOCTL diff --git a/include/linux/ide.h b/include/linux/ide.h index 4444b0884e5d..9cb935f2e7ce 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -415,7 +415,6 @@ struct ide_drive_s { unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */ unsigned no_unmask : 1; /* disallow setting unmask bit */ unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ - unsigned atapi_overlap : 1; /* ATAPI overlap (not supported) */ unsigned doorlocking : 1; /* for removable only: door lock/unlock works */ unsigned nodma : 1; /* disallow DMA */ unsigned blocked : 1; /* 1=powermanagment told us not to do anything, so sleep nicely */ -- cgit v1.2.3 From 02d599a365d04658bc9ea71762ed17c895079927 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:29 +0200 Subject: ide: remove ->supports_dsc_overlap field from ide_driver_t * Use drive->media and drive->scsi to check if ->dsc_overlap can be set by HDIO_SET_NICE ioctl in generic_ide_ioctl(). * Remove unused ->supports_dsc_overlap field from ide_driver_t. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 1 - drivers/ide/ide-disk.c | 1 - drivers/ide/ide-floppy.c | 1 - drivers/ide/ide-tape.c | 1 - drivers/ide/ide.c | 11 +++++------ drivers/scsi/ide-scsi.c | 1 - include/linux/ide.h | 1 - 7 files changed, 5 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 1f5652326489..7ea90de55058 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1969,7 +1969,6 @@ static ide_driver_t ide_cdrom_driver = { .remove = ide_cd_remove, .version = IDECD_VERSION, .media = ide_cdrom, - .supports_dsc_overlap = 1, .do_request = ide_cd_do_request, .end_request = ide_end_request, .error = __ide_error, diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 474070c1ddb6..08f47cb13425 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -980,7 +980,6 @@ static ide_driver_t idedisk_driver = { .shutdown = ide_device_shutdown, .version = IDEDISK_VERSION, .media = ide_disk, - .supports_dsc_overlap = 0, .do_request = ide_do_rw_disk, .end_request = ide_end_request, .error = __ide_error, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 673644fdb6f2..de8d42b3f698 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -1130,7 +1130,6 @@ static ide_driver_t idefloppy_driver = { .remove = ide_floppy_remove, .version = IDEFLOPPY_VERSION, .media = ide_floppy, - .supports_dsc_overlap = 0, .do_request = idefloppy_do_request, .end_request = idefloppy_end_request, .error = __ide_error, diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 7037accb0589..1fc1c2a6888f 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2607,7 +2607,6 @@ static ide_driver_t idetape_driver = { .remove = ide_tape_remove, .version = IDETAPE_VERSION, .media = ide_tape, - .supports_dsc_overlap = 1, .do_request = idetape_do_request, .end_request = idetape_end_request, .error = __ide_error, diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 5b3bfc78dcdd..4d0c661e78a5 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -562,7 +562,6 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device unsigned int cmd, unsigned long arg) { unsigned long flags; - ide_driver_t *drv; int err = 0, (*getfunc)(ide_drive_t *), (*setfunc)(ide_drive_t *, int); switch (cmd) { @@ -612,12 +611,12 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) return -EPERM; - drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; - drv = *(ide_driver_t **)bdev->bd_disk->private_data; - if (drive->dsc_overlap && !drv->supports_dsc_overlap) { - drive->dsc_overlap = 0; + if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && + (drive->media == ide_disk || + drive->media == ide_floppy || + drive->scsi)) return -EPERM; - } + drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; drive->nice1 = (arg >> IDE_NICE_1) & 1; return 0; case HDIO_DRIVE_RESET: diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 65cf84b222c5..a2d003c5e85f 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -522,7 +522,6 @@ static ide_driver_t idescsi_driver = { .remove = ide_scsi_remove, .version = IDESCSI_VERSION, .media = ide_scsi, - .supports_dsc_overlap = 0, .do_request = idescsi_do_request, .end_request = idescsi_end_request, .error = idescsi_atapi_error, diff --git a/include/linux/ide.h b/include/linux/ide.h index 9cb935f2e7ce..e5622bb5a4a1 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -912,7 +912,6 @@ enum { struct ide_driver_s { const char *version; u8 media; - unsigned supports_dsc_overlap : 1; ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t); int (*end_request)(ide_drive_t *, int, int); ide_startstop_t (*error)(ide_drive_t *, struct request *rq, u8, u8); -- cgit v1.2.3 From 5d5870f0a26e2304c4a82592870c5bc88017f7c9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:29 +0200 Subject: ide: ide_dev_has_iordy() -> ata_id_has_iordy() * Remove (id[ATA_ID_FIELD_VALID] & 2) check from ide_dev_has_iordy() (it is for validity of words 64-70, IORDY is in word 49). * ide_dev_has_iordy() -> ata_id_has_iordy() Cc: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pci/sl82c105.c | 2 +- include/linux/ide.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 61a006cb4746..8cc4e137c608 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -61,7 +61,7 @@ static unsigned int get_pio_timings(ide_drive_t *drive, u8 pio) if (cmd_off == 0) cmd_off = 1; - if (pio > 2 || ide_dev_has_iordy(drive->id)) + if (pio > 2 || ata_id_has_iordy(drive->id)) iordy = 0x40; return (cmd_on - 1) << 8 | (cmd_off - 1) | iordy; diff --git a/include/linux/ide.h b/include/linux/ide.h index e5622bb5a4a1..27829c13bef7 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1382,11 +1382,6 @@ const char *ide_xfer_verbose(u8 mode); extern void ide_toggle_bounce(ide_drive_t *drive, int on); extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate); -static inline int ide_dev_has_iordy(u16 *id) -{ - return ((id[ATA_ID_FIELD_VALID] & 2) && ata_id_has_iordy(id)) ? 1 : 0; -} - static inline int ide_dev_is_sata(u16 *id) { /* -- cgit v1.2.3 From 367d7e78dd48cf6ad35182a99d97abb5486e040e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:30 +0200 Subject: ide: ide_dev_is_sata() -> ata_id_is_sata() * Use optimized ATA version check from Sergei in ata_id_is_sata(). * ide_dev_is_sata() -> ata_id_is_sata() Cc: Jeff Garzik Cc: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-iops.c | 2 +- drivers/ide/pci/hpt366.c | 4 ++-- include/linux/ata.h | 10 +++++++++- include/linux/ide.h | 13 ------------- 4 files changed, 12 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 6256c2df62cc..0a2fd3b37ac4 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -630,7 +630,7 @@ u8 eighty_ninty_three (ide_drive_t *drive) printk(KERN_DEBUG "%s: skipping word 93 validity check\n", drive->name); - if (ide_dev_is_sata(id) && !ivb) + if (ata_id_is_sata(id) && !ivb) return 1; if (hwif->cbl != ATA_CBL_PATA80 && !ivb) diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index d706eb6b8acd..9056e3acc78b 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -654,7 +654,7 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive) case HPT372A: case HPT372N: case HPT374 : - if (ide_dev_is_sata(drive->id)) + if (ata_id_is_sata(drive->id)) mask &= ~0x0e; /* Fall thru */ default: @@ -674,7 +674,7 @@ static u8 hpt3xx_mdma_filter(ide_drive_t *drive) case HPT372A: case HPT372N: case HPT374 : - if (ide_dev_is_sata(drive->id)) + if (ata_id_is_sata(drive->id)) return 0x00; /* Fall thru */ default: diff --git a/include/linux/ata.h b/include/linux/ata.h index be00973d1a8c..d28aad991c75 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -645,7 +645,15 @@ static inline unsigned int ata_id_major_version(const u16 *id) static inline int ata_id_is_sata(const u16 *id) { - return ata_id_major_version(id) >= 5 && id[ATA_ID_HW_CONFIG] == 0; + /* + * See if word 93 is 0 AND drive is at least ATA-5 compatible + * verifying that word 80 by casting it to a signed type -- + * this trick allows us to filter out the reserved values of + * 0x0000 and 0xffff along with the earlier ATA revisions... + */ + if (id[ATA_ID_HW_CONFIG] == 0 && (short)id[ATA_ID_MAJOR_VER] >= 0x0020) + return 1; + return 0; } static inline int ata_id_has_tpm(const u16 *id) diff --git a/include/linux/ide.h b/include/linux/ide.h index 27829c13bef7..87b5b5d39539 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1382,19 +1382,6 @@ const char *ide_xfer_verbose(u8 mode); extern void ide_toggle_bounce(ide_drive_t *drive, int on); extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate); -static inline int ide_dev_is_sata(u16 *id) -{ - /* - * See if word 93 is 0 AND drive is at least ATA-5 compatible - * verifying that word 80 by casting it to a signed type -- - * this trick allows us to filter out the reserved values of - * 0x0000 and 0xffff along with the earlier ATA revisions... - */ - if (id[ATA_ID_HW_CONFIG] == 0 && (short)id[ATA_ID_MAJOR_VER] >= 0x0020) - return 1; - return 0; -} - u64 ide_get_lba_addr(struct ide_taskfile *, int); u8 ide_dump_status(ide_drive_t *, const char *, u8); -- cgit v1.2.3 From 942dcd85bf8edf38cdc3745306ca250684d99a61 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:30 +0200 Subject: ide: idedisk_supports_lba48() -> ata_id_lba48_enabled() * Add ata_id_lba48_enabled() inline helper to . * idedisk_supports_lba48() -> ata_id_lba48_enabled() The latter one also checks validity of words 83 & 86. Cc: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 21 ++++++--------------- include/linux/ata.h | 9 +++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 08f47cb13425..8f49bc0ecbf8 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -383,16 +383,6 @@ static unsigned long long sectors_to_MB(unsigned long long n) return n; } -/* - * The same here. - */ -static inline int idedisk_supports_lba48(const u16 *id) -{ - return (id[ATA_ID_COMMAND_SET_2] & 0x0400) && - (id[ATA_ID_CFS_ENABLE_2] & 0x0400) && - ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); -} - /* * Some disks report total number of sectors instead of * maximum sector address. We list them here. @@ -407,7 +397,7 @@ static const struct drive_list_entry hpa_list[] = { static void idedisk_check_hpa(ide_drive_t *drive) { unsigned long long capacity, set_max; - int lba48 = idedisk_supports_lba48(drive->id); + int lba48 = ata_id_lba48_enabled(drive->id); capacity = drive->capacity64; @@ -450,7 +440,7 @@ static void init_idedisk_capacity(ide_drive_t *drive) */ int hpa = ata_id_hpa_enabled(id); - if (idedisk_supports_lba48(id)) { + if (ata_id_lba48_enabled(id)) { /* drive speaks 48-bit LBA */ drive->select.b.lba = 1; drive->capacity64 = ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); @@ -754,9 +744,11 @@ static int set_lba_addressing(ide_drive_t *drive, int arg) if (drive->hwif->host_flags & IDE_HFLAG_NO_LBA48) return 0; - if (!idedisk_supports_lba48(drive->id)) + if (ata_id_lba48_enabled(drive->id) == 0) return -EIO; + drive->addressing = arg; + return 0; } @@ -853,8 +845,7 @@ static void idedisk_setup(ide_drive_t *drive) capacity = idedisk_capacity(drive); if (!drive->forced_geom) { - - if (idedisk_supports_lba48(drive->id)) { + if (ata_id_lba48_enabled(drive->id)) { /* compatibility */ drive->bios_sect = 63; drive->bios_head = 255; diff --git a/include/linux/ata.h b/include/linux/ata.h index d28aad991c75..8162257b474f 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -574,6 +574,15 @@ static inline int ata_id_has_lba48(const u16 *id) return id[ATA_ID_COMMAND_SET_2] & (1 << 10); } +static inline int ata_id_lba48_enabled(const u16 *id) +{ + if (ata_id_has_lba48(id) == 0) + return 0; + if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000) + return 0; + return id[ATA_ID_CFS_ENABLE_2] & (1 << 10); +} + static inline int ata_id_hpa_enabled(const u16 *id) { /* Yes children, word 83 valid bits cover word 82 data */ -- cgit v1.2.3 From 1a4e4d4d2cceb72be70526a485914abd638c7de1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:30 +0200 Subject: ide: check only for CACHE FLUSH command support in ide_id_has_flush_cache() All devices supporting CACHE FLUSH EXT command should also support CACHE FLUSH command so it is sufficient to check only for CACHE FLUSH in ide_id_has_flush_cache(). Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 87b5b5d39539..6e22cd20dd8b 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1453,8 +1453,8 @@ extern struct mutex ide_cfg_mtx; extern struct bus_type ide_bus_type; extern struct class *ide_port_class; -/* check if CACHE FLUSH (EXT) command is supported (bits defined in ATA-6) */ -#define ide_id_has_flush_cache(id) ((id)[ATA_ID_CFS_ENABLE_2] & 0x3000) +/* check if CACHE FLUSH command is supported (as defined in ATA-6) */ +#define ide_id_has_flush_cache(id) ((id)[ATA_ID_CFS_ENABLE_2] & 0x1000) /* some Maxtor disks have bit 13 defined incorrectly so check bit 10 too */ #define ide_id_has_flush_cache_ext(id) \ -- cgit v1.2.3 From 4b58f17d7c45a8e5f4acda641bec388398b9c0fa Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:30 +0200 Subject: ide: ide_id_has_flush_cache() -> ata_id_flush_enabled() * Add ata_id_flush_enabled() inline helper to . * ide_id_has_flush_cache() -> ata_id_flush_enabled() The latter one also checks if the command is marked as supported in word 83 and validity of words 83 & 86. Cc: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 6 +++--- drivers/ide/ide-io.c | 2 +- include/linux/ata.h | 9 +++++++++ include/linux/ide.h | 3 --- 4 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 8f49bc0ecbf8..7b24dff17460 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -651,7 +651,7 @@ static void update_ordered(ide_drive_t *drive) * not available so we don't need to recheck that. */ capacity = idedisk_capacity(drive); - barrier = ide_id_has_flush_cache(id) && !drive->noflush && + barrier = ata_id_flush_enabled(id) && !drive->noflush && (drive->addressing == 0 || capacity <= (1ULL << 28) || ide_id_has_flush_cache_ext(id)); @@ -678,7 +678,7 @@ static int set_wcache(ide_drive_t *drive, int arg) if (arg < 0 || arg > 1) return -EINVAL; - if (ide_id_has_flush_cache(drive->id)) { + if (ata_id_flush_enabled(drive->id)) { memset(&args, 0, sizeof(ide_task_t)); args.tf.feature = arg ? SETFEATURES_WC_ON : SETFEATURES_WC_OFF; @@ -886,7 +886,7 @@ static void idedisk_setup(ide_drive_t *drive) static void ide_cacheflush_p(ide_drive_t *drive) { - if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) + if (!drive->wcache || ata_id_flush_enabled(drive->id) == 0) return; if (do_idedisk_flushcache(drive)) diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 99e0bbca3ace..c1596178fcae 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -184,7 +184,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * if (drive->media != ide_disk) break; /* Not supported? Switch to next step now. */ - if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) { + if (!drive->wcache || ata_id_flush_enabled(drive->id) == 0) { ide_complete_power_step(drive, rq, 0, 0); return ide_stopped; } diff --git a/include/linux/ata.h b/include/linux/ata.h index 8162257b474f..921cf0fc337f 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -558,6 +558,15 @@ static inline int ata_id_has_flush(const u16 *id) return id[ATA_ID_COMMAND_SET_2] & (1 << 12); } +static inline int ata_id_flush_enabled(const u16 *id) +{ + if (ata_id_has_flush(id) == 0) + return 0; + if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000) + return 0; + return id[ATA_ID_CFS_ENABLE_2] & (1 << 12); +} + static inline int ata_id_has_flush_ext(const u16 *id) { if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) diff --git a/include/linux/ide.h b/include/linux/ide.h index 6e22cd20dd8b..d2213d7cc4cb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1453,9 +1453,6 @@ extern struct mutex ide_cfg_mtx; extern struct bus_type ide_bus_type; extern struct class *ide_port_class; -/* check if CACHE FLUSH command is supported (as defined in ATA-6) */ -#define ide_id_has_flush_cache(id) ((id)[ATA_ID_CFS_ENABLE_2] & 0x1000) - /* some Maxtor disks have bit 13 defined incorrectly so check bit 10 too */ #define ide_id_has_flush_cache_ext(id) \ (((id)[ATA_ID_CFS_ENABLE_2] & 0x2400) == 0x2400) -- cgit v1.2.3 From ff2779b568e70822e0ef2cc7afeeefbe7c607652 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:31 +0200 Subject: ide: ide_id_has_flush_cache_ext() -> ata_id_flush_ext_enabled() * Add ata_id_flush_ext_enabled() inline helper to . * ide_id_has_flush_cache_ext() -> ata_id_flush_ext_enabled() The latter one also checks if the command is marked as supported in word 83 and validity of words 83 & 86. Cc: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 6 +++--- drivers/ide/ide-io.c | 2 +- include/linux/ata.h | 13 +++++++++++++ include/linux/ide.h | 4 ---- 4 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 7b24dff17460..587d5aac7d5d 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -576,7 +576,7 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq) BUG_ON(task == NULL); memset(task, 0, sizeof(*task)); - if (ide_id_has_flush_cache_ext(drive->id) && + if (ata_id_flush_ext_enabled(drive->id) && (drive->capacity64 >= (1UL << 28))) task->tf.command = ATA_CMD_FLUSH_EXT; else @@ -653,7 +653,7 @@ static void update_ordered(ide_drive_t *drive) capacity = idedisk_capacity(drive); barrier = ata_id_flush_enabled(id) && !drive->noflush && (drive->addressing == 0 || capacity <= (1ULL << 28) || - ide_id_has_flush_cache_ext(id)); + ata_id_flush_ext_enabled(id)); printk(KERN_INFO "%s: cache flushes %ssupported\n", drive->name, barrier ? "" : "not "); @@ -699,7 +699,7 @@ static int do_idedisk_flushcache(ide_drive_t *drive) ide_task_t args; memset(&args, 0, sizeof(ide_task_t)); - if (ide_id_has_flush_cache_ext(drive->id)) + if (ata_id_flush_ext_enabled(drive->id)) args.tf.command = ATA_CMD_FLUSH_EXT; else args.tf.command = ATA_CMD_FLUSH; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index c1596178fcae..ec6664b0d3a9 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -188,7 +188,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * ide_complete_power_step(drive, rq, 0, 0); return ide_stopped; } - if (ide_id_has_flush_cache_ext(drive->id)) + if (ata_id_flush_ext_enabled(drive->id)) args->tf.command = ATA_CMD_FLUSH_EXT; else args->tf.command = ATA_CMD_FLUSH; diff --git a/include/linux/ata.h b/include/linux/ata.h index 921cf0fc337f..81d9adeb819e 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -574,6 +574,19 @@ static inline int ata_id_has_flush_ext(const u16 *id) return id[ATA_ID_COMMAND_SET_2] & (1 << 13); } +static inline int ata_id_flush_ext_enabled(const u16 *id) +{ + if (ata_id_has_flush_ext(id) == 0) + return 0; + if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000) + return 0; + /* + * some Maxtor disks have bit 13 defined incorrectly + * so check bit 10 too + */ + return (id[ATA_ID_CFS_ENABLE_2] & 0x2400) == 0x2400; +} + static inline int ata_id_has_lba48(const u16 *id) { if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000) diff --git a/include/linux/ide.h b/include/linux/ide.h index d2213d7cc4cb..432eb98f7fe7 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1453,10 +1453,6 @@ extern struct mutex ide_cfg_mtx; extern struct bus_type ide_bus_type; extern struct class *ide_port_class; -/* some Maxtor disks have bit 13 defined incorrectly so check bit 10 too */ -#define ide_id_has_flush_cache_ext(id) \ - (((id)[ATA_ID_CFS_ENABLE_2] & 0x2400) == 0x2400) - static inline void ide_dump_identify(u8 *id) { print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 2, id, 512, 0); -- cgit v1.2.3 From 93734a234447a3c091f76d76f7351af9d4dde518 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:31 +0200 Subject: ide: ide_id_to_hd_driveid() -> ata_id_to_hd_driveid() Rename ide_id_to_hd_driveid() to ata_id_to_hd_driveid() and move it to . Cc: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide.c | 22 +--------------------- include/linux/ata.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 12618d2aed2c..f42de5fe9bc2 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -509,26 +509,6 @@ static int generic_drive_reset(ide_drive_t *drive) return ret; } -static inline void ide_id_to_hd_driveid(u16 *id) -{ -#ifdef __BIG_ENDIAN - /* accessed in struct hd_driveid as 8-bit values */ - id[ATA_ID_MAX_MULTSECT] = __cpu_to_le16(id[ATA_ID_MAX_MULTSECT]); - id[ATA_ID_CAPABILITY] = __cpu_to_le16(id[ATA_ID_CAPABILITY]); - id[ATA_ID_OLD_PIO_MODES] = __cpu_to_le16(id[ATA_ID_OLD_PIO_MODES]); - id[ATA_ID_OLD_DMA_MODES] = __cpu_to_le16(id[ATA_ID_OLD_DMA_MODES]); - id[ATA_ID_MULTSECT] = __cpu_to_le16(id[ATA_ID_MULTSECT]); - - /* as 32-bit values */ - *(u32 *)&id[ATA_ID_LBA_CAPACITY] = ata_id_u32(id, ATA_ID_LBA_CAPACITY); - *(u32 *)&id[ATA_ID_SPG] = ata_id_u32(id, ATA_ID_SPG); - - /* as 64-bit value */ - *(u64 *)&id[ATA_ID_LBA_CAPACITY_2] = - ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); -#endif -} - static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, unsigned long arg) { @@ -548,7 +528,7 @@ static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, } memcpy(id, drive->id, size); - ide_id_to_hd_driveid(id); + ata_id_to_hd_driveid(id); if (copy_to_user((void __user *)arg, id, size)) rc = -EFAULT; diff --git a/include/linux/ata.h b/include/linux/ata.h index 81d9adeb819e..4c3f50070033 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -30,6 +30,7 @@ #define __LINUX_ATA_H__ #include +#include /* defines only for the constants which don't work well as enums */ #define ATA_DMA_BOUNDARY 0xffffUL @@ -781,6 +782,26 @@ static inline int atapi_id_dmadir(const u16 *dev_id) return ata_id_major_version(dev_id) >= 7 && (dev_id[62] & 0x8000); } +static inline void ata_id_to_hd_driveid(u16 *id) +{ +#ifdef __BIG_ENDIAN + /* accessed in struct hd_driveid as 8-bit values */ + id[ATA_ID_MAX_MULTSECT] = __cpu_to_le16(id[ATA_ID_MAX_MULTSECT]); + id[ATA_ID_CAPABILITY] = __cpu_to_le16(id[ATA_ID_CAPABILITY]); + id[ATA_ID_OLD_PIO_MODES] = __cpu_to_le16(id[ATA_ID_OLD_PIO_MODES]); + id[ATA_ID_OLD_DMA_MODES] = __cpu_to_le16(id[ATA_ID_OLD_DMA_MODES]); + id[ATA_ID_MULTSECT] = __cpu_to_le16(id[ATA_ID_MULTSECT]); + + /* as 32-bit values */ + *(u32 *)&id[ATA_ID_LBA_CAPACITY] = ata_id_u32(id, ATA_ID_LBA_CAPACITY); + *(u32 *)&id[ATA_ID_SPG] = ata_id_u32(id, ATA_ID_SPG); + + /* as 64-bit value */ + *(u64 *)&id[ATA_ID_LBA_CAPACITY_2] = + ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); +#endif +} + static inline int is_multi_taskfile(struct ata_taskfile *tf) { return (tf->command == ATA_CMD_READ_MULTI) || -- cgit v1.2.3 From a02227c9774b3bff08c7f557d06247e0a03ac435 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:31 +0200 Subject: ide: lba_capacity_is_ok() -> ata_id_is_lba_capacity_ok() Rename lba_capacity_is_ok() to ata_id_is_lba_capacity_ok() and move it to (remove needless parens while at it). Cc: Jeff Garzik Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 52 +------------------------------------------------- include/linux/ata.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 51 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 587d5aac7d5d..43025c9d8355 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -88,56 +88,6 @@ static void ide_disk_put(struct ide_disk_obj *idkp) mutex_unlock(&idedisk_ref_mutex); } -/* - * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity" - * value for this drive (from its reported identification information). - * - * Returns: 1 if lba_capacity looks sensible - * 0 otherwise - * - * It is called only once for each drive. - */ -static int lba_capacity_is_ok(u16 *id) -{ - unsigned long lba_sects, chs_sects, head, tail; - - /* No non-LBA info .. so valid! */ - if (id[ATA_ID_CYLS] == 0) - return 1; - - lba_sects = ata_id_u32(id, ATA_ID_LBA_CAPACITY); - - /* - * The ATA spec tells large drives to return - * C/H/S = 16383/16/63 independent of their size. - * Some drives can be jumpered to use 15 heads instead of 16. - * Some drives can be jumpered to use 4092 cyls instead of 16383. - */ - if ((id[ATA_ID_CYLS] == 16383 || - (id[ATA_ID_CYLS] == 4092 && id[ATA_ID_CUR_CYLS] == 16383)) && - id[ATA_ID_SECTORS] == 63 && - (id[ATA_ID_HEADS] == 15 || id[ATA_ID_HEADS] == 16) && - (lba_sects >= 16383 * 63 * id[ATA_ID_HEADS])) - return 1; - - chs_sects = id[ATA_ID_CYLS] * id[ATA_ID_HEADS] * id[ATA_ID_SECTORS]; - - /* perform a rough sanity check on lba_sects: within 10% is OK */ - if ((lba_sects - chs_sects) < chs_sects/10) - return 1; - - /* some drives have the word order reversed */ - head = ((lba_sects >> 16) & 0xffff); - tail = (lba_sects & 0xffff); - lba_sects = (head | (tail << 16)); - if ((lba_sects - chs_sects) < chs_sects/10) { - *(__le32 *)&id[ATA_ID_LBA_CAPACITY] = __cpu_to_le32(lba_sects); - return 1; /* lba_capacity is (now) good */ - } - - return 0; /* lba_capacity value may be bad */ -} - static const u8 ide_rw_cmds[] = { ATA_CMD_READ_MULTI, ATA_CMD_WRITE_MULTI, @@ -446,7 +396,7 @@ static void init_idedisk_capacity(ide_drive_t *drive) drive->capacity64 = ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); if (hpa) idedisk_check_hpa(drive); - } else if (ata_id_has_lba(id) && lba_capacity_is_ok(id)) { + } else if (ata_id_has_lba(id) && ata_id_is_lba_capacity_ok(id)) { /* drive speaks 28-bit LBA */ drive->select.b.lba = 1; drive->capacity64 = ata_id_u32(id, ATA_ID_LBA_CAPACITY); diff --git a/include/linux/ata.h b/include/linux/ata.h index 4c3f50070033..a53318b8cbd0 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -782,6 +782,56 @@ static inline int atapi_id_dmadir(const u16 *dev_id) return ata_id_major_version(dev_id) >= 7 && (dev_id[62] & 0x8000); } +/* + * ata_id_is_lba_capacity_ok() performs a sanity check on + * the claimed LBA capacity value for the device. + * + * Returns 1 if LBA capacity looks sensible, 0 otherwise. + * + * It is called only once for each device. + */ +static inline int ata_id_is_lba_capacity_ok(u16 *id) +{ + unsigned long lba_sects, chs_sects, head, tail; + + /* No non-LBA info .. so valid! */ + if (id[ATA_ID_CYLS] == 0) + return 1; + + lba_sects = ata_id_u32(id, ATA_ID_LBA_CAPACITY); + + /* + * The ATA spec tells large drives to return + * C/H/S = 16383/16/63 independent of their size. + * Some drives can be jumpered to use 15 heads instead of 16. + * Some drives can be jumpered to use 4092 cyls instead of 16383. + */ + if ((id[ATA_ID_CYLS] == 16383 || + (id[ATA_ID_CYLS] == 4092 && id[ATA_ID_CUR_CYLS] == 16383)) && + id[ATA_ID_SECTORS] == 63 && + (id[ATA_ID_HEADS] == 15 || id[ATA_ID_HEADS] == 16) && + (lba_sects >= 16383 * 63 * id[ATA_ID_HEADS])) + return 1; + + chs_sects = id[ATA_ID_CYLS] * id[ATA_ID_HEADS] * id[ATA_ID_SECTORS]; + + /* perform a rough sanity check on lba_sects: within 10% is OK */ + if (lba_sects - chs_sects < chs_sects/10) + return 1; + + /* some drives have the word order reversed */ + head = (lba_sects >> 16) & 0xffff; + tail = lba_sects & 0xffff; + lba_sects = head | (tail << 16); + + if (lba_sects - chs_sects < chs_sects/10) { + *(__le32 *)&id[ATA_ID_LBA_CAPACITY] = __cpu_to_le32(lba_sects); + return 1; /* LBA capacity is (now) good */ + } + + return 0; /* LBA capacity value may be bad */ +} + static inline void ata_id_to_hd_driveid(u16 *id) { #ifdef __BIG_ENDIAN -- cgit v1.2.3 From feb22b7f8e62b1b987a3a1dbad95af767a1df832 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:32 +0200 Subject: ide: add proper PCI PM support (v2) * Keep pointer to ->init_chipset method also in struct ide_host and set it in ide_host_alloc_all(). * Add ide_pci_suspend() and ide_pci_resume() helpers (default ->suspend and ->resume implementations). * ->init_chipset can no longer be marked __devinit. * Add proper PCI PM support to IDE PCI host drivers (rz1000.c and tc86c001.c are skipped for now since they need to be converted from using ->init_hwif to use ->init_chipset instead). v2: * Cleanup CONFIG_PM #ifdef-s per akpm's suggestion. Cc: Andrew Morton Cc: "Rafael J. Wysocki" Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 4 +++- drivers/ide/pci/aec62xx.c | 4 +++- drivers/ide/pci/alim15x3.c | 4 +++- drivers/ide/pci/amd74xx.c | 8 +++++--- drivers/ide/pci/atiixp.c | 2 ++ drivers/ide/pci/cmd64x.c | 4 +++- drivers/ide/pci/cs5520.c | 2 ++ drivers/ide/pci/cs5530.c | 4 +++- drivers/ide/pci/cs5535.c | 10 ++++++---- drivers/ide/pci/cy82c693.c | 4 +++- drivers/ide/pci/generic.c | 2 ++ drivers/ide/pci/hpt34x.c | 4 +++- drivers/ide/pci/hpt366.c | 8 +++++--- drivers/ide/pci/it8213.c | 2 ++ drivers/ide/pci/it821x.c | 6 ++++-- drivers/ide/pci/jmicron.c | 2 ++ drivers/ide/pci/ns87415.c | 2 ++ drivers/ide/pci/opti621.c | 2 ++ drivers/ide/pci/pdc202xx_new.c | 10 ++++++---- drivers/ide/pci/pdc202xx_old.c | 4 +++- drivers/ide/pci/piix.c | 4 +++- drivers/ide/pci/serverworks.c | 4 +++- drivers/ide/pci/siimage.c | 4 +++- drivers/ide/pci/sis5513.c | 4 +++- drivers/ide/pci/sl82c105.c | 4 +++- drivers/ide/pci/slc90e66.c | 2 ++ drivers/ide/pci/triflex.c | 2 ++ drivers/ide/pci/via82cxxx.c | 6 ++++-- drivers/ide/setup-pci.c | 33 +++++++++++++++++++++++++++++++++ include/linux/ide.h | 10 ++++++++++ 30 files changed, 130 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index e526f4967148..06575a12b635 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1586,8 +1586,10 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, if (hws[0]) host->dev[0] = hws[0]->dev; - if (d) + if (d) { + host->init_chipset = d->init_chipset; host->host_flags = d->host_flags; + } return host; } diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index f65828da65d0..e7475ba559c7 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -139,7 +139,7 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0); } -static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev) +static unsigned int init_chipset_aec62xx(struct pci_dev *dev) { /* These are necessary to get AEC6280 Macintosh cards to work */ if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) || @@ -307,6 +307,8 @@ static struct pci_driver driver = { .id_table = aec62xx_pci_tbl, .probe = aec62xx_init_one, .remove = __devexit_p(aec62xx_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init aec62xx_ide_init(void) diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index ef41e2677a56..053c75263918 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -213,7 +213,7 @@ static int ali15x3_dma_setup(ide_drive_t *drive) * appropriate also sets up the 1533 southbridge. */ -static unsigned int __devinit init_chipset_ali15x3(struct pci_dev *dev) +static unsigned int init_chipset_ali15x3(struct pci_dev *dev) { unsigned long flags; u8 tmpbyte; @@ -581,6 +581,8 @@ static struct pci_driver driver = { .id_table = alim15x3_pci_tbl, .probe = alim15x3_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init ali15x3_ide_init(void) diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index 1e66a960a96a..824471f91bf5 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -112,13 +112,13 @@ static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio) amd_set_drive(drive, XFER_PIO_0 + pio); } -static void __devinit amd7409_cable_detect(struct pci_dev *dev) +static void amd7409_cable_detect(struct pci_dev *dev) { /* no host side cable detection */ amd_80w = 0x03; } -static void __devinit amd7411_cable_detect(struct pci_dev *dev) +static void amd7411_cable_detect(struct pci_dev *dev) { int i; u32 u = 0; @@ -140,7 +140,7 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev) * The initialization callback. Initialize drive independent registers. */ -static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev) +static unsigned int init_chipset_amd74xx(struct pci_dev *dev) { u8 t = 0, offset = amd_offset(dev); @@ -324,6 +324,8 @@ static struct pci_driver driver = { .id_table = amd74xx_pci_tbl, .probe = amd74xx_probe, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init amd74xx_ide_init(void) diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 86e3120cb7c1..e4437034dd08 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -187,6 +187,8 @@ static struct pci_driver driver = { .id_table = atiixp_pci_tbl, .probe = atiixp_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init atiixp_ide_init(void) diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index 13dfeab1d19f..456dee18b660 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -331,7 +331,7 @@ static int cmd646_1_dma_end(ide_drive_t *drive) return (dma_stat & 7) != 4; } -static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev) +static unsigned int init_chipset_cmd64x(struct pci_dev *dev) { u8 mrdmode = 0; @@ -510,6 +510,8 @@ static struct pci_driver driver = { .id_table = cmd64x_pci_tbl, .probe = cmd64x_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init cmd64x_ide_init(void) diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index 27163147896b..d6341f7c4144 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -149,6 +149,8 @@ static struct pci_driver driver = { .name = "Cyrix_IDE", .id_table = cs5520_pci_tbl, .probe = cs5520_init_one, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init cs5520_ide_init(void) diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index d60806bd7dba..da42fa7e9f97 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -134,7 +134,7 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode) * Initialize the cs5530 bridge for reliable IDE DMA operation. */ -static unsigned int __devinit init_chipset_cs5530(struct pci_dev *dev) +static unsigned int init_chipset_cs5530(struct pci_dev *dev) { struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; @@ -272,6 +272,8 @@ static struct pci_driver driver = { .id_table = cs5530_pci_tbl, .probe = cs5530_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init cs5530_ide_init(void) diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index 707d2e182552..1e5bc59ea2fb 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -193,10 +193,12 @@ static const struct pci_device_id cs5535_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, cs5535_pci_tbl); static struct pci_driver driver = { - .name = "CS5535_IDE", - .id_table = cs5535_pci_tbl, - .probe = cs5535_init_one, - .remove = ide_pci_remove, + .name = "CS5535_IDE", + .id_table = cs5535_pci_tbl, + .probe = cs5535_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init cs5535_ide_init(void) diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index e6d8ee88d56d..69820e9224d1 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -332,7 +332,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) /* * this function is called during init and is used to setup the cy82c693 chip */ -static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev) +static unsigned int init_chipset_cy82c693(struct pci_dev *dev) { if (PCI_FUNC(dev->devfn) != 1) return 0; @@ -448,6 +448,8 @@ static struct pci_driver driver = { .id_table = cy82c693_pci_tbl, .probe = cy82c693_init_one, .remove = __devexit_p(cy82c693_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init cy82c693_ide_init(void) diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index bdc539868701..092b238cb250 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -171,6 +171,8 @@ static struct pci_driver driver = { .id_table = generic_pci_tbl, .probe = generic_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init generic_ide_init(void) diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 4f624899f44a..644de29f8fe4 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -78,7 +78,7 @@ static void hpt34x_set_pio_mode(ide_drive_t *drive, const u8 pio) */ #define HPT34X_PCI_INIT_REG 0x80 -static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev) +static unsigned int init_chipset_hpt34x(struct pci_dev *dev) { int i = 0; unsigned long hpt34xIoBase = pci_resource_start(dev, 4); @@ -171,6 +171,8 @@ static struct pci_driver driver = { .id_table = hpt34x_pci_tbl, .probe = hpt34x_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init hpt34x_ide_init(void) diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index ab6c217f104e..a194022b6a61 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -943,7 +943,7 @@ static void hpt3xxn_rw_disk(ide_drive_t *drive, struct request *rq) * Perform a calibration cycle on the DPLL. * Returns 1 if this succeeds */ -static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f_high) +static int hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f_high) { u32 dpll = (f_high << 16) | f_low | 0x100; u8 scr2; @@ -971,7 +971,7 @@ static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f return 1; } -static void __devinit hpt3xx_disable_fast_irq(struct pci_dev *dev, u8 mcr_addr) +static void hpt3xx_disable_fast_irq(struct pci_dev *dev, u8 mcr_addr) { struct ide_host *host = pci_get_drvdata(dev); struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]); @@ -1001,7 +1001,7 @@ static void __devinit hpt3xx_disable_fast_irq(struct pci_dev *dev, u8 mcr_addr) pci_write_config_byte(dev, mcr_addr + 1, new_mcr); } -static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev) +static unsigned int init_chipset_hpt366(struct pci_dev *dev) { unsigned long io_base = pci_resource_start(dev, 4); struct hpt_info *info = hpt3xx_get_info(&dev->dev); @@ -1627,6 +1627,8 @@ static struct pci_driver driver = { .id_table = hpt366_pci_tbl, .probe = hpt366_init_one, .remove = __devexit_p(hpt366_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init hpt366_ide_init(void) diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c index a0e058a2abfe..0954ccd08d6f 100644 --- a/drivers/ide/pci/it8213.c +++ b/drivers/ide/pci/it8213.c @@ -194,6 +194,8 @@ static struct pci_driver driver = { .id_table = it8213_pci_tbl, .probe = it8213_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init it8213_ide_init(void) diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 0fdea7e91a7a..46edd083b348 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -586,7 +586,7 @@ static void __devinit init_hwif_it821x(ide_hwif_t *hwif) hwif->mwdma_mask = ATA_MWDMA2; } -static void __devinit it8212_disable_raid(struct pci_dev *dev) +static void it8212_disable_raid(struct pci_dev *dev) { /* Reset local CPU, and set BIOS not ready */ pci_write_config_byte(dev, 0x5E, 0x01); @@ -603,7 +603,7 @@ static void __devinit it8212_disable_raid(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); } -static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev) +static unsigned int init_chipset_it821x(struct pci_dev *dev) { u8 conf; static char *mode[2] = { "pass through", "smart" }; @@ -685,6 +685,8 @@ static struct pci_driver driver = { .id_table = it821x_pci_tbl, .probe = it821x_init_one, .remove = __devexit_p(it821x_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init it821x_ide_init(void) diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c index 4010b4a8dfbb..acd647110648 100644 --- a/drivers/ide/pci/jmicron.c +++ b/drivers/ide/pci/jmicron.c @@ -154,6 +154,8 @@ static struct pci_driver driver = { .id_table = jmicron_pci_tbl, .probe = jmicron_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init jmicron_ide_init(void) diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index a482ade8e455..53bd645736d9 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -339,6 +339,8 @@ static struct pci_driver driver = { .id_table = ns87415_pci_tbl, .probe = ns87415_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init ns87415_ide_init(void) diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index fefac2c174b6..3de11ddcf863 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -225,6 +225,8 @@ static struct pci_driver driver = { .id_table = opti621_pci_tbl, .probe = opti621_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init opti621_ide_init(void) diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 73bd264fbf9f..9fc59962553b 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -226,7 +226,7 @@ static void pdcnew_reset(ide_drive_t *drive) * read_counter - Read the byte count registers * @dma_base: for the port address */ -static long __devinit read_counter(u32 dma_base) +static long read_counter(u32 dma_base) { u32 pri_dma_base = dma_base, sec_dma_base = dma_base + 0x08; u8 cnt0, cnt1, cnt2, cnt3; @@ -266,7 +266,7 @@ static long __devinit read_counter(u32 dma_base) * @dma_base: for the port address * E.g. 16949000 on 33 MHz PCI bus, i.e. half of the PCI clock. */ -static long __devinit detect_pll_input_clock(unsigned long dma_base) +static long detect_pll_input_clock(unsigned long dma_base) { struct timeval start_time, end_time; long start_count, end_count; @@ -309,7 +309,7 @@ static long __devinit detect_pll_input_clock(unsigned long dma_base) } #ifdef CONFIG_PPC_PMAC -static void __devinit apple_kiwi_init(struct pci_dev *pdev) +static void apple_kiwi_init(struct pci_dev *pdev) { struct device_node *np = pci_device_to_OF_node(pdev); u8 conf; @@ -325,7 +325,7 @@ static void __devinit apple_kiwi_init(struct pci_dev *pdev) } #endif /* CONFIG_PPC_PMAC */ -static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev) +static unsigned int init_chipset_pdcnew(struct pci_dev *dev) { const char *name = DRV_NAME; unsigned long dma_base = pci_resource_start(dev, 4); @@ -566,6 +566,8 @@ static struct pci_driver driver = { .id_table = pdc202new_pci_tbl, .probe = pdc202new_init_one, .remove = __devexit_p(pdc202new_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init pdc202new_ide_init(void) diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 8f0acb956c6b..cb6d2a00c514 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -264,7 +264,7 @@ static void pdc202xx_dma_timeout(ide_drive_t *drive) ide_dma_timeout(drive); } -static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev) +static unsigned int init_chipset_pdc202xx(struct pci_dev *dev) { unsigned long dmabase = pci_resource_start(dev, 4); u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0; @@ -431,6 +431,8 @@ static struct pci_driver driver = { .id_table = pdc202xx_pci_tbl, .probe = pdc202xx_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init pdc202xx_ide_init(void) diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 13136dddb2b4..a06c03f8e295 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -204,7 +204,7 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed) * out to be nice and simple. */ -static unsigned int __devinit init_chipset_ich(struct pci_dev *dev) +static unsigned int init_chipset_ich(struct pci_dev *dev) { u32 extra = 0; @@ -449,6 +449,8 @@ static struct pci_driver driver = { .id_table = piix_pci_tbl, .probe = piix_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init piix_ide_init(void) diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index 5f79d284ff82..3dff2aea317e 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -175,7 +175,7 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed) pci_write_config_byte(dev, 0x54, ultra_enable); } -static unsigned int __devinit init_chipset_svwks(struct pci_dev *dev) +static unsigned int init_chipset_svwks(struct pci_dev *dev) { unsigned int reg; u8 btr; @@ -448,6 +448,8 @@ static struct pci_driver driver = { .id_table = svwks_pci_tbl, .probe = svwks_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init svwks_ide_init(void) diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 874c8ca40ed6..174a873b4c64 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -463,7 +463,7 @@ static void sil_sata_pre_reset(ide_drive_t *drive) * to 133 MHz clocking if the system isn't already set up to do it. */ -static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev) +static unsigned int init_chipset_siimage(struct pci_dev *dev) { struct ide_host *host = pci_get_drvdata(dev); void __iomem *ioaddr = host->host_priv; @@ -834,6 +834,8 @@ static struct pci_driver driver = { .id_table = siimage_pci_tbl, .probe = siimage_init_one, .remove = __devexit_p(siimage_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init siimage_ide_init(void) diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 56bfb245f1fa..734dd41f1f67 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -447,7 +447,7 @@ static int __devinit sis_find_family(struct pci_dev *dev) return chipset_family; } -static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev) +static unsigned int init_chipset_sis5513(struct pci_dev *dev) { /* Make general config ops here 1/ tell IDE channels to operate in Compatibility mode only @@ -610,6 +610,8 @@ static struct pci_driver driver = { .id_table = sis5513_pci_tbl, .probe = sis5513_init_one, .remove = __devexit_p(sis5513_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init sis5513_ide_init(void) diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 8cc4e137c608..37a6b7bdc040 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -271,7 +271,7 @@ static u8 sl82c105_bridge_revision(struct pci_dev *dev) * channel 0 here at least, but channel 1 has to be enabled by * firmware or arch code. We still set both to 16 bits mode. */ -static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev) +static unsigned int init_chipset_sl82c105(struct pci_dev *dev) { u32 val; @@ -350,6 +350,8 @@ static struct pci_driver driver = { .id_table = sl82c105_pci_tbl, .probe = sl82c105_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init sl82c105_ide_init(void) diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c index a31c6911442d..a9551a13ac57 100644 --- a/drivers/ide/pci/slc90e66.c +++ b/drivers/ide/pci/slc90e66.c @@ -159,6 +159,8 @@ static struct pci_driver driver = { .id_table = slc90e66_pci_tbl, .probe = slc90e66_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init slc90e66_ide_init(void) diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c index c980a7f39052..be8715dcee05 100644 --- a/drivers/ide/pci/triflex.c +++ b/drivers/ide/pci/triflex.c @@ -119,6 +119,8 @@ static struct pci_driver driver = { .id_table = triflex_pci_tbl, .probe = triflex_init_one, .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init triflex_ide_init(void) diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 9cb531dc905a..acacdaab69c2 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -215,7 +215,7 @@ static struct via_isa_bridge *via_config_find(struct pci_dev **isa) /* * Check and handle 80-wire cable presence */ -static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u) +static void via_cable_detect(struct via82cxxx_dev *vdev, u32 u) { int i; @@ -267,7 +267,7 @@ static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u) * and initialize its drive independent registers. */ -static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev) +static unsigned int init_chipset_via82cxxx(struct pci_dev *dev) { struct ide_host *host = pci_get_drvdata(dev); struct via82cxxx_dev *vdev = host->host_priv; @@ -492,6 +492,8 @@ static struct pci_driver driver = { .id_table = via_pci_tbl, .probe = via_init_one, .remove = __devexit_p(via_remove), + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, }; static int __init via_ide_init(void) diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index a8e9e8a69a52..9f1f9163a136 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -659,3 +659,36 @@ void ide_pci_remove(struct pci_dev *dev) pci_disable_device(dev); } EXPORT_SYMBOL_GPL(ide_pci_remove); + +#ifdef CONFIG_PM +int ide_pci_suspend(struct pci_dev *dev, pm_message_t state) +{ + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + + return 0; +} +EXPORT_SYMBOL_GPL(ide_pci_suspend); + +int ide_pci_resume(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + int rc; + + pci_set_power_state(dev, PCI_D0); + + rc = pci_enable_device(dev); + if (rc) + return rc; + + pci_restore_state(dev); + pci_set_master(dev); + + if (host->init_chipset) + host->init_chipset(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(ide_pci_resume); +#endif diff --git a/include/linux/ide.h b/include/linux/ide.h index 432eb98f7fe7..2c5d83ddaef6 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef CONFIG_BLK_DEV_IDEACPI #include #endif @@ -639,6 +640,7 @@ struct ide_host { ide_hwif_t *ports[MAX_HWIFS]; unsigned int n_ports; struct device *dev[2]; + unsigned int (*init_chipset)(struct pci_dev *); unsigned long host_flags; void *host_priv; }; @@ -1264,6 +1266,14 @@ int ide_pci_init_two(struct pci_dev *, struct pci_dev *, const struct ide_port_info *, void *); void ide_pci_remove(struct pci_dev *); +#ifdef CONFIG_PM +int ide_pci_suspend(struct pci_dev *, pm_message_t); +int ide_pci_resume(struct pci_dev *); +#else +#define ide_pci_suspend NULL +#define ide_pci_resume NULL +#endif + void ide_map_sg(ide_drive_t *, struct request *); void ide_init_sg_cmd(ide_drive_t *, struct request *); -- cgit v1.2.3 From 9232c14bff36d65de254f34386c00b732c5b6099 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:33 +0200 Subject: ide: remove ->bus_state field from ide_hwif_t It is always set to BUSSTATE_ON. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide.c | 4 +--- include/linux/ide.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index f42de5fe9bc2..f78e789ea5f7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -96,8 +96,6 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) hwif->name[2] = 'e'; hwif->name[3] = '0' + index; - hwif->bus_state = BUSSTATE_ON; - init_completion(&hwif->gendev_rel_comp); hwif->tp_ops = &default_tp_ops; @@ -620,7 +618,7 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device case HDIO_GET_BUSSTATE: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (put_user(HWIF(drive)->bus_state, (long __user *)arg)) + if (put_user(BUSSTATE_ON, (long __user *)arg)) return -EFAULT; return 0; diff --git a/include/linux/ide.h b/include/linux/ide.h index 2c5d83ddaef6..42f39781af87 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -562,7 +562,6 @@ typedef struct hwif_s { u8 major; /* our major number */ u8 index; /* 0 for ide0; 1 for ide1; ... */ u8 channel; /* for dual-port chips: 0=primary, 1=secondary */ - u8 bus_state; /* power state of the IDE bus */ u32 host_flags; -- cgit v1.2.3 From aa7687738af3332470e02ac1060f6c046d83c9a3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:33 +0200 Subject: ide: add ide_setting_ioctl() helper * Add struct ide_ioctl_devset representing ioctl device setting. * Add ide_setting_ioctl() helper for matching given ioctl and its parameters against table of ioctl device settings. * Convert ide_setting_ioctl() and idedisk_ioctl() to use ide_setting_ioctl(). * Un-export ide_setting_mtx. While at it: * {get,set}_lba_addressing() -> {get,set}_addressing() There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 59 +++++++++++++++----------------------------------- drivers/ide/ide.c | 53 +++++++++++++++++++++++++++++---------------- include/linux/ide.h | 14 +++++++++++- 3 files changed, 65 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 43025c9d8355..119063470820 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -676,7 +676,7 @@ static int set_acoustic(ide_drive_t *drive, int arg) return 0; } -ide_devset_get(lba_addressing, addressing); +ide_devset_get(addressing, addressing); /* * drive->addressing: @@ -684,7 +684,7 @@ ide_devset_get(lba_addressing, addressing); * 1: 48-bit * 2: 48-bit capable doing 28-bit */ -static int set_lba_addressing(ide_drive_t *drive, int arg) +static int set_addressing(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 2) return -EINVAL; @@ -704,7 +704,7 @@ static int set_lba_addressing(ide_drive_t *drive, int arg) #ifdef CONFIG_IDE_PROC_FS ide_devset_rw_nolock(acoustic, 0, 254, acoustic); -ide_devset_rw_nolock(address, 0, 2, lba_addressing); +ide_devset_rw_nolock(address, 0, 2, addressing); ide_devset_rw_nolock(multcount, 0, 16, multcount); ide_devset_rw_nolock(nowerr, 0, 1, nowerr); ide_devset_rw_nolock(wcache, 0, 1, wcache); @@ -753,7 +753,7 @@ static void idedisk_setup(ide_drive_t *drive) drive->doorlocking = 1; } - (void)set_lba_addressing(drive, 1); + (void)set_addressing(drive, 1); if (drive->addressing == 1) { int max_s = 2048; @@ -1000,51 +1000,28 @@ static int idedisk_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } +static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = { +{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, get_addressing, set_addressing }, +{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, get_multcount, set_multcount }, +{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, get_nowerr, set_nowerr }, +{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, get_wcache, set_wcache }, +{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, get_acoustic, set_acoustic }, +{ 0 } +}; + static int idedisk_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long flags; struct block_device *bdev = inode->i_bdev; struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk); ide_drive_t *drive = idkp->drive; - int err, (*getfunc)(ide_drive_t *), (*setfunc)(ide_drive_t *, int); - - switch (cmd) { - case HDIO_GET_ADDRESS: getfunc = get_lba_addressing; goto read_val; - case HDIO_GET_MULTCOUNT: getfunc = get_multcount; goto read_val; - case HDIO_GET_NOWERR: getfunc = get_nowerr; goto read_val; - case HDIO_GET_WCACHE: getfunc = get_wcache; goto read_val; - case HDIO_GET_ACOUSTIC: getfunc = get_acoustic; goto read_val; - case HDIO_SET_ADDRESS: setfunc = set_lba_addressing; goto set_val; - case HDIO_SET_MULTCOUNT: setfunc = set_multcount; goto set_val; - case HDIO_SET_NOWERR: setfunc = set_nowerr; goto set_val; - case HDIO_SET_WCACHE: setfunc = set_wcache; goto set_val; - case HDIO_SET_ACOUSTIC: setfunc = set_acoustic; goto set_val; - } + int err; - return generic_ide_ioctl(drive, file, bdev, cmd, arg); + err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings); + if (err != -EOPNOTSUPP) + return err; -read_val: - mutex_lock(&ide_setting_mtx); - spin_lock_irqsave(&ide_lock, flags); - err = getfunc(drive); - spin_unlock_irqrestore(&ide_lock, flags); - mutex_unlock(&ide_setting_mtx); - return err >= 0 ? put_user(err, (long __user *)arg) : err; - -set_val: - if (bdev != bdev->bd_contains) - err = -EINVAL; - else { - if (!capable(CAP_SYS_ADMIN)) - err = -EACCES; - else { - mutex_lock(&ide_setting_mtx); - err = setfunc(drive, arg); - mutex_unlock(&ide_setting_mtx); - } - } - return err; + return generic_ide_ioctl(drive, file, bdev, cmd, arg); } static int idedisk_media_changed(struct gendisk *disk) diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index f78e789ea5f7..4910924abeb7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -250,8 +250,6 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) DEFINE_MUTEX(ide_setting_mtx); -EXPORT_SYMBOL_GPL(ide_setting_mtx); - /** * ide_spin_wait_hwgroup - wait for group * @drive: drive in the group @@ -558,23 +556,23 @@ static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) return 0; } +static const struct ide_ioctl_devset ide_ioctl_settings[] = { +{ HDIO_GET_32BIT, HDIO_SET_32BIT, get_io_32bit, set_io_32bit }, +{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, get_ksettings, set_ksettings }, +{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, get_unmaskirq, set_unmaskirq }, +{ HDIO_GET_DMA, HDIO_SET_DMA, get_using_dma, set_using_dma }, +{ -1, HDIO_SET_PIO_MODE, NULL, set_pio_mode }, +{ 0 } +}; + int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device *bdev, unsigned int cmd, unsigned long arg) { - unsigned long flags; - int err = 0, (*getfunc)(ide_drive_t *), (*setfunc)(ide_drive_t *, int); + int err; - switch (cmd) { - case HDIO_GET_32BIT: getfunc = get_io_32bit; goto read_val; - case HDIO_GET_KEEPSETTINGS: getfunc = get_ksettings; goto read_val; - case HDIO_GET_UNMASKINTR: getfunc = get_unmaskirq; goto read_val; - case HDIO_GET_DMA: getfunc = get_using_dma; goto read_val; - case HDIO_SET_32BIT: setfunc = set_io_32bit; goto set_val; - case HDIO_SET_KEEPSETTINGS: setfunc = set_ksettings; goto set_val; - case HDIO_SET_PIO_MODE: setfunc = set_pio_mode; goto set_val; - case HDIO_SET_UNMASKINTR: setfunc = set_unmaskirq; goto set_val; - case HDIO_SET_DMA: setfunc = set_using_dma; goto set_val; - } + err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings); + if (err != -EOPNOTSUPP) + return err; switch (cmd) { case HDIO_OBSOLETE_IDENTITY: @@ -629,11 +627,29 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device default: return -EINVAL; } +} +EXPORT_SYMBOL(generic_ide_ioctl); + +int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, + unsigned int cmd, unsigned long arg, + const struct ide_ioctl_devset *s) +{ + unsigned long flags; + int err = -EOPNOTSUPP; + + for (; s->get_ioctl; s++) { + if (s->get && s->get_ioctl == cmd) + goto read_val; + else if (s->set && s->set_ioctl == cmd) + goto set_val; + } + + return err; read_val: mutex_lock(&ide_setting_mtx); spin_lock_irqsave(&ide_lock, flags); - err = getfunc(drive); + err = s->get(drive); spin_unlock_irqrestore(&ide_lock, flags); mutex_unlock(&ide_setting_mtx); return err >= 0 ? put_user(err, (long __user *)arg) : err; @@ -646,14 +662,13 @@ set_val: err = -EACCES; else { mutex_lock(&ide_setting_mtx); - err = setfunc(drive, arg); + err = s->set(drive, arg); mutex_unlock(&ide_setting_mtx); } } return err; } - -EXPORT_SYMBOL(generic_ide_ioctl); +EXPORT_SYMBOL_GPL(ide_setting_ioctl); /** * ide_device_get - get an additional reference to a ide_drive_t diff --git a/include/linux/ide.h b/include/linux/ide.h index 42f39781af87..64624b9b645c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -932,7 +932,19 @@ struct ide_driver_s { int ide_device_get(ide_drive_t *); void ide_device_put(ide_drive_t *); -int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsigned, unsigned long); +struct ide_ioctl_devset { + unsigned int get_ioctl; + unsigned int set_ioctl; + + int (*get)(ide_drive_t *); + int (*set)(ide_drive_t *, int); +}; + +int ide_setting_ioctl(ide_drive_t *, struct block_device *, unsigned int, + unsigned long, const struct ide_ioctl_devset *); + +int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, + unsigned, unsigned long); extern int ide_vlb_clk; extern int ide_pci_clk; -- cgit v1.2.3 From 05236ea6df7419f0f37cf9603cfee265cfce5832 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:33 +0200 Subject: ide: move ioctls handling to ide-ioctls.c * Move ioctls handling to ide-ioctls.c (except HDIO_DRIVE_TASKFILE for now). * Make ide_{cmd,task}() static. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Makefile | 4 +- drivers/ide/ide-ioctls.c | 289 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-taskfile.c | 109 ----------------- drivers/ide/ide.c | 174 --------------------------- include/linux/ide.h | 2 - 5 files changed, 291 insertions(+), 287 deletions(-) create mode 100644 drivers/ide/ide-ioctls.c (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 64e0ecdc4ed5..76a30d763384 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -4,8 +4,8 @@ EXTRA_CFLAGS += -Idrivers/ide -ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o \ - ide-pio-blacklist.o +ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \ + ide-taskfile.o ide-pio-blacklist.o # core IDE code ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c new file mode 100644 index 000000000000..7a0d62e7286b --- /dev/null +++ b/drivers/ide/ide-ioctls.c @@ -0,0 +1,289 @@ +/* + * IDE ioctls handling. + */ + +#include +#include + +static const struct ide_ioctl_devset ide_ioctl_settings[] = { +{ HDIO_GET_32BIT, HDIO_SET_32BIT, get_io_32bit, set_io_32bit }, +{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, get_ksettings, set_ksettings }, +{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, get_unmaskirq, set_unmaskirq }, +{ HDIO_GET_DMA, HDIO_SET_DMA, get_using_dma, set_using_dma }, +{ -1, HDIO_SET_PIO_MODE, NULL, set_pio_mode }, +{ 0 } +}; + +int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, + unsigned int cmd, unsigned long arg, + const struct ide_ioctl_devset *s) +{ + unsigned long flags; + int err = -EOPNOTSUPP; + + for (; s->get_ioctl; s++) { + if (s->get && s->get_ioctl == cmd) + goto read_val; + else if (s->set && s->set_ioctl == cmd) + goto set_val; + } + + return err; + +read_val: + mutex_lock(&ide_setting_mtx); + spin_lock_irqsave(&ide_lock, flags); + err = s->get(drive); + spin_unlock_irqrestore(&ide_lock, flags); + mutex_unlock(&ide_setting_mtx); + return err >= 0 ? put_user(err, (long __user *)arg) : err; + +set_val: + if (bdev != bdev->bd_contains) + err = -EINVAL; + else { + if (!capable(CAP_SYS_ADMIN)) + err = -EACCES; + else { + mutex_lock(&ide_setting_mtx); + err = s->set(drive, arg); + mutex_unlock(&ide_setting_mtx); + } + } + return err; +} +EXPORT_SYMBOL_GPL(ide_setting_ioctl); + +static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, + unsigned long arg) +{ + u16 *id = NULL; + int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142; + int rc = 0; + + if (drive->id_read == 0) { + rc = -ENOMSG; + goto out; + } + + id = kmalloc(size, GFP_KERNEL); + if (id == NULL) { + rc = -ENOMEM; + goto out; + } + + memcpy(id, drive->id, size); + ata_id_to_hd_driveid(id); + + if (copy_to_user((void __user *)arg, id, size)) + rc = -EFAULT; + + kfree(id); +out: + return rc; +} + +static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg) +{ + return put_user((drive->dsc_overlap << IDE_NICE_DSC_OVERLAP) | + (drive->nice1 << IDE_NICE_1), (long __user *)arg); +} + +static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) +{ + if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) + return -EPERM; + + if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && + (drive->media == ide_disk || drive->media == ide_floppy || + drive->scsi)) + return -EPERM; + + drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; + drive->nice1 = (arg >> IDE_NICE_1) & 1; + + return 0; +} + +static int ide_cmd_ioctl(ide_drive_t *drive, unsigned cmd, unsigned long arg) +{ + u8 *buf = NULL; + int bufsize = 0, err = 0; + u8 args[4], xfer_rate = 0; + ide_task_t tfargs; + struct ide_taskfile *tf = &tfargs.tf; + u16 *id = drive->id; + + if (NULL == (void *) arg) { + struct request *rq; + + rq = blk_get_request(drive->queue, READ, __GFP_WAIT); + rq->cmd_type = REQ_TYPE_ATA_TASKFILE; + err = blk_execute_rq(drive->queue, NULL, rq, 0); + blk_put_request(rq); + + return err; + } + + if (copy_from_user(args, (void __user *)arg, 4)) + return -EFAULT; + + memset(&tfargs, 0, sizeof(ide_task_t)); + tf->feature = args[2]; + if (args[0] == ATA_CMD_SMART) { + tf->nsect = args[3]; + tf->lbal = args[1]; + tf->lbam = 0x4f; + tf->lbah = 0xc2; + tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT; + } else { + tf->nsect = args[1]; + tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE | + IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT; + } + tf->command = args[0]; + tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA; + + if (args[3]) { + tfargs.tf_flags |= IDE_TFLAG_IO_16BIT; + bufsize = SECTOR_SIZE * args[3]; + buf = kzalloc(bufsize, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } + + if (tf->command == ATA_CMD_SET_FEATURES && + tf->feature == SETFEATURES_XFER && + tf->nsect >= XFER_SW_DMA_0 && + (id[ATA_ID_UDMA_MODES] || + id[ATA_ID_MWDMA_MODES] || + id[ATA_ID_SWDMA_MODES])) { + xfer_rate = args[1]; + if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) { + printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot " + "be set\n", drive->name); + goto abort; + } + } + + err = ide_raw_taskfile(drive, &tfargs, buf, args[3]); + + args[0] = tf->status; + args[1] = tf->error; + args[2] = tf->nsect; + + if (!err && xfer_rate) { + /* active-retuning-calls future */ + ide_set_xfer_rate(drive, xfer_rate); + ide_driveid_update(drive); + } +abort: + if (copy_to_user((void __user *)arg, &args, 4)) + err = -EFAULT; + if (buf) { + if (copy_to_user((void __user *)(arg + 4), buf, bufsize)) + err = -EFAULT; + kfree(buf); + } + return err; +} + +static int ide_task_ioctl(ide_drive_t *drive, unsigned cmd, unsigned long arg) +{ + void __user *p = (void __user *)arg; + int err = 0; + u8 args[7]; + ide_task_t task; + + if (copy_from_user(args, p, 7)) + return -EFAULT; + + memset(&task, 0, sizeof(task)); + memcpy(&task.tf_array[7], &args[1], 6); + task.tf.command = args[0]; + task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; + + err = ide_no_data_taskfile(drive, &task); + + args[0] = task.tf.command; + memcpy(&args[1], &task.tf_array[7], 6); + + if (copy_to_user(p, args, 7)) + err = -EFAULT; + + return err; +} + +static int generic_drive_reset(ide_drive_t *drive) +{ + struct request *rq; + int ret = 0; + + rq = blk_get_request(drive->queue, READ, __GFP_WAIT); + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->cmd_len = 1; + rq->cmd[0] = REQ_DRIVE_RESET; + rq->cmd_flags |= REQ_SOFTBARRIER; + if (blk_execute_rq(drive->queue, NULL, rq, 1)) + ret = rq->errors; + blk_put_request(rq); + return ret; +} + +int generic_ide_ioctl(ide_drive_t *drive, struct file *file, + struct block_device *bdev, + unsigned int cmd, unsigned long arg) +{ + int err; + + err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings); + if (err != -EOPNOTSUPP) + return err; + + switch (cmd) { + case HDIO_OBSOLETE_IDENTITY: + case HDIO_GET_IDENTITY: + if (bdev != bdev->bd_contains) + return -EINVAL; + return ide_get_identity_ioctl(drive, cmd, arg); + case HDIO_GET_NICE: + return ide_get_nice_ioctl(drive, arg); + case HDIO_SET_NICE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return ide_set_nice_ioctl(drive, arg); +#ifdef CONFIG_IDE_TASK_IOCTL + case HDIO_DRIVE_TASKFILE: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + if (drive->media == ide_disk) + return ide_taskfile_ioctl(drive, cmd, arg); + return -ENOMSG; +#endif + case HDIO_DRIVE_CMD: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_cmd_ioctl(drive, cmd, arg); + case HDIO_DRIVE_TASK: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_task_ioctl(drive, cmd, arg); + case HDIO_DRIVE_RESET: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return generic_drive_reset(drive); + case HDIO_GET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (put_user(BUSSTATE_ON, (long __user *)arg)) + return -EFAULT; + return 0; + case HDIO_SET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return -EOPNOTSUPP; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(generic_ide_ioctl); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 7ffe9004a4d6..487b18b3ebae 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -726,112 +726,3 @@ abort: return err; } #endif - -int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) -{ - u8 *buf = NULL; - int bufsize = 0, err = 0; - u8 args[4], xfer_rate = 0; - ide_task_t tfargs; - struct ide_taskfile *tf = &tfargs.tf; - u16 *id = drive->id; - - if (NULL == (void *) arg) { - struct request *rq; - - rq = blk_get_request(drive->queue, READ, __GFP_WAIT); - rq->cmd_type = REQ_TYPE_ATA_TASKFILE; - err = blk_execute_rq(drive->queue, NULL, rq, 0); - blk_put_request(rq); - - return err; - } - - if (copy_from_user(args, (void __user *)arg, 4)) - return -EFAULT; - - memset(&tfargs, 0, sizeof(ide_task_t)); - tf->feature = args[2]; - if (args[0] == ATA_CMD_SMART) { - tf->nsect = args[3]; - tf->lbal = args[1]; - tf->lbam = 0x4f; - tf->lbah = 0xc2; - tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT; - } else { - tf->nsect = args[1]; - tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE | - IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT; - } - tf->command = args[0]; - tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA; - - if (args[3]) { - tfargs.tf_flags |= IDE_TFLAG_IO_16BIT; - bufsize = SECTOR_SIZE * args[3]; - buf = kzalloc(bufsize, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - } - - if (tf->command == ATA_CMD_SET_FEATURES && - tf->feature == SETFEATURES_XFER && - tf->nsect >= XFER_SW_DMA_0 && - (id[ATA_ID_UDMA_MODES] || - id[ATA_ID_MWDMA_MODES] || - id[ATA_ID_SWDMA_MODES])) { - xfer_rate = args[1]; - if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) { - printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot " - "be set\n", drive->name); - goto abort; - } - } - - err = ide_raw_taskfile(drive, &tfargs, buf, args[3]); - - args[0] = tf->status; - args[1] = tf->error; - args[2] = tf->nsect; - - if (!err && xfer_rate) { - /* active-retuning-calls future */ - ide_set_xfer_rate(drive, xfer_rate); - ide_driveid_update(drive); - } -abort: - if (copy_to_user((void __user *)arg, &args, 4)) - err = -EFAULT; - if (buf) { - if (copy_to_user((void __user *)(arg + 4), buf, bufsize)) - err = -EFAULT; - kfree(buf); - } - return err; -} - -int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) -{ - void __user *p = (void __user *)arg; - int err = 0; - u8 args[7]; - ide_task_t task; - - if (copy_from_user(args, p, 7)) - return -EFAULT; - - memset(&task, 0, sizeof(task)); - memcpy(&task.tf_array[7], &args[1], 6); - task.tf.command = args[0]; - task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; - - err = ide_no_data_taskfile(drive, &task); - - args[0] = task.tf.command; - memcpy(&args[1], &task.tf_array[7], 6); - - if (copy_to_user(p, args, 7)) - err = -EFAULT; - - return err; -} diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 19181493e722..349d7fa75585 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -489,180 +489,6 @@ static int generic_ide_resume(struct device *dev) return err; } -static int generic_drive_reset(ide_drive_t *drive) -{ - struct request *rq; - int ret = 0; - - rq = blk_get_request(drive->queue, READ, __GFP_WAIT); - rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd_len = 1; - rq->cmd[0] = REQ_DRIVE_RESET; - rq->cmd_flags |= REQ_SOFTBARRIER; - if (blk_execute_rq(drive->queue, NULL, rq, 1)) - ret = rq->errors; - blk_put_request(rq); - return ret; -} - -static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, - unsigned long arg) -{ - u16 *id = NULL; - int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142; - int rc = 0; - - if (drive->id_read == 0) { - rc = -ENOMSG; - goto out; - } - - id = kmalloc(size, GFP_KERNEL); - if (id == NULL) { - rc = -ENOMEM; - goto out; - } - - memcpy(id, drive->id, size); - ata_id_to_hd_driveid(id); - - if (copy_to_user((void __user *)arg, id, size)) - rc = -EFAULT; - - kfree(id); -out: - return rc; -} - -static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg) -{ - return put_user((drive->dsc_overlap << IDE_NICE_DSC_OVERLAP) | - (drive->nice1 << IDE_NICE_1), (long __user *)arg); -} - -static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) -{ - if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) - return -EPERM; - - if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && - (drive->media == ide_disk || drive->media == ide_floppy || - drive->scsi)) - return -EPERM; - - drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; - drive->nice1 = (arg >> IDE_NICE_1) & 1; - - return 0; -} - -static const struct ide_ioctl_devset ide_ioctl_settings[] = { -{ HDIO_GET_32BIT, HDIO_SET_32BIT, get_io_32bit, set_io_32bit }, -{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, get_ksettings, set_ksettings }, -{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, get_unmaskirq, set_unmaskirq }, -{ HDIO_GET_DMA, HDIO_SET_DMA, get_using_dma, set_using_dma }, -{ -1, HDIO_SET_PIO_MODE, NULL, set_pio_mode }, -{ 0 } -}; - -int generic_ide_ioctl(ide_drive_t *drive, struct file *file, - struct block_device *bdev, - unsigned int cmd, unsigned long arg) -{ - int err; - - err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings); - if (err != -EOPNOTSUPP) - return err; - - switch (cmd) { - case HDIO_OBSOLETE_IDENTITY: - case HDIO_GET_IDENTITY: - if (bdev != bdev->bd_contains) - return -EINVAL; - return ide_get_identity_ioctl(drive, cmd, arg); - case HDIO_GET_NICE: - return ide_get_nice_ioctl(drive, arg); - case HDIO_SET_NICE: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - return ide_set_nice_ioctl(drive, arg); -#ifdef CONFIG_IDE_TASK_IOCTL - case HDIO_DRIVE_TASKFILE: - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - if (drive->media == ide_disk) - return ide_taskfile_ioctl(drive, cmd, arg); - return -ENOMSG; -#endif - case HDIO_DRIVE_CMD: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - return ide_cmd_ioctl(drive, cmd, arg); - case HDIO_DRIVE_TASK: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - return ide_task_ioctl(drive, cmd, arg); - case HDIO_DRIVE_RESET: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - return generic_drive_reset(drive); - case HDIO_GET_BUSSTATE: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (put_user(BUSSTATE_ON, (long __user *)arg)) - return -EFAULT; - return 0; - case HDIO_SET_BUSSTATE: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - return -EOPNOTSUPP; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(generic_ide_ioctl); - -int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, - unsigned int cmd, unsigned long arg, - const struct ide_ioctl_devset *s) -{ - unsigned long flags; - int err = -EOPNOTSUPP; - - for (; s->get_ioctl; s++) { - if (s->get && s->get_ioctl == cmd) - goto read_val; - else if (s->set && s->set_ioctl == cmd) - goto set_val; - } - - return err; - -read_val: - mutex_lock(&ide_setting_mtx); - spin_lock_irqsave(&ide_lock, flags); - err = s->get(drive); - spin_unlock_irqrestore(&ide_lock, flags); - mutex_unlock(&ide_setting_mtx); - return err >= 0 ? put_user(err, (long __user *)arg) : err; - -set_val: - if (bdev != bdev->bd_contains) - err = -EINVAL; - else { - if (!capable(CAP_SYS_ADMIN)) - err = -EACCES; - else { - mutex_lock(&ide_setting_mtx); - err = s->set(drive, arg); - mutex_unlock(&ide_setting_mtx); - } - } - return err; -} -EXPORT_SYMBOL_GPL(ide_setting_ioctl); - /** * ide_device_get - get an additional reference to a ide_drive_t * @drive: device to get a reference to diff --git a/include/linux/ide.h b/include/linux/ide.h index 64624b9b645c..40102bd50a70 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1128,8 +1128,6 @@ int ide_raw_taskfile(ide_drive_t *, ide_task_t *, u8 *, u16); int ide_no_data_taskfile(ide_drive_t *, ide_task_t *); int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long); -int ide_cmd_ioctl(ide_drive_t *, unsigned int, unsigned long); -int ide_task_ioctl(ide_drive_t *, unsigned int, unsigned long); extern int ide_driveid_update(ide_drive_t *); extern int ide_config_drive_speed(ide_drive_t *, u8); -- cgit v1.2.3 From 51509eec34debffec3c6f481f7371c9aeb6c63c1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:34 +0200 Subject: ide: add ide_check_atapi_device() helper * Add ide_check_atapi_device() to ide-atapi.c and convert ide-{floppy,tape}.c to use it instead of ide*_identify_device(). While at it: * Add DRV_NAME defines to ide-{floppy,tape}.c. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-floppy.c | 48 +++--------------------------------------------- drivers/ide/ide-tape.c | 43 +++---------------------------------------- include/linux/ide.h | 2 ++ 4 files changed, 55 insertions(+), 85 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 12674e6519e6..61c52fb665ca 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -14,6 +14,53 @@ #define debug_log(fmt, args...) do {} while (0) #endif +/* + * Check whether we can support a device, + * based on the ATAPI IDENTIFY command results. + */ +int ide_check_atapi_device(ide_drive_t *drive, const char *s) +{ + u16 *id = drive->id; + u8 gcw[2], protocol, device_type, removable, drq_type, packet_size; + + *((u16 *)&gcw) = id[ATA_ID_CONFIG]; + + protocol = (gcw[1] & 0xC0) >> 6; + device_type = gcw[1] & 0x1F; + removable = (gcw[0] & 0x80) >> 7; + drq_type = (gcw[0] & 0x60) >> 5; + packet_size = gcw[0] & 0x03; + +#ifdef CONFIG_PPC + /* kludge for Apple PowerBook internal zip */ + if (drive->media == ide_floppy && device_type == 5 && + !strstr((char *)&id[ATA_ID_PROD], "CD-ROM") && + strstr((char *)&id[ATA_ID_PROD], "ZIP")) + device_type = 0; +#endif + + if (protocol != 2) + printk(KERN_ERR "%s: %s: protocol (0x%02x) is not ATAPI\n", + s, drive->name, protocol); + else if ((drive->media == ide_floppy && device_type != 0) || + (drive->media == ide_tape && device_type != 1)) + printk(KERN_ERR "%s: %s: invalid device type (0x%02x)\n", + s, drive->name, device_type); + else if (removable == 0) + printk(KERN_ERR "%s: %s: the removable flag is not set\n", + s, drive->name); + else if (drive->media == ide_floppy && drq_type == 3) + printk(KERN_ERR "%s: %s: sorry, DRQ type (0x%02x) not " + "supported\n", s, drive->name, drq_type); + else if (packet_size != 0) + printk(KERN_ERR "%s: %s: packet size (0x%02x) is not 12 " + "bytes\n", s, drive->name, packet_size); + else + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(ide_check_atapi_device); + /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index de8d42b3f698..ace6f26a296a 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -15,6 +15,8 @@ * Documentation/ide/ChangeLog.ide-floppy.1996-2002 */ +#define DRV_NAME "ide-floppy" + #define IDEFLOPPY_VERSION "1.00" #include @@ -962,50 +964,6 @@ static sector_t idefloppy_capacity(ide_drive_t *drive) return capacity; } -/* - * Check whether we can support a drive, based on the ATAPI IDENTIFY command - * results. - */ -static int idefloppy_identify_device(ide_drive_t *drive, u16 *id) -{ - u8 gcw[2]; - u8 device_type, protocol, removable, drq_type, packet_size; - - *((u16 *)&gcw) = id[ATA_ID_CONFIG]; - - device_type = gcw[1] & 0x1F; - removable = (gcw[0] & 0x80) >> 7; - protocol = (gcw[1] & 0xC0) >> 6; - drq_type = (gcw[0] & 0x60) >> 5; - packet_size = gcw[0] & 0x03; - -#ifdef CONFIG_PPC - /* kludge for Apple PowerBook internal zip */ - if (device_type == 5 && - !strstr((char *)&id[ATA_ID_PROD], "CD-ROM") && - strstr((char *)&id[ATA_ID_PROD], "ZIP")) - device_type = 0; -#endif - - if (protocol != 2) - printk(KERN_ERR "ide-floppy: Protocol (0x%02x) is not ATAPI\n", - protocol); - else if (device_type != 0) - printk(KERN_ERR "ide-floppy: Device type (0x%02x) is not set " - "to floppy\n", device_type); - else if (!removable) - printk(KERN_ERR "ide-floppy: The removable flag is not set\n"); - else if (drq_type == 3) - printk(KERN_ERR "ide-floppy: Sorry, DRQ type (0x%02x) not " - "supported\n", drq_type); - else if (packet_size != 0) - printk(KERN_ERR "ide-floppy: Packet size (0x%02x) is not 12 " - "bytes\n", packet_size); - else - return 1; - return 0; -} - #ifdef CONFIG_IDE_PROC_FS ide_devset_rw(bios_cyl, 0, 1023, bios_cyl); ide_devset_rw(bios_head, 0, 255, bios_head); @@ -1407,7 +1365,7 @@ static int ide_floppy_probe(ide_drive_t *drive) if (drive->media != ide_floppy) goto failed; - if (!idefloppy_identify_device(drive, drive->id)) { + if (!ide_check_atapi_device(drive, DRV_NAME)) { printk(KERN_ERR "ide-floppy: %s: not supported by this version" " of ide-floppy\n", drive->name); goto failed; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 1fc1c2a6888f..7f56f2003342 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -15,6 +15,8 @@ * Documentation/ide/ChangeLog.ide-tape.1995-2002 */ +#define DRV_NAME "ide-tape" + #define IDETAPE_VERSION "1.20" #include @@ -2296,45 +2298,6 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp) return 0; } -/* - * check the contents of the ATAPI IDENTIFY command results. We return: - * - * 1 - If the tape can be supported by us, based on the information we have so - * far. - * - * 0 - If this tape driver is not currently supported by us. - */ -static int idetape_identify_device(ide_drive_t *drive) -{ - u8 gcw[2], protocol, device_type, removable, packet_size; - - if (drive->id_read == 0) - return 1; - - *((u16 *)&gcw) = drive->id[ATA_ID_CONFIG]; - - protocol = (gcw[1] & 0xC0) >> 6; - device_type = gcw[1] & 0x1F; - removable = !!(gcw[0] & 0x80); - packet_size = gcw[0] & 0x3; - - /* Check that we can support this device */ - if (protocol != 2) - printk(KERN_ERR "ide-tape: Protocol (0x%02x) is not ATAPI\n", - protocol); - else if (device_type != 1) - printk(KERN_ERR "ide-tape: Device type (0x%02x) is not set " - "to tape\n", device_type); - else if (!removable) - printk(KERN_ERR "ide-tape: The removable flag is not set\n"); - else if (packet_size != 0) { - printk(KERN_ERR "ide-tape: Packet size (0x%02x) is not 12" - " bytes\n", packet_size); - } else - return 1; - return 0; -} - static void idetape_get_inquiry_results(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -2679,7 +2642,7 @@ static int ide_tape_probe(ide_drive_t *drive) if (drive->media != ide_tape) goto failed; - if (!idetape_identify_device(drive)) { + if (drive->id_read == 1 && !ide_check_atapi_device(drive, DRV_NAME)) { printk(KERN_ERR "ide-tape: %s: not supported by this version of" " the driver\n", drive->name); goto failed; diff --git a/include/linux/ide.h b/include/linux/ide.h index 40102bd50a70..e63ff63d1f0b 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1109,6 +1109,8 @@ extern int drive_is_ready(ide_drive_t *); void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); +int ide_check_atapi_device(ide_drive_t *, const char *); + ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), -- cgit v1.2.3 From acaa0f5f675ccf6b8a3a11a933419068b1ea1f46 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:36 +0200 Subject: ide: add ide_io_buffers() helper * Make ->io_buffers method return number of bytes transferred. * Use ide_end_request() instead of idefloppy_end_request() in ide_floppy_io_buffers() and then move the call out to ide_pc_intr(). * Add ide_io_buffers() helper and convert ide-{floppy,scsi}.c to use it instead of ide*_io_buffers(). There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 63 +++++++++++++++++++++++++++++++++++++++++++++--- drivers/ide/ide-floppy.c | 47 +----------------------------------- drivers/ide/ide-tape.c | 4 ++- drivers/scsi/ide-scsi.c | 46 +---------------------------------- include/linux/ide.h | 4 ++- 5 files changed, 67 insertions(+), 97 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 61c52fb665ca..3d44b45650f6 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -61,12 +61,62 @@ int ide_check_atapi_device(ide_drive_t *drive, const char *s) } EXPORT_SYMBOL_GPL(ide_check_atapi_device); +/* PIO data transfer routine using the scatter gather table. */ +int ide_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, + unsigned int bcount, int write) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; + struct scatterlist *sg = pc->sg; + char *buf; + int count, done = 0; + + while (bcount) { + count = min(sg->length - pc->b_count, bcount); + + if (PageHighMem(sg_page(sg))) { + unsigned long flags; + + local_irq_save(flags); + buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; + xf(drive, NULL, buf + pc->b_count, count); + kunmap_atomic(buf - sg->offset, KM_IRQ0); + local_irq_restore(flags); + } else { + buf = sg_virt(sg); + xf(drive, NULL, buf + pc->b_count, count); + } + + bcount -= count; + pc->b_count += count; + done += count; + + if (pc->b_count == sg->length) { + if (!--pc->sg_cnt) + break; + pc->sg = sg = sg_next(sg); + pc->b_count = 0; + } + } + + if (bcount) { + printk(KERN_ERR "%s: %d leftover bytes, %s\n", drive->name, + bcount, write ? "padding with zeros" + : "discarding data"); + ide_pad_transfer(drive, write, bcount); + } + + return done; +} +EXPORT_SYMBOL_GPL(ide_io_buffers); + /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *), - void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) + int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->hwgroup->rq; @@ -219,9 +269,14 @@ cmd_finished: if ((drive->media == ide_floppy && !scsi && !pc->buf) || (drive->media == ide_tape && !scsi && pc->bh) || - (scsi && pc->sg)) - io_buffers(drive, pc, bcount, !!(pc->flags & PC_FLAG_WRITING)); - else + (scsi && pc->sg)) { + int done = io_buffers(drive, pc, bcount, + !!(pc->flags & PC_FLAG_WRITING)); + + /* FIXME: don't do partial completions */ + if (drive->media == ide_floppy && !scsi) + ide_end_request(drive, 1, done >> 9); + } else xferfunc(drive, NULL, pc->cur_pos, bcount); /* Update the current position */ diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index a0a8ad3a3038..38dca45ffd11 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -208,51 +208,6 @@ static int idefloppy_end_request(ide_drive_t *drive, int uptodate, int nsecs) return 0; } -static void ide_floppy_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, - unsigned int bcount, int direction) -{ - ide_hwif_t *hwif = drive->hwif; - const struct ide_tp_ops *tp_ops = hwif->tp_ops; - xfer_func_t *xf = direction ? tp_ops->output_data : tp_ops->input_data; - struct scatterlist *sg = pc->sg; - char *buf; - int count, done = 0; - - while (bcount) { - count = min(sg->length - pc->b_count, bcount); - if (PageHighMem(sg_page(sg))) { - unsigned long flags; - - local_irq_save(flags); - buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; - xf(drive, NULL, buf + pc->b_count, count); - kunmap_atomic(buf - sg->offset, KM_IRQ0); - local_irq_restore(flags); - } else { - buf = sg_virt(sg); - xf(drive, NULL, buf + pc->b_count, count); - } - bcount -= count; - pc->b_count += count; - done += count; - - if (pc->b_count == sg->length) { - if (!--pc->sg_cnt) - break; - pc->sg = sg = sg_next(sg); - pc->b_count = 0; - } - } - - idefloppy_end_request(drive, 1, done >> 9); - - if (bcount) { - printk(KERN_ERR "%s: leftover data in %s, bcount == %d\n", - drive->name, __func__, bcount); - ide_pad_transfer(drive, direction, bcount); - } -} - static void idefloppy_update_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc) { @@ -356,7 +311,7 @@ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL, idefloppy_update_buffers, - idefloppy_retry_pc, NULL, ide_floppy_io_buffers); + idefloppy_retry_pc, NULL, ide_io_buffers); } /* diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index ba05e03f482b..d4a9c471dd13 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -693,13 +693,15 @@ static void ide_tape_handle_dsc(ide_drive_t *drive) idetape_postpone_request(drive); } -static void ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, +static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount, int write) { if (write) idetape_output_buffers(drive, pc, bcount); else idetape_input_buffers(drive, pc, bcount); + + return bcount; } /* diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 26e8c3c49474..27c01e368977 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -130,50 +130,6 @@ static inline idescsi_scsi_t *drive_to_idescsi(ide_drive_t *ide_drive) return scsihost_to_idescsi(ide_drive->driver_data); } -/* - * PIO data transfer routine using the scatter gather table. - */ -static void ide_scsi_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, - unsigned int bcount, int write) -{ - ide_hwif_t *hwif = drive->hwif; - const struct ide_tp_ops *tp_ops = hwif->tp_ops; - xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; - struct scatterlist *sg = pc->sg; - char *buf; - int count; - - while (bcount) { - count = min(sg->length - pc->b_count, bcount); - if (PageHighMem(sg_page(sg))) { - unsigned long flags; - - local_irq_save(flags); - buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; - xf(drive, NULL, buf + pc->b_count, count); - kunmap_atomic(buf - sg->offset, KM_IRQ0); - local_irq_restore(flags); - } else { - buf = sg_virt(sg); - xf(drive, NULL, buf + pc->b_count, count); - } - bcount -= count; pc->b_count += count; - if (pc->b_count == sg->length) { - if (!--pc->sg_cnt) - break; - pc->sg = sg = sg_next(sg); - pc->b_count = 0; - } - } - - if (bcount) { - printk(KERN_ERR "%s: scatter gather table too small, %s\n", - drive->name, write ? "padding with zeros" - : "discarding data"); - ide_pad_transfer(drive, write, bcount); - } -} - static void ide_scsi_hex_dump(u8 *data, int len) { print_hex_dump(KERN_CONT, "", DUMP_PREFIX_NONE, 16, 1, data, len, 0); @@ -343,7 +299,7 @@ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) return ide_pc_intr(drive, pc, idescsi_pc_intr, get_timeout(pc), idescsi_expiry, NULL, NULL, NULL, - ide_scsi_io_buffers); + ide_io_buffers); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) diff --git a/include/linux/ide.h b/include/linux/ide.h index e63ff63d1f0b..03dc2157a2b5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1099,6 +1099,8 @@ void ide_tf_read(ide_drive_t *, ide_task_t *); void ide_input_data(ide_drive_t *, struct request *, void *, unsigned int); void ide_output_data(ide_drive_t *, struct request *, void *, unsigned int); +int ide_io_buffers(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int); + extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); @@ -1115,7 +1117,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *), - void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, + int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int)); ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *, ide_handler_t *, unsigned int, ide_expiry_t *); -- cgit v1.2.3 From 7bf7420a318978cd6042e5a5da34b7cfa18ae559 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:37 +0200 Subject: ide: add ide_init_pc() helper * Add IDE_PC_BUFFER_SIZE define. * Add ide_init_pc() and convert ide-{floppy,tape}.c to use it instead of ide*_init_pc(). * Remove no longer used IDE*_PC_BUFFER_SIZE and ide*_init_pc(). There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 8 ++++++++ drivers/ide/ide-floppy.c | 31 +++++++++---------------------- drivers/ide/ide-tape.c | 44 +++++++++++++------------------------------- include/linux/ide.h | 10 +++++++++- 4 files changed, 39 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 3d44b45650f6..8f0842ce77f5 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -111,6 +111,14 @@ int ide_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, } EXPORT_SYMBOL_GPL(ide_io_buffers); +void ide_init_pc(struct ide_atapi_pc *pc) +{ + memset(pc, 0, sizeof(*pc)); + pc->buf = pc->pc_buf; + pc->buf_size = IDE_PC_BUFFER_SIZE; +} +EXPORT_SYMBOL_GPL(ide_init_pc); + /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 4bab0932c94a..e04eaa49a8bf 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -69,12 +69,6 @@ */ #define IDEFLOPPY_MAX_PC_RETRIES 3 -/* - * With each packet command, we allocate a buffer of IDEFLOPPY_PC_BUFFER_SIZE - * bytes. - */ -#define IDEFLOPPY_PC_BUFFER_SIZE 256 - /* format capacities descriptor codes */ #define CAPACITY_INVALID 0x00 #define CAPACITY_UNFORMATTED 0x01 @@ -274,16 +268,9 @@ static void ide_floppy_callback(ide_drive_t *drive) idefloppy_end_request(drive, uptodate, 0); } -static void idefloppy_init_pc(struct ide_atapi_pc *pc) -{ - memset(pc, 0, sizeof(*pc)); - pc->buf = pc->pc_buf; - pc->buf_size = IDEFLOPPY_PC_BUFFER_SIZE; -} - static void idefloppy_create_request_sense_cmd(struct ide_atapi_pc *pc) { - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = GPCMD_REQUEST_SENSE; pc->c[4] = 255; pc->req_xfer = 18; @@ -413,14 +400,14 @@ static void idefloppy_create_prevent_cmd(struct ide_atapi_pc *pc, int prevent) { debug_log("creating prevent removal command, prevent = %d\n", prevent); - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; pc->c[4] = prevent; } static void idefloppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) { - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = GPCMD_READ_FORMAT_CAPACITIES; pc->c[7] = 255; pc->c[8] = 255; @@ -430,7 +417,7 @@ static void idefloppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) static void idefloppy_create_format_unit_cmd(struct ide_atapi_pc *pc, int b, int l, int flags) { - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = GPCMD_FORMAT_UNIT; pc->c[1] = 0x17; @@ -454,7 +441,7 @@ static void idefloppy_create_mode_sense_cmd(struct ide_atapi_pc *pc, { u16 length = 8; /* sizeof(Mode Parameter Header) = 8 Bytes */ - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = GPCMD_MODE_SENSE_10; pc->c[1] = 0; pc->c[2] = page_code; @@ -476,7 +463,7 @@ static void idefloppy_create_mode_sense_cmd(struct ide_atapi_pc *pc, static void idefloppy_create_start_stop_cmd(struct ide_atapi_pc *pc, int start) { - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = GPCMD_START_STOP_UNIT; pc->c[4] = start; } @@ -492,7 +479,7 @@ static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy, debug_log("create_rw10_cmd: block == %d, blocks == %d\n", block, blocks); - idefloppy_init_pc(pc); + ide_init_pc(pc); pc->c[0] = cmd == READ ? GPCMD_READ_10 : GPCMD_WRITE_10; put_unaligned(cpu_to_be16(blocks), (unsigned short *)&pc->c[7]); put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[2]); @@ -511,7 +498,7 @@ static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy, static void idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy, struct ide_atapi_pc *pc, struct request *rq) { - idefloppy_init_pc(pc); + ide_init_pc(pc); memcpy(pc->c, rq->cmd, sizeof(pc->c)); pc->rq = rq; pc->b_count = 0; @@ -1071,7 +1058,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; /* Just in case */ - idefloppy_init_pc(&pc); + ide_init_pc(&pc); pc.c[0] = GPCMD_TEST_UNIT_READY; if (idefloppy_queue_pc_tail(drive, &pc)) { diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 26d5b0576f0a..5c879fbcca87 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -80,12 +80,6 @@ enum { */ #define IDETAPE_MAX_PC_RETRIES 3 -/* - * With each packet command, we allocate a buffer of IDETAPE_PC_BUFFER_SIZE - * bytes. This is used for several packet commands (Not for READ/WRITE commands) - */ -#define IDETAPE_PC_BUFFER_SIZE 256 - /* * Some drives (for example, Seagate STT3401A Travan) require a very long * timeout, because they don't return an interrupt or clear their busy bit @@ -610,21 +604,9 @@ static void ide_tape_callback(ide_drive_t *drive) idetape_end_request(drive, uptodate, 0); } -static void idetape_init_pc(struct ide_atapi_pc *pc) -{ - memset(pc->c, 0, 12); - pc->retries = 0; - pc->flags = 0; - pc->req_xfer = 0; - pc->buf = pc->pc_buf; - pc->buf_size = IDETAPE_PC_BUFFER_SIZE; - pc->bh = NULL; - pc->b_data = NULL; -} - static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = REQUEST_SENSE; pc->c[4] = 20; pc->req_xfer = 20; @@ -816,7 +798,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, /* A mode sense command is used to "sense" tape parameters. */ static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = MODE_SENSE; if (page_code != IDETAPE_BLOCK_DESCRIPTOR) /* DBD = 1 - Don't return block descriptors */ @@ -875,7 +857,7 @@ static void ide_tape_create_rw_cmd(idetape_tape_t *tape, struct idetape_bh *bh = (struct idetape_bh *)rq->special; unsigned int length = rq->current_nr_sectors; - idetape_init_pc(pc); + ide_init_pc(pc); put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]); pc->c[1] = 1; pc->bh = bh; @@ -1165,7 +1147,7 @@ static void idetape_init_merge_buffer(idetape_tape_t *tape) static void idetape_create_write_filemark_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc, int write_filemark) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = WRITE_FILEMARKS; pc->c[4] = write_filemark; pc->flags |= PC_FLAG_WAIT_FOR_DSC; @@ -1173,7 +1155,7 @@ static void idetape_create_write_filemark_cmd(ide_drive_t *drive, static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = TEST_UNIT_READY; } @@ -1200,7 +1182,7 @@ static int idetape_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc) static void idetape_create_load_unload_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc, int cmd) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = START_STOP; pc->c[4] = cmd; pc->flags |= PC_FLAG_WAIT_FOR_DSC; @@ -1252,7 +1234,7 @@ static int idetape_flush_tape_buffers(ide_drive_t *drive) static void idetape_create_read_position_cmd(struct ide_atapi_pc *pc) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = READ_POSITION; pc->req_xfer = 20; } @@ -1276,7 +1258,7 @@ static void idetape_create_locate_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int block, u8 partition, int skip) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = POSITION_TO_ELEMENT; pc->c[1] = 2; put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[3]); @@ -1293,7 +1275,7 @@ static int idetape_create_prevent_cmd(ide_drive_t *drive, if (!(tape->caps[6] & 0x01)) return 0; - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = ALLOW_MEDIUM_REMOVAL; pc->c[4] = prevent; return 1; @@ -1408,7 +1390,7 @@ static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int blocks, static void idetape_create_inquiry_cmd(struct ide_atapi_pc *pc) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = INQUIRY; pc->c[4] = 254; pc->req_xfer = 254; @@ -1417,14 +1399,14 @@ static void idetape_create_inquiry_cmd(struct ide_atapi_pc *pc) static void idetape_create_rewind_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = REZERO_UNIT; pc->flags |= PC_FLAG_WAIT_FOR_DSC; } static void idetape_create_erase_cmd(struct ide_atapi_pc *pc) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = ERASE; pc->c[1] = 1; pc->flags |= PC_FLAG_WAIT_FOR_DSC; @@ -1432,7 +1414,7 @@ static void idetape_create_erase_cmd(struct ide_atapi_pc *pc) static void idetape_create_space_cmd(struct ide_atapi_pc *pc, int count, u8 cmd) { - idetape_init_pc(pc); + ide_init_pc(pc); pc->c[0] = SPACE; put_unaligned(cpu_to_be32(count), (unsigned int *) &pc->c[1]); pc->c[1] = cmd; diff --git a/include/linux/ide.h b/include/linux/ide.h index 03dc2157a2b5..bba2f73b99a0 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -724,6 +724,12 @@ enum { PC_FLAG_TIMEDOUT = (1 << 7), }; +/* + * With each packet command, we allocate a buffer of IDE_PC_BUFFER_SIZE bytes. + * This is used for several packet commands (not for READ/WRITE commands). + */ +#define IDE_PC_BUFFER_SIZE 256 + struct ide_atapi_pc { /* actual packet bytes */ u8 c[12]; @@ -753,7 +759,7 @@ struct ide_atapi_pc { * those are more or less driver-specific and some of them are subject * to change/removal later. */ - u8 pc_buf[256]; + u8 pc_buf[IDE_PC_BUFFER_SIZE]; /* idetape only */ struct idetape_bh *bh; @@ -1113,6 +1119,8 @@ void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); int ide_check_atapi_device(ide_drive_t *, const char *); +void ide_init_pc(struct ide_atapi_pc *); + ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), -- cgit v1.2.3 From 7645c1514c7d34ebdf3ea0e8ee3a935c08abceb2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:37 +0200 Subject: ide: add ide_queue_pc_head() helper * Move REQ_IDETAPE_* enums to . * Add ide_queue_pc_head() and convert ide-{floppy,tape}.c to use it instead of ide*_queue_pc_head(). * Remove no longer used ide*_queue_pc_head(). There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 20 ++++++++++++++++++++ drivers/ide/ide-floppy.c | 21 +-------------------- drivers/ide/ide-tape.c | 36 +----------------------------------- include/linux/ide.h | 16 ++++++++++++++++ 4 files changed, 38 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 8f0842ce77f5..da71bfce92e8 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -119,6 +119,26 @@ void ide_init_pc(struct ide_atapi_pc *pc) } EXPORT_SYMBOL_GPL(ide_init_pc); +/* + * Generate a new packet command request in front of the request queue, before + * the current request, so that it will be processed immediately, on the next + * pass through the driver. + */ +void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk, + struct ide_atapi_pc *pc, struct request *rq) +{ + blk_rq_init(NULL, rq); + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->cmd_flags |= REQ_PREEMPT; + rq->buffer = (char *)pc; + rq->rq_disk = disk; + memcpy(rq->cmd, pc->c, 12); + if (drive->media == ide_tape) + rq->cmd[13] = REQ_IDETAPE_PC1; + ide_do_drive_cmd(drive, rq); +} +EXPORT_SYMBOL_GPL(ide_queue_pc_head); + /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index e04eaa49a8bf..ddce28e77a4e 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -212,25 +212,6 @@ static void idefloppy_update_buffers(ide_drive_t *drive, idefloppy_end_request(drive, 1, 0); } -/* - * Generate a new packet command request in front of the request queue, before - * the current request so that it will be processed immediately, on the next - * pass through the driver. - */ -static void idefloppy_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, - struct request *rq) -{ - struct ide_floppy_obj *floppy = drive->driver_data; - - blk_rq_init(NULL, rq); - rq->buffer = (char *) pc; - rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd_flags |= REQ_PREEMPT; - rq->rq_disk = floppy->disk; - memcpy(rq->cmd, pc->c, 12); - ide_do_drive_cmd(drive, rq); -} - static void ide_floppy_callback(ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; @@ -288,7 +269,7 @@ static void idefloppy_retry_pc(ide_drive_t *drive) (void)ide_read_error(drive); idefloppy_create_request_sense_cmd(pc); - idefloppy_queue_pc_head(drive, pc, rq); + ide_queue_pc_head(drive, floppy->disk, pc, rq); } /* The usual interrupt handler called during a packet command. */ diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 5c879fbcca87..7103b98eb53a 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -159,20 +159,6 @@ struct idetape_bh { #define IDETAPE_LU_RETENSION_MASK 2 #define IDETAPE_LU_EOT_MASK 4 -/* - * Special requests for our block device strategy routine. - * - * In order to service a character device command, we add special requests to - * the tail of our block device request queue and wait for their completion. - */ - -enum { - REQ_IDETAPE_PC1 = (1 << 0), /* packet command (first stage) */ - REQ_IDETAPE_PC2 = (1 << 1), /* packet command (second stage) */ - REQ_IDETAPE_READ = (1 << 2), - REQ_IDETAPE_WRITE = (1 << 3), -}; - /* Error codes returned in rq->errors to the higher part of the driver. */ #define IDETAPE_ERROR_GENERAL 101 #define IDETAPE_ERROR_FILEMARK 102 @@ -612,26 +598,6 @@ static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc) pc->req_xfer = 20; } -/* - * Generate a new packet command request in front of the request queue, before - * the current request, so that it will be processed immediately, on the next - * pass through the driver. - */ -static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, - struct request *rq) -{ - struct ide_tape_obj *tape = drive->driver_data; - - blk_rq_init(NULL, rq); - rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd_flags |= REQ_PREEMPT; - rq->buffer = (char *) pc; - rq->rq_disk = tape->disk; - memcpy(rq->cmd, pc->c, 12); - rq->cmd[13] = REQ_IDETAPE_PC1; - ide_do_drive_cmd(drive, rq); -} - /* * idetape_retry_pc is called when an error was detected during the * last packet command. We queue a request sense packet command in @@ -646,7 +612,7 @@ static void idetape_retry_pc(ide_drive_t *drive) (void)ide_read_error(drive); idetape_create_request_sense_cmd(pc); set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); - idetape_queue_pc_head(drive, pc, rq); + ide_queue_pc_head(drive, tape->disk, pc, rq); } /* diff --git a/include/linux/ide.h b/include/linux/ide.h index bba2f73b99a0..1bd49784e4da 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1121,6 +1121,22 @@ int ide_check_atapi_device(ide_drive_t *, const char *); void ide_init_pc(struct ide_atapi_pc *); +/* + * Special requests for ide-tape block device strategy routine. + * + * In order to service a character device command, we add special requests to + * the tail of our block device request queue and wait for their completion. + */ +enum { + REQ_IDETAPE_PC1 = (1 << 0), /* packet command (first stage) */ + REQ_IDETAPE_PC2 = (1 << 1), /* packet command (second stage) */ + REQ_IDETAPE_READ = (1 << 2), + REQ_IDETAPE_WRITE = (1 << 3), +}; + +void ide_queue_pc_head(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *, + struct request *); + ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), -- cgit v1.2.3 From 2ac07d920604eeee8966d52e70161f9b31fe90a3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:38 +0200 Subject: ide: add ide_queue_pc_tail() helper * Add ide_queue_pc_tail() and convert ide-{floppy,tape}.c to use it instead of ide*_queue_pc_tail(). * Remove no longer used ide*_queue_pc_tail(). There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 23 ++++++++++++++++ drivers/ide/ide-floppy.c | 49 ++++++++++++---------------------- drivers/ide/ide-tape.c | 69 ++++++++++++++++++++---------------------------- include/linux/ide.h | 1 + 4 files changed, 69 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index da71bfce92e8..f82ddfb9a44e 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -139,6 +139,29 @@ void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk, } EXPORT_SYMBOL_GPL(ide_queue_pc_head); +/* + * Add a special packet command request to the tail of the request queue, + * and wait for it to be serviced. + */ +int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk, + struct ide_atapi_pc *pc) +{ + struct request *rq; + int error; + + rq = blk_get_request(drive->queue, READ, __GFP_WAIT); + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->buffer = (char *)pc; + memcpy(rq->cmd, pc->c, 12); + if (drive->media == ide_tape) + rq->cmd[13] = REQ_IDETAPE_PC1; + error = blk_execute_rq(drive->queue, disk, rq, 0); + blk_put_request(rq); + + return error; +} +EXPORT_SYMBOL_GPL(ide_queue_pc_tail); + /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index ddce28e77a4e..de611c57b280 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -553,26 +553,6 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive, return idefloppy_issue_pc(drive, pc); } -/* - * Add a special packet command request to the tail of the request queue, - * and wait for it to be serviced. - */ -static int idefloppy_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc) -{ - struct ide_floppy_obj *floppy = drive->driver_data; - struct request *rq; - int error; - - rq = blk_get_request(drive->queue, READ, __GFP_WAIT); - rq->buffer = (char *) pc; - rq->cmd_type = REQ_TYPE_SPECIAL; - memcpy(rq->cmd, pc->c, 12); - error = blk_execute_rq(drive->queue, floppy->disk, rq, 0); - blk_put_request(rq); - - return error; -} - /* * Look at the flexible disk page parameters. We ignore the CHS capacity * parameters and use the LBA parameters instead. @@ -580,6 +560,7 @@ static int idefloppy_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc) static int ide_floppy_get_flexible_disk_page(ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; + struct gendisk *disk = floppy->disk; struct ide_atapi_pc pc; u8 *page; int capacity, lba_capacity; @@ -588,13 +569,13 @@ static int ide_floppy_get_flexible_disk_page(ide_drive_t *drive) idefloppy_create_mode_sense_cmd(&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE); - if (idefloppy_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, disk, &pc)) { printk(KERN_ERR "ide-floppy: Can't get flexible disk page" " parameters\n"); return 1; } floppy->wp = !!(pc.buf[3] & 0x80); - set_disk_ro(floppy->disk, floppy->wp); + set_disk_ro(disk, floppy->wp); page = &pc.buf[8]; transfer_rate = be16_to_cpup((__be16 *)&pc.buf[8 + 2]); @@ -638,7 +619,7 @@ static int idefloppy_get_sfrp_bit(ide_drive_t *drive) idefloppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE); pc.flags |= PC_FLAG_SUPPRESS_ERROR; - if (idefloppy_queue_pc_tail(drive, &pc)) + if (ide_queue_pc_tail(drive, floppy->disk, &pc)) return 1; floppy->srfp = pc.buf[8 + 2] & 0x40; @@ -652,6 +633,7 @@ static int idefloppy_get_sfrp_bit(ide_drive_t *drive) static int ide_floppy_get_capacity(ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; + struct gendisk *disk = floppy->disk; struct ide_atapi_pc pc; u8 *cap_desc; u8 header_len, desc_cnt; @@ -664,7 +646,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) set_capacity(floppy->disk, 0); idefloppy_create_read_capacity_cmd(&pc); - if (idefloppy_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, disk, &pc)) { printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n"); return 1; } @@ -739,7 +721,8 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) (void) ide_floppy_get_flexible_disk_page(drive); - set_capacity(floppy->disk, floppy->blocks * floppy->bs_factor); + set_capacity(disk, floppy->blocks * floppy->bs_factor); + return rc; } @@ -764,6 +747,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg) { + struct ide_floppy_obj *floppy = drive->driver_data; struct ide_atapi_pc pc; u8 header_len, desc_cnt; int i, blocks, length, u_array_size, u_index; @@ -776,7 +760,7 @@ static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg) return -EINVAL; idefloppy_create_read_capacity_cmd(&pc); - if (idefloppy_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, floppy->disk, &pc)) { printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n"); return -EIO; } @@ -838,7 +822,7 @@ static int ide_floppy_get_format_progress(ide_drive_t *drive, int __user *arg) if (floppy->srfp) { idefloppy_create_request_sense_cmd(&pc); - if (idefloppy_queue_pc_tail(drive, &pc)) + if (ide_queue_pc_tail(drive, floppy->disk, &pc)) return -EIO; if (floppy->sense_key == 2 && @@ -1008,12 +992,13 @@ static ide_driver_t idefloppy_driver = { static void ide_floppy_set_media_lock(ide_drive_t *drive, int on) { + struct ide_floppy_obj *floppy = drive->driver_data; struct ide_atapi_pc pc; /* IOMEGA Clik! drives do not support lock/unlock commands */ if ((drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE) == 0) { idefloppy_create_prevent_cmd(&pc, on); - (void)idefloppy_queue_pc_tail(drive, &pc); + (void)ide_queue_pc_tail(drive, floppy->disk, &pc); } } @@ -1042,9 +1027,9 @@ static int idefloppy_open(struct inode *inode, struct file *filp) ide_init_pc(&pc); pc.c[0] = GPCMD_TEST_UNIT_READY; - if (idefloppy_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, disk, &pc)) { idefloppy_create_start_stop_cmd(&pc, 1); - (void) idefloppy_queue_pc_tail(drive, &pc); + (void)ide_queue_pc_tail(drive, disk, &pc); } if (ide_floppy_get_capacity(drive) @@ -1123,7 +1108,7 @@ static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, if (cmd == CDROMEJECT) { idefloppy_create_start_stop_cmd(pc, 2); - (void) idefloppy_queue_pc_tail(floppy->drive, pc); + (void)ide_queue_pc_tail(drive, floppy->disk, pc); } return 0; @@ -1168,7 +1153,7 @@ static int ide_floppy_format_unit(ide_drive_t *drive, int __user *arg) (void) idefloppy_get_sfrp_bit(drive); idefloppy_create_format_unit_cmd(&pc, blocks, length, flags); - if (idefloppy_queue_pc_tail(drive, &pc)) + if (ide_queue_pc_tail(drive, floppy->disk, &pc)) err = -EIO; out: diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 7103b98eb53a..88cb94554267 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1125,26 +1125,6 @@ static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc) pc->c[0] = TEST_UNIT_READY; } -/* - * We add a special packet command request to the tail of the request queue, and - * wait for it to be serviced. - */ -static int idetape_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc) -{ - struct ide_tape_obj *tape = drive->driver_data; - struct request *rq; - int error; - - rq = blk_get_request(drive->queue, READ, __GFP_WAIT); - rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd[13] = REQ_IDETAPE_PC1; - rq->buffer = (char *)pc; - memcpy(rq->cmd, pc->c, 12); - error = blk_execute_rq(drive->queue, tape->disk, rq, 0); - blk_put_request(rq); - return error; -} - static void idetape_create_load_unload_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc, int cmd) { @@ -1157,6 +1137,7 @@ static void idetape_create_load_unload_cmd(ide_drive_t *drive, static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) { idetape_tape_t *tape = drive->driver_data; + struct gendisk *disk = tape->disk; struct ide_atapi_pc pc; int load_attempted = 0; @@ -1165,7 +1146,7 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) timeout += jiffies; while (time_before(jiffies, timeout)) { idetape_create_test_unit_ready_cmd(&pc); - if (!idetape_queue_pc_tail(drive, &pc)) + if (!ide_queue_pc_tail(drive, disk, &pc)) return 0; if ((tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) || (tape->asc == 0x3A)) { @@ -1174,7 +1155,7 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) return -ENOMEDIUM; idetape_create_load_unload_cmd(drive, &pc, IDETAPE_LU_LOAD_MASK); - idetape_queue_pc_tail(drive, &pc); + ide_queue_pc_tail(drive, disk, &pc); load_attempted = 1; /* not about to be ready */ } else if (!(tape->sense_key == 2 && tape->asc == 4 && @@ -1187,11 +1168,12 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) static int idetape_flush_tape_buffers(ide_drive_t *drive) { + struct ide_tape_obj *tape = drive->driver_data; struct ide_atapi_pc pc; int rc; idetape_create_write_filemark_cmd(drive, &pc, 0); - rc = idetape_queue_pc_tail(drive, &pc); + rc = ide_queue_pc_tail(drive, tape->disk, &pc); if (rc) return rc; idetape_wait_ready(drive, 60 * 5 * HZ); @@ -1214,7 +1196,7 @@ static int idetape_read_position(ide_drive_t *drive) debug_log(DBG_PROCS, "Enter %s\n", __func__); idetape_create_read_position_cmd(&pc); - if (idetape_queue_pc_tail(drive, &pc)) + if (ide_queue_pc_tail(drive, tape->disk, &pc)) return -1; position = tape->first_frame; return position; @@ -1249,12 +1231,13 @@ static int idetape_create_prevent_cmd(ide_drive_t *drive, static int ide_tape_set_media_lock(ide_drive_t *drive, int on) { + struct ide_tape_obj *tape = drive->driver_data; struct ide_atapi_pc pc; if (!idetape_create_prevent_cmd(drive, &pc, on)) return 0; - return idetape_queue_pc_tail(drive, &pc); + return ide_queue_pc_tail(drive, tape->disk, &pc); } static void __ide_tape_discard_merge_buffer(ide_drive_t *drive) @@ -1284,6 +1267,7 @@ static int idetape_position_tape(ide_drive_t *drive, unsigned int block, u8 partition, int skip) { idetape_tape_t *tape = drive->driver_data; + struct gendisk *disk = tape->disk; int retval; struct ide_atapi_pc pc; @@ -1291,12 +1275,12 @@ static int idetape_position_tape(ide_drive_t *drive, unsigned int block, __ide_tape_discard_merge_buffer(drive); idetape_wait_ready(drive, 60 * 5 * HZ); idetape_create_locate_cmd(drive, &pc, block, partition, skip); - retval = idetape_queue_pc_tail(drive, &pc); + retval = ide_queue_pc_tail(drive, disk, &pc); if (retval) return (retval); idetape_create_read_position_cmd(&pc); - return (idetape_queue_pc_tail(drive, &pc)); + return ide_queue_pc_tail(drive, disk, &pc); } static void ide_tape_discard_merge_buffer(ide_drive_t *drive, @@ -1543,20 +1527,20 @@ static void idetape_pad_zeros(ide_drive_t *drive, int bcount) */ static int idetape_rewind_tape(ide_drive_t *drive) { + struct ide_tape_obj *tape = drive->driver_data; + struct gendisk *disk = tape->disk; int retval; struct ide_atapi_pc pc; - idetape_tape_t *tape; - tape = drive->driver_data; debug_log(DBG_SENSE, "Enter %s\n", __func__); idetape_create_rewind_cmd(drive, &pc); - retval = idetape_queue_pc_tail(drive, &pc); + retval = ide_queue_pc_tail(drive, disk, &pc); if (retval) return retval; idetape_create_read_position_cmd(&pc); - retval = idetape_queue_pc_tail(drive, &pc); + retval = ide_queue_pc_tail(drive, disk, &pc); if (retval) return retval; return 0; @@ -1599,6 +1583,7 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op, int mt_count) { idetape_tape_t *tape = drive->driver_data; + struct gendisk *disk = tape->disk; struct ide_atapi_pc pc; int retval, count = 0; int sprev = !!(tape->caps[4] & 0x20); @@ -1623,7 +1608,7 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op, case MTBSF: idetape_create_space_cmd(&pc, mt_count - count, IDETAPE_SPACE_OVER_FILEMARK); - return idetape_queue_pc_tail(drive, &pc); + return ide_queue_pc_tail(drive, disk, &pc); case MTFSFM: case MTBSFM: if (!sprev) @@ -1812,11 +1797,12 @@ static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf, static int idetape_write_filemark(ide_drive_t *drive) { + struct ide_tape_obj *tape = drive->driver_data; struct ide_atapi_pc pc; /* Write a filemark */ idetape_create_write_filemark_cmd(drive, &pc, 1); - if (idetape_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, tape->disk, &pc)) { printk(KERN_ERR "ide-tape: Couldn't write a filemark\n"); return -EIO; } @@ -1839,6 +1825,7 @@ static int idetape_write_filemark(ide_drive_t *drive) static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) { idetape_tape_t *tape = drive->driver_data; + struct gendisk *disk = tape->disk; struct ide_atapi_pc pc; int i, retval; @@ -1877,7 +1864,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) ide_tape_discard_merge_buffer(drive, 0); idetape_create_load_unload_cmd(drive, &pc, IDETAPE_LU_LOAD_MASK); - return idetape_queue_pc_tail(drive, &pc); + return ide_queue_pc_tail(drive, disk, &pc); case MTUNLOAD: case MTOFFL: /* @@ -1891,7 +1878,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) ide_tape_discard_merge_buffer(drive, 0); idetape_create_load_unload_cmd(drive, &pc, !IDETAPE_LU_LOAD_MASK); - retval = idetape_queue_pc_tail(drive, &pc); + retval = ide_queue_pc_tail(drive, disk, &pc); if (!retval) clear_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags); return retval; @@ -1902,14 +1889,14 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) ide_tape_discard_merge_buffer(drive, 0); idetape_create_load_unload_cmd(drive, &pc, IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK); - return idetape_queue_pc_tail(drive, &pc); + return ide_queue_pc_tail(drive, disk, &pc); case MTEOM: idetape_create_space_cmd(&pc, 0, IDETAPE_SPACE_TO_EOD); - return idetape_queue_pc_tail(drive, &pc); + return ide_queue_pc_tail(drive, disk, &pc); case MTERASE: (void)idetape_rewind_tape(drive); idetape_create_erase_cmd(&pc); - return idetape_queue_pc_tail(drive, &pc); + return ide_queue_pc_tail(drive, disk, &pc); case MTSETBLK: if (mt_count) { if (mt_count < tape->blk_size || @@ -2018,7 +2005,7 @@ static void ide_tape_get_bsize_from_bdesc(ide_drive_t *drive) struct ide_atapi_pc pc; idetape_create_mode_sense_cmd(&pc, IDETAPE_BLOCK_DESCRIPTOR); - if (idetape_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, tape->disk, &pc)) { printk(KERN_ERR "ide-tape: Can't get block descriptor\n"); if (tape->blk_size == 0) { printk(KERN_WARNING "ide-tape: Cannot deal with zero " @@ -2170,7 +2157,7 @@ static void idetape_get_inquiry_results(ide_drive_t *drive) char fw_rev[4], vendor_id[8], product_id[16]; idetape_create_inquiry_cmd(&pc); - if (idetape_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, tape->disk, &pc)) { printk(KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name); return; @@ -2199,7 +2186,7 @@ static void idetape_get_mode_sense_results(ide_drive_t *drive) u8 speed, max_speed; idetape_create_mode_sense_cmd(&pc, IDETAPE_CAPABILITIES_PAGE); - if (idetape_queue_pc_tail(drive, &pc)) { + if (ide_queue_pc_tail(drive, tape->disk, &pc)) { printk(KERN_ERR "ide-tape: Can't get tape parameters - assuming" " some default values\n"); tape->blk_size = 512; diff --git a/include/linux/ide.h b/include/linux/ide.h index 1bd49784e4da..b5be2368c96e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1136,6 +1136,7 @@ enum { void ide_queue_pc_head(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *, struct request *); +int ide_queue_pc_tail(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *); ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, -- cgit v1.2.3 From 49cac39e71bd6bbcf934c6ba837e21503902c088 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:38 +0200 Subject: ide-floppy: ->{srfp,wp} -> IDE_AFLAG_{SRFP,WP} Add IDE_AFLAG_{SRFP,WP} drive->atapi_flags and use them instead of ->{srfp,wp} struct ide_floppy_obj fields. There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 24 ++++++++++++++---------- include/linux/ide.h | 18 +++++++++++------- 2 files changed, 25 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index de611c57b280..78d92835a3c1 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -110,10 +110,6 @@ typedef struct ide_floppy_obj { u8 cap_desc[8]; /* Copy of the flexible disk page */ u8 flexible_disk_page[32]; - /* Write protect */ - int wp; - /* Supports format progress report */ - int srfp; } idefloppy_floppy_t; #define IDEFLOPPY_TICKS_DELAY HZ/20 /* default delay for ZIP 100 (50ms) */ @@ -574,8 +570,14 @@ static int ide_floppy_get_flexible_disk_page(ide_drive_t *drive) " parameters\n"); return 1; } - floppy->wp = !!(pc.buf[3] & 0x80); - set_disk_ro(disk, floppy->wp); + + if (pc.buf[3] & 0x80) + drive->atapi_flags |= IDE_AFLAG_WP; + else + drive->atapi_flags &= ~IDE_AFLAG_WP; + + set_disk_ro(disk, !!(drive->atapi_flags & IDE_AFLAG_WP)); + page = &pc.buf[8]; transfer_rate = be16_to_cpup((__be16 *)&pc.buf[8 + 2]); @@ -614,7 +616,7 @@ static int idefloppy_get_sfrp_bit(ide_drive_t *drive) idefloppy_floppy_t *floppy = drive->driver_data; struct ide_atapi_pc pc; - floppy->srfp = 0; + drive->atapi_flags &= ~IDE_AFLAG_SRFP; idefloppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE); pc.flags |= PC_FLAG_SUPPRESS_ERROR; @@ -622,7 +624,9 @@ static int idefloppy_get_sfrp_bit(ide_drive_t *drive) if (ide_queue_pc_tail(drive, floppy->disk, &pc)) return 1; - floppy->srfp = pc.buf[8 + 2] & 0x40; + if (pc.buf[8 + 2] & 0x40) + drive->atapi_flags |= IDE_AFLAG_SRFP; + return 0; } @@ -820,7 +824,7 @@ static int ide_floppy_get_format_progress(ide_drive_t *drive, int __user *arg) struct ide_atapi_pc pc; int progress_indication = 0x10000; - if (floppy->srfp) { + if (drive->atapi_flags & IDE_AFLAG_SRFP) { idefloppy_create_request_sense_cmd(&pc); if (ide_queue_pc_tail(drive, floppy->disk, &pc)) return -EIO; @@ -1044,7 +1048,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) goto out_put_floppy; } - if (floppy->wp && (filp->f_mode & 2)) { + if ((drive->atapi_flags & IDE_AFLAG_WP) && (filp->f_mode & 2)) { ret = -EROFS; goto out_put_floppy; } diff --git a/include/linux/ide.h b/include/linux/ide.h index b5be2368c96e..cc41a885688a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -356,21 +356,25 @@ enum { IDE_AFLAG_CLIK_DRIVE = (1 << 19), /* Requires BH algorithm for packets */ IDE_AFLAG_ZIP_DRIVE = (1 << 20), + /* Write protect */ + IDE_AFLAG_WP = (1 << 21), + /* Supports format progress report */ + IDE_AFLAG_SRFP = (1 << 22), /* ide-tape */ - IDE_AFLAG_IGNORE_DSC = (1 << 21), + IDE_AFLAG_IGNORE_DSC = (1 << 23), /* 0 When the tape position is unknown */ - IDE_AFLAG_ADDRESS_VALID = (1 << 22), + IDE_AFLAG_ADDRESS_VALID = (1 << 24), /* Device already opened */ - IDE_AFLAG_BUSY = (1 << 23), + IDE_AFLAG_BUSY = (1 << 25), /* Attempt to auto-detect the current user block size */ - IDE_AFLAG_DETECT_BS = (1 << 24), + IDE_AFLAG_DETECT_BS = (1 << 26), /* Currently on a filemark */ - IDE_AFLAG_FILEMARK = (1 << 25), + IDE_AFLAG_FILEMARK = (1 << 27), /* 0 = no tape is loaded, so we don't rewind after ejecting */ - IDE_AFLAG_MEDIUM_PRESENT = (1 << 26), + IDE_AFLAG_MEDIUM_PRESENT = (1 << 28), - IDE_AFLAG_NO_AUTOCLOSE = (1 << 27), + IDE_AFLAG_NO_AUTOCLOSE = (1 << 29), }; struct ide_drive_s { -- cgit v1.2.3 From 0578042db3191e1ac76b53d213f2a691c3e1eaed Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:38 +0200 Subject: ide: add ide_set_media_lock() helper * Set IDE_AFLAG_NO_DOORLOCK in idetape_get_mode_sense_result(), check it in ide_tape_set_media_lock() and cleanup idetape_create_prevent_cmd(). * Set IDE_AFLAG_NO_DOORLOCK in ide_floppy_create_read_capacity_cmd() and check it instead of IDE_AFLAG_CLIK_DRIVE in ide_floppy_set_media_lock(). * Add ide_set_media_lock() helper and convert ide-{floppy,tape}.c to use it. * Remove no longer used ide*_create_prevent_cmd()/ide*_set_media_lock(). * Update comment in accordingly. Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 15 +++++++++++++++ drivers/ide/ide-floppy.c | 32 +++++++------------------------- drivers/ide/ide-tape.c | 41 ++++++++++------------------------------- include/linux/ide.h | 6 ++++-- 4 files changed, 36 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f82ddfb9a44e..c647a40c0d33 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -162,6 +162,21 @@ int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk, } EXPORT_SYMBOL_GPL(ide_queue_pc_tail); +int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on) +{ + struct ide_atapi_pc pc; + + if (drive->atapi_flags & IDE_AFLAG_NO_DOORLOCK) + return 0; + + ide_init_pc(&pc); + pc.c[0] = ALLOW_MEDIUM_REMOVAL; + pc.c[4] = on; + + return ide_queue_pc_tail(drive, disk, &pc); +} +EXPORT_SYMBOL_GPL(ide_set_media_lock); + /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index ca12a230d9a6..f39cf404b030 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -325,15 +325,6 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, IDEFLOPPY_WAIT_CMD, NULL); } -static void idefloppy_create_prevent_cmd(struct ide_atapi_pc *pc, int prevent) -{ - debug_log("creating prevent removal command, prevent = %d\n", prevent); - - ide_init_pc(pc); - pc->c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; - pc->c[4] = prevent; -} - void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) { ide_init_pc(pc); @@ -712,6 +703,8 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) if (strncmp((char *)&id[ATA_ID_PROD], "IOMEGA Clik!", 11) == 0) { blk_queue_max_sectors(drive->queue, 64); drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE; + /* IOMEGA Clik! drives do not support lock/unlock commands */ + drive->atapi_flags |= IDE_AFLAG_NO_DOORLOCK; } (void) ide_floppy_get_capacity(drive); @@ -782,18 +775,6 @@ static ide_driver_t idefloppy_driver = { #endif }; -static void ide_floppy_set_media_lock(ide_drive_t *drive, int on) -{ - struct ide_floppy_obj *floppy = drive->driver_data; - struct ide_atapi_pc pc; - - /* IOMEGA Clik! drives do not support lock/unlock commands */ - if ((drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE) == 0) { - idefloppy_create_prevent_cmd(&pc, on); - (void)ide_queue_pc_tail(drive, floppy->disk, &pc); - } -} - static int idefloppy_open(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; @@ -842,7 +823,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) } drive->atapi_flags |= IDE_AFLAG_MEDIA_CHANGED; - ide_floppy_set_media_lock(drive, 1); + ide_set_media_lock(drive, disk, 1); check_disk_change(inode->i_bdev); } else if (drive->atapi_flags & IDE_AFLAG_FORMAT_IN_PROGRESS) { ret = -EBUSY; @@ -865,7 +846,7 @@ static int idefloppy_release(struct inode *inode, struct file *filp) debug_log("Reached %s\n", __func__); if (floppy->openers == 1) { - ide_floppy_set_media_lock(drive, 0); + ide_set_media_lock(drive, disk, 0); drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; } @@ -891,16 +872,17 @@ static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned long arg, unsigned int cmd) { idefloppy_floppy_t *floppy = drive->driver_data; + struct gendisk *disk = floppy->disk; int prevent = (arg && cmd != CDROMEJECT) ? 1 : 0; if (floppy->openers > 1) return -EBUSY; - ide_floppy_set_media_lock(drive, prevent); + ide_set_media_lock(drive, disk, prevent); if (cmd == CDROMEJECT) { idefloppy_create_start_stop_cmd(pc, 2); - (void)ide_queue_pc_tail(drive, floppy->disk, pc); + (void)ide_queue_pc_tail(drive, disk, pc); } return 0; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 88cb94554267..b7f3eebc0d15 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1214,32 +1214,6 @@ static void idetape_create_locate_cmd(ide_drive_t *drive, pc->flags |= PC_FLAG_WAIT_FOR_DSC; } -static int idetape_create_prevent_cmd(ide_drive_t *drive, - struct ide_atapi_pc *pc, int prevent) -{ - idetape_tape_t *tape = drive->driver_data; - - /* device supports locking according to capabilities page */ - if (!(tape->caps[6] & 0x01)) - return 0; - - ide_init_pc(pc); - pc->c[0] = ALLOW_MEDIUM_REMOVAL; - pc->c[4] = prevent; - return 1; -} - -static int ide_tape_set_media_lock(ide_drive_t *drive, int on) -{ - struct ide_tape_obj *tape = drive->driver_data; - struct ide_atapi_pc pc; - - if (!idetape_create_prevent_cmd(drive, &pc, on)) - return 0; - - return ide_queue_pc_tail(drive, tape->disk, &pc); -} - static void __ide_tape_discard_merge_buffer(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -1872,7 +1846,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) * attempting to eject. */ if (tape->door_locked) { - if (!ide_tape_set_media_lock(drive, 0)) + if (!ide_set_media_lock(drive, disk, 0)) tape->door_locked = DOOR_UNLOCKED; } ide_tape_discard_merge_buffer(drive, 0); @@ -1917,13 +1891,13 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) case MTFSR: case MTBSR: case MTLOCK: - retval = ide_tape_set_media_lock(drive, 1); + retval = ide_set_media_lock(drive, disk, 1); if (retval) return retval; tape->door_locked = DOOR_EXPLICITLY_LOCKED; return 0; case MTUNLOCK: - retval = ide_tape_set_media_lock(drive, 0); + retval = ide_set_media_lock(drive, disk, 0); if (retval) return retval; tape->door_locked = DOOR_UNLOCKED; @@ -2087,7 +2061,7 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp) /* Lock the tape drive door so user can't eject. */ if (tape->chrdev_dir == IDETAPE_DIR_NONE) { - if (!ide_tape_set_media_lock(drive, 1)) { + if (!ide_set_media_lock(drive, tape->disk, 1)) { if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) tape->door_locked = DOOR_LOCKED; } @@ -2140,7 +2114,7 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp) (void) idetape_rewind_tape(drive); if (tape->chrdev_dir == IDETAPE_DIR_NONE) { if (tape->door_locked == DOOR_LOCKED) { - if (!ide_tape_set_media_lock(drive, 0)) + if (!ide_set_media_lock(drive, tape->disk, 0)) tape->door_locked = DOOR_UNLOCKED; } } @@ -2218,6 +2192,11 @@ static void idetape_get_mode_sense_results(ide_drive_t *drive) } memcpy(&tape->caps, caps, 20); + + /* device lacks locking support according to capabilities page */ + if ((caps[6] & 1) == 0) + drive->atapi_flags |= IDE_AFLAG_NO_DOORLOCK; + if (caps[7] & 0x02) tape->blk_size = 512; else if (caps[7] & 0x04) diff --git a/include/linux/ide.h b/include/linux/ide.h index cc41a885688a..ac067a3c1be3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -317,10 +317,10 @@ struct ide_acpi_hwif_link; enum { IDE_AFLAG_DRQ_INTERRUPT = (1 << 0), IDE_AFLAG_MEDIA_CHANGED = (1 << 1), - - /* ide-cd */ /* Drive cannot lock the door. */ IDE_AFLAG_NO_DOORLOCK = (1 << 2), + + /* ide-cd */ /* Drive cannot eject the disc. */ IDE_AFLAG_NO_EJECT = (1 << 3), /* Drive is a pre ATAPI 1.2 drive. */ @@ -1142,6 +1142,8 @@ void ide_queue_pc_head(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *, struct request *); int ide_queue_pc_tail(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *); +int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); + ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), -- cgit v1.2.3 From 0c8a6c7aead1d3be85ce53e3aaacd52e38ede03e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:39 +0200 Subject: ide: add ide_do_start_stop() helper * Add ide_do_start_stop() helper and convert ide-{floppy,tape}.c to use it. * Remove no longer used idefloppy_create_start_stop_cmd() and idetape_create_load_unload_cmd(). There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 15 +++++++++++++++ drivers/ide/ide-floppy.c | 19 ++++--------------- drivers/ide/ide-tape.c | 24 ++++-------------------- include/linux/ide.h | 1 + 4 files changed, 24 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index c647a40c0d33..58411591edf3 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -162,6 +162,21 @@ int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk, } EXPORT_SYMBOL_GPL(ide_queue_pc_tail); +int ide_do_start_stop(ide_drive_t *drive, struct gendisk *disk, int start) +{ + struct ide_atapi_pc pc; + + ide_init_pc(&pc); + pc.c[0] = START_STOP; + pc.c[4] = start; + + if (drive->media == ide_tape) + pc.flags |= PC_FLAG_WAIT_FOR_DSC; + + return ide_queue_pc_tail(drive, disk, &pc); +} +EXPORT_SYMBOL_GPL(ide_do_start_stop); + int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on) { struct ide_atapi_pc pc; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index f39cf404b030..b221a456e535 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -359,13 +359,6 @@ void ide_floppy_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) pc->req_xfer = length; } -static void idefloppy_create_start_stop_cmd(struct ide_atapi_pc *pc, int start) -{ - ide_init_pc(pc); - pc->c[0] = GPCMD_START_STOP_UNIT; - pc->c[4] = start; -} - static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy, struct ide_atapi_pc *pc, struct request *rq, unsigned long sector) @@ -800,10 +793,8 @@ static int idefloppy_open(struct inode *inode, struct file *filp) ide_init_pc(&pc); pc.c[0] = GPCMD_TEST_UNIT_READY; - if (ide_queue_pc_tail(drive, disk, &pc)) { - idefloppy_create_start_stop_cmd(&pc, 1); - (void)ide_queue_pc_tail(drive, disk, &pc); - } + if (ide_queue_pc_tail(drive, disk, &pc)) + ide_do_start_stop(drive, disk, 1); if (ide_floppy_get_capacity(drive) && (filp->f_flags & O_NDELAY) == 0 @@ -880,10 +871,8 @@ static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_set_media_lock(drive, disk, prevent); - if (cmd == CDROMEJECT) { - idefloppy_create_start_stop_cmd(pc, 2); - (void)ide_queue_pc_tail(drive, disk, pc); - } + if (cmd == CDROMEJECT) + ide_do_start_stop(drive, disk, 2); return 0; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index b7f3eebc0d15..5204bef4a21c 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1125,15 +1125,6 @@ static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc) pc->c[0] = TEST_UNIT_READY; } -static void idetape_create_load_unload_cmd(ide_drive_t *drive, - struct ide_atapi_pc *pc, int cmd) -{ - ide_init_pc(pc); - pc->c[0] = START_STOP; - pc->c[4] = cmd; - pc->flags |= PC_FLAG_WAIT_FOR_DSC; -} - static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) { idetape_tape_t *tape = drive->driver_data; @@ -1153,9 +1144,7 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) /* no media */ if (load_attempted) return -ENOMEDIUM; - idetape_create_load_unload_cmd(drive, &pc, - IDETAPE_LU_LOAD_MASK); - ide_queue_pc_tail(drive, disk, &pc); + ide_do_start_stop(drive, disk, IDETAPE_LU_LOAD_MASK); load_attempted = 1; /* not about to be ready */ } else if (!(tape->sense_key == 2 && tape->asc == 4 && @@ -1836,9 +1825,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) return 0; case MTLOAD: ide_tape_discard_merge_buffer(drive, 0); - idetape_create_load_unload_cmd(drive, &pc, - IDETAPE_LU_LOAD_MASK); - return ide_queue_pc_tail(drive, disk, &pc); + return ide_do_start_stop(drive, disk, IDETAPE_LU_LOAD_MASK); case MTUNLOAD: case MTOFFL: /* @@ -1850,9 +1837,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) tape->door_locked = DOOR_UNLOCKED; } ide_tape_discard_merge_buffer(drive, 0); - idetape_create_load_unload_cmd(drive, &pc, - !IDETAPE_LU_LOAD_MASK); - retval = ide_queue_pc_tail(drive, disk, &pc); + retval = ide_do_start_stop(drive, disk, !IDETAPE_LU_LOAD_MASK); if (!retval) clear_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags); return retval; @@ -1861,9 +1846,8 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) return idetape_flush_tape_buffers(drive); case MTRETEN: ide_tape_discard_merge_buffer(drive, 0); - idetape_create_load_unload_cmd(drive, &pc, + return ide_do_start_stop(drive, disk, IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK); - return ide_queue_pc_tail(drive, disk, &pc); case MTEOM: idetape_create_space_cmd(&pc, 0, IDETAPE_SPACE_TO_EOD); return ide_queue_pc_tail(drive, disk, &pc); diff --git a/include/linux/ide.h b/include/linux/ide.h index ac067a3c1be3..be79122b9431 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1142,6 +1142,7 @@ void ide_queue_pc_head(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *, struct request *); int ide_queue_pc_tail(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *); +int ide_do_start_stop(ide_drive_t *, struct gendisk *, int); int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, -- cgit v1.2.3 From de699ad595fb45022d1b049ed91ffd06fdd16c13 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:39 +0200 Subject: ide: add ide_do_test_unit_ready() helper * Add ide_do_test_unit_ready() helper and convert ide-{floppy,tape}.c to use it. * Remove no longer used idetape_create_test_unit_ready_cmd(). There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 11 +++++++++++ drivers/ide/ide-floppy.c | 6 +----- drivers/ide/ide-tape.c | 10 +--------- include/linux/ide.h | 1 + 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 58411591edf3..608c5bade929 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -162,6 +162,17 @@ int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk, } EXPORT_SYMBOL_GPL(ide_queue_pc_tail); +int ide_do_test_unit_ready(ide_drive_t *drive, struct gendisk *disk) +{ + struct ide_atapi_pc pc; + + ide_init_pc(&pc); + pc.c[0] = TEST_UNIT_READY; + + return ide_queue_pc_tail(drive, disk, &pc); +} +EXPORT_SYMBOL_GPL(ide_do_test_unit_ready); + int ide_do_start_stop(ide_drive_t *drive, struct gendisk *disk, int start) { struct ide_atapi_pc pc; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index b221a456e535..4d7e9ef82425 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -773,7 +773,6 @@ static int idefloppy_open(struct inode *inode, struct file *filp) struct gendisk *disk = inode->i_bdev->bd_disk; struct ide_floppy_obj *floppy; ide_drive_t *drive; - struct ide_atapi_pc pc; int ret = 0; debug_log("Reached %s\n", __func__); @@ -790,10 +789,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; /* Just in case */ - ide_init_pc(&pc); - pc.c[0] = GPCMD_TEST_UNIT_READY; - - if (ide_queue_pc_tail(drive, disk, &pc)) + if (ide_do_test_unit_ready(drive, disk)) ide_do_start_stop(drive, disk, 1); if (ide_floppy_get_capacity(drive) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 5204bef4a21c..737dd7db6bb7 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1119,25 +1119,17 @@ static void idetape_create_write_filemark_cmd(ide_drive_t *drive, pc->flags |= PC_FLAG_WAIT_FOR_DSC; } -static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc) -{ - ide_init_pc(pc); - pc->c[0] = TEST_UNIT_READY; -} - static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) { idetape_tape_t *tape = drive->driver_data; struct gendisk *disk = tape->disk; - struct ide_atapi_pc pc; int load_attempted = 0; /* Wait for the tape to become ready */ set_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags); timeout += jiffies; while (time_before(jiffies, timeout)) { - idetape_create_test_unit_ready_cmd(&pc); - if (!ide_queue_pc_tail(drive, disk, &pc)) + if (ide_do_test_unit_ready(drive, disk) == 0) return 0; if ((tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) || (tape->asc == 0x3A)) { diff --git a/include/linux/ide.h b/include/linux/ide.h index be79122b9431..55098eed3b21 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1142,6 +1142,7 @@ void ide_queue_pc_head(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *, struct request *); int ide_queue_pc_tail(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *); +int ide_do_test_unit_ready(ide_drive_t *, struct gendisk *); int ide_do_start_stop(ide_drive_t *, struct gendisk *, int); int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); -- cgit v1.2.3 From d6e2955a6b82d2312b5ff885ce13c8ab54d59d96 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 10 Oct 2008 22:39:39 +0200 Subject: ide: move IDE{FLOPPY,TAPE}_WAIT_CMD defines to While at it: * IDE{FLOPPY,TAPE}_WAIT_CMD -> WAIT_{FLOPPY,TAPE}_CMD * Use enum for WAIT_* defines. There should be no functional changes caused by this patch. Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 13 ++++--------- drivers/ide/ide-tape.c | 13 +++---------- include/linux/ide.h | 28 ++++++++++++++++++++++------ 3 files changed, 29 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 4d7e9ef82425..a63aba2c8265 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -61,10 +61,6 @@ #define debug_log(fmt, args...) do {} while (0) #endif - -/* Some drives require a longer irq timeout. */ -#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD) - /* * After each failed packet command we issue a request sense command and retry * the packet command IDEFLOPPY_MAX_PC_RETRIES times. @@ -226,7 +222,7 @@ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) idefloppy_floppy_t *floppy = drive->driver_data; return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr, - IDEFLOPPY_WAIT_CMD, NULL, idefloppy_update_buffers, + WAIT_FLOPPY_CMD, NULL, idefloppy_update_buffers, idefloppy_retry_pc, NULL, ide_io_buffers); } @@ -244,10 +240,9 @@ static int idefloppy_transfer_pc(ide_drive_t *drive) drive->hwif->tp_ops->output_data(drive, NULL, floppy->pc->c, 12); /* Timeout for the packet command */ - return IDEFLOPPY_WAIT_CMD; + return WAIT_FLOPPY_CMD; } - /* * Called as an interrupt (or directly). When the device says it's ready for a * packet, we schedule the packet transfer to occur about 2-3 ticks later in @@ -272,7 +267,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) timeout = floppy->ticks; expiry = &idefloppy_transfer_pc; } else { - timeout = IDEFLOPPY_WAIT_CMD; + timeout = WAIT_FLOPPY_CMD; expiry = NULL; } @@ -322,7 +317,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->retries++; return ide_issue_pc(drive, pc, idefloppy_start_pc_transfer, - IDEFLOPPY_WAIT_CMD, NULL); + WAIT_FLOPPY_CMD, NULL); } void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 737dd7db6bb7..25190966ed39 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -80,13 +80,6 @@ enum { */ #define IDETAPE_MAX_PC_RETRIES 3 -/* - * Some drives (for example, Seagate STT3401A Travan) require a very long - * timeout, because they don't return an interrupt or clear their busy bit - * until after the command completes (even retension commands). - */ -#define IDETAPE_WAIT_CMD (900*HZ) - /* * The following parameter is used to select the point in the internal tape fifo * in which we will start to refill the buffer. Decreasing the following @@ -663,7 +656,7 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; - return ide_pc_intr(drive, tape->pc, idetape_pc_intr, IDETAPE_WAIT_CMD, + return ide_pc_intr(drive, tape->pc, idetape_pc_intr, WAIT_TAPE_CMD, NULL, idetape_update_buffers, idetape_retry_pc, ide_tape_handle_dsc, ide_tape_io_buffers); } @@ -709,7 +702,7 @@ static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) idetape_tape_t *tape = drive->driver_data; return ide_transfer_pc(drive, tape->pc, idetape_pc_intr, - IDETAPE_WAIT_CMD, NULL); + WAIT_TAPE_CMD, NULL); } static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, @@ -758,7 +751,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->retries++; return ide_issue_pc(drive, pc, idetape_transfer_pc, - IDETAPE_WAIT_CMD, NULL); + WAIT_TAPE_CMD, NULL); } /* A mode sense command is used to "sense" tape parameters. */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 55098eed3b21..a9eced25acce 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -133,12 +133,28 @@ struct ide_io_ports { /* * Timeouts for various operations: */ -#define WAIT_DRQ (HZ/10) /* 100msec - spec allows up to 20ms */ -#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */ -#define WAIT_PIDENTIFY (10*HZ) /* 10sec - should be less than 3ms (?), if all ATAPI CD is closed at boot */ -#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */ -#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */ -#define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */ +enum { + /* spec allows up to 20ms */ + WAIT_DRQ = HZ / 10, /* 100ms */ + /* some laptops are very slow */ + WAIT_READY = 5 * HZ, /* 5s */ + /* should be less than 3ms (?), if all ATAPI CD is closed at boot */ + WAIT_PIDENTIFY = 10 * HZ, /* 10s */ + /* worst case when spinning up */ + WAIT_WORSTCASE = 30 * HZ, /* 30s */ + /* maximum wait for an IRQ to happen */ + WAIT_CMD = 10 * HZ, /* 10s */ + /* Some drives require a longer IRQ timeout. */ + WAIT_FLOPPY_CMD = 50 * HZ, /* 50s */ + /* + * Some drives (for example, Seagate STT3401A Travan) require a very + * long timeout, because they don't return an interrupt or clear their + * BSY bit until after the command completes (even retension commands). + */ + WAIT_TAPE_CMD = 900 * HZ, /* 900s */ + /* minimum sleep time */ + WAIT_MIN_SLEEP = HZ / 50, /* 20ms */ +}; /* * Op codes for special requests to be handled by ide_special_rq(). -- cgit v1.2.3 From 92f1f8fd8040e7b50a67a850a935509bb01201bb Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Fri, 10 Oct 2008 22:39:40 +0200 Subject: ide: Remove ide_spin_wait_hwgroup() and use special requests instead Use a special request for serialisation purposes and get rid of the awkward ide_spin_wait_hwgroup(). This also involves converting the ide_devset structure so it can be shared by the /proc and the ioctl code. Signed-off-by: Elias Oltmanns [bart: use rq->cmd[] directly] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 8 +-- drivers/ide/ide-disk.c | 66 +++++++++++------------ drivers/ide/ide-floppy.c | 20 +++---- drivers/ide/ide-io.c | 40 ++++++++++++++ drivers/ide/ide-ioctls.c | 21 ++++---- drivers/ide/ide-proc.c | 102 +++++++++++++++++------------------ drivers/ide/ide-tape.c | 48 ++++++++--------- drivers/ide/ide.c | 81 ++++++---------------------- drivers/scsi/ide-scsi.c | 34 ++++++------ include/linux/ide.h | 138 +++++++++++++++++++++++++---------------------- 10 files changed, 274 insertions(+), 284 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 7ea90de55058..465a92ca0179 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1809,11 +1809,11 @@ static ide_proc_entry_t idecd_proc[] = { { NULL, 0, NULL, NULL } }; -ide_devset_rw(dsc_overlap, 0, 1, dsc_overlap); +ide_devset_rw_field(dsc_overlap, dsc_overlap); -static const struct ide_devset *idecd_settings[] = { - &ide_devset_dsc_overlap, - NULL +static const struct ide_proc_devset idecd_settings[] = { + IDE_PROC_DEVSET(dsc_overlap, 0, 1), + { 0 }, }; #endif diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 119063470820..01846f244b40 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -575,11 +575,8 @@ static int set_nowerr(ide_drive_t *drive, int arg) if (arg < 0 || arg > 1) return -EINVAL; - if (ide_spin_wait_hwgroup(drive)) - return -EBUSY; drive->nowerr = arg; drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; - spin_unlock_irq(&ide_lock); return 0; } @@ -702,33 +699,34 @@ static int set_addressing(ide_drive_t *drive, int arg) return 0; } +ide_devset_rw(acoustic, acoustic); +ide_devset_rw(address, addressing); +ide_devset_rw(multcount, multcount); +ide_devset_rw(wcache, wcache); + +ide_devset_rw_sync(nowerr, nowerr); + #ifdef CONFIG_IDE_PROC_FS -ide_devset_rw_nolock(acoustic, 0, 254, acoustic); -ide_devset_rw_nolock(address, 0, 2, addressing); -ide_devset_rw_nolock(multcount, 0, 16, multcount); -ide_devset_rw_nolock(nowerr, 0, 1, nowerr); -ide_devset_rw_nolock(wcache, 0, 1, wcache); - -ide_devset_rw(bios_cyl, 0, 65535, bios_cyl); -ide_devset_rw(bios_head, 0, 255, bios_head); -ide_devset_rw(bios_sect, 0, 63, bios_sect); -ide_devset_rw(failures, 0, 65535, failures); -ide_devset_rw(lun, 0, 7, lun); -ide_devset_rw(max_failures, 0, 65535, max_failures); - -static const struct ide_devset *idedisk_settings[] = { - &ide_devset_acoustic, - &ide_devset_address, - &ide_devset_bios_cyl, - &ide_devset_bios_head, - &ide_devset_bios_sect, - &ide_devset_failures, - &ide_devset_lun, - &ide_devset_max_failures, - &ide_devset_multcount, - &ide_devset_nowerr, - &ide_devset_wcache, - NULL +ide_devset_rw_field(bios_cyl, bios_cyl); +ide_devset_rw_field(bios_head, bios_head); +ide_devset_rw_field(bios_sect, bios_sect); +ide_devset_rw_field(failures, failures); +ide_devset_rw_field(lun, lun); +ide_devset_rw_field(max_failures, max_failures); + +static const struct ide_proc_devset idedisk_settings[] = { + IDE_PROC_DEVSET(acoustic, 0, 254), + IDE_PROC_DEVSET(address, 0, 2), + IDE_PROC_DEVSET(bios_cyl, 0, 65535), + IDE_PROC_DEVSET(bios_head, 0, 255), + IDE_PROC_DEVSET(bios_sect, 0, 63), + IDE_PROC_DEVSET(failures, 0, 65535), + IDE_PROC_DEVSET(lun, 0, 7), + IDE_PROC_DEVSET(max_failures, 0, 65535), + IDE_PROC_DEVSET(multcount, 0, 16), + IDE_PROC_DEVSET(nowerr, 0, 1), + IDE_PROC_DEVSET(wcache, 0, 1), + { 0 }, }; #endif @@ -1001,11 +999,11 @@ static int idedisk_getgeo(struct block_device *bdev, struct hd_geometry *geo) } static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = { -{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, get_addressing, set_addressing }, -{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, get_multcount, set_multcount }, -{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, get_nowerr, set_nowerr }, -{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, get_wcache, set_wcache }, -{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, get_acoustic, set_acoustic }, +{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, &ide_devset_address }, +{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, &ide_devset_multcount }, +{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, &ide_devset_nowerr }, +{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, &ide_devset_wcache }, +{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, &ide_devset_acoustic }, { 0 } }; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index a63aba2c8265..d36f155470a4 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -629,9 +629,9 @@ static sector_t idefloppy_capacity(ide_drive_t *drive) } #ifdef CONFIG_IDE_PROC_FS -ide_devset_rw(bios_cyl, 0, 1023, bios_cyl); -ide_devset_rw(bios_head, 0, 255, bios_head); -ide_devset_rw(bios_sect, 0, 63, bios_sect); +ide_devset_rw_field(bios_cyl, bios_cyl); +ide_devset_rw_field(bios_head, bios_head); +ide_devset_rw_field(bios_sect, bios_sect); static int get_ticks(ide_drive_t *drive) { @@ -646,14 +646,14 @@ static int set_ticks(ide_drive_t *drive, int arg) return 0; } -IDE_DEVSET(ticks, S_RW, 0, 255, get_ticks, set_ticks); +IDE_DEVSET(ticks, DS_SYNC, get_ticks, set_ticks); -static const struct ide_devset *idefloppy_settings[] = { - &ide_devset_bios_cyl, - &ide_devset_bios_head, - &ide_devset_bios_sect, - &ide_devset_ticks, - NULL +static const struct ide_proc_devset idefloppy_settings[] = { + IDE_PROC_DEVSET(bios_cyl, 0, 1023), + IDE_PROC_DEVSET(bios_head, 0, 255), + IDE_PROC_DEVSET(bios_sect, 0, 63), + IDE_PROC_DEVSET(ticks, 0, 255), + { 0 }, }; #endif diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ec6664b0d3a9..1c51949833be 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -716,9 +716,49 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, return ide_stopped; } +int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting, + int arg) +{ + struct request_queue *q = drive->queue; + struct request *rq; + int ret = 0; + + if (!(setting->flags & DS_SYNC)) + return setting->set(drive, arg); + + rq = blk_get_request(q, READ, GFP_KERNEL); + if (!rq) + return -ENOMEM; + + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->cmd_len = 5; + rq->cmd[0] = REQ_DEVSET_EXEC; + *(int *)&rq->cmd[1] = arg; + rq->special = setting->set; + + if (blk_execute_rq(q, NULL, rq, 0)) + ret = rq->errors; + blk_put_request(rq); + + return ret; +} +EXPORT_SYMBOL_GPL(ide_devset_execute); + static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) { switch (rq->cmd[0]) { + case REQ_DEVSET_EXEC: + { + int err, (*setfunc)(ide_drive_t *, int) = rq->special; + + err = setfunc(drive, *(int *)&rq->cmd[1]); + if (err) + rq->errors = err; + else + err = 1; + ide_end_request(drive, err, 0); + return ide_stopped; + } case REQ_DRIVE_RESET: return ide_do_reset(drive); default: diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c index 7a0d62e7286b..cf01564901af 100644 --- a/drivers/ide/ide-ioctls.c +++ b/drivers/ide/ide-ioctls.c @@ -6,11 +6,11 @@ #include static const struct ide_ioctl_devset ide_ioctl_settings[] = { -{ HDIO_GET_32BIT, HDIO_SET_32BIT, get_io_32bit, set_io_32bit }, -{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, get_ksettings, set_ksettings }, -{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, get_unmaskirq, set_unmaskirq }, -{ HDIO_GET_DMA, HDIO_SET_DMA, get_using_dma, set_using_dma }, -{ -1, HDIO_SET_PIO_MODE, NULL, set_pio_mode }, +{ HDIO_GET_32BIT, HDIO_SET_32BIT, &ide_devset_io_32bit }, +{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, &ide_devset_keepsettings }, +{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, &ide_devset_unmaskirq }, +{ HDIO_GET_DMA, HDIO_SET_DMA, &ide_devset_using_dma }, +{ -1, HDIO_SET_PIO_MODE, &ide_devset_pio_mode }, { 0 } }; @@ -18,13 +18,14 @@ int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, unsigned int cmd, unsigned long arg, const struct ide_ioctl_devset *s) { + const struct ide_devset *ds; unsigned long flags; int err = -EOPNOTSUPP; - for (; s->get_ioctl; s++) { - if (s->get && s->get_ioctl == cmd) + for (; (ds = s->setting); s++) { + if (ds->get && s->get_ioctl == cmd) goto read_val; - else if (s->set && s->set_ioctl == cmd) + else if (ds->set && s->set_ioctl == cmd) goto set_val; } @@ -33,7 +34,7 @@ int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, read_val: mutex_lock(&ide_setting_mtx); spin_lock_irqsave(&ide_lock, flags); - err = s->get(drive); + err = ds->get(drive); spin_unlock_irqrestore(&ide_lock, flags); mutex_unlock(&ide_setting_mtx); return err >= 0 ? put_user(err, (long __user *)arg) : err; @@ -46,7 +47,7 @@ set_val: err = -EACCES; else { mutex_lock(&ide_setting_mtx); - err = s->set(drive, arg); + err = ide_devset_execute(drive, ds, arg); mutex_unlock(&ide_setting_mtx); } } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 6489c647be82..e7030a491463 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -124,15 +124,16 @@ static int proc_ide_read_identify * setting semaphore */ -static const struct ide_devset *ide_find_setting(const struct ide_devset **st, - char *name) +static +const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st, + char *name) { - while (*st) { - if (strcmp((*st)->name, name) == 0) + while (st->name) { + if (strcmp(st->name, name) == 0) break; st++; } - return *st; + return st->name ? st : NULL; } /** @@ -149,15 +150,16 @@ static const struct ide_devset *ide_find_setting(const struct ide_devset **st, */ static int ide_read_setting(ide_drive_t *drive, - const struct ide_devset *setting) + const struct ide_proc_devset *setting) { + const struct ide_devset *ds = setting->setting; int val = -EINVAL; - if ((setting->flags & S_READ)) { + if (ds->get) { unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - val = setting->get(drive); + val = ds->get(drive); spin_unlock_irqrestore(&ide_lock, flags); } @@ -183,24 +185,21 @@ static int ide_read_setting(ide_drive_t *drive, */ static int ide_write_setting(ide_drive_t *drive, - const struct ide_devset *setting, int val) + const struct ide_proc_devset *setting, int val) { + const struct ide_devset *ds = setting->setting; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (setting->set && (setting->flags & S_NOLOCK)) - return setting->set(drive, val); - if (!(setting->flags & S_WRITE)) + if (!ds->set) return -EPERM; - if (val < setting->min || val > setting->max) + if ((ds->flags & DS_SYNC) + && (val < setting->min || val > setting->max)) return -EINVAL; - if (ide_spin_wait_hwgroup(drive)) - return -EBUSY; - setting->set(drive, val); - spin_unlock_irq(&ide_lock); - return 0; + return ide_devset_execute(drive, ds, val); } -static ide_devset_get(xfer_rate, current_speed); +ide_devset_get(xfer_rate, current_speed); static int set_xfer_rate (ide_drive_t *drive, int arg) { @@ -226,29 +225,22 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) return err; } -ide_devset_rw_nolock(current_speed, 0, 70, xfer_rate); -ide_devset_rw_nolock(io_32bit, 0, 1 + (SUPPORT_VLB_SYNC << 1), io_32bit); -ide_devset_rw_nolock(keepsettings, 0, 1, ksettings); -ide_devset_rw_nolock(unmaskirq, 0, 1, unmaskirq); -ide_devset_rw_nolock(using_dma, 0, 1, using_dma); - -ide_devset_w_nolock(pio_mode, 0, 255, pio_mode); - -ide_devset_rw(init_speed, 0, 70, init_speed); -ide_devset_rw(nice1, 0, 1, nice1); -ide_devset_rw(number, 0, 3, dn); - -static const struct ide_devset *ide_generic_settings[] = { - &ide_devset_current_speed, - &ide_devset_init_speed, - &ide_devset_io_32bit, - &ide_devset_keepsettings, - &ide_devset_nice1, - &ide_devset_number, - &ide_devset_pio_mode, - &ide_devset_unmaskirq, - &ide_devset_using_dma, - NULL +ide_devset_rw(current_speed, xfer_rate); +ide_devset_rw_field(init_speed, init_speed); +ide_devset_rw_field(nice1, nice1); +ide_devset_rw_field(number, dn); + +static const struct ide_proc_devset ide_generic_settings[] = { + IDE_PROC_DEVSET(current_speed, 0, 70), + IDE_PROC_DEVSET(init_speed, 0, 70), + IDE_PROC_DEVSET(io_32bit, 0, 1 + (SUPPORT_VLB_SYNC << 1)), + IDE_PROC_DEVSET(keepsettings, 0, 1), + IDE_PROC_DEVSET(nice1, 0, 1), + IDE_PROC_DEVSET(number, 0, 3), + IDE_PROC_DEVSET(pio_mode, 0, 255), + IDE_PROC_DEVSET(unmaskirq, 0, 1), + IDE_PROC_DEVSET(using_dma, 0, 1), + { 0 }, }; static void proc_ide_settings_warn(void) @@ -266,7 +258,8 @@ static void proc_ide_settings_warn(void) static int proc_ide_read_settings (char *page, char **start, off_t off, int count, int *eof, void *data) { - const struct ide_devset *setting, **g, **d; + const struct ide_proc_devset *setting, *g, *d; + const struct ide_devset *ds; ide_drive_t *drive = (ide_drive_t *) data; char *out = page; int len, rc, mul_factor, div_factor; @@ -278,17 +271,17 @@ static int proc_ide_read_settings d = drive->settings; out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n"); out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n"); - while (*g || (d && *d)) { + while (g->name || (d && d->name)) { /* read settings in the alphabetical order */ - if (*g && d && *d) { - if (strcmp((*d)->name, (*g)->name) < 0) - setting = *d++; + if (g->name && d && d->name) { + if (strcmp(d->name, g->name) < 0) + setting = d++; else - setting = *g++; - } else if (d && *d) { - setting = *d++; + setting = g++; + } else if (d && d->name) { + setting = d++; } else - setting = *g++; + setting = g++; mul_factor = setting->mulf ? setting->mulf(drive) : 1; div_factor = setting->divf ? setting->divf(drive) : 1; out += sprintf(out, "%-24s", setting->name); @@ -298,9 +291,10 @@ static int proc_ide_read_settings else out += sprintf(out, "%-16s", "write-only"); out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor); - if (setting->flags & S_READ) + ds = setting->setting; + if (ds->get) out += sprintf(out, "r"); - if (setting->flags & S_WRITE) + if (ds->set) out += sprintf(out, "w"); out += sprintf(out, "\n"); } @@ -319,7 +313,7 @@ static int proc_ide_write_settings(struct file *file, const char __user *buffer, int for_real = 0, mul_factor, div_factor; unsigned long n; - const struct ide_devset *setting; + const struct ide_proc_devset *setting; char *buf, *s; if (!capable(CAP_SYS_ADMIN)) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 25190966ed39..f8c84df4a0bc 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2188,40 +2188,40 @@ static int set_##name(ide_drive_t *drive, int arg) \ return 0; \ } -#define ide_tape_devset_rw(_name, _min, _max, _field, _mulf, _divf) \ +#define ide_tape_devset_rw_field(_name, _field) \ ide_tape_devset_get(_name, _field) \ ide_tape_devset_set(_name, _field) \ -__IDE_DEVSET(_name, S_RW, _min, _max, get_##_name, set_##_name, _mulf, _divf) +IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name) -#define ide_tape_devset_r(_name, _min, _max, _field, _mulf, _divf) \ +#define ide_tape_devset_r_field(_name, _field) \ ide_tape_devset_get(_name, _field) \ -__IDE_DEVSET(_name, S_READ, _min, _max, get_##_name, NULL, _mulf, _divf) +IDE_DEVSET(_name, 0, get_##_name, NULL) static int mulf_tdsc(ide_drive_t *drive) { return 1000; } static int divf_tdsc(ide_drive_t *drive) { return HZ; } static int divf_buffer(ide_drive_t *drive) { return 2; } static int divf_buffer_size(ide_drive_t *drive) { return 1024; } -ide_devset_rw(dsc_overlap, 0, 1, dsc_overlap); - -ide_tape_devset_rw(debug_mask, 0, 0xffff, debug_mask, NULL, NULL); -ide_tape_devset_rw(tdsc, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, - best_dsc_rw_freq, mulf_tdsc, divf_tdsc); - -ide_tape_devset_r(avg_speed, 0, 0xffff, avg_speed, NULL, NULL); -ide_tape_devset_r(speed, 0, 0xffff, caps[14], NULL, NULL); -ide_tape_devset_r(buffer, 0, 0xffff, caps[16], NULL, divf_buffer); -ide_tape_devset_r(buffer_size, 0, 0xffff, buffer_size, NULL, divf_buffer_size); - -static const struct ide_devset *idetape_settings[] = { - &ide_devset_avg_speed, - &ide_devset_buffer, - &ide_devset_buffer_size, - &ide_devset_debug_mask, - &ide_devset_dsc_overlap, - &ide_devset_speed, - &ide_devset_tdsc, - NULL +ide_devset_rw_field(dsc_overlap, dsc_overlap); + +ide_tape_devset_rw_field(debug_mask, debug_mask); +ide_tape_devset_rw_field(tdsc, best_dsc_rw_freq); + +ide_tape_devset_r_field(avg_speed, avg_speed); +ide_tape_devset_r_field(speed, caps[14]); +ide_tape_devset_r_field(buffer, caps[16]); +ide_tape_devset_r_field(buffer_size, buffer_size); + +static const struct ide_proc_devset idetape_settings[] = { + __IDE_PROC_DEVSET(avg_speed, 0, 0xffff, NULL, NULL), + __IDE_PROC_DEVSET(buffer, 0, 0xffff, NULL, divf_buffer), + __IDE_PROC_DEVSET(buffer_size, 0, 0xffff, NULL, divf_buffer_size), + __IDE_PROC_DEVSET(debug_mask, 0, 0xffff, NULL, NULL), + __IDE_PROC_DEVSET(dsc_overlap, 0, 1, NULL, NULL), + __IDE_PROC_DEVSET(speed, 0, 0xffff, NULL, NULL), + __IDE_PROC_DEVSET(tdsc, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, + mulf_tdsc, divf_tdsc), + { 0 }, }; #endif diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 349d7fa75585..9dcf5aed92cb 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -250,42 +250,9 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) DEFINE_MUTEX(ide_setting_mtx); -/** - * ide_spin_wait_hwgroup - wait for group - * @drive: drive in the group - * - * Wait for an IDE device group to go non busy and then return - * holding the ide_lock which guards the hwgroup->busy status - * and right to use it. - */ - -int ide_spin_wait_hwgroup (ide_drive_t *drive) -{ - ide_hwgroup_t *hwgroup = HWGROUP(drive); - unsigned long timeout = jiffies + (3 * HZ); - - spin_lock_irq(&ide_lock); - - while (hwgroup->busy) { - unsigned long lflags; - spin_unlock_irq(&ide_lock); - local_irq_set(lflags); - if (time_after(jiffies, timeout)) { - local_irq_restore(lflags); - printk(KERN_ERR "%s: channel busy\n", drive->name); - return -EBUSY; - } - local_irq_restore(lflags); - spin_lock_irq(&ide_lock); - } - return 0; -} - -EXPORT_SYMBOL(ide_spin_wait_hwgroup); - ide_devset_get(io_32bit, io_32bit); -int set_io_32bit(ide_drive_t *drive, int arg) +static int set_io_32bit(ide_drive_t *drive, int arg) { if (drive->no_io_32bit) return -EPERM; @@ -293,37 +260,28 @@ int set_io_32bit(ide_drive_t *drive, int arg) if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1)) return -EINVAL; - if (ide_spin_wait_hwgroup(drive)) - return -EBUSY; - drive->io_32bit = arg; - spin_unlock_irq(&ide_lock); - return 0; } ide_devset_get(ksettings, keep_settings); -int set_ksettings(ide_drive_t *drive, int arg) +static int set_ksettings(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 1) return -EINVAL; - if (ide_spin_wait_hwgroup(drive)) - return -EBUSY; drive->keep_settings = arg; - spin_unlock_irq(&ide_lock); return 0; } ide_devset_get(using_dma, using_dma); -int set_using_dma(ide_drive_t *drive, int arg) +static int set_using_dma(ide_drive_t *drive, int arg) { #ifdef CONFIG_BLK_DEV_IDEDMA - ide_hwif_t *hwif = drive->hwif; int err = -EPERM; if (arg < 0 || arg > 1) @@ -332,18 +290,9 @@ int set_using_dma(ide_drive_t *drive, int arg) if (ata_id_has_dma(drive->id) == 0) goto out; - if (hwif->dma_ops == NULL) + if (drive->hwif->dma_ops == NULL) goto out; - err = -EBUSY; - if (ide_spin_wait_hwgroup(drive)) - goto out; - /* - * set ->busy flag, unlock and let it ride - */ - hwif->hwgroup->busy = 1; - spin_unlock_irq(&ide_lock); - err = 0; if (arg) { @@ -352,12 +301,6 @@ int set_using_dma(ide_drive_t *drive, int arg) } else ide_dma_off(drive); - /* - * lock, clear ->busy flag and unlock before leaving - */ - spin_lock_irq(&ide_lock); - hwif->hwgroup->busy = 0; - spin_unlock_irq(&ide_lock); out: return err; #else @@ -368,7 +311,7 @@ out: #endif } -int set_pio_mode(ide_drive_t *drive, int arg) +static int set_pio_mode(ide_drive_t *drive, int arg) { struct request *rq; ide_hwif_t *hwif = drive->hwif; @@ -398,7 +341,7 @@ int set_pio_mode(ide_drive_t *drive, int arg) ide_devset_get(unmaskirq, unmask); -int set_unmaskirq(ide_drive_t *drive, int arg) +static int set_unmaskirq(ide_drive_t *drive, int arg) { if (drive->no_unmask) return -EPERM; @@ -406,14 +349,20 @@ int set_unmaskirq(ide_drive_t *drive, int arg) if (arg < 0 || arg > 1) return -EINVAL; - if (ide_spin_wait_hwgroup(drive)) - return -EBUSY; drive->unmask = arg; - spin_unlock_irq(&ide_lock); return 0; } +#define ide_gen_devset_rw(_name, _func) \ +__IDE_DEVSET(_name, DS_SYNC, get_##_func, set_##_func) + +ide_gen_devset_rw(io_32bit, io_32bit); +ide_gen_devset_rw(keepsettings, ksettings); +ide_gen_devset_rw(unmaskirq, unmaskirq); +ide_gen_devset_rw(using_dma, using_dma); +__IDE_DEVSET(pio_mode, 0, NULL, set_pio_mode); + static int generic_ide_suspend(struct device *dev, pm_message_t mesg) { ide_drive_t *drive = dev->driver_data; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 27c01e368977..90212ac33be3 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -400,25 +400,25 @@ static int set_##name(ide_drive_t *drive, int arg) \ return 0; \ } -#define ide_scsi_devset_rw(_name, _min, _max, _field) \ +#define ide_scsi_devset_rw_field(_name, _field) \ ide_scsi_devset_get(_name, _field); \ ide_scsi_devset_set(_name, _field); \ -IDE_DEVSET(_name, S_RW, _min, _max, get_##_name, set_##_name) - -ide_devset_rw(bios_cyl, 0, 1023, bios_cyl); -ide_devset_rw(bios_head, 0, 255, bios_head); -ide_devset_rw(bios_sect, 0, 63, bios_sect); - -ide_scsi_devset_rw(transform, 0, 3, transform); -ide_scsi_devset_rw(log, 0, 1, log); - -static const struct ide_devset *idescsi_settings[] = { - &ide_devset_bios_cyl, - &ide_devset_bios_head, - &ide_devset_bios_sect, - &ide_devset_log, - &ide_devset_transform, - NULL +IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name); + +ide_devset_rw_field(bios_cyl, bios_cyl); +ide_devset_rw_field(bios_head, bios_head); +ide_devset_rw_field(bios_sect, bios_sect); + +ide_scsi_devset_rw_field(transform, transform); +ide_scsi_devset_rw_field(log, log); + +static const struct ide_proc_devset idescsi_settings[] = { + IDE_PROC_DEVSET(bios_cyl, 0, 1023), + IDE_PROC_DEVSET(bios_head, 0, 255), + IDE_PROC_DEVSET(bios_sect, 0, 63), + IDE_PROC_DEVSET(log, 0, 1), + IDE_PROC_DEVSET(transform, 0, 3), + { 0 }, }; #endif diff --git a/include/linux/ide.h b/include/linux/ide.h index a9eced25acce..a9d82d6e6bdd 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -161,6 +161,7 @@ enum { * Values should be in the range of 0x20 to 0x3f. */ #define REQ_DRIVE_RESET 0x20 +#define REQ_DEVSET_EXEC 0x21 /* * Check for an interrupt and acknowledge the interrupt status @@ -405,7 +406,7 @@ struct ide_drive_s { u16 *id; /* identification info */ #ifdef CONFIG_IDE_PROC_FS struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ - const struct ide_devset **settings; /* /proc/ide/ drive settings */ + const struct ide_proc_devset *settings; /* /proc/ide/ drive settings */ #endif struct hwif_s *hwif; /* actually (ide_hwif_t *) */ @@ -707,29 +708,62 @@ typedef struct ide_driver_s ide_driver_t; extern struct mutex ide_setting_mtx; -int get_io_32bit(ide_drive_t *); -int set_io_32bit(ide_drive_t *, int); -int get_ksettings(ide_drive_t *); -int set_ksettings(ide_drive_t *, int); -int set_pio_mode(ide_drive_t *, int); -int get_unmaskirq(ide_drive_t *); -int set_unmaskirq(ide_drive_t *, int); -int get_using_dma(ide_drive_t *); -int set_using_dma(ide_drive_t *, int); +/* + * configurable drive settings + */ + +#define DS_SYNC (1 << 0) + +struct ide_devset { + int (*get)(ide_drive_t *); + int (*set)(ide_drive_t *, int); + unsigned int flags; +}; + +#define __DEVSET(_flags, _get, _set) { \ + .flags = _flags, \ + .get = _get, \ + .set = _set, \ +} #define ide_devset_get(name, field) \ -int get_##name(ide_drive_t *drive) \ +static int get_##name(ide_drive_t *drive) \ { \ return drive->field; \ } #define ide_devset_set(name, field) \ -int set_##name(ide_drive_t *drive, int arg) \ +static int set_##name(ide_drive_t *drive, int arg) \ { \ drive->field = arg; \ return 0; \ } +#define __IDE_DEVSET(_name, _flags, _get, _set) \ +const struct ide_devset ide_devset_##_name = \ + __DEVSET(_flags, _get, _set) + +#define IDE_DEVSET(_name, _flags, _get, _set) \ +static __IDE_DEVSET(_name, _flags, _get, _set) + +#define ide_devset_rw(_name, _func) \ +IDE_DEVSET(_name, 0, get_##_func, set_##_func) + +#define ide_devset_w(_name, _func) \ +IDE_DEVSET(_name, 0, NULL, set_##_func) + +#define ide_devset_rw_sync(_name, _func) \ +IDE_DEVSET(_name, DS_SYNC, get_##_func, set_##_func) + +#define ide_decl_devset(_name) \ +extern const struct ide_devset ide_devset_##_name + +ide_decl_devset(io_32bit); +ide_decl_devset(keepsettings); +ide_decl_devset(pio_mode); +ide_decl_devset(unmaskirq); +ide_decl_devset(using_dma); + /* ATAPI packet command flags */ enum { /* set when an error is considered normal - no retry (ide-tape) */ @@ -797,60 +831,34 @@ struct ide_atapi_pc { #ifdef CONFIG_IDE_PROC_FS /* - * configurable drive settings + * /proc/ide interface */ -#define S_READ (1 << 0) -#define S_WRITE (1 << 1) -#define S_RW (S_READ | S_WRITE) -#define S_NOLOCK (1 << 2) - -struct ide_devset { - const char *name; - unsigned int flags; - int min, max; - int (*get)(ide_drive_t *); - int (*set)(ide_drive_t *, int); - int (*mulf)(ide_drive_t *); - int (*divf)(ide_drive_t *); +#define ide_devset_rw_field(_name, _field) \ +ide_devset_get(_name, _field); \ +ide_devset_set(_name, _field); \ +IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name) + +struct ide_proc_devset { + const char *name; + const struct ide_devset *setting; + int min, max; + int (*mulf)(ide_drive_t *); + int (*divf)(ide_drive_t *); }; -#define __DEVSET(_name, _flags, _min, _max, _get, _set, _mulf, _divf) { \ - .name = __stringify(_name), \ - .flags = _flags, \ - .min = _min, \ - .max = _max, \ - .get = _get, \ - .set = _set, \ - .mulf = _mulf, \ - .divf = _divf, \ +#define __IDE_PROC_DEVSET(_name, _min, _max, _mulf, _divf) { \ + .name = __stringify(_name), \ + .setting = &ide_devset_##_name, \ + .min = _min, \ + .max = _max, \ + .mulf = _mulf, \ + .divf = _divf, \ } -#define __IDE_DEVSET(_name, _flags, _min, _max, _get, _set, _mulf, _divf) \ -static const struct ide_devset ide_devset_##_name = \ - __DEVSET(_name, _flags, _min, _max, _get, _set, _mulf, _divf) - -#define IDE_DEVSET(_name, _flags, _min, _max, _get, _set) \ -__IDE_DEVSET(_name, _flags, _min, _max, _get, _set, NULL, NULL) - -#define ide_devset_rw_nolock(_name, _min, _max, _func) \ -IDE_DEVSET(_name, S_RW | S_NOLOCK, _min, _max, get_##_func, set_##_func) +#define IDE_PROC_DEVSET(_name, _min, _max) \ +__IDE_PROC_DEVSET(_name, _min, _max, NULL, NULL) -#define ide_devset_w_nolock(_name, _min, _max, _func) \ -IDE_DEVSET(_name, S_WRITE | S_NOLOCK, _min, _max, NULL, set_##_func) - -#define ide_devset_rw(_name, _min, _max, _field) \ -static ide_devset_get(_name, _field); \ -static ide_devset_set(_name, _field); \ -IDE_DEVSET(_name, S_RW, _min, _max, get_##_name, set_##_name) - -#define ide_devset_r(_name, _min, _max, _field) \ -ide_devset_get(_name, _field) \ -IDE_DEVSET(_name, S_READ, _min, _max, get_##_name, NULL) - -/* - * /proc/ide interface - */ typedef struct { const char *name; mode_t mode; @@ -948,8 +956,8 @@ struct ide_driver_s { void (*resume)(ide_drive_t *); void (*shutdown)(ide_drive_t *); #ifdef CONFIG_IDE_PROC_FS - ide_proc_entry_t *proc; - const struct ide_devset **settings; + ide_proc_entry_t *proc; + const struct ide_proc_devset *settings; #endif }; @@ -961,9 +969,7 @@ void ide_device_put(ide_drive_t *); struct ide_ioctl_devset { unsigned int get_ioctl; unsigned int set_ioctl; - - int (*get)(ide_drive_t *); - int (*set)(ide_drive_t *, int); + const struct ide_devset *setting; }; int ide_setting_ioctl(ide_drive_t *, struct block_device *, unsigned int, @@ -1002,6 +1008,9 @@ int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long); extern ide_startstop_t ide_do_reset (ide_drive_t *); +extern int ide_devset_execute(ide_drive_t *drive, + const struct ide_devset *setting, int arg); + extern void ide_do_drive_cmd(ide_drive_t *, struct request *); extern void ide_end_drive_cmd(ide_drive_t *, u8, u8); @@ -1191,7 +1200,6 @@ extern int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout); extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout); -extern int ide_spin_wait_hwgroup(ide_drive_t *); extern void ide_timer_expiry(unsigned long); extern irqreturn_t ide_intr(int irq, void *dev_id); extern void do_ide_request(struct request_queue *); -- cgit v1.2.3 From 44519faf22ad6ce924ad0352d3dc200d9e0b66e8 Mon Sep 17 00:00:00 2001 From: Hidehiro Kawai Date: Fri, 10 Oct 2008 20:29:13 -0400 Subject: jbd2: fix error handling for checkpoint io When a checkpointing IO fails, current JBD2 code doesn't check the error and continue journaling. This means latest metadata can be lost from both the journal and filesystem. This patch leaves the failed metadata blocks in the journal space and aborts journaling in the case of jbd2_log_do_checkpoint(). To achieve this, we need to do: 1. don't remove the failed buffer from the checkpoint list where in the case of __try_to_free_cp_buf() because it may be released or overwritten by a later transaction 2. jbd2_log_do_checkpoint() is the last chance, remove the failed buffer from the checkpoint list and abort the journal 3. when checkpointing fails, don't update the journal super block to prevent the journaled contents from being cleaned. For safety, don't update j_tail and j_tail_sequence either 4. when checkpointing fails, notify this error to the ext4 layer so that ext4 don't clear the needs_recovery flag, otherwise the journaled contents are ignored and cleaned in the recovery phase 5. if the recovery fails, keep the needs_recovery flag 6. prevent jbd2_cleanup_journal_tail() from being called between __jbd2_journal_drop_transaction() and jbd2_journal_abort() (a possible race issue between jbd2_log_do_checkpoint()s called by jbd2_journal_flush() and __jbd2_log_wait_for_space()) Signed-off-by: Hidehiro Kawai Signed-off-by: Theodore Ts'o --- fs/jbd2/checkpoint.c | 49 +++++++++++++++++++++++++++++++++++++------------ fs/jbd2/journal.c | 28 ++++++++++++++++++++++------ fs/jbd2/recovery.c | 7 +++++-- include/linux/jbd2.h | 2 +- 4 files changed, 65 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 42895d369458..9203c3332f17 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -94,7 +94,8 @@ static int __try_to_free_cp_buf(struct journal_head *jh) int ret = 0; struct buffer_head *bh = jh2bh(jh); - if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) { + if (jh->b_jlist == BJ_None && !buffer_locked(bh) && + !buffer_dirty(bh) && !buffer_write_io_error(bh)) { JBUFFER_TRACE(jh, "remove from checkpoint list"); ret = __jbd2_journal_remove_checkpoint(jh) + 1; jbd_unlock_bh_state(bh); @@ -176,21 +177,25 @@ static void jbd_sync_bh(journal_t *journal, struct buffer_head *bh) * buffers. Note that we take the buffers in the opposite ordering * from the one in which they were submitted for IO. * + * Return 0 on success, and return <0 if some buffers have failed + * to be written out. + * * Called with j_list_lock held. */ -static void __wait_cp_io(journal_t *journal, transaction_t *transaction) +static int __wait_cp_io(journal_t *journal, transaction_t *transaction) { struct journal_head *jh; struct buffer_head *bh; tid_t this_tid; int released = 0; + int ret = 0; this_tid = transaction->t_tid; restart: /* Did somebody clean up the transaction in the meanwhile? */ if (journal->j_checkpoint_transactions != transaction || transaction->t_tid != this_tid) - return; + return ret; while (!released && transaction->t_checkpoint_io_list) { jh = transaction->t_checkpoint_io_list; bh = jh2bh(jh); @@ -210,6 +215,9 @@ restart: spin_lock(&journal->j_list_lock); goto restart; } + if (unlikely(buffer_write_io_error(bh))) + ret = -EIO; + /* * Now in whatever state the buffer currently is, we know that * it has been written out and so we can drop it from the list @@ -219,6 +227,8 @@ restart: jbd2_journal_remove_journal_head(bh); __brelse(bh); } + + return ret; } #define NR_BATCH 64 @@ -242,7 +252,8 @@ __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count) * Try to flush one buffer from the checkpoint list to disk. * * Return 1 if something happened which requires us to abort the current - * scan of the checkpoint list. + * scan of the checkpoint list. Return <0 if the buffer has failed to + * be written out. * * Called with j_list_lock held and drops it if 1 is returned * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it @@ -274,6 +285,9 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, jbd2_log_wait_commit(journal, tid); ret = 1; } else if (!buffer_dirty(bh)) { + ret = 1; + if (unlikely(buffer_write_io_error(bh))) + ret = -EIO; J_ASSERT_JH(jh, !buffer_jbddirty(bh)); BUFFER_TRACE(bh, "remove from checkpoint"); __jbd2_journal_remove_checkpoint(jh); @@ -281,7 +295,6 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, jbd_unlock_bh_state(bh); jbd2_journal_remove_journal_head(bh); __brelse(bh); - ret = 1; } else { /* * Important: we are about to write the buffer, and @@ -314,6 +327,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, * to disk. We submit larger chunks of data at once. * * The journal should be locked before calling this function. + * Called with j_checkpoint_mutex held. */ int jbd2_log_do_checkpoint(journal_t *journal) { @@ -339,6 +353,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) * OK, we need to start writing disk blocks. Take one transaction * and write it. */ + result = 0; spin_lock(&journal->j_list_lock); if (!journal->j_checkpoint_transactions) goto out; @@ -357,7 +372,7 @@ restart: int batch_count = 0; struct buffer_head *bhs[NR_BATCH]; struct journal_head *jh; - int retry = 0; + int retry = 0, err; while (!retry && transaction->t_checkpoint_list) { struct buffer_head *bh; @@ -371,6 +386,8 @@ restart: } retry = __process_buffer(journal, jh, bhs, &batch_count, transaction); + if (retry < 0 && !result) + result = retry; if (!retry && (need_resched() || spin_needbreak(&journal->j_list_lock))) { spin_unlock(&journal->j_list_lock); @@ -395,14 +412,18 @@ restart: * Now we have cleaned up the first transaction's checkpoint * list. Let's clean up the second one */ - __wait_cp_io(journal, transaction); + err = __wait_cp_io(journal, transaction); + if (!result) + result = err; } out: spin_unlock(&journal->j_list_lock); - result = jbd2_cleanup_journal_tail(journal); if (result < 0) - return result; - return 0; + jbd2_journal_abort(journal, result); + else + result = jbd2_cleanup_journal_tail(journal); + + return (result < 0) ? result : 0; } /* @@ -418,8 +439,9 @@ out: * This is the only part of the journaling code which really needs to be * aware of transaction aborts. Checkpointing involves writing to the * main filesystem area rather than to the journal, so it can proceed - * even in abort state, but we must not update the journal superblock if - * we have an abort error outstanding. + * even in abort state, but we must not update the super block if + * checkpointing may have failed. Otherwise, we would lose some metadata + * buffers which should be written-back to the filesystem. */ int jbd2_cleanup_journal_tail(journal_t *journal) @@ -428,6 +450,9 @@ int jbd2_cleanup_journal_tail(journal_t *journal) tid_t first_tid; unsigned long blocknr, freed; + if (is_journal_aborted(journal)) + return 1; + /* OK, work out the oldest transaction remaining in the log, and * the log block it starts at. * diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 01c3901c3a07..783de118de92 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1451,9 +1451,12 @@ recovery_error: * * Release a journal_t structure once it is no longer in use by the * journaled object. + * Return <0 if we couldn't clean up the journal. */ -void jbd2_journal_destroy(journal_t *journal) +int jbd2_journal_destroy(journal_t *journal) { + int err = 0; + /* Wait for the commit thread to wake up and die. */ journal_kill_thread(journal); @@ -1476,11 +1479,16 @@ void jbd2_journal_destroy(journal_t *journal) J_ASSERT(journal->j_checkpoint_transactions == NULL); spin_unlock(&journal->j_list_lock); - /* We can now mark the journal as empty. */ - journal->j_tail = 0; - journal->j_tail_sequence = ++journal->j_transaction_sequence; if (journal->j_sb_buffer) { - jbd2_journal_update_superblock(journal, 1); + if (!is_journal_aborted(journal)) { + /* We can now mark the journal as empty. */ + journal->j_tail = 0; + journal->j_tail_sequence = + ++journal->j_transaction_sequence; + jbd2_journal_update_superblock(journal, 1); + } else { + err = -EIO; + } brelse(journal->j_sb_buffer); } @@ -1492,6 +1500,8 @@ void jbd2_journal_destroy(journal_t *journal) jbd2_journal_destroy_revoke(journal); kfree(journal->j_wbuf); kfree(journal); + + return err; } @@ -1717,10 +1727,16 @@ int jbd2_journal_flush(journal_t *journal) spin_lock(&journal->j_list_lock); while (!err && journal->j_checkpoint_transactions != NULL) { spin_unlock(&journal->j_list_lock); + mutex_lock(&journal->j_checkpoint_mutex); err = jbd2_log_do_checkpoint(journal); + mutex_unlock(&journal->j_checkpoint_mutex); spin_lock(&journal->j_list_lock); } spin_unlock(&journal->j_list_lock); + + if (is_journal_aborted(journal)) + return -EIO; + jbd2_cleanup_journal_tail(journal); /* Finally, mark the journal as really needing no recovery. @@ -1742,7 +1758,7 @@ int jbd2_journal_flush(journal_t *journal) J_ASSERT(journal->j_head == journal->j_tail); J_ASSERT(journal->j_tail_sequence == journal->j_transaction_sequence); spin_unlock(&journal->j_state_lock); - return err; + return 0; } /** diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 058f50f65b76..73063285b13f 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -225,7 +225,7 @@ do { \ */ int jbd2_journal_recover(journal_t *journal) { - int err; + int err, err2; journal_superblock_t * sb; struct recovery_info info; @@ -263,7 +263,10 @@ int jbd2_journal_recover(journal_t *journal) journal->j_transaction_sequence = ++info.end_transaction; jbd2_journal_clear_revoke(journal); - sync_blockdev(journal->j_fs_dev); + err2 = sync_blockdev(journal->j_fs_dev); + if (!err) + err = err2; + return err; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 66c3499478b5..c9e7d781db31 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1060,7 +1060,7 @@ extern void jbd2_journal_clear_features (journal_t *, unsigned long, unsigned long, unsigned long); extern int jbd2_journal_create (journal_t *); extern int jbd2_journal_load (journal_t *journal); -extern void jbd2_journal_destroy (journal_t *); +extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_wipe (journal_t *, int); extern int jbd2_journal_skip_recovery (journal_t *); -- cgit v1.2.3 From 5bf5683a33f3584da6eced480967c4f7e11515a8 Mon Sep 17 00:00:00 2001 From: Hidehiro Kawai Date: Fri, 10 Oct 2008 22:12:43 -0400 Subject: ext4: add an option to control error handling on file data If the journal doesn't abort when it gets an IO error in file data blocks, the file data corruption will spread silently. Because most of applications and commands do buffered writes without fsync(), they don't notice the IO error. It's scary for mission critical systems. On the other hand, if the journal aborts whenever it gets an IO error in file data blocks, the system will easily become inoperable. So this patch introduces a filesystem option to determine whether it aborts the journal or just call printk() when it gets an IO error in file data. If you mount an ext4 fs with data_err=abort option, it aborts on file data write error. If you mount it with data_err=ignore, it doesn't abort, just call printk(). data_err=ignore is the default. Here is the corresponding patch of the ext3 version: http://kerneltrap.org/mailarchive/linux-kernel/2008/9/9/3239374 Signed-off-by: Hidehiro Kawai Signed-off-by: Theodore Ts'o --- Documentation/filesystems/ext4.txt | 5 +++++ fs/ext4/ext4.h | 2 ++ fs/ext4/super.c | 16 ++++++++++++++++ fs/jbd2/commit.c | 2 ++ include/linux/jbd2.h | 3 +++ 5 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index 74484e696405..eb154ef36c2a 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -223,6 +223,11 @@ errors=remount-ro(*) Remount the filesystem read-only on an error. errors=continue Keep going on a filesystem error. errors=panic Panic and halt the machine if an error occurs. +data_err=ignore(*) Just print an error message if an error occurs + in a file data buffer in ordered mode. +data_err=abort Abort the journal if an error occurs in a file + data buffer in ordered mode. + grpid Give objects the same group ID as their creator. bsdgroups diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f46a513a5157..6690a41cdd9f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -540,6 +540,8 @@ do { \ #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */ #define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */ #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ +#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ + /* Compatibility, for having both ext2_fs.h and ext4_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H #define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 79bd3989e84f..014677b8e224 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -778,6 +778,9 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_printf(seq, ",inode_readahead_blks=%u", sbi->s_inode_readahead_blks); + if (test_opt(sb, DATA_ERR_ABORT)) + seq_puts(seq, ",data_err=abort"); + ext4_show_quota_options(seq, sb); return 0; } @@ -907,6 +910,7 @@ enum { Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_journal_checksum, Opt_journal_async_commit, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, + Opt_data_err_abort, Opt_data_err_ignore, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, @@ -953,6 +957,8 @@ static match_table_t tokens = { {Opt_data_journal, "data=journal"}, {Opt_data_ordered, "data=ordered"}, {Opt_data_writeback, "data=writeback"}, + {Opt_data_err_abort, "data_err=abort"}, + {Opt_data_err_ignore, "data_err=ignore"}, {Opt_offusrjquota, "usrjquota="}, {Opt_usrjquota, "usrjquota=%s"}, {Opt_offgrpjquota, "grpjquota="}, @@ -1187,6 +1193,12 @@ static int parse_options(char *options, struct super_block *sb, sbi->s_mount_opt |= data_opt; } break; + case Opt_data_err_abort: + set_opt(sbi->s_mount_opt, DATA_ERR_ABORT); + break; + case Opt_data_err_ignore: + clear_opt(sbi->s_mount_opt, DATA_ERR_ABORT); + break; #ifdef CONFIG_QUOTA case Opt_usrjquota: qtype = USRQUOTA; @@ -2535,6 +2547,10 @@ static void ext4_init_journal_params(struct super_block *sb, journal_t *journal) journal->j_flags |= JBD2_BARRIER; else journal->j_flags &= ~JBD2_BARRIER; + if (test_opt(sb, DATA_ERR_ABORT)) + journal->j_flags |= JBD2_ABORT_ON_SYNCDATA_ERR; + else + journal->j_flags &= ~JBD2_ABORT_ON_SYNCDATA_ERR; spin_unlock(&journal->j_state_lock); } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 849f10496cea..0abe02c4242a 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -684,6 +684,8 @@ start_journal_io: printk(KERN_WARNING "JBD2: Detected IO errors while flushing file data " "on %s\n", journal->j_devname); + if (journal->j_flags & JBD2_ABORT_ON_SYNCDATA_ERR) + jbd2_journal_abort(journal, err); err = 0; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index c9e7d781db31..d2e91ea998fd 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -967,6 +967,9 @@ struct journal_s #define JBD2_FLUSHED 0x008 /* The journal superblock has been flushed */ #define JBD2_LOADED 0x010 /* The journal superblock has been loaded */ #define JBD2_BARRIER 0x020 /* Use IDE barriers */ +#define JBD2_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file + * data write error in ordered + * mode */ /* * Function declarations for the journaling transaction and buffer -- cgit v1.2.3 From ee63a7d2287c677ed022bf3f584f5a187b6c402f Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 2 Sep 2008 10:14:13 +0200 Subject: Fix comment in include/linux/mmc/host.h In include/linux/mmc/host.h, it is mentionned that the callback to know if a card is present or not is get_ro(). But it's get_cd(). Signed-off-by: Thomas Petazzoni Signed-off-by: Pierre Ossman --- include/linux/mmc/host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9c288c909878..bde891f64591 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -65,7 +65,7 @@ struct mmc_host_ops { * -ENOSYS when not supported (equal to NULL callback) * or a negative errno value when something bad happened * - * Return values for the get_ro callback should be: + * Return values for the get_cd callback should be: * 0 for a absent card * 1 for a present card * -ENOSYS when not supported (equal to NULL callback) -- cgit v1.2.3 From 325af5fb1418c79953db0954556de048e061d8b6 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 8 Aug 2008 15:58:39 -0700 Subject: x86: ioperm user_regset This adds a user_regset type for the x86 io permissions bitmap. This makes it appear in core dumps (when ioperm has been used). It will also make it visible to debuggers in the future. Signed-off-by: Roland McGrath Signed-off-by: H. Peter Anvin [conflict resolutions: Signed-off-by: Ingo Molnar ] --- arch/x86/kernel/ptrace.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/elf.h | 1 + 2 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index e375b658efc3..4e1ef66c2ea4 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -40,7 +40,9 @@ enum x86_regset { REGSET_GENERAL, REGSET_FP, REGSET_XFP, + REGSET_IOPERM64 = REGSET_XFP, REGSET_TLS, + REGSET_IOPERM32, }; /* @@ -555,6 +557,29 @@ static int ptrace_set_debugreg(struct task_struct *child, return 0; } +/* + * These access the current or another (stopped) task's io permission + * bitmap for debugging or core dump. + */ +static int ioperm_active(struct task_struct *target, + const struct user_regset *regset) +{ + return target->thread.io_bitmap_max / regset->size; +} + +static int ioperm_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + if (!target->thread.io_bitmap_ptr) + return -ENXIO; + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.io_bitmap_ptr, + 0, IO_BITMAP_BYTES); +} + #ifdef CONFIG_X86_PTRACE_BTS /* * The configuration for a particular BTS hardware implementation. @@ -1385,6 +1410,12 @@ static const struct user_regset x86_64_regsets[] = { .size = sizeof(long), .align = sizeof(long), .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set }, + [REGSET_IOPERM64] = { + .core_note_type = NT_386_IOPERM, + .n = IO_BITMAP_LONGS, + .size = sizeof(long), .align = sizeof(long), + .active = ioperm_active, .get = ioperm_get + }, }; static const struct user_regset_view user_x86_64_view = { @@ -1431,6 +1462,12 @@ static const struct user_regset x86_32_regsets[] = { .active = regset_tls_active, .get = regset_tls_get, .set = regset_tls_set }, + [REGSET_IOPERM32] = { + .core_note_type = NT_386_IOPERM, + .n = IO_BITMAP_BYTES / sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .active = ioperm_active, .get = ioperm_get + }, }; static const struct user_regset_view user_x86_32_view = { diff --git a/include/linux/elf.h b/include/linux/elf.h index edc3dac3f02f..0b61ca41a044 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -360,6 +360,7 @@ typedef struct elf64_shdr { #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ /* Note header in a PT_NOTE section */ -- cgit v1.2.3 From 188919ac57810e39138749338d5a33ba1e970e23 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 8 Aug 2008 07:21:00 -0300 Subject: V4L/DVB (8634): v4l2: extend MPEG Encoding API with AVC and AAC Adds Advanced Audio Coding (AAC) and MPEG-4 Advanced Video Coding (AVC/H.264) as audio/video codecs to the extended controls API. Updates cx2341x driver to the new values. Signed-off-by: Janne Grunau Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx2341x.c | 5 ++++- drivers/media/video/v4l2-common.c | 14 ++++++++------ include/linux/videodev2.h | 6 ++++-- 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 22847a0444f5..cbbe47fb87b7 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -508,7 +508,10 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, /* this setting is read-only for the cx2341x since the V4L2_CID_MPEG_STREAM_TYPE really determines the MPEG-1/2 setting */ - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); if (err == 0) qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; return err; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 88ca13104417..893ac496c4b4 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -187,9 +187,10 @@ const char **v4l2_ctrl_get_menu(u32 id) NULL }; static const char *mpeg_audio_encoding[] = { - "Layer I", - "Layer II", - "Layer III", + "MPEG-1 Layer I", + "MPEG-1 Layer II", + "MPEG-1 Layer III", + "MPEG-4 AAC", NULL }; static const char *mpeg_audio_l1_bitrate[] = { @@ -271,6 +272,7 @@ const char **v4l2_ctrl_get_menu(u32 id) static const char *mpeg_video_encoding[] = { "MPEG-1", "MPEG-2", + "MPEG-4 AVC", NULL }; static const char *mpeg_video_aspect[] = { @@ -358,7 +360,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste /* MPEG controls */ case V4L2_CID_MPEG_CLASS: name = "MPEG Encoder Controls"; break; case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: name = "Audio Sampling Frequency"; break; - case V4L2_CID_MPEG_AUDIO_ENCODING: name = "Audio Encoding Layer"; break; + case V4L2_CID_MPEG_AUDIO_ENCODING: name = "Audio Encoding"; break; case V4L2_CID_MPEG_AUDIO_L1_BITRATE: name = "Audio Layer I Bitrate"; break; case V4L2_CID_MPEG_AUDIO_L2_BITRATE: name = "Audio Layer II Bitrate"; break; case V4L2_CID_MPEG_AUDIO_L3_BITRATE: name = "Audio Layer III Bitrate"; break; @@ -493,7 +495,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_AUDIO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_3, 1, + V4L2_MPEG_AUDIO_ENCODING_AAC, 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return v4l2_ctrl_query_fill(qctrl, @@ -535,7 +537,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_VIDEO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, V4L2_MPEG_VIDEO_ENCODING_MPEG_2); case V4L2_CID_MPEG_VIDEO_ASPECT: return v4l2_ctrl_query_fill(qctrl, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 303d93ffd6b2..350aba2714fc 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -910,6 +910,7 @@ enum v4l2_mpeg_audio_encoding { V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0, V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2, + V4L2_MPEG_AUDIO_ENCODING_AAC = 3, }; #define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102) enum v4l2_mpeg_audio_l1_bitrate { @@ -992,8 +993,9 @@ enum v4l2_mpeg_audio_crc { /* MPEG video */ #define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) enum v4l2_mpeg_video_encoding { - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2, }; #define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201) enum v4l2_mpeg_video_aspect { -- cgit v1.2.3 From e6b5da88fb24c5c1e52707faea7c46df09da42f0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 8 Aug 2008 07:38:07 -0300 Subject: V4L/DVB (8635): v4l: add AC-3 audio support to the MPEG Encoding API Some models of the saa6752hs support AC-3. Extend the API with the necessary controls for AC-3. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 42 ++++++++++++++++++++++++++++++++++----- include/linux/videodev2.h | 23 +++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 893ac496c4b4..0a96cc35738c 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -187,10 +187,11 @@ const char **v4l2_ctrl_get_menu(u32 id) NULL }; static const char *mpeg_audio_encoding[] = { - "MPEG-1 Layer I", - "MPEG-1 Layer II", - "MPEG-1 Layer III", - "MPEG-4 AAC", + "MPEG-1/2 Layer I", + "MPEG-1/2 Layer II", + "MPEG-1/2 Layer III", + "MPEG-2/4 AAC", + "AC-3", NULL }; static const char *mpeg_audio_l1_bitrate[] = { @@ -244,6 +245,28 @@ const char **v4l2_ctrl_get_menu(u32 id) "320 kbps", NULL }; + static const char *mpeg_audio_ac3_bitrate[] = { + "32 kbps", + "40 kbps", + "48 kbps", + "56 kbps", + "64 kbps", + "80 kbps", + "96 kbps", + "112 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "320 kbps", + "384 kbps", + "448 kbps", + "512 kbps", + "576 kbps", + "640 kbps", + NULL + }; static const char *mpeg_audio_mode[] = { "Stereo", "Joint Stereo", @@ -313,6 +336,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return mpeg_audio_l2_bitrate; case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return mpeg_audio_l3_bitrate; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return mpeg_audio_ac3_bitrate; case V4L2_CID_MPEG_AUDIO_MODE: return mpeg_audio_mode; case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: @@ -364,6 +389,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_AUDIO_L1_BITRATE: name = "Audio Layer I Bitrate"; break; case V4L2_CID_MPEG_AUDIO_L2_BITRATE: name = "Audio Layer II Bitrate"; break; case V4L2_CID_MPEG_AUDIO_L3_BITRATE: name = "Audio Layer III Bitrate"; break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: name = "Audio AC-3 Bitrate"; break; case V4L2_CID_MPEG_AUDIO_MODE: name = "Audio Stereo Mode"; break; case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break; case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break; @@ -409,6 +435,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_AUDIO_L1_BITRATE: case V4L2_CID_MPEG_AUDIO_L2_BITRATE: case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: case V4L2_CID_MPEG_AUDIO_MODE: case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: case V4L2_CID_MPEG_AUDIO_EMPHASIS: @@ -495,7 +522,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_AUDIO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_AAC, 1, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return v4l2_ctrl_query_fill(qctrl, @@ -512,6 +539,11 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) V4L2_MPEG_AUDIO_L3_BITRATE_32K, V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1, V4L2_MPEG_AUDIO_L3_BITRATE_192K); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_32K, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K); case V4L2_CID_MPEG_AUDIO_MODE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_MODE_STEREO, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 350aba2714fc..9054764f4cde 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -911,6 +911,7 @@ enum v4l2_mpeg_audio_encoding { V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2, V4L2_MPEG_AUDIO_ENCODING_AAC = 3, + V4L2_MPEG_AUDIO_ENCODING_AC3 = 4, }; #define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102) enum v4l2_mpeg_audio_l1_bitrate { @@ -989,6 +990,28 @@ enum v4l2_mpeg_audio_crc { V4L2_MPEG_AUDIO_CRC_CRC16 = 1, }; #define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109) +#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+110) +enum v4l2_mpeg_audio_ac3_bitrate { + V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0, + V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K = 2, + V4L2_MPEG_AUDIO_AC3_BITRATE_56K = 3, + V4L2_MPEG_AUDIO_AC3_BITRATE_64K = 4, + V4L2_MPEG_AUDIO_AC3_BITRATE_80K = 5, + V4L2_MPEG_AUDIO_AC3_BITRATE_96K = 6, + V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7, + V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8, + V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9, + V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12, + V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15, + V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16, + V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18, +}; /* MPEG video */ #define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) -- cgit v1.2.3 From f723af16da8359cda47320001960609b0086082b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 9 Aug 2008 10:35:29 -0300 Subject: V4L/DVB (8649): v4l2: add AAC bitrate control If you can select AAC as audio encoder, then you should also be able to set the bitrate. Add this missing control. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 3 +++ include/linux/videodev2.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 0c511839f7ee..20c3be8617ea 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -386,6 +386,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate"; case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate"; case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate"; + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode"; case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension"; @@ -548,6 +549,8 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) V4L2_MPEG_AUDIO_L3_BITRATE_32K, V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1, V4L2_MPEG_AUDIO_L3_BITRATE_192K); + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000); case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_AC3_BITRATE_32K, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 9054764f4cde..d4b03034ee73 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -990,7 +990,8 @@ enum v4l2_mpeg_audio_crc { V4L2_MPEG_AUDIO_CRC_CRC16 = 1, }; #define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109) -#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+110) +#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_MPEG_BASE+110) +#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+111) enum v4l2_mpeg_audio_ac3_bitrate { V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0, V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1, -- cgit v1.2.3 From 66c6bda79fdc273608e2700a0c6dd4cb82d0bac3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 16 Aug 2008 08:33:14 -0300 Subject: V4L/DVB (8691): i2c-id: remove obsolete SAB3036 driver ID Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/i2c-id.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index bf34c5f4c051..493435bcdbe5 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -41,7 +41,6 @@ #define I2C_DRIVERID_SAA7110 22 /* video decoder */ #define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */ #define I2C_DRIVERID_PCF8583 25 /* real time clock */ -#define I2C_DRIVERID_SAB3036 26 /* SAB3036 tuner */ #define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */ #define I2C_DRIVERID_TVMIXER 28 /* Mixer driver for tv cards */ #define I2C_DRIVERID_TVAUDIO 29 /* Generic TV sound driver */ -- cgit v1.2.3 From 4c522e74902a1fa15a7ec5e5a3325b250afa54ab Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Wed, 10 Sep 2008 08:22:06 -0300 Subject: V4L/DVB (8959): include into linux/ivtv.h linux/videodev2.h defines enum v4l2_buf_type and struct v4l2_rect Signed-off-by: Kirill A. Shutemov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/ivtv.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/ivtv.h b/include/linux/ivtv.h index 17ca64b5a66c..f2720280b9ec 100644 --- a/include/linux/ivtv.h +++ b/include/linux/ivtv.h @@ -23,6 +23,7 @@ #include #include +#include /* ivtv knows several distinct output modes: MPEG streaming, YUV streaming, YUV updates through user DMA and the passthrough -- cgit v1.2.3 From 6b73eeafbc856c0cef7166242f0e55403407f355 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 4 Sep 2008 01:12:25 -0300 Subject: V4L/DVB (8985): S2API: Added dvb frontend changes to support a newer tuning API This is an experimental patch to add a new tuning mechanism for dvb frontends. Rather than passing fixed structures across the user/kernel boundary, which need to be revised for each new modulation type (or feature the kernel developers want to add), this implements a simpler message based approach, allowing fe commands to be broken down into a series of small fixed size transactions, presented in an array. The goal is to avoid changing the user/kernel ABI in the future, by simply creating new frontend commands (and sequencies of commands) that help us add support for brand new demodulator, delivery system or statistics related commmands. known issues: checkpatch voilations feedback from various developers yet to be implemented, relating to namespace conventions, variable length array passing conventions, and generally some optimization. This patch should support all existing tuning mechanisms through the new API, as well as adding 8PSK, DVB-S2 NBC-QPSK and ISDB-T API support. For testing and exercise purposes, see the latest tune.c tool available from http://www.steventoth.net/linux/s2 Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 571 +++++++++++++++++++++++++++++- drivers/media/dvb/dvb-core/dvb_frontend.h | 44 +++ include/linux/dvb/frontend.h | 130 ++++++- 3 files changed, 737 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 3526e3ee9487..e68974b2fee9 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -755,6 +755,535 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } +struct tv_cmds_h tv_cmds[] = { + [TV_SEQ_UNDEFINED] = { + .name = "TV_SEQ_UNDEFINED", + .cmd = TV_SEQ_UNDEFINED, + .set = 1, + }, + [TV_SEQ_START] = { + .name = "TV_SEQ_START", + .cmd = TV_SEQ_START, + .set = 1, + }, + [TV_SEQ_CONTINUE] = { + .name = "TV_SEQ_CONTINUE", + .cmd = TV_SEQ_CONTINUE, + .set = 1, + }, + [TV_SEQ_COMPLETE] = { + .name = "TV_SEQ_COMPLETE", + .cmd = TV_SEQ_COMPLETE, + .set = 1, + }, + [TV_SEQ_TERMINATE] = { + .name = "TV_SEQ_TERMINATE", + .cmd = TV_SEQ_TERMINATE, + .set = 1, + }, + + /* Set */ + [TV_SET_FREQUENCY] = { + .name = "TV_SET_FREQUENCY", + .cmd = TV_SET_FREQUENCY, + .set = 1, + }, + [TV_SET_BANDWIDTH] = { + .name = "TV_SET_BANDWIDTH", + .cmd = TV_SET_BANDWIDTH, + .set = 1, + }, + [TV_SET_MODULATION] = { + .name = "TV_SET_MODULATION", + .cmd = TV_SET_MODULATION, + .set = 1, + }, + [TV_SET_INVERSION] = { + .name = "TV_SET_INVERSION", + .cmd = TV_SET_INVERSION, + .set = 1, + }, + [TV_SET_DISEQC_MASTER] = { + .name = "TV_SET_DISEQC_MASTER", + .cmd = TV_SET_DISEQC_MASTER, + .set = 1, + .buffer = 1, + }, + [TV_SET_SYMBOLRATE] = { + .name = "TV_SET_SYMBOLRATE", + .cmd = TV_SET_SYMBOLRATE, + .set = 1, + }, + [TV_SET_INNERFEC] = { + .name = "TV_SET_INNERFEC", + .cmd = TV_SET_INNERFEC, + .set = 1, + }, + [TV_SET_VOLTAGE] = { + .name = "TV_SET_VOLTAGE", + .cmd = TV_SET_VOLTAGE, + .set = 1, + }, + [TV_SET_TONE] = { + .name = "TV_SET_TONE", + .cmd = TV_SET_TONE, + .set = 1, + }, + [TV_SET_PILOT] = { + .name = "TV_SET_PILOT", + .cmd = TV_SET_PILOT, + .set = 1, + }, + [TV_SET_ROLLOFF] = { + .name = "TV_SET_ROLLOFF", + .cmd = TV_SET_ROLLOFF, + .set = 1, + }, + [TV_SET_DELIVERY_SYSTEM] = { + .name = "TV_SET_DELIVERY_SYSTEM", + .cmd = TV_SET_DELIVERY_SYSTEM, + .set = 1, + }, + [TV_SET_ISDB_SEGMENT_NUM] = { + .name = "TV_SET_ISDB_SEGMENT_NUM", + .cmd = TV_SET_ISDB_SEGMENT_NUM, + .set = 1, + }, + [TV_SET_ISDB_SEGMENT_WIDTH] = { + .name = "TV_SET_ISDB_SEGMENT_WIDTH", + .cmd = TV_SET_ISDB_SEGMENT_WIDTH, + .set = 1, + }, + + /* Get */ + [TV_GET_FREQUENCY] = { + .name = "TV_GET_FREQUENCY", + .cmd = TV_GET_FREQUENCY, + .set = 0, + }, + [TV_GET_BANDWIDTH] = { + .name = "TV_GET_BANDWIDTH", + .cmd = TV_GET_BANDWIDTH, + .set = 0, + }, + [TV_GET_MODULATION] = { + .name = "TV_GET_MODULATION", + .cmd = TV_GET_MODULATION, + .set = 0, + }, + [TV_GET_INVERSION] = { + .name = "TV_GET_INVERSION", + .cmd = TV_GET_INVERSION, + .set = 0, + }, + [TV_GET_DISEQC_SLAVE_REPLY] = { + .name = "TV_GET_DISEQC_SLAVE_REPLY", + .cmd = TV_GET_DISEQC_SLAVE_REPLY, + .set = 0, + .buffer = 1, + }, + [TV_GET_SYMBOLRATE] = { + .name = "TV_GET_SYMBOLRATE", + .cmd = TV_GET_SYMBOLRATE, + .set = 0, + }, + [TV_GET_INNERFEC] = { + .name = "TV_GET_INNERFEC", + .cmd = TV_GET_INNERFEC, + .set = 0, + }, + [TV_GET_VOLTAGE] = { + .name = "TV_GET_VOLTAGE", + .cmd = TV_GET_VOLTAGE, + .set = 0, + }, + [TV_GET_TONE] = { + .name = "TV_GET_TONE", + .cmd = TV_GET_TONE, + .set = 0, + }, + [TV_GET_PILOT] = { + .name = "TV_GET_PILOT", + .cmd = TV_GET_PILOT, + .set = 0, + }, + [TV_GET_ROLLOFF] = { + .name = "TV_GET_ROLLOFF", + .cmd = TV_GET_ROLLOFF, + .set = 0, + }, + [TV_GET_DELIVERY_SYSTEM] = { + .name = "TV_GET_DELIVERY_SYSTEM", + .cmd = TV_GET_DELIVERY_SYSTEM, + .set = 0, + }, + [TV_GET_ISDB_SEGMENT_NUM] = { + .name = "TV_GET_ISDB_SEGMENT_NUM", + .cmd = TV_GET_ISDB_SEGMENT_NUM, + .set = 0, + }, + [TV_GET_ISDB_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_SEGMENT_WIDTH, + .set = 0, + }, + [TV_GET_ISDB_LAYERA_FEC] = { + .name = "TV_GET_ISDB_LAYERA_FEC", + .cmd = TV_GET_ISDB_LAYERA_FEC, + .set = 0, + }, + [TV_GET_ISDB_LAYERA_MODULATION] = { + .name = "TV_GET_ISDB_LAYERA_MODULATION", + .cmd = TV_GET_ISDB_LAYERA_MODULATION, + .set = 0, + }, + [TV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_LAYERA_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + .set = 0, + }, + [TV_GET_ISDB_LAYERB_FEC] = { + .name = "TV_GET_ISDB_LAYERB_FEC", + .cmd = TV_GET_ISDB_LAYERB_FEC, + .set = 0, + }, + [TV_GET_ISDB_LAYERB_MODULATION] = { + .name = "TV_GET_ISDB_LAYERB_MODULATION", + .cmd = TV_GET_ISDB_LAYERB_MODULATION, + .set = 0, + }, + [TV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_LAYERB_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + .set = 0, + }, + [TV_GET_ISDB_LAYERC_FEC] = { + .name = "TV_GET_ISDB_LAYERC_FEC", + .cmd = TV_GET_ISDB_LAYERC_FEC, + .set = 0, + }, + [TV_GET_ISDB_LAYERC_MODULATION] = { + .name = "TV_GET_ISDB_LAYERC_MODULATION", + .cmd = TV_GET_ISDB_LAYERC_MODULATION, + .set = 0, + }, + [TV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_LAYERC_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + .set = 0, + }, +}; + +void tv_property_dump(tv_property_t *tvp) +{ + int i; + + printk("%s() tvp.cmd = 0x%08x (%s)\n" + ,__FUNCTION__ + ,tvp->cmd + ,tv_cmds[ tvp->cmd ].name); + + if(tv_cmds[ tvp->cmd ].buffer) { + + printk("%s() tvp.u.buffer.len = 0x%02x\n" + ,__FUNCTION__ + ,tvp->u.buffer.len); + + for(i = 0; i < tvp->u.buffer.len; i++) + printk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n" + ,__FUNCTION__ + ,i + ,tvp->u.buffer.data[i]); + + } else + printk("%s() tvp.u.data = 0x%08x\n", __FUNCTION__, tvp->u.data); +} + +int is_legacy_delivery_system(fe_delivery_system_t s) +{ + if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) || + (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS)) + return 1; + + return 0; +} + +int tv_property_cache_submit(struct dvb_frontend *fe) +{ + + /* We have to do one of two things: + * To support legacy devices using the new API we take values from + * the tv_cache and generate a legacy truning structure. + * + * Or, + * + * To support advanced tuning devices with the new API we + * notify the new advance driver type that a tuning operation is required + * and let it pull values from the cache as is, we don't need to + * pass structures. + * + * We'll use the modulation type to assess how this is handled. as the API + * progresses we'll probably want to have a flag in dvb_frontend_ops + * to allow the frontend driver to dictate how it likes to be tuned. + * + * Because of how this is attached to the ioctl handler for legacy support, + * it's important to return an appropriate result code with atleast the following + * three meanings: + * < 0 = processing error + * 0 = lecagy ioctl handler to submit a traditional set_frontend() call. + * 1 = lecagy ioctl handler should NOT submit a traditional set_frontend() call. + */ + + int r; + + struct tv_frontend_properties *c = &fe->tv_property_cache; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_frontend_parameters p; + + printk("%s()\n", __FUNCTION__); + + /* For legacy delivery systems we don't need the delivery_system to be specified */ + if(is_legacy_delivery_system(c->delivery_system)) { + switch(c->modulation) { + case QPSK: + printk("%s() Preparing QPSK req\n", __FUNCTION__); + p.frequency = c->frequency; + p.inversion = c->inversion; + p.u.qpsk.symbol_rate = c->symbol_rate; + p.u.qpsk.fec_inner = c->fec_inner; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + /* Call the traditional tuning mechanisms. */ + + r = 0; + break; + case QAM_16: + case QAM_32: + case QAM_64: + case QAM_128: + case QAM_256: + case QAM_AUTO: + printk("%s() Preparing QAM req\n", __FUNCTION__); + p.frequency = c->frequency; + p.inversion = c->inversion; + p.u.qam.symbol_rate = c->symbol_rate; + p.u.vsb.modulation = c->modulation; + printk("%s() frequency = %d\n", __FUNCTION__, p.frequency); + printk("%s() QAM = %d\n", __FUNCTION__, p.u.vsb.modulation); + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + /* At this point we're fully formed for backwards + * compatability and we need to return this + * via the ioctl handler as SET_FRONTEND (arg). + * We've already patched the new values into the + * frontends tuning structures so the ioctl code just + * continues as if a legacy tune structure was passed + * from userspace. + */ + + r = 0; + break; + case VSB_8: + case VSB_16: + printk("%s() Preparing VSB req\n", __FUNCTION__); + p.frequency = c->frequency; + p.u.vsb.modulation = c->modulation; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + /* Call the traditional tuning mechanisms. */ + + r = 0; + break; + /* TODO: Add any missing modulation types */ + default: + r = -1; + } + } else { + /* For advanced delivery systems / modulation types ... + * we seed the lecacy dvb_frontend_parameters structure + * so that the sanity checking code later in the IOCTL processing + * can validate our basic frequency ranges, symbolrates, modulation + * etc. + */ + r = -1; + + switch(c->modulation) { + case _8PSK: + case _16APSK: + case NBC_QPSK: + /* Just post a notification to the demod driver and let it pull + * the specific values it wants from its tv_property_cache. + * It can decide how best to use those parameters. + * IOCTL will call set_frontend (by default) due to zigzag + * support etc. + */ + if (fe->ops.set_params) + r = fe->ops.set_params(fe); + + p.frequency = c->frequency; + p.inversion = c->inversion; + p.u.qpsk.symbol_rate = c->symbol_rate; + p.u.qpsk.fec_inner = c->fec_inner; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + r = 0; + break; + default: + r = -1; + } + + if(c->delivery_system == SYS_ISDBT) { + /* Fake out a generic DVB-T request so we pass validation in the ioctl */ + p.frequency = c->frequency; + p.inversion = INVERSION_AUTO; + p.u.ofdm.constellation = QAM_AUTO; + p.u.ofdm.code_rate_HP = FEC_AUTO; + p.u.ofdm.code_rate_LP = FEC_AUTO; + p.u.ofdm.bandwidth = BANDWIDTH_AUTO; + p.u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + p.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + p.u.ofdm.hierarchy_information = HIERARCHY_AUTO; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + r = 0; + } + } + return r; +} + +int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp) +{ + int r = 0; + printk("%s()\n", __FUNCTION__); + tv_property_dump(tvp); + + switch(tvp->cmd) { + case TV_SEQ_START: + case TV_SEQ_TERMINATE: + /* Reset a cache of data specific to the frontend here. This does + * not effect hardware. + */ + printk("%s() Flushing property cache\n", __FUNCTION__); + memset(&fe->tv_property_cache, 0, sizeof(struct tv_frontend_properties)); + fe->tv_property_cache.state = TV_SEQ_START; + fe->tv_property_cache.delivery_system = SYS_UNDEFINED; + break; + case TV_SEQ_COMPLETE: + /* interpret the cache of data, build either a traditional frontend + * tunerequest and submit it to a subset of the ioctl handler, + * or, call a new undefined method on the frontend to deal with + * all new tune requests. + */ + fe->tv_property_cache.state = TV_SEQ_COMPLETE; + printk("%s() Finalised property cache\n", __FUNCTION__); + r = tv_property_cache_submit(fe); + break; + case TV_SET_FREQUENCY: + fe->tv_property_cache.frequency = tvp->u.data; + break; + case TV_GET_FREQUENCY: + tvp->u.data = fe->tv_property_cache.frequency; + break; + case TV_SET_MODULATION: + fe->tv_property_cache.modulation = tvp->u.data; + break; + case TV_GET_MODULATION: + tvp->u.data = fe->tv_property_cache.modulation; + break; + case TV_SET_BANDWIDTH: + fe->tv_property_cache.bandwidth = tvp->u.data; + break; + case TV_GET_BANDWIDTH: + tvp->u.data = fe->tv_property_cache.bandwidth; + break; + case TV_SET_INVERSION: + fe->tv_property_cache.inversion = tvp->u.data; + break; + case TV_GET_INVERSION: + tvp->u.data = fe->tv_property_cache.inversion; + break; + case TV_SET_SYMBOLRATE: + fe->tv_property_cache.symbol_rate = tvp->u.data; + break; + case TV_GET_SYMBOLRATE: + tvp->u.data = fe->tv_property_cache.symbol_rate; + break; + case TV_SET_INNERFEC: + fe->tv_property_cache.fec_inner = tvp->u.data; + break; + case TV_GET_INNERFEC: + tvp->u.data = fe->tv_property_cache.fec_inner; + break; + case TV_SET_PILOT: + fe->tv_property_cache.pilot = tvp->u.data; + break; + case TV_GET_PILOT: + tvp->u.data = fe->tv_property_cache.pilot; + break; + case TV_SET_ROLLOFF: + fe->tv_property_cache.rolloff = tvp->u.data; + break; + case TV_GET_ROLLOFF: + tvp->u.data = fe->tv_property_cache.rolloff; + break; + case TV_SET_DELIVERY_SYSTEM: + fe->tv_property_cache.delivery_system = tvp->u.data; + break; + case TV_GET_DELIVERY_SYSTEM: + tvp->u.data = fe->tv_property_cache.delivery_system; + break; + + /* ISDB-T Support here */ + case TV_SET_ISDB_SEGMENT_NUM: + fe->tv_property_cache.isdb_segment_num = tvp->u.data; + break; + case TV_GET_ISDB_SEGMENT_NUM: + tvp->u.data = fe->tv_property_cache.isdb_segment_num; + break; + case TV_SET_ISDB_SEGMENT_WIDTH: + fe->tv_property_cache.isdb_segment_width = tvp->u.data; + break; + case TV_GET_ISDB_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_segment_width; + break; + case TV_GET_ISDB_LAYERA_FEC: + tvp->u.data = fe->tv_property_cache.isdb_layera_fec; + break; + case TV_GET_ISDB_LAYERA_MODULATION: + tvp->u.data = fe->tv_property_cache.isdb_layera_modulation; + break; + case TV_GET_ISDB_LAYERA_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_layera_segment_width; + break; + case TV_GET_ISDB_LAYERB_FEC: + tvp->u.data = fe->tv_property_cache.isdb_layerb_fec; + break; + case TV_GET_ISDB_LAYERB_MODULATION: + tvp->u.data = fe->tv_property_cache.isdb_layerb_modulation; + break; + case TV_GET_ISDB_LAYERB_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_layerb_segment_width; + break; + case TV_GET_ISDB_LAYERC_FEC: + tvp->u.data = fe->tv_property_cache.isdb_layerc_fec; + break; + case TV_GET_ISDB_LAYERC_MODULATION: + tvp->u.data = fe->tv_property_cache.isdb_layerc_modulation; + break; + case TV_GET_ISDB_LAYERC_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_layerc_segment_width; + break; + + } + + return 0; +} + static int dvb_frontend_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { @@ -762,6 +1291,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; int err = -EOPNOTSUPP; + tv_property_t* tvp; dprintk ("%s\n", __func__); @@ -776,6 +1306,27 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, if (down_interruptible (&fepriv->sem)) return -ERESTARTSYS; + if(cmd == FE_SET_PROPERTY) { + printk("%s() FE_SET_PROPERTY\n", __FUNCTION__); + + /* TODO: basic property validation here */ + + /* TODO: ioctl userdata out of range check here */ + tvp = parg; + while(tvp->cmd != TV_SEQ_UNDEFINED) { + tv_property_process(fe, tvp); + if( (tvp->cmd == TV_SEQ_TERMINATE) || (tvp->cmd == TV_SEQ_COMPLETE) ) + break; + tvp++; + } + + if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + printk("%s() Property cache is full, tuning\n", __FUNCTION__); + cmd = FE_SET_FRONTEND; + } + err = 0; + } + switch (cmd) { case FE_GET_INFO: { struct dvb_frontend_info* info = parg; @@ -942,13 +1493,20 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if (dvb_frontend_check_parameters(fe, parg) < 0) { - err = -EINVAL; - break; - } + if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { + err = -EINVAL; + break; + } + } else { + if (dvb_frontend_check_parameters(fe, parg) < 0) { + err = -EINVAL; + break; + } - memcpy (&fepriv->parameters, parg, - sizeof (struct dvb_frontend_parameters)); + memcpy (&fepriv->parameters, parg, + sizeof (struct dvb_frontend_parameters)); + } memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); memcpy(&fetunesettings.parameters, parg, @@ -1031,6 +1589,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, return err; } + static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait) { struct dvb_device *dvbdev = file->private_data; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index aa4133f0bd19..61d53ee70f2a 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -169,6 +169,10 @@ struct dvb_frontend_ops { struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; + + int (*set_property)(struct dvb_frontend* fe, tv_property_t* tvp); + int (*get_property)(struct dvb_frontend* fe, tv_property_t* tvp); + int (*set_params)(struct dvb_frontend* fe); }; #define MAX_EVENT 8 @@ -182,6 +186,45 @@ struct dvb_fe_events { struct mutex mtx; }; +struct tv_frontend_properties { + + /* Cache State */ + u32 state; + + u32 frequency; + fe_modulation_t modulation; + + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t sectone; + fe_spectral_inversion_t inversion; + fe_code_rate_t fec_inner; + fe_transmit_mode_t transmission_mode; + fe_bandwidth_t bandwidth; + fe_guard_interval_t guard_interval; + fe_hierarchy_t hierarchy; + u32 symbol_rate; + fe_code_rate_t code_rate_HP; + fe_code_rate_t code_rate_LP; + + fe_pilot_t pilot; + fe_rolloff_t rolloff; + + fe_delivery_system_t delivery_system; + + /* ISDB-T specifics */ + u32 isdb_segment_num; + u32 isdb_segment_width; + fe_code_rate_t isdb_layera_fec; + fe_modulation_t isdb_layera_modulation; + u32 isdb_layera_segment_width; + fe_code_rate_t isdb_layerb_fec; + fe_modulation_t isdb_layerb_modulation; + u32 isdb_layerb_segment_width; + fe_code_rate_t isdb_layerc_fec; + fe_modulation_t isdb_layerc_modulation; + u32 isdb_layerc_segment_width; +}; + struct dvb_frontend { struct dvb_frontend_ops ops; struct dvb_adapter *dvb; @@ -190,6 +233,7 @@ struct dvb_frontend { void *frontend_priv; void *sec_priv; void *analog_demod_priv; + struct tv_frontend_properties tv_property_cache; }; extern int dvb_register_frontend(struct dvb_adapter *dvb, diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index c8cbd90ba375..4d3770021736 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -62,6 +62,7 @@ typedef enum fe_caps { FE_CAN_HIERARCHY_AUTO = 0x100000, FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, + FE_HAS_EXTENDED_CAPS = 0x800000, // We need more bitspace for newer APIs, indicate this. FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending) FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output @@ -147,7 +148,9 @@ typedef enum fe_code_rate { FEC_6_7, FEC_7_8, FEC_8_9, - FEC_AUTO + FEC_AUTO, + FEC_3_5, + FEC_9_10, } fe_code_rate_t; @@ -160,7 +163,11 @@ typedef enum fe_modulation { QAM_256, QAM_AUTO, VSB_8, - VSB_16 + VSB_16, + _8PSK, + _16APSK, + NBC_QPSK, + DQPSK, } fe_modulation_t; typedef enum fe_transmit_mode { @@ -239,6 +246,125 @@ struct dvb_frontend_event { struct dvb_frontend_parameters parameters; }; +/* TODO: Turn this into a series of defines, so future maintainers + * don't insert random new commands and break backwards + * binary compatability. + */ +typedef enum tv_cmd_types { + TV_SEQ_UNDEFINED, + TV_SEQ_START, + TV_SEQ_CONTINUE, + TV_SEQ_COMPLETE, + TV_SEQ_TERMINATE, + + TV_SET_FREQUENCY, + TV_SET_MODULATION, + TV_SET_BANDWIDTH, + TV_SET_INVERSION, + TV_SET_DISEQC_MASTER, + TV_SET_SYMBOLRATE, + TV_SET_INNERFEC, + TV_SET_VOLTAGE, + TV_SET_TONE, + TV_SET_PILOT, + TV_SET_ROLLOFF, + + TV_GET_FREQUENCY, + TV_GET_MODULATION, + TV_GET_BANDWIDTH, + TV_GET_INVERSION, + TV_GET_DISEQC_SLAVE_REPLY, + TV_GET_SYMBOLRATE, + TV_GET_INNERFEC, + TV_GET_VOLTAGE, + TV_GET_TONE, + TV_GET_PILOT, + TV_GET_ROLLOFF, + + /* Basic enumeration set for querying unlimited capabilities */ + TV_GET_FE_CAPABILITY_COUNT, + TV_GET_FE_CAPABILITY, + + /* New commands are always appended */ + TV_SET_DELIVERY_SYSTEM, + TV_GET_DELIVERY_SYSTEM, + + /* ISDB-T */ + TV_SET_ISDB_SEGMENT_NUM, + TV_GET_ISDB_SEGMENT_NUM, + TV_SET_ISDB_SEGMENT_WIDTH, + TV_GET_ISDB_SEGMENT_WIDTH, + TV_GET_ISDB_LAYERA_FEC, + TV_GET_ISDB_LAYERA_MODULATION, + TV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + TV_GET_ISDB_LAYERB_FEC, + TV_GET_ISDB_LAYERB_MODULATION, + TV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + TV_GET_ISDB_LAYERC_FEC, + TV_GET_ISDB_LAYERC_MODULATION, + TV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + +} tv_cmd_types_t; + +typedef enum fe_pilot { + PILOT_ON, + PILOT_OFF, + PILOT_AUTO, +} fe_pilot_t; + +typedef enum fe_rolloff { + ROLLOFF_20, + ROLLOFF_25, + ROLLOFF_35, + ROLLOFF_AUTO, +} fe_rolloff_t; + +typedef enum fe_delivery_system { + SYS_UNDEFINED, + SYS_DVBC_ANNEX_AC, + SYS_DVBC_ANNEX_B, + SYS_DVBT, + SYS_DVBS, + SYS_DVBS2, + SYS_DVBH, + SYS_ISDBT, + SYS_ISDBS, + SYS_ISDBC, + SYS_ATSC, + SYS_ATSCMH, + SYS_DMBTH, + SYS_CMMB, + SYS_DAB, +} fe_delivery_system_t; + +struct tv_cmds_h { + char *name; /* A display name for debugging purposes */ + + __u32 cmd; /* A unique ID */ + + /* Flags */ + __u32 set:1; /* Either a set or get property */ + __u32 buffer:1; /* Does this property use the buffer? */ + __u32 reserved:30; /* Align */ +}; + +typedef struct { + __u32 cmd; + union { + __u32 data; + struct { + __u8 data[32]; + __u32 len; + } buffer; + } u; +} tv_property_t; + +/* No more than 16 properties during any given ioctl */ +typedef tv_property_t tv_properties_t[16]; + +#define FE_SET_PROPERTY _IOW('o', 82, tv_properties_t) +#define FE_GET_PROPERTY _IOR('o', 83, tv_properties_t) + /** * When set, this flag will disable any zigzagging or other "normal" tuning -- cgit v1.2.3 From 56f0680a28397f4b412fc14f60ac380b910ee328 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 10:19:27 -0300 Subject: V4L/DVB (8995): S2API: tv_ / TV_ to dtv_ / DTV_ namespace changes The group preferred dtv_ over tv_, this implements it. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 442 +++++++++++++++--------------- drivers/media/dvb/dvb-core/dvb_frontend.h | 8 +- drivers/media/dvb/frontends/cx24116.c | 6 +- include/linux/dvb/frontend.h | 108 ++++---- 4 files changed, 282 insertions(+), 282 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 620c62084029..9c4761506d2d 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -755,235 +755,235 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } -struct tv_cmds_h tv_cmds[] = { - [TV_SEQ_UNDEFINED] = { - .name = "TV_SEQ_UNDEFINED", - .cmd = TV_SEQ_UNDEFINED, +struct dtv_cmds_h dtv_cmds[] = { + [DTV_SEQ_UNDEFINED] = { + .name = "DTV_SEQ_UNDEFINED", + .cmd = DTV_SEQ_UNDEFINED, .set = 1, }, - [TV_SEQ_START] = { - .name = "TV_SEQ_START", - .cmd = TV_SEQ_START, + [DTV_SEQ_START] = { + .name = "DTV_SEQ_START", + .cmd = DTV_SEQ_START, .set = 1, }, - [TV_SEQ_CONTINUE] = { - .name = "TV_SEQ_CONTINUE", - .cmd = TV_SEQ_CONTINUE, + [DTV_SEQ_CONTINUE] = { + .name = "DTV_SEQ_CONTINUE", + .cmd = DTV_SEQ_CONTINUE, .set = 1, }, - [TV_SEQ_COMPLETE] = { - .name = "TV_SEQ_COMPLETE", - .cmd = TV_SEQ_COMPLETE, + [DTV_SEQ_COMPLETE] = { + .name = "DTV_SEQ_COMPLETE", + .cmd = DTV_SEQ_COMPLETE, .set = 1, }, - [TV_SEQ_TERMINATE] = { - .name = "TV_SEQ_TERMINATE", - .cmd = TV_SEQ_TERMINATE, + [DTV_SEQ_TERMINATE] = { + .name = "DTV_SEQ_TERMINATE", + .cmd = DTV_SEQ_TERMINATE, .set = 1, }, /* Set */ - [TV_SET_FREQUENCY] = { - .name = "TV_SET_FREQUENCY", - .cmd = TV_SET_FREQUENCY, + [DTV_SET_FREQUENCY] = { + .name = "DTV_SET_FREQUENCY", + .cmd = DTV_SET_FREQUENCY, .set = 1, }, - [TV_SET_BANDWIDTH] = { - .name = "TV_SET_BANDWIDTH", - .cmd = TV_SET_BANDWIDTH, + [DTV_SET_BANDWIDTH] = { + .name = "DTV_SET_BANDWIDTH", + .cmd = DTV_SET_BANDWIDTH, .set = 1, }, - [TV_SET_MODULATION] = { - .name = "TV_SET_MODULATION", - .cmd = TV_SET_MODULATION, + [DTV_SET_MODULATION] = { + .name = "DTV_SET_MODULATION", + .cmd = DTV_SET_MODULATION, .set = 1, }, - [TV_SET_INVERSION] = { - .name = "TV_SET_INVERSION", - .cmd = TV_SET_INVERSION, + [DTV_SET_INVERSION] = { + .name = "DTV_SET_INVERSION", + .cmd = DTV_SET_INVERSION, .set = 1, }, - [TV_SET_DISEQC_MASTER] = { - .name = "TV_SET_DISEQC_MASTER", - .cmd = TV_SET_DISEQC_MASTER, + [DTV_SET_DISEQC_MASTER] = { + .name = "DTV_SET_DISEQC_MASTER", + .cmd = DTV_SET_DISEQC_MASTER, .set = 1, .buffer = 1, }, - [TV_SET_SYMBOLRATE] = { - .name = "TV_SET_SYMBOLRATE", - .cmd = TV_SET_SYMBOLRATE, + [DTV_SET_SYMBOLRATE] = { + .name = "DTV_SET_SYMBOLRATE", + .cmd = DTV_SET_SYMBOLRATE, .set = 1, }, - [TV_SET_INNERFEC] = { - .name = "TV_SET_INNERFEC", - .cmd = TV_SET_INNERFEC, + [DTV_SET_INNERFEC] = { + .name = "DTV_SET_INNERFEC", + .cmd = DTV_SET_INNERFEC, .set = 1, }, - [TV_SET_VOLTAGE] = { - .name = "TV_SET_VOLTAGE", - .cmd = TV_SET_VOLTAGE, + [DTV_SET_VOLTAGE] = { + .name = "DTV_SET_VOLTAGE", + .cmd = DTV_SET_VOLTAGE, .set = 1, }, - [TV_SET_TONE] = { - .name = "TV_SET_TONE", - .cmd = TV_SET_TONE, + [DTV_SET_TONE] = { + .name = "DTV_SET_TONE", + .cmd = DTV_SET_TONE, .set = 1, }, - [TV_SET_PILOT] = { - .name = "TV_SET_PILOT", - .cmd = TV_SET_PILOT, + [DTV_SET_PILOT] = { + .name = "DTV_SET_PILOT", + .cmd = DTV_SET_PILOT, .set = 1, }, - [TV_SET_ROLLOFF] = { - .name = "TV_SET_ROLLOFF", - .cmd = TV_SET_ROLLOFF, + [DTV_SET_ROLLOFF] = { + .name = "DTV_SET_ROLLOFF", + .cmd = DTV_SET_ROLLOFF, .set = 1, }, - [TV_SET_DELIVERY_SYSTEM] = { - .name = "TV_SET_DELIVERY_SYSTEM", - .cmd = TV_SET_DELIVERY_SYSTEM, + [DTV_SET_DELIVERY_SYSTEM] = { + .name = "DTV_SET_DELIVERY_SYSTEM", + .cmd = DTV_SET_DELIVERY_SYSTEM, .set = 1, }, - [TV_SET_ISDB_SEGMENT_NUM] = { - .name = "TV_SET_ISDB_SEGMENT_NUM", - .cmd = TV_SET_ISDB_SEGMENT_NUM, + [DTV_SET_ISDB_SEGMENT_NUM] = { + .name = "DTV_SET_ISDB_SEGMENT_NUM", + .cmd = DTV_SET_ISDB_SEGMENT_NUM, .set = 1, }, - [TV_SET_ISDB_SEGMENT_WIDTH] = { - .name = "TV_SET_ISDB_SEGMENT_WIDTH", - .cmd = TV_SET_ISDB_SEGMENT_WIDTH, + [DTV_SET_ISDB_SEGMENT_WIDTH] = { + .name = "DTV_SET_ISDB_SEGMENT_WIDTH", + .cmd = DTV_SET_ISDB_SEGMENT_WIDTH, .set = 1, }, /* Get */ - [TV_GET_FREQUENCY] = { - .name = "TV_GET_FREQUENCY", - .cmd = TV_GET_FREQUENCY, + [DTV_GET_FREQUENCY] = { + .name = "DTV_GET_FREQUENCY", + .cmd = DTV_GET_FREQUENCY, .set = 0, }, - [TV_GET_BANDWIDTH] = { - .name = "TV_GET_BANDWIDTH", - .cmd = TV_GET_BANDWIDTH, + [DTV_GET_BANDWIDTH] = { + .name = "DTV_GET_BANDWIDTH", + .cmd = DTV_GET_BANDWIDTH, .set = 0, }, - [TV_GET_MODULATION] = { - .name = "TV_GET_MODULATION", - .cmd = TV_GET_MODULATION, + [DTV_GET_MODULATION] = { + .name = "DTV_GET_MODULATION", + .cmd = DTV_GET_MODULATION, .set = 0, }, - [TV_GET_INVERSION] = { - .name = "TV_GET_INVERSION", - .cmd = TV_GET_INVERSION, + [DTV_GET_INVERSION] = { + .name = "DTV_GET_INVERSION", + .cmd = DTV_GET_INVERSION, .set = 0, }, - [TV_GET_DISEQC_SLAVE_REPLY] = { - .name = "TV_GET_DISEQC_SLAVE_REPLY", - .cmd = TV_GET_DISEQC_SLAVE_REPLY, + [DTV_GET_DISEQC_SLAVE_REPLY] = { + .name = "DTV_GET_DISEQC_SLAVE_REPLY", + .cmd = DTV_GET_DISEQC_SLAVE_REPLY, .set = 0, .buffer = 1, }, - [TV_GET_SYMBOLRATE] = { - .name = "TV_GET_SYMBOLRATE", - .cmd = TV_GET_SYMBOLRATE, + [DTV_GET_SYMBOLRATE] = { + .name = "DTV_GET_SYMBOLRATE", + .cmd = DTV_GET_SYMBOLRATE, .set = 0, }, - [TV_GET_INNERFEC] = { - .name = "TV_GET_INNERFEC", - .cmd = TV_GET_INNERFEC, + [DTV_GET_INNERFEC] = { + .name = "DTV_GET_INNERFEC", + .cmd = DTV_GET_INNERFEC, .set = 0, }, - [TV_GET_VOLTAGE] = { - .name = "TV_GET_VOLTAGE", - .cmd = TV_GET_VOLTAGE, + [DTV_GET_VOLTAGE] = { + .name = "DTV_GET_VOLTAGE", + .cmd = DTV_GET_VOLTAGE, .set = 0, }, - [TV_GET_TONE] = { - .name = "TV_GET_TONE", - .cmd = TV_GET_TONE, + [DTV_GET_TONE] = { + .name = "DTV_GET_TONE", + .cmd = DTV_GET_TONE, .set = 0, }, - [TV_GET_PILOT] = { - .name = "TV_GET_PILOT", - .cmd = TV_GET_PILOT, + [DTV_GET_PILOT] = { + .name = "DTV_GET_PILOT", + .cmd = DTV_GET_PILOT, .set = 0, }, - [TV_GET_ROLLOFF] = { - .name = "TV_GET_ROLLOFF", - .cmd = TV_GET_ROLLOFF, + [DTV_GET_ROLLOFF] = { + .name = "DTV_GET_ROLLOFF", + .cmd = DTV_GET_ROLLOFF, .set = 0, }, - [TV_GET_DELIVERY_SYSTEM] = { - .name = "TV_GET_DELIVERY_SYSTEM", - .cmd = TV_GET_DELIVERY_SYSTEM, + [DTV_GET_DELIVERY_SYSTEM] = { + .name = "DTV_GET_DELIVERY_SYSTEM", + .cmd = DTV_GET_DELIVERY_SYSTEM, .set = 0, }, - [TV_GET_ISDB_SEGMENT_NUM] = { - .name = "TV_GET_ISDB_SEGMENT_NUM", - .cmd = TV_GET_ISDB_SEGMENT_NUM, + [DTV_GET_ISDB_SEGMENT_NUM] = { + .name = "DTV_GET_ISDB_SEGMENT_NUM", + .cmd = DTV_GET_ISDB_SEGMENT_NUM, .set = 0, }, - [TV_GET_ISDB_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_SEGMENT_WIDTH, + [DTV_GET_ISDB_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_SEGMENT_WIDTH, .set = 0, }, - [TV_GET_ISDB_LAYERA_FEC] = { - .name = "TV_GET_ISDB_LAYERA_FEC", - .cmd = TV_GET_ISDB_LAYERA_FEC, + [DTV_GET_ISDB_LAYERA_FEC] = { + .name = "DTV_GET_ISDB_LAYERA_FEC", + .cmd = DTV_GET_ISDB_LAYERA_FEC, .set = 0, }, - [TV_GET_ISDB_LAYERA_MODULATION] = { - .name = "TV_GET_ISDB_LAYERA_MODULATION", - .cmd = TV_GET_ISDB_LAYERA_MODULATION, + [DTV_GET_ISDB_LAYERA_MODULATION] = { + .name = "DTV_GET_ISDB_LAYERA_MODULATION", + .cmd = DTV_GET_ISDB_LAYERA_MODULATION, .set = 0, }, - [TV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_LAYERA_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + [DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH, .set = 0, }, - [TV_GET_ISDB_LAYERB_FEC] = { - .name = "TV_GET_ISDB_LAYERB_FEC", - .cmd = TV_GET_ISDB_LAYERB_FEC, + [DTV_GET_ISDB_LAYERB_FEC] = { + .name = "DTV_GET_ISDB_LAYERB_FEC", + .cmd = DTV_GET_ISDB_LAYERB_FEC, .set = 0, }, - [TV_GET_ISDB_LAYERB_MODULATION] = { - .name = "TV_GET_ISDB_LAYERB_MODULATION", - .cmd = TV_GET_ISDB_LAYERB_MODULATION, + [DTV_GET_ISDB_LAYERB_MODULATION] = { + .name = "DTV_GET_ISDB_LAYERB_MODULATION", + .cmd = DTV_GET_ISDB_LAYERB_MODULATION, .set = 0, }, - [TV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_LAYERB_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + [DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH, .set = 0, }, - [TV_GET_ISDB_LAYERC_FEC] = { - .name = "TV_GET_ISDB_LAYERC_FEC", - .cmd = TV_GET_ISDB_LAYERC_FEC, + [DTV_GET_ISDB_LAYERC_FEC] = { + .name = "DTV_GET_ISDB_LAYERC_FEC", + .cmd = DTV_GET_ISDB_LAYERC_FEC, .set = 0, }, - [TV_GET_ISDB_LAYERC_MODULATION] = { - .name = "TV_GET_ISDB_LAYERC_MODULATION", - .cmd = TV_GET_ISDB_LAYERC_MODULATION, + [DTV_GET_ISDB_LAYERC_MODULATION] = { + .name = "DTV_GET_ISDB_LAYERC_MODULATION", + .cmd = DTV_GET_ISDB_LAYERC_MODULATION, .set = 0, }, - [TV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_LAYERC_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + [DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH, .set = 0, }, }; -void tv_property_dump(tv_property_t *tvp) +void dtv_property_dump(dtv_property_t *tvp) { int i; printk("%s() tvp.cmd = 0x%08x (%s)\n" ,__FUNCTION__ ,tvp->cmd - ,tv_cmds[ tvp->cmd ].name); + ,dtv_cmds[ tvp->cmd ].name); - if(tv_cmds[ tvp->cmd ].buffer) { + if(dtv_cmds[ tvp->cmd ].buffer) { printk("%s() tvp.u.buffer.len = 0x%02x\n" ,__FUNCTION__ @@ -1008,7 +1008,7 @@ int is_legacy_delivery_system(fe_delivery_system_t s) return 0; } -int tv_property_cache_submit(struct dvb_frontend *fe) +int dtv_property_cache_submit(struct dvb_frontend *fe) { /* We have to do one of two things: @@ -1036,7 +1036,7 @@ int tv_property_cache_submit(struct dvb_frontend *fe) int r; - struct tv_frontend_properties *c = &fe->tv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dvb_frontend_parameters p; @@ -1115,7 +1115,7 @@ int tv_property_cache_submit(struct dvb_frontend *fe) case _16APSK: case NBC_QPSK: /* Just post a notification to the demod driver and let it pull - * the specific values it wants from its tv_property_cache. + * the specific values it wants from its dtv_property_cache. * It can decide how best to use those parameters. * IOCTL will call set_frontend (by default) due to zigzag * support etc. @@ -1161,147 +1161,147 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp, +int dtv_property_process(struct dvb_frontend *fe, dtv_property_t *tvp, struct inode *inode, struct file *file) { int r = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; printk("%s()\n", __FUNCTION__); - tv_property_dump(tvp); + dtv_property_dump(tvp); switch(tvp->cmd) { - case TV_SEQ_START: - case TV_SEQ_TERMINATE: + case DTV_SEQ_START: + case DTV_SEQ_TERMINATE: /* Reset a cache of data specific to the frontend here. This does * not effect hardware. */ printk("%s() Flushing property cache\n", __FUNCTION__); - memset(&fe->tv_property_cache, 0, sizeof(struct tv_frontend_properties)); - fe->tv_property_cache.state = TV_SEQ_START; - fe->tv_property_cache.delivery_system = SYS_UNDEFINED; + memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties)); + fe->dtv_property_cache.state = DTV_SEQ_START; + fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; break; - case TV_SEQ_COMPLETE: + case DTV_SEQ_COMPLETE: /* interpret the cache of data, build either a traditional frontend * tunerequest and submit it to a subset of the ioctl handler, * or, call a new undefined method on the frontend to deal with * all new tune requests. */ - fe->tv_property_cache.state = TV_SEQ_COMPLETE; + fe->dtv_property_cache.state = DTV_SEQ_COMPLETE; printk("%s() Finalised property cache\n", __FUNCTION__); - r |= tv_property_cache_submit(fe); + r |= dtv_property_cache_submit(fe); r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, &fepriv->parameters); break; - case TV_SET_FREQUENCY: - fe->tv_property_cache.frequency = tvp->u.data; + case DTV_SET_FREQUENCY: + fe->dtv_property_cache.frequency = tvp->u.data; break; - case TV_GET_FREQUENCY: - tvp->u.data = fe->tv_property_cache.frequency; + case DTV_GET_FREQUENCY: + tvp->u.data = fe->dtv_property_cache.frequency; break; - case TV_SET_MODULATION: - fe->tv_property_cache.modulation = tvp->u.data; + case DTV_SET_MODULATION: + fe->dtv_property_cache.modulation = tvp->u.data; break; - case TV_GET_MODULATION: - tvp->u.data = fe->tv_property_cache.modulation; + case DTV_GET_MODULATION: + tvp->u.data = fe->dtv_property_cache.modulation; break; - case TV_SET_BANDWIDTH: - fe->tv_property_cache.bandwidth = tvp->u.data; + case DTV_SET_BANDWIDTH: + fe->dtv_property_cache.bandwidth = tvp->u.data; break; - case TV_GET_BANDWIDTH: - tvp->u.data = fe->tv_property_cache.bandwidth; + case DTV_GET_BANDWIDTH: + tvp->u.data = fe->dtv_property_cache.bandwidth; break; - case TV_SET_INVERSION: - fe->tv_property_cache.inversion = tvp->u.data; + case DTV_SET_INVERSION: + fe->dtv_property_cache.inversion = tvp->u.data; break; - case TV_GET_INVERSION: - tvp->u.data = fe->tv_property_cache.inversion; + case DTV_GET_INVERSION: + tvp->u.data = fe->dtv_property_cache.inversion; break; - case TV_SET_SYMBOLRATE: - fe->tv_property_cache.symbol_rate = tvp->u.data; + case DTV_SET_SYMBOLRATE: + fe->dtv_property_cache.symbol_rate = tvp->u.data; break; - case TV_GET_SYMBOLRATE: - tvp->u.data = fe->tv_property_cache.symbol_rate; + case DTV_GET_SYMBOLRATE: + tvp->u.data = fe->dtv_property_cache.symbol_rate; break; - case TV_SET_INNERFEC: - fe->tv_property_cache.fec_inner = tvp->u.data; + case DTV_SET_INNERFEC: + fe->dtv_property_cache.fec_inner = tvp->u.data; break; - case TV_GET_INNERFEC: - tvp->u.data = fe->tv_property_cache.fec_inner; + case DTV_GET_INNERFEC: + tvp->u.data = fe->dtv_property_cache.fec_inner; break; - case TV_SET_PILOT: - fe->tv_property_cache.pilot = tvp->u.data; + case DTV_SET_PILOT: + fe->dtv_property_cache.pilot = tvp->u.data; break; - case TV_GET_PILOT: - tvp->u.data = fe->tv_property_cache.pilot; + case DTV_GET_PILOT: + tvp->u.data = fe->dtv_property_cache.pilot; break; - case TV_SET_ROLLOFF: - fe->tv_property_cache.rolloff = tvp->u.data; + case DTV_SET_ROLLOFF: + fe->dtv_property_cache.rolloff = tvp->u.data; break; - case TV_GET_ROLLOFF: - tvp->u.data = fe->tv_property_cache.rolloff; + case DTV_GET_ROLLOFF: + tvp->u.data = fe->dtv_property_cache.rolloff; break; - case TV_SET_DELIVERY_SYSTEM: - fe->tv_property_cache.delivery_system = tvp->u.data; + case DTV_SET_DELIVERY_SYSTEM: + fe->dtv_property_cache.delivery_system = tvp->u.data; break; - case TV_GET_DELIVERY_SYSTEM: - tvp->u.data = fe->tv_property_cache.delivery_system; + case DTV_GET_DELIVERY_SYSTEM: + tvp->u.data = fe->dtv_property_cache.delivery_system; break; /* ISDB-T Support here */ - case TV_SET_ISDB_SEGMENT_NUM: - fe->tv_property_cache.isdb_segment_num = tvp->u.data; + case DTV_SET_ISDB_SEGMENT_NUM: + fe->dtv_property_cache.isdb_segment_num = tvp->u.data; break; - case TV_GET_ISDB_SEGMENT_NUM: - tvp->u.data = fe->tv_property_cache.isdb_segment_num; + case DTV_GET_ISDB_SEGMENT_NUM: + tvp->u.data = fe->dtv_property_cache.isdb_segment_num; break; - case TV_SET_ISDB_SEGMENT_WIDTH: - fe->tv_property_cache.isdb_segment_width = tvp->u.data; + case DTV_SET_ISDB_SEGMENT_WIDTH: + fe->dtv_property_cache.isdb_segment_width = tvp->u.data; break; - case TV_GET_ISDB_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_segment_width; + case DTV_GET_ISDB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_segment_width; break; - case TV_GET_ISDB_LAYERA_FEC: - tvp->u.data = fe->tv_property_cache.isdb_layera_fec; + case DTV_GET_ISDB_LAYERA_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; break; - case TV_GET_ISDB_LAYERA_MODULATION: - tvp->u.data = fe->tv_property_cache.isdb_layera_modulation; + case DTV_GET_ISDB_LAYERA_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; break; - case TV_GET_ISDB_LAYERA_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_layera_segment_width; + case DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; break; - case TV_GET_ISDB_LAYERB_FEC: - tvp->u.data = fe->tv_property_cache.isdb_layerb_fec; + case DTV_GET_ISDB_LAYERB_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; break; - case TV_GET_ISDB_LAYERB_MODULATION: - tvp->u.data = fe->tv_property_cache.isdb_layerb_modulation; + case DTV_GET_ISDB_LAYERB_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; break; - case TV_GET_ISDB_LAYERB_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_layerb_segment_width; + case DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; break; - case TV_GET_ISDB_LAYERC_FEC: - tvp->u.data = fe->tv_property_cache.isdb_layerc_fec; + case DTV_GET_ISDB_LAYERC_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; break; - case TV_GET_ISDB_LAYERC_MODULATION: - tvp->u.data = fe->tv_property_cache.isdb_layerc_modulation; + case DTV_GET_ISDB_LAYERC_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; break; - case TV_GET_ISDB_LAYERC_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_layerc_segment_width; + case DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; break; - case TV_SET_VOLTAGE: - fe->tv_property_cache.voltage = tvp->u.data; + case DTV_SET_VOLTAGE: + fe->dtv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, - (void *)fe->tv_property_cache.voltage); + (void *)fe->dtv_property_cache.voltage); break; - case TV_GET_VOLTAGE: - tvp->u.data = fe->tv_property_cache.voltage; + case DTV_GET_VOLTAGE: + tvp->u.data = fe->dtv_property_cache.voltage; break; - case TV_SET_TONE: - fe->tv_property_cache.sectone = tvp->u.data; + case DTV_SET_TONE: + fe->dtv_property_cache.sectone = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, - (void *)fe->tv_property_cache.sectone); + (void *)fe->dtv_property_cache.sectone); break; - case TV_GET_TONE: - tvp->u.data = fe->tv_property_cache.sectone; + case DTV_GET_TONE: + tvp->u.data = fe->dtv_property_cache.sectone; break; } @@ -1344,7 +1344,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; int err = -EOPNOTSUPP; - tv_property_t *tvp; + dtv_property_t *tvp; dprintk("%s\n", __func__); @@ -1355,14 +1355,14 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, /* TODO: ioctl userdata out of range check here */ tvp = parg; - while(tvp->cmd != TV_SEQ_UNDEFINED) { - tv_property_process(fe, tvp, inode, file); - if( (tvp->cmd == TV_SEQ_TERMINATE) || (tvp->cmd == TV_SEQ_COMPLETE) ) + while(tvp->cmd != DTV_SEQ_UNDEFINED) { + dtv_property_process(fe, tvp, inode, file); + if( (tvp->cmd == DTV_SEQ_TERMINATE) || (tvp->cmd == DTV_SEQ_COMPLETE) ) break; tvp++; } - if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } err = 0; @@ -1545,7 +1545,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { err = -EINVAL; break; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 61d53ee70f2a..f376f281cde2 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -170,8 +170,8 @@ struct dvb_frontend_ops { struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; - int (*set_property)(struct dvb_frontend* fe, tv_property_t* tvp); - int (*get_property)(struct dvb_frontend* fe, tv_property_t* tvp); + int (*set_property)(struct dvb_frontend* fe, dtv_property_t* tvp); + int (*get_property)(struct dvb_frontend* fe, dtv_property_t* tvp); int (*set_params)(struct dvb_frontend* fe); }; @@ -186,7 +186,7 @@ struct dvb_fe_events { struct mutex mtx; }; -struct tv_frontend_properties { +struct dtv_frontend_properties { /* Cache State */ u32 state; @@ -233,7 +233,7 @@ struct dvb_frontend { void *frontend_priv; void *sec_priv; void *analog_demod_priv; - struct tv_frontend_properties tv_property_cache; + struct dtv_frontend_properties dtv_property_cache; }; extern int dvb_register_frontend(struct dvb_adapter *dvb, diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c index 666a0d89e83c..f150fa24ff9f 100644 --- a/drivers/media/dvb/frontends/cx24116.c +++ b/drivers/media/dvb/frontends/cx24116.c @@ -776,7 +776,7 @@ error: static int cx24116_get_params(struct dvb_frontend* fe) { struct cx24116_state *state = fe->demodulator_priv; - struct tv_frontend_properties *cache = &fe->tv_property_cache; + struct dtv_frontend_properties *cache = &fe->dtv_property_cache; dprintk("%s()\n",__func__); @@ -796,7 +796,7 @@ static int cx24116_initfe(struct dvb_frontend* fe) return cx24116_diseqc_init(fe); } -static int cx24116_set_property(struct dvb_frontend *fe, tv_property_t* tvp) +static int cx24116_set_property(struct dvb_frontend *fe, dtv_property_t* tvp) { dprintk("%s(..)\n", __func__); return 0; @@ -814,7 +814,7 @@ static int cx24116_set_params(struct dvb_frontend *fe) static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct cx24116_state *state = fe->demodulator_priv; - struct tv_frontend_properties *c = &fe->tv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24116_cmd cmd; fe_status_t tunerstat; int ret, above30msps; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 4d3770021736..aeace74b5366 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -250,61 +250,61 @@ struct dvb_frontend_event { * don't insert random new commands and break backwards * binary compatability. */ -typedef enum tv_cmd_types { - TV_SEQ_UNDEFINED, - TV_SEQ_START, - TV_SEQ_CONTINUE, - TV_SEQ_COMPLETE, - TV_SEQ_TERMINATE, - - TV_SET_FREQUENCY, - TV_SET_MODULATION, - TV_SET_BANDWIDTH, - TV_SET_INVERSION, - TV_SET_DISEQC_MASTER, - TV_SET_SYMBOLRATE, - TV_SET_INNERFEC, - TV_SET_VOLTAGE, - TV_SET_TONE, - TV_SET_PILOT, - TV_SET_ROLLOFF, - - TV_GET_FREQUENCY, - TV_GET_MODULATION, - TV_GET_BANDWIDTH, - TV_GET_INVERSION, - TV_GET_DISEQC_SLAVE_REPLY, - TV_GET_SYMBOLRATE, - TV_GET_INNERFEC, - TV_GET_VOLTAGE, - TV_GET_TONE, - TV_GET_PILOT, - TV_GET_ROLLOFF, +typedef enum dtv_cmd_types { + DTV_SEQ_UNDEFINED, + DTV_SEQ_START, + DTV_SEQ_CONTINUE, + DTV_SEQ_COMPLETE, + DTV_SEQ_TERMINATE, + + DTV_SET_FREQUENCY, + DTV_SET_MODULATION, + DTV_SET_BANDWIDTH, + DTV_SET_INVERSION, + DTV_SET_DISEQC_MASTER, + DTV_SET_SYMBOLRATE, + DTV_SET_INNERFEC, + DTV_SET_VOLTAGE, + DTV_SET_TONE, + DTV_SET_PILOT, + DTV_SET_ROLLOFF, + + DTV_GET_FREQUENCY, + DTV_GET_MODULATION, + DTV_GET_BANDWIDTH, + DTV_GET_INVERSION, + DTV_GET_DISEQC_SLAVE_REPLY, + DTV_GET_SYMBOLRATE, + DTV_GET_INNERFEC, + DTV_GET_VOLTAGE, + DTV_GET_TONE, + DTV_GET_PILOT, + DTV_GET_ROLLOFF, /* Basic enumeration set for querying unlimited capabilities */ - TV_GET_FE_CAPABILITY_COUNT, - TV_GET_FE_CAPABILITY, + DTV_GET_FE_CAPABILITY_COUNT, + DTV_GET_FE_CAPABILITY, /* New commands are always appended */ - TV_SET_DELIVERY_SYSTEM, - TV_GET_DELIVERY_SYSTEM, + DTV_SET_DELIVERY_SYSTEM, + DTV_GET_DELIVERY_SYSTEM, /* ISDB-T */ - TV_SET_ISDB_SEGMENT_NUM, - TV_GET_ISDB_SEGMENT_NUM, - TV_SET_ISDB_SEGMENT_WIDTH, - TV_GET_ISDB_SEGMENT_WIDTH, - TV_GET_ISDB_LAYERA_FEC, - TV_GET_ISDB_LAYERA_MODULATION, - TV_GET_ISDB_LAYERA_SEGMENT_WIDTH, - TV_GET_ISDB_LAYERB_FEC, - TV_GET_ISDB_LAYERB_MODULATION, - TV_GET_ISDB_LAYERB_SEGMENT_WIDTH, - TV_GET_ISDB_LAYERC_FEC, - TV_GET_ISDB_LAYERC_MODULATION, - TV_GET_ISDB_LAYERC_SEGMENT_WIDTH, - -} tv_cmd_types_t; + DTV_SET_ISDB_SEGMENT_NUM, + DTV_GET_ISDB_SEGMENT_NUM, + DTV_SET_ISDB_SEGMENT_WIDTH, + DTV_GET_ISDB_SEGMENT_WIDTH, + DTV_GET_ISDB_LAYERA_FEC, + DTV_GET_ISDB_LAYERA_MODULATION, + DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + DTV_GET_ISDB_LAYERB_FEC, + DTV_GET_ISDB_LAYERB_MODULATION, + DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + DTV_GET_ISDB_LAYERC_FEC, + DTV_GET_ISDB_LAYERC_MODULATION, + DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + +} dtv_cmd_types_t; typedef enum fe_pilot { PILOT_ON, @@ -337,7 +337,7 @@ typedef enum fe_delivery_system { SYS_DAB, } fe_delivery_system_t; -struct tv_cmds_h { +struct dtv_cmds_h { char *name; /* A display name for debugging purposes */ __u32 cmd; /* A unique ID */ @@ -357,13 +357,13 @@ typedef struct { __u32 len; } buffer; } u; -} tv_property_t; +} dtv_property_t; /* No more than 16 properties during any given ioctl */ -typedef tv_property_t tv_properties_t[16]; +typedef dtv_property_t dtv_properties_t[16]; -#define FE_SET_PROPERTY _IOW('o', 82, tv_properties_t) -#define FE_GET_PROPERTY _IOR('o', 83, tv_properties_t) +#define FE_SET_PROPERTY _IOW('o', 82, dtv_properties_t) +#define FE_GET_PROPERTY _IOR('o', 83, dtv_properties_t) /** -- cgit v1.2.3 From e7fee0f3aa111d42cdcfc1470cfdc21dde0cdbe2 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 10:23:01 -0300 Subject: V4L/DVB (8996): S2API: typedefs replaced, _SEQ_'s removed, fixed 16 command arrays replaced After discussion the following changes were made: 1. Removed the typedefs in frontend.h, use structures. 2. In the frontend.h, remove the 16 command limit on the API and switch to a flexible variable length API. For practical reasons a #define limits this to 64, this should be discussed. 3. Changed dvb-core ioctl handing to deal with variable sequences of commands. tune-v0.0.3.c is required to use this API, it contains the interface changes. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 83 ++++++++++++++++--------------- drivers/media/dvb/dvb-core/dvb_frontend.h | 4 +- drivers/media/dvb/frontends/cx24116.c | 2 +- include/linux/dvb/frontend.h | 27 ++++++---- 4 files changed, 63 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 9c4761506d2d..763da968236f 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -756,29 +756,14 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, } struct dtv_cmds_h dtv_cmds[] = { - [DTV_SEQ_UNDEFINED] = { - .name = "DTV_SEQ_UNDEFINED", - .cmd = DTV_SEQ_UNDEFINED, + [DTV_TUNE] = { + .name = "DTV_TUNE", + .cmd = DTV_TUNE, .set = 1, }, - [DTV_SEQ_START] = { - .name = "DTV_SEQ_START", - .cmd = DTV_SEQ_START, - .set = 1, - }, - [DTV_SEQ_CONTINUE] = { - .name = "DTV_SEQ_CONTINUE", - .cmd = DTV_SEQ_CONTINUE, - .set = 1, - }, - [DTV_SEQ_COMPLETE] = { - .name = "DTV_SEQ_COMPLETE", - .cmd = DTV_SEQ_COMPLETE, - .set = 1, - }, - [DTV_SEQ_TERMINATE] = { - .name = "DTV_SEQ_TERMINATE", - .cmd = DTV_SEQ_TERMINATE, + [DTV_CLEAR] = { + .name = "DTV_CLEAR", + .cmd = DTV_CLEAR, .set = 1, }, @@ -974,7 +959,7 @@ struct dtv_cmds_h dtv_cmds[] = { }, }; -void dtv_property_dump(dtv_property_t *tvp) +void dtv_property_dump(struct dtv_property *tvp) { int i; @@ -1044,6 +1029,7 @@ int dtv_property_cache_submit(struct dvb_frontend *fe) /* For legacy delivery systems we don't need the delivery_system to be specified */ if(is_legacy_delivery_system(c->delivery_system)) { + printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation); switch(c->modulation) { case QPSK: printk("%s() Preparing QPSK req\n", __FUNCTION__); @@ -1161,7 +1147,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int dtv_property_process(struct dvb_frontend *fe, dtv_property_t *tvp, +int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, struct inode *inode, struct file *file) { int r = 0; @@ -1170,23 +1156,22 @@ int dtv_property_process(struct dvb_frontend *fe, dtv_property_t *tvp, dtv_property_dump(tvp); switch(tvp->cmd) { - case DTV_SEQ_START: - case DTV_SEQ_TERMINATE: + case DTV_CLEAR: /* Reset a cache of data specific to the frontend here. This does * not effect hardware. */ printk("%s() Flushing property cache\n", __FUNCTION__); memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties)); - fe->dtv_property_cache.state = DTV_SEQ_START; + fe->dtv_property_cache.state = tvp->cmd; fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; break; - case DTV_SEQ_COMPLETE: + case DTV_TUNE: /* interpret the cache of data, build either a traditional frontend * tunerequest and submit it to a subset of the ioctl handler, * or, call a new undefined method on the frontend to deal with * all new tune requests. */ - fe->dtv_property_cache.state = DTV_SEQ_COMPLETE; + fe->dtv_property_cache.state = tvp->cmd; printk("%s() Finalised property cache\n", __FUNCTION__); r |= dtv_property_cache_submit(fe); r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, @@ -1344,30 +1329,48 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; int err = -EOPNOTSUPP; - dtv_property_t *tvp; + + struct dtv_properties *tvps = NULL; + struct dtv_property *tvp = NULL; + int i; dprintk("%s\n", __func__); if(cmd == FE_SET_PROPERTY) { printk("%s() FE_SET_PROPERTY\n", __FUNCTION__); - /* TODO: basic property validation here */ + tvps = (struct dtv_properties __user *)parg; - /* TODO: ioctl userdata out of range check here */ - tvp = parg; - while(tvp->cmd != DTV_SEQ_UNDEFINED) { - dtv_property_process(fe, tvp, inode, file); - if( (tvp->cmd == DTV_SEQ_TERMINATE) || (tvp->cmd == DTV_SEQ_COMPLETE) ) - break; - tvp++; + printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num); + printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (tvps->num > DTV_IOCTL_MAX_MSGS) + return -EINVAL; + + tvp = (struct dtv_property *) kmalloc(tvps->num * + sizeof(struct dtv_property), GFP_KERNEL); + if (!tvp) { + err = -ENOMEM; + goto out; } - if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { + if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < tvps->num; i++) + dtv_property_process(fe, tvp + i, inode, file); + + if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } err = 0; } - +out: + kfree(tvp); return err; } @@ -1545,7 +1548,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { + if(fe->dtv_property_cache.state == DTV_TUNE) { if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { err = -EINVAL; break; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index f376f281cde2..85d30201a695 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -170,8 +170,8 @@ struct dvb_frontend_ops { struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; - int (*set_property)(struct dvb_frontend* fe, dtv_property_t* tvp); - int (*get_property)(struct dvb_frontend* fe, dtv_property_t* tvp); + int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp); + int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp); int (*set_params)(struct dvb_frontend* fe); }; diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c index f150fa24ff9f..9f93930a2594 100644 --- a/drivers/media/dvb/frontends/cx24116.c +++ b/drivers/media/dvb/frontends/cx24116.c @@ -796,7 +796,7 @@ static int cx24116_initfe(struct dvb_frontend* fe) return cx24116_diseqc_init(fe); } -static int cx24116_set_property(struct dvb_frontend *fe, dtv_property_t* tvp) +static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp) { dprintk("%s(..)\n", __func__); return 0; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index aeace74b5366..f667bf377a7b 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -251,11 +251,8 @@ struct dvb_frontend_event { * binary compatability. */ typedef enum dtv_cmd_types { - DTV_SEQ_UNDEFINED, - DTV_SEQ_START, - DTV_SEQ_CONTINUE, - DTV_SEQ_COMPLETE, - DTV_SEQ_TERMINATE, + DTV_TUNE, + DTV_CLEAR, DTV_SET_FREQUENCY, DTV_SET_MODULATION, @@ -348,22 +345,32 @@ struct dtv_cmds_h { __u32 reserved:30; /* Align */ }; -typedef struct { +struct dtv_property { __u32 cmd; + __u32 reserved[3]; union { + __s32 valuemin; + __s32 valuemax; __u32 data; struct { __u8 data[32]; __u32 len; + __u32 reserved1[3]; + void *reserved2; } buffer; } u; -} dtv_property_t; +} __attribute__ ((packed)); /* No more than 16 properties during any given ioctl */ -typedef dtv_property_t dtv_properties_t[16]; +struct dtv_properties { + __u32 num; + struct dtv_property *props; +}; + +#define DTV_IOCTL_MAX_MSGS 64 -#define FE_SET_PROPERTY _IOW('o', 82, dtv_properties_t) -#define FE_GET_PROPERTY _IOR('o', 83, dtv_properties_t) +#define FE_SET_PROPERTY _IOW('o', 82, struct dtv_properties) +#define FE_GET_PROPERTY _IOR('o', 83, struct dtv_properties) /** -- cgit v1.2.3 From 177b868d93861077619261dcecd0147d1c033026 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 10:34:19 -0300 Subject: V4L/DVB (8997): S2API: Cleanup SYMBOLRATE, INNERFEC -> SYMBOL_RATE, INNER_FEC This is now consistent with the existing API. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 32 +++++++++++++++---------------- include/linux/dvb/frontend.h | 8 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 763da968236f..ce1de403e99e 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -794,14 +794,14 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 1, .buffer = 1, }, - [DTV_SET_SYMBOLRATE] = { - .name = "DTV_SET_SYMBOLRATE", - .cmd = DTV_SET_SYMBOLRATE, + [DTV_SET_SYMBOL_RATE] = { + .name = "DTV_SET_SYMBOL_RATE", + .cmd = DTV_SET_SYMBOL_RATE, .set = 1, }, - [DTV_SET_INNERFEC] = { - .name = "DTV_SET_INNERFEC", - .cmd = DTV_SET_INNERFEC, + [DTV_SET_INNER_FEC] = { + .name = "DTV_SET_INNER_FEC", + .cmd = DTV_SET_INNER_FEC, .set = 1, }, [DTV_SET_VOLTAGE] = { @@ -867,14 +867,14 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, - [DTV_GET_SYMBOLRATE] = { - .name = "DTV_GET_SYMBOLRATE", - .cmd = DTV_GET_SYMBOLRATE, + [DTV_GET_SYMBOL_RATE] = { + .name = "DTV_GET_SYMBOL_RATE", + .cmd = DTV_GET_SYMBOL_RATE, .set = 0, }, - [DTV_GET_INNERFEC] = { - .name = "DTV_GET_INNERFEC", - .cmd = DTV_GET_INNERFEC, + [DTV_GET_INNER_FEC] = { + .name = "DTV_GET_INNER_FEC", + .cmd = DTV_GET_INNER_FEC, .set = 0, }, [DTV_GET_VOLTAGE] = { @@ -1201,16 +1201,16 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_GET_INVERSION: tvp->u.data = fe->dtv_property_cache.inversion; break; - case DTV_SET_SYMBOLRATE: + case DTV_SET_SYMBOL_RATE: fe->dtv_property_cache.symbol_rate = tvp->u.data; break; - case DTV_GET_SYMBOLRATE: + case DTV_GET_SYMBOL_RATE: tvp->u.data = fe->dtv_property_cache.symbol_rate; break; - case DTV_SET_INNERFEC: + case DTV_SET_INNER_FEC: fe->dtv_property_cache.fec_inner = tvp->u.data; break; - case DTV_GET_INNERFEC: + case DTV_GET_INNER_FEC: tvp->u.data = fe->dtv_property_cache.fec_inner; break; case DTV_SET_PILOT: diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index f667bf377a7b..bfd670fa16b2 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -259,8 +259,8 @@ typedef enum dtv_cmd_types { DTV_SET_BANDWIDTH, DTV_SET_INVERSION, DTV_SET_DISEQC_MASTER, - DTV_SET_SYMBOLRATE, - DTV_SET_INNERFEC, + DTV_SET_SYMBOL_RATE, + DTV_SET_INNER_FEC, DTV_SET_VOLTAGE, DTV_SET_TONE, DTV_SET_PILOT, @@ -271,8 +271,8 @@ typedef enum dtv_cmd_types { DTV_GET_BANDWIDTH, DTV_GET_INVERSION, DTV_GET_DISEQC_SLAVE_REPLY, - DTV_GET_SYMBOLRATE, - DTV_GET_INNERFEC, + DTV_GET_SYMBOL_RATE, + DTV_GET_INNER_FEC, DTV_GET_VOLTAGE, DTV_GET_TONE, DTV_GET_PILOT, -- cgit v1.2.3 From 65af619d84ee2d3a1faacbbeded374d0b313d3a9 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 21:08:59 -0300 Subject: V4L/DVB (8998): s2api: restore DTV_UNDEFINED Signed-off-by: Steven Toth Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index bfd670fa16b2..915edd319f34 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -251,6 +251,7 @@ struct dvb_frontend_event { * binary compatability. */ typedef enum dtv_cmd_types { + DTV_UNDEFINED, DTV_TUNE, DTV_CLEAR, -- cgit v1.2.3 From d7c1500183bc138b634377ed90c046e722b887d8 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 12 Sep 2008 00:33:04 -0300 Subject: V4L/DVB (9001): S2API: ISDBT_SEGMENT_NUM -> ISDBT_SEGMENT_IDX Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 20 ++++++++++---------- drivers/media/dvb/dvb-core/dvb_frontend.h | 2 +- include/linux/dvb/frontend.h | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 2aaaca620033..dc76f9935c81 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -829,9 +829,9 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_SET_DELIVERY_SYSTEM, .set = 1, }, - [DTV_SET_ISDB_SEGMENT_NUM] = { - .name = "DTV_SET_ISDB_SEGMENT_NUM", - .cmd = DTV_SET_ISDB_SEGMENT_NUM, + [DTV_SET_ISDB_SEGMENT_IDX] = { + .name = "DTV_SET_ISDB_SEGMENT_IDX", + .cmd = DTV_SET_ISDB_SEGMENT_IDX, .set = 1, }, [DTV_SET_ISDB_SEGMENT_WIDTH] = { @@ -902,9 +902,9 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_GET_DELIVERY_SYSTEM, .set = 0, }, - [DTV_GET_ISDB_SEGMENT_NUM] = { - .name = "DTV_GET_ISDB_SEGMENT_NUM", - .cmd = DTV_GET_ISDB_SEGMENT_NUM, + [DTV_GET_ISDB_SEGMENT_IDX] = { + .name = "DTV_GET_ISDB_SEGMENT_IDX", + .cmd = DTV_GET_ISDB_SEGMENT_IDX, .set = 0, }, [DTV_GET_ISDB_SEGMENT_WIDTH] = { @@ -1232,11 +1232,11 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, break; /* ISDB-T Support here */ - case DTV_SET_ISDB_SEGMENT_NUM: - fe->dtv_property_cache.isdb_segment_num = tvp->u.data; + case DTV_SET_ISDB_SEGMENT_IDX: + fe->dtv_property_cache.isdb_segment_idx = tvp->u.data; break; - case DTV_GET_ISDB_SEGMENT_NUM: - tvp->u.data = fe->dtv_property_cache.isdb_segment_num; + case DTV_GET_ISDB_SEGMENT_IDX: + tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; break; case DTV_SET_ISDB_SEGMENT_WIDTH: fe->dtv_property_cache.isdb_segment_width = tvp->u.data; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 85d30201a695..1c2090966baf 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -212,7 +212,7 @@ struct dtv_frontend_properties { fe_delivery_system_t delivery_system; /* ISDB-T specifics */ - u32 isdb_segment_num; + u32 isdb_segment_idx; u32 isdb_segment_width; fe_code_rate_t isdb_layera_fec; fe_modulation_t isdb_layera_modulation; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 915edd319f34..e4f211735efb 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -288,8 +288,8 @@ typedef enum dtv_cmd_types { DTV_GET_DELIVERY_SYSTEM, /* ISDB-T */ - DTV_SET_ISDB_SEGMENT_NUM, - DTV_GET_ISDB_SEGMENT_NUM, + DTV_SET_ISDB_SEGMENT_IDX, + DTV_GET_ISDB_SEGMENT_IDX, DTV_SET_ISDB_SEGMENT_WIDTH, DTV_GET_ISDB_SEGMENT_WIDTH, DTV_GET_ISDB_LAYERA_FEC, -- cgit v1.2.3 From 363429a089590f3f4071ebc492b3712fdcba770b Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 12 Sep 2008 01:34:28 -0300 Subject: V4L/DVB (9003): S2API: Remove the DTV_SET_ and DTV_GET_ prefixes The API now assumes that ioctl calls for FE_SET_PROPERTY and all set commands, and FE_GET_PROPERTY are get commands. Simplification. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 431 +++++++++++++++--------------- include/linux/dvb/frontend.h | 67 ++--- 2 files changed, 239 insertions(+), 259 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 205f60262c37..2ed748486e57 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -768,193 +768,128 @@ struct dtv_cmds_h dtv_cmds[] = { }, /* Set */ - [DTV_SET_FREQUENCY] = { - .name = "DTV_SET_FREQUENCY", - .cmd = DTV_SET_FREQUENCY, + [DTV_FREQUENCY] = { + .name = "DTV_FREQUENCY", + .cmd = DTV_FREQUENCY, .set = 1, }, - [DTV_SET_BANDWIDTH] = { - .name = "DTV_SET_BANDWIDTH", - .cmd = DTV_SET_BANDWIDTH, + [DTV_BANDWIDTH] = { + .name = "DTV_BANDWIDTH", + .cmd = DTV_BANDWIDTH, .set = 1, }, - [DTV_SET_MODULATION] = { - .name = "DTV_SET_MODULATION", - .cmd = DTV_SET_MODULATION, + [DTV_MODULATION] = { + .name = "DTV_MODULATION", + .cmd = DTV_MODULATION, .set = 1, }, - [DTV_SET_INVERSION] = { - .name = "DTV_SET_INVERSION", - .cmd = DTV_SET_INVERSION, + [DTV_INVERSION] = { + .name = "DTV_INVERSION", + .cmd = DTV_INVERSION, .set = 1, }, - [DTV_SET_DISEQC_MASTER] = { - .name = "DTV_SET_DISEQC_MASTER", - .cmd = DTV_SET_DISEQC_MASTER, + [DTV_DISEQC_MASTER] = { + .name = "DTV_DISEQC_MASTER", + .cmd = DTV_DISEQC_MASTER, .set = 1, .buffer = 1, }, - [DTV_SET_SYMBOL_RATE] = { - .name = "DTV_SET_SYMBOL_RATE", - .cmd = DTV_SET_SYMBOL_RATE, + [DTV_SYMBOL_RATE] = { + .name = "DTV_SYMBOL_RATE", + .cmd = DTV_SYMBOL_RATE, .set = 1, }, - [DTV_SET_INNER_FEC] = { - .name = "DTV_SET_INNER_FEC", - .cmd = DTV_SET_INNER_FEC, + [DTV_INNER_FEC] = { + .name = "DTV_INNER_FEC", + .cmd = DTV_INNER_FEC, .set = 1, }, - [DTV_SET_VOLTAGE] = { - .name = "DTV_SET_VOLTAGE", - .cmd = DTV_SET_VOLTAGE, + [DTV_VOLTAGE] = { + .name = "DTV_VOLTAGE", + .cmd = DTV_VOLTAGE, .set = 1, }, - [DTV_SET_TONE] = { - .name = "DTV_SET_TONE", - .cmd = DTV_SET_TONE, + [DTV_TONE] = { + .name = "DTV_TONE", + .cmd = DTV_TONE, .set = 1, }, - [DTV_SET_PILOT] = { - .name = "DTV_SET_PILOT", - .cmd = DTV_SET_PILOT, + [DTV_PILOT] = { + .name = "DTV_PILOT", + .cmd = DTV_PILOT, .set = 1, }, - [DTV_SET_ROLLOFF] = { - .name = "DTV_SET_ROLLOFF", - .cmd = DTV_SET_ROLLOFF, + [DTV_ROLLOFF] = { + .name = "DTV_ROLLOFF", + .cmd = DTV_ROLLOFF, .set = 1, }, - [DTV_SET_DELIVERY_SYSTEM] = { - .name = "DTV_SET_DELIVERY_SYSTEM", - .cmd = DTV_SET_DELIVERY_SYSTEM, + [DTV_DELIVERY_SYSTEM] = { + .name = "DTV_DELIVERY_SYSTEM", + .cmd = DTV_DELIVERY_SYSTEM, .set = 1, }, - [DTV_SET_ISDB_SEGMENT_IDX] = { - .name = "DTV_SET_ISDB_SEGMENT_IDX", - .cmd = DTV_SET_ISDB_SEGMENT_IDX, + [DTV_ISDB_SEGMENT_IDX] = { + .name = "DTV_ISDB_SEGMENT_IDX", + .cmd = DTV_ISDB_SEGMENT_IDX, .set = 1, }, - [DTV_SET_ISDB_SEGMENT_WIDTH] = { - .name = "DTV_SET_ISDB_SEGMENT_WIDTH", - .cmd = DTV_SET_ISDB_SEGMENT_WIDTH, + [DTV_ISDB_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_SEGMENT_WIDTH", + .cmd = DTV_ISDB_SEGMENT_WIDTH, .set = 1, }, /* Get */ - [DTV_GET_FREQUENCY] = { - .name = "DTV_GET_FREQUENCY", - .cmd = DTV_GET_FREQUENCY, - .set = 0, - }, - [DTV_GET_BANDWIDTH] = { - .name = "DTV_GET_BANDWIDTH", - .cmd = DTV_GET_BANDWIDTH, - .set = 0, - }, - [DTV_GET_MODULATION] = { - .name = "DTV_GET_MODULATION", - .cmd = DTV_GET_MODULATION, - .set = 0, - }, - [DTV_GET_INVERSION] = { - .name = "DTV_GET_INVERSION", - .cmd = DTV_GET_INVERSION, - .set = 0, - }, - [DTV_GET_DISEQC_SLAVE_REPLY] = { - .name = "DTV_GET_DISEQC_SLAVE_REPLY", - .cmd = DTV_GET_DISEQC_SLAVE_REPLY, + [DTV_DISEQC_SLAVE_REPLY] = { + .name = "DTV_DISEQC_SLAVE_REPLY", + .cmd = DTV_DISEQC_SLAVE_REPLY, .set = 0, .buffer = 1, }, - [DTV_GET_SYMBOL_RATE] = { - .name = "DTV_GET_SYMBOL_RATE", - .cmd = DTV_GET_SYMBOL_RATE, - .set = 0, - }, - [DTV_GET_INNER_FEC] = { - .name = "DTV_GET_INNER_FEC", - .cmd = DTV_GET_INNER_FEC, - .set = 0, - }, - [DTV_GET_VOLTAGE] = { - .name = "DTV_GET_VOLTAGE", - .cmd = DTV_GET_VOLTAGE, - .set = 0, - }, - [DTV_GET_TONE] = { - .name = "DTV_GET_TONE", - .cmd = DTV_GET_TONE, - .set = 0, - }, - [DTV_GET_PILOT] = { - .name = "DTV_GET_PILOT", - .cmd = DTV_GET_PILOT, - .set = 0, - }, - [DTV_GET_ROLLOFF] = { - .name = "DTV_GET_ROLLOFF", - .cmd = DTV_GET_ROLLOFF, - .set = 0, - }, - [DTV_GET_DELIVERY_SYSTEM] = { - .name = "DTV_GET_DELIVERY_SYSTEM", - .cmd = DTV_GET_DELIVERY_SYSTEM, - .set = 0, - }, - [DTV_GET_ISDB_SEGMENT_IDX] = { - .name = "DTV_GET_ISDB_SEGMENT_IDX", - .cmd = DTV_GET_ISDB_SEGMENT_IDX, - .set = 0, - }, - [DTV_GET_ISDB_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_SEGMENT_WIDTH, + [DTV_ISDB_LAYERA_FEC] = { + .name = "DTV_ISDB_LAYERA_FEC", + .cmd = DTV_ISDB_LAYERA_FEC, .set = 0, }, - [DTV_GET_ISDB_LAYERA_FEC] = { - .name = "DTV_GET_ISDB_LAYERA_FEC", - .cmd = DTV_GET_ISDB_LAYERA_FEC, + [DTV_ISDB_LAYERA_MODULATION] = { + .name = "DTV_ISDB_LAYERA_MODULATION", + .cmd = DTV_ISDB_LAYERA_MODULATION, .set = 0, }, - [DTV_GET_ISDB_LAYERA_MODULATION] = { - .name = "DTV_GET_ISDB_LAYERA_MODULATION", - .cmd = DTV_GET_ISDB_LAYERA_MODULATION, + [DTV_ISDB_LAYERA_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_LAYERA_SEGMENT_WIDTH", + .cmd = DTV_ISDB_LAYERA_SEGMENT_WIDTH, .set = 0, }, - [DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + [DTV_ISDB_LAYERB_FEC] = { + .name = "DTV_ISDB_LAYERB_FEC", + .cmd = DTV_ISDB_LAYERB_FEC, .set = 0, }, - [DTV_GET_ISDB_LAYERB_FEC] = { - .name = "DTV_GET_ISDB_LAYERB_FEC", - .cmd = DTV_GET_ISDB_LAYERB_FEC, + [DTV_ISDB_LAYERB_MODULATION] = { + .name = "DTV_ISDB_LAYERB_MODULATION", + .cmd = DTV_ISDB_LAYERB_MODULATION, .set = 0, }, - [DTV_GET_ISDB_LAYERB_MODULATION] = { - .name = "DTV_GET_ISDB_LAYERB_MODULATION", - .cmd = DTV_GET_ISDB_LAYERB_MODULATION, + [DTV_ISDB_LAYERB_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_LAYERB_SEGMENT_WIDTH", + .cmd = DTV_ISDB_LAYERB_SEGMENT_WIDTH, .set = 0, }, - [DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + [DTV_ISDB_LAYERC_FEC] = { + .name = "DTV_ISDB_LAYERC_FEC", + .cmd = DTV_ISDB_LAYERC_FEC, .set = 0, }, - [DTV_GET_ISDB_LAYERC_FEC] = { - .name = "DTV_GET_ISDB_LAYERC_FEC", - .cmd = DTV_GET_ISDB_LAYERC_FEC, + [DTV_ISDB_LAYERC_MODULATION] = { + .name = "DTV_ISDB_LAYERC_MODULATION", + .cmd = DTV_ISDB_LAYERC_MODULATION, .set = 0, }, - [DTV_GET_ISDB_LAYERC_MODULATION] = { - .name = "DTV_GET_ISDB_LAYERC_MODULATION", - .cmd = DTV_GET_ISDB_LAYERC_MODULATION, - .set = 0, - }, - [DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + [DTV_ISDB_LAYERC_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_LAYERC_SEGMENT_WIDTH", + .cmd = DTV_ISDB_LAYERC_SEGMENT_WIDTH, .set = 0, }, }; @@ -1160,7 +1095,92 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, +int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, + struct inode *inode, struct file *file) +{ + int r = 0; + + printk("%s()\n", __FUNCTION__); + + dtv_property_dump(tvp); + + switch(tvp->cmd) { + case DTV_FREQUENCY: + tvp->u.data = fe->dtv_property_cache.frequency; + break; + case DTV_MODULATION: + tvp->u.data = fe->dtv_property_cache.modulation; + break; + case DTV_BANDWIDTH: + tvp->u.data = fe->dtv_property_cache.bandwidth; + break; + case DTV_INVERSION: + tvp->u.data = fe->dtv_property_cache.inversion; + break; + case DTV_SYMBOL_RATE: + tvp->u.data = fe->dtv_property_cache.symbol_rate; + break; + case DTV_INNER_FEC: + tvp->u.data = fe->dtv_property_cache.fec_inner; + break; + case DTV_PILOT: + tvp->u.data = fe->dtv_property_cache.pilot; + break; + case DTV_ROLLOFF: + tvp->u.data = fe->dtv_property_cache.rolloff; + break; + case DTV_DELIVERY_SYSTEM: + tvp->u.data = fe->dtv_property_cache.delivery_system; + break; + + /* ISDB-T Support here */ + case DTV_ISDB_SEGMENT_IDX: + tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; + break; + case DTV_ISDB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_segment_width; + break; + case DTV_ISDB_LAYERA_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; + break; + case DTV_ISDB_LAYERA_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; + break; + case DTV_ISDB_LAYERA_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; + break; + case DTV_ISDB_LAYERB_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; + break; + case DTV_ISDB_LAYERB_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; + break; + case DTV_ISDB_LAYERB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; + break; + case DTV_ISDB_LAYERC_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; + break; + case DTV_ISDB_LAYERC_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; + break; + case DTV_ISDB_LAYERC_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; + break; + case DTV_VOLTAGE: + tvp->u.data = fe->dtv_property_cache.voltage; + break; + case DTV_TONE: + tvp->u.data = fe->dtv_property_cache.sectone; + break; + default: + r = -1; + } + + return r; +} + +int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, struct inode *inode, struct file *file) { int r = 0; @@ -1190,117 +1210,53 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, &fepriv->parameters); break; - case DTV_SET_FREQUENCY: + case DTV_FREQUENCY: fe->dtv_property_cache.frequency = tvp->u.data; break; - case DTV_GET_FREQUENCY: - tvp->u.data = fe->dtv_property_cache.frequency; - break; - case DTV_SET_MODULATION: + case DTV_MODULATION: fe->dtv_property_cache.modulation = tvp->u.data; break; - case DTV_GET_MODULATION: - tvp->u.data = fe->dtv_property_cache.modulation; - break; - case DTV_SET_BANDWIDTH: + case DTV_BANDWIDTH: fe->dtv_property_cache.bandwidth = tvp->u.data; break; - case DTV_GET_BANDWIDTH: - tvp->u.data = fe->dtv_property_cache.bandwidth; - break; - case DTV_SET_INVERSION: + case DTV_INVERSION: fe->dtv_property_cache.inversion = tvp->u.data; break; - case DTV_GET_INVERSION: - tvp->u.data = fe->dtv_property_cache.inversion; - break; - case DTV_SET_SYMBOL_RATE: + case DTV_SYMBOL_RATE: fe->dtv_property_cache.symbol_rate = tvp->u.data; break; - case DTV_GET_SYMBOL_RATE: - tvp->u.data = fe->dtv_property_cache.symbol_rate; - break; - case DTV_SET_INNER_FEC: + case DTV_INNER_FEC: fe->dtv_property_cache.fec_inner = tvp->u.data; break; - case DTV_GET_INNER_FEC: - tvp->u.data = fe->dtv_property_cache.fec_inner; - break; - case DTV_SET_PILOT: + case DTV_PILOT: fe->dtv_property_cache.pilot = tvp->u.data; break; - case DTV_GET_PILOT: - tvp->u.data = fe->dtv_property_cache.pilot; - break; - case DTV_SET_ROLLOFF: + case DTV_ROLLOFF: fe->dtv_property_cache.rolloff = tvp->u.data; break; - case DTV_GET_ROLLOFF: - tvp->u.data = fe->dtv_property_cache.rolloff; - break; - case DTV_SET_DELIVERY_SYSTEM: + case DTV_DELIVERY_SYSTEM: fe->dtv_property_cache.delivery_system = tvp->u.data; break; - case DTV_GET_DELIVERY_SYSTEM: - tvp->u.data = fe->dtv_property_cache.delivery_system; - break; /* ISDB-T Support here */ - case DTV_SET_ISDB_SEGMENT_IDX: + case DTV_ISDB_SEGMENT_IDX: fe->dtv_property_cache.isdb_segment_idx = tvp->u.data; break; - case DTV_GET_ISDB_SEGMENT_IDX: - tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; - break; - case DTV_SET_ISDB_SEGMENT_WIDTH: + case DTV_ISDB_SEGMENT_WIDTH: fe->dtv_property_cache.isdb_segment_width = tvp->u.data; break; - case DTV_GET_ISDB_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_segment_width; - break; - case DTV_GET_ISDB_LAYERA_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; - break; - case DTV_GET_ISDB_LAYERA_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; - break; - case DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; - break; - case DTV_GET_ISDB_LAYERB_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; - break; - case DTV_GET_ISDB_LAYERB_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; - break; - case DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; - break; - case DTV_GET_ISDB_LAYERC_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; - break; - case DTV_GET_ISDB_LAYERC_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; - break; - case DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; - break; - case DTV_SET_VOLTAGE: + case DTV_VOLTAGE: fe->dtv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, (void *)fe->dtv_property_cache.voltage); break; - case DTV_GET_VOLTAGE: - tvp->u.data = fe->dtv_property_cache.voltage; - break; - case DTV_SET_TONE: + case DTV_TONE: fe->dtv_property_cache.sectone = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, (void *)fe->dtv_property_cache.sectone); break; - case DTV_GET_TONE: - tvp->u.data = fe->dtv_property_cache.sectone; - break; + default: + r = -1; } return r; @@ -1375,13 +1331,50 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, } for (i = 0; i < tvps->num; i++) - dtv_property_process(fe, tvp + i, inode, file); + dtv_property_process_set(fe, tvp + i, inode, file); if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } err = 0; - } + } else + if(cmd == FE_GET_PROPERTY) { + printk("%s() FE_GET_PROPERTY\n", __FUNCTION__); + + tvps = (struct dtv_properties __user *)parg; + + printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num); + printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (tvps->num > DTV_IOCTL_MAX_MSGS) + return -EINVAL; + + tvp = (struct dtv_property *) kmalloc(tvps->num * + sizeof(struct dtv_property), GFP_KERNEL); + if (!tvp) { + err = -ENOMEM; + goto out; + } + + if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < tvps->num; i++) + dtv_property_process_get(fe, tvp + i, inode, file); + + if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + err = 0; + } else + err = -EOPNOTSUPP; + out: kfree(tvp); return err; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index e4f211735efb..05bdec9bc5aa 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -255,52 +255,39 @@ typedef enum dtv_cmd_types { DTV_TUNE, DTV_CLEAR, - DTV_SET_FREQUENCY, - DTV_SET_MODULATION, - DTV_SET_BANDWIDTH, - DTV_SET_INVERSION, - DTV_SET_DISEQC_MASTER, - DTV_SET_SYMBOL_RATE, - DTV_SET_INNER_FEC, - DTV_SET_VOLTAGE, - DTV_SET_TONE, - DTV_SET_PILOT, - DTV_SET_ROLLOFF, - - DTV_GET_FREQUENCY, - DTV_GET_MODULATION, - DTV_GET_BANDWIDTH, - DTV_GET_INVERSION, - DTV_GET_DISEQC_SLAVE_REPLY, - DTV_GET_SYMBOL_RATE, - DTV_GET_INNER_FEC, - DTV_GET_VOLTAGE, - DTV_GET_TONE, - DTV_GET_PILOT, - DTV_GET_ROLLOFF, + DTV_FREQUENCY, + DTV_MODULATION, + DTV_BANDWIDTH, + DTV_INVERSION, + DTV_DISEQC_MASTER, + DTV_SYMBOL_RATE, + DTV_INNER_FEC, + DTV_VOLTAGE, + DTV_TONE, + DTV_PILOT, + DTV_ROLLOFF, + + DTV_DISEQC_SLAVE_REPLY, /* Basic enumeration set for querying unlimited capabilities */ - DTV_GET_FE_CAPABILITY_COUNT, - DTV_GET_FE_CAPABILITY, + DTV_FE_CAPABILITY_COUNT, + DTV_FE_CAPABILITY, /* New commands are always appended */ - DTV_SET_DELIVERY_SYSTEM, - DTV_GET_DELIVERY_SYSTEM, + DTV_DELIVERY_SYSTEM, /* ISDB-T */ - DTV_SET_ISDB_SEGMENT_IDX, - DTV_GET_ISDB_SEGMENT_IDX, - DTV_SET_ISDB_SEGMENT_WIDTH, - DTV_GET_ISDB_SEGMENT_WIDTH, - DTV_GET_ISDB_LAYERA_FEC, - DTV_GET_ISDB_LAYERA_MODULATION, - DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH, - DTV_GET_ISDB_LAYERB_FEC, - DTV_GET_ISDB_LAYERB_MODULATION, - DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH, - DTV_GET_ISDB_LAYERC_FEC, - DTV_GET_ISDB_LAYERC_MODULATION, - DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + DTV_ISDB_SEGMENT_IDX, + DTV_ISDB_SEGMENT_WIDTH, + DTV_ISDB_LAYERA_FEC, + DTV_ISDB_LAYERA_MODULATION, + DTV_ISDB_LAYERA_SEGMENT_WIDTH, + DTV_ISDB_LAYERB_FEC, + DTV_ISDB_LAYERB_MODULATION, + DTV_ISDB_LAYERB_SEGMENT_WIDTH, + DTV_ISDB_LAYERC_FEC, + DTV_ISDB_LAYERC_MODULATION, + DTV_ISDB_LAYERC_SEGMENT_WIDTH, } dtv_cmd_types_t; -- cgit v1.2.3 From 75b7f9437b1cf63750bb58efaaeb6d72d04b3c7f Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 13 Sep 2008 16:56:34 -0300 Subject: V4L/DVB (9007): S2API: Changed bandwidth to be expressed in HZ Also added some compat code for the older API. Added more ISDB message/command suggestions, current not connected in dvb-core. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 33 ++++++++++++++++++++++--------- drivers/media/dvb/dvb-core/dvb_frontend.h | 2 +- include/linux/dvb/frontend.h | 27 ++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 6b914f9a03c5..7dffb48e55e5 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -773,9 +773,9 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_FREQUENCY, .set = 1, }, - [DTV_BANDWIDTH] = { - .name = "DTV_BANDWIDTH", - .cmd = DTV_BANDWIDTH, + [DTV_BANDWIDTH_HZ] = { + .name = "DTV_BANDWIDTH_HZ", + .cmd = DTV_BANDWIDTH_HZ, .set = 1, }, [DTV_MODULATION] = { @@ -954,7 +954,15 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame c->delivery_system = SYS_DVBC_ANNEX_AC; break; case FE_OFDM: - c->bandwidth = p->u.ofdm.bandwidth; + if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) + c->bandwidth_hz = 6000000; + else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) + c->bandwidth_hz = 7000000; + else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) + c->bandwidth_hz = 8000000; + else + /* Including BANDWIDTH_AUTO */ + c->bandwidth_hz = 0; c->code_rate_HP = p->u.ofdm.code_rate_HP; c->code_rate_LP = p->u.ofdm.code_rate_LP; c->modulation = p->u.ofdm.constellation; @@ -1003,7 +1011,14 @@ void dtv_property_legacy_params_sync(struct dvb_frontend *fe) break; case FE_OFDM: printk("%s() Preparing OFDM req\n", __FUNCTION__); - p->u.ofdm.bandwidth = c->bandwidth; + if (c->bandwidth_hz == 6000000) + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + else if (c->bandwidth_hz == 7000000) + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + else if (c->bandwidth_hz == 8000000) + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + else + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; p->u.ofdm.code_rate_HP = c->code_rate_HP; p->u.ofdm.code_rate_LP = c->code_rate_LP; p->u.ofdm.constellation = c->modulation; @@ -1118,8 +1133,8 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_MODULATION: tvp->u.data = fe->dtv_property_cache.modulation; break; - case DTV_BANDWIDTH: - tvp->u.data = fe->dtv_property_cache.bandwidth; + case DTV_BANDWIDTH_HZ: + tvp->u.data = fe->dtv_property_cache.bandwidth_hz; break; case DTV_INVERSION: tvp->u.data = fe->dtv_property_cache.inversion; @@ -1230,8 +1245,8 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_MODULATION: fe->dtv_property_cache.modulation = tvp->u.data; break; - case DTV_BANDWIDTH: - fe->dtv_property_cache.bandwidth = tvp->u.data; + case DTV_BANDWIDTH_HZ: + fe->dtv_property_cache.bandwidth_hz = tvp->u.data; break; case DTV_INVERSION: fe->dtv_property_cache.inversion = tvp->u.data; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 784e8fe1d3bd..2fa37f5a0d9a 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -198,7 +198,7 @@ struct dtv_frontend_properties { fe_spectral_inversion_t inversion; fe_code_rate_t fec_inner; fe_transmit_mode_t transmission_mode; - fe_bandwidth_t bandwidth; + u32 bandwidth_hz; /* 0 = AUTO */ fe_guard_interval_t guard_interval; fe_hierarchy_t hierarchy; u32 symbol_rate; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 05bdec9bc5aa..4f3fd641ccb8 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -257,7 +257,12 @@ typedef enum dtv_cmd_types { DTV_FREQUENCY, DTV_MODULATION, - DTV_BANDWIDTH, + + /* XXX PB: I would like to have field which describes the + * bandwidth of a channel in Hz or kHz - maybe we can remove the + * DTV_BANDWIDTH now and put a compat layer */ + DTV_BANDWIDTH_HZ, + DTV_INVERSION, DTV_DISEQC_MASTER, DTV_SYMBOL_RATE, @@ -276,18 +281,34 @@ typedef enum dtv_cmd_types { /* New commands are always appended */ DTV_DELIVERY_SYSTEM, - /* ISDB-T */ + /* ISDB */ + /* maybe a dup of DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID ??? */ DTV_ISDB_SEGMENT_IDX, - DTV_ISDB_SEGMENT_WIDTH, + DTV_ISDB_SEGMENT_WIDTH, /* 1, 3 or 13 ??? */ + + /* the central segment can be received independently or 1/3 seg in SB-mode */ + DTV_ISDB_PARTIAL_RECEPTION, + /* sound broadcasting is used 0 = 13segment, 1 = 1 or 3 see DTV_ISDB_PARTIAL_RECEPTION */ + DTV_ISDB_SOUND_BROADCASTING, + + /* only used in SB */ + /* determines the initial PRBS of the segment (to match with 13seg channel) */ + DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID, + DTV_ISDB_LAYERA_FEC, DTV_ISDB_LAYERA_MODULATION, DTV_ISDB_LAYERA_SEGMENT_WIDTH, + DTV_ISDB_LAYERA_TIME_INTERLEAVER, + DTV_ISDB_LAYERB_FEC, DTV_ISDB_LAYERB_MODULATION, DTV_ISDB_LAYERB_SEGMENT_WIDTH, + DTV_ISDB_LAYERB_TIME_INTERLEAVING, + DTV_ISDB_LAYERC_FEC, DTV_ISDB_LAYERC_MODULATION, DTV_ISDB_LAYERC_SEGMENT_WIDTH, + DTV_ISDB_LAYERC_TIME_INTERLEAVING, } dtv_cmd_types_t; -- cgit v1.2.3 From fc6e8c284bcb23ac7a576b7959803168398e390a Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Tue, 23 Sep 2008 22:14:33 -0300 Subject: V4L/DVB (9024): S2API: Cleanup dtv_property remove unwanted fields. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 4f3fd641ccb8..71f25f5bf37d 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -358,8 +358,6 @@ struct dtv_property { __u32 cmd; __u32 reserved[3]; union { - __s32 valuemin; - __s32 valuemax; __u32 data; struct { __u8 data[32]; -- cgit v1.2.3 From 8316568930074723bdc47d6777f822be0422a5b7 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Tue, 23 Sep 2008 22:21:26 -0300 Subject: V4L/DVB (9025): S2API: Deactivate the ISDB-T definitions We don't want to push the ISDB-T definitions into the kernel until we have a high level of confidence in the ISDB-T API. More testing is required before this code is released. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 99 ------------------------------- drivers/media/dvb/dvb-core/dvb_frontend.h | 13 ---- include/linux/dvb/frontend.h | 30 ---------- 3 files changed, 142 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 05d0b73cf9a3..7da25372078c 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -829,17 +829,6 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_DELIVERY_SYSTEM, .set = 1, }, - [DTV_ISDB_SEGMENT_IDX] = { - .name = "DTV_ISDB_SEGMENT_IDX", - .cmd = DTV_ISDB_SEGMENT_IDX, - .set = 1, - }, - [DTV_ISDB_SEGMENT_WIDTH] = { - .name = "DTV_ISDB_SEGMENT_WIDTH", - .cmd = DTV_ISDB_SEGMENT_WIDTH, - .set = 1, - }, - /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -847,51 +836,6 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, - [DTV_ISDB_LAYERA_FEC] = { - .name = "DTV_ISDB_LAYERA_FEC", - .cmd = DTV_ISDB_LAYERA_FEC, - .set = 0, - }, - [DTV_ISDB_LAYERA_MODULATION] = { - .name = "DTV_ISDB_LAYERA_MODULATION", - .cmd = DTV_ISDB_LAYERA_MODULATION, - .set = 0, - }, - [DTV_ISDB_LAYERA_SEGMENT_WIDTH] = { - .name = "DTV_ISDB_LAYERA_SEGMENT_WIDTH", - .cmd = DTV_ISDB_LAYERA_SEGMENT_WIDTH, - .set = 0, - }, - [DTV_ISDB_LAYERB_FEC] = { - .name = "DTV_ISDB_LAYERB_FEC", - .cmd = DTV_ISDB_LAYERB_FEC, - .set = 0, - }, - [DTV_ISDB_LAYERB_MODULATION] = { - .name = "DTV_ISDB_LAYERB_MODULATION", - .cmd = DTV_ISDB_LAYERB_MODULATION, - .set = 0, - }, - [DTV_ISDB_LAYERB_SEGMENT_WIDTH] = { - .name = "DTV_ISDB_LAYERB_SEGMENT_WIDTH", - .cmd = DTV_ISDB_LAYERB_SEGMENT_WIDTH, - .set = 0, - }, - [DTV_ISDB_LAYERC_FEC] = { - .name = "DTV_ISDB_LAYERC_FEC", - .cmd = DTV_ISDB_LAYERC_FEC, - .set = 0, - }, - [DTV_ISDB_LAYERC_MODULATION] = { - .name = "DTV_ISDB_LAYERC_MODULATION", - .cmd = DTV_ISDB_LAYERC_MODULATION, - .set = 0, - }, - [DTV_ISDB_LAYERC_SEGMENT_WIDTH] = { - .name = "DTV_ISDB_LAYERC_SEGMENT_WIDTH", - .cmd = DTV_ISDB_LAYERC_SEGMENT_WIDTH, - .set = 0, - }, }; void dtv_property_dump(struct dtv_property *tvp) @@ -1154,41 +1098,6 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_DELIVERY_SYSTEM: tvp->u.data = fe->dtv_property_cache.delivery_system; break; - - /* ISDB-T Support here */ - case DTV_ISDB_SEGMENT_IDX: - tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; - break; - case DTV_ISDB_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_segment_width; - break; - case DTV_ISDB_LAYERA_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; - break; - case DTV_ISDB_LAYERA_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; - break; - case DTV_ISDB_LAYERA_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; - break; - case DTV_ISDB_LAYERB_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; - break; - case DTV_ISDB_LAYERB_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; - break; - case DTV_ISDB_LAYERB_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; - break; - case DTV_ISDB_LAYERC_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; - break; - case DTV_ISDB_LAYERC_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; - break; - case DTV_ISDB_LAYERC_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; - break; case DTV_VOLTAGE: tvp->u.data = fe->dtv_property_cache.voltage; break; @@ -1266,14 +1175,6 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_DELIVERY_SYSTEM: fe->dtv_property_cache.delivery_system = tvp->u.data; break; - - /* ISDB-T Support here */ - case DTV_ISDB_SEGMENT_IDX: - fe->dtv_property_cache.isdb_segment_idx = tvp->u.data; - break; - case DTV_ISDB_SEGMENT_WIDTH: - fe->dtv_property_cache.isdb_segment_width = tvp->u.data; - break; case DTV_VOLTAGE: fe->dtv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 2fa37f5a0d9a..1c575402814d 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -209,19 +209,6 @@ struct dtv_frontend_properties { fe_rolloff_t rolloff; fe_delivery_system_t delivery_system; - - /* ISDB-T specifics */ - u32 isdb_segment_idx; - u32 isdb_segment_width; - fe_code_rate_t isdb_layera_fec; - fe_modulation_t isdb_layera_modulation; - u32 isdb_layera_segment_width; - fe_code_rate_t isdb_layerb_fec; - fe_modulation_t isdb_layerb_modulation; - u32 isdb_layerb_segment_width; - fe_code_rate_t isdb_layerc_fec; - fe_modulation_t isdb_layerc_modulation; - u32 isdb_layerc_segment_width; }; struct dvb_frontend { diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 71f25f5bf37d..c822bc156c48 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -280,36 +280,6 @@ typedef enum dtv_cmd_types { /* New commands are always appended */ DTV_DELIVERY_SYSTEM, - - /* ISDB */ - /* maybe a dup of DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID ??? */ - DTV_ISDB_SEGMENT_IDX, - DTV_ISDB_SEGMENT_WIDTH, /* 1, 3 or 13 ??? */ - - /* the central segment can be received independently or 1/3 seg in SB-mode */ - DTV_ISDB_PARTIAL_RECEPTION, - /* sound broadcasting is used 0 = 13segment, 1 = 1 or 3 see DTV_ISDB_PARTIAL_RECEPTION */ - DTV_ISDB_SOUND_BROADCASTING, - - /* only used in SB */ - /* determines the initial PRBS of the segment (to match with 13seg channel) */ - DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID, - - DTV_ISDB_LAYERA_FEC, - DTV_ISDB_LAYERA_MODULATION, - DTV_ISDB_LAYERA_SEGMENT_WIDTH, - DTV_ISDB_LAYERA_TIME_INTERLEAVER, - - DTV_ISDB_LAYERB_FEC, - DTV_ISDB_LAYERB_MODULATION, - DTV_ISDB_LAYERB_SEGMENT_WIDTH, - DTV_ISDB_LAYERB_TIME_INTERLEAVING, - - DTV_ISDB_LAYERC_FEC, - DTV_ISDB_LAYERC_MODULATION, - DTV_ISDB_LAYERC_SEGMENT_WIDTH, - DTV_ISDB_LAYERC_TIME_INTERLEAVING, - } dtv_cmd_types_t; typedef enum fe_pilot { -- cgit v1.2.3 From 459702bf98ae2bd20bad1e271c615aca4972c609 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 26 Sep 2008 00:04:52 -0300 Subject: V4L/DVB (9070): S2API: Removed the typedef for the commands, used defines instead. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 57 +++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index c822bc156c48..7c17c71edb59 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -246,41 +246,28 @@ struct dvb_frontend_event { struct dvb_frontend_parameters parameters; }; -/* TODO: Turn this into a series of defines, so future maintainers - * don't insert random new commands and break backwards - * binary compatability. - */ -typedef enum dtv_cmd_types { - DTV_UNDEFINED, - DTV_TUNE, - DTV_CLEAR, - - DTV_FREQUENCY, - DTV_MODULATION, - - /* XXX PB: I would like to have field which describes the - * bandwidth of a channel in Hz or kHz - maybe we can remove the - * DTV_BANDWIDTH now and put a compat layer */ - DTV_BANDWIDTH_HZ, - - DTV_INVERSION, - DTV_DISEQC_MASTER, - DTV_SYMBOL_RATE, - DTV_INNER_FEC, - DTV_VOLTAGE, - DTV_TONE, - DTV_PILOT, - DTV_ROLLOFF, - - DTV_DISEQC_SLAVE_REPLY, - - /* Basic enumeration set for querying unlimited capabilities */ - DTV_FE_CAPABILITY_COUNT, - DTV_FE_CAPABILITY, - - /* New commands are always appended */ - DTV_DELIVERY_SYSTEM, -} dtv_cmd_types_t; +/* S2API Commands */ +#define DTV_UNDEFINED 0 +#define DTV_TUNE 1 +#define DTV_CLEAR 2 +#define DTV_FREQUENCY 3 +#define DTV_MODULATION 4 +#define DTV_BANDWIDTH_HZ 5 +#define DTV_INVERSION 6 +#define DTV_DISEQC_MASTER 7 +#define DTV_SYMBOL_RATE 8 +#define DTV_INNER_FEC 9 +#define DTV_VOLTAGE 10 +#define DTV_TONE 11 +#define DTV_PILOT 12 +#define DTV_ROLLOFF 13 +#define DTV_DISEQC_SLAVE_REPLY 14 + +/* Basic enumeration set for querying unlimited capabilities */ +#define DTV_FE_CAPABILITY_COUNT 15 +#define DTV_FE_CAPABILITY 16 +#define DTV_DELIVERY_SYSTEM 17 + typedef enum fe_pilot { PILOT_ON, -- cgit v1.2.3 From d48cb402a1ba48c4ad4a36c3c561386027318459 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 26 Sep 2008 00:16:25 -0300 Subject: V4L/DVB (9071): S2API: Implement result codes for individual commands This allows application developers to determine which particular command in a sequence is invalid, or failing with error. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 12 ++++++++---- include/linux/dvb/frontend.h | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 7da25372078c..0ef9c2a2af87 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1262,8 +1262,10 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, goto out; } - for (i = 0; i < tvps->num; i++) - err |= dtv_property_process_set(fe, tvp + i, inode, file); + for (i = 0; i < tvps->num; i++) { + (tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file); + err |= (tvp + i)->result; + } if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); @@ -1295,8 +1297,10 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, goto out; } - for (i = 0; i < tvps->num; i++) - err |= dtv_property_process_get(fe, tvp + i, inode, file); + for (i = 0; i < tvps->num; i++) { + (tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file); + err |= (tvp + i)->result; + } if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { err = -EFAULT; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 7c17c71edb59..b561626fc8a0 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -323,6 +323,7 @@ struct dtv_property { void *reserved2; } buffer; } u; + int result; } __attribute__ ((packed)); /* No more than 16 properties during any given ioctl */ -- cgit v1.2.3 From eacf8d8d6bc6798f6870a2cf2c159bfcde3759ac Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 26 Sep 2008 00:29:49 -0300 Subject: V4L/DVB (9072): S2API: Add DTV_API_VERSION command This allows application developers to query the dvb-core API version dynamically, helping developers understand whether certain features will be available. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 9 +++++++++ include/linux/dvb/frontend.h | 1 + include/linux/dvb/version.h | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 0ef9c2a2af87..4c3f0d7e355c 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -40,6 +40,7 @@ #include "dvb_frontend.h" #include "dvbdev.h" +#include static int dvb_frontend_debug; static int dvb_shutdown_timeout; @@ -836,6 +837,11 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, + [DTV_API_VERSION] = { + .name = "DTV_API_VERSION", + .cmd = DTV_API_VERSION, + .set = 0, + }, }; void dtv_property_dump(struct dtv_property *tvp) @@ -1104,6 +1110,9 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_TONE: tvp->u.data = fe->dtv_property_cache.sectone; break; + case DTV_API_VERSION: + tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR; + break; default: r = -1; } diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index b561626fc8a0..eb98f8c37cad 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -268,6 +268,7 @@ struct dvb_frontend_event { #define DTV_FE_CAPABILITY 16 #define DTV_DELIVERY_SYSTEM 17 +#define DTV_API_VERSION 35 typedef enum fe_pilot { PILOT_ON, diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h index 126e0c26cb09..25b823b81734 100644 --- a/include/linux/dvb/version.h +++ b/include/linux/dvb/version.h @@ -23,7 +23,7 @@ #ifndef _DVBVERSION_H_ #define _DVBVERSION_H_ -#define DVB_API_VERSION 3 -#define DVB_API_VERSION_MINOR 2 +#define DVB_API_VERSION 5 +#define DVB_API_VERSION_MINOR 0 #endif /*_DVBVERSION_H_*/ -- cgit v1.2.3 From 46eaa6702016e3ac9a188172a2c309d6ca1be1cd Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 12 Oct 2008 15:06:29 +0200 Subject: x86: memory corruption check - cleanup Move the prototypes from the generic kernel.h header to the more appropriate include/asm-x86/bios_ebda.h header file. Also, remove the check from the power management code - this is a pure x86 matter for now. Signed-off-by: Ingo Molnar --- arch/x86/mm/init_32.c | 1 + arch/x86/mm/init_64.c | 1 + drivers/base/power/main.c | 1 - include/asm-x86/bios_ebda.h | 17 +++++++++++++++++ include/linux/kernel.h | 17 ----------------- 5 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 7e05462ffb11..bbe044dbe014 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index d84d3e91d348..3e10054c5731 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index bf6d3554e506..273a944d4040 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -254,7 +254,6 @@ static char *pm_verb(int event) static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) { - check_for_bios_corruption(); dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? ", may wakeup" : ""); diff --git a/include/asm-x86/bios_ebda.h b/include/asm-x86/bios_ebda.h index ec42ed874591..79b4b88505d7 100644 --- a/include/asm-x86/bios_ebda.h +++ b/include/asm-x86/bios_ebda.h @@ -16,4 +16,21 @@ static inline unsigned int get_bios_ebda(void) void reserve_ebda_region(void); +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION +/* + * This is obviously not a great place for this, but we want to be + * able to scatter it around anywhere in the kernel. + */ +void check_for_bios_corruption(void); +void start_periodic_check_for_corruption(void); +#else +static inline void check_for_bios_corruption(void) +{ +} + +static inline void start_periodic_check_for_corruption(void) +{ +} +#endif + #endif /* ASM_X86__BIOS_EBDA_H */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 50873b211788..2651f805ba6d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -240,23 +240,6 @@ extern const char *print_tainted(void); extern void add_taint(unsigned); extern int root_mountflags; -#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION -/* - * This is obviously not a great place for this, but we want to be - * able to scatter it around anywhere in the kernel. - */ -void check_for_bios_corruption(void); -void start_periodic_check_for_corruption(void); -#else -static inline void check_for_bios_corruption(void) -{ -} - -static inline void start_periodic_check_for_corruption(void) -{ -} -#endif - /* Values used for system_state */ extern enum system_states { SYSTEM_BOOTING, -- cgit v1.2.3 From 0dab9cfa17179d1f5b067a32a3bca06cd31a3149 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 12 Oct 2008 07:10:50 +0300 Subject: add key_revoke() dummy for KEYS=n This fixes the following build error with CONFIG_KEYS=n, caused by commit dfd15c46a6c2cafb006183c0c14f07e59eee4ac0 ("cifs: explicitly revoke SPNEGO key after session setup"): CC [M] fs/cifs/sess.o fs/cifs/sess.c: In function 'CIFS_SessSetup': fs/cifs/sess.c:628: error: implicit declaration of function 'key_revoke' make[3]: *** [fs/cifs/sess.o] Error 1 Signed-off-by: Adrian Bunk Acked-by: Jeff Layton Signed-off-by: Linus Torvalds --- include/linux/key.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/key.h b/include/linux/key.h index c45c962d1cc5..1b70e35a71e3 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -299,6 +299,7 @@ extern void key_init(void); #define key_validate(k) 0 #define key_serial(k) 0 #define key_get(k) ({ NULL; }) +#define key_revoke(k) do { } while(0) #define key_put(k) do { } while(0) #define key_ref_put(k) do { } while(0) #define make_key_ref(k, p) ({ NULL; }) -- cgit v1.2.3 From 92562927826fceb2f8e69c89e28161b8c1e0b125 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 7 Oct 2008 14:00:12 -0400 Subject: integrity: special fs magic Discussion on the mailing list questioned the use of these magic values in userspace, concluding these values are already exported to userspace via statfs and their correct/incorrect usage is left up to the userspace application. - Move special fs magic number definitions to magic.h - Add magic.h include Signed-off-by: Mimi Zohar Reviewed-by: James Morris Signed-off-by: James Morris --- fs/debugfs/inode.c | 3 +-- include/linux/magic.h | 4 ++++ mm/shmem.c | 4 +--- security/inode.c | 3 +-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 08e28c9bb416..3dbe2169cf36 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -26,8 +26,7 @@ #include #include #include - -#define DEBUGFS_MAGIC 0x64626720 +#include static struct vfsmount *debugfs_mount; static int debugfs_mount_count; diff --git a/include/linux/magic.h b/include/linux/magic.h index 1fa0c2ce4dec..f7f3fdddbef0 100644 --- a/include/linux/magic.h +++ b/include/linux/magic.h @@ -6,6 +6,10 @@ #define AFS_SUPER_MAGIC 0x5346414F #define AUTOFS_SUPER_MAGIC 0x0187 #define CODA_SUPER_MAGIC 0x73757245 +#define DEBUGFS_MAGIC 0x64626720 +#define SYSFS_MAGIC 0x62656572 +#define SECURITYFS_MAGIC 0x73636673 +#define TMPFS_MAGIC 0x01021994 #define EFS_SUPER_MAGIC 0x414A53 #define EXT2_SUPER_MAGIC 0xEF53 #define EXT3_SUPER_MAGIC 0xEF53 diff --git a/mm/shmem.c b/mm/shmem.c index 04fb4f1ab88e..bf66d0191baf 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -50,14 +50,12 @@ #include #include #include +#include #include #include #include -/* This magic number is used in glibc for posix shared memory */ -#define TMPFS_MAGIC 0x01021994 - #define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long)) #define ENTRIES_PER_PAGEPAGE (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE) #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) diff --git a/security/inode.c b/security/inode.c index ca4958ebad8d..efea5a605466 100644 --- a/security/inode.c +++ b/security/inode.c @@ -20,8 +20,7 @@ #include #include #include - -#define SECURITYFS_MAGIC 0x73636673 +#include static struct vfsmount *mount; static int mount_count; -- cgit v1.2.3 From 82d7669dc3c0e795c24111fed88e9d5d70f209e0 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 6 Oct 2008 20:44:04 -0300 Subject: V4L/DVB (9173): S2API: Remove the hardcoded command limit during validation This means that when developers add new commands then they'll be see the DTV_MAX_COMMAND define and will be more likely to modify it, without having to modify the command validation code. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 3 +-- include/linux/dvb/frontend.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index ff8cda0ac333..7fe9b3fb1e34 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -848,8 +848,7 @@ void dtv_property_dump(struct dtv_property *tvp) { int i; - if( (tvp->cmd <= 0 || tvp->cmd > DTV_DELIVERY_SYSTEM) && - tvp->cmd != DTV_API_VERSION) { + if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) { printk("%s: tvp.cmd = 0x%08x (undefined/unknown/invalid)\n", __func__, tvp->cmd); return; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index eb98f8c37cad..1cfcd1a86e8a 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -270,6 +270,8 @@ struct dvb_frontend_event { #define DTV_API_VERSION 35 +#define DTV_MAX_COMMAND DTV_API_VERSION + typedef enum fe_pilot { PILOT_ON, PILOT_OFF, -- cgit v1.2.3 From 0a6393ae21d58e85882185ce1e6b0fe28ff2dfa6 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 6 Oct 2008 21:06:48 -0300 Subject: V4L/DVB (9177): S2API: Change _8PSK / _16APSK to PSK_8 and APSK_16 ... and cleanup any drivers using them. I've also removed NBC_QPSK and modified the cx24116 driver to check the delivery_type also, removing some excess namespace baggage. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 6 ++-- drivers/media/dvb/frontends/cx24116.c | 51 ++++++++++++++++--------------- include/linux/dvb/frontend.h | 5 ++- 3 files changed, 31 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 7fe9b3fb1e34..7b4b1a5ac427 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1011,9 +1011,9 @@ void dtv_property_adv_params_sync(struct dvb_frontend *fe) p->inversion = c->inversion; switch(c->modulation) { - case _8PSK: - case _16APSK: - case NBC_QPSK: + case PSK_8: + case APSK_16: + case QPSK: p->u.qpsk.symbol_rate = c->symbol_rate; p->u.qpsk.fec_inner = c->fec_inner; break; diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c index 808ef2995d80..30f82cb0928b 100644 --- a/drivers/media/dvb/frontends/cx24116.c +++ b/drivers/media/dvb/frontends/cx24116.c @@ -344,6 +344,7 @@ static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_invers * a scheme are support. Especially, no auto detect when in S2 mode. */ struct cx24116_modfec { + fe_delivery_system_t delivery_system; fe_modulation_t modulation; fe_code_rate_t fec; u8 mask; /* In DVBS mode this is used to autodetect */ @@ -352,32 +353,32 @@ struct cx24116_modfec { /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ /*mod fec mask val */ - { QPSK, FEC_NONE, 0xfe, 0x30 }, - { QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ - { QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ - { QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ - { QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ - { QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ - { QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ - { QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ - { QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ - { QPSK, FEC_AUTO, 0xfe, 0x30 }, + { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, + { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ + { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ + { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ + { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ + { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ + { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ + { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ + { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ + { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, /* NBC-QPSK */ - { NBC_QPSK, FEC_1_2, 0x00, 0x04 }, - { NBC_QPSK, FEC_3_5, 0x00, 0x05 }, - { NBC_QPSK, FEC_2_3, 0x00, 0x06 }, - { NBC_QPSK, FEC_3_4, 0x00, 0x07 }, - { NBC_QPSK, FEC_4_5, 0x00, 0x08 }, - { NBC_QPSK, FEC_5_6, 0x00, 0x09 }, - { NBC_QPSK, FEC_8_9, 0x00, 0x0a }, - { NBC_QPSK, FEC_9_10, 0x00, 0x0b }, + { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, /* 8PSK */ - { _8PSK, FEC_3_5, 0x00, 0x0c }, - { _8PSK, FEC_2_3, 0x00, 0x0d }, - { _8PSK, FEC_3_4, 0x00, 0x0e }, - { _8PSK, FEC_5_6, 0x00, 0x0f }, - { _8PSK, FEC_8_9, 0x00, 0x10 }, - { _8PSK, FEC_9_10, 0x00, 0x11 }, + { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, /* * `val' can be found in the FECSTATUS register when tuning. * FECSTATUS will give the actual FEC in use if tuning was successful. @@ -1158,7 +1159,7 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, * but not hardware auto detection */ - if(c->modulation != _8PSK && c->modulation != NBC_QPSK) { + if(c->modulation != PSK_8 && c->modulation != QPSK) { dprintk("%s: unsupported modulation selected (%d)\n", __func__, c->modulation); return -EOPNOTSUPP; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 1cfcd1a86e8a..291dd8e5e58d 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -164,9 +164,8 @@ typedef enum fe_modulation { QAM_AUTO, VSB_8, VSB_16, - _8PSK, - _16APSK, - NBC_QPSK, + PSK_8, + APSK_16, DQPSK, } fe_modulation_t; -- cgit v1.2.3 From cc7d705e7a2c28ae1bd8b2fd29524277262967ab Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 6 Oct 2008 21:31:48 -0300 Subject: V4L/DVB (9179): S2API: frontend.h cleanup Reviewing the code briefly and saw this. You can't change more than DTV_IOCTL_MAX_MSGS at once, not 16. Signed-off-by: Brandon Philips Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 291dd8e5e58d..3d4fab495fb2 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -328,14 +328,14 @@ struct dtv_property { int result; } __attribute__ ((packed)); -/* No more than 16 properties during any given ioctl */ +/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */ +#define DTV_IOCTL_MAX_MSGS 64 + struct dtv_properties { __u32 num; struct dtv_property *props; }; -#define DTV_IOCTL_MAX_MSGS 64 - #define FE_SET_PROPERTY _IOW('o', 82, struct dtv_properties) #define FE_GET_PROPERTY _IOR('o', 83, struct dtv_properties) -- cgit v1.2.3 From a4de91be46b73ec6743b9d76155550e49507723c Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 6 Oct 2008 21:55:46 -0300 Subject: V4L/DVB (9180): S2API: Added support for DTV_CODE_RATE_HP/LP Reports from users that using the new API for tuning DTV was failing, and the cache was missing some essential items. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 32 +++++++++++++++++++++++++++++++ include/linux/dvb/frontend.h | 5 ++++- 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 7b4b1a5ac427..104f40b7171d 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -830,6 +830,16 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_DELIVERY_SYSTEM, .set = 1, }, + [DTV_CODE_RATE_HP] = { + .name = "DTV_CODE_RATE_HP", + .cmd = DTV_CODE_RATE_HP, + .set = 1, + }, + [DTV_CODE_RATE_LP] = { + .name = "DTV_CODE_RATE_LP", + .cmd = DTV_CODE_RATE_LP, + .set = 1, + }, /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -842,6 +852,16 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_API_VERSION, .set = 0, }, + [DTV_CODE_RATE_HP] = { + .name = "DTV_CODE_RATE_HP", + .cmd = DTV_CODE_RATE_HP, + .set = 0, + }, + [DTV_CODE_RATE_LP] = { + .name = "DTV_CODE_RATE_LP", + .cmd = DTV_CODE_RATE_LP, + .set = 0, + }, }; void dtv_property_dump(struct dtv_property *tvp) @@ -1121,6 +1141,12 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_API_VERSION: tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR; break; + case DTV_CODE_RATE_HP: + tvp->u.data = fe->dtv_property_cache.code_rate_HP; + break; + case DTV_CODE_RATE_LP: + tvp->u.data = fe->dtv_property_cache.code_rate_LP; + break; default: r = -1; } @@ -1202,6 +1228,12 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, (void *)fe->dtv_property_cache.sectone); break; + case DTV_CODE_RATE_HP: + fe->dtv_property_cache.code_rate_HP = tvp->u.data; + break; + case DTV_CODE_RATE_LP: + fe->dtv_property_cache.code_rate_LP = tvp->u.data; + break; default: r = -1; } diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 3d4fab495fb2..6675edfab3fc 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -268,8 +268,11 @@ struct dvb_frontend_event { #define DTV_DELIVERY_SYSTEM 17 #define DTV_API_VERSION 35 +#define DTV_API_VERSION 35 +#define DTV_CODE_RATE_HP 36 +#define DTV_CODE_RATE_LP 37 -#define DTV_MAX_COMMAND DTV_API_VERSION +#define DTV_MAX_COMMAND DTV_CODE_RATE_LP typedef enum fe_pilot { PILOT_ON, -- cgit v1.2.3 From b87625f0ccbbc67efba356e73502fa9bbb784b1c Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 6 Oct 2008 21:56:59 -0300 Subject: V4L/DVB (9181): S2API: Add support fot DTV_GUARD_INTERVAL and DTV_TRANSMISSION_MODE Tuning DVB-T via the S2API was failing, missing some essential items. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 32 +++++++++++++++++++++++++++++++ include/linux/dvb/frontend.h | 4 +++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 104f40b7171d..0ddc2f4ecd4a 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -840,6 +840,16 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_CODE_RATE_LP, .set = 1, }, + [DTV_GUARD_INTERVAL] = { + .name = "DTV_GUARD_INTERVAL", + .cmd = DTV_GUARD_INTERVAL, + .set = 1, + }, + [DTV_TRANSMISSION_MODE] = { + .name = "DTV_TRANSMISSION_MODE", + .cmd = DTV_TRANSMISSION_MODE, + .set = 1, + }, /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -862,6 +872,16 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_CODE_RATE_LP, .set = 0, }, + [DTV_GUARD_INTERVAL] = { + .name = "DTV_GUARD_INTERVAL", + .cmd = DTV_GUARD_INTERVAL, + .set = 0, + }, + [DTV_TRANSMISSION_MODE] = { + .name = "DTV_TRANSMISSION_MODE", + .cmd = DTV_TRANSMISSION_MODE, + .set = 0, + }, }; void dtv_property_dump(struct dtv_property *tvp) @@ -1147,6 +1167,12 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_CODE_RATE_LP: tvp->u.data = fe->dtv_property_cache.code_rate_LP; break; + case DTV_GUARD_INTERVAL: + tvp->u.data = fe->dtv_property_cache.guard_interval; + break; + case DTV_TRANSMISSION_MODE: + tvp->u.data = fe->dtv_property_cache.transmission_mode; + break; default: r = -1; } @@ -1234,6 +1260,12 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_CODE_RATE_LP: fe->dtv_property_cache.code_rate_LP = tvp->u.data; break; + case DTV_GUARD_INTERVAL: + fe->dtv_property_cache.guard_interval = tvp->u.data; + break; + case DTV_TRANSMISSION_MODE: + fe->dtv_property_cache.transmission_mode = tvp->u.data; + break; default: r = -1; } diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 6675edfab3fc..5578fba208f4 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -271,8 +271,10 @@ struct dvb_frontend_event { #define DTV_API_VERSION 35 #define DTV_CODE_RATE_HP 36 #define DTV_CODE_RATE_LP 37 +#define DTV_GUARD_INTERVAL 38 +#define DTV_TRANSMISSION_MODE 39 -#define DTV_MAX_COMMAND DTV_CODE_RATE_LP +#define DTV_MAX_COMMAND DTV_TRANSMISSION_MODE typedef enum fe_pilot { PILOT_ON, -- cgit v1.2.3 From ef526f4246f566370218bb6e639f7549244ca5a2 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 6 Oct 2008 22:01:47 -0300 Subject: V4L/DVB (9182): S2API: Added support for DTV_HIERARCHY A user tuning DVB-T via the S2API reports that this was not implemented, and his tuning was failing. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 16 ++++++++++++++++ include/linux/dvb/frontend.h | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 0ddc2f4ecd4a..bd59c9514ab5 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -830,6 +830,11 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_DELIVERY_SYSTEM, .set = 1, }, + [DTV_HIERARCHY] = { + .name = "DTV_HIERARCHY", + .cmd = DTV_HIERARCHY, + .set = 1, + }, [DTV_CODE_RATE_HP] = { .name = "DTV_CODE_RATE_HP", .cmd = DTV_CODE_RATE_HP, @@ -882,6 +887,11 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_TRANSMISSION_MODE, .set = 0, }, + [DTV_HIERARCHY] = { + .name = "DTV_HIERARCHY", + .cmd = DTV_HIERARCHY, + .set = 0, + }, }; void dtv_property_dump(struct dtv_property *tvp) @@ -1173,6 +1183,9 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_TRANSMISSION_MODE: tvp->u.data = fe->dtv_property_cache.transmission_mode; break; + case DTV_HIERARCHY: + tvp->u.data = fe->dtv_property_cache.hierarchy; + break; default: r = -1; } @@ -1266,6 +1279,9 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_TRANSMISSION_MODE: fe->dtv_property_cache.transmission_mode = tvp->u.data; break; + case DTV_HIERARCHY: + fe->dtv_property_cache.hierarchy = tvp->u.data; + break; default: r = -1; } diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 5578fba208f4..e185627219e3 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -273,8 +273,9 @@ struct dvb_frontend_event { #define DTV_CODE_RATE_LP 37 #define DTV_GUARD_INTERVAL 38 #define DTV_TRANSMISSION_MODE 39 +#define DTV_HIERARCHY 40 -#define DTV_MAX_COMMAND DTV_TRANSMISSION_MODE +#define DTV_MAX_COMMAND DTV_HIERARCHY typedef enum fe_pilot { PILOT_ON, -- cgit v1.2.3 From a52f68c648585ff615175269d8f6cbcbb08d7f4d Mon Sep 17 00:00:00 2001 From: Darron Broad Date: Tue, 7 Oct 2008 17:30:45 -0300 Subject: V4L/DVB (9185): S2API: Ensure we have a reasonable ROLLOFF default Non-initialised cache values get a reasonble default. Signed-off-by: Darron Broad Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index e185627219e3..6e4ace270276 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -284,9 +284,9 @@ typedef enum fe_pilot { } fe_pilot_t; typedef enum fe_rolloff { + ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */ ROLLOFF_20, ROLLOFF_25, - ROLLOFF_35, ROLLOFF_AUTO, } fe_rolloff_t; -- cgit v1.2.3 From d945b697d0eea5a811ec299c5f1a25889bb0242b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 16 Sep 2008 16:23:28 -0700 Subject: Automatic MODULE_ALIAS() for DMI match tables. This makes modpost handle MODULE_DEVICE_TABLE(dmi, xxxx). I had to change the string pointers in the match table to char arrays, and picked a size of 79 bytes almost at random -- do we need to make it bigger than that? I was a bit concerned about the 'bloat' this introduces into the match tables, but they should all be __initdata so it shouldn't matter too much. (Actually, modpost does go through the relocations and look at most of them; it wouldn't be impossible to make it handle string pointers -- but doesn't seem to be worth the effort, since they're __initdata). Signed-off-by: David Woodhouse --- include/linux/dmi.h | 41 ++--------------------------- include/linux/mod_devicetable.h | 47 +++++++++++++++++++++++++++++++++ scripts/mod/file2alias.c | 57 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 2a063b64133f..e5084eb5943a 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -2,29 +2,9 @@ #define __DMI_H__ #include +#include -enum dmi_field { - DMI_NONE, - DMI_BIOS_VENDOR, - DMI_BIOS_VERSION, - DMI_BIOS_DATE, - DMI_SYS_VENDOR, - DMI_PRODUCT_NAME, - DMI_PRODUCT_VERSION, - DMI_PRODUCT_SERIAL, - DMI_PRODUCT_UUID, - DMI_BOARD_VENDOR, - DMI_BOARD_NAME, - DMI_BOARD_VERSION, - DMI_BOARD_SERIAL, - DMI_BOARD_ASSET_TAG, - DMI_CHASSIS_VENDOR, - DMI_CHASSIS_TYPE, - DMI_CHASSIS_VERSION, - DMI_CHASSIS_SERIAL, - DMI_CHASSIS_ASSET_TAG, - DMI_STRING_MAX, -}; +/* enum dmi_field is in mod_devicetable.h */ enum dmi_device_type { DMI_DEV_TYPE_ANY = 0, @@ -48,23 +28,6 @@ struct dmi_header { u16 handle; }; -/* - * DMI callbacks for problem boards - */ -struct dmi_strmatch { - u8 slot; - char *substr; -}; - -struct dmi_system_id { - int (*callback)(const struct dmi_system_id *); - const char *ident; - struct dmi_strmatch matches[4]; - void *driver_data; -}; - -#define DMI_MATCH(a, b) { a, b } - struct dmi_device { struct list_head list; int type; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index c4db5827963d..3481a7d5bc0a 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -388,5 +388,52 @@ struct i2c_device_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; +/* dmi */ +enum dmi_field { + DMI_NONE, + DMI_BIOS_VENDOR, + DMI_BIOS_VERSION, + DMI_BIOS_DATE, + DMI_SYS_VENDOR, + DMI_PRODUCT_NAME, + DMI_PRODUCT_VERSION, + DMI_PRODUCT_SERIAL, + DMI_PRODUCT_UUID, + DMI_BOARD_VENDOR, + DMI_BOARD_NAME, + DMI_BOARD_VERSION, + DMI_BOARD_SERIAL, + DMI_BOARD_ASSET_TAG, + DMI_CHASSIS_VENDOR, + DMI_CHASSIS_TYPE, + DMI_CHASSIS_VERSION, + DMI_CHASSIS_SERIAL, + DMI_CHASSIS_ASSET_TAG, + DMI_STRING_MAX, +}; + +struct dmi_strmatch { + unsigned char slot; + char substr[79]; +}; + +#ifndef __KERNEL__ +struct dmi_system_id { + kernel_ulong_t callback; + kernel_ulong_t ident; + struct dmi_strmatch matches[4]; + kernel_ulong_t driver_data + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; +#else +struct dmi_system_id { + int (*callback)(const struct dmi_system_id *); + const char *ident; + struct dmi_strmatch matches[4]; + void *driver_data; +}; +#endif + +#define DMI_MATCH(a, b) { a, b } #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 4c9890ec2528..473f94e56ead 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -629,6 +629,59 @@ static int do_i2c_entry(const char *filename, struct i2c_device_id *id, return 1; } +static const struct dmifield { + const char *prefix; + int field; +} dmi_fields[] = { + { "bvn", DMI_BIOS_VENDOR }, + { "bvr", DMI_BIOS_VERSION }, + { "bd", DMI_BIOS_DATE }, + { "svn", DMI_SYS_VENDOR }, + { "pn", DMI_PRODUCT_NAME }, + { "pvr", DMI_PRODUCT_VERSION }, + { "rvn", DMI_BOARD_VENDOR }, + { "rn", DMI_BOARD_NAME }, + { "rvr", DMI_BOARD_VERSION }, + { "cvn", DMI_CHASSIS_VENDOR }, + { "ct", DMI_CHASSIS_TYPE }, + { "cvr", DMI_CHASSIS_VERSION }, + { NULL, DMI_NONE } +}; + +static void dmi_ascii_filter(char *d, const char *s) +{ + /* Filter out characters we don't want to see in the modalias string */ + for (; *s; s++) + if (*s > ' ' && *s < 127 && *s != ':') + *(d++) = *s; + + *d = 0; +} + + +static int do_dmi_entry(const char *filename, struct dmi_system_id *id, + char *alias) +{ + int i, j; + + sprintf(alias, "dmi*"); + + for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) { + for (j = 0; j < 4; j++) { + if (id->matches[j].slot && + id->matches[j].slot == dmi_fields[i].field) { + sprintf(alias + strlen(alias), ":%s*", + dmi_fields[i].prefix); + dmi_ascii_filter(alias + strlen(alias), + id->matches[j].substr); + strcat(alias, "*"); + } + } + } + + strcat(alias, ":"); + return 1; +} /* Ignore any prefix, eg. some architectures prepend _ */ static inline int sym_is(const char *symbol, const char *name) { @@ -760,6 +813,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, do_table(symval, sym->st_size, sizeof(struct i2c_device_id), "i2c", do_i2c_entry, mod); + else if (sym_is(symname, "__mod_dmi_device_table")) + do_table(symval, sym->st_size, + sizeof(struct dmi_system_id), "dmi", + do_dmi_entry, mod); free(zeros); } -- cgit v1.2.3 From 0c8946d97ae7d2d6691f8290a10faa63453b63f8 Mon Sep 17 00:00:00 2001 From: David Miller Date: Mon, 13 Oct 2008 10:35:23 +0100 Subject: serial: Make uart_port's ioport "unsigned long". Otherwise the top 32-bits of the resource value get chopped off on 64-bit systems, and the resulting I/O accesses go to random places. Thanks to testing and debugging by Josip Rodin, which helped track this down. Signed-off-by: David S. Miller Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- include/linux/serial_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3b2f6c04855e..e27f216361fc 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -241,7 +241,7 @@ typedef unsigned int __bitwise__ upf_t; struct uart_port { spinlock_t lock; /* port lock */ - unsigned int iobase; /* in/out[bwl] */ + unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ -- cgit v1.2.3 From e04957365b21066285557e42ffe16d8330d46c02 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:36:58 +0100 Subject: tty: split the buffering from tty_io The two are basically independent chunks of code so lets split them up for readability and sanity. It also makes the API boundaries much clearer. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/Makefile | 2 +- drivers/char/tty_buffer.c | 511 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/char/tty_io.c | 502 --------------------------------------------- include/linux/tty.h | 3 + 4 files changed, 515 insertions(+), 503 deletions(-) create mode 100644 drivers/char/tty_buffer.c (limited to 'include/linux') diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 6850f6da7576..77ea41b88ea8 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c new file mode 100644 index 000000000000..810ee25d66a4 --- /dev/null +++ b/drivers/char/tty_buffer.c @@ -0,0 +1,511 @@ +/* + * Tty buffer allocation management + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * tty_buffer_free_all - free buffers used by a tty + * @tty: tty to free from + * + * Remove all the buffers pending on a tty whether queued with data + * or in the free ring. Must be called when the tty is no longer in use + * + * Locking: none + */ + +void tty_buffer_free_all(struct tty_struct *tty) +{ + struct tty_buffer *thead; + while ((thead = tty->buf.head) != NULL) { + tty->buf.head = thead->next; + kfree(thead); + } + while ((thead = tty->buf.free) != NULL) { + tty->buf.free = thead->next; + kfree(thead); + } + tty->buf.tail = NULL; + tty->buf.memory_used = 0; +} + +/** + * tty_buffer_alloc - allocate a tty buffer + * @tty: tty device + * @size: desired size (characters) + * + * Allocate a new tty buffer to hold the desired number of characters. + * Return NULL if out of memory or the allocation would exceed the + * per device queue + * + * Locking: Caller must hold tty->buf.lock + */ + +static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +{ + struct tty_buffer *p; + + if (tty->buf.memory_used + size > 65536) + return NULL; + p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); + if (p == NULL) + return NULL; + p->used = 0; + p->size = size; + p->next = NULL; + p->commit = 0; + p->read = 0; + p->char_buf_ptr = (char *)(p->data); + p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; + tty->buf.memory_used += size; + return p; +} + +/** + * tty_buffer_free - free a tty buffer + * @tty: tty owning the buffer + * @b: the buffer to free + * + * Free a tty buffer, or add it to the free list according to our + * internal strategy + * + * Locking: Caller must hold tty->buf.lock + */ + +static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +{ + /* Dumb strategy for now - should keep some stats */ + tty->buf.memory_used -= b->size; + WARN_ON(tty->buf.memory_used < 0); + + if (b->size >= 512) + kfree(b); + else { + b->next = tty->buf.free; + tty->buf.free = b; + } +} + +/** + * __tty_buffer_flush - flush full tty buffers + * @tty: tty to flush + * + * flush all the buffers containing receive data. Caller must + * hold the buffer lock and must have ensured no parallel flush to + * ldisc is running. + * + * Locking: Caller must hold tty->buf.lock + */ + +static void __tty_buffer_flush(struct tty_struct *tty) +{ + struct tty_buffer *thead; + + while ((thead = tty->buf.head) != NULL) { + tty->buf.head = thead->next; + tty_buffer_free(tty, thead); + } + tty->buf.tail = NULL; +} + +/** + * tty_buffer_flush - flush full tty buffers + * @tty: tty to flush + * + * flush all the buffers containing receive data. If the buffer is + * being processed by flush_to_ldisc then we defer the processing + * to that function + * + * Locking: none + */ + +void tty_buffer_flush(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + + /* If the data is being pushed to the tty layer then we can't + process it here. Instead set a flag and the flush_to_ldisc + path will process the flush request before it exits */ + if (test_bit(TTY_FLUSHING, &tty->flags)) { + set_bit(TTY_FLUSHPENDING, &tty->flags); + spin_unlock_irqrestore(&tty->buf.lock, flags); + wait_event(tty->read_wait, + test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); + return; + } else + __tty_buffer_flush(tty); + spin_unlock_irqrestore(&tty->buf.lock, flags); +} + +/** + * tty_buffer_find - find a free tty buffer + * @tty: tty owning the buffer + * @size: characters wanted + * + * Locate an existing suitable tty buffer or if we are lacking one then + * allocate a new one. We round our buffers off in 256 character chunks + * to get better allocation behaviour. + * + * Locking: Caller must hold tty->buf.lock + */ + +static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +{ + struct tty_buffer **tbh = &tty->buf.free; + while ((*tbh) != NULL) { + struct tty_buffer *t = *tbh; + if (t->size >= size) { + *tbh = t->next; + t->next = NULL; + t->used = 0; + t->commit = 0; + t->read = 0; + tty->buf.memory_used += t->size; + return t; + } + tbh = &((*tbh)->next); + } + /* Round the buffer size out */ + size = (size + 0xFF) & ~0xFF; + return tty_buffer_alloc(tty, size); + /* Should possibly check if this fails for the largest buffer we + have queued and recycle that ? */ +} + +/** + * tty_buffer_request_room - grow tty buffer if needed + * @tty: tty structure + * @size: size desired + * + * Make at least size bytes of linear space available for the tty + * buffer. If we fail return the size we managed to find. + * + * Locking: Takes tty->buf.lock + */ +int tty_buffer_request_room(struct tty_struct *tty, size_t size) +{ + struct tty_buffer *b, *n; + int left; + unsigned long flags; + + spin_lock_irqsave(&tty->buf.lock, flags); + + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ + if ((b = tty->buf.tail) != NULL) + left = b->size - b->used; + else + left = 0; + + if (left < size) { + /* This is the slow path - looking for new buffers to use */ + if ((n = tty_buffer_find(tty, size)) != NULL) { + if (b != NULL) { + b->next = n; + b->commit = b->used; + } else + tty->buf.head = n; + tty->buf.tail = n; + } else + size = left; + } + + spin_unlock_irqrestore(&tty->buf.lock, flags); + return size; +} +EXPORT_SYMBOL_GPL(tty_buffer_request_room); + +/** + * tty_insert_flip_string - Add characters to the tty buffer + * @tty: tty structure + * @chars: characters + * @size: size + * + * Queue a series of bytes to the tty buffering. All the characters + * passed are marked as without error. Returns the number added. + * + * Locking: Called functions may take tty->buf.lock + */ + +int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, + size_t size) +{ + int copied = 0; + do { + int space = tty_buffer_request_room(tty, size - copied); + struct tty_buffer *tb = tty->buf.tail; + /* If there is no space then tb may be NULL */ + if (unlikely(space == 0)) + break; + memcpy(tb->char_buf_ptr + tb->used, chars, space); + memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + tb->used += space; + copied += space; + chars += space; + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); + return copied; +} +EXPORT_SYMBOL(tty_insert_flip_string); + +/** + * tty_insert_flip_string_flags - Add characters to the tty buffer + * @tty: tty structure + * @chars: characters + * @flags: flag bytes + * @size: size + * + * Queue a series of bytes to the tty buffering. For each character + * the flags array indicates the status of the character. Returns the + * number added. + * + * Locking: Called functions may take tty->buf.lock + */ + +int tty_insert_flip_string_flags(struct tty_struct *tty, + const unsigned char *chars, const char *flags, size_t size) +{ + int copied = 0; + do { + int space = tty_buffer_request_room(tty, size - copied); + struct tty_buffer *tb = tty->buf.tail; + /* If there is no space then tb may be NULL */ + if (unlikely(space == 0)) + break; + memcpy(tb->char_buf_ptr + tb->used, chars, space); + memcpy(tb->flag_buf_ptr + tb->used, flags, space); + tb->used += space; + copied += space; + chars += space; + flags += space; + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); + return copied; +} +EXPORT_SYMBOL(tty_insert_flip_string_flags); + +/** + * tty_schedule_flip - push characters to ldisc + * @tty: tty to push from + * + * Takes any pending buffers and transfers their ownership to the + * ldisc side of the queue. It then schedules those characters for + * processing by the line discipline. + * + * Locking: Takes tty->buf.lock + */ + +void tty_schedule_flip(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) + tty->buf.tail->commit = tty->buf.tail->used; + spin_unlock_irqrestore(&tty->buf.lock, flags); + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_schedule_flip); + +/** + * tty_prepare_flip_string - make room for characters + * @tty: tty + * @chars: return pointer for character write area + * @size: desired size + * + * Prepare a block of space in the buffer for data. Returns the length + * available and buffer pointer to the space which is now allocated and + * accounted for as ready for normal characters. This is used for drivers + * that need their own block copy routines into the buffer. There is no + * guarantee the buffer is a DMA target! + * + * Locking: May call functions taking tty->buf.lock + */ + +int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, + size_t size) +{ + int space = tty_buffer_request_room(tty, size); + if (likely(space)) { + struct tty_buffer *tb = tty->buf.tail; + *chars = tb->char_buf_ptr + tb->used; + memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + tb->used += space; + } + return space; +} +EXPORT_SYMBOL_GPL(tty_prepare_flip_string); + +/** + * tty_prepare_flip_string_flags - make room for characters + * @tty: tty + * @chars: return pointer for character write area + * @flags: return pointer for status flag write area + * @size: desired size + * + * Prepare a block of space in the buffer for data. Returns the length + * available and buffer pointer to the space which is now allocated and + * accounted for as ready for characters. This is used for drivers + * that need their own block copy routines into the buffer. There is no + * guarantee the buffer is a DMA target! + * + * Locking: May call functions taking tty->buf.lock + */ + +int tty_prepare_flip_string_flags(struct tty_struct *tty, + unsigned char **chars, char **flags, size_t size) +{ + int space = tty_buffer_request_room(tty, size); + if (likely(space)) { + struct tty_buffer *tb = tty->buf.tail; + *chars = tb->char_buf_ptr + tb->used; + *flags = tb->flag_buf_ptr + tb->used; + tb->used += space; + } + return space; +} +EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); + + + +/** + * flush_to_ldisc + * @work: tty structure passed from work queue. + * + * This routine is called out of the software interrupt to flush data + * from the buffer chain to the line discipline. + * + * Locking: holds tty->buf.lock to guard buffer list. Drops the lock + * while invoking the line discipline receive_buf method. The + * receive_buf method is single threaded for each tty instance. + */ + +static void flush_to_ldisc(struct work_struct *work) +{ + struct tty_struct *tty = + container_of(work, struct tty_struct, buf.work.work); + unsigned long flags; + struct tty_ldisc *disc; + struct tty_buffer *tbuf, *head; + char *char_buf; + unsigned char *flag_buf; + + disc = tty_ldisc_ref(tty); + if (disc == NULL) /* !TTY_LDISC */ + return; + + spin_lock_irqsave(&tty->buf.lock, flags); + /* So we know a flush is running */ + set_bit(TTY_FLUSHING, &tty->flags); + head = tty->buf.head; + if (head != NULL) { + tty->buf.head = NULL; + for (;;) { + int count = head->commit - head->read; + if (!count) { + if (head->next == NULL) + break; + tbuf = head; + head = head->next; + tty_buffer_free(tty, tbuf); + continue; + } + /* Ldisc or user is trying to flush the buffers + we are feeding to the ldisc, stop feeding the + line discipline as we want to empty the queue */ + if (test_bit(TTY_FLUSHPENDING, &tty->flags)) + break; + if (!tty->receive_room) { + schedule_delayed_work(&tty->buf.work, 1); + break; + } + if (count > tty->receive_room) + count = tty->receive_room; + char_buf = head->char_buf_ptr + head->read; + flag_buf = head->flag_buf_ptr + head->read; + head->read += count; + spin_unlock_irqrestore(&tty->buf.lock, flags); + disc->ops->receive_buf(tty, char_buf, + flag_buf, count); + spin_lock_irqsave(&tty->buf.lock, flags); + } + /* Restore the queue head */ + tty->buf.head = head; + } + /* We may have a deferred request to flush the input buffer, + if so pull the chain under the lock and empty the queue */ + if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { + __tty_buffer_flush(tty); + clear_bit(TTY_FLUSHPENDING, &tty->flags); + wake_up(&tty->read_wait); + } + clear_bit(TTY_FLUSHING, &tty->flags); + spin_unlock_irqrestore(&tty->buf.lock, flags); + + tty_ldisc_deref(disc); +} + +/** + * tty_flip_buffer_push - terminal + * @tty: tty to push + * + * Queue a push of the terminal flip buffers to the line discipline. This + * function must not be called from IRQ context if tty->low_latency is set. + * + * In the event of the queue being busy for flipping the work will be + * held off and retried later. + * + * Locking: tty buffer lock. Driver locks in low latency mode. + */ + +void tty_flip_buffer_push(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) + tty->buf.tail->commit = tty->buf.tail->used; + spin_unlock_irqrestore(&tty->buf.lock, flags); + + if (tty->low_latency) + flush_to_ldisc(&tty->buf.work.work); + else + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_flip_buffer_push); + +/** + * tty_buffer_init - prepare a tty buffer structure + * @tty: tty to initialise + * + * Set up the initial state of the buffer management for a tty device. + * Must be called before the other tty buffer functions are used. + * + * Locking: none + */ + +void tty_buffer_init(struct tty_struct *tty) +{ + spin_lock_init(&tty->buf.lock); + tty->buf.head = NULL; + tty->buf.tail = NULL; + tty->buf.free = NULL; + tty->buf.memory_used = 0; + INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); +} + diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 2f05728920e7..3a726936aa5b 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -176,8 +176,6 @@ static struct tty_struct *alloc_tty_struct(void) return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); } -static void tty_buffer_free_all(struct tty_struct *); - /** * free_tty_struct - free a disused tty * @tty: tty struct to free @@ -263,398 +261,6 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) return 0; } -/* - * Tty buffer allocation management - */ - -/** - * tty_buffer_free_all - free buffers used by a tty - * @tty: tty to free from - * - * Remove all the buffers pending on a tty whether queued with data - * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none - */ - -static void tty_buffer_free_all(struct tty_struct *tty) -{ - struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - kfree(thead); - } - while ((thead = tty->buf.free) != NULL) { - tty->buf.free = thead->next; - kfree(thead); - } - tty->buf.tail = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_init - prepare a tty buffer structure - * @tty: tty to initialise - * - * Set up the initial state of the buffer management for a tty device. - * Must be called before the other tty buffer functions are used. - * - * Locking: none - */ - -static void tty_buffer_init(struct tty_struct *tty) -{ - spin_lock_init(&tty->buf.lock); - tty->buf.head = NULL; - tty->buf.tail = NULL; - tty->buf.free = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_alloc - allocate a tty buffer - * @tty: tty device - * @size: desired size (characters) - * - * Allocate a new tty buffer to hold the desired number of characters. - * Return NULL if out of memory or the allocation would exceed the - * per device queue - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *p; - - if (tty->buf.memory_used + size > 65536) - return NULL; - p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); - if (p == NULL) - return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; - return p; -} - -/** - * tty_buffer_free - free a tty buffer - * @tty: tty owning the buffer - * @b: the buffer to free - * - * Free a tty buffer, or add it to the free list according to our - * internal strategy - * - * Locking: Caller must hold tty->buf.lock - */ - -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) -{ - /* Dumb strategy for now - should keep some stats */ - tty->buf.memory_used -= b->size; - WARN_ON(tty->buf.memory_used < 0); - - if (b->size >= 512) - kfree(b); - else { - b->next = tty->buf.free; - tty->buf.free = b; - } -} - -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock - */ - -static void __tty_buffer_flush(struct tty_struct *tty) -{ - struct tty_buffer *thead; - - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - tty_buffer_free(tty, thead); - } - tty->buf.tail = NULL; -} - -/** - * tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. If the buffer is - * being processed by flush_to_ldisc then we defer the processing - * to that function - * - * Locking: none - */ - -static void tty_buffer_flush(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - - /* If the data is being pushed to the tty layer then we can't - process it here. Instead set a flag and the flush_to_ldisc - path will process the flush request before it exits */ - if (test_bit(TTY_FLUSHING, &tty->flags)) { - set_bit(TTY_FLUSHPENDING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); - wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); - return; - } else - __tty_buffer_flush(tty); - spin_unlock_irqrestore(&tty->buf.lock, flags); -} - -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) -{ - struct tty_buffer **tbh = &tty->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { - *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; - tty->buf.memory_used += t->size; - return t; - } - tbh = &((*tbh)->next); - } - /* Round the buffer size out */ - size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, size); - /* Should possibly check if this fails for the largest buffer we - have queued and recycle that ? */ -} - -/** - * tty_buffer_request_room - grow tty buffer if needed - * @tty: tty structure - * @size: size desired - * - * Make at least size bytes of linear space available for the tty - * buffer. If we fail return the size we managed to find. - * - * Locking: Takes tty->buf.lock - */ -int tty_buffer_request_room(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *b, *n; - int left; - unsigned long flags; - - spin_lock_irqsave(&tty->buf.lock, flags); - - /* OPTIMISATION: We could keep a per tty "zero" sized buffer to - remove this conditional if its worth it. This would be invisible - to the callers */ - if ((b = tty->buf.tail) != NULL) - left = b->size - b->used; - else - left = 0; - - if (left < size) { - /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - tty->buf.head = n; - tty->buf.tail = n; - } else - size = left; - } - - spin_unlock_irqrestore(&tty->buf.lock, flags); - return size; -} -EXPORT_SYMBOL_GPL(tty_buffer_request_room); - -/** - * tty_insert_flip_string - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @size: size - * - * Queue a series of bytes to the tty buffering. All the characters - * passed are marked as without error. Returns the number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, - size_t size) -{ - int copied = 0; - do { - int space = tty_buffer_request_room(tty, size - copied); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); - tb->used += space; - copied += space; - chars += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string); - -/** - * tty_insert_flip_string_flags - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @flags: flag bytes - * @size: size - * - * Queue a series of bytes to the tty buffering. For each character - * the flags array indicates the status of the character. Returns the - * number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string_flags(struct tty_struct *tty, - const unsigned char *chars, const char *flags, size_t size) -{ - int copied = 0; - do { - int space = tty_buffer_request_room(tty, size - copied); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); - tb->used += space; - copied += space; - chars += space; - flags += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string_flags); - -/** - * tty_schedule_flip - push characters to ldisc - * @tty: tty to push from - * - * Takes any pending buffers and transfers their ownership to the - * ldisc side of the queue. It then schedules those characters for - * processing by the line discipline. - * - * Locking: Takes tty->buf.lock - */ - -void tty_schedule_flip(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - schedule_delayed_work(&tty->buf.work, 1); -} -EXPORT_SYMBOL(tty_schedule_flip); - -/** - * tty_prepare_flip_string - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for normal characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking tty->buf.lock - */ - -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) -{ - int space = tty_buffer_request_room(tty, size); - if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); - tb->used += space; - } - return space; -} - -EXPORT_SYMBOL_GPL(tty_prepare_flip_string); - -/** - * tty_prepare_flip_string_flags - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @flags: return pointer for status flag write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking tty->buf.lock - */ - -int tty_prepare_flip_string_flags(struct tty_struct *tty, - unsigned char **chars, char **flags, size_t size) -{ - int space = tty_buffer_request_room(tty, size); - if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - *flags = tb->flag_buf_ptr + tb->used; - tb->used += space; - } - return space; -} - -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); - - - /** * get_tty_driver - find device of a tty * @dev_t: device identifier @@ -3215,113 +2821,6 @@ void do_SAK(struct tty_struct *tty) EXPORT_SYMBOL(do_SAK); -/** - * flush_to_ldisc - * @work: tty structure passed from work queue. - * - * This routine is called out of the software interrupt to flush data - * from the buffer chain to the line discipline. - * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. - */ - -static void flush_to_ldisc(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work.work); - unsigned long flags; - struct tty_ldisc *disc; - struct tty_buffer *tbuf, *head; - char *char_buf; - unsigned char *flag_buf; - - disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ - return; - - spin_lock_irqsave(&tty->buf.lock, flags); - /* So we know a flush is running */ - set_bit(TTY_FLUSHING, &tty->flags); - head = tty->buf.head; - if (head != NULL) { - tty->buf.head = NULL; - for (;;) { - int count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - tbuf = head; - head = head->next; - tty_buffer_free(tty, tbuf); - continue; - } - /* Ldisc or user is trying to flush the buffers - we are feeding to the ldisc, stop feeding the - line discipline as we want to empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) - break; - if (!tty->receive_room) { - schedule_delayed_work(&tty->buf.work, 1); - break; - } - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; - spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); - spin_lock_irqsave(&tty->buf.lock, flags); - } - /* Restore the queue head */ - tty->buf.head = head; - } - /* We may have a deferred request to flush the input buffer, - if so pull the chain under the lock and empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { - __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); - wake_up(&tty->read_wait); - } - clear_bit(TTY_FLUSHING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); - - tty_ldisc_deref(disc); -} - -/** - * tty_flip_buffer_push - terminal - * @tty: tty to push - * - * Queue a push of the terminal flip buffers to the line discipline. This - * function must not be called from IRQ context if tty->low_latency is set. - * - * In the event of the queue being busy for flipping the work will be - * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. - */ - -void tty_flip_buffer_push(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - - if (tty->low_latency) - flush_to_ldisc(&tty->buf.work.work); - else - schedule_delayed_work(&tty->buf.work, 1); -} - -EXPORT_SYMBOL(tty_flip_buffer_push); - - /** * initialize_tty_struct * @tty: tty to initialize @@ -3342,7 +2841,6 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->overrun_time = jiffies; tty->buf.head = tty->buf.tail = NULL; tty_buffer_init(tty); - INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); mutex_init(&tty->termios_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index 0cbec74ec086..7271c6247d82 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -347,6 +347,9 @@ extern void __do_SAK(struct tty_struct *tty); extern void disassociate_ctty(int priv); extern void no_tty(void); extern void tty_flip_buffer_push(struct tty_struct *tty); +extern void tty_buffer_free_all(struct tty_struct *tty); +extern void tty_buffer_flush(struct tty_struct *tty); +extern void tty_buffer_init(struct tty_struct *tty); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); -- cgit v1.2.3 From 348eb12e5598be97400c749d3d93a71856ae0b2b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:37:17 +0100 Subject: pps: Reserve a line discipline number for PPS Add a new line discipline for "pulse per second" devices connected to a serial port. Signed-off-by: Rodolfo Giometti Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- include/linux/tty.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/tty.h b/include/linux/tty.h index 7271c6247d82..e3612c3ac194 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -23,7 +23,7 @@ */ #define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ #define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ -#define NR_LDISCS 18 +#define NR_LDISCS 19 /* line disciplines */ #define N_TTY 0 @@ -45,6 +45,7 @@ #define N_HCI 15 /* Bluetooth HCI UART */ #define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */ #define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */ +#define N_PPS 18 /* Pulse per Second */ /* * This character is the same as _POSIX_VDISABLE: it cannot be used as -- cgit v1.2.3 From 9c9f4ded90a59eee84e15f5fd38c03d60184e112 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:37:26 +0100 Subject: tty: Add a kref count Introduce a kref to the tty structure and use it to protect the tty->signal tty references. For now we don't introduce it for anything else. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 54 +++++++++++++++++++++++++++++++++++++++++++++------ include/linux/tty.h | 18 +++++++++++++++++ kernel/fork.c | 5 ++++- kernel/sys.c | 4 +--- 4 files changed, 71 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 732316899ca4..310e0703e4a1 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -559,6 +559,7 @@ static void do_tty_hangup(struct work_struct *work) struct tty_ldisc *ld; int closecount = 0, n; unsigned long flags; + int refs = 0; if (!tty) return; @@ -625,8 +626,12 @@ static void do_tty_hangup(struct work_struct *work) if (tty->session) { do_each_pid_task(tty->session, PIDTYPE_SID, p) { spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) + if (p->signal->tty == tty) { p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } if (!p->signal->leader) { spin_unlock_irq(&p->sighand->siglock); continue; @@ -652,6 +657,10 @@ static void do_tty_hangup(struct work_struct *work) tty->ctrl_status = 0; spin_unlock_irqrestore(&tty->ctrl_lock, flags); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); + /* * If one of the devices matches a console pointer, we * cannot just call hangup() because that will cause @@ -1424,6 +1433,7 @@ release_mem_out: /** * release_one_tty - release tty structure memory + * @kref: kref of tty we are obliterating * * Releases memory associated with a tty structure, and clears out the * driver table slots. This function is called when a device is no longer @@ -1433,17 +1443,19 @@ release_mem_out: * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. - * FIXME: should we require tty_mutex is held here ?? */ -static void release_one_tty(struct tty_struct *tty, int idx) +static void release_one_tty(struct kref *kref) { + struct tty_struct *tty = container_of(kref, struct tty_struct, kref); int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; struct ktermios *tp; + int idx = tty->index; if (!devpts) tty->driver->ttys[idx] = NULL; if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { + /* FIXME: Locking on ->termios array */ tp = tty->termios; if (!devpts) tty->driver->termios[idx] = NULL; @@ -1457,6 +1469,7 @@ static void release_one_tty(struct tty_struct *tty, int idx) tty->magic = 0; + /* FIXME: locking on tty->driver->refcount */ tty->driver->refcount--; file_list_lock(); @@ -1466,6 +1479,21 @@ static void release_one_tty(struct tty_struct *tty, int idx) free_tty_struct(tty); } +/** + * tty_kref_put - release a tty kref + * @tty: tty device + * + * Release a reference to a tty device and if need be let the kref + * layer destruct the object for us + */ + +void tty_kref_put(struct tty_struct *tty) +{ + if (tty) + kref_put(&tty->kref, release_one_tty); +} +EXPORT_SYMBOL(tty_kref_put); + /** * release_tty - release tty structure memory * @@ -1477,14 +1505,20 @@ static void release_one_tty(struct tty_struct *tty, int idx) * takes the file list lock internally when working on the list * of ttys that the driver keeps. * FIXME: should we require tty_mutex is held here ?? + * + * FIXME: We want to defer the module put of the driver to the + * destructor. */ static void release_tty(struct tty_struct *tty, int idx) { struct tty_driver *driver = tty->driver; + /* This should always be true but check for the moment */ + WARN_ON(tty->index != idx); + if (tty->link) - release_one_tty(tty->link, idx); - release_one_tty(tty, idx); + tty_kref_put(tty->link); + tty_kref_put(tty); module_put(driver->owner); } @@ -2798,6 +2832,7 @@ EXPORT_SYMBOL(do_SAK); static void initialize_tty_struct(struct tty_struct *tty) { memset(tty, 0, sizeof(struct tty_struct)); + kref_init(&tty->kref); tty->magic = TTY_MAGIC; tty_ldisc_init(tty); tty->session = NULL; @@ -3053,9 +3088,12 @@ EXPORT_SYMBOL(tty_devnum); void proc_clear_tty(struct task_struct *p) { + struct tty_struct *tty; spin_lock_irq(&p->sighand->siglock); + tty = p->signal->tty; p->signal->tty = NULL; spin_unlock_irq(&p->sighand->siglock); + tty_kref_put(tty); } /* Called under the sighand lock */ @@ -3071,9 +3109,13 @@ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) tty->pgrp = get_pid(task_pgrp(tsk)); spin_unlock_irqrestore(&tty->ctrl_lock, flags); tty->session = get_pid(task_session(tsk)); + if (tsk->signal->tty) { + printk(KERN_DEBUG "tty not NULL!!\n"); + tty_kref_put(tsk->signal->tty); + } } put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty; + tsk->signal->tty = tty_kref_get(tty); tsk->signal->tty_old_pgrp = NULL; } diff --git a/include/linux/tty.h b/include/linux/tty.h index e3612c3ac194..b6e6c26883ee 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -209,6 +209,7 @@ struct tty_operations; struct tty_struct { int magic; + struct kref kref; struct tty_driver *driver; const struct tty_operations *ops; int index; @@ -311,6 +312,23 @@ extern int kmsg_redirect; extern void console_init(void); extern int vcs_init(void); +/** + * tty_kref_get - get a tty reference + * @tty: tty device + * + * Return a new reference to a tty object. The caller must hold + * sufficient locks/counts to ensure that their existing reference cannot + * go away + */ + +extern inline struct tty_struct *tty_kref_get(struct tty_struct *tty) +{ + if (tty) + kref_get(&tty->kref); + return tty; +} +extern void tty_kref_put(struct tty_struct *tty); + extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); extern char *tty_name(struct tty_struct *tty, char *buf); diff --git a/kernel/fork.c b/kernel/fork.c index 7ce2ebe84796..30de644a40c4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -802,6 +802,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->leader = 0; /* session leadership doesn't inherit */ sig->tty_old_pgrp = NULL; + sig->tty = NULL; sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero; sig->gtime = cputime_zero; @@ -838,6 +839,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) void __cleanup_signal(struct signal_struct *sig) { exit_thread_group_keys(sig); + tty_kref_put(sig->tty); kmem_cache_free(signal_cachep, sig); } @@ -1227,7 +1229,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->nsproxy->pid_ns->child_reaper = p; p->signal->leader_pid = pid; - p->signal->tty = current->signal->tty; + tty_kref_put(p->signal->tty); + p->signal->tty = tty_kref_get(current->signal->tty); set_task_pgrp(p, task_pgrp_nr(current)); set_task_session(p, task_session_nr(current)); attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); diff --git a/kernel/sys.c b/kernel/sys.c index 038a7bc0901d..234d9454294e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1060,9 +1060,7 @@ asmlinkage long sys_setsid(void) group_leader->signal->leader = 1; __set_special_pids(sid); - spin_lock(&group_leader->sighand->siglock); - group_leader->signal->tty = NULL; - spin_unlock(&group_leader->sighand->siglock); + proc_clear_tty(group_leader); err = session; out: -- cgit v1.2.3 From c26c56c0f40e200e61d1390629c806f6adaffbcc Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:37:48 +0100 Subject: tty: Cris has a nice RS485 ioctl so we should steal it JP Tosoni observed: "About a RS485 ioctl: could you consider the attached files which are already in the Linux kernel (in include/asm-cris). They define a TIOCSERSETRS485 (ioctl.h), and the data structure (rs485.h) with allows to specify timings. Sounds just like what we want ?" and he's right: sort of. Rework the structure to use flag bits and make the time delay a fixed sized field so we don't get 32/64bit problems. Add the ioctls to x86 so that people know what to add to their platform of choice. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- include/asm-x86/ioctls.h | 2 ++ include/linux/serial.h | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include/linux') diff --git a/include/asm-x86/ioctls.h b/include/asm-x86/ioctls.h index 336603512399..1a8ac45b28be 100644 --- a/include/asm-x86/ioctls.h +++ b/include/asm-x86/ioctls.h @@ -51,6 +51,8 @@ #define TCSETS2 _IOW('T', 0x2B, struct termios2) #define TCSETSW2 _IOW('T', 0x2C, struct termios2) #define TCSETSF2 _IOW('T', 0x2D, struct termios2) +#define TIOCGRS485 0x542E +#define TIOCSRS485 0x542F #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ diff --git a/include/linux/serial.h b/include/linux/serial.h index deb714314fb1..1ea8d9265bf6 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -173,6 +173,22 @@ struct serial_icounter_struct { int reserved[9]; }; +/* + * Serial interface for controlling RS485 settings on chips with suitable + * support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your + * platform. The set function returns the new state, with any unsupported bits + * reverted appropriately. + */ + +struct serial_rs485 { + __u32 flags; /* RS485 feature flags */ +#define SER_RS485_ENABLED (1 << 0) +#define SER_RS485_RTS_ON_SEND (1 << 1) +#define SER_RS485_RTS_AFTER_SEND (1 << 2) + __u32 delay_rts_before_send; /* Milliseconds */ + __u32 padding[6]; /* Memory is cheap, new structs + are a royal PITA .. */ +}; #ifdef __KERNEL__ #include -- cgit v1.2.3 From 1d65b4a088de407e99714fdc27862449db04fb5c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:38:18 +0100 Subject: tty: Add termiox We need a way to describe the various additional modes and flow control features that random weird hardware shows up and software such as wine wants to emulate as Windows supports them. TCGETX/TCSETX and the termiox ioctl are a SYS5 extension that we might as well adopt. This patches adds the structures and the basic ioctl interfaces when the TCGETX etc defines are added for an architecture. Drivers wishing to use this stuff need to add new methods. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_ioctl.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ include/asm-x86/ioctls.h | 4 ++++ include/linux/termios.h | 15 ++++++++++++ include/linux/tty.h | 1 + include/linux/tty_driver.h | 9 +++++++ 5 files changed, 87 insertions(+) (limited to 'include/linux') diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index bf34e4597421..93bfa1d6cc92 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -579,6 +579,50 @@ static int get_termio(struct tty_struct *tty, struct termio __user *termio) return 0; } + +#ifdef TCGETX + +/** + * set_termiox - set termiox fields if possible + * @tty: terminal + * @arg: termiox structure from user + * @opt: option flags for ioctl type + * + * Implement the device calling points for the SYS5 termiox ioctl + * interface in Linux + */ + +static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) +{ + struct termiox tnew; + struct tty_ldisc *ld; + + if (tty->termiox == NULL) + return -EINVAL; + if (copy_from_user(&tnew, arg, sizeof(struct termiox))) + return -EFAULT; + + ld = tty_ldisc_ref(tty); + if (ld != NULL) { + if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); + tty_ldisc_deref(ld); + } + if (opt & TERMIOS_WAIT) { + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + } + + mutex_lock(&tty->termios_mutex); + if (tty->ops->set_termiox) + tty->ops->set_termiox(tty, &tnew); + mutex_unlock(&tty->termios_mutex); + return 0; +} + +#endif + static unsigned long inq_canon(struct tty_struct *tty) { int nr, head, tail; @@ -936,6 +980,20 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, return -EFAULT; return 0; #endif +#ifdef TCGETX + case TCGETX: + if (real_tty->termiox == NULL) + return -EINVAL; + if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox))) + return -EFAULT; + return 0; + case TCSETX: + return set_termiox(real_tty, p, 0); + case TCSETXW: + return set_termiox(real_tty, p, TERMIOS_WAIT); + case TCSETXF: + return set_termiox(real_tty, p, TERMIOS_FLUSH); +#endif case TIOCGSOFTCAR: /* FIXME: for correctness we may need to take the termios lock here - review */ diff --git a/include/asm-x86/ioctls.h b/include/asm-x86/ioctls.h index 1a8ac45b28be..06752a649044 100644 --- a/include/asm-x86/ioctls.h +++ b/include/asm-x86/ioctls.h @@ -56,6 +56,10 @@ #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */ +#define TCSETX 0x5433 +#define TCSETXF 0x5434 +#define TCSETXW 0x5435 #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 diff --git a/include/linux/termios.h b/include/linux/termios.h index 478662889f48..2acd0c1f8a2a 100644 --- a/include/linux/termios.h +++ b/include/linux/termios.h @@ -4,4 +4,19 @@ #include #include +#define NFF 5 + +struct termiox +{ + __u16 x_hflag; + __u16 x_cflag; + __u16 x_rflag[NFF]; + __u16 x_sflag; +}; + +#define RTSXOFF 0x0001 /* RTS flow control on input */ +#define CTSXON 0x0002 /* CTS flow control on output */ +#define DTRXOFF 0x0004 /* DTR flow control on input */ +#define DSRXON 0x0008 /* DCD flow control on output */ + #endif diff --git a/include/linux/tty.h b/include/linux/tty.h index b6e6c26883ee..b64d10b66548 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -219,6 +219,7 @@ struct tty_struct { spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ struct ktermios *termios, *termios_locked; + struct termiox *termiox; /* May be NULL for unsupported */ char name[64]; struct pid *pgrp; /* Protected by ctrl lock */ struct pid *session; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 16d27944c321..ac6e58e26b73 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -180,6 +180,14 @@ * not force errors here if they are not resizable objects (eg a serial * line). See tty_do_resize() if you need to wrap the standard method * in your own logic - the usual case. + * + * void (*set_termiox)(struct tty_struct *tty, struct termiox *new); + * + * Called when the device receives a termiox based ioctl. Passes down + * the requested data from user space. This method will not be invoked + * unless the tty also has a valid tty->termiox pointer. + * + * Optional: Called under the termios lock */ #include @@ -220,6 +228,7 @@ struct tty_operations { unsigned int set, unsigned int clear); int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize *ws); + int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); -- cgit v1.2.3 From 4a90f09b20f4622dcbff1f0e1e6bae1704f8ad8c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:39:46 +0100 Subject: tty: usb-serial krefs Use kref in the USB serial drivers so that we don't free tty structures from under the URB receive handlers as has historically been the case if you were unlucky. This also gives us a framework for general tty drivers to use tty_port objects and refcount. Contains two err->dev_err changes merged together to fix clashes in the -next tree. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_port.c | 41 +++++++++++++++++++ drivers/usb/serial/aircable.c | 15 ++++--- drivers/usb/serial/belkin_sa.c | 3 +- drivers/usb/serial/console.c | 8 ++-- drivers/usb/serial/cyberjack.c | 3 +- drivers/usb/serial/cypress_m8.c | 5 ++- drivers/usb/serial/digi_acceleport.c | 19 ++++++--- drivers/usb/serial/empeg.c | 8 ++-- drivers/usb/serial/ftdi_sio.c | 19 +++++---- drivers/usb/serial/garmin_gps.c | 3 +- drivers/usb/serial/generic.c | 3 +- drivers/usb/serial/io_edgeport.c | 43 ++++++++++++------- drivers/usb/serial/io_ti.c | 26 ++++++++---- drivers/usb/serial/ipaq.c | 3 +- drivers/usb/serial/ipw.c | 3 +- drivers/usb/serial/ir-usb.c | 3 +- drivers/usb/serial/iuu_phoenix.c | 3 +- drivers/usb/serial/keyspan.c | 77 ++++++++++++++++++----------------- drivers/usb/serial/keyspan_pda.c | 16 +++++--- drivers/usb/serial/kl5kusb105.c | 3 +- drivers/usb/serial/kobil_sct.c | 3 +- drivers/usb/serial/mct_u232.c | 6 ++- drivers/usb/serial/mos7720.c | 36 ++-------------- drivers/usb/serial/mos7840.c | 7 ++-- drivers/usb/serial/navman.c | 3 +- drivers/usb/serial/omninet.c | 10 +++-- drivers/usb/serial/option.c | 18 ++++---- drivers/usb/serial/oti6858.c | 3 +- drivers/usb/serial/pl2303.c | 4 +- drivers/usb/serial/safe_serial.c | 11 +++-- drivers/usb/serial/sierra.c | 16 ++++---- drivers/usb/serial/spcp8x5.c | 3 +- drivers/usb/serial/ti_usb_3410_5052.c | 44 ++++++++++---------- drivers/usb/serial/usb-serial.c | 24 +++++++---- drivers/usb/serial/visor.c | 18 ++++---- drivers/usb/serial/whiteheat.c | 8 ++-- include/linux/tty.h | 3 ++ 37 files changed, 313 insertions(+), 208 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 6fadb19d630b..553b0e9d8d17 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -23,6 +23,7 @@ void tty_port_init(struct tty_port *port) init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); mutex_init(&port->mutex); + spin_lock_init(&port->lock); port->close_delay = (50 * HZ) / 100; port->closing_wait = (3000 * HZ) / 100; } @@ -53,3 +54,43 @@ void tty_port_free_xmit_buf(struct tty_port *port) EXPORT_SYMBOL(tty_port_free_xmit_buf); +/** + * tty_port_tty_get - get a tty reference + * @port: tty port + * + * Return a refcount protected tty instance or NULL if the port is not + * associated with a tty (eg due to close or hangup) + */ + +struct tty_struct *tty_port_tty_get(struct tty_port *port) +{ + unsigned long flags; + struct tty_struct *tty; + + spin_lock_irqsave(&port->lock, flags); + tty = tty_kref_get(port->tty); + spin_unlock_irqrestore(&port->lock, flags); + return tty; +} +EXPORT_SYMBOL(tty_port_tty_get); + +/** + * tty_port_tty_set - set the tty of a port + * @port: tty port + * @tty: the tty + * + * Associate the port and tty pair. Manages any internal refcounts. + * Pass NULL to deassociate a port + */ + +void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (port->tty) + tty_kref_put(port->tty); + port->tty = tty; + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL(tty_port_tty_set); diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 79ea98c66fa8..99fb7dc59c45 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work) * 64 bytes, to ensure I do not get throttled. * Ask USB mailing list for better aproach. */ - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { schedule_work(&priv->rx_work); @@ -283,12 +283,13 @@ static void aircable_read(struct work_struct *work) count = min(64, serial_buf_data_avail(priv->rx_buf)); if (count <= 0) - return; /* We have finished sending everything. */ + goto out; /* We have finished sending everything. */ tty_prepare_flip_string(tty, &data, count); if (!data) { - err("%s- kzalloc(%d) failed.", __func__, count); - return; + dev_err(&port->dev, "%s- kzalloc(%d) failed.", + __func__, count); + goto out; } serial_buf_get(priv->rx_buf, data, count); @@ -297,7 +298,8 @@ static void aircable_read(struct work_struct *work) if (serial_buf_data_avail(priv->rx_buf)) schedule_work(&priv->rx_work); - +out: + tty_kref_put(tty); return; } /* End of private methods */ @@ -495,7 +497,7 @@ static void aircable_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, urb->transfer_buffer); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { if (urb->actual_length <= 2) { /* This is an incomplete package */ @@ -527,6 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb) } aircable_read(&priv->rx_work); } + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 2ebe06c3405a..1913bc7c5f0b 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -322,7 +322,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* Overrun Error */ if (priv->last_lsr & BELKIN_SA_LSR_OE) { } @@ -335,6 +335,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) /* Break Indicator */ if (priv->last_lsr & BELKIN_SA_LSR_BI) { } + tty_kref_put(tty); } #endif spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index e980766bb84b..5b20de130e08 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -117,7 +117,7 @@ static int usb_console_setup(struct console *co, char *options) } port = serial->port[0]; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); info->port = port; @@ -143,7 +143,7 @@ static int usb_console_setup(struct console *co, char *options) } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); } /* only call the device specific open if this @@ -163,7 +163,7 @@ static int usb_console_setup(struct console *co, char *options) tty_termios_encode_baud_rate(termios, baud, baud); serial->type->set_termios(tty, port, &dummy); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); kfree(termios); kfree(tty); } @@ -176,7 +176,7 @@ out: return retval; free_termios: kfree(termios); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); free_tty: kfree(tty); reset_open_count: diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index b4d72351cb96..94ef36c4764b 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -384,7 +384,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - ignoring since device not open\n", __func__); return; @@ -394,6 +394,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); spin_lock(&priv->lock); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 22837a3f2f89..f3514a91f915 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1286,7 +1286,7 @@ static void cypress_read_int_callback(struct urb *urb) } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1362,7 +1362,7 @@ static void cypress_read_int_callback(struct urb *urb) data[i]); tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push(port->port.tty); + tty_flip_buffer_push(tty); } spin_lock_irqsave(&priv->lock, flags); @@ -1371,6 +1371,7 @@ static void cypress_read_int_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); continue_read: + tty_kref_put(tty); /* Continue trying to always read... unless the port has closed. */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 240aad1acaab..5756ac6d6c92 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -604,7 +604,9 @@ static void digi_wakeup_write_lock(struct work_struct *work) static void digi_wakeup_write(struct usb_serial_port *port) { - tty_wakeup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_wakeup(tty); + tty_kref_put(tty); } @@ -1668,7 +1670,7 @@ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; @@ -1692,6 +1694,7 @@ static int digi_read_inb_callback(struct urb *urb) return -1; } + tty = tty_port_tty_get(&port->port); spin_lock(&priv->dp_port_lock); /* check for throttle; if set, do not resubmit read urb */ @@ -1735,6 +1738,7 @@ static int digi_read_inb_callback(struct urb *urb) } } spin_unlock(&priv->dp_port_lock); + tty_kref_put(tty); if (opcode == DIGI_CMD_RECEIVE_DISABLE) dbg("%s: got RECEIVE_DISABLE", __func__); @@ -1760,6 +1764,7 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; + struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode, line, status, val; int i; @@ -1787,10 +1792,11 @@ static int digi_read_oob_callback(struct urb *urb) if (priv == NULL) return -1; + tty = tty_port_tty_get(&port->port); rts = 0; if (port->port.count) - rts = port->port.tty->termios->c_cflag & CRTSCTS; - + rts = tty->termios->c_cflag & CRTSCTS; + if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) { spin_lock(&priv->dp_port_lock); /* convert from digi flags to termiox flags */ @@ -1798,14 +1804,14 @@ static int digi_read_oob_callback(struct urb *urb) priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ if (rts) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; digi_wakeup_write(port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ if (rts) - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; } if (val & DIGI_READ_INPUT_SIGNALS_DSR) priv->dp_modem_signals |= TIOCM_DSR; @@ -1830,6 +1836,7 @@ static int digi_read_oob_callback(struct urb *urb) } else if (opcode == DIGI_CMD_IFLUSH_FIFO) { wake_up_interruptible(&priv->dp_flush_wait); } + tty_kref_put(tty); } return 0; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index a6ab5b58d9ca..1072e847280f 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -33,9 +33,8 @@ * Moved MOD_DEC_USE_COUNT to end of empeg_close(). * * (12/03/2000) gb - * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to - * empeg_open(). This notifies the tty driver that the termios have - * changed. + * Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open(). + * This notifies the tty driver that the termios have changed. * * (11/13/2000) gb * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to @@ -354,7 +353,7 @@ static void empeg_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); @@ -362,6 +361,7 @@ static void empeg_read_bulk_callback(struct urb *urb) tty_flip_buffer_push(tty); bytes_in += urb->actual_length; } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb( diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1ac7e802b4b6..c2ac129557aa 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1808,7 +1808,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) if (port->port.count <= 0) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1817,7 +1817,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __func__); - return; + goto out; } if (urb != port->read_urb) @@ -1827,7 +1827,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) /* This will happen at close every time so it is a dbg not an err */ dbg("(this is ok on close) nonzero read bulk status received: %d", status); - return; + goto out; } /* count data bytes, but not status bytes */ @@ -1838,7 +1838,8 @@ static void ftdi_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->rx_lock, flags); ftdi_process_read(&priv->rx_work.work); - +out: + tty_kref_put(tty); } /* ftdi_read_bulk_callback */ @@ -1863,7 +1864,7 @@ static void ftdi_process_read(struct work_struct *work) if (port->port.count <= 0) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1872,13 +1873,13 @@ static void ftdi_process_read(struct work_struct *work) priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __func__); - return; + goto out; } urb = port->read_urb; if (!urb) { dbg("%s - bad read_urb pointer - exiting", __func__); - return; + goto out; } data = urb->transfer_buffer; @@ -2020,7 +2021,7 @@ static void ftdi_process_read(struct work_struct *work) schedule_delayed_work(&priv->rx_work, 1); else dbg("%s - port is closed", __func__); - return; + goto out; } /* urb is completely processed */ @@ -2041,6 +2042,8 @@ static void ftdi_process_read(struct work_struct *work) err("%s - failed resubmitting read urb, error %d", __func__, result); } +out: + tty_kref_put(tty); } /* ftdi_process_read */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index d95382088075..2ad0569bcf19 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -276,7 +276,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, char *data, unsigned int actual_length) { - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); if (tty && actual_length) { @@ -287,6 +287,7 @@ static void send_to_tty(struct usb_serial_port *port, tty_insert_flip_string(tty, data, actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index fe84c88ec20c..814909f1ee63 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -330,7 +330,7 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); int room; /* Push data to tty */ @@ -341,6 +341,7 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) tty_flip_buffer_push(tty); } } + tty_kref_put(tty); resubmit_read_urb(port, GFP_ATOMIC); } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index bfa508ddb0fe..611f97fd62f1 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -600,6 +600,7 @@ static void edge_interrupt_callback(struct urb *urb) struct edgeport_serial *edge_serial = urb->context; struct edgeport_port *edge_port; struct usb_serial_port *port; + struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int length = urb->actual_length; int bytes_avail; @@ -675,9 +676,12 @@ static void edge_interrupt_callback(struct urb *urb) /* tell the tty driver that something has changed */ - if (edge_port->port->port.tty) - tty_wakeup(edge_port->port->port.tty); - + tty = tty_port_tty_get( + &edge_port->port->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } /* Since we have more credit, check if more data can be sent */ send_more_port_data(edge_serial, @@ -778,13 +782,14 @@ static void edge_bulk_out_data_callback(struct urb *urb) __func__, status); } - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ tty_wakeup(tty); } + tty_kref_put(tty); /* Release the Write URB */ edge_port->write_in_progress = false; @@ -826,11 +831,12 @@ static void edge_bulk_out_cmd_callback(struct urb *urb) } /* Get pointer to tty */ - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); /* tell the tty driver that something has changed */ if (tty && edge_port->open) tty_wakeup(tty); + tty_kref_put(tty); /* we have completed the command */ edge_port->commandPending = false; @@ -1932,11 +1938,13 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port->open) { - tty = edge_port->port->port.tty; + tty = tty_port_tty_get( + &edge_port->port->port); if (tty) { dbg("%s - Sending %d bytes to TTY for port %d", __func__, rxLen, edge_serial->rxPort); edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); + tty_kref_put(tty); } edge_port->icount.rx += rxLen; } @@ -1971,6 +1979,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, { struct usb_serial_port *port; struct edgeport_port *edge_port; + struct tty_struct *tty; __u8 code = edge_serial->rxStatusCode; /* switch the port pointer to the one being currently talked about */ @@ -2020,10 +2029,12 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, /* send the current line settings to the port so we are in sync with any further termios calls */ - /* FIXME: locking on tty */ - if (edge_port->port->port.tty) - change_port_settings(edge_port->port->port.tty, - edge_port, edge_port->port->port.tty->termios); + tty = tty_port_tty_get(&edge_port->port->port); + if (tty) { + change_port_settings(tty, + edge_port, tty->termios); + tty_kref_put(tty); + } /* we have completed the open */ edge_port->openPending = false; @@ -2163,10 +2174,14 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, } /* Place LSR data byte into Rx buffer */ - if (lsrData && edge_port->port->port.tty) - edge_tty_recv(&edge_port->port->dev, - edge_port->port->port.tty, &data, 1); - + if (lsrData) { + struct tty_struct *tty = + tty_port_tty_get(&edge_port->port->port); + if (tty) { + edge_tty_recv(&edge_port->port->dev, tty, &data, 1); + tty_kref_put(tty); + } + } /* update input line counters */ icount = &edge_port->icount; if (newLsr & LSR_BREAK) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index cb4c54316cf5..541dd8e6e7a2 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -572,7 +572,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, int flush) { int baud_rate; - struct tty_struct *tty = port->port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port->port); wait_queue_t wait; unsigned long flags; @@ -599,6 +599,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, if (flush) edge_buf_clear(port->ep_out_buf); spin_unlock_irqrestore(&port->ep_lock, flags); + tty_kref_put(tty); /* wait for data to drain from the device */ timeout += jiffies; @@ -1554,7 +1555,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) /* Save the new modem status */ edge_port->shadow_msr = msr & 0xf0; - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { if (msr & EDGEPORT_MSR_CTS) { @@ -1564,6 +1565,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) tty->hw_stopped = 1; } } + tty_kref_put(tty); return; } @@ -1574,6 +1576,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, struct async_icount *icount; __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); + struct tty_struct *tty; dbg("%s - %02x", __func__, new_lsr); @@ -1587,8 +1590,13 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); /* Place LSR data byte into Rx buffer */ - if (lsr_data && edge_port->port->port.tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); + if (lsr_data) { + tty = tty_port_tty_get(&edge_port->port->port); + if (tty) { + edge_tty_recv(&edge_port->port->dev, tty, &data, 1); + tty_kref_put(tty); + } + } /* update input line counters */ icount = &edge_port->icount; @@ -1749,7 +1757,7 @@ static void edge_bulk_in_callback(struct urb *urb) ++data; } - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); if (tty && urb->actual_length) { usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data); @@ -1761,6 +1769,7 @@ static void edge_bulk_in_callback(struct urb *urb) urb->actual_length); edge_port->icount.rx += urb->actual_length; } + tty_kref_put(tty); exit: /* continue read unless stopped */ @@ -1796,6 +1805,7 @@ static void edge_bulk_out_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status = urb->status; + struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); @@ -1818,7 +1828,9 @@ static void edge_bulk_out_callback(struct urb *urb) } /* send any buffered data */ - edge_send(port->port.tty); + tty = tty_port_tty_get(&port->port); + edge_send(tty); + tty_kref_put(tty); } static int edge_open(struct tty_struct *tty, @@ -1876,7 +1888,7 @@ static int edge_open(struct tty_struct *tty, /* set up the port settings */ if (tty) - edge_set_termios(tty, port, port->port.tty->termios); + edge_set_termios(tty, port, tty->termios); /* open up the port */ diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index cd9a2e138c8b..2affa9c118b2 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -764,13 +764,14 @@ static void ipaq_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); bytes_in += urb->actual_length; } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index a842025b9b57..480cac27d646 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -170,12 +170,13 @@ static void ipw_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index e59155c6607d..45d4043e04ab 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -465,11 +465,12 @@ static void ir_read_bulk_callback(struct urb *urb) ir_baud = *data & 0x0f; usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty_buffer_request_room(tty, urb->actual_length - 1)) { tty_insert_flip_string(tty, data+1, urb->actual_length - 1); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* * No break here. diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index ddff37fa6339..53710aa7eadd 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -629,13 +629,14 @@ static void read_buf_callback(struct urb *urb) } dbg("%s - %i chars to write", __func__, urb->actual_length); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (data == NULL) dbg("%s - data is NULL !!!", __func__); if (tty && urb->actual_length && data) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); iuu_led_activity_on(urb); } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 704716f6f6d3..15447af48691 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -430,7 +430,7 @@ static void usa26_indat_callback(struct urb *urb) } port = urb->context; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -459,6 +459,7 @@ static void usa26_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -513,6 +514,7 @@ static void usa26_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -553,12 +555,11 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -604,11 +605,12 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty = port->port.tty; - if (urb->actual_length) { + tty =tty_port_tty_get(&port->port); + if (tty && urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -652,6 +654,7 @@ static void usa28_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state; int status = urb->status; @@ -689,12 +692,11 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if( old_dcd_state != p_priv->dcd_state && old_dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -785,12 +787,11 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -827,7 +828,7 @@ static void usa49_indat_callback(struct urb *urb) } port = urb->context; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -850,6 +851,7 @@ static void usa49_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -893,7 +895,7 @@ static void usa49wg_indat_callback(struct urb *urb) return; } port = serial->port[data[i++]]; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); len = data[i++]; /* 0x80 bit is error flag */ @@ -927,6 +929,7 @@ static void usa49wg_indat_callback(struct urb *urb) } if (port->port.count) tty_flip_buffer_push(tty); + tty_kref_put(tty); } } @@ -967,8 +970,8 @@ static void usa90_indat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - tty = port->port.tty; if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); /* if current mode is DMA, looks like usa28 format otherwise looks like usa26 data format */ @@ -1004,6 +1007,7 @@ static void usa90_indat_callback(struct urb *urb) } } tty_flip_buffer_push(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1025,6 +1029,7 @@ static void usa90_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -1053,12 +1058,11 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1130,12 +1134,11 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1332,7 +1335,7 @@ static void keyspan_close(struct tty_struct *tty, stop_urb(p_priv->out_urbs[i]); } } - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); } /* download the firmware to a pre-renumeration device */ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 040040a267d9..99e9a14c5bf6 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -172,8 +172,9 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - - tty_wakeup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_wakeup(tty); + tty_kref_put(tty); } static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -205,7 +206,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) static void keyspan_pda_rx_interrupt(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); unsigned char *data = urb->transfer_buffer; int retval; int status = urb->status; @@ -222,7 +223,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __func__, status); - return; + goto out; default: dbg("%s - nonzero urb status received: %d", __func__, status); @@ -261,8 +262,11 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err("%s - usb_submit_urb failed with result %d", - __func__, retval); + dev_err(&port->dev, + "%s - usb_submit_urb failed with result %d", + __func__, retval); +out: + tty_kref_put(tty); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index b84dddc71124..ff3a07f5102f 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -658,7 +658,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb) } else { int bytes_sent = ((__u8 *) data)[0] + ((unsigned int) ((__u8 *) data)[1] << 8); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* we should immediately resubmit the URB, before attempting * to pass the data on to the tty layer. But that needs locking * against re-entry an then mixed-up data because of @@ -679,6 +679,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb) tty_buffer_request_room(tty, bytes_sent); tty_insert_flip_string(tty, data + 2, bytes_sent); tty_flip_buffer_push(tty); + tty_kref_put(tty); /* again lockless, but debug info only */ priv->bytes_in += bytes_sent; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index deba28ec77e8..cfcf37c2b957 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -383,7 +383,7 @@ static void kobil_read_int_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { /* BEGIN DEBUG */ @@ -405,6 +405,7 @@ static void kobil_read_int_callback(struct urb *urb) tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 0ded8bd6ec85..9b2cef81cde0 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -563,10 +563,11 @@ static void mct_u232_read_int_callback(struct urb *urb) * Work-a-round: handle the 'usual' bulk-in pipe here */ if (urb->transfer_buffer_length > 2) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); + tty_kref_put(tty); } goto exit; } @@ -591,7 +592,7 @@ static void mct_u232_read_int_callback(struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & MCT_U232_LSR_ERR) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* Overrun Error */ if (priv->last_lsr & MCT_U232_LSR_OE) { } @@ -604,6 +605,7 @@ static void mct_u232_read_int_callback(struct urb *urb) /* Break Indicator */ if (priv->last_lsr & MCT_U232_LSR_BI) { } + tty_kref_put(tty); } #endif spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 7c4917d77c0a..7b538caec37f 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -216,12 +216,13 @@ static void mos7720_bulk_in_callback(struct urb *urb) data = urb->transfer_buffer; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); if (!port->read_urb) { dbg("URB KILLED !!!"); @@ -262,10 +263,11 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) dbg("Entering ........."); - tty = mos7720_port->port->port.tty; + tty = tty_port_tty_get(&mos7720_port->port->port); if (tty && mos7720_port->open) tty_wakeup(tty); + tty_kref_put(tty); } /* @@ -1267,29 +1269,6 @@ static int get_lsr_info(struct tty_struct *tty, return 0; } -/* - * get_number_bytes_avail - get number of bytes available - * - * Purpose: Let user call ioctl to get the count of number of bytes available. - */ -static int get_number_bytes_avail(struct moschip_port *mos7720_port, - unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = mos7720_port->port->port.tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, mos7720_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - - return -ENOIOCTLCMD; -} - static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, unsigned int __user *value) { @@ -1409,13 +1388,6 @@ static int mos7720_ioctl(struct tty_struct *tty, struct file *file, dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - case TIOCINQ: - /* return number of bytes available */ - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(mos7720_port, - (unsigned int __user *)arg); - break; - case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return get_lsr_info(tty, mos7720_port, diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 09d82062b973..60543d79ef56 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -709,12 +709,13 @@ static void mos7840_bulk_in_callback(struct urb *urb) dbg("%s", "Entering ........... \n"); if (urb->actual_length) { - tty = mos7840_port->port->port.tty; + tty = tty_port_tty_get(&mos7840_port->port->port); if (tty) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); dbg(" %s \n", data); tty_flip_buffer_push(tty); + tty_kref_put(tty); } mos7840_port->icount.rx += urb->actual_length; smp_wmb(); @@ -773,10 +774,10 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) dbg("%s \n", "Entering ........."); - tty = mos7840_port->port->port.tty; - + tty = tty_port_tty_get(&mos7840_port->port->port); if (tty && mos7840_port->open) tty_wakeup(tty); + tty_kref_put(tty); } diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index d6736531a0fa..bcdcbb822705 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,12 +64,13 @@ static void navman_read_int_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); exit: result = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index ae8e227f3db2..c4d70b0f1e48 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -172,7 +172,7 @@ static int omninet_open(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); wport = serial->port[1]; - wport->port.tty = tty; /* FIXME */ + tty_port_tty_set(&wport->port, tty); /* Start reading from the device */ usb_fill_bulk_urb(port->read_urb, serial->dev, @@ -229,9 +229,11 @@ static void omninet_read_bulk_callback(struct urb *urb) } if (urb->actual_length && header->oh_len) { - tty_insert_flip_string(port->port.tty, - data + OMNINET_DATAOFFSET, header->oh_len); - tty_flip_buffer_push(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET, + header->oh_len); + tty_flip_buffer_push(tty); + tty_kref_put(tty); } /* Continue trying to always read */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 73f8277f88f2..6b1727e751e3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -571,14 +571,14 @@ static void option_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); - } else { + } else dbg("%s: empty read urb received", __func__); - } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN) { @@ -647,9 +647,13 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) && - old_dcd_state && !portdata->dcd_state) - tty_hangup(port->port.tty); + if (old_dcd_state && !portdata->dcd_state) { + struct tty_struct *tty = + tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); + } } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -793,7 +797,7 @@ static void option_close(struct tty_struct *tty, for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - port->port.tty = NULL; /* FIXME */ + tty_port_tty_set(&port->port, NULL); } /* Helper functions used by option_setup_urbs */ diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 42f92815c6e5..ba551f00f16f 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -998,11 +998,12 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty != NULL && urb->actual_length > 0) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* schedule the interrupt urb if we are still open */ if (port->port.count != 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 8d6006894bf5..908437847165 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -1046,7 +1046,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -1056,7 +1056,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_insert_flip_char(tty, data[i], tty_flag); tty_flip_buffer_push(tty); } - + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index def52d07a4ea..72903ac9f5c0 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -217,6 +217,7 @@ static void safe_read_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; unsigned char length = urb->actual_length; + struct tty_struct *tty; int result; int status = urb->status; @@ -242,6 +243,7 @@ static void safe_read_bulk_callback(struct urb *urb) printk("\n"); } #endif + tty = tty_port_tty_get(&port->port); if (safe) { __u16 fcs; fcs = fcs_compute10(data, length, CRC10_INITFCS); @@ -250,9 +252,9 @@ static void safe_read_bulk_callback(struct urb *urb) if (actual_length <= (length - 2)) { info("%s - actual: %d", __func__, actual_length); - tty_insert_flip_string(port->port.tty, + tty_insert_flip_string(tty, data, actual_length); - tty_flip_buffer_push(port->port.tty); + tty_flip_buffer_push(tty); } else { err("%s - inconsistent lengths %d:%d", __func__, actual_length, length); @@ -261,9 +263,10 @@ static void safe_read_bulk_callback(struct urb *urb) err("%s - bad CRC %x", __func__, fcs); } } else { - tty_insert_flip_string(port->port.tty, data, length); - tty_flip_buffer_push(port->port.tty); + tty_insert_flip_string(tty, data, length); + tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(urb, port->serial->dev, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ea1a103c99be..8b9eaf383679 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -440,14 +440,14 @@ static void sierra_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->port.tty; if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); - } else { + tty_kref_put(tty); + } else dbg("%s: empty read urb received", __func__); - } /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN) { @@ -485,6 +485,7 @@ static void sierra_instat_callback(struct urb *urb) unsigned char signals = *((unsigned char *) urb->transfer_buffer + sizeof(struct usb_ctrlrequest)); + struct tty_struct *tty; dbg("%s: signal x%x", __func__, signals); @@ -494,9 +495,11 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) && + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->port.tty); + tty_hangup(tty); + tty_kref_put(tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -616,8 +619,7 @@ static void sierra_close(struct tty_struct *tty, } usb_kill_urb(port->interrupt_in_urb); - - port->port.tty = NULL; /* FIXME */ + tty_port_tty_set(&port->port, NULL); } static int sierra_startup(struct usb_serial *serial) diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 283cf6b36b2c..1533d6e12238 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -755,7 +755,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -765,6 +765,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_insert_flip_char(tty, data[i], tty_flag); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 9a3e495c769c..c90237d48b0e 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -179,7 +179,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr); static int ti_get_lsr(struct ti_port *tport); static int ti_get_serial_info(struct ti_port *tport, struct serial_struct __user *ret_arg); -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg); static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); @@ -857,8 +857,8 @@ static int ti_ioctl(struct tty_struct *tty, struct file *file, (struct serial_struct __user *)arg); case TIOCSSERIAL: dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); - return ti_set_serial_info(tport, - (struct serial_struct __user *)arg); + return ti_set_serial_info(tty, tport, + (struct serial_struct __user *)arg); case TIOCMIWAIT: dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); cprev = tport->tp_icount; @@ -1211,6 +1211,7 @@ static void ti_bulk_in_callback(struct urb *urb) struct device *dev = &urb->dev->dev; int status = urb->status; int retval = 0; + struct tty_struct *tty; dbg("%s", __func__); @@ -1239,20 +1240,22 @@ static void ti_bulk_in_callback(struct urb *urb) return; } - if (port->port.tty && urb->actual_length) { + tty = tty_port_tty_get(&port->port); + if (tty && urb->actual_length) { usb_serial_debug_data(debug, dev, __func__, urb->actual_length, urb->transfer_buffer); if (!tport->tp_is_open) dbg("%s - port closed, dropping data", __func__); else - ti_recv(&urb->dev->dev, port->port.tty, + ti_recv(&urb->dev->dev, tty, urb->transfer_buffer, urb->actual_length); spin_lock(&tport->tp_lock); tport->tp_icount.rx += urb->actual_length; spin_unlock(&tport->tp_lock); + tty_kref_put(tty); } exit: @@ -1330,7 +1333,7 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = port->port.tty; /* FIXME */ + struct tty_struct *tty = tty_port_tty_get(&port->port); /* FIXME */ unsigned long flags; @@ -1338,19 +1341,15 @@ static void ti_send(struct ti_port *tport) spin_lock_irqsave(&tport->tp_lock, flags); - if (tport->tp_write_urb_in_use) { - spin_unlock_irqrestore(&tport->tp_lock, flags); - return; - } + if (tport->tp_write_urb_in_use) + goto unlock; count = ti_buf_get(tport->tp_write_buf, port->write_urb->transfer_buffer, port->bulk_out_size); - if (count == 0) { - spin_unlock_irqrestore(&tport->tp_lock, flags); - return; - } + if (count == 0) + goto unlock; tport->tp_write_urb_in_use = 1; @@ -1380,7 +1379,13 @@ static void ti_send(struct ti_port *tport) /* more room in the buffer for new writes, wakeup */ if (tty) tty_wakeup(tty); + tty_kref_put(tty); wake_up_interruptible(&tport->tp_write_wait); + return; +unlock: + spin_unlock_irqrestore(&tport->tp_lock, flags); + tty_kref_put(tty); + return; } @@ -1464,20 +1469,16 @@ static int ti_get_serial_info(struct ti_port *tport, } -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg) { - struct usb_serial_port *port = tport->tp_port; struct serial_struct new_serial; if (copy_from_user(&new_serial, new_arg, sizeof(new_serial))) return -EFAULT; tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - /* FIXME */ - if (port->port.tty) - port->port.tty->low_latency = - (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; tport->tp_closing_wait = new_serial.closing_wait; return 0; @@ -1510,7 +1511,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tport->tp_msr = msr & TI_MSR_MASK; /* handle CTS flow control */ - tty = tport->tp_port->port.tty; + tty = tty_port_tty_get(&tport->tp_port->port); if (tty && C_CRTSCTS(tty)) { if (msr & TI_MSR_CTS) { tty->hw_stopped = 0; @@ -1519,6 +1520,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tty->hw_stopped = 1; } } + tty_kref_put(tty); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4f7f9e3ae0a4..e7d4246027b2 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -214,7 +214,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) /* set up our port structure making the tty driver * remember our port object, and us it */ tty->driver_data = port; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); if (port->port.count == 1) { @@ -246,7 +246,7 @@ bailout_module_put: bailout_mutex_unlock: port->port.count = 0; tty->driver_data = NULL; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); mutex_unlock(&port->mutex); bailout_kref_put: usb_serial_put(serial); @@ -276,10 +276,11 @@ static void serial_close(struct tty_struct *tty, struct file *filp) port->serial->type->close(tty, port, filp); if (port->port.count == (port->console? 1 : 0)) { - if (port->port.tty) { - if (port->port.tty->driver_data) - port->port.tty->driver_data = NULL; - port->port.tty = NULL; + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty) { + if (tty->driver_data) + tty->driver_data = NULL; + tty_port_tty_set(&port->port, NULL); } } @@ -508,11 +509,12 @@ static void usb_serial_port_work(struct work_struct *work) if (!port) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) return; tty_wakeup(tty); + tty_kref_put(tty); } static void port_release(struct device *dev) @@ -819,6 +821,7 @@ int usb_serial_probe(struct usb_interface *interface, port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) goto probe_error; + tty_port_init(&port->port); port->serial = serial; spin_lock_init(&port->lock); mutex_init(&port->mutex); @@ -1040,8 +1043,11 @@ void usb_serial_disconnect(struct usb_interface *interface) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { - if (port->port.tty) - tty_hangup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } kill_traffic(port); } } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index cf8924f9a2cc..a6d1c75a1c89 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -499,7 +499,7 @@ static void visor_read_bulk_callback(struct urb *urb) int status = urb->status; struct tty_struct *tty; int result; - int available_room; + int available_room = 0; dbg("%s - port %d", __func__, port->number); @@ -512,13 +512,17 @@ static void visor_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; - if (tty && urb->actual_length) { - available_room = tty_buffer_request_room(tty, + if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); + if (tty) { + available_room = tty_buffer_request_room(tty, urb->actual_length); - if (available_room) { - tty_insert_flip_string(tty, data, available_room); - tty_flip_buffer_push(tty); + if (available_room) { + tty_insert_flip_string(tty, data, + available_room); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); } spin_lock(&priv->lock); priv->bytes_in += available_room; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 3a9d14384a43..11c8b97a5177 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1481,7 +1481,7 @@ static void rx_data_softint(struct work_struct *work) struct whiteheat_private *info = container_of(work, struct whiteheat_private, rx_work); struct usb_serial_port *port = info->port; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); struct whiteheat_urb_wrap *wrap; struct urb *urb; unsigned long flags; @@ -1493,7 +1493,7 @@ static void rx_data_softint(struct work_struct *work) spin_lock_irqsave(&info->lock, flags); if (info->flags & THROTTLED) { spin_unlock_irqrestore(&info->lock, flags); - return; + goto out; } list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { @@ -1513,7 +1513,7 @@ static void rx_data_softint(struct work_struct *work) spin_unlock_irqrestore(&info->lock, flags); tty_flip_buffer_push(tty); schedule_work(&info->rx_work); - return; + goto out; } tty_insert_flip_string(tty, urb->transfer_buffer, len); sent += len; @@ -1536,6 +1536,8 @@ static void rx_data_softint(struct work_struct *work) if (sent) tty_flip_buffer_push(tty); +out: + tty_kref_put(tty); } diff --git a/include/linux/tty.h b/include/linux/tty.h index b64d10b66548..c30ed8d3bcbd 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -182,6 +182,7 @@ struct signal_struct; struct tty_port { struct tty_struct *tty; /* Back pointer */ + spinlock_t lock; /* Lock protecting tty field */ int blocked_open; /* Waiting to open */ int count; /* Usage count */ wait_queue_head_t open_wait; /* Open waiters */ @@ -405,6 +406,8 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); extern void tty_port_init(struct tty_port *port); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern struct tty_struct *tty_port_tty_get(struct tty_port *port); +extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); -- cgit v1.2.3 From 2cb5998b5f0ccc886fdda3509059eef297b49577 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:40:30 +0100 Subject: tty: the vhangup syscall is racy We now have the infrastructure to sort this out but rather than teaching the syscall tty lock rules we move the hard work into a tty helper Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 19 +++++++++++++++++++ fs/open.c | 3 +-- include/linux/tty.h | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 913b50258f90..b5f57d0b30ee 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -729,6 +729,25 @@ void tty_vhangup(struct tty_struct *tty) EXPORT_SYMBOL(tty_vhangup); +/** + * tty_vhangup_self - process vhangup for own ctty + * + * Perform a vhangup on the current controlling tty + */ + +void tty_vhangup_self(void) +{ + struct tty_struct *tty; + + mutex_lock(&tty_mutex); + tty = get_current_tty(); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + mutex_unlock(&tty_mutex); +} + /** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty diff --git a/fs/open.c b/fs/open.c index 07da9359481c..5596049863bf 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1141,8 +1141,7 @@ EXPORT_SYMBOL(sys_close); asmlinkage long sys_vhangup(void) { if (capable(CAP_SYS_TTY_CONFIG)) { - /* XXX: this needs locking */ - tty_vhangup(current->signal->tty); + tty_vhangup_self(); return 0; } return -EPERM; diff --git a/include/linux/tty.h b/include/linux/tty.h index c30ed8d3bcbd..e00393a3d1c9 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -361,6 +361,7 @@ extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); extern void tty_hangup(struct tty_struct *tty); extern void tty_vhangup(struct tty_struct *tty); +extern void tty_vhangup_self(void); extern void tty_unhangup(struct file *filp); extern int tty_hung_up_p(struct file *filp); extern void do_SAK(struct tty_struct *tty); -- cgit v1.2.3 From feebed6515a113eeb33919e9557a8b9710ea627c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:41:30 +0100 Subject: tty: shutdown method Right now there are various drivers that try to use tty->count to know when they get the final close. Aristeau Rozanski showed while debugging the vt sysfs race that this isn't entirely safe. Instead of driver side tricks to work around this introduce a shutdown which is called when the tty is being destructed. This also means that the shutdown method is tied into the refcounting. Use this to rework the console close/sysfs logic. Remove lots of special case code from the tty core code. The pty code can now have a shutdown() method that replaces the special case hackery in the tree free up paths. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 15 ++++++++++---- drivers/char/tty_io.c | 49 +++++++++++++++++++++++++++------------------- drivers/char/vt.c | 34 ++++++++++++++++---------------- include/linux/tty.h | 3 ++- include/linux/tty_driver.h | 6 ++++++ 5 files changed, 65 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 76b27932d229..ec09c1cd4fe9 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -388,7 +388,14 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } -static const struct tty_operations pty_unix98_ops = { +static void pty_shutdown(struct tty_struct *tty) +{ + /* We have our own method as we don't use the tty index */ + kfree(tty->termios); + kfree(tty->termios_locked); +} + +static const struct tty_operations ptm_unix98_ops = { .open = pty_open, .close = pty_close, .write = pty_write, @@ -397,10 +404,10 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, - .ioctl = pty_unix98_ioctl + .ioctl = pty_unix98_ioctl, + .shutdown = pty_shutdown }; - static void __init unix98_pty_init(void) { ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); @@ -427,7 +434,7 @@ static void __init unix98_pty_init(void) ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; ptm_driver->other = pts_driver; - tty_set_operations(ptm_driver, &pty_unix98_ops); + tty_set_operations(ptm_driver, &ptm_unix98_ops); pts_driver->owner = THIS_MODULE; pts_driver->driver_name = "pty_slave"; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 2e96ce0fddc5..f91704d57a4e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1482,6 +1482,31 @@ release_mem_out: goto end_init; } +void tty_free_termios(struct tty_struct *tty) +{ + struct ktermios *tp; + int idx = tty->index; + /* Kill this flag and push into drivers for locking etc */ + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { + /* FIXME: Locking on ->termios array */ + tp = tty->termios; + tty->driver->termios[idx] = NULL; + kfree(tp); + + tp = tty->termios_locked; + tty->driver->termios_locked[idx] = NULL; + kfree(tp); + } +} +EXPORT_SYMBOL(tty_free_termios); + +void tty_shutdown(struct tty_struct *tty) +{ + tty->driver->ttys[tty->index] = NULL; + tty_free_termios(tty); +} +EXPORT_SYMBOL(tty_shutdown); + /** * release_one_tty - release tty structure memory * @kref: kref of tty we are obliterating @@ -1499,27 +1524,11 @@ static void release_one_tty(struct kref *kref) { struct tty_struct *tty = container_of(kref, struct tty_struct, kref); struct tty_driver *driver = tty->driver; - int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; - struct ktermios *tp; - int idx = tty->index; - - if (!devpts) - tty->driver->ttys[idx] = NULL; - - if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* FIXME: Locking on ->termios array */ - tp = tty->termios; - if (!devpts) - tty->driver->termios[idx] = NULL; - kfree(tp); - - tp = tty->termios_locked; - if (!devpts) - tty->driver->termios_locked[idx] = NULL; - kfree(tp); - } - + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + else + tty_shutdown(tty); tty->magic = 0; /* FIXME: locking on tty->driver->refcount */ tty->driver->refcount--; diff --git a/drivers/char/vt.c b/drivers/char/vt.c index ec94521c3118..37a45db5bae0 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2758,6 +2758,12 @@ static int con_open(struct tty_struct *tty, struct file *filp) ret = vc_allocate(currcons); if (ret == 0) { struct vc_data *vc = vc_cons[currcons].d; + + /* Still being freed */ + if (vc->vc_tty) { + release_console_sem(); + return -ERESTARTSYS; + } tty->driver_data = vc; vc->vc_tty = tty; @@ -2787,25 +2793,18 @@ static int con_open(struct tty_struct *tty, struct file *filp) */ static void con_close(struct tty_struct *tty, struct file *filp) { - mutex_lock(&tty_mutex); - acquire_console_sem(); - if (tty && tty->count == 1) { - struct vc_data *vc = tty->driver_data; + /* Nothing to do - we defer to shutdown */ +} - if (vc) - vc->vc_tty = NULL; - tty->driver_data = NULL; - vcs_remove_sysfs(tty); - release_console_sem(); - mutex_unlock(&tty_mutex); - /* - * tty_mutex is released, but we still hold BKL, so there is - * still exclusion against init_dev() - */ - return; - } +static void con_shutdown(struct tty_struct *tty) +{ + struct vc_data *vc = tty->driver_data; + BUG_ON(vc == NULL); + acquire_console_sem(); + vc->vc_tty = NULL; + vcs_remove_sysfs(tty); release_console_sem(); - mutex_unlock(&tty_mutex); + tty_shutdown(tty); } static int default_italic_color = 2; // green (ASCII) @@ -2930,6 +2929,7 @@ static const struct tty_operations con_ops = { .throttle = con_throttle, .unthrottle = con_unthrottle, .resize = vt_resize, + .shutdown = con_shutdown }; int __init vty_init(void) diff --git a/include/linux/tty.h b/include/linux/tty.h index e00393a3d1c9..6e39c705b9b6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -354,7 +354,8 @@ extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize *ws); - +extern void tty_shutdown(struct tty_struct *tty); +extern void tty_free_termios(struct tty_struct *tty); extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); extern int is_ignored(int sig); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index ac6e58e26b73..2322313a8589 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -21,6 +21,11 @@ * * Required method. * + * void (*shutdown)(struct tty_struct * tty); + * + * This routine is called when a particular tty device is closed for + * the last time freeing up the resources. + * * int (*write)(struct tty_struct * tty, * const unsigned char *buf, int count); * @@ -200,6 +205,7 @@ struct tty_driver; struct tty_operations { int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); + void (*shutdown)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count); int (*put_char)(struct tty_struct *tty, unsigned char ch); -- cgit v1.2.3 From d81ed10307027e1643a65ab5fe17cc01233d376d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:41:42 +0100 Subject: tty: Remove more special casing and out of place code Carry on pushing code out of tty_io when it belongs to other drivers. I'm not 100% happy with some of this and it will be worth revisiting some of the exports later when the restructuring work is done. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 78 ++++++++++++++++++++++ drivers/char/tty_io.c | 169 ++++++++++------------------------------------- drivers/char/tty_ioctl.c | 66 ++++++++++++++++-- drivers/char/vt.c | 30 ++++----- include/linux/tty.h | 6 ++ include/linux/vt_kern.h | 2 +- 6 files changed, 195 insertions(+), 156 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index ec09c1cd4fe9..328e8ac12306 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -332,6 +333,8 @@ int pty_limit = NR_UNIX98_PTY_DEFAULT; static int pty_limit_min = 0; static int pty_limit_max = NR_UNIX98_PTY_MAX; +static struct cdev ptmx_cdev; + static struct ctl_table pty_table[] = { { .ctl_name = PTY_MAX, @@ -408,6 +411,70 @@ static const struct tty_operations ptm_unix98_ops = { .shutdown = pty_shutdown }; + +/** + * ptmx_open - open a unix 98 pty master + * @inode: inode of device file + * @filp: file pointer to tty + * + * Allocate a unix98 pty master device from the ptmx driver. + * + * Locking: tty_mutex protects the init_dev work. tty->count should + * protect the rest. + * allocated_ptys_lock handles the list of free pty numbers + */ + +static int __ptmx_open(struct inode *inode, struct file *filp) +{ + struct tty_struct *tty; + int retval; + int index; + + nonseekable_open(inode, filp); + + /* find a device that is not in use. */ + index = devpts_new_index(); + if (index < 0) + return index; + + mutex_lock(&tty_mutex); + retval = tty_init_dev(ptm_driver, index, &tty, 1); + mutex_unlock(&tty_mutex); + + if (retval) + goto out; + + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ + filp->private_data = tty; + file_move(filp, &tty->tty_files); + + retval = devpts_pty_new(tty->link); + if (retval) + goto out1; + + retval = ptm_driver->ops->open(tty, filp); + if (!retval) + return 0; +out1: + tty_release_dev(filp); + return retval; +out: + devpts_kill_index(index); + return retval; +} + +static int ptmx_open(struct inode *inode, struct file *filp) +{ + int ret; + + lock_kernel(); + ret = __ptmx_open(inode, filp); + unlock_kernel(); + return ret; +} + +static struct file_operations ptmx_fops; + static void __init unix98_pty_init(void) { ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); @@ -459,7 +526,18 @@ static void __init unix98_pty_init(void) pty_table[1].data = &ptm_driver->refcount; register_sysctl_table(pty_root_table); + + /* Now create the /dev/ptmx special device */ + tty_default_fops(&ptmx_fops); + ptmx_fops.open = ptmx_open; + + cdev_init(&ptmx_cdev, &ptmx_fops); + if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || + register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) + panic("Couldn't register /dev/ptmx driver\n"); + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); } + #else static inline void unix98_pty_init(void) { } #endif diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index f91704d57a4e..fdcc43c8ef3c 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -49,7 +49,7 @@ * implement CONFIG_VT and generalize console device interface. * -- Marko Kohtala , March 97 * - * Rewrote init_dev and release_dev to eliminate races. + * Rewrote tty_init_dev and tty_release_dev to eliminate races. * -- Bill Hawes , June 97 * * Added devfs support. @@ -136,11 +136,6 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ DEFINE_MUTEX(tty_mutex); EXPORT_SYMBOL(tty_mutex); -#ifdef CONFIG_UNIX98_PTYS -extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ -static int ptmx_open(struct inode *, struct file *); -#endif - static void initialize_tty_struct(struct tty_struct *tty); static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); @@ -425,20 +420,6 @@ static const struct file_operations tty_fops = { .fasync = tty_fasync, }; -#ifdef CONFIG_UNIX98_PTYS -static const struct file_operations ptmx_fops = { - .llseek = no_llseek, - .read = tty_read, - .write = tty_write, - .poll = tty_poll, - .unlocked_ioctl = tty_ioctl, - .compat_ioctl = tty_compat_ioctl, - .open = ptmx_open, - .release = tty_release, - .fasync = tty_fasync, -}; -#endif - static const struct file_operations console_fops = { .llseek = no_llseek, .read = tty_read, @@ -1224,7 +1205,7 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) } /** - * init_dev - initialise a tty device + * tty_init_dev - initialise a tty device * @driver: tty driver we are opening a device on * @idx: device index * @ret_tty: returned tty structure @@ -1248,7 +1229,7 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) * relaxed for the (most common) case of reopening a tty. */ -static int init_dev(struct tty_driver *driver, int idx, +int tty_init_dev(struct tty_driver *driver, int idx, struct tty_struct **ret_tty, int first_ok) { struct tty_struct *tty, *o_tty; @@ -1269,8 +1250,8 @@ static int init_dev(struct tty_driver *driver, int idx, goto end_init; } /* - * It's safe from now on because init_dev() is called with - * tty_mutex held and release_dev() won't change tty->count + * It's safe from now on because tty_init_dev() is called with + * tty_mutex held and tty_release_dev() won't change tty->count * or tty->flags without having to grab tty_mutex */ if (tty && driver->subtype == PTY_TYPE_MASTER) @@ -1449,7 +1430,7 @@ fast_track: /* FIXME */ if (!test_bit(TTY_LDISC, &tty->flags)) - printk(KERN_ERR "init_dev but no ldisc\n"); + printk(KERN_ERR "tty_init_dev but no ldisc\n"); success: *ret_tty = tty; @@ -1476,7 +1457,7 @@ fail_no_mem: /* call the tty release_tty routine to clean out this slot */ release_mem_out: if (printk_ratelimit()) - printk(KERN_INFO "init_dev: ldisc open failed, " + printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); goto end_init; @@ -1587,7 +1568,7 @@ static void release_tty(struct tty_struct *tty, int idx) * WSH 09/09/97: rewritten to avoid some nasty race conditions that could * lead to double frees or releasing memory still in use. */ -static void release_dev(struct file *filp) +void tty_release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; @@ -1597,10 +1578,10 @@ static void release_dev(struct file *filp) tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, - "release_dev")) + "tty_release_dev")) return; - check_tty_count(tty, "release_dev"); + check_tty_count(tty, "tty_release_dev"); tty_fasync(-1, filp, 0); @@ -1612,24 +1593,24 @@ static void release_dev(struct file *filp) #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "release_dev: bad idx when trying to " + printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " "free (%s)\n", tty->name); return; } if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (tty != tty->driver->ttys[idx]) { - printk(KERN_DEBUG "release_dev: driver.table[%d] not tty " + printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " "for (%s)\n", idx, tty->name); return; } if (tty->termios != tty->driver->termios[idx]) { - printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios " + printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " "for (%s)\n", idx, tty->name); return; } if (tty->termios_locked != tty->driver->termios_locked[idx]) { - printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not " + printk(KERN_DEBUG "tty_release_dev: driver.termios_locked[%d] not " "termios_locked for (%s)\n", idx, tty->name); return; @@ -1638,7 +1619,7 @@ static void release_dev(struct file *filp) #endif #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "release_dev of %s (tty count=%d)...", + printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", tty_name(tty, buf), tty->count); #endif @@ -1646,26 +1627,26 @@ static void release_dev(struct file *filp) if (tty->driver->other && !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (o_tty != tty->driver->other->ttys[idx]) { - printk(KERN_DEBUG "release_dev: other->table[%d] " + printk(KERN_DEBUG "tty_release_dev: other->table[%d] " "not o_tty for (%s)\n", idx, tty->name); return; } if (o_tty->termios != tty->driver->other->termios[idx]) { - printk(KERN_DEBUG "release_dev: other->termios[%d] " + printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " "not o_termios for (%s)\n", idx, tty->name); return; } if (o_tty->termios_locked != tty->driver->other->termios_locked[idx]) { - printk(KERN_DEBUG "release_dev: other->termios_locked[" + printk(KERN_DEBUG "tty_release_dev: other->termios_locked[" "%d] not o_termios_locked for (%s)\n", idx, tty->name); return; } if (o_tty->link != tty) { - printk(KERN_DEBUG "release_dev: bad pty pointers\n"); + printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); return; } } @@ -1723,7 +1704,7 @@ static void release_dev(struct file *filp) if (!do_sleep) break; - printk(KERN_WARNING "release_dev: %s: read/write wait queue " + printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " "active!\n", tty_name(tty, buf)); mutex_unlock(&tty_mutex); schedule(); @@ -1736,14 +1717,14 @@ static void release_dev(struct file *filp) */ if (pty_master) { if (--o_tty->count < 0) { - printk(KERN_WARNING "release_dev: bad pty slave count " + printk(KERN_WARNING "tty_release_dev: bad pty slave count " "(%d) for %s\n", o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { - printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n", + printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", tty->count, tty_name(tty, buf)); tty->count = 0; } @@ -1825,7 +1806,7 @@ static void release_dev(struct file *filp) * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * - * Locking: tty_mutex protects tty, get_tty_driver and init_dev work. + * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand */ @@ -1889,7 +1870,7 @@ retry_open: return -ENODEV; } got_driver: - retval = init_dev(driver, index, &tty, 0); + retval = tty_init_dev(driver, index, &tty, 0); mutex_unlock(&tty_mutex); if (retval) return retval; @@ -1920,7 +1901,7 @@ got_driver: printk(KERN_DEBUG "error %d in opening %s...", retval, tty->name); #endif - release_dev(filp); + tty_release_dev(filp); if (retval != -ERESTARTSYS) return retval; if (signal_pending(current)) @@ -1959,69 +1940,6 @@ static int tty_open(struct inode *inode, struct file *filp) -#ifdef CONFIG_UNIX98_PTYS -/** - * ptmx_open - open a unix 98 pty master - * @inode: inode of device file - * @filp: file pointer to tty - * - * Allocate a unix98 pty master device from the ptmx driver. - * - * Locking: tty_mutex protects theinit_dev work. tty->count should - * protect the rest. - * allocated_ptys_lock handles the list of free pty numbers - */ - -static int __ptmx_open(struct inode *inode, struct file *filp) -{ - struct tty_struct *tty; - int retval; - int index; - - nonseekable_open(inode, filp); - - /* find a device that is not in use. */ - index = devpts_new_index(); - if (index < 0) - return index; - - mutex_lock(&tty_mutex); - retval = init_dev(ptm_driver, index, &tty, 1); - mutex_unlock(&tty_mutex); - - if (retval) - goto out; - - set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ - filp->private_data = tty; - file_move(filp, &tty->tty_files); - - retval = devpts_pty_new(tty->link); - if (retval) - goto out1; - - check_tty_count(tty, "ptmx_open"); - retval = ptm_driver->ops->open(tty, filp); - if (!retval) - return 0; -out1: - release_dev(filp); - return retval; -out: - devpts_kill_index(index); - return retval; -} - -static int ptmx_open(struct inode *inode, struct file *filp) -{ - int ret; - - lock_kernel(); - ret = __ptmx_open(inode, filp); - unlock_kernel(); - return ret; -} -#endif /** * tty_release - vfs callback for close @@ -2032,13 +1950,13 @@ static int ptmx_open(struct inode *inode, struct file *filp) * this tty. There may however be several such references. * * Locking: - * Takes bkl. See release_dev + * Takes bkl. See tty_release_dev */ static int tty_release(struct inode *inode, struct file *filp) { lock_kernel(); - release_dev(filp); + tty_release_dev(filp); unlock_kernel(); return 0; } @@ -2932,7 +2850,7 @@ int tty_put_char(struct tty_struct *tty, unsigned char ch) EXPORT_SYMBOL_GPL(tty_put_char); -static struct class *tty_class; +struct class *tty_class; /** * tty_register_device - register a tty device @@ -3197,6 +3115,11 @@ struct tty_struct *get_current_tty(void) } EXPORT_SYMBOL_GPL(get_current_tty); +void tty_default_fops(struct file_operations *fops) +{ + *fops = tty_fops; +} + /* * Initialize the console device. This is called *early*, so * we can't necessarily depend on lots of kernel help here. @@ -3234,12 +3157,6 @@ postcore_initcall(tty_class_init); /* 3/2004 jmc: why do these devices exist? */ static struct cdev tty_cdev, console_cdev; -#ifdef CONFIG_UNIX98_PTYS -static struct cdev ptmx_cdev; -#endif -#ifdef CONFIG_VT -static struct cdev vc0_cdev; -#endif /* * Ok, now we can initialize the rest of the tty devices and can count @@ -3251,32 +3168,18 @@ static int __init tty_init(void) if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) panic("Couldn't register /dev/tty driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty"); cdev_init(&console_cdev, &console_fops); if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) panic("Couldn't register /dev/console driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console"); -#ifdef CONFIG_UNIX98_PTYS - cdev_init(&ptmx_cdev, &ptmx_fops); - if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || - register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) - panic("Couldn't register /dev/ptmx driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); -#endif - #ifdef CONFIG_VT - cdev_init(&vc0_cdev, &console_fops); - if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || - register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) - panic("Couldn't register /dev/tty0 driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); - - vty_init(); + vty_init(&console_fops); #endif return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 30670851e51a..14cc19c344cc 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -40,6 +40,15 @@ #define TERMIOS_OLD 8 +/** + * tty_chars_in_buffer - characters pending + * @tty: terminal + * + * Return the number of bytes of data in the device private + * output queue. If no private method is supplied there is assumed + * to be no queue on the device. + */ + int tty_chars_in_buffer(struct tty_struct *tty) { if (tty->ops->chars_in_buffer) @@ -47,26 +56,49 @@ int tty_chars_in_buffer(struct tty_struct *tty) else return 0; } - EXPORT_SYMBOL(tty_chars_in_buffer); +/** + * tty_write_room - write queue space + * @tty: terminal + * + * Return the number of bytes that can be queued to this device + * at the present time. The result should be treated as a guarantee + * and the driver cannot offer a value it later shrinks by more than + * the number of bytes written. If no method is provided 2K is always + * returned and data may be lost as there will be no flow control. + */ + int tty_write_room(struct tty_struct *tty) { if (tty->ops->write_room) return tty->ops->write_room(tty); return 2048; } - EXPORT_SYMBOL(tty_write_room); +/** + * tty_driver_flush_buffer - discard internal buffer + * @tty: terminal + * + * Discard the internal output buffer for this device. If no method + * is provided then either the buffer cannot be hardware flushed or + * there is no buffer driver side. + */ void tty_driver_flush_buffer(struct tty_struct *tty) { if (tty->ops->flush_buffer) tty->ops->flush_buffer(tty); } - EXPORT_SYMBOL(tty_driver_flush_buffer); +/** + * tty_throttle - flow control + * @tty: terminal + * + * Indicate that a tty should stop transmitting data down the stack. + */ + void tty_throttle(struct tty_struct *tty) { /* check TTY_THROTTLED first so it indicates our state */ @@ -76,6 +108,13 @@ void tty_throttle(struct tty_struct *tty) } EXPORT_SYMBOL(tty_throttle); +/** + * tty_unthrottle - flow control + * @tty: terminal + * + * Indicate that a tty may continue transmitting data down the stack. + */ + void tty_unthrottle(struct tty_struct *tty) { if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && @@ -112,6 +151,11 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) } EXPORT_SYMBOL(tty_wait_until_sent); + +/* + * Termios Helper Methods + */ + static void unset_locked_termios(struct ktermios *termios, struct ktermios *old, struct ktermios *locked) @@ -346,6 +390,16 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, } EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); +/** + * tty_encode_baud_rate - set baud rate of the tty + * @ibaud: input baud rate + * @obad: output baud rate + * + * Update the current termios data for the tty with the new speed + * settings. The caller must hold the termios_mutex for the tty in + * question. + */ + void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) { tty_termios_encode_baud_rate(tty->termios, ibaud, obaud); @@ -430,7 +484,7 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * - * Locking: termios_sem + * Locking: termios_mutex */ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) @@ -508,7 +562,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) * functions before using change_termios to do the actual changes. * * Locking: - * Called functions take ldisc and termios_sem locks + * Called functions take ldisc and termios_mutex locks */ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -715,7 +769,7 @@ static void set_sgflags(struct ktermios *termios, int flags) * Updates a terminal from the legacy BSD style terminal information * structure. * - * Locking: termios_sem + * Locking: termios_mutex */ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 37a45db5bae0..57029fefd64a 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -100,10 +100,10 @@ #include #include #include - -#include +#include +#include #include -#include +#include #define MAX_NR_CON_DRIVER 16 @@ -2352,8 +2352,6 @@ rescan_last_byte: FLUSH console_conditional_schedule(); release_console_sem(); - -out: notify_update(vc); return n; #undef FLUSH @@ -2784,13 +2782,6 @@ static int con_open(struct tty_struct *tty, struct file *filp) return ret; } -/* - * We take tty_mutex in here to prevent another thread from coming in via init_dev - * and taking a ref against the tty while we're in the process of forgetting - * about it and cleaning things up. - * - * This is because vcs_remove_sysfs() can sleep and will drop the BKL. - */ static void con_close(struct tty_struct *tty, struct file *filp) { /* Nothing to do - we defer to shutdown */ @@ -2932,8 +2923,16 @@ static const struct tty_operations con_ops = { .shutdown = con_shutdown }; -int __init vty_init(void) +static struct cdev vc0_cdev; + +int __init vty_init(const struct file_operations *console_fops) { + cdev_init(&vc0_cdev, console_fops); + if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || + register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) + panic("Couldn't register /dev/tty0 driver\n"); + device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); + vcs_init(); console_driver = alloc_tty_driver(MAX_NR_CONSOLES); @@ -2952,7 +2951,6 @@ int __init vty_init(void) tty_set_operations(console_driver, &con_ops); if (tty_register_driver(console_driver)) panic("Couldn't register console driver\n"); - kbd_init(); console_map_init(); #ifdef CONFIG_PROM_CONSOLE @@ -3446,7 +3444,7 @@ int register_con_driver(const struct consw *csw, int first, int last) if (retval) goto err; - con_driver->dev = device_create_drvdata(vtconsole_class, NULL, + con_driver->dev = device_create(vtconsole_class, NULL, MKDEV(0, con_driver->node), NULL, "vtcon%i", con_driver->node); @@ -3557,7 +3555,7 @@ static int __init vtconsole_class_init(void) struct con_driver *con = ®istered_con_driver[i]; if (con->con && !con->dev) { - con->dev = device_create_drvdata(vtconsole_class, NULL, + con->dev = device_create(vtconsole_class, NULL, MKDEV(0, con->node), NULL, "vtcon%i", con->node); diff --git a/include/linux/tty.h b/include/linux/tty.h index 6e39c705b9b6..6cc7ccc93c69 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -314,6 +314,8 @@ extern int kmsg_redirect; extern void console_init(void); extern int vcs_init(void); +extern struct class *tty_class; + /** * tty_kref_get - get a tty reference * @tty: tty device @@ -398,6 +400,10 @@ extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg); extern dev_t tty_devnum(struct tty_struct *tty); extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); +extern void tty_default_fops(struct file_operations *fops); +extern int tty_init_dev(struct tty_driver *driver, int idx, + struct tty_struct **ret_tty, int first_ok); +extern void tty_release_dev(struct file *filp); extern struct mutex tty_mutex; diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 1cbd0a7db4e6..2f1113467f70 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -96,7 +96,7 @@ void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); extern int unbind_con_driver(const struct consw *csw, int first, int last, int deflt); -int vty_init(void); +int vty_init(const struct file_operations *console_fops); /* * vc_screen.c shares this temporary buffer with the console write code so that -- cgit v1.2.3 From 99f1fe189daf8e99a847e420567e49dd7ee2aae7 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:42:00 +0100 Subject: tty: Clean up the tty_init_dev changes further Fix up the naming, style and extract some bits of code into the driver specific code Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 49 ++++++++++++++++++++++++++++++++++- drivers/char/tty_io.c | 64 +++++++++++++++++----------------------------- include/linux/tty_driver.h | 9 +++++++ 3 files changed, 81 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 328e8ac12306..6e148ade7353 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -391,6 +391,41 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } +/** + * ptm_unix98_lookup - find a pty master + * @driver: ptm driver + * @idx: tty index + * + * Look up a pty master device. Called under the tty_mutex for now. + * This provides our locking. + */ + +static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, int idx) +{ + struct tty_struct *tty = devpts_get_tty(idx); + if (tty) + tty = tty->link; + return tty; +} + +/** + * pts_unix98_lookup - find a pty slave + * @driver: pts driver + * @idx: tty index + * + * Look up a pty master device. Called under the tty_mutex for now. + * This provides our locking. + */ + +static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, int idx) +{ + struct tty_struct *tty = devpts_get_tty(idx); + /* Master must be open before slave */ + if (!tty) + return ERR_PTR(-EIO); + return tty; +} + static void pty_shutdown(struct tty_struct *tty) { /* We have our own method as we don't use the tty index */ @@ -399,6 +434,7 @@ static void pty_shutdown(struct tty_struct *tty) } static const struct tty_operations ptm_unix98_ops = { + .lookup = ptm_unix98_lookup, .open = pty_open, .close = pty_close, .write = pty_write, @@ -411,6 +447,17 @@ static const struct tty_operations ptm_unix98_ops = { .shutdown = pty_shutdown }; +static const struct tty_operations pty_unix98_ops = { + .lookup = pts_unix98_lookup, + .open = pty_open, + .close = pty_close, + .write = pty_write, + .write_room = pty_write_room, + .flush_buffer = pty_flush_buffer, + .chars_in_buffer = pty_chars_in_buffer, + .unthrottle = pty_unthrottle, + .set_termios = pty_set_termios, +}; /** * ptmx_open - open a unix 98 pty master @@ -517,7 +564,7 @@ static void __init unix98_pty_init(void) pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; pts_driver->other = ptm_driver; - tty_set_operations(pts_driver, &pty_ops); + tty_set_operations(pts_driver, &pty_unix98_ops); if (tty_register_driver(ptm_driver)) panic("Couldn't register Unix98 ptm driver"); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a5408496d301..ac41af8763d1 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1204,53 +1204,38 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) sprintf(p, "%s%d", driver->name, index + driver->name_base); } -/* - * find_tty() - find an existing tty, if any - * @driver: the driver for the tty - * @idx: the minor number +/** + * tty_driver_lookup_tty() - find an existing tty, if any + * @driver: the driver for the tty + * @idx: the minor number * - * Return the tty, if found or ERR_PTR() otherwise. + * Return the tty, if found or ERR_PTR() otherwise. * - * Locking: tty_mutex must be held. If tty is found, the mutex must - * be held until the 'fast-open' is also done. + * Locking: tty_mutex must be held. If tty is found, the mutex must + * be held until the 'fast-open' is also done. Will change once we + * have refcounting in the driver and per driver locking */ -struct tty_struct *find_tty(struct tty_driver *driver, int idx) +struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, int idx) { struct tty_struct *tty; - /* check whether we're reopening an existing tty */ - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - tty = devpts_get_tty(idx); - /* - * If we don't have a tty here on a slave open, it's because - * the master already started the close process and there's - * no relation between devpts file and tty anymore. - */ - if (!tty && driver->subtype == PTY_TYPE_SLAVE) - return ERR_PTR(-EIO); + if (driver->ops->lookup) + return driver->ops->lookup(driver, idx); - /* - * tty is safe on because we are called with tty_mutex held - * and release_dev() won't change tty->count or tty->flags - * without grabbing tty_mutex. - */ - if (tty && driver->subtype == PTY_TYPE_MASTER) - tty = tty->link; - } else tty = driver->ttys[idx]; return tty; } -/* - * fast_tty_open() - fast re-open of an open tty - * @tty - the tty to open +/** + * tty_reopen() - fast re-open of an open tty + * @tty - the tty to open * - * Return 0 on success, -errno on error. + * Return 0 on success, -errno on error. * - * Locking: tty_mutex must be held from the time the tty was found - * till this open completes. + * Locking: tty_mutex must be held from the time the tty was found + * till this open completes. */ -static int fast_tty_open(struct tty_struct *tty) +static int tty_reopen(struct tty_struct *tty) { struct tty_driver *driver = tty->driver; @@ -1271,9 +1256,7 @@ static int fast_tty_open(struct tty_struct *tty) tty->count++; tty->driver = driver; /* N.B. why do this every time?? */ - /* FIXME */ - if (!test_bit(TTY_LDISC, &tty->flags)) - printk(KERN_ERR "fast_tty_open: no ldisc\n"); + WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); return 0; } @@ -1312,14 +1295,14 @@ int tty_init_dev(struct tty_driver *driver, int idx, int retval = 0; /* check whether we're reopening an existing tty */ - tty = find_tty(driver, idx); + tty = tty_driver_lookup_tty(driver, idx); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto end_init; } if (tty) { - retval = fast_tty_open(tty); + retval = tty_reopen(tty); if (retval) return retval; *ret_tty = tty; @@ -1440,6 +1423,8 @@ int tty_init_dev(struct tty_driver *driver, int idx, * All structures have been allocated, so now we install them. * Failures after this point use release_tty to clean up, so * there's no need to null out the local pointers. + * + * FIXME: We want a 'driver->install method ? */ if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) driver->ttys[idx] = tty; @@ -1466,9 +1451,8 @@ int tty_init_dev(struct tty_driver *driver, int idx, if (retval) goto release_mem_out; -success: - *ret_tty = tty; + *ret_tty = tty; /* All paths come through here to release the mutex */ end_init: return retval; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 2322313a8589..2c5c35c4656f 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -7,6 +7,14 @@ * defined; unless noted otherwise, they are optional, and can be * filled in with a null pointer. * + * struct tty_struct * (*lookup)(struct tty_driver *self, int idx) + * + * Return the tty device corresponding to idx, NULL if there is not + * one currently in use and an ERR_PTR value on error. Called under + * tty_mutex (for now!) + * + * Optional method. Default behaviour is to use the ttys array + * * int (*open)(struct tty_struct * tty, struct file * filp); * * This routine is called when a particular tty device is opened. @@ -203,6 +211,7 @@ struct tty_struct; struct tty_driver; struct tty_operations { + struct tty_struct * (*lookup)(struct tty_driver *driver, int idx); int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); void (*shutdown)(struct tty_struct *tty); -- cgit v1.2.3 From 7d7b93c1452f381350dbaf276a63357fa6559e6d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:42:09 +0100 Subject: tty: kref the tty driver object Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/ip2/ip2main.c | 6 +-- drivers/char/pty.c | 5 ++- drivers/char/tty_io.c | 110 +++++++++++++++++++++++++-------------------- drivers/net/wan/Kconfig | 2 +- include/linux/tty_driver.h | 15 +++++-- 5 files changed, 80 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 66f52a28c40b..6774572d3759 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -264,8 +264,8 @@ static int tracewrap; /**********/ #if defined(MODULE) && defined(IP2DEBUG_OPEN) -#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ - tty->name,(pCh->flags),ip2_tty_driver->refcount, \ +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \ + tty->name,(pCh->flags), \ tty->count,/*GET_USE_COUNT(module)*/0,s) #else #define DBG_CNT(s) @@ -2893,7 +2893,7 @@ ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg ) case 13: switch ( cmd ) { case 64: /* Driver - ip2stat */ - rc = put_user(ip2_tty_driver->refcount, pIndex++ ); + rc = put_user(-1, pIndex++ ); rc = put_user(irq_counter, pIndex++ ); rc = put_user(bh_counter, pIndex++ ); break; diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 6e148ade7353..0fdfa0517140 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -571,8 +571,11 @@ static void __init unix98_pty_init(void) if (tty_register_driver(pts_driver)) panic("Couldn't register Unix98 pts driver"); + /* FIXME: WTF */ +#if 0 pty_table[1].data = &ptm_driver->refcount; - register_sysctl_table(pty_root_table); +#endif + register_sysctl_table(pty_root_table); /* Now create the /dev/ptmx special device */ tty_default_fops(&ptmx_fops); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index ac41af8763d1..47aa437effe2 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -276,7 +276,7 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index) if (device < base || device >= base + p->num) continue; *index = device - base; - return p; + return tty_driver_kref_get(p); } return NULL; } @@ -320,7 +320,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) if (tty_line >= 0 && tty_line <= p->num && p->ops && p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) { - res = p; + res = tty_driver_kref_get(p); *line = tty_line; break; } @@ -1410,7 +1410,7 @@ int tty_init_dev(struct tty_driver *driver, int idx, *o_ltp_loc = o_ltp; o_tty->termios = *o_tp_loc; o_tty->termios_locked = *o_ltp_loc; - driver->other->refcount++; + tty_driver_kref_get(driver->other); if (driver->subtype == PTY_TYPE_MASTER) o_tty->count++; @@ -1438,7 +1438,7 @@ int tty_init_dev(struct tty_driver *driver, int idx, /* Compatibility until drivers always set this */ tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); - driver->refcount++; + tty_driver_kref_get(driver); tty->count++; /* @@ -1530,8 +1530,7 @@ static void release_one_tty(struct kref *kref) else tty_shutdown(tty); tty->magic = 0; - /* FIXME: locking on tty->driver->refcount */ - tty->driver->refcount--; + tty_driver_kref_put(driver); module_put(driver->owner); file_list_lock(); @@ -1854,7 +1853,7 @@ retry_open: mutex_unlock(&tty_mutex); return -ENXIO; } - driver = tty->driver; + driver = tty_driver_kref_get(tty->driver); index = tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ @@ -1865,14 +1864,14 @@ retry_open: #ifdef CONFIG_VT if (device == MKDEV(TTY_MAJOR, 0)) { extern struct tty_driver *console_driver; - driver = console_driver; + driver = tty_driver_kref_get(console_driver); index = fg_console; noctty = 1; goto got_driver; } #endif if (device == MKDEV(TTYAUX_MAJOR, 1)) { - driver = console_device(&index); + driver = tty_driver_kref_get(console_device(&index)); if (driver) { /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; @@ -1891,6 +1890,7 @@ retry_open: got_driver: retval = tty_init_dev(driver, index, &tty, 0); mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); if (retval) return retval; @@ -2866,7 +2866,6 @@ int tty_put_char(struct tty_struct *tty, unsigned char ch) return tty->ops->put_char(tty, ch); return tty->ops->write(tty, &ch, 1); } - EXPORT_SYMBOL_GPL(tty_put_char); struct class *tty_class; @@ -2909,6 +2908,7 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, return device_create_drvdata(tty_class, device, dev, NULL, name); } +EXPORT_SYMBOL(tty_register_device); /** * tty_unregister_device - unregister a tty device @@ -2926,8 +2926,6 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); } - -EXPORT_SYMBOL(tty_register_device); EXPORT_SYMBOL(tty_unregister_device); struct tty_driver *alloc_tty_driver(int lines) @@ -2936,27 +2934,70 @@ struct tty_driver *alloc_tty_driver(int lines) driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); if (driver) { + kref_init(&driver->kref); driver->magic = TTY_DRIVER_MAGIC; driver->num = lines; /* later we'll move allocation of tables here */ } return driver; } +EXPORT_SYMBOL(alloc_tty_driver); -void put_tty_driver(struct tty_driver *driver) +static void destruct_tty_driver(struct kref *kref) { + struct tty_driver *driver = container_of(kref, struct tty_driver, kref); + int i; + struct ktermios *tp; + void *p; + + if (driver->flags & TTY_DRIVER_INSTALLED) { + /* + * Free the termios and termios_locked structures because + * we don't want to get memory leaks when modular tty + * drivers are removed from the kernel. + */ + for (i = 0; i < driver->num; i++) { + tp = driver->termios[i]; + if (tp) { + driver->termios[i] = NULL; + kfree(tp); + } + tp = driver->termios_locked[i]; + if (tp) { + driver->termios_locked[i] = NULL; + kfree(tp); + } + if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) + tty_unregister_device(driver, i); + } + p = driver->ttys; + proc_tty_unregister_driver(driver); + driver->ttys = NULL; + driver->termios = driver->termios_locked = NULL; + kfree(p); + cdev_del(&driver->cdev); + } kfree(driver); } +void tty_driver_kref_put(struct tty_driver *driver) +{ + kref_put(&driver->kref, destruct_tty_driver); +} +EXPORT_SYMBOL(tty_driver_kref_put); + void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { driver->ops = op; }; +EXPORT_SYMBOL(tty_set_operations); -EXPORT_SYMBOL(alloc_tty_driver); +void put_tty_driver(struct tty_driver *d) +{ + tty_driver_kref_put(d); +} EXPORT_SYMBOL(put_tty_driver); -EXPORT_SYMBOL(tty_set_operations); /* * Called by a tty driver to register itself. @@ -2968,9 +3009,6 @@ int tty_register_driver(struct tty_driver *driver) dev_t dev; void **p = NULL; - if (driver->flags & TTY_DRIVER_INSTALLED) - return 0; - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL); if (!p) @@ -3024,6 +3062,7 @@ int tty_register_driver(struct tty_driver *driver) tty_register_device(driver, i, NULL); } proc_tty_register_driver(driver); + driver->flags |= TTY_DRIVER_INSTALLED; return 0; } @@ -3034,46 +3073,19 @@ EXPORT_SYMBOL(tty_register_driver); */ int tty_unregister_driver(struct tty_driver *driver) { - int i; - struct ktermios *tp; - void *p; - +#if 0 + /* FIXME */ if (driver->refcount) return -EBUSY; - +#endif unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); mutex_lock(&tty_mutex); list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); - - /* - * Free the termios and termios_locked structures because - * we don't want to get memory leaks when modular tty - * drivers are removed from the kernel. - */ - for (i = 0; i < driver->num; i++) { - tp = driver->termios[i]; - if (tp) { - driver->termios[i] = NULL; - kfree(tp); - } - tp = driver->termios_locked[i]; - if (tp) { - driver->termios_locked[i] = NULL; - kfree(tp); - } - if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) - tty_unregister_device(driver, i); - } - p = driver->ttys; - proc_tty_unregister_driver(driver); - driver->ttys = NULL; - driver->termios = driver->termios_locked = NULL; - kfree(p); - cdev_del(&driver->cdev); return 0; } + EXPORT_SYMBOL(tty_unregister_driver); dev_t tty_devnum(struct tty_struct *tty) diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index 2ae2ec40015d..21efd99b9294 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -205,7 +205,7 @@ config WANXL_BUILD_FIRMWARE config PC300 tristate "Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)" - depends on HDLC && PCI + depends on HDLC && PCI && BROKEN ---help--- Driver for the Cyclades-PC300 synchronous communication boards. diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 2c5c35c4656f..ba891dd23550 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -253,6 +253,7 @@ struct tty_operations { struct tty_driver { int magic; /* magic number for this structure */ + struct kref kref; /* Reference management */ struct cdev cdev; struct module *owner; const char *driver_name; @@ -266,7 +267,6 @@ struct tty_driver { short subtype; /* subtype of tty driver */ struct ktermios init_termios; /* Initial termios */ int flags; /* tty driver flags */ - int refcount; /* for loadable tty drivers */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ @@ -288,12 +288,19 @@ struct tty_driver { extern struct list_head tty_drivers; -struct tty_driver *alloc_tty_driver(int lines); -void put_tty_driver(struct tty_driver *driver); -void tty_set_operations(struct tty_driver *driver, +extern struct tty_driver *alloc_tty_driver(int lines); +extern void put_tty_driver(struct tty_driver *driver); +extern void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op); extern struct tty_driver *tty_find_polling_driver(char *name, int *line); +extern void tty_driver_kref_put(struct tty_driver *driver); +extern inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) +{ + kref_get(&d->kref); + return d; +} + /* tty driver magic number */ #define TTY_DRIVER_MAGIC 0x5402 -- cgit v1.2.3 From 8b0a88d5912ab549d5adac2c8498ecdaae5319a5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:42:19 +0100 Subject: tty: More driver operations We have the lookup operation abstracted which is nice for pty cleanup but we really want to abstract the add/remove entries as well so that we can pull the pty code out of the tty core and create a clear defined interface for the tty driver table. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 16 +++++++++++++ drivers/char/tty_io.c | 57 ++++++++++++++++++++++++++++++++++++++-------- include/linux/tty_driver.h | 16 +++++++++++++ 3 files changed, 79 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 0fdfa0517140..4e6490bda751 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -433,8 +433,22 @@ static void pty_shutdown(struct tty_struct *tty) kfree(tty->termios_locked); } +/* We have no need to install and remove our tty objects as devpts does all + the work for us */ + +static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + return 0; +} + +static void pty_remove(struct tty_driver *driver, struct tty_struct *tty) +{ +} + static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, + .install = pty_install, + .remove = pty_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -449,6 +463,8 @@ static const struct tty_operations ptm_unix98_ops = { static const struct tty_operations pty_unix98_ops = { .lookup = pts_unix98_lookup, + .install = pty_install, + .remove = pty_remove, .open = pty_open, .close = pty_close, .write = pty_write, diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 47aa437effe2..888380f573dc 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1189,7 +1189,7 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) } /** - * pty_line_name - generate name for a tty + * tty_line_name - generate name for a tty * @driver: the tty driver in use * @index: the minor number * @p: output buffer of at least 7 bytes @@ -1222,13 +1222,51 @@ struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, int idx) if (driver->ops->lookup) return driver->ops->lookup(driver, idx); - tty = driver->ttys[idx]; + tty = driver->ttys[idx]; return tty; } /** - * tty_reopen() - fast re-open of an open tty - * @tty - the tty to open + * tty_driver_install_tty() - install a tty entry in the driver + * @driver: the driver for the tty + * @tty: the tty + * + * Install a tty object into the driver tables. The tty->index field + * will be set by the time this is called. + * + * Locking: tty_mutex for now + */ +static int tty_driver_install_tty(struct tty_driver *driver, + struct tty_struct *tty) +{ + if (driver->ops->install) + return driver->ops->install(driver, tty); + driver->ttys[tty->index] = tty; + return 0; +} + +/** + * tty_driver_remove_tty() - remove a tty from the driver tables + * @driver: the driver for the tty + * @idx: the minor number + * + * Remvoe a tty object from the driver tables. The tty->index field + * will be set by the time this is called. + * + * Locking: tty_mutex for now + */ +static void tty_driver_remove_tty(struct tty_driver *driver, + struct tty_struct *tty) +{ + if (driver->ops->remove) + driver->ops->remove(driver, tty); + else + driver->ttys[tty->index] = NULL; +} + +/* + * tty_reopen() - fast re-open of an open tty + * @tty - the tty to open * * Return 0 on success, -errno on error. * @@ -1423,11 +1461,7 @@ int tty_init_dev(struct tty_driver *driver, int idx, * All structures have been allocated, so now we install them. * Failures after this point use release_tty to clean up, so * there's no need to null out the local pointers. - * - * FIXME: We want a 'driver->install method ? */ - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) - driver->ttys[idx] = tty; if (!*tp_loc) *tp_loc = tp; @@ -1441,6 +1475,9 @@ int tty_init_dev(struct tty_driver *driver, int idx, tty_driver_kref_get(driver); tty->count++; + if (tty_driver_install_tty(driver, tty) < 0) + goto release_mem_out; + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -1502,7 +1539,7 @@ EXPORT_SYMBOL(tty_free_termios); void tty_shutdown(struct tty_struct *tty) { - tty->driver->ttys[tty->index] = NULL; + tty_driver_remove_tty(tty->driver, tty); tty_free_termios(tty); } EXPORT_SYMBOL(tty_shutdown); @@ -1615,7 +1652,7 @@ void tty_release_dev(struct file *filp) "free (%s)\n", tty->name); return; } - if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { + if (!devpts) { if (tty != tty->driver->ttys[idx]) { printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " "for (%s)\n", idx, tty->name); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index ba891dd23550..005d06ad46a6 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -15,6 +15,20 @@ * * Optional method. Default behaviour is to use the ttys array * + * int (*install)(struct tty_driver *self, struct tty_struct *tty) + * + * Install a new tty into the tty driver internal tables. Used in + * conjunction with lookup and remove methods. + * + * Optional method. Default behaviour is to use the ttys array + * + * void (*remove)(struct tty_driver *self, struct tty_struct *tty) + * + * Remove a closed tty from the tty driver internal tables. Used in + * conjunction with lookup and remove methods. + * + * Optional method. Default behaviour is to use the ttys array + * * int (*open)(struct tty_struct * tty, struct file * filp); * * This routine is called when a particular tty device is opened. @@ -212,6 +226,8 @@ struct tty_driver; struct tty_operations { struct tty_struct * (*lookup)(struct tty_driver *driver, int idx); + int (*install)(struct tty_driver *driver, struct tty_struct *tty); + void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); void (*shutdown)(struct tty_struct *tty); -- cgit v1.2.3 From 73ec06fc5f5c8e1097a7a4a4ab2d7c6c3a007e81 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:42:29 +0100 Subject: tty: Finish fixing up the init_dev interface to use ERR_PTR Original suggestion and proposal from Sukadev Bhattiprolu. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 6 ++++-- drivers/char/tty_io.c | 52 +++++++++++++++++++++------------------------------ include/linux/tty.h | 4 ++-- 3 files changed, 27 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 4e6490bda751..c98450023030 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -501,11 +501,13 @@ static int __ptmx_open(struct inode *inode, struct file *filp) return index; mutex_lock(&tty_mutex); - retval = tty_init_dev(ptm_driver, index, &tty, 1); + tty = tty_init_dev(ptm_driver, index, 1); mutex_unlock(&tty_mutex); - if (retval) + if (IS_ERR(tty)) { + retval = PTR_ERR(tty); goto out; + } set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ filp->private_data = tty; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 888380f573dc..b0ad4880c3a8 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1324,35 +1324,32 @@ static int tty_reopen(struct tty_struct *tty) * relaxed for the (most common) case of reopening a tty. */ -int tty_init_dev(struct tty_driver *driver, int idx, - struct tty_struct **ret_tty, int first_ok) +struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok) { struct tty_struct *tty, *o_tty; struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; - int retval = 0; + int retval; /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, idx); - if (IS_ERR(tty)) { - retval = PTR_ERR(tty); - goto end_init; - } + + if (IS_ERR(tty)) + return tty; if (tty) { retval = tty_reopen(tty); if (retval) - return retval; - *ret_tty = tty; - return 0; + return ERR_PTR(retval); + return tty; } /* Check if pty master is being opened multiple times */ if (driver->subtype == PTY_TYPE_MASTER && - (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) { - retval = -EIO; - goto end_init; - } + (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) + return ERR_PTR(-EIO); + /* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the @@ -1361,10 +1358,8 @@ int tty_init_dev(struct tty_driver *driver, int idx, * and locked termios may be retained.) */ - if (!try_module_get(driver->owner)) { - retval = -ENODEV; - goto end_init; - } + if (!try_module_get(driver->owner)) + return ERR_PTR(-ENODEV); o_tty = NULL; tp = o_tp = NULL; @@ -1475,7 +1470,8 @@ int tty_init_dev(struct tty_driver *driver, int idx, tty_driver_kref_get(driver); tty->count++; - if (tty_driver_install_tty(driver, tty) < 0) + retval = tty_driver_install_tty(driver, tty); + if (retval < 0) goto release_mem_out; /* @@ -1485,14 +1481,9 @@ int tty_init_dev(struct tty_driver *driver, int idx, */ retval = tty_ldisc_setup(tty, o_tty); - if (retval) goto release_mem_out; - - *ret_tty = tty; - /* All paths come through here to release the mutex */ -end_init: - return retval; + return tty; /* Release locally allocated memory ... nothing placed in slots */ free_mem_out: @@ -1507,8 +1498,7 @@ free_mem_out: fail_no_mem: module_put(driver->owner); - retval = -ENOMEM; - goto end_init; + return ERR_PTR(-ENOMEM); /* call the tty release_tty routine to clean out this slot */ release_mem_out: @@ -1516,7 +1506,7 @@ release_mem_out: printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); - goto end_init; + return ERR_PTR(retval); } void tty_free_termios(struct tty_struct *tty) @@ -1925,11 +1915,11 @@ retry_open: return -ENODEV; } got_driver: - retval = tty_init_dev(driver, index, &tty, 0); + tty = tty_init_dev(driver, index, 0); mutex_unlock(&tty_mutex); tty_driver_kref_put(driver); - if (retval) - return retval; + if (IS_ERR(tty)) + return PTR_ERR(tty); filp->private_data = tty; file_move(filp, &tty->tty_files); diff --git a/include/linux/tty.h b/include/linux/tty.h index 6cc7ccc93c69..54523a37e956 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -401,8 +401,8 @@ extern dev_t tty_devnum(struct tty_struct *tty); extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); extern void tty_default_fops(struct file_operations *fops); -extern int tty_init_dev(struct tty_driver *driver, int idx, - struct tty_struct **ret_tty, int first_ok); +extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok); extern void tty_release_dev(struct file *filp); extern struct mutex tty_mutex; -- cgit v1.2.3 From bf970ee46e0fb363c8df4393229121d54330a98e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:42:39 +0100 Subject: tty: extract the pty init time special cases The majority of the remaining init_dev code is pty special cases. We refactor this code into the driver->install method. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 126 +++++++++++++++++++++++++++++--- drivers/char/tty_io.c | 198 +++++++++++++++++--------------------------------- include/linux/tty.h | 5 ++ 3 files changed, 187 insertions(+), 142 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index c98450023030..c5a192dd00db 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -227,7 +227,58 @@ static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios tty->termios->c_cflag |= (CS8 | CREAD); } +static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct tty_struct *o_tty; + int idx = tty->index; + int retval; + + o_tty = alloc_tty_struct(); + if (!o_tty) + return -ENOMEM; + if (!try_module_get(driver->other->owner)) { + /* This cannot in fact currently happen */ + free_tty_struct(o_tty); + return -ENOMEM; + } + initialize_tty_struct(o_tty, driver->other, idx); + + /* We always use new tty termios data so we can do this + the easy way .. */ + retval = tty_init_termios(tty); + if (retval) + goto free_mem_out; + + retval = tty_init_termios(o_tty); + if (retval) { + tty_free_termios(tty); + goto free_mem_out; + } + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->ttys[idx] = o_tty; + tty_driver_kref_get(driver->other); + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; + + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; +free_mem_out: + module_put(o_tty->driver->owner); + free_tty_struct(o_tty); + return -ENOMEM; +} + + static const struct tty_operations pty_ops = { + .install = pty_install, .open = pty_open, .close = pty_close, .write = pty_write, @@ -332,6 +383,7 @@ static inline void legacy_pty_init(void) { } int pty_limit = NR_UNIX98_PTY_DEFAULT; static int pty_limit_min = 0; static int pty_limit_max = NR_UNIX98_PTY_MAX; +static int pty_count = 0; static struct cdev ptmx_cdev; @@ -351,6 +403,7 @@ static struct ctl_table pty_table[] = { .procname = "nr", .maxlen = sizeof(int), .mode = 0444, + .data = &pty_count, .proc_handler = &proc_dointvec, }, { .ctl_name = 0 @@ -426,7 +479,7 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, int idx) return tty; } -static void pty_shutdown(struct tty_struct *tty) +static void pty_unix98_shutdown(struct tty_struct *tty) { /* We have our own method as we don't use the tty index */ kfree(tty->termios); @@ -436,19 +489,71 @@ static void pty_shutdown(struct tty_struct *tty) /* We have no need to install and remove our tty objects as devpts does all the work for us */ -static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) { + struct tty_struct *o_tty; + int idx = tty->index; + + o_tty = alloc_tty_struct(); + if (!o_tty) + return -ENOMEM; + if (!try_module_get(driver->other->owner)) { + /* This cannot in fact currently happen */ + free_tty_struct(o_tty); + return -ENOMEM; + } + initialize_tty_struct(o_tty, driver->other, idx); + + tty->termios = kmalloc(sizeof(struct ktermios), GFP_KERNEL); + if (tty->termios == NULL) + goto free_mem_out; + *tty->termios = driver->init_termios; + tty->termios_locked = kzalloc(sizeof(struct ktermios), GFP_KERNEL); + if (tty->termios_locked == NULL) + goto free_mem_out; + o_tty->termios = kmalloc(sizeof(struct ktermios), GFP_KERNEL); + if (o_tty->termios == NULL) + goto free_mem_out; + *o_tty->termios = driver->other->init_termios; + o_tty->termios_locked = kzalloc(sizeof(struct ktermios), GFP_KERNEL); + if (o_tty->termios_locked == NULL) + goto free_mem_out; + + tty_driver_kref_get(driver->other); + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_tty to clean up, so + * there's no need to null out the local pointers. + */ + tty_driver_kref_get(driver); + tty->count++; + pty_count++; return 0; +free_mem_out: + kfree(o_tty->termios); + module_put(o_tty->driver->owner); + free_tty_struct(o_tty); + kfree(tty->termios_locked); + kfree(tty->termios); + free_tty_struct(tty); + module_put(driver->owner); + return -ENOMEM; } -static void pty_remove(struct tty_driver *driver, struct tty_struct *tty) +static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { + pty_count--; } static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, - .install = pty_install, - .remove = pty_remove, + .install = pty_unix98_install, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -458,13 +563,13 @@ static const struct tty_operations ptm_unix98_ops = { .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, - .shutdown = pty_shutdown + .shutdown = pty_unix98_shutdown }; static const struct tty_operations pty_unix98_ops = { .lookup = pts_unix98_lookup, - .install = pty_install, - .remove = pty_remove, + .install = pty_unix98_install, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -473,6 +578,7 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .shutdown = pty_unix98_shutdown }; /** @@ -589,10 +695,6 @@ static void __init unix98_pty_init(void) if (tty_register_driver(pts_driver)) panic("Couldn't register Unix98 pts driver"); - /* FIXME: WTF */ -#if 0 - pty_table[1].data = &ptm_driver->refcount; -#endif register_sysctl_table(pty_root_table); /* Now create the /dev/ptmx special device */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index b0ad4880c3a8..e881e9ed08de 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -136,8 +136,6 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ DEFINE_MUTEX(tty_mutex); EXPORT_SYMBOL(tty_mutex); -static void initialize_tty_struct(struct tty_struct *tty); - static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); ssize_t redirected_tty_write(struct file *, const char __user *, @@ -166,7 +164,7 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); * Locking: none */ -static struct tty_struct *alloc_tty_struct(void) +struct tty_struct *alloc_tty_struct(void) { return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); } @@ -180,7 +178,7 @@ static struct tty_struct *alloc_tty_struct(void) * Locking: none. Must be called after tty is definitely unused */ -static inline void free_tty_struct(struct tty_struct *tty) +void free_tty_struct(struct tty_struct *tty) { kfree(tty->write_buf); tty_buffer_free_all(tty); @@ -1226,23 +1224,71 @@ struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, int idx) return tty; } +/** + * tty_init_termios - helper for termios setup + * @tty: the tty to set up + * + * Initialise the termios structures for this tty. Thus runs under + * the tty_mutex currently so we can be relaxed about ordering. + */ + +int tty_init_termios(struct tty_struct *tty) +{ + struct ktermios *tp, *ltp; + int idx = tty->index; + + tp = tty->driver->termios[idx]; + ltp = tty->driver->termios_locked[idx]; + if (tp == NULL) { + WARN_ON(ltp != NULL); + tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); + ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); + if (tp == NULL || ltp == NULL) { + kfree(tp); + kfree(ltp); + return -ENOMEM; + } + memcpy(tp, &tty->driver->init_termios, + sizeof(struct ktermios)); + tty->driver->termios[idx] = tp; + tty->driver->termios_locked[idx] = ltp; + } + tty->termios = tp; + tty->termios_locked = ltp; + + /* Compatibility until drivers always set this */ + tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); + tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); + return 0; +} + /** * tty_driver_install_tty() - install a tty entry in the driver * @driver: the driver for the tty * @tty: the tty * * Install a tty object into the driver tables. The tty->index field - * will be set by the time this is called. + * will be set by the time this is called. This method is responsible + * for ensuring any need additional structures are allocated and + * configured. * * Locking: tty_mutex for now */ static int tty_driver_install_tty(struct tty_driver *driver, struct tty_struct *tty) { + int idx = tty->index; + if (driver->ops->install) return driver->ops->install(driver, tty); - driver->ttys[tty->index] = tty; - return 0; + + if (tty_init_termios(tty) == 0) { + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; + } + return -ENOMEM; } /** @@ -1327,9 +1373,7 @@ static int tty_reopen(struct tty_struct *tty) struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, int first_ok) { - struct tty_struct *tty, *o_tty; - struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; - struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; + struct tty_struct *tty; int retval; /* check whether we're reopening an existing tty */ @@ -1361,118 +1405,17 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, if (!try_module_get(driver->owner)) return ERR_PTR(-ENODEV); - o_tty = NULL; - tp = o_tp = NULL; - ltp = o_ltp = NULL; - tty = alloc_tty_struct(); if (!tty) goto fail_no_mem; - initialize_tty_struct(tty); - tty->driver = driver; - tty->ops = driver->ops; - tty->index = idx; - tty_line_name(driver, idx, tty->name); - - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - tp_loc = &tty->termios; - ltp_loc = &tty->termios_locked; - } else { - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; - } - - if (!*tp_loc) { - tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!tp) - goto free_mem_out; - *tp = driver->init_termios; - } - - if (!*ltp_loc) { - ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!ltp) - goto free_mem_out; - } - - if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty = alloc_tty_struct(); - if (!o_tty) - goto free_mem_out; - if (!try_module_get(driver->other->owner)) { - /* This cannot in fact currently happen */ - free_tty_struct(o_tty); - o_tty = NULL; - goto free_mem_out; - } - initialize_tty_struct(o_tty); - o_tty->driver = driver->other; - o_tty->ops = driver->ops; - o_tty->index = idx; - tty_line_name(driver->other, idx, o_tty->name); - - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - o_tp_loc = &o_tty->termios; - o_ltp_loc = &o_tty->termios_locked; - } else { - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; - } - - if (!*o_tp_loc) { - o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!o_tp) - goto free_mem_out; - *o_tp = driver->other->init_termios; - } - - if (!*o_ltp_loc) { - o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!o_ltp) - goto free_mem_out; - } - - /* - * Everything allocated ... set up the o_tty structure. - */ - if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) - driver->other->ttys[idx] = o_tty; - if (!*o_tp_loc) - *o_tp_loc = o_tp; - if (!*o_ltp_loc) - *o_ltp_loc = o_ltp; - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - tty_driver_kref_get(driver->other); - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; - } - - /* - * All structures have been allocated, so now we install them. - * Failures after this point use release_tty to clean up, so - * there's no need to null out the local pointers. - */ - - if (!*tp_loc) - *tp_loc = tp; - if (!*ltp_loc) - *ltp_loc = ltp; - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - /* Compatibility until drivers always set this */ - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); - tty_driver_kref_get(driver); - tty->count++; + initialize_tty_struct(tty, driver, idx); retval = tty_driver_install_tty(driver, tty); - if (retval < 0) - goto release_mem_out; + if (retval < 0) { + free_tty_struct(tty); + module_put(driver->owner); + return ERR_PTR(retval); + } /* * Structures all installed ... call the ldisc open routines. @@ -1480,22 +1423,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, * to decrement the use counts, as release_tty doesn't care. */ - retval = tty_ldisc_setup(tty, o_tty); + retval = tty_ldisc_setup(tty, tty->link); if (retval) goto release_mem_out; return tty; - /* Release locally allocated memory ... nothing placed in slots */ -free_mem_out: - kfree(o_tp); - if (o_tty) { - module_put(o_tty->driver->owner); - free_tty_struct(o_tty); - } - kfree(ltp); - kfree(tp); - free_tty_struct(tty); - fail_no_mem: module_put(driver->owner); return ERR_PTR(-ENOMEM); @@ -2852,7 +2784,8 @@ EXPORT_SYMBOL(do_SAK); * Locking: none - tty in question must not be exposed at this point */ -static void initialize_tty_struct(struct tty_struct *tty) +void initialize_tty_struct(struct tty_struct *tty, + struct tty_driver *driver, int idx) { memset(tty, 0, sizeof(struct tty_struct)); kref_init(&tty->kref); @@ -2873,6 +2806,11 @@ static void initialize_tty_struct(struct tty_struct *tty) spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); + + tty->driver = driver; + tty->ops = driver->ops; + tty->index = idx; + tty_line_name(driver, idx, tty->name); } /** diff --git a/include/linux/tty.h b/include/linux/tty.h index 54523a37e956..3c7c75794a4a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -401,9 +401,14 @@ extern dev_t tty_devnum(struct tty_struct *tty); extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); extern void tty_default_fops(struct file_operations *fops); +extern struct tty_struct *alloc_tty_struct(void); +extern void free_tty_struct(struct tty_struct *tty); +extern void initialize_tty_struct(struct tty_struct *tty, + struct tty_driver *driver, int idx); extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, int first_ok); extern void tty_release_dev(struct file *filp); +extern int tty_init_termios(struct tty_struct *tty); extern struct mutex tty_mutex; -- cgit v1.2.3 From 15f1a6338ddd4e69fff965d4b3a0e1bfb7a13d9c Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 13 Oct 2008 10:42:59 +0100 Subject: Add an instance parameter devpts interfaces Pass-in 'inode' or 'tty' parameter to devpts interfaces. With multiple devpts instances, these parameters will be used in subsequent patches to identify the instance of devpts mounted. The parameters also help simplify devpts implementation. Changelog[v3]: - minor changes due to merge with ttydev updates - rename parameters to emphasize they are ptmx or pts inodes - pass-in tty_struct * to devpts_pty_kill() (this will help cleanup the get_node() call in a subsequent patch) Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 18 ++++++++++-------- drivers/char/tty_io.c | 14 ++++++++------ fs/devpts/inode.c | 11 ++++++----- include/linux/devpts_fs.h | 31 +++++++++++++++++++++---------- include/linux/tty_driver.h | 3 ++- 5 files changed, 47 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index c5a192dd00db..a391badef52a 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -60,7 +60,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp) set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) - devpts_pty_kill(tty->index); + devpts_pty_kill(tty->link); #endif tty_vhangup(tty->link); } @@ -453,9 +453,10 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, * This provides our locking. */ -static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, int idx) +static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, + struct inode *ptm_inode, int idx) { - struct tty_struct *tty = devpts_get_tty(idx); + struct tty_struct *tty = devpts_get_tty(ptm_inode, idx); if (tty) tty = tty->link; return tty; @@ -470,9 +471,10 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, int idx) * This provides our locking. */ -static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, int idx) +static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, + struct inode *pts_inode, int idx) { - struct tty_struct *tty = devpts_get_tty(idx); + struct tty_struct *tty = devpts_get_tty(pts_inode, idx); /* Master must be open before slave */ if (!tty) return ERR_PTR(-EIO); @@ -602,7 +604,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); /* find a device that is not in use. */ - index = devpts_new_index(); + index = devpts_new_index(inode); if (index < 0) return index; @@ -619,7 +621,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp) filp->private_data = tty; file_move(filp, &tty->tty_files); - retval = devpts_pty_new(tty->link); + retval = devpts_pty_new(inode, tty->link); if (retval) goto out1; @@ -630,7 +632,7 @@ out1: tty_release_dev(filp); return retval; out: - devpts_kill_index(index); + devpts_kill_index(inode, index); return retval; } diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 36098ee8fe65..959083961024 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1213,12 +1213,13 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) * be held until the 'fast-open' is also done. Will change once we * have refcounting in the driver and per driver locking */ -struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, int idx) +struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, + struct inode *inode, int idx) { struct tty_struct *tty; if (driver->ops->lookup) - return driver->ops->lookup(driver, idx); + return driver->ops->lookup(driver, inode, idx); tty = driver->ttys[idx]; return tty; @@ -1539,10 +1540,11 @@ void tty_release_dev(struct file *filp) int devpts; int idx; char buf[64]; + struct inode *inode; + inode = filp->f_path.dentry->d_inode; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, - "tty_release_dev")) + if (tty_paranoia_check(tty, inode, "tty_release_dev")) return; check_tty_count(tty, "tty_release_dev"); @@ -1751,7 +1753,7 @@ void tty_release_dev(struct file *filp) /* Make this pty number available for reallocation */ if (devpts) - devpts_kill_index(idx); + devpts_kill_index(inode, idx); } /** @@ -1836,7 +1838,7 @@ retry_open: got_driver: if (!tty) { /* check whether we're reopening an existing tty */ - tty = tty_driver_lookup_tty(driver, index); + tty = tty_driver_lookup_tty(driver, inode, index); if (IS_ERR(tty)) return PTR_ERR(tty); diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 488eb424f662..638db9b769ac 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -177,7 +177,7 @@ static struct dentry *get_node(int num) return lookup_one_len(s, root, sprintf(s, "%d", num)); } -int devpts_new_index(void) +int devpts_new_index(struct inode *ptmx_inode) { int index; int ida_ret; @@ -205,14 +205,14 @@ retry: return index; } -void devpts_kill_index(int idx) +void devpts_kill_index(struct inode *ptmx_inode, int idx) { mutex_lock(&allocated_ptys_lock); ida_remove(&allocated_ptys, idx); mutex_unlock(&allocated_ptys_lock); } -int devpts_pty_new(struct tty_struct *tty) +int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) { int number = tty->index; /* tty layer puts index from devpts_new_index() in here */ struct tty_driver *driver = tty->driver; @@ -245,7 +245,7 @@ int devpts_pty_new(struct tty_struct *tty) return 0; } -struct tty_struct *devpts_get_tty(int number) +struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) { struct dentry *dentry = get_node(number); struct tty_struct *tty; @@ -262,8 +262,9 @@ struct tty_struct *devpts_get_tty(int number) return tty; } -void devpts_pty_kill(int number) +void devpts_pty_kill(struct tty_struct *tty) { + int number = tty->index; struct dentry *dentry = get_node(number); if (!IS_ERR(dentry)) { diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 154769cad3f3..5ce0e5fd712e 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -17,20 +17,31 @@ #ifdef CONFIG_UNIX98_PTYS -int devpts_new_index(void); -void devpts_kill_index(int idx); -int devpts_pty_new(struct tty_struct *tty); /* mknod in devpts */ -struct tty_struct *devpts_get_tty(int number); /* get tty structure */ -void devpts_pty_kill(int number); /* unlink */ +int devpts_new_index(struct inode *ptmx_inode); +void devpts_kill_index(struct inode *ptmx_inode, int idx); +/* mknod in devpts */ +int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +/* get tty structure */ +struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number); +/* unlink */ +void devpts_pty_kill(struct tty_struct *tty); #else /* Dummy stubs in the no-pty case */ -static inline int devpts_new_index(void) { return -EINVAL; } -static inline void devpts_kill_index(int idx) { } -static inline int devpts_pty_new(struct tty_struct *tty) { return -EINVAL; } -static inline struct tty_struct *devpts_get_tty(int number) { return NULL; } -static inline void devpts_pty_kill(int number) { } +static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } +static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } +static inline int devpts_pty_new(struct inode *ptmx_inode, + struct tty_struct *tty) +{ + return -EINVAL; +} +static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode, + int number) +{ + return NULL; +} +static inline void devpts_pty_kill(struct tty_struct *tty) { } #endif diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 005d06ad46a6..78416b901589 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -225,7 +225,8 @@ struct tty_struct; struct tty_driver; struct tty_operations { - struct tty_struct * (*lookup)(struct tty_driver *driver, int idx); + struct tty_struct * (*lookup)(struct tty_driver *driver, + struct inode *inode, int idx); int (*install)(struct tty_driver *driver, struct tty_struct *tty); void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp); -- cgit v1.2.3 From 47afa7a5a8a8fb9e60cdb6a3bd612e07c37e9d90 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:44:17 +0100 Subject: tty: some ICANON magic is in the wrong places Move the set up on ldisc change into the ldisc Move the INQ/OUTQ cases into the driver not in shared ioctl code where it gives bogus answers for other ldisc values Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/bluetooth/hci_ldisc.c | 2 +- drivers/char/n_hdlc.c | 2 +- drivers/char/n_tty.c | 53 +++++++++++++++++++++++++++++++++++++++++-- drivers/char/tty_ioctl.c | 42 ++-------------------------------- include/linux/tty.h | 2 +- 5 files changed, 56 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 8dfcf77cb717..4426bb552bd9 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -484,7 +484,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, return -EUNATCH; default: - err = n_tty_ioctl(tty, file, cmd, arg); + err = n_tty_ioctl_helper(tty, file, cmd, arg); break; }; diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 69ec6399c714..bacb3e2872ae 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -764,7 +764,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, break; default: - error = n_tty_ioctl (tty, file, cmd, arg); + error = n_tty_ioctl_helper(tty, file, cmd, arg); break; } return error; diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 708c2b1dbe51..b4f5dccad2a6 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1011,8 +1011,20 @@ int is_ignored(int sig) static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { - if (!tty) - return; + int canon_change = 1; + BUG_ON(!tty); + + if (old) + canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; + if (canon_change) { + memset(&tty->read_flags, 0, sizeof tty->read_flags); + tty->canon_head = tty->read_tail; + tty->canon_data = 0; + tty->erasing = 0; + } + + if (canon_change && !L_ICANON(tty) && tty->read_cnt) + wake_up_interruptible(&tty->read_wait); tty->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { @@ -1573,6 +1585,43 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, return mask; } +static unsigned long inq_canon(struct tty_struct *tty) +{ + int nr, head, tail; + + if (!tty->canon_data || !tty->read_buf) + return 0; + head = tty->canon_head; + tail = tty->read_tail; + nr = (head - tail) & (N_TTY_BUF_SIZE-1); + /* Skip EOF-chars.. */ + while (head != tail) { + if (test_bit(tail, tty->read_flags) && + tty->read_buf[tail] == __DISABLED_CHAR) + nr--; + tail = (tail+1) & (N_TTY_BUF_SIZE-1); + } + return nr; +} + +static int n_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval; + + switch (cmd) { + case TIOCOUTQ: + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); + case TIOCINQ: + retval = tty->read_cnt; + if (L_ICANON(tty)) + retval = inq_canon(tty); + return put_user(retval, (unsigned int __user *) arg); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 14cc19c344cc..a408c8e487ec 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -489,7 +489,6 @@ EXPORT_SYMBOL(tty_termios_hw_change); static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) { - int canon_change; struct ktermios old_termios; struct tty_ldisc *ld; unsigned long flags; @@ -505,18 +504,6 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) old_termios = *tty->termios; *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); - canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; - if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; - tty->erasing = 0; - } - - /* This bit should be in the ldisc code */ - if (canon_change && !L_ICANON(tty) && tty->read_cnt) - /* Get characters left over from canonical mode. */ - wake_up_interruptible(&tty->read_wait); /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { @@ -677,24 +664,6 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) #endif -static unsigned long inq_canon(struct tty_struct *tty) -{ - int nr, head, tail; - - if (!tty->canon_data || !tty->read_buf) - return 0; - head = tty->canon_head; - tail = tty->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); - /* Skip EOF-chars.. */ - while (head != tail) { - if (test_bit(tail, tty->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) - nr--; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); - } - return nr; -} #ifdef TIOCGETP /* @@ -1110,7 +1079,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) } EXPORT_SYMBOL_GPL(tty_perform_flush); -int n_tty_ioctl(struct tty_struct *tty, struct file *file, +int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long flags; @@ -1148,13 +1117,6 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return 0; case TCFLSH: return tty_perform_flush(tty, arg); - case TIOCOUTQ: - return put_user(tty_chars_in_buffer(tty), (int __user *) arg); - case TIOCINQ: - retval = tty->read_cnt; - if (L_ICANON(tty)) - retval = inq_canon(tty); - return put_user(retval, (unsigned int __user *) arg); case TIOCPKT: { int pktmode; @@ -1180,4 +1142,4 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return tty_mode_ioctl(tty, file, cmd, arg); } } -EXPORT_SYMBOL(n_tty_ioctl); +EXPORT_SYMBOL(n_tty_ioctl_helper); diff --git a/include/linux/tty.h b/include/linux/tty.h index 3c7c75794a4a..3b8121d4e36f 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -466,7 +466,7 @@ static inline void tty_audit_push_task(struct task_struct *tsk, #endif /* tty_ioctl.c */ -extern int n_tty_ioctl(struct tty_struct *tty, struct file *file, +extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); /* serial.c */ -- cgit v1.2.3 From a447c0932445f92ce6f4c1bd020f62c5097a7842 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 13 Oct 2008 10:46:57 +0100 Subject: vfs: Use const for kernel parser table This is a much better version of a previous patch to make the parser tables constant. Rather than changing the typedef, we put the "const" in all the various places where its required, allowing the __initconst exception for nfsroot which was the cause of the previous trouble. This was posted for review some time ago and I believe its been in -mm since then. Signed-off-by: Steven Whitehouse Cc: Alexander Viro Signed-off-by: Linus Torvalds --- arch/powerpc/platforms/cell/spufs/inode.c | 2 +- arch/s390/hypfs/inode.c | 2 +- drivers/infiniband/ulp/srp/ib_srp.c | 2 +- drivers/usb/core/inode.c | 2 +- fs/9p/v9fs.c | 2 +- fs/adfs/super.c | 2 +- fs/affs/super.c | 2 +- fs/afs/super.c | 2 +- fs/autofs/inode.c | 2 +- fs/autofs4/inode.c | 2 +- fs/befs/linuxvfs.c | 2 +- fs/devpts/inode.c | 2 +- fs/ecryptfs/main.c | 2 +- fs/ext2/super.c | 2 +- fs/ext3/super.c | 2 +- fs/ext4/super.c | 2 +- fs/fat/inode.c | 6 +++--- fs/fuse/inode.c | 2 +- fs/gfs2/mount.c | 2 +- fs/hfs/super.c | 2 +- fs/hfsplus/options.c | 2 +- fs/hpfs/super.c | 2 +- fs/hugetlbfs/inode.c | 2 +- fs/isofs/inode.c | 2 +- fs/jfs/super.c | 2 +- fs/nfs/nfsroot.c | 2 +- fs/nfs/super.c | 6 +++--- fs/ocfs2/super.c | 2 +- fs/omfs/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/udf/super.c | 2 +- fs/ufs/super.c | 4 ++-- fs/xfs/linux-2.6/xfs_super.c | 2 +- include/linux/parser.h | 2 +- lib/parser.c | 2 +- net/9p/client.c | 2 +- net/9p/trans_fd.c | 2 +- security/selinux/hooks.c | 2 +- 38 files changed, 43 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 690ca7b0dcf6..2c8b8091250f 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -659,7 +659,7 @@ enum { Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err, }; -static match_table_t spufs_tokens = { +static const match_table_t spufs_tokens = { { Opt_uid, "uid=%d" }, { Opt_gid, "gid=%d" }, { Opt_mode, "mode=%o" }, diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 7383781f3e6a..36313801cd5c 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -219,7 +219,7 @@ static int hypfs_release(struct inode *inode, struct file *filp) enum { opt_uid, opt_gid, opt_err }; -static match_table_t hypfs_tokens = { +static const match_table_t hypfs_tokens = { {opt_uid, "uid=%u"}, {opt_gid, "gid=%u"}, {opt_err, NULL} diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index ed7c5f72cb8b..5b8b533f2908 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1683,7 +1683,7 @@ enum { SRP_OPT_SERVICE_ID), }; -static match_table_t srp_opt_tokens = { +static const match_table_t srp_opt_tokens = { { SRP_OPT_ID_EXT, "id_ext=%s" }, { SRP_OPT_IOC_GUID, "ioc_guid=%s" }, { SRP_OPT_DGID, "dgid=%s" }, diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index db410e92c80d..77fa7a080801 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -97,7 +97,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_devuid, "devuid=%u"}, {Opt_devgid, "devgid=%u"}, {Opt_devmode, "devmode=%o"}, diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 047c791427aa..c061c3f18e7c 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -55,7 +55,7 @@ enum { Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_debug, "debug=%x"}, {Opt_dfltuid, "dfltuid=%u"}, {Opt_dfltgid, "dfltgid=%u"}, diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 26f3b43726bb..7f83a46f2b7e 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -157,7 +157,7 @@ static int adfs_show_options(struct seq_file *seq, struct vfsmount *mnt) enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err}; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_ownmask, "ownmask=%o"}, diff --git a/fs/affs/super.c b/fs/affs/super.c index 3a89094f93d0..8989c93193ed 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -135,7 +135,7 @@ enum { Opt_verbose, Opt_volume, Opt_ignore, Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bs, "bs=%u"}, {Opt_mode, "mode=%o"}, {Opt_mufs, "mufs"}, diff --git a/fs/afs/super.c b/fs/afs/super.c index 250d8c4d66e4..aee239a048cb 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -64,7 +64,7 @@ enum { afs_opt_vol, }; -static match_table_t afs_options_list = { +static const match_table_t afs_options_list = { { afs_opt_cell, "cell=%s" }, { afs_opt_rwpath, "rwpath" }, { afs_opt_vol, "vol=%s" }, diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index dda510d31f84..b70eea1e8c59 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -59,7 +59,7 @@ static const struct super_operations autofs_sops = { enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -static match_table_t autofs_tokens = { +static const match_table_t autofs_tokens = { {Opt_fd, "fd=%u"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 7bb3e5ba0537..45d55819203d 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -213,7 +213,7 @@ static const struct super_operations autofs4_sops = { enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, Opt_indirect, Opt_direct, Opt_offset}; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_fd, "fd=%u"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 740f53672a8a..9286b2af893a 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -650,7 +650,7 @@ enum { Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err, }; -static match_table_t befs_tokens = { +static const match_table_t befs_tokens = { {Opt_uid, "uid=%d"}, {Opt_gid, "gid=%d"}, {Opt_charset, "iocharset=%s"}, diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index a70d5d0890c7..4a714f6c1bed 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -49,7 +49,7 @@ enum { Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_mode, "mode=%o"}, diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 448dfd597b5f..8ebe9a5d1d99 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -211,7 +211,7 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata, ecryptfs_opt_encrypted_view, ecryptfs_opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {ecryptfs_opt_sig, "sig=%s"}, {ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"}, {ecryptfs_opt_cipher, "cipher=%s"}, diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fd88c7b43e66..647cd888ac87 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -393,7 +393,7 @@ enum { Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, diff --git a/fs/ext3/super.c b/fs/ext3/super.c index f38a5afc39a1..399a96a6c556 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -760,7 +760,7 @@ enum { Opt_grpquota }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, diff --git a/fs/ext4/super.c b/fs/ext4/super.c index fb940c22ab0d..dea8f13c2fd9 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -919,7 +919,7 @@ enum { Opt_inode_readahead_blks }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 80ff3381fa21..d12cdf2a0406 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -855,7 +855,7 @@ enum { Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, }; -static match_table_t fat_tokens = { +static const match_table_t fat_tokens = { {Opt_check_r, "check=relaxed"}, {Opt_check_s, "check=strict"}, {Opt_check_n, "check=normal"}, @@ -890,14 +890,14 @@ static match_table_t fat_tokens = { {Opt_tz_utc, "tz=UTC"}, {Opt_err, NULL}, }; -static match_table_t msdos_tokens = { +static const match_table_t msdos_tokens = { {Opt_nodots, "nodots"}, {Opt_nodots, "dotsOK=no"}, {Opt_dots, "dots"}, {Opt_dots, "dotsOK=yes"}, {Opt_err, NULL} }; -static match_table_t vfat_tokens = { +static const match_table_t vfat_tokens = { {Opt_charset, "iocharset=%s"}, {Opt_shortname_lower, "shortname=lower"}, {Opt_shortname_win95, "shortname=win95"}, diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index d2249f174e20..6a84388cacff 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -354,7 +354,7 @@ enum { OPT_ERR }; -static match_table_t tokens = { +static const match_table_t tokens = { {OPT_FD, "fd=%u"}, {OPT_ROOTMODE, "rootmode=%o"}, {OPT_USER_ID, "user_id=%u"}, diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c index df48333e6f01..f96eb90a2cfa 100644 --- a/fs/gfs2/mount.c +++ b/fs/gfs2/mount.c @@ -46,7 +46,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_lockproto, "lockproto=%s"}, {Opt_locktable, "locktable=%s"}, {Opt_hostdata, "hostdata=%s"}, diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 4abb1047c689..3c7c7637719c 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -173,7 +173,7 @@ enum { opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { { opt_uid, "uid=%u" }, { opt_gid, "gid=%u" }, { opt_umask, "umask=%o" }, diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c index 9997cbf8beb5..9699c56d323f 100644 --- a/fs/hfsplus/options.c +++ b/fs/hfsplus/options.c @@ -25,7 +25,7 @@ enum { opt_force, opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { { opt_creator, "creator=%s" }, { opt_type, "type=%s" }, { opt_umask, "umask=%o" }, diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index b8ae9c90ada0..29ad461d568f 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -215,7 +215,7 @@ enum { Opt_timeshift, Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_help, "help"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 3f58923fb39b..61edc701b0e6 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -57,7 +57,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_size, "size=%s"}, {Opt_nr_inodes, "nr_inodes=%s"}, {Opt_mode, "mode=%o"}, diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 26948a6033b6..3f8af0f1505b 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -310,7 +310,7 @@ enum { Opt_nocompress, Opt_hide, Opt_showassoc, Opt_dmode, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_norock, "norock"}, {Opt_nojoliet, "nojoliet"}, {Opt_unhide, "unhide"}, diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 3630718be395..0dae345e481b 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -199,7 +199,7 @@ enum { Opt_usrquota, Opt_grpquota, Opt_uid, Opt_gid, Opt_umask }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_integrity, "integrity"}, {Opt_nointegrity, "nointegrity"}, {Opt_iocharset, "iocharset=%s"}, diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 46763d1cd397..8478fc25daee 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -127,7 +127,7 @@ enum { Opt_err }; -static match_table_t __initdata tokens = { +static match_table_t __initconst tokens = { {Opt_port, "port=%u"}, {Opt_rsize, "rsize=%u"}, {Opt_wsize, "wsize=%u"}, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e9b20173fef3..ffb697416cb1 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -98,7 +98,7 @@ enum { Opt_err }; -static match_table_t nfs_mount_option_tokens = { +static const match_table_t nfs_mount_option_tokens = { { Opt_userspace, "bg" }, { Opt_userspace, "fg" }, { Opt_userspace, "retry=%s" }, @@ -163,7 +163,7 @@ enum { Opt_xprt_err }; -static match_table_t nfs_xprt_protocol_tokens = { +static const match_table_t nfs_xprt_protocol_tokens = { { Opt_xprt_udp, "udp" }, { Opt_xprt_tcp, "tcp" }, { Opt_xprt_rdma, "rdma" }, @@ -180,7 +180,7 @@ enum { Opt_sec_err }; -static match_table_t nfs_secflavor_tokens = { +static const match_table_t nfs_secflavor_tokens = { { Opt_sec_none, "none" }, { Opt_sec_none, "null" }, { Opt_sec_sys, "sys" }, diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 88255d3f52b4..70334d85aff1 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -157,7 +157,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_barrier, "barrier=%u"}, {Opt_err_panic, "errors=panic"}, {Opt_err_ro, "errors=remount-ro"}, diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index d29047b1b9b0..cbf047a847c5 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -346,7 +346,7 @@ enum { Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_umask, "umask=%o"}, diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 3f4902060c7a..9a9220333b3b 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -848,7 +848,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_fast_unmount, "fast_unmount"}, {Opt_norm_unmount, "norm_unmount"}, {Opt_err, NULL}, diff --git a/fs/udf/super.c b/fs/udf/super.c index 5698bbf83bbf..e25e7010627b 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -369,7 +369,7 @@ enum { Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_novrs, "novrs"}, {Opt_nostrict, "nostrict"}, {Opt_bs, "bs=%u"}, diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 3141969b456d..e65212dfb60e 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -309,7 +309,7 @@ enum { Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_type_old, "ufstype=old"}, {Opt_type_sunx86, "ufstype=sunx86"}, {Opt_type_sun, "ufstype=sun"}, @@ -1233,7 +1233,7 @@ static int ufs_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct ufs_sb_info *sbi = UFS_SB(vfs->mnt_sb); unsigned mval = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE; - struct match_token *tp = tokens; + const struct match_token *tp = tokens; while (tp->token != Opt_onerror_panic && tp->token != mval) ++tp; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 18d3c8487835..7227b2efef22 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -158,7 +158,7 @@ enum { Opt_barrier, Opt_nobarrier, Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_barrier, "barrier"}, {Opt_nobarrier, "nobarrier"}, {Opt_err, NULL} diff --git a/include/linux/parser.h b/include/linux/parser.h index 7dcd05075756..ea2281e726f6 100644 --- a/include/linux/parser.h +++ b/include/linux/parser.h @@ -25,7 +25,7 @@ typedef struct { char *to; } substring_t; -int match_token(char *, match_table_t table, substring_t args[]); +int match_token(char *, const match_table_t table, substring_t args[]); int match_int(substring_t *, int *result); int match_octal(substring_t *, int *result); int match_hex(substring_t *, int *result); diff --git a/lib/parser.c b/lib/parser.c index 4f0cbc03e0e8..b00d02059a5f 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -100,7 +100,7 @@ static int match_one(char *s, const char *p, substring_t args[]) * format identifiers which will be taken into account when matching the * tokens, and whose locations will be returned in the @args array. */ -int match_token(char *s, match_table_t table, substring_t args[]) +int match_token(char *s, const match_table_t table, substring_t args[]) { const struct match_token *p; diff --git a/net/9p/client.c b/net/9p/client.c index 10e320307ec0..e053e06028a5 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -52,7 +52,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_msize, "msize=%u"}, {Opt_legacy, "noextend"}, {Opt_trans, "trans=%s"}, diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index d652baf5ff91..6dabbdb66651 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -86,7 +86,7 @@ enum { Opt_port, Opt_rfdno, Opt_wfdno, Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_port, "port=%u"}, {Opt_rfdno, "rfdno=%u"}, {Opt_wfdno, "wfdno=%u"}, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 88f19536efad..576e51199079 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -325,7 +325,7 @@ enum { Opt_rootcontext = 4, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_context, CONTEXT_STR "%s"}, {Opt_fscontext, FSCONTEXT_STR "%s"}, {Opt_defcontext, DEFCONTEXT_STR "%s"}, -- cgit v1.2.3 From b14c72127fbe8f97e49de7437520175673f7306a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:30 +0200 Subject: ide: drop dsc_handle argument from ide_pc_intr() * Add 'int dsc' argument to ->pc_callback method. * Call ide_tape_handle_dsc() internally in ide_tape_callback() if dsc argument is set and update ide_pc_intr() accordingly. There should be no functional changes caused by this patch. Cc: Borislav Petkov Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 16 +++++++--------- drivers/ide/ide-floppy.c | 6 +++--- drivers/ide/ide-tape.c | 13 +++++++++---- drivers/scsi/ide-scsi.c | 5 ++--- include/linux/ide.h | 4 ++-- 5 files changed, 23 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 608c5bade929..fb27c94aeb0d 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -207,7 +207,7 @@ EXPORT_SYMBOL_GPL(ide_set_media_lock); ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), - void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *), + void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { ide_hwif_t *hwif = drive->hwif; @@ -216,12 +216,12 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, xfer_func_t *xferfunc; unsigned int temp; u16 bcount; - u8 stat, ireason, scsi = drive->scsi; + u8 stat, ireason, scsi = drive->scsi, dsc = 0; debug_log("Enter %s - interrupt handler\n", __func__); if (pc->flags & PC_FLAG_TIMEDOUT) { - drive->pc_callback(drive); + drive->pc_callback(drive, 0); return ide_stopped; } @@ -283,14 +283,12 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, } cmd_finished: pc->error = 0; - if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && - (stat & ATA_DSC) == 0) { - dsc_handle(drive); - return ide_stopped; - } + + if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0) + dsc = 1; /* Command finished - Call the callback function */ - drive->pc_callback(drive); + drive->pc_callback(drive, dsc); return ide_stopped; } diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index d36f155470a4..b33080675f6b 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -156,7 +156,7 @@ static void idefloppy_update_buffers(ide_drive_t *drive, idefloppy_end_request(drive, 1, 0); } -static void ide_floppy_callback(ide_drive_t *drive) +static void ide_floppy_callback(ide_drive_t *drive, int dsc) { idefloppy_floppy_t *floppy = drive->driver_data; struct ide_atapi_pc *pc = floppy->pc; @@ -223,7 +223,7 @@ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr, WAIT_FLOPPY_CMD, NULL, idefloppy_update_buffers, - idefloppy_retry_pc, NULL, ide_io_buffers); + idefloppy_retry_pc, ide_io_buffers); } /* @@ -308,7 +308,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->error = IDEFLOPPY_ERROR_GENERAL; floppy->failed_pc = NULL; - drive->pc_callback(drive); + drive->pc_callback(drive, 0); return ide_stopped; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index f8c84df4a0bc..70b499a617d8 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -522,7 +522,9 @@ static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects) return 0; } -static void ide_tape_callback(ide_drive_t *drive) +static void ide_tape_handle_dsc(ide_drive_t *); + +static void ide_tape_callback(ide_drive_t *drive, int dsc) { idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = tape->pc; @@ -530,6 +532,9 @@ static void ide_tape_callback(ide_drive_t *drive) debug_log(DBG_PROCS, "Enter %s\n", __func__); + if (dsc) + ide_tape_handle_dsc(drive); + if (tape->failed_pc == pc) tape->failed_pc = NULL; @@ -658,7 +663,7 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) return ide_pc_intr(drive, tape->pc, idetape_pc_intr, WAIT_TAPE_CMD, NULL, idetape_update_buffers, idetape_retry_pc, - ide_tape_handle_dsc, ide_tape_io_buffers); + ide_tape_io_buffers); } /* @@ -743,7 +748,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->error = IDETAPE_ERROR_GENERAL; } tape->failed_pc = NULL; - drive->pc_callback(drive); + drive->pc_callback(drive, 0); return ide_stopped; } debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]); @@ -805,7 +810,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) pc->error = IDETAPE_ERROR_GENERAL; tape->failed_pc = NULL; } - drive->pc_callback(drive); + drive->pc_callback(drive, 0); return ide_stopped; } diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 90212ac33be3..b9bfec24e913 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -137,7 +137,7 @@ static void ide_scsi_hex_dump(u8 *data, int len) static int idescsi_end_request(ide_drive_t *, int, int); -static void ide_scsi_callback(ide_drive_t *drive) +static void ide_scsi_callback(ide_drive_t *drive, int dsc) { idescsi_scsi_t *scsi = drive_to_idescsi(drive); struct ide_atapi_pc *pc = scsi->pc; @@ -298,8 +298,7 @@ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) struct ide_atapi_pc *pc = scsi->pc; return ide_pc_intr(drive, pc, idescsi_pc_intr, get_timeout(pc), - idescsi_expiry, NULL, NULL, NULL, - ide_io_buffers); + idescsi_expiry, NULL, NULL, ide_io_buffers); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) diff --git a/include/linux/ide.h b/include/linux/ide.h index a9d82d6e6bdd..93fd2bc17bf8 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -485,7 +485,7 @@ struct ide_drive_s { struct completion gendev_rel_comp; /* to deal with device release() */ /* callback for packet commands */ - void (*pc_callback)(struct ide_drive_s *); + void (*pc_callback)(struct ide_drive_s *, int); unsigned long atapi_flags; }; @@ -1174,7 +1174,7 @@ int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), - void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *), + void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int)); ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *, -- cgit v1.2.3 From 2b9efba48283f34083df6bc53f6752fba4e4d409 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:31 +0200 Subject: ide: add pointer to the current packet command to ide_drive_t * Add pointer to the current packet command (struct ide_atapi_pc *pc) to ide_drive_t and use it instead of the pointer in struct ide_*_obj. * Use drive->pc in ide_{issue,transfer}_pc() and ide_pc_intr() instead of 'pc' argument. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 9 +++++--- drivers/ide/ide-floppy.c | 20 +++++++---------- drivers/ide/ide-floppy.h | 2 -- drivers/ide/ide-tape.c | 31 +++++++++----------------- drivers/scsi/ide-scsi.c | 58 +++++++++++++++++++++++------------------------- include/linux/ide.h | 10 ++++++--- 6 files changed, 60 insertions(+), 70 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index fb27c94aeb0d..0069c4f08244 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -204,12 +204,13 @@ int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on) EXPORT_SYMBOL_GPL(ide_set_media_lock); /* TODO: unify the code thus making some arguments go away */ -ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, +ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { + struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->hwgroup->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; @@ -416,10 +417,11 @@ static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) return ireason; } -ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, +ide_startstop_t ide_transfer_pc(ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry) { + struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->hwgroup->rq; ide_startstop_t startstop; @@ -458,10 +460,11 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, } EXPORT_SYMBOL_GPL(ide_transfer_pc); -ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, +ide_startstop_t ide_issue_pc(ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry) { + struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; u16 bcount; u8 dma = 0; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index b33080675f6b..cb89caf07913 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -159,7 +159,7 @@ static void idefloppy_update_buffers(ide_drive_t *drive, static void ide_floppy_callback(ide_drive_t *drive, int dsc) { idefloppy_floppy_t *floppy = drive->driver_data; - struct ide_atapi_pc *pc = floppy->pc; + struct ide_atapi_pc *pc = drive->pc; int uptodate = pc->error ? 0 : 1; debug_log("Reached %s\n", __func__); @@ -171,7 +171,7 @@ static void ide_floppy_callback(ide_drive_t *drive, int dsc) (pc->rq && blk_pc_request(pc->rq))) uptodate = 1; /* FIXME */ else if (pc->c[0] == GPCMD_REQUEST_SENSE) { - u8 *buf = floppy->pc->buf; + u8 *buf = pc->buf; if (!pc->error) { floppy->sense_key = buf[2] & 0x0F; @@ -219,9 +219,7 @@ static void idefloppy_retry_pc(ide_drive_t *drive) /* The usual interrupt handler called during a packet command. */ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) { - idefloppy_floppy_t *floppy = drive->driver_data; - - return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr, + return ide_pc_intr(drive, idefloppy_pc_intr, WAIT_FLOPPY_CMD, NULL, idefloppy_update_buffers, idefloppy_retry_pc, ide_io_buffers); } @@ -234,10 +232,8 @@ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) */ static int idefloppy_transfer_pc(ide_drive_t *drive) { - idefloppy_floppy_t *floppy = drive->driver_data; - /* Send the actual packet */ - drive->hwif->tp_ops->output_data(drive, NULL, floppy->pc->c, 12); + drive->hwif->tp_ops->output_data(drive, NULL, drive->pc->c, 12); /* Timeout for the packet command */ return WAIT_FLOPPY_CMD; @@ -251,7 +247,6 @@ static int idefloppy_transfer_pc(ide_drive_t *drive) static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; - struct ide_atapi_pc *pc = floppy->pc; ide_expiry_t *expiry; unsigned int timeout; @@ -271,7 +266,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) expiry = NULL; } - return ide_transfer_pc(drive, pc, idefloppy_pc_intr, timeout, expiry); + return ide_transfer_pc(drive, idefloppy_pc_intr, timeout, expiry); } static void ide_floppy_report_error(idefloppy_floppy_t *floppy, @@ -298,8 +293,9 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, if (floppy->failed_pc == NULL && pc->c[0] != GPCMD_REQUEST_SENSE) floppy->failed_pc = pc; + /* Set the current packet command */ - floppy->pc = pc; + drive->pc = pc; if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES) { if (!(pc->flags & PC_FLAG_SUPPRESS_ERROR)) @@ -316,7 +312,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, pc, idefloppy_start_pc_transfer, + return ide_issue_pc(drive, idefloppy_start_pc_transfer, WAIT_FLOPPY_CMD, NULL); } diff --git a/drivers/ide/ide-floppy.h b/drivers/ide/ide-floppy.h index ecadc2bc322d..6eee8d3a7243 100644 --- a/drivers/ide/ide-floppy.h +++ b/drivers/ide/ide-floppy.h @@ -13,8 +13,6 @@ typedef struct ide_floppy_obj { struct kref kref; unsigned int openers; /* protected by BKL for now */ - /* Current packet command */ - struct ide_atapi_pc *pc; /* Last failed packet command */ struct ide_atapi_pc *failed_pc; /* used for blk_{fs,pc}_request() requests */ diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 70b499a617d8..5b2ac04d9be9 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -172,15 +172,11 @@ typedef struct ide_tape_obj { struct kref kref; /* - * pc points to the current processed packet command. - * * failed_pc points to the last failed packet command, or contains * NULL if we do not need to retry any packet command. This is * required since an additional packet command is needed before the * retry, to get detailed information on what went wrong. */ - /* Current packet command */ - struct ide_atapi_pc *pc; /* Last failed packet command */ struct ide_atapi_pc *failed_pc; /* used by REQ_IDETAPE_{READ,WRITE} requests */ @@ -527,7 +523,7 @@ static void ide_tape_handle_dsc(ide_drive_t *); static void ide_tape_callback(ide_drive_t *drive, int dsc) { idetape_tape_t *tape = drive->driver_data; - struct ide_atapi_pc *pc = tape->pc; + struct ide_atapi_pc *pc = drive->pc; int uptodate = pc->error ? 0 : 1; debug_log(DBG_PROCS, "Enter %s\n", __func__); @@ -563,7 +559,7 @@ static void ide_tape_callback(ide_drive_t *drive, int dsc) if (pc->error) uptodate = pc->error; } else if (pc->c[0] == READ_POSITION && uptodate) { - u8 *readpos = tape->pc->buf; + u8 *readpos = pc->buf; debug_log(DBG_SENSE, "BOP - %s\n", (readpos[0] & 0x80) ? "Yes" : "No"); @@ -659,9 +655,7 @@ static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, */ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) { - idetape_tape_t *tape = drive->driver_data; - - return ide_pc_intr(drive, tape->pc, idetape_pc_intr, WAIT_TAPE_CMD, + return ide_pc_intr(drive, idetape_pc_intr, WAIT_TAPE_CMD, NULL, idetape_update_buffers, idetape_retry_pc, ide_tape_io_buffers); } @@ -669,7 +663,7 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) /* * Packet Command Interface * - * The current Packet Command is available in tape->pc, and will not change + * The current Packet Command is available in drive->pc, and will not change * until we finish handling it. Each packet command is associated with a * callback function that will be called when the command is finished. * @@ -704,10 +698,7 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) */ static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) { - idetape_tape_t *tape = drive->driver_data; - - return ide_transfer_pc(drive, tape->pc, idetape_pc_intr, - WAIT_TAPE_CMD, NULL); + return ide_transfer_pc(drive, idetape_pc_intr, WAIT_TAPE_CMD, NULL); } static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, @@ -715,7 +706,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, { idetape_tape_t *tape = drive->driver_data; - if (tape->pc->c[0] == REQUEST_SENSE && + if (drive->pc->c[0] == REQUEST_SENSE && pc->c[0] == REQUEST_SENSE) { printk(KERN_ERR "ide-tape: possible ide-tape.c bug - " "Two request sense in serial were issued\n"); @@ -723,8 +714,9 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, if (tape->failed_pc == NULL && pc->c[0] != REQUEST_SENSE) tape->failed_pc = pc; + /* Set the current packet command */ - tape->pc = pc; + drive->pc = pc; if (pc->retries > IDETAPE_MAX_PC_RETRIES || (pc->flags & PC_FLAG_ABORT)) { @@ -755,8 +747,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, pc, idetape_transfer_pc, - WAIT_TAPE_CMD, NULL); + return ide_issue_pc(drive, idetape_transfer_pc, WAIT_TAPE_CMD, NULL); } /* A mode sense command is used to "sense" tape parameters. */ @@ -790,7 +781,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; - struct ide_atapi_pc *pc = tape->pc; + struct ide_atapi_pc *pc = drive->pc; u8 stat; stat = hwif->tp_ops->read_status(hwif); @@ -867,7 +858,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, } /* Retry a failed packet command */ - if (tape->failed_pc && tape->pc->c[0] == REQUEST_SENSE) { + if (tape->failed_pc && drive->pc->c[0] == REQUEST_SENSE) { pc = tape->failed_pc; goto out; } diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index b9bfec24e913..bb8b3b123c7d 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -82,7 +82,6 @@ typedef struct ide_scsi_obj { struct gendisk *disk; struct Scsi_Host *host; - struct ide_atapi_pc *pc; /* Current packet command */ unsigned long transform; /* SCSI cmd translation layer */ unsigned long log; /* log flags */ } idescsi_scsi_t; @@ -140,7 +139,7 @@ static int idescsi_end_request(ide_drive_t *, int, int); static void ide_scsi_callback(ide_drive_t *drive, int dsc) { idescsi_scsi_t *scsi = drive_to_idescsi(drive); - struct ide_atapi_pc *pc = scsi->pc; + struct ide_atapi_pc *pc = drive->pc; if (pc->flags & PC_FLAG_TIMEDOUT) debug_log("%s: got timed out packet %lu at %lu\n", __func__, @@ -267,7 +266,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) spin_unlock_irqrestore(host->host_lock, flags); kfree(pc); blk_put_request(rq); - scsi->pc = NULL; + drive->pc = NULL; return 0; } @@ -278,8 +277,7 @@ static inline unsigned long get_timeout(struct ide_atapi_pc *pc) static int idescsi_expiry(ide_drive_t *drive) { - idescsi_scsi_t *scsi = drive_to_idescsi(drive); - struct ide_atapi_pc *pc = scsi->pc; + struct ide_atapi_pc *pc = drive->pc; debug_log("%s called for %lu at %lu\n", __func__, pc->scsi_cmd->serial_number, jiffies); @@ -294,19 +292,14 @@ static int idescsi_expiry(ide_drive_t *drive) */ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { - idescsi_scsi_t *scsi = drive_to_idescsi(drive); - struct ide_atapi_pc *pc = scsi->pc; - - return ide_pc_intr(drive, pc, idescsi_pc_intr, get_timeout(pc), + return ide_pc_intr(drive, idescsi_pc_intr, get_timeout(drive->pc), idescsi_expiry, NULL, NULL, ide_io_buffers); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) { - idescsi_scsi_t *scsi = drive_to_idescsi(drive); - - return ide_transfer_pc(drive, scsi->pc, idescsi_pc_intr, - get_timeout(scsi->pc), idescsi_expiry); + return ide_transfer_pc(drive, idescsi_pc_intr, + get_timeout(drive->pc), idescsi_expiry); } static inline int idescsi_set_direction(struct ide_atapi_pc *pc) @@ -351,12 +344,10 @@ static int idescsi_map_sg(ide_drive_t *drive, struct ide_atapi_pc *pc) static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc) { - idescsi_scsi_t *scsi = drive_to_idescsi(drive); - /* Set the current packet command */ - scsi->pc = pc; + drive->pc = pc; - return ide_issue_pc(drive, pc, idescsi_transfer_pc, + return ide_issue_pc(drive, idescsi_transfer_pc, get_timeout(pc), idescsi_expiry); } @@ -621,6 +612,8 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd) int busy; int ret = FAILED; + struct ide_atapi_pc *pc; + /* In idescsi_eh_abort we try to gently pry our command from the ide subsystem */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) @@ -641,26 +634,27 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd) spin_lock_irq(&ide_lock); /* If there is no pc running we're done (our interrupt took care of it) */ - if (!scsi->pc) { + pc = drive->pc; + if (pc == NULL) { ret = SUCCESS; goto ide_unlock; } /* It's somewhere in flight. Does ide subsystem agree? */ - if (scsi->pc->scsi_cmd->serial_number == cmd->serial_number && !busy && - elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != scsi->pc->rq) { + if (pc->scsi_cmd->serial_number == cmd->serial_number && !busy && + elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != pc->rq) { /* * FIXME - not sure this condition can ever occur */ printk (KERN_ERR "ide-scsi: cmd aborted!\n"); - if (blk_sense_request(scsi->pc->rq)) - kfree(scsi->pc->buf); + if (blk_sense_request(pc->rq)) + kfree(pc->buf); /* we need to call blk_put_request twice. */ - blk_put_request(scsi->pc->rq); - blk_put_request(scsi->pc->rq); - kfree(scsi->pc); - scsi->pc = NULL; + blk_put_request(pc->rq); + blk_put_request(pc->rq); + kfree(pc); + drive->pc = NULL; ret = SUCCESS; } @@ -682,6 +676,8 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) int ready = 0; int ret = SUCCESS; + struct ide_atapi_pc *pc; + /* In idescsi_eh_reset we forcefully remove the command from the ide subsystem and reset the device. */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) @@ -696,7 +692,9 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) spin_lock_irq(cmd->device->host->host_lock); spin_lock(&ide_lock); - if (!scsi->pc || (req = scsi->pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) { + pc = drive->pc; + + if (pc == NULL || (req = pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) { printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n"); spin_unlock(&ide_lock); spin_unlock_irq(cmd->device->host->host_lock); @@ -707,9 +705,9 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) if (__blk_end_request(req, -EIO, 0)) BUG(); if (blk_sense_request(req)) - kfree(scsi->pc->buf); - kfree(scsi->pc); - scsi->pc = NULL; + kfree(pc->buf); + kfree(pc); + drive->pc = NULL; blk_put_request(req); /* now nuke the drive queue */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 93fd2bc17bf8..98d29df1ee04 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -322,6 +322,7 @@ typedef enum { ide_started, /* a drive operation was started, handler was set */ } ide_startstop_t; +struct ide_atapi_pc; struct ide_devset; struct ide_driver_s; @@ -484,6 +485,9 @@ struct ide_drive_s { struct device gendev; struct completion gendev_rel_comp; /* to deal with device release() */ + /* current packet command */ + struct ide_atapi_pc *pc; + /* callback for packet commands */ void (*pc_callback)(struct ide_drive_s *, int); @@ -1171,15 +1175,15 @@ int ide_do_test_unit_ready(ide_drive_t *, struct gendisk *); int ide_do_start_stop(ide_drive_t *, struct gendisk *, int); int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); -ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, +ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int)); -ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *, +ide_startstop_t ide_transfer_pc(ide_drive_t *, ide_handler_t *, unsigned int, ide_expiry_t *); -ide_startstop_t ide_issue_pc(ide_drive_t *, struct ide_atapi_pc *, +ide_startstop_t ide_issue_pc(ide_drive_t *, ide_handler_t *, unsigned int, ide_expiry_t *); ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); -- cgit v1.2.3 From 844b9468523c8c2c45b90df4efcabcbe4926b5ab Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:31 +0200 Subject: ide: drop 'timeout' and 'expiry' arguments from ide_pc_intr() * Move idescsi_expiry() to ide-atapi.c. * Move get_timeout() to . * Drop 'timeout' and 'expiry' arguments from ide_pc_intr(). While at it: * idescsi_expiry() -> ide_scsi_expiry() * get_timeout() -> ide_scsi_get_timeout() There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 34 +++++++++++++++++++++++++++------- drivers/ide/ide-floppy.c | 3 +-- drivers/ide/ide-tape.c | 5 ++--- drivers/scsi/ide-scsi.c | 25 ++++--------------------- include/linux/ide.h | 10 ++++++++-- 5 files changed, 42 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 0069c4f08244..184835141412 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -203,9 +203,21 @@ int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on) } EXPORT_SYMBOL_GPL(ide_set_media_lock); +int ide_scsi_expiry(ide_drive_t *drive) +{ + struct ide_atapi_pc *pc = drive->pc; + + debug_log("%s called for %lu at %lu\n", __func__, + pc->scsi_cmd->serial_number, jiffies); + + pc->flags |= PC_FLAG_TIMEDOUT; + + return 0; /* we do not want the IDE subsystem to retry */ +} +EXPORT_SYMBOL_GPL(ide_scsi_expiry); + /* TODO: unify the code thus making some arguments go away */ -ide_startstop_t ide_pc_intr(ide_drive_t *drive, - ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, +ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) @@ -215,12 +227,22 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct request *rq = hwif->hwgroup->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; - unsigned int temp; + ide_expiry_t *expiry; + unsigned int timeout, temp; u16 bcount; u8 stat, ireason, scsi = drive->scsi, dsc = 0; debug_log("Enter %s - interrupt handler\n", __func__); + if (scsi) { + timeout = ide_scsi_get_timeout(pc); + expiry = ide_scsi_expiry; + } else { + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; + expiry = NULL; + } + if (pc->flags & PC_FLAG_TIMEDOUT) { drive->pc_callback(drive, 0); return ide_stopped; @@ -347,9 +369,7 @@ cmd_finished: pc->xferred += temp; pc->cur_pos += temp; ide_pad_transfer(drive, 0, bcount - temp); - ide_set_handler(drive, handler, timeout, - expiry); - return ide_started; + goto next_irq; } debug_log("The device wants to send us more data than " "expected - allowing transfer\n"); @@ -376,7 +396,7 @@ cmd_finished: debug_log("[cmd %x] transferred %d bytes on that intr.\n", rq->cmd[0], bcount); - +next_irq: /* And set the interrupt handler again */ ide_set_handler(drive, handler, timeout, expiry); return ide_started; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index cb89caf07913..49e702670b8e 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -219,8 +219,7 @@ static void idefloppy_retry_pc(ide_drive_t *drive) /* The usual interrupt handler called during a packet command. */ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) { - return ide_pc_intr(drive, idefloppy_pc_intr, - WAIT_FLOPPY_CMD, NULL, idefloppy_update_buffers, + return ide_pc_intr(drive, idefloppy_pc_intr, idefloppy_update_buffers, idefloppy_retry_pc, ide_io_buffers); } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 5b2ac04d9be9..fe8502afd2ea 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -655,9 +655,8 @@ static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, */ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) { - return ide_pc_intr(drive, idetape_pc_intr, WAIT_TAPE_CMD, - NULL, idetape_update_buffers, idetape_retry_pc, - ide_tape_io_buffers); + return ide_pc_intr(drive, idetape_pc_intr, idetape_update_buffers, + idetape_retry_pc, ide_tape_io_buffers); } /* diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index bb8b3b123c7d..f71d1b34c3b1 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -270,36 +270,19 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) return 0; } -static inline unsigned long get_timeout(struct ide_atapi_pc *pc) -{ - return max_t(unsigned long, WAIT_CMD, pc->timeout - jiffies); -} - -static int idescsi_expiry(ide_drive_t *drive) -{ - struct ide_atapi_pc *pc = drive->pc; - - debug_log("%s called for %lu at %lu\n", __func__, - pc->scsi_cmd->serial_number, jiffies); - - pc->flags |= PC_FLAG_TIMEDOUT; - - return 0; /* we do not want the ide subsystem to retry */ -} - /* * Our interrupt handler. */ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { - return ide_pc_intr(drive, idescsi_pc_intr, get_timeout(drive->pc), - idescsi_expiry, NULL, NULL, ide_io_buffers); + return ide_pc_intr(drive, idescsi_pc_intr, NULL, NULL, ide_io_buffers); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) { return ide_transfer_pc(drive, idescsi_pc_intr, - get_timeout(drive->pc), idescsi_expiry); + ide_scsi_get_timeout(drive->pc), + ide_scsi_expiry); } static inline int idescsi_set_direction(struct ide_atapi_pc *pc) @@ -348,7 +331,7 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive, drive->pc = pc; return ide_issue_pc(drive, idescsi_transfer_pc, - get_timeout(pc), idescsi_expiry); + ide_scsi_get_timeout(pc), ide_scsi_expiry); } /* diff --git a/include/linux/ide.h b/include/linux/ide.h index 98d29df1ee04..3bf2bf0a56dc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1175,8 +1175,14 @@ int ide_do_test_unit_ready(ide_drive_t *, struct gendisk *); int ide_do_start_stop(ide_drive_t *, struct gendisk *, int); int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); -ide_startstop_t ide_pc_intr(ide_drive_t *drive, - ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry, +static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) +{ + return max_t(unsigned long, WAIT_CMD, pc->timeout - jiffies); +} + +int ide_scsi_expiry(ide_drive_t *); + +ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, -- cgit v1.2.3 From 67c56364df843fb9e3ed1af014b8fbe4b22ff25d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:31 +0200 Subject: ide: add request_sense_{pc,rq} to ide_drive_t Add 'struct ide_atapi_pc request_sense_pc' and 'request request_sense_rq' to ide_drive_t and use them instead of fields in struct ide_{floppy,tape}_obj. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 4 +- drivers/ide/ide-floppy.h | 3 -- drivers/ide/ide-tape.c | 7 +-- include/linux/ide.h | 134 ++++++++++++++++++++++++----------------------- 4 files changed, 72 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 49e702670b8e..6a1ade8ca9fe 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -208,8 +208,8 @@ void ide_floppy_create_request_sense_cmd(struct ide_atapi_pc *pc) static void idefloppy_retry_pc(ide_drive_t *drive) { struct ide_floppy_obj *floppy = drive->driver_data; - struct request *rq = &floppy->request_sense_rq; - struct ide_atapi_pc *pc = &floppy->request_sense_pc; + struct request *rq = &drive->request_sense_rq; + struct ide_atapi_pc *pc = &drive->request_sense_pc; (void)ide_read_error(drive); ide_floppy_create_request_sense_cmd(pc); diff --git a/drivers/ide/ide-floppy.h b/drivers/ide/ide-floppy.h index 6eee8d3a7243..e9e14c30fed7 100644 --- a/drivers/ide/ide-floppy.h +++ b/drivers/ide/ide-floppy.h @@ -18,9 +18,6 @@ typedef struct ide_floppy_obj { /* used for blk_{fs,pc}_request() requests */ struct ide_atapi_pc queued_pc; - struct ide_atapi_pc request_sense_pc; - struct request request_sense_rq; - /* Last error information */ u8 sense_key, asc, ascq; /* delay this long before sending packet command */ diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index fe8502afd2ea..72ecc5657db2 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -182,9 +182,6 @@ typedef struct ide_tape_obj { /* used by REQ_IDETAPE_{READ,WRITE} requests */ struct ide_atapi_pc queued_pc; - struct ide_atapi_pc request_sense_pc; - struct request request_sense_rq; - /* * DSC polling variables. * @@ -600,8 +597,8 @@ static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc) static void idetape_retry_pc(ide_drive_t *drive) { struct ide_tape_obj *tape = drive->driver_data; - struct request *rq = &tape->request_sense_rq; - struct ide_atapi_pc *pc = &tape->request_sense_pc; + struct request *rq = &drive->request_sense_rq; + struct ide_atapi_pc *pc = &drive->request_sense_pc; (void)ide_read_error(drive); idetape_create_request_sense_cmd(pc); diff --git a/include/linux/ide.h b/include/linux/ide.h index 3bf2bf0a56dc..908b4fc9772c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -322,7 +322,71 @@ typedef enum { ide_started, /* a drive operation was started, handler was set */ } ide_startstop_t; -struct ide_atapi_pc; +/* ATAPI packet command flags */ +enum { + /* set when an error is considered normal - no retry (ide-tape) */ + PC_FLAG_ABORT = (1 << 0), + PC_FLAG_SUPPRESS_ERROR = (1 << 1), + PC_FLAG_WAIT_FOR_DSC = (1 << 2), + PC_FLAG_DMA_OK = (1 << 3), + PC_FLAG_DMA_IN_PROGRESS = (1 << 4), + PC_FLAG_DMA_ERROR = (1 << 5), + PC_FLAG_WRITING = (1 << 6), + /* command timed out */ + PC_FLAG_TIMEDOUT = (1 << 7), +}; + +/* + * With each packet command, we allocate a buffer of IDE_PC_BUFFER_SIZE bytes. + * This is used for several packet commands (not for READ/WRITE commands). + */ +#define IDE_PC_BUFFER_SIZE 256 + +struct ide_atapi_pc { + /* actual packet bytes */ + u8 c[12]; + /* incremented on each retry */ + int retries; + int error; + + /* bytes to transfer */ + int req_xfer; + /* bytes actually transferred */ + int xferred; + + /* data buffer */ + u8 *buf; + /* current buffer position */ + u8 *cur_pos; + int buf_size; + /* missing/available data on the current buffer */ + int b_count; + + /* the corresponding request */ + struct request *rq; + + unsigned long flags; + + /* + * those are more or less driver-specific and some of them are subject + * to change/removal later. + */ + u8 pc_buf[IDE_PC_BUFFER_SIZE]; + + /* idetape only */ + struct idetape_bh *bh; + char *b_data; + + /* idescsi only for now */ + struct scatterlist *sg; + unsigned int sg_cnt; + + struct scsi_cmnd *scsi_cmd; + void (*done) (struct scsi_cmnd *); + + unsigned long timeout; +}; + struct ide_devset; struct ide_driver_s; @@ -492,6 +556,9 @@ struct ide_drive_s { void (*pc_callback)(struct ide_drive_s *, int); unsigned long atapi_flags; + + struct ide_atapi_pc request_sense_pc; + struct request request_sense_rq; }; typedef struct ide_drive_s ide_drive_t; @@ -768,71 +835,6 @@ ide_decl_devset(pio_mode); ide_decl_devset(unmaskirq); ide_decl_devset(using_dma); -/* ATAPI packet command flags */ -enum { - /* set when an error is considered normal - no retry (ide-tape) */ - PC_FLAG_ABORT = (1 << 0), - PC_FLAG_SUPPRESS_ERROR = (1 << 1), - PC_FLAG_WAIT_FOR_DSC = (1 << 2), - PC_FLAG_DMA_OK = (1 << 3), - PC_FLAG_DMA_IN_PROGRESS = (1 << 4), - PC_FLAG_DMA_ERROR = (1 << 5), - PC_FLAG_WRITING = (1 << 6), - /* command timed out */ - PC_FLAG_TIMEDOUT = (1 << 7), -}; - -/* - * With each packet command, we allocate a buffer of IDE_PC_BUFFER_SIZE bytes. - * This is used for several packet commands (not for READ/WRITE commands). - */ -#define IDE_PC_BUFFER_SIZE 256 - -struct ide_atapi_pc { - /* actual packet bytes */ - u8 c[12]; - /* incremented on each retry */ - int retries; - int error; - - /* bytes to transfer */ - int req_xfer; - /* bytes actually transferred */ - int xferred; - - /* data buffer */ - u8 *buf; - /* current buffer position */ - u8 *cur_pos; - int buf_size; - /* missing/available data on the current buffer */ - int b_count; - - /* the corresponding request */ - struct request *rq; - - unsigned long flags; - - /* - * those are more or less driver-specific and some of them are subject - * to change/removal later. - */ - u8 pc_buf[IDE_PC_BUFFER_SIZE]; - - /* idetape only */ - struct idetape_bh *bh; - char *b_data; - - /* idescsi only for now */ - struct scatterlist *sg; - unsigned int sg_cnt; - - struct scsi_cmnd *scsi_cmd; - void (*done) (struct scsi_cmnd *); - - unsigned long timeout; -}; - #ifdef CONFIG_IDE_PROC_FS /* * /proc/ide interface -- cgit v1.2.3 From 6b0da28b2d0f4f4e2c55689fc062db569075ff60 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:32 +0200 Subject: ide: add ide_retry_pc() helper * Add ide_create_request_sense_cmd() and ide_retry_pc() helpers and convert ide-{atapi,floppy,tape}.c to use them. * Remove no longer used ide*_create_request_sense_cmd(), ide*_retry_pc() and 'retry_pc' argument from ide_pc_intr(). * Make ide_queue_pc_head() static. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 39 ++++++++++++++++++++++++++++++++++----- drivers/ide/ide-floppy.c | 25 +------------------------ drivers/ide/ide-floppy.h | 1 - drivers/ide/ide-floppy_ioctl.c | 2 +- drivers/ide/ide-tape.c | 29 ++--------------------------- drivers/scsi/ide-scsi.c | 2 +- include/linux/ide.h | 5 ++--- 7 files changed, 41 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 184835141412..d758dcd87178 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -124,8 +124,8 @@ EXPORT_SYMBOL_GPL(ide_init_pc); * the current request, so that it will be processed immediately, on the next * pass through the driver. */ -void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk, - struct ide_atapi_pc *pc, struct request *rq) +static void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk, + struct ide_atapi_pc *pc, struct request *rq) { blk_rq_init(NULL, rq); rq->cmd_type = REQ_TYPE_SPECIAL; @@ -137,7 +137,6 @@ void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk, rq->cmd[13] = REQ_IDETAPE_PC1; ide_do_drive_cmd(drive, rq); } -EXPORT_SYMBOL_GPL(ide_queue_pc_head); /* * Add a special packet command request to the tail of the request queue, @@ -203,6 +202,37 @@ int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on) } EXPORT_SYMBOL_GPL(ide_set_media_lock); +void ide_create_request_sense_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc) +{ + ide_init_pc(pc); + pc->c[0] = REQUEST_SENSE; + if (drive->media == ide_floppy) { + pc->c[4] = 255; + pc->req_xfer = 18; + } else { + pc->c[4] = 20; + pc->req_xfer = 20; + } +} +EXPORT_SYMBOL_GPL(ide_create_request_sense_cmd); + +/* + * Called when an error was detected during the last packet command. + * We queue a request sense packet command in the head of the request list. + */ +void ide_retry_pc(ide_drive_t *drive, struct gendisk *disk) +{ + struct request *rq = &drive->request_sense_rq; + struct ide_atapi_pc *pc = &drive->request_sense_pc; + + (void)ide_read_error(drive); + ide_create_request_sense_cmd(drive, pc); + if (drive->media == ide_tape) + set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); + ide_queue_pc_head(drive, disk, pc, rq); +} +EXPORT_SYMBOL_GPL(ide_retry_pc); + int ide_scsi_expiry(ide_drive_t *drive) { struct ide_atapi_pc *pc = drive->pc; @@ -219,7 +249,6 @@ EXPORT_SYMBOL_GPL(ide_scsi_expiry); /* TODO: unify the code thus making some arguments go away */ ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), - void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { struct ide_atapi_pc *pc = drive->pc; @@ -299,7 +328,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, debug_log("[cmd %x]: check condition\n", rq->cmd[0]); /* Retry operation */ - retry_pc(drive); + ide_retry_pc(drive, rq->rq_disk); /* queued, but not started */ return ide_stopped; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 6a1ade8ca9fe..6e62ffafc562 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -193,34 +193,11 @@ static void ide_floppy_callback(ide_drive_t *drive, int dsc) idefloppy_end_request(drive, uptodate, 0); } -void ide_floppy_create_request_sense_cmd(struct ide_atapi_pc *pc) -{ - ide_init_pc(pc); - pc->c[0] = GPCMD_REQUEST_SENSE; - pc->c[4] = 255; - pc->req_xfer = 18; -} - -/* - * Called when an error was detected during the last packet command. We queue a - * request sense packet command in the head of the request list. - */ -static void idefloppy_retry_pc(ide_drive_t *drive) -{ - struct ide_floppy_obj *floppy = drive->driver_data; - struct request *rq = &drive->request_sense_rq; - struct ide_atapi_pc *pc = &drive->request_sense_pc; - - (void)ide_read_error(drive); - ide_floppy_create_request_sense_cmd(pc); - ide_queue_pc_head(drive, floppy->disk, pc, rq); -} - /* The usual interrupt handler called during a packet command. */ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) { return ide_pc_intr(drive, idefloppy_pc_intr, idefloppy_update_buffers, - idefloppy_retry_pc, ide_io_buffers); + ide_io_buffers); } /* diff --git a/drivers/ide/ide-floppy.h b/drivers/ide/ide-floppy.h index e9e14c30fed7..ced5ceb474de 100644 --- a/drivers/ide/ide-floppy.h +++ b/drivers/ide/ide-floppy.h @@ -49,7 +49,6 @@ typedef struct ide_floppy_obj { /* ide-floppy.c */ void ide_floppy_create_mode_sense_cmd(struct ide_atapi_pc *, u8); void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *); -void ide_floppy_create_request_sense_cmd(struct ide_atapi_pc *); /* ide-floppy_ioctl.c */ int ide_floppy_format_ioctl(ide_drive_t *, struct file *, unsigned int, diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c index 5ffc4512d14b..9723ed9c61b4 100644 --- a/drivers/ide/ide-floppy_ioctl.c +++ b/drivers/ide/ide-floppy_ioctl.c @@ -195,7 +195,7 @@ static int ide_floppy_get_format_progress(ide_drive_t *drive, int __user *arg) int progress_indication = 0x10000; if (drive->atapi_flags & IDE_AFLAG_SRFP) { - ide_floppy_create_request_sense_cmd(&pc); + ide_create_request_sense_cmd(drive, &pc); if (ide_queue_pc_tail(drive, floppy->disk, &pc)) return -EIO; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 72ecc5657db2..72caca3cb7aa 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -581,31 +581,6 @@ static void ide_tape_callback(ide_drive_t *drive, int dsc) idetape_end_request(drive, uptodate, 0); } -static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc) -{ - ide_init_pc(pc); - pc->c[0] = REQUEST_SENSE; - pc->c[4] = 20; - pc->req_xfer = 20; -} - -/* - * idetape_retry_pc is called when an error was detected during the - * last packet command. We queue a request sense packet command in - * the head of the request list. - */ -static void idetape_retry_pc(ide_drive_t *drive) -{ - struct ide_tape_obj *tape = drive->driver_data; - struct request *rq = &drive->request_sense_rq; - struct ide_atapi_pc *pc = &drive->request_sense_pc; - - (void)ide_read_error(drive); - idetape_create_request_sense_cmd(pc); - set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); - ide_queue_pc_head(drive, tape->disk, pc, rq); -} - /* * Postpone the current request so that ide.c will be able to service requests * from another device on the same hwgroup while we are polling for DSC. @@ -653,7 +628,7 @@ static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) { return ide_pc_intr(drive, idetape_pc_intr, idetape_update_buffers, - idetape_retry_pc, ide_tape_io_buffers); + ide_tape_io_buffers); } /* @@ -789,7 +764,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) printk(KERN_ERR "ide-tape: %s: I/O error, ", tape->name); /* Retry operation */ - idetape_retry_pc(drive); + ide_retry_pc(drive, tape->disk); return ide_stopped; } pc->error = 0; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index f71d1b34c3b1..ff2b19909838 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -275,7 +275,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) */ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { - return ide_pc_intr(drive, idescsi_pc_intr, NULL, NULL, ide_io_buffers); + return ide_pc_intr(drive, idescsi_pc_intr, NULL, ide_io_buffers); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) diff --git a/include/linux/ide.h b/include/linux/ide.h index 908b4fc9772c..d88cced0d02c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1169,13 +1169,13 @@ enum { REQ_IDETAPE_WRITE = (1 << 3), }; -void ide_queue_pc_head(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *, - struct request *); int ide_queue_pc_tail(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *); int ide_do_test_unit_ready(ide_drive_t *, struct gendisk *); int ide_do_start_stop(ide_drive_t *, struct gendisk *, int); int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); +void ide_create_request_sense_cmd(ide_drive_t *, struct ide_atapi_pc *); +void ide_retry_pc(ide_drive_t *, struct gendisk *); static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) { @@ -1186,7 +1186,6 @@ int ide_scsi_expiry(ide_drive_t *); ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), - void (*retry_pc)(ide_drive_t *), int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int)); ide_startstop_t ide_transfer_pc(ide_drive_t *, -- cgit v1.2.3 From 85e39035ca381846b031690f4d1ac1f0660da0a2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:32 +0200 Subject: ide: add ->pc_{update,io}_buffers methods Add ->pc_{update,io}_buffers methods to ide_drive_t and use them instead of {update,io}_buffers ide_pc_intr() arguments. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 14 ++++++-------- drivers/ide/ide-floppy.c | 7 ++++--- drivers/ide/ide-tape.c | 7 ++++--- drivers/scsi/ide-scsi.c | 6 ++++-- include/linux/ide.h | 9 +++++---- 5 files changed, 23 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index d758dcd87178..f46bc5124e00 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -246,10 +246,7 @@ int ide_scsi_expiry(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_scsi_expiry); -/* TODO: unify the code thus making some arguments go away */ -ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, - void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), - int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) +ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; @@ -290,8 +287,8 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, pc->flags |= PC_FLAG_DMA_ERROR; } else { pc->xferred = pc->req_xfer; - if (update_buffers) - update_buffers(drive, pc); + if (drive->pc_update_buffers) + drive->pc_update_buffers(drive, pc); } debug_log("%s: DMA finished\n", drive->name); } @@ -386,7 +383,8 @@ cmd_finished: temp = 0; if (temp) { if (pc->sg) - io_buffers(drive, pc, temp, 0); + drive->pc_io_buffers(drive, pc, + temp, 0); else tp_ops->input_data(drive, NULL, pc->cur_pos, temp); @@ -410,7 +408,7 @@ cmd_finished: if ((drive->media == ide_floppy && !scsi && !pc->buf) || (drive->media == ide_tape && !scsi && pc->bh) || (scsi && pc->sg)) { - int done = io_buffers(drive, pc, bcount, + int done = drive->pc_io_buffers(drive, pc, bcount, !!(pc->flags & PC_FLAG_WRITING)); /* FIXME: don't do partial completions */ diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 6e62ffafc562..378a22ca55c1 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -196,8 +196,7 @@ static void ide_floppy_callback(ide_drive_t *drive, int dsc) /* The usual interrupt handler called during a packet command. */ static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) { - return ide_pc_intr(drive, idefloppy_pc_intr, idefloppy_update_buffers, - ide_io_buffers); + return ide_pc_intr(drive, idefloppy_pc_intr); } /* @@ -636,7 +635,9 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) *((u16 *)&gcw) = id[ATA_ID_CONFIG]; - drive->pc_callback = ide_floppy_callback; + drive->pc_callback = ide_floppy_callback; + drive->pc_update_buffers = idefloppy_update_buffers; + drive->pc_io_buffers = ide_io_buffers; if (((gcw[0] & 0x60) >> 5) == 1) drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 72caca3cb7aa..5c26e98e2e39 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -627,8 +627,7 @@ static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, */ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) { - return ide_pc_intr(drive, idetape_pc_intr, idetape_update_buffers, - ide_tape_io_buffers); + return ide_pc_intr(drive, idetape_pc_intr); } /* @@ -2211,7 +2210,9 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) u8 gcw[2]; u16 *ctl = (u16 *)&tape->caps[12]; - drive->pc_callback = ide_tape_callback; + drive->pc_callback = ide_tape_callback; + drive->pc_update_buffers = idetape_update_buffers; + drive->pc_io_buffers = ide_tape_io_buffers; spin_lock_init(&tape->lock); drive->dsc_overlap = 1; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index ff2b19909838..b4ba40436c4f 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -275,7 +275,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) */ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { - return ide_pc_intr(drive, idescsi_pc_intr, NULL, ide_io_buffers); + return ide_pc_intr(drive, idescsi_pc_intr); } static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) @@ -407,7 +407,9 @@ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi) set_bit(IDESCSI_LOG_CMD, &scsi->log); #endif /* IDESCSI_DEBUG_LOG */ - drive->pc_callback = ide_scsi_callback; + drive->pc_callback = ide_scsi_callback; + drive->pc_update_buffers = NULL; + drive->pc_io_buffers = ide_io_buffers; ide_proc_register_driver(drive, scsi->driver); } diff --git a/include/linux/ide.h b/include/linux/ide.h index d88cced0d02c..f7fc53d1a810 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -555,6 +555,10 @@ struct ide_drive_s { /* callback for packet commands */ void (*pc_callback)(struct ide_drive_s *, int); + void (*pc_update_buffers)(struct ide_drive_s *, struct ide_atapi_pc *); + int (*pc_io_buffers)(struct ide_drive_s *, struct ide_atapi_pc *, + unsigned int, int); + unsigned long atapi_flags; struct ide_atapi_pc request_sense_pc; @@ -1184,10 +1188,7 @@ static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) int ide_scsi_expiry(ide_drive_t *); -ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler, - void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *), - int (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int, - int)); +ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler); ide_startstop_t ide_transfer_pc(ide_drive_t *, ide_handler_t *, unsigned int, ide_expiry_t *); ide_startstop_t ide_issue_pc(ide_drive_t *, -- cgit v1.2.3 From aa5d2de7b080873f6d9ac3aede423c9713bf0caa Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:32 +0200 Subject: ide: make ide_pc_intr() static * Always use ide_pc_intr as a handler in ide_pc_intr(). * Remove no longer used ide*_pc_intr() and 'handler' argument from ide_{transfer_pc,pc_intr}(). * Make ide_pc_intr() static. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 15 +++++++++------ drivers/ide/ide-floppy.c | 10 ++-------- drivers/ide/ide-tape.c | 18 +++--------------- drivers/scsi/ide-scsi.c | 11 +---------- include/linux/ide.h | 4 +--- 5 files changed, 16 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f46bc5124e00..b558663418d8 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -246,7 +246,12 @@ int ide_scsi_expiry(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_scsi_expiry); -ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler) +/* + * This is the usual interrupt handler which will be called during a packet + * command. We will transfer some of the data (as requested by the drive) + * and will re-point interrupt handler to us. + */ +static ide_startstop_t ide_pc_intr(ide_drive_t *drive) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; @@ -425,10 +430,9 @@ cmd_finished: rq->cmd[0], bcount); next_irq: /* And set the interrupt handler again */ - ide_set_handler(drive, handler, timeout, expiry); + ide_set_handler(drive, ide_pc_intr, timeout, expiry); return ide_started; } -EXPORT_SYMBOL_GPL(ide_pc_intr); static u8 ide_read_ireason(ide_drive_t *drive) { @@ -464,8 +468,7 @@ static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) return ireason; } -ide_startstop_t ide_transfer_pc(ide_drive_t *drive, - ide_handler_t *handler, unsigned int timeout, +ide_startstop_t ide_transfer_pc(ide_drive_t *drive, unsigned int timeout, ide_expiry_t *expiry) { struct ide_atapi_pc *pc = drive->pc; @@ -491,7 +494,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, } /* Set the interrupt routine */ - ide_set_handler(drive, handler, timeout, expiry); + ide_set_handler(drive, ide_pc_intr, timeout, expiry); /* Begin DMA, if necessary */ if (pc->flags & PC_FLAG_DMA_OK) { diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 378a22ca55c1..7be3cd5daa9d 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -193,12 +193,6 @@ static void ide_floppy_callback(ide_drive_t *drive, int dsc) idefloppy_end_request(drive, uptodate, 0); } -/* The usual interrupt handler called during a packet command. */ -static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive) -{ - return ide_pc_intr(drive, idefloppy_pc_intr); -} - /* * What we have here is a classic case of a top half / bottom half interrupt * service routine. In interrupt mode, the device sends an interrupt to signal @@ -230,7 +224,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) * where the Busy flag was apparently being deasserted before the * unit was ready to receive data. This was happening on a * 1200 MHz Athlon system. 10/26/01 25msec is too short, - * 40 and 50msec work well. idefloppy_pc_intr will not be actually + * 40 and 50msec work well. ide_pc_intr will not be actually * used until after the packet is moved in about 50 msec. */ if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { @@ -241,7 +235,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) expiry = NULL; } - return ide_transfer_pc(drive, idefloppy_pc_intr, timeout, expiry); + return ide_transfer_pc(drive, timeout, expiry); } static void ide_floppy_report_error(idefloppy_floppy_t *floppy, diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 5c26e98e2e39..a148de623af0 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -618,18 +618,6 @@ static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, return bcount; } -/* - * This is the usual interrupt handler which will be called during a packet - * command. We will transfer some of the data (as requested by the drive) and - * will re-point interrupt handler to us. When data transfer is finished, we - * will act according to the algorithm described before - * idetape_issue_pc. - */ -static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) -{ - return ide_pc_intr(drive, idetape_pc_intr); -} - /* * Packet Command Interface * @@ -640,9 +628,9 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) * The handling will be done in three stages: * * 1. idetape_issue_pc will send the packet command to the drive, and will set - * the interrupt handler to idetape_pc_intr. + * the interrupt handler to ide_pc_intr. * - * 2. On each interrupt, idetape_pc_intr will be called. This step will be + * 2. On each interrupt, ide_pc_intr will be called. This step will be * repeated until the device signals us that no more interrupts will be issued. * * 3. ATAPI Tape media access commands have immediate status with a delayed @@ -668,7 +656,7 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive) */ static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) { - return ide_transfer_pc(drive, idetape_pc_intr, WAIT_TAPE_CMD, NULL); + return ide_transfer_pc(drive, WAIT_TAPE_CMD, NULL); } static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index b4ba40436c4f..8733fe349254 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -270,18 +270,9 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) return 0; } -/* - * Our interrupt handler. - */ -static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) -{ - return ide_pc_intr(drive, idescsi_pc_intr); -} - static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) { - return ide_transfer_pc(drive, idescsi_pc_intr, - ide_scsi_get_timeout(drive->pc), + return ide_transfer_pc(drive, ide_scsi_get_timeout(drive->pc), ide_scsi_expiry); } diff --git a/include/linux/ide.h b/include/linux/ide.h index f7fc53d1a810..449a1094700f 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1188,9 +1188,7 @@ static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) int ide_scsi_expiry(ide_drive_t *); -ide_startstop_t ide_pc_intr(ide_drive_t *drive, ide_handler_t *handler); -ide_startstop_t ide_transfer_pc(ide_drive_t *, - ide_handler_t *, unsigned int, ide_expiry_t *); +ide_startstop_t ide_transfer_pc(ide_drive_t *, unsigned int, ide_expiry_t *); ide_startstop_t ide_issue_pc(ide_drive_t *, ide_handler_t *, unsigned int, ide_expiry_t *); -- cgit v1.2.3 From baf08f0be6d986521bb2fbdc7af51fc4847da734 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:32 +0200 Subject: ide: make ide_transfer_pc() static * Move ->ticks field from struct ide_floppy_obj to ide_drive_t. * Move idefloppy_transfer_pc() to ide-atapi.c and make ide_transfer_pc() use it. * Always use ide_transfer_pc as a handler in ide_issue_pc(). * Remove no longer used idefloppy_start_pc_transfer(), ide*_transfer_pc() and 'handler' argument from ide_issue_pc(). * Make ide_transfer_pc() static. While at it: * idefloppy_transfer_pc() -> ide_delayed_transfer_pc() * IDEFLOPPY_TICKS_DELAY -> IDEFLOPPY_PC_DELAY * ->ticks -> ->pc_delay There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 41 ++++++++++++++++++++++----- drivers/ide/ide-floppy.c | 72 ++++++------------------------------------------ drivers/ide/ide-floppy.h | 3 +- drivers/ide/ide-tape.c | 6 +--- drivers/scsi/ide-scsi.c | 9 +----- include/linux/ide.h | 7 +++-- 6 files changed, 49 insertions(+), 89 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index b558663418d8..2521677e1f48 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -468,12 +468,22 @@ static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) return ireason; } -ide_startstop_t ide_transfer_pc(ide_drive_t *drive, unsigned int timeout, - ide_expiry_t *expiry) +static int ide_delayed_transfer_pc(ide_drive_t *drive) +{ + /* Send the actual packet */ + drive->hwif->tp_ops->output_data(drive, NULL, drive->pc->c, 12); + + /* Timeout for the packet command */ + return WAIT_FLOPPY_CMD; +} + +static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->hwgroup->rq; + ide_expiry_t *expiry; + unsigned int timeout; ide_startstop_t startstop; u8 ireason; @@ -493,6 +503,25 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, unsigned int timeout, return ide_do_reset(drive); } + /* + * If necessary schedule the packet transfer to occur 'timeout' + * miliseconds later in ide_delayed_transfer_pc() after the device + * says it's ready for a packet. + */ + if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { + timeout = drive->pc_delay; + expiry = &ide_delayed_transfer_pc; + } else { + if (drive->scsi) { + timeout = ide_scsi_get_timeout(pc); + expiry = ide_scsi_expiry; + } else { + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; + expiry = NULL; + } + } + /* Set the interrupt routine */ ide_set_handler(drive, ide_pc_intr, timeout, expiry); @@ -508,10 +537,8 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, unsigned int timeout, return ide_started; } -EXPORT_SYMBOL_GPL(ide_transfer_pc); -ide_startstop_t ide_issue_pc(ide_drive_t *drive, - ide_handler_t *handler, unsigned int timeout, +ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, ide_expiry_t *expiry) { struct ide_atapi_pc *pc = drive->pc; @@ -550,12 +577,12 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, /* Issue the packet command */ if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { - ide_execute_command(drive, ATA_CMD_PACKET, handler, + ide_execute_command(drive, ATA_CMD_PACKET, ide_transfer_pc, timeout, NULL); return ide_started; } else { ide_execute_pkt_cmd(drive); - return (*handler)(drive); + return ide_transfer_pc(drive); } } EXPORT_SYMBOL_GPL(ide_issue_pc); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 7be3cd5daa9d..2a34f1ad2284 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -73,7 +73,11 @@ #define CAPACITY_CURRENT 0x02 #define CAPACITY_NO_CARTRIDGE 0x03 -#define IDEFLOPPY_TICKS_DELAY HZ/20 /* default delay for ZIP 100 (50ms) */ +/* + * The following delay solves a problem with ATAPI Zip 100 drive where BSY bit + * was apparently being deasserted before the unit was ready to receive data. + */ +#define IDEFLOPPY_PC_DELAY (HZ/20) /* default delay for ZIP 100 (50ms) */ /* Error code returned in rq->errors to the higher part of the driver. */ #define IDEFLOPPY_ERROR_GENERAL 101 @@ -193,51 +197,6 @@ static void ide_floppy_callback(ide_drive_t *drive, int dsc) idefloppy_end_request(drive, uptodate, 0); } -/* - * What we have here is a classic case of a top half / bottom half interrupt - * service routine. In interrupt mode, the device sends an interrupt to signal - * that it is ready to receive a packet. However, we need to delay about 2-3 - * ticks before issuing the packet or we gets in trouble. - */ -static int idefloppy_transfer_pc(ide_drive_t *drive) -{ - /* Send the actual packet */ - drive->hwif->tp_ops->output_data(drive, NULL, drive->pc->c, 12); - - /* Timeout for the packet command */ - return WAIT_FLOPPY_CMD; -} - -/* - * Called as an interrupt (or directly). When the device says it's ready for a - * packet, we schedule the packet transfer to occur about 2-3 ticks later in - * transfer_pc. - */ -static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) -{ - idefloppy_floppy_t *floppy = drive->driver_data; - ide_expiry_t *expiry; - unsigned int timeout; - - /* - * The following delay solves a problem with ATAPI Zip 100 drives - * where the Busy flag was apparently being deasserted before the - * unit was ready to receive data. This was happening on a - * 1200 MHz Athlon system. 10/26/01 25msec is too short, - * 40 and 50msec work well. ide_pc_intr will not be actually - * used until after the packet is moved in about 50 msec. - */ - if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { - timeout = floppy->ticks; - expiry = &idefloppy_transfer_pc; - } else { - timeout = WAIT_FLOPPY_CMD; - expiry = NULL; - } - - return ide_transfer_pc(drive, timeout, expiry); -} - static void ide_floppy_report_error(idefloppy_floppy_t *floppy, struct ide_atapi_pc *pc) { @@ -281,8 +240,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, idefloppy_start_pc_transfer, - WAIT_FLOPPY_CMD, NULL); + return ide_issue_pc(drive, WAIT_FLOPPY_CMD, NULL); } void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) @@ -597,21 +555,7 @@ static sector_t idefloppy_capacity(ide_drive_t *drive) ide_devset_rw_field(bios_cyl, bios_cyl); ide_devset_rw_field(bios_head, bios_head); ide_devset_rw_field(bios_sect, bios_sect); - -static int get_ticks(ide_drive_t *drive) -{ - idefloppy_floppy_t *floppy = drive->driver_data; - return floppy->ticks; -} - -static int set_ticks(ide_drive_t *drive, int arg) -{ - idefloppy_floppy_t *floppy = drive->driver_data; - floppy->ticks = arg; - return 0; -} - -IDE_DEVSET(ticks, DS_SYNC, get_ticks, set_ticks); +ide_devset_rw_field(ticks, pc_delay); static const struct ide_proc_devset idefloppy_settings[] = { IDE_PROC_DEVSET(bios_cyl, 0, 1023), @@ -647,7 +591,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) if (!strncmp((char *)&id[ATA_ID_PROD], "IOMEGA ZIP 100 ATAPI", 20)) { drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE; /* This value will be visible in the /proc/ide/hdx/settings */ - floppy->ticks = IDEFLOPPY_TICKS_DELAY; + drive->pc_delay = IDEFLOPPY_PC_DELAY; blk_queue_max_sectors(drive->queue, 64); } diff --git a/drivers/ide/ide-floppy.h b/drivers/ide/ide-floppy.h index ced5ceb474de..00ad5f992dc0 100644 --- a/drivers/ide/ide-floppy.h +++ b/drivers/ide/ide-floppy.h @@ -20,8 +20,7 @@ typedef struct ide_floppy_obj { /* Last error information */ u8 sense_key, asc, ascq; - /* delay this long before sending packet command */ - u8 ticks; + int progress_indication; /* Device information */ diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index a148de623af0..622d5fed2dc5 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -654,10 +654,6 @@ static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, * again, the callback function will be called and then we will handle the next * request. */ -static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) -{ - return ide_transfer_pc(drive, WAIT_TAPE_CMD, NULL); -} static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc) @@ -705,7 +701,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, idetape_transfer_pc, WAIT_TAPE_CMD, NULL); + return ide_issue_pc(drive, WAIT_TAPE_CMD, NULL); } /* A mode sense command is used to "sense" tape parameters. */ diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 8733fe349254..7d3d03f98914 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -270,12 +270,6 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) return 0; } -static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) -{ - return ide_transfer_pc(drive, ide_scsi_get_timeout(drive->pc), - ide_scsi_expiry); -} - static inline int idescsi_set_direction(struct ide_atapi_pc *pc) { switch (pc->c[0]) { @@ -321,8 +315,7 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive, /* Set the current packet command */ drive->pc = pc; - return ide_issue_pc(drive, idescsi_transfer_pc, - ide_scsi_get_timeout(pc), ide_scsi_expiry); + return ide_issue_pc(drive, ide_scsi_get_timeout(pc), ide_scsi_expiry); } /* diff --git a/include/linux/ide.h b/include/linux/ide.h index 449a1094700f..85cb96c1fd61 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -531,6 +531,9 @@ struct ide_drive_s { u8 bios_head; /* BIOS/fdisk/LILO number of heads */ u8 bios_sect; /* BIOS/fdisk/LILO sectors per track */ + /* delay this long before sending packet command */ + u8 pc_delay; + unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */ unsigned int cyl; /* "real" number of cyls */ unsigned int drive_data; /* used by set_pio_mode/selectproc */ @@ -1188,9 +1191,7 @@ static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) int ide_scsi_expiry(ide_drive_t *); -ide_startstop_t ide_transfer_pc(ide_drive_t *, unsigned int, ide_expiry_t *); -ide_startstop_t ide_issue_pc(ide_drive_t *, - ide_handler_t *, unsigned int, ide_expiry_t *); +ide_startstop_t ide_issue_pc(ide_drive_t *, unsigned int, ide_expiry_t *); ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); -- cgit v1.2.3 From 5aeddf907f149cae7e19b7c23ccea3823d00698c Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 13 Oct 2008 21:39:34 +0200 Subject: ide: unify conversion macros Introduce to_ide_drv() and ide_drv_g() macros and replace the respective definitions of similar ones in each driver. There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 17 ++++++----------- drivers/ide/ide-floppy.c | 21 +++++++++------------ drivers/ide/ide-tape.c | 23 ++++++++--------------- include/linux/ide.h | 8 +++++++- 4 files changed, 30 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 465a92ca0179..8650ad43b324 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -52,11 +52,6 @@ static DEFINE_MUTEX(idecd_ref_mutex); -#define to_ide_cd(obj) container_of(obj, struct cdrom_info, kref) - -#define ide_cd_g(disk) \ - container_of((disk)->private_data, struct cdrom_info, driver) - static void ide_cd_release(struct kref *); static struct cdrom_info *ide_cd_get(struct gendisk *disk) @@ -64,7 +59,7 @@ static struct cdrom_info *ide_cd_get(struct gendisk *disk) struct cdrom_info *cd = NULL; mutex_lock(&idecd_ref_mutex); - cd = ide_cd_g(disk); + cd = ide_drv_g(disk, cdrom_info); if (cd) { if (ide_device_get(cd->drive)) cd = NULL; @@ -1941,7 +1936,7 @@ static void ide_cd_remove(ide_drive_t *drive) static void ide_cd_release(struct kref *kref) { - struct cdrom_info *info = to_ide_cd(kref); + struct cdrom_info *info = to_ide_drv(kref, cdrom_info); struct cdrom_device_info *devinfo = &info->devinfo; ide_drive_t *drive = info->drive; struct gendisk *g = info->disk; @@ -1999,7 +1994,7 @@ static int idecd_open(struct inode *inode, struct file *file) static int idecd_release(struct inode *inode, struct file *file) { struct gendisk *disk = inode->i_bdev->bd_disk; - struct cdrom_info *info = ide_cd_g(disk); + struct cdrom_info *info = ide_drv_g(disk, cdrom_info); cdrom_release(&info->devinfo, file); @@ -2051,7 +2046,7 @@ static int idecd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - struct cdrom_info *info = ide_cd_g(bdev->bd_disk); + struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info); int err; switch (cmd) { @@ -2072,13 +2067,13 @@ static int idecd_ioctl(struct inode *inode, struct file *file, static int idecd_media_changed(struct gendisk *disk) { - struct cdrom_info *info = ide_cd_g(disk); + struct cdrom_info *info = ide_drv_g(disk, cdrom_info); return cdrom_media_changed(&info->devinfo); } static int idecd_revalidate_disk(struct gendisk *disk) { - struct cdrom_info *info = ide_cd_g(disk); + struct cdrom_info *info = ide_drv_g(disk, cdrom_info); struct request_sense sense; ide_cd_read_toc(info->drive, &sense); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 2a34f1ad2284..2a54a25090cb 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -84,11 +84,6 @@ static DEFINE_MUTEX(idefloppy_ref_mutex); -#define to_ide_floppy(obj) container_of(obj, struct ide_floppy_obj, kref) - -#define ide_floppy_g(disk) \ - container_of((disk)->private_data, struct ide_floppy_obj, driver) - static void idefloppy_cleanup_obj(struct kref *); static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk) @@ -96,7 +91,7 @@ static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk) struct ide_floppy_obj *floppy = NULL; mutex_lock(&idefloppy_ref_mutex); - floppy = ide_floppy_g(disk); + floppy = ide_drv_g(disk, ide_floppy_obj); if (floppy) { if (ide_device_get(floppy->drive)) floppy = NULL; @@ -625,7 +620,7 @@ static void ide_floppy_remove(ide_drive_t *drive) static void idefloppy_cleanup_obj(struct kref *kref) { - struct ide_floppy_obj *floppy = to_ide_floppy(kref); + struct ide_floppy_obj *floppy = to_ide_drv(kref, ide_floppy_obj); ide_drive_t *drive = floppy->drive; struct gendisk *g = floppy->disk; @@ -733,7 +728,7 @@ out_put_floppy: static int idefloppy_release(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; - struct ide_floppy_obj *floppy = ide_floppy_g(disk); + struct ide_floppy_obj *floppy = ide_drv_g(disk, ide_floppy_obj); ide_drive_t *drive = floppy->drive; debug_log("Reached %s\n", __func__); @@ -752,7 +747,8 @@ static int idefloppy_release(struct inode *inode, struct file *filp) static int idefloppy_getgeo(struct block_device *bdev, struct hd_geometry *geo) { - struct ide_floppy_obj *floppy = ide_floppy_g(bdev->bd_disk); + struct ide_floppy_obj *floppy = ide_drv_g(bdev->bd_disk, + ide_floppy_obj); ide_drive_t *drive = floppy->drive; geo->heads = drive->bios_head; @@ -783,7 +779,8 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - struct ide_floppy_obj *floppy = ide_floppy_g(bdev->bd_disk); + struct ide_floppy_obj *floppy = ide_drv_g(bdev->bd_disk, + ide_floppy_obj); ide_drive_t *drive = floppy->drive; struct ide_atapi_pc pc; void __user *argp = (void __user *)arg; @@ -812,7 +809,7 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, static int idefloppy_media_changed(struct gendisk *disk) { - struct ide_floppy_obj *floppy = ide_floppy_g(disk); + struct ide_floppy_obj *floppy = ide_drv_g(disk, ide_floppy_obj); ide_drive_t *drive = floppy->drive; int ret; @@ -828,7 +825,7 @@ static int idefloppy_media_changed(struct gendisk *disk) static int idefloppy_revalidate_disk(struct gendisk *disk) { - struct ide_floppy_obj *floppy = ide_floppy_g(disk); + struct ide_floppy_obj *floppy = ide_drv_g(disk, ide_floppy_obj); set_capacity(disk, idefloppy_capacity(floppy->drive)); return 0; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 622d5fed2dc5..2c235401aad1 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -267,11 +267,6 @@ static DEFINE_MUTEX(idetape_ref_mutex); static struct class *idetape_sysfs_class; -#define to_ide_tape(obj) container_of(obj, struct ide_tape_obj, kref) - -#define ide_tape_g(disk) \ - container_of((disk)->private_data, struct ide_tape_obj, driver) - static void ide_tape_release(struct kref *); static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) @@ -279,7 +274,7 @@ static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) struct ide_tape_obj *tape = NULL; mutex_lock(&idetape_ref_mutex); - tape = ide_tape_g(disk); + tape = ide_drv_g(disk, ide_tape_obj); if (tape) { if (ide_device_get(tape->drive)) tape = NULL; @@ -306,8 +301,6 @@ static void ide_tape_put(struct ide_tape_obj *tape) */ static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES]; -#define ide_tape_f(file) ((file)->private_data) - static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i) { struct ide_tape_obj *tape = NULL; @@ -1542,7 +1535,7 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op, static ssize_t idetape_chrdev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct ide_tape_obj *tape = ide_tape_f(file); + struct ide_tape_obj *tape = file->private_data; ide_drive_t *drive = tape->drive; ssize_t bytes_read, temp, actually_read = 0, rc; ssize_t ret = 0; @@ -1604,7 +1597,7 @@ finish: static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct ide_tape_obj *tape = ide_tape_f(file); + struct ide_tape_obj *tape = file->private_data; ide_drive_t *drive = tape->drive; ssize_t actually_written = 0; ssize_t ret = 0; @@ -1836,7 +1829,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) static int idetape_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct ide_tape_obj *tape = ide_tape_f(file); + struct ide_tape_obj *tape = file->private_data; ide_drive_t *drive = tape->drive; struct mtop mtop; struct mtget mtget; @@ -2013,7 +2006,7 @@ static void idetape_write_release(ide_drive_t *drive, unsigned int minor) static int idetape_chrdev_release(struct inode *inode, struct file *filp) { - struct ide_tape_obj *tape = ide_tape_f(filp); + struct ide_tape_obj *tape = filp->private_data; ide_drive_t *drive = tape->drive; unsigned int minor = iminor(inode); @@ -2272,7 +2265,7 @@ static void ide_tape_remove(ide_drive_t *drive) static void ide_tape_release(struct kref *kref) { - struct ide_tape_obj *tape = to_ide_tape(kref); + struct ide_tape_obj *tape = to_ide_drv(kref, ide_tape_obj); ide_drive_t *drive = tape->drive; struct gendisk *g = tape->disk; @@ -2355,7 +2348,7 @@ static int idetape_open(struct inode *inode, struct file *filp) static int idetape_release(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; - struct ide_tape_obj *tape = ide_tape_g(disk); + struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj); ide_tape_put(tape); @@ -2366,7 +2359,7 @@ static int idetape_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - struct ide_tape_obj *tape = ide_tape_g(bdev->bd_disk); + struct ide_tape_obj *tape = ide_drv_g(bdev->bd_disk, ide_tape_obj); ide_drive_t *drive = tape->drive; int err = generic_ide_ioctl(drive, file, bdev, cmd, arg); if (err == -EINVAL) diff --git a/include/linux/ide.h b/include/linux/ide.h index 85cb96c1fd61..350ef47ed616 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -570,7 +570,13 @@ struct ide_drive_s { typedef struct ide_drive_s ide_drive_t; -#define to_ide_device(dev)container_of(dev, ide_drive_t, gendev) +#define to_ide_device(dev) container_of(dev, ide_drive_t, gendev) + +#define to_ide_drv(obj, cont_type) \ + container_of(obj, struct cont_type, kref) + +#define ide_drv_g(disk, cont_type) \ + container_of((disk)->private_data, struct cont_type, driver) struct ide_task_s; struct ide_port_info; -- cgit v1.2.3 From b22b2ca4ff0730b14d14bcc36bd1b84873557512 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 13 Oct 2008 21:39:34 +0200 Subject: ide: add drive->debug_mask switch Add a debugging on/off switch for controlling driver debugging messages dynamically. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 350ef47ed616..fcd98e1d1863 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -545,6 +545,9 @@ struct ide_drive_s { int lun; /* logical unit */ int crc_count; /* crc counter to reduce drive speed */ + + unsigned long debug_mask; /* debugging levels switch */ + #ifdef CONFIG_BLK_DEV_IDEACPI struct ide_acpi_drive_link *acpidata; #endif -- cgit v1.2.3 From e1c7c4641aae8d278fca62b3b5cffad3a8dcb0a4 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 13 Oct 2008 21:39:35 +0200 Subject: ide: add a driver-wide debugging macro Add __ide_debug_log() debugging macro which is controlled by drive->debug_mask. The macro has to have the macro DRV_NAME defined in each driver before use. Also, add different debugging levels depending on the functionality debugged. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index fcd98e1d1863..02c4c642e6a5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -927,6 +927,26 @@ static inline void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t * #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0; #endif +enum { + /* enter/exit functions */ + IDE_DBG_FUNC = (1 << 0), + /* sense key/asc handling */ + IDE_DBG_SENSE = (1 << 1), + /* packet commands handling */ + IDE_DBG_PC = (1 << 2), + /* request handling */ + IDE_DBG_RQ = (1 << 3), + /* driver probing/setup */ + IDE_DBG_PROBE = (1 << 4), +}; + +/* DRV_NAME has to be defined in the driver before using the macro below */ +#define __ide_debug_log(lvl, fmt, args...) \ +{ \ + if (unlikely(drive->debug_mask & lvl)) \ + printk(KERN_INFO DRV_NAME ": " fmt, ## args); \ +} + /* * Power Management step value (rq->pm->pm_step). * -- cgit v1.2.3 From 35c137531245118962eb40a550661afe317bec03 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:36 +0200 Subject: ide-disk: set_addressing() fixes * Return -EIO if arg > 0 and LBA48 is unsupported. * No need to reset ->addressing. * Make ->addressing a single bit flag. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 11 +++++------ include/linux/ide.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 01846f244b40..65c499aab664 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -686,14 +686,13 @@ static int set_addressing(ide_drive_t *drive, int arg) if (arg < 0 || arg > 2) return -EINVAL; - drive->addressing = 0; - - if (drive->hwif->host_flags & IDE_HFLAG_NO_LBA48) - return 0; - - if (ata_id_lba48_enabled(drive->id) == 0) + if (arg && ((drive->hwif->host_flags & IDE_HFLAG_NO_LBA48) || + ata_id_lba48_enabled(drive->id) == 0)) return -EIO; + if (arg == 2) + arg = 0; + drive->addressing = arg; return 0; diff --git a/include/linux/ide.h b/include/linux/ide.h index 02c4c642e6a5..90d53c99fe92 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -509,7 +509,7 @@ struct ide_drive_s { unsigned sleeping : 1; /* 1=sleeping & sleep field valid */ unsigned post_reset : 1; unsigned udma33_warned : 1; - unsigned addressing : 2; /* 0=28-bit, 1=48-bit, 2=48-bit doing 28-bit */ + unsigned addressing : 1; /* 0=28-bit, 1=48-bit */ unsigned wcache : 1; /* status of write cache */ unsigned nowerr : 1; /* used for ignoring ATA_DF */ -- cgit v1.2.3 From 97100fc816badbbc162644cfde7ad39ae9211fb4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:36 +0200 Subject: ide: add device flags Add 'unsigned long dev_flags' to ide_drive_t and convert bitfields to IDE_DFLAG_* flags. While at it: - IDE_DFLAG_ADDRESSING -> IDE_DFLAG_LBA48 - fixup some comments - remove needless g->flags zeroing from ide*_probe() There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-acpi.c | 12 +++-- drivers/ide/ide-atapi.c | 21 +++++---- drivers/ide/ide-cd.c | 17 ++++--- drivers/ide/ide-disk.c | 94 ++++++++++++++++++++++--------------- drivers/ide/ide-dma.c | 7 +-- drivers/ide/ide-floppy.c | 9 ++-- drivers/ide/ide-io.c | 55 +++++++++++++--------- drivers/ide/ide-ioctls.c | 21 ++++++--- drivers/ide/ide-iops.c | 24 ++++++---- drivers/ide/ide-lib.c | 2 +- drivers/ide/ide-probe.c | 104 ++++++++++++++++++++++------------------- drivers/ide/ide-proc.c | 6 +-- drivers/ide/ide-tape.c | 30 +++++++----- drivers/ide/ide-taskfile.c | 14 +++--- drivers/ide/ide.c | 35 ++++++++------ drivers/ide/legacy/ht6560b.c | 9 ++-- drivers/ide/pci/amd74xx.c | 2 +- drivers/ide/pci/cmd640.c | 14 +++--- drivers/ide/pci/it821x.c | 2 +- drivers/ide/pci/ns87415.c | 11 +++-- drivers/ide/pci/pdc202xx_old.c | 4 +- drivers/ide/pci/sc1200.c | 3 +- drivers/ide/pci/trm290.c | 4 +- drivers/ide/ppc/pmac.c | 4 +- drivers/scsi/ide-scsi.c | 9 ++-- include/linux/ide.h | 100 ++++++++++++++++++++++++++++----------- 26 files changed, 370 insertions(+), 243 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index 2427c380b3dc..244a8a052ce8 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -290,7 +290,7 @@ static int do_drive_get_GTF(ide_drive_t *drive, DEBPRINT("ENTER: %s at %s, port#: %d, hard_port#: %d\n", hwif->name, dev->bus_id, port, hwif->channel); - if (!drive->present) { + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) { DEBPRINT("%s drive %d:%d not present\n", hwif->name, hwif->channel, port); goto out; @@ -420,8 +420,9 @@ static int do_drive_set_taskfiles(ide_drive_t *drive, DEBPRINT("ENTER: %s, hard_port#: %d\n", drive->name, drive->dn); - if (!drive->present) + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) goto out; + if (!gtf_count) /* shouldn't be here */ goto out; @@ -660,7 +661,8 @@ void ide_acpi_set_state(ide_hwif_t *hwif, int on) if (!drive->acpidata->obj_handle) drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive); - if (drive->acpidata->obj_handle && drive->present) { + if (drive->acpidata->obj_handle && + (drive->dev_flags & IDE_DFLAG_PRESENT)) { acpi_bus_set_power(drive->acpidata->obj_handle, on? ACPI_STATE_D0: ACPI_STATE_D3); } @@ -720,7 +722,7 @@ void ide_acpi_port_init_devices(ide_hwif_t *hwif) memset(drive->acpidata, 0, sizeof(*drive->acpidata)); - if (!drive->present) + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) continue; err = taskfile_lib_get_identify(drive, drive->acpidata->idbuff); @@ -745,7 +747,7 @@ void ide_acpi_port_init_devices(ide_hwif_t *hwif) for (i = 0; i < MAX_DRIVES; i++) { drive = &hwif->drives[i]; - if (drive->present) + if (drive->dev_flags & IDE_DFLAG_PRESENT) /* Execute ACPI startup code */ ide_acpi_exec_tfs(drive); } diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2521677e1f48..a1d8c3557a42 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -261,7 +261,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) ide_expiry_t *expiry; unsigned int timeout, temp; u16 bcount; - u8 stat, ireason, scsi = drive->scsi, dsc = 0; + u8 stat, ireason, scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI), dsc = 0; debug_log("Enter %s - interrupt handler\n", __func__); @@ -494,7 +494,8 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) } ireason = ide_read_ireason(drive); - if (drive->media == ide_tape && !drive->scsi) + if (drive->media == ide_tape && + (drive->dev_flags & IDE_DFLAG_SCSI) == 0) ireason = ide_wait_ireason(drive, ireason); if ((ireason & ATAPI_COD) == 0 || (ireason & ATAPI_IO)) { @@ -512,7 +513,7 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) timeout = drive->pc_delay; expiry = &ide_delayed_transfer_pc; } else { - if (drive->scsi) { + if (drive->dev_flags & IDE_DFLAG_SCSI) { timeout = ide_scsi_get_timeout(pc); expiry = ide_scsi_expiry; } else { @@ -544,14 +545,14 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; u16 bcount; - u8 dma = 0; + u8 dma = 0, scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI); /* We haven't transferred any data yet */ pc->xferred = 0; pc->cur_pos = pc->buf; /* Request to transfer the entire buffer at once */ - if (drive->media == ide_tape && !drive->scsi) + if (drive->media == ide_tape && scsi == 0) bcount = pc->req_xfer; else bcount = min(pc->req_xfer, 63 * 1024); @@ -561,19 +562,19 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, ide_dma_off(drive); } - if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) { - if (drive->scsi) + if ((pc->flags & PC_FLAG_DMA_OK) && + (drive->dev_flags & IDE_DFLAG_USING_DMA)) { + if (scsi) hwif->sg_mapped = 1; dma = !hwif->dma_ops->dma_setup(drive); - if (drive->scsi) + if (scsi) hwif->sg_mapped = 0; } if (!dma) pc->flags &= ~PC_FLAG_DMA_OK; - ide_pktcmd_tf_load(drive, drive->scsi ? 0 : IDE_TFLAG_OUT_DEVICE, - bcount, dma); + ide_pktcmd_tf_load(drive, scsi ? 0 : IDE_TFLAG_OUT_DEVICE, bcount, dma); /* Issue the packet command */ if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 8650ad43b324..ea7cd4e0b157 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -741,7 +741,7 @@ static ide_startstop_t cdrom_seek_intr(ide_drive_t *drive) if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) { if (--retry == 0) - drive->dsc_overlap = 0; + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; } return ide_stopped; } @@ -1129,7 +1129,7 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) } cd->dma = 0; } else - cd->dma = drive->using_dma; + cd->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); if (write) cd->devinfo.media_written = 1; @@ -1166,7 +1166,7 @@ static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq) else buf = rq->data; - info->dma = drive->using_dma; + info->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); /* * check if dma is safe @@ -1211,7 +1211,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (rq_data_dir(rq) == READ && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && - drive->dsc_overlap) { + (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)) { xferlen = 0; fn = cdrom_start_seek_continuation; @@ -1804,7 +1804,7 @@ static ide_proc_entry_t idecd_proc[] = { { NULL, 0, NULL, NULL } }; -ide_devset_rw_field(dsc_overlap, dsc_overlap); +ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP); static const struct ide_proc_devset idecd_settings[] = { IDE_PROC_DEVSET(dsc_overlap, 0, 1), @@ -1910,7 +1910,10 @@ static int ide_cdrom_setup(ide_drive_t *drive) /* set correct block size */ blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE); - drive->dsc_overlap = (drive->next != drive); + if (drive->next != drive) + drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP; + else + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; if (ide_cdrom_register(drive, nslots)) { printk(KERN_ERR "%s: %s failed to register device with the" @@ -1944,7 +1947,7 @@ static void ide_cd_release(struct kref *kref) kfree(info->toc); if (devinfo->handle == drive) unregister_cdrom(devinfo); - drive->dsc_overlap = 0; + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; drive->driver_data = NULL; blk_queue_prep_rq(drive->queue, NULL); g->private_data = NULL; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 7ea075299bd9..7ee2c9d2e5c2 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -140,9 +140,9 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, sector_t block) { ide_hwif_t *hwif = HWIF(drive); - unsigned int dma = drive->using_dma; u16 nsectors = (u16)rq->nr_sectors; - u8 lba48 = (drive->addressing == 1) ? 1 : 0; + u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48); + u8 dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); ide_task_t task; struct ide_taskfile *tf = &task.tf; ide_startstop_t rc; @@ -237,7 +237,7 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq, { ide_hwif_t *hwif = HWIF(drive); - BUG_ON(drive->blocked); + BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED); if (!blk_fs_request(rq)) { blk_dump_rq_flags(rq, "ide_do_rw_disk - bad command"); @@ -452,7 +452,7 @@ static int proc_idedisk_read_cache char *out = page; int len; - if (drive->id_read) + if (drive->dev_flags & IDE_DFLAG_ID_READ) len = sprintf(out, "%i\n", drive->id[ATA_ID_BUF_SIZE] / 2); else len = sprintf(out, "(none)\n"); @@ -568,15 +568,20 @@ static int set_multcount(ide_drive_t *drive, int arg) return (drive->mult_count == arg) ? 0 : -EIO; } -ide_devset_get(nowerr, nowerr); +ide_devset_get_flag(nowerr, IDE_DFLAG_NOWERR); static int set_nowerr(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 1) return -EINVAL; - drive->nowerr = arg; + if (arg) + drive->dev_flags |= IDE_DFLAG_NOWERR; + else + drive->dev_flags &= ~IDE_DFLAG_NOWERR; + drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; + return 0; } @@ -599,7 +604,7 @@ static void update_ordered(ide_drive_t *drive) unsigned ordered = QUEUE_ORDERED_NONE; prepare_flush_fn *prep_fn = NULL; - if (drive->wcache) { + if (drive->dev_flags & IDE_DFLAG_WCACHE) { unsigned long long capacity; int barrier; /* @@ -611,8 +616,10 @@ static void update_ordered(ide_drive_t *drive) * not available so we don't need to recheck that. */ capacity = idedisk_capacity(drive); - barrier = ata_id_flush_enabled(id) && !drive->noflush && - (drive->addressing == 0 || capacity <= (1ULL << 28) || + barrier = ata_id_flush_enabled(id) && + (drive->dev_flags & IDE_DFLAG_NOFLUSH) == 0 && + ((drive->dev_flags & IDE_DFLAG_LBA48) == 0 || + capacity <= (1ULL << 28) || ata_id_flush_ext_enabled(id)); printk(KERN_INFO "%s: cache flushes %ssupported\n", @@ -628,7 +635,7 @@ static void update_ordered(ide_drive_t *drive) blk_queue_ordered(drive->queue, ordered, prep_fn); } -ide_devset_get(wcache, wcache); +ide_devset_get_flag(wcache, IDE_DFLAG_WCACHE); static int set_wcache(ide_drive_t *drive, int arg) { @@ -640,8 +647,12 @@ static int set_wcache(ide_drive_t *drive, int arg) if (ata_id_flush_enabled(drive->id)) { err = ide_do_setfeature(drive, arg ? SETFEATURES_WC_ON : SETFEATURES_WC_OFF, 0); - if (err == 0) - drive->wcache = arg; + if (err == 0) { + if (arg) + drive->dev_flags |= IDE_DFLAG_WCACHE; + else + drive->dev_flags &= ~IDE_DFLAG_WCACHE; + } } update_ordered(drive); @@ -677,7 +688,7 @@ static int set_acoustic(ide_drive_t *drive, int arg) return 0; } -ide_devset_get(addressing, addressing); +ide_devset_get_flag(addressing, IDE_DFLAG_LBA48); /* * drive->addressing: @@ -697,7 +708,10 @@ static int set_addressing(ide_drive_t *drive, int arg) if (arg == 2) arg = 0; - drive->addressing = arg; + if (arg) + drive->dev_flags |= IDE_DFLAG_LBA48; + else + drive->dev_flags &= ~IDE_DFLAG_LBA48; return 0; } @@ -743,20 +757,20 @@ static void idedisk_setup(ide_drive_t *drive) ide_proc_register_driver(drive, idkp->driver); - if (drive->id_read == 0) + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) return; - if (drive->removable) { + if (drive->dev_flags & IDE_DFLAG_REMOVABLE) { /* * Removable disks (eg. SYQUEST); ignore 'WD' drives */ if (m[0] != 'W' || m[1] != 'D') - drive->doorlocking = 1; + drive->dev_flags |= IDE_DFLAG_DOORLOCKING; } (void)set_addressing(drive, 1); - if (drive->addressing == 1) { + if (drive->dev_flags & IDE_DFLAG_LBA48) { int max_s = 2048; if (max_s > hwif->rqsize) @@ -772,7 +786,8 @@ static void idedisk_setup(ide_drive_t *drive) init_idedisk_capacity(drive); /* limit drive capacity to 137GB if LBA48 cannot be used */ - if (drive->addressing == 0 && drive->capacity64 > 1ULL << 28) { + if ((drive->dev_flags & IDE_DFLAG_LBA48) == 0 && + drive->capacity64 > 1ULL << 28) { printk(KERN_WARNING "%s: cannot use LBA48 - full capacity " "%llu sectors (%llu MB)\n", drive->name, (unsigned long long)drive->capacity64, @@ -780,13 +795,14 @@ static void idedisk_setup(ide_drive_t *drive) drive->capacity64 = 1ULL << 28; } - if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && drive->addressing) { + if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && + (drive->dev_flags & IDE_DFLAG_LBA48)) { if (drive->capacity64 > 1ULL << 28) { printk(KERN_INFO "%s: cannot use LBA48 DMA - PIO mode" " will be used for accessing sectors " "> %u\n", drive->name, 1 << 28); } else - drive->addressing = 0; + drive->dev_flags &= ~IDE_DFLAG_LBA48; } /* @@ -795,7 +811,7 @@ static void idedisk_setup(ide_drive_t *drive) */ capacity = idedisk_capacity(drive); - if (!drive->forced_geom) { + if ((drive->dev_flags & IDE_DFLAG_FORCED_GEOM) == 0) { if (ata_id_lba48_enabled(drive->id)) { /* compatibility */ drive->bios_sect = 63; @@ -830,14 +846,15 @@ static void idedisk_setup(ide_drive_t *drive) /* write cache enabled? */ if ((id[ATA_ID_CSFO] & 1) || ata_id_wcache_enabled(id)) - drive->wcache = 1; + drive->dev_flags |= IDE_DFLAG_WCACHE; set_wcache(drive, 1); } static void ide_cacheflush_p(ide_drive_t *drive) { - if (!drive->wcache || ata_id_flush_enabled(drive->id) == 0) + if (ata_id_flush_enabled(drive->id) == 0 || + (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) return; if (do_idedisk_flushcache(drive)) @@ -956,15 +973,16 @@ static int idedisk_open(struct inode *inode, struct file *filp) idkp->openers++; - if (drive->removable && idkp->openers == 1) { + if ((drive->dev_flags & IDE_DFLAG_REMOVABLE) && idkp->openers == 1) { check_disk_change(inode->i_bdev); /* * Ignore the return code from door_lock, * since the open() has already succeeded, * and the door_lock is irrelevant at this point. */ - if (drive->doorlocking && idedisk_set_doorlock(drive, 1)) - drive->doorlocking = 0; + if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) && + idedisk_set_doorlock(drive, 1)) + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; } return 0; } @@ -978,9 +996,10 @@ static int idedisk_release(struct inode *inode, struct file *filp) if (idkp->openers == 1) ide_cacheflush_p(drive); - if (drive->removable && idkp->openers == 1) { - if (drive->doorlocking && idedisk_set_doorlock(drive, 0)) - drive->doorlocking = 0; + if ((drive->dev_flags & IDE_DFLAG_REMOVABLE) && idkp->openers == 1) { + if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) && + idedisk_set_doorlock(drive, 0)) + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; } idkp->openers--; @@ -1031,12 +1050,13 @@ static int idedisk_media_changed(struct gendisk *disk) ide_drive_t *drive = idkp->drive; /* do not scan partitions twice if this is a removable device */ - if (drive->attach) { - drive->attach = 0; + if (drive->dev_flags & IDE_DFLAG_ATTACH) { + drive->dev_flags &= ~IDE_DFLAG_ATTACH; return 0; } + /* if removable, always assume it was changed */ - return drive->removable; + return !!(drive->dev_flags & IDE_DFLAG_REMOVABLE); } static int idedisk_revalidate_disk(struct gendisk *disk) @@ -1094,15 +1114,15 @@ static int ide_disk_probe(ide_drive_t *drive) if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); - drive->attach = 0; + drive->dev_flags &= ~IDE_DFLAG_ATTACH; } else - drive->attach = 1; + drive->dev_flags |= IDE_DFLAG_ATTACH; g->minors = IDE_DISK_MINORS; g->driverfs_dev = &drive->gendev; g->flags |= GENHD_FL_EXT_DEVT; - if (drive->removable) - g->flags |= GENHD_FL_REMOVABLE; + if (drive->dev_flags & IDE_DFLAG_REMOVABLE) + g->flags = GENHD_FL_REMOVABLE; set_capacity(g, idedisk_capacity(drive)); g->fops = &idedisk_ops; add_disk(g); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index ef2f1504c0d5..2dacd802c72c 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -397,7 +397,7 @@ EXPORT_SYMBOL_GPL(ide_dma_host_set); void ide_dma_off_quietly(ide_drive_t *drive) { - drive->using_dma = 0; + drive->dev_flags &= ~IDE_DFLAG_USING_DMA; ide_toggle_bounce(drive, 0); drive->hwif->dma_ops->dma_host_set(drive, 0); @@ -430,7 +430,7 @@ EXPORT_SYMBOL(ide_dma_off); void ide_dma_on(ide_drive_t *drive) { - drive->using_dma = 1; + drive->dev_flags |= IDE_DFLAG_USING_DMA; ide_toggle_bounce(drive, 1); drive->hwif->dma_ops->dma_host_set(drive, 1); @@ -727,7 +727,8 @@ static int ide_tune_dma(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 speed; - if (drive->nodma || ata_id_has_dma(drive->id) == 0) + if (ata_id_has_dma(drive->id) == 0 || + (drive->dev_flags & IDE_DFLAG_NODMA)) return 0; /* consult the list of known "bad" drives */ diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index a11ec86925a3..cdfadb01d07f 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -828,8 +828,8 @@ static int idefloppy_media_changed(struct gendisk *disk) int ret; /* do not scan partitions twice if this is a removable device */ - if (drive->attach) { - drive->attach = 0; + if (drive->dev_flags & IDE_DFLAG_ATTACH) { + drive->dev_flags &= ~IDE_DFLAG_ATTACH; return 0; } ret = !!(drive->atapi_flags & IDE_AFLAG_MEDIA_CHANGED); @@ -896,12 +896,13 @@ static int ide_floppy_probe(ide_drive_t *drive) drive->debug_mask = debug_mask; idefloppy_setup(drive, floppy); + drive->dev_flags |= IDE_DFLAG_ATTACH; g->minors = 1 << PARTN_BITS; g->driverfs_dev = &drive->gendev; - g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; + if (drive->dev_flags & IDE_DFLAG_REMOVABLE) + g->flags = GENHD_FL_REMOVABLE; g->fops = &idefloppy_ops; - drive->attach = 1; add_disk(g); return 0; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 1c51949833be..5f0ed59bac6b 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -184,7 +184,8 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * if (drive->media != ide_disk) break; /* Not supported? Switch to next step now. */ - if (!drive->wcache || ata_id_flush_enabled(drive->id) == 0) { + if (ata_id_flush_enabled(drive->id) == 0 || + (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) { ide_complete_power_step(drive, rq, 0, 0); return ide_stopped; } @@ -222,7 +223,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * if (drive->hwif->dma_ops == NULL) break; /* - * TODO: respect ->using_dma setting + * TODO: respect IDE_DFLAG_USING_DMA */ ide_set_dma(drive); break; @@ -287,7 +288,7 @@ static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq) if (blk_pm_suspend_request(rq)) { blk_stop_queue(drive->queue); } else { - drive->blocked = 0; + drive->dev_flags &= ~IDE_DFLAG_BLOCKED; blk_start_queue(drive->queue); } HWGROUP(drive)->rq = NULL; @@ -374,7 +375,8 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 { ide_hwif_t *hwif = drive->hwif; - if ((stat & ATA_BUSY) || ((stat & ATA_DF) && !drive->nowerr)) { + if ((stat & ATA_BUSY) || + ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else if (stat & ATA_ERR) { @@ -428,7 +430,8 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u { ide_hwif_t *hwif = drive->hwif; - if ((stat & ATA_BUSY) || ((stat & ATA_DF) && !drive->nowerr)) { + if ((stat & ATA_BUSY) || + ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else { @@ -607,7 +610,7 @@ static ide_startstop_t do_special (ide_drive_t *drive) if (set_pio_mode_abuse(drive->hwif, req_pio)) { /* - * take ide_lock for drive->[no_]unmask/[no_]io_32bit + * take ide_lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */ if (req_pio == 8 || req_pio == 9) { unsigned long flags; @@ -618,7 +621,8 @@ static ide_startstop_t do_special (ide_drive_t *drive) } else port_ops->set_pio_mode(drive, req_pio); } else { - int keep_dma = drive->using_dma; + int keep_dma = + !!(drive->dev_flags & IDE_DFLAG_USING_DMA); ide_set_pio(drive, req_pio); @@ -775,7 +779,7 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) if (blk_pm_suspend_request(rq) && pm->pm_step == ide_pm_state_start_suspend) /* Mark drive blocked when starting the suspend sequence. */ - drive->blocked = 1; + drive->dev_flags |= IDE_DFLAG_BLOCKED; else if (blk_pm_resume_request(rq) && pm->pm_step == ide_pm_state_start_resume) { /* @@ -895,7 +899,7 @@ void ide_stall_queue (ide_drive_t *drive, unsigned long timeout) if (timeout > WAIT_WORSTCASE) timeout = WAIT_WORSTCASE; drive->sleep = timeout + jiffies; - drive->sleeping = 1; + drive->dev_flags |= IDE_DFLAG_SLEEPING; } EXPORT_SYMBOL(ide_stall_queue); @@ -935,18 +939,23 @@ repeat: } do { - if ((!drive->sleeping || time_after_eq(jiffies, drive->sleep)) - && !elv_queue_empty(drive->queue)) { - if (!best - || (drive->sleeping && (!best->sleeping || time_before(drive->sleep, best->sleep))) - || (!best->sleeping && time_before(WAKEUP(drive), WAKEUP(best)))) - { + u8 dev_s = !!(drive->dev_flags & IDE_DFLAG_SLEEPING); + u8 best_s = (best && !!(best->dev_flags & IDE_DFLAG_SLEEPING)); + + if ((dev_s == 0 || time_after_eq(jiffies, drive->sleep)) && + !elv_queue_empty(drive->queue)) { + if (best == NULL || + (dev_s && (best_s == 0 || time_before(drive->sleep, best->sleep))) || + (best_s == 0 && time_before(WAKEUP(drive), WAKEUP(best)))) { if (!blk_queue_plugged(drive->queue)) best = drive; } } } while ((drive = drive->next) != hwgroup->drive); - if (best && best->nice1 && !best->sleeping && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) { + + if (best && (best->dev_flags & IDE_DFLAG_NICE1) && + (best->dev_flags & IDE_DFLAG_SLEEPING) == 0 && + best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) { long t = (signed long)(WAKEUP(best) - jiffies); if (t >= WAIT_MIN_SLEEP) { /* @@ -955,7 +964,7 @@ repeat: */ drive = best->next; do { - if (!drive->sleeping + if ((drive->dev_flags & IDE_DFLAG_SLEEPING) == 0 && time_before(jiffies - best->service_time, WAKEUP(drive)) && time_before(WAKEUP(drive), jiffies + t)) { @@ -1026,7 +1035,9 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) hwgroup->rq = NULL; drive = hwgroup->drive; do { - if (drive->sleeping && (!sleeping || time_before(drive->sleep, sleep))) { + if ((drive->dev_flags & IDE_DFLAG_SLEEPING) && + (sleeping == 0 || + time_before(drive->sleep, sleep))) { sleeping = 1; sleep = drive->sleep; } @@ -1075,7 +1086,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) } hwgroup->hwif = hwif; hwgroup->drive = drive; - drive->sleeping = 0; + drive->dev_flags &= ~IDE_DFLAG_SLEEPING; drive->service_start = jiffies; if (blk_queue_plugged(drive->queue)) { @@ -1109,7 +1120,9 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * We count how many times we loop here to make sure we service * all drives in the hwgroup without looping for ever */ - if (drive->blocked && !blk_pm_request(rq) && !(rq->cmd_flags & REQ_PREEMPT)) { + if ((drive->dev_flags & IDE_DFLAG_BLOCKED) && + blk_pm_request(rq) == 0 && + (rq->cmd_flags & REQ_PREEMPT) == 0) { drive = drive->next ? drive->next : hwgroup->drive; if (loops++ < 4 && !blk_queue_plugged(drive->queue)) goto again; @@ -1491,7 +1504,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) */ hwif->ide_dma_clear_irq(drive); - if (drive->unmask) + if (drive->dev_flags & IDE_DFLAG_UNMASK) local_irq_enable_in_hardirq(); /* service this interrupt, may set handler for next interrupt */ startstop = handler(drive); diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c index cf01564901af..a90945f49792 100644 --- a/drivers/ide/ide-ioctls.c +++ b/drivers/ide/ide-ioctls.c @@ -62,7 +62,7 @@ static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142; int rc = 0; - if (drive->id_read == 0) { + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) { rc = -ENOMSG; goto out; } @@ -86,8 +86,10 @@ out: static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg) { - return put_user((drive->dsc_overlap << IDE_NICE_DSC_OVERLAP) | - (drive->nice1 << IDE_NICE_1), (long __user *)arg); + return put_user((!!(drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) + << IDE_NICE_DSC_OVERLAP) | + (!!(drive->dev_flags & IDE_DFLAG_NICE1) + << IDE_NICE_1), (long __user *)arg); } static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) @@ -97,11 +99,18 @@ static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && (drive->media == ide_disk || drive->media == ide_floppy || - drive->scsi)) + (drive->dev_flags & IDE_DFLAG_SCSI))) return -EPERM; - drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; - drive->nice1 = (arg >> IDE_NICE_1) & 1; + if ((arg >> IDE_NICE_DSC_OVERLAP) & 1) + drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP; + else + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; + + if ((arg >> IDE_NICE_1) & 1) + drive->dev_flags |= IDE_DFLAG_NICE1; + else + drive->dev_flags &= ~IDE_DFLAG_NICE1; return 0; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 0a2fd3b37ac4..cec744cbbde0 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -647,7 +647,7 @@ u8 eighty_ninty_three (ide_drive_t *drive) return 1; no_80w: - if (drive->udma33_warned == 1) + if (drive->dev_flags & IDE_DFLAG_UDMA33_WARNED) return 0; printk(KERN_WARNING "%s: %s side 80-wire cable detection failed, " @@ -655,7 +655,7 @@ no_80w: drive->name, hwif->cbl == ATA_CBL_PATA80 ? "drive" : "host"); - drive->udma33_warned = 1; + drive->dev_flags |= IDE_DFLAG_UDMA33_WARNED; return 0; } @@ -711,7 +711,7 @@ int ide_driveid_update(ide_drive_t *drive) kfree(id); - if (drive->using_dma && ide_id_dma_bug(drive)) + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) && ide_id_dma_bug(drive)) ide_dma_off(drive); return 1; @@ -790,7 +790,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) skip: #ifdef CONFIG_BLK_DEV_IDEDMA - if (speed >= XFER_SW_DMA_0 && drive->using_dma) + if (speed >= XFER_SW_DMA_0 && (drive->dev_flags & IDE_DFLAG_USING_DMA)) hwif->dma_ops->dma_host_set(drive, 1); else if (hwif->dma_ops) /* check if host supports DMA */ ide_dma_off_quietly(drive); @@ -1016,9 +1016,13 @@ static void ide_disk_pre_reset(ide_drive_t *drive) drive->special.all = 0; drive->special.b.set_geometry = legacy; drive->special.b.recalibrate = legacy; + drive->mult_count = 0; - if (!drive->keep_settings && !drive->using_dma) + + if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 && + (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) drive->mult_req = 0; + if (drive->mult_req != drive->mult_count) drive->special.b.set_multmode = 1; } @@ -1030,18 +1034,18 @@ static void pre_reset(ide_drive_t *drive) if (drive->media == ide_disk) ide_disk_pre_reset(drive); else - drive->post_reset = 1; + drive->dev_flags |= IDE_DFLAG_POST_RESET; - if (drive->using_dma) { + if (drive->dev_flags & IDE_DFLAG_USING_DMA) { if (drive->crc_count) ide_check_dma_crc(drive); else ide_dma_off(drive); } - if (!drive->keep_settings) { - if (!drive->using_dma) { - drive->unmask = 0; + if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0) { + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) { + drive->dev_flags &= ~IDE_DFLAG_UNMASK; drive->io_32bit = 0; } return; diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index ed426dd0fdd8..9fc4cfb2a272 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -317,7 +317,7 @@ static void ide_dump_sector(ide_drive_t *drive) { ide_task_t task; struct ide_taskfile *tf = &task.tf; - int lba48 = (drive->addressing == 1) ? 1 : 0; + u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48); memset(&task, 0, sizeof(task)); if (lba48) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 237b9871f80a..57c741876536 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -121,7 +121,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) /* read 512 bytes of id info */ hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); - drive->id_read = 1; + drive->dev_flags |= IDE_DFLAG_ID_READ; + local_irq_enable(); #ifdef DEBUG printk(KERN_INFO "%s: dumping identify data\n", drive->name); @@ -153,8 +154,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) printk(KERN_INFO "%s: %s, ", drive->name, m); - drive->present = 1; - drive->dead = 0; + drive->dev_flags |= IDE_DFLAG_PRESENT; + drive->dev_flags &= ~IDE_DFLAG_DEAD; /* * Check for an ATAPI device @@ -172,14 +173,14 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) printk(KERN_CONT "cdrom or floppy?, assuming "); if (drive->media != ide_cdrom) { printk(KERN_CONT "FLOPPY"); - drive->removable = 1; + drive->dev_flags |= IDE_DFLAG_REMOVABLE; break; } } /* Early cdrom models used zero */ type = ide_cdrom; case ide_cdrom: - drive->removable = 1; + drive->dev_flags |= IDE_DFLAG_REMOVABLE; #ifdef CONFIG_PPC /* kludge for Apple PowerBook internal zip */ if (!strstr(m, "CD-ROM") && strstr(m, "ZIP")) { @@ -195,7 +196,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) break; case ide_optical: printk(KERN_CONT "OPTICAL"); - drive->removable = 1; + drive->dev_flags |= IDE_DFLAG_REMOVABLE; break; default: printk(KERN_CONT "UNKNOWN (type %d)", type); @@ -216,7 +217,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) /* CF devices are *not* removable in Linux definition of the term */ if (is_cfa == 0 && (id[ATA_ID_CONFIG] & (1 << 7))) - drive->removable = 1; + drive->dev_flags |= IDE_DFLAG_REMOVABLE; drive->media = ide_disk; @@ -226,7 +227,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) err_misc: kfree(id); - drive->present = 0; + drive->dev_flags &= ~IDE_DFLAG_PRESENT; return; } @@ -426,16 +427,15 @@ static int do_probe (ide_drive_t *drive, u8 cmd) ide_hwif_t *hwif = HWIF(drive); const struct ide_tp_ops *tp_ops = hwif->tp_ops; int rc; - u8 stat; + u8 present = !!(drive->dev_flags & IDE_DFLAG_PRESENT), stat; + + /* avoid waiting for inappropriate probes */ + if (present && drive->media != ide_disk && cmd == ATA_CMD_ID_ATA) + return 4; - if (drive->present) { - /* avoid waiting for inappropriate probes */ - if (drive->media != ide_disk && cmd == ATA_CMD_ID_ATA) - return 4; - } #ifdef DEBUG printk(KERN_INFO "probing for %s: present=%d, media=%d, probetype=%s\n", - drive->name, drive->present, drive->media, + drive->name, present, drive->media, (cmd == ATA_CMD_ID_ATA) ? "ATA" : "ATAPI"); #endif @@ -446,7 +446,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(drive); msleep(50); - if (ide_read_device(drive) != drive->select.all && !drive->present) { + if (ide_read_device(drive) != drive->select.all && present == 0) { if (drive->select.b.unit != 0) { /* exit with drive0 selected */ SELECT_DRIVE(&hwif->drives[0]); @@ -460,7 +460,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) stat = tp_ops->read_status(hwif); if (OK_STAT(stat, ATA_DRDY, ATA_BUSY) || - drive->present || cmd == ATA_CMD_ID_ATAPI) { + present || cmd == ATA_CMD_ID_ATAPI) { /* send cmd and wait */ if ((rc = try_to_identify(drive, cmd))) { /* failed: try again */ @@ -542,8 +542,8 @@ static void enable_nest (ide_drive_t *drive) * and presents things to the user as needed. * * Returns: 0 no device was found - * 1 device was found (note: drive->present might - * still be 0) + * 1 device was found + * (note: IDE_DFLAG_PRESENT might still be not set) */ static inline u8 probe_for_drive (ide_drive_t *drive) @@ -559,10 +559,10 @@ static inline u8 probe_for_drive (ide_drive_t *drive) * Also note that 0 everywhere means "can't do X" */ + drive->dev_flags &= ~IDE_DFLAG_ID_READ; + drive->id = kzalloc(SECTOR_SIZE, GFP_KERNEL); - drive->id_read = 0; - if(drive->id == NULL) - { + if (drive->id == NULL) { printk(KERN_ERR "ide: out of memory for id data.\n"); return 0; } @@ -571,14 +571,14 @@ static inline u8 probe_for_drive (ide_drive_t *drive) strcpy(m, "UNKNOWN"); /* skip probing? */ - if (!drive->noprobe) { + if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0) { retry: /* if !(success||timed-out) */ if (do_probe(drive, ATA_CMD_ID_ATA) >= 2) /* look for ATAPI device */ (void)do_probe(drive, ATA_CMD_ID_ATAPI); - if (!drive->present) + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) /* drive not found */ return 0; @@ -588,7 +588,7 @@ retry: } /* identification failed? */ - if (!drive->id_read) { + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) { if (drive->media == ide_disk) { printk(KERN_INFO "%s: non-IDE drive, CHS=%d/%d/%d\n", drive->name, drive->cyl, @@ -598,15 +598,17 @@ retry: } else { /* nuke it */ printk(KERN_WARNING "%s: Unknown device on bus refused identification. Ignoring.\n", drive->name); - drive->present = 0; + drive->dev_flags &= ~IDE_DFLAG_PRESENT; } } /* drive was found */ } - if(!drive->present) + + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) return 0; + /* The drive wasn't being helpful. Add generic info only */ - if (drive->id_read == 0) { + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) { generic_id(drive); return 1; } @@ -616,7 +618,7 @@ retry: ide_disk_init_mult_count(drive); } - return drive->present; + return !!(drive->dev_flags & IDE_DFLAG_PRESENT); } static void hwif_release_dev(struct device *dev) @@ -707,7 +709,8 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) ide_drive_t *drive = &hwif->drives[unit]; /* Ignore disks that we will not probe for later. */ - if (!drive->noprobe || drive->present) { + if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 || + (drive->dev_flags & IDE_DFLAG_PRESENT)) { SELECT_DRIVE(drive); hwif->tp_ops->set_irq(hwif, 1); mdelay(2); @@ -739,7 +742,7 @@ void ide_undecoded_slave(ide_drive_t *dev1) { ide_drive_t *dev0 = &dev1->hwif->drives[0]; - if ((dev1->dn & 1) == 0 || dev0->present == 0) + if ((dev1->dn & 1) == 0 || (dev0->dev_flags & IDE_DFLAG_PRESENT) == 0) return; /* If the models don't match they are not the same product */ @@ -759,7 +762,7 @@ void ide_undecoded_slave(ide_drive_t *dev1) /* Appears to be an IDE flash adapter with decode bugs */ printk(KERN_WARNING "ide-probe: ignoring undecoded slave\n"); - dev1->present = 0; + dev1->dev_flags &= ~IDE_DFLAG_PRESENT; } EXPORT_SYMBOL_GPL(ide_undecoded_slave); @@ -772,7 +775,8 @@ static int ide_probe_port(ide_hwif_t *hwif) BUG_ON(hwif->present); - if (hwif->drives[0].noprobe && hwif->drives[1].noprobe) + if ((hwif->drives[0].dev_flags & IDE_DFLAG_NOPROBE) && + (hwif->drives[1].dev_flags & IDE_DFLAG_NOPROBE)) return -EACCES; /* @@ -796,7 +800,7 @@ static int ide_probe_port(ide_hwif_t *hwif) ide_drive_t *drive = &hwif->drives[unit]; drive->dn = (hwif->channel ? 2 : 0) + unit; (void) probe_for_drive(drive); - if (drive->present) + if (drive->dev_flags & IDE_DFLAG_PRESENT) rc = 0; } @@ -820,17 +824,19 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) for (unit = 0; unit < MAX_DRIVES; unit++) { ide_drive_t *drive = &hwif->drives[unit]; - if (drive->present && port_ops && port_ops->quirkproc) - port_ops->quirkproc(drive); + if (drive->dev_flags & IDE_DFLAG_PRESENT) { + if (port_ops && port_ops->quirkproc) + port_ops->quirkproc(drive); + } } for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; - if (drive->present) { + if (drive->dev_flags & IDE_DFLAG_PRESENT) { ide_set_max_pio(drive); - drive->nice1 = 1; + drive->dev_flags |= IDE_DFLAG_NICE1; if (hwif->dma_ops) ide_set_dma(drive); @@ -840,10 +846,11 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; - if (hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) - drive->no_io_32bit = 1; + if ((hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) || + drive->id[ATA_ID_DWORD_IO]) + drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT; else - drive->no_io_32bit = drive->id[ATA_ID_DWORD_IO] ? 1 : 0; + drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT; } } @@ -957,7 +964,7 @@ static void ide_port_setup_devices(ide_hwif_t *hwif) for (i = 0; i < MAX_DRIVES; i++) { ide_drive_t *drive = &hwif->drives[i]; - if (!drive->present) + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) continue; if (ide_init_queue(drive)) { @@ -1151,12 +1158,13 @@ static struct kobject *ata_probe(dev_t dev, int *part, void *data) ide_hwif_t *hwif = data; int unit = *part >> PARTN_BITS; ide_drive_t *drive = &hwif->drives[unit]; - if (!drive->present) + + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) return NULL; if (drive->media == ide_disk) request_module("ide-disk"); - if (drive->scsi) + if (drive->dev_flags & IDE_DFLAG_SCSI) request_module("ide-scsi"); if (drive->media == ide_cdrom || drive->media == ide_optical) request_module("ide-cd"); @@ -1246,7 +1254,7 @@ static void drive_release_dev (struct device *dev) ide_remove_drive_from_hwgroup(drive); kfree(drive->id); drive->id = NULL; - drive->present = 0; + drive->dev_flags &= ~IDE_DFLAG_PRESENT; /* Messed up locking ... */ spin_unlock_irq(&ide_lock); blk_cleanup_queue(drive->queue); @@ -1325,7 +1333,7 @@ static void hwif_register_devices(ide_hwif_t *hwif) struct device *dev = &drive->gendev; int ret; - if (!drive->present) + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) continue; snprintf(dev->bus_id, BUS_ID_SIZE, "%u.%u", hwif->index, i); @@ -1352,9 +1360,9 @@ static void ide_port_init_devices(ide_hwif_t *hwif) if (hwif->host_flags & IDE_HFLAG_IO_32BIT) drive->io_32bit = 1; if (hwif->host_flags & IDE_HFLAG_UNMASK_IRQS) - drive->unmask = 1; + drive->dev_flags |= IDE_DFLAG_UNMASK; if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS) - drive->no_unmask = 1; + drive->dev_flags |= IDE_DFLAG_NO_UNMASK; if (port_ops && port_ops->init_dev) port_ops->init_dev(drive); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index e7030a491463..b26926487cc0 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -227,7 +227,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) ide_devset_rw(current_speed, xfer_rate); ide_devset_rw_field(init_speed, init_speed); -ide_devset_rw_field(nice1, nice1); +ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1); ide_devset_rw_field(number, dn); static const struct ide_proc_devset ide_generic_settings[] = { @@ -622,9 +622,7 @@ void ide_proc_port_register_devices(ide_hwif_t *hwif) for (d = 0; d < MAX_DRIVES; d++) { ide_drive_t *drive = &hwif->drives[d]; - if (!drive->present) - continue; - if (drive->proc) + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc) continue; drive->proc = proc_mkdir(drive->name, parent); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 2c235401aad1..103f9f161716 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -826,12 +826,13 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, */ stat = hwif->tp_ops->read_status(hwif); - if (!drive->dsc_overlap && !(rq->cmd[13] & REQ_IDETAPE_PC2)) + if ((drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) == 0 && + (rq->cmd[13] & REQ_IDETAPE_PC2) == 0) set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); - if (drive->post_reset == 1) { + if (drive->dev_flags & IDE_DFLAG_POST_RESET) { set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); - drive->post_reset = 0; + drive->dev_flags &= ~IDE_DFLAG_POST_RESET; } if (!test_and_clear_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags) && @@ -1354,7 +1355,7 @@ static int idetape_init_read(ide_drive_t *drive) * No point in issuing this if DSC overlap isn't supported, some * drives (Seagate STT3401A) will return an error. */ - if (drive->dsc_overlap) { + if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) { bytes_read = idetape_queue_rw_tail(drive, REQ_IDETAPE_READ, 0, tape->merge_bh); @@ -1630,7 +1631,7 @@ static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf, * point in issuing this if DSC overlap isn't supported, some * drives (Seagate STT3401A) will return an error. */ - if (drive->dsc_overlap) { + if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) { ssize_t retval = idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, 0, tape->merge_bh); @@ -2145,7 +2146,7 @@ static int divf_tdsc(ide_drive_t *drive) { return HZ; } static int divf_buffer(ide_drive_t *drive) { return 2; } static int divf_buffer_size(ide_drive_t *drive) { return 1024; } -ide_devset_rw_field(dsc_overlap, dsc_overlap); +ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP); ide_tape_devset_rw_field(debug_mask, debug_mask); ide_tape_devset_rw_field(tdsc, best_dsc_rw_freq); @@ -2192,15 +2193,19 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) drive->pc_io_buffers = ide_tape_io_buffers; spin_lock_init(&tape->lock); - drive->dsc_overlap = 1; + + drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP; + if (drive->hwif->host_flags & IDE_HFLAG_NO_DSC) { printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", tape->name); - drive->dsc_overlap = 0; + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; } + /* Seagate Travan drives do not support DSC overlap. */ if (strstr((char *)&drive->id[ATA_ID_PROD], "Seagate STT3401")) - drive->dsc_overlap = 0; + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; + tape->minor = minor; tape->name[0] = 'h'; tape->name[1] = 't'; @@ -2247,7 +2252,7 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) (*(u16 *)&tape->caps[16] * 512) / tape->buffer_size, tape->buffer_size / 1024, tape->best_dsc_rw_freq * 1000 / HZ, - drive->using_dma ? ", DMA":""); + (drive->dev_flags & IDE_DFLAG_USING_DMA) ? ", DMA" : ""); ide_proc_register_driver(drive, tape->driver); } @@ -2271,7 +2276,7 @@ static void ide_tape_release(struct kref *kref) BUG_ON(tape->merge_bh_size); - drive->dsc_overlap = 0; + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; drive->driver_data = NULL; device_destroy(idetape_sysfs_class, MKDEV(IDETAPE_MAJOR, tape->minor)); device_destroy(idetape_sysfs_class, @@ -2386,7 +2391,8 @@ static int ide_tape_probe(ide_drive_t *drive) if (drive->media != ide_tape) goto failed; - if (drive->id_read == 1 && !ide_check_atapi_device(drive, DRV_NAME)) { + if ((drive->dev_flags & IDE_DFLAG_ID_READ) && + ide_check_atapi_device(drive, DRV_NAME) == 0) { printk(KERN_ERR "ide-tape: %s: not supported by this version of" " the driver\n", drive->name); goto failed; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 487b18b3ebae..8da8d26db7ed 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -116,7 +116,8 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) WAIT_WORSTCASE, NULL); return ide_started; default: - if (drive->using_dma == 0 || dma_ops->dma_setup(drive)) + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || + dma_ops->dma_setup(drive)) return ide_stopped; dma_ops->dma_exec_cmd(drive, tf->command); dma_ops->dma_start(drive); @@ -469,13 +470,12 @@ static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq) if (ide_wait_stat(&startstop, drive, ATA_DRQ, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n", - drive->name, - drive->hwif->data_phase ? "MULT" : "", - drive->addressing ? "_EXT" : ""); + drive->name, drive->hwif->data_phase ? "MULT" : "", + (drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : ""); return startstop; } - if (!drive->unmask) + if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0) local_irq_disable(); ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL); @@ -591,7 +591,7 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE | IDE_TFLAG_IN_TF; - if (drive->addressing == 1) + if (drive->dev_flags & IDE_DFLAG_LBA48) args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB); if (req_task->out_flags.all) { @@ -694,7 +694,7 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) if ((args.tf_flags & IDE_TFLAG_FLAGGED_SET_IN_FLAGS) && req_task->in_flags.all == 0) { req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; - if (drive->addressing == 1) + if (drive->dev_flags & IDE_DFLAG_LBA48) req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 64997873b6d7..78776bbb537e 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -138,7 +138,7 @@ static void __ide_port_unregister_devices(ide_hwif_t *hwif) for (i = 0; i < MAX_DRIVES; i++) { ide_drive_t *drive = &hwif->drives[i]; - if (drive->present) { + if (drive->dev_flags & IDE_DFLAG_PRESENT) { spin_unlock_irq(&ide_lock); device_unregister(&drive->gendev); wait_for_completion(&drive->gendev_rel_comp); @@ -254,7 +254,7 @@ ide_devset_get(io_32bit, io_32bit); static int set_io_32bit(ide_drive_t *drive, int arg) { - if (drive->no_io_32bit) + if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT) return -EPERM; if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1)) @@ -265,19 +265,22 @@ static int set_io_32bit(ide_drive_t *drive, int arg) return 0; } -ide_devset_get(ksettings, keep_settings); +ide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS); static int set_ksettings(ide_drive_t *drive, int arg) { if (arg < 0 || arg > 1) return -EINVAL; - drive->keep_settings = arg; + if (arg) + drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS; + else + drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS; return 0; } -ide_devset_get(using_dma, using_dma); +ide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA); static int set_using_dma(ide_drive_t *drive, int arg) { @@ -339,17 +342,20 @@ static int set_pio_mode(ide_drive_t *drive, int arg) return 0; } -ide_devset_get(unmaskirq, unmask); +ide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK); static int set_unmaskirq(ide_drive_t *drive, int arg) { - if (drive->no_unmask) + if (drive->dev_flags & IDE_DFLAG_NO_UNMASK) return -EPERM; if (arg < 0 || arg > 1) return -EINVAL; - drive->unmask = arg; + if (arg) + drive->dev_flags |= IDE_DFLAG_UNMASK; + else + drive->dev_flags &= ~IDE_DFLAG_UNMASK; return 0; } @@ -713,16 +719,16 @@ static void ide_dev_apply_params(ide_drive_t *drive) if (ide_nodma & (1 << i)) { printk(KERN_INFO "ide: disallowing DMA for %s\n", drive->name); - drive->nodma = 1; + drive->dev_flags |= IDE_DFLAG_NODMA; } if (ide_noflush & (1 << i)) { printk(KERN_INFO "ide: disabling flush requests for %s\n", drive->name); - drive->noflush = 1; + drive->dev_flags |= IDE_DFLAG_NOFLUSH; } if (ide_noprobe & (1 << i)) { printk(KERN_INFO "ide: skipping probe for %s\n", drive->name); - drive->noprobe = 1; + drive->dev_flags |= IDE_DFLAG_NOPROBE; } if (ide_nowerr & (1 << i)) { printk(KERN_INFO "ide: ignoring the ATA_DF bit for %s\n", @@ -731,7 +737,7 @@ static void ide_dev_apply_params(ide_drive_t *drive) } if (ide_cdroms & (1 << i)) { printk(KERN_INFO "ide: forcing %s as a CD-ROM\n", drive->name); - drive->present = 1; + drive->dev_flags |= IDE_DFLAG_PRESENT; drive->media = ide_cdrom; /* an ATAPI device ignores DRDY */ drive->ready_stat = 0; @@ -740,11 +746,12 @@ static void ide_dev_apply_params(ide_drive_t *drive) drive->cyl = drive->bios_cyl = ide_disks_chs[i].cyl; drive->head = drive->bios_head = ide_disks_chs[i].head; drive->sect = drive->bios_sect = ide_disks_chs[i].sect; - drive->forced_geom = 1; + printk(KERN_INFO "ide: forcing %s as a disk (%d/%d/%d)\n", drive->name, drive->cyl, drive->head, drive->sect); - drive->present = 1; + + drive->dev_flags |= IDE_DFLAG_FORCED_GEOM | IDE_DFLAG_PRESENT; drive->media = ide_disk; drive->ready_stat = ATA_DRDY; } diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index 5123ea291d07..c7e5c2246b79 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -120,7 +120,8 @@ static void ht6560b_selectproc (ide_drive_t *drive) * Need to enforce prefetch sometimes because otherwise * it'll hang (hard). */ - if (drive->media != ide_disk || !drive->present) + if (drive->media != ide_disk || + (drive->dev_flags & IDE_DFLAG_PRESENT) == 0) select |= HT_PREFETCH_MODE; if (select != current_select || timing != current_timing) { @@ -249,11 +250,11 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state) */ if (state) { drive->drive_data |= t; /* enable prefetch mode */ - drive->no_unmask = 1; - drive->unmask = 0; + drive->dev_flags |= IDE_DFLAG_NO_UNMASK; + drive->dev_flags &= ~IDE_DFLAG_UNMASK; } else { drive->drive_data &= ~t; /* disable prefetch mode */ - drive->no_unmask = 0; + drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK; } spin_unlock_irqrestore(&ht6560b_lock, flags); diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index 824471f91bf5..7dbc692c84c3 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -92,7 +92,7 @@ static void amd_set_drive(ide_drive_t *drive, const u8 speed) ide_timing_compute(drive, speed, &t, T, UT); - if (peer->present) { + if (peer->dev_flags & IDE_DFLAG_PRESENT) { ide_timing_compute(peer, peer->current_speed, &p, T, UT); ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); } diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 7f39cdb41410..d3afdffcb07a 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -378,13 +378,13 @@ static void __set_prefetch_mode(ide_drive_t *drive, int mode) { if (mode) { /* want prefetch on? */ #if CMD640_PREFETCH_MASKS - drive->no_unmask = 1; - drive->unmask = 0; + drive->dev_flags |= IDE_DFLAG_NO_UNMASK; + drive->dev_flags &= ~IDE_DFLAG_UNMASK; #endif - drive->no_io_32bit = 0; + drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT; } else { - drive->no_unmask = 0; - drive->no_io_32bit = 1; + drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK; + drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT; drive->io_32bit = 0; } } @@ -471,7 +471,7 @@ static void program_drive_counts(ide_drive_t *drive, unsigned int index) ide_drive_t *peer = &hwif->drives[!drive->select.b.unit]; unsigned int mate = index ^ 1; - if (peer->present) { + if (peer->dev_flags & IDE_DFLAG_PRESENT) { if (setup_count < setup_counts[mate]) setup_count = setup_counts[mate]; if (active_count < active_counts[mate]) @@ -626,7 +626,7 @@ static void cmd640_init_dev(ide_drive_t *drive) */ check_prefetch(drive, i); printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch(%s) preserved\n", - i, drive->no_io_32bit ? "off" : "on"); + i, (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT) ? "off" : "on"); #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ } diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 46edd083b348..b761015ee190 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -454,7 +454,7 @@ static void it821x_quirkproc(ide_drive_t *drive) * IRQ mask as we may well be in PIO (eg rev 0x10) * for now and we know unmasking is safe on this chipset. */ - drive->unmask = 1; + drive->dev_flags |= IDE_DFLAG_UNMASK; } else { /* * Perform fixups on smart mode. We need to "lose" some diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 53bd645736d9..99e98e5e271c 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -137,7 +137,7 @@ static void __devinit superio_init_iops(struct hwif_s *hwif) static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 }; /* - * This routine either enables/disables (according to drive->present) + * This routine either enables/disables (according to IDE_DFLAG_PRESENT) * the IRQ associated with the port (HWIF(drive)), * and selects either PIO or DMA handshaking for the next I/O operation. */ @@ -153,7 +153,11 @@ static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) /* Adjust IRQ enable bit */ bit = 1 << (8 + hwif->channel); - new = drive->present ? (new & ~bit) : (new | bit); + + if (drive->dev_flags & IDE_DFLAG_PRESENT) + new &= ~bit; + else + new |= bit; /* Select PIO or DMA, DMA may only be selected for one drive/channel. */ bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1)); @@ -187,7 +191,8 @@ static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) static void ns87415_selectproc (ide_drive_t *drive) { - ns87415_prepare_drive (drive, drive->using_dma); + ns87415_prepare_drive(drive, + !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); } static int ns87415_dma_end(ide_drive_t *drive) diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index cb6d2a00c514..6d9240a9dcfa 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -168,7 +168,7 @@ static void pdc202xx_dma_start(ide_drive_t *drive) { if (drive->current_speed > XFER_UDMA_2) pdc_old_enable_66MHz_clock(drive->hwif); - if (drive->media != ide_disk || drive->addressing == 1) { + if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { struct request *rq = HWGROUP(drive)->rq; ide_hwif_t *hwif = HWIF(drive); unsigned long high_16 = hwif->extra_base - 16; @@ -188,7 +188,7 @@ static void pdc202xx_dma_start(ide_drive_t *drive) static int pdc202xx_dma_end(ide_drive_t *drive) { - if (drive->media != ide_disk || drive->addressing == 1) { + if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { ide_hwif_t *hwif = HWIF(drive); unsigned long high_16 = hwif->extra_base - 16; unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index bdc1fed41260..50405ed6f0cb 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -216,7 +216,8 @@ static void sc1200_set_pio_mode(ide_drive_t *drive, const u8 pio) if (mode != -1) { printk("SC1200: %s: changing (U)DMA mode\n", drive->name); ide_dma_off_quietly(drive); - if (ide_set_dma_mode(drive, mode) == 0 && drive->using_dma) + if (ide_set_dma_mode(drive, mode) == 0 && + (drive->dev_flags & IDE_DFLAG_USING_DMA)) hwif->dma_ops->dma_host_set(drive, 1); return; } diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index 4dfbc6a68b5b..c12ffbb28748 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -161,7 +161,7 @@ static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) } /* enable IRQ if not probing */ - if (drive->present) { + if (drive->dev_flags & IDE_DFLAG_PRESENT) { reg = inw(hwif->config_data + 3); reg &= 0x13; reg &= ~(1 << hwif->channel); @@ -173,7 +173,7 @@ static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) static void trm290_selectproc (ide_drive_t *drive) { - trm290_prepare_drive(drive, drive->using_dma); + trm290_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); } static void trm290_dma_exec_cmd(ide_drive_t *drive, u8 command) diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index c3432da78d52..0a6d40cebe47 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -966,11 +966,11 @@ static void pmac_ide_init_dev(ide_drive_t *drive) if (pmif->mediabay) { #ifdef CONFIG_PMAC_MEDIABAY if (check_media_bay_by_base(pmif->regbase, MB_CD) == 0) { - drive->noprobe = 0; + drive->dev_flags &= ~IDE_DFLAG_NOPROBE; return; } #endif - drive->noprobe = 1; + drive->dev_flags |= IDE_DFLAG_NOPROBE; } } diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 7d3d03f98914..67e9ed95f669 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -331,7 +331,8 @@ static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *r if (blk_sense_request(rq) || blk_special_request(rq)) { struct ide_atapi_pc *pc = (struct ide_atapi_pc *)rq->special; - if (drive->using_dma && !idescsi_map_sg(drive, pc)) + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) && + idescsi_map_sg(drive, pc) == 0) pc->flags |= PC_FLAG_DMA_OK; return idescsi_issue_pc(drive, pc); @@ -415,7 +416,7 @@ static void ide_scsi_remove(ide_drive_t *drive) ide_scsi_put(scsi); - drive->scsi = 0; + drive->dev_flags &= ~IDE_DFLAG_SCSI; } static int ide_scsi_probe(ide_drive_t *); @@ -767,7 +768,7 @@ static int ide_scsi_probe(ide_drive_t *drive) !(host = scsi_host_alloc(&idescsi_template,sizeof(idescsi_scsi_t)))) return -ENODEV; - drive->scsi = 1; + drive->dev_flags |= IDE_DFLAG_SCSI; g = alloc_disk(1 << PARTN_BITS); if (!g) @@ -808,7 +809,7 @@ static int ide_scsi_probe(ide_drive_t *drive) put_disk(g); out_host_put: - drive->scsi = 0; + drive->dev_flags &= ~IDE_DFLAG_SCSI; scsi_host_put(host); return err; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 90d53c99fe92..b538d2e6dcbb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -459,6 +459,55 @@ enum { IDE_AFLAG_NO_AUTOCLOSE = (1 << 29), }; +/* device flags */ +enum { + /* restore settings after device reset */ + IDE_DFLAG_KEEP_SETTINGS = (1 << 0), + /* device is using DMA for read/write */ + IDE_DFLAG_USING_DMA = (1 << 1), + /* okay to unmask other IRQs */ + IDE_DFLAG_UNMASK = (1 << 2), + /* don't attempt flushes */ + IDE_DFLAG_NOFLUSH = (1 << 3), + /* DSC overlap */ + IDE_DFLAG_DSC_OVERLAP = (1 << 4), + /* give potential excess bandwidth */ + IDE_DFLAG_NICE1 = (1 << 5), + /* device is physically present */ + IDE_DFLAG_PRESENT = (1 << 6), + /* device ejected hint */ + IDE_DFLAG_DEAD = (1 << 7), + /* id read from device (synthetic if not set) */ + IDE_DFLAG_ID_READ = (1 << 8), + IDE_DFLAG_NOPROBE = (1 << 9), + /* need to do check_media_change() */ + IDE_DFLAG_REMOVABLE = (1 << 10), + /* needed for removable devices */ + IDE_DFLAG_ATTACH = (1 << 11), + IDE_DFLAG_FORCED_GEOM = (1 << 12), + /* disallow setting unmask bit */ + IDE_DFLAG_NO_UNMASK = (1 << 13), + /* disallow enabling 32-bit I/O */ + IDE_DFLAG_NO_IO_32BIT = (1 << 14), + /* for removable only: door lock/unlock works */ + IDE_DFLAG_DOORLOCKING = (1 << 15), + /* disallow DMA */ + IDE_DFLAG_NODMA = (1 << 16), + /* powermanagment told us not to do anything, so sleep nicely */ + IDE_DFLAG_BLOCKED = (1 << 17), + /* ide-scsi emulation */ + IDE_DFLAG_SCSI = (1 << 18), + /* sleeping & sleep field valid */ + IDE_DFLAG_SLEEPING = (1 << 19), + IDE_DFLAG_POST_RESET = (1 << 20), + IDE_DFLAG_UDMA33_WARNED = (1 << 21), + IDE_DFLAG_LBA48 = (1 << 22), + /* status of write cache */ + IDE_DFLAG_WCACHE = (1 << 23), + /* used for ignoring ATA_DF */ + IDE_DFLAG_NOWERR = (1 << 24), +}; + struct ide_drive_s { char name[4]; /* drive name, such as "hda" */ char driver_req[10]; /* requests specific driver */ @@ -475,6 +524,8 @@ struct ide_drive_s { #endif struct hwif_s *hwif; /* actually (ide_hwif_t *) */ + unsigned long dev_flags; + unsigned long sleep; /* sleep until this time */ unsigned long service_start; /* time we started last request */ unsigned long service_time; /* service time of last request */ @@ -487,32 +538,6 @@ struct ide_drive_s { u8 state; /* retry state */ u8 waiting_for_dma; /* dma currently in progress */ - unsigned keep_settings : 1; /* restore settings after drive reset */ - unsigned using_dma : 1; /* disk is using dma for read/write */ - unsigned unmask : 1; /* okay to unmask other irqs */ - unsigned noflush : 1; /* don't attempt flushes */ - unsigned dsc_overlap : 1; /* DSC overlap */ - unsigned nice1 : 1; /* give potential excess bandwidth */ - unsigned present : 1; /* drive is physically present */ - unsigned dead : 1; /* device ejected hint */ - unsigned id_read : 1; /* 1=id read from disk 0 = synthetic */ - unsigned noprobe : 1; /* from: hdx=noprobe */ - unsigned removable : 1; /* 1 if need to do check_media_change */ - unsigned attach : 1; /* needed for removable devices */ - unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */ - unsigned no_unmask : 1; /* disallow setting unmask bit */ - unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ - unsigned doorlocking : 1; /* for removable only: door lock/unlock works */ - unsigned nodma : 1; /* disallow DMA */ - unsigned blocked : 1; /* 1=powermanagment told us not to do anything, so sleep nicely */ - unsigned scsi : 1; /* 0=default, 1=ide-scsi emulation */ - unsigned sleeping : 1; /* 1=sleeping & sleep field valid */ - unsigned post_reset : 1; - unsigned udma33_warned : 1; - unsigned addressing : 1; /* 0=28-bit, 1=48-bit */ - unsigned wcache : 1; /* status of write cache */ - unsigned nowerr : 1; /* used for ignoring ATA_DF */ - u8 quirk_list; /* considered quirky, set for a specific host */ u8 init_speed; /* transfer rate set at boot */ u8 current_speed; /* current transfer rate set */ @@ -826,6 +851,22 @@ static int set_##name(ide_drive_t *drive, int arg) \ return 0; \ } +#define ide_devset_get_flag(name, flag) \ +static int get_##name(ide_drive_t *drive) \ +{ \ + return !!(drive->dev_flags & flag); \ +} + +#define ide_devset_set_flag(name, flag) \ +static int set_##name(ide_drive_t *drive, int arg) \ +{ \ + if (arg) \ + drive->dev_flags |= flag; \ + else \ + drive->dev_flags &= ~flag; \ + return 0; \ +} + #define __IDE_DEVSET(_name, _flags, _get, _set) \ const struct ide_devset ide_devset_##_name = \ __DEVSET(_flags, _get, _set) @@ -861,6 +902,11 @@ ide_devset_get(_name, _field); \ ide_devset_set(_name, _field); \ IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name) +#define ide_devset_rw_flag(_name, _field) \ +ide_devset_get_flag(_name, _field); \ +ide_devset_set_flag(_name, _field); \ +IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name) + struct ide_proc_devset { const char *name; const struct ide_devset *setting; @@ -1587,6 +1633,6 @@ static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive) { ide_drive_t *peer = &drive->hwif->drives[(drive->dn ^ 1) & 1]; - return peer->present ? peer : NULL; + return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL; } #endif /* _IDE_H */ -- cgit v1.2.3 From c39220483ebe6871fb129d4b2236cd95290c41fc Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:37 +0200 Subject: ide: DMA_PIO_RETRY -> IDE_DFLAG_DMA_PIO_RETRY Add IDE_DFLAG_DMA_PIO_RETRY and use it instead of ide_drive_t.state + DMA_PIO_RETRY. There should be no functional changes cause by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 7 ++++--- include/linux/ide.h | 9 ++------- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 5f0ed59bac6b..11b602bb5741 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -78,8 +78,9 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq, * decide whether to reenable DMA -- 3 is a random magic for now, * if we DMA timeout more than 3 times, just stay in PIO */ - if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { - drive->state = 0; + if ((drive->dev_flags & IDE_DFLAG_DMA_PIO_RETRY) && + drive->retry_pio <= 3) { + drive->dev_flags &= ~IDE_DFLAG_DMA_PIO_RETRY; ide_dma_on(drive); } @@ -1195,8 +1196,8 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) * a timeout -- we'll reenable after we finish this next request * (or rather the first chunk of it) in pio. */ + drive->dev_flags |= IDE_DFLAG_DMA_PIO_RETRY; drive->retry_pio++; - drive->state = DMA_PIO_RETRY; ide_dma_off_quietly(drive); /* diff --git a/include/linux/ide.h b/include/linux/ide.h index b538d2e6dcbb..230bd9dd851d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -48,12 +48,6 @@ typedef unsigned char byte; /* used everywhere */ #define ERROR_RESET 3 /* Reset controller every 4th retry */ #define ERROR_RECAL 1 /* Recalibrate every 2nd retry */ -/* - * state flags - */ - -#define DMA_PIO_RETRY 1 /* retrying in PIO */ - #define HWIF(drive) ((ide_hwif_t *)((drive)->hwif)) #define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup)) @@ -506,6 +500,8 @@ enum { IDE_DFLAG_WCACHE = (1 << 23), /* used for ignoring ATA_DF */ IDE_DFLAG_NOWERR = (1 << 24), + /* retrying in PIO */ + IDE_DFLAG_DMA_PIO_RETRY = (1 << 25), }; struct ide_drive_s { @@ -535,7 +531,6 @@ struct ide_drive_s { select_t select; /* basic drive/head select reg value */ u8 retry_pio; /* retrying dma capable host in pio */ - u8 state; /* retry state */ u8 waiting_for_dma; /* dma currently in progress */ u8 quirk_list; /* considered quirky, set for a specific host */ -- cgit v1.2.3 From 0ae4b3199ab1b6d511c6e0948e92049c272a346a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:37 +0200 Subject: ide: remove superfluous ->media field from ide_driver_t Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 1 - drivers/ide/ide-disk.c | 1 - drivers/ide/ide-floppy.c | 1 - drivers/ide/ide-tape.c | 1 - drivers/scsi/ide-scsi.c | 1 - include/linux/ide.h | 1 - 6 files changed, 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index ea7cd4e0b157..84bc2413312a 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1966,7 +1966,6 @@ static ide_driver_t ide_cdrom_driver = { .probe = ide_cd_probe, .remove = ide_cd_remove, .version = IDECD_VERSION, - .media = ide_cdrom, .do_request = ide_cd_do_request, .end_request = ide_end_request, .error = __ide_error, diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 7ee2c9d2e5c2..c35de54dfc22 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -938,7 +938,6 @@ static ide_driver_t idedisk_driver = { .resume = ide_disk_resume, .shutdown = ide_device_shutdown, .version = IDEDISK_VERSION, - .media = ide_disk, .do_request = ide_do_rw_disk, .end_request = ide_end_request, .error = __ide_error, diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index cdfadb01d07f..8c2b00941bd8 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -673,7 +673,6 @@ static ide_driver_t idefloppy_driver = { .probe = ide_floppy_probe, .remove = ide_floppy_remove, .version = IDEFLOPPY_VERSION, - .media = ide_floppy, .do_request = idefloppy_do_request, .end_request = idefloppy_end_request, .error = __ide_error, diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 103f9f161716..27665e3a41cb 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2318,7 +2318,6 @@ static ide_driver_t idetape_driver = { .probe = ide_tape_probe, .remove = ide_tape_remove, .version = IDETAPE_VERSION, - .media = ide_tape, .do_request = idetape_do_request, .end_request = idetape_end_request, .error = __ide_error, diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 67e9ed95f669..c2995972052c 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -437,7 +437,6 @@ static ide_driver_t idescsi_driver = { .probe = ide_scsi_probe, .remove = ide_scsi_remove, .version = IDESCSI_VERSION, - .media = ide_scsi, .do_request = idescsi_do_request, .end_request = idescsi_end_request, .error = idescsi_atapi_error, diff --git a/include/linux/ide.h b/include/linux/ide.h index 230bd9dd851d..7fd1ec135510 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1029,7 +1029,6 @@ enum { */ struct ide_driver_s { const char *version; - u8 media; ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t); int (*end_request)(ide_drive_t *, int, int); ide_startstop_t (*error)(ide_drive_t *, struct request *rq, u8, u8); -- cgit v1.2.3 From e4634d4ef04fe6d7b114b612e5b71a84187ce76a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:37 +0200 Subject: ide: remove superfluous ->dma field from ide_hwif_t Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 2 -- drivers/ide/pci/scc_pata.c | 2 -- include/linux/ide.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 2dacd802c72c..1185f5a4c154 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -525,7 +525,6 @@ void ide_dma_start(ide_drive_t *drive) outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD); } - hwif->dma = 1; wmb(); } @@ -564,7 +563,6 @@ int __ide_dma_end (ide_drive_t *drive) /* purge DMA mappings */ ide_destroy_dmatable(drive); /* verify good DMA status */ - hwif->dma = 0; wmb(); return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; } diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index e92a874b31df..9105a39398e2 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -353,7 +353,6 @@ static void scc_dma_start(ide_drive_t *drive) /* start DMA */ scc_ide_outb(dma_cmd | 1, hwif->dma_base); - hwif->dma = 1; wmb(); } @@ -374,7 +373,6 @@ static int __scc_dma_end(ide_drive_t *drive) /* purge DMA mappings */ ide_destroy_dmatable(drive); /* verify good DMA status */ - hwif->dma = 0; wmb(); return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 7fd1ec135510..fdec0108dba1 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -756,8 +756,6 @@ typedef struct hwif_s { void *hwif_data; /* extra hwif data */ - unsigned dma; - #ifdef CONFIG_BLK_DEV_IDEACPI struct ide_acpi_hwif_link *acpidata; #endif -- cgit v1.2.3 From d1d76714e2f0c520b6c2a84ab5b050d0b3244949 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:38 +0200 Subject: ide: fix HDIO_DRIVE_TASK[FILE] ioctls for CHS commands on LBA devices Add IDE_DFLAG_LBA device flag and use it instead of ->select.b.lba. Since ->tf_load uses ->select.all for ATA Device/Head register this fixes HDIO_DRIVE_TASK[FILE] ioctls for CHS commands on LBA devices. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 33 ++++++++++++++++++++------------- drivers/ide/ide-io.c | 4 ++-- include/linux/ide.h | 1 + 3 files changed, 23 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index c35de54dfc22..6eb9fea32a56 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -162,7 +162,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; - if (drive->select.b.lba) { + if (drive->dev_flags & IDE_DFLAG_LBA) { if (lba48) { pr_debug("%s: LBA=0x%012llx\n", drive->name, (unsigned long long)block); @@ -187,6 +187,8 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, tf->lbah = block >>= 8; tf->device = (block >> 8) & 0xf; } + + tf->device |= ATA_LBA; } else { unsigned int sect, head, cyl, track; @@ -384,28 +386,32 @@ static void idedisk_check_hpa(ide_drive_t *drive) static void init_idedisk_capacity(ide_drive_t *drive) { u16 *id = drive->id; - /* - * If this drive supports the Host Protected Area feature set, - * then we may need to change our opinion about the drive's capacity. - */ - int hpa = ata_id_hpa_enabled(id); + int lba; if (ata_id_lba48_enabled(id)) { /* drive speaks 48-bit LBA */ - drive->select.b.lba = 1; + lba = 1; drive->capacity64 = ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); - if (hpa) - idedisk_check_hpa(drive); } else if (ata_id_has_lba(id) && ata_id_is_lba_capacity_ok(id)) { /* drive speaks 28-bit LBA */ - drive->select.b.lba = 1; + lba = 1; drive->capacity64 = ata_id_u32(id, ATA_ID_LBA_CAPACITY); - if (hpa) - idedisk_check_hpa(drive); } else { /* drive speaks boring old 28-bit CHS */ + lba = 0; drive->capacity64 = drive->cyl * drive->head * drive->sect; } + + if (lba) { + drive->dev_flags |= IDE_DFLAG_LBA; + + /* + * If this device supports the Host Protected Area feature set, + * then we may need to change our opinion about its capacity. + */ + if (ata_id_hpa_enabled(id)) + idedisk_check_hpa(drive); + } } static sector_t idedisk_capacity(ide_drive_t *drive) @@ -1110,7 +1116,8 @@ static int ide_disk_probe(ide_drive_t *drive) drive->driver_data = idkp; idedisk_setup(drive); - if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { + if ((drive->dev_flags & IDE_DFLAG_LBA) == 0 && + (drive->head == 0 || drive->head > 16)) { printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); drive->dev_flags &= ~IDE_DFLAG_ATTACH; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 11b602bb5741..623f6c246cf5 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -383,7 +383,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 } else if (stat & ATA_ERR) { /* err has different meaning on cdrom and tape */ if (err == ATA_ABORTED) { - if (drive->select.b.lba && + if ((drive->dev_flags & IDE_DFLAG_LBA) && /* some newer drives don't support ATA_CMD_INIT_DEV_PARAMS */ hwif->tp_ops->read_status(hwif) == ATA_CMD_INIT_DEV_PARAMS) return ide_stopped; @@ -513,7 +513,7 @@ static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) tf->lbal = drive->sect; tf->lbam = drive->cyl; tf->lbah = drive->cyl >> 8; - tf->device = ((drive->head - 1) | drive->select.all) & ~ATA_LBA; + tf->device = (drive->head - 1) | drive->select.all; tf->command = ATA_CMD_INIT_DEV_PARAMS; } diff --git a/include/linux/ide.h b/include/linux/ide.h index fdec0108dba1..cf7ec3a9d173 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -502,6 +502,7 @@ enum { IDE_DFLAG_NOWERR = (1 << 24), /* retrying in PIO */ IDE_DFLAG_DMA_PIO_RETRY = (1 << 25), + IDE_DFLAG_LBA = (1 << 26), }; struct ide_drive_s { -- cgit v1.2.3 From 0d346ba0730d84f04022f9f984d3f606f69cef37 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:38 +0200 Subject: ide: sanitize ide*_pm_* enums * Move ide*_pm_* enums from ide-io.c to . * idedisk_pm_* -> ide_pm_* * ide_pm_state_* -> ide_pm_* * No need to set ide_pm_* enums to the fixed values. * Uppercase ide_pm_* enums. * Fix/update comments. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 62 +++++++++++++++++++--------------------------------- drivers/ide/ide.c | 4 ++-- include/linux/ide.h | 34 ++++++++++++++-------------- 3 files changed, 40 insertions(+), 60 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 623f6c246cf5..f8d8642903da 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -132,21 +132,6 @@ int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors) } EXPORT_SYMBOL(ide_end_request); -/* - * Power Management state machine. This one is rather trivial for now, - * we should probably add more, like switching back to PIO on suspend - * to help some BIOSes, re-do the door locking on resume, etc... - */ - -enum { - ide_pm_flush_cache = ide_pm_state_start_suspend, - idedisk_pm_standby, - - idedisk_pm_restore_pio = ide_pm_state_start_resume, - idedisk_pm_idle, - ide_pm_restore_dma, -}; - static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error) { struct request_pm_state *pm = rq->data; @@ -155,20 +140,20 @@ static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 s return; switch (pm->pm_step) { - case ide_pm_flush_cache: /* Suspend step 1 (flush cache) complete */ + case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */ if (pm->pm_state == PM_EVENT_FREEZE) - pm->pm_step = ide_pm_state_completed; + pm->pm_step = IDE_PM_COMPLETED; else - pm->pm_step = idedisk_pm_standby; + pm->pm_step = IDE_PM_STANDBY; break; - case idedisk_pm_standby: /* Suspend step 2 (standby) complete */ - pm->pm_step = ide_pm_state_completed; + case IDE_PM_STANDBY: /* Suspend step 2 (standby) */ + pm->pm_step = IDE_PM_COMPLETED; break; - case idedisk_pm_restore_pio: /* Resume step 1 complete */ - pm->pm_step = idedisk_pm_idle; + case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */ + pm->pm_step = IDE_PM_IDLE; break; - case idedisk_pm_idle: /* Resume step 2 (idle) complete */ - pm->pm_step = ide_pm_restore_dma; + case IDE_PM_IDLE: /* Resume step 2 (idle)*/ + pm->pm_step = IDE_PM_RESTORE_DMA; break; } } @@ -181,7 +166,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * memset(args, 0, sizeof(*args)); switch (pm->pm_step) { - case ide_pm_flush_cache: /* Suspend step 1 (flush cache) */ + case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */ if (drive->media != ide_disk) break; /* Not supported? Switch to next step now. */ @@ -195,27 +180,23 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * else args->tf.command = ATA_CMD_FLUSH; goto out_do_tf; - - case idedisk_pm_standby: /* Suspend step 2 (standby) */ + case IDE_PM_STANDBY: /* Suspend step 2 (standby) */ args->tf.command = ATA_CMD_STANDBYNOW1; goto out_do_tf; - - case idedisk_pm_restore_pio: /* Resume step 1 (restore PIO) */ + case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */ ide_set_max_pio(drive); /* - * skip idedisk_pm_idle for ATAPI devices + * skip IDE_PM_IDLE for ATAPI devices */ if (drive->media != ide_disk) - pm->pm_step = ide_pm_restore_dma; + pm->pm_step = IDE_PM_RESTORE_DMA; else ide_complete_power_step(drive, rq, 0, 0); return ide_stopped; - - case idedisk_pm_idle: /* Resume step 2 (idle) */ + case IDE_PM_IDLE: /* Resume step 2 (idle) */ args->tf.command = ATA_CMD_IDLEIMMEDIATE; goto out_do_tf; - - case ide_pm_restore_dma: /* Resume step 3 (restore DMA) */ + case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */ /* * Right now, all we do is call ide_set_dma(drive), * we could be smarter and check for current xfer_speed @@ -229,7 +210,8 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * ide_set_dma(drive); break; } - pm->pm_step = ide_pm_state_completed; + + pm->pm_step = IDE_PM_COMPLETED; return ide_stopped; out_do_tf: @@ -345,7 +327,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) drive->name, rq->pm->pm_step, stat, err); #endif ide_complete_power_step(drive, rq, stat, err); - if (pm->pm_step == ide_pm_state_completed) + if (pm->pm_step == IDE_PM_COMPLETED) ide_complete_pm_request(drive, rq); return; } @@ -778,11 +760,11 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) struct request_pm_state *pm = rq->data; if (blk_pm_suspend_request(rq) && - pm->pm_step == ide_pm_state_start_suspend) + pm->pm_step == IDE_PM_START_SUSPEND) /* Mark drive blocked when starting the suspend sequence. */ drive->dev_flags |= IDE_DFLAG_BLOCKED; else if (blk_pm_resume_request(rq) && - pm->pm_step == ide_pm_state_start_resume) { + pm->pm_step == IDE_PM_START_RESUME) { /* * The first thing we do on wakeup is to wait for BSY bit to * go away (with a looong timeout) as a drive on this hwif may @@ -862,7 +844,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) #endif startstop = ide_start_power_step(drive, rq); if (startstop == ide_stopped && - pm->pm_step == ide_pm_state_completed) + pm->pm_step == IDE_PM_COMPLETED) ide_complete_pm_request(drive, rq); return startstop; } else if (!rq->rq_disk && blk_special_request(rq)) diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 78776bbb537e..40b5a4614340 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -388,7 +388,7 @@ static int generic_ide_suspend(struct device *dev, pm_message_t mesg) rq->cmd_type = REQ_TYPE_PM_SUSPEND; rq->special = &args; rq->data = &rqpm; - rqpm.pm_step = ide_pm_state_start_suspend; + rqpm.pm_step = IDE_PM_START_SUSPEND; if (mesg.event == PM_EVENT_PRETHAW) mesg.event = PM_EVENT_FREEZE; rqpm.pm_state = mesg.event; @@ -427,7 +427,7 @@ static int generic_ide_resume(struct device *dev) rq->cmd_flags |= REQ_PREEMPT; rq->special = &args; rq->data = &rqpm; - rqpm.pm_step = ide_pm_state_start_resume; + rqpm.pm_step = IDE_PM_START_RESUME; rqpm.pm_state = PM_EVENT_ON; err = blk_execute_rq(drive->queue, NULL, rq, 1); diff --git a/include/linux/ide.h b/include/linux/ide.h index cf7ec3a9d173..02984f1f041a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -988,36 +988,34 @@ enum { } /* - * Power Management step value (rq->pm->pm_step). + * Power Management state machine (rq->pm->pm_step). * - * The step value starts at 0 (ide_pm_state_start_suspend) for a - * suspend operation or 1000 (ide_pm_state_start_resume) for a - * resume operation. - * - * For each step, the core calls the subdriver start_power_step() first. + * For each step, the core calls ide_start_power_step() first. * This can return: * - ide_stopped : In this case, the core calls us back again unless * step have been set to ide_power_state_completed. * - ide_started : In this case, the channel is left busy until an * async event (interrupt) occurs. - * Typically, start_power_step() will issue a taskfile request with + * Typically, ide_start_power_step() will issue a taskfile request with * do_rw_taskfile(). * - * Upon reception of the interrupt, the core will call complete_power_step() + * Upon reception of the interrupt, the core will call ide_complete_power_step() * with the error code if any. This routine should update the step value * and return. It should not start a new request. The core will call - * start_power_step for the new step value, unless step have been set to - * ide_power_state_completed. - * - * Subdrivers are expected to define their own additional power - * steps from 1..999 for suspend and from 1001..1999 for resume, - * other values are reserved for future use. + * ide_start_power_step() for the new step value, unless step have been + * set to IDE_PM_COMPLETED. */ - enum { - ide_pm_state_completed = -1, - ide_pm_state_start_suspend = 0, - ide_pm_state_start_resume = 1000, + IDE_PM_START_SUSPEND, + IDE_PM_FLUSH_CACHE = IDE_PM_START_SUSPEND, + IDE_PM_STANDBY, + + IDE_PM_START_RESUME, + IDE_PM_RESTORE_PIO = IDE_PM_START_RESUME, + IDE_PM_IDLE, + IDE_PM_RESTORE_DMA, + + IDE_PM_COMPLETED, }; /* -- cgit v1.2.3 From 7f612f272ad8abe82411f368bfacf793b466e1b3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:40 +0200 Subject: ide: remove [ata_]select_t * Use 'drive->dn & 1' in ide_init_disk(). * remove [ata_]select_t. While at it: * Use ATA_DEVICE_OBS define in ide_port_init_devices_data(). Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/h8300/ide-h8300.c | 2 +- drivers/ide/ide-io.c | 2 +- drivers/ide/ide-iops.c | 2 +- drivers/ide/ide-probe.c | 4 ++-- drivers/ide/ide.c | 2 +- drivers/ide/legacy/ide-4drives.c | 2 +- drivers/ide/pci/scc_pata.c | 2 +- include/linux/ide.h | 32 +------------------------------- 8 files changed, 9 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index bde7a585f198..e2cdd2e9cdec 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -80,7 +80,7 @@ static void h8300_tf_load(ide_drive_t *drive, ide_task_t *task) outb(tf->lbah, io_ports->lbah_addr); if (task->tf_flags & IDE_TFLAG_OUT_DEVICE) - outb((tf->device & HIHI) | drive->select.all, + outb((tf->device & HIHI) | drive->select, io_ports->device_addr); } diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index f8d8642903da..ecfb87c10097 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -495,7 +495,7 @@ static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) tf->lbal = drive->sect; tf->lbam = drive->cyl; tf->lbah = drive->cyl >> 8; - tf->device = (drive->head - 1) | drive->select.all; + tf->device = (drive->head - 1) | drive->select; tf->command = ATA_CMD_INIT_DEV_PARAMS; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index cec744cbbde0..925fd037cdde 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -181,7 +181,7 @@ void ide_tf_load(ide_drive_t *drive, ide_task_t *task) tf_outb(tf->lbah, io_ports->lbah_addr); if (task->tf_flags & IDE_TFLAG_OUT_DEVICE) - tf_outb((tf->device & HIHI) | drive->select.all, + tf_outb((tf->device & HIHI) | drive->select, io_ports->device_addr); } EXPORT_SYMBOL_GPL(ide_tf_load); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 58a2caf17903..242cfd09e16b 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -446,7 +446,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(drive); msleep(50); - if (ide_read_device(drive) != drive->select.all && present == 0) { + if (ide_read_device(drive) != drive->select && present == 0) { if (drive->dn & 1) { /* exit with drive0 selected */ SELECT_DRIVE(&hwif->drives[0]); @@ -1211,7 +1211,7 @@ EXPORT_SYMBOL_GPL(ide_unregister_region); void ide_init_disk(struct gendisk *disk, ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - unsigned int unit = (drive->select.all >> 4) & 1; + unsigned int unit = drive->dn & 1; disk->major = hwif->major; disk->first_minor = unit << PARTN_BITS; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 7624b937398a..9d3482d907c9 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -114,7 +114,7 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif) memset(drive, 0, sizeof(*drive)); drive->media = ide_disk; - drive->select.all = (unit<<4)|0xa0; + drive->select = (unit << 4) | ATA_DEVICE_OBS; drive->hwif = hwif; drive->ready_stat = ATA_DRDY; drive->bad_wstat = BAD_W_STAT; diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index c76d55de6996..9e85b1ec9607 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -14,7 +14,7 @@ MODULE_PARM_DESC(probe, "probe for generic IDE chipset with 4 drives/port"); static void ide_4drives_init_dev(ide_drive_t *drive) { if (drive->hwif->channel) - drive->select.all ^= 0x20; + drive->select ^= 0x20; } static const struct ide_port_ops ide_4drives_port_ops = { diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 0eced0ae2e86..c2e85fc21b5a 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -705,7 +705,7 @@ static void scc_tf_load(ide_drive_t *drive, ide_task_t *task) scc_ide_outb(tf->lbah, io_ports->lbah_addr); if (task->tf_flags & IDE_TFLAG_OUT_DEVICE) - scc_ide_outb((tf->device & HIHI) | drive->select.all, + scc_ide_outb((tf->device & HIHI) | drive->select, io_ports->device_addr); } diff --git a/include/linux/ide.h b/include/linux/ide.h index 02984f1f041a..3e418b996ef5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -278,36 +278,6 @@ typedef union { } b; } special_t; -/* - * ATA-IDE Select Register, aka Device-Head - * - * head : always zeros here - * unit : drive select number: 0/1 - * bit5 : always 1 - * lba : using LBA instead of CHS - * bit7 : always 1 - */ -typedef union { - unsigned all : 8; - struct { -#if defined(__LITTLE_ENDIAN_BITFIELD) - unsigned head : 4; - unsigned unit : 1; - unsigned bit5 : 1; - unsigned lba : 1; - unsigned bit7 : 1; -#elif defined(__BIG_ENDIAN_BITFIELD) - unsigned bit7 : 1; - unsigned lba : 1; - unsigned bit5 : 1; - unsigned unit : 1; - unsigned head : 4; -#else -#error "Please fix " -#endif - } b; -} select_t, ata_select_t; - /* * Status returned from various ide_ functions */ @@ -529,8 +499,8 @@ struct ide_drive_s { unsigned long timeout; /* max time to wait for irq */ special_t special; /* special action flags */ - select_t select; /* basic drive/head select reg value */ + u8 select; /* basic drive/head select reg value */ u8 retry_pio; /* retrying dma capable host in pio */ u8 waiting_for_dma; /* dma currently in progress */ -- cgit v1.2.3 From 6982daf71ca9a0b0c36043315e1968b3cb709b7c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:40 +0200 Subject: ide: convert 'pio_mode' device setting to use DS_SYNC flag * Convert 'pio_mode' device setting to use DS_SYNC flag. * Remove unused special_t.b.{set_tune,serviced} and ide_drive_t.tune_req. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 68 ++++------------------------------------------------ drivers/ide/ide.c | 52 ++++++++++++++++++++++++++++++++-------- include/linux/ide.h | 7 +----- 3 files changed, 48 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ecfb87c10097..ec709269c066 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -543,30 +543,6 @@ static ide_startstop_t ide_disk_special(ide_drive_t *drive) return ide_started; } -/* - * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away - */ -static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio) -{ - switch (req_pio) { - case 202: - case 201: - case 200: - case 102: - case 101: - case 100: - return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0; - case 9: - case 8: - return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0; - case 7: - case 6: - return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0; - default: - return 0; - } -} - /** * do_special - issue some special commands * @drive: drive the command is for @@ -584,46 +560,12 @@ static ide_startstop_t do_special (ide_drive_t *drive) #ifdef DEBUG printk("%s: do_special: 0x%02x\n", drive->name, s->all); #endif - if (s->b.set_tune) { - ide_hwif_t *hwif = drive->hwif; - const struct ide_port_ops *port_ops = hwif->port_ops; - u8 req_pio = drive->tune_req; - - s->b.set_tune = 0; - - if (set_pio_mode_abuse(drive->hwif, req_pio)) { - /* - * take ide_lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT - */ - if (req_pio == 8 || req_pio == 9) { - unsigned long flags; - - spin_lock_irqsave(&ide_lock, flags); - port_ops->set_pio_mode(drive, req_pio); - spin_unlock_irqrestore(&ide_lock, flags); - } else - port_ops->set_pio_mode(drive, req_pio); - } else { - int keep_dma = - !!(drive->dev_flags & IDE_DFLAG_USING_DMA); - - ide_set_pio(drive, req_pio); - - if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) { - if (keep_dma) - ide_dma_on(drive); - } - } - - return ide_stopped; - } else { - if (drive->media == ide_disk) - return ide_disk_special(drive); + if (drive->media == ide_disk) + return ide_disk_special(drive); - s->all = 0; - drive->mult_req = 0; - return ide_stopped; - } + s->all = 0; + drive->mult_req = 0; + return ide_stopped; } void ide_map_sg(ide_drive_t *drive, struct request *rq) diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 9d3482d907c9..73e1cc5839d3 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -314,9 +314,32 @@ out: #endif } +/* + * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away + */ +static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio) +{ + switch (req_pio) { + case 202: + case 201: + case 200: + case 102: + case 101: + case 100: + return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0; + case 9: + case 8: + return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0; + case 7: + case 6: + return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0; + default: + return 0; + } +} + static int set_pio_mode(ide_drive_t *drive, int arg) { - struct request *rq; ide_hwif_t *hwif = drive->hwif; const struct ide_port_ops *port_ops = hwif->port_ops; @@ -327,17 +350,26 @@ static int set_pio_mode(ide_drive_t *drive, int arg) (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)) return -ENOSYS; - if (drive->special.b.set_tune) - return -EBUSY; + if (set_pio_mode_abuse(drive->hwif, arg)) { + if (arg == 8 || arg == 9) { + unsigned long flags; - rq = blk_get_request(drive->queue, READ, __GFP_WAIT); - rq->cmd_type = REQ_TYPE_ATA_TASKFILE; + /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */ + spin_lock_irqsave(&ide_lock, flags); + port_ops->set_pio_mode(drive, arg); + spin_unlock_irqrestore(&ide_lock, flags); + } else + port_ops->set_pio_mode(drive, arg); + } else { + int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); - drive->tune_req = (u8) arg; - drive->special.b.set_tune = 1; + ide_set_pio(drive, arg); - blk_execute_rq(drive->queue, NULL, rq, 0); - blk_put_request(rq); + if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) { + if (keep_dma) + ide_dma_on(drive); + } + } return 0; } @@ -367,7 +399,7 @@ ide_gen_devset_rw(io_32bit, io_32bit); ide_gen_devset_rw(keepsettings, ksettings); ide_gen_devset_rw(unmaskirq, unmaskirq); ide_gen_devset_rw(using_dma, using_dma); -__IDE_DEVSET(pio_mode, 0, NULL, set_pio_mode); +__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode); static int generic_ide_suspend(struct device *dev, pm_message_t mesg) { diff --git a/include/linux/ide.h b/include/linux/ide.h index 3e418b996ef5..a5e1888b1dab 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -262,8 +262,6 @@ static inline int __ide_default_irq(unsigned long base) * set_geometry : respecify drive geometry * recalibrate : seek to cyl 0 * set_multmode : set multmode count - * set_tune : tune interface for drive - * serviced : service command * reserved : unused */ typedef union { @@ -272,9 +270,7 @@ typedef union { unsigned set_geometry : 1; unsigned recalibrate : 1; unsigned set_multmode : 1; - unsigned set_tune : 1; - unsigned serviced : 1; - unsigned reserved : 3; + unsigned reserved : 5; } b; } special_t; @@ -514,7 +510,6 @@ struct ide_drive_s { u8 ready_stat; /* min status value for drive ready */ u8 mult_count; /* current multiple sector setting */ u8 mult_req; /* requested multiple sector setting */ - u8 tune_req; /* requested drive tuning setting */ u8 io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ u8 bad_wstat; /* used for ignoring ATA_DF */ u8 head; /* "real" number of heads */ -- cgit v1.2.3 From d6ff9f64e68d23feab44efa07cc6aee01f3ef32b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:41 +0200 Subject: ide: merge all TASKFILE_NO_DATA data phase handlers into taskfile_no_intr() * Add 'struct task_s' to ide_hwif_t and init it to the current command in do_rw_taskfile(). * Merge all TASKFILE_NO_DATA data phase handlers into taskfile_no_intr(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-taskfile.c | 105 ++++++---------------- include/linux/ide.h | 211 +++++++++++++++++++++++---------------------- 2 files changed, 131 insertions(+), 185 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 8da8d26db7ed..a4c2d91179b3 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -53,9 +53,6 @@ int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf) } static ide_startstop_t task_no_data_intr(ide_drive_t *); -static ide_startstop_t set_geometry_intr(ide_drive_t *); -static ide_startstop_t recal_intr(ide_drive_t *); -static ide_startstop_t set_multmode_intr(ide_drive_t *); static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *); static ide_startstop_t task_in_intr(ide_drive_t *); @@ -79,6 +76,8 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if (task->tf_flags & IDE_TFLAG_FLAGGED) task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS; + memcpy(&hwif->task, task, sizeof(*task)); + if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); tp_ops->set_irq(hwif, 1); @@ -99,19 +98,6 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) case TASKFILE_NO_DATA: if (handler == NULL) handler = task_no_data_intr; - if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) { - switch (tf->command) { - case ATA_CMD_INIT_DEV_PARAMS: - handler = set_geometry_intr; - break; - case ATA_CMD_RESTORE: - handler = recal_intr; - break; - case ATA_CMD_SET_MULTI: - handler = set_multmode_intr; - break; - } - } ide_execute_command(drive, tf->command, handler, WAIT_WORSTCASE, NULL); return ide_started; @@ -127,33 +113,15 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) EXPORT_SYMBOL_GPL(do_rw_taskfile); /* - * set_multmode_intr() is invoked on completion of a ATA_CMD_SET_MULTI cmd. - */ -static ide_startstop_t set_multmode_intr(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u8 stat; - - local_irq_enable_in_hardirq(); - stat = hwif->tp_ops->read_status(hwif); - - if (OK_STAT(stat, ATA_DRDY, BAD_STAT)) - drive->mult_count = drive->mult_req; - else { - drive->mult_req = drive->mult_count = 0; - drive->special.b.recalibrate = 1; - (void) ide_dump_status(drive, "set_multmode", stat); - } - return ide_stopped; -} - -/* - * set_geometry_intr() is invoked on completion of a ATA_CMD_INIT_DEV_PARAMS cmd. + * Handler for commands without a data phase */ -static ide_startstop_t set_geometry_intr(ide_drive_t *drive) +static ide_startstop_t task_no_data_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - int retries = 5; + ide_task_t *task = &hwif->task; + struct ide_taskfile *tf = &task->tf; + int custom = (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0; + int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1; u8 stat; local_irq_enable_in_hardirq(); @@ -165,50 +133,27 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) udelay(10); }; - if (OK_STAT(stat, ATA_DRDY, BAD_STAT)) - return ide_stopped; - - if (stat & (ATA_ERR | ATA_DRQ)) - return ide_error(drive, "set_geometry_intr", stat); - - ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL); - return ide_started; -} - -/* - * recal_intr() is invoked on completion of a ATA_CMD_RESTORE (recalibrate) cmd. - */ -static ide_startstop_t recal_intr(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u8 stat; - - local_irq_enable_in_hardirq(); - stat = hwif->tp_ops->read_status(hwif); - - if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) - return ide_error(drive, "recal_intr", stat); - return ide_stopped; -} - -/* - * Handler for commands without a data phase - */ -static ide_startstop_t task_no_data_intr(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - ide_task_t *args = hwif->hwgroup->rq->special; - u8 stat; - - local_irq_enable_in_hardirq(); - stat = hwif->tp_ops->read_status(hwif); - - if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) + if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { + if (custom && tf->command == ATA_CMD_SET_MULTI) { + drive->mult_req = drive->mult_count = 0; + drive->special.b.recalibrate = 1; + (void)ide_dump_status(drive, __func__, stat); + return ide_stopped; + } else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) { + if ((stat & (ATA_ERR | ATA_DRQ)) == 0) { + ide_set_handler(drive, &task_no_data_intr, + WAIT_WORSTCASE, NULL); + return ide_started; + } + } return ide_error(drive, "task_no_data_intr", stat); /* calls ide_end_drive_cmd */ + } - if (args) + if (!custom) ide_end_drive_cmd(drive, stat, ide_read_error(drive)); + else if (tf->command == ATA_CMD_SET_MULTI) + drive->mult_count = drive->mult_req; return ide_stopped; } diff --git a/include/linux/ide.h b/include/linux/ide.h index a5e1888b1dab..14d489c2e5a9 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -282,6 +282,110 @@ typedef enum { ide_started, /* a drive operation was started, handler was set */ } ide_startstop_t; +enum { + IDE_TFLAG_LBA48 = (1 << 0), + IDE_TFLAG_FLAGGED = (1 << 2), + IDE_TFLAG_OUT_DATA = (1 << 3), + IDE_TFLAG_OUT_HOB_FEATURE = (1 << 4), + IDE_TFLAG_OUT_HOB_NSECT = (1 << 5), + IDE_TFLAG_OUT_HOB_LBAL = (1 << 6), + IDE_TFLAG_OUT_HOB_LBAM = (1 << 7), + IDE_TFLAG_OUT_HOB_LBAH = (1 << 8), + IDE_TFLAG_OUT_HOB = IDE_TFLAG_OUT_HOB_FEATURE | + IDE_TFLAG_OUT_HOB_NSECT | + IDE_TFLAG_OUT_HOB_LBAL | + IDE_TFLAG_OUT_HOB_LBAM | + IDE_TFLAG_OUT_HOB_LBAH, + IDE_TFLAG_OUT_FEATURE = (1 << 9), + IDE_TFLAG_OUT_NSECT = (1 << 10), + IDE_TFLAG_OUT_LBAL = (1 << 11), + IDE_TFLAG_OUT_LBAM = (1 << 12), + IDE_TFLAG_OUT_LBAH = (1 << 13), + IDE_TFLAG_OUT_TF = IDE_TFLAG_OUT_FEATURE | + IDE_TFLAG_OUT_NSECT | + IDE_TFLAG_OUT_LBAL | + IDE_TFLAG_OUT_LBAM | + IDE_TFLAG_OUT_LBAH, + IDE_TFLAG_OUT_DEVICE = (1 << 14), + IDE_TFLAG_WRITE = (1 << 15), + IDE_TFLAG_FLAGGED_SET_IN_FLAGS = (1 << 16), + IDE_TFLAG_IN_DATA = (1 << 17), + IDE_TFLAG_CUSTOM_HANDLER = (1 << 18), + IDE_TFLAG_DMA_PIO_FALLBACK = (1 << 19), + IDE_TFLAG_IN_HOB_FEATURE = (1 << 20), + IDE_TFLAG_IN_HOB_NSECT = (1 << 21), + IDE_TFLAG_IN_HOB_LBAL = (1 << 22), + IDE_TFLAG_IN_HOB_LBAM = (1 << 23), + IDE_TFLAG_IN_HOB_LBAH = (1 << 24), + IDE_TFLAG_IN_HOB_LBA = IDE_TFLAG_IN_HOB_LBAL | + IDE_TFLAG_IN_HOB_LBAM | + IDE_TFLAG_IN_HOB_LBAH, + IDE_TFLAG_IN_HOB = IDE_TFLAG_IN_HOB_FEATURE | + IDE_TFLAG_IN_HOB_NSECT | + IDE_TFLAG_IN_HOB_LBA, + IDE_TFLAG_IN_FEATURE = (1 << 1), + IDE_TFLAG_IN_NSECT = (1 << 25), + IDE_TFLAG_IN_LBAL = (1 << 26), + IDE_TFLAG_IN_LBAM = (1 << 27), + IDE_TFLAG_IN_LBAH = (1 << 28), + IDE_TFLAG_IN_LBA = IDE_TFLAG_IN_LBAL | + IDE_TFLAG_IN_LBAM | + IDE_TFLAG_IN_LBAH, + IDE_TFLAG_IN_TF = IDE_TFLAG_IN_NSECT | + IDE_TFLAG_IN_LBA, + IDE_TFLAG_IN_DEVICE = (1 << 29), + IDE_TFLAG_HOB = IDE_TFLAG_OUT_HOB | + IDE_TFLAG_IN_HOB, + IDE_TFLAG_TF = IDE_TFLAG_OUT_TF | + IDE_TFLAG_IN_TF, + IDE_TFLAG_DEVICE = IDE_TFLAG_OUT_DEVICE | + IDE_TFLAG_IN_DEVICE, + /* force 16-bit I/O operations */ + IDE_TFLAG_IO_16BIT = (1 << 30), + /* ide_task_t was allocated using kmalloc() */ + IDE_TFLAG_DYN = (1 << 31), +}; + +struct ide_taskfile { + u8 hob_data; /* 0: high data byte (for TASKFILE IOCTL) */ + + u8 hob_feature; /* 1-5: additional data to support LBA48 */ + u8 hob_nsect; + u8 hob_lbal; + u8 hob_lbam; + u8 hob_lbah; + + u8 data; /* 6: low data byte (for TASKFILE IOCTL) */ + + union { /*  7: */ + u8 error; /* read: error */ + u8 feature; /* write: feature */ + }; + + u8 nsect; /* 8: number of sectors */ + u8 lbal; /* 9: LBA low */ + u8 lbam; /* 10: LBA mid */ + u8 lbah; /* 11: LBA high */ + + u8 device; /* 12: device select */ + + union { /* 13: */ + u8 status; /*  read: status  */ + u8 command; /* write: command */ + }; +}; + +typedef struct ide_task_s { + union { + struct ide_taskfile tf; + u8 tf_array[14]; + }; + u32 tf_flags; + int data_phase; + struct request *rq; /* copy of request */ + void *special; /* valid_t generally */ +} ide_task_t; + /* ATAPI packet command flags */ enum { /* set when an error is considered normal - no retry (ide-tape) */ @@ -567,7 +671,6 @@ typedef struct ide_drive_s ide_drive_t; #define ide_drv_g(disk, cont_type) \ container_of((disk)->private_data, struct cont_type, driver) -struct ide_task_s; struct ide_port_info; struct ide_tp_ops { @@ -694,6 +797,8 @@ typedef struct hwif_s { /* data phase of the active command (currently only valid for PIO/DMA) */ int data_phase; + struct ide_task_s task; /* current command */ + unsigned int nsect; unsigned int nleft; struct scatterlist *cursg; @@ -1059,110 +1164,6 @@ extern void ide_do_drive_cmd(ide_drive_t *, struct request *); extern void ide_end_drive_cmd(ide_drive_t *, u8, u8); -enum { - IDE_TFLAG_LBA48 = (1 << 0), - IDE_TFLAG_FLAGGED = (1 << 2), - IDE_TFLAG_OUT_DATA = (1 << 3), - IDE_TFLAG_OUT_HOB_FEATURE = (1 << 4), - IDE_TFLAG_OUT_HOB_NSECT = (1 << 5), - IDE_TFLAG_OUT_HOB_LBAL = (1 << 6), - IDE_TFLAG_OUT_HOB_LBAM = (1 << 7), - IDE_TFLAG_OUT_HOB_LBAH = (1 << 8), - IDE_TFLAG_OUT_HOB = IDE_TFLAG_OUT_HOB_FEATURE | - IDE_TFLAG_OUT_HOB_NSECT | - IDE_TFLAG_OUT_HOB_LBAL | - IDE_TFLAG_OUT_HOB_LBAM | - IDE_TFLAG_OUT_HOB_LBAH, - IDE_TFLAG_OUT_FEATURE = (1 << 9), - IDE_TFLAG_OUT_NSECT = (1 << 10), - IDE_TFLAG_OUT_LBAL = (1 << 11), - IDE_TFLAG_OUT_LBAM = (1 << 12), - IDE_TFLAG_OUT_LBAH = (1 << 13), - IDE_TFLAG_OUT_TF = IDE_TFLAG_OUT_FEATURE | - IDE_TFLAG_OUT_NSECT | - IDE_TFLAG_OUT_LBAL | - IDE_TFLAG_OUT_LBAM | - IDE_TFLAG_OUT_LBAH, - IDE_TFLAG_OUT_DEVICE = (1 << 14), - IDE_TFLAG_WRITE = (1 << 15), - IDE_TFLAG_FLAGGED_SET_IN_FLAGS = (1 << 16), - IDE_TFLAG_IN_DATA = (1 << 17), - IDE_TFLAG_CUSTOM_HANDLER = (1 << 18), - IDE_TFLAG_DMA_PIO_FALLBACK = (1 << 19), - IDE_TFLAG_IN_HOB_FEATURE = (1 << 20), - IDE_TFLAG_IN_HOB_NSECT = (1 << 21), - IDE_TFLAG_IN_HOB_LBAL = (1 << 22), - IDE_TFLAG_IN_HOB_LBAM = (1 << 23), - IDE_TFLAG_IN_HOB_LBAH = (1 << 24), - IDE_TFLAG_IN_HOB_LBA = IDE_TFLAG_IN_HOB_LBAL | - IDE_TFLAG_IN_HOB_LBAM | - IDE_TFLAG_IN_HOB_LBAH, - IDE_TFLAG_IN_HOB = IDE_TFLAG_IN_HOB_FEATURE | - IDE_TFLAG_IN_HOB_NSECT | - IDE_TFLAG_IN_HOB_LBA, - IDE_TFLAG_IN_FEATURE = (1 << 1), - IDE_TFLAG_IN_NSECT = (1 << 25), - IDE_TFLAG_IN_LBAL = (1 << 26), - IDE_TFLAG_IN_LBAM = (1 << 27), - IDE_TFLAG_IN_LBAH = (1 << 28), - IDE_TFLAG_IN_LBA = IDE_TFLAG_IN_LBAL | - IDE_TFLAG_IN_LBAM | - IDE_TFLAG_IN_LBAH, - IDE_TFLAG_IN_TF = IDE_TFLAG_IN_NSECT | - IDE_TFLAG_IN_LBA, - IDE_TFLAG_IN_DEVICE = (1 << 29), - IDE_TFLAG_HOB = IDE_TFLAG_OUT_HOB | - IDE_TFLAG_IN_HOB, - IDE_TFLAG_TF = IDE_TFLAG_OUT_TF | - IDE_TFLAG_IN_TF, - IDE_TFLAG_DEVICE = IDE_TFLAG_OUT_DEVICE | - IDE_TFLAG_IN_DEVICE, - /* force 16-bit I/O operations */ - IDE_TFLAG_IO_16BIT = (1 << 30), - /* ide_task_t was allocated using kmalloc() */ - IDE_TFLAG_DYN = (1 << 31), -}; - -struct ide_taskfile { - u8 hob_data; /* 0: high data byte (for TASKFILE IOCTL) */ - - u8 hob_feature; /* 1-5: additional data to support LBA48 */ - u8 hob_nsect; - u8 hob_lbal; - u8 hob_lbam; - u8 hob_lbah; - - u8 data; /* 6: low data byte (for TASKFILE IOCTL) */ - - union { /*  7: */ - u8 error; /* read: error */ - u8 feature; /* write: feature */ - }; - - u8 nsect; /* 8: number of sectors */ - u8 lbal; /* 9: LBA low */ - u8 lbam; /* 10: LBA mid */ - u8 lbah; /* 11: LBA high */ - - u8 device; /* 12: device select */ - - union { /* 13: */ - u8 status; /*  read: status  */ - u8 command; /* write: command */ - }; -}; - -typedef struct ide_task_s { - union { - struct ide_taskfile tf; - u8 tf_array[14]; - }; - u32 tf_flags; - int data_phase; - struct request *rq; /* copy of request */ - void *special; /* valid_t generally */ -} ide_task_t; - void ide_tf_dump(const char *, struct ide_taskfile *); void ide_exec_command(ide_hwif_t *, u8); -- cgit v1.2.3 From bfa7d8e55f0c5ae22ef57eb22942c74fdde7b9bd Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:42 +0200 Subject: ide: ->ide_dma_clear_irq() -> ->clear_irq() * Rename ->ide_dma_clear_irq method to ->clear_irq and move it from ide_hwif_t to struct ide_port_ops. * Move ->waiting_for_dma check inside ->clear_irq method. * Move ->dma_base check inside ->clear_irq method. piix.c: * Add ich_port_ops and remove init_hwif_ich() wrapper. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 15 ++++----------- drivers/ide/pci/piix.c | 39 +++++++++++++++++++++++---------------- include/linux/ide.h | 4 ++-- 3 files changed, 29 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ec709269c066..d0579f1abddd 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -1418,23 +1418,16 @@ irqreturn_t ide_intr (int irq, void *dev_id) del_timer(&hwgroup->timer); spin_unlock(&ide_lock); - /* Some controllers might set DMA INTR no matter DMA or PIO; - * bmdma status might need to be cleared even for - * PIO interrupts to prevent spurious/lost irq. - */ - if (hwif->ide_dma_clear_irq && !(drive->waiting_for_dma)) - /* ide_dma_end() needs bmdma status for error checking. - * So, skip clearing bmdma status here and leave it - * to ide_dma_end() if this is dma interrupt. - */ - hwif->ide_dma_clear_irq(drive); + if (hwif->port_ops && hwif->port_ops->clear_irq) + hwif->port_ops->clear_irq(drive); if (drive->dev_flags & IDE_DFLAG_UNMASK) local_irq_enable_in_hardirq(); + /* service this interrupt, may set handler for next interrupt */ startstop = handler(drive); - spin_lock_irq(&ide_lock); + spin_lock_irq(&ide_lock); /* * Note that handler() may have set things up for another * interrupt to occur soon, but it cannot happen until diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index a909684ee61b..56feb939f82a 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -215,17 +215,26 @@ static unsigned int init_chipset_ich(struct pci_dev *dev) } /** - * piix_dma_clear_irq - clear BMDMA status - * @drive: IDE drive to clear + * ich_clear_irq - clear BMDMA status + * @drive: IDE drive * - * Called from ide_intr() for PIO interrupts - * to clear BMDMA status as needed by ICHx + * ICHx contollers set DMA INTR no matter DMA or PIO. + * BMDMA status might need to be cleared even for + * PIO interrupts to prevent spurious/lost IRQ. */ -static void piix_dma_clear_irq(ide_drive_t *drive) +static void ich_clear_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); u8 dma_stat; + /* + * ide_dma_end() needs BMDMA status for error checking. + * So, skip clearing BMDMA status here and leave it + * to ide_dma_end() if this is DMA interrupt. + */ + if (drive->waiting_for_dma || hwif->dma_base == 0) + return; + /* clear the INTR & ERROR bits */ dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Should we force the bit as well ? */ @@ -293,21 +302,19 @@ static void __devinit init_hwif_piix(ide_hwif_t *hwif) hwif->ultra_mask = hwif->mwdma_mask = hwif->swdma_mask = 0; } -static void __devinit init_hwif_ich(ide_hwif_t *hwif) -{ - init_hwif_piix(hwif); - - /* ICHx need to clear the BMDMA status for all interrupts */ - if (hwif->dma_base) - hwif->ide_dma_clear_irq = &piix_dma_clear_irq; -} - static const struct ide_port_ops piix_port_ops = { .set_pio_mode = piix_set_pio_mode, .set_dma_mode = piix_set_dma_mode, .cable_detect = piix_cable_detect, }; +static const struct ide_port_ops ich_port_ops = { + .set_pio_mode = piix_set_pio_mode, + .set_dma_mode = piix_set_dma_mode, + .clear_irq = ich_clear_irq, + .cable_detect = piix_cable_detect, +}; + #ifndef CONFIG_IA64 #define IDE_HFLAGS_PIIX IDE_HFLAG_LEGACY_IRQS #else @@ -331,9 +338,9 @@ static const struct ide_port_ops piix_port_ops = { { \ .name = DRV_NAME, \ .init_chipset = init_chipset_ich, \ - .init_hwif = init_hwif_ich, \ + .init_hwif = init_hwif_piix, \ .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \ - .port_ops = &piix_port_ops, \ + .port_ops = &ich_port_ops, \ .host_flags = IDE_HFLAGS_PIIX, \ .pio_mask = ATA_PIO4, \ .swdma_mask = ATA_SWDMA2_ONLY, \ diff --git a/include/linux/ide.h b/include/linux/ide.h index 14d489c2e5a9..37a4344f3842 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -704,6 +704,7 @@ extern const struct ide_tp_ops default_tp_ops; * @resetproc: routine to reset controller after a disk reset * @maskproc: special host masking for drive selection * @quirkproc: check host's drive quirk list + * @clear_irq: clear IRQ * * @mdma_filter: filter MDMA modes * @udma_filter: filter UDMA modes @@ -720,6 +721,7 @@ struct ide_port_ops { void (*resetproc)(ide_drive_t *); void (*maskproc)(ide_drive_t *, int); void (*quirkproc)(ide_drive_t *); + void (*clear_irq)(ide_drive_t *); u8 (*mdma_filter)(ide_drive_t *); u8 (*udma_filter)(ide_drive_t *); @@ -782,8 +784,6 @@ typedef struct hwif_s { const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; - void (*ide_dma_clear_irq)(ide_drive_t *drive); - /* dma physical region descriptor table (cpu view) */ unsigned int *dmatable_cpu; /* dma physical region descriptor table (dma view) */ -- cgit v1.2.3 From a36223b0dc14606b5c80aacbbe6288133693a841 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:43 +0200 Subject: ide: remove ide_host_alloc_all() * Remove no longer used ide_host_alloc_all(). * Add MAX_HOST_PORTS define and use it instead of MAX_HWIFS as the maximum number of host ports possible. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 31 +++++++++---------------------- include/linux/ide.h | 5 +++-- 2 files changed, 12 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 242cfd09e16b..3269cbf0e56d 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1544,8 +1544,7 @@ static void ide_free_port_slot(int idx) mutex_unlock(&ide_cfg_mtx); } -struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, - hw_regs_t **hws) +struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) { struct ide_host *host; int i; @@ -1554,7 +1553,7 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, if (host == NULL) return NULL; - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { ide_hwif_t *hwif; int idx; @@ -1596,18 +1595,6 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, return host; } -EXPORT_SYMBOL_GPL(ide_host_alloc_all); - -struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) -{ - hw_regs_t *hws_all[MAX_HWIFS]; - int i; - - for (i = 0; i < MAX_HWIFS; i++) - hws_all[i] = (i < 4) ? hws[i] : NULL; - - return ide_host_alloc_all(d, hws_all); -} EXPORT_SYMBOL_GPL(ide_host_alloc); int ide_host_register(struct ide_host *host, const struct ide_port_info *d, @@ -1616,7 +1603,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_hwif_t *hwif, *mate = NULL; int i, j = 0; - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { hwif = host->ports[i]; if (hwif == NULL) { @@ -1644,7 +1631,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_port_init_devices(hwif); } - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { hwif = host->ports[i]; if (hwif == NULL) @@ -1661,7 +1648,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_port_tune_devices(hwif); } - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { hwif = host->ports[i]; if (hwif == NULL) @@ -1685,7 +1672,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_acpi_port_init_devices(hwif); } - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { hwif = host->ports[i]; if (hwif == NULL) @@ -1698,7 +1685,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, hwif_register_devices(hwif); } - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { hwif = host->ports[i]; if (hwif == NULL) @@ -1743,7 +1730,7 @@ void ide_host_free(struct ide_host *host) ide_hwif_t *hwif; int i; - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { hwif = host->ports[i]; if (hwif == NULL) @@ -1761,7 +1748,7 @@ void ide_host_remove(struct ide_host *host) { int i; - for (i = 0; i < MAX_HWIFS; i++) { + for (i = 0; i < MAX_HOST_PORTS; i++) { if (host->ports[i]) ide_unregister(host->ports[i]); } diff --git a/include/linux/ide.h b/include/linux/ide.h index 37a4344f3842..86fa030e8022 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -832,8 +832,10 @@ typedef struct hwif_s { #endif } ____cacheline_internodealigned_in_smp ide_hwif_t; +#define MAX_HOST_PORTS 4 + struct ide_host { - ide_hwif_t *ports[MAX_HWIFS]; + ide_hwif_t *ports[MAX_HOST_PORTS]; unsigned int n_ports; struct device *dev[2]; unsigned int (*init_chipset)(struct pci_dev *); @@ -1479,7 +1481,6 @@ void ide_undecoded_slave(ide_drive_t *); void ide_port_apply_params(ide_hwif_t *); -struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); void ide_host_free(struct ide_host *); int ide_host_register(struct ide_host *, const struct ide_port_info *, -- cgit v1.2.3 From f87904898e91923a91b925078ac933f05076c7fd Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:45 +0200 Subject: ide-disk: move all ioctl handling to ide-disk_ioctl.c While at it: - idedisk_ioctl() -> ide_disk_ioctl() Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Makefile | 3 ++- drivers/ide/ide-disk.c | 48 ++++++++------------------------------------ drivers/ide/ide-disk.h | 25 +++++++++++++++++++++++ drivers/ide/ide-disk_ioctl.c | 29 ++++++++++++++++++++++++++ drivers/ide/ide.c | 11 ++++------ include/linux/ide.h | 7 +++++-- 6 files changed, 73 insertions(+), 50 deletions(-) create mode 100644 drivers/ide/ide-disk.h create mode 100644 drivers/ide/ide-disk_ioctl.c (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 06e7867052d3..57b252b8e792 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_IDE_H8300) += h8300/ obj-$(CONFIG_IDE_GENERIC) += ide-generic.o obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o +ide-disk_mod-y += ide-disk.o ide-disk_ioctl.o ide-cd_mod-y += ide-cd.o ide-cd_ioctl.o ide-cd_verbose.o ide-floppy_mod-y += ide-floppy.o ide-floppy_ioctl.o @@ -43,7 +44,7 @@ ifeq ($(CONFIG_IDE_PROC_FS), y) ide-floppy_mod-y += ide-floppy_proc.o endif -obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o +obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk_mod.o obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd_mod.o obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy_mod.o obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 6eb9fea32a56..995d448109eb 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -45,21 +45,12 @@ #define IDE_DISK_MINORS 0 #endif -struct ide_disk_obj { - ide_drive_t *drive; - ide_driver_t *driver; - struct gendisk *disk; - struct kref kref; - unsigned int openers; /* protected by BKL for now */ -}; +#include "ide-disk.h" static DEFINE_MUTEX(idedisk_ref_mutex); #define to_ide_disk(obj) container_of(obj, struct ide_disk_obj, kref) -#define ide_disk_g(disk) \ - container_of((disk)->private_data, struct ide_disk_obj, driver) - static void ide_disk_release(struct kref *); static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) @@ -722,12 +713,12 @@ static int set_addressing(ide_drive_t *drive, int arg) return 0; } -ide_devset_rw(acoustic, acoustic); -ide_devset_rw(address, addressing); -ide_devset_rw(multcount, multcount); -ide_devset_rw(wcache, wcache); +ide_ext_devset_rw(acoustic, acoustic); +ide_ext_devset_rw(address, addressing); +ide_ext_devset_rw(multcount, multcount); +ide_ext_devset_rw(wcache, wcache); -ide_devset_rw_sync(nowerr, nowerr); +ide_ext_devset_rw_sync(nowerr, nowerr); #ifdef CONFIG_IDE_PROC_FS ide_devset_rw_field(bios_cyl, bios_cyl); @@ -1025,30 +1016,6 @@ static int idedisk_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } -static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = { -{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, &ide_devset_address }, -{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, &ide_devset_multcount }, -{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, &ide_devset_nowerr }, -{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, &ide_devset_wcache }, -{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, &ide_devset_acoustic }, -{ 0 } -}; - -static int idedisk_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct block_device *bdev = inode->i_bdev; - struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk); - ide_drive_t *drive = idkp->drive; - int err; - - err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings); - if (err != -EOPNOTSUPP) - return err; - - return generic_ide_ioctl(drive, file, bdev, cmd, arg); -} - static int idedisk_media_changed(struct gendisk *disk) { struct ide_disk_obj *idkp = ide_disk_g(disk); @@ -1075,7 +1042,7 @@ static struct block_device_operations idedisk_ops = { .owner = THIS_MODULE, .open = idedisk_open, .release = idedisk_release, - .ioctl = idedisk_ioctl, + .ioctl = ide_disk_ioctl, .getgeo = idedisk_getgeo, .media_changed = idedisk_media_changed, .revalidate_disk = idedisk_revalidate_disk @@ -1151,6 +1118,7 @@ static int __init idedisk_init(void) } MODULE_ALIAS("ide:*m-disk*"); +MODULE_ALIAS("ide-disk"); module_init(idedisk_init); module_exit(idedisk_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-disk.h b/drivers/ide/ide-disk.h new file mode 100644 index 000000000000..7f007854adac --- /dev/null +++ b/drivers/ide/ide-disk.h @@ -0,0 +1,25 @@ +#ifndef __IDE_DISK_H +#define __IDE_DISK_H + +struct ide_disk_obj { + ide_drive_t *drive; + ide_driver_t *driver; + struct gendisk *disk; + struct kref kref; + unsigned int openers; /* protected by BKL for now */ +}; + +#define ide_disk_g(disk) \ + container_of((disk)->private_data, struct ide_disk_obj, driver) + +/* ide-disk.c */ +ide_decl_devset(address); +ide_decl_devset(multcount); +ide_decl_devset(nowerr); +ide_decl_devset(wcache); +ide_decl_devset(acoustic); + +/* ide-disk_ioctl.c */ +int ide_disk_ioctl(struct inode *, struct file *, unsigned int, unsigned long); + +#endif /* __IDE_DISK_H */ diff --git a/drivers/ide/ide-disk_ioctl.c b/drivers/ide/ide-disk_ioctl.c new file mode 100644 index 000000000000..a6cf1a03a806 --- /dev/null +++ b/drivers/ide/ide-disk_ioctl.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "ide-disk.h" + +static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = { +{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, &ide_devset_address }, +{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, &ide_devset_multcount }, +{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, &ide_devset_nowerr }, +{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, &ide_devset_wcache }, +{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, &ide_devset_acoustic }, +{ 0 } +}; + +int ide_disk_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk); + ide_drive_t *drive = idkp->drive; + int err; + + err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings); + if (err != -EOPNOTSUPP) + return err; + + return generic_ide_ioctl(drive, file, bdev, cmd, arg); +} diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 73e1cc5839d3..a498245dc213 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -392,13 +392,10 @@ static int set_unmaskirq(ide_drive_t *drive, int arg) return 0; } -#define ide_gen_devset_rw(_name, _func) \ -__IDE_DEVSET(_name, DS_SYNC, get_##_func, set_##_func) - -ide_gen_devset_rw(io_32bit, io_32bit); -ide_gen_devset_rw(keepsettings, ksettings); -ide_gen_devset_rw(unmaskirq, unmaskirq); -ide_gen_devset_rw(using_dma, using_dma); +ide_ext_devset_rw_sync(io_32bit, io_32bit); +ide_ext_devset_rw_sync(keepsettings, ksettings); +ide_ext_devset_rw_sync(unmaskirq, unmaskirq); +ide_ext_devset_rw_sync(using_dma, using_dma); __IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode); static int generic_ide_suspend(struct device *dev, pm_message_t mesg) diff --git a/include/linux/ide.h b/include/linux/ide.h index 86fa030e8022..7ce245e6c6a5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -946,8 +946,11 @@ IDE_DEVSET(_name, 0, get_##_func, set_##_func) #define ide_devset_w(_name, _func) \ IDE_DEVSET(_name, 0, NULL, set_##_func) -#define ide_devset_rw_sync(_name, _func) \ -IDE_DEVSET(_name, DS_SYNC, get_##_func, set_##_func) +#define ide_ext_devset_rw(_name, _func) \ +__IDE_DEVSET(_name, 0, get_##_func, set_##_func) + +#define ide_ext_devset_rw_sync(_name, _func) \ +__IDE_DEVSET(_name, DS_SYNC, get_##_func, set_##_func) #define ide_decl_devset(_name) \ extern const struct ide_devset ide_devset_##_name -- cgit v1.2.3 From 653bcf5292a9ac4ffc07315198f0ef995e0646a8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:46 +0200 Subject: ide: __ide_dma_end() -> ide_dma_end() While at it: - use EXPORT_SYMBOL_GPL() to match the rest of SFF DMA functions Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 7 +++---- drivers/ide/pci/alim15x3.c | 2 +- drivers/ide/pci/cmd64x.c | 4 ++-- drivers/ide/pci/hpt366.c | 6 +++--- drivers/ide/pci/it821x.c | 2 +- drivers/ide/pci/pdc202xx_old.c | 4 ++-- drivers/ide/pci/siimage.c | 2 +- drivers/ide/pci/sl82c105.c | 2 +- drivers/ide/pci/tc86c001.c | 2 +- include/linux/ide.h | 2 +- 10 files changed, 16 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index d5934fc8f85f..f142d8f1349e 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -531,7 +531,7 @@ void ide_dma_start(ide_drive_t *drive) EXPORT_SYMBOL_GPL(ide_dma_start); /* returns 1 on error, 0 otherwise */ -int __ide_dma_end (ide_drive_t *drive) +int ide_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; @@ -566,8 +566,7 @@ int __ide_dma_end (ide_drive_t *drive) wmb(); return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; } - -EXPORT_SYMBOL(__ide_dma_end); +EXPORT_SYMBOL_GPL(ide_dma_end); /* returns 1 if dma irq issued, 0 otherwise */ int ide_dma_test_irq(ide_drive_t *drive) @@ -880,7 +879,7 @@ const struct ide_dma_ops sff_dma_ops = { .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, + .dma_end = ide_dma_end, .dma_test_irq = ide_dma_test_irq, .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 9d017fc1895e..daf9dce39e52 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -507,7 +507,7 @@ static const struct ide_dma_ops ali_dma_ops = { .dma_setup = ali15x3_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, + .dma_end = ide_dma_end, .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index bb89c505074c..935385c77e06 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -228,7 +228,7 @@ static int cmd648_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); unsigned long base = hwif->dma_base - (hwif->channel * 8); - int err = __ide_dma_end(drive); + int err = ide_dma_end(drive); u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; u8 mrdmode = inb(base + 1); @@ -248,7 +248,7 @@ static int cmd64x_dma_end(ide_drive_t *drive) u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; u8 irq_stat = 0; - int err = __ide_dma_end(drive); + int err = ide_dma_end(drive); (void) pci_read_config_byte(dev, irq_reg, &irq_stat); /* clear the interrupt bit */ diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 91f51e7b376f..9cf171cb9376 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -835,7 +835,7 @@ static int hpt370_dma_end(ide_drive_t *drive) if (dma_stat & 0x01) hpt370_irq_timeout(drive); } - return __ide_dma_end(drive); + return ide_dma_end(drive); } static void hpt370_dma_timeout(ide_drive_t *drive) @@ -877,7 +877,7 @@ static int hpt374_dma_end(ide_drive_t *drive) pci_read_config_byte(dev, mcr_addr, &mcr); if (bwsr & mask) pci_write_config_byte(dev, mcr_addr, mcr | 0x30); - return __ide_dma_end(drive); + return ide_dma_end(drive); } /** @@ -1453,7 +1453,7 @@ static const struct ide_dma_ops hpt36x_dma_ops = { .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, + .dma_end = ide_dma_end, .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = hpt366_dma_lost_irq, .dma_timeout = ide_dma_timeout, diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index ae7e7420f198..995e18bb3139 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -385,7 +385,7 @@ static int it821x_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct it821x_dev *itdev = ide_get_hwifdata(hwif); - int ret = __ide_dma_end(drive); + int ret = ide_dma_end(drive); u8 unit = drive->dn & 1; if(itdev->mwdma[unit] != MWDMA_OFF) diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 649b807c6aa6..799557c25eef 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -200,7 +200,7 @@ static int pdc202xx_dma_end(ide_drive_t *drive) } if (drive->current_speed > XFER_UDMA_2) pdc_old_disable_66MHz_clock(drive->hwif); - return __ide_dma_end(drive); + return ide_dma_end(drive); } static int pdc202xx_dma_test_irq(ide_drive_t *drive) @@ -333,7 +333,7 @@ static const struct ide_dma_ops pdc20246_dma_ops = { .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, + .dma_end = ide_dma_end, .dma_test_irq = pdc202xx_dma_test_irq, .dma_lost_irq = pdc202xx_dma_lost_irq, .dma_timeout = pdc202xx_dma_timeout, diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 0652e31119ef..eb4faf92c571 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -713,7 +713,7 @@ static const struct ide_dma_ops sil_dma_ops = { .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, + .dma_end = ide_dma_end, .dma_test_irq = siimage_dma_test_irq, .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 4399e76aa081..84dc33602ff8 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -207,7 +207,7 @@ static int sl82c105_dma_end(ide_drive_t *drive) DBG(("%s(drive:%s)\n", __func__, drive->name)); - ret = __ide_dma_end(drive); + ret = ide_dma_end(drive); pci_write_config_word(dev, reg, drive->drive_data); diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index a683377d75f1..93e2cce4b296 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -186,7 +186,7 @@ static const struct ide_dma_ops tc86c001_dma_ops = { .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, .dma_start = tc86c001_dma_start, - .dma_end = __ide_dma_end, + .dma_end = ide_dma_end, .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, diff --git a/include/linux/ide.h b/include/linux/ide.h index 7ce245e6c6a5..22f7451f7b49 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1435,7 +1435,7 @@ void ide_dma_host_set(ide_drive_t *, int); extern int ide_dma_setup(ide_drive_t *); void ide_dma_exec_cmd(ide_drive_t *, u8); extern void ide_dma_start(ide_drive_t *); -extern int __ide_dma_end(ide_drive_t *); +int ide_dma_end(ide_drive_t *); int ide_dma_test_irq(ide_drive_t *); extern void ide_dma_lost_irq(ide_drive_t *); extern void ide_dma_timeout(ide_drive_t *); -- cgit v1.2.3 From de23ec9ca82357e6d337a2263befb1a65cf19c83 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:46 +0200 Subject: ide: make ide_dma_lost_irq() available also for CONFIG_BLK_DEV_IDEDMA_SFF=n Make ide_dma_lost_irq() available also for CONFIG_BLK_DEV_IDEDMA_SFF=n and convert {ics,au1xxx-}ide.c to use it. While at it: - use EXPORT_SYMBOL_GPL() to match the rest of SFF DMA functions Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 7 +------ drivers/ide/ide-dma.c | 9 ++++----- drivers/ide/mips/au1xxx-ide.c | 7 +------ include/linux/ide.h | 3 ++- 4 files changed, 8 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 70f5b164828b..40ebe04f3995 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -386,11 +386,6 @@ static void icside_dma_timeout(ide_drive_t *drive) icside_dma_end(drive); } -static void icside_dma_lost_irq(ide_drive_t *drive) -{ - printk(KERN_ERR "%s: IRQ lost\n", drive->name); -} - static int icside_dma_init(ide_hwif_t *hwif, const struct ide_port_info *d) { hwif->dmatable_cpu = NULL; @@ -407,7 +402,7 @@ static const struct ide_dma_ops icside_v6_dma_ops = { .dma_end = icside_dma_end, .dma_test_irq = icside_dma_test_irq, .dma_timeout = icside_dma_timeout, - .dma_lost_irq = icside_dma_lost_irq, + .dma_lost_irq = ide_dma_lost_irq, }; #else #define icside_v6_dma_ops NULL diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index f142d8f1349e..08cd878de50b 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -823,14 +823,13 @@ void ide_check_dma_crc(ide_drive_t *drive) ide_dma_on(drive); } -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF -void ide_dma_lost_irq (ide_drive_t *drive) +void ide_dma_lost_irq(ide_drive_t *drive) { - printk("%s: DMA interrupt recovery\n", drive->name); + printk(KERN_ERR "%s: DMA interrupt recovery\n", drive->name); } +EXPORT_SYMBOL_GPL(ide_dma_lost_irq); -EXPORT_SYMBOL(ide_dma_lost_irq); - +#ifdef CONFIG_BLK_DEV_IDEDMA_SFF void ide_dma_timeout (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 1c95a0ed7504..92c90db4bb30 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -340,11 +340,6 @@ static void auide_dma_host_set(ide_drive_t *drive, int on) { } -static void auide_dma_lost_irq(ide_drive_t *drive) -{ - printk(KERN_ERR "%s: IRQ lost\n", drive->name); -} - static void auide_ddma_tx_callback(int irq, void *param) { _auide_hwif *ahwif = (_auide_hwif*)param; @@ -390,7 +385,7 @@ static const struct ide_dma_ops au1xxx_dma_ops = { .dma_start = auide_dma_start, .dma_end = auide_dma_end, .dma_test_irq = auide_dma_test_irq, - .dma_lost_irq = auide_dma_lost_irq, + .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = auide_dma_timeout, }; diff --git a/include/linux/ide.h b/include/linux/ide.h index 22f7451f7b49..ae4a25d2b066 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1437,11 +1437,12 @@ void ide_dma_exec_cmd(ide_drive_t *, u8); extern void ide_dma_start(ide_drive_t *); int ide_dma_end(ide_drive_t *); int ide_dma_test_irq(ide_drive_t *); -extern void ide_dma_lost_irq(ide_drive_t *); extern void ide_dma_timeout(ide_drive_t *); extern const struct ide_dma_ops sff_dma_ops; #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ +void ide_dma_lost_irq(ide_drive_t *); + #else static inline int ide_id_dma_bug(ide_drive_t *drive) { return 0; } static inline u8 ide_find_dma_mode(ide_drive_t *drive, u8 speed) { return 0; } -- cgit v1.2.3 From ffa15a6915b7f6f6f69b4a66e1100a9c68d11250 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:46 +0200 Subject: ide: make ide_dma_timeout() available also for CONFIG_BLK_DEV_IDEDMA_SFF=n Make ide_dma_timeout() available also for CONFIG_BLK_DEV_IDEDMA_SFF=n and convert {ics,au1xxx-}ide.c to use it. While at it: - dump ATA Status register content on error - use EXPORT_SYMBOL_GPL() to match the rest of SFF DMA functions Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 16 +--------------- drivers/ide/ide-dma.c | 9 +++++---- drivers/ide/mips/au1xxx-ide.c | 14 +------------- include/linux/ide.h | 2 +- 4 files changed, 8 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 40ebe04f3995..76bdc9a27f6f 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -372,20 +372,6 @@ static int icside_dma_test_irq(ide_drive_t *drive) ICS_ARCIN_V6_INTRSTAT_1)) & 1; } -static void icside_dma_timeout(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); - - if (icside_dma_test_irq(drive)) - return; - - ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); - - icside_dma_end(drive); -} - static int icside_dma_init(ide_hwif_t *hwif, const struct ide_port_info *d) { hwif->dmatable_cpu = NULL; @@ -401,7 +387,7 @@ static const struct ide_dma_ops icside_v6_dma_ops = { .dma_start = icside_dma_start, .dma_end = icside_dma_end, .dma_test_irq = icside_dma_test_irq, - .dma_timeout = icside_dma_timeout, + .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, }; #else diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 08cd878de50b..244b61b573ce 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -829,8 +829,7 @@ void ide_dma_lost_irq(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_dma_lost_irq); -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF -void ide_dma_timeout (ide_drive_t *drive) +void ide_dma_timeout(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); @@ -839,11 +838,13 @@ void ide_dma_timeout (ide_drive_t *drive) if (hwif->dma_ops->dma_test_irq(drive)) return; + ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); + hwif->dma_ops->dma_end(drive); } +EXPORT_SYMBOL_GPL(ide_dma_timeout); -EXPORT_SYMBOL(ide_dma_timeout); - +#ifdef CONFIG_BLK_DEV_IDEDMA_SFF void ide_release_dma_engine(ide_hwif_t *hwif) { if (hwif->dmatable_cpu) { diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 92c90db4bb30..f9e88cfec827 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -366,18 +366,6 @@ static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize, u32 de } #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA -static void auide_dma_timeout(ide_drive_t *drive) -{ - ide_hwif_t *hwif = HWIF(drive); - - printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); - - if (auide_dma_test_irq(drive)) - return; - - auide_dma_end(drive); -} - static const struct ide_dma_ops au1xxx_dma_ops = { .dma_host_set = auide_dma_host_set, .dma_setup = auide_dma_setup, @@ -386,7 +374,7 @@ static const struct ide_dma_ops au1xxx_dma_ops = { .dma_end = auide_dma_end, .dma_test_irq = auide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = auide_dma_timeout, + .dma_timeout = ide_dma_timeout, }; static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d) diff --git a/include/linux/ide.h b/include/linux/ide.h index ae4a25d2b066..39aaff8ff457 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1437,11 +1437,11 @@ void ide_dma_exec_cmd(ide_drive_t *, u8); extern void ide_dma_start(ide_drive_t *); int ide_dma_end(ide_drive_t *); int ide_dma_test_irq(ide_drive_t *); -extern void ide_dma_timeout(ide_drive_t *); extern const struct ide_dma_ops sff_dma_ops; #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ void ide_dma_lost_irq(ide_drive_t *); +void ide_dma_timeout(ide_drive_t *); #else static inline int ide_id_dma_bug(ide_drive_t *drive) { return 0; } -- cgit v1.2.3 From 2bbd57cad3d72334c9fcc4e229a5a5b04dc6aebc Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:47 +0200 Subject: ide: switch to DMA-mapping API part #2 Follow-up to commit 5c05ff68b9a9b40a9be949497e0aa980185565cf ("ide: switch to DMA-mapping API"): * pci_{alloc,free}_consistent() -> dma_{alloc,free}_coherent() in ide_{allocate,release}_dma_engine(). * Add ->prd_max_nents and ->prd_ent_size fields to ide_hwif_t (+ set default values in ide_allocate_dma_engine()). * Make ide_{allocate,release}_dma_engine() available also for CONFIG_BLK_DEV_IDEDMA_SFF=n. Then convert au1xxx-ide.c, scc_pata.c and sgiioc4.c to use them. * Add missing ->init_dma method to scc_pata. This patch also fixes: - ->dmatable_cpu leak for au1xxx-ide - too early realease of ->dmatable_cpu for scc_pata - wrong amount of ->dmatable_cpu memory being freed for sgiioc4 While at it: - remove superfluous ->dma_base check from ide_unregister() - return -ENOMEM on error in ide_release_dma_engine() - beautify error message in ide_release_dma_engine() Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 31 +++++++++++++++++++------------ drivers/ide/ide.c | 3 +-- drivers/ide/mips/au1xxx-ide.c | 7 +++---- drivers/ide/pci/scc_pata.c | 14 +++++++------- drivers/ide/pci/sgiioc4.c | 15 +++++++-------- include/linux/ide.h | 17 ++++++++++------- 6 files changed, 47 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 244b61b573ce..3f949b5db353 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -844,36 +844,43 @@ void ide_dma_timeout(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_dma_timeout); -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF void ide_release_dma_engine(ide_hwif_t *hwif) { if (hwif->dmatable_cpu) { - struct pci_dev *pdev = to_pci_dev(hwif->dev); + int prd_size = hwif->prd_max_nents * hwif->prd_ent_size; - pci_free_consistent(pdev, PRD_ENTRIES * PRD_BYTES, - hwif->dmatable_cpu, hwif->dmatable_dma); + dma_free_coherent(hwif->dev, prd_size, + hwif->dmatable_cpu, hwif->dmatable_dma); hwif->dmatable_cpu = NULL; } } +EXPORT_SYMBOL_GPL(ide_release_dma_engine); int ide_allocate_dma_engine(ide_hwif_t *hwif) { - struct pci_dev *pdev = to_pci_dev(hwif->dev); + int prd_size; - hwif->dmatable_cpu = pci_alloc_consistent(pdev, - PRD_ENTRIES * PRD_BYTES, - &hwif->dmatable_dma); + if (hwif->prd_max_nents == 0) + hwif->prd_max_nents = PRD_ENTRIES; + if (hwif->prd_ent_size == 0) + hwif->prd_ent_size = PRD_BYTES; - if (hwif->dmatable_cpu) - return 0; + prd_size = hwif->prd_max_nents * hwif->prd_ent_size; - printk(KERN_ERR "%s: -- Error, unable to allocate DMA table.\n", + hwif->dmatable_cpu = dma_alloc_coherent(hwif->dev, prd_size, + &hwif->dmatable_dma, + GFP_ATOMIC); + if (hwif->dmatable_cpu == NULL) { + printk(KERN_ERR "%s: unable to allocate PRD table\n", hwif->name); + return -ENOMEM; + } - return 1; + return 0; } EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); +#ifdef CONFIG_BLK_DEV_IDEDMA_SFF const struct ide_dma_ops sff_dma_ops = { .dma_host_set = ide_dma_host_set, .dma_setup = ide_dma_setup, diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index a498245dc213..083783e851d1 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -227,8 +227,7 @@ void ide_unregister(ide_hwif_t *hwif) kfree(hwif->sg_table); unregister_blkdev(hwif->major, hwif->name); - if (hwif->dma_base) - ide_release_dma_engine(hwif); + ide_release_dma_engine(hwif); mutex_unlock(&ide_cfg_mtx); } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index f9e88cfec827..0ec8fd1e4dcb 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -427,10 +427,9 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d) NUM_DESCRIPTORS); auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->rx_chan, NUM_DESCRIPTORS); - - hwif->dmatable_cpu = dma_alloc_coherent(hwif->dev, - PRD_ENTRIES * PRD_BYTES, /* 1 Page */ - &hwif->dmatable_dma, GFP_KERNEL); + + /* FIXME: check return value */ + (void)ide_allocate_dma_engine(hwif); au1xxx_dbdma_start( auide->tx_chan ); au1xxx_dbdma_start( auide->rx_chan ); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 3e75bf5f5e37..9ce1d8059921 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -821,6 +821,12 @@ static void __devinit init_iops_scc(ide_hwif_t *hwif) init_mmio_iops_scc(hwif); } +static int __devinit scc_init_dma(ide_hwif_t *hwif, + const struct ide_port_info *d) +{ + return ide_allocate_dma_engine(hwif); +} + static u8 scc_cable_detect(ide_hwif_t *hwif) { return ATA_CBL_PATA80; @@ -885,6 +891,7 @@ static const struct ide_dma_ops scc_dma_ops = { { \ .name = name_str, \ .init_iops = init_iops_scc, \ + .init_dma = scc_init_dma, \ .init_hwif = init_hwif_scc, \ .tp_ops = &scc_tp_ops, \ .port_ops = &scc_port_ops, \ @@ -922,13 +929,6 @@ static void __devexit scc_remove(struct pci_dev *dev) { struct scc_ports *ports = pci_get_drvdata(dev); struct ide_host *host = ports->host; - ide_hwif_t *hwif = host->ports[0]; - - if (hwif->dmatable_cpu) { - pci_free_consistent(dev, PRD_ENTRIES * PRD_BYTES, - hwif->dmatable_cpu, hwif->dmatable_dma); - hwif->dmatable_cpu = NULL; - } ide_host_remove(host); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 84cd986810cf..dd634541ce36 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -357,14 +357,13 @@ ide_dma_sgiioc4(ide_hwif_t *hwif, const struct ide_port_info *d) } hwif->dma_base = (unsigned long) virt_dma_base; - hwif->dmatable_cpu = pci_alloc_consistent(dev, - IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, - &hwif->dmatable_dma); + hwif->sg_max_nents = IOC4_PRD_ENTRIES; - if (!hwif->dmatable_cpu) - goto dma_pci_alloc_failure; + hwif->prd_max_nents = IOC4_PRD_ENTRIES; + hwif->prd_ent_size = IOC4_PRD_BYTES; - hwif->sg_max_nents = IOC4_PRD_ENTRIES; + if (ide_allocate_dma_engine(hwif)) + goto dma_pci_alloc_failure; pad = pci_alloc_consistent(dev, IOC4_IDE_CACHELINE_SIZE, (dma_addr_t *)&hwif->extra_base); @@ -373,8 +372,8 @@ ide_dma_sgiioc4(ide_hwif_t *hwif, const struct ide_port_info *d) return 0; } - pci_free_consistent(dev, IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, - hwif->dmatable_cpu, hwif->dmatable_dma); + ide_release_dma_engine(hwif); + printk(KERN_ERR "%s(%s) -- ERROR: Unable to allocate DMA maps\n", __func__, hwif->name); printk(KERN_INFO "%s: changing from DMA to PIO mode", hwif->name); diff --git a/include/linux/ide.h b/include/linux/ide.h index 39aaff8ff457..8121aa9240c4 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -788,6 +788,12 @@ typedef struct hwif_s { unsigned int *dmatable_cpu; /* dma physical region descriptor table (dma view) */ dma_addr_t dmatable_dma; + + /* maximum number of PRD table entries */ + int prd_max_nents; + /* PRD entry size in bytes */ + int prd_ent_size; + /* Scatter-gather list used to build the above */ struct scatterlist *sg_table; int sg_max_nents; /* Maximum number of entries in it */ @@ -1423,14 +1429,14 @@ int ide_set_dma(ide_drive_t *); void ide_check_dma_crc(ide_drive_t *); ide_startstop_t ide_dma_intr(ide_drive_t *); +int ide_allocate_dma_engine(ide_hwif_t *); +void ide_release_dma_engine(ide_hwif_t *); + int ide_build_sglist(ide_drive_t *, struct request *); void ide_destroy_dmatable(ide_drive_t *); #ifdef CONFIG_BLK_DEV_IDEDMA_SFF extern int ide_build_dmatable(ide_drive_t *, struct request *); -int ide_allocate_dma_engine(ide_hwif_t *); -void ide_release_dma_engine(ide_hwif_t *); - void ide_dma_host_set(ide_drive_t *, int); extern int ide_dma_setup(ide_drive_t *); void ide_dma_exec_cmd(ide_drive_t *, u8); @@ -1453,11 +1459,8 @@ static inline void ide_dma_on(ide_drive_t *drive) { ; } static inline void ide_dma_verbose(ide_drive_t *drive) { ; } static inline int ide_set_dma(ide_drive_t *drive) { return 1; } static inline void ide_check_dma_crc(ide_drive_t *drive) { ; } -#endif /* CONFIG_BLK_DEV_IDEDMA */ - -#ifndef CONFIG_BLK_DEV_IDEDMA_SFF static inline void ide_release_dma_engine(ide_hwif_t *hwif) { ; } -#endif +#endif /* CONFIG_BLK_DEV_IDEDMA */ #ifdef CONFIG_BLK_DEV_IDEACPI extern int ide_acpi_exec_tfs(ide_drive_t *drive); -- cgit v1.2.3 From 2dbe7e919eb696c86790797f8a814bef19a0d50a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 13 Oct 2008 21:39:47 +0200 Subject: ide: move SFF DMA code to ide-dma-sff.c Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Makefile | 1 + drivers/ide/ide-dma-sff.c | 356 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-dma.c | 363 +--------------------------------------------- include/linux/ide.h | 4 + 4 files changed, 362 insertions(+), 362 deletions(-) create mode 100644 drivers/ide/ide-dma-sff.c (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index e6e7811812d2..0c30adb115c8 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -12,6 +12,7 @@ ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o +ide-core-$(CONFIG_BLK_DEV_IDEDMA_SFF) += ide-dma-sff.o ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c new file mode 100644 index 000000000000..0903782689e9 --- /dev/null +++ b/drivers/ide/ide-dma-sff.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include +#include + +/** + * config_drive_for_dma - attempt to activate IDE DMA + * @drive: the drive to place in DMA mode + * + * If the drive supports at least mode 2 DMA or UDMA of any kind + * then attempt to place it into DMA mode. Drives that are known to + * support DMA but predate the DMA properties or that are known + * to have DMA handling bugs are also set up appropriately based + * on the good/bad drive lists. + */ + +int config_drive_for_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u16 *id = drive->id; + + if (drive->media != ide_disk) { + if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA) + return 0; + } + + /* + * Enable DMA on any drive that has + * UltraDMA (mode 0/1/2/3/4/5/6) enabled + */ + if ((id[ATA_ID_FIELD_VALID] & 4) && + ((id[ATA_ID_UDMA_MODES] >> 8) & 0x7f)) + return 1; + + /* + * Enable DMA on any drive that has mode2 DMA + * (multi or single) enabled + */ + if (id[ATA_ID_FIELD_VALID] & 2) /* regular DMA */ + if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 || + (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404) + return 1; + + /* Consult the list of known "good" drives */ + if (ide_dma_good_drive(drive)) + return 1; + + return 0; +} + +/** + * ide_dma_host_set - Enable/disable DMA on a host + * @drive: drive to control + * + * Enable/disable DMA on an IDE controller following generic + * bus-mastering IDE controller behaviour. + */ + +void ide_dma_host_set(ide_drive_t *drive, int on) +{ + ide_hwif_t *hwif = drive->hwif; + u8 unit = drive->dn & 1; + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + + if (on) + dma_stat |= (1 << (5 + unit)); + else + dma_stat &= ~(1 << (5 + unit)); + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(dma_stat, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); +} +EXPORT_SYMBOL_GPL(ide_dma_host_set); + +/** + * ide_build_dmatable - build IDE DMA table + * + * ide_build_dmatable() prepares a dma request. We map the command + * to get the pci bus addresses of the buffers and then build up + * the PRD table that the IDE layer wants to be fed. + * + * Most chipsets correctly interpret a length of 0x0000 as 64KB, + * but at least one (e.g. CS5530) misinterprets it as zero (!). + * So we break the 64KB entry into two 32KB entries instead. + * + * Returns the number of built PRD entries if all went okay, + * returns 0 otherwise. + * + * May also be invoked from trm290.c + */ + +int ide_build_dmatable(ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = drive->hwif; + __le32 *table = (__le32 *)hwif->dmatable_cpu; + unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0; + unsigned int count = 0; + int i; + struct scatterlist *sg; + + hwif->sg_nents = ide_build_sglist(drive, rq); + if (hwif->sg_nents == 0) + return 0; + + for_each_sg(hwif->sg_table, sg, hwif->sg_nents, i) { + u32 cur_addr, cur_len, xcount, bcount; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + /* + * Fill in the dma table, without crossing any 64kB boundaries. + * Most hardware requires 16-bit alignment of all blocks, + * but the trm290 requires 32-bit alignment. + */ + + while (cur_len) { + if (count++ >= PRD_ENTRIES) + goto use_pio_instead; + + bcount = 0x10000 - (cur_addr & 0xffff); + if (bcount > cur_len) + bcount = cur_len; + *table++ = cpu_to_le32(cur_addr); + xcount = bcount & 0xffff; + if (is_trm290) + xcount = ((xcount >> 2) - 1) << 16; + if (xcount == 0x0000) { + if (count++ >= PRD_ENTRIES) + goto use_pio_instead; + *table++ = cpu_to_le32(0x8000); + *table++ = cpu_to_le32(cur_addr + 0x8000); + xcount = 0x8000; + } + *table++ = cpu_to_le32(xcount); + cur_addr += bcount; + cur_len -= bcount; + } + } + + if (count) { + if (!is_trm290) + *--table |= cpu_to_le32(0x80000000); + return count; + } + +use_pio_instead: + printk(KERN_ERR "%s: %s\n", drive->name, + count ? "DMA table too small" : "empty DMA table?"); + + ide_destroy_dmatable(drive); + + return 0; /* revert to PIO for this request */ +} +EXPORT_SYMBOL_GPL(ide_build_dmatable); + +/** + * ide_dma_setup - begin a DMA phase + * @drive: target device + * + * Build an IDE DMA PRD (IDE speak for scatter gather table) + * and then set up the DMA transfer registers for a device + * that follows generic IDE PCI DMA behaviour. Controllers can + * override this function if they need to + * + * Returns 0 on success. If a PIO fallback is required then 1 + * is returned. + */ + +int ide_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->hwgroup->rq; + unsigned int reading; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 dma_stat; + + if (rq_data_dir(rq)) + reading = 0; + else + reading = 1 << 3; + + /* fall back to pio! */ + if (!ide_build_dmatable(drive, rq)) { + ide_map_sg(drive, rq); + return 1; + } + + /* PRD table */ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writel(hwif->dmatable_dma, + (void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS)); + else + outl(hwif->dmatable_dma, hwif->dma_base + ATA_DMA_TABLE_OFS); + + /* specify r/w */ + if (mmio) + writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + else + outb(reading, hwif->dma_base + ATA_DMA_CMD); + + /* read DMA status for INTR & ERROR flags */ + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + + /* clear INTR & ERROR flags */ + if (mmio) + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); + + drive->waiting_for_dma = 1; + return 0; +} +EXPORT_SYMBOL_GPL(ide_dma_setup); + +/** + * dma_timer_expiry - handle a DMA timeout + * @drive: Drive that timed out + * + * An IDE DMA transfer timed out. In the event of an error we ask + * the driver to resolve the problem, if a DMA transfer is still + * in progress we continue to wait (arguably we need to add a + * secondary 'I don't care what the drive thinks' timeout here) + * Finally if we have an interrupt we let it complete the I/O. + * But only one time - we clear expiry and if it's still not + * completed after WAIT_CMD, we error and retry in PIO. + * This can occur if an interrupt is lost or due to hang or bugs. + */ + +static int dma_timer_expiry(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + + printk(KERN_WARNING "%s: %s: DMA status (0x%02x)\n", + drive->name, __func__, dma_stat); + + if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */ + return WAIT_CMD; + + hwif->hwgroup->expiry = NULL; /* one free ride for now */ + + /* 1 dmaing, 2 error, 4 intr */ + if (dma_stat & 2) /* ERROR */ + return -1; + + if (dma_stat & 1) /* DMAing */ + return WAIT_CMD; + + if (dma_stat & 4) /* Got an Interrupt */ + return WAIT_CMD; + + return 0; /* Status is unknown -- reset the bus */ +} + +void ide_dma_exec_cmd(ide_drive_t *drive, u8 command) +{ + /* issue cmd to drive */ + ide_execute_command(drive, command, &ide_dma_intr, 2 * WAIT_CMD, + dma_timer_expiry); +} +EXPORT_SYMBOL_GPL(ide_dma_exec_cmd); + +void ide_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_cmd; + + /* Note that this is done *after* the cmd has + * been issued to the drive, as per the BM-IDE spec. + * The Promise Ultra33 doesn't work correctly when + * we do this part before issuing the drive cmd. + */ + if (hwif->host_flags & IDE_HFLAG_MMIO) { + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + /* start DMA */ + writeb(dma_cmd | 1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + } else { + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD); + } + + wmb(); +} +EXPORT_SYMBOL_GPL(ide_dma_start); + +/* returns 1 on error, 0 otherwise */ +int ide_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 dma_stat = 0, dma_cmd = 0; + + drive->waiting_for_dma = 0; + + if (mmio) { + /* get DMA command mode */ + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + /* stop DMA */ + writeb(dma_cmd & ~1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + } else { + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); + } + + /* get DMA status */ + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + + if (mmio) + /* clear the INTR & ERROR bits */ + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); + + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + wmb(); + return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; +} +EXPORT_SYMBOL_GPL(ide_dma_end); + +/* returns 1 if dma irq issued, 0 otherwise */ +int ide_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + + /* return 1 if INTR asserted */ + if ((dma_stat & 4) == 4) + return 1; + + return 0; +} +EXPORT_SYMBOL_GPL(ide_dma_test_irq); + +const struct ide_dma_ops sff_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_exec_cmd = ide_dma_exec_cmd, + .dma_start = ide_dma_start, + .dma_end = ide_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_timeout = ide_dma_timeout, + .dma_lost_irq = ide_dma_lost_irq, +}; +EXPORT_SYMBOL_GPL(sff_dma_ops); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index d935a6ec022f..fffd11717b2d 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -33,7 +33,6 @@ #include #include #include -#include static const struct drive_list_entry drive_whitelist[] = { { "Micropolis 2112A" , NULL }, @@ -109,7 +108,7 @@ ide_startstop_t ide_dma_intr(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_dma_intr); -static int ide_dma_good_drive(ide_drive_t *drive) +int ide_dma_good_drive(ide_drive_t *drive) { return ide_in_drive_list(drive->id, drive_whitelist); } @@ -142,90 +141,6 @@ int ide_build_sglist(ide_drive_t *drive, struct request *rq) } EXPORT_SYMBOL_GPL(ide_build_sglist); -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF -/** - * ide_build_dmatable - build IDE DMA table - * - * ide_build_dmatable() prepares a dma request. We map the command - * to get the pci bus addresses of the buffers and then build up - * the PRD table that the IDE layer wants to be fed. - * - * Most chipsets correctly interpret a length of 0x0000 as 64KB, - * but at least one (e.g. CS5530) misinterprets it as zero (!). - * So we break the 64KB entry into two 32KB entries instead. - * - * Returns the number of built PRD entries if all went okay, - * returns 0 otherwise. - * - * May also be invoked from trm290.c - */ - -int ide_build_dmatable(ide_drive_t *drive, struct request *rq) -{ - ide_hwif_t *hwif = drive->hwif; - __le32 *table = (__le32 *)hwif->dmatable_cpu; - unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0; - unsigned int count = 0; - int i; - struct scatterlist *sg; - - hwif->sg_nents = ide_build_sglist(drive, rq); - if (hwif->sg_nents == 0) - return 0; - - for_each_sg(hwif->sg_table, sg, hwif->sg_nents, i) { - u32 cur_addr, cur_len, xcount, bcount; - - cur_addr = sg_dma_address(sg); - cur_len = sg_dma_len(sg); - - /* - * Fill in the dma table, without crossing any 64kB boundaries. - * Most hardware requires 16-bit alignment of all blocks, - * but the trm290 requires 32-bit alignment. - */ - - while (cur_len) { - if (count++ >= PRD_ENTRIES) - goto use_pio_instead; - - bcount = 0x10000 - (cur_addr & 0xffff); - if (bcount > cur_len) - bcount = cur_len; - *table++ = cpu_to_le32(cur_addr); - xcount = bcount & 0xffff; - if (is_trm290) - xcount = ((xcount >> 2) - 1) << 16; - if (xcount == 0x0000) { - if (count++ >= PRD_ENTRIES) - goto use_pio_instead; - *table++ = cpu_to_le32(0x8000); - *table++ = cpu_to_le32(cur_addr + 0x8000); - xcount = 0x8000; - } - *table++ = cpu_to_le32(xcount); - cur_addr += bcount; - cur_len -= bcount; - } - } - - if (count) { - if (!is_trm290) - *--table |= cpu_to_le32(0x80000000); - return count; - } - -use_pio_instead: - printk(KERN_ERR "%s: %s\n", drive->name, - count ? "DMA table too small" : "empty DMA table?"); - - ide_destroy_dmatable(drive); - - return 0; /* revert to PIO for this request */ -} -EXPORT_SYMBOL_GPL(ide_build_dmatable); -#endif - /** * ide_destroy_dmatable - clean up DMA mapping * @drive: The drive to unmap @@ -246,120 +161,6 @@ void ide_destroy_dmatable(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_destroy_dmatable); -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF -/** - * config_drive_for_dma - attempt to activate IDE DMA - * @drive: the drive to place in DMA mode - * - * If the drive supports at least mode 2 DMA or UDMA of any kind - * then attempt to place it into DMA mode. Drives that are known to - * support DMA but predate the DMA properties or that are known - * to have DMA handling bugs are also set up appropriately based - * on the good/bad drive lists. - */ - -static int config_drive_for_dma(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u16 *id = drive->id; - - if (drive->media != ide_disk) { - if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA) - return 0; - } - - /* - * Enable DMA on any drive that has - * UltraDMA (mode 0/1/2/3/4/5/6) enabled - */ - if ((id[ATA_ID_FIELD_VALID] & 4) && - ((id[ATA_ID_UDMA_MODES] >> 8) & 0x7f)) - return 1; - - /* - * Enable DMA on any drive that has mode2 DMA - * (multi or single) enabled - */ - if (id[ATA_ID_FIELD_VALID] & 2) /* regular DMA */ - if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 || - (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404) - return 1; - - /* Consult the list of known "good" drives */ - if (ide_dma_good_drive(drive)) - return 1; - - return 0; -} - -/** - * dma_timer_expiry - handle a DMA timeout - * @drive: Drive that timed out - * - * An IDE DMA transfer timed out. In the event of an error we ask - * the driver to resolve the problem, if a DMA transfer is still - * in progress we continue to wait (arguably we need to add a - * secondary 'I don't care what the drive thinks' timeout here) - * Finally if we have an interrupt we let it complete the I/O. - * But only one time - we clear expiry and if it's still not - * completed after WAIT_CMD, we error and retry in PIO. - * This can occur if an interrupt is lost or due to hang or bugs. - */ - -static int dma_timer_expiry(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); - - printk(KERN_WARNING "%s: %s: DMA status (0x%02x)\n", - drive->name, __func__, dma_stat); - - if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */ - return WAIT_CMD; - - hwif->hwgroup->expiry = NULL; /* one free ride for now */ - - /* 1 dmaing, 2 error, 4 intr */ - if (dma_stat & 2) /* ERROR */ - return -1; - - if (dma_stat & 1) /* DMAing */ - return WAIT_CMD; - - if (dma_stat & 4) /* Got an Interrupt */ - return WAIT_CMD; - - return 0; /* Status is unknown -- reset the bus */ -} - -/** - * ide_dma_host_set - Enable/disable DMA on a host - * @drive: drive to control - * - * Enable/disable DMA on an IDE controller following generic - * bus-mastering IDE controller behaviour. - */ - -void ide_dma_host_set(ide_drive_t *drive, int on) -{ - ide_hwif_t *hwif = drive->hwif; - u8 unit = drive->dn & 1; - u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); - - if (on) - dma_stat |= (1 << (5 + unit)); - else - dma_stat &= ~(1 << (5 + unit)); - - if (hwif->host_flags & IDE_HFLAG_MMIO) - writeb(dma_stat, - (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); - else - outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); -} -EXPORT_SYMBOL_GPL(ide_dma_host_set); -#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ - /** * ide_dma_off_quietly - Generic DMA kill * @drive: drive to control @@ -406,154 +207,6 @@ void ide_dma_on(ide_drive_t *drive) drive->hwif->dma_ops->dma_host_set(drive, 1); } -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF -/** - * ide_dma_setup - begin a DMA phase - * @drive: target device - * - * Build an IDE DMA PRD (IDE speak for scatter gather table) - * and then set up the DMA transfer registers for a device - * that follows generic IDE PCI DMA behaviour. Controllers can - * override this function if they need to - * - * Returns 0 on success. If a PIO fallback is required then 1 - * is returned. - */ - -int ide_dma_setup(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; - unsigned int reading; - u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; - u8 dma_stat; - - if (rq_data_dir(rq)) - reading = 0; - else - reading = 1 << 3; - - /* fall back to pio! */ - if (!ide_build_dmatable(drive, rq)) { - ide_map_sg(drive, rq); - return 1; - } - - /* PRD table */ - if (hwif->host_flags & IDE_HFLAG_MMIO) - writel(hwif->dmatable_dma, - (void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS)); - else - outl(hwif->dmatable_dma, hwif->dma_base + ATA_DMA_TABLE_OFS); - - /* specify r/w */ - if (mmio) - writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); - else - outb(reading, hwif->dma_base + ATA_DMA_CMD); - - /* read DMA status for INTR & ERROR flags */ - dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); - - /* clear INTR & ERROR flags */ - if (mmio) - writeb(dma_stat | 6, - (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); - else - outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); - - drive->waiting_for_dma = 1; - return 0; -} -EXPORT_SYMBOL_GPL(ide_dma_setup); - -void ide_dma_exec_cmd(ide_drive_t *drive, u8 command) -{ - /* issue cmd to drive */ - ide_execute_command(drive, command, &ide_dma_intr, 2 * WAIT_CMD, - dma_timer_expiry); -} -EXPORT_SYMBOL_GPL(ide_dma_exec_cmd); - -void ide_dma_start(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u8 dma_cmd; - - /* Note that this is done *after* the cmd has - * been issued to the drive, as per the BM-IDE spec. - * The Promise Ultra33 doesn't work correctly when - * we do this part before issuing the drive cmd. - */ - if (hwif->host_flags & IDE_HFLAG_MMIO) { - dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); - /* start DMA */ - writeb(dma_cmd | 1, - (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); - } else { - dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); - outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD); - } - - wmb(); -} -EXPORT_SYMBOL_GPL(ide_dma_start); - -/* returns 1 on error, 0 otherwise */ -int ide_dma_end(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; - u8 dma_stat = 0, dma_cmd = 0; - - drive->waiting_for_dma = 0; - - if (mmio) { - /* get DMA command mode */ - dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); - /* stop DMA */ - writeb(dma_cmd & ~1, - (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); - } else { - dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); - outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); - } - - /* get DMA status */ - dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); - - if (mmio) - /* clear the INTR & ERROR bits */ - writeb(dma_stat | 6, - (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); - else - outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); - - /* purge DMA mappings */ - ide_destroy_dmatable(drive); - /* verify good DMA status */ - wmb(); - return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; -} -EXPORT_SYMBOL_GPL(ide_dma_end); - -/* returns 1 if dma irq issued, 0 otherwise */ -int ide_dma_test_irq(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); - - /* return 1 if INTR asserted */ - if ((dma_stat & 4) == 4) - return 1; - - return 0; -} -EXPORT_SYMBOL_GPL(ide_dma_test_irq); -#else -static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; } -#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ - int __ide_dma_bad_drive(ide_drive_t *drive) { u16 *id = drive->id; @@ -846,17 +499,3 @@ int ide_allocate_dma_engine(ide_hwif_t *hwif) return 0; } EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); - -#ifdef CONFIG_BLK_DEV_IDEDMA_SFF -const struct ide_dma_ops sff_dma_ops = { - .dma_host_set = ide_dma_host_set, - .dma_setup = ide_dma_setup, - .dma_exec_cmd = ide_dma_exec_cmd, - .dma_start = ide_dma_start, - .dma_end = ide_dma_end, - .dma_test_irq = ide_dma_test_irq, - .dma_timeout = ide_dma_timeout, - .dma_lost_irq = ide_dma_lost_irq, -}; -EXPORT_SYMBOL_GPL(sff_dma_ops); -#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 8121aa9240c4..5a39dab2cc91 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1412,6 +1412,7 @@ struct drive_list_entry { int ide_in_drive_list(u16 *, const struct drive_list_entry *); #ifdef CONFIG_BLK_DEV_IDEDMA +int ide_dma_good_drive(ide_drive_t *); int __ide_dma_bad_drive(ide_drive_t *); int ide_id_dma_bug(ide_drive_t *); @@ -1436,6 +1437,7 @@ int ide_build_sglist(ide_drive_t *, struct request *); void ide_destroy_dmatable(ide_drive_t *); #ifdef CONFIG_BLK_DEV_IDEDMA_SFF +int config_drive_for_dma(ide_drive_t *); extern int ide_build_dmatable(ide_drive_t *, struct request *); void ide_dma_host_set(ide_drive_t *, int); extern int ide_dma_setup(ide_drive_t *); @@ -1444,6 +1446,8 @@ extern void ide_dma_start(ide_drive_t *); int ide_dma_end(ide_drive_t *); int ide_dma_test_irq(ide_drive_t *); extern const struct ide_dma_ops sff_dma_ops; +#else +static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; } #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ void ide_dma_lost_irq(ide_drive_t *); -- cgit v1.2.3 From 0a9b6f8864362e31e348b12922a92b48b1b8cc94 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 13 Oct 2008 21:39:49 +0200 Subject: ide: add ide_drive_t.dma flag This flag is to accomodate ide-cd functionality into ide atapi. There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 9 +++++---- include/linux/ide.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index a1d8c3557a42..d55784173a79 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -545,7 +545,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; u16 bcount; - u8 dma = 0, scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI); + u8 scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI); /* We haven't transferred any data yet */ pc->xferred = 0; @@ -566,15 +566,16 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, (drive->dev_flags & IDE_DFLAG_USING_DMA)) { if (scsi) hwif->sg_mapped = 1; - dma = !hwif->dma_ops->dma_setup(drive); + drive->dma = !hwif->dma_ops->dma_setup(drive); if (scsi) hwif->sg_mapped = 0; } - if (!dma) + if (!drive->dma) pc->flags &= ~PC_FLAG_DMA_OK; - ide_pktcmd_tf_load(drive, scsi ? 0 : IDE_TFLAG_OUT_DEVICE, bcount, dma); + ide_pktcmd_tf_load(drive, scsi ? 0 : IDE_TFLAG_OUT_DEVICE, bcount, + drive->dma); /* Issue the packet command */ if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { diff --git a/include/linux/ide.h b/include/linux/ide.h index 5a39dab2cc91..8247b286838d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -603,6 +603,7 @@ struct ide_drive_s { u8 select; /* basic drive/head select reg value */ u8 retry_pio; /* retrying dma capable host in pio */ u8 waiting_for_dma; /* dma currently in progress */ + u8 dma; /* atapi dma flag */ u8 quirk_list; /* considered quirky, set for a specific host */ u8 init_speed; /* transfer rate set at boot */ -- cgit v1.2.3 From 4abdc6ee7c47a1a6e12f95717e461baeebee5df7 Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Mon, 13 Oct 2008 21:39:50 +0200 Subject: ide: Implement disk shock protection support (v4) On user request (through sysfs), the IDLE IMMEDIATE command with UNLOAD FEATURE as specified in ATA-7 is issued to the device and processing of the request queue is stopped thereafter until the specified timeout expires or user space asks to resume normal operation. This is supposed to prevent the heads of a hard drive from accidentally crashing onto the platter when a heavy shock is anticipated (like a falling laptop expected to hit the floor). Port resets are deferred whenever a device on that port is in the parked state. v3: Elias Oltmanns wrote: [...] > >> 1. Make sure that no negative value is being passed to > >> jiffies_to_msecs() in ide_park_show(). > >> 2. Drop the superfluous variable hwif in ide_special_rq(). > >> 3. Skip initialisation of task and tf in ide_special_rq() if we are not > >> handling a (un)park request. > > > > Well, #3 should have been done differently because we donn't want to > > check for REQ_(UN)?PARK_HEADS more often than is necessary. > > While preparing the backport to 2.6.27, it has just occurred to me that > we need to clear the IDE_DFLAG_PARKED flag in ide_disk_pre_reset() > because this flag must not be set after *any* sort of access to the > device. v4: Fix a memory leak due to a missing blk_put_request() in issue_park_cmd(). Additionally, we should plug the queue when enqueueing the unpark request because there is no guarantee that the park timeout has not expired by then. Even though the chance for that to happen is very slim, the request might end up hanging in the queue until the next I/O operation is queued up. While at it, clean up the code a little: - make issue_park_cmd() a function of type void since nobody cares for the return value anyway; - use blk_start_queueing() instead of __blk_run_queue() since we don't have to worry about recursion; - remove a superfluous pointer deference in task_no_data_intr(). Signed-off-by: Elias Oltmanns Cc: Jeff Garzik , Cc: Randy Dunlap Cc: Tejun Heo Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Makefile | 2 +- drivers/ide/ide-io.c | 29 ++++++++++- drivers/ide/ide-iops.c | 29 ++++++++++- drivers/ide/ide-park.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-probe.c | 5 ++ drivers/ide/ide-taskfile.c | 11 ++++- drivers/ide/ide.c | 1 + include/linux/ide.h | 13 +++++ 8 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 drivers/ide/ide-park.c (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 0c30adb115c8..ceaf779054ea 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -5,7 +5,7 @@ EXTRA_CFLAGS += -Idrivers/ide ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \ - ide-taskfile.o ide-pio-blacklist.o + ide-taskfile.o ide-park.o ide-pio-blacklist.o # core IDE code ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index e205f46c3c7a..77c6eaeacefa 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -672,7 +672,32 @@ EXPORT_SYMBOL_GPL(ide_devset_execute); static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) { - switch (rq->cmd[0]) { + u8 cmd = rq->cmd[0]; + + if (cmd == REQ_PARK_HEADS || cmd == REQ_UNPARK_HEADS) { + ide_task_t task; + struct ide_taskfile *tf = &task.tf; + + memset(&task, 0, sizeof(task)); + if (cmd == REQ_PARK_HEADS) { + drive->sleep = *(unsigned long *)rq->special; + drive->dev_flags |= IDE_DFLAG_SLEEPING; + tf->command = ATA_CMD_IDLEIMMEDIATE; + tf->feature = 0x44; + tf->lbal = 0x4c; + tf->lbam = 0x4e; + tf->lbah = 0x55; + task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER; + } else /* cmd == REQ_UNPARK_HEADS */ + tf->command = ATA_CMD_CHK_POWER; + + task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE; + task.rq = rq; + drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA; + return do_rw_taskfile(drive, &task); + } + + switch (cmd) { case REQ_DEVSET_EXEC: { int err, (*setfunc)(ide_drive_t *, int) = rq->special; @@ -1008,7 +1033,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) } hwgroup->hwif = hwif; hwgroup->drive = drive; - drive->dev_flags &= ~IDE_DFLAG_SLEEPING; + drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); drive->service_start = jiffies; if (blk_queue_plugged(drive->queue)) { diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 91182ebed468..b762deb2dacb 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -1020,6 +1020,7 @@ static void ide_disk_pre_reset(ide_drive_t *drive) drive->special.b.recalibrate = legacy; drive->mult_count = 0; + drive->dev_flags &= ~IDE_DFLAG_PARKED; if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 && (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) @@ -1079,12 +1080,13 @@ static void pre_reset(ide_drive_t *drive) static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) { unsigned int unit; - unsigned long flags; + unsigned long flags, timeout; ide_hwif_t *hwif; ide_hwgroup_t *hwgroup; struct ide_io_ports *io_ports; const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; + DEFINE_WAIT(wait); spin_lock_irqsave(&ide_lock, flags); hwif = HWIF(drive); @@ -1111,6 +1113,31 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) return ide_started; } + /* We must not disturb devices in the IDE_DFLAG_PARKED state. */ + do { + unsigned long now; + + prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE); + timeout = jiffies; + for (unit = 0; unit < MAX_DRIVES; unit++) { + ide_drive_t *tdrive = &hwif->drives[unit]; + + if (tdrive->dev_flags & IDE_DFLAG_PRESENT && + tdrive->dev_flags & IDE_DFLAG_PARKED && + time_after(tdrive->sleep, timeout)) + timeout = tdrive->sleep; + } + + now = jiffies; + if (time_before_eq(timeout, now)) + break; + + spin_unlock_irqrestore(&ide_lock, flags); + timeout = schedule_timeout_uninterruptible(timeout - now); + spin_lock_irqsave(&ide_lock, flags); + } while (timeout); + finish_wait(&ide_park_wq, &wait); + /* * First, reset any device state data we were maintaining * for any of the drives on this interface. diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c new file mode 100644 index 000000000000..03b00e57e93f --- /dev/null +++ b/drivers/ide/ide-park.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +DECLARE_WAIT_QUEUE_HEAD(ide_park_wq); + +static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) +{ + struct request_queue *q = drive->queue; + struct request *rq; + int rc; + + timeout += jiffies; + spin_lock_irq(&ide_lock); + if (drive->dev_flags & IDE_DFLAG_PARKED) { + ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; + int reset_timer; + + reset_timer = time_before(timeout, drive->sleep); + drive->sleep = timeout; + wake_up_all(&ide_park_wq); + if (reset_timer && hwgroup->sleeping && + del_timer(&hwgroup->timer)) { + hwgroup->sleeping = 0; + hwgroup->busy = 0; + blk_start_queueing(q); + } + spin_unlock_irq(&ide_lock); + return; + } + spin_unlock_irq(&ide_lock); + + rq = blk_get_request(q, READ, __GFP_WAIT); + rq->cmd[0] = REQ_PARK_HEADS; + rq->cmd_len = 1; + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->special = &timeout; + rc = blk_execute_rq(q, NULL, rq, 1); + blk_put_request(rq); + if (rc) + goto out; + + /* + * Make sure that *some* command is sent to the drive after the + * timeout has expired, so power management will be reenabled. + */ + rq = blk_get_request(q, READ, GFP_NOWAIT); + if (unlikely(!rq)) + goto out; + + rq->cmd[0] = REQ_UNPARK_HEADS; + rq->cmd_len = 1; + rq->cmd_type = REQ_TYPE_SPECIAL; + elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1); + +out: + return; +} + +ssize_t ide_park_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + unsigned long now; + unsigned int msecs; + + if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) + return -EOPNOTSUPP; + + spin_lock_irq(&ide_lock); + now = jiffies; + if (drive->dev_flags & IDE_DFLAG_PARKED && + time_after(drive->sleep, now)) + msecs = jiffies_to_msecs(drive->sleep - now); + else + msecs = 0; + spin_unlock_irq(&ide_lock); + + return snprintf(buf, 20, "%u\n", msecs); +} + +ssize_t ide_park_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ +#define MAX_PARK_TIMEOUT 30000 + ide_drive_t *drive = to_ide_device(dev); + long int input; + int rc; + + rc = strict_strtol(buf, 10, &input); + if (rc || input < -2) + return -EINVAL; + if (input > MAX_PARK_TIMEOUT) { + input = MAX_PARK_TIMEOUT; + rc = -EOVERFLOW; + } + + mutex_lock(&ide_setting_mtx); + if (input >= 0) { + if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) + rc = -EOPNOTSUPP; + else if (input || drive->dev_flags & IDE_DFLAG_PARKED) + issue_park_cmd(drive, msecs_to_jiffies(input)); + } else { + if (drive->media == ide_disk) + switch (input) { + case -1: + drive->dev_flags &= ~IDE_DFLAG_NO_UNLOAD; + break; + case -2: + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; + break; + } + else + rc = -EOPNOTSUPP; + } + mutex_unlock(&ide_setting_mtx); + + return rc ? rc : len; +} diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index de8edd306c79..f27baa5f140e 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -208,6 +208,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) drive->ready_stat = 0; if (ata_id_cdb_intr(id)) drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; + /* we don't do head unloading on ATAPI devices */ + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; return; } @@ -223,6 +225,9 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) drive->media = ide_disk; + if (!ata_id_has_unload(drive->id)) + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; + printk(KERN_CONT "%s DISK drive\n", is_cfa ? "CFA" : "ATA"); return; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index a4c2d91179b3..bf4fb9d8d176 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -152,7 +152,16 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) if (!custom) ide_end_drive_cmd(drive, stat, ide_read_error(drive)); - else if (tf->command == ATA_CMD_SET_MULTI) + else if (tf->command == ATA_CMD_IDLEIMMEDIATE) { + hwif->tp_ops->tf_read(drive, task); + if (tf->lbal != 0xc4) { + printk(KERN_ERR "%s: head unload failed!\n", + drive->name); + ide_tf_dump(drive->name, tf); + } else + drive->dev_flags |= IDE_DFLAG_PARKED; + ide_end_drive_cmd(drive, stat, ide_read_error(drive)); + } else if (tf->command == ATA_CMD_SET_MULTI) drive->mult_count = drive->mult_req; return ide_stopped; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 083783e851d1..04f8f13cb9d7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -587,6 +587,7 @@ static struct device_attribute ide_dev_attrs[] = { __ATTR_RO(model), __ATTR_RO(firmware), __ATTR(serial, 0400, serial_show, NULL), + __ATTR(unload_heads, 0644, ide_park_show, ide_park_store), __ATTR_NULL }; diff --git a/include/linux/ide.h b/include/linux/ide.h index 8247b286838d..c47e371554c1 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -156,6 +156,8 @@ enum { */ #define REQ_DRIVE_RESET 0x20 #define REQ_DEVSET_EXEC 0x21 +#define REQ_PARK_HEADS 0x22 +#define REQ_UNPARK_HEADS 0x23 /* * Check for an interrupt and acknowledge the interrupt status @@ -573,6 +575,10 @@ enum { /* retrying in PIO */ IDE_DFLAG_DMA_PIO_RETRY = (1 << 25), IDE_DFLAG_LBA = (1 << 26), + /* don't unload heads */ + IDE_DFLAG_NO_UNLOAD = (1 << 27), + /* heads unloaded, please don't reset port */ + IDE_DFLAG_PARKED = (1 << 28) }; struct ide_drive_s { @@ -1207,6 +1213,13 @@ int ide_check_atapi_device(ide_drive_t *, const char *); void ide_init_pc(struct ide_atapi_pc *); +/* Disk head parking */ +extern wait_queue_head_t ide_park_wq; +ssize_t ide_park_show(struct device *dev, struct device_attribute *attr, + char *buf); +ssize_t ide_park_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len); + /* * Special requests for ide-tape block device strategy routine. * -- cgit v1.2.3 From a5766f11cfd3a0c03450d99c8fe548c2940be884 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 10 Oct 2008 13:22:20 +0100 Subject: regulator: core - Rework machine API to remove string based functions. This improves the machine level API in order to configure regulator constraints and consumers as platform data and removes the old string based API that required several calls to set up each regulator. The intention is to create a struct regulator_init_data, populate it's fields with constraints, consumers devices, etc and then register the regulator device from board.c in the standard Linux way. e.g. regulator LDO2 (supplying codec and sim) platform data. /* regulator LDO2 consumer devices */ static struct regulator_consumer_supply ldo2_consumers[] = { { .dev = &platform_audio_device.dev, .supply = "codec_avdd", }, { .dev = &platform_sim_device.dev, .supply = "sim_vcc", } }; /* regulator LDO2 constraints */ static struct regulator_init_data ldo2_data = { .constraints = { .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, .apply_uV = 1, }, .num_consumer_supplies = ARRAY_SIZE(ldo2_consumers), .consumer_supplies = ldo2_consumers, }; /* machine regulator devices with thier consumers and constraints */ static struct platform_device wm8350_regulator_devices[] = { { .name = "wm8350-regulator", .id = WM8350_LDO_2, .dev = { .platform_data = &ldo2_data, }, }, }; Changes in detail:- o Removed all const char* regulator config functions in machine API. o Created new struct regulator_init_data to contain regulator machine configuration constraints and consmuers. o Changed set_supply(), set_machine_constraints(), set_consumer_device_supply() to remove their string identifier parameters. Also made them static and moved functions nearer top of core.c. o Removed no longer used inline func to_rdev() o Added regulator_get_init_drvdata() to retrieve init data. o Added struct device* as parameter to regulator_register(). o Changed my email address. Signed-off-by: Eric Miao Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- Documentation/power/regulator/machine.txt | 140 ++++----- Documentation/power/regulator/regulator.txt | 8 +- drivers/regulator/bq24022.c | 21 +- drivers/regulator/core.c | 457 ++++++++++++++-------------- include/linux/regulator/driver.h | 8 +- include/linux/regulator/machine.h | 30 +- 6 files changed, 334 insertions(+), 330 deletions(-) (limited to 'include/linux') diff --git a/Documentation/power/regulator/machine.txt b/Documentation/power/regulator/machine.txt index c9a35665cf70..ce3487d99abe 100644 --- a/Documentation/power/regulator/machine.txt +++ b/Documentation/power/regulator/machine.txt @@ -2,17 +2,8 @@ Regulator Machine Driver Interface =================================== The regulator machine driver interface is intended for board/machine specific -initialisation code to configure the regulator subsystem. Typical things that -machine drivers would do are :- +initialisation code to configure the regulator subsystem. - 1. Regulator -> Device mapping. - 2. Regulator supply configuration. - 3. Power Domain constraint setting. - - - -1. Regulator -> device mapping -============================== Consider the following machine :- Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] @@ -21,81 +12,82 @@ Consider the following machine :- The drivers for consumers A & B must be mapped to the correct regulator in order to control their power supply. This mapping can be achieved in machine -initialisation code by calling :- +initialisation code by creating a struct regulator_consumer_supply for +each regulator. + +struct regulator_consumer_supply { + struct device *dev; /* consumer */ + const char *supply; /* consumer supply - e.g. "vcc" */ +}; -int regulator_set_device_supply(const char *regulator, struct device *dev, - const char *supply); +e.g. for the machine above -and is shown with the following code :- +static struct regulator_consumer_supply regulator1_consumers[] = { +{ + .dev = &platform_consumerB_device.dev, + .supply = "Vcc", +},}; -regulator_set_device_supply("Regulator-1", devB, "Vcc"); -regulator_set_device_supply("Regulator-2", devA, "Vcc"); +static struct regulator_consumer_supply regulator2_consumers[] = { +{ + .dev = &platform_consumerA_device.dev, + .supply = "Vcc", +},}; This maps Regulator-1 to the 'Vcc' supply for Consumer B and maps Regulator-2 to the 'Vcc' supply for Consumer A. - -2. Regulator supply configuration. -================================== -Consider the following machine (again) :- - - Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] - | - +-> [Consumer B @ 3.3V] +Constraints can now be registered by defining a struct regulator_init_data +for each regulator power domain. This structure also maps the consumers +to their supply regulator :- + +static struct regulator_init_data regulator1_data = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + }, + .num_consumer_supplies = ARRAY_SIZE(regulator1_consumers), + .consumer_supplies = regulator1_consumers, +}; Regulator-1 supplies power to Regulator-2. This relationship must be registered with the core so that Regulator-1 is also enabled when Consumer A enables it's -supply (Regulator-2). - -This relationship can be register with the core via :- - -int regulator_set_supply(const char *regulator, const char *regulator_supply); - -In this example we would use the following code :- - -regulator_set_supply("Regulator-2", "Regulator-1"); - -Relationships can be queried by calling :- - -const char *regulator_get_supply(const char *regulator); - - -3. Power Domain constraint setting. -=================================== -Each power domain within a system has physical constraints on voltage and -current. This must be defined in software so that the power domain is always -operated within specifications. - -Consider the following machine (again) :- - - Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] - | - +-> [Consumer B @ 3.3V] - -This gives us two regulators and two power domains: - - Domain 1: Regulator-2, Consumer B. - Domain 2: Consumer A. - -Constraints can be registered by calling :- - -int regulator_set_platform_constraints(const char *regulator, - struct regulation_constraints *constraints); - -The example is defined as follows :- - -struct regulation_constraints domain_1 = { - .min_uV = 3300000, - .max_uV = 3300000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, +supply (Regulator-2). The supply regulator is set by the supply_regulator_dev +field below:- + +static struct regulator_init_data regulator2_data = { + .supply_regulator_dev = &platform_regulator1_device.dev, + .constraints = { + .min_uV = 1800000, + .max_uV = 2000000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + }, + .num_consumer_supplies = ARRAY_SIZE(regulator2_consumers), + .consumer_supplies = regulator2_consumers, }; -struct regulation_constraints domain_2 = { - .min_uV = 1800000, - .max_uV = 2000000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .valid_modes_mask = REGULATOR_MODE_NORMAL, +Finally the regulator devices must be registered in the usual manner. + +static struct platform_device regulator_devices[] = { +{ + .name = "regulator", + .id = DCDC_1, + .dev = { + .platform_data = ®ulator1_data, + }, +}, +{ + .name = "regulator", + .id = DCDC_2, + .dev = { + .platform_data = ®ulator2_data, + }, +}, }; +/* register regulator 1 device */ +platform_device_register(&wm8350_regulator_devices[0]); -regulator_set_platform_constraints("Regulator-1", &domain_1); -regulator_set_platform_constraints("Regulator-2", &domain_2); +/* register regulator 2 device */ +platform_device_register(&wm8350_regulator_devices[1]); diff --git a/Documentation/power/regulator/regulator.txt b/Documentation/power/regulator/regulator.txt index a69050143592..4200accb9bba 100644 --- a/Documentation/power/regulator/regulator.txt +++ b/Documentation/power/regulator/regulator.txt @@ -10,11 +10,11 @@ Registration Drivers can register a regulator by calling :- -struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - void *reg_data); +struct regulator_dev *regulator_register(struct device *dev, + struct regulator_desc *regulator_desc); -This will register the regulators capabilities and operations the regulator -core. The core does not touch reg_data (private to regulator driver). +This will register the regulators capabilities and operations to the regulator +core. Regulators can be unregistered by calling :- diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index 263699d6152d..366565aba865 100644 --- a/drivers/regulator/bq24022.c +++ b/drivers/regulator/bq24022.c @@ -18,13 +18,13 @@ #include #include + static int bq24022_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - dev_dbg(&pdev->dev, "setting current limit to %s mA\n", + dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n", max_uA >= 500000 ? "500" : "100"); /* REVISIT: maybe return error if min_uA != 0 ? */ @@ -34,18 +34,16 @@ static int bq24022_set_current_limit(struct regulator_dev *rdev, static int bq24022_get_current_limit(struct regulator_dev *rdev) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000; } static int bq24022_enable(struct regulator_dev *rdev) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - dev_dbg(&pdev->dev, "enabling charger\n"); + dev_dbg(rdev_get_dev(rdev), "enabling charger\n"); gpio_set_value(pdata->gpio_nce, 0); return 0; @@ -53,10 +51,9 @@ static int bq24022_enable(struct regulator_dev *rdev) static int bq24022_disable(struct regulator_dev *rdev) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - dev_dbg(&pdev->dev, "disabling charger\n"); + dev_dbg(rdev_get_dev(rdev), "disabling charger\n"); gpio_set_value(pdata->gpio_nce, 1); return 0; @@ -108,7 +105,7 @@ static int __init bq24022_probe(struct platform_device *pdev) ret = gpio_direction_output(pdata->gpio_iset2, 0); ret = gpio_direction_output(pdata->gpio_nce, 1); - bq24022 = regulator_register(&bq24022_desc, pdev); + bq24022 = regulator_register(&bq24022_desc, &pdev->dev, pdata); if (IS_ERR(bq24022)) { dev_dbg(&pdev->dev, "couldn't register regulator\n"); ret = PTR_ERR(bq24022); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9c7986261568..84202eaace57 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2,8 +2,9 @@ * core.c -- Voltage/Current Regulator framework. * * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * Copyright 2008 SlimLogic Ltd. * - * Author: Liam Girdwood + * Author: Liam Girdwood * * 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 @@ -64,14 +65,9 @@ struct regulator_map { struct list_head list; struct device *dev; const char *supply; - const char *regulator; + struct regulator_dev *regulator; }; -static inline struct regulator_dev *to_rdev(struct device *d) -{ - return container_of(d, struct regulator_dev, dev); -} - /* * struct regulator * @@ -227,7 +223,7 @@ static ssize_t device_requested_uA_show(struct device *dev, static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); ssize_t ret; mutex_lock(&rdev->mutex); @@ -240,7 +236,7 @@ static ssize_t regulator_uV_show(struct device *dev, static ssize_t regulator_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev)); } @@ -248,7 +244,7 @@ static ssize_t regulator_uA_show(struct device *dev, static ssize_t regulator_opmode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); int mode = _regulator_get_mode(rdev); switch (mode) { @@ -267,7 +263,7 @@ static ssize_t regulator_opmode_show(struct device *dev, static ssize_t regulator_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); int state = _regulator_is_enabled(rdev); if (state > 0) @@ -281,7 +277,7 @@ static ssize_t regulator_state_show(struct device *dev, static ssize_t regulator_min_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -292,7 +288,7 @@ static ssize_t regulator_min_uA_show(struct device *dev, static ssize_t regulator_max_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -303,7 +299,7 @@ static ssize_t regulator_max_uA_show(struct device *dev, static ssize_t regulator_min_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -314,7 +310,7 @@ static ssize_t regulator_min_uV_show(struct device *dev, static ssize_t regulator_max_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -325,7 +321,7 @@ static ssize_t regulator_max_uV_show(struct device *dev, static ssize_t regulator_total_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); struct regulator *regulator; int uA = 0; @@ -339,14 +335,14 @@ static ssize_t regulator_total_uA_show(struct device *dev, static ssize_t regulator_num_users_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); return sprintf(buf, "%d\n", rdev->use_count); } static ssize_t regulator_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); switch (rdev->desc->type) { case REGULATOR_VOLTAGE: @@ -360,7 +356,7 @@ static ssize_t regulator_type_show(struct device *dev, static ssize_t regulator_suspend_mem_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -370,7 +366,7 @@ static ssize_t regulator_suspend_mem_uV_show(struct device *dev, static ssize_t regulator_suspend_disk_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -380,7 +376,7 @@ static ssize_t regulator_suspend_disk_uV_show(struct device *dev, static ssize_t regulator_suspend_standby_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -406,7 +402,7 @@ static ssize_t suspend_opmode_show(struct regulator_dev *rdev, static ssize_t regulator_suspend_mem_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -417,7 +413,7 @@ static ssize_t regulator_suspend_mem_mode_show(struct device *dev, static ssize_t regulator_suspend_disk_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -428,7 +424,7 @@ static ssize_t regulator_suspend_disk_mode_show(struct device *dev, static ssize_t regulator_suspend_standby_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -439,7 +435,7 @@ static ssize_t regulator_suspend_standby_mode_show(struct device *dev, static ssize_t regulator_suspend_mem_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -453,7 +449,7 @@ static ssize_t regulator_suspend_mem_state_show(struct device *dev, static ssize_t regulator_suspend_disk_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -467,7 +463,7 @@ static ssize_t regulator_suspend_disk_state_show(struct device *dev, static ssize_t regulator_suspend_standby_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -512,7 +508,7 @@ static struct device_attribute regulator_dev_attrs[] = { static void regulator_dev_release(struct device *dev) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); kfree(rdev); } @@ -569,8 +565,11 @@ static int suspend_set_state(struct regulator_dev *rdev, /* enable & disable are mandatory for suspend control */ if (!rdev->desc->ops->set_suspend_enable || - !rdev->desc->ops->set_suspend_disable) + !rdev->desc->ops->set_suspend_disable) { + printk(KERN_ERR "%s: no way to set suspend state\n", + __func__); return -EINVAL; + } if (rstate->enabled) ret = rdev->desc->ops->set_suspend_enable(rdev); @@ -656,6 +655,125 @@ static void print_constraints(struct regulator_dev *rdev) printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf); } +/** + * set_machine_constraints - sets regulator constraints + * @regulator: regulator source + * + * Allows platform initialisation code to define and constrain + * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: + * Constraints *must* be set by platform code in order for some + * regulator operations to proceed i.e. set_voltage, set_current_limit, + * set_mode. + */ +static int set_machine_constraints(struct regulator_dev *rdev, + struct regulation_constraints *constraints) +{ + int ret = 0; + + rdev->constraints = constraints; + + /* do we need to apply the constraint voltage */ + if (rdev->constraints->apply_uV && + rdev->constraints->min_uV == rdev->constraints->max_uV && + rdev->desc->ops->set_voltage) { + ret = rdev->desc->ops->set_voltage(rdev, + rdev->constraints->min_uV, rdev->constraints->max_uV); + if (ret < 0) { + printk(KERN_ERR "%s: failed to apply %duV" + " constraint\n", __func__, + rdev->constraints->min_uV); + rdev->constraints = NULL; + goto out; + } + } + + /* are we enabled at boot time by firmware / bootloader */ + if (rdev->constraints->boot_on) + rdev->use_count = 1; + + /* do we need to setup our suspend state */ + if (constraints->initial_state) + ret = suspend_prepare(rdev, constraints->initial_state); + + print_constraints(rdev); +out: + return ret; +} + +/** + * set_supply - set regulator supply regulator + * @regulator: regulator name + * @supply: supply regulator name + * + * Called by platform initialisation code to set the supply regulator for this + * regulator. This ensures that a regulators supply will also be enabled by the + * core if it's child is enabled. + */ +static int set_supply(struct regulator_dev *rdev, + struct regulator_dev *supply_rdev) +{ + int err; + + err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, + "supply"); + if (err) { + printk(KERN_ERR + "%s: could not add device link %s err %d\n", + __func__, supply_rdev->dev.kobj.name, err); + goto out; + } + rdev->supply = supply_rdev; + list_add(&rdev->slist, &supply_rdev->supply_list); +out: + return err; +} + +/** + * set_consumer_device_supply: Bind a regulator to a symbolic supply + * @regulator: regulator source + * @dev: device the supply applies to + * @supply: symbolic name for supply + * + * Allows platform initialisation code to map physical regulator + * sources to symbolic names for supplies for use by devices. Devices + * should use these symbolic names to request regulators, avoiding the + * need to provide board-specific regulator names as platform data. + */ +static int set_consumer_device_supply(struct regulator_dev *rdev, + struct device *consumer_dev, const char *supply) +{ + struct regulator_map *node; + + if (supply == NULL) + return -EINVAL; + + node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + + node->regulator = rdev; + node->dev = consumer_dev; + node->supply = supply; + + list_add(&node->list, ®ulator_map_list); + return 0; +} + +static void unset_consumer_device_supply(struct regulator_dev *rdev, + struct device *consumer_dev) +{ + struct regulator_map *node, *n; + + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { + if (rdev == node->regulator && + consumer_dev == node->dev) { + list_del(&node->list); + kfree(node); + return; + } + } +} + #define REG_STR_SIZE 32 static struct regulator *create_regulator(struct regulator_dev *rdev, @@ -746,7 +864,6 @@ struct regulator *regulator_get(struct device *dev, const char *id) struct regulator_dev *rdev; struct regulator_map *map; struct regulator *regulator = ERR_PTR(-ENODEV); - const char *supply = id; if (id == NULL) { printk(KERN_ERR "regulator: get() with no identifier\n"); @@ -758,15 +875,9 @@ struct regulator *regulator_get(struct device *dev, const char *id) list_for_each_entry(map, ®ulator_map_list, list) { if (dev == map->dev && strcmp(map->supply, id) == 0) { - supply = map->regulator; - break; - } - } - - list_for_each_entry(rdev, ®ulator_list, list) { - if (strcmp(supply, rdev->desc->name) == 0 && - try_module_get(rdev->owner)) + rdev = map->regulator; goto found; + } } printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n", id); @@ -774,12 +885,16 @@ struct regulator *regulator_get(struct device *dev, const char *id) return regulator; found: + if (!try_module_get(rdev->owner)) + goto out; + regulator = create_regulator(rdev, dev, id); if (regulator == NULL) { regulator = ERR_PTR(-ENOMEM); module_put(rdev->owner); } +out: mutex_unlock(®ulator_list_mutex); return regulator; } @@ -1559,11 +1674,12 @@ EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); * Returns 0 on success. */ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - void *reg_data) + struct device *dev, void *driver_data) { static atomic_t regulator_no = ATOMIC_INIT(0); struct regulator_dev *rdev; - int ret; + struct regulator_init_data *init_data = dev->platform_data; + int ret, i; if (regulator_desc == NULL) return ERR_PTR(-EINVAL); @@ -1582,7 +1698,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, mutex_lock(®ulator_list_mutex); mutex_init(&rdev->mutex); - rdev->reg_data = reg_data; + rdev->reg_data = driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; INIT_LIST_HEAD(&rdev->consumer_list); @@ -1591,20 +1707,68 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, INIT_LIST_HEAD(&rdev->slist); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); + /* preform any regulator specific init */ + if (init_data->regulator_init) { + ret = init_data->regulator_init(rdev->reg_data); + if (ret < 0) { + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } + } + + /* set regulator constraints */ + ret = set_machine_constraints(rdev, &init_data->constraints); + if (ret < 0) { + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } + + /* register with sysfs */ rdev->dev.class = ®ulator_class; - device_initialize(&rdev->dev); + rdev->dev.parent = dev; snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id), - "regulator_%ld_%s", - (unsigned long)atomic_inc_return(®ulator_no) - 1, - regulator_desc->name); - - ret = device_add(&rdev->dev); - if (ret == 0) - list_add(&rdev->list, ®ulator_list); - else { + "regulator.%d", atomic_inc_return(®ulator_no) - 1); + ret = device_register(&rdev->dev); + if (ret != 0) { kfree(rdev); rdev = ERR_PTR(ret); + goto out; + } + + dev_set_drvdata(&rdev->dev, rdev); + + /* set supply regulator if it exists */ + if (init_data->supply_regulator_dev) { + ret = set_supply(rdev, + dev_get_drvdata(init_data->supply_regulator_dev)); + if (ret < 0) { + device_unregister(&rdev->dev); + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } + } + + /* add consumers devices */ + for (i = 0; i < init_data->num_consumer_supplies; i++) { + ret = set_consumer_device_supply(rdev, + init_data->consumer_supplies[i].dev, + init_data->consumer_supplies[i].supply); + if (ret < 0) { + for (--i; i >= 0; i--) + unset_consumer_device_supply(rdev, + init_data->consumer_supplies[i].dev); + device_unregister(&rdev->dev); + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } } + + list_add(&rdev->list, ®ulator_list); +out: mutex_unlock(®ulator_list_mutex); return rdev; } @@ -1630,187 +1794,6 @@ void regulator_unregister(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(regulator_unregister); -/** - * regulator_set_supply - set regulator supply regulator - * @regulator: regulator name - * @supply: supply regulator name - * - * Called by platform initialisation code to set the supply regulator for this - * regulator. This ensures that a regulators supply will also be enabled by the - * core if it's child is enabled. - */ -int regulator_set_supply(const char *regulator, const char *supply) -{ - struct regulator_dev *rdev, *supply_rdev; - int err; - - if (regulator == NULL || supply == NULL) - return -EINVAL; - - mutex_lock(®ulator_list_mutex); - - list_for_each_entry(rdev, ®ulator_list, list) { - if (!strcmp(rdev->desc->name, regulator)) - goto found_regulator; - } - mutex_unlock(®ulator_list_mutex); - return -ENODEV; - -found_regulator: - list_for_each_entry(supply_rdev, ®ulator_list, list) { - if (!strcmp(supply_rdev->desc->name, supply)) - goto found_supply; - } - mutex_unlock(®ulator_list_mutex); - return -ENODEV; - -found_supply: - err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, - "supply"); - if (err) { - printk(KERN_ERR - "%s: could not add device link %s err %d\n", - __func__, supply_rdev->dev.kobj.name, err); - goto out; - } - rdev->supply = supply_rdev; - list_add(&rdev->slist, &supply_rdev->supply_list); -out: - mutex_unlock(®ulator_list_mutex); - return err; -} -EXPORT_SYMBOL_GPL(regulator_set_supply); - -/** - * regulator_get_supply - get regulator supply regulator - * @regulator: regulator name - * - * Returns the supply supply regulator name or NULL if no supply regulator - * exists (i.e the regulator is supplied directly from USB, Line, Battery, etc) - */ -const char *regulator_get_supply(const char *regulator) -{ - struct regulator_dev *rdev; - - if (regulator == NULL) - return NULL; - - mutex_lock(®ulator_list_mutex); - list_for_each_entry(rdev, ®ulator_list, list) { - if (!strcmp(rdev->desc->name, regulator)) - goto found; - } - mutex_unlock(®ulator_list_mutex); - return NULL; - -found: - mutex_unlock(®ulator_list_mutex); - if (rdev->supply) - return rdev->supply->desc->name; - else - return NULL; -} -EXPORT_SYMBOL_GPL(regulator_get_supply); - -/** - * regulator_set_machine_constraints - sets regulator constraints - * @regulator: regulator source - * - * Allows platform initialisation code to define and constrain - * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: - * Constraints *must* be set by platform code in order for some - * regulator operations to proceed i.e. set_voltage, set_current_limit, - * set_mode. - */ -int regulator_set_machine_constraints(const char *regulator_name, - struct regulation_constraints *constraints) -{ - struct regulator_dev *rdev; - int ret = 0; - - if (regulator_name == NULL) - return -EINVAL; - - mutex_lock(®ulator_list_mutex); - - list_for_each_entry(rdev, ®ulator_list, list) { - if (!strcmp(regulator_name, rdev->desc->name)) - goto found; - } - ret = -ENODEV; - goto out; - -found: - mutex_lock(&rdev->mutex); - rdev->constraints = constraints; - - /* do we need to apply the constraint voltage */ - if (rdev->constraints->apply_uV && - rdev->constraints->min_uV == rdev->constraints->max_uV && - rdev->desc->ops->set_voltage) { - ret = rdev->desc->ops->set_voltage(rdev, - rdev->constraints->min_uV, rdev->constraints->max_uV); - if (ret < 0) { - printk(KERN_ERR "%s: failed to apply %duV" - " constraint\n", __func__, - rdev->constraints->min_uV); - rdev->constraints = NULL; - goto out; - } - } - - /* are we enabled at boot time by firmware / bootloader */ - if (rdev->constraints->boot_on) - rdev->use_count = 1; - - /* do we need to setup our suspend state */ - if (constraints->initial_state) - ret = suspend_prepare(rdev, constraints->initial_state); - - print_constraints(rdev); - mutex_unlock(&rdev->mutex); - -out: - mutex_unlock(®ulator_list_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(regulator_set_machine_constraints); - - -/** - * regulator_set_device_supply: Bind a regulator to a symbolic supply - * @regulator: regulator source - * @dev: device the supply applies to - * @supply: symbolic name for supply - * - * Allows platform initialisation code to map physical regulator - * sources to symbolic names for supplies for use by devices. Devices - * should use these symbolic names to request regulators, avoiding the - * need to provide board-specific regulator names as platform data. - */ -int regulator_set_device_supply(const char *regulator, struct device *dev, - const char *supply) -{ - struct regulator_map *node; - - if (regulator == NULL || supply == NULL) - return -EINVAL; - - node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); - if (node == NULL) - return -ENOMEM; - - node->regulator = regulator; - node->dev = dev; - node->supply = supply; - - mutex_lock(®ulator_list_mutex); - list_add(&node->list, ®ulator_map_list); - mutex_unlock(®ulator_list_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(regulator_set_device_supply); - /** * regulator_suspend_prepare: prepare regulators for system wide suspend * @state: system suspend state @@ -1893,6 +1876,18 @@ int rdev_get_id(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(rdev_get_id); +struct device *rdev_get_dev(struct regulator_dev *rdev) +{ + return &rdev->dev; +} +EXPORT_SYMBOL_GPL(rdev_get_dev); + +void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data) +{ + return reg_init_data->driver_data; +} +EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); + static int __init regulator_init(void) { printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 1d712c7172a2..bc01b42a8583 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -18,8 +18,8 @@ #include #include -struct regulator_constraints; struct regulator_dev; +struct regulator_init_data; /** * struct regulator_ops - regulator operations. @@ -85,15 +85,17 @@ struct regulator_desc { struct module *owner; }; - struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - void *reg_data); + struct device *dev, void *driver_data); void regulator_unregister(struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); void *rdev_get_drvdata(struct regulator_dev *rdev); +struct device *rdev_get_dev(struct regulator_dev *rdev); int rdev_get_id(struct regulator_dev *rdev); +void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); + #endif diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 11e737dbfcf2..c6d69331a81e 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -89,15 +89,33 @@ struct regulation_constraints { unsigned apply_uV:1; /* apply uV constraint iff min == max */ }; -int regulator_set_supply(const char *regulator, const char *regulator_supply); +/** + * struct regulator_consumer_supply - supply -> device mapping + * + * This maps a supply name to a device. + */ +struct regulator_consumer_supply { + struct device *dev; /* consumer */ + const char *supply; /* consumer supply - e.g. "vcc" */ +}; -const char *regulator_get_supply(const char *regulator); +/** + * struct regulator_init_data - regulator platform initialisation data. + * + * Initialisation constraints, our supply and consumers supplies. + */ +struct regulator_init_data { + struct device *supply_regulator_dev; /* or NULL for LINE */ -int regulator_set_machine_constraints(const char *regulator, - struct regulation_constraints *constraints); + struct regulation_constraints constraints; -int regulator_set_device_supply(const char *regulator, struct device *dev, - const char *supply); + int num_consumer_supplies; + struct regulator_consumer_supply *consumer_supplies; + + /* optional regulator machine specific init */ + int (*regulator_init)(void *driver_data); + void *driver_data; /* core does not touch this */ +}; int regulator_suspend_prepare(suspend_state_t state); -- cgit v1.2.3 From 3de89609a82aa68f543cba263eb28725e0fde511 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Sep 2008 16:21:17 +0100 Subject: regulator: Fix typo Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- include/linux/regulator/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index bc01b42a8583..e37d80561985 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -51,7 +51,7 @@ struct regulator_ops { int output_uV, int load_uA); /* the operations below are for configuration of regulator state when - * it's parent PMIC enters a global STANBY/HIBERNATE state */ + * its parent PMIC enters a global STANDBY/HIBERNATE state */ /* set regulator suspend voltage */ int (*set_suspend_voltage) (struct regulator_dev *, int uV); -- cgit v1.2.3 From 1d9f9f040035da73d6ee5d2b3b3a25483a980da3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Sep 2008 18:58:42 +0100 Subject: mfd: Core support for the WM8400 AudioPlus HiFi CODEC and PMU The WM8400 is a highly integrated audio CODEC and power management unit optimised for use in mobile multimedia applications. This patch adds core support for the WM8400 to the MFD subsystem. Both I2C and SPI access are supported by the hardware but currently only I2C access is implemented. The code is structured to allow SPI support to be slotted in later. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 2 + drivers/mfd/wm8400-core.c | 455 ++++++++++++++ include/linux/mfd/wm8400-audio.h | 1186 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8400-private.h | 935 ++++++++++++++++++++++++++++ 5 files changed, 2586 insertions(+) create mode 100644 drivers/mfd/wm8400-core.c create mode 100644 include/linux/mfd/wm8400-audio.h create mode 100644 include/linux/mfd/wm8400-private.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0dae245c6259..a3ddf6581ea6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -87,6 +87,14 @@ config MFD_TC6393XB help Support for Toshiba Mobile IO Controller TC6393XB +config MFD_WM8400 + tristate "Support Wolfson Microelectronics WM8400" + help + Support for the Wolfson Microelecronics WM8400 PMIC and audio + CODEC. This driver adds provides common support for accessing + the device, additional drivers must be enabled in order to use + the functionality of the device. + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 6abebe364419..1624c7d87a49 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,6 +12,8 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o +obj-$(CONFIG_MFD_WM8400) += wm8400-core.o + obj-$(CONFIG_MFD_CORE) += mfd-core.o obj-$(CONFIG_MCP) += mcp-core.o diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c new file mode 100644 index 000000000000..6a0cedb5bb8a --- /dev/null +++ b/drivers/mfd/wm8400-core.c @@ -0,0 +1,455 @@ +/* + * Core driver for WM8400. + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +static struct { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ + int is_codec; /* Register controlled by codec reset */ + u16 default_val; /* Value on reset */ +} reg_data[] = { + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */ + { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */ + { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */ + { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */ + { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4 */ + { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5 */ + { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6 */ + { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7 */ + { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8 */ + { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9 */ + { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */ + { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */ + { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */ + { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */ + { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */ + { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */ + { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */ + { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */ + { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */ + { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */ + { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */ + { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */ + { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */ + { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */ + { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */ + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */ + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */ + { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */ + { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */ + { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */ + { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */ + { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */ + { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */ + { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */ + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */ + { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */ + { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */ + { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */ + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */ + { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */ + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */ + { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */ + { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */ + { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */ + { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */ + { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */ + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */ + { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */ + { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */ + { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */ +}; + +static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest) +{ + int i, ret = 0; + + BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache)); + + /* If there are any volatile reads then read back the entire block */ + for (i = reg; i < reg + num_regs; i++) + if (reg_data[i].vol) { + ret = wm8400->read_dev(wm8400->io_data, reg, + num_regs, dest); + if (ret != 0) + return ret; + for (i = 0; i < num_regs; i++) + dest[i] = be16_to_cpu(dest[i]); + + return 0; + } + + /* Otherwise use the cache */ + memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16)); + + return 0; +} + +static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, + u16 *src) +{ + int ret, i; + + BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache)); + + for (i = 0; i < num_regs; i++) { + BUG_ON(!reg_data[reg + i].writable); + wm8400->reg_cache[reg + i] = src[i]; + src[i] = cpu_to_be16(src[i]); + } + + /* Do the actual I/O */ + ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src); + if (ret != 0) + return -EIO; + + return 0; +} + +/** + * wm8400_reg_read - Single register read + * + * @wm8400: Pointer to wm8400 control structure + * @reg: Register to read + * + * @return Read value + */ +u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg) +{ + u16 val; + + mutex_lock(&wm8400->io_lock); + + wm8400_read(wm8400, reg, 1, &val); + + mutex_unlock(&wm8400->io_lock); + + return val; +} +EXPORT_SYMBOL_GPL(wm8400_reg_read); + +int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data) +{ + int ret; + + mutex_lock(&wm8400->io_lock); + + ret = wm8400_read(wm8400, reg, count, data); + + mutex_unlock(&wm8400->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8400_block_read); + +/** + * wm8400_set_bits - Bitmask write + * + * @wm8400: Pointer to wm8400 control structure + * @reg: Register to access + * @mask: Mask of bits to change + * @val: Value to set for masked bits + */ +int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val) +{ + u16 tmp; + int ret; + + mutex_lock(&wm8400->io_lock); + + ret = wm8400_read(wm8400, reg, 1, &tmp); + tmp = (tmp & ~mask) | val; + if (ret == 0) + ret = wm8400_write(wm8400, reg, 1, &tmp); + + mutex_unlock(&wm8400->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8400_set_bits); + +/** + * wm8400_reset_codec_reg_cache - Reset cached codec registers to + * their default values. + */ +void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) +{ + int i; + + mutex_lock(&wm8400->io_lock); + + /* Reset all codec registers to their initial value */ + for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) + if (reg_data[i].is_codec) + wm8400->reg_cache[i] = reg_data[i].default_val; + + mutex_unlock(&wm8400->io_lock); +} +EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); + +/* + * wm8400_init - Generic initialisation + * + * The WM8400 can be configured as either an I2C or SPI device. Probe + * functions for each bus set up the accessors then call into this to + * set up the device itself. + */ +static int wm8400_init(struct wm8400 *wm8400, + struct wm8400_platform_data *pdata) +{ + u16 reg; + int ret, i; + + mutex_init(&wm8400->io_lock); + + wm8400->dev->driver_data = wm8400; + + /* Check that this is actually a WM8400 */ + ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, ®); + if (ret != 0) { + dev_err(wm8400->dev, "Chip ID register read failed\n"); + return -EIO; + } + if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) { + dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", + be16_to_cpu(reg)); + return -ENODEV; + } + + /* We don't know what state the hardware is in and since this + * is a PMIC we can't reset it safely so initialise the register + * cache from the hardware. + */ + ret = wm8400->read_dev(wm8400->io_data, 0, + ARRAY_SIZE(wm8400->reg_cache), + wm8400->reg_cache); + if (ret != 0) { + dev_err(wm8400->dev, "Register cache read failed\n"); + return -EIO; + } + for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) + wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]); + + /* If the codec is in reset use hard coded values */ + if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA)) + for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) + if (reg_data[i].is_codec) + wm8400->reg_cache[i] = reg_data[i].default_val; + + ret = wm8400_read(wm8400, WM8400_ID, 1, ®); + if (ret != 0) { + dev_err(wm8400->dev, "ID register read failed: %d\n", ret); + return ret; + } + reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT; + dev_info(wm8400->dev, "WM8400 revision %x\n", reg); + + if (pdata && pdata->platform_init) { + ret = pdata->platform_init(wm8400->dev); + if (ret != 0) + dev_err(wm8400->dev, "Platform init failed: %d\n", + ret); + } else + dev_warn(wm8400->dev, "No platform initialisation supplied\n"); + + return ret; +} + +static void wm8400_release(struct wm8400 *wm8400) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++) + if (wm8400->regulators[i].name) + platform_device_unregister(&wm8400->regulators[i]); +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest) +{ + struct i2c_client *i2c = io_data; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = count * sizeof(u16); + xfer[1].buf = (u8 *)dest; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src) +{ + struct i2c_client *i2c = io_data; + u8 *msg; + int ret; + + /* We add 1 byte for device register - ideally I2C would gather. */ + msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL); + if (msg == NULL) + return -ENOMEM; + + msg[0] = reg; + memcpy(&msg[1], src, count * sizeof(u16)); + + ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1); + + if (ret == (count * 2) + 1) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + kfree(msg); + + return ret; +} + +static int wm8400_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8400 *wm8400; + int ret; + + wm8400 = kzalloc(sizeof(struct wm8400), GFP_KERNEL); + if (wm8400 == NULL) { + ret = -ENOMEM; + goto err; + } + + wm8400->io_data = i2c; + wm8400->read_dev = wm8400_i2c_read; + wm8400->write_dev = wm8400_i2c_write; + wm8400->dev = &i2c->dev; + i2c_set_clientdata(i2c, wm8400); + + ret = wm8400_init(wm8400, i2c->dev.platform_data); + if (ret != 0) + goto struct_err; + + return 0; + +struct_err: + i2c_set_clientdata(i2c, NULL); + kfree(wm8400); +err: + return ret; +} + +static int wm8400_i2c_remove(struct i2c_client *i2c) +{ + struct wm8400 *wm8400 = i2c_get_clientdata(i2c); + + wm8400_release(wm8400); + i2c_set_clientdata(i2c, NULL); + kfree(wm8400); + + return 0; +} + +static const struct i2c_device_id wm8400_i2c_id[] = { + { "wm8400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id); + +static struct i2c_driver wm8400_i2c_driver = { + .driver = { + .name = "WM8400", + .owner = THIS_MODULE, + }, + .probe = wm8400_i2c_probe, + .remove = wm8400_i2c_remove, + .id_table = wm8400_i2c_id, +}; +#endif + +static int __init wm8400_module_init(void) +{ + int ret = -ENODEV; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8400_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); +#endif + + return ret; +} +module_init(wm8400_module_init); + +static void __exit wm8400_module_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8400_i2c_driver); +#endif +} +module_exit(wm8400_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown "); diff --git a/include/linux/mfd/wm8400-audio.h b/include/linux/mfd/wm8400-audio.h new file mode 100644 index 000000000000..b6640e018046 --- /dev/null +++ b/include/linux/mfd/wm8400-audio.h @@ -0,0 +1,1186 @@ +/* + * wm8400 private definitions for audio + * + * Copyright 2008 Wolfson Microelectronics plc + * + * 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. + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_MFD_WM8400_AUDIO_H +#define __LINUX_MFD_WM8400_AUDIO_H + +#include + +/* + * R2 (0x02) - Power Management (1) + */ +#define WM8400_CODEC_ENA 0x8000 /* CODEC_ENA */ +#define WM8400_CODEC_ENA_MASK 0x8000 /* CODEC_ENA */ +#define WM8400_CODEC_ENA_SHIFT 15 /* CODEC_ENA */ +#define WM8400_CODEC_ENA_WIDTH 1 /* CODEC_ENA */ +#define WM8400_SYSCLK_ENA 0x4000 /* SYSCLK_ENA */ +#define WM8400_SYSCLK_ENA_MASK 0x4000 /* SYSCLK_ENA */ +#define WM8400_SYSCLK_ENA_SHIFT 14 /* SYSCLK_ENA */ +#define WM8400_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define WM8400_SPK_MIX_ENA 0x2000 /* SPK_MIX_ENA */ +#define WM8400_SPK_MIX_ENA_MASK 0x2000 /* SPK_MIX_ENA */ +#define WM8400_SPK_MIX_ENA_SHIFT 13 /* SPK_MIX_ENA */ +#define WM8400_SPK_MIX_ENA_WIDTH 1 /* SPK_MIX_ENA */ +#define WM8400_SPK_ENA 0x1000 /* SPK_ENA */ +#define WM8400_SPK_ENA_MASK 0x1000 /* SPK_ENA */ +#define WM8400_SPK_ENA_SHIFT 12 /* SPK_ENA */ +#define WM8400_SPK_ENA_WIDTH 1 /* SPK_ENA */ +#define WM8400_OUT3_ENA 0x0800 /* OUT3_ENA */ +#define WM8400_OUT3_ENA_MASK 0x0800 /* OUT3_ENA */ +#define WM8400_OUT3_ENA_SHIFT 11 /* OUT3_ENA */ +#define WM8400_OUT3_ENA_WIDTH 1 /* OUT3_ENA */ +#define WM8400_OUT4_ENA 0x0400 /* OUT4_ENA */ +#define WM8400_OUT4_ENA_MASK 0x0400 /* OUT4_ENA */ +#define WM8400_OUT4_ENA_SHIFT 10 /* OUT4_ENA */ +#define WM8400_OUT4_ENA_WIDTH 1 /* OUT4_ENA */ +#define WM8400_LOUT_ENA 0x0200 /* LOUT_ENA */ +#define WM8400_LOUT_ENA_MASK 0x0200 /* LOUT_ENA */ +#define WM8400_LOUT_ENA_SHIFT 9 /* LOUT_ENA */ +#define WM8400_LOUT_ENA_WIDTH 1 /* LOUT_ENA */ +#define WM8400_ROUT_ENA 0x0100 /* ROUT_ENA */ +#define WM8400_ROUT_ENA_MASK 0x0100 /* ROUT_ENA */ +#define WM8400_ROUT_ENA_SHIFT 8 /* ROUT_ENA */ +#define WM8400_ROUT_ENA_WIDTH 1 /* ROUT_ENA */ +#define WM8400_MIC1BIAS_ENA 0x0010 /* MIC1BIAS_ENA */ +#define WM8400_MIC1BIAS_ENA_MASK 0x0010 /* MIC1BIAS_ENA */ +#define WM8400_MIC1BIAS_ENA_SHIFT 4 /* MIC1BIAS_ENA */ +#define WM8400_MIC1BIAS_ENA_WIDTH 1 /* MIC1BIAS_ENA */ +#define WM8400_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */ +#define WM8400_VMID_MODE_SHIFT 1 /* VMID_MODE - [2:1] */ +#define WM8400_VMID_MODE_WIDTH 2 /* VMID_MODE - [2:1] */ +#define WM8400_VREF_ENA 0x0001 /* VREF_ENA */ +#define WM8400_VREF_ENA_MASK 0x0001 /* VREF_ENA */ +#define WM8400_VREF_ENA_SHIFT 0 /* VREF_ENA */ +#define WM8400_VREF_ENA_WIDTH 1 /* VREF_ENA */ + +/* + * R3 (0x03) - Power Management (2) + */ +#define WM8400_FLL_ENA 0x8000 /* FLL_ENA */ +#define WM8400_FLL_ENA_MASK 0x8000 /* FLL_ENA */ +#define WM8400_FLL_ENA_SHIFT 15 /* FLL_ENA */ +#define WM8400_FLL_ENA_WIDTH 1 /* FLL_ENA */ +#define WM8400_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8400_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */ +#define WM8400_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */ +#define WM8400_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */ +#define WM8400_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8400_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */ +#define WM8400_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */ +#define WM8400_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */ +#define WM8400_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8400_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8400_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8400_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8400_AINL_ENA 0x0200 /* AINL_ENA */ +#define WM8400_AINL_ENA_MASK 0x0200 /* AINL_ENA */ +#define WM8400_AINL_ENA_SHIFT 9 /* AINL_ENA */ +#define WM8400_AINL_ENA_WIDTH 1 /* AINL_ENA */ +#define WM8400_AINR_ENA 0x0100 /* AINR_ENA */ +#define WM8400_AINR_ENA_MASK 0x0100 /* AINR_ENA */ +#define WM8400_AINR_ENA_SHIFT 8 /* AINR_ENA */ +#define WM8400_AINR_ENA_WIDTH 1 /* AINR_ENA */ +#define WM8400_LIN34_ENA 0x0080 /* LIN34_ENA */ +#define WM8400_LIN34_ENA_MASK 0x0080 /* LIN34_ENA */ +#define WM8400_LIN34_ENA_SHIFT 7 /* LIN34_ENA */ +#define WM8400_LIN34_ENA_WIDTH 1 /* LIN34_ENA */ +#define WM8400_LIN12_ENA 0x0040 /* LIN12_ENA */ +#define WM8400_LIN12_ENA_MASK 0x0040 /* LIN12_ENA */ +#define WM8400_LIN12_ENA_SHIFT 6 /* LIN12_ENA */ +#define WM8400_LIN12_ENA_WIDTH 1 /* LIN12_ENA */ +#define WM8400_RIN34_ENA 0x0020 /* RIN34_ENA */ +#define WM8400_RIN34_ENA_MASK 0x0020 /* RIN34_ENA */ +#define WM8400_RIN34_ENA_SHIFT 5 /* RIN34_ENA */ +#define WM8400_RIN34_ENA_WIDTH 1 /* RIN34_ENA */ +#define WM8400_RIN12_ENA 0x0010 /* RIN12_ENA */ +#define WM8400_RIN12_ENA_MASK 0x0010 /* RIN12_ENA */ +#define WM8400_RIN12_ENA_SHIFT 4 /* RIN12_ENA */ +#define WM8400_RIN12_ENA_WIDTH 1 /* RIN12_ENA */ +#define WM8400_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8400_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8400_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8400_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8400_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8400_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8400_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8400_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R4 (0x04) - Power Management (3) + */ +#define WM8400_LON_ENA 0x2000 /* LON_ENA */ +#define WM8400_LON_ENA_MASK 0x2000 /* LON_ENA */ +#define WM8400_LON_ENA_SHIFT 13 /* LON_ENA */ +#define WM8400_LON_ENA_WIDTH 1 /* LON_ENA */ +#define WM8400_LOP_ENA 0x1000 /* LOP_ENA */ +#define WM8400_LOP_ENA_MASK 0x1000 /* LOP_ENA */ +#define WM8400_LOP_ENA_SHIFT 12 /* LOP_ENA */ +#define WM8400_LOP_ENA_WIDTH 1 /* LOP_ENA */ +#define WM8400_RON_ENA 0x0800 /* RON_ENA */ +#define WM8400_RON_ENA_MASK 0x0800 /* RON_ENA */ +#define WM8400_RON_ENA_SHIFT 11 /* RON_ENA */ +#define WM8400_RON_ENA_WIDTH 1 /* RON_ENA */ +#define WM8400_ROP_ENA 0x0400 /* ROP_ENA */ +#define WM8400_ROP_ENA_MASK 0x0400 /* ROP_ENA */ +#define WM8400_ROP_ENA_SHIFT 10 /* ROP_ENA */ +#define WM8400_ROP_ENA_WIDTH 1 /* ROP_ENA */ +#define WM8400_LOPGA_ENA 0x0080 /* LOPGA_ENA */ +#define WM8400_LOPGA_ENA_MASK 0x0080 /* LOPGA_ENA */ +#define WM8400_LOPGA_ENA_SHIFT 7 /* LOPGA_ENA */ +#define WM8400_LOPGA_ENA_WIDTH 1 /* LOPGA_ENA */ +#define WM8400_ROPGA_ENA 0x0040 /* ROPGA_ENA */ +#define WM8400_ROPGA_ENA_MASK 0x0040 /* ROPGA_ENA */ +#define WM8400_ROPGA_ENA_SHIFT 6 /* ROPGA_ENA */ +#define WM8400_ROPGA_ENA_WIDTH 1 /* ROPGA_ENA */ +#define WM8400_LOMIX_ENA 0x0020 /* LOMIX_ENA */ +#define WM8400_LOMIX_ENA_MASK 0x0020 /* LOMIX_ENA */ +#define WM8400_LOMIX_ENA_SHIFT 5 /* LOMIX_ENA */ +#define WM8400_LOMIX_ENA_WIDTH 1 /* LOMIX_ENA */ +#define WM8400_ROMIX_ENA 0x0010 /* ROMIX_ENA */ +#define WM8400_ROMIX_ENA_MASK 0x0010 /* ROMIX_ENA */ +#define WM8400_ROMIX_ENA_SHIFT 4 /* ROMIX_ENA */ +#define WM8400_ROMIX_ENA_WIDTH 1 /* ROMIX_ENA */ +#define WM8400_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8400_DACL_ENA_MASK 0x0002 /* DACL_ENA */ +#define WM8400_DACL_ENA_SHIFT 1 /* DACL_ENA */ +#define WM8400_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8400_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8400_DACR_ENA_MASK 0x0001 /* DACR_ENA */ +#define WM8400_DACR_ENA_SHIFT 0 /* DACR_ENA */ +#define WM8400_DACR_ENA_WIDTH 1 /* DACR_ENA */ + +/* + * R5 (0x05) - Audio Interface (1) + */ +#define WM8400_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8400_AIFADCL_SRC_MASK 0x8000 /* AIFADCL_SRC */ +#define WM8400_AIFADCL_SRC_SHIFT 15 /* AIFADCL_SRC */ +#define WM8400_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ +#define WM8400_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8400_AIFADCR_SRC_MASK 0x4000 /* AIFADCR_SRC */ +#define WM8400_AIFADCR_SRC_SHIFT 14 /* AIFADCR_SRC */ +#define WM8400_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ +#define WM8400_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_MASK 0x2000 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_SHIFT 13 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8400_AIFADC_TDM_CHAN_MASK 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8400_AIFADC_TDM_CHAN_SHIFT 12 /* AIFADC_TDM_CHAN */ +#define WM8400_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ +#define WM8400_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8400_AIF_BCLK_INV_MASK 0x0100 /* AIF_BCLK_INV */ +#define WM8400_AIF_BCLK_INV_SHIFT 8 /* AIF_BCLK_INV */ +#define WM8400_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM8400_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8400_AIF_LRCLK_INV_MASK 0x0080 /* AIF_LRCLK_INV */ +#define WM8400_AIF_LRCLK_INV_SHIFT 7 /* AIF_LRCLK_INV */ +#define WM8400_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM8400_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8400_AIF_WL_SHIFT 5 /* AIF_WL - [6:5] */ +#define WM8400_AIF_WL_WIDTH 2 /* AIF_WL - [6:5] */ +#define WM8400_AIF_WL_16BITS (0 << 5) +#define WM8400_AIF_WL_20BITS (1 << 5) +#define WM8400_AIF_WL_24BITS (2 << 5) +#define WM8400_AIF_WL_32BITS (3 << 5) +#define WM8400_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8400_AIF_FMT_SHIFT 3 /* AIF_FMT - [4:3] */ +#define WM8400_AIF_FMT_WIDTH 2 /* AIF_FMT - [4:3] */ +#define WM8400_AIF_FMT_RIGHTJ (0 << 3) +#define WM8400_AIF_FMT_LEFTJ (1 << 3) +#define WM8400_AIF_FMT_I2S (2 << 3) +#define WM8400_AIF_FMT_DSP (3 << 3) + +/* + * R6 (0x06) - Audio Interface (2) + */ +#define WM8400_DACL_SRC 0x8000 /* DACL_SRC */ +#define WM8400_DACL_SRC_MASK 0x8000 /* DACL_SRC */ +#define WM8400_DACL_SRC_SHIFT 15 /* DACL_SRC */ +#define WM8400_DACL_SRC_WIDTH 1 /* DACL_SRC */ +#define WM8400_DACR_SRC 0x4000 /* DACR_SRC */ +#define WM8400_DACR_SRC_MASK 0x4000 /* DACR_SRC */ +#define WM8400_DACR_SRC_SHIFT 14 /* DACR_SRC */ +#define WM8400_DACR_SRC_WIDTH 1 /* DACR_SRC */ +#define WM8400_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8400_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8400_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ +#define WM8400_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ +#define WM8400_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8400_DAC_BOOST_SHIFT 10 /* DAC_BOOST - [11:10] */ +#define WM8400_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [11:10] */ +#define WM8400_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8400_DAC_COMP_MASK 0x0010 /* DAC_COMP */ +#define WM8400_DAC_COMP_SHIFT 4 /* DAC_COMP */ +#define WM8400_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8400_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8400_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */ +#define WM8400_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */ +#define WM8400_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ +#define WM8400_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8400_ADC_COMP_MASK 0x0004 /* ADC_COMP */ +#define WM8400_ADC_COMP_SHIFT 2 /* ADC_COMP */ +#define WM8400_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8400_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8400_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */ +#define WM8400_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */ +#define WM8400_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8400_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8400_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8400_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8400_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R7 (0x07) - Clocking (1) + */ +#define WM8400_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8400_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */ +#define WM8400_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */ +#define WM8400_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */ +#define WM8400_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8400_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */ +#define WM8400_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */ +#define WM8400_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8400_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */ +#define WM8400_OPCLKDIV_SHIFT 9 /* OPCLKDIV - [12:9] */ +#define WM8400_OPCLKDIV_WIDTH 4 /* OPCLKDIV - [12:9] */ +#define WM8400_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8400_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */ +#define WM8400_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */ +#define WM8400_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8400_BCLK_DIV_SHIFT 1 /* BCLK_DIV - [4:1] */ +#define WM8400_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [4:1] */ + +/* + * R8 (0x08) - Clocking (2) + */ +#define WM8400_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8400_MCLK_SRC_MASK 0x8000 /* MCLK_SRC */ +#define WM8400_MCLK_SRC_SHIFT 15 /* MCLK_SRC */ +#define WM8400_MCLK_SRC_WIDTH 1 /* MCLK_SRC */ +#define WM8400_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8400_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */ +#define WM8400_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */ +#define WM8400_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ +#define WM8400_CLK_FORCE 0x2000 /* CLK_FORCE */ +#define WM8400_CLK_FORCE_MASK 0x2000 /* CLK_FORCE */ +#define WM8400_CLK_FORCE_SHIFT 13 /* CLK_FORCE */ +#define WM8400_CLK_FORCE_WIDTH 1 /* CLK_FORCE */ +#define WM8400_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */ +#define WM8400_MCLK_DIV_SHIFT 11 /* MCLK_DIV - [12:11] */ +#define WM8400_MCLK_DIV_WIDTH 2 /* MCLK_DIV - [12:11] */ +#define WM8400_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8400_MCLK_INV_MASK 0x0400 /* MCLK_INV */ +#define WM8400_MCLK_INV_SHIFT 10 /* MCLK_INV */ +#define WM8400_MCLK_INV_WIDTH 1 /* MCLK_INV */ +#define WM8400_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */ +#define WM8400_ADC_CLKDIV_SHIFT 5 /* ADC_CLKDIV - [7:5] */ +#define WM8400_ADC_CLKDIV_WIDTH 3 /* ADC_CLKDIV - [7:5] */ +#define WM8400_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */ +#define WM8400_DAC_CLKDIV_SHIFT 2 /* DAC_CLKDIV - [4:2] */ +#define WM8400_DAC_CLKDIV_WIDTH 3 /* DAC_CLKDIV - [4:2] */ + +/* + * R9 (0x09) - Audio Interface (3) + */ +#define WM8400_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR1_MASK 0x8000 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR1_SHIFT 15 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR1_WIDTH 1 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR2 0x4000 /* AIF_MSTR2 */ +#define WM8400_AIF_MSTR2_MASK 0x4000 /* AIF_MSTR2 */ +#define WM8400_AIF_MSTR2_SHIFT 14 /* AIF_MSTR2 */ +#define WM8400_AIF_MSTR2_WIDTH 1 /* AIF_MSTR2 */ +#define WM8400_AIF_SEL 0x2000 /* AIF_SEL */ +#define WM8400_AIF_SEL_MASK 0x2000 /* AIF_SEL */ +#define WM8400_AIF_SEL_SHIFT 13 /* AIF_SEL */ +#define WM8400_AIF_SEL_WIDTH 1 /* AIF_SEL */ +#define WM8400_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_DIR_MASK 0x0800 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_DIR_SHIFT 11 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_DIR_WIDTH 1 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */ +#define WM8400_ADCLRC_RATE_SHIFT 0 /* ADCLRC_RATE - [10:0] */ +#define WM8400_ADCLRC_RATE_WIDTH 11 /* ADCLRC_RATE - [10:0] */ + +/* + * R10 (0x0A) - Audio Interface (4) + */ +#define WM8400_ALRCGPIO1 0x8000 /* ALRCGPIO1 */ +#define WM8400_ALRCGPIO1_MASK 0x8000 /* ALRCGPIO1 */ +#define WM8400_ALRCGPIO1_SHIFT 15 /* ALRCGPIO1 */ +#define WM8400_ALRCGPIO1_WIDTH 1 /* ALRCGPIO1 */ +#define WM8400_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */ +#define WM8400_ALRCBGPIO6_MASK 0x4000 /* ALRCBGPIO6 */ +#define WM8400_ALRCBGPIO6_SHIFT 14 /* ALRCBGPIO6 */ +#define WM8400_ALRCBGPIO6_WIDTH 1 /* ALRCBGPIO6 */ +#define WM8400_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8400_AIF_TRIS_MASK 0x2000 /* AIF_TRIS */ +#define WM8400_AIF_TRIS_SHIFT 13 /* AIF_TRIS */ +#define WM8400_AIF_TRIS_WIDTH 1 /* AIF_TRIS */ +#define WM8400_DACLRC_DIR 0x0800 /* DACLRC_DIR */ +#define WM8400_DACLRC_DIR_MASK 0x0800 /* DACLRC_DIR */ +#define WM8400_DACLRC_DIR_SHIFT 11 /* DACLRC_DIR */ +#define WM8400_DACLRC_DIR_WIDTH 1 /* DACLRC_DIR */ +#define WM8400_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */ +#define WM8400_DACLRC_RATE_SHIFT 0 /* DACLRC_RATE - [10:0] */ +#define WM8400_DACLRC_RATE_WIDTH 11 /* DACLRC_RATE - [10:0] */ + +/* + * R11 (0x0B) - DAC CTRL + */ +#define WM8400_DAC_SDMCLK_RATE 0x2000 /* DAC_SDMCLK_RATE */ +#define WM8400_DAC_SDMCLK_RATE_MASK 0x2000 /* DAC_SDMCLK_RATE */ +#define WM8400_DAC_SDMCLK_RATE_SHIFT 13 /* DAC_SDMCLK_RATE */ +#define WM8400_DAC_SDMCLK_RATE_WIDTH 1 /* DAC_SDMCLK_RATE */ +#define WM8400_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */ +#define WM8400_AIF_LRCLKRATE_MASK 0x0400 /* AIF_LRCLKRATE */ +#define WM8400_AIF_LRCLKRATE_SHIFT 10 /* AIF_LRCLKRATE */ +#define WM8400_AIF_LRCLKRATE_WIDTH 1 /* AIF_LRCLKRATE */ +#define WM8400_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8400_DAC_MONO_MASK 0x0200 /* DAC_MONO */ +#define WM8400_DAC_MONO_SHIFT 9 /* DAC_MONO */ +#define WM8400_DAC_MONO_WIDTH 1 /* DAC_MONO */ +#define WM8400_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8400_DAC_SB_FILT_MASK 0x0100 /* DAC_SB_FILT */ +#define WM8400_DAC_SB_FILT_SHIFT 8 /* DAC_SB_FILT */ +#define WM8400_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ +#define WM8400_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTERATE_MASK 0x0080 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTERATE_SHIFT 7 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */ +#define WM8400_DAC_MUTEMODE_MASK 0x0040 /* DAC_MUTEMODE */ +#define WM8400_DAC_MUTEMODE_SHIFT 6 /* DAC_MUTEMODE */ +#define WM8400_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */ +#define WM8400_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */ +#define WM8400_DEEMP_SHIFT 4 /* DEEMP - [5:4] */ +#define WM8400_DEEMP_WIDTH 2 /* DEEMP - [5:4] */ +#define WM8400_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8400_DAC_MUTE_MASK 0x0004 /* DAC_MUTE */ +#define WM8400_DAC_MUTE_SHIFT 2 /* DAC_MUTE */ +#define WM8400_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8400_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8400_DACL_DATINV_MASK 0x0002 /* DACL_DATINV */ +#define WM8400_DACL_DATINV_SHIFT 1 /* DACL_DATINV */ +#define WM8400_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ +#define WM8400_DACR_DATINV 0x0001 /* DACR_DATINV */ +#define WM8400_DACR_DATINV_MASK 0x0001 /* DACR_DATINV */ +#define WM8400_DACR_DATINV_SHIFT 0 /* DACR_DATINV */ +#define WM8400_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ + +/* + * R12 (0x0C) - Left DAC Digital Volume + */ +#define WM8400_DAC_VU 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8400_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8400_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8400_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8400_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R13 (0x0D) - Right DAC Digital Volume + */ +#define WM8400_DAC_VU 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8400_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8400_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8400_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8400_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R14 (0x0E) - Digital Side Tone + */ +#define WM8400_ADCL_DAC_SVOL_MASK 0x1E00 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8400_ADCL_DAC_SVOL_SHIFT 9 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8400_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8400_ADCR_DAC_SVOL_MASK 0x01E0 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8400_ADCR_DAC_SVOL_SHIFT 5 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8400_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8400_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8400_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8400_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ +#define WM8400_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ +#define WM8400_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ +#define WM8400_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ + +/* + * R15 (0x0F) - ADC CTRL + */ +#define WM8400_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_ENA_MASK 0x0100 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_ENA_SHIFT 8 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_ENA_WIDTH 1 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ +#define WM8400_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ +#define WM8400_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ +#define WM8400_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8400_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ +#define WM8400_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ +#define WM8400_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ +#define WM8400_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8400_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ +#define WM8400_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ +#define WM8400_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ + +/* + * R16 (0x10) - Left ADC Digital Volume + */ +#define WM8400_ADC_VU 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8400_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8400_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8400_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8400_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R17 (0x11) - Right ADC Digital Volume + */ +#define WM8400_ADC_VU 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8400_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8400_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8400_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8400_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_LI12MUTE 0x0080 /* LI12MUTE */ +#define WM8400_LI12MUTE_MASK 0x0080 /* LI12MUTE */ +#define WM8400_LI12MUTE_SHIFT 7 /* LI12MUTE */ +#define WM8400_LI12MUTE_WIDTH 1 /* LI12MUTE */ +#define WM8400_LI12ZC 0x0040 /* LI12ZC */ +#define WM8400_LI12ZC_MASK 0x0040 /* LI12ZC */ +#define WM8400_LI12ZC_SHIFT 6 /* LI12ZC */ +#define WM8400_LI12ZC_WIDTH 1 /* LI12ZC */ +#define WM8400_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */ +#define WM8400_LIN12VOL_SHIFT 0 /* LIN12VOL - [4:0] */ +#define WM8400_LIN12VOL_WIDTH 5 /* LIN12VOL - [4:0] */ + +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_LI34MUTE 0x0080 /* LI34MUTE */ +#define WM8400_LI34MUTE_MASK 0x0080 /* LI34MUTE */ +#define WM8400_LI34MUTE_SHIFT 7 /* LI34MUTE */ +#define WM8400_LI34MUTE_WIDTH 1 /* LI34MUTE */ +#define WM8400_LI34ZC 0x0040 /* LI34ZC */ +#define WM8400_LI34ZC_MASK 0x0040 /* LI34ZC */ +#define WM8400_LI34ZC_SHIFT 6 /* LI34ZC */ +#define WM8400_LI34ZC_WIDTH 1 /* LI34ZC */ +#define WM8400_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */ +#define WM8400_LIN34VOL_SHIFT 0 /* LIN34VOL - [4:0] */ +#define WM8400_LIN34VOL_WIDTH 5 /* LIN34VOL - [4:0] */ + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_RI12MUTE 0x0080 /* RI12MUTE */ +#define WM8400_RI12MUTE_MASK 0x0080 /* RI12MUTE */ +#define WM8400_RI12MUTE_SHIFT 7 /* RI12MUTE */ +#define WM8400_RI12MUTE_WIDTH 1 /* RI12MUTE */ +#define WM8400_RI12ZC 0x0040 /* RI12ZC */ +#define WM8400_RI12ZC_MASK 0x0040 /* RI12ZC */ +#define WM8400_RI12ZC_SHIFT 6 /* RI12ZC */ +#define WM8400_RI12ZC_WIDTH 1 /* RI12ZC */ +#define WM8400_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */ +#define WM8400_RIN12VOL_SHIFT 0 /* RIN12VOL - [4:0] */ +#define WM8400_RIN12VOL_WIDTH 5 /* RIN12VOL - [4:0] */ + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_RI34MUTE 0x0080 /* RI34MUTE */ +#define WM8400_RI34MUTE_MASK 0x0080 /* RI34MUTE */ +#define WM8400_RI34MUTE_SHIFT 7 /* RI34MUTE */ +#define WM8400_RI34MUTE_WIDTH 1 /* RI34MUTE */ +#define WM8400_RI34ZC 0x0040 /* RI34ZC */ +#define WM8400_RI34ZC_MASK 0x0040 /* RI34ZC */ +#define WM8400_RI34ZC_SHIFT 6 /* RI34ZC */ +#define WM8400_RI34ZC_WIDTH 1 /* RI34ZC */ +#define WM8400_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */ +#define WM8400_RIN34VOL_SHIFT 0 /* RIN34VOL - [4:0] */ +#define WM8400_RIN34VOL_WIDTH 5 /* RIN34VOL - [4:0] */ + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_LOZC 0x0080 /* LOZC */ +#define WM8400_LOZC_MASK 0x0080 /* LOZC */ +#define WM8400_LOZC_SHIFT 7 /* LOZC */ +#define WM8400_LOZC_WIDTH 1 /* LOZC */ +#define WM8400_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8400_LOUTVOL_SHIFT 0 /* LOUTVOL - [6:0] */ +#define WM8400_LOUTVOL_WIDTH 7 /* LOUTVOL - [6:0] */ + +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_ROZC 0x0080 /* ROZC */ +#define WM8400_ROZC_MASK 0x0080 /* ROZC */ +#define WM8400_ROZC_SHIFT 7 /* ROZC */ +#define WM8400_ROZC_WIDTH 1 /* ROZC */ +#define WM8400_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8400_ROUTVOL_SHIFT 0 /* ROUTVOL - [6:0] */ +#define WM8400_ROUTVOL_WIDTH 7 /* ROUTVOL - [6:0] */ + +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8400_LONMUTE 0x0040 /* LONMUTE */ +#define WM8400_LONMUTE_MASK 0x0040 /* LONMUTE */ +#define WM8400_LONMUTE_SHIFT 6 /* LONMUTE */ +#define WM8400_LONMUTE_WIDTH 1 /* LONMUTE */ +#define WM8400_LOPMUTE 0x0020 /* LOPMUTE */ +#define WM8400_LOPMUTE_MASK 0x0020 /* LOPMUTE */ +#define WM8400_LOPMUTE_SHIFT 5 /* LOPMUTE */ +#define WM8400_LOPMUTE_WIDTH 1 /* LOPMUTE */ +#define WM8400_LOATTN 0x0010 /* LOATTN */ +#define WM8400_LOATTN_MASK 0x0010 /* LOATTN */ +#define WM8400_LOATTN_SHIFT 4 /* LOATTN */ +#define WM8400_LOATTN_WIDTH 1 /* LOATTN */ +#define WM8400_RONMUTE 0x0004 /* RONMUTE */ +#define WM8400_RONMUTE_MASK 0x0004 /* RONMUTE */ +#define WM8400_RONMUTE_SHIFT 2 /* RONMUTE */ +#define WM8400_RONMUTE_WIDTH 1 /* RONMUTE */ +#define WM8400_ROPMUTE 0x0002 /* ROPMUTE */ +#define WM8400_ROPMUTE_MASK 0x0002 /* ROPMUTE */ +#define WM8400_ROPMUTE_SHIFT 1 /* ROPMUTE */ +#define WM8400_ROPMUTE_WIDTH 1 /* ROPMUTE */ +#define WM8400_ROATTN 0x0001 /* ROATTN */ +#define WM8400_ROATTN_MASK 0x0001 /* ROATTN */ +#define WM8400_ROATTN_SHIFT 0 /* ROATTN */ +#define WM8400_ROATTN_WIDTH 1 /* ROATTN */ + +/* + * R31 (0x1F) - Out3/4 Volume + */ +#define WM8400_OUT3MUTE 0x0020 /* OUT3MUTE */ +#define WM8400_OUT3MUTE_MASK 0x0020 /* OUT3MUTE */ +#define WM8400_OUT3MUTE_SHIFT 5 /* OUT3MUTE */ +#define WM8400_OUT3MUTE_WIDTH 1 /* OUT3MUTE */ +#define WM8400_OUT3ATTN 0x0010 /* OUT3ATTN */ +#define WM8400_OUT3ATTN_MASK 0x0010 /* OUT3ATTN */ +#define WM8400_OUT3ATTN_SHIFT 4 /* OUT3ATTN */ +#define WM8400_OUT3ATTN_WIDTH 1 /* OUT3ATTN */ +#define WM8400_OUT4MUTE 0x0002 /* OUT4MUTE */ +#define WM8400_OUT4MUTE_MASK 0x0002 /* OUT4MUTE */ +#define WM8400_OUT4MUTE_SHIFT 1 /* OUT4MUTE */ +#define WM8400_OUT4MUTE_WIDTH 1 /* OUT4MUTE */ +#define WM8400_OUT4ATTN 0x0001 /* OUT4ATTN */ +#define WM8400_OUT4ATTN_MASK 0x0001 /* OUT4ATTN */ +#define WM8400_OUT4ATTN_SHIFT 0 /* OUT4ATTN */ +#define WM8400_OUT4ATTN_WIDTH 1 /* OUT4ATTN */ + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_LOPGAZC 0x0080 /* LOPGAZC */ +#define WM8400_LOPGAZC_MASK 0x0080 /* LOPGAZC */ +#define WM8400_LOPGAZC_SHIFT 7 /* LOPGAZC */ +#define WM8400_LOPGAZC_WIDTH 1 /* LOPGAZC */ +#define WM8400_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */ +#define WM8400_LOPGAVOL_SHIFT 0 /* LOPGAVOL - [6:0] */ +#define WM8400_LOPGAVOL_WIDTH 7 /* LOPGAVOL - [6:0] */ + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_ROPGAZC 0x0080 /* ROPGAZC */ +#define WM8400_ROPGAZC_MASK 0x0080 /* ROPGAZC */ +#define WM8400_ROPGAZC_SHIFT 7 /* ROPGAZC */ +#define WM8400_ROPGAZC_WIDTH 1 /* ROPGAZC */ +#define WM8400_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */ +#define WM8400_ROPGAVOL_SHIFT 0 /* ROPGAVOL - [6:0] */ +#define WM8400_ROPGAVOL_WIDTH 7 /* ROPGAVOL - [6:0] */ + +/* + * R34 (0x22) - Speaker Volume + */ +#define WM8400_SPKATTN_MASK 0x0003 /* SPKATTN - [1:0] */ +#define WM8400_SPKATTN_SHIFT 0 /* SPKATTN - [1:0] */ +#define WM8400_SPKATTN_WIDTH 2 /* SPKATTN - [1:0] */ + +/* + * R35 (0x23) - ClassD1 + */ +#define WM8400_CDMODE 0x0100 /* CDMODE */ +#define WM8400_CDMODE_MASK 0x0100 /* CDMODE */ +#define WM8400_CDMODE_SHIFT 8 /* CDMODE */ +#define WM8400_CDMODE_WIDTH 1 /* CDMODE */ +#define WM8400_CLASSD_CLK_SEL 0x0080 /* CLASSD_CLK_SEL */ +#define WM8400_CLASSD_CLK_SEL_MASK 0x0080 /* CLASSD_CLK_SEL */ +#define WM8400_CLASSD_CLK_SEL_SHIFT 7 /* CLASSD_CLK_SEL */ +#define WM8400_CLASSD_CLK_SEL_WIDTH 1 /* CLASSD_CLK_SEL */ +#define WM8400_CD_SRCTRL 0x0040 /* CD_SRCTRL */ +#define WM8400_CD_SRCTRL_MASK 0x0040 /* CD_SRCTRL */ +#define WM8400_CD_SRCTRL_SHIFT 6 /* CD_SRCTRL */ +#define WM8400_CD_SRCTRL_WIDTH 1 /* CD_SRCTRL */ +#define WM8400_SPKNOPOP 0x0020 /* SPKNOPOP */ +#define WM8400_SPKNOPOP_MASK 0x0020 /* SPKNOPOP */ +#define WM8400_SPKNOPOP_SHIFT 5 /* SPKNOPOP */ +#define WM8400_SPKNOPOP_WIDTH 1 /* SPKNOPOP */ +#define WM8400_DBLERATE 0x0010 /* DBLERATE */ +#define WM8400_DBLERATE_MASK 0x0010 /* DBLERATE */ +#define WM8400_DBLERATE_SHIFT 4 /* DBLERATE */ +#define WM8400_DBLERATE_WIDTH 1 /* DBLERATE */ +#define WM8400_LOOPTEST 0x0008 /* LOOPTEST */ +#define WM8400_LOOPTEST_MASK 0x0008 /* LOOPTEST */ +#define WM8400_LOOPTEST_SHIFT 3 /* LOOPTEST */ +#define WM8400_LOOPTEST_WIDTH 1 /* LOOPTEST */ +#define WM8400_HALFABBIAS 0x0004 /* HALFABBIAS */ +#define WM8400_HALFABBIAS_MASK 0x0004 /* HALFABBIAS */ +#define WM8400_HALFABBIAS_SHIFT 2 /* HALFABBIAS */ +#define WM8400_HALFABBIAS_WIDTH 1 /* HALFABBIAS */ +#define WM8400_TRIDEL_MASK 0x0003 /* TRIDEL - [1:0] */ +#define WM8400_TRIDEL_SHIFT 0 /* TRIDEL - [1:0] */ +#define WM8400_TRIDEL_WIDTH 2 /* TRIDEL - [1:0] */ + +/* + * R37 (0x25) - ClassD3 + */ +#define WM8400_DCGAIN_MASK 0x0038 /* DCGAIN - [5:3] */ +#define WM8400_DCGAIN_SHIFT 3 /* DCGAIN - [5:3] */ +#define WM8400_DCGAIN_WIDTH 3 /* DCGAIN - [5:3] */ +#define WM8400_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */ +#define WM8400_ACGAIN_SHIFT 0 /* ACGAIN - [2:0] */ +#define WM8400_ACGAIN_WIDTH 3 /* ACGAIN - [2:0] */ + +/* + * R39 (0x27) - Input Mixer1 + */ +#define WM8400_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */ +#define WM8400_AINLMODE_SHIFT 2 /* AINLMODE - [3:2] */ +#define WM8400_AINLMODE_WIDTH 2 /* AINLMODE - [3:2] */ +#define WM8400_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */ +#define WM8400_AINRMODE_SHIFT 0 /* AINRMODE - [1:0] */ +#define WM8400_AINRMODE_WIDTH 2 /* AINRMODE - [1:0] */ + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8400_LMP4 0x0080 /* LMP4 */ +#define WM8400_LMP4_MASK 0x0080 /* LMP4 */ +#define WM8400_LMP4_SHIFT 7 /* LMP4 */ +#define WM8400_LMP4_WIDTH 1 /* LMP4 */ +#define WM8400_LMN3 0x0040 /* LMN3 */ +#define WM8400_LMN3_MASK 0x0040 /* LMN3 */ +#define WM8400_LMN3_SHIFT 6 /* LMN3 */ +#define WM8400_LMN3_WIDTH 1 /* LMN3 */ +#define WM8400_LMP2 0x0020 /* LMP2 */ +#define WM8400_LMP2_MASK 0x0020 /* LMP2 */ +#define WM8400_LMP2_SHIFT 5 /* LMP2 */ +#define WM8400_LMP2_WIDTH 1 /* LMP2 */ +#define WM8400_LMN1 0x0010 /* LMN1 */ +#define WM8400_LMN1_MASK 0x0010 /* LMN1 */ +#define WM8400_LMN1_SHIFT 4 /* LMN1 */ +#define WM8400_LMN1_WIDTH 1 /* LMN1 */ +#define WM8400_RMP4 0x0008 /* RMP4 */ +#define WM8400_RMP4_MASK 0x0008 /* RMP4 */ +#define WM8400_RMP4_SHIFT 3 /* RMP4 */ +#define WM8400_RMP4_WIDTH 1 /* RMP4 */ +#define WM8400_RMN3 0x0004 /* RMN3 */ +#define WM8400_RMN3_MASK 0x0004 /* RMN3 */ +#define WM8400_RMN3_SHIFT 2 /* RMN3 */ +#define WM8400_RMN3_WIDTH 1 /* RMN3 */ +#define WM8400_RMP2 0x0002 /* RMP2 */ +#define WM8400_RMP2_MASK 0x0002 /* RMP2 */ +#define WM8400_RMP2_SHIFT 1 /* RMP2 */ +#define WM8400_RMP2_WIDTH 1 /* RMP2 */ +#define WM8400_RMN1 0x0001 /* RMN1 */ +#define WM8400_RMN1_MASK 0x0001 /* RMN1 */ +#define WM8400_RMN1_SHIFT 0 /* RMN1 */ +#define WM8400_RMN1_WIDTH 1 /* RMN1 */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8400_L34MNB 0x0100 /* L34MNB */ +#define WM8400_L34MNB_MASK 0x0100 /* L34MNB */ +#define WM8400_L34MNB_SHIFT 8 /* L34MNB */ +#define WM8400_L34MNB_WIDTH 1 /* L34MNB */ +#define WM8400_L34MNBST 0x0080 /* L34MNBST */ +#define WM8400_L34MNBST_MASK 0x0080 /* L34MNBST */ +#define WM8400_L34MNBST_SHIFT 7 /* L34MNBST */ +#define WM8400_L34MNBST_WIDTH 1 /* L34MNBST */ +#define WM8400_L12MNB 0x0020 /* L12MNB */ +#define WM8400_L12MNB_MASK 0x0020 /* L12MNB */ +#define WM8400_L12MNB_SHIFT 5 /* L12MNB */ +#define WM8400_L12MNB_WIDTH 1 /* L12MNB */ +#define WM8400_L12MNBST 0x0010 /* L12MNBST */ +#define WM8400_L12MNBST_MASK 0x0010 /* L12MNBST */ +#define WM8400_L12MNBST_SHIFT 4 /* L12MNBST */ +#define WM8400_L12MNBST_WIDTH 1 /* L12MNBST */ +#define WM8400_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */ +#define WM8400_LDBVOL_SHIFT 0 /* LDBVOL - [2:0] */ +#define WM8400_LDBVOL_WIDTH 3 /* LDBVOL - [2:0] */ + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8400_R34MNB 0x0100 /* R34MNB */ +#define WM8400_R34MNB_MASK 0x0100 /* R34MNB */ +#define WM8400_R34MNB_SHIFT 8 /* R34MNB */ +#define WM8400_R34MNB_WIDTH 1 /* R34MNB */ +#define WM8400_R34MNBST 0x0080 /* R34MNBST */ +#define WM8400_R34MNBST_MASK 0x0080 /* R34MNBST */ +#define WM8400_R34MNBST_SHIFT 7 /* R34MNBST */ +#define WM8400_R34MNBST_WIDTH 1 /* R34MNBST */ +#define WM8400_R12MNB 0x0020 /* R12MNB */ +#define WM8400_R12MNB_MASK 0x0020 /* R12MNB */ +#define WM8400_R12MNB_SHIFT 5 /* R12MNB */ +#define WM8400_R12MNB_WIDTH 1 /* R12MNB */ +#define WM8400_R12MNBST 0x0010 /* R12MNBST */ +#define WM8400_R12MNBST_MASK 0x0010 /* R12MNBST */ +#define WM8400_R12MNBST_SHIFT 4 /* R12MNBST */ +#define WM8400_R12MNBST_WIDTH 1 /* R12MNBST */ +#define WM8400_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */ +#define WM8400_RDBVOL_SHIFT 0 /* RDBVOL - [2:0] */ +#define WM8400_RDBVOL_WIDTH 3 /* RDBVOL - [2:0] */ + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8400_LI2BVOL_MASK 0x01C0 /* LI2BVOL - [8:6] */ +#define WM8400_LI2BVOL_SHIFT 6 /* LI2BVOL - [8:6] */ +#define WM8400_LI2BVOL_WIDTH 3 /* LI2BVOL - [8:6] */ +#define WM8400_LR4BVOL_MASK 0x0038 /* LR4BVOL - [5:3] */ +#define WM8400_LR4BVOL_SHIFT 3 /* LR4BVOL - [5:3] */ +#define WM8400_LR4BVOL_WIDTH 3 /* LR4BVOL - [5:3] */ +#define WM8400_LL4BVOL_MASK 0x0007 /* LL4BVOL - [2:0] */ +#define WM8400_LL4BVOL_SHIFT 0 /* LL4BVOL - [2:0] */ +#define WM8400_LL4BVOL_WIDTH 3 /* LL4BVOL - [2:0] */ + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8400_RI2BVOL_MASK 0x01C0 /* RI2BVOL - [8:6] */ +#define WM8400_RI2BVOL_SHIFT 6 /* RI2BVOL - [8:6] */ +#define WM8400_RI2BVOL_WIDTH 3 /* RI2BVOL - [8:6] */ +#define WM8400_RL4BVOL_MASK 0x0038 /* RL4BVOL - [5:3] */ +#define WM8400_RL4BVOL_SHIFT 3 /* RL4BVOL - [5:3] */ +#define WM8400_RL4BVOL_WIDTH 3 /* RL4BVOL - [5:3] */ +#define WM8400_RR4BVOL_MASK 0x0007 /* RR4BVOL - [2:0] */ +#define WM8400_RR4BVOL_SHIFT 0 /* RR4BVOL - [2:0] */ +#define WM8400_RR4BVOL_WIDTH 3 /* RR4BVOL - [2:0] */ + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8400_LRBLO 0x0080 /* LRBLO */ +#define WM8400_LRBLO_MASK 0x0080 /* LRBLO */ +#define WM8400_LRBLO_SHIFT 7 /* LRBLO */ +#define WM8400_LRBLO_WIDTH 1 /* LRBLO */ +#define WM8400_LLBLO 0x0040 /* LLBLO */ +#define WM8400_LLBLO_MASK 0x0040 /* LLBLO */ +#define WM8400_LLBLO_SHIFT 6 /* LLBLO */ +#define WM8400_LLBLO_WIDTH 1 /* LLBLO */ +#define WM8400_LRI3LO 0x0020 /* LRI3LO */ +#define WM8400_LRI3LO_MASK 0x0020 /* LRI3LO */ +#define WM8400_LRI3LO_SHIFT 5 /* LRI3LO */ +#define WM8400_LRI3LO_WIDTH 1 /* LRI3LO */ +#define WM8400_LLI3LO 0x0010 /* LLI3LO */ +#define WM8400_LLI3LO_MASK 0x0010 /* LLI3LO */ +#define WM8400_LLI3LO_SHIFT 4 /* LLI3LO */ +#define WM8400_LLI3LO_WIDTH 1 /* LLI3LO */ +#define WM8400_LR12LO 0x0008 /* LR12LO */ +#define WM8400_LR12LO_MASK 0x0008 /* LR12LO */ +#define WM8400_LR12LO_SHIFT 3 /* LR12LO */ +#define WM8400_LR12LO_WIDTH 1 /* LR12LO */ +#define WM8400_LL12LO 0x0004 /* LL12LO */ +#define WM8400_LL12LO_MASK 0x0004 /* LL12LO */ +#define WM8400_LL12LO_SHIFT 2 /* LL12LO */ +#define WM8400_LL12LO_WIDTH 1 /* LL12LO */ +#define WM8400_LDLO 0x0001 /* LDLO */ +#define WM8400_LDLO_MASK 0x0001 /* LDLO */ +#define WM8400_LDLO_SHIFT 0 /* LDLO */ +#define WM8400_LDLO_WIDTH 1 /* LDLO */ + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8400_RLBRO 0x0080 /* RLBRO */ +#define WM8400_RLBRO_MASK 0x0080 /* RLBRO */ +#define WM8400_RLBRO_SHIFT 7 /* RLBRO */ +#define WM8400_RLBRO_WIDTH 1 /* RLBRO */ +#define WM8400_RRBRO 0x0040 /* RRBRO */ +#define WM8400_RRBRO_MASK 0x0040 /* RRBRO */ +#define WM8400_RRBRO_SHIFT 6 /* RRBRO */ +#define WM8400_RRBRO_WIDTH 1 /* RRBRO */ +#define WM8400_RLI3RO 0x0020 /* RLI3RO */ +#define WM8400_RLI3RO_MASK 0x0020 /* RLI3RO */ +#define WM8400_RLI3RO_SHIFT 5 /* RLI3RO */ +#define WM8400_RLI3RO_WIDTH 1 /* RLI3RO */ +#define WM8400_RRI3RO 0x0010 /* RRI3RO */ +#define WM8400_RRI3RO_MASK 0x0010 /* RRI3RO */ +#define WM8400_RRI3RO_SHIFT 4 /* RRI3RO */ +#define WM8400_RRI3RO_WIDTH 1 /* RRI3RO */ +#define WM8400_RL12RO 0x0008 /* RL12RO */ +#define WM8400_RL12RO_MASK 0x0008 /* RL12RO */ +#define WM8400_RL12RO_SHIFT 3 /* RL12RO */ +#define WM8400_RL12RO_WIDTH 1 /* RL12RO */ +#define WM8400_RR12RO 0x0004 /* RR12RO */ +#define WM8400_RR12RO_MASK 0x0004 /* RR12RO */ +#define WM8400_RR12RO_SHIFT 2 /* RR12RO */ +#define WM8400_RR12RO_WIDTH 1 /* RR12RO */ +#define WM8400_RDRO 0x0001 /* RDRO */ +#define WM8400_RDRO_MASK 0x0001 /* RDRO */ +#define WM8400_RDRO_SHIFT 0 /* RDRO */ +#define WM8400_RDRO_WIDTH 1 /* RDRO */ + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8400_LLI3LOVOL_MASK 0x01C0 /* LLI3LOVOL - [8:6] */ +#define WM8400_LLI3LOVOL_SHIFT 6 /* LLI3LOVOL - [8:6] */ +#define WM8400_LLI3LOVOL_WIDTH 3 /* LLI3LOVOL - [8:6] */ +#define WM8400_LR12LOVOL_MASK 0x0038 /* LR12LOVOL - [5:3] */ +#define WM8400_LR12LOVOL_SHIFT 3 /* LR12LOVOL - [5:3] */ +#define WM8400_LR12LOVOL_WIDTH 3 /* LR12LOVOL - [5:3] */ +#define WM8400_LL12LOVOL_MASK 0x0007 /* LL12LOVOL - [2:0] */ +#define WM8400_LL12LOVOL_SHIFT 0 /* LL12LOVOL - [2:0] */ +#define WM8400_LL12LOVOL_WIDTH 3 /* LL12LOVOL - [2:0] */ + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8400_RRI3ROVOL_MASK 0x01C0 /* RRI3ROVOL - [8:6] */ +#define WM8400_RRI3ROVOL_SHIFT 6 /* RRI3ROVOL - [8:6] */ +#define WM8400_RRI3ROVOL_WIDTH 3 /* RRI3ROVOL - [8:6] */ +#define WM8400_RL12ROVOL_MASK 0x0038 /* RL12ROVOL - [5:3] */ +#define WM8400_RL12ROVOL_SHIFT 3 /* RL12ROVOL - [5:3] */ +#define WM8400_RL12ROVOL_WIDTH 3 /* RL12ROVOL - [5:3] */ +#define WM8400_RR12ROVOL_MASK 0x0007 /* RR12ROVOL - [2:0] */ +#define WM8400_RR12ROVOL_SHIFT 0 /* RR12ROVOL - [2:0] */ +#define WM8400_RR12ROVOL_WIDTH 3 /* RR12ROVOL - [2:0] */ + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8400_LRI3LOVOL_MASK 0x01C0 /* LRI3LOVOL - [8:6] */ +#define WM8400_LRI3LOVOL_SHIFT 6 /* LRI3LOVOL - [8:6] */ +#define WM8400_LRI3LOVOL_WIDTH 3 /* LRI3LOVOL - [8:6] */ +#define WM8400_LRBLOVOL_MASK 0x0038 /* LRBLOVOL - [5:3] */ +#define WM8400_LRBLOVOL_SHIFT 3 /* LRBLOVOL - [5:3] */ +#define WM8400_LRBLOVOL_WIDTH 3 /* LRBLOVOL - [5:3] */ +#define WM8400_LLBLOVOL_MASK 0x0007 /* LLBLOVOL - [2:0] */ +#define WM8400_LLBLOVOL_SHIFT 0 /* LLBLOVOL - [2:0] */ +#define WM8400_LLBLOVOL_WIDTH 3 /* LLBLOVOL - [2:0] */ + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8400_RLI3ROVOL_MASK 0x01C0 /* RLI3ROVOL - [8:6] */ +#define WM8400_RLI3ROVOL_SHIFT 6 /* RLI3ROVOL - [8:6] */ +#define WM8400_RLI3ROVOL_WIDTH 3 /* RLI3ROVOL - [8:6] */ +#define WM8400_RLBROVOL_MASK 0x0038 /* RLBROVOL - [5:3] */ +#define WM8400_RLBROVOL_SHIFT 3 /* RLBROVOL - [5:3] */ +#define WM8400_RLBROVOL_WIDTH 3 /* RLBROVOL - [5:3] */ +#define WM8400_RRBROVOL_MASK 0x0007 /* RRBROVOL - [2:0] */ +#define WM8400_RRBROVOL_SHIFT 0 /* RRBROVOL - [2:0] */ +#define WM8400_RRBROVOL_WIDTH 3 /* RRBROVOL - [2:0] */ + +/* + * R51 (0x33) - Out3/4 Mixer + */ +#define WM8400_VSEL_MASK 0x0180 /* VSEL - [8:7] */ +#define WM8400_VSEL_SHIFT 7 /* VSEL - [8:7] */ +#define WM8400_VSEL_WIDTH 2 /* VSEL - [8:7] */ +#define WM8400_LI4O3 0x0020 /* LI4O3 */ +#define WM8400_LI4O3_MASK 0x0020 /* LI4O3 */ +#define WM8400_LI4O3_SHIFT 5 /* LI4O3 */ +#define WM8400_LI4O3_WIDTH 1 /* LI4O3 */ +#define WM8400_LPGAO3 0x0010 /* LPGAO3 */ +#define WM8400_LPGAO3_MASK 0x0010 /* LPGAO3 */ +#define WM8400_LPGAO3_SHIFT 4 /* LPGAO3 */ +#define WM8400_LPGAO3_WIDTH 1 /* LPGAO3 */ +#define WM8400_RI4O4 0x0002 /* RI4O4 */ +#define WM8400_RI4O4_MASK 0x0002 /* RI4O4 */ +#define WM8400_RI4O4_SHIFT 1 /* RI4O4 */ +#define WM8400_RI4O4_WIDTH 1 /* RI4O4 */ +#define WM8400_RPGAO4 0x0001 /* RPGAO4 */ +#define WM8400_RPGAO4_MASK 0x0001 /* RPGAO4 */ +#define WM8400_RPGAO4_SHIFT 0 /* RPGAO4 */ +#define WM8400_RPGAO4_WIDTH 1 /* RPGAO4 */ + +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8400_LLOPGALON 0x0040 /* LLOPGALON */ +#define WM8400_LLOPGALON_MASK 0x0040 /* LLOPGALON */ +#define WM8400_LLOPGALON_SHIFT 6 /* LLOPGALON */ +#define WM8400_LLOPGALON_WIDTH 1 /* LLOPGALON */ +#define WM8400_LROPGALON 0x0020 /* LROPGALON */ +#define WM8400_LROPGALON_MASK 0x0020 /* LROPGALON */ +#define WM8400_LROPGALON_SHIFT 5 /* LROPGALON */ +#define WM8400_LROPGALON_WIDTH 1 /* LROPGALON */ +#define WM8400_LOPLON 0x0010 /* LOPLON */ +#define WM8400_LOPLON_MASK 0x0010 /* LOPLON */ +#define WM8400_LOPLON_SHIFT 4 /* LOPLON */ +#define WM8400_LOPLON_WIDTH 1 /* LOPLON */ +#define WM8400_LR12LOP 0x0004 /* LR12LOP */ +#define WM8400_LR12LOP_MASK 0x0004 /* LR12LOP */ +#define WM8400_LR12LOP_SHIFT 2 /* LR12LOP */ +#define WM8400_LR12LOP_WIDTH 1 /* LR12LOP */ +#define WM8400_LL12LOP 0x0002 /* LL12LOP */ +#define WM8400_LL12LOP_MASK 0x0002 /* LL12LOP */ +#define WM8400_LL12LOP_SHIFT 1 /* LL12LOP */ +#define WM8400_LL12LOP_WIDTH 1 /* LL12LOP */ +#define WM8400_LLOPGALOP 0x0001 /* LLOPGALOP */ +#define WM8400_LLOPGALOP_MASK 0x0001 /* LLOPGALOP */ +#define WM8400_LLOPGALOP_SHIFT 0 /* LLOPGALOP */ +#define WM8400_LLOPGALOP_WIDTH 1 /* LLOPGALOP */ + +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8400_RROPGARON 0x0040 /* RROPGARON */ +#define WM8400_RROPGARON_MASK 0x0040 /* RROPGARON */ +#define WM8400_RROPGARON_SHIFT 6 /* RROPGARON */ +#define WM8400_RROPGARON_WIDTH 1 /* RROPGARON */ +#define WM8400_RLOPGARON 0x0020 /* RLOPGARON */ +#define WM8400_RLOPGARON_MASK 0x0020 /* RLOPGARON */ +#define WM8400_RLOPGARON_SHIFT 5 /* RLOPGARON */ +#define WM8400_RLOPGARON_WIDTH 1 /* RLOPGARON */ +#define WM8400_ROPRON 0x0010 /* ROPRON */ +#define WM8400_ROPRON_MASK 0x0010 /* ROPRON */ +#define WM8400_ROPRON_SHIFT 4 /* ROPRON */ +#define WM8400_ROPRON_WIDTH 1 /* ROPRON */ +#define WM8400_RL12ROP 0x0004 /* RL12ROP */ +#define WM8400_RL12ROP_MASK 0x0004 /* RL12ROP */ +#define WM8400_RL12ROP_SHIFT 2 /* RL12ROP */ +#define WM8400_RL12ROP_WIDTH 1 /* RL12ROP */ +#define WM8400_RR12ROP 0x0002 /* RR12ROP */ +#define WM8400_RR12ROP_MASK 0x0002 /* RR12ROP */ +#define WM8400_RR12ROP_SHIFT 1 /* RR12ROP */ +#define WM8400_RR12ROP_WIDTH 1 /* RR12ROP */ +#define WM8400_RROPGAROP 0x0001 /* RROPGAROP */ +#define WM8400_RROPGAROP_MASK 0x0001 /* RROPGAROP */ +#define WM8400_RROPGAROP_SHIFT 0 /* RROPGAROP */ +#define WM8400_RROPGAROP_WIDTH 1 /* RROPGAROP */ + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8400_LB2SPK 0x0080 /* LB2SPK */ +#define WM8400_LB2SPK_MASK 0x0080 /* LB2SPK */ +#define WM8400_LB2SPK_SHIFT 7 /* LB2SPK */ +#define WM8400_LB2SPK_WIDTH 1 /* LB2SPK */ +#define WM8400_RB2SPK 0x0040 /* RB2SPK */ +#define WM8400_RB2SPK_MASK 0x0040 /* RB2SPK */ +#define WM8400_RB2SPK_SHIFT 6 /* RB2SPK */ +#define WM8400_RB2SPK_WIDTH 1 /* RB2SPK */ +#define WM8400_LI2SPK 0x0020 /* LI2SPK */ +#define WM8400_LI2SPK_MASK 0x0020 /* LI2SPK */ +#define WM8400_LI2SPK_SHIFT 5 /* LI2SPK */ +#define WM8400_LI2SPK_WIDTH 1 /* LI2SPK */ +#define WM8400_RI2SPK 0x0010 /* RI2SPK */ +#define WM8400_RI2SPK_MASK 0x0010 /* RI2SPK */ +#define WM8400_RI2SPK_SHIFT 4 /* RI2SPK */ +#define WM8400_RI2SPK_WIDTH 1 /* RI2SPK */ +#define WM8400_LOPGASPK 0x0008 /* LOPGASPK */ +#define WM8400_LOPGASPK_MASK 0x0008 /* LOPGASPK */ +#define WM8400_LOPGASPK_SHIFT 3 /* LOPGASPK */ +#define WM8400_LOPGASPK_WIDTH 1 /* LOPGASPK */ +#define WM8400_ROPGASPK 0x0004 /* ROPGASPK */ +#define WM8400_ROPGASPK_MASK 0x0004 /* ROPGASPK */ +#define WM8400_ROPGASPK_SHIFT 2 /* ROPGASPK */ +#define WM8400_ROPGASPK_WIDTH 1 /* ROPGASPK */ +#define WM8400_LDSPK 0x0002 /* LDSPK */ +#define WM8400_LDSPK_MASK 0x0002 /* LDSPK */ +#define WM8400_LDSPK_SHIFT 1 /* LDSPK */ +#define WM8400_LDSPK_WIDTH 1 /* LDSPK */ +#define WM8400_RDSPK 0x0001 /* RDSPK */ +#define WM8400_RDSPK_MASK 0x0001 /* RDSPK */ +#define WM8400_RDSPK_SHIFT 0 /* RDSPK */ +#define WM8400_RDSPK_WIDTH 1 /* RDSPK */ + +/* + * R55 (0x37) - Additional Control + */ +#define WM8400_VROI 0x0001 /* VROI */ +#define WM8400_VROI_MASK 0x0001 /* VROI */ +#define WM8400_VROI_SHIFT 0 /* VROI */ +#define WM8400_VROI_WIDTH 1 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8400_DIS_LLINE 0x0020 /* DIS_LLINE */ +#define WM8400_DIS_LLINE_MASK 0x0020 /* DIS_LLINE */ +#define WM8400_DIS_LLINE_SHIFT 5 /* DIS_LLINE */ +#define WM8400_DIS_LLINE_WIDTH 1 /* DIS_LLINE */ +#define WM8400_DIS_RLINE 0x0010 /* DIS_RLINE */ +#define WM8400_DIS_RLINE_MASK 0x0010 /* DIS_RLINE */ +#define WM8400_DIS_RLINE_SHIFT 4 /* DIS_RLINE */ +#define WM8400_DIS_RLINE_WIDTH 1 /* DIS_RLINE */ +#define WM8400_DIS_OUT3 0x0008 /* DIS_OUT3 */ +#define WM8400_DIS_OUT3_MASK 0x0008 /* DIS_OUT3 */ +#define WM8400_DIS_OUT3_SHIFT 3 /* DIS_OUT3 */ +#define WM8400_DIS_OUT3_WIDTH 1 /* DIS_OUT3 */ +#define WM8400_DIS_OUT4 0x0004 /* DIS_OUT4 */ +#define WM8400_DIS_OUT4_MASK 0x0004 /* DIS_OUT4 */ +#define WM8400_DIS_OUT4_SHIFT 2 /* DIS_OUT4 */ +#define WM8400_DIS_OUT4_WIDTH 1 /* DIS_OUT4 */ +#define WM8400_DIS_LOUT 0x0002 /* DIS_LOUT */ +#define WM8400_DIS_LOUT_MASK 0x0002 /* DIS_LOUT */ +#define WM8400_DIS_LOUT_SHIFT 1 /* DIS_LOUT */ +#define WM8400_DIS_LOUT_WIDTH 1 /* DIS_LOUT */ +#define WM8400_DIS_ROUT 0x0001 /* DIS_ROUT */ +#define WM8400_DIS_ROUT_MASK 0x0001 /* DIS_ROUT */ +#define WM8400_DIS_ROUT_SHIFT 0 /* DIS_ROUT */ +#define WM8400_DIS_ROUT_WIDTH 1 /* DIS_ROUT */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8400_SOFTST 0x0040 /* SOFTST */ +#define WM8400_SOFTST_MASK 0x0040 /* SOFTST */ +#define WM8400_SOFTST_SHIFT 6 /* SOFTST */ +#define WM8400_SOFTST_WIDTH 1 /* SOFTST */ +#define WM8400_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8400_BUFIOEN_MASK 0x0008 /* BUFIOEN */ +#define WM8400_BUFIOEN_SHIFT 3 /* BUFIOEN */ +#define WM8400_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8400_BUFDCOPEN 0x0004 /* BUFDCOPEN */ +#define WM8400_BUFDCOPEN_MASK 0x0004 /* BUFDCOPEN */ +#define WM8400_BUFDCOPEN_SHIFT 2 /* BUFDCOPEN */ +#define WM8400_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */ +#define WM8400_POBCTRL 0x0002 /* POBCTRL */ +#define WM8400_POBCTRL_MASK 0x0002 /* POBCTRL */ +#define WM8400_POBCTRL_SHIFT 1 /* POBCTRL */ +#define WM8400_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8400_VMIDTOG 0x0001 /* VMIDTOG */ +#define WM8400_VMIDTOG_MASK 0x0001 /* VMIDTOG */ +#define WM8400_VMIDTOG_SHIFT 0 /* VMIDTOG */ +#define WM8400_VMIDTOG_WIDTH 1 /* VMIDTOG */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8400_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */ +#define WM8400_MCDSCTH_SHIFT 6 /* MCDSCTH - [7:6] */ +#define WM8400_MCDSCTH_WIDTH 2 /* MCDSCTH - [7:6] */ +#define WM8400_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */ +#define WM8400_MCDTHR_SHIFT 3 /* MCDTHR - [5:3] */ +#define WM8400_MCDTHR_WIDTH 3 /* MCDTHR - [5:3] */ +#define WM8400_MCD 0x0004 /* MCD */ +#define WM8400_MCD_MASK 0x0004 /* MCD */ +#define WM8400_MCD_SHIFT 2 /* MCD */ +#define WM8400_MCD_WIDTH 1 /* MCD */ +#define WM8400_MBSEL 0x0001 /* MBSEL */ +#define WM8400_MBSEL_MASK 0x0001 /* MBSEL */ +#define WM8400_MBSEL_SHIFT 0 /* MBSEL */ +#define WM8400_MBSEL_WIDTH 1 /* MBSEL */ + +/* + * R60 (0x3C) - FLL Control 1 + */ +#define WM8400_FLL_REF_FREQ 0x1000 /* FLL_REF_FREQ */ +#define WM8400_FLL_REF_FREQ_MASK 0x1000 /* FLL_REF_FREQ */ +#define WM8400_FLL_REF_FREQ_SHIFT 12 /* FLL_REF_FREQ */ +#define WM8400_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */ +#define WM8400_FLL_CLK_SRC_MASK 0x0C00 /* FLL_CLK_SRC - [11:10] */ +#define WM8400_FLL_CLK_SRC_SHIFT 10 /* FLL_CLK_SRC - [11:10] */ +#define WM8400_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [11:10] */ +#define WM8400_FLL_FRAC 0x0200 /* FLL_FRAC */ +#define WM8400_FLL_FRAC_MASK 0x0200 /* FLL_FRAC */ +#define WM8400_FLL_FRAC_SHIFT 9 /* FLL_FRAC */ +#define WM8400_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM8400_FLL_OSC_ENA 0x0100 /* FLL_OSC_ENA */ +#define WM8400_FLL_OSC_ENA_MASK 0x0100 /* FLL_OSC_ENA */ +#define WM8400_FLL_OSC_ENA_SHIFT 8 /* FLL_OSC_ENA */ +#define WM8400_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8400_FLL_CTRL_RATE_MASK 0x00E0 /* FLL_CTRL_RATE - [7:5] */ +#define WM8400_FLL_CTRL_RATE_SHIFT 5 /* FLL_CTRL_RATE - [7:5] */ +#define WM8400_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [7:5] */ +#define WM8400_FLL_FRATIO_MASK 0x001F /* FLL_FRATIO - [4:0] */ +#define WM8400_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [4:0] */ +#define WM8400_FLL_FRATIO_WIDTH 5 /* FLL_FRATIO - [4:0] */ + +/* + * R61 (0x3D) - FLL Control 2 + */ +#define WM8400_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM8400_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM8400_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R62 (0x3E) - FLL Control 3 + */ +#define WM8400_FLL_N_MASK 0x03FF /* FLL_N - [9:0] */ +#define WM8400_FLL_N_SHIFT 0 /* FLL_N - [9:0] */ +#define WM8400_FLL_N_WIDTH 10 /* FLL_N - [9:0] */ + +/* + * R63 (0x3F) - FLL Control 4 + */ +#define WM8400_FLL_TRK_GAIN_MASK 0x0078 /* FLL_TRK_GAIN - [6:3] */ +#define WM8400_FLL_TRK_GAIN_SHIFT 3 /* FLL_TRK_GAIN - [6:3] */ +#define WM8400_FLL_TRK_GAIN_WIDTH 4 /* FLL_TRK_GAIN - [6:3] */ +#define WM8400_FLL_OUTDIV_MASK 0x0007 /* FLL_OUTDIV - [2:0] */ +#define WM8400_FLL_OUTDIV_SHIFT 0 /* FLL_OUTDIV - [2:0] */ +#define WM8400_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [2:0] */ + +void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400); + +#endif diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h new file mode 100644 index 000000000000..49042e990f05 --- /dev/null +++ b/include/linux/mfd/wm8400-private.h @@ -0,0 +1,935 @@ +/* + * wm8400 private definitions. + * + * Copyright 2008 Wolfson Microelectronics plc + * + * 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. + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_MFD_WM8400_PRIV_H +#define __LINUX_MFD_WM8400_PRIV_H + +#include +#include + +#define WM8400_REGISTER_COUNT 0x55 + +struct wm8400 { + struct device *dev; + + int (*read_dev)(void *data, char reg, int count, u16 *dst); + int (*write_dev)(void *data, char reg, int count, const u16 *src); + + struct mutex io_lock; + void *io_data; + + u16 reg_cache[WM8400_REGISTER_COUNT]; + + struct platform_device regulators[6]; +}; + +/* + * Register values. + */ +#define WM8400_RESET_ID 0x00 +#define WM8400_ID 0x01 +#define WM8400_POWER_MANAGEMENT_1 0x02 +#define WM8400_POWER_MANAGEMENT_2 0x03 +#define WM8400_POWER_MANAGEMENT_3 0x04 +#define WM8400_AUDIO_INTERFACE_1 0x05 +#define WM8400_AUDIO_INTERFACE_2 0x06 +#define WM8400_CLOCKING_1 0x07 +#define WM8400_CLOCKING_2 0x08 +#define WM8400_AUDIO_INTERFACE_3 0x09 +#define WM8400_AUDIO_INTERFACE_4 0x0A +#define WM8400_DAC_CTRL 0x0B +#define WM8400_LEFT_DAC_DIGITAL_VOLUME 0x0C +#define WM8400_RIGHT_DAC_DIGITAL_VOLUME 0x0D +#define WM8400_DIGITAL_SIDE_TONE 0x0E +#define WM8400_ADC_CTRL 0x0F +#define WM8400_LEFT_ADC_DIGITAL_VOLUME 0x10 +#define WM8400_RIGHT_ADC_DIGITAL_VOLUME 0x11 +#define WM8400_GPIO_CTRL_1 0x12 +#define WM8400_GPIO1_GPIO2 0x13 +#define WM8400_GPIO3_GPIO4 0x14 +#define WM8400_GPIO5_GPIO6 0x15 +#define WM8400_GPIOCTRL_2 0x16 +#define WM8400_GPIO_POL 0x17 +#define WM8400_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8400_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8400_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8400_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8400_LEFT_OUTPUT_VOLUME 0x1C +#define WM8400_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8400_LINE_OUTPUTS_VOLUME 0x1E +#define WM8400_OUT3_4_VOLUME 0x1F +#define WM8400_LEFT_OPGA_VOLUME 0x20 +#define WM8400_RIGHT_OPGA_VOLUME 0x21 +#define WM8400_SPEAKER_VOLUME 0x22 +#define WM8400_CLASSD1 0x23 +#define WM8400_CLASSD3 0x25 +#define WM8400_INPUT_MIXER1 0x27 +#define WM8400_INPUT_MIXER2 0x28 +#define WM8400_INPUT_MIXER3 0x29 +#define WM8400_INPUT_MIXER4 0x2A +#define WM8400_INPUT_MIXER5 0x2B +#define WM8400_INPUT_MIXER6 0x2C +#define WM8400_OUTPUT_MIXER1 0x2D +#define WM8400_OUTPUT_MIXER2 0x2E +#define WM8400_OUTPUT_MIXER3 0x2F +#define WM8400_OUTPUT_MIXER4 0x30 +#define WM8400_OUTPUT_MIXER5 0x31 +#define WM8400_OUTPUT_MIXER6 0x32 +#define WM8400_OUT3_4_MIXER 0x33 +#define WM8400_LINE_MIXER1 0x34 +#define WM8400_LINE_MIXER2 0x35 +#define WM8400_SPEAKER_MIXER 0x36 +#define WM8400_ADDITIONAL_CONTROL 0x37 +#define WM8400_ANTIPOP1 0x38 +#define WM8400_ANTIPOP2 0x39 +#define WM8400_MICBIAS 0x3A +#define WM8400_FLL_CONTROL_1 0x3C +#define WM8400_FLL_CONTROL_2 0x3D +#define WM8400_FLL_CONTROL_3 0x3E +#define WM8400_FLL_CONTROL_4 0x3F +#define WM8400_LDO1_CONTROL 0x41 +#define WM8400_LDO2_CONTROL 0x42 +#define WM8400_LDO3_CONTROL 0x43 +#define WM8400_LDO4_CONTROL 0x44 +#define WM8400_DCDC1_CONTROL_1 0x46 +#define WM8400_DCDC1_CONTROL_2 0x47 +#define WM8400_DCDC2_CONTROL_1 0x48 +#define WM8400_DCDC2_CONTROL_2 0x49 +#define WM8400_INTERFACE 0x4B +#define WM8400_PM_GENERAL 0x4C +#define WM8400_PM_SHUTDOWN_CONTROL 0x4E +#define WM8400_INTERRUPT_STATUS_1 0x4F +#define WM8400_INTERRUPT_STATUS_1_MASK 0x50 +#define WM8400_INTERRUPT_LEVELS 0x51 +#define WM8400_SHUTDOWN_REASON 0x52 +#define WM8400_LINE_CIRCUITS 0x54 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset/ID + */ +#define WM8400_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET/CHIP_ID - [15:0] */ +#define WM8400_SW_RESET_CHIP_ID_SHIFT 0 /* SW_RESET/CHIP_ID - [15:0] */ +#define WM8400_SW_RESET_CHIP_ID_WIDTH 16 /* SW_RESET/CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - ID + */ +#define WM8400_CHIP_REV_MASK 0x7000 /* CHIP_REV - [14:12] */ +#define WM8400_CHIP_REV_SHIFT 12 /* CHIP_REV - [14:12] */ +#define WM8400_CHIP_REV_WIDTH 3 /* CHIP_REV - [14:12] */ + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8400_IRQ 0x1000 /* IRQ */ +#define WM8400_IRQ_MASK 0x1000 /* IRQ */ +#define WM8400_IRQ_SHIFT 12 /* IRQ */ +#define WM8400_IRQ_WIDTH 1 /* IRQ */ +#define WM8400_TEMPOK 0x0800 /* TEMPOK */ +#define WM8400_TEMPOK_MASK 0x0800 /* TEMPOK */ +#define WM8400_TEMPOK_SHIFT 11 /* TEMPOK */ +#define WM8400_TEMPOK_WIDTH 1 /* TEMPOK */ +#define WM8400_MIC1SHRT 0x0400 /* MIC1SHRT */ +#define WM8400_MIC1SHRT_MASK 0x0400 /* MIC1SHRT */ +#define WM8400_MIC1SHRT_SHIFT 10 /* MIC1SHRT */ +#define WM8400_MIC1SHRT_WIDTH 1 /* MIC1SHRT */ +#define WM8400_MIC1DET 0x0200 /* MIC1DET */ +#define WM8400_MIC1DET_MASK 0x0200 /* MIC1DET */ +#define WM8400_MIC1DET_SHIFT 9 /* MIC1DET */ +#define WM8400_MIC1DET_WIDTH 1 /* MIC1DET */ +#define WM8400_FLL_LCK 0x0100 /* FLL_LCK */ +#define WM8400_FLL_LCK_MASK 0x0100 /* FLL_LCK */ +#define WM8400_FLL_LCK_SHIFT 8 /* FLL_LCK */ +#define WM8400_FLL_LCK_WIDTH 1 /* FLL_LCK */ +#define WM8400_GPIO_STATUS_MASK 0x00FF /* GPIO_STATUS - [7:0] */ +#define WM8400_GPIO_STATUS_SHIFT 0 /* GPIO_STATUS - [7:0] */ +#define WM8400_GPIO_STATUS_WIDTH 8 /* GPIO_STATUS - [7:0] */ + +/* + * R19 (0x13) - GPIO1 & GPIO2 + */ +#define WM8400_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_DEB_ENA_MASK 0x8000 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_DEB_ENA_SHIFT 15 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_DEB_ENA_WIDTH 1 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_IRQ_ENA_MASK 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_IRQ_ENA_SHIFT 14 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_IRQ_ENA_WIDTH 1 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_PU 0x2000 /* GPIO2_PU */ +#define WM8400_GPIO2_PU_MASK 0x2000 /* GPIO2_PU */ +#define WM8400_GPIO2_PU_SHIFT 13 /* GPIO2_PU */ +#define WM8400_GPIO2_PU_WIDTH 1 /* GPIO2_PU */ +#define WM8400_GPIO2_PD 0x1000 /* GPIO2_PD */ +#define WM8400_GPIO2_PD_MASK 0x1000 /* GPIO2_PD */ +#define WM8400_GPIO2_PD_SHIFT 12 /* GPIO2_PD */ +#define WM8400_GPIO2_PD_WIDTH 1 /* GPIO2_PD */ +#define WM8400_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */ +#define WM8400_GPIO2_SEL_SHIFT 8 /* GPIO2_SEL - [11:8] */ +#define WM8400_GPIO2_SEL_WIDTH 4 /* GPIO2_SEL - [11:8] */ +#define WM8400_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_DEB_ENA_MASK 0x0080 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_DEB_ENA_SHIFT 7 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_DEB_ENA_WIDTH 1 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_IRQ_ENA_MASK 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_IRQ_ENA_SHIFT 6 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_IRQ_ENA_WIDTH 1 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8400_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */ +#define WM8400_GPIO1_PU_SHIFT 5 /* GPIO1_PU */ +#define WM8400_GPIO1_PU_WIDTH 1 /* GPIO1_PU */ +#define WM8400_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8400_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */ +#define WM8400_GPIO1_PD_SHIFT 4 /* GPIO1_PD */ +#define WM8400_GPIO1_PD_WIDTH 1 /* GPIO1_PD */ +#define WM8400_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ +#define WM8400_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */ +#define WM8400_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - GPIO3 & GPIO4 + */ +#define WM8400_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_DEB_ENA_MASK 0x8000 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_DEB_ENA_SHIFT 15 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_DEB_ENA_WIDTH 1 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_IRQ_ENA_MASK 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_IRQ_ENA_SHIFT 14 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_IRQ_ENA_WIDTH 1 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_PU 0x2000 /* GPIO4_PU */ +#define WM8400_GPIO4_PU_MASK 0x2000 /* GPIO4_PU */ +#define WM8400_GPIO4_PU_SHIFT 13 /* GPIO4_PU */ +#define WM8400_GPIO4_PU_WIDTH 1 /* GPIO4_PU */ +#define WM8400_GPIO4_PD 0x1000 /* GPIO4_PD */ +#define WM8400_GPIO4_PD_MASK 0x1000 /* GPIO4_PD */ +#define WM8400_GPIO4_PD_SHIFT 12 /* GPIO4_PD */ +#define WM8400_GPIO4_PD_WIDTH 1 /* GPIO4_PD */ +#define WM8400_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */ +#define WM8400_GPIO4_SEL_SHIFT 8 /* GPIO4_SEL - [11:8] */ +#define WM8400_GPIO4_SEL_WIDTH 4 /* GPIO4_SEL - [11:8] */ +#define WM8400_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_DEB_ENA_MASK 0x0080 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_DEB_ENA_SHIFT 7 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_DEB_ENA_WIDTH 1 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_IRQ_ENA_MASK 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_IRQ_ENA_SHIFT 6 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_IRQ_ENA_WIDTH 1 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_PU 0x0020 /* GPIO3_PU */ +#define WM8400_GPIO3_PU_MASK 0x0020 /* GPIO3_PU */ +#define WM8400_GPIO3_PU_SHIFT 5 /* GPIO3_PU */ +#define WM8400_GPIO3_PU_WIDTH 1 /* GPIO3_PU */ +#define WM8400_GPIO3_PD 0x0010 /* GPIO3_PD */ +#define WM8400_GPIO3_PD_MASK 0x0010 /* GPIO3_PD */ +#define WM8400_GPIO3_PD_SHIFT 4 /* GPIO3_PD */ +#define WM8400_GPIO3_PD_WIDTH 1 /* GPIO3_PD */ +#define WM8400_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */ +#define WM8400_GPIO3_SEL_SHIFT 0 /* GPIO3_SEL - [3:0] */ +#define WM8400_GPIO3_SEL_WIDTH 4 /* GPIO3_SEL - [3:0] */ + +/* + * R21 (0x15) - GPIO5 & GPIO6 + */ +#define WM8400_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_DEB_ENA_MASK 0x8000 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_DEB_ENA_SHIFT 15 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_DEB_ENA_WIDTH 1 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_IRQ_ENA_MASK 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_IRQ_ENA_SHIFT 14 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_IRQ_ENA_WIDTH 1 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_PU 0x2000 /* GPIO6_PU */ +#define WM8400_GPIO6_PU_MASK 0x2000 /* GPIO6_PU */ +#define WM8400_GPIO6_PU_SHIFT 13 /* GPIO6_PU */ +#define WM8400_GPIO6_PU_WIDTH 1 /* GPIO6_PU */ +#define WM8400_GPIO6_PD 0x1000 /* GPIO6_PD */ +#define WM8400_GPIO6_PD_MASK 0x1000 /* GPIO6_PD */ +#define WM8400_GPIO6_PD_SHIFT 12 /* GPIO6_PD */ +#define WM8400_GPIO6_PD_WIDTH 1 /* GPIO6_PD */ +#define WM8400_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */ +#define WM8400_GPIO6_SEL_SHIFT 8 /* GPIO6_SEL - [11:8] */ +#define WM8400_GPIO6_SEL_WIDTH 4 /* GPIO6_SEL - [11:8] */ +#define WM8400_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_DEB_ENA_MASK 0x0080 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_DEB_ENA_SHIFT 7 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_DEB_ENA_WIDTH 1 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_IRQ_ENA_MASK 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_IRQ_ENA_SHIFT 6 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_IRQ_ENA_WIDTH 1 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_PU 0x0020 /* GPIO5_PU */ +#define WM8400_GPIO5_PU_MASK 0x0020 /* GPIO5_PU */ +#define WM8400_GPIO5_PU_SHIFT 5 /* GPIO5_PU */ +#define WM8400_GPIO5_PU_WIDTH 1 /* GPIO5_PU */ +#define WM8400_GPIO5_PD 0x0010 /* GPIO5_PD */ +#define WM8400_GPIO5_PD_MASK 0x0010 /* GPIO5_PD */ +#define WM8400_GPIO5_PD_SHIFT 4 /* GPIO5_PD */ +#define WM8400_GPIO5_PD_WIDTH 1 /* GPIO5_PD */ +#define WM8400_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */ +#define WM8400_GPIO5_SEL_SHIFT 0 /* GPIO5_SEL - [3:0] */ +#define WM8400_GPIO5_SEL_WIDTH 4 /* GPIO5_SEL - [3:0] */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8400_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8400_TEMPOK_IRQ_ENA_MASK 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8400_TEMPOK_IRQ_ENA_SHIFT 11 /* TEMPOK_IRQ_ENA */ +#define WM8400_TEMPOK_IRQ_ENA_WIDTH 1 /* TEMPOK_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA 0x0400 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA_MASK 0x0400 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA_SHIFT 10 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA_WIDTH 1 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA 0x0200 /* MIC1DET_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA_MASK 0x0200 /* MIC1DET_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA_SHIFT 9 /* MIC1DET_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA_WIDTH 1 /* MIC1DET_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA 0x0100 /* FLL_LCK_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA_MASK 0x0100 /* FLL_LCK_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA_SHIFT 8 /* FLL_LCK_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA_WIDTH 1 /* FLL_LCK_IRQ_ENA */ +#define WM8400_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_DEB_ENA_MASK 0x0080 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_DEB_ENA_SHIFT 7 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_DEB_ENA_WIDTH 1 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_IRQ_ENA_MASK 0x0040 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_IRQ_ENA_SHIFT 6 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_IRQ_ENA_WIDTH 1 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8400_GPI8_ENA_MASK 0x0010 /* GPI8_ENA */ +#define WM8400_GPI8_ENA_SHIFT 4 /* GPI8_ENA */ +#define WM8400_GPI8_ENA_WIDTH 1 /* GPI8_ENA */ +#define WM8400_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_DEB_ENA_MASK 0x0008 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_DEB_ENA_SHIFT 3 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_DEB_ENA_WIDTH 1 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_IRQ_ENA_MASK 0x0004 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_IRQ_ENA_SHIFT 2 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_IRQ_ENA_WIDTH 1 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_ENA 0x0001 /* GPI7_ENA */ +#define WM8400_GPI7_ENA_MASK 0x0001 /* GPI7_ENA */ +#define WM8400_GPI7_ENA_SHIFT 0 /* GPI7_ENA */ +#define WM8400_GPI7_ENA_WIDTH 1 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8400_IRQ_INV 0x1000 /* IRQ_INV */ +#define WM8400_IRQ_INV_MASK 0x1000 /* IRQ_INV */ +#define WM8400_IRQ_INV_SHIFT 12 /* IRQ_INV */ +#define WM8400_IRQ_INV_WIDTH 1 /* IRQ_INV */ +#define WM8400_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8400_TEMPOK_POL_MASK 0x0800 /* TEMPOK_POL */ +#define WM8400_TEMPOK_POL_SHIFT 11 /* TEMPOK_POL */ +#define WM8400_TEMPOK_POL_WIDTH 1 /* TEMPOK_POL */ +#define WM8400_MIC1SHRT_POL 0x0400 /* MIC1SHRT_POL */ +#define WM8400_MIC1SHRT_POL_MASK 0x0400 /* MIC1SHRT_POL */ +#define WM8400_MIC1SHRT_POL_SHIFT 10 /* MIC1SHRT_POL */ +#define WM8400_MIC1SHRT_POL_WIDTH 1 /* MIC1SHRT_POL */ +#define WM8400_MIC1DET_POL 0x0200 /* MIC1DET_POL */ +#define WM8400_MIC1DET_POL_MASK 0x0200 /* MIC1DET_POL */ +#define WM8400_MIC1DET_POL_SHIFT 9 /* MIC1DET_POL */ +#define WM8400_MIC1DET_POL_WIDTH 1 /* MIC1DET_POL */ +#define WM8400_FLL_LCK_POL 0x0100 /* FLL_LCK_POL */ +#define WM8400_FLL_LCK_POL_MASK 0x0100 /* FLL_LCK_POL */ +#define WM8400_FLL_LCK_POL_SHIFT 8 /* FLL_LCK_POL */ +#define WM8400_FLL_LCK_POL_WIDTH 1 /* FLL_LCK_POL */ +#define WM8400_GPIO_POL_MASK 0x00FF /* GPIO_POL - [7:0] */ +#define WM8400_GPIO_POL_SHIFT 0 /* GPIO_POL - [7:0] */ +#define WM8400_GPIO_POL_WIDTH 8 /* GPIO_POL - [7:0] */ + +/* + * R65 (0x41) - LDO 1 Control + */ +#define WM8400_LDO1_ENA 0x8000 /* LDO1_ENA */ +#define WM8400_LDO1_ENA_MASK 0x8000 /* LDO1_ENA */ +#define WM8400_LDO1_ENA_SHIFT 15 /* LDO1_ENA */ +#define WM8400_LDO1_ENA_WIDTH 1 /* LDO1_ENA */ +#define WM8400_LDO1_SWI 0x4000 /* LDO1_SWI */ +#define WM8400_LDO1_SWI_MASK 0x4000 /* LDO1_SWI */ +#define WM8400_LDO1_SWI_SHIFT 14 /* LDO1_SWI */ +#define WM8400_LDO1_SWI_WIDTH 1 /* LDO1_SWI */ +#define WM8400_LDO1_OPFLT 0x1000 /* LDO1_OPFLT */ +#define WM8400_LDO1_OPFLT_MASK 0x1000 /* LDO1_OPFLT */ +#define WM8400_LDO1_OPFLT_SHIFT 12 /* LDO1_OPFLT */ +#define WM8400_LDO1_OPFLT_WIDTH 1 /* LDO1_OPFLT */ +#define WM8400_LDO1_ERRACT 0x0800 /* LDO1_ERRACT */ +#define WM8400_LDO1_ERRACT_MASK 0x0800 /* LDO1_ERRACT */ +#define WM8400_LDO1_ERRACT_SHIFT 11 /* LDO1_ERRACT */ +#define WM8400_LDO1_ERRACT_WIDTH 1 /* LDO1_ERRACT */ +#define WM8400_LDO1_HIB_MODE 0x0400 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_HIB_MODE_MASK 0x0400 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_HIB_MODE_SHIFT 10 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_HIB_MODE_WIDTH 1 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_VIMG_MASK 0x03E0 /* LDO1_VIMG - [9:5] */ +#define WM8400_LDO1_VIMG_SHIFT 5 /* LDO1_VIMG - [9:5] */ +#define WM8400_LDO1_VIMG_WIDTH 5 /* LDO1_VIMG - [9:5] */ +#define WM8400_LDO1_VSEL_MASK 0x001F /* LDO1_VSEL - [4:0] */ +#define WM8400_LDO1_VSEL_SHIFT 0 /* LDO1_VSEL - [4:0] */ +#define WM8400_LDO1_VSEL_WIDTH 5 /* LDO1_VSEL - [4:0] */ + +/* + * R66 (0x42) - LDO 2 Control + */ +#define WM8400_LDO2_ENA 0x8000 /* LDO2_ENA */ +#define WM8400_LDO2_ENA_MASK 0x8000 /* LDO2_ENA */ +#define WM8400_LDO2_ENA_SHIFT 15 /* LDO2_ENA */ +#define WM8400_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ +#define WM8400_LDO2_SWI 0x4000 /* LDO2_SWI */ +#define WM8400_LDO2_SWI_MASK 0x4000 /* LDO2_SWI */ +#define WM8400_LDO2_SWI_SHIFT 14 /* LDO2_SWI */ +#define WM8400_LDO2_SWI_WIDTH 1 /* LDO2_SWI */ +#define WM8400_LDO2_OPFLT 0x1000 /* LDO2_OPFLT */ +#define WM8400_LDO2_OPFLT_MASK 0x1000 /* LDO2_OPFLT */ +#define WM8400_LDO2_OPFLT_SHIFT 12 /* LDO2_OPFLT */ +#define WM8400_LDO2_OPFLT_WIDTH 1 /* LDO2_OPFLT */ +#define WM8400_LDO2_ERRACT 0x0800 /* LDO2_ERRACT */ +#define WM8400_LDO2_ERRACT_MASK 0x0800 /* LDO2_ERRACT */ +#define WM8400_LDO2_ERRACT_SHIFT 11 /* LDO2_ERRACT */ +#define WM8400_LDO2_ERRACT_WIDTH 1 /* LDO2_ERRACT */ +#define WM8400_LDO2_HIB_MODE 0x0400 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_HIB_MODE_MASK 0x0400 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_HIB_MODE_SHIFT 10 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_HIB_MODE_WIDTH 1 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_VIMG_MASK 0x03E0 /* LDO2_VIMG - [9:5] */ +#define WM8400_LDO2_VIMG_SHIFT 5 /* LDO2_VIMG - [9:5] */ +#define WM8400_LDO2_VIMG_WIDTH 5 /* LDO2_VIMG - [9:5] */ +#define WM8400_LDO2_VSEL_MASK 0x001F /* LDO2_VSEL - [4:0] */ +#define WM8400_LDO2_VSEL_SHIFT 0 /* LDO2_VSEL - [4:0] */ +#define WM8400_LDO2_VSEL_WIDTH 5 /* LDO2_VSEL - [4:0] */ + +/* + * R67 (0x43) - LDO 3 Control + */ +#define WM8400_LDO3_ENA 0x8000 /* LDO3_ENA */ +#define WM8400_LDO3_ENA_MASK 0x8000 /* LDO3_ENA */ +#define WM8400_LDO3_ENA_SHIFT 15 /* LDO3_ENA */ +#define WM8400_LDO3_ENA_WIDTH 1 /* LDO3_ENA */ +#define WM8400_LDO3_SWI 0x4000 /* LDO3_SWI */ +#define WM8400_LDO3_SWI_MASK 0x4000 /* LDO3_SWI */ +#define WM8400_LDO3_SWI_SHIFT 14 /* LDO3_SWI */ +#define WM8400_LDO3_SWI_WIDTH 1 /* LDO3_SWI */ +#define WM8400_LDO3_OPFLT 0x1000 /* LDO3_OPFLT */ +#define WM8400_LDO3_OPFLT_MASK 0x1000 /* LDO3_OPFLT */ +#define WM8400_LDO3_OPFLT_SHIFT 12 /* LDO3_OPFLT */ +#define WM8400_LDO3_OPFLT_WIDTH 1 /* LDO3_OPFLT */ +#define WM8400_LDO3_ERRACT 0x0800 /* LDO3_ERRACT */ +#define WM8400_LDO3_ERRACT_MASK 0x0800 /* LDO3_ERRACT */ +#define WM8400_LDO3_ERRACT_SHIFT 11 /* LDO3_ERRACT */ +#define WM8400_LDO3_ERRACT_WIDTH 1 /* LDO3_ERRACT */ +#define WM8400_LDO3_HIB_MODE 0x0400 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_HIB_MODE_MASK 0x0400 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_HIB_MODE_SHIFT 10 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_HIB_MODE_WIDTH 1 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_VIMG_MASK 0x03E0 /* LDO3_VIMG - [9:5] */ +#define WM8400_LDO3_VIMG_SHIFT 5 /* LDO3_VIMG - [9:5] */ +#define WM8400_LDO3_VIMG_WIDTH 5 /* LDO3_VIMG - [9:5] */ +#define WM8400_LDO3_VSEL_MASK 0x001F /* LDO3_VSEL - [4:0] */ +#define WM8400_LDO3_VSEL_SHIFT 0 /* LDO3_VSEL - [4:0] */ +#define WM8400_LDO3_VSEL_WIDTH 5 /* LDO3_VSEL - [4:0] */ + +/* + * R68 (0x44) - LDO 4 Control + */ +#define WM8400_LDO4_ENA 0x8000 /* LDO4_ENA */ +#define WM8400_LDO4_ENA_MASK 0x8000 /* LDO4_ENA */ +#define WM8400_LDO4_ENA_SHIFT 15 /* LDO4_ENA */ +#define WM8400_LDO4_ENA_WIDTH 1 /* LDO4_ENA */ +#define WM8400_LDO4_SWI 0x4000 /* LDO4_SWI */ +#define WM8400_LDO4_SWI_MASK 0x4000 /* LDO4_SWI */ +#define WM8400_LDO4_SWI_SHIFT 14 /* LDO4_SWI */ +#define WM8400_LDO4_SWI_WIDTH 1 /* LDO4_SWI */ +#define WM8400_LDO4_OPFLT 0x1000 /* LDO4_OPFLT */ +#define WM8400_LDO4_OPFLT_MASK 0x1000 /* LDO4_OPFLT */ +#define WM8400_LDO4_OPFLT_SHIFT 12 /* LDO4_OPFLT */ +#define WM8400_LDO4_OPFLT_WIDTH 1 /* LDO4_OPFLT */ +#define WM8400_LDO4_ERRACT 0x0800 /* LDO4_ERRACT */ +#define WM8400_LDO4_ERRACT_MASK 0x0800 /* LDO4_ERRACT */ +#define WM8400_LDO4_ERRACT_SHIFT 11 /* LDO4_ERRACT */ +#define WM8400_LDO4_ERRACT_WIDTH 1 /* LDO4_ERRACT */ +#define WM8400_LDO4_HIB_MODE 0x0400 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_HIB_MODE_MASK 0x0400 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_HIB_MODE_SHIFT 10 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_HIB_MODE_WIDTH 1 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_VIMG_MASK 0x03E0 /* LDO4_VIMG - [9:5] */ +#define WM8400_LDO4_VIMG_SHIFT 5 /* LDO4_VIMG - [9:5] */ +#define WM8400_LDO4_VIMG_WIDTH 5 /* LDO4_VIMG - [9:5] */ +#define WM8400_LDO4_VSEL_MASK 0x001F /* LDO4_VSEL - [4:0] */ +#define WM8400_LDO4_VSEL_SHIFT 0 /* LDO4_VSEL - [4:0] */ +#define WM8400_LDO4_VSEL_WIDTH 5 /* LDO4_VSEL - [4:0] */ + +/* + * R70 (0x46) - DCDC1 Control 1 + */ +#define WM8400_DC1_ENA 0x8000 /* DC1_ENA */ +#define WM8400_DC1_ENA_MASK 0x8000 /* DC1_ENA */ +#define WM8400_DC1_ENA_SHIFT 15 /* DC1_ENA */ +#define WM8400_DC1_ENA_WIDTH 1 /* DC1_ENA */ +#define WM8400_DC1_ACTIVE 0x4000 /* DC1_ACTIVE */ +#define WM8400_DC1_ACTIVE_MASK 0x4000 /* DC1_ACTIVE */ +#define WM8400_DC1_ACTIVE_SHIFT 14 /* DC1_ACTIVE */ +#define WM8400_DC1_ACTIVE_WIDTH 1 /* DC1_ACTIVE */ +#define WM8400_DC1_SLEEP 0x2000 /* DC1_SLEEP */ +#define WM8400_DC1_SLEEP_MASK 0x2000 /* DC1_SLEEP */ +#define WM8400_DC1_SLEEP_SHIFT 13 /* DC1_SLEEP */ +#define WM8400_DC1_SLEEP_WIDTH 1 /* DC1_SLEEP */ +#define WM8400_DC1_OPFLT 0x1000 /* DC1_OPFLT */ +#define WM8400_DC1_OPFLT_MASK 0x1000 /* DC1_OPFLT */ +#define WM8400_DC1_OPFLT_SHIFT 12 /* DC1_OPFLT */ +#define WM8400_DC1_OPFLT_WIDTH 1 /* DC1_OPFLT */ +#define WM8400_DC1_ERRACT 0x0800 /* DC1_ERRACT */ +#define WM8400_DC1_ERRACT_MASK 0x0800 /* DC1_ERRACT */ +#define WM8400_DC1_ERRACT_SHIFT 11 /* DC1_ERRACT */ +#define WM8400_DC1_ERRACT_WIDTH 1 /* DC1_ERRACT */ +#define WM8400_DC1_HIB_MODE 0x0400 /* DC1_HIB_MODE */ +#define WM8400_DC1_HIB_MODE_MASK 0x0400 /* DC1_HIB_MODE */ +#define WM8400_DC1_HIB_MODE_SHIFT 10 /* DC1_HIB_MODE */ +#define WM8400_DC1_HIB_MODE_WIDTH 1 /* DC1_HIB_MODE */ +#define WM8400_DC1_SOFTST_MASK 0x0300 /* DC1_SOFTST - [9:8] */ +#define WM8400_DC1_SOFTST_SHIFT 8 /* DC1_SOFTST - [9:8] */ +#define WM8400_DC1_SOFTST_WIDTH 2 /* DC1_SOFTST - [9:8] */ +#define WM8400_DC1_OV_PROT 0x0080 /* DC1_OV_PROT */ +#define WM8400_DC1_OV_PROT_MASK 0x0080 /* DC1_OV_PROT */ +#define WM8400_DC1_OV_PROT_SHIFT 7 /* DC1_OV_PROT */ +#define WM8400_DC1_OV_PROT_WIDTH 1 /* DC1_OV_PROT */ +#define WM8400_DC1_VSEL_MASK 0x007F /* DC1_VSEL - [6:0] */ +#define WM8400_DC1_VSEL_SHIFT 0 /* DC1_VSEL - [6:0] */ +#define WM8400_DC1_VSEL_WIDTH 7 /* DC1_VSEL - [6:0] */ + +/* + * R71 (0x47) - DCDC1 Control 2 + */ +#define WM8400_DC1_FRC_PWM 0x2000 /* DC1_FRC_PWM */ +#define WM8400_DC1_FRC_PWM_MASK 0x2000 /* DC1_FRC_PWM */ +#define WM8400_DC1_FRC_PWM_SHIFT 13 /* DC1_FRC_PWM */ +#define WM8400_DC1_FRC_PWM_WIDTH 1 /* DC1_FRC_PWM */ +#define WM8400_DC1_STBY_LIM_MASK 0x0300 /* DC1_STBY_LIM - [9:8] */ +#define WM8400_DC1_STBY_LIM_SHIFT 8 /* DC1_STBY_LIM - [9:8] */ +#define WM8400_DC1_STBY_LIM_WIDTH 2 /* DC1_STBY_LIM - [9:8] */ +#define WM8400_DC1_ACT_LIM 0x0080 /* DC1_ACT_LIM */ +#define WM8400_DC1_ACT_LIM_MASK 0x0080 /* DC1_ACT_LIM */ +#define WM8400_DC1_ACT_LIM_SHIFT 7 /* DC1_ACT_LIM */ +#define WM8400_DC1_ACT_LIM_WIDTH 1 /* DC1_ACT_LIM */ +#define WM8400_DC1_VIMG_MASK 0x007F /* DC1_VIMG - [6:0] */ +#define WM8400_DC1_VIMG_SHIFT 0 /* DC1_VIMG - [6:0] */ +#define WM8400_DC1_VIMG_WIDTH 7 /* DC1_VIMG - [6:0] */ + +/* + * R72 (0x48) - DCDC2 Control 1 + */ +#define WM8400_DC2_ENA 0x8000 /* DC2_ENA */ +#define WM8400_DC2_ENA_MASK 0x8000 /* DC2_ENA */ +#define WM8400_DC2_ENA_SHIFT 15 /* DC2_ENA */ +#define WM8400_DC2_ENA_WIDTH 1 /* DC2_ENA */ +#define WM8400_DC2_ACTIVE 0x4000 /* DC2_ACTIVE */ +#define WM8400_DC2_ACTIVE_MASK 0x4000 /* DC2_ACTIVE */ +#define WM8400_DC2_ACTIVE_SHIFT 14 /* DC2_ACTIVE */ +#define WM8400_DC2_ACTIVE_WIDTH 1 /* DC2_ACTIVE */ +#define WM8400_DC2_SLEEP 0x2000 /* DC2_SLEEP */ +#define WM8400_DC2_SLEEP_MASK 0x2000 /* DC2_SLEEP */ +#define WM8400_DC2_SLEEP_SHIFT 13 /* DC2_SLEEP */ +#define WM8400_DC2_SLEEP_WIDTH 1 /* DC2_SLEEP */ +#define WM8400_DC2_OPFLT 0x1000 /* DC2_OPFLT */ +#define WM8400_DC2_OPFLT_MASK 0x1000 /* DC2_OPFLT */ +#define WM8400_DC2_OPFLT_SHIFT 12 /* DC2_OPFLT */ +#define WM8400_DC2_OPFLT_WIDTH 1 /* DC2_OPFLT */ +#define WM8400_DC2_ERRACT 0x0800 /* DC2_ERRACT */ +#define WM8400_DC2_ERRACT_MASK 0x0800 /* DC2_ERRACT */ +#define WM8400_DC2_ERRACT_SHIFT 11 /* DC2_ERRACT */ +#define WM8400_DC2_ERRACT_WIDTH 1 /* DC2_ERRACT */ +#define WM8400_DC2_HIB_MODE 0x0400 /* DC2_HIB_MODE */ +#define WM8400_DC2_HIB_MODE_MASK 0x0400 /* DC2_HIB_MODE */ +#define WM8400_DC2_HIB_MODE_SHIFT 10 /* DC2_HIB_MODE */ +#define WM8400_DC2_HIB_MODE_WIDTH 1 /* DC2_HIB_MODE */ +#define WM8400_DC2_SOFTST_MASK 0x0300 /* DC2_SOFTST - [9:8] */ +#define WM8400_DC2_SOFTST_SHIFT 8 /* DC2_SOFTST - [9:8] */ +#define WM8400_DC2_SOFTST_WIDTH 2 /* DC2_SOFTST - [9:8] */ +#define WM8400_DC2_OV_PROT 0x0080 /* DC2_OV_PROT */ +#define WM8400_DC2_OV_PROT_MASK 0x0080 /* DC2_OV_PROT */ +#define WM8400_DC2_OV_PROT_SHIFT 7 /* DC2_OV_PROT */ +#define WM8400_DC2_OV_PROT_WIDTH 1 /* DC2_OV_PROT */ +#define WM8400_DC2_VSEL_MASK 0x007F /* DC2_VSEL - [6:0] */ +#define WM8400_DC2_VSEL_SHIFT 0 /* DC2_VSEL - [6:0] */ +#define WM8400_DC2_VSEL_WIDTH 7 /* DC2_VSEL - [6:0] */ + +/* + * R73 (0x49) - DCDC2 Control 2 + */ +#define WM8400_DC2_FRC_PWM 0x2000 /* DC2_FRC_PWM */ +#define WM8400_DC2_FRC_PWM_MASK 0x2000 /* DC2_FRC_PWM */ +#define WM8400_DC2_FRC_PWM_SHIFT 13 /* DC2_FRC_PWM */ +#define WM8400_DC2_FRC_PWM_WIDTH 1 /* DC2_FRC_PWM */ +#define WM8400_DC2_STBY_LIM_MASK 0x0300 /* DC2_STBY_LIM - [9:8] */ +#define WM8400_DC2_STBY_LIM_SHIFT 8 /* DC2_STBY_LIM - [9:8] */ +#define WM8400_DC2_STBY_LIM_WIDTH 2 /* DC2_STBY_LIM - [9:8] */ +#define WM8400_DC2_ACT_LIM 0x0080 /* DC2_ACT_LIM */ +#define WM8400_DC2_ACT_LIM_MASK 0x0080 /* DC2_ACT_LIM */ +#define WM8400_DC2_ACT_LIM_SHIFT 7 /* DC2_ACT_LIM */ +#define WM8400_DC2_ACT_LIM_WIDTH 1 /* DC2_ACT_LIM */ +#define WM8400_DC2_VIMG_MASK 0x007F /* DC2_VIMG - [6:0] */ +#define WM8400_DC2_VIMG_SHIFT 0 /* DC2_VIMG - [6:0] */ +#define WM8400_DC2_VIMG_WIDTH 7 /* DC2_VIMG - [6:0] */ + +/* + * R75 (0x4B) - Interface + */ +#define WM8400_AUTOINC 0x0008 /* AUTOINC */ +#define WM8400_AUTOINC_MASK 0x0008 /* AUTOINC */ +#define WM8400_AUTOINC_SHIFT 3 /* AUTOINC */ +#define WM8400_AUTOINC_WIDTH 1 /* AUTOINC */ +#define WM8400_ARA_ENA 0x0004 /* ARA_ENA */ +#define WM8400_ARA_ENA_MASK 0x0004 /* ARA_ENA */ +#define WM8400_ARA_ENA_SHIFT 2 /* ARA_ENA */ +#define WM8400_ARA_ENA_WIDTH 1 /* ARA_ENA */ +#define WM8400_SPI_CFG 0x0002 /* SPI_CFG */ +#define WM8400_SPI_CFG_MASK 0x0002 /* SPI_CFG */ +#define WM8400_SPI_CFG_SHIFT 1 /* SPI_CFG */ +#define WM8400_SPI_CFG_WIDTH 1 /* SPI_CFG */ + +/* + * R76 (0x4C) - PM GENERAL + */ +#define WM8400_CODEC_SOFTST 0x8000 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTST_MASK 0x8000 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTST_SHIFT 15 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTST_WIDTH 1 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTSD 0x4000 /* CODEC_SOFTSD */ +#define WM8400_CODEC_SOFTSD_MASK 0x4000 /* CODEC_SOFTSD */ +#define WM8400_CODEC_SOFTSD_SHIFT 14 /* CODEC_SOFTSD */ +#define WM8400_CODEC_SOFTSD_WIDTH 1 /* CODEC_SOFTSD */ +#define WM8400_CHIP_SOFTSD 0x2000 /* CHIP_SOFTSD */ +#define WM8400_CHIP_SOFTSD_MASK 0x2000 /* CHIP_SOFTSD */ +#define WM8400_CHIP_SOFTSD_SHIFT 13 /* CHIP_SOFTSD */ +#define WM8400_CHIP_SOFTSD_WIDTH 1 /* CHIP_SOFTSD */ +#define WM8400_DSLEEP1_POL 0x0008 /* DSLEEP1_POL */ +#define WM8400_DSLEEP1_POL_MASK 0x0008 /* DSLEEP1_POL */ +#define WM8400_DSLEEP1_POL_SHIFT 3 /* DSLEEP1_POL */ +#define WM8400_DSLEEP1_POL_WIDTH 1 /* DSLEEP1_POL */ +#define WM8400_DSLEEP2_POL 0x0004 /* DSLEEP2_POL */ +#define WM8400_DSLEEP2_POL_MASK 0x0004 /* DSLEEP2_POL */ +#define WM8400_DSLEEP2_POL_SHIFT 2 /* DSLEEP2_POL */ +#define WM8400_DSLEEP2_POL_WIDTH 1 /* DSLEEP2_POL */ +#define WM8400_PWR_STATE_MASK 0x0003 /* PWR_STATE - [1:0] */ +#define WM8400_PWR_STATE_SHIFT 0 /* PWR_STATE - [1:0] */ +#define WM8400_PWR_STATE_WIDTH 2 /* PWR_STATE - [1:0] */ + +/* + * R78 (0x4E) - PM Shutdown Control + */ +#define WM8400_CHIP_GT150_ERRACT 0x0200 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT150_ERRACT_MASK 0x0200 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT150_ERRACT_SHIFT 9 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT150_ERRACT_WIDTH 1 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT 0x0100 /* CHIP_GT115_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT_MASK 0x0100 /* CHIP_GT115_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT_SHIFT 8 /* CHIP_GT115_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT_WIDTH 1 /* CHIP_GT115_ERRACT */ +#define WM8400_LINE_CMP_ERRACT 0x0080 /* LINE_CMP_ERRACT */ +#define WM8400_LINE_CMP_ERRACT_MASK 0x0080 /* LINE_CMP_ERRACT */ +#define WM8400_LINE_CMP_ERRACT_SHIFT 7 /* LINE_CMP_ERRACT */ +#define WM8400_LINE_CMP_ERRACT_WIDTH 1 /* LINE_CMP_ERRACT */ +#define WM8400_UVLO_ERRACT 0x0040 /* UVLO_ERRACT */ +#define WM8400_UVLO_ERRACT_MASK 0x0040 /* UVLO_ERRACT */ +#define WM8400_UVLO_ERRACT_SHIFT 6 /* UVLO_ERRACT */ +#define WM8400_UVLO_ERRACT_WIDTH 1 /* UVLO_ERRACT */ + +/* + * R79 (0x4F) - Interrupt Status 1 + */ +#define WM8400_MICD_CINT 0x8000 /* MICD_CINT */ +#define WM8400_MICD_CINT_MASK 0x8000 /* MICD_CINT */ +#define WM8400_MICD_CINT_SHIFT 15 /* MICD_CINT */ +#define WM8400_MICD_CINT_WIDTH 1 /* MICD_CINT */ +#define WM8400_MICSCD_CINT 0x4000 /* MICSCD_CINT */ +#define WM8400_MICSCD_CINT_MASK 0x4000 /* MICSCD_CINT */ +#define WM8400_MICSCD_CINT_SHIFT 14 /* MICSCD_CINT */ +#define WM8400_MICSCD_CINT_WIDTH 1 /* MICSCD_CINT */ +#define WM8400_JDL_CINT 0x2000 /* JDL_CINT */ +#define WM8400_JDL_CINT_MASK 0x2000 /* JDL_CINT */ +#define WM8400_JDL_CINT_SHIFT 13 /* JDL_CINT */ +#define WM8400_JDL_CINT_WIDTH 1 /* JDL_CINT */ +#define WM8400_JDR_CINT 0x1000 /* JDR_CINT */ +#define WM8400_JDR_CINT_MASK 0x1000 /* JDR_CINT */ +#define WM8400_JDR_CINT_SHIFT 12 /* JDR_CINT */ +#define WM8400_JDR_CINT_WIDTH 1 /* JDR_CINT */ +#define WM8400_CODEC_SEQ_END_EINT 0x0800 /* CODEC_SEQ_END_EINT */ +#define WM8400_CODEC_SEQ_END_EINT_MASK 0x0800 /* CODEC_SEQ_END_EINT */ +#define WM8400_CODEC_SEQ_END_EINT_SHIFT 11 /* CODEC_SEQ_END_EINT */ +#define WM8400_CODEC_SEQ_END_EINT_WIDTH 1 /* CODEC_SEQ_END_EINT */ +#define WM8400_CDEL_TO_EINT 0x0400 /* CDEL_TO_EINT */ +#define WM8400_CDEL_TO_EINT_MASK 0x0400 /* CDEL_TO_EINT */ +#define WM8400_CDEL_TO_EINT_SHIFT 10 /* CDEL_TO_EINT */ +#define WM8400_CDEL_TO_EINT_WIDTH 1 /* CDEL_TO_EINT */ +#define WM8400_CHIP_GT150_EINT 0x0200 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT150_EINT_MASK 0x0200 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT150_EINT_SHIFT 9 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT150_EINT_WIDTH 1 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT115_EINT 0x0100 /* CHIP_GT115_EINT */ +#define WM8400_CHIP_GT115_EINT_MASK 0x0100 /* CHIP_GT115_EINT */ +#define WM8400_CHIP_GT115_EINT_SHIFT 8 /* CHIP_GT115_EINT */ +#define WM8400_CHIP_GT115_EINT_WIDTH 1 /* CHIP_GT115_EINT */ +#define WM8400_LINE_CMP_EINT 0x0080 /* LINE_CMP_EINT */ +#define WM8400_LINE_CMP_EINT_MASK 0x0080 /* LINE_CMP_EINT */ +#define WM8400_LINE_CMP_EINT_SHIFT 7 /* LINE_CMP_EINT */ +#define WM8400_LINE_CMP_EINT_WIDTH 1 /* LINE_CMP_EINT */ +#define WM8400_UVLO_EINT 0x0040 /* UVLO_EINT */ +#define WM8400_UVLO_EINT_MASK 0x0040 /* UVLO_EINT */ +#define WM8400_UVLO_EINT_SHIFT 6 /* UVLO_EINT */ +#define WM8400_UVLO_EINT_WIDTH 1 /* UVLO_EINT */ +#define WM8400_DC2_UV_EINT 0x0020 /* DC2_UV_EINT */ +#define WM8400_DC2_UV_EINT_MASK 0x0020 /* DC2_UV_EINT */ +#define WM8400_DC2_UV_EINT_SHIFT 5 /* DC2_UV_EINT */ +#define WM8400_DC2_UV_EINT_WIDTH 1 /* DC2_UV_EINT */ +#define WM8400_DC1_UV_EINT 0x0010 /* DC1_UV_EINT */ +#define WM8400_DC1_UV_EINT_MASK 0x0010 /* DC1_UV_EINT */ +#define WM8400_DC1_UV_EINT_SHIFT 4 /* DC1_UV_EINT */ +#define WM8400_DC1_UV_EINT_WIDTH 1 /* DC1_UV_EINT */ +#define WM8400_LDO4_UV_EINT 0x0008 /* LDO4_UV_EINT */ +#define WM8400_LDO4_UV_EINT_MASK 0x0008 /* LDO4_UV_EINT */ +#define WM8400_LDO4_UV_EINT_SHIFT 3 /* LDO4_UV_EINT */ +#define WM8400_LDO4_UV_EINT_WIDTH 1 /* LDO4_UV_EINT */ +#define WM8400_LDO3_UV_EINT 0x0004 /* LDO3_UV_EINT */ +#define WM8400_LDO3_UV_EINT_MASK 0x0004 /* LDO3_UV_EINT */ +#define WM8400_LDO3_UV_EINT_SHIFT 2 /* LDO3_UV_EINT */ +#define WM8400_LDO3_UV_EINT_WIDTH 1 /* LDO3_UV_EINT */ +#define WM8400_LDO2_UV_EINT 0x0002 /* LDO2_UV_EINT */ +#define WM8400_LDO2_UV_EINT_MASK 0x0002 /* LDO2_UV_EINT */ +#define WM8400_LDO2_UV_EINT_SHIFT 1 /* LDO2_UV_EINT */ +#define WM8400_LDO2_UV_EINT_WIDTH 1 /* LDO2_UV_EINT */ +#define WM8400_LDO1_UV_EINT 0x0001 /* LDO1_UV_EINT */ +#define WM8400_LDO1_UV_EINT_MASK 0x0001 /* LDO1_UV_EINT */ +#define WM8400_LDO1_UV_EINT_SHIFT 0 /* LDO1_UV_EINT */ +#define WM8400_LDO1_UV_EINT_WIDTH 1 /* LDO1_UV_EINT */ + +/* + * R80 (0x50) - Interrupt Status 1 Mask + */ +#define WM8400_IM_MICD_CINT 0x8000 /* IM_MICD_CINT */ +#define WM8400_IM_MICD_CINT_MASK 0x8000 /* IM_MICD_CINT */ +#define WM8400_IM_MICD_CINT_SHIFT 15 /* IM_MICD_CINT */ +#define WM8400_IM_MICD_CINT_WIDTH 1 /* IM_MICD_CINT */ +#define WM8400_IM_MICSCD_CINT 0x4000 /* IM_MICSCD_CINT */ +#define WM8400_IM_MICSCD_CINT_MASK 0x4000 /* IM_MICSCD_CINT */ +#define WM8400_IM_MICSCD_CINT_SHIFT 14 /* IM_MICSCD_CINT */ +#define WM8400_IM_MICSCD_CINT_WIDTH 1 /* IM_MICSCD_CINT */ +#define WM8400_IM_JDL_CINT 0x2000 /* IM_JDL_CINT */ +#define WM8400_IM_JDL_CINT_MASK 0x2000 /* IM_JDL_CINT */ +#define WM8400_IM_JDL_CINT_SHIFT 13 /* IM_JDL_CINT */ +#define WM8400_IM_JDL_CINT_WIDTH 1 /* IM_JDL_CINT */ +#define WM8400_IM_JDR_CINT 0x1000 /* IM_JDR_CINT */ +#define WM8400_IM_JDR_CINT_MASK 0x1000 /* IM_JDR_CINT */ +#define WM8400_IM_JDR_CINT_SHIFT 12 /* IM_JDR_CINT */ +#define WM8400_IM_JDR_CINT_WIDTH 1 /* IM_JDR_CINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT 0x0800 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT_MASK 0x0800 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT_SHIFT 11 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT_WIDTH 1 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CDEL_TO_EINT 0x0400 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CDEL_TO_EINT_MASK 0x0400 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CDEL_TO_EINT_SHIFT 10 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CDEL_TO_EINT_WIDTH 1 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CHIP_GT150_EINT 0x0200 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT150_EINT_MASK 0x0200 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT150_EINT_SHIFT 9 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT150_EINT_WIDTH 1 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT115_EINT 0x0100 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_CHIP_GT115_EINT_MASK 0x0100 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_CHIP_GT115_EINT_SHIFT 8 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_CHIP_GT115_EINT_WIDTH 1 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_LINE_CMP_EINT 0x0080 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_LINE_CMP_EINT_MASK 0x0080 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_LINE_CMP_EINT_SHIFT 7 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_LINE_CMP_EINT_WIDTH 1 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_UVLO_EINT 0x0040 /* IM_UVLO_EINT */ +#define WM8400_IM_UVLO_EINT_MASK 0x0040 /* IM_UVLO_EINT */ +#define WM8400_IM_UVLO_EINT_SHIFT 6 /* IM_UVLO_EINT */ +#define WM8400_IM_UVLO_EINT_WIDTH 1 /* IM_UVLO_EINT */ +#define WM8400_IM_DC2_UV_EINT 0x0020 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC2_UV_EINT_MASK 0x0020 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC2_UV_EINT_SHIFT 5 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC2_UV_EINT_WIDTH 1 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT 0x0010 /* IM_DC1_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT_MASK 0x0010 /* IM_DC1_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT_SHIFT 4 /* IM_DC1_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT_WIDTH 1 /* IM_DC1_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT 0x0008 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT_MASK 0x0008 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT_SHIFT 3 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT_WIDTH 1 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT 0x0004 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT_MASK 0x0004 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT_SHIFT 2 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT_WIDTH 1 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT 0x0002 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT_MASK 0x0002 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT_SHIFT 1 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT_WIDTH 1 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT 0x0001 /* IM_LDO1_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT_MASK 0x0001 /* IM_LDO1_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT_SHIFT 0 /* IM_LDO1_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT_WIDTH 1 /* IM_LDO1_UV_EINT */ + +/* + * R81 (0x51) - Interrupt Levels + */ +#define WM8400_MICD_LVL 0x8000 /* MICD_LVL */ +#define WM8400_MICD_LVL_MASK 0x8000 /* MICD_LVL */ +#define WM8400_MICD_LVL_SHIFT 15 /* MICD_LVL */ +#define WM8400_MICD_LVL_WIDTH 1 /* MICD_LVL */ +#define WM8400_MICSCD_LVL 0x4000 /* MICSCD_LVL */ +#define WM8400_MICSCD_LVL_MASK 0x4000 /* MICSCD_LVL */ +#define WM8400_MICSCD_LVL_SHIFT 14 /* MICSCD_LVL */ +#define WM8400_MICSCD_LVL_WIDTH 1 /* MICSCD_LVL */ +#define WM8400_JDL_LVL 0x2000 /* JDL_LVL */ +#define WM8400_JDL_LVL_MASK 0x2000 /* JDL_LVL */ +#define WM8400_JDL_LVL_SHIFT 13 /* JDL_LVL */ +#define WM8400_JDL_LVL_WIDTH 1 /* JDL_LVL */ +#define WM8400_JDR_LVL 0x1000 /* JDR_LVL */ +#define WM8400_JDR_LVL_MASK 0x1000 /* JDR_LVL */ +#define WM8400_JDR_LVL_SHIFT 12 /* JDR_LVL */ +#define WM8400_JDR_LVL_WIDTH 1 /* JDR_LVL */ +#define WM8400_CODEC_SEQ_END_LVL 0x0800 /* CODEC_SEQ_END_LVL */ +#define WM8400_CODEC_SEQ_END_LVL_MASK 0x0800 /* CODEC_SEQ_END_LVL */ +#define WM8400_CODEC_SEQ_END_LVL_SHIFT 11 /* CODEC_SEQ_END_LVL */ +#define WM8400_CODEC_SEQ_END_LVL_WIDTH 1 /* CODEC_SEQ_END_LVL */ +#define WM8400_CDEL_TO_LVL 0x0400 /* CDEL_TO_LVL */ +#define WM8400_CDEL_TO_LVL_MASK 0x0400 /* CDEL_TO_LVL */ +#define WM8400_CDEL_TO_LVL_SHIFT 10 /* CDEL_TO_LVL */ +#define WM8400_CDEL_TO_LVL_WIDTH 1 /* CDEL_TO_LVL */ +#define WM8400_CHIP_GT150_LVL 0x0200 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT150_LVL_MASK 0x0200 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT150_LVL_SHIFT 9 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT150_LVL_WIDTH 1 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT115_LVL 0x0100 /* CHIP_GT115_LVL */ +#define WM8400_CHIP_GT115_LVL_MASK 0x0100 /* CHIP_GT115_LVL */ +#define WM8400_CHIP_GT115_LVL_SHIFT 8 /* CHIP_GT115_LVL */ +#define WM8400_CHIP_GT115_LVL_WIDTH 1 /* CHIP_GT115_LVL */ +#define WM8400_LINE_CMP_LVL 0x0080 /* LINE_CMP_LVL */ +#define WM8400_LINE_CMP_LVL_MASK 0x0080 /* LINE_CMP_LVL */ +#define WM8400_LINE_CMP_LVL_SHIFT 7 /* LINE_CMP_LVL */ +#define WM8400_LINE_CMP_LVL_WIDTH 1 /* LINE_CMP_LVL */ +#define WM8400_UVLO_LVL 0x0040 /* UVLO_LVL */ +#define WM8400_UVLO_LVL_MASK 0x0040 /* UVLO_LVL */ +#define WM8400_UVLO_LVL_SHIFT 6 /* UVLO_LVL */ +#define WM8400_UVLO_LVL_WIDTH 1 /* UVLO_LVL */ +#define WM8400_DC2_UV_LVL 0x0020 /* DC2_UV_LVL */ +#define WM8400_DC2_UV_LVL_MASK 0x0020 /* DC2_UV_LVL */ +#define WM8400_DC2_UV_LVL_SHIFT 5 /* DC2_UV_LVL */ +#define WM8400_DC2_UV_LVL_WIDTH 1 /* DC2_UV_LVL */ +#define WM8400_DC1_UV_LVL 0x0010 /* DC1_UV_LVL */ +#define WM8400_DC1_UV_LVL_MASK 0x0010 /* DC1_UV_LVL */ +#define WM8400_DC1_UV_LVL_SHIFT 4 /* DC1_UV_LVL */ +#define WM8400_DC1_UV_LVL_WIDTH 1 /* DC1_UV_LVL */ +#define WM8400_LDO4_UV_LVL 0x0008 /* LDO4_UV_LVL */ +#define WM8400_LDO4_UV_LVL_MASK 0x0008 /* LDO4_UV_LVL */ +#define WM8400_LDO4_UV_LVL_SHIFT 3 /* LDO4_UV_LVL */ +#define WM8400_LDO4_UV_LVL_WIDTH 1 /* LDO4_UV_LVL */ +#define WM8400_LDO3_UV_LVL 0x0004 /* LDO3_UV_LVL */ +#define WM8400_LDO3_UV_LVL_MASK 0x0004 /* LDO3_UV_LVL */ +#define WM8400_LDO3_UV_LVL_SHIFT 2 /* LDO3_UV_LVL */ +#define WM8400_LDO3_UV_LVL_WIDTH 1 /* LDO3_UV_LVL */ +#define WM8400_LDO2_UV_LVL 0x0002 /* LDO2_UV_LVL */ +#define WM8400_LDO2_UV_LVL_MASK 0x0002 /* LDO2_UV_LVL */ +#define WM8400_LDO2_UV_LVL_SHIFT 1 /* LDO2_UV_LVL */ +#define WM8400_LDO2_UV_LVL_WIDTH 1 /* LDO2_UV_LVL */ +#define WM8400_LDO1_UV_LVL 0x0001 /* LDO1_UV_LVL */ +#define WM8400_LDO1_UV_LVL_MASK 0x0001 /* LDO1_UV_LVL */ +#define WM8400_LDO1_UV_LVL_SHIFT 0 /* LDO1_UV_LVL */ +#define WM8400_LDO1_UV_LVL_WIDTH 1 /* LDO1_UV_LVL */ + +/* + * R82 (0x52) - Shutdown Reason + */ +#define WM8400_SDR_CHIP_SOFTSD 0x2000 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_CHIP_SOFTSD_MASK 0x2000 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_CHIP_SOFTSD_SHIFT 13 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_CHIP_SOFTSD_WIDTH 1 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_NPDN 0x0800 /* SDR_NPDN */ +#define WM8400_SDR_NPDN_MASK 0x0800 /* SDR_NPDN */ +#define WM8400_SDR_NPDN_SHIFT 11 /* SDR_NPDN */ +#define WM8400_SDR_NPDN_WIDTH 1 /* SDR_NPDN */ +#define WM8400_SDR_CHIP_GT150 0x0200 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT150_MASK 0x0200 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT150_SHIFT 9 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT150_WIDTH 1 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT115 0x0100 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_CHIP_GT115_MASK 0x0100 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_CHIP_GT115_SHIFT 8 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_CHIP_GT115_WIDTH 1 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_LINE_CMP 0x0080 /* SDR_LINE_CMP */ +#define WM8400_SDR_LINE_CMP_MASK 0x0080 /* SDR_LINE_CMP */ +#define WM8400_SDR_LINE_CMP_SHIFT 7 /* SDR_LINE_CMP */ +#define WM8400_SDR_LINE_CMP_WIDTH 1 /* SDR_LINE_CMP */ +#define WM8400_SDR_UVLO 0x0040 /* SDR_UVLO */ +#define WM8400_SDR_UVLO_MASK 0x0040 /* SDR_UVLO */ +#define WM8400_SDR_UVLO_SHIFT 6 /* SDR_UVLO */ +#define WM8400_SDR_UVLO_WIDTH 1 /* SDR_UVLO */ +#define WM8400_SDR_DC2_UV 0x0020 /* SDR_DC2_UV */ +#define WM8400_SDR_DC2_UV_MASK 0x0020 /* SDR_DC2_UV */ +#define WM8400_SDR_DC2_UV_SHIFT 5 /* SDR_DC2_UV */ +#define WM8400_SDR_DC2_UV_WIDTH 1 /* SDR_DC2_UV */ +#define WM8400_SDR_DC1_UV 0x0010 /* SDR_DC1_UV */ +#define WM8400_SDR_DC1_UV_MASK 0x0010 /* SDR_DC1_UV */ +#define WM8400_SDR_DC1_UV_SHIFT 4 /* SDR_DC1_UV */ +#define WM8400_SDR_DC1_UV_WIDTH 1 /* SDR_DC1_UV */ +#define WM8400_SDR_LDO4_UV 0x0008 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO4_UV_MASK 0x0008 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO4_UV_SHIFT 3 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO4_UV_WIDTH 1 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO3_UV 0x0004 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO3_UV_MASK 0x0004 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO3_UV_SHIFT 2 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO3_UV_WIDTH 1 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO2_UV 0x0002 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO2_UV_MASK 0x0002 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO2_UV_SHIFT 1 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO2_UV_WIDTH 1 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO1_UV 0x0001 /* SDR_LDO1_UV */ +#define WM8400_SDR_LDO1_UV_MASK 0x0001 /* SDR_LDO1_UV */ +#define WM8400_SDR_LDO1_UV_SHIFT 0 /* SDR_LDO1_UV */ +#define WM8400_SDR_LDO1_UV_WIDTH 1 /* SDR_LDO1_UV */ + +/* + * R84 (0x54) - Line Circuits + */ +#define WM8400_BG_LINE_COMP 0x8000 /* BG_LINE_COMP */ +#define WM8400_BG_LINE_COMP_MASK 0x8000 /* BG_LINE_COMP */ +#define WM8400_BG_LINE_COMP_SHIFT 15 /* BG_LINE_COMP */ +#define WM8400_BG_LINE_COMP_WIDTH 1 /* BG_LINE_COMP */ +#define WM8400_LINE_CMP_VTHI_MASK 0x00F0 /* LINE_CMP_VTHI - [7:4] */ +#define WM8400_LINE_CMP_VTHI_SHIFT 4 /* LINE_CMP_VTHI - [7:4] */ +#define WM8400_LINE_CMP_VTHI_WIDTH 4 /* LINE_CMP_VTHI - [7:4] */ +#define WM8400_LINE_CMP_VTHD_MASK 0x000F /* LINE_CMP_VTHD - [3:0] */ +#define WM8400_LINE_CMP_VTHD_SHIFT 0 /* LINE_CMP_VTHD - [3:0] */ +#define WM8400_LINE_CMP_VTHD_WIDTH 4 /* LINE_CMP_VTHD - [3:0] */ + +u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg); +int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data); +int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val); + +#endif -- cgit v1.2.3 From 42fad570b666256a3fd009e23e74cbb365a29ca8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 11 Sep 2008 11:12:01 +0100 Subject: regulator: Add WM8400 regulator support The WM8400 provides two programmable DCDC step-down (buck) convertors and four low-dropout (LDO) regulators. This driver provides support for runtime managment of these in the standard regulator API. Support for configuration of the suspend and hibernate mode behaviour of the regulators is not yet included. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/wm8400-regulator.c | 368 +++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8400-private.h | 1 + include/linux/mfd/wm8400.h | 40 ++++ 5 files changed, 417 insertions(+) create mode 100644 drivers/regulator/wm8400-regulator.c create mode 100644 include/linux/mfd/wm8400.h (limited to 'include/linux') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index a656128f1fdd..99906872cb91 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -56,4 +56,11 @@ config REGULATOR_BQ24022 charging select between 100 mA and 500 mA charging current limit. +config REGULATOR_WM8400 + tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC" + depends on MFD_WM8400 + select REGULATOR + help + This driver provides support for the voltage regulators of the + WM8400 AudioPlus PMIC. endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ac2c64efe65c..3c6ac73af152 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c new file mode 100644 index 000000000000..48b372e038a8 --- /dev/null +++ b/drivers/regulator/wm8400-regulator.c @@ -0,0 +1,368 @@ +/* + * Regulator support for WM8400 + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +static int wm8400_ldo_is_enabled(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + + val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); + return (val & WM8400_LDO1_ENA) != 0; +} + +static int wm8400_ldo_enable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + + return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), + WM8400_LDO1_ENA, WM8400_LDO1_ENA); +} + +static int wm8400_ldo_disable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + + return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), + WM8400_LDO1_ENA, 0); +} + +static int wm8400_ldo_get_voltage(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + + val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); + val &= WM8400_LDO1_VSEL_MASK; + + if (val < 15) + return 900000 + (val * 50000); + else + return 1600000 + ((val - 14) * 100000); +} + +static int wm8400_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + + if (min_uV < 900000 || min_uV > 3300000) + return -EINVAL; + + if (min_uV < 1700000) { + /* Steps of 50mV from 900mV; */ + val = (min_uV - 850001) / 50000; + + if ((val * 50000) + 900000 > max_uV) + return -EINVAL; + BUG_ON((val * 50000) + 900000 < min_uV); + } else { + /* Steps of 100mV from 1700mV */ + val = ((min_uV - 1600001) / 100000); + + if ((val * 100000) + 1700000 > max_uV) + return -EINVAL; + BUG_ON((val * 100000) + 1700000 < min_uV); + + val += 0xf; + } + + return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), + WM8400_LDO1_VSEL_MASK, val); +} + +static struct regulator_ops wm8400_ldo_ops = { + .is_enabled = wm8400_ldo_is_enabled, + .enable = wm8400_ldo_enable, + .disable = wm8400_ldo_disable, + .get_voltage = wm8400_ldo_get_voltage, + .set_voltage = wm8400_ldo_set_voltage, +}; + +static int wm8400_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + u16 val; + + val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); + return (val & WM8400_DC1_ENA) != 0; +} + +static int wm8400_dcdc_enable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ENA, WM8400_DC1_ENA); +} + +static int wm8400_dcdc_disable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ENA, 0); +} + +static int wm8400_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); + val &= WM8400_DC1_VSEL_MASK; + + return 850000 + (25000 * val); +} + +static int wm8400_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + if (min_uV < 850000) + return -EINVAL; + + val = (min_uV - 825001) / 25000; + + if (850000 + (25000 * val) > max_uV) + return -EINVAL; + BUG_ON(850000 + (25000 * val) < min_uV); + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_VSEL_MASK, val); +} + +static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + u16 data[2]; + int ret; + + ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2, + data); + if (ret != 0) + return 0; + + /* Datasheet: hibernate */ + if (data[0] & WM8400_DC1_SLEEP) + return REGULATOR_MODE_STANDBY; + + /* Datasheet: standby */ + if (!(data[0] & WM8400_DC1_ACTIVE)) + return REGULATOR_MODE_IDLE; + + /* Datasheet: active with or without force PWM */ + if (data[1] & WM8400_DC1_FRC_PWM) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + int ret; + + switch (mode) { + case REGULATOR_MODE_FAST: + /* Datasheet: active with force PWM */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, + WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM); + if (ret != 0) + return ret; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, + WM8400_DC1_ACTIVE); + + case REGULATOR_MODE_NORMAL: + /* Datasheet: active */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, + WM8400_DC1_FRC_PWM, 0); + if (ret != 0) + return ret; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, + WM8400_DC1_ACTIVE); + + case REGULATOR_MODE_IDLE: + /* Datasheet: standby */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE, 0); + if (ret != 0) + return ret; + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_SLEEP, 0); + + default: + return -EINVAL; + } +} + +static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev, + int input_uV, int output_uV, + int load_uA) +{ + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops wm8400_dcdc_ops = { + .is_enabled = wm8400_dcdc_is_enabled, + .enable = wm8400_dcdc_enable, + .disable = wm8400_dcdc_disable, + .get_voltage = wm8400_dcdc_get_voltage, + .set_voltage = wm8400_dcdc_set_voltage, + .get_mode = wm8400_dcdc_get_mode, + .set_mode = wm8400_dcdc_set_mode, + .get_optimum_mode = wm8400_dcdc_get_optimum_mode, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = WM8400_LDO1, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = WM8400_LDO2, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = WM8400_LDO3, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = WM8400_LDO4, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC1", + .id = WM8400_DCDC1, + .ops = &wm8400_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = WM8400_DCDC2, + .ops = &wm8400_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int __init wm8400_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + + rdev = regulator_register(®ulators[pdev->id], &pdev->dev, + pdev->dev.driver_data); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + return 0; +} + +static int __devexit wm8400_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver wm8400_regulator_driver = { + .driver = { + .name = "wm8400-regulator", + }, + .probe = wm8400_regulator_probe, + .remove = __devexit_p(wm8400_regulator_remove), +}; + +/** + * wm8400_register_regulator - enable software control of a WM8400 regulator + * + * This function enables software control of a WM8400 regulator via + * the regulator API. It is intended to be called from the + * platform_init() callback of the WM8400 MFD driver. + * + * @param dev The WM8400 device to operate on. + * @param reg The regulator to control. + * @param initdata Regulator initdata for the regulator. + */ +int wm8400_register_regulator(struct device *dev, int reg, + struct regulator_init_data *initdata) +{ + struct wm8400 *wm8400 = dev->driver_data; + + if (wm8400->regulators[reg].name) + return -EBUSY; + + initdata->driver_data = wm8400; + + wm8400->regulators[reg].name = "wm8400-regulator"; + wm8400->regulators[reg].id = reg; + wm8400->regulators[reg].dev.parent = dev; + wm8400->regulators[reg].dev.driver_data = wm8400; + wm8400->regulators[reg].dev.platform_data = initdata; + + return platform_device_register(&wm8400->regulators[reg]); +} +EXPORT_SYMBOL_GPL(wm8400_register_regulator); + +static int __init wm8400_regulator_init(void) +{ + return platform_driver_register(&wm8400_regulator_driver); +} +module_init(wm8400_regulator_init); + +static void __exit wm8400_regulator_exit(void) +{ + platform_driver_unregister(&wm8400_regulator_driver); +} +module_exit(wm8400_regulator_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM8400 regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8400-regulator"); diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h index 49042e990f05..2aab4e93a5c9 100644 --- a/include/linux/mfd/wm8400-private.h +++ b/include/linux/mfd/wm8400-private.h @@ -21,6 +21,7 @@ #ifndef __LINUX_MFD_WM8400_PRIV_H #define __LINUX_MFD_WM8400_PRIV_H +#include #include #include diff --git a/include/linux/mfd/wm8400.h b/include/linux/mfd/wm8400.h new file mode 100644 index 000000000000..b46b566ac1ac --- /dev/null +++ b/include/linux/mfd/wm8400.h @@ -0,0 +1,40 @@ +/* + * wm8400 client interface + * + * Copyright 2008 Wolfson Microelectronics plc + * + * 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. + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_MFD_WM8400_H +#define __LINUX_MFD_WM8400_H + +#include + +#define WM8400_LDO1 0 +#define WM8400_LDO2 1 +#define WM8400_LDO3 2 +#define WM8400_LDO4 3 +#define WM8400_DCDC1 4 +#define WM8400_DCDC2 5 + +struct wm8400_platform_data { + int (*platform_init)(struct device *dev); +}; + +int wm8400_register_regulator(struct device *dev, int reg, + struct regulator_init_data *initdata); + +#endif -- cgit v1.2.3 From 06532c474718e330a7e3625ef91b94e22991128c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:03 +0100 Subject: mfd: Add WM8350 audio register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/audio.h | 592 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 include/linux/mfd/wm8350/audio.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h new file mode 100644 index 000000000000..43342f767112 --- /dev/null +++ b/include/linux/mfd/wm8350/audio.h @@ -0,0 +1,592 @@ +/* + * audio.h -- Audio Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + * + */ + +#ifndef __LINUX_MFD_WM8350_AUDIO_H_ +#define __LINUX_MFD_WM8350_AUDIO_H_ + +#define WM8350_CLOCK_CONTROL_1 0x28 +#define WM8350_CLOCK_CONTROL_2 0x29 +#define WM8350_FLL_CONTROL_1 0x2A +#define WM8350_FLL_CONTROL_2 0x2B +#define WM8350_FLL_CONTROL_3 0x2C +#define WM8350_FLL_CONTROL_4 0x2D +#define WM8350_DAC_CONTROL 0x30 +#define WM8350_DAC_DIGITAL_VOLUME_L 0x32 +#define WM8350_DAC_DIGITAL_VOLUME_R 0x33 +#define WM8350_DAC_LR_RATE 0x35 +#define WM8350_DAC_CLOCK_CONTROL 0x36 +#define WM8350_DAC_MUTE 0x3A +#define WM8350_DAC_MUTE_VOLUME 0x3B +#define WM8350_DAC_SIDE 0x3C +#define WM8350_ADC_CONTROL 0x40 +#define WM8350_ADC_DIGITAL_VOLUME_L 0x42 +#define WM8350_ADC_DIGITAL_VOLUME_R 0x43 +#define WM8350_ADC_DIVIDER 0x44 +#define WM8350_ADC_LR_RATE 0x46 +#define WM8350_INPUT_CONTROL 0x48 +#define WM8350_IN3_INPUT_CONTROL 0x49 +#define WM8350_MIC_BIAS_CONTROL 0x4A +#define WM8350_OUTPUT_CONTROL 0x4C +#define WM8350_JACK_DETECT 0x4D +#define WM8350_ANTI_POP_CONTROL 0x4E +#define WM8350_LEFT_INPUT_VOLUME 0x50 +#define WM8350_RIGHT_INPUT_VOLUME 0x51 +#define WM8350_LEFT_MIXER_CONTROL 0x58 +#define WM8350_RIGHT_MIXER_CONTROL 0x59 +#define WM8350_OUT3_MIXER_CONTROL 0x5C +#define WM8350_OUT4_MIXER_CONTROL 0x5D +#define WM8350_OUTPUT_LEFT_MIXER_VOLUME 0x60 +#define WM8350_OUTPUT_RIGHT_MIXER_VOLUME 0x61 +#define WM8350_INPUT_MIXER_VOLUME_L 0x62 +#define WM8350_INPUT_MIXER_VOLUME_R 0x63 +#define WM8350_INPUT_MIXER_VOLUME 0x64 +#define WM8350_LOUT1_VOLUME 0x68 +#define WM8350_ROUT1_VOLUME 0x69 +#define WM8350_LOUT2_VOLUME 0x6A +#define WM8350_ROUT2_VOLUME 0x6B +#define WM8350_BEEP_VOLUME 0x6F +#define WM8350_AI_FORMATING 0x70 +#define WM8350_ADC_DAC_COMP 0x71 +#define WM8350_AI_ADC_CONTROL 0x72 +#define WM8350_AI_DAC_CONTROL 0x73 +#define WM8350_AIF_TEST 0x74 +#define WM8350_JACK_PIN_STATUS 0xE7 + +/* Bit values for R08 (0x08) */ +#define WM8350_CODEC_ISEL_1_5 0 /* x1.5 */ +#define WM8350_CODEC_ISEL_1_0 1 /* x1.0 */ +#define WM8350_CODEC_ISEL_0_75 2 /* x0.75 */ +#define WM8350_CODEC_ISEL_0_5 3 /* x0.5 */ + +#define WM8350_VMID_OFF 0 +#define WM8350_VMID_500K 1 +#define WM8350_VMID_100K 2 +#define WM8350_VMID_10K 3 + +/* + * R40 (0x28) - Clock Control 1 + */ +#define WM8350_TOCLK_RATE 0x4000 +#define WM8350_MCLK_SEL 0x0800 +#define WM8350_MCLK_DIV_MASK 0x0100 +#define WM8350_BCLK_DIV_MASK 0x00F0 +#define WM8350_OPCLK_DIV_MASK 0x0007 + +/* + * R41 (0x29) - Clock Control 2 + */ +#define WM8350_LRC_ADC_SEL 0x8000 +#define WM8350_MCLK_DIR 0x0001 + +/* + * R42 (0x2A) - FLL Control 1 + */ +#define WM8350_FLL_DITHER_WIDTH_MASK 0x3000 +#define WM8350_FLL_DITHER_HP 0x0800 +#define WM8350_FLL_OUTDIV_MASK 0x0700 +#define WM8350_FLL_RSP_RATE_MASK 0x00F0 +#define WM8350_FLL_RATE_MASK 0x0007 + +/* + * R43 (0x2B) - FLL Control 2 + */ +#define WM8350_FLL_RATIO_MASK 0xF800 +#define WM8350_FLL_N_MASK 0x03FF + +/* + * R44 (0x2C) - FLL Control 3 + */ +#define WM8350_FLL_K_MASK 0xFFFF + +/* + * R45 (0x2D) - FLL Control 4 + */ +#define WM8350_FLL_FRAC 0x0020 +#define WM8350_FLL_SLOW_LOCK_REF 0x0010 +#define WM8350_FLL_CLK_SRC_MASK 0x0003 + +/* + * R48 (0x30) - DAC Control + */ +#define WM8350_DAC_MONO 0x2000 +#define WM8350_AIF_LRCLKRATE 0x1000 +#define WM8350_DEEMP_MASK 0x0030 +#define WM8350_DACL_DATINV 0x0002 +#define WM8350_DACR_DATINV 0x0001 + +/* + * R50 (0x32) - DAC Digital Volume L + */ +#define WM8350_DAC_VU 0x0100 +#define WM8350_DACL_VOL_MASK 0x00FF + +/* + * R51 (0x33) - DAC Digital Volume R + */ +#define WM8350_DAC_VU 0x0100 +#define WM8350_DACR_VOL_MASK 0x00FF + +/* + * R53 (0x35) - DAC LR Rate + */ +#define WM8350_DACLRC_ENA 0x0800 +#define WM8350_DACLRC_RATE_MASK 0x07FF + +/* + * R54 (0x36) - DAC Clock Control + */ +#define WM8350_DACCLK_POL 0x0010 +#define WM8350_DAC_CLKDIV_MASK 0x0007 + +/* + * R58 (0x3A) - DAC Mute + */ +#define WM8350_DAC_MUTE_ENA 0x4000 + +/* + * R59 (0x3B) - DAC Mute Volume + */ +#define WM8350_DAC_MUTEMODE 0x4000 +#define WM8350_DAC_MUTERATE 0x2000 +#define WM8350_DAC_SB_FILT 0x1000 + +/* + * R60 (0x3C) - DAC Side + */ +#define WM8350_ADC_TO_DACL_MASK 0x3000 +#define WM8350_ADC_TO_DACR_MASK 0x0C00 + +/* + * R64 (0x40) - ADC Control + */ +#define WM8350_ADC_HPF_CUT_MASK 0x0300 +#define WM8350_ADCL_DATINV 0x0002 +#define WM8350_ADCR_DATINV 0x0001 + +/* + * R66 (0x42) - ADC Digital Volume L + */ +#define WM8350_ADC_VU 0x0100 +#define WM8350_ADCL_VOL_MASK 0x00FF + +/* + * R67 (0x43) - ADC Digital Volume R + */ +#define WM8350_ADC_VU 0x0100 +#define WM8350_ADCR_VOL_MASK 0x00FF + +/* + * R68 (0x44) - ADC Divider + */ +#define WM8350_ADCL_DAC_SVOL_MASK 0x0F00 +#define WM8350_ADCR_DAC_SVOL_MASK 0x00F0 +#define WM8350_ADCCLK_POL 0x0008 +#define WM8350_ADC_CLKDIV_MASK 0x0007 + +/* + * R70 (0x46) - ADC LR Rate + */ +#define WM8350_ADCLRC_ENA 0x0800 +#define WM8350_ADCLRC_RATE_MASK 0x07FF + +/* + * R72 (0x48) - Input Control + */ +#define WM8350_IN2R_ENA 0x0400 +#define WM8350_IN1RN_ENA 0x0200 +#define WM8350_IN1RP_ENA 0x0100 +#define WM8350_IN2L_ENA 0x0004 +#define WM8350_IN1LN_ENA 0x0002 +#define WM8350_IN1LP_ENA 0x0001 + +/* + * R73 (0x49) - IN3 Input Control + */ +#define WM8350_IN3R_SHORT 0x4000 +#define WM8350_IN3L_SHORT 0x0040 + +/* + * R74 (0x4A) - Mic Bias Control + */ +#define WM8350_MICBSEL 0x4000 +#define WM8350_MCDTHR_MASK 0x001C +#define WM8350_MCDSCTHR_MASK 0x0003 + +/* + * R76 (0x4C) - Output Control + */ +#define WM8350_OUT4_VROI 0x0800 +#define WM8350_OUT3_VROI 0x0400 +#define WM8350_OUT2_VROI 0x0200 +#define WM8350_OUT1_VROI 0x0100 +#define WM8350_OUT2_FB 0x0004 +#define WM8350_OUT1_FB 0x0001 + +/* + * R77 (0x4D) - Jack Detect + */ +#define WM8350_JDL_ENA 0x8000 +#define WM8350_JDR_ENA 0x4000 + +/* + * R78 (0x4E) - Anti Pop Control + */ +#define WM8350_ANTI_POP_MASK 0x0300 +#define WM8350_DIS_OP_LN4_MASK 0x00C0 +#define WM8350_DIS_OP_LN3_MASK 0x0030 +#define WM8350_DIS_OP_OUT2_MASK 0x000C +#define WM8350_DIS_OP_OUT1_MASK 0x0003 + +/* + * R80 (0x50) - Left Input Volume + */ +#define WM8350_INL_MUTE 0x4000 +#define WM8350_INL_ZC 0x2000 +#define WM8350_IN_VU 0x0100 +#define WM8350_INL_VOL_MASK 0x00FC + +/* + * R81 (0x51) - Right Input Volume + */ +#define WM8350_INR_MUTE 0x4000 +#define WM8350_INR_ZC 0x2000 +#define WM8350_IN_VU 0x0100 +#define WM8350_INR_VOL_MASK 0x00FC + +/* + * R88 (0x58) - Left Mixer Control + */ +#define WM8350_DACR_TO_MIXOUTL 0x1000 +#define WM8350_DACL_TO_MIXOUTL 0x0800 +#define WM8350_IN3L_TO_MIXOUTL 0x0004 +#define WM8350_INR_TO_MIXOUTL 0x0002 +#define WM8350_INL_TO_MIXOUTL 0x0001 + +/* + * R89 (0x59) - Right Mixer Control + */ +#define WM8350_DACR_TO_MIXOUTR 0x1000 +#define WM8350_DACL_TO_MIXOUTR 0x0800 +#define WM8350_IN3R_TO_MIXOUTR 0x0008 +#define WM8350_INR_TO_MIXOUTR 0x0002 +#define WM8350_INL_TO_MIXOUTR 0x0001 + +/* + * R92 (0x5C) - OUT3 Mixer Control + */ +#define WM8350_DACL_TO_OUT3 0x0800 +#define WM8350_MIXINL_TO_OUT3 0x0100 +#define WM8350_OUT4_TO_OUT3 0x0008 +#define WM8350_MIXOUTL_TO_OUT3 0x0001 + +/* + * R93 (0x5D) - OUT4 Mixer Control + */ +#define WM8350_DACR_TO_OUT4 0x1000 +#define WM8350_DACL_TO_OUT4 0x0800 +#define WM8350_OUT4_ATTN 0x0400 +#define WM8350_MIXINR_TO_OUT4 0x0200 +#define WM8350_OUT3_TO_OUT4 0x0004 +#define WM8350_MIXOUTR_TO_OUT4 0x0002 +#define WM8350_MIXOUTL_TO_OUT4 0x0001 + +/* + * R96 (0x60) - Output Left Mixer Volume + */ +#define WM8350_IN3L_MIXOUTL_VOL_MASK 0x0E00 +#define WM8350_IN3L_MIXOUTL_VOL_SHIFT 9 +#define WM8350_INR_MIXOUTL_VOL_MASK 0x00E0 +#define WM8350_INR_MIXOUTL_VOL_SHIFT 5 +#define WM8350_INL_MIXOUTL_VOL_MASK 0x000E +#define WM8350_INL_MIXOUTL_VOL_SHIFT 1 + +/* Bit values for R96 (0x60) */ +#define WM8350_IN3L_MIXOUTL_VOL_OFF 0 +#define WM8350_IN3L_MIXOUTL_VOL_M12DB 1 +#define WM8350_IN3L_MIXOUTL_VOL_M9DB 2 +#define WM8350_IN3L_MIXOUTL_VOL_M6DB 3 +#define WM8350_IN3L_MIXOUTL_VOL_M3DB 4 +#define WM8350_IN3L_MIXOUTL_VOL_0DB 5 +#define WM8350_IN3L_MIXOUTL_VOL_3DB 6 +#define WM8350_IN3L_MIXOUTL_VOL_6DB 7 + +#define WM8350_INR_MIXOUTL_VOL_OFF 0 +#define WM8350_INR_MIXOUTL_VOL_M12DB 1 +#define WM8350_INR_MIXOUTL_VOL_M9DB 2 +#define WM8350_INR_MIXOUTL_VOL_M6DB 3 +#define WM8350_INR_MIXOUTL_VOL_M3DB 4 +#define WM8350_INR_MIXOUTL_VOL_0DB 5 +#define WM8350_INR_MIXOUTL_VOL_3DB 6 +#define WM8350_INR_MIXOUTL_VOL_6DB 7 + +#define WM8350_INL_MIXOUTL_VOL_OFF 0 +#define WM8350_INL_MIXOUTL_VOL_M12DB 1 +#define WM8350_INL_MIXOUTL_VOL_M9DB 2 +#define WM8350_INL_MIXOUTL_VOL_M6DB 3 +#define WM8350_INL_MIXOUTL_VOL_M3DB 4 +#define WM8350_INL_MIXOUTL_VOL_0DB 5 +#define WM8350_INL_MIXOUTL_VOL_3DB 6 +#define WM8350_INL_MIXOUTL_VOL_6DB 7 + +/* + * R97 (0x61) - Output Right Mixer Volume + */ +#define WM8350_IN3R_MIXOUTR_VOL_MASK 0xE000 +#define WM8350_IN3R_MIXOUTR_VOL_SHIFT 13 +#define WM8350_INR_MIXOUTR_VOL_MASK 0x00E0 +#define WM8350_INR_MIXOUTR_VOL_SHIFT 5 +#define WM8350_INL_MIXOUTR_VOL_MASK 0x000E +#define WM8350_INL_MIXOUTR_VOL_SHIFT 1 + +/* Bit values for R96 (0x60) */ +#define WM8350_IN3R_MIXOUTR_VOL_OFF 0 +#define WM8350_IN3R_MIXOUTR_VOL_M12DB 1 +#define WM8350_IN3R_MIXOUTR_VOL_M9DB 2 +#define WM8350_IN3R_MIXOUTR_VOL_M6DB 3 +#define WM8350_IN3R_MIXOUTR_VOL_M3DB 4 +#define WM8350_IN3R_MIXOUTR_VOL_0DB 5 +#define WM8350_IN3R_MIXOUTR_VOL_3DB 6 +#define WM8350_IN3R_MIXOUTR_VOL_6DB 7 + +#define WM8350_INR_MIXOUTR_VOL_OFF 0 +#define WM8350_INR_MIXOUTR_VOL_M12DB 1 +#define WM8350_INR_MIXOUTR_VOL_M9DB 2 +#define WM8350_INR_MIXOUTR_VOL_M6DB 3 +#define WM8350_INR_MIXOUTR_VOL_M3DB 4 +#define WM8350_INR_MIXOUTR_VOL_0DB 5 +#define WM8350_INR_MIXOUTR_VOL_3DB 6 +#define WM8350_INR_MIXOUTR_VOL_6DB 7 + +#define WM8350_INL_MIXOUTR_VOL_OFF 0 +#define WM8350_INL_MIXOUTR_VOL_M12DB 1 +#define WM8350_INL_MIXOUTR_VOL_M9DB 2 +#define WM8350_INL_MIXOUTR_VOL_M6DB 3 +#define WM8350_INL_MIXOUTR_VOL_M3DB 4 +#define WM8350_INL_MIXOUTR_VOL_0DB 5 +#define WM8350_INL_MIXOUTR_VOL_3DB 6 +#define WM8350_INL_MIXOUTR_VOL_6DB 7 + +/* + * R98 (0x62) - Input Mixer Volume L + */ +#define WM8350_IN3L_MIXINL_VOL_MASK 0x0E00 +#define WM8350_IN2L_MIXINL_VOL_MASK 0x000E +#define WM8350_INL_MIXINL_VOL 0x0001 + +/* + * R99 (0x63) - Input Mixer Volume R + */ +#define WM8350_IN3R_MIXINR_VOL_MASK 0xE000 +#define WM8350_IN2R_MIXINR_VOL_MASK 0x00E0 +#define WM8350_INR_MIXINR_VOL 0x0001 + +/* + * R100 (0x64) - Input Mixer Volume + */ +#define WM8350_OUT4_MIXIN_DST 0x8000 +#define WM8350_OUT4_MIXIN_VOL_MASK 0x000E + +/* + * R104 (0x68) - LOUT1 Volume + */ +#define WM8350_OUT1L_MUTE 0x4000 +#define WM8350_OUT1L_ZC 0x2000 +#define WM8350_OUT1_VU 0x0100 +#define WM8350_OUT1L_VOL_MASK 0x00FC +#define WM8350_OUT1L_VOL_SHIFT 2 + +/* + * R105 (0x69) - ROUT1 Volume + */ +#define WM8350_OUT1R_MUTE 0x4000 +#define WM8350_OUT1R_ZC 0x2000 +#define WM8350_OUT1_VU 0x0100 +#define WM8350_OUT1R_VOL_MASK 0x00FC +#define WM8350_OUT1R_VOL_SHIFT 2 + +/* + * R106 (0x6A) - LOUT2 Volume + */ +#define WM8350_OUT2L_MUTE 0x4000 +#define WM8350_OUT2L_ZC 0x2000 +#define WM8350_OUT2_VU 0x0100 +#define WM8350_OUT2L_VOL_MASK 0x00FC + +/* + * R107 (0x6B) - ROUT2 Volume + */ +#define WM8350_OUT2R_MUTE 0x4000 +#define WM8350_OUT2R_ZC 0x2000 +#define WM8350_OUT2R_INV 0x0400 +#define WM8350_OUT2R_INV_MUTE 0x0200 +#define WM8350_OUT2_VU 0x0100 +#define WM8350_OUT2R_VOL_MASK 0x00FC + +/* + * R111 (0x6F) - BEEP Volume + */ +#define WM8350_IN3R_OUT2R_VOL_MASK 0x00E0 + +/* + * R112 (0x70) - AI Formating + */ +#define WM8350_AIF_BCLK_INV 0x8000 +#define WM8350_AIF_TRI 0x2000 +#define WM8350_AIF_LRCLK_INV 0x1000 +#define WM8350_AIF_WL_MASK 0x0C00 +#define WM8350_AIF_FMT_MASK 0x0300 + +/* + * R113 (0x71) - ADC DAC COMP + */ +#define WM8350_DAC_COMP 0x0080 +#define WM8350_DAC_COMPMODE 0x0040 +#define WM8350_ADC_COMP 0x0020 +#define WM8350_ADC_COMPMODE 0x0010 +#define WM8350_LOOPBACK 0x0001 + +/* + * R114 (0x72) - AI ADC Control + */ +#define WM8350_AIFADC_PD 0x0080 +#define WM8350_AIFADCL_SRC 0x0040 +#define WM8350_AIFADCR_SRC 0x0020 +#define WM8350_AIFADC_TDM_CHAN 0x0010 +#define WM8350_AIFADC_TDM 0x0008 + +/* + * R115 (0x73) - AI DAC Control + */ +#define WM8350_BCLK_MSTR 0x4000 +#define WM8350_AIFDAC_PD 0x0080 +#define WM8350_DACL_SRC 0x0040 +#define WM8350_DACR_SRC 0x0020 +#define WM8350_AIFDAC_TDM_CHAN 0x0010 +#define WM8350_AIFDAC_TDM 0x0008 +#define WM8350_DAC_BOOST_MASK 0x0003 + +/* + * R116 (0x74) - AIF Test + */ +#define WM8350_CODEC_BYP 0x4000 +#define WM8350_AIFADC_WR_TST 0x2000 +#define WM8350_AIFADC_RD_TST 0x1000 +#define WM8350_AIFDAC_WR_TST 0x0800 +#define WM8350_AIFDAC_RD_TST 0x0400 +#define WM8350_AIFADC_ASYN 0x0020 +#define WM8350_AIFDAC_ASYN 0x0010 + +/* + * R231 (0xE7) - Jack Status + */ +#define WM8350_JACK_R_LVL 0x0400 + +/* + * WM8350 Platform setup + */ +#define WM8350_S_CURVE_NONE 0x0 +#define WM8350_S_CURVE_FAST 0x1 +#define WM8350_S_CURVE_MEDIUM 0x2 +#define WM8350_S_CURVE_SLOW 0x3 + +#define WM8350_DISCHARGE_OFF 0x0 +#define WM8350_DISCHARGE_FAST 0x1 +#define WM8350_DISCHARGE_MEDIUM 0x2 +#define WM8350_DISCHARGE_SLOW 0x3 + +#define WM8350_TIE_OFF_500R 0x0 +#define WM8350_TIE_OFF_30K 0x1 + +/* + * Clock sources & directions + */ +#define WM8350_SYSCLK 0 + +#define WM8350_MCLK_SEL_PLL_MCLK 0 +#define WM8350_MCLK_SEL_PLL_DAC 1 +#define WM8350_MCLK_SEL_PLL_ADC 2 +#define WM8350_MCLK_SEL_PLL_32K 3 +#define WM8350_MCLK_SEL_MCLK 5 + +#define WM8350_MCLK_DIR_OUT 0 +#define WM8350_MCLK_DIR_IN 1 + +/* clock divider id's */ +#define WM8350_ADC_CLKDIV 0 +#define WM8350_DAC_CLKDIV 1 +#define WM8350_BCLK_CLKDIV 2 +#define WM8350_OPCLK_CLKDIV 3 +#define WM8350_TO_CLKDIV 4 +#define WM8350_SYS_CLKDIV 5 +#define WM8350_DACLR_CLKDIV 6 +#define WM8350_ADCLR_CLKDIV 7 + +/* ADC clock dividers */ +#define WM8350_ADCDIV_1 0x0 +#define WM8350_ADCDIV_1_5 0x1 +#define WM8350_ADCDIV_2 0x2 +#define WM8350_ADCDIV_3 0x3 +#define WM8350_ADCDIV_4 0x4 +#define WM8350_ADCDIV_5_5 0x5 +#define WM8350_ADCDIV_6 0x6 + +/* ADC clock dividers */ +#define WM8350_DACDIV_1 0x0 +#define WM8350_DACDIV_1_5 0x1 +#define WM8350_DACDIV_2 0x2 +#define WM8350_DACDIV_3 0x3 +#define WM8350_DACDIV_4 0x4 +#define WM8350_DACDIV_5_5 0x5 +#define WM8350_DACDIV_6 0x6 + +/* BCLK clock dividers */ +#define WM8350_BCLK_DIV_1 (0x0 << 4) +#define WM8350_BCLK_DIV_1_5 (0x1 << 4) +#define WM8350_BCLK_DIV_2 (0x2 << 4) +#define WM8350_BCLK_DIV_3 (0x3 << 4) +#define WM8350_BCLK_DIV_4 (0x4 << 4) +#define WM8350_BCLK_DIV_5_5 (0x5 << 4) +#define WM8350_BCLK_DIV_6 (0x6 << 4) +#define WM8350_BCLK_DIV_8 (0x7 << 4) +#define WM8350_BCLK_DIV_11 (0x8 << 4) +#define WM8350_BCLK_DIV_12 (0x9 << 4) +#define WM8350_BCLK_DIV_16 (0xa << 4) +#define WM8350_BCLK_DIV_22 (0xb << 4) +#define WM8350_BCLK_DIV_24 (0xc << 4) +#define WM8350_BCLK_DIV_32 (0xd << 4) +#define WM8350_BCLK_DIV_44 (0xe << 4) +#define WM8350_BCLK_DIV_48 (0xf << 4) + +/* Sys (MCLK) clock dividers */ +#define WM8350_MCLK_DIV_1 (0x0 << 8) +#define WM8350_MCLK_DIV_2 (0x1 << 8) + +/* OP clock dividers */ +#define WM8350_OPCLK_DIV_1 0x0 +#define WM8350_OPCLK_DIV_2 0x1 +#define WM8350_OPCLK_DIV_3 0x2 +#define WM8350_OPCLK_DIV_4 0x3 +#define WM8350_OPCLK_DIV_5_5 0x4 +#define WM8350_OPCLK_DIV_6 0x5 + +/* DAI ID */ +#define WM8350_HIFI_DAI 0 + +/* + * Audio interrupts. + */ +#define WM8350_IRQ_CODEC_JCK_DET_L 39 +#define WM8350_IRQ_CODEC_JCK_DET_R 40 +#define WM8350_IRQ_CODEC_MICSCD 41 +#define WM8350_IRQ_CODEC_MICD 42 + +#endif -- cgit v1.2.3 From 0c42e0b7631f1bfccc27eedac2a1e03f4c8c824c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:04 +0100 Subject: mfd: Add WM8350 GPIO register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/gpio.h | 326 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 include/linux/mfd/wm8350/gpio.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/gpio.h b/include/linux/mfd/wm8350/gpio.h new file mode 100644 index 000000000000..928aa6e91e36 --- /dev/null +++ b/include/linux/mfd/wm8350/gpio.h @@ -0,0 +1,326 @@ +/* + * gpio.h -- GPIO Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + * + */ + +#ifndef __LINUX_MFD_WM8350_GPIO_H_ +#define __LINUX_MFD_WM8350_GPIO_H_ + +/* + * GPIO Registers. + */ +#define WM8350_GPIO_DEBOUNCE 0x80 +#define WM8350_GPIO_PIN_PULL_UP_CONTROL 0x81 +#define WM8350_GPIO_PULL_DOWN_CONTROL 0x82 +#define WM8350_GPIO_INT_MODE 0x83 +#define WM8350_GPIO_CONTROL 0x85 +#define WM8350_GPIO_CONFIGURATION_I_O 0x86 +#define WM8350_GPIO_PIN_POLARITY_TYPE 0x87 +#define WM8350_GPIO_FUNCTION_SELECT_1 0x8C +#define WM8350_GPIO_FUNCTION_SELECT_2 0x8D +#define WM8350_GPIO_FUNCTION_SELECT_3 0x8E +#define WM8350_GPIO_FUNCTION_SELECT_4 0x8F + +/* + * GPIO Functions + */ +#define WM8350_GPIO0_GPIO_IN 0x0 +#define WM8350_GPIO0_GPIO_OUT 0x0 +#define WM8350_GPIO0_PWR_ON_IN 0x1 +#define WM8350_GPIO0_PWR_ON_OUT 0x1 +#define WM8350_GPIO0_LDO_EN_IN 0x2 +#define WM8350_GPIO0_VRTC_OUT 0x2 +#define WM8350_GPIO0_LPWR1_IN 0x3 +#define WM8350_GPIO0_POR_B_OUT 0x3 + +#define WM8350_GPIO1_GPIO_IN 0x0 +#define WM8350_GPIO1_GPIO_OUT 0x0 +#define WM8350_GPIO1_PWR_ON_IN 0x1 +#define WM8350_GPIO1_DO_CONF_OUT 0x1 +#define WM8350_GPIO1_LDO_EN_IN 0x2 +#define WM8350_GPIO1_RESET_OUT 0x2 +#define WM8350_GPIO1_LPWR2_IN 0x3 +#define WM8350_GPIO1_MEMRST_OUT 0x3 + +#define WM8350_GPIO2_GPIO_IN 0x0 +#define WM8350_GPIO2_GPIO_OUT 0x0 +#define WM8350_GPIO2_PWR_ON_IN 0x1 +#define WM8350_GPIO2_PWR_ON_OUT 0x1 +#define WM8350_GPIO2_WAKE_UP_IN 0x2 +#define WM8350_GPIO2_VRTC_OUT 0x2 +#define WM8350_GPIO2_32KHZ_IN 0x3 +#define WM8350_GPIO2_32KHZ_OUT 0x3 + +#define WM8350_GPIO3_GPIO_IN 0x0 +#define WM8350_GPIO3_GPIO_OUT 0x0 +#define WM8350_GPIO3_PWR_ON_IN 0x1 +#define WM8350_GPIO3_P_CLK_OUT 0x1 +#define WM8350_GPIO3_LDO_EN_IN 0x2 +#define WM8350_GPIO3_VRTC_OUT 0x2 +#define WM8350_GPIO3_PWR_OFF_IN 0x3 +#define WM8350_GPIO3_32KHZ_OUT 0x3 + +#define WM8350_GPIO4_GPIO_IN 0x0 +#define WM8350_GPIO4_GPIO_OUT 0x0 +#define WM8350_GPIO4_MR_IN 0x1 +#define WM8350_GPIO4_MEM_RST_OUT 0x1 +#define WM8350_GPIO4_FLASH_IN 0x2 +#define WM8350_GPIO4_ADA_OUT 0x2 +#define WM8350_GPIO4_HIBERNATE_IN 0x3 +#define WM8350_GPIO4_FLASH_OUT 0x3 +#define WM8350_GPIO4_MICDET_OUT 0x4 +#define WM8350_GPIO4_MICSHT_OUT 0x5 + +#define WM8350_GPIO5_GPIO_IN 0x0 +#define WM8350_GPIO5_GPIO_OUT 0x0 +#define WM8350_GPIO5_LPWR1_IN 0x1 +#define WM8350_GPIO5_P_CLK_OUT 0x1 +#define WM8350_GPIO5_ADCLRCLK_IN 0x2 +#define WM8350_GPIO5_ADCLRCLK_OUT 0x2 +#define WM8350_GPIO5_HIBERNATE_IN 0x3 +#define WM8350_GPIO5_32KHZ_OUT 0x3 +#define WM8350_GPIO5_MICDET_OUT 0x4 +#define WM8350_GPIO5_MICSHT_OUT 0x5 +#define WM8350_GPIO5_ADA_OUT 0x6 +#define WM8350_GPIO5_OPCLK_OUT 0x7 + +#define WM8350_GPIO6_GPIO_IN 0x0 +#define WM8350_GPIO6_GPIO_OUT 0x0 +#define WM8350_GPIO6_LPWR2_IN 0x1 +#define WM8350_GPIO6_MEMRST_OUT 0x1 +#define WM8350_GPIO6_FLASH_IN 0x2 +#define WM8350_GPIO6_ADA_OUT 0x2 +#define WM8350_GPIO6_HIBERNATE_IN 0x3 +#define WM8350_GPIO6_RTC_OUT 0x3 +#define WM8350_GPIO6_MICDET_OUT 0x4 +#define WM8350_GPIO6_MICSHT_OUT 0x5 +#define WM8350_GPIO6_ADCLRCLKB_OUT 0x6 +#define WM8350_GPIO6_SDOUT_OUT 0x7 + +#define WM8350_GPIO7_GPIO_IN 0x0 +#define WM8350_GPIO7_GPIO_OUT 0x0 +#define WM8350_GPIO7_LPWR3_IN 0x1 +#define WM8350_GPIO7_P_CLK_OUT 0x1 +#define WM8350_GPIO7_MASK_IN 0x2 +#define WM8350_GPIO7_VCC_FAULT_OUT 0x2 +#define WM8350_GPIO7_HIBERNATE_IN 0x3 +#define WM8350_GPIO7_BATT_FAULT_OUT 0x3 +#define WM8350_GPIO7_MICDET_OUT 0x4 +#define WM8350_GPIO7_MICSHT_OUT 0x5 +#define WM8350_GPIO7_ADA_OUT 0x6 +#define WM8350_GPIO7_CSB_IN 0x7 + +#define WM8350_GPIO8_GPIO_IN 0x0 +#define WM8350_GPIO8_GPIO_OUT 0x0 +#define WM8350_GPIO8_MR_IN 0x1 +#define WM8350_GPIO8_VCC_FAULT_OUT 0x1 +#define WM8350_GPIO8_ADCBCLK_IN 0x2 +#define WM8350_GPIO8_ADCBCLK_OUT 0x2 +#define WM8350_GPIO8_PWR_OFF_IN 0x3 +#define WM8350_GPIO8_BATT_FAULT_OUT 0x3 +#define WM8350_GPIO8_ALTSCL_IN 0xf + +#define WM8350_GPIO9_GPIO_IN 0x0 +#define WM8350_GPIO9_GPIO_OUT 0x0 +#define WM8350_GPIO9_HEARTBEAT_IN 0x1 +#define WM8350_GPIO9_VCC_FAULT_OUT 0x1 +#define WM8350_GPIO9_MASK_IN 0x2 +#define WM8350_GPIO9_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO9_PWR_OFF_IN 0x3 +#define WM8350_GPIO9_BATT_FAULT_OUT 0x3 +#define WM8350_GPIO9_ALTSDA_OUT 0xf + +#define WM8350_GPIO10_GPIO_IN 0x0 +#define WM8350_GPIO10_GPIO_OUT 0x0 +#define WM8350_GPIO10_ISINKC_OUT 0x1 +#define WM8350_GPIO10_PWR_OFF_IN 0x2 +#define WM8350_GPIO10_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO10_CHD_IND_IN 0x3 + +#define WM8350_GPIO11_GPIO_IN 0x0 +#define WM8350_GPIO11_GPIO_OUT 0x0 +#define WM8350_GPIO11_ISINKD_OUT 0x1 +#define WM8350_GPIO11_WAKEUP_IN 0x2 +#define WM8350_GPIO11_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO11_CHD_IND_IN 0x3 + +#define WM8350_GPIO12_GPIO_IN 0x0 +#define WM8350_GPIO12_GPIO_OUT 0x0 +#define WM8350_GPIO12_ISINKE_OUT 0x1 +#define WM8350_GPIO12_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO12_LINE_EN_OUT 0x3 +#define WM8350_GPIO12_32KHZ_OUT 0x4 + +#define WM8350_GPIO_DIR_IN 0 +#define WM8350_GPIO_DIR_OUT 1 +#define WM8350_GPIO_ACTIVE_LOW 0 +#define WM8350_GPIO_ACTIVE_HIGH 1 +#define WM8350_GPIO_PULL_NONE 0 +#define WM8350_GPIO_PULL_UP 1 +#define WM8350_GPIO_PULL_DOWN 2 +#define WM8350_GPIO_INVERT_OFF 0 +#define WM8350_GPIO_INVERT_ON 1 +#define WM8350_GPIO_DEBOUNCE_OFF 0 +#define WM8350_GPIO_DEBOUNCE_ON 1 + +/* + * R128 (0x80) - GPIO Debounce + */ +#define WM8350_GP12_DB 0x1000 +#define WM8350_GP11_DB 0x0800 +#define WM8350_GP10_DB 0x0400 +#define WM8350_GP9_DB 0x0200 +#define WM8350_GP8_DB 0x0100 +#define WM8350_GP7_DB 0x0080 +#define WM8350_GP6_DB 0x0040 +#define WM8350_GP5_DB 0x0020 +#define WM8350_GP4_DB 0x0010 +#define WM8350_GP3_DB 0x0008 +#define WM8350_GP2_DB 0x0004 +#define WM8350_GP1_DB 0x0002 +#define WM8350_GP0_DB 0x0001 + +/* + * R129 (0x81) - GPIO Pin pull up Control + */ +#define WM8350_GP12_PU 0x1000 +#define WM8350_GP11_PU 0x0800 +#define WM8350_GP10_PU 0x0400 +#define WM8350_GP9_PU 0x0200 +#define WM8350_GP8_PU 0x0100 +#define WM8350_GP7_PU 0x0080 +#define WM8350_GP6_PU 0x0040 +#define WM8350_GP5_PU 0x0020 +#define WM8350_GP4_PU 0x0010 +#define WM8350_GP3_PU 0x0008 +#define WM8350_GP2_PU 0x0004 +#define WM8350_GP1_PU 0x0002 +#define WM8350_GP0_PU 0x0001 + +/* + * R130 (0x82) - GPIO Pull down Control + */ +#define WM8350_GP12_PD 0x1000 +#define WM8350_GP11_PD 0x0800 +#define WM8350_GP10_PD 0x0400 +#define WM8350_GP9_PD 0x0200 +#define WM8350_GP8_PD 0x0100 +#define WM8350_GP7_PD 0x0080 +#define WM8350_GP6_PD 0x0040 +#define WM8350_GP5_PD 0x0020 +#define WM8350_GP4_PD 0x0010 +#define WM8350_GP3_PD 0x0008 +#define WM8350_GP2_PD 0x0004 +#define WM8350_GP1_PD 0x0002 +#define WM8350_GP0_PD 0x0001 + +/* + * R131 (0x83) - GPIO Interrupt Mode + */ +#define WM8350_GP12_INTMODE 0x1000 +#define WM8350_GP11_INTMODE 0x0800 +#define WM8350_GP10_INTMODE 0x0400 +#define WM8350_GP9_INTMODE 0x0200 +#define WM8350_GP8_INTMODE 0x0100 +#define WM8350_GP7_INTMODE 0x0080 +#define WM8350_GP6_INTMODE 0x0040 +#define WM8350_GP5_INTMODE 0x0020 +#define WM8350_GP4_INTMODE 0x0010 +#define WM8350_GP3_INTMODE 0x0008 +#define WM8350_GP2_INTMODE 0x0004 +#define WM8350_GP1_INTMODE 0x0002 +#define WM8350_GP0_INTMODE 0x0001 + +/* + * R133 (0x85) - GPIO Control + */ +#define WM8350_GP_DBTIME_MASK 0x00C0 + +/* + * R134 (0x86) - GPIO Configuration (i/o) + */ +#define WM8350_GP12_DIR 0x1000 +#define WM8350_GP11_DIR 0x0800 +#define WM8350_GP10_DIR 0x0400 +#define WM8350_GP9_DIR 0x0200 +#define WM8350_GP8_DIR 0x0100 +#define WM8350_GP7_DIR 0x0080 +#define WM8350_GP6_DIR 0x0040 +#define WM8350_GP5_DIR 0x0020 +#define WM8350_GP4_DIR 0x0010 +#define WM8350_GP3_DIR 0x0008 +#define WM8350_GP2_DIR 0x0004 +#define WM8350_GP1_DIR 0x0002 +#define WM8350_GP0_DIR 0x0001 + +/* + * R135 (0x87) - GPIO Pin Polarity / Type + */ +#define WM8350_GP12_CFG 0x1000 +#define WM8350_GP11_CFG 0x0800 +#define WM8350_GP10_CFG 0x0400 +#define WM8350_GP9_CFG 0x0200 +#define WM8350_GP8_CFG 0x0100 +#define WM8350_GP7_CFG 0x0080 +#define WM8350_GP6_CFG 0x0040 +#define WM8350_GP5_CFG 0x0020 +#define WM8350_GP4_CFG 0x0010 +#define WM8350_GP3_CFG 0x0008 +#define WM8350_GP2_CFG 0x0004 +#define WM8350_GP1_CFG 0x0002 +#define WM8350_GP0_CFG 0x0001 + +/* + * R140 (0x8C) - GPIO Function Select 1 + */ +#define WM8350_GP3_FN_MASK 0xF000 +#define WM8350_GP2_FN_MASK 0x0F00 +#define WM8350_GP1_FN_MASK 0x00F0 +#define WM8350_GP0_FN_MASK 0x000F + +/* + * R141 (0x8D) - GPIO Function Select 2 + */ +#define WM8350_GP7_FN_MASK 0xF000 +#define WM8350_GP6_FN_MASK 0x0F00 +#define WM8350_GP5_FN_MASK 0x00F0 +#define WM8350_GP4_FN_MASK 0x000F + +/* + * R142 (0x8E) - GPIO Function Select 3 + */ +#define WM8350_GP11_FN_MASK 0xF000 +#define WM8350_GP10_FN_MASK 0x0F00 +#define WM8350_GP9_FN_MASK 0x00F0 +#define WM8350_GP8_FN_MASK 0x000F + +/* + * R143 (0x8F) - GPIO Function Select 4 + */ +#define WM8350_GP12_FN_MASK 0x000F + +/* + * R230 (0xE6) - GPIO Pin Status + */ +#define WM8350_GP12_LVL 0x1000 +#define WM8350_GP11_LVL 0x0800 +#define WM8350_GP10_LVL 0x0400 +#define WM8350_GP9_LVL 0x0200 +#define WM8350_GP8_LVL 0x0100 +#define WM8350_GP7_LVL 0x0080 +#define WM8350_GP6_LVL 0x0040 +#define WM8350_GP5_LVL 0x0020 +#define WM8350_GP4_LVL 0x0010 +#define WM8350_GP3_LVL 0x0008 +#define WM8350_GP2_LVL 0x0004 +#define WM8350_GP1_LVL 0x0002 +#define WM8350_GP0_LVL 0x0001 + +#endif -- cgit v1.2.3 From 03c127ee1e0c5f7a5285d1af3d5c1571ac2646a1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:05 +0100 Subject: mfd: Add WM8350 PMIC register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/pmic.h | 699 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 699 insertions(+) create mode 100644 include/linux/mfd/wm8350/pmic.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h new file mode 100644 index 000000000000..28d2fab27875 --- /dev/null +++ b/include/linux/mfd/wm8350/pmic.h @@ -0,0 +1,699 @@ +/* + * pmic.h -- Power Managment Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + * + */ + +#ifndef __LINUX_MFD_WM8350_PMIC_H +#define __LINUX_MFD_WM8350_PMIC_H + +/* + * Register values. + */ + +#define WM8350_CURRENT_SINK_DRIVER_A 0xAC +#define WM8350_CSA_FLASH_CONTROL 0xAD +#define WM8350_CURRENT_SINK_DRIVER_B 0xAE +#define WM8350_CSB_FLASH_CONTROL 0xAF +#define WM8350_DCDC_LDO_REQUESTED 0xB0 +#define WM8350_DCDC_ACTIVE_OPTIONS 0xB1 +#define WM8350_DCDC_SLEEP_OPTIONS 0xB2 +#define WM8350_POWER_CHECK_COMPARATOR 0xB3 +#define WM8350_DCDC1_CONTROL 0xB4 +#define WM8350_DCDC1_TIMEOUTS 0xB5 +#define WM8350_DCDC1_LOW_POWER 0xB6 +#define WM8350_DCDC2_CONTROL 0xB7 +#define WM8350_DCDC2_TIMEOUTS 0xB8 +#define WM8350_DCDC3_CONTROL 0xBA +#define WM8350_DCDC3_TIMEOUTS 0xBB +#define WM8350_DCDC3_LOW_POWER 0xBC +#define WM8350_DCDC4_CONTROL 0xBD +#define WM8350_DCDC4_TIMEOUTS 0xBE +#define WM8350_DCDC4_LOW_POWER 0xBF +#define WM8350_DCDC5_CONTROL 0xC0 +#define WM8350_DCDC5_TIMEOUTS 0xC1 +#define WM8350_DCDC6_CONTROL 0xC3 +#define WM8350_DCDC6_TIMEOUTS 0xC4 +#define WM8350_DCDC6_LOW_POWER 0xC5 +#define WM8350_LIMIT_SWITCH_CONTROL 0xC7 +#define WM8350_LDO1_CONTROL 0xC8 +#define WM8350_LDO1_TIMEOUTS 0xC9 +#define WM8350_LDO1_LOW_POWER 0xCA +#define WM8350_LDO2_CONTROL 0xCB +#define WM8350_LDO2_TIMEOUTS 0xCC +#define WM8350_LDO2_LOW_POWER 0xCD +#define WM8350_LDO3_CONTROL 0xCE +#define WM8350_LDO3_TIMEOUTS 0xCF +#define WM8350_LDO3_LOW_POWER 0xD0 +#define WM8350_LDO4_CONTROL 0xD1 +#define WM8350_LDO4_TIMEOUTS 0xD2 +#define WM8350_LDO4_LOW_POWER 0xD3 +#define WM8350_VCC_FAULT_MASKS 0xD7 +#define WM8350_MAIN_BANDGAP_CONTROL 0xD8 +#define WM8350_OSC_CONTROL 0xD9 +#define WM8350_RTC_TICK_CONTROL 0xDA +#define WM8350_SECURITY 0xDB +#define WM8350_RAM_BIST_1 0xDC +#define WM8350_DCDC_LDO_STATUS 0xE1 +#define WM8350_GPIO_PIN_STATUS 0xE6 + +#define WM8350_DCDC1_FORCE_PWM 0xF8 +#define WM8350_DCDC3_FORCE_PWM 0xFA +#define WM8350_DCDC4_FORCE_PWM 0xFB +#define WM8350_DCDC6_FORCE_PWM 0xFD + +/* + * R172 (0xAC) - Current Sink Driver A + */ +#define WM8350_CS1_HIB_MODE 0x1000 +#define WM8350_CS1_HIB_MODE_MASK 0x1000 +#define WM8350_CS1_HIB_MODE_SHIFT 12 +#define WM8350_CS1_ISEL_MASK 0x003F +#define WM8350_CS1_ISEL_SHIFT 0 + +/* Bit values for R172 (0xAC) */ +#define WM8350_CS1_HIB_MODE_DISABLE 0 +#define WM8350_CS1_HIB_MODE_LEAVE 1 + +#define WM8350_CS1_ISEL_220M 0x3F + +/* + * R173 (0xAD) - CSA Flash control + */ +#define WM8350_CS1_FLASH_MODE 0x8000 +#define WM8350_CS1_TRIGSRC 0x4000 +#define WM8350_CS1_DRIVE 0x2000 +#define WM8350_CS1_FLASH_DUR_MASK 0x0300 +#define WM8350_CS1_OFF_RAMP_MASK 0x0030 +#define WM8350_CS1_ON_RAMP_MASK 0x0003 + +/* + * R174 (0xAE) - Current Sink Driver B + */ +#define WM8350_CS2_HIB_MODE 0x1000 +#define WM8350_CS2_ISEL_MASK 0x003F + +/* + * R175 (0xAF) - CSB Flash control + */ +#define WM8350_CS2_FLASH_MODE 0x8000 +#define WM8350_CS2_TRIGSRC 0x4000 +#define WM8350_CS2_DRIVE 0x2000 +#define WM8350_CS2_FLASH_DUR_MASK 0x0300 +#define WM8350_CS2_OFF_RAMP_MASK 0x0030 +#define WM8350_CS2_ON_RAMP_MASK 0x0003 + +/* + * R176 (0xB0) - DCDC/LDO requested + */ +#define WM8350_LS_ENA 0x8000 +#define WM8350_LDO4_ENA 0x0800 +#define WM8350_LDO3_ENA 0x0400 +#define WM8350_LDO2_ENA 0x0200 +#define WM8350_LDO1_ENA 0x0100 +#define WM8350_DC6_ENA 0x0020 +#define WM8350_DC5_ENA 0x0010 +#define WM8350_DC4_ENA 0x0008 +#define WM8350_DC3_ENA 0x0004 +#define WM8350_DC2_ENA 0x0002 +#define WM8350_DC1_ENA 0x0001 + +/* + * R177 (0xB1) - DCDC Active options + */ +#define WM8350_PUTO_MASK 0x3000 +#define WM8350_PWRUP_DELAY_MASK 0x0300 +#define WM8350_DC6_ACTIVE 0x0020 +#define WM8350_DC4_ACTIVE 0x0008 +#define WM8350_DC3_ACTIVE 0x0004 +#define WM8350_DC1_ACTIVE 0x0001 + +/* + * R178 (0xB2) - DCDC Sleep options + */ +#define WM8350_DC6_SLEEP 0x0020 +#define WM8350_DC4_SLEEP 0x0008 +#define WM8350_DC3_SLEEP 0x0004 +#define WM8350_DC1_SLEEP 0x0001 + +/* + * R179 (0xB3) - Power-check comparator + */ +#define WM8350_PCCMP_ERRACT 0x4000 +#define WM8350_PCCMP_RAIL 0x0100 +#define WM8350_PCCMP_OFF_THR_MASK 0x0070 +#define WM8350_PCCMP_ON_THR_MASK 0x0007 + +/* + * R180 (0xB4) - DCDC1 Control + */ +#define WM8350_DC1_OPFLT 0x0400 +#define WM8350_DC1_VSEL_MASK 0x007F +#define WM8350_DC1_VSEL_SHIFT 0 + +/* + * R181 (0xB5) - DCDC1 Timeouts + */ +#define WM8350_DC1_ERRACT_MASK 0xC000 +#define WM8350_DC1_ERRACT_SHIFT 14 +#define WM8350_DC1_ENSLOT_MASK 0x3C00 +#define WM8350_DC1_ENSLOT_SHIFT 10 +#define WM8350_DC1_SDSLOT_MASK 0x03C0 +#define WM8350_DC1_UVTO_MASK 0x0030 +#define WM8350_DC1_SDSLOT_SHIFT 6 + +/* Bit values for R181 (0xB5) */ +#define WM8350_DC1_ERRACT_NONE 0 +#define WM8350_DC1_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC1_ERRACT_SHUTDOWN_SYS 2 + +/* + * R182 (0xB6) - DCDC1 Low Power + */ +#define WM8350_DC1_HIB_MODE_MASK 0x7000 +#define WM8350_DC1_HIB_TRIG_MASK 0x0300 +#define WM8350_DC1_VIMG_MASK 0x007F + +/* + * R183 (0xB7) - DCDC2 Control + */ +#define WM8350_DC2_MODE 0x4000 +#define WM8350_DC2_MODE_MASK 0x4000 +#define WM8350_DC2_MODE_SHIFT 14 +#define WM8350_DC2_HIB_MODE 0x1000 +#define WM8350_DC2_HIB_MODE_MASK 0x1000 +#define WM8350_DC2_HIB_MODE_SHIFT 12 +#define WM8350_DC2_HIB_TRIG_MASK 0x0300 +#define WM8350_DC2_HIB_TRIG_SHIFT 8 +#define WM8350_DC2_ILIM 0x0040 +#define WM8350_DC2_ILIM_MASK 0x0040 +#define WM8350_DC2_ILIM_SHIFT 6 +#define WM8350_DC2_RMP_MASK 0x0018 +#define WM8350_DC2_RMP_SHIFT 3 +#define WM8350_DC2_FBSRC_MASK 0x0003 +#define WM8350_DC2_FBSRC_SHIFT 0 + +/* Bit values for R183 (0xB7) */ +#define WM8350_DC2_MODE_BOOST 0 +#define WM8350_DC2_MODE_SWITCH 1 + +#define WM8350_DC2_HIB_MODE_ACTIVE 1 +#define WM8350_DC2_HIB_MODE_DISABLE 0 + +#define WM8350_DC2_HIB_TRIG_NONE 0 +#define WM8350_DC2_HIB_TRIG_LPWR1 1 +#define WM8350_DC2_HIB_TRIG_LPWR2 2 +#define WM8350_DC2_HIB_TRIG_LPWR3 3 + +#define WM8350_DC2_ILIM_HIGH 0 +#define WM8350_DC2_ILIM_LOW 1 + +#define WM8350_DC2_RMP_30V 0 +#define WM8350_DC2_RMP_20V 1 +#define WM8350_DC2_RMP_10V 2 +#define WM8350_DC2_RMP_5V 3 + +#define WM8350_DC2_FBSRC_FB2 0 +#define WM8350_DC2_FBSRC_ISINKA 1 +#define WM8350_DC2_FBSRC_ISINKB 2 +#define WM8350_DC2_FBSRC_USB 3 + +/* + * R184 (0xB8) - DCDC2 Timeouts + */ +#define WM8350_DC2_ERRACT_MASK 0xC000 +#define WM8350_DC2_ERRACT_SHIFT 14 +#define WM8350_DC2_ENSLOT_MASK 0x3C00 +#define WM8350_DC2_ENSLOT_SHIFT 10 +#define WM8350_DC2_SDSLOT_MASK 0x03C0 +#define WM8350_DC2_UVTO_MASK 0x0030 + +/* Bit values for R184 (0xB8) */ +#define WM8350_DC2_ERRACT_NONE 0 +#define WM8350_DC2_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC2_ERRACT_SHUTDOWN_SYS 2 + +/* + * R186 (0xBA) - DCDC3 Control + */ +#define WM8350_DC3_OPFLT 0x0400 +#define WM8350_DC3_VSEL_MASK 0x007F +#define WM8350_DC3_VSEL_SHIFT 0 + +/* + * R187 (0xBB) - DCDC3 Timeouts + */ +#define WM8350_DC3_ERRACT_MASK 0xC000 +#define WM8350_DC3_ERRACT_SHIFT 14 +#define WM8350_DC3_ENSLOT_MASK 0x3C00 +#define WM8350_DC3_ENSLOT_SHIFT 10 +#define WM8350_DC3_SDSLOT_MASK 0x03C0 +#define WM8350_DC3_UVTO_MASK 0x0030 +#define WM8350_DC3_SDSLOT_SHIFT 6 + +/* Bit values for R187 (0xBB) */ +#define WM8350_DC3_ERRACT_NONE 0 +#define WM8350_DC3_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC3_ERRACT_SHUTDOWN_SYS 2 +/* + * R188 (0xBC) - DCDC3 Low Power + */ +#define WM8350_DC3_HIB_MODE_MASK 0x7000 +#define WM8350_DC3_HIB_TRIG_MASK 0x0300 +#define WM8350_DC3_VIMG_MASK 0x007F + +/* + * R189 (0xBD) - DCDC4 Control + */ +#define WM8350_DC4_OPFLT 0x0400 +#define WM8350_DC4_VSEL_MASK 0x007F +#define WM8350_DC4_VSEL_SHIFT 0 + +/* + * R190 (0xBE) - DCDC4 Timeouts + */ +#define WM8350_DC4_ERRACT_MASK 0xC000 +#define WM8350_DC4_ERRACT_SHIFT 14 +#define WM8350_DC4_ENSLOT_MASK 0x3C00 +#define WM8350_DC4_ENSLOT_SHIFT 10 +#define WM8350_DC4_SDSLOT_MASK 0x03C0 +#define WM8350_DC4_UVTO_MASK 0x0030 +#define WM8350_DC4_SDSLOT_SHIFT 6 + +/* Bit values for R190 (0xBE) */ +#define WM8350_DC4_ERRACT_NONE 0 +#define WM8350_DC4_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC4_ERRACT_SHUTDOWN_SYS 2 + +/* + * R191 (0xBF) - DCDC4 Low Power + */ +#define WM8350_DC4_HIB_MODE_MASK 0x7000 +#define WM8350_DC4_HIB_TRIG_MASK 0x0300 +#define WM8350_DC4_VIMG_MASK 0x007F + +/* + * R192 (0xC0) - DCDC5 Control + */ +#define WM8350_DC5_MODE 0x4000 +#define WM8350_DC5_MODE_MASK 0x4000 +#define WM8350_DC5_MODE_SHIFT 14 +#define WM8350_DC5_HIB_MODE 0x1000 +#define WM8350_DC5_HIB_MODE_MASK 0x1000 +#define WM8350_DC5_HIB_MODE_SHIFT 12 +#define WM8350_DC5_HIB_TRIG_MASK 0x0300 +#define WM8350_DC5_HIB_TRIG_SHIFT 8 +#define WM8350_DC5_ILIM 0x0040 +#define WM8350_DC5_ILIM_MASK 0x0040 +#define WM8350_DC5_ILIM_SHIFT 6 +#define WM8350_DC5_RMP_MASK 0x0018 +#define WM8350_DC5_RMP_SHIFT 3 +#define WM8350_DC5_FBSRC_MASK 0x0003 +#define WM8350_DC5_FBSRC_SHIFT 0 + +/* Bit values for R192 (0xC0) */ +#define WM8350_DC5_MODE_BOOST 0 +#define WM8350_DC5_MODE_SWITCH 1 + +#define WM8350_DC5_HIB_MODE_ACTIVE 1 +#define WM8350_DC5_HIB_MODE_DISABLE 0 + +#define WM8350_DC5_HIB_TRIG_NONE 0 +#define WM8350_DC5_HIB_TRIG_LPWR1 1 +#define WM8350_DC5_HIB_TRIG_LPWR2 2 +#define WM8350_DC5_HIB_TRIG_LPWR3 3 + +#define WM8350_DC5_ILIM_HIGH 0 +#define WM8350_DC5_ILIM_LOW 1 + +#define WM8350_DC5_RMP_30V 0 +#define WM8350_DC5_RMP_20V 1 +#define WM8350_DC5_RMP_10V 2 +#define WM8350_DC5_RMP_5V 3 + +#define WM8350_DC5_FBSRC_FB2 0 +#define WM8350_DC5_FBSRC_ISINKA 1 +#define WM8350_DC5_FBSRC_ISINKB 2 +#define WM8350_DC5_FBSRC_USB 3 + +/* + * R193 (0xC1) - DCDC5 Timeouts + */ +#define WM8350_DC5_ERRACT_MASK 0xC000 +#define WM8350_DC5_ERRACT_SHIFT 14 +#define WM8350_DC5_ENSLOT_MASK 0x3C00 +#define WM8350_DC5_ENSLOT_SHIFT 10 +#define WM8350_DC5_SDSLOT_MASK 0x03C0 +#define WM8350_DC5_UVTO_MASK 0x0030 +#define WM8350_DC5_SDSLOT_SHIFT 6 + +/* Bit values for R193 (0xC1) */ +#define WM8350_DC5_ERRACT_NONE 0 +#define WM8350_DC5_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC5_ERRACT_SHUTDOWN_SYS 2 + +/* + * R195 (0xC3) - DCDC6 Control + */ +#define WM8350_DC6_OPFLT 0x0400 +#define WM8350_DC6_VSEL_MASK 0x007F +#define WM8350_DC6_VSEL_SHIFT 0 + +/* + * R196 (0xC4) - DCDC6 Timeouts + */ +#define WM8350_DC6_ERRACT_MASK 0xC000 +#define WM8350_DC6_ERRACT_SHIFT 14 +#define WM8350_DC6_ENSLOT_MASK 0x3C00 +#define WM8350_DC6_ENSLOT_SHIFT 10 +#define WM8350_DC6_SDSLOT_MASK 0x03C0 +#define WM8350_DC6_UVTO_MASK 0x0030 +#define WM8350_DC6_SDSLOT_SHIFT 6 + +/* Bit values for R196 (0xC4) */ +#define WM8350_DC6_ERRACT_NONE 0 +#define WM8350_DC6_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC6_ERRACT_SHUTDOWN_SYS 2 + +/* + * R197 (0xC5) - DCDC6 Low Power + */ +#define WM8350_DC6_HIB_MODE_MASK 0x7000 +#define WM8350_DC6_HIB_TRIG_MASK 0x0300 +#define WM8350_DC6_VIMG_MASK 0x007F + +/* + * R199 (0xC7) - Limit Switch Control + */ +#define WM8350_LS_ERRACT_MASK 0xC000 +#define WM8350_LS_ERRACT_SHIFT 14 +#define WM8350_LS_ENSLOT_MASK 0x3C00 +#define WM8350_LS_ENSLOT_SHIFT 10 +#define WM8350_LS_SDSLOT_MASK 0x03C0 +#define WM8350_LS_SDSLOT_SHIFT 6 +#define WM8350_LS_HIB_MODE 0x0010 +#define WM8350_LS_HIB_MODE_MASK 0x0010 +#define WM8350_LS_HIB_MODE_SHIFT 4 +#define WM8350_LS_HIB_PROT 0x0002 +#define WM8350_LS_HIB_PROT_MASK 0x0002 +#define WM8350_LS_HIB_PROT_SHIFT 1 +#define WM8350_LS_PROT 0x0001 +#define WM8350_LS_PROT_MASK 0x0001 +#define WM8350_LS_PROT_SHIFT 0 + +/* Bit values for R199 (0xC7) */ +#define WM8350_LS_ERRACT_NONE 0 +#define WM8350_LS_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LS_ERRACT_SHUTDOWN_SYS 2 + +/* + * R200 (0xC8) - LDO1 Control + */ +#define WM8350_LDO1_SWI 0x4000 +#define WM8350_LDO1_OPFLT 0x0400 +#define WM8350_LDO1_VSEL_MASK 0x001F +#define WM8350_LDO1_VSEL_SHIFT 0 + +/* + * R201 (0xC9) - LDO1 Timeouts + */ +#define WM8350_LDO1_ERRACT_MASK 0xC000 +#define WM8350_LDO1_ERRACT_SHIFT 14 +#define WM8350_LDO1_ENSLOT_MASK 0x3C00 +#define WM8350_LDO1_ENSLOT_SHIFT 10 +#define WM8350_LDO1_SDSLOT_MASK 0x03C0 +#define WM8350_LDO1_UVTO_MASK 0x0030 +#define WM8350_LDO1_SDSLOT_SHIFT 6 + +/* Bit values for R201 (0xC9) */ +#define WM8350_LDO1_ERRACT_NONE 0 +#define WM8350_LDO1_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO1_ERRACT_SHUTDOWN_SYS 2 + +/* + * R202 (0xCA) - LDO1 Low Power + */ +#define WM8350_LDO1_HIB_MODE_MASK 0x3000 +#define WM8350_LDO1_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO1_VIMG_MASK 0x001F +#define WM8350_LDO1_HIB_MODE_DIS (0x1 << 12) + + +/* + * R203 (0xCB) - LDO2 Control + */ +#define WM8350_LDO2_SWI 0x4000 +#define WM8350_LDO2_OPFLT 0x0400 +#define WM8350_LDO2_VSEL_MASK 0x001F +#define WM8350_LDO2_VSEL_SHIFT 0 + +/* + * R204 (0xCC) - LDO2 Timeouts + */ +#define WM8350_LDO2_ERRACT_MASK 0xC000 +#define WM8350_LDO2_ERRACT_SHIFT 14 +#define WM8350_LDO2_ENSLOT_MASK 0x3C00 +#define WM8350_LDO2_ENSLOT_SHIFT 10 +#define WM8350_LDO2_SDSLOT_MASK 0x03C0 +#define WM8350_LDO2_SDSLOT_SHIFT 6 + +/* Bit values for R204 (0xCC) */ +#define WM8350_LDO2_ERRACT_NONE 0 +#define WM8350_LDO2_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO2_ERRACT_SHUTDOWN_SYS 2 + +/* + * R205 (0xCD) - LDO2 Low Power + */ +#define WM8350_LDO2_HIB_MODE_MASK 0x3000 +#define WM8350_LDO2_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO2_VIMG_MASK 0x001F + +/* + * R206 (0xCE) - LDO3 Control + */ +#define WM8350_LDO3_SWI 0x4000 +#define WM8350_LDO3_OPFLT 0x0400 +#define WM8350_LDO3_VSEL_MASK 0x001F +#define WM8350_LDO3_VSEL_SHIFT 0 + +/* + * R207 (0xCF) - LDO3 Timeouts + */ +#define WM8350_LDO3_ERRACT_MASK 0xC000 +#define WM8350_LDO3_ERRACT_SHIFT 14 +#define WM8350_LDO3_ENSLOT_MASK 0x3C00 +#define WM8350_LDO3_ENSLOT_SHIFT 10 +#define WM8350_LDO3_SDSLOT_MASK 0x03C0 +#define WM8350_LDO3_UVTO_MASK 0x0030 +#define WM8350_LDO3_SDSLOT_SHIFT 6 + +/* Bit values for R207 (0xCF) */ +#define WM8350_LDO3_ERRACT_NONE 0 +#define WM8350_LDO3_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO3_ERRACT_SHUTDOWN_SYS 2 + +/* + * R208 (0xD0) - LDO3 Low Power + */ +#define WM8350_LDO3_HIB_MODE_MASK 0x3000 +#define WM8350_LDO3_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO3_VIMG_MASK 0x001F + +/* + * R209 (0xD1) - LDO4 Control + */ +#define WM8350_LDO4_SWI 0x4000 +#define WM8350_LDO4_OPFLT 0x0400 +#define WM8350_LDO4_VSEL_MASK 0x001F +#define WM8350_LDO4_VSEL_SHIFT 0 + +/* + * R210 (0xD2) - LDO4 Timeouts + */ +#define WM8350_LDO4_ERRACT_MASK 0xC000 +#define WM8350_LDO4_ERRACT_SHIFT 14 +#define WM8350_LDO4_ENSLOT_MASK 0x3C00 +#define WM8350_LDO4_ENSLOT_SHIFT 10 +#define WM8350_LDO4_SDSLOT_MASK 0x03C0 +#define WM8350_LDO4_UVTO_MASK 0x0030 +#define WM8350_LDO4_SDSLOT_SHIFT 6 + +/* Bit values for R210 (0xD2) */ +#define WM8350_LDO4_ERRACT_NONE 0 +#define WM8350_LDO4_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO4_ERRACT_SHUTDOWN_SYS 2 + +/* + * R211 (0xD3) - LDO4 Low Power + */ +#define WM8350_LDO4_HIB_MODE_MASK 0x3000 +#define WM8350_LDO4_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO4_VIMG_MASK 0x001F + +/* + * R215 (0xD7) - VCC_FAULT Masks + */ +#define WM8350_LS_FAULT 0x8000 +#define WM8350_LDO4_FAULT 0x0800 +#define WM8350_LDO3_FAULT 0x0400 +#define WM8350_LDO2_FAULT 0x0200 +#define WM8350_LDO1_FAULT 0x0100 +#define WM8350_DC6_FAULT 0x0020 +#define WM8350_DC5_FAULT 0x0010 +#define WM8350_DC4_FAULT 0x0008 +#define WM8350_DC3_FAULT 0x0004 +#define WM8350_DC2_FAULT 0x0002 +#define WM8350_DC1_FAULT 0x0001 + +/* + * R216 (0xD8) - Main Bandgap Control + */ +#define WM8350_MBG_LOAD_FUSES 0x8000 +#define WM8350_MBG_FUSE_WPREP 0x4000 +#define WM8350_MBG_FUSE_WRITE 0x2000 +#define WM8350_MBG_FUSE_TRIM_MASK 0x1F00 +#define WM8350_MBG_TRIM_SRC 0x0020 +#define WM8350_MBG_USER_TRIM_MASK 0x001F + +/* + * R217 (0xD9) - OSC Control + */ +#define WM8350_OSC_LOAD_FUSES 0x8000 +#define WM8350_OSC_FUSE_WPREP 0x4000 +#define WM8350_OSC_FUSE_WRITE 0x2000 +#define WM8350_OSC_FUSE_TRIM_MASK 0x0F00 +#define WM8350_OSC_TRIM_SRC 0x0020 +#define WM8350_OSC_USER_TRIM_MASK 0x000F + +/* + * R248 (0xF8) - DCDC1 Force PWM + */ +#define WM8350_DCDC1_FORCE_PWM_ENA 0x0010 + +/* + * R250 (0xFA) - DCDC3 Force PWM + */ +#define WM8350_DCDC3_FORCE_PWM_ENA 0x0010 + +/* + * R251 (0xFB) - DCDC4 Force PWM + */ +#define WM8350_DCDC4_FORCE_PWM_ENA 0x0010 + +/* + * R253 (0xFD) - DCDC1 Force PWM + */ +#define WM8350_DCDC6_FORCE_PWM_ENA 0x0010 + +/* + * DCDC's + */ +#define WM8350_DCDC_1 0 +#define WM8350_DCDC_2 1 +#define WM8350_DCDC_3 2 +#define WM8350_DCDC_4 3 +#define WM8350_DCDC_5 4 +#define WM8350_DCDC_6 5 + +/* DCDC modes */ +#define WM8350_DCDC_ACTIVE_STANDBY 0 +#define WM8350_DCDC_ACTIVE_PULSE 1 +#define WM8350_DCDC_SLEEP_NORMAL 0 +#define WM8350_DCDC_SLEEP_LOW 1 + +/* DCDC Low power (Hibernate) mode */ +#define WM8350_DCDC_HIB_MODE_CUR (0 << 12) +#define WM8350_DCDC_HIB_MODE_IMAGE (1 << 12) +#define WM8350_DCDC_HIB_MODE_STANDBY (2 << 12) +#define WM8350_DCDC_HIB_MODE_LDO (4 << 12) +#define WM8350_DCDC_HIB_MODE_LDO_IM (5 << 12) +#define WM8350_DCDC_HIB_MODE_DIS (7 << 12) +#define WM8350_DCDC_HIB_MODE_MASK (7 << 12) + +/* DCDC Low Power (Hibernate) signal */ +#define WM8350_DCDC_HIB_SIG_REG (0 << 8) +#define WM8350_DCDC_HIB_SIG_LPWR1 (1 << 8) +#define WM8350_DCDC_HIB_SIG_LPWR2 (2 << 8) +#define WM8350_DCDC_HIB_SIG_LPWR3 (3 << 8) + +/* LDO Low power (Hibernate) mode */ +#define WM8350_LDO_HIB_MODE_IMAGE (0 << 0) +#define WM8350_LDO_HIB_MODE_DIS (1 << 0) + +/* LDO Low Power (Hibernate) signal */ +#define WM8350_LDO_HIB_SIG_REG (0 << 8) +#define WM8350_LDO_HIB_SIG_LPWR1 (1 << 8) +#define WM8350_LDO_HIB_SIG_LPWR2 (2 << 8) +#define WM8350_LDO_HIB_SIG_LPWR3 (3 << 8) + +/* + * LDOs + */ +#define WM8350_LDO_1 6 +#define WM8350_LDO_2 7 +#define WM8350_LDO_3 8 +#define WM8350_LDO_4 9 + +/* + * ISINKs + */ +#define WM8350_ISINK_A 10 +#define WM8350_ISINK_B 11 + +#define WM8350_ISINK_MODE_BOOST 0 +#define WM8350_ISINK_MODE_SWITCH 1 +#define WM8350_ISINK_ILIM_NORMAL 0 +#define WM8350_ISINK_ILIM_LOW 1 + +#define WM8350_ISINK_FLASH_DISABLE 0 +#define WM8350_ISINK_FLASH_ENABLE 1 +#define WM8350_ISINK_FLASH_TRIG_BIT 0 +#define WM8350_ISINK_FLASH_TRIG_GPIO 1 +#define WM8350_ISINK_FLASH_MODE_EN (1 << 13) +#define WM8350_ISINK_FLASH_MODE_DIS (0 << 13) +#define WM8350_ISINK_FLASH_DUR_32MS (0 << 8) +#define WM8350_ISINK_FLASH_DUR_64MS (1 << 8) +#define WM8350_ISINK_FLASH_DUR_96MS (2 << 8) +#define WM8350_ISINK_FLASH_DUR_1024MS (3 << 8) +#define WM8350_ISINK_FLASH_ON_INSTANT (0 << 4) +#define WM8350_ISINK_FLASH_ON_0_25S (1 << 4) +#define WM8350_ISINK_FLASH_ON_0_50S (2 << 4) +#define WM8350_ISINK_FLASH_ON_1_00S (3 << 4) +#define WM8350_ISINK_FLASH_ON_1_95S (1 << 4) +#define WM8350_ISINK_FLASH_ON_3_91S (2 << 4) +#define WM8350_ISINK_FLASH_ON_7_80S (3 << 4) +#define WM8350_ISINK_FLASH_OFF_INSTANT (0 << 0) +#define WM8350_ISINK_FLASH_OFF_0_25S (1 << 0) +#define WM8350_ISINK_FLASH_OFF_0_50S (2 << 0) +#define WM8350_ISINK_FLASH_OFF_1_00S (3 << 0) +#define WM8350_ISINK_FLASH_OFF_1_95S (1 << 0) +#define WM8350_ISINK_FLASH_OFF_3_91S (2 << 0) +#define WM8350_ISINK_FLASH_OFF_7_80S (3 << 0) + +/* + * Regulator Interrupts. + */ +#define WM8350_IRQ_CS1 13 +#define WM8350_IRQ_CS2 14 +#define WM8350_IRQ_UV_LDO4 25 +#define WM8350_IRQ_UV_LDO3 26 +#define WM8350_IRQ_UV_LDO2 27 +#define WM8350_IRQ_UV_LDO1 28 +#define WM8350_IRQ_UV_DC6 29 +#define WM8350_IRQ_UV_DC5 30 +#define WM8350_IRQ_UV_DC4 31 +#define WM8350_IRQ_UV_DC3 32 +#define WM8350_IRQ_UV_DC2 33 +#define WM8350_IRQ_UV_DC1 34 +#define WM8350_IRQ_OC_LS 35 + +#define NUM_WM8350_REGULATORS 12 + +#endif -- cgit v1.2.3 From f8ea79c2ab39513e90054ed2828a85759d979bfa Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:06 +0100 Subject: mfd: Add WM8350 PMU register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/supply.h | 105 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 include/linux/mfd/wm8350/supply.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/supply.h b/include/linux/mfd/wm8350/supply.h new file mode 100644 index 000000000000..f1d4317cf022 --- /dev/null +++ b/include/linux/mfd/wm8350/supply.h @@ -0,0 +1,105 @@ +/* + * supply.h -- Power Supply Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + * + */ + +#ifndef __LINUX_MFD_WM8350_SUPPLY_H_ +#define __LINUX_MFD_WM8350_SUPPLY_H_ + +/* + * Charger registers + */ +#define WM8350_BATTERY_CHARGER_CONTROL_1 0xA8 +#define WM8350_BATTERY_CHARGER_CONTROL_2 0xA9 +#define WM8350_BATTERY_CHARGER_CONTROL_3 0xAA + +/* + * R168 (0xA8) - Battery Charger Control 1 + */ +#define WM8350_CHG_ENA_R168 0x8000 +#define WM8350_CHG_THR 0x2000 +#define WM8350_CHG_EOC_SEL_MASK 0x1C00 +#define WM8350_CHG_TRICKLE_TEMP_CHOKE 0x0200 +#define WM8350_CHG_TRICKLE_USB_CHOKE 0x0100 +#define WM8350_CHG_RECOVER_T 0x0080 +#define WM8350_CHG_END_ACT 0x0040 +#define WM8350_CHG_FAST 0x0020 +#define WM8350_CHG_FAST_USB_THROTTLE 0x0010 +#define WM8350_CHG_NTC_MON 0x0008 +#define WM8350_CHG_BATT_HOT_MON 0x0004 +#define WM8350_CHG_BATT_COLD_MON 0x0002 +#define WM8350_CHG_CHIP_TEMP_MON 0x0001 + +/* + * R169 (0xA9) - Battery Charger Control 2 + */ +#define WM8350_CHG_ACTIVE 0x8000 +#define WM8350_CHG_PAUSE 0x4000 +#define WM8350_CHG_STS_MASK 0x3000 +#define WM8350_CHG_TIME_MASK 0x0F00 +#define WM8350_CHG_MASK_WALL_FB 0x0080 +#define WM8350_CHG_TRICKLE_SEL 0x0040 +#define WM8350_CHG_VSEL_MASK 0x0030 +#define WM8350_CHG_ISEL_MASK 0x000F +#define WM8350_CHG_STS_OFF 0x0000 +#define WM8350_CHG_STS_TRICKLE 0x1000 +#define WM8350_CHG_STS_FAST 0x2000 + +/* + * R170 (0xAA) - Battery Charger Control 3 + */ +#define WM8350_CHG_THROTTLE_T_MASK 0x0060 +#define WM8350_CHG_SMART 0x0010 +#define WM8350_CHG_TIMER_ADJT_MASK 0x000F + +/* + * Charger Interrupts + */ +#define WM8350_IRQ_CHG_BAT_HOT 0 +#define WM8350_IRQ_CHG_BAT_COLD 1 +#define WM8350_IRQ_CHG_BAT_FAIL 2 +#define WM8350_IRQ_CHG_TO 3 +#define WM8350_IRQ_CHG_END 4 +#define WM8350_IRQ_CHG_START 5 +#define WM8350_IRQ_CHG_FAST_RDY 6 +#define WM8350_IRQ_CHG_VBATT_LT_3P9 10 +#define WM8350_IRQ_CHG_VBATT_LT_3P1 11 +#define WM8350_IRQ_CHG_VBATT_LT_2P85 12 + +/* + * Charger Policy + */ +#define WM8350_CHG_TRICKLE_50mA (0 << 6) +#define WM8350_CHG_TRICKLE_100mA (1 << 6) +#define WM8350_CHG_4_05V (0 << 4) +#define WM8350_CHG_4_10V (1 << 4) +#define WM8350_CHG_4_15V (2 << 4) +#define WM8350_CHG_4_20V (3 << 4) +#define WM8350_CHG_FAST_LIMIT_mA(x) ((x / 50) & 0xf) +#define WM8350_CHG_EOC_mA(x) (((x - 10) & 0x7) << 10) +#define WM8350_CHG_TRICKLE_3_1V (0 << 13) +#define WM8350_CHG_TRICKLE_3_9V (1 << 13) + +/* + * Supply Registers. + */ +#define WM8350_USB_VOLTAGE_READBACK 0x9C +#define WM8350_LINE_VOLTAGE_READBACK 0x9D +#define WM8350_BATT_VOLTAGE_READBACK 0x9E + +/* + * Supply Interrupts. + */ +#define WM8350_IRQ_USB_LIMIT 15 +#define WM8350_IRQ_EXT_USB_FB 36 +#define WM8350_IRQ_EXT_WALL_FB 37 +#define WM8350_IRQ_EXT_BAT_FB 38 + +#endif -- cgit v1.2.3 From e7aabc346b111ae2ebcd390dcca7200a3361bb62 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:07 +0100 Subject: mfd: Add WM8350 comparator register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/comparator.h | 167 ++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 include/linux/mfd/wm8350/comparator.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/comparator.h b/include/linux/mfd/wm8350/comparator.h new file mode 100644 index 000000000000..053788649452 --- /dev/null +++ b/include/linux/mfd/wm8350/comparator.h @@ -0,0 +1,167 @@ +/* + * comparator.h -- Comparator Aux ADC for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + */ + +#ifndef __LINUX_MFD_WM8350_COMPARATOR_H_ +#define __LINUX_MFD_WM8350_COMPARATOR_H_ + +/* + * Registers + */ + +#define WM8350_DIGITISER_CONTROL_1 0x90 +#define WM8350_DIGITISER_CONTROL_2 0x91 +#define WM8350_AUX1_READBACK 0x98 +#define WM8350_AUX2_READBACK 0x99 +#define WM8350_AUX3_READBACK 0x9A +#define WM8350_AUX4_READBACK 0x9B +#define WM8350_CHIP_TEMP_READBACK 0x9F +#define WM8350_GENERIC_COMPARATOR_CONTROL 0xA3 +#define WM8350_GENERIC_COMPARATOR_1 0xA4 +#define WM8350_GENERIC_COMPARATOR_2 0xA5 +#define WM8350_GENERIC_COMPARATOR_3 0xA6 +#define WM8350_GENERIC_COMPARATOR_4 0xA7 + +/* + * R144 (0x90) - Digitiser Control (1) + */ +#define WM8350_AUXADC_CTC 0x4000 +#define WM8350_AUXADC_POLL 0x2000 +#define WM8350_AUXADC_HIB_MODE 0x1000 +#define WM8350_AUXADC_SEL8 0x0080 +#define WM8350_AUXADC_SEL7 0x0040 +#define WM8350_AUXADC_SEL6 0x0020 +#define WM8350_AUXADC_SEL5 0x0010 +#define WM8350_AUXADC_SEL4 0x0008 +#define WM8350_AUXADC_SEL3 0x0004 +#define WM8350_AUXADC_SEL2 0x0002 +#define WM8350_AUXADC_SEL1 0x0001 + +/* + * R145 (0x91) - Digitiser Control (2) + */ +#define WM8350_AUXADC_MASKMODE_MASK 0x3000 +#define WM8350_AUXADC_CRATE_MASK 0x0700 +#define WM8350_AUXADC_CAL 0x0004 +#define WM8350_AUX_RBMODE 0x0002 +#define WM8350_AUXADC_WAIT 0x0001 + +/* + * R152 (0x98) - AUX1 Readback + */ +#define WM8350_AUXADC_SCALE1_MASK 0x6000 +#define WM8350_AUXADC_REF1 0x1000 +#define WM8350_AUXADC_DATA1_MASK 0x0FFF + +/* + * R153 (0x99) - AUX2 Readback + */ +#define WM8350_AUXADC_SCALE2_MASK 0x6000 +#define WM8350_AUXADC_REF2 0x1000 +#define WM8350_AUXADC_DATA2_MASK 0x0FFF + +/* + * R154 (0x9A) - AUX3 Readback + */ +#define WM8350_AUXADC_SCALE3_MASK 0x6000 +#define WM8350_AUXADC_REF3 0x1000 +#define WM8350_AUXADC_DATA3_MASK 0x0FFF + +/* + * R155 (0x9B) - AUX4 Readback + */ +#define WM8350_AUXADC_SCALE4_MASK 0x6000 +#define WM8350_AUXADC_REF4 0x1000 +#define WM8350_AUXADC_DATA4_MASK 0x0FFF + +/* + * R156 (0x9C) - USB Voltage Readback + */ +#define WM8350_AUXADC_DATA_USB_MASK 0x0FFF + +/* + * R157 (0x9D) - LINE Voltage Readback + */ +#define WM8350_AUXADC_DATA_LINE_MASK 0x0FFF + +/* + * R158 (0x9E) - BATT Voltage Readback + */ +#define WM8350_AUXADC_DATA_BATT_MASK 0x0FFF + +/* + * R159 (0x9F) - Chip Temp Readback + */ +#define WM8350_AUXADC_DATA_CHIPTEMP_MASK 0x0FFF + +/* + * R163 (0xA3) - Generic Comparator Control + */ +#define WM8350_DCMP4_ENA 0x0008 +#define WM8350_DCMP3_ENA 0x0004 +#define WM8350_DCMP2_ENA 0x0002 +#define WM8350_DCMP1_ENA 0x0001 + +/* + * R164 (0xA4) - Generic comparator 1 + */ +#define WM8350_DCMP1_SRCSEL_MASK 0xE000 +#define WM8350_DCMP1_GT 0x1000 +#define WM8350_DCMP1_THR_MASK 0x0FFF + +/* + * R165 (0xA5) - Generic comparator 2 + */ +#define WM8350_DCMP2_SRCSEL_MASK 0xE000 +#define WM8350_DCMP2_GT 0x1000 +#define WM8350_DCMP2_THR_MASK 0x0FFF + +/* + * R166 (0xA6) - Generic comparator 3 + */ +#define WM8350_DCMP3_SRCSEL_MASK 0xE000 +#define WM8350_DCMP3_GT 0x1000 +#define WM8350_DCMP3_THR_MASK 0x0FFF + +/* + * R167 (0xA7) - Generic comparator 4 + */ +#define WM8350_DCMP4_SRCSEL_MASK 0xE000 +#define WM8350_DCMP4_GT 0x1000 +#define WM8350_DCMP4_THR_MASK 0x0FFF + +/* + * Interrupts. + */ +#define WM8350_IRQ_AUXADC_DATARDY 16 +#define WM8350_IRQ_AUXADC_DCOMP4 17 +#define WM8350_IRQ_AUXADC_DCOMP3 18 +#define WM8350_IRQ_AUXADC_DCOMP2 19 +#define WM8350_IRQ_AUXADC_DCOMP1 20 +#define WM8350_IRQ_SYS_HYST_COMP_FAIL 21 +#define WM8350_IRQ_SYS_CHIP_GT115 22 +#define WM8350_IRQ_SYS_CHIP_GT140 23 + +/* + * USB/2, LINE & BATT = ((VRTC * 2) / 4095)) * 10e6 uV + * Where VRTC = 2.7 V + */ +#define WM8350_AUX_COEFF 1319 + +#define WM8350_AUXADC_AUX1 0 +#define WM8350_AUXADC_AUX2 1 +#define WM8350_AUXADC_AUX3 2 +#define WM8350_AUXADC_AUX4 3 +#define WM8350_AUXADC_USB 4 +#define WM8350_AUXADC_LINE 5 +#define WM8350_AUXADC_BATT 6 +#define WM8350_AUXADC_TEMP 7 + +#endif -- cgit v1.2.3 From 64a91ce93499dad945c4725d39b52f734d31b742 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:08 +0100 Subject: mfd: Add WM8350 RTC register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/rtc.h | 260 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 include/linux/mfd/wm8350/rtc.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/rtc.h b/include/linux/mfd/wm8350/rtc.h new file mode 100644 index 000000000000..cb337ea8082f --- /dev/null +++ b/include/linux/mfd/wm8350/rtc.h @@ -0,0 +1,260 @@ +/* + * rtc.h -- RTC driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + */ + +#ifndef __LINUX_MFD_WM8350_RTC_H +#define __LINUX_MFD_WM8350_RTC_H + +/* + * Register values. + */ +#define WM8350_RTC_SECONDS_MINUTES 0x10 +#define WM8350_RTC_HOURS_DAY 0x11 +#define WM8350_RTC_DATE_MONTH 0x12 +#define WM8350_RTC_YEAR 0x13 +#define WM8350_ALARM_SECONDS_MINUTES 0x14 +#define WM8350_ALARM_HOURS_DAY 0x15 +#define WM8350_ALARM_DATE_MONTH 0x16 +#define WM8350_RTC_TIME_CONTROL 0x17 + +/* + * R16 (0x10) - RTC Seconds/Minutes + */ +#define WM8350_RTC_MINS_MASK 0x7F00 +#define WM8350_RTC_MINS_SHIFT 8 +#define WM8350_RTC_SECS_MASK 0x007F +#define WM8350_RTC_SECS_SHIFT 0 + +/* + * R17 (0x11) - RTC Hours/Day + */ +#define WM8350_RTC_DAY_MASK 0x0700 +#define WM8350_RTC_DAY_SHIFT 8 +#define WM8350_RTC_HPM_MASK 0x0020 +#define WM8350_RTC_HPM_SHIFT 5 +#define WM8350_RTC_HRS_MASK 0x001F +#define WM8350_RTC_HRS_SHIFT 0 + +/* Bit values for R21 (0x15) */ +#define WM8350_RTC_DAY_SUN 1 +#define WM8350_RTC_DAY_MON 2 +#define WM8350_RTC_DAY_TUE 3 +#define WM8350_RTC_DAY_WED 4 +#define WM8350_RTC_DAY_THU 5 +#define WM8350_RTC_DAY_FRI 6 +#define WM8350_RTC_DAY_SAT 7 + +#define WM8350_RTC_HPM_AM 0 +#define WM8350_RTC_HPM_PM 1 + +/* + * R18 (0x12) - RTC Date/Month + */ +#define WM8350_RTC_MTH_MASK 0x1F00 +#define WM8350_RTC_MTH_SHIFT 8 +#define WM8350_RTC_DATE_MASK 0x003F +#define WM8350_RTC_DATE_SHIFT 0 + +/* Bit values for R22 (0x16) */ +#define WM8350_RTC_MTH_JAN 1 +#define WM8350_RTC_MTH_FEB 2 +#define WM8350_RTC_MTH_MAR 3 +#define WM8350_RTC_MTH_APR 4 +#define WM8350_RTC_MTH_MAY 5 +#define WM8350_RTC_MTH_JUN 6 +#define WM8350_RTC_MTH_JUL 7 +#define WM8350_RTC_MTH_AUG 8 +#define WM8350_RTC_MTH_SEP 9 +#define WM8350_RTC_MTH_OCT 10 +#define WM8350_RTC_MTH_NOV 11 +#define WM8350_RTC_MTH_DEC 12 +#define WM8350_RTC_MTH_JAN_BCD 0x01 +#define WM8350_RTC_MTH_FEB_BCD 0x02 +#define WM8350_RTC_MTH_MAR_BCD 0x03 +#define WM8350_RTC_MTH_APR_BCD 0x04 +#define WM8350_RTC_MTH_MAY_BCD 0x05 +#define WM8350_RTC_MTH_JUN_BCD 0x06 +#define WM8350_RTC_MTH_JUL_BCD 0x07 +#define WM8350_RTC_MTH_AUG_BCD 0x08 +#define WM8350_RTC_MTH_SEP_BCD 0x09 +#define WM8350_RTC_MTH_OCT_BCD 0x10 +#define WM8350_RTC_MTH_NOV_BCD 0x11 +#define WM8350_RTC_MTH_DEC_BCD 0x12 + +/* + * R19 (0x13) - RTC Year + */ +#define WM8350_RTC_YHUNDREDS_MASK 0x3F00 +#define WM8350_RTC_YHUNDREDS_SHIFT 8 +#define WM8350_RTC_YUNITS_MASK 0x00FF +#define WM8350_RTC_YUNITS_SHIFT 0 + +/* + * R20 (0x14) - Alarm Seconds/Minutes + */ +#define WM8350_RTC_ALMMINS_MASK 0x7F00 +#define WM8350_RTC_ALMMINS_SHIFT 8 +#define WM8350_RTC_ALMSECS_MASK 0x007F +#define WM8350_RTC_ALMSECS_SHIFT 0 + +/* Bit values for R20 (0x14) */ +#define WM8350_RTC_ALMMINS_DONT_CARE -1 +#define WM8350_RTC_ALMSECS_DONT_CARE -1 + +/* + * R21 (0x15) - Alarm Hours/Day + */ +#define WM8350_RTC_ALMDAY_MASK 0x0F00 +#define WM8350_RTC_ALMDAY_SHIFT 8 +#define WM8350_RTC_ALMHPM_MASK 0x0020 +#define WM8350_RTC_ALMHPM_SHIFT 5 +#define WM8350_RTC_ALMHRS_MASK 0x001F +#define WM8350_RTC_ALMHRS_SHIFT 0 + +/* Bit values for R21 (0x15) */ +#define WM8350_RTC_ALMDAY_DONT_CARE -1 +#define WM8350_RTC_ALMDAY_SUN 1 +#define WM8350_RTC_ALMDAY_MON 2 +#define WM8350_RTC_ALMDAY_TUE 3 +#define WM8350_RTC_ALMDAY_WED 4 +#define WM8350_RTC_ALMDAY_THU 5 +#define WM8350_RTC_ALMDAY_FRI 6 +#define WM8350_RTC_ALMDAY_SAT 7 + +#define WM8350_RTC_ALMHPM_AM 0 +#define WM8350_RTC_ALMHPM_PM 1 + +#define WM8350_RTC_ALMHRS_DONT_CARE -1 + +/* + * R22 (0x16) - Alarm Date/Month + */ +#define WM8350_RTC_ALMMTH_MASK 0x1F00 +#define WM8350_RTC_ALMMTH_SHIFT 8 +#define WM8350_RTC_ALMDATE_MASK 0x003F +#define WM8350_RTC_ALMDATE_SHIFT 0 + +/* Bit values for R22 (0x16) */ +#define WM8350_RTC_ALMDATE_DONT_CARE -1 + +#define WM8350_RTC_ALMMTH_DONT_CARE -1 +#define WM8350_RTC_ALMMTH_JAN 1 +#define WM8350_RTC_ALMMTH_FEB 2 +#define WM8350_RTC_ALMMTH_MAR 3 +#define WM8350_RTC_ALMMTH_APR 4 +#define WM8350_RTC_ALMMTH_MAY 5 +#define WM8350_RTC_ALMMTH_JUN 6 +#define WM8350_RTC_ALMMTH_JUL 7 +#define WM8350_RTC_ALMMTH_AUG 8 +#define WM8350_RTC_ALMMTH_SEP 9 +#define WM8350_RTC_ALMMTH_OCT 10 +#define WM8350_RTC_ALMMTH_NOV 11 +#define WM8350_RTC_ALMMTH_DEC 12 +#define WM8350_RTC_ALMMTH_JAN_BCD 0x01 +#define WM8350_RTC_ALMMTH_FEB_BCD 0x02 +#define WM8350_RTC_ALMMTH_MAR_BCD 0x03 +#define WM8350_RTC_ALMMTH_APR_BCD 0x04 +#define WM8350_RTC_ALMMTH_MAY_BCD 0x05 +#define WM8350_RTC_ALMMTH_JUN_BCD 0x06 +#define WM8350_RTC_ALMMTH_JUL_BCD 0x07 +#define WM8350_RTC_ALMMTH_AUG_BCD 0x08 +#define WM8350_RTC_ALMMTH_SEP_BCD 0x09 +#define WM8350_RTC_ALMMTH_OCT_BCD 0x10 +#define WM8350_RTC_ALMMTH_NOV_BCD 0x11 +#define WM8350_RTC_ALMMTH_DEC_BCD 0x12 + +/* + * R23 (0x17) - RTC Time Control + */ +#define WM8350_RTC_BCD 0x8000 +#define WM8350_RTC_BCD_MASK 0x8000 +#define WM8350_RTC_BCD_SHIFT 15 +#define WM8350_RTC_12HR 0x4000 +#define WM8350_RTC_12HR_MASK 0x4000 +#define WM8350_RTC_12HR_SHIFT 14 +#define WM8350_RTC_DST 0x2000 +#define WM8350_RTC_DST_MASK 0x2000 +#define WM8350_RTC_DST_SHIFT 13 +#define WM8350_RTC_SET 0x0800 +#define WM8350_RTC_SET_MASK 0x0800 +#define WM8350_RTC_SET_SHIFT 11 +#define WM8350_RTC_STS 0x0400 +#define WM8350_RTC_STS_MASK 0x0400 +#define WM8350_RTC_STS_SHIFT 10 +#define WM8350_RTC_ALMSET 0x0200 +#define WM8350_RTC_ALMSET_MASK 0x0200 +#define WM8350_RTC_ALMSET_SHIFT 9 +#define WM8350_RTC_ALMSTS 0x0100 +#define WM8350_RTC_ALMSTS_MASK 0x0100 +#define WM8350_RTC_ALMSTS_SHIFT 8 +#define WM8350_RTC_PINT 0x0070 +#define WM8350_RTC_PINT_MASK 0x0070 +#define WM8350_RTC_PINT_SHIFT 4 +#define WM8350_RTC_DSW 0x000F +#define WM8350_RTC_DSW_MASK 0x000F +#define WM8350_RTC_DSW_SHIFT 0 + +/* Bit values for R23 (0x17) */ +#define WM8350_RTC_BCD_BINARY 0 +#define WM8350_RTC_BCD_BCD 1 + +#define WM8350_RTC_12HR_24HR 0 +#define WM8350_RTC_12HR_12HR 1 + +#define WM8350_RTC_DST_DISABLED 0 +#define WM8350_RTC_DST_ENABLED 1 + +#define WM8350_RTC_SET_RUN 0 +#define WM8350_RTC_SET_SET 1 + +#define WM8350_RTC_STS_RUNNING 0 +#define WM8350_RTC_STS_STOPPED 1 + +#define WM8350_RTC_ALMSET_RUN 0 +#define WM8350_RTC_ALMSET_SET 1 + +#define WM8350_RTC_ALMSTS_RUNNING 0 +#define WM8350_RTC_ALMSTS_STOPPED 1 + +#define WM8350_RTC_PINT_DISABLED 0 +#define WM8350_RTC_PINT_SECS 1 +#define WM8350_RTC_PINT_MINS 2 +#define WM8350_RTC_PINT_HRS 3 +#define WM8350_RTC_PINT_DAYS 4 +#define WM8350_RTC_PINT_MTHS 5 + +#define WM8350_RTC_DSW_DISABLED 0 +#define WM8350_RTC_DSW_1HZ 1 +#define WM8350_RTC_DSW_2HZ 2 +#define WM8350_RTC_DSW_4HZ 3 +#define WM8350_RTC_DSW_8HZ 4 +#define WM8350_RTC_DSW_16HZ 5 +#define WM8350_RTC_DSW_32HZ 6 +#define WM8350_RTC_DSW_64HZ 7 +#define WM8350_RTC_DSW_128HZ 8 +#define WM8350_RTC_DSW_256HZ 9 +#define WM8350_RTC_DSW_512HZ 10 +#define WM8350_RTC_DSW_1024HZ 11 + +/* + * R218 (0xDA) - RTC Tick Control + */ +#define WM8350_RTC_TICKSTS 0x4000 +#define WM8350_RTC_CLKSRC 0x2000 +#define WM8350_RTC_TRIM_MASK 0x03FF + +/* + * RTC Interrupts. + */ +#define WM8350_IRQ_RTC_PER 7 +#define WM8350_IRQ_RTC_SEC 8 +#define WM8350_IRQ_RTC_ALM 9 + +#endif -- cgit v1.2.3 From 213f326810b6852bc54f38fd8bb71793f70f2c7a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:09 +0100 Subject: mfd: Add WM8350 watchdog register definitions Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- include/linux/mfd/wm8350/wdt.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/linux/mfd/wm8350/wdt.h (limited to 'include/linux') diff --git a/include/linux/mfd/wm8350/wdt.h b/include/linux/mfd/wm8350/wdt.h new file mode 100644 index 000000000000..72fc9f554569 --- /dev/null +++ b/include/linux/mfd/wm8350/wdt.h @@ -0,0 +1,22 @@ +/* + * wdt.h -- Watchdog Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + */ + +#ifndef __LINUX_MFD_WM8350_WDT_H_ +#define __LINUX_MFD_WM8350_WDT_H_ + +#define WM8350_WDOG_HIB_MODE 0x0080 +#define WM8350_WDOG_DEBUG 0x0040 +#define WM8350_WDOG_MODE_MASK 0x0030 +#define WM8350_WDOG_TO_MASK 0x0007 + +#define WM8350_IRQ_SYS_WDOG_TO 24 + +#endif -- cgit v1.2.3 From 89b4012befb1abca5e86d232bc0e2a797b0d9825 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:10 +0100 Subject: mfd: Core support for the WM8350 AudioPlus PMIC The WM8350 is an integrated audio and power management subsystem intended for use as the primary PMIC in mobile multimedia applications. The WM8350 can be controlled via either I2C or SPI - the control interface is provided by a separate module in order to allow greatest flexibility in configuring the kernel. This driver was originally written by Liam Girdwood and has since been updated to current kernel APIs and split up for submission by me. All the heavy lifting here was done by Liam. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/Kconfig | 19 + drivers/mfd/Makefile | 2 + drivers/mfd/wm8350-core.c | 456 +++++++++++++ drivers/mfd/wm8350-regmap.c | 1347 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8350/core.h | 585 +++++++++++++++++ 5 files changed, 2409 insertions(+) create mode 100644 drivers/mfd/wm8350-core.c create mode 100644 drivers/mfd/wm8350-regmap.c create mode 100644 include/linux/mfd/wm8350/core.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index a3ddf6581ea6..5d7bbf45cae0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -95,6 +95,25 @@ config MFD_WM8400 the device, additional drivers must be enabled in order to use the functionality of the device. +config MFD_WM8350 + tristate + +config MFD_WM8350_CONFIG_MODE_0 + bool + depends on MFD_WM8350 + +config MFD_WM8350_CONFIG_MODE_1 + bool + depends on MFD_WM8350 + +config MFD_WM8350_CONFIG_MODE_2 + bool + depends on MFD_WM8350 + +config MFD_WM8350_CONFIG_MODE_3 + bool + depends on MFD_WM8350 + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 1624c7d87a49..bec1e92d37ba 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o obj-$(CONFIG_MFD_WM8400) += wm8400-core.o +wm8350-objs := wm8350-core.o wm8350-regmap.o +obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_CORE) += mfd-core.o diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c new file mode 100644 index 000000000000..c7552c0b7797 --- /dev/null +++ b/drivers/mfd/wm8350-core.c @@ -0,0 +1,456 @@ +/* + * wm8350-core.c -- Device access for Wolfson WM8350 + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood, Mark Brown + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define WM8350_UNLOCK_KEY 0x0013 +#define WM8350_LOCK_KEY 0x0000 + +#define WM8350_CLOCK_CONTROL_1 0x28 +#define WM8350_AIF_TEST 0x74 + +/* debug */ +#define WM8350_BUS_DEBUG 0 +#if WM8350_BUS_DEBUG +#define dump(regs, src) do { \ + int i_; \ + u16 *src_ = src; \ + printk(KERN_DEBUG); \ + for (i_ = 0; i_ < regs; i_++) \ + printk(" 0x%4.4x", *src_++); \ + printk("\n"); \ +} while (0); +#else +#define dump(bytes, src) +#endif + +#define WM8350_LOCK_DEBUG 0 +#if WM8350_LOCK_DEBUG +#define ldbg(format, arg...) printk(format, ## arg) +#else +#define ldbg(format, arg...) +#endif + +/* + * WM8350 Device IO + */ +static DEFINE_MUTEX(io_mutex); +static DEFINE_MUTEX(reg_lock_mutex); +static DEFINE_MUTEX(auxadc_mutex); + +/* Perform a physical read from the device. + */ +static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, + u16 *dest) +{ + int i, ret; + int bytes = num_regs * 2; + + dev_dbg(wm8350->dev, "volatile read\n"); + ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest); + + for (i = reg; i < reg + num_regs; i++) { + /* Cache is CPU endian */ + dest[i - reg] = be16_to_cpu(dest[i - reg]); + + /* Satisfy non-volatile bits from cache */ + dest[i - reg] &= wm8350_reg_io_map[i].vol; + dest[i - reg] |= wm8350->reg_cache[i]; + + /* Mask out non-readable bits */ + dest[i - reg] &= wm8350_reg_io_map[i].readable; + } + + dump(num_regs, dest); + + return ret; +} + +static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) +{ + int i; + int end = reg + num_regs; + int ret = 0; + int bytes = num_regs * 2; + + if (wm8350->read_dev == NULL) + return -ENODEV; + + if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { + dev_err(wm8350->dev, "invalid reg %x\n", + reg + num_regs - 1); + return -EINVAL; + } + + dev_dbg(wm8350->dev, + "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs); + +#if WM8350_BUS_DEBUG + /* we can _safely_ read any register, but warn if read not supported */ + for (i = reg; i < end; i++) { + if (!wm8350_reg_io_map[i].readable) + dev_warn(wm8350->dev, + "reg R%d is not readable\n", i); + } +#endif + + /* if any volatile registers are required, then read back all */ + for (i = reg; i < end; i++) + if (wm8350_reg_io_map[i].vol) + return wm8350_phys_read(wm8350, reg, num_regs, dest); + + /* no volatiles, then cache is good */ + dev_dbg(wm8350->dev, "cache read\n"); + memcpy(dest, &wm8350->reg_cache[reg], bytes); + dump(num_regs, dest); + return ret; +} + +static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg) +{ + if (reg == WM8350_SECURITY || + wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY) + return 0; + + if ((reg == WM8350_GPIO_CONFIGURATION_I_O) || + (reg >= WM8350_GPIO_FUNCTION_SELECT_1 && + reg <= WM8350_GPIO_FUNCTION_SELECT_4) || + (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && + reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) + return 1; + return 0; +} + +static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) +{ + int i; + int end = reg + num_regs; + int bytes = num_regs * 2; + + if (wm8350->write_dev == NULL) + return -ENODEV; + + if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { + dev_err(wm8350->dev, "invalid reg %x\n", + reg + num_regs - 1); + return -EINVAL; + } + + /* it's generally not a good idea to write to RO or locked registers */ + for (i = reg; i < end; i++) { + if (!wm8350_reg_io_map[i].writable) { + dev_err(wm8350->dev, + "attempted write to read only reg R%d\n", i); + return -EINVAL; + } + + if (is_reg_locked(wm8350, i)) { + dev_err(wm8350->dev, + "attempted write to locked reg R%d\n", i); + return -EINVAL; + } + + src[i - reg] &= wm8350_reg_io_map[i].writable; + + wm8350->reg_cache[i] = + (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable) + | src[i - reg]; + + src[i - reg] = cpu_to_be16(src[i - reg]); + } + + /* Actually write it out */ + return wm8350->write_dev(wm8350, reg, bytes, (char *)src); +} + +/* + * Safe read, modify, write methods + */ +int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) +{ + u16 data; + int err; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, reg, 1, &data); + if (err) { + dev_err(wm8350->dev, "read from reg R%d failed\n", reg); + goto out; + } + + data &= ~mask; + err = wm8350_write(wm8350, reg, 1, &data); + if (err) + dev_err(wm8350->dev, "write to reg R%d failed\n", reg); +out: + mutex_unlock(&io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(wm8350_clear_bits); + +int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) +{ + u16 data; + int err; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, reg, 1, &data); + if (err) { + dev_err(wm8350->dev, "read from reg R%d failed\n", reg); + goto out; + } + + data |= mask; + err = wm8350_write(wm8350, reg, 1, &data); + if (err) + dev_err(wm8350->dev, "write to reg R%d failed\n", reg); +out: + mutex_unlock(&io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(wm8350_set_bits); + +u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) +{ + u16 data; + int err; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, reg, 1, &data); + if (err) + dev_err(wm8350->dev, "read from reg R%d failed\n", reg); + + mutex_unlock(&io_mutex); + return data; +} +EXPORT_SYMBOL_GPL(wm8350_reg_read); + +int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) +{ + int ret; + u16 data = val; + + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, reg, 1, &data); + if (ret) + dev_err(wm8350->dev, "write to reg R%d failed\n", reg); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_reg_write); + +int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, + u16 *dest) +{ + int err = 0; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, start_reg, regs, dest); + if (err) + dev_err(wm8350->dev, "block read starting from R%d failed\n", + start_reg); + mutex_unlock(&io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(wm8350_block_read); + +int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, + u16 *src) +{ + int ret = 0; + + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, start_reg, regs, src); + if (ret) + dev_err(wm8350->dev, "block write starting at R%d failed\n", + start_reg); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_block_write); + +int wm8350_reg_lock(struct wm8350 *wm8350) +{ + u16 key = WM8350_LOCK_KEY; + int ret; + + ldbg(__func__); + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + if (ret) + dev_err(wm8350->dev, "lock failed\n"); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_reg_lock); + +int wm8350_reg_unlock(struct wm8350 *wm8350) +{ + u16 key = WM8350_UNLOCK_KEY; + int ret; + + ldbg(__func__); + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + if (ret) + dev_err(wm8350->dev, "unlock failed\n"); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_reg_unlock); + +/* + * Cache is always host endian. + */ +static int wm8350_create_cache(struct wm8350 *wm8350, int mode) +{ + int i, ret = 0; + u16 value; + const u16 *reg_map; + + switch (mode) { +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 + case 0: + reg_map = wm8350_mode0_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 + case 1: + reg_map = wm8350_mode1_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 + case 2: + reg_map = wm8350_mode2_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 + case 3: + reg_map = wm8350_mode3_defaults; + break; +#endif + default: + dev_err(wm8350->dev, "Configuration mode %d not supported\n", + mode); + return -EINVAL; + } + + wm8350->reg_cache = + kzalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL); + if (wm8350->reg_cache == NULL) + return -ENOMEM; + + /* Read the initial cache state back from the device - this is + * a PMIC so the device many not be in a virgin state and we + * can't rely on the silicon values. + */ + for (i = 0; i < WM8350_MAX_REGISTER; i++) { + /* audio register range */ + if (wm8350_reg_io_map[i].readable && + (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) { + ret = wm8350->read_dev(wm8350, i, 2, (char *)&value); + if (ret < 0) { + dev_err(wm8350->dev, + "failed to read initial cache value\n"); + goto out; + } + value = be16_to_cpu(value); + value &= wm8350_reg_io_map[i].readable; + wm8350->reg_cache[i] = value; + } else + wm8350->reg_cache[i] = reg_map[i]; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_create_cache); + +int wm8350_device_init(struct wm8350 *wm8350) +{ + int ret = -EINVAL; + u16 id1, id2, mask, mode; + + /* get WM8350 revision and config mode */ + wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); + wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + + id1 = be16_to_cpu(id1); + id2 = be16_to_cpu(id2); + + if (id1 == 0x0) + dev_info(wm8350->dev, "Found Rev C device\n"); + else if (id1 == 0x6143) { + switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) { + case WM8350_REV_E: + dev_info(wm8350->dev, "Found Rev E device\n"); + wm8350->rev = WM8350_REV_E; + break; + case WM8350_REV_F: + dev_info(wm8350->dev, "Found Rev F device\n"); + wm8350->rev = WM8350_REV_F; + break; + case WM8350_REV_G: + dev_info(wm8350->dev, "Found Rev G device\n"); + wm8350->rev = WM8350_REV_G; + break; + default: + /* For safety we refuse to run on unknown hardware */ + dev_info(wm8350->dev, "Found unknown rev\n"); + ret = -ENODEV; + goto err; + } + } else { + dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n", + id1); + ret = -ENODEV; + goto err; + } + + mode = id2 & WM8350_CONF_STS_MASK >> 10; + mask = id2 & WM8350_CUST_ID_MASK; + dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask); + + ret = wm8350_create_cache(wm8350, mode); + if (ret < 0) { + printk(KERN_ERR "wm8350: failed to create register cache\n"); + return ret; + } + + return 0; + +err: + kfree(wm8350->reg_cache); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_device_init); + +void wm8350_device_exit(struct wm8350 *wm8350) +{ + kfree(wm8350->reg_cache); +} +EXPORT_SYMBOL_GPL(wm8350_device_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c new file mode 100644 index 000000000000..974678db22cd --- /dev/null +++ b/drivers/mfd/wm8350-regmap.c @@ -0,0 +1,1347 @@ +/* + * wm8350-regmap.c -- Wolfson Microelectronics WM8350 register map + * + * This file splits out the tables describing the defaults and access + * status of the WM8350 registers since they are rather large. + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * 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. + */ + +#include + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode0_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1002, /* R3 - System Control 1 */ + 0x0004, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0FFC, /* R134 - GPIO Configuration (i/o) */ + 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0013, /* R140 - GPIO Function Select 1 */ + 0x0000, /* R141 - GPIO Function Select 2 */ + 0x0000, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0000, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0000, /* R186 - DCDC3 Control */ + 0x0000, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0000, /* R189 - DCDC4 Control */ + 0x0000, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0000, /* R195 - DCDC6 Control */ + 0x0000, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001B, /* R203 - LDO2 Control */ + 0x0000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001B, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001B, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode1_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1002, /* R3 - System Control 1 */ + 0x0014, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x00FB, /* R134 - GPIO Configuration (i/o) */ + 0x04FE, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0312, /* R140 - GPIO Function Select 1 */ + 0x1003, /* R141 - GPIO Function Select 2 */ + 0x1331, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x0062, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0026, /* R186 - DCDC3 Control */ + 0x0400, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0062, /* R189 - DCDC4 Control */ + 0x0400, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0800, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x0006, /* R200 - LDO1 Control */ + 0x0400, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0006, /* R203 - LDO2 Control */ + 0x0400, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001B, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001B, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode2_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1002, /* R3 - System Control 1 */ + 0x0014, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x08FB, /* R134 - GPIO Configuration (i/o) */ + 0x0CFE, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0312, /* R140 - GPIO Function Select 1 */ + 0x0003, /* R141 - GPIO Function Select 2 */ + 0x2331, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x002E, /* R186 - DCDC3 Control */ + 0x0800, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x000E, /* R189 - DCDC4 Control */ + 0x0800, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0C00, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001A, /* R200 - LDO1 Control */ + 0x0800, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0010, /* R203 - LDO2 Control */ + 0x0800, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x000A, /* R206 - LDO3 Control */ + 0x0C00, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001A, /* R209 - LDO4 Control */ + 0x0800, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode3_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1000, /* R3 - System Control 1 */ + 0x0004, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0A7B, /* R134 - GPIO Configuration (i/o) */ + 0x06FE, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x1312, /* R140 - GPIO Function Select 1 */ + 0x1030, /* R141 - GPIO Function Select 2 */ + 0x2231, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x000E, /* R186 - DCDC3 Control */ + 0x0400, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0026, /* R189 - DCDC4 Control */ + 0x0400, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0400, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001C, /* R203 - LDO2 Control */ + 0x0400, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001C, /* R206 - LDO3 Control */ + 0x0400, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001F, /* R209 - LDO4 Control */ + 0x0400, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +/* The register defaults for the config mode used must be compiled in but + * due to the impact on kernel size it is possible to disable + */ +#ifndef WM8350_HAVE_CONFIG_MODE +#warning No WM8350 config modes supported - select at least one of the +#warning MFD_WM8350_CONFIG_MODE_n options from the board driver. +#endif + +/* + * Access masks. + */ + +const struct wm8350_reg_access wm8350_reg_io_map[] = { + /* read write volatile */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0 - Reset/ID */ + { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ + { 0x0000, 0x0000, 0x0000 }, /* R2 */ + { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ + { 0xFCF7, 0xFCF7, 0xF800 }, /* R4 - System Control 2 */ + { 0x80FF, 0x80FF, 0x8000 }, /* R5 - System Hibernate */ + { 0xFB0E, 0xFB0E, 0x0000 }, /* R6 - Interface Control */ + { 0x0000, 0x0000, 0x0000 }, /* R7 */ + { 0xE537, 0xE537, 0xFFFF }, /* R8 - Power mgmt (1) */ + { 0x0FF3, 0x0FF3, 0xFFFF }, /* R9 - Power mgmt (2) */ + { 0x008F, 0x008F, 0xFFFF }, /* R10 - Power mgmt (3) */ + { 0x6D3C, 0x6D3C, 0xFFFF }, /* R11 - Power mgmt (4) */ + { 0x1F8F, 0x1F8F, 0xFFFF }, /* R12 - Power mgmt (5) */ + { 0x8F3F, 0x8F3F, 0xFFFF }, /* R13 - Power mgmt (6) */ + { 0x0003, 0x0003, 0xFFFF }, /* R14 - Power mgmt (7) */ + { 0x0000, 0x0000, 0x0000 }, /* R15 */ + { 0x7F7F, 0x7F7F, 0xFFFF }, /* R16 - RTC Seconds/Minutes */ + { 0x073F, 0x073F, 0xFFFF }, /* R17 - RTC Hours/Day */ + { 0x1F3F, 0x1F3F, 0xFFFF }, /* R18 - RTC Date/Month */ + { 0x3FFF, 0x00FF, 0xFFFF }, /* R19 - RTC Year */ + { 0x7F7F, 0x7F7F, 0x0000 }, /* R20 - Alarm Seconds/Minutes */ + { 0x0F3F, 0x0F3F, 0x0000 }, /* R21 - Alarm Hours/Day */ + { 0x1F3F, 0x1F3F, 0x0000 }, /* R22 - Alarm Date/Month */ + { 0xEF7F, 0xEA7F, 0xFFFF }, /* R23 - RTC Time Control */ + { 0x3BFF, 0x0000, 0xFFFF }, /* R24 - System Interrupts */ + { 0xFEE7, 0x0000, 0xFFFF }, /* R25 - Interrupt Status 1 */ + { 0x35FF, 0x0000, 0xFFFF }, /* R26 - Interrupt Status 2 */ + { 0x0F3F, 0x0000, 0xFFFF }, /* R27 - Power Up Interrupt Status */ + { 0x0F3F, 0x0000, 0xFFFF }, /* R28 - Under Voltage Interrupt status */ + { 0x8000, 0x0000, 0xFFFF }, /* R29 - Over Current Interrupt status */ + { 0x1FFF, 0x0000, 0xFFFF }, /* R30 - GPIO Interrupt Status */ + { 0xEF7F, 0x0000, 0xFFFF }, /* R31 - Comparator Interrupt Status */ + { 0x3FFF, 0x3FFF, 0x0000 }, /* R32 - System Interrupts Mask */ + { 0xFEE7, 0xFEE7, 0x0000 }, /* R33 - Interrupt Status 1 Mask */ + { 0xF5FF, 0xF5FF, 0x0000 }, /* R34 - Interrupt Status 2 Mask */ + { 0x0F3F, 0x0F3F, 0x0000 }, /* R35 - Power Up Interrupt Status Mask */ + { 0x0F3F, 0x0F3F, 0x0000 }, /* R36 - Under Voltage Int status Mask */ + { 0x8000, 0x8000, 0x0000 }, /* R37 - Over Current Int status Mask */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R38 - GPIO Interrupt Status Mask */ + { 0xEF7F, 0xEF7F, 0x0000 }, /* R39 - Comparator IntStatus Mask */ + { 0xC9F7, 0xC9F7, 0xFFFF }, /* R40 - Clock Control 1 */ + { 0x8001, 0x8001, 0x0000 }, /* R41 - Clock Control 2 */ + { 0xFFF7, 0xFFF7, 0xFFFF }, /* R42 - FLL Control 1 */ + { 0xFBFF, 0xFBFF, 0x0000 }, /* R43 - FLL Control 2 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R44 - FLL Control 3 */ + { 0x0033, 0x0033, 0x0000 }, /* R45 - FLL Control 4 */ + { 0x0000, 0x0000, 0x0000 }, /* R46 */ + { 0x0000, 0x0000, 0x0000 }, /* R47 */ + { 0x3033, 0x3033, 0x0000 }, /* R48 - DAC Control */ + { 0x0000, 0x0000, 0x0000 }, /* R49 */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R50 - DAC Digital Volume L */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R51 - DAC Digital Volume R */ + { 0x0000, 0x0000, 0x0000 }, /* R52 */ + { 0x0FFF, 0x0FFF, 0xFFFF }, /* R53 - DAC LR Rate */ + { 0x0017, 0x0017, 0x0000 }, /* R54 - DAC Clock Control */ + { 0x0000, 0x0000, 0x0000 }, /* R55 */ + { 0x0000, 0x0000, 0x0000 }, /* R56 */ + { 0x0000, 0x0000, 0x0000 }, /* R57 */ + { 0x4000, 0x4000, 0x0000 }, /* R58 - DAC Mute */ + { 0x7000, 0x7000, 0x0000 }, /* R59 - DAC Mute Volume */ + { 0x3C00, 0x3C00, 0x0000 }, /* R60 - DAC Side */ + { 0x0000, 0x0000, 0x0000 }, /* R61 */ + { 0x0000, 0x0000, 0x0000 }, /* R62 */ + { 0x0000, 0x0000, 0x0000 }, /* R63 */ + { 0x8303, 0x8303, 0xFFFF }, /* R64 - ADC Control */ + { 0x0000, 0x0000, 0x0000 }, /* R65 */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R66 - ADC Digital Volume L */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R67 - ADC Digital Volume R */ + { 0x0FFF, 0x0FFF, 0x0000 }, /* R68 - ADC Divider */ + { 0x0000, 0x0000, 0x0000 }, /* R69 */ + { 0x0FFF, 0x0FFF, 0xFFFF }, /* R70 - ADC LR Rate */ + { 0x0000, 0x0000, 0x0000 }, /* R71 */ + { 0x0707, 0x0707, 0xFFFF }, /* R72 - Input Control */ + { 0xC0C0, 0xC0C0, 0xFFFF }, /* R73 - IN3 Input Control */ + { 0xC09F, 0xC09F, 0xFFFF }, /* R74 - Mic Bias Control */ + { 0x0000, 0x0000, 0x0000 }, /* R75 */ + { 0x0F15, 0x0F15, 0xFFFF }, /* R76 - Output Control */ + { 0xC000, 0xC000, 0xFFFF }, /* R77 - Jack Detect */ + { 0x03FF, 0x03FF, 0x0000 }, /* R78 - Anti Pop Control */ + { 0x0000, 0x0000, 0x0000 }, /* R79 */ + { 0xE1FC, 0xE1FC, 0x8000 }, /* R80 - Left Input Volume */ + { 0xE1FC, 0xE1FC, 0x8000 }, /* R81 - Right Input Volume */ + { 0x0000, 0x0000, 0x0000 }, /* R82 */ + { 0x0000, 0x0000, 0x0000 }, /* R83 */ + { 0x0000, 0x0000, 0x0000 }, /* R84 */ + { 0x0000, 0x0000, 0x0000 }, /* R85 */ + { 0x0000, 0x0000, 0x0000 }, /* R86 */ + { 0x0000, 0x0000, 0x0000 }, /* R87 */ + { 0x9807, 0x9807, 0xFFFF }, /* R88 - Left Mixer Control */ + { 0x980B, 0x980B, 0xFFFF }, /* R89 - Right Mixer Control */ + { 0x0000, 0x0000, 0x0000 }, /* R90 */ + { 0x0000, 0x0000, 0x0000 }, /* R91 */ + { 0x8909, 0x8909, 0xFFFF }, /* R92 - OUT3 Mixer Control */ + { 0x9E07, 0x9E07, 0xFFFF }, /* R93 - OUT4 Mixer Control */ + { 0x0000, 0x0000, 0x0000 }, /* R94 */ + { 0x0000, 0x0000, 0x0000 }, /* R95 */ + { 0x0EEE, 0x0EEE, 0x0000 }, /* R96 - Output Left Mixer Volume */ + { 0xE0EE, 0xE0EE, 0x0000 }, /* R97 - Output Right Mixer Volume */ + { 0x0E0F, 0x0E0F, 0x0000 }, /* R98 - Input Mixer Volume L */ + { 0xE0E1, 0xE0E1, 0x0000 }, /* R99 - Input Mixer Volume R */ + { 0x800E, 0x800E, 0x0000 }, /* R100 - Input Mixer Volume */ + { 0x0000, 0x0000, 0x0000 }, /* R101 */ + { 0x0000, 0x0000, 0x0000 }, /* R102 */ + { 0x0000, 0x0000, 0x0000 }, /* R103 */ + { 0xE1FC, 0xE1FC, 0xFFFF }, /* R104 - LOUT1 Volume */ + { 0xE1FC, 0xE1FC, 0xFFFF }, /* R105 - ROUT1 Volume */ + { 0xE1FC, 0xE1FC, 0xFFFF }, /* R106 - LOUT2 Volume */ + { 0xE7FC, 0xE7FC, 0xFFFF }, /* R107 - ROUT2 Volume */ + { 0x0000, 0x0000, 0x0000 }, /* R108 */ + { 0x0000, 0x0000, 0x0000 }, /* R109 */ + { 0x0000, 0x0000, 0x0000 }, /* R110 */ + { 0x80E0, 0x80E0, 0xFFFF }, /* R111 - BEEP Volume */ + { 0xBF00, 0xBF00, 0x0000 }, /* R112 - AI Formating */ + { 0x00F1, 0x00F1, 0x0000 }, /* R113 - ADC DAC COMP */ + { 0x00F8, 0x00F8, 0x0000 }, /* R114 - AI ADC Control */ + { 0x40FB, 0x40FB, 0x0000 }, /* R115 - AI DAC Control */ + { 0x7C30, 0x7C30, 0x0000 }, /* R116 - AIF Test */ + { 0x0000, 0x0000, 0x0000 }, /* R117 */ + { 0x0000, 0x0000, 0x0000 }, /* R118 */ + { 0x0000, 0x0000, 0x0000 }, /* R119 */ + { 0x0000, 0x0000, 0x0000 }, /* R120 */ + { 0x0000, 0x0000, 0x0000 }, /* R121 */ + { 0x0000, 0x0000, 0x0000 }, /* R122 */ + { 0x0000, 0x0000, 0x0000 }, /* R123 */ + { 0x0000, 0x0000, 0x0000 }, /* R124 */ + { 0x0000, 0x0000, 0x0000 }, /* R125 */ + { 0x0000, 0x0000, 0x0000 }, /* R126 */ + { 0x0000, 0x0000, 0x0000 }, /* R127 */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R128 - GPIO Debounce */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R129 - GPIO Pin pull up Control */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R130 - GPIO Pull down Control */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R131 - GPIO Interrupt Mode */ + { 0x0000, 0x0000, 0x0000 }, /* R132 */ + { 0x00C0, 0x00C0, 0x0000 }, /* R133 - GPIO Control */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R134 - GPIO Configuration (i/o) */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R135 - GPIO Pin Polarity / Type */ + { 0x0000, 0x0000, 0x0000 }, /* R136 */ + { 0x0000, 0x0000, 0x0000 }, /* R137 */ + { 0x0000, 0x0000, 0x0000 }, /* R138 */ + { 0x0000, 0x0000, 0x0000 }, /* R139 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R140 - GPIO Function Select 1 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R141 - GPIO Function Select 2 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R142 - GPIO Function Select 3 */ + { 0x000F, 0x000F, 0x0000 }, /* R143 - GPIO Function Select 4 */ + { 0xF0FF, 0xF0FF, 0xA000 }, /* R144 - Digitiser Control (1) */ + { 0x3707, 0x3707, 0x0000 }, /* R145 - Digitiser Control (2) */ + { 0x0000, 0x0000, 0x0000 }, /* R146 */ + { 0x0000, 0x0000, 0x0000 }, /* R147 */ + { 0x0000, 0x0000, 0x0000 }, /* R148 */ + { 0x0000, 0x0000, 0x0000 }, /* R149 */ + { 0x0000, 0x0000, 0x0000 }, /* R150 */ + { 0x0000, 0x0000, 0x0000 }, /* R151 */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R152 - AUX1 Readback */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R153 - AUX2 Readback */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R154 - AUX3 Readback */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R155 - AUX4 Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R156 - USB Voltage Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R157 - LINE Voltage Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R158 - BATT Voltage Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R159 - Chip Temp Readback */ + { 0x0000, 0x0000, 0x0000 }, /* R160 */ + { 0x0000, 0x0000, 0x0000 }, /* R161 */ + { 0x0000, 0x0000, 0x0000 }, /* R162 */ + { 0x000F, 0x000F, 0x0000 }, /* R163 - Generic Comparator Control */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R164 - Generic comparator 1 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R165 - Generic comparator 2 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R166 - Generic comparator 3 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R167 - Generic comparator 4 */ + { 0xBFFF, 0xBFFF, 0x8000 }, /* R168 - Battery Charger Control 1 */ + { 0xFFFF, 0x4FFF, 0xB000 }, /* R169 - Battery Charger Control 2 */ + { 0x007F, 0x007F, 0x0000 }, /* R170 - Battery Charger Control 3 */ + { 0x0000, 0x0000, 0x0000 }, /* R171 */ + { 0x903F, 0x903F, 0xFFFF }, /* R172 - Current Sink Driver A */ + { 0xE333, 0xE333, 0xFFFF }, /* R173 - CSA Flash control */ + { 0x903F, 0x903F, 0xFFFF }, /* R174 - Current Sink Driver B */ + { 0xE333, 0xE333, 0xFFFF }, /* R175 - CSB Flash control */ + { 0x8F3F, 0x8F3F, 0xFFFF }, /* R176 - DCDC/LDO requested */ + { 0x332D, 0x332D, 0x0000 }, /* R177 - DCDC Active options */ + { 0x002D, 0x002D, 0x0000 }, /* R178 - DCDC Sleep options */ + { 0x5177, 0x5177, 0x8000 }, /* R179 - Power-check comparator */ + { 0x047F, 0x047F, 0x0000 }, /* R180 - DCDC1 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R181 - DCDC1 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R182 - DCDC1 Low Power */ + { 0x535B, 0x535B, 0x0000 }, /* R183 - DCDC2 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R184 - DCDC2 Timeouts */ + { 0x0000, 0x0000, 0x0000 }, /* R185 */ + { 0x047F, 0x047F, 0x0000 }, /* R186 - DCDC3 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R187 - DCDC3 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R188 - DCDC3 Low Power */ + { 0x047F, 0x047F, 0x0000 }, /* R189 - DCDC4 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R190 - DCDC4 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R191 - DCDC4 Low Power */ + { 0x535B, 0x535B, 0x0000 }, /* R192 - DCDC5 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R193 - DCDC5 Timeouts */ + { 0x0000, 0x0000, 0x0000 }, /* R194 */ + { 0x047F, 0x047F, 0x0000 }, /* R195 - DCDC6 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R196 - DCDC6 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R197 - DCDC6 Low Power */ + { 0x0000, 0x0000, 0x0000 }, /* R198 */ + { 0xFFD3, 0xFFD3, 0x0000 }, /* R199 - Limit Switch Control */ + { 0x441F, 0x441F, 0x0000 }, /* R200 - LDO1 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R201 - LDO1 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R202 - LDO1 Low Power */ + { 0x441F, 0x441F, 0x0000 }, /* R203 - LDO2 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R204 - LDO2 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R205 - LDO2 Low Power */ + { 0x441F, 0x441F, 0x0000 }, /* R206 - LDO3 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R207 - LDO3 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R208 - LDO3 Low Power */ + { 0x441F, 0x441F, 0x0000 }, /* R209 - LDO4 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R210 - LDO4 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R211 - LDO4 Low Power */ + { 0x0000, 0x0000, 0x0000 }, /* R212 */ + { 0x0000, 0x0000, 0x0000 }, /* R213 */ + { 0x0000, 0x0000, 0x0000 }, /* R214 */ + { 0x8F3F, 0x8F3F, 0x0000 }, /* R215 - VCC_FAULT Masks */ + { 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */ + { 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */ + { 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R219 */ + { 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */ + { 0x0000, 0x0000, 0x0000 }, /* R221 */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */ + { 0x0000, 0x0000, 0x0000 }, /* R224 */ + { 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */ + { 0x0000, 0x0000, 0x0000 }, /* R226 */ + { 0x0000, 0x0000, 0xFFFF }, /* R227 */ + { 0x0000, 0x0000, 0x0000 }, /* R228 */ + { 0x0000, 0x0000, 0x0000 }, /* R229 */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R230 - GPIO Pin Status */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R231 */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R232 */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R233 */ + { 0x0000, 0x0000, 0x0000 }, /* R234 */ + { 0x0000, 0x0000, 0x0000 }, /* R235 */ + { 0x0000, 0x0000, 0x0000 }, /* R236 */ + { 0x0000, 0x0000, 0x0000 }, /* R237 */ + { 0x0000, 0x0000, 0x0000 }, /* R238 */ + { 0x0000, 0x0000, 0x0000 }, /* R239 */ + { 0x0000, 0x0000, 0x0000 }, /* R240 */ + { 0x0000, 0x0000, 0x0000 }, /* R241 */ + { 0x0000, 0x0000, 0x0000 }, /* R242 */ + { 0x0000, 0x0000, 0x0000 }, /* R243 */ + { 0x0000, 0x0000, 0x0000 }, /* R244 */ + { 0x0000, 0x0000, 0x0000 }, /* R245 */ + { 0x0000, 0x0000, 0x0000 }, /* R246 */ + { 0x0000, 0x0000, 0x0000 }, /* R247 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R248 */ + { 0x0000, 0x0000, 0x0000 }, /* R249 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R250 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R251 */ + { 0x0000, 0x0000, 0x0000 }, /* R252 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R253 */ + { 0x0000, 0x0000, 0x0000 }, /* R254 */ + { 0x0000, 0x0000, 0x0000 }, /* R255 */ +}; diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h new file mode 100644 index 000000000000..94778c1669dc --- /dev/null +++ b/include/linux/mfd/wm8350/core.h @@ -0,0 +1,585 @@ +/* + * core.h -- Core Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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. + * + */ + +#ifndef __LINUX_MFD_WM8350_CORE_H_ +#define __LINUX_MFD_WM8350_CORE_H_ + +#include +#include +#include + +/* + * Register values. + */ +#define WM8350_RESET_ID 0x00 +#define WM8350_ID 0x01 +#define WM8350_SYSTEM_CONTROL_1 0x03 +#define WM8350_SYSTEM_CONTROL_2 0x04 +#define WM8350_SYSTEM_HIBERNATE 0x05 +#define WM8350_INTERFACE_CONTROL 0x06 +#define WM8350_POWER_MGMT_1 0x08 +#define WM8350_POWER_MGMT_2 0x09 +#define WM8350_POWER_MGMT_3 0x0A +#define WM8350_POWER_MGMT_4 0x0B +#define WM8350_POWER_MGMT_5 0x0C +#define WM8350_POWER_MGMT_6 0x0D +#define WM8350_POWER_MGMT_7 0x0E + +#define WM8350_SYSTEM_INTERRUPTS 0x18 +#define WM8350_INT_STATUS_1 0x19 +#define WM8350_INT_STATUS_2 0x1A +#define WM8350_POWER_UP_INT_STATUS 0x1B +#define WM8350_UNDER_VOLTAGE_INT_STATUS 0x1C +#define WM8350_OVER_CURRENT_INT_STATUS 0x1D +#define WM8350_GPIO_INT_STATUS 0x1E +#define WM8350_COMPARATOR_INT_STATUS 0x1F +#define WM8350_SYSTEM_INTERRUPTS_MASK 0x20 +#define WM8350_INT_STATUS_1_MASK 0x21 +#define WM8350_INT_STATUS_2_MASK 0x22 +#define WM8350_POWER_UP_INT_STATUS_MASK 0x23 +#define WM8350_UNDER_VOLTAGE_INT_STATUS_MASK 0x24 +#define WM8350_OVER_CURRENT_INT_STATUS_MASK 0x25 +#define WM8350_GPIO_INT_STATUS_MASK 0x26 +#define WM8350_COMPARATOR_INT_STATUS_MASK 0x27 + +#define WM8350_MAX_REGISTER 0xFF + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset/ID + */ +#define WM8350_SW_RESET_CHIP_ID_MASK 0xFFFF + +/* + * R1 (0x01) - ID + */ +#define WM8350_CHIP_REV_MASK 0x7000 +#define WM8350_CONF_STS_MASK 0x0C00 +#define WM8350_CUST_ID_MASK 0x00FF + +/* + * R3 (0x03) - System Control 1 + */ +#define WM8350_CHIP_ON 0x8000 +#define WM8350_POWERCYCLE 0x2000 +#define WM8350_VCC_FAULT_OV 0x1000 +#define WM8350_REG_RSTB_TIME_MASK 0x0C00 +#define WM8350_BG_SLEEP 0x0200 +#define WM8350_MEM_VALID 0x0020 +#define WM8350_CHIP_SET_UP 0x0010 +#define WM8350_ON_DEB_T 0x0008 +#define WM8350_ON_POL 0x0002 +#define WM8350_IRQ_POL 0x0001 + +/* + * R4 (0x04) - System Control 2 + */ +#define WM8350_USB_SUSPEND_8MA 0x8000 +#define WM8350_USB_SUSPEND 0x4000 +#define WM8350_USB_MSTR 0x2000 +#define WM8350_USB_MSTR_SRC 0x1000 +#define WM8350_USB_500MA 0x0800 +#define WM8350_USB_NOLIM 0x0400 + +/* + * R5 (0x05) - System Hibernate + */ +#define WM8350_HIBERNATE 0x8000 +#define WM8350_WDOG_HIB_MODE 0x0080 +#define WM8350_REG_HIB_STARTUP_SEQ 0x0040 +#define WM8350_REG_RESET_HIB_MODE 0x0020 +#define WM8350_RST_HIB_MODE 0x0010 +#define WM8350_IRQ_HIB_MODE 0x0008 +#define WM8350_MEMRST_HIB_MODE 0x0004 +#define WM8350_PCCOMP_HIB_MODE 0x0002 +#define WM8350_TEMPMON_HIB_MODE 0x0001 + +/* + * R6 (0x06) - Interface Control + */ +#define WM8350_USE_DEV_PINS 0x8000 +#define WM8350_USE_DEV_PINS_MASK 0x8000 +#define WM8350_USE_DEV_PINS_SHIFT 15 +#define WM8350_DEV_ADDR_MASK 0x6000 +#define WM8350_DEV_ADDR_SHIFT 13 +#define WM8350_CONFIG_DONE 0x1000 +#define WM8350_CONFIG_DONE_MASK 0x1000 +#define WM8350_CONFIG_DONE_SHIFT 12 +#define WM8350_RECONFIG_AT_ON 0x0800 +#define WM8350_RECONFIG_AT_ON_MASK 0x0800 +#define WM8350_RECONFIG_AT_ON_SHIFT 11 +#define WM8350_AUTOINC 0x0200 +#define WM8350_AUTOINC_MASK 0x0200 +#define WM8350_AUTOINC_SHIFT 9 +#define WM8350_ARA 0x0100 +#define WM8350_ARA_MASK 0x0100 +#define WM8350_ARA_SHIFT 8 +#define WM8350_SPI_CFG 0x0008 +#define WM8350_SPI_CFG_MASK 0x0008 +#define WM8350_SPI_CFG_SHIFT 3 +#define WM8350_SPI_4WIRE 0x0004 +#define WM8350_SPI_4WIRE_MASK 0x0004 +#define WM8350_SPI_4WIRE_SHIFT 2 +#define WM8350_SPI_3WIRE 0x0002 +#define WM8350_SPI_3WIRE_MASK 0x0002 +#define WM8350_SPI_3WIRE_SHIFT 1 + +/* Bit values for R06 (0x06) */ +#define WM8350_USE_DEV_PINS_PRIMARY 0 +#define WM8350_USE_DEV_PINS_DEV 1 + +#define WM8350_DEV_ADDR_34 0 +#define WM8350_DEV_ADDR_36 1 +#define WM8350_DEV_ADDR_3C 2 +#define WM8350_DEV_ADDR_3E 3 + +#define WM8350_CONFIG_DONE_OFF 0 +#define WM8350_CONFIG_DONE_DONE 1 + +#define WM8350_RECONFIG_AT_ON_OFF 0 +#define WM8350_RECONFIG_AT_ON_ON 1 + +#define WM8350_AUTOINC_OFF 0 +#define WM8350_AUTOINC_ON 1 + +#define WM8350_ARA_OFF 0 +#define WM8350_ARA_ON 1 + +#define WM8350_SPI_CFG_CMOS 0 +#define WM8350_SPI_CFG_OD 1 + +#define WM8350_SPI_4WIRE_3WIRE 0 +#define WM8350_SPI_4WIRE_4WIRE 1 + +#define WM8350_SPI_3WIRE_I2C 0 +#define WM8350_SPI_3WIRE_SPI 1 + +/* + * R8 (0x08) - Power mgmt (1) + */ +#define WM8350_CODEC_ISEL_MASK 0xC000 +#define WM8350_VBUFEN 0x2000 +#define WM8350_OUTPUT_DRAIN_EN 0x0400 +#define WM8350_MIC_DET_ENA 0x0100 +#define WM8350_BIASEN 0x0020 +#define WM8350_MICBEN 0x0010 +#define WM8350_VMIDEN 0x0004 +#define WM8350_VMID_MASK 0x0003 +#define WM8350_VMID_SHIFT 0 + +/* + * R9 (0x09) - Power mgmt (2) + */ +#define WM8350_IN3R_ENA 0x0800 +#define WM8350_IN3L_ENA 0x0400 +#define WM8350_INR_ENA 0x0200 +#define WM8350_INL_ENA 0x0100 +#define WM8350_MIXINR_ENA 0x0080 +#define WM8350_MIXINL_ENA 0x0040 +#define WM8350_OUT4_ENA 0x0020 +#define WM8350_OUT3_ENA 0x0010 +#define WM8350_MIXOUTR_ENA 0x0002 +#define WM8350_MIXOUTL_ENA 0x0001 + +/* + * R10 (0x0A) - Power mgmt (3) + */ +#define WM8350_IN3R_TO_OUT2R 0x0080 +#define WM8350_OUT2R_ENA 0x0008 +#define WM8350_OUT2L_ENA 0x0004 +#define WM8350_OUT1R_ENA 0x0002 +#define WM8350_OUT1L_ENA 0x0001 + +/* + * R11 (0x0B) - Power mgmt (4) + */ +#define WM8350_SYSCLK_ENA 0x4000 +#define WM8350_ADC_HPF_ENA 0x2000 +#define WM8350_FLL_ENA 0x0800 +#define WM8350_FLL_OSC_ENA 0x0400 +#define WM8350_TOCLK_ENA 0x0100 +#define WM8350_DACR_ENA 0x0020 +#define WM8350_DACL_ENA 0x0010 +#define WM8350_ADCR_ENA 0x0008 +#define WM8350_ADCL_ENA 0x0004 + +/* + * R12 (0x0C) - Power mgmt (5) + */ +#define WM8350_CODEC_ENA 0x1000 +#define WM8350_RTC_TICK_ENA 0x0800 +#define WM8350_OSC32K_ENA 0x0400 +#define WM8350_CHG_ENA 0x0200 +#define WM8350_ACC_DET_ENA 0x0100 +#define WM8350_AUXADC_ENA 0x0080 +#define WM8350_DCMP4_ENA 0x0008 +#define WM8350_DCMP3_ENA 0x0004 +#define WM8350_DCMP2_ENA 0x0002 +#define WM8350_DCMP1_ENA 0x0001 + +/* + * R13 (0x0D) - Power mgmt (6) + */ +#define WM8350_LS_ENA 0x8000 +#define WM8350_LDO4_ENA 0x0800 +#define WM8350_LDO3_ENA 0x0400 +#define WM8350_LDO2_ENA 0x0200 +#define WM8350_LDO1_ENA 0x0100 +#define WM8350_DC6_ENA 0x0020 +#define WM8350_DC5_ENA 0x0010 +#define WM8350_DC4_ENA 0x0008 +#define WM8350_DC3_ENA 0x0004 +#define WM8350_DC2_ENA 0x0002 +#define WM8350_DC1_ENA 0x0001 + +/* + * R14 (0x0E) - Power mgmt (7) + */ +#define WM8350_CS2_ENA 0x0002 +#define WM8350_CS1_ENA 0x0001 + +/* + * R24 (0x18) - System Interrupts + */ +#define WM8350_OC_INT 0x2000 +#define WM8350_UV_INT 0x1000 +#define WM8350_PUTO_INT 0x0800 +#define WM8350_CS_INT 0x0200 +#define WM8350_EXT_INT 0x0100 +#define WM8350_CODEC_INT 0x0080 +#define WM8350_GP_INT 0x0040 +#define WM8350_AUXADC_INT 0x0020 +#define WM8350_RTC_INT 0x0010 +#define WM8350_SYS_INT 0x0008 +#define WM8350_CHG_INT 0x0004 +#define WM8350_USB_INT 0x0002 +#define WM8350_WKUP_INT 0x0001 + +/* + * R25 (0x19) - Interrupt Status 1 + */ +#define WM8350_CHG_BAT_HOT_EINT 0x8000 +#define WM8350_CHG_BAT_COLD_EINT 0x4000 +#define WM8350_CHG_BAT_FAIL_EINT 0x2000 +#define WM8350_CHG_TO_EINT 0x1000 +#define WM8350_CHG_END_EINT 0x0800 +#define WM8350_CHG_START_EINT 0x0400 +#define WM8350_CHG_FAST_RDY_EINT 0x0200 +#define WM8350_RTC_PER_EINT 0x0080 +#define WM8350_RTC_SEC_EINT 0x0040 +#define WM8350_RTC_ALM_EINT 0x0020 +#define WM8350_CHG_VBATT_LT_3P9_EINT 0x0004 +#define WM8350_CHG_VBATT_LT_3P1_EINT 0x0002 +#define WM8350_CHG_VBATT_LT_2P85_EINT 0x0001 + +/* + * R26 (0x1A) - Interrupt Status 2 + */ +#define WM8350_CS1_EINT 0x2000 +#define WM8350_CS2_EINT 0x1000 +#define WM8350_USB_LIMIT_EINT 0x0400 +#define WM8350_AUXADC_DATARDY_EINT 0x0100 +#define WM8350_AUXADC_DCOMP4_EINT 0x0080 +#define WM8350_AUXADC_DCOMP3_EINT 0x0040 +#define WM8350_AUXADC_DCOMP2_EINT 0x0020 +#define WM8350_AUXADC_DCOMP1_EINT 0x0010 +#define WM8350_SYS_HYST_COMP_FAIL_EINT 0x0008 +#define WM8350_SYS_CHIP_GT115_EINT 0x0004 +#define WM8350_SYS_CHIP_GT140_EINT 0x0002 +#define WM8350_SYS_WDOG_TO_EINT 0x0001 + +/* + * R27 (0x1B) - Power Up Interrupt Status + */ +#define WM8350_PUTO_LDO4_EINT 0x0800 +#define WM8350_PUTO_LDO3_EINT 0x0400 +#define WM8350_PUTO_LDO2_EINT 0x0200 +#define WM8350_PUTO_LDO1_EINT 0x0100 +#define WM8350_PUTO_DC6_EINT 0x0020 +#define WM8350_PUTO_DC5_EINT 0x0010 +#define WM8350_PUTO_DC4_EINT 0x0008 +#define WM8350_PUTO_DC3_EINT 0x0004 +#define WM8350_PUTO_DC2_EINT 0x0002 +#define WM8350_PUTO_DC1_EINT 0x0001 + +/* + * R28 (0x1C) - Under Voltage Interrupt status + */ +#define WM8350_UV_LDO4_EINT 0x0800 +#define WM8350_UV_LDO3_EINT 0x0400 +#define WM8350_UV_LDO2_EINT 0x0200 +#define WM8350_UV_LDO1_EINT 0x0100 +#define WM8350_UV_DC6_EINT 0x0020 +#define WM8350_UV_DC5_EINT 0x0010 +#define WM8350_UV_DC4_EINT 0x0008 +#define WM8350_UV_DC3_EINT 0x0004 +#define WM8350_UV_DC2_EINT 0x0002 +#define WM8350_UV_DC1_EINT 0x0001 + +/* + * R29 (0x1D) - Over Current Interrupt status + */ +#define WM8350_OC_LS_EINT 0x8000 + +/* + * R30 (0x1E) - GPIO Interrupt Status + */ +#define WM8350_GP12_EINT 0x1000 +#define WM8350_GP11_EINT 0x0800 +#define WM8350_GP10_EINT 0x0400 +#define WM8350_GP9_EINT 0x0200 +#define WM8350_GP8_EINT 0x0100 +#define WM8350_GP7_EINT 0x0080 +#define WM8350_GP6_EINT 0x0040 +#define WM8350_GP5_EINT 0x0020 +#define WM8350_GP4_EINT 0x0010 +#define WM8350_GP3_EINT 0x0008 +#define WM8350_GP2_EINT 0x0004 +#define WM8350_GP1_EINT 0x0002 +#define WM8350_GP0_EINT 0x0001 + +/* + * R31 (0x1F) - Comparator Interrupt Status + */ +#define WM8350_EXT_USB_FB_EINT 0x8000 +#define WM8350_EXT_WALL_FB_EINT 0x4000 +#define WM8350_EXT_BAT_FB_EINT 0x2000 +#define WM8350_CODEC_JCK_DET_L_EINT 0x0800 +#define WM8350_CODEC_JCK_DET_R_EINT 0x0400 +#define WM8350_CODEC_MICSCD_EINT 0x0200 +#define WM8350_CODEC_MICD_EINT 0x0100 +#define WM8350_WKUP_OFF_STATE_EINT 0x0040 +#define WM8350_WKUP_HIB_STATE_EINT 0x0020 +#define WM8350_WKUP_CONV_FAULT_EINT 0x0010 +#define WM8350_WKUP_WDOG_RST_EINT 0x0008 +#define WM8350_WKUP_GP_PWR_ON_EINT 0x0004 +#define WM8350_WKUP_ONKEY_EINT 0x0002 +#define WM8350_WKUP_GP_WAKEUP_EINT 0x0001 + +/* + * R32 (0x20) - System Interrupts Mask + */ +#define WM8350_IM_OC_INT 0x2000 +#define WM8350_IM_UV_INT 0x1000 +#define WM8350_IM_PUTO_INT 0x0800 +#define WM8350_IM_SPARE_INT 0x0400 +#define WM8350_IM_CS_INT 0x0200 +#define WM8350_IM_EXT_INT 0x0100 +#define WM8350_IM_CODEC_INT 0x0080 +#define WM8350_IM_GP_INT 0x0040 +#define WM8350_IM_AUXADC_INT 0x0020 +#define WM8350_IM_RTC_INT 0x0010 +#define WM8350_IM_SYS_INT 0x0008 +#define WM8350_IM_CHG_INT 0x0004 +#define WM8350_IM_USB_INT 0x0002 +#define WM8350_IM_WKUP_INT 0x0001 + +/* + * R33 (0x21) - Interrupt Status 1 Mask + */ +#define WM8350_IM_CHG_BAT_HOT_EINT 0x8000 +#define WM8350_IM_CHG_BAT_COLD_EINT 0x4000 +#define WM8350_IM_CHG_BAT_FAIL_EINT 0x2000 +#define WM8350_IM_CHG_TO_EINT 0x1000 +#define WM8350_IM_CHG_END_EINT 0x0800 +#define WM8350_IM_CHG_START_EINT 0x0400 +#define WM8350_IM_CHG_FAST_RDY_EINT 0x0200 +#define WM8350_IM_RTC_PER_EINT 0x0080 +#define WM8350_IM_RTC_SEC_EINT 0x0040 +#define WM8350_IM_RTC_ALM_EINT 0x0020 +#define WM8350_IM_CHG_VBATT_LT_3P9_EINT 0x0004 +#define WM8350_IM_CHG_VBATT_LT_3P1_EINT 0x0002 +#define WM8350_IM_CHG_VBATT_LT_2P85_EINT 0x0001 + +/* + * R34 (0x22) - Interrupt Status 2 Mask + */ +#define WM8350_IM_SPARE2_EINT 0x8000 +#define WM8350_IM_SPARE1_EINT 0x4000 +#define WM8350_IM_CS1_EINT 0x2000 +#define WM8350_IM_CS2_EINT 0x1000 +#define WM8350_IM_USB_LIMIT_EINT 0x0400 +#define WM8350_IM_AUXADC_DATARDY_EINT 0x0100 +#define WM8350_IM_AUXADC_DCOMP4_EINT 0x0080 +#define WM8350_IM_AUXADC_DCOMP3_EINT 0x0040 +#define WM8350_IM_AUXADC_DCOMP2_EINT 0x0020 +#define WM8350_IM_AUXADC_DCOMP1_EINT 0x0010 +#define WM8350_IM_SYS_HYST_COMP_FAIL_EINT 0x0008 +#define WM8350_IM_SYS_CHIP_GT115_EINT 0x0004 +#define WM8350_IM_SYS_CHIP_GT140_EINT 0x0002 +#define WM8350_IM_SYS_WDOG_TO_EINT 0x0001 + +/* + * R35 (0x23) - Power Up Interrupt Status Mask + */ +#define WM8350_IM_PUTO_LDO4_EINT 0x0800 +#define WM8350_IM_PUTO_LDO3_EINT 0x0400 +#define WM8350_IM_PUTO_LDO2_EINT 0x0200 +#define WM8350_IM_PUTO_LDO1_EINT 0x0100 +#define WM8350_IM_PUTO_DC6_EINT 0x0020 +#define WM8350_IM_PUTO_DC5_EINT 0x0010 +#define WM8350_IM_PUTO_DC4_EINT 0x0008 +#define WM8350_IM_PUTO_DC3_EINT 0x0004 +#define WM8350_IM_PUTO_DC2_EINT 0x0002 +#define WM8350_IM_PUTO_DC1_EINT 0x0001 + +/* + * R36 (0x24) - Under Voltage Interrupt status Mask + */ +#define WM8350_IM_UV_LDO4_EINT 0x0800 +#define WM8350_IM_UV_LDO3_EINT 0x0400 +#define WM8350_IM_UV_LDO2_EINT 0x0200 +#define WM8350_IM_UV_LDO1_EINT 0x0100 +#define WM8350_IM_UV_DC6_EINT 0x0020 +#define WM8350_IM_UV_DC5_EINT 0x0010 +#define WM8350_IM_UV_DC4_EINT 0x0008 +#define WM8350_IM_UV_DC3_EINT 0x0004 +#define WM8350_IM_UV_DC2_EINT 0x0002 +#define WM8350_IM_UV_DC1_EINT 0x0001 + +/* + * R37 (0x25) - Over Current Interrupt status Mask + */ +#define WM8350_IM_OC_LS_EINT 0x8000 + +/* + * R38 (0x26) - GPIO Interrupt Status Mask + */ +#define WM8350_IM_GP12_EINT 0x1000 +#define WM8350_IM_GP11_EINT 0x0800 +#define WM8350_IM_GP10_EINT 0x0400 +#define WM8350_IM_GP9_EINT 0x0200 +#define WM8350_IM_GP8_EINT 0x0100 +#define WM8350_IM_GP7_EINT 0x0080 +#define WM8350_IM_GP6_EINT 0x0040 +#define WM8350_IM_GP5_EINT 0x0020 +#define WM8350_IM_GP4_EINT 0x0010 +#define WM8350_IM_GP3_EINT 0x0008 +#define WM8350_IM_GP2_EINT 0x0004 +#define WM8350_IM_GP1_EINT 0x0002 +#define WM8350_IM_GP0_EINT 0x0001 + +/* + * R39 (0x27) - Comparator Interrupt Status Mask + */ +#define WM8350_IM_EXT_USB_FB_EINT 0x8000 +#define WM8350_IM_EXT_WALL_FB_EINT 0x4000 +#define WM8350_IM_EXT_BAT_FB_EINT 0x2000 +#define WM8350_IM_CODEC_JCK_DET_L_EINT 0x0800 +#define WM8350_IM_CODEC_JCK_DET_R_EINT 0x0400 +#define WM8350_IM_CODEC_MICSCD_EINT 0x0200 +#define WM8350_IM_CODEC_MICD_EINT 0x0100 +#define WM8350_IM_WKUP_OFF_STATE_EINT 0x0040 +#define WM8350_IM_WKUP_HIB_STATE_EINT 0x0020 +#define WM8350_IM_WKUP_CONV_FAULT_EINT 0x0010 +#define WM8350_IM_WKUP_WDOG_RST_EINT 0x0008 +#define WM8350_IM_WKUP_GP_PWR_ON_EINT 0x0004 +#define WM8350_IM_WKUP_ONKEY_EINT 0x0002 +#define WM8350_IM_WKUP_GP_WAKEUP_EINT 0x0001 + +/* + * R220 (0xDC) - RAM BIST 1 + */ +#define WM8350_READ_STATUS 0x0800 +#define WM8350_TSTRAM_CLK 0x0100 +#define WM8350_TSTRAM_CLK_ENA 0x0080 +#define WM8350_STARTSEQ 0x0040 +#define WM8350_READ_SRC 0x0020 +#define WM8350_COUNT_DIR 0x0010 +#define WM8350_TSTRAM_MODE_MASK 0x000E +#define WM8350_TSTRAM_ENA 0x0001 + +/* + * R225 (0xE1) - DCDC/LDO status + */ +#define WM8350_LS_STS 0x8000 +#define WM8350_LDO4_STS 0x0800 +#define WM8350_LDO3_STS 0x0400 +#define WM8350_LDO2_STS 0x0200 +#define WM8350_LDO1_STS 0x0100 +#define WM8350_DC6_STS 0x0020 +#define WM8350_DC5_STS 0x0010 +#define WM8350_DC4_STS 0x0008 +#define WM8350_DC3_STS 0x0004 +#define WM8350_DC2_STS 0x0002 +#define WM8350_DC1_STS 0x0001 + +/* WM8350 wake up conditions */ +#define WM8350_IRQ_WKUP_OFF_STATE 43 +#define WM8350_IRQ_WKUP_HIB_STATE 44 +#define WM8350_IRQ_WKUP_CONV_FAULT 45 +#define WM8350_IRQ_WKUP_WDOG_RST 46 +#define WM8350_IRQ_WKUP_GP_PWR_ON 47 +#define WM8350_IRQ_WKUP_ONKEY 48 +#define WM8350_IRQ_WKUP_GP_WAKEUP 49 + +/* wm8350 chip revisions */ +#define WM8350_REV_E 0x4 +#define WM8350_REV_F 0x5 +#define WM8350_REV_G 0x6 + +struct wm8350_reg_access { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ +}; +extern const struct wm8350_reg_access wm8350_reg_io_map[]; +extern const u16 wm8350_mode0_defaults[]; +extern const u16 wm8350_mode1_defaults[]; +extern const u16 wm8350_mode2_defaults[]; +extern const u16 wm8350_mode3_defaults[]; + +struct wm8350; + +struct wm8350_irq { + void (*handler) (struct wm8350 *, int, void *); + void *data; +}; + +struct wm8350 { + int rev; /* chip revision */ + + struct device *dev; + + /* device IO */ + union { + struct i2c_client *i2c_client; + struct spi_device *spi_device; + }; + int (*read_dev)(struct wm8350 *wm8350, char reg, int size, void *dest); + int (*write_dev)(struct wm8350 *wm8350, char reg, int size, + void *src); + u16 *reg_cache; +}; + +/* + * WM8350 device initialisation and exit. + */ +int wm8350_device_init(struct wm8350 *wm8350); +void wm8350_device_exit(struct wm8350 *wm8350); + +/* + * WM8350 device IO + */ +int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask); +int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask); +u16 wm8350_reg_read(struct wm8350 *wm8350, int reg); +int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val); +int wm8350_reg_lock(struct wm8350 *wm8350); +int wm8350_reg_unlock(struct wm8350 *wm8350); +int wm8350_block_read(struct wm8350 *wm8350, int reg, int size, u16 *dest); +int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src); + +#endif -- cgit v1.2.3 From 0e7203933224cbe09b5a9125f55b177b8dd5b1bd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:12 +0100 Subject: mfd: Add GPIO pin configuration support for WM8350 The WM8350 provides a number of user-configurable pins providing access to various signals generated by the functions on the chip. These are referred to as GPIO pins in the device documentation but in Linux terms they are more general than that, providing configuration of alternate functions. This patch implements support for selecting the alternate functions for these pins. They can also be used as GPIOs in the normal Linux sense - a subsequent patch will add support for doing so. This code was all written by Liam Girdwood and has had minor updates and rearrangements by Mark Brown. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/Makefile | 2 +- drivers/mfd/wm8350-gpio.c | 222 ++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8350/gpio.h | 10 ++ 3 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/wm8350-gpio.c (limited to 'include/linux') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 45038aed2d31..759b1fe1c891 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o obj-$(CONFIG_MFD_WM8400) += wm8400-core.o -wm8350-objs := wm8350-core.o wm8350-regmap.o +wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o diff --git a/drivers/mfd/wm8350-gpio.c b/drivers/mfd/wm8350-gpio.c new file mode 100644 index 000000000000..ebf99bef392f --- /dev/null +++ b/drivers/mfd/wm8350-gpio.c @@ -0,0 +1,222 @@ +/* + * wm8350-core.c -- Device access for Wolfson WM8350 + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + */ + +#include +#include +#include + +#include +#include +#include + +static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir) +{ + int ret; + + wm8350_reg_unlock(wm8350); + if (dir == WM8350_GPIO_DIR_OUT) + ret = wm8350_clear_bits(wm8350, + WM8350_GPIO_CONFIGURATION_I_O, + 1 << gpio); + else + ret = wm8350_set_bits(wm8350, + WM8350_GPIO_CONFIGURATION_I_O, + 1 << gpio); + wm8350_reg_lock(wm8350); + return ret; +} + +static int gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db) +{ + if (db == WM8350_GPIO_DEBOUNCE_ON) + return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_DEBOUNCE, 1 << gpio); +} + +static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func) +{ + u16 reg; + + wm8350_reg_unlock(wm8350); + switch (gpio) { + case 0: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP0_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 0)); + break; + case 1: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP1_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 4)); + break; + case 2: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP2_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 8)); + break; + case 3: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP3_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 12)); + break; + case 4: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP4_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 0)); + break; + case 5: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP5_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 4)); + break; + case 6: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP6_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 8)); + break; + case 7: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP7_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 12)); + break; + case 8: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP8_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 0)); + break; + case 9: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP9_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 4)); + break; + case 10: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP10_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 8)); + break; + case 11: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP11_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 12)); + break; + case 12: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4) + & ~WM8350_GP12_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4, + reg | ((func & 0xf) << 0)); + break; + default: + wm8350_reg_lock(wm8350); + return -EINVAL; + } + + wm8350_reg_lock(wm8350); + return 0; +} + +static int gpio_set_pull_up(struct wm8350 *wm8350, int gpio, int up) +{ + if (up) + return wm8350_set_bits(wm8350, + WM8350_GPIO_PIN_PULL_UP_CONTROL, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_PIN_PULL_UP_CONTROL, + 1 << gpio); +} + +static int gpio_set_pull_down(struct wm8350 *wm8350, int gpio, int down) +{ + if (down) + return wm8350_set_bits(wm8350, + WM8350_GPIO_PULL_DOWN_CONTROL, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_PULL_DOWN_CONTROL, + 1 << gpio); +} + +static int gpio_set_polarity(struct wm8350 *wm8350, int gpio, int pol) +{ + if (pol == WM8350_GPIO_ACTIVE_HIGH) + return wm8350_set_bits(wm8350, + WM8350_GPIO_PIN_POLARITY_TYPE, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_PIN_POLARITY_TYPE, + 1 << gpio); +} + +static int gpio_set_invert(struct wm8350 *wm8350, int gpio, int invert) +{ + if (invert == WM8350_GPIO_INVERT_ON) + return wm8350_set_bits(wm8350, WM8350_GPIO_INT_MODE, 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_MODE, 1 << gpio); +} + +int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func, + int pol, int pull, int invert, int debounce) +{ + /* make sure we never pull up and down at the same time */ + if (pull == WM8350_GPIO_PULL_NONE) { + if (gpio_set_pull_up(wm8350, gpio, 0)) + goto err; + if (gpio_set_pull_down(wm8350, gpio, 0)) + goto err; + } else if (pull == WM8350_GPIO_PULL_UP) { + if (gpio_set_pull_down(wm8350, gpio, 0)) + goto err; + if (gpio_set_pull_up(wm8350, gpio, 1)) + goto err; + } else if (pull == WM8350_GPIO_PULL_DOWN) { + if (gpio_set_pull_up(wm8350, gpio, 0)) + goto err; + if (gpio_set_pull_down(wm8350, gpio, 1)) + goto err; + } + + if (gpio_set_invert(wm8350, gpio, invert)) + goto err; + if (gpio_set_polarity(wm8350, gpio, pol)) + goto err; + if (gpio_set_debounce(wm8350, gpio, debounce)) + goto err; + if (gpio_set_dir(wm8350, gpio, dir)) + goto err; + return gpio_set_func(wm8350, gpio, func); + +err: + return -EIO; +} +EXPORT_SYMBOL_GPL(wm8350_gpio_config); diff --git a/include/linux/mfd/wm8350/gpio.h b/include/linux/mfd/wm8350/gpio.h index 928aa6e91e36..c6cd2ca8854a 100644 --- a/include/linux/mfd/wm8350/gpio.h +++ b/include/linux/mfd/wm8350/gpio.h @@ -323,4 +323,14 @@ #define WM8350_GP1_LVL 0x0002 #define WM8350_GP0_LVL 0x0001 +struct wm8350; + +int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func, + int pol, int pull, int invert, int debounce); + +/* + * GPIO Interrupts + */ +#define WM8350_IRQ_GPIO(x) (50 + x) + #endif -- cgit v1.2.3 From bcdd4efc1b6b8b98f30e127115f4bc7bbcd6f7ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:13 +0100 Subject: mfd: Add initialisation callback for WM8350 Some functions of the WM8350 require board-specific initialisation on startup. Provide a callback to the WM8350 driver in platform data for platforms to use to configure the chip. Use of a callback allows platforms to control the ordering of initialisation which can be important. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/wm8350-core.c | 12 +++++++++++- drivers/mfd/wm8350-i2c.c | 2 +- include/linux/mfd/wm8350/core.h | 14 +++++++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index c7552c0b7797..071834ba6954 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -388,7 +388,8 @@ out: } EXPORT_SYMBOL_GPL(wm8350_create_cache); -int wm8350_device_init(struct wm8350 *wm8350) +int wm8350_device_init(struct wm8350 *wm8350, + struct wm8350_platform_data *pdata) { int ret = -EINVAL; u16 id1, id2, mask, mode; @@ -439,6 +440,15 @@ int wm8350_device_init(struct wm8350 *wm8350) return ret; } + if (pdata->init) { + ret = pdata->init(wm8350); + if (ret != 0) { + dev_err(wm8350->dev, "Platform init() failed: %d\n", + ret); + goto err; + } + } + return 0; err: diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 2b0569cf9512..245b790961aa 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -65,7 +65,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, wm8350->read_dev = wm8350_i2c_read_device; wm8350->write_dev = wm8350_i2c_write_device; - ret = wm8350_device_init(wm8350); + ret = wm8350_device_init(wm8350, i2c->dev.platform_data); if (ret < 0) goto err; diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 94778c1669dc..8f2beae6278e 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -564,10 +564,22 @@ struct wm8350 { u16 *reg_cache; }; +/** + * Data to be supplied by the platform to initialise the WM8350. + * + * @init: Function called during driver initialisation. Should be + * used by the platform to configure GPIO functions and similar. + */ +struct wm8350_platform_data { + int (*init)(struct wm8350 *wm8350); +}; + + /* * WM8350 device initialisation and exit. */ -int wm8350_device_init(struct wm8350 *wm8350); +int wm8350_device_init(struct wm8350 *wm8350, + struct wm8350_platform_data *pdata); void wm8350_device_exit(struct wm8350 *wm8350); /* -- cgit v1.2.3 From ebccec0fa4e35dff0c18663a492a65f4dc6cad7a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:14 +0100 Subject: mfd: Add WM8350 interrupt support The WM8350 has an interrupt line to the CPU which is shared by the devices on the CPU. This patch adds support for the interrupt controller within the WM8350 which identifies which identifies the interrupt cause. In common with other similar chips this is done outside the standard interrupt framework due to the need to access the interrupt controller over an interrupt-driven bus. This code was all originally written by Liam Girdwood with updates for submission by me. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/wm8350-core.c | 770 +++++++++++++++++++++++++++++++++++++++- drivers/mfd/wm8350-i2c.c | 2 +- include/linux/mfd/wm8350/core.h | 21 +- 3 files changed, 787 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 071834ba6954..e74829f298b9 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -15,15 +15,20 @@ #include #include #include +#include #include #include #include +#include #include #include +#include #include #include +#include #include +#include #define WM8350_UNLOCK_KEY 0x0013 #define WM8350_LOCK_KEY 0x0000 @@ -321,6 +326,743 @@ int wm8350_reg_unlock(struct wm8350 *wm8350) } EXPORT_SYMBOL_GPL(wm8350_reg_unlock); +static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq) +{ + mutex_lock(&wm8350->irq_mutex); + + if (wm8350->irq[irq].handler) + wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data); + else { + dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n", + irq); + wm8350_mask_irq(wm8350, irq); + } + + mutex_unlock(&wm8350->irq_mutex); +} + +/* + * wm8350_irq_worker actually handles the interrupts. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. + */ +static void wm8350_irq_worker(struct work_struct *work) +{ + struct wm8350 *wm8350 = container_of(work, struct wm8350, irq_work); + u16 level_one, status1, status2, comp; + + /* TODO: Use block reads to improve performance? */ + level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS) + & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK); + status1 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1) + & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_1_MASK); + status2 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_2) + & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_2_MASK); + comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS) + & ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK); + + /* over current */ + if (level_one & WM8350_OC_INT) { + u16 oc; + + oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INT_STATUS); + oc &= ~wm8350_reg_read(wm8350, + WM8350_OVER_CURRENT_INT_STATUS_MASK); + + if (oc & WM8350_OC_LS_EINT) /* limit switch */ + wm8350_irq_call_handler(wm8350, WM8350_IRQ_OC_LS); + } + + /* under voltage */ + if (level_one & WM8350_UV_INT) { + u16 uv; + + uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS); + uv &= ~wm8350_reg_read(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK); + + if (uv & WM8350_UV_DC1_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC1); + if (uv & WM8350_UV_DC2_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC2); + if (uv & WM8350_UV_DC3_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC3); + if (uv & WM8350_UV_DC4_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC4); + if (uv & WM8350_UV_DC5_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC5); + if (uv & WM8350_UV_DC6_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC6); + if (uv & WM8350_UV_LDO1_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO1); + if (uv & WM8350_UV_LDO2_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO2); + if (uv & WM8350_UV_LDO3_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO3); + if (uv & WM8350_UV_LDO4_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO4); + } + + /* charger, RTC */ + if (status1) { + if (status1 & WM8350_CHG_BAT_HOT_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_BAT_HOT); + if (status1 & WM8350_CHG_BAT_COLD_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_BAT_COLD); + if (status1 & WM8350_CHG_BAT_FAIL_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_BAT_FAIL); + if (status1 & WM8350_CHG_TO_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_TO); + if (status1 & WM8350_CHG_END_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_END); + if (status1 & WM8350_CHG_START_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_START); + if (status1 & WM8350_CHG_FAST_RDY_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_FAST_RDY); + if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_VBATT_LT_3P9); + if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_VBATT_LT_3P1); + if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_VBATT_LT_2P85); + if (status1 & WM8350_RTC_ALM_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_ALM); + if (status1 & WM8350_RTC_SEC_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_SEC); + if (status1 & WM8350_RTC_PER_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_PER); + } + + /* current sink, system, aux adc */ + if (status2) { + if (status2 & WM8350_CS1_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS1); + if (status2 & WM8350_CS2_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS2); + + if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_HYST_COMP_FAIL); + if (status2 & WM8350_SYS_CHIP_GT115_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_CHIP_GT115); + if (status2 & WM8350_SYS_CHIP_GT140_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_CHIP_GT140); + if (status2 & WM8350_SYS_WDOG_TO_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_WDOG_TO); + + if (status2 & WM8350_AUXADC_DATARDY_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DATARDY); + if (status2 & WM8350_AUXADC_DCOMP4_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP4); + if (status2 & WM8350_AUXADC_DCOMP3_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP3); + if (status2 & WM8350_AUXADC_DCOMP2_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP2); + if (status2 & WM8350_AUXADC_DCOMP1_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP1); + + if (status2 & WM8350_USB_LIMIT_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_USB_LIMIT); + } + + /* wake, codec, ext */ + if (comp) { + if (comp & WM8350_WKUP_OFF_STATE_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_OFF_STATE); + if (comp & WM8350_WKUP_HIB_STATE_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_HIB_STATE); + if (comp & WM8350_WKUP_CONV_FAULT_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_CONV_FAULT); + if (comp & WM8350_WKUP_WDOG_RST_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_WDOG_RST); + if (comp & WM8350_WKUP_GP_PWR_ON_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_GP_PWR_ON); + if (comp & WM8350_WKUP_ONKEY_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_WKUP_ONKEY); + if (comp & WM8350_WKUP_GP_WAKEUP_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_GP_WAKEUP); + + if (comp & WM8350_CODEC_JCK_DET_L_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CODEC_JCK_DET_L); + if (comp & WM8350_CODEC_JCK_DET_R_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CODEC_JCK_DET_R); + if (comp & WM8350_CODEC_MICSCD_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CODEC_MICSCD); + if (comp & WM8350_CODEC_MICD_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CODEC_MICD); + + if (comp & WM8350_EXT_USB_FB_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_USB_FB); + if (comp & WM8350_EXT_WALL_FB_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_EXT_WALL_FB); + if (comp & WM8350_EXT_BAT_FB_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_BAT_FB); + } + + if (level_one & WM8350_GP_INT) { + int i; + u16 gpio; + + gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INT_STATUS); + gpio &= ~wm8350_reg_read(wm8350, + WM8350_GPIO_INT_STATUS_MASK); + + for (i = 0; i < 12; i++) { + if (gpio & (1 << i)) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_GPIO(i)); + } + } + + enable_irq(wm8350->chip_irq); +} + +static irqreturn_t wm8350_irq(int irq, void *data) +{ + struct wm8350 *wm8350 = data; + + disable_irq_nosync(irq); + schedule_work(&wm8350->irq_work); + + return IRQ_HANDLED; +} + +int wm8350_register_irq(struct wm8350 *wm8350, int irq, + void (*handler) (struct wm8350 *, int, void *), + void *data) +{ + if (irq < 0 || irq > WM8350_NUM_IRQ || !handler) + return -EINVAL; + + if (wm8350->irq[irq].handler) + return -EBUSY; + + mutex_lock(&wm8350->irq_mutex); + wm8350->irq[irq].handler = handler; + wm8350->irq[irq].data = data; + mutex_unlock(&wm8350->irq_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_register_irq); + +int wm8350_free_irq(struct wm8350 *wm8350, int irq) +{ + if (irq < 0 || irq > WM8350_NUM_IRQ) + return -EINVAL; + + mutex_lock(&wm8350->irq_mutex); + wm8350->irq[irq].handler = NULL; + mutex_unlock(&wm8350->irq_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_free_irq); + +int wm8350_mask_irq(struct wm8350 *wm8350, int irq) +{ + switch (irq) { + case WM8350_IRQ_CHG_BAT_HOT: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_HOT_EINT); + case WM8350_IRQ_CHG_BAT_COLD: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_COLD_EINT); + case WM8350_IRQ_CHG_BAT_FAIL: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_FAIL_EINT); + case WM8350_IRQ_CHG_TO: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_TO_EINT); + case WM8350_IRQ_CHG_END: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_END_EINT); + case WM8350_IRQ_CHG_START: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_START_EINT); + case WM8350_IRQ_CHG_FAST_RDY: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_FAST_RDY_EINT); + case WM8350_IRQ_RTC_PER: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_PER_EINT); + case WM8350_IRQ_RTC_SEC: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_SEC_EINT); + case WM8350_IRQ_RTC_ALM: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_ALM_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P9: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P9_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P1: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P1_EINT); + case WM8350_IRQ_CHG_VBATT_LT_2P85: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_2P85_EINT); + case WM8350_IRQ_CS1: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS1_EINT); + case WM8350_IRQ_CS2: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS2_EINT); + case WM8350_IRQ_USB_LIMIT: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_USB_LIMIT_EINT); + case WM8350_IRQ_AUXADC_DATARDY: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DATARDY_EINT); + case WM8350_IRQ_AUXADC_DCOMP4: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP4_EINT); + case WM8350_IRQ_AUXADC_DCOMP3: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP3_EINT); + case WM8350_IRQ_AUXADC_DCOMP2: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP2_EINT); + case WM8350_IRQ_AUXADC_DCOMP1: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP1_EINT); + case WM8350_IRQ_SYS_HYST_COMP_FAIL: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_HYST_COMP_FAIL_EINT); + case WM8350_IRQ_SYS_CHIP_GT115: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT115_EINT); + case WM8350_IRQ_SYS_CHIP_GT140: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT140_EINT); + case WM8350_IRQ_SYS_WDOG_TO: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_WDOG_TO_EINT); + case WM8350_IRQ_UV_LDO4: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO4_EINT); + case WM8350_IRQ_UV_LDO3: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO3_EINT); + case WM8350_IRQ_UV_LDO2: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO2_EINT); + case WM8350_IRQ_UV_LDO1: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO1_EINT); + case WM8350_IRQ_UV_DC6: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC6_EINT); + case WM8350_IRQ_UV_DC5: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC5_EINT); + case WM8350_IRQ_UV_DC4: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC4_EINT); + case WM8350_IRQ_UV_DC3: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC3_EINT); + case WM8350_IRQ_UV_DC2: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC2_EINT); + case WM8350_IRQ_UV_DC1: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC1_EINT); + case WM8350_IRQ_OC_LS: + return wm8350_set_bits(wm8350, + WM8350_OVER_CURRENT_INT_STATUS_MASK, + WM8350_IM_OC_LS_EINT); + case WM8350_IRQ_EXT_USB_FB: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_USB_FB_EINT); + case WM8350_IRQ_EXT_WALL_FB: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_WALL_FB_EINT); + case WM8350_IRQ_EXT_BAT_FB: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_BAT_FB_EINT); + case WM8350_IRQ_CODEC_JCK_DET_L: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_L_EINT); + case WM8350_IRQ_CODEC_JCK_DET_R: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_R_EINT); + case WM8350_IRQ_CODEC_MICSCD: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICSCD_EINT); + case WM8350_IRQ_CODEC_MICD: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICD_EINT); + case WM8350_IRQ_WKUP_OFF_STATE: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_HIB_STATE: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_HIB_STATE_EINT); + case WM8350_IRQ_WKUP_CONV_FAULT: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_CONV_FAULT_EINT); + case WM8350_IRQ_WKUP_WDOG_RST: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_GP_PWR_ON: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_PWR_ON_EINT); + case WM8350_IRQ_WKUP_ONKEY: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_ONKEY_EINT); + case WM8350_IRQ_WKUP_GP_WAKEUP: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_WAKEUP_EINT); + case WM8350_IRQ_GPIO(0): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP0_EINT); + case WM8350_IRQ_GPIO(1): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP1_EINT); + case WM8350_IRQ_GPIO(2): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP2_EINT); + case WM8350_IRQ_GPIO(3): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP3_EINT); + case WM8350_IRQ_GPIO(4): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP4_EINT); + case WM8350_IRQ_GPIO(5): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP5_EINT); + case WM8350_IRQ_GPIO(6): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP6_EINT); + case WM8350_IRQ_GPIO(7): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP7_EINT); + case WM8350_IRQ_GPIO(8): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP8_EINT); + case WM8350_IRQ_GPIO(9): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP9_EINT); + case WM8350_IRQ_GPIO(10): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP10_EINT); + case WM8350_IRQ_GPIO(11): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP11_EINT); + case WM8350_IRQ_GPIO(12): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP12_EINT); + default: + dev_warn(wm8350->dev, "Attempting to mask unknown IRQ %d\n", + irq); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_mask_irq); + +int wm8350_unmask_irq(struct wm8350 *wm8350, int irq) +{ + switch (irq) { + case WM8350_IRQ_CHG_BAT_HOT: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_HOT_EINT); + case WM8350_IRQ_CHG_BAT_COLD: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_COLD_EINT); + case WM8350_IRQ_CHG_BAT_FAIL: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_FAIL_EINT); + case WM8350_IRQ_CHG_TO: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_TO_EINT); + case WM8350_IRQ_CHG_END: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_END_EINT); + case WM8350_IRQ_CHG_START: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_START_EINT); + case WM8350_IRQ_CHG_FAST_RDY: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_FAST_RDY_EINT); + case WM8350_IRQ_RTC_PER: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_PER_EINT); + case WM8350_IRQ_RTC_SEC: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_SEC_EINT); + case WM8350_IRQ_RTC_ALM: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_ALM_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P9: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P9_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P1: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P1_EINT); + case WM8350_IRQ_CHG_VBATT_LT_2P85: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_2P85_EINT); + case WM8350_IRQ_CS1: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS1_EINT); + case WM8350_IRQ_CS2: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS2_EINT); + case WM8350_IRQ_USB_LIMIT: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_USB_LIMIT_EINT); + case WM8350_IRQ_AUXADC_DATARDY: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DATARDY_EINT); + case WM8350_IRQ_AUXADC_DCOMP4: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP4_EINT); + case WM8350_IRQ_AUXADC_DCOMP3: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP3_EINT); + case WM8350_IRQ_AUXADC_DCOMP2: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP2_EINT); + case WM8350_IRQ_AUXADC_DCOMP1: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP1_EINT); + case WM8350_IRQ_SYS_HYST_COMP_FAIL: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_HYST_COMP_FAIL_EINT); + case WM8350_IRQ_SYS_CHIP_GT115: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT115_EINT); + case WM8350_IRQ_SYS_CHIP_GT140: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT140_EINT); + case WM8350_IRQ_SYS_WDOG_TO: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_WDOG_TO_EINT); + case WM8350_IRQ_UV_LDO4: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO4_EINT); + case WM8350_IRQ_UV_LDO3: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO3_EINT); + case WM8350_IRQ_UV_LDO2: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO2_EINT); + case WM8350_IRQ_UV_LDO1: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO1_EINT); + case WM8350_IRQ_UV_DC6: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC6_EINT); + case WM8350_IRQ_UV_DC5: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC5_EINT); + case WM8350_IRQ_UV_DC4: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC4_EINT); + case WM8350_IRQ_UV_DC3: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC3_EINT); + case WM8350_IRQ_UV_DC2: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC2_EINT); + case WM8350_IRQ_UV_DC1: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC1_EINT); + case WM8350_IRQ_OC_LS: + return wm8350_clear_bits(wm8350, + WM8350_OVER_CURRENT_INT_STATUS_MASK, + WM8350_IM_OC_LS_EINT); + case WM8350_IRQ_EXT_USB_FB: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_USB_FB_EINT); + case WM8350_IRQ_EXT_WALL_FB: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_WALL_FB_EINT); + case WM8350_IRQ_EXT_BAT_FB: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_BAT_FB_EINT); + case WM8350_IRQ_CODEC_JCK_DET_L: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_L_EINT); + case WM8350_IRQ_CODEC_JCK_DET_R: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_R_EINT); + case WM8350_IRQ_CODEC_MICSCD: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICSCD_EINT); + case WM8350_IRQ_CODEC_MICD: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICD_EINT); + case WM8350_IRQ_WKUP_OFF_STATE: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_HIB_STATE: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_HIB_STATE_EINT); + case WM8350_IRQ_WKUP_CONV_FAULT: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_CONV_FAULT_EINT); + case WM8350_IRQ_WKUP_WDOG_RST: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_GP_PWR_ON: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_PWR_ON_EINT); + case WM8350_IRQ_WKUP_ONKEY: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_ONKEY_EINT); + case WM8350_IRQ_WKUP_GP_WAKEUP: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_WAKEUP_EINT); + case WM8350_IRQ_GPIO(0): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP0_EINT); + case WM8350_IRQ_GPIO(1): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP1_EINT); + case WM8350_IRQ_GPIO(2): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP2_EINT); + case WM8350_IRQ_GPIO(3): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP3_EINT); + case WM8350_IRQ_GPIO(4): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP4_EINT); + case WM8350_IRQ_GPIO(5): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP5_EINT); + case WM8350_IRQ_GPIO(6): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP6_EINT); + case WM8350_IRQ_GPIO(7): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP7_EINT); + case WM8350_IRQ_GPIO(8): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP8_EINT); + case WM8350_IRQ_GPIO(9): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP9_EINT); + case WM8350_IRQ_GPIO(10): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP10_EINT); + case WM8350_IRQ_GPIO(11): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP11_EINT); + case WM8350_IRQ_GPIO(12): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP12_EINT); + default: + dev_warn(wm8350->dev, "Attempting to unmask unknown IRQ %d\n", + irq); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_unmask_irq); + /* * Cache is always host endian. */ @@ -388,11 +1130,12 @@ out: } EXPORT_SYMBOL_GPL(wm8350_create_cache); -int wm8350_device_init(struct wm8350 *wm8350, +int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { int ret = -EINVAL; u16 id1, id2, mask, mode; + int i; /* get WM8350 revision and config mode */ wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); @@ -401,9 +1144,7 @@ int wm8350_device_init(struct wm8350 *wm8350, id1 = be16_to_cpu(id1); id2 = be16_to_cpu(id2); - if (id1 == 0x0) - dev_info(wm8350->dev, "Found Rev C device\n"); - else if (id1 == 0x6143) { + if (id1 == 0x6143) { switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) { case WM8350_REV_E: dev_info(wm8350->dev, "Found Rev E device\n"); @@ -449,6 +1190,24 @@ int wm8350_device_init(struct wm8350 *wm8350, } } + mutex_init(&wm8350->irq_mutex); + INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); + if (irq != NO_IRQ) { + ret = request_irq(irq, wm8350_irq, 0, + "wm8350", wm8350); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to request IRQ: %d\n", + ret); + goto err; + } + } else { + dev_err(wm8350->dev, "No IRQ configured\n"); + goto err; + } + wm8350->chip_irq = irq; + + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); + return 0; err: @@ -459,8 +1218,11 @@ EXPORT_SYMBOL_GPL(wm8350_device_init); void wm8350_device_exit(struct wm8350 *wm8350) { + free_irq(wm8350->chip_irq, wm8350); + flush_work(&wm8350->irq_work); kfree(wm8350->reg_cache); } EXPORT_SYMBOL_GPL(wm8350_device_exit); +MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 245b790961aa..8dfe21bb3bd1 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -65,7 +65,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, wm8350->read_dev = wm8350_i2c_read_device; wm8350->write_dev = wm8350_i2c_write_device; - ret = wm8350_device_init(wm8350, i2c->dev.platform_data); + ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data); if (ret < 0) goto err; diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 8f2beae6278e..d86d38260c6b 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -530,6 +530,8 @@ #define WM8350_REV_F 0x5 #define WM8350_REV_G 0x6 +#define WM8350_NUM_IRQ 63 + struct wm8350_reg_access { u16 readable; /* Mask of readable bits */ u16 writable; /* Mask of writable bits */ @@ -562,6 +564,12 @@ struct wm8350 { int (*write_dev)(struct wm8350 *wm8350, char reg, int size, void *src); u16 *reg_cache; + + /* Interrupt handling */ + struct work_struct irq_work; + struct mutex irq_mutex; /* IRQ table mutex */ + struct wm8350_irq irq[WM8350_NUM_IRQ]; + int chip_irq; }; /** @@ -578,7 +586,7 @@ struct wm8350_platform_data { /* * WM8350 device initialisation and exit. */ -int wm8350_device_init(struct wm8350 *wm8350, +int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata); void wm8350_device_exit(struct wm8350 *wm8350); @@ -594,4 +602,15 @@ int wm8350_reg_unlock(struct wm8350 *wm8350); int wm8350_block_read(struct wm8350 *wm8350, int reg, int size, u16 *dest); int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src); +/* + * WM8350 internal interrupts + */ +int wm8350_register_irq(struct wm8350 *wm8350, int irq, + void (*handler) (struct wm8350 *, int, void *), + void *data); +int wm8350_free_irq(struct wm8350 *wm8350, int irq); +int wm8350_mask_irq(struct wm8350 *wm8350, int irq); +int wm8350_unmask_irq(struct wm8350 *wm8350, int irq); + + #endif -- cgit v1.2.3 From da09155ac8d3f04c299b3d82a6ab0df8d03da632 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Oct 2008 15:58:15 +0100 Subject: regulator: Add WM8350 regulator support The WM8350 features six DCDC convertors (four buck and two boost), four LDO voltage regulators and two constant current sinks. This driver adds support for these through the regulator API. This driver was written by Liam Girdwood with updates for submission from Mark Brown. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/mfd/wm8350-core.c | 6 + drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/wm8350-regulator.c | 1431 ++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8350/core.h | 5 + include/linux/mfd/wm8350/pmic.h | 42 + 6 files changed, 1494 insertions(+) create mode 100644 drivers/regulator/wm8350-regulator.c (limited to 'include/linux') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index e74829f298b9..9a1a0b2b581e 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1218,6 +1218,12 @@ EXPORT_SYMBOL_GPL(wm8350_device_init); void wm8350_device_exit(struct wm8350 *wm8350) { + int i; + + for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) + if (wm8350->pmic.pdev[i] != NULL) + platform_device_unregister(wm8350->pmic.pdev[i]); + free_irq(wm8350->chip_irq, wm8350); flush_work(&wm8350->irq_work); kfree(wm8350->reg_cache); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 99906872cb91..a620023169bc 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -56,6 +56,14 @@ config REGULATOR_BQ24022 charging select between 100 mA and 500 mA charging current limit. +config REGULATOR_WM8350 + tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" + depends on MFD_WM8350 + select REGULATOR + help + This driver provides support for the voltage and current regulators + of the WM8350 AudioPlus PMIC. + config REGULATOR_WM8400 tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC" depends on MFD_WM8400 @@ -63,4 +71,5 @@ config REGULATOR_WM8400 help This driver provides support for the voltage regulators of the WM8400 AudioPlus PMIC. + endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3c6ac73af152..0de39fe2eaea 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c new file mode 100644 index 000000000000..1f44b17e23b1 --- /dev/null +++ b/drivers/regulator/wm8350-regulator.c @@ -0,0 +1,1431 @@ +/* + * wm8350.c -- Voltage and current regulation for the Wolfson WM8350 PMIC + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * linux@wolfsonmicro.com + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Microamps */ +static const int isink_cur[] = { + 4, + 5, + 6, + 7, + 8, + 10, + 11, + 14, + 16, + 19, + 23, + 27, + 32, + 39, + 46, + 54, + 65, + 77, + 92, + 109, + 130, + 154, + 183, + 218, + 259, + 308, + 367, + 436, + 518, + 616, + 733, + 872, + 1037, + 1233, + 1466, + 1744, + 2073, + 2466, + 2933, + 3487, + 4147, + 4932, + 5865, + 6975, + 8294, + 9864, + 11730, + 13949, + 16589, + 19728, + 23460, + 27899, + 33178, + 39455, + 46920, + 55798, + 66355, + 78910, + 93840, + 111596, + 132710, + 157820, + 187681, + 223191 +}; + +static int get_isink_val(int min_uA, int max_uA, u16 *setting) +{ + int i; + + for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0; i--) { + if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) { + *setting = i; + return 0; + } + } + return -EINVAL; +} + +static inline int wm8350_ldo_val_to_mvolts(unsigned int val) +{ + if (val < 16) + return (val * 50) + 900; + else + return ((val - 16) * 100) + 1800; + +} + +static inline unsigned int wm8350_ldo_mvolts_to_val(int mV) +{ + if (mV < 1800) + return (mV - 900) / 50; + else + return ((mV - 1800) / 100) + 16; +} + +static inline int wm8350_dcdc_val_to_mvolts(unsigned int val) +{ + return (val * 25) + 850; +} + +static inline unsigned int wm8350_dcdc_mvolts_to_val(int mV) +{ + return (mV - 850) / 25; +} + +static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + u16 val, setting; + int ret; + + ret = get_isink_val(min_uA, max_uA, &setting); + if (ret != 0) + return ret; + + switch (isink) { + case WM8350_ISINK_A: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + ~WM8350_CS1_ISEL_MASK; + wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A, + val | setting); + break; + case WM8350_ISINK_B: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + ~WM8350_CS1_ISEL_MASK; + wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B, + val | setting); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_isink_get_current(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + u16 val; + + switch (isink) { + case WM8350_ISINK_A: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + WM8350_CS1_ISEL_MASK; + break; + case WM8350_ISINK_B: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + WM8350_CS1_ISEL_MASK; + break; + default: + return 0; + } + + return (isink_cur[val] + 50) / 100; +} + +/* turn on ISINK followed by DCDC */ +static int wm8350_isink_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + switch (wm8350->pmic.isink_A_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS1_ENA); + wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL, + WM8350_CS1_DRIVE); + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_A_dcdc - + WM8350_DCDC_1)); + break; + default: + return -EINVAL; + } + break; + case WM8350_ISINK_B: + switch (wm8350->pmic.isink_B_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS2_ENA); + wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL, + WM8350_CS2_DRIVE); + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_B_dcdc - + WM8350_DCDC_1)); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_isink_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + switch (wm8350->pmic.isink_A_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_A_dcdc - + WM8350_DCDC_1)); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS1_ENA); + break; + default: + return -EINVAL; + } + break; + case WM8350_ISINK_B: + switch (wm8350->pmic.isink_B_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_B_dcdc - + WM8350_DCDC_1)); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS2_ENA); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_isink_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + 0x8000; + case WM8350_ISINK_B: + return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + 0x8000; + } + return -EINVAL; +} + +int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, + u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, + u16 drive) +{ + switch (isink) { + case WM8350_ISINK_A: + wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, + (mode ? WM8350_CS1_FLASH_MODE : 0) | + (trigger ? WM8350_CS1_TRIGSRC : 0) | + duration | on_ramp | off_ramp | drive); + break; + case WM8350_ISINK_B: + wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL, + (mode ? WM8350_CS2_FLASH_MODE : 0) | + (trigger ? WM8350_CS2_TRIGSRC : 0) | + duration | on_ramp | off_ramp | drive); + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_isink_set_flash); + +static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, dcdc = rdev_get_id(rdev), mV, + min_mV = min_uV / 1000, max_mV = max_uV / 1000; + u16 val; + + if (min_mV < 850 || min_mV > 4025) + return -EINVAL; + if (max_mV < 850 || max_mV > 4025) + return -EINVAL; + + /* step size is 25mV */ + mV = (min_mV - 826) / 25; + if (wm8350_dcdc_val_to_mvolts(mV) > max_mV) + return -EINVAL; + BUG_ON(wm8350_dcdc_val_to_mvolts(mV) < min_mV); + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_CONTROL; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_CONTROL; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_CONTROL; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_CONTROL; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, val | mV); + return 0; +} + +static int wm8350_dcdc_get_voltage(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_CONTROL; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_CONTROL; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_CONTROL; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_CONTROL; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK; + return wm8350_dcdc_val_to_mvolts(val) * 1000; +} + +static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, mV = uV / 1000, dcdc = rdev_get_id(rdev); + u16 val; + + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, mV); + + if (mV && (mV < 850 || mV > 4025)) { + dev_err(wm8350->dev, + "DCDC%d suspend voltage %d mV out of range\n", + dcdc, mV); + return -EINVAL; + } + if (mV == 0) + mV = 850; + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_LOW_POWER; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_LOW_POWER; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_LOW_POWER; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_LOW_POWER; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, + val | wm8350_dcdc_mvolts_to_val(mV)); + return 0; +} + +static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, + wm8350->pmic.dcdc1_hib_mode); + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, + wm8350->pmic.dcdc3_hib_mode); + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, + wm8350->pmic.dcdc4_hib_mode); + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, + wm8350->pmic.dcdc6_hib_mode); + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); + wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); + wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); + wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); + wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + WM8350_DC2_HIB_MODE_ACTIVE); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + WM8350_DC5_HIB_MODE_ACTIVE); + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + WM8350_DC2_HIB_MODE_DISABLE); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + WM8350_DC2_HIB_MODE_DISABLE); + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 *hib_mode; + + switch (dcdc) { + case WM8350_DCDC_1: + hib_mode = &wm8350->pmic.dcdc1_hib_mode; + break; + case WM8350_DCDC_3: + hib_mode = &wm8350->pmic.dcdc3_hib_mode; + break; + case WM8350_DCDC_4: + hib_mode = &wm8350->pmic.dcdc4_hib_mode; + break; + case WM8350_DCDC_6: + hib_mode = &wm8350->pmic.dcdc6_hib_mode; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_NORMAL: + *hib_mode = WM8350_DCDC_HIB_MODE_IMAGE; + break; + case REGULATOR_MODE_IDLE: + *hib_mode = WM8350_DCDC_HIB_MODE_STANDBY; + break; + case REGULATOR_MODE_STANDBY: + *hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, mV = uV / 1000, ldo = rdev_get_id(rdev); + u16 val; + + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, mV); + + if (mV < 900 || mV > 3300) { + dev_err(wm8350->dev, "LDO%d voltage %d mV out of range\n", + ldo, mV); + return -EINVAL; + } + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, + val | wm8350_ldo_mvolts_to_val(mV)); + return 0; +} + +static int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; + wm8350_reg_write(wm8350, volt_reg, val); + return 0; +} + +static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; + wm8350_reg_write(wm8350, volt_reg, WM8350_LDO1_HIB_MODE_DIS); + return 0; +} + +static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000, + max_mV = max_uV / 1000; + u16 val; + + if (min_mV < 900 || min_mV > 3300) + return -EINVAL; + if (max_mV < 900 || max_mV > 3300) + return -EINVAL; + + if (min_mV < 1800) { + /* step size is 50mV < 1800mV */ + mV = (min_mV - 851) / 50; + if (wm8350_ldo_val_to_mvolts(mV) > max_mV) + return -EINVAL; + BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); + } else { + /* step size is 100mV > 1800mV */ + mV = ((min_mV - 1701) / 100) + 16; + if (wm8350_ldo_val_to_mvolts(mV) > max_mV) + return -EINVAL; + BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); + } + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_CONTROL; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_CONTROL; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_CONTROL; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_CONTROL; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, val | mV); + return 0; +} + +static int wm8350_ldo_get_voltage(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_CONTROL; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_CONTROL; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_CONTROL; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_CONTROL; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK; + return wm8350_ldo_val_to_mvolts(val) * 1000; +} + +int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, + u16 stop, u16 fault) +{ + int slot_reg; + u16 val; + + dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", + __func__, dcdc, start, stop); + + /* slot valid ? */ + if (start > 15 || stop > 15) + return -EINVAL; + + switch (dcdc) { + case WM8350_DCDC_1: + slot_reg = WM8350_DCDC1_TIMEOUTS; + break; + case WM8350_DCDC_2: + slot_reg = WM8350_DCDC2_TIMEOUTS; + break; + case WM8350_DCDC_3: + slot_reg = WM8350_DCDC3_TIMEOUTS; + break; + case WM8350_DCDC_4: + slot_reg = WM8350_DCDC4_TIMEOUTS; + break; + case WM8350_DCDC_5: + slot_reg = WM8350_DCDC5_TIMEOUTS; + break; + case WM8350_DCDC_6: + slot_reg = WM8350_DCDC6_TIMEOUTS; + break; + default: + return -EINVAL; + } + + val = wm8350_reg_read(wm8350, slot_reg) & + ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK | + WM8350_DC1_ERRACT_MASK); + wm8350_reg_write(wm8350, slot_reg, + val | (start << WM8350_DC1_ENSLOT_SHIFT) | + (stop << WM8350_DC1_SDSLOT_SHIFT) | + (fault << WM8350_DC1_ERRACT_SHIFT)); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot); + +int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop) +{ + int slot_reg; + u16 val; + + dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", + __func__, ldo, start, stop); + + /* slot valid ? */ + if (start > 15 || stop > 15) + return -EINVAL; + + switch (ldo) { + case WM8350_LDO_1: + slot_reg = WM8350_LDO1_TIMEOUTS; + break; + case WM8350_LDO_2: + slot_reg = WM8350_LDO2_TIMEOUTS; + break; + case WM8350_LDO_3: + slot_reg = WM8350_LDO3_TIMEOUTS; + break; + case WM8350_LDO_4: + slot_reg = WM8350_LDO4_TIMEOUTS; + break; + default: + return -EINVAL; + } + + val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK; + wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6))); + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_ldo_set_slot); + +int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, + u16 ilim, u16 ramp, u16 feedback) +{ + u16 val; + + dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc, + mode ? "normal" : "boost", ilim ? "low" : "normal"); + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK | + WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK); + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + (mode << WM8350_DC2_MODE_SHIFT) | + (ilim << WM8350_DC2_ILIM_SHIFT) | + (ramp << WM8350_DC2_RMP_SHIFT) | + (feedback << WM8350_DC2_FBSRC_SHIFT)); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK | + WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK); + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + (mode << WM8350_DC5_MODE_SHIFT) | + (ilim << WM8350_DC5_ILIM_SHIFT) | + (ramp << WM8350_DC5_RMP_SHIFT) | + (feedback << WM8350_DC5_FBSRC_SHIFT)); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode); + +static int wm8350_dcdc_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 shift; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + shift = dcdc - WM8350_DCDC_1; + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + return 0; +} + +static int wm8350_dcdc_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 shift; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + shift = dcdc - WM8350_DCDC_1; + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + + return 0; +} + +static int wm8350_ldo_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int ldo = rdev_get_id(rdev); + u16 shift; + + if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) + return -EINVAL; + + shift = (ldo - WM8350_LDO_1) + 8; + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + return 0; +} + +static int wm8350_ldo_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int ldo = rdev_get_id(rdev); + u16 shift; + + if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) + return -EINVAL; + + shift = (ldo - WM8350_LDO_1) + 8; + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + return 0; +} + +static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable) +{ + int reg = 0, ret; + + switch (dcdc) { + case WM8350_DCDC_1: + reg = WM8350_DCDC1_FORCE_PWM; + break; + case WM8350_DCDC_3: + reg = WM8350_DCDC3_FORCE_PWM; + break; + case WM8350_DCDC_4: + reg = WM8350_DCDC4_FORCE_PWM; + break; + case WM8350_DCDC_6: + reg = WM8350_DCDC6_FORCE_PWM; + break; + default: + return -EINVAL; + } + + if (enable) + ret = wm8350_set_bits(wm8350, reg, + WM8350_DCDC1_FORCE_PWM_ENA); + else + ret = wm8350_clear_bits(wm8350, reg, + WM8350_DCDC1_FORCE_PWM_ENA); + return ret; +} + +static int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) + return -EINVAL; + + val = 1 << (dcdc - WM8350_DCDC_1); + + switch (mode) { + case REGULATOR_MODE_FAST: + /* force continuous mode */ + wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + force_continuous_enable(wm8350, dcdc, 1); + break; + case REGULATOR_MODE_NORMAL: + /* active / pulse skipping */ + wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + force_continuous_enable(wm8350, dcdc, 0); + break; + case REGULATOR_MODE_IDLE: + /* standby mode */ + force_continuous_enable(wm8350, dcdc, 0); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + break; + case REGULATOR_MODE_STANDBY: + /* LDO mode */ + force_continuous_enable(wm8350, dcdc, 0); + wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + break; + } + + return 0; +} + +static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 mask, sleep, active, force; + int mode = REGULATOR_MODE_NORMAL; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) + return -EINVAL; + + mask = 1 << (dcdc - WM8350_DCDC_1); + active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask; + sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask; + force = wm8350_reg_read(wm8350, WM8350_DCDC1_FORCE_PWM) + & WM8350_DCDC1_FORCE_PWM_ENA; + dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x", + mask, active, sleep, force); + + if (active && !sleep) { + if (force) + mode = REGULATOR_MODE_FAST; + else + mode = REGULATOR_MODE_NORMAL; + } else if (!active && !sleep) + mode = REGULATOR_MODE_IDLE; + else if (!sleep) + mode = REGULATOR_MODE_STANDBY; + + return mode; +} + +static unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev) +{ + return REGULATOR_MODE_NORMAL; +} + +struct wm8350_dcdc_efficiency { + int uA_load_min; + int uA_load_max; + unsigned int mode; +}; + +static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = { + {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ + {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ + {100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ + {-1, -1, REGULATOR_MODE_NORMAL}, +}; + +static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = { + {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ + {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ + {100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ + {-1, -1, REGULATOR_MODE_NORMAL}, +}; + +static unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff) +{ + int i = 0; + + while (eff[i].uA_load_min != -1) { + if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max) + return eff[i].mode; + } + return REGULATOR_MODE_NORMAL; +} + +/* Query the regulator for it's most efficient mode @ uV,uA + * WM8350 regulator efficiency is pretty similar over + * different input and output uV. + */ +static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, + int output_uA) +{ + int dcdc = rdev_get_id(rdev), mode; + + switch (dcdc) { + case WM8350_DCDC_1: + case WM8350_DCDC_6: + mode = get_mode(output_uA, dcdc1_6_efficiency); + break; + case WM8350_DCDC_3: + case WM8350_DCDC_4: + mode = get_mode(output_uA, dcdc3_4_efficiency); + break; + default: + mode = REGULATOR_MODE_NORMAL; + break; + } + return mode; +} + +static int wm8350_dcdc_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev), shift; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + shift = dcdc - WM8350_DCDC_1; + return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED) + & (1 << shift); +} + +static int wm8350_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int ldo = rdev_get_id(rdev), shift; + + if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) + return -EINVAL; + + shift = (ldo - WM8350_LDO_1) + 8; + return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED) + & (1 << shift); +} + +static struct regulator_ops wm8350_dcdc_ops = { + .set_voltage = wm8350_dcdc_set_voltage, + .get_voltage = wm8350_dcdc_get_voltage, + .enable = wm8350_dcdc_enable, + .disable = wm8350_dcdc_disable, + .get_mode = wm8350_dcdc_get_mode, + .set_mode = wm8350_dcdc_set_mode, + .get_optimum_mode = wm8350_dcdc_get_optimum_mode, + .is_enabled = wm8350_dcdc_is_enabled, + .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage, + .set_suspend_enable = wm8350_dcdc_set_suspend_enable, + .set_suspend_disable = wm8350_dcdc_set_suspend_disable, + .set_suspend_mode = wm8350_dcdc_set_suspend_mode, +}; + +static struct regulator_ops wm8350_dcdc2_5_ops = { + .enable = wm8350_dcdc_enable, + .disable = wm8350_dcdc_disable, + .is_enabled = wm8350_dcdc_is_enabled, + .set_suspend_enable = wm8350_dcdc25_set_suspend_enable, + .set_suspend_disable = wm8350_dcdc25_set_suspend_disable, +}; + +static struct regulator_ops wm8350_ldo_ops = { + .set_voltage = wm8350_ldo_set_voltage, + .get_voltage = wm8350_ldo_get_voltage, + .enable = wm8350_ldo_enable, + .disable = wm8350_ldo_disable, + .is_enabled = wm8350_ldo_is_enabled, + .get_mode = wm8350_ldo_get_mode, + .set_suspend_voltage = wm8350_ldo_set_suspend_voltage, + .set_suspend_enable = wm8350_ldo_set_suspend_enable, + .set_suspend_disable = wm8350_ldo_set_suspend_disable, +}; + +static struct regulator_ops wm8350_isink_ops = { + .set_current_limit = wm8350_isink_set_current, + .get_current_limit = wm8350_isink_get_current, + .enable = wm8350_isink_enable, + .disable = wm8350_isink_disable, + .is_enabled = wm8350_isink_is_enabled, +}; + +static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { + { + .name = "DCDC1", + .id = WM8350_DCDC_1, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = WM8350_DCDC_2, + .ops = &wm8350_dcdc2_5_ops, + .irq = WM8350_IRQ_UV_DC2, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC3", + .id = WM8350_DCDC_3, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC3, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC4", + .id = WM8350_DCDC_4, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC4, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC5", + .id = WM8350_DCDC_5, + .ops = &wm8350_dcdc2_5_ops, + .irq = WM8350_IRQ_UV_DC5, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC6", + .id = WM8350_DCDC_6, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC6, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO1", + .id = WM8350_LDO_1, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = WM8350_LDO_2, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO2, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = WM8350_LDO_3, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO3, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = WM8350_LDO_4, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO4, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "ISINKA", + .id = WM8350_ISINK_A, + .ops = &wm8350_isink_ops, + .irq = WM8350_IRQ_CS1, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, + { + .name = "ISINKB", + .id = WM8350_ISINK_B, + .ops = &wm8350_isink_ops, + .irq = WM8350_IRQ_CS2, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, +}; + +static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data) +{ + struct regulator_dev *rdev = (struct regulator_dev *)data; + + if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_REGULATION_OUT, + wm8350); + else + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_UNDER_VOLTAGE, + wm8350); +} + +static int wm8350_regulator_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + struct regulator_dev *rdev; + int ret; + u16 val; + + if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B) + return -ENODEV; + + /* do any regulatior specific init */ + switch (pdev->id) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); + wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); + wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); + wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); + wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + } + + + /* register regulator */ + rdev = regulator_register(&wm8350_reg[pdev->id], &pdev->dev, + dev_get_drvdata(&pdev->dev)); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", + wm8350_reg[pdev->id].name); + return PTR_ERR(rdev); + } + + /* register regulator IRQ */ + ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq, + pmic_uv_handler, rdev); + if (ret < 0) { + regulator_unregister(rdev); + dev_err(&pdev->dev, "failed to register regulator %s IRQ\n", + wm8350_reg[pdev->id].name); + return ret; + } + + wm8350_unmask_irq(wm8350, wm8350_reg[pdev->id].irq); + + return 0; +} + +static int wm8350_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + + wm8350_mask_irq(wm8350, wm8350_reg[pdev->id].irq); + wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq); + + regulator_unregister(rdev); + + return 0; +} + +int wm8350_register_regulator(struct wm8350 *wm8350, int reg, + struct regulator_init_data *initdata) +{ + struct platform_device *pdev; + int ret; + + if (wm8350->pmic.pdev[reg]) + return -EBUSY; + + pdev = platform_device_alloc("wm8350-regulator", reg); + if (!pdev) + return -ENOMEM; + + wm8350->pmic.pdev[reg] = pdev; + + initdata->driver_data = wm8350; + + pdev->dev.platform_data = initdata; + pdev->dev.parent = wm8350->dev; + platform_set_drvdata(pdev, wm8350); + + ret = platform_device_add(pdev); + + if (ret != 0) { + dev_err(wm8350->dev, "Failed to register regulator %d: %d\n", + reg, ret); + platform_device_del(pdev); + wm8350->pmic.pdev[reg] = NULL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_register_regulator); + +static struct platform_driver wm8350_regulator_driver = { + .probe = wm8350_regulator_probe, + .remove = wm8350_regulator_remove, + .driver = { + .name = "wm8350-regulator", + }, +}; + +static int __init wm8350_regulator_init(void) +{ + return platform_driver_register(&wm8350_regulator_driver); +} +subsys_initcall(wm8350_regulator_init); + +static void __exit wm8350_regulator_exit(void) +{ + platform_driver_unregister(&wm8350_regulator_driver); +} +module_exit(wm8350_regulator_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("WM8350 voltage and current regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index d86d38260c6b..348101cb00dc 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -17,6 +17,8 @@ #include #include +#include + /* * Register values. */ @@ -570,6 +572,9 @@ struct wm8350 { struct mutex irq_mutex; /* IRQ table mutex */ struct wm8350_irq irq[WM8350_NUM_IRQ]; int chip_irq; + + /* Client devices */ + struct wm8350_pmic pmic; }; /** diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h index 28d2fab27875..69b69e07f62f 100644 --- a/include/linux/mfd/wm8350/pmic.h +++ b/include/linux/mfd/wm8350/pmic.h @@ -696,4 +696,46 @@ #define NUM_WM8350_REGULATORS 12 +struct wm8350; +struct platform_device; +struct regulator_init_data; + +struct wm8350_pmic { + /* ISINK to DCDC mapping */ + int isink_A_dcdc; + int isink_B_dcdc; + + /* hibernate configs */ + u16 dcdc1_hib_mode; + u16 dcdc3_hib_mode; + u16 dcdc4_hib_mode; + u16 dcdc6_hib_mode; + + /* regulator devices */ + struct platform_device *pdev[NUM_WM8350_REGULATORS]; +}; + +int wm8350_register_regulator(struct wm8350 *wm8350, int reg, + struct regulator_init_data *initdata); + +/* + * Additional DCDC control not supported via regulator API + */ +int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, + u16 stop, u16 fault); +int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, + u16 ilim, u16 ramp, u16 feedback); + +/* + * Additional LDO control not supported via regulator API + */ +int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop); + +/* + * Additional ISINK control not supported via regulator API + */ +int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, + u16 trigger, u16 duration, u16 on_ramp, + u16 off_ramp, u16 drive); + #endif -- cgit v1.2.3 From add41cb46175618fd42bc1ca07fe7f9dd38bf702 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Oct 2008 15:45:22 +0100 Subject: mfd: Add placeholders for WM8350 client devices In order to avoid merge problems further down the line add placeholders for several of the WM8350 client devices and register them, otherwise the patches adding the client devices will all try to update the same code. Also remove redundant checks for null regulator platform devices while we're at it. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/wm8350-core.c | 18 ++++++++++++++++-- include/linux/mfd/wm8350/audio.h | 6 ++++++ include/linux/mfd/wm8350/core.h | 10 ++++++++++ include/linux/mfd/wm8350/gpio.h | 6 ++++++ include/linux/mfd/wm8350/rtc.h | 6 ++++++ include/linux/mfd/wm8350/supply.h | 6 ++++++ include/linux/mfd/wm8350/wdt.h | 8 +++++++- 7 files changed, 57 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index cd1f76efed4e..382e38c66914 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1234,6 +1234,15 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); + wm8350_client_dev_register(wm8350, "wm8350-codec", + &(wm8350->codec.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-gpio", + &(wm8350->gpio.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-power", + &(wm8350->power.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev)); + return 0; err: @@ -1247,8 +1256,13 @@ void wm8350_device_exit(struct wm8350 *wm8350) int i; for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) - if (wm8350->pmic.pdev[i] != NULL) - platform_device_unregister(wm8350->pmic.pdev[i]); + platform_device_unregister(wm8350->pmic.pdev[i]); + + platform_device_unregister(wm8350->wdt.pdev); + platform_device_unregister(wm8350->rtc.pdev); + platform_device_unregister(wm8350->power.pdev); + platform_device_unregister(wm8350->gpio.pdev); + platform_device_unregister(wm8350->codec.pdev); free_irq(wm8350->chip_irq, wm8350); flush_work(&wm8350->irq_work); diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h index 43342f767112..217bb22ebb8e 100644 --- a/include/linux/mfd/wm8350/audio.h +++ b/include/linux/mfd/wm8350/audio.h @@ -13,6 +13,8 @@ #ifndef __LINUX_MFD_WM8350_AUDIO_H_ #define __LINUX_MFD_WM8350_AUDIO_H_ +#include + #define WM8350_CLOCK_CONTROL_1 0x28 #define WM8350_CLOCK_CONTROL_2 0x29 #define WM8350_FLL_CONTROL_1 0x2A @@ -589,4 +591,8 @@ #define WM8350_IRQ_CODEC_MICSCD 41 #define WM8350_IRQ_CODEC_MICD 42 +struct wm8350_codec { + struct platform_device *pdev; +}; + #endif diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 348101cb00dc..6ebf97f2a475 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -17,7 +17,12 @@ #include #include +#include +#include #include +#include +#include +#include /* * Register values. @@ -574,7 +579,12 @@ struct wm8350 { int chip_irq; /* Client devices */ + struct wm8350_codec codec; + struct wm8350_gpio gpio; struct wm8350_pmic pmic; + struct wm8350_power power; + struct wm8350_rtc rtc; + struct wm8350_wdt wdt; }; /** diff --git a/include/linux/mfd/wm8350/gpio.h b/include/linux/mfd/wm8350/gpio.h index c6cd2ca8854a..ed91e8f5d298 100644 --- a/include/linux/mfd/wm8350/gpio.h +++ b/include/linux/mfd/wm8350/gpio.h @@ -13,6 +13,8 @@ #ifndef __LINUX_MFD_WM8350_GPIO_H_ #define __LINUX_MFD_WM8350_GPIO_H_ +#include + /* * GPIO Registers. */ @@ -328,6 +330,10 @@ struct wm8350; int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func, int pol, int pull, int invert, int debounce); +struct wm8350_gpio { + struct platform_device *pdev; +}; + /* * GPIO Interrupts */ diff --git a/include/linux/mfd/wm8350/rtc.h b/include/linux/mfd/wm8350/rtc.h index cb337ea8082f..dfda69e9f440 100644 --- a/include/linux/mfd/wm8350/rtc.h +++ b/include/linux/mfd/wm8350/rtc.h @@ -12,6 +12,8 @@ #ifndef __LINUX_MFD_WM8350_RTC_H #define __LINUX_MFD_WM8350_RTC_H +#include + /* * Register values. */ @@ -257,4 +259,8 @@ #define WM8350_IRQ_RTC_SEC 8 #define WM8350_IRQ_RTC_ALM 9 +struct wm8350_rtc { + struct platform_device *pdev; +}; + #endif diff --git a/include/linux/mfd/wm8350/supply.h b/include/linux/mfd/wm8350/supply.h index f1d4317cf022..1c8f3cde79b0 100644 --- a/include/linux/mfd/wm8350/supply.h +++ b/include/linux/mfd/wm8350/supply.h @@ -13,6 +13,8 @@ #ifndef __LINUX_MFD_WM8350_SUPPLY_H_ #define __LINUX_MFD_WM8350_SUPPLY_H_ +#include + /* * Charger registers */ @@ -102,4 +104,8 @@ #define WM8350_IRQ_EXT_WALL_FB 37 #define WM8350_IRQ_EXT_BAT_FB 38 +struct wm8350_power { + struct platform_device *pdev; +}; + #endif diff --git a/include/linux/mfd/wm8350/wdt.h b/include/linux/mfd/wm8350/wdt.h index 72fc9f554569..f6135b5e5ef4 100644 --- a/include/linux/mfd/wm8350/wdt.h +++ b/include/linux/mfd/wm8350/wdt.h @@ -1,7 +1,7 @@ /* * wdt.h -- Watchdog Driver for Wolfson WM8350 PMIC * - * Copyright 2007 Wolfson Microelectronics PLC + * Copyright 2007, 2008 Wolfson Microelectronics PLC * * 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 @@ -12,6 +12,8 @@ #ifndef __LINUX_MFD_WM8350_WDT_H_ #define __LINUX_MFD_WM8350_WDT_H_ +#include + #define WM8350_WDOG_HIB_MODE 0x0080 #define WM8350_WDOG_DEBUG 0x0040 #define WM8350_WDOG_MODE_MASK 0x0030 @@ -19,4 +21,8 @@ #define WM8350_IRQ_SYS_WDOG_TO 24 +struct wm8350_wdt { + struct platform_device *pdev; +}; + #endif -- cgit v1.2.3 From 113aa838ec3a235d883f8357d31d90e16c47fc89 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 19:01:08 -0700 Subject: net: Rationalise email address: Network Specific Parts Clean up the various different email addresses of mine listed in the code to a single current and valid address. As Dave says his network merges for 2.6.28 are now done this seems a good point to send them in where they won't risk disrupting real changes. Signed-off-by: Alan Cox Signed-off-by: David S. Miller --- drivers/net/3c501.c | 12 ++++++------ drivers/net/3c515.c | 2 +- drivers/net/appletalk/cops.c | 2 +- drivers/net/eexpress.c | 2 +- drivers/net/ibmlana.c | 2 +- drivers/net/macmace.c | 2 +- drivers/net/pcmcia/3c589_cs.c | 2 +- drivers/net/pcmcia/nmclan_cs.c | 2 +- drivers/net/tlan.c | 3 ++- drivers/net/tokenring/smctr.c | 2 +- drivers/net/tulip/dmfe.c | 4 ++-- drivers/net/via-velocity.c | 2 +- drivers/net/wan/z85230.c | 3 ++- drivers/net/wan/z85230.h | 2 +- drivers/net/wireless/wavelan.c | 2 +- drivers/net/wireless/wavelan.p.h | 2 +- include/linux/if_ether.h | 2 +- include/linux/if_fddi.h | 2 +- include/linux/if_hippi.h | 2 +- include/linux/igmp.h | 2 +- include/linux/netdevice.h | 2 +- net/802/psnap.c | 2 +- net/appletalk/ddp.c | 4 ++-- net/core/datagram.c | 2 +- net/core/dev_mcast.c | 2 +- net/core/skbuff.c | 2 +- net/core/stream.c | 2 +- net/ipv4/icmp.c | 2 +- net/ipv4/igmp.c | 2 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_input.c | 2 +- net/ipv4/ipip.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/udp.c | 2 +- net/netlink/af_netlink.c | 2 +- net/sunrpc/xprtsock.c | 4 ++-- net/unix/af_unix.c | 2 +- 37 files changed, 47 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c index 5ba4bab6d43e..7d15e7c6bcad 100644 --- a/drivers/net/3c501.c +++ b/drivers/net/3c501.c @@ -17,7 +17,7 @@ Annapolis MD 21403 Fixed (again!) the missing interrupt locking on TX/RX shifting. - Alan Cox + Alan Cox Removed calls to init_etherdev since they are no longer needed, and cleaned up modularization just a bit. The driver still allows only @@ -29,16 +29,16 @@ the board. Now getting 150K/second FTP with a 3c501 card. Still playing with a TX-TX optimisation to see if we can touch 180-200K/second as seems theoretically maximum. - 19950402 Alan Cox + 19950402 Alan Cox Cleaned up for 2.3.x because we broke SMP now. - 20000208 Alan Cox + 20000208 Alan Cox Check up pass for 2.5. Nothing significant changed - 20021009 Alan Cox + 20021009 Alan Cox Fixed zero fill corner case - 20030104 Alan Cox + 20030104 Alan Cox For the avoidance of doubt the "preferred form" of this code is one which @@ -104,7 +104,7 @@ static const char version[] = - DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@redhat.com).\n"; + DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@lxorguk.ukuu.org.uk).\n"; /* * Braindamage remaining: diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index e4e3241628d6..a0f8b6e2d0af 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -18,7 +18,7 @@ 2001/11/17 - Added ethtool support (jgarzik) - 2002/10/28 - Locking updates for 2.5 (alan@redhat.com) + 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk) */ diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index a0b4c8516073..735fc9476403 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -4,7 +4,7 @@ * - Jay Schulist * * With more than a little help from; - * - Alan Cox + * - Alan Cox * * Derived from: * - skeleton.c: A network driver outline for linux. diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 795c594a4b7c..b751c1b96cfa 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -8,7 +8,7 @@ * * Many modifications, and currently maintained, by * Philip Blundell - * Added the Compaq LTE Alan Cox + * Added the Compaq LTE Alan Cox * Added MCA support Adam Fritzler * * Note - this driver is experimental still - it has problems on faster diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c index 95e3464068db..f02764725a22 100644 --- a/drivers/net/ibmlana.c +++ b/drivers/net/ibmlana.c @@ -71,7 +71,7 @@ History: June 1st, 2000 corrected version codes, added support for the latest 2.3 changes Oct 28th, 2002 - cleaned up for the 2.5 tree + cleaned up for the 2.5 tree *************************************************************************/ diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c index 51ad3765e075..85587a6667b9 100644 --- a/drivers/net/macmace.c +++ b/drivers/net/macmace.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. * * Copyright (C) 1996 Paul Mackerras. - * Copyright (C) 1998 Alan Cox + * Copyright (C) 1998 Alan Cox * * Modified heavily by Joshua M. Thompson based on Dave Huang's NetBSD driver * diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 549a64558420..29bd8532d323 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -15,7 +15,7 @@ incorporated herein by reference. Donald Becker may be reached at becker@scyld.com - Updated for 2.5.x by Alan Cox + Updated for 2.5.x by Alan Cox ======================================================================*/ diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index cfcbea9b7e2e..ee5dcbfd0ba9 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -69,7 +69,7 @@ Driver Notes and Issues History ------------------------------------------------------------------------------- Log: nmclan_cs.c,v - * 2.5.75-ac1 2003/07/11 Alan Cox + * 2.5.75-ac1 2003/07/11 Alan Cox * Fixed hang on card eject as we probe it * Cleaned up to use new style locking. * diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index ec871f646766..c41d68761364 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -29,7 +29,8 @@ * * Tigran Aivazian : TLan_PciProbe() now uses * new PCI BIOS interface. - * Alan Cox : Fixed the out of memory + * Alan Cox : + * Fixed the out of memory * handling. * * Torben Mathiasen New Maintainer! diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c index fa73e6eed6be..ed50d288e494 100644 --- a/drivers/net/tokenring/smctr.c +++ b/drivers/net/tokenring/smctr.c @@ -25,7 +25,7 @@ * To do: * 1. Multicast support. * - * Initial 2.5 cleanup Alan Cox 2002/10/28 + * Initial 2.5 cleanup Alan Cox 2002/10/28 */ #include diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index 656200472fa1..8e46a513a252 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -23,7 +23,7 @@ Marcelo Tosatti : Made it compile in 2.3 (device to net_device) - Alan Cox : + Alan Cox : Cleaned up for kernel merge. Removed the back compatibility support Reformatted, fixing spelling etc as I went @@ -49,7 +49,7 @@ support. Updated PCI resource allocation. Do not forget to unmap PCI mapped skbs. - Alan Cox + Alan Cox Added new PCI identifiers provided by Clear Zhang at ALi for their 1563 ethernet device. diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index ad20f96edfa1..2dced383bcfb 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -12,7 +12,7 @@ * Scatter gather * More testing * - * The changes are (c) Copyright 2004, Red Hat Inc. + * The changes are (c) Copyright 2004, Red Hat Inc. * Additional fixes and clean up: Francois Romieu * * This source has not been verified for use in safety critical systems. diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 243bd8d918fe..ccd9cd35ecbe 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -18,7 +18,8 @@ * DMA now uses get_free_page as kmalloc buffers may span a 64K * boundary. * - * Modified for SMP safety and SMP locking by Alan Cox + * Modified for SMP safety and SMP locking by Alan Cox + * * * Performance * diff --git a/drivers/net/wan/z85230.h b/drivers/net/wan/z85230.h index 4f372396c512..85b3e785d484 100644 --- a/drivers/net/wan/z85230.h +++ b/drivers/net/wan/z85230.h @@ -2,7 +2,7 @@ * Description of Z8530 Z85C30 and Z85230 communications chips * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 Alan Cox + * Copyright (C) 1998 Alan Cox */ #ifndef _Z8530_H diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index 136220b5ca81..e939a73ff794 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -4387,7 +4387,7 @@ MODULE_LICENSE("GPL"); * * Thanks go also to: * James Ashton (jaa101@syseng.anu.edu.au), - * Alan Cox (alan@redhat.com), + * Alan Cox (alan@lxorguk.ukuu.org.uk), * Allan Creighton (allanc@cs.usyd.edu.au), * Matthew Geier (matthew@cs.usyd.edu.au), * Remo di Giovanni (remo@cs.usyd.edu.au), diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h index b33ac47dd8df..44d31bbf39e4 100644 --- a/drivers/net/wireless/wavelan.p.h +++ b/drivers/net/wireless/wavelan.p.h @@ -186,7 +186,7 @@ * * Thanks go also to: * James Ashton , - * Alan Cox , + * Alan Cox , * Allan Creighton , * Matthew Geier , * Remo di Giovanni , diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index bf1a53b2682e..7f3c735f422b 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -9,7 +9,7 @@ * * Author: Fred N. van Kempen, * Donald Becker, - * Alan Cox, + * Alan Cox, * Steve Whitehouse, * * This program is free software; you can redistribute it and/or diff --git a/include/linux/if_fddi.h b/include/linux/if_fddi.h index ae77daed6c2f..45de1046dbbf 100644 --- a/include/linux/if_fddi.h +++ b/include/linux/if_fddi.h @@ -12,7 +12,7 @@ * if_fddi.h is based on previous if_ether.h and if_tr.h work by * Fred N. van Kempen, * Donald Becker, - * Alan Cox, + * Alan Cox, * Steve Whitehouse, * Peter De Schrijver, * diff --git a/include/linux/if_hippi.h b/include/linux/if_hippi.h index 94d31ca7d71a..f0f23516bb59 100644 --- a/include/linux/if_hippi.h +++ b/include/linux/if_hippi.h @@ -9,7 +9,7 @@ * * Author: Fred N. van Kempen, * Donald Becker, - * Alan Cox, + * Alan Cox, * Steve Whitehouse, * Jes Sorensen, * diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 7bb3c095c15b..f734a0ba0698 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -2,7 +2,7 @@ * Linux NET3: Internet Group Management Protocol [IGMP] * * Authors: - * Alan Cox + * Alan Cox * * Extended to talk the BSD extended IGMP protocol of mrouted 3.6 * diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d3ea3de70a8a..64875859d654 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -11,7 +11,7 @@ * Fred N. van Kempen, * Corey Minyard * Donald J. Becker, - * Alan Cox, + * Alan Cox, * Bjorn Ekwall. * Pekka Riikonen * diff --git a/net/802/psnap.c b/net/802/psnap.c index b3cfe5a14fca..70980baeb682 100644 --- a/net/802/psnap.c +++ b/net/802/psnap.c @@ -1,7 +1,7 @@ /* * SNAP data link layer. Derived from 802.2 * - * Alan Cox , + * Alan Cox , * from the 802.2 layer by Greg Page. * Merged in additions from Greg Page's psnap.c. * diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 0c850427a85b..d3134e7e6ee8 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -2,7 +2,7 @@ * DDP: An implementation of the AppleTalk DDP protocol for * Ethernet 'ELAP'. * - * Alan Cox + * Alan Cox * * With more than a little assistance from * @@ -1934,6 +1934,6 @@ static void __exit atalk_exit(void) module_exit(atalk_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alan Cox "); +MODULE_AUTHOR("Alan Cox "); MODULE_DESCRIPTION("AppleTalk 0.20\n"); MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/net/core/datagram.c b/net/core/datagram.c index 52f577a0f544..ee631843c2f5 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -9,7 +9,7 @@ * identical recvmsg() code. So we share it here. The poll was * shared before but buried in udp.c so I moved it. * - * Authors: Alan Cox . (datagram_poll() from old + * Authors: Alan Cox . (datagram_poll() from old * udp.c code) * * Fixes: diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 5402b3b38e0d..9e2fa39f22a3 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -6,7 +6,7 @@ * Richard Underwood * * Stir fried together from the IP multicast and CAP patches above - * Alan Cox + * Alan Cox * * Fixes: * Alan Cox : Update the device on a real delete diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7f7bb1a636d9..4e22e3a35359 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1,7 +1,7 @@ /* * Routines having to do with the 'struct sk_buff' memory handlers. * - * Authors: Alan Cox + * Authors: Alan Cox * Florian La Roche * * Fixes: diff --git a/net/core/stream.c b/net/core/stream.c index a6b3437ff082..8727cead64ad 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -9,7 +9,7 @@ * * Authors: Arnaldo Carvalho de Melo * (from old tcp.c code) - * Alan Cox (Borrowed comments 8-)) + * Alan Cox (Borrowed comments 8-)) */ #include diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 55c355e63234..72b2de76f1cd 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -1,7 +1,7 @@ /* * NET3: Implementation of the ICMP protocol layer. * - * Alan Cox, + * Alan Cox, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 7f9e337e3908..a0d86455c53e 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -9,7 +9,7 @@ * seems to fall out with gcc 2.6.2. * * Authors: - * Alan Cox + * Alan Cox * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 2152d222b954..e4f81f54befe 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -6,7 +6,7 @@ * The IP fragmentation functionality. * * Authors: Fred N. van Kempen - * Alan Cox + * Alan Cox * * Fixes: * Alan Cox : Split from ip.c , see ip_input.c for history. diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index e0bed56c51f1..861978a4f1a8 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -8,7 +8,7 @@ * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, - * Alan Cox, + * Alan Cox, * Richard Underwood * Stefan Becker, * Jorge Cwik, diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 4c6d2caf9203..29609d29df76 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -41,7 +41,7 @@ Made the tunnels use dev->name not tunnel: when error reporting. Added tx_dropped stat - -Alan Cox (Alan.Cox@linux.org) 21 March 95 + -Alan Cox (alan@lxorguk.ukuu.org.uk) 21 March 95 Reworked: Changed to tunnel to destination gateway in addition to the diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index c519b8d30eee..b42e082cc170 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1,7 +1,7 @@ /* * IP multicast routing support for mrouted 3.6/3.8 * - * (c) 1995 Alan Cox, + * (c) 1995 Alan Cox, * Linux Consultancy and Custom Driver Development * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index eacf4cfef146..2095abc3caba 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -8,7 +8,7 @@ * Authors: Ross Biro * Fred N. van Kempen, * Arnt Gulbrandsen, - * Alan Cox, + * Alan Cox, * Hirokazu Takahashi, * * Fixes: diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b0eacc0007cc..2fd8afac5f71 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1,7 +1,7 @@ /* * NETLINK Kernel-user communication protocol. * - * Authors: Alan Cox + * Authors: Alan Cox * Alexey Kuznetsov * * This program is free software; you can redistribute it and/or diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 4486c59c3aca..9a288d5eea64 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -3,8 +3,8 @@ * * Client-side transport implementation for sockets. * - * TCP callback races fixes (C) 1998 Red Hat Software - * TCP send fixes (C) 1998 Red Hat Software + * TCP callback races fixes (C) 1998 Red Hat + * TCP send fixes (C) 1998 Red Hat * TCP NFS related read + write fixes * (C) 1999 Dave Airlie, University of Limerick, Ireland * diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 015606b54d9b..c647aab8d418 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1,7 +1,7 @@ /* * NET4: Implementation of BSD Unix domain sockets. * - * Authors: Alan Cox, + * Authors: Alan Cox, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License -- cgit v1.2.3 From 93f78da405685a756beeaeae4b5e41fcec39eab3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 14 Oct 2008 12:12:02 -0700 Subject: Revert "vt: fix background color on line feed" This reverts commit c9e587abfdec2c2aaa55fab83bcb4972e2f84f9b, and the subsequent commits that fixed it up: - afa9b649 "fbcon: prevent cursor disappearance after switching to 512 character font" - d850a2fa "vt/fbcon: fix background color on line feed" - 7fe3915a "vt/fbcon: update scrl_erase_char after 256/512-glyph font switch" by request of Alan Cox. Quoth Alan: "Unfortunately it's wrong and its been causing breakages because various apps like ncurses expect our previous (and correct) behaviour." Alexander sent out a similar patch. Requested-by: Alan Cox Tested-by: Jan Engelhardt Cc: Alexander V. Lukyanov Signed-off-by: Linus Torvalds --- drivers/char/vt.c | 5 ++--- drivers/video/console/fbcon.c | 39 ++++++++++----------------------------- drivers/video/console/mdacon.c | 2 +- drivers/video/console/sticon.c | 4 ++-- drivers/video/console/vgacon.c | 4 ++-- include/linux/console_struct.h | 1 - 6 files changed, 17 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 57029fefd64a..a0f7ffb68087 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -301,7 +301,7 @@ static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_scrl_erase_char, + scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char, vc->vc_size_row * nr); } @@ -319,7 +319,7 @@ static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr) s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); step = vc->vc_cols * nr; scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(s, vc->vc_scrl_erase_char, 2 * step); + scr_memsetw(s, vc->vc_video_erase_char, 2 * step); } static void do_update_region(struct vc_data *vc, unsigned long start, int count) @@ -434,7 +434,6 @@ static void update_attr(struct vc_data *vc) vc->vc_blink, vc->vc_underline, vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic); vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' '; - vc->vc_scrl_erase_char = (build_attr(vc, vc->vc_def_color, 1, false, false, vc->vc_decscnm, false) << 8) | ' '; } /* Note: inverting the screen twice should revert to the original state */ diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 9cbff84b787d..da91bb16da8a 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1855,8 +1855,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; - unsigned short saved_ec; - int ret; if (fbcon_is_inactive(vc, info)) return -EINVAL; @@ -1869,11 +1867,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, * whole screen (prevents flicker). */ - saved_ec = vc->vc_video_erase_char; - vc->vc_video_erase_char = vc->vc_scrl_erase_char; - - ret = 0; - switch (dir) { case SM_UP: if (count > vc->vc_rows) /* Maximum realistic size */ @@ -1890,9 +1883,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; + return 1; break; case SCROLL_WRAP_MOVE: @@ -1962,10 +1955,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; - break; + return 1; } break; @@ -1982,9 +1974,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; + return 1; break; case SCROLL_WRAP_MOVE: @@ -2052,15 +2044,12 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; - break; + return 1; } - break; } - vc->vc_video_erase_char = saved_ec; - return ret; + return 0; } @@ -2522,9 +2511,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, c = vc->vc_video_erase_char; vc->vc_video_erase_char = ((c & 0xfe00) >> 1) | (c & 0xff); - c = vc->vc_scrl_erase_char; - vc->vc_scrl_erase_char = - ((c & 0xFE00) >> 1) | (c & 0xFF); vc->vc_attr >>= 1; } } else if (!vc->vc_hi_font_mask && cnt == 512) { @@ -2555,14 +2541,9 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, if (vc->vc_can_do_color) { vc->vc_video_erase_char = ((c & 0xff00) << 1) | (c & 0xff); - c = vc->vc_scrl_erase_char; - vc->vc_scrl_erase_char = - ((c & 0xFF00) << 1) | (c & 0xFF); vc->vc_attr <<= 1; - } else { + } else vc->vc_video_erase_char = c & ~0x100; - vc->vc_scrl_erase_char = c & ~0x100; - } } } diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 9901064199bd..dd3eaaad4441 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -533,7 +533,7 @@ static void mdacon_cursor(struct vc_data *c, int mode) static int mdacon_scroll(struct vc_data *c, int t, int b, int dir, int lines) { - u16 eattr = mda_convert_attr(c->vc_scrl_erase_char); + u16 eattr = mda_convert_attr(c->vc_video_erase_char); if (!lines) return 0; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 4055dbdd1b42..491c1c1baf4c 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -170,12 +170,12 @@ static int sticon_scroll(struct vc_data *conp, int t, int b, int dir, int count) switch (dir) { case SM_UP: sti_bmove(sti, t + count, 0, t, 0, b - t - count, conp->vc_cols); - sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_scrl_erase_char); + sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_video_erase_char); break; case SM_DOWN: sti_bmove(sti, t, 0, t + count, 0, b - t - count, conp->vc_cols); - sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_scrl_erase_char); + sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_video_erase_char); break; } diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index bd1f57b259d9..6df29a62d720 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1350,7 +1350,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, } else c->vc_origin += delta; scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size - - delta), c->vc_scrl_erase_char, + delta), c->vc_video_erase_char, delta); } else { if (oldo - delta < vga_vram_base) { @@ -1363,7 +1363,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, } else c->vc_origin -= delta; c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; - scr_memsetw((u16 *) (c->vc_origin), c->vc_scrl_erase_char, + scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char, delta); } c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index b03f80a078be..d71f7c0f931b 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -53,7 +53,6 @@ struct vc_data { unsigned short vc_hi_font_mask; /* [#] Attribute set for upper 256 chars of font or 0 if not supported */ struct console_font vc_font; /* Current VC font set */ unsigned short vc_video_erase_char; /* Background erase character */ - unsigned short vc_scrl_erase_char; /* Erase character for scroll */ /* VT terminal data */ unsigned int vc_state; /* Escape sequence parser state */ unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */ -- cgit v1.2.3 From e8c84f9a5f06912c94c38961096c994da3890a2e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 19 May 2008 15:50:01 +0200 Subject: modpost: add support for hid Generate aliases for hid device modules to support autoloading. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- include/linux/hid.h | 1 + include/linux/mod_devicetable.h | 10 ++++++++++ scripts/mod/file2alias.c | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) (limited to 'include/linux') diff --git a/include/linux/hid.h b/include/linux/hid.h index ac4e678a04ed..b7a17762a0b2 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -67,6 +67,7 @@ #include #include #include +#include /* hid_device_id */ #include #include #include diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 3481a7d5bc0a..d6a3f47e95cb 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -131,6 +131,16 @@ struct usb_device_id { #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 +#define HID_ANY_ID (~0) + +struct hid_device_id { + __u16 bus; + __u32 vendor; + __u32 product; + kernel_ulong_t driver_data + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; + /* s390 CCW devices */ struct ccw_device_id { __u16 match_flags; /* which fields to match against */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 473f94e56ead..d4dc222a74f3 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -206,6 +206,20 @@ static void do_usb_table(void *symval, unsigned long size, do_usb_entry_multi(symval + i, mod); } +/* Looks like: hid:bNvNpN */ +static int do_hid_entry(const char *filename, + struct hid_device_id *id, char *alias) +{ + id->vendor = TO_NATIVE(id->vendor); + id->product = TO_NATIVE(id->product); + + sprintf(alias, "hid:b%04X", id->bus); + ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor); + ADD(alias, "p", id->product != HID_ANY_ID, id->product); + + return 1; +} + /* Looks like: ieee1394:venNmoNspNverN */ static int do_ieee1394_entry(const char *filename, struct ieee1394_device_id *id, char *alias) @@ -745,6 +759,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, else if (sym_is(symname, "__mod_usb_device_table")) /* special case to handle bcdDevice ranges */ do_usb_table(symval, sym->st_size, mod); + else if (sym_is(symname, "__mod_hid_device_table")) + do_table(symval, sym->st_size, + sizeof(struct hid_device_id), "hid", + do_hid_entry, mod); else if (sym_is(symname, "__mod_ieee1394_device_table")) do_table(symval, sym->st_size, sizeof(struct ieee1394_device_id), "ieee1394", -- cgit v1.2.3 From 85cdaf524b7ddab627e7d15405693f2511ef7505 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 16 May 2008 11:49:15 +0200 Subject: HID: make a bus from hid code Make a bus from hid core. This is the first step for converting all the quirks and separate almost-drivers into real drivers attached to this bus. It's implemented to change behaviour in very tiny manner, so that no driver needs to be changed this time. Also add generic drivers for both usb and bt into usbhid or hidp respectively which will bind all non-blacklisted device. Those blacklisted will be either grabbed by special drivers or by nobody if they are broken at the very rude base. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 474 +++++++++++++++++++++++++++++++++++------- drivers/hid/hid-input.c | 2 +- drivers/hid/usbhid/hid-core.c | 44 +++- drivers/hid/usbhid/usbhid.h | 2 +- include/linux/hid.h | 104 ++++++++- net/bluetooth/hidp/core.c | 64 +++++- 6 files changed, 586 insertions(+), 104 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 426ac5add585..017fc20167d4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -534,9 +534,10 @@ static void hid_free_report(struct hid_report *report) * Free a device structure, all reports, and all fields. */ -void hid_free_device(struct hid_device *device) +static void hid_device_release(struct device *dev) { - unsigned i,j; + struct hid_device *device = container_of(dev, struct hid_device, dev); + unsigned i, j; for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; @@ -552,7 +553,6 @@ void hid_free_device(struct hid_device *device) kfree(device->collection); kfree(device); } -EXPORT_SYMBOL_GPL(hid_free_device); /* * Fetch a report description item from the data stream. We support long @@ -622,18 +622,24 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) return NULL; } -/* +/** + * hid_parse_report - parse device report + * + * @device: hid device + * @start: report start + * @size: report size + * * Parse a report description into a hid_device structure. Reports are * enumerated, fields are attached to these reports. + * 0 returned on success, otherwise nonzero error value. */ - -struct hid_device *hid_parse_report(__u8 *start, unsigned size) +int hid_parse_report(struct hid_device *device, __u8 *start, + unsigned size) { - struct hid_device *device; struct hid_parser *parser; struct hid_item item; __u8 *end; - unsigned i; + int ret; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_parser_main, @@ -642,76 +648,54 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) hid_parser_reserved }; - if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL))) - return NULL; - - if (!(device->collection = kzalloc(sizeof(struct hid_collection) * - HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) { - kfree(device); - return NULL; - } - device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; - - for (i = 0; i < HID_REPORT_TYPES; i++) - INIT_LIST_HEAD(&device->report_enum[i].report_list); - - if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) { - kfree(device->collection); - kfree(device); - return NULL; - } + device->rdesc = kmalloc(size, GFP_KERNEL); + if (device->rdesc == NULL) + return -ENOMEM; memcpy(device->rdesc, start, size); device->rsize = size; - if (!(parser = vmalloc(sizeof(struct hid_parser)))) { - kfree(device->rdesc); - kfree(device->collection); - kfree(device); - return NULL; + parser = vmalloc(sizeof(struct hid_parser)); + if (!parser) { + ret = -ENOMEM; + goto err; } + memset(parser, 0, sizeof(struct hid_parser)); parser->device = device; end = start + size; + ret = -EINVAL; while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { dbg_hid("unexpected long global item\n"); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } if (dispatch_type[item.type](parser, &item)) { dbg_hid("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } if (start == end) { if (parser->collection_stack_ptr) { dbg_hid("unbalanced collection at end of report description\n"); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } if (parser->local.delimiter_depth) { dbg_hid("unbalanced delimiter at end of report description\n"); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } vfree(parser); - return device; + return 0; } } dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); - hid_free_device(device); +err: vfree(parser); - return NULL; + return ret; } EXPORT_SYMBOL_GPL(hid_parse_report); @@ -815,9 +799,73 @@ static __inline__ int search(__s32 *array, __s32 value, unsigned n) return -1; } -static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt) +/** + * hid_match_report - check if driver's raw_event should be called + * + * @hid: hid device + * @report_type: type to match against + * + * compare hid->driver->report_table->report_type to report->type + */ +static int hid_match_report(struct hid_device *hid, struct hid_report *report) { + const struct hid_report_id *id = hid->driver->report_table; + + if (!id) /* NULL means all */ + return 1; + + for (; id->report_type != HID_TERMINATOR; id++) + if (id->report_type == HID_ANY_ID || + id->report_type == report->type) + return 1; + return 0; +} + +/** + * hid_match_usage - check if driver's event should be called + * + * @hid: hid device + * @usage: usage to match against + * + * compare hid->driver->usage_table->usage_{type,code} to + * usage->usage_{type,code} + */ +static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage) +{ + const struct hid_usage_id *id = hid->driver->usage_table; + + if (!id) /* NULL means all */ + return 1; + + for (; id->usage_type != HID_ANY_ID - 1; id++) + if ((id->usage_hid == HID_ANY_ID || + id->usage_hid == usage->hid) && + (id->usage_type == HID_ANY_ID || + id->usage_type == usage->type) && + (id->usage_code == HID_ANY_ID || + id->usage_code == usage->code)) + return 1; + return 0; +} + +static void hid_process_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value, int interrupt) +{ + struct hid_driver *hdrv = hid->driver; + int ret; + hid_dump_input(usage, value); + + if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { + ret = hdrv->event(hid, field, usage, value); + if (ret != 0) { + if (ret < 0) + dbg_hid("%s's event failed with %d\n", + hdrv->name, ret); + return; + } + } + if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value); if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) @@ -946,44 +994,47 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) } EXPORT_SYMBOL_GPL(hid_set_field); -int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) +static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, + const u8 *data) { - struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; - int n, rsize, i; + unsigned int n = 0; /* Normally report number is 0 */ - if (!hid) - return -ENODEV; + /* Device uses numbered reports, data[0] is report number */ + if (report_enum->numbered) + n = *data; - if (!size) { - dbg_hid("empty report\n"); - return -1; - } + report = report_enum->report_id_hash[n]; + if (report == NULL) + dbg_hid("undefined report_id %u received\n", n); - dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); + return report; +} - n = 0; /* Normally report number is 0 */ - if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ - n = *data++; - size--; - } +void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, + int interrupt) +{ + struct hid_report_enum *report_enum = hid->report_enum + type; + struct hid_report *report; + unsigned int a; + int rsize, csize = size; + u8 *cdata = data; - /* dump the report */ - dbg_hid("report %d (size %u) = ", n, size); - for (i = 0; i < size; i++) - dbg_hid_line(" %02x", data[i]); - dbg_hid_line("\n"); + report = hid_get_report(report_enum, data); + if (!report) + return; - if (!(report = report_enum->report_id_hash[n])) { - dbg_hid("undefined report_id %d received\n", n); - return -1; + if (report_enum->numbered) { + cdata++; + csize--; } rsize = ((report->size - 1) >> 3) + 1; - if (size < rsize) { - dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize); - memset(data + size, 0, rsize - size); + if (csize < rsize) { + dbg_hid("report %d is too short, (%d < %d)\n", report->id, + csize, rsize); + memset(cdata + csize, 0, rsize - csize); } if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) @@ -996,24 +1047,295 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i hidraw_report_event(hid, data, size); } - for (n = 0; n < report->maxfield; n++) - hid_input_field(hid, report->field[n], data, interrupt); + for (a = 0; a < report->maxfield; a++) + hid_input_field(hid, report->field[a], cdata, interrupt); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); +} +EXPORT_SYMBOL_GPL(hid_report_raw_event); + +/** + * hid_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @size: size of data parameter + * @interrupt: called from atomic? + * + * This is data entry for lower layers. + */ +int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) +{ + struct hid_report_enum *report_enum = hid->report_enum + type; + struct hid_driver *hdrv = hid->driver; + struct hid_report *report; + unsigned int i; + int ret; + + if (!hid || !hid->driver) + return -ENODEV; + + if (!size) { + dbg_hid("empty report\n"); + return -1; + } + + dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); + + report = hid_get_report(report_enum, data); + if (!report) + return -1; + + /* dump the report */ + dbg_hid("report %d (size %u) = ", report->id, size); + for (i = 0; i < size; i++) + dbg_hid_line(" %02x", data[i]); + dbg_hid_line("\n"); + + if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { + ret = hdrv->raw_event(hid, report, data, size); + if (ret != 0) + return ret < 0 ? ret : 0; + } + + hid_report_raw_event(hid, type, data, size, interrupt); return 0; } EXPORT_SYMBOL_GPL(hid_input_report); +static bool hid_match_one_id(struct hid_device *hdev, + const struct hid_device_id *id) +{ + return id->bus == hdev->bus && + (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && + (id->product == HID_ANY_ID || id->product == hdev->product); +} + +static const struct hid_device_id *hid_match_id(struct hid_device *hdev, + const struct hid_device_id *id) +{ + for (; id->bus; id++) + if (hid_match_one_id(hdev, id)) + return id; + + return NULL; +} + +static const struct hid_device_id hid_blacklist[] = { + { } +}; + +static int hid_bus_match(struct device *dev, struct device_driver *drv) +{ + struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + if (!hid_match_id(hdev, hdrv->id_table)) + return 0; + + /* generic wants all non-blacklisted */ + if (!strncmp(hdrv->name, "generic-", 8)) + return !hid_match_id(hdev, hid_blacklist); + + return 1; +} + +static int hid_device_probe(struct device *dev) +{ + struct hid_driver *hdrv = container_of(dev->driver, + struct hid_driver, driver); + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + const struct hid_device_id *id; + int ret = 0; + + if (!hdev->driver) { + if (hdrv->probe) { + ret = -ENODEV; + + id = hid_match_id(hdev, hdrv->id_table); + if (id) + ret = hdrv->probe(hdev, id); + } + if (!ret) + hdev->driver = hdrv; + } + return ret; +} + +static int hid_device_remove(struct device *dev) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct hid_driver *hdrv = hdev->driver; + + if (hdrv) { + if (hdrv->remove) + hdrv->remove(hdev); + hdev->driver = NULL; + } + + return 0; +} + +static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X", + hdev->bus, hdev->vendor, hdev->product)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_NAME=%s", hdev->name)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq)) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X", + hdev->bus, hdev->vendor, hdev->product)) + return -ENOMEM; + + return 0; +} + +static struct bus_type hid_bus_type = { + .name = "hid", + .match = hid_bus_match, + .probe = hid_device_probe, + .remove = hid_device_remove, + .uevent = hid_uevent, +}; + +int hid_add_device(struct hid_device *hdev) +{ + static atomic_t id = ATOMIC_INIT(0); + int ret; + + if (WARN_ON(hdev->status & HID_STAT_ADDED)) + return -EBUSY; + + /* XXX hack, any other cleaner solution < 20 bus_id bytes? */ + sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus, + hdev->vendor, hdev->product, atomic_inc_return(&id)); + + ret = device_add(&hdev->dev); + if (!ret) + hdev->status |= HID_STAT_ADDED; + + return ret; +} +EXPORT_SYMBOL_GPL(hid_add_device); + +/** + * hid_allocate_device - allocate new hid device descriptor + * + * Allocate and initialize hid device, so that hid_destroy_device might be + * used to free it. + * + * New hid_device pointer is returned on success, otherwise ERR_PTR encoded + * error value. + */ +struct hid_device *hid_allocate_device(void) +{ + struct hid_device *hdev; + unsigned int i; + int ret = -ENOMEM; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (hdev == NULL) + return ERR_PTR(ret); + + device_initialize(&hdev->dev); + hdev->dev.release = hid_device_release; + hdev->dev.bus = &hid_bus_type; + + hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, + sizeof(struct hid_collection), GFP_KERNEL); + if (hdev->collection == NULL) + goto err; + hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + + for (i = 0; i < HID_REPORT_TYPES; i++) + INIT_LIST_HEAD(&hdev->report_enum[i].report_list); + + return hdev; +err: + put_device(&hdev->dev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(hid_allocate_device); + +static void hid_remove_device(struct hid_device *hdev) +{ + if (hdev->status & HID_STAT_ADDED) { + device_del(&hdev->dev); + hdev->status &= ~HID_STAT_ADDED; + } +} + +/** + * hid_destroy_device - free previously allocated device + * + * @hdev: hid device + * + * If you allocate hid_device through hid_allocate_device, you should ever + * free by this function. + */ +void hid_destroy_device(struct hid_device *hdev) +{ + hid_remove_device(hdev); + put_device(&hdev->dev); +} +EXPORT_SYMBOL_GPL(hid_destroy_device); + +int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, + const char *mod_name) +{ + hdrv->driver.name = hdrv->name; + hdrv->driver.bus = &hid_bus_type; + hdrv->driver.owner = owner; + hdrv->driver.mod_name = mod_name; + + return driver_register(&hdrv->driver); +} +EXPORT_SYMBOL_GPL(__hid_register_driver); + +void hid_unregister_driver(struct hid_driver *hdrv) +{ + driver_unregister(&hdrv->driver); +} +EXPORT_SYMBOL_GPL(hid_unregister_driver); + static int __init hid_init(void) { - return hidraw_init(); + int ret; + + ret = bus_register(&hid_bus_type); + if (ret) { + printk(KERN_ERR "HID: can't register hid bus\n"); + goto err; + } + + ret = hidraw_init(); + if (ret) + goto err_bus; + + return 0; +err_bus: + bus_unregister(&hid_bus_type); +err: + return ret; } static void __exit hid_exit(void) { hidraw_exit(); + bus_unregister(&hid_bus_type); } module_init(hid_init); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1b2e8dc3398d..4ae5603804e7 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1032,7 +1032,7 @@ int hidinput_connect(struct hid_device *hid) input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; - input_dev->dev.parent = hid->dev; + input_dev->dev.parent = hid->dev.parent; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 27fe4d8912cb..5955d05ae542 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -770,8 +770,15 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) dbg_hid_line(" %02x", (unsigned char) rdesc[n]); dbg_hid_line("\n"); - if (!(hid = hid_parse_report(rdesc, n))) { + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + kfree(rdesc); + return NULL; + } + + if (hid_parse_report(hid, rdesc, n)) { dbg_hid("parsing report descriptor failed\n"); + hid_destroy_device(hid); kfree(rdesc); return NULL; } @@ -798,10 +805,8 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (insize > HID_MAX_BUFFER_SIZE) insize = HID_MAX_BUFFER_SIZE; - if (hid_alloc_buffers(dev, hid)) { - hid_free_buffers(dev, hid); + if (hid_alloc_buffers(dev, hid)) goto fail; - } hid->name[0] = 0; @@ -881,7 +886,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) hid->version = le16_to_cpu(hdesc->bcdHID); hid->country = hdesc->bCountryCode; - hid->dev = &intf->dev; + hid->dev.parent = &intf->dev; usbhid->intf = intf; usbhid->ifnum = interface->desc.bInterfaceNumber; @@ -925,7 +930,7 @@ fail: hid_free_buffers(dev, hid); kfree(usbhid); fail_no_usbhid: - hid_free_device(hid); + hid_destroy_device(hid); return NULL; } @@ -964,14 +969,14 @@ static void hid_disconnect(struct usb_interface *intf) hid_free_buffers(hid_to_usb_dev(hid), hid); kfree(usbhid); - hid_free_device(hid); + hid_destroy_device(hid); } static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct hid_device *hid; char path[64]; - int i; + int i, ret; char *c; dbg_hid("HID probe called for ifnum %d\n", @@ -1037,7 +1042,12 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) printk(": USB HID v%x.%02x %s [%s] on %s\n", hid->version >> 8, hid->version & 0xff, c, hid->name, path); - return 0; + ret = hid_add_device(hid); + if (ret) { + dev_err(&intf->dev, "can't add hid device: %d\n", ret); + hid_disconnect(intf); + } + return ret; } static int hid_suspend(struct usb_interface *intf, pm_message_t message) @@ -1107,9 +1117,22 @@ static struct usb_driver hid_driver = { .supports_autosuspend = 1, }; +static const struct hid_device_id hid_usb_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver hid_usb_driver = { + .name = "generic-usb", + .id_table = hid_usb_table, +}; + static int __init hid_init(void) { int retval; + retval = hid_register_driver(&hid_usb_driver); + if (retval) + goto hid_register_fail; retval = usbhid_quirks_init(quirks_param); if (retval) goto usbhid_quirks_init_fail; @@ -1127,6 +1150,8 @@ usb_register_fail: hiddev_init_fail: usbhid_quirks_exit(); usbhid_quirks_init_fail: + hid_unregister_driver(&hid_usb_driver); +hid_register_fail: return retval; } @@ -1135,6 +1160,7 @@ static void __exit hid_exit(void) usb_deregister(&hid_driver); hiddev_exit(); usbhid_quirks_exit(); + hid_unregister_driver(&hid_usb_driver); } module_init(hid_init); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 62d2d7c925bd..b47f991867e9 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -82,7 +82,7 @@ struct usbhid_device { }; #define hid_to_usb_dev(hid_dev) \ - container_of(hid_dev->dev->parent, struct usb_device, dev) + container_of(hid_dev->dev.parent->parent, struct usb_device, dev) #endif diff --git a/include/linux/hid.h b/include/linux/hid.h index b7a17762a0b2..c4bea0eda85b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -418,6 +418,8 @@ struct hid_control_fifo { #define HID_CLAIMED_HIDDEV 2 #define HID_CLAIMED_HIDRAW 4 +#define HID_STAT_ADDED 1 + #define HID_CTRL_RUNNING 1 #define HID_OUT_RUNNING 2 #define HID_IN_RUNNING 3 @@ -432,22 +434,26 @@ struct hid_input { struct input_dev *input; }; +struct hid_driver; + struct hid_device { /* device report descriptor */ - __u8 *rdesc; + __u8 *rdesc; unsigned rsize; struct hid_collection *collection; /* List of HID collections */ unsigned collection_size; /* Number of allocated hid_collections */ unsigned maxcollection; /* Number of parsed collections */ unsigned maxapplication; /* Number of applications */ - unsigned short bus; /* BUS ID */ - unsigned short vendor; /* Vendor ID */ - unsigned short product; /* Product ID */ - unsigned version; /* HID version */ + __u16 bus; /* BUS ID */ + __u32 vendor; /* Vendor ID */ + __u32 product; /* Product ID */ + __u32 version; /* HID version */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; - struct device *dev; /* device */ + struct device dev; /* device */ + struct hid_driver *driver; + unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ @@ -483,6 +489,16 @@ struct hid_device { /* device report descriptor */ #endif }; +static inline void *hid_get_drvdata(struct hid_device *hdev) +{ + return dev_get_drvdata(&hdev->dev); +} + +static inline void hid_set_drvdata(struct hid_device *hdev, void *data) +{ + dev_set_drvdata(&hdev->dev, data); +} + #define HID_GLOBAL_STACK_SIZE 4 #define HID_COLLECTION_STACK_SIZE 4 @@ -511,6 +527,61 @@ struct hid_descriptor { struct hid_class_descriptor desc[1]; } __attribute__ ((packed)); +#define HID_DEVICE(b, ven, prod) \ + .bus = (b), \ + .vendor = (ven), .product = (prod) + +#define HID_USB_DEVICE(ven, prod) HID_DEVICE(BUS_USB, ven, prod) +#define HID_BLUETOOTH_DEVICE(ven, prod) HID_DEVICE(BUS_BLUETOOTH, ven, prod) + +#define HID_REPORT_ID(rep) \ + .report_type = (rep) +#define HID_USAGE_ID(uhid, utype, ucode) \ + .usage_hid = (uhid), .usage_type = (utype), .usage_code = (ucode) +/* we don't want to catch types and codes equal to 0 */ +#define HID_TERMINATOR (HID_ANY_ID - 1) + +struct hid_report_id { + __u32 report_type; +}; +struct hid_usage_id { + __u32 usage_hid; + __u32 usage_type; + __u32 usage_code; +}; + +/** + * struct hid_driver + * @name: driver name (e.g. "Footech_bar-wheel") + * @id_table: which devices is this driver for (must be non-NULL for probe + * to be called) + * @probe: new device inserted + * @remove: device removed (NULL if not a hot-plug capable driver) + * @report_table: on which reports to call raw_event (NULL means all) + * @raw_event: if report in report_table, this hook is called (NULL means nop) + * @usage_table: on which events to call event (NULL means all) + * @event: if usage in usage_table, this hook is called (NULL means nop) + * + * raw_event and event should return 0 on no action performed, 1 when no + * further processing should be done and negative on error + */ +struct hid_driver { + char *name; + const struct hid_device_id *id_table; + + int (*probe)(struct hid_device *dev, const struct hid_device_id *id); + void (*remove)(struct hid_device *dev); + + const struct hid_report_id *report_table; + int (*raw_event)(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size); + const struct hid_usage_id *usage_table; + int (*event)(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value); +/* private: */ + struct device_driver driver; +}; + /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) @@ -521,6 +592,17 @@ struct hid_descriptor { extern int hid_debug; #endif +extern int hid_add_device(struct hid_device *); +extern void hid_destroy_device(struct hid_device *); + +extern int __must_check __hid_register_driver(struct hid_driver *, + struct module *, const char *mod_name); +static inline int __must_check hid_register_driver(struct hid_driver *driver) +{ + return __hid_register_driver(driver, THIS_MODULE, KBUILD_MODNAME); +} +extern void hid_unregister_driver(struct hid_driver *); + extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); extern int hidinput_connect(struct hid_device *); @@ -533,8 +615,14 @@ int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned lon int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32); void hid_output_report(struct hid_report *report, __u8 *data); -void hid_free_device(struct hid_device *device); -struct hid_device *hid_parse_report(__u8 *start, unsigned size); +struct hid_device *hid_allocate_device(void); +int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); + +void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, + int interrupt); + +extern int hid_generic_init(void); +extern void hid_generic_exit(void); /* HID quirks API */ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 96434d774c84..56a51f91591a 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -578,7 +578,7 @@ static int hidp_session(void *arg) if (session->hid) { if (session->hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(session->hid); - hid_free_device(session->hid); + hid_destroy_device(session->hid); } /* Wakeup user-space polling for socket errors */ @@ -698,12 +698,13 @@ static void hidp_setup_quirks(struct hid_device *hid) hid->quirks = hidp_blacklist[n].quirks; } -static void hidp_setup_hid(struct hidp_session *session, +static int hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { struct hid_device *hid = session->hid; struct hid_report *report; bdaddr_t src, dst; + int ret; baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); @@ -721,7 +722,7 @@ static void hidp_setup_hid(struct hidp_session *session, strncpy(hid->phys, batostr(&src), 64); strncpy(hid->uniq, batostr(&dst), 64); - hid->dev = hidp_get_device(session); + hid->dev.parent = hidp_get_device(session); hid->hid_open = hidp_open; hid->hid_close = hidp_close; @@ -738,6 +739,15 @@ static void hidp_setup_hid(struct hidp_session *session, if (hidinput_connect(hid) == 0) hid->claimed |= HID_CLAIMED_INPUT; + + ret = hid_add_device(hid); + if (ret) { + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + skb_queue_purge(&session->intr_transmit); + } + + return ret; } int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) @@ -771,11 +781,19 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, return -EFAULT; } - session->hid = hid_parse_report(buf, req->rd_size); + session->hid = hid_allocate_device(); + if (IS_ERR(session->hid)) { + kfree(buf); + kfree(session); + return PTR_ERR(session->hid); + } + + err = hid_parse_report(session->hid, buf, req->rd_size); kfree(buf); - if (!session->hid) { + if (err) { + hid_destroy_device(session->hid); kfree(session); return -EINVAL; } @@ -822,8 +840,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, goto failed; } - if (session->hid) - hidp_setup_hid(session, req); + if (session->hid) { + err = hidp_setup_hid(session, req); + if (err) + goto failed; + } __hidp_link_session(session); @@ -859,7 +880,7 @@ failed: up_write(&hidp_session_sem); if (session->hid) - hid_free_device(session->hid); + hid_destroy_device(session->hid); input_free_device(session->input); kfree(session); @@ -950,18 +971,43 @@ int hidp_get_conninfo(struct hidp_conninfo *ci) return err; } +static const struct hid_device_id hidp_table[] = { + { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver hidp_driver = { + .name = "generic-bluetooth", + .id_table = hidp_table, +}; + static int __init hidp_init(void) { + int ret; + l2cap_load(); BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); - return hidp_init_sockets(); + ret = hid_register_driver(&hidp_driver); + if (ret) + goto err; + + ret = hidp_init_sockets(); + if (ret) + goto err_drv; + + return 0; +err_drv: + hid_unregister_driver(&hidp_driver); +err: + return ret; } static void __exit hidp_exit(void) { hidp_cleanup_sockets(); + hid_unregister_driver(&hidp_driver); } module_init(hidp_init); -- cgit v1.2.3 From c500c9714011edab021591340042787722db9cf0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 16 May 2008 11:49:16 +0200 Subject: HID: hid, make parsing event driven Next step for complete hid bus, this patch includes: - call parser either from probe or from hid-core if there is no probe. - add ll_driver structure and centralize some stuff there (open, close...) - split and merge usb_hid_configure and hid_probe into several functions to allow hooks/fixes between them Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 24 ++- drivers/hid/hid-input.c | 20 ++- drivers/hid/hidraw.c | 6 +- drivers/hid/usbhid/hid-core.c | 330 ++++++++++++++++++++++++------------------ include/linux/hid.h | 104 ++++++++++++- net/bluetooth/hidp/core.c | 191 +++++++++++++----------- net/bluetooth/hidp/hidp.h | 2 + 7 files changed, 438 insertions(+), 239 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 017fc20167d4..3dacbcd7e41c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -648,6 +648,9 @@ int hid_parse_report(struct hid_device *device, __u8 *start, hid_parser_reserved }; + if (device->driver->report_fixup) + device->driver->report_fixup(device, start, size); + device->rdesc = kmalloc(size, GFP_KERNEL); if (device->rdesc == NULL) return -ENOMEM; @@ -1152,15 +1155,20 @@ static int hid_device_probe(struct device *dev) int ret = 0; if (!hdev->driver) { - if (hdrv->probe) { - ret = -ENODEV; + id = hid_match_id(hdev, hdrv->id_table); + if (id == NULL) + return -ENODEV; - id = hid_match_id(hdev, hdrv->id_table); - if (id) - ret = hdrv->probe(hdev, id); + hdev->driver = hdrv; + if (hdrv->probe) { + ret = hdrv->probe(hdev, id); + } else { /* default probe */ + ret = hid_parse(hdev); + if (!ret) + ret = hid_hw_start(hdev); } - if (!ret) - hdev->driver = hdrv; + if (ret) + hdev->driver = NULL; } return ret; } @@ -1173,6 +1181,8 @@ static int hid_device_remove(struct device *dev) if (hdrv) { if (hdrv->remove) hdrv->remove(hdev); + else /* default remove */ + hid_hw_stop(hdev); hdev->driver = NULL; } diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 4ae5603804e7..9fa7239ab310 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -390,6 +390,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (ret) goto mapped; + if (device->driver->input_mapping) { + int ret = device->driver->input_mapping(device, hidinput, field, + usage, &bit, &max); + if (ret > 0) + goto mapped; + if (ret < 0) + goto ignore; + } + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_UNDEFINED: @@ -755,6 +764,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } mapped: + if (device->driver->input_mapped && device->driver->input_mapped(device, + hidinput, field, usage, &bit, &max) < 0) + goto ignore; + if (device->quirks & HID_QUIRK_MIGHTYMOUSE) { if (usage->hid == HID_GD_Z) map_rel(REL_HWHEEL); @@ -961,14 +974,14 @@ static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); - return hid->hid_open(hid); + return hid->ll_driver->open(hid); } static void hidinput_close(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); - hid->hid_close(hid); + hid->ll_driver->close(hid); } /* @@ -1019,7 +1032,8 @@ int hidinput_connect(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - input_dev->event = hid->hidinput_input_event; + input_dev->event = + hid->ll_driver->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index c40f0403edaf..4be240e74d4f 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -181,7 +181,7 @@ static int hidraw_open(struct inode *inode, struct file *file) dev = hidraw_table[minor]; if (!dev->open++) - dev->hid->hid_open(dev->hid); + dev->hid->ll_driver->open(dev->hid); out_unlock: spin_unlock(&minors_lock); @@ -207,7 +207,7 @@ static int hidraw_release(struct inode * inode, struct file * file) dev = hidraw_table[minor]; if (!dev->open--) { if (list->hidraw->exist) - dev->hid->hid_close(dev->hid); + dev->hid->ll_driver->close(dev->hid); else kfree(list->hidraw); } @@ -367,7 +367,7 @@ void hidraw_disconnect(struct hid_device *hid) device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); if (hidraw->open) { - hid->hid_close(hid); + hid->ll_driver->close(hid); wake_up_interruptible(&hidraw->wait); } else { kfree(hidraw); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 5955d05ae542..d2a3461909a3 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -701,17 +701,84 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) kfree(buf); } -static struct hid_device *usb_hid_configure(struct usb_interface *intf) +static int usbhid_start_finish(struct hid_device *hid) { + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + char path[64], *type; + unsigned int i; + + usbhid_init_reports(hid); + hid_dump_device(hid); + if (hid->quirks & HID_QUIRK_RESET_LEDS) + usbhid_set_leds(hid); + + if (!hidinput_connect(hid)) + hid->claimed |= HID_CLAIMED_INPUT; + if (!hiddev_connect(hid)) + hid->claimed |= HID_CLAIMED_HIDDEV; + if (!hidraw_connect(hid)) + hid->claimed |= HID_CLAIMED_HIDRAW; + + if (!hid->claimed) { + printk(KERN_ERR "HID device claimed by neither input, hiddev " + "nor hidraw\n"); + return -ENODEV; + } + + if ((hid->claimed & HID_CLAIMED_INPUT)) + hid_ff_init(hid); + + if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) + hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), + intf->cur_altsetting->desc.bInterfaceNumber); + + printk(KERN_INFO); + + if (hid->claimed & HID_CLAIMED_INPUT) + printk("input"); + if ((hid->claimed & HID_CLAIMED_INPUT) && + ((hid->claimed & HID_CLAIMED_HIDDEV) || + hid->claimed & HID_CLAIMED_HIDRAW)) + printk(","); + if (hid->claimed & HID_CLAIMED_HIDDEV) + printk("hiddev%d", hid->minor); + if ((hid->claimed & HID_CLAIMED_INPUT) && + (hid->claimed & HID_CLAIMED_HIDDEV) && + (hid->claimed & HID_CLAIMED_HIDRAW)) + printk(","); + if (hid->claimed & HID_CLAIMED_HIDRAW) + printk("hidraw%d", ((struct hidraw *)hid->hidraw)->minor); + + type = "Device"; + for (i = 0; i < hid->maxcollection; i++) { + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && + (hid->collection[i].usage & HID_USAGE_PAGE) == + HID_UP_GENDESK && + (hid->collection[i].usage & 0xffff) < + ARRAY_SIZE(hid_types)) { + type = hid_types[hid->collection[i].usage & 0xffff]; + break; + } + } + + usb_make_path(interface_to_usbdev(intf), path, 63); + + printk(": USB HID v%x.%02x %s [%s] on %s\n", + hid->version >> 8, hid->version & 0xff, type, hid->name, path); + + return 0; +} + +static int usbhid_parse(struct hid_device *hid) +{ + struct usb_interface *intf = to_usb_interface(hid->dev.parent); struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev (intf); struct hid_descriptor *hdesc; - struct hid_device *hid; u32 quirks = 0; - unsigned int insize = 0, rsize = 0; + unsigned int rsize = 0; char *rdesc; - int n, len; - struct usbhid_device *usbhid; + int ret, n; quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -725,40 +792,44 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) } if (quirks & HID_QUIRK_IGNORE) - return NULL; + return -ENODEV; if ((quirks & HID_QUIRK_IGNORE_MOUSE) && (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) - return NULL; - + return -ENODEV; if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && (!interface->desc.bNumEndpoints || usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { dbg_hid("class descriptor not present\n"); - return NULL; + return -ENODEV; } + hid->version = le16_to_cpu(hdesc->bcdHID); + hid->country = hdesc->bCountryCode; + for (n = 0; n < hdesc->bNumDescriptors; n++) if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dbg_hid("weird size of report descriptor (%u)\n", rsize); - return NULL; + return -EINVAL; } if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { dbg_hid("couldn't allocate rdesc memory\n"); - return NULL; + return -ENOMEM; } hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); - if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { + ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, + HID_DT_REPORT, rdesc, rsize); + if (ret < 0) { dbg_hid("reading report descriptor failed\n"); kfree(rdesc); - return NULL; + goto err; } usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor), @@ -770,24 +841,36 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) dbg_hid_line(" %02x", (unsigned char) rdesc[n]); dbg_hid_line("\n"); - hid = hid_allocate_device(); - if (IS_ERR(hid)) { - kfree(rdesc); - return NULL; - } - - if (hid_parse_report(hid, rdesc, n)) { + ret = hid_parse_report(hid, rdesc, rsize); + kfree(rdesc); + if (ret) { dbg_hid("parsing report descriptor failed\n"); - hid_destroy_device(hid); - kfree(rdesc); - return NULL; + goto err; } - kfree(rdesc); hid->quirks = quirks; - if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL))) - goto fail_no_usbhid; + return 0; +err: + return ret; +} + +static int usbhid_start(struct hid_device *hid) +{ + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_device *dev = interface_to_usbdev(intf); + struct usbhid_device *usbhid; + unsigned int n, insize = 0; + int ret; + + WARN_ON(hid->driver_data); + + usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL); + if (usbhid == NULL) { + ret = -ENOMEM; + goto err; + } hid->driver_data = usbhid; usbhid->hid = hid; @@ -805,27 +888,12 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (insize > HID_MAX_BUFFER_SIZE) insize = HID_MAX_BUFFER_SIZE; - if (hid_alloc_buffers(dev, hid)) + if (hid_alloc_buffers(dev, hid)) { + ret = -ENOMEM; goto fail; - - hid->name[0] = 0; - - if (dev->manufacturer) - strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(hid->name, " ", sizeof(hid->name)); - strlcat(hid->name, dev->product, sizeof(hid->name)); } - if (!strlen(hid->name)) - snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - for (n = 0; n < interface->desc.bNumEndpoints; n++) { - struct usb_endpoint_descriptor *endpoint; int pipe; int interval; @@ -837,7 +905,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) interval = endpoint->bInterval; /* Some vendors give fullspeed interval on highspeed devides */ - if (quirks & HID_QUIRK_FULLSPEED_INTERVAL && + if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL && dev->speed == USB_SPEED_HIGH) { interval = fls(endpoint->bInterval*8); printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n", @@ -848,6 +916,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) interval = hid_mousepoll_interval; + ret = -ENOMEM; if (usb_endpoint_dir_in(endpoint)) { if (usbhid->urbin) continue; @@ -873,6 +942,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (!usbhid->urbin) { err_hid("couldn't find an input interrupt endpoint"); + ret = -ENODEV; goto fail; } @@ -884,44 +954,26 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) spin_lock_init(&usbhid->outlock); spin_lock_init(&usbhid->ctrllock); - hid->version = le16_to_cpu(hdesc->bcdHID); - hid->country = hdesc->bCountryCode; - hid->dev.parent = &intf->dev; usbhid->intf = intf; usbhid->ifnum = interface->desc.bInterfaceNumber; - hid->bus = BUS_USB; - hid->vendor = le16_to_cpu(dev->descriptor.idVendor); - hid->product = le16_to_cpu(dev->descriptor.idProduct); - - usb_make_path(dev, hid->phys, sizeof(hid->phys)); - strlcat(hid->phys, "/input", sizeof(hid->phys)); - len = strlen(hid->phys); - if (len < sizeof(hid->phys) - 1) - snprintf(hid->phys + len, sizeof(hid->phys) - len, - "%d", intf->altsetting[0].desc.bInterfaceNumber); - - if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) - hid->uniq[0] = 0; - usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); - if (!usbhid->urbctrl) + if (!usbhid->urbctrl) { + ret = -ENOMEM; goto fail; + } usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr, usbhid->ctrlbuf, 1, hid_ctrl, hid); usbhid->urbctrl->setup_dma = usbhid->cr_dma; usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - hid->hidinput_input_event = usb_hidinput_input_event; - hid->hid_open = usbhid_open; - hid->hid_close = usbhid_close; -#ifdef CONFIG_USB_HIDDEV - hid->hiddev_hid_event = hiddev_hid_event; - hid->hiddev_report_event = hiddev_report_event; -#endif - hid->hid_output_raw_report = usbhid_output_raw_report; - return hid; + + ret = usbhid_start_finish(hid); + if (ret) + goto fail; + + return 0; fail: usb_free_urb(usbhid->urbin); @@ -929,24 +981,18 @@ fail: usb_free_urb(usbhid->urbctrl); hid_free_buffers(dev, hid); kfree(usbhid); -fail_no_usbhid: - hid_destroy_device(hid); - - return NULL; +err: + return ret; } -static void hid_disconnect(struct usb_interface *intf) +static void usbhid_stop(struct hid_device *hid) { - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid; + struct usbhid_device *usbhid = hid->driver_data; - if (!hid) + if (WARN_ON(!usbhid)) return; - usbhid = hid->driver_data; - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ - usb_set_intfdata(intf, NULL); set_bit(HID_DISCONNECTED, &usbhid->iofl); spin_unlock_irq(&usbhid->inlock); usb_kill_urb(usbhid->urbin); @@ -963,93 +1009,99 @@ static void hid_disconnect(struct usb_interface *intf) if (hid->claimed & HID_CLAIMED_HIDRAW) hidraw_disconnect(hid); + hid->claimed = 0; + usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbctrl); usb_free_urb(usbhid->urbout); hid_free_buffers(hid_to_usb_dev(hid), hid); kfree(usbhid); - hid_destroy_device(hid); + hid->driver_data = NULL; } +static struct hid_ll_driver usb_hid_driver = { + .parse = usbhid_parse, + .start = usbhid_start, + .stop = usbhid_stop, + .open = usbhid_open, + .close = usbhid_close, + .hidinput_input_event = usb_hidinput_input_event, +}; + static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_device *dev = interface_to_usbdev(intf); struct hid_device *hid; - char path[64]; - int i, ret; - char *c; + size_t len; + int ret; dbg_hid("HID probe called for ifnum %d\n", intf->altsetting->desc.bInterfaceNumber); - if (!(hid = usb_hid_configure(intf))) - return -ENODEV; - - usbhid_init_reports(hid); - hid_dump_device(hid); - if (hid->quirks & HID_QUIRK_RESET_LEDS) - usbhid_set_leds(hid); - - if (!hidinput_connect(hid)) - hid->claimed |= HID_CLAIMED_INPUT; - if (!hiddev_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDDEV; - if (!hidraw_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDRAW; + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); usb_set_intfdata(intf, hid); + hid->ll_driver = &usb_hid_driver; + hid->hid_output_raw_report = usbhid_output_raw_report; +#ifdef CONFIG_USB_HIDDEV + hid->hiddev_hid_event = hiddev_hid_event; + hid->hiddev_report_event = hiddev_report_event; +#endif + hid->dev.parent = &intf->dev; + hid->bus = BUS_USB; + hid->vendor = le16_to_cpu(dev->descriptor.idVendor); + hid->product = le16_to_cpu(dev->descriptor.idProduct); + hid->name[0] = 0; - if (!hid->claimed) { - printk ("HID device claimed by neither input, hiddev nor hidraw\n"); - hid_disconnect(intf); - return -ENODEV; - } - - if ((hid->claimed & HID_CLAIMED_INPUT)) - hid_ff_init(hid); - - if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) - hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), - intf->cur_altsetting->desc.bInterfaceNumber); - - printk(KERN_INFO); - - if (hid->claimed & HID_CLAIMED_INPUT) - printk("input"); - if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) || - hid->claimed & HID_CLAIMED_HIDRAW)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDDEV) - printk("hiddev%d", hid->minor); - if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) && - (hid->claimed & HID_CLAIMED_HIDRAW)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDRAW) - printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor); + if (dev->manufacturer) + strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); - c = "Device"; - for (i = 0; i < hid->maxcollection; i++) { - if (hid->collection[i].type == HID_COLLECTION_APPLICATION && - (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK && - (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) { - c = hid_types[hid->collection[i].usage & 0xffff]; - break; - } + if (dev->product) { + if (dev->manufacturer) + strlcat(hid->name, " ", sizeof(hid->name)); + strlcat(hid->name, dev->product, sizeof(hid->name)); } - usb_make_path(interface_to_usbdev(intf), path, 63); + if (!strlen(hid->name)) + snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); - printk(": USB HID v%x.%02x %s [%s] on %s\n", - hid->version >> 8, hid->version & 0xff, c, hid->name, path); + usb_make_path(dev, hid->phys, sizeof(hid->phys)); + strlcat(hid->phys, "/input", sizeof(hid->phys)); + len = strlen(hid->phys); + if (len < sizeof(hid->phys) - 1) + snprintf(hid->phys + len, sizeof(hid->phys) - len, + "%d", intf->altsetting[0].desc.bInterfaceNumber); + + if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) + hid->uniq[0] = 0; ret = hid_add_device(hid); if (ret) { dev_err(&intf->dev, "can't add hid device: %d\n", ret); - hid_disconnect(intf); + goto err; } + + return 0; +err: + hid_destroy_device(hid); return ret; } +static void hid_disconnect(struct usb_interface *intf) +{ + struct hid_device *hid = usb_get_intfdata(intf); + + if (WARN_ON(!hid)) + return; + + hid_destroy_device(hid); +} + static int hid_suspend(struct usb_interface *intf, pm_message_t message) { struct hid_device *hid = usb_get_intfdata (intf); diff --git a/include/linux/hid.h b/include/linux/hid.h index c4bea0eda85b..ac2584fe65c5 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -419,6 +419,7 @@ struct hid_control_fifo { #define HID_CLAIMED_HIDRAW 4 #define HID_STAT_ADDED 1 +#define HID_STAT_PARSED 2 #define HID_CTRL_RUNNING 1 #define HID_OUT_RUNNING 2 @@ -435,6 +436,7 @@ struct hid_input { }; struct hid_driver; +struct hid_ll_driver; struct hid_device { /* device report descriptor */ __u8 *rdesc; @@ -452,6 +454,7 @@ struct hid_device { /* device report descriptor */ struct device dev; /* device */ struct hid_driver *driver; + struct hid_ll_driver *ll_driver; unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ @@ -471,11 +474,6 @@ struct hid_device { /* device report descriptor */ __s32 delayed_value; /* For A4 Tech mice hwheel quirk */ - /* device-specific function pointers */ - int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int); - int (*hid_open) (struct hid_device *); - void (*hid_close) (struct hid_device *); - /* hiddev event handler */ void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, struct hid_usage *, __s32); @@ -561,9 +559,22 @@ struct hid_usage_id { * @raw_event: if report in report_table, this hook is called (NULL means nop) * @usage_table: on which events to call event (NULL means all) * @event: if usage in usage_table, this hook is called (NULL means nop) + * @report_fixup: called before report descriptor parsing (NULL means nop) + * @input_mapping: invoked on input registering before mapping an usage + * @input_mapped: invoked on input registering after mapping an usage * * raw_event and event should return 0 on no action performed, 1 when no * further processing should be done and negative on error + * + * input_mapping shall return a negative value to completely ignore this usage + * (e.g. doubled or invalid usage), zero to continue with parsing of this + * usage by generic code (no special handling needed) or positive to skip + * generic parsing (needed special handling which was done in the hook already) + * input_mapped shall return negative to inform the layer that this usage + * should not be considered for further processing or zero to notify that + * no processing was performed and should be done in a generic manner + * Both these functions may be NULL which means the same behavior as returning + * zero from them. */ struct hid_driver { char *name; @@ -578,10 +589,43 @@ struct hid_driver { const struct hid_usage_id *usage_table; int (*event)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value); + + void (*report_fixup)(struct hid_device *hdev, __u8 *buf, + unsigned int size); + + int (*input_mapping)(struct hid_device *hdev, + struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max); + int (*input_mapped)(struct hid_device *hdev, + struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max); /* private: */ struct device_driver driver; }; +/** + * hid_ll_driver - low level driver callbacks + * @start: called on probe to start the device + * @stop: called on remove + * @open: called by input layer on open + * @close: called by input layer on close + * @hidinput_input_event: event input event (e.g. ff or leds) + * @parse: this method is called only once to parse the device data, + * shouldn't allocate anything to not leak memory + */ +struct hid_ll_driver { + int (*start)(struct hid_device *hdev); + void (*stop)(struct hid_device *hdev); + + int (*open)(struct hid_device *hdev); + void (*close)(struct hid_device *hdev); + + int (*hidinput_input_event) (struct input_dev *idev, unsigned int type, + unsigned int code, int value); + + int (*parse)(struct hid_device *hdev); +}; + /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) @@ -618,6 +662,56 @@ void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); +/** + * hid_parse - parse HW reports + * + * @hdev: hid device + * + * Call this from probe after you set up the device (if needed). Your + * report_fixup will be called (if non-NULL) after reading raw report from + * device before passing it to hid layer for real parsing. + */ +static inline int __must_check hid_parse(struct hid_device *hdev) +{ + int ret; + + if (hdev->status & HID_STAT_PARSED) + return 0; + + ret = hdev->ll_driver->parse(hdev); + if (!ret) + hdev->status |= HID_STAT_PARSED; + + return ret; +} + +/** + * hid_hw_start - start underlaying HW + * + * @hdev: hid device + * + * Call this in probe function *after* hid_parse. This will setup HW buffers + * and start the device (if not deffered to device open). hid_hw_stop must be + * called if this was successfull. + */ +static inline int __must_check hid_hw_start(struct hid_device *hdev) +{ + return hdev->ll_driver->start(hdev); +} + +/** + * hid_hw_stop - stop underlaying HW + * + * @hdev: hid device + * + * This is usually called from remove function or from probe when something + * failed and hid_hw_start was called already. + */ +static inline void hid_hw_stop(struct hid_device *hdev) +{ + hdev->ll_driver->stop(hdev); +} + void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 56a51f91591a..d8029cfcd452 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -623,9 +623,15 @@ static struct device *hidp_get_device(struct hidp_session *session) static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { - struct input_dev *input = session->input; + struct input_dev *input; int i; + input = input_allocate_device(); + if (!input) + return -ENOMEM; + + session->input = input; + input_set_drvdata(input, session); input->name = "Bluetooth HID Boot Protocol Device"; @@ -698,55 +704,117 @@ static void hidp_setup_quirks(struct hid_device *hid) hid->quirks = hidp_blacklist[n].quirks; } +static int hidp_parse(struct hid_device *hid) +{ + struct hidp_session *session = hid->driver_data; + struct hidp_connadd_req *req = session->req; + unsigned char *buf; + int ret; + + buf = kmalloc(req->rd_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, req->rd_data, req->rd_size)) { + kfree(buf); + return -EFAULT; + } + + ret = hid_parse_report(session->hid, buf, req->rd_size); + + kfree(buf); + + if (ret) + return ret; + + session->req = NULL; + + hidp_setup_quirks(hid); + return 0; +} + +static int hidp_start(struct hid_device *hid) +{ + struct hidp_session *session = hid->driver_data; + struct hid_report *report; + + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. + report_list, list) + hidp_send_report(session, report); + + list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. + report_list, list) + hidp_send_report(session, report); + + if (hidinput_connect(hid) == 0) + hid->claimed |= HID_CLAIMED_INPUT; + + return 0; +} + +static void hidp_stop(struct hid_device *hid) +{ + struct hidp_session *session = hid->driver_data; + + skb_queue_purge(&session->ctrl_transmit); + skb_queue_purge(&session->intr_transmit); + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + hid->claimed = 0; +} + +static struct hid_ll_driver hidp_hid_driver = { + .parse = hidp_parse, + .start = hidp_start, + .stop = hidp_stop, + .open = hidp_open, + .close = hidp_close, + .hidinput_input_event = hidp_hidinput_event, +}; + static int hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { - struct hid_device *hid = session->hid; - struct hid_report *report; + struct hid_device *hid; bdaddr_t src, dst; int ret; - baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); - baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(session->hid); + goto err; + } + session->hid = hid; + session->req = req; hid->driver_data = session; - hid->country = req->country; + baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); + baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); hid->bus = BUS_BLUETOOTH; hid->vendor = req->vendor; hid->product = req->product; hid->version = req->version; + hid->country = req->country; strncpy(hid->name, req->name, 128); strncpy(hid->phys, batostr(&src), 64); strncpy(hid->uniq, batostr(&dst), 64); hid->dev.parent = hidp_get_device(session); - - hid->hid_open = hidp_open; - hid->hid_close = hidp_close; - - hid->hidinput_input_event = hidp_hidinput_event; - - hidp_setup_quirks(hid); - - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - hidp_send_report(session, report); - - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) - hidp_send_report(session, report); - - if (hidinput_connect(hid) == 0) - hid->claimed |= HID_CLAIMED_INPUT; + hid->ll_driver = &hidp_hid_driver; ret = hid_add_device(hid); - if (ret) { - if (hid->claimed & HID_CLAIMED_INPUT) - hidinput_disconnect(hid); - skb_queue_purge(&session->intr_transmit); - } + if (ret) + goto err_hid; + return 0; +err_hid: + hid_destroy_device(hid); + session->hid = NULL; +err: return ret; } @@ -767,46 +835,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); - if (req->rd_size > 0) { - unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); - - if (!buf) { - kfree(session); - return -ENOMEM; - } - - if (copy_from_user(buf, req->rd_data, req->rd_size)) { - kfree(buf); - kfree(session); - return -EFAULT; - } - - session->hid = hid_allocate_device(); - if (IS_ERR(session->hid)) { - kfree(buf); - kfree(session); - return PTR_ERR(session->hid); - } - - err = hid_parse_report(session->hid, buf, req->rd_size); - - kfree(buf); - - if (err) { - hid_destroy_device(session->hid); - kfree(session); - return -EINVAL; - } - } - - if (!session->hid) { - session->input = input_allocate_device(); - if (!session->input) { - kfree(session); - return -ENOMEM; - } - } - down_write(&hidp_session_sem); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); @@ -834,16 +862,16 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; - if (session->input) { - err = hidp_setup_input(session, req); - if (err < 0) - goto failed; - } - - if (session->hid) { + if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err) - goto failed; + goto err_skb; + } + + if (!session->hid) { + err = hidp_setup_input(session, req); + if (err < 0) + goto err_skb; } __hidp_link_session(session); @@ -871,16 +899,15 @@ unlink: __hidp_unlink_session(session); - if (session->input) { + if (session->input) input_unregister_device(session->input); - session->input = NULL; /* don't try to free it here */ - } - -failed: - up_write(&hidp_session_sem); - if (session->hid) hid_destroy_device(session->hid); +err_skb: + skb_queue_purge(&session->ctrl_transmit); + skb_queue_purge(&session->intr_transmit); +failed: + up_write(&hidp_session_sem); input_free_device(session->input); kfree(session); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 343fb0566b3e..e503c89057ad 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -151,6 +151,8 @@ struct hidp_session { struct sk_buff_head ctrl_transmit; struct sk_buff_head intr_transmit; + + struct hidp_connadd_req *req; }; static inline void hidp_schedule(struct hidp_session *session) -- cgit v1.2.3 From 022e8c4d08b3b06361594b60412db0242035c4b4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 16 May 2008 11:49:18 +0200 Subject: HID: move usage input mapping to hid.h This mapping are currently used on 2 placces and will be needed by more quirk drivers, so move them to hid.h to allow them to use it. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-input-quirks.c | 77 +++++++++++++++++++++--------------------- drivers/hid/hid-input.c | 16 +++++---- include/linux/hid.h | 56 +++++++++++++++++++++++++++++- 3 files changed, 103 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 16feea014494..8ec64b74d38d 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -16,16 +16,14 @@ #include #include -#define map_abs(c) do { usage->code = c; usage->type = EV_ABS; *bit = input->absbit; *max = ABS_MAX; } while (0) -#define map_rel(c) do { usage->code = c; usage->type = EV_REL; *bit = input->relbit; *max = REL_MAX; } while (0) -#define map_key(c) do { usage->code = c; usage->type = EV_KEY; *bit = input->keybit; *max = KEY_MAX; } while (0) -#define map_led(c) do { usage->code = c; usage->type = EV_LED; *bit = input->ledbit; *max = LED_MAX; } while (0) +#define map_rel(c) hid_map_usage(hidinput, usage, bit, max, EV_REL, (c)) +#define map_key(c) hid_map_usage(hidinput, usage, bit, max, EV_KEY, (c)) -#define map_abs_clear(c) do { map_abs(c); clear_bit(c, *bit); } while (0) -#define map_key_clear(c) do { map_key(c); clear_bit(c, *bit); } while (0) +#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, bit, \ + max, EV_KEY, (c)) -static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_belkin_wkbd(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) return 0; @@ -40,8 +38,8 @@ static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input, return 1; } -static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_cherry_cymotion(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) return 0; @@ -56,13 +54,13 @@ static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *inpu return 1; } -static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_logitech_ultrax_remote(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) return 0; - set_bit(EV_REP, input->evbit); + set_bit(EV_REP, hidinput->input->evbit); switch(usage->hid & HID_USAGE) { /* Reported on Logitech Ultra X Media Remote */ case 0x004: map_key_clear(KEY_AGAIN); break; @@ -89,13 +87,13 @@ static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_de return 1; } -static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_gyration_remote(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) return 0; - set_bit(EV_REP, input->evbit); + set_bit(EV_REP, hidinput->input->evbit); switch(usage->hid & HID_USAGE) { /* Reported on Gyration MCE Remote */ case 0x00d: map_key_clear(KEY_HOME); break; @@ -112,13 +110,13 @@ static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *inpu return 1; } -static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_chicony_tactical_pad(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) return 0; - set_bit(EV_REP, input->evbit); + set_bit(EV_REP, hidinput->input->evbit); switch (usage->hid & HID_USAGE) { case 0xff01: map_key_clear(BTN_1); break; case 0xff02: map_key_clear(BTN_2); break; @@ -137,9 +135,11 @@ static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev return 1; } -static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { + struct input_dev *input = hidinput->input; + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) return 0; @@ -160,13 +160,13 @@ static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev return 1; } -static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_microsoft_presenter_8k(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) return 0; - set_bit(EV_REP, input->evbit); + set_bit(EV_REP, hidinput->input->evbit); switch(usage->hid & HID_USAGE) { case 0xfd08: map_key_clear(KEY_FORWARD); break; case 0xfd09: map_key_clear(KEY_BACK); break; @@ -179,8 +179,8 @@ static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_de return 1; } -static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_petalynx_remote(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) && ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)) @@ -207,8 +207,8 @@ static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *inpu return 1; } -static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_logitech_wireless(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) return 0; @@ -259,8 +259,8 @@ static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *in return 1; } -static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_cherry_genius_29e(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) return 0; @@ -277,7 +277,7 @@ static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *in return 1; } -static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input, +static int quirk_btc_8193(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) @@ -299,8 +299,8 @@ static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input, return 1; } -static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) +static int quirk_sunplus_wdesktop(struct hid_usage *usage, + struct hid_input *hidinput, unsigned long **bit, int *max) { if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) return 0; @@ -353,7 +353,8 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *inp static const struct hid_input_blacklist { __u16 idVendor; __u16 idProduct; - int (*quirk)(struct hid_usage *, struct input_dev *, unsigned long **, int *); + int (*quirk)(struct hid_usage *, struct hid_input *, unsigned long **, + int *); } hid_input_blacklist[] = { { VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd }, @@ -385,16 +386,16 @@ static const struct hid_input_blacklist { }; int hidinput_mapping_quirks(struct hid_usage *usage, - struct input_dev *input, - unsigned long **bit, int *max) + struct hid_input *hi, unsigned long **bit, int *max) { - struct hid_device *device = input_get_drvdata(input); + struct hid_device *device = input_get_drvdata(hi->input); int i = 0; while (hid_input_blacklist[i].quirk) { if (hid_input_blacklist[i].idVendor == device->vendor && hid_input_blacklist[i].idProduct == device->product) - return hid_input_blacklist[i].quirk(usage, input, bit, max); + return hid_input_blacklist[i].quirk(usage, hi, bit, + max); i++; } return 0; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 9fa7239ab310..be2c7a8ad254 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -76,13 +76,15 @@ static const struct { __s32 y; } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; -#define map_abs(c) do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0) -#define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0) -#define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0) -#define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0) +#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c)) +#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c)) +#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c)) +#define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c)) -#define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0) -#define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) +#define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ + &max, EV_ABS, (c)) +#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ + &max, EV_KEY, (c)) #ifdef CONFIG_USB_HIDINPUT_POWERBOOK @@ -386,7 +388,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } /* handle input mappings for quirky devices */ - ret = hidinput_mapping_quirks(usage, input, &bit, &max); + ret = hidinput_mapping_quirks(usage, hidinput, &bit, &max); if (ret) goto mapped; diff --git a/include/linux/hid.h b/include/linux/hid.h index ac2584fe65c5..986c0e7ea66a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -655,13 +655,67 @@ extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); -int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned long **, int *); +int hidinput_mapping_quirks(struct hid_usage *, struct hid_input *, + unsigned long **, int *); int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); +/** + * hid_map_usage - map usage input bits + * + * @hidinput: hidinput which we are interested in + * @usage: usage to fill in + * @bit: pointer to input->{}bit (out parameter) + * @max: maximal valid usage->code to consider later (out parameter) + * @type: input event type (EV_KEY, EV_REL, ...) + * @c: code which corresponds to this usage and type + */ +static inline void hid_map_usage(struct hid_input *hidinput, + struct hid_usage *usage, unsigned long **bit, int *max, + __u8 type, __u16 c) +{ + struct input_dev *input = hidinput->input; + + usage->type = type; + usage->code = c; + + switch (type) { + case EV_ABS: + *bit = input->absbit; + *max = ABS_MAX; + break; + case EV_REL: + *bit = input->relbit; + *max = REL_MAX; + break; + case EV_KEY: + *bit = input->keybit; + *max = KEY_MAX; + break; + case EV_LED: + *bit = input->ledbit; + *max = LED_MAX; + break; + } +} + +/** + * hid_map_usage_clear - map usage input bits and clear the input bit + * + * The same as hid_map_usage, except the @c bit is also cleared in supported + * bits (@bit). + */ +static inline void hid_map_usage_clear(struct hid_input *hidinput, + struct hid_usage *usage, unsigned long **bit, int *max, + __u8 type, __u16 c) +{ + hid_map_usage(hidinput, usage, bit, max, type, c); + clear_bit(c, *bit); +} + /** * hid_parse - parse HW reports * -- cgit v1.2.3 From 5f22a7992349c5ca3842190be52d5e9a1dd7adf4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 16 May 2008 11:49:19 +0200 Subject: HID: move logitech quirks Move them from the core and input code to a separate driver. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 13 ++ drivers/hid/Makefile | 2 + drivers/hid/hid-core.c | 16 +++ drivers/hid/hid-input-quirks.c | 96 ------------- drivers/hid/hid-input.c | 38 ----- drivers/hid/hid-logitech.c | 312 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 37 ----- include/linux/hid.h | 5 - 8 files changed, 343 insertions(+), 176 deletions(-) create mode 100644 drivers/hid/hid-logitech.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cacf89e65af4..066e8c08c268 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -67,4 +67,17 @@ config HIDRAW source "drivers/hid/usbhid/Kconfig" +menu "Special HID drivers" + depends on HID + +config HID_LOGITECH + tristate "Logitech" + default m + depends on USB_HID + ---help--- + Support for some Logitech devices which breaks less or more + HID specification. + +endmenu + endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 275dc522c738..cae036bfe837 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o +obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o + obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3dacbcd7e41c..c3ff7b13a4be 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -33,6 +33,8 @@ #include #include +#include "hid-ids.h" + /* * Version Information */ @@ -1128,6 +1130,20 @@ static const struct hid_device_id *hid_match_id(struct hid_device *hdev, } static const struct hid_device_id hid_blacklist[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, { } }; diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 8ec64b74d38d..10451ca3a786 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -54,39 +54,6 @@ static int quirk_cherry_cymotion(struct hid_usage *usage, return 1; } -static int quirk_logitech_ultrax_remote(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) - return 0; - - set_bit(EV_REP, hidinput->input->evbit); - switch(usage->hid & HID_USAGE) { - /* Reported on Logitech Ultra X Media Remote */ - case 0x004: map_key_clear(KEY_AGAIN); break; - case 0x00d: map_key_clear(KEY_HOME); break; - case 0x024: map_key_clear(KEY_SHUFFLE); break; - case 0x025: map_key_clear(KEY_TV); break; - case 0x026: map_key_clear(KEY_MENU); break; - case 0x031: map_key_clear(KEY_AUDIO); break; - case 0x032: map_key_clear(KEY_TEXT); break; - case 0x033: map_key_clear(KEY_LAST); break; - case 0x047: map_key_clear(KEY_MP3); break; - case 0x048: map_key_clear(KEY_DVD); break; - case 0x049: map_key_clear(KEY_MEDIA); break; - case 0x04a: map_key_clear(KEY_VIDEO); break; - case 0x04b: map_key_clear(KEY_ANGLE); break; - case 0x04c: map_key_clear(KEY_LANGUAGE); break; - case 0x04d: map_key_clear(KEY_SUBTITLE); break; - case 0x051: map_key_clear(KEY_RED); break; - case 0x052: map_key_clear(KEY_CLOSE); break; - - default: - return 0; - } - return 1; -} - static int quirk_gyration_remote(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { @@ -207,58 +174,6 @@ static int quirk_petalynx_remote(struct hid_usage *usage, return 1; } -static int quirk_logitech_wireless(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x1001: map_key_clear(KEY_MESSENGER); break; - case 0x1003: map_key_clear(KEY_SOUND); break; - case 0x1004: map_key_clear(KEY_VIDEO); break; - case 0x1005: map_key_clear(KEY_AUDIO); break; - case 0x100a: map_key_clear(KEY_DOCUMENTS); break; - case 0x1011: map_key_clear(KEY_PREVIOUSSONG); break; - case 0x1012: map_key_clear(KEY_NEXTSONG); break; - case 0x1013: map_key_clear(KEY_CAMERA); break; - case 0x1014: map_key_clear(KEY_MESSENGER); break; - case 0x1015: map_key_clear(KEY_RECORD); break; - case 0x1016: map_key_clear(KEY_PLAYER); break; - case 0x1017: map_key_clear(KEY_EJECTCD); break; - case 0x1018: map_key_clear(KEY_MEDIA); break; - case 0x1019: map_key_clear(KEY_PROG1); break; - case 0x101a: map_key_clear(KEY_PROG2); break; - case 0x101b: map_key_clear(KEY_PROG3); break; - case 0x101f: map_key_clear(KEY_ZOOMIN); break; - case 0x1020: map_key_clear(KEY_ZOOMOUT); break; - case 0x1021: map_key_clear(KEY_ZOOMRESET); break; - case 0x1023: map_key_clear(KEY_CLOSE); break; - case 0x1027: map_key_clear(KEY_MENU); break; - /* this one is marked as 'Rotate' */ - case 0x1028: map_key_clear(KEY_ANGLE); break; - case 0x1029: map_key_clear(KEY_SHUFFLE); break; - case 0x102a: map_key_clear(KEY_BACK); break; - case 0x102b: map_key_clear(KEY_CYCLEWINDOWS); break; - case 0x1041: map_key_clear(KEY_BATTERY); break; - case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; - case 0x1043: map_key_clear(KEY_SPREADSHEET); break; - case 0x1044: map_key_clear(KEY_PRESENTATION); break; - case 0x1045: map_key_clear(KEY_UNDO); break; - case 0x1046: map_key_clear(KEY_REDO); break; - case 0x1047: map_key_clear(KEY_PRINT); break; - case 0x1048: map_key_clear(KEY_SAVE); break; - case 0x1049: map_key_clear(KEY_PROG1); break; - case 0x104a: map_key_clear(KEY_PROG2); break; - case 0x104b: map_key_clear(KEY_PROG3); break; - case 0x104c: map_key_clear(KEY_PROG4); break; - - default: - return 0; - } - return 1; -} - static int quirk_cherry_genius_29e(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { @@ -329,12 +244,6 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage, #define VENDOR_ID_GYRATION 0x0c16 #define DEVICE_ID_GYRATION_REMOTE 0x0002 -#define VENDOR_ID_LOGITECH 0x046d -#define DEVICE_ID_LOGITECH_RECEIVER 0xc101 -#define DEVICE_ID_S510_RECEIVER 0xc50c -#define DEVICE_ID_S510_RECEIVER_2 0xc517 -#define DEVICE_ID_MX3000_RECEIVER 0xc513 - #define VENDOR_ID_MICROSOFT 0x045e #define DEVICE_ID_MS4K 0x00db #define DEVICE_ID_MS6K 0x00f9 @@ -366,11 +275,6 @@ static const struct hid_input_blacklist { { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, - { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote }, - { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless }, - { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless }, - { VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb }, { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb }, { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k }, diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index be2c7a8ad254..4f2bac010f59 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -58,19 +58,6 @@ static const unsigned char hid_keyboard[256] = { 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk }; -/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */ -#define LOGITECH_EXPANDED_KEYMAP_SIZE 80 -static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = { - 0,216, 0,213,175,156, 0, 0, 0, 0, - 144, 0, 0, 0, 0, 0, 0, 0, 0,212, - 174,167,152,161,112, 0, 0, 0,154, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,183,184,185,186,187, - 188,189,190,191,192,193,194, 0, 0, 0 -}; - static const struct { __s32 x; __s32 y; @@ -437,21 +424,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } } - /* Special handling for Logitech Cordless Desktop */ - if (field->application != HID_GD_MOUSE) { - if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) { - int hid = usage->hid & HID_USAGE; - if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0) - code = logitech_expanded_keymap[hid]; - } - } else { - if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) { - int hid = usage->hid & HID_USAGE; - if (hid == 7 || hid == 8) - goto ignore; - } - } - map_key(code); break; @@ -788,18 +760,8 @@ mapped: || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) goto ignore; - if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) && - usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE)) - field->flags &= ~HID_MAIN_ITEM_RELATIVE; - set_bit(usage->type, input->evbit); - if (device->quirks & HID_QUIRK_DUPLICATE_USAGES && - (usage->type == EV_KEY || - usage->type == EV_REL || - usage->type == EV_ABS)) - clear_bit(usage->code, bit); - while (usage->code <= max && test_and_set_bit(usage->code, bit)) usage->code = find_next_zero_bit(bit, max + 1, usage->code); diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c new file mode 100644 index 000000000000..395e42ffb4d5 --- /dev/null +++ b/drivers/hid/hid-logitech.c @@ -0,0 +1,312 @@ +/* + * HID driver for some logitech "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" + +#define LG_RDESC 0x001 +#define LG_BAD_RELATIVE_KEYS 0x002 +#define LG_DUPLICATE_USAGES 0x004 +#define LG_RESET_LEDS 0x008 +#define LG_EXPANDED_KEYMAP 0x010 +#define LG_IGNORE_DOUBLED_WHEEL 0x020 +#define LG_WIRELESS 0x040 +#define LG_INVERT_HWHEEL 0x080 +#define LG_NOGET 0x100 + +/* + * Certain Logitech keyboards send in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 && + rdesc[84] == 0x8c && rdesc[85] == 0x02) { + dev_info(&hdev->dev, "fixing up Logitech keyboard report " + "descriptor\n"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + +#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) + +static int lg_ultrax_remote_mapping(struct hid_input *hi, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) + return 0; + + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + /* Reported on Logitech Ultra X Media Remote */ + case 0x004: lg_map_key_clear(KEY_AGAIN); break; + case 0x00d: lg_map_key_clear(KEY_HOME); break; + case 0x024: lg_map_key_clear(KEY_SHUFFLE); break; + case 0x025: lg_map_key_clear(KEY_TV); break; + case 0x026: lg_map_key_clear(KEY_MENU); break; + case 0x031: lg_map_key_clear(KEY_AUDIO); break; + case 0x032: lg_map_key_clear(KEY_TEXT); break; + case 0x033: lg_map_key_clear(KEY_LAST); break; + case 0x047: lg_map_key_clear(KEY_MP3); break; + case 0x048: lg_map_key_clear(KEY_DVD); break; + case 0x049: lg_map_key_clear(KEY_MEDIA); break; + case 0x04a: lg_map_key_clear(KEY_VIDEO); break; + case 0x04b: lg_map_key_clear(KEY_ANGLE); break; + case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break; + case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break; + case 0x051: lg_map_key_clear(KEY_RED); break; + case 0x052: lg_map_key_clear(KEY_CLOSE); break; + + default: + return 0; + } + return 1; +} + +static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x1001: lg_map_key_clear(KEY_MESSENGER); break; + case 0x1003: lg_map_key_clear(KEY_SOUND); break; + case 0x1004: lg_map_key_clear(KEY_VIDEO); break; + case 0x1005: lg_map_key_clear(KEY_AUDIO); break; + case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break; + case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break; + case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break; + case 0x1013: lg_map_key_clear(KEY_CAMERA); break; + case 0x1014: lg_map_key_clear(KEY_MESSENGER); break; + case 0x1015: lg_map_key_clear(KEY_RECORD); break; + case 0x1016: lg_map_key_clear(KEY_PLAYER); break; + case 0x1017: lg_map_key_clear(KEY_EJECTCD); break; + case 0x1018: lg_map_key_clear(KEY_MEDIA); break; + case 0x1019: lg_map_key_clear(KEY_PROG1); break; + case 0x101a: lg_map_key_clear(KEY_PROG2); break; + case 0x101b: lg_map_key_clear(KEY_PROG3); break; + case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break; + case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break; + case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break; + case 0x1023: lg_map_key_clear(KEY_CLOSE); break; + case 0x1027: lg_map_key_clear(KEY_MENU); break; + /* this one is marked as 'Rotate' */ + case 0x1028: lg_map_key_clear(KEY_ANGLE); break; + case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break; + case 0x102a: lg_map_key_clear(KEY_BACK); break; + case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break; + case 0x1041: lg_map_key_clear(KEY_BATTERY); break; + case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break; + case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break; + case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break; + case 0x1045: lg_map_key_clear(KEY_UNDO); break; + case 0x1046: lg_map_key_clear(KEY_REDO); break; + case 0x1047: lg_map_key_clear(KEY_PRINT); break; + case 0x1048: lg_map_key_clear(KEY_SAVE); break; + case 0x1049: lg_map_key_clear(KEY_PROG1); break; + case 0x104a: lg_map_key_clear(KEY_PROG2); break; + case 0x104b: lg_map_key_clear(KEY_PROG3); break; + case 0x104c: lg_map_key_clear(KEY_PROG4); break; + + default: + return 0; + } + return 1; +} + +static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* extended mapping for certain Logitech hardware (Logitech cordless + desktop LX500) */ + static const u8 e_keymap[] = { + 0,216, 0,213,175,156, 0, 0, 0, 0, + 144, 0, 0, 0, 0, 0, 0, 0, 0,212, + 174,167,152,161,112, 0, 0, 0,154, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,183,184,185,186,187, + 188,189,190,191,192,193,194, 0, 0, 0 + }; + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + unsigned int hid = usage->hid; + + if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && + lg_ultrax_remote_mapping(hi, usage, bit, max)) + return 1; + + if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) + return 1; + + if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return 0; + + hid &= HID_USAGE; + + /* Special handling for Logitech Cordless Desktop */ + if (field->application == HID_GD_MOUSE) { + if ((quirks & LG_IGNORE_DOUBLED_WHEEL) && + (hid == 7 || hid == 8)) + return -1; + } else { + if ((quirks & LG_EXPANDED_KEYMAP) && + hid < ARRAY_SIZE(e_keymap) && + e_keymap[hid] != 0) { + hid_map_usage(hi, usage, bit, max, EV_KEY, + e_keymap[hid]); + return 1; + } + } + + return 0; +} + +static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && + (field->flags & HID_MAIN_ITEM_RELATIVE)) + field->flags &= ~HID_MAIN_ITEM_RELATIVE; + + if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || + usage->type == EV_REL || usage->type == EV_ABS)) + clear_bit(usage->code, *bit); + + return 0; +} + +static int lg_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { + input_event(field->hidinput->input, usage->type, usage->code, + -value); + return 1; + } + + return 0; +} + +static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + if (quirks & LG_RESET_LEDS) + hdev->quirks |= HID_QUIRK_RESET_LEDS; + if (quirks & LG_NOGET) + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id lg_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), + .driver_data = LG_RDESC | LG_WIRELESS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), + .driver_data = LG_RDESC | LG_WIRELESS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), + .driver_data = LG_RDESC | LG_WIRELESS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), + .driver_data = LG_BAD_RELATIVE_KEYS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP), + .driver_data = LG_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE), + .driver_data = LG_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), + .driver_data = LG_DUPLICATE_USAGES }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD), + .driver_data = LG_RESET_LEDS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), + .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), + .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3), + .driver_data = LG_INVERT_HWHEEL }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150), + .driver_data = LG_INVERT_HWHEEL }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), + .driver_data = LG_NOGET }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), + .driver_data = LG_NOGET }, + { } +}; +MODULE_DEVICE_TABLE(hid, lg_devices); + +static struct hid_driver lg_driver = { + .name = "logitech", + .id_table = lg_devices, + .report_fixup = lg_report_fixup, + .input_mapping = lg_input_mapping, + .input_mapped = lg_input_mapped, + .event = lg_event, + .probe = lg_probe, +}; + +static int lg_init(void) +{ + return hid_register_driver(&lg_driver); +} + +static void lg_exit(void) +{ + hid_unregister_driver(&lg_driver); +} + +module_init(lg_init); +module_exit(lg_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 8b5ce9c9e353..48fdaa5db739 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -33,8 +33,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, @@ -48,10 +46,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP, HID_QUIRK_DUPLICATE_USAGES }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES }, - { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, @@ -196,11 +190,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3, HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150, HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, @@ -218,8 +207,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, @@ -259,7 +246,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD, HID_QUIRK_RESET_LEDS }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_2, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_3, HID_QUIRK_IGNORE }, @@ -339,9 +325,6 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 }, { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, @@ -611,23 +594,6 @@ static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize) } } - -/* - * Certain Logitech keyboards send in report #3 keys which are far - * above the logical maximum described in descriptor. This extends - * the original value of 0x28c of logical maximum to 0x104d - */ -static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 90 && rdesc[83] == 0x26 - && rdesc[84] == 0x8c - && rdesc[85] == 0x02) { - printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n"); - rdesc[84] = rdesc[89] = 0x4d; - rdesc[85] = rdesc[90] = 0x10; - } -} - static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize) { if (rsize >= 107 && rdesc[104] == 0x26 @@ -753,9 +719,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if ((quirks & HID_QUIRK_RDESC_CYMOTION)) usbhid_fixup_cymotion_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_LOGITECH) - usbhid_fixup_logitech_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX) usbhid_fixup_cypress_descriptor(rdesc, rsize); diff --git a/include/linux/hid.h b/include/linux/hid.h index 986c0e7ea66a..e9c4154ba336 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -270,15 +270,11 @@ struct hid_item { #define HID_QUIRK_APPLE_FN_ON 0x00001000 #define HID_QUIRK_INVERT_HWHEEL 0x00002000 #define HID_QUIRK_APPLE_ISO_KEYBOARD 0x00004000 -#define HID_QUIRK_BAD_RELATIVE_KEYS 0x00008000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_IGNORE_MOUSE 0x00020000 #define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 -#define HID_QUIRK_DUPLICATE_USAGES 0x00080000 #define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_HIDINPUT 0x00200000 -#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL 0x00400000 -#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP 0x00800000 #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 #define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000 #define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 @@ -291,7 +287,6 @@ struct hid_item { */ #define HID_QUIRK_RDESC_CYMOTION 0x00000001 -#define HID_QUIRK_RDESC_LOGITECH 0x00000002 #define HID_QUIRK_RDESC_SWAPPED_MIN_MAX 0x00000004 #define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_MACBOOK_JIS 0x00000010 -- cgit v1.2.3 From d458a9dfc4de24870b8c747484b1988726534bee Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 16 May 2008 11:49:20 +0200 Subject: HID: move ignore quirks Move ignore quirks from usbhid-quirks into hid-core code. Also don't output warning when ENODEV is error code in usbhid and try ordinal input in hidp when that error is returned. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 163 +++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 66 +------------ drivers/hid/usbhid/hid-core.c | 6 +- drivers/hid/usbhid/hid-quirks.c | 210 ---------------------------------------- include/linux/hid.h | 1 - net/bluetooth/hidp/core.c | 2 +- 6 files changed, 168 insertions(+), 280 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c3ff7b13a4be..08470ab295b1 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1237,6 +1237,164 @@ static struct bus_type hid_bus_type = { .uevent = hid_uevent, }; +static const struct hid_device_id hid_ignore_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)}, + { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, + { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, + { } +}; + +static bool hid_ignore(struct hid_device *hdev) +{ + switch (hdev->vendor) { + case USB_VENDOR_ID_CODEMERCS: + /* ignore all Code Mercenaries IOWarrior devices */ + if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && + hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) + return true; + break; + case USB_VENDOR_ID_LOGITECH: + if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST && + hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST) + return true; + break; + } + + return !!hid_match_id(hdev, hid_ignore_list); +} + int hid_add_device(struct hid_device *hdev) { static atomic_t id = ATOMIC_INIT(0); @@ -1245,6 +1403,11 @@ int hid_add_device(struct hid_device *hdev) if (WARN_ON(hdev->status & HID_STAT_ADDED)) return -EBUSY; + /* we need to kill them here, otherwise they will stay allocated to + * wait for coming driver */ + if (hid_ignore(hdev)) + return -ENODEV; + /* XXX hack, any other cleaner solution < 20 bus_id bytes? */ sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus, hdev->vendor, hdev->product, atomic_inc_return(&id)); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f4ef84ebe97b..23d021bd95d0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -251,70 +251,8 @@ #define USB_DEVICE_ID_LOGITECH_LX3 0xc044 #define USB_DEVICE_ID_LOGITECH_V150 0xc047 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 -#define USB_DEVICE_ID_LOGITECH_HARMONY 0xc110 -#define USB_DEVICE_ID_LOGITECH_HARMONY_2 0xc111 -#define USB_DEVICE_ID_LOGITECH_HARMONY_3 0xc112 -#define USB_DEVICE_ID_LOGITECH_HARMONY_4 0xc113 -#define USB_DEVICE_ID_LOGITECH_HARMONY_5 0xc114 -#define USB_DEVICE_ID_LOGITECH_HARMONY_6 0xc115 -#define USB_DEVICE_ID_LOGITECH_HARMONY_7 0xc116 -#define USB_DEVICE_ID_LOGITECH_HARMONY_8 0xc117 -#define USB_DEVICE_ID_LOGITECH_HARMONY_9 0xc118 -#define USB_DEVICE_ID_LOGITECH_HARMONY_10 0xc119 -#define USB_DEVICE_ID_LOGITECH_HARMONY_11 0xc11a -#define USB_DEVICE_ID_LOGITECH_HARMONY_12 0xc11b -#define USB_DEVICE_ID_LOGITECH_HARMONY_13 0xc11c -#define USB_DEVICE_ID_LOGITECH_HARMONY_14 0xc11d -#define USB_DEVICE_ID_LOGITECH_HARMONY_15 0xc11e -#define USB_DEVICE_ID_LOGITECH_HARMONY_16 0xc11f -#define USB_DEVICE_ID_LOGITECH_HARMONY_17 0xc120 -#define USB_DEVICE_ID_LOGITECH_HARMONY_18 0xc121 -#define USB_DEVICE_ID_LOGITECH_HARMONY_19 0xc122 -#define USB_DEVICE_ID_LOGITECH_HARMONY_20 0xc123 -#define USB_DEVICE_ID_LOGITECH_HARMONY_21 0xc124 -#define USB_DEVICE_ID_LOGITECH_HARMONY_22 0xc125 -#define USB_DEVICE_ID_LOGITECH_HARMONY_23 0xc126 -#define USB_DEVICE_ID_LOGITECH_HARMONY_24 0xc127 -#define USB_DEVICE_ID_LOGITECH_HARMONY_25 0xc128 -#define USB_DEVICE_ID_LOGITECH_HARMONY_26 0xc129 -#define USB_DEVICE_ID_LOGITECH_HARMONY_27 0xc12a -#define USB_DEVICE_ID_LOGITECH_HARMONY_28 0xc12b -#define USB_DEVICE_ID_LOGITECH_HARMONY_29 0xc12c -#define USB_DEVICE_ID_LOGITECH_HARMONY_30 0xc12d -#define USB_DEVICE_ID_LOGITECH_HARMONY_31 0xc12e -#define USB_DEVICE_ID_LOGITECH_HARMONY_32 0xc12f -#define USB_DEVICE_ID_LOGITECH_HARMONY_33 0xc130 -#define USB_DEVICE_ID_LOGITECH_HARMONY_34 0xc131 -#define USB_DEVICE_ID_LOGITECH_HARMONY_35 0xc132 -#define USB_DEVICE_ID_LOGITECH_HARMONY_36 0xc133 -#define USB_DEVICE_ID_LOGITECH_HARMONY_37 0xc134 -#define USB_DEVICE_ID_LOGITECH_HARMONY_38 0xc135 -#define USB_DEVICE_ID_LOGITECH_HARMONY_39 0xc136 -#define USB_DEVICE_ID_LOGITECH_HARMONY_40 0xc137 -#define USB_DEVICE_ID_LOGITECH_HARMONY_41 0xc138 -#define USB_DEVICE_ID_LOGITECH_HARMONY_42 0xc139 -#define USB_DEVICE_ID_LOGITECH_HARMONY_43 0xc13a -#define USB_DEVICE_ID_LOGITECH_HARMONY_44 0xc13b -#define USB_DEVICE_ID_LOGITECH_HARMONY_45 0xc13c -#define USB_DEVICE_ID_LOGITECH_HARMONY_46 0xc13d -#define USB_DEVICE_ID_LOGITECH_HARMONY_47 0xc13e -#define USB_DEVICE_ID_LOGITECH_HARMONY_48 0xc13f -#define USB_DEVICE_ID_LOGITECH_HARMONY_49 0xc140 -#define USB_DEVICE_ID_LOGITECH_HARMONY_50 0xc141 -#define USB_DEVICE_ID_LOGITECH_HARMONY_51 0xc142 -#define USB_DEVICE_ID_LOGITECH_HARMONY_52 0xc143 -#define USB_DEVICE_ID_LOGITECH_HARMONY_53 0xc144 -#define USB_DEVICE_ID_LOGITECH_HARMONY_54 0xc145 -#define USB_DEVICE_ID_LOGITECH_HARMONY_55 0xc146 -#define USB_DEVICE_ID_LOGITECH_HARMONY_56 0xc147 -#define USB_DEVICE_ID_LOGITECH_HARMONY_57 0xc148 -#define USB_DEVICE_ID_LOGITECH_HARMONY_58 0xc149 -#define USB_DEVICE_ID_LOGITECH_HARMONY_59 0xc14a -#define USB_DEVICE_ID_LOGITECH_HARMONY_60 0xc14b -#define USB_DEVICE_ID_LOGITECH_HARMONY_61 0xc14c -#define USB_DEVICE_ID_LOGITECH_HARMONY_62 0xc14d -#define USB_DEVICE_ID_LOGITECH_HARMONY_63 0xc14e -#define USB_DEVICE_ID_LOGITECH_HARMONY_64 0xc14f +#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 +#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index d2a3461909a3..9f5e100e95ec 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -791,9 +791,6 @@ static int usbhid_parse(struct hid_device *hid) quirks |= HID_QUIRK_NOGET; } - if (quirks & HID_QUIRK_IGNORE) - return -ENODEV; - if ((quirks & HID_QUIRK_IGNORE_MOUSE) && (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) return -ENODEV; @@ -1082,7 +1079,8 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) ret = hid_add_device(hid); if (ret) { - dev_err(&intf->dev, "can't add hid device: %d\n", ret); + if (ret != -ENODEV) + dev_err(&intf->dev, "can't add hid device: %d\n", ret); goto err; } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 48fdaa5db739..466d608ba6ac 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -55,140 +55,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT }, - { USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM, HID_QUIRK_IGNORE}, - { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, @@ -246,72 +112,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_3, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_4, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_6, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_7, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_8, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_9, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_11, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_12, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_13, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_14, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_15, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_16, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_17, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_18, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_19, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_21, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_22, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_23, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_25, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_26, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_27, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_28, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_29, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_31, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_32, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_33, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_34, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_35, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_36, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_37, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_38, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_39, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_40, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_41, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_42, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_43, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_44, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_45, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_46, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_47, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_48, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_49, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_50, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_51, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_52, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_53, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_54, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_55, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_56, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_57, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_58, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_59, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_60, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_61, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_62, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_63, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_64, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560, HID_QUIRK_IGNORE }, { 0, 0 } }; @@ -552,16 +352,6 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) u32 quirks = 0; const struct hid_blacklist *bl_entry = NULL; - /* Ignore all Wacom devices */ - if (idVendor == USB_VENDOR_ID_WACOM) - return HID_QUIRK_IGNORE; - - /* ignore all Code Mercenaries IOWarrior devices */ - if (idVendor == USB_VENDOR_ID_CODEMERCS) - if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && - idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) - return HID_QUIRK_IGNORE; - /* NCR devices must not be queried for reports */ if (idVendor == USB_VENDOR_ID_NCR && idProduct >= USB_DEVICE_ID_NCR_FIRST && diff --git a/include/linux/hid.h b/include/linux/hid.h index e9c4154ba336..0644fd33b983 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -257,7 +257,6 @@ struct hid_item { #define HID_QUIRK_INVERT 0x00000001 #define HID_QUIRK_NOTOUCH 0x00000002 -#define HID_QUIRK_IGNORE 0x00000004 #define HID_QUIRK_NOGET 0x00000008 #define HID_QUIRK_HIDDEV 0x00000010 #define HID_QUIRK_BADPAD 0x00000020 diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index d8029cfcd452..4ae3207e8a98 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -864,7 +864,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (req->rd_size > 0) { err = hidp_setup_hid(session, req); - if (err) + if (err && err != -ENODEV) goto err_skb; } -- cgit v1.2.3 From 8c19a51591d06f5226499972567f528cf6066bb7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 18 Jun 2008 23:36:49 +0200 Subject: HID: move apple quirks Move them from the core code to a separate driver. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 14 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-apple.c | 479 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 32 +++ drivers/hid/hid-input-quirks.c | 8 - drivers/hid/hid-input.c | 221 +----------------- drivers/hid/usbhid/Kconfig | 11 - drivers/hid/usbhid/hid-core.c | 4 - drivers/hid/usbhid/hid-quirks.c | 49 ---- drivers/hid/usbhid/usbmouse.c | 5 + include/linux/hid.h | 13 -- net/bluetooth/hidp/core.c | 22 -- 12 files changed, 532 insertions(+), 327 deletions(-) create mode 100644 drivers/hid/hid-apple.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 066e8c08c268..41283fff5145 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -70,6 +70,20 @@ source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" depends on HID +config HID_APPLE + tristate "Apple" + default m + depends on (USB_HID || BT_HIDP) + ---help--- + Support for some Apple devices which less or more break + HID specification. + + Say Y here if you want support for the special keys (Fn, Numlock) on + Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB + keyboards. + + If unsure, say M. + config HID_LOGITECH tristate "Logitech" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cae036bfe837..4a14821ceefa 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o +obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_USB_HID) += usbhid/ diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c new file mode 100644 index 000000000000..5642e2c685fc --- /dev/null +++ b/drivers/hid/hid-apple.c @@ -0,0 +1,479 @@ +/* + * USB HID quirks support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define APPLE_RDESC_JIS 0x0001 +#define APPLE_IGNORE_MOUSE 0x0002 +#define APPLE_HAS_FN 0x0004 +#define APPLE_HIDDEV 0x0008 +#define APPLE_ISO_KEYBOARD 0x0010 +#define APPLE_MIGHTYMOUSE 0x0020 +#define APPLE_INVERT_HWHEEL 0x0040 +#define APPLE_IGNORE_HIDINPUT 0x0080 +#define APPLE_NUMLOCK_EMULATION 0x0100 + +#define APPLE_FLAG_FKEY 0x01 + +static unsigned int fnmode = 1; +module_param(fnmode, uint, 0644); +MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " + "[1] = fkeyslast, 2 = fkeysfirst)"); + +struct apple_sc { + unsigned long quirks; + unsigned int fn_on; + DECLARE_BITMAP(pressed_fn, KEY_CNT); + DECLARE_BITMAP(pressed_numlock, KEY_CNT); +}; + +struct apple_key_translation { + u16 from; + u16 to; + u8 flags; +}; + +static struct apple_key_translation apple_fn_keys[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, + { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ + { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ + { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, + { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, + { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, + { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, + { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, + { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, + { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { } +}; + +static struct apple_key_translation powerbook_fn_keys[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, + { KEY_F3, KEY_MUTE, APPLE_FLAG_FKEY }, + { KEY_F4, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, + { KEY_F5, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_NUMLOCK, APPLE_FLAG_FKEY }, + { KEY_F7, KEY_SWITCHVIDEOMODE, APPLE_FLAG_FKEY }, + { KEY_F8, KEY_KBDILLUMTOGGLE, APPLE_FLAG_FKEY }, + { KEY_F9, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, + { KEY_F10, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { } +}; + +static struct apple_key_translation powerbook_numlock_keys[] = { + { KEY_J, KEY_KP1 }, + { KEY_K, KEY_KP2 }, + { KEY_L, KEY_KP3 }, + { KEY_U, KEY_KP4 }, + { KEY_I, KEY_KP5 }, + { KEY_O, KEY_KP6 }, + { KEY_7, KEY_KP7 }, + { KEY_8, KEY_KP8 }, + { KEY_9, KEY_KP9 }, + { KEY_M, KEY_KP0 }, + { KEY_DOT, KEY_KPDOT }, + { KEY_SLASH, KEY_KPPLUS }, + { KEY_SEMICOLON, KEY_KPMINUS }, + { KEY_P, KEY_KPASTERISK }, + { KEY_MINUS, KEY_KPEQUAL }, + { KEY_0, KEY_KPSLASH }, + { KEY_F6, KEY_NUMLOCK }, + { KEY_KPENTER, KEY_KPENTER }, + { KEY_BACKSPACE, KEY_BACKSPACE }, + { } +}; + +static struct apple_key_translation apple_iso_keyboard[] = { + { KEY_GRAVE, KEY_102ND }, + { KEY_102ND, KEY_GRAVE }, + { } +}; + +static struct apple_key_translation *apple_find_translation( + struct apple_key_translation *table, u16 from) +{ + struct apple_key_translation *trans; + + /* Look for the translation */ + for (trans = table; trans->from; trans++) + if (trans->from == from) + return trans; + + return NULL; +} + +static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, + struct hid_usage *usage, __s32 value) +{ + struct apple_sc *asc = hid_get_drvdata(hid); + struct apple_key_translation *trans; + + if (usage->code == KEY_FN) { + asc->fn_on = !!value; + input_event(input, usage->type, usage->code, value); + return 1; + } + + if (fnmode) { + int do_translate; + + trans = apple_find_translation((hid->product < 0x220 || + hid->product >= 0x300) ? + powerbook_fn_keys : apple_fn_keys, + usage->code); + if (trans) { + if (test_bit(usage->code, asc->pressed_fn)) + do_translate = 1; + else if (trans->flags & APPLE_FLAG_FKEY) + do_translate = (fnmode == 2 && asc->fn_on) || + (fnmode == 1 && !asc->fn_on); + else + do_translate = asc->fn_on; + + if (do_translate) { + if (value) + set_bit(usage->code, asc->pressed_fn); + else + clear_bit(usage->code, asc->pressed_fn); + + input_event(input, usage->type, trans->to, + value); + + return 1; + } + } + + if (asc->quirks & APPLE_NUMLOCK_EMULATION && + (test_bit(usage->code, asc->pressed_numlock) || + test_bit(LED_NUML, input->led))) { + trans = apple_find_translation(powerbook_numlock_keys, + usage->code); + + if (trans) { + if (value) + set_bit(usage->code, + asc->pressed_numlock); + else + clear_bit(usage->code, + asc->pressed_numlock); + + input_event(input, usage->type, trans->to, + value); + } + + return 1; + } + } + + if (asc->quirks & APPLE_ISO_KEYBOARD) { + trans = apple_find_translation(apple_iso_keyboard, usage->code); + if (trans) { + input_event(input, usage->type, trans->to, value); + return 1; + } + } + + return 0; +} + +static int apple_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + if ((asc->quirks & APPLE_INVERT_HWHEEL) && + usage->code == REL_HWHEEL) { + input_event(field->hidinput->input, usage->type, usage->code, + -value); + return 1; + } + + if ((asc->quirks & APPLE_HAS_FN) && + hidinput_apple_event(hdev, field->hidinput->input, + usage, value)) + return 1; + + + return 0; +} + +/* + * MacBook JIS keyboard has wrong logical maximum + */ +static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 && + rdesc[53] == 0x65 && rdesc[59] == 0x65) { + dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report " + "descriptor\n"); + rdesc[53] = rdesc[59] = 0xe7; + } +} + +static void apple_setup_input(struct input_dev *input) +{ + struct apple_key_translation *trans; + + set_bit(KEY_NUMLOCK, input->keybit); + + /* Enable all needed keys */ + for (trans = apple_fn_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = powerbook_fn_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = powerbook_numlock_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = apple_iso_keyboard; trans->from; trans++) + set_bit(trans->to, input->keybit); +} + +static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->hid == (HID_UP_CUSTOM | 0x0003)) { + /* The fn key on Apple USB keyboards */ + set_bit(EV_REP, hi->input->evbit); + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN); + apple_setup_input(hi->input); + return 1; + } + + /* we want the hid layer to go through standard path (set and ignore) */ + return 0; +} + +static int apple_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + if (asc->quirks & APPLE_MIGHTYMOUSE) { + if (usage->hid == HID_GD_Z) + hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL); + else if (usage->code == BTN_1) + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_2); + else if (usage->code == BTN_2) + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_1); + } + + return 0; +} + +static int apple_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + struct apple_sc *asc; + int ret; + + /* return something else or move to hid layer? device will reside + allocated */ + if (id->bus == BUS_USB && (quirks & APPLE_IGNORE_MOUSE) && + to_usb_interface(hdev->dev.parent)->cur_altsetting-> + desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + return -ENODEV; + + asc = kzalloc(sizeof(*asc), GFP_KERNEL); + if (asc == NULL) { + dev_err(&hdev->dev, "can't alloc apple descriptor\n"); + return -ENOMEM; + } + + asc->quirks = quirks; + + hid_set_drvdata(hdev, asc); + + if (quirks & APPLE_HIDDEV) + hdev->quirks |= HID_QUIRK_HIDDEV; + if (quirks & APPLE_IGNORE_HIDINPUT) + hdev->quirks |= HID_QUIRK_IGNORE_HIDINPUT; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + kfree(asc); + return ret; +} + +static void apple_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); +} + +static const struct hid_device_id apple_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4), + .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), + .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, + + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS}, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + + /* Apple wireless Mighty Mouse */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c), + .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, + + { } +}; +MODULE_DEVICE_TABLE(hid, apple_devices); + +static struct hid_driver apple_driver = { + .name = "apple", + .id_table = apple_devices, + .report_fixup = apple_report_fixup, + .probe = apple_probe, + .remove = apple_remove, + .event = apple_event, + .input_mapping = apple_input_mapping, + .input_mapped = apple_input_mapped, +}; + +static int apple_init(void) +{ + int ret; + + ret = hid_register_driver(&apple_driver); + if (ret) + printk(KERN_ERR "can't register apple driver\n"); + + return ret; +} + +static void apple_exit(void) +{ + hid_unregister_driver(&apple_driver); +} + +module_init(apple_init); +module_exit(apple_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 08470ab295b1..8e3c264c9b2b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1130,6 +1130,36 @@ static const struct hid_device_id *hid_match_id(struct hid_device *hdev, } static const struct hid_device_id hid_blacklist[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, @@ -1144,6 +1174,8 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, + + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, { } }; diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 10451ca3a786..878e193c6d75 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -331,19 +331,11 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc return 1; } - if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) { - input_event(input, usage->type, usage->code, -value); - return 1; - } - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) { input_event(input, usage->type, REL_HWHEEL, value); return 1; } - if ((hid->quirks & HID_QUIRK_APPLE_HAS_FN) && hidinput_apple_event(hid, input, usage, value)) - return 1; - /* Handling MS keyboards special buttons */ if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 4f2bac010f59..76ddf23f1965 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -32,11 +32,6 @@ #include #include -static int hid_apple_fnmode = 1; -module_param_named(pb_fnmode, hid_apple_fnmode, int, 0644); -MODULE_PARM_DESC(pb_fnmode, - "Mode of fn key on Apple keyboards (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)"); - #define unk KEY_UNKNOWN static const unsigned char hid_keyboard[256] = { @@ -73,202 +68,6 @@ static const struct { #define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ &max, EV_KEY, (c)) -#ifdef CONFIG_USB_HIDINPUT_POWERBOOK - -struct hidinput_key_translation { - u16 from; - u16 to; - u8 flags; -}; - -#define APPLE_FLAG_FKEY 0x01 - -static struct hidinput_key_translation apple_fn_keys[] = { - { KEY_BACKSPACE, KEY_DELETE }, - { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, - { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, - { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ - { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ - { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, - { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, - { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, - { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, - { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, - { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, - { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, - { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, - { KEY_UP, KEY_PAGEUP }, - { KEY_DOWN, KEY_PAGEDOWN }, - { KEY_LEFT, KEY_HOME }, - { KEY_RIGHT, KEY_END }, - { } -}; - -static struct hidinput_key_translation powerbook_fn_keys[] = { - { KEY_BACKSPACE, KEY_DELETE }, - { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, - { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, - { KEY_F3, KEY_MUTE, APPLE_FLAG_FKEY }, - { KEY_F4, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, - { KEY_F5, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, - { KEY_F6, KEY_NUMLOCK, APPLE_FLAG_FKEY }, - { KEY_F7, KEY_SWITCHVIDEOMODE, APPLE_FLAG_FKEY }, - { KEY_F8, KEY_KBDILLUMTOGGLE, APPLE_FLAG_FKEY }, - { KEY_F9, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, - { KEY_F10, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, - { KEY_UP, KEY_PAGEUP }, - { KEY_DOWN, KEY_PAGEDOWN }, - { KEY_LEFT, KEY_HOME }, - { KEY_RIGHT, KEY_END }, - { } -}; - -static struct hidinput_key_translation powerbook_numlock_keys[] = { - { KEY_J, KEY_KP1 }, - { KEY_K, KEY_KP2 }, - { KEY_L, KEY_KP3 }, - { KEY_U, KEY_KP4 }, - { KEY_I, KEY_KP5 }, - { KEY_O, KEY_KP6 }, - { KEY_7, KEY_KP7 }, - { KEY_8, KEY_KP8 }, - { KEY_9, KEY_KP9 }, - { KEY_M, KEY_KP0 }, - { KEY_DOT, KEY_KPDOT }, - { KEY_SLASH, KEY_KPPLUS }, - { KEY_SEMICOLON, KEY_KPMINUS }, - { KEY_P, KEY_KPASTERISK }, - { KEY_MINUS, KEY_KPEQUAL }, - { KEY_0, KEY_KPSLASH }, - { KEY_F6, KEY_NUMLOCK }, - { KEY_KPENTER, KEY_KPENTER }, - { KEY_BACKSPACE, KEY_BACKSPACE }, - { } -}; - -static struct hidinput_key_translation apple_iso_keyboard[] = { - { KEY_GRAVE, KEY_102ND }, - { KEY_102ND, KEY_GRAVE }, - { } -}; - -static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from) -{ - struct hidinput_key_translation *trans; - - /* Look for the translation */ - for (trans = table; trans->from; trans++) - if (trans->from == from) - return trans; - - return NULL; -} - -int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, - struct hid_usage *usage, __s32 value) -{ - struct hidinput_key_translation *trans; - - if (usage->code == KEY_FN) { - if (value) hid->quirks |= HID_QUIRK_APPLE_FN_ON; - else hid->quirks &= ~HID_QUIRK_APPLE_FN_ON; - - input_event(input, usage->type, usage->code, value); - - return 1; - } - - if (hid_apple_fnmode) { - int do_translate; - - trans = find_translation((hid->product < 0x220 || - hid->product >= 0x300) ? - powerbook_fn_keys : apple_fn_keys, - usage->code); - if (trans) { - if (test_bit(usage->code, hid->apple_pressed_fn)) - do_translate = 1; - else if (trans->flags & APPLE_FLAG_FKEY) - do_translate = - (hid_apple_fnmode == 2 && (hid->quirks & HID_QUIRK_APPLE_FN_ON)) || - (hid_apple_fnmode == 1 && !(hid->quirks & HID_QUIRK_APPLE_FN_ON)); - else - do_translate = (hid->quirks & HID_QUIRK_APPLE_FN_ON); - - if (do_translate) { - if (value) - set_bit(usage->code, hid->apple_pressed_fn); - else - clear_bit(usage->code, hid->apple_pressed_fn); - - input_event(input, usage->type, trans->to, value); - - return 1; - } - } - - if (hid->quirks & HID_QUIRK_APPLE_NUMLOCK_EMULATION && ( - test_bit(usage->code, hid->pb_pressed_numlock) || - test_bit(LED_NUML, input->led))) { - trans = find_translation(powerbook_numlock_keys, usage->code); - - if (trans) { - if (value) - set_bit(usage->code, hid->pb_pressed_numlock); - else - clear_bit(usage->code, hid->pb_pressed_numlock); - - input_event(input, usage->type, trans->to, value); - } - - return 1; - } - } - - if (hid->quirks & HID_QUIRK_APPLE_ISO_KEYBOARD) { - trans = find_translation(apple_iso_keyboard, usage->code); - if (trans) { - input_event(input, usage->type, trans->to, value); - return 1; - } - } - - return 0; -} - -static void hidinput_apple_setup(struct input_dev *input) -{ - struct hidinput_key_translation *trans; - - set_bit(KEY_NUMLOCK, input->keybit); - - /* Enable all needed keys */ - for (trans = apple_fn_keys; trans->from; trans++) - set_bit(trans->to, input->keybit); - - for (trans = powerbook_fn_keys; trans->from; trans++) - set_bit(trans->to, input->keybit); - - for (trans = powerbook_numlock_keys; trans->from; trans++) - set_bit(trans->to, input->keybit); - - for (trans = apple_iso_keyboard; trans->from; trans++) - set_bit(trans->to, input->keybit); - -} -#else -inline int hidinput_apple_event(struct hid_device *hid, - struct input_dev *input, - struct hid_usage *usage, __s32 value) -{ - return 0; -} - -static inline void hidinput_apple_setup(struct input_dev *input) -{ -} -#endif - static inline int match_scancode(int code, int scancode) { if (scancode == 0) @@ -696,16 +495,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */ set_bit(EV_REP, input->evbit); - switch(usage->hid & HID_USAGE) { - case 0x003: - /* The fn key on Apple USB keyboards */ - map_key_clear(KEY_FN); - hidinput_apple_setup(input); - break; - - default: goto ignore; - } - break; + goto ignore; case HID_UP_LOGIVENDOR: @@ -742,15 +532,6 @@ mapped: hidinput, field, usage, &bit, &max) < 0) goto ignore; - if (device->quirks & HID_QUIRK_MIGHTYMOUSE) { - if (usage->hid == HID_GD_Z) - map_rel(REL_HWHEEL); - else if (usage->code == BTN_1) - map_key(BTN_2); - else if (usage->code == BTN_2) - map_key(BTN_1); - } - if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 | HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) && (usage->code == REL_WHEEL)) diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 18f09104765c..177bfa1a3e5b 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -24,17 +24,6 @@ config USB_HID comment "Input core support is needed for USB HID input layer or HIDBP support" depends on USB_HID && INPUT=n -config USB_HIDINPUT_POWERBOOK - bool "Enable support for Apple laptop/aluminum USB special keys" - default n - depends on USB_HID - help - Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB - keyboards. - - If unsure, say N. - config HID_FF bool "Force feedback support (EXPERIMENTAL)" depends on USB_HID && EXPERIMENTAL diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 9f5e100e95ec..972680820730 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -791,10 +791,6 @@ static int usbhid_parse(struct hid_device *hid) quirks |= HID_QUIRK_NOGET; } - if ((quirks & HID_QUIRK_IGNORE_MOUSE) && - (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) - return -ENODEV; - if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && (!interface->desc.bNumEndpoints || usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 466d608ba6ac..4c5ee9ecd40a 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -49,7 +49,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, @@ -59,8 +58,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, @@ -82,35 +79,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI, HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS, HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, { 0, 0 } @@ -129,8 +97,6 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS }, - { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, @@ -461,18 +427,6 @@ static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) printk(KERN_INFO "Fixing up Cypress report descriptor\n"); } -/* - * MacBook JIS keyboard has wrong logical maximum - */ -static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 60 && rdesc[53] == 0x65 - && rdesc[59] == 0x65) { - printk(KERN_INFO "Fixing up MacBook JIS keyboard report descriptor\n"); - rdesc[53] = rdesc[59] = 0xe7; - } -} - static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize) { if (rsize >= 30 && rdesc[29] == 0x05 @@ -515,9 +469,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if (quirks & HID_QUIRK_RDESC_PETALYNX) usbhid_fixup_petalynx_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS) - usbhid_fixup_macbook_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER) usbhid_fixup_button_consumer_descriptor(rdesc, rsize); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index 35689ef172cc..a89646a3c1fc 100644 --- a/drivers/hid/usbhid/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -31,6 +31,11 @@ #include #include +/* for apple IDs */ +#ifdef CONFIG_USB_HID_MODULE +#include "../hid-ids.h" +#endif + /* * Version Information */ diff --git a/include/linux/hid.h b/include/linux/hid.h index 0644fd33b983..75cc1531dd84 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -264,13 +264,7 @@ struct hid_item { #define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 -#define HID_QUIRK_MIGHTYMOUSE 0x00000400 -#define HID_QUIRK_APPLE_HAS_FN 0x00000800 -#define HID_QUIRK_APPLE_FN_ON 0x00001000 -#define HID_QUIRK_INVERT_HWHEEL 0x00002000 -#define HID_QUIRK_APPLE_ISO_KEYBOARD 0x00004000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 -#define HID_QUIRK_IGNORE_MOUSE 0x00020000 #define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 #define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_HIDINPUT 0x00200000 @@ -279,7 +273,6 @@ struct hid_item { #define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 #define HID_QUIRK_MICROSOFT_KEYS 0x08000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 -#define HID_QUIRK_APPLE_NUMLOCK_EMULATION 0x20000000 /* * Separate quirks for runtime report descriptor fixup @@ -288,7 +281,6 @@ struct hid_item { #define HID_QUIRK_RDESC_CYMOTION 0x00000001 #define HID_QUIRK_RDESC_SWAPPED_MIN_MAX 0x00000004 #define HID_QUIRK_RDESC_PETALYNX 0x00000008 -#define HID_QUIRK_RDESC_MACBOOK_JIS 0x00000010 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 #define HID_QUIRK_RDESC_MICROSOFT_RECV_1028 0x00000080 @@ -475,10 +467,6 @@ struct hid_device { /* device report descriptor */ /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); -#ifdef CONFIG_USB_HIDINPUT_POWERBOOK - unsigned long apple_pressed_fn[BITS_TO_LONGS(KEY_CNT)]; - unsigned long pb_pressed_numlock[BITS_TO_LONGS(KEY_CNT)]; -#endif }; static inline void *hid_get_drvdata(struct hid_device *hdev) @@ -652,7 +640,6 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int int hidinput_mapping_quirks(struct hid_usage *, struct hid_input *, unsigned long **, int *); int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); -int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 4ae3207e8a98..f3d830747b96 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -683,27 +683,6 @@ static void hidp_close(struct hid_device *hid) { } -static const struct { - __u16 idVendor; - __u16 idProduct; - unsigned quirks; -} hidp_blacklist[] = { - /* Apple wireless Mighty Mouse */ - { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, - - { } /* Terminating entry */ -}; - -static void hidp_setup_quirks(struct hid_device *hid) -{ - unsigned int n; - - for (n = 0; hidp_blacklist[n].idVendor; n++) - if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) && - hidp_blacklist[n].idProduct == le16_to_cpu(hid->product)) - hid->quirks = hidp_blacklist[n].quirks; -} - static int hidp_parse(struct hid_device *hid) { struct hidp_session *session = hid->driver_data; @@ -729,7 +708,6 @@ static int hidp_parse(struct hid_device *hid) session->req = NULL; - hidp_setup_quirks(hid); return 0; } -- cgit v1.2.3 From 02ae9a1a8bc1d08a8fd5f6a0b8bde400b0f891b9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 16 May 2008 11:49:22 +0200 Subject: HID: add compat support Add compat option to hid code to allow loading of all modules on systems which don't allow autoloading because of old userspace. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- Documentation/feature-removal-schedule.txt | 7 +++++++ drivers/hid/Kconfig | 12 ++++++++++++ drivers/hid/Makefile | 4 ++++ drivers/hid/hid-apple.c | 2 ++ drivers/hid/hid-core.c | 12 ++++++++++++ drivers/hid/hid-dummy.c | 18 ++++++++++++++++++ drivers/hid/hid-logitech.c | 2 ++ include/linux/hid.h | 17 +++++++++++++++-- 8 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 drivers/hid/hid-dummy.c (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index cc8093c15cf5..4d2566a7d168 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -287,6 +287,13 @@ Who: Glauber Costa --------------------------- +What: remove HID compat support +When: 2.6.29 +Why: needed only as a temporary solution until distros fix themselves up +Who: Jiri Slaby + +--------------------------- + What: /sys/o2cb symlink When: January 2010 Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 41283fff5145..d9d1a5671d95 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -70,6 +70,18 @@ source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" depends on HID +config HID_COMPAT + bool "Load all HID drivers on hid core load" + default y + ---help--- + Compatible option for older userspace. If you have system without udev + support of module loading through aliases and also old + module-init-tools which can't handle hid bus, choose Y here. Otherwise + say N. If you say N and your userspace is old enough, the only + functionality you loose is modules autoloading. + + If unsure, say Y. + config HID_APPLE tristate "Apple" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 4a14821ceefa..8e053eca4742 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,10 @@ obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o +ifdef CONFIG_HID_COMPAT +obj-m += hid-dummy.o +endif + obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 5642e2c685fc..2a68661fcea8 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -477,3 +477,5 @@ static void apple_exit(void) module_init(apple_init); module_exit(apple_exit); MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(apple); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8e3c264c9b2b..397e1b2ffe5a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1532,6 +1532,14 @@ void hid_unregister_driver(struct hid_driver *hdrv) } EXPORT_SYMBOL_GPL(hid_unregister_driver); +#ifdef CONFIG_HID_COMPAT +static void hid_compat_load(struct work_struct *ws) +{ + request_module("hid-dummy"); +} +static DECLARE_WORK(hid_compat_work, hid_compat_load); +#endif + static int __init hid_init(void) { int ret; @@ -1546,6 +1554,10 @@ static int __init hid_init(void) if (ret) goto err_bus; +#ifdef CONFIG_HID_COMPAT + schedule_work(&hid_compat_work); +#endif + return 0; err_bus: bus_unregister(&hid_bus_type); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c new file mode 100644 index 000000000000..b76c44efe1b8 --- /dev/null +++ b/drivers/hid/hid-dummy.c @@ -0,0 +1,18 @@ +#include +#include +#include + +static int __init hid_dummy_init(void) +{ +#ifdef CONFIG_HID_APPLE_MODULE + HID_COMPAT_CALL_DRIVER(apple); +#endif +#ifdef CONFIG_HID_LOGITECH_MODULE + HID_COMPAT_CALL_DRIVER(logitech); +#endif + + return -EIO; +} +module_init(hid_dummy_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c index 395e42ffb4d5..b2aaebe1ac05 100644 --- a/drivers/hid/hid-logitech.c +++ b/drivers/hid/hid-logitech.c @@ -310,3 +310,5 @@ static void lg_exit(void) module_init(lg_init); module_exit(lg_exit); MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(logitech); diff --git a/include/linux/hid.h b/include/linux/hid.h index 75cc1531dd84..60e44e6b86e6 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -790,10 +790,23 @@ dbg_hid(const char *fmt, ...) return 0; } #define dbg_hid_line dbg_hid -#endif +#endif /* HID_DEBUG */ #define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ __FILE__ , ## arg) -#endif +#endif /* HID_FF */ + +#ifdef CONFIG_HID_COMPAT +#define HID_COMPAT_LOAD_DRIVER(name) \ +void hid_compat_##name(void) { } \ +EXPORT_SYMBOL(hid_compat_##name) +#else +#define HID_COMPAT_LOAD_DRIVER(name) +#endif /* HID_COMPAT */ +#define HID_COMPAT_CALL_DRIVER(name) do { \ + extern void hid_compat_##name(void); \ + hid_compat_##name(); \ +} while (0) + #endif -- cgit v1.2.3 From 78a849a682a1d5ee7b7187b08abdc48656326a4e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 20 Jun 2008 21:26:11 +0200 Subject: HID: move microsoft quirks Move them from the core code to a separate driver. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 6 ++ drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-ids.h | 4 +- drivers/hid/hid-input-quirks.c | 76 -------------- drivers/hid/hid-microsoft.c | 220 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 33 ------ include/linux/hid.h | 2 - 9 files changed, 241 insertions(+), 112 deletions(-) create mode 100644 drivers/hid/hid-microsoft.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index d9d1a5671d95..8067b653f8bf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -104,6 +104,14 @@ config HID_LOGITECH Support for some Logitech devices which breaks less or more HID specification. +config HID_MICROSOFT + tristate "Microsoft" + default m + depends on USB_HID + ---help--- + Support for some Microsoft devices which breaks less or more + HID specification. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 8e053eca4742..3dc2828fb719 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -14,6 +14,7 @@ endif obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o +obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5e62e010d805..db8fbd2f5028 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1176,8 +1176,14 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { } }; diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index b76c44efe1b8..fe64d60f9010 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -10,6 +10,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_LOGITECH_MODULE HID_COMPAT_CALL_DRIVER(logitech); #endif +#ifdef CONFIG_HID_MICROSOFT_MODULE + HID_COMPAT_CALL_DRIVER(microsoft); +#endif return -EIO; } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 23d021bd95d0..d7e548dbe7c3 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -280,9 +280,11 @@ #define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d -#define USB_DEVICE_ID_DESKTOP_RECV_1028 0x00f9 #define USB_DEVICE_ID_MS_NE4K 0x00db #define USB_DEVICE_ID_MS_LK6K 0x00f9 +#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 +#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 + #define USB_VENDOR_ID_MONTEREY 0x0566 #define USB_DEVICE_ID_GENIUS_KB29E 0x3004 diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 878e193c6d75..d1b4f093dcb3 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -102,50 +102,6 @@ static int quirk_chicony_tactical_pad(struct hid_usage *usage, return 1; } -static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - struct input_dev *input = hidinput->input; - - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - switch(usage->hid & HID_USAGE) { - case 0xfd06: map_key_clear(KEY_CHAT); break; - case 0xfd07: map_key_clear(KEY_PHONE); break; - case 0xff05: - set_bit(EV_REP, input->evbit); - map_key_clear(KEY_F13); - set_bit(KEY_F14, input->keybit); - set_bit(KEY_F15, input->keybit); - set_bit(KEY_F16, input->keybit); - set_bit(KEY_F17, input->keybit); - set_bit(KEY_F18, input->keybit); - default: - return 0; - } - return 1; -} - -static int quirk_microsoft_presenter_8k(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - set_bit(EV_REP, hidinput->input->evbit); - switch(usage->hid & HID_USAGE) { - case 0xfd08: map_key_clear(KEY_FORWARD); break; - case 0xfd09: map_key_clear(KEY_BACK); break; - case 0xfd0b: map_key_clear(KEY_PLAYPAUSE); break; - case 0xfd0e: map_key_clear(KEY_CLOSE); break; - case 0xfd0f: map_key_clear(KEY_PLAY); break; - default: - return 0; - } - return 1; -} - static int quirk_petalynx_remote(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { @@ -244,12 +200,6 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage, #define VENDOR_ID_GYRATION 0x0c16 #define DEVICE_ID_GYRATION_REMOTE 0x0002 -#define VENDOR_ID_MICROSOFT 0x045e -#define DEVICE_ID_MS4K 0x00db -#define DEVICE_ID_MS6K 0x00f9 -#define DEVICE_IS_MS_PRESENTER_8K_BT 0x0701 -#define DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 - #define VENDOR_ID_MONTEREY 0x0566 #define DEVICE_ID_GENIUS_KB29E 0x3004 @@ -275,11 +225,6 @@ static const struct hid_input_blacklist { { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb }, - { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k }, - { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, @@ -336,27 +281,6 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc return 1; } - /* Handling MS keyboards special buttons */ - if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && - usage->hid == (HID_UP_MSVENDOR | 0xff05)) { - int key = 0; - static int last_key = 0; - switch (value) { - case 0x01: key = KEY_F14; break; - case 0x02: key = KEY_F15; break; - case 0x04: key = KEY_F16; break; - case 0x08: key = KEY_F17; break; - case 0x10: key = KEY_F18; break; - default: break; - } - if (key) { - input_event(input, usage->type, key, 1); - last_key = key; - } else { - input_event(input, usage->type, last_key, 0); - } - } - /* handle the temporary quirky mapping to HWHEEL */ if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT && usage->type == EV_REL && usage->code == REL_HWHEEL) { diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c new file mode 100644 index 000000000000..1fa8b813d441 --- /dev/null +++ b/drivers/hid/hid-microsoft.c @@ -0,0 +1,220 @@ +/* + * HID driver for some microsoft "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define MS_HIDINPUT 0x01 +#define MS_ERGONOMY 0x02 +#define MS_PRESENTER 0x04 +#define MS_RDESC 0x08 +#define MS_NOGET 0x10 + +/* + * Microsoft Wireless Desktop Receiver (Model 1028) has several + * 'Usage Min/Max' where it ought to have 'Physical Min/Max' + */ +static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 && + rdesc[286] == 0x2a && rdesc[304] == 0x19 && + rdesc[306] == 0x29 && rdesc[352] == 0x1a && + rdesc[355] == 0x2a && rdesc[557] == 0x19 && + rdesc[559] == 0x29) { + dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver " + "Model 1028 report descriptor\n"); + rdesc[284] = rdesc[304] = rdesc[557] = 0x35; + rdesc[352] = 0x36; + rdesc[286] = rdesc[355] = 0x46; + rdesc[306] = rdesc[559] = 0x45; + } +} + +#define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct input_dev *input = hi->input; + + switch (usage->hid & HID_USAGE) { + case 0xfd06: ms_map_key_clear(KEY_CHAT); break; + case 0xfd07: ms_map_key_clear(KEY_PHONE); break; + case 0xff05: + set_bit(EV_REP, input->evbit); + ms_map_key_clear(KEY_F13); + set_bit(KEY_F14, input->keybit); + set_bit(KEY_F15, input->keybit); + set_bit(KEY_F16, input->keybit); + set_bit(KEY_F17, input->keybit); + set_bit(KEY_F18, input->keybit); + default: + return 0; + } + return 1; +} + +static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0xfd08: ms_map_key_clear(KEY_FORWARD); break; + case 0xfd09: ms_map_key_clear(KEY_BACK); break; + case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE); break; + case 0xfd0e: ms_map_key_clear(KEY_CLOSE); break; + case 0xfd0f: ms_map_key_clear(KEY_PLAY); break; + default: + return 0; + } + return 1; +} + +static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) + return 0; + + if (quirks & MS_ERGONOMY) { + int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max); + if (ret) + return ret; + } + + if ((quirks & MS_PRESENTER) && + ms_presenter_8k_quirk(hi, usage, bit, max)) + return 1; + + return 0; +} + +static int ms_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + /* Handling MS keyboards special buttons */ + if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { + struct input_dev *input = field->hidinput->input; + static unsigned int last_key = 0; + unsigned int key = 0; + switch (value) { + case 0x01: key = KEY_F14; break; + case 0x02: key = KEY_F15; break; + case 0x04: key = KEY_F16; break; + case 0x08: key = KEY_F17; break; + case 0x10: key = KEY_F18; break; + } + if (key) { + input_event(input, usage->type, key, 1); + last_key = key; + } else + input_event(input, usage->type, last_key, 0); + + return 1; + } + + return 0; +} + +static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + if (quirks & MS_HIDINPUT) + hdev->quirks |= HID_QUIRK_HIDINPUT; + if (quirks & MS_NOGET) + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id ms_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV), + .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K), + .driver_data = MS_ERGONOMY }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), + .driver_data = MS_ERGONOMY | MS_RDESC }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), + .driver_data = MS_PRESENTER }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0), + .driver_data = MS_NOGET }, + + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), + .driver_data = MS_PRESENTER }, + { } +}; +MODULE_DEVICE_TABLE(hid, ms_devices); + +static struct hid_driver ms_driver = { + .name = "microsoft", + .id_table = ms_devices, + .report_fixup = ms_report_fixup, + .input_mapping = ms_input_mapping, + .event = ms_event, + .probe = ms_probe, +}; + +static int ms_init(void) +{ + return hid_register_driver(&ms_driver); +} + +static void ms_exit(void) +{ + hid_unregister_driver(&ms_driver); +} + +module_init(ms_init); +module_exit(ms_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(microsoft); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 4c5ee9ecd40a..1614ed2efc15 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -50,14 +50,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT }, - - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, @@ -70,7 +65,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, @@ -93,8 +87,6 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 }, - { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, @@ -436,28 +428,6 @@ static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rs } } -/* - * Microsoft Wireless Desktop Receiver (Model 1028) has several - * 'Usage Min/Max' where it ought to have 'Physical Min/Max' - */ -static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize == 571 && rdesc[284] == 0x19 - && rdesc[286] == 0x2a - && rdesc[304] == 0x19 - && rdesc[306] == 0x29 - && rdesc[352] == 0x1a - && rdesc[355] == 0x2a - && rdesc[557] == 0x19 - && rdesc[559] == 0x29) { - printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n"); - rdesc[284] = rdesc[304] = rdesc[557] = 0x35; - rdesc[352] = 0x36; - rdesc[286] = rdesc[355] = 0x46; - rdesc[306] = rdesc[559] = 0x45; - } -} - static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { if ((quirks & HID_QUIRK_RDESC_CYMOTION)) @@ -475,9 +445,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_MICROSOFT_RECV_1028) - usbhid_fixup_microsoft_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP) usbhid_fixup_sunplus_wdesktop(rdesc, rsize); } diff --git a/include/linux/hid.h b/include/linux/hid.h index 60e44e6b86e6..1f1edd886ef8 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -271,7 +271,6 @@ struct hid_item { #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 #define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000 #define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 -#define HID_QUIRK_MICROSOFT_KEYS 0x08000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 /* @@ -283,7 +282,6 @@ struct hid_item { #define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -#define HID_QUIRK_RDESC_MICROSOFT_RECV_1028 0x00000080 #define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP 0x00000100 /* -- cgit v1.2.3 From 90231e7eaf752856a2c13f786f36ec7f641bad28 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 23 Jun 2008 21:56:07 +0200 Subject: HID: move sunplus quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 ++ drivers/hid/hid-input-quirks.c | 20 ---------- drivers/hid/hid-sunplus.c | 82 +++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 16 -------- include/linux/hid.h | 1 - 8 files changed, 94 insertions(+), 37 deletions(-) create mode 100644 drivers/hid/hid-sunplus.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 8067b653f8bf..147223993b86 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -112,6 +112,13 @@ config HID_MICROSOFT Support for some Microsoft devices which breaks less or more HID specification. +config HID_SUNPLUS + tristate "Sunplus" + default m + depends on USB_HID + ---help--- + Support for Sunplus WDesktop input device. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 3dc2828fb719..314d5b0c70d5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -15,6 +15,7 @@ endif obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o +obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index db8fbd2f5028..e9e6dbbaaa83 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1181,6 +1181,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index fe64d60f9010..0c833d920c3d 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -13,6 +13,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_MICROSOFT_MODULE HID_COMPAT_CALL_DRIVER(microsoft); #endif +#ifdef CONFIG_HID_SUNPLUS_MODULE + HID_COMPAT_CALL_DRIVER(sunplus); +#endif return -EIO; } diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index d1b4f093dcb3..a5e7163158fb 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -170,21 +170,6 @@ static int quirk_btc_8193(struct hid_usage *usage, struct hid_input *hidinput, return 1; } -static int quirk_sunplus_wdesktop(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x2003: map_key_clear(KEY_ZOOMIN); break; - case 0x2103: map_key_clear(KEY_ZOOMOUT); break; - default: - return 0; - } - return 1; -} - #define VENDOR_ID_BELKIN 0x1020 #define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006 @@ -206,9 +191,6 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage, #define VENDOR_ID_PETALYNX 0x18b1 #define DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 -#define VENDOR_ID_SUNPLUS 0x04fc -#define DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 - static const struct hid_input_blacklist { __u16 idVendor; __u16 idProduct; @@ -229,8 +211,6 @@ static const struct hid_input_blacklist { { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, - { VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop }, - { 0, 0, NULL } }; diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c new file mode 100644 index 000000000000..5ba68f7dbb78 --- /dev/null +++ b/drivers/hid/hid-sunplus.c @@ -0,0 +1,82 @@ +/* + * HID driver for some sunplus "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" + +static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 && + rdesc[106] == 0x03) { + dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop " + "report descriptor\n"); + rdesc[105] = rdesc[110] = 0x03; + rdesc[106] = rdesc[111] = 0x21; + } +} + +#define sp_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int sp_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x2003: sp_map_key_clear(KEY_ZOOMIN); break; + case 0x2103: sp_map_key_clear(KEY_ZOOMOUT); break; + default: + return 0; + } + return 1; +} + +static const struct hid_device_id sp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { } +}; +MODULE_DEVICE_TABLE(hid, sp_devices); + +static struct hid_driver sp_driver = { + .name = "sunplus", + .id_table = sp_devices, + .report_fixup = sp_report_fixup, + .input_mapping = sp_input_mapping, +}; + +static int sp_init(void) +{ + return hid_register_driver(&sp_driver); +} + +static void sp_exit(void) +{ + hid_unregister_driver(&sp_driver); +} + +module_init(sp_init); +module_exit(sp_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(sunplus); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1614ed2efc15..7f38c21e146b 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -93,8 +93,6 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, - { USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP, HID_QUIRK_RDESC_SUNPLUS_WDESKTOP }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, @@ -342,17 +340,6 @@ static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize) } } -static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize) -{ - if (rsize >= 107 && rdesc[104] == 0x26 - && rdesc[105] == 0x80 - && rdesc[106] == 0x03) { - printk(KERN_INFO "Fixing up Sunplus Wireless Desktop report descriptor\n"); - rdesc[105] = rdesc[110] = 0x03; - rdesc[106] = rdesc[111] = 0x21; - } -} - /* * Samsung IrDA remote controller (reports as Cypress USB Mouse). * @@ -444,9 +431,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP) - usbhid_fixup_sunplus_wdesktop(rdesc, rsize); } /** diff --git a/include/linux/hid.h b/include/linux/hid.h index 1f1edd886ef8..a83d211021de 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -282,7 +282,6 @@ struct hid_item { #define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -#define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP 0x00000100 /* * This is the global environment of the parser. This information is -- cgit v1.2.3 From 0f2213208f8da51bcb665309e3468f000489c04f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 23 Jun 2008 22:54:08 +0200 Subject: HID: move cypress quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 3 + drivers/hid/hid-cypress.c | 158 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-input-quirks.c | 4 +- drivers/hid/hid-input.c | 6 +- drivers/hid/usbhid/hid-quirks.c | 31 -------- include/linux/hid.h | 2 - 9 files changed, 177 insertions(+), 38 deletions(-) create mode 100644 drivers/hid/hid-cypress.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 147223993b86..69f3420882a6 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -96,6 +96,13 @@ config HID_APPLE If unsure, say M. +config HID_CYPRESS + tristate "Cypress" + default m + depends on USB_HID + ---help--- + Support for Cypress mouse and barcodes. + config HID_LOGITECH tristate "Logitech" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 314d5b0c70d5..061066201186 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -13,6 +13,7 @@ obj-m += hid-dummy.o endif obj-$(CONFIG_HID_APPLE) += hid-apple.o +obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e9e6dbbaaa83..331670b32e68 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1162,6 +1162,9 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c new file mode 100644 index 000000000000..a1e13f15f0a7 --- /dev/null +++ b/drivers/hid/hid-cypress.c @@ -0,0 +1,158 @@ +/* + * HID driver for some cypress "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define CP_RDESC_SWAPPED_MIN_MAX 0x01 +#define CP_2WHEEL_MOUSE_HACK 0x02 +#define CP_2WHEEL_MOUSE_HACK_ON 0x04 + +/* + * Some USB barcode readers from cypress have usage min and usage max in + * the wrong order + */ +static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + unsigned int i; + + if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX)) + return; + + for (i = 0; i < rsize - 4; i++) + if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { + __u8 tmp; + + rdesc[i] = 0x19; + rdesc[i + 2] = 0x29; + tmp = rdesc[i + 3]; + rdesc[i + 3] = rdesc[i + 1]; + rdesc[i + 1] = tmp; + } +} + +static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(quirks & CP_2WHEEL_MOUSE_HACK)) + return 0; + + if (usage->type == EV_REL && usage->code == REL_WHEEL) + set_bit(REL_HWHEEL, *bit); + if (usage->hid == 0x00090005) + return -1; + + return 0; +} + +static int cp_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK)) + return 0; + + if (usage->hid == 0x00090005) { + if (value) + quirks |= CP_2WHEEL_MOUSE_HACK_ON; + else + quirks &= ~CP_2WHEEL_MOUSE_HACK_ON; + hid_set_drvdata(hdev, (void *)quirks); + return 1; + } + + if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) { + struct input_dev *input = field->hidinput->input; + + input_event(input, usage->type, REL_HWHEEL, value); + return 1; + } + + return 0; +} + +static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id cp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1), + .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2), + .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE), + .driver_data = CP_2WHEEL_MOUSE_HACK }, + { } +}; +MODULE_DEVICE_TABLE(hid, cp_devices); + +static struct hid_driver cp_driver = { + .name = "cypress", + .id_table = cp_devices, + .report_fixup = cp_report_fixup, + .input_mapped = cp_input_mapped, + .event = cp_event, + .probe = cp_probe, +}; + +static int cp_init(void) +{ + return hid_register_driver(&cp_driver); +} + +static void cp_exit(void) +{ + hid_unregister_driver(&cp_driver); +} + +module_init(cp_init); +module_exit(cp_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(cypress); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 0c833d920c3d..27cffe3586f5 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -7,6 +7,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_APPLE_MODULE HID_COMPAT_CALL_DRIVER(apple); #endif +#ifdef CONFIG_HID_CYPRESS_MODULE + HID_COMPAT_CALL_DRIVER(cypress); +#endif #ifdef CONFIG_HID_LOGITECH_MODULE HID_COMPAT_CALL_DRIVER(logitech); #endif diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index a5e7163158fb..6e7314f7b998 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -236,8 +236,8 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc input = field->hidinput->input; - if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) - || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) { + if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && + (usage->hid == 0x00090007)) { if (value) hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON; else hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON; return 1; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a6223bc5c734..f1df25ab0baa 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -515,13 +515,13 @@ mapped: hidinput, field, usage, &bit, &max) < 0) goto ignore; - if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 | + if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) && (usage->code == REL_WHEEL)) set_bit(REL_HWHEEL, bit); - if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) - || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) + if ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && + (usage->hid == 0x00090007)) goto ignore; set_bit(usage->type, input->evbit); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 7f38c21e146b..da80c64fb25c 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -31,7 +31,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, @@ -93,9 +92,6 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, - { 0, 0 } }; @@ -382,30 +378,6 @@ static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize) } } -/* - * Some USB barcode readers from cypress have usage min and usage max in - * the wrong order - */ -static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) -{ - short fixed = 0; - int i; - - for (i = 0; i < rsize - 4; i++) { - if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { - unsigned char tmp; - - rdesc[i] = 0x19; rdesc[i+2] = 0x29; - tmp = rdesc[i+3]; - rdesc[i+3] = rdesc[i+1]; - rdesc[i+1] = tmp; - } - } - - if (fixed) - printk(KERN_INFO "Fixing up Cypress report descriptor\n"); -} - static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize) { if (rsize >= 30 && rdesc[29] == 0x05 @@ -420,9 +392,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if ((quirks & HID_QUIRK_RDESC_CYMOTION)) usbhid_fixup_cymotion_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX) - usbhid_fixup_cypress_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_PETALYNX) usbhid_fixup_petalynx_descriptor(rdesc, rsize); diff --git a/include/linux/hid.h b/include/linux/hid.h index a83d211021de..27bfbcc1ef81 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -262,7 +262,6 @@ struct hid_item { #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 @@ -278,7 +277,6 @@ struct hid_item { */ #define HID_QUIRK_RDESC_CYMOTION 0x00000001 -#define HID_QUIRK_RDESC_SWAPPED_MIN_MAX 0x00000004 #define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -- cgit v1.2.3 From 14a21cd459f97e3b3cc4fcde48fc5bcdb81d097e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 23 Jun 2008 23:31:09 +0200 Subject: HID: move a4tech quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-a4tech.c | 162 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 2 + drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-input-quirks.c | 25 ------- drivers/hid/hid-input.c | 9 --- drivers/hid/usbhid/hid-quirks.c | 4 - include/linux/hid.h | 5 -- 9 files changed, 175 insertions(+), 43 deletions(-) create mode 100644 drivers/hid/hid-a4tech.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 69f3420882a6..01456b1d3833 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -82,6 +82,13 @@ config HID_COMPAT If unsure, say Y. +config HID_A4TECH + tristate "A4 tech" + default m + depends on USB_HID + ---help--- + Support for A4 tech X5 and WOP-35 / Trust 450L mice. + config HID_APPLE tristate "Apple" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 061066201186..ceede11eed7c 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -12,6 +12,7 @@ ifdef CONFIG_HID_COMPAT obj-m += hid-dummy.o endif +obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c new file mode 100644 index 000000000000..26e16083a1f1 --- /dev/null +++ b/drivers/hid/hid-a4tech.c @@ -0,0 +1,162 @@ +/* + * HID driver for some a4tech "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define A4_2WHEEL_MOUSE_HACK_7 0x01 +#define A4_2WHEEL_MOUSE_HACK_B8 0x02 + +struct a4tech_sc { + unsigned long quirks; + unsigned int hw_wheel; + __s32 delayed_value; +}; + +static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + + if (usage->type == EV_REL && usage->code == REL_WHEEL) + set_bit(REL_HWHEEL, *bit); + + if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) + return -1; + + return 0; +} + +static int a4_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + struct input_dev *input; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + input = field->hidinput->input; + + if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) { + if (usage->type == EV_REL && usage->code == REL_WHEEL) { + a4->delayed_value = value; + return 1; + } + + if (usage->hid == 0x000100b8) { + input_event(input, EV_REL, value ? REL_HWHEEL : + REL_WHEEL, a4->delayed_value); + return 1; + } + } + + if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) { + a4->hw_wheel = !!value; + return 1; + } + + if (usage->code == REL_WHEEL && a4->hw_wheel) { + input_event(input, usage->type, REL_HWHEEL, value); + return 1; + } + + return 0; +} + +static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct a4tech_sc *a4; + int ret; + + a4 = kzalloc(sizeof(*a4), GFP_KERNEL); + if (a4 == NULL) { + dev_err(&hdev->dev, "can't alloc device descriptor\n"); + ret = -ENOMEM; + goto err_free; + } + + a4->quirks = id->driver_data; + + hid_set_drvdata(hdev, a4); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + kfree(a4); + return ret; +} + +static void a4_remove(struct hid_device *hdev) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + + hid_hw_stop(hdev); + kfree(a4); +} + +static const struct hid_device_id a4_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU), + .driver_data = A4_2WHEEL_MOUSE_HACK_7 }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D), + .driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, + { } +}; +MODULE_DEVICE_TABLE(hid, a4_devices); + +static struct hid_driver a4_driver = { + .name = "a4tech", + .id_table = a4_devices, + .input_mapped = a4_input_mapped, + .event = a4_event, + .probe = a4_probe, + .remove = a4_remove, +}; + +static int a4_init(void) +{ + return hid_register_driver(&a4_driver); +} + +static void a4_exit(void) +{ + hid_unregister_driver(&a4_driver); +} + +module_init(a4_init); +module_exit(a4_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(a4tech); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 331670b32e68..be582976db2c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1132,6 +1132,8 @@ static const struct hid_device_id *hid_match_id(struct hid_device *hdev, } static const struct hid_device_id hid_blacklist[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 27cffe3586f5..123f1c71cdf2 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -4,6 +4,9 @@ static int __init hid_dummy_init(void) { +#ifdef CONFIG_HID_A4TECH_MODULE + HID_COMPAT_CALL_DRIVER(a4tech); +#endif #ifdef CONFIG_HID_APPLE_MODULE HID_COMPAT_CALL_DRIVER(apple); #endif diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 6e7314f7b998..5bacf181a8ca 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -236,31 +236,6 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc input = field->hidinput->input; - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && - (usage->hid == 0x00090007)) { - if (value) hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON; - else hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON; - return 1; - } - - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) && - (usage->type == EV_REL) && - (usage->code == REL_WHEEL)) { - hid->delayed_value = value; - return 1; - } - - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) && - (usage->hid == 0x000100b8)) { - input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, hid->delayed_value); - return 1; - } - - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) { - input_event(input, usage->type, REL_HWHEEL, value); - return 1; - } - /* handle the temporary quirky mapping to HWHEEL */ if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT && usage->type == EV_REL && usage->code == REL_HWHEEL) { diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f1df25ab0baa..1d2d0827820c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -515,15 +515,6 @@ mapped: hidinput, field, usage, &bit, &max) < 0) goto ignore; - if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | - HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) && - (usage->code == REL_WHEEL)) - set_bit(REL_HWHEEL, bit); - - if ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && - (usage->hid == 0x00090007)) - goto ignore; - set_bit(usage->type, input->evbit); while (usage->code <= max && test_and_set_bit(usage->code, bit)) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index da80c64fb25c..1d12fb24829c 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -28,10 +28,6 @@ static const struct hid_blacklist { __u16 idProduct; __u32 quirks; } hid_blacklist[] = { - - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 }, - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 27bfbcc1ef81..a7cc4af2e467 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -261,14 +261,11 @@ struct hid_item { #define HID_QUIRK_HIDDEV 0x00000010 #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 #define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_HIDINPUT 0x00200000 #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000 #define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 @@ -453,8 +450,6 @@ struct hid_device { /* device report descriptor */ void *driver_data; - __s32 delayed_value; /* For A4 Tech mice hwheel quirk */ - /* hiddev event handler */ void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, struct hid_usage *, __s32); -- cgit v1.2.3 From 3b239cd739a9499da08326356add3d9d992c7911 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 24 Jun 2008 20:42:25 +0200 Subject: HID: move cherry quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-cherry.c | 87 +++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 ++ drivers/hid/hid-input-quirks.c | 21 ---------- drivers/hid/usbhid/hid-quirks.c | 19 --------- include/linux/hid.h | 1 - 8 files changed, 99 insertions(+), 41 deletions(-) create mode 100644 drivers/hid/hid-cherry.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 01456b1d3833..85aabb5d9712 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -103,6 +103,13 @@ config HID_APPLE If unsure, say M. +config HID_CHERRY + tristate "Cherry" + default m + depends on USB_HID + ---help--- + Support for Cherry Cymotion. + config HID_CYPRESS tristate "Cypress" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ceede11eed7c..cd6bd024767c 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -14,6 +14,7 @@ endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o +obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c new file mode 100644 index 000000000000..b833b9769aba --- /dev/null +++ b/drivers/hid/hid-cherry.c @@ -0,0 +1,87 @@ +/* + * HID driver for some cherry "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" + +/* + * Cherry Cymotion keyboard have an invalid HID report descriptor, + * that needs fixing before we can parse it. + */ +static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { + dev_info(&hdev->dev, "fixing up Cherry Cymotion report " + "descriptor\n"); + rdesc[11] = rdesc[16] = 0xff; + rdesc[12] = rdesc[17] = 0x03; + } +} + +#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x301: ch_map_key_clear(KEY_PROG1); break; + case 0x302: ch_map_key_clear(KEY_PROG2); break; + case 0x303: ch_map_key_clear(KEY_PROG3); break; + default: + return 0; + } + + return 1; +} + +static const struct hid_device_id ch_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ch_devices); + +static struct hid_driver ch_driver = { + .name = "cherry", + .id_table = ch_devices, + .report_fixup = ch_report_fixup, + .input_mapping = ch_input_mapping, +}; + +static int ch_init(void) +{ + return hid_register_driver(&ch_driver); +} + +static void ch_exit(void) +{ + hid_unregister_driver(&ch_driver); +} + +module_init(ch_init); +module_exit(ch_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(cherry); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index be582976db2c..5acdc3742851 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1164,6 +1164,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 123f1c71cdf2..178344ea651b 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -10,6 +10,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_APPLE_MODULE HID_COMPAT_CALL_DRIVER(apple); #endif +#ifdef CONFIG_HID_CHERRY_MODULE + HID_COMPAT_CALL_DRIVER(cherry); +#endif #ifdef CONFIG_HID_CYPRESS_MODULE HID_COMPAT_CALL_DRIVER(cypress); #endif diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 5bacf181a8ca..97ee75064a0e 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -38,22 +38,6 @@ static int quirk_belkin_wkbd(struct hid_usage *usage, return 1; } -static int quirk_cherry_cymotion(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x301: map_key_clear(KEY_PROG1); break; - case 0x302: map_key_clear(KEY_PROG2); break; - case 0x303: map_key_clear(KEY_PROG3); break; - default: - return 0; - } - return 1; -} - static int quirk_gyration_remote(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { @@ -173,9 +157,6 @@ static int quirk_btc_8193(struct hid_usage *usage, struct hid_input *hidinput, #define VENDOR_ID_BELKIN 0x1020 #define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006 -#define VENDOR_ID_CHERRY 0x046a -#define DEVICE_ID_CHERRY_CYMOTION 0x0023 - #define VENDOR_ID_CHICONY 0x04f2 #define DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 @@ -199,8 +180,6 @@ static const struct hid_input_blacklist { } hid_input_blacklist[] = { { VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd }, - { VENDOR_ID_CHERRY, DEVICE_ID_CHERRY_CYMOTION, quirk_cherry_cymotion }, - { VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad }, { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 }, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1d12fb24829c..0cc6e4223cd1 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -79,9 +79,6 @@ static const struct hid_rdesc_blacklist { __u16 idProduct; __u32 quirks; } hid_rdesc_blacklist[] = { - - { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION }, - { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, @@ -319,19 +316,6 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) EXPORT_SYMBOL_GPL(usbhid_lookup_quirk); -/* - * Cherry Cymotion keyboard have an invalid HID report descriptor, - * that needs fixing before we can parse it. - */ -static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize) -{ - if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { - printk(KERN_INFO "Fixing up Cherry Cymotion report descriptor\n"); - rdesc[11] = rdesc[16] = 0xff; - rdesc[12] = rdesc[17] = 0x03; - } -} - /* * Samsung IrDA remote controller (reports as Cypress USB Mouse). * @@ -385,9 +369,6 @@ static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rs static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { - if ((quirks & HID_QUIRK_RDESC_CYMOTION)) - usbhid_fixup_cymotion_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_PETALYNX) usbhid_fixup_petalynx_descriptor(rdesc, rsize); diff --git a/include/linux/hid.h b/include/linux/hid.h index a7cc4af2e467..d9ab4a3af431 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -273,7 +273,6 @@ struct hid_item { * Separate quirks for runtime report descriptor fixup */ -#define HID_QUIRK_RDESC_CYMOTION 0x00000001 #define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -- cgit v1.2.3 From 1f243e302cea1561ac881eb5d27041c5342beba4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 24 Jun 2008 21:11:21 +0200 Subject: HID: move ezkey quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 ++ drivers/hid/hid-ezkey.c | 95 +++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-input-quirks.c | 37 ---------------- drivers/hid/usbhid/hid-quirks.c | 2 - include/linux/hid.h | 1 - 8 files changed, 107 insertions(+), 40 deletions(-) create mode 100644 drivers/hid/hid-ezkey.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 85aabb5d9712..8098ad322b71 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -117,6 +117,13 @@ config HID_CYPRESS ---help--- Support for Cypress mouse and barcodes. +config HID_EZKEY + tristate "Ezkey" + default m + depends on USB_HID + ---help--- + Support for Ezkey mouse and barcodes. + config HID_LOGITECH tristate "Logitech" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cd6bd024767c..4790777f8435 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o +obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5acdc3742851..84e478df057c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1168,6 +1168,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 178344ea651b..0dbdbb73b73c 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -16,6 +16,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_CYPRESS_MODULE HID_COMPAT_CALL_DRIVER(cypress); #endif +#ifdef CONFIG_HID_EZKEY_MODULE + HID_COMPAT_CALL_DRIVER(ezkey); +#endif #ifdef CONFIG_HID_LOGITECH_MODULE HID_COMPAT_CALL_DRIVER(logitech); #endif diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c new file mode 100644 index 000000000000..deb42f931b7e --- /dev/null +++ b/drivers/hid/hid-ezkey.c @@ -0,0 +1,95 @@ +/* + * HID driver for some ezkey "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define ez_map_rel(c) hid_map_usage(hi, usage, bit, max, EV_REL, (c)) +#define ez_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c)) + +static int ez_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x230: ez_map_key(BTN_MOUSE); break; + case 0x231: ez_map_rel(REL_WHEEL); break; + /* + * this keyboard has a scrollwheel implemented in + * totally broken way. We map this usage temporarily + * to HWHEEL and handle it in the event quirk handler + */ + case 0x232: ez_map_rel(REL_HWHEEL); break; + default: + return 0; + } + return 1; +} + +static int ez_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + /* handle the temporary quirky mapping to HWHEEL */ + if (usage->type == EV_REL && usage->code == REL_HWHEEL) { + struct input_dev *input = field->hidinput->input; + input_event(input, usage->type, REL_WHEEL, -value); + return 1; + } + + return 0; +} + +static const struct hid_device_id ez_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ez_devices); + +static struct hid_driver ez_driver = { + .name = "ezkey", + .id_table = ez_devices, + .input_mapping = ez_input_mapping, + .event = ez_event, +}; + +static int ez_init(void) +{ + return hid_register_driver(&ez_driver); +} + +static void ez_exit(void) +{ + hid_unregister_driver(&ez_driver); +} + +module_init(ez_init); +module_exit(ez_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(ezkey); diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 97ee75064a0e..4cd585b64ae0 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -16,9 +16,6 @@ #include #include -#define map_rel(c) hid_map_usage(hidinput, usage, bit, max, EV_REL, (c)) -#define map_key(c) hid_map_usage(hidinput, usage, bit, max, EV_KEY, (c)) - #define map_key_clear(c) hid_map_usage_clear(hidinput, usage, bit, \ max, EV_KEY, (c)) @@ -132,37 +129,12 @@ static int quirk_cherry_genius_29e(struct hid_usage *usage, return 1; } -static int quirk_btc_8193(struct hid_usage *usage, struct hid_input *hidinput, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x230: map_key(BTN_MOUSE); break; - case 0x231: map_rel(REL_WHEEL); break; - /* - * this keyboard has a scrollwheel implemented in - * totally broken way. We map this usage temporarily - * to HWHEEL and handle it in the event quirk handler - */ - case 0x232: map_rel(REL_HWHEEL); break; - - default: - return 0; - } - return 1; -} - #define VENDOR_ID_BELKIN 0x1020 #define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006 #define VENDOR_ID_CHICONY 0x04f2 #define DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 -#define VENDOR_ID_EZKEY 0x0518 -#define DEVICE_ID_BTC_8193 0x0002 - #define VENDOR_ID_GYRATION 0x0c16 #define DEVICE_ID_GYRATION_REMOTE 0x0002 @@ -182,8 +154,6 @@ static const struct hid_input_blacklist { { VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad }, - { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 }, - { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, @@ -215,13 +185,6 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc input = field->hidinput->input; - /* handle the temporary quirky mapping to HWHEEL */ - if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT && - usage->type == EV_REL && usage->code == REL_HWHEEL) { - input_event(input, usage->type, REL_WHEEL, -value); - return 1; - } - /* Gyration MCE remote "Sleep" key */ if (hid->vendor == VENDOR_ID_GYRATION && hid->product == DEVICE_ID_GYRATION_REMOTE && diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 0cc6e4223cd1..ddc16ea159a3 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -46,8 +46,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, - { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT }, - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, diff --git a/include/linux/hid.h b/include/linux/hid.h index d9ab4a3af431..298cbcce3a2d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -266,7 +266,6 @@ struct hid_item { #define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_HIDINPUT 0x00200000 #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 -#define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 /* -- cgit v1.2.3 From 1e76253220dbe66e048e55680266dd1f4af0be85 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 24 Jun 2008 23:46:21 +0200 Subject: HID: move petalynx quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-input-quirks.c | 33 ----------- drivers/hid/hid-petalynx.c | 122 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 21 ------- include/linux/hid.h | 1 - 8 files changed, 134 insertions(+), 55 deletions(-) create mode 100644 drivers/hid/hid-petalynx.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index b093f3c83e36..04f646a90d32 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -154,6 +154,13 @@ config HID_MICROSOFT Support for some Microsoft devices which breaks less or more HID specification. +config HID_PETALYNX + tristate "Petalynx" + default m + depends on USB_HID + ---help--- + Support for Petalynx Maxter remote. + config HID_SUNPLUS tristate "Sunplus" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ffb58f8001b7..359a5bae4082 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o +obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_USB_HID) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 81e7d70260a4..2b7a08fe1718 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1191,6 +1191,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index b95476c47449..c28422c9d2b5 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -31,6 +31,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_MICROSOFT_MODULE HID_COMPAT_CALL_DRIVER(microsoft); #endif +#ifdef CONFIG_HID_PETALYNX_MODULE + HID_COMPAT_CALL_DRIVER(petalynx); +#endif #ifdef CONFIG_HID_SUNPLUS_MODULE HID_COMPAT_CALL_DRIVER(sunplus); #endif diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index d10f47765553..903162a63c4f 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -42,34 +42,6 @@ static int quirk_gyration_remote(struct hid_usage *usage, return 1; } -static int quirk_petalynx_remote(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) && - ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)) - return 0; - - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) - switch(usage->hid & HID_USAGE) { - case 0x05a: map_key_clear(KEY_TEXT); break; - case 0x05b: map_key_clear(KEY_RED); break; - case 0x05c: map_key_clear(KEY_GREEN); break; - case 0x05d: map_key_clear(KEY_YELLOW); break; - case 0x05e: map_key_clear(KEY_BLUE); break; - default: - return 0; - } - - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) - switch(usage->hid & HID_USAGE) { - case 0x0f6: map_key_clear(KEY_NEXT); break; - case 0x0fa: map_key_clear(KEY_BACK); break; - default: - return 0; - } - return 1; -} - static int quirk_cherry_genius_29e(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { @@ -94,9 +66,6 @@ static int quirk_cherry_genius_29e(struct hid_usage *usage, #define VENDOR_ID_MONTEREY 0x0566 #define DEVICE_ID_GENIUS_KB29E 0x3004 -#define VENDOR_ID_PETALYNX 0x18b1 -#define DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 - static const struct hid_input_blacklist { __u16 idVendor; __u16 idProduct; @@ -107,8 +76,6 @@ static const struct hid_input_blacklist { { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, - { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, - { 0, 0, NULL } }; diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c new file mode 100644 index 000000000000..4109244f1d72 --- /dev/null +++ b/drivers/hid/hid-petalynx.c @@ -0,0 +1,122 @@ +/* + * HID driver for some petalynx "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" + +/* Petalynx Maxter Remote has maximum for consumer page set too low */ +static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 && + rdesc[41] == 0x00 && rdesc[59] == 0x26 && + rdesc[60] == 0xf9 && rdesc[61] == 0x00) { + dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report " + "descriptor\n"); + rdesc[60] = 0xfa; + rdesc[40] = 0xfa; + } +} + +#define pl_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int pl_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) { + switch (usage->hid & HID_USAGE) { + case 0x05a: pl_map_key_clear(KEY_TEXT); break; + case 0x05b: pl_map_key_clear(KEY_RED); break; + case 0x05c: pl_map_key_clear(KEY_GREEN); break; + case 0x05d: pl_map_key_clear(KEY_YELLOW); break; + case 0x05e: pl_map_key_clear(KEY_BLUE); break; + default: + return 0; + } + return 1; + } + + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { + switch (usage->hid & HID_USAGE) { + case 0x0f6: pl_map_key_clear(KEY_NEXT); break; + case 0x0fa: pl_map_key_clear(KEY_BACK); break; + default: + return 0; + } + return 1; + } + + return 0; +} + +static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id pl_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, pl_devices); + +static struct hid_driver pl_driver = { + .name = "petalynx", + .id_table = pl_devices, + .report_fixup = pl_report_fixup, + .input_mapping = pl_input_mapping, + .probe = pl_probe, +}; + +static int pl_init(void) +{ + return hid_register_driver(&pl_driver); +} + +static void pl_exit(void) +{ + hid_unregister_driver(&pl_driver); +} + +module_init(pl_init); +module_exit(pl_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(petalynx); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index c344ec2fad5d..3f977abb62b1 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -57,7 +57,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, @@ -78,8 +77,6 @@ static const struct hid_rdesc_blacklist { } hid_rdesc_blacklist[] = { { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, - { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, - { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, { 0, 0 } @@ -340,21 +337,6 @@ static void usbhid_fixup_samsung_irda_descriptor(unsigned char *rdesc, } } -/* Petalynx Maxter Remote has maximum for consumer page set too low */ -static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 60 && rdesc[39] == 0x2a - && rdesc[40] == 0xf5 - && rdesc[41] == 0x00 - && rdesc[59] == 0x26 - && rdesc[60] == 0xf9 - && rdesc[61] == 0x00) { - printk(KERN_INFO "Fixing up Petalynx Maxter Remote report descriptor\n"); - rdesc[60] = 0xfa; - rdesc[40] = 0xfa; - } -} - static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize) { if (rsize >= 30 && rdesc[29] == 0x05 @@ -366,9 +348,6 @@ static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rs static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { - if (quirks & HID_QUIRK_RDESC_PETALYNX) - usbhid_fixup_petalynx_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER) usbhid_fixup_button_consumer_descriptor(rdesc, rsize); diff --git a/include/linux/hid.h b/include/linux/hid.h index 298cbcce3a2d..7f4c94ffa617 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -272,7 +272,6 @@ struct hid_item { * Separate quirks for runtime report descriptor fixup */ -#define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -- cgit v1.2.3 From 3b8006e51038ef263a0404756d9e190c9a9f74d5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 25 Jun 2008 00:07:50 +0200 Subject: HID: move monterey quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 ++ drivers/hid/hid-input-quirks.c | 23 ------------ drivers/hid/hid-monterey.c | 82 +++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 14 ------- include/linux/hid.h | 1 - 8 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 drivers/hid/hid-monterey.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 04f646a90d32..d71507a5cccb 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -154,6 +154,13 @@ config HID_MICROSOFT Support for some Microsoft devices which breaks less or more HID specification. +config HID_MONTEREY + tristate "Monterey" + default m + depends on USB_HID + ---help--- + Support for Monterey Genius KB29E. + config HID_PETALYNX tristate "Petalynx" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 359a5bae4082..48b5fb25d16a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o +obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b7a08fe1718..fa2723100450 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1191,6 +1191,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index c28422c9d2b5..1dbb454c8112 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -31,6 +31,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_MICROSOFT_MODULE HID_COMPAT_CALL_DRIVER(microsoft); #endif +#ifdef CONFIG_HID_MONTEREY_MODULE + HID_COMPAT_CALL_DRIVER(monterey); +#endif #ifdef CONFIG_HID_PETALYNX_MODULE HID_COMPAT_CALL_DRIVER(petalynx); #endif diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 903162a63c4f..1a4ba0313030 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -42,30 +42,9 @@ static int quirk_gyration_remote(struct hid_usage *usage, return 1; } -static int quirk_cherry_genius_29e(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x156: map_key_clear(KEY_WORDPROCESSOR); break; - case 0x157: map_key_clear(KEY_SPREADSHEET); break; - case 0x158: map_key_clear(KEY_PRESENTATION); break; - case 0x15c: map_key_clear(KEY_STOP); break; - - default: - return 0; - } - return 1; -} - #define VENDOR_ID_GYRATION 0x0c16 #define DEVICE_ID_GYRATION_REMOTE 0x0002 -#define VENDOR_ID_MONTEREY 0x0566 -#define DEVICE_ID_GENIUS_KB29E 0x3004 - static const struct hid_input_blacklist { __u16 idVendor; __u16 idProduct; @@ -74,8 +53,6 @@ static const struct hid_input_blacklist { } hid_input_blacklist[] = { { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, - { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, - { 0, 0, NULL } }; diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c new file mode 100644 index 000000000000..f3a85a065f18 --- /dev/null +++ b/drivers/hid/hid-monterey.c @@ -0,0 +1,82 @@ +/* + * HID driver for some monterey "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" + +static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) { + dev_info(&hdev->dev, "fixing up button/consumer in HID report " + "descriptor\n"); + rdesc[30] = 0x0c; + } +} + +#define mr_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int mr_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x156: mr_map_key_clear(KEY_WORDPROCESSOR); break; + case 0x157: mr_map_key_clear(KEY_SPREADSHEET); break; + case 0x158: mr_map_key_clear(KEY_PRESENTATION); break; + case 0x15c: mr_map_key_clear(KEY_STOP); break; + default: + return 0; + } + return 1; +} + +static const struct hid_device_id mr_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { } +}; +MODULE_DEVICE_TABLE(hid, mr_devices); + +static struct hid_driver mr_driver = { + .name = "monterey", + .id_table = mr_devices, + .report_fixup = mr_report_fixup, + .input_mapping = mr_input_mapping, +}; + +static int mr_init(void) +{ + return hid_register_driver(&mr_driver); +} + +static void mr_exit(void) +{ + hid_unregister_driver(&mr_driver); +} + +module_init(mr_init); +module_exit(mr_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(monterey); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 3f977abb62b1..cc1927b1b88a 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -75,8 +75,6 @@ static const struct hid_rdesc_blacklist { __u16 idProduct; __u32 quirks; } hid_rdesc_blacklist[] = { - { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, - { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, { 0, 0 } @@ -337,20 +335,8 @@ static void usbhid_fixup_samsung_irda_descriptor(unsigned char *rdesc, } } -static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 30 && rdesc[29] == 0x05 - && rdesc[30] == 0x09) { - printk(KERN_INFO "Fixing up button/consumer in HID report descriptor\n"); - rdesc[30] = 0x0c; - } -} - static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { - if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER) - usbhid_fixup_button_consumer_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); } diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f4c94ffa617..3a639bff4ab2 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -272,7 +272,6 @@ struct hid_item { * Separate quirks for runtime report descriptor fixup */ -#define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 /* -- cgit v1.2.3 From 980a3da6acdd577ee3ae192e868dc52fe4b7f2e5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 25 Jun 2008 22:31:48 +0200 Subject: HID: move samsung quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 ++ drivers/hid/hid-samsung.c | 101 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 33 ------------- include/linux/hid.h | 6 --- 7 files changed, 113 insertions(+), 39 deletions(-) create mode 100644 drivers/hid/hid-samsung.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4dcb92ac887a..52b617bf2b93 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -175,6 +175,13 @@ config HID_PETALYNX ---help--- Support for Petalynx Maxter remote. +config HID_SAMSUNG + tristate "Samsung" + default m + depends on USB_HID + ---help--- + Support for Samsung IR remote. + config HID_SUNPLUS tristate "Sunplus" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 50a06f71b48a..735c371ca02e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o +obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_USB_HID) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 53f30560b930..1b43af072ce0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1194,6 +1194,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 1c9e7c2814a5..cd21dcc41816 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -40,6 +40,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_PETALYNX_MODULE HID_COMPAT_CALL_DRIVER(petalynx); #endif +#ifdef CONFIG_HID_SAMSUNG_MODULE + HID_COMPAT_CALL_DRIVER(samsung); +#endif #ifdef CONFIG_HID_SUNPLUS_MODULE HID_COMPAT_CALL_DRIVER(sunplus); #endif diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c new file mode 100644 index 000000000000..8771bfae02f5 --- /dev/null +++ b/drivers/hid/hid-samsung.c @@ -0,0 +1,101 @@ +/* + * HID driver for some samsung "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" + +/* + * Samsung IrDA remote controller (reports as Cypress USB Mouse). + * + * Vendor specific report #4 has a size of 48 bit, + * and therefore is not accepted when inspecting the descriptors. + * As a workaround we reinterpret the report as: + * Variable type, count 6, size 8 bit, log. maximum 255 + * The burden to reconstruct the data is moved into user space. + */ +static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && + rdesc[177] == 0x75 && rdesc[178] == 0x30 && + rdesc[179] == 0x95 && rdesc[180] == 0x01 && + rdesc[182] == 0x40) { + dev_info(&hdev->dev, "fixing up Samsung IrDA report " + "descriptor\n"); + rdesc[176] = 0xff; + rdesc[178] = 0x08; + rdesc[180] = 0x06; + rdesc[182] = 0x42; + } +} + +static int samsung_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + hdev->quirks |= HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id samsung_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, samsung_devices); + +static struct hid_driver samsung_driver = { + .name = "samsung", + .id_table = samsung_devices, + .report_fixup = samsung_report_fixup, + .probe = samsung_probe, +}; + +static int samsung_init(void) +{ + return hid_register_driver(&samsung_driver); +} + +static void samsung_exit(void) +{ + hid_unregister_driver(&samsung_driver); +} + +module_init(samsung_init); +module_exit(samsung_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(samsung); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index cc1927b1b88a..d57a37997f9a 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -43,8 +43,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, - { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, @@ -75,8 +73,6 @@ static const struct hid_rdesc_blacklist { __u16 idProduct; __u32 quirks; } hid_rdesc_blacklist[] = { - { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, - { 0, 0 } }; @@ -308,37 +304,8 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) EXPORT_SYMBOL_GPL(usbhid_lookup_quirk); -/* - * Samsung IrDA remote controller (reports as Cypress USB Mouse). - * - * Vendor specific report #4 has a size of 48 bit, - * and therefore is not accepted when inspecting the descriptors. - * As a workaround we reinterpret the report as: - * Variable type, count 6, size 8 bit, log. maximum 255 - * The burden to reconstruct the data is moved into user space. - */ -static void usbhid_fixup_samsung_irda_descriptor(unsigned char *rdesc, - int rsize) -{ - if (rsize >= 182 && rdesc[175] == 0x25 - && rdesc[176] == 0x40 - && rdesc[177] == 0x75 - && rdesc[178] == 0x30 - && rdesc[179] == 0x95 - && rdesc[180] == 0x01 - && rdesc[182] == 0x40) { - printk(KERN_INFO "Fixing up Samsung IrDA report descriptor\n"); - rdesc[176] = 0xff; - rdesc[178] = 0x08; - rdesc[180] = 0x06; - rdesc[182] = 0x42; - } -} - static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { - if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) - usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); } /** diff --git a/include/linux/hid.h b/include/linux/hid.h index 3a639bff4ab2..5b47feecc101 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -268,12 +268,6 @@ struct hid_item { #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 -/* - * Separate quirks for runtime report descriptor fixup - */ - -#define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 - /* * This is the global environment of the parser. This information is * persistent for main-items. The global environment can be saved and -- cgit v1.2.3 From 3715ade981d524f9bb3b851a1eb81d3604a873bc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 31 Jul 2008 11:09:37 +0200 Subject: HID: remove hid-input-quirks Remove the file since these is no user now. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Makefile | 2 +- drivers/hid/hid-input-quirks.c | 49 ------------------------------------------ drivers/hid/hid-input.c | 11 +--------- include/linux/hid.h | 3 --- 4 files changed, 2 insertions(+), 63 deletions(-) delete mode 100644 drivers/hid/hid-input-quirks.c (limited to 'include/linux') diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 735c371ca02e..0141ff88008e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -1,7 +1,7 @@ # # Makefile for the HID driver # -hid-objs := hid-core.o hid-input.o hid-input-quirks.o +hid-objs := hid-core.o hid-input.o obj-$(CONFIG_HID) += hid.o diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c deleted file mode 100644 index 980e7456e260..000000000000 --- a/drivers/hid/hid-input-quirks.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * HID-input usage mapping quirks - * - * This is used to handle HID-input mappings for devices violating - * HUT 1.12 specification. - * - * Copyright (c) 2007-2008 Jiri Kosina - */ - -/* - * 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 - */ - -#include -#include - -static const struct hid_input_blacklist { - __u16 idVendor; - __u16 idProduct; - int (*quirk)(struct hid_usage *, struct hid_input *, unsigned long **, - int *); -} hid_input_blacklist[] = { - { 0, 0, NULL } -}; - -int hidinput_mapping_quirks(struct hid_usage *usage, - struct hid_input *hi, unsigned long **bit, int *max) -{ - struct hid_device *device = input_get_drvdata(hi->input); - int i = 0; - - while (hid_input_blacklist[i].quirk) { - if (hid_input_blacklist[i].idVendor == device->vendor && - hid_input_blacklist[i].idProduct == device->product) - return hid_input_blacklist[i].quirk(usage, hi, bit, - max); - i++; - } - return 0; -} - -int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) -{ - return 0; -} - - diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1d2d0827820c..0a68935c20b8 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -154,7 +154,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel { struct input_dev *input = hidinput->input; struct hid_device *device = input_get_drvdata(input); - int max = 0, code, ret; + int max = 0, code; unsigned long *bit = NULL; field->hidinput = hidinput; @@ -173,11 +173,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - /* handle input mappings for quirky devices */ - ret = hidinput_mapping_quirks(usage, hidinput, &bit, &max); - if (ret) - goto mapped; - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -590,10 +585,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct if (!usage->type) return; - /* handle input events for quirky devices */ - if (hidinput_event_quirks(hid, field, usage, value)) - return; - if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) diff --git a/include/linux/hid.h b/include/linux/hid.h index 5b47feecc101..0de5fe8894d9 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -617,9 +617,6 @@ extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); -int hidinput_mapping_quirks(struct hid_usage *, struct hid_input *, - unsigned long **, int *); -int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); -- cgit v1.2.3 From 2b88b803018dbc2e9c68cbcd1739186e0715911a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 25 Jun 2008 23:03:55 +0200 Subject: HID: remove rdesc quirk support Remove support for both dynamic and static report descriptor quirks. There is no longer rdesc code which it would support, so it's useless. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 10 -------- drivers/hid/usbhid/hid-quirks.c | 51 ----------------------------------------- include/linux/hid.h | 1 - 3 files changed, 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 78553b457a2b..e900d597bc2d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -61,12 +61,6 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " " quirks=vendorID:productID:quirks" " where vendorID, productID, and quirks are all in" " 0x-prefixed hex"); -static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; -module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444); -MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying " - " rdesc_quirks=vendorID:productID:rdesc_quirks" - " where vendorID, productID, and rdesc_quirks are all in" - " 0x-prefixed hex"); /* * Input submission and I/O error handler. */ @@ -826,10 +820,6 @@ static int usbhid_parse(struct hid_device *hid) goto err; } - usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct), rdesc, - rsize, rdesc_quirks_param); - dbg_hid("report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) dbg_hid_line(" %02x", (unsigned char) rdesc[n]); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index d57a37997f9a..f66d2e43b5d5 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -67,15 +67,6 @@ static const struct hid_blacklist { { 0, 0 } }; -/* Quirks for devices which require report descriptor fixup go here */ -static const struct hid_rdesc_blacklist { - __u16 idVendor; - __u16 idProduct; - __u32 quirks; -} hid_rdesc_blacklist[] = { - { 0, 0 } -}; - /* Dynamic HID quirks list - specified at runtime */ struct quirks_list_struct { struct hid_blacklist hid_bl_item; @@ -303,45 +294,3 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) } EXPORT_SYMBOL_GPL(usbhid_lookup_quirk); - -static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) -{ -} - -/** - * usbhid_fixup_report_descriptor: check if report descriptor needs fixup - * - * Description: - * Walks the hid_rdesc_blacklist[] array and checks whether the device - * is known to have broken report descriptor that needs to be fixed up - * prior to entering the HID parser - * - * Returns: nothing - */ -void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct, - char *rdesc, unsigned rsize, char **quirks_param) -{ - int n, m; - u16 paramVendor, paramProduct; - u32 quirks; - - /* static rdesc quirk entries */ - for (n = 0; hid_rdesc_blacklist[n].idVendor; n++) - if (hid_rdesc_blacklist[n].idVendor == idVendor && - hid_rdesc_blacklist[n].idProduct == idProduct) - __usbhid_fixup_report_descriptor(hid_rdesc_blacklist[n].quirks, - rdesc, rsize); - - /* runtime rdesc quirk entries handling */ - for (n = 0; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { - m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", - ¶mVendor, ¶mProduct, &quirks); - - if (m != 3) - printk(KERN_WARNING - "Could not parse HID quirk module param %s\n", - quirks_param[n]); - else if (paramVendor == idVendor && paramProduct == idProduct) - __usbhid_fixup_report_descriptor(quirks, rdesc, rsize); - } -} diff --git a/include/linux/hid.h b/include/linux/hid.h index 0de5fe8894d9..9eac330a1dfa 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -734,7 +734,6 @@ extern void hid_generic_exit(void); u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); -void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -- cgit v1.2.3 From bd28ce008bdc68ef5902f68d2d62cbb7fa78c415 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 25 Jun 2008 23:47:04 +0200 Subject: HID: move sony quirks Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-dummy.c | 3 ++ drivers/hid/hid-sony.c | 111 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-core.c | 30 ----------- drivers/hid/usbhid/hid-quirks.c | 2 - include/linux/hid.h | 1 - 8 files changed, 123 insertions(+), 33 deletions(-) create mode 100644 drivers/hid/hid-sony.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 52b617bf2b93..7ced6476026a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -182,6 +182,13 @@ config HID_SAMSUNG ---help--- Support for Samsung IR remote. +config HID_SONY + tristate "Sony" + default m + depends on USB_HID + ---help--- + Support for Sony PS3 controller. + config HID_SUNPLUS tristate "Sunplus" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0141ff88008e..4f39b9431ebe 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o +obj-$(CONFIG_HID_SONY) += hid-sony.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_USB_HID) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 1b43af072ce0..f527e332b596 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1195,6 +1195,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index cd21dcc41816..1ef3111f7fe7 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -43,6 +43,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_SAMSUNG_MODULE HID_COMPAT_CALL_DRIVER(samsung); #endif +#ifdef CONFIG_HID_SONY_MODULE + HID_COMPAT_CALL_DRIVER(sony); +#endif #ifdef CONFIG_HID_SUNPLUS_MODULE HID_COMPAT_CALL_DRIVER(sunplus); #endif diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c new file mode 100644 index 000000000000..97668c68f0a6 --- /dev/null +++ b/drivers/hid/hid-sony.c @@ -0,0 +1,111 @@ +/* + * HID driver for some sony "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +/* + * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller + * to "operational". Without this, the ps3 controller will not report any + * events. + */ +static int sony_set_operational(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *dev = interface_to_usbdev(intf); + __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + int ret; + char *buf = kmalloc(18, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + (3 << 8) | 0xf2, ifnum, buf, 17, + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + dev_err(&hdev->dev, "can't set operational mode\n"); + + kfree(buf); + + return ret; +} + +static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + hdev->quirks |= HID_QUIRK_HIDDEV; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + ret = sony_set_operational(hdev); + if (ret) + goto err_stop; + + return 0; +err_stop: + hid_hw_stop(hdev); +err_free: + return ret; +} + +static const struct hid_device_id sony_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, + { } +}; +MODULE_DEVICE_TABLE(hid, sony_devices); + +static struct hid_driver sony_driver = { + .name = "sony", + .id_table = sony_devices, + .probe = sony_probe, +}; + +static int sony_init(void) +{ + return hid_register_driver(&sony_driver); +} + +static void sony_exit(void) +{ + hid_unregister_driver(&sony_driver); +} + +module_init(sony_init); +module_exit(sony_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(sony); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index e900d597bc2d..b41d0110a75e 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -670,32 +670,6 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } -/* - * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller - * to "operational". Without this, the ps3 controller will not report any - * events. - */ -static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) -{ - int result; - char *buf = kmalloc(18, GFP_KERNEL); - - if (!buf) - return; - - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (3 << 8) | 0xf2, ifnum, buf, 17, - USB_CTRL_GET_TIMEOUT); - - if (result < 0) - err_hid("%s failed: %d\n", __func__, result); - - kfree(buf); -} - static int usbhid_start_finish(struct hid_device *hid) { struct usb_interface *intf = to_usb_interface(hid->dev.parent); @@ -723,10 +697,6 @@ static int usbhid_start_finish(struct hid_device *hid) if ((hid->claimed & HID_CLAIMED_INPUT)) hid_ff_init(hid); - if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) - hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), - intf->cur_altsetting->desc.bInterfaceNumber); - printk(KERN_INFO); if (hid->claimed & HID_CLAIMED_INPUT) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index f66d2e43b5d5..a154a7dc1e63 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -46,8 +46,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER | HID_QUIRK_HIDDEV }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 9eac330a1dfa..43aa51a7fa95 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -262,7 +262,6 @@ struct hid_item { #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 -#define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 #define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_HIDINPUT 0x00200000 #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 -- cgit v1.2.3 From 93c10132a7ac160df3175b53f7ee857625412165 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 27 Jun 2008 00:04:24 +0200 Subject: HID: move connect quirks Move connecting from usbhid to the hid layer and fix also hidp in that manner. This removes all the ignore/force hidinput/hiddev connecting quirks. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-a4tech.c | 2 +- drivers/hid/hid-apple.c | 13 ++++---- drivers/hid/hid-belkin.c | 6 ++-- drivers/hid/hid-core.c | 76 ++++++++++++++++++++++++++++++++++++++++++- drivers/hid/hid-cypress.c | 2 +- drivers/hid/hid-dell.c | 2 +- drivers/hid/hid-input.c | 23 ++++++------- drivers/hid/hid-logitech.c | 2 +- drivers/hid/hid-microsoft.c | 5 ++- drivers/hid/hid-petalynx.c | 2 +- drivers/hid/hid-samsung.c | 5 ++- drivers/hid/hid-sony.c | 5 ++- drivers/hid/usbhid/hid-core.c | 76 ++++--------------------------------------- drivers/hid/usbhid/hiddev.c | 20 +++++++----- include/linux/hid.h | 37 +++++++++++++++++---- include/linux/hiddev.h | 6 ++-- net/bluetooth/hidp/core.c | 3 -- 17 files changed, 159 insertions(+), 126 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 26e16083a1f1..ebca00e6c103 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -107,7 +107,7 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 2a68661fcea8..f0b177844cf8 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -309,6 +309,7 @@ static int apple_probe(struct hid_device *hdev, { unsigned long quirks = id->driver_data; struct apple_sc *asc; + unsigned int connect_mask = HID_CONNECT_DEFAULT; int ret; /* return something else or move to hid layer? device will reside @@ -328,18 +329,18 @@ static int apple_probe(struct hid_device *hdev, hid_set_drvdata(hdev, asc); - if (quirks & APPLE_HIDDEV) - hdev->quirks |= HID_QUIRK_HIDDEV; - if (quirks & APPLE_IGNORE_HIDINPUT) - hdev->quirks |= HID_QUIRK_IGNORE_HIDINPUT; - ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "parse failed\n"); goto err_free; } - ret = hid_hw_start(hdev); + if (quirks & APPLE_HIDDEV) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + if (quirks & APPLE_IGNORE_HIDINPUT) + connect_mask &= ~HID_CONNECT_HIDINPUT; + + ret = hid_hw_start(hdev, connect_mask); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index 050b9892d7ef..12c8a9ba6ed6 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c @@ -54,16 +54,14 @@ static int belkin_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(hdev, (void *)quirks); - if (quirks & BELKIN_HIDDEV) - hdev->quirks |= HID_QUIRK_HIDDEV; - ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "parse failed\n"); goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | + ((quirks & BELKIN_HIDDEV) ? HID_CONNECT_HIDDEV_FORCE : 0)); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ea5f8bc900e0..699547ce257f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1113,6 +1113,80 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i } EXPORT_SYMBOL_GPL(hid_input_report); +int hid_connect(struct hid_device *hdev, unsigned int connect_mask) +{ + static const char *types[] = { "Device", "Pointer", "Mouse", "Device", + "Joystick", "Gamepad", "Keyboard", "Keypad", + "Multi-Axis Controller" + }; + const char *type, *bus; + char buf[64]; + unsigned int i; + int len; + + if (hdev->bus != BUS_USB) + connect_mask &= ~HID_CONNECT_HIDDEV; + + if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, + connect_mask & HID_CONNECT_HIDINPUT_FORCE)) + hdev->claimed |= HID_CLAIMED_INPUT; + if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && + !hdev->hiddev_connect(hdev, + connect_mask & HID_CONNECT_HIDDEV_FORCE)) + hdev->claimed |= HID_CLAIMED_HIDDEV; + if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) + hdev->claimed |= HID_CLAIMED_HIDRAW; + + if (!hdev->claimed) { + dev_err(&hdev->dev, "claimed by neither input, hiddev nor " + "hidraw\n"); + return -ENODEV; + } + + if ((hdev->claimed & HID_CLAIMED_INPUT) && + (connect_mask & HID_CONNECT_FF) && hdev->ff_init) + hdev->ff_init(hdev); + + len = 0; + if (hdev->claimed & HID_CLAIMED_INPUT) + len += sprintf(buf + len, "input"); + if (hdev->claimed & HID_CLAIMED_HIDDEV) + len += sprintf(buf + len, "%shiddev%d", len ? "," : "", + hdev->minor); + if (hdev->claimed & HID_CLAIMED_HIDRAW) + len += sprintf(buf + len, "%shidraw%d", len ? "," : "", + ((struct hidraw *)hdev->hidraw)->minor); + + type = "Device"; + for (i = 0; i < hdev->maxcollection; i++) { + struct hid_collection *col = &hdev->collection[i]; + if (col->type == HID_COLLECTION_APPLICATION && + (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK && + (col->usage & 0xffff) < ARRAY_SIZE(types)) { + type = types[col->usage & 0xffff]; + break; + } + } + + switch (hdev->bus) { + case BUS_USB: + bus = "USB"; + break; + case BUS_BLUETOOTH: + bus = "BLUETOOTH"; + break; + default: + bus = ""; + } + + dev_info(&hdev->dev, "%s: %s HID v%x.%02x %s [%s] on %s\n", + buf, bus, hdev->version >> 8, hdev->version & 0xff, + type, hdev->name, hdev->phys); + + return 0; +} +EXPORT_SYMBOL_GPL(hid_connect); + static bool hid_match_one_id(struct hid_device *hdev, const struct hid_device_id *id) { @@ -1238,7 +1312,7 @@ static int hid_device_probe(struct device *dev) } else { /* default probe */ ret = hid_parse(hdev); if (!ret) - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); } if (ret) hdev->driver = NULL; diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index a1e13f15f0a7..5d69d27b935d 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -110,7 +110,7 @@ static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c index 5d1d54cfa87e..788faa6b6cac 100644 --- a/drivers/hid/hid-dell.c +++ b/drivers/hid/hid-dell.c @@ -34,7 +34,7 @@ static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 0a68935c20b8..7f183b7147e1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -700,7 +700,7 @@ static void hidinput_close(struct input_dev *dev) * Read all reports and initialize the absolute field values. */ -int hidinput_connect(struct hid_device *hid) +int hidinput_connect(struct hid_device *hid, unsigned int force) { struct hid_report *report; struct hid_input *hidinput = NULL; @@ -708,19 +708,20 @@ int hidinput_connect(struct hid_device *hid) int i, j, k; int max_report_type = HID_OUTPUT_REPORT; - if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT) - return -1; - INIT_LIST_HEAD(&hid->inputs); - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == HID_COLLECTION_APPLICATION || - hid->collection[i].type == HID_COLLECTION_PHYSICAL) - if (IS_INPUT_APPLICATION(hid->collection[i].usage)) - break; + if (!force) { + for (i = 0; i < hid->maxcollection; i++) { + struct hid_collection *col = &hid->collection[i]; + if (col->type == HID_COLLECTION_APPLICATION || + col->type == HID_COLLECTION_PHYSICAL) + if (IS_INPUT_APPLICATION(col->usage)) + break; + } - if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0) - return -1; + if (i == hid->maxcollection) + return -1; + } if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) max_report_type = HID_INPUT_REPORT; diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c index b2aaebe1ac05..732258241c05 100644 --- a/drivers/hid/hid-logitech.c +++ b/drivers/hid/hid-logitech.c @@ -237,7 +237,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 1fa8b813d441..d718b1607d0f 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -154,8 +154,6 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(hdev, (void *)quirks); - if (quirks & MS_HIDINPUT) - hdev->quirks |= HID_QUIRK_HIDINPUT; if (quirks & MS_NOGET) hdev->quirks |= HID_QUIRK_NOGET; @@ -165,7 +163,8 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ? + HID_CONNECT_HIDINPUT_FORCE : 0)); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index 4109244f1d72..10945fe12d50 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -80,7 +80,7 @@ static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 8771bfae02f5..15f3c0492450 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -52,15 +52,14 @@ static int samsung_probe(struct hid_device *hdev, { int ret; - hdev->quirks |= HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT; - ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "parse failed\n"); goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) | + HID_CONNECT_HIDDEV_FORCE); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 97668c68f0a6..3af8095a7de1 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -57,15 +57,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; - hdev->quirks |= HID_QUIRK_HIDDEV; - ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "parse failed\n"); goto err_free; } - ret = hid_hw_start(hdev); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | + HID_CONNECT_HIDDEV_FORCE); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b41d0110a75e..0513b60728d3 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -44,8 +44,6 @@ #define DRIVER_DESC "USB HID core driver" #define DRIVER_LICENSE "GPL" -static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", - "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; /* * Module parameters. */ @@ -670,70 +668,6 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } -static int usbhid_start_finish(struct hid_device *hid) -{ - struct usb_interface *intf = to_usb_interface(hid->dev.parent); - char path[64], *type; - unsigned int i; - - usbhid_init_reports(hid); - hid_dump_device(hid); - if (hid->quirks & HID_QUIRK_RESET_LEDS) - usbhid_set_leds(hid); - - if (!hidinput_connect(hid)) - hid->claimed |= HID_CLAIMED_INPUT; - if (!hiddev_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDDEV; - if (!hidraw_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDRAW; - - if (!hid->claimed) { - printk(KERN_ERR "HID device claimed by neither input, hiddev " - "nor hidraw\n"); - return -ENODEV; - } - - if ((hid->claimed & HID_CLAIMED_INPUT)) - hid_ff_init(hid); - - printk(KERN_INFO); - - if (hid->claimed & HID_CLAIMED_INPUT) - printk("input"); - if ((hid->claimed & HID_CLAIMED_INPUT) && - ((hid->claimed & HID_CLAIMED_HIDDEV) || - hid->claimed & HID_CLAIMED_HIDRAW)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDDEV) - printk("hiddev%d", hid->minor); - if ((hid->claimed & HID_CLAIMED_INPUT) && - (hid->claimed & HID_CLAIMED_HIDDEV) && - (hid->claimed & HID_CLAIMED_HIDRAW)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDRAW) - printk("hidraw%d", ((struct hidraw *)hid->hidraw)->minor); - - type = "Device"; - for (i = 0; i < hid->maxcollection; i++) { - if (hid->collection[i].type == HID_COLLECTION_APPLICATION && - (hid->collection[i].usage & HID_USAGE_PAGE) == - HID_UP_GENDESK && - (hid->collection[i].usage & 0xffff) < - ARRAY_SIZE(hid_types)) { - type = hid_types[hid->collection[i].usage & 0xffff]; - break; - } - } - - usb_make_path(interface_to_usbdev(intf), path, 63); - - printk(": USB HID v%x.%02x %s [%s] on %s\n", - hid->version >> 8, hid->version & 0xff, type, hid->name, path); - - return 0; -} - static int usbhid_parse(struct hid_device *hid) { struct usb_interface *intf = to_usb_interface(hid->dev.parent); @@ -923,9 +857,11 @@ static int usbhid_start(struct hid_device *hid) usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - ret = usbhid_start_finish(hid); - if (ret) - goto fail; + usbhid_init_reports(hid); + hid_dump_device(hid); + + if (hid->quirks & HID_QUIRK_RESET_LEDS) + usbhid_set_leds(hid); return 0; @@ -1000,7 +936,9 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; hid->hid_output_raw_report = usbhid_output_raw_report; + hid->ff_init = hid_ff_init; #ifdef CONFIG_USB_HIDDEV + hid->hiddev_connect = hiddev_connect; hid->hiddev_hid_event = hiddev_hid_event; hid->hiddev_report_event = hiddev_report_event; #endif diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 842e9edb888e..babd65dd46ad 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -790,21 +790,23 @@ static struct usb_class_driver hiddev_class = { /* * This is where hid.c calls us to connect a hid device to the hiddev driver */ -int hiddev_connect(struct hid_device *hid) +int hiddev_connect(struct hid_device *hid, unsigned int force) { struct hiddev *hiddev; struct usbhid_device *usbhid = hid->driver_data; - int i; int retval; - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == - HID_COLLECTION_APPLICATION && - !IS_INPUT_APPLICATION(hid->collection[i].usage)) - break; + if (!force) { + unsigned int i; + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && + !IS_INPUT_APPLICATION(hid->collection[i].usage)) + break; - if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) - return -1; + if (i == hid->maxcollection) + return -1; + } if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) return -1; diff --git a/include/linux/hid.h b/include/linux/hid.h index 43aa51a7fa95..043209f7bfcf 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -246,6 +246,19 @@ struct hid_item { #define HID_OUTPUT_REPORT 1 #define HID_FEATURE_REPORT 2 +/* + * HID connect requests + */ + +#define HID_CONNECT_HIDINPUT 0x01 +#define HID_CONNECT_HIDINPUT_FORCE 0x02 +#define HID_CONNECT_HIDRAW 0x04 +#define HID_CONNECT_HIDDEV 0x08 +#define HID_CONNECT_HIDDEV_FORCE 0x10 +#define HID_CONNECT_FF 0x20 +#define HID_CONNECT_DEFAULT (HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| \ + HID_CONNECT_HIDDEV|HID_CONNECT_FF) + /* * HID device quirks. */ @@ -258,13 +271,10 @@ struct hid_item { #define HID_QUIRK_INVERT 0x00000001 #define HID_QUIRK_NOTOUCH 0x00000002 #define HID_QUIRK_NOGET 0x00000008 -#define HID_QUIRK_HIDDEV 0x00000010 #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_RESET_LEDS 0x00100000 -#define HID_QUIRK_HIDINPUT 0x00200000 -#define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 /* @@ -439,7 +449,11 @@ struct hid_device { /* device report descriptor */ void *driver_data; + /* temporary hid_ff handling (until moved to the drivers) */ + int (*ff_init)(struct hid_device *); + /* hiddev event handler */ + int (*hiddev_connect)(struct hid_device *, unsigned int); void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); @@ -610,7 +624,7 @@ extern void hid_unregister_driver(struct hid_driver *); extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); -extern int hidinput_connect(struct hid_device *); +extern int hidinput_connect(struct hid_device *hid, unsigned int force); extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); @@ -619,6 +633,7 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); +int hid_connect(struct hid_device *hid, unsigned int connect_mask); /** * hid_map_usage - map usage input bits @@ -700,14 +715,22 @@ static inline int __must_check hid_parse(struct hid_device *hdev) * hid_hw_start - start underlaying HW * * @hdev: hid device + * @connect_mask: which outputs to connect, see HID_CONNECT_* * * Call this in probe function *after* hid_parse. This will setup HW buffers * and start the device (if not deffered to device open). hid_hw_stop must be * called if this was successfull. */ -static inline int __must_check hid_hw_start(struct hid_device *hdev) +static inline int __must_check hid_hw_start(struct hid_device *hdev, + unsigned int connect_mask) { - return hdev->ll_driver->start(hdev); + int ret = hdev->ll_driver->start(hdev); + if (ret || !connect_mask) + return ret; + ret = hid_connect(hdev, connect_mask); + if (ret) + hdev->ll_driver->stop(hdev); + return ret; } /** @@ -749,7 +772,7 @@ static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; } #endif #else -static inline int hid_ff_init(struct hid_device *hid) { return -1; } +#define hid_ff_init NULL #endif #ifdef CONFIG_HID_DEBUG diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h index a416b904ba90..6ae77b3468f9 100644 --- a/include/linux/hiddev.h +++ b/include/linux/hiddev.h @@ -217,7 +217,7 @@ struct hid_field; struct hid_report; #ifdef CONFIG_USB_HIDDEV -int hiddev_connect(struct hid_device *); +int hiddev_connect(struct hid_device *hid, unsigned int force); void hiddev_disconnect(struct hid_device *); void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value); @@ -225,7 +225,9 @@ void hiddev_report_event(struct hid_device *hid, struct hid_report *report); int __init hiddev_init(void); void hiddev_exit(void); #else -static inline int hiddev_connect(struct hid_device *hid) { return -1; } +static inline int hiddev_connect(struct hid_device *hid, + unsigned int force) +{ return -1; } static inline void hiddev_disconnect(struct hid_device *hid) { } static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { } diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index f3d830747b96..acdeab3d9807 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -724,9 +724,6 @@ static int hidp_start(struct hid_device *hid) report_list, list) hidp_send_report(session, report); - if (hidinput_connect(hid) == 0) - hid->claimed |= HID_CLAIMED_INPUT; - return 0; } -- cgit v1.2.3 From 6edfa8dc33803a49ad936ead9840e453bee6ca3b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 27 Jun 2008 20:41:02 +0200 Subject: HID: move reset leds quirk Move the handling of the leds resetting from the core to the dell and logitech drivers. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-dell.c | 4 ++-- drivers/hid/hid-logitech.c | 5 +++-- drivers/hid/usbhid/hid-core.c | 6 ++---- include/linux/hid.h | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c index 788faa6b6cac..98ee40e8751f 100644 --- a/drivers/hid/hid-dell.c +++ b/drivers/hid/hid-dell.c @@ -26,8 +26,6 @@ static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; - hdev->quirks |= HID_QUIRK_RESET_LEDS; - ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "parse failed\n"); @@ -40,6 +38,8 @@ static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } + usbhid_set_leds(hdev); + return 0; err_free: return ret; diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c index 732258241c05..df27f9aadf26 100644 --- a/drivers/hid/hid-logitech.c +++ b/drivers/hid/hid-logitech.c @@ -226,8 +226,6 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(hdev, (void *)quirks); - if (quirks & LG_RESET_LEDS) - hdev->quirks |= HID_QUIRK_RESET_LEDS; if (quirks & LG_NOGET) hdev->quirks |= HID_QUIRK_NOGET; @@ -243,6 +241,9 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } + if (quirks & LG_RESET_LEDS) + usbhid_set_leds(hdev); + return 0; err_free: return ret; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 0513b60728d3..402ace751271 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -591,7 +591,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page, return -1; } -static void usbhid_set_leds(struct hid_device *hid) +void usbhid_set_leds(struct hid_device *hid) { struct hid_field *field; int offset; @@ -601,6 +601,7 @@ static void usbhid_set_leds(struct hid_device *hid) usbhid_submit_report(hid, field->report, USB_DIR_OUT); } } +EXPORT_SYMBOL_GPL(usbhid_set_leds); /* * Traverse the supplied list of reports and find the longest @@ -860,9 +861,6 @@ static int usbhid_start(struct hid_device *hid) usbhid_init_reports(hid); hid_dump_device(hid); - if (hid->quirks & HID_QUIRK_RESET_LEDS) - usbhid_set_leds(hid); - return 0; fail: diff --git a/include/linux/hid.h b/include/linux/hid.h index 043209f7bfcf..b0f03fa2ed19 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -274,7 +274,6 @@ struct hid_item { #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 -#define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 /* @@ -756,6 +755,7 @@ extern void hid_generic_exit(void); u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); +void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -- cgit v1.2.3 From 606bd0a8616a0e59021cb2997e942513f24f641d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 4 Jul 2008 23:06:45 +0200 Subject: HID: move logitech FF processing Merge the logitech force feedback processing directly into logitech driver from the usbhid core. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 24 +++ drivers/hid/Makefile | 8 + drivers/hid/hid-core.c | 7 + drivers/hid/hid-ids.h | 7 + drivers/hid/hid-lg.c | 342 +++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-lg.h | 18 +++ drivers/hid/hid-lg2ff.c | 116 ++++++++++++++ drivers/hid/hid-lgff.c | 153 ++++++++++++++++++ drivers/hid/hid-logitech.c | 315 ------------------------------------- drivers/hid/usbhid/Kconfig | 24 --- drivers/hid/usbhid/Makefile | 6 - drivers/hid/usbhid/hid-core.c | 1 + drivers/hid/usbhid/hid-ff.c | 12 -- drivers/hid/usbhid/hid-lg2ff.c | 114 -------------- drivers/hid/usbhid/hid-lgff.c | 151 ------------------ include/linux/hid.h | 2 - 16 files changed, 676 insertions(+), 624 deletions(-) create mode 100644 drivers/hid/hid-lg.c create mode 100644 drivers/hid/hid-lg.h create mode 100644 drivers/hid/hid-lg2ff.c create mode 100644 drivers/hid/hid-lgff.c delete mode 100644 drivers/hid/hid-logitech.c delete mode 100644 drivers/hid/usbhid/hid-lg2ff.c delete mode 100644 drivers/hid/usbhid/hid-lgff.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1ab067ee7e6c..4319af320adf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -160,6 +160,30 @@ config HID_LOGITECH Support for some Logitech devices which breaks less or more HID specification. +config LOGITECH_FF + bool "Logitech force feedback" + depends on HID_LOGITECH + select INPUT_FF_MEMLESS + help + Say Y here if you have one of these devices: + - Logitech WingMan Cordless RumblePad + - Logitech WingMan Cordless RumblePad 2 + - Logitech WingMan Force 3D + - Logitech Formula Force EX + - Logitech MOMO Force wheel + + and if you want to enable force feedback for them. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config LOGIRUMBLEPAD2_FF + bool "Logitech Rumblepad 2 force feedback" + depends on HID_LOGITECH + select INPUT_FF_MEMLESS + help + Say Y here if you want to enable force feedback support for Logitech + Rumblepad 2 devices. + config HID_MICROSOFT tristate "Microsoft" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 4a756c6e3a95..300ee00913bc 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -12,6 +12,14 @@ ifdef CONFIG_HID_COMPAT obj-m += hid-dummy.o endif +hid-logitech-objs := hid-lg.o +ifdef CONFIG_LOGITECH_FF + hid-logitech-objs += hid-lgff.o +endif +ifdef CONFIG_LOGIRUMBLEPAD2_FF + hid-logitech-objs += hid-lg2ff.o +endif + obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 699547ce257f..6a730df336b4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1262,6 +1262,13 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 678f7c3f341d..e2fd52ca68b7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -263,8 +263,14 @@ #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 +#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 +#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 +#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a #define USB_DEVICE_ID_LOGITECH_KBD 0xc311 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c @@ -274,6 +280,7 @@ #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 #define USB_DEVICE_ID_DINOVO_EDGE 0xc714 #define USB_DEVICE_ID_DINOVO_MINI 0xc71f +#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03 #define USB_VENDOR_ID_MCC 0x09db #define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c new file mode 100644 index 000000000000..406d8c82abf1 --- /dev/null +++ b/drivers/hid/hid-lg.c @@ -0,0 +1,342 @@ +/* + * HID driver for some logitech "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * 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. + */ + +#include +#include +#include + +#include "hid-ids.h" +#include "hid-lg.h" + +#define LG_RDESC 0x001 +#define LG_BAD_RELATIVE_KEYS 0x002 +#define LG_DUPLICATE_USAGES 0x004 +#define LG_RESET_LEDS 0x008 +#define LG_EXPANDED_KEYMAP 0x010 +#define LG_IGNORE_DOUBLED_WHEEL 0x020 +#define LG_WIRELESS 0x040 +#define LG_INVERT_HWHEEL 0x080 +#define LG_NOGET 0x100 +#define LG_FF 0x200 +#define LG_FF2 0x400 + +/* + * Certain Logitech keyboards send in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 && + rdesc[84] == 0x8c && rdesc[85] == 0x02) { + dev_info(&hdev->dev, "fixing up Logitech keyboard report " + "descriptor\n"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + +#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) + +static int lg_ultrax_remote_mapping(struct hid_input *hi, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) + return 0; + + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + /* Reported on Logitech Ultra X Media Remote */ + case 0x004: lg_map_key_clear(KEY_AGAIN); break; + case 0x00d: lg_map_key_clear(KEY_HOME); break; + case 0x024: lg_map_key_clear(KEY_SHUFFLE); break; + case 0x025: lg_map_key_clear(KEY_TV); break; + case 0x026: lg_map_key_clear(KEY_MENU); break; + case 0x031: lg_map_key_clear(KEY_AUDIO); break; + case 0x032: lg_map_key_clear(KEY_TEXT); break; + case 0x033: lg_map_key_clear(KEY_LAST); break; + case 0x047: lg_map_key_clear(KEY_MP3); break; + case 0x048: lg_map_key_clear(KEY_DVD); break; + case 0x049: lg_map_key_clear(KEY_MEDIA); break; + case 0x04a: lg_map_key_clear(KEY_VIDEO); break; + case 0x04b: lg_map_key_clear(KEY_ANGLE); break; + case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break; + case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break; + case 0x051: lg_map_key_clear(KEY_RED); break; + case 0x052: lg_map_key_clear(KEY_CLOSE); break; + + default: + return 0; + } + return 1; +} + +static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x1001: lg_map_key_clear(KEY_MESSENGER); break; + case 0x1003: lg_map_key_clear(KEY_SOUND); break; + case 0x1004: lg_map_key_clear(KEY_VIDEO); break; + case 0x1005: lg_map_key_clear(KEY_AUDIO); break; + case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break; + case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break; + case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break; + case 0x1013: lg_map_key_clear(KEY_CAMERA); break; + case 0x1014: lg_map_key_clear(KEY_MESSENGER); break; + case 0x1015: lg_map_key_clear(KEY_RECORD); break; + case 0x1016: lg_map_key_clear(KEY_PLAYER); break; + case 0x1017: lg_map_key_clear(KEY_EJECTCD); break; + case 0x1018: lg_map_key_clear(KEY_MEDIA); break; + case 0x1019: lg_map_key_clear(KEY_PROG1); break; + case 0x101a: lg_map_key_clear(KEY_PROG2); break; + case 0x101b: lg_map_key_clear(KEY_PROG3); break; + case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break; + case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break; + case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break; + case 0x1023: lg_map_key_clear(KEY_CLOSE); break; + case 0x1027: lg_map_key_clear(KEY_MENU); break; + /* this one is marked as 'Rotate' */ + case 0x1028: lg_map_key_clear(KEY_ANGLE); break; + case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break; + case 0x102a: lg_map_key_clear(KEY_BACK); break; + case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break; + case 0x1041: lg_map_key_clear(KEY_BATTERY); break; + case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break; + case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break; + case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break; + case 0x1045: lg_map_key_clear(KEY_UNDO); break; + case 0x1046: lg_map_key_clear(KEY_REDO); break; + case 0x1047: lg_map_key_clear(KEY_PRINT); break; + case 0x1048: lg_map_key_clear(KEY_SAVE); break; + case 0x1049: lg_map_key_clear(KEY_PROG1); break; + case 0x104a: lg_map_key_clear(KEY_PROG2); break; + case 0x104b: lg_map_key_clear(KEY_PROG3); break; + case 0x104c: lg_map_key_clear(KEY_PROG4); break; + + default: + return 0; + } + return 1; +} + +static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* extended mapping for certain Logitech hardware (Logitech cordless + desktop LX500) */ + static const u8 e_keymap[] = { + 0,216, 0,213,175,156, 0, 0, 0, 0, + 144, 0, 0, 0, 0, 0, 0, 0, 0,212, + 174,167,152,161,112, 0, 0, 0,154, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,183,184,185,186,187, + 188,189,190,191,192,193,194, 0, 0, 0 + }; + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + unsigned int hid = usage->hid; + + if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && + lg_ultrax_remote_mapping(hi, usage, bit, max)) + return 1; + + if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) + return 1; + + if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return 0; + + hid &= HID_USAGE; + + /* Special handling for Logitech Cordless Desktop */ + if (field->application == HID_GD_MOUSE) { + if ((quirks & LG_IGNORE_DOUBLED_WHEEL) && + (hid == 7 || hid == 8)) + return -1; + } else { + if ((quirks & LG_EXPANDED_KEYMAP) && + hid < ARRAY_SIZE(e_keymap) && + e_keymap[hid] != 0) { + hid_map_usage(hi, usage, bit, max, EV_KEY, + e_keymap[hid]); + return 1; + } + } + + return 0; +} + +static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && + (field->flags & HID_MAIN_ITEM_RELATIVE)) + field->flags &= ~HID_MAIN_ITEM_RELATIVE; + + if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || + usage->type == EV_REL || usage->type == EV_ABS)) + clear_bit(usage->code, *bit); + + return 0; +} + +static int lg_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { + input_event(field->hidinput->input, usage->type, usage->code, + -value); + return 1; + } + + return 0; +} + +static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + unsigned int connect_mask = HID_CONNECT_DEFAULT; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + if (quirks & LG_NOGET) + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + if (quirks & (LG_FF | LG_FF2)) + connect_mask &= ~HID_CONNECT_FF; + + ret = hid_hw_start(hdev, connect_mask); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + if (quirks & LG_RESET_LEDS) + usbhid_set_leds(hdev); + + if (quirks & LG_FF) + lgff_init(hdev); + if (quirks & LG_FF2) + lg2ff_init(hdev); + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id lg_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), + .driver_data = LG_RDESC | LG_WIRELESS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), + .driver_data = LG_RDESC | LG_WIRELESS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), + .driver_data = LG_RDESC | LG_WIRELESS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), + .driver_data = LG_BAD_RELATIVE_KEYS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP), + .driver_data = LG_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE), + .driver_data = LG_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), + .driver_data = LG_DUPLICATE_USAGES }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD), + .driver_data = LG_RESET_LEDS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), + .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), + .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3), + .driver_data = LG_INVERT_HWHEEL }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150), + .driver_data = LG_INVERT_HWHEEL }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), + .driver_data = LG_NOGET }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), + .driver_data = LG_NOGET | LG_FF }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), + .driver_data = LG_FF2 }, + { } +}; +MODULE_DEVICE_TABLE(hid, lg_devices); + +static struct hid_driver lg_driver = { + .name = "logitech", + .id_table = lg_devices, + .report_fixup = lg_report_fixup, + .input_mapping = lg_input_mapping, + .input_mapped = lg_input_mapped, + .event = lg_event, + .probe = lg_probe, +}; + +static int lg_init(void) +{ + return hid_register_driver(&lg_driver); +} + +static void lg_exit(void) +{ + hid_unregister_driver(&lg_driver); +} + +module_init(lg_init); +module_exit(lg_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(logitech); diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h new file mode 100644 index 000000000000..27ae750ca878 --- /dev/null +++ b/drivers/hid/hid-lg.h @@ -0,0 +1,18 @@ +#ifndef __HID_LG_H +#define __HID_LG_H + +#include + +#ifdef CONFIG_LOGITECH_FF +int lgff_init(struct hid_device *hdev); +#else +static inline int lgff_init(struct hid_device *hdev) { return -1; } +#endif + +#ifdef CONFIG_LOGIRUMBLEPAD2_FF +int lg2ff_init(struct hid_device *hdev); +#else +static inline int lg2ff_init(struct hid_device *hdev) { return -1; } +#endif + +#endif diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c new file mode 100644 index 000000000000..b2e9a67aa652 --- /dev/null +++ b/drivers/hid/hid-lg2ff.c @@ -0,0 +1,116 @@ +/* + * Force feedback support for Logitech Rumblepad 2 + * + * Copyright (c) 2008 Anssi Hannula + */ + +/* + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include + +#include "usbhid/usbhid.h" +#include "hid-lg.h" + +struct lg2ff_device { + struct hid_report *report; +}; + +static int play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct lg2ff_device *lg2ff = data; + int weak, strong; + + strong = effect->u.rumble.strong_magnitude; + weak = effect->u.rumble.weak_magnitude; + + if (weak || strong) { + weak = weak * 0xff / 0xffff; + strong = strong * 0xff / 0xffff; + + lg2ff->report->field[0]->value[0] = 0x51; + lg2ff->report->field[0]->value[2] = weak; + lg2ff->report->field[0]->value[4] = strong; + } else { + lg2ff->report->field[0]->value[0] = 0xf3; + lg2ff->report->field[0]->value[2] = 0x00; + lg2ff->report->field[0]->value[4] = 0x00; + } + + usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT); + return 0; +} + +int lg2ff_init(struct hid_device *hid) +{ + struct lg2ff_device *lg2ff; + struct hid_report *report; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-lg2ff: no output report found\n"); + return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 1) { + printk(KERN_ERR "hid-lg2ff: output report is empty\n"); + return -ENODEV; + } + if (report->field[0]->report_count < 7) { + printk(KERN_ERR "hid-lg2ff: not enough values in the field\n"); + return -ENODEV; + } + + lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL); + if (!lg2ff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, lg2ff, play_effect); + if (error) { + kfree(lg2ff); + return error; + } + + lg2ff->report = report; + report->field[0]->value[0] = 0xf3; + report->field[0]->value[1] = 0x00; + report->field[0]->value[2] = 0x00; + report->field[0]->value[3] = 0x00; + report->field[0]->value[4] = 0x00; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + + usbhid_submit_report(hid, report, USB_DIR_OUT); + + printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by " + "Anssi Hannula \n"); + + return 0; +} diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c new file mode 100644 index 000000000000..e379c167ac9e --- /dev/null +++ b/drivers/hid/hid-lgff.c @@ -0,0 +1,153 @@ +/* + * Force feedback support for hid-compliant for some of the devices from + * Logitech, namely: + * - WingMan Cordless RumblePad + * - WingMan Force 3D + * + * Copyright (c) 2002-2004 Johann Deneux + * Copyright (c) 2006 Anssi Hannula + */ + +/* + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include +#include +#include + +#include "usbhid/usbhid.h" +#include "hid-lg.h" + +struct dev_type { + u16 idVendor; + u16 idProduct; + const signed short *ff; +}; + +static const signed short ff_rumble[] = { + FF_RUMBLE, + -1 +}; + +static const signed short ff_joystick[] = { + FF_CONSTANT, + -1 +}; + +static const struct dev_type devices[] = { + { 0x046d, 0xc211, ff_rumble }, + { 0x046d, 0xc219, ff_rumble }, + { 0x046d, 0xc283, ff_joystick }, + { 0x046d, 0xc286, ff_joystick }, + { 0x046d, 0xc294, ff_joystick }, + { 0x046d, 0xc295, ff_joystick }, + { 0x046d, 0xca03, ff_joystick }, +}; + +static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + int x, y; + unsigned int left, right; + +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff + + switch (effect->type) { + case FF_CONSTANT: + x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ + y = effect->u.ramp.end_level + 0x7f; + CLAMP(x); + CLAMP(y); + report->field[0]->value[0] = 0x51; + report->field[0]->value[1] = 0x08; + report->field[0]->value[2] = x; + report->field[0]->value[3] = y; + dbg_hid("(x, y)=(%04x, %04x)\n", x, y); + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + + case FF_RUMBLE: + right = effect->u.rumble.strong_magnitude; + left = effect->u.rumble.weak_magnitude; + right = right * 0xff / 0xffff; + left = left * 0xff / 0xffff; + CLAMP(left); + CLAMP(right); + report->field[0]->value[0] = 0x42; + report->field[0]->value[1] = 0x00; + report->field[0]->value[2] = left; + report->field[0]->value[3] = right; + dbg_hid("(left, right)=(%04x, %04x)\n", left, right); + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + } + return 0; +} + +int lgff_init(struct hid_device* hid) +{ + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; + const signed short *ff_bits = ff_joystick; + int error; + int i; + + /* Find the report to use */ + if (list_empty(report_list)) { + err_hid("No output report found"); + return -1; + } + + /* Check that the report looks ok */ + report = list_entry(report_list->next, struct hid_report, list); + if (!report) { + err_hid("NULL output report"); + return -1; + } + + field = report->field[0]; + if (!field) { + err_hid("NULL field"); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + if (dev->id.vendor == devices[i].idVendor && + dev->id.product == devices[i].idProduct) { + ff_bits = devices[i].ff; + break; + } + } + + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], dev->ffbit); + + error = input_ff_create_memless(dev, NULL, hid_lgff_play); + if (error) + return error; + + printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); + + return 0; +} diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c deleted file mode 100644 index df27f9aadf26..000000000000 --- a/drivers/hid/hid-logitech.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * HID driver for some logitech "special" devices - * - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2005 Vojtech Pavlik - * Copyright (c) 2005 Michael Haboustak for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina - * Copyright (c) 2007 Paul Walmsley - * Copyright (c) 2008 Jiri Slaby - */ - -/* - * 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. - */ - -#include -#include -#include - -#include "hid-ids.h" - -#define LG_RDESC 0x001 -#define LG_BAD_RELATIVE_KEYS 0x002 -#define LG_DUPLICATE_USAGES 0x004 -#define LG_RESET_LEDS 0x008 -#define LG_EXPANDED_KEYMAP 0x010 -#define LG_IGNORE_DOUBLED_WHEEL 0x020 -#define LG_WIRELESS 0x040 -#define LG_INVERT_HWHEEL 0x080 -#define LG_NOGET 0x100 - -/* - * Certain Logitech keyboards send in report #3 keys which are far - * above the logical maximum described in descriptor. This extends - * the original value of 0x28c of logical maximum to 0x104d - */ -static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int rsize) -{ - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - - if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 && - rdesc[84] == 0x8c && rdesc[85] == 0x02) { - dev_info(&hdev->dev, "fixing up Logitech keyboard report " - "descriptor\n"); - rdesc[84] = rdesc[89] = 0x4d; - rdesc[85] = rdesc[90] = 0x10; - } -} - -#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ - EV_KEY, (c)) - -static int lg_ultrax_remote_mapping(struct hid_input *hi, - struct hid_usage *usage, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) - return 0; - - set_bit(EV_REP, hi->input->evbit); - switch (usage->hid & HID_USAGE) { - /* Reported on Logitech Ultra X Media Remote */ - case 0x004: lg_map_key_clear(KEY_AGAIN); break; - case 0x00d: lg_map_key_clear(KEY_HOME); break; - case 0x024: lg_map_key_clear(KEY_SHUFFLE); break; - case 0x025: lg_map_key_clear(KEY_TV); break; - case 0x026: lg_map_key_clear(KEY_MENU); break; - case 0x031: lg_map_key_clear(KEY_AUDIO); break; - case 0x032: lg_map_key_clear(KEY_TEXT); break; - case 0x033: lg_map_key_clear(KEY_LAST); break; - case 0x047: lg_map_key_clear(KEY_MP3); break; - case 0x048: lg_map_key_clear(KEY_DVD); break; - case 0x049: lg_map_key_clear(KEY_MEDIA); break; - case 0x04a: lg_map_key_clear(KEY_VIDEO); break; - case 0x04b: lg_map_key_clear(KEY_ANGLE); break; - case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break; - case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break; - case 0x051: lg_map_key_clear(KEY_RED); break; - case 0x052: lg_map_key_clear(KEY_CLOSE); break; - - default: - return 0; - } - return 1; -} - -static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x1001: lg_map_key_clear(KEY_MESSENGER); break; - case 0x1003: lg_map_key_clear(KEY_SOUND); break; - case 0x1004: lg_map_key_clear(KEY_VIDEO); break; - case 0x1005: lg_map_key_clear(KEY_AUDIO); break; - case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break; - case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break; - case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break; - case 0x1013: lg_map_key_clear(KEY_CAMERA); break; - case 0x1014: lg_map_key_clear(KEY_MESSENGER); break; - case 0x1015: lg_map_key_clear(KEY_RECORD); break; - case 0x1016: lg_map_key_clear(KEY_PLAYER); break; - case 0x1017: lg_map_key_clear(KEY_EJECTCD); break; - case 0x1018: lg_map_key_clear(KEY_MEDIA); break; - case 0x1019: lg_map_key_clear(KEY_PROG1); break; - case 0x101a: lg_map_key_clear(KEY_PROG2); break; - case 0x101b: lg_map_key_clear(KEY_PROG3); break; - case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break; - case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break; - case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break; - case 0x1023: lg_map_key_clear(KEY_CLOSE); break; - case 0x1027: lg_map_key_clear(KEY_MENU); break; - /* this one is marked as 'Rotate' */ - case 0x1028: lg_map_key_clear(KEY_ANGLE); break; - case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break; - case 0x102a: lg_map_key_clear(KEY_BACK); break; - case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break; - case 0x1041: lg_map_key_clear(KEY_BATTERY); break; - case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break; - case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break; - case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break; - case 0x1045: lg_map_key_clear(KEY_UNDO); break; - case 0x1046: lg_map_key_clear(KEY_REDO); break; - case 0x1047: lg_map_key_clear(KEY_PRINT); break; - case 0x1048: lg_map_key_clear(KEY_SAVE); break; - case 0x1049: lg_map_key_clear(KEY_PROG1); break; - case 0x104a: lg_map_key_clear(KEY_PROG2); break; - case 0x104b: lg_map_key_clear(KEY_PROG3); break; - case 0x104c: lg_map_key_clear(KEY_PROG4); break; - - default: - return 0; - } - return 1; -} - -static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - /* extended mapping for certain Logitech hardware (Logitech cordless - desktop LX500) */ - static const u8 e_keymap[] = { - 0,216, 0,213,175,156, 0, 0, 0, 0, - 144, 0, 0, 0, 0, 0, 0, 0, 0,212, - 174,167,152,161,112, 0, 0, 0,154, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,183,184,185,186,187, - 188,189,190,191,192,193,194, 0, 0, 0 - }; - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - unsigned int hid = usage->hid; - - if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && - lg_ultrax_remote_mapping(hi, usage, bit, max)) - return 1; - - if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) - return 1; - - if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) - return 0; - - hid &= HID_USAGE; - - /* Special handling for Logitech Cordless Desktop */ - if (field->application == HID_GD_MOUSE) { - if ((quirks & LG_IGNORE_DOUBLED_WHEEL) && - (hid == 7 || hid == 8)) - return -1; - } else { - if ((quirks & LG_EXPANDED_KEYMAP) && - hid < ARRAY_SIZE(e_keymap) && - e_keymap[hid] != 0) { - hid_map_usage(hi, usage, bit, max, EV_KEY, - e_keymap[hid]); - return 1; - } - } - - return 0; -} - -static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - - if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && - (field->flags & HID_MAIN_ITEM_RELATIVE)) - field->flags &= ~HID_MAIN_ITEM_RELATIVE; - - if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || - usage->type == EV_REL || usage->type == EV_ABS)) - clear_bit(usage->code, *bit); - - return 0; -} - -static int lg_event(struct hid_device *hdev, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - - if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { - input_event(field->hidinput->input, usage->type, usage->code, - -value); - return 1; - } - - return 0; -} - -static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - unsigned long quirks = id->driver_data; - int ret; - - hid_set_drvdata(hdev, (void *)quirks); - - if (quirks & LG_NOGET) - hdev->quirks |= HID_QUIRK_NOGET; - - ret = hid_parse(hdev); - if (ret) { - dev_err(&hdev->dev, "parse failed\n"); - goto err_free; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - dev_err(&hdev->dev, "hw start failed\n"); - goto err_free; - } - - if (quirks & LG_RESET_LEDS) - usbhid_set_leds(hdev); - - return 0; -err_free: - return ret; -} - -static const struct hid_device_id lg_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), - .driver_data = LG_RDESC | LG_WIRELESS }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), - .driver_data = LG_RDESC | LG_WIRELESS }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), - .driver_data = LG_RDESC | LG_WIRELESS }, - - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), - .driver_data = LG_BAD_RELATIVE_KEYS }, - - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP), - .driver_data = LG_DUPLICATE_USAGES }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE), - .driver_data = LG_DUPLICATE_USAGES }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), - .driver_data = LG_DUPLICATE_USAGES }, - - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD), - .driver_data = LG_RESET_LEDS }, - - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), - .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), - .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, - - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3), - .driver_data = LG_INVERT_HWHEEL }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150), - .driver_data = LG_INVERT_HWHEEL }, - - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), - .driver_data = LG_NOGET }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), - .driver_data = LG_NOGET }, - { } -}; -MODULE_DEVICE_TABLE(hid, lg_devices); - -static struct hid_driver lg_driver = { - .name = "logitech", - .id_table = lg_devices, - .report_fixup = lg_report_fixup, - .input_mapping = lg_input_mapping, - .input_mapped = lg_input_mapped, - .event = lg_event, - .probe = lg_probe, -}; - -static int lg_init(void) -{ - return hid_register_driver(&lg_driver); -} - -static void lg_exit(void) -{ - hid_unregister_driver(&lg_driver); -} - -module_init(lg_init); -module_exit(lg_exit); -MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(logitech); diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 177bfa1a3e5b..0e7e2e6d975e 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -44,30 +44,6 @@ config HID_PID feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such devices. -config LOGITECH_FF - bool "Logitech devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have one of these devices: - - Logitech WingMan Cordless RumblePad - - Logitech WingMan Cordless RumblePad 2 - - Logitech WingMan Force 3D - - Logitech Formula Force EX - - Logitech MOMO Force wheel - - and if you want to enable force feedback for them. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config LOGIRUMBLEPAD2_FF - bool "Logitech Rumblepad 2 support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you want to enable force feedback support for Logitech - Rumblepad 2 devices. - config PANTHERLORD_FF bool "PantherLord/GreenAsia based device support" depends on HID_FF diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index 00a7b7090192..224c62dc6a32 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -13,12 +13,6 @@ endif ifeq ($(CONFIG_HID_PID),y) usbhid-objs += hid-pidff.o endif -ifeq ($(CONFIG_LOGITECH_FF),y) - usbhid-objs += hid-lgff.o -endif -ifeq ($(CONFIG_LOGIRUMBLEPAD2_FF),y) - usbhid-objs += hid-lg2ff.o -endif ifeq ($(CONFIG_PANTHERLORD_FF),y) usbhid-objs += hid-plff.o endif diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 402ace751271..4ec10aa618db 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -444,6 +444,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns spin_unlock_irqrestore(&usbhid->ctrllock, flags); } +EXPORT_SYMBOL_GPL(usbhid_submit_report); static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index 1d0dac52f166..8891f60d3beb 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -50,18 +50,6 @@ struct hid_ff_initializer { * be a PID device */ static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_LOGITECH_FF - { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ - { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ - { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ - { 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */ - { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ - { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ - { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ -#endif -#ifdef CONFIG_LOGIRUMBLEPAD2_FF - { 0x46d, 0xc218, hid_lg2ff_init }, /* Logitech Rumblepad 2 */ -#endif #ifdef CONFIG_PANTHERLORD_FF { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */ { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */ diff --git a/drivers/hid/usbhid/hid-lg2ff.c b/drivers/hid/usbhid/hid-lg2ff.c deleted file mode 100644 index d469bd0061c9..000000000000 --- a/drivers/hid/usbhid/hid-lg2ff.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Force feedback support for Logitech Rumblepad 2 - * - * Copyright (c) 2008 Anssi Hannula - */ - -/* - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include "usbhid.h" - -struct lg2ff_device { - struct hid_report *report; -}; - -static int play_effect(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct lg2ff_device *lg2ff = data; - int weak, strong; - - strong = effect->u.rumble.strong_magnitude; - weak = effect->u.rumble.weak_magnitude; - - if (weak || strong) { - weak = weak * 0xff / 0xffff; - strong = strong * 0xff / 0xffff; - - lg2ff->report->field[0]->value[0] = 0x51; - lg2ff->report->field[0]->value[2] = weak; - lg2ff->report->field[0]->value[4] = strong; - } else { - lg2ff->report->field[0]->value[0] = 0xf3; - lg2ff->report->field[0]->value[2] = 0x00; - lg2ff->report->field[0]->value[4] = 0x00; - } - - usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT); - return 0; -} - -int hid_lg2ff_init(struct hid_device *hid) -{ - struct lg2ff_device *lg2ff; - struct hid_report *report; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - int error; - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-lg2ff: no output report found\n"); - return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 1) { - printk(KERN_ERR "hid-lg2ff: output report is empty\n"); - return -ENODEV; - } - if (report->field[0]->report_count < 7) { - printk(KERN_ERR "hid-lg2ff: not enough values in the field\n"); - return -ENODEV; - } - - lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL); - if (!lg2ff) - return -ENOMEM; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, lg2ff, play_effect); - if (error) { - kfree(lg2ff); - return error; - } - - lg2ff->report = report; - report->field[0]->value[0] = 0xf3; - report->field[0]->value[1] = 0x00; - report->field[0]->value[2] = 0x00; - report->field[0]->value[3] = 0x00; - report->field[0]->value[4] = 0x00; - report->field[0]->value[5] = 0x00; - report->field[0]->value[6] = 0x00; - - usbhid_submit_report(hid, report, USB_DIR_OUT); - - printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by " - "Anssi Hannula \n"); - - return 0; -} diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c deleted file mode 100644 index 4b7ab6a46d93..000000000000 --- a/drivers/hid/usbhid/hid-lgff.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Force feedback support for hid-compliant for some of the devices from - * Logitech, namely: - * - WingMan Cordless RumblePad - * - WingMan Force 3D - * - * Copyright (c) 2002-2004 Johann Deneux - * Copyright (c) 2006 Anssi Hannula - */ - -/* - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include -#include -#include -#include "usbhid.h" - -struct dev_type { - u16 idVendor; - u16 idProduct; - const signed short *ff; -}; - -static const signed short ff_rumble[] = { - FF_RUMBLE, - -1 -}; - -static const signed short ff_joystick[] = { - FF_CONSTANT, - -1 -}; - -static const struct dev_type devices[] = { - { 0x046d, 0xc211, ff_rumble }, - { 0x046d, 0xc219, ff_rumble }, - { 0x046d, 0xc283, ff_joystick }, - { 0x046d, 0xc286, ff_joystick }, - { 0x046d, 0xc294, ff_joystick }, - { 0x046d, 0xc295, ff_joystick }, - { 0x046d, 0xca03, ff_joystick }, -}; - -static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct hid_report *report = list_entry(report_list->next, struct hid_report, list); - int x, y; - unsigned int left, right; - -#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff - - switch (effect->type) { - case FF_CONSTANT: - x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ - y = effect->u.ramp.end_level + 0x7f; - CLAMP(x); - CLAMP(y); - report->field[0]->value[0] = 0x51; - report->field[0]->value[1] = 0x08; - report->field[0]->value[2] = x; - report->field[0]->value[3] = y; - dbg_hid("(x, y)=(%04x, %04x)\n", x, y); - usbhid_submit_report(hid, report, USB_DIR_OUT); - break; - - case FF_RUMBLE: - right = effect->u.rumble.strong_magnitude; - left = effect->u.rumble.weak_magnitude; - right = right * 0xff / 0xffff; - left = left * 0xff / 0xffff; - CLAMP(left); - CLAMP(right); - report->field[0]->value[0] = 0x42; - report->field[0]->value[1] = 0x00; - report->field[0]->value[2] = left; - report->field[0]->value[3] = right; - dbg_hid("(left, right)=(%04x, %04x)\n", left, right); - usbhid_submit_report(hid, report, USB_DIR_OUT); - break; - } - return 0; -} - -int hid_lgff_init(struct hid_device* hid) -{ - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; - const signed short *ff_bits = ff_joystick; - int error; - int i; - - /* Find the report to use */ - if (list_empty(report_list)) { - err_hid("No output report found"); - return -1; - } - - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - err_hid("NULL output report"); - return -1; - } - - field = report->field[0]; - if (!field) { - err_hid("NULL field"); - return -1; - } - - for (i = 0; i < ARRAY_SIZE(devices); i++) { - if (dev->id.vendor == devices[i].idVendor && - dev->id.product == devices[i].idProduct) { - ff_bits = devices[i].ff; - break; - } - } - - for (i = 0; ff_bits[i] >= 0; i++) - set_bit(ff_bits[i], dev->ffbit); - - error = input_ff_create_memless(dev, NULL, hid_lgff_play); - if (error) - return error; - - printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); - - return 0; -} diff --git a/include/linux/hid.h b/include/linux/hid.h index b0f03fa2ed19..15ee33e0463e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -760,8 +760,6 @@ void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -int hid_lgff_init(struct hid_device *hid); -int hid_lg2ff_init(struct hid_device *hid); int hid_plff_init(struct hid_device *hid); int hid_tmff_init(struct hid_device *hid); int hid_zpff_init(struct hid_device *hid); -- cgit v1.2.3 From 5f022298aab58ddff9bccdb28b82a59109789da9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Sep 2008 19:43:32 +0200 Subject: HID: move pantherlord FF processing Move the force feedback processing into a separate module. [jkosina@suse.cz: fix Kconfig texts a little bit] Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 16 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 2 + drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-pl.c | 205 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/Kconfig | 8 -- drivers/hid/usbhid/Makefile | 3 - drivers/hid/usbhid/hid-ff.c | 4 - drivers/hid/usbhid/hid-plff.c | 139 --------------------------- drivers/hid/usbhid/hid-quirks.c | 1 - include/linux/hid.h | 1 - 12 files changed, 229 insertions(+), 156 deletions(-) create mode 100644 drivers/hid/hid-pl.c delete mode 100644 drivers/hid/usbhid/hid-plff.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 46337a28d1d6..c836caba82d6 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -199,6 +199,22 @@ config HID_MONTEREY ---help--- Support for Monterey Genius KB29E. +config HID_PANTHERLORD + tristate "Pantherlord devices support" + default m + depends on USB_HID + ---help--- + Support for PantherLord/GreenAsia based device support. + + +config PANTHERLORD_FF + bool "Pantherlord force feedback support" + depends on HID_PANTHERLORD + select INPUT_FF_MEMLESS + help + Say Y here if you have a PantherLord/GreenAsia based game controller + or adapter and want to enable force feedback support for it. + config HID_PETALYNX tristate "Petalynx" default m diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 300ee00913bc..e60706835243 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o +obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SONY) += hid-sony.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 63c8ce5540fe..48a76e791e02 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1411,6 +1411,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, @@ -1426,6 +1427,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) }, { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 69dcf9b1a444..2f5d9a1a352c 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -40,6 +40,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_MONTEREY_MODULE HID_COMPAT_CALL_DRIVER(monterey); #endif +#ifdef CONFIG_HID_PANTHERLORD_MODULE + HID_COMPAT_CALL_DRIVER(pantherlord); +#endif #ifdef CONFIG_HID_PETALYNX_MODULE HID_COMPAT_CALL_DRIVER(petalynx); #endif diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index fa4e4fd96f65..fdd2d13036f7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -169,6 +169,8 @@ #define USB_DEVICE_ID_GOGOPEN 0x00ce #define USB_DEVICE_ID_PENPOWER 0x00f4 +#define USB_VENDOR_ID_GREENASIA 0x0e8f + #define USB_VENDOR_ID_GRETAGMACBETH 0x0971 #define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005 diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c new file mode 100644 index 000000000000..a1d1fb9da125 --- /dev/null +++ b/drivers/hid/hid-pl.c @@ -0,0 +1,205 @@ +/* + * Force feedback support for PantherLord/GreenAsia based devices + * + * The devices are distributed under various names and the same USB device ID + * can be used in both adapters and actual game controllers. + * + * 0810:0001 "Twin USB Joystick" + * - tested with PantherLord USB/PS2 2in1 Adapter + * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) + * + * 0e8f:0003 "GreenAsia Inc. USB Joystick " + * - tested with K??ng Gaming gamepad + * + * Copyright (c) 2007 Anssi Hannula + */ + +/* + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) + +#include +#include +#include + +#include "hid-ids.h" + +#ifdef CONFIG_PANTHERLORD_FF +#include "usbhid/usbhid.h" + +struct plff_device { + struct hid_report *report; +}; + +static int hid_plff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct plff_device *plff = data; + int left, right; + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + debug("called with 0x%04x 0x%04x", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + plff->report->field[0]->value[2] = left; + plff->report->field[0]->value[3] = right; + debug("running with 0x%02x 0x%02x", left, right); + usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + + return 0; +} + +static int plff_init(struct hid_device *hid) +{ + struct plff_device *plff; + struct hid_report *report; + struct hid_input *hidinput; + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct list_head *report_ptr = report_list; + struct input_dev *dev; + int error; + + /* The device contains one output report per physical device, all + containing 1 field, which contains 4 ff00.0002 usages and 4 16bit + absolute values. + + The input reports also contain a field which contains + 8 ff00.0001 usages and 8 boolean values. Their meaning is + currently unknown. */ + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-plff: no output reports found\n"); + return -ENODEV; + } + + list_for_each_entry(hidinput, &hid->inputs, list) { + + report_ptr = report_ptr->next; + + if (report_ptr == report_list) { + printk(KERN_ERR "hid-plff: required output report is missing\n"); + return -ENODEV; + } + + report = list_entry(report_ptr, struct hid_report, list); + if (report->maxfield < 1) { + printk(KERN_ERR "hid-plff: no fields in the report\n"); + return -ENODEV; + } + + if (report->field[0]->report_count < 4) { + printk(KERN_ERR "hid-plff: not enough values in the field\n"); + return -ENODEV; + } + + plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); + if (!plff) + return -ENOMEM; + + dev = hidinput->input; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, plff, hid_plff_play); + if (error) { + kfree(plff); + return error; + } + + plff->report = report; + plff->report->field[0]->value[0] = 0x00; + plff->report->field[0]->value[1] = 0x00; + plff->report->field[0]->value[2] = 0x00; + plff->report->field[0]->value[3] = 0x00; + usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + } + + printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia " + "devices by Anssi Hannula \n"); + + return 0; +} +#else +static inline int plff_init(struct hid_device *hid) +{ + return 0; +} +#endif + +static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + if (id->driver_data) + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + plff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id pl_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR), + .driver_data = 1 }, /* Twin USB Joystick */ + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */ + { } +}; +MODULE_DEVICE_TABLE(hid, pl_devices); + +static struct hid_driver pl_driver = { + .name = "pantherlord", + .id_table = pl_devices, + .probe = pl_probe, +}; + +static int pl_init(void) +{ + return hid_register_driver(&pl_driver); +} + +static void pl_exit(void) +{ + hid_unregister_driver(&pl_driver); +} + +module_init(pl_init); +module_exit(pl_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(pantherlord); diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 0e7e2e6d975e..02c8228ed7ee 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -44,14 +44,6 @@ config HID_PID feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such devices. -config PANTHERLORD_FF - bool "PantherLord/GreenAsia based device support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a PantherLord/GreenAsia based game controller - or adapter and want to enable force feedback support for it. - config THRUSTMASTER_FF bool "ThrustMaster devices support" depends on HID_FF diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index 224c62dc6a32..0ee4803375b2 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -13,9 +13,6 @@ endif ifeq ($(CONFIG_HID_PID),y) usbhid-objs += hid-pidff.o endif -ifeq ($(CONFIG_PANTHERLORD_FF),y) - usbhid-objs += hid-plff.o -endif ifeq ($(CONFIG_THRUSTMASTER_FF),y) usbhid-objs += hid-tmff.o endif diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index 8891f60d3beb..a868eef06189 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -50,10 +50,6 @@ struct hid_ff_initializer { * be a PID device */ static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_PANTHERLORD_FF - { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */ - { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */ -#endif #ifdef CONFIG_THRUSTMASTER_FF { 0x44f, 0xb300, hid_tmff_init }, { 0x44f, 0xb304, hid_tmff_init }, diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/usbhid/hid-plff.c deleted file mode 100644 index 9eb83cf9d22b..000000000000 --- a/drivers/hid/usbhid/hid-plff.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Force feedback support for PantherLord/GreenAsia based devices - * - * The devices are distributed under various names and the same USB device ID - * can be used in both adapters and actual game controllers. - * - * 0810:0001 "Twin USB Joystick" - * - tested with PantherLord USB/PS2 2in1 Adapter - * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) - * - * 0e8f:0003 "GreenAsia Inc. USB Joystick " - * - tested with Köng Gaming gamepad - * - * Copyright (c) 2007 Anssi Hannula - */ - -/* - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) - -#include -#include -#include -#include "usbhid.h" - -struct plff_device { - struct hid_report *report; -}; - -static int hid_plff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct plff_device *plff = data; - int left, right; - - left = effect->u.rumble.strong_magnitude; - right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); - - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; - - plff->report->field[0]->value[2] = left; - plff->report->field[0]->value[3] = right; - debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); - - return 0; -} - -int hid_plff_init(struct hid_device *hid) -{ - struct plff_device *plff; - struct hid_report *report; - struct hid_input *hidinput; - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct list_head *report_ptr = report_list; - struct input_dev *dev; - int error; - - /* The device contains one output report per physical device, all - containing 1 field, which contains 4 ff00.0002 usages and 4 16bit - absolute values. - - The input reports also contain a field which contains - 8 ff00.0001 usages and 8 boolean values. Their meaning is - currently unknown. */ - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-plff: no output reports found\n"); - return -ENODEV; - } - - list_for_each_entry(hidinput, &hid->inputs, list) { - - report_ptr = report_ptr->next; - - if (report_ptr == report_list) { - printk(KERN_ERR "hid-plff: required output report is missing\n"); - return -ENODEV; - } - - report = list_entry(report_ptr, struct hid_report, list); - if (report->maxfield < 1) { - printk(KERN_ERR "hid-plff: no fields in the report\n"); - return -ENODEV; - } - - if (report->field[0]->report_count < 4) { - printk(KERN_ERR "hid-plff: not enough values in the field\n"); - return -ENODEV; - } - - plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); - if (!plff) - return -ENOMEM; - - dev = hidinput->input; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, plff, hid_plff_play); - if (error) { - kfree(plff); - return error; - } - - plff->report = report; - plff->report->field[0]->value[0] = 0x00; - plff->report->field[0]->value[1] = 0x00; - plff->report->field[0]->value[2] = 0x00; - plff->report->field[0]->value[3] = 0x00; - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); - } - - printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia " - "devices by Anssi Hannula \n"); - - return 0; -} diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index d40f00904c5e..47ebe045f9b5 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -32,7 +32,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 15ee33e0463e..63b808067203 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -760,7 +760,6 @@ void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -int hid_plff_init(struct hid_device *hid); int hid_tmff_init(struct hid_device *hid); int hid_zpff_init(struct hid_device *hid); #ifdef CONFIG_HID_PID -- cgit v1.2.3 From 10e41a711e55f485709b4ca157e587cf36ef5a69 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Sep 2008 12:23:31 +0200 Subject: HID: move thrustmaster FF processing Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 9 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 4 + drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-tmff.c | 267 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/Kconfig | 11 -- drivers/hid/usbhid/Makefile | 3 - drivers/hid/usbhid/hid-ff.c | 6 - drivers/hid/usbhid/hid-tmff.c | 225 ----------------------------------- include/linux/hid.h | 1 - 11 files changed, 286 insertions(+), 246 deletions(-) create mode 100644 drivers/hid/hid-tmff.c delete mode 100644 drivers/hid/usbhid/hid-tmff.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index c836caba82d6..78e3ba9504f3 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -243,6 +243,15 @@ config HID_SUNPLUS ---help--- Support for Sunplus WDesktop input device. +config THRUSTMASTER_FF + tristate "ThrustMaster devices support" + default m + depends on USB_HID + select INPUT_FF_MEMLESS + help + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or + a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e60706835243..9bd6daf6aaa4 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SONY) += hid-sony.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o +obj-$(CONFIG_THRUSTMASTER_FF) += hid-tmff.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 48a76e791e02..b0996ff1462b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1516,6 +1516,10 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index 2f5d9a1a352c..ae122a122faa 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -55,6 +55,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_SUNPLUS_MODULE HID_COMPAT_CALL_DRIVER(sunplus); #endif +#ifdef CONFIG_THRUSTMASTER_FF_MODULE + HID_COMPAT_CALL_DRIVER(thrustmaster); +#endif return -EIO; } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index fdd2d13036f7..b8cc0196ec00 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -357,6 +357,8 @@ #define USB_DEVICE_ID_TENX_IBUDDY1 0x0001 #define USB_DEVICE_ID_TENX_IBUDDY2 0x0002 +#define USB_VENDOR_ID_THRUSTMASTER 0x044f + #define USB_VENDOR_ID_TOPMAX 0x0663 #define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c new file mode 100644 index 000000000000..be7ebe286a16 --- /dev/null +++ b/drivers/hid/hid-tmff.c @@ -0,0 +1,267 @@ +/* + * Force feedback support for various HID compliant devices by ThrustMaster: + * ThrustMaster FireStorm Dual Power 2 + * and possibly others whose device ids haven't been added. + * + * Modified to support ThrustMaster devices by Zinx Verituse + * on 2003-01-25 from the Logitech force feedback driver, + * which is by Johann Deneux. + * + * Copyright (c) 2003 Zinx Verituse + * Copyright (c) 2002 Johann Deneux + */ + +/* + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "hid-ids.h" + +#include "usbhid/usbhid.h" + +/* Usages for thrustmaster devices I know about */ +#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) + +static const signed short ff_rumble[] = { + FF_RUMBLE, + -1 +}; + +static const signed short ff_joystick[] = { + FF_CONSTANT, + -1 +}; + +struct tmff_device { + struct hid_report *report; + struct hid_field *ff_field; +}; + +/* Changes values from 0 to 0xffff into values from minimum to maximum */ +static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum) +{ + int ret; + + ret = (in * (maximum - minimum) / 0xffff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} + +/* Changes values from -0x80 to 0x7f into values from minimum to maximum */ +static inline int tmff_scale_s8(int in, int minimum, int maximum) +{ + int ret; + + ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} + +static int tmff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct tmff_device *tmff = data; + struct hid_field *ff_field = tmff->ff_field; + int x, y; + int left, right; /* Rumbling */ + + switch (effect->type) { + case FF_CONSTANT: + x = tmff_scale_s8(effect->u.ramp.start_level, + ff_field->logical_minimum, + ff_field->logical_maximum); + y = tmff_scale_s8(effect->u.ramp.end_level, + ff_field->logical_minimum, + ff_field->logical_maximum); + + dbg_hid("(x, y)=(%04x, %04x)\n", x, y); + ff_field->value[0] = x; + ff_field->value[1] = y; + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + break; + + case FF_RUMBLE: + left = tmff_scale_u16(effect->u.rumble.weak_magnitude, + ff_field->logical_minimum, + ff_field->logical_maximum); + right = tmff_scale_u16(effect->u.rumble.strong_magnitude, + ff_field->logical_minimum, + ff_field->logical_maximum); + + dbg_hid("(left,right)=(%08x, %08x)\n", left, right); + ff_field->value[0] = left; + ff_field->value[1] = right; + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + break; + } + return 0; +} + +static int tmff_init(struct hid_device *hid, const signed short *ff_bits) +{ + struct tmff_device *tmff; + struct hid_report *report; + struct list_head *report_list; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + int error; + int i; + + tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); + if (!tmff) + return -ENOMEM; + + /* Find the report to use */ + report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + list_for_each_entry(report, report_list, list) { + int fieldnum; + + for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { + struct hid_field *field = report->field[fieldnum]; + + if (field->maxusage <= 0) + continue; + + switch (field->usage[0].hid) { + case THRUSTMASTER_USAGE_FF: + if (field->report_count < 2) { + warn("ignoring FF field with " + "report_count < 2"); + continue; + } + + if (field->logical_maximum == + field->logical_minimum) { + warn("ignoring FF field with " + "logical_maximum == " + "logical_minimum"); + continue; + } + + if (tmff->report && tmff->report != report) { + warn("ignoring FF field in other " + "report"); + continue; + } + + if (tmff->ff_field && tmff->ff_field != field) { + warn("ignoring duplicate FF field"); + continue; + } + + tmff->report = report; + tmff->ff_field = field; + + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], input_dev->ffbit); + + break; + + default: + warn("ignoring unknown output usage %08x", + field->usage[0].hid); + continue; + } + } + } + + if (!tmff->report) { + err("cant find FF field in output reports\n"); + error = -ENODEV; + goto fail; + } + + error = input_ff_create_memless(input_dev, tmff, tmff_play); + if (error) + goto fail; + + info("Force feedback for ThrustMaster devices by Zinx Verituse " + ""); + return 0; + +fail: + kfree(tmff); + return error; +} + +static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + tmff_init(hdev, (void *)id->driver_data); + + return 0; +err: + return ret; +} + +static const struct hid_device_id tm_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300), + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */ + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654), /* FGT Force Feedback Wheel */ + .driver_data = (unsigned long)ff_joystick }, + { } +}; +MODULE_DEVICE_TABLE(hid, tm_devices); + +static struct hid_driver tm_driver = { + .name = "thrustmaster", + .id_table = tm_devices, + .probe = tm_probe, +}; + +static int tm_init(void) +{ + return hid_register_driver(&tm_driver); +} + +static void tm_exit(void) +{ + hid_unregister_driver(&tm_driver); +} + +module_init(tm_init); +module_exit(tm_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(thrustmaster); diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 02c8228ed7ee..c236fb3deb59 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -44,17 +44,6 @@ config HID_PID feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such devices. -config THRUSTMASTER_FF - bool "ThrustMaster devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or - a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel, - and want to enable force feedback support for it. - Note: if you say N here, this device will still be supported, but without - force feedback. - config ZEROPLUS_FF bool "Zeroplus based game controller support" depends on HID_FF diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index 0ee4803375b2..dd3b8634800f 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -13,9 +13,6 @@ endif ifeq ($(CONFIG_HID_PID),y) usbhid-objs += hid-pidff.o endif -ifeq ($(CONFIG_THRUSTMASTER_FF),y) - usbhid-objs += hid-tmff.o -endif ifeq ($(CONFIG_ZEROPLUS_FF),y) usbhid-objs += hid-zpff.o endif diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index a868eef06189..ed3a869d54e3 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -50,12 +50,6 @@ struct hid_ff_initializer { * be a PID device */ static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_THRUSTMASTER_FF - { 0x44f, 0xb300, hid_tmff_init }, - { 0x44f, 0xb304, hid_tmff_init }, - { 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */ - { 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */ -#endif #ifdef CONFIG_ZEROPLUS_FF { 0xc12, 0x0005, hid_zpff_init }, { 0xc12, 0x0030, hid_zpff_init }, diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c deleted file mode 100644 index 144578b1a00c..000000000000 --- a/drivers/hid/usbhid/hid-tmff.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Force feedback support for various HID compliant devices by ThrustMaster: - * ThrustMaster FireStorm Dual Power 2 - * and possibly others whose device ids haven't been added. - * - * Modified to support ThrustMaster devices by Zinx Verituse - * on 2003-01-25 from the Logitech force feedback driver, - * which is by Johann Deneux. - * - * Copyright (c) 2003 Zinx Verituse - * Copyright (c) 2002 Johann Deneux - */ - -/* - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#undef DEBUG -#include - -#include -#include "usbhid.h" - -/* Usages for thrustmaster devices I know about */ -#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) - -struct dev_type { - u16 idVendor; - u16 idProduct; - const signed short *ff; -}; - -static const signed short ff_rumble[] = { - FF_RUMBLE, - -1 -}; - -static const signed short ff_joystick[] = { - FF_CONSTANT, - -1 -}; - -static const struct dev_type devices[] = { - { 0x44f, 0xb300, ff_rumble }, - { 0x44f, 0xb304, ff_rumble }, - { 0x44f, 0xb651, ff_rumble }, /* FGT Rumble Force Wheel */ - { 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */ -}; - -struct tmff_device { - struct hid_report *report; - struct hid_field *ff_field; -}; - -/* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale_u16(unsigned int in, - int minimum, int maximum) -{ - int ret; - - ret = (in * (maximum - minimum) / 0xffff) + minimum; - if (ret < minimum) - return minimum; - if (ret > maximum) - return maximum; - return ret; -} - -/* Changes values from -0x80 to 0x7f into values from minimum to maximum */ -static inline int hid_tmff_scale_s8(int in, - int minimum, int maximum) -{ - int ret; - - ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum; - if (ret < minimum) - return minimum; - if (ret > maximum) - return maximum; - return ret; -} - -static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct tmff_device *tmff = data; - struct hid_field *ff_field = tmff->ff_field; - int x, y; - int left, right; /* Rumbling */ - - switch (effect->type) { - case FF_CONSTANT: - x = hid_tmff_scale_s8(effect->u.ramp.start_level, - ff_field->logical_minimum, - ff_field->logical_maximum); - y = hid_tmff_scale_s8(effect->u.ramp.end_level, - ff_field->logical_minimum, - ff_field->logical_maximum); - - dbg_hid("(x, y)=(%04x, %04x)\n", x, y); - ff_field->value[0] = x; - ff_field->value[1] = y; - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); - break; - - case FF_RUMBLE: - left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude, - ff_field->logical_minimum, - ff_field->logical_maximum); - right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude, - ff_field->logical_minimum, - ff_field->logical_maximum); - - dbg_hid("(left,right)=(%08x, %08x)\n", left, right); - ff_field->value[0] = left; - ff_field->value[1] = right; - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); - break; - } - return 0; -} - -int hid_tmff_init(struct hid_device *hid) -{ - struct tmff_device *tmff; - struct hid_report *report; - struct list_head *report_list; - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - const signed short *ff_bits = ff_joystick; - int error; - int i; - - tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); - if (!tmff) - return -ENOMEM; - - /* Find the report to use */ - report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - list_for_each_entry(report, report_list, list) { - int fieldnum; - - for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { - struct hid_field *field = report->field[fieldnum]; - - if (field->maxusage <= 0) - continue; - - switch (field->usage[0].hid) { - case THRUSTMASTER_USAGE_FF: - if (field->report_count < 2) { - warn("ignoring FF field with report_count < 2"); - continue; - } - - if (field->logical_maximum == field->logical_minimum) { - warn("ignoring FF field with logical_maximum == logical_minimum"); - continue; - } - - if (tmff->report && tmff->report != report) { - warn("ignoring FF field in other report"); - continue; - } - - if (tmff->ff_field && tmff->ff_field != field) { - warn("ignoring duplicate FF field"); - continue; - } - - tmff->report = report; - tmff->ff_field = field; - - for (i = 0; i < ARRAY_SIZE(devices); i++) { - if (input_dev->id.vendor == devices[i].idVendor && - input_dev->id.product == devices[i].idProduct) { - ff_bits = devices[i].ff; - break; - } - } - - for (i = 0; ff_bits[i] >= 0; i++) - set_bit(ff_bits[i], input_dev->ffbit); - - break; - - default: - warn("ignoring unknown output usage %08x", field->usage[0].hid); - continue; - } - } - } - - if (!tmff->report) { - err("cant find FF field in output reports\n"); - error = -ENODEV; - goto fail; - } - - error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); - if (error) - goto fail; - - info("Force feedback for ThrustMaster devices by Zinx Verituse "); - return 0; - - fail: - kfree(tmff); - return error; -} - diff --git a/include/linux/hid.h b/include/linux/hid.h index 63b808067203..8266e50637b4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -760,7 +760,6 @@ void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -int hid_tmff_init(struct hid_device *hid); int hid_zpff_init(struct hid_device *hid); #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); -- cgit v1.2.3 From 987fbc1f7d446f4bf7063d3b756ae29db80be75e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Sep 2008 12:23:32 +0200 Subject: HID: move zeroplus FF processing Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 2 + drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-zpff.c | 162 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/Kconfig | 8 --- drivers/hid/usbhid/Makefile | 3 - drivers/hid/usbhid/hid-ff.c | 4 -- drivers/hid/usbhid/hid-zpff.c | 107 ---------------------------- include/linux/hid.h | 1 - 11 files changed, 178 insertions(+), 123 deletions(-) create mode 100644 drivers/hid/hid-zpff.c delete mode 100644 drivers/hid/usbhid/hid-zpff.c (limited to 'include/linux') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 78e3ba9504f3..fc4f80c4006c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -252,6 +252,14 @@ config THRUSTMASTER_FF Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel. +config ZEROPLUS_FF + tristate "Zeroplus based game controller support" + default m + depends on USB_HID + select INPUT_FF_MEMLESS + help + Say Y here if you have a Zeroplus based game controller. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 9bd6daf6aaa4..767f29500716 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SONY) += hid-sony.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_THRUSTMASTER_FF) += hid-tmff.o +obj-$(CONFIG_ZEROPLUS_FF) += hid-zpff.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b0996ff1462b..5a23077175e6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1530,6 +1530,8 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { } }; diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index ae122a122faa..54d1fb6fd4d8 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -58,6 +58,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_THRUSTMASTER_FF_MODULE HID_COMPAT_CALL_DRIVER(thrustmaster); #endif +#ifdef CONFIG_ZEROPLUS_FF_MODULE + HID_COMPAT_CALL_DRIVER(zeroplus); +#endif return -EIO; } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b8cc0196ec00..e68b6d9bcd1a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -388,6 +388,8 @@ #define USB_VENDOR_ID_YEALINK 0x6993 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 +#define USB_VENDOR_ID_ZEROPLUS 0x0c12 + #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c new file mode 100644 index 000000000000..9ed04ee9d642 --- /dev/null +++ b/drivers/hid/hid-zpff.c @@ -0,0 +1,162 @@ +/* + * Force feedback support for Zeroplus based devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * 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. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include + +#include "hid-ids.h" + +#include "usbhid/usbhid.h" + +struct zpff_device { + struct hid_report *report; +}; + +static int zpff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct zpff_device *zpff = data; + int left, right; + + /* + * The following is specified the other way around in the Zeroplus + * datasheet but the order below is correct for the XFX Executioner; + * however it is possible that the XFX Executioner is an exception + */ + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + dbg_hid("called with 0x%04x 0x%04x\n", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + zpff->report->field[2]->value[0] = left; + zpff->report->field[3]->value[0] = right; + dbg_hid("running with 0x%02x 0x%02x\n", left, right); + usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + + return 0; +} + +static int zpff_init(struct hid_device *hid) +{ + struct zpff_device *zpff; + struct hid_report *report; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-zpff: no output report found\n"); + return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 4) { + printk(KERN_ERR "hid-zpff: not enough fields in report\n"); + return -ENODEV; + } + + zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); + if (!zpff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, zpff, zpff_play); + if (error) { + kfree(zpff); + return error; + } + + zpff->report = report; + zpff->report->field[0]->value[0] = 0x00; + zpff->report->field[1]->value[0] = 0x02; + zpff->report->field[2]->value[0] = 0x00; + zpff->report->field[3]->value[0] = 0x00; + usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + + printk(KERN_INFO "Force feedback for Zeroplus based devices by " + "Anssi Hannula \n"); + + return 0; +} + +static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + zpff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id zp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, + { } +}; +MODULE_DEVICE_TABLE(hid, zp_devices); + +static struct hid_driver zp_driver = { + .name = "zeroplus", + .id_table = zp_devices, + .probe = zp_probe, +}; + +static int zp_init(void) +{ + return hid_register_driver(&zp_driver); +} + +static void zp_exit(void) +{ + hid_unregister_driver(&zp_driver); +} + +module_init(zp_init); +module_exit(zp_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(zeroplus); diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index c236fb3deb59..3cfc076f5f71 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -44,14 +44,6 @@ config HID_PID feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such devices. -config ZEROPLUS_FF - bool "Zeroplus based game controller support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a Zeroplus based game controller and want to - enable force feedback for it. - config USB_HIDDEV bool "/dev/hiddev raw HID device support" depends on USB_HID diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index dd3b8634800f..5c460fcdd75c 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -13,9 +13,6 @@ endif ifeq ($(CONFIG_HID_PID),y) usbhid-objs += hid-pidff.o endif -ifeq ($(CONFIG_ZEROPLUS_FF),y) - usbhid-objs += hid-zpff.o -endif ifeq ($(CONFIG_HID_FF),y) usbhid-objs += hid-ff.o endif diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index ed3a869d54e3..eca01a6fda59 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -50,10 +50,6 @@ struct hid_ff_initializer { * be a PID device */ static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_ZEROPLUS_FF - { 0xc12, 0x0005, hid_zpff_init }, - { 0xc12, 0x0030, hid_zpff_init }, -#endif { 0, 0, hid_pidff_init} /* Matches anything */ }; diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c deleted file mode 100644 index 5a688274f6a3..000000000000 --- a/drivers/hid/usbhid/hid-zpff.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Force feedback support for Zeroplus based devices - * - * Copyright (c) 2005, 2006 Anssi Hannula - */ - -/* - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include "usbhid.h" - -struct zpff_device { - struct hid_report *report; -}; - -static int hid_zpff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct zpff_device *zpff = data; - int left, right; - - /* - * The following is specified the other way around in the Zeroplus - * datasheet but the order below is correct for the XFX Executioner; - * however it is possible that the XFX Executioner is an exception - */ - - left = effect->u.rumble.strong_magnitude; - right = effect->u.rumble.weak_magnitude; - dbg_hid("called with 0x%04x 0x%04x\n", left, right); - - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; - - zpff->report->field[2]->value[0] = left; - zpff->report->field[3]->value[0] = right; - dbg_hid("running with 0x%02x 0x%02x\n", left, right); - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - - return 0; -} - -int hid_zpff_init(struct hid_device *hid) -{ - struct zpff_device *zpff; - struct hid_report *report; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - int error; - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-zpff: no output report found\n"); - return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 4) { - printk(KERN_ERR "hid-zpff: not enough fields in report\n"); - return -ENODEV; - } - - zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); - if (!zpff) - return -ENOMEM; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, zpff, hid_zpff_play); - if (error) { - kfree(zpff); - return error; - } - - zpff->report = report; - zpff->report->field[0]->value[0] = 0x00; - zpff->report->field[1]->value[0] = 0x02; - zpff->report->field[2]->value[0] = 0x00; - zpff->report->field[3]->value[0] = 0x00; - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - - printk(KERN_INFO "Force feedback for Zeroplus based devices by " - "Anssi Hannula \n"); - - return 0; -} diff --git a/include/linux/hid.h b/include/linux/hid.h index 8266e50637b4..0773ba6a66f2 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -760,7 +760,6 @@ void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -int hid_zpff_init(struct hid_device *hid); #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); #else -- cgit v1.2.3 From 76483cf4d0efbc35eaf9905a437f2f1be0221360 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Sep 2008 12:23:33 +0200 Subject: HID: remove hid-ff hid-ff.c now calls only pidff (generic driver), the special ones are now in separate drivers. Invoke pidff on all non-special directly. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/Kconfig | 13 -------- drivers/hid/usbhid/Makefile | 3 -- drivers/hid/usbhid/hid-core.c | 2 +- drivers/hid/usbhid/hid-ff.c | 69 ------------------------------------------- include/linux/hid.h | 9 +----- 5 files changed, 2 insertions(+), 94 deletions(-) delete mode 100644 drivers/hid/usbhid/hid-ff.c (limited to 'include/linux') diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 3cfc076f5f71..5d9aa95fc3ef 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -24,21 +24,8 @@ config USB_HID comment "Input core support is needed for USB HID input layer or HIDBP support" depends on USB_HID && INPUT=n -config HID_FF - bool "Force feedback support (EXPERIMENTAL)" - depends on USB_HID && EXPERIMENTAL - help - Say Y here is you want force feedback support for a few HID devices. - See below for a list of supported devices. - - See for a description of the force - feedback API. - - If unsure, say N. - config HID_PID bool "PID device support" - depends on HID_FF help Say Y here if you have a PID-compliant device and wish to enable force feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index 5c460fcdd75c..1329ecb37a1c 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -13,9 +13,6 @@ endif ifeq ($(CONFIG_HID_PID),y) usbhid-objs += hid-pidff.o endif -ifeq ($(CONFIG_HID_FF),y) - usbhid-objs += hid-ff.o -endif obj-$(CONFIG_USB_HID) += usbhid.o obj-$(CONFIG_USB_KBD) += usbkbd.o diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 4ec10aa618db..07840df56c63 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -935,7 +935,7 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; hid->hid_output_raw_report = usbhid_output_raw_report; - hid->ff_init = hid_ff_init; + hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV hid->hiddev_connect = hiddev_connect; hid->hiddev_hid_event = hiddev_hid_event; diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c deleted file mode 100644 index eca01a6fda59..000000000000 --- a/drivers/hid/usbhid/hid-ff.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Force feedback support for hid devices. - * Not all hid devices use the same protocol. For example, some use PID, - * other use their own proprietary procotol. - * - * Copyright (c) 2002-2004 Johann Deneux - */ - -/* - * 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. - * - * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include - -#undef DEBUG -#include - -#include -#include "usbhid.h" - -/* - * This table contains pointers to initializers. To add support for new - * devices, you need to add the USB vendor and product ids here. - */ -struct hid_ff_initializer { - u16 idVendor; - u16 idProduct; - int (*init)(struct hid_device*); -}; - -/* - * We try pidff when no other driver is found because PID is the - * standards compliant way of implementing force feedback in HID. - * pidff_init() will quickly abort if the device doesn't appear to - * be a PID device - */ -static struct hid_ff_initializer inits[] = { - { 0, 0, hid_pidff_init} /* Matches anything */ -}; - -int hid_ff_init(struct hid_device* hid) -{ - struct hid_ff_initializer *init; - int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor); - int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct); - - for (init = inits; init->idVendor; init++) - if (init->idVendor == vendor && init->idProduct == product) - break; - - return init->init(hid); -} -EXPORT_SYMBOL_GPL(hid_ff_init); - diff --git a/include/linux/hid.h b/include/linux/hid.h index 0773ba6a66f2..dcdef0bb4bba 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -757,17 +757,10 @@ int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); void usbhid_set_leds(struct hid_device *hid); -#ifdef CONFIG_HID_FF -int hid_ff_init(struct hid_device *hid); - #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); #else -static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; } -#endif - -#else -#define hid_ff_init NULL +#define hid_pidff_init NULL #endif #ifdef CONFIG_HID_DEBUG -- cgit v1.2.3 From 34a5ceee5eafe8cfa650d333304ce84a573ff7f0 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Thu, 2 Oct 2008 22:14:54 +0200 Subject: HID: hiddev.h: Fix mixed space and tabs in example code. Fix mixed space and tabs in example code. Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- include/linux/hiddev.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h index 6ae77b3468f9..7a9ba2944127 100644 --- a/include/linux/hiddev.h +++ b/include/linux/hiddev.h @@ -182,26 +182,26 @@ struct hiddev_usage_ref_multi { /* To traverse the input report descriptor info for a HID device, perform the * following: * - * rinfo.report_type = HID_REPORT_TYPE_INPUT; - * rinfo.report_id = HID_REPORT_ID_FIRST; - * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); + * rinfo.report_type = HID_REPORT_TYPE_INPUT; + * rinfo.report_id = HID_REPORT_ID_FIRST; + * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); * - * while (ret >= 0) { - * for (i = 0; i < rinfo.num_fields; i++) { - * finfo.report_type = rinfo.report_type; - * finfo.report_id = rinfo.report_id; - * finfo.field_index = i; - * ioctl(fd, HIDIOCGFIELDINFO, &finfo); - * for (j = 0; j < finfo.maxusage; j++) { - * uref.field_index = i; - * uref.usage_index = j; - * ioctl(fd, HIDIOCGUCODE, &uref); - * ioctl(fd, HIDIOCGUSAGE, &uref); - * } - * } - * rinfo.report_id |= HID_REPORT_ID_NEXT; - * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); - * } + * while (ret >= 0) { + * for (i = 0; i < rinfo.num_fields; i++) { + * finfo.report_type = rinfo.report_type; + * finfo.report_id = rinfo.report_id; + * finfo.field_index = i; + * ioctl(fd, HIDIOCGFIELDINFO, &finfo); + * for (j = 0; j < finfo.maxusage; j++) { + * uref.field_index = i; + * uref.usage_index = j; + * ioctl(fd, HIDIOCGUCODE, &uref); + * ioctl(fd, HIDIOCGUSAGE, &uref); + * } + * } + * rinfo.report_id |= HID_REPORT_ID_NEXT; + * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); + * } */ -- cgit v1.2.3 From dded364bf4e1f0de67d7d7b9e77c06b23a9f081f Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Thu, 2 Oct 2008 22:15:02 +0200 Subject: HID: hiddev.h: Fix example code. Fix hiddev.h example code. To get the correct usage code, you need to set report_type and report_id. Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- include/linux/hiddev.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h index 7a9ba2944127..c760ae0eb6a1 100644 --- a/include/linux/hiddev.h +++ b/include/linux/hiddev.h @@ -193,6 +193,8 @@ struct hiddev_usage_ref_multi { * finfo.field_index = i; * ioctl(fd, HIDIOCGFIELDINFO, &finfo); * for (j = 0; j < finfo.maxusage; j++) { + * uref.report_type = rinfo.report_type; + * uref.report_id = rinfo.report_id; * uref.field_index = i; * uref.usage_index = j; * ioctl(fd, HIDIOCGUCODE, &uref); -- cgit v1.2.3 From f129ea6d1efe0eddcbb1f0faaec5623788ad9e58 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 4 Oct 2008 14:44:06 +0200 Subject: HID: fix a lockup regression when using force feedback on a PID device Commit 8006479c9b75fb6594a7b746af3d7f1fbb68f18f introduced a spinlock in input_dev->event_lock, which is locked when handling input events. However, the hid-pidff driver sleeps when handling events as it waits for reports being sent to the device before changing the report contents again. This causes a system lockup when trying to use force feedback with a PID device, a regression introduced in 2.6.24 and 2.6.23.15. Fix it by extracting the raw report data from struct hid_report immediately when hid_submit_report() is called, therefore allowing drivers to change the contents of struct hid_report immediately without affecting the already-queued transfer. In hid-pidff, re-add the removed usbhid_wait_io() to pidff_erase_effect() instead, to prevent a full report queue from causing the submission to fail, thus not freeing up device memory. pidff_erase_effect() is not called while dev->event_lock is held. Signed-off-by: Anssi Hannula Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 31 +++++++++++++++++++++++++++---- drivers/hid/usbhid/hid-pidff.c | 5 +++-- drivers/hid/usbhid/usbhid.h | 2 +- include/linux/hid.h | 6 ++++++ 4 files changed, 37 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 07840df56c63..1ae047cd4fa1 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -232,13 +232,16 @@ static void hid_irq_in(struct urb *urb) static int hid_submit_out(struct hid_device *hid) { struct hid_report *report; + char *raw_report; struct usbhid_device *usbhid = hid->driver_data; - report = usbhid->out[usbhid->outtail]; + report = usbhid->out[usbhid->outtail].report; + raw_report = usbhid->out[usbhid->outtail].raw_report; - hid_output_report(report, usbhid->outbuf); usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); usbhid->urbout->dev = hid_to_usb_dev(hid); + memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); + kfree(raw_report); dbg_hid("submitting out urb\n"); @@ -254,17 +257,20 @@ static int hid_submit_ctrl(struct hid_device *hid) { struct hid_report *report; unsigned char dir; + char *raw_report; int len; struct usbhid_device *usbhid = hid->driver_data; report = usbhid->ctrl[usbhid->ctrltail].report; + raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; dir = usbhid->ctrl[usbhid->ctrltail].dir; len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if (dir == USB_DIR_OUT) { - hid_output_report(report, usbhid->ctrlbuf); usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); usbhid->urbctrl->transfer_buffer_length = len; + memcpy(usbhid->ctrlbuf, raw_report, len); + kfree(raw_report); } else { int maxpacket, padlen; @@ -401,6 +407,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns int head; unsigned long flags; struct usbhid_device *usbhid = hid->driver_data; + int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) return; @@ -415,7 +422,14 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns return; } - usbhid->out[usbhid->outhead] = report; + usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); + if (!usbhid->out[usbhid->outhead].raw_report) { + spin_unlock_irqrestore(&usbhid->outlock, flags); + warn("output queueing failed"); + return; + } + hid_output_report(report, usbhid->out[usbhid->outhead].raw_report); + usbhid->out[usbhid->outhead].report = report; usbhid->outhead = head; if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) @@ -434,6 +448,15 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns return; } + if (dir == USB_DIR_OUT) { + usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); + if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + warn("control queueing failed"); + return; + } + hid_output_report(report, usbhid->ctrl[usbhid->ctrlhead].raw_report); + } usbhid->ctrl[usbhid->ctrlhead].report = report; usbhid->ctrl[usbhid->ctrlhead].dir = dir; usbhid->ctrlhead = head; diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 011326178c06..484e3eec2f88 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -397,7 +397,6 @@ static void pidff_set_condition_report(struct pidff_device *pidff, effect->u.condition[i].left_saturation); pidff_set(&pidff->set_condition[PID_DEAD_BAND], effect->u.condition[i].deadband); - usbhid_wait_io(pidff->hid); usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], USB_DIR_OUT); } @@ -512,7 +511,6 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; } - usbhid_wait_io(pidff->hid); usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], USB_DIR_OUT); } @@ -548,6 +546,9 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id) int pid_id = pidff->pid_id[effect_id]; debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); + /* Wait for the queue to clear. We do not want a full fifo to + prevent the effect removal. */ + usbhid_wait_io(pidff->hid); pidff_playback_pid(pidff, pid_id, 0); pidff_erase_pid(pidff, pid_id); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index b47f991867e9..abedb13c623e 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -67,7 +67,7 @@ struct usbhid_device { spinlock_t ctrllock; /* Control fifo spinlock */ struct urb *urbout; /* Output URB */ - struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ + struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ unsigned char outhead, outtail; /* Output pipe fifo head & tail */ char *outbuf; /* Output buffer */ dma_addr_t outbuf_dma; /* Output buffer dma */ diff --git a/include/linux/hid.h b/include/linux/hid.h index dcdef0bb4bba..f13bca2dd53b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -388,6 +388,12 @@ struct hid_report_enum { struct hid_control_fifo { unsigned char dir; struct hid_report *report; + char *raw_report; +}; + +struct hid_output_fifo { + struct hid_report *report; + char *raw_report; }; #define HID_CLAIMED_INPUT 1 -- cgit v1.2.3